From 95dff5ebc8f3fea49ea49b3ac75886c234bdb2a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 23:37:05 +0100 Subject: [PATCH 01/51] Bump proc-macro2 from 1.0.74 to 1.0.76 (#2390) Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.74 to 1.0.76. - [Release notes](https://github.com/dtolnay/proc-macro2/releases) - [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.74...1.0.76) --- updated-dependencies: - dependency-name: proc-macro2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e9908df97b..c09f1b3937 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9866,9 +9866,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.74" +version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2de98502f212cfcea8d0bb305bd0f49d7ebdd75b64ba0a68f937d888f4e0d6db" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" dependencies = [ "unicode-ident", ] From 6cdfc273c92f9a37005417658d53a8db27d65540 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 23:37:24 +0100 Subject: [PATCH 02/51] Bump syn from 2.0.46 to 2.0.48 (#2389) Bumps [syn](https://github.com/dtolnay/syn) from 2.0.46 to 2.0.48. - [Release notes](https://github.com/dtolnay/syn/releases) - [Commits](https://github.com/dtolnay/syn/compare/2.0.46...2.0.48) --- updated-dependencies: - dependency-name: syn dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 86 +++++++++++++++++++++++++++--------------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c09f1b3937..b8770af223 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -445,7 +445,7 @@ checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -456,7 +456,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -1058,7 +1058,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -1736,7 +1736,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -2049,7 +2049,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -2066,7 +2066,7 @@ checksum = "b8fcfa71f66c8563c4fa9dd2bb68368d50267856f831ac5d85367e0805f9606c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -2344,7 +2344,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -2537,7 +2537,7 @@ checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745" dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -2548,7 +2548,7 @@ checksum = "b893c4eb2dc092c811165f84dc7447fae16fb66521717968c34c509b39b1a5c5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -2848,7 +2848,7 @@ dependencies = [ "fs-err", "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -3365,7 +3365,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -3480,7 +3480,7 @@ dependencies = [ "proc-macro-warning", "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -3492,7 +3492,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -3502,7 +3502,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -3669,7 +3669,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -5433,7 +5433,7 @@ version = "0.9.12" dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -6501,7 +6501,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -6512,7 +6512,7 @@ checksum = "6c11e44798ad209ccdd91fc192f0526a369a01234f7373e1b141c96d7cee4f0e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -7863,7 +7863,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -8381,7 +8381,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -8422,7 +8422,7 @@ checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -9746,7 +9746,7 @@ dependencies = [ "proc-macro2", "quote", "sha3", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -9861,7 +9861,7 @@ checksum = "0e99670bafb56b9a106419397343bdbc8b8742c3cc449fec6345f86173f47cd4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -10257,7 +10257,7 @@ checksum = "2dfaf0c85b766276c797f3791f5bc6d5bd116b41d53049af2789666b0c0bc9fa" dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -11035,7 +11035,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -12011,7 +12011,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -12297,7 +12297,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -12638,7 +12638,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -12880,7 +12880,7 @@ dependencies = [ "proc-macro2", "quote", "sp-core-hashing", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -12899,7 +12899,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -13110,7 +13110,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -13278,7 +13278,7 @@ dependencies = [ "parity-scale-codec", "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -13471,7 +13471,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -13656,9 +13656,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.46" +version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89456b690ff72fddcecf231caedbe615c59480c93358a93dfae7fc29e3ebbf0e" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", @@ -13782,7 +13782,7 @@ checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -13951,7 +13951,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -14097,7 +14097,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -14140,7 +14140,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -14587,7 +14587,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", "wasm-bindgen-shared", ] @@ -14621,7 +14621,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -15695,7 +15695,7 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] @@ -15803,7 +15803,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.46", + "syn 2.0.48", ] [[package]] From eda1cf26acdff436d2a77a4c8593b4dc091ddd0f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 Jan 2024 01:05:34 +0100 Subject: [PATCH 03/51] Bump follow-redirects from 1.15.3 to 1.15.4 in /ts-tests (#2392) Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.3 to 1.15.4. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.3...v1.15.4) --- updated-dependencies: - dependency-name: follow-redirects dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ts-tests/pnpm-lock.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ts-tests/pnpm-lock.yaml b/ts-tests/pnpm-lock.yaml index a04b119ea0..6e43417664 100644 --- a/ts-tests/pnpm-lock.yaml +++ b/ts-tests/pnpm-lock.yaml @@ -2642,8 +2642,8 @@ packages: resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} dev: true - /follow-redirects@1.15.3: - resolution: {integrity: sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==} + /follow-redirects@1.15.4: + resolution: {integrity: sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==} engines: {node: '>=4.0'} peerDependencies: debug: '*' @@ -4436,7 +4436,7 @@ packages: dependencies: command-exists: 1.2.9 commander: 8.3.0 - follow-redirects: 1.15.3 + follow-redirects: 1.15.4 js-sha3: 0.8.0 memorystream: 0.3.1 semver: 5.7.2 From b5dc4f89dfdd68db92d5ed9902a0ae143f2c4900 Mon Sep 17 00:00:00 2001 From: Zhouhui Tian <125243011+zhouhuitian@users.noreply.github.com> Date: Wed, 10 Jan 2024 00:38:26 +0800 Subject: [PATCH 04/51] Add missing litentry-cli commands for request-vc (#2394) * add LITStaking * add BRC20AmountHolder --- .../cli/src/trusted_base_cli/commands/litentry/request_vc.rs | 4 ++++ .../trusted_base_cli/commands/litentry/request_vc_direct.rs | 2 ++ 2 files changed, 6 insertions(+) diff --git a/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc.rs b/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc.rs index b740d93d2a..64723c63dd 100644 --- a/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc.rs +++ b/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc.rs @@ -104,6 +104,8 @@ pub enum Command { #[clap(subcommand)] EVMAmountHolding(EVMAmountHoldingCommand), CryptoSummary, + LITStaking, + BRC20AmountHolder, } #[derive(Args, Debug)] @@ -476,6 +478,8 @@ impl RequestVcCommand { EVMAmountHoldingCommand::Trx => Assertion::EVMAmountHolding(EVMTokenType::Trx), }, Command::CryptoSummary => Assertion::CryptoSummary, + Command::LITStaking => Assertion::LITStaking, + Command::BRC20AmountHolder => Assertion::BRC20AmountHolder, }; let key = Self::random_aes_key(); diff --git a/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc_direct.rs b/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc_direct.rs index d909a05212..54da244d3d 100644 --- a/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc_direct.rs +++ b/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc_direct.rs @@ -254,6 +254,8 @@ impl RequestVcDirectCommand { EVMAmountHoldingCommand::Trx => Assertion::EVMAmountHolding(EVMTokenType::Trx), }, Command::CryptoSummary => Assertion::CryptoSummary, + Command::LITStaking => Assertion::LITStaking, + Command::BRC20AmountHolder => Assertion::BRC20AmountHolder, }; let mut key: RequestAesKey = RequestAesKey::default(); From b2a8883ec4d37194117ff779e6944c080c567555 Mon Sep 17 00:00:00 2001 From: Kai <7630809+Kailai-Wang@users.noreply.github.com> Date: Tue, 9 Jan 2024 20:45:32 +0100 Subject: [PATCH 05/51] Fix empty web3network for assertions (#2396) * Correct supported web3networks for assertions * Update some crates * adjust order --- Cargo.lock | 30 ++++++++++++------------ pallets/teerex/sgx-verify/Cargo.toml | 2 +- precompiles/bridge-transfer/Cargo.toml | 2 +- precompiles/parachain-staking/Cargo.toml | 2 +- precompiles/utils/Cargo.toml | 2 +- precompiles/utils/macro/Cargo.toml | 2 +- primitives/core/src/assertion.rs | 16 ++++++------- tee-worker/Cargo.lock | 18 +++++++------- 8 files changed, 36 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b8770af223..e4b3cfabcd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6485,11 +6485,11 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683751d591e6d81200c39fb0d1032608b77724f34114db54f571ff1317b337c0" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" dependencies = [ - "num_enum_derive 0.7.1", + "num_enum_derive 0.7.2", ] [[package]] @@ -6506,9 +6506,9 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c11e44798ad209ccdd91fc192f0526a369a01234f7373e1b141c96d7cee4f0e" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ "proc-macro2", "quote", @@ -7219,7 +7219,7 @@ dependencies = [ "frame-system", "hex-literal 0.4.1", "log", - "num_enum 0.7.1", + "num_enum 0.7.2", "pallet-balances", "pallet-bridge", "pallet-bridge-transfer", @@ -7274,7 +7274,7 @@ dependencies = [ "frame-support", "frame-system", "log", - "num_enum 0.7.1", + "num_enum 0.7.2", "pallet-balances", "pallet-evm", "pallet-parachain-staking", @@ -9725,7 +9725,7 @@ dependencies = [ "hex-literal 0.4.1", "impl-trait-for-tuples", "log", - "num_enum 0.7.1", + "num_enum 0.7.2", "pallet-evm", "parity-scale-codec", "precompile-utils-macro", @@ -9742,7 +9742,7 @@ dependencies = [ name = "precompile-utils-macro" version = "0.9.17" dependencies = [ - "num_enum 0.7.1", + "num_enum 0.7.2", "proc-macro2", "quote", "sha3", @@ -12282,18 +12282,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.193" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.193" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" dependencies = [ "proc-macro2", "quote", @@ -12302,9 +12302,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.108" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +checksum = "cb0652c533506ad7a2e353cce269330d6afd8bdfb6d75e0ace5b35aacbd7b9e9" dependencies = [ "itoa", "ryu", diff --git a/pallets/teerex/sgx-verify/Cargo.toml b/pallets/teerex/sgx-verify/Cargo.toml index e6bdc23e82..fdcd9cfa5c 100644 --- a/pallets/teerex/sgx-verify/Cargo.toml +++ b/pallets/teerex/sgx-verify/Cargo.toml @@ -16,7 +16,7 @@ der = { default-features = false, version = "0.6.0" } hex = { default-features = false, version = "0.4.3", features = ["alloc"] } ring = { version = "0.16.20", default-features = false, features = ["alloc"] } scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } -serde = { default-features = false, version = "1.0.193", features = ["derive"] } +serde = { default-features = false, version = "1.0", features = ["derive"] } serde_json = { version = "1.0", default-features = false, features = ["alloc"] } webpki = { git = "https://github.com/rustls/webpki", version = "=0.102.0-alpha.3", rev = "da923ed", package = "rustls-webpki", default-features = false, features = ["alloc", "ring"] } x509-cert = { default-features = false, version = "0.1.0", features = ["alloc"] } diff --git a/precompiles/bridge-transfer/Cargo.toml b/precompiles/bridge-transfer/Cargo.toml index a90dd3e709..a070b72df1 100644 --- a/precompiles/bridge-transfer/Cargo.toml +++ b/precompiles/bridge-transfer/Cargo.toml @@ -6,7 +6,7 @@ version = '0.9.17' [dependencies] log = { version = "0.4", default-features = false } -num_enum = { version = "0.7.1", default-features = false } +num_enum = { version = "0.7.2", default-features = false } parity-scale-codec = { version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } rustc-hex = { version = "2.0.1", default-features = false } diff --git a/precompiles/parachain-staking/Cargo.toml b/precompiles/parachain-staking/Cargo.toml index 920147bdbd..45071ef98d 100644 --- a/precompiles/parachain-staking/Cargo.toml +++ b/precompiles/parachain-staking/Cargo.toml @@ -6,7 +6,7 @@ version = '0.9.17' [dependencies] log = { version = "0.4", default-features = false } -num_enum = { version = "0.7.1", default-features = false } +num_enum = { version = "0.7.2", default-features = false } parity-scale-codec = { version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } rustc-hex = { version = "2.0.1", default-features = false } diff --git a/precompiles/utils/Cargo.toml b/precompiles/utils/Cargo.toml index 1c5333f58b..45a48561b3 100644 --- a/precompiles/utils/Cargo.toml +++ b/precompiles/utils/Cargo.toml @@ -9,7 +9,7 @@ version = '0.9.17' evm = { git = "https://github.com/rust-blockchain/evm", rev = "b7b82c7e1fc57b7449d6dfa6826600de37cc1e65", default-features = false, optional = true } impl-trait-for-tuples = "0.2.2" log = { version = "0.4", default-features = false } -num_enum = { version = "0.7.1", default-features = false } +num_enum = { version = "0.7.2", default-features = false } sha3 = { version = "0.10", default-features = false } similar-asserts = { version = "1.5.0", optional = true } diff --git a/precompiles/utils/macro/Cargo.toml b/precompiles/utils/macro/Cargo.toml index 81a17076bc..8ee1727ad5 100644 --- a/precompiles/utils/macro/Cargo.toml +++ b/precompiles/utils/macro/Cargo.toml @@ -12,7 +12,7 @@ name = "tests" path = "tests/tests.rs" [dependencies] -num_enum = { version = "0.7.1", default-features = false } +num_enum = { version = "0.7.2", default-features = false } proc-macro2 = { version = "1" } quote = { version = "1" } sha3 = { version = "0.10", default-features = false } diff --git a/primitives/core/src/assertion.rs b/primitives/core/src/assertion.rs index 642448e71e..8b54e5aea1 100644 --- a/primitives/core/src/assertion.rs +++ b/primitives/core/src/assertion.rs @@ -271,8 +271,6 @@ impl Assertion { // the broader `Web3Network` (see network.rs) pub fn get_supported_web3networks(&self) -> Vec { match self { - // A1, any web3 network is allowed - Self::A1 => all_web3networks(), // LIT holder, not including `LitentryRococo` as it's not supported by any data provider Self::A4(..) => vec![Web3Network::Litentry, Web3Network::Litmus, Web3Network::Ethereum], // DOT holder @@ -299,13 +297,13 @@ impl Assertion { vec![Web3Network::Ethereum, Web3Network::Bsc], // BRC20 Holder Self::BRC20AmountHolder => vec![Web3Network::BitcoinP2tr], - // we don't care about any specific web3 network - Self::A2(..) | - Self::A3(..) | - Self::A6 | - Self::A13(..) | - Self::A20 | - Self::GenericDiscordRole(..) => vec![], + // + // general rules + // + // any web3 network is allowed + Self::A1 | Self::A13(..) | Self::A20 => all_web3networks(), + // no web3 network is allowed + Self::A2(..) | Self::A3(..) | Self::A6 | Self::GenericDiscordRole(..) => vec![], } } } diff --git a/tee-worker/Cargo.lock b/tee-worker/Cargo.lock index 4e92ea7348..604b0ee31b 100644 --- a/tee-worker/Cargo.lock +++ b/tee-worker/Cargo.lock @@ -8195,11 +8195,11 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683751d591e6d81200c39fb0d1032608b77724f34114db54f571ff1317b337c0" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" dependencies = [ - "num_enum_derive 0.7.1", + "num_enum_derive 0.7.2", ] [[package]] @@ -8216,9 +8216,9 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c11e44798ad209ccdd91fc192f0526a369a01234f7373e1b141c96d7cee4f0e" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ "proc-macro2", "quote", @@ -8868,7 +8868,7 @@ dependencies = [ "frame-support", "frame-system", "log 0.4.20", - "num_enum 0.7.1", + "num_enum 0.7.2", "pallet-bridge", "pallet-bridge-transfer", "pallet-evm 6.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", @@ -8917,7 +8917,7 @@ dependencies = [ "frame-support", "frame-system", "log 0.4.20", - "num_enum 0.7.1", + "num_enum 0.7.2", "pallet-evm 6.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", "pallet-parachain-staking", "parity-scale-codec", @@ -10191,7 +10191,7 @@ dependencies = [ "frame-system", "impl-trait-for-tuples", "log 0.4.20", - "num_enum 0.7.1", + "num_enum 0.7.2", "pallet-evm 6.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", "parity-scale-codec", "precompile-utils-macro", @@ -10207,7 +10207,7 @@ dependencies = [ name = "precompile-utils-macro" version = "0.9.17" dependencies = [ - "num_enum 0.7.1", + "num_enum 0.7.2", "proc-macro2", "quote", "sha3", From b3341a732d5fc2ec822dc5cc67f18cc1ee723344 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Jan 2024 00:17:01 +0100 Subject: [PATCH 06/51] Bump serde_json from 1.0.109 to 1.0.111 (#2397) Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.109 to 1.0.111. - [Release notes](https://github.com/serde-rs/json/releases) - [Commits](https://github.com/serde-rs/json/compare/v1.0.109...v1.0.111) --- updated-dependencies: - dependency-name: serde_json dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e4b3cfabcd..b2af255f9c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12302,9 +12302,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.109" +version = "1.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0652c533506ad7a2e353cce269330d6afd8bdfb6d75e0ace5b35aacbd7b9e9" +checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" dependencies = [ "itoa", "ryu", From 01d364ed820212deafe9b38ca53c59a9d3f85d80 Mon Sep 17 00:00:00 2001 From: Faisal Ahmed <42486737+felixfaisal@users.noreply.github.com> Date: Wed, 10 Jan 2024 11:54:10 +0530 Subject: [PATCH 07/51] fix: double encoding (#2393) Co-authored-by: Zhouhui Tian <125243011+zhouhuitian@users.noreply.github.com> Co-authored-by: Kai <7630809+Kailai-Wang@users.noreply.github.com> --- tee-worker/sidechain/rpc-handler/src/direct_top_pool_api.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tee-worker/sidechain/rpc-handler/src/direct_top_pool_api.rs b/tee-worker/sidechain/rpc-handler/src/direct_top_pool_api.rs index c4c7a00220..5cf3421c1b 100644 --- a/tee-worker/sidechain/rpc-handler/src/direct_top_pool_api.rs +++ b/tee-worker/sidechain/rpc-handler/src/direct_top_pool_api.rs @@ -132,7 +132,7 @@ where Ok(Ok(response)) => { let json_value = RpcReturnValue { do_watch: false, - value: response.encode(), + value: response, status: DirectRequestStatus::Ok, }; Ok(json!(json_value.to_hex())) From 68f4237da7d94bd71eb4fb3afee573aeac1f6562 Mon Sep 17 00:00:00 2001 From: Zhouhui Tian <125243011+zhouhuitian@users.noreply.github.com> Date: Wed, 10 Jan 2024 23:04:32 +0800 Subject: [PATCH 08/51] filter out web2 identites (#2398) --- .../litentry/core/assertion-build/src/lit_staking.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tee-worker/litentry/core/assertion-build/src/lit_staking.rs b/tee-worker/litentry/core/assertion-build/src/lit_staking.rs index 223c0f41a0..0319353717 100644 --- a/tee-worker/litentry/core/assertion-build/src/lit_staking.rs +++ b/tee-worker/litentry/core/assertion-build/src/lit_staking.rs @@ -175,9 +175,12 @@ pub fn build(req: &AssertionBuildRequest) -> Result { debug!("Assertion building LIT staking amount"); let mut identities = vec![]; - req.identities.iter().for_each(|identity| { - identities.push(identity.0.clone()); - }); + req.identities + .iter() + .filter(|(identity, _)| identity.is_substrate()) + .for_each(|identity| { + identities.push(identity.0.clone()); + }); let mut client = LitentryStakingClient::new(); let staking_amount = DelegatorState.query_lit_staking(&mut client, &identities)?; From 88f227c4f8816536ea20b248061ee460c6bd48c0 Mon Sep 17 00:00:00 2001 From: Zhouhui Tian <125243011+zhouhuitian@users.noreply.github.com> Date: Thu, 11 Jan 2024 07:25:00 +0800 Subject: [PATCH 09/51] Perform parameter check on direct_top_pool_api (#2395) * add submit_vc_request_inner * check first request params * small update * make fmt --------- Co-authored-by: Kai <7630809+Kailai-Wang@users.noreply.github.com> --- .../rpc-handler/src/direct_top_pool_api.rs | 85 +++++++++++-------- 1 file changed, 48 insertions(+), 37 deletions(-) diff --git a/tee-worker/sidechain/rpc-handler/src/direct_top_pool_api.rs b/tee-worker/sidechain/rpc-handler/src/direct_top_pool_api.rs index 5cf3421c1b..f8631e710a 100644 --- a/tee-worker/sidechain/rpc-handler/src/direct_top_pool_api.rs +++ b/tee-worker/sidechain/rpc-handler/src/direct_top_pool_api.rs @@ -34,7 +34,7 @@ use itp_types::{DirectRequestStatus, RsaRequest, ShardIdentifier, TrustedOperati use itp_utils::{FromHexPrefixed, ToHexPrefixed}; use jsonrpc_core::{futures::executor, serde_json::json, Error as RpcError, IoHandler, Params}; use lc_vc_task_sender::{VCRequest, VcRequestSender}; -use litentry_primitives::{AesOutput, AesRequest}; +use litentry_primitives::AesRequest; use log::*; use std::{borrow::ToOwned, format, string::String, sync::Arc, vec, vec::Vec}; @@ -116,36 +116,14 @@ where io_handler.add_method("author_submitVCRequest", move |params: Params| { debug!("worker_api_direct rpc was called: author_submitVCRequest"); + async move { - let hex_encoded_params = params.parse::>().unwrap(); - let request = AesRequest::from_hex(&hex_encoded_params[0].clone()).unwrap(); - let shard: ShardIdentifier = request.shard; - let key = request.key; - let encrypted_trusted_call: AesOutput = request.payload; - let request_sender = VcRequestSender::new(); - let (sender, receiver) = oneshot::channel::, String>>(); - let vc_request = VCRequest { encrypted_trusted_call, sender, shard, key }; - if let Err(e) = request_sender.send_vc_request(vc_request) { - return Ok(json!(compute_hex_encoded_return_error(&e))) - } - match receiver.await { - Ok(Ok(response)) => { - let json_value = RpcReturnValue { - do_watch: false, - value: response, - status: DirectRequestStatus::Ok, - }; - Ok(json!(json_value.to_hex())) - }, - Ok(Err(e)) => { - log::error!("Received error in jsonresponse: {:?} ", e); - Ok(json!(compute_hex_encoded_return_error(&e))) - }, - Err(_) => { - // This case will only happen if the sender has been dropped - Ok(json!(compute_hex_encoded_return_error("The sender has been dropped"))) - }, - } + let json_value = match submit_vc_request_inner(params).await { + Ok(value) => value.to_hex(), + Err(error) => compute_hex_encoded_return_error(&error), + }; + + Ok(json!(json_value)) } .boxed() }); @@ -299,12 +277,12 @@ where debug!("Author submit and watch trusted operation.."); let hex_encoded_params = params.parse::>().map_err(|e| format!("{:?}", e))?; + let param = &hex_encoded_params.get(0).ok_or("Could not get first param")?; - info!("Got request hex: {:?}", &hex_encoded_params[0]); - std::println!("Got request hex: {:?}", &hex_encoded_params[0]); + info!("Got request hex: {:?}", param); + std::println!("Got request hex: {:?}", param); - let request = - RsaRequest::from_hex(&hex_encoded_params[0].clone()).map_err(|e| format!("{:?}", e))?; + let request = RsaRequest::from_hex(param).map_err(|e| format!("{:?}", e))?; let response: Result = if let Some(method) = json_rpc_method { executor::block_on(async { author.watch_and_broadcast_top(request, method).await }) @@ -331,10 +309,11 @@ where G: PartialEq + Encode + Decode + Debug + Send + Sync + 'static, { let hex_encoded_params = params.parse::>().map_err(|e| format!("{:?}", e))?; - info!("author_submitAndWatchAesRequest, request hex: {:?}", &hex_encoded_params[0]); + let param = &hex_encoded_params.get(0).ok_or("Could not get first param")?; + + info!("author_submitAndWatchAesRequest, request hex: {:?}", param); - let request = - AesRequest::from_hex(&hex_encoded_params[0].clone()).map_err(|e| format!("{:?}", e))?; + let request = AesRequest::from_hex(param).map_err(|e| format!("{:?}", e))?; let response: Result = if let Some(method) = json_rpc_method { executor::block_on(async { author.watch_and_broadcast_top(request, method).await }) @@ -349,3 +328,35 @@ where response.map_err(|e| format!("{:?}", e)) } + +async fn submit_vc_request_inner(params: Params) -> Result { + let hex_encoded_params = params.parse::>().map_err(|e| format!("{:?}", e))?; + let param = &hex_encoded_params.get(0).ok_or("Could not get first param")?; + let request = AesRequest::from_hex(param).map_err(|e| format!("{:?}", e))?; + + let (sender, receiver) = oneshot::channel::, String>>(); + + let vc_request = VCRequest { + encrypted_trusted_call: request.payload, + sender, + shard: request.shard, + key: request.key, + }; + + if let Err(e) = VcRequestSender::new().send_vc_request(vc_request) { + return Err(compute_hex_encoded_return_error(&e)) + } + + match receiver.await { + Ok(Ok(response)) => + Ok(RpcReturnValue { do_watch: false, value: response, status: DirectRequestStatus::Ok }), + Ok(Err(e)) => { + log::error!("Received error in jsonresponse: {:?} ", e); + Err(compute_hex_encoded_return_error(&e)) + }, + Err(_) => { + // This case will only happen if the sender has been dropped + Err(compute_hex_encoded_return_error("The sender has been dropped")) + }, + } +} From b2d5bc2da2a83bbb78b5f7c502e591ee51b73768 Mon Sep 17 00:00:00 2001 From: Faisal Ahmed <42486737+felixfaisal@users.noreply.github.com> Date: Fri, 12 Jan 2024 21:03:35 +0530 Subject: [PATCH 10/51] refactor: remove substratetestnet (#2399) * refactor: remove substratetestnet * refactor: use _Unused_6: null --- primitives/core/src/network.rs | 9 ++------- .../prepare-build/interfaces/identity/definitions.ts | 2 +- tee-worker/litentry/core/assertion-build/src/lib.rs | 1 - .../litentry/core/data-providers/src/achainable.rs | 1 - 4 files changed, 3 insertions(+), 10 deletions(-) diff --git a/primitives/core/src/network.rs b/primitives/core/src/network.rs index 34fcb119ce..48258458e6 100644 --- a/primitives/core/src/network.rs +++ b/primitives/core/src/network.rs @@ -70,9 +70,7 @@ pub enum Web3Network { LitentryRococo, #[codec(index = 5)] Khala, - #[codec(index = 6)] - SubstrateTestnet, // when launched it with standalone (integritee-)node - + // Index 6 used to SubstrateTestnet, So let's not break the indexing... // evm #[codec(index = 7)] Ethereum, @@ -109,7 +107,7 @@ impl Web3Network { Self::Polkadot | Self::Kusama | Self::Litentry | Self::Litmus | Self::LitentryRococo | - Self::Khala | Self::SubstrateTestnet + Self::Khala ) } @@ -167,7 +165,6 @@ mod tests { Web3Network::Litmus => false, Web3Network::LitentryRococo => false, Web3Network::Khala => false, - Web3Network::SubstrateTestnet => false, Web3Network::Ethereum => true, Web3Network::Bsc => true, Web3Network::BitcoinP2tr => false, @@ -192,7 +189,6 @@ mod tests { Web3Network::Litmus => true, Web3Network::LitentryRococo => true, Web3Network::Khala => true, - Web3Network::SubstrateTestnet => true, Web3Network::Ethereum => false, Web3Network::Bsc => false, Web3Network::BitcoinP2tr => false, @@ -217,7 +213,6 @@ mod tests { Web3Network::Litmus => false, Web3Network::LitentryRococo => false, Web3Network::Khala => false, - Web3Network::SubstrateTestnet => false, Web3Network::Ethereum => false, Web3Network::Bsc => false, Web3Network::BitcoinP2tr => true, diff --git a/tee-worker/client-api/parachain-api/prepare-build/interfaces/identity/definitions.ts b/tee-worker/client-api/parachain-api/prepare-build/interfaces/identity/definitions.ts index 60ea972254..b6246bf3a4 100644 --- a/tee-worker/client-api/parachain-api/prepare-build/interfaces/identity/definitions.ts +++ b/tee-worker/client-api/parachain-api/prepare-build/interfaces/identity/definitions.ts @@ -41,7 +41,7 @@ export default { "Litmus", "LitentryRococo", "Khala", - "SubstrateTestnet", + "Null", "Ethereum", "Bsc", "BitcoinP2tr", diff --git a/tee-worker/litentry/core/assertion-build/src/lib.rs b/tee-worker/litentry/core/assertion-build/src/lib.rs index 297333b264..a982deb232 100644 --- a/tee-worker/litentry/core/assertion-build/src/lib.rs +++ b/tee-worker/litentry/core/assertion-build/src/lib.rs @@ -177,7 +177,6 @@ fn pubkey_to_address(network: &Web3Network, pubkey: &str) -> String { | Web3Network::Litmus | Web3Network::LitentryRococo | Web3Network::Khala - | Web3Network::SubstrateTestnet | Web3Network::Ethereum | Web3Network::Bsc => "".to_string(), } diff --git a/tee-worker/litentry/core/data-providers/src/achainable.rs b/tee-worker/litentry/core/data-providers/src/achainable.rs index 2fa0939be9..db97dbc371 100644 --- a/tee-worker/litentry/core/data-providers/src/achainable.rs +++ b/tee-worker/litentry/core/data-providers/src/achainable.rs @@ -176,7 +176,6 @@ pub fn web3_network_to_chain(network: &Web3Network) -> String { Web3Network::Litmus => "litmus".into(), Web3Network::LitentryRococo => "litentry_rococo".into(), Web3Network::Khala => "khala".into(), - Web3Network::SubstrateTestnet => "substrate_testnet".into(), Web3Network::Ethereum => "ethereum".into(), Web3Network::Bsc => "bsc".into(), Web3Network::BitcoinP2tr => "bitcoin_p2tr".into(), From aaff9885aeac002d43fe905a7d121d63a72f9c67 Mon Sep 17 00:00:00 2001 From: BillyWooo Date: Mon, 15 Jan 2024 12:18:29 +0100 Subject: [PATCH 11/51] bug fix: compose teerex::register_enclave extrinsics; use intel 'dev' server for attestation temporary; add one example config file (#2400) --- .../src/attestation_handler.rs | 5 ++-- tee-worker/enclave-runtime/src/attestation.rs | 22 +------------- tee-worker/local-setup/rococo_one_worker.json | 29 +++++++++++++++++++ 3 files changed, 33 insertions(+), 23 deletions(-) create mode 100644 tee-worker/local-setup/rococo_one_worker.json diff --git a/tee-worker/core-primitives/attestation-handler/src/attestation_handler.rs b/tee-worker/core-primitives/attestation-handler/src/attestation_handler.rs index 3a94ab547c..9657db5edc 100644 --- a/tee-worker/core-primitives/attestation-handler/src/attestation_handler.rs +++ b/tee-worker/core-primitives/attestation-handler/src/attestation_handler.rs @@ -65,10 +65,11 @@ use std::{ pub const DEV_HOSTNAME: &str = "api.trustedservices.intel.com"; +// Litentry TODO: use `dev` for production temporary. Will switch to dcap later. #[cfg(feature = "production")] -pub const SIGRL_SUFFIX: &str = "/sgx/attestation/v4/sigrl/"; +pub const SIGRL_SUFFIX: &str = "/sgx/dev/attestation/v4/sigrl/"; #[cfg(feature = "production")] -pub const REPORT_SUFFIX: &str = "/sgx/attestation/v4/report"; +pub const REPORT_SUFFIX: &str = "/sgx/dev/attestation/v4/report"; #[cfg(not(feature = "production"))] pub const SIGRL_SUFFIX: &str = "/sgx/dev/attestation/v4/sigrl/"; diff --git a/tee-worker/enclave-runtime/src/attestation.rs b/tee-worker/enclave-runtime/src/attestation.rs index 267c3f404d..45f3a4b2ef 100644 --- a/tee-worker/enclave-runtime/src/attestation.rs +++ b/tee-worker/enclave-runtime/src/attestation.rs @@ -362,11 +362,7 @@ fn generate_ias_ra_extrinsic_internal( let attestation_handler = GLOBAL_ATTESTATION_HANDLER_COMPONENT.get()?; let cert_der = attestation_handler.generate_ias_ra_cert(skip_ra)?; - if !skip_ra { - generate_ias_ra_extrinsic_from_der_cert_internal(url, &cert_der) - } else { - generate_ias_skip_ra_extrinsic_from_der_cert_internal(url, &cert_der) - } + generate_ias_ra_extrinsic_from_der_cert_internal(url, &cert_der) } pub fn generate_ias_ra_extrinsic_from_der_cert_internal( @@ -375,22 +371,6 @@ pub fn generate_ias_ra_extrinsic_from_der_cert_internal( ) -> EnclaveResult { let node_metadata_repo = get_node_metadata_repository_from_integritee_solo_or_parachain()?; - info!(" [Enclave] Compose register enclave call"); - let call_ids = node_metadata_repo - .get_from_metadata(|m| m.register_enclave_call_indexes())? - .map_err(MetadataProviderError::MetadataError)?; - - let call = OpaqueCall::from_tuple(&(call_ids, cert_der, Some(url), SgxAttestationMethod::Ias)); - - create_extrinsics(call) -} - -pub fn generate_ias_skip_ra_extrinsic_from_der_cert_internal( - url: String, - cert_der: &[u8], -) -> EnclaveResult { - let node_metadata_repo = get_node_metadata_repository_from_integritee_solo_or_parachain()?; - info!(" [Enclave] Compose register ias enclave (skip-ra) call"); let call_ids = node_metadata_repo .get_from_metadata(|m| m.register_enclave_call_indexes())? diff --git a/tee-worker/local-setup/rococo_one_worker.json b/tee-worker/local-setup/rococo_one_worker.json new file mode 100644 index 0000000000..10e0e556cc --- /dev/null +++ b/tee-worker/local-setup/rococo_one_worker.json @@ -0,0 +1,29 @@ +{ + "workers": [ + { + "source": "bin", + "flags": [ + "--clean-reset", + "--ws-external", + "-P", + "2000", + "-w", + "2001", + "-r", + "3443", + "-h", + "4545", + "-u", + "wss://rpc.rococo-parachain.litentry.io", + "-p", + "443", + "--running-mode", + "mock", + "--parentchain-start-block", + "3299860" + ], + "subcommand_flags": [ + ] + } + ] +} \ No newline at end of file From d0759fb60703c89186357bd0b9c53c8df374422c Mon Sep 17 00:00:00 2001 From: Kasper Ziemianek Date: Fri, 19 Jan 2024 15:48:10 +0100 Subject: [PATCH 12/51] Use whole unsigned VC as VC proof signature payload (#2404) * Use whole unsigned VC as VC proof signature payload * fix fmt * fix ii-vc ts-test * improve test * remove VCRegistry and events * [benchmarking bot] Auto commit generated weights files (#2409) Co-authored-by: kziemianek --------- Co-authored-by: BillyWooo Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: kziemianek --- pallets/vc-management/src/benchmarking.rs | 89 +----- pallets/vc-management/src/lib.rs | 117 +------- pallets/vc-management/src/schema.rs | 11 +- pallets/vc-management/src/tests.rs | 244 +---------------- pallets/vc-management/src/vc_context.rs | 51 ---- pallets/vc-management/src/weights.rs | 101 ------- .../src/weights/pallet_vc_management.rs | 256 ------------------ .../src/weights/pallet_vc_management.rs | 128 ++------- tee-worker/app-libs/stf/src/trusted_call.rs | 17 +- .../app-libs/stf/src/trusted_call_result.rs | 2 - .../interfaces/vc/definitions.ts | 2 - .../stf-executor/src/enclave_signer.rs | 2 +- .../core-primitives/stf-executor/src/mocks.rs | 2 +- .../stf-executor/src/traits.rs | 4 +- .../litentry/core/credentials/src/lib.rs | 7 - .../receiver/src/handler/assertion.rs | 28 +- .../lc-vc-task-receiver/src/lib.rs | 8 +- .../lc-vc-task-receiver/src/vc_handling.rs | 21 +- .../vc-issuance/lc-vc-task-sender/src/lib.rs | 4 +- .../common/utils/assertion.ts | 60 ++-- .../common/utils/vc-helper.ts | 11 +- .../ts-tests/integration-tests/ii_vc.test.ts | 82 +----- 22 files changed, 99 insertions(+), 1148 deletions(-) delete mode 100644 pallets/vc-management/src/vc_context.rs delete mode 100644 runtime/litmus/src/weights/pallet_vc_management.rs diff --git a/pallets/vc-management/src/benchmarking.rs b/pallets/vc-management/src/benchmarking.rs index a6bde4b77b..1f740d083f 100644 --- a/pallets/vc-management/src/benchmarking.rs +++ b/pallets/vc-management/src/benchmarking.rs @@ -28,26 +28,11 @@ use sp_std::vec; use test_utils::ias::consts::TEST8_MRENCLAVE; const USER_SEED: u32 = 9966; -const VC_HASH: H256 = H256::zero(); -const VC_INDEX: H256 = H256::zero(); fn assert_last_event(generic_event: ::RuntimeEvent) { frame_system::Pallet::::assert_last_event(generic_event.into()); } -fn convert_u32_array_to_u8_array(u32_array: [u32; 8]) -> [u8; 32] { - let mut u8_array = [0u8; 32]; - let mut index = 0; - - for u32_element in &u32_array { - let u8_slice = u32_element.to_le_bytes(); - u8_array[index..index + 4].copy_from_slice(&u8_slice); - index += 4; - } - - u8_array -} - benchmarks! { // Benchmark `add_delegatee`. There are no worst conditions. The benchmark showed that // execution time is constant irrespective of encrypted_data size. @@ -78,34 +63,6 @@ benchmarks! { assert_last_event::(Event::VCRequested{ account, shard, assertion }.into()); } - // Benchmark `disable_vc`. There are no worst conditions. The benchmark showed that - // execution time is constant irrespective of encrypted_data size. - disable_vc { - let account: T::AccountId = frame_benchmarking::account("TEST_A", 0u32, USER_SEED); - let identity: Identity = frame_benchmarking::account::("TEST_A", 0u32, USER_SEED).into(); - let assertion = Assertion::A1; - let req_ext_hash = H256::default(); - let tee_origin = T::TEECallOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - VCManagement::::vc_issued(tee_origin, identity, assertion, VC_INDEX, VC_HASH, req_ext_hash)?; - }: _(RawOrigin::Signed(account.clone()), VC_INDEX) - verify{ - assert_last_event::(Event::VCDisabled{ account, index: VC_HASH }.into()); - } - - // Benchmark `revoke_vc`. There are no worst conditions. The benchmark showed that - // execution time is constant irrespective of encrypted_data size. - revoke_vc { - let account: T::AccountId = frame_benchmarking::account("TEST_A", 0u32, USER_SEED); - let identity: Identity = frame_benchmarking::account::("TEST_A", 0u32, USER_SEED).into(); - let assertion = Assertion::A1; - let req_ext_hash = H256::default(); - let tee_origin = T::TEECallOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - VCManagement::::vc_issued(tee_origin, identity, assertion, VC_INDEX, VC_HASH, req_ext_hash)?; - }: _(RawOrigin::Signed(account.clone()), VC_INDEX) - verify{ - assert_last_event::(Event::VCRevoked{ account, index: VC_HASH }.into()); - } - // Benchmark `vc_issued`. There are no worst conditions. The benchmark showed that // execution time is constant irrespective of encrypted_data size. vc_issued { @@ -113,9 +70,9 @@ benchmarks! { let identity: Identity = frame_benchmarking::account::("TEST_A", 0u32, USER_SEED).into(); let assertion = Assertion::A1; let req_ext_hash = H256::default(); - }: _(call_origin, identity.clone(), assertion.clone(), VC_INDEX, VC_HASH, req_ext_hash) + }: _(call_origin, identity.clone(), assertion.clone(), req_ext_hash) verify{ - assert_last_event::(Event::VCIssued{ identity, assertion, index: VC_INDEX, req_ext_hash}.into()); + assert_last_event::(Event::VCIssued{ identity, assertion, req_ext_hash}.into()); } // Benchmark `some_error`. There are no worst conditions. The benchmark showed that @@ -199,48 +156,6 @@ benchmarks! { assert_last_event::(Event::SchemaRevoked { account, shard, index: 0 }.into()) } - // Benchmark `add_vc_registry_item`. There are no worst conditions. The benchmark showed that - // execution time is constant irrespective of encrypted_data size. - add_vc_registry_item { - let account: T::AccountId = frame_benchmarking::account("TEST_A", 0u32, USER_SEED); - let identity: Identity = frame_benchmarking::account::("TEST_B", 0u32, USER_SEED).into(); - VCManagement::::set_admin(RawOrigin::Root.into(), account.clone())?; - let assertion = Assertion::A1; - }: _(RawOrigin::Signed(account.clone()), VC_INDEX, identity.clone(), assertion.clone(), VC_HASH) - verify { - assert_last_event::(Event::VCRegistryItemAdded { identity, assertion, index: VC_INDEX }.into()) - } - - // Benchmark `remove_vc_registry_item`. There are no worst conditions. The benchmark showed that - // execution time is constant irrespective of encrypted_data size. - remove_vc_registry_item { - let account: T::AccountId = frame_benchmarking::account("TEST_A", 0u32, USER_SEED); - let identity: Identity = frame_benchmarking::account::("TEST_B", 0u32, USER_SEED).into(); - VCManagement::::set_admin(RawOrigin::Root.into(), account.clone())?; - let assertion = Assertion::A1; - VCManagement::::add_vc_registry_item(RawOrigin::Signed(account.clone()).into(), VC_INDEX, identity, assertion, VC_HASH)?; - }: _(RawOrigin::Signed(account), VC_INDEX) - verify { - assert_last_event::(Event::VCRegistryItemRemoved { index: VC_INDEX }.into()) - } - - // Benchmark `clear_vc_registry`. - clear_vc_registry { - let x in 0..100u32; - let account: T::AccountId = frame_benchmarking::account("TEST_A", 0u32, USER_SEED); - VCManagement::::set_admin(RawOrigin::Root.into(), account.clone())?; - let assertion = Assertion::A1; - for i in 0..x { - let seed = USER_SEED - i; - let identity: Identity = frame_benchmarking::account::("TEST_A", 0u32, seed).into(); - let seed_hash_u8_32 = convert_u32_array_to_u8_array([seed; 8]); - let hash: H256 = seed_hash_u8_32.into(); - VCManagement::::add_vc_registry_item(RawOrigin::Signed(account.clone()).into(), hash, identity, assertion.clone(), VC_HASH)?; - } - }: _(RawOrigin::Signed(account)) - verify { - assert_last_event::(Event::VCRegistryCleared.into()) - } } #[cfg(test)] diff --git a/pallets/vc-management/src/lib.rs b/pallets/vc-management/src/lib.rs index ec62b54282..e031200756 100644 --- a/pallets/vc-management/src/lib.rs +++ b/pallets/vc-management/src/lib.rs @@ -39,9 +39,6 @@ use sp_core::H256; use sp_std::vec::Vec; use teerex_primitives::ShardIdentifier; -mod vc_context; -pub use vc_context::*; - mod schema; pub use schema::*; @@ -73,12 +70,6 @@ pub mod pallet { type ExtrinsicWhitelistOrigin: EnsureOrigin; } - // a map VCIndex -> VC context - // TODO: to be removed in P-350 - #[pallet::storage] - #[pallet::getter(fn vc_registry)] - pub type VCRegistry = StorageMap<_, Blake2_128Concat, VCIndex, VCContext>; - // the admin account #[pallet::storage] #[pallet::getter(fn admin)] @@ -87,7 +78,7 @@ pub mod pallet { // delegatees who can request (and receive) VCs on users' behalf, // some VCs can only be requested by delegatee accounts (e.g. A13) // delegatees and admins are different: - // - admins are meant to manage the pallet state manually, e.g. schema, vcRegistry + // - admins are meant to manage the pallet state manually, e.g. schema // - delegatees can request VCs for users, similar to `proxied account` #[pallet::storage] #[pallet::getter(fn delegatee)] @@ -117,22 +108,11 @@ pub mod pallet { shard: ShardIdentifier, assertion: Assertion, }, - // a VC is disabled on chain - VCDisabled { - account: T::AccountId, - index: VCIndex, - }, - // a VC is revoked on chain - VCRevoked { - account: T::AccountId, - index: VCIndex, - }, // event that should be triggered by TEECallOrigin // a VC is just issued VCIssued { identity: Identity, assertion: Assertion, - index: VCIndex, req_ext_hash: H256, }, // Admin account was changed @@ -178,15 +158,6 @@ pub mod pallet { detail: ErrorDetail, req_ext_hash: H256, }, - VCRegistryItemAdded { - identity: Identity, - assertion: Assertion, - index: VCIndex, - }, - VCRegistryItemRemoved { - index: VCIndex, - }, - VCRegistryCleared, } #[pallet::error] @@ -276,38 +247,6 @@ pub mod pallet { Ok(().into()) } - #[pallet::call_index(3)] - #[pallet::weight(::WeightInfo::disable_vc())] - pub fn disable_vc(origin: OriginFor, index: VCIndex) -> DispatchResultWithPostInfo { - let who = T::ExtrinsicWhitelistOrigin::ensure_origin(origin)?; - VCRegistry::::try_mutate(index, |context| { - let mut c = context.take().ok_or(Error::::VCNotExist)?; - ensure!( - Some(who.clone()).encode() == c.subject.to_account_id().encode(), - Error::::VCSubjectMismatch - ); - ensure!(c.status == Status::Active, Error::::VCAlreadyDisabled); - c.status = Status::Disabled; - *context = Some(c); - Self::deposit_event(Event::VCDisabled { account: who, index }); - Ok(().into()) - }) - } - - #[pallet::call_index(4)] - #[pallet::weight(::WeightInfo::revoke_vc())] - pub fn revoke_vc(origin: OriginFor, index: VCIndex) -> DispatchResultWithPostInfo { - let who = T::ExtrinsicWhitelistOrigin::ensure_origin(origin)?; - let context = VCRegistry::::get(index).ok_or(Error::::VCNotExist)?; - ensure!( - Some(who.clone()).encode() == context.subject.to_account_id().encode(), - Error::::VCSubjectMismatch - ); - VCRegistry::::remove(index); - Self::deposit_event(Event::VCRevoked { account: who, index }); - Ok(().into()) - } - #[pallet::call_index(5)] #[pallet::weight(::WeightInfo::set_admin())] pub fn set_admin(origin: OriginFor, new: T::AccountId) -> DispatchResultWithPostInfo { @@ -399,51 +338,6 @@ pub mod pallet { Ok(().into()) } - #[pallet::call_index(10)] - #[pallet::weight(::WeightInfo::add_vc_registry_item())] - pub fn add_vc_registry_item( - origin: OriginFor, - index: VCIndex, - identity: Identity, - assertion: Assertion, - hash: H256, - ) -> DispatchResultWithPostInfo { - let sender = ensure_signed(origin)?; - ensure!(Some(sender) == Self::admin(), Error::::RequireAdmin); - ensure!(!VCRegistry::::contains_key(index), Error::::VCAlreadyExists); - VCRegistry::::insert( - index, - VCContext::new(identity.clone(), assertion.clone(), hash), - ); - Self::deposit_event(Event::VCRegistryItemAdded { identity, assertion, index }); - Ok(().into()) - } - - #[pallet::call_index(11)] - #[pallet::weight(::WeightInfo::remove_vc_registry_item())] - pub fn remove_vc_registry_item( - origin: OriginFor, - index: VCIndex, - ) -> DispatchResultWithPostInfo { - let sender = ensure_signed(origin)?; - ensure!(Some(sender) == Self::admin(), Error::::RequireAdmin); - let _ = VCRegistry::::get(index).ok_or(Error::::VCNotExist)?; - VCRegistry::::remove(index); - Self::deposit_event(Event::VCRegistryItemRemoved { index }); - Ok(().into()) - } - - #[pallet::call_index(12)] - #[pallet::weight(::WeightInfo::clear_vc_registry(u32::max_value()))] - pub fn clear_vc_registry(origin: OriginFor) -> DispatchResultWithPostInfo { - let sender = ensure_signed(origin)?; - ensure!(Some(sender) == Self::admin(), Error::::RequireAdmin); - // If more than u32 max, the map itself is overflow, so no worry - let _ = VCRegistry::::clear(u32::max_value(), None); - Self::deposit_event(Event::VCRegistryCleared); - Ok(Pays::No.into()) - } - /// --------------------------------------------------- /// The following extrinsics are supposed to be called by TEE only /// --------------------------------------------------- @@ -453,17 +347,10 @@ pub mod pallet { origin: OriginFor, identity: Identity, assertion: Assertion, - index: H256, - hash: H256, req_ext_hash: H256, ) -> DispatchResultWithPostInfo { let _ = T::TEECallOrigin::ensure_origin(origin)?; - ensure!(!VCRegistry::::contains_key(index), Error::::VCAlreadyExists); - VCRegistry::::insert( - index, - VCContext::new(identity.clone(), assertion.clone(), hash), - ); - Self::deposit_event(Event::VCIssued { identity, assertion, index, req_ext_hash }); + Self::deposit_event(Event::VCIssued { identity, assertion, req_ext_hash }); Ok(Pays::No.into()) } diff --git a/pallets/vc-management/src/schema.rs b/pallets/vc-management/src/schema.rs index b16ce2582c..394eb73218 100644 --- a/pallets/vc-management/src/schema.rs +++ b/pallets/vc-management/src/schema.rs @@ -17,12 +17,21 @@ // VC Schema // According to https://w3c-ccg.github.io/vc-json-schemas/, it defines JSON Schema for W3C Verifiable Credential. -use crate::{vc_context::Status, Config}; +use crate::Config; use codec::{Decode, Encode, MaxEncodedLen}; use core_primitives::{SchemaContentString, SchemaIdString}; use scale_info::TypeInfo; use sp_std::vec::Vec; +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +pub enum Status { + #[codec(index = 0)] + Active, + #[codec(index = 1)] + Disabled, + // Revoked, // commented out for now, we can delete the VC entry when revoked +} + #[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)] #[scale_info(skip_type_params(T))] #[codec(mel_bound())] diff --git a/pallets/vc-management/src/tests.rs b/pallets/vc-management/src/tests.rs index 32507d15a7..e4995bc0c7 100644 --- a/pallets/vc-management/src/tests.rs +++ b/pallets/vc-management/src/tests.rs @@ -20,8 +20,6 @@ use frame_support::{assert_noop, assert_ok}; use sp_core::H256; use test_utils::ias::consts::{TEST8_MRENCLAVE, TEST8_SIGNER_PUB}; -const VC_HASH: H256 = H256::zero(); -const VC_INDEX: H256 = H256::zero(); type SystemAccountId = ::AccountId; const ALICE_PUBKEY: &[u8; 32] = &[1u8; 32]; @@ -89,17 +87,10 @@ fn vc_issued_works() { let alice: Identity = test_utils::get_signer(ALICE_PUBKEY); assert_ok!(VCManagement::vc_issued( RuntimeOrigin::signed(teerex_signer), - alice.clone(), + alice, Assertion::A1, - VC_INDEX, - VC_HASH, H256::default(), )); - assert!(VCManagement::vc_registry(VC_INDEX).is_some()); - let context = VCManagement::vc_registry(VC_INDEX).unwrap(); - assert_eq!(context.subject, alice); - assert_eq!(context.assertion, Assertion::A1); - assert_eq!(context.status, Status::Active); }); } @@ -114,150 +105,12 @@ fn vc_issued_with_unpriviledged_origin_fails() { alice.into(), Assertion::A1, H256::default(), - H256::default(), - H256::default(), ), sp_runtime::DispatchError::BadOrigin ); }); } -#[test] -fn vc_issued_with_duplicated_index_fails() { - new_test_ext().execute_with(|| { - let teerex_signer: SystemAccountId = test_utils::get_signer(TEST8_SIGNER_PUB); - let alice: SystemAccountId = test_utils::get_signer(ALICE_PUBKEY); - assert_ok!(VCManagement::vc_issued( - RuntimeOrigin::signed(teerex_signer.clone()), - alice.clone().into(), - Assertion::A1, - VC_INDEX, - VC_HASH, - H256::default(), - )); - assert_noop!( - VCManagement::vc_issued( - RuntimeOrigin::signed(teerex_signer), - alice.into(), - Assertion::A1, - VC_INDEX, - VC_HASH, - H256::default(), - ), - Error::::VCAlreadyExists - ); - }); -} - -#[test] -fn disable_vc_works() { - new_test_ext().execute_with(|| { - let teerex_signer: SystemAccountId = test_utils::get_signer(TEST8_SIGNER_PUB); - let bob: SystemAccountId = test_utils::get_signer(BOB_PUBKEY); - assert_ok!(VCManagement::vc_issued( - RuntimeOrigin::signed(teerex_signer), - bob.clone().into(), - Assertion::A1, - VC_INDEX, - VC_HASH, - H256::default(), - )); - assert!(VCManagement::vc_registry(VC_INDEX).is_some()); - assert_ok!(VCManagement::disable_vc(RuntimeOrigin::signed(bob), VC_INDEX)); - // vc is not deleted - assert!(VCManagement::vc_registry(VC_INDEX).is_some()); - let context = VCManagement::vc_registry(VC_INDEX).unwrap(); - assert_eq!(context.status, Status::Disabled); - }); -} - -#[test] -fn disable_vc_with_non_existent_vc_event() { - new_test_ext().execute_with(|| { - let alice: SystemAccountId = test_utils::get_signer(ALICE_PUBKEY); - assert_noop!( - VCManagement::disable_vc(RuntimeOrigin::signed(alice), VC_INDEX), - Error::::VCNotExist - ); - }); -} - -#[test] -fn disable_vc_with_other_subject_fails() { - new_test_ext().execute_with(|| { - let teerex_signer: SystemAccountId = test_utils::get_signer(TEST8_SIGNER_PUB); - let alice: SystemAccountId = test_utils::get_signer(ALICE_PUBKEY); - let bob: SystemAccountId = test_utils::get_signer(BOB_PUBKEY); - assert_ok!(VCManagement::vc_issued( - RuntimeOrigin::signed(teerex_signer), - bob.into(), - Assertion::A1, - VC_INDEX, - VC_HASH, - H256::default(), - )); - assert_noop!( - VCManagement::disable_vc(RuntimeOrigin::signed(alice), VC_HASH), - Error::::VCSubjectMismatch - ); - - assert_eq!(VCManagement::vc_registry(VC_INDEX).unwrap().status, Status::Active); - }); -} - -#[test] -fn revoke_vc_works() { - new_test_ext().execute_with(|| { - let teerex_signer: SystemAccountId = test_utils::get_signer(TEST8_SIGNER_PUB); - let bob: SystemAccountId = test_utils::get_signer(BOB_PUBKEY); - assert_ok!(VCManagement::vc_issued( - RuntimeOrigin::signed(teerex_signer), - bob.clone().into(), - Assertion::A1, - VC_INDEX, - VC_HASH, - H256::default(), - )); - assert!(VCManagement::vc_registry(VC_INDEX).is_some()); - assert_ok!(VCManagement::revoke_vc(RuntimeOrigin::signed(bob), VC_INDEX)); - // vc is deleted - assert!(VCManagement::vc_registry(VC_INDEX).is_none()); - }); -} - -#[test] -fn revokevc_with_non_existent_vc_fails() { - new_test_ext().execute_with(|| { - let alice: SystemAccountId = test_utils::get_signer(ALICE_PUBKEY); - assert_noop!( - VCManagement::revoke_vc(RuntimeOrigin::signed(alice), VC_INDEX), - Error::::VCNotExist - ); - }); -} - -#[test] -fn revoke_vc_with_other_subject_fails() { - new_test_ext().execute_with(|| { - let teerex_signer: SystemAccountId = test_utils::get_signer(TEST8_SIGNER_PUB); - let alice: SystemAccountId = test_utils::get_signer(ALICE_PUBKEY); - let bob: SystemAccountId = test_utils::get_signer(BOB_PUBKEY); - assert_ok!(VCManagement::vc_issued( - RuntimeOrigin::signed(teerex_signer), - bob.into(), - Assertion::A1, - VC_INDEX, - VC_HASH, - H256::default(), - )); - assert_noop!( - VCManagement::revoke_vc(RuntimeOrigin::signed(alice), VC_HASH), - Error::::VCSubjectMismatch - ); - assert_eq!(VCManagement::vc_registry(VC_INDEX).unwrap().status, Status::Active); - }); -} - #[test] fn set_admin_works() { new_test_ext().execute_with(|| { @@ -493,98 +346,3 @@ fn revoke_schema_with_unprivileged_origin_fails() { ); }); } - -#[test] -fn manual_add_remove_vc_registry_item_works() { - new_test_ext().execute_with(|| { - let alice: SystemAccountId = test_utils::get_signer(ALICE_PUBKEY); - let bob: SystemAccountId = test_utils::get_signer(BOB_PUBKEY); - // Can not remove non-existing vc - assert_noop!( - VCManagement::remove_vc_registry_item(RuntimeOrigin::signed(alice.clone()), VC_INDEX), - Error::::VCNotExist - ); - // Unauthorized party can not add vc - assert_noop!( - VCManagement::add_vc_registry_item( - RuntimeOrigin::signed(bob.clone()), - VC_INDEX, - bob.clone().into(), - Assertion::A1, - VC_HASH - ), - Error::::RequireAdmin - ); - // Successfully add vc - assert_ok!(VCManagement::add_vc_registry_item( - RuntimeOrigin::signed(alice.clone()), - VC_INDEX, - alice.clone().into(), - Assertion::A1, - VC_HASH - )); - // Check result - assert!(VCManagement::vc_registry(VC_INDEX).is_some()); - System::assert_last_event(RuntimeEvent::VCManagement(crate::Event::VCRegistryItemAdded { - identity: alice.clone().into(), - assertion: Assertion::A1, - index: VC_INDEX, - })); - // Unauthorized party can not remove vc - assert_noop!( - VCManagement::remove_vc_registry_item(RuntimeOrigin::signed(bob), VC_INDEX), - Error::::RequireAdmin - ); - // Successfully remove vc - assert_ok!(VCManagement::remove_vc_registry_item(RuntimeOrigin::signed(alice), VC_INDEX)); - // Check result and events - assert!(VCManagement::vc_registry(VC_INDEX).is_none()); - System::assert_last_event(RuntimeEvent::VCManagement( - crate::Event::VCRegistryItemRemoved { index: VC_INDEX }, - )); - }); -} - -#[test] -fn manual_add_clear_vc_registry_item_works() { - new_test_ext().execute_with(|| { - let alice: SystemAccountId = test_utils::get_signer(ALICE_PUBKEY); - let bob: SystemAccountId = test_utils::get_signer(BOB_PUBKEY); - // Unauthorized party can not add vc - assert_noop!( - VCManagement::add_vc_registry_item( - RuntimeOrigin::signed(bob.clone()), - VC_INDEX, - bob.clone().into(), - Assertion::A1, - VC_HASH - ), - Error::::RequireAdmin - ); - // Successfully add vc - assert_ok!(VCManagement::add_vc_registry_item( - RuntimeOrigin::signed(alice.clone()), - VC_INDEX, - alice.clone().into(), - Assertion::A1, - VC_HASH - )); - // Check result - assert!(VCManagement::vc_registry(VC_INDEX).is_some()); - System::assert_last_event(RuntimeEvent::VCManagement(crate::Event::VCRegistryItemAdded { - identity: alice.clone().into(), - assertion: Assertion::A1, - index: VC_INDEX, - })); - // Unauthorized party can not clear vc - assert_noop!( - VCManagement::clear_vc_registry(RuntimeOrigin::signed(bob)), - Error::::RequireAdmin - ); - // Successfully clear vc - assert_ok!(VCManagement::clear_vc_registry(RuntimeOrigin::signed(alice))); - // Check result and events - assert!(VCManagement::vc_registry(VC_INDEX).is_none()); - System::assert_last_event(RuntimeEvent::VCManagement(crate::Event::VCRegistryCleared)); - }); -} diff --git a/pallets/vc-management/src/vc_context.rs b/pallets/vc-management/src/vc_context.rs deleted file mode 100644 index 24815cbd51..0000000000 --- a/pallets/vc-management/src/vc_context.rs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2020-2023 Trust Computing GmbH. -// This file is part of Litentry. -// -// Litentry is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Litentry is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Litentry. If not, see . - -use codec::{Decode, Encode, MaxEncodedLen}; -use core_primitives::{Assertion, Identity}; -use scale_info::TypeInfo; -use sp_core::H256; - -#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo, MaxEncodedLen)] -pub enum Status { - #[codec(index = 0)] - Active, - #[codec(index = 1)] - Disabled, - // Revoked, // commented out for now, we can delete the VC entry when revoked -} - -#[derive(Clone, Eq, PartialEq, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)] -pub struct VCContext { - // To be discussed: shall we make it public? - // pros: easier for the user to disable/revoke VCs, we'll need the AccountId to verify - // the owner of VC. An alternative is to store such information within TEE. - // cons: this information is then public, everyone knows e.g. ALICE owns VC ID 1234 + 4321 - // It's not bad though as it helps to verify the ownership of VC - pub subject: Identity, - // requested assertion type - pub assertion: Assertion, - // hash of the VC, computed via blake2_256 - pub hash: H256, - // status of the VC - pub status: Status, -} - -impl VCContext { - pub fn new(subject: Identity, assertion: Assertion, hash: H256) -> Self { - Self { subject, assertion, hash, status: Status::Active } - } -} diff --git a/pallets/vc-management/src/weights.rs b/pallets/vc-management/src/weights.rs index c33d96a652..10f541d305 100644 --- a/pallets/vc-management/src/weights.rs +++ b/pallets/vc-management/src/weights.rs @@ -50,8 +50,6 @@ pub trait WeightInfo { fn add_delegatee() -> Weight; fn remove_delegatee() -> Weight; fn request_vc() -> Weight; - fn disable_vc() -> Weight; - fn revoke_vc() -> Weight; fn vc_issued() -> Weight; fn some_error() -> Weight; fn set_admin() -> Weight; @@ -59,9 +57,6 @@ pub trait WeightInfo { fn disable_schema() -> Weight; fn activate_schema() -> Weight; fn revoke_schema() -> Weight; - fn add_vc_registry_item() -> Weight; - fn remove_vc_registry_item() -> Weight; - fn clear_vc_registry(x: u32, ) -> Weight; } /// Weights for pallet_vc_management using the Litentry node and recommended hardware. @@ -86,24 +81,6 @@ impl WeightInfo for LitentryWeight { Weight::from_parts(35_640_000 as u64, 0) .saturating_add(T::DbWeight::get().reads(1 as u64)) } - // Storage: VCMPExtrinsicWhitelist GroupControlOn (r:1 w:0) - // Proof Skipped: VCMPExtrinsicWhitelist GroupControlOn (max_values: Some(1), max_size: None, mode: Measured) - // Storage: VCManagement VCRegistry (r:1 w:1) - // Proof: VCManagement VCRegistry (max_values: None, max_size: Some(312), added: 2787, mode: MaxEncodedLen) - fn disable_vc() -> Weight { - Weight::from_parts(24_542_000 as u64, 0) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: VCMPExtrinsicWhitelist GroupControlOn (r:1 w:0) - // Proof Skipped: VCMPExtrinsicWhitelist GroupControlOn (max_values: Some(1), max_size: None, mode: Measured) - // Storage: VCManagement VCRegistry (r:1 w:1) - // Proof: VCManagement VCRegistry (max_values: None, max_size: Some(312), added: 2787, mode: MaxEncodedLen) - fn revoke_vc() -> Weight { - Weight::from_parts(53_908_000 as u64, 0) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } // Storage: Teerex EnclaveIndex (r:1 w:0) // Proof Skipped: Teerex EnclaveIndex (max_values: None, max_size: None, mode: Measured) // Storage: VCManagement VCRegistry (r:1 w:1) @@ -164,36 +141,6 @@ impl WeightInfo for LitentryWeight { .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } - // Storage: VCManagement Admin (r:1 w:0) - // Proof: VCManagement Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - // Storage: VCManagement VCRegistry (r:1 w:1) - // Proof: VCManagement VCRegistry (max_values: None, max_size: Some(312), added: 2787, mode: MaxEncodedLen) - fn add_vc_registry_item() -> Weight { - Weight::from_parts(22_937_000 as u64, 0) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: VCManagement Admin (r:1 w:0) - // Proof: VCManagement Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - // Storage: VCManagement VCRegistry (r:1 w:1) - // Proof: VCManagement VCRegistry (max_values: None, max_size: Some(312), added: 2787, mode: MaxEncodedLen) - fn remove_vc_registry_item() -> Weight { - Weight::from_parts(23_555_000 as u64, 0) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: VCManagement Admin (r:1 w:0) - // Proof: VCManagement Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - // Storage: VCManagement VCRegistry (r:100 w:100) - // Proof: VCManagement VCRegistry (max_values: None, max_size: Some(312), added: 2787, mode: MaxEncodedLen) - fn clear_vc_registry(x: u32, ) -> Weight { - Weight::from_parts(22_760_592 as u64, 0) - // Standard Error: 3_714 - .saturating_add(Weight::from_parts(856_025 as u64, 0).saturating_mul(x as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(x as u64))) - .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(x as u64))) - } } // For backwards compatibility and tests @@ -217,24 +164,6 @@ impl WeightInfo for () { Weight::from_parts(35_640_000 as u64, 0) .saturating_add(RocksDbWeight::get().reads(1 as u64)) } - // Storage: VCMPExtrinsicWhitelist GroupControlOn (r:1 w:0) - // Proof Skipped: VCMPExtrinsicWhitelist GroupControlOn (max_values: Some(1), max_size: None, mode: Measured) - // Storage: VCManagement VCRegistry (r:1 w:1) - // Proof: VCManagement VCRegistry (max_values: None, max_size: Some(312), added: 2787, mode: MaxEncodedLen) - fn disable_vc() -> Weight { - Weight::from_parts(24_542_000 as u64, 0) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: VCMPExtrinsicWhitelist GroupControlOn (r:1 w:0) - // Proof Skipped: VCMPExtrinsicWhitelist GroupControlOn (max_values: Some(1), max_size: None, mode: Measured) - // Storage: VCManagement VCRegistry (r:1 w:1) - // Proof: VCManagement VCRegistry (max_values: None, max_size: Some(312), added: 2787, mode: MaxEncodedLen) - fn revoke_vc() -> Weight { - Weight::from_parts(53_908_000 as u64, 0) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } // Storage: Teerex EnclaveIndex (r:1 w:0) // Proof Skipped: Teerex EnclaveIndex (max_values: None, max_size: None, mode: Measured) // Storage: VCManagement VCRegistry (r:1 w:1) @@ -295,35 +224,5 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } - // Storage: VCManagement Admin (r:1 w:0) - // Proof: VCManagement Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - // Storage: VCManagement VCRegistry (r:1 w:1) - // Proof: VCManagement VCRegistry (max_values: None, max_size: Some(312), added: 2787, mode: MaxEncodedLen) - fn add_vc_registry_item() -> Weight { - Weight::from_parts(22_937_000 as u64, 0) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: VCManagement Admin (r:1 w:0) - // Proof: VCManagement Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - // Storage: VCManagement VCRegistry (r:1 w:1) - // Proof: VCManagement VCRegistry (max_values: None, max_size: Some(312), added: 2787, mode: MaxEncodedLen) - fn remove_vc_registry_item() -> Weight { - Weight::from_parts(23_555_000 as u64, 0) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: VCManagement Admin (r:1 w:0) - // Proof: VCManagement Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - // Storage: VCManagement VCRegistry (r:100 w:100) - // Proof: VCManagement VCRegistry (max_values: None, max_size: Some(312), added: 2787, mode: MaxEncodedLen) - fn clear_vc_registry(x: u32, ) -> Weight { - Weight::from_parts(22_760_592 as u64, 0) - // Standard Error: 3_714 - .saturating_add(Weight::from_parts(856_025 as u64, 0).saturating_mul(x as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(x as u64))) - .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(x as u64))) - } } diff --git a/runtime/litmus/src/weights/pallet_vc_management.rs b/runtime/litmus/src/weights/pallet_vc_management.rs deleted file mode 100644 index 154689b869..0000000000 --- a/runtime/litmus/src/weights/pallet_vc_management.rs +++ /dev/null @@ -1,256 +0,0 @@ -// Copyright 2020-2023 Trust Computing GmbH. -// This file is part of Litentry. -// -// Litentry is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Litentry is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Litentry. If not, see . - -//! Autogenerated weights for `pallet_vc_management` -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-10-17, STEPS: `20`, REPEAT: `50`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `parachain-benchmark`, CPU: `Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("litmus-dev"), DB CACHE: 20 - -// Executed Command: -// ./litentry-collator -// benchmark -// pallet -// --chain=litmus-dev -// --execution=wasm -// --db-cache=20 -// --wasm-execution=compiled -// --pallet=pallet_vc_management -// --extrinsic=* -// --heap-pages=4096 -// --steps=20 -// --repeat=50 -// --header=./LICENSE_HEADER -// --output=./runtime/litmus/src/weights/pallet_vc_management.rs - -#![cfg_attr(rustfmt, rustfmt_skip)] -#![allow(unused_parens)] -#![allow(unused_imports)] -#![allow(missing_docs)] - -use frame_support::{traits::Get, weights::Weight}; -use core::marker::PhantomData; - -/// Weight functions for `pallet_vc_management`. -pub struct WeightInfo(PhantomData); -impl pallet_vc_management::WeightInfo for WeightInfo { - /// Storage: VCManagement Delegatee (r:0 w:1) - /// Proof: VCManagement Delegatee (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - fn add_delegatee() -> Weight { - // Proof Size summary in bytes: - // Measured: `0` - // Estimated: `0` - // Minimum execution time: 12_420_000 picoseconds. - Weight::from_parts(12_862_000, 0) - .saturating_add(Weight::from_parts(0, 0)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: VCManagement Delegatee (r:1 w:1) - /// Proof: VCManagement Delegatee (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) - fn remove_delegatee() -> Weight { - // Proof Size summary in bytes: - // Measured: `79` - // Estimated: `3513` - // Minimum execution time: 18_536_000 picoseconds. - Weight::from_parts(19_100_000, 0) - .saturating_add(Weight::from_parts(0, 3513)) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: VCMPExtrinsicWhitelist GroupControlOn (r:1 w:0) - /// Proof Skipped: VCMPExtrinsicWhitelist GroupControlOn (max_values: Some(1), max_size: None, mode: Measured) - fn request_vc() -> Weight { - // Proof Size summary in bytes: - // Measured: `42` - // Estimated: `1527` - // Minimum execution time: 15_114_000 picoseconds. - Weight::from_parts(15_520_000, 0) - .saturating_add(Weight::from_parts(0, 1527)) - .saturating_add(T::DbWeight::get().reads(1)) - } - /// Storage: VCMPExtrinsicWhitelist GroupControlOn (r:1 w:0) - /// Proof Skipped: VCMPExtrinsicWhitelist GroupControlOn (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: VCManagement VCRegistry (r:1 w:1) - /// Proof: VCManagement VCRegistry (max_values: None, max_size: Some(381), added: 2856, mode: MaxEncodedLen) - fn disable_vc() -> Weight { - // Proof Size summary in bytes: - // Measured: `190` - // Estimated: `3846` - // Minimum execution time: 23_205_000 picoseconds. - Weight::from_parts(23_614_000, 0) - .saturating_add(Weight::from_parts(0, 3846)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: VCMPExtrinsicWhitelist GroupControlOn (r:1 w:0) - /// Proof Skipped: VCMPExtrinsicWhitelist GroupControlOn (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: VCManagement VCRegistry (r:1 w:1) - /// Proof: VCManagement VCRegistry (max_values: None, max_size: Some(381), added: 2856, mode: MaxEncodedLen) - fn revoke_vc() -> Weight { - // Proof Size summary in bytes: - // Measured: `190` - // Estimated: `3846` - // Minimum execution time: 23_322_000 picoseconds. - Weight::from_parts(23_776_000, 0) - .saturating_add(Weight::from_parts(0, 3846)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: Teerex EnclaveIndex (r:1 w:0) - /// Proof Skipped: Teerex EnclaveIndex (max_values: None, max_size: None, mode: Measured) - /// Storage: VCManagement VCRegistry (r:1 w:1) - /// Proof: VCManagement VCRegistry (max_values: None, max_size: Some(381), added: 2856, mode: MaxEncodedLen) - fn vc_issued() -> Weight { - // Proof Size summary in bytes: - // Measured: `248` - // Estimated: `3846` - // Minimum execution time: 26_213_000 picoseconds. - Weight::from_parts(26_792_000, 0) - .saturating_add(Weight::from_parts(0, 3846)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: Teerex EnclaveIndex (r:1 w:0) - /// Proof Skipped: Teerex EnclaveIndex (max_values: None, max_size: None, mode: Measured) - fn some_error() -> Weight { - // Proof Size summary in bytes: - // Measured: `242` - // Estimated: `3707` - // Minimum execution time: 20_214_000 picoseconds. - Weight::from_parts(20_829_000, 0) - .saturating_add(Weight::from_parts(0, 3707)) - .saturating_add(T::DbWeight::get().reads(1)) - } - /// Storage: VCManagement Admin (r:1 w:1) - /// Proof: VCManagement Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - fn set_admin() -> Weight { - // Proof Size summary in bytes: - // Measured: `61` - // Estimated: `1517` - // Minimum execution time: 16_575_000 picoseconds. - Weight::from_parts(17_339_000, 0) - .saturating_add(Weight::from_parts(0, 1517)) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: VCManagement Admin (r:1 w:0) - /// Proof: VCManagement Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: VCManagement SchemaRegistryIndex (r:1 w:1) - /// Proof: VCManagement SchemaRegistryIndex (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) - /// Storage: VCManagement SchemaRegistry (r:0 w:1) - /// Proof: VCManagement SchemaRegistry (max_values: None, max_size: Some(2621), added: 5096, mode: MaxEncodedLen) - fn add_schema() -> Weight { - // Proof Size summary in bytes: - // Measured: `61` - // Estimated: `1517` - // Minimum execution time: 20_658_000 picoseconds. - Weight::from_parts(21_536_000, 0) - .saturating_add(Weight::from_parts(0, 1517)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(2)) - } - /// Storage: VCManagement Admin (r:1 w:0) - /// Proof: VCManagement Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: VCManagement SchemaRegistry (r:1 w:1) - /// Proof: VCManagement SchemaRegistry (max_values: None, max_size: Some(2621), added: 5096, mode: MaxEncodedLen) - fn disable_schema() -> Weight { - // Proof Size summary in bytes: - // Measured: `179` - // Estimated: `6086` - // Minimum execution time: 21_217_000 picoseconds. - Weight::from_parts(21_760_000, 0) - .saturating_add(Weight::from_parts(0, 6086)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: VCManagement Admin (r:1 w:0) - /// Proof: VCManagement Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: VCManagement SchemaRegistry (r:1 w:1) - /// Proof: VCManagement SchemaRegistry (max_values: None, max_size: Some(2621), added: 5096, mode: MaxEncodedLen) - fn activate_schema() -> Weight { - // Proof Size summary in bytes: - // Measured: `179` - // Estimated: `6086` - // Minimum execution time: 21_325_000 picoseconds. - Weight::from_parts(22_705_000, 0) - .saturating_add(Weight::from_parts(0, 6086)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: VCManagement Admin (r:1 w:0) - /// Proof: VCManagement Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: VCManagement SchemaRegistry (r:1 w:1) - /// Proof: VCManagement SchemaRegistry (max_values: None, max_size: Some(2621), added: 5096, mode: MaxEncodedLen) - fn revoke_schema() -> Weight { - // Proof Size summary in bytes: - // Measured: `179` - // Estimated: `6086` - // Minimum execution time: 22_043_000 picoseconds. - Weight::from_parts(22_697_000, 0) - .saturating_add(Weight::from_parts(0, 6086)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: VCManagement Admin (r:1 w:0) - /// Proof: VCManagement Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: VCManagement VCRegistry (r:1 w:1) - /// Proof: VCManagement VCRegistry (max_values: None, max_size: Some(381), added: 2856, mode: MaxEncodedLen) - fn add_vc_registry_item() -> Weight { - // Proof Size summary in bytes: - // Measured: `61` - // Estimated: `3846` - // Minimum execution time: 20_945_000 picoseconds. - Weight::from_parts(22_169_000, 0) - .saturating_add(Weight::from_parts(0, 3846)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: VCManagement Admin (r:1 w:0) - /// Proof: VCManagement Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: VCManagement VCRegistry (r:1 w:1) - /// Proof: VCManagement VCRegistry (max_values: None, max_size: Some(381), added: 2856, mode: MaxEncodedLen) - fn remove_vc_registry_item() -> Weight { - // Proof Size summary in bytes: - // Measured: `200` - // Estimated: `3846` - // Minimum execution time: 22_357_000 picoseconds. - Weight::from_parts(23_090_000, 0) - .saturating_add(Weight::from_parts(0, 3846)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: VCManagement Admin (r:1 w:0) - /// Proof: VCManagement Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: VCManagement VCRegistry (r:100 w:100) - /// Proof: VCManagement VCRegistry (max_values: None, max_size: Some(381), added: 2856, mode: MaxEncodedLen) - /// The range of component `x` is `[0, 100]`. - fn clear_vc_registry(x: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `91 + x * (85 ±0)` - // Estimated: `1517 + x * (2856 ±0)` - // Minimum execution time: 18_150_000 picoseconds. - Weight::from_parts(19_859_373, 0) - .saturating_add(Weight::from_parts(0, 1517)) - // Standard Error: 2_963 - .saturating_add(Weight::from_parts(1_438_870, 0).saturating_mul(x.into())) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(x.into()))) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(x.into()))) - .saturating_add(Weight::from_parts(0, 2856).saturating_mul(x.into())) - } -} diff --git a/runtime/rococo/src/weights/pallet_vc_management.rs b/runtime/rococo/src/weights/pallet_vc_management.rs index 1407b9c3a5..6bc8f6e6aa 100644 --- a/runtime/rococo/src/weights/pallet_vc_management.rs +++ b/runtime/rococo/src/weights/pallet_vc_management.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for `pallet_vc_management` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-10-17, STEPS: `20`, REPEAT: `50`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-01-19, STEPS: `20`, REPEAT: `50`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `parachain-benchmark`, CPU: `Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("rococo-dev"), DB CACHE: 20 @@ -55,8 +55,8 @@ impl pallet_vc_management::WeightInfo for WeightInfo // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 12_852_000 picoseconds. - Weight::from_parts(13_659_000, 0) + // Minimum execution time: 12_994_000 picoseconds. + Weight::from_parts(13_371_000, 0) .saturating_add(Weight::from_parts(0, 0)) .saturating_add(T::DbWeight::get().writes(1)) } @@ -66,8 +66,8 @@ impl pallet_vc_management::WeightInfo for WeightInfo // Proof Size summary in bytes: // Measured: `113` // Estimated: `3513` - // Minimum execution time: 19_272_000 picoseconds. - Weight::from_parts(20_217_000, 0) + // Minimum execution time: 18_939_000 picoseconds. + Weight::from_parts(19_373_000, 0) .saturating_add(Weight::from_parts(0, 3513)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -78,52 +78,21 @@ impl pallet_vc_management::WeightInfo for WeightInfo // Proof Size summary in bytes: // Measured: `42` // Estimated: `1527` - // Minimum execution time: 15_305_000 picoseconds. - Weight::from_parts(15_766_000, 0) + // Minimum execution time: 15_031_000 picoseconds. + Weight::from_parts(16_267_000, 0) .saturating_add(Weight::from_parts(0, 1527)) .saturating_add(T::DbWeight::get().reads(1)) } - /// Storage: VCMPExtrinsicWhitelist GroupControlOn (r:1 w:0) - /// Proof Skipped: VCMPExtrinsicWhitelist GroupControlOn (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: VCManagement VCRegistry (r:1 w:1) - /// Proof: VCManagement VCRegistry (max_values: None, max_size: Some(381), added: 2856, mode: MaxEncodedLen) - fn disable_vc() -> Weight { - // Proof Size summary in bytes: - // Measured: `224` - // Estimated: `3846` - // Minimum execution time: 23_455_000 picoseconds. - Weight::from_parts(23_974_000, 0) - .saturating_add(Weight::from_parts(0, 3846)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: VCMPExtrinsicWhitelist GroupControlOn (r:1 w:0) - /// Proof Skipped: VCMPExtrinsicWhitelist GroupControlOn (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: VCManagement VCRegistry (r:1 w:1) - /// Proof: VCManagement VCRegistry (max_values: None, max_size: Some(381), added: 2856, mode: MaxEncodedLen) - fn revoke_vc() -> Weight { - // Proof Size summary in bytes: - // Measured: `224` - // Estimated: `3846` - // Minimum execution time: 23_458_000 picoseconds. - Weight::from_parts(24_091_000, 0) - .saturating_add(Weight::from_parts(0, 3846)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) - } /// Storage: Teerex EnclaveIndex (r:1 w:0) /// Proof Skipped: Teerex EnclaveIndex (max_values: None, max_size: None, mode: Measured) - /// Storage: VCManagement VCRegistry (r:1 w:1) - /// Proof: VCManagement VCRegistry (max_values: None, max_size: Some(381), added: 2856, mode: MaxEncodedLen) fn vc_issued() -> Weight { // Proof Size summary in bytes: - // Measured: `298` - // Estimated: `3846` - // Minimum execution time: 26_449_000 picoseconds. - Weight::from_parts(27_004_000, 0) - .saturating_add(Weight::from_parts(0, 3846)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) + // Measured: `255` + // Estimated: `3720` + // Minimum execution time: 19_878_000 picoseconds. + Weight::from_parts(20_219_000, 0) + .saturating_add(Weight::from_parts(0, 3720)) + .saturating_add(T::DbWeight::get().reads(1)) } /// Storage: Teerex EnclaveIndex (r:1 w:0) /// Proof Skipped: Teerex EnclaveIndex (max_values: None, max_size: None, mode: Measured) @@ -131,8 +100,8 @@ impl pallet_vc_management::WeightInfo for WeightInfo // Proof Size summary in bytes: // Measured: `255` // Estimated: `3720` - // Minimum execution time: 19_959_000 picoseconds. - Weight::from_parts(20_416_000, 0) + // Minimum execution time: 20_075_000 picoseconds. + Weight::from_parts(20_681_000, 0) .saturating_add(Weight::from_parts(0, 3720)) .saturating_add(T::DbWeight::get().reads(1)) } @@ -142,8 +111,8 @@ impl pallet_vc_management::WeightInfo for WeightInfo // Proof Size summary in bytes: // Measured: `61` // Estimated: `1517` - // Minimum execution time: 16_502_000 picoseconds. - Weight::from_parts(16_925_000, 0) + // Minimum execution time: 16_327_000 picoseconds. + Weight::from_parts(16_714_000, 0) .saturating_add(Weight::from_parts(0, 1517)) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) @@ -158,8 +127,8 @@ impl pallet_vc_management::WeightInfo for WeightInfo // Proof Size summary in bytes: // Measured: `61` // Estimated: `1517` - // Minimum execution time: 20_992_000 picoseconds. - Weight::from_parts(21_499_000, 0) + // Minimum execution time: 20_968_000 picoseconds. + Weight::from_parts(21_334_000, 0) .saturating_add(Weight::from_parts(0, 1517)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(2)) @@ -172,8 +141,8 @@ impl pallet_vc_management::WeightInfo for WeightInfo // Proof Size summary in bytes: // Measured: `179` // Estimated: `6086` - // Minimum execution time: 21_718_000 picoseconds. - Weight::from_parts(22_198_000, 0) + // Minimum execution time: 21_697_000 picoseconds. + Weight::from_parts(22_113_000, 0) .saturating_add(Weight::from_parts(0, 6086)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -186,8 +155,8 @@ impl pallet_vc_management::WeightInfo for WeightInfo // Proof Size summary in bytes: // Measured: `179` // Estimated: `6086` - // Minimum execution time: 21_612_000 picoseconds. - Weight::from_parts(21_998_000, 0) + // Minimum execution time: 21_328_000 picoseconds. + Weight::from_parts(21_743_000, 0) .saturating_add(Weight::from_parts(0, 6086)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) @@ -200,57 +169,10 @@ impl pallet_vc_management::WeightInfo for WeightInfo // Proof Size summary in bytes: // Measured: `179` // Estimated: `6086` - // Minimum execution time: 22_351_000 picoseconds. - Weight::from_parts(22_811_000, 0) + // Minimum execution time: 22_141_000 picoseconds. + Weight::from_parts(22_502_000, 0) .saturating_add(Weight::from_parts(0, 6086)) .saturating_add(T::DbWeight::get().reads(2)) .saturating_add(T::DbWeight::get().writes(1)) } - /// Storage: VCManagement Admin (r:1 w:0) - /// Proof: VCManagement Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: VCManagement VCRegistry (r:1 w:1) - /// Proof: VCManagement VCRegistry (max_values: None, max_size: Some(381), added: 2856, mode: MaxEncodedLen) - fn add_vc_registry_item() -> Weight { - // Proof Size summary in bytes: - // Measured: `61` - // Estimated: `3846` - // Minimum execution time: 20_999_000 picoseconds. - Weight::from_parts(21_508_000, 0) - .saturating_add(Weight::from_parts(0, 3846)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: VCManagement Admin (r:1 w:0) - /// Proof: VCManagement Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: VCManagement VCRegistry (r:1 w:1) - /// Proof: VCManagement VCRegistry (max_values: None, max_size: Some(381), added: 2856, mode: MaxEncodedLen) - fn remove_vc_registry_item() -> Weight { - // Proof Size summary in bytes: - // Measured: `200` - // Estimated: `3846` - // Minimum execution time: 22_423_000 picoseconds. - Weight::from_parts(22_897_000, 0) - .saturating_add(Weight::from_parts(0, 3846)) - .saturating_add(T::DbWeight::get().reads(2)) - .saturating_add(T::DbWeight::get().writes(1)) - } - /// Storage: VCManagement Admin (r:1 w:0) - /// Proof: VCManagement Admin (max_values: Some(1), max_size: Some(32), added: 527, mode: MaxEncodedLen) - /// Storage: VCManagement VCRegistry (r:100 w:100) - /// Proof: VCManagement VCRegistry (max_values: None, max_size: Some(381), added: 2856, mode: MaxEncodedLen) - /// The range of component `x` is `[0, 100]`. - fn clear_vc_registry(x: u32, ) -> Weight { - // Proof Size summary in bytes: - // Measured: `91 + x * (85 ±0)` - // Estimated: `1517 + x * (2856 ±0)` - // Minimum execution time: 18_216_000 picoseconds. - Weight::from_parts(21_216_060, 0) - .saturating_add(Weight::from_parts(0, 1517)) - // Standard Error: 1_701 - .saturating_add(Weight::from_parts(1_385_070, 0).saturating_mul(x.into())) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(x.into()))) - .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(x.into()))) - .saturating_add(Weight::from_parts(0, 2856).saturating_mul(x.into())) - } } diff --git a/tee-worker/app-libs/stf/src/trusted_call.rs b/tee-worker/app-libs/stf/src/trusted_call.rs index 3168d9bc16..cb732571d8 100644 --- a/tee-worker/app-libs/stf/src/trusted_call.rs +++ b/tee-worker/app-libs/stf/src/trusted_call.rs @@ -131,16 +131,7 @@ pub enum TrustedCall { H256, ), #[codec(index = 21)] - request_vc_callback( - Identity, - Identity, - Assertion, - H256, - H256, - Vec, - Option, - H256, - ), + request_vc_callback(Identity, Identity, Assertion, Vec, Option, H256), #[codec(index = 22)] handle_imp_error(Identity, Option, IMPError, H256), #[codec(index = 23)] @@ -868,8 +859,6 @@ where signer, who, assertion, - vc_index, - vc_hash, vc_payload, maybe_key, req_ext_hash, @@ -904,15 +893,11 @@ where call_index, who, assertion, - vc_index, - vc_hash, req_ext_hash, )))); if let Some(key) = maybe_key { Ok(TrustedCallResult::RequestVC(RequestVCResult { - vc_index, - vc_hash, vc_payload: aes_encrypt_default(&key, &vc_payload), })) } else { diff --git a/tee-worker/app-libs/stf/src/trusted_call_result.rs b/tee-worker/app-libs/stf/src/trusted_call_result.rs index 7589a80ed0..d15286d216 100644 --- a/tee-worker/app-libs/stf/src/trusted_call_result.rs +++ b/tee-worker/app-libs/stf/src/trusted_call_result.rs @@ -95,7 +95,5 @@ pub struct SetIdentityNetworksResult { #[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] pub struct RequestVCResult { - pub vc_index: H256, - pub vc_hash: H256, pub vc_payload: AesOutput, } diff --git a/tee-worker/client-api/parachain-api/prepare-build/interfaces/vc/definitions.ts b/tee-worker/client-api/parachain-api/prepare-build/interfaces/vc/definitions.ts index 4f29666552..71d11cd3f2 100644 --- a/tee-worker/client-api/parachain-api/prepare-build/interfaces/vc/definitions.ts +++ b/tee-worker/client-api/parachain-api/prepare-build/interfaces/vc/definitions.ts @@ -37,8 +37,6 @@ export default { _enum: ["Litentry", "Litmus", "LitentryRococo", "Polkadot", "Kusama", "Khala", "Ethereum", "TestNet"], }, RequestVCResult: { - vc_index: "H256", - vc_hash: "H256", vc_payload: "AesOutput", }, // Achainable diff --git a/tee-worker/core-primitives/stf-executor/src/enclave_signer.rs b/tee-worker/core-primitives/stf-executor/src/enclave_signer.rs index 5f8f830753..7de6a1ff4c 100644 --- a/tee-worker/core-primitives/stf-executor/src/enclave_signer.rs +++ b/tee-worker/core-primitives/stf-executor/src/enclave_signer.rs @@ -145,7 +145,7 @@ where )) } - fn sign_vc_with_self(&self, payload: &[u8]) -> Result<(AccountId, Vec)> { + fn sign(&self, payload: &[u8]) -> Result<(AccountId, Vec)> { let enclave_account = self.get_enclave_account()?; let enclave_call_signing_key = self.get_enclave_call_signing_key()?; diff --git a/tee-worker/core-primitives/stf-executor/src/mocks.rs b/tee-worker/core-primitives/stf-executor/src/mocks.rs index db52adc39c..fb4079a331 100644 --- a/tee-worker/core-primitives/stf-executor/src/mocks.rs +++ b/tee-worker/core-primitives/stf-executor/src/mocks.rs @@ -142,7 +142,7 @@ impl StfEnclaveSigning for StfEnclaveSigne Ok(trusted_call.sign(&KeyPair::Ed25519(Box::new(self.signer)), 1, &self.mr_enclave, shard)) } - fn sign_vc_with_self(&self, _payload: &[u8]) -> Result<(AccountId, Vec)> { + fn sign(&self, _payload: &[u8]) -> Result<(AccountId, Vec)> { Ok((self.signer.public().into(), [0u8; 32].to_vec())) } } diff --git a/tee-worker/core-primitives/stf-executor/src/traits.rs b/tee-worker/core-primitives/stf-executor/src/traits.rs index accacc6ccd..4f7efd1532 100644 --- a/tee-worker/core-primitives/stf-executor/src/traits.rs +++ b/tee-worker/core-primitives/stf-executor/src/traits.rs @@ -33,7 +33,7 @@ pub enum StatePostProcessing { Prune, } -/// Allows signing of a trusted call or a credential with the enclave account that is registered in the STF. +/// Allows signing of a trusted call or a raw bytes with the enclave account that is registered in the STF. /// /// The signing key is derived from the shielding key, which guarantees that all enclaves sign the same key. pub trait StfEnclaveSigning @@ -49,7 +49,7 @@ where ) -> Result; // litentry - fn sign_vc_with_self(&self, payload: &[u8]) -> Result<(AccountId, Vec)>; + fn sign(&self, payload: &[u8]) -> Result<(AccountId, Vec)>; } pub trait StfShardVaultQuery { diff --git a/tee-worker/litentry/core/credentials/src/lib.rs b/tee-worker/litentry/core/credentials/src/lib.rs index d8fa42071a..19d76ea977 100644 --- a/tee-worker/litentry/core/credentials/src/lib.rs +++ b/tee-worker/litentry/core/credentials/src/lib.rs @@ -281,13 +281,6 @@ impl Credential { Ok(json_str) } - pub fn get_index(&self) -> Result<[u8; 32], Error> { - let bytes = &self.id.as_bytes()[b"0x".len()..]; - let index = hex::decode(bytes).map_err(|err| Error::ParseError(format!("{}", err)))?; - let vi: [u8; 32] = index.try_into().unwrap(); - Ok(vi) - } - pub fn validate_unsigned(&self) -> Result<(), Error> { if !self.types.contains(&CredentialType::VerifiableCredential) { return Err(Error::EmptyCredentialType) diff --git a/tee-worker/litentry/core/stf-task/receiver/src/handler/assertion.rs b/tee-worker/litentry/core/stf-task/receiver/src/handler/assertion.rs index 3a720057d2..58a69f5697 100644 --- a/tee-worker/litentry/core/stf-task/receiver/src/handler/assertion.rs +++ b/tee-worker/litentry/core/stf-task/receiver/src/handler/assertion.rs @@ -32,7 +32,6 @@ use litentry_primitives::{ VCMPError, }; use log::*; -use sp_core::hashing::blake2_256; use std::{format, sync::Arc, vec::Vec}; pub(crate) struct AssertionHandler< @@ -56,7 +55,7 @@ where O: EnclaveOnChainOCallApi, { type Error = VCMPError; - type Result = (H256, H256, Vec); // (vc_index, vc_hash, vc_byte_array) + type Result = Vec; // vc_byte_array fn on_process(&self) -> Result { // create the initial credential @@ -162,8 +161,12 @@ where ErrorDetail::StfError(ErrorString::truncate_from(format!("{e:?}").into())), ) })?; - let payload = credential.issuer.mrenclave.clone(); - let (enclave_account, sig) = signer.sign_vc_with_self(payload.as_bytes()).map_err(|e| { + + let json_string = credential.to_json().map_err(|_| { + VCMPError::RequestVCFailed(self.req.assertion.clone(), ErrorDetail::ParseError) + })?; + let payload = json_string.as_bytes(); + let (enclave_account, sig) = signer.sign(payload).map_err(|e| { VCMPError::RequestVCFailed( self.req.assertion.clone(), ErrorDetail::StfError(ErrorString::truncate_from(format!("{e:?}").into())), @@ -179,22 +182,11 @@ where ) })?; - let vc_index = credential - .get_index() - .map_err(|e| { - VCMPError::RequestVCFailed( - self.req.assertion.clone(), - ErrorDetail::StfError(ErrorString::truncate_from(format!("{e:?}").into())), - ) - })? - .into(); let credential_str = credential.to_json().map_err(|_| { VCMPError::RequestVCFailed(self.req.assertion.clone(), ErrorDetail::ParseError) })?; debug!("Credential: {}, length: {}", credential_str, credential_str.len()); - let vc_hash = blake2_256(credential_str.as_bytes()).into(); - debug!("VC hash: {:?}", vc_hash); - Ok((vc_index, vc_hash, credential_str.as_bytes().to_vec())) + Ok(credential_str.as_bytes().to_vec()) } fn on_success( @@ -205,14 +197,12 @@ where debug!("Assertion build OK"); // we shouldn't have the maximum text length limit in normal RSA3072 encryption, as the payload // using enclave's shielding key is encrypted in chunks - let (vc_index, vc_hash, vc_payload) = result; + let vc_payload = result; if let Ok(enclave_signer) = self.context.enclave_signer.get_enclave_account() { let c = TrustedCall::request_vc_callback( enclave_signer.into(), self.req.who.clone(), self.req.assertion.clone(), - vc_index, - vc_hash, vc_payload, self.req.maybe_key, self.req.req_ext_hash, diff --git a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/lib.rs b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/lib.rs index 4155fd20e5..bda9c702eb 100644 --- a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/lib.rs +++ b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/lib.rs @@ -216,15 +216,9 @@ where call_index, response.assertion_request.who, response.assertion_request.assertion, - response.vc_index, - response.vc_hash, H256::zero(), )); - let res = RequestVCResult { - vc_index: response.vc_index, - vc_hash: response.vc_hash, - vc_payload: result, - }; + let res = RequestVCResult { vc_payload: result }; // This internally fetches nonce from a Mutex and then updates it thereby ensuring ordering let xt = extrinsic_factory .create_extrinsics(&[call], None) diff --git a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/vc_handling.rs b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/vc_handling.rs index 1ad285938d..ed16501cb0 100644 --- a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/vc_handling.rs +++ b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/vc_handling.rs @@ -9,7 +9,6 @@ use itp_sgx_externalities::SgxExternalitiesTrait; use itp_stf_executor::traits::StfEnclaveSigning; use itp_stf_state_handler::handle_state::HandleState; use itp_top_pool_author::traits::AuthorApi; -use itp_types::H256; use lc_data_providers::{DataProviderConfigReader, ReadDataProviderConfig}; use lc_stf_task_receiver::StfTaskContext; use lc_stf_task_sender::AssertionBuildRequest; @@ -18,7 +17,6 @@ use litentry_primitives::{ AmountHoldingTimeType, Assertion, ErrorDetail, ErrorString, Identity, ParameterString, VCMPError, }; -use sp_core::hashing::blake2_256; use std::{format, sync::Arc}; pub(crate) struct VCRequestHandler< @@ -133,8 +131,11 @@ where ErrorDetail::StfError(ErrorString::truncate_from(format!("{e:?}").into())), ) })?; - let payload = credential.issuer.mrenclave.clone(); - let (enclave_account, sig) = signer.sign_vc_with_self(payload.as_bytes()).map_err(|e| { + let json_string = credential.to_json().map_err(|_| { + VCMPError::RequestVCFailed(self.req.assertion.clone(), ErrorDetail::ParseError) + })?; + let payload = json_string.as_bytes(); + let (enclave_account, sig) = signer.sign(payload).map_err(|e| { VCMPError::RequestVCFailed( self.req.assertion.clone(), ErrorDetail::StfError(ErrorString::truncate_from(format!("{e:?}").into())), @@ -149,25 +150,13 @@ where ) })?; - let vc_index: H256 = credential - .get_index() - .map_err(|e| { - VCMPError::RequestVCFailed( - self.req.assertion.clone(), - ErrorDetail::StfError(ErrorString::truncate_from(format!("{e:?}").into())), - ) - })? - .into(); let credential_str = credential.to_json().map_err(|_| { VCMPError::RequestVCFailed(self.req.assertion.clone(), ErrorDetail::ParseError) })?; - let vc_hash: H256 = blake2_256(credential_str.as_bytes()).into(); let vc_response = VCResponse { assertion_request: self.req.clone(), - vc_hash, vc_payload: credential_str.as_bytes().to_vec(), - vc_index, }; Ok(vc_response) diff --git a/tee-worker/litentry/core/vc-issuance/lc-vc-task-sender/src/lib.rs b/tee-worker/litentry/core/vc-issuance/lc-vc-task-sender/src/lib.rs index 9ca4e36df8..7721ecde09 100644 --- a/tee-worker/litentry/core/vc-issuance/lc-vc-task-sender/src/lib.rs +++ b/tee-worker/litentry/core/vc-issuance/lc-vc-task-sender/src/lib.rs @@ -20,7 +20,7 @@ pub use crate::sgx_reexport_prelude::*; use codec::{Decode, Encode}; use futures::channel::oneshot; -use itp_types::{ShardIdentifier, H256}; +use itp_types::ShardIdentifier; use lazy_static::lazy_static; use lc_stf_task_sender::AssertionBuildRequest; use litentry_primitives::AesOutput; @@ -50,9 +50,7 @@ pub struct VCRequest { #[derive(Encode, Decode, Clone)] pub struct VCResponse { pub assertion_request: AssertionBuildRequest, - pub vc_hash: H256, pub vc_payload: Vec, - pub vc_index: H256, } pub type VcSender = Sender; diff --git a/tee-worker/ts-tests/integration-tests/common/utils/assertion.ts b/tee-worker/ts-tests/integration-tests/common/utils/assertion.ts index bc5853364e..a9cf3176ca 100644 --- a/tee-worker/ts-tests/integration-tests/common/utils/assertion.ts +++ b/tee-worker/ts-tests/integration-tests/common/utils/assertion.ts @@ -18,7 +18,6 @@ import { FrameSystemEventRecord, WorkerRpcReturnValue, RequestVCResult, - PalletVcManagementVcContext, StfError, } from 'parachain-api'; import { Bytes } from '@polkadot/types-codec'; @@ -26,6 +25,7 @@ import { Signer, decryptWithAes } from './crypto'; import { blake2AsHex } from '@polkadot/util-crypto'; import { PalletIdentityManagementTeeIdentityContext } from 'sidechain-api'; import { KeyObject } from 'crypto'; +import * as base58 from 'micro-base58'; export async function assertFailedEvent( context: IntegrationTestContext, @@ -244,64 +244,72 @@ export async function assertIdGraphMutationResult( export async function assertVc(context: IntegrationTestContext, subject: CorePrimitivesIdentity, data: Bytes) { const results = context.api.createType('RequestVCResult', data) as unknown as RequestVCResult; - const vcHash = results.vc_hash.toString(); - // step 1 - const vcIndex = results.vc_index.toString(); - const vcRegistry = (await context.api.query.vcManagement.vcRegistry( - vcIndex - )) as unknown as PalletVcManagementVcContext; - const vcStatus = vcRegistry.toHuman()['status']; - assert.equal(vcStatus, 'Active', 'Check VcRegistry error:status should be equal to Active'); - - // step 2 // decryptWithAes function added 0x prefix const vcPayload = results.vc_payload; const decryptVcPayload = decryptWithAes(aesKey, vcPayload, 'utf-8').replace('0x', ''); - const vcPayloadHash = blake2AsHex(Buffer.from(decryptVcPayload)); - assert.equal(vcPayloadHash, vcHash, 'Check VcPayload error: vcPayloadHash should be equal to vcHash'); - /* DID format did:litentry:substrate:0x12345... did:litentry:evm:0x123456... did:litentry:twitter:my_twitter_handle */ - // step 3 + // step 2 + // check credential subject's DID const credentialSubjectId = JSON.parse(decryptVcPayload).credentialSubject.id; const expectSubject = Object.entries(JSON.parse(subject.toString())); + // step 3 // convert to DID format const expectDid = 'did:litentry:' + expectSubject[0][0] + ':' + expectSubject[0][1]; assert.equal( expectDid, credentialSubjectId, - 'Check credentialSubjec error: expectDid should be equal to credentialSubject id' + 'Check credentialSubject error: expectDid should be equal to credentialSubject id' ); // step 4 + // extrac proof and vc without proof json const vcPayloadJson = JSON.parse(decryptVcPayload); const { proof, ...vcWithoutProof } = vcPayloadJson; - assert.equal(vcIndex, vcPayloadJson.id, 'Check VcIndex error: VcIndex should be equal to vcPayload id'); // step 5 - const enclaveCount = await context.api.query.teerex.enclaveCount(); + // prepare teerex enclave registry data for further checks + const parachainBlockHash = await context.api.query.system.blockHash(vcPayloadJson.parachainBlockNumber); + const apiAtVcIssuedBlock = await context.api.at(parachainBlockHash); + const enclaveCount = await apiAtVcIssuedBlock.query.teerex.enclaveCount(); - const enclaveRegistry = (await context.api.query.teerex.enclaveRegistry( - enclaveCount - )) as unknown as TeerexPrimitivesEnclave; + const lastRegisteredEnclave = (await apiAtVcIssuedBlock.query.teerex.enclaveRegistry(enclaveCount)) + .value as TeerexPrimitivesEnclave; + // step 6 + // check vc signature const signature = Buffer.from(hexToU8a(`0x${proof.proofValue}`)); - - const message = Buffer.from(vcWithoutProof.issuer.mrenclave); - - const vcPubkey = Buffer.from(hexToU8a(enclaveRegistry.toHuman()['vcPubkey'] as HexString)); + const message = Buffer.from(JSON.stringify(vcWithoutProof)); + const vcPubkey = Buffer.from(lastRegisteredEnclave.vcPubkey.value); const signatureStatus = await ed.verify(signature, message, vcPubkey); assert.isTrue(signatureStatus, 'Check Vc signature error: signature should be valid'); - // step 6 + // step 7 + // check VC mrenclave with enclave's mrenclave from registry + assert.equal( + base58.encode(lastRegisteredEnclave.mrEnclave), + vcPayloadJson.issuer.mrenclave, + 'Check VC mrenclave: it should equals enclaves mrenclave from parachains enclave registry' + ); + + // step 8 + // check vc issuer id + assert.equal( + `did:litentry:substrate:${lastRegisteredEnclave.vcPubkey.value.toHex()}`, + vcPayloadJson.issuer.id, + 'Check VC id: it should equals enclaves pubkey from parachains enclave registry' + ); + + // step 9 + // validate VC aganist schema const ajv = new Ajv(); const validate = ajv.compile(jsonSchema); diff --git a/tee-worker/ts-tests/integration-tests/common/utils/vc-helper.ts b/tee-worker/ts-tests/integration-tests/common/utils/vc-helper.ts index 1feeb3b883..9ad26dda1d 100644 --- a/tee-worker/ts-tests/integration-tests/common/utils/vc-helper.ts +++ b/tee-worker/ts-tests/integration-tests/common/utils/vc-helper.ts @@ -1,7 +1,4 @@ -export async function handleVcEvents( - events: any[], - method: 'VCIssued' | 'VCDisabled' | 'VCRevoked' | 'Failed' -): Promise { +export async function handleVcEvents(events: any[], method: 'VCIssued' | 'Failed'): Promise { const results: any = []; for (let k = 0; k < events.length; k++) { switch (method) { @@ -11,12 +8,6 @@ export async function handleVcEvents( index: events[k].data.index.toHex(), }); break; - case 'VCDisabled': - results.push(events[k].data.index.toHex()); - break; - case 'VCRevoked': - results.push(events[k].data.index.toHex()); - break; case 'Failed': results.push(events[k].data.detail.toHuman()); break; diff --git a/tee-worker/ts-tests/integration-tests/ii_vc.test.ts b/tee-worker/ts-tests/integration-tests/ii_vc.test.ts index d4b78418bb..516309ba65 100644 --- a/tee-worker/ts-tests/integration-tests/ii_vc.test.ts +++ b/tee-worker/ts-tests/integration-tests/ii_vc.test.ts @@ -1,8 +1,7 @@ -import { describeLitentry, handleVcEvents } from './common/utils'; +import { describeLitentry } from './common/utils'; import { step } from 'mocha-steps'; -import type { HexString } from '@polkadot/util/types'; import { assert } from 'chai'; -import { sendTxsWithUtility, sendTxUntilInBlockList } from './common/transactions'; +import { sendTxsWithUtility } from './common/transactions'; import { ApiTypes, SubmittableExtrinsic } from '@polkadot/api/types'; // TODO: keep the list short, the manual types will be solved in #1878 @@ -16,7 +15,6 @@ const allAssertions = [ // It doesn't make much difference test A1 only vs test A1 - A11, one VC type is enough. // So only use A1 to trigger the wrong event describeLitentry('VC ii test', async (context) => { - const indexList: HexString[] = []; step('Request VC', async () => { // request all vc const txs: { @@ -36,80 +34,6 @@ describeLitentry('VC ii test', async (context) => { ['VCIssued'], 30 ); - const res = await handleVcEvents(events, 'VCIssued'); - - for (let k = 0; k < res.length; k++) { - const registry = (await context.api.query.vcManagement.vcRegistry(res[k].index)) as any; - assert.equal(registry.toHuman()!['status'], 'Active', 'check registry error'); - indexList.push(res[k].index); - } - }); - step('Disable VC', async () => { - const txs: { - tx: SubmittableExtrinsic; - }[] = []; - for (let i = 0; i < indexList.length; i++) { - const tx = context.api.tx.vcManagement.disableVc(indexList[i]); - txs.push({ tx }); - } - const events = await sendTxsWithUtility(context, context.substrateWallet.alice, txs, 'vcManagement', [ - 'VCDisabled', - ]); - const res = await handleVcEvents(events, 'VCDisabled'); - - for (let k = 0; k < res.length; k++) { - assert.equal(res[k], indexList[k], 'check index error'); - const registry = (await context.api.query.vcManagement.vcRegistry(indexList[k])) as any; - assert.equal(registry.toHuman()!['status'], 'Disabled'); - } - }); - step('Disable error VC(A1)', async () => { - // Alice has already disabled the A1 VC - const tx = context.api.tx.vcManagement.disableVc(indexList[0]); - const nonce = (await context.api.rpc.system.accountNextIndex(context.substrateWallet.alice.address)).toNumber(); - - const [error] = await sendTxUntilInBlockList(context.api, [{ tx, nonce }], context.substrateWallet.alice); - - assert.equal( - error, - 'vcManagement.VCAlreadyDisabled', - 'check disable vc error: error should be equal to vcManagement.VCAlreadyDisabled' - ); - }); - - step('Revoke VC', async () => { - const txs: { - tx: SubmittableExtrinsic; - }[] = []; - - for (let i = 0; i < indexList.length; i++) { - const tx = context.api.tx.vcManagement.revokeVc(indexList[i]); - txs.push({ tx }); - } - const events = await sendTxsWithUtility(context, context.substrateWallet.alice, txs, 'vcManagement', [ - 'VCRevoked', - ]); - - const res = await handleVcEvents(events, 'VCRevoked'); - - for (let k = 0; k < indexList.length; k++) { - assert.equal(res[k], indexList[k], 'check index error'); - const registry = (await context.api.query.vcManagement.vcRegistry(indexList[k])) as any; - - assert.equal(registry.toHuman(), null); - } - }); - - step('Revoke Error VC(A1)', async () => { - // Alice has already revoked the A1 VC - const tx = context.api.tx.vcManagement.revokeVc(indexList[0]); - const nonce = (await context.api.rpc.system.accountNextIndex(context.substrateWallet.alice.address)).toNumber(); - const [error] = await sendTxUntilInBlockList(context.api, [{ tx, nonce }], context.substrateWallet.alice); - - assert.equal( - error, - 'vcManagement.VCNotExist', - 'check revoke vc error: error should be equal to vcManagement.VCNotExist' - ); + assert.equal(events.length, 1); }); }); From 9b0fad8cb6b599b5c458d3e4a605bacd96006228 Mon Sep 17 00:00:00 2001 From: Zhouhui Tian <125243011+zhouhuitian@users.noreply.github.com> Date: Mon, 22 Jan 2024 05:23:06 +0800 Subject: [PATCH 13/51] Register enclave with DCAP feature in SW mode (#2408) * register_enclave * make fmt --------- Co-authored-by: BillyWooo --- tee-worker/enclave-runtime/src/attestation.rs | 82 +++++++++++-------- 1 file changed, 46 insertions(+), 36 deletions(-) diff --git a/tee-worker/enclave-runtime/src/attestation.rs b/tee-worker/enclave-runtime/src/attestation.rs index 45f3a4b2ef..5b7f7ded3a 100644 --- a/tee-worker/enclave-runtime/src/attestation.rs +++ b/tee-worker/enclave-runtime/src/attestation.rs @@ -58,7 +58,6 @@ use sgx_types::*; use sp_core::Pair; use sp_runtime::OpaqueExtrinsic; use std::{prelude::v1::*, slice, vec::Vec}; -use teerex_primitives::SgxAttestationMethod; #[no_mangle] pub unsafe extern "C" fn get_mrenclave(mrenclave: *mut u8, mrenclave_size: usize) -> sgx_status_t { @@ -322,12 +321,11 @@ pub fn generate_dcap_ra_extrinsic_from_quote_internal( .get_from_metadata(|m| m.register_enclave_call_indexes())? .map_err(MetadataProviderError::MetadataError)?; info!(" [Enclave] Compose register enclave call DCAP IDs: {:?}", call_ids); - let call = OpaqueCall::from_tuple(&( - call_ids, - quote, - Some(url), - SgxAttestationMethod::Dcap { proxied: false }, - )); + + let shielding_pubkey = get_shielding_pubkey()?; + let vc_pubkey = get_vc_pubkey()?; + + let call = OpaqueCall::from_tuple(&(call_ids, quote, url, shielding_pubkey, vc_pubkey)); info!(" [Enclave] Compose register enclave got extrinsic, returning"); create_extrinsics(call) @@ -344,12 +342,11 @@ pub fn generate_dcap_skip_ra_extrinsic_from_mr_enclave( .get_from_metadata(|m| m.register_enclave_call_indexes())? .map_err(MetadataProviderError::MetadataError)?; info!(" [Enclave] Compose register enclave (skip-ra) call DCAP IDs: {:?}", call_ids); - let call = OpaqueCall::from_tuple(&( - call_ids, - quote, - Some(url), - SgxAttestationMethod::Skip { proxied: false }, - )); + + let shielding_pubkey = get_shielding_pubkey()?; + let vc_pubkey = get_vc_pubkey()?; + + let call = OpaqueCall::from_tuple(&(call_ids, quote, url, shielding_pubkey, vc_pubkey)); info!(" [Enclave] Compose register enclave (skip-ra) got extrinsic, returning"); create_extrinsics(call) @@ -376,29 +373,8 @@ pub fn generate_ias_ra_extrinsic_from_der_cert_internal( .get_from_metadata(|m| m.register_enclave_call_indexes())? .map_err(MetadataProviderError::MetadataError)?; - let shielding_pubkey = GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT - .get()? - .retrieve_key() - .and_then(|keypair| { - keypair - .export_pubkey() - .and_then(|pubkey| { - serde_json::to_vec(&pubkey).map_err(|e| SgxCryptoError::Serialization(e).into()) - }) - .map_err(|e| SgxCryptoError::Other(Box::new(e))) - }) - .ok(); - debug!("[Enclave] shielding_pubkey size: {:?}", shielding_pubkey.clone().map(|key| key.len())); - - let vc_pubkey = GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT - .get()? - .retrieve_key() - .and_then(|keypair| { - // vc signing pubkey - keypair.derive_ed25519().map(|keypair| keypair.public().to_vec()) - }) - .ok(); - debug!("[Enclave] VC pubkey: {:?}", vc_pubkey); + let shielding_pubkey = get_shielding_pubkey()?; + let vc_pubkey = get_vc_pubkey()?; let call = OpaqueCall::from_tuple(&(call_ids, cert_der, url, shielding_pubkey, vc_pubkey)); @@ -542,3 +518,37 @@ pub unsafe extern "C" fn dump_dcap_collateral_to_disk( collateral.dump_to_disk(); sgx_status_t::SGX_SUCCESS } + +fn get_shielding_pubkey() -> EnclaveResult>> { + let shielding_pubkey = GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT + .get()? + .retrieve_key() + .and_then(|keypair| { + keypair + .export_pubkey() + .and_then(|pubkey| { + serde_json::to_vec(&pubkey).map_err(|e| SgxCryptoError::Serialization(e).into()) + }) + .map_err(|e| SgxCryptoError::Other(Box::new(e))) + }) + .ok(); + + debug!("[Enclave] shielding_pubkey size: {:?}", shielding_pubkey.clone().map(|key| key.len())); + + Ok(shielding_pubkey) +} + +fn get_vc_pubkey() -> EnclaveResult>> { + let vc_pubkey = GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT + .get()? + .retrieve_key() + .and_then(|keypair| { + // vc signing pubkey + keypair.derive_ed25519().map(|keypair| keypair.public().to_vec()) + }) + .ok(); + + debug!("[Enclave] VC pubkey: {:?}", vc_pubkey); + + Ok(vc_pubkey) +} From b27c8640be8e5e8deea1a53aeb99439659df5e36 Mon Sep 17 00:00:00 2001 From: Zhouhui Tian <125243011+zhouhuitian@users.noreply.github.com> Date: Mon, 22 Jan 2024 11:04:38 +0800 Subject: [PATCH 14/51] Fix build error while attesteer feature enabled (#2407) * make build * updates --------- Co-authored-by: BillyWooo --- tee-worker/service/src/main_impl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tee-worker/service/src/main_impl.rs b/tee-worker/service/src/main_impl.rs index 019fbdfe2e..184b0b9921 100644 --- a/tee-worker/service/src/main_impl.rs +++ b/tee-worker/service/src/main_impl.rs @@ -934,7 +934,7 @@ fn register_quotes_from_marblerun( marblerun_base_url: &str, ) { let enclave = enclave.as_ref(); - let events = prometheus_metrics::fetch_marblerun_events(marblerun_base_url) + let events = crate::prometheus_metrics::fetch_marblerun_events(marblerun_base_url) .map_err(|e| { info!("Fetching events from Marblerun failed with: {:?}, continuing with 0 events.", e); }) From 8969e7e2dc12ab683c4c10efe5806342f6781512 Mon Sep 17 00:00:00 2001 From: Kasper Ziemianek Date: Mon, 22 Jan 2024 09:05:03 +0100 Subject: [PATCH 15/51] Manual tests of removing the second worker during running (#2316) * resuming worker tests * move resuming worker test to separate workspace * correct script name * add workspace * fix evm build * lock file * fix package.json * missing worker workspace deps * adjust bitcoin identity test docker compose * fix test * remove unwanted paths from tsconfig.json * add config * add tracing * add more dev deps * debug * remove problematic function * debugging * add withRetry * remove sleep --------- Co-authored-by: Zhouhui Tian <125243011+zhouhuitian@users.noreply.github.com> --- .github/workflows/ci.yml | 3 +- ..._ts_test.sh => lit_ts_integration_test.sh} | 2 +- tee-worker/cli/lit_ts_worker_test.sh | 25 ++++ .../docker/lit-di-bitcoin-identity-test.yml | 2 +- .../lit-di-evm-identity-multiworker-test.yml | 2 +- .../docker/lit-di-evm-identity-test.yml | 2 +- ...di-substrate-identity-multiworker-test.yml | 2 +- .../docker/lit-di-substrate-identity-test.yml | 2 +- .../docker/lit-di-vc-multiworker-test.yml | 2 +- tee-worker/docker/lit-di-vc-test.yml | 2 +- .../docker/lit-ii-batch-test-multiworker.yml | 2 +- tee-worker/docker/lit-ii-batch-test.yml | 2 +- .../lit-ii-identity-multiworker-test.yml | 2 +- tee-worker/docker/lit-ii-identity-test.yml | 2 +- .../docker/lit-ii-vc-multiworker-test.yml | 2 +- tee-worker/docker/lit-ii-vc-test.yml | 2 +- tee-worker/docker/lit-resume-worker.yml | 5 +- tee-worker/docker/lit-test-stress-script.yml | 2 +- .../integration-tests/common/utils/crypto.ts | 2 +- .../ts-tests/integration-tests/package.json | 2 - tee-worker/ts-tests/pnpm-lock.yaml | 130 ++++++++++++++++++ tee-worker/ts-tests/pnpm-workspace.yaml | 2 +- tee-worker/ts-tests/worker/.env.local.example | 3 + tee-worker/ts-tests/worker/.env.staging | 4 + tee-worker/ts-tests/worker/.eslintrc.json | 40 ++++++ tee-worker/ts-tests/worker/package.json | 58 ++++++++ .../resuming_worker.test.ts | 43 ++++-- tee-worker/ts-tests/worker/tsconfig.json | 19 +++ 28 files changed, 333 insertions(+), 33 deletions(-) rename tee-worker/cli/{lit_ts_test.sh => lit_ts_integration_test.sh} (94%) create mode 100755 tee-worker/cli/lit_ts_worker_test.sh create mode 100644 tee-worker/ts-tests/worker/.env.local.example create mode 100644 tee-worker/ts-tests/worker/.env.staging create mode 100644 tee-worker/ts-tests/worker/.eslintrc.json create mode 100644 tee-worker/ts-tests/worker/package.json rename tee-worker/ts-tests/{integration-tests => worker}/resuming_worker.test.ts (91%) create mode 100644 tee-worker/ts-tests/worker/tsconfig.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2ab68e47e7..cfe52a969b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -625,8 +625,6 @@ jobs: - test_name: lit-parentchain-nonce - test_name: lit-ii-batch-test - test_name: lit-test-stress-script - # disabled due to P-208 - # - test_name: lit-resume-worker steps: - uses: actions/checkout@v4 @@ -709,6 +707,7 @@ jobs: - test_name: lit-ii-batch-test-multiworker - test_name: lit-ii-identity-multiworker-test - test_name: lit-ii-vc-multiworker-test + - test_name: lit-resume-worker steps: - uses: actions/checkout@v4 diff --git a/tee-worker/cli/lit_ts_test.sh b/tee-worker/cli/lit_ts_integration_test.sh similarity index 94% rename from tee-worker/cli/lit_ts_test.sh rename to tee-worker/cli/lit_ts_integration_test.sh index 718cf86cc4..bdce639176 100755 --- a/tee-worker/cli/lit_ts_test.sh +++ b/tee-worker/cli/lit_ts_integration_test.sh @@ -40,7 +40,7 @@ CLIENT="${CLIENT_BIN} -p ${NPORT} -P ${WORKER1PORT} -u ${NODEURL} -U ${WORKER1UR function usage() { echo "" - echo "This is a script for tee-worker ts-test. Preparing to test: $1" + echo "This is a script for tee-worker integration ts tests. Pass test name as first argument" echo "" } diff --git a/tee-worker/cli/lit_ts_worker_test.sh b/tee-worker/cli/lit_ts_worker_test.sh new file mode 100755 index 0000000000..8528b59691 --- /dev/null +++ b/tee-worker/cli/lit_ts_worker_test.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# Copyright 2020-2023 Trust Computing GmbH. + +set -euo pipefail + +function usage() { + echo "" + echo "This is a script for tee-worker worker ts-test. Pass test name as first argument" + echo "" +} + +[ $# -ne 1 ] && (usage; exit 1) +TEST=$1 + + +BINARY_DIR="/usr/local/bin" +NODE_ENDPOINT="ws://litentry-node:9912" + +echo "Using binary dir: $BINARY_DIR" +echo "Using node endpoint: $NODE_ENDPOINT" + +cd /ts-tests +pnpm install +pnpm --filter worker run $TEST:staging diff --git a/tee-worker/docker/lit-di-bitcoin-identity-test.yml b/tee-worker/docker/lit-di-bitcoin-identity-test.yml index 8a4b5a2c59..9cc7e41c35 100644 --- a/tee-worker/docker/lit-di-bitcoin-identity-test.yml +++ b/tee-worker/docker/lit-di-bitcoin-identity-test.yml @@ -17,7 +17,7 @@ services: condition: service_healthy networks: - litentry-test-network - entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_test.sh test-di-bitcoin-identity 2>&1' " + entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_integration_test.sh test-di-bitcoin-identity 2>&1' " restart: "no" networks: litentry-test-network: diff --git a/tee-worker/docker/lit-di-evm-identity-multiworker-test.yml b/tee-worker/docker/lit-di-evm-identity-multiworker-test.yml index dbceb71a7a..4649e93fdb 100644 --- a/tee-worker/docker/lit-di-evm-identity-multiworker-test.yml +++ b/tee-worker/docker/lit-di-evm-identity-multiworker-test.yml @@ -21,7 +21,7 @@ services: condition: service_healthy networks: - litentry-test-network - entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_test.sh test-di-evm-identity 2>&1' " + entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_integration_test.sh test-di-evm-identity 2>&1' " restart: "no" networks: litentry-test-network: diff --git a/tee-worker/docker/lit-di-evm-identity-test.yml b/tee-worker/docker/lit-di-evm-identity-test.yml index 970580e7a7..7938ca67a8 100644 --- a/tee-worker/docker/lit-di-evm-identity-test.yml +++ b/tee-worker/docker/lit-di-evm-identity-test.yml @@ -17,7 +17,7 @@ services: condition: service_healthy networks: - litentry-test-network - entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_test.sh test-di-evm-identity 2>&1' " + entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_integration_test.sh test-di-evm-identity 2>&1' " restart: "no" networks: litentry-test-network: diff --git a/tee-worker/docker/lit-di-substrate-identity-multiworker-test.yml b/tee-worker/docker/lit-di-substrate-identity-multiworker-test.yml index 53eb542803..f5647624b9 100644 --- a/tee-worker/docker/lit-di-substrate-identity-multiworker-test.yml +++ b/tee-worker/docker/lit-di-substrate-identity-multiworker-test.yml @@ -21,7 +21,7 @@ services: condition: service_healthy networks: - litentry-test-network - entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_test.sh test-di-substrate-identity 2>&1' " + entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_integration_test.sh test-di-substrate-identity 2>&1' " restart: "no" networks: litentry-test-network: diff --git a/tee-worker/docker/lit-di-substrate-identity-test.yml b/tee-worker/docker/lit-di-substrate-identity-test.yml index 3cb262127f..3f1c8250c7 100644 --- a/tee-worker/docker/lit-di-substrate-identity-test.yml +++ b/tee-worker/docker/lit-di-substrate-identity-test.yml @@ -17,7 +17,7 @@ services: condition: service_healthy networks: - litentry-test-network - entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_test.sh test-di-substrate-identity 2>&1' " + entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_integration_test.sh test-di-substrate-identity 2>&1' " restart: "no" networks: litentry-test-network: diff --git a/tee-worker/docker/lit-di-vc-multiworker-test.yml b/tee-worker/docker/lit-di-vc-multiworker-test.yml index 18a694893c..08bb708212 100644 --- a/tee-worker/docker/lit-di-vc-multiworker-test.yml +++ b/tee-worker/docker/lit-di-vc-multiworker-test.yml @@ -21,7 +21,7 @@ services: condition: service_healthy networks: - litentry-test-network - entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_test.sh test-di-vc 2>&1' " + entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_integration_test.sh test-di-vc 2>&1' " restart: "no" networks: litentry-test-network: diff --git a/tee-worker/docker/lit-di-vc-test.yml b/tee-worker/docker/lit-di-vc-test.yml index 8a5fb637d2..a41ebfc457 100644 --- a/tee-worker/docker/lit-di-vc-test.yml +++ b/tee-worker/docker/lit-di-vc-test.yml @@ -17,7 +17,7 @@ services: condition: service_healthy networks: - litentry-test-network - entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_test.sh test-di-vc 2>&1' " + entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_integration_test.sh test-di-vc 2>&1' " restart: "no" networks: litentry-test-network: diff --git a/tee-worker/docker/lit-ii-batch-test-multiworker.yml b/tee-worker/docker/lit-ii-batch-test-multiworker.yml index e182d454af..6d26ecea5d 100644 --- a/tee-worker/docker/lit-ii-batch-test-multiworker.yml +++ b/tee-worker/docker/lit-ii-batch-test-multiworker.yml @@ -21,7 +21,7 @@ services: condition: service_healthy networks: - litentry-test-network - entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_test.sh test-ii-batch 2>&1' " + entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_integration_test.sh test-ii-batch 2>&1' " restart: "no" networks: litentry-test-network: diff --git a/tee-worker/docker/lit-ii-batch-test.yml b/tee-worker/docker/lit-ii-batch-test.yml index f5fb42b428..f0a6ee9fbf 100644 --- a/tee-worker/docker/lit-ii-batch-test.yml +++ b/tee-worker/docker/lit-ii-batch-test.yml @@ -17,7 +17,7 @@ services: condition: service_healthy networks: - litentry-test-network - entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_test.sh test-ii-batch 2>&1' " + entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_integration_test.sh test-ii-batch 2>&1' " restart: "no" networks: litentry-test-network: diff --git a/tee-worker/docker/lit-ii-identity-multiworker-test.yml b/tee-worker/docker/lit-ii-identity-multiworker-test.yml index 041e20ebd6..684f832367 100644 --- a/tee-worker/docker/lit-ii-identity-multiworker-test.yml +++ b/tee-worker/docker/lit-ii-identity-multiworker-test.yml @@ -21,7 +21,7 @@ services: condition: service_healthy networks: - litentry-test-network - entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_test.sh test-ii-identity 2>&1' " + entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_integration_test.sh test-ii-identity 2>&1' " restart: "no" networks: litentry-test-network: diff --git a/tee-worker/docker/lit-ii-identity-test.yml b/tee-worker/docker/lit-ii-identity-test.yml index 5f2011f1ed..874b3dad67 100644 --- a/tee-worker/docker/lit-ii-identity-test.yml +++ b/tee-worker/docker/lit-ii-identity-test.yml @@ -17,7 +17,7 @@ services: condition: service_healthy networks: - litentry-test-network - entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_test.sh test-ii-identity 2>&1' " + entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_integration_test.sh test-ii-identity 2>&1' " restart: "no" networks: litentry-test-network: diff --git a/tee-worker/docker/lit-ii-vc-multiworker-test.yml b/tee-worker/docker/lit-ii-vc-multiworker-test.yml index f762375df9..f84a6e52a2 100644 --- a/tee-worker/docker/lit-ii-vc-multiworker-test.yml +++ b/tee-worker/docker/lit-ii-vc-multiworker-test.yml @@ -21,7 +21,7 @@ services: condition: service_healthy networks: - litentry-test-network - entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_test.sh test-ii-vc 2>&1' " + entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_integration_test.sh test-ii-vc 2>&1' " restart: "no" networks: litentry-test-network: diff --git a/tee-worker/docker/lit-ii-vc-test.yml b/tee-worker/docker/lit-ii-vc-test.yml index d72852b81b..f530e499f8 100644 --- a/tee-worker/docker/lit-ii-vc-test.yml +++ b/tee-worker/docker/lit-ii-vc-test.yml @@ -17,7 +17,7 @@ services: condition: service_healthy networks: - litentry-test-network - entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_test.sh test-ii-vc 2>&1' " + entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_integration_test.sh test-ii-vc 2>&1' " restart: "no" networks: litentry-test-network: diff --git a/tee-worker/docker/lit-resume-worker.yml b/tee-worker/docker/lit-resume-worker.yml index c1beb8c6ce..319d426d2f 100644 --- a/tee-worker/docker/lit-resume-worker.yml +++ b/tee-worker/docker/lit-resume-worker.yml @@ -15,9 +15,8 @@ services: condition: service_healthy networks: - litentry-test-network - entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_test.sh test-resuming-worker 2>&1' " + entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_worker_test.sh test-resuming-worker 2>&1' " restart: "no" networks: litentry-test-network: - driver: bridge -# docker compose -f docker-compose.yml -f resume-worker.yml up relaychain-alice relaychain-bob litentry-node --no-build --exit-code-from resume-worker -- resume-worker + driver: bridge \ No newline at end of file diff --git a/tee-worker/docker/lit-test-stress-script.yml b/tee-worker/docker/lit-test-stress-script.yml index 00b08d3d68..735670a852 100644 --- a/tee-worker/docker/lit-test-stress-script.yml +++ b/tee-worker/docker/lit-test-stress-script.yml @@ -17,7 +17,7 @@ services: condition: service_healthy networks: - litentry-test-network - entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_test.sh test-stress-script 2>&1' " + entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_integration_test.sh test-stress-script 2>&1' " restart: "no" networks: litentry-test-network: diff --git a/tee-worker/ts-tests/integration-tests/common/utils/crypto.ts b/tee-worker/ts-tests/integration-tests/common/utils/crypto.ts index d6c6b4e306..333c637fc4 100644 --- a/tee-worker/ts-tests/integration-tests/common/utils/crypto.ts +++ b/tee-worker/ts-tests/integration-tests/common/utils/crypto.ts @@ -7,7 +7,7 @@ import { KeyringPair } from '@polkadot/keyring/types'; import { ethers } from 'ethers'; import { blake2AsU8a } from '@polkadot/util-crypto'; import bitcore from 'bitcore-lib'; -import { IntegrationTestContext } from 'common/common-types'; +import { IntegrationTestContext } from './../common-types'; import { buildIdentityHelper } from './identity-helper'; export type KeypairType = 'ed25519' | 'sr25519' | 'ecdsa' | 'ethereum' | 'bitcoin'; diff --git a/tee-worker/ts-tests/integration-tests/package.json b/tee-worker/ts-tests/integration-tests/package.json index 40ecbaaf8f..fbe5595223 100644 --- a/tee-worker/ts-tests/integration-tests/package.json +++ b/tee-worker/ts-tests/integration-tests/package.json @@ -15,8 +15,6 @@ "test-di-bitcoin-identity:staging": "pnpm exec eslint . && pnpm exec cross-env NODE_ENV=staging mocha --exit --sort -r ts-node/register --loader=ts-node/esm 'di_bitcoin_identity.test.ts'", "test-di-vc:local": "pnpm exec eslint . && pnpm exec cross-env NODE_ENV=local mocha --exit --sort -r ts-node/register --loader=ts-node/esm 'di_vc.test.ts'", "test-di-vc:staging": "pnpm exec eslint . && pnpm exec cross-env NODE_ENV=staging mocha --exit --sort -r ts-node/register --loader=ts-node/esm 'di_vc.test.ts'", - "test-resuming-worker:staging": "pnpm exec eslint . && pnpm exec cross-env NODE_ENV=staging mocha --exit --sort -r ts-node/register --loader=ts-node/esm 'resuming_worker.test.ts'", - "test-resuming-worker:local": "pnpm exec eslint . && pnpm exec cross-env NODE_ENV=local mocha --exit --sort -r ts-node/register --loader=ts-node/esm 'resuming_worker.test.ts'", "test-ii-vc:local": "pnpm exec eslint . && pnpm exec cross-env NODE_ENV=local mocha --exit --sort -r ts-node/register --loader=ts-node/esm 'ii_vc.test.ts'", "test-ii-vc:staging": "pnpm exec eslint . && pnpm exec cross-env NODE_ENV=staging mocha --exit --sort -r ts-node/register --loader=ts-node/esm 'ii_vc.test.ts'", "test-ii-batch:local": "pnpm exec eslint . && pnpm exec cross-env NODE_ENV=local mocha --exit --sort -r ts-node/register --loader=ts-node/esm 'ii_batch.test.ts'", diff --git a/tee-worker/ts-tests/pnpm-lock.yaml b/tee-worker/ts-tests/pnpm-lock.yaml index f6605b0ca5..da37548f54 100644 --- a/tee-worker/ts-tests/pnpm-lock.yaml +++ b/tee-worker/ts-tests/pnpm-lock.yaml @@ -244,6 +244,136 @@ importers: specifier: 5.0.4 version: 5.0.4 + worker: + dependencies: + '@noble/ed25519': + specifier: ^1.7.3 + version: 1.7.3 + '@polkadot/api': + specifier: ^10.9.1 + version: 10.9.1 + '@polkadot/api-augment': + specifier: ^10.9.1 + version: 10.9.1 + '@polkadot/api-base': + specifier: ^10.9.1 + version: 10.9.1 + '@polkadot/api-derive': + specifier: ^10.9.1 + version: 10.9.1 + '@polkadot/keyring': + specifier: ^12.2.1 + version: 12.5.1(@polkadot/util-crypto@12.5.1)(@polkadot/util@12.5.1) + '@polkadot/rpc-core': + specifier: ^10.9.1 + version: 10.9.1 + '@polkadot/types': + specifier: ^10.9.1 + version: 10.9.1 + '@polkadot/types-augment': + specifier: ^10.9.1 + version: 10.9.1 + '@polkadot/types-codec': + specifier: ^10.9.1 + version: 10.9.1 + '@polkadot/types-create': + specifier: ^10.9.1 + version: 10.9.1 + '@polkadot/types-known': + specifier: ^10.9.1 + version: 10.9.1 + '@polkadot/types-support': + specifier: ^10.9.1 + version: 10.9.1 + '@polkadot/util': + specifier: ^12.5.1 + version: 12.5.1 + '@polkadot/util-crypto': + specifier: ^12.5.1 + version: 12.5.1(@polkadot/util@12.5.1) + add: + specifier: ^2.0.6 + version: 2.0.6 + ajv: + specifier: ^8.12.0 + version: 8.12.0 + bitcore-lib: + specifier: ^10.0.21 + version: 10.0.21 + chai: + specifier: ^4.3.6 + version: 4.3.10 + colors: + specifier: ^1.4.0 + version: 1.4.0 + js-base64: + specifier: ^3.7.5 + version: 3.7.5 + micro-base58: + specifier: ^0.5.1 + version: 0.5.1 + mocha: + specifier: ^10.1.0 + version: 10.2.0 + mocha-steps: + specifier: ^1.3.0 + version: 1.3.0 + scale-ts: + specifier: ^0.2.11 + version: 0.2.12 + websocket-as-promised: + specifier: ^2.0.1 + version: 2.0.1 + ws: + specifier: ^8.8.1 + version: 8.14.2 + devDependencies: + '@ethersproject/providers': + specifier: ^5.7.2 + version: 5.7.2 + '@types/bitcore-lib': + specifier: ^0.15.1 + version: 0.15.6 + '@types/chai': + specifier: ^4.3.3 + version: 4.3.6 + '@types/mocha': + specifier: ^10.0.0 + version: 10.0.2 + '@types/node': + specifier: ^20.4.4 + version: 20.7.1 + '@types/ws': + specifier: ^8.5.3 + version: 8.5.6 + '@typescript-eslint/eslint-plugin': + specifier: ^5.60.0 + version: 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.50.0)(typescript@5.0.4) + '@typescript-eslint/parser': + specifier: ^5.60.0 + version: 5.62.0(eslint@8.50.0)(typescript@5.0.4) + cross-env: + specifier: ^7.0.3 + version: 7.0.3 + dotenv: + specifier: ^16.0.3 + version: 16.3.1 + eslint: + specifier: ^8.43.0 + version: 8.50.0 + ethers: + specifier: ^5.7.2 + version: 5.7.2 + prettier: + specifier: 2.8.1 + version: 2.8.1 + ts-node: + specifier: ^10.9.1 + version: 10.9.1(@types/node@20.7.1)(typescript@5.0.4) + typescript: + specifier: 5.0.4 + version: 5.0.4 + packages: /@aashutoshrathi/word-wrap@1.2.6: diff --git a/tee-worker/ts-tests/pnpm-workspace.yaml b/tee-worker/ts-tests/pnpm-workspace.yaml index 7b9194120a..5593dc92b9 100644 --- a/tee-worker/ts-tests/pnpm-workspace.yaml +++ b/tee-worker/ts-tests/pnpm-workspace.yaml @@ -1 +1 @@ -packages: ["integration-tests", "stress"] +packages: ["integration-tests", "stress", "worker"] diff --git a/tee-worker/ts-tests/worker/.env.local.example b/tee-worker/ts-tests/worker/.env.local.example new file mode 100644 index 0000000000..76543e7d79 --- /dev/null +++ b/tee-worker/ts-tests/worker/.env.local.example @@ -0,0 +1,3 @@ +NODE_ENV = local +NODE_ENDPOINT = "ws://localhost:9944" +BINARY_DIR=/usr/local/bin \ No newline at end of file diff --git a/tee-worker/ts-tests/worker/.env.staging b/tee-worker/ts-tests/worker/.env.staging new file mode 100644 index 0000000000..4ac611a373 --- /dev/null +++ b/tee-worker/ts-tests/worker/.env.staging @@ -0,0 +1,4 @@ +NODE_ENV=staging +NODE_ENDPOINT="ws://litentry-node:9912" +NODE_TLS_REJECT_UNAUTHORIZED=0 +BINARY_DIR=/usr/local/bin \ No newline at end of file diff --git a/tee-worker/ts-tests/worker/.eslintrc.json b/tee-worker/ts-tests/worker/.eslintrc.json new file mode 100644 index 0000000000..0e93a1188c --- /dev/null +++ b/tee-worker/ts-tests/worker/.eslintrc.json @@ -0,0 +1,40 @@ +{ + "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"], + "root": true, + "rules": { + /** + It's a temporary solution, folks. We had no choice but to shut it off, + because there's just a liiittle bit too much "any" lurking around in the code. + But fear not, my friends, for this is not the end of the story. + We shall return, armed with determination and resolve, + to tackle those "any" types head-on in the near future. + **/ + "@typescript-eslint/no-explicit-any": ["off"], + "@typescript-eslint/no-non-null-assertion": ["off"], + "@typescript-eslint/no-var-requires": ["off"], + + // explanation: https://typescript-eslint.io/rules/naming-convention/ + "@typescript-eslint/naming-convention": [ + "error", + { + "selector": "typeLike", + "format": ["StrictPascalCase"] + }, + { + "selector": "variable", + "modifiers": ["const"], + "format": ["strictCamelCase", "UPPER_CASE"] + }, + { + "selector": "function", + "format": ["strictCamelCase", "StrictPascalCase"] + }, + { + "selector": "parameter", + "format": ["strictCamelCase"] + } + ] + } +} diff --git a/tee-worker/ts-tests/worker/package.json b/tee-worker/ts-tests/worker/package.json new file mode 100644 index 0000000000..2113d20611 --- /dev/null +++ b/tee-worker/ts-tests/worker/package.json @@ -0,0 +1,58 @@ +{ + "name": "worker", + "license": "ISC", + "type": "module", + "scripts": { + "format": "pnpm exec prettier --write .", + "check-format": "pnpm exec prettier --check .", + "test-resuming-worker:staging": "pnpm exec eslint . && pnpm exec cross-env NODE_ENV=staging mocha --trace-warnings --full-trace --exit --sort -r ts-node/register --loader=ts-node/esm 'resuming_worker.test.ts'", + "test-resuming-worker:local": "pnpm exec eslint . && pnpm exec cross-env NODE_ENV=local mocha --exit --sort -r ts-node/register --loader=ts-node/esm 'resuming_worker.test.ts'" + }, + "dependencies": { + "chai": "^4.3.6", + "micro-base58": "^0.5.1", + "mocha": "^10.1.0", + "mocha-steps": "^1.3.0", + "@polkadot/util": "^12.5.1", + "websocket-as-promised": "^2.0.1", + "@noble/ed25519": "^1.7.3", + "@polkadot/api": "^10.9.1", + "@polkadot/api-augment": "^10.9.1", + "@polkadot/api-base": "^10.9.1", + "@polkadot/api-derive": "^10.9.1", + "@polkadot/keyring": "^12.2.1", + "@polkadot/rpc-core": "^10.9.1", + "@polkadot/types": "^10.9.1", + "@polkadot/types-augment": "^10.9.1", + "@polkadot/types-codec": "^10.9.1", + "@polkadot/types-create": "^10.9.1", + "@polkadot/types-known": "^10.9.1", + "@polkadot/types-support": "^10.9.1", + "@polkadot/util-crypto": "^12.5.1", + "add": "^2.0.6", + "ajv": "^8.12.0", + "bitcore-lib": "^10.0.21", + "colors": "^1.4.0", + "js-base64": "^3.7.5", + "scale-ts": "^0.2.11", + "ws": "^8.8.1" + }, + "devDependencies": { + "@ethersproject/providers": "^5.7.2", + "@types/bitcore-lib": "^0.15.1", + "@types/chai": "^4.3.3", + "@types/mocha": "^10.0.0", + "@types/node": "^20.4.4", + "@types/ws": "^8.5.3", + "@typescript-eslint/eslint-plugin": "^5.60.0", + "@typescript-eslint/parser": "^5.60.0", + "cross-env": "^7.0.3", + "dotenv": "^16.0.3", + "eslint": "^8.43.0", + "ethers": "^5.7.2", + "prettier": "2.8.1", + "ts-node": "^10.9.1", + "typescript": "5.0.4" + }, + "packageManager": "pnpm@8.7.6" +} diff --git a/tee-worker/ts-tests/integration-tests/resuming_worker.test.ts b/tee-worker/ts-tests/worker/resuming_worker.test.ts similarity index 91% rename from tee-worker/ts-tests/integration-tests/resuming_worker.test.ts rename to tee-worker/ts-tests/worker/resuming_worker.test.ts index 535ebb5399..62a12a7c9d 100644 --- a/tee-worker/ts-tests/integration-tests/resuming_worker.test.ts +++ b/tee-worker/ts-tests/worker/resuming_worker.test.ts @@ -1,14 +1,21 @@ +import dotenv from 'dotenv'; import { ChildProcess, spawn } from 'child_process'; import * as readline from 'readline'; import fs from 'fs'; import * as path from 'path'; import * as process from 'process'; -import { step, xstep } from 'mocha-steps'; +import { step } from 'mocha-steps'; import WebSocketAsPromised from 'websocket-as-promised'; import os from 'os'; -import { initWorkerConnection, sleep } from './common/utils'; import { assert } from 'chai'; import type { HexString } from '@polkadot/util/types'; +import * as base58 from 'micro-base58'; +import { u8aToHex } from '@polkadot/util'; +import Options from 'websocket-as-promised/types/options'; +import WebSocket from 'ws'; + +// eslint-disable-next-line @typescript-eslint/no-var-requires, no-undef +dotenv.config({ path: `.env.${process.env.NODE_ENV}` }); type WorkerConfig = { name: string; @@ -168,7 +175,7 @@ async function spawnWorkerJob( const errorStream = readline.createInterface(job.stderr); errorStream.on('line', (line: string) => { console.warn(name, line); - if (line.includes('lock file: sidechain_db/LOCK: Resource temporarily unavailable')) { + if (line.includes('sidechain_db/LOCK: Resource temporarily unavailable')) { reject(new SidechainDbLockUnavailable()); } }); @@ -178,7 +185,7 @@ async function spawnWorkerJob( outputStream.on('line', (line: string) => { console.log(name, line); - const match = line.match(/^Successfully initialized shard (?0x[\w\d]{64}).*/); + const match = line.match(/^Successfully initialized shard "(?[\w].*).*"/); if (match !== null) { /** * Assertions needed because regex contents aren't reflected in function typing; @@ -188,7 +195,8 @@ async function spawnWorkerJob( * as well as the corresponding named capturing groups. See * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match */ - shard = match.groups!.shard as HexString; + const base58shard = match.groups!.shard; + shard = u8aToHex(base58.decode(base58shard)) as HexString; return; } @@ -389,8 +397,7 @@ describe('Resume worker', function () { let worker1State: WorkerState | undefined = undefined; - // #fixme #1524 multiworker not supported - xstep('Two workers & resume worker1', async function () { + step('Two workers & resume worker1', async function () { assert(worker0State); // first launch worker1 @@ -422,8 +429,7 @@ describe('Resume worker', function () { console.log('=========== worker1 produced blocks =================='); }); - // #fixme #1524 multiworker not supported - xstep('Kill and resume both workers', async function () { + step('Kill and resume both workers', async function () { assert(worker0State); assert(worker1State); @@ -474,3 +480,22 @@ describe('Resume worker', function () { } }); }); + +export async function initWorkerConnection(endpoint: string): Promise { + const wsp = new WebSocketAsPromised(endpoint, ({ + createWebSocket: (url: any) => new WebSocket(url), + extractMessageData: (event: any) => event, + packMessage: (data: any) => JSON.stringify(data), + unpackMessage: (data: string | ArrayBuffer | Blob) => JSON.parse(data.toString()), + attachRequestId: (data: any, requestId: string | number) => Object.assign({ id: requestId }, data), + extractRequestId: (data: any) => data && data.id, // read requestId from message `id` field + })); + await wsp.open(); + return wsp; +} + +export function sleep(secs: number) { + return new Promise((resolve) => { + setTimeout(resolve, secs * 1000); + }); +} diff --git a/tee-worker/ts-tests/worker/tsconfig.json b/tee-worker/ts-tests/worker/tsconfig.json new file mode 100644 index 0000000000..160c8db3b8 --- /dev/null +++ b/tee-worker/ts-tests/worker/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "Node", + "declaration": true, + "strict": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "resolveJsonModule": true, + "baseUrl": "." + }, + "ts-node": { + "esm": true, + "experimentalResolver": true, + "experimentalSpecifierResolution": "node" + } +} From 3869810c84b206019a91631285a366661c71126f Mon Sep 17 00:00:00 2001 From: Verin1005 <104152026+Verin1005@users.noreply.github.com> Date: Mon, 22 Jan 2024 17:21:52 +0800 Subject: [PATCH 16/51] Build match worker image (#2313) * add worker && cli build * separate docker builds * add Docker Buildx * add caches --------- Co-authored-by: Igor Trofimov Co-authored-by: BillyWooo --- .github/workflows/create-release-draft.yml | 152 +++++++++++++++--- .github/workflows/release-ts-api-package.yml | 41 +++-- tee-worker/cli/lit_ts_api_package_build.sh | 4 +- .../docker/lit-ts-api-package-build.yml | 8 +- 4 files changed, 159 insertions(+), 46 deletions(-) diff --git a/.github/workflows/create-release-draft.yml b/.github/workflows/create-release-draft.yml index fa619fa504..62c3f510ef 100644 --- a/.github/workflows/create-release-draft.yml +++ b/.github/workflows/create-release-draft.yml @@ -24,10 +24,10 @@ on: required: true default: true release_tag: - description: an existing tag for creating release (e.g. v1.2.3) + description: an existing tag for creating release (e.g. p1.2.0-w0.0.1-101) required: true diff_tag: - description: an existing tag to run diff against (e.g. v1.2.0) + description: an existing tag to run diff against (e.g. p1.1.0-w0.0.1-100) default: "" required: false genesis_release: @@ -44,6 +44,7 @@ env: DIFF_TAG: ${{ github.event.inputs.diff_tag }} GENESIS_RELEASE: ${{ github.event.inputs.genesis_release }} DOCKER_BUILDKIT: 1 + REF_VERSION: ${{ github.head_ref || github.ref_name }} jobs: set-release-type: @@ -114,8 +115,8 @@ jobs: ${{ matrix.chain }}-parachain-srtool-digest.json ${{ matrix.chain }}-parachain-runtime.compact.compressed.wasm - ## build docker image of parachain binary ## - build-docker: + # build docker image of parachain binary ## + build-parachain-docker: if: ${{ github.event.inputs.release_client == 'true' }} runs-on: ubuntu-latest steps: @@ -127,7 +128,7 @@ jobs: - name: Set env run: | - DOCKER_TAG=$(echo ${{ env.RELEASE_TAG }} | cut -d'-' -f1 | sed 's/p/v/') + DOCKER_TAG=$(echo ${{ env.RELEASE_TAG }} | sed 's/p/v/;s/\(.*\)-w.*/\1/') echo "DOCKER_TAG=$DOCKER_TAG" >> $GITHUB_ENV - name: Build docker image @@ -166,6 +167,121 @@ jobs: ${{ env.GENESIS_RELEASE }}-genesis-state ${{ env.GENESIS_RELEASE }}-genesis-wasm + build-worker-docker: + if: ${{ github.event.inputs.release_worker == 'true' }} + runs-on: ubuntu-latest + steps: + - name: Checkout codes on ${{ env.RELEASE_TAG }} + uses: actions/checkout@v4 + with: + ref: ${{ env.RELEASE_TAG }} + fetch-depth: 0 + - name: Set env + run: | + WORKER_TAG=$(echo ${{ env.RELEASE_TAG }} | sed 's/.*\(w.*\)/\1/;s/w/v/') + echo "WORKER_TAG=$WORKER_TAG" >> $GITHUB_ENV + + - name: Free up disk space + if: startsWith(runner.name, 'GitHub Actions') + uses: jlumbroso/free-disk-space@main + with: + tool-cache: true + swap-storage: false + large-packages: false + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + # use the docker driver to access the local image + # we don't need external caches or multi platforms here + # see https://docs.docker.com/build/drivers/ + driver: docker + + - name: Cache worker-cache + uses: actions/cache@v3 + with: + path: | + worker-cache + key: worker-cache-${{ env.REF_VERSION }}-${{ hashFiles('tee-worker/**/Cargo.lock', 'tee-worker/**/Cargo.toml') }} + restore-keys: | + worker-cache-${{ env.REF_VERSION }}- + worker-cache- + + - name: Create cache folder if not exist + run: | + for i in 'git/db' 'registry/cache' 'registry/index' 'sccache'; do + [ ! -d "worker-cache/$i" ] && mkdir -p "worker-cache/$i" || true + echo "hello" > worker-cache/$i/nix + done + echo "::group::List worker-cache size" + du -sh worker-cache/* + echo "::endgroup::" + echo "::group::Show disk usage" + df -h . + echo "::endgroup::" + + - name: Build local builder + uses: docker/build-push-action@v5 + with: + context: . + file: tee-worker/build.Dockerfile + tags: local-builder:latest + target: builder + build-args: | + WORKER_MODE_ARG=sidechain + ADDITIONAL_FEATURES_ARG= + + - name: Copy caches from the built image + run: | + echo "::group::Show disk usage" + df -h . + echo "::endgroup::" + echo "::group::docker images" + docker images --all + echo "::endgroup::" + echo "::group::copy cache out" + for i in 'git/db' 'registry/cache' 'registry/index'; do + b="${i%/*}" + rm -rf worker-cache/$i + docker cp "$(docker create --rm local-builder:latest):/opt/rust/$i" worker-cache/$b + done + rm -rf worker-cache/sccache + docker cp "$(docker create --rm local-builder:latest):/opt/rust/sccache" worker-cache + du -sh worker-cache/* + echo "::endgroup::" + echo "::group::df -h ." + df -h . + echo "::endgroup::" + + - name: Build worker + uses: docker/build-push-action@v5 + with: + context: . + file: tee-worker/build.Dockerfile + tags: litentry/litentry-worker:${{ env.WORKER_TAG }} + target: deployed-worker + + - name: Build cli + uses: docker/build-push-action@v5 + with: + context: . + file: tee-worker/build.Dockerfile + tags: litentry/litentry-cli:${{ env.WORKER_TAG }} + target: deployed-client + + - run: docker images --all + + - name: Dockerhub login + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + + - name: Push worker image + run: | + docker push litentry/litentry-worker:$WORKER_TAG + docker push litentry/litentry-cli:$WORKER_TAG + ## Build the enclave and package config files build-tee: if: ${{ github.event.inputs.release_worker == 'true' }} || ${{ github.event.inputs.release_enclave == 'true' }} @@ -181,8 +297,8 @@ jobs: ref: ${{ env.RELEASE_TAG }} fetch-depth: 0 - - name: Build release artefacts - run: | + - name: Build release artefacts + run: | source /opt/intel/sgxsdk/environment ./tee-worker/scripts/litentry/release/build.sh ${{ github.event.inputs.release_worker }} ${{ github.event.inputs.release_enclave }} @@ -214,11 +330,11 @@ jobs: - name: Fail early if: failure() uses: andymckay/cancel-action@0.3 - + ## test again the built docker image ## run-ts-tests: runs-on: ubuntu-latest - needs: build-docker + needs: build-parachain-docker strategy: matrix: chain: @@ -235,7 +351,7 @@ jobs: - name: Download and tag docker image run: | - export DOCKER_TAG=$(echo ${{ env.RELEASE_TAG }} | cut -d'-' -f1 | sed 's/p/v/') + export DOCKER_TAG=$(echo ${{ env.RELEASE_TAG }} | sed 's/p/v/;s/\(.*\)-w.*/\1/') docker pull litentry/litentry-parachain:$DOCKER_TAG docker tag litentry/litentry-parachain:$DOCKER_TAG litentry/litentry-parachain:latest @@ -260,7 +376,7 @@ jobs: ## check extrinsic ## extrinsic-ordering-check-from-bin: runs-on: ubuntu-latest - needs: build-docker + needs: build-parachain-docker strategy: matrix: chain: [rococo, litmus, litentry] @@ -280,7 +396,7 @@ jobs: - name: Prepare output and compare the metadata timeout-minutes: 3 run: | - export DOCKER_TAG=$(echo ${{ env.RELEASE_TAG }} | cut -d'-' -f1 | sed 's/p/v/') + export DOCKER_TAG=$(echo ${{ env.RELEASE_TAG }} | sed 's/p/v/;s/\(.*\)-w.*/\1/') PARACHAIN_NAME=local-parachain BASE_URL=ws://127.0.0.1:9944 chain=${{ matrix.chain }} @@ -310,16 +426,16 @@ jobs: uses: actions-cool/issues-helper@v3 id: findissueid with: - actions: 'find-issues' + actions: "find-issues" token: ${{ secrets.GITHUB_TOKEN }} - issue-state: 'open' + issue-state: "open" title-includes: Litentry-parachain ${{ env.RELEASE_TAG }} Release checklist - + - name: Create comment if: ${{ steps.findissueid.outputs.issues }} != '[]' uses: actions-cool/issues-helper@v3 with: - actions: 'create-comment' + actions: "create-comment" token: ${{ secrets.GITHUB_TOKEN }} issue-number: ${{ fromJson(steps.findissueid.outputs.issues)[0].number }} body: | @@ -334,7 +450,7 @@ jobs: - set-release-type - build-tee - run-ts-tests - - build-wasm + - build-wasm if: | !failure() && (success('build-wasm') || success('run-ts-tests') || success('build-tee')) @@ -347,7 +463,7 @@ jobs: - name: Download all artefacts uses: actions/download-artifact@v3 - + - name: Generate release notes run: | export MRENCLAVE="${{ needs.build-tee.outputs.mrenclave }}" diff --git a/.github/workflows/release-ts-api-package.yml b/.github/workflows/release-ts-api-package.yml index 5505c02816..b61bc5d52a 100644 --- a/.github/workflows/release-ts-api-package.yml +++ b/.github/workflows/release-ts-api-package.yml @@ -3,19 +3,10 @@ name: Release Ts API Package on: workflow_dispatch: - inputs: - parachain-tag: - description: 'Parachain docker image tag' - required: true - default: 'latest' - worker-tag: - description: 'Worker docker image tag' - required: true - default: 'latest' - release-tag: - description: 'Client-api release tag' - required: true - default: 'latest' + inputs: + release-tag: + description: "Client-api release tag (e.g. p1.2.0-9701-w0.0.1-101)" + required: true env: NODE_AUTH_TOKEN: ${{ secrets.RELEASE_TS_API_PACKAGE_TOKEN }} @@ -25,18 +16,24 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Set ENV + run: | + # extracting parachain version and worker version from release tag + echo "PARACHAIN_TAG=$(echo ${{inputs.release-tag}} | sed 's/p/v/;s/\(.*\)-w.*/\1/')" >> $GITHUB_ENV + echo "WORKER_TAG=$(echo ${{inputs.release-tag}} | sed 's/.*\(w.*\)/\1/;s/w/v/')" >> $GITHUB_ENV + - name: Pull litentry image optionally run: | docker pull parity/polkadot - docker pull litentry/litentry-worker:${{ inputs.worker-tag }} - docker pull litentry/litentry-cli:${{ inputs.worker-tag }} - docker pull litentry/litentry-parachain:${{ inputs.parachain-tag }} + docker pull litentry/litentry-worker:$WORKER_TAG + docker pull litentry/litentry-cli:$WORKER_TAG + docker pull litentry/litentry-parachain:$PARACHAIN_TAG - name: Re-tag docker image run: | - docker tag litentry/litentry-worker:${{ inputs.worker-tag }} litentry/litentry-worker:latest - docker tag litentry/litentry-cli:${{ inputs.worker-tag }} litentry/litentry-cli:latest - docker tag litentry/litentry-parachain:${{ inputs.parachain-tag }} litentry/litentry-parachain:latest + docker tag litentry/litentry-worker:$WORKER_TAG litentry/litentry-worker:latest + docker tag litentry/litentry-cli:$WORKER_TAG litentry/litentry-cli:latest + docker tag litentry/litentry-parachain:$PARACHAIN_TAG litentry/litentry-parachain:latest - run: docker images --all @@ -51,9 +48,8 @@ jobs: run: | cd tee-worker/docker docker compose -f litentry-parachain.build.yml build - + - name: Update metadata and generate types - timeout-minutes: 10 run: | cd tee-worker/docker docker compose -f docker-compose.yml -f lit-ts-api-package-build.yml up --no-build --exit-code-from lit-ts-api-package-build lit-ts-api-package-build @@ -83,7 +79,6 @@ jobs: echo "$api dist and build files do not exist. Publishing failed." exit 1 fi - npm publish --tag ${{ inputs.release-tag }} echo "------------------------$api published------------------------" @@ -109,4 +104,4 @@ jobs: with: name: logs-lit-ts-api-package-build path: logs - if-no-files-found: ignore \ No newline at end of file + if-no-files-found: ignore diff --git a/tee-worker/cli/lit_ts_api_package_build.sh b/tee-worker/cli/lit_ts_api_package_build.sh index 0d962815eb..684833b7c5 100755 --- a/tee-worker/cli/lit_ts_api_package_build.sh +++ b/tee-worker/cli/lit_ts_api_package_build.sh @@ -42,8 +42,8 @@ echo "Using client binary $CLIENT_BIN" echo "Using node uri $NODEURL:$NPORT" echo "Using trusted-worker uri $WORKER1URL:$WORKER1PORT" echo "Using node http uri $NODEHTTPURL:$NPORT" -echo "" - +echo "waiting 20 secs worker to run successfully" +sleep 20 cd /client-api/parachain-api curl -s -H "Content-Type: application/json" -d '{"id": "1", "jsonrpc": "2.0", "method": "state_getMetadata", "params": []}' $NODEHTTPURL:$NPORT > prepare-build/litentry-parachain-metadata.json echo "update parachain metadata" diff --git a/tee-worker/docker/lit-ts-api-package-build.yml b/tee-worker/docker/lit-ts-api-package-build.yml index 979cc7205e..21fcd8e135 100644 --- a/tee-worker/docker/lit-ts-api-package-build.yml +++ b/tee-worker/docker/lit-ts-api-package-build.yml @@ -13,11 +13,13 @@ services: litentry-node: condition: service_healthy litentry-worker-1: - condition: service_healthy + # using +service_started+ over +service_healthy+ since worker runs successfully but can not connect to parachain + # as requires additional pre-setup for parachain image which built in production mode + # for generating types there is no need for fully workable interaction between worker and parachain + condition: service_started networks: - litentry-test-network - entrypoint: - "/usr/local/worker-cli/lit_ts_api_package_build.sh -p 9912 -u ws://litentry-node + entrypoint: "/usr/local/worker-cli/lit_ts_api_package_build.sh -p 9912 -u ws://litentry-node -W http://litentry-node -V wss://litentry-worker-1 -A 2011 -C /usr/local/bin/litentry-cli 2>&1" restart: "no" networks: From dd8d3cb15e49012db01a243c9dc84c21adf7c2bb Mon Sep 17 00:00:00 2001 From: Igor Trofimov Date: Mon, 22 Jan 2024 14:05:21 +0200 Subject: [PATCH 17/51] Follow up changes for release draft action (#2413) --- .github/workflows/create-release-draft.yml | 73 ++++--------------- .../docker/lit-ts-api-package-build.yml | 4 +- 2 files changed, 16 insertions(+), 61 deletions(-) diff --git a/.github/workflows/create-release-draft.yml b/.github/workflows/create-release-draft.yml index 62c3f510ef..261b8976ee 100644 --- a/.github/workflows/create-release-draft.yml +++ b/.github/workflows/create-release-draft.yml @@ -115,7 +115,7 @@ jobs: ${{ matrix.chain }}-parachain-srtool-digest.json ${{ matrix.chain }}-parachain-runtime.compact.compressed.wasm - # build docker image of parachain binary ## + ## build docker image of parachain binary ## build-parachain-docker: if: ${{ github.event.inputs.release_client == 'true' }} runs-on: ubuntu-latest @@ -128,12 +128,12 @@ jobs: - name: Set env run: | - DOCKER_TAG=$(echo ${{ env.RELEASE_TAG }} | sed 's/p/v/;s/\(.*\)-w.*/\1/') - echo "DOCKER_TAG=$DOCKER_TAG" >> $GITHUB_ENV + PARACHAIN_DOCKER_TAG=$(echo ${{ env.RELEASE_TAG }} | sed 's/p/v/;s/\(.*\)-w.*/\1/') + echo "PARACHAIN_DOCKER_TAG=$PARACHAIN_DOCKER_TAG" >> $GITHUB_ENV - name: Build docker image run: | - ./scripts/build-docker.sh production $DOCKER_TAG + ./scripts/build-docker.sh production $PARACHAIN_DOCKER_TAG echo "=============================" docker images @@ -145,17 +145,17 @@ jobs: - name: Push docker image run: | - docker push litentry/litentry-parachain:$DOCKER_TAG + docker push litentry/litentry-parachain:$PARACHAIN_DOCKER_TAG - name: Generate genesis artefacts if need if: github.event.inputs.genesis_release != 'none' run: | - docker run --rm litentry/litentry-parachain:$DOCKER_TAG export-genesis-state --chain=${{ env.GENESIS_RELEASE }} > ${{ env.GENESIS_RELEASE }}-genesis-state - docker run --rm litentry/litentry-parachain:$DOCKER_TAG export-genesis-wasm --chain=${{ env.GENESIS_RELEASE }} > ${{ env.GENESIS_RELEASE }}-genesis-wasm + docker run --rm litentry/litentry-parachain:$PARACHAIN_DOCKER_TAG export-genesis-state --chain=${{ env.GENESIS_RELEASE }} > ${{ env.GENESIS_RELEASE }}-genesis-state + docker run --rm litentry/litentry-parachain:$PARACHAIN_DOCKER_TAG export-genesis-wasm --chain=${{ env.GENESIS_RELEASE }} > ${{ env.GENESIS_RELEASE }}-genesis-wasm - name: Copy client binary to disk run: | - docker cp $(docker create --rm litentry/litentry-parachain:$DOCKER_TAG):/usr/local/bin/litentry-collator . + docker cp $(docker create --rm litentry/litentry-parachain:$PARACHAIN_DOCKER_TAG):/usr/local/bin/litentry-collator . - name: Upload the client binary uses: actions/upload-artifact@v3 @@ -178,8 +178,8 @@ jobs: fetch-depth: 0 - name: Set env run: | - WORKER_TAG=$(echo ${{ env.RELEASE_TAG }} | sed 's/.*\(w.*\)/\1/;s/w/v/') - echo "WORKER_TAG=$WORKER_TAG" >> $GITHUB_ENV + WORKER_DOCKER_TAG=$(echo ${{ env.RELEASE_TAG }} | sed 's/.*\(w.*\)/\1/;s/w/v/') + echo "WORKER_DOCKER_TAG=$WORKER_DOCKER_TAG" >> $GITHUB_ENV - name: Free up disk space if: startsWith(runner.name, 'GitHub Actions') @@ -197,29 +197,6 @@ jobs: # see https://docs.docker.com/build/drivers/ driver: docker - - name: Cache worker-cache - uses: actions/cache@v3 - with: - path: | - worker-cache - key: worker-cache-${{ env.REF_VERSION }}-${{ hashFiles('tee-worker/**/Cargo.lock', 'tee-worker/**/Cargo.toml') }} - restore-keys: | - worker-cache-${{ env.REF_VERSION }}- - worker-cache- - - - name: Create cache folder if not exist - run: | - for i in 'git/db' 'registry/cache' 'registry/index' 'sccache'; do - [ ! -d "worker-cache/$i" ] && mkdir -p "worker-cache/$i" || true - echo "hello" > worker-cache/$i/nix - done - echo "::group::List worker-cache size" - du -sh worker-cache/* - echo "::endgroup::" - echo "::group::Show disk usage" - df -h . - echo "::endgroup::" - - name: Build local builder uses: docker/build-push-action@v5 with: @@ -231,34 +208,12 @@ jobs: WORKER_MODE_ARG=sidechain ADDITIONAL_FEATURES_ARG= - - name: Copy caches from the built image - run: | - echo "::group::Show disk usage" - df -h . - echo "::endgroup::" - echo "::group::docker images" - docker images --all - echo "::endgroup::" - echo "::group::copy cache out" - for i in 'git/db' 'registry/cache' 'registry/index'; do - b="${i%/*}" - rm -rf worker-cache/$i - docker cp "$(docker create --rm local-builder:latest):/opt/rust/$i" worker-cache/$b - done - rm -rf worker-cache/sccache - docker cp "$(docker create --rm local-builder:latest):/opt/rust/sccache" worker-cache - du -sh worker-cache/* - echo "::endgroup::" - echo "::group::df -h ." - df -h . - echo "::endgroup::" - - name: Build worker uses: docker/build-push-action@v5 with: context: . file: tee-worker/build.Dockerfile - tags: litentry/litentry-worker:${{ env.WORKER_TAG }} + tags: litentry/litentry-worker:${{ env.WORKER_DOCKER_TAG }} target: deployed-worker - name: Build cli @@ -266,7 +221,7 @@ jobs: with: context: . file: tee-worker/build.Dockerfile - tags: litentry/litentry-cli:${{ env.WORKER_TAG }} + tags: litentry/litentry-cli:${{ env.WORKER_DOCKER_TAG }} target: deployed-client - run: docker images --all @@ -279,8 +234,8 @@ jobs: - name: Push worker image run: | - docker push litentry/litentry-worker:$WORKER_TAG - docker push litentry/litentry-cli:$WORKER_TAG + docker push litentry/litentry-worker:$WORKER_DOCKER_TAG + docker push litentry/litentry-cli:$WORKER_DOCKER_TAG ## Build the enclave and package config files build-tee: diff --git a/tee-worker/docker/lit-ts-api-package-build.yml b/tee-worker/docker/lit-ts-api-package-build.yml index 21fcd8e135..154bc22e4f 100644 --- a/tee-worker/docker/lit-ts-api-package-build.yml +++ b/tee-worker/docker/lit-ts-api-package-build.yml @@ -19,8 +19,8 @@ services: condition: service_started networks: - litentry-test-network - entrypoint: "/usr/local/worker-cli/lit_ts_api_package_build.sh -p 9912 -u ws://litentry-node - -W http://litentry-node -V wss://litentry-worker-1 -A 2011 -C /usr/local/bin/litentry-cli 2>&1" + entrypoint: + "/usr/local/worker-cli/lit_ts_api_package_build.sh -p 9912 -u ws://litentry-node -W http://litentry-node -V wss://litentry-worker-1 -A 2011 -C /usr/local/bin/litentry-cli 2>&1" restart: "no" networks: litentry-test-network: From ff22b32bc410e19fd8026bbad0c175c154278f3d Mon Sep 17 00:00:00 2001 From: Kasper Ziemianek Date: Mon, 22 Jan 2024 13:57:39 +0100 Subject: [PATCH 18/51] hardcode data provider config into prod enclave (#2291) * hardcode dataprovider configuration for prod build * docker enclave config * fix evm build * add missing config * read config from env for new properties * config for multiworker setup * remove running mode arg * use shielding key repo instead of shielding key * fix local env files --- tee-worker/.env.dev | 25 ++- tee-worker/Cargo.lock | 16 +- tee-worker/app-libs/stf/Cargo.toml | 3 - .../core-primitives/enclave-api/Cargo.toml | 1 - .../enclave-api/ffi/src/lib.rs | 13 -- .../core-primitives/enclave-api/src/lib.rs | 5 - .../enclave-api/src/stf_task_handler.rs | 49 ------ .../enclave-api/src/vc_issuance.rs | 49 ------ .../test/src/mock/shielding_crypto_mock.rs | 4 - tee-worker/docker/docker-compose.yml | 2 +- .../docker/multiworker-docker-compose.yml | 43 ++++- tee-worker/enclave-runtime/Cargo.lock | 11 +- tee-worker/enclave-runtime/Enclave.edl | 8 - .../src/initialization/global_components.rs | 4 + .../enclave-runtime/src/initialization/mod.rs | 91 ++++++++++- tee-worker/enclave-runtime/src/lib.rs | 1 - .../enclave-runtime/src/stf_task_handler.rs | 121 -------------- .../enclave-runtime/src/vc_issuance_task.rs | 130 ---------------- .../litentry/core/assertion-build/src/a14.rs | 15 +- .../litentry/core/assertion-build/src/a2.rs | 22 +-- .../litentry/core/assertion-build/src/a3.rs | 17 +- .../litentry/core/assertion-build/src/a6.rs | 12 +- .../litentry/core/assertion-build/src/a8.rs | 11 +- .../assertion-build/src/achainable/amount.rs | 29 ++-- .../src/achainable/amount_holding.rs | 20 +-- .../src/achainable/amount_token.rs | 26 ++-- .../assertion-build/src/achainable/amounts.rs | 9 +- .../assertion-build/src/achainable/basic.rs | 14 +- .../src/achainable/between_percents.rs | 4 +- .../src/achainable/class_of_year.rs | 5 +- .../assertion-build/src/achainable/date.rs | 9 +- .../src/achainable/date_interval.rs | 4 +- .../src/achainable/date_percent.rs | 4 +- .../assertion-build/src/achainable/mirror.rs | 10 +- .../assertion-build/src/achainable/mod.rs | 71 +++++---- .../assertion-build/src/achainable/token.rs | 9 +- .../src/brc20/amount_holder.rs | 9 +- .../src/generic_discord_role.rs | 71 +++++---- .../core/assertion-build/src/holding_time.rs | 39 +++-- .../amount_holding/evm_amount_holding.rs | 58 +++---- .../bnb_digit_domain_club_amount.rs | 8 +- .../bnb_domain/bnb_domain_holding_amount.rs | 8 +- .../src/nodereal/bnb_domain/mod.rs | 29 ++-- .../src/nodereal/crypto_summary/mod.rs | 8 +- .../nft_holder/weirdo_ghost_gang_holder.rs | 29 ++-- .../assertion-build/src/oneblock/course.rs | 9 +- .../core/assertion-build/src/oneblock/mod.rs | 16 +- .../core/assertion-build/src/vip3/card.rs | 9 +- .../core/assertion-build/src/vip3/mod.rs | 9 +- .../src/nodereal/crypto_summary/mod.rs | 13 +- .../litentry/core/data-providers/Cargo.toml | 3 +- .../core/data-providers/src/achainable.rs | 6 +- .../data-providers/src/discord_litentry.rs | 36 ++--- .../data-providers/src/discord_official.rs | 10 +- .../core/data-providers/src/geniidata.rs | 11 +- .../litentry/core/data-providers/src/lib.rs | 147 ++++++++++++------ .../data-providers/src/nodereal_jsonrpc.rs | 43 +++-- .../data-providers/src/twitter_official.rs | 53 +++---- .../core/identity-verification/src/lib.rs | 5 +- .../identity-verification/src/web2/mod.rs | 13 +- .../receiver/src/handler/assertion.rs | 146 +++++++++++------ .../src/handler/identity_verification.rs | 19 ++- .../core/stf-task/receiver/src/lib.rs | 44 ++++-- .../core/stf-task/receiver/src/test.rs | 6 +- .../lc-vc-task-receiver/Cargo.toml | 11 -- .../lc-vc-task-receiver/src/lib.rs | 26 ++-- .../lc-vc-task-receiver/src/vc_handling.rs | 147 ++++++++++++------ .../vc-issuance/lc-vc-task-sender/Cargo.toml | 9 -- .../vc-issuance/lc-vc-task-sender/src/lib.rs | 2 - tee-worker/local-setup/config/one-worker.json | 2 - .../local-setup/config/three-workers.json | 6 - .../local-setup/config/two-workers.json | 4 - .../local-setup/development-worker.json | 2 - tee-worker/scripts/launch_local_worker.sh | 3 - tee-worker/scripts/litentry/release/ReadMe.md | 5 +- tee-worker/service/Cargo.toml | 1 + tee-worker/service/src/cli.yml | 5 - tee-worker/service/src/config.rs | 13 -- tee-worker/service/src/main_impl.rs | 122 --------------- .../service/src/running-mode-config.json | 106 ------------- tee-worker/service/src/tests/commons.rs | 1 - .../ts-tests/worker/resuming_worker.test.ts | 1 - 82 files changed, 965 insertions(+), 1225 deletions(-) delete mode 100644 tee-worker/core-primitives/enclave-api/src/stf_task_handler.rs delete mode 100644 tee-worker/core-primitives/enclave-api/src/vc_issuance.rs delete mode 100644 tee-worker/service/src/running-mode-config.json diff --git a/tee-worker/.env.dev b/tee-worker/.env.dev index 6ffad948b4..65aa47f85d 100644 --- a/tee-worker/.env.dev +++ b/tee-worker/.env.dev @@ -11,4 +11,27 @@ TrustedWorkerPort=2000 UntrustedWorkerPort=2001 MuRaPort=3443 UntrustedHttpPort=4545 -NODE_ENV=local \ No newline at end of file +NODE_ENV=local +# tee-worker dataproviders config +TWITTER_OFFICIAL_URL=http://localhost:19527 +TWITTER_LITENTRY_URL=http://localhost:19527 +TWITTER_AUTH_TOKEN_V2= +DISCORD_OFFICIAL_URL=http://localhost:19527 +DISCORD_LITENTRY_URL=http://localhost:19527 +DISCORD_AUTH_TOKEN= +ACHAINABLE_URL=http://localhost:19527 +ACHAINABLE_AUTH_KEY= +CREDENTIAL_ENDPOINT=http://localhost:9933 +ONEBLOCK_NOTION_KEY=ABCDEFGHIJKLMNOPQRSTUVWXYZ +ONEBLOCK_NOTION_URL=https://abc.com +SORA_QUIZ_MASTER_ID=SORA_QUIZ_MASTER_ID +SORA_QUIZ_ATTENDEE_ID=SORA_QUIZ_ATTENDEE_ID +NODEREAL_API_KEY=NODEREAL_API_KEY +NODEREAL_API_URL=https://open-platform.nodereal.io/ +NODEREAL_API_CHAIN_NETWORK_URL= +CONTEST_LEGEND_DISCORD_ROLE_ID=CONTEST_LEGEND_DISCORD_ROLE_ID +CONTEST_POPULARITY_DISCORD_ROLE_ID=CONTEST_POPULARITY_DISCORD_ROLE_ID +CONTEST_PARTICIPANT_DISCORD_ROLE_ID=CONTEST_PARTICIPANT_DISCORD_ROLE_ID +VIP3_URL=https://dappapi.vip3.io/ +GENIIDATA_URL=https://api.geniidata.com/api/1/brc20/balance? +GENIIDATA_API_KEY=142cf1b0-1ca7-11ee-bb5e-9d74c2e854ac \ No newline at end of file diff --git a/tee-worker/Cargo.lock b/tee-worker/Cargo.lock index 604b0ee31b..01b2f96fd0 100644 --- a/tee-worker/Cargo.lock +++ b/tee-worker/Cargo.lock @@ -4577,7 +4577,6 @@ dependencies = [ "itp-types", "itp-utils", "lc-stf-task-sender", - "lc-vc-task-sender", "litentry-primitives", "log 0.4.20", "pallet-balances", @@ -4995,7 +4994,6 @@ dependencies = [ "itp-settings", "itp-storage", "itp-types", - "lc-data-providers", "log 0.4.20", "parity-scale-codec", "serde_json 1.0.103", @@ -6201,7 +6199,7 @@ dependencies = [ "itc-rest-client", "itp-rpc", "itp-stf-primitives", - "lazy_static", + "itp-utils", "lc-mock-server", "litentry-primitives", "log 0.4.20", @@ -6340,7 +6338,6 @@ dependencies = [ "hex 0.4.0", "ita-sgx-runtime", "ita-stf", - "itp-enclave-metrics", "itp-extrinsics-factory", "itp-node-api", "itp-ocall-api", @@ -6351,11 +6348,9 @@ dependencies = [ "itp-storage", "itp-top-pool-author", "itp-types", - "lazy_static", "lc-assertion-build", "lc-credentials", "lc-data-providers", - "lc-identity-verification", "lc-stf-task-receiver", "lc-stf-task-sender", "lc-vc-task-sender", @@ -6365,11 +6360,8 @@ dependencies = [ "parity-scale-codec", "sgx_tstd", "sp-core", - "thiserror 1.0.44", - "thiserror 1.0.9", "threadpool 1.8.0", "threadpool 1.8.1", - "url 2.1.1", ] [[package]] @@ -6378,7 +6370,6 @@ version = "0.1.0" dependencies = [ "futures 0.3.28", "futures 0.3.8", - "itp-stf-primitives", "itp-types", "lazy_static", "lc-stf-task-sender", @@ -6388,10 +6379,6 @@ dependencies = [ "sgx_tstd", "sp-runtime", "sp-std 5.0.0", - "thiserror 1.0.44", - "thiserror 1.0.9", - "url 2.1.1", - "url 2.4.0", ] [[package]] @@ -7120,6 +7107,7 @@ dependencies = [ "futures 0.3.28", "hex 0.4.3", "ipfs-api", + "ita-stf", "itc-parentchain", "itc-parentchain-test", "itc-rest-client", diff --git a/tee-worker/app-libs/stf/Cargo.toml b/tee-worker/app-libs/stf/Cargo.toml index 22a3c17e3a..9c6dd0aad8 100644 --- a/tee-worker/app-libs/stf/Cargo.toml +++ b/tee-worker/app-libs/stf/Cargo.toml @@ -41,7 +41,6 @@ sp-std = { default-features = false, git = "https://github.com/paritytech/substr # litentry itp-node-api-metadata-provider = { path = "../../core-primitives/node-api/metadata-provider", default-features = false } lc-stf-task-sender = { path = "../../litentry/core/stf-task/sender", default-features = false } -lc-vc-task-sender = { path = "../../litentry/core/vc-issuance/lc-vc-task-sender", default-features = false } litentry-primitives = { path = "../../litentry/primitives", default-features = false } pallet-parentchain = { path = "../../../pallets/parentchain", default-features = false } @@ -60,7 +59,6 @@ sgx = [ # litentry "litentry-primitives/sgx", "lc-stf-task-sender/sgx", - "lc-vc-task-sender/sgx", "itp-node-api-metadata-provider/sgx", ] std = [ @@ -89,7 +87,6 @@ std = [ # litentry "litentry-primitives/std", "lc-stf-task-sender/std", - "lc-vc-task-sender/std", "itp-node-api-metadata-provider/std", ] test = [] diff --git a/tee-worker/core-primitives/enclave-api/Cargo.toml b/tee-worker/core-primitives/enclave-api/Cargo.toml index 6908046ec9..c9dfaa9dff 100644 --- a/tee-worker/core-primitives/enclave-api/Cargo.toml +++ b/tee-worker/core-primitives/enclave-api/Cargo.toml @@ -26,7 +26,6 @@ itp-storage = { path = "../storage" } itp-types = { path = "../types" } # litentry -lc-data-providers = { path = "../../litentry/core/data-providers" } teerex-primitives = { path = "../../../primitives/teerex", default-features = false } [features] diff --git a/tee-worker/core-primitives/enclave-api/ffi/src/lib.rs b/tee-worker/core-primitives/enclave-api/ffi/src/lib.rs index 48336e9268..2dbb8fb016 100644 --- a/tee-worker/core-primitives/enclave-api/ffi/src/lib.rs +++ b/tee-worker/core-primitives/enclave-api/ffi/src/lib.rs @@ -276,17 +276,4 @@ extern "C" { until: *const u32, ) -> sgx_status_t; - pub fn run_stf_task_handler( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - data_provider_config: *const u8, - data_provider_config_size: usize, - ) -> sgx_status_t; - - pub fn run_vc_issuance( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - data_provider_config: *const u8, - data_provider_config_size: usize, - ) -> sgx_status_t; } diff --git a/tee-worker/core-primitives/enclave-api/src/lib.rs b/tee-worker/core-primitives/enclave-api/src/lib.rs index 767e311b46..38c810624f 100644 --- a/tee-worker/core-primitives/enclave-api/src/lib.rs +++ b/tee-worker/core-primitives/enclave-api/src/lib.rs @@ -22,11 +22,6 @@ pub mod sidechain; pub mod teeracle_api; pub mod utils; -#[cfg(feature = "implement-ffi")] -pub mod stf_task_handler; -#[cfg(feature = "implement-ffi")] -pub mod vc_issuance; - #[cfg(feature = "implement-ffi")] pub use sgx_urts::SgxEnclave; diff --git a/tee-worker/core-primitives/enclave-api/src/stf_task_handler.rs b/tee-worker/core-primitives/enclave-api/src/stf_task_handler.rs deleted file mode 100644 index a039b6461f..0000000000 --- a/tee-worker/core-primitives/enclave-api/src/stf_task_handler.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2020-2023 Trust Computing GmbH. -// This file is part of Litentry. -// -// Litentry is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Litentry is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Litentry. If not, see . - -use crate::{error::Error, Enclave, EnclaveResult}; -use codec::Encode; -use frame_support::ensure; -use itp_enclave_api_ffi as ffi; -use lc_data_providers::DataProviderConfig; -use sgx_types::*; - -/// Trait to run a stf task handling thread inside the enclave. -pub trait StfTaskHandler { - fn run_stf_task_handler(&self, data_provider_config: DataProviderConfig) -> EnclaveResult<()>; -} - -impl StfTaskHandler for Enclave { - fn run_stf_task_handler(&self, data_provider_config: DataProviderConfig) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let data_provider_config_enc = data_provider_config.encode(); - - let result = unsafe { - ffi::run_stf_task_handler( - self.eid, - &mut retval, - data_provider_config_enc.as_ptr(), - data_provider_config_enc.len(), - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(()) - } -} diff --git a/tee-worker/core-primitives/enclave-api/src/vc_issuance.rs b/tee-worker/core-primitives/enclave-api/src/vc_issuance.rs deleted file mode 100644 index 7fc4598c9a..0000000000 --- a/tee-worker/core-primitives/enclave-api/src/vc_issuance.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2020-2023 Trust Computing GmbH. -// This file is part of Litentry. -// -// Litentry is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Litentry is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Litentry. If not, see . - -use crate::{error::Error, Enclave, EnclaveResult}; -use codec::Encode; -use frame_support::ensure; -use itp_enclave_api_ffi as ffi; -use lc_data_providers::DataProviderConfig; -use sgx_types::*; - -/// Trait to run a stf task handling thread inside the enclave. -pub trait VcIssuance { - fn run_vc_issuance(&self, data_provider_config: DataProviderConfig) -> EnclaveResult<()>; -} - -impl VcIssuance for Enclave { - fn run_vc_issuance(&self, data_provider_config: DataProviderConfig) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let data_provider_config_enc = data_provider_config.encode(); - - let result = unsafe { - ffi::run_vc_issuance( - self.eid, - &mut retval, - data_provider_config_enc.as_ptr(), - data_provider_config_enc.len(), - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(()) - } -} diff --git a/tee-worker/core-primitives/test/src/mock/shielding_crypto_mock.rs b/tee-worker/core-primitives/test/src/mock/shielding_crypto_mock.rs index 441c770833..0006ba1245 100644 --- a/tee-worker/core-primitives/test/src/mock/shielding_crypto_mock.rs +++ b/tee-worker/core-primitives/test/src/mock/shielding_crypto_mock.rs @@ -22,10 +22,6 @@ use sgx_crypto_helper::{rsa3072::Rsa3072KeyPair, RsaKeyPair}; use sp_core::ed25519::Pair as Ed25519Pair; use std::vec::Vec; -/// Crypto key mock -/// -/// mock implementation that does not encrypt -/// encrypt/decrypt return the input as is #[derive(Clone)] pub struct ShieldingCryptoMock { key: Rsa3072KeyPair, diff --git a/tee-worker/docker/docker-compose.yml b/tee-worker/docker/docker-compose.yml index ae89562fcc..707588493d 100644 --- a/tee-worker/docker/docker-compose.yml +++ b/tee-worker/docker/docker-compose.yml @@ -144,7 +144,7 @@ services: retries: 20 entrypoint: "/usr/local/bin/litentry-worker --clean-reset --ws-external -M litentry-worker-1 -T wss://litentry-worker-1 - -u ws://litentry-node -U ws://litentry-worker-1 -P 2011 -w 2101 -p 9912 -h 4645 --enable-mock-server --running-mode mock + -u ws://litentry-node -U ws://litentry-worker-1 -P 2011 -w 2101 -p 9912 -h 4645 --enable-mock-server run --dev --skip-ra" restart: "no" volumes: diff --git a/tee-worker/docker/multiworker-docker-compose.yml b/tee-worker/docker/multiworker-docker-compose.yml index fa594742a4..70225a3d47 100644 --- a/tee-worker/docker/multiworker-docker-compose.yml +++ b/tee-worker/docker/multiworker-docker-compose.yml @@ -98,6 +98,7 @@ services: - --execution=wasm environment: RUST_LOG: sc_basic_authorship=trace,cumulus-consensus=trace,cumulus-collator=trace,collator_protocol=trace,collation_generation=trace,aura=debug + ulimits: *a1 litentry-worker-1: image: litentry/litentry-worker:latest @@ -144,7 +145,7 @@ services: retries: 20 entrypoint: "/usr/local/bin/litentry-worker --clean-reset --ws-external -M litentry-worker-1 -T wss://litentry-worker-1 - -u ws://litentry-node -U ws://litentry-worker-1 -P 2011 -w 2101 -p 9912 -h 4645 --enable-mock-server --running-mode mock + -u ws://litentry-node -U ws://litentry-worker-1 -P 2011 -w 2101 -p 9912 -h 4645 --enable-mock-server run --dev --skip-ra" restart: "no" litentry-worker-2: @@ -167,6 +168,24 @@ services: - "${SGX_QCNL:-/dev/null}:/etc/sgx_default_qcnl.conf" environment: - RUST_LOG=info,litentry_worker=debug,ws=warn,sp_io=error,substrate_api_client=warn,itc_parentchain_light_client=info,jsonrpsee_ws_client=warn,jsonrpsee_ws_server=warn,enclave_runtime=debug,ita_stf=debug,its_rpc_handler=warn,itc_rpc_client=warn,its_consensus_common=debug,its_state=warn,its_consensus_aura=warn,aura*=warn,its_consensus_slots=warn,itp_attestation_handler=debug,http_req=debug,lc_mock_server=warn,itc_rest_client=debug,lc_credentials=debug,lc_identity_verification=debug,lc_stf_task_receiver=debug,lc_stf_task_sender=debug,lc_data_providers=debug,itp_top_pool=debug,itc_parentchain_indirect_calls_executor=debug, + - TWITTER_OFFICIAL_URL=http://localhost:19527 + - TWITTER_LITENTRY_URL=http://localhost:19527 + - TWITTER_AUTH_TOKEN_V2= + - DISCORD_OFFICIAL_URL=http://localhost:19527 + - DISCORD_LITENTRY_URL=http://localhost:19527 + - DISCORD_AUTH_TOKEN= + - ACHAINABLE_URL=http://localhost:19527 + - ACHAINABLE_AUTH_KEY= + - CREDENTIAL_ENDPOINT=http://localhost:9933 + - ONEBLOCK_NOTION_KEY=ABCDEFGHIJKLMNOPQRSTUVWXYZ + - ONEBLOCK_NOTION_URL=https://abc.com + - SORA_QUIZ_MASTER_ID=SORA_QUIZ_MASTER_ID + - SORA_QUIZ_ATTENDEE_ID=SORA_QUIZ_ATTENDEE_ID + - NODEREAL_API_KEY=NODEREAL_API_KEY + - NODEREAL_API_URL=https://open-platform.nodereal.io/ + - CONTEST_LEGEND_DISCORD_ROLE_ID=CONTEST_LEGEND_DISCORD_ROLE_ID + - CONTEST_POPULARITY_DISCORD_ROLE_ID=CONTEST_POPULARITY_DISCORD_ROLE_ID + - CONTEST_PARTICIPANT_DISCORD_ROLE_ID=CONTEST_PARTICIPANT_DISCORD_ROLE_ID networks: - litentry-test-network healthcheck: @@ -176,7 +195,7 @@ services: retries: 20 entrypoint: "/usr/local/bin/litentry-worker --clean-reset --ws-external -M litentry-worker-2 -T wss://litentry-worker-2 - -u ws://litentry-node -U ws://litentry-worker-2 -P 2011 -w 2101 -p 9912 -h 4645 --enable-mock-server --running-mode mock + -u ws://litentry-node -U ws://litentry-worker-2 -P 2011 -w 2101 -p 9912 -h 4645 --enable-mock-server run --dev --skip-ra --request-state" restart: "no" litentry-worker-3: @@ -199,6 +218,24 @@ services: - "${SGX_QCNL:-/dev/null}:/etc/sgx_default_qcnl.conf" environment: - RUST_LOG=info,litentry_worker=debug,ws=warn,sp_io=error,substrate_api_client=warn,itc_parentchain_light_client=info,jsonrpsee_ws_client=warn,jsonrpsee_ws_server=warn,enclave_runtime=debug,ita_stf=debug,its_rpc_handler=warn,itc_rpc_client=warn,its_consensus_common=debug,its_state=warn,its_consensus_aura=warn,aura*=warn,its_consensus_slots=warn,itp_attestation_handler=debug,http_req=debug,lc_mock_server=warn,itc_rest_client=debug,lc_credentials=debug,lc_identity_verification=debug,lc_stf_task_receiver=debug,lc_stf_task_sender=debug,lc_data_providers=debug,itp_top_pool=debug,itc_parentchain_indirect_calls_executor=debug, + - TWITTER_OFFICIAL_URL=http://localhost:19527 + - TWITTER_LITENTRY_URL=http://localhost:19527 + - TWITTER_AUTH_TOKEN_V2= + - DISCORD_OFFICIAL_URL=http://localhost:19527 + - DISCORD_LITENTRY_URL=http://localhost:19527 + - DISCORD_AUTH_TOKEN= + - ACHAINABLE_URL=http://localhost:19527 + - ACHAINABLE_AUTH_KEY= + - CREDENTIAL_ENDPOINT=http://localhost:9933 + - ONEBLOCK_NOTION_KEY=ABCDEFGHIJKLMNOPQRSTUVWXYZ + - ONEBLOCK_NOTION_URL=https://abc.com + - SORA_QUIZ_MASTER_ID=SORA_QUIZ_MASTER_ID + - SORA_QUIZ_ATTENDEE_ID=SORA_QUIZ_ATTENDEE_ID + - NODEREAL_API_KEY=NODEREAL_API_KEY + - NODEREAL_API_URL=https://open-platform.nodereal.io/ + - CONTEST_LEGEND_DISCORD_ROLE_ID=CONTEST_LEGEND_DISCORD_ROLE_ID + - CONTEST_POPULARITY_DISCORD_ROLE_ID=CONTEST_POPULARITY_DISCORD_ROLE_ID + - CONTEST_PARTICIPANT_DISCORD_ROLE_ID=CONTEST_PARTICIPANT_DISCORD_ROLE_ID networks: - litentry-test-network healthcheck: @@ -208,7 +245,7 @@ services: retries: 20 entrypoint: "/usr/local/bin/litentry-worker --clean-reset --ws-external -M litentry-worker-3 -T wss://litentry-worker-3 - -u ws://litentry-node -U ws://litentry-worker-3 -P 2011 -w 2101 -p 9912 -h 4645 --enable-mock-server --running-mode mock + -u ws://litentry-node -U ws://litentry-worker-3 -P 2011 -w 2101 -p 9912 -h 4645 --enable-mock-server run --dev --skip-ra --request-state" restart: "no" volumes: diff --git a/tee-worker/enclave-runtime/Cargo.lock b/tee-worker/enclave-runtime/Cargo.lock index 277b721f63..0fabae9a7d 100644 --- a/tee-worker/enclave-runtime/Cargo.lock +++ b/tee-worker/enclave-runtime/Cargo.lock @@ -1922,7 +1922,6 @@ dependencies = [ "itp-types", "itp-utils", "lc-stf-task-sender", - "lc-vc-task-sender", "litentry-primitives", "log", "pallet-balances", @@ -2954,7 +2953,7 @@ dependencies = [ "http_req", "itc-rest-client", "itp-rpc", - "lazy_static", + "itp-utils", "litentry-primitives", "log", "parity-scale-codec", @@ -3059,7 +3058,6 @@ dependencies = [ "hex 0.4.0", "ita-sgx-runtime", "ita-stf", - "itp-enclave-metrics", "itp-extrinsics-factory", "itp-node-api", "itp-ocall-api", @@ -3070,11 +3068,9 @@ dependencies = [ "itp-storage", "itp-top-pool-author", "itp-types", - "lazy_static", "lc-assertion-build", "lc-credentials", "lc-data-providers", - "lc-identity-verification", "lc-stf-task-receiver", "lc-stf-task-sender", "lc-vc-task-sender", @@ -3084,9 +3080,7 @@ dependencies = [ "parity-scale-codec", "sgx_tstd", "sp-core", - "thiserror", "threadpool", - "url", ] [[package]] @@ -3094,7 +3088,6 @@ name = "lc-vc-task-sender" version = "0.1.0" dependencies = [ "futures 0.3.8", - "itp-stf-primitives", "itp-types", "lazy_static", "lc-stf-task-sender", @@ -3104,8 +3097,6 @@ dependencies = [ "sgx_tstd", "sp-runtime", "sp-std", - "thiserror", - "url", ] [[package]] diff --git a/tee-worker/enclave-runtime/Enclave.edl b/tee-worker/enclave-runtime/Enclave.edl index 53dfca0220..04c02fea61 100644 --- a/tee-worker/enclave-runtime/Enclave.edl +++ b/tee-worker/enclave-runtime/Enclave.edl @@ -192,14 +192,6 @@ enclave { public sgx_status_t ignore_parentchain_block_import_validation_until( [in] uint32_t* until ); - - public size_t run_stf_task_handler( - [in, size=data_providers_static_len] uint8_t* data_providers_static, uint32_t data_providers_static_len - ); - - public size_t run_vc_issuance( - [in, size=data_providers_static_len] uint8_t* data_providers_static, uint32_t data_providers_static_len - ); }; untrusted { diff --git a/tee-worker/enclave-runtime/src/initialization/global_components.rs b/tee-worker/enclave-runtime/src/initialization/global_components.rs index 8f45ddcc7f..8460670bc5 100644 --- a/tee-worker/enclave-runtime/src/initialization/global_components.rs +++ b/tee-worker/enclave-runtime/src/initialization/global_components.rs @@ -91,6 +91,7 @@ use its_sidechain::{ slots::FailSlotOnDemand, }; use lazy_static::lazy_static; +use lc_data_providers::DataProviderConfig; use litentry_primitives::BroadcastedRequest; use sgx_crypto_helper::rsa3072::Rsa3072KeyPair; use sgx_tstd::vec::Vec; @@ -499,3 +500,6 @@ pub static GLOBAL_SIDECHAIN_BLOCK_SYNCER_COMPONENT: ComponentContainer< pub static GLOBAL_SIDECHAIN_FAIL_SLOT_ON_DEMAND_COMPONENT: ComponentContainer< Option, > = ComponentContainer::new("sidechain_fail_slot_on_demand"); + +pub static GLOBAL_DATA_PROVIDER_CONFIG: ComponentContainer = + ComponentContainer::new("data_provider_config"); diff --git a/tee-worker/enclave-runtime/src/initialization/mod.rs b/tee-worker/enclave-runtime/src/initialization/mod.rs index 7faee9d686..55845d2eac 100644 --- a/tee-worker/enclave-runtime/src/initialization/mod.rs +++ b/tee-worker/enclave-runtime/src/initialization/mod.rs @@ -27,14 +27,15 @@ use crate::{ EnclaveStateHandler, EnclaveStateInitializer, EnclaveStateObserver, EnclaveStateSnapshotRepository, EnclaveStfEnclaveSigner, EnclaveTopPool, EnclaveTopPoolAuthor, DIRECT_RPC_REQUEST_SINK_COMPONENT, - GLOBAL_ATTESTATION_HANDLER_COMPONENT, GLOBAL_DIRECT_RPC_BROADCASTER_COMPONENT, - GLOBAL_INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_SEAL, GLOBAL_OCALL_API_COMPONENT, - GLOBAL_RPC_WS_HANDLER_COMPONENT, GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT, - GLOBAL_SIDECHAIN_BLOCK_COMPOSER_COMPONENT, GLOBAL_SIDECHAIN_BLOCK_SYNCER_COMPONENT, - GLOBAL_SIDECHAIN_FAIL_SLOT_ON_DEMAND_COMPONENT, GLOBAL_SIDECHAIN_IMPORT_QUEUE_COMPONENT, - GLOBAL_SIDECHAIN_IMPORT_QUEUE_WORKER_COMPONENT, GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT, - GLOBAL_STATE_HANDLER_COMPONENT, GLOBAL_STATE_KEY_REPOSITORY_COMPONENT, - GLOBAL_STATE_OBSERVER_COMPONENT, GLOBAL_TARGET_A_PARENTCHAIN_LIGHT_CLIENT_SEAL, + GLOBAL_ATTESTATION_HANDLER_COMPONENT, GLOBAL_DATA_PROVIDER_CONFIG, + GLOBAL_DIRECT_RPC_BROADCASTER_COMPONENT, GLOBAL_INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_SEAL, + GLOBAL_OCALL_API_COMPONENT, GLOBAL_RPC_WS_HANDLER_COMPONENT, + GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT, GLOBAL_SIDECHAIN_BLOCK_COMPOSER_COMPONENT, + GLOBAL_SIDECHAIN_BLOCK_SYNCER_COMPONENT, GLOBAL_SIDECHAIN_FAIL_SLOT_ON_DEMAND_COMPONENT, + GLOBAL_SIDECHAIN_IMPORT_QUEUE_COMPONENT, GLOBAL_SIDECHAIN_IMPORT_QUEUE_WORKER_COMPONENT, + GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT, GLOBAL_STATE_HANDLER_COMPONENT, + GLOBAL_STATE_KEY_REPOSITORY_COMPONENT, GLOBAL_STATE_OBSERVER_COMPONENT, + GLOBAL_TARGET_A_PARENTCHAIN_LIGHT_CLIENT_SEAL, GLOBAL_TARGET_B_PARENTCHAIN_LIGHT_CLIENT_SEAL, GLOBAL_TOP_POOL_AUTHOR_COMPONENT, GLOBAL_WEB_SOCKET_SERVER_COMPONENT, }, @@ -83,7 +84,10 @@ use its_sidechain::{ block_composer::BlockComposer, slots::{FailSlotMode, FailSlotOnDemand}, }; +use lc_data_providers::DataProviderConfig; use lc_scheduled_enclave::{ScheduledEnclaveUpdater, GLOBAL_SCHEDULED_ENCLAVE}; +use lc_stf_task_receiver::{run_stf_task_receiver, StfTaskContext}; +use lc_vc_task_receiver::run_vc_handler_runner; use litentry_primitives::BroadcastedRequest; use log::*; use sgx_types::sgx_status_t; @@ -212,6 +216,19 @@ pub(crate) fn init_enclave( Arc::new(IntelAttestationHandler::new(ocall_api, signing_key_repository)); GLOBAL_ATTESTATION_HANDLER_COMPONENT.initialize(attestation_handler); + let data_provider_config = DataProviderConfig::new(); + GLOBAL_DATA_PROVIDER_CONFIG.initialize(data_provider_config.into()); + + std::thread::spawn(move || { + #[allow(clippy::unwrap_used)] + run_stf_task_handler().unwrap(); + }); + + std::thread::spawn(move || { + #[allow(clippy::unwrap_used)] + run_vc_issuance().unwrap(); + }); + Ok(()) } @@ -230,6 +247,64 @@ fn initialize_state_observer( Ok(Arc::new(EnclaveStateObserver::from_map(states_map))) } +fn run_stf_task_handler() -> Result<(), Error> { + let author_api = GLOBAL_TOP_POOL_AUTHOR_COMPONENT.get()?; + let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; + let state_observer = GLOBAL_STATE_OBSERVER_COMPONENT.get()?; + let data_provider_config = GLOBAL_DATA_PROVIDER_CONFIG.get()?; + + let shielding_key_repository = GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT.get()?; + + let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; + let stf_enclave_signer = Arc::new(EnclaveStfEnclaveSigner::new( + state_observer, + ocall_api.clone(), + shielding_key_repository.clone(), + author_api.clone(), + )); + + let stf_task_context = StfTaskContext::new( + shielding_key_repository, + author_api, + stf_enclave_signer, + state_handler, + ocall_api, + data_provider_config, + ); + + run_stf_task_receiver(Arc::new(stf_task_context)).map_err(Error::StfTaskReceiver) +} + +fn run_vc_issuance() -> Result<(), Error> { + let author_api = GLOBAL_TOP_POOL_AUTHOR_COMPONENT.get()?; + let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; + let state_observer = GLOBAL_STATE_OBSERVER_COMPONENT.get()?; + let data_provider_config = GLOBAL_DATA_PROVIDER_CONFIG.get()?; + + let shielding_key_repository = GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT.get()?; + #[allow(clippy::unwrap_used)] + let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; + let stf_enclave_signer = Arc::new(EnclaveStfEnclaveSigner::new( + state_observer, + ocall_api.clone(), + shielding_key_repository.clone(), + author_api.clone(), + )); + + let stf_task_context = StfTaskContext::new( + shielding_key_repository, + author_api, + stf_enclave_signer, + state_handler, + ocall_api, + data_provider_config, + ); + let extrinsic_factory = get_extrinsic_factory_from_integritee_solo_or_parachain()?; + let node_metadata_repo = get_node_metadata_repository_from_integritee_solo_or_parachain()?; + run_vc_handler_runner(Arc::new(stf_task_context), extrinsic_factory, node_metadata_repo); + Ok(()) +} + pub(crate) fn init_enclave_sidechain_components( fail_mode: Option, fail_at: u64, diff --git a/tee-worker/enclave-runtime/src/lib.rs b/tee-worker/enclave-runtime/src/lib.rs index b0f3d8b98b..9c3b078558 100644 --- a/tee-worker/enclave-runtime/src/lib.rs +++ b/tee-worker/enclave-runtime/src/lib.rs @@ -94,7 +94,6 @@ mod ocall; mod shard_vault; mod stf_task_handler; mod utils; -mod vc_issuance_task; pub mod error; pub mod rpc; diff --git a/tee-worker/enclave-runtime/src/stf_task_handler.rs b/tee-worker/enclave-runtime/src/stf_task_handler.rs index 11876a4418..8b13789179 100644 --- a/tee-worker/enclave-runtime/src/stf_task_handler.rs +++ b/tee-worker/enclave-runtime/src/stf_task_handler.rs @@ -1,122 +1 @@ -// Copyright 2020-2023 Trust Computing GmbH. -// This file is part of Litentry. -// -// Litentry is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Litentry is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Litentry. If not, see . -use crate::utils::DecodeRaw; -use itp_component_container::ComponentGetter; -use itp_sgx_crypto::key_repository::AccessKey; -use lc_data_providers::{DataProviderConfig, GLOBAL_DATA_PROVIDER_CONFIG}; -use lc_stf_task_receiver::{run_stf_task_receiver, StfTaskContext}; -use log::*; -use sgx_types::sgx_status_t; -use std::sync::Arc; - -use crate::{ - error::{Error, Result}, - initialization::global_components::{ - EnclaveStfEnclaveSigner, GLOBAL_OCALL_API_COMPONENT, - GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT, GLOBAL_STATE_OBSERVER_COMPONENT, - GLOBAL_TOP_POOL_AUTHOR_COMPONENT, - }, - GLOBAL_STATE_HANDLER_COMPONENT, -}; - -#[no_mangle] -pub unsafe extern "C" fn run_stf_task_handler(dpc: *const u8, dpc_size: usize) -> sgx_status_t { - let data_provider_config = match DataProviderConfig::decode_raw(dpc, dpc_size) { - Ok(data_provider_config) => data_provider_config, - Err(e) => return Error::Codec(e).into(), - }; - - match GLOBAL_DATA_PROVIDER_CONFIG.write() { - Ok(mut dpc) => { - dpc.set_twitter_official_url(data_provider_config.twitter_official_url); - dpc.set_twitter_litentry_url(data_provider_config.twitter_litentry_url); - dpc.set_twitter_auth_token_v2(data_provider_config.twitter_auth_token_v2); - dpc.set_discord_official_url(data_provider_config.discord_official_url); - dpc.set_discord_litentry_url(data_provider_config.discord_litentry_url); - dpc.set_discord_auth_token(data_provider_config.discord_auth_token); - dpc.set_achainable_url(data_provider_config.achainable_url); - dpc.set_achainable_auth_key(data_provider_config.achainable_auth_key); - dpc.set_credential_endpoint(data_provider_config.credential_endpoint); - dpc.set_oneblock_notion_key(data_provider_config.oneblock_notion_key); - dpc.set_oneblock_notion_url(data_provider_config.oneblock_notion_url); - dpc.set_sora_quiz_master_id(data_provider_config.sora_quiz_master_id); - dpc.set_sora_quiz_attendee_id(data_provider_config.sora_quiz_attendee_id); - dpc.set_nodereal_api_key(data_provider_config.nodereal_api_key); - dpc.set_nodereal_api_retry_delay(data_provider_config.nodereal_api_retry_delay); - dpc.set_nodereal_api_retry_times(data_provider_config.nodereal_api_retry_times); - dpc.set_nodereal_api_url(data_provider_config.nodereal_api_url); - dpc.set_nodereal_api_chain_network_url( - data_provider_config.nodereal_api_chain_network_url, - ); - dpc.set_contest_legend_discord_role_id( - data_provider_config.contest_legend_discord_role_id, - ); - dpc.set_contest_popularity_discord_role_id( - data_provider_config.contest_popularity_discord_role_id, - ); - dpc.set_contest_participant_discord_role_id( - data_provider_config.contest_participant_discord_role_id, - ); - dpc.set_vip3_url(data_provider_config.vip3_url); - dpc.set_geniidata_url(data_provider_config.geniidata_url); - dpc.set_geniidata_api_key(data_provider_config.geniidata_api_key); - }, - Err(e) => { - error!("Error while setting data provider config: {:?}", e); - return Error::MutexAccess.into() - }, - } - - if let Err(e) = run_stf_task_handler_internal() { - error!("Error while running stf task handler thread: {:?}", e); - return e.into() - } - - sgx_status_t::SGX_SUCCESS -} - -/// Internal [`run_stf_task_handler`] function to be able to use the `?` operator. -/// -/// Runs an extrinsic request inside the enclave, opening a channel and waiting for -/// senders to send requests. -fn run_stf_task_handler_internal() -> Result<()> { - let author_api = GLOBAL_TOP_POOL_AUTHOR_COMPONENT.get()?; - let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; - let state_observer = GLOBAL_STATE_OBSERVER_COMPONENT.get()?; - - let shielding_key_repository = GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT.get()?; - #[allow(clippy::unwrap_used)] - let shielding_key = shielding_key_repository.retrieve_key().unwrap(); - - let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; - let stf_enclave_signer = Arc::new(EnclaveStfEnclaveSigner::new( - state_observer, - ocall_api.clone(), - shielding_key_repository, - author_api.clone(), - )); - - let stf_task_context = StfTaskContext::new( - shielding_key, - author_api, - stf_enclave_signer, - state_handler, - ocall_api, - ); - - run_stf_task_receiver(Arc::new(stf_task_context)).map_err(Error::StfTaskReceiver) -} diff --git a/tee-worker/enclave-runtime/src/vc_issuance_task.rs b/tee-worker/enclave-runtime/src/vc_issuance_task.rs index bdbce0ecc7..e69de29bb2 100644 --- a/tee-worker/enclave-runtime/src/vc_issuance_task.rs +++ b/tee-worker/enclave-runtime/src/vc_issuance_task.rs @@ -1,130 +0,0 @@ -// Copyright 2020-2023 Trust Computing GmbH. -// This file is part of Litentry. -// -// Litentry is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Litentry is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Litentry. If not, see . - -use crate::utils::DecodeRaw; -use itp_component_container::ComponentGetter; -use itp_sgx_crypto::key_repository::AccessKey; -use lc_data_providers::{DataProviderConfig, GLOBAL_DATA_PROVIDER_CONFIG}; -use lc_stf_task_receiver::StfTaskContext; -use lc_vc_task_receiver::run_vc_handler_runner; -use log::*; -use sgx_types::sgx_status_t; -use std::sync::Arc; - -use crate::{ - error::{Error, Result}, - initialization::global_components::{ - EnclaveStfEnclaveSigner, GLOBAL_OCALL_API_COMPONENT, - GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT, GLOBAL_STATE_OBSERVER_COMPONENT, - GLOBAL_TOP_POOL_AUTHOR_COMPONENT, - }, - utils::{ - get_extrinsic_factory_from_integritee_solo_or_parachain, - get_node_metadata_repository_from_integritee_solo_or_parachain, - }, - GLOBAL_STATE_HANDLER_COMPONENT, -}; - -#[no_mangle] -pub unsafe extern "C" fn run_vc_issuance(dpc: *const u8, dpc_size: usize) -> sgx_status_t { - let data_provider_config = match DataProviderConfig::decode_raw(dpc, dpc_size) { - Ok(data_provider_config) => data_provider_config, - Err(e) => return Error::Codec(e).into(), - }; - - match GLOBAL_DATA_PROVIDER_CONFIG.write() { - Ok(mut dpc) => { - dpc.set_twitter_official_url(data_provider_config.twitter_official_url); - dpc.set_twitter_litentry_url(data_provider_config.twitter_litentry_url); - dpc.set_twitter_auth_token_v2(data_provider_config.twitter_auth_token_v2); - dpc.set_discord_official_url(data_provider_config.discord_official_url); - dpc.set_discord_litentry_url(data_provider_config.discord_litentry_url); - dpc.set_discord_auth_token(data_provider_config.discord_auth_token); - dpc.set_achainable_url(data_provider_config.achainable_url); - dpc.set_achainable_auth_key(data_provider_config.achainable_auth_key); - dpc.set_credential_endpoint(data_provider_config.credential_endpoint); - dpc.set_oneblock_notion_key(data_provider_config.oneblock_notion_key); - dpc.set_oneblock_notion_url(data_provider_config.oneblock_notion_url); - dpc.set_sora_quiz_master_id(data_provider_config.sora_quiz_master_id); - dpc.set_sora_quiz_attendee_id(data_provider_config.sora_quiz_attendee_id); - dpc.set_nodereal_api_key(data_provider_config.nodereal_api_key); - dpc.set_nodereal_api_retry_delay(data_provider_config.nodereal_api_retry_delay); - dpc.set_nodereal_api_retry_times(data_provider_config.nodereal_api_retry_times); - dpc.set_nodereal_api_url(data_provider_config.nodereal_api_url); - dpc.set_nodereal_api_chain_network_url( - data_provider_config.nodereal_api_chain_network_url, - ); - dpc.set_contest_legend_discord_role_id( - data_provider_config.contest_legend_discord_role_id, - ); - dpc.set_contest_popularity_discord_role_id( - data_provider_config.contest_popularity_discord_role_id, - ); - dpc.set_contest_participant_discord_role_id( - data_provider_config.contest_participant_discord_role_id, - ); - dpc.set_vip3_url(data_provider_config.vip3_url); - dpc.set_geniidata_url(data_provider_config.geniidata_url); - dpc.set_geniidata_api_key(data_provider_config.geniidata_api_key); - }, - Err(e) => { - error!("Error while setting data provider config: {:?}", e); - return Error::MutexAccess.into() - }, - } - - println!("[+] Starting to Run VC Issuance Internal"); - if let Err(e) = run_vc_issuance_internal() { - error!("Error while running stf task handler thread: {:?}", e); - return e.into() - } - - sgx_status_t::SGX_SUCCESS -} - -/// Internal [`run_stf_task_handler`] function to be able to use the `?` operator. -/// -/// Runs an extrinsic request inside the enclave, opening a channel and waiting for -/// senders to send requests. -fn run_vc_issuance_internal() -> Result<()> { - let author_api = GLOBAL_TOP_POOL_AUTHOR_COMPONENT.get()?; - let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; - let state_observer = GLOBAL_STATE_OBSERVER_COMPONENT.get()?; - - let shielding_key_repository = GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT.get()?; - #[allow(clippy::unwrap_used)] - let shielding_key = shielding_key_repository.retrieve_key().unwrap(); - - let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; - let stf_enclave_signer = Arc::new(EnclaveStfEnclaveSigner::new( - state_observer, - ocall_api.clone(), - shielding_key_repository, - author_api.clone(), - )); - - let stf_task_context = StfTaskContext::new( - shielding_key, - author_api, - stf_enclave_signer, - state_handler, - ocall_api, - ); - let extrinsic_factory = get_extrinsic_factory_from_integritee_solo_or_parachain()?; - let node_metadata_repo = get_node_metadata_repository_from_integritee_solo_or_parachain()?; - run_vc_handler_runner(Arc::new(stf_task_context), extrinsic_factory, node_metadata_repo); - Ok(()) -} diff --git a/tee-worker/litentry/core/assertion-build/src/a14.rs b/tee-worker/litentry/core/assertion-build/src/a14.rs index 1c05d3d0df..72ebef5c58 100644 --- a/tee-worker/litentry/core/assertion-build/src/a14.rs +++ b/tee-worker/litentry/core/assertion-build/src/a14.rs @@ -32,9 +32,7 @@ use itc_rest_client::{ rest_client::RestClient, RestPath, RestPost, }; -use lc_data_providers::{ - build_client, DataProviderConfig, DataProviderConfigReader, ReadDataProviderConfig, -}; +use lc_data_providers::{build_client, DataProviderConfig}; use serde::{Deserialize, Serialize}; const VC_A14_SUBJECT_DESCRIPTION: &str = @@ -99,7 +97,10 @@ impl A14Client { } } -pub fn build(req: &AssertionBuildRequest) -> Result { +pub fn build( + req: &AssertionBuildRequest, + data_provider_config: &DataProviderConfig, +) -> Result { debug!("Assertion A14 build, who: {:?}", account_id_to_string(&req.who)); // achainable expects polkadot addresses (those start with 1...) @@ -111,12 +112,8 @@ pub fn build(req: &AssertionBuildRequest) -> Result { polkadot_addresses.push(address); } } - - let data_provider_config = - DataProviderConfigReader::read().map_err(|e| Error::RequestVCFailed(Assertion::A14, e))?; - let mut value = false; - let mut client = A14Client::new(&data_provider_config); + let mut client = A14Client::new(data_provider_config); for address in polkadot_addresses { let data = A14Data { diff --git a/tee-worker/litentry/core/assertion-build/src/a2.rs b/tee-worker/litentry/core/assertion-build/src/a2.rs index 60f9420934..d6063b45f6 100644 --- a/tee-worker/litentry/core/assertion-build/src/a2.rs +++ b/tee-worker/litentry/core/assertion-build/src/a2.rs @@ -21,14 +21,20 @@ compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the sam extern crate sgx_tstd as std; use crate::*; -use lc_data_providers::{discord_litentry::DiscordLitentryClient, vec_to_string}; +use lc_data_providers::{ + discord_litentry::DiscordLitentryClient, vec_to_string, DataProviderConfig, +}; const VC_A2_SUBJECT_DESCRIPTION: &str = "The user is a member of Litentry Discord. Server link: https://discord.gg/phBSa3eMX9 Guild ID: 807161594245152800."; const VC_A2_SUBJECT_TYPE: &str = "Litentry Discord Member"; -pub fn build(req: &AssertionBuildRequest, guild_id: ParameterString) -> Result { +pub fn build( + req: &AssertionBuildRequest, + guild_id: ParameterString, + data_provider_config: &DataProviderConfig, +) -> Result { debug!("Assertion A2 build, who: {:?}", account_id_to_string(&req.who)); let mut discord_cnt: i32 = 0; @@ -38,7 +44,7 @@ pub fn build(req: &AssertionBuildRequest, guild_id: ParameterString) -> Result = format!("{}", guild_id_u).as_bytes().to_vec(); @@ -113,7 +117,7 @@ mod tests { req_ext_hash: Default::default(), }; - let _ = build(&req, guild_id); + let _ = build(&req, guild_id, &data_provider_config); log::info!("build A2 done"); } } diff --git a/tee-worker/litentry/core/assertion-build/src/a3.rs b/tee-worker/litentry/core/assertion-build/src/a3.rs index b0209afdab..4790256730 100644 --- a/tee-worker/litentry/core/assertion-build/src/a3.rs +++ b/tee-worker/litentry/core/assertion-build/src/a3.rs @@ -21,7 +21,9 @@ compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the sam extern crate sgx_tstd as std; use crate::*; -use lc_data_providers::{discord_litentry::DiscordLitentryClient, vec_to_string}; +use lc_data_providers::{ + discord_litentry::DiscordLitentryClient, vec_to_string, DataProviderConfig, +}; const VC_A3_SUBJECT_DESCRIPTION: &str = "You have commented in Litentry Discord #🪂id-hubber channel. Channel link: https://discord.com/channels/807161594245152800/1093886939746291882"; @@ -32,6 +34,7 @@ pub fn build( guild_id: ParameterString, channel_id: ParameterString, role_id: ParameterString, + data_provider_config: &DataProviderConfig, ) -> Result { debug!("Assertion A3 build, who: {:?}", account_id_to_string(&req.who),); @@ -56,7 +59,7 @@ pub fn build( ) })?; - let mut client = DiscordLitentryClient::new(); + let mut client = DiscordLitentryClient::new(&data_provider_config.discord_litentry_url); for identity in &req.identities { if let Identity::Discord(address) = &identity.0 { let resp = client @@ -106,17 +109,15 @@ mod tests { use crate::{a3::build, AccountId, AssertionBuildRequest}; use frame_support::BoundedVec; use itp_stf_primitives::types::ShardIdentifier; - use lc_data_providers::GLOBAL_DATA_PROVIDER_CONFIG; + use lc_data_providers::DataProviderConfig; use litentry_primitives::{Assertion, Identity, IdentityNetworkTuple, IdentityString}; use log; use std::{format, vec, vec::Vec}; #[test] fn build_a3_works() { - GLOBAL_DATA_PROVIDER_CONFIG - .write() - .unwrap() - .set_discord_litentry_url("http://localhost:19527".to_string()); + let mut data_provider_config = DataProviderConfig::new(); + data_provider_config.set_discord_litentry_url("http://localhost:19527".to_string()); let guild_id_u: u64 = 919848390156767232; let channel_id_u: u64 = 919848392035794945; let role_id_u: u64 = 1034083718425493544; @@ -146,7 +147,7 @@ mod tests { req_ext_hash: Default::default(), }; - let _ = build(&req, guild_id, channel_id, role_id); + let _ = build(&req, guild_id, channel_id, role_id, &data_provider_config); log::info!("build A3 done"); } } diff --git a/tee-worker/litentry/core/assertion-build/src/a6.rs b/tee-worker/litentry/core/assertion-build/src/a6.rs index cbb4f4bd27..0b1d82a8b4 100644 --- a/tee-worker/litentry/core/assertion-build/src/a6.rs +++ b/tee-worker/litentry/core/assertion-build/src/a6.rs @@ -21,7 +21,7 @@ compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the sam extern crate sgx_tstd as std; use crate::*; -use lc_data_providers::twitter_official::TwitterOfficialClient; +use lc_data_providers::{twitter_official::TwitterOfficialClient, DataProviderConfig}; const VC_A6_SUBJECT_DESCRIPTION: &str = "The range of the user's Twitter follower count"; const VC_A6_SUBJECT_TYPE: &str = "Twitter Follower Amount"; @@ -33,10 +33,16 @@ const VC_A6_SUBJECT_TYPE: &str = "Twitter Follower Amount"; /// * 1,000+ followers /// * 10,000+ followers /// * 100,000+ followers -pub fn build(req: &AssertionBuildRequest) -> Result { +pub fn build( + req: &AssertionBuildRequest, + data_provider_config: &DataProviderConfig, +) -> Result { debug!("Assertion A6 build, who: {:?}", account_id_to_string(&req.who),); - let mut client = TwitterOfficialClient::v2(); + let mut client = TwitterOfficialClient::v2( + &data_provider_config.twitter_official_url, + &data_provider_config.twitter_auth_token_v2, + ); let mut sum: u32 = 0; for identity in &req.identities { diff --git a/tee-worker/litentry/core/assertion-build/src/a8.rs b/tee-worker/litentry/core/assertion-build/src/a8.rs index 9633f1c3a1..a5ab09169e 100644 --- a/tee-worker/litentry/core/assertion-build/src/a8.rs +++ b/tee-worker/litentry/core/assertion-build/src/a8.rs @@ -23,22 +23,23 @@ extern crate sgx_tstd as std; use crate::*; use lc_data_providers::{ achainable::{AchainableAccountTotalTransactions, AchainableClient}, - DataProviderConfigReader, ReadDataProviderConfig, + DataProviderConfig, }; use litentry_primitives::BoundedWeb3Network; const VC_A8_SUBJECT_DESCRIPTION: &str = "Gets the range of number of transactions a user has made for a specific token on all supported networks (invalid transactions are also counted)"; const VC_A8_SUBJECT_TYPE: &str = "EVM/Substrate Transaction Count"; -pub fn build(req: &AssertionBuildRequest) -> Result { +pub fn build( + req: &AssertionBuildRequest, + data_provider_config: &DataProviderConfig, +) -> Result { debug!("Assertion A8 build, who: {:?}", account_id_to_string(&req.who),); // It should never fail because `req.assertion.get_supported_web3networks()` // returns the vector which is converted from a BoundedVec let bounded_web3networks: BoundedWeb3Network = req.assertion.get_supported_web3networks().try_into().unwrap(); - let data_provider_config = DataProviderConfigReader::read() - .map_err(|e| Error::RequestVCFailed(Assertion::A8(bounded_web3networks.clone()), e))?; - let mut client = AchainableClient::new(&data_provider_config); + let mut client = AchainableClient::new(data_provider_config); let mut total_txs: u64 = 0; let identities: Vec<(Web3Network, Vec)> = transpose_identity(&req.identities); diff --git a/tee-worker/litentry/core/assertion-build/src/achainable/amount.rs b/tee-worker/litentry/core/assertion-build/src/achainable/amount.rs index a3dd24bf74..0cf9777f7d 100644 --- a/tee-worker/litentry/core/assertion-build/src/achainable/amount.rs +++ b/tee-worker/litentry/core/assertion-build/src/achainable/amount.rs @@ -25,7 +25,9 @@ use crate::{ *, }; use lc_credentials::litentry_profile::holding_amount::LitentryProfileHoldingAmount; -use lc_data_providers::{achainable_names::AchainableNameAmount, ConvertParameterString}; +use lc_data_providers::{ + achainable_names::AchainableNameAmount, ConvertParameterString, DataProviderConfig, +}; const CREATED_OVER_AMOUNT_CONTRACTS: &str = "Created over {amount} contracts"; const BALANCE_OVER_AMOUNT: &str = "Balance over {amount}"; @@ -98,7 +100,11 @@ const BALANCE_OVER_AMOUNT: &str = "Balance over {amount}"; /// ] /// } /// -pub fn build_amount(req: &AssertionBuildRequest, param: AchainableAmount) -> Result { +pub fn build_amount( + req: &AssertionBuildRequest, + param: AchainableAmount, + data_provider_config: &DataProviderConfig, +) -> Result { debug!("Assertion Achainable build_amount, who: {:?}", account_id_to_string(&req.who)); let identities = transpose_identity(&req.identities); let addresses = identities @@ -116,16 +122,17 @@ pub fn build_amount(req: &AssertionBuildRequest, param: AchainableAmount) -> Res let mut balance = 0.0; let mut flag = false; if bname == AchainableNameAmount::BalanceUnderAmount { - balance = request_achainable_balance(addresses, achainable_param.clone())? - .parse::() - .map_err(|_| { - Error::RequestVCFailed( - Assertion::Achainable(achainable_param.clone()), - ErrorDetail::ParseError, - ) - })?; + balance = + request_achainable_balance(addresses, achainable_param.clone(), data_provider_config)? + .parse::() + .map_err(|_| { + Error::RequestVCFailed( + Assertion::Achainable(achainable_param.clone()), + ErrorDetail::ParseError, + ) + })?; } else { - flag = request_achainable(addresses, achainable_param.clone())?; + flag = request_achainable(addresses, achainable_param.clone(), data_provider_config)?; } match Credential::new(&req.who, &req.shard) { diff --git a/tee-worker/litentry/core/assertion-build/src/achainable/amount_holding.rs b/tee-worker/litentry/core/assertion-build/src/achainable/amount_holding.rs index f51033e6db..9b2f151acb 100644 --- a/tee-worker/litentry/core/assertion-build/src/achainable/amount_holding.rs +++ b/tee-worker/litentry/core/assertion-build/src/achainable/amount_holding.rs @@ -22,11 +22,12 @@ extern crate sgx_tstd as std; use crate::{achainable::request_achainable_balance, *}; use lc_credentials::litentry_profile::token_balance::TokenBalanceInfo; -use lc_data_providers::{ETokenAddress, TokenFromString}; +use lc_data_providers::{DataProviderConfig, ETokenAddress, TokenFromString}; pub fn build_amount_holding( req: &AssertionBuildRequest, param: AchainableAmountHolding, + data_provider_config: &DataProviderConfig, ) -> Result { let identities = transpose_identity(&req.identities); let addresses = identities @@ -36,14 +37,15 @@ pub fn build_amount_holding( let token = ETokenAddress::from_vec(param.clone().token.unwrap_or_default()); let achainable_param = AchainableParams::AmountHolding(param); - let balance = request_achainable_balance(addresses, achainable_param.clone())? - .parse::() - .map_err(|_| { - Error::RequestVCFailed( - Assertion::Achainable(achainable_param.clone()), - ErrorDetail::ParseError, - ) - })?; + let balance = + request_achainable_balance(addresses, achainable_param.clone(), data_provider_config)? + .parse::() + .map_err(|_| { + Error::RequestVCFailed( + Assertion::Achainable(achainable_param.clone()), + ErrorDetail::ParseError, + ) + })?; match Credential::new(&req.who, &req.shard) { Ok(mut credential_unsigned) => { diff --git a/tee-worker/litentry/core/assertion-build/src/achainable/amount_token.rs b/tee-worker/litentry/core/assertion-build/src/achainable/amount_token.rs index 77c2615edd..3c4ee1df34 100644 --- a/tee-worker/litentry/core/assertion-build/src/achainable/amount_token.rs +++ b/tee-worker/litentry/core/assertion-build/src/achainable/amount_token.rs @@ -29,7 +29,7 @@ use lc_credentials::{ litentry_profile::token_balance::TokenBalanceInfo, }; use lc_data_providers::{ - achainable_names::AchainableNameAmountToken, ETokenAddress, TokenFromString, + achainable_names::AchainableNameAmountToken, DataProviderConfig, ETokenAddress, TokenFromString, }; /// ERC20 Holder: USDC and others @@ -53,6 +53,7 @@ use lc_data_providers::{ pub fn build_amount_token( req: &AssertionBuildRequest, param: AchainableAmountToken, + data_provider_config: &DataProviderConfig, ) -> Result { debug!("Assertion Building AchainableAmountToken"); @@ -75,7 +76,8 @@ pub fn build_amount_token( })?; match amount_token_name { AchainableNameAmountToken::LITHoldingAmount => { - let lit_holding_amount = query_lit_holding_amount(&achainable_param, &identities)?; + let lit_holding_amount = + query_lit_holding_amount(&achainable_param, &identities, data_provider_config)?; credential.update_lit_holding_amount(lit_holding_amount); }, _ => { @@ -85,14 +87,18 @@ pub fn build_amount_token( .flat_map(|(_, addresses)| addresses) .collect::>(); let token = ETokenAddress::from_vec(param.token.unwrap_or_default()); - let balance = request_achainable_balance(addresses, achainable_param.clone())? - .parse::() - .map_err(|_| { - Error::RequestVCFailed( - Assertion::Achainable(achainable_param.clone()), - ErrorDetail::ParseError, - ) - })?; + let balance = request_achainable_balance( + addresses, + achainable_param.clone(), + data_provider_config, + )? + .parse::() + .map_err(|_| { + Error::RequestVCFailed( + Assertion::Achainable(achainable_param.clone()), + ErrorDetail::ParseError, + ) + })?; credential.update_token_balance(token, balance); }, diff --git a/tee-worker/litentry/core/assertion-build/src/achainable/amounts.rs b/tee-worker/litentry/core/assertion-build/src/achainable/amounts.rs index 7d6bb4c74a..826f423b40 100644 --- a/tee-worker/litentry/core/assertion-build/src/achainable/amounts.rs +++ b/tee-worker/litentry/core/assertion-build/src/achainable/amounts.rs @@ -21,8 +21,13 @@ compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the sam extern crate sgx_tstd as std; use crate::{achainable::request_achainable, *}; +use lc_data_providers::DataProviderConfig; -pub fn build_amounts(req: &AssertionBuildRequest, param: AchainableAmounts) -> Result { +pub fn build_amounts( + req: &AssertionBuildRequest, + param: AchainableAmounts, + data_provider_config: &DataProviderConfig, +) -> Result { debug!("Assertion Achainable build_amounts, who: {:?}", account_id_to_string(&req.who)); let identities = transpose_identity(&req.identities); @@ -32,7 +37,7 @@ pub fn build_amounts(req: &AssertionBuildRequest, param: AchainableAmounts) -> R .collect::>(); let achainable_param = AchainableParams::Amounts(param); - let _flag = request_achainable(addresses, achainable_param.clone())?; + let _flag = request_achainable(addresses, achainable_param.clone(), data_provider_config)?; match Credential::new(&req.who, &req.shard) { Ok(mut _credential_unsigned) => Ok(_credential_unsigned), Err(e) => { diff --git a/tee-worker/litentry/core/assertion-build/src/achainable/basic.rs b/tee-worker/litentry/core/assertion-build/src/achainable/basic.rs index cc305089e1..e8310b5c4f 100644 --- a/tee-worker/litentry/core/assertion-build/src/achainable/basic.rs +++ b/tee-worker/litentry/core/assertion-build/src/achainable/basic.rs @@ -25,9 +25,13 @@ use crate::{ *, }; use lc_credentials::achainable::{bab_holder::UpdateBABHolder, uniswap_user::UpdateUniswapUser}; -use lc_data_providers::achainable_names::AchainableNameBasic; +use lc_data_providers::{achainable_names::AchainableNameBasic, DataProviderConfig}; -pub fn build_basic(req: &AssertionBuildRequest, param: AchainableBasic) -> Result { +pub fn build_basic( + req: &AssertionBuildRequest, + param: AchainableBasic, + data_provider_config: &DataProviderConfig, +) -> Result { debug!("Assertion Achainable building Basic"); let identities = transpose_identity(&req.identities); @@ -52,11 +56,13 @@ pub fn build_basic(req: &AssertionBuildRequest, param: AchainableBasic) -> Resul })?; match basic_name { AchainableNameBasic::UniswapV23User => { - let (v2_user, v3_user) = request_uniswap_v2_or_v3_user(addresses, achainable_param)?; + let (v2_user, v3_user) = + request_uniswap_v2_or_v3_user(addresses, achainable_param, data_provider_config)?; credential.update_uniswap_user(v2_user, v3_user); }, AchainableNameBasic::BABHolder => { - let is_bab_holder = request_achainable(addresses, achainable_param)?; + let is_bab_holder = + request_achainable(addresses, achainable_param, data_provider_config)?; credential.update_bab_holder(is_bab_holder); }, } diff --git a/tee-worker/litentry/core/assertion-build/src/achainable/between_percents.rs b/tee-worker/litentry/core/assertion-build/src/achainable/between_percents.rs index aeeec91e67..33334f063a 100644 --- a/tee-worker/litentry/core/assertion-build/src/achainable/between_percents.rs +++ b/tee-worker/litentry/core/assertion-build/src/achainable/between_percents.rs @@ -21,10 +21,12 @@ compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the sam extern crate sgx_tstd as std; use crate::{achainable::request_achainable, *}; +use lc_data_providers::DataProviderConfig; pub fn build_between_percents( req: &AssertionBuildRequest, param: AchainableBetweenPercents, + data_provider_config: &DataProviderConfig, ) -> Result { debug!( "Assertion Achainable build_between_percents, who: {:?}", @@ -38,7 +40,7 @@ pub fn build_between_percents( .collect::>(); let achainable_param = AchainableParams::BetweenPercents(param.clone()); - let _flag = request_achainable(addresses, achainable_param)?; + let _flag = request_achainable(addresses, achainable_param, data_provider_config)?; match Credential::new(&req.who, &req.shard) { Ok(mut _credential_unsigned) => Ok(_credential_unsigned), Err(e) => { diff --git a/tee-worker/litentry/core/assertion-build/src/achainable/class_of_year.rs b/tee-worker/litentry/core/assertion-build/src/achainable/class_of_year.rs index 40e85cf65b..ae46cd4ae0 100644 --- a/tee-worker/litentry/core/assertion-build/src/achainable/class_of_year.rs +++ b/tee-worker/litentry/core/assertion-build/src/achainable/class_of_year.rs @@ -22,6 +22,7 @@ extern crate sgx_tstd as std; use crate::{achainable::request_achainable_classofyear, *}; use lc_credentials::Credential; +use lc_data_providers::DataProviderConfig; use lc_stf_task_sender::AssertionBuildRequest; use litentry_primitives::{AchainableClassOfYear, AchainableParams}; use log::debug; @@ -59,6 +60,7 @@ const VC_SUBJECT_TYPE: &str = "Account Class Of Year"; pub fn build_class_of_year( req: &AssertionBuildRequest, param: AchainableClassOfYear, + data_provider_config: &DataProviderConfig, ) -> Result { debug!("Assertion Achainable build_class_of_year, who: {:?}", account_id_to_string(&req.who)); let identities = transpose_identity(&req.identities); @@ -68,7 +70,8 @@ pub fn build_class_of_year( .collect::>(); let achainable_param = AchainableParams::ClassOfYear(param); - let (ret, created_date) = request_achainable_classofyear(addresses, achainable_param.clone())?; + let (ret, created_date) = + request_achainable_classofyear(addresses, achainable_param.clone(), data_provider_config)?; match Credential::new(&req.who, &req.shard) { Ok(mut credential_unsigned) => { credential_unsigned.add_subject_info(VC_SUBJECT_DESCRIPTION, VC_SUBJECT_TYPE); diff --git a/tee-worker/litentry/core/assertion-build/src/achainable/date.rs b/tee-worker/litentry/core/assertion-build/src/achainable/date.rs index 1ab7a298a0..485280a149 100644 --- a/tee-worker/litentry/core/assertion-build/src/achainable/date.rs +++ b/tee-worker/litentry/core/assertion-build/src/achainable/date.rs @@ -21,8 +21,13 @@ compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the sam extern crate sgx_tstd as std; use crate::{achainable::request_achainable, *}; +use lc_data_providers::DataProviderConfig; -pub fn build_date(req: &AssertionBuildRequest, param: AchainableDate) -> Result { +pub fn build_date( + req: &AssertionBuildRequest, + param: AchainableDate, + data_provider_config: &DataProviderConfig, +) -> Result { debug!("Assertion Achainable build_date, who: {:?}", account_id_to_string(&req.who)); let identities = transpose_identity(&req.identities); @@ -32,7 +37,7 @@ pub fn build_date(req: &AssertionBuildRequest, param: AchainableDate) -> Result< .collect::>(); let achainable_param = AchainableParams::Date(param.clone()); - let _flag = request_achainable(addresses, achainable_param)?; + let _flag = request_achainable(addresses, achainable_param, data_provider_config)?; match Credential::new(&req.who, &req.shard) { Ok(mut _credential_unsigned) => Ok(_credential_unsigned), Err(e) => { diff --git a/tee-worker/litentry/core/assertion-build/src/achainable/date_interval.rs b/tee-worker/litentry/core/assertion-build/src/achainable/date_interval.rs index 9248aa13dc..411e808d76 100644 --- a/tee-worker/litentry/core/assertion-build/src/achainable/date_interval.rs +++ b/tee-worker/litentry/core/assertion-build/src/achainable/date_interval.rs @@ -22,10 +22,12 @@ extern crate sgx_tstd as std; use crate::{achainable::request_achainable, *}; use lc_credentials::Credential; +use lc_data_providers::DataProviderConfig; pub fn build_date_interval( req: &AssertionBuildRequest, param: AchainableDateInterval, + data_provider_config: &DataProviderConfig, ) -> Result { debug!("Assertion Achainable build_date_interval, who: {:?}", account_id_to_string(&req.who)); @@ -36,7 +38,7 @@ pub fn build_date_interval( .collect::>(); let achainable_param = AchainableParams::DateInterval(param.clone()); - let _flag = request_achainable(addresses, achainable_param)?; + let _flag = request_achainable(addresses, achainable_param, data_provider_config)?; match Credential::new(&req.who, &req.shard) { Ok(mut _credential_unsigned) => Ok(_credential_unsigned), Err(e) => { diff --git a/tee-worker/litentry/core/assertion-build/src/achainable/date_percent.rs b/tee-worker/litentry/core/assertion-build/src/achainable/date_percent.rs index 145610a30e..7c10a93e93 100644 --- a/tee-worker/litentry/core/assertion-build/src/achainable/date_percent.rs +++ b/tee-worker/litentry/core/assertion-build/src/achainable/date_percent.rs @@ -21,10 +21,12 @@ compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the sam extern crate sgx_tstd as std; use crate::{achainable::request_achainable, *}; +use lc_data_providers::DataProviderConfig; pub fn build_date_percent( req: &AssertionBuildRequest, param: AchainableDatePercent, + data_provider_config: &DataProviderConfig, ) -> Result { debug!("Assertion Achainable build_date_percent, who: {:?}", account_id_to_string(&req.who)); @@ -35,7 +37,7 @@ pub fn build_date_percent( .collect::>(); let achainable_param = AchainableParams::DatePercent(param.clone()); - let _flag = request_achainable(addresses, achainable_param)?; + let _flag = request_achainable(addresses, achainable_param, data_provider_config)?; match Credential::new(&req.who, &req.shard) { Ok(mut _credential_unsigned) => Ok(_credential_unsigned), Err(e) => { diff --git a/tee-worker/litentry/core/assertion-build/src/achainable/mirror.rs b/tee-worker/litentry/core/assertion-build/src/achainable/mirror.rs index b9761e9e30..8623c759ad 100644 --- a/tee-worker/litentry/core/assertion-build/src/achainable/mirror.rs +++ b/tee-worker/litentry/core/assertion-build/src/achainable/mirror.rs @@ -23,7 +23,7 @@ extern crate sgx_tstd as std; use super::request_achainable; use crate::*; use lc_credentials::{litentry_profile::mirror::MirrorInfo, Credential}; -use lc_data_providers::achainable_names::AchainableNameMirror; +use lc_data_providers::{achainable_names::AchainableNameMirror, DataProviderConfig}; use litentry_primitives::AchainableMirror; // Request Inputs @@ -46,7 +46,11 @@ use litentry_primitives::AchainableMirror; // "includeMetadata": true // } -pub fn build_on_mirror(req: &AssertionBuildRequest, param: AchainableMirror) -> Result { +pub fn build_on_mirror( + req: &AssertionBuildRequest, + param: AchainableMirror, + data_provider_config: &DataProviderConfig, +) -> Result { let identities = transpose_identity(&req.identities); let addresses = identities .into_iter() @@ -60,7 +64,7 @@ pub fn build_on_mirror(req: &AssertionBuildRequest, param: AchainableMirror) -> e.into_error_detail(), ) })?; - let value = request_achainable(addresses, achainable_param.clone())?; + let value = request_achainable(addresses, achainable_param.clone(), data_provider_config)?; match Credential::new(&req.who, &req.shard) { Ok(mut credential_unsigned) => { credential_unsigned.update_mirror(mtype, value); diff --git a/tee-worker/litentry/core/assertion-build/src/achainable/mod.rs b/tee-worker/litentry/core/assertion-build/src/achainable/mod.rs index 4d765c556a..ca1c07fced 100644 --- a/tee-worker/litentry/core/assertion-build/src/achainable/mod.rs +++ b/tee-worker/litentry/core/assertion-build/src/achainable/mod.rs @@ -32,7 +32,7 @@ use lc_data_providers::{ AchainableClient, AchainableTagDeFi, HoldingAmount, Params, ParamsBasicTypeWithAmountToken, }, achainable_names::{AchainableNameAmountToken, GetAchainableName}, - DataProviderConfigReader, ReadDataProviderConfig, LIT_TOKEN_ADDRESS, + DataProviderConfig, LIT_TOKEN_ADDRESS, }; use lc_stf_task_sender::AssertionBuildRequest; use litentry_primitives::AchainableParams; @@ -51,29 +51,41 @@ pub mod date_percent; pub mod mirror; pub mod token; -pub fn build(req: &AssertionBuildRequest, param: AchainableParams) -> Result { +pub fn build( + req: &AssertionBuildRequest, + param: AchainableParams, + data_provider_config: &DataProviderConfig, +) -> Result { match param { - AchainableParams::AmountHolding(param) => build_amount_holding(req, param), - AchainableParams::AmountToken(param) => build_amount_token(req, param), - AchainableParams::Amount(param) => build_amount(req, param), - AchainableParams::Amounts(param) => build_amounts(req, param), - AchainableParams::Basic(param) => build_basic(req, param), - AchainableParams::BetweenPercents(param) => build_between_percents(req, param), - AchainableParams::ClassOfYear(param) => build_class_of_year(req, param), - AchainableParams::DateInterval(param) => build_date_interval(req, param), - AchainableParams::DatePercent(param) => build_date_percent(req, param), - AchainableParams::Date(param) => build_date(req, param), - AchainableParams::Token(param) => build_token(req, param), - AchainableParams::Mirror(param) => build_on_mirror(req, param), + AchainableParams::AmountHolding(param) => + build_amount_holding(req, param, data_provider_config), + AchainableParams::AmountToken(param) => + build_amount_token(req, param, data_provider_config), + AchainableParams::Amount(param) => build_amount(req, param, data_provider_config), + AchainableParams::Amounts(param) => build_amounts(req, param, data_provider_config), + AchainableParams::Basic(param) => build_basic(req, param, data_provider_config), + AchainableParams::BetweenPercents(param) => + build_between_percents(req, param, data_provider_config), + AchainableParams::ClassOfYear(param) => + build_class_of_year(req, param, data_provider_config), + AchainableParams::DateInterval(param) => + build_date_interval(req, param, data_provider_config), + AchainableParams::DatePercent(param) => + build_date_percent(req, param, data_provider_config), + AchainableParams::Date(param) => build_date(req, param, data_provider_config), + AchainableParams::Token(param) => build_token(req, param, data_provider_config), + AchainableParams::Mirror(param) => build_on_mirror(req, param, data_provider_config), } } -pub fn request_achainable(addresses: Vec, param: AchainableParams) -> Result { +pub fn request_achainable( + addresses: Vec, + param: AchainableParams, + data_provider_config: &DataProviderConfig, +) -> Result { let request_param = Params::try_from(param.clone())?; - let data_provider_config = DataProviderConfigReader::read() - .map_err(|e| Error::RequestVCFailed(Assertion::Achainable(param.clone()), e))?; - let mut client: AchainableClient = AchainableClient::new(&data_provider_config); + let mut client: AchainableClient = AchainableClient::new(data_provider_config); for address in &addresses { let ret = client.query_system_label(address, request_param.clone()).map_err(|e| { @@ -93,12 +105,11 @@ pub fn request_achainable(addresses: Vec, param: AchainableParams) -> Re pub fn request_uniswap_v2_or_v3_user( addresses: Vec, param: AchainableParams, + data_provider_config: &DataProviderConfig, ) -> Result<(bool, bool)> { let _request_param = Params::try_from(param.clone())?; - let data_provider_config = DataProviderConfigReader::read() - .map_err(|e| Error::RequestVCFailed(Assertion::Achainable(param.clone()), e))?; - let mut client: AchainableClient = AchainableClient::new(&data_provider_config); + let mut client: AchainableClient = AchainableClient::new(data_provider_config); let mut v2_user = false; let mut v3_user = false; @@ -119,12 +130,10 @@ const INVALID_CLASS_OF_YEAR: &str = "Invalid"; pub fn request_achainable_classofyear( addresses: Vec, param: AchainableParams, + data_provider_config: &DataProviderConfig, ) -> Result<(bool, String)> { let request_param = Params::try_from(param.clone())?; - - let data_provider_config = DataProviderConfigReader::read() - .map_err(|e| Error::RequestVCFailed(Assertion::Achainable(param.clone()), e))?; - let mut client: AchainableClient = AchainableClient::new(&data_provider_config); + let mut client: AchainableClient = AchainableClient::new(data_provider_config); let mut longest_created_year = INVALID_CLASS_OF_YEAR.into(); for address in &addresses { @@ -148,12 +157,10 @@ pub fn request_achainable_classofyear( pub fn request_achainable_balance( addresses: Vec, param: AchainableParams, + data_provider_config: &DataProviderConfig, ) -> Result { let request_param = Params::try_from(param.clone())?; - - let data_provider_config = DataProviderConfigReader::read() - .map_err(|e| Error::RequestVCFailed(Assertion::Achainable(param.clone()), e))?; - let mut client: AchainableClient = AchainableClient::new(&data_provider_config); + let mut client: AchainableClient = AchainableClient::new(data_provider_config); let balance = client.holding_amount(addresses, request_param).map_err(|e| { Error::RequestVCFailed(Assertion::Achainable(param.clone()), e.into_error_detail()) })?; @@ -164,12 +171,10 @@ pub fn request_achainable_balance( pub fn query_lit_holding_amount( aparam: &AchainableParams, identities: &Vec<(Web3Network, Vec)>, + data_provider_config: &DataProviderConfig, ) -> Result { let mut total_lit_balance = 0_f64; - - let data_provider_config = DataProviderConfigReader::read() - .map_err(|e| Error::RequestVCFailed(Assertion::Achainable(aparam.clone()), e))?; - let mut client: AchainableClient = AchainableClient::new(&data_provider_config); + let mut client: AchainableClient = AchainableClient::new(data_provider_config); for (network, addresses) in identities { let (q_name, q_network, q_token) = if *network == Web3Network::Ethereum { diff --git a/tee-worker/litentry/core/assertion-build/src/achainable/token.rs b/tee-worker/litentry/core/assertion-build/src/achainable/token.rs index 36e2cca667..6df95c7e6e 100644 --- a/tee-worker/litentry/core/assertion-build/src/achainable/token.rs +++ b/tee-worker/litentry/core/assertion-build/src/achainable/token.rs @@ -21,8 +21,13 @@ compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the sam extern crate sgx_tstd as std; use crate::{achainable::request_achainable, *}; +use lc_data_providers::DataProviderConfig; -pub fn build_token(req: &AssertionBuildRequest, param: AchainableToken) -> Result { +pub fn build_token( + req: &AssertionBuildRequest, + param: AchainableToken, + data_provider_config: &DataProviderConfig, +) -> Result { debug!("Assertion Achainable build_token, who: {:?}", account_id_to_string(&req.who)); let identities = transpose_identity(&req.identities); @@ -32,7 +37,7 @@ pub fn build_token(req: &AssertionBuildRequest, param: AchainableToken) -> Resul .collect::>(); let achainable_param = AchainableParams::Token(param.clone()); - let _flag = request_achainable(addresses, achainable_param)?; + let _flag = request_achainable(addresses, achainable_param, data_provider_config)?; match Credential::new(&req.who, &req.shard) { Ok(mut _credential_unsigned) => Ok(_credential_unsigned), Err(e) => { diff --git a/tee-worker/litentry/core/assertion-build/src/brc20/amount_holder.rs b/tee-worker/litentry/core/assertion-build/src/brc20/amount_holder.rs index 09be442d97..93a07ef818 100644 --- a/tee-worker/litentry/core/assertion-build/src/brc20/amount_holder.rs +++ b/tee-worker/litentry/core/assertion-build/src/brc20/amount_holder.rs @@ -22,9 +22,12 @@ extern crate sgx_tstd as std; use crate::*; use lc_credentials::brc20::amount_holder::BRC20AmountHolderCredential; -use lc_data_providers::geniidata::GeniidataClient; +use lc_data_providers::{geniidata::GeniidataClient, DataProviderConfig}; -pub fn build(req: &AssertionBuildRequest) -> Result { +pub fn build( + req: &AssertionBuildRequest, + data_provider_config: &DataProviderConfig, +) -> Result { let identities = transpose_identity(&req.identities); let addresses = identities .into_iter() @@ -35,7 +38,7 @@ pub fn build(req: &AssertionBuildRequest) -> Result { error!("Generate unsigned credential failed {:?}", e); Error::RequestVCFailed(Assertion::BRC20AmountHolder, e.into_error_detail()) })?; - let mut client = GeniidataClient::new() + let mut client = GeniidataClient::new(data_provider_config) .map_err(|e| Error::RequestVCFailed(Assertion::BRC20AmountHolder, e))?; let response = client.create_brc20_amount_holder_sum(addresses).map_err(|e| { Error::RequestVCFailed( diff --git a/tee-worker/litentry/core/assertion-build/src/generic_discord_role.rs b/tee-worker/litentry/core/assertion-build/src/generic_discord_role.rs index 3b16bebcea..002f111cf6 100644 --- a/tee-worker/litentry/core/assertion-build/src/generic_discord_role.rs +++ b/tee-worker/litentry/core/assertion-build/src/generic_discord_role.rs @@ -22,19 +22,23 @@ extern crate sgx_tstd as std; use crate::*; use lc_credentials::{generic_discord_role::GenericDiscordRoleAssertionUpdate, Credential}; -use lc_data_providers::{ - discord_litentry::DiscordLitentryClient, DataProviderConfigReader, ReadDataProviderConfig, -}; +use lc_data_providers::{discord_litentry::DiscordLitentryClient, DataProviderConfig}; use lc_stf_task_sender::AssertionBuildRequest; use litentry_primitives::{ContestType, GenericDiscordRoleType, SoraQuizType}; - -pub fn build(req: &AssertionBuildRequest, rtype: GenericDiscordRoleType) -> Result { - let role_id = get_generic_discord_role_id(&rtype).map_err(|error_detail| { - Error::RequestVCFailed(Assertion::GenericDiscordRole(rtype.clone()), error_detail) - })?; +use std::string::ToString; + +pub fn build( + req: &AssertionBuildRequest, + rtype: GenericDiscordRoleType, + data_provider_config: &DataProviderConfig, +) -> Result { + let role_id = + get_generic_discord_role_id(&rtype, data_provider_config).map_err(|error_detail| { + Error::RequestVCFailed(Assertion::GenericDiscordRole(rtype.clone()), error_detail) + })?; let mut has_role_value = false; - let mut client = DiscordLitentryClient::new(); + let mut client = DiscordLitentryClient::new(&data_provider_config.discord_litentry_url); for identity in &req.identities { if let Identity::Discord(address) = &identity.0 { let resp = @@ -69,18 +73,20 @@ pub fn build(req: &AssertionBuildRequest, rtype: GenericDiscordRoleType) -> Resu fn get_generic_discord_role_id( rtype: &GenericDiscordRoleType, + data_provider_config: &DataProviderConfig, ) -> core::result::Result { - let data_provider_config = DataProviderConfigReader::read()?; match rtype { GenericDiscordRoleType::Contest(ctype) => match ctype { - ContestType::Legend => Ok(data_provider_config.contest_legend_discord_role_id), - ContestType::Popularity => Ok(data_provider_config.contest_popularity_discord_role_id), + ContestType::Legend => + Ok(data_provider_config.contest_legend_discord_role_id.to_string()), + ContestType::Popularity => + Ok(data_provider_config.contest_popularity_discord_role_id.to_string()), ContestType::Participant => - Ok(data_provider_config.contest_participant_discord_role_id), + Ok(data_provider_config.contest_participant_discord_role_id.to_string()), }, GenericDiscordRoleType::SoraQuiz(qtype) => match qtype { - SoraQuizType::Attendee => Ok(data_provider_config.sora_quiz_attendee_id), - SoraQuizType::Master => Ok(data_provider_config.sora_quiz_master_id), + SoraQuizType::Attendee => Ok(data_provider_config.sora_quiz_attendee_id.to_string()), + SoraQuizType::Master => Ok(data_provider_config.sora_quiz_master_id.to_string()), }, } } @@ -90,7 +96,7 @@ mod tests { use crate::{generic_discord_role::build, AccountId, AssertionBuildRequest}; use itp_stf_primitives::types::ShardIdentifier; use lc_credentials::assertion_logic::{AssertionLogic, Op}; - use lc_data_providers::GLOBAL_DATA_PROVIDER_CONFIG; + use lc_data_providers::DataProviderConfig; use lc_mock_server::run; use litentry_primitives::{ Assertion, ContestType, GenericDiscordRoleType, Identity, IdentityNetworkTuple, @@ -99,23 +105,20 @@ mod tests { use log; use std::{vec, vec::Vec}; - fn init() { + fn init() -> DataProviderConfig { let _ = env_logger::builder().is_test(true).try_init(); let url = run(0).unwrap(); - GLOBAL_DATA_PROVIDER_CONFIG.write().unwrap().set_discord_litentry_url(url); - GLOBAL_DATA_PROVIDER_CONFIG - .write() - .unwrap() - .set_contest_legend_discord_role_id("1034083718425493544".to_string()); - GLOBAL_DATA_PROVIDER_CONFIG - .write() - .unwrap() - .set_sora_quiz_attendee_id("1034083718425493544".to_string()); + let mut data_provider_conifg = DataProviderConfig::new(); + + data_provider_conifg.set_discord_litentry_url(url); + data_provider_conifg.set_contest_legend_discord_role_id("1034083718425493544".to_string()); + data_provider_conifg.set_sora_quiz_attendee_id("1034083718425493544".to_string()); + data_provider_conifg } #[test] fn build_contest_role_works() { - init(); + let data_provider_config = init(); let handler_vec: Vec = "againstwar".to_string().as_bytes().to_vec(); @@ -137,7 +140,11 @@ mod tests { req_ext_hash: Default::default(), }; - match build(&req, GenericDiscordRoleType::Contest(ContestType::Legend)) { + match build( + &req, + GenericDiscordRoleType::Contest(ContestType::Legend), + &data_provider_config, + ) { Ok(credential) => { log::info!("build GenericDiscordRole Contest done"); assert_eq!( @@ -158,7 +165,7 @@ mod tests { #[test] fn build_sora_quiz_role_works() { - init(); + let data_provider_config = init(); let handler_vec: Vec = "ericzhang.eth".to_string().as_bytes().to_vec(); @@ -180,7 +187,11 @@ mod tests { req_ext_hash: Default::default(), }; - match build(&req, GenericDiscordRoleType::SoraQuiz(SoraQuizType::Attendee)) { + match build( + &req, + GenericDiscordRoleType::SoraQuiz(SoraQuizType::Attendee), + &data_provider_config, + ) { Ok(credential) => { log::info!("build GenericDiscordRole SoraQuiz done"); assert_eq!(*(credential.credential_subject.values.first().unwrap()), false); diff --git a/tee-worker/litentry/core/assertion-build/src/holding_time.rs b/tee-worker/litentry/core/assertion-build/src/holding_time.rs index 26efeb5eff..5620c2c78c 100644 --- a/tee-worker/litentry/core/assertion-build/src/holding_time.rs +++ b/tee-worker/litentry/core/assertion-build/src/holding_time.rs @@ -24,8 +24,7 @@ use crate::*; use lc_credentials::achainable::amount_holding_time::AchainableAmountHoldingTimeUpdate; use lc_data_providers::{ achainable::{AchainableClient, AchainableHolder, ParamsBasicTypeWithAmountHolding}, - vec_to_string, DataProviderConfigReader, ReadDataProviderConfig, LIT_TOKEN_ADDRESS, - WBTC_TOKEN_ADDRESS, + vec_to_string, DataProviderConfig, LIT_TOKEN_ADDRESS, WBTC_TOKEN_ADDRESS, }; use litentry_primitives::AmountHoldingTimeType; use std::string::ToString; @@ -78,13 +77,15 @@ pub fn build( req: &AssertionBuildRequest, htype: AmountHoldingTimeType, min_balance: ParameterString, + data_provider_config: &DataProviderConfig, ) -> Result { debug!("Assertion A4 build, who: {:?}", account_id_to_string(&req.who)); let q_min_balance = pre_build(&htype, &min_balance)?; let identities = transpose_identity(&req.identities); - let (is_hold, optimal_hold_index) = do_build(identities, &htype, &q_min_balance) - .map_err(|e| emit_error(&htype, &min_balance, e))?; + let (is_hold, optimal_hold_index) = + do_build(identities, &htype, &q_min_balance, data_provider_config) + .map_err(|e| emit_error(&htype, &min_balance, e))?; generate_vc(req, &htype, &q_min_balance, is_hold, optimal_hold_index) .map_err(|e| emit_error(&htype, &min_balance, e)) @@ -107,9 +108,9 @@ fn do_build( identities: Vec<(Web3Network, Vec)>, htype: &AmountHoldingTimeType, q_min_balance: &str, + data_provider_config: &DataProviderConfig, ) -> core::result::Result<(bool, usize), ErrorDetail> { - let data_provider_config = DataProviderConfigReader::read()?; - let mut client = AchainableClient::new(&data_provider_config); + let mut client = AchainableClient::new(data_provider_config); let mut is_hold = false; let mut optimal_hold_index = usize::MAX; @@ -233,19 +234,20 @@ fn match_token_address(htype: &AmountHoldingTimeType, network: &Web3Network) -> #[cfg(test)] mod tests { use super::*; - use lc_data_providers::GLOBAL_DATA_PROVIDER_CONFIG; use lc_mock_server::run; use litentry_primitives::{AmountHoldingTimeType, Web3Network}; - fn init() { + fn init() -> DataProviderConfig { let _ = env_logger::builder().is_test(true).try_init(); let url = run(0).unwrap(); - GLOBAL_DATA_PROVIDER_CONFIG.write().unwrap().set_achainable_url(url); + let mut data_provider_config = DataProviderConfig::new(); + data_provider_config.set_achainable_url(url); + data_provider_config } #[test] fn do_build_lit_works() { - init(); + let data_provider_config = init(); let identities = vec![( Web3Network::Litentry, @@ -255,13 +257,14 @@ mod tests { let htype = AmountHoldingTimeType::LIT; let q_min_balance = "10".to_string(); - let (is_hold, _optimal_hold_index) = do_build(identities, &htype, &q_min_balance).unwrap(); + let (is_hold, _optimal_hold_index) = + do_build(identities, &htype, &q_min_balance, &data_provider_config).unwrap(); assert!(is_hold); } #[test] fn do_build_dot_works() { - init(); + let data_provider_config = init(); let identities = vec![( Web3Network::Polkadot, @@ -271,13 +274,13 @@ mod tests { let q_min_balance = "10".to_string(); let (is_hold, _optimal_hold_index) = - do_build(identities, &dot_type, &q_min_balance).unwrap(); + do_build(identities, &dot_type, &q_min_balance, &data_provider_config).unwrap(); assert!(is_hold); } #[test] fn do_build_wbtc_works() { - init(); + let data_provider_config = init(); let identities = vec![( Web3Network::Ethereum, @@ -289,13 +292,14 @@ mod tests { let htype = AmountHoldingTimeType::WBTC; let q_min_balance = "10".to_string(); - let (is_hold, _optimal_hold_index) = do_build(identities, &htype, &q_min_balance).unwrap(); + let (is_hold, _optimal_hold_index) = + do_build(identities, &htype, &q_min_balance, &data_provider_config).unwrap(); assert!(is_hold); } #[test] fn do_build_non_hold_works() { - init(); + let data_provider_config = init(); let identities = vec![( Web3Network::Ethereum, @@ -304,7 +308,8 @@ mod tests { let htype = AmountHoldingTimeType::LIT; let q_min_balance = "10".to_string(); - let (is_hold, optimal_hold_index) = do_build(identities, &htype, &q_min_balance).unwrap(); + let (is_hold, optimal_hold_index) = + do_build(identities, &htype, &q_min_balance, &data_provider_config).unwrap(); assert!(!is_hold); assert_eq!(optimal_hold_index, 0); } diff --git a/tee-worker/litentry/core/assertion-build/src/nodereal/amount_holding/evm_amount_holding.rs b/tee-worker/litentry/core/assertion-build/src/nodereal/amount_holding/evm_amount_holding.rs index 30759ff7a7..0c05d1d15c 100644 --- a/tee-worker/litentry/core/assertion-build/src/nodereal/amount_holding/evm_amount_holding.rs +++ b/tee-worker/litentry/core/assertion-build/src/nodereal/amount_holding/evm_amount_holding.rs @@ -33,16 +33,17 @@ use lc_data_providers::{ nodereal_jsonrpc::{ FungibleApiList, GetTokenBalance20Param, NoderealChain, NoderealJsonrpcClient, }, - Error as DataProviderError, + DataProviderConfig, Error as DataProviderError, }; use litentry_primitives::EVMTokenType; fn get_holding_balance( token_type: EVMTokenType, addresses: Vec<(Web3Network, String)>, + data_provider_config: &DataProviderConfig, ) -> result::Result { - let mut eth_client = NoderealJsonrpcClient::new(NoderealChain::Eth); - let mut bsc_client = NoderealJsonrpcClient::new(NoderealChain::Bsc); + let mut eth_client = NoderealJsonrpcClient::new(NoderealChain::Eth, data_provider_config); + let mut bsc_client = NoderealJsonrpcClient::new(NoderealChain::Bsc, data_provider_config); let mut total_balance = 0_f64; let decimals = token_type.get_decimals(); @@ -73,7 +74,11 @@ fn get_holding_balance( Ok(total_balance / decimals) } -pub fn build(req: &AssertionBuildRequest, token_type: EVMTokenType) -> Result { +pub fn build( + req: &AssertionBuildRequest, + token_type: EVMTokenType, + data_provider_config: &DataProviderConfig, +) -> Result { debug!("evm amount holding: {:?}", token_type); let identities: Vec<(Web3Network, Vec)> = transpose_identity(&req.identities); @@ -85,14 +90,15 @@ pub fn build(req: &AssertionBuildRequest, token_type: EVMTokenType) -> Result>(); - let result = get_holding_balance(token_type.clone(), addresses).map_err(|e| { - Error::RequestVCFailed( - Assertion::EVMAmountHolding(token_type.clone()), - ErrorDetail::DataProviderError(ErrorString::truncate_from( - format!("{e:?}").as_bytes().to_vec(), - )), - ) - }); + let result = + get_holding_balance(token_type.clone(), addresses, data_provider_config).map_err(|e| { + Error::RequestVCFailed( + Assertion::EVMAmountHolding(token_type.clone()), + ErrorDetail::DataProviderError(ErrorString::truncate_from( + format!("{e:?}").as_bytes().to_vec(), + )), + ) + }); match result { Ok(value) => match Credential::new(&req.who, &req.shard) { @@ -118,7 +124,6 @@ mod tests { use itp_stf_primitives::types::ShardIdentifier; use itp_utils::hex::decode_hex; use lc_credentials::assertion_logic::{AssertionLogic, Op}; - use lc_data_providers::GLOBAL_DATA_PROVIDER_CONFIG; use lc_mock_server::run; fn create_ton_token_assertion_logic() -> Box { @@ -160,22 +165,19 @@ mod tests { }) } - fn init() { + fn init() -> DataProviderConfig { let _ = env_logger::builder().is_test(true).try_init(); let url = run(0).unwrap() + "/nodereal_jsonrpc/"; - GLOBAL_DATA_PROVIDER_CONFIG - .write() - .unwrap() - .set_nodereal_api_key("d416f55179dbd0e45b1a8ed030e3".into()); - GLOBAL_DATA_PROVIDER_CONFIG - .write() - .unwrap() - .set_nodereal_api_chain_network_url(url); + let mut data_provider_config = DataProviderConfig::default(); + + data_provider_config.set_nodereal_api_key("d416f55179dbd0e45b1a8ed030e3".into()); + data_provider_config.set_nodereal_api_chain_network_url(url); + data_provider_config } #[test] fn build_evm_amount_holding_works() { - init(); + let data_provider_config = init(); let identities: Vec = vec![ (Identity::Evm([0; 20].into()), vec![Web3Network::Ethereum]), (Identity::Evm([0; 20].into()), vec![Web3Network::Ethereum, Web3Network::Bsc]), @@ -194,7 +196,7 @@ mod tests { req_ext_hash: Default::default(), }; - match build(&req, EVMTokenType::Ton) { + match build(&req, EVMTokenType::Ton, &data_provider_config) { Ok(credential) => { log::info!("build EVMAmount holding done"); assert_eq!( @@ -226,7 +228,7 @@ mod tests { #[test] fn build_evm_amount_holding_lt_min_works() { - init(); + let data_provider_config = init(); let address = decode_hex("0x85be4e2ccc9c85be8783798b6e8a101bdac6467f".as_bytes().to_vec()) .unwrap() .as_slice() @@ -248,7 +250,7 @@ mod tests { req_ext_hash: Default::default(), }; - match build(&req, EVMTokenType::Ton) { + match build(&req, EVMTokenType::Ton, &data_provider_config) { Ok(credential) => { log::info!("build EVMAmount holding done"); assert_eq!( @@ -280,7 +282,7 @@ mod tests { #[test] fn build_evm_amount_holding_gte_max_works() { - init(); + let data_provider_config = init(); let address = decode_hex("0x90d53026a47ac20609accc3f2ddc9fb9b29bb310".as_bytes().to_vec()) .unwrap() .as_slice() @@ -302,7 +304,7 @@ mod tests { req_ext_hash: Default::default(), }; - match build(&req, EVMTokenType::Ton) { + match build(&req, EVMTokenType::Ton, &data_provider_config) { Ok(credential) => { log::info!("build EVMAmount holding done"); assert_eq!( diff --git a/tee-worker/litentry/core/assertion-build/src/nodereal/bnb_domain/bnb_digit_domain_club_amount.rs b/tee-worker/litentry/core/assertion-build/src/nodereal/bnb_domain/bnb_digit_domain_club_amount.rs index bf3b296029..a0d7854d32 100644 --- a/tee-worker/litentry/core/assertion-build/src/nodereal/bnb_domain/bnb_digit_domain_club_amount.rs +++ b/tee-worker/litentry/core/assertion-build/src/nodereal/bnb_domain/bnb_digit_domain_club_amount.rs @@ -23,11 +23,13 @@ extern crate sgx_tstd as std; use super::{BnbDomainInfo, BnbDomainInfoInterface}; use crate::*; use lc_credentials::nodereal::bnb_domain::bnb_digit_domain_club_amount::UpdateDigitDomainClubAmountCredential; +use lc_data_providers::DataProviderConfig; use litentry_primitives::BnbDigitDomainType; pub fn build( req: &AssertionBuildRequest, digit_domain_type: BnbDigitDomainType, + data_provider_config: &DataProviderConfig, ) -> Result { debug!("building digit_domain credential: {:?}", digit_domain_type); @@ -37,7 +39,11 @@ pub fn build( .flat_map(|(_, addresses)| addresses) .collect::>(); - let amount = BnbDomainInfo.get_bnb_digit_domain_club_amount(&addresses, &digit_domain_type)?; + let amount = BnbDomainInfo.get_bnb_digit_domain_club_amount( + &addresses, + &digit_domain_type, + data_provider_config, + )?; match Credential::new(&req.who, &req.shard) { Ok(mut credential_unsigned) => { credential_unsigned.update_digit_domain_club_amount(&digit_domain_type, amount); diff --git a/tee-worker/litentry/core/assertion-build/src/nodereal/bnb_domain/bnb_domain_holding_amount.rs b/tee-worker/litentry/core/assertion-build/src/nodereal/bnb_domain/bnb_domain_holding_amount.rs index a347770ddb..d765634f0c 100644 --- a/tee-worker/litentry/core/assertion-build/src/nodereal/bnb_domain/bnb_domain_holding_amount.rs +++ b/tee-worker/litentry/core/assertion-build/src/nodereal/bnb_domain/bnb_domain_holding_amount.rs @@ -21,11 +21,15 @@ compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the sam extern crate sgx_tstd as std; use lc_credentials::nodereal::bnb_domain::bnb_domain_holding_amount::UpdateBnbDomainHoldingAmountCredential; +use lc_data_providers::DataProviderConfig; use super::{BnbDomainInfo, BnbDomainInfoInterface}; use crate::*; -pub fn build(req: &AssertionBuildRequest) -> Result { +pub fn build( + req: &AssertionBuildRequest, + data_provider_config: &DataProviderConfig, +) -> Result { debug!("bnb domain holding amount"); let identities = transpose_identity(&req.identities); @@ -34,7 +38,7 @@ pub fn build(req: &AssertionBuildRequest) -> Result { .flat_map(|(_, addresses)| addresses) .collect::>(); - let amount = BnbDomainInfo.get_bnb_domain_holding_amount(&addresses)?; + let amount = BnbDomainInfo.get_bnb_domain_holding_amount(&addresses, data_provider_config)?; match Credential::new(&req.who, &req.shard) { Ok(mut credential_unsigned) => { credential_unsigned.update_bnb_holding_amount(amount); diff --git a/tee-worker/litentry/core/assertion-build/src/nodereal/bnb_domain/mod.rs b/tee-worker/litentry/core/assertion-build/src/nodereal/bnb_domain/mod.rs index fe3f3d4f07..ec47649d60 100644 --- a/tee-worker/litentry/core/assertion-build/src/nodereal/bnb_domain/mod.rs +++ b/tee-worker/litentry/core/assertion-build/src/nodereal/bnb_domain/mod.rs @@ -26,7 +26,7 @@ pub mod bnb_domain_holding_amount; use crate::*; use lc_data_providers::{ nodereal::{BnbDomainApiList, DomainInfo, NoderealClient}, - DataProviderConfigReader, ReadDataProviderConfig, + DataProviderConfig, }; use litentry_primitives::BnbDigitDomainType; use serde::{Deserialize, Serialize}; @@ -37,26 +37,35 @@ impl BnbDomainInfo { fn get_bnb_domain_data_by_owners( &self, owners: &[String], + config: &DataProviderConfig, ) -> core::result::Result { - let config = DataProviderConfigReader::read()?; - let mut client = NoderealClient::new(&config); + let mut client = NoderealClient::new(config); client.by_owners(owners).map_err(|e| e.into_error_detail()) } } pub trait BnbDomainInfoInterface { - fn get_bnb_domain_holding_amount(&self, addresses: &[String]) -> Result; + fn get_bnb_domain_holding_amount( + &self, + addresses: &[String], + config: &DataProviderConfig, + ) -> Result; fn get_bnb_digit_domain_club_amount( &self, owners: &[String], digit_domain_type: &BnbDigitDomainType, + data_provider_config: &DataProviderConfig, ) -> Result; } impl BnbDomainInfoInterface for BnbDomainInfo { - fn get_bnb_domain_holding_amount(&self, owners: &[String]) -> Result { + fn get_bnb_domain_holding_amount( + &self, + owners: &[String], + data_provider_config: &DataProviderConfig, + ) -> Result { let response = self - .get_bnb_domain_data_by_owners(owners) + .get_bnb_domain_data_by_owners(owners, data_provider_config) .map_err(|e| Error::RequestVCFailed(Assertion::BnbDomainHolding, e))?; let owned_domains: Domains = Domains::from_value(&response) @@ -69,10 +78,12 @@ impl BnbDomainInfoInterface for BnbDomainInfo { &self, owners: &[String], digit_domain_type: &BnbDigitDomainType, + data_provider_config: &DataProviderConfig, ) -> Result { - let response = self.get_bnb_domain_data_by_owners(owners).map_err(|e| { - Error::RequestVCFailed(Assertion::BnbDigitDomainClub(digit_domain_type.clone()), e) - })?; + let response = + self.get_bnb_domain_data_by_owners(owners, data_provider_config).map_err(|e| { + Error::RequestVCFailed(Assertion::BnbDigitDomainClub(digit_domain_type.clone()), e) + })?; let owned_domains: Domains = Domains::from_value(&response).map_err(|e| { Error::RequestVCFailed(Assertion::BnbDigitDomainClub(digit_domain_type.clone()), e) diff --git a/tee-worker/litentry/core/assertion-build/src/nodereal/crypto_summary/mod.rs b/tee-worker/litentry/core/assertion-build/src/nodereal/crypto_summary/mod.rs index 240374d32d..c80f4736ea 100644 --- a/tee-worker/litentry/core/assertion-build/src/nodereal/crypto_summary/mod.rs +++ b/tee-worker/litentry/core/assertion-build/src/nodereal/crypto_summary/mod.rs @@ -23,12 +23,16 @@ extern crate sgx_tstd as std; use lc_credentials::nodereal::crypto_summary::{ summary::CryptoSummaryCredentialUpdate, CryptoSummaryClient, }; +use lc_data_providers::DataProviderConfig; use crate::*; -pub fn build(req: &AssertionBuildRequest) -> Result { +pub fn build( + req: &AssertionBuildRequest, + data_provider_config: &DataProviderConfig, +) -> Result { let identities = transpose_identity(&req.identities); - let (txs, summary) = CryptoSummaryClient::new() + let (txs, summary) = CryptoSummaryClient::new(data_provider_config) .logic(&identities) .map_err(|e| Error::RequestVCFailed(Assertion::CryptoSummary, e))?; diff --git a/tee-worker/litentry/core/assertion-build/src/nodereal/nft_holder/weirdo_ghost_gang_holder.rs b/tee-worker/litentry/core/assertion-build/src/nodereal/nft_holder/weirdo_ghost_gang_holder.rs index f4d3b13eb6..a7640315dd 100644 --- a/tee-worker/litentry/core/assertion-build/src/nodereal/nft_holder/weirdo_ghost_gang_holder.rs +++ b/tee-worker/litentry/core/assertion-build/src/nodereal/nft_holder/weirdo_ghost_gang_holder.rs @@ -28,7 +28,7 @@ use lc_data_providers::nodereal_jsonrpc::{ }; use crate::*; -use lc_data_providers::Error as DataProviderError; +use lc_data_providers::{DataProviderConfig, Error as DataProviderError}; const NFT_TOKEN_ADDRESS: &str = "0x9401518f4EBBA857BAA879D9f76E1Cc8b31ed197"; @@ -54,11 +54,14 @@ fn check_has_nft( } } -pub fn build(req: &AssertionBuildRequest) -> Result { +pub fn build( + req: &AssertionBuildRequest, + data_provider_config: &DataProviderConfig, +) -> Result { debug!("WeirdoGhostGang holder"); let mut has_nft = false; - let mut client = NoderealJsonrpcClient::new(NoderealChain::Eth); + let mut client = NoderealJsonrpcClient::new(NoderealChain::Eth, data_provider_config); let identities: Vec<(Web3Network, Vec)> = transpose_identity(&req.identities); let addresses = identities @@ -112,25 +115,21 @@ mod tests { use super::*; use itp_stf_primitives::types::ShardIdentifier; use lc_credentials::assertion_logic::{AssertionLogic, Op}; - use lc_data_providers::GLOBAL_DATA_PROVIDER_CONFIG; use lc_mock_server::run; - fn init() { + fn init() -> DataProviderConfig { let _ = env_logger::builder().is_test(true).try_init(); let url = run(0).unwrap() + "/nodereal_jsonrpc/"; - GLOBAL_DATA_PROVIDER_CONFIG - .write() - .unwrap() - .set_nodereal_api_key("d416f55179dbd0e45b1a8ed030e3".into()); - GLOBAL_DATA_PROVIDER_CONFIG - .write() - .unwrap() - .set_nodereal_api_chain_network_url(url); + let mut config = DataProviderConfig::new(); + + config.set_nodereal_api_key("d416f55179dbd0e45b1a8ed030e3".to_string()); + config.set_nodereal_api_chain_network_url(url); + config } #[test] fn build_weirdo_ghost_gang_holder_works() { - init(); + let config = init(); let identities: Vec = vec![(Identity::Evm([0; 20].into()), vec![Web3Network::Ethereum])]; @@ -147,7 +146,7 @@ mod tests { req_ext_hash: Default::default(), }; - match build(&req) { + match build(&req, &config) { Ok(credential) => { log::info!("build WeirdoGhostGang holder done"); assert_eq!( diff --git a/tee-worker/litentry/core/assertion-build/src/oneblock/course.rs b/tee-worker/litentry/core/assertion-build/src/oneblock/course.rs index ec2210db4a..93e18a7605 100644 --- a/tee-worker/litentry/core/assertion-build/src/oneblock/course.rs +++ b/tee-worker/litentry/core/assertion-build/src/oneblock/course.rs @@ -22,15 +22,20 @@ extern crate sgx_tstd as std; use crate::{oneblock::query_oneblock_status, *}; use lc_credentials::oneblock::OneBlockAssertionUpdate; +use lc_data_providers::DataProviderConfig; -pub fn build(req: &AssertionBuildRequest, course_type: OneBlockCourseType) -> Result { +pub fn build( + req: &AssertionBuildRequest, + course_type: OneBlockCourseType, + data_provider_config: &DataProviderConfig, +) -> Result { let identities = transpose_identity(&req.identities); let addresses = identities .into_iter() .flat_map(|(_, addresses)| addresses) .collect::>(); - let value = query_oneblock_status(&course_type, addresses)?; + let value = query_oneblock_status(&course_type, addresses, data_provider_config)?; match Credential::new(&req.who, &req.shard) { Ok(mut credential_unsigned) => { credential_unsigned.update_notion_assertion(&course_type, value); diff --git a/tee-worker/litentry/core/assertion-build/src/oneblock/mod.rs b/tee-worker/litentry/core/assertion-build/src/oneblock/mod.rs index 06219b5351..7dc640138a 100644 --- a/tee-worker/litentry/core/assertion-build/src/oneblock/mod.rs +++ b/tee-worker/litentry/core/assertion-build/src/oneblock/mod.rs @@ -26,7 +26,7 @@ use crate::*; use http::header::{AUTHORIZATION, CONNECTION}; use http_req::response::Headers; use itc_rest_client::{error::Error as RestClientError, RestGet, RestPath}; -use lc_data_providers::{build_client, DataProviderConfigReader, ReadDataProviderConfig}; +use lc_data_providers::{build_client, DataProviderConfig}; use serde::{Deserialize, Serialize}; use std::string::ToString; @@ -43,11 +43,12 @@ impl RestPath for OneBlockResponse { } } -fn fetch_data_from_notion(course_type: &OneBlockCourseType) -> Result { - let data_provider_config = DataProviderConfigReader::read() - .map_err(|e| Error::RequestVCFailed(Assertion::Oneblock(course_type.clone()), e))?; - let oneblock_notion_key = data_provider_config.oneblock_notion_key; - let oneblock_notion_url = data_provider_config.oneblock_notion_url; +fn fetch_data_from_notion( + course_type: &OneBlockCourseType, + data_provider_config: &DataProviderConfig, +) -> Result { + let oneblock_notion_key = data_provider_config.oneblock_notion_key.to_string(); + let oneblock_notion_url = data_provider_config.oneblock_notion_url.to_string(); let mut headers = Headers::new(); headers.insert(CONNECTION.as_str(), "close"); @@ -174,8 +175,9 @@ impl OneBlockAssertionQualify for OneBlockData { pub fn query_oneblock_status( course_type: &OneBlockCourseType, addresses: Vec, + data_provider_config: &DataProviderConfig, ) -> Result { - let oneblock_response = fetch_data_from_notion(course_type)?; + let oneblock_response = fetch_data_from_notion(course_type, data_provider_config)?; debug!("OneBlock Assertion Response: {oneblock_response:?}"); Ok(check_oneblock_data(&oneblock_response, course_type, addresses)) diff --git a/tee-worker/litentry/core/assertion-build/src/vip3/card.rs b/tee-worker/litentry/core/assertion-build/src/vip3/card.rs index 90bac56961..bd12ec59f0 100644 --- a/tee-worker/litentry/core/assertion-build/src/vip3/card.rs +++ b/tee-worker/litentry/core/assertion-build/src/vip3/card.rs @@ -19,9 +19,14 @@ use crate::{ *, }; use lc_credentials::vip3::UpdateVIP3MembershipCardCredential; +use lc_data_providers::DataProviderConfig; use litentry_primitives::VIP3MembershipCardLevel; -pub fn build(req: &AssertionBuildRequest, level: VIP3MembershipCardLevel) -> Result { +pub fn build( + req: &AssertionBuildRequest, + level: VIP3MembershipCardLevel, + data_provider_config: &DataProviderConfig, +) -> Result { debug!("Building VIP3 membership card level: {:?}", level); let identities = transpose_identity(&req.identities); @@ -30,7 +35,7 @@ pub fn build(req: &AssertionBuildRequest, level: VIP3MembershipCardLevel) -> Res .flat_map(|(_, addresses)| addresses) .collect::>(); - let mut sbt = VIP3SBTInfo::new() + let mut sbt = VIP3SBTInfo::new(data_provider_config) .map_err(|e| Error::RequestVCFailed(Assertion::VIP3MembershipCard(level.clone()), e))?; let value = sbt .has_card_level(addresses, &level) diff --git a/tee-worker/litentry/core/assertion-build/src/vip3/mod.rs b/tee-worker/litentry/core/assertion-build/src/vip3/mod.rs index af72685f44..05e045e985 100644 --- a/tee-worker/litentry/core/assertion-build/src/vip3/mod.rs +++ b/tee-worker/litentry/core/assertion-build/src/vip3/mod.rs @@ -25,7 +25,7 @@ pub mod card; use crate::*; use lc_data_providers::{ vip3::{VIP3Client, VIP3QuerySet}, - DataProviderConfigReader, ReadDataProviderConfig, + DataProviderConfig, }; use litentry_primitives::VIP3MembershipCardLevel; @@ -34,9 +34,10 @@ pub struct VIP3SBTInfo { } impl VIP3SBTInfo { - pub fn new() -> core::result::Result { - let data_provider_config = DataProviderConfigReader::read()?; - let client = VIP3Client::new(&data_provider_config); + pub fn new( + data_provider_config: &DataProviderConfig, + ) -> core::result::Result { + let client = VIP3Client::new(data_provider_config); Ok(VIP3SBTInfo { client }) } diff --git a/tee-worker/litentry/core/credentials/src/nodereal/crypto_summary/mod.rs b/tee-worker/litentry/core/credentials/src/nodereal/crypto_summary/mod.rs index fc6ecf3d9e..42527c2ebd 100644 --- a/tee-worker/litentry/core/credentials/src/nodereal/crypto_summary/mod.rs +++ b/tee-worker/litentry/core/credentials/src/nodereal/crypto_summary/mod.rs @@ -25,6 +25,7 @@ use lc_data_providers::{ FungibleApiList, GetNFTHoldingsParam, NftApiList, NoderealChain, NoderealJsonrpcClient, TransactionCount, }, + DataProviderConfig, }; use litentry_primitives::{ErrorDetail, IntoErrorDetail}; use serde::{Deserialize, Serialize}; @@ -213,16 +214,10 @@ pub struct CryptoSummaryClient { pub bsc_client: NoderealJsonrpcClient, } -impl Default for CryptoSummaryClient { - fn default() -> Self { - Self::new() - } -} - impl CryptoSummaryClient { - pub fn new() -> Self { - let eth_client = NoderealJsonrpcClient::new(NoderealChain::Eth); - let bsc_client = NoderealJsonrpcClient::new(NoderealChain::Bsc); + pub fn new(data_provider_config: &DataProviderConfig) -> Self { + let eth_client = NoderealJsonrpcClient::new(NoderealChain::Eth, data_provider_config); + let bsc_client = NoderealJsonrpcClient::new(NoderealChain::Bsc, data_provider_config); Self { eth_client, bsc_client } } diff --git a/tee-worker/litentry/core/data-providers/Cargo.toml b/tee-worker/litentry/core/data-providers/Cargo.toml index e22440f17e..6eca7cb2b3 100644 --- a/tee-worker/litentry/core/data-providers/Cargo.toml +++ b/tee-worker/litentry/core/data-providers/Cargo.toml @@ -15,7 +15,6 @@ thiserror = { version = "1.0.26", optional = true } url = { version = "2.0.0", optional = true } # no_std dependencies -lazy_static = { version = "1.4.0", features = ["spin_no_std"] } log = { version = "0.4", default-features = false } serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] } serde_json = { version = "1.0", default-features = false, features = ["alloc"] } @@ -23,6 +22,7 @@ serde_json = { version = "1.0", default-features = false, features = ["alloc"] } # internal dependencies itc-rest-client = { path = "../../../core/rest-client", default-features = false } itp-rpc = { path = "../../../core-primitives/rpc", default-features = false } +itp-utils = { path = "../../../core-primitives/utils", default-features = false } # sgx dependencies chrono_sgx = { package = "chrono", git = "https://github.com/mesalock-linux/chrono-sgx", optional = true } @@ -61,6 +61,7 @@ std = [ "url", "itc-rest-client/std", "itp-rpc/std", + "itp-utils/std", "log/std", "serde/std", "serde_json/std", diff --git a/tee-worker/litentry/core/data-providers/src/achainable.rs b/tee-worker/litentry/core/data-providers/src/achainable.rs index db97dbc371..d60be8c4dc 100644 --- a/tee-worker/litentry/core/data-providers/src/achainable.rs +++ b/tee-worker/litentry/core/data-providers/src/achainable.rs @@ -1425,7 +1425,7 @@ mod tests { AchainableAccountTotalTransactions, AchainableClient, AchainableTagAccount, AchainableTagBalance, AchainableTagDeFi, AchainableTagDotsama, AchainableUtils, }, - DataProviderConfigReader, ReadDataProviderConfig, GLOBAL_DATA_PROVIDER_CONFIG, + DataProviderConfig, }; use lc_mock_server::run; use litentry_primitives::Web3Network; @@ -1434,9 +1434,9 @@ mod tests { fn new_achainable_client() -> AchainableClient { let _ = env_logger::builder().is_test(true).try_init(); let url = run(0).unwrap(); - GLOBAL_DATA_PROVIDER_CONFIG.write().unwrap().set_achainable_url(url); - let data_provider_config = DataProviderConfigReader::read().unwrap(); + let mut data_provider_config = DataProviderConfig::new(); + data_provider_config.set_achainable_url(url); AchainableClient::new(&data_provider_config) } diff --git a/tee-worker/litentry/core/data-providers/src/discord_litentry.rs b/tee-worker/litentry/core/data-providers/src/discord_litentry.rs index 7d5bcdeb3d..99ce9d60d6 100644 --- a/tee-worker/litentry/core/data-providers/src/discord_litentry.rs +++ b/tee-worker/litentry/core/data-providers/src/discord_litentry.rs @@ -17,7 +17,7 @@ #[cfg(all(not(feature = "std"), feature = "sgx"))] use crate::sgx_reexport_prelude::*; -use crate::{build_client, vec_to_string, Error, HttpError, GLOBAL_DATA_PROVIDER_CONFIG}; +use crate::{build_client, vec_to_string, Error, HttpError}; use http::header::CONNECTION; use http_req::response::Headers; use itc_rest_client::{ @@ -28,7 +28,6 @@ use itc_rest_client::{ use log::*; use serde::{Deserialize, Serialize}; use std::{ - default::Default, format, string::{String, ToString}, vec, @@ -54,25 +53,11 @@ pub struct DiscordLitentryClient { client: RestClient>, } -impl Default for DiscordLitentryClient { - fn default() -> Self { - Self::new() - } -} - impl DiscordLitentryClient { - pub fn new() -> Self { + pub fn new(url: &str) -> Self { let mut headers = Headers::new(); headers.insert(CONNECTION.as_str(), "close"); - let client = build_client( - GLOBAL_DATA_PROVIDER_CONFIG - .write() - .unwrap() - .discord_litentry_url - .clone() - .as_str(), - headers, - ); + let client = build_client(url, headers); DiscordLitentryClient { client } } @@ -170,32 +155,35 @@ impl DiscordLitentryClient { #[cfg(test)] mod tests { use super::*; + use crate::DataProviderConfig; use lc_mock_server::run; - fn init() { + fn init() -> DataProviderConfig { let _ = env_logger::builder().is_test(true).try_init(); let url = run(0).unwrap(); - GLOBAL_DATA_PROVIDER_CONFIG.write().unwrap().set_discord_litentry_url(url); + let mut data_provider_config = DataProviderConfig::new(); + data_provider_config.set_discord_litentry_url(url); + data_provider_config } #[test] fn check_join_work() { - init(); + let data_provider_config = init(); let guild_id = "919848390156767232".as_bytes().to_vec(); let handler = "againstwar".as_bytes().to_vec(); - let mut client = DiscordLitentryClient::new(); + let mut client = DiscordLitentryClient::new(&data_provider_config.discord_litentry_url); let response = client.check_join(guild_id, handler); assert!(response.is_ok(), "check join discord error: {:?}", response); } #[test] fn check_id_hubber_work() { - init(); + let data_provider_config = init(); let guild_id = "919848390156767232".as_bytes().to_vec(); let channel_id = "919848392035794945".as_bytes().to_vec(); let role_id = "1034083718425493544".as_bytes().to_vec(); let handler = "ericzhang.eth".as_bytes().to_vec(); - let mut client = DiscordLitentryClient::new(); + let mut client = DiscordLitentryClient::new(&data_provider_config.discord_litentry_url); let response = client.check_id_hubber(guild_id, channel_id, role_id, handler); assert!(response.is_ok(), "check discord id hubber error: {:?}", response); } diff --git a/tee-worker/litentry/core/data-providers/src/discord_official.rs b/tee-worker/litentry/core/data-providers/src/discord_official.rs index ffcaba4f03..5ab8a73293 100644 --- a/tee-worker/litentry/core/data-providers/src/discord_official.rs +++ b/tee-worker/litentry/core/data-providers/src/discord_official.rs @@ -118,25 +118,25 @@ impl DiscordOfficialClient { #[cfg(test)] mod tests { - use crate::{DataProviderConfigReader, ReadDataProviderConfig, GLOBAL_DATA_PROVIDER_CONFIG}; use super::*; use lc_mock_server::run; - fn init() { + fn init() -> DataProviderConfig { let _ = env_logger::builder().is_test(true).try_init(); let url = run(0).unwrap(); - GLOBAL_DATA_PROVIDER_CONFIG.write().unwrap().set_discord_official_url(url); + let mut data_provider_config = DataProviderConfig::new(); + data_provider_config.set_discord_official_url(url); + data_provider_config } #[test] fn query_message_work() { - init(); + let data_provider_config = init(); let channel_id = "919848392035794945"; let message_id = "1"; - let data_provider_config = DataProviderConfigReader::read().unwrap(); let mut client = DiscordOfficialClient::new(&data_provider_config); let result = client.query_message(channel_id.as_bytes().to_vec(), message_id.as_bytes().to_vec()); diff --git a/tee-worker/litentry/core/data-providers/src/geniidata.rs b/tee-worker/litentry/core/data-providers/src/geniidata.rs index 8857fb8d0d..a7e7861c9c 100644 --- a/tee-worker/litentry/core/data-providers/src/geniidata.rs +++ b/tee-worker/litentry/core/data-providers/src/geniidata.rs @@ -20,10 +20,7 @@ use crate::sgx_reexport_prelude::*; #[cfg(all(not(feature = "std"), feature = "sgx"))] extern crate sgx_tstd as std; -use crate::{ - build_client_with_cert, DataProviderConfigReader, Error as DataProviderError, - ReadDataProviderConfig, -}; +use crate::{build_client_with_cert, DataProviderConfig, Error as DataProviderError}; use http::header::{ACCEPT, CONNECTION}; use http_req::response::Headers; use itc_rest_client::{ @@ -79,9 +76,9 @@ pub struct GeniidataClient { } impl GeniidataClient { - pub fn new() -> core::result::Result { - let data_provider_config = DataProviderConfigReader::read()?; - + pub fn new( + data_provider_config: &DataProviderConfig, + ) -> core::result::Result { let mut headers = Headers::new(); headers.insert(CONNECTION.as_str(), "close"); headers.insert(ACCEPT.as_str(), "application/json"); diff --git a/tee-worker/litentry/core/data-providers/src/lib.rs b/tee-worker/litentry/core/data-providers/src/lib.rs index 30216ff696..030f4c66df 100644 --- a/tee-worker/litentry/core/data-providers/src/lib.rs +++ b/tee-worker/litentry/core/data-providers/src/lib.rs @@ -44,23 +44,18 @@ use itc_rest_client::{ http_client::{DefaultSend, HttpClient}, rest_client::RestClient, }; -use lazy_static::lazy_static; +use itp_utils::if_not_production; use log::debug; use serde::{Deserialize, Serialize}; use std::vec; -#[cfg(feature = "std")] -use std::sync::RwLock; -#[cfg(feature = "sgx")] -use std::sync::SgxRwLock as RwLock; - use itc_rest_client::http_client::SendWithCertificateVerification; use litentry_primitives::{ AchainableParams, Assertion, ErrorDetail, ErrorString, IntoErrorDetail, ParameterString, VCMPError, }; use std::{ - format, + env, format, string::{String, ToString}, vec::Vec, }; @@ -168,7 +163,7 @@ impl TokenFromString for ETokenAddress { } } -#[derive(PartialEq, Eq, Clone, Encode, Decode, Serialize, Deserialize)] +#[derive(PartialEq, Eq, Clone, Encode, Decode, Serialize, Deserialize, Debug)] pub struct DataProviderConfig { pub twitter_official_url: String, pub twitter_litentry_url: String, @@ -204,32 +199,115 @@ impl Default for DataProviderConfig { impl DataProviderConfig { pub fn new() -> Self { - DataProviderConfig { + std::println!("Initializing data providers config"); + + // default prod config + let mut config = DataProviderConfig { twitter_official_url: "https://api.twitter.com".to_string(), - twitter_litentry_url: "".to_string(), - twitter_auth_token_v2: "Bearer ".to_string(), + twitter_litentry_url: "http://127.0.0.1:9527”".to_string(), + twitter_auth_token_v2: "".to_string(), discord_official_url: "https://discordapp.com".to_string(), - discord_litentry_url: "".to_string(), + discord_litentry_url: "http://127.0.0.1:9527”".to_string(), discord_auth_token: "".to_string(), - achainable_url: "https://graph.tdf-labs.io/".to_string(), + achainable_url: "https://label-production.graph.tdf-labs.io/".to_string(), achainable_auth_key: "".to_string(), - credential_endpoint: "".to_string(), + credential_endpoint: "wss://tee-staging.litentry.io".to_string(), oneblock_notion_key: "".to_string(), - oneblock_notion_url: "".to_string(), - sora_quiz_master_id: "".to_string(), - sora_quiz_attendee_id: "".to_string(), - nodereal_api_key: "".to_string(), + oneblock_notion_url: + "https://api.notion.com/v1/blocks/e4068e6a326243468f35dcdc0c43f686/children" + .to_string(), + sora_quiz_master_id: "1164463721989554218".to_string(), + sora_quiz_attendee_id: "1166941149219532800".to_string(), + nodereal_api_key: "https://{chain}-{network}.nodereal.io/".to_string(), nodereal_api_retry_delay: 5000, nodereal_api_retry_times: 2, - nodereal_api_url: "".to_string(), + nodereal_api_url: "https://open-platform.nodereal.io/".to_string(), nodereal_api_chain_network_url: "".to_string(), - contest_legend_discord_role_id: "".to_string(), - contest_popularity_discord_role_id: "".to_string(), - contest_participant_discord_role_id: "".to_string(), - vip3_url: "".to_string(), - geniidata_url: "".to_string(), + contest_legend_discord_role_id: "1172576273063739462".to_string(), + contest_popularity_discord_role_id: "1172576681119195208".to_string(), + contest_participant_discord_role_id: "1172576734135210104".to_string(), + vip3_url: "https://dappapi.vip3.io/".to_string(), + geniidata_url: "https://api.geniidata.com/api/1/brc20/balance?".to_string(), geniidata_api_key: "".to_string(), + }; + + // we allow to override following config properties for non prod dev + if_not_production!({ + if let Ok(v) = env::var("TWITTER_OFFICIAL_URL") { + config.set_twitter_official_url(v); + } + if let Ok(v) = env::var("TWITTER_LITENTRY_URL") { + config.set_twitter_litentry_url(v); + } + if let Ok(v) = env::var("DISCORD_OFFICIAL_URL") { + config.set_discord_official_url(v); + } + if let Ok(v) = env::var("DISCORD_LITENTRY_URL") { + config.set_discord_litentry_url(v); + } + if let Ok(v) = env::var("ACHAINABLE_URL") { + config.set_achainable_url(v); + } + if let Ok(v) = env::var("CREDENTIAL_ENDPOINT") { + config.set_credential_endpoint(v); + } + if let Ok(v) = env::var("ONEBLOCK_NOTION_URL") { + config.set_oneblock_notion_url(v); + } + if let Ok(v) = env::var("SORA_QUIZ_MASTER_ID") { + config.set_sora_quiz_master_id(v); + } + if let Ok(v) = env::var("SORA_QUIZ_ATTENDEE_ID") { + config.set_sora_quiz_attendee_id(v); + } + if let Ok(v) = env::var("NODEREAL_API_URL") { + config.set_nodereal_api_url(v); + } + if let Ok(v) = env::var("NODEREAL_API_RETRY_DELAY") { + config.set_nodereal_api_retry_delay(v.parse::().unwrap()); + } + if let Ok(v) = env::var("NODEREAL_API_RETRY_TIME") { + config.set_nodereal_api_retry_times(v.parse::().unwrap()); + } + if let Ok(v) = env::var("NODEREAL_API_CHAIN_NETWORK_URL") { + config.set_nodereal_api_chain_network_url(v); + } + if let Ok(v) = env::var("CONTEST_LEGEND_DISCORD_ROLE_ID") { + config.set_contest_legend_discord_role_id(v); + } + if let Ok(v) = env::var("CONTEST_POPULARITY_DISCORD_ROLE_ID") { + config.set_contest_popularity_discord_role_id(v); + } + if let Ok(v) = env::var("CONTEST_PARTICIPANT_DISCORD_ROLE_ID") { + config.set_contest_participant_discord_role_id(v); + } + if let Ok(v) = env::var("VIP3_URL") { + config.set_vip3_url(v); + } + if let Ok(v) = env::var("GENIIDATA_URL") { + config.set_geniidata_url(v); + } + }); + // set secrets from env variables + if let Ok(v) = env::var("TWITTER_AUTH_TOKEN_V2") { + config.set_twitter_auth_token_v2(v); + } + if let Ok(v) = env::var("DISCORD_AUTH_TOKEN") { + config.set_discord_auth_token(v); + } + if let Ok(v) = env::var("ACHAINABLE_AUTH_KEY") { + config.set_achainable_auth_key(v); } + if let Ok(v) = env::var("ONEBLOCK_NOTION_KEY") { + config.set_oneblock_notion_key(v); + } + if let Ok(v) = env::var("NODEREAL_API_KEY") { + config.set_nodereal_api_key(v); + } + if let Ok(v) = env::var("GENIIDATA_API_KEY") { + config.set_geniidata_api_key(v); + } + config } pub fn set_twitter_official_url(&mut self, v: String) { debug!("set_twitter_official_url: {:?}", v); @@ -329,27 +407,6 @@ impl DataProviderConfig { } } -lazy_static! { - pub static ref GLOBAL_DATA_PROVIDER_CONFIG: RwLock = - RwLock::new(DataProviderConfig::new()); -} - -pub struct DataProviderConfigReader; -pub trait ReadDataProviderConfig { - fn read() -> Result; -} - -impl ReadDataProviderConfig for DataProviderConfigReader { - fn read() -> Result { - match GLOBAL_DATA_PROVIDER_CONFIG.read() { - Ok(c) => Ok(c.clone()), - Err(e) => Err(ErrorDetail::DataProviderError(ErrorString::truncate_from( - format!("{e:?}").as_bytes().to_vec(), - ))), - } - } -} - #[derive(Debug, thiserror::Error, Clone)] pub enum Error { #[error("Request error: {0}")] diff --git a/tee-worker/litentry/core/data-providers/src/nodereal_jsonrpc.rs b/tee-worker/litentry/core/data-providers/src/nodereal_jsonrpc.rs index a028429bc5..7dd24cbc36 100644 --- a/tee-worker/litentry/core/data-providers/src/nodereal_jsonrpc.rs +++ b/tee-worker/litentry/core/data-providers/src/nodereal_jsonrpc.rs @@ -17,7 +17,7 @@ #[cfg(all(not(feature = "std"), feature = "sgx"))] use crate::sgx_reexport_prelude::*; -use crate::{build_client, hex_to_decimal, Error, HttpError, GLOBAL_DATA_PROVIDER_CONFIG}; +use crate::{build_client, hex_to_decimal, DataProviderConfig, Error, HttpError}; use http::header::CONNECTION; use http_req::response::Headers; use itc_rest_client::{ @@ -100,15 +100,11 @@ pub struct NoderealJsonrpcClient { } impl NoderealJsonrpcClient { - pub fn new(chain: NoderealChain) -> Self { - let api_key = GLOBAL_DATA_PROVIDER_CONFIG.write().unwrap().nodereal_api_key.clone(); - let api_retry_delay = GLOBAL_DATA_PROVIDER_CONFIG.write().unwrap().nodereal_api_retry_delay; - let api_retry_times = GLOBAL_DATA_PROVIDER_CONFIG.write().unwrap().nodereal_api_retry_times; - let api_url = GLOBAL_DATA_PROVIDER_CONFIG - .write() - .unwrap() - .nodereal_api_chain_network_url - .clone(); + pub fn new(chain: NoderealChain, data_provider_config: &DataProviderConfig) -> Self { + let api_key = data_provider_config.nodereal_api_key.clone(); + let api_retry_delay = data_provider_config.nodereal_api_retry_delay; + let api_retry_times = data_provider_config.nodereal_api_retry_times; + let api_url = data_provider_config.nodereal_api_chain_network_url.clone(); let base_url = api_url.replace("{chain}", chain.to_string()); let mut headers = Headers::new(); @@ -415,23 +411,20 @@ mod tests { use super::*; use lc_mock_server::run; - fn init() { + fn init() -> DataProviderConfig { let _ = env_logger::builder().is_test(true).try_init(); let url = run(0).unwrap() + "/nodereal_jsonrpc/"; - GLOBAL_DATA_PROVIDER_CONFIG - .write() - .unwrap() - .set_nodereal_api_key("d416f55179dbd0e45b1a8ed030e3".into()); - GLOBAL_DATA_PROVIDER_CONFIG - .write() - .unwrap() - .set_nodereal_api_chain_network_url(url); + + let mut config = DataProviderConfig::new(); + config.set_nodereal_api_key("d416f55179dbd0e45b1a8ed030e3".to_string()); + config.set_nodereal_api_chain_network_url(url); + config } #[test] fn does_get_nft_holdings_works() { - init(); - let mut client = NoderealJsonrpcClient::new(NoderealChain::Eth); + let config = init(); + let mut client = NoderealJsonrpcClient::new(NoderealChain::Eth, &config); let param = GetNFTHoldingsParam { account_address: "0x49AD262C49C7aA708Cc2DF262eD53B64A17Dd5EE".into(), token_type: "ERC721".into(), @@ -449,8 +442,8 @@ mod tests { #[test] fn does_get_token_balance_721_works() { - init(); - let mut client = NoderealJsonrpcClient::new(NoderealChain::Eth); + let config = init(); + let mut client = NoderealJsonrpcClient::new(NoderealChain::Eth, &config); let param = GetTokenBalance721Param { token_address: "0x07D971C03553011a48E951a53F48632D37652Ba1".into(), account_address: "0x49AD262C49C7aA708Cc2DF262eD53B64A17Dd5EE".into(), @@ -462,8 +455,8 @@ mod tests { #[test] fn does_get_token_balance_20_works() { - init(); - let mut client = NoderealJsonrpcClient::new(NoderealChain::Eth); + let config = init(); + let mut client = NoderealJsonrpcClient::new(NoderealChain::Eth, &config); let param = GetTokenBalance20Param { contract_address: "0x76A797A59Ba2C17726896976B7B3747BfD1d220f".into(), address: "0x85Be4e2ccc9c85BE8783798B6e8A101BDaC6467F".into(), diff --git a/tee-worker/litentry/core/data-providers/src/twitter_official.rs b/tee-worker/litentry/core/data-providers/src/twitter_official.rs index 5936e1316b..5039b2194b 100644 --- a/tee-worker/litentry/core/data-providers/src/twitter_official.rs +++ b/tee-worker/litentry/core/data-providers/src/twitter_official.rs @@ -17,9 +17,7 @@ #[cfg(all(not(feature = "std"), feature = "sgx"))] use crate::sgx_reexport_prelude::*; -use crate::{ - build_client_with_cert, vec_to_string, Error, HttpError, UserInfo, GLOBAL_DATA_PROVIDER_CONFIG, -}; +use crate::{build_client_with_cert, vec_to_string, Error, HttpError, UserInfo}; use http::header::{AUTHORIZATION, CONNECTION}; use http_req::response::Headers; use itc_rest_client::{ @@ -157,27 +155,11 @@ impl TargetUser { /// rate limit: https://developer.twitter.com/en/docs/twitter-api/rate-limits impl TwitterOfficialClient { - pub fn v2() -> Self { + pub fn v2(url: &str, token: &str) -> Self { let mut headers = Headers::new(); headers.insert(CONNECTION.as_str(), "close"); - headers.insert( - AUTHORIZATION.as_str(), - GLOBAL_DATA_PROVIDER_CONFIG - .read() - .unwrap() - .twitter_auth_token_v2 - .clone() - .as_str(), - ); - let client = build_client_with_cert( - GLOBAL_DATA_PROVIDER_CONFIG - .read() - .unwrap() - .twitter_official_url - .clone() - .as_str(), - headers.clone(), - ); + headers.insert(AUTHORIZATION.as_str(), token); + let client = build_client_with_cert(url, headers.clone()); TwitterOfficialClient { client } } @@ -267,20 +249,24 @@ impl TwitterOfficialClient { #[cfg(test)] mod tests { use super::*; + use crate::DataProviderConfig; use lc_mock_server::run; - fn init() { + fn init() -> DataProviderConfig { let _ = env_logger::builder().is_test(true).try_init(); let url = run(0).unwrap(); - GLOBAL_DATA_PROVIDER_CONFIG.write().unwrap().set_twitter_official_url(url); + let mut data_provider_config = DataProviderConfig::new(); + data_provider_config.set_twitter_official_url(url); + data_provider_config } #[test] fn query_tweet_work() { - init(); + let data_provider_config = init(); let tweet_id = "100"; - let mut client = TwitterOfficialClient::v2(); + let mut client = + TwitterOfficialClient::v2(&data_provider_config.twitter_official_url, "token"); let result = client.query_tweet(tweet_id.as_bytes().to_vec()); assert!(result.is_ok(), "error: {:?}", result); let tweet = result.unwrap(); @@ -292,9 +278,10 @@ mod tests { #[test] fn query_retweeted_work() { - init(); + let data_provider_config = init(); - let mut client = TwitterOfficialClient::v2(); + let mut client = + TwitterOfficialClient::v2(&data_provider_config.twitter_official_url, "token"); let original_tweet_id = "100".as_bytes().to_vec(); let response = client.query_retweeted_by(original_tweet_id); @@ -303,20 +290,22 @@ mod tests { #[test] fn query_user_by_name_work() { - init(); + let data_provider_config = init(); let user = "twitterdev"; - let mut client = TwitterOfficialClient::v2(); + let mut client = + TwitterOfficialClient::v2(&data_provider_config.twitter_official_url, "token"); let result = client.query_user_by_name(user.as_bytes().to_vec()); assert!(result.is_ok(), "error: {:?}", result); } #[test] fn query_user_by_id_work() { - init(); + let data_provider_config = init(); let user_id = "2244994945"; - let mut client = TwitterOfficialClient::v2(); + let mut client = + TwitterOfficialClient::v2(&data_provider_config.twitter_official_url, "token"); let result = client.query_user_by_id(user_id.as_bytes().to_vec()); assert!(result.is_ok(), "error: {:?}", result); } diff --git a/tee-worker/litentry/core/identity-verification/src/lib.rs b/tee-worker/litentry/core/identity-verification/src/lib.rs index d8749a6ed5..03692ebb67 100644 --- a/tee-worker/litentry/core/identity-verification/src/lib.rs +++ b/tee-worker/litentry/core/identity-verification/src/lib.rs @@ -40,7 +40,8 @@ mod web2; mod error; use error::{Error, Result}; +use lc_data_providers::DataProviderConfig; -pub fn verify(r: &Web2IdentityVerificationRequest) -> Result<()> { - web2::verify(&r.who, &r.identity, &r.raw_msg, &r.validation_data) +pub fn verify(r: &Web2IdentityVerificationRequest, config: &DataProviderConfig) -> Result<()> { + web2::verify(&r.who, &r.identity, &r.raw_msg, &r.validation_data, config) } diff --git a/tee-worker/litentry/core/identity-verification/src/web2/mod.rs b/tee-worker/litentry/core/identity-verification/src/web2/mod.rs index 540e6c5d1c..fa0faf933b 100644 --- a/tee-worker/litentry/core/identity-verification/src/web2/mod.rs +++ b/tee-worker/litentry/core/identity-verification/src/web2/mod.rs @@ -28,7 +28,7 @@ use itp_sgx_crypto::ShieldingCryptoDecrypt; use lc_data_providers::{ discord_official::{DiscordMessage, DiscordOfficialClient}, twitter_official::{Tweet, TwitterOfficialClient}, - DataProviderConfigReader, ReadDataProviderConfig, UserInfo, + DataProviderConfig, UserInfo, }; use litentry_primitives::{ DiscordValidationData, ErrorDetail, Identity, IntoErrorDetail, TwitterValidationData, @@ -57,12 +57,16 @@ pub fn verify( identity: &Identity, raw_msg: &[u8], data: &Web2ValidationData, + config: &DataProviderConfig, ) -> Result<()> { debug!("verify web2 identity, who: {:?}", who); let (user_name, payload) = match data { Web2ValidationData::Twitter(TwitterValidationData { ref tweet_id }) => { - let mut client = TwitterOfficialClient::v2(); + let mut client = TwitterOfficialClient::v2( + config.twitter_official_url.as_str(), + config.twitter_auth_token_v2.as_str(), + ); let tweet: Tweet = client .query_tweet(tweet_id.to_vec()) .map_err(|e| Error::LinkIdentityFailed(e.into_error_detail()))?; @@ -84,10 +88,7 @@ pub fn verify( ref message_id, .. }) => { - let data_provider_config = - DataProviderConfigReader::read().map_err(Error::UnclassifiedError)?; - - let mut client = DiscordOfficialClient::new(&data_provider_config); + let mut client = DiscordOfficialClient::new(config); let message: DiscordMessage = client .query_message(channel_id.to_vec(), message_id.to_vec()) .map_err(|e| Error::LinkIdentityFailed(e.into_error_detail()))?; diff --git a/tee-worker/litentry/core/stf-task/receiver/src/handler/assertion.rs b/tee-worker/litentry/core/stf-task/receiver/src/handler/assertion.rs index 58a69f5697..a41eae52e0 100644 --- a/tee-worker/litentry/core/stf-task/receiver/src/handler/assertion.rs +++ b/tee-worker/litentry/core/stf-task/receiver/src/handler/assertion.rs @@ -19,35 +19,40 @@ use crate::{handler::TaskHandler, EnclaveOnChainOCallApi, StfTaskContext, TrustedCall, H256}; use ita_sgx_runtime::Hash; use ita_stf::{Getter, TrustedCallSigned}; -use itp_sgx_crypto::{ShieldingCryptoDecrypt, ShieldingCryptoEncrypt}; +use itp_sgx_crypto::{key_repository::AccessKey, ShieldingCryptoEncrypt}; use itp_sgx_externalities::SgxExternalitiesTrait; use itp_stf_executor::traits::StfEnclaveSigning; use itp_stf_state_handler::handle_state::HandleState; use itp_top_pool_author::traits::AuthorApi; use itp_types::ShardIdentifier; -use lc_data_providers::{DataProviderConfigReader, ReadDataProviderConfig}; +use lc_data_providers::DataProviderConfig; use lc_stf_task_sender::AssertionBuildRequest; use litentry_primitives::{ AmountHoldingTimeType, Assertion, ErrorDetail, ErrorString, Identity, ParameterString, VCMPError, }; use log::*; -use std::{format, sync::Arc, vec::Vec}; +use std::{format, string::ToString, sync::Arc, vec::Vec}; pub(crate) struct AssertionHandler< - K: ShieldingCryptoDecrypt + ShieldingCryptoEncrypt + Clone, + ShieldingKeyRepository, A: AuthorApi, S: StfEnclaveSigning, H: HandleState, O: EnclaveOnChainOCallApi, -> { +> where + ShieldingKeyRepository: AccessKey, + ::KeyType: ShieldingCryptoEncrypt + 'static, +{ pub(crate) req: AssertionBuildRequest, - pub(crate) context: Arc>, + pub(crate) context: Arc>, } -impl TaskHandler for AssertionHandler +impl TaskHandler + for AssertionHandler where - K: ShieldingCryptoDecrypt + ShieldingCryptoEncrypt + Clone, + ShieldingKeyRepository: AccessKey, + ::KeyType: ShieldingCryptoEncrypt + 'static, A: AuthorApi, S: StfEnclaveSigning, H: HandleState, @@ -68,72 +73,125 @@ where } lc_assertion_build::a1::build(&self.req) }, - Assertion::A2(guild_id) => lc_assertion_build::a2::build(&self.req, guild_id), - - Assertion::A3(guild_id, channel_id, role_id) => - lc_assertion_build::a3::build(&self.req, guild_id, channel_id, role_id), - - Assertion::A4(min_balance) => - build_holding_time(&self.req, AmountHoldingTimeType::LIT, min_balance), - - Assertion::A6 => lc_assertion_build::a6::build(&self.req), - - Assertion::A7(min_balance) => - build_holding_time(&self.req, AmountHoldingTimeType::DOT, min_balance), + Assertion::A2(guild_id) => lc_assertion_build::a2::build( + &self.req, + guild_id, + &self.context.data_provider_config, + ), + + Assertion::A3(guild_id, channel_id, role_id) => lc_assertion_build::a3::build( + &self.req, + guild_id, + channel_id, + role_id, + &self.context.data_provider_config, + ), + + Assertion::A4(min_balance) => build_holding_time( + &self.req, + AmountHoldingTimeType::LIT, + min_balance, + &self.context.data_provider_config, + ), + + Assertion::A6 => + lc_assertion_build::a6::build(&self.req, &self.context.data_provider_config), + + Assertion::A7(min_balance) => build_holding_time( + &self.req, + AmountHoldingTimeType::DOT, + min_balance, + &self.context.data_provider_config, + ), // no need to pass `networks` again because it's the same as the `get_supported_web3networks` - Assertion::A8(_networks) => lc_assertion_build::a8::build(&self.req), - - Assertion::A10(min_balance) => - build_holding_time(&self.req, AmountHoldingTimeType::WBTC, min_balance), - - Assertion::A11(min_balance) => - build_holding_time(&self.req, AmountHoldingTimeType::ETH, min_balance), + Assertion::A8(_networks) => + lc_assertion_build::a8::build(&self.req, &self.context.data_provider_config), + + Assertion::A10(min_balance) => build_holding_time( + &self.req, + AmountHoldingTimeType::WBTC, + min_balance, + &self.context.data_provider_config, + ), + + Assertion::A11(min_balance) => build_holding_time( + &self.req, + AmountHoldingTimeType::ETH, + min_balance, + &self.context.data_provider_config, + ), Assertion::A13(owner) => lc_assertion_build::a13::build(&self.req, self.context.ocall_api.clone(), &owner), - Assertion::A14 => lc_assertion_build::a14::build(&self.req), + Assertion::A14 => + lc_assertion_build::a14::build(&self.req, &self.context.data_provider_config), - Assertion::Achainable(param) => lc_assertion_build::achainable::build(&self.req, param), + Assertion::Achainable(param) => lc_assertion_build::achainable::build( + &self.req, + param, + &self.context.data_provider_config, + ), Assertion::A20 => lc_assertion_build::a20::build(&self.req), - Assertion::Oneblock(course_type) => - lc_assertion_build::oneblock::course::build(&self.req, course_type), + Assertion::Oneblock(course_type) => lc_assertion_build::oneblock::course::build( + &self.req, + course_type, + &self.context.data_provider_config, + ), Assertion::GenericDiscordRole(role_type) => - lc_assertion_build::generic_discord_role::build(&self.req, role_type), + lc_assertion_build::generic_discord_role::build( + &self.req, + role_type, + &self.context.data_provider_config, + ), Assertion::BnbDomainHolding => lc_assertion_build::nodereal::bnb_domain::bnb_domain_holding_amount::build( &self.req, + &self.context.data_provider_config, ), Assertion::BnbDigitDomainClub(digit_domain_type) => lc_assertion_build::nodereal::bnb_domain::bnb_digit_domain_club_amount::build( &self.req, digit_domain_type, + &self.context.data_provider_config, ), - Assertion::VIP3MembershipCard(level) => - lc_assertion_build::vip3::card::build(&self.req, level), + Assertion::VIP3MembershipCard(level) => lc_assertion_build::vip3::card::build( + &self.req, + level, + &self.context.data_provider_config, + ), Assertion::WeirdoGhostGangHolder => - lc_assertion_build::nodereal::nft_holder::weirdo_ghost_gang_holder::build(&self.req), + lc_assertion_build::nodereal::nft_holder::weirdo_ghost_gang_holder::build( + &self.req, + &self.context.data_provider_config, + ), Assertion::LITStaking => lc_assertion_build::lit_staking::build(&self.req), Assertion::EVMAmountHolding(token_type) => lc_assertion_build::nodereal::amount_holding::evm_amount_holding::build( - &self.req, token_type, + &self.req, + token_type, + &self.context.data_provider_config, ), - Assertion::BRC20AmountHolder => - lc_assertion_build::brc20::amount_holder::build(&self.req), + Assertion::BRC20AmountHolder => lc_assertion_build::brc20::amount_holder::build( + &self.req, + &self.context.data_provider_config, + ), - Assertion::CryptoSummary => - lc_assertion_build::nodereal::crypto_summary::build(&self.req), + Assertion::CryptoSummary => lc_assertion_build::nodereal::crypto_summary::build( + &self.req, + &self.context.data_provider_config, + ), }?; // post-process the credential @@ -148,9 +206,8 @@ where credential.parachain_block_number = self.req.parachain_block_number; credential.sidechain_block_number = self.req.sidechain_block_number; - let data_provider_config = DataProviderConfigReader::read() - .map_err(|e| VCMPError::RequestVCFailed(self.req.assertion.clone(), e))?; - credential.credential_subject.endpoint = data_provider_config.credential_endpoint; + credential.credential_subject.endpoint = + self.context.data_provider_config.credential_endpoint.to_string(); credential.credential_subject.assertion_text = format!("{:?}", self.req.assertion); @@ -241,6 +298,7 @@ fn build_holding_time( req: &AssertionBuildRequest, htype: AmountHoldingTimeType, min_balance: ParameterString, + data_provider_config: &DataProviderConfig, ) -> Result { - lc_assertion_build::holding_time::build(req, htype, min_balance) + lc_assertion_build::holding_time::build(req, htype, min_balance, data_provider_config) } diff --git a/tee-worker/litentry/core/stf-task/receiver/src/handler/identity_verification.rs b/tee-worker/litentry/core/stf-task/receiver/src/handler/identity_verification.rs index 033cf30b09..ecfd9fe3e8 100644 --- a/tee-worker/litentry/core/stf-task/receiver/src/handler/identity_verification.rs +++ b/tee-worker/litentry/core/stf-task/receiver/src/handler/identity_verification.rs @@ -20,7 +20,7 @@ use crate::{ }; use ita_sgx_runtime::Hash; use ita_stf::H256; -use itp_sgx_crypto::{ShieldingCryptoDecrypt, ShieldingCryptoEncrypt}; +use itp_sgx_crypto::{key_repository::AccessKey, ShieldingCryptoEncrypt}; use itp_sgx_externalities::SgxExternalitiesTrait; use itp_stf_executor::traits::StfEnclaveSigning; use itp_stf_state_handler::handle_state::HandleState; @@ -32,19 +32,24 @@ use log::*; use std::sync::Arc; pub(crate) struct IdentityVerificationHandler< - K: ShieldingCryptoDecrypt + ShieldingCryptoEncrypt + Clone, + ShieldingKeyRepository, A: AuthorApi, S: StfEnclaveSigning, H: HandleState, O: EnclaveOnChainOCallApi, -> { +> where + ShieldingKeyRepository: AccessKey, + ::KeyType: ShieldingCryptoEncrypt + 'static, +{ pub(crate) req: Web2IdentityVerificationRequest, - pub(crate) context: Arc>, + pub(crate) context: Arc>, } -impl TaskHandler for IdentityVerificationHandler +impl TaskHandler + for IdentityVerificationHandler where - K: ShieldingCryptoDecrypt + ShieldingCryptoEncrypt + Clone, + ShieldingKeyRepository: AccessKey, + ::KeyType: ShieldingCryptoEncrypt + 'static, A: AuthorApi, S: StfEnclaveSigning, H: HandleState, @@ -55,7 +60,7 @@ where type Result = (); fn on_process(&self) -> Result { - lc_identity_verification::verify(&self.req) + lc_identity_verification::verify(&self.req, &self.context.data_provider_config) } fn on_success( diff --git a/tee-worker/litentry/core/stf-task/receiver/src/lib.rs b/tee-worker/litentry/core/stf-task/receiver/src/lib.rs index 44e0f77de2..b83033ea38 100644 --- a/tee-worker/litentry/core/stf-task/receiver/src/lib.rs +++ b/tee-worker/litentry/core/stf-task/receiver/src/lib.rs @@ -47,12 +47,13 @@ use ita_sgx_runtime::Hash; use ita_stf::{Getter, TrustedCall, TrustedCallSigned, TrustedOperation}; use itp_enclave_metrics::EnclaveMetric; use itp_ocall_api::{EnclaveMetricsOCallApi, EnclaveOnChainOCallApi}; -use itp_sgx_crypto::{ShieldingCryptoDecrypt, ShieldingCryptoEncrypt}; +use itp_sgx_crypto::{key_repository::AccessKey, ShieldingCryptoEncrypt}; use itp_sgx_externalities::SgxExternalitiesTrait; use itp_stf_executor::traits::StfEnclaveSigning; use itp_stf_state_handler::handle_state::HandleState; use itp_top_pool_author::traits::AuthorApi; use itp_types::{RsaRequest, ShardIdentifier, H256}; +use lc_data_providers::DataProviderConfig; use lc_stf_task_sender::{stf_task_sender, RequestType}; use log::{debug, error, info}; use std::{ @@ -83,37 +84,51 @@ pub enum Error { #[allow(dead_code)] pub struct StfTaskContext< - K: ShieldingCryptoDecrypt + ShieldingCryptoEncrypt + Clone, + ShieldingKeyRepository, A: AuthorApi, S: StfEnclaveSigning, H: HandleState, O: EnclaveOnChainOCallApi, -> { - pub shielding_key: K, +> where + ShieldingKeyRepository: AccessKey, + ::KeyType: ShieldingCryptoEncrypt + 'static, +{ + pub shielding_key: Arc, author_api: Arc, pub enclave_signer: Arc, pub state_handler: Arc, pub ocall_api: Arc, + pub data_provider_config: Arc, } impl< - K: ShieldingCryptoDecrypt + ShieldingCryptoEncrypt + Clone, + ShieldingKeyRepository, A: AuthorApi, S: StfEnclaveSigning, H: HandleState, O: EnclaveOnChainOCallApi, - > StfTaskContext + > StfTaskContext where + ShieldingKeyRepository: AccessKey, + ::KeyType: ShieldingCryptoEncrypt + 'static, H::StateT: SgxExternalitiesTrait, { pub fn new( - shielding_key: K, + shielding_key: Arc, author_api: Arc, enclave_signer: Arc, state_handler: Arc, ocall_api: Arc, + data_provider_config: Arc, ) -> Self { - Self { shielding_key, author_api, enclave_signer, state_handler, ocall_api } + Self { + shielding_key, + author_api, + enclave_signer, + state_handler, + ocall_api, + data_provider_config, + } } fn submit_trusted_call( @@ -156,8 +171,12 @@ where // the right channel self.author_api.swap_rpc_connection_hash(*old_top_hash, top.hash()); - let encrypted_trusted_call = self + let shielding_key = self .shielding_key + .retrieve_key() + .map_err(|e| Error::OtherError(format!("{:?}", e)))?; + + let encrypted_trusted_call = shielding_key .encrypt(&top.encode()) .map_err(|e| Error::OtherError(format!("{:?}", e)))?; @@ -179,11 +198,12 @@ where } // lifetime elision: StfTaskContext is guaranteed to outlive the fn -pub fn run_stf_task_receiver( - context: Arc>, +pub fn run_stf_task_receiver( + context: Arc>, ) -> Result<(), Error> where - K: ShieldingCryptoDecrypt + ShieldingCryptoEncrypt + Clone + Send + Sync + 'static, + ShieldingKeyRepository: AccessKey + Sync + Send + 'static, + ::KeyType: ShieldingCryptoEncrypt, A: AuthorApi + Send + Sync + 'static, S: StfEnclaveSigning + Send + Sync + 'static, H: HandleState + Send + Sync + 'static, diff --git a/tee-worker/litentry/core/stf-task/receiver/src/test.rs b/tee-worker/litentry/core/stf-task/receiver/src/test.rs index 87e7a1c115..61a9048590 100644 --- a/tee-worker/litentry/core/stf-task/receiver/src/test.rs +++ b/tee-worker/litentry/core/stf-task/receiver/src/test.rs @@ -3,6 +3,7 @@ use mock::*; use codec::Decode; use ita_stf::{TrustedCall, TrustedCallSigned}; +use itp_sgx_crypto::{mocks::KeyRepositoryMock, ShieldingCryptoDecrypt}; use itp_stf_executor::mocks::StfEnclaveSignerMock; use itp_test::mock::{ handle_state_mock::HandleStateMock, onchain_mock::OnchainMock, @@ -15,16 +16,19 @@ use litentry_primitives::Assertion; #[test] fn test_threadpool_behaviour() { let shielding_key = ShieldingCryptoMock::default(); + let shielding_key_repository_mock = KeyRepositoryMock::new(shielding_key.clone()); let author_mock = AuthorApiMock::default(); let stf_enclave_signer_mock = StfEnclaveSignerMock::default(); let handle_state_mock = HandleStateMock::default(); let onchain_mock = OnchainMock::default(); + let data_provider_conifg = DataProviderConfig::new(); let context = StfTaskContext::new( - shielding_key.clone(), + Arc::new(shielding_key_repository_mock), author_mock.into(), stf_enclave_signer_mock.into(), handle_state_mock.into(), onchain_mock.into(), + data_provider_conifg.into(), ); let _handle = std::thread::spawn(move || { run_stf_task_receiver(Arc::new(context)).unwrap(); diff --git a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/Cargo.toml b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/Cargo.toml index de66eb7b43..39a5a0c0cc 100644 --- a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/Cargo.toml +++ b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/Cargo.toml @@ -8,20 +8,16 @@ edition = "2021" [dependencies] # std dependencies futures = { version = "0.3.8", optional = true } -thiserror = { version = "1.0.26", optional = true } threadpool = { version = "1.8.0", optional = true } # sgx dependencies futures_sgx = { package = "futures", git = "https://github.com/mesalock-linux/futures-rs-sgx", optional = true } hex-sgx = { package = "hex", git = "https://github.com/mesalock-linux/rust-hex-sgx", tag = "sgx_1.1.3", features = ["sgx_tstd"], optional = true } sgx_tstd = { git = "https://github.com/apache/teaclave-sgx-sdk.git", branch = "master", features = ["net", "thread"], optional = true } -thiserror_sgx = { package = "thiserror", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3", optional = true } threadpool_sgx = { git = "https://github.com/mesalock-linux/rust-threadpool-sgx", package = "threadpool", tag = "sgx_1.1.3", optional = true } -url_sgx = { package = "url", git = "https://github.com/mesalock-linux/rust-url-sgx", tag = "sgx_1.1.3", optional = true } # no_std dependencies codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } -lazy_static = { version = "1.1.0", features = ["spin_no_std"] } log = { version = "0.4", default-features = false } sp-core = { default-features = false, features = ["full_crypto"], git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } @@ -31,7 +27,6 @@ ita-stf = { path = "../../../../app-libs/stf", default-features = false } itp-extrinsics-factory = { path = "../../../../core-primitives/extrinsics-factory", default-features = false } itp-types = { path = "../../../../core-primitives/types", default-features = false } -itp-enclave-metrics = { path = "../../../../core-primitives/enclave-metrics", default-features = false } itp-node-api = { path = "../../../../core-primitives/node-api", default-features = false } itp-ocall-api = { path = "../../../../core-primitives/ocall-api", default-features = false } itp-sgx-crypto = { path = "../../../../core-primitives/sgx/crypto", default-features = false } @@ -46,7 +41,6 @@ frame-support = { default-features = false, git = "https://github.com/paritytech lc-assertion-build = { path = "../../assertion-build", default-features = false } lc-credentials = { path = "../../credentials", default-features = false } lc-data-providers = { path = "../../data-providers", default-features = false } -lc-identity-verification = { path = "../../identity-verification", default-features = false } lc-stf-task-receiver = { path = "../../stf-task/receiver", default-features = false } lc-stf-task-sender = { path = "../../stf-task/sender", default-features = false } lc-vc-task-sender = { path = "../lc-vc-task-sender", default-features = false } @@ -60,8 +54,6 @@ sgx = [ "futures_sgx", "hex-sgx", "sgx_tstd", - "thiserror_sgx", - "url_sgx", "ita-stf/sgx", "itp-sgx-externalities/sgx", "itp-stf-executor/sgx", @@ -69,7 +61,6 @@ sgx = [ "itp-top-pool-author/sgx", "sp-core/full_crypto", "litentry-primitives/sgx", - "lc-identity-verification/sgx", "lc-assertion-build/sgx", "lc-credentials/sgx", "lc-data-providers/sgx", @@ -84,14 +75,12 @@ std = [ "threadpool", "futures", "log/std", - "thiserror", "itp-types/std", "itp-top-pool-author/std", "itp-stf-executor/std", "itp-stf-state-handler/std", "sp-core/std", "litentry-primitives/std", - "lc-identity-verification/std", "lc-assertion-build/std", "ita-sgx-runtime/std", "frame-support/std", diff --git a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/lib.rs b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/lib.rs index bda9c702eb..653ca79a8d 100644 --- a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/lib.rs +++ b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/lib.rs @@ -8,9 +8,7 @@ extern crate sgx_tstd as std; pub mod sgx_reexport_prelude { pub use futures_sgx as futures; pub use hex_sgx as hex; - pub use thiserror_sgx as thiserror; pub use threadpool_sgx as threadpool; - pub use url_sgx as url; } #[cfg(all(feature = "std", feature = "sgx"))] @@ -33,7 +31,7 @@ use itp_node_api::metadata::{ pallet_vcmp::VCMPCallIndexes, provider::AccessNodeMetadata, NodeMetadataTrait, }; use itp_ocall_api::{EnclaveMetricsOCallApi, EnclaveOnChainOCallApi}; -use itp_sgx_crypto::{ShieldingCryptoDecrypt, ShieldingCryptoEncrypt}; +use itp_sgx_crypto::{key_repository::AccessKey, ShieldingCryptoDecrypt, ShieldingCryptoEncrypt}; use itp_sgx_externalities::SgxExternalitiesTrait; use itp_stf_executor::traits::StfEnclaveSigning; use itp_stf_state_handler::handle_state::HandleState; @@ -58,12 +56,14 @@ use threadpool::ThreadPool; mod vc_handling; -pub fn run_vc_handler_runner( - context: Arc>, +pub fn run_vc_handler_runner( + context: Arc>, extrinsic_factory: Arc, node_metadata_repo: Arc, ) where - K: ShieldingCryptoDecrypt + ShieldingCryptoEncrypt + Clone + Send + Sync + 'static, + ShieldingKeyRepository: AccessKey + Send + Sync + 'static, + ::KeyType: + ShieldingCryptoEncrypt + ShieldingCryptoDecrypt + 'static, A: AuthorApi + Send + Sync + 'static, S: StfEnclaveSigning + Send + Sync + 'static, H: HandleState + Send + Sync + 'static, @@ -96,16 +96,18 @@ pub fn run_vc_handler_runner( } } -pub fn handle_request( +pub fn handle_request( key: Vec, mut encrypted_trusted_call: AesOutput, shard: ShardIdentifier, - context: Arc>, + context: Arc>, extrinsic_factory: Arc, node_metadata_repo: Arc, ) -> Result, String> where - K: ShieldingCryptoDecrypt + ShieldingCryptoEncrypt + Clone + Send + Sync + 'static, + ShieldingKeyRepository: AccessKey, + ::KeyType: + ShieldingCryptoEncrypt + ShieldingCryptoDecrypt + 'static, A: AuthorApi + Send + Sync + 'static, S: StfEnclaveSigning + Send + Sync + 'static, H: HandleState + Send + Sync + 'static, @@ -115,8 +117,12 @@ where N: AccessNodeMetadata + Send + Sync + 'static, N::MetadataType: NodeMetadataTrait, { - let aes_key: RequestAesKey = context + let shielding_key = context .shielding_key + .retrieve_key() + .map_err(|e| format!("Failed to retrieve shielding key: {:?}", e))?; + + let aes_key: RequestAesKey = shielding_key .decrypt(&key) .map_err(|e| format!("Failed to decrypted AES Key: {:?}", e))? .try_into() diff --git a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/vc_handling.rs b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/vc_handling.rs index ed16501cb0..33d6cc1ab6 100644 --- a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/vc_handling.rs +++ b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/vc_handling.rs @@ -4,12 +4,12 @@ use crate::{Getter, TrustedCallSigned}; use ita_sgx_runtime::Hash; pub use ita_stf::aes_encrypt_default; use itp_ocall_api::EnclaveOnChainOCallApi; -use itp_sgx_crypto::{ShieldingCryptoDecrypt, ShieldingCryptoEncrypt}; +use itp_sgx_crypto::{key_repository::AccessKey, ShieldingCryptoDecrypt, ShieldingCryptoEncrypt}; use itp_sgx_externalities::SgxExternalitiesTrait; use itp_stf_executor::traits::StfEnclaveSigning; use itp_stf_state_handler::handle_state::HandleState; use itp_top_pool_author::traits::AuthorApi; -use lc_data_providers::{DataProviderConfigReader, ReadDataProviderConfig}; +use lc_data_providers::DataProviderConfig; use lc_stf_task_receiver::StfTaskContext; use lc_stf_task_sender::AssertionBuildRequest; use lc_vc_task_sender::VCResponse; @@ -17,22 +17,28 @@ use litentry_primitives::{ AmountHoldingTimeType, Assertion, ErrorDetail, ErrorString, Identity, ParameterString, VCMPError, }; -use std::{format, sync::Arc}; +use std::{format, string::ToString, sync::Arc}; pub(crate) struct VCRequestHandler< - K: ShieldingCryptoDecrypt + ShieldingCryptoEncrypt + Clone, + ShieldingKeyRepository, A: AuthorApi, S: StfEnclaveSigning, H: HandleState, O: EnclaveOnChainOCallApi, -> { +> where + ShieldingKeyRepository: AccessKey, + ::KeyType: + ShieldingCryptoDecrypt + ShieldingCryptoEncrypt + 'static, +{ pub(crate) req: AssertionBuildRequest, - pub(crate) context: Arc>, + pub(crate) context: Arc>, } -impl VCRequestHandler +impl VCRequestHandler where - K: ShieldingCryptoDecrypt + ShieldingCryptoEncrypt + Clone, + ShieldingKeyRepository: AccessKey, + ::KeyType: + ShieldingCryptoDecrypt + ShieldingCryptoEncrypt + 'static, A: AuthorApi, S: StfEnclaveSigning, H: HandleState, @@ -43,72 +49,125 @@ where let mut credential = match self.req.assertion.clone() { Assertion::A1 => lc_assertion_build::a1::build(&self.req), - Assertion::A2(guild_id) => lc_assertion_build::a2::build(&self.req, guild_id), - - Assertion::A3(guild_id, channel_id, role_id) => - lc_assertion_build::a3::build(&self.req, guild_id, channel_id, role_id), - - Assertion::A4(min_balance) => - build_holding_time(&self.req, AmountHoldingTimeType::LIT, min_balance), - - Assertion::A6 => lc_assertion_build::a6::build(&self.req), - - Assertion::A7(min_balance) => - build_holding_time(&self.req, AmountHoldingTimeType::DOT, min_balance), + Assertion::A2(guild_id) => lc_assertion_build::a2::build( + &self.req, + guild_id, + &self.context.data_provider_config, + ), + + Assertion::A3(guild_id, channel_id, role_id) => lc_assertion_build::a3::build( + &self.req, + guild_id, + channel_id, + role_id, + &self.context.data_provider_config, + ), + + Assertion::A4(min_balance) => build_holding_time( + &self.req, + AmountHoldingTimeType::LIT, + min_balance, + &self.context.data_provider_config, + ), + + Assertion::A6 => + lc_assertion_build::a6::build(&self.req, &self.context.data_provider_config), + + Assertion::A7(min_balance) => build_holding_time( + &self.req, + AmountHoldingTimeType::DOT, + min_balance, + &self.context.data_provider_config, + ), // no need to pass `networks` again because it's the same as the `get_supported_web3networks` - Assertion::A8(_networks) => lc_assertion_build::a8::build(&self.req), - - Assertion::A10(min_balance) => - build_holding_time(&self.req, AmountHoldingTimeType::WBTC, min_balance), - - Assertion::A11(min_balance) => - build_holding_time(&self.req, AmountHoldingTimeType::ETH, min_balance), + Assertion::A8(_networks) => + lc_assertion_build::a8::build(&self.req, &self.context.data_provider_config), + + Assertion::A10(min_balance) => build_holding_time( + &self.req, + AmountHoldingTimeType::WBTC, + min_balance, + &self.context.data_provider_config, + ), + + Assertion::A11(min_balance) => build_holding_time( + &self.req, + AmountHoldingTimeType::ETH, + min_balance, + &self.context.data_provider_config, + ), Assertion::A13(owner) => lc_assertion_build::a13::build(&self.req, self.context.ocall_api.clone(), &owner), - Assertion::A14 => lc_assertion_build::a14::build(&self.req), + Assertion::A14 => + lc_assertion_build::a14::build(&self.req, &self.context.data_provider_config), - Assertion::Achainable(param) => lc_assertion_build::achainable::build(&self.req, param), + Assertion::Achainable(param) => lc_assertion_build::achainable::build( + &self.req, + param, + &self.context.data_provider_config, + ), Assertion::A20 => lc_assertion_build::a20::build(&self.req), - Assertion::Oneblock(course_type) => - lc_assertion_build::oneblock::course::build(&self.req, course_type), + Assertion::Oneblock(course_type) => lc_assertion_build::oneblock::course::build( + &self.req, + course_type, + &self.context.data_provider_config, + ), Assertion::GenericDiscordRole(role_type) => - lc_assertion_build::generic_discord_role::build(&self.req, role_type), + lc_assertion_build::generic_discord_role::build( + &self.req, + role_type, + &self.context.data_provider_config, + ), Assertion::BnbDomainHolding => lc_assertion_build::nodereal::bnb_domain::bnb_domain_holding_amount::build( &self.req, + &self.context.data_provider_config, ), Assertion::BnbDigitDomainClub(digit_domain_type) => lc_assertion_build::nodereal::bnb_domain::bnb_digit_domain_club_amount::build( &self.req, digit_domain_type, + &self.context.data_provider_config, ), - Assertion::VIP3MembershipCard(level) => - lc_assertion_build::vip3::card::build(&self.req, level), + Assertion::VIP3MembershipCard(level) => lc_assertion_build::vip3::card::build( + &self.req, + level, + &self.context.data_provider_config, + ), Assertion::WeirdoGhostGangHolder => - lc_assertion_build::nodereal::nft_holder::weirdo_ghost_gang_holder::build(&self.req), + lc_assertion_build::nodereal::nft_holder::weirdo_ghost_gang_holder::build( + &self.req, + &self.context.data_provider_config, + ), Assertion::LITStaking => lc_assertion_build::lit_staking::build(&self.req), Assertion::EVMAmountHolding(token_type) => lc_assertion_build::nodereal::amount_holding::evm_amount_holding::build( - &self.req, token_type, + &self.req, + token_type, + &self.context.data_provider_config, ), - Assertion::BRC20AmountHolder => - lc_assertion_build::brc20::amount_holder::build(&self.req), + Assertion::BRC20AmountHolder => lc_assertion_build::brc20::amount_holder::build( + &self.req, + &self.context.data_provider_config, + ), - Assertion::CryptoSummary => - lc_assertion_build::nodereal::crypto_summary::build(&self.req), + Assertion::CryptoSummary => lc_assertion_build::nodereal::crypto_summary::build( + &self.req, + &self.context.data_provider_config, + ), }?; // post-process the credential @@ -120,9 +179,8 @@ where ) })?; - let data_provider_config = DataProviderConfigReader::read() - .map_err(|e| VCMPError::RequestVCFailed(self.req.assertion.clone(), e))?; - credential.credential_subject.endpoint = data_provider_config.credential_endpoint; + credential.credential_subject.endpoint = + self.context.data_provider_config.credential_endpoint.to_string(); credential.issuer.id = Identity::Substrate(enclave_account.into()).to_did().map_err(|e| { @@ -167,6 +225,7 @@ fn build_holding_time( req: &AssertionBuildRequest, htype: AmountHoldingTimeType, min_balance: ParameterString, + data_provider_config: &DataProviderConfig, ) -> Result { - lc_assertion_build::holding_time::build(req, htype, min_balance) + lc_assertion_build::holding_time::build(req, htype, min_balance, data_provider_config) } diff --git a/tee-worker/litentry/core/vc-issuance/lc-vc-task-sender/Cargo.toml b/tee-worker/litentry/core/vc-issuance/lc-vc-task-sender/Cargo.toml index 53c08394c1..69e6fe4e21 100644 --- a/tee-worker/litentry/core/vc-issuance/lc-vc-task-sender/Cargo.toml +++ b/tee-worker/litentry/core/vc-issuance/lc-vc-task-sender/Cargo.toml @@ -8,14 +8,10 @@ edition = "2021" [dependencies] # std dependencies futures = { version = "0.3.8", optional = true } -thiserror = { version = "1.0.26", optional = true } -url = { version = "2.0.0", optional = true } # sgx dependencies futures_sgx = { package = "futures", git = "https://github.com/mesalock-linux/futures-rs-sgx", optional = true } sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", features = ["net", "thread"], optional = true } -thiserror_sgx = { package = "thiserror", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3", optional = true } -url_sgx = { package = "url", git = "https://github.com/mesalock-linux/rust-url-sgx", tag = "sgx_1.1.3", optional = true } # no_std dependencies codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } @@ -30,7 +26,6 @@ sp-std = { git = "https://github.com/paritytech/substrate.git", branch = "polkad itp-types = { path = "../../../../core-primitives/types", default-features = false } # litentry -itp-stf-primitives = { default-features = false, path = "../../../../core-primitives/stf-primitives" } lc-stf-task-sender = { path = "../../stf-task/sender", default-features = false } litentry-primitives = { path = "../../../primitives", default-features = false } @@ -38,8 +33,6 @@ litentry-primitives = { path = "../../../primitives", default-features = false } default = ["std"] sgx = [ "sgx_tstd", - "thiserror_sgx", - "url_sgx", "lc-stf-task-sender/sgx", "futures_sgx", ] @@ -47,8 +40,6 @@ std = [ "log/std", "sp-runtime/std", "sp-std/std", - "thiserror", - "url", "itp-types/std", "lc-stf-task-sender/std", "futures", diff --git a/tee-worker/litentry/core/vc-issuance/lc-vc-task-sender/src/lib.rs b/tee-worker/litentry/core/vc-issuance/lc-vc-task-sender/src/lib.rs index 7721ecde09..a6d642b65f 100644 --- a/tee-worker/litentry/core/vc-issuance/lc-vc-task-sender/src/lib.rs +++ b/tee-worker/litentry/core/vc-issuance/lc-vc-task-sender/src/lib.rs @@ -11,8 +11,6 @@ extern crate sgx_tstd as std; #[cfg(all(not(feature = "std"), feature = "sgx"))] pub mod sgx_reexport_prelude { pub use futures_sgx as futures; - pub use thiserror_sgx as thiserror; - pub use url_sgx as url; } #[cfg(all(not(feature = "std"), feature = "sgx"))] diff --git a/tee-worker/local-setup/config/one-worker.json b/tee-worker/local-setup/config/one-worker.json index 92bd62c04e..8e208e1849 100644 --- a/tee-worker/local-setup/config/one-worker.json +++ b/tee-worker/local-setup/config/one-worker.json @@ -34,8 +34,6 @@ "-h", "4545", "--ws-external", - "--running-mode", - "mock", "--enable-mock-server", "--parentchain-start-block", "0", diff --git a/tee-worker/local-setup/config/three-workers.json b/tee-worker/local-setup/config/three-workers.json index 8f1949ef44..d31faa7cd9 100644 --- a/tee-worker/local-setup/config/three-workers.json +++ b/tee-worker/local-setup/config/three-workers.json @@ -15,8 +15,6 @@ "3443", "-h", "4545", - "--running-mode", - "mock", "--enable-mock-server", "--parentchain-start-block", "0", @@ -42,8 +40,6 @@ "3453", "-h", "4555", - "--running-mode", - "mock", "--enable-mock-server", "--parentchain-start-block", "0" @@ -69,8 +65,6 @@ "3463", "-h", "4565", - "--running-mode", - "mock", "--enable-mock-server", "--parentchain-start-block", "0" diff --git a/tee-worker/local-setup/config/two-workers.json b/tee-worker/local-setup/config/two-workers.json index 212b5e3ddc..2ed4f08d17 100644 --- a/tee-worker/local-setup/config/two-workers.json +++ b/tee-worker/local-setup/config/two-workers.json @@ -34,8 +34,6 @@ "-h", "4545", "--ws-external", - "--running-mode", - "mock", "--enable-mock-server", "--parentchain-start-block", "0", @@ -63,8 +61,6 @@ "-h", "4546", "--ws-external", - "--running-mode", - "mock", "--enable-mock-server", "--parentchain-start-block", "0", diff --git a/tee-worker/local-setup/development-worker.json b/tee-worker/local-setup/development-worker.json index e27ae0da34..3aff16fc7a 100644 --- a/tee-worker/local-setup/development-worker.json +++ b/tee-worker/local-setup/development-worker.json @@ -15,8 +15,6 @@ "$UntrustedHttpPort", "-p", "$CollatorWSPort", - "--running-mode", - "mock", "--enable-mock-server", "--parentchain-start-block", "0", diff --git a/tee-worker/scripts/launch_local_worker.sh b/tee-worker/scripts/launch_local_worker.sh index 4fd4d40bbe..31e9c61873 100755 --- a/tee-worker/scripts/launch_local_worker.sh +++ b/tee-worker/scripts/launch_local_worker.sh @@ -27,8 +27,6 @@ WORKER_NUM=${worker_num:-1} NODE_URL=${node_url:-"ws://127.0.0.1"} # "ws://host.docker.internal" NODE_PORT=${node_port:-"9944"} # "9946" -RUNNING_MODE=${mode:-"mock"} - # Fixed values: WORKER_ENDPOINT="localhost" MU_RA_PORT="3443" @@ -130,7 +128,6 @@ for ((i = 0; i < ${WORKER_NUM}; i++)); do --untrusted-external-address ws://${WORKER_ENDPOINT} \ --untrusted-http-port ${untrusted_http_port} \ --untrusted-worker-port ${untrusted_worker_port} \ ---running-mode ${RUNNING_MODE} ${MOCK_SERVER} \ run --skip-ra ${FSUBCMD_DEV} ${FSUBCMD_REQ_STATE}" echo "${worker_name} command: ${launch_command}" diff --git a/tee-worker/scripts/litentry/release/ReadMe.md b/tee-worker/scripts/litentry/release/ReadMe.md index 7a4cbfa22a..59f516c258 100644 --- a/tee-worker/scripts/litentry/release/ReadMe.md +++ b/tee-worker/scripts/litentry/release/ReadMe.md @@ -45,7 +45,7 @@ Before starting the workers, please make sure the target parachain is already up The service will start up like this example: ``` - RUST_LOG=info,litentry_worker=debug,ws=warn,sp_io=error,substrate_api_client=warn,itc_parentchain_light_client=info,jsonrpsee_ws_client=warn,jsonrpsee_ws_server=warn,enclave_runtime=debug,ita_stf=debug,its_rpc_handler=warn,itc_rpc_client=warn,its_consensus_common=debug,its_state=warn,its_consensus_aura=warn,aura*=warn,its_consensus_slots=warn,itp_attestation_handler=debug,http_req=debug,lc_mock_server=warn,itc_rest_client=debug,lc_credentials=debug,lc_identity_verification=debug,lc_stf_task_receiver=debug,lc_stf_task_sender=debug,lc_data_providers=debug,itp_top_pool=debug,itc_parentchain_indirect_calls_executor=debug ./litentry-worker --clean-reset --ws-external --mu-ra-external-address localhost --mu-ra-port 3443 --node-port 9944 --node-url ws://127.0.0.1 --trusted-external-address wss://localhost --trusted-worker-port 2000 --untrusted-external-address ws://localhost --untrusted-http-port 4545 --untrusted-worker-port 3000 --running-mode dev --enable-mock-server run --skip-ra --dev + RUST_LOG=info,litentry_worker=debug,ws=warn,sp_io=error,substrate_api_client=warn,itc_parentchain_light_client=info,jsonrpsee_ws_client=warn,jsonrpsee_ws_server=warn,enclave_runtime=debug,ita_stf=debug,its_rpc_handler=warn,itc_rpc_client=warn,its_consensus_common=debug,its_state=warn,its_consensus_aura=warn,aura*=warn,its_consensus_slots=warn,itp_attestation_handler=debug,http_req=debug,lc_mock_server=warn,itc_rest_client=debug,lc_credentials=debug,lc_identity_verification=debug,lc_stf_task_receiver=debug,lc_stf_task_sender=debug,lc_data_providers=debug,itp_top_pool=debug,itc_parentchain_indirect_calls_executor=debug ./litentry-worker --clean-reset --ws-external --mu-ra-external-address localhost --mu-ra-port 3443 --node-port 9944 --node-url ws://127.0.0.1 --trusted-external-address wss://localhost --trusted-worker-port 2000 --untrusted-external-address ws://localhost --untrusted-http-port 4545 --untrusted-worker-port 3000 --enable-mock-server run --skip-ra --dev ``` The first part is RUST_LOG info. In production env, most of them will be disabled. Or `RUST_LOG=info` is enough. @@ -81,9 +81,6 @@ Before starting the workers, please make sure the target parachain is already up -u, --node-url Set the node server protocol and IP address [default: ws://127.0.0.1] - --running-mode - Litentry TEE service running mode [default: dev] - -T, --trusted-external-address Set the trusted worker address to be advertised on the parentchain. If no port is given, the same as in `trusted-worker-port` will be used. diff --git a/tee-worker/service/Cargo.toml b/tee-worker/service/Cargo.toml index f7f0e2e353..c751b43e50 100644 --- a/tee-worker/service/Cargo.toml +++ b/tee-worker/service/Cargo.toml @@ -70,6 +70,7 @@ sp-runtime = { git = "https://github.com/paritytech/substrate.git", branch = "po # litentry config = "0.13.3" +ita-stf = { path = "../app-libs/stf", default-features = false } lc-data-providers = { path = "../litentry/core/data-providers" } lc-mock-server = { path = "../litentry/core/mock-server" } lc-stf-task-sender = { path = "../litentry/core/stf-task/sender", default-features = false } diff --git a/tee-worker/service/src/cli.yml b/tee-worker/service/src/cli.yml index a26232c5ba..db53ab0174 100644 --- a/tee-worker/service/src/cli.yml +++ b/tee-worker/service/src/cli.yml @@ -108,11 +108,6 @@ args: long: clean-reset short: c help: Cleans and purges any previous state and key files and generates them anew before starting. - - running-mode: - long: running-mode - help: Litentry TEE service running mode - takes_value: true - default_value: "dev" - enable-mock-server: long: enable-mock-server takes_value: false diff --git a/tee-worker/service/src/config.rs b/tee-worker/service/src/config.rs index 68fb269657..5c7b569d21 100644 --- a/tee-worker/service/src/config.rs +++ b/tee-worker/service/src/config.rs @@ -33,7 +33,6 @@ static DEFAULT_UNTRUSTED_PORT: &str = "2001"; static DEFAULT_MU_RA_PORT: &str = "3443"; static DEFAULT_METRICS_PORT: &str = "8787"; static DEFAULT_UNTRUSTED_HTTP_PORT: &str = "4545"; -static DEFAULT_RUNNING_MODE: &str = "dev"; static DEFAULT_MOCK_SERVER_PORT: &str = "19527"; static DEFAULT_PARENTCHAIN_START_BLOCK: &str = "0"; static DEFAULT_FAIL_AT: &str = "0"; @@ -70,9 +69,6 @@ pub struct Config { /// Config of the 'run' subcommand pub run_config: Option, - /// Litentry - /// running mode that determins the config: dev/staging/prod/mock - pub running_mode: String, /// whether to enable the HTTP mock server for testing pub enable_mock_server: bool, /// the mock server port @@ -106,7 +102,6 @@ impl Config { untrusted_http_port: String, data_dir: PathBuf, run_config: Option, - running_mode: String, enable_mock_server: bool, mock_server_port: String, parentchain_start_block: String, @@ -132,7 +127,6 @@ impl Config { untrusted_http_port, data_dir, run_config, - running_mode, enable_mock_server, mock_server_port, parentchain_start_block, @@ -299,7 +293,6 @@ impl From<&ArgMatches<'_>> for Config { untrusted_http_port.to_string(), data_dir, run_config, - m.value_of("running-mode").unwrap_or(DEFAULT_RUNNING_MODE).to_string(), is_mock_server_enabled, mock_server_port.to_string(), parentchain_start_block.to_string(), @@ -446,7 +439,6 @@ mod test { assert_eq!(config.untrusted_http_port, DEFAULT_UNTRUSTED_HTTP_PORT); assert_eq!(config.data_dir, pwd()); assert!(config.run_config.is_none()); - assert_eq!(config.running_mode, DEFAULT_RUNNING_MODE); assert_eq!(config.mock_server_port, DEFAULT_MOCK_SERVER_PORT); assert_eq!(config.parentchain_start_block, DEFAULT_PARENTCHAIN_START_BLOCK); assert_matches!(config.fail_slot_mode, Option::None); @@ -476,8 +468,6 @@ mod test { let mu_ra_port = "99"; let untrusted_http_port = "4321"; - // running mode for litentry: dev / staging / prod - let running_mode = "dev"; let mock_server_port = "19527"; let parentchain_start_block = "30"; @@ -493,7 +483,6 @@ mod test { ("untrusted-worker-port", Default::default()), ("trusted-worker-port", Default::default()), ("untrusted-http-port", Default::default()), - ("running-mode", Default::default()), ("mock-server-port", Default::default()), ("parentchain-start-block", Default::default()), ]); @@ -508,7 +497,6 @@ mod test { args.args.get_mut("untrusted-worker-port").unwrap().vals = vec![untrusted_port.into()]; args.args.get_mut("trusted-worker-port").unwrap().vals = vec![trusted_port.into()]; args.args.get_mut("untrusted-http-port").unwrap().vals = vec![untrusted_http_port.into()]; - args.args.get_mut("running-mode").unwrap().vals = vec![running_mode.into()]; args.args.get_mut("mock-server-port").unwrap().vals = vec![mock_server_port.into()]; args.args.get_mut("parentchain-start-block").unwrap().vals = vec![parentchain_start_block.into()]; @@ -524,7 +512,6 @@ mod test { assert_eq!(config.untrusted_external_worker_address, Some(untrusted_ext_addr.to_string())); assert_eq!(config.mu_ra_external_address, Some(mu_ra_ext_addr.to_string())); assert_eq!(config.untrusted_http_port, untrusted_http_port.to_string()); - assert_eq!(config.running_mode, running_mode.to_string()); assert_eq!(config.mock_server_port, mock_server_port.to_string()); assert_eq!(config.parentchain_start_block, parentchain_start_block.to_string()); } diff --git a/tee-worker/service/src/main_impl.rs b/tee-worker/service/src/main_impl.rs index 184b0b9921..a211d214f5 100644 --- a/tee-worker/service/src/main_impl.rs +++ b/tee-worker/service/src/main_impl.rs @@ -36,9 +36,7 @@ use itp_enclave_api::{ enclave_base::EnclaveBase, remote_attestation::{RemoteAttestation, TlsRemoteAttestation}, sidechain::Sidechain, - stf_task_handler::StfTaskHandler, teeracle_api::TeeracleApi, - vc_issuance::VcIssuance, }; use itp_node_api::{ api_client::{AccountApi, PalletTeerexApi, ParentchainApi}, @@ -182,8 +180,6 @@ pub(crate) fn main() { #[cfg(not(feature = "dcap"))] let quote_size = None; - let data_provider_config = get_data_provider_config(&config); - if let Some(run_config) = config.run_config() { let shard = extract_shard(run_config.shard(), enclave.as_ref()); @@ -231,7 +227,6 @@ pub(crate) fn main() { initialization_handler, quoting_enclave_target_info, quote_size, - &data_provider_config, ); } else if let Some(smatches) = matches.subcommand_matches("request-state") { println!("*** Requesting state from a registered worker \n"); @@ -337,7 +332,6 @@ fn start_worker( initialization_handler: Arc, quoting_enclave_target_info: Option, quote_size: Option, - data_provider_config: &DataProviderConfig, ) where T: GetTokioHandle, E: EnclaveBase @@ -346,8 +340,6 @@ fn start_worker( + RemoteAttestation + TlsRemoteAttestation + TeeracleApi - + StfTaskHandler - + VcIssuance + Clone, D: BlockPruner + FetchBlocks + Sync + Send + 'static, InitializationHandler: TrackInitialization + IsInitialized + Sync + Send + 'static, @@ -611,24 +603,6 @@ fn start_worker( initialization_handler.registered_on_parentchain(); - println!("[+] Starting stf task handler thread"); - // ------------------------------------------------------------------------ - // Start stf task handler thread - let enclave_api_stf_task_handler = enclave.clone(); - let data_provider = data_provider_config.clone(); - thread::spawn(move || { - enclave_api_stf_task_handler.run_stf_task_handler(data_provider).unwrap(); - }); - - println!("[+] Starting VC issuance handler thread"); - // ------------------------------------------------------------------------ - // Start vc issuance handler thread - let enclave_api_vc_task_handler = enclave.clone(); - let data_provider = data_provider_config.clone(); - thread::spawn(move || { - enclave_api_vc_task_handler.run_vc_issuance(data_provider).unwrap(); - }); - match WorkerModeProvider::worker_mode() { WorkerMode::Teeracle => { // ------------------------------------------------------------------------ @@ -1087,99 +1061,3 @@ fn we_are_primary_worker( node_api.enclave_count(Some(*register_enclave_xt_header.parent_hash()))?; Ok(enclave_count_of_previous_block == 0) } - -fn get_data_provider_config(config: &Config) -> DataProviderConfig { - let built_in_modes = vec!["dev", "staging", "prod", "mock"]; - let built_in_config: Value = - serde_json::from_slice(include_bytes!("running-mode-config.json")).unwrap(); - - let mut data_provider_config = if built_in_modes.contains(&config.running_mode.as_str()) { - let config = built_in_config.get(config.running_mode.as_str()).unwrap(); - serde_json::from_value::(config.clone()).unwrap() - } else { - let file_path = config.running_mode.as_str(); - let mut file = File::open(file_path) - .map_err(|e| format!("{:?}, file:{}", e, file_path)) - .unwrap(); - let mut data = String::new(); - file.read_to_string(&mut data).unwrap(); - serde_json::from_str::(data.as_str()).unwrap() - }; - if let Ok(v) = env::var("TWITTER_OFFICIAL_URL") { - data_provider_config.set_twitter_official_url(v); - } - if let Ok(v) = env::var("TWITTER_LITENTRY_URL") { - data_provider_config.set_twitter_litentry_url(v); - } - // Bearer Token is as same as App only Access Token on Twitter (https://developer.twitter.com/en/docs/authentication/oauth-2-0/application-only), - // that is for developers that just need read-only access to public information. - if let Ok(v) = env::var("TWITTER_AUTH_TOKEN_V2") { - data_provider_config.set_twitter_auth_token_v2(v); - } - if let Ok(v) = env::var("DISCORD_OFFICIAL_URL") { - data_provider_config.set_discord_official_url(v); - } - if let Ok(v) = env::var("DISCORD_LITENTRY_URL") { - data_provider_config.set_discord_litentry_url(v); - } - if let Ok(v) = env::var("DISCORD_AUTH_TOKEN") { - data_provider_config.set_discord_auth_token(v); - } - if let Ok(v) = env::var("ACHAINABLE_URL") { - data_provider_config.set_achainable_url(v); - } - if let Ok(v) = env::var("ACHAINABLE_AUTH_KEY") { - data_provider_config.set_achainable_auth_key(v); - } - if let Ok(v) = env::var("CREDENTIAL_ENDPOINT") { - data_provider_config.set_credential_endpoint(v); - } - if let Ok(v) = env::var("ONEBLOCK_NOTION_KEY") { - data_provider_config.set_oneblock_notion_key(v); - } - if let Ok(v) = env::var("ONEBLOCK_NOTION_URL") { - data_provider_config.set_oneblock_notion_url(v); - } - if let Ok(v) = env::var("SORA_QUIZ_MASTER_ID") { - data_provider_config.set_sora_quiz_master_id(v); - } - if let Ok(v) = env::var("SORA_QUIZ_ATTENDEE_ID") { - data_provider_config.set_sora_quiz_attendee_id(v); - } - if let Ok(v) = env::var("NODEREAL_API_KEY") { - data_provider_config.set_nodereal_api_key(v); - } - if let Ok(v) = env::var("NODEREAL_API_RETRY_DELAY") { - let value: u64 = v.parse().unwrap(); - data_provider_config.set_nodereal_api_retry_delay(value); - } - if let Ok(v) = env::var("NODEREAL_API_RETRY_TIMES") { - let value: u16 = v.parse().unwrap(); - data_provider_config.set_nodereal_api_retry_times(value); - } - if let Ok(v) = env::var("NODEREAL_API_URL") { - data_provider_config.set_nodereal_api_url(v); - } - if let Ok(v) = env::var("NODEREAL_API_CHAIN_NETWORK_URL") { - data_provider_config.set_nodereal_api_chain_network_url(v); - } - if let Ok(v) = env::var("CONTEST_LEGEND_DISCORD_ROLE_ID") { - data_provider_config.set_contest_legend_discord_role_id(v); - } - if let Ok(v) = env::var("CONTEST_POPULARITY_DISCORD_ROLE_ID") { - data_provider_config.set_contest_popularity_discord_role_id(v); - } - if let Ok(v) = env::var("CONTEST_PARTICIPANT_DISCORD_ROLE_ID") { - data_provider_config.set_contest_participant_discord_role_id(v); - } - if let Ok(v) = env::var("VIP3_URL") { - data_provider_config.set_vip3_url(v); - } - if let Ok(v) = env::var("GENIIDATA_URL") { - data_provider_config.set_geniidata_url(v); - } - if let Ok(v) = env::var("GENIIDATA_API_KEY") { - data_provider_config.set_geniidata_api_key(v); - } - data_provider_config -} diff --git a/tee-worker/service/src/running-mode-config.json b/tee-worker/service/src/running-mode-config.json deleted file mode 100644 index 117cd7e55c..0000000000 --- a/tee-worker/service/src/running-mode-config.json +++ /dev/null @@ -1,106 +0,0 @@ -{ - "dev": { - "twitter_official_url": "https://api.twitter.com", - "twitter_litentry_url": "http://54.255.182.249:9527", - "twitter_auth_token_v2": "abcdefghijklmnopqrstuvwxyz", - "discord_official_url": "https://discordapp.com", - "discord_litentry_url": "http://54.255.182.249:9527", - "discord_auth_token": "ABCDEFGHIJKLMNOPQRSTUVWXYZ", - "achainable_url": "https://label-production.graph.tdf-labs.io", - "achainable_auth_key": "ABCDEFGHIJKLMNOPQRSTUVWXYZ", - "credential_endpoint": "http://localhost:9933", - "oneblock_notion_key": "ABCDEFGHIJKLMNOPQRSTUVWXYZ", - "oneblock_notion_url": "https://abc.com", - "sora_quiz_master_id": "SORA_QUIZ_MASTER_ID", - "sora_quiz_attendee_id": "SORA_QUIZ_ATTENDEE_ID", - "nodereal_api_key": "NODEREAL_API_KEY", - "nodereal_api_retry_delay": 5000, - "nodereal_api_retry_times": 2, - "nodereal_api_url": "https://open-platform.nodereal.io/", - "nodereal_api_chain_network_url": "https://{chain}-mainnet.nodereal.io/", - "contest_legend_discord_role_id": "CONTEST_LEGEND_DISCORD_ROLE_ID", - "contest_popularity_discord_role_id": "CONTEST_POPULARITY_DISCORD_ROLE_ID", - "contest_participant_discord_role_id": "CONTEST_PARTICIPANT_DISCORD_ROLE_ID", - "vip3_url": "https://dappapi.vip3.io/", - "geniidata_url": "https://api.geniidata.com/api/1/brc20/balance?", - "geniidata_api_key": "142cf1b0-1ca7-11ee-bb5e-9d74c2e854ac" - }, - "mock": { - "twitter_official_url": "http://localhost:19527", - "twitter_litentry_url": "http://localhost:19527", - "twitter_auth_token_v2": "", - "discord_official_url": "http://localhost:19527", - "discord_litentry_url": "http://localhost:19527", - "discord_auth_token": "", - "achainable_url": "http://localhost:19527", - "achainable_auth_key": "", - "credential_endpoint": "http://localhost:9933", - "oneblock_notion_key": "ABCDEFGHIJKLMNOPQRSTUVWXYZ", - "oneblock_notion_url": "https://abc.com", - "sora_quiz_master_id": "SORA_QUIZ_MASTER_ID", - "sora_quiz_attendee_id": "SORA_QUIZ_ATTENDEE_ID", - "nodereal_api_key": "NODEREAL_API_KEY", - "nodereal_api_retry_delay": 5000, - "nodereal_api_retry_times": 2, - "nodereal_api_url": "https://open-platform.nodereal.io/", - "nodereal_api_chain_network_url": "https://{chain}-mainnet.nodereal.io/", - "contest_legend_discord_role_id": "CONTEST_LEGEND_DISCORD_ROLE_ID", - "contest_popularity_discord_role_id": "CONTEST_POPULARITY_DISCORD_ROLE_ID", - "contest_participant_discord_role_id": "CONTEST_PARTICIPANT_DISCORD_ROLE_ID", - "vip3_url": "https://dappapi.vip3.io/", - "geniidata_url": "https://api.geniidata.com/api/1/brc20/balance?", - "geniidata_api_key": "142cf1b0-1ca7-11ee-bb5e-9d74c2e854ac" - }, - "prod": { - "twitter_official_url": "https://api.twitter.com", - "twitter_litentry_url": "", - "twitter_auth_token_v2": "abcdefghijklmnopqrstuvwxyz", - "discord_official_url": "https://discordapp.com", - "discord_litentry_url": "", - "discord_auth_token": "ABCDEFGHIJKLMNOPQRSTUVWXYZ", - "achainable_url": "https://label-production.graph.tdf-labs.io", - "achainable_auth_key": "ABCDEFGHIJKLMNOPQRSTUVWXYZ", - "credential_endpoint": "", - "oneblock_notion_key": "ABCDEFGHIJKLMNOPQRSTUVWXYZ", - "oneblock_notion_url": "https://abc.com", - "sora_quiz_master_id": "SORA_QUIZ_MASTER_ID", - "sora_quiz_attendee_id": "SORA_QUIZ_ATTENDEE_ID", - "nodereal_api_key": "NODEREAL_API_KEY", - "nodereal_api_retry_delay": 5000, - "nodereal_api_retry_times": 2, - "nodereal_api_url": "https://open-platform.nodereal.io/", - "nodereal_api_chain_network_url": "https://{chain}-mainnet.nodereal.io/", - "contest_legend_discord_role_id": "CONTEST_LEGEND_DISCORD_ROLE_ID", - "contest_popularity_discord_role_id": "CONTEST_POPULARITY_DISCORD_ROLE_ID", - "contest_participant_discord_role_id": "CONTEST_PARTICIPANT_DISCORD_ROLE_ID", - "vip3_url": "https://dappapi.vip3.io/", - "geniidata_url": "https://api.geniidata.com/api/1/brc20/balance?", - "geniidata_api_key": "142cf1b0-1ca7-11ee-bb5e-9d74c2e854ac" - }, - "staging": { - "twitter_official_url": "https://api.twitter.com", - "twitter_litentry_url": "http://54.255.182.249:9527", - "twitter_auth_token_v2": "abcdefghijklmnopqrstuvwxyz", - "discord_official_url": "https://discordapp.com", - "discord_litentry_url": "http://54.255.182.249:9527", - "discord_auth_token": "ABCDEFGHIJKLMNOPQRSTUVWXYZ", - "achainable_url": "https://label-production.graph.tdf-labs.io", - "achainable_auth_key": "ABCDEFGHIJKLMNOPQRSTUVWXYZ", - "credential_endpoint": "wss://tee-staging.litentry.io", - "oneblock_notion_key": "ABCDEFGHIJKLMNOPQRSTUVWXYZ", - "oneblock_notion_url": "https://abc.com", - "sora_quiz_master_id": "SORA_QUIZ_MASTER_ID", - "sora_quiz_attendee_id": "SORA_QUIZ_ATTENDEE_ID", - "nodereal_api_key": "NODEREAL_API_KEY", - "nodereal_api_retry_delay": 5000, - "nodereal_api_retry_times": 2, - "nodereal_api_url": "https://open-platform.nodereal.io/", - "nodereal_api_chain_network_url": "https://{chain}-mainnet.nodereal.io/", - "contest_legend_discord_role_id": "CONTEST_LEGEND_DISCORD_ROLE_ID", - "contest_popularity_discord_role_id": "CONTEST_POPULARITY_DISCORD_ROLE_ID", - "contest_participant_discord_role_id": "CONTEST_PARTICIPANT_DISCORD_ROLE_ID", - "vip3_url": "https://dappapi.vip3.io/", - "geniidata_url": "https://api.geniidata.com/api/1/brc20/balance?", - "geniidata_api_key": "142cf1b0-1ca7-11ee-bb5e-9d74c2e854ac" - } -} \ No newline at end of file diff --git a/tee-worker/service/src/tests/commons.rs b/tee-worker/service/src/tests/commons.rs index 0060c2d825..05bb81810a 100644 --- a/tee-worker/service/src/tests/commons.rs +++ b/tee-worker/service/src/tests/commons.rs @@ -56,7 +56,6 @@ pub fn local_worker_config( "4545".to_string(), crate::config::pwd(), None, - Default::default(), false, "19527".to_string(), "0".to_string(), diff --git a/tee-worker/ts-tests/worker/resuming_worker.test.ts b/tee-worker/ts-tests/worker/resuming_worker.test.ts index 62a12a7c9d..f5a7f49c1d 100644 --- a/tee-worker/ts-tests/worker/resuming_worker.test.ts +++ b/tee-worker/ts-tests/worker/resuming_worker.test.ts @@ -63,7 +63,6 @@ function generateWorkerCommandArguments( const isLaunch = command === 'launch'; return [ - '--running-mode mock', ...(workerParams.enableMockServer ? ['--enable-mock-server'] : []), ...(isLaunch ? ['--clean-reset'] : []), '--mu-ra-external-address localhost', From a31c3cb2b085392858c6dda2ed9cd0a9e4fbac68 Mon Sep 17 00:00:00 2001 From: Jonathan Alvarez Date: Mon, 22 Jan 2024 09:40:28 -0500 Subject: [PATCH 19/51] refactor(launch-local-docker): P-273 Detect block height more reliably --- scripts/launch-local-docker.sh | 67 +++++-------------- ts-tests/common/setup/wait-finalized-block.ts | 59 ++++++++++++++++ ts-tests/package.json | 1 + 3 files changed, 78 insertions(+), 49 deletions(-) create mode 100644 ts-tests/common/setup/wait-finalized-block.ts diff --git a/scripts/launch-local-docker.sh b/scripts/launch-local-docker.sh index deae4928bb..86d7bbaf4e 100755 --- a/scripts/launch-local-docker.sh +++ b/scripts/launch-local-docker.sh @@ -10,13 +10,6 @@ function usage() { CHAIN=$1 -# interval and rounds to wait to check the block production and finalization of parachain -WAIT_INTERVAL_SECONDS=10 -WAIT_ROUNDS=30 - -# if the parachain has produced the first block -BLOCK_PRODUCED=false - function print_divider() { echo "------------------------------------------------------------" } @@ -31,53 +24,29 @@ cd "$ROOTDIR/docker/generated-$CHAIN/" docker compose up -d --build -# sleep for a while to make sure `docker compose` is ready -# otherwise `docker compose logs` could print empty output -sleep 10 +print_divider + +# Install Node.js dependencies in the middle. +# It also buys `docker compose` some time. +cd "$ROOTDIR/ts-tests" +if [[ -z "${NODE_ENV}" ]]; then + echo "NODE_ENV=ci" > .env +else + echo "NODE_ENV=${NODE_ENV}" > .env +fi -parachain_service=$(docker compose ps --services --filter 'status=running' | grep -F 'parachain-') +pnpm install print_divider -echo "waiting for parachain to produce blocks ..." +echo "Waiting for parachain to produce block #1..." +pnpm run wait-finalized-block 2>&1 -for _ in $(seq 1 $WAIT_ROUNDS); do - sleep $WAIT_INTERVAL_SECONDS - if docker compose logs "$parachain_service" 2>&1 | grep -F '0 peers' 2>/dev/null | grep -Fq "best: #1" 2>/dev/null; then - echo "parachain produced #1" - BLOCK_PRODUCED=true - break - fi -done +print_divider -if [ "$BLOCK_PRODUCED" = "false" ]; then - echo "no block production detected, you might want to check it manually. Quit now" - exit 1 -fi +echo "Extending leasing period..." +pnpm run upgrade-parathread 2>&1 print_divider - -echo "waiting for parachain to finalize blocks ..." - -for _ in $(seq 1 $WAIT_ROUNDS); do - sleep $WAIT_INTERVAL_SECONDS - if docker compose logs "$parachain_service" 2>&1 | grep -F '0 peers' 2>/dev/null | grep -Fq "finalized #1" 2>/dev/null; then - echo "parachain finalized #1, all good." - print_divider - echo "extend leasing period now ..." - cd "$ROOTDIR/ts-tests" - if [[ -z "${NODE_ENV}" ]]; then - echo "NODE_ENV=ci" > .env - else - echo "NODE_ENV=${NODE_ENV}" > .env - fi - pnpm install - pnpm run upgrade-parathread 2>&1 - print_divider - echo "Done." - exit 0 - fi -done - -echo "no block finalization detected, you might want to check it manually. Quit now" -exit 1 \ No newline at end of file +echo "Done." +exit 0 diff --git a/ts-tests/common/setup/wait-finalized-block.ts b/ts-tests/common/setup/wait-finalized-block.ts new file mode 100644 index 0000000000..46aef0ba48 --- /dev/null +++ b/ts-tests/common/setup/wait-finalized-block.ts @@ -0,0 +1,59 @@ +import '@polkadot/api-augment'; +import { ApiPromise, Keyring, WsProvider } from '@polkadot/api'; +import type { VoidFn } from '@polkadot/api/types'; +import type { ISubmittableResult } from '@polkadot/types/types'; +import { loadConfig } from '../utils'; + +const FINALIZED_BLOCKS_COUNT = 1; +const TIMEOUT_MIN = 1000 * 60 * 3; // 1min + +/** + * Connects to the parachain via the config file + * and waits for `FINALIZED_BLOCKS_COUNT` blocks to + * get finalized. + * + * It times out after `TIMEOUT_MIN` min. + */ +(async () => { + const config = loadConfig(); + let timeout: NodeJS.Timeout; + let count = 0; + let unsub: VoidFn; + let api: ApiPromise; + + const provider = new WsProvider(config.parachain_ws); + + console.log(`Connecting to parachain ${config.parachain_ws}`); + + timeout = global.setTimeout(async () => { + + if (typeof unsub === 'function') unsub(); + + if (api) api.disconnect(); + provider.on('disconnected', () => { + console.log(`\nno block production detected after ${TIMEOUT_MIN}min, you might want to check it manually. Quit now`); + process.exit(1); + }); + }, TIMEOUT_MIN); + + api = await ApiPromise.create({ + provider: provider, + }); + + unsub = await api.rpc.chain.subscribeFinalizedHeads(async (head) => { + const blockNumber = head.number.toNumber(); + console.log(`Parachain finalized block #${blockNumber} with hash ${head.hash}`); + count += 1; + + if (blockNumber >= 1 && count >= FINALIZED_BLOCKS_COUNT) { + unsub(); + if (timeout) global.clearTimeout(timeout) + + await api.disconnect(); + provider.on('disconnected', () => { + console.log('Done.'); + process.exit(0); + }); + } + }) +})(); diff --git a/ts-tests/package.json b/ts-tests/package.json index ca9bdb873a..c5237b64f8 100644 --- a/ts-tests/package.json +++ b/ts-tests/package.json @@ -9,6 +9,7 @@ "scripts": { "register-parathread": "pnpm exec ts-node common/setup/register-parathread.ts", "upgrade-parathread": "pnpm exec ts-node common/setup/upgrade-parathread.ts", + "wait-finalized-block": "pnpm exec ts-node common/setup/wait-finalized-block.ts", "setup-enclave": "pnpm exec ts-node common/setup/setup-enclave.ts", "test-filter": "pnpm exec mocha --exit --sort -r ts-node/register 'integration-tests/base-filter.test.ts'", "test-bridge": "pnpm exec mocha --exit --sort -r ts-node/register 'integration-tests/bridge.test.ts'", From 37f5d74bdcc36a4d4c93ce3471dea1a10259dd3e Mon Sep 17 00:00:00 2001 From: Kai <7630809+Kailai-Wang@users.noreply.github.com> Date: Mon, 22 Jan 2024 17:27:43 +0100 Subject: [PATCH 20/51] Create idgraph upon first vc request (#2403) * init impl * fix benchmarks * init impl * add stf version * add comment * fix tests * always use threaded version * fix bug * use oneshot * Use a dedicated receiver thread --- pallets/vc-management/src/benchmarking.rs | 5 +- pallets/vc-management/src/lib.rs | 10 +- pallets/vc-management/src/tests.rs | 2 + tee-worker/Cargo.lock | 5 +- tee-worker/app-libs/stf/src/helpers.rs | 5 +- tee-worker/app-libs/stf/src/trusted_call.rs | 61 ++-- .../app-libs/stf/src/trusted_call_litentry.rs | 29 +- .../app-libs/stf/src/trusted_call_result.rs | 4 + .../commands/litentry/request_vc_direct.rs | 13 +- tee-worker/cli/src/trusted_operation.rs | 22 +- tee-worker/enclave-runtime/Cargo.lock | 102 +++--- .../enclave-runtime/src/initialization/mod.rs | 3 +- .../src/rpc/worker_api_direct.rs | 70 ++--- .../litentry/core/assertion-build/src/a2.rs | 1 + .../litentry/core/assertion-build/src/a3.rs | 1 + .../src/generic_discord_role.rs | 2 + .../amount_holding/evm_amount_holding.rs | 3 + .../nft_holder/weirdo_ghost_gang_holder.rs | 1 + .../receiver/src/handler/assertion.rs | 1 + .../src/handler/identity_verification.rs | 11 +- .../core/stf-task/receiver/src/handler/mod.rs | 15 +- .../core/stf-task/receiver/src/lib.rs | 47 +-- .../core/stf-task/receiver/src/mock.rs | 1 + .../litentry/core/stf-task/sender/src/lib.rs | 1 + .../lc-vc-task-receiver/Cargo.toml | 11 +- .../lc-vc-task-receiver/src/lib.rs | 292 ++++++++++++------ .../lc-vc-task-receiver/src/vc_handling.rs | 5 +- .../vc-issuance/lc-vc-task-sender/Cargo.toml | 2 + .../vc-issuance/lc-vc-task-sender/src/lib.rs | 23 +- .../src/identity_context.rs | 6 +- .../pallets/identity-management/src/lib.rs | 2 +- .../pallets/identity-management/src/tests.rs | 4 +- tee-worker/sidechain/rpc-handler/Cargo.toml | 6 +- .../rpc-handler/src/direct_top_pool_api.rs | 69 ++--- 34 files changed, 480 insertions(+), 355 deletions(-) diff --git a/pallets/vc-management/src/benchmarking.rs b/pallets/vc-management/src/benchmarking.rs index 1f740d083f..58b55a65b7 100644 --- a/pallets/vc-management/src/benchmarking.rs +++ b/pallets/vc-management/src/benchmarking.rs @@ -69,10 +69,11 @@ benchmarks! { let call_origin = T::TEECallOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let identity: Identity = frame_benchmarking::account::("TEST_A", 0u32, USER_SEED).into(); let assertion = Assertion::A1; + let id_graph_hash = H256::default(); let req_ext_hash = H256::default(); - }: _(call_origin, identity.clone(), assertion.clone(), req_ext_hash) + }: _(call_origin, identity.clone(), assertion.clone(), id_graph_hash, req_ext_hash) verify{ - assert_last_event::(Event::VCIssued{ identity, assertion, req_ext_hash}.into()); + assert_last_event::(Event::VCIssued{ identity, assertion, id_graph_hash, req_ext_hash }.into()); } // Benchmark `some_error`. There are no worst conditions. The benchmark showed that diff --git a/pallets/vc-management/src/lib.rs b/pallets/vc-management/src/lib.rs index e031200756..1f97ac95cf 100644 --- a/pallets/vc-management/src/lib.rs +++ b/pallets/vc-management/src/lib.rs @@ -110,9 +110,11 @@ pub mod pallet { }, // event that should be triggered by TEECallOrigin // a VC is just issued + // we have `id_graph_hash` field since vc request could create the IDGraph VCIssued { identity: Identity, assertion: Assertion, + id_graph_hash: H256, req_ext_hash: H256, }, // Admin account was changed @@ -347,10 +349,16 @@ pub mod pallet { origin: OriginFor, identity: Identity, assertion: Assertion, + id_graph_hash: H256, req_ext_hash: H256, ) -> DispatchResultWithPostInfo { let _ = T::TEECallOrigin::ensure_origin(origin)?; - Self::deposit_event(Event::VCIssued { identity, assertion, req_ext_hash }); + Self::deposit_event(Event::VCIssued { + identity, + assertion, + id_graph_hash, + req_ext_hash, + }); Ok(Pays::No.into()) } diff --git a/pallets/vc-management/src/tests.rs b/pallets/vc-management/src/tests.rs index e4995bc0c7..9b41b840a4 100644 --- a/pallets/vc-management/src/tests.rs +++ b/pallets/vc-management/src/tests.rs @@ -90,6 +90,7 @@ fn vc_issued_works() { alice, Assertion::A1, H256::default(), + H256::default(), )); }); } @@ -105,6 +106,7 @@ fn vc_issued_with_unpriviledged_origin_fails() { alice.into(), Assertion::A1, H256::default(), + H256::default(), ), sp_runtime::DispatchError::BadOrigin ); diff --git a/tee-worker/Cargo.lock b/tee-worker/Cargo.lock index 01b2f96fd0..1b63c16d32 100644 --- a/tee-worker/Cargo.lock +++ b/tee-worker/Cargo.lock @@ -6333,11 +6333,10 @@ name = "lc-vc-task-receiver" version = "0.1.0" dependencies = [ "frame-support", - "futures 0.3.28", - "futures 0.3.8", "hex 0.4.0", "ita-sgx-runtime", "ita-stf", + "itp-enclave-metrics", "itp-extrinsics-factory", "itp-node-api", "itp-ocall-api", @@ -6348,6 +6347,7 @@ dependencies = [ "itp-storage", "itp-top-pool-author", "itp-types", + "itp-utils", "lc-assertion-build", "lc-credentials", "lc-data-providers", @@ -6360,6 +6360,7 @@ dependencies = [ "parity-scale-codec", "sgx_tstd", "sp-core", + "thiserror 1.0.44", "threadpool 1.8.0", "threadpool 1.8.1", ] diff --git a/tee-worker/app-libs/stf/src/helpers.rs b/tee-worker/app-libs/stf/src/helpers.rs index d7ac5782e8..0c6fd39896 100644 --- a/tee-worker/app-libs/stf/src/helpers.rs +++ b/tee-worker/app-libs/stf/src/helpers.rs @@ -113,10 +113,7 @@ pub fn set_block_number(block_number: u32) { sp_io::storage::set(&storage_value_key("System", "Number"), &block_number.encode()); } -pub fn ensure_self( - signer: &AccountId, - who: &AccountId, -) -> bool { +pub fn ensure_self(signer: &AccountId, who: &AccountId) -> bool { signer == who } diff --git a/tee-worker/app-libs/stf/src/trusted_call.rs b/tee-worker/app-libs/stf/src/trusted_call.rs index cb732571d8..6a9f8308fe 100644 --- a/tee-worker/app-libs/stf/src/trusted_call.rs +++ b/tee-worker/app-libs/stf/src/trusted_call.rs @@ -131,13 +131,15 @@ pub enum TrustedCall { H256, ), #[codec(index = 21)] - request_vc_callback(Identity, Identity, Assertion, Vec, Option, H256), + request_vc_callback(Identity, Identity, Assertion, Vec, Option, bool, H256), #[codec(index = 22)] handle_imp_error(Identity, Option, IMPError, H256), #[codec(index = 23)] handle_vcmp_error(Identity, Option, VCMPError, H256), #[codec(index = 24)] send_erroneous_parentchain_call(Identity), + #[codec(index = 25)] + maybe_create_id_graph(Identity, Identity), // original integritee trusted calls, starting from index 50 #[codec(index = 50)] @@ -226,6 +228,7 @@ impl TrustedCall { Self::handle_imp_error(sender_identity, ..) => sender_identity, Self::handle_vcmp_error(sender_identity, ..) => sender_identity, Self::send_erroneous_parentchain_call(sender_identity) => sender_identity, + Self::maybe_create_id_graph(sender_identity, ..) => sender_identity, #[cfg(not(feature = "production"))] Self::remove_identity(sender_identity, ..) => sender_identity, } @@ -241,6 +244,7 @@ impl TrustedCall { Self::handle_imp_error(..) => "handle_imp_error", Self::deactivate_identity(..) => "deactivate_identity", Self::activate_identity(..) => "activate_identity", + Self::maybe_create_id_graph(..) => "maybe_create_id_graph", _ => "unsupported_trusted_call", } } @@ -861,17 +865,21 @@ where assertion, vc_payload, maybe_key, + should_create_id_graph, req_ext_hash, ) => { debug!( - "request_vc_callback, who: {}, assertion: {:?}", + "request_vc_callback, who: {}, should_create_id_graph: {}, assertion: {:?}", account_id_to_string(&who), + should_create_id_graph, assertion ); Self::request_vc_callback_internal( signer.to_account_id().ok_or(Self::Error::InvalidAccount)?, + who.clone(), assertion.clone(), + should_create_id_graph, ) .map_err(|e| { debug!("pushing error event ... error: {}", e); @@ -889,16 +897,24 @@ where let call_index = node_metadata_repo.get_from_metadata(|m| m.vc_issued_call_indexes())??; + // IDGraph hash can't be `None` as we should have created it otherwise + let id_graph_hash: H256 = IMT::id_graph_hash(&who).ok_or(StfError::EmptyIDGraph)?; + let mutated_id_graph = + if should_create_id_graph { IMT::id_graph(&who) } else { Vec::new() }; + calls.push(ParentchainCall::Litentry(OpaqueCall::from_tuple(&( call_index, who, assertion, + id_graph_hash, req_ext_hash, )))); if let Some(key) = maybe_key { Ok(TrustedCallResult::RequestVC(RequestVCResult { vc_payload: aes_encrypt_default(&key, &vc_payload), + pre_mutated_id_graph: aes_encrypt_default(&key, &mutated_id_graph.encode()), + pre_id_graph_hash: id_graph_hash, })) } else { Ok(TrustedCallResult::Empty) @@ -981,35 +997,26 @@ where )))); Ok(TrustedCallResult::Empty) }, + TrustedCall::maybe_create_id_graph(signer, who) => { + debug!("maybe_create_id_graph, who: {:?}", who); + let signer_account: AccountId32 = + signer.to_account_id().ok_or(Self::Error::InvalidAccount)?; + ensure_enclave_signer_account(&signer_account)?; + + // we only log the error + match IMT::maybe_create_id_graph(&who) { + Ok(()) => info!("maybe_create_id_graph OK"), + Err(e) => warn!("maybe_create_id_graph NOK: {:?}", e), + }; + + Ok(TrustedCallResult::Empty) + }, } } fn get_storage_hashes_to_update(self) -> Vec> { - let key_hashes = Vec::new(); - match self.call { - TrustedCall::noop(_) => debug!("No storage updates needed..."), - TrustedCall::balance_set_balance(..) => debug!("No storage updates needed..."), - TrustedCall::balance_transfer(..) => debug!("No storage updates needed..."), - TrustedCall::balance_unshield(..) => debug!("No storage updates needed..."), - TrustedCall::balance_shield(..) => debug!("No storage updates needed..."), - // litentry - TrustedCall::link_identity(..) => debug!("No storage updates needed..."), - #[cfg(not(feature = "production"))] - TrustedCall::remove_identity(..) => debug!("No storage updates needed..."), - TrustedCall::deactivate_identity(..) => debug!("No storage updates needed..."), - TrustedCall::activate_identity(..) => debug!("No storage updates needed..."), - TrustedCall::request_vc(..) => debug!("No storage updates needed..."), - TrustedCall::link_identity_callback(..) => debug!("No storage updates needed..."), - TrustedCall::request_vc_callback(..) => debug!("No storage updates needed..."), - TrustedCall::set_identity_networks(..) => debug!("No storage updates needed..."), - TrustedCall::handle_imp_error(..) => debug!("No storage updates needed..."), - TrustedCall::handle_vcmp_error(..) => debug!("No storage updates needed..."), - TrustedCall::send_erroneous_parentchain_call(..) => - debug!("No storage updates needed..."), - #[cfg(feature = "evm")] - _ => debug!("No storage updates needed..."), - }; - key_hashes + debug!("No storage updates needed..."); + Vec::new() } } diff --git a/tee-worker/app-libs/stf/src/trusted_call_litentry.rs b/tee-worker/app-libs/stf/src/trusted_call_litentry.rs index 597f4ce1e9..442d01d879 100644 --- a/tee-worker/app-libs/stf/src/trusted_call_litentry.rs +++ b/tee-worker/app-libs/stf/src/trusted_call_litentry.rs @@ -170,15 +170,24 @@ impl TrustedCallSigned { } let mut id_graph = IMT::id_graph(&who); + let mut should_create_id_graph = false; if id_graph.is_empty() { + // create a "virtual" IDGraph now for VC building, the "real" IDGraph will be created + // in `request_vc_callback` when a VC is guaranteed to be issued + // + // we don't mutate the IDGraph here as the transaction is **not** atomic: imagine the VC + // building fails (e.g. due to data provider error), the client won't expect the IDGraph + // to be updated, they can't get the latest IDGraph hash either + // // we are safe to use `default_web3networks` and `Active` as IDGraph would be non-empty otherwise id_graph.push(( who.clone(), IdentityContext::new(BlockNumber::one(), who.default_web3networks()), )); + should_create_id_graph = true; } let assertion_networks = assertion.get_supported_web3networks(); - let identities = get_eligible_identities(id_graph, assertion_networks); + let identities = get_eligible_identities(id_graph.as_ref(), assertion_networks); ensure!( !identities.is_empty(), @@ -198,6 +207,7 @@ impl TrustedCallSigned { parachain_block_number, sidechain_block_number, maybe_key, + should_create_id_graph, req_ext_hash, } .into(); @@ -235,10 +245,21 @@ impl TrustedCallSigned { Ok(()) } - pub fn request_vc_callback_internal(signer: AccountId, assertion: Assertion) -> StfResult<()> { + pub fn request_vc_callback_internal( + signer: AccountId, + who: Identity, + assertion: Assertion, + should_create_id_graph: bool, + ) -> StfResult<()> { // important! The signer has to be enclave_signer_account, as this TrustedCall can only be constructed internally - ensure_enclave_signer_account(&signer) - .map_err(|_| StfError::RequestVCFailed(assertion, ErrorDetail::UnauthorizedSigner))?; + ensure_enclave_signer_account(&signer).map_err(|_| { + StfError::RequestVCFailed(assertion.clone(), ErrorDetail::UnauthorizedSigner) + })?; + + if should_create_id_graph { + IMT::maybe_create_id_graph(&who) + .map_err(|e| StfError::RequestVCFailed(assertion, e.into()))?; + } Ok(()) } diff --git a/tee-worker/app-libs/stf/src/trusted_call_result.rs b/tee-worker/app-libs/stf/src/trusted_call_result.rs index d15286d216..509a3a069d 100644 --- a/tee-worker/app-libs/stf/src/trusted_call_result.rs +++ b/tee-worker/app-libs/stf/src/trusted_call_result.rs @@ -96,4 +96,8 @@ pub struct SetIdentityNetworksResult { #[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] pub struct RequestVCResult { pub vc_payload: AesOutput, + // see comments in `lc-vc-task-receiver` why it's prefixed with `pre...` + // they should be referenced/used only when the client's local IDGraph is empty + pub pre_mutated_id_graph: AesOutput, + pub pre_id_graph_hash: H256, } diff --git a/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc_direct.rs b/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc_direct.rs index 54da244d3d..3decc2615e 100644 --- a/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc_direct.rs +++ b/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc_direct.rs @@ -31,6 +31,7 @@ use litentry_primitives::{ AchainableDate, AchainableDateInterval, AchainableDatePercent, AchainableParams, AchainableToken, Assertion, ContestType, EVMTokenType, GenericDiscordRoleType, Identity, OneBlockCourseType, RequestAesKey, SoraQuizType, VIP3MembershipCardLevel, Web3Network, + REQUEST_AES_KEY_LEN, }; use sp_core::Pair; @@ -258,12 +259,7 @@ impl RequestVcDirectCommand { Command::BRC20AmountHolder => Assertion::BRC20AmountHolder, }; - let mut key: RequestAesKey = RequestAesKey::default(); - hex::decode_to_slice( - "22fc82db5b606998ad45099b7978b5b4f9dd4ea6017e57370ac56141caaabd12", - &mut key, - ) - .expect("decoding shielding_key failed"); + let key: [u8; 32] = Self::random_aes_key(); let top = TrustedCall::request_vc( alice.public().into(), @@ -289,4 +285,9 @@ impl RequestVcDirectCommand { } Ok(CliResultOk::None) } + + fn random_aes_key() -> RequestAesKey { + let random: Vec = (0..REQUEST_AES_KEY_LEN).map(|_| rand::random::()).collect(); + random[0..REQUEST_AES_KEY_LEN].try_into().unwrap() + } } diff --git a/tee-worker/cli/src/trusted_operation.rs b/tee-worker/cli/src/trusted_operation.rs index 758cb3418f..0404216d85 100644 --- a/tee-worker/cli/src/trusted_operation.rs +++ b/tee-worker/cli/src/trusted_operation.rs @@ -281,11 +281,11 @@ pub fn read_shard(trusted_args: &TrustedCli, cli: &Cli) -> Result( cli: &Cli, trusted_args: &TrustedCli, - operation_call: &TrustedOperation, + top: &TrustedOperation, ) -> TrustedOpResult { let encryption_key = get_shielding_key(cli).unwrap(); let shard = read_shard(trusted_args, cli).unwrap(); - let jsonrpc_call: String = get_json_request(shard, operation_call, encryption_key); + let jsonrpc_call: String = get_json_request(shard, top, encryption_key); debug!("get direct api"); let direct_api = get_worker_api_direct(cli); @@ -361,12 +361,12 @@ fn send_direct_request( fn send_direct_vc_request( cli: &Cli, trusted_args: &TrustedCli, - operation_call: &TrustedOperation, + top: &TrustedOperation, key: RequestAesKey, ) -> TrustedOpResult { let encryption_key = get_shielding_key(cli).unwrap(); let shard = read_shard(trusted_args, cli).unwrap(); - let jsonrpc_call: String = get_vc_json_request(shard, operation_call, encryption_key, key); + let jsonrpc_call: String = get_vc_json_request(shard, top, encryption_key, key); debug!("get direct api"); let direct_api = get_worker_api_direct(cli); @@ -419,18 +419,18 @@ fn send_direct_vc_request( pub(crate) fn get_vc_json_request( shard: ShardIdentifier, - operation_call: &TrustedOperation, + top: &TrustedOperation, shielding_pubkey: sgx_crypto_helper::rsa3072::Rsa3072PubKey, key: RequestAesKey, ) -> String { let encrypted_key = shielding_pubkey.encrypt(&key).unwrap(); - let operation_call_encrypted = aes_encrypt_default(&key, &operation_call.encode()); + let encrypted_top = aes_encrypt_default(&key, &top.encode()); // compose jsonrpc call - let request = AesRequest { shard, payload: operation_call_encrypted, key: encrypted_key }; + let request = AesRequest { shard, key: encrypted_key, payload: encrypted_top }; RpcRequest::compose_jsonrpc_call( Id::Number(1), - "author_submitVCRequest".to_string(), + "author_requestVc".to_string(), vec![request.to_hex()], ) .unwrap() @@ -446,13 +446,13 @@ fn decode_response_value( pub(crate) fn get_json_request( shard: ShardIdentifier, - operation_call: &TrustedOperation, + top: &TrustedOperation, shielding_pubkey: sgx_crypto_helper::rsa3072::Rsa3072PubKey, ) -> String { - let operation_call_encrypted = shielding_pubkey.encrypt(&operation_call.encode()).unwrap(); + let encrypted_top = shielding_pubkey.encrypt(&top.encode()).unwrap(); // compose jsonrpc call - let request = RsaRequest::new(shard, operation_call_encrypted); + let request = RsaRequest::new(shard, encrypted_top); RpcRequest::compose_jsonrpc_call( Id::Text("1".to_string()), "author_submitAndWatchRsaRequest".to_string(), diff --git a/tee-worker/enclave-runtime/Cargo.lock b/tee-worker/enclave-runtime/Cargo.lock index 0fabae9a7d..4966a88d9a 100644 --- a/tee-worker/enclave-runtime/Cargo.lock +++ b/tee-worker/enclave-runtime/Cargo.lock @@ -39,7 +39,7 @@ dependencies = [ "scale-decode", "scale-encode", "scale-info", - "serde 1.0.188", + "serde 1.0.193", "serde_json 1.0.107", "sp-application-crypto", "sp-core", @@ -56,7 +56,7 @@ dependencies = [ "parity-scale-codec", "primitive-types", "scale-info", - "serde 1.0.188", + "serde 1.0.193", "serde_json 1.0.107", "sp-application-crypto", "sp-core", @@ -105,7 +105,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.3", + "getrandom 0.2.12", "once_cell 1.18.0", "version_check", ] @@ -494,7 +494,7 @@ version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3f9629bc6c4388ea699781dc988c2b99766d7679b151c81990b4fa1208fafd3" dependencies = [ - "serde 1.0.188", + "serde 1.0.193", "toml", ] @@ -614,7 +614,7 @@ dependencies = [ "parity-scale-codec", "ring 0.16.20", "scale-info", - "serde 1.0.188", + "serde 1.0.193", "sp-core", "sp-io", "sp-runtime", @@ -1270,7 +1270,7 @@ dependencies = [ "cfg-if 1.0.0", "parity-scale-codec", "scale-info", - "serde 1.0.188", + "serde 1.0.193", ] [[package]] @@ -1561,13 +1561,13 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.3" -source = "git+https://github.com/integritee-network/getrandom-sgx?branch=update-v2.3#0a4af01fe1df0e6200192e7a709fd18da413466e" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if 1.0.0", - "sgx_libc", - "sgx_trts", - "sgx_tstd", + "libc", + "wasi", ] [[package]] @@ -1775,7 +1775,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" dependencies = [ - "serde 1.0.188", + "serde 1.0.193", ] [[package]] @@ -1848,7 +1848,7 @@ dependencies = [ "lazy_static", "log", "parity-scale-codec", - "serde 1.0.188", + "serde 1.0.193", "sgx_tstd", "substrate-fixed 0.5.9 (git+https://github.com/encointer/substrate-fixed?tag=v0.5.9)", "thiserror", @@ -2118,7 +2118,7 @@ dependencies = [ "http", "http_req", "log", - "serde 1.0.188", + "serde 1.0.193", "serde_json 1.0.107", "sgx_tstd", "thiserror", @@ -2336,7 +2336,7 @@ version = "0.9.0" dependencies = [ "itp-types", "parity-scale-codec", - "serde 1.0.188", + "serde 1.0.193", "serde_json 1.0.107", "sgx_tstd", ] @@ -2374,7 +2374,7 @@ dependencies = [ "log", "parity-scale-codec", "postcard", - "serde 1.0.188", + "serde 1.0.193", "sgx_tstd", "sp-core", ] @@ -2755,7 +2755,7 @@ dependencies = [ "itp-types", "parity-scale-codec", "scale-info", - "serde 1.0.188", + "serde 1.0.193", "sp-core", "sp-runtime", "sp-std", @@ -2908,7 +2908,7 @@ dependencies = [ "pallet-parachain-staking", "parity-scale-codec", "rust-base58 0.0.4 (git+https://github.com/mesalock-linux/rust-base58-sgx)", - "serde 1.0.188", + "serde 1.0.193", "serde_json 1.0.107", "sgx_tstd", "sp-core", @@ -2935,7 +2935,7 @@ dependencies = [ "rand 0.7.3", "rust-base58 0.0.4 (git+https://github.com/mesalock-linux/rust-base58-sgx)", "scale-info", - "serde 1.0.188", + "serde 1.0.193", "serde_json 1.0.107", "serde_json 1.0.60 (git+https://github.com/mesalock-linux/serde-json-sgx?tag=sgx_1.1.3)", "sgx_tstd", @@ -2957,7 +2957,7 @@ dependencies = [ "litentry-primitives", "log", "parity-scale-codec", - "serde 1.0.188", + "serde 1.0.193", "serde_json 1.0.107", "sgx_tstd", "thiserror", @@ -3054,10 +3054,10 @@ name = "lc-vc-task-receiver" version = "0.1.0" dependencies = [ "frame-support", - "futures 0.3.8", "hex 0.4.0", "ita-sgx-runtime", "ita-stf", + "itp-enclave-metrics", "itp-extrinsics-factory", "itp-node-api", "itp-ocall-api", @@ -3068,6 +3068,7 @@ dependencies = [ "itp-storage", "itp-top-pool-author", "itp-types", + "itp-utils", "lc-assertion-build", "lc-credentials", "lc-data-providers", @@ -3101,9 +3102,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.148" +version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" [[package]] name = "libsecp256k1" @@ -3118,7 +3119,7 @@ dependencies = [ "libsecp256k1-gen-ecmult", "libsecp256k1-gen-genmult", "rand 0.8.5", - "serde 1.0.188", + "serde 1.0.193", ] [[package]] @@ -3191,7 +3192,7 @@ dependencies = [ "ring 0.16.20", "scale-info", "secp256k1 0.28.0", - "serde 1.0.188", + "serde 1.0.193", "sgx_tstd", "sp-core", "sp-io", @@ -3649,7 +3650,7 @@ dependencies = [ "bytes 1.5.0", "impl-trait-for-tuples", "parity-scale-codec-derive", - "serde 1.0.188", + "serde 1.0.193", ] [[package]] @@ -3705,7 +3706,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a25c0b0ae06fcffe600ad392aabfa535696c8973f2253d9ac83171924c58a858" dependencies = [ "postcard-cobs", - "serde 1.0.188", + "serde 1.0.193", ] [[package]] @@ -4116,7 +4117,7 @@ checksum = "036575c29af9b6e4866ffb7fa055dbf623fe7a9cc159b33786de6013a6969d89" dependencies = [ "parity-scale-codec", "scale-info", - "serde 1.0.188", + "serde 1.0.193", ] [[package]] @@ -4184,7 +4185,7 @@ dependencies = [ "derive_more", "parity-scale-codec", "scale-info-derive", - "serde 1.0.188", + "serde 1.0.193", ] [[package]] @@ -4301,11 +4302,11 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.188" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ - "serde_derive 1.0.188", + "serde_derive 1.0.193", ] [[package]] @@ -4329,9 +4330,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote 1.0.33", @@ -4369,7 +4370,7 @@ checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa 1.0.9", "ryu", - "serde 1.0.188", + "serde 1.0.193", ] [[package]] @@ -4378,7 +4379,7 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" dependencies = [ - "serde 1.0.188", + "serde 1.0.193", ] [[package]] @@ -5059,7 +5060,7 @@ dependencies = [ "Inflector", "proc-macro2", "quote 1.0.33", - "serde 1.0.188", + "serde 1.0.193", "serde_json 1.0.107", "unicode-xid 0.2.4", ] @@ -5110,7 +5111,7 @@ dependencies = [ "log", "maybe-async", "parity-scale-codec", - "serde 1.0.188", + "serde 1.0.193", "serde_json 1.0.107", "sp-core", "sp-runtime", @@ -5149,9 +5150,9 @@ dependencies = [ [[package]] name = "subtle" -version = "2.5.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" @@ -5221,7 +5222,7 @@ dependencies = [ "log", "parity-scale-codec", "scale-info", - "serde 1.0.188", + "serde 1.0.193", "sp-core", "sp-runtime", "sp-std", @@ -5286,7 +5287,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c226a7bba6d859b63c92c4b4fe69c5b6b72d0cb897dbc8e6012298e6154cb56e" dependencies = [ - "serde 1.0.188", + "serde 1.0.193", "serde_spanned", "toml_datetime", "toml_edit 0.20.0", @@ -5298,7 +5299,7 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" dependencies = [ - "serde 1.0.188", + "serde 1.0.193", ] [[package]] @@ -5319,7 +5320,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ff63e60a958cefbb518ae1fd6566af80d9d4be430a33f3723dfc47d1d411d95" dependencies = [ "indexmap 2.0.0", - "serde 1.0.188", + "serde 1.0.193", "serde_spanned", "toml_datetime", "winnow", @@ -5407,7 +5408,7 @@ version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "digest 0.10.7", "static_assertions", ] @@ -5520,6 +5521,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "wasm-bindgen" version = "0.2.88" @@ -5682,3 +5689,8 @@ dependencies = [ "quote 1.0.33", "syn 2.0.37", ] + +[[patch.unused]] +name = "getrandom" +version = "0.2.3" +source = "git+https://github.com/integritee-network/getrandom-sgx?branch=update-v2.3#0a4af01fe1df0e6200192e7a709fd18da413466e" diff --git a/tee-worker/enclave-runtime/src/initialization/mod.rs b/tee-worker/enclave-runtime/src/initialization/mod.rs index 55845d2eac..1ce81d7f45 100644 --- a/tee-worker/enclave-runtime/src/initialization/mod.rs +++ b/tee-worker/enclave-runtime/src/initialization/mod.rs @@ -316,8 +316,7 @@ pub(crate) fn init_enclave_sidechain_components( let top_pool_author = GLOBAL_TOP_POOL_AUTHOR_COMPONENT.get()?; let state_key_repository = GLOBAL_STATE_KEY_REPOSITORY_COMPONENT.get()?; - // intialise the scheduled enlcave, must be after the attestation_handler and enclave initialisation - // get the scheduled mr enclaves and initialize the scheduled enclaves + // GLOBAL_SCHEDULED_ENCLAVE must be initialized after attestation_handler and enclave let attestation_handler = GLOBAL_ATTESTATION_HANDLER_COMPONENT.get()?; let mrenclave = attestation_handler.get_mrenclave()?; GLOBAL_SCHEDULED_ENCLAVE.init(mrenclave).map_err(|e| Error::Other(e.into()))?; diff --git a/tee-worker/enclave-runtime/src/rpc/worker_api_direct.rs b/tee-worker/enclave-runtime/src/rpc/worker_api_direct.rs index 9a372b9bb6..4133bccca6 100644 --- a/tee-worker/enclave-runtime/src/rpc/worker_api_direct.rs +++ b/tee-worker/enclave-runtime/src/rpc/worker_api_direct.rs @@ -20,7 +20,6 @@ use crate::{ generate_dcap_ra_extrinsic_from_quote_internal, generate_ias_ra_extrinsic_from_der_cert_internal, }, - initialization::global_components::GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT, utils::{ get_stf_enclave_signer_from_solo_or_parachain, get_validator_accessor_from_integritee_solo_or_parachain, @@ -31,12 +30,12 @@ use core::result::Result; use ita_sgx_runtime::{Runtime, System}; use ita_stf::{Getter, TrustedCallSigned}; use itc_parentchain::light_client::{concurrent_access::ValidatorAccess, ExtrinsicSender}; -use itp_component_container::ComponentGetter; use itp_primitives_cache::{GetPrimitives, GLOBAL_PRIMITIVES_CACHE}; use itp_rpc::RpcReturnValue; use itp_sgx_crypto::{ ed25519_derivation::DeriveEd25519, key_repository::{AccessKey, AccessPubkey}, + ShieldingCryptoDecrypt, ShieldingCryptoEncrypt, }; use itp_sgx_externalities::SgxExternalitiesTrait; use itp_stf_executor::{getter_executor::ExecuteGetter, traits::StfShardVaultQuery}; @@ -74,27 +73,30 @@ fn get_all_rpc_methods_string(io_handler: &IoHandler) -> String { format!("methods: [{}]", method_string) } -pub fn public_api_rpc_handler( +pub fn public_api_rpc_handler( top_pool_author: Arc, getter_executor: Arc, shielding_key: Arc, - state: Option>, + state: Option>, ) -> IoHandler where Author: AuthorApi + Send + Sync + 'static, GetterExecutor: ExecuteGetter + Send + Sync + 'static, - AccessShieldingKey: AccessPubkey + Send + Sync + 'static, - S: HandleState + Send + Sync + 'static, - S::StateT: SgxExternalitiesTrait, + AccessShieldingKey: AccessPubkey + AccessKey + Send + Sync + 'static, + ::KeyType: + ShieldingCryptoDecrypt + ShieldingCryptoEncrypt + DeriveEd25519 + Send + Sync + 'static, + State: HandleState + Send + Sync + 'static, + State::StateT: SgxExternalitiesTrait, { let mut io = direct_top_pool_api::add_top_pool_direct_rpc_methods( top_pool_author.clone(), IoHandler::new(), ); + let shielding_key_cloned = shielding_key.clone(); io.add_sync_method("author_getShieldingKey", move |_: Params| { debug!("worker_api_direct rpc was called: author_getShieldingKey"); - let rsa_pubkey = match shielding_key.retrieve_pubkey() { + let rsa_pubkey = match shielding_key_cloned.retrieve_pubkey() { Ok(key) => key, Err(status) => { let error_msg: String = format!("Could not get rsa pubkey due to: {}", status); @@ -118,15 +120,7 @@ where // author_getEnclaveSignerAccount let rsa_pubkey_name: &str = "author_getEnclaveSignerAccount"; io.add_sync_method(rsa_pubkey_name, move |_: Params| { - let shielding_key_repository = match GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT.get() { - Ok(s) => s, - Err(e) => { - let error_msg: String = format!("{:?}", e); - debug!("{:?}", e); - return Ok(json!(compute_hex_encoded_return_error(error_msg.as_str()))) - }, - }; - let enclave_signer_public_key = match shielding_key_repository + let enclave_signer_public_key = match shielding_key .retrieve_key() .and_then(|keypair| keypair.derive_ed25519().map(|keypair| keypair.public().to_hex())) { @@ -148,16 +142,16 @@ where }); let local_top_pool_author = top_pool_author.clone(); - let state_storage = state.clone(); + let local_state = state.clone(); io.add_sync_method("author_getNextNonce", move |params: Params| { - let state_nonce = state.clone(); - if state_nonce.is_none() { - return Ok(json!(compute_hex_encoded_return_error( - "author_getNextNonce is not avaiable" - ))) - } - #[allow(clippy::unwrap_used)] - let state_nonce_unwrap = state_nonce.unwrap(); + let local_state = match local_state.clone() { + Some(s) => s, + None => + return Ok(json!(compute_hex_encoded_return_error( + "author_getNextNonce is not avaiable" + ))), + }; + match params.parse::<(String, String)>() { Ok((shard_base58, account_hex)) => { let shard = match decode_shard_from_base58(shard_base58.as_str()) { @@ -179,7 +173,7 @@ where }, }; - match state_nonce_unwrap.load_cloned(&shard) { + match local_state.load_cloned(&shard) { Ok((mut state, _hash)) => { let trusted_calls = local_top_pool_author.get_pending_trusted_calls_for(shard, &account); @@ -384,14 +378,13 @@ where // state_getStorage io.add_sync_method("state_getStorage", move |params: Params| { - if state_storage.is_none() { - return Ok(json!(compute_hex_encoded_return_error( - "state_getStorage is not avaiable" - ))) - } - - #[allow(clippy::unwrap_used)] - let state_storage = state_storage.clone().unwrap(); + let local_state = match state.clone() { + Some(s) => s, + None => + return Ok(json!(compute_hex_encoded_return_error( + "state_getStorage is not avaiable" + ))), + }; match params.parse::<(String, String)>() { Ok((shard_str, key_hash)) => { let key_hash = if key_hash.starts_with("0x") { @@ -414,11 +407,10 @@ where return Ok(json!(compute_hex_encoded_return_error(error_msg.as_str()))) }, }; - match state_storage.load_cloned(&shard) { - Ok((state_storage, _hash)) => { + match local_state.load_cloned(&shard) { + Ok((state, _)) => { // Get storage by key hash - let value = - state_storage.get(key_hash.as_slice()).cloned().unwrap_or_default(); + let value = state.get(key_hash.as_slice()).cloned().unwrap_or_default(); debug!("query storage value:{:?}", &value); let json_value = RpcReturnValue::new(value, false, DirectRequestStatus::Ok); diff --git a/tee-worker/litentry/core/assertion-build/src/a2.rs b/tee-worker/litentry/core/assertion-build/src/a2.rs index d6063b45f6..fe723970a9 100644 --- a/tee-worker/litentry/core/assertion-build/src/a2.rs +++ b/tee-worker/litentry/core/assertion-build/src/a2.rs @@ -114,6 +114,7 @@ mod tests { parachain_block_number: 0u32, sidechain_block_number: 0u32, maybe_key: None, + should_create_id_graph: false, req_ext_hash: Default::default(), }; diff --git a/tee-worker/litentry/core/assertion-build/src/a3.rs b/tee-worker/litentry/core/assertion-build/src/a3.rs index 4790256730..6dfc4db4b3 100644 --- a/tee-worker/litentry/core/assertion-build/src/a3.rs +++ b/tee-worker/litentry/core/assertion-build/src/a3.rs @@ -144,6 +144,7 @@ mod tests { parachain_block_number: 0u32, sidechain_block_number: 0u32, maybe_key: None, + should_create_id_graph: false, req_ext_hash: Default::default(), }; diff --git a/tee-worker/litentry/core/assertion-build/src/generic_discord_role.rs b/tee-worker/litentry/core/assertion-build/src/generic_discord_role.rs index 002f111cf6..e5e9af6ca4 100644 --- a/tee-worker/litentry/core/assertion-build/src/generic_discord_role.rs +++ b/tee-worker/litentry/core/assertion-build/src/generic_discord_role.rs @@ -137,6 +137,7 @@ mod tests { parachain_block_number: 0u32, sidechain_block_number: 0u32, maybe_key: None, + should_create_id_graph: false, req_ext_hash: Default::default(), }; @@ -184,6 +185,7 @@ mod tests { parachain_block_number: 0u32, sidechain_block_number: 0u32, maybe_key: None, + should_create_id_graph: false, req_ext_hash: Default::default(), }; diff --git a/tee-worker/litentry/core/assertion-build/src/nodereal/amount_holding/evm_amount_holding.rs b/tee-worker/litentry/core/assertion-build/src/nodereal/amount_holding/evm_amount_holding.rs index 0c05d1d15c..99756dc61a 100644 --- a/tee-worker/litentry/core/assertion-build/src/nodereal/amount_holding/evm_amount_holding.rs +++ b/tee-worker/litentry/core/assertion-build/src/nodereal/amount_holding/evm_amount_holding.rs @@ -193,6 +193,7 @@ mod tests { parachain_block_number: 0u32, sidechain_block_number: 0u32, maybe_key: None, + should_create_id_graph: false, req_ext_hash: Default::default(), }; @@ -247,6 +248,7 @@ mod tests { parachain_block_number: 0u32, sidechain_block_number: 0u32, maybe_key: None, + should_create_id_graph: false, req_ext_hash: Default::default(), }; @@ -301,6 +303,7 @@ mod tests { parachain_block_number: 0u32, sidechain_block_number: 0u32, maybe_key: None, + should_create_id_graph: false, req_ext_hash: Default::default(), }; diff --git a/tee-worker/litentry/core/assertion-build/src/nodereal/nft_holder/weirdo_ghost_gang_holder.rs b/tee-worker/litentry/core/assertion-build/src/nodereal/nft_holder/weirdo_ghost_gang_holder.rs index a7640315dd..3f9f72a629 100644 --- a/tee-worker/litentry/core/assertion-build/src/nodereal/nft_holder/weirdo_ghost_gang_holder.rs +++ b/tee-worker/litentry/core/assertion-build/src/nodereal/nft_holder/weirdo_ghost_gang_holder.rs @@ -143,6 +143,7 @@ mod tests { parachain_block_number: 0u32, sidechain_block_number: 0u32, maybe_key: None, + should_create_id_graph: false, req_ext_hash: Default::default(), }; diff --git a/tee-worker/litentry/core/stf-task/receiver/src/handler/assertion.rs b/tee-worker/litentry/core/stf-task/receiver/src/handler/assertion.rs index a41eae52e0..c1acc8b102 100644 --- a/tee-worker/litentry/core/stf-task/receiver/src/handler/assertion.rs +++ b/tee-worker/litentry/core/stf-task/receiver/src/handler/assertion.rs @@ -262,6 +262,7 @@ where self.req.assertion.clone(), vc_payload, self.req.maybe_key, + self.req.should_create_id_graph, self.req.req_ext_hash, ); if let Err(e) = sender.send((self.req.shard, self.req.top_hash, c)) { diff --git a/tee-worker/litentry/core/stf-task/receiver/src/handler/identity_verification.rs b/tee-worker/litentry/core/stf-task/receiver/src/handler/identity_verification.rs index ecfd9fe3e8..952120d07c 100644 --- a/tee-worker/litentry/core/stf-task/receiver/src/handler/identity_verification.rs +++ b/tee-worker/litentry/core/stf-task/receiver/src/handler/identity_verification.rs @@ -29,8 +29,7 @@ use itp_types::ShardIdentifier; use lc_stf_task_sender::Web2IdentityVerificationRequest; use litentry_primitives::IMPError; use log::*; -use std::sync::Arc; - +use std::sync::{mpsc::Sender, Arc}; pub(crate) struct IdentityVerificationHandler< ShieldingKeyRepository, A: AuthorApi, @@ -66,7 +65,7 @@ where fn on_success( &self, _result: Self::Result, - sender: std::sync::mpsc::Sender<(ShardIdentifier, H256, TrustedCall)>, + sender: Sender<(ShardIdentifier, H256, TrustedCall)>, ) { debug!("verify identity OK"); if let Ok(enclave_signer) = self.context.enclave_signer.get_enclave_account() { @@ -86,11 +85,7 @@ where } } - fn on_failure( - &self, - error: Self::Error, - sender: std::sync::mpsc::Sender<(ShardIdentifier, H256, TrustedCall)>, - ) { + fn on_failure(&self, error: Self::Error, sender: Sender<(ShardIdentifier, H256, TrustedCall)>) { error!("verify identity failed:{:?}", error); if let Ok(enclave_signer) = self.context.enclave_signer.get_enclave_account() { let c = TrustedCall::handle_imp_error( diff --git a/tee-worker/litentry/core/stf-task/receiver/src/handler/mod.rs b/tee-worker/litentry/core/stf-task/receiver/src/handler/mod.rs index 3f0b4357ec..6817d541d1 100644 --- a/tee-worker/litentry/core/stf-task/receiver/src/handler/mod.rs +++ b/tee-worker/litentry/core/stf-task/receiver/src/handler/mod.rs @@ -19,25 +19,18 @@ pub mod identity_verification; use ita_stf::{TrustedCall, H256}; use itp_types::ShardIdentifier; +use std::sync::mpsc::Sender; pub trait TaskHandler { type Error; type Result; - fn start(&self, sender: std::sync::mpsc::Sender<(ShardIdentifier, H256, TrustedCall)>) { + fn start(&self, sender: Sender<(ShardIdentifier, H256, TrustedCall)>) { match self.on_process() { Ok(r) => self.on_success(r, sender), Err(e) => self.on_failure(e, sender), } } fn on_process(&self) -> Result; - fn on_success( - &self, - r: Self::Result, - sender: std::sync::mpsc::Sender<(ShardIdentifier, H256, TrustedCall)>, - ); - fn on_failure( - &self, - e: Self::Error, - sender: std::sync::mpsc::Sender<(ShardIdentifier, H256, TrustedCall)>, - ); + fn on_success(&self, r: Self::Result, sender: Sender<(ShardIdentifier, H256, TrustedCall)>); + fn on_failure(&self, e: Self::Error, sender: Sender<(ShardIdentifier, H256, TrustedCall)>); } diff --git a/tee-worker/litentry/core/stf-task/receiver/src/lib.rs b/tee-worker/litentry/core/stf-task/receiver/src/lib.rs index b83033ea38..e960407c10 100644 --- a/tee-worker/litentry/core/stf-task/receiver/src/lib.rs +++ b/tee-worker/litentry/core/stf-task/receiver/src/lib.rs @@ -55,12 +55,14 @@ use itp_top_pool_author::traits::AuthorApi; use itp_types::{RsaRequest, ShardIdentifier, H256}; use lc_data_providers::DataProviderConfig; use lc_stf_task_sender::{stf_task_sender, RequestType}; -use log::{debug, error, info}; +use log::*; use std::{ boxed::Box, format, string::{String, ToString}, - sync::Arc, + sync::{mpsc::channel, Arc}, + thread, + time::Instant, }; use threadpool::ThreadPool; @@ -131,10 +133,10 @@ where } } - fn submit_trusted_call( + pub fn submit_trusted_call( &self, shard: &ShardIdentifier, - old_top_hash: &H256, + maybe_old_top_hash: Option, trusted_call: &TrustedCall, ) -> Result<(), Error> { let signed_trusted_call = self @@ -169,7 +171,9 @@ where // swap the hash in the rpc connection registry to make sure furthre RPC responses go to // the right channel - self.author_api.swap_rpc_connection_hash(*old_top_hash, top.hash()); + if let Some(old_hash) = maybe_old_top_hash { + self.author_api.swap_rpc_connection_hash(old_hash, top.hash()); + } let shielding_key = self .shielding_key @@ -210,36 +214,31 @@ where H::StateT: SgxExternalitiesTrait, O: EnclaveOnChainOCallApi + EnclaveMetricsOCallApi + 'static, { - let receiver = stf_task_sender::init_stf_task_sender_storage() + let stf_task_receiver = stf_task_sender::init_stf_task_sender_storage() .map_err(|e| Error::OtherError(format!("read storage error:{:?}", e)))?; + let n_workers = 4; + let pool = ThreadPool::new(n_workers); - let (sender, to_receiver) = std::sync::mpsc::channel::<(ShardIdentifier, H256, TrustedCall)>(); + let (sender, receiver) = channel::<(ShardIdentifier, H256, TrustedCall)>(); - // Spawn thread to handle received tasks - let context_for_thread = context.clone(); - std::thread::spawn(move || loop { - if let Ok((shard, hash, call)) = to_receiver.recv() { + // Spawn thread to handle received tasks, to serialize the nonce increase even if multiple threads + // are submitting trusted calls simultaneously + let context_cloned = context.clone(); + thread::spawn(move || loop { + if let Ok((shard, hash, call)) = receiver.recv() { info!("Submitting trusted call to the pool"); - if let Err(e) = context_for_thread.submit_trusted_call(&shard, &hash, &call) { + if let Err(e) = context_cloned.submit_trusted_call(&shard, Some(hash), &call) { error!("Submit Trusted Call failed: {:?}", e); } } }); - // The total number of threads that will be used to spawn tasks in the ThreadPool - let n_workers = 4; - let pool = ThreadPool::new(n_workers); - - loop { - let req = receiver - .recv() - .map_err(|e| Error::OtherError(format!("receiver error:{:?}", e)))?; - + while let Ok(req) = stf_task_receiver.recv() { let context_pool = context.clone(); let sender_pool = sender.clone(); pool.execute(move || { - let start_time = std::time::Instant::now(); + let start_time = Instant::now(); match &req { RequestType::IdentityVerification(req) => @@ -259,4 +258,8 @@ where } }); } + + pool.join(); + warn!("stf_task_receiver loop terminated"); + Ok(()) } diff --git a/tee-worker/litentry/core/stf-task/receiver/src/mock.rs b/tee-worker/litentry/core/stf-task/receiver/src/mock.rs index 452411ea4d..dd75ae809d 100644 --- a/tee-worker/litentry/core/stf-task/receiver/src/mock.rs +++ b/tee-worker/litentry/core/stf-task/receiver/src/mock.rs @@ -44,6 +44,7 @@ pub fn construct_assertion_request(assertion: Assertion) -> RequestType { parachain_block_number: 0u32, sidechain_block_number: 0u32, top_hash: H256::zero(), + should_create_id_graph: false, req_ext_hash: H256::zero(), } .into(); diff --git a/tee-worker/litentry/core/stf-task/sender/src/lib.rs b/tee-worker/litentry/core/stf-task/sender/src/lib.rs index 96042e12af..0a15729363 100644 --- a/tee-worker/litentry/core/stf-task/sender/src/lib.rs +++ b/tee-worker/litentry/core/stf-task/sender/src/lib.rs @@ -93,6 +93,7 @@ pub struct AssertionBuildRequest { pub parachain_block_number: ParentchainBlockNumber, pub sidechain_block_number: SidechainBlockNumber, pub maybe_key: Option, + pub should_create_id_graph: bool, pub req_ext_hash: H256, } diff --git a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/Cargo.toml b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/Cargo.toml index 39a5a0c0cc..5a09e4d99a 100644 --- a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/Cargo.toml +++ b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/Cargo.toml @@ -7,11 +7,10 @@ edition = "2021" [dependencies] # std dependencies -futures = { version = "0.3.8", optional = true } +thiserror = { version = "1.0.26", optional = true } threadpool = { version = "1.8.0", optional = true } # sgx dependencies -futures_sgx = { package = "futures", git = "https://github.com/mesalock-linux/futures-rs-sgx", optional = true } hex-sgx = { package = "hex", git = "https://github.com/mesalock-linux/rust-hex-sgx", tag = "sgx_1.1.3", features = ["sgx_tstd"], optional = true } sgx_tstd = { git = "https://github.com/apache/teaclave-sgx-sdk.git", branch = "master", features = ["net", "thread"], optional = true } threadpool_sgx = { git = "https://github.com/mesalock-linux/rust-threadpool-sgx", package = "threadpool", tag = "sgx_1.1.3", optional = true } @@ -24,9 +23,8 @@ sp-core = { default-features = false, features = ["full_crypto"], git = "https:/ # internal dependencies ita-sgx-runtime = { path = "../../../../app-libs/sgx-runtime", default-features = false } ita-stf = { path = "../../../../app-libs/stf", default-features = false } +itp-enclave-metrics = { path = "../../../../core-primitives/enclave-metrics", default-features = false } itp-extrinsics-factory = { path = "../../../../core-primitives/extrinsics-factory", default-features = false } -itp-types = { path = "../../../../core-primitives/types", default-features = false } - itp-node-api = { path = "../../../../core-primitives/node-api", default-features = false } itp-ocall-api = { path = "../../../../core-primitives/ocall-api", default-features = false } itp-sgx-crypto = { path = "../../../../core-primitives/sgx/crypto", default-features = false } @@ -35,8 +33,9 @@ itp-stf-executor = { path = "../../../../core-primitives/stf-executor", default- itp-stf-state-handler = { path = "../../../../core-primitives/stf-state-handler", default-features = false } itp-storage = { path = "../../../../core-primitives/storage", default-features = false } itp-top-pool-author = { path = "../../../../core-primitives/top-pool-author", default-features = false } +itp-types = { path = "../../../../core-primitives/types", default-features = false } +itp-utils = { path = "../../../../core-primitives/utils", default-features = false } -# litentry frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } lc-assertion-build = { path = "../../assertion-build", default-features = false } lc-credentials = { path = "../../credentials", default-features = false } @@ -51,7 +50,6 @@ pallet-identity-management-tee = { path = "../../../pallets/identity-management" default = ["std"] sgx = [ "threadpool_sgx", - "futures_sgx", "hex-sgx", "sgx_tstd", "ita-stf/sgx", @@ -73,7 +71,6 @@ sgx = [ ] std = [ "threadpool", - "futures", "log/std", "itp-types/std", "itp-top-pool-author/std", diff --git a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/lib.rs b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/lib.rs index 653ca79a8d..28ce33d4bf 100644 --- a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/lib.rs +++ b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/lib.rs @@ -6,7 +6,6 @@ extern crate sgx_tstd as std; // re-export module to properly feature gate sgx and regular std environment #[cfg(all(not(feature = "std"), feature = "sgx"))] pub mod sgx_reexport_prelude { - pub use futures_sgx as futures; pub use hex_sgx as hex; pub use threadpool_sgx as threadpool; } @@ -20,36 +19,47 @@ pub use crate::sgx_reexport_prelude::*; use crate::vc_handling::VCRequestHandler; use codec::{Decode, Encode}; use frame_support::{ensure, sp_runtime::traits::One}; -pub use futures; use ita_sgx_runtime::{pallet_imt::get_eligible_identities, BlockNumber, Hash, Runtime}; use ita_stf::{ - aes_encrypt_default, trusted_call_result::RequestVCResult, Getter, OpaqueCall, TrustedCall, - TrustedCallSigned, TrustedOperation, H256, + aes_encrypt_default, + helpers::{ensure_alice, ensure_self}, + trusted_call_result::RequestVCResult, + Getter, OpaqueCall, TrustedCall, TrustedCallSigned, TrustedCallVerification, TrustedOperation, + H256, }; use itp_extrinsics_factory::CreateExtrinsics; use itp_node_api::metadata::{ pallet_vcmp::VCMPCallIndexes, provider::AccessNodeMetadata, NodeMetadataTrait, }; -use itp_ocall_api::{EnclaveMetricsOCallApi, EnclaveOnChainOCallApi}; +use itp_ocall_api::{EnclaveAttestationOCallApi, EnclaveMetricsOCallApi, EnclaveOnChainOCallApi}; use itp_sgx_crypto::{key_repository::AccessKey, ShieldingCryptoDecrypt, ShieldingCryptoEncrypt}; use itp_sgx_externalities::SgxExternalitiesTrait; use itp_stf_executor::traits::StfEnclaveSigning; use itp_stf_state_handler::handle_state::HandleState; use itp_storage::{storage_map_key, storage_value_key, StorageHasher}; use itp_top_pool_author::traits::AuthorApi; -use itp_types::{parentchain::ParentchainId, BlockNumber as SidechainBlockNumber}; +use itp_types::{ + parentchain::ParentchainId, AccountId, BlockNumber as SidechainBlockNumber, ShardIdentifier, +}; +use itp_utils::if_production_or; use lc_stf_task_receiver::StfTaskContext; use lc_stf_task_sender::AssertionBuildRequest; use lc_vc_task_sender::init_vc_task_sender_storage; use litentry_primitives::{ - aes_decrypt, AesOutput, Identity, ParentchainBlockNumber, RequestAesKey, ShardIdentifier, + AesRequest, Assertion, DecryptableRequest, Identity, ParentchainBlockNumber, }; use log::*; use pallet_identity_management_tee::{identity_context::sort_id_graph, IdentityContext}; +use sp_core::blake2_256; use std::{ + boxed::Box, format, string::{String, ToString}, - sync::Arc, + sync::{ + mpsc::{channel, Sender}, + Arc, + }, + thread, vec::Vec, }; use threadpool::ThreadPool; @@ -68,41 +78,58 @@ pub fn run_vc_handler_runner( S: StfEnclaveSigning + Send + Sync + 'static, H: HandleState + Send + Sync + 'static, H::StateT: SgxExternalitiesTrait, - O: EnclaveOnChainOCallApi + EnclaveMetricsOCallApi + 'static, + O: EnclaveOnChainOCallApi + EnclaveMetricsOCallApi + EnclaveAttestationOCallApi + 'static, Z: CreateExtrinsics + Send + Sync + 'static, N: AccessNodeMetadata + Send + Sync + 'static, N::MetadataType: NodeMetadataTrait, { - let receiver = init_vc_task_sender_storage(); + let vc_task_receiver = init_vc_task_sender_storage(); let n_workers = 4; let pool = ThreadPool::new(n_workers); - while let Ok(req) = receiver.recv() { + let (sender, receiver) = channel::<(ShardIdentifier, TrustedCall)>(); + + // Spawn thread to handle received tasks, to serialize the nonce increase even if multiple threads + // are submitting trusted calls simultaneously + let context_cloned = context.clone(); + thread::spawn(move || loop { + if let Ok((shard, call)) = receiver.recv() { + info!("Submitting trusted call to the pool"); + if let Err(e) = context_cloned.submit_trusted_call(&shard, None, &call) { + error!("Submit Trusted Call failed: {:?}", e); + } + } + }); + + while let Ok(mut req) = vc_task_receiver.recv() { let context_pool = context.clone(); let extrinsic_factory_pool = extrinsic_factory.clone(); let node_metadata_repo_pool = node_metadata_repo.clone(); + let sender_pool = sender.clone(); + pool.execute(move || { if let Err(e) = req.sender.send(handle_request( - req.key, - req.encrypted_trusted_call, - req.shard, + &mut req.request, context_pool, extrinsic_factory_pool, node_metadata_repo_pool, + sender_pool, )) { warn!("Unable to submit response back to the handler: {:?}", e); } }); } + + pool.join(); + warn!("vc_task_receiver loop terminated"); } pub fn handle_request( - key: Vec, - mut encrypted_trusted_call: AesOutput, - shard: ShardIdentifier, + request: &mut AesRequest, context: Arc>, extrinsic_factory: Arc, node_metadata_repo: Arc, + sender: Sender<(ShardIdentifier, TrustedCall)>, ) -> Result, String> where ShieldingKeyRepository: AccessKey, @@ -112,123 +139,188 @@ where S: StfEnclaveSigning + Send + Sync + 'static, H: HandleState + Send + Sync + 'static, H::StateT: SgxExternalitiesTrait, - O: EnclaveOnChainOCallApi + EnclaveMetricsOCallApi + 'static, + O: EnclaveOnChainOCallApi + EnclaveMetricsOCallApi + EnclaveAttestationOCallApi + 'static, Z: CreateExtrinsics + Send + Sync + 'static, N: AccessNodeMetadata + Send + Sync + 'static, N::MetadataType: NodeMetadataTrait, { - let shielding_key = context + let enclave_shielding_key = context .shielding_key .retrieve_key() .map_err(|e| format!("Failed to retrieve shielding key: {:?}", e))?; + let tcs = request + .decrypt(Box::new(enclave_shielding_key)) + .ok() + .and_then(|v| TrustedOperation::::decode(&mut v.as_slice()).ok()) + .and_then(|top| top.to_call().cloned()) + .ok_or_else(|| "Failed to decode payload".to_string())?; - let aes_key: RequestAesKey = shielding_key - .decrypt(&key) - .map_err(|e| format!("Failed to decrypted AES Key: {:?}", e))? - .try_into() - .map_err(|e| format!("Failed to convert to UserShieldingKeyType: {:?}", e))?; - - let decrypted_trusted_operation = aes_decrypt(&aes_key, &mut encrypted_trusted_call) - .ok_or_else(|| "Failed to decrypt trusted operation".to_string())?; - - let trusted_operation = TrustedOperation::::decode( - &mut decrypted_trusted_operation.as_slice(), - ) - .map_err(|e| format!("Failed to decode trusted operation, {:?}", e))?; - - let trusted_call: &TrustedCallSigned = trusted_operation - .to_call() - .ok_or_else(|| "Failed to convert trusted operation to trusted call".to_string())?; - - if let TrustedCall::request_vc(signer, who, assertion, maybe_key, req_ext_hash) = - trusted_call.call.clone() - { - let key = maybe_key.ok_or_else(|| "User shielding key not provided".to_string())?; - let (identities, parachain_block_number, sidechain_block_number) = context - .state_handler - .execute_on_current(&shard, |state, _| { - let prefix_key = storage_map_key( - "IdentityManagement", - "IDGraphs", - &who, - &StorageHasher::Blake2_128Concat, - ); - - // `None` means empty IDGraph, thus `unwrap_or_default` - let mut id_graph = state - .iter_prefix::>(&prefix_key) - .unwrap_or_default(); - - // Sorts the IDGraph in place - sort_id_graph::(&mut id_graph); - - if id_graph.is_empty() { - // we are safe to use `default_web3networks` and `Active` as IDGraph would be non-empty otherwise - id_graph.push(( - who.clone(), - IdentityContext::new(BlockNumber::one(), who.default_web3networks()), - )); - } - - // should never be `None`, but use `unwrap_or_default` to not panic - let parachain_block_number = state - .get(&storage_value_key("Parentchain", "Number")) - .and_then(|v| ParentchainBlockNumber::decode(&mut v.as_slice()).ok()) - .unwrap_or_default(); - let sidechain_block_number = state - .get(&storage_value_key("System", "Number")) - .and_then(|v| SidechainBlockNumber::decode(&mut v.as_slice()).ok()) - .unwrap_or_default(); - - let assertion_networks = assertion.clone().get_supported_web3networks(); - ( - get_eligible_identities(id_graph, assertion_networks), - parachain_block_number, - sidechain_block_number, - ) - }) - .map_err(|e| format!("Failed to fetch sidechain data due to: {:?}", e))?; + let mrenclave = match context.ocall_api.get_mrenclave_of_self() { + Ok(m) => m.m, + Err(_) => return Err("Failed to get mrenclave".to_string()), + }; + + ensure!(tcs.verify_signature(&mrenclave, &request.shard), "Failed to verify sig".to_string()); + + if let TrustedCall::request_vc(signer, who, assertion, maybe_key, req_ext_hash) = tcs.call { + let (mut id_graph, is_already_linked, parachain_block_number, sidechain_block_number) = + context + .state_handler + .execute_on_current(&request.shard, |state, _| { + let storage_key = storage_map_key( + "IdentityManagement", + "IDGraphs", + &who, + &StorageHasher::Blake2_128Concat, + ); + + // `None` means empty IDGraph, thus `unwrap_or_default` + let mut id_graph: Vec<(Identity, IdentityContext)> = state + .iter_prefix::>(&storage_key) + .unwrap_or_default(); + + // Sorts the IDGraph in place + sort_id_graph::(&mut id_graph); + + let storage_key = storage_map_key( + "IdentityManagement", + "LinkedIdentities", + &who, + &StorageHasher::Blake2_128Concat, + ); + + // should never be `None`, but use `unwrap_or_default` to not panic + let parachain_block_number = state + .get(&storage_value_key("Parentchain", "Number")) + .and_then(|v| ParentchainBlockNumber::decode(&mut v.as_slice()).ok()) + .unwrap_or_default(); + let sidechain_block_number = state + .get(&storage_value_key("System", "Number")) + .and_then(|v| SidechainBlockNumber::decode(&mut v.as_slice()).ok()) + .unwrap_or_default(); + + ( + id_graph, + state.contains_key(&storage_key), + parachain_block_number, + sidechain_block_number, + ) + }) + .map_err(|e| format!("Failed to fetch sidechain data due to: {:?}", e))?; + let mut should_create_id_graph = false; + if id_graph.is_empty() { + info!("IDGraph is empty, will pre-create one"); + // To create IDGraph upon first vc request (see P-410), there're two options: + // + // 1. synchronous creation: + // we delegate the vc handling to the STF version, which only returns when the IDGraph is actually created (= InSidechainBlock state). + // The downside of this method is that the first vc_request processing time is limited to the sidechain block interval. + // + // 2. asynchronous creation (this implementation): + // we check if an IDGraph **could** be created and then process the VC request right away, meanwhile, we submit a trusted call to + // top pool. So the IDGraph will be created async: in the next sidechain block. In the `RequestVCResult` we return the pre-calculated + // `mutated_id_graph` and `id_graph_hash`. + // + // Corner case: there's a small chance that some IDGraph mutation was injectd in between. For example, a client sends `request_vc` which + // is closely followed by a `link_identity` request. In this case, the IDGrpah creation resulting from `vc_request` would fail, as + // the IDGraph would have been created already by that time. But this is OK as long as it reaches the desired state eventually. + // + // However, `RequestVCResult` might carry with outdated `mutated_id_graph` and `id_graph_hash` if it lands later than `LinkIdentityResult`. + // So we call the fields `pre_mutated_id_graph` and `pre_id_graph_hash` to show they are pre-calculated. + // The client should take proper actions against it, e.g., only use the value when the local IDGraph is empty. + // + // Please note we can't mutate the state inside vc-task-receiver via `load_for_mutation` even + // though it's lock guarded, because: a) it intereferes with the block import on another thread, which eventually + // cause state mismatch before/after applying the state diff b) it's not guaranteed to be broadcasted to other workers. + // + ensure!(!is_already_linked, "Identity already exists in other IDGraph".to_string()); + // we are safe to use `default_web3networks` and `Active` as IDGraph would be non-empty otherwise + id_graph.push(( + who.clone(), + IdentityContext::new(BlockNumber::one(), who.default_web3networks()), + )); + should_create_id_graph = true; + } + info!("should_create_id_graph: {}", should_create_id_graph); + + let id_graph_hash = H256::from(blake2_256(&id_graph.encode())); + let assertion_networks = assertion.get_supported_web3networks(); + let identities = get_eligible_identities(id_graph.as_ref(), assertion_networks); ensure!(!identities.is_empty(), "No eligible identity".to_string()); - let signer = signer + let signer_account = signer .to_account_id() .ok_or_else(|| "Invalid signer account, failed to convert".to_string())?; + match assertion { + // the signer will be checked inside A13, as we don't seem to have access to ocall_api here + Assertion::A13(_) => (), + _ => if_production_or!( + ensure!(ensure_self(&signer, &who), "Unauthorized signer",), + ensure!( + ensure_self(&signer, &who) || ensure_alice(&signer_account), + "Unauthorized signer", + ) + ), + } + let req = AssertionBuildRequest { - shard, - signer, - who, - assertion, + shard: request.shard, + signer: signer_account, + who: who.clone(), + assertion: assertion.clone(), identities, top_hash: H256::zero(), parachain_block_number, sidechain_block_number, maybe_key, + should_create_id_graph, req_ext_hash, }; let vc_request_handler = VCRequestHandler { req, context: context.clone() }; - let response = vc_request_handler + let res = vc_request_handler .process() .map_err(|e| format!("Failed to build assertion due to: {:?}", e))?; let call_index = node_metadata_repo .get_from_metadata(|m| m.vc_issued_call_indexes()) - .unwrap() - .unwrap(); - let result = aes_encrypt_default(&key, &response.vc_payload); + .map_err(|_| "Failed to get vc_issued_call_indexes".to_string())? + .map_err(|_| "Failed to get metadata".to_string())?; + + let key = maybe_key.ok_or_else(|| "Invalid aes key".to_string())?; let call = OpaqueCall::from_tuple(&( call_index, - response.assertion_request.who, - response.assertion_request.assertion, - H256::zero(), + who.clone(), + assertion, + id_graph_hash, + req_ext_hash, )); - let res = RequestVCResult { vc_payload: result }; - // This internally fetches nonce from a Mutex and then updates it thereby ensuring ordering + + let mutated_id_graph = if should_create_id_graph { id_graph } else { Default::default() }; + + let res = RequestVCResult { + vc_payload: aes_encrypt_default(&key, &res.vc_payload), + pre_mutated_id_graph: aes_encrypt_default(&key, &mutated_id_graph.encode()), + pre_id_graph_hash: id_graph_hash, + }; + + // submit TrustedCall::maybe_create_id_graph to the reciever thread + let enclave_signer: AccountId = context + .enclave_signer + .get_enclave_account() + .map_err(|_| "Failed to get enclave signer".to_string())?; + let c = TrustedCall::maybe_create_id_graph(enclave_signer.into(), who); + sender + .send((request.shard, c)) + .map_err(|e| format!("Failed to send trusted call: {}", e))?; + + // this internally fetches nonce from a mutex and then updates it thereby ensuring ordering let xt = extrinsic_factory .create_extrinsics(&[call], None) .map_err(|e| format!("Failed to construct extrinsic for parentchain: {:?}", e))?; + context .ocall_api .send_to_parentchain(xt, &ParentchainId::Litentry, false) @@ -236,6 +328,6 @@ where Ok(res.encode()) } else { - Err("Invalid Trusted Operation send to VC Request handler".to_string()) + Err("Expect request_vc trusted call".to_string()) } } diff --git a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/vc_handling.rs b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/vc_handling.rs index 33d6cc1ab6..45e1a66847 100644 --- a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/vc_handling.rs +++ b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/vc_handling.rs @@ -212,10 +212,7 @@ where VCMPError::RequestVCFailed(self.req.assertion.clone(), ErrorDetail::ParseError) })?; - let vc_response = VCResponse { - assertion_request: self.req.clone(), - vc_payload: credential_str.as_bytes().to_vec(), - }; + let vc_response = VCResponse { vc_payload: credential_str.as_bytes().to_vec() }; Ok(vc_response) } diff --git a/tee-worker/litentry/core/vc-issuance/lc-vc-task-sender/Cargo.toml b/tee-worker/litentry/core/vc-issuance/lc-vc-task-sender/Cargo.toml index 69e6fe4e21..954b6ebc8e 100644 --- a/tee-worker/litentry/core/vc-issuance/lc-vc-task-sender/Cargo.toml +++ b/tee-worker/litentry/core/vc-issuance/lc-vc-task-sender/Cargo.toml @@ -32,11 +32,13 @@ litentry-primitives = { path = "../../../primitives", default-features = false } [features] default = ["std"] sgx = [ + "futures_sgx", "sgx_tstd", "lc-stf-task-sender/sgx", "futures_sgx", ] std = [ + "futures", "log/std", "sp-runtime/std", "sp-std/std", diff --git a/tee-worker/litentry/core/vc-issuance/lc-vc-task-sender/src/lib.rs b/tee-worker/litentry/core/vc-issuance/lc-vc-task-sender/src/lib.rs index a6d642b65f..66ce4e809a 100644 --- a/tee-worker/litentry/core/vc-issuance/lc-vc-task-sender/src/lib.rs +++ b/tee-worker/litentry/core/vc-issuance/lc-vc-task-sender/src/lib.rs @@ -18,10 +18,8 @@ pub use crate::sgx_reexport_prelude::*; use codec::{Decode, Encode}; use futures::channel::oneshot; -use itp_types::ShardIdentifier; use lazy_static::lazy_static; -use lc_stf_task_sender::AssertionBuildRequest; -use litentry_primitives::AesOutput; +use litentry_primitives::AesRequest; use log::*; #[cfg(feature = "std")] use std::sync::Mutex; @@ -31,7 +29,7 @@ use std::{ format, string::String, sync::{ - mpsc::{channel, Receiver, Sender}, + mpsc::{channel, Receiver, Sender as MpscSender}, Arc, }, vec::Vec, @@ -39,23 +37,20 @@ use std::{ #[derive(Debug)] pub struct VCRequest { - pub encrypted_trusted_call: AesOutput, pub sender: oneshot::Sender, String>>, - pub shard: ShardIdentifier, - pub key: Vec, + pub request: AesRequest, } #[derive(Encode, Decode, Clone)] pub struct VCResponse { - pub assertion_request: AssertionBuildRequest, pub vc_payload: Vec, } -pub type VcSender = Sender; +pub type VcSender = MpscSender; // Global storage of the sender. Should not be accessed directly. lazy_static! { - static ref GLOBAL_VC_REQUEST_TASK: Arc>> = + static ref GLOBAL_VC_TASK_SENDER: Arc>> = Arc::new(Mutex::new(Default::default())); } @@ -73,14 +68,12 @@ impl Default for VcRequestSender { } impl VcRequestSender { - pub fn send_vc_request(&self, request: VCRequest) -> Result<(), String> { + pub fn send(&self, request: VCRequest) -> Result<(), String> { debug!("send vc request: {:?}", request); // Acquire lock on extrinsic sender - let mutex_guard = GLOBAL_VC_REQUEST_TASK.lock().unwrap(); - + let mutex_guard = GLOBAL_VC_TASK_SENDER.lock().unwrap(); let vc_task_sender = mutex_guard.clone().unwrap(); - // Release mutex lock, so we don't block the lock longer than necessary. drop(mutex_guard); @@ -96,7 +89,7 @@ pub fn init_vc_task_sender_storage() -> Receiver { let (sender, receiver) = channel(); // It makes no sense to handle the unwrap, as this statement fails only if the lock has been poisoned // I believe at that point it is an unrecoverable error - let mut vc_task_storage = GLOBAL_VC_REQUEST_TASK.lock().unwrap(); + let mut vc_task_storage = GLOBAL_VC_TASK_SENDER.lock().unwrap(); *vc_task_storage = Some(VcTaskSender::new(sender)); receiver } diff --git a/tee-worker/litentry/pallets/identity-management/src/identity_context.rs b/tee-worker/litentry/pallets/identity-management/src/identity_context.rs index 1dff25389e..fb40d632a1 100644 --- a/tee-worker/litentry/pallets/identity-management/src/identity_context.rs +++ b/tee-worker/litentry/pallets/identity-management/src/identity_context.rs @@ -87,11 +87,11 @@ pub fn sort_id_graph(id_graph: &mut [(Identity, IdentityContext)]) // get the active identities in the `id_graph` whose web3networks match the `desired_web3networks`, // return a `Vec<(Identity, Vec)` with retained web3networks pub fn get_eligible_identities( - id_graph: IDGraph, + id_graph: &IDGraph, desired_web3networks: Vec, ) -> Vec { id_graph - .into_iter() + .iter() .filter_map(|item| { if item.1.is_active() { let mut networks = item.1.web3networks.to_vec(); @@ -104,7 +104,7 @@ pub fn get_eligible_identities( if networks.is_empty() && item.0.is_web3() { None } else { - Some((item.0, networks)) + Some((item.0.clone(), networks)) } } else { None diff --git a/tee-worker/litentry/pallets/identity-management/src/lib.rs b/tee-worker/litentry/pallets/identity-management/src/lib.rs index d5f12ee04b..71657036f6 100644 --- a/tee-worker/litentry/pallets/identity-management/src/lib.rs +++ b/tee-worker/litentry/pallets/identity-management/src/lib.rs @@ -261,7 +261,7 @@ pub mod pallet { impl Pallet { // try to create an IDGraph if there's none - `who` will be the prime identity // please note the web3networks for the prime identity will be all avaiable networks - fn maybe_create_id_graph(who: &Identity) -> Result<(), DispatchError> { + pub fn maybe_create_id_graph(who: &Identity) -> Result<(), DispatchError> { if IDGraphs::::get(who, who).is_none() { ensure!( !LinkedIdentities::::contains_key(who), diff --git a/tee-worker/litentry/pallets/identity-management/src/tests.rs b/tee-worker/litentry/pallets/identity-management/src/tests.rs index 8660d370a8..33753a3bc3 100644 --- a/tee-worker/litentry/pallets/identity-management/src/tests.rs +++ b/tee-worker/litentry/pallets/identity-management/src/tests.rs @@ -33,14 +33,14 @@ fn get_eligible_identities_works() { )); id_graph.push((alice_twitter_identity(1), IdentityContext::new(2u64, vec![]))); let desired_web3networks = vec![Web3Network::Litentry, Web3Network::Polkadot]; - let mut identities = get_eligible_identities(id_graph.clone(), desired_web3networks.clone()); + let mut identities = get_eligible_identities(id_graph.as_ref(), desired_web3networks.clone()); assert_eq!(identities.len(), 2); assert_eq!(identities[0].1, vec![Web3Network::Litentry]); assert_eq!(identities[1].1, vec![]); // `alice_evm_identity` should be filtered out id_graph.push((alice_evm_identity(), IdentityContext::new(1u64, vec![Web3Network::Bsc]))); - identities = get_eligible_identities(id_graph, desired_web3networks); + identities = get_eligible_identities(id_graph.as_ref(), desired_web3networks); assert_eq!(identities.len(), 2); assert_eq!(identities[0].1, vec![Web3Network::Litentry]); assert_eq!(identities[1].1, vec![]); diff --git a/tee-worker/sidechain/rpc-handler/Cargo.toml b/tee-worker/sidechain/rpc-handler/Cargo.toml index 95373a287c..1b8d8b715c 100644 --- a/tee-worker/sidechain/rpc-handler/Cargo.toml +++ b/tee-worker/sidechain/rpc-handler/Cargo.toml @@ -16,6 +16,7 @@ itp-types = { path = "../../core-primitives/types", default-features = false } itp-utils = { path = "../../core-primitives/utils", default-features = false } its-primitives = { path = "../primitives", default-features = false } +lc-vc-task-sender = { path = "../../litentry/core/vc-issuance/lc-vc-task-sender", default-features = false } litentry-primitives = { path = "../../litentry/primitives", default-features = false } # sgx enabled external libraries @@ -30,13 +31,13 @@ rust-base58 = { package = "rust-base58", version = "0.0.4", optional = true } # no-std compatible libraries codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } -lc-vc-task-sender = { path = "../../litentry/core/vc-issuance/lc-vc-task-sender", default-features = false } log = { version = "0.4", default-features = false } sp-core = { default-features = false, features = ["full_crypto"], git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } [features] default = ["std"] std = [ + "futures", "itp-rpc/std", "itp-stf-primitives/std", "itp-top-pool-author/std", @@ -47,14 +48,13 @@ std = [ "log/std", "rust-base58", "lc-vc-task-sender/std", - "futures", ] sgx = [ + "futures_sgx", "sgx_tstd", "itp-rpc/sgx", "itp-top-pool-author/sgx", "jsonrpc-core_sgx", "rust-base58_sgx", "lc-vc-task-sender/sgx", - "futures_sgx", ] diff --git a/tee-worker/sidechain/rpc-handler/src/direct_top_pool_api.rs b/tee-worker/sidechain/rpc-handler/src/direct_top_pool_api.rs index f8631e710a..d832e91024 100644 --- a/tee-worker/sidechain/rpc-handler/src/direct_top_pool_api.rs +++ b/tee-worker/sidechain/rpc-handler/src/direct_top_pool_api.rs @@ -26,7 +26,7 @@ use rust_base58::base58::FromBase58; use base58::FromBase58; use codec::{Decode, Encode}; -use futures::{channel::oneshot, FutureExt}; +use futures::channel::oneshot; use itp_rpc::RpcReturnValue; use itp_stf_primitives::types::AccountId; use itp_top_pool_author::traits::AuthorApi; @@ -36,7 +36,14 @@ use jsonrpc_core::{futures::executor, serde_json::json, Error as RpcError, IoHan use lc_vc_task_sender::{VCRequest, VcRequestSender}; use litentry_primitives::AesRequest; use log::*; -use std::{borrow::ToOwned, format, string::String, sync::Arc, vec, vec::Vec}; +use std::{ + borrow::ToOwned, + format, + string::{String, ToString}, + sync::Arc, + vec, + vec::Vec, +}; type Hash = sp_core::H256; @@ -114,18 +121,16 @@ where Ok(json!(json_value)) }); - io_handler.add_method("author_submitVCRequest", move |params: Params| { - debug!("worker_api_direct rpc was called: author_submitVCRequest"); + io_handler.add_method("author_requestVc", move |params: Params| { + debug!("worker_api_direct rpc was called: author_requestVc"); async move { - let json_value = match submit_vc_request_inner(params).await { + let json_value = match request_vc_inner(params).await { Ok(value) => value.to_hex(), Err(error) => compute_hex_encoded_return_error(&error), }; - Ok(json!(json_value)) } - .boxed() }); // Litentry: a morphling of `author_submitAndWatchRsaRequest` @@ -264,6 +269,16 @@ fn compute_hex_encoded_return_error(error_msg: &str) -> String { RpcReturnValue::from_error_message(error_msg).to_hex() } +// we expect our `params` to be "by-position array" +// see https://www.jsonrpc.org/specification#parameter_structures +fn get_request_payload(params: Params) -> Result { + let s_vec = params.parse::>().map_err(|e| format!("{}", e))?; + + let s = s_vec.get(0).ok_or_else(|| "Empty params".to_string())?; + debug!("Request payload: {}", s); + Ok(s.to_owned()) +} + fn author_submit_extrinsic_inner( author: Arc, params: Params, @@ -274,15 +289,8 @@ where TCS: PartialEq + Encode + Decode + Debug + Send + Sync + 'static, G: PartialEq + Encode + Decode + Debug + Send + Sync + 'static, { - debug!("Author submit and watch trusted operation.."); - - let hex_encoded_params = params.parse::>().map_err(|e| format!("{:?}", e))?; - let param = &hex_encoded_params.get(0).ok_or("Could not get first param")?; - - info!("Got request hex: {:?}", param); - std::println!("Got request hex: {:?}", param); - - let request = RsaRequest::from_hex(param).map_err(|e| format!("{:?}", e))?; + let payload = get_request_payload(params)?; + let request = RsaRequest::from_hex(&payload).map_err(|e| format!("{:?}", e))?; let response: Result = if let Some(method) = json_rpc_method { executor::block_on(async { author.watch_and_broadcast_top(request, method).await }) @@ -308,12 +316,8 @@ where TCS: PartialEq + Encode + Decode + Debug + Send + Sync + 'static, G: PartialEq + Encode + Decode + Debug + Send + Sync + 'static, { - let hex_encoded_params = params.parse::>().map_err(|e| format!("{:?}", e))?; - let param = &hex_encoded_params.get(0).ok_or("Could not get first param")?; - - info!("author_submitAndWatchAesRequest, request hex: {:?}", param); - - let request = AesRequest::from_hex(param).map_err(|e| format!("{:?}", e))?; + let payload = get_request_payload(params)?; + let request = AesRequest::from_hex(&payload).map_err(|e| format!("{:?}", e))?; let response: Result = if let Some(method) = json_rpc_method { executor::block_on(async { author.watch_and_broadcast_top(request, method).await }) @@ -329,24 +333,17 @@ where response.map_err(|e| format!("{:?}", e)) } -async fn submit_vc_request_inner(params: Params) -> Result { - let hex_encoded_params = params.parse::>().map_err(|e| format!("{:?}", e))?; - let param = &hex_encoded_params.get(0).ok_or("Could not get first param")?; - let request = AesRequest::from_hex(param).map_err(|e| format!("{:?}", e))?; +async fn request_vc_inner(params: Params) -> Result { + let payload = get_request_payload(params)?; + let request = AesRequest::from_hex(&payload) + .map_err(|e| format!("AesRequest construction error: {:?}", e))?; + let vc_request_sender = VcRequestSender::new(); let (sender, receiver) = oneshot::channel::, String>>(); - let vc_request = VCRequest { - encrypted_trusted_call: request.payload, - sender, - shard: request.shard, - key: request.key, - }; - - if let Err(e) = VcRequestSender::new().send_vc_request(vc_request) { - return Err(compute_hex_encoded_return_error(&e)) - } + vc_request_sender.send(VCRequest { sender, request })?; + // we only expect one response, hence no loop match receiver.await { Ok(Ok(response)) => Ok(RpcReturnValue { do_watch: false, value: response, status: DirectRequestStatus::Ok }), From f2290ab106df5b25957f4c8d04e60c1e5d3a452b Mon Sep 17 00:00:00 2001 From: "will.li" <120463031+higherordertech@users.noreply.github.com> Date: Tue, 23 Jan 2024 22:12:17 +1100 Subject: [PATCH 21/51] P-216: add new vc structure independent of data provider (#2402) * feat: P-216 add new vc structure independent of data provider * fix clippy * attempt to fix integration test by increase sleep time --------- Co-authored-by: higherordertech --- primitives/core/src/assertion.rs | 5 + primitives/core/src/lib.rs | 3 + primitives/core/src/web3_token.rs | 85 +++ tee-worker/Cargo.lock | 70 +++ .../commands/litentry/request_vc.rs | 66 ++- .../commands/litentry/request_vc_direct.rs | 38 +- .../interfaces/vc/definitions.ts | 28 + tee-worker/enclave-runtime/Cargo.lock | 65 +++ .../core/assertion-build-v2/Cargo.toml | 62 +++ .../core/assertion-build-v2/src/lib.rs | 44 ++ .../src/token_holding_amount/mod.rs | 507 ++++++++++++++++++ .../amount_holding/evm_amount_holding.rs | 5 +- tee-worker/litentry/core/common/Cargo.toml | 23 + tee-worker/litentry/core/common/src/lib.rs | 45 ++ .../core/common/src/web3_token/mod.rs | 206 +++++++ .../litentry/core/credentials-v2/Cargo.toml | 49 ++ .../litentry/core/credentials-v2/src/lib.rs | 39 ++ .../src/token_holding_amount/mod.rs | 133 +++++ .../litentry/core/data-providers/src/lib.rs | 60 ++- .../data-providers/src/nodereal_jsonrpc.rs | 71 +-- .../core/mock-server/src/achainable.rs | 18 + .../core/mock-server/src/nodereal_jsonrpc.rs | 19 +- tee-worker/litentry/core/service/Cargo.toml | 50 ++ tee-worker/litentry/core/service/src/lib.rs | 37 ++ .../core/service/src/web3_token/mod.rs | 23 + .../web3_token/token_balance/bnb_balance.rs | 76 +++ .../src/web3_token/token_balance/common.rs | 89 +++ .../web3_token/token_balance/eth_balance.rs | 76 +++ .../web3_token/token_balance/lit_balance.rs | 94 ++++ .../src/web3_token/token_balance/mod.rs | 43 ++ .../core/stf-task/receiver/Cargo.toml | 3 + .../receiver/src/handler/assertion.rs | 7 + .../lc-vc-task-receiver/Cargo.toml | 3 + .../lc-vc-task-receiver/src/vc_handling.rs | 7 + tee-worker/litentry/primitives/src/lib.rs | 2 +- tee-worker/service/src/prometheus_metrics.rs | 1 + ts-tests/common/setup/setup-bridge.ts | 2 +- ts-tests/common/setup/wait-finalized-block.ts | 45 +- 38 files changed, 2123 insertions(+), 76 deletions(-) create mode 100644 primitives/core/src/web3_token.rs create mode 100644 tee-worker/litentry/core/assertion-build-v2/Cargo.toml create mode 100644 tee-worker/litentry/core/assertion-build-v2/src/lib.rs create mode 100644 tee-worker/litentry/core/assertion-build-v2/src/token_holding_amount/mod.rs create mode 100644 tee-worker/litentry/core/common/Cargo.toml create mode 100644 tee-worker/litentry/core/common/src/lib.rs create mode 100644 tee-worker/litentry/core/common/src/web3_token/mod.rs create mode 100644 tee-worker/litentry/core/credentials-v2/Cargo.toml create mode 100644 tee-worker/litentry/core/credentials-v2/src/lib.rs create mode 100644 tee-worker/litentry/core/credentials-v2/src/token_holding_amount/mod.rs create mode 100644 tee-worker/litentry/core/service/Cargo.toml create mode 100644 tee-worker/litentry/core/service/src/lib.rs create mode 100644 tee-worker/litentry/core/service/src/web3_token/mod.rs create mode 100644 tee-worker/litentry/core/service/src/web3_token/token_balance/bnb_balance.rs create mode 100644 tee-worker/litentry/core/service/src/web3_token/token_balance/common.rs create mode 100644 tee-worker/litentry/core/service/src/web3_token/token_balance/eth_balance.rs create mode 100644 tee-worker/litentry/core/service/src/web3_token/token_balance/lit_balance.rs create mode 100644 tee-worker/litentry/core/service/src/web3_token/token_balance/mod.rs diff --git a/primitives/core/src/assertion.rs b/primitives/core/src/assertion.rs index 8b54e5aea1..8c280b3fe6 100644 --- a/primitives/core/src/assertion.rs +++ b/primitives/core/src/assertion.rs @@ -20,6 +20,7 @@ use crate::{ all_web3networks, AccountId, BnbDigitDomainType, BoundedWeb3Network, EVMTokenType, GenericDiscordRoleType, OneBlockCourseType, VIP3MembershipCardLevel, Web3Network, + Web3TokenType, }; use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; @@ -257,6 +258,9 @@ pub enum Assertion { #[codec(index = 23)] CryptoSummary, + + #[codec(index = 24)] + TokenHoldingAmount(Web3TokenType), } impl Assertion { @@ -304,6 +308,7 @@ impl Assertion { Self::A1 | Self::A13(..) | Self::A20 => all_web3networks(), // no web3 network is allowed Self::A2(..) | Self::A3(..) | Self::A6 | Self::GenericDiscordRole(..) => vec![], + Self::TokenHoldingAmount(t_type) => t_type.get_supported_networks(), } } } diff --git a/primitives/core/src/lib.rs b/primitives/core/src/lib.rs index 8f41ee4af7..c41e64cbe3 100644 --- a/primitives/core/src/lib.rs +++ b/primitives/core/src/lib.rs @@ -59,6 +59,9 @@ pub use generic_discord_role::*; mod evm_amount_holding; pub use evm_amount_holding::*; +mod web3_token; +pub use web3_token::*; + /// Common types of parachains. mod types { use sp_runtime::{ diff --git a/primitives/core/src/web3_token.rs b/primitives/core/src/web3_token.rs new file mode 100644 index 0000000000..8debf0276c --- /dev/null +++ b/primitives/core/src/web3_token.rs @@ -0,0 +1,85 @@ +// Copyright 2020-2024 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +use codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_std::{vec, vec::Vec}; + +use crate::Web3Network; + +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, MaxEncodedLen, TypeInfo)] +pub enum Web3TokenType { + #[codec(index = 0)] + Bnb, + #[codec(index = 1)] + Eth, + #[codec(index = 2)] + SpaceId, + #[codec(index = 3)] + Lit, + #[codec(index = 4)] + Wbtc, + #[codec(index = 5)] + Usdc, + #[codec(index = 6)] + Usdt, + #[codec(index = 7)] + Crv, + #[codec(index = 8)] + Matic, + #[codec(index = 9)] + Dydx, + #[codec(index = 10)] + Amp, + #[codec(index = 11)] + Cvx, + #[codec(index = 12)] + Tusd, + #[codec(index = 13)] + Usdd, + #[codec(index = 14)] + Gusd, + #[codec(index = 15)] + Link, + #[codec(index = 16)] + Grt, + #[codec(index = 17)] + Comp, + #[codec(index = 18)] + People, + #[codec(index = 19)] + Gtc, + #[codec(index = 20)] + Ton, + #[codec(index = 21)] + Trx, +} + +impl Web3TokenType { + pub fn get_supported_networks(&self) -> Vec { + match self { + Self::Bnb | Self::Eth | Self::SpaceId | Self::Ton | Self::Trx => + vec![Web3Network::Bsc, Web3Network::Ethereum], + Self::Lit => vec![ + Web3Network::Bsc, + Web3Network::Ethereum, + Web3Network::Litentry, + Web3Network::Litmus, + ], + _ => vec![Web3Network::Ethereum], + } + } +} diff --git a/tee-worker/Cargo.lock b/tee-worker/Cargo.lock index 1b63c16d32..08913fa98f 100644 --- a/tee-worker/Cargo.lock +++ b/tee-worker/Cargo.lock @@ -6154,6 +6154,38 @@ dependencies = [ "url 2.1.1", ] +[[package]] +name = "lc-assertion-build-v2" +version = "0.1.0" +dependencies = [ + "env_logger 0.10.0", + "itc-rest-client", + "itp-ocall-api", + "itp-stf-primitives", + "itp-types", + "itp-utils", + "lc-assertion-build", + "lc-common", + "lc-credentials-v2", + "lc-mock-server", + "lc-service", + "lc-stf-task-sender", + "litentry-primitives", + "log 0.4.20", + "parity-scale-codec", + "sgx_tstd", + "thiserror 1.0.44", + "thiserror 1.0.9", +] + +[[package]] +name = "lc-common" +version = "0.1.0" +dependencies = [ + "litentry-primitives", + "sgx_tstd", +] + [[package]] name = "lc-credentials" version = "0.1.0" @@ -6184,6 +6216,24 @@ dependencies = [ "thiserror 1.0.9", ] +[[package]] +name = "lc-credentials-v2" +version = "0.1.0" +dependencies = [ + "itp-stf-primitives", + "itp-time-utils", + "itp-types", + "itp-utils", + "lc-common", + "lc-credentials", + "litentry-primitives", + "log 0.4.20", + "parity-scale-codec", + "sgx_tstd", + "thiserror 1.0.44", + "thiserror 1.0.9", +] + [[package]] name = "lc-data-providers" version = "0.1.0" @@ -6265,6 +6315,24 @@ dependencies = [ "thiserror 1.0.9", ] +[[package]] +name = "lc-service" +version = "0.1.0" +dependencies = [ + "itp-stf-primitives", + "itp-time-utils", + "itp-types", + "itp-utils", + "lc-common", + "lc-data-providers", + "litentry-primitives", + "log 0.4.20", + "parity-scale-codec", + "sgx_tstd", + "thiserror 1.0.44", + "thiserror 1.0.9", +] + [[package]] name = "lc-stf-task-receiver" version = "0.1.0" @@ -6292,6 +6360,7 @@ dependencies = [ "jsonrpc-core 18.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static", "lc-assertion-build", + "lc-assertion-build-v2", "lc-credentials", "lc-data-providers", "lc-identity-verification", @@ -6349,6 +6418,7 @@ dependencies = [ "itp-types", "itp-utils", "lc-assertion-build", + "lc-assertion-build-v2", "lc-credentials", "lc-data-providers", "lc-stf-task-receiver", diff --git a/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc.rs b/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc.rs index 64723c63dd..cd6f22afbe 100644 --- a/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc.rs +++ b/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc.rs @@ -30,7 +30,7 @@ use litentry_primitives::{ AchainableDate, AchainableDateInterval, AchainableDatePercent, AchainableParams, AchainableToken, Assertion, BoundedWeb3Network, ContestType, EVMTokenType, GenericDiscordRoleType, Identity, OneBlockCourseType, ParameterString, RequestAesKey, - SoraQuizType, VIP3MembershipCardLevel, Web3Network, REQUEST_AES_KEY_LEN, + SoraQuizType, VIP3MembershipCardLevel, Web3Network, Web3TokenType, REQUEST_AES_KEY_LEN, }; use sp_core::Pair; @@ -106,6 +106,8 @@ pub enum Command { CryptoSummary, LITStaking, BRC20AmountHolder, + #[clap(subcommand)] + TokenHoldingAmount(TokenHoldingAmountCommand), } #[derive(Args, Debug)] @@ -192,6 +194,32 @@ pub enum EVMAmountHoldingCommand { Trx, } +#[derive(Subcommand, Debug)] +pub enum TokenHoldingAmountCommand { + Bnb, + Eth, + SpaceId, + Lit, + Wbtc, + Usdc, + Usdt, + Crv, + Matic, + Dydx, + Amp, + Cvx, + Tusd, + Usdd, + Gusd, + Link, + Grt, + Comp, + People, + Gtc, + Ton, + Trx, +} + // I haven't found a good way to use common args for subcommands #[derive(Args, Debug)] pub struct AmountHoldingArg { @@ -480,6 +508,42 @@ impl RequestVcCommand { Command::CryptoSummary => Assertion::CryptoSummary, Command::LITStaking => Assertion::LITStaking, Command::BRC20AmountHolder => Assertion::BRC20AmountHolder, + Command::TokenHoldingAmount(arg) => match arg { + TokenHoldingAmountCommand::Bnb => Assertion::TokenHoldingAmount(Web3TokenType::Bnb), + TokenHoldingAmountCommand::Eth => Assertion::TokenHoldingAmount(Web3TokenType::Eth), + TokenHoldingAmountCommand::SpaceId => + Assertion::TokenHoldingAmount(Web3TokenType::SpaceId), + TokenHoldingAmountCommand::Lit => Assertion::TokenHoldingAmount(Web3TokenType::Lit), + TokenHoldingAmountCommand::Wbtc => + Assertion::TokenHoldingAmount(Web3TokenType::Wbtc), + TokenHoldingAmountCommand::Usdc => + Assertion::TokenHoldingAmount(Web3TokenType::Usdc), + TokenHoldingAmountCommand::Usdt => + Assertion::TokenHoldingAmount(Web3TokenType::Usdt), + TokenHoldingAmountCommand::Crv => Assertion::TokenHoldingAmount(Web3TokenType::Crv), + TokenHoldingAmountCommand::Matic => + Assertion::TokenHoldingAmount(Web3TokenType::Matic), + TokenHoldingAmountCommand::Dydx => + Assertion::TokenHoldingAmount(Web3TokenType::Dydx), + TokenHoldingAmountCommand::Amp => Assertion::TokenHoldingAmount(Web3TokenType::Amp), + TokenHoldingAmountCommand::Cvx => Assertion::TokenHoldingAmount(Web3TokenType::Cvx), + TokenHoldingAmountCommand::Tusd => + Assertion::TokenHoldingAmount(Web3TokenType::Tusd), + TokenHoldingAmountCommand::Usdd => + Assertion::TokenHoldingAmount(Web3TokenType::Usdd), + TokenHoldingAmountCommand::Gusd => + Assertion::TokenHoldingAmount(Web3TokenType::Gusd), + TokenHoldingAmountCommand::Link => + Assertion::TokenHoldingAmount(Web3TokenType::Link), + TokenHoldingAmountCommand::Grt => Assertion::TokenHoldingAmount(Web3TokenType::Grt), + TokenHoldingAmountCommand::Comp => + Assertion::TokenHoldingAmount(Web3TokenType::Comp), + TokenHoldingAmountCommand::People => + Assertion::TokenHoldingAmount(Web3TokenType::People), + TokenHoldingAmountCommand::Gtc => Assertion::TokenHoldingAmount(Web3TokenType::Gtc), + TokenHoldingAmountCommand::Ton => Assertion::TokenHoldingAmount(Web3TokenType::Ton), + TokenHoldingAmountCommand::Trx => Assertion::TokenHoldingAmount(Web3TokenType::Trx), + }, }; let key = Self::random_aes_key(); diff --git a/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc_direct.rs b/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc_direct.rs index 3decc2615e..06603a4881 100644 --- a/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc_direct.rs +++ b/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc_direct.rs @@ -31,7 +31,7 @@ use litentry_primitives::{ AchainableDate, AchainableDateInterval, AchainableDatePercent, AchainableParams, AchainableToken, Assertion, ContestType, EVMTokenType, GenericDiscordRoleType, Identity, OneBlockCourseType, RequestAesKey, SoraQuizType, VIP3MembershipCardLevel, Web3Network, - REQUEST_AES_KEY_LEN, + Web3TokenType, REQUEST_AES_KEY_LEN, }; use sp_core::Pair; @@ -257,6 +257,42 @@ impl RequestVcDirectCommand { Command::CryptoSummary => Assertion::CryptoSummary, Command::LITStaking => Assertion::LITStaking, Command::BRC20AmountHolder => Assertion::BRC20AmountHolder, + Command::TokenHoldingAmount(arg) => match arg { + TokenHoldingAmountCommand::Bnb => Assertion::TokenHoldingAmount(Web3TokenType::Bnb), + TokenHoldingAmountCommand::Eth => Assertion::TokenHoldingAmount(Web3TokenType::Eth), + TokenHoldingAmountCommand::SpaceId => + Assertion::TokenHoldingAmount(Web3TokenType::SpaceId), + TokenHoldingAmountCommand::Lit => Assertion::TokenHoldingAmount(Web3TokenType::Lit), + TokenHoldingAmountCommand::Wbtc => + Assertion::TokenHoldingAmount(Web3TokenType::Wbtc), + TokenHoldingAmountCommand::Usdc => + Assertion::TokenHoldingAmount(Web3TokenType::Usdc), + TokenHoldingAmountCommand::Usdt => + Assertion::TokenHoldingAmount(Web3TokenType::Usdt), + TokenHoldingAmountCommand::Crv => Assertion::TokenHoldingAmount(Web3TokenType::Crv), + TokenHoldingAmountCommand::Matic => + Assertion::TokenHoldingAmount(Web3TokenType::Matic), + TokenHoldingAmountCommand::Dydx => + Assertion::TokenHoldingAmount(Web3TokenType::Dydx), + TokenHoldingAmountCommand::Amp => Assertion::TokenHoldingAmount(Web3TokenType::Amp), + TokenHoldingAmountCommand::Cvx => Assertion::TokenHoldingAmount(Web3TokenType::Cvx), + TokenHoldingAmountCommand::Tusd => + Assertion::TokenHoldingAmount(Web3TokenType::Tusd), + TokenHoldingAmountCommand::Usdd => + Assertion::TokenHoldingAmount(Web3TokenType::Usdd), + TokenHoldingAmountCommand::Gusd => + Assertion::TokenHoldingAmount(Web3TokenType::Gusd), + TokenHoldingAmountCommand::Link => + Assertion::TokenHoldingAmount(Web3TokenType::Link), + TokenHoldingAmountCommand::Grt => Assertion::TokenHoldingAmount(Web3TokenType::Grt), + TokenHoldingAmountCommand::Comp => + Assertion::TokenHoldingAmount(Web3TokenType::Comp), + TokenHoldingAmountCommand::People => + Assertion::TokenHoldingAmount(Web3TokenType::People), + TokenHoldingAmountCommand::Gtc => Assertion::TokenHoldingAmount(Web3TokenType::Gtc), + TokenHoldingAmountCommand::Ton => Assertion::TokenHoldingAmount(Web3TokenType::Ton), + TokenHoldingAmountCommand::Trx => Assertion::TokenHoldingAmount(Web3TokenType::Trx), + }, }; let key: [u8; 32] = Self::random_aes_key(); diff --git a/tee-worker/client-api/parachain-api/prepare-build/interfaces/vc/definitions.ts b/tee-worker/client-api/parachain-api/prepare-build/interfaces/vc/definitions.ts index 71d11cd3f2..701fdf5377 100644 --- a/tee-worker/client-api/parachain-api/prepare-build/interfaces/vc/definitions.ts +++ b/tee-worker/client-api/parachain-api/prepare-build/interfaces/vc/definitions.ts @@ -31,6 +31,7 @@ export default { EVMAmountHolding: "EVMTokenType", BRC20AmountHolder: "Null", CyptoSummary: "Null", + TokenHoldingAmount: "Web3TokenType", }, }, AssertionSupportedNetwork: { @@ -145,5 +146,32 @@ export default { EVMTokenType: { _enum: ["Ton", "Trx"], }, + // Web3TokenType + Web3TokenType: { + _enum: [ + "Bnb", + "Eth", + "SpaceId", + "Lit", + "Wbtc", + "Usdc", + "Usdt", + "Crv", + "Matic", + "Dydx", + "Amp", + "Cvx", + "Tusd", + "Usdd", + "Gusd", + "Link", + "Grt", + "Comp", + "People", + "Gtc", + "Ton", + "Trx", + ], + }, }, }; diff --git a/tee-worker/enclave-runtime/Cargo.lock b/tee-worker/enclave-runtime/Cargo.lock index 4966a88d9a..1417f0067e 100644 --- a/tee-worker/enclave-runtime/Cargo.lock +++ b/tee-worker/enclave-runtime/Cargo.lock @@ -2917,6 +2917,35 @@ dependencies = [ "url", ] +[[package]] +name = "lc-assertion-build-v2" +version = "0.1.0" +dependencies = [ + "itc-rest-client", + "itp-ocall-api", + "itp-stf-primitives", + "itp-types", + "itp-utils", + "lc-assertion-build", + "lc-common", + "lc-credentials-v2", + "lc-service", + "lc-stf-task-sender", + "litentry-primitives", + "log", + "parity-scale-codec", + "sgx_tstd", + "thiserror", +] + +[[package]] +name = "lc-common" +version = "0.1.0" +dependencies = [ + "litentry-primitives", + "sgx_tstd", +] + [[package]] name = "lc-credentials" version = "0.1.0" @@ -2943,6 +2972,23 @@ dependencies = [ "thiserror", ] +[[package]] +name = "lc-credentials-v2" +version = "0.1.0" +dependencies = [ + "itp-stf-primitives", + "itp-time-utils", + "itp-types", + "itp-utils", + "lc-common", + "lc-credentials", + "litentry-primitives", + "log", + "parity-scale-codec", + "sgx_tstd", + "thiserror", +] + [[package]] name = "lc-data-providers" version = "0.1.0" @@ -2998,6 +3044,23 @@ dependencies = [ "thiserror", ] +[[package]] +name = "lc-service" +version = "0.1.0" +dependencies = [ + "itp-stf-primitives", + "itp-time-utils", + "itp-types", + "itp-utils", + "lc-common", + "lc-data-providers", + "litentry-primitives", + "log", + "parity-scale-codec", + "sgx_tstd", + "thiserror", +] + [[package]] name = "lc-stf-task-receiver" version = "0.1.0" @@ -3018,6 +3081,7 @@ dependencies = [ "itp-types", "itp-utils", "lc-assertion-build", + "lc-assertion-build-v2", "lc-credentials", "lc-data-providers", "lc-identity-verification", @@ -3070,6 +3134,7 @@ dependencies = [ "itp-types", "itp-utils", "lc-assertion-build", + "lc-assertion-build-v2", "lc-credentials", "lc-data-providers", "lc-stf-task-receiver", diff --git a/tee-worker/litentry/core/assertion-build-v2/Cargo.toml b/tee-worker/litentry/core/assertion-build-v2/Cargo.toml new file mode 100644 index 0000000000..7a9380d09b --- /dev/null +++ b/tee-worker/litentry/core/assertion-build-v2/Cargo.toml @@ -0,0 +1,62 @@ +[package] +authors = ["Trust Computing GmbH "] +edition = "2021" +name = "lc-assertion-build-v2" +version = "0.1.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +# std dependencies +thiserror = { version = "1.0.38", optional = true } + +# sgx dependencies +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true, features = ["net", "thread"] } +thiserror_sgx = { package = "thiserror", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3", optional = true } + +# no_std dependencies +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +log = { version = "0.4", default-features = false } + +# internal dependencies +itc-rest-client = { path = "../../../core/rest-client", default-features = false } +itp-ocall-api = { path = "../../../core-primitives/ocall-api", default-features = false } +itp-stf-primitives = { default-features = false, path = "../../../core-primitives/stf-primitives" } +itp-types = { path = "../../../core-primitives/types", default-features = false } +itp-utils = { path = "../../../core-primitives/utils", default-features = false } + +# litentry +lc-assertion-build = { path = "../assertion-build", default-features = false } +lc-common = { path = "../common", default-features = false } +lc-credentials-v2 = { path = "../credentials-v2", default-features = false } +lc-service = { path = "../service", default-features = false } +lc-stf-task-sender = { path = "../stf-task/sender", default-features = false } +litentry-primitives = { path = "../../primitives", default-features = false } + +[dev-dependencies] +env_logger = "0.10.0" +lc-mock-server = { path = "../mock-server" } + +[features] +default = ["std"] +sgx = [ + "sgx_tstd", + "thiserror_sgx", + "litentry-primitives/sgx", + "lc-common/sgx", + "lc-assertion-build/sgx", + "lc-credentials-v2/sgx", + "lc-service/sgx", + "lc-stf-task-sender/sgx", +] +std = [ + "log/std", + "itp-types/std", + "itp-utils/std", + "litentry-primitives/std", + "lc-common/std", + "lc-assertion-build/std", + "lc-credentials-v2/std", + "lc-service/std", + "lc-stf-task-sender/std", +] diff --git a/tee-worker/litentry/core/assertion-build-v2/src/lib.rs b/tee-worker/litentry/core/assertion-build-v2/src/lib.rs new file mode 100644 index 0000000000..4d4e798eac --- /dev/null +++ b/tee-worker/litentry/core/assertion-build-v2/src/lib.rs @@ -0,0 +1,44 @@ +// Copyright 2020-2024 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] +#![allow(clippy::result_large_err)] + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +#[macro_use] +extern crate sgx_tstd as std; + +// re-export module to properly feature gate sgx and regular std environment +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod sgx_reexport_prelude { + pub use thiserror_sgx as thiserror; +} + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +use std::{string::String, vec::Vec}; + +use litentry_primitives::{ + Assertion, ErrorDetail, ErrorString, IntoErrorDetail, VCMPError as Error, +}; + +// TODO migration to v2 in the future +use lc_assertion_build::{transpose_identity, Result}; +use lc_service::DataProviderConfig; +use log::*; + +pub mod token_holding_amount; diff --git a/tee-worker/litentry/core/assertion-build-v2/src/token_holding_amount/mod.rs b/tee-worker/litentry/core/assertion-build-v2/src/token_holding_amount/mod.rs new file mode 100644 index 0000000000..2cae98d3ac --- /dev/null +++ b/tee-worker/litentry/core/assertion-build-v2/src/token_holding_amount/mod.rs @@ -0,0 +1,507 @@ +// Copyright 2020-2024 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +use lc_credentials_v2::{token_holding_amount::TokenHoldingAmountAssertionUpdate, Credential}; +use lc_service::web3_token::token_balance::get_token_balance; +use lc_stf_task_sender::AssertionBuildRequest; +use litentry_primitives::{Web3Network, Web3TokenType}; +use log::debug; + +use crate::*; + +pub fn build( + req: &AssertionBuildRequest, + token_type: Web3TokenType, + data_provider_config: &DataProviderConfig, +) -> Result { + debug!("token holding amount: {:?}", token_type); + + let identities: Vec<(Web3Network, Vec)> = transpose_identity(&req.identities); + let addresses = identities + .into_iter() + .flat_map(|(newtwork_type, addresses)| { + addresses.into_iter().map(move |address| (newtwork_type, address)) + }) + .collect::>(); + + let result = + get_token_balance(token_type.clone(), addresses, data_provider_config).map_err(|e| { + Error::RequestVCFailed( + Assertion::TokenHoldingAmount(token_type.clone()), + ErrorDetail::DataProviderError(ErrorString::truncate_from( + format!("{e:?}").as_bytes().to_vec(), + )), + ) + }); + + match result { + Ok(value) => match Credential::new(&req.who, &req.shard) { + Ok(mut credential_unsigned) => { + credential_unsigned.update_token_holding_amount_assertion(token_type, value); + Ok(credential_unsigned) + }, + Err(e) => { + error!("Generate unsigned credential failed {:?}", e); + Err(Error::RequestVCFailed( + Assertion::TokenHoldingAmount(token_type), + e.into_error_detail(), + )) + }, + }, + Err(e) => Err(e), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use itp_stf_primitives::types::ShardIdentifier; + use itp_types::AccountId; + use itp_utils::hex::decode_hex; + use lc_common::web3_token::{TokenAddress, TokenName}; + use lc_credentials_v2::assertion_logic::{AssertionLogic, Op}; + use lc_mock_server::run; + use litentry_primitives::{Identity, IdentityNetworkTuple}; + + fn crate_assertion_build_request( + token_type: Web3TokenType, + identities: Vec, + ) -> AssertionBuildRequest { + AssertionBuildRequest { + shard: ShardIdentifier::default(), + signer: AccountId::from([0; 32]), + who: AccountId::from([0; 32]).into(), + assertion: Assertion::TokenHoldingAmount(token_type), + identities, + top_hash: Default::default(), + parachain_block_number: 0u32, + sidechain_block_number: 0u32, + maybe_key: None, + should_create_id_graph: false, + req_ext_hash: Default::default(), + } + } + + fn create_token_assertion_logic(token_type: Web3TokenType) -> Box { + Box::new(AssertionLogic::Item { + src: "$token".into(), + op: Op::Equal, + dst: token_type.get_token_name().into(), + }) + } + + fn create_bsc_assertion_logic() -> Box { + Box::new(AssertionLogic::Or { + items: vec![ + Box::new(AssertionLogic::And { + items: vec![Box::new(AssertionLogic::Item { + src: "$network".into(), + op: Op::Equal, + dst: "bsc".into(), + })], + }), + Box::new(AssertionLogic::And { + items: vec![ + Box::new(AssertionLogic::Item { + src: "$network".into(), + op: Op::Equal, + dst: "ethereum".into(), + }), + Box::new(AssertionLogic::Item { + src: "$address".into(), + op: Op::Equal, + dst: Web3TokenType::Bnb + .get_token_address(Web3Network::Ethereum) + .unwrap() + .into(), + }), + ], + }), + ], + }) + } + + fn create_eth_assertion_logic() -> Box { + Box::new(AssertionLogic::Or { + items: vec![ + Box::new(AssertionLogic::And { + items: vec![ + Box::new(AssertionLogic::Item { + src: "$network".into(), + op: Op::Equal, + dst: "bsc".into(), + }), + Box::new(AssertionLogic::Item { + src: "$address".into(), + op: Op::Equal, + dst: Web3TokenType::Eth + .get_token_address(Web3Network::Bsc) + .unwrap() + .into(), + }), + ], + }), + Box::new(AssertionLogic::And { + items: vec![Box::new(AssertionLogic::Item { + src: "$network".into(), + op: Op::Equal, + dst: "ethereum".into(), + })], + }), + ], + }) + } + + fn create_evm_assertion_logic(token_type: Web3TokenType) -> Box { + Box::new(AssertionLogic::Or { + items: vec![ + Box::new(AssertionLogic::And { + items: vec![ + Box::new(AssertionLogic::Item { + src: "$network".into(), + op: Op::Equal, + dst: "bsc".into(), + }), + Box::new(AssertionLogic::Item { + src: "$address".into(), + op: Op::Equal, + dst: token_type.get_token_address(Web3Network::Bsc).unwrap().into(), + }), + ], + }), + Box::new(AssertionLogic::And { + items: vec![ + Box::new(AssertionLogic::Item { + src: "$network".into(), + op: Op::Equal, + dst: "ethereum".into(), + }), + Box::new(AssertionLogic::Item { + src: "$address".into(), + op: Op::Equal, + dst: token_type + .get_token_address(Web3Network::Ethereum) + .unwrap() + .into(), + }), + ], + }), + ], + }) + } + + fn create_ethereum_assertion_logic(token_type: Web3TokenType) -> Box { + Box::new(AssertionLogic::Or { + items: vec![Box::new(AssertionLogic::And { + items: vec![ + Box::new(AssertionLogic::Item { + src: "$network".into(), + op: Op::Equal, + dst: "ethereum".into(), + }), + Box::new(AssertionLogic::Item { + src: "$address".into(), + op: Op::Equal, + dst: token_type.get_token_address(Web3Network::Ethereum).unwrap().into(), + }), + ], + })], + }) + } + + fn create_lit_assertion_logic() -> Box { + Box::new(AssertionLogic::Or { + items: vec![ + Box::new(AssertionLogic::And { + items: vec![ + Box::new(AssertionLogic::Item { + src: "$network".into(), + op: Op::Equal, + dst: "bsc".into(), + }), + Box::new(AssertionLogic::Item { + src: "$address".into(), + op: Op::Equal, + dst: Web3TokenType::Lit + .get_token_address(Web3Network::Bsc) + .unwrap() + .into(), + }), + ], + }), + Box::new(AssertionLogic::And { + items: vec![ + Box::new(AssertionLogic::Item { + src: "$network".into(), + op: Op::Equal, + dst: "ethereum".into(), + }), + Box::new(AssertionLogic::Item { + src: "$address".into(), + op: Op::Equal, + dst: Web3TokenType::Lit + .get_token_address(Web3Network::Ethereum) + .unwrap() + .into(), + }), + ], + }), + Box::new(AssertionLogic::And { + items: vec![Box::new(AssertionLogic::Item { + src: "$network".into(), + op: Op::Equal, + dst: "litentry".into(), + })], + }), + Box::new(AssertionLogic::And { + items: vec![Box::new(AssertionLogic::Item { + src: "$network".into(), + op: Op::Equal, + dst: "litmus".into(), + })], + }), + ], + }) + } + + fn init() -> DataProviderConfig { + let _ = env_logger::builder().is_test(true).try_init(); + let url = run(0).unwrap(); + + let mut data_provider_config = DataProviderConfig::default(); + + data_provider_config.set_nodereal_api_key("d416f55179dbd0e45b1a8ed030e3".into()); + data_provider_config.set_nodereal_api_chain_network_url(url.clone() + "/nodereal_jsonrpc/"); + data_provider_config.set_achainable_url(url.clone()); + data_provider_config + } + + #[test] + fn build_bnb_holding_amount_works() { + let data_provider_config = init(); + let address = decode_hex("0x45cdb67696802b9d01ed156b883269dbdb9c6239".as_bytes().to_vec()) + .unwrap() + .as_slice() + .try_into() + .unwrap(); + let identities: Vec = + vec![(Identity::Evm(address), vec![Web3Network::Bsc, Web3Network::Ethereum])]; + + let req = crate_assertion_build_request(Web3TokenType::Bnb, identities); + + match build(&req, Web3TokenType::Bnb, &data_provider_config) { + Ok(credential) => { + log::info!("build bnb TokenHoldingAmount done"); + assert_eq!( + *(credential.credential_subject.assertions.first().unwrap()), + AssertionLogic::And { + items: vec![ + create_token_assertion_logic(Web3TokenType::Bnb), + create_bsc_assertion_logic(), + Box::new(AssertionLogic::Item { + src: "$holding_amount".into(), + op: Op::GreaterEq, + dst: "50".into() + }), + Box::new(AssertionLogic::Item { + src: "$holding_amount".into(), + op: Op::LessThan, + dst: "100".into() + }) + ] + } + ); + assert_eq!(*(credential.credential_subject.values.first().unwrap()), true); + }, + Err(e) => { + panic!("build bnb TokenHoldingAmount failed with error {:?}", e); + }, + } + } + + #[test] + fn build_eth_holding_amount_works() { + let data_provider_config = init(); + let identities: Vec = + vec![(Identity::Evm([0; 20].into()), vec![Web3Network::Ethereum])]; + + let req = crate_assertion_build_request(Web3TokenType::Eth, identities); + + match build(&req, Web3TokenType::Eth, &data_provider_config) { + Ok(credential) => { + log::info!("build eth TokenHoldingAmount done"); + assert_eq!( + *(credential.credential_subject.assertions.first().unwrap()), + AssertionLogic::And { + items: vec![ + create_token_assertion_logic(Web3TokenType::Eth), + create_eth_assertion_logic(), + Box::new(AssertionLogic::Item { + src: "$holding_amount".into(), + op: Op::GreaterEq, + dst: "1".into() + }), + Box::new(AssertionLogic::Item { + src: "$holding_amount".into(), + op: Op::LessThan, + dst: "50".into() + }) + ] + } + ); + assert_eq!(*(credential.credential_subject.values.first().unwrap()), true); + }, + Err(e) => { + panic!("build eth TokenHoldingAmount failed with error {:?}", e); + }, + } + } + + #[test] + fn build_evm_holding_amount_works() { + let data_provider_config = init(); + let address = decode_hex("0x75438d34c9125839c8b08d21b7f3167281659e7c".as_bytes().to_vec()) + .unwrap() + .as_slice() + .try_into() + .unwrap(); + let identities: Vec = + vec![(Identity::Evm(address), vec![Web3Network::Bsc, Web3Network::Ethereum])]; + + let req = crate_assertion_build_request(Web3TokenType::SpaceId, identities); + + match build(&req, Web3TokenType::SpaceId, &data_provider_config) { + Ok(credential) => { + log::info!("build evm TokenHoldingAmount done"); + assert_eq!( + *(credential.credential_subject.assertions.first().unwrap()), + AssertionLogic::And { + items: vec![ + create_token_assertion_logic(Web3TokenType::SpaceId), + create_evm_assertion_logic(Web3TokenType::SpaceId), + Box::new(AssertionLogic::Item { + src: "$holding_amount".into(), + op: Op::GreaterEq, + dst: "800".into() + }), + Box::new(AssertionLogic::Item { + src: "$holding_amount".into(), + op: Op::LessThan, + dst: "1200".into() + }) + ] + } + ); + assert_eq!(*(credential.credential_subject.values.first().unwrap()), true); + }, + Err(e) => { + panic!("build evm TokenHoldingAmount failed with error {:?}", e); + }, + } + } + + #[test] + fn build_ethereum_holding_amount_works() { + let data_provider_config = init(); + let address = decode_hex("0x75438d34c9125839c8b08d21b7f3167281659e7c".as_bytes().to_vec()) + .unwrap() + .as_slice() + .try_into() + .unwrap(); + let identities: Vec = + vec![(Identity::Evm(address), vec![Web3Network::Ethereum])]; + + let req = crate_assertion_build_request(Web3TokenType::Amp, identities); + + match build(&req, Web3TokenType::Amp, &data_provider_config) { + Ok(credential) => { + log::info!("build ethereum TokenHoldingAmount done"); + assert_eq!( + *(credential.credential_subject.assertions.first().unwrap()), + AssertionLogic::And { + items: vec![ + create_token_assertion_logic(Web3TokenType::Amp), + create_ethereum_assertion_logic(Web3TokenType::Amp), + Box::new(AssertionLogic::Item { + src: "$holding_amount".into(), + op: Op::GreaterEq, + dst: "200".into() + }), + Box::new(AssertionLogic::Item { + src: "$holding_amount".into(), + op: Op::LessThan, + dst: "500".into() + }) + ] + } + ); + assert_eq!(*(credential.credential_subject.values.first().unwrap()), true); + }, + Err(e) => { + panic!("build ethereum TokenHoldingAmount failed with error {:?}", e); + }, + } + } + + #[test] + fn build_lit_holding_amount_works() { + let data_provider_config = init(); + let address = decode_hex("0xba359c153ad11aa17c3122b05a4db8b46bb3191b".as_bytes().to_vec()) + .unwrap() + .as_slice() + .try_into() + .unwrap(); + let identities: Vec = + vec![(Identity::Evm(address), vec![Web3Network::Ethereum, Web3Network::Litentry])]; + + let req = crate_assertion_build_request(Web3TokenType::Lit, identities); + + match build(&req, Web3TokenType::Lit, &data_provider_config) { + Ok(credential) => { + log::info!("build lit TokenHoldingAmount done"); + assert_eq!( + *(credential.credential_subject.assertions.first().unwrap()), + AssertionLogic::And { + items: vec![ + create_token_assertion_logic(Web3TokenType::Lit), + create_lit_assertion_logic(), + Box::new(AssertionLogic::Item { + src: "$holding_amount".into(), + op: Op::GreaterEq, + dst: "1600".into() + }), + Box::new(AssertionLogic::Item { + src: "$holding_amount".into(), + op: Op::LessThan, + dst: "3000".into() + }) + ] + } + ); + assert_eq!(*(credential.credential_subject.values.first().unwrap()), true); + }, + Err(e) => { + panic!("build lit TokenHoldingAmount failed with error {:?}", e); + }, + } + } +} diff --git a/tee-worker/litentry/core/assertion-build/src/nodereal/amount_holding/evm_amount_holding.rs b/tee-worker/litentry/core/assertion-build/src/nodereal/amount_holding/evm_amount_holding.rs index 99756dc61a..85489e80b6 100644 --- a/tee-worker/litentry/core/assertion-build/src/nodereal/amount_holding/evm_amount_holding.rs +++ b/tee-worker/litentry/core/assertion-build/src/nodereal/amount_holding/evm_amount_holding.rs @@ -44,7 +44,7 @@ fn get_holding_balance( ) -> result::Result { let mut eth_client = NoderealJsonrpcClient::new(NoderealChain::Eth, data_provider_config); let mut bsc_client = NoderealJsonrpcClient::new(NoderealChain::Bsc, data_provider_config); - let mut total_balance = 0_f64; + let mut total_balance = 0_u128; let decimals = token_type.get_decimals(); @@ -71,7 +71,8 @@ fn get_holding_balance( } } - Ok(total_balance / decimals) + Ok((total_balance / decimals as u128) as f64 + + ((total_balance % decimals as u128) as f64 / decimals)) } pub fn build( diff --git a/tee-worker/litentry/core/common/Cargo.toml b/tee-worker/litentry/core/common/Cargo.toml new file mode 100644 index 0000000000..a3cbf6f988 --- /dev/null +++ b/tee-worker/litentry/core/common/Cargo.toml @@ -0,0 +1,23 @@ +[package] +edition = "2021" +name = "lc-common" +version = "0.1.0" + +[dependencies] +# std dependencies + +# sgx dependencies +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true, features = ["net", "thread"] } + +# Internal dependencies +litentry-primitives = { path = "../../primitives", default-features = false } + +[features] +default = ["std"] +sgx = [ + "sgx_tstd", + "litentry-primitives/sgx", +] +std = [ + "litentry-primitives/std", +] diff --git a/tee-worker/litentry/core/common/src/lib.rs b/tee-worker/litentry/core/common/src/lib.rs new file mode 100644 index 0000000000..cfaa59582d --- /dev/null +++ b/tee-worker/litentry/core/common/src/lib.rs @@ -0,0 +1,45 @@ +// Copyright 2020-2024 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +use litentry_primitives::Web3Network; + +pub mod web3_token; + +pub fn web3_network_to_chain(network: &Web3Network) -> &'static str { + match network { + Web3Network::Polkadot => "polkadot", + Web3Network::Kusama => "kusama", + Web3Network::Litentry => "litentry", + Web3Network::Litmus => "litmus", + Web3Network::LitentryRococo => "litentry_rococo", + Web3Network::Khala => "khala", + Web3Network::Ethereum => "ethereum", + Web3Network::Bsc => "bsc", + Web3Network::BitcoinP2tr => "bitcoin_p2tr", + Web3Network::BitcoinP2pkh => "bitcoin_p2pkh", + Web3Network::BitcoinP2sh => "bitcoin_p2sh", + Web3Network::BitcoinP2wpkh => "bitcoin_p2wpkh", + Web3Network::BitcoinP2wsh => "bitcoin_p2wsh", + } +} diff --git a/tee-worker/litentry/core/common/src/web3_token/mod.rs b/tee-worker/litentry/core/common/src/web3_token/mod.rs new file mode 100644 index 0000000000..cee37495ac --- /dev/null +++ b/tee-worker/litentry/core/common/src/web3_token/mod.rs @@ -0,0 +1,206 @@ +// Copyright 2020-2024 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +use litentry_primitives::Web3TokenType; + +use crate::Web3Network; + +pub trait TokenName { + fn get_token_name(&self) -> &'static str; +} + +impl TokenName for Web3TokenType { + fn get_token_name(&self) -> &'static str { + match self { + Self::Bnb => "BNB", + Self::Eth => "ETH", + Self::SpaceId => "SPACE_ID", + Self::Lit => "LIT", + Self::Wbtc => "WBTC", + Self::Usdc => "USDC", + Self::Usdt => "USDT", + Self::Crv => "CRV", + Self::Matic => "MATIC", + Self::Dydx => "DYDX", + Self::Amp => "AMP", + Self::Cvx => "CVX", + Self::Tusd => "TUSD", + Self::Usdd => "USDD", + Self::Gusd => "GUSD", + Self::Link => "LINK", + Self::Grt => "GRT", + Self::Comp => "COMP", + Self::People => "PEOPLE", + Self::Gtc => "GTC", + Self::Ton => "TON", + Self::Trx => "TRX", + } + } +} + +pub trait TokenAddress { + fn get_token_address(&self, network: Web3Network) -> Option<&'static str>; +} + +impl TokenAddress for Web3TokenType { + fn get_token_address(&self, network: Web3Network) -> Option<&'static str> { + match (self, network) { + // Bnb + (Self::Bnb, Web3Network::Ethereum) => + Some("0xb8c77482e45f1f44de1745f52c74426c631bdd52"), + // Eth + (Self::Eth, Web3Network::Bsc) => Some("0x2170ed0880ac9a755fd29b2688956bd959f933f8"), + // SpaceId + (Self::SpaceId, Web3Network::Bsc) | (Self::SpaceId, Web3Network::Ethereum) => + Some("0x2dff88a56767223a5529ea5960da7a3f5f766406"), + // Lit + (Self::Lit, Web3Network::Bsc) | (Self::Lit, Web3Network::Ethereum) => + Some("0xb59490ab09a0f526cc7305822ac65f2ab12f9723"), + // Wbtc + (Self::Wbtc, Web3Network::Ethereum) => + Some("0x2260fac5e5542a773aa44fbcfedf7c193bc2c599"), + // Usdc + (Self::Usdc, Web3Network::Bsc) => Some("0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d"), + (Self::Usdc, Web3Network::Ethereum) => + Some("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"), + // Usdt + (Self::Usdt, Web3Network::Bsc) => Some("0x55d398326f99059ff775485246999027b3197955"), + (Self::Usdt, Web3Network::Ethereum) => + Some("0xdac17f958d2ee523a2206206994597c13d831ec7"), + // Crv + (Self::Crv, Web3Network::Ethereum) => + Some("0xd533a949740bb3306d119cc777fa900ba034cd52"), + // Matic + (Self::Matic, Web3Network::Bsc) => Some("0xcc42724c6683b7e57334c4e856f4c9965ed682bd"), + (Self::Matic, Web3Network::Ethereum) => + Some("0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0"), + // Dydx + (Self::Dydx, Web3Network::Ethereum) => + Some("0x92d6c1e31e14520e676a687f0a93788b716beff5"), + // Amp + (Self::Amp, Web3Network::Ethereum) => + Some("0xff20817765cb7f73d4bde2e66e067e58d11095c2"), + // Cvx + (Self::Cvx, Web3Network::Ethereum) => + Some("0x4e3fbd56cd56c3e72c1403e103b45db9da5b9d2b"), + // Tusd + (Self::Tusd, Web3Network::Bsc) => Some("0x40af3827F39D0EAcBF4A168f8D4ee67c121D11c9"), + (Self::Tusd, Web3Network::Ethereum) => + Some("0x0000000000085d4780b73119b644ae5ecd22b376"), + // Usdd + (Self::Usdd, Web3Network::Bsc) => Some("0xd17479997f34dd9156deef8f95a52d81d265be9c"), + (Self::Usdd, Web3Network::Ethereum) => + Some("0x0c10bf8fcb7bf5412187a595ab97a3609160b5c6"), + // Gusd + (Self::Gusd, Web3Network::Ethereum) => + Some("0x056fd409e1d7a124bd7017459dfea2f387b6d5cd"), + // Link + (Self::Link, Web3Network::Bsc) => Some("0xf8a0bf9cf54bb92f17374d9e9a321e6a111a51bd"), + (Self::Link, Web3Network::Ethereum) => + Some("0x514910771af9ca656af840dff83e8264ecf986ca"), + // Grt + (Self::Grt, Web3Network::Bsc) => Some("0x52ce071bd9b1c4b00a0b92d298c512478cad67e8"), + (Self::Grt, Web3Network::Ethereum) => + Some("0xc944e90c64b2c07662a292be6244bdf05cda44a7"), + // Comp + (Self::Comp, Web3Network::Ethereum) => + Some("0xc00e94cb662c3520282e6f5717214004a7f26888"), + // People + (Self::People, Web3Network::Ethereum) => + Some("0x7a58c0be72be218b41c608b7fe7c5bb630736c71"), + // Gtc + (Self::Gtc, Web3Network::Ethereum) => + Some("0xde30da39c46104798bb5aa3fe8b9e0e1f348163f"), + // Ton + (Self::Ton, Web3Network::Bsc) => Some("0x76a797a59ba2c17726896976b7b3747bfd1d220f"), + (Self::Ton, Web3Network::Ethereum) => + Some("0x582d872a1b094fc48f5de31d3b73f2d9be47def1"), + // Trx + (Self::Trx, Web3Network::Bsc) => Some("0xCE7de646e7208a4Ef112cb6ed5038FA6cC6b12e3"), + (Self::Trx, Web3Network::Ethereum) => + Some("0x50327c6c5a14dcade707abad2e27eb517df87ab5"), + _ => None, + } + } +} + +pub trait TokenDecimals { + fn get_decimals(&self, network: Web3Network) -> u64; +} + +impl TokenDecimals for Web3TokenType { + fn get_decimals(&self, network: Web3Network) -> u64 { + let decimals = match (self, network) { + // Bnb + (Self::Bnb, Web3Network::Bsc) | (Self::Bnb, Web3Network::Ethereum) | + // Eth + (Self::Eth, Web3Network::Bsc) | (Self::Eth, Web3Network::Ethereum) | + // SpaceId + (Self::SpaceId, Web3Network::Bsc) | (Self::SpaceId, Web3Network::Ethereum) | + // Lit + (Self::Lit, Web3Network::Bsc) | (Self::Lit, Web3Network::Ethereum) | + // Usdc + (Self::Usdc, Web3Network::Bsc) | + // Usdt + (Self::Usdt, Web3Network::Bsc) | + // Crv + (Self::Crv, Web3Network::Ethereum) | + // Matic + (Self::Matic, Web3Network::Bsc) | (Self::Matic, Web3Network::Ethereum) | + // Dydx + (Self::Dydx, Web3Network::Ethereum) | + // Amp + (Self::Amp, Web3Network::Ethereum) | + // Cvx + (Self::Cvx, Web3Network::Ethereum) | + // Tusd + (Self::Tusd, Web3Network::Bsc) | (Self::Tusd, Web3Network::Ethereum) | + // Usdd + (Self::Usdd, Web3Network::Bsc) | (Self::Usdd, Web3Network::Ethereum) | + // Link + (Self::Link, Web3Network::Bsc) | (Self::Link, Web3Network::Ethereum) | + // Grt + (Self::Grt, Web3Network::Bsc) | (Self::Grt, Web3Network::Ethereum) | + // Comp + (Self::Comp, Web3Network::Ethereum) | + // People + (Self::People, Web3Network::Ethereum) | + // Gtc + (Self::Gtc, Web3Network::Ethereum) => 18, + // Ton + (Self::Ton, Web3Network::Bsc) | (Self::Ton, Web3Network::Ethereum) => 9, + // Wbtc + (Self::Wbtc, Web3Network::Bsc) | (Self::Wbtc, Web3Network::Ethereum) => 8, + // Usdc + (Self::Usdc, Web3Network::Ethereum) | + // Usdt + (Self::Usdt, Web3Network::Ethereum) | + // Trx + (Self::Trx, Web3Network::Bsc) | (Self::Trx, Web3Network::Ethereum) => 6, + // Gusd + (Self::Gusd, Web3Network::Ethereum) => 2, + _ => 1, + }; + + 10_u64.pow(decimals) + } +} diff --git a/tee-worker/litentry/core/credentials-v2/Cargo.toml b/tee-worker/litentry/core/credentials-v2/Cargo.toml new file mode 100644 index 0000000000..dd8a690c21 --- /dev/null +++ b/tee-worker/litentry/core/credentials-v2/Cargo.toml @@ -0,0 +1,49 @@ +[package] +authors = ["Trust Computing GmbH "] +edition = "2021" +name = "lc-credentials-v2" +version = "0.1.0" + +[dependencies] +# std dependencies +thiserror = { version = "1.0.38", optional = true } + +# sgx dependencies +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true, features = ["net", "thread"] } +thiserror_sgx = { package = "thiserror", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3", optional = true } + +# no_std dependencies +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +log = { version = "0.4", default-features = false } + +# internal dependencies +itp-stf-primitives = { default-features = false, path = "../../../core-primitives/stf-primitives" } +itp-time-utils = { path = "../../../core-primitives/time-utils", default-features = false } +itp-types = { path = "../../../core-primitives/types", default-features = false } +itp-utils = { path = "../../../core-primitives/utils", default-features = false } + +# litentry +lc-common = { path = "../common", default-features = false } +lc-credentials = { path = "../credentials", default-features = false } +litentry-primitives = { path = "../../primitives", default-features = false } + +[features] +default = ["std"] +sgx = [ + "sgx_tstd", + "thiserror_sgx", + "litentry-primitives/sgx", + "itp-time-utils/sgx", + "lc-common/sgx", + "lc-credentials/sgx", +] +std = [ + "log/std", + "thiserror", + "itp-types/std", + "itp-utils/std", + "litentry-primitives/std", + "itp-time-utils/std", + "lc-common/std", + "lc-credentials/std", +] diff --git a/tee-worker/litentry/core/credentials-v2/src/lib.rs b/tee-worker/litentry/core/credentials-v2/src/lib.rs new file mode 100644 index 0000000000..4bdd90bb13 --- /dev/null +++ b/tee-worker/litentry/core/credentials-v2/src/lib.rs @@ -0,0 +1,39 @@ +// Copyright 2020-2024 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . +// +// TEE Implementation of Verifiable Credentials Data Model v2.0 +// W3C Editor's Draft 07 January 2023 +// https://w3c.github.io/vc-data-model + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +#[macro_use] +extern crate sgx_tstd as std; + +// re-export module to properly feature gate sgx and regular std environment +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod sgx_reexport_prelude { + pub use thiserror_sgx as thiserror; +} + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +// TODO migration to v2 in the future +pub use lc_credentials::{assertion_logic, Credential}; + +pub mod token_holding_amount; diff --git a/tee-worker/litentry/core/credentials-v2/src/token_holding_amount/mod.rs b/tee-worker/litentry/core/credentials-v2/src/token_holding_amount/mod.rs new file mode 100644 index 0000000000..b2d95dbe2a --- /dev/null +++ b/tee-worker/litentry/core/credentials-v2/src/token_holding_amount/mod.rs @@ -0,0 +1,133 @@ +// Copyright 2020-2024 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +use lc_common::{ + web3_network_to_chain, + web3_token::{TokenAddress, TokenName}, +}; +use litentry_primitives::{Web3Network, Web3TokenType}; + +// TODO migration to v2 in the future +use lc_credentials::{ + assertion_logic::{AssertionLogic, Op}, + litentry_profile::{BalanceRange, BalanceRangeIndex}, + Credential, +}; + +const TOKEN_HOLDING_AMOUNT_RANGE: [f64; 10] = + [0.0, 1.0, 50.0, 100.0, 200.0, 500.0, 800.0, 1200.0, 1600.0, 3000.0]; + +const TYPE: &str = "Token Holding Amount"; +const DESCRIPTION: &str = "The amount of a particular token you are holding"; + +struct AssertionKeys { + token: &'static str, + network: &'static str, + address: &'static str, + holding_amount: &'static str, +} + +const ASSERTION_KEYS: AssertionKeys = AssertionKeys { + token: "$token", + network: "$network", + address: "$address", + holding_amount: "$holding_amount", +}; + +pub trait TokenHoldingAmountAssertionUpdate { + fn update_token_holding_amount_assertion(&mut self, token_type: Web3TokenType, amount: f64); +} + +impl TokenHoldingAmountAssertionUpdate for Credential { + fn update_token_holding_amount_assertion(&mut self, token_type: Web3TokenType, amount: f64) { + self.add_subject_info(DESCRIPTION, TYPE); + + update_assertion(token_type, amount, self); + } +} + +fn update_assertion(token_type: Web3TokenType, balance: f64, credential: &mut Credential) { + let mut assertion = AssertionLogic::new_and(); + + assertion = assertion.add_item(AssertionLogic::new_item( + ASSERTION_KEYS.token, + Op::Equal, + token_type.get_token_name(), + )); + + let mut network_assertion: AssertionLogic = AssertionLogic::new_or(); + for newtork in token_type.get_supported_networks() { + network_assertion = + network_assertion.add_item(create_network_assertion_logic(newtork, token_type.clone())); + } + + assertion = assertion.add_item(network_assertion); + + let index = BalanceRange::index(&TOKEN_HOLDING_AMOUNT_RANGE, balance); + match index { + Some(index) => { + let min = format!("{}", &TOKEN_HOLDING_AMOUNT_RANGE[index]); + let max = format!("{}", &TOKEN_HOLDING_AMOUNT_RANGE[index + 1]); + let min_item = + AssertionLogic::new_item(ASSERTION_KEYS.holding_amount, Op::GreaterEq, &min); + let max_item = + AssertionLogic::new_item(ASSERTION_KEYS.holding_amount, Op::LessThan, &max); + + assertion = assertion.add_item(min_item); + assertion = assertion.add_item(max_item); + + credential.credential_subject.values.push(index != 0); + }, + None => { + let min_item = AssertionLogic::new_item( + ASSERTION_KEYS.holding_amount, + Op::GreaterEq, + &format!("{}", &TOKEN_HOLDING_AMOUNT_RANGE.last().unwrap()), + ); + assertion = assertion.add_item(min_item); + + credential.credential_subject.values.push(true); + }, + } + + credential.credential_subject.assertions.push(assertion); +} + +fn create_network_assertion_logic( + network: Web3Network, + token_type: Web3TokenType, +) -> AssertionLogic { + let mut assertion = AssertionLogic::new_and(); + assertion = assertion.add_item(AssertionLogic::new_item( + ASSERTION_KEYS.network, + Op::Equal, + web3_network_to_chain(&network), + )); + if let Some(address) = token_type.get_token_address(network) { + assertion = assertion.add_item(AssertionLogic::new_item( + ASSERTION_KEYS.address, + Op::Equal, + address, + )); + } + assertion +} diff --git a/tee-worker/litentry/core/data-providers/src/lib.rs b/tee-worker/litentry/core/data-providers/src/lib.rs index 030f4c66df..f8bc98526b 100644 --- a/tee-worker/litentry/core/data-providers/src/lib.rs +++ b/tee-worker/litentry/core/data-providers/src/lib.rs @@ -481,28 +481,56 @@ impl ConvertParameterString for AchainableParams { } } -pub fn hex_to_decimal(hex_string: &str) -> f64 { - let parts: Vec<&str> = hex_string.split('.').collect(); - - let integer_part = u64::from_str_radix(parts[0], 16).unwrap_or_default(); - - if parts.len() > 1 { - let decimal_part = u64::from_str_radix(parts[1], 16).unwrap(); - let decimal_str = format!("{}.{}", integer_part, decimal_part); - decimal_str.parse::().unwrap_or_default() - } else { - integer_part as f64 +pub fn convert_balance_hex_to_u128(result: serde_json::Value) -> Result { + match result.as_str() { + Some(result) => match u128::from_str_radix(&result[2..], 16) { + Ok(balance) => Ok(balance), + Err(_) => Err(Error::RequestError(format!("Cannot parse result {:?} to u128", result))), + }, + None => Err(Error::RequestError(format!("Cannot tansform result {:?} to &str", result))), } } #[cfg(test)] mod tests { - use crate::hex_to_decimal; + use super::*; + + #[test] + fn should_return_correct_value_when_param_is_valid() { + assert_eq!( + convert_balance_hex_to_u128(serde_json::Value::String("0x0".into())).unwrap(), + 0_u128 + ); + + assert_eq!( + convert_balance_hex_to_u128(serde_json::Value::String("0x320".into())).unwrap(), + 800_u128 + ); + + assert_eq!( + convert_balance_hex_to_u128(serde_json::Value::String("0x2b5e3af16b1880000".into())) + .unwrap(), + 50_000_000_000_000_000_000_u128 + ); + } + + #[test] + fn shoud_return_error_when_param_is_not_a_str() { + match convert_balance_hex_to_u128(serde_json::Value::Bool(true)) { + Ok(_) => panic!("Expected an error, but got Ok"), + Err(err) => assert_eq!( + err.to_string(), + "Request error: Cannot tansform result Bool(true) to &str" + ), + } + } #[test] - fn hex_to_decimal_works() { - let hex_string = "0000000000000000000000000000000000000000000000000000000babf2cf8b"; - let d = hex_to_decimal(hex_string); - assert_eq!(d, 50129457035.0); + fn shoud_return_error_when_param_is_not_a_hex_str() { + match convert_balance_hex_to_u128(serde_json::Value::String("qwexyz".into())) { + Ok(_) => panic!("Expected an error, but got Ok"), + Err(err) => + assert_eq!(err.to_string(), "Request error: Cannot parse result \"qwexyz\" to u128"), + } } } diff --git a/tee-worker/litentry/core/data-providers/src/nodereal_jsonrpc.rs b/tee-worker/litentry/core/data-providers/src/nodereal_jsonrpc.rs index 7dd24cbc36..e8d58a234a 100644 --- a/tee-worker/litentry/core/data-providers/src/nodereal_jsonrpc.rs +++ b/tee-worker/litentry/core/data-providers/src/nodereal_jsonrpc.rs @@ -17,7 +17,7 @@ #[cfg(all(not(feature = "std"), feature = "sgx"))] use crate::sgx_reexport_prelude::*; -use crate::{build_client, hex_to_decimal, DataProviderConfig, Error, HttpError}; +use crate::{build_client, convert_balance_hex_to_u128, DataProviderConfig, Error, HttpError}; use http::header::CONNECTION; use http_req::response::Headers; use itc_rest_client::{ @@ -26,6 +26,7 @@ use itc_rest_client::{ RestPath, RestPost, }; use itp_rpc::{Id, RpcRequest}; +use litentry_primitives::Web3Network; use log::debug; use serde::{Deserialize, Serialize}; use std::{ @@ -35,6 +36,28 @@ use std::{ vec::Vec, }; +pub trait Web3NetworkNoderealJsonrpcClient { + fn create_nodereal_jsonrpc_client( + &self, + data_provider_config: &DataProviderConfig, + ) -> Option; +} + +impl Web3NetworkNoderealJsonrpcClient for Web3Network { + fn create_nodereal_jsonrpc_client( + &self, + data_provider_config: &DataProviderConfig, + ) -> Option { + match self { + Web3Network::Bsc => + Some(NoderealJsonrpcClient::new(NoderealChain::Bsc, data_provider_config)), + Web3Network::Ethereum => + Some(NoderealJsonrpcClient::new(NoderealChain::Eth, data_provider_config)), + _ => None, + } + } +} + // https://docs.nodereal.io/reference/getting-started-with-your-api pub enum NoderealChain { // BNB Smart Chain @@ -219,7 +242,7 @@ pub trait NftApiList { param: &GetNFTHoldingsParam, ) -> Result; - fn get_token_balance_721(&mut self, param: &GetTokenBalance721Param) -> Result; + fn get_token_balance_721(&mut self, param: &GetTokenBalance721Param) -> Result; } // NFT API @@ -251,7 +274,7 @@ impl NftApiList for NoderealJsonrpcClient { } // https://docs.nodereal.io/reference/nr_gettokenbalance721 - fn get_token_balance_721(&mut self, param: &GetTokenBalance721Param) -> Result { + fn get_token_balance_721(&mut self, param: &GetTokenBalance721Param) -> Result { let params: Vec = vec![ param.token_address.clone(), param.account_address.clone(), @@ -269,13 +292,7 @@ impl NftApiList for NoderealJsonrpcClient { Ok(resp) => { // result example: '0x', '0x8' debug!("get_token_balance_721, response: {:?}", resp); - match resp.result.as_str() { - Some(result) => Ok(usize::from_str_radix(&result[2..], 16).unwrap_or_default()), - None => Err(Error::RequestError(format!( - "Cannot tansform response result {:?} to &str", - resp.result - ))), - } + convert_balance_hex_to_u128(resp.result) }, Err(e) => Err(Error::RequestError(format!("{:?}", e))), } @@ -294,13 +311,13 @@ pub struct GetTokenBalance20Param { // Fungible Tokens API pub trait FungibleApiList { - fn get_token_balance_20(&mut self, param: &GetTokenBalance20Param) -> Result; + fn get_token_balance_20(&mut self, param: &GetTokenBalance20Param) -> Result; fn get_token_holdings(&mut self, address: &str) -> Result; } impl FungibleApiList for NoderealJsonrpcClient { // https://docs.nodereal.io/reference/nr_gettokenbalance20 - fn get_token_balance_20(&mut self, param: &GetTokenBalance20Param) -> Result { + fn get_token_balance_20(&mut self, param: &GetTokenBalance20Param) -> Result { let params: Vec = vec![param.contract_address.clone(), param.address.clone(), param.block_number.clone()]; debug!("get_token_balance_20: {:?}", param); @@ -315,13 +332,7 @@ impl FungibleApiList for NoderealJsonrpcClient { Ok(resp) => { // result example: '0x', '0x8' debug!("get_token_balance_20, response: {:?}", resp); - match resp.result.as_str() { - Some(result) => Ok(hex_to_decimal(&result[2..])), - None => Err(Error::RequestError(format!( - "Cannot tansform response result {:?} to &str", - resp.result - ))), - } + convert_balance_hex_to_u128(resp.result) }, Err(e) => Err(Error::RequestError(format!("{:?}", e))), } @@ -343,11 +354,11 @@ impl FungibleApiList for NoderealJsonrpcClient { } pub trait EthBalance { - fn get_balance(&mut self, address: &str) -> Result; + fn get_balance(&mut self, address: &str) -> Result; } impl EthBalance for NoderealJsonrpcClient { - fn get_balance(&mut self, address: &str) -> Result { + fn get_balance(&mut self, address: &str) -> Result { let params = vec![address.to_string(), "latest".to_string()]; let req_body = RpcRequest { @@ -361,13 +372,7 @@ impl EthBalance for NoderealJsonrpcClient { Ok(resp) => { // result example: '0x', '0x8' debug!("eth_getBalance, response: {:?}", resp); - match resp.result.as_str() { - Some(result) => Ok(hex_to_decimal(&result[2..])), - None => Err(Error::RequestError(format!( - "Cannot tansform response result {:?} to &str", - resp.result - ))), - } + convert_balance_hex_to_u128(resp.result) }, Err(e) => Err(Error::RequestError(format!("{:?}", e))), } @@ -394,7 +399,13 @@ impl TransactionCount for NoderealJsonrpcClient { // result example: '0x', '0x8' debug!("eth_getTransactionCount, response: {:?}", resp); match resp.result.as_str() { - Some(result) => Ok(hex_to_decimal(&result[2..]) as u64), + Some(result) => match u64::from_str_radix(&result[2..], 16) { + Ok(balance) => Ok(balance), + Err(_) => Err(Error::RequestError(format!( + "Cannot parse result {:?} to u64", + result + ))), + }, None => Err(Error::RequestError(format!( "Cannot tansform response result {:?} to &str", resp.result @@ -463,6 +474,6 @@ mod tests { block_number: "latest".into(), }; let result = client.get_token_balance_20(¶m).unwrap(); - assert_eq!(result, 800.1); + assert_eq!(result, 800); } } diff --git a/tee-worker/litentry/core/mock-server/src/achainable.rs b/tee-worker/litentry/core/mock-server/src/achainable.rs index 523f2a2726..4a67e7a9e5 100644 --- a/tee-worker/litentry/core/mock-server/src/achainable.rs +++ b/tee-worker/litentry/core/mock-server/src/achainable.rs @@ -63,6 +63,20 @@ const RES_BODY_OK_CLASS_OF_YEAR: &str = r#" "runningCost": 1 } "#; +const RES_BODY_OK_HOLDING_AMOUNT: &str = r#" +{ + "name": "Balance over {amount}", + "result": true, + "display": [ + { + "text": "Balance over 0 (Balance is 800)", + "result": true + } + ], + "analyticsDisplay": [], + "runningCost": 1 +} +"#; const RES_ERRBODY: &str = r#"Error request."#; use lc_data_providers::achainable::ReqBody; @@ -105,6 +119,10 @@ pub(crate) fn query() -> impl Filter impl Filter "0x174876E800", // 3000 "0x90d53026a47ac20609accc3f2ddc9fb9b29bb310" => "0xBB8", - // 800.1 - _ => "0x320.1", + // 50 * 10^18 + "0x45cdb67696802b9d01ed156b883269dbdb9c6239" => "0x2b5e3af16b1880000", + // 400 * 10^18 + "0x75438d34c9125839c8b08d21b7f3167281659e7c" => "0x15af1d78b58c400000", + // 2199 * 10^18 + "0xba359c153ad11aa17c3122b05a4db8b46bb3191b" => "0x7735416132dbfc0000", + // 800 + _ => "0x320", }; let body = RpcResponse { jsonrpc: "2.0".into(), @@ -78,6 +84,15 @@ pub(crate) fn query() -> impl Filter { + let body = RpcResponse { + jsonrpc: "2.0".into(), + id: Id::Number(1), + // 1 * 10^18 + result: serde_json::to_value("0xde0b6b3a7640000").unwrap(), + }; + Response::builder().body(serde_json::to_string(&body).unwrap()) + }, _ => Response::builder().status(404).body(String::from("Error query")), } }) diff --git a/tee-worker/litentry/core/service/Cargo.toml b/tee-worker/litentry/core/service/Cargo.toml new file mode 100644 index 0000000000..4c290bcb1d --- /dev/null +++ b/tee-worker/litentry/core/service/Cargo.toml @@ -0,0 +1,50 @@ +[package] +authors = ["Trust Computing GmbH "] +edition = "2021" +name = "lc-service" +version = "0.1.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +# std dependencies +thiserror = { version = "1.0.38", optional = true } + +# sgx dependencies +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true, features = ["net", "thread"] } +thiserror_sgx = { package = "thiserror", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3", optional = true } + +# no_std dependencies +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +log = { version = "0.4", default-features = false } + +# internal dependencies +itp-stf-primitives = { default-features = false, path = "../../../core-primitives/stf-primitives" } +itp-time-utils = { path = "../../../core-primitives/time-utils", default-features = false } +itp-types = { path = "../../../core-primitives/types", default-features = false } +itp-utils = { path = "../../../core-primitives/utils", default-features = false } + +# litentry +lc-common = { path = "../common", default-features = false } +lc-data-providers = { path = "../data-providers", default-features = false } +litentry-primitives = { path = "../../primitives", default-features = false } + +[features] +default = ["std"] +sgx = [ + "sgx_tstd", + "thiserror_sgx", + "litentry-primitives/sgx", + "itp-time-utils/sgx", + "lc-common/sgx", + "lc-data-providers/sgx", +] +std = [ + "log/std", + "itp-types/std", + "itp-utils/std", + "litentry-primitives/std", + "itp-time-utils/std", + "lc-common/std", + "lc-data-providers/std", +] diff --git a/tee-worker/litentry/core/service/src/lib.rs b/tee-worker/litentry/core/service/src/lib.rs new file mode 100644 index 0000000000..e1773ead74 --- /dev/null +++ b/tee-worker/litentry/core/service/src/lib.rs @@ -0,0 +1,37 @@ +// Copyright 2020-2024 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +// re-export module to properly feature gate sgx and regular std environment +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod sgx_reexport_prelude { + pub use thiserror_sgx as thiserror; +} + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +use std::{string::String, vec::Vec}; + +use litentry_primitives::{ErrorDetail as Error, IntoErrorDetail, Web3Network, Web3TokenType}; + +pub use lc_data_providers::DataProviderConfig; + +pub mod web3_token; diff --git a/tee-worker/litentry/core/service/src/web3_token/mod.rs b/tee-worker/litentry/core/service/src/web3_token/mod.rs new file mode 100644 index 0000000000..f3940b6dd6 --- /dev/null +++ b/tee-worker/litentry/core/service/src/web3_token/mod.rs @@ -0,0 +1,23 @@ +// Copyright 2020-2024 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +pub mod token_balance; diff --git a/tee-worker/litentry/core/service/src/web3_token/token_balance/bnb_balance.rs b/tee-worker/litentry/core/service/src/web3_token/token_balance/bnb_balance.rs new file mode 100644 index 0000000000..2b8f19874b --- /dev/null +++ b/tee-worker/litentry/core/service/src/web3_token/token_balance/bnb_balance.rs @@ -0,0 +1,76 @@ +// Copyright 2020-2024 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +use core::result::Result; + +use lc_common::web3_token::{TokenAddress, TokenDecimals}; +use lc_data_providers::nodereal_jsonrpc::{ + EthBalance, FungibleApiList, GetTokenBalance20Param, Web3NetworkNoderealJsonrpcClient, +}; + +use crate::*; + +use super::common::calculate_balance_with_decimals; + +pub fn get_balance( + addresses: Vec<(Web3Network, String)>, + data_provider_config: &DataProviderConfig, +) -> Result { + let mut total_balance = 0_f64; + + for address in addresses.iter() { + let network = address.0; + + match network { + Web3Network::Bsc | Web3Network::Ethereum => { + let decimals = Web3TokenType::Bnb.get_decimals(network); + if let Some(mut client) = + network.create_nodereal_jsonrpc_client(data_provider_config) + { + let result = if network == Web3Network::Bsc { + client.get_balance(address.1.as_str()) + } else { + let param = GetTokenBalance20Param { + contract_address: Web3TokenType::Bnb + .get_token_address(network) + .unwrap_or_default() + .into(), + address: address.1.clone(), + block_number: "latest".into(), + }; + client.get_token_balance_20(¶m) + }; + + match result { + Ok(balance) => { + total_balance += calculate_balance_with_decimals(balance, decimals); + }, + Err(err) => return Err(err.into_error_detail()), + } + } + }, + _ => {}, + } + } + + Ok(total_balance) +} diff --git a/tee-worker/litentry/core/service/src/web3_token/token_balance/common.rs b/tee-worker/litentry/core/service/src/web3_token/token_balance/common.rs new file mode 100644 index 0000000000..4f4cf1bcf6 --- /dev/null +++ b/tee-worker/litentry/core/service/src/web3_token/token_balance/common.rs @@ -0,0 +1,89 @@ +// Copyright 2020-2024 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +use core::result::Result; + +use lc_common::web3_token::{TokenAddress, TokenDecimals}; +use lc_data_providers::nodereal_jsonrpc::{ + FungibleApiList, GetTokenBalance20Param, Web3NetworkNoderealJsonrpcClient, +}; + +use crate::*; + +// only support to get balance for non-native token +pub fn get_balance_from_evm( + addresses: Vec<(Web3Network, String)>, + token_type: Web3TokenType, + data_provider_config: &DataProviderConfig, +) -> Result { + let mut total_balance = 0_f64; + + for address in addresses.iter() { + let network = address.0; + let decimals = token_type.get_decimals(network); + let param = GetTokenBalance20Param { + contract_address: token_type.get_token_address(network).unwrap_or_default().into(), + address: address.1.clone(), + block_number: "latest".into(), + }; + + match network { + Web3Network::Bsc | Web3Network::Ethereum => { + if let Some(mut client) = + network.create_nodereal_jsonrpc_client(data_provider_config) + { + match client.get_token_balance_20(¶m) { + Ok(balance) => { + total_balance += calculate_balance_with_decimals(balance, decimals); + }, + Err(err) => return Err(err.into_error_detail()), + } + } + }, + _ => {}, + } + } + + Ok(total_balance) +} + +pub fn calculate_balance_with_decimals(source_balance: u128, decimals: u64) -> f64 { + let decimals_value = (if decimals == 0 { 1 } else { decimals }) as u128; + (source_balance / decimals_value) as f64 + + ((source_balance % decimals_value) as f64 / decimals_value as f64) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn should_calculate_balance_with_decimals_works() { + assert_eq!(calculate_balance_with_decimals(100, 0), 100_f64); + + assert_eq!(calculate_balance_with_decimals(123, 100), 1.23_f64); + + assert_eq!(calculate_balance_with_decimals(123, 1000), 0.123_f64); + + assert_eq!(calculate_balance_with_decimals(0, 1000), 0_f64); + } +} diff --git a/tee-worker/litentry/core/service/src/web3_token/token_balance/eth_balance.rs b/tee-worker/litentry/core/service/src/web3_token/token_balance/eth_balance.rs new file mode 100644 index 0000000000..63140ce609 --- /dev/null +++ b/tee-worker/litentry/core/service/src/web3_token/token_balance/eth_balance.rs @@ -0,0 +1,76 @@ +// Copyright 2020-2024 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +use core::result::Result; + +use lc_common::web3_token::{TokenAddress, TokenDecimals}; +use lc_data_providers::nodereal_jsonrpc::{ + EthBalance, FungibleApiList, GetTokenBalance20Param, Web3NetworkNoderealJsonrpcClient, +}; + +use crate::*; + +use super::common::calculate_balance_with_decimals; + +pub fn get_balance( + addresses: Vec<(Web3Network, String)>, + data_provider_config: &DataProviderConfig, +) -> Result { + let mut total_balance = 0_f64; + + for address in addresses.iter() { + let network = address.0; + + match network { + Web3Network::Bsc | Web3Network::Ethereum => { + let decimals = Web3TokenType::Eth.get_decimals(network); + if let Some(mut client) = + network.create_nodereal_jsonrpc_client(data_provider_config) + { + let result = if network == Web3Network::Ethereum { + client.get_balance(address.1.as_str()) + } else { + let param = GetTokenBalance20Param { + contract_address: Web3TokenType::Eth + .get_token_address(network) + .unwrap_or_default() + .into(), + address: address.1.clone(), + block_number: "latest".into(), + }; + client.get_token_balance_20(¶m) + }; + + match result { + Ok(balance) => { + total_balance += calculate_balance_with_decimals(balance, decimals); + }, + Err(err) => return Err(err.into_error_detail()), + } + } + }, + _ => {}, + } + } + + Ok(total_balance) +} diff --git a/tee-worker/litentry/core/service/src/web3_token/token_balance/lit_balance.rs b/tee-worker/litentry/core/service/src/web3_token/token_balance/lit_balance.rs new file mode 100644 index 0000000000..3dd70bab55 --- /dev/null +++ b/tee-worker/litentry/core/service/src/web3_token/token_balance/lit_balance.rs @@ -0,0 +1,94 @@ +// Copyright 2020-2024 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +use core::result::Result; +use std::vec; + +use lc_common::web3_token::{TokenAddress, TokenDecimals}; +use lc_data_providers::{ + achainable::{AchainableClient, HoldingAmount, Params, ParamsBasicTypeWithAmountToken}, + achainable_names::{AchainableNameAmountToken, GetAchainableName}, + nodereal_jsonrpc::{FungibleApiList, GetTokenBalance20Param, Web3NetworkNoderealJsonrpcClient}, +}; + +use crate::*; + +use super::common::calculate_balance_with_decimals; + +pub fn get_balance( + addresses: Vec<(Web3Network, String)>, + data_provider_config: &DataProviderConfig, +) -> Result { + let mut total_balance = 0_f64; + + for address in addresses.iter() { + let network = address.0; + let account_address = address.1.clone(); + match network { + Web3Network::Bsc | Web3Network::Ethereum => { + let decimals = Web3TokenType::Lit.get_decimals(network); + let param = GetTokenBalance20Param { + contract_address: Web3TokenType::Lit + .get_token_address(address.0) + .unwrap_or_default() + .into(), + address: account_address, + block_number: "latest".into(), + }; + + if let Some(mut client) = + network.create_nodereal_jsonrpc_client(data_provider_config) + { + match client.get_token_balance_20(¶m) { + Ok(balance) => { + total_balance += calculate_balance_with_decimals(balance, decimals); + }, + Err(err) => return Err(err.into_error_detail()), + } + } + }, + Web3Network::Litentry | Web3Network::Litmus => { + let mut client = AchainableClient::new(data_provider_config); + + let param = + Params::ParamsBasicTypeWithAmountToken(ParamsBasicTypeWithAmountToken::new( + AchainableNameAmountToken::BalanceOverAmount.name().into(), + &network, + "0".into(), + None, + )); + match client.holding_amount(vec![account_address], param) { + Ok(balance) => match balance.parse::() { + Ok(balance_value) => { + total_balance += balance_value; + }, + Err(_) => return Err(Error::ParseError), + }, + Err(err) => return Err(err.into_error_detail()), + } + }, + _ => {}, + } + } + + Ok(total_balance) +} diff --git a/tee-worker/litentry/core/service/src/web3_token/token_balance/mod.rs b/tee-worker/litentry/core/service/src/web3_token/token_balance/mod.rs new file mode 100644 index 0000000000..03ad51bf39 --- /dev/null +++ b/tee-worker/litentry/core/service/src/web3_token/token_balance/mod.rs @@ -0,0 +1,43 @@ +// Copyright 2020-2024 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +use core::result::Result; + +use crate::*; + +mod bnb_balance; +mod common; +mod eth_balance; +mod lit_balance; + +pub fn get_token_balance( + token_type: Web3TokenType, + addresses: Vec<(Web3Network, String)>, + data_provider_config: &DataProviderConfig, +) -> Result { + match token_type { + Web3TokenType::Bnb => bnb_balance::get_balance(addresses, data_provider_config), + Web3TokenType::Eth => eth_balance::get_balance(addresses, data_provider_config), + Web3TokenType::Lit => lit_balance::get_balance(addresses, data_provider_config), + _ => common::get_balance_from_evm(addresses, token_type, data_provider_config), + } +} diff --git a/tee-worker/litentry/core/stf-task/receiver/Cargo.toml b/tee-worker/litentry/core/stf-task/receiver/Cargo.toml index bda856e19b..4e74c49c7b 100644 --- a/tee-worker/litentry/core/stf-task/receiver/Cargo.toml +++ b/tee-worker/litentry/core/stf-task/receiver/Cargo.toml @@ -42,6 +42,7 @@ itp-utils = { path = "../../../../core-primitives/utils", default-features = fal frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } ita-sgx-runtime = { path = "../../../../app-libs/sgx-runtime", default-features = false } lc-assertion-build = { path = "../../assertion-build", default-features = false } +lc-assertion-build-v2 = { path = "../../assertion-build-v2", default-features = false } lc-credentials = { path = "../../credentials", default-features = false } lc-data-providers = { path = "../../data-providers", default-features = false } lc-identity-verification = { path = "../../identity-verification", default-features = false } @@ -79,6 +80,7 @@ sgx = [ "lc-stf-task-sender/sgx", "lc-identity-verification/sgx", "lc-assertion-build/sgx", + "lc-assertion-build-v2/sgx", "lc-credentials/sgx", "lc-data-providers/sgx", ] @@ -98,6 +100,7 @@ std = [ "lc-stf-task-sender/std", "lc-identity-verification/std", "lc-assertion-build/std", + "lc-assertion-build-v2/std", "ita-sgx-runtime/std", "frame-support/std", "lc-credentials/std", diff --git a/tee-worker/litentry/core/stf-task/receiver/src/handler/assertion.rs b/tee-worker/litentry/core/stf-task/receiver/src/handler/assertion.rs index c1acc8b102..0ee5629a70 100644 --- a/tee-worker/litentry/core/stf-task/receiver/src/handler/assertion.rs +++ b/tee-worker/litentry/core/stf-task/receiver/src/handler/assertion.rs @@ -192,6 +192,13 @@ where &self.req, &self.context.data_provider_config, ), + + Assertion::TokenHoldingAmount(token_type) => + lc_assertion_build_v2::token_holding_amount::build( + &self.req, + token_type, + &self.context.data_provider_config, + ), }?; // post-process the credential diff --git a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/Cargo.toml b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/Cargo.toml index 5a09e4d99a..609185f601 100644 --- a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/Cargo.toml +++ b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/Cargo.toml @@ -38,6 +38,7 @@ itp-utils = { path = "../../../../core-primitives/utils", default-features = fal frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } lc-assertion-build = { path = "../../assertion-build", default-features = false } +lc-assertion-build-v2 = { path = "../../assertion-build-v2", default-features = false } lc-credentials = { path = "../../credentials", default-features = false } lc-data-providers = { path = "../../data-providers", default-features = false } lc-stf-task-receiver = { path = "../../stf-task/receiver", default-features = false } @@ -60,6 +61,7 @@ sgx = [ "sp-core/full_crypto", "litentry-primitives/sgx", "lc-assertion-build/sgx", + "lc-assertion-build-v2/sgx", "lc-credentials/sgx", "lc-data-providers/sgx", "lc-stf-task-receiver/sgx", @@ -79,6 +81,7 @@ std = [ "sp-core/std", "litentry-primitives/std", "lc-assertion-build/std", + "lc-assertion-build-v2/std", "ita-sgx-runtime/std", "frame-support/std", "lc-credentials/std", diff --git a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/vc_handling.rs b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/vc_handling.rs index 45e1a66847..d21759f4a9 100644 --- a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/vc_handling.rs +++ b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/vc_handling.rs @@ -168,6 +168,13 @@ where &self.req, &self.context.data_provider_config, ), + + Assertion::TokenHoldingAmount(token_type) => + lc_assertion_build_v2::token_holding_amount::build( + &self.req, + token_type, + &self.context.data_provider_config, + ), }?; // post-process the credential diff --git a/tee-worker/litentry/primitives/src/lib.rs b/tee-worker/litentry/primitives/src/lib.rs index 9d8dbb7426..b1f76256e7 100644 --- a/tee-worker/litentry/primitives/src/lib.rs +++ b/tee-worker/litentry/primitives/src/lib.rs @@ -54,7 +54,7 @@ pub use parentchain_primitives::{ GenericDiscordRoleType, Hash as ParentchainHash, Header as ParentchainHeader, IMPError, Index as ParentchainIndex, IntoErrorDetail, OneBlockCourseType, ParameterString, SchemaContentString, SchemaIdString, Signature as ParentchainSignature, SoraQuizType, - VCMPError, VIP3MembershipCardLevel, Web3Network, ASSERTION_FROM_DATE, MINUTES, + VCMPError, VIP3MembershipCardLevel, Web3Network, Web3TokenType, ASSERTION_FROM_DATE, MINUTES, }; use scale_info::TypeInfo; use sp_core::{ecdsa, ed25519, sr25519, ByteArray}; diff --git a/tee-worker/service/src/prometheus_metrics.rs b/tee-worker/service/src/prometheus_metrics.rs index 66fc15e94f..c998261561 100644 --- a/tee-worker/service/src/prometheus_metrics.rs +++ b/tee-worker/service/src/prometheus_metrics.rs @@ -291,6 +291,7 @@ fn handle_stf_call_request(req: RequestType, time: f64) { Assertion::EVMAmountHolding(_) => "EVMAmountHolding", Assertion::BRC20AmountHolder => "BRC20AmountHolder", Assertion::CryptoSummary => "CryptoSummary", + Assertion::TokenHoldingAmount(_) => "TokenHoldingAmount", }, }; inc_stf_calls(category, label); diff --git a/ts-tests/common/setup/setup-bridge.ts b/ts-tests/common/setup/setup-bridge.ts index 75fbded4b6..75e4969d53 100644 --- a/ts-tests/common/setup/setup-bridge.ts +++ b/ts-tests/common/setup/setup-bridge.ts @@ -99,7 +99,7 @@ async function deployBridgeContracts(wallet: Wallet) { console.log('GenericHandler: ', genericHandler.address); console.log('ERC20: ', erc20.address); - await sleep(1); + await sleep(10); return { bridge, erc20Handler, erc721Handler, genericHandler, erc20 }; } diff --git a/ts-tests/common/setup/wait-finalized-block.ts b/ts-tests/common/setup/wait-finalized-block.ts index 46aef0ba48..29ea7ef057 100644 --- a/ts-tests/common/setup/wait-finalized-block.ts +++ b/ts-tests/common/setup/wait-finalized-block.ts @@ -26,14 +26,15 @@ const TIMEOUT_MIN = 1000 * 60 * 3; // 1min console.log(`Connecting to parachain ${config.parachain_ws}`); timeout = global.setTimeout(async () => { + if (typeof unsub === 'function') unsub(); - if (typeof unsub === 'function') unsub(); - - if (api) api.disconnect(); - provider.on('disconnected', () => { - console.log(`\nno block production detected after ${TIMEOUT_MIN}min, you might want to check it manually. Quit now`); - process.exit(1); - }); + if (api) api.disconnect(); + provider.on('disconnected', () => { + console.log( + `\nno block production detected after ${TIMEOUT_MIN}min, you might want to check it manually. Quit now` + ); + process.exit(1); + }); }, TIMEOUT_MIN); api = await ApiPromise.create({ @@ -41,19 +42,19 @@ const TIMEOUT_MIN = 1000 * 60 * 3; // 1min }); unsub = await api.rpc.chain.subscribeFinalizedHeads(async (head) => { - const blockNumber = head.number.toNumber(); - console.log(`Parachain finalized block #${blockNumber} with hash ${head.hash}`); - count += 1; - - if (blockNumber >= 1 && count >= FINALIZED_BLOCKS_COUNT) { - unsub(); - if (timeout) global.clearTimeout(timeout) - - await api.disconnect(); - provider.on('disconnected', () => { - console.log('Done.'); - process.exit(0); - }); - } - }) + const blockNumber = head.number.toNumber(); + console.log(`Parachain finalized block #${blockNumber} with hash ${head.hash}`); + count += 1; + + if (blockNumber >= 1 && count >= FINALIZED_BLOCKS_COUNT) { + unsub(); + if (timeout) global.clearTimeout(timeout); + + await api.disconnect(); + provider.on('disconnected', () => { + console.log('Done.'); + process.exit(0); + }); + } + }); })(); From b62e965ec63205293b3d4fcedf27ecda60f944d7 Mon Sep 17 00:00:00 2001 From: Faisal Ahmed <42486737+felixfaisal@users.noreply.github.com> Date: Tue, 23 Jan 2024 18:37:57 +0530 Subject: [PATCH 22/51] fix: add storage item for feature control (#2382) * fix: add storage item for feature control * [benchmarking bot] Auto commit generated weights files (#2384) Co-authored-by: felixfaisal * fix: update unit tests to not use feature control * refactor: remove feature * refactor: fix clippy warnings * fix: update the storage item and relevant scripts and tests * fix: update deploy.sh script * fix: update launch-local-docker * fix: update launch-local-standalone * refactor: change the extrinsic name * fix: update mock.rs * fix: ias-check tests * fix: update benchmarking setup * refactor: clippy fix * fix: update to use extrinsic only in rococo chain * fix: update launch-local-docker * fix: update launch-local-docker script * fix: update genesis instead of extrinsic * fix: update mock.rs for the genesis * fix: use flag only in rococo-dev * fix: remove extrinsic from launch script * fix: update standalone script * fix: remvoe ts extrinsic call from deploy * fix: remove unused code --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: felixfaisal Co-authored-by: Zhouhui Tian <125243011+zhouhuitian@users.noreply.github.com> --- docker/pnpm-lock.yaml | 404 +++++++++--------- node/src/chain_specs/litmus.rs | 6 +- node/src/chain_specs/rococo.rs | 9 +- pallets/identity-management/Cargo.toml | 2 +- pallets/identity-management/src/mock.rs | 9 +- pallets/sidechain/src/mock.rs | 17 +- pallets/teeracle/Cargo.toml | 2 +- pallets/teeracle/src/benchmarking.rs | 5 + pallets/teeracle/src/mock.rs | 17 +- pallets/teerex/Cargo.toml | 2 - pallets/teerex/src/lib.rs | 41 +- pallets/teerex/src/mock.rs | 28 +- pallets/teerex/src/tests/mod.rs | 2 +- .../teerex/src/tests/skip_ias_check_tests.rs | 10 +- pallets/teerex/src/tests/test_cases.rs | 4 +- pallets/vc-management/Cargo.toml | 2 +- pallets/vc-management/src/mock.rs | 7 +- runtime/rococo/Cargo.toml | 1 - runtime/rococo/src/weights/pallet_teerex.rs | 40 +- scripts/launch-local-binary.sh | 1 + scripts/launch-local-docker.sh | 1 + .../setup/skip-schedule-enclave-check.ts | 47 ++ ts-tests/package.json | 1 + 23 files changed, 401 insertions(+), 257 deletions(-) create mode 100644 ts-tests/common/setup/skip-schedule-enclave-check.ts diff --git a/docker/pnpm-lock.yaml b/docker/pnpm-lock.yaml index 6104996f31..fa9b6e8f7b 100644 --- a/docker/pnpm-lock.yaml +++ b/docker/pnpm-lock.yaml @@ -11,27 +11,27 @@ dependencies: packages: - /@noble/curves@1.2.0: - resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==} + /@noble/curves@1.3.0: + resolution: {integrity: sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==} dependencies: - '@noble/hashes': 1.3.2 + '@noble/hashes': 1.3.3 dev: false - /@noble/hashes@1.3.2: - resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==} + /@noble/hashes@1.3.3: + resolution: {integrity: sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==} engines: {node: '>= 16'} dev: false - /@polkadot/api-augment@10.11.1: - resolution: {integrity: sha512-9Sk7fi6wzvxAoxvGJPcMt0hU4WzuIAlBy4Rng6WPiS6Ed0HJLr1dkZaqFFmV5my2pb3tu//1JGYkt+MUVB0Kqw==} + /@polkadot/api-augment@10.11.2: + resolution: {integrity: sha512-PTpnqpezc75qBqUtgrc0GYB8h9UHjfbHSRZamAbecIVAJ2/zc6CqtnldeaBlIu1IKTgBzi3FFtTyYu+ZGbNT2Q==} engines: {node: '>=18'} dependencies: - '@polkadot/api-base': 10.11.1 - '@polkadot/rpc-augment': 10.11.1 - '@polkadot/types': 10.11.1 - '@polkadot/types-augment': 10.11.1 - '@polkadot/types-codec': 10.11.1 - '@polkadot/util': 12.6.1 + '@polkadot/api-base': 10.11.2 + '@polkadot/rpc-augment': 10.11.2 + '@polkadot/types': 10.11.2 + '@polkadot/types-augment': 10.11.2 + '@polkadot/types-codec': 10.11.2 + '@polkadot/util': 12.6.2 tslib: 2.6.2 transitivePeerDependencies: - bufferutil @@ -39,13 +39,13 @@ packages: - utf-8-validate dev: false - /@polkadot/api-base@10.11.1: - resolution: {integrity: sha512-A645Hj9bGtq0EOEWcwTaGoD40vp8/ih1suwinl5il8Psg+bdDmzodnVH5Jhuwe1dNKOuXuvxZvOmbYUPWyIqyg==} + /@polkadot/api-base@10.11.2: + resolution: {integrity: sha512-4LIjaUfO9nOzilxo7XqzYKCNMtmUypdk8oHPdrRnSjKEsnK7vDsNi+979z2KXNXd2KFSCFHENmI523fYnMnReg==} engines: {node: '>=18'} dependencies: - '@polkadot/rpc-core': 10.11.1 - '@polkadot/types': 10.11.1 - '@polkadot/util': 12.6.1 + '@polkadot/rpc-core': 10.11.2 + '@polkadot/types': 10.11.2 + '@polkadot/util': 12.6.2 rxjs: 7.8.1 tslib: 2.6.2 transitivePeerDependencies: @@ -54,18 +54,18 @@ packages: - utf-8-validate dev: false - /@polkadot/api-derive@10.11.1: - resolution: {integrity: sha512-i48okJr0l1IrFTPa9KVkoJnDL2EHKExR6XC0Z7I9+kW9noxYWqo0tIoi5s1bNVD475xWK/rUjT7qHxiDbPaCUQ==} + /@polkadot/api-derive@10.11.2: + resolution: {integrity: sha512-m3BQbPionkd1iSlknddxnL2hDtolPIsT+aRyrtn4zgMRPoLjHFmTmovvg8RaUyYofJtZeYrnjMw0mdxiSXx7eA==} engines: {node: '>=18'} dependencies: - '@polkadot/api': 10.11.1 - '@polkadot/api-augment': 10.11.1 - '@polkadot/api-base': 10.11.1 - '@polkadot/rpc-core': 10.11.1 - '@polkadot/types': 10.11.1 - '@polkadot/types-codec': 10.11.1 - '@polkadot/util': 12.6.1 - '@polkadot/util-crypto': 12.6.1(@polkadot/util@12.6.1) + '@polkadot/api': 10.11.2 + '@polkadot/api-augment': 10.11.2 + '@polkadot/api-base': 10.11.2 + '@polkadot/rpc-core': 10.11.2 + '@polkadot/types': 10.11.2 + '@polkadot/types-codec': 10.11.2 + '@polkadot/util': 12.6.2 + '@polkadot/util-crypto': 12.6.2(@polkadot/util@12.6.2) rxjs: 7.8.1 tslib: 2.6.2 transitivePeerDependencies: @@ -74,24 +74,24 @@ packages: - utf-8-validate dev: false - /@polkadot/api@10.11.1: - resolution: {integrity: sha512-WEgUYvY90AHX9drmsvWQ4DDuqlE7h4x3f28K5eOoJF4dQ5AkWsFogxwJ4TH57POWLfyi8AIn6/f1vsqPtReDhA==} + /@polkadot/api@10.11.2: + resolution: {integrity: sha512-AorCZxCWCoTtdbl4DPUZh+ACe/pbLIS1BkdQY0AFJuZllm0x/yWzjgampcPd5jQAA/O3iKShRBkZqj6Mk9yG/A==} engines: {node: '>=18'} dependencies: - '@polkadot/api-augment': 10.11.1 - '@polkadot/api-base': 10.11.1 - '@polkadot/api-derive': 10.11.1 - '@polkadot/keyring': 12.6.1(@polkadot/util-crypto@12.6.1)(@polkadot/util@12.6.1) - '@polkadot/rpc-augment': 10.11.1 - '@polkadot/rpc-core': 10.11.1 - '@polkadot/rpc-provider': 10.11.1 - '@polkadot/types': 10.11.1 - '@polkadot/types-augment': 10.11.1 - '@polkadot/types-codec': 10.11.1 - '@polkadot/types-create': 10.11.1 - '@polkadot/types-known': 10.11.1 - '@polkadot/util': 12.6.1 - '@polkadot/util-crypto': 12.6.1(@polkadot/util@12.6.1) + '@polkadot/api-augment': 10.11.2 + '@polkadot/api-base': 10.11.2 + '@polkadot/api-derive': 10.11.2 + '@polkadot/keyring': 12.6.2(@polkadot/util-crypto@12.6.2)(@polkadot/util@12.6.2) + '@polkadot/rpc-augment': 10.11.2 + '@polkadot/rpc-core': 10.11.2 + '@polkadot/rpc-provider': 10.11.2 + '@polkadot/types': 10.11.2 + '@polkadot/types-augment': 10.11.2 + '@polkadot/types-codec': 10.11.2 + '@polkadot/types-create': 10.11.2 + '@polkadot/types-known': 10.11.2 + '@polkadot/util': 12.6.2 + '@polkadot/util-crypto': 12.6.2(@polkadot/util@12.6.2) eventemitter3: 5.0.1 rxjs: 7.8.1 tslib: 2.6.2 @@ -101,35 +101,35 @@ packages: - utf-8-validate dev: false - /@polkadot/keyring@12.6.1(@polkadot/util-crypto@12.6.1)(@polkadot/util@12.6.1): - resolution: {integrity: sha512-cicTctZr5Jy5vgNT2FsNiKoTZnz6zQkgDoIYv79NI+p1Fhwc9C+DN/iMCnk3Cm9vR2gSAd2fSV+Y5iKVDhAmUw==} + /@polkadot/keyring@12.6.2(@polkadot/util-crypto@12.6.2)(@polkadot/util@12.6.2): + resolution: {integrity: sha512-O3Q7GVmRYm8q7HuB3S0+Yf/q/EB2egKRRU3fv9b3B7V+A52tKzA+vIwEmNVaD1g5FKW9oB97rmpggs0zaKFqHw==} engines: {node: '>=18'} peerDependencies: - '@polkadot/util': 12.6.1 - '@polkadot/util-crypto': 12.6.1 + '@polkadot/util': 12.6.2 + '@polkadot/util-crypto': 12.6.2 dependencies: - '@polkadot/util': 12.6.1 - '@polkadot/util-crypto': 12.6.1(@polkadot/util@12.6.1) + '@polkadot/util': 12.6.2 + '@polkadot/util-crypto': 12.6.2(@polkadot/util@12.6.2) tslib: 2.6.2 dev: false - /@polkadot/networks@12.6.1: - resolution: {integrity: sha512-pzyirxTYAnsx+6kyLYcUk26e4TLz3cX6p2KhTgAVW77YnpGX5VTKTbYykyXC8fXFd/migeQsLaa2raFN47mwoA==} + /@polkadot/networks@12.6.2: + resolution: {integrity: sha512-1oWtZm1IvPWqvMrldVH6NI2gBoCndl5GEwx7lAuQWGr7eNL+6Bdc5K3Z9T0MzFvDGoi2/CBqjX9dRKo39pDC/w==} engines: {node: '>=18'} dependencies: - '@polkadot/util': 12.6.1 - '@substrate/ss58-registry': 1.44.0 + '@polkadot/util': 12.6.2 + '@substrate/ss58-registry': 1.45.0 tslib: 2.6.2 dev: false - /@polkadot/rpc-augment@10.11.1: - resolution: {integrity: sha512-wrtxHnEwqS3b1GuZ3sA1pzLuUjjLnW4FPawOklONRcIuKbGmFuvu7QvEIHmxBV1FAS/fs8gbvp8ImKWUPnT93Q==} + /@polkadot/rpc-augment@10.11.2: + resolution: {integrity: sha512-9AhT0WW81/8jYbRcAC6PRmuxXqNhJje8OYiulBQHbG1DTCcjAfz+6VQBke9BwTStzPq7d526+yyBKD17O3zlAA==} engines: {node: '>=18'} dependencies: - '@polkadot/rpc-core': 10.11.1 - '@polkadot/types': 10.11.1 - '@polkadot/types-codec': 10.11.1 - '@polkadot/util': 12.6.1 + '@polkadot/rpc-core': 10.11.2 + '@polkadot/types': 10.11.2 + '@polkadot/types-codec': 10.11.2 + '@polkadot/util': 12.6.2 tslib: 2.6.2 transitivePeerDependencies: - bufferutil @@ -137,14 +137,14 @@ packages: - utf-8-validate dev: false - /@polkadot/rpc-core@10.11.1: - resolution: {integrity: sha512-3l4l+zL7MDWzQx3WnaieXXUKsbeA1Miu4wsje5trYJEE+hm+nMW8h7fiFKfYzXBi7ty/wMS+S7BfQPTrDkYHxA==} + /@polkadot/rpc-core@10.11.2: + resolution: {integrity: sha512-Ot0CFLWx8sZhLZog20WDuniPA01Bk2StNDsdAQgcFKPwZw6ShPaZQCHuKLQK6I6DodOrem9FXX7c1hvoKJP5Ww==} engines: {node: '>=18'} dependencies: - '@polkadot/rpc-augment': 10.11.1 - '@polkadot/rpc-provider': 10.11.1 - '@polkadot/types': 10.11.1 - '@polkadot/util': 12.6.1 + '@polkadot/rpc-augment': 10.11.2 + '@polkadot/rpc-provider': 10.11.2 + '@polkadot/types': 10.11.2 + '@polkadot/util': 12.6.2 rxjs: 7.8.1 tslib: 2.6.2 transitivePeerDependencies: @@ -153,21 +153,21 @@ packages: - utf-8-validate dev: false - /@polkadot/rpc-provider@10.11.1: - resolution: {integrity: sha512-86aDUOnaG42si0jSOAgn6Fs3F3rz57x+iNBK1JpM0PLL2XvmPuoMZL5dZwzqSIey3nVdGJqRYfnFquWuyQpnOQ==} + /@polkadot/rpc-provider@10.11.2: + resolution: {integrity: sha512-he5jWMpDJp7e+vUzTZDzpkB7ps3H8psRally+/ZvZZScPvFEjfczT7I1WWY9h58s8+ImeVP/lkXjL9h/gUOt3Q==} engines: {node: '>=18'} dependencies: - '@polkadot/keyring': 12.6.1(@polkadot/util-crypto@12.6.1)(@polkadot/util@12.6.1) - '@polkadot/types': 10.11.1 - '@polkadot/types-support': 10.11.1 - '@polkadot/util': 12.6.1 - '@polkadot/util-crypto': 12.6.1(@polkadot/util@12.6.1) - '@polkadot/x-fetch': 12.6.1 - '@polkadot/x-global': 12.6.1 - '@polkadot/x-ws': 12.6.1 + '@polkadot/keyring': 12.6.2(@polkadot/util-crypto@12.6.2)(@polkadot/util@12.6.2) + '@polkadot/types': 10.11.2 + '@polkadot/types-support': 10.11.2 + '@polkadot/util': 12.6.2 + '@polkadot/util-crypto': 12.6.2(@polkadot/util@12.6.2) + '@polkadot/x-fetch': 12.6.2 + '@polkadot/x-global': 12.6.2 + '@polkadot/x-ws': 12.6.2 eventemitter3: 5.0.1 mock-socket: 9.3.1 - nock: 13.3.8 + nock: 13.5.0 tslib: 2.6.2 optionalDependencies: '@substrate/connect': 0.7.35 @@ -177,243 +177,243 @@ packages: - utf-8-validate dev: false - /@polkadot/types-augment@10.11.1: - resolution: {integrity: sha512-Exd5mMCuSOXXz73iWqy8ocScWTrwAPqHz0Kxpz5OWlAu+5usipMuhjoeaZA803FHQntZh9lHUN31fuc50Exhew==} + /@polkadot/types-augment@10.11.2: + resolution: {integrity: sha512-8eB8ew04wZiE5GnmFvEFW1euJWmF62SGxb1O+8wL3zoUtB9Xgo1vB6w6xbTrd+HLV6jNSeXXnbbF1BEUvi9cNg==} engines: {node: '>=18'} dependencies: - '@polkadot/types': 10.11.1 - '@polkadot/types-codec': 10.11.1 - '@polkadot/util': 12.6.1 + '@polkadot/types': 10.11.2 + '@polkadot/types-codec': 10.11.2 + '@polkadot/util': 12.6.2 tslib: 2.6.2 dev: false - /@polkadot/types-codec@10.11.1: - resolution: {integrity: sha512-B9Fu2hq3cRpJpGPcgfZ8Qi1OSX9u82J46adlbIG95ktoA+70eZ83VS3Zvtt9ACsdLVGETCJfDjSO25XptjhZKQ==} + /@polkadot/types-codec@10.11.2: + resolution: {integrity: sha512-3xjOQL+LOOMzYqlgP9ROL0FQnzU8lGflgYewzau7AsDlFziSEtb49a9BpYo6zil4koC+QB8zQ9OHGFumG08T8w==} engines: {node: '>=18'} dependencies: - '@polkadot/util': 12.6.1 - '@polkadot/x-bigint': 12.6.1 + '@polkadot/util': 12.6.2 + '@polkadot/x-bigint': 12.6.2 tslib: 2.6.2 dev: false - /@polkadot/types-create@10.11.1: - resolution: {integrity: sha512-oeaI185F3XeWSz9/fe//qZ0KsQyE6C6c13WuOa+5cX/Yuz7cSAXawrhl58HRaU+fueaE/ijEHLcuK1sdM6e1JQ==} + /@polkadot/types-create@10.11.2: + resolution: {integrity: sha512-SJt23NxYvefRxVZZm6mT9ed1pR6FDoIGQ3xUpbjhTLfU2wuhpKjekMVorYQ6z/gK2JLMu2kV92Ardsz+6GX5XQ==} engines: {node: '>=18'} dependencies: - '@polkadot/types-codec': 10.11.1 - '@polkadot/util': 12.6.1 + '@polkadot/types-codec': 10.11.2 + '@polkadot/util': 12.6.2 tslib: 2.6.2 dev: false - /@polkadot/types-known@10.11.1: - resolution: {integrity: sha512-BPHI7EbdRaznZR4RVVrQC5epyxL6caJ5dkluZP6rRwx7VmQK0FTGIwgh3UP724mzQhM8rT77MD3h2ftnq1cteg==} + /@polkadot/types-known@10.11.2: + resolution: {integrity: sha512-kbEIX7NUQFxpDB0FFGNyXX/odY7jbp56RGD+Z4A731fW2xh/DgAQrI994xTzuh0c0EqPE26oQm3kATSpseqo9w==} engines: {node: '>=18'} dependencies: - '@polkadot/networks': 12.6.1 - '@polkadot/types': 10.11.1 - '@polkadot/types-codec': 10.11.1 - '@polkadot/types-create': 10.11.1 - '@polkadot/util': 12.6.1 + '@polkadot/networks': 12.6.2 + '@polkadot/types': 10.11.2 + '@polkadot/types-codec': 10.11.2 + '@polkadot/types-create': 10.11.2 + '@polkadot/util': 12.6.2 tslib: 2.6.2 dev: false - /@polkadot/types-support@10.11.1: - resolution: {integrity: sha512-eCvWjdpELsHvXiTq201DdbIeOIaEr53zTD7HqC2wR/Z1bkQuw79Z+CyIU4sp79GL1vZ1PxS7vUH9M3FKNaTl1Q==} + /@polkadot/types-support@10.11.2: + resolution: {integrity: sha512-X11hoykFYv/3efg4coZy2hUOUc97JhjQMJLzDhHniFwGLlYU8MeLnPdCVGkXx0xDDjTo4/ptS1XpZ5HYcg+gRw==} engines: {node: '>=18'} dependencies: - '@polkadot/util': 12.6.1 + '@polkadot/util': 12.6.2 tslib: 2.6.2 dev: false - /@polkadot/types@10.11.1: - resolution: {integrity: sha512-4uKnzW2GZqNA5qRZpTPJ7z+G/ARTvXI89etv9xXXVttUdfTaYZsMf4rMuMThOAE/mAUn70LoH0JKthZLwzVgNQ==} + /@polkadot/types@10.11.2: + resolution: {integrity: sha512-d52j3xXni+C8GdYZVTSfu8ROAnzXFMlyRvXtor0PudUc8UQHOaC4+mYAkTBGA2gKdmL8MHSfRSbhcxHhsikY6Q==} engines: {node: '>=18'} dependencies: - '@polkadot/keyring': 12.6.1(@polkadot/util-crypto@12.6.1)(@polkadot/util@12.6.1) - '@polkadot/types-augment': 10.11.1 - '@polkadot/types-codec': 10.11.1 - '@polkadot/types-create': 10.11.1 - '@polkadot/util': 12.6.1 - '@polkadot/util-crypto': 12.6.1(@polkadot/util@12.6.1) + '@polkadot/keyring': 12.6.2(@polkadot/util-crypto@12.6.2)(@polkadot/util@12.6.2) + '@polkadot/types-augment': 10.11.2 + '@polkadot/types-codec': 10.11.2 + '@polkadot/types-create': 10.11.2 + '@polkadot/util': 12.6.2 + '@polkadot/util-crypto': 12.6.2(@polkadot/util@12.6.2) rxjs: 7.8.1 tslib: 2.6.2 dev: false - /@polkadot/util-crypto@12.6.1(@polkadot/util@12.6.1): - resolution: {integrity: sha512-2ezWFLmdgeDXqB9NAUdgpp3s2rQztNrZLY+y0SJYNOG4ch+PyodTW/qSksnOrVGVdRhZ5OESRE9xvo9LYV5UAw==} + /@polkadot/util-crypto@12.6.2(@polkadot/util@12.6.2): + resolution: {integrity: sha512-FEWI/dJ7wDMNN1WOzZAjQoIcCP/3vz3wvAp5QQm+lOrzOLj0iDmaIGIcBkz8HVm3ErfSe/uKP0KS4jgV/ib+Mg==} engines: {node: '>=18'} peerDependencies: - '@polkadot/util': 12.6.1 - dependencies: - '@noble/curves': 1.2.0 - '@noble/hashes': 1.3.2 - '@polkadot/networks': 12.6.1 - '@polkadot/util': 12.6.1 - '@polkadot/wasm-crypto': 7.3.1(@polkadot/util@12.6.1)(@polkadot/x-randomvalues@12.6.1) - '@polkadot/wasm-util': 7.3.1(@polkadot/util@12.6.1) - '@polkadot/x-bigint': 12.6.1 - '@polkadot/x-randomvalues': 12.6.1(@polkadot/util@12.6.1)(@polkadot/wasm-util@7.3.1) - '@scure/base': 1.1.3 + '@polkadot/util': 12.6.2 + dependencies: + '@noble/curves': 1.3.0 + '@noble/hashes': 1.3.3 + '@polkadot/networks': 12.6.2 + '@polkadot/util': 12.6.2 + '@polkadot/wasm-crypto': 7.3.2(@polkadot/util@12.6.2)(@polkadot/x-randomvalues@12.6.2) + '@polkadot/wasm-util': 7.3.2(@polkadot/util@12.6.2) + '@polkadot/x-bigint': 12.6.2 + '@polkadot/x-randomvalues': 12.6.2(@polkadot/util@12.6.2)(@polkadot/wasm-util@7.3.2) + '@scure/base': 1.1.5 tslib: 2.6.2 dev: false - /@polkadot/util@12.6.1: - resolution: {integrity: sha512-10ra3VfXtK8ZSnWI7zjhvRrhupg3rd4iFC3zCaXmRpOU+AmfIoCFVEmuUuC66gyXiz2/g6k5E6j0lWQCOProSQ==} + /@polkadot/util@12.6.2: + resolution: {integrity: sha512-l8TubR7CLEY47240uki0TQzFvtnxFIO7uI/0GoWzpYD/O62EIAMRsuY01N4DuwgKq2ZWD59WhzsLYmA5K6ksdw==} engines: {node: '>=18'} dependencies: - '@polkadot/x-bigint': 12.6.1 - '@polkadot/x-global': 12.6.1 - '@polkadot/x-textdecoder': 12.6.1 - '@polkadot/x-textencoder': 12.6.1 + '@polkadot/x-bigint': 12.6.2 + '@polkadot/x-global': 12.6.2 + '@polkadot/x-textdecoder': 12.6.2 + '@polkadot/x-textencoder': 12.6.2 '@types/bn.js': 5.1.5 bn.js: 5.2.1 tslib: 2.6.2 dev: false - /@polkadot/wasm-bridge@7.3.1(@polkadot/util@12.6.1)(@polkadot/x-randomvalues@12.6.1): - resolution: {integrity: sha512-wPtDkGaOQx5BUIYP+kJv5aV3BnCQ+HXr36khGKYrRQAMBrG+ybCNPOTVXDQnSbraPQRSw7fSIJmiQpEmFsIz0w==} + /@polkadot/wasm-bridge@7.3.2(@polkadot/util@12.6.2)(@polkadot/x-randomvalues@12.6.2): + resolution: {integrity: sha512-AJEXChcf/nKXd5Q/YLEV5dXQMle3UNT7jcXYmIffZAo/KI394a+/24PaISyQjoNC0fkzS1Q8T5pnGGHmXiVz2g==} engines: {node: '>=18'} peerDependencies: '@polkadot/util': '*' '@polkadot/x-randomvalues': '*' dependencies: - '@polkadot/util': 12.6.1 - '@polkadot/wasm-util': 7.3.1(@polkadot/util@12.6.1) - '@polkadot/x-randomvalues': 12.6.1(@polkadot/util@12.6.1)(@polkadot/wasm-util@7.3.1) + '@polkadot/util': 12.6.2 + '@polkadot/wasm-util': 7.3.2(@polkadot/util@12.6.2) + '@polkadot/x-randomvalues': 12.6.2(@polkadot/util@12.6.2)(@polkadot/wasm-util@7.3.2) tslib: 2.6.2 dev: false - /@polkadot/wasm-crypto-asmjs@7.3.1(@polkadot/util@12.6.1): - resolution: {integrity: sha512-pTUOCIP0nUc4tjzdG1vtEBztKEWde4DBEZm7NaxBLvwNUxsbYhLKYvuhASEyEIz0ZyE4rOBWEmRF4Buic8oO+g==} + /@polkadot/wasm-crypto-asmjs@7.3.2(@polkadot/util@12.6.2): + resolution: {integrity: sha512-QP5eiUqUFur/2UoF2KKKYJcesc71fXhQFLT3D4ZjG28Mfk2ZPI0QNRUfpcxVQmIUpV5USHg4geCBNuCYsMm20Q==} engines: {node: '>=18'} peerDependencies: '@polkadot/util': '*' dependencies: - '@polkadot/util': 12.6.1 + '@polkadot/util': 12.6.2 tslib: 2.6.2 dev: false - /@polkadot/wasm-crypto-init@7.3.1(@polkadot/util@12.6.1)(@polkadot/x-randomvalues@12.6.1): - resolution: {integrity: sha512-Fx15ItLcxCe7uJCWZVXhFbsrXqHUKAp9KGYQFKBRK7r1C2va4Y7qnirjwkxoMHQcunusLe2KdbrD+YJuzh4wlA==} + /@polkadot/wasm-crypto-init@7.3.2(@polkadot/util@12.6.2)(@polkadot/x-randomvalues@12.6.2): + resolution: {integrity: sha512-FPq73zGmvZtnuJaFV44brze3Lkrki3b4PebxCy9Fplw8nTmisKo9Xxtfew08r0njyYh+uiJRAxPCXadkC9sc8g==} engines: {node: '>=18'} peerDependencies: '@polkadot/util': '*' '@polkadot/x-randomvalues': '*' dependencies: - '@polkadot/util': 12.6.1 - '@polkadot/wasm-bridge': 7.3.1(@polkadot/util@12.6.1)(@polkadot/x-randomvalues@12.6.1) - '@polkadot/wasm-crypto-asmjs': 7.3.1(@polkadot/util@12.6.1) - '@polkadot/wasm-crypto-wasm': 7.3.1(@polkadot/util@12.6.1) - '@polkadot/wasm-util': 7.3.1(@polkadot/util@12.6.1) - '@polkadot/x-randomvalues': 12.6.1(@polkadot/util@12.6.1)(@polkadot/wasm-util@7.3.1) + '@polkadot/util': 12.6.2 + '@polkadot/wasm-bridge': 7.3.2(@polkadot/util@12.6.2)(@polkadot/x-randomvalues@12.6.2) + '@polkadot/wasm-crypto-asmjs': 7.3.2(@polkadot/util@12.6.2) + '@polkadot/wasm-crypto-wasm': 7.3.2(@polkadot/util@12.6.2) + '@polkadot/wasm-util': 7.3.2(@polkadot/util@12.6.2) + '@polkadot/x-randomvalues': 12.6.2(@polkadot/util@12.6.2)(@polkadot/wasm-util@7.3.2) tslib: 2.6.2 dev: false - /@polkadot/wasm-crypto-wasm@7.3.1(@polkadot/util@12.6.1): - resolution: {integrity: sha512-hBMRwrBLCfVsFHSdnwwIxEPshoZdW/dHehYRxMSpUdmqOxtD1gnjocXGE1KZUYGX675+EFuR+Ch6OoTKFJxwTA==} + /@polkadot/wasm-crypto-wasm@7.3.2(@polkadot/util@12.6.2): + resolution: {integrity: sha512-15wd0EMv9IXs5Abp1ZKpKKAVyZPhATIAHfKsyoWCEFDLSOA0/K0QGOxzrAlsrdUkiKZOq7uzSIgIDgW8okx2Mw==} engines: {node: '>=18'} peerDependencies: '@polkadot/util': '*' dependencies: - '@polkadot/util': 12.6.1 - '@polkadot/wasm-util': 7.3.1(@polkadot/util@12.6.1) + '@polkadot/util': 12.6.2 + '@polkadot/wasm-util': 7.3.2(@polkadot/util@12.6.2) tslib: 2.6.2 dev: false - /@polkadot/wasm-crypto@7.3.1(@polkadot/util@12.6.1)(@polkadot/x-randomvalues@12.6.1): - resolution: {integrity: sha512-BSK0YyCN4ohjtwbiHG71fgf+7ufgfLrHxjn7pKsvXhyeiEVuDhbDreNcpUf3eGOJ5tNk75aSbKGF4a3EJGIiNA==} + /@polkadot/wasm-crypto@7.3.2(@polkadot/util@12.6.2)(@polkadot/x-randomvalues@12.6.2): + resolution: {integrity: sha512-+neIDLSJ6jjVXsjyZ5oLSv16oIpwp+PxFqTUaZdZDoA2EyFRQB8pP7+qLsMNk+WJuhuJ4qXil/7XiOnZYZ+wxw==} engines: {node: '>=18'} peerDependencies: '@polkadot/util': '*' '@polkadot/x-randomvalues': '*' dependencies: - '@polkadot/util': 12.6.1 - '@polkadot/wasm-bridge': 7.3.1(@polkadot/util@12.6.1)(@polkadot/x-randomvalues@12.6.1) - '@polkadot/wasm-crypto-asmjs': 7.3.1(@polkadot/util@12.6.1) - '@polkadot/wasm-crypto-init': 7.3.1(@polkadot/util@12.6.1)(@polkadot/x-randomvalues@12.6.1) - '@polkadot/wasm-crypto-wasm': 7.3.1(@polkadot/util@12.6.1) - '@polkadot/wasm-util': 7.3.1(@polkadot/util@12.6.1) - '@polkadot/x-randomvalues': 12.6.1(@polkadot/util@12.6.1)(@polkadot/wasm-util@7.3.1) + '@polkadot/util': 12.6.2 + '@polkadot/wasm-bridge': 7.3.2(@polkadot/util@12.6.2)(@polkadot/x-randomvalues@12.6.2) + '@polkadot/wasm-crypto-asmjs': 7.3.2(@polkadot/util@12.6.2) + '@polkadot/wasm-crypto-init': 7.3.2(@polkadot/util@12.6.2)(@polkadot/x-randomvalues@12.6.2) + '@polkadot/wasm-crypto-wasm': 7.3.2(@polkadot/util@12.6.2) + '@polkadot/wasm-util': 7.3.2(@polkadot/util@12.6.2) + '@polkadot/x-randomvalues': 12.6.2(@polkadot/util@12.6.2)(@polkadot/wasm-util@7.3.2) tslib: 2.6.2 dev: false - /@polkadot/wasm-util@7.3.1(@polkadot/util@12.6.1): - resolution: {integrity: sha512-0m6ozYwBrJgnGl6QvS37ZiGRu4FFPPEtMYEVssfo1Tz4skHJlByWaHWhRNoNCVFAKiGEBu+rfx5HAQMAhoPkvg==} + /@polkadot/wasm-util@7.3.2(@polkadot/util@12.6.2): + resolution: {integrity: sha512-bmD+Dxo1lTZyZNxbyPE380wd82QsX+43mgCm40boyKrRppXEyQmWT98v/Poc7chLuskYb6X8IQ6lvvK2bGR4Tg==} engines: {node: '>=18'} peerDependencies: '@polkadot/util': '*' dependencies: - '@polkadot/util': 12.6.1 + '@polkadot/util': 12.6.2 tslib: 2.6.2 dev: false - /@polkadot/x-bigint@12.6.1: - resolution: {integrity: sha512-YlABeVIlgYQZJ4ZpW/+akFGGxw5jMGt4g5vaP7EumlORGneJHzzWJYDmI5v2y7j1zvC9ofOle7z4tRmtN/QDew==} + /@polkadot/x-bigint@12.6.2: + resolution: {integrity: sha512-HSIk60uFPX4GOFZSnIF7VYJz7WZA7tpFJsne7SzxOooRwMTWEtw3fUpFy5cYYOeLh17/kHH1Y7SVcuxzVLc74Q==} engines: {node: '>=18'} dependencies: - '@polkadot/x-global': 12.6.1 + '@polkadot/x-global': 12.6.2 tslib: 2.6.2 dev: false - /@polkadot/x-fetch@12.6.1: - resolution: {integrity: sha512-iyBv0ecfCsqGSv26CPJk9vSoKtry/Fn7x549ysA4hlc9KboraMHxOHTpcNZYC/OdgvbFZl40zIXCY0SA1ai8aw==} + /@polkadot/x-fetch@12.6.2: + resolution: {integrity: sha512-8wM/Z9JJPWN1pzSpU7XxTI1ldj/AfC8hKioBlUahZ8gUiJaOF7K9XEFCrCDLis/A1BoOu7Ne6WMx/vsJJIbDWw==} engines: {node: '>=18'} dependencies: - '@polkadot/x-global': 12.6.1 + '@polkadot/x-global': 12.6.2 node-fetch: 3.3.2 tslib: 2.6.2 dev: false - /@polkadot/x-global@12.6.1: - resolution: {integrity: sha512-w5t19HIdBPuyu7X/AiCyH2DsKqxBF0KpF4Ymolnx8PfcSIgnq9ZOmgs74McPR6FgEmeEkr9uNKujZrsfURi1ug==} + /@polkadot/x-global@12.6.2: + resolution: {integrity: sha512-a8d6m+PW98jmsYDtAWp88qS4dl8DyqUBsd0S+WgyfSMtpEXu6v9nXDgPZgwF5xdDvXhm+P0ZfVkVTnIGrScb5g==} engines: {node: '>=18'} dependencies: tslib: 2.6.2 dev: false - /@polkadot/x-randomvalues@12.6.1(@polkadot/util@12.6.1)(@polkadot/wasm-util@7.3.1): - resolution: {integrity: sha512-1uVKlfYYbgIgGV5v1Dgn960cGovenWm5pmg+aTMeUGXVYiJwRD2zOpLyC1i/tP454iA74j74pmWb8Nkn0tJZUQ==} + /@polkadot/x-randomvalues@12.6.2(@polkadot/util@12.6.2)(@polkadot/wasm-util@7.3.2): + resolution: {integrity: sha512-Vr8uG7rH2IcNJwtyf5ebdODMcr0XjoCpUbI91Zv6AlKVYOGKZlKLYJHIwpTaKKB+7KPWyQrk4Mlym/rS7v9feg==} engines: {node: '>=18'} peerDependencies: - '@polkadot/util': 12.6.1 + '@polkadot/util': 12.6.2 '@polkadot/wasm-util': '*' dependencies: - '@polkadot/util': 12.6.1 - '@polkadot/wasm-util': 7.3.1(@polkadot/util@12.6.1) - '@polkadot/x-global': 12.6.1 + '@polkadot/util': 12.6.2 + '@polkadot/wasm-util': 7.3.2(@polkadot/util@12.6.2) + '@polkadot/x-global': 12.6.2 tslib: 2.6.2 dev: false - /@polkadot/x-textdecoder@12.6.1: - resolution: {integrity: sha512-IasodJeV1f2Nr/VtA207+LXCQEqYcG8y9qB/EQcRsrEP58NbwwxM5Z2obV0lSjJOxRTJ4/OlhUwnLHwcbIp6+g==} + /@polkadot/x-textdecoder@12.6.2: + resolution: {integrity: sha512-M1Bir7tYvNappfpFWXOJcnxUhBUFWkUFIdJSyH0zs5LmFtFdbKAeiDXxSp2Swp5ddOZdZgPac294/o2TnQKN1w==} engines: {node: '>=18'} dependencies: - '@polkadot/x-global': 12.6.1 + '@polkadot/x-global': 12.6.2 tslib: 2.6.2 dev: false - /@polkadot/x-textencoder@12.6.1: - resolution: {integrity: sha512-sTq/+tXqBhGe01a1rjieSHFh3y935vuRgtahVgVJZnfqh5SmLPgSN5tTPxZWzyx7gHIfotle8laTJbJarv7V1A==} + /@polkadot/x-textencoder@12.6.2: + resolution: {integrity: sha512-4N+3UVCpI489tUJ6cv3uf0PjOHvgGp9Dl+SZRLgFGt9mvxnvpW/7+XBADRMtlG4xi5gaRK7bgl5bmY6OMDsNdw==} engines: {node: '>=18'} dependencies: - '@polkadot/x-global': 12.6.1 + '@polkadot/x-global': 12.6.2 tslib: 2.6.2 dev: false - /@polkadot/x-ws@12.6.1: - resolution: {integrity: sha512-fs9V+XekjJLpVLLwxnqq3llqSZu2T/b9brvld8anvzS/htDLPbi7+c5W3VGJ9Po8fS67IsU3HCt0Gu6F6mGrMA==} + /@polkadot/x-ws@12.6.2: + resolution: {integrity: sha512-cGZWo7K5eRRQCRl2LrcyCYsrc3lRbTlixZh3AzgU8uX4wASVGRlNWi/Hf4TtHNe1ExCDmxabJzdIsABIfrr7xw==} engines: {node: '>=18'} dependencies: - '@polkadot/x-global': 12.6.1 + '@polkadot/x-global': 12.6.2 tslib: 2.6.2 - ws: 8.14.2 + ws: 8.16.0 transitivePeerDependencies: - bufferutil - utf-8-validate dev: false - /@scure/base@1.1.3: - resolution: {integrity: sha512-/+SgoRjLq7Xlf0CWuLHq2LUZeL/w65kfzAPG5NH9pcmBhs+nunQTn4gvdwgMTIXnt9b2C/1SeL2XiysZEyIC9Q==} + /@scure/base@1.1.5: + resolution: {integrity: sha512-Brj9FiG2W1MRQSTB212YVPRrcbjkv48FoZi/u4l/zds/ieRrqsh7aUf6CLwkAq61oKXr/ZlTzlY66gLIj3TFTQ==} dev: false /@substrate/connect-extension-protocol@1.0.1: @@ -434,18 +434,18 @@ packages: dev: false optional: true - /@substrate/ss58-registry@1.44.0: - resolution: {integrity: sha512-7lQ/7mMCzVNSEfDS4BCqnRnKCFKpcOaPrxMeGTXHX1YQzM/m2BBHjbK2C3dJvjv7GYxMiaTq/HdWQj1xS6ss+A==} + /@substrate/ss58-registry@1.45.0: + resolution: {integrity: sha512-NHmOkILimbLRPKvpnR+JGVckc1q4AJjxz9FnzZ3dKAVBL17AKknDy2FxXcwlJbmOXl9u7W3FS3bLDHgfKi4tQw==} dev: false /@types/bn.js@5.1.5: resolution: {integrity: sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==} dependencies: - '@types/node': 20.9.1 + '@types/node': 20.11.5 dev: false - /@types/node@20.9.1: - resolution: {integrity: sha512-HhmzZh5LSJNS5O8jQKpJ/3ZcrrlG6L70hpGqMIAoM9YVD0YBRNWYsfwcXq8VnSjlNpCpgLzMXdiPo+dxcvSmiA==} + /@types/node@20.11.5: + resolution: {integrity: sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w==} dependencies: undici-types: 5.26.5 dev: false @@ -536,7 +536,7 @@ packages: engines: {node: ^12.20 || >= 14.13} dependencies: node-domexception: 1.0.0 - web-streams-polyfill: 3.2.1 + web-streams-polyfill: 3.3.2 dev: false /formdata-polyfill@4.0.10: @@ -627,8 +627,8 @@ packages: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} dev: false - /nock@13.3.8: - resolution: {integrity: sha512-96yVFal0c/W1lG7mmfRe7eO+hovrhJYd2obzzOZ90f6fjpeU/XNvd9cYHZKZAQJumDfhXgoTpkpJ9pvMj+hqHw==} + /nock@13.5.0: + resolution: {integrity: sha512-9hc1eCS2HtOz+sE9W7JQw/tXJktg0zoPSu48s/pYe73e25JW9ywiowbqnUSd7iZPeVawLcVpPZeZS312fwSY+g==} engines: {node: '>= 10.13'} dependencies: debug: 4.3.4 @@ -718,7 +718,7 @@ packages: resolution: {integrity: sha512-VAOBqEen6vises36/zgrmAT1GWk2qE3X8AGnO7lmQFdskbKx8EovnwS22rtPAG+Y1Rk23/S22kDJUdPANyPkBA==} requiresBuild: true dependencies: - ws: 8.14.2 + ws: 8.16.0 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -754,8 +754,8 @@ packages: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} dev: false - /web-streams-polyfill@3.2.1: - resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==} + /web-streams-polyfill@3.3.2: + resolution: {integrity: sha512-3pRGuxRF5gpuZc0W+EpwQRmCD7gRqcDOMt688KmdlDAgAyaB1XlN0zq2njfDNm44XVdIouE7pZ6GzbdyH47uIQ==} engines: {node: '>= 8'} dev: false @@ -772,8 +772,8 @@ packages: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} dev: false - /ws@8.14.2: - resolution: {integrity: sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==} + /ws@8.16.0: + resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -819,10 +819,10 @@ packages: version: 1.4.2 hasBin: true dependencies: - '@polkadot/api': 10.11.1 - '@polkadot/keyring': 12.6.1(@polkadot/util-crypto@12.6.1)(@polkadot/util@12.6.1) - '@polkadot/util': 12.6.1 - '@polkadot/util-crypto': 12.6.1(@polkadot/util@12.6.1) + '@polkadot/api': 10.11.2 + '@polkadot/keyring': 12.6.2(@polkadot/util-crypto@12.6.2)(@polkadot/util@12.6.2) + '@polkadot/util': 12.6.2 + '@polkadot/util-crypto': 12.6.2(@polkadot/util@12.6.2) lodash: 4.17.21 readline-sync: 1.4.10 shelljs: 0.8.5 diff --git a/node/src/chain_specs/litmus.rs b/node/src/chain_specs/litmus.rs index 31644e50ff..edbc86b973 100644 --- a/node/src/chain_specs/litmus.rs +++ b/node/src/chain_specs/litmus.rs @@ -237,7 +237,11 @@ fn generate_genesis( aura_ext: Default::default(), parachain_system: Default::default(), polkadot_xcm: PolkadotXcmConfig { safe_xcm_version: Some(SAFE_XCM_VERSION) }, - teerex: TeerexConfig { allow_sgx_debug_mode: true, admin: None }, + teerex: TeerexConfig { + allow_sgx_debug_mode: true, + admin: None, + skip_scheduled_enclave_check: true, + }, transaction_payment: Default::default(), tokens: Default::default(), } diff --git a/node/src/chain_specs/rococo.rs b/node/src/chain_specs/rococo.rs index 8cba6ee558..df926527eb 100644 --- a/node/src/chain_specs/rococo.rs +++ b/node/src/chain_specs/rococo.rs @@ -93,6 +93,7 @@ pub fn get_chain_spec_dev(is_standalone: bool) -> ChainSpec { ], vec![get_account_id_from_seed::("Alice")], DEFAULT_PARA_ID.into(), + true, ) }, Vec::new(), @@ -165,6 +166,7 @@ fn get_chain_spec_from_genesis_info( genesis_info_cloned.council, genesis_info_cloned.technical_committee, para_id, + false, ) }, boot_nodes @@ -194,6 +196,7 @@ fn generate_genesis( council_members: Vec, technical_committee_members: Vec, id: ParaId, + skip_scheduled_enclave_check: bool, ) -> GenesisConfig { GenesisConfig { system: SystemConfig { @@ -237,7 +240,11 @@ fn generate_genesis( parachain_system: Default::default(), polkadot_xcm: PolkadotXcmConfig { safe_xcm_version: Some(SAFE_XCM_VERSION) }, // use sudo key as genesis admin for teerex and VCMP - teerex: TeerexConfig { allow_sgx_debug_mode: true, admin: Some(root_key.clone()) }, + teerex: TeerexConfig { + allow_sgx_debug_mode: true, + admin: Some(root_key.clone()), + skip_scheduled_enclave_check, + }, vc_management: VCManagementConfig { admin: Some(root_key) }, transaction_payment: Default::default(), tokens: Default::default(), diff --git a/pallets/identity-management/Cargo.toml b/pallets/identity-management/Cargo.toml index 6a440532e5..1f281705d8 100644 --- a/pallets/identity-management/Cargo.toml +++ b/pallets/identity-management/Cargo.toml @@ -32,7 +32,7 @@ teerex-primitives = { path = "../../primitives/teerex", default-features = false [dev-dependencies] pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } pallet-group = { path = "../../pallets/group" } -pallet-teerex = { path = "../teerex", features = ["skip-scheduled-enclave-check"] } +pallet-teerex = { path = "../teerex" } pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } test-utils = { path = "../test-utils" } diff --git a/pallets/identity-management/src/mock.rs b/pallets/identity-management/src/mock.rs index ba1f914b68..cd0724aba6 100644 --- a/pallets/identity-management/src/mock.rs +++ b/pallets/identity-management/src/mock.rs @@ -183,10 +183,15 @@ pub fn new_test_ext() -> sp_io::TestExternalities { // add `5` to delegatee let _ = IdentityManagement::add_delegatee(RuntimeOrigin::root(), eddie); System::set_block_number(1); - use test_utils::ias::consts::{TEST8_CERT, TEST8_SIGNER_PUB, TEST8_TIMESTAMP, URL}; - Timestamp::set_timestamp(TEST8_TIMESTAMP); let teerex_signer: SystemAccountId = test_utils::get_signer(TEST8_SIGNER_PUB); + assert_ok!(Teerex::set_admin(RuntimeOrigin::root(), teerex_signer.clone())); + assert_ok!(Teerex::set_skip_scheduled_enclave_check( + RuntimeOrigin::signed(teerex_signer.clone()), + true + )); + + Timestamp::set_timestamp(TEST8_TIMESTAMP); if !pallet_teerex::EnclaveIndex::::contains_key(teerex_signer.clone()) { assert_ok!(Teerex::register_enclave( RuntimeOrigin::signed(teerex_signer), diff --git a/pallets/sidechain/src/mock.rs b/pallets/sidechain/src/mock.rs index 2970253699..cf9199b4d5 100644 --- a/pallets/sidechain/src/mock.rs +++ b/pallets/sidechain/src/mock.rs @@ -17,7 +17,7 @@ // Creating mock runtime here use crate as pallet_sidechain; -use frame_support::{pallet_prelude::GenesisBuild, parameter_types}; +use frame_support::{assert_ok, pallet_prelude::GenesisBuild, parameter_types}; use frame_system as system; use frame_system::EnsureRoot; use pallet_sidechain::Config; @@ -158,10 +158,21 @@ pub fn new_test_ext() -> sp_io::TestExternalities { } .assimilate_storage(&mut t) .unwrap(); - let teerex_config = pallet_teerex::GenesisConfig { allow_sgx_debug_mode: true, admin: None }; + let teerex_config = pallet_teerex::GenesisConfig { + allow_sgx_debug_mode: true, + admin: None, + skip_scheduled_enclave_check: true, + }; GenesisBuild::::assimilate_storage(&teerex_config, &mut t).unwrap(); let mut ext: sp_io::TestExternalities = t.into(); - ext.execute_with(|| System::set_block_number(1)); + ext.execute_with(|| { + System::set_block_number(1); + assert_ok!(Teerex::set_admin(RuntimeOrigin::root(), AccountKeyring::Alice.to_account_id())); + assert_ok!(Teerex::set_skip_scheduled_enclave_check( + RuntimeOrigin::signed(AccountKeyring::Alice.to_account_id()), + true + )); + }); ext } diff --git a/pallets/teeracle/Cargo.toml b/pallets/teeracle/Cargo.toml index 4625bbcd3a..9eb337f4ab 100644 --- a/pallets/teeracle/Cargo.toml +++ b/pallets/teeracle/Cargo.toml @@ -44,7 +44,7 @@ test-utils = { path = "../test-utils" } timestamp = { package = "pallet-timestamp", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } # litentry -pallet-teerex = { path = "../teerex", features = ["skip-scheduled-enclave-check"] } +pallet-teerex = { path = "../teerex" } [features] default = ["std"] diff --git a/pallets/teeracle/src/benchmarking.rs b/pallets/teeracle/src/benchmarking.rs index 2848daadd9..286c0c7a51 100644 --- a/pallets/teeracle/src/benchmarking.rs +++ b/pallets/teeracle/src/benchmarking.rs @@ -108,6 +108,11 @@ benchmarks! { TEST4_MRENCLAVE, ).unwrap(); + Teerex::::set_skip_scheduled_enclave_check( + RawOrigin::Signed(signer.clone()).into(), + true + ).unwrap(); + // simply register the enclave before to make sure it already // exists when running the benchmark Teerex::::register_enclave( diff --git a/pallets/teeracle/src/mock.rs b/pallets/teeracle/src/mock.rs index 9b6789d074..f5955bd20e 100644 --- a/pallets/teeracle/src/mock.rs +++ b/pallets/teeracle/src/mock.rs @@ -15,7 +15,7 @@ */ use crate as pallet_teeracle; -use frame_support::{pallet_prelude::GenesisBuild, parameter_types}; +use frame_support::{assert_ok, pallet_prelude::GenesisBuild, parameter_types}; use frame_system as system; use frame_system::EnsureRoot; use pallet_teeracle::Config; @@ -156,10 +156,21 @@ pub fn new_test_ext() -> sp_io::TestExternalities { } .assimilate_storage(&mut t) .unwrap(); - let teerex_config = pallet_teerex::GenesisConfig { allow_sgx_debug_mode: true, admin: None }; + let teerex_config = pallet_teerex::GenesisConfig { + allow_sgx_debug_mode: true, + admin: None, + skip_scheduled_enclave_check: true, + }; GenesisBuild::::assimilate_storage(&teerex_config, &mut t).unwrap(); let mut ext: sp_io::TestExternalities = t.into(); - ext.execute_with(|| System::set_block_number(1)); + ext.execute_with(|| { + System::set_block_number(1); + assert_ok!(Teerex::set_admin(RuntimeOrigin::root(), AccountKeyring::Alice.to_account_id())); + assert_ok!(Teerex::set_skip_scheduled_enclave_check( + RuntimeOrigin::signed(AccountKeyring::Alice.to_account_id()), + true + )); + }); ext } diff --git a/pallets/teerex/Cargo.toml b/pallets/teerex/Cargo.toml index f4c76e4081..da629927ad 100644 --- a/pallets/teerex/Cargo.toml +++ b/pallets/teerex/Cargo.toml @@ -70,7 +70,5 @@ runtime-benchmarks = [ ] # allow workers to register without remote attestation for dev purposes skip-ias-check = [] -# allow workers to register without checking the scheduled enclave, should only used in dev/tests -skip-scheduled-enclave-check = [] try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/teerex/src/lib.rs b/pallets/teerex/src/lib.rs index 860caee0ae..967811a5c3 100644 --- a/pallets/teerex/src/lib.rs +++ b/pallets/teerex/src/lib.rs @@ -105,6 +105,8 @@ pub mod pallet { new_mrenclave: MrEnclave, }, RegisteredEnclaveLimitSet(u64), + /// Flag used only in dev to skip scheduled check + SkipScheduledEnclaveCheck(bool), } #[pallet::storage] @@ -132,6 +134,10 @@ pub mod pallet { #[pallet::getter(fn enclave_count)] pub type EnclaveCount = StorageValue<_, u64, ValueQuery>; + #[pallet::storage] + #[pallet::getter(fn is_skip_scheduled_enclave)] + pub type SkipScheduledEnclaveCheck = StorageValue<_, bool, ValueQuery>; + #[pallet::storage] #[pallet::getter(fn quoting_enclave)] pub type QuotingEnclaveRegistry = StorageValue<_, QuotingEnclave, ValueQuery>; @@ -185,12 +191,13 @@ pub mod pallet { pub struct GenesisConfig { pub allow_sgx_debug_mode: bool, pub admin: Option, + pub skip_scheduled_enclave_check: bool, } #[cfg(feature = "std")] impl Default for GenesisConfig { fn default() -> Self { - Self { allow_sgx_debug_mode: false, admin: None } + Self { allow_sgx_debug_mode: false, admin: None, skip_scheduled_enclave_check: false } } } @@ -198,6 +205,7 @@ pub mod pallet { impl GenesisBuild for GenesisConfig { fn build(&self) { AllowSGXDebugMode::::put(self.allow_sgx_debug_mode); + SkipScheduledEnclaveCheck::::put(self.skip_scheduled_enclave_check); if let Some(ref admin) = self.admin { Admin::::put(admin); } @@ -282,11 +290,15 @@ pub mod pallet { // TODO: imagine this fn is not called for the first time (e.g. when worker restarts), // should we check the current sidechain_blocknumber >= registered // sidechain_blocknumber? - #[cfg(not(feature = "skip-scheduled-enclave-check"))] - ensure!( - ScheduledEnclave::::iter_values().any(|m| m == enclave.mr_enclave), - Error::::EnclaveNotInSchedule - ); + // Dev setup -> SkipScheduledEnclave Extrinsic -> Does it make sense to set in dev + // setup? + let schedule_enclave = SkipScheduledEnclaveCheck::::get(); + if !schedule_enclave { + ensure!( + ScheduledEnclave::::iter_values().any(|m| m == enclave.mr_enclave), + Error::::EnclaveNotInSchedule + ); + } Self::add_enclave(&sender, &enclave)?; Self::deposit_event(Event::AddedEnclave(sender, worker_url)); @@ -637,6 +649,23 @@ pub mod pallet { // Do not pay a fee Ok(Pays::No.into()) } + + /// This extrinsic is used to set ScheduleEnclave storage item + /// This storage item is used to perform feature control during register_enclave + /// Can only be called by the Teerex Admin + #[pallet::call_index(31)] + #[pallet::weight((195_000_000, DispatchClass::Normal, Pays::No))] + pub fn set_skip_scheduled_enclave_check( + origin: OriginFor, + should_skip: bool, + ) -> DispatchResultWithPostInfo { + let sender = ensure_signed(origin)?; + ensure!(Some(sender) == Self::admin(), Error::::RequireAdmin); + + >::set(should_skip); + Self::deposit_event(Event::SkipScheduledEnclaveCheck(should_skip)); + Ok(Pays::No.into()) + } } #[pallet::error] diff --git a/pallets/teerex/src/mock.rs b/pallets/teerex/src/mock.rs index 2b19a76d94..6d26d28e0e 100644 --- a/pallets/teerex/src/mock.rs +++ b/pallets/teerex/src/mock.rs @@ -19,6 +19,7 @@ // Creating mock runtime here use crate as pallet_teerex; use frame_support::{ + assert_ok, pallet_prelude::GenesisBuild, parameter_types, traits::{OnFinalize, OnInitialize}, @@ -153,14 +154,22 @@ pub fn new_test_ext() -> sp_io::TestExternalities { } .assimilate_storage(&mut t) .unwrap(); - let teerex_config = crate::GenesisConfig { + let teerex_config: pallet_teerex::GenesisConfig = crate::GenesisConfig { allow_sgx_debug_mode: true, admin: Some(AccountKeyring::Alice.to_account_id()), + skip_scheduled_enclave_check: true, }; GenesisBuild::::assimilate_storage(&teerex_config, &mut t).unwrap(); let mut ext: sp_io::TestExternalities = t.into(); - ext.execute_with(|| System::set_block_number(1)); + ext.execute_with(|| { + System::set_block_number(1); + assert_ok!(Teerex::set_admin(RuntimeOrigin::root(), AccountKeyring::Alice.to_account_id())); + assert_ok!(Teerex::set_skip_scheduled_enclave_check( + RuntimeOrigin::signed(AccountKeyring::Alice.to_account_id()), + true + )); + }); ext } @@ -173,11 +182,22 @@ pub fn new_test_production_ext() -> sp_io::TestExternalities { .assimilate_storage(&mut t) .unwrap(); - let teerex_config = crate::GenesisConfig { allow_sgx_debug_mode: false, admin: None }; + let teerex_config = crate::GenesisConfig { + allow_sgx_debug_mode: false, + admin: None, + skip_scheduled_enclave_check: true, + }; GenesisBuild::::assimilate_storage(&teerex_config, &mut t).unwrap(); let mut ext: sp_io::TestExternalities = t.into(); - ext.execute_with(|| System::set_block_number(1)); + ext.execute_with(|| { + System::set_block_number(1); + assert_ok!(Teerex::set_admin(RuntimeOrigin::root(), AccountKeyring::Alice.to_account_id())); + assert_ok!(Teerex::set_skip_scheduled_enclave_check( + RuntimeOrigin::signed(AccountKeyring::Alice.to_account_id()), + true + )); + }); ext } diff --git a/pallets/teerex/src/tests/mod.rs b/pallets/teerex/src/tests/mod.rs index 7824e4b794..139777a68a 100644 --- a/pallets/teerex/src/tests/mod.rs +++ b/pallets/teerex/src/tests/mod.rs @@ -16,5 +16,5 @@ */ #[cfg(feature = "skip-ias-check")] mod skip_ias_check_tests; -#[cfg(all(not(feature = "skip-ias-check"), feature = "skip-scheduled-enclave-check"))] +#[cfg(all(not(feature = "skip-ias-check")))] mod test_cases; diff --git a/pallets/teerex/src/tests/skip_ias_check_tests.rs b/pallets/teerex/src/tests/skip_ias_check_tests.rs index 81aff6b96d..1a550c74a5 100644 --- a/pallets/teerex/src/tests/skip_ias_check_tests.rs +++ b/pallets/teerex/src/tests/skip_ias_check_tests.rs @@ -35,7 +35,6 @@ fn test_enclave() -> Enclave> { } #[test] -#[cfg(feature = "skip-scheduled-enclave-check")] fn register_enclave_with_empty_mrenclave_works() { new_test_ext().execute_with(|| { assert_ok!(Teerex::register_enclave( @@ -52,7 +51,6 @@ fn register_enclave_with_empty_mrenclave_works() { } #[test] -#[cfg(feature = "skip-scheduled-enclave-check")] fn register_enclave_with_mrenclave_works() { new_test_ext().execute_with(|| { assert_ok!(Teerex::register_enclave( @@ -71,7 +69,6 @@ fn register_enclave_with_mrenclave_works() { } #[test] -#[cfg(feature = "skip-scheduled-enclave-check")] fn register_enclave_with_faulty_mrenclave_inserts_default() { new_test_ext().execute_with(|| { assert_ok!(Teerex::register_enclave( @@ -88,7 +85,6 @@ fn register_enclave_with_faulty_mrenclave_inserts_default() { } #[test] -#[cfg(feature = "skip-scheduled-enclave-check")] fn register_enclave_with_empty_url_inserts_default() { new_test_ext().execute_with(|| { assert_ok!(Teerex::register_enclave( @@ -107,7 +103,6 @@ fn register_enclave_with_empty_url_inserts_default() { } #[test] -#[cfg(not(feature = "skip-scheduled-enclave-check"))] fn register_enclave_with_scheduled_enclave_works() { new_test_ext().execute_with(|| { assert_ok!(Teerex::update_scheduled_enclave( @@ -126,11 +121,14 @@ fn register_enclave_with_scheduled_enclave_works() { } #[test] -#[cfg(not(feature = "skip-scheduled-enclave-check"))] fn register_enclave_without_scheduled_enclave_fails() { use crate::Error; use frame_support::assert_noop; new_test_ext().execute_with(|| { + assert_ok!(Teerex::set_skip_scheduled_enclave_check( + RuntimeOrigin::signed(AccountKeyring::Alice.to_account_id()), + false + )); assert_noop!( Teerex::register_enclave( RuntimeOrigin::signed(AccountKeyring::Alice.to_account_id()), diff --git a/pallets/teerex/src/tests/test_cases.rs b/pallets/teerex/src/tests/test_cases.rs index 4c4684ebd1..929381c33e 100644 --- a/pallets/teerex/src/tests/test_cases.rs +++ b/pallets/teerex/src/tests/test_cases.rs @@ -1116,7 +1116,9 @@ fn can_set_registered_enclave_limit_to_equal_actual_registered_enclaves_count() new_limit )); System::assert_last_event(TeerexEvent::RegisteredEnclaveLimitSet(new_limit).into()); - assert_eq!(System::events().len(), 1) + // Note: There are going to be 3 events in total, We are setting the admin and setting the + // skip_scheduled_enclave_check flag, which emits 2 events in total. + assert_eq!(System::events().len(), 3) }); } diff --git a/pallets/vc-management/Cargo.toml b/pallets/vc-management/Cargo.toml index 1098aa0dc6..8566ffd22d 100644 --- a/pallets/vc-management/Cargo.toml +++ b/pallets/vc-management/Cargo.toml @@ -32,7 +32,7 @@ test-utils = { path = "../test-utils", default-features = false, optional = true frame-benchmarking = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } pallet-group = { path = "../../pallets/group" } -pallet-teerex = { path = "../../pallets/teerex", features = ["skip-scheduled-enclave-check"] } +pallet-teerex = { path = "../../pallets/teerex" } pallet-timestamp = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } test-utils = { path = "../test-utils" } diff --git a/pallets/vc-management/src/mock.rs b/pallets/vc-management/src/mock.rs index f915128b1a..47c0fbd15a 100644 --- a/pallets/vc-management/src/mock.rs +++ b/pallets/vc-management/src/mock.rs @@ -181,8 +181,13 @@ pub fn new_test_ext() -> sp_io::TestExternalities { let mut ext = sp_io::TestExternalities::new(t); ext.execute_with(|| { System::set_block_number(1); - let _ = VCManagement::set_admin(RuntimeOrigin::root(), alice); + let _ = VCManagement::set_admin(RuntimeOrigin::root(), alice.clone()); let _ = VCManagement::add_delegatee(RuntimeOrigin::root(), eddie); + assert_ok!(Teerex::set_admin(RuntimeOrigin::root(), alice.clone())); + assert_ok!(Teerex::set_skip_scheduled_enclave_check( + RuntimeOrigin::signed(alice.clone()), + true + )); use test_utils::ias::consts::{TEST8_CERT, TEST8_SIGNER_PUB, TEST8_TIMESTAMP, URL}; Timestamp::set_timestamp(TEST8_TIMESTAMP); diff --git a/runtime/rococo/Cargo.toml b/runtime/rococo/Cargo.toml index 98985da3f9..9ce0f6106b 100644 --- a/runtime/rococo/Cargo.toml +++ b/runtime/rococo/Cargo.toml @@ -274,7 +274,6 @@ std = [ ] tee-dev = [ "pallet-teerex/skip-ias-check", - "pallet-teerex/skip-scheduled-enclave-check", ] try-runtime = [ "cumulus-pallet-aura-ext/try-runtime", diff --git a/runtime/rococo/src/weights/pallet_teerex.rs b/runtime/rococo/src/weights/pallet_teerex.rs index 1ac96b2733..613bc0d803 100644 --- a/runtime/rococo/src/weights/pallet_teerex.rs +++ b/runtime/rococo/src/weights/pallet_teerex.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for `pallet_teerex` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-11-30, STEPS: `20`, REPEAT: `50`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-01-07, STEPS: `20`, REPEAT: `50`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `parachain-benchmark`, CPU: `Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("rococo-dev"), DB CACHE: 20 @@ -53,8 +53,8 @@ impl pallet_teerex::WeightInfo for WeightInfo { /// Proof: Timestamp Now (max_values: Some(1), max_size: Some(8), added: 503, mode: MaxEncodedLen) /// Storage: Teerex AllowSGXDebugMode (r:1 w:0) /// Proof Skipped: Teerex AllowSGXDebugMode (max_values: Some(1), max_size: None, mode: Measured) - /// Storage: Teerex ScheduledEnclave (r:2 w:0) - /// Proof Skipped: Teerex ScheduledEnclave (max_values: None, max_size: None, mode: Measured) + /// Storage: Teerex ScheduleEnclave (r:1 w:0) + /// Proof Skipped: Teerex ScheduleEnclave (max_values: Some(1), max_size: None, mode: Measured) /// Storage: Teerex EnclaveIndex (r:1 w:0) /// Proof Skipped: Teerex EnclaveIndex (max_values: None, max_size: None, mode: Measured) /// Storage: Teerex EnclaveRegistry (r:0 w:1) @@ -62,11 +62,11 @@ impl pallet_teerex::WeightInfo for WeightInfo { fn register_enclave() -> Weight { // Proof Size summary in bytes: // Measured: `451` - // Estimated: `6391` - // Minimum execution time: 1_914_501_000 picoseconds. - Weight::from_parts(1_935_356_000, 0) - .saturating_add(Weight::from_parts(0, 6391)) - .saturating_add(T::DbWeight::get().reads(5)) + // Estimated: `3916` + // Minimum execution time: 2_014_977_000 picoseconds. + Weight::from_parts(2_084_821_000, 0) + .saturating_add(Weight::from_parts(0, 3916)) + .saturating_add(T::DbWeight::get().reads(4)) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: Teerex EnclaveIndex (r:1 w:2) @@ -79,8 +79,8 @@ impl pallet_teerex::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `491` // Estimated: `3956` - // Minimum execution time: 41_444_000 picoseconds. - Weight::from_parts(42_457_000, 0) + // Minimum execution time: 41_422_000 picoseconds. + Weight::from_parts(43_114_000, 0) .saturating_add(Weight::from_parts(0, 3956)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(5)) @@ -89,8 +89,8 @@ impl pallet_teerex::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 16_014_000 picoseconds. - Weight::from_parts(16_269_000, 0) + // Minimum execution time: 15_035_000 picoseconds. + Weight::from_parts(15_478_000, 0) .saturating_add(Weight::from_parts(0, 0)) } /// Storage: Teerex EnclaveIndex (r:1 w:0) @@ -103,8 +103,8 @@ impl pallet_teerex::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `391` // Estimated: `3856` - // Minimum execution time: 38_306_000 picoseconds. - Weight::from_parts(38_926_000, 0) + // Minimum execution time: 37_608_000 picoseconds. + Weight::from_parts(38_418_000, 0) .saturating_add(Weight::from_parts(0, 3856)) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(1)) @@ -121,13 +121,13 @@ impl pallet_teerex::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `349` // Estimated: `3814 + t * (2475 ±0)` - // Minimum execution time: 33_643_000 picoseconds. - Weight::from_parts(31_635_160, 0) + // Minimum execution time: 32_316_000 picoseconds. + Weight::from_parts(29_154_486, 0) .saturating_add(Weight::from_parts(0, 3814)) - // Standard Error: 296 - .saturating_add(Weight::from_parts(1_770, 0).saturating_mul(l.into())) - // Standard Error: 6_627 - .saturating_add(Weight::from_parts(2_628_908, 0).saturating_mul(t.into())) + // Standard Error: 1_681 + .saturating_add(Weight::from_parts(23_247, 0).saturating_mul(l.into())) + // Standard Error: 37_526 + .saturating_add(Weight::from_parts(2_784_587, 0).saturating_mul(t.into())) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(1)) diff --git a/scripts/launch-local-binary.sh b/scripts/launch-local-binary.sh index ed5272ac64..55df7b5eb7 100755 --- a/scripts/launch-local-binary.sh +++ b/scripts/launch-local-binary.sh @@ -131,6 +131,7 @@ else fi corepack pnpm install corepack pnpm run upgrade-parathread 2>&1 | tee "$LITENTRY_PARACHAIN_DIR/upgrade-parathread.log" + print_divider echo "done. please check $LITENTRY_PARACHAIN_DIR for generated files if need" diff --git a/scripts/launch-local-docker.sh b/scripts/launch-local-docker.sh index 86d7bbaf4e..51bea041b8 100755 --- a/scripts/launch-local-docker.sh +++ b/scripts/launch-local-docker.sh @@ -48,5 +48,6 @@ echo "Extending leasing period..." pnpm run upgrade-parathread 2>&1 print_divider + echo "Done." exit 0 diff --git a/ts-tests/common/setup/skip-schedule-enclave-check.ts b/ts-tests/common/setup/skip-schedule-enclave-check.ts new file mode 100644 index 0000000000..47e150d6d5 --- /dev/null +++ b/ts-tests/common/setup/skip-schedule-enclave-check.ts @@ -0,0 +1,47 @@ +import '@polkadot/api-augment'; +import { ApiPromise, Keyring, WsProvider } from '@polkadot/api'; +import { loadConfig, signAndSend } from '../utils'; +import { hexToU8a } from '@polkadot/util'; + +const mrenclave = process.argv[2]; +const block = process.argv[3]; + +async function setAliceAsAdmin(api: ApiPromise, config: any) { + // Get keyring of Alice, who is also the sudo in dev chain spec + const keyring = new Keyring({ type: 'sr25519' }); + const alice = keyring.addFromUri('//Alice'); + + const tx = api.tx.sudo.sudo(api.tx.teerex.setAdmin('esqZdrqhgH8zy1wqYh1aLKoRyoRWLFbX9M62eKfaTAoK67pJ5')); + + console.log(`Setting Alice as Admin for Teerex`); + return signAndSend(tx, alice); +} + +async function updateSkipScheduledEnclaveCheck(api: ApiPromise, config: any) { + const keyring = new Keyring({ type: 'sr25519' }); + const alice = keyring.addFromUri('//Alice'); + + const tx = api.tx.teerex.setSkipScheduledEnclaveCheck(true); + + console.log('set Skip Schedule Enclave Extrinsic sent'); + return signAndSend(tx, alice); +} + +(async () => { + console.log('Schedule enclave on parachain ...'); + const config = loadConfig(); + + const provider = new WsProvider(config.parachain_ws); + const api = await ApiPromise.create({ + provider: provider, + }); + + await setAliceAsAdmin(api, config); + await updateSkipScheduledEnclaveCheck(api, config); + + await api.disconnect(); + provider.on('disconnected', () => { + console.log('Disconnect from relaychain'); + process.exit(0); + }); +})(); diff --git a/ts-tests/package.json b/ts-tests/package.json index c5237b64f8..06412a4233 100644 --- a/ts-tests/package.json +++ b/ts-tests/package.json @@ -8,6 +8,7 @@ }, "scripts": { "register-parathread": "pnpm exec ts-node common/setup/register-parathread.ts", + "skip-schedule-enclave-check": "pnpm exec ts-node common/setup/skip-schedule-enclave-check.ts", "upgrade-parathread": "pnpm exec ts-node common/setup/upgrade-parathread.ts", "wait-finalized-block": "pnpm exec ts-node common/setup/wait-finalized-block.ts", "setup-enclave": "pnpm exec ts-node common/setup/setup-enclave.ts", From 208379670754c55342b65eb06cc979ab0627ee35 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 21:45:06 +0100 Subject: [PATCH 23/51] Bump proc-macro2 from 1.0.76 to 1.0.78 (#2415) Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.76 to 1.0.78. - [Release notes](https://github.com/dtolnay/proc-macro2/releases) - [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.76...1.0.78) --- updated-dependencies: - dependency-name: proc-macro2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b2af255f9c..271fb76d96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9866,9 +9866,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.76" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] From 14f53084bb615ae609d513366427271f9debf07c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 23:32:51 +0100 Subject: [PATCH 24/51] Bump shlex from 1.1.0 to 1.3.0 (#2416) Bumps [shlex](https://github.com/comex/rust-shlex) from 1.1.0 to 1.3.0. - [Changelog](https://github.com/comex/rust-shlex/blob/master/CHANGELOG.md) - [Commits](https://github.com/comex/rust-shlex/commits) --- updated-dependencies: - dependency-name: shlex dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: BillyWooo --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 271fb76d96..45eebffcca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12423,9 +12423,9 @@ dependencies = [ [[package]] name = "shlex" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "sidechain-primitives" From 72d6b341d8ddc5f3aaa69786780cb8d526116f12 Mon Sep 17 00:00:00 2001 From: "will.li" <120463031+higherordertech@users.noreply.github.com> Date: Wed, 24 Jan 2024 19:03:33 +1100 Subject: [PATCH 25/51] fix: P-381 VC typo (#2420) Co-authored-by: higherordertech --- .../nodereal/bnb_domain/bnb_domain_holding_amount.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tee-worker/litentry/core/credentials/src/nodereal/bnb_domain/bnb_domain_holding_amount.rs b/tee-worker/litentry/core/credentials/src/nodereal/bnb_domain/bnb_domain_holding_amount.rs index c61b342150..4d5611648b 100644 --- a/tee-worker/litentry/core/credentials/src/nodereal/bnb_domain/bnb_domain_holding_amount.rs +++ b/tee-worker/litentry/core/credentials/src/nodereal/bnb_domain/bnb_domain_holding_amount.rs @@ -25,17 +25,17 @@ const BNB_DOMAIN_HOLDING_AMOUNT_INFOS: (&str, &str) = // [x-y) pub const BNB_DOMAIN_HOLDING_AMOUNT_RANGE: [usize; 8] = [0, 1, 5, 10, 20, 50, 100, 200]; -pub struct BnbDomainHodingAmount { +pub struct BnbDomainHoldingAmount { pub amount: usize, } -impl BnbDomainHodingAmount { +impl BnbDomainHoldingAmount { pub fn new(amount: usize) -> Self { Self { amount } } } -impl RangeCredentialDetail for BnbDomainHodingAmount { +impl RangeCredentialDetail for BnbDomainHoldingAmount { fn get_info(&self) -> (&'static str, &'static str) { BNB_DOMAIN_HOLDING_AMOUNT_INFOS } @@ -49,7 +49,7 @@ impl RangeCredentialDetail for BnbDomainHodingAmount { } fn get_breakdown(&self) -> &'static str { - "$bnb_domain_hoding_amount" + "$bnb_domain_holding_amount" } } @@ -59,7 +59,7 @@ pub trait UpdateBnbDomainHoldingAmountCredential { impl UpdateBnbDomainHoldingAmountCredential for Credential { fn update_bnb_holding_amount(&mut self, amount: usize) { - let bnb_amount = BnbDomainHodingAmount::new(amount); + let bnb_amount = BnbDomainHoldingAmount::new(amount); let items = bnb_amount.get_assertion_items(amount); let mut assertion = AssertionLogic::new_and(); for item in items { From 128fb1e80fce4ebdd84a83806921c6ca7423beef Mon Sep 17 00:00:00 2001 From: BillyWooo Date: Wed, 24 Jan 2024 15:02:56 +0100 Subject: [PATCH 26/51] refactor of environment variables (#2417) * refactor of environment variables * revert yml file changs --- tee-worker/.env.dev | 37 ------------- .../litentry/core/data-providers/src/lib.rs | 6 +-- tee-worker/local-setup/.env.dev | 52 +++++++++++++++++++ tee-worker/local-setup/.env.example | 13 ----- tee-worker/local-setup/rococo_one_worker.json | 2 - 5 files changed, 55 insertions(+), 55 deletions(-) delete mode 100644 tee-worker/.env.dev create mode 100644 tee-worker/local-setup/.env.dev delete mode 100644 tee-worker/local-setup/.env.example diff --git a/tee-worker/.env.dev b/tee-worker/.env.dev deleted file mode 100644 index 65aa47f85d..0000000000 --- a/tee-worker/.env.dev +++ /dev/null @@ -1,37 +0,0 @@ -AliceWSPort=9946 -AliceRPCPort=9936 -AlicePort=30336 -BobWSPort=9947 -BobRPCPort=9937 -BobPort=30337 -CollatorWSPort=9944 -CollatorRPCPort=9933 -CollatorPort=30333 -TrustedWorkerPort=2000 -UntrustedWorkerPort=2001 -MuRaPort=3443 -UntrustedHttpPort=4545 -NODE_ENV=local -# tee-worker dataproviders config -TWITTER_OFFICIAL_URL=http://localhost:19527 -TWITTER_LITENTRY_URL=http://localhost:19527 -TWITTER_AUTH_TOKEN_V2= -DISCORD_OFFICIAL_URL=http://localhost:19527 -DISCORD_LITENTRY_URL=http://localhost:19527 -DISCORD_AUTH_TOKEN= -ACHAINABLE_URL=http://localhost:19527 -ACHAINABLE_AUTH_KEY= -CREDENTIAL_ENDPOINT=http://localhost:9933 -ONEBLOCK_NOTION_KEY=ABCDEFGHIJKLMNOPQRSTUVWXYZ -ONEBLOCK_NOTION_URL=https://abc.com -SORA_QUIZ_MASTER_ID=SORA_QUIZ_MASTER_ID -SORA_QUIZ_ATTENDEE_ID=SORA_QUIZ_ATTENDEE_ID -NODEREAL_API_KEY=NODEREAL_API_KEY -NODEREAL_API_URL=https://open-platform.nodereal.io/ -NODEREAL_API_CHAIN_NETWORK_URL= -CONTEST_LEGEND_DISCORD_ROLE_ID=CONTEST_LEGEND_DISCORD_ROLE_ID -CONTEST_POPULARITY_DISCORD_ROLE_ID=CONTEST_POPULARITY_DISCORD_ROLE_ID -CONTEST_PARTICIPANT_DISCORD_ROLE_ID=CONTEST_PARTICIPANT_DISCORD_ROLE_ID -VIP3_URL=https://dappapi.vip3.io/ -GENIIDATA_URL=https://api.geniidata.com/api/1/brc20/balance? -GENIIDATA_API_KEY=142cf1b0-1ca7-11ee-bb5e-9d74c2e854ac \ No newline at end of file diff --git a/tee-worker/litentry/core/data-providers/src/lib.rs b/tee-worker/litentry/core/data-providers/src/lib.rs index f8bc98526b..936203826e 100644 --- a/tee-worker/litentry/core/data-providers/src/lib.rs +++ b/tee-worker/litentry/core/data-providers/src/lib.rs @@ -211,18 +211,18 @@ impl DataProviderConfig { discord_auth_token: "".to_string(), achainable_url: "https://label-production.graph.tdf-labs.io/".to_string(), achainable_auth_key: "".to_string(), - credential_endpoint: "wss://tee-staging.litentry.io".to_string(), + credential_endpoint: "wss://rpc.rococo-parachain.litentry.io".to_string(), oneblock_notion_key: "".to_string(), oneblock_notion_url: "https://api.notion.com/v1/blocks/e4068e6a326243468f35dcdc0c43f686/children" .to_string(), sora_quiz_master_id: "1164463721989554218".to_string(), sora_quiz_attendee_id: "1166941149219532800".to_string(), - nodereal_api_key: "https://{chain}-{network}.nodereal.io/".to_string(), + nodereal_api_key: "".to_string(), nodereal_api_retry_delay: 5000, nodereal_api_retry_times: 2, nodereal_api_url: "https://open-platform.nodereal.io/".to_string(), - nodereal_api_chain_network_url: "".to_string(), + nodereal_api_chain_network_url: "https://{chain}-{network}.nodereal.io/".to_string(), contest_legend_discord_role_id: "1172576273063739462".to_string(), contest_popularity_discord_role_id: "1172576681119195208".to_string(), contest_participant_discord_role_id: "1172576734135210104".to_string(), diff --git a/tee-worker/local-setup/.env.dev b/tee-worker/local-setup/.env.dev new file mode 100644 index 0000000000..4dee670bfd --- /dev/null +++ b/tee-worker/local-setup/.env.dev @@ -0,0 +1,52 @@ + +## ----------------------------------- +# litentry-worker start options value +## ----------------------------------- +# The followings are default value. +# Can be skipped; or overwrite within non-production mode. +AliceWSPort=9946 +AliceRPCPort=9936 +AlicePort=30336 +BobWSPort=9947 +BobRPCPort=9937 +BobPort=30337 +CollatorWSPort=9944 +CollatorRPCPort=9933 +CollatorPort=30333 +TrustedWorkerPort=2000 +UntrustedWorkerPort=2001 +MuRaPort=3443 +UntrustedHttpPort=4545 +NODE_ENV=local + +## ----------------------------------- +# litentry-worker dataproviders config +## ----------------------------------- + +# The following key/token are MANDATORY to give when running worker. +# Otherwise request-vc might suffering from data provider error. +TWITTER_AUTH_TOKEN_V2= +DISCORD_AUTH_TOKEN= +ACHAINABLE_AUTH_KEY= +ONEBLOCK_NOTION_KEY= +NODEREAL_API_KEY= +GENIIDATA_API_KEY=142cf1b0-1ca7-11ee-bb5e-9d74c2e854ac + +# The followings are default value. +# Can be skipped; or overwrite within non-production mode. +TWITTER_OFFICIAL_URL=https://api.twitter.com +TWITTER_LITENTRY_URL=http://localhost:19527 +DISCORD_OFFICIAL_URL=https://discordapp.com +DISCORD_LITENTRY_URL=http://localhost:19527 +ACHAINABLE_URL=https://label-production.graph.tdf-labs.io/ +CREDENTIAL_ENDPOINT=wss://rpc.rococo-parachain.litentry.io +ONEBLOCK_NOTION_URL=https://api.notion.com/v1/blocks/e4068e6a326243468f35dcdc0c43f686/children +SORA_QUIZ_MASTER_ID=1164463721989554218 +SORA_QUIZ_ATTENDEE_ID=1166941149219532800 +NODEREAL_API_URL=https://open-platform.nodereal.io/ +NODEREAL_API_CHAIN_NETWORK_URL=https://{chain}-{network}.nodereal.io/ +CONTEST_LEGEND_DISCORD_ROLE_ID=1172576273063739462 +CONTEST_POPULARITY_DISCORD_ROLE_ID=1172576681119195208 +CONTEST_PARTICIPANT_DISCORD_ROLE_ID=1172576734135210104 +VIP3_URL=https://dappapi.vip3.io/ +GENIIDATA_URL=https://api.geniidata.com/api/1/brc20/balance? \ No newline at end of file diff --git a/tee-worker/local-setup/.env.example b/tee-worker/local-setup/.env.example deleted file mode 100644 index b8fab39de9..0000000000 --- a/tee-worker/local-setup/.env.example +++ /dev/null @@ -1,13 +0,0 @@ -AliceWSPort=9946 -AliceRPCPort=9936 -AlicePort=30336 -BobWSPort=9947 -BobRPCPort=9937 -BobPort=30337 -CollatorWSPort=9944 -CollatorRPCPort=9933 -CollatorPort=30333 -TrustedWorkerPort=2000 -UntrustedWorkerPort=2001 -MuRaPort=3443 -UntrustedHttpPort=4545 \ No newline at end of file diff --git a/tee-worker/local-setup/rococo_one_worker.json b/tee-worker/local-setup/rococo_one_worker.json index 10e0e556cc..72093f1099 100644 --- a/tee-worker/local-setup/rococo_one_worker.json +++ b/tee-worker/local-setup/rococo_one_worker.json @@ -17,8 +17,6 @@ "wss://rpc.rococo-parachain.litentry.io", "-p", "443", - "--running-mode", - "mock", "--parentchain-start-block", "3299860" ], From 1f9f496a926559fe4eaa94eb319b8c18a2ff4777 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Jan 2024 17:36:09 +0100 Subject: [PATCH 27/51] Bump h2 from 0.3.20 to 0.3.24 (#2411) Bumps [h2](https://github.com/hyperium/h2) from 0.3.20 to 0.3.24. - [Release notes](https://github.com/hyperium/h2/releases) - [Changelog](https://github.com/hyperium/h2/blob/v0.3.24/CHANGELOG.md) - [Commits](https://github.com/hyperium/h2/compare/v0.3.20...v0.3.24) --- updated-dependencies: - dependency-name: h2 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Kai <7630809+Kailai-Wang@users.noreply.github.com> Co-authored-by: BillyWooo --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 45eebffcca..0530c430ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3861,9 +3861,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.20" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" +checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" dependencies = [ "bytes", "fnv", @@ -3871,7 +3871,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 1.9.3", + "indexmap 2.0.0", "slab", "tokio", "tokio-util", From d80ef201ac1babba2ef838146df8e80ca95da430 Mon Sep 17 00:00:00 2001 From: "will.li" <120463031+higherordertech@users.noreply.github.com> Date: Thu, 25 Jan 2024 03:36:52 +1100 Subject: [PATCH 28/51] chore: P-433 updated copyright (#2422) Co-authored-by: higherordertech --- LICENSE_HEADER | 2 +- node/build.rs | 2 +- node/src/chain_specs/litentry.rs | 2 +- node/src/chain_specs/litmus.rs | 2 +- node/src/chain_specs/mod.rs | 2 +- node/src/chain_specs/rococo.rs | 2 +- node/src/cli.rs | 2 +- node/src/command.rs | 2 +- node/src/evm_tracing_types.rs | 2 +- node/src/main.rs | 2 +- node/src/rpc.rs | 2 +- node/src/service.rs | 2 +- node/src/standalone_block_import.rs | 2 +- node/src/tracing.rs | 2 +- pallets/bridge-transfer/src/benchmarking.rs | 2 +- pallets/bridge-transfer/src/lib.rs | 2 +- pallets/bridge-transfer/src/mock.rs | 2 +- pallets/bridge-transfer/src/tests.rs | 2 +- pallets/bridge-transfer/src/weights.rs | 2 +- pallets/bridge/src/benchmarking.rs | 2 +- pallets/bridge/src/hashing.rs | 2 +- pallets/bridge/src/lib.rs | 2 +- pallets/bridge/src/mock.rs | 2 +- pallets/bridge/src/tests.rs | 2 +- pallets/bridge/src/weights.rs | 2 +- pallets/drop3/src/benchmarking.rs | 2 +- pallets/drop3/src/lib.rs | 2 +- pallets/drop3/src/mock.rs | 2 +- pallets/drop3/src/tests.rs | 2 +- pallets/drop3/src/weights.rs | 2 +- pallets/evm-address/src/lib.rs | 2 +- pallets/evm-address/src/mock.rs | 2 +- pallets/evm-address/src/tests.rs | 2 +- pallets/extrinsic-filter/src/benchmarking.rs | 2 +- pallets/extrinsic-filter/src/lib.rs | 2 +- pallets/extrinsic-filter/src/mock.rs | 2 +- pallets/extrinsic-filter/src/tests.rs | 2 +- pallets/extrinsic-filter/src/weights.rs | 2 +- pallets/group/src/lib.rs | 2 +- pallets/group/src/mock.rs | 2 +- pallets/group/src/tests.rs | 2 +- pallets/identity-management/src/benchmarking.rs | 2 +- pallets/identity-management/src/lib.rs | 2 +- pallets/identity-management/src/mock.rs | 2 +- pallets/identity-management/src/tests.rs | 2 +- pallets/identity-management/src/weights.rs | 2 +- pallets/parachain-staking/src/auto_compound.rs | 2 +- pallets/parachain-staking/src/benchmarking.rs | 2 +- pallets/parachain-staking/src/delegation_requests.rs | 2 +- pallets/parachain-staking/src/inflation.rs | 2 +- pallets/parachain-staking/src/lib.rs | 2 +- pallets/parachain-staking/src/mock.rs | 2 +- pallets/parachain-staking/src/set.rs | 2 +- pallets/parachain-staking/src/tests.rs | 2 +- pallets/parachain-staking/src/traits.rs | 2 +- pallets/parachain-staking/src/types.rs | 2 +- pallets/parachain-staking/src/weights.rs | 2 +- pallets/sidechain/src/weights.rs | 2 +- pallets/teeracle/src/weights.rs | 2 +- pallets/teerex/src/weights.rs | 2 +- pallets/vc-management/src/benchmarking.rs | 2 +- pallets/vc-management/src/lib.rs | 2 +- pallets/vc-management/src/mock.rs | 2 +- pallets/vc-management/src/schema.rs | 2 +- pallets/vc-management/src/tests.rs | 2 +- pallets/vc-management/src/weights.rs | 2 +- pallets/xcm-asset-manager/src/benchmarking.rs | 2 +- pallets/xcm-asset-manager/src/lib.rs | 2 +- pallets/xcm-asset-manager/src/mock.rs | 2 +- pallets/xcm-asset-manager/src/tests.rs | 2 +- pallets/xcm-asset-manager/src/weights.rs | 2 +- precompiles/bridge-transfer/src/lib.rs | 2 +- precompiles/bridge-transfer/src/mock.rs | 2 +- precompiles/bridge-transfer/src/tests.rs | 2 +- precompiles/parachain-staking/src/lib.rs | 2 +- precompiles/parachain-staking/src/mock.rs | 2 +- precompiles/parachain-staking/src/tests.rs | 2 +- precompiles/utils/macro/src/lib.rs | 2 +- precompiles/utils/macro/tests/tests.rs | 2 +- precompiles/utils/src/data.rs | 2 +- precompiles/utils/src/lib.rs | 2 +- precompiles/utils/src/testing.rs | 2 +- precompiles/utils/src/tests.rs | 2 +- primitives/core/macros/src/lib.rs | 2 +- primitives/core/macros/src/reuse.rs | 2 +- primitives/core/src/assertion.rs | 2 +- primitives/core/src/bnb_domain.rs | 2 +- primitives/core/src/contest.rs | 2 +- primitives/core/src/error.rs | 2 +- primitives/core/src/evm_amount_holding.rs | 2 +- primitives/core/src/generic_discord_role.rs | 2 +- primitives/core/src/identity.rs | 2 +- primitives/core/src/lib.rs | 2 +- primitives/core/src/network.rs | 2 +- primitives/core/src/oneblock.rs | 2 +- primitives/core/src/soraquiz.rs | 2 +- primitives/core/src/vc.rs | 2 +- primitives/core/src/vip3.rs | 2 +- runtime/common/src/lib.rs | 2 +- runtime/common/src/tests/base_call_filter.rs | 2 +- runtime/common/src/tests/mod.rs | 2 +- runtime/common/src/tests/orml_xcm.rs | 2 +- runtime/common/src/tests/setup/mod.rs | 2 +- runtime/common/src/tests/setup/relay.rs | 2 +- runtime/common/src/tests/transaction_payment.rs | 2 +- runtime/common/src/tests/xcm_parachain/mod.rs | 2 +- runtime/common/src/tests/xcm_parachain/relay_sproof_builder.rs | 2 +- runtime/common/src/xcm_impl.rs | 2 +- runtime/litentry/build.rs | 2 +- runtime/litentry/src/constants.rs | 2 +- runtime/litentry/src/lib.rs | 2 +- runtime/litentry/src/tests/base_call_filter.rs | 2 +- runtime/litentry/src/tests/mod.rs | 2 +- runtime/litentry/src/tests/orml_xcm.rs | 2 +- runtime/litentry/src/tests/transaction_payment.rs | 2 +- runtime/litentry/src/weights/cumulus_pallet_xcmp_queue.rs | 2 +- runtime/litentry/src/weights/frame_system.rs | 2 +- runtime/litentry/src/weights/mod.rs | 2 +- runtime/litentry/src/weights/pallet_asset_manager.rs | 2 +- runtime/litentry/src/weights/pallet_balances.rs | 2 +- runtime/litentry/src/weights/pallet_bridge.rs | 2 +- runtime/litentry/src/weights/pallet_bridge_transfer.rs | 2 +- runtime/litentry/src/weights/pallet_collective.rs | 2 +- runtime/litentry/src/weights/pallet_democracy.rs | 2 +- runtime/litentry/src/weights/pallet_drop3.rs | 2 +- runtime/litentry/src/weights/pallet_extrinsic_filter.rs | 2 +- runtime/litentry/src/weights/pallet_membership.rs | 2 +- runtime/litentry/src/weights/pallet_multisig.rs | 2 +- runtime/litentry/src/weights/pallet_parachain_staking.rs | 2 +- runtime/litentry/src/weights/pallet_preimage.rs | 2 +- runtime/litentry/src/weights/pallet_proxy.rs | 2 +- runtime/litentry/src/weights/pallet_scheduler.rs | 2 +- runtime/litentry/src/weights/pallet_session.rs | 2 +- runtime/litentry/src/weights/pallet_timestamp.rs | 2 +- runtime/litentry/src/weights/pallet_treasury.rs | 2 +- runtime/litentry/src/weights/pallet_utility.rs | 2 +- runtime/litentry/src/xcm_config.rs | 2 +- runtime/litmus/build.rs | 2 +- runtime/litmus/src/constants.rs | 2 +- runtime/litmus/src/lib.rs | 2 +- runtime/litmus/src/tests/base_call_filter.rs | 2 +- runtime/litmus/src/tests/mod.rs | 2 +- runtime/litmus/src/tests/orml_xcm.rs | 2 +- runtime/litmus/src/weights/cumulus_pallet_xcmp_queue.rs | 2 +- runtime/litmus/src/weights/frame_system.rs | 2 +- runtime/litmus/src/weights/mod.rs | 2 +- runtime/litmus/src/weights/pallet_asset_manager.rs | 2 +- runtime/litmus/src/weights/pallet_balances.rs | 2 +- runtime/litmus/src/weights/pallet_bridge.rs | 2 +- runtime/litmus/src/weights/pallet_bridge_transfer.rs | 2 +- runtime/litmus/src/weights/pallet_collator_selection.rs | 2 +- runtime/litmus/src/weights/pallet_collective.rs | 2 +- runtime/litmus/src/weights/pallet_democracy.rs | 2 +- runtime/litmus/src/weights/pallet_drop3.rs | 2 +- runtime/litmus/src/weights/pallet_extrinsic_filter.rs | 2 +- runtime/litmus/src/weights/pallet_identity_management.rs | 2 +- runtime/litmus/src/weights/pallet_membership.rs | 2 +- runtime/litmus/src/weights/pallet_multisig.rs | 2 +- runtime/litmus/src/weights/pallet_preimage.rs | 2 +- runtime/litmus/src/weights/pallet_proxy.rs | 2 +- runtime/litmus/src/weights/pallet_scheduler.rs | 2 +- runtime/litmus/src/weights/pallet_session.rs | 2 +- runtime/litmus/src/weights/pallet_sidechain.rs | 2 +- runtime/litmus/src/weights/pallet_teeracle.rs | 2 +- runtime/litmus/src/weights/pallet_teerex.rs | 2 +- runtime/litmus/src/weights/pallet_timestamp.rs | 2 +- runtime/litmus/src/weights/pallet_treasury.rs | 2 +- runtime/litmus/src/weights/pallet_utility.rs | 2 +- runtime/litmus/src/xcm_config.rs | 2 +- runtime/rococo/build.rs | 2 +- runtime/rococo/src/constants.rs | 2 +- runtime/rococo/src/lib.rs | 2 +- runtime/rococo/src/precompiles.rs | 2 +- runtime/rococo/src/tests/base_call_filter.rs | 2 +- runtime/rococo/src/tests/mod.rs | 2 +- runtime/rococo/src/tests/orml_xcm.rs | 2 +- runtime/rococo/src/weights/cumulus_pallet_xcmp_queue.rs | 2 +- runtime/rococo/src/weights/frame_system.rs | 2 +- runtime/rococo/src/weights/mod.rs | 2 +- runtime/rococo/src/weights/pallet_asset_manager.rs | 2 +- runtime/rococo/src/weights/pallet_balances.rs | 2 +- runtime/rococo/src/weights/pallet_bridge.rs | 2 +- runtime/rococo/src/weights/pallet_bridge_transfer.rs | 2 +- runtime/rococo/src/weights/pallet_collective.rs | 2 +- runtime/rococo/src/weights/pallet_democracy.rs | 2 +- runtime/rococo/src/weights/pallet_drop3.rs | 2 +- runtime/rococo/src/weights/pallet_extrinsic_filter.rs | 2 +- runtime/rococo/src/weights/pallet_identity_management.rs | 2 +- runtime/rococo/src/weights/pallet_membership.rs | 2 +- runtime/rococo/src/weights/pallet_multisig.rs | 2 +- runtime/rococo/src/weights/pallet_parachain_staking.rs | 2 +- runtime/rococo/src/weights/pallet_preimage.rs | 2 +- runtime/rococo/src/weights/pallet_proxy.rs | 2 +- runtime/rococo/src/weights/pallet_scheduler.rs | 2 +- runtime/rococo/src/weights/pallet_session.rs | 2 +- runtime/rococo/src/weights/pallet_sidechain.rs | 2 +- runtime/rococo/src/weights/pallet_teeracle.rs | 2 +- runtime/rococo/src/weights/pallet_teerex.rs | 2 +- runtime/rococo/src/weights/pallet_timestamp.rs | 2 +- runtime/rococo/src/weights/pallet_treasury.rs | 2 +- runtime/rococo/src/weights/pallet_utility.rs | 2 +- runtime/rococo/src/weights/pallet_vc_management.rs | 2 +- runtime/rococo/src/xcm_config.rs | 2 +- .../src/indirect_calls/litentry/activate_identity.rs | 2 +- .../src/indirect_calls/litentry/args_executor.rs | 2 +- .../src/indirect_calls/litentry/deactivate_identity.rs | 2 +- .../src/indirect_calls/litentry/link_identity.rs | 2 +- .../parentchain-interface/src/indirect_calls/litentry/mod.rs | 2 +- .../src/indirect_calls/litentry/request_vc.rs | 2 +- .../src/indirect_calls/litentry/scheduled_enclave.rs | 2 +- tee-worker/app-libs/stf/src/trusted_call_litentry.rs | 2 +- tee-worker/app-libs/stf/src/trusted_call_result.rs | 2 +- tee-worker/cli/lit_id_graph_stats.sh | 2 +- tee-worker/cli/lit_parentchain_nonce.sh | 2 +- tee-worker/cli/lit_set_heartbeat_timeout.sh | 2 +- tee-worker/cli/lit_ts_api_package_build.sh | 2 +- tee-worker/cli/lit_ts_integration_test.sh | 2 +- tee-worker/cli/lit_ts_worker_test.sh | 2 +- tee-worker/cli/src/base_cli/commands/litentry/id_graph_hash.rs | 2 +- tee-worker/cli/src/base_cli/commands/litentry/link_identity.rs | 2 +- tee-worker/cli/src/base_cli/commands/litentry/mod.rs | 2 +- .../cli/src/base_cli/commands/litentry/set_heartbeat_timeout.rs | 2 +- .../cli/src/trusted_base_cli/commands/litentry/get_storage.rs | 2 +- .../cli/src/trusted_base_cli/commands/litentry/id_graph.rs | 2 +- .../src/trusted_base_cli/commands/litentry/id_graph_stats.rs | 2 +- .../cli/src/trusted_base_cli/commands/litentry/link_identity.rs | 2 +- tee-worker/cli/src/trusted_base_cli/commands/litentry/mod.rs | 2 +- .../src/trusted_base_cli/commands/litentry/remove_identity.rs | 2 +- .../cli/src/trusted_base_cli/commands/litentry/request_vc.rs | 2 +- .../src/trusted_base_cli/commands/litentry/request_vc_direct.rs | 2 +- .../commands/litentry/send_erroneous_parentchain_call.rs | 2 +- tee-worker/core-primitives/node-api/metadata/src/pallet_imp.rs | 2 +- .../core-primitives/node-api/metadata/src/pallet_utility.rs | 2 +- tee-worker/core-primitives/node-api/metadata/src/pallet_vcmp.rs | 2 +- .../core-primitives/node-api/metadata/src/runtime_call.rs | 2 +- tee-worker/core-primitives/stf-interface/src/runtime_upgrade.rs | 2 +- tee-worker/core-primitives/utils/src/macros.rs | 2 +- tee-worker/core/direct-rpc-client/src/lib.rs | 2 +- tee-worker/core/peer-top-broadcaster/src/lib.rs | 2 +- tee-worker/enclave-runtime/src/test/mocks/peer_updater_mock.rs | 2 +- tee-worker/litentry/core/assertion-build/src/a1.rs | 2 +- tee-worker/litentry/core/assertion-build/src/a13.rs | 2 +- tee-worker/litentry/core/assertion-build/src/a14.rs | 2 +- tee-worker/litentry/core/assertion-build/src/a2.rs | 2 +- tee-worker/litentry/core/assertion-build/src/a20.rs | 2 +- tee-worker/litentry/core/assertion-build/src/a3.rs | 2 +- tee-worker/litentry/core/assertion-build/src/a6.rs | 2 +- tee-worker/litentry/core/assertion-build/src/a8.rs | 2 +- .../litentry/core/assertion-build/src/achainable/amount.rs | 2 +- .../core/assertion-build/src/achainable/amount_holding.rs | 2 +- .../core/assertion-build/src/achainable/amount_token.rs | 2 +- .../litentry/core/assertion-build/src/achainable/amounts.rs | 2 +- .../litentry/core/assertion-build/src/achainable/basic.rs | 2 +- .../core/assertion-build/src/achainable/between_percents.rs | 2 +- .../core/assertion-build/src/achainable/class_of_year.rs | 2 +- tee-worker/litentry/core/assertion-build/src/achainable/date.rs | 2 +- .../core/assertion-build/src/achainable/date_interval.rs | 2 +- .../core/assertion-build/src/achainable/date_percent.rs | 2 +- .../litentry/core/assertion-build/src/achainable/mirror.rs | 2 +- tee-worker/litentry/core/assertion-build/src/achainable/mod.rs | 2 +- .../litentry/core/assertion-build/src/achainable/token.rs | 2 +- .../litentry/core/assertion-build/src/brc20/amount_holder.rs | 2 +- tee-worker/litentry/core/assertion-build/src/brc20/mod.rs | 2 +- .../litentry/core/assertion-build/src/generic_discord_role.rs | 2 +- tee-worker/litentry/core/assertion-build/src/holding_time.rs | 2 +- tee-worker/litentry/core/assertion-build/src/lib.rs | 2 +- tee-worker/litentry/core/assertion-build/src/lit_staking.rs | 2 +- .../src/nodereal/amount_holding/evm_amount_holding.rs | 2 +- .../core/assertion-build/src/nodereal/amount_holding/mod.rs | 2 +- .../src/nodereal/bnb_domain/bnb_digit_domain_club_amount.rs | 2 +- .../src/nodereal/bnb_domain/bnb_domain_holding_amount.rs | 2 +- .../core/assertion-build/src/nodereal/bnb_domain/mod.rs | 2 +- .../core/assertion-build/src/nodereal/crypto_summary/mod.rs | 2 +- tee-worker/litentry/core/assertion-build/src/nodereal/mod.rs | 2 +- .../core/assertion-build/src/nodereal/nft_holder/mod.rs | 2 +- .../src/nodereal/nft_holder/weirdo_ghost_gang_holder.rs | 2 +- tee-worker/litentry/core/assertion-build/src/oneblock/course.rs | 2 +- tee-worker/litentry/core/assertion-build/src/oneblock/mod.rs | 2 +- tee-worker/litentry/core/assertion-build/src/vip3/card.rs | 2 +- tee-worker/litentry/core/assertion-build/src/vip3/mod.rs | 2 +- .../core/credentials/src/achainable/amount_holding_time.rs | 2 +- .../litentry/core/credentials/src/achainable/bab_holder.rs | 2 +- .../core/credentials/src/achainable/lit_holding_amount.rs | 2 +- tee-worker/litentry/core/credentials/src/achainable/mod.rs | 2 +- .../litentry/core/credentials/src/achainable/uniswap_user.rs | 2 +- tee-worker/litentry/core/credentials/src/assertion_logic.rs | 2 +- tee-worker/litentry/core/credentials/src/brc20/amount_holder.rs | 2 +- tee-worker/litentry/core/credentials/src/brc20/mod.rs | 2 +- tee-worker/litentry/core/credentials/src/error.rs | 2 +- .../litentry/core/credentials/src/generic_discord_role/mod.rs | 2 +- tee-worker/litentry/core/credentials/src/lib.rs | 2 +- .../core/credentials/src/litentry_profile/holding_amount.rs | 2 +- .../core/credentials/src/litentry_profile/lit_staking.rs | 2 +- .../litentry/core/credentials/src/litentry_profile/mirror.rs | 2 +- .../litentry/core/credentials/src/litentry_profile/mod.rs | 2 +- .../core/credentials/src/litentry_profile/token_balance.rs | 2 +- .../src/nodereal/amount_holding/evm_amount_holding.rs | 2 +- .../core/credentials/src/nodereal/amount_holding/mod.rs | 2 +- .../src/nodereal/bnb_domain/bnb_digit_domain_club_amount.rs | 2 +- .../src/nodereal/bnb_domain/bnb_domain_holding_amount.rs | 2 +- .../litentry/core/credentials/src/nodereal/bnb_domain/mod.rs | 2 +- .../core/credentials/src/nodereal/crypto_summary/mod.rs | 2 +- .../core/credentials/src/nodereal/crypto_summary/summary.rs | 2 +- tee-worker/litentry/core/credentials/src/nodereal/mod.rs | 2 +- .../litentry/core/credentials/src/nodereal/nft_holder/mod.rs | 2 +- .../src/nodereal/nft_holder/weirdo_ghost_gang_holder.rs | 2 +- tee-worker/litentry/core/credentials/src/oneblock/mod.rs | 2 +- tee-worker/litentry/core/credentials/src/schema.rs | 2 +- tee-worker/litentry/core/credentials/src/vip3.rs | 2 +- tee-worker/litentry/core/data-providers/src/achainable.rs | 2 +- tee-worker/litentry/core/data-providers/src/achainable_names.rs | 2 +- tee-worker/litentry/core/data-providers/src/discord_litentry.rs | 2 +- tee-worker/litentry/core/data-providers/src/discord_official.rs | 2 +- tee-worker/litentry/core/data-providers/src/geniidata.rs | 2 +- tee-worker/litentry/core/data-providers/src/lib.rs | 2 +- tee-worker/litentry/core/data-providers/src/nodereal.rs | 2 +- tee-worker/litentry/core/data-providers/src/nodereal_jsonrpc.rs | 2 +- tee-worker/litentry/core/data-providers/src/twitter_official.rs | 2 +- tee-worker/litentry/core/data-providers/src/vip3.rs | 2 +- tee-worker/litentry/core/identity-verification/src/error.rs | 2 +- tee-worker/litentry/core/identity-verification/src/lib.rs | 2 +- tee-worker/litentry/core/identity-verification/src/web2/mod.rs | 2 +- tee-worker/litentry/core/mock-server/src/achainable.rs | 2 +- tee-worker/litentry/core/mock-server/src/discord_litentry.rs | 2 +- tee-worker/litentry/core/mock-server/src/discord_official.rs | 2 +- tee-worker/litentry/core/mock-server/src/lib.rs | 2 +- tee-worker/litentry/core/mock-server/src/nodereal_jsonrpc.rs | 2 +- tee-worker/litentry/core/mock-server/src/twitter_litentry.rs | 2 +- tee-worker/litentry/core/mock-server/src/twitter_official.rs | 2 +- tee-worker/litentry/core/scheduled-enclave/src/error.rs | 2 +- tee-worker/litentry/core/scheduled-enclave/src/io.rs | 2 +- tee-worker/litentry/core/scheduled-enclave/src/lib.rs | 2 +- .../litentry/core/stf-task/receiver/src/handler/assertion.rs | 2 +- .../core/stf-task/receiver/src/handler/identity_verification.rs | 2 +- tee-worker/litentry/core/stf-task/receiver/src/handler/mod.rs | 2 +- tee-worker/litentry/core/stf-task/receiver/src/lib.rs | 2 +- tee-worker/litentry/core/stf-task/sender/src/error.rs | 2 +- tee-worker/litentry/core/stf-task/sender/src/lib.rs | 2 +- tee-worker/litentry/core/stf-task/sender/src/stf_task_sender.rs | 2 +- tee-worker/litentry/macros/src/lib.rs | 2 +- .../pallets/identity-management/src/identity_context.rs | 2 +- tee-worker/litentry/pallets/identity-management/src/lib.rs | 2 +- .../litentry/pallets/identity-management/src/migrations.rs | 2 +- tee-worker/litentry/pallets/identity-management/src/mock.rs | 2 +- tee-worker/litentry/pallets/identity-management/src/tests.rs | 2 +- tee-worker/litentry/primitives/src/aes.rs | 2 +- tee-worker/litentry/primitives/src/aes_request.rs | 2 +- tee-worker/litentry/primitives/src/bitcoin_signature.rs | 2 +- tee-worker/litentry/primitives/src/ethereum_signature.rs | 2 +- tee-worker/litentry/primitives/src/lib.rs | 2 +- tee-worker/litentry/primitives/src/validation_data.rs | 2 +- 351 files changed, 351 insertions(+), 351 deletions(-) diff --git a/LICENSE_HEADER b/LICENSE_HEADER index e65bdadb3c..20182fa371 100644 --- a/LICENSE_HEADER +++ b/LICENSE_HEADER @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/node/build.rs b/node/build.rs index efc8251108..fd49029c74 100644 --- a/node/build.rs +++ b/node/build.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/node/src/chain_specs/litentry.rs b/node/src/chain_specs/litentry.rs index b7e4602d2a..93a585d2a7 100644 --- a/node/src/chain_specs/litentry.rs +++ b/node/src/chain_specs/litentry.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/node/src/chain_specs/litmus.rs b/node/src/chain_specs/litmus.rs index edbc86b973..b5f0ef40b8 100644 --- a/node/src/chain_specs/litmus.rs +++ b/node/src/chain_specs/litmus.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/node/src/chain_specs/mod.rs b/node/src/chain_specs/mod.rs index 3740793c3d..5d8e532105 100644 --- a/node/src/chain_specs/mod.rs +++ b/node/src/chain_specs/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/node/src/chain_specs/rococo.rs b/node/src/chain_specs/rococo.rs index df926527eb..0e6e80facf 100644 --- a/node/src/chain_specs/rococo.rs +++ b/node/src/chain_specs/rococo.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/node/src/cli.rs b/node/src/cli.rs index 29c1407eb5..3d55c2c0e0 100644 --- a/node/src/cli.rs +++ b/node/src/cli.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/node/src/command.rs b/node/src/command.rs index 6f0de38e02..f90146a631 100644 --- a/node/src/command.rs +++ b/node/src/command.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/node/src/evm_tracing_types.rs b/node/src/evm_tracing_types.rs index 944710489e..fdd321e604 100644 --- a/node/src/evm_tracing_types.rs +++ b/node/src/evm_tracing_types.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/node/src/main.rs b/node/src/main.rs index 1281be9469..bfdba099d8 100644 --- a/node/src/main.rs +++ b/node/src/main.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/node/src/rpc.rs b/node/src/rpc.rs index d353c71081..7b22863481 100644 --- a/node/src/rpc.rs +++ b/node/src/rpc.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/node/src/service.rs b/node/src/service.rs index 44ae29fdc5..8948e0a104 100644 --- a/node/src/service.rs +++ b/node/src/service.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/node/src/standalone_block_import.rs b/node/src/standalone_block_import.rs index 10aaee4411..353631f436 100644 --- a/node/src/standalone_block_import.rs +++ b/node/src/standalone_block_import.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/node/src/tracing.rs b/node/src/tracing.rs index 13e4c06733..3ee1f7bd17 100644 --- a/node/src/tracing.rs +++ b/node/src/tracing.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/bridge-transfer/src/benchmarking.rs b/pallets/bridge-transfer/src/benchmarking.rs index 5a2c33b391..8878a53a7a 100644 --- a/pallets/bridge-transfer/src/benchmarking.rs +++ b/pallets/bridge-transfer/src/benchmarking.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/bridge-transfer/src/lib.rs b/pallets/bridge-transfer/src/lib.rs index 1bd75616e2..4a7056bb4e 100644 --- a/pallets/bridge-transfer/src/lib.rs +++ b/pallets/bridge-transfer/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/bridge-transfer/src/mock.rs b/pallets/bridge-transfer/src/mock.rs index eff99af095..691a643011 100644 --- a/pallets/bridge-transfer/src/mock.rs +++ b/pallets/bridge-transfer/src/mock.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/bridge-transfer/src/tests.rs b/pallets/bridge-transfer/src/tests.rs index 89429bcc38..3bf3b55a3c 100644 --- a/pallets/bridge-transfer/src/tests.rs +++ b/pallets/bridge-transfer/src/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/bridge-transfer/src/weights.rs b/pallets/bridge-transfer/src/weights.rs index 9e4af31a0d..bb986d8926 100644 --- a/pallets/bridge-transfer/src/weights.rs +++ b/pallets/bridge-transfer/src/weights.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/bridge/src/benchmarking.rs b/pallets/bridge/src/benchmarking.rs index ff265d10d2..4f2855ec8f 100644 --- a/pallets/bridge/src/benchmarking.rs +++ b/pallets/bridge/src/benchmarking.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/bridge/src/hashing.rs b/pallets/bridge/src/hashing.rs index b5cbe9ae45..140142a951 100644 --- a/pallets/bridge/src/hashing.rs +++ b/pallets/bridge/src/hashing.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/bridge/src/lib.rs b/pallets/bridge/src/lib.rs index 04bf6e314f..b0c77be869 100644 --- a/pallets/bridge/src/lib.rs +++ b/pallets/bridge/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/bridge/src/mock.rs b/pallets/bridge/src/mock.rs index f07f4f5ca8..5da367a9d5 100644 --- a/pallets/bridge/src/mock.rs +++ b/pallets/bridge/src/mock.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/bridge/src/tests.rs b/pallets/bridge/src/tests.rs index 4a1ce01364..1acbd196ff 100644 --- a/pallets/bridge/src/tests.rs +++ b/pallets/bridge/src/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/bridge/src/weights.rs b/pallets/bridge/src/weights.rs index 45c3fea3a1..78903b785f 100644 --- a/pallets/bridge/src/weights.rs +++ b/pallets/bridge/src/weights.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/drop3/src/benchmarking.rs b/pallets/drop3/src/benchmarking.rs index d4f6a4dd85..f862f61f31 100644 --- a/pallets/drop3/src/benchmarking.rs +++ b/pallets/drop3/src/benchmarking.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/drop3/src/lib.rs b/pallets/drop3/src/lib.rs index 30bcd4d4d2..96e63760bb 100644 --- a/pallets/drop3/src/lib.rs +++ b/pallets/drop3/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/drop3/src/mock.rs b/pallets/drop3/src/mock.rs index ccfc8c57b9..4ddef36714 100644 --- a/pallets/drop3/src/mock.rs +++ b/pallets/drop3/src/mock.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/drop3/src/tests.rs b/pallets/drop3/src/tests.rs index 7bf9cefe10..21d65a90d9 100644 --- a/pallets/drop3/src/tests.rs +++ b/pallets/drop3/src/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/drop3/src/weights.rs b/pallets/drop3/src/weights.rs index 44fded0090..640de1ec4c 100644 --- a/pallets/drop3/src/weights.rs +++ b/pallets/drop3/src/weights.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/evm-address/src/lib.rs b/pallets/evm-address/src/lib.rs index 92dcd1a391..7bfdd2ea8b 100644 --- a/pallets/evm-address/src/lib.rs +++ b/pallets/evm-address/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/evm-address/src/mock.rs b/pallets/evm-address/src/mock.rs index c26fa438c8..d067dba3fe 100644 --- a/pallets/evm-address/src/mock.rs +++ b/pallets/evm-address/src/mock.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/evm-address/src/tests.rs b/pallets/evm-address/src/tests.rs index 06824991cf..79f6c75cdc 100644 --- a/pallets/evm-address/src/tests.rs +++ b/pallets/evm-address/src/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/extrinsic-filter/src/benchmarking.rs b/pallets/extrinsic-filter/src/benchmarking.rs index 1ffe901e85..f3f1325316 100644 --- a/pallets/extrinsic-filter/src/benchmarking.rs +++ b/pallets/extrinsic-filter/src/benchmarking.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/extrinsic-filter/src/lib.rs b/pallets/extrinsic-filter/src/lib.rs index f0a0c613ea..52cad598db 100644 --- a/pallets/extrinsic-filter/src/lib.rs +++ b/pallets/extrinsic-filter/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/extrinsic-filter/src/mock.rs b/pallets/extrinsic-filter/src/mock.rs index 174aba1ef2..ff3b8c07b2 100644 --- a/pallets/extrinsic-filter/src/mock.rs +++ b/pallets/extrinsic-filter/src/mock.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/extrinsic-filter/src/tests.rs b/pallets/extrinsic-filter/src/tests.rs index e17853ce4b..109dd8e711 100644 --- a/pallets/extrinsic-filter/src/tests.rs +++ b/pallets/extrinsic-filter/src/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/extrinsic-filter/src/weights.rs b/pallets/extrinsic-filter/src/weights.rs index c2c9a4a6e2..d60c77498f 100644 --- a/pallets/extrinsic-filter/src/weights.rs +++ b/pallets/extrinsic-filter/src/weights.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/group/src/lib.rs b/pallets/group/src/lib.rs index abf978e45a..cf07808b43 100644 --- a/pallets/group/src/lib.rs +++ b/pallets/group/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/group/src/mock.rs b/pallets/group/src/mock.rs index 4c80d25455..955768bab5 100644 --- a/pallets/group/src/mock.rs +++ b/pallets/group/src/mock.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/group/src/tests.rs b/pallets/group/src/tests.rs index a28f3a5eb5..50645d825c 100644 --- a/pallets/group/src/tests.rs +++ b/pallets/group/src/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/identity-management/src/benchmarking.rs b/pallets/identity-management/src/benchmarking.rs index 0e9bbea396..6ba386bd27 100644 --- a/pallets/identity-management/src/benchmarking.rs +++ b/pallets/identity-management/src/benchmarking.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/identity-management/src/lib.rs b/pallets/identity-management/src/lib.rs index b5051b5641..c876f045ee 100644 --- a/pallets/identity-management/src/lib.rs +++ b/pallets/identity-management/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/identity-management/src/mock.rs b/pallets/identity-management/src/mock.rs index cd0724aba6..7a4fcfdfbf 100644 --- a/pallets/identity-management/src/mock.rs +++ b/pallets/identity-management/src/mock.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/identity-management/src/tests.rs b/pallets/identity-management/src/tests.rs index 147d2c1240..4db8abf85d 100644 --- a/pallets/identity-management/src/tests.rs +++ b/pallets/identity-management/src/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/identity-management/src/weights.rs b/pallets/identity-management/src/weights.rs index 706ae3e421..aa4a2c8ec1 100644 --- a/pallets/identity-management/src/weights.rs +++ b/pallets/identity-management/src/weights.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/parachain-staking/src/auto_compound.rs b/pallets/parachain-staking/src/auto_compound.rs index 78586baf55..df15b021d1 100644 --- a/pallets/parachain-staking/src/auto_compound.rs +++ b/pallets/parachain-staking/src/auto_compound.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/parachain-staking/src/benchmarking.rs b/pallets/parachain-staking/src/benchmarking.rs index 93f447cc4e..de52f97278 100644 --- a/pallets/parachain-staking/src/benchmarking.rs +++ b/pallets/parachain-staking/src/benchmarking.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/parachain-staking/src/delegation_requests.rs b/pallets/parachain-staking/src/delegation_requests.rs index e01b7edf34..6ca8e3abe2 100644 --- a/pallets/parachain-staking/src/delegation_requests.rs +++ b/pallets/parachain-staking/src/delegation_requests.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/parachain-staking/src/inflation.rs b/pallets/parachain-staking/src/inflation.rs index 3f2dda8513..7f4b525d53 100644 --- a/pallets/parachain-staking/src/inflation.rs +++ b/pallets/parachain-staking/src/inflation.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/parachain-staking/src/lib.rs b/pallets/parachain-staking/src/lib.rs index dfd2452860..3b65396adb 100644 --- a/pallets/parachain-staking/src/lib.rs +++ b/pallets/parachain-staking/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/parachain-staking/src/mock.rs b/pallets/parachain-staking/src/mock.rs index 4d110cb318..c4e38862d5 100644 --- a/pallets/parachain-staking/src/mock.rs +++ b/pallets/parachain-staking/src/mock.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/parachain-staking/src/set.rs b/pallets/parachain-staking/src/set.rs index 5be41d01a4..100b4ead39 100644 --- a/pallets/parachain-staking/src/set.rs +++ b/pallets/parachain-staking/src/set.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/parachain-staking/src/tests.rs b/pallets/parachain-staking/src/tests.rs index e5a1619277..24e44fe1d5 100644 --- a/pallets/parachain-staking/src/tests.rs +++ b/pallets/parachain-staking/src/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/parachain-staking/src/traits.rs b/pallets/parachain-staking/src/traits.rs index c069c5c3aa..f8ee21824b 100644 --- a/pallets/parachain-staking/src/traits.rs +++ b/pallets/parachain-staking/src/traits.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/parachain-staking/src/types.rs b/pallets/parachain-staking/src/types.rs index 7dd17e7bbb..2d7c5b7ea2 100644 --- a/pallets/parachain-staking/src/types.rs +++ b/pallets/parachain-staking/src/types.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/parachain-staking/src/weights.rs b/pallets/parachain-staking/src/weights.rs index d70fc850df..59df88d052 100644 --- a/pallets/parachain-staking/src/weights.rs +++ b/pallets/parachain-staking/src/weights.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/sidechain/src/weights.rs b/pallets/sidechain/src/weights.rs index fc6682af97..ed2a1937d3 100644 --- a/pallets/sidechain/src/weights.rs +++ b/pallets/sidechain/src/weights.rs @@ -15,7 +15,7 @@ */ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/teeracle/src/weights.rs b/pallets/teeracle/src/weights.rs index ce753d60c9..cd3798a18f 100644 --- a/pallets/teeracle/src/weights.rs +++ b/pallets/teeracle/src/weights.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/teerex/src/weights.rs b/pallets/teerex/src/weights.rs index 73bb275d5e..9c8e080f5e 100644 --- a/pallets/teerex/src/weights.rs +++ b/pallets/teerex/src/weights.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/vc-management/src/benchmarking.rs b/pallets/vc-management/src/benchmarking.rs index 58b55a65b7..4d3e8762b6 100644 --- a/pallets/vc-management/src/benchmarking.rs +++ b/pallets/vc-management/src/benchmarking.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/vc-management/src/lib.rs b/pallets/vc-management/src/lib.rs index 1f97ac95cf..d59b6f3791 100644 --- a/pallets/vc-management/src/lib.rs +++ b/pallets/vc-management/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/vc-management/src/mock.rs b/pallets/vc-management/src/mock.rs index 47c0fbd15a..9ac8374515 100644 --- a/pallets/vc-management/src/mock.rs +++ b/pallets/vc-management/src/mock.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/vc-management/src/schema.rs b/pallets/vc-management/src/schema.rs index 394eb73218..dbd172399a 100644 --- a/pallets/vc-management/src/schema.rs +++ b/pallets/vc-management/src/schema.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/vc-management/src/tests.rs b/pallets/vc-management/src/tests.rs index 9b41b840a4..6b15029310 100644 --- a/pallets/vc-management/src/tests.rs +++ b/pallets/vc-management/src/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/vc-management/src/weights.rs b/pallets/vc-management/src/weights.rs index 10f541d305..f16cb2a432 100644 --- a/pallets/vc-management/src/weights.rs +++ b/pallets/vc-management/src/weights.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/xcm-asset-manager/src/benchmarking.rs b/pallets/xcm-asset-manager/src/benchmarking.rs index a6acf0aeb6..19e8694ef7 100644 --- a/pallets/xcm-asset-manager/src/benchmarking.rs +++ b/pallets/xcm-asset-manager/src/benchmarking.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/xcm-asset-manager/src/lib.rs b/pallets/xcm-asset-manager/src/lib.rs index ae5f4c159a..5c521d35d7 100644 --- a/pallets/xcm-asset-manager/src/lib.rs +++ b/pallets/xcm-asset-manager/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/xcm-asset-manager/src/mock.rs b/pallets/xcm-asset-manager/src/mock.rs index 4460dfc5a0..2d227f72f8 100644 --- a/pallets/xcm-asset-manager/src/mock.rs +++ b/pallets/xcm-asset-manager/src/mock.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/xcm-asset-manager/src/tests.rs b/pallets/xcm-asset-manager/src/tests.rs index 7870f83f97..fafcd26140 100644 --- a/pallets/xcm-asset-manager/src/tests.rs +++ b/pallets/xcm-asset-manager/src/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/pallets/xcm-asset-manager/src/weights.rs b/pallets/xcm-asset-manager/src/weights.rs index 90c5ef1c1a..83605dfff6 100644 --- a/pallets/xcm-asset-manager/src/weights.rs +++ b/pallets/xcm-asset-manager/src/weights.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/precompiles/bridge-transfer/src/lib.rs b/precompiles/bridge-transfer/src/lib.rs index 77df916842..38f50298d2 100644 --- a/precompiles/bridge-transfer/src/lib.rs +++ b/precompiles/bridge-transfer/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/precompiles/bridge-transfer/src/mock.rs b/precompiles/bridge-transfer/src/mock.rs index 403d512db3..76227e5119 100644 --- a/precompiles/bridge-transfer/src/mock.rs +++ b/precompiles/bridge-transfer/src/mock.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/precompiles/bridge-transfer/src/tests.rs b/precompiles/bridge-transfer/src/tests.rs index 1d038bcdc9..9efc096cc4 100644 --- a/precompiles/bridge-transfer/src/tests.rs +++ b/precompiles/bridge-transfer/src/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/precompiles/parachain-staking/src/lib.rs b/precompiles/parachain-staking/src/lib.rs index 4641d90054..f15de55612 100644 --- a/precompiles/parachain-staking/src/lib.rs +++ b/precompiles/parachain-staking/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/precompiles/parachain-staking/src/mock.rs b/precompiles/parachain-staking/src/mock.rs index 5d28b1698d..0513e7e91a 100644 --- a/precompiles/parachain-staking/src/mock.rs +++ b/precompiles/parachain-staking/src/mock.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/precompiles/parachain-staking/src/tests.rs b/precompiles/parachain-staking/src/tests.rs index 7baa50506a..66d540af1c 100644 --- a/precompiles/parachain-staking/src/tests.rs +++ b/precompiles/parachain-staking/src/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/precompiles/utils/macro/src/lib.rs b/precompiles/utils/macro/src/lib.rs index 145d6d5ecd..2499a22015 100644 --- a/precompiles/utils/macro/src/lib.rs +++ b/precompiles/utils/macro/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/precompiles/utils/macro/tests/tests.rs b/precompiles/utils/macro/tests/tests.rs index e719a0f7cc..76ab10e68e 100644 --- a/precompiles/utils/macro/tests/tests.rs +++ b/precompiles/utils/macro/tests/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/precompiles/utils/src/data.rs b/precompiles/utils/src/data.rs index b03c5d33c8..104d4606f9 100644 --- a/precompiles/utils/src/data.rs +++ b/precompiles/utils/src/data.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/precompiles/utils/src/lib.rs b/precompiles/utils/src/lib.rs index 6f502b723e..8187b7427e 100644 --- a/precompiles/utils/src/lib.rs +++ b/precompiles/utils/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/precompiles/utils/src/testing.rs b/precompiles/utils/src/testing.rs index c26c7bbb5c..efabdf94fa 100644 --- a/precompiles/utils/src/testing.rs +++ b/precompiles/utils/src/testing.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/precompiles/utils/src/tests.rs b/precompiles/utils/src/tests.rs index 0529ca80ec..043ef89ea9 100644 --- a/precompiles/utils/src/tests.rs +++ b/precompiles/utils/src/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/primitives/core/macros/src/lib.rs b/primitives/core/macros/src/lib.rs index 5af6fc6d22..e988af4ccb 100644 --- a/primitives/core/macros/src/lib.rs +++ b/primitives/core/macros/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/primitives/core/macros/src/reuse.rs b/primitives/core/macros/src/reuse.rs index aed2a39d86..d0c120d88f 100644 --- a/primitives/core/macros/src/reuse.rs +++ b/primitives/core/macros/src/reuse.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/primitives/core/src/assertion.rs b/primitives/core/src/assertion.rs index 8c280b3fe6..777757f62e 100644 --- a/primitives/core/src/assertion.rs +++ b/primitives/core/src/assertion.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/primitives/core/src/bnb_domain.rs b/primitives/core/src/bnb_domain.rs index 369b5e669c..285d408013 100644 --- a/primitives/core/src/bnb_domain.rs +++ b/primitives/core/src/bnb_domain.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/primitives/core/src/contest.rs b/primitives/core/src/contest.rs index 386d04c180..4e8e7d3098 100644 --- a/primitives/core/src/contest.rs +++ b/primitives/core/src/contest.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/primitives/core/src/error.rs b/primitives/core/src/error.rs index 6e93ebe6ea..0853463377 100644 --- a/primitives/core/src/error.rs +++ b/primitives/core/src/error.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/primitives/core/src/evm_amount_holding.rs b/primitives/core/src/evm_amount_holding.rs index d4afdd495d..fc28979158 100644 --- a/primitives/core/src/evm_amount_holding.rs +++ b/primitives/core/src/evm_amount_holding.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/primitives/core/src/generic_discord_role.rs b/primitives/core/src/generic_discord_role.rs index 3b454315ef..1df476de3f 100644 --- a/primitives/core/src/generic_discord_role.rs +++ b/primitives/core/src/generic_discord_role.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/primitives/core/src/identity.rs b/primitives/core/src/identity.rs index 35902f4660..bd55c6425f 100644 --- a/primitives/core/src/identity.rs +++ b/primitives/core/src/identity.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/primitives/core/src/lib.rs b/primitives/core/src/lib.rs index c41e64cbe3..2183646d9c 100644 --- a/primitives/core/src/lib.rs +++ b/primitives/core/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/primitives/core/src/network.rs b/primitives/core/src/network.rs index 48258458e6..4ac7ee8df3 100644 --- a/primitives/core/src/network.rs +++ b/primitives/core/src/network.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/primitives/core/src/oneblock.rs b/primitives/core/src/oneblock.rs index beb0733577..d9347d02ba 100644 --- a/primitives/core/src/oneblock.rs +++ b/primitives/core/src/oneblock.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/primitives/core/src/soraquiz.rs b/primitives/core/src/soraquiz.rs index c7732468e8..71f89da2f9 100644 --- a/primitives/core/src/soraquiz.rs +++ b/primitives/core/src/soraquiz.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/primitives/core/src/vc.rs b/primitives/core/src/vc.rs index ad73ecea2f..121b0a2b95 100644 --- a/primitives/core/src/vc.rs +++ b/primitives/core/src/vc.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/primitives/core/src/vip3.rs b/primitives/core/src/vip3.rs index fdeacb4cad..a612d281a6 100644 --- a/primitives/core/src/vip3.rs +++ b/primitives/core/src/vip3.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 5122f1a105..0297efb21a 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/common/src/tests/base_call_filter.rs b/runtime/common/src/tests/base_call_filter.rs index b531c1597a..a6a4209d84 100644 --- a/runtime/common/src/tests/base_call_filter.rs +++ b/runtime/common/src/tests/base_call_filter.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/common/src/tests/mod.rs b/runtime/common/src/tests/mod.rs index 07d2dd64db..98342ef527 100644 --- a/runtime/common/src/tests/mod.rs +++ b/runtime/common/src/tests/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/common/src/tests/orml_xcm.rs b/runtime/common/src/tests/orml_xcm.rs index aeae8bd43a..bf176567b6 100644 --- a/runtime/common/src/tests/orml_xcm.rs +++ b/runtime/common/src/tests/orml_xcm.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/common/src/tests/setup/mod.rs b/runtime/common/src/tests/setup/mod.rs index d61b40a213..c3221b2ed4 100644 --- a/runtime/common/src/tests/setup/mod.rs +++ b/runtime/common/src/tests/setup/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/common/src/tests/setup/relay.rs b/runtime/common/src/tests/setup/relay.rs index d7a03302b4..7d92279790 100644 --- a/runtime/common/src/tests/setup/relay.rs +++ b/runtime/common/src/tests/setup/relay.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/common/src/tests/transaction_payment.rs b/runtime/common/src/tests/transaction_payment.rs index 6377e4139e..4686aa892e 100644 --- a/runtime/common/src/tests/transaction_payment.rs +++ b/runtime/common/src/tests/transaction_payment.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/common/src/tests/xcm_parachain/mod.rs b/runtime/common/src/tests/xcm_parachain/mod.rs index d4b9baf14c..e8e60753eb 100644 --- a/runtime/common/src/tests/xcm_parachain/mod.rs +++ b/runtime/common/src/tests/xcm_parachain/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/common/src/tests/xcm_parachain/relay_sproof_builder.rs b/runtime/common/src/tests/xcm_parachain/relay_sproof_builder.rs index da8e3da8c7..31cbe2f5b1 100644 --- a/runtime/common/src/tests/xcm_parachain/relay_sproof_builder.rs +++ b/runtime/common/src/tests/xcm_parachain/relay_sproof_builder.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/common/src/xcm_impl.rs b/runtime/common/src/xcm_impl.rs index c564814575..ac46a8c684 100644 --- a/runtime/common/src/xcm_impl.rs +++ b/runtime/common/src/xcm_impl.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litentry/build.rs b/runtime/litentry/build.rs index e44c15a588..3ccbec6b26 100644 --- a/runtime/litentry/build.rs +++ b/runtime/litentry/build.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litentry/src/constants.rs b/runtime/litentry/src/constants.rs index b722f107f8..b24f3ddb51 100644 --- a/runtime/litentry/src/constants.rs +++ b/runtime/litentry/src/constants.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litentry/src/lib.rs b/runtime/litentry/src/lib.rs index eef82fda31..6c557175f8 100644 --- a/runtime/litentry/src/lib.rs +++ b/runtime/litentry/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litentry/src/tests/base_call_filter.rs b/runtime/litentry/src/tests/base_call_filter.rs index e6c8f0a9a6..03523bab00 100644 --- a/runtime/litentry/src/tests/base_call_filter.rs +++ b/runtime/litentry/src/tests/base_call_filter.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litentry/src/tests/mod.rs b/runtime/litentry/src/tests/mod.rs index e2e4be459e..127fa728ee 100644 --- a/runtime/litentry/src/tests/mod.rs +++ b/runtime/litentry/src/tests/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litentry/src/tests/orml_xcm.rs b/runtime/litentry/src/tests/orml_xcm.rs index 102be611a5..52495baa5f 100644 --- a/runtime/litentry/src/tests/orml_xcm.rs +++ b/runtime/litentry/src/tests/orml_xcm.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litentry/src/tests/transaction_payment.rs b/runtime/litentry/src/tests/transaction_payment.rs index e1e45bef20..1466d95e06 100644 --- a/runtime/litentry/src/tests/transaction_payment.rs +++ b/runtime/litentry/src/tests/transaction_payment.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litentry/src/weights/cumulus_pallet_xcmp_queue.rs b/runtime/litentry/src/weights/cumulus_pallet_xcmp_queue.rs index 639c6eaad3..962a3b95e9 100644 --- a/runtime/litentry/src/weights/cumulus_pallet_xcmp_queue.rs +++ b/runtime/litentry/src/weights/cumulus_pallet_xcmp_queue.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litentry/src/weights/frame_system.rs b/runtime/litentry/src/weights/frame_system.rs index 65d66bf86f..1d18c15897 100644 --- a/runtime/litentry/src/weights/frame_system.rs +++ b/runtime/litentry/src/weights/frame_system.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litentry/src/weights/mod.rs b/runtime/litentry/src/weights/mod.rs index ff4cdedae0..91e65a5b49 100644 --- a/runtime/litentry/src/weights/mod.rs +++ b/runtime/litentry/src/weights/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litentry/src/weights/pallet_asset_manager.rs b/runtime/litentry/src/weights/pallet_asset_manager.rs index 52416afcbf..c37bb5646b 100644 --- a/runtime/litentry/src/weights/pallet_asset_manager.rs +++ b/runtime/litentry/src/weights/pallet_asset_manager.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litentry/src/weights/pallet_balances.rs b/runtime/litentry/src/weights/pallet_balances.rs index 94f29651ce..63f03f6943 100644 --- a/runtime/litentry/src/weights/pallet_balances.rs +++ b/runtime/litentry/src/weights/pallet_balances.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litentry/src/weights/pallet_bridge.rs b/runtime/litentry/src/weights/pallet_bridge.rs index f1be7107ea..d856d6c374 100644 --- a/runtime/litentry/src/weights/pallet_bridge.rs +++ b/runtime/litentry/src/weights/pallet_bridge.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litentry/src/weights/pallet_bridge_transfer.rs b/runtime/litentry/src/weights/pallet_bridge_transfer.rs index e76b701d13..39a778eaad 100644 --- a/runtime/litentry/src/weights/pallet_bridge_transfer.rs +++ b/runtime/litentry/src/weights/pallet_bridge_transfer.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litentry/src/weights/pallet_collective.rs b/runtime/litentry/src/weights/pallet_collective.rs index 3aa0d0b956..c809238d35 100644 --- a/runtime/litentry/src/weights/pallet_collective.rs +++ b/runtime/litentry/src/weights/pallet_collective.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litentry/src/weights/pallet_democracy.rs b/runtime/litentry/src/weights/pallet_democracy.rs index 6d53237b82..ea7938f31c 100644 --- a/runtime/litentry/src/weights/pallet_democracy.rs +++ b/runtime/litentry/src/weights/pallet_democracy.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litentry/src/weights/pallet_drop3.rs b/runtime/litentry/src/weights/pallet_drop3.rs index f886277438..3cbc4782e4 100644 --- a/runtime/litentry/src/weights/pallet_drop3.rs +++ b/runtime/litentry/src/weights/pallet_drop3.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litentry/src/weights/pallet_extrinsic_filter.rs b/runtime/litentry/src/weights/pallet_extrinsic_filter.rs index db9edcdb3f..44834f1738 100644 --- a/runtime/litentry/src/weights/pallet_extrinsic_filter.rs +++ b/runtime/litentry/src/weights/pallet_extrinsic_filter.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litentry/src/weights/pallet_membership.rs b/runtime/litentry/src/weights/pallet_membership.rs index bcfb8aabf8..15e60c0e5a 100644 --- a/runtime/litentry/src/weights/pallet_membership.rs +++ b/runtime/litentry/src/weights/pallet_membership.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litentry/src/weights/pallet_multisig.rs b/runtime/litentry/src/weights/pallet_multisig.rs index 9c50494459..7be7000ba0 100644 --- a/runtime/litentry/src/weights/pallet_multisig.rs +++ b/runtime/litentry/src/weights/pallet_multisig.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litentry/src/weights/pallet_parachain_staking.rs b/runtime/litentry/src/weights/pallet_parachain_staking.rs index 6da7fea488..5e98c5f611 100644 --- a/runtime/litentry/src/weights/pallet_parachain_staking.rs +++ b/runtime/litentry/src/weights/pallet_parachain_staking.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litentry/src/weights/pallet_preimage.rs b/runtime/litentry/src/weights/pallet_preimage.rs index 37b69b8f26..9a6abc3b3b 100644 --- a/runtime/litentry/src/weights/pallet_preimage.rs +++ b/runtime/litentry/src/weights/pallet_preimage.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litentry/src/weights/pallet_proxy.rs b/runtime/litentry/src/weights/pallet_proxy.rs index ab75516b38..1e3473bd82 100644 --- a/runtime/litentry/src/weights/pallet_proxy.rs +++ b/runtime/litentry/src/weights/pallet_proxy.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litentry/src/weights/pallet_scheduler.rs b/runtime/litentry/src/weights/pallet_scheduler.rs index 4c81613439..01e34c44a2 100644 --- a/runtime/litentry/src/weights/pallet_scheduler.rs +++ b/runtime/litentry/src/weights/pallet_scheduler.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litentry/src/weights/pallet_session.rs b/runtime/litentry/src/weights/pallet_session.rs index 274e3a5a32..45c932e803 100644 --- a/runtime/litentry/src/weights/pallet_session.rs +++ b/runtime/litentry/src/weights/pallet_session.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litentry/src/weights/pallet_timestamp.rs b/runtime/litentry/src/weights/pallet_timestamp.rs index 830edabb57..7c9db583df 100644 --- a/runtime/litentry/src/weights/pallet_timestamp.rs +++ b/runtime/litentry/src/weights/pallet_timestamp.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litentry/src/weights/pallet_treasury.rs b/runtime/litentry/src/weights/pallet_treasury.rs index 9bfd2f0b2e..6fb3ef536c 100644 --- a/runtime/litentry/src/weights/pallet_treasury.rs +++ b/runtime/litentry/src/weights/pallet_treasury.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litentry/src/weights/pallet_utility.rs b/runtime/litentry/src/weights/pallet_utility.rs index b97eeb6082..ec20abfca6 100644 --- a/runtime/litentry/src/weights/pallet_utility.rs +++ b/runtime/litentry/src/weights/pallet_utility.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litentry/src/xcm_config.rs b/runtime/litentry/src/xcm_config.rs index ce9e6849e5..8c582388fe 100644 --- a/runtime/litentry/src/xcm_config.rs +++ b/runtime/litentry/src/xcm_config.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/build.rs b/runtime/litmus/build.rs index e44c15a588..3ccbec6b26 100644 --- a/runtime/litmus/build.rs +++ b/runtime/litmus/build.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/constants.rs b/runtime/litmus/src/constants.rs index b722f107f8..b24f3ddb51 100644 --- a/runtime/litmus/src/constants.rs +++ b/runtime/litmus/src/constants.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/lib.rs b/runtime/litmus/src/lib.rs index 8131f09ebd..add4b7349d 100644 --- a/runtime/litmus/src/lib.rs +++ b/runtime/litmus/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/tests/base_call_filter.rs b/runtime/litmus/src/tests/base_call_filter.rs index e6c8f0a9a6..03523bab00 100644 --- a/runtime/litmus/src/tests/base_call_filter.rs +++ b/runtime/litmus/src/tests/base_call_filter.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/tests/mod.rs b/runtime/litmus/src/tests/mod.rs index e2e4be459e..127fa728ee 100644 --- a/runtime/litmus/src/tests/mod.rs +++ b/runtime/litmus/src/tests/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/tests/orml_xcm.rs b/runtime/litmus/src/tests/orml_xcm.rs index 102be611a5..52495baa5f 100644 --- a/runtime/litmus/src/tests/orml_xcm.rs +++ b/runtime/litmus/src/tests/orml_xcm.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/weights/cumulus_pallet_xcmp_queue.rs b/runtime/litmus/src/weights/cumulus_pallet_xcmp_queue.rs index 866d65d125..6fbdd185f7 100644 --- a/runtime/litmus/src/weights/cumulus_pallet_xcmp_queue.rs +++ b/runtime/litmus/src/weights/cumulus_pallet_xcmp_queue.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/weights/frame_system.rs b/runtime/litmus/src/weights/frame_system.rs index 786ef37075..929601b649 100644 --- a/runtime/litmus/src/weights/frame_system.rs +++ b/runtime/litmus/src/weights/frame_system.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/weights/mod.rs b/runtime/litmus/src/weights/mod.rs index 9023fd3a93..4dd3450d13 100644 --- a/runtime/litmus/src/weights/mod.rs +++ b/runtime/litmus/src/weights/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/weights/pallet_asset_manager.rs b/runtime/litmus/src/weights/pallet_asset_manager.rs index 616d31ddc7..434dfa1c78 100644 --- a/runtime/litmus/src/weights/pallet_asset_manager.rs +++ b/runtime/litmus/src/weights/pallet_asset_manager.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/weights/pallet_balances.rs b/runtime/litmus/src/weights/pallet_balances.rs index 63a4fa810f..af4767870a 100644 --- a/runtime/litmus/src/weights/pallet_balances.rs +++ b/runtime/litmus/src/weights/pallet_balances.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/weights/pallet_bridge.rs b/runtime/litmus/src/weights/pallet_bridge.rs index b3962133fd..709bd638e6 100644 --- a/runtime/litmus/src/weights/pallet_bridge.rs +++ b/runtime/litmus/src/weights/pallet_bridge.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/weights/pallet_bridge_transfer.rs b/runtime/litmus/src/weights/pallet_bridge_transfer.rs index da5e4d0965..dd44da826c 100644 --- a/runtime/litmus/src/weights/pallet_bridge_transfer.rs +++ b/runtime/litmus/src/weights/pallet_bridge_transfer.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/weights/pallet_collator_selection.rs b/runtime/litmus/src/weights/pallet_collator_selection.rs index 12a4ba52c1..f77fdc32ff 100644 --- a/runtime/litmus/src/weights/pallet_collator_selection.rs +++ b/runtime/litmus/src/weights/pallet_collator_selection.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/weights/pallet_collective.rs b/runtime/litmus/src/weights/pallet_collective.rs index 8e570569a9..c8d071294b 100644 --- a/runtime/litmus/src/weights/pallet_collective.rs +++ b/runtime/litmus/src/weights/pallet_collective.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/weights/pallet_democracy.rs b/runtime/litmus/src/weights/pallet_democracy.rs index d18246ee9d..2c24f6ea27 100644 --- a/runtime/litmus/src/weights/pallet_democracy.rs +++ b/runtime/litmus/src/weights/pallet_democracy.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/weights/pallet_drop3.rs b/runtime/litmus/src/weights/pallet_drop3.rs index 1d04a9b396..222c028456 100644 --- a/runtime/litmus/src/weights/pallet_drop3.rs +++ b/runtime/litmus/src/weights/pallet_drop3.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/weights/pallet_extrinsic_filter.rs b/runtime/litmus/src/weights/pallet_extrinsic_filter.rs index 4e26a9e53e..41932fa333 100644 --- a/runtime/litmus/src/weights/pallet_extrinsic_filter.rs +++ b/runtime/litmus/src/weights/pallet_extrinsic_filter.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/weights/pallet_identity_management.rs b/runtime/litmus/src/weights/pallet_identity_management.rs index 3b14661fbc..47f0abf29a 100644 --- a/runtime/litmus/src/weights/pallet_identity_management.rs +++ b/runtime/litmus/src/weights/pallet_identity_management.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/weights/pallet_membership.rs b/runtime/litmus/src/weights/pallet_membership.rs index c19d6c73e8..93b2ca4896 100644 --- a/runtime/litmus/src/weights/pallet_membership.rs +++ b/runtime/litmus/src/weights/pallet_membership.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/weights/pallet_multisig.rs b/runtime/litmus/src/weights/pallet_multisig.rs index 3a51b0b913..c60de69151 100644 --- a/runtime/litmus/src/weights/pallet_multisig.rs +++ b/runtime/litmus/src/weights/pallet_multisig.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/weights/pallet_preimage.rs b/runtime/litmus/src/weights/pallet_preimage.rs index 517db750a7..ba8fe9644c 100644 --- a/runtime/litmus/src/weights/pallet_preimage.rs +++ b/runtime/litmus/src/weights/pallet_preimage.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/weights/pallet_proxy.rs b/runtime/litmus/src/weights/pallet_proxy.rs index 940d1b61e4..0205138fb8 100644 --- a/runtime/litmus/src/weights/pallet_proxy.rs +++ b/runtime/litmus/src/weights/pallet_proxy.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/weights/pallet_scheduler.rs b/runtime/litmus/src/weights/pallet_scheduler.rs index e493b207b0..00661f794e 100644 --- a/runtime/litmus/src/weights/pallet_scheduler.rs +++ b/runtime/litmus/src/weights/pallet_scheduler.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/weights/pallet_session.rs b/runtime/litmus/src/weights/pallet_session.rs index 88a5d92988..bb87830419 100644 --- a/runtime/litmus/src/weights/pallet_session.rs +++ b/runtime/litmus/src/weights/pallet_session.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/weights/pallet_sidechain.rs b/runtime/litmus/src/weights/pallet_sidechain.rs index 92cfe17979..45134fabd5 100644 --- a/runtime/litmus/src/weights/pallet_sidechain.rs +++ b/runtime/litmus/src/weights/pallet_sidechain.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/weights/pallet_teeracle.rs b/runtime/litmus/src/weights/pallet_teeracle.rs index 56ca8eb9c6..d0ff0ba31b 100644 --- a/runtime/litmus/src/weights/pallet_teeracle.rs +++ b/runtime/litmus/src/weights/pallet_teeracle.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/weights/pallet_teerex.rs b/runtime/litmus/src/weights/pallet_teerex.rs index e4e1535287..7a009dfe2d 100644 --- a/runtime/litmus/src/weights/pallet_teerex.rs +++ b/runtime/litmus/src/weights/pallet_teerex.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/weights/pallet_timestamp.rs b/runtime/litmus/src/weights/pallet_timestamp.rs index f130501d84..da3341ce8a 100644 --- a/runtime/litmus/src/weights/pallet_timestamp.rs +++ b/runtime/litmus/src/weights/pallet_timestamp.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/weights/pallet_treasury.rs b/runtime/litmus/src/weights/pallet_treasury.rs index d443804109..94322fd86a 100644 --- a/runtime/litmus/src/weights/pallet_treasury.rs +++ b/runtime/litmus/src/weights/pallet_treasury.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/weights/pallet_utility.rs b/runtime/litmus/src/weights/pallet_utility.rs index 2360f0d1bb..77d93e8e1d 100644 --- a/runtime/litmus/src/weights/pallet_utility.rs +++ b/runtime/litmus/src/weights/pallet_utility.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/litmus/src/xcm_config.rs b/runtime/litmus/src/xcm_config.rs index 448c41fc02..0180f59bb4 100644 --- a/runtime/litmus/src/xcm_config.rs +++ b/runtime/litmus/src/xcm_config.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/build.rs b/runtime/rococo/build.rs index e44c15a588..3ccbec6b26 100644 --- a/runtime/rococo/build.rs +++ b/runtime/rococo/build.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/constants.rs b/runtime/rococo/src/constants.rs index b722f107f8..b24f3ddb51 100644 --- a/runtime/rococo/src/constants.rs +++ b/runtime/rococo/src/constants.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/lib.rs b/runtime/rococo/src/lib.rs index 5dd3428506..5d991f2eb9 100644 --- a/runtime/rococo/src/lib.rs +++ b/runtime/rococo/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/precompiles.rs b/runtime/rococo/src/precompiles.rs index 6221bbd985..d645551a08 100644 --- a/runtime/rococo/src/precompiles.rs +++ b/runtime/rococo/src/precompiles.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/tests/base_call_filter.rs b/runtime/rococo/src/tests/base_call_filter.rs index e6c8f0a9a6..03523bab00 100644 --- a/runtime/rococo/src/tests/base_call_filter.rs +++ b/runtime/rococo/src/tests/base_call_filter.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/tests/mod.rs b/runtime/rococo/src/tests/mod.rs index e2e4be459e..127fa728ee 100644 --- a/runtime/rococo/src/tests/mod.rs +++ b/runtime/rococo/src/tests/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/tests/orml_xcm.rs b/runtime/rococo/src/tests/orml_xcm.rs index 102be611a5..52495baa5f 100644 --- a/runtime/rococo/src/tests/orml_xcm.rs +++ b/runtime/rococo/src/tests/orml_xcm.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/weights/cumulus_pallet_xcmp_queue.rs b/runtime/rococo/src/weights/cumulus_pallet_xcmp_queue.rs index 3892d3bb10..11ddda9d2c 100644 --- a/runtime/rococo/src/weights/cumulus_pallet_xcmp_queue.rs +++ b/runtime/rococo/src/weights/cumulus_pallet_xcmp_queue.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/weights/frame_system.rs b/runtime/rococo/src/weights/frame_system.rs index 49692a0714..beb8e20ee4 100644 --- a/runtime/rococo/src/weights/frame_system.rs +++ b/runtime/rococo/src/weights/frame_system.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/weights/mod.rs b/runtime/rococo/src/weights/mod.rs index 16fcb9c5b4..47af2c4cc6 100644 --- a/runtime/rococo/src/weights/mod.rs +++ b/runtime/rococo/src/weights/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/weights/pallet_asset_manager.rs b/runtime/rococo/src/weights/pallet_asset_manager.rs index b6a5aa5771..252f531858 100644 --- a/runtime/rococo/src/weights/pallet_asset_manager.rs +++ b/runtime/rococo/src/weights/pallet_asset_manager.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/weights/pallet_balances.rs b/runtime/rococo/src/weights/pallet_balances.rs index 19a7f798bc..4e4de5a3b0 100644 --- a/runtime/rococo/src/weights/pallet_balances.rs +++ b/runtime/rococo/src/weights/pallet_balances.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/weights/pallet_bridge.rs b/runtime/rococo/src/weights/pallet_bridge.rs index 22e9207347..b08be4dbeb 100644 --- a/runtime/rococo/src/weights/pallet_bridge.rs +++ b/runtime/rococo/src/weights/pallet_bridge.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/weights/pallet_bridge_transfer.rs b/runtime/rococo/src/weights/pallet_bridge_transfer.rs index 4be629f2d2..938737145c 100644 --- a/runtime/rococo/src/weights/pallet_bridge_transfer.rs +++ b/runtime/rococo/src/weights/pallet_bridge_transfer.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/weights/pallet_collective.rs b/runtime/rococo/src/weights/pallet_collective.rs index 48d92d249c..34960feeaf 100644 --- a/runtime/rococo/src/weights/pallet_collective.rs +++ b/runtime/rococo/src/weights/pallet_collective.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/weights/pallet_democracy.rs b/runtime/rococo/src/weights/pallet_democracy.rs index 50ac0f7ba7..7e5417f379 100644 --- a/runtime/rococo/src/weights/pallet_democracy.rs +++ b/runtime/rococo/src/weights/pallet_democracy.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/weights/pallet_drop3.rs b/runtime/rococo/src/weights/pallet_drop3.rs index 90d8387357..4a9cbee7ca 100644 --- a/runtime/rococo/src/weights/pallet_drop3.rs +++ b/runtime/rococo/src/weights/pallet_drop3.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/weights/pallet_extrinsic_filter.rs b/runtime/rococo/src/weights/pallet_extrinsic_filter.rs index 787a47e0df..d0020f8a51 100644 --- a/runtime/rococo/src/weights/pallet_extrinsic_filter.rs +++ b/runtime/rococo/src/weights/pallet_extrinsic_filter.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/weights/pallet_identity_management.rs b/runtime/rococo/src/weights/pallet_identity_management.rs index bbe38fdeb7..f34c50665d 100644 --- a/runtime/rococo/src/weights/pallet_identity_management.rs +++ b/runtime/rococo/src/weights/pallet_identity_management.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/weights/pallet_membership.rs b/runtime/rococo/src/weights/pallet_membership.rs index 55e534eafb..90be12f750 100644 --- a/runtime/rococo/src/weights/pallet_membership.rs +++ b/runtime/rococo/src/weights/pallet_membership.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/weights/pallet_multisig.rs b/runtime/rococo/src/weights/pallet_multisig.rs index 59e0810b91..a953d564ae 100644 --- a/runtime/rococo/src/weights/pallet_multisig.rs +++ b/runtime/rococo/src/weights/pallet_multisig.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/weights/pallet_parachain_staking.rs b/runtime/rococo/src/weights/pallet_parachain_staking.rs index 5ff1cbfa51..b09131e24e 100644 --- a/runtime/rococo/src/weights/pallet_parachain_staking.rs +++ b/runtime/rococo/src/weights/pallet_parachain_staking.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/weights/pallet_preimage.rs b/runtime/rococo/src/weights/pallet_preimage.rs index cfde9ff5b3..967472a8e9 100644 --- a/runtime/rococo/src/weights/pallet_preimage.rs +++ b/runtime/rococo/src/weights/pallet_preimage.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/weights/pallet_proxy.rs b/runtime/rococo/src/weights/pallet_proxy.rs index a201e7d069..56a78bd262 100644 --- a/runtime/rococo/src/weights/pallet_proxy.rs +++ b/runtime/rococo/src/weights/pallet_proxy.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/weights/pallet_scheduler.rs b/runtime/rococo/src/weights/pallet_scheduler.rs index 521f911aa4..cedb804851 100644 --- a/runtime/rococo/src/weights/pallet_scheduler.rs +++ b/runtime/rococo/src/weights/pallet_scheduler.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/weights/pallet_session.rs b/runtime/rococo/src/weights/pallet_session.rs index a6c06be95d..bbd70fb8a9 100644 --- a/runtime/rococo/src/weights/pallet_session.rs +++ b/runtime/rococo/src/weights/pallet_session.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/weights/pallet_sidechain.rs b/runtime/rococo/src/weights/pallet_sidechain.rs index b9040023c7..11daf3eabc 100644 --- a/runtime/rococo/src/weights/pallet_sidechain.rs +++ b/runtime/rococo/src/weights/pallet_sidechain.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/weights/pallet_teeracle.rs b/runtime/rococo/src/weights/pallet_teeracle.rs index 67ecd47a79..f38318ebb8 100644 --- a/runtime/rococo/src/weights/pallet_teeracle.rs +++ b/runtime/rococo/src/weights/pallet_teeracle.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/weights/pallet_teerex.rs b/runtime/rococo/src/weights/pallet_teerex.rs index 613bc0d803..5afa456c2e 100644 --- a/runtime/rococo/src/weights/pallet_teerex.rs +++ b/runtime/rococo/src/weights/pallet_teerex.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/weights/pallet_timestamp.rs b/runtime/rococo/src/weights/pallet_timestamp.rs index 1e535e9e4b..6aaf72bd60 100644 --- a/runtime/rococo/src/weights/pallet_timestamp.rs +++ b/runtime/rococo/src/weights/pallet_timestamp.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/weights/pallet_treasury.rs b/runtime/rococo/src/weights/pallet_treasury.rs index 1251a6d63d..e9b15b993d 100644 --- a/runtime/rococo/src/weights/pallet_treasury.rs +++ b/runtime/rococo/src/weights/pallet_treasury.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/weights/pallet_utility.rs b/runtime/rococo/src/weights/pallet_utility.rs index cae7580b0b..477b911611 100644 --- a/runtime/rococo/src/weights/pallet_utility.rs +++ b/runtime/rococo/src/weights/pallet_utility.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/weights/pallet_vc_management.rs b/runtime/rococo/src/weights/pallet_vc_management.rs index 6bc8f6e6aa..6ede65a8ae 100644 --- a/runtime/rococo/src/weights/pallet_vc_management.rs +++ b/runtime/rococo/src/weights/pallet_vc_management.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/runtime/rococo/src/xcm_config.rs b/runtime/rococo/src/xcm_config.rs index 5b37d6c975..06d0f08269 100644 --- a/runtime/rococo/src/xcm_config.rs +++ b/runtime/rococo/src/xcm_config.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/activate_identity.rs b/tee-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/activate_identity.rs index 624652db73..469075e59f 100644 --- a/tee-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/activate_identity.rs +++ b/tee-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/activate_identity.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/args_executor.rs b/tee-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/args_executor.rs index d31e1e3bfc..33a946e508 100644 --- a/tee-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/args_executor.rs +++ b/tee-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/args_executor.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/deactivate_identity.rs b/tee-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/deactivate_identity.rs index 49a346bd4b..adc3014677 100644 --- a/tee-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/deactivate_identity.rs +++ b/tee-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/deactivate_identity.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/link_identity.rs b/tee-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/link_identity.rs index 3a19c46449..b8471be489 100644 --- a/tee-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/link_identity.rs +++ b/tee-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/link_identity.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/mod.rs b/tee-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/mod.rs index f435174712..5c826c17e9 100644 --- a/tee-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/mod.rs +++ b/tee-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/request_vc.rs b/tee-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/request_vc.rs index 722d0343b5..65be9edc3e 100644 --- a/tee-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/request_vc.rs +++ b/tee-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/request_vc.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/scheduled_enclave.rs b/tee-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/scheduled_enclave.rs index a0d1ff65bc..f2ae695c9e 100644 --- a/tee-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/scheduled_enclave.rs +++ b/tee-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/scheduled_enclave.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/app-libs/stf/src/trusted_call_litentry.rs b/tee-worker/app-libs/stf/src/trusted_call_litentry.rs index 442d01d879..fc286822a5 100644 --- a/tee-worker/app-libs/stf/src/trusted_call_litentry.rs +++ b/tee-worker/app-libs/stf/src/trusted_call_litentry.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/app-libs/stf/src/trusted_call_result.rs b/tee-worker/app-libs/stf/src/trusted_call_result.rs index 509a3a069d..bfa28d78c7 100644 --- a/tee-worker/app-libs/stf/src/trusted_call_result.rs +++ b/tee-worker/app-libs/stf/src/trusted_call_result.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/cli/lit_id_graph_stats.sh b/tee-worker/cli/lit_id_graph_stats.sh index 5834e1985f..ef3615b4c3 100755 --- a/tee-worker/cli/lit_id_graph_stats.sh +++ b/tee-worker/cli/lit_id_graph_stats.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2020-2023 Trust Computing GmbH. +# Copyright 2020-2024 Trust Computing GmbH. while getopts ":p:A:B:u:W:V:C:" opt; do case $opt in diff --git a/tee-worker/cli/lit_parentchain_nonce.sh b/tee-worker/cli/lit_parentchain_nonce.sh index 0035d9de38..34c6419892 100755 --- a/tee-worker/cli/lit_parentchain_nonce.sh +++ b/tee-worker/cli/lit_parentchain_nonce.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2020-2023 Trust Computing GmbH. +# Copyright 2020-2024 Trust Computing GmbH. while getopts ":p:A:B:u:W:V:C:" opt; do case $opt in diff --git a/tee-worker/cli/lit_set_heartbeat_timeout.sh b/tee-worker/cli/lit_set_heartbeat_timeout.sh index 96e5ce2a13..7c806480c4 100755 --- a/tee-worker/cli/lit_set_heartbeat_timeout.sh +++ b/tee-worker/cli/lit_set_heartbeat_timeout.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2020-2023 Trust Computing GmbH. +# Copyright 2020-2024 Trust Computing GmbH. while getopts ":p:A:B:u:W:V:C:" opt; do case $opt in diff --git a/tee-worker/cli/lit_ts_api_package_build.sh b/tee-worker/cli/lit_ts_api_package_build.sh index 684833b7c5..d5777c0880 100755 --- a/tee-worker/cli/lit_ts_api_package_build.sh +++ b/tee-worker/cli/lit_ts_api_package_build.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2020-2023 Trust Computing GmbH. +# Copyright 2020-2024 Trust Computing GmbH. set -euo pipefail diff --git a/tee-worker/cli/lit_ts_integration_test.sh b/tee-worker/cli/lit_ts_integration_test.sh index bdce639176..4549d32048 100755 --- a/tee-worker/cli/lit_ts_integration_test.sh +++ b/tee-worker/cli/lit_ts_integration_test.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2020-2023 Trust Computing GmbH. +# Copyright 2020-2024 Trust Computing GmbH. set -euo pipefail diff --git a/tee-worker/cli/lit_ts_worker_test.sh b/tee-worker/cli/lit_ts_worker_test.sh index 8528b59691..a4983a99ae 100755 --- a/tee-worker/cli/lit_ts_worker_test.sh +++ b/tee-worker/cli/lit_ts_worker_test.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2020-2023 Trust Computing GmbH. +# Copyright 2020-2024 Trust Computing GmbH. set -euo pipefail diff --git a/tee-worker/cli/src/base_cli/commands/litentry/id_graph_hash.rs b/tee-worker/cli/src/base_cli/commands/litentry/id_graph_hash.rs index 9abf282b20..237238dd8f 100644 --- a/tee-worker/cli/src/base_cli/commands/litentry/id_graph_hash.rs +++ b/tee-worker/cli/src/base_cli/commands/litentry/id_graph_hash.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/cli/src/base_cli/commands/litentry/link_identity.rs b/tee-worker/cli/src/base_cli/commands/litentry/link_identity.rs index e79b7aaf02..7c7f85a08a 100644 --- a/tee-worker/cli/src/base_cli/commands/litentry/link_identity.rs +++ b/tee-worker/cli/src/base_cli/commands/litentry/link_identity.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/cli/src/base_cli/commands/litentry/mod.rs b/tee-worker/cli/src/base_cli/commands/litentry/mod.rs index 820e551764..71f772d2ed 100644 --- a/tee-worker/cli/src/base_cli/commands/litentry/mod.rs +++ b/tee-worker/cli/src/base_cli/commands/litentry/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/cli/src/base_cli/commands/litentry/set_heartbeat_timeout.rs b/tee-worker/cli/src/base_cli/commands/litentry/set_heartbeat_timeout.rs index f4efb49ae7..996b369a59 100644 --- a/tee-worker/cli/src/base_cli/commands/litentry/set_heartbeat_timeout.rs +++ b/tee-worker/cli/src/base_cli/commands/litentry/set_heartbeat_timeout.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/cli/src/trusted_base_cli/commands/litentry/get_storage.rs b/tee-worker/cli/src/trusted_base_cli/commands/litentry/get_storage.rs index 196e7b0b96..ae5fb5860d 100644 --- a/tee-worker/cli/src/trusted_base_cli/commands/litentry/get_storage.rs +++ b/tee-worker/cli/src/trusted_base_cli/commands/litentry/get_storage.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/cli/src/trusted_base_cli/commands/litentry/id_graph.rs b/tee-worker/cli/src/trusted_base_cli/commands/litentry/id_graph.rs index f3cd37bf54..41e7e1ea9f 100644 --- a/tee-worker/cli/src/trusted_base_cli/commands/litentry/id_graph.rs +++ b/tee-worker/cli/src/trusted_base_cli/commands/litentry/id_graph.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/cli/src/trusted_base_cli/commands/litentry/id_graph_stats.rs b/tee-worker/cli/src/trusted_base_cli/commands/litentry/id_graph_stats.rs index ac1dc66b09..5291734c9a 100644 --- a/tee-worker/cli/src/trusted_base_cli/commands/litentry/id_graph_stats.rs +++ b/tee-worker/cli/src/trusted_base_cli/commands/litentry/id_graph_stats.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/cli/src/trusted_base_cli/commands/litentry/link_identity.rs b/tee-worker/cli/src/trusted_base_cli/commands/litentry/link_identity.rs index c7917b90a8..e884858785 100644 --- a/tee-worker/cli/src/trusted_base_cli/commands/litentry/link_identity.rs +++ b/tee-worker/cli/src/trusted_base_cli/commands/litentry/link_identity.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/cli/src/trusted_base_cli/commands/litentry/mod.rs b/tee-worker/cli/src/trusted_base_cli/commands/litentry/mod.rs index 0636afe5c6..d582982aee 100644 --- a/tee-worker/cli/src/trusted_base_cli/commands/litentry/mod.rs +++ b/tee-worker/cli/src/trusted_base_cli/commands/litentry/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/cli/src/trusted_base_cli/commands/litentry/remove_identity.rs b/tee-worker/cli/src/trusted_base_cli/commands/litentry/remove_identity.rs index bef8480154..0ea457bf52 100644 --- a/tee-worker/cli/src/trusted_base_cli/commands/litentry/remove_identity.rs +++ b/tee-worker/cli/src/trusted_base_cli/commands/litentry/remove_identity.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc.rs b/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc.rs index cd6f22afbe..aaf4ac527e 100644 --- a/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc.rs +++ b/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc_direct.rs b/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc_direct.rs index 06603a4881..7ac1c81da5 100644 --- a/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc_direct.rs +++ b/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc_direct.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/cli/src/trusted_base_cli/commands/litentry/send_erroneous_parentchain_call.rs b/tee-worker/cli/src/trusted_base_cli/commands/litentry/send_erroneous_parentchain_call.rs index a10b0d8902..17176acfa3 100644 --- a/tee-worker/cli/src/trusted_base_cli/commands/litentry/send_erroneous_parentchain_call.rs +++ b/tee-worker/cli/src/trusted_base_cli/commands/litentry/send_erroneous_parentchain_call.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/core-primitives/node-api/metadata/src/pallet_imp.rs b/tee-worker/core-primitives/node-api/metadata/src/pallet_imp.rs index 636d93cdab..14130de247 100644 --- a/tee-worker/core-primitives/node-api/metadata/src/pallet_imp.rs +++ b/tee-worker/core-primitives/node-api/metadata/src/pallet_imp.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/core-primitives/node-api/metadata/src/pallet_utility.rs b/tee-worker/core-primitives/node-api/metadata/src/pallet_utility.rs index 909e4a7d30..0eeef1339a 100644 --- a/tee-worker/core-primitives/node-api/metadata/src/pallet_utility.rs +++ b/tee-worker/core-primitives/node-api/metadata/src/pallet_utility.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/core-primitives/node-api/metadata/src/pallet_vcmp.rs b/tee-worker/core-primitives/node-api/metadata/src/pallet_vcmp.rs index 210d55e74f..ae7ccde07b 100644 --- a/tee-worker/core-primitives/node-api/metadata/src/pallet_vcmp.rs +++ b/tee-worker/core-primitives/node-api/metadata/src/pallet_vcmp.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/core-primitives/node-api/metadata/src/runtime_call.rs b/tee-worker/core-primitives/node-api/metadata/src/runtime_call.rs index a484e6f779..8fa69cc9ad 100644 --- a/tee-worker/core-primitives/node-api/metadata/src/runtime_call.rs +++ b/tee-worker/core-primitives/node-api/metadata/src/runtime_call.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/core-primitives/stf-interface/src/runtime_upgrade.rs b/tee-worker/core-primitives/stf-interface/src/runtime_upgrade.rs index 30ee22140e..649ba34ca5 100644 --- a/tee-worker/core-primitives/stf-interface/src/runtime_upgrade.rs +++ b/tee-worker/core-primitives/stf-interface/src/runtime_upgrade.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/core-primitives/utils/src/macros.rs b/tee-worker/core-primitives/utils/src/macros.rs index 69783ff727..9d5234ce50 100644 --- a/tee-worker/core-primitives/utils/src/macros.rs +++ b/tee-worker/core-primitives/utils/src/macros.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/core/direct-rpc-client/src/lib.rs b/tee-worker/core/direct-rpc-client/src/lib.rs index 6de127f7df..ba5253fdf7 100644 --- a/tee-worker/core/direct-rpc-client/src/lib.rs +++ b/tee-worker/core/direct-rpc-client/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/core/peer-top-broadcaster/src/lib.rs b/tee-worker/core/peer-top-broadcaster/src/lib.rs index eef091de21..9e34033741 100644 --- a/tee-worker/core/peer-top-broadcaster/src/lib.rs +++ b/tee-worker/core/peer-top-broadcaster/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/enclave-runtime/src/test/mocks/peer_updater_mock.rs b/tee-worker/enclave-runtime/src/test/mocks/peer_updater_mock.rs index 63a60108df..2fe4526c43 100644 --- a/tee-worker/enclave-runtime/src/test/mocks/peer_updater_mock.rs +++ b/tee-worker/enclave-runtime/src/test/mocks/peer_updater_mock.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/a1.rs b/tee-worker/litentry/core/assertion-build/src/a1.rs index 1a3cda047f..1c4677b620 100644 --- a/tee-worker/litentry/core/assertion-build/src/a1.rs +++ b/tee-worker/litentry/core/assertion-build/src/a1.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/a13.rs b/tee-worker/litentry/core/assertion-build/src/a13.rs index b89026ba07..33d6c082d4 100644 --- a/tee-worker/litentry/core/assertion-build/src/a13.rs +++ b/tee-worker/litentry/core/assertion-build/src/a13.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/a14.rs b/tee-worker/litentry/core/assertion-build/src/a14.rs index 72ebef5c58..bde9bef807 100644 --- a/tee-worker/litentry/core/assertion-build/src/a14.rs +++ b/tee-worker/litentry/core/assertion-build/src/a14.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/a2.rs b/tee-worker/litentry/core/assertion-build/src/a2.rs index fe723970a9..191777cb49 100644 --- a/tee-worker/litentry/core/assertion-build/src/a2.rs +++ b/tee-worker/litentry/core/assertion-build/src/a2.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/a20.rs b/tee-worker/litentry/core/assertion-build/src/a20.rs index 08e22d4ffa..40fa7e72b5 100644 --- a/tee-worker/litentry/core/assertion-build/src/a20.rs +++ b/tee-worker/litentry/core/assertion-build/src/a20.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/a3.rs b/tee-worker/litentry/core/assertion-build/src/a3.rs index 6dfc4db4b3..9e43923fba 100644 --- a/tee-worker/litentry/core/assertion-build/src/a3.rs +++ b/tee-worker/litentry/core/assertion-build/src/a3.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/a6.rs b/tee-worker/litentry/core/assertion-build/src/a6.rs index 0b1d82a8b4..555bea89c9 100644 --- a/tee-worker/litentry/core/assertion-build/src/a6.rs +++ b/tee-worker/litentry/core/assertion-build/src/a6.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/a8.rs b/tee-worker/litentry/core/assertion-build/src/a8.rs index a5ab09169e..8c1e10c085 100644 --- a/tee-worker/litentry/core/assertion-build/src/a8.rs +++ b/tee-worker/litentry/core/assertion-build/src/a8.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/achainable/amount.rs b/tee-worker/litentry/core/assertion-build/src/achainable/amount.rs index 0cf9777f7d..af9245bb9d 100644 --- a/tee-worker/litentry/core/assertion-build/src/achainable/amount.rs +++ b/tee-worker/litentry/core/assertion-build/src/achainable/amount.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/achainable/amount_holding.rs b/tee-worker/litentry/core/assertion-build/src/achainable/amount_holding.rs index 9b2f151acb..f5cce52dcb 100644 --- a/tee-worker/litentry/core/assertion-build/src/achainable/amount_holding.rs +++ b/tee-worker/litentry/core/assertion-build/src/achainable/amount_holding.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/achainable/amount_token.rs b/tee-worker/litentry/core/assertion-build/src/achainable/amount_token.rs index 3c4ee1df34..f5a61fe3e1 100644 --- a/tee-worker/litentry/core/assertion-build/src/achainable/amount_token.rs +++ b/tee-worker/litentry/core/assertion-build/src/achainable/amount_token.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/achainable/amounts.rs b/tee-worker/litentry/core/assertion-build/src/achainable/amounts.rs index 826f423b40..f1ae322bb4 100644 --- a/tee-worker/litentry/core/assertion-build/src/achainable/amounts.rs +++ b/tee-worker/litentry/core/assertion-build/src/achainable/amounts.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/achainable/basic.rs b/tee-worker/litentry/core/assertion-build/src/achainable/basic.rs index e8310b5c4f..20c909464f 100644 --- a/tee-worker/litentry/core/assertion-build/src/achainable/basic.rs +++ b/tee-worker/litentry/core/assertion-build/src/achainable/basic.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/achainable/between_percents.rs b/tee-worker/litentry/core/assertion-build/src/achainable/between_percents.rs index 33334f063a..2813ecd6f7 100644 --- a/tee-worker/litentry/core/assertion-build/src/achainable/between_percents.rs +++ b/tee-worker/litentry/core/assertion-build/src/achainable/between_percents.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/achainable/class_of_year.rs b/tee-worker/litentry/core/assertion-build/src/achainable/class_of_year.rs index ae46cd4ae0..913f1f46ca 100644 --- a/tee-worker/litentry/core/assertion-build/src/achainable/class_of_year.rs +++ b/tee-worker/litentry/core/assertion-build/src/achainable/class_of_year.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/achainable/date.rs b/tee-worker/litentry/core/assertion-build/src/achainable/date.rs index 485280a149..bd7679d412 100644 --- a/tee-worker/litentry/core/assertion-build/src/achainable/date.rs +++ b/tee-worker/litentry/core/assertion-build/src/achainable/date.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/achainable/date_interval.rs b/tee-worker/litentry/core/assertion-build/src/achainable/date_interval.rs index 411e808d76..f46b5edc70 100644 --- a/tee-worker/litentry/core/assertion-build/src/achainable/date_interval.rs +++ b/tee-worker/litentry/core/assertion-build/src/achainable/date_interval.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/achainable/date_percent.rs b/tee-worker/litentry/core/assertion-build/src/achainable/date_percent.rs index 7c10a93e93..5206b8038f 100644 --- a/tee-worker/litentry/core/assertion-build/src/achainable/date_percent.rs +++ b/tee-worker/litentry/core/assertion-build/src/achainable/date_percent.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/achainable/mirror.rs b/tee-worker/litentry/core/assertion-build/src/achainable/mirror.rs index 8623c759ad..f0c926a3c0 100644 --- a/tee-worker/litentry/core/assertion-build/src/achainable/mirror.rs +++ b/tee-worker/litentry/core/assertion-build/src/achainable/mirror.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/achainable/mod.rs b/tee-worker/litentry/core/assertion-build/src/achainable/mod.rs index ca1c07fced..a447df8e83 100644 --- a/tee-worker/litentry/core/assertion-build/src/achainable/mod.rs +++ b/tee-worker/litentry/core/assertion-build/src/achainable/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/achainable/token.rs b/tee-worker/litentry/core/assertion-build/src/achainable/token.rs index 6df95c7e6e..ffa91308d1 100644 --- a/tee-worker/litentry/core/assertion-build/src/achainable/token.rs +++ b/tee-worker/litentry/core/assertion-build/src/achainable/token.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/brc20/amount_holder.rs b/tee-worker/litentry/core/assertion-build/src/brc20/amount_holder.rs index 93a07ef818..8c385b814e 100644 --- a/tee-worker/litentry/core/assertion-build/src/brc20/amount_holder.rs +++ b/tee-worker/litentry/core/assertion-build/src/brc20/amount_holder.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/brc20/mod.rs b/tee-worker/litentry/core/assertion-build/src/brc20/mod.rs index 028dceb3dc..17d123a22c 100644 --- a/tee-worker/litentry/core/assertion-build/src/brc20/mod.rs +++ b/tee-worker/litentry/core/assertion-build/src/brc20/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/generic_discord_role.rs b/tee-worker/litentry/core/assertion-build/src/generic_discord_role.rs index e5e9af6ca4..cadb2ef9e2 100644 --- a/tee-worker/litentry/core/assertion-build/src/generic_discord_role.rs +++ b/tee-worker/litentry/core/assertion-build/src/generic_discord_role.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/holding_time.rs b/tee-worker/litentry/core/assertion-build/src/holding_time.rs index 5620c2c78c..446aa6b4c7 100644 --- a/tee-worker/litentry/core/assertion-build/src/holding_time.rs +++ b/tee-worker/litentry/core/assertion-build/src/holding_time.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/lib.rs b/tee-worker/litentry/core/assertion-build/src/lib.rs index a982deb232..0a68f76135 100644 --- a/tee-worker/litentry/core/assertion-build/src/lib.rs +++ b/tee-worker/litentry/core/assertion-build/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/lit_staking.rs b/tee-worker/litentry/core/assertion-build/src/lit_staking.rs index 0319353717..c24ef05256 100644 --- a/tee-worker/litentry/core/assertion-build/src/lit_staking.rs +++ b/tee-worker/litentry/core/assertion-build/src/lit_staking.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/nodereal/amount_holding/evm_amount_holding.rs b/tee-worker/litentry/core/assertion-build/src/nodereal/amount_holding/evm_amount_holding.rs index 85489e80b6..7fb7a8de2b 100644 --- a/tee-worker/litentry/core/assertion-build/src/nodereal/amount_holding/evm_amount_holding.rs +++ b/tee-worker/litentry/core/assertion-build/src/nodereal/amount_holding/evm_amount_holding.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/nodereal/amount_holding/mod.rs b/tee-worker/litentry/core/assertion-build/src/nodereal/amount_holding/mod.rs index 09c8ec0fdf..c7d0e8eed4 100644 --- a/tee-worker/litentry/core/assertion-build/src/nodereal/amount_holding/mod.rs +++ b/tee-worker/litentry/core/assertion-build/src/nodereal/amount_holding/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/nodereal/bnb_domain/bnb_digit_domain_club_amount.rs b/tee-worker/litentry/core/assertion-build/src/nodereal/bnb_domain/bnb_digit_domain_club_amount.rs index a0d7854d32..c4ab79f7a8 100644 --- a/tee-worker/litentry/core/assertion-build/src/nodereal/bnb_domain/bnb_digit_domain_club_amount.rs +++ b/tee-worker/litentry/core/assertion-build/src/nodereal/bnb_domain/bnb_digit_domain_club_amount.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/nodereal/bnb_domain/bnb_domain_holding_amount.rs b/tee-worker/litentry/core/assertion-build/src/nodereal/bnb_domain/bnb_domain_holding_amount.rs index d765634f0c..cdf7f81a46 100644 --- a/tee-worker/litentry/core/assertion-build/src/nodereal/bnb_domain/bnb_domain_holding_amount.rs +++ b/tee-worker/litentry/core/assertion-build/src/nodereal/bnb_domain/bnb_domain_holding_amount.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/nodereal/bnb_domain/mod.rs b/tee-worker/litentry/core/assertion-build/src/nodereal/bnb_domain/mod.rs index ec47649d60..f3096db521 100644 --- a/tee-worker/litentry/core/assertion-build/src/nodereal/bnb_domain/mod.rs +++ b/tee-worker/litentry/core/assertion-build/src/nodereal/bnb_domain/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/nodereal/crypto_summary/mod.rs b/tee-worker/litentry/core/assertion-build/src/nodereal/crypto_summary/mod.rs index c80f4736ea..31816f1787 100644 --- a/tee-worker/litentry/core/assertion-build/src/nodereal/crypto_summary/mod.rs +++ b/tee-worker/litentry/core/assertion-build/src/nodereal/crypto_summary/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/nodereal/mod.rs b/tee-worker/litentry/core/assertion-build/src/nodereal/mod.rs index 84a4bff2f7..4c852da296 100644 --- a/tee-worker/litentry/core/assertion-build/src/nodereal/mod.rs +++ b/tee-worker/litentry/core/assertion-build/src/nodereal/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/nodereal/nft_holder/mod.rs b/tee-worker/litentry/core/assertion-build/src/nodereal/nft_holder/mod.rs index b023ebf540..a4d2197d8c 100644 --- a/tee-worker/litentry/core/assertion-build/src/nodereal/nft_holder/mod.rs +++ b/tee-worker/litentry/core/assertion-build/src/nodereal/nft_holder/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/nodereal/nft_holder/weirdo_ghost_gang_holder.rs b/tee-worker/litentry/core/assertion-build/src/nodereal/nft_holder/weirdo_ghost_gang_holder.rs index 3f9f72a629..b06474ca12 100644 --- a/tee-worker/litentry/core/assertion-build/src/nodereal/nft_holder/weirdo_ghost_gang_holder.rs +++ b/tee-worker/litentry/core/assertion-build/src/nodereal/nft_holder/weirdo_ghost_gang_holder.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/oneblock/course.rs b/tee-worker/litentry/core/assertion-build/src/oneblock/course.rs index 93e18a7605..42da6bd72a 100644 --- a/tee-worker/litentry/core/assertion-build/src/oneblock/course.rs +++ b/tee-worker/litentry/core/assertion-build/src/oneblock/course.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/oneblock/mod.rs b/tee-worker/litentry/core/assertion-build/src/oneblock/mod.rs index 7dc640138a..545e21ebfe 100644 --- a/tee-worker/litentry/core/assertion-build/src/oneblock/mod.rs +++ b/tee-worker/litentry/core/assertion-build/src/oneblock/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/vip3/card.rs b/tee-worker/litentry/core/assertion-build/src/vip3/card.rs index bd12ec59f0..e63d25c889 100644 --- a/tee-worker/litentry/core/assertion-build/src/vip3/card.rs +++ b/tee-worker/litentry/core/assertion-build/src/vip3/card.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/assertion-build/src/vip3/mod.rs b/tee-worker/litentry/core/assertion-build/src/vip3/mod.rs index 05e045e985..28977cfcf0 100644 --- a/tee-worker/litentry/core/assertion-build/src/vip3/mod.rs +++ b/tee-worker/litentry/core/assertion-build/src/vip3/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/credentials/src/achainable/amount_holding_time.rs b/tee-worker/litentry/core/credentials/src/achainable/amount_holding_time.rs index 10b44d7ad4..85b1e468aa 100644 --- a/tee-worker/litentry/core/credentials/src/achainable/amount_holding_time.rs +++ b/tee-worker/litentry/core/credentials/src/achainable/amount_holding_time.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/credentials/src/achainable/bab_holder.rs b/tee-worker/litentry/core/credentials/src/achainable/bab_holder.rs index 2fdf6c5ab9..997d7bfa23 100644 --- a/tee-worker/litentry/core/credentials/src/achainable/bab_holder.rs +++ b/tee-worker/litentry/core/credentials/src/achainable/bab_holder.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/credentials/src/achainable/lit_holding_amount.rs b/tee-worker/litentry/core/credentials/src/achainable/lit_holding_amount.rs index 60e8434f74..c311641c9c 100644 --- a/tee-worker/litentry/core/credentials/src/achainable/lit_holding_amount.rs +++ b/tee-worker/litentry/core/credentials/src/achainable/lit_holding_amount.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/credentials/src/achainable/mod.rs b/tee-worker/litentry/core/credentials/src/achainable/mod.rs index a795bf7892..3da388c9a8 100644 --- a/tee-worker/litentry/core/credentials/src/achainable/mod.rs +++ b/tee-worker/litentry/core/credentials/src/achainable/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/credentials/src/achainable/uniswap_user.rs b/tee-worker/litentry/core/credentials/src/achainable/uniswap_user.rs index 18f7855687..c34ef97db0 100644 --- a/tee-worker/litentry/core/credentials/src/achainable/uniswap_user.rs +++ b/tee-worker/litentry/core/credentials/src/achainable/uniswap_user.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/credentials/src/assertion_logic.rs b/tee-worker/litentry/core/credentials/src/assertion_logic.rs index 9be2335447..e2ed5bb40e 100644 --- a/tee-worker/litentry/core/credentials/src/assertion_logic.rs +++ b/tee-worker/litentry/core/credentials/src/assertion_logic.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/credentials/src/brc20/amount_holder.rs b/tee-worker/litentry/core/credentials/src/brc20/amount_holder.rs index b70173ac3b..7c602d0fc5 100644 --- a/tee-worker/litentry/core/credentials/src/brc20/amount_holder.rs +++ b/tee-worker/litentry/core/credentials/src/brc20/amount_holder.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/credentials/src/brc20/mod.rs b/tee-worker/litentry/core/credentials/src/brc20/mod.rs index 40eee4424d..6f967ca9f4 100644 --- a/tee-worker/litentry/core/credentials/src/brc20/mod.rs +++ b/tee-worker/litentry/core/credentials/src/brc20/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/credentials/src/error.rs b/tee-worker/litentry/core/credentials/src/error.rs index dff82d7387..b1e3832655 100644 --- a/tee-worker/litentry/core/credentials/src/error.rs +++ b/tee-worker/litentry/core/credentials/src/error.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/credentials/src/generic_discord_role/mod.rs b/tee-worker/litentry/core/credentials/src/generic_discord_role/mod.rs index b7441d0a63..70bd8cb540 100644 --- a/tee-worker/litentry/core/credentials/src/generic_discord_role/mod.rs +++ b/tee-worker/litentry/core/credentials/src/generic_discord_role/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/credentials/src/lib.rs b/tee-worker/litentry/core/credentials/src/lib.rs index 19d76ea977..b35050ff72 100644 --- a/tee-worker/litentry/core/credentials/src/lib.rs +++ b/tee-worker/litentry/core/credentials/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/credentials/src/litentry_profile/holding_amount.rs b/tee-worker/litentry/core/credentials/src/litentry_profile/holding_amount.rs index 9b89fd07e2..5caadc795d 100644 --- a/tee-worker/litentry/core/credentials/src/litentry_profile/holding_amount.rs +++ b/tee-worker/litentry/core/credentials/src/litentry_profile/holding_amount.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/credentials/src/litentry_profile/lit_staking.rs b/tee-worker/litentry/core/credentials/src/litentry_profile/lit_staking.rs index cd5ac65ed1..36cce7cfae 100644 --- a/tee-worker/litentry/core/credentials/src/litentry_profile/lit_staking.rs +++ b/tee-worker/litentry/core/credentials/src/litentry_profile/lit_staking.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/credentials/src/litentry_profile/mirror.rs b/tee-worker/litentry/core/credentials/src/litentry_profile/mirror.rs index b7176051be..1e79d3db80 100644 --- a/tee-worker/litentry/core/credentials/src/litentry_profile/mirror.rs +++ b/tee-worker/litentry/core/credentials/src/litentry_profile/mirror.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/credentials/src/litentry_profile/mod.rs b/tee-worker/litentry/core/credentials/src/litentry_profile/mod.rs index 630660173d..25d626a392 100644 --- a/tee-worker/litentry/core/credentials/src/litentry_profile/mod.rs +++ b/tee-worker/litentry/core/credentials/src/litentry_profile/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/credentials/src/litentry_profile/token_balance.rs b/tee-worker/litentry/core/credentials/src/litentry_profile/token_balance.rs index 21f9da8744..8dc755b227 100644 --- a/tee-worker/litentry/core/credentials/src/litentry_profile/token_balance.rs +++ b/tee-worker/litentry/core/credentials/src/litentry_profile/token_balance.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/credentials/src/nodereal/amount_holding/evm_amount_holding.rs b/tee-worker/litentry/core/credentials/src/nodereal/amount_holding/evm_amount_holding.rs index 4bd0ed6f3e..ccdabd2851 100644 --- a/tee-worker/litentry/core/credentials/src/nodereal/amount_holding/evm_amount_holding.rs +++ b/tee-worker/litentry/core/credentials/src/nodereal/amount_holding/evm_amount_holding.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/credentials/src/nodereal/amount_holding/mod.rs b/tee-worker/litentry/core/credentials/src/nodereal/amount_holding/mod.rs index 09c8ec0fdf..c7d0e8eed4 100644 --- a/tee-worker/litentry/core/credentials/src/nodereal/amount_holding/mod.rs +++ b/tee-worker/litentry/core/credentials/src/nodereal/amount_holding/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/credentials/src/nodereal/bnb_domain/bnb_digit_domain_club_amount.rs b/tee-worker/litentry/core/credentials/src/nodereal/bnb_domain/bnb_digit_domain_club_amount.rs index bfa17b470a..f92663c7b6 100644 --- a/tee-worker/litentry/core/credentials/src/nodereal/bnb_domain/bnb_digit_domain_club_amount.rs +++ b/tee-worker/litentry/core/credentials/src/nodereal/bnb_domain/bnb_digit_domain_club_amount.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/credentials/src/nodereal/bnb_domain/bnb_domain_holding_amount.rs b/tee-worker/litentry/core/credentials/src/nodereal/bnb_domain/bnb_domain_holding_amount.rs index 4d5611648b..56d1bd2917 100644 --- a/tee-worker/litentry/core/credentials/src/nodereal/bnb_domain/bnb_domain_holding_amount.rs +++ b/tee-worker/litentry/core/credentials/src/nodereal/bnb_domain/bnb_domain_holding_amount.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/credentials/src/nodereal/bnb_domain/mod.rs b/tee-worker/litentry/core/credentials/src/nodereal/bnb_domain/mod.rs index 7ff0d828f1..651ba32826 100644 --- a/tee-worker/litentry/core/credentials/src/nodereal/bnb_domain/mod.rs +++ b/tee-worker/litentry/core/credentials/src/nodereal/bnb_domain/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/credentials/src/nodereal/crypto_summary/mod.rs b/tee-worker/litentry/core/credentials/src/nodereal/crypto_summary/mod.rs index 42527c2ebd..1c587c060a 100644 --- a/tee-worker/litentry/core/credentials/src/nodereal/crypto_summary/mod.rs +++ b/tee-worker/litentry/core/credentials/src/nodereal/crypto_summary/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/credentials/src/nodereal/crypto_summary/summary.rs b/tee-worker/litentry/core/credentials/src/nodereal/crypto_summary/summary.rs index 3d768c4735..546fe21948 100644 --- a/tee-worker/litentry/core/credentials/src/nodereal/crypto_summary/summary.rs +++ b/tee-worker/litentry/core/credentials/src/nodereal/crypto_summary/summary.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/credentials/src/nodereal/mod.rs b/tee-worker/litentry/core/credentials/src/nodereal/mod.rs index 84a4bff2f7..4c852da296 100644 --- a/tee-worker/litentry/core/credentials/src/nodereal/mod.rs +++ b/tee-worker/litentry/core/credentials/src/nodereal/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/credentials/src/nodereal/nft_holder/mod.rs b/tee-worker/litentry/core/credentials/src/nodereal/nft_holder/mod.rs index b023ebf540..a4d2197d8c 100644 --- a/tee-worker/litentry/core/credentials/src/nodereal/nft_holder/mod.rs +++ b/tee-worker/litentry/core/credentials/src/nodereal/nft_holder/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/credentials/src/nodereal/nft_holder/weirdo_ghost_gang_holder.rs b/tee-worker/litentry/core/credentials/src/nodereal/nft_holder/weirdo_ghost_gang_holder.rs index 4d07f87d45..89aca4a0cc 100644 --- a/tee-worker/litentry/core/credentials/src/nodereal/nft_holder/weirdo_ghost_gang_holder.rs +++ b/tee-worker/litentry/core/credentials/src/nodereal/nft_holder/weirdo_ghost_gang_holder.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/credentials/src/oneblock/mod.rs b/tee-worker/litentry/core/credentials/src/oneblock/mod.rs index bd0e2ef2ed..df848f6850 100644 --- a/tee-worker/litentry/core/credentials/src/oneblock/mod.rs +++ b/tee-worker/litentry/core/credentials/src/oneblock/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/credentials/src/schema.rs b/tee-worker/litentry/core/credentials/src/schema.rs index 184d3ddb00..a942db8233 100644 --- a/tee-worker/litentry/core/credentials/src/schema.rs +++ b/tee-worker/litentry/core/credentials/src/schema.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/credentials/src/vip3.rs b/tee-worker/litentry/core/credentials/src/vip3.rs index 7926128b60..dee9ed306e 100644 --- a/tee-worker/litentry/core/credentials/src/vip3.rs +++ b/tee-worker/litentry/core/credentials/src/vip3.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/data-providers/src/achainable.rs b/tee-worker/litentry/core/data-providers/src/achainable.rs index d60be8c4dc..ae44d49c74 100644 --- a/tee-worker/litentry/core/data-providers/src/achainable.rs +++ b/tee-worker/litentry/core/data-providers/src/achainable.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/data-providers/src/achainable_names.rs b/tee-worker/litentry/core/data-providers/src/achainable_names.rs index d3b6f09921..a42d3ed2e4 100644 --- a/tee-worker/litentry/core/data-providers/src/achainable_names.rs +++ b/tee-worker/litentry/core/data-providers/src/achainable_names.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/data-providers/src/discord_litentry.rs b/tee-worker/litentry/core/data-providers/src/discord_litentry.rs index 99ce9d60d6..129f5ce55e 100644 --- a/tee-worker/litentry/core/data-providers/src/discord_litentry.rs +++ b/tee-worker/litentry/core/data-providers/src/discord_litentry.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/data-providers/src/discord_official.rs b/tee-worker/litentry/core/data-providers/src/discord_official.rs index 5ab8a73293..938b996133 100644 --- a/tee-worker/litentry/core/data-providers/src/discord_official.rs +++ b/tee-worker/litentry/core/data-providers/src/discord_official.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/data-providers/src/geniidata.rs b/tee-worker/litentry/core/data-providers/src/geniidata.rs index a7e7861c9c..382844c078 100644 --- a/tee-worker/litentry/core/data-providers/src/geniidata.rs +++ b/tee-worker/litentry/core/data-providers/src/geniidata.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/data-providers/src/lib.rs b/tee-worker/litentry/core/data-providers/src/lib.rs index 936203826e..ce252983e0 100644 --- a/tee-worker/litentry/core/data-providers/src/lib.rs +++ b/tee-worker/litentry/core/data-providers/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/data-providers/src/nodereal.rs b/tee-worker/litentry/core/data-providers/src/nodereal.rs index ab2e14b40b..d976f56b52 100644 --- a/tee-worker/litentry/core/data-providers/src/nodereal.rs +++ b/tee-worker/litentry/core/data-providers/src/nodereal.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/data-providers/src/nodereal_jsonrpc.rs b/tee-worker/litentry/core/data-providers/src/nodereal_jsonrpc.rs index e8d58a234a..ff9cf7b52f 100644 --- a/tee-worker/litentry/core/data-providers/src/nodereal_jsonrpc.rs +++ b/tee-worker/litentry/core/data-providers/src/nodereal_jsonrpc.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/data-providers/src/twitter_official.rs b/tee-worker/litentry/core/data-providers/src/twitter_official.rs index 5039b2194b..09404f862a 100644 --- a/tee-worker/litentry/core/data-providers/src/twitter_official.rs +++ b/tee-worker/litentry/core/data-providers/src/twitter_official.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/data-providers/src/vip3.rs b/tee-worker/litentry/core/data-providers/src/vip3.rs index 1f0fb6c392..3222f63bf9 100644 --- a/tee-worker/litentry/core/data-providers/src/vip3.rs +++ b/tee-worker/litentry/core/data-providers/src/vip3.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/identity-verification/src/error.rs b/tee-worker/litentry/core/identity-verification/src/error.rs index 742a3238ff..40fcacaa86 100644 --- a/tee-worker/litentry/core/identity-verification/src/error.rs +++ b/tee-worker/litentry/core/identity-verification/src/error.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/identity-verification/src/lib.rs b/tee-worker/litentry/core/identity-verification/src/lib.rs index 03692ebb67..b2d9f706fd 100644 --- a/tee-worker/litentry/core/identity-verification/src/lib.rs +++ b/tee-worker/litentry/core/identity-verification/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/identity-verification/src/web2/mod.rs b/tee-worker/litentry/core/identity-verification/src/web2/mod.rs index fa0faf933b..66cf832468 100644 --- a/tee-worker/litentry/core/identity-verification/src/web2/mod.rs +++ b/tee-worker/litentry/core/identity-verification/src/web2/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/mock-server/src/achainable.rs b/tee-worker/litentry/core/mock-server/src/achainable.rs index 4a67e7a9e5..89794d54c1 100644 --- a/tee-worker/litentry/core/mock-server/src/achainable.rs +++ b/tee-worker/litentry/core/mock-server/src/achainable.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/mock-server/src/discord_litentry.rs b/tee-worker/litentry/core/mock-server/src/discord_litentry.rs index 8d22c342c0..86681a38a8 100644 --- a/tee-worker/litentry/core/mock-server/src/discord_litentry.rs +++ b/tee-worker/litentry/core/mock-server/src/discord_litentry.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/mock-server/src/discord_official.rs b/tee-worker/litentry/core/mock-server/src/discord_official.rs index 58ba9813ef..ae85aec1ce 100644 --- a/tee-worker/litentry/core/mock-server/src/discord_official.rs +++ b/tee-worker/litentry/core/mock-server/src/discord_official.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/mock-server/src/lib.rs b/tee-worker/litentry/core/mock-server/src/lib.rs index 7f3770b738..ebe5f3245b 100644 --- a/tee-worker/litentry/core/mock-server/src/lib.rs +++ b/tee-worker/litentry/core/mock-server/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/mock-server/src/nodereal_jsonrpc.rs b/tee-worker/litentry/core/mock-server/src/nodereal_jsonrpc.rs index 4630812623..43d97de947 100644 --- a/tee-worker/litentry/core/mock-server/src/nodereal_jsonrpc.rs +++ b/tee-worker/litentry/core/mock-server/src/nodereal_jsonrpc.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/mock-server/src/twitter_litentry.rs b/tee-worker/litentry/core/mock-server/src/twitter_litentry.rs index 10a59a18ac..00443af0eb 100644 --- a/tee-worker/litentry/core/mock-server/src/twitter_litentry.rs +++ b/tee-worker/litentry/core/mock-server/src/twitter_litentry.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/mock-server/src/twitter_official.rs b/tee-worker/litentry/core/mock-server/src/twitter_official.rs index a8d6193e3a..2ebe61ba93 100644 --- a/tee-worker/litentry/core/mock-server/src/twitter_official.rs +++ b/tee-worker/litentry/core/mock-server/src/twitter_official.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/scheduled-enclave/src/error.rs b/tee-worker/litentry/core/scheduled-enclave/src/error.rs index 6353db15f5..70228b33a0 100644 --- a/tee-worker/litentry/core/scheduled-enclave/src/error.rs +++ b/tee-worker/litentry/core/scheduled-enclave/src/error.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/scheduled-enclave/src/io.rs b/tee-worker/litentry/core/scheduled-enclave/src/io.rs index 9912fe4a6f..7aad2bc57d 100644 --- a/tee-worker/litentry/core/scheduled-enclave/src/io.rs +++ b/tee-worker/litentry/core/scheduled-enclave/src/io.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/scheduled-enclave/src/lib.rs b/tee-worker/litentry/core/scheduled-enclave/src/lib.rs index e71edfb88a..ddb47e2e3d 100644 --- a/tee-worker/litentry/core/scheduled-enclave/src/lib.rs +++ b/tee-worker/litentry/core/scheduled-enclave/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/stf-task/receiver/src/handler/assertion.rs b/tee-worker/litentry/core/stf-task/receiver/src/handler/assertion.rs index 0ee5629a70..dc9c355e85 100644 --- a/tee-worker/litentry/core/stf-task/receiver/src/handler/assertion.rs +++ b/tee-worker/litentry/core/stf-task/receiver/src/handler/assertion.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/stf-task/receiver/src/handler/identity_verification.rs b/tee-worker/litentry/core/stf-task/receiver/src/handler/identity_verification.rs index 952120d07c..dcb2a2159e 100644 --- a/tee-worker/litentry/core/stf-task/receiver/src/handler/identity_verification.rs +++ b/tee-worker/litentry/core/stf-task/receiver/src/handler/identity_verification.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/stf-task/receiver/src/handler/mod.rs b/tee-worker/litentry/core/stf-task/receiver/src/handler/mod.rs index 6817d541d1..fcd656019a 100644 --- a/tee-worker/litentry/core/stf-task/receiver/src/handler/mod.rs +++ b/tee-worker/litentry/core/stf-task/receiver/src/handler/mod.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/stf-task/receiver/src/lib.rs b/tee-worker/litentry/core/stf-task/receiver/src/lib.rs index e960407c10..5ce085919e 100644 --- a/tee-worker/litentry/core/stf-task/receiver/src/lib.rs +++ b/tee-worker/litentry/core/stf-task/receiver/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/stf-task/sender/src/error.rs b/tee-worker/litentry/core/stf-task/sender/src/error.rs index 28d16f3281..8c8f29060f 100644 --- a/tee-worker/litentry/core/stf-task/sender/src/error.rs +++ b/tee-worker/litentry/core/stf-task/sender/src/error.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/stf-task/sender/src/lib.rs b/tee-worker/litentry/core/stf-task/sender/src/lib.rs index 0a15729363..2f434f69e9 100644 --- a/tee-worker/litentry/core/stf-task/sender/src/lib.rs +++ b/tee-worker/litentry/core/stf-task/sender/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/core/stf-task/sender/src/stf_task_sender.rs b/tee-worker/litentry/core/stf-task/sender/src/stf_task_sender.rs index e5f8b3fcce..6ad8cb5412 100644 --- a/tee-worker/litentry/core/stf-task/sender/src/stf_task_sender.rs +++ b/tee-worker/litentry/core/stf-task/sender/src/stf_task_sender.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/macros/src/lib.rs b/tee-worker/litentry/macros/src/lib.rs index b57ac19473..f76bbe03c0 100644 --- a/tee-worker/litentry/macros/src/lib.rs +++ b/tee-worker/litentry/macros/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/pallets/identity-management/src/identity_context.rs b/tee-worker/litentry/pallets/identity-management/src/identity_context.rs index fb40d632a1..0ca6b2b5a9 100644 --- a/tee-worker/litentry/pallets/identity-management/src/identity_context.rs +++ b/tee-worker/litentry/pallets/identity-management/src/identity_context.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/pallets/identity-management/src/lib.rs b/tee-worker/litentry/pallets/identity-management/src/lib.rs index 71657036f6..8f0891b221 100644 --- a/tee-worker/litentry/pallets/identity-management/src/lib.rs +++ b/tee-worker/litentry/pallets/identity-management/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/pallets/identity-management/src/migrations.rs b/tee-worker/litentry/pallets/identity-management/src/migrations.rs index 60969870ae..a50ece4e68 100644 --- a/tee-worker/litentry/pallets/identity-management/src/migrations.rs +++ b/tee-worker/litentry/pallets/identity-management/src/migrations.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/pallets/identity-management/src/mock.rs b/tee-worker/litentry/pallets/identity-management/src/mock.rs index 4be1d2633e..4985bf026e 100644 --- a/tee-worker/litentry/pallets/identity-management/src/mock.rs +++ b/tee-worker/litentry/pallets/identity-management/src/mock.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/pallets/identity-management/src/tests.rs b/tee-worker/litentry/pallets/identity-management/src/tests.rs index 33753a3bc3..41fa9282ab 100644 --- a/tee-worker/litentry/pallets/identity-management/src/tests.rs +++ b/tee-worker/litentry/pallets/identity-management/src/tests.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/primitives/src/aes.rs b/tee-worker/litentry/primitives/src/aes.rs index d63b02432a..8abbb7b149 100644 --- a/tee-worker/litentry/primitives/src/aes.rs +++ b/tee-worker/litentry/primitives/src/aes.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/primitives/src/aes_request.rs b/tee-worker/litentry/primitives/src/aes_request.rs index 7c133429e2..998d642837 100644 --- a/tee-worker/litentry/primitives/src/aes_request.rs +++ b/tee-worker/litentry/primitives/src/aes_request.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/primitives/src/bitcoin_signature.rs b/tee-worker/litentry/primitives/src/bitcoin_signature.rs index cb6db71a23..689e088fbc 100644 --- a/tee-worker/litentry/primitives/src/bitcoin_signature.rs +++ b/tee-worker/litentry/primitives/src/bitcoin_signature.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/primitives/src/ethereum_signature.rs b/tee-worker/litentry/primitives/src/ethereum_signature.rs index 75496fa61d..e0869efd08 100644 --- a/tee-worker/litentry/primitives/src/ethereum_signature.rs +++ b/tee-worker/litentry/primitives/src/ethereum_signature.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/primitives/src/lib.rs b/tee-worker/litentry/primitives/src/lib.rs index b1f76256e7..4bf0e4c6c1 100644 --- a/tee-worker/litentry/primitives/src/lib.rs +++ b/tee-worker/litentry/primitives/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify diff --git a/tee-worker/litentry/primitives/src/validation_data.rs b/tee-worker/litentry/primitives/src/validation_data.rs index aac3427799..0b9eb19001 100644 --- a/tee-worker/litentry/primitives/src/validation_data.rs +++ b/tee-worker/litentry/primitives/src/validation_data.rs @@ -1,4 +1,4 @@ -// Copyright 2020-2023 Trust Computing GmbH. +// Copyright 2020-2024 Trust Computing GmbH. // This file is part of Litentry. // // Litentry is free software: you can redistribute it and/or modify From 03bdde8bcd447adbb86b57300905f5ca4599d462 Mon Sep 17 00:00:00 2001 From: BillyWooo Date: Wed, 24 Jan 2024 21:17:18 +0100 Subject: [PATCH 29/51] mess up the relative path after env refactor (#2424) --- tee-worker/local-setup/launch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tee-worker/local-setup/launch.py b/tee-worker/local-setup/launch.py index cc0c61f781..3c15bd1e46 100755 --- a/tee-worker/local-setup/launch.py +++ b/tee-worker/local-setup/launch.py @@ -156,7 +156,7 @@ def offset_port(offset): def setup_environment(offset, config, parachain_dir): - load_dotenv(".env.dev") + load_dotenv("./local-setup/.env.dev") offset_port(offset) check_all_ports_and_reallocate() generate_config_local_json(parachain_dir) From 969b2016190d03c01473ce6044a512ce980bf1e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 25 Jan 2024 00:08:32 +0100 Subject: [PATCH 30/51] Bump snow from 0.9.2 to 0.9.5 (#2426) Bumps [snow](https://github.com/mcginty/snow) from 0.9.2 to 0.9.5. - [Release notes](https://github.com/mcginty/snow/releases) - [Commits](https://github.com/mcginty/snow/compare/v0.9.2...v0.9.5) --- updated-dependencies: - dependency-name: snow dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 133 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 75 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0530c430ad..b0d2fbca6a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -927,25 +927,24 @@ checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" [[package]] name = "chacha20" -version = "0.8.2" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c80e5460aa66fe3b91d40bcbdab953a597b60053e34d684ac6903f863b680a6" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ "cfg-if", - "cipher 0.3.0", + "cipher 0.4.4", "cpufeatures", - "zeroize", ] [[package]] name = "chacha20poly1305" -version = "0.9.1" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18446b09be63d457bbec447509e85f662f32952b035ce892290396bc0b0cff5" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" dependencies = [ - "aead 0.4.3", + "aead 0.5.2", "chacha20", - "cipher 0.3.0", + "cipher 0.4.4", "poly1305", "zeroize", ] @@ -1004,6 +1003,7 @@ checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ "crypto-common", "inout", + "zeroize", ] [[package]] @@ -1190,7 +1190,7 @@ dependencies = [ "litentry-macros", "pallet-evm", "parity-scale-codec", - "ring", + "ring 0.16.20", "scale-info", "serde", "sp-core", @@ -2013,18 +2013,31 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.0.0-rc.1" +version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d4ba9852b42210c7538b75484f9daa0655e9a3ac04f693747bb0f02cf3cfe16" +checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c" dependencies = [ "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", "fiat-crypto", - "packed_simd_2", "platforms 3.0.2", + "rustc_version", "subtle", "zeroize", ] +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "cxx" version = "1.0.102" @@ -3072,9 +3085,9 @@ dependencies = [ [[package]] name = "fiat-crypto" -version = "0.1.20" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" +checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7" [[package]] name = "file-per-thread-logger" @@ -4732,7 +4745,7 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" dependencies = [ - "spin", + "spin 0.5.2", ] [[package]] @@ -4757,12 +4770,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "libm" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" - [[package]] name = "libm" version = "0.2.7" @@ -5136,7 +5143,7 @@ dependencies = [ "libp2p-core 0.39.2", "libp2p-identity", "rcgen 0.10.0", - "ring", + "ring 0.16.20", "rustls 0.20.8", "thiserror", "webpki 0.22.0", @@ -6731,16 +6738,6 @@ dependencies = [ "sha2 0.10.7", ] -[[package]] -name = "packed_simd_2" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1914cd452d8fccd6f9db48147b29fd4ae05bea9dc5d9ad578509f72415de282" -dependencies = [ - "cfg-if", - "libm 0.1.4", -] - [[package]] name = "pallet-asset-manager" version = "0.1.0" @@ -9668,13 +9665,13 @@ dependencies = [ [[package]] name = "poly1305" -version = "0.7.2" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" dependencies = [ "cpufeatures", "opaque-debug 0.3.0", - "universal-hash 0.4.1", + "universal-hash 0.5.1", ] [[package]] @@ -10040,7 +10037,7 @@ checksum = "c956be1b23f4261676aed05a0046e204e8a6836e50203902683a718af0797989" dependencies = [ "bytes", "rand 0.8.5", - "ring", + "ring 0.16.20", "rustc-hash", "rustls 0.20.8", "slab", @@ -10180,7 +10177,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" dependencies = [ "pem", - "ring", + "ring 0.16.20", "time", "x509-parser 0.13.2", "yasna", @@ -10193,7 +10190,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" dependencies = [ "pem", - "ring", + "ring 0.16.20", "time", "yasna", ] @@ -10369,7 +10366,7 @@ dependencies = [ "log", "once_cell", "rkyv", - "spin", + "spin 0.5.2", "untrusted 0.7.1", "winapi", "xous", @@ -10377,6 +10374,20 @@ dependencies = [ "xous-ipc", ] +[[package]] +name = "ring" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babe80d5c16becf6594aa32ad2be8fe08498e7ae60b77de8df700e67f191d7e" +dependencies = [ + "cc", + "getrandom 0.2.10", + "libc", + "spin 0.9.8", + "untrusted 0.9.0", + "windows-sys 0.48.0", +] + [[package]] name = "ripemd" version = "0.1.3" @@ -10836,7 +10847,7 @@ checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" dependencies = [ "base64 0.13.1", "log", - "ring", + "ring 0.16.20", "sct 0.6.1", "webpki 0.21.4", ] @@ -10848,7 +10859,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" dependencies = [ "log", - "ring", + "ring 0.16.20", "sct 0.7.0", "webpki 0.22.0", ] @@ -10885,7 +10896,7 @@ name = "rustls-webpki" version = "0.102.0-alpha.3" source = "git+https://github.com/rustls/webpki?rev=da923ed#da923edaab56f599971e58773617fb574cd019dc" dependencies = [ - "ring", + "ring 0.16.20", "rustls-pki-types", "untrusted 0.9.0", ] @@ -12152,7 +12163,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" dependencies = [ - "ring", + "ring 0.16.20", "untrusted 0.7.1", ] @@ -12162,7 +12173,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" dependencies = [ - "ring", + "ring 0.16.20", "untrusted 0.7.1", ] @@ -12331,7 +12342,7 @@ dependencies = [ "hex", "hex-literal 0.4.1", "parity-scale-codec", - "ring", + "ring 0.16.20", "rustls-webpki", "scale-info", "serde", @@ -12555,16 +12566,16 @@ checksum = "5e9f0ab6ef7eb7353d9119c170a436d1bf248eea575ac42d19d12f4e34130831" [[package]] name = "snow" -version = "0.9.2" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ccba027ba85743e09d15c03296797cad56395089b832b48b5a5217880f57733" +checksum = "2e87c18a6608909007e75a60e04d03eda77b601c94de1c74d9a9dc2c04ab789a" dependencies = [ - "aes-gcm 0.9.4", + "aes-gcm 0.10.2", "blake2", "chacha20poly1305", - "curve25519-dalek 4.0.0-rc.1", + "curve25519-dalek 4.1.1", "rand_core 0.6.4", - "ring", + "ring 0.17.3", "rustc_version", "sha2 0.10.7", "subtle", @@ -13316,6 +13327,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "spinners" version = "4.1.0" @@ -13485,7 +13502,7 @@ dependencies = [ "lazy_static", "md-5", "rand 0.8.5", - "ring", + "ring 0.16.20", "subtle", "thiserror", "tokio", @@ -14326,7 +14343,7 @@ dependencies = [ "log", "md-5", "rand 0.8.5", - "ring", + "ring 0.16.20", "stun", "thiserror", "tokio", @@ -14724,7 +14741,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57d20cb3c59b788653d99541c646c561c9dd26506f25c0cebfe810659c54c6d7" dependencies = [ "downcast-rs", - "libm 0.2.7", + "libm", "memory_units", "num-rational", "num-traits", @@ -14936,7 +14953,7 @@ version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" dependencies = [ - "ring", + "ring 0.16.20", "untrusted 0.7.1", ] @@ -14946,7 +14963,7 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" dependencies = [ - "ring", + "ring 0.16.20", "untrusted 0.7.1", ] @@ -14975,7 +14992,7 @@ dependencies = [ "rand 0.8.5", "rcgen 0.9.3", "regex", - "ring", + "ring 0.16.20", "rtcp", "rtp", "rustls 0.19.1", @@ -15040,7 +15057,7 @@ dependencies = [ "rand 0.8.5", "rand_core 0.6.4", "rcgen 0.9.3", - "ring", + "ring 0.16.20", "rustls 0.19.1", "sec1 0.3.0", "serde", @@ -15606,7 +15623,7 @@ dependencies = [ "lazy_static", "nom", "oid-registry 0.4.0", - "ring", + "ring 0.16.20", "rusticata-macros", "thiserror", "time", From e6cfa16781bc8799c9ffd17bbd380894d477840b Mon Sep 17 00:00:00 2001 From: Jonathan Alvarez Date: Thu, 25 Jan 2024 15:26:03 -0500 Subject: [PATCH 31/51] chore(launch scripts): wait for parachain block finalization (#2425) --- scripts/launch-local-binary.sh | 22 ++++++++++++---------- scripts/launch-local-docker.sh | 9 +++------ scripts/launch-standalone.sh | 20 ++++++++++++++++++++ 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/scripts/launch-local-binary.sh b/scripts/launch-local-binary.sh index 55df7b5eb7..f2e1354fe5 100755 --- a/scripts/launch-local-binary.sh +++ b/scripts/launch-local-binary.sh @@ -109,31 +109,33 @@ $PARACHAIN_BIN --alice --collator --force-authoring --tmp --chain $CHAIN-dev \ --bootnodes /ip4/127.0.0.1/tcp/${AlicePort:-30336}/p2p/$RELAY_ALICE_IDENTITY &> "para.alice.log" & sleep 10 -echo "register parathread now ..." +# Prepare Node.js enviroment cd "$ROOTDIR/ts-tests" + if [[ -z "${NODE_ENV}" ]]; then echo "NODE_ENV=ci" > .env else echo "NODE_ENV=${NODE_ENV}" > .env fi corepack pnpm install + + +echo "register parathread now ..." corepack pnpm run register-parathread 2>&1 | tee "$LITENTRY_PARACHAIN_DIR/register-parathread.log" + print_divider -echo "upgrade parathread to parachain now ..." +echo "upgrade parathread to parachain in 90s..." # Wait for 90s to allow onboarding finish, after that we do the upgrade sleep 90 -cd "$ROOTDIR/ts-tests" -if [[ -z "${NODE_ENV}" ]]; then - echo "NODE_ENV=ci" > .env -else - echo "NODE_ENV=${NODE_ENV}" > .env -fi -corepack pnpm install corepack pnpm run upgrade-parathread 2>&1 | tee "$LITENTRY_PARACHAIN_DIR/upgrade-parathread.log" print_divider -echo "done. please check $LITENTRY_PARACHAIN_DIR for generated files if need" +echo "wait for parachain to produce block #1..." +pnpm run wait-finalized-block 2>&1 + +echo +echo "Check $LITENTRY_PARACHAIN_DIR for generated files if need" print_divider diff --git a/scripts/launch-local-docker.sh b/scripts/launch-local-docker.sh index 51bea041b8..6d912e6400 100755 --- a/scripts/launch-local-docker.sh +++ b/scripts/launch-local-docker.sh @@ -39,15 +39,12 @@ pnpm install print_divider -echo "Waiting for parachain to produce block #1..." -pnpm run wait-finalized-block 2>&1 - -print_divider - echo "Extending leasing period..." pnpm run upgrade-parathread 2>&1 print_divider -echo "Done." +echo "Waiting for parachain to produce block #1..." +pnpm run wait-finalized-block 2>&1 + exit 0 diff --git a/scripts/launch-standalone.sh b/scripts/launch-standalone.sh index 0c78c2ecdf..459b7bbc7c 100755 --- a/scripts/launch-standalone.sh +++ b/scripts/launch-standalone.sh @@ -22,6 +22,10 @@ if ! "$PARACHAIN_BIN" --version &> /dev/null; then exit 1 fi +function print_divider() { + echo "------------------------------------------------------------" +} + echo "Starting litentry-collator in standalone mode ..." $PARACHAIN_BIN --dev --unsafe-ws-external --unsafe-rpc-external \ @@ -29,3 +33,19 @@ $PARACHAIN_BIN --dev --unsafe-ws-external --unsafe-rpc-external \ &> "$LITENTRY_PARACHAIN_DIR/para.alice.log" & sleep 10 + +print_divider + +# Check parachain status +echo "wait for parachain to produce block #1..." +cd "$ROOTDIR/ts-tests" + +if [[ -z "${NODE_ENV}" ]]; then + echo "NODE_ENV=ci" > .env +else + echo "NODE_ENV=${NODE_ENV}" > .env +fi +corepack pnpm install +pnpm run wait-finalized-block 2>&1 + +print_divider From 8737753e4e79a032d755483443d0c5570e7cde66 Mon Sep 17 00:00:00 2001 From: Kasper Ziemianek Date: Fri, 26 Jan 2024 16:52:06 +0100 Subject: [PATCH 32/51] remove parachain dependency on tee (#2433) --- Cargo.lock | 71 +++++++++++--- Cargo.toml | 2 + primitives/core/Cargo.toml | 4 +- primitives/core/macros/Cargo.toml | 8 -- primitives/core/macros/src/lib.rs | 93 ++++--------------- primitives/core/proc-macros/Cargo.toml | 15 +++ .../core/proc-macros}/src/lib.rs | 79 +++++++++++++++- .../core/{macros => proc-macros}/src/reuse.rs | 0 primitives/core/src/identity.rs | 6 +- primitives/core/src/lib.rs | 2 +- primitives/hex/Cargo.toml | 19 ++++ primitives/hex/src/lib.rs | 69 ++++++++++++++ tee-worker/Cargo.lock | 51 ++++++---- tee-worker/Cargo.toml | 1 - .../app-libs/parentchain-interface/Cargo.toml | 1 + .../src/integritee/event_handler.rs | 2 +- .../src/target_a/event_handler.rs | 2 +- tee-worker/app-libs/stf/Cargo.toml | 1 + tee-worker/app-libs/stf/src/getter.rs | 3 +- .../app-libs/stf/src/trusted_call_litentry.rs | 3 +- tee-worker/cli/Cargo.toml | 1 + .../commands/litentry/request_vc.rs | 2 +- .../commands/litentry/request_vc_direct.rs | 2 +- tee-worker/core-primitives/utils/Cargo.toml | 1 + tee-worker/core-primitives/utils/src/hex.rs | 46 +-------- tee-worker/core-primitives/utils/src/lib.rs | 1 - .../core-primitives/utils/src/macros.rs | 35 ------- tee-worker/enclave-runtime/Cargo.lock | 39 +++++--- tee-worker/enclave-runtime/Cargo.toml | 4 +- tee-worker/enclave-runtime/src/lib.rs | 5 +- .../src/rpc/worker_api_direct.rs | 7 +- .../enclave-runtime/src/top_pool_execution.rs | 2 +- .../core/assertion-build-v2/Cargo.toml | 1 + .../src/token_holding_amount/mod.rs | 2 +- .../litentry/core/assertion-build/Cargo.toml | 1 + .../amount_holding/evm_amount_holding.rs | 2 +- .../litentry/core/data-providers/Cargo.toml | 1 + .../litentry/core/data-providers/src/lib.rs | 2 +- .../lc-vc-task-receiver/Cargo.toml | 1 + .../lc-vc-task-receiver/src/lib.rs | 2 +- tee-worker/litentry/macros/Cargo.toml | 12 --- tee-worker/litentry/primitives/Cargo.toml | 1 + tee-worker/litentry/primitives/src/lib.rs | 2 +- tee-worker/service/Cargo.toml | 2 + tee-worker/service/src/main_impl.rs | 2 +- tee-worker/service/src/teeracle/mod.rs | 2 +- .../sidechain/consensus/aura/Cargo.toml | 1 + .../sidechain/consensus/aura/src/lib.rs | 2 +- 48 files changed, 365 insertions(+), 248 deletions(-) create mode 100644 primitives/core/proc-macros/Cargo.toml rename {tee-worker/litentry/macros => primitives/core/proc-macros}/src/lib.rs (56%) rename primitives/core/{macros => proc-macros}/src/reuse.rs (100%) create mode 100644 primitives/hex/Cargo.toml create mode 100644 primitives/hex/src/lib.rs delete mode 100644 tee-worker/core-primitives/utils/src/macros.rs delete mode 100644 tee-worker/litentry/macros/Cargo.toml diff --git a/Cargo.lock b/Cargo.lock index b0d2fbca6a..1bfc20ed17 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -875,6 +875,16 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cargo_toml" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3f9629bc6c4388ea699781dc988c2b99766d7679b151c81990b4fa1208fafd3" +dependencies = [ + "serde", + "toml 0.8.2", +] + [[package]] name = "cc" version = "1.0.79" @@ -1186,8 +1196,9 @@ name = "core-primitives" version = "0.9.12" dependencies = [ "frame-support", - "itp-utils", + "litentry-hex-utils", "litentry-macros", + "litentry-proc-macros", "pallet-evm", "parity-scale-codec", "ring 0.16.20", @@ -4402,14 +4413,6 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" -[[package]] -name = "itp-utils" -version = "0.9.0" -dependencies = [ - "hex", - "parity-scale-codec", -] - [[package]] name = "jobserver" version = "0.1.26" @@ -5435,14 +5438,17 @@ dependencies = [ ] [[package]] -name = "litentry-macros" +name = "litentry-hex-utils" version = "0.9.12" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", + "hex", + "parity-scale-codec", ] +[[package]] +name = "litentry-macros" +version = "0.9.12" + [[package]] name = "litentry-parachain-runtime" version = "0.9.17" @@ -5524,6 +5530,16 @@ dependencies = [ "xcm-simulator", ] +[[package]] +name = "litentry-proc-macros" +version = "0.9.12" +dependencies = [ + "cargo_toml", + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "litmus-parachain-runtime" version = "0.9.17" @@ -9823,7 +9839,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "toml_edit", + "toml_edit 0.19.14", ] [[package]] @@ -14027,7 +14043,19 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_edit 0.19.14", +] + +[[package]] +name = "toml" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.20.2", ] [[package]] @@ -14052,6 +14080,19 @@ dependencies = [ "winnow", ] +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.0.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tower" version = "0.4.13" diff --git a/Cargo.toml b/Cargo.toml index 25f8a177fb..1ea3e746d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,9 @@ members = [ 'precompiles/*', 'primitives/common', 'primitives/core', + 'primitives/core/proc-macros', 'primitives/core/macros', + 'primitives/hex', 'primitives/sidechain', 'primitives/teeracle', 'primitives/teerex', diff --git a/primitives/core/Cargo.toml b/primitives/core/Cargo.toml index b42ebebf71..da25b3b95d 100644 --- a/primitives/core/Cargo.toml +++ b/primitives/core/Cargo.toml @@ -19,8 +19,9 @@ sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0 sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -itp-utils = { path = "../../tee-worker/core-primitives/utils", default-features = false } +litentry-hex-utils = { path = "../hex", default-features = false } litentry-macros = { path = "macros" } +litentry-proc-macros = { path = "proc-macros" } [features] default = ["std"] @@ -35,6 +36,5 @@ std = [ "sp-std/std", "sp-io/std", "ring/std", - "itp-utils/std", "pallet-evm/std", ] diff --git a/primitives/core/macros/Cargo.toml b/primitives/core/macros/Cargo.toml index 3743df47f7..c4f4e3ff25 100644 --- a/primitives/core/macros/Cargo.toml +++ b/primitives/core/macros/Cargo.toml @@ -4,11 +4,3 @@ description = 'Proc-macros used by Litentry crates.' name = "litentry-macros" version = "0.9.12" edition = "2021" - -[lib] -proc-macro = true - -[dependencies] -proc-macro2 = { version = "1" } -quote = { version = "1" } -syn = { version = "2", features = ["full", "visit-mut"] } diff --git a/primitives/core/macros/src/lib.rs b/primitives/core/macros/src/lib.rs index e988af4ccb..9494db3e0c 100644 --- a/primitives/core/macros/src/lib.rs +++ b/primitives/core/macros/src/lib.rs @@ -13,81 +13,24 @@ // // You should have received a copy of the GNU General Public License // along with Litentry. If not, see . - -#![allow(clippy::tabs_in_doc_comments)] - -use proc_macro::TokenStream; -use reuse::handle_reuse; -use syn::{parse_macro_input, Error}; - -mod reuse; - -/** -This macro is used to reuse implementations when the rust's trait system cannot gracefully express the abstraction. - -This works similar with `#[cfg(..)]` that sets the target only appear on the specified cases. - -# Usage: - -``` -use litentry_macros::reuse; - -#[reuse(x, y)] . // Define the cases that the following implementation expands for each one -mod __ { // Leave mod name with double discards, which is to be replaced by the cases - #[x] // This item would only appear on case `x` - fn u() { - __ - } - - #[y] // This item would only appear on case `y` - fn v(a: String) { - println!("hello world!") - } - - #[x] // Specifying multiple cases indicates that the item would appear on all of them - #[y] .// This behaviour is designed to be different from `#[cfg(..)]` - fn a() -> i32 { - #[x] // This statement would only appear on case `x` - let p = 1; - #[y] // This statement would only appear on case `y` - let p = 2; - p + 1 - } - - - fn g<#[x] 'a, #[y] T>(#[x] a: i32, #[y] a: u32) {} -} - -``` -Expands to: -``` -mod x { - fn a() -> i32 { - let p = 1; - p + 1 - } - fn u() { - println!("hello world!"); - } - fn g<'a>(a: i32) {} -} - -mod y { - fn a() -> i32 { - let p = 2; - p + 1 - } - fn v(a: String) { - println!("hello world!"); - } - fn g(a: u32) {} +#![cfg_attr(not(feature = "std"), no_std)] + +#[macro_export] +macro_rules! if_production_or { + ($prod_variant:expr, $non_prod_variant:expr) => { + if cfg!(feature = "production") { + $prod_variant + } else { + $non_prod_variant + } + }; } -``` -*/ -#[proc_macro_attribute] -pub fn reuse(args: TokenStream, input: TokenStream) -> TokenStream { - handle_reuse(parse_macro_input!(args), parse_macro_input!(input)) - .unwrap_or_else(Error::into_compile_error) - .into() +#[macro_export] +macro_rules! if_not_production { + ($expression:expr) => { + if cfg!(not(feature = "production")) { + $expression + } + }; } diff --git a/primitives/core/proc-macros/Cargo.toml b/primitives/core/proc-macros/Cargo.toml new file mode 100644 index 0000000000..3c7ddb598e --- /dev/null +++ b/primitives/core/proc-macros/Cargo.toml @@ -0,0 +1,15 @@ +[package] +authors = ['Trust Computing GmbH '] +description = 'Proc-macros used by Litentry crates.' +name = "litentry-proc-macros" +version = "0.9.12" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +cargo_toml = "0.16.3" +proc-macro2 = { version = "1" } +quote = { version = "1" } +syn = { version = "2", features = ["full", "visit-mut"] } diff --git a/tee-worker/litentry/macros/src/lib.rs b/primitives/core/proc-macros/src/lib.rs similarity index 56% rename from tee-worker/litentry/macros/src/lib.rs rename to primitives/core/proc-macros/src/lib.rs index f76bbe03c0..16ac88fe39 100644 --- a/tee-worker/litentry/macros/src/lib.rs +++ b/primitives/core/proc-macros/src/lib.rs @@ -14,10 +14,86 @@ // You should have received a copy of the GNU General Public License // along with Litentry. If not, see . +#![allow(clippy::tabs_in_doc_comments)] + use cargo_toml::{Dependency, Manifest}; use proc_macro::TokenStream; use quote::quote; +use reuse::handle_reuse; use std::fs; +use syn::{parse_macro_input, Error}; + +mod reuse; + +/** +This macro is used to reuse implementations when the rust's trait system cannot gracefully express the abstraction. + +This works similar with `#[cfg(..)]` that sets the target only appear on the specified cases. + +# Usage: + +``` +use litentry_proc_macros::reuse; + +#[reuse(x, y)] . // Define the cases that the following implementation expands for each one +mod __ { // Leave mod name with double discards, which is to be replaced by the cases + #[x] // This item would only appear on case `x` + fn u() { + __ + } + + #[y] // This item would only appear on case `y` + fn v(a: String) { + println!("hello world!") + } + + #[x] // Specifying multiple cases indicates that the item would appear on all of them + #[y] .// This behaviour is designed to be different from `#[cfg(..)]` + fn a() -> i32 { + #[x] // This statement would only appear on case `x` + let p = 1; + #[y] // This statement would only appear on case `y` + let p = 2; + p + 1 + } + + + fn g<#[x] 'a, #[y] T>(#[x] a: i32, #[y] a: u32) {} +} + +``` +Expands to: +``` +mod x { + fn a() -> i32 { + let p = 1; + p + 1 + } + fn u() { + println!("hello world!"); + } + fn g<'a>(a: i32) {} +} + +mod y { + fn a() -> i32 { + let p = 2; + p + 1 + } + fn v(a: String) { + println!("hello world!"); + } + fn g(a: u32) {} +} + +``` +*/ +#[proc_macro_attribute] +pub fn reuse(args: TokenStream, input: TokenStream) -> TokenStream { + handle_reuse(parse_macro_input!(args), parse_macro_input!(input)) + .unwrap_or_else(Error::into_compile_error) + .into() +} #[proc_macro] pub fn local_modules(_item: TokenStream) -> TokenStream { @@ -46,7 +122,8 @@ fn read_module_names(path: &str, relative_to: &str, module_names: &mut Vec'] +description = 'Litentry hex utils' +name = "litentry-hex-utils" +version = "0.9.12" +edition = "2021" + +[dependencies] +hex = { version = "0.4.3", default-features = false, features = ["alloc"] } + +[dev-dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } + + +[features] +default = ["std"] +std = [ + "hex/std", +] diff --git a/primitives/hex/src/lib.rs b/primitives/hex/src/lib.rs new file mode 100644 index 0000000000..b9015815ba --- /dev/null +++ b/primitives/hex/src/lib.rs @@ -0,0 +1,69 @@ +// Copyright 2020-2024 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; +use alloc::{string::String, vec::Vec}; + +use hex::FromHexError; + +/// Hex encodes given data and preappends a "0x". +pub fn hex_encode(data: &[u8]) -> String { + let mut hex_str = hex::encode(data); + hex_str.insert_str(0, "0x"); + hex_str +} + +/// Helper method for decoding hex. +pub fn decode_hex>(message: T) -> Result, FromHexError> { + let message = message.as_ref(); + let message = match message { + [b'0', b'x', hex_value @ ..] => hex_value, + _ => message, + }; + + let decoded_message = hex::decode(message)?; + Ok(decoded_message) +} + +#[cfg(test)] +mod tests { + use super::*; + use alloc::string::ToString; + use codec::{Decode, Encode}; + + #[test] + fn hex_encode_decode_works() { + let data = "Hello World!".to_string(); + + let hex_encoded_data = hex_encode(&data.encode()); + let decoded_data = + String::decode(&mut decode_hex(hex_encoded_data).unwrap().as_slice()).unwrap(); + + assert_eq!(data, decoded_data); + } + + #[test] + fn hex_encode_decode_works_empty_input() { + let data = String::new(); + + let hex_encoded_data = hex_encode(&data.encode()); + let decoded_data = + String::decode(&mut decode_hex(hex_encoded_data).unwrap().as_slice()).unwrap(); + + assert_eq!(data, decoded_data); + } +} diff --git a/tee-worker/Cargo.lock b/tee-worker/Cargo.lock index 08913fa98f..8882ec6ce8 100644 --- a/tee-worker/Cargo.lock +++ b/tee-worker/Cargo.lock @@ -973,7 +973,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3f9629bc6c4388ea699781dc988c2b99766d7679b151c81990b4fa1208fafd3" dependencies = [ "serde 1.0.193", - "toml 0.8.0", + "toml 0.8.2", ] [[package]] @@ -1305,8 +1305,9 @@ name = "core-primitives" version = "0.9.12" dependencies = [ "frame-support", - "itp-utils", - "litentry-macros 0.9.12", + "litentry-hex-utils", + "litentry-macros", + "litentry-proc-macros", "pallet-evm 6.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", "parity-scale-codec", "ring 0.16.20", @@ -4524,6 +4525,7 @@ dependencies = [ "itp-types", "itp-utils", "lc-scheduled-enclave", + "litentry-hex-utils", "litentry-primitives", "log 0.4.20", "parity-scale-codec", @@ -4577,6 +4579,7 @@ dependencies = [ "itp-types", "itp-utils", "lc-stf-task-sender", + "litentry-macros", "litentry-primitives", "log 0.4.20", "pallet-balances", @@ -5453,6 +5456,7 @@ name = "itp-utils" version = "0.9.0" dependencies = [ "hex 0.4.3", + "litentry-hex-utils", "parity-scale-codec", ] @@ -5534,6 +5538,7 @@ dependencies = [ "its-test", "its-validateer-fetch", "lc-scheduled-enclave", + "litentry-hex-utils", "log 0.4.20", "parity-scale-codec", "sgx_tstd", @@ -6139,6 +6144,7 @@ dependencies = [ "lc-data-providers", "lc-mock-server", "lc-stf-task-sender", + "litentry-hex-utils", "litentry-primitives", "log 0.4.20", "pallet-parachain-staking", @@ -6170,6 +6176,7 @@ dependencies = [ "lc-mock-server", "lc-service", "lc-stf-task-sender", + "litentry-hex-utils", "litentry-primitives", "log 0.4.20", "parity-scale-codec", @@ -6251,6 +6258,7 @@ dependencies = [ "itp-stf-primitives", "itp-utils", "lc-mock-server", + "litentry-macros", "litentry-primitives", "log 0.4.20", "parity-scale-codec", @@ -6424,6 +6432,7 @@ dependencies = [ "lc-stf-task-receiver", "lc-stf-task-sender", "lc-vc-task-sender", + "litentry-macros", "litentry-primitives", "log 0.4.20", "pallet-identity-management-tee", @@ -7089,6 +7098,7 @@ dependencies = [ "itp-types", "itp-utils", "lc-credentials", + "litentry-hex-utils", "litentry-primitives", "log 0.4.20", "pallet-balances", @@ -7117,21 +7127,15 @@ dependencies = [ ] [[package]] -name = "litentry-macros" -version = "0.1.0" +name = "litentry-hex-utils" +version = "0.9.12" dependencies = [ - "cargo_toml", - "quote", + "hex 0.4.3", ] [[package]] name = "litentry-macros" version = "0.9.12" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.32", -] [[package]] name = "litentry-primitives" @@ -7144,6 +7148,7 @@ dependencies = [ "hex 0.4.3", "itp-sgx-crypto", "itp-utils", + "litentry-hex-utils", "log 0.4.20", "pallet-evm 6.0.0-dev (git+https://github.com/integritee-network/frontier.git?branch=bar/polkadot-v0.9.42)", "parity-scale-codec", @@ -7163,6 +7168,16 @@ dependencies = [ "teerex-primitives", ] +[[package]] +name = "litentry-proc-macros" +version = "0.9.12" +dependencies = [ + "cargo_toml", + "proc-macro2", + "quote", + "syn 2.0.32", +] + [[package]] name = "litentry-worker" version = "0.0.1" @@ -7203,6 +7218,8 @@ dependencies = [ "lc-data-providers", "lc-mock-server", "lc-stf-task-sender", + "litentry-hex-utils", + "litentry-macros", "litentry-primitives", "log 0.4.20", "mockall", @@ -14799,14 +14816,14 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c226a7bba6d859b63c92c4b4fe69c5b6b72d0cb897dbc8e6012298e6154cb56e" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" dependencies = [ "serde 1.0.193", "serde_spanned", "toml_datetime", - "toml_edit 0.20.0", + "toml_edit 0.20.2", ] [[package]] @@ -14833,9 +14850,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.20.0" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ff63e60a958cefbb518ae1fd6566af80d9d4be430a33f3723dfc47d1d411d95" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" dependencies = [ "indexmap 2.0.0", "serde 1.0.193", diff --git a/tee-worker/Cargo.toml b/tee-worker/Cargo.toml index 8aaf35264a..120306da83 100644 --- a/tee-worker/Cargo.toml +++ b/tee-worker/Cargo.toml @@ -72,7 +72,6 @@ members = [ "sidechain/state", "sidechain/validateer-fetch", "litentry/primitives", - "litentry/macros", "litentry/pallets/identity-management", "litentry/core/stf-task/sender", "litentry/core/stf-task/receiver", diff --git a/tee-worker/app-libs/parentchain-interface/Cargo.toml b/tee-worker/app-libs/parentchain-interface/Cargo.toml index 39e4827588..59ec735302 100644 --- a/tee-worker/app-libs/parentchain-interface/Cargo.toml +++ b/tee-worker/app-libs/parentchain-interface/Cargo.toml @@ -29,6 +29,7 @@ sp-runtime = { default-features = false, git = "https://github.com/paritytech/su # litentry lc-scheduled-enclave = { path = "../../litentry/core/scheduled-enclave", default-features = false, optional = true } +litentry-hex-utils = { path = "../../../primitives/hex", default-features = false } litentry-primitives = { path = "../../litentry/primitives", default-features = false } sp-std = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } diff --git a/tee-worker/app-libs/parentchain-interface/src/integritee/event_handler.rs b/tee-worker/app-libs/parentchain-interface/src/integritee/event_handler.rs index 1cc6cd3d0e..c8111afae0 100644 --- a/tee-worker/app-libs/parentchain-interface/src/integritee/event_handler.rs +++ b/tee-worker/app-libs/parentchain-interface/src/integritee/event_handler.rs @@ -22,7 +22,7 @@ use ita_stf::{Getter, TrustedCall, TrustedCallSigned}; use itc_parentchain_indirect_calls_executor::error::Error; use itp_stf_primitives::{traits::IndirectExecutor, types::TrustedOperation}; use itp_types::parentchain::{AccountId, FilterEvents, HandleParentchainEvents, ParentchainError}; -use itp_utils::hex::hex_encode; +use litentry_hex_utils::hex_encode; use log::*; pub struct ParentchainEventHandler {} diff --git a/tee-worker/app-libs/parentchain-interface/src/target_a/event_handler.rs b/tee-worker/app-libs/parentchain-interface/src/target_a/event_handler.rs index 7ea752aa55..cb02df3e2a 100644 --- a/tee-worker/app-libs/parentchain-interface/src/target_a/event_handler.rs +++ b/tee-worker/app-libs/parentchain-interface/src/target_a/event_handler.rs @@ -22,7 +22,7 @@ use ita_stf::{Getter, TrustedCall, TrustedCallSigned}; use itc_parentchain_indirect_calls_executor::error::Error; use itp_stf_primitives::{traits::IndirectExecutor, types::TrustedOperation}; use itp_types::parentchain::{AccountId, FilterEvents, HandleParentchainEvents, ParentchainError}; -use itp_utils::hex::hex_encode; +use litentry_hex_utils::hex_encode; use log::*; pub struct ParentchainEventHandler {} diff --git a/tee-worker/app-libs/stf/Cargo.toml b/tee-worker/app-libs/stf/Cargo.toml index 9c6dd0aad8..71ea86671e 100644 --- a/tee-worker/app-libs/stf/Cargo.toml +++ b/tee-worker/app-libs/stf/Cargo.toml @@ -41,6 +41,7 @@ sp-std = { default-features = false, git = "https://github.com/paritytech/substr # litentry itp-node-api-metadata-provider = { path = "../../core-primitives/node-api/metadata-provider", default-features = false } lc-stf-task-sender = { path = "../../litentry/core/stf-task/sender", default-features = false } +litentry-macros = { path = "../../../primitives/core/macros", default-features = false } litentry-primitives = { path = "../../litentry/primitives", default-features = false } pallet-parentchain = { path = "../../../pallets/parentchain", default-features = false } diff --git a/tee-worker/app-libs/stf/src/getter.rs b/tee-worker/app-libs/stf/src/getter.rs index d2ccec900a..c80cc31fbf 100644 --- a/tee-worker/app-libs/stf/src/getter.rs +++ b/tee-worker/app-libs/stf/src/getter.rs @@ -19,7 +19,8 @@ use codec::{Decode, Encode}; use ita_sgx_runtime::{IdentityManagement, System}; use itp_stf_interface::ExecuteGetter; use itp_stf_primitives::{traits::GetterAuthorization, types::KeyPair}; -use itp_utils::{if_production_or, stringify::account_id_to_string}; +use itp_utils::stringify::account_id_to_string; +use litentry_macros::if_production_or; use litentry_primitives::{Identity, LitentryMultiSignature}; use log::*; use sp_std::vec; diff --git a/tee-worker/app-libs/stf/src/trusted_call_litentry.rs b/tee-worker/app-libs/stf/src/trusted_call_litentry.rs index fc286822a5..58b999bef8 100644 --- a/tee-worker/app-libs/stf/src/trusted_call_litentry.rs +++ b/tee-worker/app-libs/stf/src/trusted_call_litentry.rs @@ -36,11 +36,12 @@ use itp_node_api::metadata::NodeMetadataTrait; use itp_node_api_metadata::pallet_imp::IMPCallIndexes; use itp_node_api_metadata_provider::AccessNodeMetadata; use itp_types::parentchain::ParentchainCall; -use itp_utils::{if_production_or, stringify::account_id_to_string}; +use itp_utils::stringify::account_id_to_string; use lc_stf_task_sender::{ stf_task_sender::{SendStfRequest, StfRequestSender}, AssertionBuildRequest, RequestType, Web2IdentityVerificationRequest, }; +use litentry_macros::if_production_or; use litentry_primitives::{ Assertion, ErrorDetail, Identity, RequestAesKey, ValidationData, Web3Network, }; diff --git a/tee-worker/cli/Cargo.toml b/tee-worker/cli/Cargo.toml index ec426c6ac2..f3ad923d46 100644 --- a/tee-worker/cli/Cargo.toml +++ b/tee-worker/cli/Cargo.toml @@ -53,6 +53,7 @@ lc-credentials = { path = "../litentry/core/credentials" } # litentry frame-metadata = "15.0.0" ita-sgx-runtime = { path = "../app-libs/sgx-runtime" } +litentry-hex-utils = { path = "../../primitives/hex", default-features = false } litentry-primitives = { path = "../litentry/primitives" } my-node-runtime = { package = "rococo-parachain-runtime", path = "../../runtime/rococo" } pallet-teerex = { path = "../../pallets/teerex", default-features = false } diff --git a/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc.rs b/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc.rs index aaf4ac527e..17158a882a 100644 --- a/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc.rs +++ b/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc.rs @@ -23,7 +23,7 @@ use crate::{ }; use ita_stf::{trusted_call_result::RequestVCResult, Index, TrustedCall, TrustedCallSigning}; use itp_stf_primitives::types::KeyPair; -use itp_utils::hex::decode_hex; +use litentry_hex_utils::decode_hex; use litentry_primitives::{ aes_decrypt, AchainableAmount, AchainableAmountHolding, AchainableAmountToken, AchainableAmounts, AchainableBasic, AchainableBetweenPercents, AchainableClassOfYear, diff --git a/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc_direct.rs b/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc_direct.rs index 7ac1c81da5..15e5153610 100644 --- a/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc_direct.rs +++ b/tee-worker/cli/src/trusted_base_cli/commands/litentry/request_vc_direct.rs @@ -24,7 +24,7 @@ use crate::{ }; use ita_stf::{trusted_call_result::RequestVCResult, Index, TrustedCall, TrustedCallSigning}; use itp_stf_primitives::types::KeyPair; -use itp_utils::hex::decode_hex; +use litentry_hex_utils::decode_hex; use litentry_primitives::{ aes_decrypt, AchainableAmount, AchainableAmountHolding, AchainableAmountToken, AchainableAmounts, AchainableBasic, AchainableBetweenPercents, AchainableClassOfYear, diff --git a/tee-worker/core-primitives/utils/Cargo.toml b/tee-worker/core-primitives/utils/Cargo.toml index 7c293aa011..1e8bd059ae 100644 --- a/tee-worker/core-primitives/utils/Cargo.toml +++ b/tee-worker/core-primitives/utils/Cargo.toml @@ -10,6 +10,7 @@ edition = "2021" [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } hex = { version = "0.4.3", default-features = false, features = ["alloc"] } +litentry-hex-utils = { path = "../../../primitives/hex", default-features = false } [features] default = ["std"] diff --git a/tee-worker/core-primitives/utils/src/hex.rs b/tee-worker/core-primitives/utils/src/hex.rs index 4c167af6f3..3b6ff8a8a8 100644 --- a/tee-worker/core-primitives/utils/src/hex.rs +++ b/tee-worker/core-primitives/utils/src/hex.rs @@ -20,8 +20,9 @@ // Todo: merge with hex_display use crate::error::{Error, Result}; -use alloc::{string::String, vec::Vec}; +use alloc::string::String; use codec::{Decode, Encode}; +use litentry_hex_utils::{decode_hex, hex_encode}; /// Trait to encode a given value to a hex string, prefixed with "0x". pub trait ToHexPrefixed { @@ -45,57 +46,16 @@ impl FromHexPrefixed for T { type Output = T; fn from_hex(msg: &str) -> Result { - let byte_array = decode_hex(msg)?; + let byte_array = decode_hex(msg).map_err(Error::Hex)?; Decode::decode(&mut byte_array.as_slice()).map_err(Error::Codec) } } -/// Hex encodes given data and preappends a "0x". -pub fn hex_encode(data: &[u8]) -> String { - let mut hex_str = hex::encode(data); - hex_str.insert_str(0, "0x"); - hex_str -} - -/// Helper method for decoding hex. -pub fn decode_hex>(message: T) -> Result> { - let message = message.as_ref(); - let message = match message { - [b'0', b'x', hex_value @ ..] => hex_value, - _ => message, - }; - - let decoded_message = hex::decode(message).map_err(Error::Hex)?; - Ok(decoded_message) -} - #[cfg(test)] mod tests { use super::*; use alloc::string::ToString; - #[test] - fn hex_encode_decode_works() { - let data = "Hello World!".to_string(); - - let hex_encoded_data = hex_encode(&data.encode()); - let decoded_data = - String::decode(&mut decode_hex(hex_encoded_data).unwrap().as_slice()).unwrap(); - - assert_eq!(data, decoded_data); - } - - #[test] - fn hex_encode_decode_works_empty_input() { - let data = String::new(); - - let hex_encoded_data = hex_encode(&data.encode()); - let decoded_data = - String::decode(&mut decode_hex(hex_encoded_data).unwrap().as_slice()).unwrap(); - - assert_eq!(data, decoded_data); - } - #[test] fn hex_encode_decode_works_empty_input_for_decode() { let data = String::new(); diff --git a/tee-worker/core-primitives/utils/src/lib.rs b/tee-worker/core-primitives/utils/src/lib.rs index d03767e6c6..297ff5090e 100644 --- a/tee-worker/core-primitives/utils/src/lib.rs +++ b/tee-worker/core-primitives/utils/src/lib.rs @@ -25,7 +25,6 @@ pub mod buffer; pub mod error; pub mod hex; pub mod hex_display; -pub mod macros; pub mod stringify; // Public re-exports. diff --git a/tee-worker/core-primitives/utils/src/macros.rs b/tee-worker/core-primitives/utils/src/macros.rs deleted file mode 100644 index 9d5234ce50..0000000000 --- a/tee-worker/core-primitives/utils/src/macros.rs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2020-2024 Trust Computing GmbH. -// This file is part of Litentry. -// -// Litentry is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Litentry is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Litentry. If not, see . - -#[macro_export] -macro_rules! if_production_or { - ($prod_variant:expr, $non_prod_variant:expr) => { - if cfg!(feature = "production") { - $prod_variant - } else { - $non_prod_variant - } - }; -} - -#[macro_export] -macro_rules! if_not_production { - ($expression:expr) => { - if cfg!(not(feature = "production")) { - $expression - } - }; -} diff --git a/tee-worker/enclave-runtime/Cargo.lock b/tee-worker/enclave-runtime/Cargo.lock index 1417f0067e..c825310a29 100644 --- a/tee-worker/enclave-runtime/Cargo.lock +++ b/tee-worker/enclave-runtime/Cargo.lock @@ -608,8 +608,9 @@ name = "core-primitives" version = "0.9.12" dependencies = [ "frame-support", - "itp-utils", - "litentry-macros 0.9.12", + "litentry-hex-utils", + "litentry-macros", + "litentry-proc-macros", "pallet-evm 6.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", "parity-scale-codec", "ring 0.16.20", @@ -911,8 +912,10 @@ dependencies = [ "lc-scheduled-enclave", "lc-stf-task-receiver", "lc-vc-task-receiver", - "litentry-macros 0.1.0", + "litentry-hex-utils", + "litentry-macros", "litentry-primitives", + "litentry-proc-macros", "log", "multibase", "once_cell 1.4.0", @@ -1869,6 +1872,7 @@ dependencies = [ "itp-types", "itp-utils", "lc-scheduled-enclave", + "litentry-hex-utils", "litentry-primitives", "log", "parity-scale-codec", @@ -1922,6 +1926,7 @@ dependencies = [ "itp-types", "itp-utils", "lc-stf-task-sender", + "litentry-macros", "litentry-primitives", "log", "pallet-balances", @@ -2621,6 +2626,7 @@ name = "itp-utils" version = "0.9.0" dependencies = [ "hex 0.4.3", + "litentry-hex-utils", "parity-scale-codec", ] @@ -2692,6 +2698,7 @@ dependencies = [ "its-state", "its-validateer-fetch", "lc-scheduled-enclave", + "litentry-hex-utils", "log", "parity-scale-codec", "sgx_tstd", @@ -2931,6 +2938,7 @@ dependencies = [ "lc-credentials-v2", "lc-service", "lc-stf-task-sender", + "litentry-hex-utils", "litentry-primitives", "log", "parity-scale-codec", @@ -3000,6 +3008,7 @@ dependencies = [ "itc-rest-client", "itp-rpc", "itp-utils", + "litentry-macros", "litentry-primitives", "log", "parity-scale-codec", @@ -3140,6 +3149,7 @@ dependencies = [ "lc-stf-task-receiver", "lc-stf-task-sender", "lc-vc-task-sender", + "litentry-macros", "litentry-primitives", "log", "pallet-identity-management-tee", @@ -3225,21 +3235,15 @@ dependencies = [ ] [[package]] -name = "litentry-macros" -version = "0.1.0" +name = "litentry-hex-utils" +version = "0.9.12" dependencies = [ - "cargo_toml", - "quote 1.0.33", + "hex 0.4.3", ] [[package]] name = "litentry-macros" version = "0.9.12" -dependencies = [ - "proc-macro2", - "quote 1.0.33", - "syn 2.0.37", -] [[package]] name = "litentry-primitives" @@ -3250,6 +3254,7 @@ dependencies = [ "hex 0.4.3", "itp-sgx-crypto", "itp-utils", + "litentry-hex-utils", "log", "pallet-evm 6.0.0-dev (git+https://github.com/integritee-network/frontier.git?branch=bar/polkadot-v0.9.42)", "parity-scale-codec", @@ -3268,6 +3273,16 @@ dependencies = [ "teerex-primitives 0.1.0", ] +[[package]] +name = "litentry-proc-macros" +version = "0.9.12" +dependencies = [ + "cargo_toml", + "proc-macro2", + "quote 1.0.33", + "syn 2.0.37", +] + [[package]] name = "log" version = "0.4.17" diff --git a/tee-worker/enclave-runtime/Cargo.toml b/tee-worker/enclave-runtime/Cargo.toml index b828f44e72..cad156a51b 100644 --- a/tee-worker/enclave-runtime/Cargo.toml +++ b/tee-worker/enclave-runtime/Cargo.toml @@ -137,8 +137,10 @@ lc-data-providers = { path = "../litentry/core/data-providers", default-features lc-scheduled-enclave = { path = "../litentry/core/scheduled-enclave", default-features = false, features = ["sgx"] } lc-stf-task-receiver = { path = "../litentry/core/stf-task/receiver", default-features = false, features = ["sgx"] } lc-vc-task-receiver = { path = "../litentry/core/vc-issuance/lc-vc-task-receiver", default-features = false, features = ["sgx"] } -litentry-macros = { path = "../litentry/macros" } +litentry-hex-utils = { path = "../../primitives/hex", default-features = false } +litentry-macros = { path = "../../primitives/core/macros", default-features = false } litentry-primitives = { path = "../litentry/primitives", default-features = false, features = ["sgx"] } +litentry-proc-macros = { path = "../../primitives/core/proc-macros", default-features = false } # substrate deps frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } diff --git a/tee-worker/enclave-runtime/src/lib.rs b/tee-worker/enclave-runtime/src/lib.rs index 9c3b078558..208dcb50f5 100644 --- a/tee-worker/enclave-runtime/src/lib.rs +++ b/tee-worker/enclave-runtime/src/lib.rs @@ -75,7 +75,8 @@ use itp_settings::worker_mode::{ProvideWorkerMode, WorkerMode, WorkerModeProvide use itp_sgx_crypto::key_repository::AccessPubkey; use itp_storage::{StorageProof, StorageProofChecker}; use itp_types::{ShardIdentifier, SignedBlock}; -use itp_utils::{if_production_or, write_slice_and_whitespace_pad}; +use itp_utils::write_slice_and_whitespace_pad; +use litentry_macros::if_production_or; use log::*; use once_cell::sync::OnceCell; use sgx_types::sgx_status_t; @@ -133,7 +134,7 @@ pub unsafe extern "C" fn init( // Initialize the logging environment in the enclave. if_production_or!( { - let module_names = litentry_macros::local_modules!(); + let module_names = litentry_proc_macros::local_modules!(); println!( "Initializing logger to filter only following local modules: {:?}", module_names diff --git a/tee-worker/enclave-runtime/src/rpc/worker_api_direct.rs b/tee-worker/enclave-runtime/src/rpc/worker_api_direct.rs index 4133bccca6..802107c544 100644 --- a/tee-worker/enclave-runtime/src/rpc/worker_api_direct.rs +++ b/tee-worker/enclave-runtime/src/rpc/worker_api_direct.rs @@ -45,13 +45,14 @@ use itp_top_pool_author::traits::AuthorApi; use itp_types::{ DirectRequestStatus, Index, MrEnclave, RsaRequest, ShardIdentifier, SidechainBlockNumber, H256, }; -use itp_utils::{if_not_production, FromHexPrefixed, ToHexPrefixed}; +use itp_utils::{FromHexPrefixed, ToHexPrefixed}; use its_primitives::types::block::SignedBlock; use its_sidechain::rpc_handler::{ direct_top_pool_api, direct_top_pool_api::decode_shard_from_base58, import_block_api, }; use jsonrpc_core::{serde_json::json, IoHandler, Params, Value}; use lc_scheduled_enclave::{ScheduledEnclaveUpdater, GLOBAL_SCHEDULED_ENCLAVE}; +use litentry_macros::if_not_production; use litentry_primitives::DecryptableRequest; use log::debug; use sgx_crypto_helper::rsa3072::Rsa3072PubKey; @@ -489,7 +490,7 @@ fn forward_dcap_quote_inner(params: Params) -> Result { let param = &hex_encoded_params.get(0).ok_or("Could not get first param")?; let encoded_quote_to_forward: Vec = - itp_utils::hex::decode_hex(param).map_err(|e| format!("{:?}", e))?; + litentry_hex_utils::decode_hex(param).map_err(|e| format!("{:?}", e))?; let url = String::new(); let ext = generate_dcap_ra_extrinsic_from_quote_internal(url, &encoded_quote_to_forward) @@ -519,7 +520,7 @@ fn attesteer_forward_ias_attestation_report_inner( let param = &hex_encoded_params.get(0).ok_or("Could not get first param")?; let ias_attestation_report = - itp_utils::hex::decode_hex(param).map_err(|e| format!("{:?}", e))?; + litentry_hex_utils::decode_hex(param).map_err(|e| format!("{:?}", e))?; let url = String::new(); let ext = generate_ias_ra_extrinsic_from_der_cert_internal(url, &ias_attestation_report) diff --git a/tee-worker/enclave-runtime/src/top_pool_execution.rs b/tee-worker/enclave-runtime/src/top_pool_execution.rs index a8168864e3..060bb54d60 100644 --- a/tee-worker/enclave-runtime/src/top_pool_execution.rs +++ b/tee-worker/enclave-runtime/src/top_pool_execution.rs @@ -55,7 +55,6 @@ use itp_sgx_externalities::SgxExternalities; use itp_stf_state_handler::{handle_state::HandleState, query_shard_state::QueryShardState}; use itp_time_utils::duration_now; use itp_types::{parentchain::ParentchainCall, Block, OpaqueCall, H256}; -use itp_utils::if_not_production; use its_primitives::{ traits::{ Block as SidechainBlockTrait, Header as HeaderTrait, ShardIdentifierFor, SignedBlock, @@ -69,6 +68,7 @@ use its_sidechain::{ validateer_fetch::ValidateerFetch, }; use lc_scheduled_enclave::{ScheduledEnclaveUpdater, GLOBAL_SCHEDULED_ENCLAVE}; +use litentry_macros::if_not_production; use log::*; use sgx_types::sgx_status_t; use sp_core::{crypto::UncheckedFrom, Pair}; diff --git a/tee-worker/litentry/core/assertion-build-v2/Cargo.toml b/tee-worker/litentry/core/assertion-build-v2/Cargo.toml index 7a9380d09b..3451c85a06 100644 --- a/tee-worker/litentry/core/assertion-build-v2/Cargo.toml +++ b/tee-worker/litentry/core/assertion-build-v2/Cargo.toml @@ -31,6 +31,7 @@ lc-common = { path = "../common", default-features = false } lc-credentials-v2 = { path = "../credentials-v2", default-features = false } lc-service = { path = "../service", default-features = false } lc-stf-task-sender = { path = "../stf-task/sender", default-features = false } +litentry-hex-utils = { path = "../../../../primitives/hex", default-features = false } litentry-primitives = { path = "../../primitives", default-features = false } [dev-dependencies] diff --git a/tee-worker/litentry/core/assertion-build-v2/src/token_holding_amount/mod.rs b/tee-worker/litentry/core/assertion-build-v2/src/token_holding_amount/mod.rs index 2cae98d3ac..4973ce5ecf 100644 --- a/tee-worker/litentry/core/assertion-build-v2/src/token_holding_amount/mod.rs +++ b/tee-worker/litentry/core/assertion-build-v2/src/token_holding_amount/mod.rs @@ -76,10 +76,10 @@ mod tests { use super::*; use itp_stf_primitives::types::ShardIdentifier; use itp_types::AccountId; - use itp_utils::hex::decode_hex; use lc_common::web3_token::{TokenAddress, TokenName}; use lc_credentials_v2::assertion_logic::{AssertionLogic, Op}; use lc_mock_server::run; + use litentry_hex_utils::decode_hex; use litentry_primitives::{Identity, IdentityNetworkTuple}; fn crate_assertion_build_request( diff --git a/tee-worker/litentry/core/assertion-build/Cargo.toml b/tee-worker/litentry/core/assertion-build/Cargo.toml index f23f8835bf..63b5a54ac6 100644 --- a/tee-worker/litentry/core/assertion-build/Cargo.toml +++ b/tee-worker/litentry/core/assertion-build/Cargo.toml @@ -52,6 +52,7 @@ pallet-parachain-staking = { path = "../../../../pallets/parachain-staking", def [dev-dependencies] env_logger = "0.10.0" lc-mock-server = { path = "../mock-server" } +litentry-hex-utils = { path = "../../../../primitives/hex" } [features] default = ["std"] diff --git a/tee-worker/litentry/core/assertion-build/src/nodereal/amount_holding/evm_amount_holding.rs b/tee-worker/litentry/core/assertion-build/src/nodereal/amount_holding/evm_amount_holding.rs index 7fb7a8de2b..70715e0770 100644 --- a/tee-worker/litentry/core/assertion-build/src/nodereal/amount_holding/evm_amount_holding.rs +++ b/tee-worker/litentry/core/assertion-build/src/nodereal/amount_holding/evm_amount_holding.rs @@ -123,9 +123,9 @@ pub fn build( mod tests { use super::*; use itp_stf_primitives::types::ShardIdentifier; - use itp_utils::hex::decode_hex; use lc_credentials::assertion_logic::{AssertionLogic, Op}; use lc_mock_server::run; + use litentry_hex_utils::decode_hex; fn create_ton_token_assertion_logic() -> Box { Box::new(AssertionLogic::Item { src: "$token".into(), op: Op::Equal, dst: "TON".into() }) diff --git a/tee-worker/litentry/core/data-providers/Cargo.toml b/tee-worker/litentry/core/data-providers/Cargo.toml index 6eca7cb2b3..7e97240746 100644 --- a/tee-worker/litentry/core/data-providers/Cargo.toml +++ b/tee-worker/litentry/core/data-providers/Cargo.toml @@ -34,6 +34,7 @@ thiserror_sgx = { package = "thiserror", git = "https://github.com/mesalock-linu url_sgx = { package = "url", git = "https://github.com/mesalock-linux/rust-url-sgx", tag = "sgx_1.1.3", optional = true } # litentry +litentry-macros = { path = "../../../../primitives/core/macros", default-features = false } litentry-primitives = { path = "../../primitives", default-features = false } [dev-dependencies] diff --git a/tee-worker/litentry/core/data-providers/src/lib.rs b/tee-worker/litentry/core/data-providers/src/lib.rs index ce252983e0..afcfb02676 100644 --- a/tee-worker/litentry/core/data-providers/src/lib.rs +++ b/tee-worker/litentry/core/data-providers/src/lib.rs @@ -44,7 +44,7 @@ use itc_rest_client::{ http_client::{DefaultSend, HttpClient}, rest_client::RestClient, }; -use itp_utils::if_not_production; +use litentry_macros::if_not_production; use log::debug; use serde::{Deserialize, Serialize}; use std::vec; diff --git a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/Cargo.toml b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/Cargo.toml index 609185f601..e7640939fc 100644 --- a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/Cargo.toml +++ b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/Cargo.toml @@ -44,6 +44,7 @@ lc-data-providers = { path = "../../data-providers", default-features = false } lc-stf-task-receiver = { path = "../../stf-task/receiver", default-features = false } lc-stf-task-sender = { path = "../../stf-task/sender", default-features = false } lc-vc-task-sender = { path = "../lc-vc-task-sender", default-features = false } +litentry-macros = { path = "../../../../../primitives/core/macros", default-features = false } litentry-primitives = { path = "../../../primitives", default-features = false } pallet-identity-management-tee = { path = "../../../pallets/identity-management", default-features = false } diff --git a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/lib.rs b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/lib.rs index 28ce33d4bf..64ca0225b2 100644 --- a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/lib.rs +++ b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/lib.rs @@ -41,10 +41,10 @@ use itp_top_pool_author::traits::AuthorApi; use itp_types::{ parentchain::ParentchainId, AccountId, BlockNumber as SidechainBlockNumber, ShardIdentifier, }; -use itp_utils::if_production_or; use lc_stf_task_receiver::StfTaskContext; use lc_stf_task_sender::AssertionBuildRequest; use lc_vc_task_sender::init_vc_task_sender_storage; +use litentry_macros::if_production_or; use litentry_primitives::{ AesRequest, Assertion, DecryptableRequest, Identity, ParentchainBlockNumber, }; diff --git a/tee-worker/litentry/macros/Cargo.toml b/tee-worker/litentry/macros/Cargo.toml deleted file mode 100644 index c3039927e1..0000000000 --- a/tee-worker/litentry/macros/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -authors = ["Trust Computing GmbH "] -name = "litentry-macros" -version = "0.1.0" -edition = "2021" - -[dependencies] -cargo_toml = "0.16.3" -quote = "1.0.33" - -[lib] -proc-macro = true diff --git a/tee-worker/litentry/primitives/Cargo.toml b/tee-worker/litentry/primitives/Cargo.toml index 2ad014ae67..b57c4e5b20 100644 --- a/tee-worker/litentry/primitives/Cargo.toml +++ b/tee-worker/litentry/primitives/Cargo.toml @@ -30,6 +30,7 @@ sgx_tstd = { git = "https://github.com/apache/teaclave-sgx-sdk.git", branch = "m # internal dependencies itp-sgx-crypto = { path = "../../core-primitives/sgx/crypto", default-features = false } itp-utils = { path = "../../core-primitives/utils", default-features = false } +litentry-hex-utils = { path = "../../../primitives/hex", default-features = false } parentchain-primitives = { package = "core-primitives", path = "../../../primitives/core", default-features = false } teerex-primitives = { path = "../../../primitives/teerex", default-features = false } diff --git a/tee-worker/litentry/primitives/src/lib.rs b/tee-worker/litentry/primitives/src/lib.rs index 4bf0e4c6c1..4978ee7369 100644 --- a/tee-worker/litentry/primitives/src/lib.rs +++ b/tee-worker/litentry/primitives/src/lib.rs @@ -41,7 +41,7 @@ pub use validation_data::*; use bitcoin::sign_message::{signed_msg_hash, MessageSignature}; use codec::{Decode, Encode, MaxEncodedLen}; use itp_sgx_crypto::ShieldingCryptoDecrypt; -use itp_utils::hex::hex_encode; +use litentry_hex_utils::hex_encode; use log::error; pub use parentchain_primitives::{ all_bitcoin_web3networks, all_evm_web3networks, all_substrate_web3networks, all_web3networks, diff --git a/tee-worker/service/Cargo.toml b/tee-worker/service/Cargo.toml index c751b43e50..207628ffd3 100644 --- a/tee-worker/service/Cargo.toml +++ b/tee-worker/service/Cargo.toml @@ -74,6 +74,8 @@ ita-stf = { path = "../app-libs/stf", default-features = false } lc-data-providers = { path = "../litentry/core/data-providers" } lc-mock-server = { path = "../litentry/core/mock-server" } lc-stf-task-sender = { path = "../litentry/core/stf-task/sender", default-features = false } +litentry-hex-utils = { path = "../../primitives/hex", default-features = false } +litentry-macros = { path = "../../primitives/core/macros", default-features = false } litentry-primitives = { path = "../litentry/primitives" } my-node-runtime = { package = "rococo-parachain-runtime", path = "../../runtime/rococo" } sgx-verify = { path = "../../pallets/teerex/sgx-verify", default-features = false } diff --git a/tee-worker/service/src/main_impl.rs b/tee-worker/service/src/main_impl.rs index a211d214f5..bba89601df 100644 --- a/tee-worker/service/src/main_impl.rs +++ b/tee-worker/service/src/main_impl.rs @@ -44,13 +44,13 @@ use itp_node_api::{ node_api_factory::{CreateNodeApi, NodeApiFactory}, }; use itp_settings::worker_mode::{ProvideWorkerMode, WorkerMode, WorkerModeProvider}; -use itp_utils::if_production_or; use its_peer_fetch::{ block_fetch_client::BlockFetcher, untrusted_peer_fetch::UntrustedPeerFetcher, }; use its_primitives::types::block::SignedBlock as SignedSidechainBlock; use its_storage::{interface::FetchBlocks, BlockPruner, SidechainStorageLock}; use lc_data_providers::DataProviderConfig; +use litentry_macros::if_production_or; use log::*; use my_node_runtime::{Hash, Header, RuntimeEvent}; use regex::Regex; diff --git a/tee-worker/service/src/teeracle/mod.rs b/tee-worker/service/src/teeracle/mod.rs index 420a175b26..415f7c461d 100644 --- a/tee-worker/service/src/teeracle/mod.rs +++ b/tee-worker/service/src/teeracle/mod.rs @@ -20,7 +20,7 @@ use codec::{Decode, Encode}; use itp_enclave_api::teeracle_api::TeeracleApi; use itp_node_api::api_client::ParentchainApi; use itp_types::parentchain::Hash; -use itp_utils::hex::hex_encode; +use litentry_hex_utils::hex_encode; use log::*; use sp_runtime::OpaqueExtrinsic; use std::time::Duration; diff --git a/tee-worker/sidechain/consensus/aura/Cargo.toml b/tee-worker/sidechain/consensus/aura/Cargo.toml index a7a52de35e..edb3ed9916 100644 --- a/tee-worker/sidechain/consensus/aura/Cargo.toml +++ b/tee-worker/sidechain/consensus/aura/Cargo.toml @@ -42,6 +42,7 @@ its-validateer-fetch = { path = "../../validateer-fetch", default-features = fal # litentry itp-utils = { path = "../../../core-primitives/utils", default-features = false } lc-scheduled-enclave = { path = "../../../litentry/core/scheduled-enclave", default-features = false } +litentry-hex-utils = { path = "../../../../primitives/hex", default-features = false } [dev-dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } diff --git a/tee-worker/sidechain/consensus/aura/src/lib.rs b/tee-worker/sidechain/consensus/aura/src/lib.rs index 193eb0501c..dbbe099e4e 100644 --- a/tee-worker/sidechain/consensus/aura/src/lib.rs +++ b/tee-worker/sidechain/consensus/aura/src/lib.rs @@ -37,7 +37,6 @@ use itp_sgx_externalities::SgxExternalities; use itp_stf_state_handler::handle_state::HandleState; use itp_time_utils::duration_now; -use itp_utils::hex::hex_encode; use its_block_verification::slot::slot_author; use its_consensus_common::{Environment, Error as ConsensusError, Proposer}; use its_consensus_slots::{SimpleSlotWorker, Slot, SlotInfo}; @@ -47,6 +46,7 @@ use its_primitives::{ }; use its_validateer_fetch::ValidateerFetch; use lc_scheduled_enclave::ScheduledEnclaveUpdater; +use litentry_hex_utils::hex_encode; use sp_core::ByteArray; use sp_runtime::{ app_crypto::{sp_core::H256, Pair}, From f120d8714f94bcc703732e011bc09234ea66b4a8 Mon Sep 17 00:00:00 2001 From: Ariel Birnbaum Date: Fri, 26 Jan 2024 19:45:57 +0100 Subject: [PATCH 33/51] P-54 Binary search for holding time assertion (#2401) Co-authored-by: Kailai Wang Co-authored-by: Zhouhui Tian --- primitives/core/src/assertion.rs | 23 +- .../core/assertion-build/src/holding_time.rs | 347 ++++++++++++------ .../litentry/core/assertion-build/src/lib.rs | 2 +- tee-worker/litentry/primitives/src/lib.rs | 2 +- 4 files changed, 235 insertions(+), 139 deletions(-) diff --git a/primitives/core/src/assertion.rs b/primitives/core/src/assertion.rs index 777757f62e..99de17a200 100644 --- a/primitives/core/src/assertion.rs +++ b/primitives/core/src/assertion.rs @@ -25,7 +25,7 @@ use crate::{ use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_runtime::{traits::ConstU32, BoundedVec}; -use sp_std::{str, vec, vec::Vec}; +use sp_std::{vec, vec::Vec}; pub type ParameterString = BoundedVec>; @@ -313,27 +313,6 @@ impl Assertion { } } -pub const ASSERTION_DATE_LEN: usize = 15; -pub const ASSERTION_FROM_DATE: [&str; ASSERTION_DATE_LEN] = [ - "2017-01-01", - "2017-07-01", - "2018-01-01", - "2018-07-01", - "2019-01-01", - "2019-07-01", - "2020-01-01", - "2020-07-01", - "2021-01-01", - "2021-07-01", - "2022-01-01", - "2022-07-01", - "2023-01-01", - "2023-07-01", - // In order to address the issue of the community encountering a false query for WBTC in - // November, the product team feels that adding this date temporarily solves this problem. - "2023-12-01", -]; - #[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, MaxEncodedLen, TypeInfo)] pub enum AmountHoldingTimeType { #[codec(index = 0)] diff --git a/tee-worker/litentry/core/assertion-build/src/holding_time.rs b/tee-worker/litentry/core/assertion-build/src/holding_time.rs index 446aa6b4c7..a6744d6eb0 100644 --- a/tee-worker/litentry/core/assertion-build/src/holding_time.rs +++ b/tee-worker/litentry/core/assertion-build/src/holding_time.rs @@ -81,113 +81,194 @@ pub fn build( ) -> Result { debug!("Assertion A4 build, who: {:?}", account_id_to_string(&req.who)); - let q_min_balance = pre_build(&htype, &min_balance)?; - let identities = transpose_identity(&req.identities); - let (is_hold, optimal_hold_index) = - do_build(identities, &htype, &q_min_balance, data_provider_config) - .map_err(|e| emit_error(&htype, &min_balance, e))?; + let q_min_balance = prepare_min_balance(&htype, &min_balance)?; + let accounts = prepare_accounts(&req.identities, &htype); - generate_vc(req, &htype, &q_min_balance, is_hold, optimal_hold_index) + // Redundant check in principle, but better safe than sorry :) + if accounts.is_empty() { + return Err(emit_error(&htype, &min_balance, ErrorDetail::NoEligibleIdentity)) + } + + let holding_date = search_holding_date(data_provider_config, accounts, &q_min_balance) + .map_err(|e| emit_error(&htype, &min_balance, e))?; + + generate_vc(req, &htype, &q_min_balance, holding_date) .map_err(|e| emit_error(&htype, &min_balance, e)) } -/// Credential Build Workflow -fn pre_build(htype: &AmountHoldingTimeType, min_balance: &ParameterString) -> Result { +fn prepare_min_balance( + htype: &AmountHoldingTimeType, + min_balance: &ParameterString, +) -> Result { vec_to_string(min_balance.to_vec()) .map_err(|_| emit_error(htype, min_balance, ErrorDetail::ParseError)) } -// TODO: -// There's an issue for this: https://github.com/litentry/litentry-parachain/issues/1655 -// -// There is a problem here, because TDF does not support mixed network types, -// It is need to request TDF 2 (substrate+evm networks) * ASSERTION_DATE_LEN * addresses http requests. -// If TDF can handle mixed network type, and even supports from_date array, -// so that ideally, up to one http request can yield results. -fn do_build( - identities: Vec<(Web3Network, Vec)>, +#[derive(Clone)] +// Represents an individual account that may or not be holding the desired token amount +struct Account { + network: Web3Network, + token: Option, + address: String, +} + +// TODO: unit test? +fn prepare_accounts( + identities: &Vec, htype: &AmountHoldingTimeType, - q_min_balance: &str, - data_provider_config: &DataProviderConfig, -) -> core::result::Result<(bool, usize), ErrorDetail> { - let mut client = AchainableClient::new(data_provider_config); +) -> Vec { + transpose_identity(identities) + .into_iter() + .flat_map(|(network, addresses)| -> Vec { + let token = match_token_address(htype, &network); + addresses + .into_iter() + .map(move |address| Account { network, token: token.clone(), address }) + .collect() + }) + .collect() +} - let mut is_hold = false; - let mut optimal_hold_index = usize::MAX; - - // If both Substrate and Evm networks meet the conditions, take the interval with the longest holding time. - // Here's an example: - // - // ALICE holds 100 LITs since 2018-03-02 on substrate network - // ALICE holds 100 LITs since 2020-03-02 on evm network - // - // min_amount is 1 LIT - // - // the result should be - // Alice: - // [ - // from_date: < 2019-01-01 - // to_date: >= 2023-03-30 (now) - // value: true - // ] - for (network, addresses) in identities { - // If found query result is the optimal solution, i.e optimal_hold_index = 0, (2017-01-01) - // there is no need to query other networks. - if optimal_hold_index == 0 { - break - } +// Represents the outcome of a holding query for a given date +// Ok(true) => positive: user/account is holding (uninterrupted) since the given date +// Ok(false) => negative: user/account did not hold (at some point) since the given date +// Err(...) => inconclusive: query failed +type QueryOutcome = core::result::Result; - let token = match_token_address(htype, &network); - let addresses: Vec = addresses.into_iter().collect(); - - for (index, date) in ASSERTION_FROM_DATE.iter().enumerate() { - for address in &addresses { - let holding = ParamsBasicTypeWithAmountHolding::new( - &network, - q_min_balance.to_string(), - date.to_string(), - token.clone(), - ); - let is_amount_holder = client.is_holder(address, holding).map_err(|e| { - error!("Assertion HoldingTime request error: {:?}", e); - e.into_error_detail() - })?; - - if is_amount_holder { - if index < optimal_hold_index { - optimal_hold_index = index; - } - - is_hold = true; - - break - } - } - } - } +fn is_positive(outcome: &QueryOutcome) -> bool { + matches!(outcome, Ok(true)) +} + +fn is_negative(outcome: &QueryOutcome) -> bool { + matches!(outcome, Ok(false)) +} - // If is_hold is false, then the optimal_hold_index is always 0 (2017-01-01) - if !is_hold { - optimal_hold_index = 0; +fn is_inconclusive(outcome: &QueryOutcome) -> bool { + matches!(outcome, Err(_)) +} + +// Check against the data provider whether a single account has been holding since the given date. +fn account_is_holding( + client: &mut AchainableClient, + q_min_balance: &String, + account: &Account, + date: &str, +) -> QueryOutcome { + let holding = ParamsBasicTypeWithAmountHolding::new( + &account.network, + q_min_balance.to_string(), + date.to_string(), + account.token.clone(), + ); + return client.is_holder(account.address.as_str(), holding).map_err(|e| { + error!("Assertion HoldingTime request error: {:?}", e); + e.into_error_detail() + }) +} + +// Check against the data provider whether any of the given accounts has been holding since the given date. +// If at least one positive outcome is found, the accounts that yielded a (conclusive) negative outcome are eliminated. +fn holding_time_search_step( + client: &mut AchainableClient, + q_min_balance: &String, + accounts: Vec, + date: &str, +) -> (QueryOutcome, Vec) { + // Check all remaining identities on the given date + let outcomes: Vec = accounts + .iter() + .map(|account| account_is_holding(client, q_min_balance, account, date)) + .collect(); + + // If any positive result is found: + // - Discard all identities that yielded a _negative_ result + // - but KEEP the ones that yielded error; they may still be relevant! + // - Return the remaining identities with a positive value to continue the search + if outcomes.iter().any(is_positive) { + let new_accounts = accounts + .into_iter() + .zip(outcomes.iter()) + .filter_map(|(account, outcome)| (!is_negative(outcome)).then_some(account)) + .collect(); + return (Ok(true), new_accounts) } - Ok((is_hold, optimal_hold_index)) + /* + * If any error is found: + * - The search is stuck; bubble the error + * TODO: retry? + * + * Otherwise (all results were negative): + * - Keep all identities + * - Return with a negative result and continue the search + */ + let outcome = match outcomes.into_iter().find(is_inconclusive) { + Some(Err(e)) => Err(e), + _ => Ok(false), + }; + + (outcome, accounts) +} + +const ASSERTION_DATE_LEN: usize = 15; +const ASSERTION_FROM_DATE: [&str; ASSERTION_DATE_LEN] = [ + "2017-01-01", + "2017-07-01", + "2018-01-01", + "2018-07-01", + "2019-01-01", + "2019-07-01", + "2020-01-01", + "2020-07-01", + "2021-01-01", + "2021-07-01", + "2022-01-01", + "2022-07-01", + "2023-01-01", + "2023-07-01", + // In order to address the issue of the community encountering a false query for WBTC in + // November, the product team feels that adding this date temporarily solves this problem. + "2023-12-01", +]; + +// Search against the data provider for the holding time of the user's longest holding account. +// Return the date if successful, `None` if none of the accounts is currently holding. +fn search_holding_date( + data_provider_config: &DataProviderConfig, + mut accounts: Vec, + q_min_balance: &String, +) -> core::result::Result, ErrorDetail> { + let mut client = AchainableClient::new(data_provider_config); + + let mut pred = |date: &&str| { + let (outcome, new_accounts) = + holding_time_search_step(&mut client, q_min_balance, accounts.clone(), date); + accounts = new_accounts; + outcome.map(|is_holding| !is_holding) // negated to match the partition_point API + }; + + partition_point(ASSERTION_FROM_DATE.as_ref(), &mut pred).map(|index| { + if index < ASSERTION_DATE_LEN { + Some(ASSERTION_FROM_DATE[index]) + } else { + None + } + }) } fn generate_vc( req: &AssertionBuildRequest, htype: &AmountHoldingTimeType, q_min_balance: &str, - is_hold: bool, - optimal_hold_index: usize, + holding_date: Option<&str>, ) -> core::result::Result { match Credential::new(&req.who, &req.shard) { Ok(mut credential_unsigned) => { credential_unsigned.update_amount_holding_time_credential( htype, - is_hold, + holding_date.is_some(), q_min_balance, - ASSERTION_FROM_DATE[optimal_hold_index], + holding_date.unwrap_or("2017-01-01"), ); Ok(credential_unsigned) @@ -231,6 +312,30 @@ fn match_token_address(htype: &AmountHoldingTimeType, network: &Web3Network) -> } } +fn partition_point(vector: &[T], pred: &mut P) -> core::result::Result +where + P: FnMut(&T) -> core::result::Result, +{ + let mut trapped_error: Option = None; + let wrapped_pred = |element: &T| -> bool { + if trapped_error.is_some() { + return true + } + match pred(element) { + Ok(result) => result, + Err(error) => { + trapped_error = Some(error); + true + }, + } + }; + let index = vector.partition_point(wrapped_pred); + match trapped_error { + Some(error) => Err(error), + None => Ok(index), + } +} + #[cfg(test)] mod tests { use super::*; @@ -249,69 +354,81 @@ mod tests { fn do_build_lit_works() { let data_provider_config = init(); - let identities = vec![( - Web3Network::Litentry, - vec!["0x1A64eD145A3CFAB3AA3D08721D520B4FD6Cf2C11".to_string()], - )]; - let htype = AmountHoldingTimeType::LIT; + let network = Web3Network::Litentry; + let accounts: Vec = vec![Account { + address: "0x1A64eD145A3CFAB3AA3D08721D520B4FD6Cf2C11".to_string(), + network, + token: match_token_address(&htype, &network), + }]; + let q_min_balance = "10".to_string(); - let (is_hold, _optimal_hold_index) = - do_build(identities, &htype, &q_min_balance, &data_provider_config).unwrap(); - assert!(is_hold); + let holding_date = + search_holding_date(&data_provider_config, accounts, &q_min_balance).unwrap(); + assert!(holding_date.is_some()); } #[test] fn do_build_dot_works() { let data_provider_config = init(); - let identities = vec![( - Web3Network::Polkadot, - vec!["0x1A64eD145A3CFAB3AA3D08721D520B4FD6Cf2C13".to_string()], - )]; - let dot_type = AmountHoldingTimeType::DOT; + let htype = AmountHoldingTimeType::DOT; + let network = Web3Network::Polkadot; + let accounts = vec![Account { + address: "0x1A64eD145A3CFAB3AA3D08721D520B4FD6Cf2C13".to_string(), + network, + token: match_token_address(&htype, &network), + }]; let q_min_balance = "10".to_string(); - let (is_hold, _optimal_hold_index) = - do_build(identities, &dot_type, &q_min_balance, &data_provider_config).unwrap(); - assert!(is_hold); + let holding_date = + search_holding_date(&data_provider_config, accounts, &q_min_balance).unwrap(); + assert!(holding_date.is_some()); } #[test] fn do_build_wbtc_works() { let data_provider_config = init(); - let identities = vec![( - Web3Network::Ethereum, - vec![ - "0x1A64eD145A3CFAB3AA3D08721D520B4FD6Cf2C11".to_string(), - "0x1A64eD145A3CFAB3AA3D08721D520B4FD6Cf2C12".to_string(), - ], - )]; let htype = AmountHoldingTimeType::WBTC; + let network = Web3Network::Ethereum; + let accounts = vec![ + "0x1A64eD145A3CFAB3AA3D08721D520B4FD6Cf2C11", + "0x1A64eD145A3CFAB3AA3D08721D520B4FD6Cf2C12", + ] + .iter() + .map(|address| Account { + address: address.to_string(), + network, + token: match_token_address(&htype, &network), + }) + .collect(); + let q_min_balance = "10".to_string(); - let (is_hold, _optimal_hold_index) = - do_build(identities, &htype, &q_min_balance, &data_provider_config).unwrap(); - assert!(is_hold); + let holding_date = + search_holding_date(&data_provider_config, accounts, &q_min_balance).unwrap(); + assert!(holding_date.is_some()); } #[test] fn do_build_non_hold_works() { let data_provider_config = init(); - let identities = vec![( - Web3Network::Ethereum, - vec!["0x1A64eD145A3CFAB3AA3D08721D520B4FD6Cf2C14".to_string()], - )]; let htype = AmountHoldingTimeType::LIT; + let network = Web3Network::Ethereum; + let accounts = vec![Account { + address: "0x1A64eD145A3CFAB3AA3D08721D520B4FD6Cf2C14".to_string(), + network, + token: match_token_address(&htype, &network), + }]; + let q_min_balance = "10".to_string(); - let (is_hold, optimal_hold_index) = - do_build(identities, &htype, &q_min_balance, &data_provider_config).unwrap(); - assert!(!is_hold); - assert_eq!(optimal_hold_index, 0); + let holding_date = + search_holding_date(&data_provider_config, accounts, &q_min_balance).unwrap(); + assert!(holding_date.is_none()); } #[test] diff --git a/tee-worker/litentry/core/assertion-build/src/lib.rs b/tee-worker/litentry/core/assertion-build/src/lib.rs index 0a68f76135..467142feab 100644 --- a/tee-worker/litentry/core/assertion-build/src/lib.rs +++ b/tee-worker/litentry/core/assertion-build/src/lib.rs @@ -66,7 +66,7 @@ use litentry_primitives::{ AchainableBetweenPercents, AchainableDate, AchainableDateInterval, AchainableDatePercent, AchainableParams, AchainableToken, Assertion, ErrorDetail, ErrorString, Identity, IdentityNetworkTuple, IntoErrorDetail, OneBlockCourseType, ParameterString, VCMPError as Error, - Web3Network, ASSERTION_FROM_DATE, + Web3Network, }; use log::*; use rust_base58::ToBase58; diff --git a/tee-worker/litentry/primitives/src/lib.rs b/tee-worker/litentry/primitives/src/lib.rs index 4978ee7369..ef643d7c19 100644 --- a/tee-worker/litentry/primitives/src/lib.rs +++ b/tee-worker/litentry/primitives/src/lib.rs @@ -54,7 +54,7 @@ pub use parentchain_primitives::{ GenericDiscordRoleType, Hash as ParentchainHash, Header as ParentchainHeader, IMPError, Index as ParentchainIndex, IntoErrorDetail, OneBlockCourseType, ParameterString, SchemaContentString, SchemaIdString, Signature as ParentchainSignature, SoraQuizType, - VCMPError, VIP3MembershipCardLevel, Web3Network, Web3TokenType, ASSERTION_FROM_DATE, MINUTES, + VCMPError, VIP3MembershipCardLevel, Web3Network, Web3TokenType, MINUTES, }; use scale_info::TypeInfo; use sp_core::{ecdsa, ed25519, sr25519, ByteArray}; From 17ce73838ffe8632bd319bc796afb9439d139909 Mon Sep 17 00:00:00 2001 From: WMQ <46511820+wangminqi@users.noreply.github.com> Date: Mon, 29 Jan 2024 10:14:12 +0800 Subject: [PATCH 34/51] feat: pallet_account_fix (#2427) * feat: pallet_account_fix * debug: fmt * debug: add try-runtime feature * debug: change weight formula * debug: bump spec version * debug: for weight * debug: weird * debug: fix u64 --- Cargo.lock | 16 ++++++ Cargo.toml | 1 + pallets/account-fix/Cargo.toml | 44 ++++++++++++++++ pallets/account-fix/src/lib.rs | 92 ++++++++++++++++++++++++++++++++++ runtime/litentry/Cargo.toml | 4 ++ runtime/litentry/src/lib.rs | 7 ++- 6 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 pallets/account-fix/Cargo.toml create mode 100644 pallets/account-fix/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 1bfc20ed17..ed94aa9b0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5476,6 +5476,7 @@ dependencies = [ "orml-tokens", "orml-traits", "orml-xtokens", + "pallet-account-fix", "pallet-asset-manager", "pallet-aura", "pallet-authorship", @@ -6754,6 +6755,21 @@ dependencies = [ "sha2 0.10.7", ] +[[package]] +name = "pallet-account-fix" +version = "0.1.0" +dependencies = [ + "frame-support", + "frame-system", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-asset-manager" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 1ea3e746d3..ba77c155ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ 'node', + 'pallets/account-fix', 'pallets/bridge', 'pallets/bridge-transfer', 'pallets/drop3', diff --git a/pallets/account-fix/Cargo.toml b/pallets/account-fix/Cargo.toml new file mode 100644 index 0000000000..c8ff7f130c --- /dev/null +++ b/pallets/account-fix/Cargo.toml @@ -0,0 +1,44 @@ +[package] +authors = ['Trust Computing GmbH '] +description = 'Pallet for temporary fix of onchain accountInfo' +edition = '2021' +homepage = 'https://litentry.com/' +license = 'GPL-3.0' +name = 'pallet-account-fix' +repository = 'https://github.com/litentry/litentry-parachain' +version = '0.1.0' + +[dependencies] +# third-party dependencies +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } +scale-info = { version = "2.10.0", default-features = false, features = ["derive"] } + +# primitives +sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } + +# frame dependencies +frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } + +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } + +[features] +default = ["std"] +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", +] +std = [ + "codec/std", + "sp-std/std", + "sp-runtime/std", + "sp-io/std", + "sp-core/std", + "frame-support/std", + "frame-system/std", + "pallet-balances/std", +] +try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/account-fix/src/lib.rs b/pallets/account-fix/src/lib.rs new file mode 100644 index 0000000000..3a1c795ba8 --- /dev/null +++ b/pallets/account-fix/src/lib.rs @@ -0,0 +1,92 @@ +// Copyright 2020-2024 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +//! A pallet for temporary fix of onchain accountInfo. +//! No storage for this pallet and it should be removed right after fixing. +#![cfg_attr(not(feature = "std"), no_std)] + +use frame_support::{ + pallet_prelude::*, + traits::{Currency, ReservableCurrency, StorageVersion}, +}; +use frame_system::pallet_prelude::*; +pub use pallet::*; +use sp_runtime::traits::StaticLookup; + +use sp_std::vec::Vec; +type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; +pub type BalanceOf = + <::Currency as Currency<::AccountId>>::Balance; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + /// The current storage version. + const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); + + #[pallet::pallet] + #[pallet::storage_version(STORAGE_VERSION)] + #[pallet::without_storage_info] + pub struct Pallet(_); + + #[pallet::config] + pub trait Config: frame_system::Config + pallet_balances::Config { + /// The currency mechanism. + type Currency: ReservableCurrency; + } + + #[pallet::call] + impl Pallet { + /// Change the admin account + /// similar to sudo.set_key, the old account will be supplied in event + #[pallet::call_index(0)] + #[pallet::weight(Weight::from_parts(20_000_000u64 * (who.len() as u64), 0))] + pub fn upgrade_accounts( + origin: OriginFor, + who: Vec, + ) -> DispatchResultWithPostInfo { + ensure_root(origin)?; + for i in &who { + frame_system::Pallet::::inc_consumers(i)?; + } + // Do not pay a fee + Ok(Pays::No.into()) + } + + /// add some balance of an existing account + #[pallet::call_index(1)] + #[pallet::weight({10_000})] + pub fn set_balance( + origin: OriginFor, + who: AccountIdLookupOf, + #[pallet::compact] add_free: BalanceOf, + #[pallet::compact] add_reserved: BalanceOf, + ) -> DispatchResultWithPostInfo { + ensure_root(origin)?; + let who = T::Lookup::lookup(who)?; + + let add_total = add_free + add_reserved; + + // First we try to modify the account's balance to the forced balance. + T::Currency::deposit_into_existing(&who, add_total)?; + // Then do the reservation + T::Currency::reserve(&who, add_reserved)?; + + Ok(().into()) + } + } +} diff --git a/runtime/litentry/Cargo.toml b/runtime/litentry/Cargo.toml index 12a0ab1bda..c424c781f3 100644 --- a/runtime/litentry/Cargo.toml +++ b/runtime/litentry/Cargo.toml @@ -85,6 +85,7 @@ frame-benchmarking = { git = "https://github.com/paritytech/substrate", default- frame-system-benchmarking = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.42", optional = true } # Litentry pallets +pallet-account-fix = { path = "../../pallets/account-fix", default-features = false } pallet-asset-manager = { path = "../../pallets/xcm-asset-manager", default-features = false } pallet-bridge = { path = "../../pallets/bridge", default-features = false } pallet-bridge-transfer = { path = "../../pallets/bridge-transfer", default-features = false } @@ -137,6 +138,7 @@ runtime-benchmarks = [ "pallet-drop3/runtime-benchmarks", "pallet-extrinsic-filter/runtime-benchmarks", "cumulus-pallet-xcmp-queue/runtime-benchmarks", + "pallet-account-fix/runtime-benchmarks", "pallet-asset-manager/runtime-benchmarks", ] std = [ @@ -162,6 +164,7 @@ std = [ "orml-tokens/std", "orml-traits/std", "orml-xtokens/std", + "pallet-account-fix/std", "pallet-asset-manager/std", "pallet-aura/std", "pallet-authorship/std", @@ -219,6 +222,7 @@ try-runtime = [ "frame-try-runtime", "orml-tokens/try-runtime", "orml-xtokens/try-runtime", + "pallet-account-fix/try-runtime", "pallet-asset-manager/try-runtime", "pallet-aura/try-runtime", "pallet-authorship/try-runtime", diff --git a/runtime/litentry/src/lib.rs b/runtime/litentry/src/lib.rs index 6c557175f8..e82fdf1278 100644 --- a/runtime/litentry/src/lib.rs +++ b/runtime/litentry/src/lib.rs @@ -142,7 +142,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_name: create_runtime_str!("litentry-parachain"), authoring_version: 1, // same versioning-mechanism as polkadot: use last digit for minor updates - spec_version: 9172, + spec_version: 9173, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, @@ -629,6 +629,10 @@ impl pallet_sudo::Config for Runtime { type RuntimeEvent = RuntimeEvent; } +impl pallet_account_fix::Config for Runtime { + type Currency = Balances; +} + parameter_types! { pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); @@ -929,6 +933,7 @@ construct_runtime! { AssetManager: pallet_asset_manager = 64, // TMP + AccountFix: pallet_account_fix = 254, Sudo: pallet_sudo = 255, } } From e47d2a3f78268eb984047e945d86eea770edf4eb Mon Sep 17 00:00:00 2001 From: Igor Trofimov Date: Mon, 29 Jan 2024 17:18:32 +0200 Subject: [PATCH 35/51] Bump download-artifact version (#2435) * Bump download-artifact version * bump version for upload artifact * bump dorny/paths-filter * bump latest lts node version * bump actions/cache@v4 --- .github/workflows/build-docker-with-args.yml | 12 ++++---- .github/workflows/build-wasm.yml | 10 +++---- .github/workflows/ci.yml | 30 +++++++++---------- .github/workflows/coverage.yml | 3 +- .github/workflows/create-release-draft.yml | 10 +++---- .github/workflows/release-ts-api-package.yml | 2 +- .../workflows/simulate-runtime-upgrade.yml | 2 +- .nvmrc | 2 +- 8 files changed, 35 insertions(+), 36 deletions(-) diff --git a/.github/workflows/build-docker-with-args.yml b/.github/workflows/build-docker-with-args.yml index 6c7bde5ed4..ffe1ca1bca 100644 --- a/.github/workflows/build-docker-with-args.yml +++ b/.github/workflows/build-docker-with-args.yml @@ -5,24 +5,24 @@ on: inputs: ref: description: The commit SHA or tag for checking out code - default: '' + default: "" required: false cargo_profile: type: choice description: which profile to use in cargo options: - - release - - production + - release + - production docker_tag: description: The tag for the built docker image required: true args: description: Args to pass to `cargo build`, e.g. --features=runtime-benchmarks - default: '' + default: "" required: false env: - DOCKER_BUILDKIT: 1 + DOCKER_BUILDKIT: 1 jobs: ## build docker image of client binary with args ## @@ -56,7 +56,7 @@ jobs: docker cp $(docker create --rm litentry/litentry-parachain:${{ github.event.inputs.docker_tag }}):/usr/local/bin/litentry-collator . - name: Upload the client binary - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: litentry-collator path: | diff --git a/.github/workflows/build-wasm.yml b/.github/workflows/build-wasm.yml index ba584156b0..9276a9fc73 100644 --- a/.github/workflows/build-wasm.yml +++ b/.github/workflows/build-wasm.yml @@ -7,12 +7,12 @@ on: type: choice description: The chain whose runtime-wasm is built options: - - litmus - - litentry - - rococo + - litmus + - litentry + - rococo ref: description: The commit SHA or tag for checking out code - default: '' + default: "" required: false env: @@ -50,7 +50,7 @@ jobs: cp ${{ steps.srtool_build.outputs.wasm_compressed }} ${{ github.event.inputs.chain }}-parachain-runtime.compact.compressed.wasm - name: Upload wasm artefacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ github.event.inputs.chain }}-parachain-runtime path: | diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cfe52a969b..e4a60199bc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -94,7 +94,7 @@ jobs: # Checks to see if any files in the PR/commit match one of the listed file types. # We can use this filter to decide whether or not to build docker images - - uses: dorny/paths-filter@v2 + - uses: dorny/paths-filter@v3 id: filter with: filters: .github/file-filter.yml @@ -319,7 +319,7 @@ jobs: docker save litentry/litentry-parachain:latest | gzip > litentry-parachain-dev.tar.gz - name: Upload docker image - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: litentry-parachain-dev path: litentry-parachain-dev.tar.gz @@ -356,7 +356,7 @@ jobs: - name: Cache worker-cache if: needs.set-condition.outputs.rebuild_tee == 'true' - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | worker-cache @@ -463,7 +463,7 @@ jobs: run: docker save litentry/litentry-worker:latest litentry/litentry-cli:latest | gzip > litentry-tee.tar.gz - name: Upload docker images - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: litentry-tee path: litentry-tee.tar.gz @@ -487,7 +487,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: litentry-parachain-dev @@ -513,7 +513,7 @@ jobs: dest: docker-logs - name: Upload docker logs if test fails - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: failure() with: name: ${{ matrix.chain }}-ts-tests-docker-logs @@ -522,7 +522,7 @@ jobs: retention-days: 3 - name: Archive logs if test fails - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: failure() with: name: ${{ matrix.chain }}-ts-tests-artifact @@ -632,11 +632,11 @@ jobs: run: | docker pull parity/polkadot - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: litentry-parachain-dev - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: litentry-tee @@ -682,7 +682,7 @@ jobs: dest: docker-logs - name: Upload docker logs if test fails - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: failure() with: name: ${{ matrix.test_name }}-docker-logs @@ -715,11 +715,11 @@ jobs: run: | docker pull parity/polkadot - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: litentry-parachain-dev - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: litentry-tee @@ -765,7 +765,7 @@ jobs: dest: docker-logs - name: Upload docker logs if test fails - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: failure() with: name: ${{ matrix.test_name }}-docker-logs @@ -795,11 +795,11 @@ jobs: - tee-single-worker-test if: ${{ !failure() && needs.set-condition.outputs.push_docker == 'true' }} steps: - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: litentry-parachain-dev - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: litentry-tee diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 58f8deb0f1..ea82464c2f 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -21,7 +21,7 @@ jobs: # Checks to see if any files in the PR/commit match one of the listed file types. # We can use this filter to decide whether or not to build docker images - - uses: dorny/paths-filter@v2 + - uses: dorny/paths-filter@v3 id: filter with: filters: .github/file-filter.yml @@ -63,4 +63,3 @@ jobs: with: fail_ci_if_error: false verbose: true - diff --git a/.github/workflows/create-release-draft.yml b/.github/workflows/create-release-draft.yml index 261b8976ee..52d5890518 100644 --- a/.github/workflows/create-release-draft.yml +++ b/.github/workflows/create-release-draft.yml @@ -108,7 +108,7 @@ jobs: cp ${{ steps.srtool_build.outputs.wasm_compressed }} ${{ matrix.chain }}-parachain-runtime.compact.compressed.wasm - name: Upload wasm artefacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ matrix.chain }}-parachain-runtime path: | @@ -158,7 +158,7 @@ jobs: docker cp $(docker create --rm litentry/litentry-parachain:$PARACHAIN_DOCKER_TAG):/usr/local/bin/litentry-collator . - name: Upload the client binary - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: litentry-collator if-no-files-found: ignore @@ -277,7 +277,7 @@ jobs: echo "worker_sha1sum=$WORKER_SHA1SUM" >> $GITHUB_OUTPUT - name: Upload artefacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: litentry-tee path: ./tee-worker/enclave_release/* @@ -316,7 +316,7 @@ jobs: make test-ts-docker-${{ matrix.chain }} - name: Archive logs if test fails - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ failure() }} with: name: ${{ matrix.chain }}-ts-tests-artifacts @@ -417,7 +417,7 @@ jobs: fetch-depth: 0 - name: Download all artefacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 - name: Generate release notes run: | diff --git a/.github/workflows/release-ts-api-package.yml b/.github/workflows/release-ts-api-package.yml index b61bc5d52a..21f9881eba 100644 --- a/.github/workflows/release-ts-api-package.yml +++ b/.github/workflows/release-ts-api-package.yml @@ -100,7 +100,7 @@ jobs: - name: Upload logs if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: logs-lit-ts-api-package-build path: logs diff --git a/.github/workflows/simulate-runtime-upgrade.yml b/.github/workflows/simulate-runtime-upgrade.yml index af9e760d01..e5490d5b85 100644 --- a/.github/workflows/simulate-runtime-upgrade.yml +++ b/.github/workflows/simulate-runtime-upgrade.yml @@ -62,7 +62,7 @@ jobs: dest: docker-logs - name: Upload docker logs if test fails - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: failure() with: name: ${{ matrix.chain }}-docker-logs diff --git a/.nvmrc b/.nvmrc index a77793ecc5..9de2256827 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -lts/hydrogen +lts/iron From 7503916f01192363018ecf6caaecb5e4dcf9ae6a Mon Sep 17 00:00:00 2001 From: Kai <7630809+Kailai-Wang@users.noreply.github.com> Date: Mon, 29 Jan 2024 19:30:06 +0100 Subject: [PATCH 36/51] Add back `SubstrateTestnet` enum (#2437) * Revert "refactor: remove substratetestnet (#2399)" This reverts commit b2d5bc2da2a83bbb78b5f7c502e591ee51b73768. * add back it * remove comment --- primitives/core/src/network.rs | 9 +++++++-- .../prepare-build/interfaces/identity/definitions.ts | 2 +- tee-worker/litentry/core/assertion-build/src/lib.rs | 1 + tee-worker/litentry/core/common/src/lib.rs | 1 + .../litentry/core/data-providers/src/achainable.rs | 1 + 5 files changed, 11 insertions(+), 3 deletions(-) diff --git a/primitives/core/src/network.rs b/primitives/core/src/network.rs index 4ac7ee8df3..ddad0cef7e 100644 --- a/primitives/core/src/network.rs +++ b/primitives/core/src/network.rs @@ -70,7 +70,9 @@ pub enum Web3Network { LitentryRococo, #[codec(index = 5)] Khala, - // Index 6 used to SubstrateTestnet, So let's not break the indexing... + #[codec(index = 6)] + SubstrateTestnet, + // evm #[codec(index = 7)] Ethereum, @@ -107,7 +109,7 @@ impl Web3Network { Self::Polkadot | Self::Kusama | Self::Litentry | Self::Litmus | Self::LitentryRococo | - Self::Khala + Self::Khala | Self::SubstrateTestnet ) } @@ -165,6 +167,7 @@ mod tests { Web3Network::Litmus => false, Web3Network::LitentryRococo => false, Web3Network::Khala => false, + Web3Network::SubstrateTestnet => false, Web3Network::Ethereum => true, Web3Network::Bsc => true, Web3Network::BitcoinP2tr => false, @@ -189,6 +192,7 @@ mod tests { Web3Network::Litmus => true, Web3Network::LitentryRococo => true, Web3Network::Khala => true, + Web3Network::SubstrateTestnet => true, Web3Network::Ethereum => false, Web3Network::Bsc => false, Web3Network::BitcoinP2tr => false, @@ -213,6 +217,7 @@ mod tests { Web3Network::Litmus => false, Web3Network::LitentryRococo => false, Web3Network::Khala => false, + Web3Network::SubstrateTestnet => false, Web3Network::Ethereum => false, Web3Network::Bsc => false, Web3Network::BitcoinP2tr => true, diff --git a/tee-worker/client-api/parachain-api/prepare-build/interfaces/identity/definitions.ts b/tee-worker/client-api/parachain-api/prepare-build/interfaces/identity/definitions.ts index b6246bf3a4..60ea972254 100644 --- a/tee-worker/client-api/parachain-api/prepare-build/interfaces/identity/definitions.ts +++ b/tee-worker/client-api/parachain-api/prepare-build/interfaces/identity/definitions.ts @@ -41,7 +41,7 @@ export default { "Litmus", "LitentryRococo", "Khala", - "Null", + "SubstrateTestnet", "Ethereum", "Bsc", "BitcoinP2tr", diff --git a/tee-worker/litentry/core/assertion-build/src/lib.rs b/tee-worker/litentry/core/assertion-build/src/lib.rs index 467142feab..6f2f1a124b 100644 --- a/tee-worker/litentry/core/assertion-build/src/lib.rs +++ b/tee-worker/litentry/core/assertion-build/src/lib.rs @@ -177,6 +177,7 @@ fn pubkey_to_address(network: &Web3Network, pubkey: &str) -> String { | Web3Network::Litmus | Web3Network::LitentryRococo | Web3Network::Khala + | Web3Network::SubstrateTestnet | Web3Network::Ethereum | Web3Network::Bsc => "".to_string(), } diff --git a/tee-worker/litentry/core/common/src/lib.rs b/tee-worker/litentry/core/common/src/lib.rs index cfaa59582d..bb84990a23 100644 --- a/tee-worker/litentry/core/common/src/lib.rs +++ b/tee-worker/litentry/core/common/src/lib.rs @@ -34,6 +34,7 @@ pub fn web3_network_to_chain(network: &Web3Network) -> &'static str { Web3Network::Litmus => "litmus", Web3Network::LitentryRococo => "litentry_rococo", Web3Network::Khala => "khala", + Web3Network::SubstrateTestnet => "substrate_testnet", Web3Network::Ethereum => "ethereum", Web3Network::Bsc => "bsc", Web3Network::BitcoinP2tr => "bitcoin_p2tr", diff --git a/tee-worker/litentry/core/data-providers/src/achainable.rs b/tee-worker/litentry/core/data-providers/src/achainable.rs index ae44d49c74..16636ca8b5 100644 --- a/tee-worker/litentry/core/data-providers/src/achainable.rs +++ b/tee-worker/litentry/core/data-providers/src/achainable.rs @@ -176,6 +176,7 @@ pub fn web3_network_to_chain(network: &Web3Network) -> String { Web3Network::Litmus => "litmus".into(), Web3Network::LitentryRococo => "litentry_rococo".into(), Web3Network::Khala => "khala".into(), + Web3Network::SubstrateTestnet => "substrate_testnet".into(), Web3Network::Ethereum => "ethereum".into(), Web3Network::Bsc => "bsc".into(), Web3Network::BitcoinP2tr => "bitcoin_p2tr".into(), From 7e9b4306fcacb339d7b910a54fb93b0e9d4bef18 Mon Sep 17 00:00:00 2001 From: Kasper Ziemianek Date: Mon, 29 Jan 2024 19:56:10 +0100 Subject: [PATCH 37/51] include only one variant in compiled code (#2438) --- primitives/core/macros/src/lib.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/primitives/core/macros/src/lib.rs b/primitives/core/macros/src/lib.rs index 9494db3e0c..9f6d16ef6e 100644 --- a/primitives/core/macros/src/lib.rs +++ b/primitives/core/macros/src/lib.rs @@ -17,19 +17,23 @@ #[macro_export] macro_rules! if_production_or { - ($prod_variant:expr, $non_prod_variant:expr) => { - if cfg!(feature = "production") { - $prod_variant - } else { + ($prod_variant:expr, $non_prod_variant:expr) => {{ + #[cfg(not(feature = "production"))] + { $non_prod_variant } - }; + #[cfg(feature = "production")] + { + $prod_variant + } + }}; } #[macro_export] macro_rules! if_not_production { ($expression:expr) => { - if cfg!(not(feature = "production")) { + #[cfg(not(feature = "production"))] + { $expression } }; From 9e293d7a7bd6cea7fb8f828e6063ba78e115196a Mon Sep 17 00:00:00 2001 From: Faisal Ahmed <42486737+felixfaisal@users.noreply.github.com> Date: Tue, 30 Jan 2024 01:26:36 +0530 Subject: [PATCH 38/51] fix: bug in request-vc-direct (#2436) Co-authored-by: Kasper Ziemianek --- tee-worker/enclave-runtime/src/initialization/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tee-worker/enclave-runtime/src/initialization/mod.rs b/tee-worker/enclave-runtime/src/initialization/mod.rs index 1ce81d7f45..6f3078c501 100644 --- a/tee-worker/enclave-runtime/src/initialization/mod.rs +++ b/tee-worker/enclave-runtime/src/initialization/mod.rs @@ -224,11 +224,6 @@ pub(crate) fn init_enclave( run_stf_task_handler().unwrap(); }); - std::thread::spawn(move || { - #[allow(clippy::unwrap_used)] - run_vc_issuance().unwrap(); - }); - Ok(()) } @@ -372,6 +367,11 @@ pub(crate) fn init_enclave_sidechain_components( GLOBAL_SIDECHAIN_FAIL_SLOT_ON_DEMAND_COMPONENT.initialize(Arc::new(None)); } + std::thread::spawn(move || { + #[allow(clippy::unwrap_used)] + run_vc_issuance().unwrap(); + }); + Ok(()) } From 8ddda3c853c6ca732a684f09fc6773014fc733ce Mon Sep 17 00:00:00 2001 From: Kai <7630809+Kailai-Wang@users.noreply.github.com> Date: Mon, 29 Jan 2024 22:27:59 +0100 Subject: [PATCH 39/51] Fix `production` feature propagation (#2439) * Add production feature for worker and enclave compilation * some more fix * some more fix --- primitives/core/Cargo.toml | 3 ++ primitives/core/macros/Cargo.toml | 3 ++ tee-worker/app-libs/stf/Cargo.toml | 3 ++ tee-worker/app-libs/stf/src/helpers.rs | 34 +++++++++++-------- tee-worker/app-libs/stf/src/trusted_call.rs | 5 +-- tee-worker/enclave-runtime/Cargo.toml | 10 +++++- .../src/rpc/worker_api_direct.rs | 5 ++- .../enclave-runtime/src/top_pool_execution.rs | 4 ++- .../litentry/core/data-providers/Cargo.toml | 3 ++ .../lc-vc-task-receiver/Cargo.toml | 5 +++ .../lc-vc-task-receiver/src/lib.rs | 9 +++-- tee-worker/litentry/primitives/Cargo.toml | 4 ++- tee-worker/service/Cargo.toml | 8 ++++- tee-worker/service/src/main_impl.rs | 2 ++ 14 files changed, 70 insertions(+), 28 deletions(-) diff --git a/primitives/core/Cargo.toml b/primitives/core/Cargo.toml index da25b3b95d..4534063d76 100644 --- a/primitives/core/Cargo.toml +++ b/primitives/core/Cargo.toml @@ -38,3 +38,6 @@ std = [ "ring/std", "pallet-evm/std", ] +production = [ + "litentry-macros/production", +] diff --git a/primitives/core/macros/Cargo.toml b/primitives/core/macros/Cargo.toml index c4f4e3ff25..0b079b6ed3 100644 --- a/primitives/core/macros/Cargo.toml +++ b/primitives/core/macros/Cargo.toml @@ -4,3 +4,6 @@ description = 'Proc-macros used by Litentry crates.' name = "litentry-macros" version = "0.9.12" edition = "2021" + +[features] +production = [] diff --git a/tee-worker/app-libs/stf/Cargo.toml b/tee-worker/app-libs/stf/Cargo.toml index 71ea86671e..ca7cfe2b2e 100644 --- a/tee-worker/app-libs/stf/Cargo.toml +++ b/tee-worker/app-libs/stf/Cargo.toml @@ -91,3 +91,6 @@ std = [ "itp-node-api-metadata-provider/std", ] test = [] +production = [ + "litentry-macros/production", +] diff --git a/tee-worker/app-libs/stf/src/helpers.rs b/tee-worker/app-libs/stf/src/helpers.rs index 0c6fd39896..a0d3fa75b9 100644 --- a/tee-worker/app-libs/stf/src/helpers.rs +++ b/tee-worker/app-libs/stf/src/helpers.rs @@ -17,7 +17,6 @@ use crate::ENCLAVE_ACCOUNT_KEY; use codec::{Decode, Encode}; use frame_support::ensure; -use hex_literal::hex; use itp_stf_primitives::error::{StfError, StfResult}; use itp_storage::{storage_double_map_key, storage_map_key, storage_value_key, StorageHasher}; use itp_types::Index; @@ -25,11 +24,10 @@ use itp_utils::stringify::account_id_to_string; use litentry_primitives::{ErrorDetail, Identity, Web3ValidationData}; use log::*; use sp_core::blake2_256; -use sp_runtime::AccountId32; use std::prelude::v1::*; -pub const ALICE_ACCOUNTID32: AccountId32 = - AccountId32::new(hex!["d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"]); +#[cfg(not(feature = "production"))] +pub use non_prod::*; pub fn get_storage_value( storage_prefix: &'static str, @@ -128,16 +126,6 @@ pub fn ensure_enclave_signer_or_self( } } -#[cfg(not(feature = "production"))] -pub fn ensure_alice(signer: &AccountId32) -> bool { - signer == &ALICE_ACCOUNTID32 -} - -#[cfg(not(feature = "production"))] -pub fn ensure_enclave_signer_or_alice(signer: &AccountId32) -> bool { - signer == &enclave_signer_account::() || ensure_alice(signer) -} - // verification message format: // ``` // blake2_256( + + ) @@ -173,3 +161,21 @@ pub fn verify_web3_identity( Ok(()) } + +#[cfg(not(feature = "production"))] +mod non_prod { + use super::*; + use hex_literal::hex; + use sp_runtime::AccountId32; + + pub const ALICE_ACCOUNTID32: AccountId32 = + AccountId32::new(hex!["d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"]); + + pub fn ensure_alice(signer: &AccountId32) -> bool { + signer == &ALICE_ACCOUNTID32 + } + + pub fn ensure_enclave_signer_or_alice(signer: &AccountId32) -> bool { + signer == &enclave_signer_account::() || ensure_alice(signer) + } +} diff --git a/tee-worker/app-libs/stf/src/trusted_call.rs b/tee-worker/app-libs/stf/src/trusted_call.rs index 6a9f8308fe..045eaa5c6a 100644 --- a/tee-worker/app-libs/stf/src/trusted_call.rs +++ b/tee-worker/app-libs/stf/src/trusted_call.rs @@ -23,10 +23,11 @@ use std::vec::Vec; #[cfg(feature = "evm")] use crate::evm_helpers::{create_code_hash, evm_create2_address, evm_create_address}; +#[cfg(not(feature = "production"))] +use crate::helpers::ensure_enclave_signer_or_alice; use crate::{ helpers::{ - enclave_signer_account, ensure_enclave_signer_account, ensure_enclave_signer_or_alice, - ensure_self, get_storage_by_key_hash, + enclave_signer_account, ensure_enclave_signer_account, ensure_self, get_storage_by_key_hash, }, trusted_call_result::{ ActivateIdentityResult, DeactivateIdentityResult, RequestVCResult, diff --git a/tee-worker/enclave-runtime/Cargo.toml b/tee-worker/enclave-runtime/Cargo.toml index cad156a51b..6ac3c843b0 100644 --- a/tee-worker/enclave-runtime/Cargo.toml +++ b/tee-worker/enclave-runtime/Cargo.toml @@ -17,7 +17,15 @@ evm = [ "ita-sgx-runtime/evm", "ita-stf/evm", ] -production = ["itp-settings/production", "itp-attestation-handler/production"] +production = [ + "ita-stf/production", + "itp-settings/production", + "itp-attestation-handler/production", + "lc-data-providers/production", + "lc-vc-task-receiver/production", + "litentry-primitives/production", + "litentry-macros/production", +] sidechain = ["itp-settings/sidechain", "itp-top-pool-author/sidechain"] offchain-worker = [ "itp-settings/offchain-worker", diff --git a/tee-worker/enclave-runtime/src/rpc/worker_api_direct.rs b/tee-worker/enclave-runtime/src/rpc/worker_api_direct.rs index 802107c544..93b9370d57 100644 --- a/tee-worker/enclave-runtime/src/rpc/worker_api_direct.rs +++ b/tee-worker/enclave-runtime/src/rpc/worker_api_direct.rs @@ -42,9 +42,7 @@ use itp_stf_executor::{getter_executor::ExecuteGetter, traits::StfShardVaultQuer use itp_stf_primitives::types::AccountId; use itp_stf_state_handler::handle_state::HandleState; use itp_top_pool_author::traits::AuthorApi; -use itp_types::{ - DirectRequestStatus, Index, MrEnclave, RsaRequest, ShardIdentifier, SidechainBlockNumber, H256, -}; +use itp_types::{DirectRequestStatus, Index, RsaRequest, ShardIdentifier, H256}; use itp_utils::{FromHexPrefixed, ToHexPrefixed}; use its_primitives::types::block::SignedBlock; use its_sidechain::rpc_handler::{ @@ -340,6 +338,7 @@ where }); if_not_production!({ + use itp_types::{MrEnclave, SidechainBlockNumber}; // state_updateScheduledEnclave, params: sidechainBlockNumber, hex encoded mrenclave io.add_sync_method("state_updateScheduledEnclave", move |params: Params| { match params.parse::<(SidechainBlockNumber, String)>() { diff --git a/tee-worker/enclave-runtime/src/top_pool_execution.rs b/tee-worker/enclave-runtime/src/top_pool_execution.rs index 060bb54d60..e80611707b 100644 --- a/tee-worker/enclave-runtime/src/top_pool_execution.rs +++ b/tee-worker/enclave-runtime/src/top_pool_execution.rs @@ -15,11 +15,12 @@ */ +#[cfg(not(feature = "production"))] +use crate::initialization::global_components::GLOBAL_SIDECHAIN_FAIL_SLOT_ON_DEMAND_COMPONENT; use crate::{ error::Result, initialization::global_components::{ GLOBAL_OCALL_API_COMPONENT, GLOBAL_SIDECHAIN_BLOCK_COMPOSER_COMPONENT, - GLOBAL_SIDECHAIN_FAIL_SLOT_ON_DEMAND_COMPONENT, GLOBAL_SIDECHAIN_IMPORT_QUEUE_WORKER_COMPONENT, GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT, GLOBAL_STATE_HANDLER_COMPONENT, GLOBAL_TOP_POOL_AUTHOR_COMPONENT, }, @@ -172,6 +173,7 @@ fn execute_top_pool_trusted_calls_internal() -> Result<()> { let authority = GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT.get()?.retrieve_key()?; + #[cfg(not(feature = "production"))] let fail_on_demand = GLOBAL_SIDECHAIN_FAIL_SLOT_ON_DEMAND_COMPONENT.get()?; match yield_next_slot( diff --git a/tee-worker/litentry/core/data-providers/Cargo.toml b/tee-worker/litentry/core/data-providers/Cargo.toml index 7e97240746..3c5d84e9d4 100644 --- a/tee-worker/litentry/core/data-providers/Cargo.toml +++ b/tee-worker/litentry/core/data-providers/Cargo.toml @@ -69,3 +69,6 @@ std = [ "litentry-primitives/std", "chrono", ] +production = [ + "litentry-macros/production", +] diff --git a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/Cargo.toml b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/Cargo.toml index e7640939fc..29cf14194f 100644 --- a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/Cargo.toml +++ b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/Cargo.toml @@ -94,3 +94,8 @@ std = [ "itp-storage/std", "lc-vc-task-sender/std", ] +production = [ + "ita-stf/production", + "lc-data-providers/production", + "litentry-macros/production", +] diff --git a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/lib.rs b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/lib.rs index 64ca0225b2..0c48bda55f 100644 --- a/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/lib.rs +++ b/tee-worker/litentry/core/vc-issuance/lc-vc-task-receiver/src/lib.rs @@ -20,12 +20,11 @@ use crate::vc_handling::VCRequestHandler; use codec::{Decode, Encode}; use frame_support::{ensure, sp_runtime::traits::One}; use ita_sgx_runtime::{pallet_imt::get_eligible_identities, BlockNumber, Hash, Runtime}; +#[cfg(not(feature = "production"))] +use ita_stf::helpers::ensure_alice; use ita_stf::{ - aes_encrypt_default, - helpers::{ensure_alice, ensure_self}, - trusted_call_result::RequestVCResult, - Getter, OpaqueCall, TrustedCall, TrustedCallSigned, TrustedCallVerification, TrustedOperation, - H256, + aes_encrypt_default, helpers::ensure_self, trusted_call_result::RequestVCResult, Getter, + OpaqueCall, TrustedCall, TrustedCallSigned, TrustedCallVerification, TrustedOperation, H256, }; use itp_extrinsics_factory::CreateExtrinsics; use itp_node_api::metadata::{ diff --git a/tee-worker/litentry/primitives/Cargo.toml b/tee-worker/litentry/primitives/Cargo.toml index b57c4e5b20..a809fa5a35 100644 --- a/tee-worker/litentry/primitives/Cargo.toml +++ b/tee-worker/litentry/primitives/Cargo.toml @@ -39,7 +39,9 @@ base64 = { version = "0.13", features = ["alloc"] } [features] default = ["std"] -production = [] +production = [ + "parentchain-primitives/production", +] sgx = [ "sgx_tstd", "rand-sgx", diff --git a/tee-worker/service/Cargo.toml b/tee-worker/service/Cargo.toml index 207628ffd3..8363903079 100644 --- a/tee-worker/service/Cargo.toml +++ b/tee-worker/service/Cargo.toml @@ -86,7 +86,13 @@ default = [] evm = [] sidechain = ["itp-settings/sidechain"] offchain-worker = ["itp-settings/offchain-worker"] -production = ["itp-settings/production"] +production = [ + "ita-stf/production", + "itp-settings/production", + "lc-data-providers/production", + "litentry-macros/production", + "litentry-primitives/production", +] teeracle = ["itp-settings/teeracle"] dcap = [] attesteer = ["dcap"] diff --git a/tee-worker/service/src/main_impl.rs b/tee-worker/service/src/main_impl.rs index bba89601df..3e660cefc2 100644 --- a/tee-worker/service/src/main_impl.rs +++ b/tee-worker/service/src/main_impl.rs @@ -242,6 +242,8 @@ pub(crate) fn main() { setup::generate_shielding_key_file(enclave.as_ref()); } else if matches.is_present("signing-key") { setup::generate_signing_key_file(enclave.as_ref()); + let tee_accountid = enclave_account(enclave.as_ref()); + println!("Enclave signing account: {:}", &tee_accountid.to_ss58check()); } else if matches.is_present("dump-ra") { info!("*** Perform RA and dump cert to disk"); #[cfg(not(feature = "dcap"))] From 1a863e5c0b156d2b5ae5552e2ca37f5371349f16 Mon Sep 17 00:00:00 2001 From: Kasper Ziemianek Date: Tue, 30 Jan 2024 06:57:52 +0100 Subject: [PATCH 40/51] init threads only for sidechain (#2445) --- tee-worker/enclave-runtime/src/initialization/mod.rs | 12 +++++++----- tee-worker/litentry/core/data-providers/src/lib.rs | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/tee-worker/enclave-runtime/src/initialization/mod.rs b/tee-worker/enclave-runtime/src/initialization/mod.rs index 6f3078c501..5f50cdb42d 100644 --- a/tee-worker/enclave-runtime/src/initialization/mod.rs +++ b/tee-worker/enclave-runtime/src/initialization/mod.rs @@ -219,11 +219,6 @@ pub(crate) fn init_enclave( let data_provider_config = DataProviderConfig::new(); GLOBAL_DATA_PROVIDER_CONFIG.initialize(data_provider_config.into()); - std::thread::spawn(move || { - #[allow(clippy::unwrap_used)] - run_stf_task_handler().unwrap(); - }); - Ok(()) } @@ -368,6 +363,13 @@ pub(crate) fn init_enclave_sidechain_components( } std::thread::spawn(move || { + println!("running stf task handler"); + #[allow(clippy::unwrap_used)] + run_stf_task_handler().unwrap(); + }); + + std::thread::spawn(move || { + println!("running vc issuance"); #[allow(clippy::unwrap_used)] run_vc_issuance().unwrap(); }); diff --git a/tee-worker/litentry/core/data-providers/src/lib.rs b/tee-worker/litentry/core/data-providers/src/lib.rs index afcfb02676..26bc0b4cf7 100644 --- a/tee-worker/litentry/core/data-providers/src/lib.rs +++ b/tee-worker/litentry/core/data-providers/src/lib.rs @@ -199,7 +199,7 @@ impl Default for DataProviderConfig { impl DataProviderConfig { pub fn new() -> Self { - std::println!("Initializing data providers config"); + log::debug!("Initializing data providers config"); // default prod config let mut config = DataProviderConfig { From 3df47f9bc7cfdda1c882817fe74e76f12190fbb4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Jan 2024 08:45:25 +0100 Subject: [PATCH 41/51] Bump serde_json from 1.0.111 to 1.0.113 (#2444) Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.111 to 1.0.113. - [Release notes](https://github.com/serde-rs/json/releases) - [Commits](https://github.com/serde-rs/json/compare/v1.0.111...v1.0.113) --- updated-dependencies: - dependency-name: serde_json dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ed94aa9b0e..d644d951af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12345,9 +12345,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.111" +version = "1.0.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" dependencies = [ "itoa", "ryu", From 118b195b00df5b7bd272dea3850ff73d81ec96d8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Jan 2024 08:45:52 +0100 Subject: [PATCH 42/51] Bump serde from 1.0.195 to 1.0.196 (#2442) Bumps [serde](https://github.com/serde-rs/serde) from 1.0.195 to 1.0.196. - [Release notes](https://github.com/serde-rs/serde/releases) - [Commits](https://github.com/serde-rs/serde/compare/v1.0.195...v1.0.196) --- updated-dependencies: - dependency-name: serde dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d644d951af..de4b8b49da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12325,18 +12325,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.195" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.195" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" dependencies = [ "proc-macro2", "quote", From 28167f622576d31324ebe2f1b484a7c34e0de879 Mon Sep 17 00:00:00 2001 From: Kasper Ziemianek Date: Tue, 30 Jan 2024 10:12:29 +0100 Subject: [PATCH 43/51] bitacross init (#2419) * bitacross init * add ci fmt * fmt * comment out docker upload job * comment out docker image based jobs * more adjustments * remove parachain dependency on tee (#2433) * adjust crate name * P-54 Binary search for holding time assertion (#2401) Co-authored-by: Kailai Wang Co-authored-by: Zhouhui Tian * clippy fix --------- Co-authored-by: Zhouhui Tian <125243011+zhouhuitian@users.noreply.github.com> Co-authored-by: Ariel Birnbaum Co-authored-by: Kailai Wang Co-authored-by: Zhouhui Tian Co-authored-by: Kai <7630809+Kailai-Wang@users.noreply.github.com> --- .github/file-filter.yml | 14 + .github/workflows/ci.yml | 86 +- Makefile | 2 + bitacross-worker/.dockerignore | 16 + bitacross-worker/.editorconfig | 27 + bitacross-worker/.env.dev | 14 + bitacross-worker/.gitattributes.orig | 18 + bitacross-worker/.githooks/pre-commit | 17 + .../.github/workflows/build_and_test.yml | 599 + .../.github/workflows/check_labels.yml | 24 + .../.github/workflows/delete-release.yml | 70 + .../.github/workflows/label-checker.yml | 21 + .../workflows/publish-docker-release.yml | 69 + .../workflows/publish-docker-sidechain.yml | 43 + .../workflows/publish-docker-teeracle.yml | 43 + .../workflows/publish-draft-release.yml | 69 + bitacross-worker/.gitignore | 78 + bitacross-worker/.taplo.toml | 21 + bitacross-worker/Cargo.lock | 16436 ++++++++++++++++ bitacross-worker/Cargo.toml | 112 + bitacross-worker/DESIGN.md | 72 + bitacross-worker/Dockerfile | 23 + bitacross-worker/Jenkinsfile | 104 + bitacross-worker/LICENSE | 201 + bitacross-worker/Makefile | 287 + bitacross-worker/README.md | 1 + bitacross-worker/UpdateRustSGXSDK.mk | 33 + bitacross-worker/app-libs/oracle/Cargo.toml | 48 + .../src/certificates/amazon_root_ca_a.pem | 20 + .../baltimore_cyber_trust_root_v3.pem | 21 + .../certificates/lets_encrypt_root_cert.pem | 31 + .../src/certificates/open_meteo_root.pem | 31 + bitacross-worker/app-libs/oracle/src/error.rs | 39 + bitacross-worker/app-libs/oracle/src/lib.rs | 84 + .../app-libs/oracle/src/metrics_exporter.rs | 104 + bitacross-worker/app-libs/oracle/src/mock.rs | 120 + .../oracle/src/oracle_sources/coin_gecko.rs | 220 + .../src/oracle_sources/coin_market_cap.rs | 242 + .../app-libs/oracle/src/oracle_sources/mod.rs | 19 + .../oracle_sources/weather_oracle_source.rs | 120 + .../src/oracles/exchange_rate_oracle.rs | 154 + .../app-libs/oracle/src/oracles/mod.rs | 18 + .../oracle/src/oracles/weather_oracle.rs | 83 + bitacross-worker/app-libs/oracle/src/test.rs | 125 + .../app-libs/oracle/src/traits.rs | 55 + bitacross-worker/app-libs/oracle/src/types.rs | 61 + .../app-libs/parentchain-interface/Cargo.toml | 79 + .../src/indirect_calls/invoke.rs | 41 + .../indirect_calls/litentry/args_executor.rs | 62 + .../src/indirect_calls/litentry/mod.rs | 18 + .../litentry/scheduled_enclave.rs | 62 + .../src/indirect_calls/mod.rs | 26 + .../src/indirect_calls/shield_funds.rs | 62 + .../transfer_to_alice_shields_funds.rs | 98 + .../src/integritee/event_filter.rs | 85 + .../src/integritee/event_handler.rs | 83 + .../src/integritee/extrinsic_parser.rs | 83 + .../src/integritee/mod.rs | 173 + .../app-libs/parentchain-interface/src/lib.rs | 39 + .../src/target_a/event_filter.rs | 89 + .../src/target_a/event_handler.rs | 81 + .../src/target_a/extrinsic_parser.rs | 77 + .../parentchain-interface/src/target_a/mod.rs | 116 + .../src/target_b/event_filter.rs | 89 + .../src/target_b/event_handler.rs | 41 + .../src/target_b/extrinsic_parser.rs | 77 + .../parentchain-interface/src/target_b/mod.rs | 86 + .../app-libs/sgx-runtime/Cargo.toml | 70 + .../app-libs/sgx-runtime/src/evm.rs | 91 + .../app-libs/sgx-runtime/src/lib.rs | 335 + bitacross-worker/app-libs/stf/Cargo.toml | 89 + .../app-libs/stf/src/evm_helpers.rs | 66 + bitacross-worker/app-libs/stf/src/getter.rs | 269 + bitacross-worker/app-libs/stf/src/hash.rs | 29 + bitacross-worker/app-libs/stf/src/helpers.rs | 175 + bitacross-worker/app-libs/stf/src/lib.rs | 53 + bitacross-worker/app-libs/stf/src/stf_sgx.rs | 334 + .../app-libs/stf/src/stf_sgx_primitives.rs | 30 + .../app-libs/stf/src/stf_sgx_tests.rs | 82 + .../app-libs/stf/src/test_genesis.rs | 114 + .../app-libs/stf/src/trusted_call.rs | 686 + .../app-libs/stf/src/trusted_call_result.rs | 44 + bitacross-worker/bin/README.md | 1 + bitacross-worker/build.Dockerfile | 122 + bitacross-worker/cli/Cargo.toml | 69 + bitacross-worker/cli/README.md | 35 + bitacross-worker/cli/benchmark.sh | 61 + bitacross-worker/cli/demo_direct_call.sh | 144 + .../cli/demo_direct_call_2_workers.sh | 63 + .../cli/demo_shielding_unshielding.sh | 275 + .../demo_shielding_unshielding_multiworker.sh | 69 + ...shielding_unshielding_using_shard_vault.sh | 266 + ...shielding_using_shard_vault_on_target_a.sh | 302 + bitacross-worker/cli/demo_sidechain.sh | 178 + bitacross-worker/cli/demo_smart_contract.sh | 108 + bitacross-worker/cli/demo_teeracle_generic.sh | 136 + .../cli/demo_teeracle_whitelist.sh | 157 + bitacross-worker/cli/lit_parentchain_nonce.sh | 69 + .../cli/lit_set_heartbeat_timeout.sh | 85 + .../cli/src/attesteer/commands/mod.rs | 23 + .../src/attesteer/commands/send_dcap_quote.rs | 65 + .../commands/send_ias_attestation.rs | 66 + bitacross-worker/cli/src/attesteer/mod.rs | 41 + .../cli/src/base_cli/commands/balance.rs | 39 + .../cli/src/base_cli/commands/faucet.rs | 61 + .../cli/src/base_cli/commands/listen.rs | 149 + .../cli/src/base_cli/commands/litentry/mod.rs | 17 + .../litentry/set_heartbeat_timeout.rs | 54 + .../cli/src/base_cli/commands/mod.rs | 7 + .../base_cli/commands/register_tcb_info.rs | 146 + .../cli/src/base_cli/commands/shield_funds.rs | 92 + .../cli/src/base_cli/commands/transfer.rs | 61 + bitacross-worker/cli/src/base_cli/mod.rs | 192 + bitacross-worker/cli/src/benchmark/mod.rs | 378 + bitacross-worker/cli/src/command_utils.rs | 87 + bitacross-worker/cli/src/commands.rs | 60 + bitacross-worker/cli/src/error.rs | 36 + .../cli/src/evm/commands/evm_call.rs | 87 + .../cli/src/evm/commands/evm_command_utils.rs | 32 + .../cli/src/evm/commands/evm_create.rs | 89 + .../cli/src/evm/commands/evm_read.rs | 69 + bitacross-worker/cli/src/evm/commands/mod.rs | 6 + bitacross-worker/cli/src/evm/mod.rs | 49 + bitacross-worker/cli/src/lib.rs | 137 + bitacross-worker/cli/src/main.rs | 27 + .../src/oracle/commands/add_to_whitelist.rs | 67 + .../src/oracle/commands/listen_to_exchange.rs | 79 + .../src/oracle/commands/listen_to_oracle.rs | 91 + .../cli/src/oracle/commands/mod.rs | 25 + bitacross-worker/cli/src/oracle/mod.rs | 49 + .../src/trusted_base_cli/commands/balance.rs | 34 + .../trusted_base_cli/commands/get_shard.rs | 69 + .../commands/get_shard_vault.rs | 73 + .../cli/src/trusted_base_cli/commands/mod.rs | 7 + .../src/trusted_base_cli/commands/nonce.rs | 44 + .../trusted_base_cli/commands/set_balance.rs | 64 + .../src/trusted_base_cli/commands/transfer.rs | 72 + .../commands/unshield_funds.rs | 67 + .../cli/src/trusted_base_cli/mod.rs | 111 + bitacross-worker/cli/src/trusted_cli.rs | 68 + .../cli/src/trusted_command_utils.rs | 164 + bitacross-worker/cli/src/trusted_operation.rs | 420 + .../test_auto_shielding_with_transfer_bob.sh | 141 + ..._on_target_nodes_with_transfer_to_alice.sh | 159 + bitacross-worker/cli/tests/basic_tests.rs | 24 + .../AttestationReportSigningCACert.pem | 31 + .../attestation-handler/Cargo.toml | 102 + .../src/attestation_handler.rs | 853 + .../attestation-handler/src/cert.rs | 497 + .../attestation-handler/src/collateral.rs | 158 + .../attestation-handler/src/error.rs | 64 + .../attestation-handler/src/lib.rs | 58 + .../component-container/Cargo.toml | 26 + .../src/atomic_container.rs | 100 + .../src/component_container.rs | 100 + .../component-container/src/error.rs | 32 + .../component-container/src/lib.rs | 36 + .../core-primitives/enclave-api/Cargo.toml | 36 + .../core-primitives/enclave-api/build.rs | 24 + .../enclave-api/ffi/Cargo.toml | 14 + .../core-primitives/enclave-api/ffi/build.rs | 44 + .../enclave-api/ffi/src/lib.rs | 279 + .../enclave-api/src/direct_request.rs | 58 + .../enclave-api/src/enclave_base.rs | 409 + .../enclave-api/src/enclave_test.rs | 48 + .../core-primitives/enclave-api/src/error.rs | 14 + .../core-primitives/enclave-api/src/lib.rs | 49 + .../enclave-api/src/remote_attestation.rs | 857 + .../enclave-api/src/sidechain.rs | 122 + .../enclave-api/src/teeracle_api.rs | 114 + .../core-primitives/enclave-api/src/utils.rs | 27 + .../enclave-bridge-storage/Cargo.toml | 20 + .../enclave-bridge-storage/src/lib.rs | 31 + .../enclave-metrics/Cargo.toml | 25 + .../enclave-metrics/src/lib.rs | 68 + .../extrinsics-factory/Cargo.toml | 46 + .../extrinsics-factory/src/error.rs | 49 + .../extrinsics-factory/src/lib.rs | 241 + .../extrinsics-factory/src/mock.rs | 46 + .../core-primitives/hashing/Cargo.toml | 13 + .../core-primitives/hashing/src/lib.rs | 46 + .../core-primitives/hashing/src/std_hash.rs | 31 + .../core-primitives/import-queue/Cargo.toml | 32 + .../core-primitives/import-queue/src/error.rs | 41 + .../import-queue/src/import_queue.rs | 273 + .../core-primitives/import-queue/src/lib.rs | 89 + .../networking-utils/Cargo.toml | 20 + .../networking-utils/src/lib.rs | 26 + .../networking-utils/src/ports.rs | 48 + .../core-primitives/node-api/Cargo.toml | 28 + .../node-api/api-client-extensions/Cargo.toml | 27 + .../api-client-extensions/src/account.rs | 54 + .../api-client-extensions/src/chain.rs | 142 + .../node-api/api-client-extensions/src/lib.rs | 32 + .../src/pallet_teeracle.rs | 19 + .../src/pallet_teerex.rs | 105 + .../src/pallet_teerex_api_mock.rs | 70 + .../node-api/api-client-types/Cargo.toml | 28 + .../node-api/api-client-types/src/lib.rs | 98 + .../node-api/factory/Cargo.toml | 14 + .../node-api/factory/src/lib.rs | 73 + .../node-api/metadata-provider/Cargo.toml | 34 + .../node-api/metadata-provider/src/error.rs | 45 + .../node-api/metadata-provider/src/lib.rs | 114 + .../node-api/metadata/Cargo.toml | 29 + .../node-api/metadata/src/error.rs | 37 + .../node-api/metadata/src/lib.rs | 173 + .../node-api/metadata/src/metadata_mocks.rs | 317 + .../node-api/metadata/src/pallet_balances.rs | 43 + .../node-api/metadata/src/pallet_imp.rs | 71 + .../node-api/metadata/src/pallet_proxy.rs | 39 + .../node-api/metadata/src/pallet_sidechain.rs | 30 + .../node-api/metadata/src/pallet_system.rs | 52 + .../node-api/metadata/src/pallet_teeracle.rs | 46 + .../node-api/metadata/src/pallet_teerex.rs | 114 + .../node-api/metadata/src/pallet_utility.rs | 50 + .../node-api/metadata/src/pallet_vcmp.rs | 42 + .../node-api/metadata/src/runtime_call.rs | 41 + .../core-primitives/node-api/src/lib.rs | 37 + .../core-primitives/nonce-cache/Cargo.toml | 27 + .../core-primitives/nonce-cache/src/error.rs | 32 + .../core-primitives/nonce-cache/src/lib.rs | 64 + .../nonce-cache/src/nonce_cache.rs | 101 + .../core-primitives/ocall-api/Cargo.toml | 32 + .../core-primitives/ocall-api/src/lib.rs | 158 + .../primitives-cache/Cargo.toml | 30 + .../primitives-cache/src/error.rs | 31 + .../primitives-cache/src/lib.rs | 114 + .../primitives-cache/src/primitives_cache.rs | 117 + .../core-primitives/rpc/Cargo.toml | 26 + .../core-primitives/rpc/src/lib.rs | 114 + .../core-primitives/settings/Cargo.toml | 14 + .../core-primitives/settings/src/lib.rs | 120 + .../settings/src/worker_mode.rs | 59 + .../sgx-runtime-primitives/Cargo.toml | 27 + .../sgx-runtime-primitives/src/constants.rs | 29 + .../sgx-runtime-primitives/src/lib.rs | 21 + .../sgx-runtime-primitives/src/types.rs | 86 + .../core-primitives/sgx/crypto/Cargo.toml | 56 + .../core-primitives/sgx/crypto/src/aes.rs | 203 + .../core-primitives/sgx/crypto/src/ed25519.rs | 180 + .../sgx/crypto/src/ed25519_derivation.rs | 36 + .../core-primitives/sgx/crypto/src/error.rs | 43 + .../sgx/crypto/src/key_repository.rs | 122 + .../core-primitives/sgx/crypto/src/lib.rs | 63 + .../core-primitives/sgx/crypto/src/mocks.rs | 118 + .../core-primitives/sgx/crypto/src/rsa3072.rs | 221 + .../core-primitives/sgx/crypto/src/traits.rs | 42 + .../core-primitives/sgx/io/Cargo.toml | 18 + .../core-primitives/sgx/io/src/lib.rs | 94 + .../core-primitives/sgx/temp-dir/Cargo.toml | 20 + .../core-primitives/sgx/temp-dir/src/lib.rs | 192 + .../core-primitives/sgx/temp-dir/src/test.rs | 231 + .../core-primitives/stf-executor/Cargo.toml | 97 + .../stf-executor/src/enclave_signer.rs | 177 + .../core-primitives/stf-executor/src/error.rs | 88 + .../stf-executor/src/executor.rs | 381 + .../stf-executor/src/executor_tests.rs | 279 + .../stf-executor/src/getter_executor.rs | 137 + .../core-primitives/stf-executor/src/lib.rs | 305 + .../core-primitives/stf-executor/src/mocks.rs | 170 + .../stf-executor/src/state_getter.rs | 85 + .../stf-executor/src/traits.rs | 89 + .../core-primitives/stf-interface/Cargo.toml | 23 + .../core-primitives/stf-interface/src/lib.rs | 141 + .../stf-interface/src/mocks.rs | 132 + .../stf-interface/src/parentchain_pallet.rs | 27 + .../stf-interface/src/runtime_upgrade.rs | 21 + .../stf-interface/src/sudo_pallet.rs | 27 + .../stf-interface/src/system_pallet.rs | 53 + .../core-primitives/stf-primitives/Cargo.toml | 31 + .../stf-primitives/src/error.rs | 118 + .../core-primitives/stf-primitives/src/lib.rs | 22 + .../stf-primitives/src/traits.rs | 76 + .../stf-primitives/src/types.rs | 211 + .../stf-state-handler/Cargo.toml | 72 + .../stf-state-handler/src/error.rs | 90 + .../stf-state-handler/src/file_io.rs | 428 + .../stf-state-handler/src/handle_state.rs | 83 + .../src/in_memory_state_file_io.rs | 418 + .../stf-state-handler/src/lib.rs | 46 + .../src/query_shard_state.rs | 32 + .../stf-state-handler/src/state_handler.rs | 423 + .../src/state_initializer.rs | 64 + .../src/state_snapshot_primitives.rs | 56 + .../src/state_snapshot_repository.rs | 484 + .../src/state_snapshot_repository_loader.rs | 221 + .../src/test/mocks/initialize_state_mock.rs | 42 + .../stf-state-handler/src/test/mocks/mod.rs | 20 + .../test/mocks/state_key_repository_mock.rs | 68 + .../test/mocks/versioned_state_access_mock.rs | 102 + .../stf-state-handler/src/test/mod.rs | 25 + .../stf-state-handler/src/test/sgx_tests.rs | 360 + .../stf-state-observer/Cargo.toml | 31 + .../stf-state-observer/src/error.rs | 34 + .../stf-state-observer/src/lib.rs | 38 + .../stf-state-observer/src/mock.rs | 79 + .../stf-state-observer/src/state_observer.rs | 148 + .../stf-state-observer/src/traits.rs | 37 + .../core-primitives/storage/Cargo.toml | 49 + .../core-primitives/storage/src/error.rs | 43 + .../core-primitives/storage/src/keys.rs | 71 + .../core-primitives/storage/src/lib.rs | 35 + .../core-primitives/storage/src/proof.rs | 121 + .../storage/src/verify_storage_proof.rs | 67 + .../substrate-sgx/environmental/Cargo.toml | 15 + .../substrate-sgx/environmental/src/lib.rs | 479 + .../substrate-sgx/externalities/Cargo.toml | 44 + .../substrate-sgx/externalities/src/bypass.rs | 60 + .../externalities/src/codec_impl.rs | 149 + .../substrate-sgx/externalities/src/lib.rs | 470 + .../externalities/src/scope_limited.rs | 38 + .../externalities/src/vectorize.rs | 76 + .../substrate-sgx/sp-io/Cargo.toml | 39 + .../substrate-sgx/sp-io/src/lib.rs | 1012 + .../core-primitives/teerex-storage/Cargo.toml | 18 + .../core-primitives/teerex-storage/src/lib.rs | 35 + .../core-primitives/test/Cargo.toml | 73 + .../test/src/builders/enclave_gen_builder.rs | 63 + .../core-primitives/test/src/builders/mod.rs | 21 + .../core-primitives/test/src/lib.rs | 37 + .../test/src/mock/handle_state_mock.rs | 241 + .../test/src/mock/metrics_ocall_mock.rs | 54 + .../core-primitives/test/src/mock/mod.rs | 23 + .../test/src/mock/onchain_mock.rs | 243 + .../test/src/mock/shielding_crypto_mock.rs | 58 + .../test/src/mock/sidechain_ocall_api_mock.rs | 124 + .../core-primitives/test/src/mock/stf_mock.rs | 297 + .../core-primitives/time-utils/Cargo.toml | 21 + .../core-primitives/time-utils/src/lib.rs | 79 + .../top-pool-author/Cargo.toml | 78 + .../top-pool-author/src/api.rs | 174 + .../top-pool-author/src/author.rs | 582 + .../top-pool-author/src/author_tests.rs | 203 + .../top-pool-author/src/client_error.rs | 183 + .../top-pool-author/src/error.rs | 111 + .../top-pool-author/src/lib.rs | 51 + .../top-pool-author/src/mocks.rs | 315 + .../top-pool-author/src/test_fixtures.rs | 42 + .../top-pool-author/src/test_utils.rs | 63 + .../top-pool-author/src/top_filter.rs | 320 + .../top-pool-author/src/traits.rs | 109 + .../core-primitives/top-pool/Cargo.toml | 66 + .../core-primitives/top-pool/src/base_pool.rs | 1379 ++ .../top-pool/src/basic_pool.rs | 258 + .../core-primitives/top-pool/src/error.rs | 95 + .../core-primitives/top-pool/src/future.rs | 316 + .../core-primitives/top-pool/src/lib.rs | 47 + .../core-primitives/top-pool/src/listener.rs | 185 + .../core-primitives/top-pool/src/mocks/mod.rs | 22 + .../top-pool/src/mocks/rpc_responder_mock.rs | 76 + .../src/mocks/trusted_operation_pool_mock.rs | 227 + .../core-primitives/top-pool/src/pool.rs | 818 + .../top-pool/src/primitives.rs | 350 + .../core-primitives/top-pool/src/ready.rs | 800 + .../core-primitives/top-pool/src/rotator.rs | 221 + .../top-pool/src/tracked_map.rs | 198 + .../top-pool/src/validated_pool.rs | 738 + .../core-primitives/top-pool/src/watcher.rs | 171 + .../core-primitives/types/Cargo.toml | 50 + .../core-primitives/types/src/lib.rs | 218 + .../core-primitives/types/src/parentchain.rs | 224 + .../core-primitives/types/src/storage.rs | 59 + .../core-primitives/utils/Cargo.toml | 19 + .../core-primitives/utils/src/buffer.rs | 67 + .../core-primitives/utils/src/error.rs | 27 + .../core-primitives/utils/src/hex.rs | 117 + .../core-primitives/utils/src/hex_display.rs | 96 + .../core-primitives/utils/src/lib.rs | 35 + .../core-primitives/utils/src/macros.rs | 35 + .../core-primitives/utils/src/stringify.rs | 36 + .../core/direct-rpc-client/Cargo.toml | 53 + .../core/direct-rpc-client/src/lib.rs | 262 + .../core/direct-rpc-server/Cargo.toml | 55 + .../direct-rpc-server/src/builders/mod.rs | 19 + .../src/builders/rpc_response_builder.rs | 64 + .../src/builders/rpc_return_value_builder.rs | 62 + .../core/direct-rpc-server/src/lib.rs | 158 + .../src/mocks/determine_watch_mock.rs | 52 + .../core/direct-rpc-server/src/mocks/mod.rs | 20 + .../src/mocks/response_channel_mock.rs | 55 + .../src/mocks/send_rpc_response_mock.rs | 74 + .../direct-rpc-server/src/response_channel.rs | 26 + .../src/rpc_connection_registry.rs | 140 + .../direct-rpc-server/src/rpc_responder.rs | 363 + .../src/rpc_watch_extractor.rs | 131 + .../direct-rpc-server/src/rpc_ws_handler.rs | 226 + .../core/offchain-worker-executor/Cargo.toml | 70 + .../offchain-worker-executor/src/error.rs | 40 + .../offchain-worker-executor/src/executor.rs | 373 + .../core/offchain-worker-executor/src/lib.rs | 33 + .../block-import-dispatcher/Cargo.toml | 50 + .../block-import-dispatcher/src/error.rs | 47 + .../src/immediate_dispatcher.rs | 107 + .../block-import-dispatcher/src/lib.rs | 125 + .../trigger_parentchain_block_import_mock.rs | 102 + .../src/triggered_dispatcher.rs | 374 + .../parentchain/block-importer/Cargo.toml | 69 + .../block-importer/src/block_importer.rs | 190 + .../block-importer/src/block_importer_mock.rs | 65 + .../parentchain/block-importer/src/error.rs | 51 + .../parentchain/block-importer/src/lib.rs | 61 + .../indirect-calls-executor/Cargo.toml | 94 + .../indirect-calls-executor/src/error.rs | 111 + .../src/event_filter.rs | 33 + .../indirect-calls-executor/src/executor.rs | 519 + .../src/filter_metadata.rs | 112 + .../indirect-calls-executor/src/lib.rs | 52 + .../indirect-calls-executor/src/mock.rs | 247 + .../indirect-calls-executor/src/traits.rs | 59 + .../core/parentchain/light-client/Cargo.toml | 71 + .../light-client/src/concurrent_access.rs | 143 + .../parentchain/light-client/src/error.rs | 84 + .../parentchain/light-client/src/finality.rs | 187 + .../core/parentchain/light-client/src/io.rs | 386 + .../light-client/src/justification.rs | 229 + .../core/parentchain/light-client/src/lib.rs | 111 + .../src/light_client_init_params.rs | 49 + .../light-client/src/light_validation.rs | 266 + .../src/light_validation_state.rs | 68 + .../parentchain/light-client/src/mocks/mod.rs | 20 + .../src/mocks/validator_access_mock.rs | 66 + .../light-client/src/mocks/validator_mock.rs | 79 + .../src/mocks/validator_mock_seal.rs | 64 + .../parentchain/light-client/src/state.rs | 100 + .../parentchain/parentchain-crate/Cargo.toml | 44 + .../parentchain/parentchain-crate/src/lib.rs | 33 + .../parentchain-crate/src/primitives.rs | 61 + .../core/parentchain/test/Cargo.toml | 19 + .../core/parentchain/test/src/lib.rs | 27 + .../test/src/parentchain_block_builder.rs | 62 + .../test/src/parentchain_header_builder.rs | 53 + .../core/peer-top-broadcaster/Cargo.toml | 47 + .../core/peer-top-broadcaster/src/lib.rs | 374 + bitacross-worker/core/rest-client/Cargo.toml | 47 + .../core/rest-client/src/error.rs | 58 + .../src/fixtures/amazon_root_ca_1_v3.pem | 20 + .../baltimore_cyber_trust_root_v3.pem | 21 + .../src/fixtures/lets_encrypt_root_cert.pem | 31 + .../core/rest-client/src/http_client.rs | 584 + .../rest-client/src/http_client_builder.rs | 112 + bitacross-worker/core/rest-client/src/lib.rs | 182 + .../rest-client/src/mocks/http_client_mock.rs | 144 + .../core/rest-client/src/mocks/mod.rs | 18 + .../core/rest-client/src/rest_client.rs | 354 + bitacross-worker/core/rpc-client/Cargo.toml | 40 + .../core/rpc-client/src/direct_client.rs | 369 + bitacross-worker/core/rpc-client/src/error.rs | 48 + bitacross-worker/core/rpc-client/src/lib.rs | 22 + bitacross-worker/core/rpc-client/src/mock.rs | 122 + .../core/rpc-client/src/ws_client.rs | 168 + bitacross-worker/core/rpc-server/Cargo.toml | 30 + bitacross-worker/core/rpc-server/src/lib.rs | 81 + bitacross-worker/core/rpc-server/src/mock.rs | 75 + bitacross-worker/core/rpc-server/src/tests.rs | 56 + .../core/tls-websocket-server/Cargo.toml | 70 + .../src/certificate_generation.rs | 172 + .../src/config_provider.rs | 45 + .../tls-websocket-server/src/connection.rs | 344 + .../src/connection_id_generator.rs | 76 + .../core/tls-websocket-server/src/error.rs | 55 + .../core/tls-websocket-server/src/lib.rs | 177 + .../tls-websocket-server/src/stream_state.rs | 105 + .../src/test/fixtures/mod.rs | 22 + .../src/test/fixtures/no_cert_verifier.rs | 51 + .../src/test/fixtures/test_cert.rs | 139 + .../src/test/fixtures/test_private_key.rs | 53 + .../src/test/fixtures/test_server.rs | 41 + .../fixtures/test_server_config_provider.rs | 43 + .../src/test/mocks/mod.rs | 19 + .../test/mocks/web_socket_connection_mock.rs | 103 + .../src/test/mocks/web_socket_handler_mock.rs | 68 + .../core/tls-websocket-server/src/test/mod.rs | 19 + .../tls-websocket-server/src/tls_common.rs | 70 + .../tls-websocket-server/src/ws_server.rs | 518 + bitacross-worker/docker/README.md | 116 + bitacross-worker/docker/demo-direct-call.yml | 27 + ...demo-shielding-unshielding-multiworker.yml | 29 + bitacross-worker/docker/demo-sidechain.yml | 32 + .../docker/demo-smart-contract.yml | 31 + .../docker/demo-teeracle-generic.yml | 68 + bitacross-worker/docker/demo-teeracle.yml | 71 + bitacross-worker/docker/docker-compose.yml | 156 + bitacross-worker/docker/entrypoint.sh | 19 + bitacross-worker/docker/fork-inducer.yml | 43 + bitacross-worker/docker/fork.Dockerfile | 26 + bitacross-worker/docker/lit-ii-batch-test.yml | 24 + .../docker/lit-parentchain-nonce.yml | 24 + .../docker/lit-set-heartbeat-timeout.yml | 24 + .../docker/litentry-parachain.build.yml | 104 + .../docker/litentry/docker-compose.yml | 87 + .../docker/litentry/parachain-2106.Dockerfile | 2 + .../docker/litentry/relaychain.Dockerfile | 2 + .../docker/litentry/rococo-dev-2106.json | 138 + .../docker/litentry/rococo-local.json | 170 + .../docker/multiworker-docker-compose.yml | 257 + bitacross-worker/docker/ping.Dockerfile | 19 + .../docker/sidechain-benchmark.yml | 27 + bitacross-worker/docs/README.md | 25 + .../docs/diagramms/block_import_sequence.svg | 4 + bitacross-worker/enclave-runtime/Cargo.lock | 5349 +++++ bitacross-worker/enclave-runtime/Cargo.toml | 175 + .../Enclave.config.production.xml | 12 + .../enclave-runtime/Enclave.config.xml | 12 + bitacross-worker/enclave-runtime/Enclave.edl | 277 + bitacross-worker/enclave-runtime/Enclave.lds | 9 + .../enclave-runtime/Enclave_private.pem | 39 + bitacross-worker/enclave-runtime/Makefile | 58 + bitacross-worker/enclave-runtime/README.md | 2 + .../enclave-runtime/rust-toolchain.toml | 4 + bitacross-worker/enclave-runtime/rustfmt.toml | 18 + .../enclave-runtime/src/attestation.rs | 554 + .../enclave-runtime/src/empty_impls.rs | 56 + bitacross-worker/enclave-runtime/src/error.rs | 87 + .../src/initialization/global_components.rs | 501 + .../enclave-runtime/src/initialization/mod.rs | 366 + .../src/initialization/parentchain/common.rs | 297 + .../parentchain/integritee_parachain.rs | 118 + .../parentchain/integritee_solochain.rs | 117 + .../src/initialization/parentchain/mod.rs | 120 + .../parentchain/target_a_parachain.rs | 122 + .../parentchain/target_a_solochain.rs | 115 + .../parentchain/target_b_parachain.rs | 122 + .../parentchain/target_b_solochain.rs | 115 + bitacross-worker/enclave-runtime/src/ipfs.rs | 105 + bitacross-worker/enclave-runtime/src/lib.rs | 708 + .../src/ocall/attestation_ocall.rs | 275 + .../enclave-runtime/src/ocall/ffi.rs | 138 + .../enclave-runtime/src/ocall/ipfs_ocall.rs | 57 + .../src/ocall/metrics_ocall.rs | 42 + .../enclave-runtime/src/ocall/mod.rs | 26 + .../src/ocall/on_chain_ocall.rs | 144 + .../src/ocall/sidechain_ocall.rs | 139 + .../enclave-runtime/src/rpc/mod.rs | 19 + .../src/rpc/rpc_response_channel.rs | 40 + .../src/rpc/worker_api_direct.rs | 565 + .../enclave-runtime/src/shard_vault.rs | 250 + .../enclave-runtime/src/stf_task_handler.rs | 1 + bitacross-worker/enclave-runtime/src/sync.rs | 104 + .../enclave-runtime/src/teeracle/mod.rs | 279 + .../enclave-runtime/src/test/Counter.sol | 31 + .../enclave-runtime/src/test/cert_tests.rs | 72 + .../src/test/direct_rpc_tests.rs | 91 + .../src/test/enclave_signer_tests.rs | 169 + .../src/test/evm_pallet_tests.rs | 401 + .../src/test/fixtures/components.rs | 69 + .../test/fixtures/initialize_test_state.rs | 42 + .../enclave-runtime/src/test/fixtures/mod.rs | 21 + .../src/test/fixtures/ra_dump_cert_TEST4.der | Bin 0 -> 3234 bytes ...st_ra_signer_attn_MRSIGNER1_MRENCLAVE1.bin | Bin 0 -> 64 bytes .../src/test/fixtures/test_setup.rs | 128 + .../enclave-runtime/src/test/ipfs_tests.rs | 42 + .../src/test/mocks/attestation_ocall_mock.rs | 101 + .../src/test/mocks/enclave_rpc_ocall_mock.rs | 40 + .../enclave-runtime/src/test/mocks/mod.rs | 24 + .../src/test/mocks/peer_updater_mock.rs | 24 + .../test/mocks/propose_to_import_call_mock.rs | 137 + .../src/test/mocks/rpc_responder_mock.rs | 75 + .../enclave-runtime/src/test/mocks/types.rs | 114 + .../enclave-runtime/src/test/mod.rs | 34 + .../src/test/sidechain_aura_tests.rs | 287 + .../src/test/sidechain_event_tests.rs | 190 + .../src/test/state_getter_tests.rs | 53 + .../src/test/teeracle_tests.rs | 50 + .../enclave-runtime/src/test/tests_main.rs | 810 + .../src/test/top_pool_tests.rs | 236 + .../enclave-runtime/src/tls_ra/README.md | 33 + .../src/tls_ra/authentication.rs | 158 + .../enclave-runtime/src/tls_ra/mocks.rs | 87 + .../enclave-runtime/src/tls_ra/mod.rs | 81 + .../src/tls_ra/seal_handler.rs | 268 + .../enclave-runtime/src/tls_ra/tests.rs | 196 + .../src/tls_ra/tls_ra_client.rs | 327 + .../src/tls_ra/tls_ra_server.rs | 323 + .../enclave-runtime/src/top_pool_execution.rs | 412 + bitacross-worker/enclave-runtime/src/utils.rs | 279 + .../enclave-runtime/src/vc_issuance_task.rs | 0 .../x86_64-unknown-linux-sgx.json | 31 + bitacross-worker/extract_identity | 28 + bitacross-worker/lib/readme.txt | 1 + bitacross-worker/license_header_scs.txt | 16 + .../core/scheduled-enclave/Cargo.toml | 37 + .../core/scheduled-enclave/src/error.rs | 51 + .../litentry/core/scheduled-enclave/src/io.rs | 179 + .../core/scheduled-enclave/src/lib.rs | 153 + bitacross-worker/litentry/macros/Cargo.toml | 12 + bitacross-worker/litentry/macros/src/lib.rs | 59 + .../litentry/primitives/Cargo.toml | 64 + .../litentry/primitives/src/aes.rs | 134 + .../litentry/primitives/src/aes_request.rs | 69 + .../primitives/src/bitcoin_address.rs | 57 + .../primitives/src/bitcoin_signature.rs | 72 + .../primitives/src/ethereum_signature.rs | 72 + .../litentry/primitives/src/lib.rs | 279 + .../primitives/src/validation_data.rs | 96 + bitacross-worker/local-setup/.env.example | 13 + bitacross-worker/local-setup/README.md | 37 + bitacross-worker/local-setup/__init__.py | 0 .../local-setup/config/benchmark.json | 44 + .../local-setup/config/one-worker.json | 49 + .../config/three-nodes-one-worker.json | 94 + .../local-setup/config/three-workers.json | 76 + .../local-setup/config/two-workers.json | 76 + .../local-setup/development-worker.json | 28 + bitacross-worker/local-setup/launch.py | 275 + bitacross-worker/local-setup/py/__init__.py | 0 bitacross-worker/local-setup/py/helpers.py | 104 + bitacross-worker/local-setup/py/worker.py | 220 + .../local-setup/rococo_one_worker.json | 29 + bitacross-worker/local-setup/tmux_logger.sh | 32 + .../local-setup/tmux_logger_three_nodes.sh | 34 + .../local-setup/worker-log-level-config.toml | 38 + bitacross-worker/rust-sgx-sdk/Readme.md | 5 + bitacross-worker/rust-sgx-sdk/buildenv.mk | 179 + .../rust-sgx-sdk/common/inc/assert.h | 63 + .../rust-sgx-sdk/common/inc/complex.h | 134 + .../rust-sgx-sdk/common/inc/ctype.h | 65 + .../rust-sgx-sdk/common/inc/dirent.h | 48 + .../rust-sgx-sdk/common/inc/endian.h | 33 + .../rust-sgx-sdk/common/inc/errno.h | 187 + .../rust-sgx-sdk/common/inc/fenv.h | 139 + .../rust-sgx-sdk/common/inc/float.h | 84 + .../rust-sgx-sdk/common/inc/inttypes.h | 330 + .../rust-sgx-sdk/common/inc/iso646.h | 26 + .../rust-sgx-sdk/common/inc/limits.h | 41 + .../rust-sgx-sdk/common/inc/math.h | 430 + .../rust-sgx-sdk/common/inc/mbusafecrt.h | 85 + .../rust-sgx-sdk/common/inc/netdb.h | 41 + .../rust-sgx-sdk/common/inc/poll.h | 38 + .../rust-sgx-sdk/common/inc/pthread.h | 34 + .../rust-sgx-sdk/common/inc/pwd.h | 40 + .../rust-sgx-sdk/common/inc/sched.h | 62 + .../rust-sgx-sdk/common/inc/setjmp.h | 65 + .../rust-sgx-sdk/common/inc/signal.h | 104 + .../rust-sgx-sdk/common/inc/stdalign.h | 15 + .../rust-sgx-sdk/common/inc/stdarg.h | 48 + .../rust-sgx-sdk/common/inc/stdbool.h | 44 + .../rust-sgx-sdk/common/inc/stddef.h | 70 + .../rust-sgx-sdk/common/inc/stdint.h | 24 + .../rust-sgx-sdk/common/inc/stdio.h | 95 + .../rust-sgx-sdk/common/inc/stdlib.h | 159 + .../rust-sgx-sdk/common/inc/string.h | 130 + .../rust-sgx-sdk/common/inc/sys/_types.h | 168 + .../rust-sgx-sdk/common/inc/sys/cdefs.h | 132 + .../rust-sgx-sdk/common/inc/sys/endian.h | 54 + .../rust-sgx-sdk/common/inc/sys/epoll.h | 42 + .../rust-sgx-sdk/common/inc/sys/fpu.h | 99 + .../rust-sgx-sdk/common/inc/sys/ieee.h | 170 + .../rust-sgx-sdk/common/inc/sys/limits.h | 77 + .../rust-sgx-sdk/common/inc/sys/sockaddr.h | 32 + .../rust-sgx-sdk/common/inc/sys/socket.h | 54 + .../rust-sgx-sdk/common/inc/sys/stat.h | 127 + .../rust-sgx-sdk/common/inc/sys/stdint.h | 260 + .../common/inc/sys/struct_timespec.h | 37 + .../rust-sgx-sdk/common/inc/sys/types.h | 129 + .../rust-sgx-sdk/common/inc/sys/uio.h | 35 + .../rust-sgx-sdk/common/inc/time.h | 105 + .../rust-sgx-sdk/common/inc/unistd.h | 59 + .../rust-sgx-sdk/common/inc/wchar.h | 143 + .../rust-sgx-sdk/common/inc/wctype.h | 80 + .../rust-sgx-sdk/edl/inc/dirent.h | 39 + bitacross-worker/rust-sgx-sdk/edl/inc/stat.h | 65 + .../rust-sgx-sdk/edl/intel/sgx_dcap_tvl.edl | 73 + .../rust-sgx-sdk/edl/intel/sgx_pthread.edl | 38 + .../edl/intel/sgx_tkey_exchange.edl | 49 + .../edl/intel/sgx_tprotected_fs.edl | 47 + .../rust-sgx-sdk/edl/intel/sgx_tstdc.edl | 48 + .../edl/intel/sgx_tswitchless.edl | 39 + .../rust-sgx-sdk/edl/intel/sgx_ttls.edl | 62 + .../rust-sgx-sdk/edl/sgx_asyncio.edl | 33 + .../rust-sgx-sdk/edl/sgx_backtrace.edl | 31 + bitacross-worker/rust-sgx-sdk/edl/sgx_env.edl | 40 + bitacross-worker/rust-sgx-sdk/edl/sgx_fd.edl | 57 + .../rust-sgx-sdk/edl/sgx_file.edl | 66 + bitacross-worker/rust-sgx-sdk/edl/sgx_fs.edl | 31 + bitacross-worker/rust-sgx-sdk/edl/sgx_mem.edl | 40 + bitacross-worker/rust-sgx-sdk/edl/sgx_net.edl | 41 + .../rust-sgx-sdk/edl/sgx_net_switchless.edl | 92 + .../rust-sgx-sdk/edl/sgx_pipe.edl | 31 + .../rust-sgx-sdk/edl/sgx_process.edl | 28 + .../rust-sgx-sdk/edl/sgx_signal.edl | 43 + .../rust-sgx-sdk/edl/sgx_socket.edl | 111 + .../rust-sgx-sdk/edl/sgx_stdio.edl | 29 + bitacross-worker/rust-sgx-sdk/edl/sgx_sys.edl | 32 + .../rust-sgx-sdk/edl/sgx_thread.edl | 32 + .../rust-sgx-sdk/edl/sgx_time.edl | 29 + .../rust-sgx-sdk/edl/sgx_tstd.edl | 38 + bitacross-worker/rust-sgx-sdk/version | 1 + bitacross-worker/rust-toolchain.toml | 4 + bitacross-worker/rustfmt.toml | 18 + bitacross-worker/samples/teeracle/README.md | 58 + .../samples/teeracle/install-teeracle.sh | 7 + .../samples/teeracle/kubernetes/Chart.yaml | 7 + .../kubernetes/templates/teeracle.yaml | 73 + .../samples/teeracle/kubernetes/values.yaml | 14 + .../scripts/benchmark_local-setup.sh | 31 + bitacross-worker/scripts/changelog/.gitignore | 4 + bitacross-worker/scripts/changelog/Gemfile | 21 + .../scripts/changelog/Gemfile.lock | 79 + bitacross-worker/scripts/changelog/README.md | 3 + .../scripts/changelog/bin/changelog | 84 + .../scripts/changelog/digests/.gitignore | 1 + .../scripts/changelog/digests/.gitkeep | 0 .../scripts/changelog/lib/changelog.rb | 38 + .../changelog/templates/_free_notes.md.tera | 10 + .../templates/challenge_level.md.tera | 37 + .../changelog/templates/change.md.tera | 42 + .../changelog/templates/changes.md.tera | 24 + .../templates/changes_applibs.md.tera | 17 + .../templates/changes_client.md.tera | 17 + .../changelog/templates/changes_core.md.tera | 17 + .../changelog/templates/changes_evm.md.tera | 17 + .../changelog/templates/changes_misc.md.tera | 37 + .../templates/changes_offchain.md.tera | 17 + .../templates/changes_sidechain.md.tera | 17 + .../templates/changes_teeracle.md.tera | 17 + .../scripts/changelog/templates/debug.md.tera | 8 + .../templates/global_challenge_level.md.tera | 26 + .../templates/global_priority.md.tera | 27 + .../changelog/templates/high_priority.md.tera | 38 + .../changelog/templates/pre_release.md.tera | 11 + .../changelog/templates/template.md.tera | 33 + bitacross-worker/scripts/init_env.sh | 15 + bitacross-worker/scripts/launch.sh | 90 + .../scripts/launch_local_worker.sh | 138 + bitacross-worker/scripts/litentry/cleanup.sh | 12 + .../litentry/generate_parachain_artefacts.sh | 13 + .../scripts/litentry/identity_test.sh | 36 + .../scripts/litentry/release/ReadMe.md | 106 + .../scripts/litentry/release/build.sh | 50 + .../scripts/litentry/release/config.json.eg | 11 + .../scripts/litentry/release/deploy.sh | 555 + .../scripts/litentry/release/prepare.sh | 50 + .../release/template/para-alice.service | 15 + .../release/template/relay-alice.service | 15 + .../release/template/relay-bob.service | 15 + .../litentry/release/template/worker.service | 14 + .../scripts/litentry/start_parachain.sh | 22 + .../scripts/litentry/stop_parachain.sh | 6 + .../scripts/litentry/ubuntu_setup.sh | 71 + bitacross-worker/scripts/m6.sh | 23 + bitacross-worker/scripts/m8.sh | 21 + bitacross-worker/scripts/polkadot_update.sh | 97 + bitacross-worker/scripts/sidechain.sh | 17 + bitacross-worker/scripts/teeracle.sh | 19 + .../scripts/test_transfer/README.md | 6 + .../scripts/test_transfer/package-lock.json | 1322 ++ .../scripts/test_transfer/package.json | 16 + .../scripts/test_transfer/transfer.js | 53 + bitacross-worker/service/Cargo.toml | 107 + bitacross-worker/service/build.rs | 31 + .../service/src/account_funding.rs | 168 + bitacross-worker/service/src/cli.yml | 227 + bitacross-worker/service/src/config.rs | 641 + bitacross-worker/service/src/enclave/api.rs | 114 + bitacross-worker/service/src/enclave/mod.rs | 20 + .../service/src/enclave/tls_ra.rs | 110 + bitacross-worker/service/src/error.rs | 61 + bitacross-worker/service/src/globals/mod.rs | 19 + .../service/src/globals/tokio_handle.rs | 108 + .../service/src/initialized_service.rs | 172 + bitacross-worker/service/src/main.rs | 52 + bitacross-worker/service/src/main_impl.rs | 1046 + .../service/src/ocall_bridge/bridge_api.rs | 264 + .../src/ocall_bridge/component_factory.rs | 176 + .../ffi/fetch_sidechain_blocks_from_peer.rs | 193 + .../src/ocall_bridge/ffi/get_ias_socket.rs | 86 + .../service/src/ocall_bridge/ffi/get_peers.rs | 37 + .../service/src/ocall_bridge/ffi/get_quote.rs | 140 + .../ffi/get_qve_report_on_quote.rs | 100 + .../src/ocall_bridge/ffi/get_update_info.rs | 61 + .../src/ocall_bridge/ffi/init_quote.rs | 85 + .../service/src/ocall_bridge/ffi/ipfs.rs | 76 + .../service/src/ocall_bridge/ffi/mod.rs | 36 + .../ffi/propose_sidechain_blocks.rs | 50 + .../ocall_bridge/ffi/send_to_parentchain.rs | 67 + .../ffi/store_sidechain_blocks.rs | 50 + .../src/ocall_bridge/ffi/update_metric.rs | 50 + .../src/ocall_bridge/ffi/worker_request.rs | 77 + .../service/src/ocall_bridge/ipfs_ocall.rs | 112 + .../service/src/ocall_bridge/metrics_ocall.rs | 51 + .../service/src/ocall_bridge/mod.rs | 32 + .../ocall_bridge/remote_attestation_ocall.rs | 150 + .../src/ocall_bridge/sidechain_ocall.rs | 282 + .../src/ocall_bridge/test/mocks/mod.rs | 19 + .../test/mocks/sidechain_bridge_mock.rs | 54 + .../service/src/ocall_bridge/test/mod.rs | 19 + .../src/ocall_bridge/worker_on_chain_ocall.rs | 257 + .../service/src/parentchain_handler.rs | 260 + .../service/src/prometheus_metrics.rs | 300 + bitacross-worker/service/src/setup.rs | 242 + .../service/src/sidechain_setup.rs | 129 + .../service/src/sync_block_broadcaster.rs | 57 + bitacross-worker/service/src/sync_state.rs | 99 + bitacross-worker/service/src/teeracle/mod.rs | 142 + .../service/src/teeracle/schedule_periodic.rs | 46 + .../service/src/teeracle/teeracle_metrics.rs | 76 + bitacross-worker/service/src/tests/commons.rs | 63 + bitacross-worker/service/src/tests/mock.rs | 68 + .../src/tests/mocks/broadcast_blocks_mock.rs | 28 + .../src/tests/mocks/direct_request_mock.rs | 26 + .../src/tests/mocks/enclave_api_mock.rs | 124 + .../mocks/initialization_handler_mock.rs | 36 + .../service/src/tests/mocks/mod.rs | 23 + .../src/tests/mocks/parentchain_api_mock.rs | 104 + .../tests/mocks/update_worker_peers_mock.rs | 33 + bitacross-worker/service/src/tests/mod.rs | 48 + .../src/tests/parentchain_handler_test.rs | 51 + bitacross-worker/service/src/utils.rs | 53 + bitacross-worker/service/src/wasm.rs | 62 + bitacross-worker/service/src/worker.rs | 297 + .../service/src/worker_peers_registry.rs | 56 + .../sidechain/block-composer/Cargo.toml | 64 + .../block-composer/src/block_composer.rs | 185 + .../sidechain/block-composer/src/error.rs | 59 + .../sidechain/block-composer/src/lib.rs | 36 + .../sidechain/block-verification/Cargo.toml | 52 + .../sidechain/block-verification/src/error.rs | 46 + .../sidechain/block-verification/src/lib.rs | 492 + .../sidechain/block-verification/src/slot.rs | 45 + .../sidechain/consensus/aura/Cargo.toml | 105 + .../consensus/aura/src/block_importer.rs | 367 + .../sidechain/consensus/aura/src/lib.rs | 773 + .../consensus/aura/src/proposer_factory.rs | 131 + .../consensus/aura/src/slot_proposer.rs | 206 + .../aura/src/test/block_importer_tests.rs | 318 + .../consensus/aura/src/test/fixtures/mod.rs | 27 + .../consensus/aura/src/test/fixtures/types.rs | 48 + .../aura/src/test/mocks/environment_mock.rs | 58 + .../consensus/aura/src/test/mocks/mod.rs | 20 + .../aura/src/test/mocks/peer_updater_mock.rs | 7 + .../aura/src/test/mocks/proposer_mock.rs | 73 + .../sidechain/consensus/aura/src/test/mod.rs | 20 + .../sidechain/consensus/aura/src/verifier.rs | 89 + .../sidechain/consensus/common/Cargo.toml | 85 + .../consensus/common/src/block_import.rs | 201 + .../src/block_import_confirmation_handler.rs | 130 + .../common/src/block_import_queue_worker.rs | 120 + .../common/src/block_production_suspension.rs | 112 + .../sidechain/consensus/common/src/error.rs | 99 + .../consensus/common/src/header_db.rs | 44 + .../common/src/is_descendant_of_builder.rs | 133 + .../sidechain/consensus/common/src/lib.rs | 114 + .../consensus/common/src/peer_block_sync.rs | 320 + .../mocks/block_import_queue_worker_mock.rs | 263 + .../src/test/mocks/block_importer_mock.rs | 168 + .../test/mocks/confirm_block_import_mock.rs | 29 + .../consensus/common/src/test/mocks/mod.rs | 21 + .../common/src/test/mocks/verifier_mock.rs | 62 + .../consensus/common/src/test/mod.rs | 18 + .../sidechain/consensus/slots/Cargo.toml | 78 + .../sidechain/consensus/slots/src/lib.rs | 613 + .../sidechain/consensus/slots/src/mocks.rs | 158 + .../slots/src/per_shard_slot_worker_tests.rs | 100 + .../consensus/slots/src/slot_stream.rs | 116 + .../sidechain/consensus/slots/src/slots.rs | 421 + .../sidechain/fork-tree/Cargo.toml | 27 + .../sidechain/fork-tree/src/lib.rs | 1552 ++ .../sidechain/peer-fetch/Cargo.toml | 36 + .../peer-fetch/src/block_fetch_client.rs | 141 + .../peer-fetch/src/block_fetch_server.rs | 103 + .../sidechain/peer-fetch/src/error.rs | 44 + .../sidechain/peer-fetch/src/lib.rs | 49 + .../src/mocks/fetch_blocks_from_peer_mock.rs | 61 + .../sidechain/peer-fetch/src/mocks/mod.rs | 19 + .../src/mocks/untrusted_peer_fetch_mock.rs | 35 + .../peer-fetch/src/untrusted_peer_fetch.rs | 59 + .../sidechain/primitives/Cargo.toml | 36 + .../sidechain/primitives/src/lib.rs | 21 + .../sidechain/primitives/src/traits/mod.rs | 176 + .../sidechain/primitives/src/types/block.rs | 159 + .../primitives/src/types/block_data.rs | 82 + .../sidechain/primitives/src/types/header.rs | 91 + .../sidechain/primitives/src/types/mod.rs | 22 + .../sidechain/rpc-handler/Cargo.toml | 57 + .../sidechain/rpc-handler/src/constants.rs | 24 + .../rpc-handler/src/direct_top_pool_api.rs | 320 + .../rpc-handler/src/import_block_api.rs | 126 + .../sidechain/rpc-handler/src/lib.rs | 38 + .../sidechain/sidechain-crate/Cargo.toml | 36 + .../sidechain/sidechain-crate/src/lib.rs | 39 + bitacross-worker/sidechain/state/Cargo.toml | 56 + bitacross-worker/sidechain/state/src/error.rs | 31 + bitacross-worker/sidechain/state/src/impls.rs | 184 + bitacross-worker/sidechain/state/src/lib.rs | 208 + bitacross-worker/sidechain/storage/Cargo.toml | 34 + bitacross-worker/sidechain/storage/src/db.rs | 67 + .../sidechain/storage/src/error.rs | 34 + .../storage/src/fetch_blocks_mock.rs | 71 + .../sidechain/storage/src/interface.rs | 154 + bitacross-worker/sidechain/storage/src/lib.rs | 70 + .../sidechain/storage/src/storage.rs | 1176 ++ .../src/storage_tests_get_blocks_after.rs | 124 + .../src/storage_tests_get_blocks_in_range.rs | 104 + .../sidechain/storage/src/test_utils.rs | 96 + bitacross-worker/sidechain/test/Cargo.toml | 32 + bitacross-worker/sidechain/test/src/lib.rs | 29 + .../test/src/sidechain_block_builder.rs | 105 + .../test/src/sidechain_block_data_builder.rs | 102 + .../test/src/sidechain_header_builder.rs | 92 + .../sidechain/validateer-fetch/Cargo.toml | 38 + .../sidechain/validateer-fetch/src/error.rs | 27 + .../sidechain/validateer-fetch/src/lib.rs | 24 + .../validateer-fetch/src/validateer.rs | 104 + bitacross-worker/ts-tests/.editorconfig | 6 + bitacross-worker/ts-tests/.gitignore | 1 + bitacross-worker/ts-tests/.prettierrc | 7 + bitacross-worker/ts-tests/README.md | 23 + bitacross-worker/ts-tests/package.json | 9 + bitacross-worker/ts-tests/pnpm-lock.yaml | 9 + bitacross-worker/ts-tests/pnpm-workspace.yaml | 0 bitacross-worker/upstream_commit | 1 + scripts/pre-commit.sh | 25 +- 913 files changed, 123307 insertions(+), 7 deletions(-) create mode 100644 bitacross-worker/.dockerignore create mode 100644 bitacross-worker/.editorconfig create mode 100644 bitacross-worker/.env.dev create mode 100644 bitacross-worker/.gitattributes.orig create mode 100755 bitacross-worker/.githooks/pre-commit create mode 100644 bitacross-worker/.github/workflows/build_and_test.yml create mode 100644 bitacross-worker/.github/workflows/check_labels.yml create mode 100644 bitacross-worker/.github/workflows/delete-release.yml create mode 100644 bitacross-worker/.github/workflows/label-checker.yml create mode 100644 bitacross-worker/.github/workflows/publish-docker-release.yml create mode 100644 bitacross-worker/.github/workflows/publish-docker-sidechain.yml create mode 100644 bitacross-worker/.github/workflows/publish-docker-teeracle.yml create mode 100644 bitacross-worker/.github/workflows/publish-draft-release.yml create mode 100644 bitacross-worker/.gitignore create mode 100644 bitacross-worker/.taplo.toml create mode 100644 bitacross-worker/Cargo.lock create mode 100644 bitacross-worker/Cargo.toml create mode 100644 bitacross-worker/DESIGN.md create mode 100644 bitacross-worker/Dockerfile create mode 100755 bitacross-worker/Jenkinsfile create mode 100644 bitacross-worker/LICENSE create mode 100755 bitacross-worker/Makefile create mode 100755 bitacross-worker/README.md create mode 100755 bitacross-worker/UpdateRustSGXSDK.mk create mode 100644 bitacross-worker/app-libs/oracle/Cargo.toml create mode 100644 bitacross-worker/app-libs/oracle/src/certificates/amazon_root_ca_a.pem create mode 100644 bitacross-worker/app-libs/oracle/src/certificates/baltimore_cyber_trust_root_v3.pem create mode 100644 bitacross-worker/app-libs/oracle/src/certificates/lets_encrypt_root_cert.pem create mode 100644 bitacross-worker/app-libs/oracle/src/certificates/open_meteo_root.pem create mode 100644 bitacross-worker/app-libs/oracle/src/error.rs create mode 100644 bitacross-worker/app-libs/oracle/src/lib.rs create mode 100644 bitacross-worker/app-libs/oracle/src/metrics_exporter.rs create mode 100644 bitacross-worker/app-libs/oracle/src/mock.rs create mode 100644 bitacross-worker/app-libs/oracle/src/oracle_sources/coin_gecko.rs create mode 100644 bitacross-worker/app-libs/oracle/src/oracle_sources/coin_market_cap.rs create mode 100644 bitacross-worker/app-libs/oracle/src/oracle_sources/mod.rs create mode 100644 bitacross-worker/app-libs/oracle/src/oracle_sources/weather_oracle_source.rs create mode 100644 bitacross-worker/app-libs/oracle/src/oracles/exchange_rate_oracle.rs create mode 100644 bitacross-worker/app-libs/oracle/src/oracles/mod.rs create mode 100644 bitacross-worker/app-libs/oracle/src/oracles/weather_oracle.rs create mode 100644 bitacross-worker/app-libs/oracle/src/test.rs create mode 100644 bitacross-worker/app-libs/oracle/src/traits.rs create mode 100644 bitacross-worker/app-libs/oracle/src/types.rs create mode 100644 bitacross-worker/app-libs/parentchain-interface/Cargo.toml create mode 100644 bitacross-worker/app-libs/parentchain-interface/src/indirect_calls/invoke.rs create mode 100644 bitacross-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/args_executor.rs create mode 100644 bitacross-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/mod.rs create mode 100644 bitacross-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/scheduled_enclave.rs create mode 100644 bitacross-worker/app-libs/parentchain-interface/src/indirect_calls/mod.rs create mode 100644 bitacross-worker/app-libs/parentchain-interface/src/indirect_calls/shield_funds.rs create mode 100644 bitacross-worker/app-libs/parentchain-interface/src/indirect_calls/transfer_to_alice_shields_funds.rs create mode 100644 bitacross-worker/app-libs/parentchain-interface/src/integritee/event_filter.rs create mode 100644 bitacross-worker/app-libs/parentchain-interface/src/integritee/event_handler.rs create mode 100644 bitacross-worker/app-libs/parentchain-interface/src/integritee/extrinsic_parser.rs create mode 100644 bitacross-worker/app-libs/parentchain-interface/src/integritee/mod.rs create mode 100644 bitacross-worker/app-libs/parentchain-interface/src/lib.rs create mode 100644 bitacross-worker/app-libs/parentchain-interface/src/target_a/event_filter.rs create mode 100644 bitacross-worker/app-libs/parentchain-interface/src/target_a/event_handler.rs create mode 100644 bitacross-worker/app-libs/parentchain-interface/src/target_a/extrinsic_parser.rs create mode 100644 bitacross-worker/app-libs/parentchain-interface/src/target_a/mod.rs create mode 100644 bitacross-worker/app-libs/parentchain-interface/src/target_b/event_filter.rs create mode 100644 bitacross-worker/app-libs/parentchain-interface/src/target_b/event_handler.rs create mode 100644 bitacross-worker/app-libs/parentchain-interface/src/target_b/extrinsic_parser.rs create mode 100644 bitacross-worker/app-libs/parentchain-interface/src/target_b/mod.rs create mode 100644 bitacross-worker/app-libs/sgx-runtime/Cargo.toml create mode 100644 bitacross-worker/app-libs/sgx-runtime/src/evm.rs create mode 100644 bitacross-worker/app-libs/sgx-runtime/src/lib.rs create mode 100644 bitacross-worker/app-libs/stf/Cargo.toml create mode 100644 bitacross-worker/app-libs/stf/src/evm_helpers.rs create mode 100644 bitacross-worker/app-libs/stf/src/getter.rs create mode 100644 bitacross-worker/app-libs/stf/src/hash.rs create mode 100644 bitacross-worker/app-libs/stf/src/helpers.rs create mode 100644 bitacross-worker/app-libs/stf/src/lib.rs create mode 100644 bitacross-worker/app-libs/stf/src/stf_sgx.rs create mode 100644 bitacross-worker/app-libs/stf/src/stf_sgx_primitives.rs create mode 100644 bitacross-worker/app-libs/stf/src/stf_sgx_tests.rs create mode 100644 bitacross-worker/app-libs/stf/src/test_genesis.rs create mode 100644 bitacross-worker/app-libs/stf/src/trusted_call.rs create mode 100644 bitacross-worker/app-libs/stf/src/trusted_call_result.rs create mode 100644 bitacross-worker/bin/README.md create mode 100644 bitacross-worker/build.Dockerfile create mode 100644 bitacross-worker/cli/Cargo.toml create mode 100644 bitacross-worker/cli/README.md create mode 100755 bitacross-worker/cli/benchmark.sh create mode 100755 bitacross-worker/cli/demo_direct_call.sh create mode 100755 bitacross-worker/cli/demo_direct_call_2_workers.sh create mode 100755 bitacross-worker/cli/demo_shielding_unshielding.sh create mode 100755 bitacross-worker/cli/demo_shielding_unshielding_multiworker.sh create mode 100755 bitacross-worker/cli/demo_shielding_unshielding_using_shard_vault.sh create mode 100755 bitacross-worker/cli/demo_shielding_unshielding_using_shard_vault_on_target_a.sh create mode 100755 bitacross-worker/cli/demo_sidechain.sh create mode 100755 bitacross-worker/cli/demo_smart_contract.sh create mode 100755 bitacross-worker/cli/demo_teeracle_generic.sh create mode 100755 bitacross-worker/cli/demo_teeracle_whitelist.sh create mode 100755 bitacross-worker/cli/lit_parentchain_nonce.sh create mode 100755 bitacross-worker/cli/lit_set_heartbeat_timeout.sh create mode 100644 bitacross-worker/cli/src/attesteer/commands/mod.rs create mode 100644 bitacross-worker/cli/src/attesteer/commands/send_dcap_quote.rs create mode 100644 bitacross-worker/cli/src/attesteer/commands/send_ias_attestation.rs create mode 100644 bitacross-worker/cli/src/attesteer/mod.rs create mode 100644 bitacross-worker/cli/src/base_cli/commands/balance.rs create mode 100644 bitacross-worker/cli/src/base_cli/commands/faucet.rs create mode 100644 bitacross-worker/cli/src/base_cli/commands/listen.rs create mode 100644 bitacross-worker/cli/src/base_cli/commands/litentry/mod.rs create mode 100644 bitacross-worker/cli/src/base_cli/commands/litentry/set_heartbeat_timeout.rs create mode 100644 bitacross-worker/cli/src/base_cli/commands/mod.rs create mode 100644 bitacross-worker/cli/src/base_cli/commands/register_tcb_info.rs create mode 100644 bitacross-worker/cli/src/base_cli/commands/shield_funds.rs create mode 100644 bitacross-worker/cli/src/base_cli/commands/transfer.rs create mode 100644 bitacross-worker/cli/src/base_cli/mod.rs create mode 100644 bitacross-worker/cli/src/benchmark/mod.rs create mode 100644 bitacross-worker/cli/src/command_utils.rs create mode 100644 bitacross-worker/cli/src/commands.rs create mode 100644 bitacross-worker/cli/src/error.rs create mode 100644 bitacross-worker/cli/src/evm/commands/evm_call.rs create mode 100644 bitacross-worker/cli/src/evm/commands/evm_command_utils.rs create mode 100644 bitacross-worker/cli/src/evm/commands/evm_create.rs create mode 100644 bitacross-worker/cli/src/evm/commands/evm_read.rs create mode 100644 bitacross-worker/cli/src/evm/commands/mod.rs create mode 100644 bitacross-worker/cli/src/evm/mod.rs create mode 100644 bitacross-worker/cli/src/lib.rs create mode 100644 bitacross-worker/cli/src/main.rs create mode 100644 bitacross-worker/cli/src/oracle/commands/add_to_whitelist.rs create mode 100644 bitacross-worker/cli/src/oracle/commands/listen_to_exchange.rs create mode 100644 bitacross-worker/cli/src/oracle/commands/listen_to_oracle.rs create mode 100644 bitacross-worker/cli/src/oracle/commands/mod.rs create mode 100644 bitacross-worker/cli/src/oracle/mod.rs create mode 100644 bitacross-worker/cli/src/trusted_base_cli/commands/balance.rs create mode 100644 bitacross-worker/cli/src/trusted_base_cli/commands/get_shard.rs create mode 100644 bitacross-worker/cli/src/trusted_base_cli/commands/get_shard_vault.rs create mode 100644 bitacross-worker/cli/src/trusted_base_cli/commands/mod.rs create mode 100644 bitacross-worker/cli/src/trusted_base_cli/commands/nonce.rs create mode 100644 bitacross-worker/cli/src/trusted_base_cli/commands/set_balance.rs create mode 100644 bitacross-worker/cli/src/trusted_base_cli/commands/transfer.rs create mode 100644 bitacross-worker/cli/src/trusted_base_cli/commands/unshield_funds.rs create mode 100644 bitacross-worker/cli/src/trusted_base_cli/mod.rs create mode 100644 bitacross-worker/cli/src/trusted_cli.rs create mode 100644 bitacross-worker/cli/src/trusted_command_utils.rs create mode 100644 bitacross-worker/cli/src/trusted_operation.rs create mode 100644 bitacross-worker/cli/test_auto_shielding_with_transfer_bob.sh create mode 100755 bitacross-worker/cli/test_shield_on_target_nodes_with_transfer_to_alice.sh create mode 100644 bitacross-worker/cli/tests/basic_tests.rs create mode 100644 bitacross-worker/core-primitives/attestation-handler/AttestationReportSigningCACert.pem create mode 100644 bitacross-worker/core-primitives/attestation-handler/Cargo.toml create mode 100644 bitacross-worker/core-primitives/attestation-handler/src/attestation_handler.rs create mode 100644 bitacross-worker/core-primitives/attestation-handler/src/cert.rs create mode 100644 bitacross-worker/core-primitives/attestation-handler/src/collateral.rs create mode 100644 bitacross-worker/core-primitives/attestation-handler/src/error.rs create mode 100644 bitacross-worker/core-primitives/attestation-handler/src/lib.rs create mode 100644 bitacross-worker/core-primitives/component-container/Cargo.toml create mode 100644 bitacross-worker/core-primitives/component-container/src/atomic_container.rs create mode 100644 bitacross-worker/core-primitives/component-container/src/component_container.rs create mode 100644 bitacross-worker/core-primitives/component-container/src/error.rs create mode 100644 bitacross-worker/core-primitives/component-container/src/lib.rs create mode 100644 bitacross-worker/core-primitives/enclave-api/Cargo.toml create mode 100644 bitacross-worker/core-primitives/enclave-api/build.rs create mode 100644 bitacross-worker/core-primitives/enclave-api/ffi/Cargo.toml create mode 100644 bitacross-worker/core-primitives/enclave-api/ffi/build.rs create mode 100644 bitacross-worker/core-primitives/enclave-api/ffi/src/lib.rs create mode 100644 bitacross-worker/core-primitives/enclave-api/src/direct_request.rs create mode 100644 bitacross-worker/core-primitives/enclave-api/src/enclave_base.rs create mode 100644 bitacross-worker/core-primitives/enclave-api/src/enclave_test.rs create mode 100644 bitacross-worker/core-primitives/enclave-api/src/error.rs create mode 100644 bitacross-worker/core-primitives/enclave-api/src/lib.rs create mode 100644 bitacross-worker/core-primitives/enclave-api/src/remote_attestation.rs create mode 100644 bitacross-worker/core-primitives/enclave-api/src/sidechain.rs create mode 100644 bitacross-worker/core-primitives/enclave-api/src/teeracle_api.rs create mode 100644 bitacross-worker/core-primitives/enclave-api/src/utils.rs create mode 100644 bitacross-worker/core-primitives/enclave-bridge-storage/Cargo.toml create mode 100644 bitacross-worker/core-primitives/enclave-bridge-storage/src/lib.rs create mode 100644 bitacross-worker/core-primitives/enclave-metrics/Cargo.toml create mode 100644 bitacross-worker/core-primitives/enclave-metrics/src/lib.rs create mode 100644 bitacross-worker/core-primitives/extrinsics-factory/Cargo.toml create mode 100644 bitacross-worker/core-primitives/extrinsics-factory/src/error.rs create mode 100644 bitacross-worker/core-primitives/extrinsics-factory/src/lib.rs create mode 100644 bitacross-worker/core-primitives/extrinsics-factory/src/mock.rs create mode 100644 bitacross-worker/core-primitives/hashing/Cargo.toml create mode 100644 bitacross-worker/core-primitives/hashing/src/lib.rs create mode 100644 bitacross-worker/core-primitives/hashing/src/std_hash.rs create mode 100644 bitacross-worker/core-primitives/import-queue/Cargo.toml create mode 100644 bitacross-worker/core-primitives/import-queue/src/error.rs create mode 100644 bitacross-worker/core-primitives/import-queue/src/import_queue.rs create mode 100644 bitacross-worker/core-primitives/import-queue/src/lib.rs create mode 100644 bitacross-worker/core-primitives/networking-utils/Cargo.toml create mode 100644 bitacross-worker/core-primitives/networking-utils/src/lib.rs create mode 100644 bitacross-worker/core-primitives/networking-utils/src/ports.rs create mode 100644 bitacross-worker/core-primitives/node-api/Cargo.toml create mode 100644 bitacross-worker/core-primitives/node-api/api-client-extensions/Cargo.toml create mode 100644 bitacross-worker/core-primitives/node-api/api-client-extensions/src/account.rs create mode 100644 bitacross-worker/core-primitives/node-api/api-client-extensions/src/chain.rs create mode 100644 bitacross-worker/core-primitives/node-api/api-client-extensions/src/lib.rs create mode 100644 bitacross-worker/core-primitives/node-api/api-client-extensions/src/pallet_teeracle.rs create mode 100644 bitacross-worker/core-primitives/node-api/api-client-extensions/src/pallet_teerex.rs create mode 100644 bitacross-worker/core-primitives/node-api/api-client-extensions/src/pallet_teerex_api_mock.rs create mode 100644 bitacross-worker/core-primitives/node-api/api-client-types/Cargo.toml create mode 100644 bitacross-worker/core-primitives/node-api/api-client-types/src/lib.rs create mode 100644 bitacross-worker/core-primitives/node-api/factory/Cargo.toml create mode 100644 bitacross-worker/core-primitives/node-api/factory/src/lib.rs create mode 100644 bitacross-worker/core-primitives/node-api/metadata-provider/Cargo.toml create mode 100644 bitacross-worker/core-primitives/node-api/metadata-provider/src/error.rs create mode 100644 bitacross-worker/core-primitives/node-api/metadata-provider/src/lib.rs create mode 100644 bitacross-worker/core-primitives/node-api/metadata/Cargo.toml create mode 100644 bitacross-worker/core-primitives/node-api/metadata/src/error.rs create mode 100644 bitacross-worker/core-primitives/node-api/metadata/src/lib.rs create mode 100644 bitacross-worker/core-primitives/node-api/metadata/src/metadata_mocks.rs create mode 100644 bitacross-worker/core-primitives/node-api/metadata/src/pallet_balances.rs create mode 100644 bitacross-worker/core-primitives/node-api/metadata/src/pallet_imp.rs create mode 100644 bitacross-worker/core-primitives/node-api/metadata/src/pallet_proxy.rs create mode 100644 bitacross-worker/core-primitives/node-api/metadata/src/pallet_sidechain.rs create mode 100644 bitacross-worker/core-primitives/node-api/metadata/src/pallet_system.rs create mode 100644 bitacross-worker/core-primitives/node-api/metadata/src/pallet_teeracle.rs create mode 100644 bitacross-worker/core-primitives/node-api/metadata/src/pallet_teerex.rs create mode 100644 bitacross-worker/core-primitives/node-api/metadata/src/pallet_utility.rs create mode 100644 bitacross-worker/core-primitives/node-api/metadata/src/pallet_vcmp.rs create mode 100644 bitacross-worker/core-primitives/node-api/metadata/src/runtime_call.rs create mode 100644 bitacross-worker/core-primitives/node-api/src/lib.rs create mode 100644 bitacross-worker/core-primitives/nonce-cache/Cargo.toml create mode 100644 bitacross-worker/core-primitives/nonce-cache/src/error.rs create mode 100644 bitacross-worker/core-primitives/nonce-cache/src/lib.rs create mode 100644 bitacross-worker/core-primitives/nonce-cache/src/nonce_cache.rs create mode 100644 bitacross-worker/core-primitives/ocall-api/Cargo.toml create mode 100644 bitacross-worker/core-primitives/ocall-api/src/lib.rs create mode 100644 bitacross-worker/core-primitives/primitives-cache/Cargo.toml create mode 100644 bitacross-worker/core-primitives/primitives-cache/src/error.rs create mode 100644 bitacross-worker/core-primitives/primitives-cache/src/lib.rs create mode 100644 bitacross-worker/core-primitives/primitives-cache/src/primitives_cache.rs create mode 100644 bitacross-worker/core-primitives/rpc/Cargo.toml create mode 100644 bitacross-worker/core-primitives/rpc/src/lib.rs create mode 100644 bitacross-worker/core-primitives/settings/Cargo.toml create mode 100644 bitacross-worker/core-primitives/settings/src/lib.rs create mode 100644 bitacross-worker/core-primitives/settings/src/worker_mode.rs create mode 100644 bitacross-worker/core-primitives/sgx-runtime-primitives/Cargo.toml create mode 100644 bitacross-worker/core-primitives/sgx-runtime-primitives/src/constants.rs create mode 100644 bitacross-worker/core-primitives/sgx-runtime-primitives/src/lib.rs create mode 100644 bitacross-worker/core-primitives/sgx-runtime-primitives/src/types.rs create mode 100644 bitacross-worker/core-primitives/sgx/crypto/Cargo.toml create mode 100644 bitacross-worker/core-primitives/sgx/crypto/src/aes.rs create mode 100644 bitacross-worker/core-primitives/sgx/crypto/src/ed25519.rs create mode 100644 bitacross-worker/core-primitives/sgx/crypto/src/ed25519_derivation.rs create mode 100644 bitacross-worker/core-primitives/sgx/crypto/src/error.rs create mode 100644 bitacross-worker/core-primitives/sgx/crypto/src/key_repository.rs create mode 100644 bitacross-worker/core-primitives/sgx/crypto/src/lib.rs create mode 100644 bitacross-worker/core-primitives/sgx/crypto/src/mocks.rs create mode 100644 bitacross-worker/core-primitives/sgx/crypto/src/rsa3072.rs create mode 100644 bitacross-worker/core-primitives/sgx/crypto/src/traits.rs create mode 100644 bitacross-worker/core-primitives/sgx/io/Cargo.toml create mode 100644 bitacross-worker/core-primitives/sgx/io/src/lib.rs create mode 100644 bitacross-worker/core-primitives/sgx/temp-dir/Cargo.toml create mode 100644 bitacross-worker/core-primitives/sgx/temp-dir/src/lib.rs create mode 100644 bitacross-worker/core-primitives/sgx/temp-dir/src/test.rs create mode 100644 bitacross-worker/core-primitives/stf-executor/Cargo.toml create mode 100644 bitacross-worker/core-primitives/stf-executor/src/enclave_signer.rs create mode 100644 bitacross-worker/core-primitives/stf-executor/src/error.rs create mode 100644 bitacross-worker/core-primitives/stf-executor/src/executor.rs create mode 100644 bitacross-worker/core-primitives/stf-executor/src/executor_tests.rs create mode 100644 bitacross-worker/core-primitives/stf-executor/src/getter_executor.rs create mode 100644 bitacross-worker/core-primitives/stf-executor/src/lib.rs create mode 100644 bitacross-worker/core-primitives/stf-executor/src/mocks.rs create mode 100644 bitacross-worker/core-primitives/stf-executor/src/state_getter.rs create mode 100644 bitacross-worker/core-primitives/stf-executor/src/traits.rs create mode 100644 bitacross-worker/core-primitives/stf-interface/Cargo.toml create mode 100644 bitacross-worker/core-primitives/stf-interface/src/lib.rs create mode 100644 bitacross-worker/core-primitives/stf-interface/src/mocks.rs create mode 100644 bitacross-worker/core-primitives/stf-interface/src/parentchain_pallet.rs create mode 100644 bitacross-worker/core-primitives/stf-interface/src/runtime_upgrade.rs create mode 100644 bitacross-worker/core-primitives/stf-interface/src/sudo_pallet.rs create mode 100644 bitacross-worker/core-primitives/stf-interface/src/system_pallet.rs create mode 100644 bitacross-worker/core-primitives/stf-primitives/Cargo.toml create mode 100644 bitacross-worker/core-primitives/stf-primitives/src/error.rs create mode 100644 bitacross-worker/core-primitives/stf-primitives/src/lib.rs create mode 100644 bitacross-worker/core-primitives/stf-primitives/src/traits.rs create mode 100644 bitacross-worker/core-primitives/stf-primitives/src/types.rs create mode 100644 bitacross-worker/core-primitives/stf-state-handler/Cargo.toml create mode 100644 bitacross-worker/core-primitives/stf-state-handler/src/error.rs create mode 100644 bitacross-worker/core-primitives/stf-state-handler/src/file_io.rs create mode 100644 bitacross-worker/core-primitives/stf-state-handler/src/handle_state.rs create mode 100644 bitacross-worker/core-primitives/stf-state-handler/src/in_memory_state_file_io.rs create mode 100644 bitacross-worker/core-primitives/stf-state-handler/src/lib.rs create mode 100644 bitacross-worker/core-primitives/stf-state-handler/src/query_shard_state.rs create mode 100644 bitacross-worker/core-primitives/stf-state-handler/src/state_handler.rs create mode 100644 bitacross-worker/core-primitives/stf-state-handler/src/state_initializer.rs create mode 100644 bitacross-worker/core-primitives/stf-state-handler/src/state_snapshot_primitives.rs create mode 100644 bitacross-worker/core-primitives/stf-state-handler/src/state_snapshot_repository.rs create mode 100644 bitacross-worker/core-primitives/stf-state-handler/src/state_snapshot_repository_loader.rs create mode 100644 bitacross-worker/core-primitives/stf-state-handler/src/test/mocks/initialize_state_mock.rs create mode 100644 bitacross-worker/core-primitives/stf-state-handler/src/test/mocks/mod.rs create mode 100644 bitacross-worker/core-primitives/stf-state-handler/src/test/mocks/state_key_repository_mock.rs create mode 100644 bitacross-worker/core-primitives/stf-state-handler/src/test/mocks/versioned_state_access_mock.rs create mode 100644 bitacross-worker/core-primitives/stf-state-handler/src/test/mod.rs create mode 100644 bitacross-worker/core-primitives/stf-state-handler/src/test/sgx_tests.rs create mode 100644 bitacross-worker/core-primitives/stf-state-observer/Cargo.toml create mode 100644 bitacross-worker/core-primitives/stf-state-observer/src/error.rs create mode 100644 bitacross-worker/core-primitives/stf-state-observer/src/lib.rs create mode 100644 bitacross-worker/core-primitives/stf-state-observer/src/mock.rs create mode 100644 bitacross-worker/core-primitives/stf-state-observer/src/state_observer.rs create mode 100644 bitacross-worker/core-primitives/stf-state-observer/src/traits.rs create mode 100644 bitacross-worker/core-primitives/storage/Cargo.toml create mode 100644 bitacross-worker/core-primitives/storage/src/error.rs create mode 100644 bitacross-worker/core-primitives/storage/src/keys.rs create mode 100644 bitacross-worker/core-primitives/storage/src/lib.rs create mode 100644 bitacross-worker/core-primitives/storage/src/proof.rs create mode 100644 bitacross-worker/core-primitives/storage/src/verify_storage_proof.rs create mode 100644 bitacross-worker/core-primitives/substrate-sgx/environmental/Cargo.toml create mode 100644 bitacross-worker/core-primitives/substrate-sgx/environmental/src/lib.rs create mode 100644 bitacross-worker/core-primitives/substrate-sgx/externalities/Cargo.toml create mode 100644 bitacross-worker/core-primitives/substrate-sgx/externalities/src/bypass.rs create mode 100644 bitacross-worker/core-primitives/substrate-sgx/externalities/src/codec_impl.rs create mode 100644 bitacross-worker/core-primitives/substrate-sgx/externalities/src/lib.rs create mode 100644 bitacross-worker/core-primitives/substrate-sgx/externalities/src/scope_limited.rs create mode 100644 bitacross-worker/core-primitives/substrate-sgx/externalities/src/vectorize.rs create mode 100644 bitacross-worker/core-primitives/substrate-sgx/sp-io/Cargo.toml create mode 100644 bitacross-worker/core-primitives/substrate-sgx/sp-io/src/lib.rs create mode 100644 bitacross-worker/core-primitives/teerex-storage/Cargo.toml create mode 100644 bitacross-worker/core-primitives/teerex-storage/src/lib.rs create mode 100644 bitacross-worker/core-primitives/test/Cargo.toml create mode 100644 bitacross-worker/core-primitives/test/src/builders/enclave_gen_builder.rs create mode 100644 bitacross-worker/core-primitives/test/src/builders/mod.rs create mode 100644 bitacross-worker/core-primitives/test/src/lib.rs create mode 100644 bitacross-worker/core-primitives/test/src/mock/handle_state_mock.rs create mode 100644 bitacross-worker/core-primitives/test/src/mock/metrics_ocall_mock.rs create mode 100644 bitacross-worker/core-primitives/test/src/mock/mod.rs create mode 100644 bitacross-worker/core-primitives/test/src/mock/onchain_mock.rs create mode 100644 bitacross-worker/core-primitives/test/src/mock/shielding_crypto_mock.rs create mode 100644 bitacross-worker/core-primitives/test/src/mock/sidechain_ocall_api_mock.rs create mode 100644 bitacross-worker/core-primitives/test/src/mock/stf_mock.rs create mode 100644 bitacross-worker/core-primitives/time-utils/Cargo.toml create mode 100644 bitacross-worker/core-primitives/time-utils/src/lib.rs create mode 100644 bitacross-worker/core-primitives/top-pool-author/Cargo.toml create mode 100644 bitacross-worker/core-primitives/top-pool-author/src/api.rs create mode 100644 bitacross-worker/core-primitives/top-pool-author/src/author.rs create mode 100644 bitacross-worker/core-primitives/top-pool-author/src/author_tests.rs create mode 100644 bitacross-worker/core-primitives/top-pool-author/src/client_error.rs create mode 100644 bitacross-worker/core-primitives/top-pool-author/src/error.rs create mode 100644 bitacross-worker/core-primitives/top-pool-author/src/lib.rs create mode 100644 bitacross-worker/core-primitives/top-pool-author/src/mocks.rs create mode 100644 bitacross-worker/core-primitives/top-pool-author/src/test_fixtures.rs create mode 100644 bitacross-worker/core-primitives/top-pool-author/src/test_utils.rs create mode 100644 bitacross-worker/core-primitives/top-pool-author/src/top_filter.rs create mode 100644 bitacross-worker/core-primitives/top-pool-author/src/traits.rs create mode 100644 bitacross-worker/core-primitives/top-pool/Cargo.toml create mode 100644 bitacross-worker/core-primitives/top-pool/src/base_pool.rs create mode 100644 bitacross-worker/core-primitives/top-pool/src/basic_pool.rs create mode 100644 bitacross-worker/core-primitives/top-pool/src/error.rs create mode 100644 bitacross-worker/core-primitives/top-pool/src/future.rs create mode 100644 bitacross-worker/core-primitives/top-pool/src/lib.rs create mode 100644 bitacross-worker/core-primitives/top-pool/src/listener.rs create mode 100644 bitacross-worker/core-primitives/top-pool/src/mocks/mod.rs create mode 100644 bitacross-worker/core-primitives/top-pool/src/mocks/rpc_responder_mock.rs create mode 100644 bitacross-worker/core-primitives/top-pool/src/mocks/trusted_operation_pool_mock.rs create mode 100644 bitacross-worker/core-primitives/top-pool/src/pool.rs create mode 100644 bitacross-worker/core-primitives/top-pool/src/primitives.rs create mode 100644 bitacross-worker/core-primitives/top-pool/src/ready.rs create mode 100644 bitacross-worker/core-primitives/top-pool/src/rotator.rs create mode 100644 bitacross-worker/core-primitives/top-pool/src/tracked_map.rs create mode 100644 bitacross-worker/core-primitives/top-pool/src/validated_pool.rs create mode 100644 bitacross-worker/core-primitives/top-pool/src/watcher.rs create mode 100644 bitacross-worker/core-primitives/types/Cargo.toml create mode 100644 bitacross-worker/core-primitives/types/src/lib.rs create mode 100644 bitacross-worker/core-primitives/types/src/parentchain.rs create mode 100644 bitacross-worker/core-primitives/types/src/storage.rs create mode 100644 bitacross-worker/core-primitives/utils/Cargo.toml create mode 100644 bitacross-worker/core-primitives/utils/src/buffer.rs create mode 100644 bitacross-worker/core-primitives/utils/src/error.rs create mode 100644 bitacross-worker/core-primitives/utils/src/hex.rs create mode 100644 bitacross-worker/core-primitives/utils/src/hex_display.rs create mode 100644 bitacross-worker/core-primitives/utils/src/lib.rs create mode 100644 bitacross-worker/core-primitives/utils/src/macros.rs create mode 100644 bitacross-worker/core-primitives/utils/src/stringify.rs create mode 100644 bitacross-worker/core/direct-rpc-client/Cargo.toml create mode 100644 bitacross-worker/core/direct-rpc-client/src/lib.rs create mode 100644 bitacross-worker/core/direct-rpc-server/Cargo.toml create mode 100644 bitacross-worker/core/direct-rpc-server/src/builders/mod.rs create mode 100644 bitacross-worker/core/direct-rpc-server/src/builders/rpc_response_builder.rs create mode 100644 bitacross-worker/core/direct-rpc-server/src/builders/rpc_return_value_builder.rs create mode 100644 bitacross-worker/core/direct-rpc-server/src/lib.rs create mode 100644 bitacross-worker/core/direct-rpc-server/src/mocks/determine_watch_mock.rs create mode 100644 bitacross-worker/core/direct-rpc-server/src/mocks/mod.rs create mode 100644 bitacross-worker/core/direct-rpc-server/src/mocks/response_channel_mock.rs create mode 100644 bitacross-worker/core/direct-rpc-server/src/mocks/send_rpc_response_mock.rs create mode 100644 bitacross-worker/core/direct-rpc-server/src/response_channel.rs create mode 100644 bitacross-worker/core/direct-rpc-server/src/rpc_connection_registry.rs create mode 100644 bitacross-worker/core/direct-rpc-server/src/rpc_responder.rs create mode 100644 bitacross-worker/core/direct-rpc-server/src/rpc_watch_extractor.rs create mode 100644 bitacross-worker/core/direct-rpc-server/src/rpc_ws_handler.rs create mode 100644 bitacross-worker/core/offchain-worker-executor/Cargo.toml create mode 100644 bitacross-worker/core/offchain-worker-executor/src/error.rs create mode 100644 bitacross-worker/core/offchain-worker-executor/src/executor.rs create mode 100644 bitacross-worker/core/offchain-worker-executor/src/lib.rs create mode 100644 bitacross-worker/core/parentchain/block-import-dispatcher/Cargo.toml create mode 100644 bitacross-worker/core/parentchain/block-import-dispatcher/src/error.rs create mode 100644 bitacross-worker/core/parentchain/block-import-dispatcher/src/immediate_dispatcher.rs create mode 100644 bitacross-worker/core/parentchain/block-import-dispatcher/src/lib.rs create mode 100644 bitacross-worker/core/parentchain/block-import-dispatcher/src/trigger_parentchain_block_import_mock.rs create mode 100644 bitacross-worker/core/parentchain/block-import-dispatcher/src/triggered_dispatcher.rs create mode 100644 bitacross-worker/core/parentchain/block-importer/Cargo.toml create mode 100644 bitacross-worker/core/parentchain/block-importer/src/block_importer.rs create mode 100644 bitacross-worker/core/parentchain/block-importer/src/block_importer_mock.rs create mode 100644 bitacross-worker/core/parentchain/block-importer/src/error.rs create mode 100644 bitacross-worker/core/parentchain/block-importer/src/lib.rs create mode 100644 bitacross-worker/core/parentchain/indirect-calls-executor/Cargo.toml create mode 100644 bitacross-worker/core/parentchain/indirect-calls-executor/src/error.rs create mode 100644 bitacross-worker/core/parentchain/indirect-calls-executor/src/event_filter.rs create mode 100644 bitacross-worker/core/parentchain/indirect-calls-executor/src/executor.rs create mode 100644 bitacross-worker/core/parentchain/indirect-calls-executor/src/filter_metadata.rs create mode 100644 bitacross-worker/core/parentchain/indirect-calls-executor/src/lib.rs create mode 100644 bitacross-worker/core/parentchain/indirect-calls-executor/src/mock.rs create mode 100644 bitacross-worker/core/parentchain/indirect-calls-executor/src/traits.rs create mode 100644 bitacross-worker/core/parentchain/light-client/Cargo.toml create mode 100644 bitacross-worker/core/parentchain/light-client/src/concurrent_access.rs create mode 100644 bitacross-worker/core/parentchain/light-client/src/error.rs create mode 100644 bitacross-worker/core/parentchain/light-client/src/finality.rs create mode 100644 bitacross-worker/core/parentchain/light-client/src/io.rs create mode 100644 bitacross-worker/core/parentchain/light-client/src/justification.rs create mode 100644 bitacross-worker/core/parentchain/light-client/src/lib.rs create mode 100644 bitacross-worker/core/parentchain/light-client/src/light_client_init_params.rs create mode 100644 bitacross-worker/core/parentchain/light-client/src/light_validation.rs create mode 100644 bitacross-worker/core/parentchain/light-client/src/light_validation_state.rs create mode 100644 bitacross-worker/core/parentchain/light-client/src/mocks/mod.rs create mode 100644 bitacross-worker/core/parentchain/light-client/src/mocks/validator_access_mock.rs create mode 100644 bitacross-worker/core/parentchain/light-client/src/mocks/validator_mock.rs create mode 100644 bitacross-worker/core/parentchain/light-client/src/mocks/validator_mock_seal.rs create mode 100644 bitacross-worker/core/parentchain/light-client/src/state.rs create mode 100644 bitacross-worker/core/parentchain/parentchain-crate/Cargo.toml create mode 100644 bitacross-worker/core/parentchain/parentchain-crate/src/lib.rs create mode 100644 bitacross-worker/core/parentchain/parentchain-crate/src/primitives.rs create mode 100644 bitacross-worker/core/parentchain/test/Cargo.toml create mode 100644 bitacross-worker/core/parentchain/test/src/lib.rs create mode 100644 bitacross-worker/core/parentchain/test/src/parentchain_block_builder.rs create mode 100644 bitacross-worker/core/parentchain/test/src/parentchain_header_builder.rs create mode 100644 bitacross-worker/core/peer-top-broadcaster/Cargo.toml create mode 100644 bitacross-worker/core/peer-top-broadcaster/src/lib.rs create mode 100644 bitacross-worker/core/rest-client/Cargo.toml create mode 100644 bitacross-worker/core/rest-client/src/error.rs create mode 100644 bitacross-worker/core/rest-client/src/fixtures/amazon_root_ca_1_v3.pem create mode 100644 bitacross-worker/core/rest-client/src/fixtures/baltimore_cyber_trust_root_v3.pem create mode 100644 bitacross-worker/core/rest-client/src/fixtures/lets_encrypt_root_cert.pem create mode 100644 bitacross-worker/core/rest-client/src/http_client.rs create mode 100644 bitacross-worker/core/rest-client/src/http_client_builder.rs create mode 100644 bitacross-worker/core/rest-client/src/lib.rs create mode 100644 bitacross-worker/core/rest-client/src/mocks/http_client_mock.rs create mode 100644 bitacross-worker/core/rest-client/src/mocks/mod.rs create mode 100644 bitacross-worker/core/rest-client/src/rest_client.rs create mode 100644 bitacross-worker/core/rpc-client/Cargo.toml create mode 100644 bitacross-worker/core/rpc-client/src/direct_client.rs create mode 100644 bitacross-worker/core/rpc-client/src/error.rs create mode 100644 bitacross-worker/core/rpc-client/src/lib.rs create mode 100644 bitacross-worker/core/rpc-client/src/mock.rs create mode 100644 bitacross-worker/core/rpc-client/src/ws_client.rs create mode 100644 bitacross-worker/core/rpc-server/Cargo.toml create mode 100644 bitacross-worker/core/rpc-server/src/lib.rs create mode 100644 bitacross-worker/core/rpc-server/src/mock.rs create mode 100644 bitacross-worker/core/rpc-server/src/tests.rs create mode 100644 bitacross-worker/core/tls-websocket-server/Cargo.toml create mode 100644 bitacross-worker/core/tls-websocket-server/src/certificate_generation.rs create mode 100644 bitacross-worker/core/tls-websocket-server/src/config_provider.rs create mode 100644 bitacross-worker/core/tls-websocket-server/src/connection.rs create mode 100644 bitacross-worker/core/tls-websocket-server/src/connection_id_generator.rs create mode 100644 bitacross-worker/core/tls-websocket-server/src/error.rs create mode 100644 bitacross-worker/core/tls-websocket-server/src/lib.rs create mode 100644 bitacross-worker/core/tls-websocket-server/src/stream_state.rs create mode 100644 bitacross-worker/core/tls-websocket-server/src/test/fixtures/mod.rs create mode 100644 bitacross-worker/core/tls-websocket-server/src/test/fixtures/no_cert_verifier.rs create mode 100644 bitacross-worker/core/tls-websocket-server/src/test/fixtures/test_cert.rs create mode 100644 bitacross-worker/core/tls-websocket-server/src/test/fixtures/test_private_key.rs create mode 100644 bitacross-worker/core/tls-websocket-server/src/test/fixtures/test_server.rs create mode 100644 bitacross-worker/core/tls-websocket-server/src/test/fixtures/test_server_config_provider.rs create mode 100644 bitacross-worker/core/tls-websocket-server/src/test/mocks/mod.rs create mode 100644 bitacross-worker/core/tls-websocket-server/src/test/mocks/web_socket_connection_mock.rs create mode 100644 bitacross-worker/core/tls-websocket-server/src/test/mocks/web_socket_handler_mock.rs create mode 100644 bitacross-worker/core/tls-websocket-server/src/test/mod.rs create mode 100644 bitacross-worker/core/tls-websocket-server/src/tls_common.rs create mode 100644 bitacross-worker/core/tls-websocket-server/src/ws_server.rs create mode 100644 bitacross-worker/docker/README.md create mode 100644 bitacross-worker/docker/demo-direct-call.yml create mode 100644 bitacross-worker/docker/demo-shielding-unshielding-multiworker.yml create mode 100644 bitacross-worker/docker/demo-sidechain.yml create mode 100644 bitacross-worker/docker/demo-smart-contract.yml create mode 100644 bitacross-worker/docker/demo-teeracle-generic.yml create mode 100644 bitacross-worker/docker/demo-teeracle.yml create mode 100644 bitacross-worker/docker/docker-compose.yml create mode 100755 bitacross-worker/docker/entrypoint.sh create mode 100644 bitacross-worker/docker/fork-inducer.yml create mode 100644 bitacross-worker/docker/fork.Dockerfile create mode 100644 bitacross-worker/docker/lit-ii-batch-test.yml create mode 100644 bitacross-worker/docker/lit-parentchain-nonce.yml create mode 100644 bitacross-worker/docker/lit-set-heartbeat-timeout.yml create mode 100644 bitacross-worker/docker/litentry-parachain.build.yml create mode 100644 bitacross-worker/docker/litentry/docker-compose.yml create mode 100644 bitacross-worker/docker/litentry/parachain-2106.Dockerfile create mode 100644 bitacross-worker/docker/litentry/relaychain.Dockerfile create mode 100644 bitacross-worker/docker/litentry/rococo-dev-2106.json create mode 100644 bitacross-worker/docker/litentry/rococo-local.json create mode 100644 bitacross-worker/docker/multiworker-docker-compose.yml create mode 100644 bitacross-worker/docker/ping.Dockerfile create mode 100644 bitacross-worker/docker/sidechain-benchmark.yml create mode 100644 bitacross-worker/docs/README.md create mode 100644 bitacross-worker/docs/diagramms/block_import_sequence.svg create mode 100644 bitacross-worker/enclave-runtime/Cargo.lock create mode 100644 bitacross-worker/enclave-runtime/Cargo.toml create mode 100644 bitacross-worker/enclave-runtime/Enclave.config.production.xml create mode 100644 bitacross-worker/enclave-runtime/Enclave.config.xml create mode 100644 bitacross-worker/enclave-runtime/Enclave.edl create mode 100644 bitacross-worker/enclave-runtime/Enclave.lds create mode 100644 bitacross-worker/enclave-runtime/Enclave_private.pem create mode 100644 bitacross-worker/enclave-runtime/Makefile create mode 100644 bitacross-worker/enclave-runtime/README.md create mode 100644 bitacross-worker/enclave-runtime/rust-toolchain.toml create mode 100644 bitacross-worker/enclave-runtime/rustfmt.toml create mode 100644 bitacross-worker/enclave-runtime/src/attestation.rs create mode 100644 bitacross-worker/enclave-runtime/src/empty_impls.rs create mode 100644 bitacross-worker/enclave-runtime/src/error.rs create mode 100644 bitacross-worker/enclave-runtime/src/initialization/global_components.rs create mode 100644 bitacross-worker/enclave-runtime/src/initialization/mod.rs create mode 100644 bitacross-worker/enclave-runtime/src/initialization/parentchain/common.rs create mode 100644 bitacross-worker/enclave-runtime/src/initialization/parentchain/integritee_parachain.rs create mode 100644 bitacross-worker/enclave-runtime/src/initialization/parentchain/integritee_solochain.rs create mode 100644 bitacross-worker/enclave-runtime/src/initialization/parentchain/mod.rs create mode 100644 bitacross-worker/enclave-runtime/src/initialization/parentchain/target_a_parachain.rs create mode 100644 bitacross-worker/enclave-runtime/src/initialization/parentchain/target_a_solochain.rs create mode 100644 bitacross-worker/enclave-runtime/src/initialization/parentchain/target_b_parachain.rs create mode 100644 bitacross-worker/enclave-runtime/src/initialization/parentchain/target_b_solochain.rs create mode 100644 bitacross-worker/enclave-runtime/src/ipfs.rs create mode 100644 bitacross-worker/enclave-runtime/src/lib.rs create mode 100644 bitacross-worker/enclave-runtime/src/ocall/attestation_ocall.rs create mode 100644 bitacross-worker/enclave-runtime/src/ocall/ffi.rs create mode 100644 bitacross-worker/enclave-runtime/src/ocall/ipfs_ocall.rs create mode 100644 bitacross-worker/enclave-runtime/src/ocall/metrics_ocall.rs create mode 100644 bitacross-worker/enclave-runtime/src/ocall/mod.rs create mode 100644 bitacross-worker/enclave-runtime/src/ocall/on_chain_ocall.rs create mode 100644 bitacross-worker/enclave-runtime/src/ocall/sidechain_ocall.rs create mode 100644 bitacross-worker/enclave-runtime/src/rpc/mod.rs create mode 100644 bitacross-worker/enclave-runtime/src/rpc/rpc_response_channel.rs create mode 100644 bitacross-worker/enclave-runtime/src/rpc/worker_api_direct.rs create mode 100644 bitacross-worker/enclave-runtime/src/shard_vault.rs create mode 100644 bitacross-worker/enclave-runtime/src/stf_task_handler.rs create mode 100644 bitacross-worker/enclave-runtime/src/sync.rs create mode 100644 bitacross-worker/enclave-runtime/src/teeracle/mod.rs create mode 100644 bitacross-worker/enclave-runtime/src/test/Counter.sol create mode 100644 bitacross-worker/enclave-runtime/src/test/cert_tests.rs create mode 100644 bitacross-worker/enclave-runtime/src/test/direct_rpc_tests.rs create mode 100644 bitacross-worker/enclave-runtime/src/test/enclave_signer_tests.rs create mode 100644 bitacross-worker/enclave-runtime/src/test/evm_pallet_tests.rs create mode 100644 bitacross-worker/enclave-runtime/src/test/fixtures/components.rs create mode 100644 bitacross-worker/enclave-runtime/src/test/fixtures/initialize_test_state.rs create mode 100644 bitacross-worker/enclave-runtime/src/test/fixtures/mod.rs create mode 100644 bitacross-worker/enclave-runtime/src/test/fixtures/ra_dump_cert_TEST4.der create mode 100644 bitacross-worker/enclave-runtime/src/test/fixtures/test_ra_signer_attn_MRSIGNER1_MRENCLAVE1.bin create mode 100644 bitacross-worker/enclave-runtime/src/test/fixtures/test_setup.rs create mode 100644 bitacross-worker/enclave-runtime/src/test/ipfs_tests.rs create mode 100644 bitacross-worker/enclave-runtime/src/test/mocks/attestation_ocall_mock.rs create mode 100644 bitacross-worker/enclave-runtime/src/test/mocks/enclave_rpc_ocall_mock.rs create mode 100644 bitacross-worker/enclave-runtime/src/test/mocks/mod.rs create mode 100644 bitacross-worker/enclave-runtime/src/test/mocks/peer_updater_mock.rs create mode 100644 bitacross-worker/enclave-runtime/src/test/mocks/propose_to_import_call_mock.rs create mode 100644 bitacross-worker/enclave-runtime/src/test/mocks/rpc_responder_mock.rs create mode 100644 bitacross-worker/enclave-runtime/src/test/mocks/types.rs create mode 100644 bitacross-worker/enclave-runtime/src/test/mod.rs create mode 100644 bitacross-worker/enclave-runtime/src/test/sidechain_aura_tests.rs create mode 100644 bitacross-worker/enclave-runtime/src/test/sidechain_event_tests.rs create mode 100644 bitacross-worker/enclave-runtime/src/test/state_getter_tests.rs create mode 100644 bitacross-worker/enclave-runtime/src/test/teeracle_tests.rs create mode 100644 bitacross-worker/enclave-runtime/src/test/tests_main.rs create mode 100644 bitacross-worker/enclave-runtime/src/test/top_pool_tests.rs create mode 100644 bitacross-worker/enclave-runtime/src/tls_ra/README.md create mode 100644 bitacross-worker/enclave-runtime/src/tls_ra/authentication.rs create mode 100644 bitacross-worker/enclave-runtime/src/tls_ra/mocks.rs create mode 100644 bitacross-worker/enclave-runtime/src/tls_ra/mod.rs create mode 100644 bitacross-worker/enclave-runtime/src/tls_ra/seal_handler.rs create mode 100644 bitacross-worker/enclave-runtime/src/tls_ra/tests.rs create mode 100644 bitacross-worker/enclave-runtime/src/tls_ra/tls_ra_client.rs create mode 100644 bitacross-worker/enclave-runtime/src/tls_ra/tls_ra_server.rs create mode 100644 bitacross-worker/enclave-runtime/src/top_pool_execution.rs create mode 100644 bitacross-worker/enclave-runtime/src/utils.rs create mode 100644 bitacross-worker/enclave-runtime/src/vc_issuance_task.rs create mode 100644 bitacross-worker/enclave-runtime/x86_64-unknown-linux-sgx.json create mode 100755 bitacross-worker/extract_identity create mode 100644 bitacross-worker/lib/readme.txt create mode 100644 bitacross-worker/license_header_scs.txt create mode 100644 bitacross-worker/litentry/core/scheduled-enclave/Cargo.toml create mode 100644 bitacross-worker/litentry/core/scheduled-enclave/src/error.rs create mode 100644 bitacross-worker/litentry/core/scheduled-enclave/src/io.rs create mode 100644 bitacross-worker/litentry/core/scheduled-enclave/src/lib.rs create mode 100644 bitacross-worker/litentry/macros/Cargo.toml create mode 100644 bitacross-worker/litentry/macros/src/lib.rs create mode 100644 bitacross-worker/litentry/primitives/Cargo.toml create mode 100644 bitacross-worker/litentry/primitives/src/aes.rs create mode 100644 bitacross-worker/litentry/primitives/src/aes_request.rs create mode 100644 bitacross-worker/litentry/primitives/src/bitcoin_address.rs create mode 100644 bitacross-worker/litentry/primitives/src/bitcoin_signature.rs create mode 100644 bitacross-worker/litentry/primitives/src/ethereum_signature.rs create mode 100644 bitacross-worker/litentry/primitives/src/lib.rs create mode 100644 bitacross-worker/litentry/primitives/src/validation_data.rs create mode 100644 bitacross-worker/local-setup/.env.example create mode 100644 bitacross-worker/local-setup/README.md create mode 100644 bitacross-worker/local-setup/__init__.py create mode 100644 bitacross-worker/local-setup/config/benchmark.json create mode 100644 bitacross-worker/local-setup/config/one-worker.json create mode 100644 bitacross-worker/local-setup/config/three-nodes-one-worker.json create mode 100644 bitacross-worker/local-setup/config/three-workers.json create mode 100644 bitacross-worker/local-setup/config/two-workers.json create mode 100644 bitacross-worker/local-setup/development-worker.json create mode 100755 bitacross-worker/local-setup/launch.py create mode 100644 bitacross-worker/local-setup/py/__init__.py create mode 100644 bitacross-worker/local-setup/py/helpers.py create mode 100644 bitacross-worker/local-setup/py/worker.py create mode 100644 bitacross-worker/local-setup/rococo_one_worker.json create mode 100755 bitacross-worker/local-setup/tmux_logger.sh create mode 100755 bitacross-worker/local-setup/tmux_logger_three_nodes.sh create mode 100644 bitacross-worker/local-setup/worker-log-level-config.toml create mode 100644 bitacross-worker/rust-sgx-sdk/Readme.md create mode 100644 bitacross-worker/rust-sgx-sdk/buildenv.mk create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/assert.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/complex.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/ctype.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/dirent.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/endian.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/errno.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/fenv.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/float.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/inttypes.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/iso646.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/limits.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/math.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/mbusafecrt.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/netdb.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/poll.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/pthread.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/pwd.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/sched.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/setjmp.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/signal.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/stdalign.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/stdarg.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/stdbool.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/stddef.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/stdint.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/stdio.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/stdlib.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/string.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/sys/_types.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/sys/cdefs.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/sys/endian.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/sys/epoll.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/sys/fpu.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/sys/ieee.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/sys/limits.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/sys/sockaddr.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/sys/socket.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/sys/stat.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/sys/stdint.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/sys/struct_timespec.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/sys/types.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/sys/uio.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/time.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/unistd.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/wchar.h create mode 100644 bitacross-worker/rust-sgx-sdk/common/inc/wctype.h create mode 100644 bitacross-worker/rust-sgx-sdk/edl/inc/dirent.h create mode 100644 bitacross-worker/rust-sgx-sdk/edl/inc/stat.h create mode 100644 bitacross-worker/rust-sgx-sdk/edl/intel/sgx_dcap_tvl.edl create mode 100644 bitacross-worker/rust-sgx-sdk/edl/intel/sgx_pthread.edl create mode 100644 bitacross-worker/rust-sgx-sdk/edl/intel/sgx_tkey_exchange.edl create mode 100644 bitacross-worker/rust-sgx-sdk/edl/intel/sgx_tprotected_fs.edl create mode 100644 bitacross-worker/rust-sgx-sdk/edl/intel/sgx_tstdc.edl create mode 100644 bitacross-worker/rust-sgx-sdk/edl/intel/sgx_tswitchless.edl create mode 100644 bitacross-worker/rust-sgx-sdk/edl/intel/sgx_ttls.edl create mode 100644 bitacross-worker/rust-sgx-sdk/edl/sgx_asyncio.edl create mode 100644 bitacross-worker/rust-sgx-sdk/edl/sgx_backtrace.edl create mode 100644 bitacross-worker/rust-sgx-sdk/edl/sgx_env.edl create mode 100644 bitacross-worker/rust-sgx-sdk/edl/sgx_fd.edl create mode 100644 bitacross-worker/rust-sgx-sdk/edl/sgx_file.edl create mode 100644 bitacross-worker/rust-sgx-sdk/edl/sgx_fs.edl create mode 100644 bitacross-worker/rust-sgx-sdk/edl/sgx_mem.edl create mode 100644 bitacross-worker/rust-sgx-sdk/edl/sgx_net.edl create mode 100644 bitacross-worker/rust-sgx-sdk/edl/sgx_net_switchless.edl create mode 100644 bitacross-worker/rust-sgx-sdk/edl/sgx_pipe.edl create mode 100644 bitacross-worker/rust-sgx-sdk/edl/sgx_process.edl create mode 100644 bitacross-worker/rust-sgx-sdk/edl/sgx_signal.edl create mode 100644 bitacross-worker/rust-sgx-sdk/edl/sgx_socket.edl create mode 100644 bitacross-worker/rust-sgx-sdk/edl/sgx_stdio.edl create mode 100644 bitacross-worker/rust-sgx-sdk/edl/sgx_sys.edl create mode 100644 bitacross-worker/rust-sgx-sdk/edl/sgx_thread.edl create mode 100644 bitacross-worker/rust-sgx-sdk/edl/sgx_time.edl create mode 100644 bitacross-worker/rust-sgx-sdk/edl/sgx_tstd.edl create mode 100644 bitacross-worker/rust-sgx-sdk/version create mode 100644 bitacross-worker/rust-toolchain.toml create mode 100644 bitacross-worker/rustfmt.toml create mode 100644 bitacross-worker/samples/teeracle/README.md create mode 100755 bitacross-worker/samples/teeracle/install-teeracle.sh create mode 100644 bitacross-worker/samples/teeracle/kubernetes/Chart.yaml create mode 100644 bitacross-worker/samples/teeracle/kubernetes/templates/teeracle.yaml create mode 100644 bitacross-worker/samples/teeracle/kubernetes/values.yaml create mode 100644 bitacross-worker/scripts/benchmark_local-setup.sh create mode 100644 bitacross-worker/scripts/changelog/.gitignore create mode 100644 bitacross-worker/scripts/changelog/Gemfile create mode 100644 bitacross-worker/scripts/changelog/Gemfile.lock create mode 100644 bitacross-worker/scripts/changelog/README.md create mode 100755 bitacross-worker/scripts/changelog/bin/changelog create mode 100644 bitacross-worker/scripts/changelog/digests/.gitignore create mode 100644 bitacross-worker/scripts/changelog/digests/.gitkeep create mode 100644 bitacross-worker/scripts/changelog/lib/changelog.rb create mode 100644 bitacross-worker/scripts/changelog/templates/_free_notes.md.tera create mode 100644 bitacross-worker/scripts/changelog/templates/challenge_level.md.tera create mode 100644 bitacross-worker/scripts/changelog/templates/change.md.tera create mode 100644 bitacross-worker/scripts/changelog/templates/changes.md.tera create mode 100644 bitacross-worker/scripts/changelog/templates/changes_applibs.md.tera create mode 100644 bitacross-worker/scripts/changelog/templates/changes_client.md.tera create mode 100644 bitacross-worker/scripts/changelog/templates/changes_core.md.tera create mode 100644 bitacross-worker/scripts/changelog/templates/changes_evm.md.tera create mode 100644 bitacross-worker/scripts/changelog/templates/changes_misc.md.tera create mode 100644 bitacross-worker/scripts/changelog/templates/changes_offchain.md.tera create mode 100644 bitacross-worker/scripts/changelog/templates/changes_sidechain.md.tera create mode 100644 bitacross-worker/scripts/changelog/templates/changes_teeracle.md.tera create mode 100644 bitacross-worker/scripts/changelog/templates/debug.md.tera create mode 100644 bitacross-worker/scripts/changelog/templates/global_challenge_level.md.tera create mode 100644 bitacross-worker/scripts/changelog/templates/global_priority.md.tera create mode 100644 bitacross-worker/scripts/changelog/templates/high_priority.md.tera create mode 100644 bitacross-worker/scripts/changelog/templates/pre_release.md.tera create mode 100644 bitacross-worker/scripts/changelog/templates/template.md.tera create mode 100755 bitacross-worker/scripts/init_env.sh create mode 100755 bitacross-worker/scripts/launch.sh create mode 100755 bitacross-worker/scripts/launch_local_worker.sh create mode 100755 bitacross-worker/scripts/litentry/cleanup.sh create mode 100755 bitacross-worker/scripts/litentry/generate_parachain_artefacts.sh create mode 100755 bitacross-worker/scripts/litentry/identity_test.sh create mode 100644 bitacross-worker/scripts/litentry/release/ReadMe.md create mode 100755 bitacross-worker/scripts/litentry/release/build.sh create mode 100644 bitacross-worker/scripts/litentry/release/config.json.eg create mode 100755 bitacross-worker/scripts/litentry/release/deploy.sh create mode 100755 bitacross-worker/scripts/litentry/release/prepare.sh create mode 100644 bitacross-worker/scripts/litentry/release/template/para-alice.service create mode 100644 bitacross-worker/scripts/litentry/release/template/relay-alice.service create mode 100644 bitacross-worker/scripts/litentry/release/template/relay-bob.service create mode 100644 bitacross-worker/scripts/litentry/release/template/worker.service create mode 100755 bitacross-worker/scripts/litentry/start_parachain.sh create mode 100755 bitacross-worker/scripts/litentry/stop_parachain.sh create mode 100755 bitacross-worker/scripts/litentry/ubuntu_setup.sh create mode 100755 bitacross-worker/scripts/m6.sh create mode 100755 bitacross-worker/scripts/m8.sh create mode 100755 bitacross-worker/scripts/polkadot_update.sh create mode 100755 bitacross-worker/scripts/sidechain.sh create mode 100644 bitacross-worker/scripts/teeracle.sh create mode 100644 bitacross-worker/scripts/test_transfer/README.md create mode 100644 bitacross-worker/scripts/test_transfer/package-lock.json create mode 100644 bitacross-worker/scripts/test_transfer/package.json create mode 100644 bitacross-worker/scripts/test_transfer/transfer.js create mode 100644 bitacross-worker/service/Cargo.toml create mode 100644 bitacross-worker/service/build.rs create mode 100644 bitacross-worker/service/src/account_funding.rs create mode 100644 bitacross-worker/service/src/cli.yml create mode 100644 bitacross-worker/service/src/config.rs create mode 100644 bitacross-worker/service/src/enclave/api.rs create mode 100644 bitacross-worker/service/src/enclave/mod.rs create mode 100644 bitacross-worker/service/src/enclave/tls_ra.rs create mode 100644 bitacross-worker/service/src/error.rs create mode 100644 bitacross-worker/service/src/globals/mod.rs create mode 100644 bitacross-worker/service/src/globals/tokio_handle.rs create mode 100644 bitacross-worker/service/src/initialized_service.rs create mode 100644 bitacross-worker/service/src/main.rs create mode 100644 bitacross-worker/service/src/main_impl.rs create mode 100644 bitacross-worker/service/src/ocall_bridge/bridge_api.rs create mode 100644 bitacross-worker/service/src/ocall_bridge/component_factory.rs create mode 100644 bitacross-worker/service/src/ocall_bridge/ffi/fetch_sidechain_blocks_from_peer.rs create mode 100644 bitacross-worker/service/src/ocall_bridge/ffi/get_ias_socket.rs create mode 100644 bitacross-worker/service/src/ocall_bridge/ffi/get_peers.rs create mode 100644 bitacross-worker/service/src/ocall_bridge/ffi/get_quote.rs create mode 100755 bitacross-worker/service/src/ocall_bridge/ffi/get_qve_report_on_quote.rs create mode 100644 bitacross-worker/service/src/ocall_bridge/ffi/get_update_info.rs create mode 100644 bitacross-worker/service/src/ocall_bridge/ffi/init_quote.rs create mode 100644 bitacross-worker/service/src/ocall_bridge/ffi/ipfs.rs create mode 100644 bitacross-worker/service/src/ocall_bridge/ffi/mod.rs create mode 100644 bitacross-worker/service/src/ocall_bridge/ffi/propose_sidechain_blocks.rs create mode 100644 bitacross-worker/service/src/ocall_bridge/ffi/send_to_parentchain.rs create mode 100644 bitacross-worker/service/src/ocall_bridge/ffi/store_sidechain_blocks.rs create mode 100644 bitacross-worker/service/src/ocall_bridge/ffi/update_metric.rs create mode 100644 bitacross-worker/service/src/ocall_bridge/ffi/worker_request.rs create mode 100644 bitacross-worker/service/src/ocall_bridge/ipfs_ocall.rs create mode 100644 bitacross-worker/service/src/ocall_bridge/metrics_ocall.rs create mode 100644 bitacross-worker/service/src/ocall_bridge/mod.rs create mode 100644 bitacross-worker/service/src/ocall_bridge/remote_attestation_ocall.rs create mode 100644 bitacross-worker/service/src/ocall_bridge/sidechain_ocall.rs create mode 100644 bitacross-worker/service/src/ocall_bridge/test/mocks/mod.rs create mode 100644 bitacross-worker/service/src/ocall_bridge/test/mocks/sidechain_bridge_mock.rs create mode 100644 bitacross-worker/service/src/ocall_bridge/test/mod.rs create mode 100644 bitacross-worker/service/src/ocall_bridge/worker_on_chain_ocall.rs create mode 100644 bitacross-worker/service/src/parentchain_handler.rs create mode 100644 bitacross-worker/service/src/prometheus_metrics.rs create mode 100644 bitacross-worker/service/src/setup.rs create mode 100644 bitacross-worker/service/src/sidechain_setup.rs create mode 100644 bitacross-worker/service/src/sync_block_broadcaster.rs create mode 100644 bitacross-worker/service/src/sync_state.rs create mode 100644 bitacross-worker/service/src/teeracle/mod.rs create mode 100644 bitacross-worker/service/src/teeracle/schedule_periodic.rs create mode 100644 bitacross-worker/service/src/teeracle/teeracle_metrics.rs create mode 100644 bitacross-worker/service/src/tests/commons.rs create mode 100644 bitacross-worker/service/src/tests/mock.rs create mode 100644 bitacross-worker/service/src/tests/mocks/broadcast_blocks_mock.rs create mode 100644 bitacross-worker/service/src/tests/mocks/direct_request_mock.rs create mode 100644 bitacross-worker/service/src/tests/mocks/enclave_api_mock.rs create mode 100644 bitacross-worker/service/src/tests/mocks/initialization_handler_mock.rs create mode 100644 bitacross-worker/service/src/tests/mocks/mod.rs create mode 100644 bitacross-worker/service/src/tests/mocks/parentchain_api_mock.rs create mode 100644 bitacross-worker/service/src/tests/mocks/update_worker_peers_mock.rs create mode 100644 bitacross-worker/service/src/tests/mod.rs create mode 100644 bitacross-worker/service/src/tests/parentchain_handler_test.rs create mode 100644 bitacross-worker/service/src/utils.rs create mode 100644 bitacross-worker/service/src/wasm.rs create mode 100644 bitacross-worker/service/src/worker.rs create mode 100644 bitacross-worker/service/src/worker_peers_registry.rs create mode 100644 bitacross-worker/sidechain/block-composer/Cargo.toml create mode 100644 bitacross-worker/sidechain/block-composer/src/block_composer.rs create mode 100644 bitacross-worker/sidechain/block-composer/src/error.rs create mode 100644 bitacross-worker/sidechain/block-composer/src/lib.rs create mode 100644 bitacross-worker/sidechain/block-verification/Cargo.toml create mode 100644 bitacross-worker/sidechain/block-verification/src/error.rs create mode 100644 bitacross-worker/sidechain/block-verification/src/lib.rs create mode 100644 bitacross-worker/sidechain/block-verification/src/slot.rs create mode 100644 bitacross-worker/sidechain/consensus/aura/Cargo.toml create mode 100644 bitacross-worker/sidechain/consensus/aura/src/block_importer.rs create mode 100644 bitacross-worker/sidechain/consensus/aura/src/lib.rs create mode 100644 bitacross-worker/sidechain/consensus/aura/src/proposer_factory.rs create mode 100644 bitacross-worker/sidechain/consensus/aura/src/slot_proposer.rs create mode 100644 bitacross-worker/sidechain/consensus/aura/src/test/block_importer_tests.rs create mode 100644 bitacross-worker/sidechain/consensus/aura/src/test/fixtures/mod.rs create mode 100644 bitacross-worker/sidechain/consensus/aura/src/test/fixtures/types.rs create mode 100644 bitacross-worker/sidechain/consensus/aura/src/test/mocks/environment_mock.rs create mode 100644 bitacross-worker/sidechain/consensus/aura/src/test/mocks/mod.rs create mode 100644 bitacross-worker/sidechain/consensus/aura/src/test/mocks/peer_updater_mock.rs create mode 100644 bitacross-worker/sidechain/consensus/aura/src/test/mocks/proposer_mock.rs create mode 100644 bitacross-worker/sidechain/consensus/aura/src/test/mod.rs create mode 100644 bitacross-worker/sidechain/consensus/aura/src/verifier.rs create mode 100644 bitacross-worker/sidechain/consensus/common/Cargo.toml create mode 100644 bitacross-worker/sidechain/consensus/common/src/block_import.rs create mode 100644 bitacross-worker/sidechain/consensus/common/src/block_import_confirmation_handler.rs create mode 100644 bitacross-worker/sidechain/consensus/common/src/block_import_queue_worker.rs create mode 100644 bitacross-worker/sidechain/consensus/common/src/block_production_suspension.rs create mode 100644 bitacross-worker/sidechain/consensus/common/src/error.rs create mode 100644 bitacross-worker/sidechain/consensus/common/src/header_db.rs create mode 100644 bitacross-worker/sidechain/consensus/common/src/is_descendant_of_builder.rs create mode 100644 bitacross-worker/sidechain/consensus/common/src/lib.rs create mode 100644 bitacross-worker/sidechain/consensus/common/src/peer_block_sync.rs create mode 100644 bitacross-worker/sidechain/consensus/common/src/test/mocks/block_import_queue_worker_mock.rs create mode 100644 bitacross-worker/sidechain/consensus/common/src/test/mocks/block_importer_mock.rs create mode 100644 bitacross-worker/sidechain/consensus/common/src/test/mocks/confirm_block_import_mock.rs create mode 100644 bitacross-worker/sidechain/consensus/common/src/test/mocks/mod.rs create mode 100644 bitacross-worker/sidechain/consensus/common/src/test/mocks/verifier_mock.rs create mode 100644 bitacross-worker/sidechain/consensus/common/src/test/mod.rs create mode 100644 bitacross-worker/sidechain/consensus/slots/Cargo.toml create mode 100644 bitacross-worker/sidechain/consensus/slots/src/lib.rs create mode 100644 bitacross-worker/sidechain/consensus/slots/src/mocks.rs create mode 100644 bitacross-worker/sidechain/consensus/slots/src/per_shard_slot_worker_tests.rs create mode 100644 bitacross-worker/sidechain/consensus/slots/src/slot_stream.rs create mode 100644 bitacross-worker/sidechain/consensus/slots/src/slots.rs create mode 100644 bitacross-worker/sidechain/fork-tree/Cargo.toml create mode 100644 bitacross-worker/sidechain/fork-tree/src/lib.rs create mode 100644 bitacross-worker/sidechain/peer-fetch/Cargo.toml create mode 100644 bitacross-worker/sidechain/peer-fetch/src/block_fetch_client.rs create mode 100644 bitacross-worker/sidechain/peer-fetch/src/block_fetch_server.rs create mode 100644 bitacross-worker/sidechain/peer-fetch/src/error.rs create mode 100644 bitacross-worker/sidechain/peer-fetch/src/lib.rs create mode 100644 bitacross-worker/sidechain/peer-fetch/src/mocks/fetch_blocks_from_peer_mock.rs create mode 100644 bitacross-worker/sidechain/peer-fetch/src/mocks/mod.rs create mode 100644 bitacross-worker/sidechain/peer-fetch/src/mocks/untrusted_peer_fetch_mock.rs create mode 100644 bitacross-worker/sidechain/peer-fetch/src/untrusted_peer_fetch.rs create mode 100644 bitacross-worker/sidechain/primitives/Cargo.toml create mode 100644 bitacross-worker/sidechain/primitives/src/lib.rs create mode 100644 bitacross-worker/sidechain/primitives/src/traits/mod.rs create mode 100644 bitacross-worker/sidechain/primitives/src/types/block.rs create mode 100644 bitacross-worker/sidechain/primitives/src/types/block_data.rs create mode 100644 bitacross-worker/sidechain/primitives/src/types/header.rs create mode 100644 bitacross-worker/sidechain/primitives/src/types/mod.rs create mode 100644 bitacross-worker/sidechain/rpc-handler/Cargo.toml create mode 100644 bitacross-worker/sidechain/rpc-handler/src/constants.rs create mode 100644 bitacross-worker/sidechain/rpc-handler/src/direct_top_pool_api.rs create mode 100644 bitacross-worker/sidechain/rpc-handler/src/import_block_api.rs create mode 100644 bitacross-worker/sidechain/rpc-handler/src/lib.rs create mode 100644 bitacross-worker/sidechain/sidechain-crate/Cargo.toml create mode 100644 bitacross-worker/sidechain/sidechain-crate/src/lib.rs create mode 100644 bitacross-worker/sidechain/state/Cargo.toml create mode 100644 bitacross-worker/sidechain/state/src/error.rs create mode 100644 bitacross-worker/sidechain/state/src/impls.rs create mode 100644 bitacross-worker/sidechain/state/src/lib.rs create mode 100644 bitacross-worker/sidechain/storage/Cargo.toml create mode 100644 bitacross-worker/sidechain/storage/src/db.rs create mode 100644 bitacross-worker/sidechain/storage/src/error.rs create mode 100644 bitacross-worker/sidechain/storage/src/fetch_blocks_mock.rs create mode 100644 bitacross-worker/sidechain/storage/src/interface.rs create mode 100644 bitacross-worker/sidechain/storage/src/lib.rs create mode 100644 bitacross-worker/sidechain/storage/src/storage.rs create mode 100644 bitacross-worker/sidechain/storage/src/storage_tests_get_blocks_after.rs create mode 100644 bitacross-worker/sidechain/storage/src/storage_tests_get_blocks_in_range.rs create mode 100644 bitacross-worker/sidechain/storage/src/test_utils.rs create mode 100644 bitacross-worker/sidechain/test/Cargo.toml create mode 100644 bitacross-worker/sidechain/test/src/lib.rs create mode 100644 bitacross-worker/sidechain/test/src/sidechain_block_builder.rs create mode 100644 bitacross-worker/sidechain/test/src/sidechain_block_data_builder.rs create mode 100644 bitacross-worker/sidechain/test/src/sidechain_header_builder.rs create mode 100644 bitacross-worker/sidechain/validateer-fetch/Cargo.toml create mode 100644 bitacross-worker/sidechain/validateer-fetch/src/error.rs create mode 100644 bitacross-worker/sidechain/validateer-fetch/src/lib.rs create mode 100644 bitacross-worker/sidechain/validateer-fetch/src/validateer.rs create mode 100644 bitacross-worker/ts-tests/.editorconfig create mode 100644 bitacross-worker/ts-tests/.gitignore create mode 100644 bitacross-worker/ts-tests/.prettierrc create mode 100644 bitacross-worker/ts-tests/README.md create mode 100644 bitacross-worker/ts-tests/package.json create mode 100644 bitacross-worker/ts-tests/pnpm-lock.yaml create mode 100644 bitacross-worker/ts-tests/pnpm-workspace.yaml create mode 100644 bitacross-worker/upstream_commit diff --git a/.github/file-filter.yml b/.github/file-filter.yml index b19f45c68b..66c6ba58a6 100644 --- a/.github/file-filter.yml +++ b/.github/file-filter.yml @@ -33,3 +33,17 @@ tee_test: &tee_test - 'tee-worker/cli/*.sh' - 'docker/**' - 'tee-worker/docker/*.yml' + +bitacross_src: &bitacross_src + - 'bitacross-worker/**/*.rs' + - 'bitacross-worker/**/Cargo.toml' + - 'bitacross-worker/**/Cargo.lock' + - 'bitacross-worker/**/rust-toolchain.toml' + - 'bitacross-worker/build.Dockerfile' + - 'bitacross-worker/enclave-runtime/**' + +bitacross_test: &bitacross_src + - 'bitacross-worker/ts-tests/**' + - 'bitacross-worker/cli/*.sh' + - 'docker/**' + - 'bitacross-worker/docker/*.yml' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e4a60199bc..37d6fb6c2e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -84,9 +84,11 @@ jobs: outputs: rebuild_parachain: ${{ steps.env.outputs.rebuild_parachain }} rebuild_tee: ${{ steps.env.outputs.rebuild_tee }} + rebuild_bitacross: ${{ steps.env.outputs.rebuild_bitacross }} push_docker: ${{ steps.env.outputs.push_docker }} run_parachain_test: ${{ steps.env.outputs.run_parachain_test }} run_tee_test: ${{ steps.env.outputs.run_tee_test }} + run_bitacross_test: ${{ steps.env.outputs.run_bitacross_test }} steps: - uses: actions/checkout@v4 with: @@ -105,6 +107,7 @@ jobs: run: | rebuild_parachain=false rebuild_tee=false + rebuild_bitacross=false push_docker=false run_parachain_test=false run_tee_test=false @@ -114,6 +117,9 @@ jobs: if [ "${{ github.event.inputs.rebuild-tee-docker }}" = "true" ] || [ "${{ steps.filter.outputs.tee_src }}" = "true" ]; then rebuild_tee=true fi + if [ "${{ github.event.inputs.rebuild-bitacross-docker }}" = "true" ] || [ "${{ steps.filter.outputs.bitacross_src }}" = "true" ]; then + rebuild_bitacross=true + fi if [ "${{ github.event.inputs.push-docker }}" = "true" ]; then push_docker=true elif [ "${{ github.event_name }}" = 'push' ] && [ "${{ github.ref }}" = 'refs/heads/dev' ]; then @@ -125,11 +131,16 @@ jobs: if [ "${{ steps.filter.outputs.tee_test }}" = "true" ] || [ "$rebuild_parachain" = "true" ] || [ "$rebuild_tee" = "true" ]; then run_tee_test=true fi + if [ "${{ steps.filter.outputs.bitacross_test }}" = "true" ] || [ "$rebuild_parachain" = "true" ] || [ "$rebuild_bitacross" = "true" ]; then + run_bitacross_test=true + fi echo "rebuild_parachain=$rebuild_parachain" | tee -a $GITHUB_OUTPUT echo "rebuild_tee=$rebuild_tee" | tee -a $GITHUB_OUTPUT + echo "rebuild_bitacross=$rebuild_bitacross" | tee -a $GITHUB_OUTPUT echo "push_docker=$push_docker" | tee -a $GITHUB_OUTPUT echo "run_parachain_test=$run_parachain_test" | tee -a $GITHUB_OUTPUT echo "run_tee_test=$run_tee_test" | tee -a $GITHUB_OUTPUT + echo "run_bitacross_test=$$run_tee_test" | tee -a $GITHUB_OUTPUT fmt: runs-on: ubuntu-latest @@ -159,11 +170,22 @@ jobs: cargo fmt --all -- --check taplo fmt --check - - name: Enclave-runtime fmt check + - name: Tee-worker enclave-runtime fmt check working-directory: ./tee-worker/enclave-runtime run: | cargo fmt --all -- --check + - name: bitacross-worker fmt check + working-directory: ./bitacross-worker + run: | + cargo fmt --all -- --check + taplo fmt --check + + - name: bitacross-worker enclave-runtime fmt check + working-directory: ./bitacross-worker/enclave-runtime + run: | + cargo fmt --all -- --check + - name: Enable corepack and pnpm run: corepack enable && corepack enable pnpm @@ -282,6 +304,66 @@ jobs: if: failure() uses: andymckay/cancel-action@0.3 + bitacross-clippy: + runs-on: ubuntu-latest + needs: + - fmt + - set-condition + - sequentialise + if: needs.set-condition.outputs.rebuild_bitacross == 'true' +# todo: we might want to change this image in the future + container: "litentry/litentry-tee-dev:latest" + steps: + - uses: actions/checkout@v4 + + - name: Install dependencies + run: | + sudo apt-get update && \ + sudo apt-get install -yq openssl clang libclang-dev cmake protobuf-compiler + + - name: bitacross-worker clippy + working-directory: ./bitacross-worker + run: | + echo "::group::cargo clippy all" + cargo clippy --release -- -D warnings + echo "::endgroup::" + echo "::group::cargo clippy sidechain" + cargo clippy --release --features sidechain -- -D warnings + echo "::endgroup::" + echo "::group::cargo clippy offchain-worker" + cargo clean --profile release + cargo clippy --release --features offchain-worker -- -D warnings + echo "::endgroup::" + + - name: Clean up disk + working-directory: ./bitacross-worker + run: | + echo "::group::Show disk usage" + df -h . + echo "::endgroup::" + cargo clean --profile release + echo "::group::Show disk usage" + df -h . + echo "::endgroup::" + + - name: bitacross-enclave clippy + working-directory: ./bitacross-worker/enclave-runtime + run: | + echo "::group::cargo clippy all" + cargo clippy --release -- -D warnings + echo "::endgroup::" + echo "::group::cargo clippy sidechain" + cargo clippy --release --features sidechain -- -D warnings + echo "::endgroup::" + echo "::group::cargo clippy offchain-worker" + cargo clean --profile release + cargo clippy --release --features offchain-worker -- -D warnings + echo "::endgroup::" + + - name: Fail early + if: failure() + uses: andymckay/cancel-action@0.3 + parachain-build-dev: runs-on: ubuntu-latest needs: @@ -378,7 +460,6 @@ jobs: echo "::group::Show disk usage" df -h . echo "::endgroup::" - # cache mount in buildkit won't be exported as image layers, so it doesn't work well with GHA cache, see # https://github.com/moby/buildkit/issues/1512 # https://www.reddit.com/r/rust/comments/126xeyx/exploring_the_problem_of_faster_cargo_docker/ @@ -468,7 +549,6 @@ jobs: name: litentry-tee path: litentry-tee.tar.gz if-no-files-found: error - - name: Fail early if: failure() uses: andymckay/cancel-action@0.3 diff --git a/Makefile b/Makefile index 2572c54712..2fa14b1e8a 100644 --- a/Makefile +++ b/Makefile @@ -195,6 +195,8 @@ fmt-cargo: @cargo fmt --all @cd tee-worker && cargo fmt --all @cd tee-worker/enclave-runtime && cargo fmt --all + @cd bitacross-worker && cargo fmt --all + @cd bitacross-worker/enclave-runtime && cargo fmt --all .PHONY: fmt-taplo ## taplo fmt fmt-taplo: diff --git a/bitacross-worker/.dockerignore b/bitacross-worker/.dockerignore new file mode 100644 index 0000000000..10a8164af1 --- /dev/null +++ b/bitacross-worker/.dockerignore @@ -0,0 +1,16 @@ +# Litentry note: this file is unused +# Please edit the ../.dockerignore directly +.git +.githooks +.github +.idea +ci/ +docker/*yml +docs/ +local-setup/ +scripts/ +target/ +enclave-runtime/target/ +tmp/ +*.Dockerfile +Dockerfile \ No newline at end of file diff --git a/bitacross-worker/.editorconfig b/bitacross-worker/.editorconfig new file mode 100644 index 0000000000..de2a30a350 --- /dev/null +++ b/bitacross-worker/.editorconfig @@ -0,0 +1,27 @@ +root = true + +[*] +indent_style = tab +indent_size = 4 +tab_width = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +max_line_length = 100 +insert_final_newline = true + +[*.yml] +indent_style = space +indent_size = 4 +tab_width = 4 +end_of_line = lf + +[*.ts] +indent_style = space +indent_size = 4 +tab_width = 4 +end_of_line = lf + + +[*.toml] +indent_style = space \ No newline at end of file diff --git a/bitacross-worker/.env.dev b/bitacross-worker/.env.dev new file mode 100644 index 0000000000..6ffad948b4 --- /dev/null +++ b/bitacross-worker/.env.dev @@ -0,0 +1,14 @@ +AliceWSPort=9946 +AliceRPCPort=9936 +AlicePort=30336 +BobWSPort=9947 +BobRPCPort=9937 +BobPort=30337 +CollatorWSPort=9944 +CollatorRPCPort=9933 +CollatorPort=30333 +TrustedWorkerPort=2000 +UntrustedWorkerPort=2001 +MuRaPort=3443 +UntrustedHttpPort=4545 +NODE_ENV=local \ No newline at end of file diff --git a/bitacross-worker/.gitattributes.orig b/bitacross-worker/.gitattributes.orig new file mode 100644 index 0000000000..00c1715114 --- /dev/null +++ b/bitacross-worker/.gitattributes.orig @@ -0,0 +1,18 @@ +# TODO: why do we need binary mode for Cargo.lock? +# Cargo.lock linguist-generated=true -diff + +[attr]rust text eol=lf whitespace=tab-in-indent,trailing-space,tabwidth=4 + +* text=auto eol=lf +*.cpp rust +*.h rust +*.rs rust +*.fixed linguist-language=Rust +src/etc/installer/gfx/* binary +*.woff binary +src/vendor/** -text +Cargo.lock -merge linguist-generated=false + +# Older git versions try to fix line endings on images, this prevents it. +*.png binary +*.ico binary diff --git a/bitacross-worker/.githooks/pre-commit b/bitacross-worker/.githooks/pre-commit new file mode 100755 index 0000000000..399188a65d --- /dev/null +++ b/bitacross-worker/.githooks/pre-commit @@ -0,0 +1,17 @@ +#!/bin/bash + +# This pre-commit hook uses cargo fmt to check the code style +# Install it either with `make githooks` or copy the file to .git/hooks + +echo '+cargo fmt -- --check' +cargo fmt -- --check +result=$? + +if [[ ${result} -ne 0 ]] ; then + cat <<\EOF +There are some code style issues, run `cargo fmt` first. +EOF + exit 1 +fi + +exit 0 \ No newline at end of file diff --git a/bitacross-worker/.github/workflows/build_and_test.yml b/bitacross-worker/.github/workflows/build_and_test.yml new file mode 100644 index 0000000000..d007fe1eb4 --- /dev/null +++ b/bitacross-worker/.github/workflows/build_and_test.yml @@ -0,0 +1,599 @@ +name: Build, Test, Clippy + +on: + workflow_dispatch: + push: + branches: + - master + - 'sdk-v[0-9]+.[0-9]+.[0-9]+-*' + tags: + - 'v[0-9]+.[0-9]+.[0-9]+*' + pull_request: + branches: + - master + - 'sdk-v[0-9]+.[0-9]+.[0-9]+-*' + +env: + CARGO_TERM_COLOR: always + LOG_DIR: logs + BUILD_CONTAINER_NAME: integritee_worker_enclave_test + +jobs: + cancel_previous_runs: + name: Cancel Previous Runs + runs-on: ubuntu-latest + steps: + - uses: styfle/cancel-workflow-action@0.11.0 + with: + access_token: ${{ secrets.GITHUB_TOKEN }} + + build-test: + runs-on: ${{ matrix.host }} + strategy: + fail-fast: false + matrix: + include: + - flavor_id: sidechain + mode: sidechain + host: integritee-builder-sgx + sgx_mode: HW + additional_features: dcap + - flavor_id: offchain-worker + mode: offchain-worker + host: integritee-builder-sgx + sgx_mode: HW + additional_features: dcap + - flavor_id: teeracle + mode: teeracle + host: integritee-builder-sgx + sgx_mode: HW + additional_features: dcap + - flavor_id: sidechain-evm + mode: sidechain + additional_features: evm,dcap + host: integritee-builder-sgx + sgx_mode: HW + + steps: + - uses: actions/checkout@v3 + + - name: Set env + run: | + fingerprint=$RANDOM + echo "FINGERPRINT=$fingerprint" >> $GITHUB_ENV + SGX_MODE_LOWERCASE=$(echo "${${{ matrix.sgx_mode }},,}") + echo "IMAGE_SUFFIX=$SGX_MODE_LOWERCASE-${{ matrix.flavor_id }}-${{ github.sha }}" >> $GITHUB_ENV + if [[ ${{ matrix.sgx_mode }} == 'HW' ]]; then + echo "DOCKER_DEVICES=--device=/dev/sgx/enclave --device=/dev/sgx/provision" >> $GITHUB_ENV + echo "DOCKER_VOLUMES=--volume /var/run/aesmd:/var/run/aesmd --volume /etc/sgx_default_qcnl.conf:/etc/sgx_default_qcnl.conf" >> $GITHUB_ENV + else + echo "DOCKER_DEVICES=" >> $GITHUB_ENV + echo "DOCKER_VOLUMES=" >> $GITHUB_ENV + fi + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + with: + buildkitd-flags: --debug + driver: docker-container + + - name: Build Worker + env: + DOCKER_BUILDKIT: 1 + run: > + docker build -t integritee-worker-${{ env.IMAGE_SUFFIX }} + --target deployed-worker + --build-arg WORKER_MODE_ARG=${{ matrix.mode }} --build-arg FINGERPRINT=${FINGERPRINT} --build-arg ADDITIONAL_FEATURES_ARG=${{ matrix.additional_features }} --build-arg SGX_MODE=${{ matrix.sgx_mode }} + -f build.Dockerfile . + + - run: docker images --all + + - name: Test Enclave # cargo test is not supported in the enclave, see: https://github.com/apache/incubator-teaclave-sgx-sdk/issues/232 + run: docker run --rm ${{ env.DOCKER_DEVICES }} ${{ env.DOCKER_VOLUMES }} integritee-worker-${{ env.IMAGE_SUFFIX }} test --all + + - name: Export worker image + run: | + docker image save integritee-worker-${{ env.IMAGE_SUFFIX }} | gzip > integritee-worker-${{ env.IMAGE_SUFFIX }}.tar.gz + + - name: Upload worker image + uses: actions/upload-artifact@v3 + with: + name: integritee-worker-${{ env.IMAGE_SUFFIX }}.tar.gz + path: integritee-worker-${{ env.IMAGE_SUFFIX }}.tar.gz + + - name: Create Enclave Digest File + run: | + mrenclave_hex=$(docker run integritee-worker-${{ env.IMAGE_SUFFIX }} mrenclave | grep -oP ':\s*\K[a-fA-F0-9]+') + echo "$mrenclave_hex" > mrenclave-${{ env.IMAGE_SUFFIX }}.hex + + - name: Upload Enclave Digest File + uses: actions/upload-artifact@v3 + with: + name: mrenclave-${{ env.IMAGE_SUFFIX }}.hex + path: mrenclave-${{ env.IMAGE_SUFFIX }}.hex + + - name: Delete images + run: | + if [[ "$(docker images -q integritee-worker-${{ env.IMAGE_SUFFIX }} 2> /dev/null)" != "" ]]; then + docker image rmi --force integritee-worker-${{ env.IMAGE_SUFFIX }} 2>/dev/null + fi + docker images --all + + build-client: + runs-on: ${{ matrix.host }} + strategy: + fail-fast: false + matrix: + include: + - flavor_id: sidechain + mode: sidechain + host: integritee-builder-sgx + sgx_mode: HW + additional_features: dcap + - flavor_id: offchain-worker + mode: offchain-worker + host: integritee-builder-sgx + sgx_mode: HW + additional_features: dcap + - flavor_id: teeracle + mode: teeracle + host: integritee-builder-sgx + sgx_mode: HW + additional_features: dcap + - flavor_id: sidechain-evm + mode: sidechain + additional_features: evm,dcap + host: integritee-builder-sgx + sgx_mode: HW + + steps: + - uses: actions/checkout@v3 + + - name: Set env + run: | + fingerprint=$RANDOM + echo "FINGERPRINT=$fingerprint" >> $GITHUB_ENV + SGX_MODE_LOWERCASE=$(echo "${${{ matrix.sgx_mode }},,}") + echo "IMAGE_SUFFIX=$SGX_MODE_LOWERCASE-${{ matrix.flavor_id }}-${{ github.sha }}" >> $GITHUB_ENV + if [[ ${{ matrix.sgx_mode }} == 'HW' ]]; then + echo "DOCKER_DEVICES=--device=/dev/sgx/enclave --device=/dev/sgx/provision" >> $GITHUB_ENV + echo "DOCKER_VOLUMES=--volume /var/run/aesmd:/var/run/aesmd --volume /etc/sgx_default_qcnl.conf:/etc/sgx_default_qcnl.conf" >> $GITHUB_ENV + else + echo "DOCKER_DEVICES=" >> $GITHUB_ENV + echo "DOCKER_VOLUMES=" >> $GITHUB_ENV + fi + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + with: + buildkitd-flags: --debug + driver: docker-container + + - name: Build CLI client + env: + DOCKER_BUILDKIT: 1 + run: > + docker build -t integritee-cli-client-${{ env.IMAGE_SUFFIX }} + --target deployed-client + --build-arg WORKER_MODE_ARG=${{ matrix.mode }} --build-arg ADDITIONAL_FEATURES_ARG=${{ matrix.additional_features }} + -f build.Dockerfile . + + - run: docker images --all + + - name: Export client image + run: | + docker image save integritee-cli-client-${{ env.IMAGE_SUFFIX }} | gzip > integritee-cli-client-${{ env.IMAGE_SUFFIX }}.tar.gz + + - name: Upload CLI client image + uses: actions/upload-artifact@v3 + with: + name: integritee-cli-client-${{ env.IMAGE_SUFFIX }}.tar.gz + path: integritee-cli-client-${{ env.IMAGE_SUFFIX }}.tar.gz + + - name: Delete images + run: | + if [[ "$(docker images -q integritee-cli-client-${{ env.IMAGE_SUFFIX }} 2> /dev/null)" != "" ]]; then + docker image rmi --force integritee-cli-client-${{ env.IMAGE_SUFFIX }} 2>/dev/null + fi + docker images --all + + code-quality: + runs-on: ubuntu-latest + container: "integritee/integritee-dev:0.2.2" + strategy: + fail-fast: false + matrix: + check: [ + # Workspace + cargo test --release, + # Worker + # Use release mode as the CI runs out of disk space otherwise. + cargo clippy --release -- -D warnings, + cargo clippy --release --features evm -- -D warnings, + cargo clippy --release --features sidechain -- -D warnings, + cargo clippy --release --features teeracle -- -D warnings, + cargo clippy --release --features offchain-worker -- -D warnings, + + # Enclave + cd enclave-runtime && cargo clippy -- -D warnings, + cd enclave-runtime && cargo clippy --features evm -- -D warnings, + cd enclave-runtime && cargo clippy --features sidechain -- -D warnings, + cd enclave-runtime && cargo clippy --features teeracle -- -D warnings, + cd enclave-runtime && cargo clippy --features offchain-worker -- -D warnings, + + # Fmt + cargo fmt --all -- --check, + cd enclave-runtime && cargo fmt --all -- --check, + ] + steps: + - uses: actions/checkout@v3 + - name: init-rust-target + # Enclave is not in the same workspace + run: rustup show && cd enclave-runtime && rustup show + + - uses: Swatinem/rust-cache@v2 + with: + key: ${{ matrix.check }} + + - name: ${{ matrix.check }} + run: ${{ matrix.check }} + + toml-fmt: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: init rust + run: rustup show + + - name: Install taplo + run: cargo install taplo-cli --locked + - name: Cargo.toml fmt + run: taplo fmt --check + + - name: Fail-fast; cancel other jobs + if: failure() + uses: andymckay/cancel-action@0.3 + + integration-tests: + runs-on: ${{ matrix.host }} + if: ${{ always() }} + needs: [build-test, build-client] + env: + WORKER_IMAGE_TAG: integritee-worker:dev + CLIENT_IMAGE_TAG: integritee-cli:dev + COINMARKETCAP_KEY: ${{ secrets.COINMARKETCAP_KEY }} + # IAS_EPID_SPID: ${{ secrets.IAS_SPID }} + # IAS_EPID_KEY: ${{ secrets.IAS_PRIMARY_KEY }} + TEERACLE_INTERVAL_SECONDS: 10 + + strategy: + fail-fast: false + matrix: + include: + - test: M6 + flavor_id: sidechain + demo_name: demo-shielding-unshielding-multiworker + host: test-runner-sgx + sgx_mode: HW + - test: M8 + flavor_id: sidechain + demo_name: demo-direct-call + host: test-runner-sgx + sgx_mode: HW + - test: Sidechain + flavor_id: sidechain + demo_name: demo-sidechain + host: test-runner-sgx + sgx_mode: HW + - test: M6 + flavor_id: offchain-worker + demo_name: demo-shielding-unshielding-multiworker + host: test-runner-sgx + sgx_mode: HW + - test: Teeracle + flavor_id: teeracle + demo_name: demo-teeracle + host: test-runner-sgx + sgx_mode: HW + - test: Teeracle + flavor_id: teeracle + demo_name: demo-teeracle-generic + host: test-runner-sgx + sgx_mode: HW + - test: Benchmark + flavor_id: sidechain + demo_name: sidechain-benchmark + host: test-runner-sgx + sgx_mode: HW + - test: EVM + flavor_id: sidechain-evm + demo_name: demo-smart-contract + host: test-runner-sgx + sgx_mode: HW + + steps: + - uses: actions/checkout@v3 + + - name: Set env + run: | + version=$RANDOM + SGX_MODE_LOWERCASE=$(echo "${${{ matrix.sgx_mode }},,}") + echo "IMAGE_SUFFIX=$SGX_MODE_LOWERCASE-${{ matrix.flavor_id }}-${{ github.sha }}" >> $GITHUB_ENV + echo "FLAVOR_ID=${{ matrix.flavor_id }}" >> $GITHUB_ENV + echo "PROJECT=${{ matrix.flavor_id }}-${{ matrix.demo_name }}" >> $GITHUB_ENV + echo "VERSION=dev.$version" >> $GITHUB_ENV + echo "WORKER_IMAGE_TAG=integritee-worker:dev.$version" >> $GITHUB_ENV + echo "INTEGRITEE_NODE=integritee-node:1.1.3.$version" >> $GITHUB_ENV + echo "CLIENT_IMAGE_TAG=integritee-cli:dev.$version" >> $GITHUB_ENV + if [[ ${{ matrix.sgx_mode }} == 'HW' ]]; then + echo "SGX_PROVISION=/dev/sgx/provision" >> $GITHUB_ENV + echo "SGX_ENCLAVE=/dev/sgx/enclave" >> $GITHUB_ENV + echo "AESMD=/var/run/aesmd" >> $GITHUB_ENV + echo "SGX_QCNL=/etc/sgx_default_qcnl.conf" >> $GITHUB_ENV + fi + + echo "LOG_DIR=./logs-$version" >> $GITHUB_ENV + + - name: Download Worker Image + uses: actions/download-artifact@v3 + with: + name: integritee-worker-${{ env.IMAGE_SUFFIX }}.tar.gz + path: . + + - name: Download CLI client Image + uses: actions/download-artifact@v3 + with: + name: integritee-cli-client-${{ env.IMAGE_SUFFIX }}.tar.gz + path: . + + - name: Load Worker & Client Images + env: + DOCKER_BUILDKIT: 1 + run: | + docker image load --input integritee-worker-${{ env.IMAGE_SUFFIX }}.tar.gz + docker image load --input integritee-cli-client-${{ env.IMAGE_SUFFIX }}.tar.gz + docker images --all + + ## + # Before tagging, delete the old "stuck" ones to be sure that the newly created ones are the latest + # Without if the docker image rmi throws an error if the image doesn't exist. + ## + - name: Re-name Image Tags + run: | + if [[ "$(docker images -q ${{ env.WORKER_IMAGE_TAG }} 2> /dev/null)" == "" ]]; then + docker image rmi --force ${{ env.WORKER_IMAGE_TAG }} 2>/dev/null + fi + if [[ "$(docker images -q ${{ env.CLIENT_IMAGE_TAG }} 2> /dev/null)" == "" ]]; then + docker image rmi --force ${{ env.CLIENT_IMAGE_TAG }} 2>/dev/null + fi + docker tag integritee-worker-${{ env.IMAGE_SUFFIX }} ${{ env.WORKER_IMAGE_TAG }} + docker tag integritee-cli-client-${{ env.IMAGE_SUFFIX }} ${{ env.CLIENT_IMAGE_TAG }} + docker pull integritee/integritee-node:1.1.3 + docker tag integritee/integritee-node:1.1.3 ${{ env.INTEGRITEE_NODE }} + docker images --all + + ## + # Stop any stucked/running compose projects + ## + - name: Stop docker containers + if: always() + continue-on-error: true + run: | + cd docker + docker compose -f <(envsubst < docker-compose.yml) -f <(envsubst < ${{ matrix.demo_name }}.yml) -p ${PROJECT} stop + + - name: Integration Test ${{ matrix.test }}-${{ matrix.flavor_id }} + run: | + cd docker + docker compose -f <(envsubst < docker-compose.yml) -f <(envsubst < ${{ matrix.demo_name }}.yml) -p ${PROJECT} up ${{ matrix.demo_name }} --no-build --exit-code-from ${{ matrix.demo_name }} --remove-orphans + + + - name: Collect Docker Logs + continue-on-error: true + if: always() + uses: jwalton/gh-docker-logs@v2 + with: + images: '${{ env.WORKER_IMAGE_TAG }},${{ env.CLIENT_IMAGE_TAG }},${{ env.INTEGRITEE_NODE }}' + tail: all + dest: ${{ env.LOG_DIR }} + + - name: Upload logs + if: always() + uses: actions/upload-artifact@v3 + with: + name: logs-${{ matrix.test }}-${{ matrix.flavor_id }} + path: ${{ env.LOG_DIR }} + + - name: Stop docker containers + if: always() + continue-on-error: true + run: | + cd docker + docker compose -f <(envsubst < docker-compose.yml) -f <(envsubst < ${{ matrix.demo_name }}.yml) -p ${PROJECT} stop + + - name: Delete images + run: | + if [[ "$(docker images -q integritee-worker-${{ env.IMAGE_SUFFIX }} 2> /dev/null)" != "" ]]; then + docker image rmi --force integritee-worker-${{ env.IMAGE_SUFFIX }} 2>/dev/null + fi + if [[ "$(docker images -q integritee-cli-client-${{ env.IMAGE_SUFFIX }} 2> /dev/null)" != "" ]]; then + docker image rmi --force integritee-cli-client-${{ env.IMAGE_SUFFIX }} 2>/dev/null + fi + if [[ "$(docker images -q ${{ env.WORKER_IMAGE_TAG }} 2> /dev/null)" != "" ]]; then + docker image rmi --force ${{ env.WORKER_IMAGE_TAG }} 2>/dev/null + fi + if [[ "$(docker images -q ${{ env.CLIENT_IMAGE_TAG }} 2> /dev/null)" != "" ]]; then + docker image rmi --force ${{ env.CLIENT_IMAGE_TAG }} 2>/dev/null + fi + if [[ "$(docker images -q ${{ env.INTEGRITEE_NODE }} 2> /dev/null)" != "" ]]; then + docker image rmi --force ${{ env.INTEGRITEE_NODE }} 2>/dev/null + fi + docker images --all + + release-build: + runs-on: integritee-builder-sgx + name: Release Build of teeracle + if: startsWith(github.ref, 'refs/tags/') + needs: [ build-test, integration-tests ] + + strategy: + fail-fast: false + matrix: + include: + - flavor_id: teeracle + mode: teeracle + sgx_mode: HW + additional_features: dcap + - flavor_id: sidechain + mode: sidechain + sgx_mode: HW + additional_features: dcap + + steps: + - uses: actions/checkout@v3 + + - name: Add masks + run: | + echo "::add-mask::$VAULT_TOKEN" + echo "::add-mask::$PRIVKEY_B64" + echo "::add-mask::$PRIVKEY_PASS" + + - name: Set env + run: | + fingerprint=$RANDOM + echo "FINGERPRINT=$fingerprint" >> $GITHUB_ENV + SGX_MODE_LOWERCASE=$(echo "${${{ matrix.sgx_mode }},,}") + echo "IMAGE_SUFFIX=$SGX_MODE_LOWERCASE-${{ matrix.flavor_id }}-${{ github.sha }}" >> $GITHUB_ENV + if [[ ${{ matrix.sgx_mode }} == 'HW' ]]; then + echo "DOCKER_DEVICES=--device=/dev/sgx/enclave --device=/dev/sgx/provision" >> $GITHUB_ENV + echo "DOCKER_VOLUMES=--volume /var/run/aesmd:/var/run/aesmd --volume /etc/sgx_default_qcnl.conf:/etc/sgx_default_qcnl.conf" >> $GITHUB_ENV + else + echo "DOCKER_DEVICES=" >> $GITHUB_ENV + echo "DOCKER_VOLUMES=" >> $GITHUB_ENV + fi + echo "VAULT_TOKEN=$VAULT_TOKEN" >> "$GITHUB_ENV" + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + with: + buildkitd-flags: --debug + driver: docker-container + + - name: Import secrets + uses: hashicorp/vault-action@v2 + id: import-secrets + with: + url: ${{ secrets.VAULT_URL }} + tlsSkipVerify: false + token: ${{ env.VAULT_TOKEN }} + exportEnv: false + secrets: | + ${{ secrets.VAULT_PATH }} intel_sgx_pem_base64 | PRIVKEY_B64 ; + ${{ secrets.VAULT_PATH }} password | PRIVKEY_PASS + + - name: Get secrets + env: + PRIVKEY_B64: ${{ steps.import-secrets.outputs.PRIVKEY_B64 }} + PRIVKEY_PASS: ${{ steps.import-secrets.outputs.PRIVKEY_PASS }} + run: | + echo $PRIVKEY_B64 | base64 --ignore-garbage --decode > enclave-runtime/intel_sgx.pem + echo $PRIVKEY_PASS > enclave-runtime/passfile.txt + + - name: Build Worker & Run Cargo Test + env: + DOCKER_BUILDKIT: 1 + run: > + docker build -t integritee/${{ matrix.flavor_id }}:${{ github.ref_name }} + --target deployed-worker + --build-arg WORKER_MODE_ARG=${{ matrix.mode }} --build-arg SGX_COMMERCIAL_KEY=enclave-runtime/intel_sgx.pem --build-arg SGX_PASSFILE=enclave-runtime/passfile.txt --build-arg SGX_PRODUCTION=1 --build-arg ADDITIONAL_FEATURES_ARG=${{ matrix.additional_features }} --build-arg SGX_MODE=${{ matrix.sgx_mode }} + -f build.Dockerfile . + + - name: Save released teeracle + run: | + docker image save integritee/${{ matrix.flavor_id }}:${{ github.ref_name }} | gzip > integritee-worker-${{ matrix.flavor_id }}-${{ github.ref_name }}.tar.gz + docker images --all + + - name: Upload teeracle image + uses: actions/upload-artifact@v3 + with: + name: integritee-worker-${{ matrix.flavor_id }}-${{ github.ref_name }}.tar.gz + path: integritee-worker-${{ matrix.flavor_id }}-${{ github.ref_name }}.tar.gz + + - name: Delete images + run: | + if [[ "$(docker images -q integritee/${{ matrix.flavor_id }}:${{ github.ref_name }} 2> /dev/null)" != "" ]]; then + docker image rmi --force integritee/${{ matrix.flavor_id }}:${{ github.ref_name }} 2>/dev/null + fi + docker images --all + + release: + runs-on: ubuntu-latest + name: Draft Release + if: startsWith(github.ref, 'refs/tags/') + needs: [ build-test, integration-tests, release-build ] + outputs: + release_url: ${{ steps.create-release.outputs.html_url }} + asset_upload_url: ${{ steps.create-release.outputs.upload_url }} + steps: + - uses: actions/checkout@v3 + + - name: Download Worker Image + uses: actions/download-artifact@v3 + with: + name: integritee-worker-teeracle-${{ github.ref_name }}.tar.gz + path: . + + - name: Download Worker Image + uses: actions/download-artifact@v3 + with: + name: integritee-worker-sidechain-${{ github.ref_name }}.tar.gz + path: . + + # + # Temporary comment out until we decide what to release + # + # - name: Download Integritee Client + # uses: actions/download-artifact@v3 + # with: + # name: integritee-client-sidechain-${{ github.sha }} + # path: integritee-client-tmp + + # - name: Download Enclave Signed + # uses: actions/download-artifact@v3 + # with: + # name: enclave-signed-sidechain-${{ github.sha }} + # path: enclave-signed-tmp + + # - name: Move service binaries + # run: mv integritee-worker-tmp/integritee-service ./integritee-demo-validateer + + # - name: Move service client binaries + # run: mv integritee-client-tmp/integritee-cli ./integritee-client + + # - name: Move service client binaries + # run: mv enclave-signed-tmp/enclave.signed.so ./enclave.signed.so + + - name: Changelog + uses: scottbrenner/generate-changelog-action@master + id: Changelog + + - name: Display structure of downloaded files + run: ls -R + working-directory: . + + - name: Release + id: create-release + uses: softprops/action-gh-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + body: | + ${{ steps.Changelog.outputs.changelog }} + draft: true + name: Docker ${{ github.ref_name }} + files: | + integritee-worker-teeracle-${{ github.ref_name }}.tar.gz + integritee-worker-sidechain-${{ github.ref_name }}.tar.gz + integritee-client + integritee-demo-validateer + enclave.signed.so diff --git a/bitacross-worker/.github/workflows/check_labels.yml b/bitacross-worker/.github/workflows/check_labels.yml new file mode 100644 index 0000000000..9511ed0b93 --- /dev/null +++ b/bitacross-worker/.github/workflows/check_labels.yml @@ -0,0 +1,24 @@ +name: Labels Check +on: + pull_request: + types: [opened, labeled, unlabeled, synchronize, ready_for_review] +jobs: + A-label-check: + uses: ./.github/workflows/label-checker.yml + with: + predefined_labels: "A0-core,A1-cli,A2-applibs,A3-sidechain,A4-offchain,A5-teeracle,A6-evm,A7-somethingelse" + + B-label-check: + uses: ./.github/workflows/label-checker.yml + with: + predefined_labels: "B0-silent,B1-releasenotes" + + C-label-check: + uses: ./.github/workflows/label-checker.yml + with: + predefined_labels: "C1-low 📌,C3-medium 📣,C7-high ❗️,C9-critical ‼️" + + E-label-check: + uses: ./.github/workflows/label-checker.yml + with: + predefined_labels: "E0-breaksnothing,E3-hardmerge,E5-publicapi,E6-parentchain,E8-breakseverything" diff --git a/bitacross-worker/.github/workflows/delete-release.yml b/bitacross-worker/.github/workflows/delete-release.yml new file mode 100644 index 0000000000..53fbdbb0f3 --- /dev/null +++ b/bitacross-worker/.github/workflows/delete-release.yml @@ -0,0 +1,70 @@ +name: Delete-Release + +on: + release: + types: [deleted] # should be deleted + +jobs: + purge-image: + name: Delete image from ghcr.io + runs-on: ubuntu-latest + strategy: + matrix: + #binary: ["integritee-client", "integritee-demo-validateer"] + binary: ["teeracle"] + steps: + - uses: actions/checkout@v2 + + - name: Set output + id: vars + run: echo "{tag}={$GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT + + - name: Get Tag + id: get_tag + run: echo ::set-output name=TAG::${GITHUB_REF/refs\/tags\//} + + - name: Check output + env: + RELEASE_VERSION: ${{ steps.get_tag.outputs.TAG }} + run: | + echo $RELEASE_VERSION + echo ${{ steps.vars.outputs.tag }} + echo ${{github.event.pull_request.number}} + + - name: Login to DockerHub + if: github.event_name != 'pull_request' + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKER_HUB_USERNAME }} + password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} + + # Unfortunately accessing the repo with personal access token is not possible + # Workaround: disable 2FA and user password instead of TOKEN + - name: Delete docker tag + run: | + ORGANIZATION="integritee" + IMAGE="${{ matrix.binary }}" + TAG="${{ steps.get_tag.outputs.TAG }}" + + login_data() { + cat < /dev/null)" != "" ]]; then + docker image rmi --force integritee/sidechain:${{ github.event.release.tag_name }} 2>/dev/null + fi + docker images --all diff --git a/bitacross-worker/.github/workflows/publish-docker-teeracle.yml b/bitacross-worker/.github/workflows/publish-docker-teeracle.yml new file mode 100644 index 0000000000..01a9a6f8b0 --- /dev/null +++ b/bitacross-worker/.github/workflows/publish-docker-teeracle.yml @@ -0,0 +1,43 @@ +name: Publish Docker image for new teeracle release + +on: + release: + types: + - published + +jobs: + main: + name: Push Integritee Teeracle to Dockerhub + runs-on: [ self-hosted ] + steps: + - uses: actions/checkout@v3 + + - name: Download teeracle from release + uses: dsaltares/fetch-gh-release-asset@master + with: + version: "tags/${{ github.event.release.tag_name }}" + file: integritee-worker-teeracle-${{ github.event.release.tag_name }}.tar.gz + target: "integritee-worker-teeracle.tar.gz" + token: ${{ secrets.GITHUB_TOKEN }} + + + - name: Login to Dockerhub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKER_HUB_USERNAME }} + password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} + + - name: Load Worker & Push + env: + DOCKER_BUILDKIT: 1 + run: | + docker image load --input integritee-worker-teeracle.tar.gz + docker images --all + docker push integritee/teeracle:${{ github.event.release.tag_name }} + + - name: Delete images + run: | + if [[ "$(docker images -q integritee/teeracle:${{ github.event.release.tag_name }} 2> /dev/null)" != "" ]]; then + docker image rmi --force integritee/teeracle:${{ github.event.release.tag_name }} 2>/dev/null + fi + docker images --all diff --git a/bitacross-worker/.github/workflows/publish-draft-release.yml b/bitacross-worker/.github/workflows/publish-draft-release.yml new file mode 100644 index 0000000000..0e8c72dd6c --- /dev/null +++ b/bitacross-worker/.github/workflows/publish-draft-release.yml @@ -0,0 +1,69 @@ +name: Release - Publish draft + +on: + push: + tags: + # Catches only v1.2.3 (-dev,-rc1 etc won't be released as SDK) + - v[0-9]+.[0-9]+.[0-9]+ + +jobs: + publish-draft-release: + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v3 + with: + fetch-depth: 0 + path: worker + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.0.0 + + - name: Download srtool json output + uses: actions/download-artifact@v3 + + - name: Prepare tooling + run: | + cd worker/scripts/changelog + gem install bundler changelogerator:0.9.1 + bundle install + changelogerator --help + URL=https://github.com/chevdor/tera-cli/releases/download/v0.2.1/tera-cli_linux_amd64.deb + wget $URL -O tera.deb + sudo dpkg -i tera.deb + tera --version + + - name: Generate release notes + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DEBUG: 1 + PRE_RELEASE: ${{ github.event.inputs.pre_release }} + run: | + find ${{env.GITHUB_WORKSPACE}} -type f -name "*_srtool_output.json" + + cd worker/scripts/changelog + + ./bin/changelog ${GITHUB_REF} + ls -al release-notes.md + ls -al context.json + + - name: Archive artifact context.json + uses: actions/upload-artifact@v3 + with: + name: release-notes-context + path: | + worker/scripts/changelog/context.json + **/*_srtool_output.json + + - name: Create draft release + id: create-release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: SDK ${{ github.ref }} + body_path: ./worker/scripts/changelog/release-notes.md + draft: true diff --git a/bitacross-worker/.gitignore b/bitacross-worker/.gitignore new file mode 100644 index 0000000000..a14498c6eb --- /dev/null +++ b/bitacross-worker/.gitignore @@ -0,0 +1,78 @@ +# Generated by Cargo +# will have compiled files and executables +**/target/ + +**/__pycache__/ +/log/* +log* +**/tmp/* + +**/node_modules/* + +# These are backup files generated by rustfmt +**/*.rs.bk + +# binaries +bin/*.so +bin/bitacross-* +bin/*.wasm + +# sealed data +bin/*.bin + +# public RSA key +bin/rsa_pubkey.txt +bin/ecc_pubkey.txt + +# VS Code settings +.vscode + +#intelliJ +.idea/ +*.iml + +*.log + +# vim +*.swp + +# keystores +my_keystore/* +my_trusted_keystore/* + +# generated enclave files +service/Enclave_u.* +service/libEnclave_u.* +enclave-runtime/Enclave_t.* +enclave-runtime/enclave.so +lib/libEnclave_u.* +lib/libcompiler-rt-patch.a +lib/libenclave.a + +# certificate, key, spid and generated report for remote attestation +bin/client.crt +bin/client.key +bin/spid.txt +bin/spid_production.txt +bin/key.txt +bin/key_production.txt +bin/attestation_report.json +bin/shards +bin/*.der +bin/enclave-shielding-pubkey.json +bin/sidechain_db +bin/my_trusted_keystore + +# client +cli/my_keystore +cli/my_trusted_keystore +bin/light_client_db.bin.1 + +# generated upstream patch +upstream.patch + +# backup log files +log-backup + +# env files and configs +.env diff --git a/bitacross-worker/.taplo.toml b/bitacross-worker/.taplo.toml new file mode 100644 index 0000000000..a1de67fb52 --- /dev/null +++ b/bitacross-worker/.taplo.toml @@ -0,0 +1,21 @@ +include = ["**/Cargo.toml"] + +[formatting] +array_auto_expand = false +array_auto_collapse = false +indent_string = " " +inline_table_expand = false + +[[rule]] +include = ["**/Cargo.toml"] +keys = ["dependencies", "target", "patch"] + +[rule.formatting] +reorder_keys = true + +[[rule]] +include = ["**/Cargo.toml"] +keys = ["features"] + +[rule.formatting] +array_auto_expand = true \ No newline at end of file diff --git a/bitacross-worker/Cargo.lock b/bitacross-worker/Cargo.lock new file mode 100644 index 0000000000..9e85f8180c --- /dev/null +++ b/bitacross-worker/Cargo.lock @@ -0,0 +1,16436 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex 1.9.5", +] + +[[package]] +name = "ac-compose-macros" +version = "0.4.2" +source = "git+https://github.com/scs/substrate-api-client.git?branch=polkadot-v0.9.42-tag-v0.14.0#e4ed74b0fb6c2fd5585f55c2702b97b56d99c7f6" +dependencies = [ + "ac-primitives", + "log 0.4.20", + "maybe-async", +] + +[[package]] +name = "ac-node-api" +version = "0.5.1" +source = "git+https://github.com/scs/substrate-api-client.git?branch=polkadot-v0.9.42-tag-v0.14.0#e4ed74b0fb6c2fd5585f55c2702b97b56d99c7f6" +dependencies = [ + "ac-primitives", + "bitvec", + "derive_more", + "either", + "frame-metadata", + "hex", + "log 0.4.20", + "parity-scale-codec", + "scale-bits 0.4.0", + "scale-decode 0.8.0", + "scale-encode", + "scale-info", + "serde 1.0.193", + "serde_json 1.0.103", + "sp-application-crypto", + "sp-core", + "sp-runtime", + "sp-runtime-interface", +] + +[[package]] +name = "ac-primitives" +version = "0.9.0" +source = "git+https://github.com/scs/substrate-api-client.git?branch=polkadot-v0.9.42-tag-v0.14.0#e4ed74b0fb6c2fd5585f55c2702b97b56d99c7f6" +dependencies = [ + "frame-system", + "impl-serde", + "pallet-assets", + "pallet-balances", + "parity-scale-codec", + "primitive-types", + "scale-info", + "serde 1.0.193", + "serde_json 1.0.103", + "sp-application-crypto", + "sp-core", + "sp-core-hashing 5.0.0", + "sp-runtime", + "sp-runtime-interface", + "sp-staking", + "sp-version", + "sp-weights", +] + +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli 0.26.2", +] + +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli 0.27.3", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aead" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "aead" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", +] + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array 0.14.7", +] + +[[package]] +name = "aes" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" +dependencies = [ + "aes-soft", + "aesni", + "cipher 0.2.5", +] + +[[package]] +name = "aes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +dependencies = [ + "cfg-if 1.0.0", + "cipher 0.3.0", + "cpufeatures", + "opaque-debug 0.3.0", +] + +[[package]] +name = "aes" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +dependencies = [ + "cfg-if 1.0.0", + "cipher 0.4.4", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" +dependencies = [ + "aead 0.4.3", + "aes 0.7.5", + "cipher 0.3.0", + "ctr 0.8.0", + "ghash 0.4.4", + "subtle", +] + +[[package]] +name = "aes-gcm" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237" +dependencies = [ + "aead 0.5.2", + "aes 0.8.3", + "cipher 0.4.4", + "ctr 0.9.2", + "ghash 0.5.0", + "subtle", +] + +[[package]] +name = "aes-soft" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" +dependencies = [ + "cipher 0.2.5", + "opaque-debug 0.3.0", +] + +[[package]] +name = "aesni" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" +dependencies = [ + "cipher 0.2.5", + "opaque-debug 0.3.0", +] + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom 0.2.10", + "once_cell 1.18.0", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if 1.0.0", + "getrandom 0.2.10", + "once_cell 1.18.0", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr 2.6.3", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "anyhow" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits 0.2.16", +] + +[[package]] +name = "arc-swap" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" + +[[package]] +name = "array-bytes" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52f63c5c1316a16a4b35eaac8b76a98248961a533f061684cb2a7cb0eafb6c6" + +[[package]] +name = "array-bytes" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b1c5a481ec30a5abd8dfbd94ab5cf1bb4e9a66be7f1b3b322f2f1170c200fd" + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" +dependencies = [ + "nodrop", +] + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "asn1-rs" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ff05a702273012438132f449575dbc804e27b2f3cbe3069aa237d26c98fa33" +dependencies = [ + "asn1-rs-derive 0.1.0", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits 0.2.16", + "rusticata-macros", + "thiserror 1.0.44", + "time 0.3.22", +] + +[[package]] +name = "asn1-rs" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +dependencies = [ + "asn1-rs-derive 0.4.0", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits 0.2.16", + "rusticata-macros", + "thiserror 1.0.44", + "time 0.3.22", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8b7511298d5b7784b40b092d9e9dcd3a627a5707e4b5e507931ab0d44eeebf" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "asn1_der" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" + +[[package]] +name = "assert_matches" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core 0.3.28", +] + +[[package]] +name = "async-io" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +dependencies = [ + "async-lock", + "autocfg 1.1.0", + "cfg-if 1.0.0", + "concurrent-queue", + "futures-lite", + "log 0.4.20", + "parking", + "polling", + "rustix 0.37.23", + "slab 0.4.8", + "socket2 0.4.9", + "waker-fn", +] + +[[package]] +name = "async-lock" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-trait" +version = "0.1.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "asynchronous-codec" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06a0daa378f5fd10634e44b0a29b2a87b890657658e072a30d6f26e57ddee182" +dependencies = [ + "bytes 1.4.0", + "futures-sink 0.3.28", + "futures-util 0.3.28", + "memchr 2.6.3", + "pin-project-lite 0.2.10", +] + +[[package]] +name = "atomic-waker" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "auto_impl" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "autocfg" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" +dependencies = [ + "autocfg 1.1.0", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +dependencies = [ + "addr2line 0.20.0", + "cc", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object 0.31.1", + "rustc-demangle", +] + +[[package]] +name = "base-x" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base58" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" + +[[package]] +name = "base64" +version = "0.13.0" +source = "git+https://github.com/mesalock-linux/rust-base64-sgx?tag=sgx_1.1.3#dc7389e10817b078f289386b3b6a852ab6c4c021" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "base64" +version = "0.13.0" +source = "git+https://github.com/mesalock-linux/rust-base64-sgx?rev=sgx_1.1.3#dc7389e10817b078f289386b3b6a852ab6c4c021" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "base64" +version = "0.13.0" +source = "git+https://github.com/mesalock-linux/rust-base64-sgx#dc7389e10817b078f289386b3b6a852ab6c4c021" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bech32" +version = "0.10.0-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98f7eed2b2781a6f0b5c903471d48e15f56fb4e1165df8a9a2337fd1a59d45ea" + +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" +dependencies = [ + "serde 1.0.193", +] + +[[package]] +name = "binary-merkle-tree" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "hash-db 0.16.0", + "log 0.4.20", +] + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde 1.0.193", +] + +[[package]] +name = "bindgen" +version = "0.64.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "proc-macro2", + "quote", + "regex 1.9.5", + "rustc-hash", + "shlex", + "syn 1.0.109", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitacross-cli" +version = "0.0.1" +dependencies = [ + "array-bytes 6.1.0", + "base58", + "chrono 0.4.26", + "clap 4.1.0", + "env_logger 0.9.3", + "frame-metadata", + "hdrhistogram", + "hex", + "ita-sgx-runtime", + "ita-stf", + "itc-rpc-client", + "itp-node-api", + "itp-rpc", + "itp-sgx-crypto", + "itp-stf-primitives", + "itp-time-utils", + "itp-types", + "itp-utils", + "litentry-primitives", + "log 0.4.20", + "pallet-balances", + "pallet-evm 6.0.0-dev (git+https://github.com/integritee-network/frontier.git?branch=bar/polkadot-v0.9.42)", + "pallet-teerex", + "parity-scale-codec", + "rand 0.8.5", + "rayon", + "regex 1.9.5", + "reqwest", + "rococo-parachain-runtime", + "scale-value", + "serde 1.0.193", + "serde_json 1.0.103", + "sgx_crypto_helper", + "sp-application-crypto", + "sp-core", + "sp-core-hashing 6.0.0", + "sp-keyring", + "sp-keystore", + "sp-runtime", + "substrate-api-client", + "substrate-client-keystore", + "thiserror 1.0.44", + "urlencoding", +] + +[[package]] +name = "bitacross-worker" +version = "0.0.1" +dependencies = [ + "anyhow", + "async-trait", + "base58", + "clap 2.34.0", + "config", + "dirs", + "env_logger 0.9.3", + "frame-support", + "futures 0.3.28", + "hex", + "ipfs-api", + "ita-stf", + "itc-parentchain", + "itc-parentchain-test", + "itc-rest-client", + "itc-rpc-client", + "itc-rpc-server", + "itp-api-client-types", + "itp-enclave-api", + "itp-enclave-metrics", + "itp-node-api", + "itp-settings", + "itp-storage", + "itp-types", + "itp-utils", + "its-consensus-slots", + "its-peer-fetch", + "its-primitives", + "its-rpc-handler", + "its-storage", + "its-test", + "jsonrpsee 0.2.0", + "lazy_static", + "litentry-primitives", + "log 0.4.20", + "mockall", + "pallet-balances", + "parity-scale-codec", + "parking_lot 0.12.1", + "parse_duration", + "primitive-types", + "prometheus", + "regex 1.9.5", + "rococo-parachain-runtime", + "scale-info", + "serde 1.0.193", + "serde_derive 1.0.193", + "serde_json 1.0.103", + "sgx-verify", + "sgx_crypto_helper", + "sgx_types", + "sp-consensus-grandpa", + "sp-core", + "sp-keyring", + "sp-runtime", + "substrate-api-client", + "teerex-primitives", + "thiserror 1.0.44", + "tokio", + "warp", +] + +[[package]] +name = "bitcoin" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5973a027b341b462105675962214dfe3c938ad9afd395d84b28602608bdcec7b" +dependencies = [ + "bech32", + "bitcoin-internals", + "bitcoin_hashes", + "core2 0.3.3", + "hex-conservative", + "hex_lit", + "secp256k1 0.28.0", +] + +[[package]] +name = "bitcoin-internals" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" + +[[package]] +name = "bitcoin_hashes" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" +dependencies = [ + "bitcoin-internals", + "core2 0.3.3", + "hex-conservative", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "blake2-rfc" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" +dependencies = [ + "arrayvec 0.4.12", + "constant_time_eq 0.1.5", +] + +[[package]] +name = "blake2b_simd" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" +dependencies = [ + "arrayref", + "arrayvec 0.7.4", + "constant_time_eq 0.2.6", +] + +[[package]] +name = "blake2s_simd" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637f448b9e61dfadbdcbae9a885fadee1f3eaffb1f8d3c1965d3ade8bdfd44f" +dependencies = [ + "arrayref", + "arrayvec 0.7.4", + "constant_time_eq 0.2.6", +] + +[[package]] +name = "blake3" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "729b71f35bd3fa1a4c86b85d32c8b9069ea7fe14f7a53cfabb65f62d4265b888" +dependencies = [ + "arrayref", + "arrayvec 0.7.4", + "cc", + "cfg-if 1.0.0", + "constant_time_eq 0.2.6", + "digest 0.10.7", +] + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding 0.1.5", + "byte-tools", + "byteorder 1.4.3", + "generic-array 0.12.4", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "block-modes" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a0e8073e8baa88212fb5823574c02ebccb395136ba9a164ab89379ec6072f0" +dependencies = [ + "block-padding 0.2.1", + "cipher 0.2.5", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + +[[package]] +name = "bounded-collections" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb5b05133427c07c4776906f673ccf36c21b102c9829c641a5b56bd151d44fd6" +dependencies = [ + "log 0.4.20", + "parity-scale-codec", + "scale-info", + "serde 1.0.193", +] + +[[package]] +name = "bounded-vec" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68534a48cbf63a4b1323c433cf21238c9ec23711e0df13b08c33e5c2082663ce" +dependencies = [ + "thiserror 1.0.44", +] + +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + +[[package]] +name = "bstr" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" +dependencies = [ + "memchr 2.6.3", + "serde 1.0.193", +] + +[[package]] +name = "build-helper" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdce191bf3fa4995ce948c8c83b4640a1745457a149e73c6db75b4ffe36aad5f" +dependencies = [ + "semver 0.6.0", +] + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + +[[package]] +name = "bytemuck" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "git+https://github.com/mesalock-linux/byteorder-sgx?tag=sgx_1.1.3#325f392dcd294109eb05f0a3c45e4141514c7784" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +dependencies = [ + "byteorder 1.4.3", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bytes" +version = "1.0.1" +source = "git+https://github.com/integritee-network/bytes-sgx?branch=sgx-experimental#62ed3082be2e23cb9bc8cc7ee9983a523de69292" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "camino" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c530edf18f37068ac2d977409ed5cd50d53d73bc653c7647b48eb78976ac9ae2" +dependencies = [ + "serde 1.0.193", +] + +[[package]] +name = "cargo-platform" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27" +dependencies = [ + "serde 1.0.193", +] + +[[package]] +name = "cargo_metadata" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" +dependencies = [ + "camino", + "cargo-platform", + "semver 1.0.18", + "serde 1.0.193", + "serde_json 1.0.103", + "thiserror 1.0.44", +] + +[[package]] +name = "cargo_toml" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3f9629bc6c4388ea699781dc988c2b99766d7679b151c81990b4fa1208fafd3" +dependencies = [ + "serde 1.0.193", + "toml 0.8.2", +] + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +dependencies = [ + "jobserver", +] + +[[package]] +name = "ccm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aca1a8fbc20b50ac9673ff014abfb2b5f4085ee1a850d408f14a159c5853ac7" +dependencies = [ + "aead 0.3.2", + "cipher 0.2.5", + "subtle", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-expr" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aacacf4d96c24b2ad6eb8ee6df040e4f27b0d0b39a5710c30091baa830485db" +dependencies = [ + "smallvec 1.11.0", +] + +[[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 = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "chacha20" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c80e5460aa66fe3b91d40bcbdab953a597b60053e34d684ac6903f863b680a6" +dependencies = [ + "cfg-if 1.0.0", + "cipher 0.3.0", + "cpufeatures", + "zeroize", +] + +[[package]] +name = "chacha20poly1305" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18446b09be63d457bbec447509e85f662f32952b035ce892290396bc0b0cff5" +dependencies = [ + "aead 0.4.3", + "chacha20", + "cipher 0.3.0", + "poly1305", + "zeroize", +] + +[[package]] +name = "chrono" +version = "0.4.11" +source = "git+https://github.com/mesalock-linux/chrono-sgx#f964ae7f5f65bd2c9cd6f44a067e7980afc08ca0" +dependencies = [ + "num-integer 0.1.41", + "num-traits 0.2.10", + "sgx_tstd", +] + +[[package]] +name = "chrono" +version = "0.4.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits 0.2.16", + "serde 1.0.193", + "time 0.1.45", + "wasm-bindgen", + "winapi 0.3.9", +] + +[[package]] +name = "cid" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ed9c8b2d17acb8110c46f1da5bf4a696d745e1474a16db0cd2b49cd0249bf2" +dependencies = [ + "core2 0.4.0", + "multibase", + "multihash 0.16.3", + "serde 1.0.193", + "unsigned-varint 0.7.1", +] + +[[package]] +name = "cipher" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "clang-sys" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags 1.3.2", + "strsim 0.8.0", + "textwrap", + "unicode-width", + "vec_map", + "yaml-rust 0.3.5", +] + +[[package]] +name = "clap" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa91278560fc226a5d9d736cc21e485ff9aad47d26b8ffe1f54cba868b684b9f" +dependencies = [ + "bitflags 1.3.2", + "clap_derive", + "clap_lex", + "is-terminal", + "once_cell 1.18.0", + "strsim 0.10.0", + "termcolor", +] + +[[package]] +name = "clap_derive" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "clap_lex" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "033f6b7a4acb1f358c742aaca805c939ee73b4c6209ae4318ec7aca81c42e646" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "coarsetime" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90d114103adbc625300f346d4d09dfb4ab1c4a8df6868435dd903392ecf4354" +dependencies = [ + "libc", + "once_cell 1.18.0", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "common-multipart-rfc7578" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d0a7a42b9c13f2b2a1a7e64b949a19bcb56a49b190076e60261001ceaa5304" +dependencies = [ + "bytes 1.4.0", + "futures 0.3.28", + "http 0.2.9", + "mime", + "mime_guess", + "rand 0.8.5", + "thiserror 1.0.44", +] + +[[package]] +name = "common-primitives" +version = "0.1.0" +dependencies = [ + "sp-std 5.0.0", +] + +[[package]] +name = "concurrent-queue" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "config" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d379af7f68bfc21714c6c7dea883544201741d2ce8274bb12fa54f89507f52a7" +dependencies = [ + "async-trait", + "json5", + "lazy_static", + "nom", + "pathdiff", + "ron", + "rust-ini", + "serde 1.0.193", + "serde_json 1.0.103", + "toml 0.5.11", + "yaml-rust 0.4.5", +] + +[[package]] +name = "const-oid" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "795bc6e66a8e340f075fcf6227e417a2dc976b92b91f3cdc778bb858778b6747" + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "constant_time_eq" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a53c0a4d288377e7415b53dcfc3c04da5cdc2cc95c8d5ac178b58f0b861ad6" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "core-primitives" +version = "0.9.12" +dependencies = [ + "frame-support", + "litentry-hex-utils", + "litentry-macros 0.9.12", + "litentry-proc-macros", + "pallet-evm 6.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", + "parity-scale-codec", + "ring 0.16.20", + "scale-info", + "serde 1.0.193", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", + "strum 0.25.0", + "strum_macros 0.25.3", +] + +[[package]] +name = "core2" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239fa3ae9b63c2dc74bd3fa852d4792b8b305ae64eeede946265b6af62f1fff3" +dependencies = [ + "memchr 2.6.3", +] + +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr 2.6.3", +] + +[[package]] +name = "cpp_demangle" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "cranelift-bforest" +version = "0.93.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc42ba2e232e5b20ff7dc299a812d53337dadce9a7e39a238e6a5cb82d2e57b" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen" +version = "0.93.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "253531aca9b6f56103c9420369db3263e784df39aa1c90685a1f69cfbba0623e" +dependencies = [ + "arrayvec 0.7.4", + "bumpalo", + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-entity", + "cranelift-isle", + "gimli 0.26.2", + "hashbrown 0.12.3", + "log 0.4.20", + "regalloc2", + "smallvec 1.11.0", + "target-lexicon", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.93.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f2154365e2bff1b1b8537a7181591fdff50d8e27fa6e40d5c69c3bad0ca7c8" +dependencies = [ + "cranelift-codegen-shared", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.93.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "687e14e3f5775248930e0d5a84195abef8b829958e9794bf8d525104993612b4" + +[[package]] +name = "cranelift-entity" +version = "0.93.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f42ea692c7b450ad18b8c9889661505d51c09ec4380cf1c2d278dbb2da22cae1" +dependencies = [ + "serde 1.0.193", +] + +[[package]] +name = "cranelift-frontend" +version = "0.93.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8483c2db6f45fe9ace984e5adc5d058102227e4c62e5aa2054e16b0275fd3a6e" +dependencies = [ + "cranelift-codegen", + "log 0.4.20", + "smallvec 1.11.0", + "target-lexicon", +] + +[[package]] +name = "cranelift-isle" +version = "0.93.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9793158837678902446c411741d87b43f57dadfb944f2440db4287cda8cbd59" + +[[package]] +name = "cranelift-native" +version = "0.93.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72668c7755f2b880665cb422c8ad2d56db58a88b9bebfef0b73edc2277c13c49" +dependencies = [ + "cranelift-codegen", + "libc", + "target-lexicon", +] + +[[package]] +name = "cranelift-wasm" +version = "0.93.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3852ce4b088b44ac4e29459573943009a70d1b192c8d77ef949b4e814f656fc1" +dependencies = [ + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "itertools 0.10.5", + "log 0.4.20", + "smallvec 1.11.0", + "wasmparser", + "wasmtime-types", +] + +[[package]] +name = "crc" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +dependencies = [ + "autocfg 1.1.0", + "cfg-if 1.0.0", + "crossbeam-utils", + "memoffset 0.9.0", + "scopeguard", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "740fe28e594155f10cfc383984cbefd529d7396050557148f79cb0f621204124" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "typenum 1.16.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array 0.14.7", + "subtle", +] + +[[package]] +name = "crypto-mac" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +dependencies = [ + "generic-array 0.14.7", + "subtle", +] + +[[package]] +name = "ct-logs" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1a816186fa68d9e426e3cb4ae4dff1fcd8e4a2c34b781bf7a822574a0d0aac8" +dependencies = [ + "sct 0.6.1", +] + +[[package]] +name = "ctr" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" +dependencies = [ + "cipher 0.3.0", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher 0.4.4", +] + +[[package]] +name = "cumulus-pallet-aura-ext" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=polkadot-v0.9.42#f603a61ff370fc33740c9373833c3c6ba1486846" +dependencies = [ + "frame-support", + "frame-system", + "pallet-aura", + "parity-scale-codec", + "scale-info", + "sp-application-crypto", + "sp-consensus-aura", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "cumulus-pallet-dmp-queue" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=polkadot-v0.9.42#f603a61ff370fc33740c9373833c3c6ba1486846" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "frame-system", + "log 0.4.20", + "parity-scale-codec", + "scale-info", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", + "xcm", +] + +[[package]] +name = "cumulus-pallet-parachain-system" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=polkadot-v0.9.42#f603a61ff370fc33740c9373833c3c6ba1486846" +dependencies = [ + "bytes 1.4.0", + "cumulus-pallet-parachain-system-proc-macro", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "environmental 1.1.4", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "log 0.4.20", + "parity-scale-codec", + "polkadot-parachain", + "scale-info", + "sp-core", + "sp-externalities", + "sp-inherents", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-state-machine", + "sp-std 5.0.0", + "sp-trie", + "sp-version", + "xcm", +] + +[[package]] +name = "cumulus-pallet-parachain-system-proc-macro" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=polkadot-v0.9.42#f603a61ff370fc33740c9373833c3c6ba1486846" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "cumulus-pallet-xcm" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=polkadot-v0.9.42#f603a61ff370fc33740c9373833c3c6ba1486846" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", + "xcm", +] + +[[package]] +name = "cumulus-pallet-xcmp-queue" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=polkadot-v0.9.42#f603a61ff370fc33740c9373833c3c6ba1486846" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "frame-system", + "log 0.4.20", + "parity-scale-codec", + "polkadot-runtime-common", + "rand_chacha 0.3.1", + "scale-info", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", + "xcm", + "xcm-executor", +] + +[[package]] +name = "cumulus-primitives-core" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=polkadot-v0.9.42#f603a61ff370fc33740c9373833c3c6ba1486846" +dependencies = [ + "parity-scale-codec", + "polkadot-core-primitives", + "polkadot-parachain", + "polkadot-primitives", + "scale-info", + "sp-api", + "sp-runtime", + "sp-std 5.0.0", + "sp-trie", + "xcm", +] + +[[package]] +name = "cumulus-primitives-parachain-inherent" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=polkadot-v0.9.42#f603a61ff370fc33740c9373833c3c6ba1486846" +dependencies = [ + "async-trait", + "cumulus-primitives-core", + "cumulus-relay-chain-interface", + "cumulus-test-relay-sproof-builder", + "parity-scale-codec", + "sc-client-api", + "scale-info", + "sp-api", + "sp-core", + "sp-inherents", + "sp-runtime", + "sp-state-machine", + "sp-std 5.0.0", + "sp-storage", + "sp-trie", + "tracing", +] + +[[package]] +name = "cumulus-primitives-timestamp" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=polkadot-v0.9.42#f603a61ff370fc33740c9373833c3c6ba1486846" +dependencies = [ + "cumulus-primitives-core", + "futures 0.3.28", + "parity-scale-codec", + "sp-inherents", + "sp-std 5.0.0", + "sp-timestamp", +] + +[[package]] +name = "cumulus-primitives-utility" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=polkadot-v0.9.42#f603a61ff370fc33740c9373833c3c6ba1486846" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "log 0.4.20", + "parity-scale-codec", + "polkadot-runtime-common", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", + "xcm", + "xcm-builder", + "xcm-executor", +] + +[[package]] +name = "cumulus-relay-chain-interface" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=polkadot-v0.9.42#f603a61ff370fc33740c9373833c3c6ba1486846" +dependencies = [ + "async-trait", + "cumulus-primitives-core", + "futures 0.3.28", + "jsonrpsee-core", + "parity-scale-codec", + "polkadot-overseer", + "sc-client-api", + "sp-api", + "sp-blockchain", + "sp-state-machine", + "thiserror 1.0.44", +] + +[[package]] +name = "cumulus-test-relay-sproof-builder" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=polkadot-v0.9.42#f603a61ff370fc33740c9373833c3c6ba1486846" +dependencies = [ + "cumulus-primitives-core", + "parity-scale-codec", + "polkadot-primitives", + "sp-runtime", + "sp-state-machine", + "sp-std 5.0.0", +] + +[[package]] +name = "curve25519-dalek" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b85542f99a2dfa2a1b8e192662741c9859a846b296bef1c92ef9b58b5a216" +dependencies = [ + "byteorder 1.4.3", + "digest 0.8.1", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder 1.4.3", + "digest 0.9.0", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "4.0.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d4ba9852b42210c7538b75484f9daa0655e9a3ac04f693747bb0f02cf3cfe16" +dependencies = [ + "cfg-if 1.0.0", + "fiat-crypto", + "packed_simd_2", + "platforms", + "subtle", + "zeroize", +] + +[[package]] +name = "cxx" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88abab2f5abbe4c56e8f1fb431b784d710b709888f35755a160e62e33fe38e8" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c0c11acd0e63bae27dcd2afced407063312771212b7a823b4fd72d633be30fb" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell 1.18.0", + "proc-macro2", + "quote", + "scratch", + "syn 2.0.32", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d3816ed957c008ccd4728485511e3d9aaf7db419aa321e3d2c5a2f3411e36c8" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26acccf6f445af85ea056362561a24ef56cdc15fcc685f03aec50b9c702cb6d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv 1.0.7", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "data-encoding" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" + +[[package]] +name = "data-encoding-macro" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c904b33cc60130e1aeea4956ab803d08a3f4a0ca82d64ed757afac3891f2bb99" +dependencies = [ + "data-encoding", + "data-encoding-macro-internal", +] + +[[package]] +name = "data-encoding-macro-internal" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fdf3fce3ce863539ec1d7fd1b6dcc3c645663376b43ed376bbf887733e4f772" +dependencies = [ + "data-encoding", + "syn 1.0.109", +] + +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid", + "der_derive", + "flagset", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "der-parser" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe398ac75057914d7d07307bf67dc7f3f574a26783b4fc7805a20ffa9f506e82" +dependencies = [ + "asn1-rs 0.3.1", + "displaydoc", + "nom", + "num-bigint 0.4.3", + "num-traits 0.2.16", + "rusticata-macros", +] + +[[package]] +name = "der-parser" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" +dependencies = [ + "asn1-rs 0.5.2", + "displaydoc", + "nom", + "num-bigint 0.4.3", + "num-traits 0.2.16", + "rusticata-macros", +] + +[[package]] +name = "der_derive" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ef71ddb5b3a1f53dee24817c8f70dfa1cb29e804c18d88c228d4bc9c86ee3b9" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive-syn-parse" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79116f119dd1dba1abf1f3405f03b9b0e79a27a3883864bfebded8a3dc768cd" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_builder" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07adf7be193b71cc36b193d0f5fe60b918a3a9db4dad0449f57bcfd519704a3" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f91d4cfa921f1c05904dc3c57b4a32c38aed3340cce209f3a6fd1478babafc4" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_builder_macro" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f0314b72bed045f3a68671b3c86328386762c93f82d98c65c3cb5e5f573dd68" +dependencies = [ + "derive_builder_core", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.4", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "directories" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "directories-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339ee130d97a610ea5a5872d2bbb130fdf68884ff09d3028b81bec8a1ac23bbc" +dependencies = [ + "cfg-if 1.0.0", + "dirs-sys-next", +] + +[[package]] +name = "dirs" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi 0.3.9", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi 0.3.9", +] + +[[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "dlv-list" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" + +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[package]] +name = "dtoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65d09067bfacaa79114679b279d7f5885b53295b1e2cfb4e79c8e4bd3d633169" + +[[package]] +name = "dyn-clonable" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e9232f0e607a262ceb9bd5141a3dfb3e4db6994b31989bbfd845878cba59fd4" +dependencies = [ + "dyn-clonable-impl", + "dyn-clone", +] + +[[package]] +name = "dyn-clonable-impl" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558e40ea573c374cf53507fd240b7ee2f5477df7cfebdb97323ec61c719399c5" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "dyn-clone" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "304e6508efa593091e97a9abbc10f90aa7ca635b6d2784feff3c89d41dd12272" + +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der 0.6.1", + "elliptic-curve 0.12.3", + "rfc6979 0.3.1", + "signature 1.6.4", +] + +[[package]] +name = "ecdsa" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" +dependencies = [ + "der 0.7.8", + "digest 0.10.7", + "elliptic-curve 0.13.5", + "rfc6979 0.4.0", + "signature 2.1.0", + "spki 0.7.2", +] + +[[package]] +name = "ed25519" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +dependencies = [ + "signature 1.6.4", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek 3.2.0", + "ed25519", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.193", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "ed25519-zebra" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" +dependencies = [ + "curve25519-dalek 3.2.0", + "hashbrown 0.12.3", + "hex", + "rand_core 0.6.4", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct 0.1.1", + "crypto-bigint 0.4.9", + "der 0.6.1", + "digest 0.10.7", + "ff 0.12.1", + "generic-array 0.14.7", + "group 0.12.1", + "hkdf", + "pem-rfc7468", + "pkcs8 0.9.0", + "rand_core 0.6.4", + "sec1 0.3.0", + "subtle", + "zeroize", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" +dependencies = [ + "base16ct 0.2.0", + "crypto-bigint 0.5.3", + "digest 0.10.7", + "ff 0.13.0", + "generic-array 0.14.7", + "group 0.13.0", + "pkcs8 0.10.2", + "rand_core 0.6.4", + "sec1 0.7.3", + "subtle", + "zeroize", +] + +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "enum-as-inner" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "enumflags2" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c041f5090df68b32bcd905365fd51769c8b9d553fe87fde0b683534f10c01bd2" +dependencies = [ + "enumflags2_derive", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "enumn" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48016319042fb7c87b78d2993084a831793a897a5cd1a2a67cab9d1eeb4b7d76" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "env_logger" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "atty", + "humantime", + "log 0.4.20", + "regex 1.9.5", + "termcolor", +] + +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log 0.4.20", + "regex 1.9.5", + "termcolor", +] + +[[package]] +name = "environmental" +version = "1.1.3" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "environmental" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48c92028aaa870e83d51c64e5d4e0b6981b360c522198c23959f219a4e1b15b" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "ethbloom" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" +dependencies = [ + "crunchy", + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde", + "scale-info", + "tiny-keccak", +] + +[[package]] +name = "ethereum" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a89fb87a9e103f71b903b80b670200b54cc67a07578f070681f1fffb7396fb7" +dependencies = [ + "bytes 1.4.0", + "ethereum-types", + "hash-db 0.15.2", + "hash256-std-hasher", + "parity-scale-codec", + "rlp", + "scale-info", + "serde 1.0.193", + "sha3", + "triehash", +] + +[[package]] +name = "ethereum-types" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" +dependencies = [ + "ethbloom", + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde", + "primitive-types", + "scale-info", + "uint", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "evm" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4448c65b71e8e2b9718232d84d09045eeaaccb2320494e6bd6dbf7e58fec8ff" +dependencies = [ + "auto_impl", + "environmental 1.1.4", + "ethereum", + "evm-core 0.37.0", + "evm-gasometer 0.37.0", + "evm-runtime 0.37.0", + "log 0.4.20", + "parity-scale-codec", + "primitive-types", + "rlp", + "scale-info", + "serde 1.0.193", + "sha3", +] + +[[package]] +name = "evm" +version = "0.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a49a4e11987c51220aa89dbe1a5cc877f5079fa6864c0a5b4533331db44e9365" +dependencies = [ + "auto_impl", + "environmental 1.1.4", + "ethereum", + "evm-core 0.39.0 (registry+https://github.com/rust-lang/crates.io-index)", + "evm-gasometer 0.39.0 (registry+https://github.com/rust-lang/crates.io-index)", + "evm-runtime 0.39.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.20", + "parity-scale-codec", + "primitive-types", + "rlp", + "scale-info", + "serde 1.0.193", + "sha3", +] + +[[package]] +name = "evm" +version = "0.39.1" +source = "git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65#b7b82c7e1fc57b7449d6dfa6826600de37cc1e65" +dependencies = [ + "auto_impl", + "environmental 1.1.4", + "ethereum", + "evm-core 0.39.0 (git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65)", + "evm-gasometer 0.39.0 (git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65)", + "evm-runtime 0.39.0 (git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65)", + "log 0.4.20", + "parity-scale-codec", + "primitive-types", + "rlp", + "scale-info", + "serde 1.0.193", + "sha3", +] + +[[package]] +name = "evm-core" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64c51bec0eb68a891c2575c758eaaa1d61373fc51f7caaf216b1fb5c3fea3b5d" +dependencies = [ + "parity-scale-codec", + "primitive-types", + "scale-info", + "serde 1.0.193", +] + +[[package]] +name = "evm-core" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f1f13264b044cb66f0602180f0bc781c29accb41ff560669a3ec15858d5b606" +dependencies = [ + "parity-scale-codec", + "primitive-types", + "scale-info", + "serde 1.0.193", +] + +[[package]] +name = "evm-core" +version = "0.39.0" +source = "git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65#b7b82c7e1fc57b7449d6dfa6826600de37cc1e65" +dependencies = [ + "parity-scale-codec", + "primitive-types", + "scale-info", + "serde 1.0.193", +] + +[[package]] +name = "evm-gasometer" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8b93c59c54fc26522d842f0e0d3f8e8be331c776df18ff3e540b53c2f64d509" +dependencies = [ + "environmental 1.1.4", + "evm-core 0.37.0", + "evm-runtime 0.37.0", + "primitive-types", +] + +[[package]] +name = "evm-gasometer" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d43eadc395bd1a52990787ca1495c26b0248165444912be075c28909a853b8c" +dependencies = [ + "environmental 1.1.4", + "evm-core 0.39.0 (registry+https://github.com/rust-lang/crates.io-index)", + "evm-runtime 0.39.0 (registry+https://github.com/rust-lang/crates.io-index)", + "primitive-types", +] + +[[package]] +name = "evm-gasometer" +version = "0.39.0" +source = "git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65#b7b82c7e1fc57b7449d6dfa6826600de37cc1e65" +dependencies = [ + "environmental 1.1.4", + "evm-core 0.39.0 (git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65)", + "evm-runtime 0.39.0 (git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65)", + "primitive-types", +] + +[[package]] +name = "evm-runtime" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c79b9459ce64f1a28688397c4013764ce53cd57bb84efc16b5187fa9b05b13ad" +dependencies = [ + "auto_impl", + "environmental 1.1.4", + "evm-core 0.37.0", + "primitive-types", + "sha3", +] + +[[package]] +name = "evm-runtime" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aa5b32f59ec582a5651978004e5c784920291263b7dcb6de418047438e37f4f" +dependencies = [ + "auto_impl", + "environmental 1.1.4", + "evm-core 0.39.0 (registry+https://github.com/rust-lang/crates.io-index)", + "primitive-types", + "sha3", +] + +[[package]] +name = "evm-runtime" +version = "0.39.0" +source = "git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65#b7b82c7e1fc57b7449d6dfa6826600de37cc1e65" +dependencies = [ + "auto_impl", + "environmental 1.1.4", + "evm-core 0.39.0 (git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65)", + "primitive-types", + "sha3", +] + +[[package]] +name = "evm-tracing-events" +version = "0.1.0" +source = "git+https://github.com/litentry/astar-frame?branch=polkadot-v0.9.42#d9a49c58f248f49e274b0730b8f4ef7f1e72c4b5" +dependencies = [ + "environmental 1.1.4", + "ethereum", + "ethereum-types", + "evm 0.37.0", + "evm-gasometer 0.37.0", + "evm-runtime 0.37.0", + "parity-scale-codec", + "sp-runtime-interface", +] + +[[package]] +name = "exit-future" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e43f2f1833d64e33f15592464d6fdd70f349dda7b1a53088eb83cd94014008c5" +dependencies = [ + "futures 0.3.28", +] + +[[package]] +name = "expander" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a718c0675c555c5f976fff4ea9e2c150fa06cefa201cadef87cfbf9324075881" +dependencies = [ + "blake3", + "fs-err", + "proc-macro2", + "quote", +] + +[[package]] +name = "expander" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3774182a5df13c3d1690311ad32fbe913feef26baba609fa2dd5f72042bd2ab6" +dependencies = [ + "blake2", + "fs-err", + "proc-macro2", + "quote", +] + +[[package]] +name = "expander" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f360349150728553f92e4c997a16af8915f418d3a0f21b440d34c5632f16ed84" +dependencies = [ + "blake2", + "fs-err", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "expander" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f86a749cf851891866c10515ef6c299b5c69661465e9c3bbe7e07a2b77fb0f7" +dependencies = [ + "blake2", + "fs-err", + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "failure" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" +dependencies = [ + "backtrace", + "failure_derive", +] + +[[package]] +name = "failure_derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fastrand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + +[[package]] +name = "fatality" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ad875162843b0d046276327afe0136e9ed3a23d5a754210fb6f1f33610d39ab" +dependencies = [ + "fatality-proc-macro", + "thiserror 1.0.44", +] + +[[package]] +name = "fatality-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5aa1e3ae159e592ad222dc90c5acbad632b527779ba88486abe92782ab268bd" +dependencies = [ + "expander 0.0.4", + "indexmap 1.9.3", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", + "thiserror 1.0.44", +] + +[[package]] +name = "fdlimit" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4c9e43643f5a3be4ca5b67d26b98031ff9db6806c3440ae32e02e3ceac3f1b" +dependencies = [ + "libc", +] + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" + +[[package]] +name = "file-per-thread-logger" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84f2e425d9790201ba4af4630191feac6dcc98765b118d4d18e91d23c2353866" +dependencies = [ + "env_logger 0.10.0", + "log 0.4.20", +] + +[[package]] +name = "filetime" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall 0.2.16", + "windows-sys 0.48.0", +] + +[[package]] +name = "finality-grandpa" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36530797b9bf31cd4ff126dcfee8170f86b00cfdcea3269d73133cc0415945c3" +dependencies = [ + "either", + "futures 0.3.28", + "futures-timer", + "log 0.4.20", + "num-traits 0.2.16", + "parity-scale-codec", + "parking_lot 0.12.1", + "scale-info", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder 1.4.3", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flagset" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda653ca797810c02f7ca4b804b40b8b95ae046eb989d356bce17919a8c25499" + +[[package]] +name = "flate2" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +dependencies = [ + "crc32fast", + "libz-sys", + "miniz_oxide", +] + +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits 0.2.16", +] + +[[package]] +name = "fnv" +version = "1.0.6" +source = "git+https://github.com/mesalock-linux/rust-fnv-sgx#c3bd6153c1403c1fa32fa54be5544d91f5efb017" +dependencies = [ + "hashbrown 0.3.1", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "fork-tree" +version = "3.0.0" +dependencies = [ + "parity-scale-codec", + "sgx_tstd", +] + +[[package]] +name = "fork-tree" +version = "3.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding 2.3.0", +] + +[[package]] +name = "fp-account" +version = "1.0.0-dev" +source = "git+https://github.com/integritee-network/frontier.git?branch=bar/polkadot-v0.9.42#a5a5e1e6ec08cd542a6084c310863150fb8841b1" +dependencies = [ + "hex", + "impl-serde", + "libsecp256k1", + "log 0.4.20", + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "fp-account" +version = "1.0.0-dev" +source = "git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42#2499d18c936edbcb7fcb711827db7abb9b4f4da4" +dependencies = [ + "hex", + "impl-serde", + "libsecp256k1", + "log 0.4.20", + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-runtime-interface", + "sp-std 5.0.0", +] + +[[package]] +name = "fp-consensus" +version = "2.0.0-dev" +source = "git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42#2499d18c936edbcb7fcb711827db7abb9b4f4da4" +dependencies = [ + "ethereum", + "parity-scale-codec", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "fp-ethereum" +version = "1.0.0-dev" +source = "git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42#2499d18c936edbcb7fcb711827db7abb9b4f4da4" +dependencies = [ + "ethereum", + "ethereum-types", + "fp-evm 3.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", + "frame-support", + "num_enum 0.6.1", + "parity-scale-codec", + "sp-std 5.0.0", +] + +[[package]] +name = "fp-evm" +version = "3.0.0-dev" +source = "git+https://github.com/integritee-network/frontier.git?branch=bar/polkadot-v0.9.42#a5a5e1e6ec08cd542a6084c310863150fb8841b1" +dependencies = [ + "evm 0.39.1 (registry+https://github.com/rust-lang/crates.io-index)", + "frame-support", + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "fp-evm" +version = "3.0.0-dev" +source = "git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42#2499d18c936edbcb7fcb711827db7abb9b4f4da4" +dependencies = [ + "evm 0.39.1 (git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65)", + "frame-support", + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "fp-rpc" +version = "3.0.0-dev" +source = "git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42#2499d18c936edbcb7fcb711827db7abb9b4f4da4" +dependencies = [ + "ethereum", + "ethereum-types", + "fp-evm 3.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-core", + "sp-runtime", + "sp-state-machine", + "sp-std 5.0.0", +] + +[[package]] +name = "fp-self-contained" +version = "1.0.0-dev" +source = "git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42#2499d18c936edbcb7fcb711827db7abb9b4f4da4" +dependencies = [ + "frame-support", + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "sp-runtime", +] + +[[package]] +name = "fp-storage" +version = "2.0.0" +source = "git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42#2499d18c936edbcb7fcb711827db7abb9b4f4da4" +dependencies = [ + "parity-scale-codec", + "serde 1.0.193", +] + +[[package]] +name = "fragile" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" + +[[package]] +name = "frame-benchmarking" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-support", + "frame-support-procedural", + "frame-system", + "linregress", + "log 0.4.20", + "parity-scale-codec", + "paste", + "scale-info", + "serde 1.0.193", + "sp-api", + "sp-application-crypto", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-runtime-interface", + "sp-std 5.0.0", + "sp-storage", + "static_assertions", +] + +[[package]] +name = "frame-election-provider-solution-type" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "frame-election-provider-support" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-election-provider-solution-type", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-npos-elections", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "frame-executive" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", + "sp-tracing", +] + +[[package]] +name = "frame-metadata" +version = "15.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "878babb0b136e731cc77ec2fd883ff02745ff21e6fb662729953d44923df009c" +dependencies = [ + "cfg-if 1.0.0", + "parity-scale-codec", + "scale-info", + "serde 1.0.193", +] + +[[package]] +name = "frame-support" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "bitflags 1.3.2", + "environmental 1.1.4", + "frame-metadata", + "frame-support-procedural", + "impl-trait-for-tuples", + "k256", + "log 0.4.20", + "once_cell 1.18.0", + "parity-scale-codec", + "paste", + "scale-info", + "serde 1.0.193", + "smallvec 1.11.0", + "sp-api", + "sp-arithmetic", + "sp-core", + "sp-core-hashing-proc-macro", + "sp-inherents", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-staking", + "sp-state-machine", + "sp-std 5.0.0", + "sp-tracing", + "sp-weights", + "tt-call", +] + +[[package]] +name = "frame-support-procedural" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "Inflector", + "cfg-expr", + "derive-syn-parse", + "frame-support-procedural-tools", + "itertools 0.10.5", + "proc-macro-warning", + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "frame-support-procedural-tools" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-support-procedural-tools-derive", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "frame-support-procedural-tools-derive" +version = "3.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "frame-system" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-support", + "log 0.4.20", + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", + "sp-version", + "sp-weights", +] + +[[package]] +name = "frame-system-benchmarking" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "frame-system-rpc-runtime-api" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "parity-scale-codec", + "sp-api", +] + +[[package]] +name = "frame-try-runtime" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-support", + "parity-scale-codec", + "sp-api", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "fs-err" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0845fa252299212f0389d64ba26f34fa32cfe41588355f21ed507c59a0f64541" + +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "fs4" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eeb4ed9e12f43b7fa0baae3f9cdda28352770132ef2e09a23760c29cae8bd47" +dependencies = [ + "rustix 0.38.4", + "windows-sys 0.48.0", +] + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags 1.3.2", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.8" +source = "git+https://github.com/mesalock-linux/futures-rs-sgx#d54882f24ddf7d61327a067b2f608d6940a36444" +dependencies = [ + "futures-channel 0.3.8", + "futures-core 0.3.8", + "futures-executor 0.3.8", + "futures-io 0.3.8", + "futures-sink 0.3.8", + "futures-task 0.3.8", + "futures-util 0.3.8", + "sgx_tstd", +] + +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel 0.3.28", + "futures-core 0.3.28", + "futures-executor 0.3.28", + "futures-io 0.3.28", + "futures-sink 0.3.28", + "futures-task 0.3.28", + "futures-util 0.3.28", +] + +[[package]] +name = "futures-channel" +version = "0.3.8" +source = "git+https://github.com/mesalock-linux/futures-rs-sgx#d54882f24ddf7d61327a067b2f608d6940a36444" +dependencies = [ + "futures-core 0.3.8", + "futures-sink 0.3.8", + "sgx_tstd", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core 0.3.28", + "futures-sink 0.3.28", +] + +[[package]] +name = "futures-core" +version = "0.3.8" +source = "git+https://github.com/mesalock-linux/futures-rs-sgx#d54882f24ddf7d61327a067b2f608d6940a36444" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-executor" +version = "0.3.8" +source = "git+https://github.com/mesalock-linux/futures-rs-sgx#d54882f24ddf7d61327a067b2f608d6940a36444" +dependencies = [ + "futures-core 0.3.8", + "futures-task 0.3.8", + "futures-util 0.3.8", + "sgx_tstd", +] + +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core 0.3.28", + "futures-task 0.3.28", + "futures-util 0.3.28", + "num_cpus", +] + +[[package]] +name = "futures-io" +version = "0.3.8" +source = "git+https://github.com/mesalock-linux/futures-rs-sgx#d54882f24ddf7d61327a067b2f608d6940a36444" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand 1.9.0", + "futures-core 0.3.28", + "futures-io 0.3.28", + "memchr 2.6.3", + "parking", + "pin-project-lite 0.2.10", + "waker-fn", +] + +[[package]] +name = "futures-macro" +version = "0.3.8" +source = "git+https://github.com/mesalock-linux/futures-rs-sgx#d54882f24ddf7d61327a067b2f608d6940a36444" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "futures-rustls" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2411eed028cdf8c8034eaf21f9915f956b6c3abec4d4c7949ee67f0721127bd" +dependencies = [ + "futures-io 0.3.28", + "rustls 0.20.8", + "webpki 0.22.0", +] + +[[package]] +name = "futures-sink" +version = "0.3.8" +source = "git+https://github.com/mesalock-linux/futures-rs-sgx#d54882f24ddf7d61327a067b2f608d6940a36444" + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.8" +source = "git+https://github.com/mesalock-linux/futures-rs-sgx#d54882f24ddf7d61327a067b2f608d6940a36444" +dependencies = [ + "once_cell 1.4.0", + "sgx_tstd", +] + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" + +[[package]] +name = "futures-util" +version = "0.3.8" +source = "git+https://github.com/mesalock-linux/futures-rs-sgx#d54882f24ddf7d61327a067b2f608d6940a36444" +dependencies = [ + "futures-channel 0.3.8", + "futures-core 0.3.8", + "futures-io 0.3.8", + "futures-macro 0.3.8", + "futures-sink 0.3.8", + "futures-task 0.3.8", + "memchr 2.2.1", + "pin-project-lite 0.2.10", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "sgx_tstd", + "slab 0.4.2", +] + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-channel 0.3.28", + "futures-core 0.3.28", + "futures-io 0.3.28", + "futures-macro 0.3.28", + "futures-sink 0.3.28", + "futures-task 0.3.28", + "memchr 2.6.3", + "pin-project-lite 0.2.10", + "pin-utils", + "slab 0.4.8", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder 1.4.3", +] + +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum 1.16.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum 1.16.0 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.1.14" +source = "git+https://github.com/mesalock-linux/getrandom-sgx#0aa9cc20c7dea713ccaac2c44430d625a395ebae" +dependencies = [ + "cfg-if 0.1.10", + "sgx_libc", + "sgx_trts", + "sgx_tstd", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "ghash" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" +dependencies = [ + "opaque-debug 0.3.0", + "polyval 0.5.3", +] + +[[package]] +name = "ghash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +dependencies = [ + "opaque-debug 0.3.0", + "polyval 0.6.1", +] + +[[package]] +name = "gimli" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +dependencies = [ + "fallible-iterator", + "indexmap 1.9.3", + "stable_deref_trait", +] + +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "globset" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1391ab1f92ffcc08911957149833e682aa3fe252b9f45f966d2ef972274c97df" +dependencies = [ + "aho-corasick", + "bstr", + "fnv 1.0.7", + "log 0.4.20", + "regex 1.9.5", +] + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff 0.12.1", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff 0.13.0", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "h2" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" +dependencies = [ + "bytes 1.4.0", + "fnv 1.0.7", + "futures-core 0.3.28", + "futures-sink 0.3.28", + "futures-util 0.3.28", + "http 0.2.9", + "indexmap 1.9.3", + "slab 0.4.8", + "tokio", + "tokio-util 0.7.8", + "tracing", +] + +[[package]] +name = "hash-db" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" + +[[package]] +name = "hash-db" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e7d7786361d7425ae2fe4f9e407eb0efaa0840f5212d109cc018c40c35c6ab4" + +[[package]] +name = "hash256-std-hasher" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2" +dependencies = [ + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29fba9abe4742d586dfd0c06ae4f7e73a1c2d86b856933509b269d82cdf06e18" + +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.6", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.3", +] + +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + +[[package]] +name = "hashbrown_tstd" +version = "0.12.0" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" + +[[package]] +name = "hdrhistogram" +version = "7.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f19b9f54f7c7f55e31401bb647626ce0cf0f67b0004982ce815b3ee72a02aa8" +dependencies = [ + "base64 0.13.1", + "byteorder 1.4.3", + "crossbeam-channel", + "flate2", + "nom", + "num-traits 0.2.16", +] + +[[package]] +name = "headers" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" +dependencies = [ + "base64 0.13.1", + "bitflags 1.3.2", + "bytes 1.4.0", + "headers-core", + "http 0.2.9", + "httpdate", + "mime", + "sha1 0.10.5", +] + +[[package]] +name = "headers-core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +dependencies = [ + "http 0.2.9", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde 1.0.193", +] + +[[package]] +name = "hex-conservative" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ed443af458ccb6d81c1e7e661545f94d3176752fb1df2f543b902a1e0f51e2" +dependencies = [ + "core2 0.3.3", +] + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hex_lit" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" + +[[package]] +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac 0.12.1", +] + +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac 0.8.0", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac 0.11.1", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "hmac-drbg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +dependencies = [ + "digest 0.9.0", + "generic-array 0.14.7", + "hmac 0.8.1", +] + +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi 0.3.9", +] + +[[package]] +name = "http" +version = "0.2.1" +source = "git+https://github.com/integritee-network/http-sgx.git?branch=sgx-experimental#307b5421fb7a489a114bede0dc05c8d32b804f49" +dependencies = [ + "bytes 1.0.1", + "fnv 1.0.6", + "itoa 0.4.5", + "sgx_tstd", +] + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes 1.4.0", + "fnv 1.0.7", + "itoa 1.0.9", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes 1.4.0", + "http 0.2.9", + "pin-project-lite 0.2.10", +] + +[[package]] +name = "http-range-header" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" + +[[package]] +name = "http_req" +version = "0.8.1" +source = "git+https://github.com/integritee-network/http_req?branch=master#3723e88235f2b29bc1a31835853b072ffd0455fd" +dependencies = [ + "log 0.4.20", + "rustls 0.19.1", + "unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "webpki 0.21.4 (registry+https://github.com/rust-lang/crates.io-index)", + "webpki-roots 0.21.1", +] + +[[package]] +name = "http_req" +version = "0.8.1" +source = "git+https://github.com/integritee-network/http_req#3723e88235f2b29bc1a31835853b072ffd0455fd" +dependencies = [ + "log 0.4.20", + "rustls 0.19.0 (git+https://github.com/mesalock-linux/rustls?branch=mesalock_sgx)", + "sgx_tstd", + "unicase 2.6.0 (git+https://github.com/mesalock-linux/unicase-sgx)", + "webpki 0.21.4 (git+https://github.com/mesalock-linux/webpki?branch=mesalock_sgx)", + "webpki-roots 0.21.0 (git+https://github.com/mesalock-linux/webpki-roots?branch=mesalock_sgx)", +] + +[[package]] +name = "httparse" +version = "1.4.1" +source = "git+https://github.com/integritee-network/httparse-sgx?branch=sgx-experimental#cc97e4b34d2c44a1e3df5bdebef446b9771f5cc3" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +dependencies = [ + "bytes 1.4.0", + "futures-channel 0.3.28", + "futures-core 0.3.28", + "futures-util 0.3.28", + "h2", + "http 0.2.9", + "http-body", + "httparse 1.8.0", + "httpdate", + "itoa 1.0.9", + "pin-project-lite 0.2.10", + "socket2 0.4.9", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-multipart-rfc7578" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538ce6aeb81f7cd0d547a42435944d2283714a3f696630318bc47bd839fcfc9" +dependencies = [ + "bytes 1.4.0", + "common-multipart-rfc7578", + "futures 0.3.28", + "http 0.2.9", + "hyper", +] + +[[package]] +name = "hyper-rustls" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64" +dependencies = [ + "ct-logs", + "futures-util 0.3.28", + "hyper", + "log 0.4.20", + "rustls 0.19.1", + "rustls-native-certs 0.5.0", + "tokio", + "tokio-rustls 0.22.0", + "webpki 0.21.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "hyper-rustls" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" +dependencies = [ + "http 0.2.9", + "hyper", + "log 0.4.20", + "rustls 0.20.8", + "rustls-native-certs 0.6.3", + "tokio", + "tokio-rustls 0.23.4", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes 1.4.0", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows 0.48.0", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.2.0" +source = "git+https://github.com/mesalock-linux/rust-url-sgx?tag=sgx_1.1.3#23832f3191456c2d4a0faab10952e1747be58ca8" +dependencies = [ + "matches 0.1.8", + "sgx_tstd", + "unicode-bidi 0.3.4", + "unicode-normalization 0.1.12", +] + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches 0.1.10", + "unicode-bidi 0.3.13", + "unicode-normalization 0.1.22", +] + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi 0.3.13", + "unicode-normalization 0.1.22", +] + +[[package]] +name = "if-addrs" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc0fa01ffc752e9dbc72818cdb072cd028b86be5e09dd04c5a643704fe101a9" +dependencies = [ + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "if-watch" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9465340214b296cd17a0009acdb890d6160010b8adf8f78a00d0d7ab270f79f" +dependencies = [ + "async-io", + "core-foundation", + "fnv 1.0.7", + "futures 0.3.28", + "if-addrs", + "ipnet", + "log 0.4.20", + "rtnetlink", + "system-configuration", + "tokio", + "windows 0.34.0", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-rlp" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" +dependencies = [ + "rlp", +] + +[[package]] +name = "impl-serde" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" +dependencies = [ + "serde 1.0.193", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "indexmap" +version = "1.6.1" +source = "git+https://github.com/mesalock-linux/indexmap-sgx#19f52458ba64dd7349a5d3a62227619a17e4db85" +dependencies = [ + "autocfg 1.1.0", + "hashbrown 0.9.1", + "sgx_tstd", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg 1.1.0", + "hashbrown 0.12.3", + "serde 1.0.193", +] + +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "integer-encoding" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" + +[[package]] +name = "integer-sqrt" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" +dependencies = [ + "num-traits 0.2.16", +] + +[[package]] +name = "interceptor" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e8a11ae2da61704edada656798b61c94b35ecac2c58eb955156987d5e6be90b" +dependencies = [ + "async-trait", + "bytes 1.4.0", + "log 0.4.20", + "rand 0.8.5", + "rtcp", + "rtp", + "thiserror 1.0.44", + "tokio", + "waitgroup", + "webrtc-srtp", + "webrtc-util", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.2", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "git+https://github.com/mesalock-linux/iovec-sgx#5c2f8e81925b4c06c556d856f3237461b00e27c9" +dependencies = [ + "sgx_libc", +] + +[[package]] +name = "ip_network" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2f047c0a98b2f299aa5d6d7088443570faae494e9ae1305e48be000c9e0eb1" + +[[package]] +name = "ipconfig" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" +dependencies = [ + "socket2 0.5.3", + "widestring", + "windows-sys 0.48.0", + "winreg", +] + +[[package]] +name = "ipfs-api" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3824538e42e84c792988098df4ad5a35b47be98b19e31454e09f4e322f00fc" +dependencies = [ + "bytes 1.4.0", + "dirs", + "failure", + "futures 0.3.28", + "http 0.2.9", + "hyper", + "hyper-multipart-rfc7578", + "hyper-tls", + "parity-multiaddr", + "serde 1.0.193", + "serde_json 1.0.103", + "serde_urlencoded", + "tokio", + "tokio-util 0.6.10", + "tracing", + "typed-builder", + "walkdir", +] + +[[package]] +name = "ipnet" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" + +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi 0.3.2", + "rustix 0.38.4", + "windows-sys 0.48.0", +] + +[[package]] +name = "ita-oracle" +version = "0.9.0" +dependencies = [ + "itc-rest-client", + "itp-enclave-metrics", + "itp-ocall-api", + "lazy_static", + "log 0.4.20", + "parity-scale-codec", + "serde 1.0.193", + "sgx_tstd", + "substrate-fixed 0.5.9 (git+https://github.com/encointer/substrate-fixed?tag=v0.5.9)", + "thiserror 1.0.44", + "thiserror 1.0.9", + "url 2.1.1", + "url 2.4.0", +] + +[[package]] +name = "ita-parentchain-interface" +version = "0.9.0" +dependencies = [ + "bs58", + "env_logger 0.9.3", + "ita-sgx-runtime", + "ita-stf", + "itc-parentchain-indirect-calls-executor", + "itc-parentchain-test", + "itp-api-client-types", + "itp-node-api", + "itp-sgx-crypto", + "itp-stf-executor", + "itp-stf-primitives", + "itp-test", + "itp-top-pool-author", + "itp-types", + "itp-utils", + "lc-scheduled-enclave", + "litentry-primitives", + "log 0.4.20", + "parity-scale-codec", + "sgx_tstd", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "ita-sgx-runtime" +version = "0.9.0" +dependencies = [ + "frame-executive", + "frame-support", + "frame-system", + "itp-sgx-runtime-primitives", + "pallet-balances", + "pallet-evm 6.0.0-dev (git+https://github.com/integritee-network/frontier.git?branch=bar/polkadot-v0.9.42)", + "pallet-parentchain", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", + "sp-version", +] + +[[package]] +name = "ita-stf" +version = "0.9.0" +dependencies = [ + "frame-support", + "frame-system", + "hex", + "hex-literal", + "ita-sgx-runtime", + "itp-hashing", + "itp-node-api", + "itp-node-api-metadata", + "itp-node-api-metadata-provider", + "itp-sgx-externalities", + "itp-stf-interface", + "itp-stf-primitives", + "itp-storage", + "itp-types", + "itp-utils", + "litentry-primitives", + "log 0.4.20", + "pallet-balances", + "pallet-parentchain", + "pallet-sudo", + "parity-scale-codec", + "rlp", + "sgx_tstd", + "sha3", + "sp-core", + "sp-io 7.0.0", + "sp-keyring", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "itc-direct-rpc-client" +version = "0.1.0" +dependencies = [ + "itp-rpc", + "itp-types", + "itp-utils", + "litentry-primitives", + "log 0.4.20", + "parity-scale-codec", + "rustls 0.19.0 (git+https://github.com/mesalock-linux/rustls?tag=sgx_1.1.3)", + "rustls 0.19.1", + "serde_json 1.0.103", + "sgx_tstd", + "tungstenite 0.14.0", + "tungstenite 0.15.0", + "url 2.1.1", + "url 2.4.0", + "webpki 0.21.4 (registry+https://github.com/rust-lang/crates.io-index)", + "webpki 0.21.4 (git+https://github.com/mesalock-linux/webpki?branch=mesalock_sgx)", +] + +[[package]] +name = "itc-direct-rpc-server" +version = "0.9.0" +dependencies = [ + "itc-tls-websocket-server", + "itp-rpc", + "itp-types", + "itp-utils", + "jsonrpc-core 18.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 18.0.0 (git+https://github.com/scs/jsonrpc?branch=no_std_v18)", + "log 0.4.20", + "parity-scale-codec", + "serde_json 1.0.103", + "sgx_tstd", + "sp-runtime", + "thiserror 1.0.44", + "thiserror 1.0.9", +] + +[[package]] +name = "itc-offchain-worker-executor" +version = "0.9.0" +dependencies = [ + "itc-parentchain-light-client", + "itp-extrinsics-factory", + "itp-sgx-externalities", + "itp-stf-executor", + "itp-stf-interface", + "itp-stf-primitives", + "itp-stf-state-handler", + "itp-test", + "itp-top-pool-author", + "itp-types", + "log 0.4.20", + "parity-scale-codec", + "sgx_tstd", + "sp-core", + "sp-runtime", + "thiserror 1.0.44", + "thiserror 1.0.9", +] + +[[package]] +name = "itc-parentchain" +version = "0.9.0" +dependencies = [ + "itc-parentchain-block-import-dispatcher", + "itc-parentchain-block-importer", + "itc-parentchain-indirect-calls-executor", + "itc-parentchain-light-client", + "itp-types", + "parity-scale-codec", + "sp-runtime", +] + +[[package]] +name = "itc-parentchain-block-import-dispatcher" +version = "0.9.0" +dependencies = [ + "itc-parentchain-block-importer", + "itp-import-queue", + "log 0.4.20", + "sgx_tstd", + "sgx_types", + "thiserror 1.0.44", + "thiserror 1.0.9", +] + +[[package]] +name = "itc-parentchain-block-importer" +version = "0.9.0" +dependencies = [ + "ita-stf", + "itc-parentchain-indirect-calls-executor", + "itc-parentchain-light-client", + "itp-enclave-metrics", + "itp-extrinsics-factory", + "itp-ocall-api", + "itp-stf-executor", + "itp-types", + "log 0.4.20", + "parity-scale-codec", + "sgx_tstd", + "sgx_types", + "sp-runtime", + "thiserror 1.0.44", + "thiserror 1.0.9", +] + +[[package]] +name = "itc-parentchain-indirect-calls-executor" +version = "0.9.0" +dependencies = [ + "binary-merkle-tree", + "bs58", + "core-primitives", + "env_logger 0.9.3", + "futures 0.3.28", + "futures 0.3.8", + "itc-parentchain-test", + "itp-api-client-types", + "itp-node-api", + "itp-sgx-crypto", + "itp-sgx-runtime-primitives", + "itp-stf-executor", + "itp-stf-primitives", + "itp-test", + "itp-top-pool-author", + "itp-types", + "itp-utils", + "lc-scheduled-enclave", + "litentry-primitives", + "log 0.4.20", + "parity-scale-codec", + "sgx_tstd", + "sgx_types", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", + "thiserror 1.0.44", + "thiserror 1.0.9", +] + +[[package]] +name = "itc-parentchain-light-client" +version = "0.9.0" +dependencies = [ + "finality-grandpa", + "itc-parentchain-test", + "itp-ocall-api", + "itp-sgx-io", + "itp-sgx-temp-dir", + "itp-storage", + "itp-test", + "itp-types", + "log 0.4.20", + "parity-scale-codec", + "sgx_tstd", + "sgx_types", + "sp-consensus-grandpa", + "sp-runtime", + "thiserror 1.0.44", + "thiserror 1.0.9", +] + +[[package]] +name = "itc-parentchain-test" +version = "0.9.0" +dependencies = [ + "itp-types", + "sp-runtime", +] + +[[package]] +name = "itc-peer-top-broadcaster" +version = "0.1.0" +dependencies = [ + "itc-direct-rpc-client", + "itc-direct-rpc-server", + "itp-rpc", + "itp-stf-primitives", + "itp-types", + "itp-utils", + "litentry-primitives", + "log 0.4.20", + "sgx_tstd", +] + +[[package]] +name = "itc-rest-client" +version = "0.9.0" +dependencies = [ + "base64 0.13.1", + "http 0.2.1", + "http 0.2.9", + "http_req 0.8.1 (git+https://github.com/integritee-network/http_req?branch=master)", + "http_req 0.8.1 (git+https://github.com/integritee-network/http_req)", + "log 0.4.20", + "serde 1.0.193", + "serde_json 1.0.103", + "sgx_tstd", + "thiserror 1.0.44", + "thiserror 1.0.9", + "url 2.1.1", + "url 2.4.0", +] + +[[package]] +name = "itc-rpc-client" +version = "0.9.0" +dependencies = [ + "base58", + "env_logger 0.9.3", + "frame-metadata", + "ita-stf", + "itc-tls-websocket-server", + "itp-api-client-types", + "itp-networking-utils", + "itp-rpc", + "itp-stf-primitives", + "itp-types", + "itp-utils", + "litentry-primitives", + "log 0.4.20", + "openssl", + "parity-scale-codec", + "parking_lot 0.12.1", + "rustls 0.19.1", + "serde_json 1.0.103", + "sgx_crypto_helper", + "sp-core", + "teerex-primitives", + "thiserror 1.0.44", + "url 2.4.0", + "ws", +] + +[[package]] +name = "itc-rpc-server" +version = "0.9.0" +dependencies = [ + "anyhow", + "env_logger 0.10.0", + "itp-enclave-api", + "itp-rpc", + "itp-utils", + "its-peer-fetch", + "its-primitives", + "its-rpc-handler", + "its-storage", + "its-test", + "jsonrpsee 0.2.0", + "log 0.4.20", + "parity-scale-codec", + "sp-core", + "tokio", +] + +[[package]] +name = "itc-tls-websocket-server" +version = "0.9.0" +dependencies = [ + "bit-vec", + "chrono 0.4.26", + "env_logger 0.9.3", + "log 0.4.20", + "mio 0.6.21", + "mio 0.6.23", + "mio-extras 2.0.6 (git+https://github.com/integritee-network/mio-extras-sgx?rev=963234b)", + "rcgen 0.9.2", + "rustls 0.19.0 (git+https://github.com/mesalock-linux/rustls?branch=mesalock_sgx)", + "rustls 0.19.1", + "sgx_tstd", + "sp-core", + "thiserror 1.0.44", + "thiserror 1.0.9", + "tungstenite 0.14.0", + "tungstenite 0.15.0", + "url 2.4.0", + "webpki 0.21.4 (registry+https://github.com/rust-lang/crates.io-index)", + "webpki 0.21.4 (git+https://github.com/mesalock-linux/webpki?branch=mesalock_sgx)", + "yasna 0.3.1", + "yasna 0.4.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.5" +source = "git+https://github.com/mesalock-linux/itoa-sgx#295ee451f5ec74f25c299552b481beb445ea3eb7" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "itp-api-client-extensions" +version = "0.9.0" +dependencies = [ + "hex", + "itp-api-client-types", + "itp-types", + "sp-consensus-grandpa", + "sp-core", + "sp-runtime", + "substrate-api-client", +] + +[[package]] +name = "itp-api-client-types" +version = "0.9.0" +dependencies = [ + "itp-types", + "rococo-parachain-runtime", + "sp-runtime", + "substrate-api-client", +] + +[[package]] +name = "itp-attestation-handler" +version = "0.8.0" +dependencies = [ + "arrayvec 0.7.4", + "base64 0.13.0 (git+https://github.com/mesalock-linux/rust-base64-sgx?rev=sgx_1.1.3)", + "base64 0.13.1", + "bit-vec", + "chrono 0.4.11", + "chrono 0.4.26", + "hex", + "httparse 1.4.1", + "itertools 0.10.5", + "itp-ocall-api", + "itp-settings", + "itp-sgx-crypto", + "itp-sgx-io", + "itp-time-utils", + "log 0.4.20", + "num-bigint 0.2.5", + "parity-scale-codec", + "rustls 0.19.0 (git+https://github.com/mesalock-linux/rustls?rev=sgx_1.1.3)", + "rustls 0.19.1", + "serde_json 1.0.103", + "serde_json 1.0.60 (git+https://github.com/mesalock-linux/serde-json-sgx?tag=sgx_1.1.3)", + "sgx_rand", + "sgx_tcrypto", + "sgx_tse", + "sgx_tstd", + "sgx_types", + "sp-core", + "thiserror 1.0.44", + "thiserror 1.0.9", + "webpki 0.21.4 (registry+https://github.com/rust-lang/crates.io-index)", + "webpki 0.21.4 (git+https://github.com/mesalock-linux/webpki?branch=mesalock_sgx)", + "webpki-roots 0.21.0 (git+https://github.com/mesalock-linux/webpki-roots?branch=mesalock_sgx)", + "yasna 0.3.1", +] + +[[package]] +name = "itp-component-container" +version = "0.8.0" +dependencies = [ + "sgx_tstd", + "thiserror 1.0.44", + "thiserror 1.0.9", +] + +[[package]] +name = "itp-enclave-api" +version = "0.9.0" +dependencies = [ + "frame-support", + "hex", + "itc-parentchain", + "itp-enclave-api-ffi", + "itp-settings", + "itp-storage", + "itp-types", + "log 0.4.20", + "parity-scale-codec", + "serde_json 1.0.103", + "sgx_crypto_helper", + "sgx_types", + "sgx_urts", + "sp-core", + "sp-runtime", + "teerex-primitives", + "thiserror 1.0.44", +] + +[[package]] +name = "itp-enclave-api-ffi" +version = "0.9.0" +dependencies = [ + "sgx_types", +] + +[[package]] +name = "itp-enclave-metrics" +version = "0.9.0" +dependencies = [ + "parity-scale-codec", + "sgx_tstd", + "substrate-fixed 0.5.9 (git+https://github.com/encointer/substrate-fixed?tag=v0.5.9)", +] + +[[package]] +name = "itp-extrinsics-factory" +version = "0.9.0" +dependencies = [ + "itp-node-api", + "itp-nonce-cache", + "itp-types", + "log 0.4.20", + "parity-scale-codec", + "sgx_tstd", + "sgx_types", + "sp-core", + "sp-runtime", + "substrate-api-client", + "thiserror 1.0.44", + "thiserror 1.0.9", +] + +[[package]] +name = "itp-hashing" +version = "0.9.0" +dependencies = [ + "sp-core", +] + +[[package]] +name = "itp-import-queue" +version = "0.8.0" +dependencies = [ + "sgx_tstd", + "sgx_types", + "thiserror 1.0.44", + "thiserror 1.0.9", +] + +[[package]] +name = "itp-networking-utils" +version = "0.9.0" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "itp-node-api" +version = "0.9.0" +dependencies = [ + "itp-api-client-extensions", + "itp-api-client-types", + "itp-node-api-factory", + "itp-node-api-metadata", + "itp-node-api-metadata-provider", +] + +[[package]] +name = "itp-node-api-factory" +version = "0.9.0" +dependencies = [ + "itp-api-client-types", + "sp-core", + "thiserror 1.0.44", +] + +[[package]] +name = "itp-node-api-metadata" +version = "0.9.0" +dependencies = [ + "derive_more", + "itp-api-client-types", + "itp-stf-primitives", + "parity-scale-codec", + "sp-core", +] + +[[package]] +name = "itp-node-api-metadata-provider" +version = "0.9.0" +dependencies = [ + "itp-node-api-metadata", + "itp-stf-primitives", + "sgx_tstd", + "thiserror 1.0.44", + "thiserror 1.0.9", +] + +[[package]] +name = "itp-nonce-cache" +version = "0.8.0" +dependencies = [ + "sgx_tstd", + "thiserror 1.0.44", + "thiserror 1.0.9", +] + +[[package]] +name = "itp-ocall-api" +version = "0.9.0" +dependencies = [ + "derive_more", + "itp-storage", + "itp-types", + "parity-scale-codec", + "sgx_types", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "itp-primitives-cache" +version = "0.9.0" +dependencies = [ + "lazy_static", + "sgx_tstd", + "thiserror 1.0.44", + "thiserror 1.0.9", +] + +[[package]] +name = "itp-rpc" +version = "0.9.0" +dependencies = [ + "itp-types", + "parity-scale-codec", + "serde 1.0.193", + "serde_json 1.0.103", + "sgx_tstd", +] + +[[package]] +name = "itp-settings" +version = "0.9.0" + +[[package]] +name = "itp-sgx-crypto" +version = "0.9.0" +dependencies = [ + "aes 0.6.0", + "derive_more", + "itp-sgx-io", + "itp-sgx-temp-dir", + "log 0.4.20", + "ofb", + "parity-scale-codec", + "serde_json 1.0.103", + "serde_json 1.0.60 (git+https://github.com/mesalock-linux/serde-json-sgx?tag=sgx_1.1.3)", + "sgx_crypto_helper", + "sgx_rand", + "sgx_tstd", + "sgx_types", + "sp-core", +] + +[[package]] +name = "itp-sgx-externalities" +version = "0.9.0" +dependencies = [ + "derive_more", + "environmental 1.1.3", + "itp-hashing", + "itp-storage", + "log 0.4.20", + "parity-scale-codec", + "postcard", + "serde 1.0.193", + "sgx_tstd", + "sp-core", +] + +[[package]] +name = "itp-sgx-io" +version = "0.8.0" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "itp-sgx-runtime-primitives" +version = "0.9.0" +dependencies = [ + "frame-system", + "litentry-primitives", + "pallet-balances", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "itp-sgx-temp-dir" +version = "0.1.0" +dependencies = [ + "lazy_static", + "safe-lock", + "sgx_tstd", +] + +[[package]] +name = "itp-stf-executor" +version = "0.9.0" +dependencies = [ + "hex", + "itc-parentchain-test", + "itp-enclave-metrics", + "itp-node-api", + "itp-ocall-api", + "itp-sgx-crypto", + "itp-sgx-externalities", + "itp-stf-interface", + "itp-stf-primitives", + "itp-stf-state-handler", + "itp-stf-state-observer", + "itp-test", + "itp-time-utils", + "itp-top-pool", + "itp-top-pool-author", + "itp-types", + "litentry-primitives", + "log 0.4.20", + "parity-scale-codec", + "sgx_tstd", + "sgx_types", + "sp-core", + "sp-runtime", + "thiserror 1.0.44", + "thiserror 1.0.9", +] + +[[package]] +name = "itp-stf-interface" +version = "0.8.0" +dependencies = [ + "itp-node-api-metadata", + "itp-node-api-metadata-provider", + "itp-stf-primitives", + "itp-types", + "parity-scale-codec", +] + +[[package]] +name = "itp-stf-primitives" +version = "0.9.0" +dependencies = [ + "derive_more", + "itp-sgx-runtime-primitives", + "litentry-primitives", + "parity-scale-codec", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "itp-stf-state-handler" +version = "0.9.0" +dependencies = [ + "itp-hashing", + "itp-settings", + "itp-sgx-crypto", + "itp-sgx-externalities", + "itp-sgx-io", + "itp-sgx-temp-dir", + "itp-stf-interface", + "itp-stf-state-observer", + "itp-time-utils", + "itp-types", + "log 0.4.20", + "parity-scale-codec", + "rust-base58 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-base58 0.0.4 (git+https://github.com/mesalock-linux/rust-base58-sgx?rev=sgx_1.1.3)", + "sgx_tstd", + "sgx_types", + "sp-core", + "thiserror 1.0.44", + "thiserror 1.0.9", +] + +[[package]] +name = "itp-stf-state-observer" +version = "0.9.0" +dependencies = [ + "itp-types", + "log 0.4.20", + "sgx_tstd", + "thiserror 1.0.44", + "thiserror 1.0.9", +] + +[[package]] +name = "itp-storage" +version = "0.9.0" +dependencies = [ + "derive_more", + "frame-metadata", + "frame-support", + "hash-db 0.15.2", + "itp-types", + "parity-scale-codec", + "sgx_tstd", + "sp-core", + "sp-runtime", + "sp-state-machine", + "sp-std 5.0.0", + "sp-trie", + "thiserror 1.0.44", + "thiserror 1.0.9", +] + +[[package]] +name = "itp-teerex-storage" +version = "0.9.0" +dependencies = [ + "itp-storage", + "sp-std 5.0.0", +] + +[[package]] +name = "itp-test" +version = "0.9.0" +dependencies = [ + "hex", + "itp-node-api", + "itp-node-api-metadata-provider", + "itp-ocall-api", + "itp-sgx-crypto", + "itp-sgx-externalities", + "itp-stf-interface", + "itp-stf-primitives", + "itp-stf-state-handler", + "itp-storage", + "itp-teerex-storage", + "itp-time-utils", + "itp-types", + "jsonrpc-core 18.0.0 (git+https://github.com/scs/jsonrpc?branch=no_std_v18)", + "litentry-primitives", + "log 0.4.20", + "parity-scale-codec", + "sgx_crypto_helper", + "sgx_tstd", + "sgx_types", + "sp-core", + "sp-io 7.0.0", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "itp-time-utils" +version = "0.9.0" +dependencies = [ + "chrono 0.4.11", + "chrono 0.4.26", + "sgx_tstd", +] + +[[package]] +name = "itp-top-pool" +version = "0.9.0" +dependencies = [ + "byteorder 1.4.3", + "derive_more", + "itc-direct-rpc-server", + "itp-stf-primitives", + "itp-test", + "itp-types", + "its-primitives", + "jsonrpc-core 18.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 18.0.0 (git+https://github.com/scs/jsonrpc?branch=no_std_v18)", + "linked-hash-map 0.5.2", + "linked-hash-map 0.5.6", + "litentry-primitives", + "log 0.4.20", + "parity-scale-codec", + "parity-util-mem", + "serde 1.0.193", + "sgx_tstd", + "sp-application-crypto", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "itp-top-pool-author" +version = "0.9.0" +dependencies = [ + "derive_more", + "futures 0.3.28", + "itp-enclave-metrics", + "itp-ocall-api", + "itp-sgx-crypto", + "itp-stf-primitives", + "itp-stf-state-handler", + "itp-test", + "itp-top-pool", + "itp-types", + "itp-utils", + "jsonrpc-core 18.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 18.0.0 (git+https://github.com/scs/jsonrpc?branch=no_std_v18)", + "lazy_static", + "litentry-primitives", + "log 0.4.20", + "parity-scale-codec", + "sgx_crypto_helper", + "sgx_tstd", + "sp-core", + "sp-keyring", + "sp-runtime", +] + +[[package]] +name = "itp-types" +version = "0.9.0" +dependencies = [ + "frame-system", + "itp-sgx-crypto", + "itp-sgx-runtime-primitives", + "itp-stf-primitives", + "itp-utils", + "litentry-primitives", + "pallet-balances", + "parity-scale-codec", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", + "substrate-api-client", +] + +[[package]] +name = "itp-utils" +version = "0.9.0" +dependencies = [ + "hex", + "parity-scale-codec", +] + +[[package]] +name = "its-block-composer" +version = "0.9.0" +dependencies = [ + "itp-node-api", + "itp-settings", + "itp-sgx-crypto", + "itp-sgx-externalities", + "itp-stf-executor", + "itp-stf-primitives", + "itp-time-utils", + "itp-top-pool-author", + "itp-types", + "its-primitives", + "its-state", + "log 0.4.20", + "parity-scale-codec", + "sgx_tstd", + "sgx_types", + "sp-core", + "sp-runtime", + "thiserror 1.0.44", + "thiserror 1.0.9", +] + +[[package]] +name = "its-block-verification" +version = "0.9.0" +dependencies = [ + "frame-support", + "itc-parentchain-test", + "itp-types", + "itp-utils", + "its-primitives", + "its-test", + "log 0.4.20", + "sgx_tstd", + "sp-consensus-slots", + "sp-core", + "sp-keyring", + "sp-runtime", + "thiserror 1.0.44", + "thiserror 1.0.9", +] + +[[package]] +name = "its-consensus-aura" +version = "0.9.0" +dependencies = [ + "env_logger 0.9.3", + "finality-grandpa", + "ita-stf", + "itc-parentchain-block-import-dispatcher", + "itc-parentchain-test", + "itc-peer-top-broadcaster", + "itp-enclave-metrics", + "itp-ocall-api", + "itp-settings", + "itp-sgx-crypto", + "itp-sgx-externalities", + "itp-stf-executor", + "itp-stf-primitives", + "itp-stf-state-handler", + "itp-storage", + "itp-test", + "itp-time-utils", + "itp-top-pool-author", + "itp-types", + "itp-utils", + "its-block-composer", + "its-block-verification", + "its-consensus-common", + "its-consensus-slots", + "its-primitives", + "its-state", + "its-test", + "its-validateer-fetch", + "lc-scheduled-enclave", + "log 0.4.20", + "parity-scale-codec", + "sgx_tstd", + "sp-core", + "sp-keyring", + "sp-runtime", +] + +[[package]] +name = "its-consensus-common" +version = "0.9.0" +dependencies = [ + "fork-tree 3.0.0", + "itc-parentchain-light-client", + "itc-parentchain-test", + "itp-enclave-metrics", + "itp-extrinsics-factory", + "itp-import-queue", + "itp-node-api-metadata", + "itp-node-api-metadata-provider", + "itp-ocall-api", + "itp-settings", + "itp-sgx-crypto", + "itp-sgx-externalities", + "itp-test", + "itp-types", + "its-block-verification", + "its-primitives", + "its-state", + "its-test", + "log 0.4.20", + "parity-scale-codec", + "sgx_tstd", + "sgx_types", + "sp-core", + "sp-runtime", + "thiserror 1.0.44", + "thiserror 1.0.9", +] + +[[package]] +name = "its-consensus-slots" +version = "0.9.0" +dependencies = [ + "derive_more", + "futures-timer", + "hex", + "itc-parentchain-test", + "itp-settings", + "itp-sgx-externalities", + "itp-stf-state-handler", + "itp-test", + "itp-time-utils", + "itp-types", + "its-block-verification", + "its-consensus-common", + "its-primitives", + "its-state", + "its-test", + "lazy_static", + "lc-scheduled-enclave", + "log 0.4.20", + "parity-scale-codec", + "sgx_tstd", + "sp-consensus-slots", + "sp-keyring", + "sp-runtime", + "tokio", +] + +[[package]] +name = "its-peer-fetch" +version = "0.9.0" +dependencies = [ + "anyhow", + "async-trait", + "itc-rpc-client", + "itp-node-api", + "itp-test", + "its-primitives", + "its-rpc-handler", + "its-storage", + "its-test", + "jsonrpsee 0.2.0", + "log 0.4.20", + "serde 1.0.193", + "serde_json 1.0.103", + "thiserror 1.0.44", + "tokio", +] + +[[package]] +name = "its-primitives" +version = "0.1.0" +dependencies = [ + "itp-types", + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "its-rpc-handler" +version = "0.9.0" +dependencies = [ + "futures 0.3.28", + "futures 0.3.8", + "itp-rpc", + "itp-stf-primitives", + "itp-top-pool-author", + "itp-types", + "itp-utils", + "its-primitives", + "jsonrpc-core 18.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 18.0.0 (git+https://github.com/scs/jsonrpc?branch=no_std_v18)", + "litentry-primitives", + "log 0.4.20", + "parity-scale-codec", + "rust-base58 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-base58 0.0.4 (git+https://github.com/mesalock-linux/rust-base58-sgx?rev=sgx_1.1.3)", + "sgx_tstd", + "sp-core", +] + +[[package]] +name = "its-sidechain" +version = "0.9.0" +dependencies = [ + "its-block-composer", + "its-consensus-aura", + "its-consensus-common", + "its-consensus-slots", + "its-primitives", + "its-rpc-handler", + "its-state", + "its-validateer-fetch", +] + +[[package]] +name = "its-state" +version = "0.9.0" +dependencies = [ + "frame-support", + "itp-sgx-externalities", + "itp-storage", + "its-primitives", + "log 0.4.20", + "parity-scale-codec", + "sgx_tstd", + "sp-core", + "sp-io 7.0.0", + "sp-runtime", + "thiserror 1.0.44", + "thiserror 1.0.9", +] + +[[package]] +name = "its-storage" +version = "0.9.0" +dependencies = [ + "itp-settings", + "itp-time-utils", + "itp-types", + "its-primitives", + "its-test", + "log 0.4.20", + "mockall", + "parity-scale-codec", + "parking_lot 0.12.1", + "rocksdb", + "serde 1.0.193", + "sp-core", + "temp-dir", + "thiserror 1.0.44", +] + +[[package]] +name = "its-test" +version = "0.9.0" +dependencies = [ + "itp-types", + "its-primitives", + "sgx_tstd", + "sp-core", +] + +[[package]] +name = "its-validateer-fetch" +version = "0.9.0" +dependencies = [ + "derive_more", + "frame-support", + "itc-parentchain-test", + "itp-ocall-api", + "itp-teerex-storage", + "itp-test", + "itp-types", + "parity-scale-codec", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "jobserver" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "json5" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" +dependencies = [ + "pest", + "pest_derive", + "serde 1.0.193", +] + +[[package]] +name = "jsonrpc-core" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb" +dependencies = [ + "futures 0.3.28", + "futures-executor 0.3.28", + "futures-util 0.3.28", + "log 0.4.20", + "serde 1.0.193", + "serde_derive 1.0.193", + "serde_json 1.0.103", +] + +[[package]] +name = "jsonrpc-core" +version = "18.0.0" +source = "git+https://github.com/scs/jsonrpc?branch=no_std_v18#0faf53c491c3222b96242a973d902dd06e9b6674" +dependencies = [ + "futures 0.3.8", + "log 0.4.14 (git+https://github.com/mesalock-linux/log-sgx)", + "serde 1.0.118", + "serde_derive 1.0.118", + "serde_json 1.0.60 (git+https://github.com/mesalock-linux/serde-json-sgx)", +] + +[[package]] +name = "jsonrpsee" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "316a89048d2ea5530ab5502aa31e1128f6429b524a37e4c0bc54903bcdf3d342" +dependencies = [ + "jsonrpsee-http-client", + "jsonrpsee-http-server", + "jsonrpsee-proc-macros 0.2.0", + "jsonrpsee-types 0.2.0", + "jsonrpsee-utils", + "jsonrpsee-ws-client", + "jsonrpsee-ws-server", +] + +[[package]] +name = "jsonrpsee" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d291e3a5818a2384645fd9756362e6d89cf0541b0b916fa7702ea4a9833608e" +dependencies = [ + "jsonrpsee-core", + "jsonrpsee-proc-macros 0.16.2", + "jsonrpsee-server", + "jsonrpsee-types 0.16.2", + "tracing", +] + +[[package]] +name = "jsonrpsee-core" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e70b4439a751a5de7dd5ed55eacff78ebf4ffe0fc009cb1ebb11417f5b536b" +dependencies = [ + "anyhow", + "arrayvec 0.7.4", + "async-trait", + "beef", + "futures-channel 0.3.28", + "futures-util 0.3.28", + "globset", + "hyper", + "jsonrpsee-types 0.16.2", + "parking_lot 0.12.1", + "rand 0.8.5", + "rustc-hash", + "serde 1.0.193", + "serde_json 1.0.103", + "soketto 0.7.1", + "thiserror 1.0.44", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-http-client" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7275601ba6f9f6feaa82d3c66b51e34d190e75f1cf23d5c40f7801f3a7610a6" +dependencies = [ + "async-trait", + "fnv 1.0.7", + "hyper", + "hyper-rustls 0.22.1", + "jsonrpsee-types 0.2.0", + "jsonrpsee-utils", + "log 0.4.20", + "serde 1.0.193", + "serde_json 1.0.103", + "thiserror 1.0.44", + "url 2.4.0", +] + +[[package]] +name = "jsonrpsee-http-server" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d22372378f63f7d16de453e786afc740fca5ee80bd260be024a616b6ac2cefe5" +dependencies = [ + "futures-channel 0.3.28", + "futures-util 0.3.28", + "globset", + "hyper", + "jsonrpsee-types 0.2.0", + "jsonrpsee-utils", + "lazy_static", + "log 0.4.20", + "serde 1.0.193", + "serde_json 1.0.103", + "socket2 0.4.9", + "thiserror 1.0.44", + "tokio", + "unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "jsonrpsee-proc-macros" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b4c85cfa6767333f3e5f3b2f2f765dad2727b0033ee270ae07c599bf43ed5ae" +dependencies = [ + "Inflector", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "jsonrpsee-proc-macros" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baa6da1e4199c10d7b1d0a6e5e8bd8e55f351163b6f4b3cbb044672a69bd4c1c" +dependencies = [ + "heck", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "jsonrpsee-server" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb69dad85df79527c019659a992498d03f8495390496da2f07e6c24c2b356fc" +dependencies = [ + "futures-channel 0.3.28", + "futures-util 0.3.28", + "http 0.2.9", + "hyper", + "jsonrpsee-core", + "jsonrpsee-types 0.16.2", + "serde 1.0.193", + "serde_json 1.0.103", + "soketto 0.7.1", + "tokio", + "tokio-stream", + "tokio-util 0.7.8", + "tower", + "tracing", +] + +[[package]] +name = "jsonrpsee-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0cf7bd4e93b3b56e59131de7f24afbea871faf914e97bcdd942c86927ab0172" +dependencies = [ + "async-trait", + "beef", + "futures-channel 0.3.28", + "futures-util 0.3.28", + "hyper", + "log 0.4.20", + "serde 1.0.193", + "serde_json 1.0.103", + "soketto 0.5.0", + "thiserror 1.0.44", +] + +[[package]] +name = "jsonrpsee-types" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bd522fe1ce3702fd94812965d7bb7a3364b1c9aba743944c5a00529aae80f8c" +dependencies = [ + "anyhow", + "beef", + "serde 1.0.193", + "serde_json 1.0.103", + "thiserror 1.0.44", + "tracing", +] + +[[package]] +name = "jsonrpsee-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47554ecaacb479285da68799d9b6afc258c32b332cc8b85829c6a9304ee98776" +dependencies = [ + "futures-channel 0.3.28", + "futures-util 0.3.28", + "hyper", + "jsonrpsee-types 0.2.0", + "log 0.4.20", + "parking_lot 0.11.2", + "rand 0.8.5", + "rustc-hash", + "serde 1.0.193", + "serde_json 1.0.103", + "thiserror 1.0.44", +] + +[[package]] +name = "jsonrpsee-ws-client" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ec51150965544e1a4468f372bdab8545243a1b045d4ab272023aac74c60de32" +dependencies = [ + "async-trait", + "fnv 1.0.7", + "futures 0.3.28", + "jsonrpsee-types 0.2.0", + "log 0.4.20", + "pin-project", + "rustls 0.19.1", + "rustls-native-certs 0.5.0", + "serde 1.0.193", + "serde_json 1.0.103", + "soketto 0.5.0", + "thiserror 1.0.44", + "tokio", + "tokio-rustls 0.22.0", + "tokio-util 0.6.10", + "url 2.4.0", +] + +[[package]] +name = "jsonrpsee-ws-server" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b512c3c679a89d20f97802f69188a2d01f6234491b7513076e21e8424efccafe" +dependencies = [ + "futures-channel 0.3.28", + "futures-util 0.3.28", + "jsonrpsee-types 0.2.0", + "jsonrpsee-utils", + "log 0.4.20", + "rustc-hash", + "serde 1.0.193", + "serde_json 1.0.103", + "soketto 0.5.0", + "thiserror 1.0.44", + "tokio", + "tokio-stream", + "tokio-util 0.6.10", +] + +[[package]] +name = "k256" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +dependencies = [ + "cfg-if 1.0.0", + "ecdsa 0.16.8", + "elliptic-curve 0.13.5", + "once_cell 1.18.0", + "sha2 0.10.7", +] + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "kvdb" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7d770dcb02bf6835887c3a979b5107a04ff4bbde97a5f0928d27404a155add9" +dependencies = [ + "smallvec 1.11.0", +] + +[[package]] +name = "kvdb-memorydb" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7a85fe66f9ff9cd74e169fdd2c94c6e1e74c412c99a73b4df3200b5d3760b2" +dependencies = [ + "kvdb", + "parking_lot 0.12.1", +] + +[[package]] +name = "kvdb-rocksdb" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe7a749456510c45f795e8b04a6a3e0976d0139213ecbf465843830ad55e2217" +dependencies = [ + "kvdb", + "num_cpus", + "parking_lot 0.12.1", + "regex 1.9.5", + "rocksdb", + "smallvec 1.11.0", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin 0.5.2", +] + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "lc-scheduled-enclave" +version = "0.8.0" +dependencies = [ + "itp-settings", + "itp-sgx-io", + "itp-types", + "lazy_static", + "log 0.4.20", + "parity-scale-codec", + "sgx_tstd", + "sp-std 5.0.0", + "thiserror 1.0.44", + "thiserror 1.0.9", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if 1.0.0", + "winapi 0.3.9", +] + +[[package]] +name = "libm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" + +[[package]] +name = "libm" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" + +[[package]] +name = "libp2p" +version = "0.50.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c7b0104790be871edcf97db9bd2356604984e623a08d825c3f27852290266b8" +dependencies = [ + "bytes 1.4.0", + "futures 0.3.28", + "futures-timer", + "getrandom 0.2.10", + "instant", + "libp2p-core 0.38.0", + "libp2p-dns", + "libp2p-identify", + "libp2p-kad", + "libp2p-mdns", + "libp2p-metrics", + "libp2p-mplex", + "libp2p-noise", + "libp2p-ping", + "libp2p-quic", + "libp2p-request-response", + "libp2p-swarm", + "libp2p-tcp", + "libp2p-wasm-ext", + "libp2p-webrtc", + "libp2p-websocket", + "libp2p-yamux", + "multiaddr 0.16.0", + "parking_lot 0.12.1", + "pin-project", + "smallvec 1.11.0", +] + +[[package]] +name = "libp2p-core" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6a8fcd392ff67af6cc3f03b1426c41f7f26b6b9aff2dc632c1c56dd649e571f" +dependencies = [ + "asn1_der", + "bs58", + "ed25519-dalek", + "either", + "fnv 1.0.7", + "futures 0.3.28", + "futures-timer", + "instant", + "log 0.4.20", + "multiaddr 0.16.0", + "multihash 0.16.3", + "multistream-select", + "once_cell 1.18.0", + "parking_lot 0.12.1", + "pin-project", + "prost", + "prost-build", + "rand 0.8.5", + "rw-stream-sink", + "sec1 0.3.0", + "sha2 0.10.7", + "smallvec 1.11.0", + "thiserror 1.0.44", + "unsigned-varint 0.7.1", + "void", + "zeroize", +] + +[[package]] +name = "libp2p-core" +version = "0.39.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c1df63c0b582aa434fb09b2d86897fa2b419ffeccf934b36f87fcedc8e835c2" +dependencies = [ + "either", + "fnv 1.0.7", + "futures 0.3.28", + "futures-timer", + "instant", + "libp2p-identity", + "log 0.4.20", + "multiaddr 0.17.1", + "multihash 0.17.0", + "multistream-select", + "once_cell 1.18.0", + "parking_lot 0.12.1", + "pin-project", + "quick-protobuf", + "rand 0.8.5", + "rw-stream-sink", + "smallvec 1.11.0", + "thiserror 1.0.44", + "unsigned-varint 0.7.1", + "void", +] + +[[package]] +name = "libp2p-dns" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e42a271c1b49f789b92f7fc87749fa79ce5c7bdc88cbdfacb818a4bca47fec5" +dependencies = [ + "futures 0.3.28", + "libp2p-core 0.38.0", + "log 0.4.20", + "parking_lot 0.12.1", + "smallvec 1.11.0", + "trust-dns-resolver", +] + +[[package]] +name = "libp2p-identify" +version = "0.41.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c052d0026f4817b44869bfb6810f4e1112f43aec8553f2cb38881c524b563abf" +dependencies = [ + "asynchronous-codec", + "futures 0.3.28", + "futures-timer", + "libp2p-core 0.38.0", + "libp2p-swarm", + "log 0.4.20", + "lru 0.8.1", + "prost", + "prost-build", + "prost-codec", + "smallvec 1.11.0", + "thiserror 1.0.44", + "void", +] + +[[package]] +name = "libp2p-identity" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e2d584751cecb2aabaa56106be6be91338a60a0f4e420cf2af639204f596fc1" +dependencies = [ + "bs58", + "ed25519-dalek", + "log 0.4.20", + "multiaddr 0.17.1", + "multihash 0.17.0", + "quick-protobuf", + "rand 0.8.5", + "sha2 0.10.7", + "thiserror 1.0.44", + "zeroize", +] + +[[package]] +name = "libp2p-kad" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2766dcd2be8c87d5e1f35487deb22d765f49c6ae1251b3633efe3b25698bd3d2" +dependencies = [ + "arrayvec 0.7.4", + "asynchronous-codec", + "bytes 1.4.0", + "either", + "fnv 1.0.7", + "futures 0.3.28", + "futures-timer", + "instant", + "libp2p-core 0.38.0", + "libp2p-swarm", + "log 0.4.20", + "prost", + "prost-build", + "rand 0.8.5", + "sha2 0.10.7", + "smallvec 1.11.0", + "thiserror 1.0.44", + "uint", + "unsigned-varint 0.7.1", + "void", +] + +[[package]] +name = "libp2p-mdns" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04f378264aade9872d6ccd315c0accc18be3a35d15fc1b9c36e5b6f983b62b5b" +dependencies = [ + "data-encoding", + "futures 0.3.28", + "if-watch", + "libp2p-core 0.38.0", + "libp2p-swarm", + "log 0.4.20", + "rand 0.8.5", + "smallvec 1.11.0", + "socket2 0.4.9", + "tokio", + "trust-dns-proto", + "void", +] + +[[package]] +name = "libp2p-metrics" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ad8a64f29da86005c86a4d2728b8a0719e9b192f4092b609fd8790acb9dec55" +dependencies = [ + "libp2p-core 0.38.0", + "libp2p-identify", + "libp2p-kad", + "libp2p-ping", + "libp2p-swarm", + "prometheus-client", +] + +[[package]] +name = "libp2p-mplex" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03805b44107aa013e7cbbfa5627b31c36cbedfdfb00603c0311998882bc4bace" +dependencies = [ + "asynchronous-codec", + "bytes 1.4.0", + "futures 0.3.28", + "libp2p-core 0.38.0", + "log 0.4.20", + "nohash-hasher", + "parking_lot 0.12.1", + "rand 0.8.5", + "smallvec 1.11.0", + "unsigned-varint 0.7.1", +] + +[[package]] +name = "libp2p-noise" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a978cb57efe82e892ec6f348a536bfbd9fee677adbe5689d7a93ad3a9bffbf2e" +dependencies = [ + "bytes 1.4.0", + "curve25519-dalek 3.2.0", + "futures 0.3.28", + "libp2p-core 0.38.0", + "log 0.4.20", + "once_cell 1.18.0", + "prost", + "prost-build", + "rand 0.8.5", + "sha2 0.10.7", + "snow", + "static_assertions", + "thiserror 1.0.44", + "x25519-dalek 1.1.1", + "zeroize", +] + +[[package]] +name = "libp2p-ping" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "929fcace45a112536e22b3dcfd4db538723ef9c3cb79f672b98be2cc8e25f37f" +dependencies = [ + "futures 0.3.28", + "futures-timer", + "instant", + "libp2p-core 0.38.0", + "libp2p-swarm", + "log 0.4.20", + "rand 0.8.5", + "void", +] + +[[package]] +name = "libp2p-quic" +version = "0.7.0-alpha" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01e7c867e95c8130667b24409d236d37598270e6da69b3baf54213ba31ffca59" +dependencies = [ + "bytes 1.4.0", + "futures 0.3.28", + "futures-timer", + "if-watch", + "libp2p-core 0.38.0", + "libp2p-tls", + "log 0.4.20", + "parking_lot 0.12.1", + "quinn-proto", + "rand 0.8.5", + "rustls 0.20.8", + "thiserror 1.0.44", + "tokio", +] + +[[package]] +name = "libp2p-request-response" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3236168796727bfcf4927f766393415361e2c644b08bedb6a6b13d957c9a4884" +dependencies = [ + "async-trait", + "bytes 1.4.0", + "futures 0.3.28", + "instant", + "libp2p-core 0.38.0", + "libp2p-swarm", + "log 0.4.20", + "rand 0.8.5", + "smallvec 1.11.0", + "unsigned-varint 0.7.1", +] + +[[package]] +name = "libp2p-swarm" +version = "0.41.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a35472fe3276b3855c00f1c032ea8413615e030256429ad5349cdf67c6e1a0" +dependencies = [ + "either", + "fnv 1.0.7", + "futures 0.3.28", + "futures-timer", + "instant", + "libp2p-core 0.38.0", + "libp2p-swarm-derive", + "log 0.4.20", + "pin-project", + "rand 0.8.5", + "smallvec 1.11.0", + "thiserror 1.0.44", + "tokio", + "void", +] + +[[package]] +name = "libp2p-swarm-derive" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d527d5827582abd44a6d80c07ff8b50b4ee238a8979e05998474179e79dc400" +dependencies = [ + "heck", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "libp2p-tcp" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4b257baf6df8f2df39678b86c578961d48cc8b68642a12f0f763f56c8e5858d" +dependencies = [ + "futures 0.3.28", + "futures-timer", + "if-watch", + "libc", + "libp2p-core 0.38.0", + "log 0.4.20", + "socket2 0.4.9", + "tokio", +] + +[[package]] +name = "libp2p-tls" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff08d13d0dc66e5e9ba6279c1de417b84fa0d0adc3b03e5732928c180ec02781" +dependencies = [ + "futures 0.3.28", + "futures-rustls", + "libp2p-core 0.39.2", + "libp2p-identity", + "rcgen 0.10.0", + "ring 0.16.20", + "rustls 0.20.8", + "thiserror 1.0.44", + "webpki 0.22.0", + "x509-parser 0.14.0", + "yasna 0.5.2", +] + +[[package]] +name = "libp2p-wasm-ext" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb1a35299860e0d4b3c02a3e74e3b293ad35ae0cee8a056363b0c862d082069" +dependencies = [ + "futures 0.3.28", + "js-sys", + "libp2p-core 0.38.0", + "parity-send-wrapper", + "wasm-bindgen", + "wasm-bindgen-futures", +] + +[[package]] +name = "libp2p-webrtc" +version = "0.4.0-alpha" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb6cd86dd68cba72308ea05de1cebf3ba0ae6e187c40548167955d4e3970f6a" +dependencies = [ + "async-trait", + "asynchronous-codec", + "bytes 1.4.0", + "futures 0.3.28", + "futures-timer", + "hex", + "if-watch", + "libp2p-core 0.38.0", + "libp2p-noise", + "log 0.4.20", + "multihash 0.16.3", + "prost", + "prost-build", + "prost-codec", + "rand 0.8.5", + "rcgen 0.9.3", + "serde 1.0.193", + "stun", + "thiserror 1.0.44", + "tinytemplate", + "tokio", + "tokio-util 0.7.8", + "webrtc", +] + +[[package]] +name = "libp2p-websocket" +version = "0.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d705506030d5c0aaf2882437c70dab437605f21c5f9811978f694e6917a3b54" +dependencies = [ + "either", + "futures 0.3.28", + "futures-rustls", + "libp2p-core 0.38.0", + "log 0.4.20", + "parking_lot 0.12.1", + "quicksink", + "rw-stream-sink", + "soketto 0.7.1", + "url 2.4.0", + "webpki-roots 0.22.6", +] + +[[package]] +name = "libp2p-yamux" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f63594a0aa818642d9d4915c791945053877253f08a3626f13416b5cd928a29" +dependencies = [ + "futures 0.3.28", + "libp2p-core 0.38.0", + "log 0.4.20", + "parking_lot 0.12.1", + "thiserror 1.0.44", + "yamux", +] + +[[package]] +name = "librocksdb-sys" +version = "0.10.0+7.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fe4d5874f5ff2bc616e55e8c6086d478fcda13faf9495768a4aa1c22042d30b" +dependencies = [ + "bindgen", + "bzip2-sys", + "cc", + "glob", + "libc", + "libz-sys", + "tikv-jemalloc-sys", +] + +[[package]] +name = "libsecp256k1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" +dependencies = [ + "arrayref", + "base64 0.13.1", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand 0.8.5", + "serde 1.0.193", + "sha2 0.9.9", + "typenum 1.16.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libz-sys" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24e6ab01971eb092ffe6a7d42f49f9ff42662f17604681e2843ad65077ba47dc" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.2" +source = "git+https://github.com/mesalock-linux/linked-hash-map-sgx#03e763f7c251c16e0b85e2fb058ba47be52f2a49" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linked_hash_set" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47186c6da4d81ca383c7c47c1bfc80f4b95f4720514d860a5407aaf4233f9588" +dependencies = [ + "linked-hash-map 0.5.6", +] + +[[package]] +name = "linregress" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4de0b5f52a9f84544d268f5fabb71b38962d6aa3c6600b8bcd27d44ccf9c9c45" +dependencies = [ + "nalgebra", +] + +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" + +[[package]] +name = "litentry-hex-utils" +version = "0.9.12" +dependencies = [ + "hex", +] + +[[package]] +name = "litentry-macros" +version = "0.1.0" +dependencies = [ + "cargo_toml", + "quote", +] + +[[package]] +name = "litentry-macros" +version = "0.9.12" + +[[package]] +name = "litentry-primitives" +version = "0.1.0" +dependencies = [ + "base64 0.13.0 (git+https://github.com/mesalock-linux/rust-base64-sgx?rev=sgx_1.1.3)", + "base64 0.13.1", + "bitcoin", + "core-primitives", + "hex", + "itp-sgx-crypto", + "itp-utils", + "log 0.4.20", + "pallet-evm 6.0.0-dev (git+https://github.com/integritee-network/frontier.git?branch=bar/polkadot-v0.9.42)", + "parity-scale-codec", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (git+https://github.com/mesalock-linux/rand-sgx?tag=sgx_1.1.3)", + "ring 0.16.20", + "scale-info", + "secp256k1 0.28.0", + "serde 1.0.193", + "sgx_tstd", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", + "strum 0.25.0", + "strum_macros 0.25.3", + "teerex-primitives", +] + +[[package]] +name = "litentry-proc-macros" +version = "0.9.12" +dependencies = [ + "cargo_toml", + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg 1.1.0", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "git+https://github.com/mesalock-linux/log-sgx?tag=sgx_1.1.3#2ca9039a9ebba0ed90ed2ad57425917d4b3a2a24" +dependencies = [ + "cfg-if 1.0.0", + "sgx_tstd", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "git+https://github.com/mesalock-linux/log-sgx#2ca9039a9ebba0ed90ed2ad57425917d4b3a2a24" +dependencies = [ + "cfg-if 1.0.0", + "sgx_tstd", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "lru" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6e8aaa3f231bb4bd57b84b2d5dc3ae7f350265df8aa96492e0bc394a1571909" +dependencies = [ + "hashbrown 0.12.3", +] + +[[package]] +name = "lru" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e7d46de488603ffdd5f30afbc64fbba2378214a2c3a2fb83abf3d33126df17" +dependencies = [ + "hashbrown 0.13.2", +] + +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map 0.5.6", +] + +[[package]] +name = "lz4" +version = "1.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9e2dd86df36ce760a60f6ff6ad526f7ba1f14ba0356f8254fb6905e6494df1" +dependencies = [ + "libc", + "lz4-sys", +] + +[[package]] +name = "lz4-sys" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + +[[package]] +name = "matchers" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "matches" +version = "0.1.8" +source = "git+https://github.com/mesalock-linux/rust-std-candidates-sgx#5747bcf37f3e18687758838da0339ff0f2c83924" + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "matrixmultiply" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090126dc04f95dc0d1c1c91f61bdd474b3930ca064c1edc8a849da2c6cbe1e77" +dependencies = [ + "autocfg 1.1.0", + "rawpointer", +] + +[[package]] +name = "maybe-async" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f1b8c13cb1f814b634a96b2c725449fe7ed464a7b8781de8688be5ffbd3f305" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "md-5" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "memchr" +version = "2.2.1" +source = "git+https://github.com/mesalock-linux/rust-memchr-sgx#fb51ee32766cb9a2be39b7fb2b5de26bb86dcdeb" +dependencies = [ + "sgx_libc", + "sgx_tstd", + "sgx_types", +] + +[[package]] +name = "memchr" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" + +[[package]] +name = "memfd" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc89ccdc6e10d6907450f753537ebc5c5d3460d2e4e62ea74bd571db62c0f9e" +dependencies = [ + "rustix 0.37.23", +] + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg 1.1.0", +] + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg 1.1.0", +] + +[[package]] +name = "memory-db" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808b50db46293432a45e63bc15ea51e0ab4c0a1647b8eb114e31a3e698dd6fbe" +dependencies = [ + "hash-db 0.16.0", +] + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "merlin" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e261cf0f8b3c42ded9f7d2bb59dea03aa52bc8a1cbc7482f9fc3fd1229d3b42" +dependencies = [ + "byteorder 1.4.3", + "keccak", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize", +] + +[[package]] +name = "mick-jaeger" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69672161530e8aeca1d1400fbf3f1a1747ff60ea604265a4e906c2442df20532" +dependencies = [ + "futures 0.3.28", + "rand 0.8.5", + "thrift", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.6.21" +source = "git+https://github.com/mesalock-linux/mio-sgx?tag=sgx_1.1.3#5b0e56a3066231c7a8d1876c7be3a19b08ffdfd5" +dependencies = [ + "iovec 0.1.4 (git+https://github.com/mesalock-linux/iovec-sgx)", + "log 0.4.14 (git+https://github.com/mesalock-linux/log-sgx)", + "net2 0.2.33", + "sgx_libc", + "sgx_trts", + "sgx_tstd", + "slab 0.4.2", +] + +[[package]] +name = "mio" +version = "0.6.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" +dependencies = [ + "cfg-if 0.1.10", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys", + "libc", + "log 0.4.20", + "miow", + "net2 0.2.39", + "slab 0.4.8", + "winapi 0.2.8", +] + +[[package]] +name = "mio" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", +] + +[[package]] +name = "mio-extras" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" +dependencies = [ + "lazycell", + "log 0.4.20", + "mio 0.6.23", + "slab 0.4.8", +] + +[[package]] +name = "mio-extras" +version = "2.0.6" +source = "git+https://github.com/integritee-network/mio-extras-sgx?rev=963234b#963234bf55e44f9efff921938255126c48deef3a" +dependencies = [ + "lazycell", + "log 0.4.20", + "mio 0.6.21", + "mio 0.6.23", + "sgx_tstd", + "sgx_types", + "slab 0.4.8", +] + +[[package]] +name = "miow" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" +dependencies = [ + "kernel32-sys", + "net2 0.2.39", + "winapi 0.2.8", + "ws2_32-sys", +] + +[[package]] +name = "mockall" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96" +dependencies = [ + "cfg-if 1.0.0", + "downcast", + "fragile", + "lazy_static", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" +dependencies = [ + "cfg-if 1.0.0", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "moonbeam-evm-tracer" +version = "0.1.0" +source = "git+https://github.com/litentry/astar-frame?branch=polkadot-v0.9.42#d9a49c58f248f49e274b0730b8f4ef7f1e72c4b5" +dependencies = [ + "ethereum-types", + "evm 0.37.0", + "evm-gasometer 0.37.0", + "evm-runtime 0.37.0", + "evm-tracing-events", + "fp-evm 3.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", + "moonbeam-primitives-ext", + "pallet-evm 6.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", + "parity-scale-codec", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "moonbeam-primitives-ext" +version = "0.1.0" +source = "git+https://github.com/litentry/astar-frame?branch=polkadot-v0.9.42#d9a49c58f248f49e274b0730b8f4ef7f1e72c4b5" +dependencies = [ + "ethereum-types", + "evm-tracing-events", + "parity-scale-codec", + "sp-externalities", + "sp-runtime-interface", + "sp-std 5.0.0", +] + +[[package]] +name = "moonbeam-rpc-primitives-debug" +version = "0.1.0" +source = "git+https://github.com/litentry/astar-frame?branch=polkadot-v0.9.42#d9a49c58f248f49e274b0730b8f4ef7f1e72c4b5" +dependencies = [ + "environmental 1.1.4", + "ethereum", + "ethereum-types", + "hex", + "parity-scale-codec", + "serde 1.0.193", + "serde_json 1.0.103", + "sp-api", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "moonbeam-rpc-primitives-txpool" +version = "0.6.0" +source = "git+https://github.com/litentry/astar-frame?branch=polkadot-v0.9.42#d9a49c58f248f49e274b0730b8f4ef7f1e72c4b5" +dependencies = [ + "ethereum", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "multer" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2" +dependencies = [ + "bytes 1.4.0", + "encoding_rs", + "futures-util 0.3.28", + "http 0.2.9", + "httparse 1.8.0", + "log 0.4.20", + "memchr 2.6.3", + "mime", + "spin 0.9.8", + "version_check", +] + +[[package]] +name = "multiaddr" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aebdb21e90f81d13ed01dc84123320838e53963c2ca94b60b305d3fa64f31e" +dependencies = [ + "arrayref", + "byteorder 1.4.3", + "data-encoding", + "multibase", + "multihash 0.16.3", + "percent-encoding 2.3.0", + "serde 1.0.193", + "static_assertions", + "unsigned-varint 0.7.1", + "url 2.4.0", +] + +[[package]] +name = "multiaddr" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b36f567c7099511fa8612bbbb52dda2419ce0bdbacf31714e3a5ffdb766d3bd" +dependencies = [ + "arrayref", + "byteorder 1.4.3", + "data-encoding", + "log 0.4.20", + "multibase", + "multihash 0.17.0", + "percent-encoding 2.3.0", + "serde 1.0.193", + "static_assertions", + "unsigned-varint 0.7.1", + "url 2.4.0", +] + +[[package]] +name = "multibase" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" +dependencies = [ + "base-x", + "data-encoding", + "data-encoding-macro", +] + +[[package]] +name = "multihash" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dac63698b887d2d929306ea48b63760431ff8a24fac40ddb22f9c7f49fb7cab" +dependencies = [ + "generic-array 0.14.7", + "multihash-derive 0.7.2", + "unsigned-varint 0.5.1", +] + +[[package]] +name = "multihash" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c346cf9999c631f002d8f977c4eaeaa0e6386f16007202308d0b3757522c2cc" +dependencies = [ + "blake2b_simd", + "blake2s_simd", + "blake3", + "core2 0.4.0", + "digest 0.10.7", + "multihash-derive 0.8.0", + "sha2 0.10.7", + "sha3", + "unsigned-varint 0.7.1", +] + +[[package]] +name = "multihash" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835d6ff01d610179fbce3de1694d007e500bf33a7f29689838941d6bf783ae40" +dependencies = [ + "core2 0.4.0", + "multihash-derive 0.8.0", + "unsigned-varint 0.7.1", +] + +[[package]] +name = "multihash-derive" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "424f6e86263cd5294cbd7f1e95746b95aca0e0d66bff31e5a40d6baa87b4aa99" +dependencies = [ + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "multihash-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc076939022111618a5026d3be019fd8b366e76314538ff9a1b59ffbcbf98bcd" +dependencies = [ + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "multimap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + +[[package]] +name = "multistream-select" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8552ab875c1313b97b8d20cb857b9fd63e2d1d6a0a1b53ce9821e575405f27a" +dependencies = [ + "bytes 1.4.0", + "futures 0.3.28", + "log 0.4.20", + "pin-project", + "smallvec 1.11.0", + "unsigned-varint 0.7.1", +] + +[[package]] +name = "nalgebra" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "307ed9b18cc2423f29e83f84fd23a8e73628727990181f18641a8b5dc2ab1caa" +dependencies = [ + "approx", + "matrixmultiply", + "nalgebra-macros", + "num-complex 0.4.3", + "num-rational 0.4.1", + "num-traits 0.2.16", + "simba", + "typenum 1.16.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "nalgebra-macros" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91761aed67d03ad966ef783ae962ef9bbaca728d2dd7ceb7939ec110fffad998" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "names" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7d66043b25d4a6cccb23619d10c19c25304b355a7dccd4a8e11423dd2382146" +dependencies = [ + "rand 0.8.5", +] + +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log 0.4.20", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "net2" +version = "0.2.33" +source = "git+https://github.com/mesalock-linux/net2-rs-sgx#554583d15f3c9dff5d862a6ae64e227bb38fa729" +dependencies = [ + "cfg-if 0.1.10", + "sgx_libc", + "sgx_tstd", +] + +[[package]] +name = "net2" +version = "0.2.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b13b648036a2339d06de780866fbdfda0dde886de7b3af2ddeba8b14f4ee34ac" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "netlink-packet-core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "345b8ab5bd4e71a2986663e88c56856699d060e78e152e6e9d7966fcd5491297" +dependencies = [ + "anyhow", + "byteorder 1.4.3", + "libc", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-route" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9ea4302b9759a7a88242299225ea3688e63c85ea136371bb6cf94fd674efaab" +dependencies = [ + "anyhow", + "bitflags 1.3.2", + "byteorder 1.4.3", + "libc", + "netlink-packet-core", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-utils" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" +dependencies = [ + "anyhow", + "byteorder 1.4.3", + "paste", + "thiserror 1.0.44", +] + +[[package]] +name = "netlink-proto" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65b4b14489ab424703c092062176d52ba55485a89c076b4f9db05092b7223aa6" +dependencies = [ + "bytes 1.4.0", + "futures 0.3.28", + "log 0.4.20", + "netlink-packet-core", + "netlink-sys", + "thiserror 1.0.44", + "tokio", +] + +[[package]] +name = "netlink-sys" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6471bf08e7ac0135876a9581bf3217ef0333c191c128d34878079f42ee150411" +dependencies = [ + "bytes 1.4.0", + "futures 0.3.28", + "libc", + "log 0.4.20", + "tokio", +] + +[[package]] +name = "nix" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +dependencies = [ + "bitflags 1.3.2", + "cfg-if 1.0.0", + "libc", + "memoffset 0.6.5", +] + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr 2.6.3", + "minimal-lexical", +] + +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + +[[package]] +name = "num" +version = "0.2.0" +source = "git+https://github.com/mesalock-linux/num-sgx#22645415542cc67551890dfdd34f4d5638b9ec78" +dependencies = [ + "num-bigint 0.2.5", + "num-complex 0.2.3", + "num-integer 0.1.41", + "num-iter 0.1.39", + "num-rational 0.2.2", + "num-traits 0.2.10", +] + +[[package]] +name = "num" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36" +dependencies = [ + "num-bigint 0.2.6", + "num-complex 0.2.4", + "num-integer 0.1.45", + "num-iter 0.1.43", + "num-rational 0.2.4", + "num-traits 0.2.16", +] + +[[package]] +name = "num" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +dependencies = [ + "num-bigint 0.4.3", + "num-complex 0.4.3", + "num-integer 0.1.45", + "num-iter 0.1.43", + "num-rational 0.4.1", + "num-traits 0.2.16", +] + +[[package]] +name = "num-bigint" +version = "0.2.5" +source = "git+https://github.com/mesalock-linux/num-bigint-sgx#76a5bed94dc31c32bd1670dbf72877abcf9bbc09" +dependencies = [ + "autocfg 1.1.0", + "num-integer 0.1.41", + "num-traits 0.2.10", + "sgx_tstd", +] + +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +dependencies = [ + "autocfg 1.1.0", + "num-integer 0.1.45", + "num-traits 0.2.16", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg 1.1.0", + "num-integer 0.1.45", + "num-traits 0.2.16", +] + +[[package]] +name = "num-complex" +version = "0.2.3" +source = "git+https://github.com/mesalock-linux/num-complex-sgx#19700ad6de079ebc5560db472c282d1591e0d84f" +dependencies = [ + "autocfg 0.1.8", + "num-traits 0.2.10", + "sgx_tstd", +] + +[[package]] +name = "num-complex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" +dependencies = [ + "autocfg 1.1.0", + "num-traits 0.2.16", +] + +[[package]] +name = "num-complex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" +dependencies = [ + "num-traits 0.2.16", +] + +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "num-format" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" +dependencies = [ + "arrayvec 0.7.4", + "itoa 1.0.9", +] + +[[package]] +name = "num-integer" +version = "0.1.41" +source = "git+https://github.com/mesalock-linux/num-integer-sgx#404c50e5378ca635261688b080dee328ff42b6bd" +dependencies = [ + "autocfg 0.1.8", + "num-traits 0.2.10", + "sgx_tstd", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg 1.1.0", + "num-traits 0.2.16", +] + +[[package]] +name = "num-iter" +version = "0.1.39" +source = "git+https://github.com/mesalock-linux/num-iter-sgx#f19fc44fcad0b82a040e5a24c511e5049cc04b60" +dependencies = [ + "num-integer 0.1.41", + "num-traits 0.2.10", + "sgx_tstd", +] + +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg 1.1.0", + "num-integer 0.1.45", + "num-traits 0.2.16", +] + +[[package]] +name = "num-rational" +version = "0.2.2" +source = "git+https://github.com/mesalock-linux/num-rational-sgx#be65f9ce439f3c9ec850d8041635ab6c3309b816" +dependencies = [ + "autocfg 0.1.8", + "num-bigint 0.2.5", + "num-integer 0.1.41", + "num-traits 0.2.10", + "sgx_tstd", +] + +[[package]] +name = "num-rational" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" +dependencies = [ + "autocfg 1.1.0", + "num-bigint 0.2.6", + "num-integer 0.1.45", + "num-traits 0.2.16", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg 1.1.0", + "num-bigint 0.4.3", + "num-integer 0.1.45", + "num-traits 0.2.16", +] + +[[package]] +name = "num-traits" +version = "0.2.10" +source = "git+https://github.com/mesalock-linux/num-traits-sgx#af046e0b15c594c960007418097dd4ff37ec3f7a" +dependencies = [ + "autocfg 0.1.8", + "sgx_tstd", +] + +[[package]] +name = "num-traits" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +dependencies = [ + "autocfg 1.1.0", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.2", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" +dependencies = [ + "num_enum_derive 0.6.1", +] + +[[package]] +name = "num_enum" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" +dependencies = [ + "num_enum_derive 0.7.2", +] + +[[package]] +name = "num_enum_derive" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "object" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +dependencies = [ + "crc32fast", + "hashbrown 0.12.3", + "indexmap 1.9.3", + "memchr 2.6.3", +] + +[[package]] +name = "object" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr 2.6.3", +] + +[[package]] +name = "ofb" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5e609fc8b72da3dabd56427be9489d8a9f4bd2e4dc41660dd033c3c8e90b93c" +dependencies = [ + "cipher 0.2.5", +] + +[[package]] +name = "oid-registry" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e20717fa0541f39bd146692035c37bedfa532b3e5071b35761082407546b2a" +dependencies = [ + "asn1-rs 0.3.1", +] + +[[package]] +name = "oid-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" +dependencies = [ + "asn1-rs 0.5.2", +] + +[[package]] +name = "once_cell" +version = "1.4.0" +source = "git+https://github.com/mesalock-linux/once_cell-sgx#cefcaa03fed4d85276b3235d875f1b45d399cc3c" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl" +version = "0.10.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" +dependencies = [ + "bitflags 1.3.2", + "cfg-if 1.0.0", + "foreign-types", + "libc", + "once_cell 1.18.0", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "orchestra" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "227585216d05ba65c7ab0a0450a3cf2cbd81a98862a54c4df8e14d5ac6adb015" +dependencies = [ + "async-trait", + "dyn-clonable", + "futures 0.3.28", + "futures-timer", + "orchestra-proc-macro", + "pin-project", + "prioritized-metered-channel", + "thiserror 1.0.44", + "tracing", +] + +[[package]] +name = "orchestra-proc-macro" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2871aadd82a2c216ee68a69837a526dfe788ecbe74c4c5038a6acdbff6653066" +dependencies = [ + "expander 0.0.6", + "itertools 0.10.5", + "petgraph", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ordered-float" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3305af35278dd29f46fcdd139e0b1fbfae2153f0e5928b39b035542dd31e37b7" +dependencies = [ + "num-traits 0.2.16", +] + +[[package]] +name = "ordered-multimap" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" +dependencies = [ + "dlv-list", + "hashbrown 0.12.3", +] + +[[package]] +name = "orml-tokens" +version = "0.4.1-dev" +source = "git+https://github.com/open-web3-stack/open-runtime-module-library?branch=polkadot-v0.9.42#4ae0372e2c624e6acc98305564b9d395f70814c0" +dependencies = [ + "frame-support", + "frame-system", + "orml-traits", + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "sp-arithmetic", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "orml-traits" +version = "0.4.1-dev" +source = "git+https://github.com/open-web3-stack/open-runtime-module-library?branch=polkadot-v0.9.42#4ae0372e2c624e6acc98305564b9d395f70814c0" +dependencies = [ + "frame-support", + "impl-trait-for-tuples", + "num-traits 0.2.16", + "orml-utilities", + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", + "xcm", +] + +[[package]] +name = "orml-utilities" +version = "0.4.1-dev" +source = "git+https://github.com/open-web3-stack/open-runtime-module-library?branch=polkadot-v0.9.42#4ae0372e2c624e6acc98305564b9d395f70814c0" +dependencies = [ + "frame-support", + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "orml-xcm-support" +version = "0.4.1-dev" +source = "git+https://github.com/open-web3-stack/open-runtime-module-library?branch=polkadot-v0.9.42#4ae0372e2c624e6acc98305564b9d395f70814c0" +dependencies = [ + "frame-support", + "orml-traits", + "parity-scale-codec", + "sp-runtime", + "sp-std 5.0.0", + "xcm", + "xcm-executor", +] + +[[package]] +name = "orml-xtokens" +version = "0.4.1-dev" +source = "git+https://github.com/open-web3-stack/open-runtime-module-library?branch=polkadot-v0.9.42#4ae0372e2c624e6acc98305564b9d395f70814c0" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "frame-system", + "orml-traits", + "orml-xcm-support", + "pallet-xcm", + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", + "xcm", + "xcm-executor", +] + +[[package]] +name = "os_str_bytes" +version = "6.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" + +[[package]] +name = "p256" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" +dependencies = [ + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", + "sha2 0.10.7", +] + +[[package]] +name = "p384" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc8c5bf642dde52bb9e87c0ecd8ca5a76faac2eeed98dedb7c717997e1080aa" +dependencies = [ + "ecdsa 0.14.8", + "elliptic-curve 0.12.3", + "sha2 0.10.7", +] + +[[package]] +name = "packed_simd_2" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1914cd452d8fccd6f9db48147b29fd4ae05bea9dc5d9ad578509f72415de282" +dependencies = [ + "cfg-if 1.0.0", + "libm 0.1.4", +] + +[[package]] +name = "pallet-asset-manager" +version = "0.1.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "orml-traits", + "parity-scale-codec", + "scale-info", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", + "xcm", +] + +[[package]] +name = "pallet-assets" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-aura" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-support", + "frame-system", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-application-crypto", + "sp-consensus-aura", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-authority-discovery" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-support", + "frame-system", + "pallet-session", + "parity-scale-codec", + "scale-info", + "sp-application-crypto", + "sp-authority-discovery", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-authorship" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-babe" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log 0.4.20", + "pallet-authorship", + "pallet-session", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-application-crypto", + "sp-consensus-babe", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-balances" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log 0.4.20", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-bounties" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log 0.4.20", + "pallet-treasury", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-bridge" +version = "0.1.0" +dependencies = [ + "blake2-rfc", + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-bridge-transfer" +version = "0.1.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-bridge", + "pallet-parachain-staking", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-collective" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log 0.4.20", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-democracy" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log 0.4.20", + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-drop3" +version = "0.1.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-election-provider-multi-phase" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "log 0.4.20", + "pallet-election-provider-support-benchmarking", + "parity-scale-codec", + "rand 0.8.5", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-npos-elections", + "sp-runtime", + "sp-std 5.0.0", + "strum 0.24.1", +] + +[[package]] +name = "pallet-election-provider-support-benchmarking" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-system", + "parity-scale-codec", + "sp-npos-elections", + "sp-runtime", +] + +[[package]] +name = "pallet-ethereum" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42#2499d18c936edbcb7fcb711827db7abb9b4f4da4" +dependencies = [ + "ethereum", + "ethereum-types", + "evm 0.39.1 (git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65)", + "fp-consensus", + "fp-ethereum", + "fp-evm 3.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", + "fp-rpc", + "fp-storage", + "frame-support", + "frame-system", + "pallet-evm 6.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", + "parity-scale-codec", + "scale-info", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-evm" +version = "6.0.0-dev" +source = "git+https://github.com/integritee-network/frontier.git?branch=bar/polkadot-v0.9.42#a5a5e1e6ec08cd542a6084c310863150fb8841b1" +dependencies = [ + "environmental 1.1.4", + "evm 0.39.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fp-account 1.0.0-dev (git+https://github.com/integritee-network/frontier.git?branch=bar/polkadot-v0.9.42)", + "fp-evm 3.0.0-dev (git+https://github.com/integritee-network/frontier.git?branch=bar/polkadot-v0.9.42)", + "frame-benchmarking", + "frame-support", + "frame-system", + "hex", + "impl-trait-for-tuples", + "log 0.4.20", + "parity-scale-codec", + "rlp", + "scale-info", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-evm" +version = "6.0.0-dev" +source = "git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42#2499d18c936edbcb7fcb711827db7abb9b4f4da4" +dependencies = [ + "environmental 1.1.4", + "evm 0.39.1 (git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65)", + "fp-account 1.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", + "fp-evm 3.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", + "frame-benchmarking", + "frame-support", + "frame-system", + "hex", + "hex-literal", + "impl-trait-for-tuples", + "log 0.4.20", + "parity-scale-codec", + "rlp", + "scale-info", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-evm-precompile-blake2" +version = "2.0.0-dev" +source = "git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42#2499d18c936edbcb7fcb711827db7abb9b4f4da4" +dependencies = [ + "fp-evm 3.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", +] + +[[package]] +name = "pallet-evm-precompile-bn128" +version = "2.0.0-dev" +source = "git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42#2499d18c936edbcb7fcb711827db7abb9b4f4da4" +dependencies = [ + "fp-evm 3.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", + "sp-core", + "substrate-bn", +] + +[[package]] +name = "pallet-evm-precompile-bridge-transfer" +version = "0.9.17" +dependencies = [ + "fp-evm 3.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", + "frame-support", + "frame-system", + "log 0.4.20", + "num_enum 0.7.2", + "pallet-bridge", + "pallet-bridge-transfer", + "pallet-evm 6.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", + "parity-scale-codec", + "precompile-utils", + "rustc-hex", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-evm-precompile-dispatch" +version = "2.0.0-dev" +source = "git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42#2499d18c936edbcb7fcb711827db7abb9b4f4da4" +dependencies = [ + "fp-evm 3.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", + "frame-support", + "pallet-evm 6.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", +] + +[[package]] +name = "pallet-evm-precompile-ed25519" +version = "2.0.0-dev" +source = "git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42#2499d18c936edbcb7fcb711827db7abb9b4f4da4" +dependencies = [ + "ed25519-dalek", + "fp-evm 3.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", +] + +[[package]] +name = "pallet-evm-precompile-modexp" +version = "2.0.0-dev" +source = "git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42#2499d18c936edbcb7fcb711827db7abb9b4f4da4" +dependencies = [ + "fp-evm 3.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", + "num 0.4.1", +] + +[[package]] +name = "pallet-evm-precompile-parachain-staking" +version = "0.9.17" +dependencies = [ + "fp-evm 3.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", + "frame-support", + "frame-system", + "log 0.4.20", + "num_enum 0.7.2", + "pallet-evm 6.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", + "pallet-parachain-staking", + "parity-scale-codec", + "precompile-utils", + "rustc-hex", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-evm-precompile-sha3fips" +version = "2.0.0-dev" +source = "git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42#2499d18c936edbcb7fcb711827db7abb9b4f4da4" +dependencies = [ + "fp-evm 3.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", + "tiny-keccak", +] + +[[package]] +name = "pallet-evm-precompile-simple" +version = "2.0.0-dev" +source = "git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42#2499d18c936edbcb7fcb711827db7abb9b4f4da4" +dependencies = [ + "fp-evm 3.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", + "ripemd", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", +] + +[[package]] +name = "pallet-extrinsic-filter" +version = "0.1.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-fast-unstake" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "log 0.4.20", + "parity-scale-codec", + "scale-info", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-staking", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-group" +version = "0.1.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-identity" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "enumflags2", + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-identity-management" +version = "0.1.0" +dependencies = [ + "core-primitives", + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-teerex", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", + "teerex-primitives", +] + +[[package]] +name = "pallet-membership" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log 0.4.20", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-multisig" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log 0.4.20", + "parity-scale-codec", + "scale-info", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-parachain-staking" +version = "0.1.0" +dependencies = [ + "core-primitives", + "frame-benchmarking", + "frame-support", + "frame-system", + "log 0.4.20", + "pallet-authorship", + "pallet-balances", + "pallet-session", + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "sp-runtime", + "sp-staking", + "sp-std 5.0.0", + "substrate-fixed 0.5.9 (git+https://github.com/encointer/substrate-fixed)", +] + +[[package]] +name = "pallet-parentchain" +version = "0.9.0" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", +] + +[[package]] +name = "pallet-preimage" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log 0.4.20", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-proxy" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-scheduler" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log 0.4.20", + "parity-scale-codec", + "scale-info", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", + "sp-weights", +] + +[[package]] +name = "pallet-session" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "log 0.4.20", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std 5.0.0", + "sp-trie", +] + +[[package]] +name = "pallet-sidechain" +version = "0.9.0" +dependencies = [ + "frame-support", + "frame-system", + "log 0.4.20", + "pallet-teerex", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "sidechain-primitives", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", + "teerex-primitives", +] + +[[package]] +name = "pallet-staking" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-benchmarking", + "frame-election-provider-support", + "frame-support", + "frame-system", + "log 0.4.20", + "pallet-authorship", + "pallet-session", + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "sp-application-crypto", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-staking", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-staking-reward-fn" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "log 0.4.20", + "sp-arithmetic", +] + +[[package]] +name = "pallet-sudo" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-teeracle" +version = "0.1.0" +dependencies = [ + "frame-support", + "frame-system", + "log 0.4.20", + "pallet-teerex", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", + "substrate-fixed 0.5.9 (git+https://github.com/encointer/substrate-fixed?tag=v0.5.9)", + "teeracle-primitives", +] + +[[package]] +name = "pallet-teerex" +version = "0.9.0" +dependencies = [ + "frame-support", + "frame-system", + "log 0.4.20", + "pallet-timestamp", + "parity-scale-codec", + "rustls-webpki", + "scale-info", + "sgx-verify", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", + "teerex-primitives", +] + +[[package]] +name = "pallet-timestamp" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log 0.4.20", + "parity-scale-codec", + "scale-info", + "sp-inherents", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", + "sp-timestamp", +] + +[[package]] +name = "pallet-tips" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log 0.4.20", + "pallet-treasury", + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-transaction-payment" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-transaction-payment-rpc-runtime-api" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "pallet-transaction-payment", + "parity-scale-codec", + "sp-api", + "sp-runtime", + "sp-weights", +] + +[[package]] +name = "pallet-treasury" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-utility" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-vc-management" +version = "0.1.0" +dependencies = [ + "core-primitives", + "frame-benchmarking", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", + "teerex-primitives", +] + +[[package]] +name = "pallet-vesting" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log 0.4.20", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "pallet-xcm" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" +dependencies = [ + "bounded-collections", + "frame-benchmarking", + "frame-support", + "frame-system", + "log 0.4.20", + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", + "xcm", + "xcm-executor", +] + +[[package]] +name = "parachain-info" +version = "0.1.0" +source = "git+https://github.com/paritytech/cumulus?branch=polkadot-v0.9.42#f603a61ff370fc33740c9373833c3c6ba1486846" +dependencies = [ + "cumulus-primitives-core", + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", +] + +[[package]] +name = "parity-db" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4890dcb9556136a4ec2b0c51fa4a08c8b733b829506af8fff2e853f3a065985b" +dependencies = [ + "blake2", + "crc32fast", + "fs2", + "hex", + "libc", + "log 0.4.20", + "lz4", + "memmap2", + "parking_lot 0.12.1", + "rand 0.8.5", + "siphasher", + "snap", +] + +[[package]] +name = "parity-multiaddr" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58341485071825827b7f03cf7efd1cb21e6a709bea778fb50227fd45d2f361b4" +dependencies = [ + "arrayref", + "bs58", + "byteorder 1.4.3", + "data-encoding", + "multihash 0.13.2", + "percent-encoding 2.3.0", + "serde 1.0.193", + "static_assertions", + "unsigned-varint 0.7.1", + "url 2.4.0", +] + +[[package]] +name = "parity-scale-codec" +version = "3.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8e946cc0cc711189c0b0249fb8b599cbeeab9784d83c415719368bb8d4ac64" +dependencies = [ + "arrayvec 0.7.4", + "bitvec", + "byte-slice-cast", + "bytes 1.4.0", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde 1.0.193", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a296c3079b5fefbc499e1de58dc26c09b1b9a5952d26694ee89f04a43ebbb3e" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "parity-send-wrapper" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9777aa91b8ad9dd5aaa04a9b6bcb02c7f1deb952fca5a66034d5e63afc5c6f" + +[[package]] +name = "parity-util-mem" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d32c34f4f5ca7f9196001c0aba5a1f9a5a12382c8944b8b0f90233282d1e8f8" +dependencies = [ + "cfg-if 1.0.0", + "impl-trait-for-tuples", + "parity-util-mem-derive", + "primitive-types", + "winapi 0.3.9", +] + +[[package]] +name = "parity-util-mem-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f557c32c6d268a07c921471619c0295f5efad3a0e76d4f97a05c091a51d110b2" +dependencies = [ + "proc-macro2", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "parity-wasm" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" + +[[package]] +name = "parking" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.6", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.8", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if 1.0.0", + "instant", + "libc", + "redox_syscall 0.2.16", + "smallvec 1.11.0", + "winapi 0.3.9", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall 0.3.5", + "smallvec 1.11.0", + "windows-targets 0.48.1", +] + +[[package]] +name = "parse_duration" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7037e5e93e0172a5a96874380bf73bc6ecef022e26fa25f2be26864d6b3ba95d" +dependencies = [ + "lazy_static", + "num 0.2.1", + "regex 1.9.5", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + +[[package]] +name = "pbkdf2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" +dependencies = [ + "crypto-mac 0.11.1", +] + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "pem" +version = "0.8.2" +source = "git+https://github.com/mesalock-linux/pem-rs-sgx#fdfef4f24a9fb3fa72e8a71bb28bd8ff15feff2f" +dependencies = [ + "base64 0.13.0 (git+https://github.com/mesalock-linux/rust-base64-sgx)", + "once_cell 1.4.0", + "regex 1.3.1", + "sgx_tstd", +] + +[[package]] +name = "pem" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +dependencies = [ + "base64 0.13.1", +] + +[[package]] +name = "pem-rfc7468" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d159833a9105500e0398934e205e0773f0b27529557134ecfc51c27646adac" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "git+https://github.com/mesalock-linux/rust-url-sgx?tag=sgx_1.1.3#23832f3191456c2d4a0faab10952e1747be58ca8" + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "pest" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d2d1d55045829d65aad9d389139882ad623b33b904e7c9f1b10c5b8927298e5" +dependencies = [ + "thiserror 1.0.44", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f94bca7e7a599d89dea5dfa309e217e7906c3c007fb9c3299c40b10d6a315d3" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d490fe7e8556575ff6911e45567ab95e71617f43781e5c05490dc8d75c965c" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "pest_meta" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2674c66ebb4b4d9036012091b537aae5878970d6999f81a265034d85b136b341" +dependencies = [ + "once_cell 1.18.0", + "pest", + "sha2 0.10.7", +] + +[[package]] +name = "petgraph" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" +dependencies = [ + "fixedbitset", + "indexmap 1.9.3", +] + +[[package]] +name = "pin-project" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "030ad2bc4db10a8944cb0d837f158bdfec4d4a4873ab701a95046770d11f8842" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "pin-project-lite" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" + +[[package]] +name = "pin-project-lite" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der 0.6.1", + "spki 0.6.0", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der 0.7.8", + "spki 0.7.2", +] + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "platforms" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" + +[[package]] +name = "polkadot-core-primitives" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "polkadot-node-jaeger" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" +dependencies = [ + "lazy_static", + "log 0.4.20", + "mick-jaeger", + "parity-scale-codec", + "parking_lot 0.12.1", + "polkadot-node-primitives", + "polkadot-primitives", + "sc-network", + "sp-core", + "thiserror 1.0.44", + "tokio", +] + +[[package]] +name = "polkadot-node-metrics" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" +dependencies = [ + "bs58", + "futures 0.3.28", + "futures-timer", + "log 0.4.20", + "parity-scale-codec", + "polkadot-primitives", + "prioritized-metered-channel", + "sc-cli", + "sc-service", + "sc-tracing", + "substrate-prometheus-endpoint", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-network-protocol" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" +dependencies = [ + "async-trait", + "derive_more", + "fatality", + "futures 0.3.28", + "hex", + "parity-scale-codec", + "polkadot-node-jaeger", + "polkadot-node-primitives", + "polkadot-primitives", + "rand 0.8.5", + "sc-authority-discovery", + "sc-network", + "strum 0.24.1", + "thiserror 1.0.44", + "tracing-gum", +] + +[[package]] +name = "polkadot-node-primitives" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" +dependencies = [ + "bounded-vec", + "futures 0.3.28", + "parity-scale-codec", + "polkadot-parachain", + "polkadot-primitives", + "schnorrkel", + "serde 1.0.193", + "sp-application-crypto", + "sp-consensus-babe", + "sp-core", + "sp-keystore", + "sp-maybe-compressed-blob", + "sp-runtime", + "thiserror 1.0.44", + "zstd 0.11.2+zstd.1.5.2", +] + +[[package]] +name = "polkadot-node-subsystem-types" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" +dependencies = [ + "async-trait", + "derive_more", + "futures 0.3.28", + "orchestra", + "polkadot-node-jaeger", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-primitives", + "polkadot-statement-table", + "sc-network", + "smallvec 1.11.0", + "sp-api", + "sp-authority-discovery", + "sp-consensus-babe", + "substrate-prometheus-endpoint", + "thiserror 1.0.44", +] + +[[package]] +name = "polkadot-overseer" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" +dependencies = [ + "async-trait", + "futures 0.3.28", + "futures-timer", + "lru 0.9.0", + "orchestra", + "parking_lot 0.12.1", + "polkadot-node-metrics", + "polkadot-node-network-protocol", + "polkadot-node-primitives", + "polkadot-node-subsystem-types", + "polkadot-primitives", + "sc-client-api", + "sp-api", + "sp-core", + "tikv-jemalloc-ctl", + "tracing-gum", +] + +[[package]] +name = "polkadot-parachain" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" +dependencies = [ + "bounded-collections", + "derive_more", + "frame-support", + "parity-scale-codec", + "polkadot-core-primitives", + "scale-info", + "serde 1.0.193", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "polkadot-primitives" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" +dependencies = [ + "bitvec", + "hex-literal", + "parity-scale-codec", + "polkadot-core-primitives", + "polkadot-parachain", + "scale-info", + "serde 1.0.193", + "sp-api", + "sp-application-crypto", + "sp-arithmetic", + "sp-authority-discovery", + "sp-consensus-slots", + "sp-core", + "sp-inherents", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-keystore", + "sp-runtime", + "sp-staking", + "sp-std 5.0.0", +] + +[[package]] +name = "polkadot-runtime-common" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" +dependencies = [ + "bitvec", + "frame-election-provider-support", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "libsecp256k1", + "log 0.4.20", + "pallet-authorship", + "pallet-balances", + "pallet-election-provider-multi-phase", + "pallet-fast-unstake", + "pallet-session", + "pallet-staking", + "pallet-staking-reward-fn", + "pallet-timestamp", + "pallet-transaction-payment", + "pallet-treasury", + "pallet-vesting", + "parity-scale-codec", + "polkadot-primitives", + "polkadot-runtime-parachains", + "rustc-hex", + "scale-info", + "serde 1.0.193", + "serde_derive 1.0.193", + "slot-range-helper", + "sp-api", + "sp-core", + "sp-inherents", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-npos-elections", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std 5.0.0", + "static_assertions", + "xcm", +] + +[[package]] +name = "polkadot-runtime-metrics" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" +dependencies = [ + "bs58", + "parity-scale-codec", + "polkadot-primitives", + "sp-std 5.0.0", + "sp-tracing", +] + +[[package]] +name = "polkadot-runtime-parachains" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" +dependencies = [ + "bitflags 1.3.2", + "bitvec", + "derive_more", + "frame-support", + "frame-system", + "log 0.4.20", + "pallet-authority-discovery", + "pallet-authorship", + "pallet-babe", + "pallet-balances", + "pallet-session", + "pallet-staking", + "pallet-timestamp", + "pallet-vesting", + "parity-scale-codec", + "polkadot-parachain", + "polkadot-primitives", + "polkadot-runtime-metrics", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rustc-hex", + "scale-info", + "serde 1.0.193", + "sp-api", + "sp-core", + "sp-inherents", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-keystore", + "sp-runtime", + "sp-session", + "sp-staking", + "sp-std 5.0.0", + "xcm", + "xcm-executor", +] + +[[package]] +name = "polkadot-statement-table" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" +dependencies = [ + "parity-scale-codec", + "polkadot-primitives", + "sp-core", +] + +[[package]] +name = "polling" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +dependencies = [ + "autocfg 1.1.0", + "bitflags 1.3.2", + "cfg-if 1.0.0", + "concurrent-queue", + "libc", + "log 0.4.20", + "pin-project-lite 0.2.10", + "windows-sys 0.48.0", +] + +[[package]] +name = "poly1305" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" +dependencies = [ + "cpufeatures", + "opaque-debug 0.3.0", + "universal-hash 0.4.1", +] + +[[package]] +name = "polyval" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "opaque-debug 0.3.0", + "universal-hash 0.4.1", +] + +[[package]] +name = "polyval" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "opaque-debug 0.3.0", + "universal-hash 0.5.1", +] + +[[package]] +name = "postcard" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a25c0b0ae06fcffe600ad392aabfa535696c8973f2253d9ac83171924c58a858" +dependencies = [ + "postcard-cobs", + "serde 1.0.193", +] + +[[package]] +name = "postcard-cobs" +version = "0.1.5-pre" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c68cb38ed13fd7bc9dd5db8f165b7c8d9c1a315104083a2b10f11354c2af97f" + +[[package]] +name = "ppv-lite86" +version = "0.2.6" +source = "git+https://github.com/mesalock-linux/cryptocorrosion-sgx#32d7de50b5f03a10fe5a42167410be2dd3c2e389" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "precompile-utils" +version = "0.9.17" +dependencies = [ + "assert_matches", + "evm 0.39.1 (git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65)", + "fp-evm 3.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "log 0.4.20", + "num_enum 0.7.2", + "pallet-evm 6.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", + "parity-scale-codec", + "precompile-utils-macro", + "sha3", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", + "xcm", +] + +[[package]] +name = "precompile-utils-macro" +version = "0.9.17" +dependencies = [ + "num_enum 0.7.2", + "proc-macro2", + "quote", + "sha3", + "syn 2.0.32", +] + +[[package]] +name = "predicates" +version = "2.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd" +dependencies = [ + "difflib", + "float-cmp", + "itertools 0.10.5", + "normalize-line-endings", + "predicates-core", + "regex 1.9.5", +] + +[[package]] +name = "predicates-core" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" + +[[package]] +name = "predicates-tree" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "prettyplease" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +dependencies = [ + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "primitive-types" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" +dependencies = [ + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde", + "scale-info", + "uint", +] + +[[package]] +name = "prioritized-metered-channel" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "382698e48a268c832d0b181ed438374a6bb708a82a8ca273bb0f61c74cf209c4" +dependencies = [ + "coarsetime", + "crossbeam-queue", + "derive_more", + "futures 0.3.28", + "futures-timer", + "nanorand", + "thiserror 1.0.44", + "tracing", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell 1.18.0", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro-nested" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" + +[[package]] +name = "proc-macro-warning" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e99670bafb56b9a106419397343bdbc8b8742c3cc449fec6345f86173f47cd4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "proc-macro2" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "procfs" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1de8dacb0873f77e6aefc6d71e044761fcc68060290f5b1089fcdf84626bb69" +dependencies = [ + "bitflags 1.3.2", + "byteorder 1.4.3", + "hex", + "lazy_static", + "rustix 0.36.15", +] + +[[package]] +name = "prometheus" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c" +dependencies = [ + "cfg-if 1.0.0", + "fnv 1.0.7", + "lazy_static", + "libc", + "memchr 2.6.3", + "parking_lot 0.12.1", + "procfs", + "thiserror 1.0.44", +] + +[[package]] +name = "prometheus-client" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83cd1b99916654a69008fd66b4f9397fbe08e6e51dfe23d4417acf5d3b8cb87c" +dependencies = [ + "dtoa", + "itoa 1.0.9", + "parking_lot 0.12.1", + "prometheus-client-derive-text-encode", +] + +[[package]] +name = "prometheus-client-derive-text-encode" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66a455fbcb954c1a7decf3c586e860fd7889cddf4b8e164be736dbac95a953cd" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "prost" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +dependencies = [ + "bytes 1.4.0", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" +dependencies = [ + "bytes 1.4.0", + "heck", + "itertools 0.10.5", + "lazy_static", + "log 0.4.20", + "multimap", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex 1.9.5", + "syn 1.0.109", + "tempfile", + "which", +] + +[[package]] +name = "prost-codec" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc34979ff898b6e141106178981ce2596c387ea6e62533facfc61a37fc879c0" +dependencies = [ + "asynchronous-codec", + "bytes 1.4.0", + "prost", + "thiserror 1.0.44", + "unsigned-varint 0.7.1", +] + +[[package]] +name = "prost-derive" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +dependencies = [ + "anyhow", + "itertools 0.10.5", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "prost-types" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" +dependencies = [ + "prost", +] + +[[package]] +name = "psm" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +dependencies = [ + "cc", +] + +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quick-protobuf" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6da84cc204722a989e01ba2f6e1e276e190f22263d0cb6ce8526fcdb0d2e1f" +dependencies = [ + "byteorder 1.4.3", +] + +[[package]] +name = "quicksink" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77de3c815e5a160b1539c6592796801df2043ae35e123b46d73380cfa57af858" +dependencies = [ + "futures-core 0.3.28", + "futures-sink 0.3.28", + "pin-project-lite 0.1.12", +] + +[[package]] +name = "quinn-proto" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c10f662eee9c94ddd7135043e544f3c82fa839a1e7b865911331961b53186c" +dependencies = [ + "bytes 1.4.0", + "rand 0.8.5", + "ring 0.16.20", + "rustc-hash", + "rustls 0.20.8", + "slab 0.4.8", + "thiserror 1.0.44", + "tinyvec", + "tracing", + "webpki 0.22.0", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "git+https://github.com/mesalock-linux/rand-sgx?tag=sgx_1.1.3#83583f073de3b4f75c3c3ef5e174d484ed941f85" +dependencies = [ + "getrandom 0.1.14", + "rand_chacha 0.2.2 (git+https://github.com/mesalock-linux/rand-sgx?tag=sgx_1.1.3)", + "rand_core 0.5.1 (git+https://github.com/mesalock-linux/rand-sgx?tag=sgx_1.1.3)", + "sgx_tstd", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86 0.2.17", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "git+https://github.com/mesalock-linux/rand-sgx?tag=sgx_1.1.3#83583f073de3b4f75c3c3ef5e174d484ed941f85" +dependencies = [ + "ppv-lite86 0.2.6", + "rand_core 0.5.1 (git+https://github.com/mesalock-linux/rand-sgx?tag=sgx_1.1.3)", + "sgx_tstd", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86 0.2.17", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "git+https://github.com/mesalock-linux/rand-sgx?tag=sgx_1.1.3#83583f073de3b4f75c3c3ef5e174d484ed941f85" +dependencies = [ + "getrandom 0.1.14", + "sgx_tstd", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.10", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_pcg" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59cad018caf63deb318e5a4586d99a24424a364f40f1e5778c29aca23f4fc73e" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "rcgen" +version = "0.9.2" +source = "git+https://github.com/integritee-network/rcgen#1852c8dbeb74de36a422d218254b659497daf717" +dependencies = [ + "chrono 0.4.11", + "chrono 0.4.26", + "pem 0.8.2", + "pem 1.1.1", + "ring 0.16.19", + "ring 0.16.20", + "sgx_tstd", + "yasna 0.3.1", + "yasna 0.4.0", +] + +[[package]] +name = "rcgen" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" +dependencies = [ + "pem 1.1.1", + "ring 0.16.20", + "time 0.3.22", + "x509-parser 0.13.2", + "yasna 0.5.2", +] + +[[package]] +name = "rcgen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" +dependencies = [ + "pem 1.1.1", + "ring 0.16.20", + "time 0.3.22", + "yasna 0.5.2", +] + +[[package]] +name = "rdrand" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5014f842b5515f60c15d3bca398477951f785883f73e7f9bc8a9d9c9bb6821c7" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom 0.2.10", + "redox_syscall 0.2.16", + "thiserror 1.0.44", +] + +[[package]] +name = "ref-cast" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61ef7e18e8841942ddb1cf845054f8008410030a3997875d9e49b7a363063df1" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfaf0c85b766276c797f3791f5bc6d5bd116b41d53049af2789666b0c0bc9fa" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "regalloc2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300d4fbfb40c1c66a78ba3ddd41c1110247cf52f97b87d0f2fc9209bd49b030c" +dependencies = [ + "fxhash", + "log 0.4.20", + "slice-group-by", + "smallvec 1.11.0", +] + +[[package]] +name = "regex" +version = "1.3.1" +source = "git+https://github.com/mesalock-linux/regex-sgx#76aef86f9836532d17764523d0fa23bb7d2e31cf" +dependencies = [ + "regex-syntax 0.6.12", + "sgx_tstd", +] + +[[package]] +name = "regex" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" +dependencies = [ + "aho-corasick", + "memchr 2.6.3", + "regex-automata 0.3.8", + "regex-syntax 0.7.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +dependencies = [ + "aho-corasick", + "memchr 2.6.3", + "regex-syntax 0.7.5", +] + +[[package]] +name = "regex-syntax" +version = "0.6.12" +source = "git+https://github.com/mesalock-linux/regex-sgx#76aef86f9836532d17764523d0fa23bb7d2e31cf" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" + +[[package]] +name = "region" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" +dependencies = [ + "bitflags 1.3.2", + "libc", + "mach", + "winapi 0.3.9", +] + +[[package]] +name = "reqwest" +version = "0.11.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" +dependencies = [ + "base64 0.21.2", + "bytes 1.4.0", + "encoding_rs", + "futures-core 0.3.28", + "futures-util 0.3.28", + "h2", + "http 0.2.9", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log 0.4.20", + "mime", + "native-tls", + "once_cell 1.18.0", + "percent-encoding 2.3.0", + "pin-project-lite 0.2.10", + "serde 1.0.193", + "serde_json 1.0.103", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url 2.4.0", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "resolv-conf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +dependencies = [ + "hostname", + "quick-error", +] + +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint 0.4.9", + "hmac 0.12.1", + "zeroize", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac 0.12.1", + "subtle", +] + +[[package]] +name = "ring" +version = "0.16.19" +source = "git+https://github.com/mesalock-linux/ring-sgx?tag=v0.16.5#844efe271ed78a399d803b2579f5f2424d543c9f" +dependencies = [ + "cc", + "sgx_tstd", + "spin 0.5.2", + "untrusted 0.7.1", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "git+https://github.com/betrusted-io/ring-xous?branch=0.16.20-cleanup#4296c2e7904898766cf7d8d589759a129794783b" +dependencies = [ + "cc", + "libc", + "log 0.4.20", + "once_cell 1.18.0", + "rkyv", + "spin 0.5.2", + "untrusted 0.7.1", + "winapi 0.3.9", + "xous", + "xous-api-names", + "xous-ipc", +] + +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "rkyv" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70de01b38fe7baba4ecdd33b777096d2b326993d8ea99bc5b6ede691883d3010" +dependencies = [ + "memoffset 0.6.5", + "ptr_meta", + "rkyv_derive", +] + +[[package]] +name = "rkyv_derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a169f6bc5a81033e86ed39d0f4150e2608160b73d2b93c6e8e6a3efa873f14" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes 1.4.0", + "rlp-derive", + "rustc-hex", +] + +[[package]] +name = "rlp-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rocksdb" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "015439787fce1e75d55f279078d33ff14b4af5d93d995e8838ee4631301c8a99" +dependencies = [ + "libc", + "librocksdb-sys", +] + +[[package]] +name = "rococo-parachain-runtime" +version = "0.9.17" +dependencies = [ + "core-primitives", + "cumulus-pallet-aura-ext", + "cumulus-pallet-dmp-queue", + "cumulus-pallet-parachain-system", + "cumulus-pallet-xcm", + "cumulus-pallet-xcmp-queue", + "cumulus-primitives-core", + "cumulus-primitives-timestamp", + "cumulus-primitives-utility", + "fp-rpc", + "fp-self-contained", + "frame-benchmarking", + "frame-executive", + "frame-support", + "frame-system", + "frame-system-benchmarking", + "frame-system-rpc-runtime-api", + "frame-try-runtime", + "hex-literal", + "log 0.4.20", + "moonbeam-evm-tracer", + "moonbeam-rpc-primitives-debug", + "moonbeam-rpc-primitives-txpool", + "orml-tokens", + "orml-traits", + "orml-xtokens", + "pallet-asset-manager", + "pallet-aura", + "pallet-authorship", + "pallet-balances", + "pallet-bounties", + "pallet-bridge", + "pallet-bridge-transfer", + "pallet-collective", + "pallet-democracy", + "pallet-drop3", + "pallet-ethereum", + "pallet-evm 6.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", + "pallet-evm-precompile-blake2", + "pallet-evm-precompile-bn128", + "pallet-evm-precompile-bridge-transfer", + "pallet-evm-precompile-dispatch", + "pallet-evm-precompile-ed25519", + "pallet-evm-precompile-modexp", + "pallet-evm-precompile-parachain-staking", + "pallet-evm-precompile-sha3fips", + "pallet-evm-precompile-simple", + "pallet-extrinsic-filter", + "pallet-group", + "pallet-identity", + "pallet-identity-management", + "pallet-membership", + "pallet-multisig", + "pallet-parachain-staking", + "pallet-preimage", + "pallet-proxy", + "pallet-scheduler", + "pallet-session", + "pallet-sidechain", + "pallet-sudo", + "pallet-teeracle", + "pallet-teerex", + "pallet-timestamp", + "pallet-tips", + "pallet-transaction-payment", + "pallet-transaction-payment-rpc-runtime-api", + "pallet-treasury", + "pallet-utility", + "pallet-vc-management", + "pallet-vesting", + "pallet-xcm", + "parachain-info", + "parity-scale-codec", + "polkadot-parachain", + "runtime-common", + "scale-info", + "sp-api", + "sp-block-builder", + "sp-consensus-aura", + "sp-core", + "sp-inherents", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-offchain", + "sp-runtime", + "sp-session", + "sp-std 5.0.0", + "sp-transaction-pool", + "sp-version", + "substrate-wasm-builder", + "xcm", + "xcm-builder", + "xcm-executor", +] + +[[package]] +name = "ron" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a" +dependencies = [ + "base64 0.13.1", + "bitflags 1.3.2", + "serde 1.0.193", +] + +[[package]] +name = "rpassword" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6678cf63ab3491898c0d021b493c94c9b221d91295294a2a5746eacbe5928322" +dependencies = [ + "libc", + "rtoolbox", + "winapi 0.3.9", +] + +[[package]] +name = "rtcp" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1919efd6d4a6a85d13388f9487549bb8e359f17198cc03ffd72f79b553873691" +dependencies = [ + "bytes 1.4.0", + "thiserror 1.0.44", + "webrtc-util", +] + +[[package]] +name = "rtnetlink" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322c53fd76a18698f1c27381d58091de3a043d356aa5bd0d510608b565f469a0" +dependencies = [ + "futures 0.3.28", + "log 0.4.20", + "netlink-packet-route", + "netlink-proto", + "nix", + "thiserror 1.0.44", + "tokio", +] + +[[package]] +name = "rtoolbox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "034e22c514f5c0cb8a10ff341b9b048b5ceb21591f31c8f44c43b960f9b3524a" +dependencies = [ + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "rtp" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2a095411ff00eed7b12e4c6a118ba984d113e1079582570d56a5ee723f11f80" +dependencies = [ + "async-trait", + "bytes 1.4.0", + "rand 0.8.5", + "serde 1.0.193", + "thiserror 1.0.44", + "webrtc-util", +] + +[[package]] +name = "runtime-common" +version = "0.9.17" +dependencies = [ + "core-primitives", + "cumulus-pallet-parachain-system", + "cumulus-primitives-core", + "cumulus-primitives-parachain-inherent", + "frame-support", + "frame-system", + "log 0.4.20", + "orml-tokens", + "orml-traits", + "orml-xtokens", + "pallet-asset-manager", + "pallet-authorship", + "pallet-balances", + "pallet-collective", + "pallet-extrinsic-filter", + "pallet-group", + "pallet-membership", + "pallet-multisig", + "pallet-teerex", + "pallet-transaction-payment", + "pallet-treasury", + "pallet-vesting", + "pallet-xcm", + "parachain-info", + "parity-scale-codec", + "polkadot-parachain", + "polkadot-primitives", + "polkadot-runtime-parachains", + "scale-info", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-state-machine", + "sp-std 5.0.0", + "teerex-primitives", + "xcm", + "xcm-builder", + "xcm-executor", +] + +[[package]] +name = "rust-base58" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b313b91fcdc6719ad41fa2dad2b7e810b03833fae4bf911950e15529a5f04439" +dependencies = [ + "num 0.4.1", +] + +[[package]] +name = "rust-base58" +version = "0.0.4" +source = "git+https://github.com/mesalock-linux/rust-base58-sgx?rev=sgx_1.1.3#13fb3e0a543690e6e19332f37ba85fd74c56cb2f" +dependencies = [ + "num 0.2.0", + "sgx_tstd", +] + +[[package]] +name = "rust-ini" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" +dependencies = [ + "cfg-if 1.0.0", + "ordered-multimap", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver 1.0.18", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + +[[package]] +name = "rustix" +version = "0.36.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c37f1bd5ef1b5422177b7646cba67430579cfe2ace80f284fee876bca52ad941" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.1.4", + "windows-sys 0.45.0", +] + +[[package]] +name = "rustix" +version = "0.37.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" +dependencies = [ + "bitflags 2.3.3", + "errno", + "libc", + "linux-raw-sys 0.4.3", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustls" +version = "0.19.0" +source = "git+https://github.com/mesalock-linux/rustls?tag=sgx_1.1.3#95b5e79dc24b02f3ce424437eb9698509d0baf58" +dependencies = [ + "base64 0.13.0 (git+https://github.com/mesalock-linux/rust-base64-sgx)", + "log 0.4.14 (git+https://github.com/mesalock-linux/log-sgx)", + "ring 0.16.19", + "sct 0.6.0", + "sgx_tstd", + "webpki 0.21.4 (git+https://github.com/mesalock-linux/webpki?branch=mesalock_sgx)", +] + +[[package]] +name = "rustls" +version = "0.19.0" +source = "git+https://github.com/mesalock-linux/rustls?branch=mesalock_sgx#95b5e79dc24b02f3ce424437eb9698509d0baf58" +dependencies = [ + "base64 0.13.0 (git+https://github.com/mesalock-linux/rust-base64-sgx)", + "log 0.4.14 (git+https://github.com/mesalock-linux/log-sgx)", + "ring 0.16.19", + "sct 0.6.0", + "sgx_tstd", + "webpki 0.21.4 (git+https://github.com/mesalock-linux/webpki?branch=mesalock_sgx)", +] + +[[package]] +name = "rustls" +version = "0.19.0" +source = "git+https://github.com/mesalock-linux/rustls?rev=sgx_1.1.3#95b5e79dc24b02f3ce424437eb9698509d0baf58" +dependencies = [ + "base64 0.13.0 (git+https://github.com/mesalock-linux/rust-base64-sgx)", + "log 0.4.14 (git+https://github.com/mesalock-linux/log-sgx)", + "ring 0.16.19", + "sct 0.6.0", + "sgx_tstd", + "webpki 0.21.4 (git+https://github.com/mesalock-linux/webpki?branch=mesalock_sgx)", +] + +[[package]] +name = "rustls" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" +dependencies = [ + "base64 0.13.1", + "log 0.4.20", + "ring 0.16.20", + "sct 0.6.1", + "webpki 0.21.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustls" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +dependencies = [ + "log 0.4.20", + "ring 0.16.20", + "sct 0.7.0", + "webpki 0.22.0", +] + +[[package]] +name = "rustls-native-certs" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a07b7c1885bd8ed3831c289b7870b13ef46fe0e856d288c30d9cc17d75a2092" +dependencies = [ + "openssl-probe", + "rustls 0.19.1", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +dependencies = [ + "base64 0.21.2", +] + +[[package]] +name = "rustls-pki-types" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47003264dea418db67060fa420ad16d0d2f8f0a0360d825c00e177ac52cb5d8" + +[[package]] +name = "rustls-webpki" +version = "0.102.0-alpha.3" +source = "git+https://github.com/rustls/webpki?rev=da923ed#da923edaab56f599971e58773617fb574cd019dc" +dependencies = [ + "ring 0.16.20", + "rustls-pki-types", + "untrusted 0.9.0", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "rw-stream-sink" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26338f5e09bb721b85b135ea05af7767c90b52f6de4f087d4f4a3a9d64e7dc04" +dependencies = [ + "futures 0.3.28", + "pin-project", + "static_assertions", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "safe-lock" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "077d73db7973cccf63eb4aff1e5a34dc2459baa867512088269ea5f2f4253c90" + +[[package]] +name = "safe_arch" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f398075ce1e6a179b46f51bd88d0598b92b00d3551f1a2d4ac49e771b56ac354" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "sc-allocator" +version = "4.1.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "log 0.4.20", + "sp-core", + "sp-wasm-interface", + "thiserror 1.0.44", +] + +[[package]] +name = "sc-authority-discovery" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "async-trait", + "futures 0.3.28", + "futures-timer", + "ip_network", + "libp2p", + "log 0.4.20", + "parity-scale-codec", + "prost", + "prost-build", + "rand 0.8.5", + "sc-client-api", + "sc-network", + "sc-network-common", + "sp-api", + "sp-authority-discovery", + "sp-blockchain", + "sp-core", + "sp-keystore", + "sp-runtime", + "substrate-prometheus-endpoint", + "thiserror 1.0.44", +] + +[[package]] +name = "sc-block-builder" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "parity-scale-codec", + "sc-client-api", + "sp-api", + "sp-block-builder", + "sp-blockchain", + "sp-core", + "sp-inherents", + "sp-runtime", +] + +[[package]] +name = "sc-chain-spec" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "memmap2", + "sc-chain-spec-derive", + "sc-client-api", + "sc-executor", + "sc-network", + "sc-telemetry", + "serde 1.0.193", + "serde_json 1.0.103", + "sp-blockchain", + "sp-core", + "sp-runtime", + "sp-state-machine", +] + +[[package]] +name = "sc-chain-spec-derive" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "sc-cli" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "array-bytes 4.2.0", + "chrono 0.4.26", + "clap 4.1.0", + "fdlimit", + "futures 0.3.28", + "libp2p", + "log 0.4.20", + "names", + "parity-scale-codec", + "rand 0.8.5", + "regex 1.9.5", + "rpassword", + "sc-client-api", + "sc-client-db", + "sc-keystore", + "sc-network", + "sc-network-common", + "sc-service", + "sc-telemetry", + "sc-tracing", + "sc-utils", + "serde 1.0.193", + "serde_json 1.0.103", + "sp-blockchain", + "sp-core", + "sp-keyring", + "sp-keystore", + "sp-panic-handler", + "sp-runtime", + "sp-version", + "thiserror 1.0.44", + "tiny-bip39", + "tokio", +] + +[[package]] +name = "sc-client-api" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "fnv 1.0.7", + "futures 0.3.28", + "log 0.4.20", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-executor", + "sc-transaction-pool-api", + "sc-utils", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-database", + "sp-externalities", + "sp-keystore", + "sp-runtime", + "sp-state-machine", + "sp-storage", + "substrate-prometheus-endpoint", +] + +[[package]] +name = "sc-client-db" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "hash-db 0.16.0", + "kvdb", + "kvdb-memorydb", + "kvdb-rocksdb", + "linked-hash-map 0.5.6", + "log 0.4.20", + "parity-db", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-client-api", + "sc-state-db", + "schnellru", + "sp-arithmetic", + "sp-blockchain", + "sp-core", + "sp-database", + "sp-runtime", + "sp-state-machine", + "sp-trie", +] + +[[package]] +name = "sc-consensus" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "async-trait", + "futures 0.3.28", + "futures-timer", + "libp2p", + "log 0.4.20", + "mockall", + "parking_lot 0.12.1", + "sc-client-api", + "sc-utils", + "serde 1.0.193", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-runtime", + "sp-state-machine", + "substrate-prometheus-endpoint", + "thiserror 1.0.44", +] + +[[package]] +name = "sc-executor" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "lru 0.8.1", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-executor-common", + "sc-executor-wasmi", + "sc-executor-wasmtime", + "sp-api", + "sp-core", + "sp-externalities", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-panic-handler", + "sp-runtime-interface", + "sp-trie", + "sp-version", + "sp-wasm-interface", + "tracing", + "wasmi", +] + +[[package]] +name = "sc-executor-common" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "sc-allocator", + "sp-maybe-compressed-blob", + "sp-wasm-interface", + "thiserror 1.0.44", + "wasm-instrument", + "wasmi", +] + +[[package]] +name = "sc-executor-wasmi" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "log 0.4.20", + "sc-allocator", + "sc-executor-common", + "sp-runtime-interface", + "sp-wasm-interface", + "wasmi", +] + +[[package]] +name = "sc-executor-wasmtime" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "anyhow", + "cfg-if 1.0.0", + "libc", + "log 0.4.20", + "once_cell 1.18.0", + "rustix 0.36.15", + "sc-allocator", + "sc-executor-common", + "sp-runtime-interface", + "sp-wasm-interface", + "wasmtime", +] + +[[package]] +name = "sc-informant" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "ansi_term", + "futures 0.3.28", + "futures-timer", + "log 0.4.20", + "sc-client-api", + "sc-network", + "sc-network-common", + "sp-blockchain", + "sp-runtime", +] + +[[package]] +name = "sc-keystore" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "array-bytes 4.2.0", + "async-trait", + "parking_lot 0.12.1", + "serde_json 1.0.103", + "sp-application-crypto", + "sp-core", + "sp-keystore", + "thiserror 1.0.44", +] + +[[package]] +name = "sc-network" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "array-bytes 4.2.0", + "async-channel", + "async-trait", + "asynchronous-codec", + "bytes 1.4.0", + "either", + "fnv 1.0.7", + "futures 0.3.28", + "futures-timer", + "ip_network", + "libp2p", + "linked_hash_set", + "log 0.4.20", + "lru 0.8.1", + "mockall", + "parity-scale-codec", + "parking_lot 0.12.1", + "pin-project", + "rand 0.8.5", + "sc-block-builder", + "sc-client-api", + "sc-consensus", + "sc-network-common", + "sc-peerset", + "sc-utils", + "serde 1.0.193", + "serde_json 1.0.103", + "smallvec 1.11.0", + "snow", + "sp-arithmetic", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-runtime", + "substrate-prometheus-endpoint", + "thiserror 1.0.44", + "unsigned-varint 0.7.1", + "zeroize", +] + +[[package]] +name = "sc-network-bitswap" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "cid", + "futures 0.3.28", + "libp2p", + "log 0.4.20", + "prost", + "prost-build", + "sc-client-api", + "sc-network", + "sc-network-common", + "sp-blockchain", + "sp-runtime", + "thiserror 1.0.44", + "unsigned-varint 0.7.1", +] + +[[package]] +name = "sc-network-common" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "array-bytes 4.2.0", + "async-trait", + "bitflags 1.3.2", + "bytes 1.4.0", + "futures 0.3.28", + "futures-timer", + "libp2p", + "parity-scale-codec", + "prost-build", + "sc-consensus", + "sc-peerset", + "sc-utils", + "serde 1.0.193", + "smallvec 1.11.0", + "sp-blockchain", + "sp-consensus", + "sp-consensus-grandpa", + "sp-runtime", + "substrate-prometheus-endpoint", + "thiserror 1.0.44", + "zeroize", +] + +[[package]] +name = "sc-network-light" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "array-bytes 4.2.0", + "futures 0.3.28", + "libp2p", + "log 0.4.20", + "parity-scale-codec", + "prost", + "prost-build", + "sc-client-api", + "sc-network", + "sc-network-common", + "sc-peerset", + "sp-blockchain", + "sp-core", + "sp-runtime", + "thiserror 1.0.44", +] + +[[package]] +name = "sc-network-sync" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "array-bytes 4.2.0", + "async-trait", + "fork-tree 3.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "futures 0.3.28", + "futures-timer", + "libp2p", + "log 0.4.20", + "lru 0.8.1", + "mockall", + "parity-scale-codec", + "prost", + "prost-build", + "sc-client-api", + "sc-consensus", + "sc-network", + "sc-network-common", + "sc-peerset", + "sc-utils", + "smallvec 1.11.0", + "sp-arithmetic", + "sp-blockchain", + "sp-consensus", + "sp-consensus-grandpa", + "sp-core", + "sp-runtime", + "substrate-prometheus-endpoint", + "thiserror 1.0.44", +] + +[[package]] +name = "sc-network-transactions" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "array-bytes 4.2.0", + "futures 0.3.28", + "libp2p", + "log 0.4.20", + "parity-scale-codec", + "pin-project", + "sc-network", + "sc-network-common", + "sc-peerset", + "sc-utils", + "sp-consensus", + "sp-runtime", + "substrate-prometheus-endpoint", +] + +[[package]] +name = "sc-offchain" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "array-bytes 4.2.0", + "bytes 1.4.0", + "fnv 1.0.7", + "futures 0.3.28", + "futures-timer", + "hyper", + "hyper-rustls 0.23.2", + "libp2p", + "num_cpus", + "once_cell 1.18.0", + "parity-scale-codec", + "parking_lot 0.12.1", + "rand 0.8.5", + "sc-client-api", + "sc-network", + "sc-network-common", + "sc-peerset", + "sc-utils", + "sp-api", + "sp-core", + "sp-offchain", + "sp-runtime", + "threadpool", + "tracing", +] + +[[package]] +name = "sc-peerset" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "futures 0.3.28", + "libp2p", + "log 0.4.20", + "sc-utils", + "serde_json 1.0.103", + "wasm-timer", +] + +[[package]] +name = "sc-rpc" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "futures 0.3.28", + "jsonrpsee 0.16.2", + "log 0.4.20", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-block-builder", + "sc-chain-spec", + "sc-client-api", + "sc-rpc-api", + "sc-tracing", + "sc-transaction-pool-api", + "sc-utils", + "serde_json 1.0.103", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-keystore", + "sp-offchain", + "sp-rpc", + "sp-runtime", + "sp-session", + "sp-version", + "tokio", +] + +[[package]] +name = "sc-rpc-api" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "jsonrpsee 0.16.2", + "parity-scale-codec", + "sc-chain-spec", + "sc-transaction-pool-api", + "scale-info", + "serde 1.0.193", + "serde_json 1.0.103", + "sp-core", + "sp-rpc", + "sp-runtime", + "sp-version", + "thiserror 1.0.44", +] + +[[package]] +name = "sc-rpc-server" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "http 0.2.9", + "jsonrpsee 0.16.2", + "log 0.4.20", + "serde_json 1.0.103", + "substrate-prometheus-endpoint", + "tokio", + "tower", + "tower-http", +] + +[[package]] +name = "sc-rpc-spec-v2" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "array-bytes 4.2.0", + "futures 0.3.28", + "futures-util 0.3.28", + "hex", + "jsonrpsee 0.16.2", + "log 0.4.20", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-chain-spec", + "sc-client-api", + "sc-transaction-pool-api", + "serde 1.0.193", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-runtime", + "sp-version", + "thiserror 1.0.44", + "tokio-stream", +] + +[[package]] +name = "sc-service" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "async-trait", + "directories", + "exit-future", + "futures 0.3.28", + "futures-timer", + "jsonrpsee 0.16.2", + "log 0.4.20", + "parity-scale-codec", + "parking_lot 0.12.1", + "pin-project", + "rand 0.8.5", + "sc-block-builder", + "sc-chain-spec", + "sc-client-api", + "sc-client-db", + "sc-consensus", + "sc-executor", + "sc-informant", + "sc-keystore", + "sc-network", + "sc-network-bitswap", + "sc-network-common", + "sc-network-light", + "sc-network-sync", + "sc-network-transactions", + "sc-offchain", + "sc-rpc", + "sc-rpc-server", + "sc-rpc-spec-v2", + "sc-storage-monitor", + "sc-sysinfo", + "sc-telemetry", + "sc-tracing", + "sc-transaction-pool", + "sc-transaction-pool-api", + "sc-utils", + "serde 1.0.193", + "serde_json 1.0.103", + "sp-api", + "sp-blockchain", + "sp-consensus", + "sp-core", + "sp-externalities", + "sp-keystore", + "sp-runtime", + "sp-session", + "sp-state-machine", + "sp-storage", + "sp-transaction-pool", + "sp-transaction-storage-proof", + "sp-trie", + "sp-version", + "static_init", + "substrate-prometheus-endpoint", + "tempfile", + "thiserror 1.0.44", + "tokio", + "tracing", + "tracing-futures", +] + +[[package]] +name = "sc-state-db" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "log 0.4.20", + "parity-scale-codec", + "parking_lot 0.12.1", + "sp-core", +] + +[[package]] +name = "sc-storage-monitor" +version = "0.1.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "clap 4.1.0", + "fs4", + "futures 0.3.28", + "log 0.4.20", + "sc-client-db", + "sc-utils", + "sp-core", + "thiserror 1.0.44", + "tokio", +] + +[[package]] +name = "sc-sysinfo" +version = "6.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "futures 0.3.28", + "libc", + "log 0.4.20", + "rand 0.8.5", + "rand_pcg", + "regex 1.9.5", + "sc-telemetry", + "serde 1.0.193", + "serde_json 1.0.103", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-std 5.0.0", +] + +[[package]] +name = "sc-telemetry" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "chrono 0.4.26", + "futures 0.3.28", + "libp2p", + "log 0.4.20", + "parking_lot 0.12.1", + "pin-project", + "rand 0.8.5", + "sc-utils", + "serde 1.0.193", + "serde_json 1.0.103", + "thiserror 1.0.44", + "wasm-timer", +] + +[[package]] +name = "sc-tracing" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "ansi_term", + "atty", + "chrono 0.4.26", + "lazy_static", + "libc", + "log 0.4.20", + "once_cell 1.18.0", + "parking_lot 0.12.1", + "regex 1.9.5", + "rustc-hash", + "sc-client-api", + "sc-rpc-server", + "sc-tracing-proc-macro", + "serde 1.0.193", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-rpc", + "sp-runtime", + "sp-tracing", + "thiserror 1.0.44", + "tracing", + "tracing-log", + "tracing-subscriber", +] + +[[package]] +name = "sc-tracing-proc-macro" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "sc-transaction-pool" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "async-trait", + "futures 0.3.28", + "futures-timer", + "linked-hash-map 0.5.6", + "log 0.4.20", + "num-traits 0.2.16", + "parity-scale-codec", + "parking_lot 0.12.1", + "sc-client-api", + "sc-transaction-pool-api", + "sc-utils", + "serde 1.0.193", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-runtime", + "sp-tracing", + "sp-transaction-pool", + "substrate-prometheus-endpoint", + "thiserror 1.0.44", +] + +[[package]] +name = "sc-transaction-pool-api" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "async-trait", + "futures 0.3.28", + "log 0.4.20", + "serde 1.0.193", + "sp-blockchain", + "sp-runtime", + "thiserror 1.0.44", +] + +[[package]] +name = "sc-utils" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "async-channel", + "futures 0.3.28", + "futures-timer", + "lazy_static", + "log 0.4.20", + "parking_lot 0.12.1", + "prometheus", + "sp-arithmetic", +] + +[[package]] +name = "scale-bits" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dd7aca73785181cc41f0bbe017263e682b585ca660540ba569133901d013ecf" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde 1.0.193", +] + +[[package]] +name = "scale-bits" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "036575c29af9b6e4866ffb7fa055dbf623fe7a9cc159b33786de6013a6969d89" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde 1.0.193", +] + +[[package]] +name = "scale-decode" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d823d4be477fc33321f93d08fb6c2698273d044f01362dc27573a750deb7c233" +dependencies = [ + "parity-scale-codec", + "scale-bits 0.3.0", + "scale-info", + "thiserror 1.0.44", +] + +[[package]] +name = "scale-decode" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea509715113edab351e1f4d51fba6b186653259049a1155b52e2e994dd2f0e6d" +dependencies = [ + "parity-scale-codec", + "primitive-types", + "scale-bits 0.4.0", + "scale-decode-derive", + "scale-info", + "smallvec 1.11.0", +] + +[[package]] +name = "scale-decode-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66c9d7a1341497e9d016722144310de3dc6c933909c0376017c88f65092fff37" +dependencies = [ + "darling", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "scale-encode" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6f51bc8cd927dab2f4567b1a8a8e9d7fd5d0866f2dbc7c84fc97cfa9383a26" +dependencies = [ + "parity-scale-codec", + "primitive-types", + "scale-bits 0.4.0", + "scale-encode-derive", + "scale-info", + "smallvec 1.11.0", +] + +[[package]] +name = "scale-encode-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28be1877787156a2df01be3c029b92bdffa6b6a9748d4996e383fff218c88f3" +dependencies = [ + "darling", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "scale-info" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7d66a1128282b7ef025a8ead62a4a9fcf017382ec53b8ffbf4d7bf77bd3c60" +dependencies = [ + "bitvec", + "cfg-if 1.0.0", + "derive_more", + "parity-scale-codec", + "scale-info-derive", + "serde 1.0.193", +] + +[[package]] +name = "scale-info-derive" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf2c68b89cafb3b8d918dd07b42be0da66ff202cf1155c5739a4e0c1ea0dc19" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "scale-value" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16a5e7810815bd295da73e4216d1dfbced3c7c7c7054d70fa5f6e4c58123fff4" +dependencies = [ + "either", + "frame-metadata", + "parity-scale-codec", + "scale-bits 0.3.0", + "scale-decode 0.4.0", + "scale-info", + "serde 1.0.193", + "thiserror 1.0.44", + "yap", +] + +[[package]] +name = "schannel" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "schnellru" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "772575a524feeb803e5b0fcbc6dd9f367e579488197c94c6e4023aad2305774d" +dependencies = [ + "ahash 0.8.3", + "cfg-if 1.0.0", + "hashbrown 0.13.2", +] + +[[package]] +name = "schnorrkel" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "021b403afe70d81eea68f6ea12f6b3c9588e5d536a94c3bf80f15e7faa267862" +dependencies = [ + "arrayref", + "arrayvec 0.5.2", + "curve25519-dalek 2.1.3", + "getrandom 0.1.16", + "merlin", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.2", + "subtle", + "zeroize", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "scratch" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" + +[[package]] +name = "sct" +version = "0.6.0" +source = "git+https://github.com/mesalock-linux/sct.rs?branch=mesalock_sgx#c4d859cca232e6c9d88ca12048df3bc26e1ed4ad" +dependencies = [ + "ring 0.16.19", + "sgx_tstd", + "untrusted 0.7.1", +] + +[[package]] +name = "sct" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" +dependencies = [ + "ring 0.16.20", + "untrusted 0.7.1", +] + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring 0.16.20", + "untrusted 0.7.1", +] + +[[package]] +name = "sdp" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d22a5ef407871893fd72b4562ee15e4742269b173959db4b8df6f538c414e13" +dependencies = [ + "rand 0.8.5", + "substring", + "thiserror 1.0.44", + "url 2.4.0", +] + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct 0.1.1", + "der 0.6.1", + "generic-array 0.14.7", + "pkcs8 0.9.0", + "subtle", + "zeroize", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct 0.2.0", + "der 0.7.8", + "generic-array 0.14.7", + "pkcs8 0.10.2", + "subtle", + "zeroize", +] + +[[package]] +name = "secp256k1" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" +dependencies = [ + "secp256k1-sys 0.6.1", +] + +[[package]] +name = "secp256k1" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acea373acb8c21ecb5a23741452acd2593ed44ee3d343e72baaa143bc89d0d5" +dependencies = [ + "bitcoin_hashes", + "secp256k1-sys 0.9.1", +] + +[[package]] +name = "secp256k1-sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83080e2c2fc1006e625be82e5d1eb6a43b7fd9578b617fcc55814daf286bba4b" +dependencies = [ + "cc", +] + +[[package]] +name = "secp256k1-sys" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dd97a086ec737e30053fd5c46f097465d25bb81dd3608825f65298c4c98be83" +dependencies = [ + "cc", +] + +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" +dependencies = [ + "serde 1.0.193", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.118" +source = "git+https://github.com/mesalock-linux/serde-sgx#db0226f1d5d70fca6b96af2c285851502204e21c" +dependencies = [ + "serde_derive 1.0.118", + "sgx_tstd", +] + +[[package]] +name = "serde" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +dependencies = [ + "serde_derive 1.0.193", +] + +[[package]] +name = "serde-big-array" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b926cfbabfe8011609dda0350cb24d884955d294909ac71c0db7027366c77e3e" +dependencies = [ + "serde 1.0.193", + "serde_derive 1.0.193", +] + +[[package]] +name = "serde-big-array" +version = "0.3.0" +source = "git+https://github.com/mesalock-linux/serde-big-array-sgx#94122c5167aee38b39b09a620a60db2c28cf7428" +dependencies = [ + "serde 1.0.118", + "serde_derive 1.0.118", +] + +[[package]] +name = "serde_derive" +version = "1.0.118" +source = "git+https://github.com/mesalock-linux/serde-sgx#db0226f1d5d70fca6b96af2c285851502204e21c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "serde_derive" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "serde_json" +version = "1.0.60" +source = "git+https://github.com/mesalock-linux/serde-json-sgx?tag=sgx_1.1.3#380893814ad2a057758d825bab798aa117f7362a" +dependencies = [ + "indexmap 1.6.1", + "itoa 0.4.5", + "ryu", + "serde 1.0.118", + "sgx_tstd", +] + +[[package]] +name = "serde_json" +version = "1.0.60" +source = "git+https://github.com/mesalock-linux/serde-json-sgx#380893814ad2a057758d825bab798aa117f7362a" +dependencies = [ + "itoa 0.4.5", + "ryu", + "serde 1.0.118", + "sgx_tstd", +] + +[[package]] +name = "serde_json" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d03b412469450d4404fe8499a268edd7f8b79fecb074b0d812ad64ca21f4031b" +dependencies = [ + "indexmap 2.0.0", + "itoa 1.0.9", + "ryu", + "serde 1.0.193", +] + +[[package]] +name = "serde_spanned" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +dependencies = [ + "serde 1.0.193", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa 1.0.9", + "ryu", + "serde 1.0.193", +] + +[[package]] +name = "sgx-verify" +version = "0.1.4" +dependencies = [ + "base64 0.13.1", + "chrono 0.4.26", + "der 0.6.1", + "frame-support", + "hex", + "parity-scale-codec", + "ring 0.16.20", + "rustls-webpki", + "scale-info", + "serde 1.0.193", + "serde_json 1.0.103", + "sp-core", + "sp-std 5.0.0", + "teerex-primitives", + "x509-cert", +] + +[[package]] +name = "sgx_alloc" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" + +[[package]] +name = "sgx_backtrace_sys" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" +dependencies = [ + "cc", + "sgx_build_helper", + "sgx_libc", +] + +[[package]] +name = "sgx_build_helper" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" + +[[package]] +name = "sgx_crypto_helper" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" +dependencies = [ + "itertools 0.11.0", + "libc", + "serde 1.0.118", + "serde 1.0.193", + "serde-big-array 0.1.5", + "serde-big-array 0.3.0", + "serde_derive 1.0.118", + "serde_derive 1.0.193", + "sgx_tcrypto", + "sgx_tstd", + "sgx_types", + "sgx_ucrypto", +] + +[[package]] +name = "sgx_demangle" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" + +[[package]] +name = "sgx_libc" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" +dependencies = [ + "sgx_types", +] + +[[package]] +name = "sgx_rand" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" +dependencies = [ + "sgx_trts", + "sgx_tstd", + "sgx_types", +] + +[[package]] +name = "sgx_tcrypto" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" +dependencies = [ + "sgx_types", +] + +[[package]] +name = "sgx_tprotected_fs" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" +dependencies = [ + "sgx_trts", + "sgx_types", +] + +[[package]] +name = "sgx_trts" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" +dependencies = [ + "sgx_libc", + "sgx_types", +] + +[[package]] +name = "sgx_tse" +version = "1.1.6" +source = "git+https://github.com/apache/teaclave-sgx-sdk.git?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" +dependencies = [ + "sgx_types", +] + +[[package]] +name = "sgx_tstd" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" +dependencies = [ + "hashbrown_tstd", + "sgx_alloc", + "sgx_backtrace_sys", + "sgx_demangle", + "sgx_libc", + "sgx_tprotected_fs", + "sgx_trts", + "sgx_types", + "sgx_unwind", +] + +[[package]] +name = "sgx_types" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" + +[[package]] +name = "sgx_ucrypto" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" +dependencies = [ + "libc", + "rand_core 0.3.1", + "rdrand", + "sgx_types", +] + +[[package]] +name = "sgx_unwind" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" +dependencies = [ + "sgx_build_helper", +] + +[[package]] +name = "sgx_urts" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" +dependencies = [ + "libc", + "sgx_types", +] + +[[package]] +name = "sha-1" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + +[[package]] +name = "sha-1" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + +[[package]] +name = "sha1" +version = "0.6.0" +source = "git+https://github.com/mesalock-linux/rust-sha1-sgx?tag=sgx_1.1.3#482a4d489e860d63a21662aaea988f600f8e20a4" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + +[[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 0.3.0", +] + +[[package]] +name = "sha2" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" + +[[package]] +name = "sidechain-primitives" +version = "0.1.0" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "sp-core", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "signature" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "simba" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae" +dependencies = [ + "approx", + "num-complex 0.4.3", + "num-traits 0.2.16", + "paste", + "wide", +] + +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + +[[package]] +name = "slab" +version = "0.4.2" +source = "git+https://github.com/mesalock-linux/slab-sgx#0b0e6ec2abd588afd2f40fd082bc473d100d0f40" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg 1.1.0", +] + +[[package]] +name = "slice-group-by" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" + +[[package]] +name = "slot-range-helper" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" +dependencies = [ + "enumn", + "parity-scale-codec", + "paste", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "smallvec" +version = "1.6.1" +source = "git+https://github.com/mesalock-linux/rust-smallvec-sgx#b5925f10aa5bc3370a0fb339140ee063f5a888dd" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "smallvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" + +[[package]] +name = "snap" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e9f0ab6ef7eb7353d9119c170a436d1bf248eea575ac42d19d12f4e34130831" + +[[package]] +name = "snow" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ccba027ba85743e09d15c03296797cad56395089b832b48b5a5217880f57733" +dependencies = [ + "aes-gcm 0.9.4", + "blake2", + "chacha20poly1305", + "curve25519-dalek 4.0.0-rc.1", + "rand_core 0.6.4", + "ring 0.16.20", + "rustc_version", + "sha2 0.10.7", + "subtle", +] + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "socket2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "soketto" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4919971d141dbadaa0e82b5d369e2d7666c98e4625046140615ca363e50d4daa" +dependencies = [ + "base64 0.13.1", + "bytes 1.4.0", + "futures 0.3.28", + "httparse 1.8.0", + "log 0.4.20", + "rand 0.8.5", + "sha-1 0.9.8", +] + +[[package]] +name = "soketto" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" +dependencies = [ + "base64 0.13.1", + "bytes 1.4.0", + "flate2", + "futures 0.3.28", + "http 0.2.9", + "httparse 1.8.0", + "log 0.4.20", + "rand 0.8.5", + "sha-1 0.9.8", +] + +[[package]] +name = "sp-api" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "hash-db 0.16.0", + "log 0.4.20", + "parity-scale-codec", + "scale-info", + "sp-api-proc-macro", + "sp-core", + "sp-metadata-ir", + "sp-runtime", + "sp-state-machine", + "sp-std 5.0.0", + "sp-trie", + "sp-version", + "thiserror 1.0.44", +] + +[[package]] +name = "sp-api-proc-macro" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "Inflector", + "blake2", + "expander 1.0.0", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "sp-application-crypto" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-std 5.0.0", +] + +[[package]] +name = "sp-arithmetic" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "integer-sqrt", + "num-traits 0.2.16", + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "sp-std 5.0.0", + "static_assertions", +] + +[[package]] +name = "sp-authority-discovery" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-application-crypto", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "sp-block-builder" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "parity-scale-codec", + "sp-api", + "sp-inherents", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "sp-blockchain" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "futures 0.3.28", + "log 0.4.20", + "lru 0.8.1", + "parity-scale-codec", + "parking_lot 0.12.1", + "sp-api", + "sp-consensus", + "sp-database", + "sp-runtime", + "sp-state-machine", + "thiserror 1.0.44", +] + +[[package]] +name = "sp-consensus" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "async-trait", + "futures 0.3.28", + "log 0.4.20", + "sp-core", + "sp-inherents", + "sp-runtime", + "sp-state-machine", + "thiserror 1.0.44", +] + +[[package]] +name = "sp-consensus-aura" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "async-trait", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-application-crypto", + "sp-consensus", + "sp-consensus-slots", + "sp-inherents", + "sp-runtime", + "sp-std 5.0.0", + "sp-timestamp", +] + +[[package]] +name = "sp-consensus-babe" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "async-trait", + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "sp-api", + "sp-application-crypto", + "sp-consensus", + "sp-consensus-slots", + "sp-core", + "sp-inherents", + "sp-keystore", + "sp-runtime", + "sp-std 5.0.0", + "sp-timestamp", +] + +[[package]] +name = "sp-consensus-grandpa" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "finality-grandpa", + "log 0.4.20", + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "sp-api", + "sp-application-crypto", + "sp-core", + "sp-keystore", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "sp-consensus-slots" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "sp-std 5.0.0", + "sp-timestamp", +] + +[[package]] +name = "sp-core" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "array-bytes 4.2.0", + "bitflags 1.3.2", + "blake2", + "bounded-collections", + "bs58", + "dyn-clonable", + "ed25519-zebra", + "futures 0.3.28", + "hash-db 0.16.0", + "hash256-std-hasher", + "impl-serde", + "lazy_static", + "libsecp256k1", + "log 0.4.20", + "merlin", + "parity-scale-codec", + "parking_lot 0.12.1", + "paste", + "primitive-types", + "rand 0.8.5", + "regex 1.9.5", + "scale-info", + "schnorrkel", + "secp256k1 0.24.3", + "secrecy", + "serde 1.0.193", + "sp-core-hashing 5.0.0", + "sp-debug-derive", + "sp-externalities", + "sp-runtime-interface", + "sp-std 5.0.0", + "sp-storage", + "ss58-registry", + "substrate-bip39", + "thiserror 1.0.44", + "tiny-bip39", + "zeroize", +] + +[[package]] +name = "sp-core-hashing" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "blake2b_simd", + "byteorder 1.4.3", + "digest 0.10.7", + "sha2 0.10.7", + "sha3", + "sp-std 5.0.0", + "twox-hash", +] + +[[package]] +name = "sp-core-hashing" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc2d1947252b7a4e403b0a260f596920443742791765ec111daa2bbf98eff25" +dependencies = [ + "blake2", + "byteorder 1.4.3", + "digest 0.10.7", + "sha2 0.10.7", + "sha3", + "sp-std 6.0.0", + "twox-hash", +] + +[[package]] +name = "sp-core-hashing-proc-macro" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "proc-macro2", + "quote", + "sp-core-hashing 5.0.0", + "syn 2.0.32", +] + +[[package]] +name = "sp-database" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "kvdb", + "parking_lot 0.12.1", +] + +[[package]] +name = "sp-debug-derive" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "sp-externalities" +version = "0.13.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "environmental 1.1.4", + "parity-scale-codec", + "sp-std 5.0.0", + "sp-storage", +] + +[[package]] +name = "sp-inherents" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "async-trait", + "impl-trait-for-tuples", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", + "thiserror 1.0.44", +] + +[[package]] +name = "sp-io" +version = "7.0.0" +dependencies = [ + "itp-sgx-externalities", + "libsecp256k1", + "log 0.4.20", + "parity-scale-codec", + "sgx_tstd", + "sp-core", +] + +[[package]] +name = "sp-io" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "bytes 1.4.0", + "ed25519", + "ed25519-dalek", + "futures 0.3.28", + "libsecp256k1", + "log 0.4.20", + "parity-scale-codec", + "rustversion", + "secp256k1 0.24.3", + "sp-core", + "sp-externalities", + "sp-keystore", + "sp-runtime-interface", + "sp-state-machine", + "sp-std 5.0.0", + "sp-tracing", + "sp-trie", + "tracing", + "tracing-core", +] + +[[package]] +name = "sp-keyring" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "lazy_static", + "sp-core", + "sp-runtime", + "strum 0.24.1", +] + +[[package]] +name = "sp-keystore" +version = "0.13.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "futures 0.3.28", + "parity-scale-codec", + "parking_lot 0.12.1", + "serde 1.0.193", + "sp-core", + "sp-externalities", + "thiserror 1.0.44", +] + +[[package]] +name = "sp-maybe-compressed-blob" +version = "4.1.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "thiserror 1.0.44", + "zstd 0.12.4", +] + +[[package]] +name = "sp-metadata-ir" +version = "0.1.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-metadata", + "parity-scale-codec", + "scale-info", + "sp-std 5.0.0", +] + +[[package]] +name = "sp-npos-elections" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "sp-arithmetic", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "sp-offchain" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "sp-api", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "sp-panic-handler" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "backtrace", + "lazy_static", + "regex 1.9.5", +] + +[[package]] +name = "sp-rpc" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "rustc-hash", + "serde 1.0.193", + "sp-core", +] + +[[package]] +name = "sp-runtime" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "either", + "hash256-std-hasher", + "impl-trait-for-tuples", + "log 0.4.20", + "parity-scale-codec", + "paste", + "rand 0.8.5", + "scale-info", + "serde 1.0.193", + "sp-application-crypto", + "sp-arithmetic", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-std 5.0.0", + "sp-weights", +] + +[[package]] +name = "sp-runtime-interface" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "bytes 1.4.0", + "impl-trait-for-tuples", + "parity-scale-codec", + "primitive-types", + "sp-externalities", + "sp-runtime-interface-proc-macro", + "sp-std 5.0.0", + "sp-storage", + "sp-tracing", + "sp-wasm-interface", + "static_assertions", +] + +[[package]] +name = "sp-runtime-interface-proc-macro" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "Inflector", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "sp-session" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-core", + "sp-runtime", + "sp-staking", + "sp-std 5.0.0", +] + +[[package]] +name = "sp-staking" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "sp-core", + "sp-runtime", + "sp-std 5.0.0", +] + +[[package]] +name = "sp-state-machine" +version = "0.13.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "hash-db 0.16.0", + "log 0.4.20", + "parity-scale-codec", + "parking_lot 0.12.1", + "rand 0.8.5", + "smallvec 1.11.0", + "sp-core", + "sp-externalities", + "sp-panic-handler", + "sp-std 5.0.0", + "sp-trie", + "thiserror 1.0.44", + "tracing", +] + +[[package]] +name = "sp-std" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" + +[[package]] +name = "sp-std" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af0ee286f98455272f64ac5bb1384ff21ac029fbb669afbaf48477faff12760e" + +[[package]] +name = "sp-storage" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "impl-serde", + "parity-scale-codec", + "ref-cast", + "serde 1.0.193", + "sp-debug-derive", + "sp-std 5.0.0", +] + +[[package]] +name = "sp-timestamp" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "async-trait", + "futures-timer", + "log 0.4.20", + "parity-scale-codec", + "sp-inherents", + "sp-runtime", + "sp-std 5.0.0", + "thiserror 1.0.44", +] + +[[package]] +name = "sp-tracing" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "parity-scale-codec", + "sp-std 5.0.0", + "tracing", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "sp-transaction-pool" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "sp-api", + "sp-runtime", +] + +[[package]] +name = "sp-transaction-storage-proof" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "async-trait", + "log 0.4.20", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-inherents", + "sp-runtime", + "sp-std 5.0.0", + "sp-trie", +] + +[[package]] +name = "sp-trie" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "ahash 0.8.3", + "hash-db 0.16.0", + "hashbrown 0.13.2", + "lazy_static", + "memory-db", + "nohash-hasher", + "parity-scale-codec", + "parking_lot 0.12.1", + "scale-info", + "schnellru", + "sp-core", + "sp-std 5.0.0", + "thiserror 1.0.44", + "tracing", + "trie-db", + "trie-root", +] + +[[package]] +name = "sp-version" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "impl-serde", + "parity-scale-codec", + "parity-wasm", + "scale-info", + "serde 1.0.193", + "sp-core-hashing-proc-macro", + "sp-runtime", + "sp-std 5.0.0", + "sp-version-proc-macro", + "thiserror 1.0.44", +] + +[[package]] +name = "sp-version-proc-macro" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "parity-scale-codec", + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "sp-wasm-interface" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "anyhow", + "impl-trait-for-tuples", + "log 0.4.20", + "parity-scale-codec", + "sp-std 5.0.0", + "wasmi", + "wasmtime", +] + +[[package]] +name = "sp-weights" +version = "4.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "smallvec 1.11.0", + "sp-arithmetic", + "sp-core", + "sp-debug-derive", + "sp-std 5.0.0", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der 0.6.1", +] + +[[package]] +name = "spki" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +dependencies = [ + "base64ct", + "der 0.7.8", +] + +[[package]] +name = "ss58-registry" +version = "1.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfc443bad666016e012538782d9e3006213a7db43e9fb1dda91657dc06a6fa08" +dependencies = [ + "Inflector", + "num-format", + "proc-macro2", + "quote", + "serde 1.0.193", + "serde_json 1.0.103", + "unicode-xid", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "static_init" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a2a1c578e98c1c16fc3b8ec1328f7659a500737d7a0c6d625e73e830ff9c1f6" +dependencies = [ + "bitflags 1.3.2", + "cfg_aliases", + "libc", + "parking_lot 0.11.2", + "parking_lot_core 0.8.6", + "static_init_macro", + "winapi 0.3.9", +] + +[[package]] +name = "static_init_macro" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70a2595fc3aa78f2d0e45dd425b22282dd863273761cc77780914b2cf3003acf" +dependencies = [ + "cfg_aliases", + "memchr 2.6.3", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +dependencies = [ + "strum_macros 0.24.3", +] + +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 1.0.109", +] + +[[package]] +name = "strum_macros" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.32", +] + +[[package]] +name = "stun" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7e94b1ec00bad60e6410e058b52f1c66de3dc5fe4d62d09b3e52bb7d3b73e25" +dependencies = [ + "base64 0.13.1", + "crc", + "lazy_static", + "md-5", + "rand 0.8.5", + "ring 0.16.20", + "subtle", + "thiserror 1.0.44", + "tokio", + "url 2.4.0", + "webrtc-util", +] + +[[package]] +name = "substrate-api-client" +version = "0.14.0" +source = "git+https://github.com/scs/substrate-api-client.git?branch=polkadot-v0.9.42-tag-v0.14.0#e4ed74b0fb6c2fd5585f55c2702b97b56d99c7f6" +dependencies = [ + "ac-compose-macros", + "ac-node-api", + "ac-primitives", + "async-trait", + "derive_more", + "frame-metadata", + "frame-support", + "hex", + "log 0.4.20", + "maybe-async", + "parity-scale-codec", + "serde 1.0.193", + "serde_json 1.0.103", + "sp-core", + "sp-runtime", + "sp-runtime-interface", + "tungstenite 0.18.0", + "url 2.4.0", +] + +[[package]] +name = "substrate-bip39" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49eee6965196b32f882dd2ee85a92b1dbead41b04e53907f269de3b0dc04733c" +dependencies = [ + "hmac 0.11.0", + "pbkdf2 0.8.0", + "schnorrkel", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "substrate-bn" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b5bbfa79abbae15dd642ea8176a21a635ff3c00059961d1ea27ad04e5b441c" +dependencies = [ + "byteorder 1.4.3", + "crunchy", + "lazy_static", + "rand 0.8.5", + "rustc-hex", +] + +[[package]] +name = "substrate-client-keystore" +version = "0.9.1" +source = "git+https://github.com/scs/substrate-api-client.git?branch=polkadot-v0.9.42-tag-v0.14.0#e4ed74b0fb6c2fd5585f55c2702b97b56d99c7f6" +dependencies = [ + "array-bytes 4.2.0", + "async-trait", + "parking_lot 0.12.1", + "sc-keystore", + "serde_json 1.0.103", + "sp-application-crypto", + "sp-core", + "sp-keyring", + "sp-keystore", +] + +[[package]] +name = "substrate-fixed" +version = "0.5.9" +source = "git+https://github.com/encointer/substrate-fixed?tag=v0.5.9#a4fb461aae6205ffc55bed51254a40c52be04e5d" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "typenum 1.16.0 (git+https://github.com/encointer/typenum?tag=v1.16.0)", +] + +[[package]] +name = "substrate-fixed" +version = "0.5.9" +source = "git+https://github.com/encointer/substrate-fixed#a4fb461aae6205ffc55bed51254a40c52be04e5d" +dependencies = [ + "parity-scale-codec", + "scale-info", + "typenum 1.16.0 (git+https://github.com/encointer/typenum?tag=v1.16.0)", +] + +[[package]] +name = "substrate-prometheus-endpoint" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "hyper", + "log 0.4.20", + "prometheus", + "thiserror 1.0.44", + "tokio", +] + +[[package]] +name = "substrate-wasm-builder" +version = "5.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "ansi_term", + "build-helper", + "cargo_metadata", + "filetime", + "sp-maybe-compressed-blob", + "strum 0.24.1", + "tempfile", + "toml 0.7.8", + "walkdir", + "wasm-opt", +] + +[[package]] +name = "substring" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ee6433ecef213b2e72f587ef64a2f5943e7cd16fbd82dbe8bc07486c534c86" +dependencies = [ + "autocfg 1.1.0", +] + +[[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.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-xid", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "target-lexicon" +version = "0.12.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2faeef5759ab89935255b1a4cd98e0baf99d1085e37d36599c625dac49ae8e" + +[[package]] +name = "teeracle-primitives" +version = "0.1.0" +dependencies = [ + "common-primitives", + "substrate-fixed 0.5.9 (git+https://github.com/encointer/substrate-fixed?tag=v0.5.9)", +] + +[[package]] +name = "teerex-primitives" +version = "0.1.0" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-std 5.0.0", +] + +[[package]] +name = "temp-dir" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af547b166dd1ea4b472165569fc456cfb6818116f854690b0ff205e636523dab" + +[[package]] +name = "tempfile" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998" +dependencies = [ + "cfg-if 1.0.0", + "fastrand 2.0.0", + "redox_syscall 0.3.5", + "rustix 0.38.4", + "windows-sys 0.48.0", +] + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "termtree" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.9" +source = "git+https://github.com/mesalock-linux/thiserror-sgx?tag=sgx_1.1.3#c2f806b88616e06aab0af770366a76885d974fdc" +dependencies = [ + "sgx_tstd", + "thiserror-impl 1.0.9", +] + +[[package]] +name = "thiserror" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" +dependencies = [ + "thiserror-impl 1.0.44", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.9" +source = "git+https://github.com/mesalock-linux/thiserror-sgx?tag=sgx_1.1.3#c2f806b88616e06aab0af770366a76885d974fdc" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if 1.0.0", + "once_cell 1.18.0", +] + +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "thrift" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b82ca8f46f95b3ce96081fe3dd89160fdea970c254bb72925255d1b62aae692e" +dependencies = [ + "byteorder 1.4.3", + "integer-encoding", + "log 0.4.20", + "ordered-float", + "threadpool", +] + +[[package]] +name = "tikv-jemalloc-ctl" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e37706572f4b151dff7a0146e040804e9c26fe3a3118591112f05cf12a4216c1" +dependencies = [ + "libc", + "paste", + "tikv-jemalloc-sys", +] + +[[package]] +name = "tikv-jemalloc-sys" +version = "0.5.3+5.3.0-patched" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a678df20055b43e57ef8cddde41cdfda9a3c1a060b67f4c5836dfb1d78543ba8" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi 0.3.9", +] + +[[package]] +name = "time" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea9e1b3cf1243ae005d9e74085d4d542f3125458f3a81af210d901dcd7411efd" +dependencies = [ + "itoa 1.0.9", + "serde 1.0.193", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" + +[[package]] +name = "time-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" +dependencies = [ + "time-core", +] + +[[package]] +name = "tiny-bip39" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62cc94d358b5a1e84a5cb9109f559aa3c4d634d2b1b4de3d0fa4adc7c78e2861" +dependencies = [ + "anyhow", + "hmac 0.12.1", + "once_cell 1.18.0", + "pbkdf2 0.11.0", + "rand 0.8.5", + "rustc-hash", + "sha2 0.10.7", + "thiserror 1.0.44", + "unicode-normalization 0.1.22", + "wasm-bindgen", + "zeroize", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde 1.0.193", + "serde_json 1.0.103", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" +dependencies = [ + "autocfg 1.1.0", + "backtrace", + "bytes 1.4.0", + "libc", + "mio 0.8.8", + "num_cpus", + "parking_lot 0.12.1", + "pin-project-lite 0.2.10", + "signal-hook-registry", + "socket2 0.4.9", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" +dependencies = [ + "rustls 0.19.1", + "tokio", + "webpki 0.21.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls 0.20.8", + "tokio", + "webpki 0.22.0", +] + +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core 0.3.28", + "pin-project-lite 0.2.10", + "tokio", + "tokio-util 0.7.8", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54319c93411147bced34cb5609a80e0a8e44c5999c93903a81cd866630ec0bfd" +dependencies = [ + "futures-util 0.3.28", + "log 0.4.20", + "tokio", + "tungstenite 0.18.0", +] + +[[package]] +name = "tokio-util" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" +dependencies = [ + "bytes 1.4.0", + "futures-core 0.3.28", + "futures-io 0.3.28", + "futures-sink 0.3.28", + "log 0.4.20", + "pin-project-lite 0.2.10", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +dependencies = [ + "bytes 1.4.0", + "futures-core 0.3.28", + "futures-io 0.3.28", + "futures-sink 0.3.28", + "pin-project-lite 0.2.10", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde 1.0.193", +] + +[[package]] +name = "toml" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +dependencies = [ + "serde 1.0.193", + "serde_spanned", + "toml_datetime", + "toml_edit 0.19.15", +] + +[[package]] +name = "toml" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +dependencies = [ + "serde 1.0.193", + "serde_spanned", + "toml_datetime", + "toml_edit 0.20.2", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde 1.0.193", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.0.0", + "serde 1.0.193", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.0.0", + "serde 1.0.193", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" +dependencies = [ + "bitflags 1.3.2", + "bytes 1.4.0", + "futures-core 0.3.28", + "futures-util 0.3.28", + "http 0.2.9", + "http-body", + "http-range-header", + "pin-project-lite 0.2.10", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if 1.0.0", + "log 0.4.20", + "pin-project-lite 0.2.10", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "tracing-core" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +dependencies = [ + "once_cell 1.18.0", + "valuable", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "tracing-gum" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" +dependencies = [ + "polkadot-node-jaeger", + "polkadot-primitives", + "tracing", + "tracing-gum-proc-macro", +] + +[[package]] +name = "tracing-gum-proc-macro" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" +dependencies = [ + "expander 2.0.0", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log 0.4.20", + "tracing-core", +] + +[[package]] +name = "tracing-serde" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +dependencies = [ + "serde 1.0.193", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" +dependencies = [ + "ansi_term", + "chrono 0.4.26", + "lazy_static", + "matchers", + "parking_lot 0.11.2", + "regex 1.9.5", + "serde 1.0.193", + "serde_json 1.0.103", + "sharded-slab", + "smallvec 1.11.0", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", + "tracing-serde", +] + +[[package]] +name = "trie-db" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "767abe6ffed88a1889671a102c2861ae742726f52e0a5a425b92c9fbfa7e9c85" +dependencies = [ + "hash-db 0.16.0", + "hashbrown 0.13.2", + "log 0.4.20", + "rustc-hex", + "smallvec 1.11.0", +] + +[[package]] +name = "trie-root" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4ed310ef5ab98f5fa467900ed906cb9232dd5376597e00fd4cba2a449d06c0b" +dependencies = [ + "hash-db 0.16.0", +] + +[[package]] +name = "triehash" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1631b201eb031b563d2e85ca18ec8092508e262a3196ce9bd10a67ec87b9f5c" +dependencies = [ + "hash-db 0.15.2", + "rlp", +] + +[[package]] +name = "trust-dns-proto" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f7f83d1e4a0e4358ac54c5c3681e5d7da5efc5a7a632c90bb6d6669ddd9bc26" +dependencies = [ + "async-trait", + "cfg-if 1.0.0", + "data-encoding", + "enum-as-inner", + "futures-channel 0.3.28", + "futures-io 0.3.28", + "futures-util 0.3.28", + "idna 0.2.3", + "ipnet", + "lazy_static", + "rand 0.8.5", + "smallvec 1.11.0", + "socket2 0.4.9", + "thiserror 1.0.44", + "tinyvec", + "tokio", + "tracing", + "url 2.4.0", +] + +[[package]] +name = "trust-dns-resolver" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aff21aa4dcefb0a1afbfac26deb0adc93888c7d295fb63ab273ef276ba2b7cfe" +dependencies = [ + "cfg-if 1.0.0", + "futures-util 0.3.28", + "ipconfig", + "lazy_static", + "lru-cache", + "parking_lot 0.12.1", + "resolv-conf", + "smallvec 1.11.0", + "thiserror 1.0.44", + "tokio", + "tracing", + "trust-dns-proto", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "tt-call" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f195fd851901624eee5a58c4bb2b4f06399148fcd0ed336e6f1cb60a9881df" + +[[package]] +name = "tungstenite" +version = "0.14.0" +source = "git+https://github.com/integritee-network/tungstenite-rs-sgx?branch=sgx-experimental#c87a2c08ea00897bb8b127ca0a5c30c3671492b0" +dependencies = [ + "base64 0.13.0 (git+https://github.com/mesalock-linux/rust-base64-sgx?tag=sgx_1.1.3)", + "byteorder 1.3.4", + "bytes 1.0.1", + "http 0.2.1", + "httparse 1.4.1", + "log 0.4.14 (git+https://github.com/mesalock-linux/log-sgx?tag=sgx_1.1.3)", + "rand 0.7.3 (git+https://github.com/mesalock-linux/rand-sgx?tag=sgx_1.1.3)", + "rustls 0.19.0 (git+https://github.com/mesalock-linux/rustls?tag=sgx_1.1.3)", + "sgx_tstd", + "sha1 0.6.0", + "thiserror 1.0.9", + "url 2.1.1", + "utf-8 0.7.4", + "webpki 0.21.4 (git+https://github.com/mesalock-linux/webpki?branch=mesalock_sgx)", + "webpki-roots 0.21.0 (git+https://github.com/mesalock-linux/webpki-roots?tag=sgx_1.1.3)", +] + +[[package]] +name = "tungstenite" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "983d40747bce878d2fb67d910dcb8bd3eca2b2358540c3cc1b98c027407a3ae3" +dependencies = [ + "base64 0.13.1", + "byteorder 1.4.3", + "bytes 1.4.0", + "http 0.2.9", + "httparse 1.8.0", + "log 0.4.20", + "rand 0.8.5", + "rustls 0.19.1", + "sha-1 0.9.8", + "thiserror 1.0.44", + "url 2.4.0", + "utf-8 0.7.6", + "webpki 0.21.4 (registry+https://github.com/rust-lang/crates.io-index)", + "webpki-roots 0.21.1", +] + +[[package]] +name = "tungstenite" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ee6ab729cd4cf0fd55218530c4522ed30b7b6081752839b68fcec8d0960788" +dependencies = [ + "base64 0.13.1", + "byteorder 1.4.3", + "bytes 1.4.0", + "http 0.2.9", + "httparse 1.8.0", + "log 0.4.20", + "native-tls", + "rand 0.8.5", + "sha1 0.10.5", + "thiserror 1.0.44", + "url 2.4.0", + "utf-8 0.7.6", +] + +[[package]] +name = "turn" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4712ee30d123ec7ae26d1e1b218395a16c87cdbaf4b3925d170d684af62ea5e8" +dependencies = [ + "async-trait", + "base64 0.13.1", + "futures 0.3.28", + "log 0.4.20", + "md-5", + "rand 0.8.5", + "ring 0.16.20", + "stun", + "thiserror 1.0.44", + "tokio", + "webrtc-util", +] + +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if 1.0.0", + "digest 0.10.7", + "rand 0.8.5", + "static_assertions", +] + +[[package]] +name = "typed-builder" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a46ee5bd706ff79131be9c94e7edcb82b703c487766a114434e5790361cf08c5" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "typenum" +version = "1.16.0" +source = "git+https://github.com/encointer/typenum?tag=v1.16.0#4c8dddaa8bdd13130149e43b4085ad14e960617f" +dependencies = [ + "parity-scale-codec", + "scale-info", +] + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder 1.4.3", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicase" +version = "2.6.0" +source = "git+https://github.com/mesalock-linux/unicase-sgx#0b0519348572927118af47af3da4da9ffdca8ec6" +dependencies = [ + "sgx_tstd", + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "git+https://github.com/mesalock-linux/unicode-bidi-sgx#eb10728a635a046e75747849fbc680cbbb7832c7" +dependencies = [ + "matches 0.1.8", + "sgx_tstd", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" + +[[package]] +name = "unicode-normalization" +version = "0.1.12" +source = "git+https://github.com/mesalock-linux/unicode-normalization-sgx#c1b030611969f87d75782c1df77975167cbbd509" +dependencies = [ + "smallvec 1.6.1", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "universal-hash" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +dependencies = [ + "generic-array 0.14.7", + "subtle", +] + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "unsigned-varint" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fdeedbf205afadfe39ae559b75c3240f24e257d0ca27e85f85cb82aa19ac35" + +[[package]] +name = "unsigned-varint" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d86a8dc7f45e4c1b0d30e43038c38f274e77af056aa5f74b93c2cf9eb3c1c836" +dependencies = [ + "asynchronous-codec", + "bytes 1.4.0", + "futures-io 0.3.28", + "futures-util 0.3.28", +] + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.1.1" +source = "git+https://github.com/mesalock-linux/rust-url-sgx?tag=sgx_1.1.3#23832f3191456c2d4a0faab10952e1747be58ca8" +dependencies = [ + "idna 0.2.0", + "matches 0.1.8", + "percent-encoding 2.1.0", + "sgx_tstd", +] + +[[package]] +name = "url" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +dependencies = [ + "form_urlencoded", + "idna 0.4.0", + "percent-encoding 2.3.0", +] + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "utf-8" +version = "0.7.4" +source = "git+https://github.com/integritee-network/rust-utf8-sgx?branch=sgx-experimental#b026700da83a2f00f0e9f36f813ef28e447a719e" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "uuid" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa2982af2eec27de306107c027578ff7f423d65f7250e40ce0fea8f45248b81" +dependencies = [ + "getrandom 0.2.10", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "waitgroup" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1f50000a783467e6c0200f9d10642f4bc424e39efc1b770203e88b488f79292" +dependencies = [ + "atomic-waker", +] + +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + +[[package]] +name = "walkdir" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "warp" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba431ef570df1287f7f8b07e376491ad54f84d26ac473489427231e1718e1f69" +dependencies = [ + "bytes 1.4.0", + "futures-channel 0.3.28", + "futures-util 0.3.28", + "headers", + "http 0.2.9", + "hyper", + "log 0.4.20", + "mime", + "mime_guess", + "multer", + "percent-encoding 2.3.0", + "pin-project", + "rustls-pemfile", + "scoped-tls", + "serde 1.0.193", + "serde_json 1.0.103", + "serde_urlencoded", + "tokio", + "tokio-stream", + "tokio-tungstenite", + "tokio-util 0.7.8", + "tower-service", + "tracing", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log 0.4.20", + "once_cell 1.18.0", + "proc-macro2", + "quote", + "syn 2.0.32", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "wasm-instrument" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa1dafb3e60065305741e83db35c6c2584bb3725b692b5b66148a38d72ace6cd" +dependencies = [ + "parity-wasm", +] + +[[package]] +name = "wasm-opt" +version = "0.111.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a303793cbc01fb96551badfc7367db6007396bba6bac97936b3c8b6f7fdb41" +dependencies = [ + "anyhow", + "libc", + "strum 0.24.1", + "strum_macros 0.24.3", + "tempfile", + "thiserror 1.0.44", + "wasm-opt-cxx-sys", + "wasm-opt-sys", +] + +[[package]] +name = "wasm-opt-cxx-sys" +version = "0.111.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c9deb56f8a9f2ec177b3bd642a8205621835944ed5da55f2388ef216aca5a4" +dependencies = [ + "anyhow", + "cxx", + "cxx-build", + "wasm-opt-sys", +] + +[[package]] +name = "wasm-opt-sys" +version = "0.111.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4432e28b542738a9776cedf92e8a99d8991c7b4667ee2c7ccddfb479dd2856a7" +dependencies = [ + "anyhow", + "cc", + "cxx", + "cxx-build", + "regex 1.9.5", +] + +[[package]] +name = "wasm-timer" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" +dependencies = [ + "futures 0.3.28", + "js-sys", + "parking_lot 0.11.2", + "pin-utils", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wasmi" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06c326c93fbf86419608361a2c925a31754cf109da1b8b55737070b4d6669422" +dependencies = [ + "parity-wasm", + "wasmi-validation", + "wasmi_core", +] + +[[package]] +name = "wasmi-validation" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ff416ad1ff0c42e5a926ed5d5fab74c0f098749aa0ad8b2a34b982ce0e867b" +dependencies = [ + "parity-wasm", +] + +[[package]] +name = "wasmi_core" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d20cb3c59b788653d99541c646c561c9dd26506f25c0cebfe810659c54c6d7" +dependencies = [ + "downcast-rs", + "libm 0.2.7", + "memory_units", + "num-rational 0.4.1", + "num-traits 0.2.16", + "region", +] + +[[package]] +name = "wasmparser" +version = "0.100.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64b20236ab624147dfbb62cf12a19aaf66af0e41b8398838b66e997d07d269d4" +dependencies = [ + "indexmap 1.9.3", + "url 2.4.0", +] + +[[package]] +name = "wasmtime" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a222f5fa1e14b2cefc286f1b68494d7a965f4bf57ec04c59bb62673d639af6" +dependencies = [ + "anyhow", + "bincode", + "cfg-if 1.0.0", + "indexmap 1.9.3", + "libc", + "log 0.4.20", + "object 0.29.0", + "once_cell 1.18.0", + "paste", + "psm", + "rayon", + "serde 1.0.193", + "target-lexicon", + "wasmparser", + "wasmtime-cache", + "wasmtime-cranelift", + "wasmtime-environ", + "wasmtime-jit", + "wasmtime-runtime", + "windows-sys 0.42.0", +] + +[[package]] +name = "wasmtime-asm-macros" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4407a7246e7d2f3d8fb1cf0c72fda8dbafdb6dd34d555ae8bea0e5ae031089cc" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "wasmtime-cache" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ceb3adf61d654be0be67fffdce42447b0880481348785be5fe40b5dd7663a4c" +dependencies = [ + "anyhow", + "base64 0.13.1", + "bincode", + "directories-next", + "file-per-thread-logger", + "log 0.4.20", + "rustix 0.36.15", + "serde 1.0.193", + "sha2 0.10.7", + "toml 0.5.11", + "windows-sys 0.42.0", + "zstd 0.11.2+zstd.1.5.2", +] + +[[package]] +name = "wasmtime-cranelift" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c366bb8647e01fd08cb5589976284b00abfded5529b33d7e7f3f086c68304a4" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "cranelift-native", + "cranelift-wasm", + "gimli 0.26.2", + "log 0.4.20", + "object 0.29.0", + "target-lexicon", + "thiserror 1.0.44", + "wasmparser", + "wasmtime-environ", +] + +[[package]] +name = "wasmtime-environ" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b8b50962eae38ee319f7b24900b7cf371f03eebdc17400c1dc8575fc10c9a7" +dependencies = [ + "anyhow", + "cranelift-entity", + "gimli 0.26.2", + "indexmap 1.9.3", + "log 0.4.20", + "object 0.29.0", + "serde 1.0.193", + "target-lexicon", + "thiserror 1.0.44", + "wasmparser", + "wasmtime-types", +] + +[[package]] +name = "wasmtime-jit" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffaed4f9a234ba5225d8e64eac7b4a5d13b994aeb37353cde2cbeb3febda9eaa" +dependencies = [ + "addr2line 0.17.0", + "anyhow", + "bincode", + "cfg-if 1.0.0", + "cpp_demangle", + "gimli 0.26.2", + "log 0.4.20", + "object 0.29.0", + "rustc-demangle", + "serde 1.0.193", + "target-lexicon", + "wasmtime-environ", + "wasmtime-jit-debug", + "wasmtime-jit-icache-coherence", + "wasmtime-runtime", + "windows-sys 0.42.0", +] + +[[package]] +name = "wasmtime-jit-debug" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed41cbcbf74ce3ff6f1d07d1b707888166dc408d1a880f651268f4f7c9194b2" +dependencies = [ + "object 0.29.0", + "once_cell 1.18.0", + "rustix 0.36.15", +] + +[[package]] +name = "wasmtime-jit-icache-coherence" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a28ae1e648461bfdbb79db3efdaee1bca5b940872e4175390f465593a2e54c" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "windows-sys 0.42.0", +] + +[[package]] +name = "wasmtime-runtime" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e704b126e4252788ccfc3526d4d4511d4b23c521bf123e447ac726c14545217b" +dependencies = [ + "anyhow", + "cc", + "cfg-if 1.0.0", + "indexmap 1.9.3", + "libc", + "log 0.4.20", + "mach", + "memfd", + "memoffset 0.6.5", + "paste", + "rand 0.8.5", + "rustix 0.36.15", + "wasmtime-asm-macros", + "wasmtime-environ", + "wasmtime-jit-debug", + "windows-sys 0.42.0", +] + +[[package]] +name = "wasmtime-types" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83e5572c5727c1ee7e8f28717aaa8400e4d22dcbd714ea5457d85b5005206568" +dependencies = [ + "cranelift-entity", + "serde 1.0.193", + "thiserror 1.0.44", + "wasmparser", +] + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +dependencies = [ + "ring 0.16.20", + "untrusted 0.7.1", +] + +[[package]] +name = "webpki" +version = "0.21.4" +source = "git+https://github.com/mesalock-linux/webpki?branch=mesalock_sgx#8dbe6fbeefadf05582ae47c7fa818b04db49c61e" +dependencies = [ + "ring 0.16.19", + "sgx_tstd", + "untrusted 0.7.1", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring 0.16.20", + "untrusted 0.7.1", +] + +[[package]] +name = "webpki-roots" +version = "0.21.0" +source = "git+https://github.com/mesalock-linux/webpki-roots?tag=sgx_1.1.3#6ff3be547ac13ccd46ae55605ad6506ce30688ef" +dependencies = [ + "sgx_tstd", + "webpki 0.21.4 (git+https://github.com/mesalock-linux/webpki?branch=mesalock_sgx)", +] + +[[package]] +name = "webpki-roots" +version = "0.21.0" +source = "git+https://github.com/mesalock-linux/webpki-roots?branch=mesalock_sgx#6ff3be547ac13ccd46ae55605ad6506ce30688ef" +dependencies = [ + "sgx_tstd", + "webpki 0.21.4 (git+https://github.com/mesalock-linux/webpki?branch=mesalock_sgx)", +] + +[[package]] +name = "webpki-roots" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" +dependencies = [ + "webpki 0.21.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "webpki-roots" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +dependencies = [ + "webpki 0.22.0", +] + +[[package]] +name = "webrtc" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3bc9049bdb2cea52f5fd4f6f728184225bdb867ed0dc2410eab6df5bdd67bb" +dependencies = [ + "arc-swap", + "async-trait", + "bytes 1.4.0", + "hex", + "interceptor", + "lazy_static", + "log 0.4.20", + "rand 0.8.5", + "rcgen 0.9.3", + "regex 1.9.5", + "ring 0.16.20", + "rtcp", + "rtp", + "rustls 0.19.1", + "sdp", + "serde 1.0.193", + "serde_json 1.0.103", + "sha2 0.10.7", + "stun", + "thiserror 1.0.44", + "time 0.3.22", + "tokio", + "turn", + "url 2.4.0", + "waitgroup", + "webrtc-data", + "webrtc-dtls", + "webrtc-ice", + "webrtc-mdns", + "webrtc-media", + "webrtc-sctp", + "webrtc-srtp", + "webrtc-util", +] + +[[package]] +name = "webrtc-data" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef36a4d12baa6e842582fe9ec16a57184ba35e1a09308307b67d43ec8883100" +dependencies = [ + "bytes 1.4.0", + "derive_builder", + "log 0.4.20", + "thiserror 1.0.44", + "tokio", + "webrtc-sctp", + "webrtc-util", +] + +[[package]] +name = "webrtc-dtls" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942be5bd85f072c3128396f6e5a9bfb93ca8c1939ded735d177b7bcba9a13d05" +dependencies = [ + "aes 0.6.0", + "aes-gcm 0.10.2", + "async-trait", + "bincode", + "block-modes", + "byteorder 1.4.3", + "ccm", + "curve25519-dalek 3.2.0", + "der-parser 8.2.0", + "elliptic-curve 0.12.3", + "hkdf", + "hmac 0.12.1", + "log 0.4.20", + "oid-registry 0.6.1", + "p256", + "p384", + "rand 0.8.5", + "rand_core 0.6.4", + "rcgen 0.9.3", + "ring 0.16.20", + "rustls 0.19.1", + "sec1 0.3.0", + "serde 1.0.193", + "sha1 0.10.5", + "sha2 0.10.7", + "signature 1.6.4", + "subtle", + "thiserror 1.0.44", + "tokio", + "webpki 0.21.4 (registry+https://github.com/rust-lang/crates.io-index)", + "webrtc-util", + "x25519-dalek 2.0.0-pre.1", + "x509-parser 0.13.2", +] + +[[package]] +name = "webrtc-ice" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465a03cc11e9a7d7b4f9f99870558fe37a102b65b93f8045392fef7c67b39e80" +dependencies = [ + "arc-swap", + "async-trait", + "crc", + "log 0.4.20", + "rand 0.8.5", + "serde 1.0.193", + "serde_json 1.0.103", + "stun", + "thiserror 1.0.44", + "tokio", + "turn", + "url 2.4.0", + "uuid", + "waitgroup", + "webrtc-mdns", + "webrtc-util", +] + +[[package]] +name = "webrtc-mdns" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f08dfd7a6e3987e255c4dbe710dde5d94d0f0574f8a21afa95d171376c143106" +dependencies = [ + "log 0.4.20", + "socket2 0.4.9", + "thiserror 1.0.44", + "tokio", + "webrtc-util", +] + +[[package]] +name = "webrtc-media" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f72e1650a8ae006017d1a5280efb49e2610c19ccc3c0905b03b648aee9554991" +dependencies = [ + "byteorder 1.4.3", + "bytes 1.4.0", + "rand 0.8.5", + "rtp", + "thiserror 1.0.44", +] + +[[package]] +name = "webrtc-sctp" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d47adcd9427eb3ede33d5a7f3424038f63c965491beafcc20bc650a2f6679c0" +dependencies = [ + "arc-swap", + "async-trait", + "bytes 1.4.0", + "crc", + "log 0.4.20", + "rand 0.8.5", + "thiserror 1.0.44", + "tokio", + "webrtc-util", +] + +[[package]] +name = "webrtc-srtp" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6183edc4c1c6c0175f8812eefdce84dfa0aea9c3ece71c2bf6ddd3c964de3da5" +dependencies = [ + "aead 0.4.3", + "aes 0.7.5", + "aes-gcm 0.9.4", + "async-trait", + "byteorder 1.4.3", + "bytes 1.4.0", + "ctr 0.8.0", + "hmac 0.11.0", + "log 0.4.20", + "rtcp", + "rtp", + "sha-1 0.9.8", + "subtle", + "thiserror 1.0.44", + "tokio", + "webrtc-util", +] + +[[package]] +name = "webrtc-util" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f1db1727772c05cf7a2cfece52c3aca8045ca1e176cd517d323489aa3c6d87" +dependencies = [ + "async-trait", + "bitflags 1.3.2", + "bytes 1.4.0", + "cc", + "ipnet", + "lazy_static", + "libc", + "log 0.4.20", + "nix", + "rand 0.8.5", + "thiserror 1.0.44", + "tokio", + "winapi 0.3.9", +] + +[[package]] +name = "which" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +dependencies = [ + "either", + "libc", + "once_cell 1.18.0", +] + +[[package]] +name = "wide" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa469ffa65ef7e0ba0f164183697b89b854253fd31aeb92358b7b6155177d62f" +dependencies = [ + "bytemuck", + "safe_arch", +] + +[[package]] +name = "widestring" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + +[[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-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + +[[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-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45296b64204227616fdbf2614cefa4c236b98ee64dfaaaa435207ed99fe7829f" +dependencies = [ + "windows_aarch64_msvc 0.34.0", + "windows_i686_gnu 0.34.0", + "windows_i686_msvc 0.34.0", + "windows_x86_64_gnu 0.34.0", + "windows_x86_64_msvc 0.34.0", +] + +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.1", +] + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.1", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "winnow" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25b5872fa2e10bd067ae946f927e726d7d603eaeb6e02fa6a350e0722d2b8c11" +dependencies = [ + "memchr 2.6.3", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if 1.0.0", + "windows-sys 0.48.0", +] + +[[package]] +name = "ws" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25fe90c75f236a0a00247d5900226aea4f2d7b05ccc34da9e7a8880ff59b5848" +dependencies = [ + "byteorder 1.4.3", + "bytes 0.4.12", + "httparse 1.8.0", + "log 0.4.20", + "mio 0.6.23", + "mio-extras 2.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sha-1 0.8.2", + "slab 0.4.8", + "url 2.4.0", +] + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "x25519-dalek" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f" +dependencies = [ + "curve25519-dalek 3.2.0", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize", +] + +[[package]] +name = "x25519-dalek" +version = "2.0.0-pre.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5da623d8af10a62342bcbbb230e33e58a63255a58012f8653c578e54bab48df" +dependencies = [ + "curve25519-dalek 3.2.0", + "rand_core 0.6.4", + "zeroize", +] + +[[package]] +name = "x509-cert" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d224a125dec5adda27d0346b9cae9794830279c4f9c27e4ab0b6c408d54012" +dependencies = [ + "const-oid", + "der 0.6.1", + "flagset", + "spki 0.6.0", +] + +[[package]] +name = "x509-parser" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb9bace5b5589ffead1afb76e43e34cff39cd0f3ce7e170ae0c29e53b88eb1c" +dependencies = [ + "asn1-rs 0.3.1", + "base64 0.13.1", + "data-encoding", + "der-parser 7.0.0", + "lazy_static", + "nom", + "oid-registry 0.4.0", + "ring 0.16.20", + "rusticata-macros", + "thiserror 1.0.44", + "time 0.3.22", +] + +[[package]] +name = "x509-parser" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8" +dependencies = [ + "asn1-rs 0.5.2", + "base64 0.13.1", + "data-encoding", + "der-parser 8.2.0", + "lazy_static", + "nom", + "oid-registry 0.6.1", + "rusticata-macros", + "thiserror 1.0.44", + "time 0.3.22", +] + +[[package]] +name = "xcm" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" +dependencies = [ + "bounded-collections", + "derivative", + "impl-trait-for-tuples", + "log 0.4.20", + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "sp-weights", + "xcm-procedural", +] + +[[package]] +name = "xcm-builder" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" +dependencies = [ + "frame-support", + "frame-system", + "impl-trait-for-tuples", + "log 0.4.20", + "pallet-transaction-payment", + "parity-scale-codec", + "polkadot-parachain", + "scale-info", + "sp-arithmetic", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", + "xcm", + "xcm-executor", +] + +[[package]] +name = "xcm-executor" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" +dependencies = [ + "environmental 1.1.4", + "frame-support", + "impl-trait-for-tuples", + "log 0.4.20", + "parity-scale-codec", + "sp-arithmetic", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", + "sp-weights", + "xcm", +] + +[[package]] +name = "xcm-procedural" +version = "0.9.42" +source = "git+https://github.com/paritytech/polkadot?branch=release-v0.9.42#6f991987c0b4cbbd7d4badc9ef08d83da5fefbfd" +dependencies = [ + "Inflector", + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "xous" +version = "0.9.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a9f0a696320940ab2652fa1d20c98dc59eb7ba4591eeb91a3b8e40bc9255a1" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "xous-api-log" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03e07c190c743d6d9e076f715333e94c48de41b99078343d174c707803df28c7" +dependencies = [ + "log 0.4.20", + "num-derive", + "num-traits 0.2.16", + "xous", + "xous-ipc", +] + +[[package]] +name = "xous-api-names" +version = "0.9.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32d8361077e67966d25922056284d17d042cbb1c96a7ebc2584eb8181427cbb0" +dependencies = [ + "log 0.4.20", + "num-derive", + "num-traits 0.2.16", + "rkyv", + "xous", + "xous-api-log", + "xous-ipc", +] + +[[package]] +name = "xous-ipc" +version = "0.9.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee1d318dacbd6242e4e2291dee7c4532249e5a0845de05d264c20fc871a0a1a" +dependencies = [ + "bitflags 1.3.2", + "rkyv", + "xous", +] + +[[package]] +name = "yaml-rust" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e66366e18dc58b46801afbf2ca7661a9f59cc8c5962c29892b6039b4f86fa992" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map 0.5.6", +] + +[[package]] +name = "yamux" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d9ba232399af1783a58d8eb26f6b5006fbefe2dc9ef36bd283324792d03ea5" +dependencies = [ + "futures 0.3.28", + "log 0.4.20", + "nohash-hasher", + "parking_lot 0.12.1", + "rand 0.8.5", + "static_assertions", +] + +[[package]] +name = "yap" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc77f52dc9e9b10d55d3f4462c3b7fc393c4f17975d641542833ab2d3bc26ef" + +[[package]] +name = "yasna" +version = "0.3.1" +source = "git+https://github.com/mesalock-linux/yasna.rs-sgx?rev=sgx_1.1.3#a1f50714cd3eb29608ecf7888cacedc173edfdb2" +dependencies = [ + "bit-vec", + "chrono 0.4.11", + "num-bigint 0.2.5", + "sgx_tstd", +] + +[[package]] +name = "yasna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e262a29d0e61ccf2b6190d7050d4b237535fc76ce4c1210d9caa316f71dffa75" +dependencies = [ + "bit-vec", + "chrono 0.4.26", + "num-bigint 0.4.3", +] + +[[package]] +name = "yasna" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" +dependencies = [ + "time 0.3.22", +] + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.32", +] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe 5.0.2+zstd.1.5.2", +] + +[[package]] +name = "zstd" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c" +dependencies = [ + "zstd-safe 6.0.6", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-safe" +version = "6.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.8+zstd.1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" +dependencies = [ + "cc", + "libc", + "pkg-config", +] diff --git a/bitacross-worker/Cargo.toml b/bitacross-worker/Cargo.toml new file mode 100644 index 0000000000..2ad3682233 --- /dev/null +++ b/bitacross-worker/Cargo.toml @@ -0,0 +1,112 @@ +[workspace] + +members = [ + "app-libs/oracle", + "app-libs/parentchain-interface", + "app-libs/sgx-runtime", + "app-libs/stf", + "cli", + "core/direct-rpc-client", + "core/direct-rpc-server", + "core/peer-top-broadcaster", + "core/offchain-worker-executor", + "core/parentchain/block-import-dispatcher", + "core/parentchain/block-importer", + "core/parentchain/indirect-calls-executor", + "core/parentchain/light-client", + "core/parentchain/parentchain-crate", + "core/rest-client", + "core/rpc-client", + "core/rpc-server", + "core/tls-websocket-server", + "core-primitives/attestation-handler", + "core-primitives/import-queue", + "core-primitives/component-container", + "core-primitives/enclave-api", + "core-primitives/enclave-api/ffi", + "core-primitives/enclave-metrics", + "core-primitives/extrinsics-factory", + "core-primitives/hashing", + "core-primitives/networking-utils", + "core-primitives/node-api", + "core-primitives/node-api/api-client-extensions", + "core-primitives/node-api/api-client-types", + "core-primitives/node-api/factory", + "core-primitives/node-api/metadata", + "core-primitives/node-api/metadata-provider", + "core-primitives/nonce-cache", + "core-primitives/ocall-api", + "core-primitives/primitives-cache", + "core-primitives/rpc", + "core-primitives/settings", + "core-primitives/sgx/crypto", + "core-primitives/sgx/io", + "core-primitives/sgx-runtime-primitives", + "core-primitives/stf-executor", + "core-primitives/stf-interface", + "core-primitives/stf-primitives", + "core-primitives/stf-state-handler", + "core-primitives/stf-state-observer", + "core-primitives/storage", + "core-primitives/substrate-sgx/environmental", + "core-primitives/substrate-sgx/externalities", + "core-primitives/substrate-sgx/sp-io", + "core-primitives/teerex-storage", + "core-primitives/test", + "core-primitives/time-utils", + "core-primitives/top-pool", + "core-primitives/top-pool-author", + "core-primitives/types", + "core-primitives/utils", + "service", + "sidechain/block-composer", + "sidechain/block-verification", + "sidechain/consensus/aura", + "sidechain/consensus/common", + "sidechain/consensus/slots", + "sidechain/fork-tree", + "sidechain/peer-fetch", + "sidechain/primitives", + "sidechain/rpc-handler", + "sidechain/sidechain-crate", + "sidechain/state", + "sidechain/validateer-fetch", + "litentry/primitives", + "litentry/macros", +] + +[patch."https://github.com/apache/teaclave-sgx-sdk.git"] +sgx_alloc = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } +sgx_crypto_helper = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } +sgx_libc = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } +sgx_rand = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } +sgx_tcrypto = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } +sgx_trts = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } +sgx_tstd = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } +sgx_types = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } +sgx_ucrypto = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } +sgx_urts = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } + +[patch.crates-io] +ring = { git = "https://github.com/betrusted-io/ring-xous", branch = "0.16.20-cleanup" } + +#[patch."https://github.com/integritee-network/integritee-node"] +#my-node-runtime = { package = "integritee-node-runtime", git = "https://github.com/integritee-network//integritee-node", branch = "ab/integrate-pallet-teerex-refactoring" } + +#[patch."https://github.com/scs/substrate-api-client"] +#substrate-api-client = { path = "../../scs/substrate-api-client" } +#substrate-client-keystore = { path = "../../scs/substrate-api-client/client-keystore" } + +#[patch."https://github.com/integritee-network/pallets.git"] +#pallet-claims = { git = "https://github.com/integritee-network//pallets", branch = "ab/shard-config-upgradability-2" } +#pallet-enclave-bridge = { git = "https://github.com/integritee-network//pallets", branch = "ab/shard-config-upgradability-2" } +#pallet-teerex = { git = "https://github.com/integritee-network//pallets", branch = "ab/shard-config-upgradability-2" } +#pallet-sidechain = { git = "https://github.com/integritee-network//pallets", branch = "ab/shard-config-upgradability-2" } +#sgx-verify = { git = "https://github.com/integritee-network//pallets", branch = "ab/shard-config-upgradability-2" } +#pallet-teeracle = { git = "https://github.com/integritee-network//pallets", branch = "ab/shard-config-upgradability-2" } +#test-utils = { git = "https://github.com/integritee-network//pallets", branch = "ab/shard-config-upgradability-2" } +#claims-primitives = { git = "https://github.com/integritee-network//pallets", branch = "ab/shard-config-upgradability-2" } +#enclave-bridge-primitives = { git = "https://github.com/integritee-network//pallets", branch = "ab/shard-config-upgradability-2" } +#teerex-primitives = { git = "https://github.com/integritee-network//pallets", branch = "ab/shard-config-upgradability-2" } +#teeracle-primitives = { git = "https://github.com/integritee-network//pallets", branch = "ab/shard-config-upgradability-2" } +#common-primitives = { git = "https://github.com/integritee-network//pallets", branch = "ab/shard-config-upgradability-2" } diff --git a/bitacross-worker/DESIGN.md b/bitacross-worker/DESIGN.md new file mode 100644 index 0000000000..8d579d96b7 --- /dev/null +++ b/bitacross-worker/DESIGN.md @@ -0,0 +1,72 @@ +# sidechain startup internal view +```mermaid +sequenceDiagram + participant integritee_network + participant service + participant slotworker + participant parentsync + participant enclave + participant enclave_rpc + participant provisioningserver + participant isinitializedserver + participant metrics + service ->> enclave: EnclaveBase.get_mrenclave + service ->> provisioningserver: spawn (`--mu-ra-port` | 3443) + activate provisioningserver + service ->> enclave: get_ecc_signing_pubkey + service ->> isinitializedserver: spawn (`--untrusted-http-port | 4545) + activate isinitializedserver + service ->> metrics: spawn (`--metrics-port`| 8787) + activate metrics + service ->> enclave_rpc: spawn (`--trusted-worker-port`| 2000) + activate enclave_rpc + + service ->> enclave: generate_dcap_ra_extrinsic + service ->> integritee_network: send register_sgx_enclave extrinsic + service ->> integritee_network: get ShardStatus + service ->> isinitializedserver: registered_on_parentchain +# schedule teeracle re-registration and updates + loop while blocks to sync + service ->> integritee_network: get_block + service ->> enclave: sync_parentchain(blocks, events, proofs) + end + service ->> enclave: init_enclave_sidechain_components + service ->> slotworker: spawn + loop forever + slotworker ->> enclave: execute_trusted_calls + activate enclave + enclave ->> enclave: propose_sidechain_block + enclave ->> integritee_network: send_extrinsics + deactivate enclave + end + service ->> parentsync: spawn + loop forever + parentsync ->> integritee_network: subscribe new headers + parentsync ->> enclave: sync_parentchain + end + service ->> service: poll worker_for_shard + service ->> isinitializedserver: worker_for_shard_registered + + deactivate enclave_rpc + deactivate metrics + deactivate isinitializedserver + deactivate provisioningserver +``` + +# sidechain lifetime external view + +```mermaid +sequenceDiagram + participant integritee_network + participant validateer_1 + participant validateer_2 + actor alice + + validateer_1 ->> integritee_network: register_sgx_enclave() + + validateer_2 ->> integritee_network: register_sgx_enclave() + + validateer_2 ->> validateer_1: sidechain_fetchBlocksFromPeer() + + validateer_1 ->> validateer_2: sidechain_importBlock() +``` diff --git a/bitacross-worker/Dockerfile b/bitacross-worker/Dockerfile new file mode 100644 index 0000000000..95bd8a9d60 --- /dev/null +++ b/bitacross-worker/Dockerfile @@ -0,0 +1,23 @@ +FROM integritee/integritee-dev:0.2.2 +LABEL maintainer="zoltan@integritee.network" + +# By default we warp the service +ARG BINARY_FILE=integritee-service + +COPY bin/enclave.signed.so /usr/local/bin/ +COPY bin/${BINARY_FILE} /usr/local/bin/integritee + +RUN chmod +x /usr/local/bin/integritee + +WORKDIR /usr/local/bin +RUN touch spid.txt key.txt +RUN if [[ "x$BINARY_FILE" != "xintegritee-client" ]] ; then ./integritee init-shard; fi +RUN if [[ "x$BINARY_FILE" != "xintegritee-client" ]] ; then ./integritee shielding-key; fi +RUN if [[ "x$BINARY_FILE" != "xintegritee-client" ]] ; then ./integritee signing-key; fi +RUN if [[ "x$BINARY_FILE" != "xintegritee-client" ]] ; then ./integritee mrenclave > ~/mrenclave.b58; fi + +# checks +RUN ldd /usr/local/bin/integritee && \ + /usr/local/bin/integritee --version + +ENTRYPOINT ["/usr/local/bin/integritee"] diff --git a/bitacross-worker/Jenkinsfile b/bitacross-worker/Jenkinsfile new file mode 100755 index 0000000000..62c9197d68 --- /dev/null +++ b/bitacross-worker/Jenkinsfile @@ -0,0 +1,104 @@ +pipeline { + agent { + docker { + image 'integritee/integritee-dev:0.2.2' + args ''' + -u root + --privileged + ''' + } + } + options { + timeout(time: 2, unit: 'HOURS') + buildDiscarder(logRotator(numToKeepStr: '14')) + } + stages { + stage('Init rust') { + steps { + sh 'cargo --version' + sh 'rustup show' + sh 'env' + } + } + stage('Build') { + steps { + sh 'export SGX_SDK=/opt/intel/sgxsdk' + sh 'make' + } + } + stage('Archive build output') { + steps { + archiveArtifacts artifacts: 'bin/enclave.signed.so, bin/integritee-*', caseSensitive: false, fingerprint: true, onlyIfSuccessful: true + } + } + stage('Test') { + steps { + sh 'cd cli && cargo test 2>&1 | tee ${WORKSPACE}/test_client.log' + sh 'cd service && cargo test 2>&1 | tee ${WORKSPACE}/test_server.log' + sh 'cd enclave-runtime && cargo test 2>&1 | tee ${WORKSPACE}/test_enclave.log' + } + } + stage('Clippy') { + steps { + sh 'cargo clean' + catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') { + sh 'cd cli && cargo clippy 2>&1 | tee ${WORKSPACE}/clippy_client.log' + } + catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') { + sh 'cd worker && cargo clippy 2>&1 | tee ${WORKSPACE}/clippy_worker.log' + } + catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') { + sh 'cd enclave && cargo clippy 2>&1 | tee ${WORKSPACE}/clippy_enclave.log' + } + } + } + stage('Formatter') { + steps { + catchError(buildResult: 'SUCCESS', stageResult: 'SUCCESS') { + sh 'cargo fmt -- --check > ${WORKSPACE}/fmt.log' + } + } + } + stage('Results') { + steps { + recordIssues( + aggregatingResults: true, + enabledForFailure: true, + qualityGates: [[threshold: 1, type: 'TOTAL', unstable: true]], + tools: [ + groovyScript( + parserId:'clippy-warnings', + pattern: 'clippy_*.log', + reportEncoding: 'UTF-8' + ), + groovyScript( + parserId:'clippy-errors', + pattern: 'clippy_*.log', + reportEncoding: 'UTF-8' + ) + ] + ) + catchError(buildResult: 'SUCCESS', stageResult: 'SUCCESS') { + sh './ci/check_fmt_log.sh' + } + } + } + stage('Archive logs') { + steps { + archiveArtifacts artifacts: '*.log' + } + } + } + post { + unsuccessful { + emailext ( + subject: "Jenkins Build '${env.JOB_NAME} [${env.BUILD_NUMBER}]' is ${currentBuild.currentResult}", + body: "${env.JOB_NAME} build ${env.BUILD_NUMBER} is ${currentBuild.currentResult}\n\nMore info at: ${env.BUILD_URL}", + to: "${env.RECIPIENTS_SUBSTRATEE}" + ) + } + always { + cleanWs() + } + } +} diff --git a/bitacross-worker/LICENSE b/bitacross-worker/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/bitacross-worker/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/bitacross-worker/Makefile b/bitacross-worker/Makefile new file mode 100755 index 0000000000..7c65260557 --- /dev/null +++ b/bitacross-worker/Makefile @@ -0,0 +1,287 @@ +# Copyright 2021 Integritee AG and Supercomputing Systems AG +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +######## Update SGX SDK ######## +# use this manually to update sdk +#include UpdateRustSGXSDK.mk + +######## SGX SDK Settings ######## +SGX_SDK ?= /opt/intel/sgxsdk +SGX_MODE ?= HW +SGX_ARCH ?= x64 +SGX_DEBUG ?= 0 +SGX_PRERELEASE ?= 0 +SGX_PRODUCTION ?= 0 + +######## Worker Feature Settings ######## +# Set offchain-worker as default feature mode +WORKER_MODE ?= offchain-worker + +SKIP_WASM_BUILD = 1 +# include the build settings from rust-sgx-sdk +include rust-sgx-sdk/buildenv.mk + +ifeq ($(shell getconf LONG_BIT), 32) + SGX_ARCH := x86 +else ifeq ($(findstring -m32, $(CXXFLAGS)), -m32) + SGX_ARCH := x86 +endif + +ifeq ($(SGX_ARCH), x86) + SGX_COMMON_CFLAGS := -m32 + SGX_LIBRARY_PATH := $(SGX_SDK)/lib + SGX_ENCLAVE_SIGNER := $(SGX_SDK)/bin/x86/sgx_sign + SGX_EDGER8R := $(SGX_SDK)/bin/x86/sgx_edger8r +else + SGX_COMMON_CFLAGS := -m64 + SGX_LIBRARY_PATH := $(SGX_SDK)/lib64 + SGX_ENCLAVE_SIGNER := $(SGX_SDK)/bin/x64/sgx_sign + SGX_EDGER8R := $(SGX_SDK)/bin/x64/sgx_edger8r +endif + +ifeq ($(SGX_DEBUG), 1) +ifeq ($(SGX_PRERELEASE), 1) +$(error Cannot set SGX_DEBUG and SGX_PRERELEASE at the same time!!) +endif +ifeq ($(SGX_PRODUCTION), 1) +$(error Cannot set SGX_DEBUG and SGX_PRODUCTION at the same time!!) +endif +endif + +ifeq ($(SGX_DEBUG), 1) + SGX_COMMON_CFLAGS += -O0 -g -ggdb + OUTPUT_PATH := debug + CARGO_TARGET := +else + SGX_COMMON_CFLAGS += -O2 + OUTPUT_PATH := release + CARGO_TARGET := --release +endif + +SGX_COMMON_CFLAGS += -fstack-protector + +ifeq ($(SGX_PRODUCTION), 1) + SGX_ENCLAVE_MODE = "Production Mode" + SGX_ENCLAVE_CONFIG = "enclave-runtime/Enclave.config.production.xml" + SGX_SIGN_KEY = $(SGX_COMMERCIAL_KEY) + SGX_SIGN_PASSFILE = $(SGX_PASSFILE) + WORKER_FEATURES := --features=production,link-binary,$(WORKER_MODE),$(WORKER_FEATURES),$(ADDITIONAL_FEATURES) +else + SGX_ENCLAVE_MODE = "Development Mode" + SGX_ENCLAVE_CONFIG = "enclave-runtime/Enclave.config.xml" + SGX_SIGN_KEY = "enclave-runtime/Enclave_private.pem" + SGX_SIGN_PASSFILE = "" + WORKER_FEATURES := --features=default,link-binary,$(WORKER_MODE),$(WORKER_FEATURES),$(ADDITIONAL_FEATURES) +endif + +CLIENT_FEATURES = --features=$(WORKER_MODE),$(ADDITIONAL_FEATURES) + +# check if running on Jenkins +ifdef BUILD_ID + CARGO_TARGET += --verbose +endif + +######## CUSTOM settings ######## +CUSTOM_LIBRARY_PATH := ./lib +CUSTOM_BIN_PATH := ./bin +CUSTOM_EDL_PATH := ./rust-sgx-sdk/edl +CUSTOM_COMMON_PATH := ./rust-sgx-sdk/common + +######## EDL settings ######## +Enclave_EDL_Files := enclave-runtime/Enclave_t.c enclave-runtime/Enclave_t.h service/Enclave_u.c service/Enclave_u.h + +######## bitacross-worker settings ######## +SRC_Files := $(shell find . -type f -name '*.rs') $(shell find . -type f -name 'Cargo.toml') +Worker_Rust_Flags := $(CARGO_TARGET) $(WORKER_FEATURES) +Worker_Include_Paths := -I ./service -I./include -I$(SGX_SDK)/include -I$(CUSTOM_EDL_PATH) +Worker_C_Flags := $(SGX_COMMON_CFLAGS) -fPIC -Wno-attributes $(Worker_Include_Paths) + +Worker_Rust_Path := target/$(OUTPUT_PATH) +Worker_Enclave_u_Object :=service/libEnclave_u.a +Worker_Name := bin/app + +######## bitacross-cli settings ######## +Client_Rust_Flags := $(CARGO_TARGET) $(CLIENT_FEATURES) + +Client_Rust_Path := target/$(OUTPUT_PATH) +Client_Path := bin +Client_Binary := bitacross-cli +Client_Name := $(Client_Path)/$(Client_Binary) + +######## Enclave settings ######## +ifneq ($(SGX_MODE), HW) + Trts_Library_Name := sgx_trts_sim + Service_Library_Name := sgx_tservice_sim +else + Trts_Library_Name := sgx_trts + Service_Library_Name := sgx_tservice +endif +Crypto_Library_Name := sgx_tcrypto +KeyExchange_Library_Name := sgx_tkey_exchange +ProtectedFs_Library_Name := sgx_tprotected_fs + +RustEnclave_C_Files := $(wildcard ./enclave-runtime/*.c) +RustEnclave_C_Objects := $(RustEnclave_C_Files:.c=.o) +RustEnclave_Include_Paths := -I$(CUSTOM_COMMON_PATH)/inc -I$(CUSTOM_EDL_PATH) -I$(SGX_SDK)/include -I$(SGX_SDK)/include/tlibc -I$(SGX_SDK)/include/stlport -I$(SGX_SDK)/include/epid -I ./enclave-runtime -I./include + +RustEnclave_Link_Libs := -L$(CUSTOM_LIBRARY_PATH) -lenclave +RustEnclave_Compile_Flags := $(SGX_COMMON_CFLAGS) $(ENCLAVE_CFLAGS) $(RustEnclave_Include_Paths) +RustEnclave_Link_Flags := -Wl,--no-undefined -nostdlib -nodefaultlibs -nostartfiles -L$(SGX_LIBRARY_PATH) \ + -Wl,--whole-archive -l$(Trts_Library_Name) -Wl,--no-whole-archive \ + -Wl,--start-group -lsgx_tstdc -lsgx_tcxx -lsgx_dcap_tvl -l$(Crypto_Library_Name) -l$(Service_Library_Name) -l$(ProtectedFs_Library_Name) $(RustEnclave_Link_Libs) -Wl,--end-group \ + -Wl,--version-script=enclave-runtime/Enclave.lds \ + $(ENCLAVE_LDFLAGS) + +RustEnclave_Name := enclave-runtime/enclave.so +Signed_RustEnclave_Name := bin/enclave.signed.so + +######## Targets ######## +.PHONY: all +all: $(Worker_Name) $(Client_Name) $(Signed_RustEnclave_Name) +service: $(Worker_Name) +client: $(Client_Name) +githooks: .git/hooks/pre-commit + +######## EDL objects ######## +$(Enclave_EDL_Files): $(SGX_EDGER8R) enclave-runtime/Enclave.edl + $(SGX_EDGER8R) --trusted enclave-runtime/Enclave.edl --search-path $(SGX_SDK)/include --search-path $(CUSTOM_EDL_PATH) --trusted-dir enclave-runtime + $(SGX_EDGER8R) --untrusted enclave-runtime/Enclave.edl --search-path $(SGX_SDK)/include --search-path $(CUSTOM_EDL_PATH) --untrusted-dir service + @echo "GEN => $(Enclave_EDL_Files)" + +######## bitacross-worker objects ######## +service/Enclave_u.o: $(Enclave_EDL_Files) + @$(CC) $(Worker_C_Flags) -c service/Enclave_u.c -o $@ + @echo "CC <= $<" + +$(Worker_Enclave_u_Object): service/Enclave_u.o + $(AR) rcsD $@ $^ + cp $(Worker_Enclave_u_Object) ./lib + +$(Worker_Name): $(Worker_Enclave_u_Object) $(SRC_Files) + @echo + @echo "Building the bitacross-worker: $(Worker_Rust_Flags)" + @SGX_SDK=$(SGX_SDK) SGX_MODE=$(SGX_MODE) cargo build -p bitacross-worker $(Worker_Rust_Flags) + @echo "Cargo => $@" + cp $(Worker_Rust_Path)/bitacross-worker ./bin + +######## bitacross-client objects ######## +$(Client_Name): $(SRC_Files) + @echo + @echo "Building the bitacross-cli $(Client_Rust_Flags)" + @cargo build -p bitacross-cli $(Client_Rust_Flags) + @echo "Cargo => $@" + cp $(Client_Rust_Path)/$(Client_Binary) ./bin + +######## Enclave objects ######## +enclave-runtime/Enclave_t.o: $(Enclave_EDL_Files) + @$(CC) $(RustEnclave_Compile_Flags) -c enclave-runtime/Enclave_t.c -o $@ + @echo "CC <= $<" + +$(RustEnclave_Name): enclave enclave-runtime/Enclave_t.o + @echo Compiling $(RustEnclave_Name) + @$(CXX) enclave-runtime/Enclave_t.o -o $@ $(RustEnclave_Link_Flags) + @echo "LINK => $@" + +$(Signed_RustEnclave_Name): $(RustEnclave_Name) + @echo + @echo "Signing the enclave: $(SGX_ENCLAVE_MODE)" + @echo "SGX_ENCLAVE_SIGNER: $(SGX_ENCLAVE_SIGNER)" + @echo "RustEnclave_Name: $(RustEnclave_Name)" + @echo "SGX_ENCLAVE_CONFIG: $(SGX_ENCLAVE_CONFIG)" + @echo "SGX_SIGN_PASSFILE: $(SGX_SIGN_PASSFILE)" + @echo "SGX_SIGN_KEY: $(SGX_SIGN_KEY)" + + +# TODO: figure out if/how to use the passphrase file in PROD +ifeq ($(SGX_PRODUCTION), 1) + $(SGX_ENCLAVE_SIGNER) gendata -enclave $(RustEnclave_Name) -out enclave_sig.dat -config $(SGX_ENCLAVE_CONFIG) + openssl rsa -pubout -in $(SGX_SIGN_KEY) -out intel_sgx.pub + openssl dgst -sha256 -sign $(SGX_SIGN_KEY) -out signature.dat enclave_sig.dat + openssl dgst -sha256 -verify intel_sgx.pub -signature signature.dat enclave_sig.dat + $(SGX_ENCLAVE_SIGNER) catsig -enclave $(RustEnclave_Name) -config $(SGX_ENCLAVE_CONFIG) -out $@ -key intel_sgx.pub -sig signature.dat -unsigned enclave_sig.dat +else + $(SGX_ENCLAVE_SIGNER) sign -key $(SGX_SIGN_KEY) -enclave $(RustEnclave_Name) -out $@ -config $(SGX_ENCLAVE_CONFIG) +endif + @echo "SIGN => $@" + @echo + @echo "Enclave is in $(SGX_ENCLAVE_MODE)" + +.PHONY: enclave +enclave: + @echo + @echo "Building the enclave" + $(MAKE) -C ./enclave-runtime/ + +.git/hooks/pre-commit: .githooks/pre-commit + @echo "Installing git hooks" + cp .githooks/pre-commit .git/hooks + +.PHONY: clean +clean: + @echo "Removing the compiled files" + @rm -f $(Client_Name) $(Worker_Name) $(RustEnclave_Name) $(Signed_RustEnclave_Name) \ + enclave-runtime/*_t.* \ + service/*_u.* \ + lib/*.a \ + bin/*.bin + @echo "cargo clean in enclave directory" + @cd enclave-runtime && cargo clean + @echo "cargo clean in root directory" + @cargo clean + +.PHONY: fmt +fmt: + @echo "Cargo format all ..." + @cargo fmt --all + @cd enclave-runtime && cargo fmt --all + +.PHONY: pin-sgx +pin-sgx: + @echo "Pin sgx dependencies to 594806f827b57e6c4c9a0611fa4cbf2d83aabd2e" + @cd enclave-runtime && cargo update -p sgx_tstd --precise 594806f827b57e6c4c9a0611fa4cbf2d83aabd2e + @cargo update -p sgx_tstd --precise 594806f827b57e6c4c9a0611fa4cbf2d83aabd2e + +mrenclave: + @$(SGX_ENCLAVE_SIGNER) dump -enclave ./bin/enclave.signed.so -dumpfile df.out && ./extract_identity < df.out && rm df.out + +mrsigner: + @$(SGX_ENCLAVE_SIGNER) dump -enclave ./bin/enclave.signed.so -dumpfile df.out && ./extract_identity --mrsigner < df.out && rm df.out + +.PHONY: identity +identity: mrenclave mrsigner + +.PHONY: release-pkg +release-pkg: + @./scripts/litentry/release/generate_release_pkg.sh + +.PHONY: help +help: + @echo "Available targets" + @echo " all - builds all targets (default)" + @echo " service - builds the bitacross-worker" + @echo " client - builds the bitacross-cli" + @echo " githooks - installs the git hooks (copy .githooks/pre-commit to .git/hooks)" + @echo "" + @echo " clean - cleanup" + @echo "" + @echo "Compilation options. Prepend them to the make command. Example: 'SGX_MODE=SW make'" + @echo " SGX_MODE" + @echo " HW (default): Use SGX hardware" + @echo " SW: Simulation mode" + @echo " SGX_DEBUG" + @echo " 0 (default): No debug information, optimization level 2, cargo release build" + @echo " 1: Debug information, optimization level 0, cargo debug build" + @echo " SGX_PRODUCTION" + @echo " 0 (default): Using SGX development environment" + @echo " 1: Using SGX production environment" diff --git a/bitacross-worker/README.md b/bitacross-worker/README.md new file mode 100755 index 0000000000..e2be743ff3 --- /dev/null +++ b/bitacross-worker/README.md @@ -0,0 +1 @@ +# bitacross worker \ No newline at end of file diff --git a/bitacross-worker/UpdateRustSGXSDK.mk b/bitacross-worker/UpdateRustSGXSDK.mk new file mode 100755 index 0000000000..88c95d5dc6 --- /dev/null +++ b/bitacross-worker/UpdateRustSGXSDK.mk @@ -0,0 +1,33 @@ +# helper script to update the files in rust-sgx-sdk to the lastest version + +GIT = git +CP = cp + +REPO = https://github.com/apache/incubator-teaclave-sgx-sdk +SDK_PATH_GIT = rust-sgx-sdk-github +SDK_PATH = rust-sgx-sdk +VERSION_FILE = rust-sgx-sdk/version +LOCAL_VERSION = $(shell cat $(VERSION_FILE)) +COMMAND = git ls-remote $(REPO) HEAD | awk '{ print $$1 }' +REMOTE_VERSION = $(shell $(COMMAND)) +# or specify the exact hash if you need a non-default branch / tag / commit etc. +#REMOTE_VERSION = 9c1bbd52f188f600a212b57c916124245da1b7fd + +# update the SDK files +all: updatesdk + +updatesdk: +# check for already updated version +ifneq ('$(LOCAL_VERSION)','$(REMOTE_VERSION)') + @echo Local version = $(LOCAL_VERSION) + @echo Remote version = $(REMOTE_VERSION) + + @rm -rf $(SDK_PATH_GIT) + @$(GIT) clone $(REPO) $(SDK_PATH_GIT) + @$(GIT) -C $(SDK_PATH_GIT) checkout $(REMOTE_VERSION) + rsync -a $(SDK_PATH_GIT)/edl $(SDK_PATH) + rsync -a $(SDK_PATH_GIT)/common $(SDK_PATH) + rm -rf $(SDK_PATH_GIT) + @echo $(REMOTE_VERSION) > $(VERSION_FILE) + +endif diff --git a/bitacross-worker/app-libs/oracle/Cargo.toml b/bitacross-worker/app-libs/oracle/Cargo.toml new file mode 100644 index 0000000000..eb8fa135e2 --- /dev/null +++ b/bitacross-worker/app-libs/oracle/Cargo.toml @@ -0,0 +1,48 @@ +[package] +name = "ita-oracle" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] + +# std dependencies +thiserror = { version = "1.0.26", optional = true } +url = { version = "2.0.0", optional = true } + +# sgx dependencies +sgx_tstd = { rev = "v1.1.3", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } +thiserror_sgx = { package = "thiserror", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3", optional = true } +url_sgx = { package = "url", git = "https://github.com/mesalock-linux/rust-url-sgx", tag = "sgx_1.1.3", optional = true } + +# no_std dependencies +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +lazy_static = { version = "1.1.0", features = ["spin_no_std"] } +log = { version = "0.4", default-features = false } +serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] } +substrate-fixed = { default-features = false, git = "https://github.com/encointer/substrate-fixed", tag = "v0.5.9" } + +# internal dependencies +itc-rest-client = { path = "../../core/rest-client", default-features = false } +itp-enclave-metrics = { path = "../../core-primitives/enclave-metrics", default-features = false } +itp-ocall-api = { path = "../../core-primitives/ocall-api", default-features = false } + +[features] +default = ["std"] +std = [ + "itc-rest-client/std", + "itp-enclave-metrics/std", + "itp-ocall-api/std", + "log/std", + "serde/std", + "substrate-fixed/std", + "thiserror", + "url", +] +sgx = [ + "itc-rest-client/sgx", + "itp-enclave-metrics/sgx", + "sgx_tstd", + "thiserror_sgx", + "url_sgx", +] diff --git a/bitacross-worker/app-libs/oracle/src/certificates/amazon_root_ca_a.pem b/bitacross-worker/app-libs/oracle/src/certificates/amazon_root_ca_a.pem new file mode 100644 index 0000000000..a6f3e92af5 --- /dev/null +++ b/bitacross-worker/app-libs/oracle/src/certificates/amazon_root_ca_a.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj +ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM +9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw +IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 +VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L +93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm +jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA +A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI +U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs +N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv +o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU +5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy +rqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE----- diff --git a/bitacross-worker/app-libs/oracle/src/certificates/baltimore_cyber_trust_root_v3.pem b/bitacross-worker/app-libs/oracle/src/certificates/baltimore_cyber_trust_root_v3.pem new file mode 100644 index 0000000000..519028c63b --- /dev/null +++ b/bitacross-worker/app-libs/oracle/src/certificates/baltimore_cyber_trust_root_v3.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX +DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y +ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy +VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr +mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr +IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK +mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu +XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy +dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye +jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 +BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 +DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 +9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx +jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 +Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz +ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS +R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- diff --git a/bitacross-worker/app-libs/oracle/src/certificates/lets_encrypt_root_cert.pem b/bitacross-worker/app-libs/oracle/src/certificates/lets_encrypt_root_cert.pem new file mode 100644 index 0000000000..57d4a3766c --- /dev/null +++ b/bitacross-worker/app-libs/oracle/src/certificates/lets_encrypt_root_cert.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/bitacross-worker/app-libs/oracle/src/certificates/open_meteo_root.pem b/bitacross-worker/app-libs/oracle/src/certificates/open_meteo_root.pem new file mode 100644 index 0000000000..b85c8037f6 --- /dev/null +++ b/bitacross-worker/app-libs/oracle/src/certificates/open_meteo_root.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- diff --git a/bitacross-worker/app-libs/oracle/src/error.rs b/bitacross-worker/app-libs/oracle/src/error.rs new file mode 100644 index 0000000000..df72280f34 --- /dev/null +++ b/bitacross-worker/app-libs/oracle/src/error.rs @@ -0,0 +1,39 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use crate::types::TradingPair; +use std::{boxed::Box, string::String}; + +/// Exchange rate error +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("Rest client error")] + RestClient(#[from] itc_rest_client::error::Error), + #[error("Could not retrieve any data from {0} for {1}")] + NoValidData(String, String), + #[error("Value for exchange rate is null")] + EmptyExchangeRate(TradingPair), + #[error("Invalid id for crypto currency")] + InvalidCryptoCurrencyId, + #[error("Invalid id for fiat currency")] + InvalidFiatCurrencyId, + #[error(transparent)] + Other(#[from] Box), +} diff --git a/bitacross-worker/app-libs/oracle/src/lib.rs b/bitacross-worker/app-libs/oracle/src/lib.rs new file mode 100644 index 0000000000..6faee79a63 --- /dev/null +++ b/bitacross-worker/app-libs/oracle/src/lib.rs @@ -0,0 +1,84 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(test, feature(assert_matches))] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +#[macro_use] +extern crate sgx_tstd as std; + +// re-export module to properly feature gate sgx and regular std environment +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod sgx_reexport_prelude { + pub use thiserror_sgx as thiserror; + pub use url_sgx as url; +} + +use crate::{error::Error, metrics_exporter::MetricsExporter}; +use itp_ocall_api::EnclaveMetricsOCallApi; +use std::sync::Arc; + +pub mod error; +pub mod metrics_exporter; +pub mod traits; +pub mod types; + +pub mod oracles; +pub use oracles::{exchange_rate_oracle::ExchangeRateOracle, weather_oracle::WeatherOracle}; + +pub mod oracle_sources; +pub use oracle_sources::{ + coin_gecko::CoinGeckoSource, coin_market_cap::CoinMarketCapSource, + weather_oracle_source::WeatherOracleSource, +}; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod test; + +pub type CoinGeckoExchangeRateOracle = + ExchangeRateOracle>; + +pub type CoinMarketCapExchangeRateOracle = + ExchangeRateOracle>; + +pub type OpenMeteoWeatherOracle = + WeatherOracle>; + +pub fn create_coin_gecko_oracle( + ocall_api: Arc, +) -> CoinGeckoExchangeRateOracle { + ExchangeRateOracle::new(CoinGeckoSource {}, Arc::new(MetricsExporter::new(ocall_api))) +} + +pub fn create_coin_market_cap_oracle( + ocall_api: Arc, +) -> CoinMarketCapExchangeRateOracle { + ExchangeRateOracle::new(CoinMarketCapSource {}, Arc::new(MetricsExporter::new(ocall_api))) +} + +pub fn create_open_meteo_weather_oracle( + ocall_api: Arc, +) -> OpenMeteoWeatherOracle { + WeatherOracle::new(WeatherOracleSource {}, Arc::new(MetricsExporter::new(ocall_api))) +} diff --git a/bitacross-worker/app-libs/oracle/src/metrics_exporter.rs b/bitacross-worker/app-libs/oracle/src/metrics_exporter.rs new file mode 100644 index 0000000000..aa10516fd1 --- /dev/null +++ b/bitacross-worker/app-libs/oracle/src/metrics_exporter.rs @@ -0,0 +1,104 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::types::{ExchangeRate, TradingPair}; +use itp_enclave_metrics::{EnclaveMetric, ExchangeRateOracleMetric, OracleMetric}; +use itp_ocall_api::EnclaveMetricsOCallApi; +use log::error; +use std::{string::String, sync::Arc, time::Instant}; + +/// Trait to export metrics for any Teeracle. +pub trait ExportMetrics { + fn increment_number_requests(&self, source: String); + + fn record_response_time(&self, source: String, timer: Instant); + + fn update_exchange_rate( + &self, + source: String, + exchange_rate: ExchangeRate, + trading_pair: TradingPair, + ); + + fn update_weather(&self, source: String, metrics_info: MetricsInfo); +} + +pub trait UpdateMetric { + fn update_metric(&self, metric: OracleMetric); +} + +/// Metrics exporter implementation. +pub struct MetricsExporter { + ocall_api: Arc, +} + +impl UpdateMetric for MetricsExporter +where + OCallApi: EnclaveMetricsOCallApi, +{ + fn update_metric(&self, _metric: OracleMetric) { + // TODO: Implement me + } +} + +impl MetricsExporter +where + OCallApi: EnclaveMetricsOCallApi, +{ + pub fn new(ocall_api: Arc) -> Self { + MetricsExporter { ocall_api } + } + + fn update_metric(&self, metric: ExchangeRateOracleMetric) { + if let Err(e) = self.ocall_api.update_metric(EnclaveMetric::ExchangeRateOracle(metric)) { + error!("Failed to update enclave metric, sgx_status_t: {}", e) + } + } +} + +impl ExportMetrics for MetricsExporter +where + OCallApi: EnclaveMetricsOCallApi, +{ + fn increment_number_requests(&self, source: String) { + self.update_metric(ExchangeRateOracleMetric::NumberRequestsIncrement(source)); + } + + fn record_response_time(&self, source: String, timer: Instant) { + self.update_metric(ExchangeRateOracleMetric::ResponseTime( + source, + timer.elapsed().as_millis(), + )); + } + + fn update_exchange_rate( + &self, + source: String, + exchange_rate: ExchangeRate, + trading_pair: TradingPair, + ) { + self.update_metric(ExchangeRateOracleMetric::ExchangeRate( + source, + trading_pair.key(), + exchange_rate, + )); + } + + fn update_weather(&self, _source: String, _metrics_info: MetricsInfo) { + // TODO: Implement me + } +} diff --git a/bitacross-worker/app-libs/oracle/src/mock.rs b/bitacross-worker/app-libs/oracle/src/mock.rs new file mode 100644 index 0000000000..f12224b0ea --- /dev/null +++ b/bitacross-worker/app-libs/oracle/src/mock.rs @@ -0,0 +1,120 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(feature = "sgx")] +use std::sync::SgxRwLock as RwLock; + +#[cfg(feature = "std")] +use std::sync::RwLock; + +use crate::{ + error::Error, + metrics_exporter::ExportMetrics, + traits::OracleSource, + types::{ExchangeRate, TradingPair}, +}; +use itc_rest_client::{ + http_client::{HttpClient, SendWithCertificateVerification}, + rest_client::RestClient, +}; +use std::{ + time::{Duration, Instant}, + vec, + vec::Vec, +}; +use url::Url; + +/// Mock metrics exporter. +#[derive(Default)] +pub(crate) struct MetricsExporterMock { + number_requests: RwLock, + response_times: RwLock>, + exchange_rates: RwLock>, +} + +impl MetricsExporterMock { + pub fn get_number_request(&self) -> u64 { + *self.number_requests.read().unwrap() + } + + pub fn get_response_times(&self) -> Vec { + self.response_times.read().unwrap().clone() + } + + pub fn get_exchange_rates(&self) -> Vec<(TradingPair, ExchangeRate)> { + self.exchange_rates.read().unwrap().clone() + } +} + +impl ExportMetrics for MetricsExporterMock { + fn increment_number_requests(&self, _source: String) { + (*self.number_requests.write().unwrap()) += 1; + } + + fn record_response_time(&self, _source: String, timer: Instant) { + self.response_times.write().unwrap().push(timer.elapsed().as_millis()); + } + + fn update_exchange_rate( + &self, + _source: String, + exchange_rate: ExchangeRate, + trading_pair: TradingPair, + ) { + self.exchange_rates.write().unwrap().push((trading_pair, exchange_rate)); + } + + fn update_weather(&self, _source: String, _metrics_info: MetricsInfo) {} +} + +/// Mock oracle source. +#[derive(Default)] +pub(crate) struct OracleSourceMock; + +impl OracleSource for OracleSourceMock { + type OracleRequestResult = Result; + + fn metrics_id(&self) -> String { + "source_mock".to_string() + } + + fn request_timeout(&self) -> Option { + None + } + + fn base_url(&self) -> Result { + Url::parse("https://mock.base.url").map_err(|e| Error::Other(format!("{:?}", e).into())) + } + + fn root_certificates_content(&self) -> Vec { + vec!["MOCK_CERTIFICATE".to_string()] + } + fn execute_exchange_rate_request( + &self, + _rest_client: &mut RestClient>, + _trading_pair: TradingPair, + ) -> Result { + Ok(ExchangeRate::from_num(42.3f32)) + } + + fn execute_request( + _rest_client: &mut RestClient>, + _source_info: OracleSourceInfo, + ) -> Self::OracleRequestResult { + Ok(42.3f32) + } +} diff --git a/bitacross-worker/app-libs/oracle/src/oracle_sources/coin_gecko.rs b/bitacross-worker/app-libs/oracle/src/oracle_sources/coin_gecko.rs new file mode 100644 index 0000000000..d9b8ad91ee --- /dev/null +++ b/bitacross-worker/app-libs/oracle/src/oracle_sources/coin_gecko.rs @@ -0,0 +1,220 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use crate::{ + error::Error, + traits::OracleSource, + types::{ExchangeRate, TradingInfo, TradingPair}, +}; +use itc_rest_client::{ + http_client::{HttpClient, SendWithCertificateVerification}, + rest_client::RestClient, + RestGet, RestPath, +}; +use lazy_static::lazy_static; +use log::{debug, error}; +use serde::{Deserialize, Serialize}; +use std::{ + collections::HashMap, + string::{String, ToString}, + time::Duration, + vec::Vec, +}; +use url::Url; + +const COINGECKO_URL: &str = "https://api.coingecko.com"; +const COINGECKO_PARAM_CURRENCY: &str = "vs_currency"; +const COINGECKO_PARAM_COIN: &str = "ids"; +const COINGECKO_PATH: &str = "api/v3/coins/markets"; +const COINGECKO_TIMEOUT: Duration = Duration::from_secs(20u64); +const COINGECKO_ROOT_CERTIFICATE_BALTIMORE: &str = + include_str!("../certificates/baltimore_cyber_trust_root_v3.pem"); +const COINGECKO_ROOT_CERTIFICATE_LETSENCRYPT: &str = + include_str!("../certificates/lets_encrypt_root_cert.pem"); + +lazy_static! { + static ref SYMBOL_ID_MAP: HashMap<&'static str, &'static str> = HashMap::from([ + ("DOT", "polkadot"), + ("TEER", "integritee"), + ("KSM", "kusama"), + ("BTC", "bitcoin"), + ]); +} + +/// CoinGecko oracle source. +#[derive(Default)] +pub struct CoinGeckoSource; + +impl CoinGeckoSource { + fn map_crypto_currency_id(trading_pair: &TradingPair) -> Result { + let key = &trading_pair.crypto_currency; + match SYMBOL_ID_MAP.get(key.as_str()) { + Some(v) => Ok(v.to_string()), + None => Err(Error::InvalidCryptoCurrencyId), + } + } +} + +impl> OracleSource for CoinGeckoSource { + type OracleRequestResult = Result<(), Error>; + + fn metrics_id(&self) -> String { + "coin_gecko".to_string() + } + + fn request_timeout(&self) -> Option { + Some(COINGECKO_TIMEOUT) + } + + fn base_url(&self) -> Result { + Url::parse(COINGECKO_URL).map_err(|e| Error::Other(format!("{:?}", e).into())) + } + + fn root_certificates_content(&self) -> Vec { + vec![ + COINGECKO_ROOT_CERTIFICATE_LETSENCRYPT.to_string(), + COINGECKO_ROOT_CERTIFICATE_BALTIMORE.to_string(), + ] + } + + fn execute_request( + _rest_client: &mut RestClient>, + source_info: OracleSourceInfo, + ) -> Self::OracleRequestResult { + let _trading_info: TradingInfo = source_info.into(); + // TODO Implement me + Ok(()) + } + + fn execute_exchange_rate_request( + &self, + rest_client: &mut RestClient>, + trading_pair: TradingPair, + ) -> Result { + let fiat_id = trading_pair.fiat_currency.clone(); + let crypto_id = Self::map_crypto_currency_id(&trading_pair)?; + + let response = rest_client.get_with::( + COINGECKO_PATH.to_string(), + &[(COINGECKO_PARAM_CURRENCY, &fiat_id), (COINGECKO_PARAM_COIN, &crypto_id)], + ); + + let response = match response { + Ok(response) => response, + Err(e) => { + error!("coingecko execute_exchange_rate_request() failed with: {:?}", &e); + return Err(Error::RestClient(e)) + }, + }; + + debug!("coingecko received response: {:?}", &response); + let list = response.0; + if list.is_empty() { + return Err(Error::NoValidData(COINGECKO_URL.to_string(), trading_pair.key())) + } + + match list[0].current_price { + Some(r) => Ok(ExchangeRate::from_num(r)), + None => Err(Error::EmptyExchangeRate(trading_pair)), + } + } +} + +#[derive(Serialize, Deserialize, Debug)] +struct CoinGeckoMarketStruct { + id: String, + symbol: String, + name: String, + current_price: Option, + last_updated: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +struct CoinGeckoMarket(pub Vec); + +impl RestPath for CoinGeckoMarket { + fn get_path(path: String) -> Result { + Ok(path) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + mock::MetricsExporterMock, + oracles::exchange_rate_oracle::{ExchangeRateOracle, GetExchangeRate}, + }; + use core::assert_matches::assert_matches; + use std::sync::Arc; + + type TestCoinGeckoClient = ExchangeRateOracle; + + fn get_coin_gecko_crypto_currency_id(crypto_currency: &str) -> Result { + let trading_pair = TradingPair { + crypto_currency: crypto_currency.to_string(), + fiat_currency: "USD".to_string(), + }; + CoinGeckoSource::map_crypto_currency_id(&trading_pair) + } + + #[test] + fn crypto_currency_id_works_for_dot() { + let coin_id = get_coin_gecko_crypto_currency_id("DOT").unwrap(); + assert_eq!(&coin_id, "polkadot"); + } + + #[test] + fn crypto_currency_id_works_for_teer() { + let coin_id = get_coin_gecko_crypto_currency_id("TEER").unwrap(); + assert_eq!(&coin_id, "integritee"); + } + + #[test] + fn crypto_currency_id_works_for_ksm() { + let coin_id = get_coin_gecko_crypto_currency_id("KSM").unwrap(); + assert_eq!(&coin_id, "kusama"); + } + + #[test] + fn crypto_currency_id_works_for_btc() { + let coin_id = get_coin_gecko_crypto_currency_id("BTC").unwrap(); + assert_eq!(&coin_id, "bitcoin"); + } + + #[test] + fn crypto_currency_id_fails_for_undefined_crypto_currency() { + let result = get_coin_gecko_crypto_currency_id("Undefined"); + assert_matches!(result, Err(Error::InvalidCryptoCurrencyId)); + } + + #[test] + fn get_exchange_rate_for_undefined_fiat_currency_fails() { + let coin_gecko_client = create_coin_gecko_client(); + let trading_pair = + TradingPair { crypto_currency: "DOT".to_string(), fiat_currency: "CH".to_string() }; + let result = coin_gecko_client.get_exchange_rate(trading_pair); + assert_matches!(result, Err(Error::RestClient(_))); + } + + fn create_coin_gecko_client() -> TestCoinGeckoClient { + TestCoinGeckoClient::new(CoinGeckoSource {}, Arc::new(MetricsExporterMock::default())) + } +} diff --git a/bitacross-worker/app-libs/oracle/src/oracle_sources/coin_market_cap.rs b/bitacross-worker/app-libs/oracle/src/oracle_sources/coin_market_cap.rs new file mode 100644 index 0000000000..a0e053b8e6 --- /dev/null +++ b/bitacross-worker/app-libs/oracle/src/oracle_sources/coin_market_cap.rs @@ -0,0 +1,242 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use crate::{ + error::Error, + traits::OracleSource, + types::{ExchangeRate, TradingInfo, TradingPair}, +}; +use itc_rest_client::{ + http_client::{HttpClient, SendWithCertificateVerification}, + rest_client::RestClient, + RestGet, RestPath, +}; +use lazy_static::lazy_static; +use serde::{Deserialize, Serialize}; +use std::{ + collections::{BTreeMap, HashMap}, + env, + string::{String, ToString}, + time::Duration, + vec::Vec, +}; +use url::Url; + +const COINMARKETCAP_URL: &str = "https://pro-api.coinmarketcap.com"; +const COINMARKETCAP_KEY_PARAM: &str = "CMC_PRO_API_KEY"; +const FIAT_CURRENCY_PARAM: &str = "convert_id"; +const CRYPTO_CURRENCY_PARAM: &str = "id"; +const COINMARKETCAP_PATH: &str = "v2/cryptocurrency/quotes/latest"; // API endpoint to get the exchange rate with a basic API plan (free) +const COINMARKETCAP_TIMEOUT: Duration = Duration::from_secs(3u64); +const COINMARKETCAP_ROOT_CERTIFICATE: &str = include_str!("../certificates/amazon_root_ca_a.pem"); + +lazy_static! { + static ref CRYPTO_SYMBOL_ID_MAP: HashMap<&'static str, &'static str> = + HashMap::from([("DOT", "6636"), ("TEER", "13323"), ("KSM", "5034"), ("BTC", "1"),]); + static ref COINMARKETCAP_KEY: String = env::var("COINMARKETCAP_KEY").unwrap_or_default(); +} + +lazy_static! { + static ref FIAT_SYMBOL_ID_MAP: HashMap<&'static str, &'static str> = + HashMap::from([("USD", "2781"), ("EUR", "2790"), ("CHF", "2785"), ("JPY", "2797"),]); +} + +#[derive(Default)] +pub struct CoinMarketCapSource; + +impl CoinMarketCapSource { + fn map_crypto_currency_id(trading_pair: &TradingPair) -> Result { + CRYPTO_SYMBOL_ID_MAP + .get(trading_pair.crypto_currency.as_str()) + .map(|v| v.to_string()) + .ok_or(Error::InvalidCryptoCurrencyId) + } + + fn map_fiat_currency_id(trading_pair: &TradingPair) -> Result { + FIAT_SYMBOL_ID_MAP + .get(trading_pair.fiat_currency.as_str()) + .map(|v| v.to_string()) + .ok_or(Error::InvalidFiatCurrencyId) + } +} + +impl> OracleSource for CoinMarketCapSource { + // TODO Change this to return something useful? + type OracleRequestResult = Result<(), Error>; + + fn metrics_id(&self) -> String { + "coin_market_cap".to_string() + } + + fn request_timeout(&self) -> Option { + Some(COINMARKETCAP_TIMEOUT) + } + + fn base_url(&self) -> Result { + Url::parse(COINMARKETCAP_URL).map_err(|e| Error::Other(format!("{:?}", e).into())) + } + + fn root_certificates_content(&self) -> Vec { + vec![COINMARKETCAP_ROOT_CERTIFICATE.to_string()] + } + + fn execute_request( + _rest_client: &mut RestClient>, + source_info: OracleSourceInfo, + ) -> Self::OracleRequestResult { + let trading_info: TradingInfo = source_info.into(); + let _fiat_currency = trading_info.trading_pair.fiat_currency; + let _crypto_currency = trading_info.trading_pair.crypto_currency; + // TODO Implement me + Ok(()) + } + + fn execute_exchange_rate_request( + &self, + rest_client: &mut RestClient>, + trading_pair: TradingPair, + ) -> Result { + let fiat_id = Self::map_fiat_currency_id(&trading_pair)?; + let crypto_id = Self::map_crypto_currency_id(&trading_pair)?; + + let response = rest_client + .get_with::( + COINMARKETCAP_PATH.to_string(), + &[ + (FIAT_CURRENCY_PARAM, &fiat_id), + (CRYPTO_CURRENCY_PARAM, &crypto_id), + (COINMARKETCAP_KEY_PARAM, &COINMARKETCAP_KEY), + ], + ) + .map_err(Error::RestClient)?; + + let data_struct = response.0; + + let data = match data_struct.data.get(&crypto_id) { + Some(d) => d, + None => + return Err(Error::NoValidData( + COINMARKETCAP_URL.to_string(), + trading_pair.crypto_currency, + )), + }; + + let quote = match data.quote.get(&fiat_id) { + Some(q) => q, + None => + return Err(Error::NoValidData(COINMARKETCAP_URL.to_string(), trading_pair.key())), + }; + match quote.price { + Some(r) => Ok(ExchangeRate::from_num(r)), + None => Err(Error::EmptyExchangeRate(trading_pair)), + } + } +} + +#[derive(Serialize, Deserialize, Debug)] +struct DataStruct { + id: Option, + name: String, + symbol: String, + quote: BTreeMap, +} + +#[derive(Serialize, Deserialize, Debug)] +struct QuoteStruct { + price: Option, + last_updated: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +struct CoinMarketCapMarketStruct { + data: BTreeMap, +} + +#[derive(Serialize, Deserialize, Debug)] +struct CoinMarketCapMarket(pub CoinMarketCapMarketStruct); + +impl RestPath for CoinMarketCapMarket { + fn get_path(path: String) -> Result { + Ok(path) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + mock::MetricsExporterMock, + oracles::exchange_rate_oracle::{ExchangeRateOracle, GetExchangeRate}, + }; + use core::assert_matches::assert_matches; + use std::sync::Arc; + + type TestClient = ExchangeRateOracle; + + fn get_coin_market_cap_crypto_currency_id(crypto_currency: &str) -> Result { + let trading_pair = TradingPair { + crypto_currency: crypto_currency.to_string(), + fiat_currency: "USD".to_string(), + }; + CoinMarketCapSource::map_crypto_currency_id(&trading_pair) + } + + #[test] + fn crypto_currency_id_works_for_dot() { + let coin_id = get_coin_market_cap_crypto_currency_id("DOT").unwrap(); + assert_eq!(&coin_id, "6636"); + } + + #[test] + fn crypto_currency_id_works_for_teer() { + let coin_id = get_coin_market_cap_crypto_currency_id("TEER").unwrap(); + assert_eq!(&coin_id, "13323"); + } + + #[test] + fn crypto_currency_id_works_for_ksm() { + let coin_id = get_coin_market_cap_crypto_currency_id("KSM").unwrap(); + assert_eq!(&coin_id, "5034"); + } + + #[test] + fn crypto_currency_id_works_for_btc() { + let coin_id = get_coin_market_cap_crypto_currency_id("BTC").unwrap(); + assert_eq!(&coin_id, "1"); + } + + #[test] + fn crypto_currency_id_fails_for_undefined_crypto_currency() { + let coin_id = get_coin_market_cap_crypto_currency_id("Undefined"); + assert_matches!(coin_id, Err(Error::InvalidCryptoCurrencyId)); + } + + #[test] + fn get_exchange_rate_for_undefined_fiat_currency_fails() { + let coin_market_cap_client = create_client(); + let trading_pair = + TradingPair { crypto_currency: "DOT".to_string(), fiat_currency: "CH".to_string() }; + let result = coin_market_cap_client.get_exchange_rate(trading_pair); + assert_matches!(result, Err(Error::InvalidFiatCurrencyId)); + } + + fn create_client() -> TestClient { + TestClient::new(CoinMarketCapSource {}, Arc::new(MetricsExporterMock::default())) + } +} diff --git a/bitacross-worker/app-libs/oracle/src/oracle_sources/mod.rs b/bitacross-worker/app-libs/oracle/src/oracle_sources/mod.rs new file mode 100644 index 0000000000..d2d88153c3 --- /dev/null +++ b/bitacross-worker/app-libs/oracle/src/oracle_sources/mod.rs @@ -0,0 +1,19 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +pub mod coin_gecko; +pub mod coin_market_cap; +pub mod weather_oracle_source; diff --git a/bitacross-worker/app-libs/oracle/src/oracle_sources/weather_oracle_source.rs b/bitacross-worker/app-libs/oracle/src/oracle_sources/weather_oracle_source.rs new file mode 100644 index 0000000000..9f199be5dc --- /dev/null +++ b/bitacross-worker/app-libs/oracle/src/oracle_sources/weather_oracle_source.rs @@ -0,0 +1,120 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use crate::{ + error::Error, + traits::OracleSource, + types::{ExchangeRate, TradingPair, WeatherInfo}, +}; +use itc_rest_client::{ + http_client::{HttpClient, SendWithCertificateVerification}, + rest_client::RestClient, + RestGet, RestPath, +}; +use serde::{Deserialize, Serialize}; +use std::{ + string::{String, ToString}, + time::Duration, + vec::Vec, +}; +use url::Url; + +const WEATHER_URL: &str = "https://api.open-meteo.com"; +const WEATHER_PARAM_LONGITUDE: &str = "longitude"; +const WEATHER_PARAM_LATITUDE: &str = "latitude"; +// const WEATHER_PARAM_HOURLY: &str = "hourly"; // TODO: Add to Query +const WEATHER_PATH: &str = "v1/forecast"; +const WEATHER_TIMEOUT: Duration = Duration::from_secs(3u64); +const WEATHER_ROOT_CERTIFICATE: &str = include_str!("../certificates/open_meteo_root.pem"); + +// TODO: Change f32 types to appropriate Substrate Fixed Type +#[derive(Default)] +pub struct WeatherOracleSource; + +impl> OracleSource for WeatherOracleSource { + type OracleRequestResult = Result; // TODO: Change from f32 type + + fn metrics_id(&self) -> String { + "weather".to_string() + } + + fn request_timeout(&self) -> Option { + Some(WEATHER_TIMEOUT) + } + + fn base_url(&self) -> Result { + Url::parse(WEATHER_URL).map_err(|e| Error::Other(format!("{:?}", e).into())) + } + + /// The server's root certificate. A valid certificate is required to open a tls connection + fn root_certificates_content(&self) -> Vec { + vec![WEATHER_ROOT_CERTIFICATE.to_string()] + } + + fn execute_exchange_rate_request( + &self, + _rest_client: &mut RestClient>, + _trading_pair: TradingPair, + ) -> Result { + Err(Error::NoValidData("None".into(), "None".into())) + } + + // TODO: Make this take a variant perhaps or a Closure so that it is more generic + fn execute_request( + rest_client: &mut RestClient>, + source_info: OracleSourceInfo, + ) -> Self::OracleRequestResult { + let weather_info: WeatherInfo = source_info.into(); + let query = weather_info.weather_query; + + // TODO: + // This part is opinionated towards a hard coded query need to make more generic + let response = rest_client + .get_with::( + WEATHER_PATH.into(), + &[ + (WEATHER_PARAM_LATITUDE, &query.latitude), + (WEATHER_PARAM_LONGITUDE, &query.longitude), + //(WEATHER_PARAM_HOURLY), &query.hourly), + ], + ) + .map_err(Error::RestClient)?; + + let open_meteo_weather_struct = response.0; + + Ok(open_meteo_weather_struct.longitude) + } +} + +#[derive(Serialize, Deserialize, Debug)] +struct OpenMeteoWeatherStruct { + latitude: f32, + longitude: f32, + //hourly: String, +} + +#[derive(Serialize, Deserialize, Debug)] +struct OpenMeteo(pub OpenMeteoWeatherStruct); + +impl RestPath for OpenMeteo { + fn get_path(path: String) -> Result { + Ok(path) + } +} diff --git a/bitacross-worker/app-libs/oracle/src/oracles/exchange_rate_oracle.rs b/bitacross-worker/app-libs/oracle/src/oracles/exchange_rate_oracle.rs new file mode 100644 index 0000000000..0198a5fe1b --- /dev/null +++ b/bitacross-worker/app-libs/oracle/src/oracles/exchange_rate_oracle.rs @@ -0,0 +1,154 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use crate::{ + metrics_exporter::ExportMetrics, + traits::OracleSource, + types::{ExchangeRate, TradingInfo, TradingPair}, + Error, +}; +use itc_rest_client::{ + http_client::{HttpClient, SendWithCertificateVerification}, + rest_client::RestClient, +}; +use log::*; +use std::{ + sync::Arc, + thread, + time::{Duration, Instant}, +}; +use url::Url; + +#[allow(unused)] +pub struct ExchangeRateOracle { + oracle_source: OracleSourceType, + metrics_exporter: Arc, +} + +impl ExchangeRateOracle { + pub fn new(oracle_source: OracleSourceType, metrics_exporter: Arc) -> Self { + ExchangeRateOracle { oracle_source, metrics_exporter } + } +} + +pub trait GetExchangeRate { + /// Get the cryptocurrency/fiat_currency exchange rate + fn get_exchange_rate(&self, trading_pair: TradingPair) -> Result<(ExchangeRate, Url), Error>; +} + +impl GetExchangeRate + for ExchangeRateOracle +where + OracleSourceType: OracleSource, + MetricsExporter: ExportMetrics, +{ + fn get_exchange_rate(&self, trading_pair: TradingPair) -> Result<(ExchangeRate, Url), Error> { + let source_id = self.oracle_source.metrics_id(); + self.metrics_exporter.increment_number_requests(source_id.clone()); + + let base_url = self.oracle_source.base_url()?; + let root_certificates = self.oracle_source.root_certificates_content(); + let request_timeout = self.oracle_source.request_timeout(); + + debug!("Get exchange rate from URI: {}, trading pair: {:?}", base_url, trading_pair); + + let http_client = HttpClient::new( + SendWithCertificateVerification::new(root_certificates), + true, + request_timeout, + None, + None, + ); + let mut rest_client = RestClient::new(http_client, base_url.clone()); + + // Due to possible failures that may be temporarily this function tries to fetch the exchange rates `number_of_tries` times. + // If it still fails for the last attempt, then only in that case will it be considered a non-recoverable error. + let number_of_tries = 3; + let timer_start = Instant::now(); + + let mut tries = 0; + let result = loop { + tries += 1; + let exchange_result = self + .oracle_source + .execute_exchange_rate_request(&mut rest_client, trading_pair.clone()); + + match exchange_result { + Ok(exchange_rate) => { + self.metrics_exporter.record_response_time(source_id.clone(), timer_start); + self.metrics_exporter.update_exchange_rate( + source_id, + exchange_rate, + trading_pair, + ); + + debug!("Successfully executed exchange rate request"); + break Ok((exchange_rate, base_url)) + }, + Err(e) => + if tries < number_of_tries { + error!( + "Getting exchange rate from {} failed with {}, trying again in {:?}.", + &base_url, e, request_timeout + ); + debug!("Check that the API endpoint is available, for coingecko: https://status.coingecko.com/"); + thread::sleep( + request_timeout.unwrap_or_else(|| Duration::from_secs(number_of_tries)), + ); + } else { + error!( + "Getting exchange rate from {} failed {} times, latest error is: {}.", + &base_url, number_of_tries, &e + ); + break Err(e) + }, + } + }; + result + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mock::{MetricsExporterMock, OracleSourceMock}; + + type TestOracle = ExchangeRateOracle; + + #[test] + fn get_exchange_rate_updates_metrics() { + let metrics_exporter = Arc::new(MetricsExporterMock::default()); + let test_client = TestOracle::new(OracleSourceMock {}, metrics_exporter.clone()); + + let trading_pair = + TradingPair { crypto_currency: "BTC".to_string(), fiat_currency: "USD".to_string() }; + let _bit_usd = test_client.get_exchange_rate(trading_pair.clone()).unwrap(); + + assert_eq!(1, metrics_exporter.get_number_request()); + assert_eq!(1, metrics_exporter.get_response_times().len()); + assert_eq!(1, metrics_exporter.get_exchange_rates().len()); + + let (metric_trading_pair, exchange_rate) = + metrics_exporter.get_exchange_rates().first().unwrap().clone(); + + assert_eq!(trading_pair, metric_trading_pair); + assert_eq!(ExchangeRate::from_num(42.3f32), exchange_rate); + } +} diff --git a/bitacross-worker/app-libs/oracle/src/oracles/mod.rs b/bitacross-worker/app-libs/oracle/src/oracles/mod.rs new file mode 100644 index 0000000000..d6100d2469 --- /dev/null +++ b/bitacross-worker/app-libs/oracle/src/oracles/mod.rs @@ -0,0 +1,18 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +pub mod exchange_rate_oracle; +pub mod weather_oracle; diff --git a/bitacross-worker/app-libs/oracle/src/oracles/weather_oracle.rs b/bitacross-worker/app-libs/oracle/src/oracles/weather_oracle.rs new file mode 100644 index 0000000000..66809f7f3a --- /dev/null +++ b/bitacross-worker/app-libs/oracle/src/oracles/weather_oracle.rs @@ -0,0 +1,83 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use crate::{metrics_exporter::ExportMetrics, traits::OracleSource, types::WeatherInfo, Error}; +use itc_rest_client::{ + http_client::{HttpClient, SendWithCertificateVerification}, + rest_client::RestClient, +}; +use log::*; +use std::sync::Arc; +use url::Url; + +#[allow(unused)] +pub struct WeatherOracle { + oracle_source: OracleSourceType, + metrics_exporter: Arc, +} + +impl WeatherOracle +where + OracleSourceType: OracleSource, +{ + pub fn new(oracle_source: OracleSourceType, metrics_exporter: Arc) -> Self { + WeatherOracle { oracle_source, metrics_exporter } + } + + pub fn get_base_url(&self) -> Result { + self.oracle_source.base_url() + } +} + +pub trait GetLongitude { + type LongitudeResult; + fn get_longitude(&self, weather_info: WeatherInfo) -> Self::LongitudeResult; +} + +impl GetLongitude + for WeatherOracle +where + OracleSourceType: OracleSource>, + MetricsExporter: ExportMetrics, +{ + type LongitudeResult = Result; + + fn get_longitude(&self, weather_info: WeatherInfo) -> Self::LongitudeResult { + let query = weather_info.weather_query.clone(); + + let base_url = self.oracle_source.base_url()?; + let root_certificates = self.oracle_source.root_certificates_content(); + + debug!("Get longitude from URI: {}, query: {:?}", base_url, query); + + let http_client = HttpClient::new( + SendWithCertificateVerification::new(root_certificates), + true, + self.oracle_source.request_timeout(), + None, + None, + ); + let mut rest_client = RestClient::new(http_client, base_url); + >::execute_request( + &mut rest_client, + weather_info, + ) + } +} diff --git a/bitacross-worker/app-libs/oracle/src/test.rs b/bitacross-worker/app-libs/oracle/src/test.rs new file mode 100644 index 0000000000..8d083a18a0 --- /dev/null +++ b/bitacross-worker/app-libs/oracle/src/test.rs @@ -0,0 +1,125 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Integration tests for concrete exchange rate oracle implementations. +//! Uses real HTTP requests, so the sites must be available for these tests. + +use crate::{ + error::Error, + mock::MetricsExporterMock, + oracle_sources::{ + coin_gecko::CoinGeckoSource, coin_market_cap::CoinMarketCapSource, + weather_oracle_source::WeatherOracleSource, + }, + oracles::{ + exchange_rate_oracle::{ExchangeRateOracle, GetExchangeRate}, + weather_oracle::{GetLongitude, WeatherOracle}, + }, + traits::OracleSource, + types::{TradingInfo, TradingPair, WeatherInfo, WeatherQuery}, +}; +use core::assert_matches::assert_matches; +use std::sync::Arc; +use substrate_fixed::transcendental::ZERO; + +type TestOracle = ExchangeRateOracle; +type TestWeatherOracle = WeatherOracle; + +#[test] +#[ignore = "requires API key for CoinMarketCap"] +fn get_exchange_rate_from_coin_market_cap_works() { + test_suite_exchange_rates::(); +} + +#[test] +#[ignore = "requires external coin gecko service, disabled temporarily"] +fn get_exchange_rate_from_coin_gecko_works() { + test_suite_exchange_rates::(); +} + +#[test] +fn get_longitude_from_open_meteo_works() { + let oracle = create_weather_oracle::(); + let weather_query = + WeatherQuery { latitude: "52.52".into(), longitude: "13.41".into(), hourly: "none".into() }; + // Todo: hourly param is temperature_2m to get temp or relativehumidity_2m to get humidity + let weather_info = WeatherInfo { weather_query }; + let expected_longitude = 13.41f32; + let response_longitude = + oracle.get_longitude(weather_info).expect("Can grab longitude from oracle"); + assert!((response_longitude - expected_longitude) < 0.5); +} + +#[test] +fn get_exchange_rate_for_undefined_coin_market_cap_crypto_currency_fails() { + get_exchange_rate_for_undefined_crypto_currency_fails::(); +} + +#[test] +fn get_exchange_rate_for_undefined_coin_gecko_crypto_currency_fails() { + get_exchange_rate_for_undefined_crypto_currency_fails::(); +} + +fn create_weather_oracle>( +) -> TestWeatherOracle { + let oracle_source = OracleSourceType::default(); + WeatherOracle::new(oracle_source, Arc::new(MetricsExporterMock::default())) +} + +fn create_exchange_rate_oracle>( +) -> TestOracle { + let oracle_source = OracleSourceType::default(); + ExchangeRateOracle::new(oracle_source, Arc::new(MetricsExporterMock::default())) +} + +fn get_exchange_rate_for_undefined_crypto_currency_fails< + OracleSourceType: OracleSource, +>() { + let oracle = create_exchange_rate_oracle::(); + let trading_pair = TradingPair { + crypto_currency: "invalid_coin".to_string(), + fiat_currency: "USD".to_string(), + }; + let result = oracle.get_exchange_rate(trading_pair); + assert_matches!(result, Err(Error::InvalidCryptoCurrencyId)); +} + +fn test_suite_exchange_rates>() { + let oracle = create_exchange_rate_oracle::(); + let dot_to_usd = + TradingPair { crypto_currency: "DOT".to_string(), fiat_currency: "USD".to_string() }; + let dot_usd = oracle.get_exchange_rate(dot_to_usd).unwrap().0; + assert!(dot_usd > 0f32); + let btc_to_usd = + TradingPair { crypto_currency: "BTC".to_string(), fiat_currency: "USD".to_string() }; + let bit_usd = oracle.get_exchange_rate(btc_to_usd).unwrap().0; + assert!(bit_usd > 0f32); + let dot_to_chf = + TradingPair { crypto_currency: "DOT".to_string(), fiat_currency: "CHF".to_string() }; + let dot_chf = oracle.get_exchange_rate(dot_to_chf).unwrap().0; + assert!(dot_chf > 0f32); + let bit_to_chf = + TradingPair { crypto_currency: "BTC".to_string(), fiat_currency: "CHF".to_string() }; + let bit_chf = oracle.get_exchange_rate(bit_to_chf).unwrap().0; + + // Ensure that get_exchange_rate returns a positive rate + assert!(dot_usd > ZERO); + + // Ensure that get_exchange_rate returns a valid value by checking + // that the values obtained for DOT/BIT from different exchange rates are the same + assert_eq!((dot_usd / bit_usd).round(), (dot_chf / bit_chf).round()); +} diff --git a/bitacross-worker/app-libs/oracle/src/traits.rs b/bitacross-worker/app-libs/oracle/src/traits.rs new file mode 100644 index 0000000000..1ca1d21428 --- /dev/null +++ b/bitacross-worker/app-libs/oracle/src/traits.rs @@ -0,0 +1,55 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use crate::{ + types::{ExchangeRate, TradingPair}, + Error, +}; +use core::time::Duration; +use itc_rest_client::{ + http_client::{HttpClient, SendWithCertificateVerification}, + rest_client::RestClient, +}; +use std::{string::String, vec::Vec}; +use url::Url; + +pub trait OracleSource: Default { + type OracleRequestResult; + + fn metrics_id(&self) -> String; + + fn request_timeout(&self) -> Option; + + fn base_url(&self) -> Result; + + /// The server's root certificate(s). A valid certificate is required to open a tls connection + fn root_certificates_content(&self) -> Vec; + + fn execute_exchange_rate_request( + &self, + rest_client: &mut RestClient>, + trading_pair: TradingPair, + ) -> Result; + + fn execute_request( + rest_client: &mut RestClient>, + source_info: OracleSourceInfo, + ) -> Self::OracleRequestResult; +} diff --git a/bitacross-worker/app-libs/oracle/src/types.rs b/bitacross-worker/app-libs/oracle/src/types.rs new file mode 100644 index 0000000000..ef969ccb90 --- /dev/null +++ b/bitacross-worker/app-libs/oracle/src/types.rs @@ -0,0 +1,61 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use codec::{Decode, Encode}; +use std::string::String; +use substrate_fixed::types::U32F32; + +#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq)] +pub struct WeatherInfo { + pub weather_query: WeatherQuery, +} + +#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq)] +pub struct WeatherQuery { + pub longitude: String, + pub latitude: String, + pub hourly: String, +} + +impl WeatherQuery { + pub fn key(self) -> String { + format!("{}/{}", self.latitude, self.longitude) + } +} + +#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq)] +pub struct TradingInfo { + pub trading_pair: TradingPair, + pub exchange_rate: ExchangeRate, +} +/// Market identifier for order +#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq)] +pub struct TradingPair { + pub crypto_currency: String, + pub fiat_currency: String, +} + +impl TradingPair { + pub fn key(self) -> String { + format!("{}/{}", self.crypto_currency, self.fiat_currency) + } +} + +/// TODO Fix https://github.com/integritee-network/pallets/issues/71 and get it from https://github.com/integritee-network/pallets.git +/// Teeracle types +pub type ExchangeRate = U32F32; +// pub type Coordinate = U32F32; diff --git a/bitacross-worker/app-libs/parentchain-interface/Cargo.toml b/bitacross-worker/app-libs/parentchain-interface/Cargo.toml new file mode 100644 index 0000000000..39e4827588 --- /dev/null +++ b/bitacross-worker/app-libs/parentchain-interface/Cargo.toml @@ -0,0 +1,79 @@ +[package] +name = "ita-parentchain-interface" +version = "0.9.0" +authors = ["Integritee AG "] +edition = "2021" + +[dependencies] +# sgx dependencies +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } + +# local dependencies +ita-sgx-runtime = { path = "../sgx-runtime", default-features = false } +ita-stf = { path = "../stf", default-features = false } +itc-parentchain-indirect-calls-executor = { path = "../../core/parentchain/indirect-calls-executor", default-features = false } +itp-api-client-types = { path = "../../core-primitives/node-api/api-client-types", default-features = false } +itp-node-api = { path = "../../core-primitives/node-api", default-features = false } +itp-stf-primitives = { path = "../../core-primitives/stf-primitives", default-features = false } +itp-types = { path = "../../core-primitives/types", default-features = false } +itp-utils = { path = "../../core-primitives/utils", default-features = false } + +# no-std compatible libraries +bs58 = { version = "0.4.0", default-features = false, features = ["alloc"] } +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +log = { version = "0.4", default-features = false } + +# substrate dep +sp-core = { default-features = false, features = ["full_crypto"], git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# litentry +lc-scheduled-enclave = { path = "../../litentry/core/scheduled-enclave", default-features = false, optional = true } +litentry-primitives = { path = "../../litentry/primitives", default-features = false } +sp-std = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +[dev-dependencies] +env_logger = "0.9.0" +itp-node-api = { path = "../../core-primitives/node-api", features = ["mocks"] } +itp-sgx-crypto = { path = "../../core-primitives/sgx/crypto", features = ["mocks"] } +itp-stf-executor = { path = "../../core-primitives/stf-executor", features = ["mocks"] } +itp-test = { path = "../../core-primitives/test" } +itp-top-pool-author = { path = "../../core-primitives/top-pool-author", features = ["mocks"] } +itc-parentchain-test = { path = "../../core/parentchain/test" } + + +[features] +default = ["std"] +std = [ + "bs58/std", + "codec/std", + "ita-sgx-runtime/std", + "ita-stf/std", + "itc-parentchain-indirect-calls-executor/std", + "itp-api-client-types/std", + "itp-node-api/std", + "itp-sgx-crypto/std", + "itp-stf-executor/std", + "itp-stf-primitives/std", + "itp-top-pool-author/std", + "itp-types/std", + "itp-utils/std", + "log/std", + #substrate + "sp-core/std", + "sp-runtime/std", + "litentry-primitives/std", + "lc-scheduled-enclave/std", + "sp-std/std", +] +sgx = [ + "sgx_tstd", + "ita-stf/sgx", + "itc-parentchain-indirect-calls-executor/sgx", + "itp-node-api/sgx", + "itp-sgx-crypto/sgx", + "itp-stf-executor/sgx", + "itp-top-pool-author/sgx", + "litentry-primitives/sgx", + "lc-scheduled-enclave/sgx", +] diff --git a/bitacross-worker/app-libs/parentchain-interface/src/indirect_calls/invoke.rs b/bitacross-worker/app-libs/parentchain-interface/src/indirect_calls/invoke.rs new file mode 100644 index 0000000000..af3bb0c088 --- /dev/null +++ b/bitacross-worker/app-libs/parentchain-interface/src/indirect_calls/invoke.rs @@ -0,0 +1,41 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use codec::{Decode, Encode}; +use ita_stf::TrustedCallSigned; +use itc_parentchain_indirect_calls_executor::{ + error::{Error, Result}, + IndirectDispatch, +}; +use itp_stf_primitives::traits::IndirectExecutor; +use itp_types::{DecryptableRequest, RsaRequest}; + +#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq)] +pub struct InvokeArgs { + request: RsaRequest, +} + +impl> + IndirectDispatch for InvokeArgs +{ + type Args = (); + fn dispatch(&self, executor: &Executor, _args: Self::Args) -> Result<()> { + log::debug!("Found trusted call extrinsic, submitting it to the top pool"); + executor.submit_trusted_call(self.request.shard(), self.request.payload().to_vec()); + Ok(()) + } +} diff --git a/bitacross-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/args_executor.rs b/bitacross-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/args_executor.rs new file mode 100644 index 0000000000..05084bfee0 --- /dev/null +++ b/bitacross-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/args_executor.rs @@ -0,0 +1,62 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +use codec::Encode; +use ita_stf::{Getter, TrustedCall, TrustedCallSigned, TrustedOperation}; +use itc_parentchain_indirect_calls_executor::error::{Error, Result}; +use itp_stf_primitives::traits::IndirectExecutor; +use itp_types::{ShardIdentifier, H256}; +use sp_core::crypto::AccountId32; +use sp_runtime::MultiAddress; + +pub trait ArgsExecutor { + fn error(&self) -> Error; + fn name() -> &'static str; + fn shard(&self) -> ShardIdentifier; + fn prepare_trusted_call>( + &self, + executor: &Executor, + address: MultiAddress, + hash: H256, + ) -> Result; + fn execute>( + &self, + executor: &Executor, + address: Option>, + hash: H256, + ) -> Result<()> { + if let Some(address) = address { + self.submit(executor, address, hash)? + } + Ok(()) + } + + fn submit>( + &self, + executor: &Executor, + address: MultiAddress, + hash: H256, + ) -> Result<()> { + let trusted_call = self.prepare_trusted_call(executor, address, hash)?; + let signed_trusted_call = executor.sign_call_with_self(&trusted_call, &self.shard())?; + let trusted_operation = + TrustedOperation::::indirect_call(signed_trusted_call); + + let encrypted_trusted_call = executor.encrypt(&trusted_operation.encode())?; + executor.submit_trusted_call(self.shard(), encrypted_trusted_call); + Ok(()) + } +} diff --git a/bitacross-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/mod.rs b/bitacross-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/mod.rs new file mode 100644 index 0000000000..830f452476 --- /dev/null +++ b/bitacross-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/mod.rs @@ -0,0 +1,18 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +mod args_executor; +pub mod scheduled_enclave; diff --git a/bitacross-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/scheduled_enclave.rs b/bitacross-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/scheduled_enclave.rs new file mode 100644 index 0000000000..a0d1ff65bc --- /dev/null +++ b/bitacross-worker/app-libs/parentchain-interface/src/indirect_calls/litentry/scheduled_enclave.rs @@ -0,0 +1,62 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +use codec::{Decode, Encode}; +use ita_stf::TrustedCallSigned; +use itc_parentchain_indirect_calls_executor::{ + error::{Error, Result}, + IndirectDispatch, +}; +use itp_stf_primitives::traits::IndirectExecutor; +use itp_types::{MrEnclave, SidechainBlockNumber}; +use lc_scheduled_enclave::{ScheduledEnclaveUpdater, GLOBAL_SCHEDULED_ENCLAVE}; +use log::debug; + +#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq)] +pub struct UpdateScheduledEnclaveArgs { + sbn: codec::Compact, + mrenclave: MrEnclave, +} + +impl> + IndirectDispatch for UpdateScheduledEnclaveArgs +{ + type Args = (); + fn dispatch(&self, _executor: &Executor, _args: Self::Args) -> Result<()> { + debug!("execute indirect call: UpdateScheduledEnclave, sidechain_block_number: {:?}, mrenclave: {:?}", self.sbn, self.mrenclave); + GLOBAL_SCHEDULED_ENCLAVE.update(self.sbn.into(), self.mrenclave)?; + Ok(()) + } +} + +#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq)] +pub struct RemoveScheduledEnclaveArgs { + sbn: codec::Compact, +} + +impl> + IndirectDispatch for RemoveScheduledEnclaveArgs +{ + type Args = (); + fn dispatch(&self, _executor: &Executor, _args: Self::Args) -> Result<()> { + debug!( + "execute indirect call: RemoveScheduledEnclave, sidechain_block_number: {:?}", + self.sbn + ); + GLOBAL_SCHEDULED_ENCLAVE.remove(self.sbn.into())?; + Ok(()) + } +} diff --git a/bitacross-worker/app-libs/parentchain-interface/src/indirect_calls/mod.rs b/bitacross-worker/app-libs/parentchain-interface/src/indirect_calls/mod.rs new file mode 100644 index 0000000000..88fe8aab4d --- /dev/null +++ b/bitacross-worker/app-libs/parentchain-interface/src/indirect_calls/mod.rs @@ -0,0 +1,26 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pub mod invoke; +mod litentry; +pub mod shield_funds; +pub mod transfer_to_alice_shields_funds; + +pub use invoke::InvokeArgs; +pub use litentry::scheduled_enclave::{RemoveScheduledEnclaveArgs, UpdateScheduledEnclaveArgs}; +pub use shield_funds::ShieldFundsArgs; +pub use transfer_to_alice_shields_funds::{TransferToAliceShieldsFundsArgs, ALICE_ACCOUNT_ID}; diff --git a/bitacross-worker/app-libs/parentchain-interface/src/indirect_calls/shield_funds.rs b/bitacross-worker/app-libs/parentchain-interface/src/indirect_calls/shield_funds.rs new file mode 100644 index 0000000000..0204c0b80b --- /dev/null +++ b/bitacross-worker/app-libs/parentchain-interface/src/indirect_calls/shield_funds.rs @@ -0,0 +1,62 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use codec::{Decode, Encode}; +use ita_stf::{Getter, TrustedCall, TrustedCallSigned}; +use itc_parentchain_indirect_calls_executor::{ + error::{Error, Result}, + IndirectDispatch, +}; +use itp_stf_primitives::{ + traits::IndirectExecutor, + types::{AccountId, TrustedOperation}, +}; +use itp_types::{Balance, ShardIdentifier}; +use log::{debug, info}; +use std::vec::Vec; +/// Arguments of the Integritee-Parachain's shield fund dispatchable. +#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq)] +pub struct ShieldFundsArgs { + account_encrypted: Vec, + amount: Balance, + shard: ShardIdentifier, +} + +impl> + IndirectDispatch for ShieldFundsArgs +{ + type Args = (); + fn dispatch(&self, executor: &Executor, _args: Self::Args) -> Result<()> { + info!("Found ShieldFunds extrinsic in block: \nAccount Encrypted {:?} \nAmount: {} \nShard: {}", + self.account_encrypted, self.amount, bs58::encode(self.shard.encode()).into_string()); + + debug!("decrypt the account id"); + let account_vec = executor.decrypt(&self.account_encrypted)?; + let account = AccountId::decode(&mut account_vec.as_slice())?; + + let enclave_account_id = executor.get_enclave_account()?; + let trusted_call = + TrustedCall::balance_shield(enclave_account_id.into(), account, self.amount); + let signed_trusted_call = executor.sign_call_with_self(&trusted_call, &self.shard)?; + let trusted_operation = + TrustedOperation::::indirect_call(signed_trusted_call); + + let encrypted_trusted_call = executor.encrypt(&trusted_operation.encode())?; + executor.submit_trusted_call(self.shard, encrypted_trusted_call); + Ok(()) + } +} diff --git a/bitacross-worker/app-libs/parentchain-interface/src/indirect_calls/transfer_to_alice_shields_funds.rs b/bitacross-worker/app-libs/parentchain-interface/src/indirect_calls/transfer_to_alice_shields_funds.rs new file mode 100644 index 0000000000..7aef56cddd --- /dev/null +++ b/bitacross-worker/app-libs/parentchain-interface/src/indirect_calls/transfer_to_alice_shields_funds.rs @@ -0,0 +1,98 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use codec::{Decode, Encode}; +use core::fmt::Debug; +use ita_stf::{Getter, TrustedCall, TrustedCallSigned}; +use itc_parentchain_indirect_calls_executor::{ + error::{Error, Result}, + IndirectDispatch, +}; +use itp_stf_primitives::{ + traits::IndirectExecutor, + types::{AccountId, TrustedOperation}, +}; +use itp_types::Balance; +use log::info; +use sp_runtime::MultiAddress; +/// Arguments of a parentchains `transfer` or `transfer_allow_death` dispatchable. +/// +/// This is a simple demo indirect call where a transfer to alice on chain will transfer +/// funds to alice on sidechain. +#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq)] +pub struct TransferToAliceShieldsFundsArgs { + // () is just a placeholder for index, which we don't use + pub destination: MultiAddress, + #[codec(compact)] + pub value: Balance, +} + +/// AccountId for `//Alice` because we can't derive the alice account in `no-std` otherwise. +/// +/// The following seed has been obtained by: +/// +/// ``` +/// use sp_core::{sr25519, Pair}; +/// use ita_parentchain_interface::indirect_calls::ALICE_ACCOUNT_ID; +/// let alice = sr25519::Pair::from_string_with_seed("//Alice", None).unwrap(); +/// println!("{:?}", alice.0.public().to_vec()); +/// assert_eq!(ALICE_ACCOUNT_ID, alice.0.public().into()) +/// ``` +pub const ALICE_ACCOUNT_ID: AccountId = AccountId::new([ + 212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, + 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125, +]); + +impl> + IndirectDispatch for TransferToAliceShieldsFundsArgs +{ + type Args = (); + fn dispatch(&self, executor: &Executor, _args: Self::Args) -> Result<()> { + if self.destination == ALICE_ACCOUNT_ID.into() { + info!("Found Transfer to Alice extrinsic in block: \nAmount: {}", self.value); + + let shard = executor.get_default_shard(); + let trusted_call = TrustedCall::balance_shield( + executor.get_enclave_account()?.into(), + ALICE_ACCOUNT_ID, + self.value, + ); + let signed_trusted_call = executor.sign_call_with_self(&trusted_call, &shard)?; + let trusted_operation = + TrustedOperation::::indirect_call(signed_trusted_call); + + let encrypted_trusted_call = executor.encrypt(&trusted_operation.encode())?; + executor.submit_trusted_call(shard, encrypted_trusted_call); + } else { + log::trace!("Transfer on parentchain was not for alice") + } + + Ok(()) + } +} + +#[cfg(test)] +mod test { + use crate::indirect_calls::transfer_to_alice_shields_funds::ALICE_ACCOUNT_ID; + use sp_core::{sr25519, Pair}; + + #[test] + fn alice_account_is_correct() { + let alice = sr25519::Pair::from_string_with_seed("//Alice", None).unwrap(); + assert_eq!(ALICE_ACCOUNT_ID, alice.0.public().into()); + } +} diff --git a/bitacross-worker/app-libs/parentchain-interface/src/integritee/event_filter.rs b/bitacross-worker/app-libs/parentchain-interface/src/integritee/event_filter.rs new file mode 100644 index 0000000000..d403a93948 --- /dev/null +++ b/bitacross-worker/app-libs/parentchain-interface/src/integritee/event_filter.rs @@ -0,0 +1,85 @@ +/* + Copyright 2021 Integritee AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +//! Various way to filter Parentchain events + +use itc_parentchain_indirect_calls_executor::event_filter::ToEvents; +use itp_api_client_types::Events; + +use itp_types::{ + parentchain::{ + BalanceTransfer, ExtrinsicFailed, ExtrinsicStatus, ExtrinsicSuccess, FilterEvents, + }, + H256, +}; +use std::vec::Vec; + +#[derive(Clone)] +pub struct FilterableEvents(pub Events); + +// todo: improve: https://github.com/integritee-network/worker/pull/1378#discussion_r1393933766 +impl ToEvents> for FilterableEvents { + fn to_events(&self) -> &Events { + &self.0 + } +} + +impl From> for FilterableEvents { + fn from(ev: Events) -> Self { + Self(ev) + } +} + +impl FilterEvents for FilterableEvents { + type Error = itc_parentchain_indirect_calls_executor::Error; + + fn get_extrinsic_statuses(&self) -> core::result::Result, Self::Error> { + Ok(self + .to_events() + .iter() + .filter_map(|ev| { + ev.and_then(|ev| { + if (ev.as_event::()?).is_some() { + return Ok(Some(ExtrinsicStatus::Success)) + } + + if (ev.as_event::()?).is_some() { + return Ok(Some(ExtrinsicStatus::Failed)) + } + + Ok(None) + }) + .ok() + .flatten() + }) + .collect()) + } + + fn get_transfer_events(&self) -> core::result::Result, Self::Error> { + Ok(self + .to_events() + .iter() + .flatten() // flatten filters out the nones + .filter_map(|ev| match ev.as_event::() { + Ok(maybe_event) => maybe_event, + Err(e) => { + log::error!("Could not decode event: {:?}", e); + None + }, + }) + .collect()) + } +} diff --git a/bitacross-worker/app-libs/parentchain-interface/src/integritee/event_handler.rs b/bitacross-worker/app-libs/parentchain-interface/src/integritee/event_handler.rs new file mode 100644 index 0000000000..1cc6cd3d0e --- /dev/null +++ b/bitacross-worker/app-libs/parentchain-interface/src/integritee/event_handler.rs @@ -0,0 +1,83 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use codec::Encode; + +pub use ita_sgx_runtime::{Balance, Index}; +use ita_stf::{Getter, TrustedCall, TrustedCallSigned}; +use itc_parentchain_indirect_calls_executor::error::Error; +use itp_stf_primitives::{traits::IndirectExecutor, types::TrustedOperation}; +use itp_types::parentchain::{AccountId, FilterEvents, HandleParentchainEvents, ParentchainError}; +use itp_utils::hex::hex_encode; +use log::*; + +pub struct ParentchainEventHandler {} + +impl ParentchainEventHandler { + fn shield_funds>( + executor: &Executor, + account: &AccountId, + amount: Balance, + ) -> Result<(), Error> { + log::info!("shielding for {:?} amount {}", account, amount,); + let shard = executor.get_default_shard(); + let trusted_call = TrustedCall::balance_shield( + executor.get_enclave_account()?.into(), + account.clone(), + amount, + ); + let signed_trusted_call = executor.sign_call_with_self(&trusted_call, &shard)?; + let trusted_operation = + TrustedOperation::::indirect_call(signed_trusted_call); + + let encrypted_trusted_call = executor.encrypt(&trusted_operation.encode())?; + executor.submit_trusted_call(shard, encrypted_trusted_call); + + Ok(()) + } +} + +impl HandleParentchainEvents + for ParentchainEventHandler +where + Executor: IndirectExecutor, +{ + fn handle_events( + executor: &Executor, + events: impl FilterEvents, + vault_account: &AccountId, + ) -> Result<(), Error> { + let filter_events = events.get_transfer_events(); + trace!( + "filtering transfer events to shard vault account: {}", + hex_encode(vault_account.encode().as_slice()) + ); + if let Ok(events) = filter_events { + events + .iter() + .filter(|&event| event.to == *vault_account) + .try_for_each(|event| { + info!("found transfer_event to vault account: {}", event); + //debug!("shielding from Integritee suppressed"); + Self::shield_funds(executor, &event.from, event.amount) + //Err(ParentchainError::FunctionalityDisabled) + }) + .map_err(|_| ParentchainError::ShieldFundsFailure)?; + } + Ok(()) + } +} diff --git a/bitacross-worker/app-libs/parentchain-interface/src/integritee/extrinsic_parser.rs b/bitacross-worker/app-libs/parentchain-interface/src/integritee/extrinsic_parser.rs new file mode 100644 index 0000000000..8e6520477e --- /dev/null +++ b/bitacross-worker/app-libs/parentchain-interface/src/integritee/extrinsic_parser.rs @@ -0,0 +1,83 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use codec::{Decode, Encode}; +use core::marker::PhantomData; +use itc_parentchain_indirect_calls_executor::hash_of; +use itp_node_api::api_client::{ + Address, CallIndex, PairSignature, ParentchainSignedExtra, Signature, UncheckedExtrinsicV4, +}; +use itp_types::H256; + +pub struct ExtrinsicParser { + _phantom: PhantomData, +} + +/// Parses the extrinsics corresponding to the parentchain. +pub type ParentchainExtrinsicParser = ExtrinsicParser; + +/// Partially interpreted extrinsic containing the `signature` and the `call_index` whereas +/// the `call_args` remain in encoded form. +/// +/// Intended for usage, where the actual `call_args` form is unknown. +pub struct SemiOpaqueExtrinsic<'a, SignedExtra> { + /// Signature of the Extrinsic. + pub signature: Signature, + /// Call index of the dispatchable. + pub call_index: CallIndex, + /// Encoded arguments of the dispatchable corresponding to the `call_index`. + pub call_args: &'a [u8], + /// Hashed Extrinsic + pub hashed_extrinsic: H256, +} + +/// Trait to extract signature and call indexes of an encoded [UncheckedExtrinsicV4]. +pub trait ParseExtrinsic { + /// Signed extra of the extrinsic. + type SignedExtra; + + fn parse(encoded_call: &[u8]) -> Result, codec::Error>; +} + +impl ParseExtrinsic for ExtrinsicParser +where + SignedExtra: Decode + Encode, +{ + type SignedExtra = SignedExtra; + + /// Extract a call index of an encoded call. + fn parse(encoded_call: &[u8]) -> Result, codec::Error> { + let call_mut = &mut &encoded_call[..]; + + // `()` is a trick to stop decoding after the call index. So the remaining bytes + // of `call` after decoding only contain the parentchain's dispatchable's arguments. + let xt = UncheckedExtrinsicV4::< + Address, + (CallIndex, ()), + PairSignature, + Self::SignedExtra, + >::decode(call_mut)?; + let hashed_xt = hash_of(&xt); + + Ok(SemiOpaqueExtrinsic { + signature: xt.signature, + call_index: xt.function.0, + call_args: call_mut, + hashed_extrinsic: hashed_xt, + }) + } +} diff --git a/bitacross-worker/app-libs/parentchain-interface/src/integritee/mod.rs b/bitacross-worker/app-libs/parentchain-interface/src/integritee/mod.rs new file mode 100644 index 0000000000..f27609698c --- /dev/null +++ b/bitacross-worker/app-libs/parentchain-interface/src/integritee/mod.rs @@ -0,0 +1,173 @@ +/* + Copyright 2021 Integritee AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +mod event_filter; +mod event_handler; +mod extrinsic_parser; + +use crate::{ + decode_and_log_error, + indirect_calls::{ + InvokeArgs, RemoveScheduledEnclaveArgs, ShieldFundsArgs, UpdateScheduledEnclaveArgs, + }, + integritee::extrinsic_parser::ParseExtrinsic, +}; +use codec::{Decode, Encode}; +use core::marker::PhantomData; +pub use event_filter::FilterableEvents; +pub use event_handler::ParentchainEventHandler; +pub use extrinsic_parser::ParentchainExtrinsicParser; +use ita_stf::TrustedCallSigned; +use itc_parentchain_indirect_calls_executor::{ + error::{Error, Result}, + filter_metadata::FilterIntoDataFrom, + IndirectDispatch, +}; +use itp_node_api::metadata::NodeMetadataTrait; +use itp_stf_primitives::traits::IndirectExecutor; +use itp_types::CallIndex; +use log::trace; +use sp_std::vec::Vec; + +/// The default indirect call (extrinsic-triggered) of the Integritee-Parachain. +#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq)] +pub enum IndirectCall { + #[codec(index = 0)] + ShieldFunds(ShieldFundsArgs), + #[codec(index = 1)] + Invoke(InvokeArgs), + // Litentry + #[codec(index = 6)] + UpdateScheduledEnclave(UpdateScheduledEnclaveArgs), + #[codec(index = 7)] + RemoveScheduledEnclave(RemoveScheduledEnclaveArgs), + #[codec(index = 8)] + BatchAll(Vec), +} + +impl> + IndirectDispatch for IndirectCall +{ + type Args = (); + fn dispatch(&self, executor: &Executor, _args: Self::Args) -> Result<()> { + trace!("dispatching indirect call {:?}", self); + match self { + IndirectCall::ShieldFunds(shieldfunds_args) => shieldfunds_args.dispatch(executor, ()), + IndirectCall::Invoke(invoke_args) => invoke_args.dispatch(executor, ()), + // Litentry + IndirectCall::UpdateScheduledEnclave(update_enclave_args) => + update_enclave_args.dispatch(executor, ()), + IndirectCall::RemoveScheduledEnclave(remove_enclave_args) => + remove_enclave_args.dispatch(executor, ()), + IndirectCall::BatchAll(calls) => { + for x in calls.clone() { + if let Err(e) = x.dispatch(executor, ()) { + log::warn!("Failed to execute indirect call in batch all due to: {:?}", e); + continue + } + } + Ok(()) + }, + } + } +} + +/// Default filter we use for the Integritee-Parachain. +pub struct ShieldFundsAndInvokeFilter { + _phantom: PhantomData, +} + +impl FilterIntoDataFrom + for ShieldFundsAndInvokeFilter +where + ExtrinsicParser: ParseExtrinsic, +{ + type Output = IndirectCall; + type ParseParentchainMetadata = ExtrinsicParser; + + fn filter_into_from_metadata( + encoded_data: &[u8], + metadata: &NodeMetadata, + ) -> Option { + let call_mut = &mut &encoded_data[..]; + + // Todo: the filter should not need to parse, only filter. This should directly be configured + // in the indirect executor. + let xt = match Self::ParseParentchainMetadata::parse(call_mut) { + Ok(xt) => xt, + Err(e) => { + log::error!( + "[ShieldFundsAndInvokeFilter] Could not parse parentchain extrinsic: {:?}", + e + ); + return None + }, + }; + let index = xt.call_index; + let call_args = &mut &xt.call_args[..]; + log::trace!( + "[ShieldFundsAndInvokeFilter] attempting to execute indirect call with index {:?}", + index + ); + if index == metadata.shield_funds_call_indexes().ok()? { + log::debug!("executing shield funds call"); + let args = decode_and_log_error::(call_args)?; + Some(IndirectCall::ShieldFunds(args)) + } else if index == metadata.invoke_call_indexes().ok()? { + log::debug!("executing invoke call"); + let args = decode_and_log_error::(call_args)?; + Some(IndirectCall::Invoke(args)) + // Litentry + } else if index == metadata.update_scheduled_enclave().ok()? { + let args = decode_and_log_error::(call_args)?; + Some(IndirectCall::UpdateScheduledEnclave(args)) + } else if index == metadata.remove_scheduled_enclave().ok()? { + let args = decode_and_log_error::(call_args)?; + Some(IndirectCall::RemoveScheduledEnclave(args)) + } else if index == metadata.batch_all_call_indexes().ok()? { + parse_batch_all(call_args, metadata) + } else { + None + } + } +} + +fn parse_batch_all( + call_args: &mut &[u8], + metadata: &NodeMetadata, +) -> Option { + let call_count: sp_std::vec::Vec<()> = Decode::decode(call_args).ok()?; + let mut calls: Vec = Vec::new(); + log::debug!("Received BatchAll including {} calls", call_count.len()); + for _i in 0..call_count.len() { + let index: CallIndex = Decode::decode(call_args).ok()?; + if index == metadata.shield_funds_call_indexes().ok()? { + let args = decode_and_log_error::(call_args)?; + calls.push(IndirectCall::ShieldFunds(args)) + } else if index == metadata.invoke_call_indexes().ok()? { + let args = decode_and_log_error::(call_args)?; + calls.push(IndirectCall::Invoke(args)) + } else if index == metadata.update_scheduled_enclave().ok()? { + let args = decode_and_log_error::(call_args)?; + calls.push(IndirectCall::UpdateScheduledEnclave(args)) + } else if index == metadata.remove_scheduled_enclave().ok()? { + let args = decode_and_log_error::(call_args)?; + calls.push(IndirectCall::RemoveScheduledEnclave(args)) + } + } + Some(IndirectCall::BatchAll(calls)) +} diff --git a/bitacross-worker/app-libs/parentchain-interface/src/lib.rs b/bitacross-worker/app-libs/parentchain-interface/src/lib.rs new file mode 100644 index 0000000000..2aa70c6447 --- /dev/null +++ b/bitacross-worker/app-libs/parentchain-interface/src/lib.rs @@ -0,0 +1,39 @@ +/* + Copyright 2021 Integritee AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#![cfg_attr(all(not(target_env = "sgx"), not(feature = "std")), no_std)] +#![cfg_attr(target_env = "sgx", feature(rustc_private))] + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +use codec::Decode; + +pub mod indirect_calls; +pub mod integritee; +pub mod target_a; +pub mod target_b; + +pub fn decode_and_log_error(encoded: &mut &[u8]) -> Option { + match V::decode(encoded) { + Ok(v) => Some(v), + Err(e) => { + log::warn!("Could not decode. {:?}", e); + None + }, + } +} diff --git a/bitacross-worker/app-libs/parentchain-interface/src/target_a/event_filter.rs b/bitacross-worker/app-libs/parentchain-interface/src/target_a/event_filter.rs new file mode 100644 index 0000000000..b3efc37129 --- /dev/null +++ b/bitacross-worker/app-libs/parentchain-interface/src/target_a/event_filter.rs @@ -0,0 +1,89 @@ +/* + Copyright 2021 Integritee AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +//! Various way to filter Parentchain events + +use itc_parentchain_indirect_calls_executor::event_filter::ToEvents; +use itp_api_client_types::Events; + +use itp_types::{ + parentchain::{ + BalanceTransfer, ExtrinsicFailed, ExtrinsicStatus, ExtrinsicSuccess, FilterEvents, + }, + H256, +}; +use std::vec::Vec; + +#[derive(Clone)] +pub struct FilterableEvents(pub Events); + +impl ToEvents> for FilterableEvents { + fn to_events(&self) -> &Events { + &self.0 + } +} + +impl From> for FilterableEvents { + fn from(ev: Events) -> Self { + Self(ev) + } +} + +impl FilterEvents for FilterableEvents { + type Error = itc_parentchain_indirect_calls_executor::Error; + + fn get_extrinsic_statuses(&self) -> core::result::Result, Self::Error> { + Ok(self + .to_events() + .iter() + .filter_map(|ev| { + ev.and_then(|ev| { + if (ev.as_event::()?).is_some() { + return Ok(Some(ExtrinsicStatus::Success)) + } + + if (ev.as_event::()?).is_some() { + return Ok(Some(ExtrinsicStatus::Failed)) + } + + Ok(None) + }) + .ok() + .flatten() + }) + .collect()) + } + + fn get_transfer_events(&self) -> core::result::Result, Self::Error> { + Ok(self + .to_events() + .iter() + .flatten() // flatten filters out the nones + .filter_map(|ev| match ev.as_event::() { + Ok(maybe_event) => { + if maybe_event.is_none() { + log::warn!("Transfer event does not exist in parentchain metadata"); + }; + maybe_event + }, + Err(e) => { + log::error!("Could not decode event: {:?}", e); + None + }, + }) + .collect()) + } +} diff --git a/bitacross-worker/app-libs/parentchain-interface/src/target_a/event_handler.rs b/bitacross-worker/app-libs/parentchain-interface/src/target_a/event_handler.rs new file mode 100644 index 0000000000..7ea752aa55 --- /dev/null +++ b/bitacross-worker/app-libs/parentchain-interface/src/target_a/event_handler.rs @@ -0,0 +1,81 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use codec::Encode; +pub use ita_sgx_runtime::{Balance, Index}; + +use ita_stf::{Getter, TrustedCall, TrustedCallSigned}; +use itc_parentchain_indirect_calls_executor::error::Error; +use itp_stf_primitives::{traits::IndirectExecutor, types::TrustedOperation}; +use itp_types::parentchain::{AccountId, FilterEvents, HandleParentchainEvents, ParentchainError}; +use itp_utils::hex::hex_encode; +use log::*; + +pub struct ParentchainEventHandler {} + +impl ParentchainEventHandler { + fn shield_funds>( + executor: &Executor, + account: &AccountId, + amount: Balance, + ) -> Result<(), Error> { + trace!("[TargetA] shielding for {:?} amount {}", account, amount,); + let shard = executor.get_default_shard(); + let trusted_call = TrustedCall::balance_shield( + executor.get_enclave_account()?.into(), + account.clone(), + amount, + ); + let signed_trusted_call = executor.sign_call_with_self(&trusted_call, &shard)?; + let trusted_operation = + TrustedOperation::::indirect_call(signed_trusted_call); + + let encrypted_trusted_call = executor.encrypt(&trusted_operation.encode())?; + executor.submit_trusted_call(shard, encrypted_trusted_call); + + Ok(()) + } +} + +impl HandleParentchainEvents + for ParentchainEventHandler +where + Executor: IndirectExecutor, +{ + fn handle_events( + executor: &Executor, + events: impl FilterEvents, + vault_account: &AccountId, + ) -> Result<(), Error> { + let filter_events = events.get_transfer_events(); + trace!( + "[TargetA] filtering transfer events to shard vault account: {}", + hex_encode(vault_account.encode().as_slice()) + ); + if let Ok(events) = filter_events { + events + .iter() + .filter(|&event| event.to == *vault_account) + .try_for_each(|event| { + std::println!("⣿TargetA⣿ 🛡 found transfer event to shard vault account: {} will shield to {}", event.amount, hex_encode(event.from.encode().as_ref())); + Self::shield_funds(executor, &event.from, event.amount) + }) + .map_err(|_| ParentchainError::ShieldFundsFailure)?; + } + Ok(()) + } +} diff --git a/bitacross-worker/app-libs/parentchain-interface/src/target_a/extrinsic_parser.rs b/bitacross-worker/app-libs/parentchain-interface/src/target_a/extrinsic_parser.rs new file mode 100644 index 0000000000..925aca30ee --- /dev/null +++ b/bitacross-worker/app-libs/parentchain-interface/src/target_a/extrinsic_parser.rs @@ -0,0 +1,77 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use codec::{Decode, Encode}; +use core::marker::PhantomData; +use itp_node_api::api_client::{ + Address, CallIndex, PairSignature, ParentchainSignedExtra, Signature, UncheckedExtrinsicV4, +}; + +pub struct ExtrinsicParser { + _phantom: PhantomData, +} + +/// Parses the extrinsics corresponding to the parentchain. +pub type ParentchainExtrinsicParser = ExtrinsicParser; + +/// Partially interpreted extrinsic containing the `signature` and the `call_index` whereas +/// the `call_args` remain in encoded form. +/// +/// Intended for usage, where the actual `call_args` form is unknown. +pub struct SemiOpaqueExtrinsic<'a, SignedExtra> { + /// Signature of the Extrinsic. + pub signature: Signature, + /// Call index of the dispatchable. + pub call_index: CallIndex, + /// Encoded arguments of the dispatchable corresponding to the `call_index`. + pub call_args: &'a [u8], +} + +/// Trait to extract signature and call indexes of an encoded [UncheckedExtrinsicV4]. +pub trait ParseExtrinsic { + /// Signed extra of the extrinsic. + type SignedExtra; + + fn parse(encoded_call: &[u8]) -> Result, codec::Error>; +} + +impl ParseExtrinsic for ExtrinsicParser +where + SignedExtra: Decode + Encode, +{ + type SignedExtra = SignedExtra; + + /// Extract a call index of an encoded call. + fn parse(encoded_call: &[u8]) -> Result, codec::Error> { + let call_mut = &mut &encoded_call[..]; + + // `()` is a trick to stop decoding after the call index. So the remaining bytes + // of `call` after decoding only contain the parentchain's dispatchable's arguments. + let xt = UncheckedExtrinsicV4::< + Address, + (CallIndex, ()), + PairSignature, + Self::SignedExtra, + >::decode(call_mut)?; + + Ok(SemiOpaqueExtrinsic { + signature: xt.signature, + call_index: xt.function.0, + call_args: call_mut, + }) + } +} diff --git a/bitacross-worker/app-libs/parentchain-interface/src/target_a/mod.rs b/bitacross-worker/app-libs/parentchain-interface/src/target_a/mod.rs new file mode 100644 index 0000000000..56a7be3927 --- /dev/null +++ b/bitacross-worker/app-libs/parentchain-interface/src/target_a/mod.rs @@ -0,0 +1,116 @@ +/* + Copyright 2021 Integritee AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +mod event_filter; +mod event_handler; +mod extrinsic_parser; +use crate::{ + decode_and_log_error, + indirect_calls::{ + transfer_to_alice_shields_funds::TransferToAliceShieldsFundsArgs, ALICE_ACCOUNT_ID, + }, +}; +use codec::{Decode, Encode}; +use core::marker::PhantomData; +pub use event_filter::FilterableEvents; +pub use event_handler::ParentchainEventHandler; +pub use extrinsic_parser::ParentchainExtrinsicParser; +use extrinsic_parser::ParseExtrinsic; +use ita_stf::TrustedCallSigned; +use itc_parentchain_indirect_calls_executor::{ + error::{Error, Result}, + filter_metadata::FilterIntoDataFrom, + IndirectDispatch, +}; +use itp_node_api::metadata::pallet_balances::BalancesCallIndexes; +use itp_stf_primitives::traits::IndirectExecutor; +use log::{debug, trace}; + +/// The default indirect call (extrinsic-triggered) of the Target-A-Parachain. +#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq)] +pub enum IndirectCall { + TransferToAliceShieldsFunds(TransferToAliceShieldsFundsArgs), +} + +impl> + IndirectDispatch for IndirectCall +{ + type Args = (); + fn dispatch(&self, _executor: &Executor, _args: Self::Args) -> Result<()> { + debug!("shielding from TargetA extrinsic to Alice suppressed"); + /* + trace!("dispatching indirect call {:?}", self); + match self { + IndirectCall::TransferToAliceShieldsFunds(args) => args.dispatch(executor, ()), + } + + */ + Ok(()) + } +} + +/// Simple demo filter for testing. +/// +/// A transfer to Alice will issue the corresponding balance to Alice in the enclave. +/// It does not do anything else. +pub struct TransferToAliceShieldsFundsFilter { + _phantom: PhantomData, +} + +impl FilterIntoDataFrom + for TransferToAliceShieldsFundsFilter +where + ExtrinsicParser: ParseExtrinsic, +{ + type Output = IndirectCall; + type ParseParentchainMetadata = ExtrinsicParser; + + fn filter_into_from_metadata( + encoded_data: &[u8], + metadata: &NodeMetadata, + ) -> Option { + let call_mut = &mut &encoded_data[..]; + + // Todo: the filter should not need to parse, only filter. This should directly be configured + // in the indirect executor. + let xt = match Self::ParseParentchainMetadata::parse(call_mut) { + Ok(xt) => xt, + Err(e) => { + log::error!("[TransferToAliceShieldsFundsFilter] Could not parse parentchain extrinsic: {:?}", e); + return None + }, + }; + let index = xt.call_index; + let call_args = &mut &xt.call_args[..]; + trace!("[TransferToAliceShieldsFundsFilter] attempting to execute indirect call with index {:?}", index); + if index == metadata.transfer_call_indexes().ok()? + || index == metadata.transfer_keep_alive_call_indexes().ok()? + || index == metadata.transfer_allow_death_call_indexes().ok()? + { + debug!("found `transfer` or `transfer_allow_death` or `transfer_keep_alive` call."); + let args = decode_and_log_error::(call_args)?; + if args.destination == ALICE_ACCOUNT_ID.into() { + Some(IndirectCall::TransferToAliceShieldsFunds(args)) + } else { + debug!("Parentchain transfer extrinsic was not for Alice; ignoring..."); + // No need to put it into the top pool if it isn't executed in the first place. + None + } + } else { + None + } + } +} diff --git a/bitacross-worker/app-libs/parentchain-interface/src/target_b/event_filter.rs b/bitacross-worker/app-libs/parentchain-interface/src/target_b/event_filter.rs new file mode 100644 index 0000000000..b3efc37129 --- /dev/null +++ b/bitacross-worker/app-libs/parentchain-interface/src/target_b/event_filter.rs @@ -0,0 +1,89 @@ +/* + Copyright 2021 Integritee AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +//! Various way to filter Parentchain events + +use itc_parentchain_indirect_calls_executor::event_filter::ToEvents; +use itp_api_client_types::Events; + +use itp_types::{ + parentchain::{ + BalanceTransfer, ExtrinsicFailed, ExtrinsicStatus, ExtrinsicSuccess, FilterEvents, + }, + H256, +}; +use std::vec::Vec; + +#[derive(Clone)] +pub struct FilterableEvents(pub Events); + +impl ToEvents> for FilterableEvents { + fn to_events(&self) -> &Events { + &self.0 + } +} + +impl From> for FilterableEvents { + fn from(ev: Events) -> Self { + Self(ev) + } +} + +impl FilterEvents for FilterableEvents { + type Error = itc_parentchain_indirect_calls_executor::Error; + + fn get_extrinsic_statuses(&self) -> core::result::Result, Self::Error> { + Ok(self + .to_events() + .iter() + .filter_map(|ev| { + ev.and_then(|ev| { + if (ev.as_event::()?).is_some() { + return Ok(Some(ExtrinsicStatus::Success)) + } + + if (ev.as_event::()?).is_some() { + return Ok(Some(ExtrinsicStatus::Failed)) + } + + Ok(None) + }) + .ok() + .flatten() + }) + .collect()) + } + + fn get_transfer_events(&self) -> core::result::Result, Self::Error> { + Ok(self + .to_events() + .iter() + .flatten() // flatten filters out the nones + .filter_map(|ev| match ev.as_event::() { + Ok(maybe_event) => { + if maybe_event.is_none() { + log::warn!("Transfer event does not exist in parentchain metadata"); + }; + maybe_event + }, + Err(e) => { + log::error!("Could not decode event: {:?}", e); + None + }, + }) + .collect()) + } +} diff --git a/bitacross-worker/app-libs/parentchain-interface/src/target_b/event_handler.rs b/bitacross-worker/app-libs/parentchain-interface/src/target_b/event_handler.rs new file mode 100644 index 0000000000..39a5555973 --- /dev/null +++ b/bitacross-worker/app-libs/parentchain-interface/src/target_b/event_handler.rs @@ -0,0 +1,41 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pub use ita_sgx_runtime::{Balance, Index}; + +use ita_stf::TrustedCallSigned; +use itc_parentchain_indirect_calls_executor::error::Error; +use itp_stf_primitives::traits::IndirectExecutor; +use itp_types::parentchain::{AccountId, FilterEvents, HandleParentchainEvents}; +use log::*; + +pub struct ParentchainEventHandler {} + +impl HandleParentchainEvents + for ParentchainEventHandler +where + Executor: IndirectExecutor, +{ + fn handle_events( + _executor: &Executor, + _events: impl FilterEvents, + _vault_account: &AccountId, + ) -> Result<(), Error> { + debug!("not handling any events for target B"); + Ok(()) + } +} diff --git a/bitacross-worker/app-libs/parentchain-interface/src/target_b/extrinsic_parser.rs b/bitacross-worker/app-libs/parentchain-interface/src/target_b/extrinsic_parser.rs new file mode 100644 index 0000000000..925aca30ee --- /dev/null +++ b/bitacross-worker/app-libs/parentchain-interface/src/target_b/extrinsic_parser.rs @@ -0,0 +1,77 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use codec::{Decode, Encode}; +use core::marker::PhantomData; +use itp_node_api::api_client::{ + Address, CallIndex, PairSignature, ParentchainSignedExtra, Signature, UncheckedExtrinsicV4, +}; + +pub struct ExtrinsicParser { + _phantom: PhantomData, +} + +/// Parses the extrinsics corresponding to the parentchain. +pub type ParentchainExtrinsicParser = ExtrinsicParser; + +/// Partially interpreted extrinsic containing the `signature` and the `call_index` whereas +/// the `call_args` remain in encoded form. +/// +/// Intended for usage, where the actual `call_args` form is unknown. +pub struct SemiOpaqueExtrinsic<'a, SignedExtra> { + /// Signature of the Extrinsic. + pub signature: Signature, + /// Call index of the dispatchable. + pub call_index: CallIndex, + /// Encoded arguments of the dispatchable corresponding to the `call_index`. + pub call_args: &'a [u8], +} + +/// Trait to extract signature and call indexes of an encoded [UncheckedExtrinsicV4]. +pub trait ParseExtrinsic { + /// Signed extra of the extrinsic. + type SignedExtra; + + fn parse(encoded_call: &[u8]) -> Result, codec::Error>; +} + +impl ParseExtrinsic for ExtrinsicParser +where + SignedExtra: Decode + Encode, +{ + type SignedExtra = SignedExtra; + + /// Extract a call index of an encoded call. + fn parse(encoded_call: &[u8]) -> Result, codec::Error> { + let call_mut = &mut &encoded_call[..]; + + // `()` is a trick to stop decoding after the call index. So the remaining bytes + // of `call` after decoding only contain the parentchain's dispatchable's arguments. + let xt = UncheckedExtrinsicV4::< + Address, + (CallIndex, ()), + PairSignature, + Self::SignedExtra, + >::decode(call_mut)?; + + Ok(SemiOpaqueExtrinsic { + signature: xt.signature, + call_index: xt.function.0, + call_args: call_mut, + }) + } +} diff --git a/bitacross-worker/app-libs/parentchain-interface/src/target_b/mod.rs b/bitacross-worker/app-libs/parentchain-interface/src/target_b/mod.rs new file mode 100644 index 0000000000..f0e81c4c54 --- /dev/null +++ b/bitacross-worker/app-libs/parentchain-interface/src/target_b/mod.rs @@ -0,0 +1,86 @@ +/* + Copyright 2021 Integritee AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +/* + Copyright 2021 Integritee AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +mod event_filter; +mod event_handler; +mod extrinsic_parser; + +use codec::{Decode, Encode}; +use core::marker::PhantomData; +pub use event_filter::FilterableEvents; +pub use event_handler::ParentchainEventHandler; +pub use extrinsic_parser::ParentchainExtrinsicParser; +use extrinsic_parser::ParseExtrinsic; +use ita_stf::TrustedCallSigned; +use itc_parentchain_indirect_calls_executor::{ + error::{Error, Result}, + filter_metadata::FilterIntoDataFrom, + IndirectDispatch, +}; +use itp_node_api::metadata::pallet_balances::BalancesCallIndexes; +use itp_stf_primitives::traits::IndirectExecutor; +use log::error; + +/// The default indirect call (extrinsic-triggered) of the Target-A-Parachain. +#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq)] +pub enum IndirectCall {} + +impl> + IndirectDispatch for IndirectCall +{ + type Args = (); + fn dispatch(&self, _executor: &Executor, _args: Self::Args) -> Result<()> { + Err(Error::Other("no indirect calls defined for target_b".into())) + } +} + +pub struct TargetBExtrinsicFilter { + _phantom: PhantomData, +} + +impl FilterIntoDataFrom + for TargetBExtrinsicFilter +where + ExtrinsicParser: ParseExtrinsic, +{ + type Output = IndirectCall; + type ParseParentchainMetadata = ExtrinsicParser; + + fn filter_into_from_metadata( + _encoded_data: &[u8], + _metadata: &NodeMetadata, + ) -> Option { + error!("no indirect calls filter has been implemented for target_b"); + None + } +} diff --git a/bitacross-worker/app-libs/sgx-runtime/Cargo.toml b/bitacross-worker/app-libs/sgx-runtime/Cargo.toml new file mode 100644 index 0000000000..7b94576e4c --- /dev/null +++ b/bitacross-worker/app-libs/sgx-runtime/Cargo.toml @@ -0,0 +1,70 @@ +[package] +name = "ita-sgx-runtime" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } + +# local dependencies +itp-sgx-runtime-primitives = { path = "../../core-primitives/sgx-runtime-primitives", default-features = false } + +# Substrate dependencies +frame-executive = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +frame-system = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +pallet-balances = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +pallet-sudo = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +pallet-timestamp = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +pallet-transaction-payment = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-api = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-core = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-std = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-version = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# Integritee dependencies +pallet-evm = { default-features = false, optional = true, git = "https://github.com/integritee-network/frontier.git", branch = "bar/polkadot-v0.9.42" } + +pallet-parentchain = { path = "../../../pallets/parentchain", default-features = false } + +[features] +default = ["std"] +# Compile the sgx-runtime with evm-pallet support in `no_std`. +evm = ["pallet-evm"] +# Compile the sgx-runtime with evm-pallet support in `std`. +evm_std = [ + "evm", # Activate the `feature = evm` for the compiler flags. + "std", + "pallet-evm/std", +] +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-timestamp/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +std = [ + "codec/std", + "scale-info/std", + "itp-sgx-runtime-primitives/std", + "frame-executive/std", + "frame-support/std", + "frame-system/std", + "pallet-balances/std", + "pallet-sudo/std", + "pallet-timestamp/std", + "pallet-transaction-payment/std", + "pallet-parentchain/std", + "sp-api/std", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", + "sp-version/std", +] diff --git a/bitacross-worker/app-libs/sgx-runtime/src/evm.rs b/bitacross-worker/app-libs/sgx-runtime/src/evm.rs new file mode 100644 index 0000000000..990fdd1492 --- /dev/null +++ b/bitacross-worker/app-libs/sgx-runtime/src/evm.rs @@ -0,0 +1,91 @@ +//! Adds the `pallet-evm` support for the `sgx-runtime. + +// Import types from the crate root including the ones generated by the `construct_runtime!` macro. +use crate::{Balances, Runtime, RuntimeEvent, Timestamp, NORMAL_DISPATCH_RATIO}; +use frame_support::{ + pallet_prelude::Weight, parameter_types, weights::constants::WEIGHT_REF_TIME_PER_SECOND, +}; +use sp_core::{H160, U256}; +use sp_runtime::traits::BlakeTwo256; + +pub use pallet_evm::{ + AddressMapping, Call as EvmCall, EnsureAddressTruncated, FeeCalculator, GasWeightMapping, + HashedAddressMapping as GenericHashedAddressMapping, SubstrateBlockHashMapping, +}; + +pub type HashedAddressMapping = GenericHashedAddressMapping; + +/// Maximum weight per block +pub const MAXIMUM_BLOCK_WEIGHT: Weight = + Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_div(2), u64::MAX); + +// FIXME: For now just a random value. +pub struct FixedGasPrice; +impl FeeCalculator for FixedGasPrice { + fn min_gas_price() -> (U256, Weight) { + (1.into(), Weight::from_parts(1, 0u64)) + } +} + +/// Current approximation of the gas/s consumption considering +/// EVM execution over compiled WASM (on 4.4Ghz CPU). +/// Given the 500ms Weight, from which 75% only are used for transactions, +/// the total EVM execution gas limit is: GAS_PER_SECOND * 0.500 * 0.75 ~= 15_000_000. +pub const GAS_PER_SECOND: u64 = 40_000_000; + +/// Approximate ratio of the amount of Weight per Gas. +/// u64 works for approximations because Weight is a very small unit compared to gas. +pub const WEIGHT_PER_GAS: u64 = WEIGHT_REF_TIME_PER_SECOND / GAS_PER_SECOND; + +pub struct FixedGasWeightMapping; + +impl GasWeightMapping for FixedGasWeightMapping { + fn gas_to_weight(gas: u64, _without_base_weight: bool) -> Weight { + Weight::from_parts(gas.saturating_mul(WEIGHT_PER_GAS), 0u64) + } + fn weight_to_gas(weight: Weight) -> u64 { + weight.ref_time().wrapping_div(WEIGHT_PER_GAS) + } +} + +/// An ipmlementation of Frontier's AddressMapping trait for Sgx Accounts. +/// This is basically identical to Frontier's own IdentityAddressMapping, but it works for any type +/// that is Into like AccountId20 for example. +pub struct IntoAddressMapping; + +impl> AddressMapping for IntoAddressMapping { + fn into_account_id(address: H160) -> T { + address.into() + } +} + +parameter_types! { + pub const ChainId: u64 = 42; + pub BlockGasLimit: U256 = U256::from(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT.ref_time() / WEIGHT_PER_GAS); + pub const GasLimitPovSizeRatio: u64 = 150_000_000 / (5 * 1024 * 1024); + //pub PrecompilesValue: FrontierPrecompiles = FrontierPrecompiles::<_>::new(); +} + +impl pallet_evm::Config for Runtime { + type FeeCalculator = FixedGasPrice; + type GasWeightMapping = FixedGasWeightMapping; + type BlockHashMapping = SubstrateBlockHashMapping; + type CallOrigin = EnsureAddressTruncated; + type WithdrawOrigin = EnsureAddressTruncated; + type AddressMapping = HashedAddressMapping; + type Currency = Balances; + type RuntimeEvent = RuntimeEvent; + type Runner = pallet_evm::runner::stack::Runner; + type PrecompilesType = (); + type PrecompilesValue = (); + type ChainId = ChainId; + type OnChargeTransaction = (); + type BlockGasLimit = BlockGasLimit; + type FindAuthor = (); // Currently not available. Would need some more thoughts how prioritisation fees could be handled. + // BlockGasLimit / MAX_POV_SIZE + // type GasLimitPovSizeRatio = GasLimitPovSizeRatio; + type WeightPerGas = (); + type OnCreate = (); + type Timestamp = Timestamp; + type WeightInfo = (); +} diff --git a/bitacross-worker/app-libs/sgx-runtime/src/lib.rs b/bitacross-worker/app-libs/sgx-runtime/src/lib.rs new file mode 100644 index 0000000000..0a653c3ab1 --- /dev/null +++ b/bitacross-worker/app-libs/sgx-runtime/src/lib.rs @@ -0,0 +1,335 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +//! The Substrate Node Template sgx-runtime for SGX. +//! This is only meant to be used inside an SGX enclave with `#[no_std]` +//! +//! you should assemble your sgx-runtime to be used with your STF here +//! and get all your needed pallets in + +#![cfg_attr(not(feature = "std"), no_std)] +#![feature(prelude_import)] +#![feature(structural_match)] +#![feature(core_intrinsics)] +#![feature(derive_eq)] +// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. +#![recursion_limit = "256"] + +#[cfg(feature = "evm")] +mod evm; +#[cfg(feature = "evm")] +pub use evm::{ + AddressMapping, EnsureAddressTruncated, EvmCall, FeeCalculator, FixedGasPrice, + FixedGasWeightMapping, GasWeightMapping, HashedAddressMapping, IntoAddressMapping, + SubstrateBlockHashMapping, GAS_PER_SECOND, MAXIMUM_BLOCK_WEIGHT, WEIGHT_PER_GAS, +}; + +use core::convert::{TryFrom, TryInto}; +use frame_support::{traits::ConstU32, weights::ConstantMultiplier}; +use pallet_transaction_payment::CurrencyAdapter; +use sp_api::impl_runtime_apis; +use sp_core::OpaqueMetadata; +use sp_runtime::{ + create_runtime_str, generic, + traits::{AccountIdLookup, BlakeTwo256, Block as BlockT}, +}; +use sp_std::prelude::*; +use sp_version::RuntimeVersion; + +// Re-exports from itp-sgx-runtime-primitives. +pub use itp_sgx_runtime_primitives::{ + constants::SLOT_DURATION, + types::{ + AccountData, AccountId, Address, Balance, BlockNumber, ConvertAccountId, Hash, Header, + Index, SgxParentchainTypeConverter, Signature, + }, +}; + +// A few exports that help ease life for downstream crates. +pub use frame_support::{ + construct_runtime, parameter_types, + traits::{KeyOwnerProofSystem, Randomness}, + weights::{ + constants::{ + BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND, + }, + IdentityFee, Weight, + }, + StorageValue, +}; +pub use pallet_balances::Call as BalancesCall; +pub use pallet_parentchain::Call as ParentchainPalletCall; +pub use pallet_timestamp::Call as TimestampCall; +#[cfg(any(feature = "std", test))] +pub use sp_runtime::BuildStorage; +pub use sp_runtime::{Perbill, Permill}; + +/// Block type as expected by this sgx-runtime. +pub type Block = generic::Block; +/// A Block signed with a Justification +pub type SignedBlock = generic::SignedBlock; +/// BlockId type as expected by this sgx-runtime. +pub type BlockId = generic::BlockId; + +/// The SignedExtension to the basic transaction logic. +pub type SignedExtra = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckEra, + frame_system::CheckNonce, + frame_system::CheckWeight, + pallet_transaction_payment::ChargeTransactionPayment, +); +/// Unchecked extrinsic type as expected by this sgx-runtime. +pub type UncheckedExtrinsic = + generic::UncheckedExtrinsic; +/// Extrinsic type that has already been checked. +pub type CheckedExtrinsic = generic::CheckedExtrinsic; + +/// Executive: handles dispatch to the various modules. +pub type Executive = frame_executive::Executive< + Runtime, + Block, + frame_system::ChainContext, + Runtime, + AllPalletsWithSystem, +>; + +/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know +/// the specifics of the sgx-runtime. They can then be made to be agnostic over specific formats +/// of data like extrinsics, allowing for them to continue syncing the network through upgrades +/// to even the core data structures. +pub mod opaque { + + use sp_runtime::generic; + pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; + + /// Opaque block header type. + pub type Header = itp_sgx_runtime_primitives::types::Header; + /// Opaque block type. + pub type Block = super::Block; + /// Opaque block identifier type. + pub type BlockId = generic::BlockId; +} + +pub const VERSION: RuntimeVersion = RuntimeVersion { + spec_name: create_runtime_str!("node-template"), + impl_name: create_runtime_str!("node-template"), + authoring_version: 1, + spec_version: 102, + impl_version: 1, + apis: RUNTIME_API_VERSIONS, + transaction_version: 1, + state_version: 0, +}; + +const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); + +parameter_types! { + pub const Version: RuntimeVersion = VERSION; + pub const BlockHashCount: BlockNumber = 2400; + /// We allow for 2 seconds of compute with a 6 second average block time. + pub BlockWeights: frame_system::limits::BlockWeights = frame_system::limits::BlockWeights + ::with_sensible_defaults(Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND, u64::MAX), NORMAL_DISPATCH_RATIO); + pub BlockLength: frame_system::limits::BlockLength = frame_system::limits::BlockLength + ::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); + pub const SS58Prefix: u8 = 42; +} + +// Configure FRAME pallets to include in sgx-runtime. + +impl frame_system::Config for Runtime { + /// The basic call filter to use in dispatchable. + type BaseCallFilter = frame_support::traits::Everything; + /// Block & extrinsics weights: base values and limits. + type BlockWeights = BlockWeights; + /// The maximum length of a block (in bytes). + type BlockLength = BlockLength; + /// The identifier used to distinguish between accounts. + type AccountId = AccountId; + /// The aggregated dispatch type that is available for extrinsics. + type RuntimeCall = RuntimeCall; + /// The lookup mechanism to get account ID from whatever is passed in dispatchers. + type Lookup = AccountIdLookup; + /// The index type for storing how many extrinsics an account has signed. + type Index = Index; + /// The index type for blocks. + type BlockNumber = BlockNumber; + /// The type for hashing blocks and tries. + type Hash = Hash; + /// The hashing algorithm used. + type Hashing = BlakeTwo256; + /// The header type. + type Header = Header; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + /// The ubiquitous origin type. + type RuntimeOrigin = RuntimeOrigin; + /// Maximum number of block number to block hash mappings to keep (oldest pruned first). + type BlockHashCount = BlockHashCount; + /// The weight of database operations that the sgx-runtime can invoke. + type DbWeight = RocksDbWeight; + /// Version of the sgx-runtime. + type Version = Version; + /// Converts a module to the index of the module in `construct_runtime!`. + /// + /// This type is being generated by `construct_runtime!`. + type PalletInfo = PalletInfo; + /// What to do if a new account is created. + type OnNewAccount = (); + /// What to do if an account is fully reaped from the system. + type OnKilledAccount = (); + /// The data to be stored in an account. + type AccountData = AccountData; + /// Weight information for the extrinsics of this pallet. + type SystemWeightInfo = (); + /// This is used as an identifier of the chain. 42 is the generic substrate prefix. + type SS58Prefix = SS58Prefix; + /// The set code logic, just the default since we're not a parachain. + type OnSetCode = (); + /// The maximum number of consumers allowed on a single account. + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +parameter_types! { + pub const MinimumPeriod: u64 = SLOT_DURATION / 2; +} + +impl pallet_timestamp::Config for Runtime { + /// A timestamp: milliseconds since the unix epoch. + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; + type WeightInfo = (); +} + +parameter_types! { + pub const ExistentialDeposit: u128 = 500; + pub const MaxLocks: u32 = 50; +} + +impl pallet_balances::Config for Runtime { + type MaxLocks = MaxLocks; + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + /// The type for recording an account's balance. + type Balance = Balance; + /// The ubiquitous event type. + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type HoldIdentifier = (); + type FreezeIdentifier = (); + type MaxHolds = ConstU32<0>; + type MaxFreezes = ConstU32<0>; +} + +parameter_types! { + pub const TransactionByteFee: Balance = 1; + pub const OperationalFeeMultiplier: u8 = 5; +} + +impl pallet_transaction_payment::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OnChargeTransaction = CurrencyAdapter; + type OperationalFeeMultiplier = OperationalFeeMultiplier; + type WeightToFee = IdentityFee; + type LengthToFee = ConstantMultiplier; + type FeeMultiplierUpdate = (); +} + +impl pallet_sudo::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; +} + +impl pallet_parentchain::Config for Runtime { + type WeightInfo = (); +} + +// The plain sgx-runtime without the `evm-pallet` +#[cfg(not(feature = "evm"))] +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = opaque::Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, + Sudo: pallet_sudo::{Pallet, Call, Config, Storage, Event}, + Parentchain: pallet_parentchain::{Pallet, Call, Storage}, + } +); + +// Runtime constructed with the evm pallet. +// +// We need add the compiler-flag for the whole macro because it does not support +// compiler flags withing the macro. +#[cfg(feature = "evm")] +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = opaque::Block, + UncheckedExtrinsic = UncheckedExtrinsic + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, + Sudo: pallet_sudo::{Pallet, Call, Config, Storage, Event}, + Parentchain: pallet_parentchain::{Pallet, Call, Storage}, + + Evm: pallet_evm::{Pallet, Call, Storage, Config, Event}, + } +); + +impl_runtime_apis! { + impl sp_api::Core for Runtime { + fn version() -> RuntimeVersion { + VERSION + } + + fn execute_block(block: Block) { + Executive::execute_block(block); + } + + fn initialize_block(header: &::Header) { + Executive::initialize_block(header) + } + } + + impl sp_api::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + OpaqueMetadata::new(Runtime::metadata().into()) + } + + fn metadata_at_version(version: u32) -> Option { + Runtime::metadata_at_version(version) + } + + fn metadata_versions() -> sp_std::vec::Vec { + Runtime::metadata_versions() + } + } + +} diff --git a/bitacross-worker/app-libs/stf/Cargo.toml b/bitacross-worker/app-libs/stf/Cargo.toml new file mode 100644 index 0000000000..ddec15630a --- /dev/null +++ b/bitacross-worker/app-libs/stf/Cargo.toml @@ -0,0 +1,89 @@ +[package] +name = "ita-stf" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +# crates.io +codec = { version = "3.0.0", default-features = false, features = ["derive"], package = "parity-scale-codec" } +hex = { version = "0.4", default-features = false } +hex-literal = { version = "0.4" } +log = { version = "0.4", default-features = false } +rlp = { version = "0.5", default-features = false } +sha3 = { version = "0.10", default-features = false } + +# sgx deps +sgx_tstd = { branch = "master", features = ["untrusted_fs", "net", "backtrace"], git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } + +# local crates +ita-sgx-runtime = { default-features = false, path = "../sgx-runtime" } +itp-hashing = { default-features = false, path = "../../core-primitives/hashing" } +itp-node-api = { default-features = false, path = "../../core-primitives/node-api" } +itp-node-api-metadata = { default-features = false, path = "../../core-primitives/node-api/metadata" } +itp-sgx-externalities = { default-features = false, path = "../../core-primitives/substrate-sgx/externalities" } +itp-stf-interface = { default-features = false, path = "../../core-primitives/stf-interface" } +itp-stf-primitives = { default-features = false, path = "../../core-primitives/stf-primitives" } +itp-storage = { default-features = false, path = "../../core-primitives/storage" } +itp-types = { default-features = false, path = "../../core-primitives/types" } +itp-utils = { default-features = false, path = "../../core-primitives/utils" } +sp-io = { default-features = false, features = ["disable_oom", "disable_panic_handler", "disable_allocator"], path = "../../core-primitives/substrate-sgx/sp-io" } + +# Substrate dependencies +frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +frame-system = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +pallet-balances = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +pallet-sudo = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-core = { default-features = false, features = ["full_crypto"], git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-std = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# litentry +itp-node-api-metadata-provider = { path = "../../core-primitives/node-api/metadata-provider", default-features = false } +litentry-primitives = { path = "../../litentry/primitives", default-features = false } +pallet-parentchain = { path = "../../../pallets/parentchain", default-features = false } + +[dev-dependencies] +sp-keyring = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +[features] +default = ["std"] +evm = ["ita-sgx-runtime/evm"] +evm_std = ["evm", "ita-sgx-runtime/evm_std"] +sgx = [ + "sgx_tstd", + "itp-sgx-externalities/sgx", + "sp-io/sgx", + "itp-node-api/sgx", + # litentry + "litentry-primitives/sgx", + "itp-node-api-metadata-provider/sgx", +] +std = [ + # crates.io + "codec/std", + "log/std", + # local + "ita-sgx-runtime/std", + "itp-hashing/std", + "itp-sgx-externalities/std", + "itp-stf-interface/std", + "itp-storage/std", + "itp-types/std", + "itp-node-api/std", + "itp-node-api-metadata/std", + # substrate + "sp-core/std", + "pallet-balances/std", + "pallet-sudo/std", + "frame-system/std", + "frame-support/std", + "sp-runtime/std", + # scs/integritee + "pallet-parentchain/std", + "sp-io/std", + # litentry + "litentry-primitives/std", + "itp-node-api-metadata-provider/std", +] +test = [] diff --git a/bitacross-worker/app-libs/stf/src/evm_helpers.rs b/bitacross-worker/app-libs/stf/src/evm_helpers.rs new file mode 100644 index 0000000000..f0e96d1f87 --- /dev/null +++ b/bitacross-worker/app-libs/stf/src/evm_helpers.rs @@ -0,0 +1,66 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +use crate::helpers::{get_storage_double_map, get_storage_map}; +use itp_stf_primitives::types::Nonce; +use itp_storage::StorageHasher; +use itp_types::AccountId; +use sha3::{Digest, Keccak256}; +use sp_core::{H160, H256}; +use std::prelude::v1::*; + +pub fn get_evm_account_codes(evm_account: &H160) -> Option> { + get_storage_map("Evm", "AccountCodes", evm_account, &StorageHasher::Blake2_128Concat) +} + +pub fn get_evm_account_storages(evm_account: &H160, index: &H256) -> Option { + get_storage_double_map( + "Evm", + "AccountStorages", + evm_account, + &StorageHasher::Blake2_128Concat, + index, + &StorageHasher::Blake2_128Concat, + ) +} + +// FIXME: Once events are available, these addresses should be read from events. +pub fn evm_create_address(caller: H160, nonce: Nonce) -> H160 { + let mut stream = rlp::RlpStream::new_list(2); + stream.append(&caller); + stream.append(&nonce); + H256::from_slice(Keccak256::digest(&stream.out()).as_slice()).into() +} + +// FIXME: Once events are available, these addresses should be read from events. +pub fn evm_create2_address(caller: H160, salt: H256, code_hash: H256) -> H160 { + let mut hasher = Keccak256::new(); + hasher.update([0xff]); + hasher.update(&caller[..]); + hasher.update(&salt[..]); + hasher.update(&code_hash[..]); + H256::from_slice(hasher.finalize().as_slice()).into() +} + +pub fn create_code_hash(code: &[u8]) -> H256 { + H256::from_slice(Keccak256::digest(code).as_slice()) +} + +pub fn get_evm_account(account: &AccountId) -> H160 { + let mut evm_acc_slice: [u8; 20] = [0; 20]; + evm_acc_slice.copy_from_slice((<[u8; 32]>::from(account.clone())).get(0..20).unwrap()); + evm_acc_slice.into() +} diff --git a/bitacross-worker/app-libs/stf/src/getter.rs b/bitacross-worker/app-libs/stf/src/getter.rs new file mode 100644 index 0000000000..de8d466189 --- /dev/null +++ b/bitacross-worker/app-libs/stf/src/getter.rs @@ -0,0 +1,269 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use codec::{Decode, Encode}; +use ita_sgx_runtime::System; +use itp_stf_interface::ExecuteGetter; +use itp_stf_primitives::{traits::GetterAuthorization, types::KeyPair}; +use itp_utils::{if_production_or, stringify::account_id_to_string}; +use litentry_primitives::{Identity, LitentryMultiSignature}; +use log::*; +use sp_std::vec; +use std::prelude::v1::*; + +#[cfg(feature = "evm")] +use ita_sgx_runtime::{AddressMapping, HashedAddressMapping}; + +#[cfg(feature = "evm")] +use crate::evm_helpers::{get_evm_account, get_evm_account_codes, get_evm_account_storages}; + +use itp_stf_primitives::traits::PoolTransactionValidation; +#[cfg(feature = "evm")] +use sp_core::{H160, H256}; +use sp_runtime::transaction_validity::{ + TransactionValidityError, UnknownTransaction, ValidTransaction, +}; + +#[cfg(not(feature = "production"))] +use crate::helpers::ALICE_ACCOUNTID32; + +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] +#[allow(non_camel_case_types)] +pub enum Getter { + #[codec(index = 0)] + public(PublicGetter), + #[codec(index = 1)] + trusted(TrustedGetterSigned), +} + +impl Default for Getter { + fn default() -> Self { + Getter::public(PublicGetter::some_value) + } +} +impl From for Getter { + fn from(item: PublicGetter) -> Self { + Getter::public(item) + } +} + +impl From for Getter { + fn from(item: TrustedGetterSigned) -> Self { + Getter::trusted(item) + } +} + +impl GetterAuthorization for Getter { + fn is_authorized(&self) -> bool { + match self { + Self::trusted(ref getter) => getter.verify_signature(), + Self::public(_) => true, + } + } +} + +impl PoolTransactionValidation for Getter { + fn validate(&self) -> Result { + match self { + Self::public(_) => + Err(TransactionValidityError::Unknown(UnknownTransaction::CannotLookup)), + Self::trusted(trusted_getter_signed) => Ok(ValidTransaction { + priority: 1 << 20, + requires: vec![], + provides: vec![trusted_getter_signed.signature.encode()], + longevity: 64, + propagate: true, + }), + } + } +} + +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] +#[allow(non_camel_case_types)] +pub enum PublicGetter { + #[codec(index = 0)] + some_value, + #[codec(index = 1)] + nonce(Identity), +} + +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] +#[allow(non_camel_case_types)] +pub enum TrustedGetter { + #[codec(index = 0)] + free_balance(Identity), + #[codec(index = 1)] + reserved_balance(Identity), + #[cfg(feature = "evm")] + #[codec(index = 2)] + evm_nonce(Identity), + #[cfg(feature = "evm")] + #[codec(index = 3)] + evm_account_codes(Identity, H160), + #[cfg(feature = "evm")] + #[codec(index = 4)] + evm_account_storages(Identity, H160, H256), +} + +impl TrustedGetter { + pub fn sender_identity(&self) -> &Identity { + match self { + TrustedGetter::free_balance(sender_identity) => sender_identity, + TrustedGetter::reserved_balance(sender_identity) => sender_identity, + #[cfg(feature = "evm")] + TrustedGetter::evm_nonce(sender_identity) => sender_identity, + #[cfg(feature = "evm")] + TrustedGetter::evm_account_codes(sender_identity, _) => sender_identity, + #[cfg(feature = "evm")] + TrustedGetter::evm_account_storages(sender_identity, ..) => sender_identity, + } + } + + pub fn sign(&self, pair: &KeyPair) -> TrustedGetterSigned { + let signature = pair.sign(self.encode().as_slice()); + TrustedGetterSigned { getter: self.clone(), signature } + } +} + +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] +pub struct TrustedGetterSigned { + pub getter: TrustedGetter, + pub signature: LitentryMultiSignature, +} + +impl TrustedGetterSigned { + pub fn new(getter: TrustedGetter, signature: LitentryMultiSignature) -> Self { + TrustedGetterSigned { getter, signature } + } + + pub fn verify_signature(&self) -> bool { + // in non-prod, we accept signature from Alice too + if_production_or!( + { + self.signature + .verify(self.getter.encode().as_slice(), self.getter.sender_identity()) + }, + { + self.signature + .verify(self.getter.encode().as_slice(), self.getter.sender_identity()) + || self + .signature + .verify(self.getter.encode().as_slice(), &ALICE_ACCOUNTID32.into()) + } + ) + } +} + +impl ExecuteGetter for Getter { + fn execute(self) -> Option> { + match self { + Getter::trusted(g) => g.execute(), + Getter::public(g) => g.execute(), + } + } + + fn get_storage_hashes_to_update(self) -> Vec> { + match self { + Getter::trusted(g) => g.get_storage_hashes_to_update(), + Getter::public(g) => g.get_storage_hashes_to_update(), + } + } +} + +impl ExecuteGetter for TrustedGetterSigned { + fn execute(self) -> Option> { + match self.getter { + TrustedGetter::free_balance(who) => + if let Some(account_id) = who.to_account_id() { + let info = System::account(&account_id); + debug!("TrustedGetter free_balance"); + debug!("AccountInfo for {} is {:?}", account_id_to_string(&who), info); + std::println!("⣿STF⣿ 🔍 TrustedGetter query: free balance for ⣿⣿⣿ is ⣿⣿⣿",); + Some(info.data.free.encode()) + } else { + None + }, + TrustedGetter::reserved_balance(who) => + if let Some(account_id) = who.to_account_id() { + let info = System::account(&account_id); + debug!("TrustedGetter reserved_balance"); + debug!("AccountInfo for {} is {:?}", account_id_to_string(&who), info); + debug!("Account reserved balance is {}", info.data.reserved); + Some(info.data.reserved.encode()) + } else { + None + }, + #[cfg(feature = "evm")] + TrustedGetter::evm_nonce(who) => + if let Some(account_id) = who.to_account_id() { + let evm_account = get_evm_account(&account_id); + let evm_account = HashedAddressMapping::into_account_id(evm_account); + let nonce = System::account_nonce(&evm_account); + debug!("TrustedGetter evm_nonce"); + debug!("Account nonce is {}", nonce); + Some(nonce.encode()) + } else { + None + }, + #[cfg(feature = "evm")] + TrustedGetter::evm_account_codes(_who, evm_account) => + // TODO: This probably needs some security check if who == evm_account (or assosciated) + if let Some(info) = get_evm_account_codes(&evm_account) { + debug!("TrustedGetter Evm Account Codes"); + debug!("AccountCodes for {} is {:?}", evm_account, info); + Some(info) // TOOD: encoded? + } else { + None + }, + #[cfg(feature = "evm")] + TrustedGetter::evm_account_storages(_who, evm_account, index) => + // TODO: This probably needs some security check if who == evm_account (or assosciated) + if let Some(value) = get_evm_account_storages(&evm_account, &index) { + debug!("TrustedGetter Evm Account Storages"); + debug!("AccountStorages for {} is {:?}", evm_account, value); + Some(value.encode()) + } else { + None + }, + } + } + + fn get_storage_hashes_to_update(self) -> Vec> { + Vec::new() + } +} + +impl ExecuteGetter for PublicGetter { + fn execute(self) -> Option> { + match self { + PublicGetter::some_value => Some(42u32.encode()), + PublicGetter::nonce(identity) => + if let Some(account_id) = identity.to_account_id() { + let nonce = System::account_nonce(&account_id); + debug!("PublicGetter nonce"); + debug!("Account nonce is {}", nonce); + Some(nonce.encode()) + } else { + None + }, + } + } + + fn get_storage_hashes_to_update(self) -> Vec> { + Vec::new() + } +} diff --git a/bitacross-worker/app-libs/stf/src/hash.rs b/bitacross-worker/app-libs/stf/src/hash.rs new file mode 100644 index 0000000000..f3cde9fa32 --- /dev/null +++ b/bitacross-worker/app-libs/stf/src/hash.rs @@ -0,0 +1,29 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::TrustedGetter; +use codec::Encode; +pub use itp_hashing::Hash; + +use itp_types::H256; +use sp_core::blake2_256; + +impl Hash for TrustedGetter { + fn hash(&self) -> H256 { + blake2_256(&self.encode()).into() + } +} diff --git a/bitacross-worker/app-libs/stf/src/helpers.rs b/bitacross-worker/app-libs/stf/src/helpers.rs new file mode 100644 index 0000000000..0c6fd39896 --- /dev/null +++ b/bitacross-worker/app-libs/stf/src/helpers.rs @@ -0,0 +1,175 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +use crate::ENCLAVE_ACCOUNT_KEY; +use codec::{Decode, Encode}; +use frame_support::ensure; +use hex_literal::hex; +use itp_stf_primitives::error::{StfError, StfResult}; +use itp_storage::{storage_double_map_key, storage_map_key, storage_value_key, StorageHasher}; +use itp_types::Index; +use itp_utils::stringify::account_id_to_string; +use litentry_primitives::{ErrorDetail, Identity, Web3ValidationData}; +use log::*; +use sp_core::blake2_256; +use sp_runtime::AccountId32; +use std::prelude::v1::*; + +pub const ALICE_ACCOUNTID32: AccountId32 = + AccountId32::new(hex!["d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"]); + +pub fn get_storage_value( + storage_prefix: &'static str, + storage_key_name: &'static str, +) -> Option { + let key = storage_value_key(storage_prefix, storage_key_name); + get_storage_by_key_hash(key) +} + +pub fn get_storage_map( + storage_prefix: &'static str, + storage_key_name: &'static str, + map_key: &K, + hasher: &StorageHasher, +) -> Option { + let key = storage_map_key::(storage_prefix, storage_key_name, map_key, hasher); + get_storage_by_key_hash(key) +} + +pub fn get_storage_double_map( + storage_prefix: &'static str, + storage_key_name: &'static str, + first: &K, + first_hasher: &StorageHasher, + second: &Q, + second_hasher: &StorageHasher, +) -> Option { + let key = storage_double_map_key::( + storage_prefix, + storage_key_name, + first, + first_hasher, + second, + second_hasher, + ); + get_storage_by_key_hash(key) +} + +/// Get value in storage. +pub fn get_storage_by_key_hash(key: Vec) -> Option { + if let Some(value_encoded) = sp_io::storage::get(&key) { + if let Ok(value) = Decode::decode(&mut value_encoded.as_slice()) { + Some(value) + } else { + error!("could not decode state for key {:x?}", key); + None + } + } else { + info!("key not found in state {:x?}", key); + None + } +} + +/// Get the AccountInfo key where the account is stored. +pub fn account_key_hash(account: &AccountId) -> Vec { + storage_map_key("System", "Account", account, &StorageHasher::Blake2_128Concat) +} + +pub fn enclave_signer_account() -> AccountId { + get_storage_value("Sudo", ENCLAVE_ACCOUNT_KEY).expect("No enclave account") +} + +/// Ensures an account is a registered enclave account. +pub fn ensure_enclave_signer_account( + account: &AccountId, +) -> StfResult<()> { + let expected_enclave_account: AccountId = enclave_signer_account(); + if &expected_enclave_account == account { + Ok(()) + } else { + error!( + "Expected enclave account {}, but found {}", + account_id_to_string(&expected_enclave_account), + account_id_to_string(account) + ); + Err(StfError::RequireEnclaveSignerAccount) + } +} + +pub fn set_block_number(block_number: u32) { + sp_io::storage::set(&storage_value_key("System", "Number"), &block_number.encode()); +} + +pub fn ensure_self(signer: &AccountId, who: &AccountId) -> bool { + signer == who +} + +pub fn ensure_enclave_signer_or_self( + signer: &AccountId, + who: Option, +) -> bool { + match who { + Some(ref who) => + signer == &enclave_signer_account::() || ensure_self(signer, who), + None => false, + } +} + +#[cfg(not(feature = "production"))] +pub fn ensure_alice(signer: &AccountId32) -> bool { + signer == &ALICE_ACCOUNTID32 +} + +#[cfg(not(feature = "production"))] +pub fn ensure_enclave_signer_or_alice(signer: &AccountId32) -> bool { + signer == &enclave_signer_account::() || ensure_alice(signer) +} + +// verification message format: +// ``` +// blake2_256( + + ) +// ``` +// where <> means SCALE-encoded +// see https://github.com/litentry/litentry-parachain/issues/1739 and P-174 +pub fn get_expected_raw_message( + who: &Identity, + identity: &Identity, + sidechain_nonce: Index, +) -> Vec { + let mut payload = Vec::new(); + payload.append(&mut sidechain_nonce.encode()); + payload.append(&mut who.encode()); + payload.append(&mut identity.encode()); + blake2_256(payload.as_slice()).to_vec() +} + +pub fn verify_web3_identity( + identity: &Identity, + raw_msg: &[u8], + data: &Web3ValidationData, +) -> StfResult<()> { + ensure!( + raw_msg == data.message().as_slice(), + StfError::LinkIdentityFailed(ErrorDetail::UnexpectedMessage) + ); + + ensure!( + data.signature().verify(raw_msg, identity), + StfError::LinkIdentityFailed(ErrorDetail::VerifyWeb3SignatureFailed) + ); + + Ok(()) +} diff --git a/bitacross-worker/app-libs/stf/src/lib.rs b/bitacross-worker/app-libs/stf/src/lib.rs new file mode 100644 index 0000000000..894633b49f --- /dev/null +++ b/bitacross-worker/app-libs/stf/src/lib.rs @@ -0,0 +1,53 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +///////////////////////////////////////////////////////////////////////////// +#![feature(structural_match)] +#![feature(rustc_attrs)] +#![feature(core_intrinsics)] +#![feature(derive_eq)] +#![cfg_attr(all(not(target_env = "sgx"), not(feature = "std")), no_std)] +#![cfg_attr(target_env = "sgx", feature(rustc_private))] +#![allow(clippy::large_enum_variant)] +#![allow(clippy::result_large_err)] + +extern crate core; +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +pub use getter::*; +pub use stf_sgx_primitives::{types::*, Stf}; +pub use trusted_call::*; + +#[cfg(feature = "evm")] +pub mod evm_helpers; +pub mod getter; +pub mod hash; +pub mod helpers; +pub mod stf_sgx; +pub mod stf_sgx_primitives; +#[cfg(all(feature = "test", feature = "sgx"))] +pub mod stf_sgx_tests; +#[cfg(all(feature = "test", feature = "sgx"))] +pub mod test_genesis; +pub mod trusted_call; +pub mod trusted_call_result; + +pub(crate) const ENCLAVE_ACCOUNT_KEY: &str = "Enclave_Account_Key"; + +// fixme: this if a temporary hack only +pub const STF_TX_FEE: Balance = 100000000; diff --git a/bitacross-worker/app-libs/stf/src/stf_sgx.rs b/bitacross-worker/app-libs/stf/src/stf_sgx.rs new file mode 100644 index 0000000000..8af7217ec2 --- /dev/null +++ b/bitacross-worker/app-libs/stf/src/stf_sgx.rs @@ -0,0 +1,334 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(feature = "test")] +use crate::test_genesis::test_genesis_setup; +use crate::{helpers::enclave_signer_account, Stf, ENCLAVE_ACCOUNT_KEY}; +use codec::{Decode, Encode}; +use frame_support::traits::{OriginTrait, UnfilteredDispatchable}; +use ita_sgx_runtime::Executive; +use itp_node_api::metadata::{provider::AccessNodeMetadata, NodeMetadataTrait}; +use itp_sgx_externalities::SgxExternalitiesTrait; +use itp_stf_interface::{ + parentchain_pallet::ParentchainPalletInterface, + runtime_upgrade::RuntimeUpgradeInterface, + sudo_pallet::SudoPalletInterface, + system_pallet::{SystemPalletAccountInterface, SystemPalletEventInterface}, + ExecuteCall, ExecuteGetter, InitState, ShardVaultQuery, StateCallInterface, + StateGetterInterface, UpdateState, SHARD_VAULT_KEY, +}; +use itp_stf_primitives::{ + error::StfError, traits::TrustedCallVerification, types::ShardIdentifier, +}; +use itp_storage::storage_value_key; +use itp_types::{ + parentchain::{AccountId, ParentchainCall, ParentchainId}, + H256, +}; +use itp_utils::stringify::account_id_to_string; +use log::*; +use sp_runtime::traits::StaticLookup; +use std::{fmt::Debug, format, prelude::v1::*, sync::Arc, vec}; + +impl InitState for Stf +where + State: SgxExternalitiesTrait + Debug, + ::SgxExternalitiesType: core::default::Default, + Runtime: frame_system::Config + pallet_balances::Config, + <::Lookup as StaticLookup>::Source: + std::convert::From, + AccountId: Encode, +{ + fn init_state(enclave_account: AccountId) -> State { + debug!("initializing stf state, account id {}", account_id_to_string(&enclave_account)); + let mut state = State::new(Default::default()); + + state.execute_with(|| { + // Do not set genesis for pallets that are meant to be on-chain + // use get_storage_hashes_to_update instead. + + sp_io::storage::set(&storage_value_key("Balances", "TotalIssuance"), &11u128.encode()); + sp_io::storage::set(&storage_value_key("Balances", "CreationFee"), &1u128.encode()); + sp_io::storage::set(&storage_value_key("Balances", "TransferFee"), &1u128.encode()); + sp_io::storage::set( + &storage_value_key("Balances", "TransactionBaseFee"), + &1u128.encode(), + ); + sp_io::storage::set( + &storage_value_key("Balances", "TransactionByteFee"), + &1u128.encode(), + ); + sp_io::storage::set( + &storage_value_key("Balances", "ExistentialDeposit"), + &1u128.encode(), + ); + }); + + #[cfg(feature = "test")] + test_genesis_setup(&mut state); + + state.execute_with(|| { + sp_io::storage::set( + &storage_value_key("Sudo", ENCLAVE_ACCOUNT_KEY), + &enclave_account.encode(), + ); + + if let Err(e) = create_enclave_self_account::(enclave_account) { + error!("Failed to initialize the enclave signer account: {:?}", e); + } + }); + + trace!("Returning updated state: {:?}", state); + state + } +} + +impl + UpdateState::SgxExternalitiesDiffType> + for Stf +where + State: SgxExternalitiesTrait + Debug, + ::SgxExternalitiesType: core::default::Default, + ::SgxExternalitiesDiffType: + IntoIterator, Option>)>, +{ + fn apply_state_diff( + state: &mut State, + map_update: ::SgxExternalitiesDiffType, + ) { + state.execute_with(|| { + map_update.into_iter().for_each(|(k, v)| { + match v { + Some(value) => sp_io::storage::set(&k, &value), + None => sp_io::storage::clear(&k), + }; + }); + }); + } + + fn storage_hashes_to_update_on_block(parentchain_id: &ParentchainId) -> Vec> { + // Get all shards that are currently registered. + match parentchain_id { + ParentchainId::Litentry => vec![], // shards_key_hash() moved to stf_executor and is currently unused + ParentchainId::TargetA => vec![], + ParentchainId::TargetB => vec![], + } + } +} + +impl + StateCallInterface for Stf +where + TCS: PartialEq + + ExecuteCall + + Encode + + Decode + + Debug + + Clone + + Sync + + Send + + TrustedCallVerification, + State: SgxExternalitiesTrait + Debug, + NodeMetadataRepository: AccessNodeMetadata, + NodeMetadataRepository::MetadataType: NodeMetadataTrait, +{ + type Error = TCS::Error; + type Result = TCS::Result; + + fn execute_call( + state: &mut State, + shard: &ShardIdentifier, + call: TCS, + top_hash: H256, + calls: &mut Vec, + node_metadata_repo: Arc, + ) -> Result { + state.execute_with(|| call.execute(shard, top_hash, calls, node_metadata_repo)) + } +} + +impl StateGetterInterface for Stf +where + G: PartialEq + ExecuteGetter, + State: SgxExternalitiesTrait + Debug, +{ + fn execute_getter(state: &mut State, getter: G) -> Option> { + state.execute_with(|| getter.execute()) + } +} + +impl ShardVaultQuery for Stf +where + State: SgxExternalitiesTrait + Debug, +{ + fn get_vault(state: &mut State) -> Option { + state + .get(SHARD_VAULT_KEY.as_bytes()) + .and_then(|v| Decode::decode(&mut v.clone().as_slice()).ok()) + } +} + +impl SudoPalletInterface for Stf +where + State: SgxExternalitiesTrait, + Runtime: frame_system::Config + pallet_sudo::Config, +{ + type AccountId = Runtime::AccountId; + + fn get_root(state: &mut State) -> Self::AccountId { + state.execute_with(|| pallet_sudo::Pallet::::key().expect("No root account")) + } + + fn get_enclave_account(state: &mut State) -> Self::AccountId { + state.execute_with(enclave_signer_account::) + } +} + +impl SystemPalletAccountInterface + for Stf +where + State: SgxExternalitiesTrait, + Runtime: frame_system::Config, + AccountId: Encode, +{ + type Index = Runtime::Index; + type AccountData = Runtime::AccountData; + + fn get_account_nonce(state: &mut State, account: &AccountId) -> Self::Index { + state.execute_with(|| { + let nonce = frame_system::Pallet::::account_nonce(account); + debug!("Account {} nonce is {:?}", account_id_to_string(account), nonce); + nonce + }) + } + + fn get_account_data(state: &mut State, account: &AccountId) -> Self::AccountData { + state.execute_with(|| frame_system::Pallet::::account(account).data) + } +} + +impl SystemPalletEventInterface for Stf +where + State: SgxExternalitiesTrait, + Runtime: frame_system::Config, +{ + type EventRecord = frame_system::EventRecord; + type EventIndex = u32; // For some reason this is not a pub type in frame_system + type BlockNumber = Runtime::BlockNumber; + type Hash = Runtime::Hash; + + fn get_events(state: &mut State) -> Vec> { + // Fixme: Not nice to have to call collect here, but we can't use impl Iterator<..> + // in trait method return types yet, see: + // https://rust-lang.github.io/impl-trait-initiative/RFCs/rpit-in-traits.html + state.execute_with(|| frame_system::Pallet::::read_events_no_consensus().collect()) + } + + fn get_event_count(state: &mut State) -> Self::EventIndex { + state.execute_with(|| frame_system::Pallet::::event_count()) + } + + fn get_event_topics( + state: &mut State, + topic: &Self::Hash, + ) -> Vec<(Self::BlockNumber, Self::EventIndex)> { + state.execute_with(|| frame_system::Pallet::::event_topics(topic)) + } + + fn reset_events(state: &mut State) { + state.execute_with(|| frame_system::Pallet::::reset_events()) + } +} + +impl ParentchainPalletInterface + for Stf +where + State: SgxExternalitiesTrait, + Runtime: frame_system::Config
+ pallet_parentchain::Config, +{ + type Error = StfError; + + fn update_parentchain_block( + state: &mut State, + header: ParentchainHeader, + ) -> Result<(), Self::Error> { + state.execute_with(|| { + pallet_parentchain::Call::::set_block { header } + .dispatch_bypass_filter(Runtime::RuntimeOrigin::root()) + .map_err(|e| { + Self::Error::Dispatch(format!("Update parentchain block error: {:?}", e.error)) + }) + })?; + Ok(()) + } +} + +impl RuntimeUpgradeInterface for Stf +where + State: SgxExternalitiesTrait, + Runtime: frame_system::Config, +{ + type Error = StfError; + + fn on_runtime_upgrade(state: &mut State) -> Result<(), Self::Error> { + // Returns if the runtime was upgraded since the last time this function was called. + let runtime_upgraded = || -> bool { + let last = frame_system::LastRuntimeUpgrade::::get(); + let current = + <::Version as frame_support::traits::Get<_>>::get( + ); + + if last.as_ref().map(|v| v.was_upgraded(¤t)).unwrap_or(true) { + frame_system::LastRuntimeUpgrade::::put( + frame_system::LastRuntimeUpgradeInfo::from(current.clone()), + ); + debug!("Do some migrations, last: {:?}, current: {:?}", last, current.spec_version); + true + } else { + false + } + }; + + state.execute_with(|| { + if runtime_upgraded() { + Executive::execute_on_runtime_upgrade(); + } + }); + Ok(()) + } +} + +/// Creates valid enclave account with a balance that is above the existential deposit. +/// !! Requires a root to be set. +fn create_enclave_self_account( + enclave_account: AccountId, +) -> Result<(), StfError> +where + Runtime: frame_system::Config + pallet_balances::Config, + <::Lookup as StaticLookup>::Source: From, + Runtime::Balance: From, +{ + pallet_balances::Call::::force_set_balance { + who: enclave_account.into(), + new_free: 1000.into(), + } + .dispatch_bypass_filter(Runtime::RuntimeOrigin::root()) + .map_err(|e| { + StfError::Dispatch(format!("Set Balance for enclave signer account error: {:?}", e.error)) + }) + .map(|_| ()) +} diff --git a/bitacross-worker/app-libs/stf/src/stf_sgx_primitives.rs b/bitacross-worker/app-libs/stf/src/stf_sgx_primitives.rs new file mode 100644 index 0000000000..8ccfccc787 --- /dev/null +++ b/bitacross-worker/app-libs/stf/src/stf_sgx_primitives.rs @@ -0,0 +1,30 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use std::marker::PhantomData; + +pub mod types { + pub use itp_types::{AccountData, AccountInfo, BlockNumber, Header as ParentchainHeader}; + + pub type State = itp_sgx_externalities::SgxExternalities; + pub type StateType = itp_sgx_externalities::SgxExternalitiesType; + pub type StateDiffType = itp_sgx_externalities::SgxExternalitiesDiffType; +} + +pub struct Stf { + phantom_data: PhantomData<(TCS, G, State, Runtime)>, +} diff --git a/bitacross-worker/app-libs/stf/src/stf_sgx_tests.rs b/bitacross-worker/app-libs/stf/src/stf_sgx_tests.rs new file mode 100644 index 0000000000..7c3a6e4b7c --- /dev/null +++ b/bitacross-worker/app-libs/stf/src/stf_sgx_tests.rs @@ -0,0 +1,82 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{Getter, State, Stf, TrustedCall, TrustedCallSigned}; +use ita_sgx_runtime::Runtime; +use itp_node_api::metadata::{metadata_mocks::NodeMetadataMock, provider::NodeMetadataRepository}; +use itp_stf_interface::{ + sudo_pallet::SudoPalletInterface, system_pallet::SystemPalletAccountInterface, InitState, + StateCallInterface, +}; +use itp_stf_primitives::types::{AccountId, ShardIdentifier}; +use litentry_primitives::LitentryMultiSignature; +use sp_core::{ + ed25519::{Pair as Ed25519Pair, Signature as Ed25519Signature}, + Pair, +}; +use std::{sync::Arc, vec::Vec}; + +pub type StfState = Stf; + +pub fn enclave_account_initialization_works() { + let enclave_account = AccountId::new([2u8; 32]); + let mut state = StfState::init_state(enclave_account.clone()); + let _root = StfState::get_root(&mut state); + let account_data = StfState::get_account_data(&mut state, &enclave_account); + + assert_eq!(0, StfState::get_account_nonce(&mut state, &enclave_account)); + assert_eq!(enclave_account, StfState::get_enclave_account(&mut state)); + assert_eq!(1000, account_data.free); +} + +pub fn shield_funds_increments_signer_account_nonce() { + let enclave_call_signer = Ed25519Pair::from_seed(b"14672678901234567890123456789012"); + let enclave_signer_account_id: AccountId = enclave_call_signer.public().into(); + let mut state = StfState::init_state(enclave_signer_account_id.clone()); + + let shield_funds_call = TrustedCallSigned::new( + TrustedCall::balance_shield( + enclave_call_signer.public().into(), + AccountId::new([1u8; 32]), + 500u128, + ), + 0, + LitentryMultiSignature::Ed25519(Ed25519Signature([0u8; 64])), + ); + + let repo = Arc::new(NodeMetadataRepository::new(NodeMetadataMock::new())); + let shard = ShardIdentifier::default(); + StfState::execute_call( + &mut state, + &shard, + shield_funds_call, + Default::default(), + &mut Vec::new(), + repo, + ) + .unwrap(); + assert_eq!(1, StfState::get_account_nonce(&mut state, &enclave_signer_account_id)); +} + +pub fn test_root_account_exists_after_initialization() { + let enclave_account = AccountId::new([2u8; 32]); + let mut state = StfState::init_state(enclave_account); + let root_account = StfState::get_root(&mut state); + + let account_data = StfState::get_account_data(&mut state, &root_account); + assert!(account_data.free > 0); +} diff --git a/bitacross-worker/app-libs/stf/src/test_genesis.rs b/bitacross-worker/app-libs/stf/src/test_genesis.rs new file mode 100644 index 0000000000..161dec8e5e --- /dev/null +++ b/bitacross-worker/app-libs/stf/src/test_genesis.rs @@ -0,0 +1,114 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +use frame_support::traits::UnfilteredDispatchable; +use ita_sgx_runtime::{Balance, Runtime, System}; +use itp_sgx_externalities::SgxExternalitiesTrait; +use itp_stf_primitives::error::StfError; +use itp_storage::storage_value_key; +use log::*; +use sgx_tstd as std; +use sp_core::{crypto::AccountId32, ed25519, Pair}; +use sp_runtime::MultiAddress; +use std::{format, vec, vec::Vec}; + +#[cfg(feature = "evm")] +use ita_sgx_runtime::{AddressMapping, HashedAddressMapping}; + +#[cfg(feature = "evm")] +use crate::evm_helpers::get_evm_account; + +type Seed = [u8; 32]; + +const ALICE_ENCODED: Seed = [ + 212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, + 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125, +]; + +const ENDOWED_SEED: Seed = *b"12345678901234567890123456789012"; +const SECOND_ENDOWED_SEED: Seed = *b"22345678901234567890123456789012"; +const UNENDOWED_SEED: Seed = *b"92345678901234567890123456789012"; + +const ALICE_FUNDS: Balance = 10_000_000_000_000_000; +pub const ENDOWED_ACC_FUNDS: Balance = 2_000_000_000_000; +pub const SECOND_ENDOWED_ACC_FUNDS: Balance = 1_000_000_000_000; + +pub fn endowed_account() -> ed25519::Pair { + ed25519::Pair::from_seed(&ENDOWED_SEED) +} +pub fn second_endowed_account() -> ed25519::Pair { + ed25519::Pair::from_seed(&SECOND_ENDOWED_SEED) +} + +pub fn unendowed_account() -> ed25519::Pair { + ed25519::Pair::from_seed(&UNENDOWED_SEED) +} + +pub fn test_genesis_setup(state: &mut impl SgxExternalitiesTrait) { + // set alice sudo account + set_sudo_account(state, &ALICE_ENCODED); + trace!("Set new sudo account: {:?}", &ALICE_ENCODED); + + let mut endowees: Vec<(AccountId32, Balance)> = vec![ + (endowed_account().public().into(), ENDOWED_ACC_FUNDS), + (second_endowed_account().public().into(), SECOND_ENDOWED_ACC_FUNDS), + (ALICE_ENCODED.into(), ALICE_FUNDS), + ]; + + append_funded_alice_evm_account(&mut endowees); + + endow(state, endowees); +} + +#[cfg(feature = "evm")] +fn append_funded_alice_evm_account(endowees: &mut Vec<(AccountId32, Balance)>) { + let alice_evm = get_evm_account(&ALICE_ENCODED.into()); + let alice_evm_substrate_version = HashedAddressMapping::into_account_id(alice_evm); + let mut other: Vec<(AccountId32, Balance)> = vec![(alice_evm_substrate_version, ALICE_FUNDS)]; + endowees.append(other.as_mut()); +} + +#[cfg(not(feature = "evm"))] +fn append_funded_alice_evm_account(_: &mut Vec<(AccountId32, Balance)>) {} + +fn set_sudo_account(state: &mut impl SgxExternalitiesTrait, account_encoded: &[u8]) { + state.execute_with(|| { + sp_io::storage::set(&storage_value_key("Sudo", "Key"), account_encoded); + }) +} + +pub fn endow( + state: &mut impl SgxExternalitiesTrait, + endowees: impl IntoIterator, +) { + state.execute_with(|| { + for e in endowees.into_iter() { + let account = e.0; + + ita_sgx_runtime::BalancesCall::::force_set_balance { + who: MultiAddress::Id(account.clone()), + new_free: e.1, + } + .dispatch_bypass_filter(ita_sgx_runtime::RuntimeOrigin::root()) + .map_err(|e| StfError::Dispatch(format!("Balance Set Balance error: {:?}", e.error))) + .unwrap(); + + let print_public: [u8; 32] = account.clone().into(); + let account_info = System::account(&&print_public.into()); + debug!("{:?} balance is {}", print_public, account_info.data.free); + } + }); +} diff --git a/bitacross-worker/app-libs/stf/src/trusted_call.rs b/bitacross-worker/app-libs/stf/src/trusted_call.rs new file mode 100644 index 0000000000..2ebeb97741 --- /dev/null +++ b/bitacross-worker/app-libs/stf/src/trusted_call.rs @@ -0,0 +1,686 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(feature = "evm")] +use sp_core::{H160, U256}; + +#[cfg(feature = "evm")] +use std::vec::Vec; + +#[cfg(feature = "evm")] +use crate::evm_helpers::{create_code_hash, evm_create2_address, evm_create_address}; +use crate::{ + helpers::{enclave_signer_account, ensure_enclave_signer_account, get_storage_by_key_hash}, + trusted_call_result::TrustedCallResult, + Getter, +}; +use codec::{Compact, Decode, Encode}; +use frame_support::{ensure, traits::UnfilteredDispatchable}; +#[cfg(feature = "evm")] +use ita_sgx_runtime::{AddressMapping, HashedAddressMapping}; +pub use ita_sgx_runtime::{Balance, Index, Runtime, System}; +use itp_node_api::metadata::{provider::AccessNodeMetadata, NodeMetadataTrait}; +use itp_node_api_metadata::{ + pallet_balances::BalancesCallIndexes, pallet_imp::IMPCallIndexes, + pallet_proxy::ProxyCallIndexes, pallet_teerex::TeerexCallIndexes, pallet_vcmp::VCMPCallIndexes, +}; +use itp_stf_interface::{ExecuteCall, SHARD_VAULT_KEY}; +pub use itp_stf_primitives::{ + error::{StfError, StfResult}, + traits::{TrustedCallSigning, TrustedCallVerification}, + types::{AccountId, KeyPair, ShardIdentifier, TrustedOperation}, +}; +use itp_types::{ + parentchain::{ParentchainCall, ProxyType}, + Address, +}; +pub use itp_types::{OpaqueCall, H256}; +use itp_utils::stringify::account_id_to_string; +pub use litentry_primitives::{ + aes_encrypt_default, all_evm_web3networks, all_substrate_web3networks, AesOutput, Assertion, + ErrorDetail, IMPError, Identity, LitentryMultiSignature, ParentchainBlockNumber, RequestAesKey, + RequestAesKeyNonce, VCMPError, ValidationData, Web3Network, +}; +use log::*; +use sp_core::{ + crypto::{AccountId32, UncheckedFrom}, + ed25519, +}; +use sp_io::hashing::blake2_256; +use sp_runtime::MultiAddress; +use std::{format, prelude::v1::*, sync::Arc}; + +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] +#[allow(non_camel_case_types)] +pub enum TrustedCall { + // original integritee trusted calls, starting from index 50 + #[codec(index = 50)] + noop(Identity), + #[codec(index = 51)] + balance_set_balance(Identity, AccountId, Balance, Balance), + #[codec(index = 52)] + balance_transfer(Identity, AccountId, Balance), + #[codec(index = 53)] + balance_unshield(Identity, AccountId, Balance, ShardIdentifier), // (AccountIncognito, BeneficiaryPublicAccount, Amount, Shard) + #[codec(index = 54)] + balance_shield(Identity, AccountId, Balance), // (Root, AccountIncognito, Amount) + #[cfg(feature = "evm")] + #[codec(index = 55)] + evm_withdraw(Identity, H160, Balance), // (Origin, Address EVM Account, Value) + // (Origin, Source, Target, Input, Value, Gas limit, Max fee per gas, Max priority fee per gas, Nonce, Access list) + #[cfg(feature = "evm")] + #[codec(index = 56)] + evm_call( + Identity, + H160, + H160, + Vec, + U256, + u64, + U256, + Option, + Option, + Vec<(H160, Vec)>, + ), + // (Origin, Source, Init, Value, Gas limit, Max fee per gas, Max priority fee per gas, Nonce, Access list) + #[cfg(feature = "evm")] + #[codec(index = 57)] + evm_create( + Identity, + H160, + Vec, + U256, + u64, + U256, + Option, + Option, + Vec<(H160, Vec)>, + ), + // (Origin, Source, Init, Salt, Value, Gas limit, Max fee per gas, Max priority fee per gas, Nonce, Access list) + #[cfg(feature = "evm")] + #[codec(index = 58)] + evm_create2( + Identity, + H160, + Vec, + H256, + U256, + u64, + U256, + Option, + Option, + Vec<(H160, Vec)>, + ), +} + +impl TrustedCall { + pub fn sender_identity(&self) -> &Identity { + match self { + Self::noop(sender_identity) => sender_identity, + Self::balance_set_balance(sender_identity, ..) => sender_identity, + Self::balance_transfer(sender_identity, ..) => sender_identity, + Self::balance_unshield(sender_identity, ..) => sender_identity, + Self::balance_shield(sender_identity, ..) => sender_identity, + #[cfg(feature = "evm")] + Self::evm_withdraw(sender_identity, ..) => sender_identity, + #[cfg(feature = "evm")] + Self::evm_call(sender_identity, ..) => sender_identity, + #[cfg(feature = "evm")] + Self::evm_create(sender_identity, ..) => sender_identity, + #[cfg(feature = "evm")] + Self::evm_create2(sender_identity, ..) => sender_identity, + } + } +} + +impl TrustedCallSigning for TrustedCall { + fn sign( + &self, + pair: &KeyPair, + nonce: Index, + mrenclave: &[u8; 32], + shard: &ShardIdentifier, + ) -> TrustedCallSigned { + let mut payload = self.encode(); + payload.append(&mut nonce.encode()); + payload.append(&mut mrenclave.encode()); + payload.append(&mut shard.encode()); + + TrustedCallSigned { call: self.clone(), nonce, signature: pair.sign(payload.as_slice()) } + } +} + +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] +pub struct TrustedCallSigned { + pub call: TrustedCall, + pub nonce: Index, + pub signature: LitentryMultiSignature, +} + +impl TrustedCallSigned { + pub fn new(call: TrustedCall, nonce: Index, signature: LitentryMultiSignature) -> Self { + TrustedCallSigned { call, nonce, signature } + } + + pub fn into_trusted_operation( + self, + direct: bool, + ) -> TrustedOperation { + match direct { + true => TrustedOperation::direct_call(self), + false => TrustedOperation::indirect_call(self), + } + } +} + +impl Default for TrustedCallSigned { + fn default() -> Self { + Self { + call: TrustedCall::noop(AccountId32::unchecked_from([0u8; 32].into()).into()), + nonce: 0, + signature: LitentryMultiSignature::Ed25519(ed25519::Signature::unchecked_from( + [0u8; 64], + )), + } + } +} +impl TrustedCallVerification for TrustedCallSigned { + fn sender_identity(&self) -> &Identity { + self.call.sender_identity() + } + + fn nonce(&self) -> Index { + self.nonce + } + + fn verify_signature(&self, mrenclave: &[u8; 32], shard: &ShardIdentifier) -> bool { + let mut payload = self.call.encode(); + payload.append(&mut self.nonce.encode()); + payload.append(&mut mrenclave.encode()); + payload.append(&mut shard.encode()); + + self.signature.verify(payload.as_slice(), self.call.sender_identity()) + } +} + +impl ExecuteCall for TrustedCallSigned +where + NodeMetadataRepository: AccessNodeMetadata, + NodeMetadataRepository::MetadataType: NodeMetadataTrait, +{ + type Error = StfError; + type Result = TrustedCallResult; + + // TODO(Kai@litentry): + // If this function returns Err(), it will feed the executor with Ok(ExecutedOperation::failed()), + // which will remove the failed op from its **own** top pool while preventing it from being included + // in a sidechain block - see `execute_trusted_call_on_stf`. + // + // As a result, when other workers import sidechain blocks, they will treat the op as + // "not yet executed" (before it's not recorded in the sidechain block) and try to execute it again from + // its own top pool (if the op is added to the top pool upon e.g. parentchain block import). + // + // The execution will most likely fail again. However, the state could have been changed already by applying + // the state diff from the imported sidechain block. This could cause an inconsistent/mismatching state, + // for example, the nonce. See the nonce handling below: we increased the nonce no matter the STF is executed + // successfully or not. + // + // This is probably the reason why the nonce-handling test in `demo_shielding_unshielding.sh` sometimes fails. + // + // Update: + // see discussion in https://github.com/integritee-network/worker/issues/1232 + // my current thoughts are: + // - we should return Err() if the STF execution fails, the parentchain effect will get applied regardless + // - the failed top should be removed from the pool + // - however, the failed top hash needs to be included in the sidechain block (still TODO) + // + // Almost every (Litentry) trusted call has a `H256` as parameter, this is used as the request identifier. + // It should be generated by the client (requester), and checked against when getting the response. + // It might seem redundant for direct invocation (DI) as the response is synchronous, however, we do need it + // when the request is handled asynchronously interanlly, which leads to streamed responses. Without it, it's + // impossible to pair the request and response. `top_hash` won't suffice as you can't know all hashes from + // client side beforehand (e.g. those trusted calls signed by enclave signer). + // + // TODO: + // - shall we add `req_ext_hash` in RpcReturnValue and use it to find streamed trustedCalls? + // - show error details for "Invalid" synchronous responses + fn execute( + self, + _shard: &ShardIdentifier, + _top_hash: H256, + calls: &mut Vec, + node_metadata_repo: Arc, + ) -> Result { + let sender = self.call.sender_identity().clone(); + let call_hash = blake2_256(&self.call.encode()); + let account_id: AccountId = sender.to_account_id().ok_or(Self::Error::InvalidAccount)?; + let system_nonce = System::account_nonce(&account_id); + ensure!(self.nonce == system_nonce, Self::Error::InvalidNonce(self.nonce, system_nonce)); + + // Increment the nonce no matter if the call succeeds or fails. + // We consider the call "valid" once it reaches here (= it entered the tx pool) + System::inc_account_nonce(&account_id); + + // TODO: maybe we can further simplify this by effacing the duplicate code + match self.call { + TrustedCall::noop(who) => { + debug!("noop called by {}", account_id_to_string(&who),); + Ok(TrustedCallResult::Empty) + }, + TrustedCall::balance_set_balance(root, who, free_balance, reserved_balance) => { + let root_account_id: AccountId = + root.to_account_id().ok_or(Self::Error::InvalidAccount)?; + ensure!( + is_root::(&root_account_id), + Self::Error::MissingPrivileges(root_account_id) + ); + debug!( + "balance_set_balance({}, {}, {})", + account_id_to_string(&who), + free_balance, + reserved_balance + ); + ita_sgx_runtime::BalancesCall::::force_set_balance { + who: MultiAddress::Id(who), + new_free: free_balance, + } + .dispatch_bypass_filter(ita_sgx_runtime::RuntimeOrigin::root()) + .map_err(|e| { + Self::Error::Dispatch(format!("Balance Set Balance error: {:?}", e.error)) + })?; + // This explicit Error type is somehow still needed, otherwise the compiler complains + // multiple `impl`s satisfying `StfError: std::convert::From<_>` + // note: and another `impl` found in the `core` crate: `impl std::convert::From for T;` + // the impl From<..> for StfError conflicts with the standard convert + // + // Alternatively, removing the customised "impl From<..> for StfError" and use map_err directly + // would also work + Ok(TrustedCallResult::Empty) + }, + TrustedCall::balance_transfer(from, to, value) => { + let origin = ita_sgx_runtime::RuntimeOrigin::signed( + from.to_account_id().ok_or(Self::Error::InvalidAccount)?, + ); + std::println!("⣿STF⣿ 🔄 balance_transfer from ⣿⣿⣿ to ⣿⣿⣿ amount ⣿⣿⣿"); + // endow fee to enclave (self) + let fee_recipient: AccountId = enclave_signer_account(); + // fixme: apply fees through standard frame process and tune it + let fee = crate::STF_TX_FEE; + info!( + "from {}, to {}, amount {}, fee {}", + account_id_to_string(&from), + account_id_to_string(&to), + value, + fee + ); + ita_sgx_runtime::BalancesCall::::transfer { + dest: MultiAddress::Id(fee_recipient), + value: fee, + } + .dispatch_bypass_filter(origin.clone()) + .map_err(|e| { + Self::Error::Dispatch(format!("Balance Transfer error: {:?}", e.error)) + })?; + ita_sgx_runtime::BalancesCall::::transfer { + dest: MultiAddress::Id(to), + value, + } + .dispatch_bypass_filter(origin) + .map_err(|e| { + Self::Error::Dispatch(format!("Balance Transfer error: {:?}", e.error)) + })?; + Ok(TrustedCallResult::Empty) + }, + TrustedCall::balance_unshield(account_incognito, beneficiary, value, shard) => { + std::println!( + "⣿STF⣿ 🛡👐 balance_unshield from ⣿⣿⣿ to {}, amount {}", + account_id_to_string(&beneficiary), + value + ); + // endow fee to enclave (self) + let fee_recipient: AccountId = enclave_signer_account(); + // fixme: apply fees through standard frame process and tune it. has to be at least two L1 transfer's fees + let fee = crate::STF_TX_FEE * 3; + + info!( + "balance_unshield(from (L2): {}, to (L1): {}, amount {} (+fee: {}), shard {})", + account_id_to_string(&account_incognito), + account_id_to_string(&beneficiary), + value, + fee, + shard + ); + + let origin = ita_sgx_runtime::RuntimeOrigin::signed( + account_incognito.to_account_id().ok_or(StfError::InvalidAccount)?, + ); + ita_sgx_runtime::BalancesCall::::transfer { + dest: MultiAddress::Id(fee_recipient), + value: fee, + } + .dispatch_bypass_filter(origin) + .map_err(|e| { + Self::Error::Dispatch(format!("Balance Unshielding error: {:?}", e.error)) + })?; + burn_funds( + account_incognito.to_account_id().ok_or(StfError::InvalidAccount)?, + value, + )?; + + let vault_pubkey: [u8; 32] = get_storage_by_key_hash(SHARD_VAULT_KEY.into()) + .ok_or_else(|| { + StfError::Dispatch("shard vault key hasn't been set".to_string()) + })?; + let vault_address = Address::from(AccountId::from(vault_pubkey)); + let vault_transfer_call = OpaqueCall::from_tuple(&( + node_metadata_repo + .get_from_metadata(|m| m.transfer_keep_alive_call_indexes()) + .map_err(|_| StfError::InvalidMetadata)? + .map_err(|_| StfError::InvalidMetadata)?, + Address::from(beneficiary), + Compact(value), + )); + let proxy_call = OpaqueCall::from_tuple(&( + node_metadata_repo + .get_from_metadata(|m| m.proxy_call_indexes()) + .map_err(|_| StfError::InvalidMetadata)? + .map_err(|_| StfError::InvalidMetadata)?, + vault_address, + None::, + vault_transfer_call, + )); + calls.push(ParentchainCall::TargetA(proxy_call)); + Ok(TrustedCallResult::Empty) + }, + TrustedCall::balance_shield(enclave_account, who, value) => { + let account_id: AccountId32 = + enclave_account.to_account_id().ok_or(Self::Error::InvalidAccount)?; + ensure_enclave_signer_account(&account_id)?; + debug!("balance_shield({}, {})", account_id_to_string(&who), value); + shield_funds(who, value)?; + + // Send proof of execution on chain. + calls.push(ParentchainCall::Litentry(OpaqueCall::from_tuple(&( + node_metadata_repo + .get_from_metadata(|m| m.publish_hash_call_indexes()) + .map_err(|_| StfError::InvalidMetadata)? + .map_err(|_| StfError::InvalidMetadata)?, + call_hash, + Vec::::new(), + b"shielded some funds!".to_vec(), + )))); + Ok(TrustedCallResult::Empty) + }, + #[cfg(feature = "evm")] + TrustedCall::evm_withdraw(from, address, value) => { + debug!("evm_withdraw({}, {}, {})", account_id_to_string(&from), address, value); + ita_sgx_runtime::EvmCall::::withdraw { address, value } + .dispatch_bypass_filter(ita_sgx_runtime::RuntimeOrigin::signed( + from.to_account_id().ok_or(Self::Error::InvalidAccount)?, + )) + .map_err(|e| { + Self::Error::Dispatch(format!("Evm Withdraw error: {:?}", e.error)) + })?; + Ok(TrustedCallResult::Empty) + }, + #[cfg(feature = "evm")] + TrustedCall::evm_call( + from, + source, + target, + input, + value, + gas_limit, + max_fee_per_gas, + max_priority_fee_per_gas, + nonce, + access_list, + ) => { + debug!( + "evm_call(from: {}, source: {}, target: {})", + account_id_to_string(&from), + source, + target + ); + ita_sgx_runtime::EvmCall::::call { + source, + target, + input, + value, + gas_limit, + max_fee_per_gas, + max_priority_fee_per_gas, + nonce, + access_list, + } + .dispatch_bypass_filter(ita_sgx_runtime::RuntimeOrigin::signed( + from.to_account_id().ok_or(Self::Error::InvalidAccount)?, + )) + .map_err(|e| Self::Error::Dispatch(format!("Evm Call error: {:?}", e.error)))?; + Ok(TrustedCallResult::Empty) + }, + #[cfg(feature = "evm")] + TrustedCall::evm_create( + from, + source, + init, + value, + gas_limit, + max_fee_per_gas, + max_priority_fee_per_gas, + nonce, + access_list, + ) => { + debug!( + "evm_create(from: {}, source: {}, value: {})", + account_id_to_string(&from), + source, + value + ); + let nonce_evm_account = + System::account_nonce(&HashedAddressMapping::into_account_id(source)); + ita_sgx_runtime::EvmCall::::create { + source, + init, + value, + gas_limit, + max_fee_per_gas, + max_priority_fee_per_gas, + nonce, + access_list, + } + .dispatch_bypass_filter(ita_sgx_runtime::RuntimeOrigin::signed( + from.to_account_id().ok_or(Self::Error::InvalidAccount)?, + )) + .map_err(|e| Self::Error::Dispatch(format!("Evm Create error: {:?}", e.error)))?; + let contract_address = evm_create_address(source, nonce_evm_account); + info!("Trying to create evm contract with address {:?}", contract_address); + Ok(TrustedCallResult::Empty) + }, + #[cfg(feature = "evm")] + TrustedCall::evm_create2( + from, + source, + init, + salt, + value, + gas_limit, + max_fee_per_gas, + max_priority_fee_per_gas, + nonce, + access_list, + ) => { + debug!( + "evm_create2(from: {}, source: {}, value: {})", + account_id_to_string(&from), + source, + value + ); + let code_hash = create_code_hash(&init); + ita_sgx_runtime::EvmCall::::create2 { + source, + init, + salt, + value, + gas_limit, + max_fee_per_gas, + max_priority_fee_per_gas, + nonce, + access_list, + } + .dispatch_bypass_filter(ita_sgx_runtime::RuntimeOrigin::signed( + from.to_account_id().ok_or(Self::Error::InvalidAccount)?, + )) + .map_err(|e| Self::Error::Dispatch(format!("Evm Create2 error: {:?}", e.error)))?; + let contract_address = evm_create2_address(source, salt, code_hash); + info!("Trying to create evm contract with address {:?}", contract_address); + Ok(TrustedCallResult::Empty) + }, + } + } + + fn get_storage_hashes_to_update(self) -> Vec> { + debug!("No storage updates needed..."); + Vec::new() + } +} + +fn burn_funds(account: AccountId, amount: u128) -> Result<(), StfError> { + let account_info = System::account(&account); + if account_info.data.free < amount { + return Err(StfError::MissingFunds) + } + + ita_sgx_runtime::BalancesCall::::force_set_balance { + who: MultiAddress::Id(account), + new_free: account_info.data.free - amount, + } + .dispatch_bypass_filter(ita_sgx_runtime::RuntimeOrigin::root()) + .map_err(|e| StfError::Dispatch(format!("Burn funds error: {:?}", e.error)))?; + Ok(()) +} + +fn shield_funds(account: AccountId, amount: u128) -> Result<(), StfError> { + //fixme: make fee configurable and send fee to vault account on L2 + let fee = amount / 571; // approx 0.175% + + // endow fee to enclave (self) + let fee_recipient: AccountId = enclave_signer_account(); + + let account_info = System::account(&fee_recipient); + ita_sgx_runtime::BalancesCall::::force_set_balance { + who: MultiAddress::Id(fee_recipient), + new_free: account_info.data.free + fee, + } + .dispatch_bypass_filter(ita_sgx_runtime::RuntimeOrigin::root()) + .map_err(|e| StfError::Dispatch(format!("Shield funds error: {:?}", e.error)))?; + + // endow shieding amount - fee to beneficiary + let account_info = System::account(&account); + ita_sgx_runtime::BalancesCall::::force_set_balance { + who: MultiAddress::Id(account), + new_free: account_info.data.free + amount - fee, + } + .dispatch_bypass_filter(ita_sgx_runtime::RuntimeOrigin::root()) + .map_err(|e| StfError::Dispatch(format!("Shield funds error: {:?}", e.error)))?; + + Ok(()) +} + +pub(crate) fn is_root(account: &AccountId) -> bool +where + Runtime: frame_system::Config + pallet_sudo::Config, + AccountId: PartialEq, +{ + pallet_sudo::Pallet::::key().map_or(false, |k| account == &k) +} + +pub fn push_call_imp_some_error( + calls: &mut Vec, + node_metadata_repo: Arc, + identity: Option, + e: IMPError, + req_ext_hash: H256, +) where + NodeMetadataRepository: AccessNodeMetadata, + NodeMetadataRepository::MetadataType: NodeMetadataTrait, +{ + debug!("pushing IMP::some_error call ..."); + // TODO: anyway to simplify this? `and_then` won't be applicable here + match node_metadata_repo.get_from_metadata(|m| m.imp_some_error_call_indexes()) { + Ok(Ok(call_index)) => calls.push(ParentchainCall::Litentry(OpaqueCall::from_tuple(&( + call_index, + identity, + e, + req_ext_hash, + )))), + Ok(e) => warn!("error getting IMP::some_error call indexes: {:?}", e), + Err(e) => warn!("error getting IMP::some_error call indexes: {:?}", e), + } +} + +pub fn push_call_vcmp_some_error( + calls: &mut Vec, + node_metadata_repo: Arc, + identity: Option, + e: VCMPError, + req_ext_hash: H256, +) where + NodeMetadataRepository: AccessNodeMetadata, + NodeMetadataRepository::MetadataType: NodeMetadataTrait, +{ + debug!("pushing VCMP::some_error call ..."); + match node_metadata_repo.get_from_metadata(|m| m.vcmp_some_error_call_indexes()) { + Ok(Ok(call_index)) => calls.push(ParentchainCall::Litentry(OpaqueCall::from_tuple(&( + call_index, + identity, + e, + req_ext_hash, + )))), + Ok(e) => warn!("error getting VCMP::some_error call indexes: {:?}", e), + Err(e) => warn!("error getting VCMP::some_error call indexes: {:?}", e), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use itp_stf_primitives::types::KeyPair; + use sp_keyring::AccountKeyring; + + #[test] + fn verify_signature_works() { + let nonce = 21; + let mrenclave = [0u8; 32]; + let shard = ShardIdentifier::default(); + + let call = TrustedCall::balance_set_balance( + AccountKeyring::Alice.public().into(), + AccountKeyring::Alice.public().into(), + 42, + 42, + ); + let signed_call = call.sign( + &KeyPair::Sr25519(Box::new(AccountKeyring::Alice.pair())), + nonce, + &mrenclave, + &shard, + ); + + assert!(signed_call.verify_signature(&mrenclave, &shard)); + } +} diff --git a/bitacross-worker/app-libs/stf/src/trusted_call_result.rs b/bitacross-worker/app-libs/stf/src/trusted_call_result.rs new file mode 100644 index 0000000000..f9cbbeff95 --- /dev/null +++ b/bitacross-worker/app-libs/stf/src/trusted_call_result.rs @@ -0,0 +1,44 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +// This file contain the RPC response struct which will be encoded and +// passed back to the requester of trustedCall direct invocation (DI). +// They are mostly translated from the callback extrinsics in IMP. + +use codec::{Decode, Encode}; +use itp_stf_interface::StfExecutionResult; +use std::vec::Vec; + +#[derive(Encode, Decode, Debug)] +pub enum TrustedCallResult { + #[codec(index = 0)] + Empty, + #[codec(index = 1)] + Streamed, +} + +impl StfExecutionResult for TrustedCallResult { + fn get_encoded_result(self) -> Vec { + match self { + Self::Empty => Vec::default(), + Self::Streamed => Vec::default(), + } + } + + fn force_connection_wait(&self) -> bool { + matches!(self, Self::Streamed) + } +} diff --git a/bitacross-worker/bin/README.md b/bitacross-worker/bin/README.md new file mode 100644 index 0000000000..9cf10b5eb8 --- /dev/null +++ b/bitacross-worker/bin/README.md @@ -0,0 +1 @@ +Output directory for the binaries \ No newline at end of file diff --git a/bitacross-worker/build.Dockerfile b/bitacross-worker/build.Dockerfile new file mode 100644 index 0000000000..f417478865 --- /dev/null +++ b/bitacross-worker/build.Dockerfile @@ -0,0 +1,122 @@ +# syntax=docker/dockerfile:1 +# Copyright 2021 Integritee AG +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This is a multi-stage docker file, where the first stage is used +# for building and the second deploys the built application. + +### Builder Stage +################################################## +# todo: we might need to change this image in future +FROM litentry/litentry-tee-dev:latest AS builder +LABEL maintainer="Trust Computing GmbH " + +# set environment variables +ENV SGX_SDK /opt/sgxsdk +ENV PATH "$PATH:${SGX_SDK}/bin:${SGX_SDK}/bin/x64:/opt/rust/bin" +ENV PKG_CONFIG_PATH "${PKG_CONFIG_PATH}:${SGX_SDK}/pkgconfig" +ENV LD_LIBRARY_PATH "${LD_LIBRARY_PATH}:${SGX_SDK}/sdk_libs" +ENV CARGO_NET_GIT_FETCH_WITH_CLI true + +ENV SCCACHE_CACHE_SIZE="20G" +ENV SCCACHE_DIR="/opt/rust/sccache" +ENV RUSTC_WRAPPER="/opt/rust/bin/sccache" + +# Default SGX MODE is software mode +ARG SGX_MODE=SW +ENV SGX_MODE=$SGX_MODE + +ARG SGX_PRODUCTION=0 +ENV SGX_PRODUCTION=$SGX_PRODUCTION + +ENV HOME=/home/ubuntu + +ARG WORKER_MODE_ARG +ENV WORKER_MODE=$WORKER_MODE_ARG + +ARG ADDITIONAL_FEATURES_ARG +ENV ADDITIONAL_FEATURES=$ADDITIONAL_FEATURES_ARG + +ARG FINGERPRINT=none + +WORKDIR $HOME/bitacross-worker +COPY . $HOME + +RUN \ + rm -rf /opt/rust/registry/cache && mv /home/ubuntu/worker-cache/registry/cache /opt/rust/registry && \ + rm -rf /opt/rust/registry/index && mv /home/ubuntu/worker-cache/registry/index /opt/rust/registry && \ + rm -rf /opt/rust/git/db && mv /home/ubuntu/worker-cache/git/db /opt/rust/git && \ + rm -rf /opt/rust/sccache && mv /home/ubuntu/worker-cache/sccache /opt/rust && \ + make && sccache --show-stats + +RUN cargo test --release + + +### Base Runner Stage +################################################## +FROM node:18-bookworm-slim AS runner + +RUN apt update && apt install -y libssl-dev iproute2 jq curl +RUN corepack enable && corepack prepare pnpm@8.7.6 --activate && corepack enable pnpm + + +### Deployed CLI client +################################################## +FROM runner AS deployed-client +LABEL maintainer="Trust Computing GmbH " + +ARG SCRIPT_DIR=/usr/local/worker-cli +ARG LOG_DIR=/usr/local/log + +ENV SCRIPT_DIR ${SCRIPT_DIR} +ENV LOG_DIR ${LOG_DIR} + +COPY --from=local-builder:latest /home/ubuntu/bitacross-worker/bin/bitacross-cli /usr/local/bin +COPY --from=local-builder:latest /home/ubuntu/bitacross-worker/cli/*.sh /usr/local/worker-cli/ + +RUN chmod +x /usr/local/bin/bitacross-cli ${SCRIPT_DIR}/*.sh +RUN mkdir ${LOG_DIR} + +RUN ldd /usr/local/bin/bitacross-cli && /usr/local/bin/bitacross-cli --version + +ENTRYPOINT ["/usr/local/bin/bitacross-cli"] + + +### Deployed worker service +################################################## +FROM runner AS deployed-worker +LABEL maintainer="Trust Computing GmbH " + +WORKDIR /usr/local/bin + +COPY --from=local-builder:latest /opt/sgxsdk /opt/sgxsdk +COPY --from=local-builder:latest /home/ubuntu/bitacross-worker/bin/* /usr/local/bin +COPY --from=local-builder:latest /home/ubuntu/bitacross-worker/cli/*.sh /usr/local/worker-cli/ +COPY --from=local-builder:latest /lib/x86_64-linux-gnu/libsgx* /lib/x86_64-linux-gnu/ +COPY --from=local-builder:latest /lib/x86_64-linux-gnu/libdcap* /lib/x86_64-linux-gnu/ + +RUN touch spid.txt key.txt +RUN chmod +x /usr/local/bin/bitacross-worker +RUN ls -al /usr/local/bin + +# checks +ENV SGX_SDK /opt/sgxsdk +ENV SGX_ENCLAVE_SIGNER $SGX_SDK/bin/x64/sgx_sign +ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/intel/sgx-aesm-service/aesm:$SGX_SDK/sdk_libs +ENV AESM_PATH=/opt/intel/sgx-aesm-service/aesm + +RUN ldd /usr/local/bin/bitacross-worker && /usr/local/bin/bitacross-worker --version + +# TODO: use entrypoint and aesm service launch, see P-295 too +ENTRYPOINT ["/usr/local/bin/bitacross-worker"] \ No newline at end of file diff --git a/bitacross-worker/cli/Cargo.toml b/bitacross-worker/cli/Cargo.toml new file mode 100644 index 0000000000..6e42a13d52 --- /dev/null +++ b/bitacross-worker/cli/Cargo.toml @@ -0,0 +1,69 @@ +[package] +name = "bitacross-cli" +version = "0.0.1" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +array-bytes = { version = "6.0.0" } +base58 = "0.2" +chrono = "*" +clap = { version = "=4.1.0", features = ["derive"] } +codec = { version = "3.0.0", package = "parity-scale-codec", features = ["derive"] } +env_logger = "0.9" +hdrhistogram = "7.5.0" +hex = "0.4.2" +log = "0.4" +rand = "0.8.5" +rayon = "1.5.1" +regex = "1.9.5" +reqwest = { version = "0.11", features = ["blocking", "json"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +sgx_crypto_helper = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } +thiserror = "1.0" +urlencoding = "2.1.3" + +# scs / integritee +pallet-evm = { optional = true, git = "https://github.com/integritee-network/frontier.git", branch = "bar/polkadot-v0.9.42" } +# `default-features = false` to remove the jsonrpsee dependency. +substrate-api-client = { default-features = false, features = ["std", "sync-api"], git = "https://github.com/scs/substrate-api-client.git", branch = "polkadot-v0.9.42-tag-v0.14.0" } +substrate-client-keystore = { git = "https://github.com/scs/substrate-api-client.git", branch = "polkadot-v0.9.42-tag-v0.14.0" } + +# substrate dependencies +pallet-balances = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-application-crypto = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-keyring = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-keystore = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-runtime = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# local dependencies +ita-stf = { path = "../app-libs/stf" } +itc-rpc-client = { path = "../core/rpc-client" } +itp-node-api = { path = "../core-primitives/node-api" } +itp-rpc = { path = "../core-primitives/rpc" } +itp-sgx-crypto = { path = "../core-primitives/sgx/crypto" } +itp-stf-primitives = { path = "../core-primitives/stf-primitives" } +itp-time-utils = { path = "../core-primitives/time-utils" } +itp-types = { path = "../core-primitives/types" } +itp-utils = { path = "../core-primitives/utils" } + +# litentry +frame-metadata = "15.0.0" +ita-sgx-runtime = { path = "../app-libs/sgx-runtime" } +litentry-primitives = { path = "../litentry/primitives" } +my-node-runtime = { package = "rococo-parachain-runtime", path = "../../runtime/rococo" } +pallet-teerex = { path = "../../pallets/teerex", default-features = false } +scale-value = "0.6.0" +sp-core-hashing = "6.0.0" + +[features] +default = [] +evm = ["ita-stf/evm_std", "pallet-evm"] +teeracle = [] +sidechain = [] +offchain-worker = [] +production = [] +# dcap feature flag is not used in this crate, but for easier build purposes only it present here as well +dcap = [] diff --git a/bitacross-worker/cli/README.md b/bitacross-worker/cli/README.md new file mode 100644 index 0000000000..a1eb6463f5 --- /dev/null +++ b/bitacross-worker/cli/README.md @@ -0,0 +1,35 @@ +# Integritee CLI client +Interact with the Integritee chain and workers from the command line + +Includes +* keystore (incompatible with polkadot js app json) +* basic balance transfer +* Integritee-specific calls + +## examples +``` +> ./bitacross-cli transfer //Bob //Alice 12345 +> ./bitacross-cli -u ws://127.0.0.1 list-workers +number of workers registered: 1 +Enclave 1 + AccountId: 5HN8RGEiJuc9iNA3vfiYj7Lk6ULWzBZXvSDheohBu3usSUqn + MRENCLAVE: 4GMb72Acyg8hnnnGEJ89jZK5zxNC4LvSe2ME96wLRV6J + RA timestamp: 2022-03-16 10:43:12.001 UTC + URL: wss://127.0.0.1:2345 +> ./bitacross-cli -P 2345 trusted --direct --mrenclave 4GMb72Acyg8hnnn +GE4LvSe2ME96wLRV6J unshield-funds //Bob //Alice 12345 +from ss58 is 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty +to ss58 is 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY +send trusted call unshield_funds from 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty to 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY: 12345 +Trusted call 0x69ddfd1698bd2d629180c2dca34ce7add087526c51f43cf68245241b3f13154e is Submitted +Trusted call 0x69ddfd1698bd2d629180c2dca34ce7add087526c51f43cf68245241b3f13154e is Invalid + +``` + +## housekeeping tasks + +populate all TCBinfo's Intel has published +``` +../target/release/bitacross-cli register-tcb-info //Alice --fmspc 00606a000000 +../target/release/bitacross-cli register-tcb-info //Alice --all +``` diff --git a/bitacross-worker/cli/benchmark.sh b/bitacross-worker/cli/benchmark.sh new file mode 100755 index 0000000000..080651fdc6 --- /dev/null +++ b/bitacross-worker/cli/benchmark.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +while getopts ":m:p:A:u:V:C:" opt; do + case $opt in + m) + READMRENCLAVE=$OPTARG + ;; + p) + NPORT=$OPTARG + ;; + A) + WORKER1PORT=$OPTARG + ;; + u) + NODEURL=$OPTARG + ;; + V) + WORKER1URL=$OPTARG + ;; + C) + CLIENT_BIN=$OPTARG + ;; + *) + ;; + esac +done + +# using default port if none given as arguments +NPORT=${NPORT:-9944} +NODEURL=${NODEURL:-"ws://127.0.0.1"} + +WORKER1PORT=${WORKER1PORT:-2000} +WORKER1URL=${WORKER1URL:-"wss://127.0.0.1"} + +CLIENT_BIN=${CLIENT_BIN:-"./../bin/bitacross-cli"} + +echo "Using client binary ${CLIENT_BIN}" +echo "Using node uri ${NODEURL}:${NPORT}" +echo "Using trusted-worker uri ${WORKER1URL}:${WORKER1PORT}" + +CLIENTWORKER1="${CLIENT_BIN} -p ${NPORT} -P ${WORKER1PORT} -u ${NODEURL} -U ${WORKER1URL}" + +if [ "$READMRENCLAVE" = "file" ] +then + read -r MRENCLAVE <<< "$(cat ~/mrenclave.b58)" + echo "Reading MRENCLAVE from file: ${MRENCLAVE}" +else + # this will always take the first MRENCLAVE found in the registry !! + read -r MRENCLAVE <<< "$($CLIENTWORKER1 list-workers | awk '/ MRENCLAVE: / { print $2; exit }')" + echo "Reading MRENCLAVE from worker list: ${MRENCLAVE}" +fi +[[ -z $MRENCLAVE ]] && { echo "MRENCLAVE is empty. cannot continue" ; exit 1; } + +# needed when many clients are started +ulimit -S -n 4096 + +echo "Starting benchmark" +${CLIENTWORKER1} trusted --direct --mrenclave "${MRENCLAVE}" benchmark 20 100 -w +echo "" + +exit 0 diff --git a/bitacross-worker/cli/demo_direct_call.sh b/bitacross-worker/cli/demo_direct_call.sh new file mode 100755 index 0000000000..a3c816bd93 --- /dev/null +++ b/bitacross-worker/cli/demo_direct_call.sh @@ -0,0 +1,144 @@ +#!/bin/bash + +# Executes a direct call on a worker and checks the balance afterwards. +# +# setup: +# run all on localhost: +# litentry-node purge-chain --dev +# litentry-node --tmp --dev -lruntime=debug +# rm light_client_db.bin +# export RUST_LOG=litentry_worker=info,ita_stf=debug +# bitacross-worker init_shard +# bitacross-worker shielding-key +# bitacross-worker signing-key +# bitacross-worker run +# +# then run this script + +# usage: +# demo_direct_call.sh -p -P -t -m file +# +# TEST_BALANCE_RUN is either "first" or "second" +# if -m file is set, the mrenclave will be read from file + +while getopts ":m:p:P:t:u:V:C:" opt; do + case $opt in + t) + TEST=$OPTARG + ;; + m) + READ_MRENCLAVE=$OPTARG + ;; + p) + LITENTRY_RPC_PORT=$OPTARG + ;; + P) + WORKER_1_PORT=$OPTARG + ;; + u) + LITENTRY_RPC_URL=$OPTARG + ;; + V) + WORKER_1_URL=$OPTARG + ;; + C) + CLIENT_BIN=$OPTARG + ;; + *) + echo "invalid arg ${OPTARG}" + exit 1 + esac +done + +# Using default port if none given as arguments. +LITENTRY_RPC_PORT=${LITENTRY_RPC_PORT:-9944} +LITENTRY_RPC_URL=${LITENTRY_RPC_URL:-"ws://127.0.0.1"} + +WORKER_1_PORT=${WORKER_1_PORT:-2000} +WORKER_1_URL=${WORKER_1_URL:-"wss://127.0.0.1"} + +CLIENT_BIN=${CLIENT_BIN:-"./../bin/bitacross-cli"} + +echo "Using client binary ${CLIENT_BIN}" +${CLIENT_BIN} --version +echo "Using node uri ${LITENTRY_RPC_URL}:${LITENTRY_RPC_PORT}" +echo "Using trusted-worker uri ${WORKER_1_URL}:${WORKER_1_PORT}" +echo "" + + +AMOUNTSHIELD=50000000000 +AMOUNTTRANSFER=40000000000 + +CLIENT="${CLIENT_BIN} -p ${LITENTRY_RPC_PORT} -P ${WORKER_1_PORT} -u ${LITENTRY_RPC_URL} -U ${WORKER_1_URL}" +read -r MRENCLAVE <<< "$($CLIENT list-workers | awk '/ MRENCLAVE: / { print $2; exit }')" + +echo "" +echo "* Create a new incognito account for Alice" +ICGACCOUNTALICE=//AliceIncognito +ICGACCOUNTALICE_PUBKEY=0x50503350955afe8a107d6f115dc253eb5d75a3fe37a90b373db26cc12e3c6661 +echo " Alice's incognito account = ${ICGACCOUNTALICE}" +echo "" + +echo "* Create a new incognito account for Bob" +ICGACCOUNTBOB=//BobIncognito +ICGACCOUNTBOB_PUBKEY=0xc24c5b3969d8ec4ca8a655a98dcc136d5d4c29d1206ffe7721e80ebdfa1d0b77 +echo " Bob's incognito account = ${ICGACCOUNTBOB}" +echo "" + +echo "* Issue ${AMOUNTSHIELD} tokens to Alice's incognito account" +${CLIENT} trusted --mrenclave ${MRENCLAVE} --direct set-balance ${ICGACCOUNTALICE} ${AMOUNTSHIELD} +echo "" + +echo "Get balance of Alice's incognito account" +${CLIENT} trusted --mrenclave ${MRENCLAVE} balance ${ICGACCOUNTALICE} +echo "" + +# Send funds from Alice to Bob's account. +echo "* Send ${AMOUNTTRANSFER} funds from Alice's incognito account to Bob's incognito account" +$CLIENT trusted --mrenclave ${MRENCLAVE} --direct transfer ${ICGACCOUNTALICE} ${ICGACCOUNTBOB} ${AMOUNTTRANSFER} +echo "" + +# Prevent getter being executed too early and returning an outdated result, before the transfer was made. +echo "* Waiting 6 seconds" +sleep 6 +echo "" + +echo "* Get balance of Alice's incognito account" +# RESULT=$(${CLIENT} trusted --mrenclave ${MRENCLAVE} balance ${ICGACCOUNTALICE} | xargs) +RESULT=$(${CLIENT} trusted --mrenclave ${MRENCLAVE} get-storage System Account ${ICGACCOUNTALICE_PUBKEY} | jq ".data.free" | xargs) +echo $RESULT +echo "" + +echo "* Bob's incognito account balance" +# RESULT=$(${CLIENT} trusted --mrenclave ${MRENCLAVE} balance ${ICGACCOUNTBOB} | xargs) +RESULT=$(${CLIENT} trusted --mrenclave ${MRENCLAVE} get-storage System Account ${ICGACCOUNTBOB_PUBKEY} | jq ".data.free" | xargs) +echo $RESULT +echo "" + + +# The following tests are for automated CI. +# They only work if you're running from fresh genesis. +case $TEST in + first) + if [ "40000000000" = "$RESULT" ]; then + echo "test passed (1st time)" + echo "" + exit 0 + else + echo "test ran through but balance is wrong. have you run the script from fresh genesis?" + exit 1 + fi + ;; + second) + if [ "80000000000" = "$RESULT" ]; then + echo "test passed (2nd time)" + echo "" + exit 0 + else + echo "test ran through but balance is wrong. is this really the second time you run this since genesis?" + exit 1 + fi + ;; +esac + +exit 0 diff --git a/bitacross-worker/cli/demo_direct_call_2_workers.sh b/bitacross-worker/cli/demo_direct_call_2_workers.sh new file mode 100755 index 0000000000..3375efa0fd --- /dev/null +++ b/bitacross-worker/cli/demo_direct_call_2_workers.sh @@ -0,0 +1,63 @@ +#!/bin/bash +set -euo pipefail + +# Runs the `demo_direct_call.sh` twice once with worker1 and worker2. +# This verifies that the two workers are successfully sharing state updates +# by broadcasting sidechain blocks. +# +# It does the same as `scripts/m8.sh`, but is mainly used in the docker tests. + +while getopts ":p:A:B:u:W:V:C:" opt; do + case $opt in + p) + NPORT=$OPTARG + ;; + A) + WORKER1PORT=$OPTARG + ;; + B) + WORKER2PORT=$OPTARG + ;; + u) + NODEURL=$OPTARG + ;; + V) + WORKER1URL=$OPTARG + ;; + W) + WORKER2URL=$OPTARG + ;; + C) + CLIENT_BIN=$OPTARG + ;; + *) + echo "invalid arg ${OPTARG}" + exit 1 + esac +done + +# Using default port if none given as arguments. +NPORT=${NPORT:-9944} +NODEURL=${NODEURL:-"ws://127.0.0.1"} + +WORKER1PORT=${WORKER1PORT:-2000} +WORKER1URL=${WORKER1URL:-"wss://127.0.0.1"} + +WORKER2PORT=${WORKER2PORT:-3000} +WORKER2URL=${WORKER2URL:-"wss://127.0.0.1"} + +CLIENT_BIN=${CLIENT_BIN:-"./../bin/bitacross-cli"} + +echo "Using client binary ${CLIENT_BIN}" +${CLIENT_BIN} --version +echo "Using node uri ${NODEURL}:${NPORT}" +echo "Using trusted-worker uri 1 ${WORKER1URL}:${WORKER1PORT}" +echo "Using trusted-worker uri 2 ${WORKER2URL}:${WORKER2PORT}" +echo "" + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) + +"${SCRIPT_DIR}"/demo_direct_call.sh -p "${NPORT}" -u "${NODEURL}" -V "${WORKER1URL}" -P "${WORKER1PORT}" -C "${CLIENT_BIN}" -t first +"${SCRIPT_DIR}"/demo_direct_call.sh -p "${NPORT}" -u "${NODEURL}" -V "${WORKER2URL}" -P "${WORKER2PORT}" -C "${CLIENT_BIN}" -t second + +exit 0 diff --git a/bitacross-worker/cli/demo_shielding_unshielding.sh b/bitacross-worker/cli/demo_shielding_unshielding.sh new file mode 100755 index 0000000000..33ccc1b394 --- /dev/null +++ b/bitacross-worker/cli/demo_shielding_unshielding.sh @@ -0,0 +1,275 @@ +#!/bin/bash + +# to make sure the script aborts when (sub-)function exits abnormally +set -e + +# Demonstrates how to shield tokens from the parentchain into the sidechain. +# +# setup: +# run all on localhost: +# litentry-node purge-chain --dev +# litentry-node --dev -lruntime=debug +# rm light_client_db.bin +# export RUST_LOG=litentry_worker=info,ita_stf=debug +# bitacross-worker init_shard +# bitacross-worker shielding-key +# bitacross-worker signing-key +# bitacross-worker run +# +# then run this script + +# usage: +# demo_shielding_unshielding.sh -p -P -t -m file +# +# TEST_BALANCE_RUN is either "first" or "second" +# if -m file is set, the mrenclave will be read from file + +while getopts ":m:p:P:t:u:V:C:" opt; do + case $opt in + t) + TEST=$OPTARG + ;; + m) + READ_MRENCLAVE=$OPTARG + ;; + p) + LITENTRY_RPC_PORT=$OPTARG + ;; + P) + WORKER_1_PORT=$OPTARG + ;; + u) + LITENTRY_RPC_URL=$OPTARG + ;; + V) + WORKER_1_URL=$OPTARG + ;; + C) + CLIENT_BIN=$OPTARG + ;; + *) + echo "invalid arg ${OPTARG}" + exit 1 + esac +done + +# Using default port if none given as arguments. +LITENTRY_RPC_PORT=${LITENTRY_RPC_PORT:-9944} +LITENTRY_RPC_URL=${LITENTRY_RPC_URL:-"ws://127.0.0.1"} + +WORKER_1_PORT=${WORKER_1_PORT:-2000} +WORKER_1_URL=${WORKER_1_URL:-"wss://127.0.0.1"} + +CLIENT_BIN=${CLIENT_BIN:-"./../bin/bitacross-cli"} + +echo "Using client binary ${CLIENT_BIN}" +${CLIENT_BIN} --version +echo "Using node uri ${LITENTRY_RPC_URL}:${LITENTRY_RPC_PORT}" +echo "Using trusted-worker uri ${WORKER_1_URL}:${WORKER_1_PORT}" +echo "" + +# the parentchain token is 12 decimal +UNIT=$(( 10 ** 12 )) +FEE_TOLERANCE=$((10 ** 11)) + +# we have to make these amounts greater than ED, see +# https://github.com/litentry/litentry-parachain/issues/1162 +AMOUNT_SHIELD=$(( 6 * UNIT )) +AMOUNT_TRANSFER=$(( 2 * UNIT )) +AMOUNT_UNSHIELD=$(( 1 * UNIT )) + +CLIENT="${CLIENT_BIN} -p ${LITENTRY_RPC_PORT} -P ${WORKER_1_PORT} -u ${LITENTRY_RPC_URL} -U ${WORKER_1_URL}" + +# interval and max rounds to wait to check the given account balance in sidechain +WAIT_INTERVAL_SECONDS=10 +WAIT_ROUNDS=20 + +# Do a live query and assert the given account's balance is equal to expected +# usage: +# assert_account_balance +function assert_account_balance() +{ + for i in $(seq 1 $WAIT_ROUNDS); do + state=$(${CLIENT} trusted --mrenclave "$1" balance "$2") + if (( $3 >= state ? $3 - state < FEE_TOLERANCE : state - $3 < FEE_TOLERANCE)); then + return + else + sleep $WAIT_INTERVAL_SECONDS + fi + done + echo + echo "Assert $2 failed, expected = $3, actual = $state, tolerance = $FEE_TOLERANCE" + exit 1 +} + + +# Do a live query and assert the given account's nonce is equal to expected +# usage: +# assert_account_nonce +function assert_account_nonce() +{ + for i in $(seq 1 $WAIT_ROUNDS); do + state=$(${CLIENT} trusted --mrenclave "$1" nonce "$2") + echo $state + if [ $state -eq "$3" ]; then + return + else + sleep $WAIT_INTERVAL_SECONDS + fi + done + echo + echo "Assert $2 failed, expected = $3, actual = $state" + exit 1 +} + +# Do a live query and assert the given account's state is equal to expected +# usage: +# assert_account_state +function assert_account_state() +{ + state=$(${CLIENT} trusted --mrenclave "$1" get-storage System Account "$2" | jq "$3") + if [ -z "$state" ]; then + echo "Query Account $2 $3 failed" + exit 1 + fi + + if [ $state -eq "$4" ]; then + return + fi + echo + echo "Assert $2 $3 failed, expected = $4, actual = $state" + exit 1 +} + +echo "* Query on-chain enclave registry:" +${CLIENT} list-workers +echo "" + +if [ "$READ_MRENCLAVE" = "file" ] +then + read MRENCLAVE <<< $(cat ~/mrenclave.b58) + echo "Reading MRENCLAVE from file: ${MRENCLAVE}" +else + # this will always take the first MRENCLAVE found in the registry !! + read MRENCLAVE <<< $($CLIENT list-workers | awk '/ MRENCLAVE: / { print $2; exit }') + echo "Reading MRENCLAVE from worker list: ${MRENCLAVE}" +fi +[[ -z $MRENCLAVE ]] && { echo "MRENCLAVE is empty. cannot continue" ; exit 1; } + +echo "* Create a new incognito account for Alice" +ICGACCOUNTALICE=//AliceIncognito +echo " Alice's incognito account = ${ICGACCOUNTALICE}" +echo "" + +# Asssert the initial balance of Alice incognito +# We create different (new) accounts for Bob incognito, hence his initial balance is always 0 +BALANCE_INCOGNITO_ALICE=0 +case $TEST in + first) + assert_account_balance ${MRENCLAVE} ${ICGACCOUNTALICE} 0 + ICGACCOUNTBOB=//BobIncognitoFirst ;; + second) + assert_account_balance ${MRENCLAVE} ${ICGACCOUNTALICE} $(( AMOUNT_SHIELD - AMOUNT_TRANSFER - AMOUNT_UNSHIELD )) + BALANCE_INCOGNITO_ALICE=$(( AMOUNT_SHIELD - AMOUNT_TRANSFER - AMOUNT_UNSHIELD )) + ICGACCOUNTBOB=//BobIncognitoSecond ;; + *) + echo "unsupported test mode" + exit 1 ;; +esac + +echo "* Create a new incognito account for Bob" +echo " Bob's incognito account = ${ICGACCOUNTBOB}" +echo "" + +echo "* Shield ${AMOUNT_SHIELD} tokens to Alice's incognito account" +${CLIENT} shield-funds //Alice ${ICGACCOUNTALICE} ${AMOUNT_SHIELD} ${MRENCLAVE} +echo "" + +echo "* Wait and assert Alice's incognito account balance... " +assert_account_balance ${MRENCLAVE} ${ICGACCOUNTALICE} $(( BALANCE_INCOGNITO_ALICE + AMOUNT_SHIELD )) +echo "✔ ok" + +echo "* Wait and assert Bob's incognito account balance... " +assert_account_balance ${MRENCLAVE} ${ICGACCOUNTBOB} 0 +echo "✔ ok" +echo "" + +echo "* Send ${AMOUNT_TRANSFER} funds from Alice's incognito account to Bob's incognito account" +$CLIENT trusted --mrenclave ${MRENCLAVE} transfer ${ICGACCOUNTALICE} ${ICGACCOUNTBOB} ${AMOUNT_TRANSFER} +echo "" + +echo "* Wait and assert Alice's incognito account balance... " +assert_account_balance ${MRENCLAVE} ${ICGACCOUNTALICE} $(( BALANCE_INCOGNITO_ALICE + AMOUNT_SHIELD - AMOUNT_TRANSFER )) +echo "✔ ok" + +echo "* Wait and assert Bob's incognito account balance... " +assert_account_balance ${MRENCLAVE} ${ICGACCOUNTBOB} ${AMOUNT_TRANSFER} +echo "✔ ok" +echo "" + +echo "* Un-shield ${AMOUNT_UNSHIELD} tokens from Alice's incognito account to Ferie's L1 account" +${CLIENT} trusted --mrenclave ${MRENCLAVE} unshield-funds ${ICGACCOUNTALICE} //Ferdie ${AMOUNT_UNSHIELD} +echo "" + +echo "* Wait and assert Alice's incognito account balance... " +assert_account_balance ${MRENCLAVE} ${ICGACCOUNTALICE} $(( BALANCE_INCOGNITO_ALICE + AMOUNT_SHIELD - AMOUNT_TRANSFER - AMOUNT_UNSHIELD )) +echo "✔ ok" + +echo "* Wait and assert Bob's incognito account balance... " +assert_account_balance ${MRENCLAVE} ${ICGACCOUNTBOB} ${AMOUNT_TRANSFER} +echo "✔ ok" + +# Test the nonce handling, using Bob's incognito account as the sender as Alice's +# balance needs to be verified in the second round while Bob is newly created each time + +echo "* Create a new incognito account for Charlie" +ICGACCOUNTCHARLIE=$(${CLIENT} trusted --mrenclave ${MRENCLAVE} new-account) +echo " Charlie's incognito account = ${ICGACCOUNTCHARLIE}" +echo "" + +echo "* Assert Bob's incognito initial nonce..." +assert_account_nonce ${MRENCLAVE} ${ICGACCOUNTBOB} 0 +echo "✔ ok" +echo "" + +echo "* Send 3 consecutive 0.2 UNIT balance Transfer Bob -> Charlie" +for i in $(seq 1 3); do + # use direct calls so they are submitted to the top pool synchronously + $CLIENT trusted --direct --mrenclave ${MRENCLAVE} transfer ${ICGACCOUNTBOB} ${ICGACCOUNTCHARLIE} $(( AMOUNT_TRANSFER / 10 )) +done +echo "" + +echo "* Assert Bob's incognito current nonce..." +assert_account_nonce ${MRENCLAVE} ${ICGACCOUNTBOB} 3 +echo "✔ ok" +echo "" + +echo "* Send a 2 UNIT balance Transfer Bob -> Charlie (that will fail)" +$CLIENT trusted --direct --mrenclave ${MRENCLAVE} transfer ${ICGACCOUNTBOB} ${ICGACCOUNTCHARLIE} ${AMOUNT_TRANSFER} || true +echo "" + +echo "* Assert Bob's incognito nonce..." +# the nonce should be increased nontheless, even for the failed tx +assert_account_nonce ${MRENCLAVE} ${ICGACCOUNTBOB} 4 +echo "✔ ok" +echo "" + +echo "* Send another 0.2 UNIT balance Transfer Bob -> Charlie" +$CLIENT trusted --direct --mrenclave ${MRENCLAVE} transfer ${ICGACCOUNTBOB} ${ICGACCOUNTCHARLIE} $(( AMOUNT_TRANSFER / 10 )) +echo "" + +echo "* Assert Bob's incognito nonce..." +assert_account_nonce ${MRENCLAVE} ${ICGACCOUNTBOB} 5 +echo "✔ ok" +echo "" + +echo "* Wait and assert Bob's incognito account balance... " +# in total 4 balance transfer should go through => 1.2 UNIT remaining +assert_account_balance ${MRENCLAVE} ${ICGACCOUNTBOB} $(( AMOUNT_TRANSFER * 6 / 10 )) +echo "✔ ok" + +echo "" +echo "-----------------------" +echo "✔ The $TEST test passed!" +echo "-----------------------" +echo "" diff --git a/bitacross-worker/cli/demo_shielding_unshielding_multiworker.sh b/bitacross-worker/cli/demo_shielding_unshielding_multiworker.sh new file mode 100755 index 0000000000..476a64a87d --- /dev/null +++ b/bitacross-worker/cli/demo_shielding_unshielding_multiworker.sh @@ -0,0 +1,69 @@ +#!/bin/bash +set -euo pipefail + +# Runs the direct call demo twice, with worker 1 and worker 2. +# +# It does the same as `./scripts/m6.sh`, but is mainly used in the docker tests. + +while getopts ":p:A:B:u:W:V:C:" opt; do + case $opt in + p) + INTEGRITEE_RPC_PORT=$OPTARG + ;; + A) + WORKER_1_PORT=$OPTARG + ;; + B) + WORKER_2_PORT=$OPTARG + ;; + u) + INTEGRITEE_RPC_URL=$OPTARG + ;; + V) + WORKER_1_URL=$OPTARG + ;; + W) + WORKER_2_URL=$OPTARG + ;; + C) + CLIENT_BIN=$OPTARG + ;; + *) + echo "invalid arg ${OPTARG}" + exit 1 + esac +done + +# Using default port if none given as arguments. +INTEGRITEE_RPC_PORT=${INTEGRITEE_RPC_PORT:-9944} +INTEGRITEE_RPC_URL=${INTEGRITEE_RPC_URL:-"ws://127.0.0.1"} + +WORKER_1_PORT=${WORKER_1_PORT:-2000} +WORKER_1_URL=${WORKER_1_URL:-"wss://127.0.0.1"} + +WORKER_2_PORT=${WORKER_2_PORT:-3000} +WORKER_2_URL=${WORKER_2_URL:-"wss://127.0.0.1"} + +CLIENT_BIN=${CLIENT_BIN:-"./../bin/integritee-cli"} + +echo "Using client binary ${CLIENT_BIN}" +${CLIENT_BIN} --version +echo "Using node uri ${INTEGRITEE_RPC_URL}:${INTEGRITEE_RPC_PORT}" +echo "Using trusted-worker 1 uri ${WORKER_1_URL}:${WORKER_1_PORT}" +echo "Using trusted-worker 2 uri ${WORKER_2_URL}:${WORKER_2_PORT}" +echo "" + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) + +"${SCRIPT_DIR}"/demo_shielding_unshielding.sh -p "${INTEGRITEE_RPC_PORT}" -u "${INTEGRITEE_RPC_URL}" -V "${WORKER_1_URL}" -P "${WORKER_1_PORT}" -C "${CLIENT_BIN}" -t first +"${SCRIPT_DIR}"/demo_shielding_unshielding.sh -p "${INTEGRITEE_RPC_PORT}" -u "${INTEGRITEE_RPC_URL}" -V "${WORKER_2_URL}" -P "${WORKER_2_PORT}" -C "${CLIENT_BIN}" -t second + +if [ "$FLAVOR_ID" = offchain-worker ]; then + echo "offchain-worker does not support shard vault shielding, therefore we skip those tests" + exit 0 +fi + +"${SCRIPT_DIR}"/demo_shielding_unshielding_using_shard_vault.sh -p "${INTEGRITEE_RPC_PORT}" -u "${INTEGRITEE_RPC_URL}" -V "${WORKER_1_URL}" -P "${WORKER_1_PORT}" -C "${CLIENT_BIN}" -t first +"${SCRIPT_DIR}"/demo_shielding_unshielding_using_shard_vault.sh -p "${INTEGRITEE_RPC_PORT}" -u "${INTEGRITEE_RPC_URL}" -V "${WORKER_2_URL}" -P "${WORKER_2_PORT}" -C "${CLIENT_BIN}" -t second + +exit 0 \ No newline at end of file diff --git a/bitacross-worker/cli/demo_shielding_unshielding_using_shard_vault.sh b/bitacross-worker/cli/demo_shielding_unshielding_using_shard_vault.sh new file mode 100755 index 0000000000..82399f7cc3 --- /dev/null +++ b/bitacross-worker/cli/demo_shielding_unshielding_using_shard_vault.sh @@ -0,0 +1,266 @@ +#!/bin/bash + +# to make sure the script aborts when (sub-)function exits abnormally +set -e + +# Demonstrates how to shield tokens from the parentchain into the sidechain. +# +# setup: +# run all on localhost: +# integritee-node purge-chain --dev +# integritee-node --dev -lruntime=debug +# rm light_client_db.bin +# export RUST_LOG=integritee_service=info,ita_stf=debug +# integritee-service init_shard +# integritee-service shielding-key +# integritee-service signing-key +# integritee-service run +# +# then run this script + +# usage: +# demo_shielding_unshielding.sh -p -P -t -m file +# +# TEST_BALANCE_RUN is either "first" or "second" +# if -m file is set, the mrenclave will be read from file + +while getopts ":m:p:P:t:u:V:C:" opt; do + case $opt in + t) + TEST=$OPTARG + ;; + m) + READ_MRENCLAVE=$OPTARG + ;; + p) + INTEGRITEE_RPC_PORT=$OPTARG + ;; + P) + WORKER_1_PORT=$OPTARG + ;; + u) + INTEGRITEE_RPC_URL=$OPTARG + ;; + V) + WORKER_1_URL=$OPTARG + ;; + C) + CLIENT_BIN=$OPTARG + ;; + *) + echo "invalid arg ${OPTARG}" + exit 1 + esac +done + +# Using default port if none given as arguments. +INTEGRITEE_RPC_PORT=${INTEGRITEE_RPC_PORT:-9944} +INTEGRITEE_RPC_URL=${INTEGRITEE_RPC_URL:-"ws://127.0.0.1"} + +WORKER_1_PORT=${WORKER_1_PORT:-2000} +WORKER_1_URL=${WORKER_1_URL:-"wss://127.0.0.1"} + +CLIENT_BIN=${CLIENT_BIN:-"./../bin/integritee-cli"} + +echo "Using client binary ${CLIENT_BIN}" +${CLIENT_BIN} --version +echo "Using node uri ${INTEGRITEE_RPC_URL}:${INTEGRITEE_RPC_PORT}" +echo "Using trusted-worker uri ${WORKER_1_URL}:${WORKER_1_PORT}" +echo "" + +# the parentchain token is 12 decimal +UNIT=$(( 10 ** 12 )) +FEE_TOLERANCE=$((10 ** 11)) + +# make these amounts greater than ED +AMOUNT_SHIELD=$(( 6 * UNIT )) +AMOUNT_TRANSFER=$(( 2 * UNIT )) +AMOUNT_UNSHIELD=$(( 1 * UNIT )) + +CLIENT="${CLIENT_BIN} -p ${INTEGRITEE_RPC_PORT} -P ${WORKER_1_PORT} -u ${INTEGRITEE_RPC_URL} -U ${WORKER_1_URL}" + +# offchain-worker only suppports indirect calls +CALLTYPE= +case "$FLAVOR_ID" in + sidechain) CALLTYPE="--direct" ;; + offchain-worker) : ;; + *) CALLTYPE="--direct" ;; +esac +echo "using call type: ${CALLTYPE} (empty means indirect)" + +# interval and max rounds to wait to check the given account balance in sidechain +WAIT_INTERVAL_SECONDS=10 +WAIT_ROUNDS=20 + +# Poll and assert the given account's state is equal to expected, +# with timeout WAIT_INTERVAL_SECONDS * WAIT_ROUNDS +# usage: +# wait_assert_state +# the `state-name` has to be the supported subcommand, e.g. `balance`, `nonce` +function wait_assert_state() +{ + for i in $(seq 1 $WAIT_ROUNDS); do + sleep $WAIT_INTERVAL_SECONDS + state=$(${CLIENT} trusted --mrenclave "$1" "$3" "$2") + if (( $4 >= state ? $4 - state < FEE_TOLERANCE : state - $4 < FEE_TOLERANCE)); then + return + else + : + fi + done + echo + echo "Assert $2 $3 failed, expected = $4, actual = $state, tolerance = $FEE_TOLERANCE" + exit 1 +} + +# Do a live query and assert the given account's state is equal to expected +# usage: +# assert_state +function assert_state() +{ + state=$(${CLIENT} trusted --mrenclave "$1" "$3" "$2") + if [ -z "$state" ]; then + echo "Query $2 $3 failed" + exit 1 + fi + + if [ $state -eq "$4" ]; then + return + fi + echo + echo "Assert $2 $3 failed, expected = $4, actual = $state" + exit 1 +} + +echo "* Query on-chain enclave registry:" +${CLIENT} list-workers +echo "" + +if [ "$READ_MRENCLAVE" = "file" ] +then + read MRENCLAVE <<< $(cat ~/mrenclave.b58) + echo "Reading MRENCLAVE from file: ${MRENCLAVE}" +else + # this will always take the first MRENCLAVE found in the registry !! + read MRENCLAVE <<< $($CLIENT list-workers | awk '/ MRENCLAVE: / { print $2; exit }') + echo "Reading MRENCLAVE from worker list: ${MRENCLAVE}" +fi +[[ -z $MRENCLAVE ]] && { echo "MRENCLAVE is empty. cannot continue" ; exit 1; } + + +echo "* Create a new incognito account for Bob" +ICGACCOUNTBOB=$(${CLIENT} trusted --mrenclave ${MRENCLAVE} new-account) +echo " Bob's incognito account = ${ICGACCOUNTBOB}" +echo "" + +echo "* Query shard vault account" +VAULT=$(${CLIENT} trusted get-shard-vault) +echo " shard vault account = ${VAULT}" +echo "" + +# Asssert the initial balance of Charlie incognito +# The initial balance of Bob incognito should always be 0, as Bob is newly created +BALANCE_INCOGNITO_CHARLIE=0 +case $TEST in + first) + wait_assert_state ${MRENCLAVE} //Charlie balance 0 ;; + second) + wait_assert_state ${MRENCLAVE} //Charlie balance $(( AMOUNT_SHIELD - AMOUNT_TRANSFER - AMOUNT_UNSHIELD )) + BALANCE_INCOGNITO_CHARLIE=$(( AMOUNT_SHIELD - AMOUNT_TRANSFER - AMOUNT_UNSHIELD )) ;; + *) + echo "assuming first run of test" + wait_assert_state ${MRENCLAVE} //Charlie balance 0 ;; +esac + +echo "* Shield ${AMOUNT_SHIELD} tokens to Charlie's account on L2" +${CLIENT} transfer //Charlie ${VAULT} ${AMOUNT_SHIELD} +echo "" + +echo "* Wait and assert Charlie's L2 account balance... " +wait_assert_state ${MRENCLAVE} //Charlie balance $(( BALANCE_INCOGNITO_CHARLIE + AMOUNT_SHIELD )) +echo "✔ ok" + +echo "* Wait and assert Bob's incognito account balance... " +wait_assert_state ${MRENCLAVE} ${ICGACCOUNTBOB} balance 0 +echo "✔ ok" +echo "" + +echo "* Send ${AMOUNT_TRANSFER} funds from Charlie's L2 account to Bob's incognito account" +$CLIENT trusted --mrenclave ${MRENCLAVE} transfer //Charlie ${ICGACCOUNTBOB} ${AMOUNT_TRANSFER} +echo "" + +echo "* Wait and assert Charlie's L2 account balance... " +wait_assert_state ${MRENCLAVE} //Charlie balance $(( BALANCE_INCOGNITO_CHARLIE + AMOUNT_SHIELD - AMOUNT_TRANSFER )) +echo "✔ ok" + +echo "* Wait and assert Bob's incognito account balance... " +wait_assert_state ${MRENCLAVE} ${ICGACCOUNTBOB} balance ${AMOUNT_TRANSFER} +echo "✔ ok" +echo "" + +echo "* Un-shield ${AMOUNT_UNSHIELD} tokens from Charlie's incognito account to Ferie's L1 account" +${CLIENT} trusted --mrenclave ${MRENCLAVE} unshield-funds //Charlie //Ferdie ${AMOUNT_UNSHIELD} +echo "" + +echo "* Wait and assert Charlie's incognito account balance... " +wait_assert_state ${MRENCLAVE} //Charlie balance $(( BALANCE_INCOGNITO_CHARLIE + AMOUNT_SHIELD - AMOUNT_TRANSFER - AMOUNT_UNSHIELD )) +echo "✔ ok" + +echo "* Wait and assert Bob's incognito account balance... " +wait_assert_state ${MRENCLAVE} ${ICGACCOUNTBOB} balance ${AMOUNT_TRANSFER} +echo "✔ ok" + +# Test the nonce handling, using Bob's incognito account as the sender as Charlie's +# balance needs to be verified in the second round while Bob is newly created each time + +echo "* Create a new incognito account for Charlie" +ICGACCOUNTCHARLIE=$(${CLIENT} trusted --mrenclave ${MRENCLAVE} new-account) +echo " Charlie's incognito account = ${ICGACCOUNTCHARLIE}" +echo "" + +echo "* Assert Bob's incognito initial nonce..." +assert_state ${MRENCLAVE} ${ICGACCOUNTBOB} nonce 0 +echo "✔ ok" +echo "" + +echo "* Send 3 consecutive 0.2 UNIT balance Transfer Bob -> Charlie" +for i in $(seq 1 3); do + # use direct calls so they are submitted to the top pool synchronously + $CLIENT trusted $CALLTYPE --mrenclave ${MRENCLAVE} transfer ${ICGACCOUNTBOB} ${ICGACCOUNTCHARLIE} $(( AMOUNT_TRANSFER / 10 )) +done +echo "" + +echo "* Assert Bob's incognito current nonce..." +wait_assert_state ${MRENCLAVE} ${ICGACCOUNTBOB} nonce 3 +echo "✔ ok" +echo "" + +echo "* Send a 2 UNIT balance Transfer Bob -> Charlie (that will fail)" +$CLIENT trusted $CALLTYPE --mrenclave ${MRENCLAVE} transfer ${ICGACCOUNTBOB} ${ICGACCOUNTCHARLIE} ${AMOUNT_TRANSFER} +echo "" + +echo "* Assert Bob's incognito nonce..." +# the nonce should be increased nontheless, even for the failed tx +wait_assert_state ${MRENCLAVE} ${ICGACCOUNTBOB} nonce 4 +echo "✔ ok" +echo "" + +echo "* Send another 0.2 UNIT balance Transfer Bob -> Charlie" +$CLIENT trusted $CALLTYPE --mrenclave ${MRENCLAVE} transfer ${ICGACCOUNTBOB} ${ICGACCOUNTCHARLIE} $(( AMOUNT_TRANSFER / 10 )) +echo "" + +echo "* Assert Bob's incognito nonce..." +wait_assert_state ${MRENCLAVE} ${ICGACCOUNTBOB} nonce 5 +echo "✔ ok" +echo "" + +echo "* Wait and assert Bob's incognito account balance... " +# in total 4 balance transfer should go through => 1.2 UNIT remaining +wait_assert_state ${MRENCLAVE} ${ICGACCOUNTBOB} balance $(( AMOUNT_TRANSFER * 6 / 10 )) +echo "✔ ok" + +echo "" +echo "-----------------------" +echo "✔ The $TEST test passed!" +echo "-----------------------" +echo "" \ No newline at end of file diff --git a/bitacross-worker/cli/demo_shielding_unshielding_using_shard_vault_on_target_a.sh b/bitacross-worker/cli/demo_shielding_unshielding_using_shard_vault_on_target_a.sh new file mode 100755 index 0000000000..9ccf34c84d --- /dev/null +++ b/bitacross-worker/cli/demo_shielding_unshielding_using_shard_vault_on_target_a.sh @@ -0,0 +1,302 @@ +#!/bin/bash + +# to make sure the script aborts when (sub-)function exits abnormally +set -e + +# Demonstrates how to shield tokens from the parentchain into the sidechain. +# +# setup: +# run all on localhost: +# integritee-node purge-chain --dev +# integritee-node --dev -lruntime=debug +# rm light_client_db.bin +# export RUST_LOG=integritee_service=info,ita_stf=debug +# integritee-service init_shard +# integritee-service shielding-key +# integritee-service signing-key +# integritee-service run +# +# then run this script + +# usage: +# demo_shielding_unshielding.sh -p -P -t -m file +# +# TEST_BALANCE_RUN is either "first" or "second" +# if -m file is set, the mrenclave will be read from file + +while getopts ":m:p:P:t:u:V:C:a:A:" opt; do + case $opt in + t) + TEST=$OPTARG + ;; + m) + READ_MRENCLAVE=$OPTARG + ;; + p) + INTEGRITEE_RPC_PORT=$OPTARG + ;; + a) + TARGET_A_RPC_PORT=$OPTARG + ;; + P) + WORKER_1_PORT=$OPTARG + ;; + u) + INTEGRITEE_RPC_URL=$OPTARG + ;; + A) + TARGET_A_RPC_URL=$OPTARG + ;; + V) + WORKER_1_URL=$OPTARG + ;; + C) + CLIENT_BIN=$OPTARG + ;; + *) + echo "invalid arg ${OPTARG}" + exit 1 + esac +done + +# Using default port if none given as arguments. +INTEGRITEE_RPC_PORT=${INTEGRITEE_RPC_PORT:-9944} +INTEGRITEE_RPC_URL=${INTEGRITEE_RPC_URL:-"ws://127.0.0.1"} + +TARGET_A_RPC_PORT=${TARGET_A_RPC_PORT:-9954} +TARGET_A_RPC_URL=${TARGET_A_RPC_URL:-"ws://127.0.0.1"} + +WORKER_1_PORT=${WORKER_1_PORT:-2000} +WORKER_1_URL=${WORKER_1_URL:-"wss://127.0.0.1"} + +CLIENT_BIN=${CLIENT_BIN:-"./../bin/integritee-cli"} + +echo "Using client binary ${CLIENT_BIN}" +${CLIENT_BIN} --version +echo "Using integritee node uri ${INTEGRITEE_RPC_URL}:${INTEGRITEE_RPC_PORT}" +echo "Using target_a node uri ${TARGET_A_RPC_URL}:${TARGET_A_RPC_PORT}" +echo "Using trusted-worker uri ${WORKER_1_URL}:${WORKER_1_PORT}" +echo "" + +# the parentchain token is 12 decimal +UNIT=$(( 10 ** 12 )) +FEE_TOLERANCE=$((10 ** 11)) + +# make these amounts greater than ED +AMOUNT_SHIELD=$(( 6 * UNIT )) +AMOUNT_TRANSFER=$(( 2 * UNIT )) +AMOUNT_UNSHIELD=$(( 1 * UNIT )) + +CLIENT="${CLIENT_BIN} -p ${INTEGRITEE_RPC_PORT} -P ${WORKER_1_PORT} -u ${INTEGRITEE_RPC_URL} -U ${WORKER_1_URL}" + +# for talking to TARGET_A L1 +CLIENT_A="${CLIENT_BIN} -p ${TARGET_A_RPC_PORT} -P ${WORKER_1_PORT} -u ${TARGET_A_RPC_URL} -U ${WORKER_1_URL}" + +# offchain-worker only suppports indirect calls +CALLTYPE= +case "$FLAVOR_ID" in + sidechain) CALLTYPE="--direct" ;; + offchain-worker) : ;; + *) CALLTYPE="--direct" ;; +esac +echo "using call type: ${CALLTYPE} (empty means indirect)" + +# interval and max rounds to wait to check the given account balance in sidechain +WAIT_INTERVAL_SECONDS=6 +WAIT_ROUNDS=20 + +# Poll and assert the given account's state is equal to expected, +# with timeout WAIT_INTERVAL_SECONDS * WAIT_ROUNDS +# usage: +# wait_assert_state +# the `state-name` has to be the supported subcommand, e.g. `balance`, `nonce` +function wait_assert_state() +{ + for i in $(seq 1 $WAIT_ROUNDS); do + sleep $WAIT_INTERVAL_SECONDS + state=$(${CLIENT} trusted --mrenclave "$1" "$3" "$2") + if (( $4 >= state ? $4 - state < FEE_TOLERANCE : state - $4 < FEE_TOLERANCE)); then + return + else + echo -n "." + fi + done + echo + echo "Assert $2 $3 failed, expected = $4, actual = $state, tolerance = $FEE_TOLERANCE" + exit 1 +} + +function wait_assert_state_target_a() +{ + for i in $(seq 1 $WAIT_ROUNDS); do + sleep $WAIT_INTERVAL_SECONDS + state=$(${CLIENT_A} "$2" "$1") + if (( $4 >= state ? $4 - state < FEE_TOLERANCE : state - $4 < FEE_TOLERANCE)); then + return + else + echo -n "." + fi + done + echo + echo "Assert $2 $3 failed, expected = $4, actual = $state, tolerance = $FEE_TOLERANCE" + exit 1 +} +# Do a live query and assert the given account's state is equal to expected +# usage: +# assert_state +function assert_state() +{ + state=$(${CLIENT} trusted --mrenclave "$1" "$3" "$2") + if [ -z "$state" ]; then + echo "Query $2 $3 failed" + exit 1 + fi + + if [ $state -eq "$4" ]; then + return + fi + echo + echo "Assert $2 $3 failed, expected = $4, actual = $state" + exit 1 +} + +echo "* Query on-chain enclave registry:" +${CLIENT} list-workers +echo "" + +if [ "$READ_MRENCLAVE" = "file" ] +then + read MRENCLAVE <<< $(cat ~/mrenclave.b58) + echo "Reading MRENCLAVE from file: ${MRENCLAVE}" +else + # this will always take the first MRENCLAVE found in the registry !! + read MRENCLAVE <<< $($CLIENT list-workers | awk '/ MRENCLAVE: / { print $2; exit }') + echo "Reading MRENCLAVE from worker list: ${MRENCLAVE}" +fi +[[ -z $MRENCLAVE ]] && { echo "MRENCLAVE is empty. cannot continue" ; exit 1; } + + +echo "* Create a new incognito account for Bob" +ICGACCOUNTBOB=$(${CLIENT} trusted --mrenclave ${MRENCLAVE} new-account) +echo " Bob's incognito account = ${ICGACCOUNTBOB}" +echo "" + +echo "* Query shard vault account" +VAULT=$(${CLIENT} trusted get-shard-vault) +echo " shard vault account = ${VAULT}" +echo "" + +# Asssert the initial balance of Charlie incognito +# The initial balance of Bob incognito should always be 0, as Bob is newly created +BALANCE_INCOGNITO_CHARLIE=0 +BALANCE_A_FERDIE=$(${CLIENT_A} balance //Ferdie) + +case $TEST in + first) + wait_assert_state ${MRENCLAVE} //Charlie balance 0 ;; + second) + wait_assert_state ${MRENCLAVE} //Charlie balance $(( AMOUNT_SHIELD - AMOUNT_TRANSFER - AMOUNT_UNSHIELD )) + BALANCE_INCOGNITO_CHARLIE=$(( AMOUNT_SHIELD - AMOUNT_TRANSFER - AMOUNT_UNSHIELD )) ;; + *) + echo "assuming first run of test" + wait_assert_state ${MRENCLAVE} //Charlie balance 0 ;; +esac + +echo "* Shield ${AMOUNT_SHIELD} tokens from TARGET_A to Charlie's account on L2" +${CLIENT_A} transfer //Alice //Charlie $((AMOUNT_SHIELD * 2)) +${CLIENT_A} transfer //Charlie ${VAULT} ${AMOUNT_SHIELD} +echo "" + +echo "* Wait and assert Charlie's L2 account balance... " +wait_assert_state ${MRENCLAVE} //Charlie balance $(( BALANCE_INCOGNITO_CHARLIE + AMOUNT_SHIELD )) +echo "✔ ok" + +echo "* Wait and assert Bob's incognito account balance... " +wait_assert_state ${MRENCLAVE} ${ICGACCOUNTBOB} balance 0 +echo "✔ ok" +echo "" + +echo "* Send ${AMOUNT_TRANSFER} funds from Charlie's L2 account to Bob's incognito account" +$CLIENT trusted $CALLTYPE --mrenclave ${MRENCLAVE} transfer //Charlie ${ICGACCOUNTBOB} ${AMOUNT_TRANSFER} +echo "" + +echo "* Wait and assert Charlie's L2 account balance... " +wait_assert_state ${MRENCLAVE} //Charlie balance $(( BALANCE_INCOGNITO_CHARLIE + AMOUNT_SHIELD - AMOUNT_TRANSFER )) +echo "✔ ok" + +echo "* Wait and assert Bob's incognito account balance... " +wait_assert_state ${MRENCLAVE} ${ICGACCOUNTBOB} balance ${AMOUNT_TRANSFER} +echo "✔ ok" +echo "" + +echo "* Un-shield ${AMOUNT_UNSHIELD} tokens from Charlie's incognito account to Ferie's L1 account" +${CLIENT} trusted $CALLTYPE --mrenclave ${MRENCLAVE} unshield-funds //Charlie //Ferdie ${AMOUNT_UNSHIELD} +echo "" + +echo "* Wait and assert Charlie's incognito account balance... " +wait_assert_state ${MRENCLAVE} //Charlie balance $(( BALANCE_INCOGNITO_CHARLIE + AMOUNT_SHIELD - AMOUNT_TRANSFER - AMOUNT_UNSHIELD )) +echo "✔ ok" + +echo "* Wait and assert Ferdie's Target A account balance... " +wait_assert_state_target_a //Ferdie balance $(( BALANCE_A_FERDIE + AMOUNT_UNSHIELD )) +echo "✔ ok" + +echo "* Wait and assert Bob's incognito account balance... " +wait_assert_state ${MRENCLAVE} ${ICGACCOUNTBOB} balance ${AMOUNT_TRANSFER} +echo "✔ ok" + +# Test the nonce handling, using Bob's incognito account as the sender as Charlie's +# balance needs to be verified in the second round while Bob is newly created each time + +echo "* Create a new incognito account for Charlie" +ICGACCOUNTCHARLIE=$(${CLIENT} trusted --mrenclave ${MRENCLAVE} new-account) +echo " Charlie's incognito account = ${ICGACCOUNTCHARLIE}" +echo "" + + +echo "* Assert Bob's incognito initial nonce..." +assert_state ${MRENCLAVE} ${ICGACCOUNTBOB} nonce 0 +echo "✔ ok" +echo "" + +echo "* Send 3 consecutive 0.2 UNIT balance Transfer Bob -> Charlie" +for i in $(seq 1 3); do + # use direct calls so they are submitted to the top pool synchronously + $CLIENT trusted $CALLTYPE --mrenclave ${MRENCLAVE} transfer ${ICGACCOUNTBOB} ${ICGACCOUNTCHARLIE} $(( AMOUNT_TRANSFER / 10 )) +done +echo "" + +echo "* Assert Bob's incognito current nonce..." +wait_assert_state ${MRENCLAVE} ${ICGACCOUNTBOB} nonce 3 +echo "✔ ok" +echo "" + +echo "* Send a 2 UNIT balance Transfer Bob -> Charlie (that will fail)" +$CLIENT trusted $CALLTYPE --mrenclave ${MRENCLAVE} transfer ${ICGACCOUNTBOB} ${ICGACCOUNTCHARLIE} ${AMOUNT_TRANSFER} +echo "" + +echo "* Assert Bob's incognito nonce..." +# the nonce should be increased nontheless, even for the failed tx +wait_assert_state ${MRENCLAVE} ${ICGACCOUNTBOB} nonce 4 +echo "✔ ok" +echo "" + +echo "* Send another 0.2 UNIT balance Transfer Bob -> Charlie" +$CLIENT trusted $CALLTYPE --mrenclave ${MRENCLAVE} transfer ${ICGACCOUNTBOB} ${ICGACCOUNTCHARLIE} $(( AMOUNT_TRANSFER / 10 )) +echo "" + +echo "* Assert Bob's incognito nonce..." +wait_assert_state ${MRENCLAVE} ${ICGACCOUNTBOB} nonce 5 +echo "✔ ok" +echo "" + +echo "* Wait and assert Bob's incognito account balance... " +# in total 4 balance transfer should go through => 1.2 UNIT remaining +wait_assert_state ${MRENCLAVE} ${ICGACCOUNTBOB} balance $(( AMOUNT_TRANSFER * 6 / 10 )) +echo "✔ ok" + +echo "" +echo "-----------------------" +echo "✔ The $TEST test passed!" +echo "-----------------------" +echo "" diff --git a/bitacross-worker/cli/demo_sidechain.sh b/bitacross-worker/cli/demo_sidechain.sh new file mode 100755 index 0000000000..dceb28503e --- /dev/null +++ b/bitacross-worker/cli/demo_sidechain.sh @@ -0,0 +1,178 @@ +#!/bin/bash + +# Sidechain Demo: +# +# Demonstrates that transfers happening on worker1 are communicated via sidechain blocks to worker2. +# It does essentially the same as `m8.sh`, but in one script and more streamlined. +# +# setup: +# run all on localhost: +# litentry-node purge-chain --dev +# litentry-node --tmp --dev -lruntime=debug +# rm light_client_db.bin +# export RUST_LOG=bitacross_worker=info,ita_stf=debug +# bitacross-worker init_shard +# bitacross-worker shielding-key +# bitacross-worker signing-key +# bitacross-worker run +# +# Then run this script. +# +# usage: +# export RUST_LOG_LOG=bitacross-cli=info,ita_stf=info +# demo_sidechain.sh -p -A -B -m file +# +# TEST_BALANCE_RUN is either "first" or "second" +# if -m file is set, the mrenclave will be read from file. + +while getopts ":m:p:A:B:t:u:W:V:C:" opt; do + case $opt in + m) + READ_MRENCLAVE=$OPTARG + ;; + p) + LITENTRY_RPC_PORT=$OPTARG + ;; + A) + WORKER_1_PORT=$OPTARG + ;; + B) + WORKER_2_PORT=$OPTARG + ;; + u) + LITENTRY_RPC_URL=$OPTARG + ;; + V) + WORKER_1_URL=$OPTARG + ;; + W) + WORKER_2_URL=$OPTARG + ;; + C) + CLIENT_BIN=$OPTARG + ;; + *) + echo "invalid arg ${OPTARG}" + exit 1 + esac +done + +# Using default port if none given as arguments. +LITENTRY_RPC_PORT=${LITENTRY_RPC_PORT:-9944} +LITENTRY_RPC_URL=${LITENTRY_RPC_URL:-"ws://127.0.0.1"} + +WORKER_1_PORT=${WORKER_1_PORT:-2000} +WORKER_1_URL=${WORKER_1_URL:-"wss://127.0.0.1"} + +WORKER_2_PORT=${WORKER_2_PORT:-3000} +WORKER_2_URL=${WORKER_2_URL:-"wss://127.0.0.1"} + +CLIENT_BIN=${CLIENT_BIN:-"./../bin/bitacross-cli"} + +echo "Using client binary ${CLIENT_BIN}" +${CLIENT_BIN} --version +echo "Using node uri ${LITENTRY_RPC_URL}:${LITENTRY_RPC_PORT}" +echo "Using trusted-worker 1 uri ${WORKER_1_URL}:${WORKER_1_PORT}" +echo "Using trusted-worker 2 uri ${WORKER_2_URL}:${WORKER_2_PORT}" + +# the parentchain token is 12 decimal +UNIT=$(( 10 ** 12 )) +FEE_TOLERANCE=$((10 ** 11)) + +INITIALFUNDS=$((5 * UNIT)) +AMOUNTTRANSFER=$((2 * UNIT)) + +CLIENTWORKER1="${CLIENT_BIN} -p ${LITENTRY_RPC_PORT} -P ${WORKER_1_PORT} -u ${LITENTRY_RPC_URL} -U ${WORKER_1_URL}" +CLIENTWORKER2="${CLIENT_BIN} -p ${LITENTRY_RPC_PORT} -P ${WORKER_2_PORT} -u ${LITENTRY_RPC_URL} -U ${WORKER_2_URL}" + +if [ "$READ_MRENCLAVE" = "file" ] +then + read MRENCLAVE <<< $(cat ~/mrenclave.b58) + echo "Reading MRENCLAVE from file: ${MRENCLAVE}" +else + # This will always take the first MRENCLAVE found in the registry !! + read MRENCLAVE <<< $($CLIENTWORKER1 list-workers | awk '/ MRENCLAVE: / { print $2; exit }') + echo "Reading MRENCLAVE from worker list: ${MRENCLAVE}" +fi +[[ -z $MRENCLAVE ]] && { echo "MRENCLAVE is empty. cannot continue" ; exit 1; } + +echo "" +echo "* Create a new incognito account for Alice" +ICGACCOUNTALICE=//AliceIncognito +ICGACCOUNTALICE_PUBKEY=0x50503350955afe8a107d6f115dc253eb5d75a3fe37a90b373db26cc12e3c6661 +echo " Alice's incognito account = ${ICGACCOUNTALICE}" +echo "" + +echo "* Create a new incognito account for Bob" +ICGACCOUNTBOB=//BobIncognito +ICGACCOUNTBOB_PUBKEY=0xc24c5b3969d8ec4ca8a655a98dcc136d5d4c29d1206ffe7721e80ebdfa1d0b77 +echo " Bob's incognito account = ${ICGACCOUNTBOB}" +echo "" + +echo "* Issue ${INITIALFUNDS} tokens to Alice's incognito account (on worker 1)" +${CLIENTWORKER1} trusted --mrenclave ${MRENCLAVE} --direct set-balance ${ICGACCOUNTALICE} ${INITIALFUNDS} +echo "" + +# see bob's initial balance to 0 +${CLIENTWORKER1} trusted --mrenclave ${MRENCLAVE} --direct set-balance ${ICGACCOUNTBOB} 0 + +echo "Get balance of Alice's incognito account (on worker 1)" +# ${CLIENTWORKER1} trusted --mrenclave ${MRENCLAVE} balance ${ICGACCOUNTALICE} +# ICGACCOUNTALICE's public key is 0x50503350955afe8a107d6f115dc253eb5d75a3fe37a90b373db26cc12e3c6661 +${CLIENTWORKER1} trusted --mrenclave ${MRENCLAVE} get-storage System Account ${ICGACCOUNTALICE_PUBKEY} +echo "" + +# Send funds from Alice to Bobs account, on worker 1. +echo "* First transfer: Send ${AMOUNTTRANSFER} funds from Alice's incognito account to Bob's incognito account (on worker 1)" +$CLIENTWORKER1 trusted --mrenclave ${MRENCLAVE} --direct transfer ${ICGACCOUNTALICE} ${ICGACCOUNTBOB} ${AMOUNTTRANSFER} +echo "" + +# Prevent nonce clash when sending direct trusted calls to different workers. +echo "* Waiting 2 seconds" +sleep 2 +echo "" + +# Send funds from Alice to Bobs account, on worker 2. +echo "* Second transfer: Send ${AMOUNTTRANSFER} funds from Alice's incognito account to Bob's incognito account (on worker 2)" +$CLIENTWORKER2 trusted --mrenclave ${MRENCLAVE} --direct transfer ${ICGACCOUNTALICE} ${ICGACCOUNTBOB} ${AMOUNTTRANSFER} +echo "" + +# Prevent getter being executed too early and returning an outdated result, before the transfer was made. +echo "* Waiting 6 seconds" +sleep 6 +echo "" + +echo "* Get balance of Alice's incognito account (on worker 1)" +# ALICE_BALANCE=$(${CLIENTWORKER1} trusted --mrenclave ${MRENCLAVE} balance ${ICGACCOUNTALICE} | xargs) +ALICE_BALANCE=$(${CLIENTWORKER1} trusted --mrenclave ${MRENCLAVE} get-storage System Account ${ICGACCOUNTALICE_PUBKEY} | jq ".data.free" | xargs) +echo "$ALICE_BALANCE" +echo "" + +echo "* Get balance of Bob's incognito account (on worker 1)" +# BOB_BALANCE=$(${CLIENTWORKER1} trusted --mrenclave ${MRENCLAVE} balance ${ICGACCOUNTBOB} | xargs) +BOB_BALANCE=$(${CLIENTWORKER1} trusted --mrenclave ${MRENCLAVE} get-storage System Account ${ICGACCOUNTBOB_PUBKEY} | jq ".data.free" | xargs) +echo "$BOB_BALANCE" +echo "" + +ALICE_EXPECTED_BALANCE=$(( 1 * UNIT )) +BOB_EXPECTED_BALANCE=$(( 4 * UNIT )) + +echo "* Verifying Alice's balance" +if (( ALICE_BALANCE >= ALICE_EXPECTED_BALANCE ? ALICE_BALANCE - ALICE_EXPECTED_BALANCE > FEE_TOLERANCE : ALICE_EXPECTED_BALANCE - ALICE_BALANCE > FEE_TOLERANCE)); then + echo "Alice's balance is wrong (expected: $ALICE_EXPECTED_BALANCE, actual: $ALICE_BALANCE), tolerance = $FEE_TOLERANCE" + exit 1 +else + echo "Alice's balance is correct ($ALICE_BALANCE)" +fi +echo "" + +echo "* Verifying Bob's balance" +if [ "$BOB_BALANCE" -ne "$BOB_EXPECTED_BALANCE" ]; then + echo "Bob's balance is wrong (expected: $BOB_EXPECTED_BALANCE, actual: $BOB_BALANCE)" + exit 1 +else + echo "Bob's balance is correct ($BOB_BALANCE)" +fi +echo "" + +exit 0 \ No newline at end of file diff --git a/bitacross-worker/cli/demo_smart_contract.sh b/bitacross-worker/cli/demo_smart_contract.sh new file mode 100755 index 0000000000..dd0aad2508 --- /dev/null +++ b/bitacross-worker/cli/demo_smart_contract.sh @@ -0,0 +1,108 @@ +#!/bin/bash + +# Deploys a simple counter smart contract on our EVM sidechain and increments the value. +# +# setup: +# run all on localhost: +# litentry-node purge-chain --dev +# litentry-node --tmp --dev -lruntime=debug +# export RUST_LOG=litentry_worker=info,ita_stf=debug +# bitacross-worker run +# +# then run this script + +# usage: +# export RUST_LOG_LOG=bitacross-cli=info,ita_stf=info +# demo_smart_contract.sh -p -P + +while getopts ":p:A:u:V:C:" opt; do + case $opt in + p) + LITENTRY_RPC_PORT=$OPTARG + ;; + A) + WORKER_PORT=$OPTARG + ;; + u) + LITENTRY_RPC_URL=$OPTARG + ;; + V) + WORKER_URL=$OPTARG + ;; + C) + CLIENT_BIN=$OPTARG + ;; + *) + echo "invalid arg ${OPTARG}" + exit 1 + esac +done + +# Bytecode from Counter.sol with slightly modified values +SMARTCONTRACT="608060405234801561001057600080fd5b50602260008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610378806100696000396000f3fe6080604052600436106100435760003560e01c80631003e2d21461004e57806333cf508014610077578063371303c0146100a257806358992216146100b957610044565b5b6042600081905550005b34801561005a57600080fd5b50610075600480360381019061007091906101e4565b6100e4565b005b34801561008357600080fd5b5061008c610140565b604051610099919061024a565b60405180910390f35b3480156100ae57600080fd5b506100b7610149565b005b3480156100c557600080fd5b506100ce6101a5565b6040516100db919061022f565b60405180910390f35b806000808282546100f59190610265565b9250508190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60008054905090565b600160008082825461015b9190610265565b9250508190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b6000813590506101de8161032b565b92915050565b6000602082840312156101fa576101f9610326565b5b6000610208848285016101cf565b91505092915050565b61021a816102bb565b82525050565b610229816102ed565b82525050565b60006020820190506102446000830184610211565b92915050565b600060208201905061025f6000830184610220565b92915050565b6000610270826102ed565b915061027b836102ed565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156102b0576102af6102f7565b5b828201905092915050565b60006102c6826102cd565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b610334816102ed565b811461033f57600080fd5b5056fea26469706673582212206242c58933a5e80fcfdd7f0044569af44caa21c61740067483a287cc361fc5b464736f6c63430008070033" +INCFUNTION="371303c0" +DEFAULTFUNCTION="371303c1" +ADDFUNCTION="1003e2d20000000000000000000000000000000000000000000000000000000000000003" + + +# using default port if none given as arguments +LITENTRY_RPC_PORT=${LITENTRY_RPC_PORT:-9944} +LITENTRY_RPC_URL=${LITENTRY_RPC_URL:-"ws://127.0.0.1"} + +WORKER_PORT=${WORKER_PORT:-2000} +WORKER_URL=${WORKER_URL:-"wss://127.0.0.1"} + + +CLIENT_BIN=${CLIENT_BIN:-"./../bin/bitacross-cli"} + +echo "Using client binary ${CLIENT_BIN}" +${CLIENT_BIN} --version +echo "Using node uri ${LITENTRY_RPC_URL}:${LITENTRY_RPC_PORT}" +echo "Using trusted-worker uri ${WORKER_URL}:${WORKER_PORT}" + +CLIENTWORKER="${CLIENT_BIN} -p ${LITENTRY_RPC_PORT} -P ${WORKER_PORT} -u ${LITENTRY_RPC_URL} -U ${WORKER_URL}" + + +# this will always take the first MRENCLAVE found in the registry !! +read -r MRENCLAVE <<< "$($CLIENTWORKER list-workers | awk '/ MRENCLAVE: / { print $2; exit }')" +echo "Reading MRENCLAVE from worker list: ${MRENCLAVE}" + +ACCOUNTALICE=//Alice + +echo "Create smart contract" +${CLIENTWORKER} trusted --mrenclave ${MRENCLAVE} --direct evm-create ${ACCOUNTALICE} ${SMARTCONTRACT} +echo "" + +echo "Get storage" +${CLIENTWORKER} trusted --mrenclave ${MRENCLAVE} evm-read ${ACCOUNTALICE} 0x8a50db1e0f9452cfd91be8dc004ceb11cb08832f +echo "" + +echo "Call inc function" +${CLIENTWORKER} trusted --mrenclave ${MRENCLAVE} --direct evm-call ${ACCOUNTALICE} 0x8a50db1e0f9452cfd91be8dc004ceb11cb08832f ${INCFUNTION} +echo "" + +echo "Get storage" +${CLIENTWORKER} trusted --mrenclave ${MRENCLAVE} evm-read ${ACCOUNTALICE} 0x8a50db1e0f9452cfd91be8dc004ceb11cb08832f +echo "" + +echo "Call add 3 function" +${CLIENTWORKER} trusted --mrenclave ${MRENCLAVE} --direct evm-call ${ACCOUNTALICE} 0x8a50db1e0f9452cfd91be8dc004ceb11cb08832f ${ADDFUNCTION} +echo "" + +echo "Get storage" +RESULT=$(${CLIENTWORKER} trusted --mrenclave ${MRENCLAVE} evm-read ${ACCOUNTALICE} 0x8a50db1e0f9452cfd91be8dc004ceb11cb08832f | xargs) +echo $RESULT +echo "" + +EXPECTED_RETURN_VALUE="0x0000000000000000000000000000000000000000000000000000000000000026" + +echo "* Verifying correct return value" +if (("$RESULT" == "$EXPECTED_RETURN_VALUE")); then + echo "Smart contract return value is correct ($RESULT)" + exit 0 +else + echo "Smart contract return value is wrong (expected: $EXPECTED_RETURN_VALUE, actual: $RESULT)" + exit 1 +fi + +exit 0 diff --git a/bitacross-worker/cli/demo_teeracle_generic.sh b/bitacross-worker/cli/demo_teeracle_generic.sh new file mode 100755 index 0000000000..8c2de3bf87 --- /dev/null +++ b/bitacross-worker/cli/demo_teeracle_generic.sh @@ -0,0 +1,136 @@ +#!/bin/bash +set -euo pipefail + +trap "echo The demo is terminated (SIGINT); exit 1" SIGINT +trap "echo The demo is terminated (SIGTERM); exit 1" SIGTERM + +# Registers a teeracle with the parentchain, and publish some oracle data. +# +# Demo to show that an enclave can update the exchange rate only when +# 1. the enclave is registered at the pallet-teerex. +# 2. and that the code used is reliable -> the enclave has been put the teeracle whitelist via a governance or sudo +# call. +# +# The teeracle's whitelist has to be empty at the start. So the script needs to run with a clean node state. +# A registered mrenclave will be added in the whitelist by a sudo account. Here //Alice +# +# setup: +# run all on localhost: +# integritee-node purge-chain --dev +# integritee-node --dev -lpallet_teeracle=debug,parity_ws=error,aura=error,sc_basic_authorship=error +# integritee-service --clean-reset run (--skip-ra --dev) +# +# then run this script +# +# usage: +# demo_teeracle_generic.sh -p -P -d -i -u -V -C + +while getopts ":p:P:d:i:u:V:C:" opt; do + case $opt in + p) + LITENTRY_RPC_PORT=$OPTARG + ;; + P) + WORKER_1_PORT=$OPTARG + ;; + d) + DURATION=$OPTARG + ;; + i) + INTERVAL=$OPTARG + ;; + u) + LITENTRY_RPC_URL=$OPTARG + ;; + V) + WORKER_1_URL=$OPTARG + ;; + C) + CLIENT_BIN=$OPTARG + ;; + *) + echo "invalid arg ${OPTARG}" + exit 1 + esac +done + +# using default port if none given as arguments +LITENTRY_RPC_PORT=${LITENTRY_RPC_PORT:-9944} +LITENTRY_RPC_URL=${LITENTRY_RPC_URL:-"ws://127.0.0.1"} + +WORKER_1_PORT=${WORKER_1_PORT:-2000} +WORKER_1_URL=${WORKER_1_URL:-"wss://127.0.0.1"} + +CLIENT_BIN=${CLIENT_BIN:-"./../bin/bitacross-cli"} + +DURATION=${DURATION:-48} +INTERVAL=${INTERVAL:-86400} + +LISTEN_TO_ORACLE_EVENTS_CMD="oracle listen-to-oracle-events" +ADD_TO_WHITELIST_CMD="oracle add-to-whitelist" + +echo "Using client binary ${CLIENT_BIN}" +${CLIENT_BIN} --version +echo "Using node uri ${LITENTRY_RPC_URL}:${LITENTRY_RPC_PORT}" +echo "Using trusted-worker uri ${WORKER_1_URL}:${WORKER_1_PORT}" +echo "Using worker data update interval ${INTERVAL}" +echo "Count the update events for ${DURATION}" +echo "" + +OPEN_METEO="https://api.open-meteo.com/" +let "MIN_EXPECTED_NUM_OF_EVENTS=$DURATION/$INTERVAL-3" +echo "Minimum expected number of events with a single oracle source: ${MIN_EXPECTED_NUM_OF_EVENTS}" + +# let "MIN_EXPECTED_NUM_OF_EVENTS_2 = 2*$MIN_EXPECTED_NUM_OF_EVENTS" +# echo "Minimum expected number of events with two oracle sources: ${MIN_EXPECTED_NUM_OF_EVENTS_2}" + +CLIENT="${CLIENT_BIN} -p ${LITENTRY_RPC_PORT} -P ${WORKER_1_PORT} -u ${LITENTRY_RPC_URL} -U ${WORKER_1_URL}" + +echo "* Query on-chain enclave registry:" +${CLIENT} list-workers +echo "" + +# this will always take the first MRENCLAVE found in the registry !! +read MRENCLAVE <<< $($CLIENT list-workers | awk '/ MRENCLAVE: / { print $2; exit }') +echo "Reading MRENCLAVE from worker list: ${MRENCLAVE}" + +[[ -z $MRENCLAVE ]] && { echo "MRENCLAVE is empty. cannot continue" ; exit 1; } +echo "" + +echo "Listen to OracleUpdated events for ${DURATION} seconds. There should be no trusted oracle source!" + +read NO_EVENTS <<< $(${CLIENT} ${LISTEN_TO_ORACLE_EVENTS_CMD} ${DURATION} | awk '/ EVENTS_COUNT: / { print $2; exit }') +echo "Got ${NO_EVENTS} oracle updates when no trusted oracle source is in the whitelist" +echo "" + +echo "Add ${OPEN_METEO} for ${MRENCLAVE} as trusted oracle source" +${CLIENT} ${ADD_TO_WHITELIST_CMD} //Alice ${OPEN_METEO} ${MRENCLAVE} +echo "MRENCLAVE in whitelist for ${OPEN_METEO}" +echo "" + +echo "Listen to OracleUpdated events for ${DURATION} seconds, after a trusted oracle source has been added to the whitelist." +#${CLIENT} ${LISTEN_TO_ORACLE_EVENTS_CMD} ${DURATION} +#echo "" + +read EVENTS_COUNT <<< $($CLIENT ${LISTEN_TO_ORACLE_EVENTS_CMD} ${DURATION} | awk '/ EVENTS_COUNT: / { print $2; exit }') +echo "Got ${EVENTS_COUNT} oracle updates from the trusted oracle source in ${DURATION} second(s)" +echo "" + +echo "Results :" + +# the following test is for automated CI +# it only works if the teeracle's whitelist is empty at the start (run it from genesis) +if [ $EVENTS_COUNT -ge $MIN_EXPECTED_NUM_OF_EVENTS ]; then + if [ 0 -eq $NO_EVENTS ]; then + echo "test passed" + exit 0 + else + echo "The test ran through but we received OracleUpdated events before the enclave was added to the whitelist. Was the enclave previously whitelisted? Perhaps by another teeracle?" + exit 1 + fi +else +echo "test failed: Not enough events received for single oracle source: $EVENTS_COUNT. Should be greater than $MIN_EXPECTED_NUM_OF_EVENTS" +exit 1 +fi + +exit 1 diff --git a/bitacross-worker/cli/demo_teeracle_whitelist.sh b/bitacross-worker/cli/demo_teeracle_whitelist.sh new file mode 100755 index 0000000000..cfe48f8545 --- /dev/null +++ b/bitacross-worker/cli/demo_teeracle_whitelist.sh @@ -0,0 +1,157 @@ +#!/bin/bash +set -euo pipefail + +trap "echo The demo is terminated (SIGINT); exit 1" SIGINT +trap "echo The demo is terminated (SIGTERM); exit 1" SIGTERM + +# Registers a teeracle with the parentchain, and publish some oracle data. +# +# Demo to show that an enclave can update the exchange rate only when +# 1. the enclave is registered at the pallet-teerex. +# 2. and that the code used is reliable -> the enclave has been put the teeracle whitelist via a governance or sudo +# call. +# +# The teeracle's whitelist has to be empty at the start. So the script needs to run with a clean node state. +# A registered mrenclave will be added in the whitelist by a sudo account. Here //Alice +# +# setup: +# run all on localhost: +# integritee-node purge-chain --dev +# integritee-node --dev -lpallet_teeracle=debug,parity_ws=error,aura=error,sc_basic_authorship=error +# integritee-service --clean-reset run (--skip-ra --dev) +# +# then run this script +# +# usage: +# demo_teeracle_whitelist.sh -p -P -d -i -u -V -C + +while getopts ":p:P:d:i:u:V:C:" opt; do + case $opt in + p) + LITENTRY_RPC_PORT=$OPTARG + ;; + P) + WORKER_1_PORT=$OPTARG + ;; + d) + DURATION=$OPTARG + ;; + i) + INTERVAL=$OPTARG + ;; + u) + LITENTRY_RPC_URL=$OPTARG + ;; + V) + WORKER_1_URL=$OPTARG + ;; + C) + CLIENT_BIN=$OPTARG + ;; + *) + echo "invalid arg ${OPTARG}" + exit 1 + esac +done + +# using default port if none given as arguments +LITENTRY_RPC_PORT=${LITENTRY_RPC_PORT:-9944} +LITENTRY_RPC_URL=${LITENTRY_RPC_URL:-"ws://127.0.0.1"} + +WORKER_1_PORT=${WORKER_1_PORT:-2000} +WORKER_1_URL=${WORKER_1_URL:-"wss://127.0.0.1"} + +CLIENT_BIN=${CLIENT_BIN:-"./../bin/bitacross-cli"} + +DURATION=${DURATION:-48} +INTERVAL=${INTERVAL:-86400} + +LISTEN_TO_EXCHANGE_RATE_EVENTS_CMD="oracle listen-to-exchange-rate-events" +ADD_TO_WHITELIST_CMD="oracle add-to-whitelist" + +echo "Using client binary ${CLIENT_BIN}" +${CLIENT_BIN} --version +echo "Using node uri ${LITENTRY_RPC_URL}:${LITENTRY_RPC_PORT}" +echo "Using trusted-worker uri ${WORKER_1_URL}:${WORKER_1_PORT}" +echo "Using worker market data update interval ${INTERVAL}" +echo "Count the update events for ${DURATION} blocks" +echo "" + +COIN_GECKO="https://api.coingecko.com/" +COIN_MARKET_CAP="https://pro-api.coinmarketcap.com/" +let "MIN_EXPECTED_NUM_OF_EVENTS=$DURATION*6/$INTERVAL-3" +echo "Minimum expected number of events with a single oracle source: ${MIN_EXPECTED_NUM_OF_EVENTS}" + +let "MIN_EXPECTED_NUM_OF_EVENTS_2 = 2*$MIN_EXPECTED_NUM_OF_EVENTS" +echo "Minimum expected number of events with two oracle sources: ${MIN_EXPECTED_NUM_OF_EVENTS_2}" + +CLIENT="${CLIENT_BIN} -p ${LITENTRY_RPC_PORT} -P ${WORKER_1_PORT} -u ${LITENTRY_RPC_URL} -U ${WORKER_1_URL}" + +echo "* Query on-chain enclave registry:" +${CLIENT} list-workers +echo "" + +# this will always take the first MRENCLAVE found in the registry !! +read MRENCLAVE <<< $($CLIENT list-workers | awk '/ MRENCLAVE: / { print $2; exit }') +echo "Reading MRENCLAVE from worker list: ${MRENCLAVE}" + +[[ -z $MRENCLAVE ]] && { echo "MRENCLAVE is empty. cannot continue" ; exit 1; } +echo "" + +echo "Listen to ExchangeRateUpdated events for ${DURATION} blocks. There should be no trusted oracle source!" +#${CLIENT} ${LISTEN_TO_EXCHANGE_RATE_EVENTS_CMD} ${DURATION} +#echo "" + +read NO_EVENTS <<< $(${CLIENT} ${LISTEN_TO_EXCHANGE_RATE_EVENTS_CMD} ${DURATION} | awk '/ EVENTS_COUNT: / { print $2; exit }') +echo "Got ${NO_EVENTS} exchange rate updates when no trusted oracle source is in the whitelist" +echo "" + +echo "Add ${COIN_GECKO} for ${MRENCLAVE} as trusted oracle source" +${CLIENT} ${ADD_TO_WHITELIST_CMD} //Alice ${COIN_GECKO} ${MRENCLAVE} +echo "MRENCLAVE in whitelist for ${COIN_GECKO}" +echo "" + +echo "Listen to ExchangeRateUpdated events for ${DURATION} blocks, after a trusted oracle source has been added to the whitelist." +#${CLIENT} ${LISTEN_TO_EXCHANGE_RATE_EVENTS_CMD} ${DURATION} +#echo "" + +read EVENTS_COUNT <<< $($CLIENT ${LISTEN_TO_EXCHANGE_RATE_EVENTS_CMD} ${DURATION} | awk '/ EVENTS_COUNT: / { print $2; exit }') +echo "Got ${EVENTS_COUNT} exchange rate updates from the trusted oracle source in ${DURATION} blocks(s)" +echo "" + +echo "Add ${COIN_MARKET_CAP} for ${MRENCLAVE} as trusted oracle source" +${CLIENT} ${ADD_TO_WHITELIST_CMD} //Alice ${COIN_MARKET_CAP} ${MRENCLAVE} +echo "MRENCLAVE in whitelist for ${COIN_MARKET_CAP}" +echo "" + +echo "Listen to ExchangeRateUpdated events for ${DURATION} blocks, after a second trusted oracle source has been added to the whitelist." +#${CLIENT} ${LISTEN_TO_EXCHANGE_RATE_EVENTS_CMD} ${DURATION} +#echo "" + +read EVENTS_COUNT_2 <<< $($CLIENT ${LISTEN_TO_EXCHANGE_RATE_EVENTS_CMD} ${DURATION} | awk '/ EVENTS_COUNT: / { print $2; exit }') +echo "Got ${EVENTS_COUNT_2} exchange rate updates from 2 trusted oracle sources in ${DURATION} blocks(s)" +echo "" + +echo "Results :" + +# the following test is for automated CI +# it only works if the teeracle's whitelist is empty at the start (run it from genesis) +if [ $EVENTS_COUNT_2 -ge $MIN_EXPECTED_NUM_OF_EVENTS_2 ]; then + if [ $EVENTS_COUNT -ge $MIN_EXPECTED_NUM_OF_EVENTS ]; then + if [ 0 -eq $NO_EVENTS ]; then + echo "test passed" + exit 0 + else + echo "The test ran through but we received ExchangeRateUpdated events before the enclave was added to the whitelist. Was the enclave previously whitelisted? Perhaps by another teeracle?" + exit 1 + fi + else + echo "test failed: Not enough events received for single oracle source: $EVENTS_COUNT. Should be greater than $MIN_EXPECTED_NUM_OF_EVENTS" + exit 1 + fi +else + echo "test failed: Not enough events received for 2 oracle sources: $EVENTS_COUNT_2. Should be greater than $MIN_EXPECTED_NUM_OF_EVENTS_2" + exit 1 +fi + +exit 1 diff --git a/bitacross-worker/cli/lit_parentchain_nonce.sh b/bitacross-worker/cli/lit_parentchain_nonce.sh new file mode 100755 index 0000000000..505469f11b --- /dev/null +++ b/bitacross-worker/cli/lit_parentchain_nonce.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +# Copyright 2020-2023 Trust Computing GmbH. + +while getopts ":p:A:B:u:W:V:C:" opt; do + case $opt in + p) + NPORT=$OPTARG + ;; + A) + WORKER1PORT=$OPTARG + ;; + u) + NODEURL=$OPTARG + ;; + V) + WORKER1URL=$OPTARG + ;; + C) + CLIENT_BIN=$OPTARG + ;; + esac +done + +# Using default port if none given as arguments. +NPORT=${NPORT:-9944} +NODEURL=${NODEURL:-"ws://127.0.0.1"} + +WORKER1PORT=${WORKER1PORT:-2000} +WORKER1URL=${WORKER1URL:-"wss://127.0.0.1"} + +CLIENT_BIN=${CLIENT_BIN:-"./../bin/bitacross-cli"} + +echo "Using client binary $CLIENT_BIN" +echo "Using node uri $NODEURL:$NPORT" +echo "Using trusted-worker uri $WORKER1URL:$WORKER1PORT" +echo "" + +CLIENT="$CLIENT_BIN -p $NPORT -P $WORKER1PORT -u $NODEURL -U $WORKER1URL" +echo "CLIENT is: $CLIENT" + +echo "* Query on-chain enclave registry:" +WORKERS=$($CLIENT list-workers) +echo "WORKERS: " +echo "${WORKERS}" +echo "" + +if [ "$READMRENCLAVE" = "file" ] +then + read MRENCLAVE <<< $(cat ~/mrenclave.b58) + echo "Reading MRENCLAVE from file: ${MRENCLAVE}" +else + # This will always take the first MRENCLAVE found in the registry !! + read MRENCLAVE <<< $(echo "$WORKERS" | awk '/ MRENCLAVE: / { print $2; exit }') + echo "Reading MRENCLAVE from worker list: ${MRENCLAVE}" +fi +[[ -z $MRENCLAVE ]] && { echo "MRENCLAVE is empty. cannot continue" ; exit 1; } + +sleep 10 +echo "* Send wrong parentchain extrinsic" +${CLIENT} trusted --mrenclave $MRENCLAVE --direct send-erroneous-parentchain-call +echo "" + +sleep 20 +# wait for 10 `ProcessedParentchainBlock` events, which should take around 2 min (1 worker) +# if the incoming parentchain extrinsic is blocked (due to the wrong nonce), there won't be +# such many events. +set -e +timeout -v --foreground 150s $CLIENT listen -e 10 diff --git a/bitacross-worker/cli/lit_set_heartbeat_timeout.sh b/bitacross-worker/cli/lit_set_heartbeat_timeout.sh new file mode 100755 index 0000000000..f062118c0a --- /dev/null +++ b/bitacross-worker/cli/lit_set_heartbeat_timeout.sh @@ -0,0 +1,85 @@ +#!/bin/bash + +# Copyright 2020-2023 Trust Computing GmbH. + +while getopts ":p:A:B:u:W:V:C:" opt; do + case $opt in + p) + NPORT=$OPTARG + ;; + A) + WORKER1PORT=$OPTARG + ;; + B) + WORKER2PORT=$OPTARG + ;; + u) + NODEURL=$OPTARG + ;; + V) + WORKER1URL=$OPTARG + ;; + W) + WORKER2URL=$OPTARG + ;; + C) + CLIENT_BIN=$OPTARG + ;; + esac +done + +# Using default port if none given as arguments. +NPORT=${NPORT:-9944} +NODEURL=${NODEURL:-"ws://127.0.0.1"} + +WORKER1PORT=${WORKER1PORT:-2000} +WORKER1URL=${WORKER1URL:-"wss://127.0.0.1"} + +CLIENT_BIN=${CLIENT_BIN:-"../bin/bitacross-cli"} + +LOG_FOLDER="./../log" + +echo "Using client binary $CLIENT_BIN" +echo "Using node uri $NODEURL:$NPORT" +echo "Using trusted-worker uri $WORKER1URL:$WORKER1PORT" +echo "" + +TIMEOUT=5000 # 5 seconds, smaller than 12s (the block duration) + +CLIENT="$CLIENT_BIN -p $NPORT -P $WORKER1PORT -u $NODEURL -U $WORKER1URL" +echo "CLIENT is: $CLIENT" + +echo "* Query on-chain enclave registry:" +WORKERS=$($CLIENT list-workers) +echo "WORKERS: " +echo "${WORKERS}" +echo "" + +if [ "$READMRENCLAVE" = "file" ] +then + read MRENCLAVE <<< $(cat ~/mrenclave.b58) + echo "Reading MRENCLAVE from file: ${MRENCLAVE}" +else + # This will always take the first MRENCLAVE found in the registry !! + read MRENCLAVE <<< $(echo "$WORKERS" | awk '/ MRENCLAVE: / { print $2; exit }') + echo "Reading MRENCLAVE from worker list: ${MRENCLAVE}" +fi +[[ -z $MRENCLAVE ]] && { echo "MRENCLAVE is empty. cannot continue" ; exit 1; } + + +# indirect call that will be sent to the parachain, it will be synchronously handled +sleep 10 +echo "* Set heartbeat timeout to $TIMEOUT" +${CLIENT} set-heartbeat-timeout "$TIMEOUT" +echo "" + +sleep 120 + +read MRENCLAVE <<< $($CLIENT list-workers | awk '/ MRENCLAVE: / { print $2; exit }') +if [[ -z $MRENCLAVE ]] +then + echo "All workers removed, test passed" +else + echo "Worker(s) still exist(s), test fail" + exit 1 +fi diff --git a/bitacross-worker/cli/src/attesteer/commands/mod.rs b/bitacross-worker/cli/src/attesteer/commands/mod.rs new file mode 100644 index 0000000000..70119bf399 --- /dev/null +++ b/bitacross-worker/cli/src/attesteer/commands/mod.rs @@ -0,0 +1,23 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +mod send_dcap_quote; +mod send_ias_attestation; + +pub use self::{ + send_dcap_quote::SendDcapQuoteCmd, send_ias_attestation::SendIasAttestationReportCmd, +}; diff --git a/bitacross-worker/cli/src/attesteer/commands/send_dcap_quote.rs b/bitacross-worker/cli/src/attesteer/commands/send_dcap_quote.rs new file mode 100644 index 0000000000..6ee0baf02f --- /dev/null +++ b/bitacross-worker/cli/src/attesteer/commands/send_dcap_quote.rs @@ -0,0 +1,65 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{command_utils::get_worker_api_direct, Cli}; +use itc_rpc_client::direct_client::DirectApi; +use itp_rpc::{Id, RpcRequest, RpcResponse, RpcReturnValue}; +use itp_types::DirectRequestStatus; +use itp_utils::FromHexPrefixed; +use log::*; +use std::fs::read_to_string; + +/// Forward DCAP quote for verification. +#[derive(Debug, Clone, Parser)] +pub struct SendDcapQuoteCmd { + /// Hex encoded DCAP quote filename. + quote: String, +} + +impl SendDcapQuoteCmd { + pub fn run(&self, cli: &Cli) { + let direct_api = get_worker_api_direct(cli); + let hex_encoded_quote = match read_to_string(&self.quote) { + Ok(hex_encoded_quote) => hex_encoded_quote, + Err(e) => panic!("Opening hex encoded DCAP quote file failed: {:?}", e), + }; + + let rpc_method = "attesteer_forwardDcapQuote".to_owned(); + let jsonrpc_call: String = RpcRequest::compose_jsonrpc_call( + Id::Text("1".to_string()), + rpc_method, + vec![hex_encoded_quote], + ) + .unwrap(); + + let rpc_response_str = direct_api.get(&jsonrpc_call).unwrap(); + + // Decode RPC response. + let Ok(rpc_response) = serde_json::from_str::(&rpc_response_str) else { + panic!("Can't parse RPC response: '{rpc_response_str}'"); + }; + let rpc_return_value = match RpcReturnValue::from_hex(&rpc_response.result) { + Ok(rpc_return_value) => rpc_return_value, + Err(e) => panic!("Failed to decode RpcReturnValue: {:?}", e), + }; + + match rpc_return_value.status { + DirectRequestStatus::Ok => println!("DCAP quote verification succeded."), + _ => error!("DCAP quote verification failed"), + } + } +} diff --git a/bitacross-worker/cli/src/attesteer/commands/send_ias_attestation.rs b/bitacross-worker/cli/src/attesteer/commands/send_ias_attestation.rs new file mode 100644 index 0000000000..af4128b138 --- /dev/null +++ b/bitacross-worker/cli/src/attesteer/commands/send_ias_attestation.rs @@ -0,0 +1,66 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use itc_rpc_client::direct_client::DirectApi; +use itp_rpc::{Id, RpcRequest, RpcResponse, RpcReturnValue}; +use itp_types::DirectRequestStatus; +use itp_utils::FromHexPrefixed; +use log::*; +use std::fs::read_to_string; + +use crate::{command_utils::get_worker_api_direct, Cli}; + +/// Forward IAS attestation report for verification. +#[derive(Debug, Clone, Parser)] +pub struct SendIasAttestationReportCmd { + /// Hex encoded IAS attestation report filename. + report: String, +} + +impl SendIasAttestationReportCmd { + pub fn run(&self, cli: &Cli) { + let direct_api = get_worker_api_direct(cli); + let hex_encoded_report = match read_to_string(&self.report) { + Ok(hex_encoded_report) => hex_encoded_report, + Err(e) => panic!("Opening hex encoded IAS attestation report file failed: {:?}", e), + }; + + let rpc_method = "attesteer_forwardIasAttestationReport".to_owned(); + let jsonrpc_call: String = RpcRequest::compose_jsonrpc_call( + Id::Text("1".to_string()), + rpc_method, + vec![hex_encoded_report], + ) + .unwrap(); + + let rpc_response_str = direct_api.get(&jsonrpc_call).unwrap(); + + // Decode RPC response. + let Ok(rpc_response) = serde_json::from_str::(&rpc_response_str) else { + panic!("Can't parse RPC response: '{rpc_response_str}'"); + }; + let rpc_return_value = match RpcReturnValue::from_hex(&rpc_response.result) { + Ok(rpc_return_value) => rpc_return_value, + Err(e) => panic!("Failed to decode RpcReturnValue: {:?}", e), + }; + + match rpc_return_value.status { + DirectRequestStatus::Ok => println!("IAS attestation report verification succeded."), + _ => error!("IAS attestation report verification failed"), + } + } +} diff --git a/bitacross-worker/cli/src/attesteer/mod.rs b/bitacross-worker/cli/src/attesteer/mod.rs new file mode 100644 index 0000000000..9f03c59065 --- /dev/null +++ b/bitacross-worker/cli/src/attesteer/mod.rs @@ -0,0 +1,41 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::Cli; + +use self::commands::{SendDcapQuoteCmd, SendIasAttestationReportCmd}; + +mod commands; + +/// Attesteer subcommands for the CLI. +#[derive(Debug, clap::Subcommand)] +pub enum AttesteerCommand { + /// Forward DCAP quote for verification. + SendDCAPQuote(SendDcapQuoteCmd), + + /// Forward IAS attestation report for verification. + SendIASAttestationReport(SendIasAttestationReportCmd), +} + +impl AttesteerCommand { + pub fn run(&self, cli: &Cli) { + match self { + AttesteerCommand::SendDCAPQuote(cmd) => cmd.run(cli), + AttesteerCommand::SendIASAttestationReport(cmd) => cmd.run(cli), + } + } +} diff --git a/bitacross-worker/cli/src/base_cli/commands/balance.rs b/bitacross-worker/cli/src/base_cli/commands/balance.rs new file mode 100644 index 0000000000..cea86ae48b --- /dev/null +++ b/bitacross-worker/cli/src/base_cli/commands/balance.rs @@ -0,0 +1,39 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + command_utils::{get_accountid_from_str, get_chain_api}, + Cli, CliResult, CliResultOk, +}; +use substrate_api_client::GetAccountInformation; + +#[derive(Parser)] +pub struct BalanceCommand { + /// AccountId in ss58check format + account: String, +} + +impl BalanceCommand { + pub(crate) fn run(&self, cli: &Cli) -> CliResult { + let api = get_chain_api(cli); + let accountid = get_accountid_from_str(&self.account); + let balance = + if let Some(data) = api.get_account_data(&accountid).unwrap() { data.free } else { 0 }; + println!("{}", balance); + Ok(CliResultOk::Balance { balance }) + } +} diff --git a/bitacross-worker/cli/src/base_cli/commands/faucet.rs b/bitacross-worker/cli/src/base_cli/commands/faucet.rs new file mode 100644 index 0000000000..be33b3bf86 --- /dev/null +++ b/bitacross-worker/cli/src/base_cli/commands/faucet.rs @@ -0,0 +1,61 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + command_utils::{get_accountid_from_str, get_chain_api}, + Cli, CliResult, CliResultOk, +}; +use my_node_runtime::{BalancesCall, RuntimeCall}; +use sp_keyring::AccountKeyring; +use sp_runtime::MultiAddress; +use std::vec::Vec; +use substrate_api_client::{ac_compose_macros::compose_extrinsic_offline, SubmitExtrinsic}; + +const PREFUNDING_AMOUNT: u128 = 1_000_000_000; + +#[derive(Parser)] +pub struct FaucetCommand { + /// Account(s) to be funded, ss58check encoded + #[clap(num_args = 1.., required = true)] + accounts: Vec, +} + +impl FaucetCommand { + pub(crate) fn run(&self, cli: &Cli) -> CliResult { + let mut api = get_chain_api(cli); + api.set_signer(AccountKeyring::Alice.pair().into()); + let mut nonce = api.get_nonce().unwrap(); + for account in &self.accounts { + let to = get_accountid_from_str(account); + #[allow(clippy::redundant_clone)] + let xt = compose_extrinsic_offline!( + api.signer().unwrap(), + RuntimeCall::Balances(BalancesCall::transfer { + dest: MultiAddress::Id(to.clone()), + value: PREFUNDING_AMOUNT + }), + api.extrinsic_params(nonce) + ); + // send and watch extrinsic until finalized + println!("Faucet drips to {} (Alice's nonce={})", to, nonce); + let _blockh = api.submit_extrinsic(xt).unwrap(); + nonce += 1; + } + + Ok(CliResultOk::None) + } +} diff --git a/bitacross-worker/cli/src/base_cli/commands/listen.rs b/bitacross-worker/cli/src/base_cli/commands/listen.rs new file mode 100644 index 0000000000..27a9b15811 --- /dev/null +++ b/bitacross-worker/cli/src/base_cli/commands/listen.rs @@ -0,0 +1,149 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{command_utils::get_chain_api, Cli, CliResult, CliResultOk}; +use base58::ToBase58; +use codec::Encode; +use log::*; +use my_node_runtime::{Hash, RuntimeEvent}; +use substrate_api_client::SubscribeEvents; + +#[derive(Parser)] +pub struct ListenCommand { + /// exit after given number of parentchain events + #[clap(short, long = "exit-after")] + events: Option, + + /// exit after given number of blocks + #[clap(short, long = "await-blocks")] + blocks: Option, +} + +impl ListenCommand { + pub(crate) fn run(&self, cli: &Cli) -> CliResult { + println!("{:?} {:?}", self.events, self.blocks); + let api = get_chain_api(cli); + info!("Subscribing to events"); + let mut count = 0u32; + let mut blocks = 0u32; + let mut subscription = api.subscribe_events().unwrap(); + loop { + if let Some(e) = self.events { + if count >= e { + return Ok(CliResultOk::None) + } + }; + if let Some(b) = self.blocks { + if blocks >= b { + return Ok(CliResultOk::None) + } + }; + + let event_results = subscription.next_events::().unwrap(); + blocks += 1; + match event_results { + Ok(evts) => + for evr in &evts { + println!("decoded: phase {:?} event {:?}", evr.phase, evr.event); + match &evr.event { + RuntimeEvent::Balances(be) => { + println!(">>>>>>>>>> balances event: {:?}", be); + match &be { + pallet_balances::Event::Transfer { from, to, amount } => { + println!("From: {:?}", from); + println!("To: {:?}", to); + println!("Value: {:?}", amount); + }, + _ => { + debug!("ignoring unsupported balances event"); + }, + } + }, + RuntimeEvent::Teerex(ee) => { + println!(">>>>>>>>>> integritee teerex event: {:?}", ee); + count += 1; + match &ee { + my_node_runtime::pallet_teerex::Event::AddedEnclave( + accountid, + url, + ) => { + println!( + "AddedEnclave: {:?} at url {}", + accountid, + String::from_utf8(url.to_vec()) + .unwrap_or_else(|_| "error".to_string()) + ); + }, + my_node_runtime::pallet_teerex::Event::RemovedEnclave( + accountid, + ) => { + println!("RemovedEnclave: {:?}", accountid); + }, + my_node_runtime::pallet_teerex::Event::Forwarded(shard) => { + println!( + "Forwarded request for shard {}", + shard.encode().to_base58() + ); + }, + my_node_runtime::pallet_teerex::Event::ProcessedParentchainBlock( + accountid, + block_hash, + merkle_root, + block_number, + ) => { + println!( + "ProcessedParentchainBlock from {} with hash {:?}, number {} and merkle root {:?}", + accountid, block_hash, merkle_root, block_number + ); + }, + my_node_runtime::pallet_teerex::Event::ShieldFunds( + incognito_account, + ) => { + println!("ShieldFunds for {:?}", incognito_account); + }, + my_node_runtime::pallet_teerex::Event::UnshieldedFunds( + public_account, + ) => { + println!("UnshieldFunds for {:?}", public_account); + }, + _ => debug!("ignoring unsupported teerex event: {:?}", ee), + } + }, + RuntimeEvent::Sidechain(ee) => { + println!(">>>>>>>>>> integritee sidechain event: {:?}", ee); + count += 1; + match &ee { + my_node_runtime::pallet_sidechain::Event::ProposedSidechainBlock( + accountid, + block_hash, + ) => { + println!( + "ProposedSidechainBlock from {} with hash {:?}", + accountid, block_hash + ); + }, + _ => debug!("ignoring unsupported sidechain event: {:?}", ee), + } + }, + _ => debug!("ignoring unsupported module event: {:?}", evr.event), + } + }, + Err(_) => error!("couldn't decode event record list"), + } + } + } +} diff --git a/bitacross-worker/cli/src/base_cli/commands/litentry/mod.rs b/bitacross-worker/cli/src/base_cli/commands/litentry/mod.rs new file mode 100644 index 0000000000..b5700e1258 --- /dev/null +++ b/bitacross-worker/cli/src/base_cli/commands/litentry/mod.rs @@ -0,0 +1,17 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +pub mod set_heartbeat_timeout; diff --git a/bitacross-worker/cli/src/base_cli/commands/litentry/set_heartbeat_timeout.rs b/bitacross-worker/cli/src/base_cli/commands/litentry/set_heartbeat_timeout.rs new file mode 100644 index 0000000000..f4efb49ae7 --- /dev/null +++ b/bitacross-worker/cli/src/base_cli/commands/litentry/set_heartbeat_timeout.rs @@ -0,0 +1,54 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +use crate::{command_utils::get_chain_api, Cli}; + +use crate::{CliResult, CliResultOk}; +use itp_node_api::api_client::TEEREX; +use log::*; +use sp_keyring::AccountKeyring; +use substrate_api_client::{ac_compose_macros::compose_extrinsic, SubmitAndWatch, XtStatus}; +#[derive(Parser)] +pub struct SetHeartbeatTimeoutCommand { + /// Heartbeat timeout + timeout: u64, +} + +impl SetHeartbeatTimeoutCommand { + pub(crate) fn run(&self, cli: &Cli) -> CliResult { + let mut chain_api = get_chain_api(cli); + + // has to be //Alice as this is the genesis admin for teerex pallet, + // otherwise `set_heartbeat_timeout` call won't work + chain_api.set_signer(AccountKeyring::Alice.pair().into()); + + // call set_heartbeat_timeout + let xt = compose_extrinsic!( + chain_api, + TEEREX, + "set_heartbeat_timeout", + codec::Compact(self.timeout) + ); + + let tx_hash = chain_api.submit_and_watch_extrinsic_until(xt, XtStatus::Finalized).unwrap(); + println!( + "[+] SetHeartbeatTimeoutCommand TrustedOperation got finalized. Hash: {:?}\n", + tx_hash + ); + + Ok(CliResultOk::None) + } +} diff --git a/bitacross-worker/cli/src/base_cli/commands/mod.rs b/bitacross-worker/cli/src/base_cli/commands/mod.rs new file mode 100644 index 0000000000..313a32249c --- /dev/null +++ b/bitacross-worker/cli/src/base_cli/commands/mod.rs @@ -0,0 +1,7 @@ +pub mod balance; +pub mod faucet; +pub mod listen; +pub mod litentry; +pub mod register_tcb_info; +pub mod shield_funds; +pub mod transfer; diff --git a/bitacross-worker/cli/src/base_cli/commands/register_tcb_info.rs b/bitacross-worker/cli/src/base_cli/commands/register_tcb_info.rs new file mode 100644 index 0000000000..7802794a09 --- /dev/null +++ b/bitacross-worker/cli/src/base_cli/commands/register_tcb_info.rs @@ -0,0 +1,146 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + command_utils::{get_chain_api, *}, + Cli, CliResult, CliResultOk, +}; +use itp_node_api::api_client::TEEREX; +use itp_types::{parentchain::Hash, OpaqueCall}; +use itp_utils::ToHexPrefixed; +use log::*; +use regex::Regex; +use serde::Deserialize; +use substrate_api_client::{ + ac_compose_macros::{compose_call, compose_extrinsic_offline}, + SubmitAndWatch, XtStatus, +}; +use urlencoding; + +#[derive(Debug, Deserialize)] +struct Platform { + fmspc: String, + #[serde(rename = "platform")] + _platform: String, +} + +#[derive(Parser)] +pub struct RegisterTcbInfoCommand { + /// Sender's parentchain AccountId in ss58check format. + sender: String, + /// Intel's Family-Model-Stepping-Platform-Custom SKU. 6-Byte non-prefixed hex value + #[clap(short, long, action, conflicts_with = "all")] + fmspc: Option, + /// registers all fmspc currently published by Intel + #[clap(short, long, action)] + all: bool, +} + +impl RegisterTcbInfoCommand { + pub(crate) fn run(&self, cli: &Cli) -> CliResult { + let mut chain_api = get_chain_api(cli); + + // Get the sender. + let from = get_pair_from_str(&self.sender); + chain_api.set_signer(from.into()); + + let fmspcs = if self.all { + trace!("fetching all fmspc's from api.trustedservices.intel.com"); + let fmspcs = reqwest::blocking::get( + "https://api.trustedservices.intel.com/sgx/certification/v4/fmspcs", + ) + .unwrap(); + let fmspcs: Vec = fmspcs.json().expect("Error parsing JSON"); + println!("{:?}", fmspcs); + fmspcs.into_iter().map(|f| f.fmspc).collect() + } else if let Some(fmspc) = self.fmspc.clone() { + vec![fmspc] + } else { + panic!("must specify either '--all' or '--fmspc'"); + }; + let mut nonce = chain_api.get_nonce().unwrap(); + let xt_hashes: Vec<(String, Option)> = fmspcs + .into_iter() + .map(|fmspc| { + println!( + "fetching tcb info for fmspc {} from api.trustedservices.intel.com", + fmspc + ); + let response = reqwest::blocking::get(format!( + "https://api.trustedservices.intel.com/sgx/certification/v4/tcb?fmspc={}", + fmspc + )) + .unwrap(); + //extract certificate chain from header + let certificate_chain = urlencoding::decode( + response.headers().get("TCB-Info-Issuer-Chain").unwrap().to_str().unwrap(), + ) + .unwrap() + .to_string(); + trace!("certificate chain: \n{}", certificate_chain); + + let body = response.text().unwrap(); + trace!("raw json: \n{}", body); + let re = Regex::new(r#"tcbInfo\"\s?:(\{.*\}),\s?\"signature"#).unwrap(); + let tcb_info = &re.captures(&body).unwrap()[1]; + let re = Regex::new(r#"\"signature\"\s?:\s?\"(.*)\"\}"#).unwrap(); + let intel_signature_hex = &re.captures(&body).unwrap()[1]; + trace!("TCB info: {}", tcb_info); + trace!("signature: {}", intel_signature_hex); + + let intel_signature = hex::decode(intel_signature_hex).unwrap(); + + let call = OpaqueCall::from_tuple(&compose_call!( + chain_api.metadata(), + TEEREX, + "register_tcb_info", + tcb_info, + intel_signature, + certificate_chain + )); + + trace!( + "encoded call to be sent as extrinsic with nonce {}: {}", + nonce, + call.to_hex() + ); + + let xt = compose_extrinsic_offline!( + chain_api.clone().signer().unwrap(), + call, + chain_api.extrinsic_params(nonce) + ); + nonce += 1; + match chain_api.submit_and_watch_extrinsic_until(xt, XtStatus::InBlock) { + Ok(xt_report) => { + println!( + "[+] register_tcb_info. extrinsic hash: {:?} / status: {:?}", + xt_report.extrinsic_hash, xt_report.status, + ); + (fmspc, Some(xt_report.extrinsic_hash)) + }, + Err(e) => { + error!("register_tcb_info extrinsic failed {:?}", e); + (fmspc, None) + }, + } + }) + .collect(); + println!("{:?}", xt_hashes); + Ok(CliResultOk::None) + } +} diff --git a/bitacross-worker/cli/src/base_cli/commands/shield_funds.rs b/bitacross-worker/cli/src/base_cli/commands/shield_funds.rs new file mode 100644 index 0000000000..ec45da50fb --- /dev/null +++ b/bitacross-worker/cli/src/base_cli/commands/shield_funds.rs @@ -0,0 +1,92 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + command_utils::{get_accountid_from_str, get_chain_api, *}, + Cli, CliError, CliResult, CliResultOk, +}; +use base58::FromBase58; +use codec::{Decode, Encode}; +use itp_node_api::api_client::TEEREX; +use itp_sgx_crypto::ShieldingCryptoEncrypt; +use itp_stf_primitives::types::ShardIdentifier; +use litentry_primitives::ParentchainBalance as Balance; +use log::*; +use sp_core::sr25519 as sr25519_core; +use substrate_api_client::{ac_compose_macros::compose_extrinsic, SubmitAndWatch, XtStatus}; + +#[derive(Parser)] +pub struct ShieldFundsCommand { + /// Sender's parentchain AccountId in ss58check format. + from: String, + /// Recipient's incognito AccountId in ss58check format. + to: String, + /// Amount to be transferred. + amount: Balance, + /// Shard identifier. + shard: String, +} + +impl ShieldFundsCommand { + pub(crate) fn run(&self, cli: &Cli) -> CliResult { + let mut chain_api = get_chain_api(cli); + + let shard_opt = match self.shard.from_base58() { + Ok(s) => ShardIdentifier::decode(&mut &s[..]), + _ => panic!("shard argument must be base58 encoded"), + }; + + let shard = match shard_opt { + Ok(shard) => shard, + Err(e) => panic!("{}", e), + }; + + // Get the sender. + let from = get_pair_from_str(&self.from); + chain_api.set_signer(sr25519_core::Pair::from(from).into()); + + // Get the recipient. + let to = get_accountid_from_str(&self.to); + + let encryption_key = get_shielding_key(cli).unwrap(); + let encrypted_recevier = encryption_key.encrypt(&to.encode()).unwrap(); + + // Compose the extrinsic. + let xt = compose_extrinsic!( + chain_api, + TEEREX, + "shield_funds", + encrypted_recevier, + self.amount, + shard + ); + + match chain_api.submit_and_watch_extrinsic_until(xt, XtStatus::Finalized) { + Ok(xt_report) => { + println!( + "[+] shield funds success. extrinsic hash: {:?} / status: {:?} / block hash: {:?}", + xt_report.extrinsic_hash, xt_report.status, xt_report.block_hash.unwrap() + ); + Ok(CliResultOk::H256 { hash: xt_report.block_hash.unwrap() }) + }, + Err(e) => { + error!("shield_funds extrinsic failed {:?}", e); + Err(CliError::Extrinsic { msg: format!("{:?}", e) }) + }, + } + } +} diff --git a/bitacross-worker/cli/src/base_cli/commands/transfer.rs b/bitacross-worker/cli/src/base_cli/commands/transfer.rs new file mode 100644 index 0000000000..58cfb19ece --- /dev/null +++ b/bitacross-worker/cli/src/base_cli/commands/transfer.rs @@ -0,0 +1,61 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + command_utils::{get_accountid_from_str, get_chain_api, *}, + Cli, CliResult, CliResultOk, +}; +use litentry_primitives::ParentchainBalance as Balance; +use log::*; +use sp_core::{crypto::Ss58Codec, Pair}; +use substrate_api_client::{ + extrinsic::BalancesExtrinsics, GetAccountInformation, SubmitAndWatch, XtStatus, +}; + +#[derive(Parser)] +pub struct TransferCommand { + /// sender's AccountId in ss58check format + from: String, + + /// recipient's AccountId in ss58check format + to: String, + + /// amount to be transferred + amount: Balance, +} + +impl TransferCommand { + pub(crate) fn run(&self, cli: &Cli) -> CliResult { + let from_account = get_pair_from_str(&self.from); + let to_account = get_accountid_from_str(&self.to); + info!("from ss58 is {}", from_account.public().to_ss58check()); + info!("to ss58 is {}", to_account.to_ss58check()); + let mut api = get_chain_api(cli); + api.set_signer(from_account.into()); + let xt = api.balance_transfer_allow_death(to_account.clone().into(), self.amount); + let tx_report = api.submit_and_watch_extrinsic_until(xt, XtStatus::InBlock).unwrap(); + println!( + "[+] L1 extrinsic success. extrinsic hash: {:?} / status: {:?}", + tx_report.extrinsic_hash, tx_report.status + ); + let result = api.get_account_data(&to_account).unwrap().unwrap(); + let balance = result.free; + println!("balance for {} is now {}", to_account, balance); + + Ok(CliResultOk::Balance { balance }) + } +} diff --git a/bitacross-worker/cli/src/base_cli/mod.rs b/bitacross-worker/cli/src/base_cli/mod.rs new file mode 100644 index 0000000000..9ba67f94f7 --- /dev/null +++ b/bitacross-worker/cli/src/base_cli/mod.rs @@ -0,0 +1,192 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + base_cli::commands::{ + balance::BalanceCommand, faucet::FaucetCommand, listen::ListenCommand, + litentry::set_heartbeat_timeout::SetHeartbeatTimeoutCommand, + register_tcb_info::RegisterTcbInfoCommand, shield_funds::ShieldFundsCommand, + transfer::TransferCommand, + }, + command_utils::*, + Cli, CliResult, CliResultOk, ED25519_KEY_TYPE, SR25519_KEY_TYPE, +}; +use base58::ToBase58; +use chrono::{DateTime, Utc}; +use clap::Subcommand; +use itc_rpc_client::direct_client::DirectApi; +use itp_node_api::api_client::PalletTeerexApi; +use sp_core::crypto::Ss58Codec; +use sp_keystore::Keystore; +use std::{ + path::PathBuf, + time::{Duration, UNIX_EPOCH}, +}; +use substrate_client_keystore::LocalKeystore; + +mod commands; + +#[derive(Subcommand)] +pub enum BaseCommand { + /// query parentchain balance for AccountId + Balance(BalanceCommand), + + /// generates a new account for the integritee chain in your local keystore + NewAccount, + + /// lists all accounts in your local keystore for the integritee chain + ListAccounts, + + /// query node metadata and print it as json to stdout + PrintMetadata, + + /// query sgx-runtime metadata and print it as json to stdout + PrintSgxMetadata, + + /// send some bootstrapping funds to supplied account(s) + Faucet(FaucetCommand), + + /// transfer funds from one parentchain account to another + Transfer(TransferCommand), + + /// query enclave registry and list all workers + ListWorkers, + + /// listen to parentchain events + Listen(ListenCommand), + + /// Register TCB info for FMSPC + RegisterTcbInfo(RegisterTcbInfoCommand), + + /// Transfer funds from an parentchain account to an incognito account + ShieldFunds(ShieldFundsCommand), + + // Litentry's commands below + /// query sgx-runtime metadata and print the raw (hex-encoded) metadata to stdout + /// we could have added a parameter like `--raw` to `PrintSgxMetadata`, but + /// we want to keep our changes isolated + PrintSgxMetadataRaw, + + /// set heartbeat timeout storage + SetHeartbeatTimeout(SetHeartbeatTimeoutCommand), +} + +impl BaseCommand { + pub fn run(&self, cli: &Cli) -> CliResult { + match self { + BaseCommand::Balance(cmd) => cmd.run(cli), + BaseCommand::NewAccount => new_account(), + BaseCommand::ListAccounts => list_accounts(), + BaseCommand::PrintMetadata => print_metadata(cli), + BaseCommand::PrintSgxMetadata => print_sgx_metadata(cli), + BaseCommand::Faucet(cmd) => cmd.run(cli), + BaseCommand::Transfer(cmd) => cmd.run(cli), + BaseCommand::ListWorkers => list_workers(cli), + BaseCommand::Listen(cmd) => cmd.run(cli), + BaseCommand::RegisterTcbInfo(cmd) => cmd.run(cli), + BaseCommand::ShieldFunds(cmd) => cmd.run(cli), + // Litentry's commands below + BaseCommand::PrintSgxMetadataRaw => print_sgx_metadata_raw(cli), + BaseCommand::SetHeartbeatTimeout(cmd) => cmd.run(cli), + } + } +} + +fn new_account() -> CliResult { + let store = LocalKeystore::open(PathBuf::from(&KEYSTORE_PATH), None).unwrap(); + let key = LocalKeystore::sr25519_generate_new(&store, SR25519_KEY_TYPE, None).unwrap(); + let key_base58 = key.to_ss58check(); + drop(store); + println!("{}", key_base58); + Ok(CliResultOk::PubKeysBase58 { + pubkeys_sr25519: Some(vec![key_base58]), + pubkeys_ed25519: None, + }) +} + +fn list_accounts() -> CliResult { + let store = LocalKeystore::open(PathBuf::from(&KEYSTORE_PATH), None).unwrap(); + println!("sr25519 keys:"); + let mut keys_sr25519 = vec![]; + for pubkey in store.sr25519_public_keys(SR25519_KEY_TYPE).into_iter() { + let key_ss58 = pubkey.to_ss58check(); + println!("{}", key_ss58); + keys_sr25519.push(key_ss58); + } + println!("ed25519 keys:"); + let mut keys_ed25519 = vec![]; + for pubkey in store.ed25519_public_keys(ED25519_KEY_TYPE).into_iter() { + let key_ss58 = pubkey.to_ss58check(); + println!("{}", key_ss58); + keys_ed25519.push(key_ss58); + } + drop(store); + + Ok(CliResultOk::PubKeysBase58 { + pubkeys_sr25519: Some(keys_sr25519), + pubkeys_ed25519: Some(keys_ed25519), + }) +} + +fn print_metadata(cli: &Cli) -> CliResult { + let api = get_chain_api(cli); + let meta = api.metadata(); + println!("Metadata:\n {}", &meta.pretty_format().unwrap()); + Ok(CliResultOk::Metadata { metadata: meta.clone() }) +} +fn print_sgx_metadata(cli: &Cli) -> CliResult { + let worker_api_direct = get_worker_api_direct(cli); + let metadata = worker_api_direct.get_state_metadata().unwrap(); + println!("Metadata:\n {}", metadata.pretty_format().unwrap()); + Ok(CliResultOk::Metadata { metadata }) +} + +fn print_sgx_metadata_raw(cli: &Cli) -> CliResult { + let worker_api_direct = get_worker_api_direct(cli); + let metadata = worker_api_direct.get_state_metadata_raw().unwrap(); + println!("{metadata}"); + Ok(CliResultOk::None) +} + +fn list_workers(cli: &Cli) -> CliResult { + let api = get_chain_api(cli); + let wcount = api.enclave_count(None).unwrap(); + println!("number of workers registered: {}", wcount); + + let mut mr_enclaves = Vec::with_capacity(wcount as usize); + + for w in 1..=wcount { + let enclave = api.enclave(w, None).unwrap(); + if enclave.is_none() { + println!("error reading enclave data"); + continue + }; + let enclave = enclave.unwrap(); + let timestamp = + DateTime::::from(UNIX_EPOCH + Duration::from_millis(enclave.timestamp)); + let mr_enclave = enclave.mr_enclave.to_base58(); + println!("Enclave {}", w); + println!(" AccountId: {}", enclave.pubkey.to_ss58check()); + println!(" MRENCLAVE: {}", mr_enclave); + println!(" RA timestamp: {}", timestamp); + println!(" URL: {}", enclave.url); + + mr_enclaves.push(mr_enclave); + } + + Ok(CliResultOk::MrEnclaveBase58 { mr_enclaves }) +} diff --git a/bitacross-worker/cli/src/benchmark/mod.rs b/bitacross-worker/cli/src/benchmark/mod.rs new file mode 100644 index 0000000000..04e1694f21 --- /dev/null +++ b/bitacross-worker/cli/src/benchmark/mod.rs @@ -0,0 +1,378 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + command_utils::get_worker_api_direct, + get_layer_two_nonce, + trusted_cli::TrustedCli, + trusted_command_utils::{get_identifiers, get_keystore_path, get_pair_from_str}, + trusted_operation::{get_json_request, get_state, wait_until}, + Cli, CliResult, CliResultOk, SR25519_KEY_TYPE, +}; +use codec::Decode; +use hdrhistogram::Histogram; +use ita_stf::{ + Getter, Index, PublicGetter, TrustedCall, TrustedCallSigned, TrustedGetter, STF_TX_FEE, +}; +use itc_rpc_client::direct_client::{DirectApi, DirectClient}; +use itp_stf_primitives::{ + traits::TrustedCallSigning, + types::{KeyPair, TrustedOperation}, +}; +use itp_types::{ + Balance, ShardIdentifier, TrustedOperationStatus, + TrustedOperationStatus::{InSidechainBlock, Submitted}, +}; +use log::*; +use rand::Rng; +use rayon::prelude::*; +use sgx_crypto_helper::rsa3072::Rsa3072PubKey; +use sp_application_crypto::sr25519; +use sp_core::{sr25519 as sr25519_core, Pair}; +use sp_keystore::Keystore; +use std::{ + boxed::Box, + string::ToString, + sync::mpsc::{channel, Receiver}, + thread, time, + time::Instant, + vec::Vec, +}; +use substrate_client_keystore::LocalKeystore; + +// Needs to be above the existential deposit minimum, otherwise an account will not +// be created and the state is not increased. +const EXISTENTIAL_DEPOSIT: Balance = 1000; + +#[derive(Parser)] +pub struct BenchmarkCommand { + /// The number of clients (=threads) to be used in the benchmark + #[clap(default_value_t = 10)] + number_clients: u32, + + /// The number of iterations to execute for each client + #[clap(default_value_t = 30)] + number_iterations: u128, + + /// Adds a random wait before each transaction. This is the lower bound for the interval in ms. + #[clap(default_value_t = 0)] + random_wait_before_transaction_min_ms: u32, + + /// Adds a random wait before each transaction. This is the upper bound for the interval in ms. + #[clap(default_value_t = 0)] + random_wait_before_transaction_max_ms: u32, + + /// Whether to wait for "InSidechainBlock" confirmation for each transaction + #[clap(short, long)] + wait_for_confirmation: bool, + + /// Account to be used for initial funding of generated accounts used in benchmark + #[clap(default_value_t = String::from("//Alice"))] + funding_account: String, +} + +struct BenchmarkClient { + account: sr25519_core::Pair, + current_balance: u128, + client_api: DirectClient, + receiver: Receiver, +} + +impl BenchmarkClient { + fn new( + account: sr25519_core::Pair, + initial_balance: u128, + initial_request: String, + cli: &Cli, + ) -> Self { + debug!("get direct api"); + let client_api = get_worker_api_direct(cli); + + debug!("setup sender and receiver"); + let (sender, receiver) = channel(); + client_api.watch(initial_request, sender); + BenchmarkClient { account, current_balance: initial_balance, client_api, receiver } + } +} + +/// Stores timing information about a specific transaction +struct BenchmarkTransaction { + started: Instant, + submitted: Instant, + confirmed: Option, +} + +impl BenchmarkCommand { + pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) -> CliResult { + let random_wait_before_transaction_ms: (u32, u32) = ( + self.random_wait_before_transaction_min_ms, + self.random_wait_before_transaction_max_ms, + ); + let store = LocalKeystore::open(get_keystore_path(trusted_args, cli), None).unwrap(); + let funding_account_keys = get_pair_from_str(trusted_args, &self.funding_account, cli); + + let (mrenclave, shard) = get_identifiers(trusted_args, cli); + + // Get shielding pubkey. + let worker_api_direct = get_worker_api_direct(cli); + let shielding_pubkey: Rsa3072PubKey = match worker_api_direct.get_rsa_pubkey() { + Ok(key) => key, + Err(err_msg) => panic!("{}", err_msg.to_string()), + }; + + let nonce_start = get_layer_two_nonce!(funding_account_keys, cli, trusted_args); + println!("Nonce for account {}: {}", self.funding_account, nonce_start); + + let mut accounts = Vec::new(); + let initial_balance = (self.number_iterations + 1) * (STF_TX_FEE + EXISTENTIAL_DEPOSIT); + // Setup new accounts and initialize them with money from Alice. + for i in 0..self.number_clients { + let nonce = i + nonce_start; + println!("Initializing account {} with initial amount {:?}", i, initial_balance); + + // Create new account to use. + let a = LocalKeystore::sr25519_generate_new(&store, SR25519_KEY_TYPE, None).unwrap(); + let account = get_pair_from_str(trusted_args, a.to_string().as_str(), cli); + + // Transfer amount from Alice to new account. + let top: TrustedOperation = TrustedCall::balance_transfer( + funding_account_keys.public().into(), + account.public().into(), + initial_balance, + ) + .sign( + &KeyPair::Sr25519(Box::new(funding_account_keys.clone())), + nonce, + &mrenclave, + &shard, + ) + .into_trusted_operation(trusted_args.direct); + + // For the last account we wait for confirmation in order to ensure all accounts were setup correctly + let wait_for_confirmation = i == self.number_clients - 1; + let account_funding_request = get_json_request(shard, &top, shielding_pubkey); + + let client = + BenchmarkClient::new(account, initial_balance, account_funding_request, cli); + let _result = wait_for_top_confirmation(wait_for_confirmation, &client); + accounts.push(client); + } + + rayon::ThreadPoolBuilder::new() + .num_threads(self.number_clients as usize) + .build_global() + .unwrap(); + + let overall_start = Instant::now(); + + // Run actual benchmark logic, in parallel, for each account initialized above. + let outputs: Vec> = accounts + .into_par_iter() + .map(move |mut client| { + let mut output: Vec = Vec::new(); + + for i in 0..self.number_iterations { + println!("Iteration: {}", i); + + if random_wait_before_transaction_ms.1 > 0 { + random_wait(random_wait_before_transaction_ms); + } + + // Create new account. + let account_keys = LocalKeystore::sr25519_generate_new(&store, SR25519_KEY_TYPE, None).unwrap(); + + let new_account = + get_pair_from_str(trusted_args, account_keys.to_string().as_str(), cli); + + println!(" Transfer amount: {}", EXISTENTIAL_DEPOSIT); + println!(" From: {:?}", client.account.public()); + println!(" To: {:?}", new_account.public()); + + // Get nonce of account. + let nonce = get_nonce(client.account.clone(), shard, &client.client_api); + + // Transfer money from client account to new account. + let top: TrustedOperation = TrustedCall::balance_transfer( + client.account.public().into(), + new_account.public().into(), + EXISTENTIAL_DEPOSIT, + ) + .sign(&KeyPair::Sr25519(Box::new(client.account.clone())), nonce, &mrenclave, &shard) + .into_trusted_operation(trusted_args.direct); + + let last_iteration = i == self.number_iterations - 1; + let jsonrpc_call = get_json_request(shard, &top, shielding_pubkey); + client.client_api.send(&jsonrpc_call).unwrap(); + let result = wait_for_top_confirmation( + self.wait_for_confirmation || last_iteration, + &client, + ); + + client.current_balance -= EXISTENTIAL_DEPOSIT; + + let balance = get_balance(client.account.clone(), shard, &client.client_api); + println!("Balance: {}", balance.unwrap_or_default()); + assert_eq!(client.current_balance, balance.unwrap()); + + output.push(result); + + // FIXME: We probably should re-fund the account in this case. + if client.current_balance <= EXISTENTIAL_DEPOSIT + STF_TX_FEE { + error!("Account {:?} does not have enough balance anymore. Finishing benchmark early", client.account.public()); + break; + } + } + + client.client_api.close().unwrap(); + + output + }) + .collect(); + + println!( + "Finished benchmark with {} clients and {} transactions in {} ms", + self.number_clients, + self.number_iterations, + overall_start.elapsed().as_millis() + ); + + print_benchmark_statistic(outputs, self.wait_for_confirmation); + + Ok(CliResultOk::None) + } +} + +fn get_balance( + account: sr25519::Pair, + shard: ShardIdentifier, + direct_client: &DirectClient, +) -> Option { + let getter = Getter::trusted( + TrustedGetter::free_balance(account.public().into()) + .sign(&KeyPair::Sr25519(Box::new(account.clone()))), + ); + + let getter_start_timer = Instant::now(); + let getter_result = direct_client.get_state(shard, &getter); + let getter_execution_time = getter_start_timer.elapsed().as_millis(); + + let balance = decode_balance(getter_result); + info!("Balance getter execution took {} ms", getter_execution_time,); + debug!("Retrieved {:?} Balance for {:?}", balance.unwrap_or_default(), account.public()); + balance +} + +fn get_nonce( + account: sr25519::Pair, + shard: ShardIdentifier, + direct_client: &DirectClient, +) -> Index { + let getter = Getter::public(PublicGetter::nonce(account.public().into())); + + let getter_start_timer = Instant::now(); + let nonce = get_state::(direct_client, shard, &getter).ok().unwrap_or_default(); + let getter_execution_time = getter_start_timer.elapsed().as_millis(); + info!("Nonce getter execution took {} ms", getter_execution_time,); + debug!("Retrieved {:?} nonce for {:?}", nonce, account.public()); + nonce +} + +fn print_benchmark_statistic(outputs: Vec>, wait_for_confirmation: bool) { + let mut hist = Histogram::::new(1).unwrap(); + for output in outputs { + for t in output { + let benchmarked_timestamp = + if wait_for_confirmation { t.confirmed } else { Some(t.submitted) }; + if let Some(confirmed) = benchmarked_timestamp { + hist += confirmed.duration_since(t.started).as_millis() as u64; + } else { + println!("Missing measurement data"); + } + } + } + + for i in (5..=100).step_by(5) { + let text = format!( + "{} percent are done within {} ms", + i, + hist.value_at_quantile(i as f64 / 100.0) + ); + println!("{}", text); + } +} + +fn random_wait(random_wait_before_transaction_ms: (u32, u32)) { + let mut rng = rand::thread_rng(); + let sleep_time = time::Duration::from_millis( + rng.gen_range(random_wait_before_transaction_ms.0..=random_wait_before_transaction_ms.1) + .into(), + ); + println!("Sleep for: {}ms", sleep_time.as_millis()); + thread::sleep(sleep_time); +} + +fn wait_for_top_confirmation( + wait_for_sidechain_block: bool, + client: &BenchmarkClient, +) -> BenchmarkTransaction { + let started = Instant::now(); + + let submitted = wait_until(&client.receiver, is_submitted); + + let confirmed = if wait_for_sidechain_block { + // We wait for the transaction hash that actually matches the submitted hash + loop { + let transaction_information = wait_until(&client.receiver, is_sidechain_block); + if let Some((hash, _)) = transaction_information { + if hash == submitted.unwrap().0 { + break transaction_information + } + } + } + } else { + None + }; + if let (Some(s), Some(c)) = (submitted, confirmed) { + // Assert the two hashes are identical + assert_eq!(s.0, c.0); + } + + BenchmarkTransaction { + started, + submitted: submitted.unwrap().1, + confirmed: confirmed.map(|v| v.1), + } +} + +fn is_submitted(s: TrustedOperationStatus) -> bool { + matches!(s, Submitted) +} + +fn is_sidechain_block(s: TrustedOperationStatus) -> bool { + matches!(s, InSidechainBlock(_)) +} + +fn decode_balance(maybe_encoded_balance: Option>) -> Option { + maybe_encoded_balance.and_then(|encoded_balance| { + if let Ok(vd) = Balance::decode(&mut encoded_balance.as_slice()) { + Some(vd) + } else { + warn!("Could not decode balance. maybe hasn't been set? {:x?}", encoded_balance); + None + } + }) +} diff --git a/bitacross-worker/cli/src/command_utils.rs b/bitacross-worker/cli/src/command_utils.rs new file mode 100644 index 0000000000..1779a6aebf --- /dev/null +++ b/bitacross-worker/cli/src/command_utils.rs @@ -0,0 +1,87 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::Cli; +use base58::FromBase58; +use itc_rpc_client::direct_client::{DirectApi, DirectClient as DirectWorkerApi}; +use itp_node_api::api_client::{ParentchainApi, TungsteniteRpcClient}; +use litentry_primitives::{ParentchainAccountId as AccountId, ParentchainSignature as Signature}; +use log::*; +use sgx_crypto_helper::rsa3072::Rsa3072PubKey; +use sp_application_crypto::sr25519; +use sp_core::{crypto::Ss58Codec, Pair}; +use sp_runtime::traits::{IdentifyAccount, Verify}; +use std::path::PathBuf; +use substrate_client_keystore::LocalKeystore; + +type AccountPublic = ::Signer; +pub(crate) const KEYSTORE_PATH: &str = "my_keystore"; + +/// Retrieves the public shielding key via the enclave websocket server. +pub(crate) fn get_shielding_key(cli: &Cli) -> Result { + let worker_api_direct = get_worker_api_direct(cli); + worker_api_direct.get_rsa_pubkey().map_err(|e| e.to_string()) +} + +pub(crate) fn get_chain_api(cli: &Cli) -> ParentchainApi { + let url = format!("{}:{}", cli.node_url, cli.node_port); + info!("connecting to {}", url); + ParentchainApi::new(TungsteniteRpcClient::new(&url, 5).unwrap()).unwrap() +} + +pub(crate) fn get_accountid_from_str(account: &str) -> AccountId { + match &account[..2] { + "//" => AccountPublic::from(sr25519::Pair::from_string(account, None).unwrap().public()) + .into_account(), + _ => AccountPublic::from(sr25519::Public::from_ss58check(account).unwrap()).into_account(), + } +} + +pub(crate) fn get_worker_api_direct(cli: &Cli) -> DirectWorkerApi { + let url = format!("{}:{}", cli.worker_url, cli.trusted_worker_port); + info!("Connecting to bitacross-worker-direct-port on '{}'", url); + DirectWorkerApi::new(url) +} + +/// get a pair either form keyring (well known keys) or from the store +pub(crate) fn get_pair_from_str(account: &str) -> sr25519::AppPair { + info!("getting pair for {}", account); + match &account[..2] { + "//" => sr25519::AppPair::from_string(account, None).unwrap(), + _ => { + info!("fetching from keystore at {}", &KEYSTORE_PATH); + // open store without password protection + let store = LocalKeystore::open(PathBuf::from(&KEYSTORE_PATH), None) + .expect("store should exist"); + info!("store opened"); + let _pair = store + .key_pair::( + &sr25519::Public::from_ss58check(account).unwrap().into(), + ) + .unwrap() + .unwrap(); + drop(store); + _pair + }, + } +} + +pub(crate) fn mrenclave_from_base58(src: &str) -> [u8; 32] { + let mut mrenclave = [0u8; 32]; + mrenclave.copy_from_slice(&src.from_base58().expect("mrenclave has to be base58 encoded")); + mrenclave +} diff --git a/bitacross-worker/cli/src/commands.rs b/bitacross-worker/cli/src/commands.rs new file mode 100644 index 0000000000..e01a79d930 --- /dev/null +++ b/bitacross-worker/cli/src/commands.rs @@ -0,0 +1,60 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +extern crate chrono; +use crate::{base_cli::BaseCommand, trusted_cli::TrustedCli, Cli, CliResult, CliResultOk}; +use clap::Subcommand; + +#[cfg(feature = "teeracle")] +use crate::oracle::OracleCommand; + +use crate::attesteer::AttesteerCommand; + +#[derive(Subcommand)] +pub enum Commands { + #[clap(flatten)] + Base(BaseCommand), + + /// trusted calls to worker enclave + #[clap(after_help = "stf subcommands depend on the stf crate this has been built against")] + Trusted(TrustedCli), + + /// Subcommands for the oracle. + #[cfg(feature = "teeracle")] + #[clap(subcommand)] + Oracle(OracleCommand), + + /// Subcommand for the attesteer. + #[clap(subcommand)] + Attesteer(AttesteerCommand), +} + +pub fn match_command(cli: &Cli) -> CliResult { + match &cli.command { + Commands::Base(cmd) => cmd.run(cli), + Commands::Trusted(trusted_cli) => trusted_cli.run(cli), + #[cfg(feature = "teeracle")] + Commands::Oracle(cmd) => { + cmd.run(cli); + Ok(CliResultOk::None) + }, + Commands::Attesteer(cmd) => { + cmd.run(cli); + Ok(CliResultOk::None) + }, + } +} diff --git a/bitacross-worker/cli/src/error.rs b/bitacross-worker/cli/src/error.rs new file mode 100644 index 0000000000..ad64fb4ca1 --- /dev/null +++ b/bitacross-worker/cli/src/error.rs @@ -0,0 +1,36 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +use itp_node_api::api_client::ApiClientError; +use itp_types::parentchain::{BlockHash, BlockNumber}; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("{0:?}")] + ApiClient(ApiClientError), + #[error("Could not retrieve Header from node")] + MissingBlock, + #[error("Confirmed Block Number ({0:?}) exceeds expected one ({0:?})")] + ConfirmedBlockNumberTooHigh(BlockNumber, BlockNumber), + #[error("Confirmed Block Hash ({0:?}) does not match expected one ({0:?})")] + ConfirmedBlockHashDoesNotMatchExpected(BlockHash, BlockHash), +} + +impl From for Error { + fn from(error: ApiClientError) -> Self { + Error::ApiClient(error) + } +} diff --git a/bitacross-worker/cli/src/evm/commands/evm_call.rs b/bitacross-worker/cli/src/evm/commands/evm_call.rs new file mode 100644 index 0000000000..04a7b56879 --- /dev/null +++ b/bitacross-worker/cli/src/evm/commands/evm_call.rs @@ -0,0 +1,87 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + get_layer_two_evm_nonce, get_layer_two_nonce, + trusted_cli::TrustedCli, + trusted_command_utils::{get_identifiers, get_pair_from_str}, + trusted_operation::perform_trusted_operation, + Cli, CliResult, CliResultOk, +}; +use ita_stf::{Index, TrustedCall, TrustedGetter}; +use itp_stf_primitives::{ + traits::TrustedCallSigning, + types::{KeyPair, TrustedOperation}, +}; +use itp_types::AccountId; +use log::*; +use sp_core::{crypto::Ss58Codec, Pair, H160, U256}; +use std::{boxed::Box, vec::Vec}; +#[derive(Parser)] +pub struct EvmCallCommands { + /// Sender's incognito AccountId in ss58check format + from: String, + + /// Execution address of the smart contract + execution_address: String, + + /// Function hash + function: String, +} + +impl EvmCallCommands { + pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) -> CliResult { + let sender = get_pair_from_str(trusted_args, &self.from, cli); + let sender_acc: AccountId = sender.public().into(); + + info!("senders ss58 is {}", sender.public().to_ss58check()); + + let mut sender_evm_acc_slice: [u8; 20] = [0; 20]; + sender_evm_acc_slice + .copy_from_slice((<[u8; 32]>::from(sender_acc.clone())).get(0..20).unwrap()); + let sender_evm_acc: H160 = sender_evm_acc_slice.into(); + + info!("senders evm account is {}", sender_evm_acc); + + let execution_address = + H160::from_slice(&array_bytes::hex2bytes(&self.execution_address).unwrap()); + + let function_hash = array_bytes::hex2bytes(&self.function).unwrap(); + + let (mrenclave, shard) = get_identifiers(trusted_args, cli); + let nonce = get_layer_two_nonce!(sender, cli, trusted_args); + let evm_nonce = get_layer_two_evm_nonce!(sender, cli, trusted_args); + + println!("calling smart contract function"); + let function_call = TrustedCall::evm_call( + sender_acc.into(), + sender_evm_acc, + execution_address, + function_hash, + U256::from(0), + 10_000_000, // gas limit + U256::from(1), // max_fee_per_gas !>= min_gas_price defined in runtime + None, + Some(U256::from(evm_nonce)), + Vec::new(), + ) + .sign(&KeyPair::Sr25519(Box::new(sender)), nonce, &mrenclave, &shard) + .into_trusted_operation(trusted_args.direct); + Ok(perform_trusted_operation::<()>(cli, trusted_args, &function_call) + .map(|_| CliResultOk::None)?) + } +} diff --git a/bitacross-worker/cli/src/evm/commands/evm_command_utils.rs b/bitacross-worker/cli/src/evm/commands/evm_command_utils.rs new file mode 100644 index 0000000000..cc8c5fff34 --- /dev/null +++ b/bitacross-worker/cli/src/evm/commands/evm_command_utils.rs @@ -0,0 +1,32 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[macro_export] +macro_rules! get_layer_two_evm_nonce { + ($signer_pair:ident, $cli:ident, $trusted_args:ident ) => {{ + use ita_stf::{Getter, TrustedCallSigned}; + + let top = TrustedOperation::::get(Getter::trusted( + TrustedGetter::evm_nonce($signer_pair.public().into()) + .sign(&KeyPair::Sr25519(Box::new($signer_pair.clone()))), + )); + let res = perform_trusted_operation::($cli, $trusted_args, &top); + let nonce = res.ok().unwrap_or(0); + debug!("got evm nonce: {:?}", nonce); + nonce + }}; +} diff --git a/bitacross-worker/cli/src/evm/commands/evm_create.rs b/bitacross-worker/cli/src/evm/commands/evm_create.rs new file mode 100644 index 0000000000..acce77e3e5 --- /dev/null +++ b/bitacross-worker/cli/src/evm/commands/evm_create.rs @@ -0,0 +1,89 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + get_layer_two_evm_nonce, get_layer_two_nonce, + trusted_cli::TrustedCli, + trusted_command_utils::{get_identifiers, get_pair_from_str}, + trusted_operation::perform_trusted_operation, + Cli, CliResult, CliResultOk, +}; +use ita_stf::{evm_helpers::evm_create_address, Index, TrustedCall, TrustedGetter}; +use itp_stf_primitives::{ + traits::TrustedCallSigning, + types::{KeyPair, TrustedOperation}, +}; +use itp_types::AccountId; +use log::*; +use pallet_evm::{AddressMapping, HashedAddressMapping}; +use sp_core::{crypto::Ss58Codec, Pair, H160, U256}; +use sp_runtime::traits::BlakeTwo256; +use std::vec::Vec; +#[derive(Parser)] +pub struct EvmCreateCommands { + /// Sender's incognito AccountId in ss58check format + from: String, + + /// Smart Contract in Hex format + smart_contract: String, +} + +impl EvmCreateCommands { + pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) -> CliResult { + let from = get_pair_from_str(trusted_args, &self.from, cli); + let from_acc: AccountId = from.public().into(); + println!("from ss58 is {}", from.public().to_ss58check()); + + let mut sender_evm_acc_slice: [u8; 20] = [0; 20]; + sender_evm_acc_slice + .copy_from_slice((<[u8; 32]>::from(from_acc.clone())).get(0..20).unwrap()); + let sender_evm_acc: H160 = sender_evm_acc_slice.into(); + + let (mrenclave, shard) = get_identifiers(trusted_args, cli); + + let sender_evm_substrate_addr = + HashedAddressMapping::::into_account_id(sender_evm_acc); + println!( + "Trying to get nonce of evm account {:?}", + sender_evm_substrate_addr.to_ss58check() + ); + + let nonce = get_layer_two_nonce!(from, cli, trusted_args); + let evm_account_nonce = get_layer_two_evm_nonce!(from, cli, trusted_args); + + let top = TrustedCall::evm_create( + from_acc.into(), + sender_evm_acc, + array_bytes::hex2bytes(&self.smart_contract).unwrap().to_vec(), + U256::from(0), + 967295, // gas limit + U256::from(1), // max_fee_per_gas !>= min_gas_price defined in runtime + None, + None, + Vec::new(), + ) + .sign(&from.into(), nonce, &mrenclave, &shard) + .into_trusted_operation(trusted_args.direct); + + perform_trusted_operation::<()>(cli, trusted_args, &top)?; + + let execution_address = evm_create_address(sender_evm_acc, evm_account_nonce); + info!("trusted call evm_create executed"); + println!("Created the smart contract with address {:?}", execution_address); + Ok(CliResultOk::H160 { hash: execution_address }) + } +} diff --git a/bitacross-worker/cli/src/evm/commands/evm_read.rs b/bitacross-worker/cli/src/evm/commands/evm_read.rs new file mode 100644 index 0000000000..b863533860 --- /dev/null +++ b/bitacross-worker/cli/src/evm/commands/evm_read.rs @@ -0,0 +1,69 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + trusted_cli::TrustedCli, trusted_command_utils::get_pair_from_str, + trusted_operation::perform_trusted_operation, Cli, CliError, CliResult, CliResultOk, +}; +use ita_stf::{Getter, TrustedCallSigned, TrustedGetter}; +use itp_stf_primitives::types::{KeyPair, TrustedOperation}; +use itp_types::AccountId; +use log::*; +use sp_core::{crypto::Ss58Codec, Pair, H160, H256}; + +#[derive(Parser)] +pub struct EvmReadCommands { + /// Sender's incognito AccountId in ss58check format + from: String, + + /// Execution address of the smart contract + execution_address: String, +} + +impl EvmReadCommands { + pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) -> CliResult { + let sender = get_pair_from_str(trusted_args, &self.from, cli); + let sender_acc: AccountId = sender.public().into(); + + info!("senders ss58 is {}", sender.public().to_ss58check()); + + let mut sender_evm_acc_slice: [u8; 20] = [0; 20]; + sender_evm_acc_slice + .copy_from_slice((<[u8; 32]>::from(sender_acc.clone())).get(0..20).unwrap()); + let sender_evm_acc: H160 = sender_evm_acc_slice.into(); + + info!("senders evm account is {}", sender_evm_acc); + + let execution_address = + H160::from_slice(&array_bytes::hex2bytes(&self.execution_address).unwrap()); + + let top = TrustedOperation::::get(Getter::trusted( + TrustedGetter::evm_account_storages(sender_acc.into(), execution_address, H256::zero()) + .sign(&KeyPair::Sr25519(Box::new(sender))), + )); + match perform_trusted_operation::(cli, trusted_args, &top) { + Ok(hash) => { + println!("{:?}", hash); + Ok(CliResultOk::H256 { hash }) + }, + Err(e) => { + error!("Nothing in state! Reason: {:?} !", e); + Err(CliError::EvmRead { msg: "Nothing in state!".to_string() }) + }, + } + } +} diff --git a/bitacross-worker/cli/src/evm/commands/mod.rs b/bitacross-worker/cli/src/evm/commands/mod.rs new file mode 100644 index 0000000000..014b093832 --- /dev/null +++ b/bitacross-worker/cli/src/evm/commands/mod.rs @@ -0,0 +1,6 @@ +pub mod evm_call; +pub mod evm_command_utils; +pub mod evm_create; +pub mod evm_read; + +pub use crate::get_layer_two_evm_nonce; diff --git a/bitacross-worker/cli/src/evm/mod.rs b/bitacross-worker/cli/src/evm/mod.rs new file mode 100644 index 0000000000..0b1ff31d47 --- /dev/null +++ b/bitacross-worker/cli/src/evm/mod.rs @@ -0,0 +1,49 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + evm::commands::{ + evm_call::EvmCallCommands, evm_create::EvmCreateCommands, evm_read::EvmReadCommands, + }, + trusted_cli::TrustedCli, + Cli, CliResult, +}; + +mod commands; + +#[allow(clippy::enum_variant_names)] +#[derive(Subcommand)] +pub enum EvmCommand { + /// Create smart contract + EvmCreate(EvmCreateCommands), + + /// Read smart contract storage + EvmRead(EvmReadCommands), + + /// Create smart contract + EvmCall(EvmCallCommands), +} + +impl EvmCommand { + pub fn run(&self, cli: &Cli, trusted_args: &TrustedCli) -> CliResult { + match self { + EvmCommand::EvmCreate(cmd) => cmd.run(cli, trusted_args), + EvmCommand::EvmRead(cmd) => cmd.run(cli, trusted_args), + EvmCommand::EvmCall(cmd) => cmd.run(cli, trusted_args), + } + } +} diff --git a/bitacross-worker/cli/src/lib.rs b/bitacross-worker/cli/src/lib.rs new file mode 100644 index 0000000000..0738cc6dd4 --- /dev/null +++ b/bitacross-worker/cli/src/lib.rs @@ -0,0 +1,137 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! an RPC client to Integritee using websockets +//! +//! examples +//! litentry_cli 127.0.0.1:9944 transfer //Alice 5G9RtsTbiYJYQYMHbWfyPoeuuxNaCbC16tZ2JGrZ4gRKwz14 1000 +//! +#![feature(rustc_private)] +#[macro_use] +extern crate clap; +extern crate chrono; + +extern crate env_logger; +extern crate log; + +mod attesteer; +mod base_cli; +mod benchmark; +mod command_utils; +mod error; +#[cfg(feature = "evm")] +mod evm; +#[cfg(feature = "teeracle")] +mod oracle; +mod trusted_base_cli; +mod trusted_cli; +mod trusted_command_utils; +mod trusted_operation; + +pub mod commands; + +use crate::commands::Commands; +use clap::Parser; +use itp_node_api::api_client::Metadata; +use sp_application_crypto::KeyTypeId; +use sp_core::{H160, H256}; +use thiserror::Error; + +const VERSION: &str = env!("CARGO_PKG_VERSION"); + +pub(crate) const SR25519_KEY_TYPE: KeyTypeId = KeyTypeId(*b"sr25"); +pub(crate) const ED25519_KEY_TYPE: KeyTypeId = KeyTypeId(*b"ed25"); + +#[derive(Parser)] +#[clap(name = "bitacross-cli")] +#[clap(version = VERSION)] +#[clap(author = "Trust Computing GmbH ")] +#[clap(about = "cli tool to interact with litentry-parachain and workers", long_about = None)] +#[cfg_attr(feature = "teeracle", clap(about = "interact with litentry-parachain and teeracle", long_about = None))] +#[cfg_attr(feature = "sidechain", clap(about = "interact with litentry-parachain and sidechain", long_about = None))] +#[cfg_attr(feature = "offchain-worker", clap(about = "interact with litentry-parachain and offchain-worker", long_about = None))] +#[clap(after_help = "stf subcommands depend on the stf crate this has been built against")] +pub struct Cli { + /// node url + #[clap(short = 'u', long, default_value_t = String::from("ws://127.0.0.1"))] + node_url: String, + + /// node port + #[clap(short = 'p', long, default_value_t = String::from("9944"))] + node_port: String, + + /// worker url + #[clap(short = 'U', long, default_value_t = String::from("wss://127.0.0.1"))] + worker_url: String, + + /// worker direct invocation port + #[clap(short = 'P', long, default_value_t = String::from("2000"))] + trusted_worker_port: String, + + #[clap(subcommand)] + command: Commands, +} + +pub enum CliResultOk { + PubKeysBase58 { + pubkeys_sr25519: Option>, + pubkeys_ed25519: Option>, + }, + Balance { + balance: u128, + }, + MrEnclaveBase58 { + mr_enclaves: Vec, + }, + Metadata { + metadata: Metadata, + }, + H256 { + hash: H256, + }, + /// Result of "EvmCreateCommands": execution_address + H160 { + hash: H160, + }, + // TODO should ideally be removed; or at least drastically less used + // We WANT all commands exposed by the cli to return something useful for the caller(ie instead of printing) + None, +} + +#[derive(Debug, Error)] +pub enum CliError { + #[error("extrinsic error: {:?}", msg)] + Extrinsic { msg: String }, + #[error("trusted operation error: {:?}", msg)] + TrustedOp { msg: String }, + #[error("EvmReadCommands error: {:?}", msg)] + EvmRead { msg: String }, + #[error("worker rpc api error: {:?}", msg)] + WorkerRpcApi { msg: String }, +} + +pub type CliResult = Result; + +/// This is used for the commands that directly call `perform_trusted_operation` +/// which typically return `CliResultOk::None` +/// +/// eg: `SetBalanceCommand`,`TransferCommand`,`UnshieldFundsCommand` +impl From for CliError { + fn from(value: trusted_operation::TrustedOperationError) -> Self { + CliError::TrustedOp { msg: value.to_string() } + } +} diff --git a/bitacross-worker/cli/src/main.rs b/bitacross-worker/cli/src/main.rs new file mode 100644 index 0000000000..2e4652612a --- /dev/null +++ b/bitacross-worker/cli/src/main.rs @@ -0,0 +1,27 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use bitacross_cli::{commands, Cli}; +use clap::Parser; + +fn main() { + env_logger::init(); + + let cli = Cli::parse(); + + commands::match_command(&cli).unwrap(); +} diff --git a/bitacross-worker/cli/src/oracle/commands/add_to_whitelist.rs b/bitacross-worker/cli/src/oracle/commands/add_to_whitelist.rs new file mode 100644 index 0000000000..98afeb801d --- /dev/null +++ b/bitacross-worker/cli/src/oracle/commands/add_to_whitelist.rs @@ -0,0 +1,67 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + command_utils::{get_chain_api, get_pair_from_str, mrenclave_from_base58}, + Cli, +}; +use itp_node_api::api_client::{ADD_TO_WHITELIST, TEERACLE}; +use substrate_api_client::{ + ac_compose_macros::{compose_call, compose_extrinsic}, + SubmitAndWatch, XtStatus, +}; + +/// Add a trusted market data source to the on-chain whitelist. +#[derive(Debug, Clone, Parser)] +pub struct AddToWhitelistCmd { + /// Sender's on-chain AccountId in ss58check format. + /// + /// It has to be a sudo account. + from: String, + + /// Market data URL + source: String, + + /// MRENCLAVE of the oracle worker base58 encoded. + mrenclave: String, +} + +impl AddToWhitelistCmd { + pub fn run(&self, cli: &Cli) { + let mut api = get_chain_api(cli); + let mrenclave = mrenclave_from_base58(&self.mrenclave); + let from = get_pair_from_str(&self.from); + + let market_data_source = self.source.clone(); + + api.set_signer(from.into()); + + let call = compose_call!( + api.metadata(), + TEERACLE, + ADD_TO_WHITELIST, + market_data_source, + mrenclave + ); + + // compose the extrinsic + let xt = compose_extrinsic!(api, "Sudo", "sudo", call); + + let report = api.submit_and_watch_extrinsic_until(xt, XtStatus::Finalized).unwrap(); + println!("[+] Add to whitelist got finalized. Hash: {:?}\n", report.extrinsic_hash); + } +} diff --git a/bitacross-worker/cli/src/oracle/commands/listen_to_exchange.rs b/bitacross-worker/cli/src/oracle/commands/listen_to_exchange.rs new file mode 100644 index 0000000000..181be4febd --- /dev/null +++ b/bitacross-worker/cli/src/oracle/commands/listen_to_exchange.rs @@ -0,0 +1,79 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{command_utils::get_chain_api, Cli}; +use itp_node_api::api_client::ParentchainApi; +use itp_time_utils::{duration_now, remaining_time}; +use log::{debug, info, trace}; +use my_node_runtime::{Hash, RuntimeEvent}; +use std::time::Duration; +use substrate_api_client::SubscribeEvents; + +/// Listen to exchange rate events. +#[derive(Debug, Clone, Parser)] +pub struct ListenToExchangeRateEventsCmd { + /// Listen for `duration` in seconds. + duration: u64, +} + +impl ListenToExchangeRateEventsCmd { + pub fn run(&self, cli: &Cli) { + let api = get_chain_api(cli); + let duration = Duration::from_secs(self.duration); + + let count = count_exchange_rate_update_events(&api, duration); + + println!("Number of ExchangeRateUpdated events received : "); + println!(" EVENTS_COUNT: {}", count); + } +} + +pub fn count_exchange_rate_update_events(api: &ParentchainApi, duration: Duration) -> u32 { + let stop = duration_now() + duration; + + //subscribe to events + let mut subscription = api.subscribe_events().unwrap(); + let mut count = 0; + + while remaining_time(stop).unwrap_or_default() > Duration::ZERO { + let events_result = subscription.next_events::().unwrap(); + if let Ok(events) = events_result { + for event_record in &events { + info!("received event {:?}", event_record.event); + if let RuntimeEvent::Teeracle(event) = &event_record.event { + match &event { + my_node_runtime::pallet_teeracle::Event::ExchangeRateUpdated( + data_source, + trading_pair, + exchange_rate, + ) => { + count += 1; + debug!("Received ExchangeRateUpdated event"); + println!( + "ExchangeRateUpdated: TRADING_PAIR : {}, SRC : {}, VALUE :{:?}", + trading_pair, data_source, exchange_rate + ); + }, + _ => trace!("ignoring teeracle event: {:?}", event), + } + } + } + } + } + debug!("Received {} ExchangeRateUpdated event(s) in total", count); + count +} diff --git a/bitacross-worker/cli/src/oracle/commands/listen_to_oracle.rs b/bitacross-worker/cli/src/oracle/commands/listen_to_oracle.rs new file mode 100644 index 0000000000..87cc334040 --- /dev/null +++ b/bitacross-worker/cli/src/oracle/commands/listen_to_oracle.rs @@ -0,0 +1,91 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{command_utils::get_chain_api, Cli}; +use itp_node_api::api_client::ParentchainApi; +use itp_time_utils::{duration_now, remaining_time}; +use log::{debug, info}; +use my_node_runtime::{Hash, RuntimeEvent}; +use std::time::Duration; +use substrate_api_client::{ac_node_api::EventRecord, SubscribeEvents}; + +/// Listen to exchange rate events. +#[derive(Debug, Clone, Parser)] +pub struct ListenToOracleEventsCmd { + /// Listen for `duration` in seconds. + duration: u64, +} + +type EventCount = u32; +type Event = EventRecord; + +impl ListenToOracleEventsCmd { + pub fn run(&self, cli: &Cli) { + let api = get_chain_api(cli); + let duration = Duration::from_secs(self.duration); + let count = count_oracle_update_events(&api, duration); + println!("Number of Oracle events received : "); + println!(" EVENTS_COUNT: {}", count); + } +} + +fn count_oracle_update_events(api: &ParentchainApi, duration: Duration) -> EventCount { + let stop = duration_now() + duration; + + //subscribe to events + let mut subscription = api.subscribe_events().unwrap(); + let mut count = 0; + + while remaining_time(stop).unwrap_or_default() > Duration::ZERO { + let events_result = subscription.next_events::(); + let event_count = match events_result { + Some(Ok(event_records)) => { + debug!("Could not successfully decode event_bytes {:?}", event_records); + report_event_count(event_records) + }, + _ => 0, + }; + count += event_count; + } + debug!("Received {} ExchangeRateUpdated event(s) in total", count); + count +} + +fn report_event_count(event_records: Vec) -> EventCount { + let mut count = 0; + event_records.iter().for_each(|event_record| { + info!("received event {:?}", event_record.event); + if let RuntimeEvent::Teeracle(event) = &event_record.event { + match &event { + my_node_runtime::pallet_teeracle::Event::OracleUpdated( + oracle_data_name, + data_source, + ) => { + count += 1; + debug!("Received OracleUpdated event"); + println!( + "OracleUpdated: ORACLE_NAME : {}, SRC : {}", + oracle_data_name, data_source + ); + }, + // Can just remove this and ignore handling this case + _ => debug!("ignoring teeracle event: {:?}", event), + } + } + }); + count +} diff --git a/bitacross-worker/cli/src/oracle/commands/mod.rs b/bitacross-worker/cli/src/oracle/commands/mod.rs new file mode 100644 index 0000000000..22b0a326c6 --- /dev/null +++ b/bitacross-worker/cli/src/oracle/commands/mod.rs @@ -0,0 +1,25 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +mod add_to_whitelist; +mod listen_to_exchange; +mod listen_to_oracle; + +pub use self::{ + add_to_whitelist::AddToWhitelistCmd, listen_to_exchange::ListenToExchangeRateEventsCmd, + listen_to_oracle::ListenToOracleEventsCmd, +}; diff --git a/bitacross-worker/cli/src/oracle/mod.rs b/bitacross-worker/cli/src/oracle/mod.rs new file mode 100644 index 0000000000..e12f117cd4 --- /dev/null +++ b/bitacross-worker/cli/src/oracle/mod.rs @@ -0,0 +1,49 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Add cli commands for the oracle +//! +//! Todo: This shall be a standalone crate in app-libs/oracle. However, this needs: +//! https://github.com/integritee-network/worker/issues/852 + +use crate::Cli; +use commands::{AddToWhitelistCmd, ListenToExchangeRateEventsCmd, ListenToOracleEventsCmd}; + +mod commands; + +/// Oracle subcommands for the cli. +#[derive(Debug, clap::Subcommand)] +pub enum OracleCommand { + /// Add a market source to the teeracle's whitelist. + AddToWhitelist(AddToWhitelistCmd), + + /// Listen to exchange rate events + ListenToExchangeRateEvents(ListenToExchangeRateEventsCmd), + + /// Listen to all oracles event updates + ListenToOracleEvents(ListenToOracleEventsCmd), +} + +impl OracleCommand { + pub fn run(&self, cli: &Cli) { + match self { + OracleCommand::AddToWhitelist(cmd) => cmd.run(cli), + OracleCommand::ListenToExchangeRateEvents(cmd) => cmd.run(cli), + OracleCommand::ListenToOracleEvents(cmd) => cmd.run(cli), + } + } +} diff --git a/bitacross-worker/cli/src/trusted_base_cli/commands/balance.rs b/bitacross-worker/cli/src/trusted_base_cli/commands/balance.rs new file mode 100644 index 0000000000..3b5b9f4f33 --- /dev/null +++ b/bitacross-worker/cli/src/trusted_base_cli/commands/balance.rs @@ -0,0 +1,34 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + trusted_cli::TrustedCli, trusted_command_utils::get_balance, Cli, CliResult, CliResultOk, +}; + +#[derive(Parser)] +pub struct BalanceCommand { + /// AccountId in ss58check format + account: String, +} + +impl BalanceCommand { + pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) -> CliResult { + let balance = get_balance(cli, trusted_args, &self.account).unwrap_or_default(); + println!("{}", balance); + Ok(CliResultOk::Balance { balance }) + } +} diff --git a/bitacross-worker/cli/src/trusted_base_cli/commands/get_shard.rs b/bitacross-worker/cli/src/trusted_base_cli/commands/get_shard.rs new file mode 100644 index 0000000000..fd16136cdb --- /dev/null +++ b/bitacross-worker/cli/src/trusted_base_cli/commands/get_shard.rs @@ -0,0 +1,69 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + command_utils::get_worker_api_direct, trusted_cli::TrustedCli, Cli, CliError, CliResult, + CliResultOk, +}; +use base58::ToBase58; +use codec::{Decode, Encode}; + +use itc_rpc_client::direct_client::DirectApi; +use itp_rpc::{Id, RpcRequest, RpcResponse, RpcReturnValue}; + +use itp_types::DirectRequestStatus; +use itp_utils::FromHexPrefixed; +use log::*; + +use sp_core::H256; + +#[derive(Parser)] +pub struct GetShardCommand {} + +impl GetShardCommand { + pub(crate) fn run(&self, cli: &Cli, _trusted_args: &TrustedCli) -> CliResult { + let direct_api = get_worker_api_direct(cli); + let rpc_method = "author_getShard".to_owned(); + let jsonrpc_call: String = + RpcRequest::compose_jsonrpc_call(Id::Text("1".to_string()), rpc_method, vec![]) + .unwrap(); + let rpc_response_str = direct_api.get(&jsonrpc_call).unwrap(); + // Decode RPC response. + let rpc_response: RpcResponse = serde_json::from_str(&rpc_response_str) + .map_err(|err| CliError::WorkerRpcApi { msg: err.to_string() })?; + let rpc_return_value = RpcReturnValue::from_hex(&rpc_response.result) + // Replace with `inspect_err` once it's stable. + .map_err(|err| { + error!("Failed to decode RpcReturnValue: {:?}", err); + CliError::WorkerRpcApi { msg: "failed to decode RpcReturnValue".to_string() } + })?; + + if rpc_return_value.status == DirectRequestStatus::Error { + println!("[Error] {}", String::decode(&mut rpc_return_value.value.as_slice()).unwrap()); + return Err(CliError::WorkerRpcApi { msg: "rpc error".to_string() }) + } + + let shard = H256::decode(&mut rpc_return_value.value.as_slice()) + // Replace with `inspect_err` once it's stable. + .map_err(|err| { + error!("Failed to decode shard: {:?}", err); + CliError::WorkerRpcApi { msg: err.to_string() } + })?; + println!("{}", shard.encode().to_base58()); + Ok(CliResultOk::H256 { hash: shard }) + } +} diff --git a/bitacross-worker/cli/src/trusted_base_cli/commands/get_shard_vault.rs b/bitacross-worker/cli/src/trusted_base_cli/commands/get_shard_vault.rs new file mode 100644 index 0000000000..a4af8a2ec8 --- /dev/null +++ b/bitacross-worker/cli/src/trusted_base_cli/commands/get_shard_vault.rs @@ -0,0 +1,73 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + command_utils::get_worker_api_direct, trusted_cli::TrustedCli, Cli, CliError, CliResult, + CliResultOk, +}; + +use codec::Decode; + +use itc_rpc_client::direct_client::DirectApi; +use itp_rpc::{Id, RpcRequest, RpcResponse, RpcReturnValue}; + +use itp_types::{AccountId, DirectRequestStatus}; +use itp_utils::FromHexPrefixed; +use log::*; + +use sp_core::crypto::Ss58Codec; + +#[derive(Parser)] +pub struct GetShardVaultCommand {} + +impl GetShardVaultCommand { + pub(crate) fn run(&self, cli: &Cli, _trusted_args: &TrustedCli) -> CliResult { + let direct_api = get_worker_api_direct(cli); + let rpc_method = "author_getShardVault".to_owned(); + let jsonrpc_call: String = + RpcRequest::compose_jsonrpc_call(Id::Text("1".to_string()), rpc_method, vec![]) + .unwrap(); + let rpc_response_str = direct_api.get(&jsonrpc_call).unwrap(); + // Decode RPC response. + let rpc_response: RpcResponse = serde_json::from_str(&rpc_response_str) + .map_err(|err| CliError::WorkerRpcApi { msg: err.to_string() })?; + let rpc_return_value = RpcReturnValue::from_hex(&rpc_response.result) + // Replace with `inspect_err` once it's stable. + .map_err(|err| { + error!("Failed to decode RpcReturnValue: {:?}", err); + CliError::WorkerRpcApi { msg: "failed to decode RpcReturnValue".to_string() } + })?; + + if rpc_return_value.status == DirectRequestStatus::Error { + println!("[Error] {}", String::decode(&mut rpc_return_value.value.as_slice()).unwrap()); + return Err(CliError::WorkerRpcApi { msg: "rpc error".to_string() }) + } + + let vault = AccountId::decode(&mut rpc_return_value.value.as_slice()) + // Replace with `inspect_err` once it's stable. + .map_err(|err| { + error!("Failed to decode vault account: {:?}", err); + CliError::WorkerRpcApi { msg: err.to_string() } + })?; + let vault_ss58 = vault.to_ss58check(); + println!("{}", vault_ss58); + Ok(CliResultOk::PubKeysBase58 { + pubkeys_sr25519: None, + pubkeys_ed25519: Some(vec![vault_ss58]), + }) + } +} diff --git a/bitacross-worker/cli/src/trusted_base_cli/commands/mod.rs b/bitacross-worker/cli/src/trusted_base_cli/commands/mod.rs new file mode 100644 index 0000000000..0687a4fe1d --- /dev/null +++ b/bitacross-worker/cli/src/trusted_base_cli/commands/mod.rs @@ -0,0 +1,7 @@ +pub mod balance; +pub mod get_shard; +pub mod get_shard_vault; +pub mod nonce; +pub mod set_balance; +pub mod transfer; +pub mod unshield_funds; diff --git a/bitacross-worker/cli/src/trusted_base_cli/commands/nonce.rs b/bitacross-worker/cli/src/trusted_base_cli/commands/nonce.rs new file mode 100644 index 0000000000..f8abee5519 --- /dev/null +++ b/bitacross-worker/cli/src/trusted_base_cli/commands/nonce.rs @@ -0,0 +1,44 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + command_utils::get_worker_api_direct, + trusted_cli::TrustedCli, + trusted_command_utils::{get_identifiers, get_pair_from_str}, + Cli, CliResult, CliResultOk, +}; +use itc_rpc_client::direct_client::DirectApi; +use sp_core::Pair; + +#[derive(Parser)] +pub struct NonceCommand { + /// AccountId in ss58check format + account: String, +} + +impl NonceCommand { + pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) -> CliResult { + let (_mrenclave, shard) = get_identifiers(trusted_args, cli); + let who = get_pair_from_str(trusted_args, &self.account, cli); + let worker_api_direct = get_worker_api_direct(cli); + let nonce_ret = worker_api_direct.get_next_nonce(&shard, &(who.public().into())); + let nonce = nonce_ret.expect("get nonce error!"); + println!("{}", nonce); + worker_api_direct.close().unwrap(); + Ok(CliResultOk::None) + } +} diff --git a/bitacross-worker/cli/src/trusted_base_cli/commands/set_balance.rs b/bitacross-worker/cli/src/trusted_base_cli/commands/set_balance.rs new file mode 100644 index 0000000000..5fd5f6c900 --- /dev/null +++ b/bitacross-worker/cli/src/trusted_base_cli/commands/set_balance.rs @@ -0,0 +1,64 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + get_layer_two_nonce, + trusted_cli::TrustedCli, + trusted_command_utils::{get_identifiers, get_pair_from_str}, + trusted_operation::perform_trusted_operation, + Cli, CliResult, CliResultOk, +}; +use ita_stf::{Getter, Index, TrustedCall, TrustedCallSigned}; +use itp_stf_primitives::{ + traits::TrustedCallSigning, + types::{KeyPair, TrustedOperation}, +}; +use litentry_primitives::ParentchainBalance as Balance; +use log::*; +use sp_core::{crypto::Ss58Codec, Pair}; +use std::boxed::Box; + +#[derive(Parser)] +pub struct SetBalanceCommand { + /// sender's AccountId in ss58check format + account: String, + + /// amount to be transferred + amount: Balance, +} + +impl SetBalanceCommand { + pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) -> CliResult { + let who = get_pair_from_str(trusted_args, &self.account, cli); + let signer = get_pair_from_str(trusted_args, "//Alice", cli); + info!("account ss58 is {}", who.public().to_ss58check()); + + println!("send trusted call set-balance({}, {})", who.public(), self.amount); + + let (mrenclave, shard) = get_identifiers(trusted_args, cli); + let nonce = get_layer_two_nonce!(signer, cli, trusted_args); + let top: TrustedOperation = TrustedCall::balance_set_balance( + signer.public().into(), + who.public().into(), + self.amount, + self.amount, + ) + .sign(&KeyPair::Sr25519(Box::new(signer)), nonce, &mrenclave, &shard) + .into_trusted_operation(trusted_args.direct); + Ok(perform_trusted_operation::<()>(cli, trusted_args, &top).map(|_| CliResultOk::None)?) + } +} diff --git a/bitacross-worker/cli/src/trusted_base_cli/commands/transfer.rs b/bitacross-worker/cli/src/trusted_base_cli/commands/transfer.rs new file mode 100644 index 0000000000..770833ffa5 --- /dev/null +++ b/bitacross-worker/cli/src/trusted_base_cli/commands/transfer.rs @@ -0,0 +1,72 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + get_layer_two_nonce, + trusted_cli::TrustedCli, + trusted_command_utils::{get_accountid_from_str, get_identifiers, get_pair_from_str}, + trusted_operation::perform_trusted_operation, + Cli, CliResult, CliResultOk, +}; +use ita_stf::{Getter, Index, TrustedCall, TrustedCallSigned}; +use itp_stf_primitives::{ + traits::TrustedCallSigning, + types::{KeyPair, TrustedOperation}, +}; +use litentry_primitives::ParentchainBalance as Balance; +use log::*; +use sp_core::{crypto::Ss58Codec, Pair}; +use std::boxed::Box; + +#[derive(Parser)] +pub struct TransferCommand { + /// sender's AccountId in ss58check format + from: String, + + /// recipient's AccountId in ss58check format + to: String, + + /// amount to be transferred + amount: Balance, +} + +impl TransferCommand { + pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) -> CliResult { + let from = get_pair_from_str(trusted_args, &self.from, cli); + let to = get_accountid_from_str(&self.to); + info!("from ss58 is {}", from.public().to_ss58check()); + info!("to ss58 is {}", to.to_ss58check()); + + let (mrenclave, shard) = get_identifiers(trusted_args, cli); + let nonce = get_layer_two_nonce!(from, cli, trusted_args); + println!( + "send trusted call transfer from {} to {}: {}, nonce: {}", + from.public(), + to, + self.amount, + nonce + ); + let top: TrustedOperation = + TrustedCall::balance_transfer(from.public().into(), to, self.amount) + .sign(&KeyPair::Sr25519(Box::new(from)), nonce, &mrenclave, &shard) + .into_trusted_operation(trusted_args.direct); + let res = + perform_trusted_operation::<()>(cli, trusted_args, &top).map(|_| CliResultOk::None)?; + info!("trusted call transfer executed"); + Ok(res) + } +} diff --git a/bitacross-worker/cli/src/trusted_base_cli/commands/unshield_funds.rs b/bitacross-worker/cli/src/trusted_base_cli/commands/unshield_funds.rs new file mode 100644 index 0000000000..6e78c54401 --- /dev/null +++ b/bitacross-worker/cli/src/trusted_base_cli/commands/unshield_funds.rs @@ -0,0 +1,67 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + get_layer_two_nonce, + trusted_cli::TrustedCli, + trusted_command_utils::{get_accountid_from_str, get_identifiers, get_pair_from_str}, + trusted_operation::perform_trusted_operation, + Cli, CliResult, CliResultOk, +}; +use ita_stf::{Getter, Index, TrustedCall, TrustedCallSigned}; +use itp_stf_primitives::{ + traits::TrustedCallSigning, + types::{KeyPair, TrustedOperation}, +}; +use litentry_primitives::ParentchainBalance as Balance; +use sp_core::{crypto::Ss58Codec, Pair}; +use std::boxed::Box; +#[derive(Parser)] +pub struct UnshieldFundsCommand { + /// Sender's incognito AccountId in ss58check format + from: String, + + /// Recipient's parentchain AccountId in ss58check format + to: String, + + /// amount to be transferred + amount: Balance, +} + +impl UnshieldFundsCommand { + pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) -> CliResult { + let from = get_pair_from_str(trusted_args, &self.from, cli); + let to = get_accountid_from_str(&self.to); + println!("from ss58 is {}", from.public().to_ss58check()); + println!("to ss58 is {}", to.to_ss58check()); + + println!( + "send trusted call unshield_funds from {} to {}: {}", + from.public(), + to, + self.amount + ); + + let (mrenclave, shard) = get_identifiers(trusted_args, cli); + let nonce = get_layer_two_nonce!(from, cli, trusted_args); + let top: TrustedOperation = + TrustedCall::balance_unshield(from.public().into(), to, self.amount, shard) + .sign(&KeyPair::Sr25519(Box::new(from)), nonce, &mrenclave, &shard) + .into_trusted_operation(trusted_args.direct); + Ok(perform_trusted_operation::<()>(cli, trusted_args, &top).map(|_| CliResultOk::None)?) + } +} diff --git a/bitacross-worker/cli/src/trusted_base_cli/mod.rs b/bitacross-worker/cli/src/trusted_base_cli/mod.rs new file mode 100644 index 0000000000..e964e1f3df --- /dev/null +++ b/bitacross-worker/cli/src/trusted_base_cli/mod.rs @@ -0,0 +1,111 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + trusted_base_cli::commands::{ + balance::BalanceCommand, get_shard::GetShardCommand, get_shard_vault::GetShardVaultCommand, + nonce::NonceCommand, set_balance::SetBalanceCommand, transfer::TransferCommand, + unshield_funds::UnshieldFundsCommand, + }, + trusted_cli::TrustedCli, + trusted_command_utils::get_keystore_path, + Cli, CliResult, CliResultOk, ED25519_KEY_TYPE, SR25519_KEY_TYPE, +}; +use log::*; +use sp_core::crypto::Ss58Codec; +use sp_keystore::Keystore; +use substrate_client_keystore::LocalKeystore; + +mod commands; + +#[derive(Subcommand)] +pub enum TrustedBaseCommand { + /// generates a new incognito account for the given shard + NewAccount, + + /// lists all incognito accounts in a given shard + ListAccounts, + + /// send funds from one incognito account to another + Transfer(TransferCommand), + + /// ROOT call to set some account balance to an arbitrary number + SetBalance(SetBalanceCommand), + + /// query balance for incognito account in keystore + Balance(BalanceCommand), + + /// Transfer funds from an incognito account to an parentchain account + UnshieldFunds(UnshieldFundsCommand), + + /// gets the nonce of a given account, taking the pending trusted calls + /// in top pool in consideration + Nonce(NonceCommand), + + /// get shard for this worker + GetShard(GetShardCommand), + + /// get shard vault for shielding (if defined for this worker) + GetShardVault(GetShardVaultCommand), +} + +impl TrustedBaseCommand { + pub fn run(&self, cli: &Cli, trusted_cli: &TrustedCli) -> CliResult { + match self { + TrustedBaseCommand::NewAccount => new_account(trusted_cli, cli), + TrustedBaseCommand::ListAccounts => list_accounts(trusted_cli, cli), + TrustedBaseCommand::Transfer(cmd) => cmd.run(cli, trusted_cli), + TrustedBaseCommand::SetBalance(cmd) => cmd.run(cli, trusted_cli), + TrustedBaseCommand::Balance(cmd) => cmd.run(cli, trusted_cli), + TrustedBaseCommand::UnshieldFunds(cmd) => cmd.run(cli, trusted_cli), + TrustedBaseCommand::Nonce(cmd) => cmd.run(cli, trusted_cli), + TrustedBaseCommand::GetShard(cmd) => cmd.run(cli, trusted_cli), + TrustedBaseCommand::GetShardVault(cmd) => cmd.run(cli, trusted_cli), + } + } +} + +fn new_account(trusted_args: &TrustedCli, cli: &Cli) -> CliResult { + let store = LocalKeystore::open(get_keystore_path(trusted_args, cli), None).unwrap(); + let key = LocalKeystore::sr25519_generate_new(&store, SR25519_KEY_TYPE, None).unwrap(); + drop(store); + info!("new account {}", key.to_ss58check()); + let key_str = key.to_ss58check(); + println!("{}", key_str); + + Ok(CliResultOk::PubKeysBase58 { pubkeys_sr25519: Some(vec![key_str]), pubkeys_ed25519: None }) +} + +fn list_accounts(trusted_args: &TrustedCli, cli: &Cli) -> CliResult { + let store = LocalKeystore::open(get_keystore_path(trusted_args, cli), None).unwrap(); + info!("sr25519 keys:"); + for pubkey in store.sr25519_public_keys(SR25519_KEY_TYPE).into_iter() { + println!("{}", pubkey.to_ss58check()); + } + info!("ed25519 keys:"); + let pubkeys: Vec = store + .ed25519_public_keys(ED25519_KEY_TYPE) + .into_iter() + .map(|pubkey| pubkey.to_ss58check()) + .collect(); + for pubkey in &pubkeys { + println!("{}", pubkey); + } + drop(store); + + Ok(CliResultOk::PubKeysBase58 { pubkeys_sr25519: None, pubkeys_ed25519: Some(pubkeys) }) +} diff --git a/bitacross-worker/cli/src/trusted_cli.rs b/bitacross-worker/cli/src/trusted_cli.rs new file mode 100644 index 0000000000..5c1f5d6553 --- /dev/null +++ b/bitacross-worker/cli/src/trusted_cli.rs @@ -0,0 +1,68 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{benchmark::BenchmarkCommand, Cli, CliResult}; + +#[cfg(feature = "evm")] +use crate::evm::EvmCommand; +use crate::trusted_base_cli::TrustedBaseCommand; + +#[derive(Args)] +pub struct TrustedCli { + /// targeted worker MRENCLAVE + #[clap(short, long)] + pub(crate) mrenclave: Option, + + /// shard identifier + #[clap(short, long)] + pub(crate) shard: Option, + + /// signer for publicly observable extrinsic + #[clap(short='a', long, default_value_t = String::from("//Alice"))] + pub(crate) xt_signer: String, + + /// insert if direct invocation call is desired + #[clap(short, long)] + pub(crate) direct: bool, + + #[clap(subcommand)] + pub(crate) command: TrustedCommand, +} + +#[derive(Subcommand)] +pub enum TrustedCommand { + #[clap(flatten)] + BaseTrusted(TrustedBaseCommand), + + #[cfg(feature = "evm")] + #[clap(flatten)] + EvmCommands(EvmCommand), + + /// Run Benchmark + Benchmark(BenchmarkCommand), +} + +impl TrustedCli { + pub(crate) fn run(&self, cli: &Cli) -> CliResult { + match &self.command { + TrustedCommand::BaseTrusted(cmd) => cmd.run(cli, self), + TrustedCommand::Benchmark(cmd) => cmd.run(cli, self), + #[cfg(feature = "evm")] + TrustedCommand::EvmCommands(cmd) => cmd.run(cli, self), + } + } +} diff --git a/bitacross-worker/cli/src/trusted_command_utils.rs b/bitacross-worker/cli/src/trusted_command_utils.rs new file mode 100644 index 0000000000..57704b982c --- /dev/null +++ b/bitacross-worker/cli/src/trusted_command_utils.rs @@ -0,0 +1,164 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + command_utils::{get_worker_api_direct, mrenclave_from_base58}, + trusted_cli::TrustedCli, + trusted_operation::{perform_trusted_operation, read_shard}, + Cli, +}; +use base58::{FromBase58, ToBase58}; +use codec::{Decode, Encode}; +use ita_stf::{Getter, TrustedCallSigned, TrustedGetter}; +use itc_rpc_client::direct_client::DirectApi; +use itp_rpc::{Id, RpcRequest, RpcResponse, RpcReturnValue}; +use itp_stf_primitives::types::{AccountId, KeyPair, ShardIdentifier, TrustedOperation}; +use itp_types::DirectRequestStatus; +use itp_utils::{FromHexPrefixed, ToHexPrefixed}; +use litentry_primitives::ParentchainBalance as Balance; +use log::*; +use sp_application_crypto::sr25519; +use sp_core::{crypto::Ss58Codec, sr25519 as sr25519_core, Pair}; +use sp_runtime::traits::IdentifyAccount; +use std::{boxed::Box, path::PathBuf}; +use substrate_client_keystore::LocalKeystore; + +#[macro_export] +macro_rules! get_layer_two_nonce { + ($signer_pair:ident, $cli: ident, $trusted_args:ident ) => {{ + use ita_stf::{Getter, PublicGetter, TrustedCallSigned}; + use itp_stf_primitives::types::TrustedOperation; + use litentry_primitives::Identity; + use $crate::{ + trusted_command_utils::get_pending_trusted_calls_for, + trusted_operation::perform_trusted_operation, + }; + let top = TrustedOperation::::get(Getter::public( + PublicGetter::nonce(Identity::Substrate($signer_pair.public().into())), + )); + // final nonce = current system nonce + pending tx count, panic early + let nonce = perform_trusted_operation::($cli, $trusted_args, &top) + .ok() + .unwrap_or_default(); + log::debug!("got system nonce: {:?}", nonce); + let pending_tx_count = + get_pending_trusted_calls_for($cli, $trusted_args, &$signer_pair.public().into()).len(); + let pending_tx_count = Index::try_from(pending_tx_count).unwrap(); + nonce + pending_tx_count + }}; +} + +const TRUSTED_KEYSTORE_PATH: &str = "my_trusted_keystore"; + +pub(crate) fn get_balance(cli: &Cli, trusted_args: &TrustedCli, arg_who: &str) -> Option { + debug!("arg_who = {:?}", arg_who); + let who = get_pair_from_str(trusted_args, arg_who, cli); + let top = TrustedOperation::::get(Getter::trusted( + TrustedGetter::free_balance(who.public().into()).sign(&KeyPair::Sr25519(Box::new(who))), + )); + perform_trusted_operation::(cli, trusted_args, &top).ok() +} + +pub(crate) fn get_keystore_path(trusted_args: &TrustedCli, cli: &Cli) -> PathBuf { + let (_mrenclave, shard) = get_identifiers(trusted_args, cli); + PathBuf::from(&format!("{}/{}", TRUSTED_KEYSTORE_PATH, shard.encode().to_base58())) +} + +pub(crate) fn get_identifiers(trusted_args: &TrustedCli, cli: &Cli) -> ([u8; 32], ShardIdentifier) { + let mrenclave = if let Some(mrenclave) = &trusted_args.mrenclave { + mrenclave_from_base58(mrenclave) + } else { + let direct_api = get_worker_api_direct(cli); + direct_api + .get_state_mrenclave() + .expect("Unable to retrieve MRENCLAVE from endpoint") + }; + let shard = match &trusted_args.shard { + Some(val) => + ShardIdentifier::from_slice(&val.from_base58().expect("shard has to be base58 encoded")), + None => ShardIdentifier::from_slice(&mrenclave), + }; + (mrenclave, shard) +} + +// TODO this function is redundant with client::main +pub(crate) fn get_accountid_from_str(account: &str) -> AccountId { + match &account[..2] { + "//" => sr25519::Pair::from_string(account, None) + .unwrap() + .public() + .into_account() + .into(), + _ => sr25519::Public::from_ss58check(account).unwrap().into_account().into(), + } +} + +// TODO this function is ALMOST redundant with client::main +// get a pair either form keyring (well known keys) or from the store +pub(crate) fn get_pair_from_str( + trusted_args: &TrustedCli, + account: &str, + cli: &Cli, +) -> sr25519_core::Pair { + info!("getting pair for {}", account); + match &account[..2] { + "//" => sr25519_core::Pair::from_string(account, None).unwrap(), + _ => { + info!("fetching from keystore at {}", &TRUSTED_KEYSTORE_PATH); + // open store without password protection + let store = LocalKeystore::open(get_keystore_path(trusted_args, cli), None) + .expect("store should exist"); + info!("store opened"); + let public_key = &sr25519::AppPublic::from_ss58check(account).unwrap(); + info!("public_key: {:?}", &public_key); + let _pair = store.key_pair::(public_key).unwrap().unwrap(); + info!("key pair fetched"); + drop(store); + _pair.into() + }, + } +} + +// helper method to get the pending trusted calls for a given account via direct RPC +pub(crate) fn get_pending_trusted_calls_for( + cli: &Cli, + trusted_args: &TrustedCli, + who: &AccountId, +) -> Vec> { + let shard = read_shard(trusted_args, cli).unwrap(); + let direct_api = get_worker_api_direct(cli); + let rpc_method = "author_pendingTrustedCallsFor".to_owned(); + let jsonrpc_call: String = RpcRequest::compose_jsonrpc_call( + Id::Text("1".to_string()), + rpc_method, + vec![shard.encode().to_base58(), who.to_hex()], + ) + .unwrap(); + + let rpc_response_str = direct_api.get(&jsonrpc_call).unwrap(); + let rpc_response: RpcResponse = serde_json::from_str(&rpc_response_str).unwrap(); + let rpc_return_value = RpcReturnValue::from_hex(&rpc_response.result).unwrap(); + + if rpc_return_value.status == DirectRequestStatus::Error { + println!("[Error] {}", String::decode(&mut rpc_return_value.value.as_slice()).unwrap()); + direct_api.close().unwrap(); + return vec![] + } + + direct_api.close().unwrap(); + Decode::decode(&mut rpc_return_value.value.as_slice()).unwrap_or_default() +} diff --git a/bitacross-worker/cli/src/trusted_operation.rs b/bitacross-worker/cli/src/trusted_operation.rs new file mode 100644 index 0000000000..bfbb18c221 --- /dev/null +++ b/bitacross-worker/cli/src/trusted_operation.rs @@ -0,0 +1,420 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + command_utils::{get_chain_api, get_pair_from_str, get_shielding_key, get_worker_api_direct}, + error::Error, + trusted_cli::TrustedCli, + Cli, +}; +use base58::{FromBase58, ToBase58}; +use codec::{Decode, Encode, Input}; +use ita_stf::{Getter, StfError, TrustedCallSigned}; +use itc_rpc_client::direct_client::{DirectApi, DirectClient}; +use itp_node_api::api_client::{ParentchainApi, TEEREX}; +use itp_rpc::{Id, RpcRequest, RpcResponse, RpcReturnValue}; +use itp_sgx_crypto::ShieldingCryptoEncrypt; +use itp_stf_primitives::types::{ShardIdentifier, TrustedOperation}; +use itp_types::{BlockNumber, DirectRequestStatus, RsaRequest, TrustedOperationStatus}; +use itp_utils::{FromHexPrefixed, ToHexPrefixed}; +use litentry_primitives::ParentchainHash as Hash; +use log::*; +use my_node_runtime::RuntimeEvent; +use pallet_teerex::Event as TeerexEvent; +use sp_core::H256; +use std::{ + fmt::Debug, + result::Result as StdResult, + sync::mpsc::{channel, Receiver}, + time::Instant, +}; +use substrate_api_client::{ + ac_compose_macros::compose_extrinsic, GetChainInfo, SubmitAndWatch, SubscribeEvents, XtStatus, +}; +use thiserror::Error; + +#[derive(Debug, Error)] +pub(crate) enum TrustedOperationError { + #[error("extrinsic L1 error: {msg:?}")] + Extrinsic { msg: String }, + #[error("default error: {msg:?}")] + Default { msg: String }, +} + +pub(crate) type TrustedOpResult = StdResult; + +pub(crate) fn perform_trusted_operation( + cli: &Cli, + trusted_args: &TrustedCli, + top: &TrustedOperation, +) -> TrustedOpResult { + match top { + TrustedOperation::indirect_call(_) => send_indirect_request::(cli, trusted_args, top), + TrustedOperation::direct_call(_) => send_direct_request::(cli, trusted_args, top), + TrustedOperation::get(getter) => + execute_getter_from_cli_args::(cli, trusted_args, getter), + } +} + +fn execute_getter_from_cli_args( + cli: &Cli, + trusted_args: &TrustedCli, + getter: &Getter, +) -> TrustedOpResult { + let shard = read_shard(trusted_args, cli).unwrap(); + let direct_api = get_worker_api_direct(cli); + get_state(&direct_api, shard, getter) +} + +pub(crate) fn get_state( + direct_api: &DirectClient, + shard: ShardIdentifier, + getter: &Getter, +) -> TrustedOpResult { + // Compose jsonrpc call. + let data = RsaRequest::new(shard, getter.encode()); + let rpc_method = "state_executeGetter".to_owned(); + let jsonrpc_call: String = RpcRequest::compose_jsonrpc_call( + Id::Text("1".to_string()), + rpc_method, + vec![data.to_hex()], + ) + .unwrap(); + + let rpc_response_str = direct_api.get(&jsonrpc_call).unwrap(); + + // Decode RPC response. + let rpc_response: RpcResponse = serde_json::from_str(&rpc_response_str) + .map_err(|err| TrustedOperationError::Default { msg: err.to_string() })?; + let rpc_return_value = RpcReturnValue::from_hex(&rpc_response.result) + // Replace with `inspect_err` once it's stable. + .map_err(|err| { + error!("Failed to decode RpcReturnValue: {:?}", err); + TrustedOperationError::Default { msg: "RpcReturnValue::from_hex".to_string() } + })?; + + if rpc_return_value.status == DirectRequestStatus::Error { + println!("[Error] {}", String::decode(&mut rpc_return_value.value.as_slice()).unwrap()); + return Err(TrustedOperationError::Default { + msg: "[Error] DirectRequestStatus::Error".to_string(), + }) + } + + let maybe_state: Option> = Option::decode(&mut rpc_return_value.value.as_slice()) + // Replace with `inspect_err` once it's stable. + .map_err(|err| { + error!("Failed to decode return value: {:?}", err); + TrustedOperationError::Default { msg: "Option::decode".to_string() } + })?; + + match maybe_state { + Some(state) => { + let decoded = decode_response_value(&mut state.as_slice())?; + Ok(decoded) + }, + None => Err(TrustedOperationError::Default { msg: "Value not present".to_string() }), + } +} + +fn send_indirect_request( + cli: &Cli, + trusted_args: &TrustedCli, + trusted_operation: &TrustedOperation, +) -> TrustedOpResult { + let mut chain_api = get_chain_api(cli); + let encryption_key = get_shielding_key(cli).unwrap(); + let call_encrypted = encryption_key.encrypt(&trusted_operation.encode()).unwrap(); + + let shard = read_shard(trusted_args, cli).unwrap(); + debug!( + "invoke indirect send_request: trusted operation: {:?}, shard: {}", + trusted_operation, + shard.encode().to_base58() + ); + let arg_signer = &trusted_args.xt_signer; + let signer = get_pair_from_str(arg_signer); + chain_api.set_signer(signer.into()); + + let request = RsaRequest::new(shard, call_encrypted); + let xt = compose_extrinsic!(&chain_api, TEEREX, "call_worker", request); + + let block_hash = match chain_api.submit_and_watch_extrinsic_until(xt, XtStatus::InBlock) { + Ok(xt_report) => { + println!( + "[+] invoke TrustedOperation extrinsic success. extrinsic hash: {:?} / status: {:?} / block hash: {:?}", + xt_report.extrinsic_hash, xt_report.status, xt_report.block_hash.unwrap() + ); + xt_report.block_hash.unwrap() + }, + Err(e) => { + error!("invoke TrustedOperation extrinsic failed {:?}", e); + return Err(TrustedOperationError::Extrinsic { msg: format!("{:?}", e) }) + }, + }; + + info!( + "Trusted call extrinsic sent and successfully included in parentchain block with hash {:?}.", + block_hash + ); + info!("Waiting for execution confirmation from enclave..."); + let mut subscription = chain_api.subscribe_events().unwrap(); + loop { + let event_result = subscription.next_events::(); + if let Some(Ok(event_records)) = event_result { + for event_record in event_records { + if let RuntimeEvent::Teerex(TeerexEvent::ProcessedParentchainBlock( + _signer, + confirmed_block_hash, + trusted_calls_merkle_root, + confirmed_block_number, + )) = event_record.event + { + info!("Confirmation of ProcessedParentchainBlock received"); + debug!("shard: {:?}", shard); + debug!("confirmed parentchain block Hash: {:?}", block_hash); + debug!("trusted calls merkle root: {:?}", trusted_calls_merkle_root); + debug!("Confirmed stf block Hash: {:?}", confirmed_block_hash); + if let Err(e) = check_if_received_event_exceeds_expected( + &chain_api, + block_hash, + confirmed_block_hash, + confirmed_block_number, + ) { + error!("ProcessedParentchainBlock event: {:?}", e); + return Err(TrustedOperationError::Default { + msg: format!("ProcessedParentchainBlock event: {:?}", e), + }) + }; + + if confirmed_block_hash == block_hash { + let value = decode_response_value(&mut block_hash.encode().as_slice())?; + return Ok(value) + } + } + } + } else { + warn!("Error in event subscription: {:?}", event_result) + } + } +} + +fn check_if_received_event_exceeds_expected( + chain_api: &ParentchainApi, + block_hash: Hash, + confirmed_block_hash: Hash, + confirmed_block_number: BlockNumber, +) -> Result<(), Error> { + let block_number = chain_api.get_header(Some(block_hash))?.ok_or(Error::MissingBlock)?.number; + + info!("Expected block Number: {:?}", block_number); + info!("Confirmed block Number: {:?}", confirmed_block_number); + // The returned block number belongs to a subsequent event. We missed our event and can break the loop. + if confirmed_block_number > block_number { + return Err(Error::ConfirmedBlockNumberTooHigh(confirmed_block_number, block_number)) + } + // The block number is correct, but the block hash does not fit. + if block_number == confirmed_block_number && block_hash != confirmed_block_hash { + return Err(Error::ConfirmedBlockHashDoesNotMatchExpected(confirmed_block_hash, block_hash)) + } + Ok(()) +} + +pub fn read_shard(trusted_args: &TrustedCli, cli: &Cli) -> Result { + match &trusted_args.shard { + Some(s) => match s.from_base58() { + Ok(s) => ShardIdentifier::decode(&mut &s[..]), + _ => panic!("shard argument must be base58 encoded"), + }, + None => match trusted_args.mrenclave.clone() { + Some(mrenclave) => + if let Ok(s) = mrenclave.from_base58() { + ShardIdentifier::decode(&mut &s[..]) + } else { + panic!("Mrenclave argument must be base58 encoded") + }, + None => { + // Fetch mrenclave from worker + let direct_api = get_worker_api_direct(cli); + if let Ok(s) = direct_api.get_state_mrenclave() { + ShardIdentifier::decode(&mut &s[..]) + } else { + panic!("Unable to fetch MRENCLAVE from worker endpoint"); + } + }, + }, + } +} + +/// sends a rpc watch request to the worker api server +fn send_direct_request( + cli: &Cli, + trusted_args: &TrustedCli, + top: &TrustedOperation, +) -> TrustedOpResult { + let encryption_key = get_shielding_key(cli).unwrap(); + let shard = read_shard(trusted_args, cli).unwrap(); + let jsonrpc_call: String = get_json_request(shard, top, encryption_key); + + debug!("get direct api"); + let direct_api = get_worker_api_direct(cli); + + debug!("setup sender and receiver"); + let (sender, receiver) = channel(); + direct_api.watch(jsonrpc_call, sender); + + debug!("waiting for rpc response"); + loop { + match receiver.recv() { + Ok(response) => { + debug!("received response"); + let response: RpcResponse = serde_json::from_str(&response).unwrap(); + if let Ok(return_value) = RpcReturnValue::from_hex(&response.result) { + match return_value.status { + DirectRequestStatus::Error => { + debug!("request status is error"); + if let Ok(value) = String::decode(&mut return_value.value.as_slice()) { + println!("[Error] {}", value); + } + direct_api.close().unwrap(); + return Err(TrustedOperationError::Default { + msg: "[Error] DirectRequestStatus::Error".to_string(), + }) + }, + DirectRequestStatus::TrustedOperationStatus(status, top_hash) => { + debug!("request status is: {:?}, top_hash: {:?}", status, top_hash); + + if matches!(status, TrustedOperationStatus::Invalid) { + let error = StfError::decode(&mut return_value.value.as_slice()) + .map_err(|e| TrustedOperationError::Default { + msg: format!("Could not decode error value: {:?}", e), + })?; + return Err(TrustedOperationError::Default { + msg: format!( + "[Error] Error occurred while executing trusted call: {:?}", + error + ), + }) + } + if let Ok(value) = Hash::decode(&mut return_value.value.as_slice()) { + println!("Trusted call {:?} is {:?}", value, status); + } + if !return_value.do_watch { + direct_api.close().unwrap(); + let value = + decode_response_value(&mut return_value.value.as_slice())?; + return Ok(value) + } + }, + DirectRequestStatus::Ok => { + debug!("request status is ignored"); + direct_api.close().unwrap(); + return Err(TrustedOperationError::Default { + msg: "Unexpected status: DirectRequestStatus::Ok".to_string(), + }) + }, + } + }; + }, + Err(e) => { + error!("failed to receive rpc response: {:?}", e); + direct_api.close().unwrap(); + return Err(TrustedOperationError::Default { + msg: "failed to receive rpc response".to_string(), + }) + }, + }; + } +} + +fn decode_response_value( + value: &mut I, +) -> StdResult { + T::decode(value).map_err(|e| TrustedOperationError::Default { + msg: format!("Could not decode result value: {:?}", e), + }) +} + +pub(crate) fn get_json_request( + shard: ShardIdentifier, + top: &TrustedOperation, + shielding_pubkey: sgx_crypto_helper::rsa3072::Rsa3072PubKey, +) -> String { + let encrypted_top = shielding_pubkey.encrypt(&top.encode()).unwrap(); + + // compose jsonrpc call + let request = RsaRequest::new(shard, encrypted_top); + RpcRequest::compose_jsonrpc_call( + Id::Text("1".to_string()), + "author_submitAndWatchRsaRequest".to_string(), + vec![request.to_hex()], + ) + .unwrap() +} + +pub(crate) fn wait_until( + receiver: &Receiver, + until: impl Fn(TrustedOperationStatus) -> bool, +) -> Option<(H256, Instant)> { + debug!("waiting for rpc response"); + loop { + match receiver.recv() { + Ok(response) => { + debug!("received response: {}", response); + let parse_result: Result = serde_json::from_str(&response); + if let Ok(response) = parse_result { + if let Ok(return_value) = RpcReturnValue::from_hex(&response.result) { + debug!("successfully decoded rpc response: {:?}", return_value); + match return_value.status { + DirectRequestStatus::Error => { + debug!("request status is error"); + if let Ok(value) = + String::decode(&mut return_value.value.as_slice()) + { + println!("[Error] {}", value); + } + return None + }, + DirectRequestStatus::TrustedOperationStatus(status, top_hash) => { + debug!("request status is: {:?}, top_hash: {:?}", status, top_hash); + if let Ok(value) = Hash::decode(&mut return_value.value.as_slice()) + { + println!("Trusted call {:?} is {:?}", value, status); + if until(status.clone()) { + return Some((top_hash, Instant::now())) + } else if status == TrustedOperationStatus::Invalid { + error!("Invalid request"); + return None + } + } + }, + DirectRequestStatus::Ok => { + debug!("request status is ignored"); + return None + }, + } + }; + } else { + error!("Could not parse response"); + }; + }, + Err(e) => { + error!("failed to receive rpc response: {:?}", e); + return None + }, + }; + } +} diff --git a/bitacross-worker/cli/test_auto_shielding_with_transfer_bob.sh b/bitacross-worker/cli/test_auto_shielding_with_transfer_bob.sh new file mode 100644 index 0000000000..255d3f5bbc --- /dev/null +++ b/bitacross-worker/cli/test_auto_shielding_with_transfer_bob.sh @@ -0,0 +1,141 @@ +#!/bin/bash +set -euo pipefail + +# Verifies that auto shielding transfers sent to vault account: //Alice are verified from sender //Bob +# + +while getopts ":m:p:A:u:V:w:x:y:z:C:" opt; do + case $opt in + p) + INTEGRITEE_RPC_PORT=$OPTARG + ;; + A) + WORKER_1_PORT=$OPTARG + ;; + u) + INTEGRITEE_RPC_URL=$OPTARG + ;; + V) + WORKER_1_URL=$OPTARG + ;; + w) + TARGET_A_PARENTCHAIN_RPC_URL=$OPTARG + ;; + x) + TARGET_A_PARENTCHAIN_RPC_PORT=$OPTARG + ;; + C) + CLIENT_BIN=$OPTARG + ;; + *) + echo "invalid arg ${OPTARG}" + exit 1 + esac +done + +# Using default port if none given as arguments. +INTEGRITEE_RPC_PORT=${INTEGRITEE_RPC_PORT:-9944} +INTEGRITEE_RPC_URL=${INTEGRITEE_RPC_URL:-"ws://127.0.0.1"} +TARGET_A_PARENTCHAIN_RPC_PORT=${TARGET_A_PARENTCHAIN_RPC_PORT:-9966} +TARGET_A_PARENTCHAIN_RPC_URL=${TARGET_A_PARENTCHAIN_RPC_URL:-"ws://127.0.0.1"} + +WORKER_1_PORT=${WORKER_1_PORT:-2000} +WORKER_1_URL=${WORKER_1_URL:-"wss://127.0.0.1"} + +CLIENT_BIN=${CLIENT_BIN:-"./../bin/integritee-cli"} + +echo "Using client binary ${CLIENT_BIN}" +${CLIENT_BIN} --version +echo "Using Integritee RPC uri ${INTEGRITEE_RPC_URL}:${INTEGRITEE_RPC_PORT}" +echo "Using Target A RPC uri ${TARGET_A_PARENTCHAIN_RPC_URL}:${TARGET_A_PARENTCHAIN_RPC_PORT}" +echo "Using trusted-worker 1 uri ${WORKER_1_URL}:${WORKER_1_PORT}" +echo "" + +# the parentchain token is 12 decimal +UNIT=$(( 10 ** 12 )) +FEE_TOLERANCE=$((10 ** 11)) + +# make these amounts greater than ED +AMOUNT_SHIELD=$(( 6 * UNIT )) + +CLIENT="${CLIENT_BIN} -p ${INTEGRITEE_RPC_PORT} -P ${WORKER_1_PORT} -u ${INTEGRITEE_RPC_URL} -U ${WORKER_1_URL}" +CLIENT2="${CLIENT_BIN} -p ${TARGET_A_PARENTCHAIN_RPC_PORT} -P ${WORKER_1_PORT} -u ${TARGET_A_PARENTCHAIN_RPC_URL} -U ${WORKER_1_URL}" + +# interval and max rounds to wait to check the given account balance in sidechain +WAIT_INTERVAL_SECONDS=10 +WAIT_ROUNDS=20 + +# Poll and assert the given account's state is equal to expected, +# with timeout WAIT_INTERVAL_SECONDS * WAIT_ROUNDS +# usage: +# wait_assert_state +# the `state-name` has to be the supported subcommand, e.g. `balance`, `nonce` +function wait_assert_state() +{ + for i in $(seq 1 $WAIT_ROUNDS); do + sleep $WAIT_INTERVAL_SECONDS + state=$(${CLIENT} trusted --mrenclave "$1" "$3" "$2") + if (( $4 >= state ? $4 - state < FEE_TOLERANCE : state - $4 < FEE_TOLERANCE)); then + return + else + : + fi + done + echo + echo "Assert $2 $3 failed, expected = $4, actual = $state, tolerance = $FEE_TOLERANCE" + exit 1 +} + +# Do a live query and assert the given account's state is equal to expected +# usage: +# assert_state +function assert_state() +{ + state=$(${CLIENT} trusted --mrenclave "$1" "$3" "$2") + if [ -z "$state" ]; then + echo "Query $2 $3 failed" + exit 1 + fi + + if [ $state -eq "$4" ]; then + return + fi + echo + echo "Assert $2 $3 failed, expected = $4, actual = $state" + exit 1 +} + +echo "* Query on-chain enclave registry:" +${CLIENT} list-workers +echo "" + +# this will always take the first MRENCLAVE found in the registry !! +read MRENCLAVE <<< $($CLIENT list-workers | awk '/ MRENCLAVE: / { print $2; exit }') +echo "Reading MRENCLAVE from worker list: ${MRENCLAVE}" + +[[ -z $MRENCLAVE ]] && { echo "MRENCLAVE is empty. cannot continue" ; exit 1; } + +VAULTACCOUNT=//Alice +## Sender account to shield for +BOBTRUSTEDACCOUNT=//Bob +echo " Bob's trusted account (same as public account) = ${BOBTRUSTEDACCOUNT}" +echo "" + +# Assert the initial trusted balance of Alice incognito +TRUSTED_BALANCE_BOB=1000000000000000 +wait_assert_state ${MRENCLAVE} ${BOBTRUSTEDACCOUNT} balance ${TRUSTED_BALANCE_BOB} + + +echo "* Send ${AMOUNT_SHIELD} from //Bob to //Alice on the Target A parentchain, which should trigger the shield process" +${CLIENT2} transfer //Bob ${VAULTACCOUNT} ${AMOUNT_SHIELD} +echo "" + +echo "* Wait and assert Bob's incognito account balance, should be $(( TRUSTED_BALANCE_BOB + AMOUNT_SHIELD ))" +wait_assert_state ${MRENCLAVE} ${BOBTRUSTEDACCOUNT} balance $(( TRUSTED_BALANCE_BOB + AMOUNT_SHIELD )) +echo "✔ ok" + +echo "" +echo "-----------------------" +echo "✔ The test passed!" +echo "-----------------------" +echo "" diff --git a/bitacross-worker/cli/test_shield_on_target_nodes_with_transfer_to_alice.sh b/bitacross-worker/cli/test_shield_on_target_nodes_with_transfer_to_alice.sh new file mode 100755 index 0000000000..b1670e5bb8 --- /dev/null +++ b/bitacross-worker/cli/test_shield_on_target_nodes_with_transfer_to_alice.sh @@ -0,0 +1,159 @@ +#!/bin/bash +set -euo pipefail + +# Verifies that shielding from the Target A and B parentchains works by sending a transfer to //Alice. +# +# Note: This test does not do anything meaningful. It only verifies the basic functionality of the Target parentchain +# connections. + +while getopts ":m:p:A:u:V:w:x:y:z:C:" opt; do + case $opt in + p) + LITENTRY_RPC_PORT=$OPTARG + ;; + A) + WORKER_1_PORT=$OPTARG + ;; + u) + LITENTRY_RPC_URL=$OPTARG + ;; + V) + WORKER_1_URL=$OPTARG + ;; + w) + TARGET_A_PARENTCHAIN_RPC_URL=$OPTARG + ;; + x) + TARGET_A_PARENTCHAIN_RPC_PORT=$OPTARG + ;; + y) + TARGET_B_PARENTCHAIN_RPC_URL=$OPTARG + ;; + z) + TARGET_B_PARENTCHAIN_RPC_PORT=$OPTARG + ;; + C) + CLIENT_BIN=$OPTARG + ;; + *) + echo "invalid arg ${OPTARG}" + exit 1 + esac +done + +# Using default port if none given as arguments. +LITENTRY_RPC_PORT=${LITENTRY_RPC_PORT:-9944} +LITENTRY_RPC_URL=${LITENTRY_RPC_URL:-"ws://127.0.0.1"} +TARGET_A_PARENTCHAIN_RPC_PORT=${TARGET_A_PARENTCHAIN_RPC_PORT:-9966} +TARGET_A_PARENTCHAIN_RPC_URL=${TARGET_A_PARENTCHAIN_RPC_URL:-"ws://127.0.0.1"} +TARGET_B_PARENTCHAIN_RPC_PORT=${TARGET_B_PARENTCHAIN_RPC_PORT:-9988} +TARGET_B_PARENTCHAIN_RPC_URL=${TARGET_B_PARENTCHAIN_RPC_URL:-"ws://127.0.0.1"} + +WORKER_1_PORT=${WORKER_1_PORT:-2000} +WORKER_1_URL=${WORKER_1_URL:-"wss://127.0.0.1"} + +CLIENT_BIN=${CLIENT_BIN:-"./../bin/bitacross-cli"} + +echo "Using client binary ${CLIENT_BIN}" +${CLIENT_BIN} --version +echo "Using Integritee RPC uri ${LITENTRY_RPC_URL}:${LITENTRY_RPC_PORT}" +echo "Using Target A RPC uri ${TARGET_A_PARENTCHAIN_RPC_URL}:${TARGET_A_PARENTCHAIN_RPC_PORT}" +echo "Using Target B RPC uri ${TARGET_B_PARENTCHAIN_RPC_URL}:${TARGET_B_PARENTCHAIN_RPC_PORT}" +echo "Using trusted-worker 1 uri ${WORKER_1_URL}:${WORKER_1_PORT}" +echo "" + +# the parentchain token is 12 decimal +UNIT=$(( 10 ** 12 )) +FEE_TOLERANCE=$((10 ** 11)) + +# make these amounts greater than ED +AMOUNT_SHIELD=$(( 6 * UNIT )) + +CLIENT="${CLIENT_BIN} -p ${LITENTRY_RPC_PORT} -P ${WORKER_1_PORT} -u ${LITENTRY_RPC_URL} -U ${WORKER_1_URL}" +CLIENT2="${CLIENT_BIN} -p ${TARGET_A_PARENTCHAIN_RPC_PORT} -P ${WORKER_1_PORT} -u ${TARGET_A_PARENTCHAIN_RPC_URL} -U ${WORKER_1_URL}" +CLIENT3="${CLIENT_BIN} -p ${TARGET_B_PARENTCHAIN_RPC_PORT} -P ${WORKER_1_PORT} -u ${TARGET_B_PARENTCHAIN_RPC_URL} -U ${WORKER_1_URL}" + +# interval and max rounds to wait to check the given account balance in sidechain +WAIT_INTERVAL_SECONDS=10 +WAIT_ROUNDS=20 + +# Poll and assert the given account's state is equal to expected, +# with timeout WAIT_INTERVAL_SECONDS * WAIT_ROUNDS +# usage: +# wait_assert_state +# the `state-name` has to be the supported subcommand, e.g. `balance`, `nonce` +function wait_assert_state() +{ + for i in $(seq 1 $WAIT_ROUNDS); do + sleep $WAIT_INTERVAL_SECONDS + state=$(${CLIENT} trusted --mrenclave "$1" "$3" "$2") + if (( $4 >= state ? $4 - state < FEE_TOLERANCE : state - $4 < FEE_TOLERANCE)); then + return + else + : + fi + done + echo + echo "Assert $2 $3 failed, expected = $4, actual = $state, tolerance = $FEE_TOLERANCE" + exit 1 +} + +# Do a live query and assert the given account's state is equal to expected +# usage: +# assert_state +function assert_state() +{ + state=$(${CLIENT} trusted --mrenclave "$1" "$3" "$2") + if [ -z "$state" ]; then + echo "Query $2 $3 failed" + exit 1 + fi + + if [ $state -eq "$4" ]; then + return + fi + echo + echo "Assert $2 $3 failed, expected = $4, actual = $state" + exit 1 +} + +echo "* Query on-chain enclave registry:" +${CLIENT} list-workers +echo "" + +# this will always take the first MRENCLAVE found in the registry !! +read MRENCLAVE <<< $($CLIENT list-workers | awk '/ MRENCLAVE: / { print $2; exit }') +echo "Reading MRENCLAVE from worker list: ${MRENCLAVE}" + +[[ -z $MRENCLAVE ]] && { echo "MRENCLAVE is empty. cannot continue" ; exit 1; } + +ALICETRUSTEDACCOUNT=//Alice +echo " Alice's trusted account (same as public account) = ${ALICETRUSTEDACCOUNT}" +echo "" + +# Assert the initial trusted balance of Alice incognito +TRUSTED_BALANCE_ALICE=1000000000000000 +wait_assert_state ${MRENCLAVE} ${ALICETRUSTEDACCOUNT} balance ${TRUSTED_BALANCE_ALICE} + + +echo "* Send ${AMOUNT_SHIELD} from //Alice to //Alice on the Target A parentchain, which should trigger the shield process" +${CLIENT2} transfer //Alice ${ALICETRUSTEDACCOUNT} ${AMOUNT_SHIELD} +echo "" + +echo "* Wait and assert Alice's incognito account balance, should be $(( TRUSTED_BALANCE_ALICE + AMOUNT_SHIELD ))" +wait_assert_state ${MRENCLAVE} ${ALICETRUSTEDACCOUNT} balance $(( TRUSTED_BALANCE_ALICE + AMOUNT_SHIELD )) +echo "✔ ok" + +echo "* Send ${AMOUNT_SHIELD} from //Alice to //Alice on the Target B Parentchain, which should trigger the shield process again" +${CLIENT3} transfer //Alice ${ALICETRUSTEDACCOUNT} ${AMOUNT_SHIELD} +echo "" + +echo "* Wait and assert Alice's incognito account balance, should be $(( TRUSTED_BALANCE_ALICE + 2*AMOUNT_SHIELD ))" +wait_assert_state ${MRENCLAVE} ${ALICETRUSTEDACCOUNT} balance $(( TRUSTED_BALANCE_ALICE + 2*AMOUNT_SHIELD )) +echo "✔ ok" + +echo "" +echo "-----------------------" +echo "✔ The test passed!" +echo "-----------------------" +echo "" diff --git a/bitacross-worker/cli/tests/basic_tests.rs b/bitacross-worker/cli/tests/basic_tests.rs new file mode 100644 index 0000000000..d063b36072 --- /dev/null +++ b/bitacross-worker/cli/tests/basic_tests.rs @@ -0,0 +1,24 @@ +use bitacross_cli::Cli; +use clap::Parser; + +fn init() { + let _ = env_logger::try_init(); +} + +#[test] +fn test_version() { + init(); + + let res = Cli::try_parse_from(vec!["placeholder_cli_path", "--version"]); + let err = clap::Error::new(clap::error::ErrorKind::DisplayVersion); + assert!(matches!(res, Err(err))); +} + +#[test] +fn test_help() { + init(); + + let res = Cli::try_parse_from(vec!["placeholder_cli_path", "--help"]); + let err = clap::Error::new(clap::error::ErrorKind::DisplayHelp); + assert!(matches!(res, Err(err))); +} diff --git a/bitacross-worker/core-primitives/attestation-handler/AttestationReportSigningCACert.pem b/bitacross-worker/core-primitives/attestation-handler/AttestationReportSigningCACert.pem new file mode 100644 index 0000000000..948b4c0cdd --- /dev/null +++ b/bitacross-worker/core-primitives/attestation-handler/AttestationReportSigningCACert.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFSzCCA7OgAwIBAgIJANEHdl0yo7CUMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNV +BAYTAlVTMQswCQYDVQQIDAJDQTEUMBIGA1UEBwwLU2FudGEgQ2xhcmExGjAYBgNV +BAoMEUludGVsIENvcnBvcmF0aW9uMTAwLgYDVQQDDCdJbnRlbCBTR1ggQXR0ZXN0 +YXRpb24gUmVwb3J0IFNpZ25pbmcgQ0EwIBcNMTYxMTE0MTUzNzMxWhgPMjA0OTEy +MzEyMzU5NTlaMH4xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEUMBIGA1UEBwwL +U2FudGEgQ2xhcmExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0aW9uMTAwLgYDVQQD +DCdJbnRlbCBTR1ggQXR0ZXN0YXRpb24gUmVwb3J0IFNpZ25pbmcgQ0EwggGiMA0G +CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCfPGR+tXc8u1EtJzLA10Feu1Wg+p7e +LmSRmeaCHbkQ1TF3Nwl3RmpqXkeGzNLd69QUnWovYyVSndEMyYc3sHecGgfinEeh +rgBJSEdsSJ9FpaFdesjsxqzGRa20PYdnnfWcCTvFoulpbFR4VBuXnnVLVzkUvlXT +L/TAnd8nIZk0zZkFJ7P5LtePvykkar7LcSQO85wtcQe0R1Raf/sQ6wYKaKmFgCGe +NpEJUmg4ktal4qgIAxk+QHUxQE42sxViN5mqglB0QJdUot/o9a/V/mMeH8KvOAiQ +byinkNndn+Bgk5sSV5DFgF0DffVqmVMblt5p3jPtImzBIH0QQrXJq39AT8cRwP5H +afuVeLHcDsRp6hol4P+ZFIhu8mmbI1u0hH3W/0C2BuYXB5PC+5izFFh/nP0lc2Lf +6rELO9LZdnOhpL1ExFOq9H/B8tPQ84T3Sgb4nAifDabNt/zu6MmCGo5U8lwEFtGM +RoOaX4AS+909x00lYnmtwsDVWv9vBiJCXRsCAwEAAaOByTCBxjBgBgNVHR8EWTBX +MFWgU6BRhk9odHRwOi8vdHJ1c3RlZHNlcnZpY2VzLmludGVsLmNvbS9jb250ZW50 +L0NSTC9TR1gvQXR0ZXN0YXRpb25SZXBvcnRTaWduaW5nQ0EuY3JsMB0GA1UdDgQW +BBR4Q3t2pn680K9+QjfrNXw7hwFRPDAfBgNVHSMEGDAWgBR4Q3t2pn680K9+Qjfr +NXw7hwFRPDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADANBgkq +hkiG9w0BAQsFAAOCAYEAeF8tYMXICvQqeXYQITkV2oLJsp6J4JAqJabHWxYJHGir +IEqucRiJSSx+HjIJEUVaj8E0QjEud6Y5lNmXlcjqRXaCPOqK0eGRz6hi+ripMtPZ +sFNaBwLQVV905SDjAzDzNIDnrcnXyB4gcDFCvwDFKKgLRjOB/WAqgscDUoGq5ZVi +zLUzTqiQPmULAQaB9c6Oti6snEFJiCQ67JLyW/E83/frzCmO5Ru6WjU4tmsmy8Ra +Ud4APK0wZTGtfPXU7w+IBdG5Ez0kE1qzxGQaL4gINJ1zMyleDnbuS8UicjJijvqA +152Sq049ESDz+1rRGc2NVEqh1KaGXmtXvqxXcTB+Ljy5Bw2ke0v8iGngFBPqCTVB +3op5KBG3RjbF6RRSzwzuWfL7QErNC8WEy5yDVARzTA5+xmBc388v9Dm21HGfcC8O +DD+gT9sSpssq0ascmvH49MOgjt1yoysLtdCtJW/9FZpoOypaHx0R+mJTLwPXVMrv +DaVzWh5aiEx+idkSGMnX +-----END CERTIFICATE----- diff --git a/bitacross-worker/core-primitives/attestation-handler/Cargo.toml b/bitacross-worker/core-primitives/attestation-handler/Cargo.toml new file mode 100644 index 0000000000..a00ec0affb --- /dev/null +++ b/bitacross-worker/core-primitives/attestation-handler/Cargo.toml @@ -0,0 +1,102 @@ +[package] +name = "itp-attestation-handler" +version = "0.8.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +# crates-io no_std deps +arrayvec = { version = "0.7.1", default-features = false } +bit-vec = { version = "0.6", default-features = false } +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +hex = { version = "0.4.3", default-features = false, features = ["alloc"] } +itertools = { default-features = false, version = "0.10.1" } +log = { version = "0.4", default-features = false } + +# std only deps +base64 = { version = "0.13", features = ["alloc"], optional = true } +chrono = { version = "0.4.19", features = ["alloc"], optional = true } +rustls = { version = "0.19", optional = true } +serde_json = { version = "1.0", features = ["preserve_order"], optional = true } +thiserror = { version = "1.0", optional = true } +webpki = { version = "0.21", optional = true } + +# mesalock +base64_sgx = { package = "base64", rev = "sgx_1.1.3", git = "https://github.com/mesalock-linux/rust-base64-sgx", optional = true } +chrono_sgx = { package = "chrono", git = "https://github.com/mesalock-linux/chrono-sgx", optional = true } +num-bigint = { optional = true, git = "https://github.com/mesalock-linux/num-bigint-sgx" } +rustls_sgx = { package = "rustls", rev = "sgx_1.1.3", features = ["dangerous_configuration"], git = "https://github.com/mesalock-linux/rustls", optional = true } +serde_json_sgx = { package = "serde_json", tag = "sgx_1.1.3", features = ["preserve_order"], git = "https://github.com/mesalock-linux/serde-json-sgx", optional = true } +thiserror_sgx = { package = "thiserror", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3", optional = true } +webpki-roots = { git = "https://github.com/mesalock-linux/webpki-roots", branch = "mesalock_sgx" } +webpki_sgx = { package = "webpki", git = "https://github.com/mesalock-linux/webpki", branch = "mesalock_sgx", optional = true } +yasna_sgx = { package = "yasna", optional = true, default-features = false, features = ["bit-vec", "num-bigint", "chrono", "mesalock_sgx"], git = "https://github.com/mesalock-linux/yasna.rs-sgx", rev = "sgx_1.1.3" } + +# sgx +sgx_rand = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } +sgx_tcrypto = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } +sgx_tse = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", features = ["untrusted_fs", "net", "backtrace"], optional = true } +sgx_types = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", features = ["extra_traits"] } + +# local deps +itp-ocall-api = { path = "../ocall-api", default-features = false } +itp-settings = { path = "../settings" } +itp-sgx-crypto = { path = "../sgx/crypto", default-features = false } +itp-sgx-io = { path = "../sgx/io", default-features = false } +itp-time-utils = { path = "../time-utils", default-features = false } + +# integritee +httparse = { default-features = false, git = "https://github.com/integritee-network/httparse-sgx", branch = "sgx-experimental" } + +# substrate deps +sp-core = { default-features = false, features = ["full_crypto"], git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +[features] +default = ["std"] +std = [ + # crates-io no_std + "arrayvec/std", + "codec/std", + "hex/std", + "log/std", + "itertools/use_std", + # optional std only + "base64", + "chrono", + "rustls", + "serde_json", + "thiserror", + "webpki", + # local + "itp-ocall-api/std", + "itp-sgx-io/std", + "itp-sgx-crypto/std", + # substrate + "sp-core/std", + # integritee + "httparse/std", +] + +sgx = [ + # sgx-only + "base64_sgx", + "chrono_sgx", + "rustls_sgx", + "serde_json_sgx", + "thiserror_sgx", + "webpki_sgx", + "yasna_sgx", + "sgx_tse", + "sgx_tstd", + "sgx_rand", + "sgx_tcrypto", + "num-bigint", + # local + "itp-sgx-io/sgx", + "itp-sgx-crypto/sgx", + # integritee + "httparse/mesalock_sgx", +] +test = [] +production = [] diff --git a/bitacross-worker/core-primitives/attestation-handler/src/attestation_handler.rs b/bitacross-worker/core-primitives/attestation-handler/src/attestation_handler.rs new file mode 100644 index 0000000000..9657db5edc --- /dev/null +++ b/bitacross-worker/core-primitives/attestation-handler/src/attestation_handler.rs @@ -0,0 +1,853 @@ +// Copyright 2022 Integritee AG and Supercomputing Systems AG +// Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in +// the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Baidu, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use crate::{cert, Error as EnclaveError, Error, Result as EnclaveResult}; +use codec::Encode; +use core::{convert::TryInto, default::Default}; +use itertools::Itertools; +use itp_ocall_api::EnclaveAttestationOCallApi; +use itp_settings::{ + files::{RA_API_KEY_FILE, RA_DUMP_CERT_DER_FILE, RA_SPID_FILE}, + worker::MR_ENCLAVE_SIZE, +}; +use itp_sgx_crypto::key_repository::AccessKey; +use itp_sgx_io as io; +use itp_time_utils::now_as_secs; +use log::*; +use sgx_rand::{os, Rng}; +use sgx_tcrypto::{rsgx_sha256_slice, SgxEccHandle}; +use sgx_tse::{rsgx_create_report, rsgx_verify_report}; +use sgx_types::{ + c_int, sgx_epid_group_id_t, sgx_quote_nonce_t, sgx_quote_sign_type_t, sgx_report_data_t, + sgx_spid_t, sgx_status_t, sgx_target_info_t, SgxResult, *, +}; +use sp_core::{ed25519, Pair}; +use std::{ + borrow::ToOwned, + env, format, + io::{Read, Write}, + net::TcpStream, + prelude::v1::*, + println, str, + string::{String, ToString}, + sync::Arc, + vec::Vec, +}; + +pub const DEV_HOSTNAME: &str = "api.trustedservices.intel.com"; + +// Litentry TODO: use `dev` for production temporary. Will switch to dcap later. +#[cfg(feature = "production")] +pub const SIGRL_SUFFIX: &str = "/sgx/dev/attestation/v4/sigrl/"; +#[cfg(feature = "production")] +pub const REPORT_SUFFIX: &str = "/sgx/dev/attestation/v4/report"; + +#[cfg(not(feature = "production"))] +pub const SIGRL_SUFFIX: &str = "/sgx/dev/attestation/v4/sigrl/"; +#[cfg(not(feature = "production"))] +pub const REPORT_SUFFIX: &str = "/sgx/dev/attestation/v4/report"; + +/// Trait to provide an abstraction to the attestation logic +pub trait AttestationHandler { + /// Generates an encoded remote attestation certificate. Returns DER encoded certificate. + /// If skip_ra is set, it will not perform a remote attestation via IAS + /// but instead generate a mock certificate. + fn generate_ias_ra_cert(&self, skip_ra: bool) -> EnclaveResult>; + + /// Returns the DER encoded private_key, DER encoded certificate and the raw DCAP quote. + /// If skip_ra is set, it will not perform a remote attestation via IAS + /// but instead generate a mock certificate. + fn generate_dcap_ra_cert( + &self, + quoting_enclave_target_info: Option<&sgx_target_info_t>, + quote_size: Option<&u32>, + skip_ra: bool, + ) -> EnclaveResult<(Vec, Vec, Vec)>; + + /// Get the measurement register value of the enclave + fn get_mrenclave(&self) -> EnclaveResult<[u8; MR_ENCLAVE_SIZE]>; + + /// Write the remote attestation report to the disk + fn dump_ias_ra_cert_to_disk(&self) -> EnclaveResult<()>; + + /// Write the remote attestation report to the disk + fn dump_dcap_ra_cert_to_disk( + &self, + quoting_enclave_target_info: &sgx_target_info_t, + quote_size: u32, + ) -> EnclaveResult<()>; + + /// Create the remote attestation report and encapsulate it in a DER certificate + /// Returns a pair consisting of (private key DER, certificate DER) + fn create_epid_ra_report_and_signature( + &self, + sign_type: sgx_quote_sign_type_t, + skip_ra: bool, + ) -> EnclaveResult<(Vec, Vec)>; +} + +pub struct IntelAttestationHandler { + pub(crate) ocall_api: Arc, + pub(crate) signing_key_repo: Arc, +} + +impl IntelAttestationHandler +where + OCallApi: EnclaveAttestationOCallApi, + AccessSigningKey: AccessKey, +{ + fn create_payload_epid( + &self, + pub_k: &[u8; 32], + sign_type: sgx_quote_sign_type_t, + ) -> EnclaveResult { + info!(" [Enclave] Create attestation report"); + let (attn_report, sig, cert) = match self.create_epid_attestation_report(&pub_k, sign_type) + { + Ok(r) => r, + Err(e) => { + error!(" [Enclave] Error in create_attestation_report: {:?}", e); + return Err(e.into()) + }, + }; + println!(" [Enclave] Create attestation report successful"); + debug!(" attn_report = {:?}", attn_report); + debug!(" sig = {:?}", sig); + debug!(" cert = {:?}", cert); + + // concat the information + Ok(attn_report + "|" + &sig + "|" + &cert) + } +} + +impl AttestationHandler + for IntelAttestationHandler +where + OCallApi: EnclaveAttestationOCallApi, + AccessSigningKey: AccessKey, +{ + fn generate_ias_ra_cert(&self, skip_ra: bool) -> EnclaveResult> { + // Our certificate is unlinkable. + let sign_type = sgx_quote_sign_type_t::SGX_UNLINKABLE_SIGNATURE; + + // FIXME: should call `create_ra_report_and_signature` in skip_ra mode as well: + // https://github.com/integritee-network/worker/issues/321. + let cert_der = if !skip_ra { + match self.create_epid_ra_report_and_signature(sign_type, skip_ra) { + Ok((_key_der, cert_der)) => cert_der, + Err(e) => return Err(e), + } + } else { + self.get_mrenclave()?.encode() + }; + + Ok(cert_der) + } + + fn get_mrenclave(&self) -> EnclaveResult<[u8; MR_ENCLAVE_SIZE]> { + match self.ocall_api.get_mrenclave_of_self() { + Ok(m) => Ok(m.m), + Err(e) => Err(EnclaveError::Sgx(e)), + } + } + + fn dump_ias_ra_cert_to_disk(&self) -> EnclaveResult<()> { + // our certificate is unlinkable + let sign_type = sgx_quote_sign_type_t::SGX_UNLINKABLE_SIGNATURE; + + let (_key_der, cert_der) = match self.create_epid_ra_report_and_signature(sign_type, false) + { + Ok(r) => r, + Err(e) => return Err(e), + }; + + if let Err(err) = io::write(&cert_der, RA_DUMP_CERT_DER_FILE) { + error!( + " [Enclave] failed to write RA file ({}), status: {:?}", + RA_DUMP_CERT_DER_FILE, err + ); + return Err(Error::IoError(err)) + } + info!(" [Enclave] dumped ra cert to {}", RA_DUMP_CERT_DER_FILE); + Ok(()) + } + + fn dump_dcap_ra_cert_to_disk( + &self, + quoting_enclave_target_info: &sgx_target_info_t, + quote_size: u32, + ) -> EnclaveResult<()> { + let (_priv_key_der, _cert_der, dcap_quote) = match self.generate_dcap_ra_cert( + Some(quoting_enclave_target_info), + Some("e_size), + false, + ) { + Ok(r) => r, + Err(e) => return Err(e), + }; + + if let Err(err) = io::write(&dcap_quote, RA_DUMP_CERT_DER_FILE) { + error!( + " [Enclave] failed to write RA file ({}), status: {:?}", + RA_DUMP_CERT_DER_FILE, err + ); + return Err(Error::IoError(err)) + } + info!(" [Enclave] dumped ra cert to {}", RA_DUMP_CERT_DER_FILE); + Ok(()) + } + + fn create_epid_ra_report_and_signature( + &self, + sign_type: sgx_quote_sign_type_t, + skip_ra: bool, + ) -> EnclaveResult<(Vec, Vec)> { + let chain_signer = self.signing_key_repo.retrieve_key()?; + info!("[Enclave Attestation] Ed25519 pub raw : {:?}", chain_signer.public().0); + + info!(" [Enclave] Generate keypair"); + let ecc_handle = SgxEccHandle::new(); + let _result = ecc_handle.open(); + let (prv_k, pub_k) = ecc_handle.create_key_pair()?; + info!(" [Enclave] Generate ephemeral ECDSA keypair successful"); + debug!(" pubkey X is {:02x}", pub_k.gx.iter().format("")); + debug!(" pubkey Y is {:02x}", pub_k.gy.iter().format("")); + + let payload = if !skip_ra { + self.create_payload_epid(&chain_signer.public().0, sign_type)? + } else { + Default::default() + }; + + // generate an ECC certificate + info!(" [Enclave] Generate ECC Certificate"); + let (key_der, cert_der) = match cert::gen_ecc_cert(&payload, &prv_k, &pub_k, &ecc_handle) { + Ok(r) => r, + Err(e) => { + error!(" [Enclave] gen_ecc_cert failed: {:?}", e); + return Err(e.into()) + }, + }; + + let _ = ecc_handle.close(); + info!(" [Enclave] Generate ECC Certificate successful"); + Ok((key_der, cert_der)) + } + + fn generate_dcap_ra_cert( + &self, + quoting_enclave_target_info: Option<&sgx_target_info_t>, + quote_size: Option<&u32>, + skip_ra: bool, + ) -> EnclaveResult<(Vec, Vec, Vec)> { + if !skip_ra && quoting_enclave_target_info.is_none() && quote_size.is_none() { + error!("Enclave Attestation] remote attestation not skipped, but Quoting Enclave (QE) data is not available"); + return Err(EnclaveError::Sgx(sgx_status_t::SGX_ERROR_UNEXPECTED)) + } + let chain_signer = self.signing_key_repo.retrieve_key()?; + info!("[Enclave Attestation] Ed25519 signer pub key: {:?}", chain_signer.public().0); + + let ecc_handle = SgxEccHandle::new(); + let _result = ecc_handle.open(); + let (prv_k, pub_k) = ecc_handle.create_key_pair()?; + info!("Enclave Attestation] Generated ephemeral ECDSA keypair:"); + debug!(" pubkey X is {:02x}", pub_k.gx.iter().format("")); + debug!(" pubkey Y is {:02x}", pub_k.gy.iter().format("")); + + let qe_quote = if !skip_ra { + let qe_quote = match self.retrieve_qe_dcap_quote( + &chain_signer.public().0, + quoting_enclave_target_info.unwrap(), + *quote_size.unwrap(), + ) { + Ok(quote) => quote, + Err(e) => { + error!("[Enclave] Error in create_dcap_attestation_report: {:?}", e); + return Err(e.into()) + }, + }; + qe_quote + } else { + Default::default() + }; + + let qe_quote_base_64 = base64::encode(&qe_quote[..]); + // generate an ECC certificate + debug!("[Enclave] Generate ECC Certificate"); + let (key_der, cert_der) = + match cert::gen_ecc_cert(&qe_quote_base_64, &prv_k, &pub_k, &ecc_handle) { + Ok(r) => r, + Err(e) => { + error!("[Enclave] gen_ecc_cert failed: {:?}", e); + return Err(e.into()) + }, + }; + + let _ = ecc_handle.close(); + + debug!("[Enclave] Generated ECC cert info:"); + trace!("[Enclave] Generated ECC cert info: key_der={:?}", &key_der); + trace!("[Enclave] Generated ECC cert info: cert_der={:?}", &cert_der); + trace!("[Enclave] Generated ECC cert info: qe_quote={:?}", &qe_quote); + Ok((key_der, cert_der, qe_quote)) + } +} + +impl IntelAttestationHandler { + pub fn new(ocall_api: Arc, signing_key_repo: Arc) -> Self { + Self { ocall_api, signing_key_repo } + } +} + +impl IntelAttestationHandler +where + OCallApi: EnclaveAttestationOCallApi, + AccessSigningKey: AccessKey, +{ + fn parse_response_attn_report(&self, resp: &[u8]) -> EnclaveResult<(String, String, String)> { + debug!(" [Enclave] Entering parse_response_attn_report"); + let mut headers = [httparse::EMPTY_HEADER; 16]; + let mut respp = httparse::Response::new(&mut headers); + let result = respp.parse(resp); + debug!(" [Enclave] respp.parse result {:?}", result); + + self.log_resp_code(&mut respp.code); + + let mut len_num: u32 = 0; + + let mut sig = String::new(); + let mut cert = String::new(); + let mut attn_report = String::new(); + + for i in 0..respp.headers.len() { + let h = respp.headers[i]; + //println!("{} : {}", h.name, str::from_utf8(h.value).unwrap()); + match h.name { + "Content-Length" => { + let len_str = String::from_utf8(h.value.to_vec()) + .map_err(|e| EnclaveError::Other(e.into()))?; + len_num = len_str.parse::().map_err(|e| EnclaveError::Other(e.into()))?; + debug!(" [Enclave] Content length = {}", len_num); + }, + "X-IASReport-Signature" => + sig = String::from_utf8(h.value.to_vec()) + .map_err(|e| EnclaveError::Other(e.into()))?, + "X-IASReport-Signing-Certificate" => + cert = String::from_utf8(h.value.to_vec()) + .map_err(|e| EnclaveError::Other(e.into()))?, + _ => (), + } + } + + // Remove %0A from cert, and only obtain the signing cert + cert = cert.replace("%0A", ""); + cert = cert::percent_decode(cert)?; + let v: Vec<&str> = cert.split("-----").collect(); + let sig_cert = v[2].to_string(); + + if len_num != 0 { + // The unwrap is safe. It resolves to the https::Status' unwrap function which only panics + // if the the response is not complete, which cannot happen if the result is Ok(). + let header_len = result.map_err(|e| EnclaveError::Other(e.into()))?.unwrap(); + let resp_body = &resp[header_len..]; + attn_report = + String::from_utf8(resp_body.to_vec()).map_err(|e| EnclaveError::Other(e.into()))?; + debug!(" [Enclave] Attestation report = {}", attn_report); + } + + // len_num == 0 + Ok((attn_report, sig, sig_cert)) + } + + fn log_resp_code(&self, resp_code: &mut Option) { + let msg = match resp_code { + Some(200) => "OK, operation successful", + Some(400) => "Bad request, quote is invalid, or linkability of quote/subscription does not match.", + Some(401) => "Unauthorized, failed to authenticate or authorize request.", + Some(404) => "Not found, GID does not refer to a valid EPID group ID.", + Some(500) => "Internal error occurred.", + Some(503) => + "Service is currently not able to process the request (due to + a temporary overloading or maintenance). This is a + temporary state – the same request can be repeated after + some time.", + _ => { + error!("Error, received unknown HTTP response: {:?}", resp_code); + "Unknown error occured" + }, + }; + debug!(" [Enclave] msg = {}", msg); + } + + fn parse_response_sigrl(&self, resp: &[u8]) -> EnclaveResult> { + debug!(" [Enclave] Entering parse_response_sigrl"); + let mut headers = [httparse::EMPTY_HEADER; 16]; + let mut respp = httparse::Response::new(&mut headers); + let result = respp.parse(resp); + debug!(" [Enclave] Parse result {:?}", result); + debug!(" [Enclave] Parse response {:?}", respp); + + self.log_resp_code(&mut respp.code); + + let mut len_num: u32 = 0; + + for i in 0..respp.headers.len() { + let h = respp.headers[i]; + if h.name == "content-length" { + let len_str = String::from_utf8(h.value.to_vec()) + .map_err(|e| EnclaveError::Other(e.into()))?; + len_num = len_str.parse::().map_err(|e| EnclaveError::Other(e.into()))?; + debug!(" [Enclave] Content length = {}", len_num); + } + } + + if len_num != 0 { + // The unwrap is safe. It resolves to the https::Status' unwrap function which only panics + // if the the response is not complete, which cannot happen if the result is Ok(). + let header_len = result.map_err(|e| EnclaveError::Other(e.into()))?.unwrap(); + let resp_body = &resp[header_len..]; + debug!(" [Enclave] Base64-encoded SigRL: {:?}", resp_body); + + let resp_str = str::from_utf8(resp_body).map_err(|e| EnclaveError::Other(e.into()))?; + return base64::decode(resp_str).map_err(|e| EnclaveError::Other(e.into())) + } + + // len_num == 0 + Ok(Vec::new()) + } + + fn make_ias_client_config() -> rustls::ClientConfig { + let mut config = rustls::ClientConfig::new(); + + config.root_store.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); + config + } + + fn get_sigrl_from_intel(&self, fd: c_int, gid: u32) -> EnclaveResult> { + debug!(" [Enclave] Entering get_sigrl_from_intel. fd = {:?}", fd); + let config = Self::make_ias_client_config(); + //let sigrl_arg = SigRLArg { group_id : gid }; + //let sigrl_req = sigrl_arg.to_httpreq(); + let ias_key = Self::get_ias_api_key()?; + + let req = format!("GET {}{:08x} HTTP/1.1\r\nHOST: {}\r\nOcp-Apim-Subscription-Key: {}\r\nConnection: Close\r\n\r\n", + SIGRL_SUFFIX, + gid, + DEV_HOSTNAME, + ias_key); + debug!(" [Enclave] request = {}", req); + + let dns_name = webpki::DNSNameRef::try_from_ascii_str(DEV_HOSTNAME) + .map_err(|e| EnclaveError::Other(e.into()))?; + let mut sess = rustls::ClientSession::new(&Arc::new(config), dns_name); + let mut sock = TcpStream::new(fd)?; + let mut tls = rustls::Stream::new(&mut sess, &mut sock); + + let _result = tls.write(req.as_bytes()); + let mut plaintext = Vec::new(); + + debug!(" [Enclave] tls.write complete"); + + tls.read_to_end(&mut plaintext)?; + + debug!(" [Enclave] tls.read_to_end complete"); + let resp_string = + String::from_utf8(plaintext.clone()).map_err(|e| EnclaveError::Other(e.into()))?; + + debug!(" [Enclave] resp_string = {}", resp_string); + + self.parse_response_sigrl(&plaintext) + } + + // TODO: support pse + fn get_report_from_intel( + &self, + fd: c_int, + quote: Vec, + ) -> EnclaveResult<(String, String, String)> { + debug!(" [Enclave] Entering get_report_from_intel. fd = {:?}", fd); + let config = Self::make_ias_client_config(); + let encoded_quote = base64::encode("e[..]); + let encoded_json = format!("{{\"isvEnclaveQuote\":\"{}\"}}\r\n", encoded_quote); + + let ias_key = Self::get_ias_api_key()?; + + let req = format!("POST {} HTTP/1.1\r\nHOST: {}\r\nOcp-Apim-Subscription-Key:{}\r\nContent-Length:{}\r\nContent-Type: application/json\r\nConnection: close\r\n\r\n{}", + REPORT_SUFFIX, + DEV_HOSTNAME, + ias_key, + encoded_json.len(), + encoded_json); + debug!(" [Enclave] Req = {}", req); + let dns_name = webpki::DNSNameRef::try_from_ascii_str(DEV_HOSTNAME).map_err(|e| { + error!("Invalid DEV_HOSTNAME"); + EnclaveError::Other(e.into()) + })?; + let mut sess = rustls::ClientSession::new(&Arc::new(config), dns_name); + let mut sock = TcpStream::new(fd)?; + let mut tls = rustls::Stream::new(&mut sess, &mut sock); + + let _result = tls.write(req.as_bytes()); + let mut plaintext = Vec::new(); + + debug!(" [Enclave] tls.write complete"); + + tls.read_to_end(&mut plaintext)?; + debug!(" [Enclave] tls.read_to_end complete"); + let resp_string = String::from_utf8(plaintext.clone()).map_err(|e| { + error!(" [Enclave] error decoding tls answer to string"); + EnclaveError::Other(e.into()) + })?; + + debug!(" [Enclave] resp_string = {}", resp_string); + + self.parse_response_attn_report(&plaintext) + } + + fn as_u32_le(&self, array: [u8; 4]) -> u32 { + u32::from(array[0]) + + (u32::from(array[1]) << 8) + + (u32::from(array[2]) << 16) + + (u32::from(array[3]) << 24) + } + + fn create_epid_attestation_report( + &self, + pub_k: &[u8; 32], + sign_type: sgx_quote_sign_type_t, + ) -> SgxResult<(String, String, String)> { + // Workflow: + // (1) ocall to get the target_info structure (ti) and epid group id (eg) + // (1.5) get sigrl + // (2) call sgx_create_report with ti+data, produce an sgx_report_t + // (3) ocall to sgx_get_quote to generate (*mut sgx-quote_t, uint32_t) + + // (1) get ti + eg + let init_quote = self.ocall_api.sgx_init_quote()?; + + let epid_group_id: sgx_epid_group_id_t = init_quote.1; + let target_info: sgx_target_info_t = init_quote.0; + + debug!(" [Enclave] EPID group id = {:?}", epid_group_id); + + let eg_num = self.as_u32_le(epid_group_id); + + // (1.5) get sigrl + let ias_socket = self.ocall_api.get_ias_socket()?; + + info!(" [Enclave] ias_sock = {}", ias_socket); + + // Now sigrl_vec is the revocation list, a vec + let sigrl_vec: Vec = self.get_sigrl_from_intel(ias_socket, eg_num)?; + + // (2) Generate the report + let mut report_data: sgx_report_data_t = sgx_report_data_t::default(); + report_data.d[..32].clone_from_slice(&pub_k[..]); + + let report = match rsgx_create_report(&target_info, &report_data) { + Ok(r) => { + debug!( + " [Enclave] Report creation successful. mr_signer.m = {:x?}", + r.body.mr_signer.m + ); + r + }, + Err(e) => { + error!(" [Enclave] Report creation failed. {:?}", e); + return Err(e) + }, + }; + + let mut quote_nonce = sgx_quote_nonce_t { rand: [0; 16] }; + let mut os_rng = os::SgxRng::new().map_err(|e| EnclaveError::Other(e.into()))?; + os_rng.fill_bytes(&mut quote_nonce.rand); + + // (3) Generate the quote + // Args: + // 1. sigrl: ptr + len + // 2. report: ptr 432bytes + // 3. linkable: u32, unlinkable=0, linkable=1 + // 4. spid: sgx_spid_t ptr 16bytes + // 5. sgx_quote_nonce_t ptr 16bytes + // 6. p_sig_rl + sigrl size ( same to sigrl) + // 7. [out]p_qe_report need further check + // 8. [out]p_quote + // 9. quote_size + + let spid: sgx_spid_t = Self::load_spid(RA_SPID_FILE)?; + + let quote_result = + self.ocall_api.get_quote(sigrl_vec, report, sign_type, spid, quote_nonce)?; + + let qe_report = quote_result.0; + let quote_content = quote_result.1; + + // Added 09-28-2018 + // Perform a check on qe_report to verify if the qe_report is valid + match rsgx_verify_report(&qe_report) { + Ok(()) => debug!(" [Enclave] rsgx_verify_report success!"), + Err(x) => { + error!(" [Enclave] rsgx_verify_report failed. {:?}", x); + return Err(x) + }, + } + + // Check if the qe_report is produced on the same platform + if target_info.mr_enclave.m != qe_report.body.mr_enclave.m + || target_info.attributes.flags != qe_report.body.attributes.flags + || target_info.attributes.xfrm != qe_report.body.attributes.xfrm + { + error!(" [Enclave] qe_report does not match current target_info!"); + return Err(sgx_status_t::SGX_ERROR_UNEXPECTED) + } + + debug!(" [Enclave] qe_report check success"); + + // Check qe_report to defend against replay attack + // The purpose of p_qe_report is for the ISV enclave to confirm the QUOTE + // it received is not modified by the untrusted SW stack, and not a replay. + // The implementation in QE is to generate a REPORT targeting the ISV + // enclave (target info from p_report) , with the lower 32Bytes in + // report.data = SHA256(p_nonce||p_quote). The ISV enclave can verify the + // p_qe_report and report.data to confirm the QUOTE has not be modified and + // is not a replay. It is optional. + + // need to call this a second time (first time is when we get the sigrl revocation list) + // (has some internal state that needs to be reset)! + let ias_socket = self.ocall_api.get_ias_socket()?; + + let mut rhs_vec: Vec = quote_nonce.rand.to_vec(); + rhs_vec.extend("e_content); + let rhs_hash = rsgx_sha256_slice(&rhs_vec[..])?; + let lhs_hash = &qe_report.body.report_data.d[..32]; + + debug!(" [Enclave] rhs hash = {:02X}", rhs_hash.iter().format("")); + debug!(" [Enclave] lhs hash = {:02X}", lhs_hash.iter().format("")); + + if rhs_hash != lhs_hash { + error!(" [Enclave] Quote is tampered!"); + return Err(sgx_status_t::SGX_ERROR_UNEXPECTED) + } + + let (attn_report, sig, cert) = self.get_report_from_intel(ias_socket, quote_content)?; + Ok((attn_report, sig, cert)) + } + + fn load_spid(filename: &str) -> SgxResult { + // Check if set as an environment variable + match env::var("IAS_EPID_SPID").or_else(|_| io::read_to_string(filename)) { + Ok(spid) => decode_spid(&spid), + Err(e) => { + error!("Failed to load SPID: {:?}", e); + Err(sgx_status_t::SGX_ERROR_UNEXPECTED) + }, + } + } + + fn get_ias_api_key() -> EnclaveResult { + // Check if set as an environment variable + env::var("IAS_EPID_KEY") + .or_else(|_| io::read_to_string(RA_API_KEY_FILE)) + .map(|key| key.trim_end().to_owned()) + .map_err(|e| EnclaveError::Other(e.into())) + } + + /// Returns Ok if the verification of the quote by the quote verification enclave (QVE) was successful + pub fn ecdsa_quote_verification(&self, quote: Vec) -> SgxResult<()> { + let mut app_enclave_target_info: sgx_target_info_t = unsafe { std::mem::zeroed() }; + let quote_collateral: sgx_ql_qve_collateral_t = unsafe { std::mem::zeroed() }; + let mut qve_report_info: sgx_ql_qe_report_info_t = unsafe { std::mem::zeroed() }; + let supplemental_data_size = std::mem::size_of::() as u32; + + // Get target info of the app enclave. QvE will target the generated report to this enclave. + let ret_val = + unsafe { sgx_self_target(&mut app_enclave_target_info as *mut sgx_target_info_t) }; + if ret_val != sgx_status_t::SGX_SUCCESS { + error!("sgx_self_target returned: {:?}", ret_val); + return Err(sgx_status_t::SGX_ERROR_UNEXPECTED) + } + + // Set current time, which is needed to check against the expiration date of the certificate. + let current_time: i64 = now_as_secs().try_into().unwrap_or_else(|e| { + panic!("Could not convert SystemTime from u64 into i64: {:?}", e); + }); + + // Set random nonce. + let mut rand_nonce = vec![0u8; 16]; + let ret_val = unsafe { sgx_read_rand(rand_nonce.as_mut_ptr(), rand_nonce.len()) }; + if ret_val != sgx_status_t::SGX_SUCCESS { + error!("sgx_read_rand returned: {:?}", ret_val); + return Err(sgx_status_t::SGX_ERROR_UNEXPECTED) + } + debug!("Retrieved random nonce {:?}", rand_nonce); + qve_report_info.nonce.rand.copy_from_slice(rand_nonce.as_slice()); + qve_report_info.app_enclave_target_info = app_enclave_target_info; + + // Ocall to call Quote verification Enclave (QvE), which verifies the generated quote. + let ( + collateral_expiration_status, + quote_verification_result, + qve_report_info_return_value, + supplemental_data, + ) = self.ocall_api.get_qve_report_on_quote( + quote.clone(), + current_time, + quote_collateral, + qve_report_info, + supplemental_data_size, + )?; + + // Check nonce of qve report to protect against replay attacks, as the qve report + // is coming from the untrusted side. + if qve_report_info_return_value.nonce.rand != qve_report_info.nonce.rand { + error!( + "Nonce of input value and return value are not matching. Input: {:?}, Output: {:?}", + qve_report_info.nonce.rand, qve_report_info_return_value.nonce.rand + ); + return Err(sgx_status_t::SGX_ERROR_UNEXPECTED) + } + + // Set the threshold of QvE ISV SVN. The ISV SVN of QvE used to verify quote must be greater or equal to this threshold + // e.g. You can check latest QvE ISVSVN from QvE configuration file on Github + // https://github.com/intel/SGXDataCenterAttestationPrimitives/blob/master/QuoteVerification/QvE/Enclave/linux/config.xml#L4 + // or you can get latest QvE ISVSVN in QvE Identity JSON file from + // https://api.trustedservices.intel.com/sgx/certification/v3/qve/identity + // Make sure you are using trusted & latest QvE ISV SVN as threshold + // Warning: The function may return erroneous result if QvE ISV SVN has been modified maliciously. + let qve_isvsvn_threshold: sgx_isv_svn_t = 6; + + // Verify the qve report to validate that it is coming from a legit quoting verification enclave + // and has not been tampered with. + let ret_val = unsafe { + sgx_tvl_verify_qve_report_and_identity( + quote.as_ptr(), + quote.len() as u32, + &qve_report_info_return_value as *const sgx_ql_qe_report_info_t, + current_time, + collateral_expiration_status, + quote_verification_result, + supplemental_data.as_ptr(), + supplemental_data_size, + qve_isvsvn_threshold, + ) + }; + + if ret_val != sgx_quote3_error_t::SGX_QL_SUCCESS { + error!("sgx_tvl_verify_qve_report_and_identity returned: {:?}", ret_val); + return Err(sgx_status_t::SGX_ERROR_UNEXPECTED) + } + + Ok(()) + } + + pub fn retrieve_qe_dcap_quote( + &self, + pub_k: &[u8; 32], + quoting_enclave_target_info: &sgx_target_info_t, + quote_size: u32, + ) -> SgxResult> { + // Generate app enclave report and include the enclave public key. + // The quote will be generated on top of this report and validate that the + // report as well as the public key inside it are coming from a legit + // intel sgx enclave. + let mut report_data: sgx_report_data_t = sgx_report_data_t::default(); + report_data.d[..32].clone_from_slice(&pub_k[..]); + + let app_report = match rsgx_create_report(quoting_enclave_target_info, &report_data) { + Ok(report) => { + debug!( + "rsgx_create_report creation successful. mr_signer: {:?}", + report.body.mr_signer.m + ); + report + }, + Err(e) => { + error!("rsgx_create_report creation failed. {:?}", e); + return Err(e) + }, + }; + + // Retrieve quote from pccs for our app enclave. + debug!("Entering ocall_api.get_dcap_quote with quote size: {:?} ", quote_size); + let quote_vec = self.ocall_api.get_dcap_quote(app_report, quote_size)?; + + // Check mrenclave of quote, to ensure the quote has not been tampered with + // while being on the untrusted side. + // This step is probably obsolete, as the QvE will check the quote as well on behalf + // of the target enclave. + let p_quote3: *const sgx_quote3_t = quote_vec.as_ptr() as *const sgx_quote3_t; + let quote3: sgx_quote3_t = unsafe { *p_quote3 }; + if quote3.report_body.mr_enclave.m != app_report.body.mr_enclave.m { + error!("mr_enclave of quote and app_report are not matching"); + error!("mr_enclave of quote: {:?}", quote3.report_body.mr_enclave.m); + error!("mr_enclave of quote: {:?}", app_report.body.mr_enclave.m); + return Err(sgx_status_t::SGX_ERROR_UNEXPECTED) + } + + Ok(quote_vec) + } +} + +fn decode_spid(hex_encoded_string: &str) -> SgxResult { + let mut spid = sgx_spid_t::default(); + let hex = hex_encoded_string.trim(); + + if hex.len() < itp_settings::files::SPID_MIN_LENGTH { + error!( + "Input spid length ({}) is incorrect, minimum length required is {}", + hex.len(), + itp_settings::files::SPID_MIN_LENGTH + ); + return Err(sgx_status_t::SGX_ERROR_UNEXPECTED) + } + + let decoded_vec = hex::decode(hex).map_err(|_| sgx_status_t::SGX_ERROR_UNEXPECTED)?; + + spid.id.copy_from_slice(&decoded_vec[..16]); + Ok(spid) +} + +#[cfg(feature = "test")] +pub mod tests { + + use super::*; + + pub fn decode_spid_works() { + let spid_encoded = "F39ABCF95015A5BF6C7D360EF5035E12"; + let expected_spid = sgx_spid_t { + id: [243, 154, 188, 249, 80, 21, 165, 191, 108, 125, 54, 14, 245, 3, 94, 18], + }; + + let decoded_spid = decode_spid(spid_encoded).unwrap(); + assert_eq!(decoded_spid.id, expected_spid.id); + } +} diff --git a/bitacross-worker/core-primitives/attestation-handler/src/cert.rs b/bitacross-worker/core-primitives/attestation-handler/src/cert.rs new file mode 100644 index 0000000000..7d1a2d6064 --- /dev/null +++ b/bitacross-worker/core-primitives/attestation-handler/src/cert.rs @@ -0,0 +1,497 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use crate::{Error as EnclaveError, Result as EnclaveResult}; +use arrayvec::ArrayVec; +use chrono::DateTime; +use itertools::Itertools; +use itp_ocall_api::EnclaveAttestationOCallApi; +use log::*; +use serde_json::Value; +use sgx_types::{ + sgx_platform_info_t, sgx_quote_t, sgx_status_t, SgxResult, SGX_PLATFORM_INFO_SIZE, +}; +use std::{ + io::BufReader, + ptr, str, + string::String, + time::{SystemTime, UNIX_EPOCH}, + vec::Vec, +}; + +type SignatureAlgorithms = &'static [&'static webpki::SignatureAlgorithm]; +static SUPPORTED_SIG_ALGS: SignatureAlgorithms = &[ + &webpki::ECDSA_P256_SHA256, + &webpki::ECDSA_P256_SHA384, + &webpki::ECDSA_P384_SHA256, + &webpki::ECDSA_P384_SHA384, + &webpki::RSA_PSS_2048_8192_SHA256_LEGACY_KEY, + &webpki::RSA_PSS_2048_8192_SHA384_LEGACY_KEY, + &webpki::RSA_PSS_2048_8192_SHA512_LEGACY_KEY, + &webpki::RSA_PKCS1_2048_8192_SHA256, + &webpki::RSA_PKCS1_2048_8192_SHA384, + &webpki::RSA_PKCS1_2048_8192_SHA512, + &webpki::RSA_PKCS1_3072_8192_SHA384, +]; + +pub const CERTEXPIRYDAYS: i64 = 90i64; +pub const IAS_REPORT_CA: &[u8] = include_bytes!("../AttestationReportSigningCACert.pem"); + +#[cfg(feature = "sgx")] +pub use sgx::*; + +#[cfg(feature = "sgx")] +pub mod sgx { + use super::*; + use bit_vec::BitVec; + use chrono::{Duration, TimeZone, Utc as TzUtc}; + use num_bigint::BigUint; + use sgx_tcrypto::SgxEccHandle; + use sgx_types::{sgx_ec256_private_t, sgx_ec256_public_t}; + use yasna::models::ObjectIdentifier; + + const ISSUER: &str = "Integritee"; + const SUBJECT: &str = "Integritee ephemeral"; + + /// `payload` must be a valid a string, not just arbitrary data. + pub fn gen_ecc_cert( + payload: &str, + prv_k: &sgx_ec256_private_t, + pub_k: &sgx_ec256_public_t, + ecc_handle: &SgxEccHandle, + ) -> Result<(Vec, Vec), sgx_status_t> { + // Generate public key bytes since both DER will use it + let mut pub_key_bytes: Vec = vec![4]; + let mut pk_gx = pub_k.gx; + pk_gx.reverse(); + let mut pk_gy = pub_k.gy; + pk_gy.reverse(); + pub_key_bytes.extend_from_slice(&pk_gx); + pub_key_bytes.extend_from_slice(&pk_gy); + + // Generate Certificate DER + let cert_der = yasna::construct_der(|writer| { + writer.write_sequence(|writer| { + writer.next().write_sequence(|writer| { + // Certificate Version + writer.next().write_tagged(yasna::Tag::context(0), |writer| { + writer.write_i8(2); + }); + // Certificate Serial Number (unused but required) + writer.next().write_u8(1); + // Signature Algorithm: ecdsa-with-SHA256 + writer.next().write_sequence(|writer| { + writer + .next() + .write_oid(&ObjectIdentifier::from_slice(&[1, 2, 840, 10045, 4, 3, 2])); + }); + // Issuer: CN=MesaTEE (unused but required) + writer.next().write_sequence(|writer| { + writer.next().write_set(|writer| { + writer.next().write_sequence(|writer| { + writer + .next() + .write_oid(&ObjectIdentifier::from_slice(&[2, 5, 4, 3])); + writer.next().write_utf8_string(ISSUER); + }); + }); + }); + // Validity: Issuing/Expiring Time (unused but required) + let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); + let issue_ts = TzUtc.timestamp(now.as_secs() as i64, 0); + let expire = now + Duration::days(CERTEXPIRYDAYS).to_std().unwrap(); + let expire_ts = TzUtc.timestamp(expire.as_secs() as i64, 0); + writer.next().write_sequence(|writer| { + writer + .next() + .write_utctime(&yasna::models::UTCTime::from_datetime(&issue_ts)); + writer + .next() + .write_utctime(&yasna::models::UTCTime::from_datetime(&expire_ts)); + }); + // Subject: CN=MesaTEE (unused but required) + writer.next().write_sequence(|writer| { + writer.next().write_set(|writer| { + writer.next().write_sequence(|writer| { + writer + .next() + .write_oid(&ObjectIdentifier::from_slice(&[2, 5, 4, 3])); + writer.next().write_utf8_string(SUBJECT); + }); + }); + }); + writer.next().write_sequence(|writer| { + // Public Key Algorithm + writer.next().write_sequence(|writer| { + // id-ecPublicKey + writer.next().write_oid(&ObjectIdentifier::from_slice(&[ + 1, 2, 840, 10045, 2, 1, + ])); + // prime256v1 + writer.next().write_oid(&ObjectIdentifier::from_slice(&[ + 1, 2, 840, 10045, 3, 1, 7, + ])); + }); + // Public Key + writer.next().write_bitvec(&BitVec::from_bytes(&pub_key_bytes)); + }); + // Certificate V3 Extension + writer.next().write_tagged(yasna::Tag::context(3), |writer| { + writer.write_sequence(|writer| { + writer.next().write_sequence(|writer| { + writer.next().write_oid(&ObjectIdentifier::from_slice(&[ + 2, 16, 840, 1, 113_730, 1, 13, + ])); + writer.next().write_bytes(payload.as_bytes()); + }); + }); + }); + }); + // Signature Algorithm: ecdsa-with-SHA256 + writer.next().write_sequence(|writer| { + writer + .next() + .write_oid(&ObjectIdentifier::from_slice(&[1, 2, 840, 10045, 4, 3, 2])); + }); + // Signature + let sig = { + let tbs = &writer.buf[4..]; + ecc_handle.ecdsa_sign_slice(tbs, prv_k).unwrap() + }; + let sig_der = yasna::construct_der(|writer| { + writer.write_sequence(|writer| { + let mut sig_x = sig.x; + sig_x.reverse(); + let mut sig_y = sig.y; + sig_y.reverse(); + writer.next().write_biguint(&BigUint::from_slice(&sig_x)); + writer.next().write_biguint(&BigUint::from_slice(&sig_y)); + }); + }); + writer.next().write_bitvec(&BitVec::from_bytes(&sig_der)); + }); + }); + + // Generate Private Key DER + let key_der = yasna::construct_der(|writer| { + writer.write_sequence(|writer| { + writer.next().write_u8(0); + writer.next().write_sequence(|writer| { + writer + .next() + .write_oid(&ObjectIdentifier::from_slice(&[1, 2, 840, 10045, 2, 1])); + writer + .next() + .write_oid(&ObjectIdentifier::from_slice(&[1, 2, 840, 10045, 3, 1, 7])); + }); + let inner_key_der = yasna::construct_der(|writer| { + writer.write_sequence(|writer| { + writer.next().write_u8(1); + let mut prv_k_r = prv_k.r; + prv_k_r.reverse(); + writer.next().write_bytes(&prv_k_r); + writer.next().write_tagged(yasna::Tag::context(1), |writer| { + writer.write_bitvec(&BitVec::from_bytes(&pub_key_bytes)); + }); + }); + }); + writer.next().write_bytes(&inner_key_der); + }); + }); + + Ok((key_der, cert_der)) + } +} + +pub fn percent_decode(orig: String) -> EnclaveResult { + let v: Vec<&str> = orig.split('%').collect(); + let mut ret = String::new(); + ret.push_str(v[0]); + if v.len() > 1 { + for s in v[1..].iter() { + ret.push(u8::from_str_radix(&s[0..2], 16).map_err(|e| EnclaveError::Other(e.into()))? + as char); + ret.push_str(&s[2..]); + } + } + Ok(ret) +} + +pub fn parse_cert_issuer(cert_der: &[u8]) -> SgxResult> { + // Before we reach here, Webpki already verified the cert is properly signed + + // Search for Public Key prime256v1 OID + let prime256v1_oid = &[0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07]; + let mut offset = cert_der + .windows(prime256v1_oid.len()) + .position(|window| window == prime256v1_oid) + .ok_or(sgx_status_t::SGX_ERROR_UNEXPECTED)?; + offset += 11; // 10 + TAG (0x03) + + // Obtain Public Key length + let mut len = cert_der[offset] as usize; + if len > 0x80 { + len = (cert_der[offset + 1] as usize) * 0x100 + (cert_der[offset + 2] as usize); + offset += 2; + } + + // Obtain Public Key + offset += 1; + let pub_k = cert_der[offset + 2..offset + len].to_vec(); // skip "00 04" + + Ok(pub_k) +} + +// FIXME: This code is redundant with the host call of the integritee-node +pub fn verify_mra_cert( + cert_der: &[u8], + is_payload_base64_encoded: bool, + is_dcap: bool, + attestation_ocall: &A, +) -> SgxResult<()> +where + A: EnclaveAttestationOCallApi, +{ + // Before we reach here, Webpki already verified the cert is properly signed + + // Search for Public Key prime256v1 OID + let prime256v1_oid = &[0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07]; + let mut offset = cert_der + .windows(prime256v1_oid.len()) + .position(|window| window == prime256v1_oid) + .ok_or(sgx_status_t::SGX_ERROR_UNEXPECTED)?; + offset += 11; // 10 + TAG (0x03) + + // Obtain Public Key length + let mut len = cert_der[offset] as usize; + if len > 0x80 { + len = (cert_der[offset + 1] as usize) * 0x100 + (cert_der[offset + 2] as usize); + offset += 2; + } + + // Obtain Public Key + offset += 1; + let pub_k = cert_der[offset + 2..offset + len].to_vec(); // skip "00 04" + + // Search for Netscape Comment OID + let ns_cmt_oid = &[0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x42, 0x01, 0x0D]; + let mut offset = cert_der + .windows(ns_cmt_oid.len()) + .position(|window| window == ns_cmt_oid) + .ok_or(sgx_status_t::SGX_ERROR_UNEXPECTED)?; + offset += 12; // 11 + TAG (0x04) + + // Obtain Netscape Comment length + let mut len = cert_der[offset] as usize; + if len > 0x80 { + len = (cert_der[offset + 1] as usize) * 0x100 + (cert_der[offset + 2] as usize); + offset += 2; + } + + // Obtain Netscape Comment + offset += 1; + let mut payload = cert_der[offset..offset + len].to_vec(); + trace!("payload in mra cert verifier is: {:?}", &payload); + if is_payload_base64_encoded { + payload = base64::decode(&payload[..]).or(Err(sgx_status_t::SGX_ERROR_UNEXPECTED))?; + } + trace!("payload in mra cert verifier is: {:?}", &payload); + if !is_dcap { + // Extract each field + let mut iter = payload.split(|x| *x == b'|'); + let attn_report_raw = iter.next().ok_or(sgx_status_t::SGX_ERROR_UNEXPECTED)?; + let sig_raw = iter.next().ok_or(sgx_status_t::SGX_ERROR_UNEXPECTED)?; + let sig = base64::decode(sig_raw).map_err(|e| EnclaveError::Other(e.into()))?; + + let sig_cert_raw = iter.next().ok_or(sgx_status_t::SGX_ERROR_UNEXPECTED)?; + let sig_cert_dec = base64::decode_config(sig_cert_raw, base64::STANDARD) + .map_err(|e| EnclaveError::Other(e.into()))?; + let sig_cert = webpki::EndEntityCert::from(&sig_cert_dec).expect("Bad DER"); + + // Verify if the signing cert is issued by Intel CA + let mut ias_ca_stripped = IAS_REPORT_CA.to_vec(); + ias_ca_stripped.retain(|&x| x != b'\r' && x != b'\n'); + let head_len = "-----BEGIN CERTIFICATE-----".len(); + let tail_len = "-----END CERTIFICATE-----".len(); + let full_len = ias_ca_stripped.len(); + let ias_ca_core: &[u8] = &ias_ca_stripped[head_len..full_len - tail_len]; + let ias_cert_dec = base64::decode_config(ias_ca_core, base64::STANDARD) + .map_err(|e| EnclaveError::Other(e.into()))?; + + let mut ca_reader = BufReader::new(IAS_REPORT_CA); + + let mut root_store = rustls::RootCertStore::empty(); + root_store.add_pem_file(&mut ca_reader).expect("Failed to add CA"); + + let trust_anchors: Vec = + root_store.roots.iter().map(|cert| cert.to_trust_anchor()).collect(); + + let now_func = webpki::Time::try_from(SystemTime::now()); + + match sig_cert.verify_is_valid_tls_server_cert( + SUPPORTED_SIG_ALGS, + &webpki::TLSServerTrustAnchors(&trust_anchors), + &[ias_cert_dec.as_slice()], + now_func.map_err(|_e| EnclaveError::Time)?, + ) { + Ok(_) => info!("Cert is good"), + Err(e) => { + error!("Cert verification error {:?}", e); + return Err(sgx_status_t::SGX_ERROR_UNEXPECTED) + }, + } + + // Verify the signature against the signing cert + match sig_cert.verify_signature(&webpki::RSA_PKCS1_2048_8192_SHA256, attn_report_raw, &sig) + { + Ok(_) => info!("Signature good"), + Err(e) => { + error!("Signature verification error {:?}", e); + return Err(sgx_status_t::SGX_ERROR_UNEXPECTED) + }, + } + + verify_attn_report(attn_report_raw, pub_k, attestation_ocall) + } else { + // TODO Refactor state provisioning to not use MURA #1385 + // TODO DCAP is currently just passed through! SECURITY!!! + Ok(()) + } +} + +pub fn verify_attn_report( + report_raw: &[u8], + pub_k: Vec, + attestation_ocall: &A, +) -> SgxResult<()> +where + A: EnclaveAttestationOCallApi, +{ + // Verify attestation report + // 1. Check timestamp is within 24H (90day is recommended by Intel) + let attn_report: Value = + serde_json::from_slice(report_raw).map_err(|e| EnclaveError::Other(e.into()))?; + if let Value::String(time) = &attn_report["timestamp"] { + let time_fixed = time.clone() + "+0000"; + let ts = DateTime::parse_from_str(&time_fixed, "%Y-%m-%dT%H:%M:%S%.f%z") + .map_err(|e| EnclaveError::Other(e.into()))? + .timestamp(); + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_err(|e| EnclaveError::Other(e.into()))? + .as_secs() as i64; + info!("Time diff = {}", now - ts); + } else { + error!("Failed to fetch timestamp from attestation report"); + return Err(sgx_status_t::SGX_ERROR_UNEXPECTED) + } + + // 2. Verify quote status (mandatory field) + if let Value::String(quote_status) = &attn_report["isvEnclaveQuoteStatus"] { + debug!("isvEnclaveQuoteStatus = {}", quote_status); + match quote_status.as_ref() { + "OK" => (), + "SW_HARDENING_NEEDED" => info!("Status in attestation report is SW_HARDENING_NEEDED, which is considered acceptable."), + "GROUP_OUT_OF_DATE" | "GROUP_REVOKED" | "CONFIGURATION_NEEDED" => { + // Verify platformInfoBlob for further info if status not OK + if let Value::String(pib) = &attn_report["platformInfoBlob"] { + let mut buf = ArrayVec::<_, SGX_PLATFORM_INFO_SIZE>::new(); + + // the TLV Header (4 bytes/8 hexes) should be skipped + let n = (pib.len() - 8) / 2; + for i in 0..n { + buf.try_push( + u8::from_str_radix(&pib[(i * 2 + 8)..(i * 2 + 10)], 16) + .map_err(|e| EnclaveError::Other(e.into()))?, + ) + .map_err(|e| { + error!("failed to push element to platform info blob buffer, exceeding buffer size ({})", e); + sgx_status_t::SGX_ERROR_UNEXPECTED + })?; + } + + // ArrayVec .into_inner() requires that all elements are occupied by a value + // if that's not the case, the following error will occur + let platform_info = buf.into_inner().map_err(|e| { + error!("Failed to extract platform info from InfoBlob, result does not contain enough elements (require: {}, found: {})", e.capacity(), e.len()); + sgx_status_t::SGX_ERROR_UNEXPECTED + })?; + + attestation_ocall.get_update_info(sgx_platform_info_t { platform_info }, 1)?; + } else { + error!("Failed to fetch platformInfoBlob from attestation report"); + return Err(sgx_status_t::SGX_ERROR_UNEXPECTED) + } + }, + status => { + error!("Unexpected status in attestation report: {}", status); + return Err(sgx_status_t::SGX_ERROR_UNEXPECTED) + }, + } + } else { + error!("Failed to fetch isvEnclaveQuoteStatus from attestation report"); + return Err(sgx_status_t::SGX_ERROR_UNEXPECTED) + } + + // 3. Verify quote body + if let Value::String(quote_raw) = &attn_report["isvEnclaveQuoteBody"] { + let quote = base64::decode(quote_raw).map_err(|e| EnclaveError::Other(e.into()))?; + debug!("Quote = {:?}", quote); + // TODO: lack security check here + let sgx_quote: sgx_quote_t = unsafe { ptr::read(quote.as_ptr() as *const _) }; + + let ti = attestation_ocall.get_mrenclave_of_self()?; + if sgx_quote.report_body.mr_enclave.m != ti.m { + error!( + "mr_enclave is not equal to self {:?} != {:?}", + sgx_quote.report_body.mr_enclave.m, ti.m + ); + return Err(sgx_status_t::SGX_ERROR_UNEXPECTED) + } + + // ATTENTION + // DO SECURITY CHECK ON DEMAND + // DO SECURITY CHECK ON DEMAND + // DO SECURITY CHECK ON DEMAND + + // Curly braces to copy `unaligned_references` of packed fields into properly aligned temporary: + // https://github.com/rust-lang/rust/issues/82523 + debug!("sgx quote version = {}", { sgx_quote.version }); + debug!("sgx quote signature type = {}", { sgx_quote.sign_type }); + debug!( + "sgx quote report_data = {:02x}", + sgx_quote.report_body.report_data.d.iter().format("") + ); + debug!( + "sgx quote mr_enclave = {:02x}", + sgx_quote.report_body.mr_enclave.m.iter().format("") + ); + debug!("sgx quote mr_signer = {:02x}", sgx_quote.report_body.mr_signer.m.iter().format("")); + debug!("Anticipated public key = {:02x}", pub_k.iter().format("")); + if sgx_quote.report_body.report_data.d.to_vec() == pub_k.to_vec() { + info!("Mutual RA done!"); + } + } else { + error!("Failed to fetch isvEnclaveQuoteBody from attestation report"); + return Err(sgx_status_t::SGX_ERROR_UNEXPECTED) + } + + Ok(()) +} diff --git a/bitacross-worker/core-primitives/attestation-handler/src/collateral.rs b/bitacross-worker/core-primitives/attestation-handler/src/collateral.rs new file mode 100644 index 0000000000..a4713c5c94 --- /dev/null +++ b/bitacross-worker/core-primitives/attestation-handler/src/collateral.rs @@ -0,0 +1,158 @@ +/* + Copyright 2022 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::serde_json; +use sgx_types::sgx_ql_qve_collateral_t; +use std::{io::Write, string::String, vec::Vec}; + +/// This is a rust-ified version of the type sgx_ql_qve_collateral_t. +/// See Appendix A.3 in the document +/// "Intel® Software Guard Extensions (Intel® SGX) Data Center Attestation Primitives: ECDSA Quote Library API" +/// https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_SGX_ECDSA_QuoteLibReference_DCAP_API.pdf +pub struct SgxQlQveCollateral { + pub version: u32, // version = 1. PCK Cert chain is in the Quote. + /* intel DCAP 1.13 */ + pub tee_type: u32, // 0x00000000: SGX or 0x00000081: TDX + pub pck_crl_issuer_chain: Vec, + pub root_ca_crl: Vec, + pub pck_crl: Vec, + pub tcb_info_issuer_chain: Vec, + pub tcb_info: Vec, + pub qe_identity_issuer_chain: Vec, + pub qe_identity: Vec, +} + +impl SgxQlQveCollateral { + /// # Safety + /// + /// The caller is in charge of ensuring that `c` is properly initialized and all + /// its members have a value that is not nullptr + pub unsafe fn from_c_type(c: &sgx_ql_qve_collateral_t) -> Self { + let pck_crl_issuer_chain = std::slice::from_raw_parts( + c.pck_crl_issuer_chain as *const u8, + c.pck_crl_issuer_chain_size as usize, + ) + .to_vec(); + let root_ca_crl = + std::slice::from_raw_parts(c.root_ca_crl as *const u8, c.root_ca_crl_size as usize) + .to_vec(); + let pck_crl = + std::slice::from_raw_parts(c.pck_crl as *const u8, c.pck_crl_size as usize).to_vec(); + let tcb_info_issuer_chain = std::slice::from_raw_parts( + c.tcb_info_issuer_chain as *const u8, + c.tcb_info_issuer_chain_size as usize, + ) + .to_vec(); + let tcb_info = + std::slice::from_raw_parts(c.tcb_info as *const u8, c.tcb_info_size as usize).to_vec(); + let qe_identity_issuer_chain = std::slice::from_raw_parts( + c.qe_identity_issuer_chain as *const u8, + c.qe_identity_issuer_chain_size as usize, + ) + .to_vec(); + let qe_identity = + std::slice::from_raw_parts(c.qe_identity as *const u8, c.qe_identity_size as usize) + .to_vec(); + SgxQlQveCollateral { + version: c.version, + tee_type: c.tee_type, + pck_crl_issuer_chain, + root_ca_crl, + pck_crl, + tcb_info_issuer_chain, + tcb_info, + qe_identity_issuer_chain, + qe_identity, + } + } + + pub fn dump_to_disk(&self) { + Self::write_data_to_disk("pck_crl_issuer_chain", &self.pck_crl_issuer_chain); + Self::write_data_to_disk("root_ca_crl", &self.root_ca_crl); + Self::write_data_to_disk("pck_crl", &self.pck_crl); + Self::write_data_to_disk("tcb_info_issuer_chain", &self.tcb_info_issuer_chain); + Self::write_data_to_disk("tcb_info", &self.tcb_info); + Self::write_data_to_disk("qe_identity_issuer_chain", &self.qe_identity_issuer_chain); + Self::write_data_to_disk("qe_identity", &self.qe_identity); + } + + /// Returns the tcb_info split into two parts: json_data and signature + pub fn get_tcb_info_split(&self) -> Option<(String, Vec)> { + let (json_data, signature) = + Self::separate_json_data_and_signature("tcbInfo", &self.tcb_info)?; + match hex::decode(signature) { + Ok(hex_signature) => Some((json_data, hex_signature)), + Err(_) => None, + } + } + + /// Returns the tcb_info split into two parts: json_data and signature + pub fn get_quoting_enclave_split(&self) -> Option<(String, Vec)> { + let (json_data, signature) = + Self::separate_json_data_and_signature("enclaveIdentity", &self.qe_identity)?; + match hex::decode(signature) { + Ok(hex_signature) => Some((json_data, hex_signature)), + Err(_) => None, + } + } + + /// Separates the actual data part from the signature for an Intel collateral in JSON format + /// Returns the data part and signature as a pair + fn separate_json_data_and_signature(data_name: &str, data: &[u8]) -> Option<(String, String)> { + let json = String::from_utf8_lossy(data); + // Remove potential C-style null terminators + let json = json.trim_matches(char::from(0)); + let value: serde_json::Value = serde_json::from_str(json).ok()?; + if value[data_name].is_null() || value["signature"].is_null() { + return None + } + let data_json = serde_json::to_string(&value[data_name]).ok()?; + let signature = serde_json::to_string(&value["signature"]).ok()?; + // We want the signature without leading/ending " + let signature = signature.replace('\"', ""); + Some((data_json, signature)) + } + + fn write_data_to_disk(filename: &str, contents: &[u8]) { + let mut file = std::fs::File::create(filename).unwrap(); + file.write_all(contents).unwrap(); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn separate_json_data_and_signature() { + // A bit more complex json to ensure the ordering stays the same + let json = br#"{"tcbInfo":{"id":"SGX","version":3,"issueDate":"2022-11-17T12:45:32Z"},"signature":"71746f2"}"#; + let (data, signature) = + SgxQlQveCollateral::separate_json_data_and_signature("tcbInfo", json).unwrap(); + assert_eq!(data, r#"{"id":"SGX","version":3,"issueDate":"2022-11-17T12:45:32Z"}"#); + assert_eq!(signature, "71746f2"); + + let json = br#"{"tcbInfo":{not_a_valid_json},"nosignature":"thesignature"}"#; + assert!(SgxQlQveCollateral::separate_json_data_and_signature("tcbInfo", json).is_none()); + + let json = br#"{"tcbInfo":{"id":"SGX"},"nosignature":"thesignature"}"#; + assert!(SgxQlQveCollateral::separate_json_data_and_signature("tcbInfo", json).is_none()); + + let json = br#"{"tcbInfo":{"id":"SGX"},"signature":""#; + assert!(SgxQlQveCollateral::separate_json_data_and_signature("tcbInfo", json).is_none()); + } +} diff --git a/bitacross-worker/core-primitives/attestation-handler/src/error.rs b/bitacross-worker/core-primitives/attestation-handler/src/error.rs new file mode 100644 index 0000000000..e681ce8c2a --- /dev/null +++ b/bitacross-worker/core-primitives/attestation-handler/src/error.rs @@ -0,0 +1,64 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use sgx_types::sgx_status_t; +use std::boxed::Box; + +pub type Result = core::result::Result; + +/// Parentchain block importer error. +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("SGX error, status: {0}")] + Sgx(sgx_status_t), + #[error("{0}")] + IoError(#[from] std::io::Error), + #[error("Crypto error: {0}")] + Crypto(itp_sgx_crypto::Error), + #[error("Error specifying time")] + Time, + #[error(transparent)] + Other(#[from] Box), +} + +impl From for Error { + fn from(sgx_status: sgx_status_t) -> Self { + Self::Sgx(sgx_status) + } +} + +impl From for Error { + fn from(error: itp_sgx_crypto::error::Error) -> Self { + Self::Crypto(error) + } +} + +impl From for sgx_status_t { + /// return sgx_status for top level enclave functions + fn from(error: Error) -> sgx_status_t { + match error { + Error::Sgx(status) => status, + _ => { + log::error!("Returning error {:?} as sgx unexpected.", error); + sgx_status_t::SGX_ERROR_UNEXPECTED + }, + } + } +} diff --git a/bitacross-worker/core-primitives/attestation-handler/src/lib.rs b/bitacross-worker/core-primitives/attestation-handler/src/lib.rs new file mode 100644 index 0000000000..c6763b3d9a --- /dev/null +++ b/bitacross-worker/core-primitives/attestation-handler/src/lib.rs @@ -0,0 +1,58 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +#[macro_use] +extern crate sgx_tstd as std; + +// re-export module to properly feature gate sgx and regular std environment +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod sgx_reexport_prelude { + pub use base64_sgx as base64; + pub use chrono_sgx as chrono; + pub use rustls_sgx as rustls; + pub use serde_json_sgx as serde_json; + pub use thiserror_sgx as thiserror; + pub use webpki_sgx as webpki; + pub use yasna_sgx as yasna; +} + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod attestation_handler; + +pub mod collateral; + +pub mod cert; + +pub mod error; + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub use attestation_handler::{AttestationHandler, IntelAttestationHandler, DEV_HOSTNAME}; +pub use collateral::SgxQlQveCollateral; + +pub use error::{Error, Result}; + +#[derive(Clone, Eq, PartialEq, Debug)] +pub enum RemoteAttestationType { + Epid, + Dcap, +} diff --git a/bitacross-worker/core-primitives/component-container/Cargo.toml b/bitacross-worker/core-primitives/component-container/Cargo.toml new file mode 100644 index 0000000000..cb5d3b5541 --- /dev/null +++ b/bitacross-worker/core-primitives/component-container/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "itp-component-container" +version = "0.8.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +# sgx dependencies +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } + +# sgx enabled external libraries +thiserror_sgx = { package = "thiserror", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3", optional = true } + +# std compatible external libraries (make sure these versions match with the sgx-enabled ones above) +thiserror = { version = "1.0", optional = true } + +[features] +default = ["std"] +std = [ + "thiserror", +] +sgx = [ + # sgx + "sgx_tstd", + "thiserror_sgx", +] diff --git a/bitacross-worker/core-primitives/component-container/src/atomic_container.rs b/bitacross-worker/core-primitives/component-container/src/atomic_container.rs new file mode 100644 index 0000000000..3f52ab291a --- /dev/null +++ b/bitacross-worker/core-primitives/component-container/src/atomic_container.rs @@ -0,0 +1,100 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Container for a generic item, held by an AtomicPtr. + +#[cfg(feature = "sgx")] +use std::sync::SgxMutex as Mutex; + +#[cfg(feature = "std")] +use std::sync::Mutex; + +use std::{ + default::Default, + sync::{ + atomic::{AtomicPtr, Ordering}, + Arc, + }, +}; + +/// Generic atomic container that holds an item in a container. +pub struct AtomicContainer { + atomic_ptr: AtomicPtr<()>, +} + +impl AtomicContainer { + pub const fn new() -> Self { + AtomicContainer { atomic_ptr: AtomicPtr::new(0 as *mut ()) } + } + + /// Store and item in the container. + pub fn store(&self, item: T) { + let pool_ptr = Arc::new(Mutex::::new(item)); + let ptr = Arc::into_raw(pool_ptr); + self.atomic_ptr.store(ptr as *mut (), Ordering::SeqCst); + } + + /// Load an item from the container, returning a mutex. + pub fn load(&self) -> Option<&Mutex> { + let ptr = self.atomic_ptr.load(Ordering::SeqCst) as *mut Mutex; + if ptr.is_null() { + None + } else { + Some(unsafe { &*ptr }) + } + } +} + +impl Default for AtomicContainer { + fn default() -> Self { + Self::new() + } +} + +#[cfg(test)] +pub mod tests { + + use super::*; + use std::{ + ops::Deref, + string::{String, ToString}, + vec::Vec, + }; + + #[derive(PartialEq, Eq, Clone, Debug)] + struct TestPayload { + name: String, + data: Vec, + } + + #[test] + pub fn store_and_load_works() { + let atomic_container = AtomicContainer::new(); + + let test_payload = TestPayload { + name: "Payload".to_string(), + data: Vec::from("lots_of_data_to_be_stored".as_bytes()), + }; + + atomic_container.store(test_payload.clone()); + + let retrieved_mutex = atomic_container.load::().unwrap().lock().unwrap(); + let retrieved_payload = retrieved_mutex.deref(); + + assert_eq!(&test_payload, retrieved_payload); + } +} diff --git a/bitacross-worker/core-primitives/component-container/src/component_container.rs b/bitacross-worker/core-primitives/component-container/src/component_container.rs new file mode 100644 index 0000000000..ec0a16d50e --- /dev/null +++ b/bitacross-worker/core-primitives/component-container/src/component_container.rs @@ -0,0 +1,100 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Generic component containers. + +#[cfg(feature = "sgx")] +use std::sync::SgxMutex as Mutex; + +#[cfg(feature = "std")] +use std::sync::Mutex; + +use crate::{ + atomic_container::AtomicContainer, + error::{Error, Result}, +}; +use std::{ + format, + marker::PhantomData, + string::{String, ToString}, + sync::Arc, +}; + +/// Trait to initialize a generic component. +pub trait ComponentInitializer { + type ComponentType; + + fn initialize(&self, component: Arc); +} + +/// Trait to retrieve a generic component. +pub trait ComponentGetter { + type ComponentType; + + /// Try to get a specific component, returns `None` if component has not been initialized. + fn get(&self) -> Result>; +} + +/// Workaround to make `new()` a `const fn`. +/// Is required in order to have the `ComponentContainer` in a static variable. +struct Invariant(T); + +/// Component container implementation. Can be used in a global static context. +pub struct ComponentContainer { + container: AtomicContainer, + component_name: &'static str, + _phantom: PhantomData>, +} + +impl ComponentContainer { + /// Create a new container instance. + /// + /// Has to be `const` in order to be used in a `static` context. + pub const fn new(component_name: &'static str) -> Self { + ComponentContainer { + container: AtomicContainer::new(), + component_name, + _phantom: PhantomData, + } + } +} + +impl ComponentInitializer for ComponentContainer { + type ComponentType = Component; + + fn initialize(&self, component: Arc) { + self.container.store(component) + } +} + +impl ToString for ComponentContainer { + fn to_string(&self) -> String { + format!("{} component", self.component_name) + } +} + +impl ComponentGetter for ComponentContainer { + type ComponentType = Component; + + fn get(&self) -> Result> { + let component_mutex: &Mutex> = self + .container + .load() + .ok_or_else(|| Error::ComponentNotInitialized(self.to_string()))?; + Ok(component_mutex.lock().expect("Lock poisoning").clone()) + } +} diff --git a/bitacross-worker/core-primitives/component-container/src/error.rs b/bitacross-worker/core-primitives/component-container/src/error.rs new file mode 100644 index 0000000000..9ca0ac0b20 --- /dev/null +++ b/bitacross-worker/core-primitives/component-container/src/error.rs @@ -0,0 +1,32 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use std::{boxed::Box, string::String}; + +pub type Result = core::result::Result; + +/// extrinsics factory error +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("Component is not initialized: {0}")] + ComponentNotInitialized(String), + #[error(transparent)] + Other(#[from] Box), +} diff --git a/bitacross-worker/core-primitives/component-container/src/lib.rs b/bitacross-worker/core-primitives/component-container/src/lib.rs new file mode 100644 index 0000000000..9c684e4361 --- /dev/null +++ b/bitacross-worker/core-primitives/component-container/src/lib.rs @@ -0,0 +1,36 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +// re-export module to properly feature gate sgx and regular std environment +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod sgx_reexport_prelude { + pub use thiserror_sgx as thiserror; +} + +mod atomic_container; +pub mod component_container; +pub mod error; + +pub use component_container::*; diff --git a/bitacross-worker/core-primitives/enclave-api/Cargo.toml b/bitacross-worker/core-primitives/enclave-api/Cargo.toml new file mode 100644 index 0000000000..c9dfaa9dff --- /dev/null +++ b/bitacross-worker/core-primitives/enclave-api/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "itp-enclave-api" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +hex = "0.4" +log = "0.4" +serde_json = "1.0" +thiserror = "1.0.25" + +sgx_crypto_helper = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } +sgx_types = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } +sgx_urts = { optional = true, branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } + +frame-support = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-runtime = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +itc-parentchain = { path = "../../core/parentchain/parentchain-crate" } +itp-enclave-api-ffi = { path = "ffi" } +itp-settings = { path = "../settings" } +itp-storage = { path = "../storage" } +itp-types = { path = "../types" } + +# litentry +teerex-primitives = { path = "../../../primitives/teerex", default-features = false } + +[features] +default = [] +implement-ffi = [ + "sgx_urts", + "itp-enclave-api-ffi/link-sgx-libs", +] diff --git a/bitacross-worker/core-primitives/enclave-api/build.rs b/bitacross-worker/core-primitives/enclave-api/build.rs new file mode 100644 index 0000000000..1c20ea4c84 --- /dev/null +++ b/bitacross-worker/core-primitives/enclave-api/build.rs @@ -0,0 +1,24 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +fn main() { + // If the linker failed to find libsgx_dcap_ql.so, please make sure that + // (1) libsgx-dcap-ql is installed + // (2) libsgx_dcap_ql.so exists. typicall at /usr/lib/x86_64-linux-gnu + // if libsgx_dcap_ql.so.1 is there, but no libsgx-dcap_ql, + // just create a symlink by + // ln -s libsgx_dcap_ql.so.1 libsgx_dcap_ql.so + println!("cargo:rustc-link-lib=dylib=sgx_dcap_ql"); + println!("cargo:rustc-link-lib=dylib=sgx_dcap_quoteverify"); + println!("cargo:rustc-link-lib=dylib=dcap_quoteprov"); +} diff --git a/bitacross-worker/core-primitives/enclave-api/ffi/Cargo.toml b/bitacross-worker/core-primitives/enclave-api/ffi/Cargo.toml new file mode 100644 index 0000000000..4ce7be0e66 --- /dev/null +++ b/bitacross-worker/core-primitives/enclave-api/ffi/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "itp-enclave-api-ffi" +version = "0.9.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +sgx_types = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } + +[features] +# necessary to run cargo tests without any preliminaries +# See: https://github.com/rust-lang/cargo/issues/2549 +link-sgx-libs = [] diff --git a/bitacross-worker/core-primitives/enclave-api/ffi/build.rs b/bitacross-worker/core-primitives/enclave-api/ffi/build.rs new file mode 100644 index 0000000000..766abb3eb4 --- /dev/null +++ b/bitacross-worker/core-primitives/enclave-api/ffi/build.rs @@ -0,0 +1,44 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +fn main() { + if cfg!(feature = "link-sgx-libs") { + use std::env; + + let sdk_dir = env::var("SGX_SDK").unwrap_or_else(|_| "/opt/intel/sgxsdk".to_string()); + let is_sim = env::var("SGX_MODE").unwrap_or_else(|_| "HW".to_string()); + + // NOTE: if the crate is a workspace member rustc-paths are relative from the root directory + println!("cargo:rustc-link-search=native=./lib"); + println!("cargo:rustc-link-lib=static=Enclave_u"); + + println!("cargo:rustc-link-search=native={}/lib64", sdk_dir); + println!("cargo:rustc-link-lib=static=sgx_uprotected_fs"); + match is_sim.as_ref() { + "SW" => { + println!("cargo:rustc-link-lib=dylib=sgx_urts_sim"); + println!("cargo:rustc-link-lib=dylib=sgx_uae_service_sim"); + }, + _ => { + // HW by default + println!("cargo:rustc-link-lib=dylib=sgx_urts"); + println!("cargo:rustc-link-lib=dylib=sgx_uae_service"); + }, + } + } +} diff --git a/bitacross-worker/core-primitives/enclave-api/ffi/src/lib.rs b/bitacross-worker/core-primitives/enclave-api/ffi/src/lib.rs new file mode 100644 index 0000000000..2dbb8fb016 --- /dev/null +++ b/bitacross-worker/core-primitives/enclave-api/ffi/src/lib.rs @@ -0,0 +1,279 @@ +///! FFI's that call into the enclave. These functions need to be added to the +/// enclave edl file and be implemented within the enclave. +use sgx_types::{ + c_int, sgx_enclave_id_t, sgx_ql_qve_collateral_t, sgx_quote_sign_type_t, sgx_status_t, + sgx_target_info_t, +}; + +extern "C" { + + pub fn generate_dcap_ra_extrinsic_from_quote( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + w_url: *const u8, + w_url_size: u32, + quote: *const u8, + quote_size: u32, + unchecked_extrinsic: *mut u8, + unchecked_extrinsic_max_size: u32, + unchecked_extrinsic_size: *mut u32, + ) -> sgx_status_t; + + pub fn init( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + mu_ra_addr: *const u8, + mu_ra_addr_size: u32, + untrusted_worker_addr: *const u8, + untrusted_worker_addr_size: u32, + encoded_base_dir_str: *const u8, + encoded_base_dir_size: u32, + ) -> sgx_status_t; + + pub fn init_enclave_sidechain_components( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + fail_mode: *const u8, + fail_mode_size: u32, + fail_at: *const u8, + fail_at_size: u32, + ) -> sgx_status_t; + + pub fn init_direct_invocation_server( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + server_addr: *const u8, + server_addr_size: u32, + ) -> sgx_status_t; + + pub fn init_parentchain_components( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + params: *const u8, + params_size: usize, + latest_header: *mut u8, + latest_header_size: usize, + ) -> sgx_status_t; + + pub fn init_shard( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + shard: *const u8, + shard_size: u32, + ) -> sgx_status_t; + + pub fn init_proxied_shard_vault( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + shard: *const u8, + shard_size: u32, + parentchain_id: *const u8, + parentchain_id_size: u32, + ) -> sgx_status_t; + + pub fn execute_trusted_calls(eid: sgx_enclave_id_t, retval: *mut sgx_status_t) -> sgx_status_t; + + pub fn sync_parentchain( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + blocks: *const u8, + blocks_size: usize, + events: *const u8, + events_size: usize, + events_proofs: *const u8, + events_proofs_size: usize, + parentchain_id: *const u8, + parentchain_id_size: u32, + is_syncing: c_int, + ) -> sgx_status_t; + + pub fn set_nonce( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + nonce: *const u32, + parentchain_id: *const u8, + parentchain_id_size: u32, + ) -> sgx_status_t; + + pub fn set_node_metadata( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + node_metadata: *const u8, + node_metadata_size: u32, + parentchain_id: *const u8, + parentchain_id_size: u32, + ) -> sgx_status_t; + + pub fn get_rsa_encryption_pubkey( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + pubkey: *mut u8, + pubkey_size: u32, + ) -> sgx_status_t; + + pub fn get_ecc_signing_pubkey( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + pubkey: *mut u8, + pubkey_size: u32, + ) -> sgx_status_t; + + pub fn get_ecc_vault_pubkey( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + shard: *const u8, + shard_size: u32, + pubkey: *mut u8, + pubkey_size: u32, + ) -> sgx_status_t; + + pub fn get_mrenclave( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + mrenclave: *mut u8, + mrenclave_size: u32, + ) -> sgx_status_t; + + pub fn generate_ias_ra_extrinsic( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + w_url: *const u8, + w_url_size: u32, + unchecked_extrinsic: *mut u8, + unchecked_extrinsic_max_size: u32, + unchecked_extrinsic_size: *mut u32, + skip_ra: c_int, + ) -> sgx_status_t; + + pub fn generate_dcap_ra_extrinsic( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + w_url: *const u8, + w_url_size: u32, + unchecked_extrinsic: *mut u8, + unchecked_extrinsic_max_size: u32, + unchecked_extrinsic_size: *mut u32, + skip_ra: c_int, + quoting_enclave_target_info: Option<&sgx_target_info_t>, + quote_size: Option<&u32>, + ) -> sgx_status_t; + + pub fn generate_dcap_ra_quote( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + skip_ra: c_int, + quoting_enclave_target_info: &sgx_target_info_t, + quote_size: u32, + dcap_quote_p: *mut u8, + dcap_quote_size: u32, + ) -> sgx_status_t; + + pub fn generate_register_quoting_enclave_extrinsic( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + collateral: *const sgx_ql_qve_collateral_t, + unchecked_extrinsic: *mut u8, + unchecked_extrinsic_max_size: u32, + unchecked_extrinsic_size: *mut u32, + ) -> sgx_status_t; + + pub fn generate_register_tcb_info_extrinsic( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + collateral: *const sgx_ql_qve_collateral_t, + unchecked_extrinsic: *mut u8, + unchecked_extrinsic_max_size: u32, + unchecked_extrinsic_size: *mut u32, + ) -> sgx_status_t; + + pub fn dump_ias_ra_cert_to_disk( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + ) -> sgx_status_t; + + pub fn dump_dcap_ra_cert_to_disk( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + quoting_enclave_target_info: &sgx_target_info_t, + quote_size: u32, + ) -> sgx_status_t; + + pub fn dump_dcap_collateral_to_disk( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + collateral: *const sgx_ql_qve_collateral_t, + ) -> sgx_status_t; + + pub fn test_main_entrance(eid: sgx_enclave_id_t, retval: *mut sgx_status_t) -> sgx_status_t; + + pub fn call_rpc_methods( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + request: *const u8, + request_len: u32, + response: *mut u8, + response_len: u32, + ) -> sgx_status_t; + + pub fn update_market_data_xt( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + crypto_currency: *const u8, + crypto_currency_size: u32, + fiat_currency: *const u8, + fiat_currency_size: u32, + unchecked_extrinsic: *mut u8, + unchecked_extrinsic_max_size: u32, + unchecked_extrinsic_size: *mut u32, + ) -> sgx_status_t; + + pub fn update_weather_data_xt( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + weather_info_longitude: *const u8, + weather_info_longitude_size: u32, + weather_info_latitude: *const u8, + weather_info_latitude_size: u32, + unchecked_extrinsic: *mut u8, + unchecked_extrinsic_max_size: u32, + unchecked_extrinsic_size: *mut u32, + ) -> sgx_status_t; + + pub fn run_state_provisioning_server( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + socket_fd: c_int, + sign_type: sgx_quote_sign_type_t, + quoting_enclave_target_info: Option<&sgx_target_info_t>, + quote_size: Option<&u32>, + skip_ra: c_int, + ) -> sgx_status_t; + + pub fn request_state_provisioning( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + socket_fd: c_int, + sign_type: sgx_quote_sign_type_t, + quoting_enclave_target_info: Option<&sgx_target_info_t>, + quote_size: Option<&u32>, + shard: *const u8, + shard_size: u32, + skip_ra: c_int, + ) -> sgx_status_t; + + // litentry + pub fn migrate_shard( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + old_shard: *const u8, + new_shard: *const u8, + shard_size: u32, + ) -> sgx_status_t; + + pub fn ignore_parentchain_block_import_validation_until( + eid: sgx_enclave_id_t, + retval: *mut sgx_status_t, + until: *const u32, + ) -> sgx_status_t; + +} diff --git a/bitacross-worker/core-primitives/enclave-api/src/direct_request.rs b/bitacross-worker/core-primitives/enclave-api/src/direct_request.rs new file mode 100644 index 0000000000..f3fff3388a --- /dev/null +++ b/bitacross-worker/core-primitives/enclave-api/src/direct_request.rs @@ -0,0 +1,58 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::EnclaveResult; + +pub trait DirectRequest: Send + Sync + 'static { + // Todo: Vec shall be replaced by D: Decode, E: Encode but this is currently + // not compatible with the direct_api_server... + fn rpc(&self, request: Vec) -> EnclaveResult>; +} + +#[cfg(feature = "implement-ffi")] +mod impl_ffi { + use super::DirectRequest; + use crate::{error::Error, Enclave, EnclaveResult}; + use frame_support::ensure; + use itp_enclave_api_ffi as ffi; + use sgx_types::sgx_status_t; + + impl DirectRequest for Enclave { + fn rpc(&self, request: Vec) -> EnclaveResult> { + let mut retval = sgx_status_t::SGX_SUCCESS; + let response_len = 8192; + let mut response: Vec = vec![0u8; response_len as usize]; + + let res = unsafe { + ffi::call_rpc_methods( + self.eid, + &mut retval, + request.as_ptr(), + request.len() as u32, + response.as_mut_ptr(), + response_len, + ) + }; + + ensure!(res == sgx_status_t::SGX_SUCCESS, Error::Sgx(res)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(response) + } + } +} diff --git a/bitacross-worker/core-primitives/enclave-api/src/enclave_base.rs b/bitacross-worker/core-primitives/enclave-api/src/enclave_base.rs new file mode 100644 index 0000000000..4e79a6f902 --- /dev/null +++ b/bitacross-worker/core-primitives/enclave-api/src/enclave_base.rs @@ -0,0 +1,409 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::EnclaveResult; +use codec::Decode; +use core::fmt::Debug; +use itc_parentchain::primitives::{ParentchainId, ParentchainInitParams}; +use itp_types::ShardIdentifier; +use sgx_crypto_helper::rsa3072::Rsa3072PubKey; +use sp_core::ed25519; +use teerex_primitives::EnclaveFingerprint; + +/// Trait for base/common Enclave API functions +pub trait EnclaveBase: Send + Sync + 'static { + /// Initialize the enclave (needs to be called once at application startup). + fn init( + &self, + mu_ra_addr: &str, + untrusted_worker_addr: &str, + base_dir: &str, + ) -> EnclaveResult<()>; + + /// Initialize the enclave sidechain components. + fn init_enclave_sidechain_components( + &self, + fail_mode: Option, + fail_at: u64, + ) -> EnclaveResult<()>; + + /// Initialize the direct invocation RPC server. + fn init_direct_invocation_server(&self, rpc_server_addr: String) -> EnclaveResult<()>; + + /// Initialize the light client (needs to be called once at application startup). + fn init_parentchain_components( + &self, + params: ParentchainInitParams, + ) -> EnclaveResult
; + + /// Initialize a new shard. + fn init_shard(&self, shard: Vec) -> EnclaveResult<()>; + + /// Initialize a new shard vault account and register enclave signer as its proxy. + fn init_proxied_shard_vault( + &self, + shard: &ShardIdentifier, + parentchain_id: &ParentchainId, + ) -> EnclaveResult<()>; + + fn set_nonce(&self, nonce: u32, parentchain_id: ParentchainId) -> EnclaveResult<()>; + + fn set_node_metadata( + &self, + metadata: Vec, + parentchain_id: ParentchainId, + ) -> EnclaveResult<()>; + + fn get_rsa_shielding_pubkey(&self) -> EnclaveResult; + + fn get_ecc_signing_pubkey(&self) -> EnclaveResult; + + /// retrieve vault account from shard state + fn get_ecc_vault_pubkey(&self, shard: &ShardIdentifier) -> EnclaveResult; + + fn get_fingerprint(&self) -> EnclaveResult; + + // litentry + fn migrate_shard(&self, old_shard: Vec, new_shard: Vec) -> EnclaveResult<()>; +} + +/// EnclaveApi implementation for Enclave struct +#[cfg(feature = "implement-ffi")] +mod impl_ffi { + use super::EnclaveBase; + use crate::{error::Error, Enclave, EnclaveResult}; + use codec::{Decode, Encode}; + use core::fmt::Debug; + use frame_support::ensure; + use itc_parentchain::primitives::{ParentchainId, ParentchainInitParams}; + use itp_enclave_api_ffi as ffi; + use itp_settings::worker::{ + HEADER_MAX_SIZE, MR_ENCLAVE_SIZE, SHIELDING_KEY_SIZE, SIGNING_KEY_SIZE, + }; + use itp_types::ShardIdentifier; + use log::*; + use sgx_crypto_helper::rsa3072::Rsa3072PubKey; + use sgx_types::*; + use sp_core::ed25519; + use teerex_primitives::EnclaveFingerprint; + + impl EnclaveBase for Enclave { + fn init( + &self, + mu_ra_addr: &str, + untrusted_worker_addr: &str, + base_dir: &str, + ) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + + let encoded_mu_ra_addr = mu_ra_addr.encode(); + let encoded_untrusted_worker_addr = untrusted_worker_addr.encode(); + let encoded_base_dir = base_dir.encode(); + + let result = unsafe { + ffi::init( + self.eid, + &mut retval, + encoded_mu_ra_addr.as_ptr(), + encoded_mu_ra_addr.len() as u32, + encoded_untrusted_worker_addr.as_ptr(), + encoded_untrusted_worker_addr.len() as u32, + encoded_base_dir.as_ptr(), + encoded_base_dir.len() as u32, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(()) + } + + fn init_enclave_sidechain_components( + &self, + fail_mode: Option, + fail_at: u64, + ) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + let encoded_fail_mode = fail_mode.encode(); + let encoded_fail_at = fail_at.encode(); + + let result = unsafe { + ffi::init_enclave_sidechain_components( + self.eid, + &mut retval, + encoded_fail_mode.as_ptr(), + encoded_fail_mode.len() as u32, + encoded_fail_at.as_ptr(), + encoded_fail_at.len() as u32, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(()) + } + + fn init_direct_invocation_server(&self, rpc_server_addr: String) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + + let encoded_rpc_server_addr = rpc_server_addr.encode(); + + let result = unsafe { + ffi::init_direct_invocation_server( + self.eid, + &mut retval, + encoded_rpc_server_addr.as_ptr(), + encoded_rpc_server_addr.len() as u32, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(()) + } + + fn init_parentchain_components( + &self, + params: ParentchainInitParams, + ) -> EnclaveResult
{ + let latest_header_encoded = init_parentchain_components_ffi(self.eid, params.encode())?; + + let latest = Header::decode(&mut latest_header_encoded.as_slice())?; + info!("Latest Header {:?}", latest); + + Ok(latest) + } + + fn init_shard(&self, shard: Vec) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + + let result = unsafe { + ffi::init_shard(self.eid, &mut retval, shard.as_ptr(), shard.len() as u32) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(()) + } + + fn init_proxied_shard_vault( + &self, + shard: &ShardIdentifier, + parentchain_id: &ParentchainId, + ) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + let parentchain_id_enc = parentchain_id.encode(); + let shard_bytes = shard.encode(); + let result = unsafe { + ffi::init_proxied_shard_vault( + self.eid, + &mut retval, + shard_bytes.as_ptr(), + shard_bytes.len() as u32, + parentchain_id_enc.as_ptr(), + parentchain_id_enc.len() as u32, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(()) + } + + fn set_nonce(&self, nonce: u32, parentchain_id: ParentchainId) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + + let parentchain_id_enc = parentchain_id.encode(); + + let result = unsafe { + ffi::set_nonce( + self.eid, + &mut retval, + &nonce, + parentchain_id_enc.as_ptr(), + parentchain_id_enc.len() as u32, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(()) + } + + fn set_node_metadata( + &self, + metadata: Vec, + parentchain_id: ParentchainId, + ) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + + let parentchain_id_enc = parentchain_id.encode(); + + let result = unsafe { + ffi::set_node_metadata( + self.eid, + &mut retval, + metadata.as_ptr(), + metadata.len() as u32, + parentchain_id_enc.as_ptr(), + parentchain_id_enc.len() as u32, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(()) + } + + fn get_rsa_shielding_pubkey(&self) -> EnclaveResult { + let mut retval = sgx_status_t::SGX_SUCCESS; + + let pubkey_size = SHIELDING_KEY_SIZE; + let mut pubkey = vec![0u8; pubkey_size]; + + let result = unsafe { + ffi::get_rsa_encryption_pubkey( + self.eid, + &mut retval, + pubkey.as_mut_ptr(), + pubkey.len() as u32, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + let rsa_pubkey: Rsa3072PubKey = + serde_json::from_slice(pubkey.as_slice()).expect("Invalid public key"); + debug!("got RSA pubkey {:?}", rsa_pubkey); + Ok(rsa_pubkey) + } + + fn get_ecc_signing_pubkey(&self) -> EnclaveResult { + let mut retval = sgx_status_t::SGX_SUCCESS; + let mut pubkey = [0u8; SIGNING_KEY_SIZE]; + + let result = unsafe { + ffi::get_ecc_signing_pubkey( + self.eid, + &mut retval, + pubkey.as_mut_ptr(), + pubkey.len() as u32, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(ed25519::Public::from_raw(pubkey)) + } + + fn get_ecc_vault_pubkey(&self, shard: &ShardIdentifier) -> EnclaveResult { + let mut retval = sgx_status_t::SGX_SUCCESS; + let mut pubkey = [0u8; SIGNING_KEY_SIZE]; + let shard_bytes = shard.encode(); + + let result = unsafe { + ffi::get_ecc_vault_pubkey( + self.eid, + &mut retval, + shard_bytes.as_ptr(), + shard_bytes.len() as u32, + pubkey.as_mut_ptr(), + pubkey.len() as u32, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(ed25519::Public::from_raw(pubkey)) + } + + fn get_fingerprint(&self) -> EnclaveResult { + let mut retval = sgx_status_t::SGX_SUCCESS; + let mut mr_enclave = [0u8; MR_ENCLAVE_SIZE]; + + let result = unsafe { + ffi::get_mrenclave( + self.eid, + &mut retval, + mr_enclave.as_mut_ptr(), + mr_enclave.len() as u32, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(mr_enclave.into()) + } + + fn migrate_shard(&self, old_shard: Vec, new_shard: Vec) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + + let result = unsafe { + ffi::migrate_shard( + self.eid, + &mut retval, + old_shard.as_ptr(), + new_shard.as_ptr(), + old_shard.len() as u32, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(()) + } + } + + fn init_parentchain_components_ffi( + enclave_id: sgx_enclave_id_t, + params: Vec, + ) -> EnclaveResult> { + let mut retval = sgx_status_t::SGX_SUCCESS; + + let latest_header_size = HEADER_MAX_SIZE; + let mut latest_header = vec![0u8; latest_header_size]; + + let result = unsafe { + ffi::init_parentchain_components( + enclave_id, + &mut retval, + params.as_ptr(), + params.len(), + latest_header.as_mut_ptr(), + latest_header.len(), + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(latest_header) + } +} diff --git a/bitacross-worker/core-primitives/enclave-api/src/enclave_test.rs b/bitacross-worker/core-primitives/enclave-api/src/enclave_test.rs new file mode 100644 index 0000000000..aaf3a8e97d --- /dev/null +++ b/bitacross-worker/core-primitives/enclave-api/src/enclave_test.rs @@ -0,0 +1,48 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::EnclaveResult; + +pub trait EnclaveTest: Send + Sync + 'static { + fn test_main_entrance(&self) -> EnclaveResult<()>; +} + +#[cfg(feature = "implement-ffi")] +mod impl_ffi { + use super::EnclaveTest; + use crate::{error::Error, Enclave, EnclaveResult}; + use frame_support::ensure; + use itp_enclave_api_ffi as ffi; + use log::*; + use sgx_types::sgx_status_t; + + impl EnclaveTest for Enclave { + fn test_main_entrance(&self) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + + let result = unsafe { ffi::test_main_entrance(self.eid, &mut retval) }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + debug!("[+] successfully executed enclave test main"); + + Ok(()) + } + } +} diff --git a/bitacross-worker/core-primitives/enclave-api/src/error.rs b/bitacross-worker/core-primitives/enclave-api/src/error.rs new file mode 100644 index 0000000000..d510c56db4 --- /dev/null +++ b/bitacross-worker/core-primitives/enclave-api/src/error.rs @@ -0,0 +1,14 @@ +use codec::Error as CodecError; +use sgx_types::{sgx_quote3_error_t, sgx_status_t}; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("{0}")] + Codec(#[from] CodecError), + #[error("Enclave Error: {0}")] + Sgx(sgx_status_t), + #[error("Enclave Quote Error: {0}")] + SgxQuote(sgx_quote3_error_t), + #[error("Error, other: {0}")] + Other(Box), +} diff --git a/bitacross-worker/core-primitives/enclave-api/src/lib.rs b/bitacross-worker/core-primitives/enclave-api/src/lib.rs new file mode 100644 index 0000000000..38c810624f --- /dev/null +++ b/bitacross-worker/core-primitives/enclave-api/src/lib.rs @@ -0,0 +1,49 @@ +//! Some definitions and traits that facilitate interaction with the enclave. +//! +//! This serves as a proof of concept on how we could design the interface between the worker and +//! the enclave. +//! +//! Design principle here should be to keep the traits as slim as possible - because then the +//! worker can also define slim interfaces with less demanding trait bounds. +//! +//! This can further be simplified once https://github.com/integritee-network/worker/issues/254 +//! is implemented. Then we can replace the several ffi:: and the boilerplate code +//! around it with a simple `fn ecall(call: CallEnum) -> Result`, which wraps one single +//! ffi function. + +use crate::error::Error; + +pub mod direct_request; +pub mod enclave_base; +pub mod enclave_test; +pub mod error; +pub mod remote_attestation; +pub mod sidechain; +pub mod teeracle_api; +pub mod utils; + +#[cfg(feature = "implement-ffi")] +pub use sgx_urts::SgxEnclave; + +#[cfg(feature = "implement-ffi")] +use sgx_types::sgx_enclave_id_t; + +pub type EnclaveResult = Result; + +#[cfg(feature = "implement-ffi")] +#[derive(Clone, Debug, Default)] +pub struct Enclave { + eid: sgx_enclave_id_t, + sgx_enclave: SgxEnclave, +} + +#[cfg(feature = "implement-ffi")] +impl Enclave { + pub fn new(sgx_enclave: SgxEnclave) -> Self { + Enclave { eid: sgx_enclave.geteid(), sgx_enclave } + } + + pub fn destroy(self) { + self.sgx_enclave.destroy() + } +} diff --git a/bitacross-worker/core-primitives/enclave-api/src/remote_attestation.rs b/bitacross-worker/core-primitives/enclave-api/src/remote_attestation.rs new file mode 100644 index 0000000000..9aa32cb631 --- /dev/null +++ b/bitacross-worker/core-primitives/enclave-api/src/remote_attestation.rs @@ -0,0 +1,857 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::EnclaveResult; +use itp_types::ShardIdentifier; +use sgx_types::*; +use teerex_primitives::Fmspc; + +/// Struct that unites all relevant data reported by the QVE +pub struct QveReport { + pub supplemental_data: Vec, + pub qve_report_info_return_value: sgx_ql_qe_report_info_t, + pub quote_verification_result: sgx_ql_qv_result_t, + pub collateral_expiration_status: u32, +} + +/// general remote attestation methods +pub trait RemoteAttestation { + fn generate_ias_ra_extrinsic(&self, w_url: &str, skip_ra: bool) -> EnclaveResult>; + + fn generate_dcap_ra_extrinsic(&self, w_url: &str, skip_ra: bool) -> EnclaveResult>; + fn generate_dcap_ra_extrinsic_from_quote( + &self, + url: String, + quote: &[u8], + ) -> EnclaveResult>; + fn generate_dcap_ra_quote(&self, skip_ra: bool) -> EnclaveResult>; + + fn generate_register_quoting_enclave_extrinsic(&self, fmspc: Fmspc) -> EnclaveResult>; + + fn generate_register_tcb_info_extrinsic(&self, fmspc: Fmspc) -> EnclaveResult>; + + fn dump_ias_ra_cert_to_disk(&self) -> EnclaveResult<()>; + + fn dump_dcap_ra_cert_to_disk(&self) -> EnclaveResult<()>; + + fn dump_dcap_collateral_to_disk(&self, fmspc: Fmspc) -> EnclaveResult<()>; + + fn set_ql_qe_enclave_paths(&self) -> EnclaveResult<()>; + + fn set_sgx_qpl_logging(&self) -> EnclaveResult<()>; + + fn qe_get_target_info(&self) -> EnclaveResult; + + fn qe_get_quote_size(&self) -> EnclaveResult; + + fn get_dcap_collateral(&self, fmspc: Fmspc) -> EnclaveResult<*const sgx_ql_qve_collateral_t>; +} + +/// call-backs that are made from inside the enclave (using o-call), to e-calls again inside the enclave +pub trait RemoteAttestationCallBacks { + fn init_quote(&self) -> EnclaveResult<(sgx_target_info_t, sgx_epid_group_id_t)>; + + fn calc_quote_size(&self, revocation_list: Vec) -> EnclaveResult; + + fn get_quote( + &self, + revocation_list: Vec, + report: sgx_report_t, + quote_type: sgx_quote_sign_type_t, + spid: sgx_spid_t, + quote_nonce: sgx_quote_nonce_t, + quote_length: u32, + ) -> EnclaveResult<(sgx_report_t, Vec)>; + + fn get_dcap_quote(&self, report: sgx_report_t, quote_size: u32) -> EnclaveResult>; + + fn get_qve_report_on_quote( + &self, + quote: Vec, + current_time: i64, + quote_collateral: &sgx_ql_qve_collateral_t, + qve_report_info: sgx_ql_qe_report_info_t, + supplemental_data_size: u32, + ) -> EnclaveResult; + + fn get_update_info( + &self, + platform_blob: sgx_platform_info_t, + enclave_trusted: i32, + ) -> EnclaveResult; +} + +/// TLS remote attestations methods +pub trait TlsRemoteAttestation { + fn run_state_provisioning_server( + &self, + socket_fd: c_int, + sign_type: sgx_quote_sign_type_t, + quoting_enclave_target_info: Option<&sgx_target_info_t>, + quote_size: Option<&u32>, + skip_ra: bool, + ) -> EnclaveResult<()>; + + fn request_state_provisioning( + &self, + socket_fd: c_int, + sign_type: sgx_quote_sign_type_t, + quoting_enclave_target_info: Option<&sgx_target_info_t>, + quote_size: Option<&u32>, + shard: &ShardIdentifier, + skip_ra: bool, + ) -> EnclaveResult<()>; +} + +#[cfg(feature = "implement-ffi")] +mod impl_ffi { + use super::{QveReport, RemoteAttestation, RemoteAttestationCallBacks, TlsRemoteAttestation}; + use crate::{error::Error, utils, Enclave, EnclaveResult}; + use codec::Encode; + use frame_support::ensure; + use itp_enclave_api_ffi as ffi; + use itp_settings::worker::EXTRINSIC_MAX_SIZE; + use itp_types::ShardIdentifier; + use log::*; + use sgx_types::*; + use teerex_primitives::Fmspc; + + const OS_SYSTEM_PATH: &str = "/usr/lib/x86_64-linux-gnu/"; + const C_STRING_ENDING: &str = "\0"; + const PCE_ENCLAVE: &str = "libsgx_pce.signed.so.1"; + const QE3_ENCLAVE: &str = "libsgx_qe3.signed.so.1"; + const ID_ENCLAVE: &str = "libsgx_id_enclave.signed.so.1"; + const LIBDCAP_QUOTEPROV: &str = "libdcap_quoteprov.so.1"; + const QVE_ENCLAVE: &str = "libsgx_qve.signed.so.1"; + + impl RemoteAttestation for Enclave { + fn generate_ias_ra_extrinsic(&self, w_url: &str, skip_ra: bool) -> EnclaveResult> { + let mut retval = sgx_status_t::SGX_SUCCESS; + + let mut unchecked_extrinsic: Vec = vec![0u8; EXTRINSIC_MAX_SIZE]; + let mut unchecked_extrinsic_size: u32 = 0; + + trace!("Generating ias_ra_extrinsic with URL: {}", w_url); + + let url = w_url.encode(); + + let result = unsafe { + ffi::generate_ias_ra_extrinsic( + self.eid, + &mut retval, + url.as_ptr(), + url.len() as u32, + unchecked_extrinsic.as_mut_ptr(), + unchecked_extrinsic.len() as u32, + &mut unchecked_extrinsic_size as *mut u32, + skip_ra.into(), + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(Vec::from(&unchecked_extrinsic[..unchecked_extrinsic_size as usize])) + } + fn generate_dcap_ra_extrinsic_from_quote( + &self, + url: String, + quote: &[u8], + ) -> EnclaveResult> { + let mut retval = sgx_status_t::SGX_SUCCESS; + let mut unchecked_extrinsic: Vec = vec![0u8; EXTRINSIC_MAX_SIZE]; + let mut unchecked_extrinsic_size: u32 = 0; + let url = url.encode(); + + let result = unsafe { + ffi::generate_dcap_ra_extrinsic_from_quote( + self.eid, + &mut retval, + url.as_ptr(), + url.len() as u32, + quote.as_ptr(), + quote.len() as u32, + unchecked_extrinsic.as_mut_ptr(), + unchecked_extrinsic.len() as u32, + &mut unchecked_extrinsic_size as *mut u32, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(Vec::from(&unchecked_extrinsic[..unchecked_extrinsic_size as usize])) + } + + fn generate_dcap_ra_quote(&self, skip_ra: bool) -> EnclaveResult> { + let mut retval = sgx_status_t::SGX_SUCCESS; + let quoting_enclave_target_info = self.qe_get_target_info()?; + let quote_size = self.qe_get_quote_size()?; + + let mut dcap_quote_vec: Vec = vec![0; quote_size as usize]; + let (dcap_quote_p, dcap_quote_size) = + (dcap_quote_vec.as_mut_ptr(), dcap_quote_vec.len() as u32); + + let result = unsafe { + ffi::generate_dcap_ra_quote( + self.eid, + &mut retval, + skip_ra.into(), + "ing_enclave_target_info, + quote_size, + dcap_quote_p, + dcap_quote_size, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + unsafe { + trace!("Generating DCAP RA Quote: {}", *dcap_quote_p); + } + + Ok(dcap_quote_vec) + } + + fn generate_dcap_ra_extrinsic(&self, w_url: &str, skip_ra: bool) -> EnclaveResult> { + let mut retval = sgx_status_t::SGX_SUCCESS; + + self.set_ql_qe_enclave_paths()?; + let quoting_enclave_target_info = if !skip_ra { + match self.qe_get_target_info() { + Ok(target_info) => Some(target_info), + Err(e) => return Err(e), + } + } else { + None + }; + let quote_size = if !skip_ra { + match self.qe_get_quote_size() { + Ok(quote_size) => Some(quote_size), + Err(e) => return Err(e), + } + } else { + None + }; + info!("Retrieved quote size of {:?}", quote_size); + + trace!("Generating dcap_ra_extrinsic with URL: {}", w_url); + + let mut unchecked_extrinsic: Vec = vec![0u8; EXTRINSIC_MAX_SIZE]; + let mut unchecked_extrinsic_size: u32 = 0; + let url = w_url.encode(); + + let result = unsafe { + ffi::generate_dcap_ra_extrinsic( + self.eid, + &mut retval, + url.as_ptr(), + url.len() as u32, + unchecked_extrinsic.as_mut_ptr(), + unchecked_extrinsic.len() as u32, + &mut unchecked_extrinsic_size as *mut u32, + skip_ra.into(), + quoting_enclave_target_info.as_ref(), + quote_size.as_ref(), + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(Vec::from(&unchecked_extrinsic[..unchecked_extrinsic_size as usize])) + } + + fn generate_register_quoting_enclave_extrinsic( + &self, + fmspc: Fmspc, + ) -> EnclaveResult> { + let mut retval = sgx_status_t::SGX_SUCCESS; + let mut unchecked_extrinsic: Vec = vec![0u8; EXTRINSIC_MAX_SIZE]; + let mut unchecked_extrinsic_size: u32 = 0; + + trace!("Generating register quoting enclave"); + + let collateral_ptr = self.get_dcap_collateral(fmspc)?; + + let result = unsafe { + ffi::generate_register_quoting_enclave_extrinsic( + self.eid, + &mut retval, + collateral_ptr, + unchecked_extrinsic.as_mut_ptr(), + unchecked_extrinsic.len() as u32, + &mut unchecked_extrinsic_size as *mut u32, + ) + }; + let free_status = unsafe { sgx_ql_free_quote_verification_collateral(collateral_ptr) }; + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + ensure!( + free_status == sgx_quote3_error_t::SGX_QL_SUCCESS, + Error::SgxQuote(free_status) + ); + + Ok(Vec::from(&unchecked_extrinsic[..unchecked_extrinsic_size as usize])) + } + + fn generate_register_tcb_info_extrinsic(&self, fmspc: Fmspc) -> EnclaveResult> { + let mut retval = sgx_status_t::SGX_SUCCESS; + let mut unchecked_extrinsic: Vec = vec![0u8; EXTRINSIC_MAX_SIZE]; + let mut unchecked_extrinsic_size: u32 = 0; + + trace!("Generating tcb_info registration"); + + let collateral_ptr = self.get_dcap_collateral(fmspc)?; + + let result = unsafe { + ffi::generate_register_tcb_info_extrinsic( + self.eid, + &mut retval, + collateral_ptr, + unchecked_extrinsic.as_mut_ptr(), + unchecked_extrinsic.len() as u32, + &mut unchecked_extrinsic_size as *mut u32, + ) + }; + let free_status = unsafe { sgx_ql_free_quote_verification_collateral(collateral_ptr) }; + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + ensure!( + free_status == sgx_quote3_error_t::SGX_QL_SUCCESS, + Error::SgxQuote(free_status) + ); + + Ok(Vec::from(&unchecked_extrinsic[..unchecked_extrinsic_size as usize])) + } + + fn dump_ias_ra_cert_to_disk(&self) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + + let result = unsafe { ffi::dump_ias_ra_cert_to_disk(self.eid, &mut retval) }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(()) + } + + fn dump_dcap_ra_cert_to_disk(&self) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + + self.set_ql_qe_enclave_paths()?; + let quoting_enclave_target_info = self.qe_get_target_info()?; + let quote_size = self.qe_get_quote_size()?; + + let result = unsafe { + ffi::dump_dcap_ra_cert_to_disk( + self.eid, + &mut retval, + "ing_enclave_target_info, + quote_size, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(()) + } + + fn set_ql_qe_enclave_paths(&self) -> EnclaveResult<()> { + set_ql_path(sgx_ql_path_type_t::SGX_QL_PCE_PATH, PCE_ENCLAVE)?; + set_ql_path(sgx_ql_path_type_t::SGX_QL_QE3_PATH, QE3_ENCLAVE)?; + set_ql_path(sgx_ql_path_type_t::SGX_QL_IDE_PATH, ID_ENCLAVE)?; + if set_ql_path(sgx_ql_path_type_t::SGX_QL_QPL_PATH, LIBDCAP_QUOTEPROV).is_err() { + // Ignore the error, because user may want to get cert type=3 quote. + warn!("Cannot set QPL directory, you may get ECDSA quote with `Encrypted PPID` cert type.\n"); + }; + set_qv_path(sgx_qv_path_type_t::SGX_QV_QVE_PATH, QVE_ENCLAVE)?; + + Ok(()) + } + + fn set_sgx_qpl_logging(&self) -> EnclaveResult<()> { + let log_level = sgx_ql_log_level_t::SGX_QL_LOG_INFO; + let res = unsafe { sgx_ql_set_logging_callback(forward_qpl_log, log_level) }; + if res == sgx_quote3_error_t::SGX_QL_SUCCESS { + Ok(()) + } else { + error!("Setting logging function failed with: {:?}", res); + Err(Error::SgxQuote(res)) + } + } + + fn qe_get_target_info(&self) -> EnclaveResult { + let mut quoting_enclave_target_info: sgx_target_info_t = sgx_target_info_t::default(); + let qe3_ret = + unsafe { sgx_qe_get_target_info(&mut quoting_enclave_target_info as *mut _) }; + ensure!(qe3_ret == sgx_quote3_error_t::SGX_QL_SUCCESS, Error::SgxQuote(qe3_ret)); + + Ok(quoting_enclave_target_info) + } + + fn qe_get_quote_size(&self) -> EnclaveResult { + let mut quote_size: u32 = 0; + let qe3_ret = unsafe { sgx_qe_get_quote_size(&mut quote_size as *mut _) }; + ensure!(qe3_ret == sgx_quote3_error_t::SGX_QL_SUCCESS, Error::SgxQuote(qe3_ret)); + + Ok(quote_size) + } + + fn dump_dcap_collateral_to_disk(&self, fmspc: Fmspc) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + let collateral_ptr = self.get_dcap_collateral(fmspc)?; + let result = + unsafe { ffi::dump_dcap_collateral_to_disk(self.eid, &mut retval, collateral_ptr) }; + let free_status = unsafe { sgx_ql_free_quote_verification_collateral(collateral_ptr) }; + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!( + free_status == sgx_quote3_error_t::SGX_QL_SUCCESS, + Error::SgxQuote(free_status) + ); + Ok(()) + } + + fn get_dcap_collateral( + &self, + fmspc: Fmspc, + ) -> EnclaveResult<*const sgx_ql_qve_collateral_t> { + let pck_ra = b"processor\x00"; + + // SAFETY: Just get a nullptr for the FFI to overwrite later + let mut collateral_ptr: *mut sgx_ql_qve_collateral_t = unsafe { std::mem::zeroed() }; + + let collateral_ptr_ptr: *mut *mut sgx_ql_qve_collateral_t = &mut collateral_ptr; + // SAFETY: All parameters are properly initialized so the FFI call should be fine + let sgx_status = unsafe { + sgx_ql_get_quote_verification_collateral( + fmspc.as_ptr(), + fmspc.len() as uint16_t, //fmspc len is fixed in the function signature + pck_ra.as_ptr() as _, + collateral_ptr_ptr, + ) + }; + + trace!("FMSPC: {:?}", hex::encode(fmspc)); + + if collateral_ptr.is_null() { + error!("PCK quote collateral data is null, sgx_status is: {}", sgx_status); + return Err(Error::SgxQuote(sgx_status)) + } + + trace!("collateral:"); + // SAFETY: the previous block checks for `collateral_ptr` being null. + // SAFETY: the fields should be nul terminated C strings. + unsafe { + let collateral = &*collateral_ptr; + trace!( + "version: {}\n, \ + tee_type: {}\n, \ + pck_crl_issuer_chain: {:?}\n, \ + pck_crl_issuer_chain_size: {}\n, \ + root_ca_crl: {:?}\n, \ + root_ca_crl_size: {}\n, \ + pck_crl: {:?}\n, \ + pck_crl_size: {}\n, \ + tcb_info_issuer_chain: {:?}\n, \ + tcb_info_issuer_chain_size: {}\n, \ + tcb_info: {}\n, \ + tcb_info_size: {}\n, \ + qe_identity_issuer_chain: {:?}\n, \ + qe_identity_issuer_chain_size: {}\n, \ + qe_identity: {}\n, \ + qe_identity_size: {}\n", + collateral.version, + collateral.tee_type, + std::ffi::CStr::from_ptr(collateral.pck_crl_issuer_chain).to_string_lossy(), + collateral.pck_crl_issuer_chain_size, + std::ffi::CStr::from_ptr(collateral.root_ca_crl).to_string_lossy(), + collateral.root_ca_crl_size, + std::ffi::CStr::from_ptr(collateral.pck_crl).to_string_lossy(), + collateral.pck_crl_size, + std::ffi::CStr::from_ptr(collateral.tcb_info_issuer_chain).to_string_lossy(), + collateral.tcb_info_issuer_chain_size, + std::ffi::CStr::from_ptr(collateral.tcb_info).to_string_lossy(), + collateral.tcb_info_size, + std::ffi::CStr::from_ptr(collateral.qe_identity_issuer_chain).to_string_lossy(), + collateral.qe_identity_issuer_chain_size, + std::ffi::CStr::from_ptr(collateral.qe_identity).to_string_lossy(), + collateral.qe_identity_size, + ); + }; + + ensure!(sgx_status == sgx_quote3_error_t::SGX_QL_SUCCESS, Error::SgxQuote(sgx_status)); + Ok(collateral_ptr) + } + } + + #[cfg(feature = "implement-ffi")] + impl RemoteAttestationCallBacks for Enclave { + fn init_quote(&self) -> EnclaveResult<(sgx_target_info_t, sgx_epid_group_id_t)> { + let mut ti: sgx_target_info_t = sgx_target_info_t::default(); + let mut eg: sgx_epid_group_id_t = sgx_epid_group_id_t::default(); + + let result = unsafe { + sgx_init_quote( + &mut ti as *mut sgx_target_info_t, + &mut eg as *mut sgx_epid_group_id_t, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + + Ok((ti, eg)) + } + + fn calc_quote_size(&self, revocation_list: Vec) -> EnclaveResult { + let mut real_quote_len: u32 = 0; + + let (p_sig_rl, sig_rl_size) = utils::vec_to_c_pointer_with_len(revocation_list); + + let result = unsafe { + sgx_calc_quote_size(p_sig_rl, sig_rl_size, &mut real_quote_len as *mut u32) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + + Ok(real_quote_len) + } + + fn get_quote( + &self, + revocation_list: Vec, + report: sgx_report_t, + quote_type: sgx_quote_sign_type_t, + spid: sgx_spid_t, + quote_nonce: sgx_quote_nonce_t, + quote_length: u32, + ) -> EnclaveResult<(sgx_report_t, Vec)> { + let (p_sig_rl, sig_rl_size) = utils::vec_to_c_pointer_with_len(revocation_list); + let p_report = &report as *const sgx_report_t; + let p_spid = &spid as *const sgx_spid_t; + let p_nonce = "e_nonce as *const sgx_quote_nonce_t; + + let mut qe_report = sgx_report_t::default(); + let p_qe_report = &mut qe_report as *mut sgx_report_t; + + let mut return_quote_buf = vec![0u8; quote_length as usize]; + let p_quote = return_quote_buf.as_mut_ptr(); + + let ret = unsafe { + sgx_get_quote( + p_report, + quote_type, + p_spid, + p_nonce, + p_sig_rl, + sig_rl_size, + p_qe_report, + p_quote as *mut sgx_quote_t, + quote_length, + ) + }; + + ensure!(ret == sgx_status_t::SGX_SUCCESS, Error::Sgx(ret)); + + Ok((qe_report, return_quote_buf)) + } + + fn get_dcap_quote(&self, report: sgx_report_t, quote_size: u32) -> EnclaveResult> { + let mut quote_vec: Vec = vec![0; quote_size as usize]; + let qe3_ret = + unsafe { sgx_qe_get_quote(&report, quote_size, quote_vec.as_mut_ptr() as _) }; + + ensure!(qe3_ret == sgx_quote3_error_t::SGX_QL_SUCCESS, Error::SgxQuote(qe3_ret)); + + Ok(quote_vec) + } + + fn get_qve_report_on_quote( + &self, + quote: Vec, + current_time: i64, + quote_collateral: &sgx_ql_qve_collateral_t, + qve_report_info: sgx_ql_qe_report_info_t, + supplemental_data_size: u32, + ) -> EnclaveResult { + let mut collateral_expiration_status = 1u32; + let mut quote_verification_result = sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OK; + let mut supplemental_data: Vec = vec![0; supplemental_data_size as usize]; + let mut qve_report_info_return_value: sgx_ql_qe_report_info_t = qve_report_info; + + // Set QvE (Quote verification Enclave) loading policy. + let dcap_ret = unsafe { + sgx_qv_set_enclave_load_policy(sgx_ql_request_policy_t::SGX_QL_EPHEMERAL) + }; + + if dcap_ret != sgx_quote3_error_t::SGX_QL_SUCCESS { + error!("sgx_qv_set_enclave_load_policy failed: {:#04x}", dcap_ret as u32); + return Err(Error::SgxQuote(dcap_ret)) + } + + // Retrieve supplemental data size from QvE. + let mut qve_supplemental_data_size = 0u32; + let dcap_ret = + unsafe { sgx_qv_get_quote_supplemental_data_size(&mut qve_supplemental_data_size) }; + + if dcap_ret != sgx_quote3_error_t::SGX_QL_SUCCESS { + error!("sgx_qv_get_quote_supplemental_data_size failed: {:?}", dcap_ret); + return Err(Error::SgxQuote(dcap_ret)) + } + if qve_supplemental_data_size != supplemental_data_size { + warn!("Quote supplemental data size is different between DCAP QVL and QvE, please make sure you installed DCAP QVL and QvE from same release."); + return Err(Error::Sgx(sgx_status_t::SGX_ERROR_INVALID_PARAMETER)) + } + + // Check if a collateral has been given, or if it's a simple zero assignment. + // If it's zero, let the pointer point to null. The collateral will then be retrieved + // directly by the QvE in `sgx_qv_verify_quote`. + let p_quote_collateral: *const sgx_ql_qve_collateral_t = + if quote_collateral.version == 0 { + std::ptr::null() + } else { + quote_collateral as *const sgx_ql_qve_collateral_t + }; + + // Call the QvE for quote verification + // here you can choose 'trusted' or 'untrusted' quote verification by specifying parameter '&qve_report_info' + // if '&qve_report_info' is NOT NULL, this API will call Intel QvE to verify quote + // if '&qve_report_info' is NULL, this API will call 'untrusted quote verify lib' to verify quote, + // this mode doesn't rely on SGX capable system, but the results can not be cryptographically authenticated + let dcap_ret = unsafe { + sgx_qv_verify_quote( + quote.as_ptr(), + quote.len() as u32, + p_quote_collateral, + current_time, + &mut collateral_expiration_status as *mut u32, + &mut quote_verification_result as *mut sgx_ql_qv_result_t, + &mut qve_report_info_return_value as *mut sgx_ql_qe_report_info_t, + supplemental_data_size, + supplemental_data.as_mut_ptr(), + ) + }; + + if sgx_quote3_error_t::SGX_QL_SUCCESS != dcap_ret { + error!("sgx_qv_verify_quote failed: {:?}", dcap_ret); + error!("quote_verification_result: {:?}", quote_verification_result); + return Err(Error::SgxQuote(dcap_ret)) + } + + // Check and print verification result. + match quote_verification_result { + sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OK => { + // Check verification collateral expiration status. + // This value should be considered in your own attestation/verification policy. + if 0u32 == collateral_expiration_status { + info!("QvE verification completed successfully."); + } else { + warn!("QvE verification completed, but collateral is out of date based on 'expiration_check_date' you provided."); + } + }, + sgx_ql_qv_result_t::SGX_QL_QV_RESULT_CONFIG_NEEDED + | sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OUT_OF_DATE + | sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OUT_OF_DATE_CONFIG_NEEDED + | sgx_ql_qv_result_t::SGX_QL_QV_RESULT_SW_HARDENING_NEEDED + | sgx_ql_qv_result_t::SGX_QL_QV_RESULT_CONFIG_AND_SW_HARDENING_NEEDED => { + warn!( + "QvE verification completed with Non-terminal result: {:?}", + quote_verification_result + ); + }, + _ => { + error!( + "QvE verification completed with Terminal result: {:?}", + quote_verification_result + ); + }, + } + + // Check supplemental data. + if supplemental_data_size > 0 { + // For now we simply print it, no checks done. + let p_supplemental_data: *const sgx_ql_qv_supplemental_t = + supplemental_data.as_ptr() as *const sgx_ql_qv_supplemental_t; + let qv_supplemental_data: sgx_ql_qv_supplemental_t = + unsafe { *p_supplemental_data }; + info!( + "QvE verification: Supplemental data version: {}", + qv_supplemental_data.version + ); + } + + Ok(QveReport { + collateral_expiration_status, + quote_verification_result, + qve_report_info_return_value, + supplemental_data, + }) + } + + fn get_update_info( + &self, + platform_blob: sgx_platform_info_t, + enclave_trusted: i32, + ) -> EnclaveResult { + let mut update_info: sgx_update_info_bit_t = sgx_update_info_bit_t::default(); + + let result = unsafe { + sgx_report_attestation_status( + &platform_blob as *const sgx_platform_info_t, + enclave_trusted, + &mut update_info as *mut sgx_update_info_bit_t, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + + Ok(update_info) + } + } + + #[cfg(feature = "implement-ffi")] + impl TlsRemoteAttestation for Enclave { + fn run_state_provisioning_server( + &self, + socket_fd: c_int, + sign_type: sgx_quote_sign_type_t, + quoting_enclave_target_info: Option<&sgx_target_info_t>, + quote_size: Option<&u32>, + skip_ra: bool, + ) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + + let result = unsafe { + ffi::run_state_provisioning_server( + self.eid, + &mut retval, + socket_fd, + sign_type, + quoting_enclave_target_info, + quote_size, + skip_ra.into(), + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(()) + } + + fn request_state_provisioning( + &self, + socket_fd: c_int, + sign_type: sgx_quote_sign_type_t, + quoting_enclave_target_info: Option<&sgx_target_info_t>, + quote_size: Option<&u32>, + shard: &ShardIdentifier, + skip_ra: bool, + ) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + + let encoded_shard = shard.encode(); + + let result = unsafe { + ffi::request_state_provisioning( + self.eid, + &mut retval, + socket_fd, + sign_type, + quoting_enclave_target_info, + quote_size, + encoded_shard.as_ptr(), + encoded_shard.len() as u32, + skip_ra.into(), + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(()) + } + } + + fn create_system_path(file_name: &str) -> String { + info!("create_system_path:: file_name={}", &file_name); + let default_path = format!("{}{}", OS_SYSTEM_PATH, file_name); + + let full_path = find_library_by_name(file_name).unwrap_or(default_path); + + let c_terminated_path = format!("{}{}", full_path, C_STRING_ENDING); + info!("create_system_path:: created path={}", &c_terminated_path); + c_terminated_path + } + + fn find_library_by_name(lib_name: &str) -> Option { + use std::process::Command; + // ldconfig -p | grep libsgx_pce_logic.so.1 + + let ldconfig_output = Command::new("ldconfig").args(["-p"]).output().ok()?; + let possible_path = String::from_utf8(ldconfig_output.stdout) + .ok()? + .lines() + .filter(|line| line.contains(lib_name)) + .map(|lib_name_and_path| { + lib_name_and_path + .rsplit_once("=>") + .map(|(_, lib_path)| lib_path.trim().to_owned()) + }) + .next()?; + + possible_path + } + + fn set_ql_path(path_type: sgx_ql_path_type_t, path: &str) -> EnclaveResult<()> { + let ret_val = unsafe { sgx_ql_set_path(path_type, create_system_path(path).as_ptr() as _) }; + if ret_val != sgx_quote3_error_t::SGX_QL_SUCCESS { + error!("Could not set {:?}", path_type); + return Err(Error::SgxQuote(ret_val)) + } + Ok(()) + } + + fn set_qv_path(path_type: sgx_qv_path_type_t, path: &str) -> EnclaveResult<()> { + let ret_val = unsafe { sgx_qv_set_path(path_type, create_system_path(path).as_ptr() as _) }; + if ret_val != sgx_quote3_error_t::SGX_QL_SUCCESS { + error!("Could not set {:?}", path_type); + return Err(Error::SgxQuote(ret_val)) + } + Ok(()) + } + + #[allow(clippy::not_unsafe_ptr_arg_deref)] + /// Make sure that the `log_slice_ptr` points to a null terminated string. + // This function must not be marked as `unsafe`, because `sgx_ql_set_logging_callback` expects a safe (i.e. not `unsafe`) function. + pub extern "C" fn forward_qpl_log(log_level: sgx_ql_log_level_t, log_slice_ptr: *const c_char) { + if log_slice_ptr.is_null() { + error!("[QPL - ERROR], slice to print was NULL"); + return + } + // This is safe, as the previous block checks for `NULL` pointer. + let slice = unsafe { core::ffi::CStr::from_ptr(log_slice_ptr) }; + match log_level { + sgx_ql_log_level_t::SGX_QL_LOG_INFO => info!("[QPL - INFO], {:?}", slice), + sgx_ql_log_level_t::SGX_QL_LOG_ERROR => error!("[QPL - ERROR], {:?}", slice), + } + } +} diff --git a/bitacross-worker/core-primitives/enclave-api/src/sidechain.rs b/bitacross-worker/core-primitives/enclave-api/src/sidechain.rs new file mode 100644 index 0000000000..877460075b --- /dev/null +++ b/bitacross-worker/core-primitives/enclave-api/src/sidechain.rs @@ -0,0 +1,122 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::EnclaveResult; +use codec::Encode; +use itp_storage::StorageProof; +use itp_types::parentchain::ParentchainId; +use sp_runtime::generic::SignedBlock; + +/// trait for handling blocks on the side chain +pub trait Sidechain: Send + Sync + 'static { + /// Sync parentchain blocks and events. Execute pending tops + /// and events proof in the enclave. + fn sync_parentchain( + &self, + blocks: &[SignedBlock], + events: &[Vec], + events_proofs: &[StorageProof], + parentchain_id: &ParentchainId, + is_syncing: bool, + ) -> EnclaveResult<()>; + + fn execute_trusted_calls(&self) -> EnclaveResult<()>; + + // litentry + /// Ignore the parentchain block import validation until the given block number + /// TODO: use the generic Header::Number trait + fn ignore_parentchain_block_import_validation_until(&self, until: u32) -> EnclaveResult<()>; +} + +#[cfg(feature = "implement-ffi")] +mod impl_ffi { + use super::Sidechain; + use crate::{error::Error, Enclave, EnclaveResult}; + use codec::Encode; + use frame_support::ensure; + use itp_enclave_api_ffi as ffi; + use itp_storage::StorageProof; + use itp_types::parentchain::ParentchainId; + use sgx_types::sgx_status_t; + use sp_runtime::generic::SignedBlock; + + impl Sidechain for Enclave { + fn sync_parentchain( + &self, + blocks: &[SignedBlock], + events: &[Vec], + events_proofs: &[StorageProof], + parentchain_id: &ParentchainId, + is_syncing: bool, + ) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + let blocks_enc = blocks.encode(); + let events_enc = events.encode(); + let events_proofs_enc = events_proofs.encode(); + let parentchain_id_enc = parentchain_id.encode(); + + let result = unsafe { + ffi::sync_parentchain( + self.eid, + &mut retval, + blocks_enc.as_ptr(), + blocks_enc.len(), + events_enc.as_ptr(), + events_enc.len(), + events_proofs_enc.as_ptr(), + events_proofs_enc.len(), + parentchain_id_enc.as_ptr(), + parentchain_id_enc.len() as u32, + is_syncing.into(), + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(()) + } + + fn execute_trusted_calls(&self) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + + let result = unsafe { ffi::execute_trusted_calls(self.eid, &mut retval) }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(()) + } + + fn ignore_parentchain_block_import_validation_until( + &self, + until: u32, + ) -> EnclaveResult<()> { + let mut retval = sgx_status_t::SGX_SUCCESS; + + let result = unsafe { + ffi::ignore_parentchain_block_import_validation_until(self.eid, &mut retval, &until) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(()) + } + } +} diff --git a/bitacross-worker/core-primitives/enclave-api/src/teeracle_api.rs b/bitacross-worker/core-primitives/enclave-api/src/teeracle_api.rs new file mode 100644 index 0000000000..530e2ff127 --- /dev/null +++ b/bitacross-worker/core-primitives/enclave-api/src/teeracle_api.rs @@ -0,0 +1,114 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::EnclaveResult; + +pub trait TeeracleApi: Send + Sync + 'static { + /// Update the currency market data for the token oracle. + fn update_market_data_xt( + &self, + crypto_currency: &str, + fiat_currency: &str, + ) -> EnclaveResult>; + + /// Update weather data for the corresponding coordinates. + fn update_weather_data_xt(&self, longitude: &str, latitude: &str) -> EnclaveResult>; +} + +#[cfg(feature = "implement-ffi")] +mod impl_ffi { + use super::TeeracleApi; + use crate::{error::Error, Enclave, EnclaveResult}; + use codec::Encode; + use frame_support::ensure; + use itp_enclave_api_ffi as ffi; + use log::*; + use sgx_types::*; + impl TeeracleApi for Enclave { + fn update_market_data_xt( + &self, + crypto_currency: &str, + fiat_currency: &str, + ) -> EnclaveResult> { + info!( + "TeeracleApi update_market_data_xt in with crypto {} and fiat {}", + crypto_currency, fiat_currency + ); + let mut retval = sgx_status_t::SGX_SUCCESS; + let response_max_len = 8192; + let mut response: Vec = vec![0u8; response_max_len as usize]; + let mut response_len: u32 = 0; + + let crypto_curr = crypto_currency.encode(); + let fiat_curr = fiat_currency.encode(); + + let res = unsafe { + ffi::update_market_data_xt( + self.eid, + &mut retval, + crypto_curr.as_ptr(), + crypto_curr.len() as u32, + fiat_curr.as_ptr(), + fiat_curr.len() as u32, + response.as_mut_ptr(), + response_max_len, + &mut response_len as *mut u32, + ) + }; + + ensure!(res == sgx_status_t::SGX_SUCCESS, Error::Sgx(res)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + + Ok(Vec::from(&response[..response_len as usize])) + } + fn update_weather_data_xt( + &self, + longitude: &str, + latitude: &str, + ) -> EnclaveResult> { + info!( + "TeeracleApi update_weather_data_xt in with latitude: {}, longitude: {}", + latitude, longitude + ); + let mut retval = sgx_status_t::SGX_SUCCESS; + let response_max_len = 8192; + let mut response: Vec = vec![0u8; response_max_len as usize]; + let mut response_len: u32 = 0; + + let longitude_encoded: Vec = longitude.encode(); + let latitude_encoded: Vec = latitude.encode(); + + let res = unsafe { + ffi::update_weather_data_xt( + self.eid, + &mut retval, + longitude_encoded.as_ptr(), + longitude_encoded.len() as u32, + latitude_encoded.as_ptr(), + latitude_encoded.len() as u32, + response.as_mut_ptr(), + response_max_len, + &mut response_len as *mut u32, + ) + }; + + ensure!(res == sgx_status_t::SGX_SUCCESS, Error::Sgx(res)); + ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); + Ok(Vec::from(&response[..response_len as usize])) + } + } +} diff --git a/bitacross-worker/core-primitives/enclave-api/src/utils.rs b/bitacross-worker/core-primitives/enclave-api/src/utils.rs new file mode 100644 index 0000000000..e36764f7ac --- /dev/null +++ b/bitacross-worker/core-primitives/enclave-api/src/utils.rs @@ -0,0 +1,27 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use std::ptr; + +pub fn vec_to_c_pointer_with_len(input: Vec) -> (*const A, u32) { + if input.is_empty() { + (ptr::null(), 0) + } else { + (input.as_ptr(), input.len() as u32) + } +} diff --git a/bitacross-worker/core-primitives/enclave-bridge-storage/Cargo.toml b/bitacross-worker/core-primitives/enclave-bridge-storage/Cargo.toml new file mode 100644 index 0000000000..8b191f3458 --- /dev/null +++ b/bitacross-worker/core-primitives/enclave-bridge-storage/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "itp-enclave-bridge-storage" +version = "0.9.0" +authors = ["Integritee AG "] +edition = "2021" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +sp-std = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +#local deps +itp-storage = { path = "../storage", default-features = false } + +[features] +default = ["std"] +std = [ + "codec/std", + "sp-std/std", + "itp-storage/std", +] diff --git a/bitacross-worker/core-primitives/enclave-bridge-storage/src/lib.rs b/bitacross-worker/core-primitives/enclave-bridge-storage/src/lib.rs new file mode 100644 index 0000000000..9077d756b6 --- /dev/null +++ b/bitacross-worker/core-primitives/enclave-bridge-storage/src/lib.rs @@ -0,0 +1,31 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +use codec::Encode; +use itp_storage::{storage_map_key, StorageHasher}; +use sp_std::prelude::Vec; + +pub struct EnclaveBridgeStorage; + +// Separate the prefix from the rest because in our case we changed the storage prefix due to +// the rebranding. With the below implementation of the `TeerexStorageKeys`, we could simply +// define another struct `OtherStorage`, implement `StoragePrefix` for it, and get the +// `TeerexStorageKeys` implementation for free. +pub trait StoragePrefix { + fn prefix() -> &'static str; +} + +impl StoragePrefix for EnclaveBridgeStorage { + fn prefix() -> &'static str { + "EnclaveBridge" + } +} + +pub trait EnclaveBridgeStorageKeys { + fn shard_status(shard: T) -> Vec; +} + +impl EnclaveBridgeStorageKeys for S { + fn shard_status(shard: T) -> Vec { + storage_map_key(Self::prefix(), "ShardStatus", &shard, &StorageHasher::Blake2_128Concat) + } +} diff --git a/bitacross-worker/core-primitives/enclave-metrics/Cargo.toml b/bitacross-worker/core-primitives/enclave-metrics/Cargo.toml new file mode 100644 index 0000000000..b6f3ae3e29 --- /dev/null +++ b/bitacross-worker/core-primitives/enclave-metrics/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "itp-enclave-metrics" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +# sgx +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } + +# no-std dependencies +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "full"] } +substrate-fixed = { default-features = false, git = "https://github.com/encointer/substrate-fixed", tag = "v0.5.9" } + +[features] +default = ["std"] +std = [ + "substrate-fixed/std", + "codec/std", +] +sgx = [ + "sgx_tstd", +] diff --git a/bitacross-worker/core-primitives/enclave-metrics/src/lib.rs b/bitacross-worker/core-primitives/enclave-metrics/src/lib.rs new file mode 100644 index 0000000000..ae7f253adc --- /dev/null +++ b/bitacross-worker/core-primitives/enclave-metrics/src/lib.rs @@ -0,0 +1,68 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +extern crate core; +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +use codec::{Decode, Encode}; +use core::time::Duration; +use std::string::String; +use substrate_fixed::types::U32F32; + +// FIXME: Copied from ita-oracle because of cyclic deps. Should be removed after integritee-network/pallets#71 +pub type ExchangeRate = U32F32; + +#[derive(Encode, Decode, Debug)] +pub enum EnclaveMetric { + SetSidechainBlockHeight(u64), + TopPoolSizeSet(u64), + TopPoolSizeIncrement, + TopPoolSizeDecrement, + ExchangeRateOracle(ExchangeRateOracleMetric), + SuccessfulTrustedOperationIncrement(String), + FailedTrustedOperationIncrement(String), + ParentchainBlockImportTime(Duration), + SidechainBlockImportTime(Duration), + SidechainSlotPrepareTime(Duration), + SidechainSlotStfExecutionTime(Duration), + SidechainSlotBlockCompositionTime(Duration), + SidechainBlockBroadcastingTime(Duration), + // OracleMetric(OracleMetric), +} + +#[derive(Encode, Decode, Debug)] +pub enum ExchangeRateOracleMetric { + /// Exchange Rate from CoinGecko - (Source, TradingPair, ExchangeRate) + ExchangeRate(String, String, ExchangeRate), + /// Response time of the request in [ms]. (Source, ResponseTime) + ResponseTime(String, u128), + /// Increment the number of requests (Source) + NumberRequestsIncrement(String), +} + +#[derive(Encode, Decode, Debug)] +pub enum OracleMetric { + OracleSpecificMetric(MetricsInfo), + ResponseTime(String, u128), + NumberRequestsIncrement(String), +} diff --git a/bitacross-worker/core-primitives/extrinsics-factory/Cargo.toml b/bitacross-worker/core-primitives/extrinsics-factory/Cargo.toml new file mode 100644 index 0000000000..56ae7283d1 --- /dev/null +++ b/bitacross-worker/core-primitives/extrinsics-factory/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "itp-extrinsics-factory" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +# sgx dependencies +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } +sgx_types = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } +substrate-api-client = { default-features = false, features = ["sync-api"], git = "https://github.com/scs/substrate-api-client.git", branch = "polkadot-v0.9.42-tag-v0.14.0" } + +# local dependencies +itp-node-api = { path = "../node-api", default-features = false } +itp-nonce-cache = { path = "../nonce-cache", default-features = false } +itp-types = { path = "../types", default-features = false } + +# sgx enabled external libraries +thiserror_sgx = { package = "thiserror", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3", optional = true } + +# std compatible external libraries (make sure these versions match with the sgx-enabled ones above) +thiserror = { version = "1.0", optional = true } + +# no-std dependencies +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +log = { version = "0.4", default-features = false } +sp-core = { default-features = false, features = ["full_crypto"], git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +[features] +default = ["std"] +std = [ + "itp-node-api/std", + "itp-nonce-cache/std", + "itp-types/std", + "log/std", + "substrate-api-client/std", + "thiserror", +] +sgx = [ + "itp-node-api/sgx", + "itp-nonce-cache/sgx", + "sgx_tstd", + "thiserror_sgx", +] +mocks = [] diff --git a/bitacross-worker/core-primitives/extrinsics-factory/src/error.rs b/bitacross-worker/core-primitives/extrinsics-factory/src/error.rs new file mode 100644 index 0000000000..4f052b9f94 --- /dev/null +++ b/bitacross-worker/core-primitives/extrinsics-factory/src/error.rs @@ -0,0 +1,49 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use sgx_types::sgx_status_t; +use std::{boxed::Box, format}; + +pub type Result = core::result::Result; + +/// extrinsics factory error +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("Nonce cache error: {0}")] + NonceCache(#[from] itp_nonce_cache::error::Error), + #[error("Node API error: {0:?}")] + NodeMetadataProvider(#[from] itp_node_api::metadata::provider::Error), + #[error("SGX error, status: {0}")] + Sgx(sgx_status_t), + #[error(transparent)] + Other(#[from] Box), +} + +impl From for Error { + fn from(sgx_status: sgx_status_t) -> Self { + Self::Sgx(sgx_status) + } +} + +impl From for Error { + fn from(e: codec::Error) -> Self { + Self::Other(format!("{:?}", e).into()) + } +} diff --git a/bitacross-worker/core-primitives/extrinsics-factory/src/lib.rs b/bitacross-worker/core-primitives/extrinsics-factory/src/lib.rs new file mode 100644 index 0000000000..dd4180fee8 --- /dev/null +++ b/bitacross-worker/core-primitives/extrinsics-factory/src/lib.rs @@ -0,0 +1,241 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +// re-export module to properly feature gate sgx and regular std environment +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod sgx_reexport_prelude { + pub use thiserror_sgx as thiserror; +} + +use codec::Encode; +use error::Result; +use itp_node_api::{ + api_client::{ + ExtrinsicParams, ParentchainAdditionalParams, ParentchainExtrinsicParams, SignExtrinsic, + }, + metadata::{provider::AccessNodeMetadata, NodeMetadata}, +}; +use itp_nonce_cache::{MutateNonce, Nonce}; +use itp_types::{parentchain::AccountId, OpaqueCall}; +use sp_core::H256; +use sp_runtime::{generic::Era, OpaqueExtrinsic}; +use std::{sync::Arc, vec::Vec}; +use substrate_api_client::ac_compose_macros::compose_extrinsic_offline; + +pub mod error; + +#[cfg(feature = "mocks")] +pub mod mock; + +/// Create extrinsics from opaque calls +/// +/// Also increases the nonce counter for each extrinsic that is created. +pub trait CreateExtrinsics { + fn create_extrinsics( + &self, + calls: &[OpaqueCall], + extrinsics_params: Option, + ) -> Result>; +} + +/// Extrinsics factory +pub struct ExtrinsicsFactory +where + Signer: SignExtrinsic, + NonceCache: MutateNonce, + NodeMetadataRepository: AccessNodeMetadata, +{ + genesis_hash: H256, + signer: Signer, + nonce_cache: Arc, + node_metadata_repository: Arc, +} + +impl + ExtrinsicsFactory +where + Signer: SignExtrinsic, + NonceCache: MutateNonce, + NodeMetadataRepository: AccessNodeMetadata, +{ + pub fn new( + genesis_hash: H256, + signer: Signer, + nonce_cache: Arc, + node_metadata_repository: Arc, + ) -> Self { + ExtrinsicsFactory { genesis_hash, signer, nonce_cache, node_metadata_repository } + } + + pub fn with_signer(&self, signer: Signer, nonce_cache: Arc) -> Self { + ExtrinsicsFactory { + genesis_hash: self.genesis_hash, + signer, + nonce_cache, + node_metadata_repository: self.node_metadata_repository.clone(), + } + } +} + +impl CreateExtrinsics + for ExtrinsicsFactory +where + Signer: SignExtrinsic, + NonceCache: MutateNonce, + NodeMetadataRepository: AccessNodeMetadata, +{ + fn create_extrinsics( + &self, + calls: &[OpaqueCall], + extrinsics_params: Option, + ) -> Result> { + let mut nonce_lock = self.nonce_cache.load_for_mutation()?; + let mut nonce_value = nonce_lock.0; + + let additional_extrinsic_params = extrinsics_params.unwrap_or_else(|| { + ParentchainAdditionalParams::new().era(Era::Immortal, self.genesis_hash).tip(0) + }); + + let (runtime_spec_version, runtime_transaction_version) = + self.node_metadata_repository.get_from_metadata(|m| { + (m.get_runtime_version(), m.get_runtime_transaction_version()) + })?; + + let extrinsics_buffer: Vec = calls + .iter() + .map(|call| { + log::info!("Creating extrinsics using nonce: {}", nonce_value); + let extrinsic_params = ParentchainExtrinsicParams::new( + runtime_spec_version, + runtime_transaction_version, + nonce_value, + self.genesis_hash, + additional_extrinsic_params, + ); + let xt = compose_extrinsic_offline!(&self.signer, call, extrinsic_params).encode(); + nonce_value += 1; + xt + }) + .map(|xt| { + OpaqueExtrinsic::from_bytes(&xt) + .expect("A previously encoded extrinsic has valid codec; qed.") + }) + .collect(); + + *nonce_lock = Nonce(nonce_value); + + Ok(extrinsics_buffer) + } +} + +#[cfg(test)] +pub mod tests { + + use super::*; + use itp_node_api::{ + api_client::{PairSignature, StaticExtrinsicSigner}, + metadata::provider::NodeMetadataRepository, + }; + use itp_nonce_cache::{GetNonce, Nonce, NonceCache, NonceValue}; + use sp_core::{ed25519, Pair}; + //use substrate_api_client::extrinsic::xt_primitives::UncheckedExtrinsicV4; + + #[test] + pub fn creating_xts_increases_nonce_for_each_xt() { + let nonce_cache = Arc::new(NonceCache::default()); + let node_metadata_repo = Arc::new(NodeMetadataRepository::new(NodeMetadata::default())); + let extrinsics_factory = ExtrinsicsFactory::new( + test_genesis_hash(), + StaticExtrinsicSigner::<_, PairSignature>::new(test_account()), + nonce_cache.clone(), + node_metadata_repo, + ); + + let opaque_calls = [OpaqueCall(vec![3u8; 42]), OpaqueCall(vec![12u8, 78])]; + let xts = extrinsics_factory.create_extrinsics(&opaque_calls, None).unwrap(); + + assert_eq!(opaque_calls.len(), xts.len()); + assert_eq!(nonce_cache.get_nonce().unwrap(), Nonce(opaque_calls.len() as NonceValue)); + } + + #[test] + pub fn with_signer_works() { + let nonce_cache1 = Arc::new(NonceCache::default()); + *nonce_cache1.load_for_mutation().unwrap() = Nonce(42); + + let node_metadata_repo = Arc::new(NodeMetadataRepository::new(NodeMetadata::default())); + let extrinsics_factory = ExtrinsicsFactory::new( + test_genesis_hash(), + StaticExtrinsicSigner::<_, PairSignature>::new(test_account()), + nonce_cache1.clone(), + node_metadata_repo, + ); + + let nonce_cache2 = Arc::new(NonceCache::default()); + let extrinsics_factory = extrinsics_factory.with_signer( + StaticExtrinsicSigner::<_, PairSignature>::new(test_account2()), + nonce_cache2.clone(), + ); + + let opaque_calls = [OpaqueCall(vec![3u8; 42]), OpaqueCall(vec![12u8, 78])]; + let xts = extrinsics_factory.create_extrinsics(&opaque_calls, None).unwrap(); + + assert_eq!(opaque_calls.len(), xts.len()); + assert_eq!(nonce_cache2.get_nonce().unwrap(), Nonce(opaque_calls.len() as NonceValue)); + assert_eq!(nonce_cache1.get_nonce().unwrap(), Nonce(42)); + } + + // #[test] + // pub fn xts_have_increasing_nonce() { + // let nonce_cache = Arc::new(NonceCache::default()); + // nonce_cache.set_nonce(Nonce(34)).unwrap(); + // let extrinsics_factory = + // ExtrinsicsFactory::new(test_genesis_hash(), test_account(), nonce_cache); + // + // let opaque_calls = + // [OpaqueCall(vec![3u8; 42]), OpaqueCall(vec![12u8, 78]), OpaqueCall(vec![15u8, 12])]; + // let xts: Vec> = extrinsics_factory + // .create_extrinsics(&opaque_calls) + // .unwrap() + // .iter() + // .map(|mut x| UncheckedExtrinsicV4::::decode(&mut x)) + // .collect(); + // + // assert_eq!(xts.len(), opaque_calls.len()); + // assert_eq!(xts[0].signature.unwrap().2 .2, 34u128); + // } + + fn test_account() -> ed25519::Pair { + ed25519::Pair::from_seed(b"42315678901234567890123456789012") + } + + fn test_account2() -> ed25519::Pair { + ed25519::Pair::from_seed(b"12315678901234567890123456789012") + } + + fn test_genesis_hash() -> H256 { + H256::from_slice(&[56u8; 32]) + } +} diff --git a/bitacross-worker/core-primitives/extrinsics-factory/src/mock.rs b/bitacross-worker/core-primitives/extrinsics-factory/src/mock.rs new file mode 100644 index 0000000000..4e1923210e --- /dev/null +++ b/bitacross-worker/core-primitives/extrinsics-factory/src/mock.rs @@ -0,0 +1,46 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{error::Result, CreateExtrinsics}; +use itp_node_api::api_client::ParentchainAdditionalParams; +use itp_types::OpaqueCall; +use sp_runtime::OpaqueExtrinsic; +use std::vec::Vec; + +/// Mock of an extrinsics factory. To be used in unit tests. +/// +/// Returns an empty extrinsic. +#[derive(Default, Clone)] +pub struct ExtrinsicsFactoryMock; + +impl CreateExtrinsics for ExtrinsicsFactoryMock { + fn create_extrinsics( + &self, + _calls: &[OpaqueCall], + _additional_params: Option, + ) -> Result> { + // Intention was to map an OpaqueCall to some dummy OpaqueExtrinsic, + // so the output vector has the same size as the input one (and thus can be tested from the outside). + // However, it doesn't seem to be possible to construct an empty of dummy OpaqueExtrinsic, + // `from_bytes` expects a valid encoded OpaqueExtrinsic. + // Ok(calls + // .iter() + // .map(|_| OpaqueExtrinsic::from_bytes(Vec::new().as_slice()).unwrap()) + // .collect()) + Ok(Vec::new()) + } +} diff --git a/bitacross-worker/core-primitives/hashing/Cargo.toml b/bitacross-worker/core-primitives/hashing/Cargo.toml new file mode 100644 index 0000000000..5caa95d92b --- /dev/null +++ b/bitacross-worker/core-primitives/hashing/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "itp-hashing" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +# substrate +sp-core = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +[features] +default = ["std"] +std = [] diff --git a/bitacross-worker/core-primitives/hashing/src/lib.rs b/bitacross-worker/core-primitives/hashing/src/lib.rs new file mode 100644 index 0000000000..6e44afbcaa --- /dev/null +++ b/bitacross-worker/core-primitives/hashing/src/lib.rs @@ -0,0 +1,46 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +//! Hashing traits and utilities. + +#![cfg_attr(not(feature = "std"), no_std)] + +use sp_core::H256; + +#[cfg(feature = "std")] +pub mod std_hash; + +/// Trait to compute a hash of self. +pub trait Hash { + fn hash(&self) -> Output; +} + +// Cannot use the implementation below unfortunately, because our externalities +// have their own hash implementation which ignores the state diff. +// /// Implement Hash for any types that implement encode. +// /// +// /// +// impl Hash for T { +// fn hash(&self) -> H256 { +// blake2_256(&self.encode()).into() +// } +// } + +pub fn hash_from_slice(hash_slize: &[u8]) -> H256 { + let mut g = [0; 32]; + g.copy_from_slice(hash_slize); + H256::from(&mut g) +} diff --git a/bitacross-worker/core-primitives/hashing/src/std_hash.rs b/bitacross-worker/core-primitives/hashing/src/std_hash.rs new file mode 100644 index 0000000000..2a6524a800 --- /dev/null +++ b/bitacross-worker/core-primitives/hashing/src/std_hash.rs @@ -0,0 +1,31 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::Hash; +use std::{ + collections::hash_map::DefaultHasher, + hash::{Hash as StdHash, Hasher}, +}; + +/// Implement Hash for all types implementing core::hash::Hash. +impl Hash for T { + fn hash(&self) -> u64 { + let mut hasher = DefaultHasher::new(); + self.hash(&mut hasher); + hasher.finish() + } +} diff --git a/bitacross-worker/core-primitives/import-queue/Cargo.toml b/bitacross-worker/core-primitives/import-queue/Cargo.toml new file mode 100644 index 0000000000..2d358d8102 --- /dev/null +++ b/bitacross-worker/core-primitives/import-queue/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "itp-import-queue" +version = "0.8.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +# sgx dependencies +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } +sgx_types = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } + +# sgx enabled external libraries +thiserror_sgx = { package = "thiserror", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3", optional = true } + +# crates.io std-only compatible external libraries (make sure these versions match with the sgx-enabled ones above) +thiserror = { version = "1.0", optional = true } + +# crates.io no-std compatible libraries + +[features] +default = ["std"] +std = [ + # no-std compatible libraries + # std compatible external libraries + "thiserror", +] +sgx = [ + # sgx + "sgx_tstd", + # sgx enabled external libraries + "thiserror_sgx", +] diff --git a/bitacross-worker/core-primitives/import-queue/src/error.rs b/bitacross-worker/core-primitives/import-queue/src/error.rs new file mode 100644 index 0000000000..c1492cf550 --- /dev/null +++ b/bitacross-worker/core-primitives/import-queue/src/error.rs @@ -0,0 +1,41 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use sgx_types::sgx_status_t; +use std::boxed::Box; + +pub type Result = core::result::Result; + +/// Parentchain block importer error. +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("SGX error, status: {0}")] + Sgx(sgx_status_t), + #[error("Queue lock is poisoned")] + PoisonedLock, + #[error(transparent)] + Other(#[from] Box), +} + +impl From for Error { + fn from(sgx_status: sgx_status_t) -> Self { + Self::Sgx(sgx_status) + } +} diff --git a/bitacross-worker/core-primitives/import-queue/src/import_queue.rs b/bitacross-worker/core-primitives/import-queue/src/import_queue.rs new file mode 100644 index 0000000000..2555d3b5a3 --- /dev/null +++ b/bitacross-worker/core-primitives/import-queue/src/import_queue.rs @@ -0,0 +1,273 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Import queue implementation + +#[cfg(feature = "sgx")] +use std::sync::SgxRwLock as RwLock; + +#[cfg(feature = "std")] +use std::sync::RwLock; + +use crate::{ + error::{Error, Result}, + PeekQueue, PopFromQueue, PushToQueue, +}; +use std::{collections::VecDeque, vec::Vec}; + +/// Any import queue. +/// +/// Uses RwLock internally to guard against concurrent access and ensure all operations are atomic. +pub struct ImportQueue { + queue: RwLock>, +} + +impl ImportQueue { + pub fn is_empty(&self) -> Result { + let queue_lock = self.queue.read().map_err(|_| Error::PoisonedLock)?; + Ok(queue_lock.is_empty()) + } +} + +impl Default for ImportQueue { + fn default() -> Self { + ImportQueue { queue: Default::default() } + } +} + +impl PushToQueue for ImportQueue { + fn push_multiple(&self, items: Vec) -> Result<()> { + let mut queue_lock = self.queue.write().map_err(|_| Error::PoisonedLock)?; + queue_lock.extend(items); + Ok(()) + } + + fn push_single(&self, item: Item) -> Result<()> { + let mut queue_lock = self.queue.write().map_err(|_| Error::PoisonedLock)?; + queue_lock.push_back(item); + Ok(()) + } +} + +impl PopFromQueue for ImportQueue { + type ItemType = Item; + + fn pop_all_but_last(&self) -> Result> { + let mut queue_lock = self.queue.write().map_err(|_| Error::PoisonedLock)?; + let queue_length = queue_lock.len(); + if queue_length < 2 { + return Ok(Vec::::default()) + } + Ok(queue_lock.drain(..queue_length - 1).collect::>()) + } + + fn pop_all(&self) -> Result> { + let mut queue_lock = self.queue.write().map_err(|_| Error::PoisonedLock)?; + Ok(queue_lock.drain(..).collect::>()) + } + + fn pop_until(&self, predicate: Predicate) -> Result> + where + Predicate: FnMut(&Self::ItemType) -> bool, + { + let mut queue_lock = self.queue.write().map_err(|_| Error::PoisonedLock)?; + match queue_lock.iter().position(predicate) { + None => Ok(Vec::new()), + Some(p) => Ok(queue_lock.drain(..p + 1).collect::>()), + } + } + + fn pop_front(&self) -> Result> { + let mut queue_lock = self.queue.write().map_err(|_| Error::PoisonedLock)?; + Ok(queue_lock.pop_front()) + } + + fn pop_from_front_until(&self, amount: usize) -> Result> { + let mut queue_lock = self.queue.write().map_err(|_| Error::PoisonedLock)?; + if amount > queue_lock.len() { + return Err(Error::Other( + "Cannot Pop more items from the queue than are available".into(), + )) + } + Ok(queue_lock.drain(..amount).collect::>()) + } +} + +impl PeekQueue for ImportQueue +where + Item: Clone, +{ + type ItemType = Item; + + fn peek_find(&self, predicate: Predicate) -> Result> + where + Predicate: Fn(&Self::ItemType) -> bool, + { + let queue_lock = self.queue.read().map_err(|_| Error::PoisonedLock)?; + let maybe_item = queue_lock.iter().find(|&b| predicate(b)); + Ok(maybe_item.cloned()) + } + + fn peek_last(&self) -> Result> { + let queue_lock = self.queue.read().map_err(|_| Error::PoisonedLock)?; + Ok(queue_lock.back().cloned()) + } + + fn peek_queue_size(&self) -> Result { + let queue_lock = self.queue.read().map_err(|_| Error::PoisonedLock)?; + Ok(queue_lock.len()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use core::assert_matches::assert_matches; + + type TestBlock = u32; + + #[test] + fn default_queue_is_empty() { + let queue = ImportQueue::::default(); + assert!(queue.is_empty().unwrap()); + } + + #[test] + fn pop_all_on_default_returns_empty_vec() { + let queue = ImportQueue::::default(); + assert!(queue.pop_all().unwrap().is_empty()); + } + + #[test] + fn after_inserting_queue_is_not_empty() { + let queue = ImportQueue::::default(); + queue.push_single(TestBlock::default()).unwrap(); + assert!(!queue.is_empty().unwrap()); + } + + #[test] + fn pop_all_after_inserting_leaves_empty_queue() { + let queue = ImportQueue::::default(); + queue + .push_multiple(vec![TestBlock::default(), TestBlock::default(), TestBlock::default()]) + .unwrap(); + + let all_popped = queue.pop_all().unwrap(); + assert_eq!(3, all_popped.len()); + assert!(queue.is_empty().unwrap()); + } + + #[test] + fn pop_all_except_last_on_default_returns_empty_vec() { + let queue = ImportQueue::::default(); + assert!(queue.pop_all_but_last().unwrap().is_empty()); + } + + #[test] + fn pop_all_except_last_with_single_element_returns_empty_vec() { + let queue = ImportQueue::::default(); + queue.push_single(TestBlock::default()).unwrap(); + assert!(queue.pop_all_but_last().unwrap().is_empty()); + } + + #[test] + fn pop_all_except_last_with_multiple_elements_returns_all_but_last_inserted() { + let queue = ImportQueue::::default(); + queue.push_multiple(vec![1, 3, 5, 7]).unwrap(); + assert_eq!(3, queue.pop_all_but_last().unwrap().len()); + assert!(!queue.is_empty().unwrap()); + assert_eq!(7, queue.pop_all().unwrap()[0]); + } + + #[test] + fn pop_until_returns_empty_vec_if_nothing_matches() { + let queue = ImportQueue::::default(); + queue.push_multiple(vec![1, 3, 5, 7]).unwrap(); + + let popped_elements = queue.pop_until(|i| i > &10u32).unwrap(); + assert!(popped_elements.is_empty()); + } + + #[test] + fn pop_until_returns_elements_until_and_including_match() { + let queue = ImportQueue::::default(); + queue.push_multiple(vec![1, 2, 3, 10]).unwrap(); + + assert_eq!(queue.pop_until(|i| i == &3).unwrap(), vec![1, 2, 3]); + } + + #[test] + fn pop_until_returns_all_elements_if_last_matches() { + let queue = ImportQueue::::default(); + queue.push_multiple(vec![1, 2, 3, 10]).unwrap(); + + assert_eq!(queue.pop_until(|i| i == &10).unwrap(), vec![1, 2, 3, 10]); + } + + #[test] + fn pop_until_returns_first_element_if_it_matches() { + let queue = ImportQueue::::default(); + queue.push_single(4).unwrap(); + assert_eq!(queue.pop_until(|i| i == &4).unwrap(), vec![4]) + } + + #[test] + fn pop_front_returns_none_if_queue_is_empty() { + let queue = ImportQueue::::default(); + assert_matches!(queue.pop_front().unwrap(), None); + } + + #[test] + fn pop_front_works() { + let queue = ImportQueue::::default(); + queue.push_multiple(vec![1, 2, 3, 5]).unwrap(); + assert_eq!(queue.pop_front().unwrap(), Some(1)); + assert_eq!(queue.pop_front().unwrap(), Some(2)); + assert_eq!(queue.pop_front().unwrap(), Some(3)); + assert_eq!(queue.pop_front().unwrap(), Some(5)); + assert_eq!(queue.pop_front().unwrap(), None); + } + + #[test] + fn peek_find_works() { + let queue = ImportQueue::::default(); + queue.push_multiple(vec![1, 2, 3, 5]).unwrap(); + + assert_eq!(None, queue.peek_find(|i| i == &4).unwrap()); + assert!(queue.peek_find(|i| i == &1).unwrap().is_some()); + assert!(queue.peek_find(|i| i == &5).unwrap().is_some()); + } + + #[test] + fn peek_find_on_empty_queue_returns_none() { + let queue = ImportQueue::::default(); + assert_eq!(None, queue.peek_find(|i| i == &1).unwrap()); + } + + #[test] + fn peek_last_works() { + let queue = ImportQueue::::default(); + queue.push_multiple(vec![1, 2, 3, 5, 6, 9, 10]).unwrap(); + assert_eq!(queue.peek_last().unwrap(), Some(10)); + } + + #[test] + fn peek_last_on_empty_queue_returns_none() { + let queue = ImportQueue::::default(); + assert_eq!(None, queue.peek_last().unwrap()); + } +} diff --git a/bitacross-worker/core-primitives/import-queue/src/lib.rs b/bitacross-worker/core-primitives/import-queue/src/lib.rs new file mode 100644 index 0000000000..d223317f78 --- /dev/null +++ b/bitacross-worker/core-primitives/import-queue/src/lib.rs @@ -0,0 +1,89 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +//! Queueing of item imports. + +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(test, feature(assert_matches))] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +// re-export module to properly feature gate sgx and regular std environment +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod sgx_reexport_prelude { + pub use thiserror_sgx as thiserror; +} + +pub mod error; +pub mod import_queue; + +pub use import_queue::*; + +use error::Result; +use std::vec::Vec; + +/// Trait to push items such as blocks to an import queue. +pub trait PushToQueue { + /// Push multiple items to the queue, ordering from the Vec is preserved. + fn push_multiple(&self, item: Vec) -> Result<()>; + + /// Push a single item to the queue. + fn push_single(&self, item: Item) -> Result<()>; +} + +/// Trait to pop items from the import queue. +pub trait PopFromQueue { + type ItemType; + + /// Pop (i.e. removes and returns) all but the last item from the import queue. + fn pop_all_but_last(&self) -> Result>; + + /// Pop (i.e. removes and returns) all items from the import queue. + fn pop_all(&self) -> Result>; + + /// Pop (front) until specified item is found. If no item matches, empty Vec is returned. + fn pop_until(&self, predicate: Predicate) -> Result> + where + Predicate: Fn(&Self::ItemType) -> bool; + + /// Pop (front) queue. Returns None if queue is empty. + fn pop_front(&self) -> Result>; + + /// Pop (front) queue until a specific amount of pops has been reached + fn pop_from_front_until(&self, amount: usize) -> Result>; +} + +/// Trait to peek items in the import queue without altering the queue. +pub trait PeekQueue { + type ItemType: Clone; + + /// Search the queue with a given predicate and return a reference to the first element that matches. + /// Returns None if nothing matches. + fn peek_find(&self, predicate: Predicate) -> Result> + where + Predicate: Fn(&Self::ItemType) -> bool; + + /// Peeks the last element in the queue (aka the newest one, last to be popped). + /// Returns None if queue is empty. + fn peek_last(&self) -> Result>; + + /// Peek the queue size (i.e. number of elements the queue contains). + fn peek_queue_size(&self) -> Result; +} diff --git a/bitacross-worker/core-primitives/networking-utils/Cargo.toml b/bitacross-worker/core-primitives/networking-utils/Cargo.toml new file mode 100644 index 0000000000..c94ad8f685 --- /dev/null +++ b/bitacross-worker/core-primitives/networking-utils/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "itp-networking-utils" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +# sgx dependencies +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } + +[features] +default = ["std"] +std = [ + +] +sgx = [ + "sgx_tstd", +] diff --git a/bitacross-worker/core-primitives/networking-utils/src/lib.rs b/bitacross-worker/core-primitives/networking-utils/src/lib.rs new file mode 100644 index 0000000000..46b8ab91d3 --- /dev/null +++ b/bitacross-worker/core-primitives/networking-utils/src/lib.rs @@ -0,0 +1,26 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +pub mod ports; diff --git a/bitacross-worker/core-primitives/networking-utils/src/ports.rs b/bitacross-worker/core-primitives/networking-utils/src/ports.rs new file mode 100644 index 0000000000..4b8a523b27 --- /dev/null +++ b/bitacross-worker/core-primitives/networking-utils/src/ports.rs @@ -0,0 +1,48 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use std::{net::TcpListener, ops::Range}; + +/// Gets the first available port in a range. +/// Returns None if no port in range is available. +/// +pub fn get_available_port_in_range(mut port_range: Range) -> Option { + port_range.find(|port| port_is_available(*port)) +} + +fn port_is_available(port: u16) -> bool { + TcpListener::bind(("127.0.0.1", port)).is_ok() +} + +#[cfg(test)] +mod tests { + use super::*; + use std::mem::drop; + + #[test] + fn port_is_not_available_when_bound() { + let available_port = get_available_port_in_range(12000..13000).unwrap(); + + let tcp_listener = TcpListener::bind(("127.0.0.1", available_port)).unwrap(); + + assert!(!port_is_available(available_port)); + + drop(tcp_listener); + + assert!(port_is_available(available_port)); + } +} diff --git a/bitacross-worker/core-primitives/node-api/Cargo.toml b/bitacross-worker/core-primitives/node-api/Cargo.toml new file mode 100644 index 0000000000..b836b98427 --- /dev/null +++ b/bitacross-worker/core-primitives/node-api/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "itp-node-api" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +itp-api-client-extensions = { optional = true, path = "api-client-extensions" } +itp-api-client-types = { default-features = false, path = "api-client-types" } +itp-node-api-factory = { optional = true, path = "factory" } +itp-node-api-metadata = { default-features = false, path = "metadata" } +itp-node-api-metadata-provider = { default-features = false, path = "metadata-provider" } + +[features] +default = ["std"] +std = [ + "itp-api-client-extensions", + "itp-api-client-types/std", + "itp-node-api-factory", + "itp-node-api-metadata/std", + "itp-node-api-metadata-provider/std", +] +sgx = [ + "itp-node-api-metadata-provider/sgx", +] +mocks = [ + "itp-node-api-metadata/mocks", +] diff --git a/bitacross-worker/core-primitives/node-api/api-client-extensions/Cargo.toml b/bitacross-worker/core-primitives/node-api/api-client-extensions/Cargo.toml new file mode 100644 index 0000000000..8ebf52e504 --- /dev/null +++ b/bitacross-worker/core-primitives/node-api/api-client-extensions/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "itp-api-client-extensions" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] + +# substrate +sp-consensus-grandpa = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-runtime = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# scs +# `default-features = false` to remove the jsonrpsee dependency. +substrate-api-client = { default-features = false, features = ["std", "sync-api"], git = "https://github.com/scs/substrate-api-client.git", branch = "polkadot-v0.9.42-tag-v0.14.0" } + +# local deps +itp-api-client-types = { path = "../api-client-types" } +itp-types = { path = "../../types" } + +# litentry +hex = "0.4" +sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +[features] +# used for unit testing only! +mocks = [] diff --git a/bitacross-worker/core-primitives/node-api/api-client-extensions/src/account.rs b/bitacross-worker/core-primitives/node-api/api-client-extensions/src/account.rs new file mode 100644 index 0000000000..8834f942bb --- /dev/null +++ b/bitacross-worker/core-primitives/node-api/api-client-extensions/src/account.rs @@ -0,0 +1,54 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::ApiResult; +use itp_api_client_types::{ + traits::GetAccountInformation, Api, Config, ParentchainRuntimeConfig, Request, +}; + +/// ApiClient extension that contains some convenience methods around accounts. +// Todo: make generic over `Config` type instead? +pub trait AccountApi { + type AccountId; + type Index; + type Balance; + + fn get_nonce_of(&self, who: &Self::AccountId) -> ApiResult; + fn get_free_balance(&self, who: &Self::AccountId) -> ApiResult; + fn get_account_next_index(&self, who: &Self::AccountId) -> ApiResult; +} + +impl AccountApi for Api +where + Client: Request, +{ + type AccountId = ::AccountId; + type Index = ::Index; + type Balance = ::Balance; + + fn get_nonce_of(&self, who: &Self::AccountId) -> ApiResult { + Ok(self.get_account_info(who)?.map(|info| info.nonce).unwrap_or_default()) + } + + fn get_free_balance(&self, who: &Self::AccountId) -> ApiResult { + Ok(self.get_account_data(who)?.map(|data| data.free).unwrap_or_default()) + } + + fn get_account_next_index(&self, who: &Self::AccountId) -> ApiResult { + self.get_system_account_next_index(who.clone()) + } +} diff --git a/bitacross-worker/core-primitives/node-api/api-client-extensions/src/chain.rs b/bitacross-worker/core-primitives/node-api/api-client-extensions/src/chain.rs new file mode 100644 index 0000000000..89321b0034 --- /dev/null +++ b/bitacross-worker/core-primitives/node-api/api-client-extensions/src/chain.rs @@ -0,0 +1,142 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ApiClientError, ApiResult}; +use itp_api_client_types::{ + storage_key, + traits::{GetChainInfo, GetStorage}, + Api, Config, Request, StorageKey, +}; +use itp_types::parentchain::{BlockNumber, StorageProof}; +use sp_consensus_grandpa::{AuthorityList, VersionedAuthorityList, GRANDPA_AUTHORITIES_KEY}; +use sp_runtime::generic::SignedBlock as GenericSignedBlock; + +type RawEvents = Vec; + +/// ApiClient extension that simplifies chain data access. +pub trait ChainApi { + type Hash; + type Block; + type Header; + type BlockNumber; + + fn last_finalized_block(&self) -> ApiResult>>; + fn signed_block( + &self, + hash: Option, + ) -> ApiResult>>; + fn get_genesis_hash(&self) -> ApiResult; + fn header(&self, header_hash: Option) -> ApiResult>; + /// Fetch blocks from parentchain with blocknumber from until to, including both boundaries. + /// Returns a vector with one element if from equals to. + /// Returns an empty vector if from is greater than to. + fn get_blocks( + &self, + from: Self::BlockNumber, + to: Self::BlockNumber, + ) -> ApiResult>>; + fn is_grandpa_available(&self) -> ApiResult; + fn grandpa_authorities(&self, hash: Option) -> ApiResult; + fn grandpa_authorities_proof(&self, hash: Option) -> ApiResult; + fn get_events_value_proof(&self, block_hash: Option) -> ApiResult; + fn get_events_for_block(&self, block_hash: Option) -> ApiResult; +} + +impl ChainApi for Api +where + RuntimeConfig: Config, + Client: Request, +{ + type Hash = RuntimeConfig::Hash; + type Header = RuntimeConfig::Header; + type Block = RuntimeConfig::Block; + type BlockNumber = RuntimeConfig::BlockNumber; + + fn last_finalized_block(&self) -> ApiResult>> { + self.get_finalized_head()? + .map_or_else(|| Ok(None), |hash| self.signed_block(Some(hash))) + } + + fn signed_block( + &self, + hash: Option, + ) -> ApiResult>> { + Ok(self.get_signed_block(hash)?.map(|block| block.into())) + } + + fn get_genesis_hash(&self) -> ApiResult { + self.get_block_hash(Some(0u32))?.ok_or(ApiClientError::BlockHashNotFound) + } + + fn header(&self, header_hash: Option) -> ApiResult> { + self.get_header(header_hash) + } + + fn get_blocks( + &self, + from: Self::BlockNumber, + to: Self::BlockNumber, + ) -> ApiResult>> { + let mut blocks = Vec::>::new(); + + for n in from..=to { + if let Some(block) = self.get_signed_block_by_num(Some(n))? { + blocks.push(block.into()); + } + } + Ok(blocks) + } + + fn is_grandpa_available(&self) -> ApiResult { + let genesis_hash = Some(self.get_genesis_hash().expect("Failed to get genesis hash")); + Ok(self + .get_storage_by_key(StorageKey(GRANDPA_AUTHORITIES_KEY.to_vec()), genesis_hash)? + .map(|v: VersionedAuthorityList| v.into()) + .map(|v: AuthorityList| !v.is_empty()) + .unwrap_or(false)) + } + + fn grandpa_authorities(&self, at_block: Option) -> ApiResult { + Ok(self + .get_storage_by_key(StorageKey(GRANDPA_AUTHORITIES_KEY.to_vec()), at_block)? + .map(|g: VersionedAuthorityList| g.into()) + .unwrap_or_default()) + } + + fn grandpa_authorities_proof(&self, at_block: Option) -> ApiResult { + Ok(self + .get_storage_proof_by_keys( + vec![StorageKey(GRANDPA_AUTHORITIES_KEY.to_vec())], + at_block, + )? + .map(|read_proof| read_proof.proof.into_iter().map(|bytes| bytes.0).collect()) + .unwrap_or_default()) + } + + fn get_events_value_proof(&self, block_hash: Option) -> ApiResult { + let key = storage_key("System", "Events"); + Ok(self + .get_storage_proof_by_keys(Vec::from([key]), block_hash)? + .map(|read_proof| read_proof.proof.into_iter().map(|bytes| bytes.0).collect()) + .unwrap_or_default()) + } + + fn get_events_for_block(&self, block_hash: Option) -> ApiResult { + let key = storage_key("System", "Events"); + Ok(self.get_opaque_storage_by_key(key, block_hash)?.unwrap_or_default()) + } +} diff --git a/bitacross-worker/core-primitives/node-api/api-client-extensions/src/lib.rs b/bitacross-worker/core-primitives/node-api/api-client-extensions/src/lib.rs new file mode 100644 index 0000000000..2829b53c1c --- /dev/null +++ b/bitacross-worker/core-primitives/node-api/api-client-extensions/src/lib.rs @@ -0,0 +1,32 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Some substrate-api-client extension traits. + +pub use substrate_api_client::{api::Error as ApiClientError, rpc::TungsteniteRpcClient, Api}; + +pub mod account; +pub mod chain; +pub mod pallet_teeracle; +pub mod pallet_teerex; + +pub use account::*; +pub use chain::*; +pub use pallet_teeracle::*; +pub use pallet_teerex::*; + +pub type ApiResult = Result; diff --git a/bitacross-worker/core-primitives/node-api/api-client-extensions/src/pallet_teeracle.rs b/bitacross-worker/core-primitives/node-api/api-client-extensions/src/pallet_teeracle.rs new file mode 100644 index 0000000000..3f1ad2d198 --- /dev/null +++ b/bitacross-worker/core-primitives/node-api/api-client-extensions/src/pallet_teeracle.rs @@ -0,0 +1,19 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pub const TEERACLE: &str = "Teeracle"; +pub const ADD_TO_WHITELIST: &str = "add_to_whitelist"; diff --git a/bitacross-worker/core-primitives/node-api/api-client-extensions/src/pallet_teerex.rs b/bitacross-worker/core-primitives/node-api/api-client-extensions/src/pallet_teerex.rs new file mode 100644 index 0000000000..222e249402 --- /dev/null +++ b/bitacross-worker/core-primitives/node-api/api-client-extensions/src/pallet_teerex.rs @@ -0,0 +1,105 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::ApiResult; +use itp_api_client_types::{storage_key, traits::GetStorage, Api, Config, Request}; +use itp_types::{Enclave, IpfsHash, MrEnclave, ShardIdentifier}; +use sp_core::storage::StorageKey; + +pub const TEEREX: &str = "Teerex"; +pub const SIDECHAIN: &str = "Sidechain"; + +/// ApiClient extension that enables communication with the `teerex` pallet. +// Todo: make generic over `Config` type instead? +pub trait PalletTeerexApi { + type Hash; + + fn enclave(&self, index: u64, at_block: Option) -> ApiResult>; + fn enclave_count(&self, at_block: Option) -> ApiResult; + fn all_enclaves(&self, at_block: Option) -> ApiResult>; + fn worker_for_shard( + &self, + shard: &ShardIdentifier, + at_block: Option, + ) -> ApiResult>; + fn latest_ipfs_hash( + &self, + shard: &ShardIdentifier, + at_block: Option, + ) -> ApiResult>; + + // litentry + fn all_scheduled_mrenclaves(&self, at_block: Option) -> ApiResult>; +} + +impl PalletTeerexApi for Api +where + RuntimeConfig: Config, + Client: Request, +{ + type Hash = RuntimeConfig::Hash; + + fn enclave(&self, index: u64, at_block: Option) -> ApiResult> { + self.get_storage_map(TEEREX, "EnclaveRegistry", index, at_block) + } + + fn enclave_count(&self, at_block: Option) -> ApiResult { + Ok(self.get_storage(TEEREX, "EnclaveCount", at_block)?.unwrap_or(0u64)) + } + + fn all_enclaves(&self, at_block: Option) -> ApiResult> { + let count = self.enclave_count(at_block)?; + let mut enclaves = Vec::with_capacity(count as usize); + for n in 1..=count { + enclaves.push(self.enclave(n, at_block)?.expect("None enclave")) + } + Ok(enclaves) + } + + fn worker_for_shard( + &self, + shard: &ShardIdentifier, + at_block: Option, + ) -> ApiResult> { + self.get_storage_map(SIDECHAIN, "WorkerForShard", shard, at_block)? + .map_or_else(|| Ok(None), |w_index| self.enclave(w_index, at_block)) + } + + fn latest_ipfs_hash( + &self, + shard: &ShardIdentifier, + at_block: Option, + ) -> ApiResult> { + self.get_storage_map(TEEREX, "LatestIPFSHash", shard, at_block) + } + + fn all_scheduled_mrenclaves(&self, at_block: Option) -> ApiResult> { + let keys: Vec<_> = self + .get_keys(storage_key(TEEREX, "ScheduledEnclave"), at_block)? + .unwrap_or_default() + .iter() + .map(|key| { + let key = key.strip_prefix("0x").unwrap_or(key); + let raw_key = hex::decode(key).unwrap(); + self.get_storage_by_key::(StorageKey(raw_key).into(), at_block) + }) + .filter(|enclave| matches!(enclave, Ok(Some(_)))) + .map(|enclave| enclave.unwrap().unwrap()) + .collect(); + Ok(keys) + } +} diff --git a/bitacross-worker/core-primitives/node-api/api-client-extensions/src/pallet_teerex_api_mock.rs b/bitacross-worker/core-primitives/node-api/api-client-extensions/src/pallet_teerex_api_mock.rs new file mode 100644 index 0000000000..df5bf3646f --- /dev/null +++ b/bitacross-worker/core-primitives/node-api/api-client-extensions/src/pallet_teerex_api_mock.rs @@ -0,0 +1,70 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{pallet_teerex::PalletTeerexApi, ApiResult}; +use itp_types::{parentchain::Hash, AccountId, IpfsHash, MrEnclave, MultiEnclave, ShardIdentifier}; +use std::collections::HashMap; + +#[derive(Default)] +pub struct PalletTeerexApiMock { + registered_enclaves: HashMap>>, +} + +impl PalletTeerexApiMock { + pub fn with_enclaves(mut self, enclaves: Vec>>) -> Self { + enclaves.iter().map(|enclave| self.registered_enclaves.insert(enclave)); + self + } +} + +impl PalletTeerexApi for PalletTeerexApiMock { + fn enclave( + &self, + account: AccountId, + _at_block: Option, + ) -> ApiResult>>> { + Ok(self.registered_enclaves.get(index as usize).cloned()) + } + + fn enclave_count(&self, _at_block: Option) -> ApiResult { + Ok(self.registered_enclaves.len() as u64) + } + + fn all_enclaves(&self, _at_block: Option) -> ApiResult>>> { + Ok(self.registered_enclaves.clone()) + } + + fn primary_worker_for_shard( + &self, + _shard: &ShardIdentifier, + _at_block: Option, + ) -> ApiResult>>> { + todo!() + } + + fn latest_ipfs_hash( + &self, + _shard: &ShardIdentifier, + _at_block: Option, + ) -> ApiResult> { + todo!() + } + + fn all_scheduled_mrenclaves(&self, _at_block: Option) -> ApiResult> { + Ok(self.registered_enclaves.iter().map(|k| k.mr_enclave).collect()) + } +} diff --git a/bitacross-worker/core-primitives/node-api/api-client-types/Cargo.toml b/bitacross-worker/core-primitives/node-api/api-client-types/Cargo.toml new file mode 100644 index 0000000000..babeae9d38 --- /dev/null +++ b/bitacross-worker/core-primitives/node-api/api-client-types/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "itp-api-client-types" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +# scs +substrate-api-client = { default-features = false, features = ["sync-api"], git = "https://github.com/scs/substrate-api-client.git", branch = "polkadot-v0.9.42-tag-v0.14.0" } + +# substrate +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# local +itp-types = { default-features = false, path = "../../types" } + +# litentry +my-node-runtime = { package = "rococo-parachain-runtime", path = "../../../../runtime/rococo", optional = true } + +[features] +default = ["std"] +std = [ + "itp-types/std", + "substrate-api-client/std", + "substrate-api-client/tungstenite-client", + "sp-runtime/std", + "my-node-runtime/std", +] diff --git a/bitacross-worker/core-primitives/node-api/api-client-types/src/lib.rs b/bitacross-worker/core-primitives/node-api/api-client-types/src/lib.rs new file mode 100644 index 0000000000..f3bee4590d --- /dev/null +++ b/bitacross-worker/core-primitives/node-api/api-client-types/src/lib.rs @@ -0,0 +1,98 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Contains type definitions to talk to the node. +//! +//! You need to update this if you have a signed extension in your node that +//! is different from the integritee-node, e.g., if you use the `pallet_asset_tx_payment`. + +#![cfg_attr(not(feature = "std"), no_std)] + +pub use itp_types::parentchain::{ + AccountData, AccountId, AccountInfo, Address, Balance, Hash, Index, Signature as PairSignature, +}; +pub use substrate_api_client::{ + ac_node_api::{ + metadata::{InvalidMetadataError, Metadata, MetadataError}, + EventDetails, Events, StaticEvent, + }, + ac_primitives::{ + config::{AssetRuntimeConfig, Config, DefaultRuntimeConfig}, + extrinsics::{ + AssetTip, CallIndex, ExtrinsicParams, GenericAdditionalParams, GenericAdditionalSigned, + GenericExtrinsicParams, GenericSignedExtra, PlainTip, UncheckedExtrinsicV4, + }, + serde_impls::StorageKey, + signer::{SignExtrinsic, StaticExtrinsicSigner}, + }, + rpc::Request, + storage_key, Api, +}; + +// traits from the api-client +pub mod traits { + pub use substrate_api_client::{GetAccountInformation, GetChainInfo, GetStorage}; +} + +pub type ParentchainPlainTip = PlainTip; +pub type ParentchainAssetTip = AssetTip; + +/// Configuration for the ExtrinsicParams. +/// +/// Valid for the default integritee node +pub type ParentchainExtrinsicParams = + GenericExtrinsicParams; +pub type ParentchainAdditionalParams = GenericAdditionalParams; +pub use DefaultRuntimeConfig as ParentchainRuntimeConfig; + +// Pay in asset fees. +// +// This needs to be used if the node uses the `pallet_asset_tx_payment`. +//pub type ParentchainExtrinsicParams = GenericExtrinsicParams; +// pub type ParentchainAdditionalParams = GenericAdditionalParams; + +pub type ParentchainUncheckedExtrinsic = + UncheckedExtrinsicV4; +pub type ParentchainSignedExtra = GenericSignedExtra; +pub type ParentchainSignature = Signature; + +/// Signature type of the [UncheckedExtrinsicV4]. +pub type Signature = Option<(Address, PairSignature, SignedExtra)>; + +#[cfg(feature = "std")] +pub use api::*; + +#[cfg(feature = "std")] +mod api { + use super::ParentchainRuntimeConfig; + use sp_runtime::generic::SignedBlock as GenericSignedBlock; + use substrate_api_client::Api; + + // We should probably switch to the opaque block, then we can get rid of the + // runtime dependency here. + // pub use itp_types::Block; + pub use my_node_runtime::{Block, Runtime, UncheckedExtrinsic}; + + pub use substrate_api_client::{ + api::Error as ApiClientError, + rpc::{tungstenite_client::TungsteniteRpcClient, Error as RpcClientError}, + }; + + pub type SignedBlock = GenericSignedBlock; + + pub type ParentchainApi = Api; +} diff --git a/bitacross-worker/core-primitives/node-api/factory/Cargo.toml b/bitacross-worker/core-primitives/node-api/factory/Cargo.toml new file mode 100644 index 0000000000..dc6084a8d9 --- /dev/null +++ b/bitacross-worker/core-primitives/node-api/factory/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "itp-node-api-factory" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +thiserror = { version = "1.0" } + +# substrate +sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# local +itp-api-client-types = { path = "../api-client-types" } diff --git a/bitacross-worker/core-primitives/node-api/factory/src/lib.rs b/bitacross-worker/core-primitives/node-api/factory/src/lib.rs new file mode 100644 index 0000000000..2afea5f423 --- /dev/null +++ b/bitacross-worker/core-primitives/node-api/factory/src/lib.rs @@ -0,0 +1,73 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use itp_api_client_types::{ParentchainApi, TungsteniteRpcClient}; +use sp_core::sr25519; + +/// Trait to create a node API, based on a node URL and signer. +pub trait CreateNodeApi: Send + Sync + 'static { + fn create_api(&self) -> Result; +} + +/// Node API factory error. +#[derive(Debug, thiserror::Error)] +pub enum NodeApiFactoryError { + #[error("Could not connect to node with rpc client")] + FailedToCreateRpcClient(itp_api_client_types::RpcClientError), + #[error("Failed to create a node API")] + FailedToCreateNodeApi(itp_api_client_types::ApiClientError), + #[error(transparent)] + Other(#[from] Box), +} + +impl From for NodeApiFactoryError { + fn from(error: itp_api_client_types::RpcClientError) -> Self { + NodeApiFactoryError::FailedToCreateRpcClient(error) + } +} + +impl From for NodeApiFactoryError { + fn from(error: itp_api_client_types::ApiClientError) -> Self { + NodeApiFactoryError::FailedToCreateNodeApi(error) + } +} + +pub type Result = std::result::Result; + +/// Node API factory implementation. +pub struct NodeApiFactory { + node_url: String, + signer: sr25519::Pair, +} + +impl NodeApiFactory { + pub fn new(url: String, signer: sr25519::Pair) -> Self { + NodeApiFactory { node_url: url, signer } + } +} + +impl CreateNodeApi for NodeApiFactory { + fn create_api(&self) -> Result { + let rpc_client = TungsteniteRpcClient::new(self.node_url.as_str(), 5) + .map_err(NodeApiFactoryError::FailedToCreateRpcClient)?; + let mut api = + ParentchainApi::new(rpc_client).map_err(NodeApiFactoryError::FailedToCreateNodeApi)?; + api.set_signer(self.signer.clone().into()); + Ok(api) + } +} diff --git a/bitacross-worker/core-primitives/node-api/metadata-provider/Cargo.toml b/bitacross-worker/core-primitives/node-api/metadata-provider/Cargo.toml new file mode 100644 index 0000000000..dfcfd8f3f4 --- /dev/null +++ b/bitacross-worker/core-primitives/node-api/metadata-provider/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "itp-node-api-metadata-provider" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +# crates.io + +# sgx dependencies +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } + +# sgx enabled external libraries +thiserror_sgx = { optional = true, package = "thiserror", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3" } + +# std compatible external libraries (make sure these versions match with the sgx-enabled ones above) +thiserror = { version = "1.0", optional = true } + +# local dependencies +itp-node-api-metadata = { default-features = false, path = "../metadata" } +itp-stf-primitives = { default-features = false, path = "../../stf-primitives" } + +[features] +default = ["std"] +std = [ + "thiserror", + "itp-stf-primitives/std", +] +sgx = [ + "sgx_tstd", + "thiserror_sgx", +] +# used for unit testing only! +mocks = [] diff --git a/bitacross-worker/core-primitives/node-api/metadata-provider/src/error.rs b/bitacross-worker/core-primitives/node-api/metadata-provider/src/error.rs new file mode 100644 index 0000000000..fc45ff5f92 --- /dev/null +++ b/bitacross-worker/core-primitives/node-api/metadata-provider/src/error.rs @@ -0,0 +1,45 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(feature = "sgx")] +extern crate thiserror_sgx as thiserror; + +use itp_stf_primitives::error::StfError; + +#[derive(Debug, PartialEq, Eq, thiserror::Error)] +pub enum Error { + /// Metadata has not been set + #[error("Metadata has no been set")] + MetadataNotSet, + /// Node metadata error + #[error("Metadata Error: {0:?}")] + MetadataError(itp_node_api_metadata::error::Error), +} + +pub type Result = core::result::Result; + +impl From for Error { + fn from(e: itp_node_api_metadata::error::Error) -> Self { + Self::MetadataError(e) + } +} + +impl From for StfError { + fn from(_e: Error) -> Self { + StfError::InvalidMetadata + } +} diff --git a/bitacross-worker/core-primitives/node-api/metadata-provider/src/lib.rs b/bitacross-worker/core-primitives/node-api/metadata-provider/src/lib.rs new file mode 100644 index 0000000000..9d2f16d54d --- /dev/null +++ b/bitacross-worker/core-primitives/node-api/metadata-provider/src/lib.rs @@ -0,0 +1,114 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Minimalistic crate for global metadata access withing the enclave. + +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(test, feature(assert_matches))] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(feature = "sgx")] +extern crate sgx_tstd as std; + +#[cfg(feature = "sgx")] +use std::sync::SgxRwLock as RwLock; + +#[cfg(feature = "std")] +use std::sync::RwLock; + +pub use crate::error::Error; + +use crate::error::Result; +use std::ops::Deref; + +pub mod error; + +/// Trait to get access to the node API metadata. +pub trait AccessNodeMetadata { + type MetadataType; + + fn get_from_metadata(&self, getter_function: F) -> Result + where + F: FnOnce(&Self::MetadataType) -> R; +} + +/// Repository to manage the node metadata. +/// +/// Provides simple means to set the metadata and read from it, guarded by a lock. +#[derive(Default)] +pub struct NodeMetadataRepository { + metadata_lock: RwLock>, +} + +impl NodeMetadataRepository { + pub fn new(metadata: NodeMetadata) -> Self { + NodeMetadataRepository { metadata_lock: RwLock::new(Some(metadata)) } + } + + pub fn set_metadata(&self, metadata: NodeMetadata) { + let mut metadata_lock = self.metadata_lock.write().expect("Lock poisoning"); + *metadata_lock = Some(metadata) + } +} + +impl AccessNodeMetadata for NodeMetadataRepository +where + NodeMetadata:, +{ + type MetadataType = NodeMetadata; + + fn get_from_metadata(&self, getter_function: F) -> Result + where + F: FnOnce(&Self::MetadataType) -> R, + { + match self.metadata_lock.read().expect("Lock poisoning").deref() { + Some(metadata) => Ok(getter_function(metadata)), + None => Err(Error::MetadataNotSet), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::assert_matches::assert_matches; + + #[derive(Default)] + struct NodeMetadataMock; + + impl NodeMetadataMock { + fn get_one(&self) -> u32 { + 1 + } + } + #[test] + fn get_from_meta_data_returns_error_if_not_set() { + let repo = NodeMetadataRepository::::default(); + + assert_matches!(repo.get_from_metadata(|m| m.get_one()), Err(Error::MetadataNotSet)); + } + + #[test] + fn get_from_metadata_works() { + let repo = NodeMetadataRepository::::default(); + repo.set_metadata(NodeMetadataMock); + + assert_eq!(1, repo.get_from_metadata(|m| m.get_one()).unwrap()); + } +} diff --git a/bitacross-worker/core-primitives/node-api/metadata/Cargo.toml b/bitacross-worker/core-primitives/node-api/metadata/Cargo.toml new file mode 100644 index 0000000000..2e6e9f3268 --- /dev/null +++ b/bitacross-worker/core-primitives/node-api/metadata/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "itp-node-api-metadata" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +# crates.io +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +derive_more = { version = "0.99.5" } + +# local +itp-api-client-types = { default-features = false, path = "../api-client-types" } +itp-stf-primitives = { default-features = false, path = "../../stf-primitives" } + +# substrate +sp-core = { git = "https://github.com/paritytech/substrate.git", default-features = false, branch = "polkadot-v0.9.42" } + +[features] +default = ["std"] +std = [ + "codec/std", + "itp-api-client-types/std", + "sp-core/std", + "itp-stf-primitives/std", +] + +# used for unit testing only! +mocks = [] diff --git a/bitacross-worker/core-primitives/node-api/metadata/src/error.rs b/bitacross-worker/core-primitives/node-api/metadata/src/error.rs new file mode 100644 index 0000000000..c0bcf39355 --- /dev/null +++ b/bitacross-worker/core-primitives/node-api/metadata/src/error.rs @@ -0,0 +1,37 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +use derive_more::From; +use itp_stf_primitives::error::StfError; + +#[derive(Debug, PartialEq, Eq, From)] +pub enum Error { + /// Metadata has not been set + MetadataNotSet, + /// Api-client metadata error + NodeMetadata(itp_api_client_types::MetadataError), + // litentry + /// Invalid Metadata + InvalidMetadata, +} + +pub type Result = core::result::Result; + +impl From for StfError { + fn from(_e: Error) -> Self { + StfError::InvalidMetadata + } +} diff --git a/bitacross-worker/core-primitives/node-api/metadata/src/lib.rs b/bitacross-worker/core-primitives/node-api/metadata/src/lib.rs new file mode 100644 index 0000000000..0a069c0277 --- /dev/null +++ b/bitacross-worker/core-primitives/node-api/metadata/src/lib.rs @@ -0,0 +1,173 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Handle SGX compatible substrate chain metadata. + +#![cfg_attr(not(feature = "std"), no_std)] + +use crate::{ + error::Result, pallet_balances::BalancesCallIndexes, pallet_imp::IMPCallIndexes, + pallet_proxy::ProxyCallIndexes, pallet_sidechain::SidechainCallIndexes, + pallet_system::SystemSs58Prefix, pallet_teerex::TeerexCallIndexes, + pallet_utility::UtilityCallIndexes, pallet_vcmp::VCMPCallIndexes, +}; +use codec::{Decode, Encode}; +use sp_core::storage::StorageKey; + +pub use crate::error::Error; +pub use itp_api_client_types::{Metadata, MetadataError}; + +pub mod error; +pub mod pallet_balances; +pub mod pallet_imp; +pub mod pallet_proxy; +pub mod pallet_sidechain; +pub mod pallet_system; +pub mod pallet_teeracle; +pub mod pallet_teerex; +pub mod pallet_utility; +pub mod pallet_vcmp; +pub mod runtime_call; + +#[cfg(feature = "mocks")] +pub mod metadata_mocks; + +pub trait NodeMetadataTrait: + TeerexCallIndexes + + SidechainCallIndexes + + IMPCallIndexes + + VCMPCallIndexes + + SystemSs58Prefix + + UtilityCallIndexes + + ProxyCallIndexes + + BalancesCallIndexes +{ +} +impl< + T: TeerexCallIndexes + + SidechainCallIndexes + + IMPCallIndexes + + VCMPCallIndexes + + SystemSs58Prefix + + UtilityCallIndexes + + ProxyCallIndexes + + BalancesCallIndexes, + > NodeMetadataTrait for T +{ +} + +impl TryFrom for Metadata { + type Error = crate::error::Error; + + fn try_from(value: NodeMetadata) -> core::result::Result { + value.node_metadata.ok_or(Error::MetadataNotSet) + } +} + +#[derive(Default, Encode, Decode, Debug, Clone)] +pub struct NodeMetadata { + node_metadata: Option, + runtime_spec_version: u32, + runtime_transaction_version: u32, +} + +impl NodeMetadata { + pub fn new( + node_metadata: Metadata, + runtime_spec_version: u32, + runtime_transaction_version: u32, + ) -> Self { + Self { + node_metadata: Some(node_metadata), + runtime_spec_version, + runtime_transaction_version, + } + } + /// Return the substrate chain runtime version. + pub fn get_runtime_version(&self) -> u32 { + self.runtime_spec_version + } + + /// Return the substrate chain runtime transaction version. + pub fn get_runtime_transaction_version(&self) -> u32 { + self.runtime_transaction_version + } + + /// Generic call indexes: + /// Get the array [pallet index, call index] corresponding to a pallet's call over the metadata. + pub fn call_indexes( + &self, + pallet_name: &'static str, + call_name: &'static str, + ) -> Result<[u8; 2]> { + let pallet = match &self.node_metadata { + None => return Err(Error::MetadataNotSet), + Some(m) => m.pallet_by_name_err(pallet_name)?, + }; + let call_index = pallet + .call_variant_by_name(call_name) + .ok_or(Error::NodeMetadata(MetadataError::CallNotFound(call_name)))?; + Ok([pallet.index(), call_index.index]) + } + + /// Generic storages: + /// Get the storage keys corresponding to a storage over the metadata: + pub fn storage_value_key( + &self, + storage_prefix: &'static str, + storage_key_name: &'static str, + ) -> Result { + match &self.node_metadata { + None => Err(Error::MetadataNotSet), + Some(m) => m + .storage_value_key(storage_prefix, storage_key_name) + .map(|key| key.into()) + .map_err(Error::NodeMetadata), + } + } + + pub fn storage_map_key( + &self, + storage_prefix: &'static str, + storage_key_name: &'static str, + map_key: K, + ) -> Result { + match &self.node_metadata { + None => Err(Error::MetadataNotSet), + Some(m) => m + .storage_map_key::(storage_prefix, storage_key_name, map_key) + .map(|key| key.into()) + .map_err(Error::NodeMetadata), + } + } + + pub fn storage_double_map_key( + &self, + storage_prefix: &'static str, + storage_key_name: &'static str, + first: K, + second: Q, + ) -> Result { + match &self.node_metadata { + None => Err(Error::MetadataNotSet), + Some(m) => m + .storage_double_map_key(storage_prefix, storage_key_name, first, second) + .map(|key| key.into()) + .map_err(Error::NodeMetadata), + } + } +} diff --git a/bitacross-worker/core-primitives/node-api/metadata/src/metadata_mocks.rs b/bitacross-worker/core-primitives/node-api/metadata/src/metadata_mocks.rs new file mode 100644 index 0000000000..cdf24e4fcc --- /dev/null +++ b/bitacross-worker/core-primitives/node-api/metadata/src/metadata_mocks.rs @@ -0,0 +1,317 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + error::Result, pallet_balances::BalancesCallIndexes, pallet_imp::IMPCallIndexes, + pallet_proxy::ProxyCallIndexes, pallet_sidechain::SidechainCallIndexes, + pallet_system::SystemSs58Prefix, pallet_teerex::TeerexCallIndexes, + pallet_utility::UtilityCallIndexes, pallet_vcmp::VCMPCallIndexes, runtime_call::RuntimeCall, +}; +use codec::{Decode, Encode}; + +use itp_api_client_types::Metadata; + +impl TryFrom for Metadata { + type Error = (); + + fn try_from(_: NodeMetadataMock) -> core::result::Result { + Err(()) + } +} + +#[derive(Default, Encode, Decode, Debug, Clone)] +pub struct NodeMetadataMock { + teerex_module: u8, + register_enclave: u8, + unregister_sovereign_enclave: u8, + unregister_proxied_enclave: u8, + register_quoting_enclave: u8, + register_tcb_info: u8, + enclave_bridge_module: u8, + invoke: u8, + confirm_processed_parentchain_block: u8, + shield_funds: u8, + unshield_funds: u8, + publish_hash: u8, + update_shard_config: u8, + sidechain_module: u8, + // litentry + update_scheduled_enclave: u8, + remove_scheduled_enclave: u8, + // IMP + imp_module: u8, + imp_link_identity: u8, + imp_deactivate_identity: u8, + imp_activate_identity: u8, + imp_update_id_graph_hash: u8, + imp_identity_linked: u8, + imp_identity_deactivated: u8, + imp_identity_activated: u8, + imp_identity_networks_set: u8, + imp_some_error: u8, + // VCMP + vcmp_module: u8, + vcmp_request_vc: u8, + vcmp_vc_issued: u8, + vcmp_some_error: u8, + + utility_module: u8, + utility_batch: u8, + utility_as_derivative: u8, + utility_batch_all: u8, + utility_dispatch_as: u8, + utility_force_batch: u8, + + imported_sidechain_block: u8, + proxy_module: u8, + add_proxy: u8, + proxy: u8, + balances_module: u8, + transfer: u8, + transfer_keep_alive: u8, + transfer_allow_death: u8, + runtime_spec_version: u32, + runtime_transaction_version: u32, +} + +impl NodeMetadataMock { + pub fn new() -> Self { + NodeMetadataMock { + teerex_module: 50u8, + register_enclave: 0u8, + unregister_sovereign_enclave: 1u8, + unregister_proxied_enclave: 2u8, + register_quoting_enclave: 3, + register_tcb_info: 4, + enclave_bridge_module: 54u8, + invoke: 0u8, + confirm_processed_parentchain_block: 1u8, + shield_funds: 2u8, + unshield_funds: 3u8, + publish_hash: 4u8, + update_shard_config: 5u8, + sidechain_module: 53u8, + // litentry + update_scheduled_enclave: 10u8, + remove_scheduled_enclave: 11u8, + + imp_module: 64u8, + imp_link_identity: 1u8, + imp_deactivate_identity: 2u8, + imp_activate_identity: 3u8, + imp_update_id_graph_hash: 4u8, + imp_identity_linked: 6u8, + imp_identity_deactivated: 7u8, + imp_identity_activated: 8u8, + imp_identity_networks_set: 9u8, + imp_some_error: 10u8, + + vcmp_module: 66u8, + vcmp_request_vc: 0u8, + vcmp_vc_issued: 3u8, + vcmp_some_error: 9u8, + + utility_module: 80u8, + utility_batch: 0u8, + utility_as_derivative: 1u8, + utility_batch_all: 2u8, + utility_dispatch_as: 3u8, + utility_force_batch: 4u8, + + imported_sidechain_block: 0u8, + proxy_module: 7u8, + add_proxy: 1u8, + proxy: 0u8, + balances_module: 10u8, + transfer: 7u8, + transfer_keep_alive: 3u8, + transfer_allow_death: 0u8, + runtime_spec_version: 25, + runtime_transaction_version: 4, + } + } +} + +impl TeerexCallIndexes for NodeMetadataMock { + fn register_enclave_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.teerex_module, self.register_enclave]) + } + + fn unregister_sovereign_enclave_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.teerex_module, self.unregister_sovereign_enclave]) + } + + fn unregister_proxied_enclave_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.teerex_module, self.unregister_proxied_enclave]) + } + + fn register_quoting_enclave_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.teerex_module, self.register_quoting_enclave]) + } + + fn register_tcb_info_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.teerex_module, self.register_tcb_info]) + } + + fn invoke_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.teerex_module, self.invoke]) + } + + fn confirm_processed_parentchain_block_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.teerex_module, self.confirm_processed_parentchain_block]) + } + + fn shield_funds_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.teerex_module, self.shield_funds]) + } + + fn unshield_funds_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.teerex_module, self.unshield_funds]) + } + + fn publish_hash_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.teerex_module, self.publish_hash]) + } + + // fn update_shard_config_call_indexes(&self) -> Result<[u8; 2]> { + // Ok([self.teerex_module, self.update_shard_config]) + // } + + fn update_scheduled_enclave(&self) -> Result<[u8; 2]> { + Ok([self.teerex_module, self.update_scheduled_enclave]) + } + + fn remove_scheduled_enclave(&self) -> Result<[u8; 2]> { + Ok([self.teerex_module, self.remove_scheduled_enclave]) + } +} + +impl SidechainCallIndexes for NodeMetadataMock { + fn confirm_imported_sidechain_block_indexes(&self) -> Result<[u8; 2]> { + Ok([self.sidechain_module, self.imported_sidechain_block]) + } +} + +impl IMPCallIndexes for NodeMetadataMock { + fn link_identity_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.imp_module, self.imp_link_identity]) + } + + fn deactivate_identity_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.imp_module, self.imp_deactivate_identity]) + } + + fn activate_identity_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.imp_module, self.imp_activate_identity]) + } + + fn update_id_graph_hash_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.imp_module, self.imp_update_id_graph_hash]) + } + + fn identity_linked_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.imp_module, self.imp_identity_linked]) + } + + fn identity_deactivated_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.imp_module, self.imp_identity_deactivated]) + } + + fn identity_activated_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.imp_module, self.imp_identity_activated]) + } + + fn identity_networks_set_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.imp_module, self.imp_identity_networks_set]) + } + + fn imp_some_error_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.imp_module, self.imp_some_error]) + } +} + +impl VCMPCallIndexes for NodeMetadataMock { + fn request_vc_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.vcmp_module, self.vcmp_request_vc]) + } + + fn vc_issued_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.vcmp_module, self.vcmp_vc_issued]) + } + + fn vcmp_some_error_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.vcmp_module, self.vcmp_some_error]) + } +} + +impl UtilityCallIndexes for NodeMetadataMock { + fn batch_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.utility_module, self.utility_batch]) + } + + fn as_derivative_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.utility_module, self.utility_as_derivative]) + } + + fn batch_all_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.utility_module, self.utility_batch_all]) + } + + fn dispatch_as_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.utility_module, self.utility_dispatch_as]) + } + + fn force_batch_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.utility_module, self.utility_force_batch]) + } +} + +impl RuntimeCall for NodeMetadataMock { + fn retrieve(&self) -> Result { + Err(crate::Error::MetadataNotSet) + } +} + +impl SystemSs58Prefix for NodeMetadataMock { + fn system_ss58_prefix(&self) -> Result { + Ok(131) + } +} + +impl ProxyCallIndexes for NodeMetadataMock { + fn add_proxy_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.proxy_module, self.add_proxy]) + } + + fn proxy_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.proxy_module, self.proxy]) + } +} + +impl BalancesCallIndexes for NodeMetadataMock { + fn transfer_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.balances_module, self.transfer]) + } + + fn transfer_keep_alive_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.balances_module, self.transfer_keep_alive]) + } + + fn transfer_allow_death_call_indexes(&self) -> Result<[u8; 2]> { + Ok([self.balances_module, self.transfer_allow_death]) + } +} diff --git a/bitacross-worker/core-primitives/node-api/metadata/src/pallet_balances.rs b/bitacross-worker/core-primitives/node-api/metadata/src/pallet_balances.rs new file mode 100644 index 0000000000..9ae88dd742 --- /dev/null +++ b/bitacross-worker/core-primitives/node-api/metadata/src/pallet_balances.rs @@ -0,0 +1,43 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{error::Result, NodeMetadata}; + +/// Pallet name: +const BALANCES: &str = "Balances"; + +pub trait BalancesCallIndexes { + fn transfer_call_indexes(&self) -> Result<[u8; 2]>; + + fn transfer_keep_alive_call_indexes(&self) -> Result<[u8; 2]>; + + fn transfer_allow_death_call_indexes(&self) -> Result<[u8; 2]>; +} + +impl BalancesCallIndexes for NodeMetadata { + fn transfer_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(BALANCES, "transfer") + } + + fn transfer_keep_alive_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(BALANCES, "transfer_keep_alive") + } + + fn transfer_allow_death_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(BALANCES, "transfer_allow_death") + } +} diff --git a/bitacross-worker/core-primitives/node-api/metadata/src/pallet_imp.rs b/bitacross-worker/core-primitives/node-api/metadata/src/pallet_imp.rs new file mode 100644 index 0000000000..636d93cdab --- /dev/null +++ b/bitacross-worker/core-primitives/node-api/metadata/src/pallet_imp.rs @@ -0,0 +1,71 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +// TODO: maybe use macros to simplify this +use crate::{error::Result, NodeMetadata}; + +/// Pallet' name: +const IMP: &str = "IdentityManagement"; + +pub trait IMPCallIndexes { + fn link_identity_call_indexes(&self) -> Result<[u8; 2]>; + fn deactivate_identity_call_indexes(&self) -> Result<[u8; 2]>; + fn activate_identity_call_indexes(&self) -> Result<[u8; 2]>; + fn update_id_graph_hash_call_indexes(&self) -> Result<[u8; 2]>; + fn identity_linked_call_indexes(&self) -> Result<[u8; 2]>; + fn identity_deactivated_call_indexes(&self) -> Result<[u8; 2]>; + fn identity_activated_call_indexes(&self) -> Result<[u8; 2]>; + fn identity_networks_set_call_indexes(&self) -> Result<[u8; 2]>; + fn imp_some_error_call_indexes(&self) -> Result<[u8; 2]>; +} + +impl IMPCallIndexes for NodeMetadata { + fn link_identity_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(IMP, "link_identity") + } + + fn deactivate_identity_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(IMP, "deactivate_identity") + } + + fn activate_identity_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(IMP, "activate_identity") + } + + fn update_id_graph_hash_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(IMP, "update_id_graph_hash") + } + + fn identity_linked_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(IMP, "identity_linked") + } + + fn identity_deactivated_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(IMP, "identity_deactivated") + } + + fn identity_activated_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(IMP, "identity_activated") + } + + fn identity_networks_set_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(IMP, "identity_networks_set") + } + + fn imp_some_error_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(IMP, "some_error") + } +} diff --git a/bitacross-worker/core-primitives/node-api/metadata/src/pallet_proxy.rs b/bitacross-worker/core-primitives/node-api/metadata/src/pallet_proxy.rs new file mode 100644 index 0000000000..6a7aa14b08 --- /dev/null +++ b/bitacross-worker/core-primitives/node-api/metadata/src/pallet_proxy.rs @@ -0,0 +1,39 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{error::Result, NodeMetadata}; + +/// Pallet name: +const PROXY: &str = "Proxy"; +/// the deposit needed to register up to 20 proxies in native parentchain token +pub const PROXY_DEPOSIT: u128 = 21_000_000_000_000; + +pub trait ProxyCallIndexes { + fn add_proxy_call_indexes(&self) -> Result<[u8; 2]>; + + fn proxy_call_indexes(&self) -> Result<[u8; 2]>; +} + +impl ProxyCallIndexes for NodeMetadata { + fn add_proxy_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(PROXY, "add_proxy") + } + + fn proxy_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(PROXY, "proxy") + } +} diff --git a/bitacross-worker/core-primitives/node-api/metadata/src/pallet_sidechain.rs b/bitacross-worker/core-primitives/node-api/metadata/src/pallet_sidechain.rs new file mode 100644 index 0000000000..c014227dd9 --- /dev/null +++ b/bitacross-worker/core-primitives/node-api/metadata/src/pallet_sidechain.rs @@ -0,0 +1,30 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{error::Result, NodeMetadata}; +/// Pallet' name: +pub const SIDECHAIN: &str = "Sidechain"; + +pub trait SidechainCallIndexes { + fn confirm_imported_sidechain_block_indexes(&self) -> Result<[u8; 2]>; +} + +impl SidechainCallIndexes for NodeMetadata { + fn confirm_imported_sidechain_block_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(SIDECHAIN, "confirm_imported_sidechain_block") + } +} diff --git a/bitacross-worker/core-primitives/node-api/metadata/src/pallet_system.rs b/bitacross-worker/core-primitives/node-api/metadata/src/pallet_system.rs new file mode 100644 index 0000000000..5005fdbecb --- /dev/null +++ b/bitacross-worker/core-primitives/node-api/metadata/src/pallet_system.rs @@ -0,0 +1,52 @@ +/* +Copyright 2021 Integritee AG and Supercomputing Systems AG +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +use crate::{error::Result, Error, NodeMetadata}; +use codec::Decode; +use sp_core::storage::StorageKey; + +/// Pallet' name: +const SYSTEM: &str = "System"; + +pub trait SystemStorageIndexes { + fn system_account_storage_key(&self) -> Result; + + fn system_account_storage_map_key(&self, index: u64) -> Result; +} + +impl SystemStorageIndexes for NodeMetadata { + fn system_account_storage_key(&self) -> Result { + self.storage_value_key(SYSTEM, "Account") + } + + fn system_account_storage_map_key(&self, index: u64) -> Result { + self.storage_map_key(SYSTEM, "Account", index) + } +} + +// litentry +pub trait SystemSs58Prefix { + fn system_ss58_prefix(&self) -> Result; +} + +impl SystemSs58Prefix for NodeMetadata { + fn system_ss58_prefix(&self) -> Result { + match &self.node_metadata { + None => Err(Error::MetadataNotSet), + Some(meta_data) => { + let pallet = meta_data.pallet_by_name(SYSTEM).ok_or(Error::MetadataNotSet)?; + let mut raw = pallet.constant_by_name("SS58Prefix").unwrap().value.as_slice(); + u16::decode(&mut raw).map_err(|_| Error::InvalidMetadata) + }, + } + } +} diff --git a/bitacross-worker/core-primitives/node-api/metadata/src/pallet_teeracle.rs b/bitacross-worker/core-primitives/node-api/metadata/src/pallet_teeracle.rs new file mode 100644 index 0000000000..0d10003514 --- /dev/null +++ b/bitacross-worker/core-primitives/node-api/metadata/src/pallet_teeracle.rs @@ -0,0 +1,46 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{error::Result, NodeMetadata}; + +/// Pallet' name: +pub const TEERACLE: &str = "Teeracle"; + +pub trait TeeracleCallIndexes { + fn add_to_whitelist_call_indexes(&self) -> Result<[u8; 2]>; + fn remove_from_whitelist_call_indexes(&self) -> Result<[u8; 2]>; + fn update_exchange_rate_call_indexes(&self) -> Result<[u8; 2]>; + fn update_oracle_call_indexes(&self) -> Result<[u8; 2]>; +} + +impl TeeracleCallIndexes for NodeMetadata { + fn add_to_whitelist_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(TEERACLE, "add_to_whitelist") + } + + fn remove_from_whitelist_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(TEERACLE, "remove_from_whitelist") + } + + fn update_exchange_rate_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(TEERACLE, "update_exchange_rate") + } + + fn update_oracle_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(TEERACLE, "update_oracle") + } +} diff --git a/bitacross-worker/core-primitives/node-api/metadata/src/pallet_teerex.rs b/bitacross-worker/core-primitives/node-api/metadata/src/pallet_teerex.rs new file mode 100644 index 0000000000..d2cd618e80 --- /dev/null +++ b/bitacross-worker/core-primitives/node-api/metadata/src/pallet_teerex.rs @@ -0,0 +1,114 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +use crate::{error::Result, NodeMetadata}; +use sp_core::storage::StorageKey; + +/// Pallet' name: +pub const TEEREX: &str = "Teerex"; + +pub trait TeerexCallIndexes { + fn register_enclave_call_indexes(&self) -> Result<[u8; 2]>; + + fn unregister_sovereign_enclave_call_indexes(&self) -> Result<[u8; 2]>; + + fn unregister_proxied_enclave_call_indexes(&self) -> Result<[u8; 2]>; + + fn register_quoting_enclave_call_indexes(&self) -> Result<[u8; 2]>; + + fn register_tcb_info_call_indexes(&self) -> Result<[u8; 2]>; + + fn invoke_call_indexes(&self) -> Result<[u8; 2]>; + + fn confirm_processed_parentchain_block_call_indexes(&self) -> Result<[u8; 2]>; + + fn shield_funds_call_indexes(&self) -> Result<[u8; 2]>; + + fn unshield_funds_call_indexes(&self) -> Result<[u8; 2]>; + + fn publish_hash_call_indexes(&self) -> Result<[u8; 2]>; + + // litentry + fn update_scheduled_enclave(&self) -> Result<[u8; 2]>; + + fn remove_scheduled_enclave(&self) -> Result<[u8; 2]>; +} + +pub trait TeerexStorageKey { + fn sovereign_enclaves_storage_map_key(&self, index: u64) -> Result; + + fn proxied_enclaves_storage_map_key(&self, index: u64) -> Result; +} + +impl TeerexCallIndexes for NodeMetadata { + fn register_enclave_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(TEEREX, "register_enclave") + } + + fn unregister_sovereign_enclave_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(TEEREX, "unregister_sovereign_enclave") + } + + fn unregister_proxied_enclave_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(TEEREX, "unregister_proxied_enclave") + } + + fn register_quoting_enclave_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(TEEREX, "register_quoting_enclave") + } + + fn register_tcb_info_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(TEEREX, "register_tcb_info") + } + + /* Keep parachain extrinsic name untouched. Keep alignment with upstream worker */ + fn invoke_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(TEEREX, "call_worker") + } + + fn confirm_processed_parentchain_block_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(TEEREX, "confirm_processed_parentchain_block") + } + + fn shield_funds_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(TEEREX, "shield_funds") + } + + fn unshield_funds_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(TEEREX, "unshield_funds") + } + + fn publish_hash_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(TEEREX, "publish_hash") + } + + fn update_scheduled_enclave(&self) -> Result<[u8; 2]> { + self.call_indexes(TEEREX, "update_scheduled_enclave") + } + + fn remove_scheduled_enclave(&self) -> Result<[u8; 2]> { + self.call_indexes(TEEREX, "remove_scheduled_enclave") + } +} + +impl TeerexStorageKey for NodeMetadata { + fn sovereign_enclaves_storage_map_key(&self, index: u64) -> Result { + self.storage_map_key(TEEREX, "SovereignEnclaves", index) + } + fn proxied_enclaves_storage_map_key(&self, index: u64) -> Result { + self.storage_map_key(TEEREX, "ProxiedEnclaves", index) + } +} diff --git a/bitacross-worker/core-primitives/node-api/metadata/src/pallet_utility.rs b/bitacross-worker/core-primitives/node-api/metadata/src/pallet_utility.rs new file mode 100644 index 0000000000..909e4a7d30 --- /dev/null +++ b/bitacross-worker/core-primitives/node-api/metadata/src/pallet_utility.rs @@ -0,0 +1,50 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +use crate::{error::Result, NodeMetadata}; + +/// Pallet' name: +const UTIL: &str = "Utility"; + +pub trait UtilityCallIndexes { + fn batch_call_indexes(&self) -> Result<[u8; 2]>; + fn as_derivative_call_indexes(&self) -> Result<[u8; 2]>; + fn batch_all_call_indexes(&self) -> Result<[u8; 2]>; + fn dispatch_as_call_indexes(&self) -> Result<[u8; 2]>; + fn force_batch_call_indexes(&self) -> Result<[u8; 2]>; +} + +impl UtilityCallIndexes for NodeMetadata { + fn batch_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(UTIL, "batch") + } + + fn as_derivative_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(UTIL, "as_derivative") + } + + fn batch_all_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(UTIL, "batch_all") + } + + fn dispatch_as_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(UTIL, "dispatch_as") + } + + fn force_batch_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(UTIL, "force_batch") + } +} diff --git a/bitacross-worker/core-primitives/node-api/metadata/src/pallet_vcmp.rs b/bitacross-worker/core-primitives/node-api/metadata/src/pallet_vcmp.rs new file mode 100644 index 0000000000..210d55e74f --- /dev/null +++ b/bitacross-worker/core-primitives/node-api/metadata/src/pallet_vcmp.rs @@ -0,0 +1,42 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +// TODO: maybe use macros to simplify this +use crate::{error::Result, NodeMetadata}; + +const VCMP: &str = "VCManagement"; + +pub trait VCMPCallIndexes { + fn request_vc_call_indexes(&self) -> Result<[u8; 2]>; + + fn vc_issued_call_indexes(&self) -> Result<[u8; 2]>; + + fn vcmp_some_error_call_indexes(&self) -> Result<[u8; 2]>; +} + +impl VCMPCallIndexes for NodeMetadata { + fn request_vc_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(VCMP, "request_vc") + } + + fn vc_issued_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(VCMP, "vc_issued") + } + + fn vcmp_some_error_call_indexes(&self) -> Result<[u8; 2]> { + self.call_indexes(VCMP, "some_error") + } +} diff --git a/bitacross-worker/core-primitives/node-api/metadata/src/runtime_call.rs b/bitacross-worker/core-primitives/node-api/metadata/src/runtime_call.rs new file mode 100644 index 0000000000..a484e6f779 --- /dev/null +++ b/bitacross-worker/core-primitives/node-api/metadata/src/runtime_call.rs @@ -0,0 +1,41 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +use crate::{error::Result, Error, NodeMetadata}; +use itp_api_client_types::MetadataError; + +pub trait RuntimeCall { + fn retrieve(&self) -> Result; +} + +impl RuntimeCall for NodeMetadata { + fn retrieve(&self) -> Result { + if self.node_metadata.as_ref().is_none() { + return Err(Error::MetadataNotSet) + } + let node_metadata = self.node_metadata.as_ref().unwrap(); + + let runtime_call = node_metadata.types().types.iter().find(|ty| { + let path = &ty.ty.path.segments; + path.len() == 2 && path[1].as_str() == "RuntimeCall" + }); + + match runtime_call { + Some(runtime_call) => Ok(runtime_call.id), + None => Err(Error::NodeMetadata(MetadataError::CallNotFound("RuntimeCall not found"))), + } + } +} diff --git a/bitacross-worker/core-primitives/node-api/src/lib.rs b/bitacross-worker/core-primitives/node-api/src/lib.rs new file mode 100644 index 0000000000..aea624c771 --- /dev/null +++ b/bitacross-worker/core-primitives/node-api/src/lib.rs @@ -0,0 +1,37 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Re-export crate for all the node-api stuff to simplify downstream imports. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(feature = "std")] +pub use itp_node_api_factory as node_api_factory; + +pub mod api_client { + #[cfg(feature = "std")] + pub use itp_api_client_extensions::*; + pub use itp_api_client_types::*; +} + +pub mod metadata { + pub use itp_node_api_metadata::*; + pub use itp_node_api_metadata_provider as provider; +} diff --git a/bitacross-worker/core-primitives/nonce-cache/Cargo.toml b/bitacross-worker/core-primitives/nonce-cache/Cargo.toml new file mode 100644 index 0000000000..e7f3f012fb --- /dev/null +++ b/bitacross-worker/core-primitives/nonce-cache/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "itp-nonce-cache" +version = "0.8.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +# sgx dependencies +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } + +# local dependencies + +# sgx enabled external libraries +thiserror_sgx = { package = "thiserror", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3", optional = true } + +# std compatible external libraries (make sure these versions match with the sgx-enabled ones above) +thiserror = { version = "1.0", optional = true } + +[features] +default = ["std"] +std = [ + "thiserror", +] +sgx = [ + "sgx_tstd", + "thiserror_sgx", +] diff --git a/bitacross-worker/core-primitives/nonce-cache/src/error.rs b/bitacross-worker/core-primitives/nonce-cache/src/error.rs new file mode 100644 index 0000000000..6b1731a77e --- /dev/null +++ b/bitacross-worker/core-primitives/nonce-cache/src/error.rs @@ -0,0 +1,32 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use std::boxed::Box; + +pub type Result = core::result::Result; + +/// nonce cache error +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("Nonce lock is poisoned")] + LockPoisoning, + #[error(transparent)] + Other(#[from] Box), +} diff --git a/bitacross-worker/core-primitives/nonce-cache/src/lib.rs b/bitacross-worker/core-primitives/nonce-cache/src/lib.rs new file mode 100644 index 0000000000..a1e515ac65 --- /dev/null +++ b/bitacross-worker/core-primitives/nonce-cache/src/lib.rs @@ -0,0 +1,64 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#![cfg_attr(not(feature = "std"), no_std)] +#![feature(assert_matches)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +// re-export module to properly feature gate sgx and regular std environment +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod sgx_reexport_prelude { + pub use thiserror_sgx as thiserror; +} + +#[cfg(feature = "std")] +use std::sync::RwLockWriteGuard; +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use std::sync::SgxRwLockWriteGuard as RwLockWriteGuard; + +use crate::error::Result; + +pub use nonce_cache::NonceCache; + +pub mod error; +pub mod nonce_cache; + +pub type NonceValue = u32; + +/// Nonce type (newtype wrapper for NonceValue) +#[derive(Default, Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] +pub struct Nonce(pub NonceValue); +/// Trait to mutate a nonce. +/// +/// Used in a combination of loading a lock and then writing the updated +/// value back, returning the lock again. +pub trait MutateNonce { + /// load a nonce with the intention to mutate it. lock is released once it goes out of scope + fn load_for_mutation(&self) -> Result>; +} + +/// Trait to get a nonce. +/// +/// +pub trait GetNonce { + fn get_nonce(&self) -> Result; +} diff --git a/bitacross-worker/core-primitives/nonce-cache/src/nonce_cache.rs b/bitacross-worker/core-primitives/nonce-cache/src/nonce_cache.rs new file mode 100644 index 0000000000..af55045cd0 --- /dev/null +++ b/bitacross-worker/core-primitives/nonce-cache/src/nonce_cache.rs @@ -0,0 +1,101 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use std::sync::SgxRwLock as RwLock; +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use std::sync::SgxRwLockWriteGuard as RwLockWriteGuard; + +#[cfg(feature = "std")] +use std::sync::RwLock; +#[cfg(feature = "std")] +use std::sync::RwLockWriteGuard; + +use crate::{ + error::{Error, Result}, + GetNonce, MutateNonce, Nonce, +}; + +/// Local nonce cache +/// +/// stores the nonce internally, protected by a RW lock for concurrent access +#[derive(Default)] +pub struct NonceCache { + nonce_lock: RwLock, +} + +impl NonceCache { + pub fn new(nonce_lock: RwLock) -> Self { + NonceCache { nonce_lock } + } +} + +impl MutateNonce for NonceCache { + fn load_for_mutation(&self) -> Result> { + self.nonce_lock.write().map_err(|_| Error::LockPoisoning) + } +} + +impl GetNonce for NonceCache { + fn get_nonce(&self) -> Result { + let nonce_lock = self.nonce_lock.read().map_err(|_| Error::LockPoisoning)?; + Ok(*nonce_lock) + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + use std::{sync::Arc, thread}; + + #[test] + pub fn nonce_defaults_to_zero() { + let nonce_cache = NonceCache::default(); + assert_eq!(Nonce(0), nonce_cache.get_nonce().unwrap()); + } + + #[test] + pub fn set_nonce_works() { + let nonce_cache = NonceCache::default(); + let mut nonce_lock = nonce_cache.load_for_mutation().unwrap(); + *nonce_lock = Nonce(42); + std::mem::drop(nonce_lock); + assert_eq!(Nonce(42), nonce_cache.get_nonce().unwrap()); + } + + #[test] + pub fn concurrent_read_access_blocks_until_write_is_done() { + let nonce_cache = Arc::new(NonceCache::default()); + + let mut nonce_write_lock = nonce_cache.load_for_mutation().unwrap(); + + // spawn a new thread that reads the nonce + // this thread should be blocked until the write lock is released, i.e. until + // the new nonce is written. We can verify this, by trying to read that nonce variable + // that will be inserted further down below + let new_thread_nonce_cache = nonce_cache.clone(); + let join_handle = thread::spawn(move || { + let nonce_read = new_thread_nonce_cache.get_nonce().unwrap(); + assert_eq!(Nonce(3108), nonce_read); + }); + + *nonce_write_lock = Nonce(3108); + std::mem::drop(nonce_write_lock); + + join_handle.join().unwrap(); + } +} diff --git a/bitacross-worker/core-primitives/ocall-api/Cargo.toml b/bitacross-worker/core-primitives/ocall-api/Cargo.toml new file mode 100644 index 0000000000..ef11a2a828 --- /dev/null +++ b/bitacross-worker/core-primitives/ocall-api/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "itp-ocall-api" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +derive_more = { version = "0.99.5" } + +# sgx deps +sgx_types = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } + +# substrate deps +sp-core = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-std = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# local deps +itp-storage = { path = "../storage", default-features = false } +itp-types = { path = "../types", default-features = false } + +[features] +default = ["std"] +std = [ + "codec/std", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", + "itp-storage/std", + "itp-types/std", +] diff --git a/bitacross-worker/core-primitives/ocall-api/src/lib.rs b/bitacross-worker/core-primitives/ocall-api/src/lib.rs new file mode 100644 index 0000000000..d4a0a9b944 --- /dev/null +++ b/bitacross-worker/core-primitives/ocall-api/src/lib.rs @@ -0,0 +1,158 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#![cfg_attr(not(feature = "std"), no_std)] + +pub extern crate alloc; + +use alloc::{string::String, vec::Vec}; +use codec::{Decode, Encode}; +use core::result::Result as StdResult; +use derive_more::{Display, From}; +use itp_storage::Error as StorageError; +use itp_types::{ + parentchain::ParentchainId, storage::StorageEntryVerified, BlockHash, ShardIdentifier, + TrustedOperationStatus, WorkerRequest, WorkerResponse, +}; +use sgx_types::*; +use sp_core::H256; +use sp_runtime::{traits::Header, OpaqueExtrinsic}; +use sp_std::prelude::*; + +#[derive(Debug, Display, From)] +pub enum Error { + Storage(StorageError), + Codec(codec::Error), + Sgx(sgx_types::sgx_status_t), +} + +pub type Result = StdResult; +/// Trait for the enclave to make o-calls related to remote attestation +pub trait EnclaveAttestationOCallApi: Clone + Send + Sync { + fn sgx_init_quote(&self) -> SgxResult<(sgx_target_info_t, sgx_epid_group_id_t)>; + + fn get_ias_socket(&self) -> SgxResult; + + fn get_quote( + &self, + sig_rl: Vec, + report: sgx_report_t, + sign_type: sgx_quote_sign_type_t, + spid: sgx_spid_t, + quote_nonce: sgx_quote_nonce_t, + ) -> SgxResult<(sgx_report_t, Vec)>; + + fn get_dcap_quote(&self, report: sgx_report_t, quote_size: u32) -> SgxResult>; + + fn get_qve_report_on_quote( + &self, + quote: Vec, + current_time: i64, + quote_collateral: sgx_ql_qve_collateral_t, + qve_report_info: sgx_ql_qe_report_info_t, + supplemental_data_size: u32, + ) -> SgxResult<(u32, sgx_ql_qv_result_t, sgx_ql_qe_report_info_t, Vec)>; + + fn get_update_info( + &self, + platform_info: sgx_platform_info_t, + enclave_trusted: i32, + ) -> SgxResult; + + fn get_mrenclave_of_self(&self) -> SgxResult; +} + +/// trait for o-calls related to RPC +pub trait EnclaveRpcOCallApi: Clone + Send + Sync + Default { + fn update_status_event( + &self, + hash: H, + status_update: TrustedOperationStatus, + ) -> SgxResult<()>; + + fn send_state(&self, hash: H, value_opt: Option>) -> SgxResult<()>; +} + +/// trait for o-calls related to on-chain interactions +pub trait EnclaveOnChainOCallApi: Clone + Send + Sync { + fn send_to_parentchain( + &self, + extrinsics: Vec, + parentchain_id: &ParentchainId, + await_each_inclusion: bool, + ) -> SgxResult<()>; + + fn worker_request( + &self, + req: Vec, + parentchain_id: &ParentchainId, + ) -> SgxResult>>; + + fn get_storage_verified, V: Decode>( + &self, + storage_hash: Vec, + header: &H, + parentchain_id: &ParentchainId, + ) -> Result>; + + fn get_multiple_storages_verified, V: Decode>( + &self, + storage_hashes: Vec>, + header: &H, + parentchain_id: &ParentchainId, + ) -> Result>>; + + // Litentry + // given a key prefix, get all storage keys + fn get_storage_keys(&self, key_prefix: Vec) -> Result>>; +} + +/// Trait for sending metric updates. +pub trait EnclaveMetricsOCallApi: Clone + Send + Sync { + fn update_metric(&self, metric: Metric) -> SgxResult<()>; +} + +pub trait EnclaveSidechainOCallApi: Clone + Send + Sync { + fn propose_sidechain_blocks( + &self, + signed_blocks: Vec, + ) -> SgxResult<()>; + + fn store_sidechain_blocks( + &self, + signed_blocks: Vec, + ) -> SgxResult<()>; + + fn fetch_sidechain_blocks_from_peer( + &self, + last_imported_block_hash: BlockHash, + maybe_until_block_hash: Option, + shard_identifier: ShardIdentifier, + ) -> SgxResult>; + + fn get_trusted_peers_urls(&self) -> SgxResult>; +} + +/// Newtype for IPFS CID +pub struct IpfsCid(pub [u8; 46]); + +/// trait for o-call related to IPFS +pub trait EnclaveIpfsOCallApi: Clone + Send + Sync { + fn write_ipfs(&self, encoded_state: &[u8]) -> SgxResult; + + fn read_ipfs(&self, cid: &IpfsCid) -> SgxResult<()>; +} diff --git a/bitacross-worker/core-primitives/primitives-cache/Cargo.toml b/bitacross-worker/core-primitives/primitives-cache/Cargo.toml new file mode 100644 index 0000000000..6b9f8e40e5 --- /dev/null +++ b/bitacross-worker/core-primitives/primitives-cache/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "itp-primitives-cache" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +# sgx dependencies +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } + +# local dependencies + +# sgx enabled external libraries +thiserror_sgx = { package = "thiserror", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3", optional = true } + +# std compatible external libraries (make sure these versions match with the sgx-enabled ones above) +thiserror = { version = "1.0", optional = true } + +# no-std dependencies +lazy_static = { version = "1.1.0", features = ["spin_no_std"] } + +[features] +default = ["std"] +std = [ + "thiserror", +] +sgx = [ + "sgx_tstd", + "thiserror_sgx", +] diff --git a/bitacross-worker/core-primitives/primitives-cache/src/error.rs b/bitacross-worker/core-primitives/primitives-cache/src/error.rs new file mode 100644 index 0000000000..2873dd8156 --- /dev/null +++ b/bitacross-worker/core-primitives/primitives-cache/src/error.rs @@ -0,0 +1,31 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use std::boxed::Box; + +pub type Result = core::result::Result; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("Primitives lock is poisoned")] + LockPoisoning, + #[error(transparent)] + Other(#[from] Box), +} diff --git a/bitacross-worker/core-primitives/primitives-cache/src/lib.rs b/bitacross-worker/core-primitives/primitives-cache/src/lib.rs new file mode 100644 index 0000000000..e4a2724e3f --- /dev/null +++ b/bitacross-worker/core-primitives/primitives-cache/src/lib.rs @@ -0,0 +1,114 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Stores all primitives of the enclave that do need to be accessed often, but are +//! not be frequently mutated, such as keys and server urls. +//! +//! TODO: For now only the mu-ra server and untrusted worker url is stored here. Keys and such could also be stored here. + +#![cfg_attr(not(feature = "std"), no_std)] +#![feature(assert_matches)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +// Re-export module to properly feature gate sgx and regular std environment. +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod sgx_reexport_prelude { + pub use thiserror_sgx as thiserror; +} + +#[cfg(feature = "std")] +use std::sync::RwLockReadGuard; +#[cfg(feature = "std")] +use std::sync::RwLockWriteGuard; + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use std::sync::SgxRwLockReadGuard as RwLockReadGuard; +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use std::sync::SgxRwLockWriteGuard as RwLockWriteGuard; + +use crate::error::Result; +use lazy_static::lazy_static; +use std::{string::String, sync::Arc}; + +pub use primitives_cache::PrimitivesCache; + +lazy_static! { + /// Global instance of the primitives cache. + /// + /// Concurrent access is managed internally, using RW locks. + pub static ref GLOBAL_PRIMITIVES_CACHE: Arc = Default::default(); +} + +pub mod error; +pub mod primitives_cache; + +#[derive(Default, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] +pub struct Primitives { + mu_ra_url: String, + untrusted_worker_url: String, +} + +impl Primitives { + pub fn new(mu_ra_url: String, untrusted_worker_url: String) -> Primitives { + Primitives { mu_ra_url, untrusted_worker_url } + } + + pub fn mu_ra_url(&self) -> &str { + &self.mu_ra_url + } + + pub fn untrusted_worker_url(&self) -> &str { + &self.untrusted_worker_url + } +} + +/// Trait to mutate the primitives. +/// +/// Used in a combination of loading a lock and then writing the updated +/// value back, returning the lock again. +pub trait MutatePrimitives { + fn load_for_mutation(&self) -> Result>; +} + +/// Trait to get the primitives. +pub trait GetPrimitives { + /// Returns a clone of the full Primitives struct. + fn get_primitives(&self) -> Result>; + + fn get_mu_ra_url(&self) -> Result; + + fn get_untrusted_worker_url(&self) -> Result; +} + +// Helper function to set primitives of a given cache. +pub fn set_primitives( + cache: &E, + mu_ra_url: String, + untrusted_worker_url: String, +) -> Result<()> { + let primitives = Primitives::new(mu_ra_url, untrusted_worker_url); + let mut rw_lock = cache.load_for_mutation()?; + + *rw_lock = primitives; + + Ok(()) +} diff --git a/bitacross-worker/core-primitives/primitives-cache/src/primitives_cache.rs b/bitacross-worker/core-primitives/primitives-cache/src/primitives_cache.rs new file mode 100644 index 0000000000..40bc516f51 --- /dev/null +++ b/bitacross-worker/core-primitives/primitives-cache/src/primitives_cache.rs @@ -0,0 +1,117 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use std::sync::SgxRwLock as RwLock; +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use std::sync::SgxRwLockReadGuard as RwLockReadGuard; +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use std::sync::SgxRwLockWriteGuard as RwLockWriteGuard; + +#[cfg(feature = "std")] +use std::sync::RwLock; +#[cfg(feature = "std")] +use std::sync::RwLockReadGuard; +#[cfg(feature = "std")] +use std::sync::RwLockWriteGuard; + +use std::string::{String, ToString}; + +use crate::{ + error::{Error, Result}, + GetPrimitives, MutatePrimitives, Primitives, +}; + +/// Local primitives cache. +/// +/// Stores the primitives internally, protected by a RW lock for concurrent access. +#[derive(Default)] +pub struct PrimitivesCache { + primitives_lock: RwLock, +} + +impl PrimitivesCache { + pub fn new(primitives_lock: RwLock) -> Self { + PrimitivesCache { primitives_lock } + } +} + +impl MutatePrimitives for PrimitivesCache { + fn load_for_mutation(&self) -> Result> { + self.primitives_lock.write().map_err(|_| Error::LockPoisoning) + } +} + +impl GetPrimitives for PrimitivesCache { + fn get_primitives(&self) -> Result> { + self.primitives_lock.read().map_err(|_| Error::LockPoisoning) + } + + fn get_mu_ra_url(&self) -> Result { + let primitives_lock = self.primitives_lock.read().map_err(|_| Error::LockPoisoning)?; + Ok(primitives_lock.mu_ra_url().to_string()) + } + + fn get_untrusted_worker_url(&self) -> Result { + let primitives_lock = self.primitives_lock.read().map_err(|_| Error::LockPoisoning)?; + Ok(primitives_lock.untrusted_worker_url().to_string()) + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + use std::{sync::Arc, thread}; + + #[test] + pub fn set_primitives_works() { + let cache = PrimitivesCache::default(); + let mut lock = cache.load_for_mutation().unwrap(); + let mu_ra_url = "hello".to_string(); + let untrusted_url = "world".to_string(); + let primitives = Primitives::new(mu_ra_url, untrusted_url); + *lock = primitives.clone(); + std::mem::drop(lock); + assert_eq!(primitives, *cache.get_primitives().unwrap()); + } + + #[test] + pub fn concurrent_read_access_blocks_until_write_is_done() { + let cache = Arc::new(PrimitivesCache::default()); + let mu_ra_url = "hello".to_string(); + let untrusted_url = "world".to_string(); + let primitives = Primitives::new(mu_ra_url, untrusted_url); + + let mut write_lock = cache.load_for_mutation().unwrap(); + + // Spawn a new thread that reads the primitives. + // This thread should be blocked until the write lock is released, i.e. until + // the new primitves are written. We can verify this, by trying to read the primitives variable + // that will be inserted further down below. + let new_thread_cache = cache.clone(); + let primitives_one = primitives.clone(); + let join_handle = thread::spawn(move || { + let read = new_thread_cache.get_primitives().unwrap(); + assert_eq!(primitives_one, *read); + }); + + *write_lock = primitives; + std::mem::drop(write_lock); + + join_handle.join().unwrap(); + } +} diff --git a/bitacross-worker/core-primitives/rpc/Cargo.toml b/bitacross-worker/core-primitives/rpc/Cargo.toml new file mode 100644 index 0000000000..784d850495 --- /dev/null +++ b/bitacross-worker/core-primitives/rpc/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "itp-rpc" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +itp-types = { default-features = false, path = "../types" } +serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } +serde_json = { version = "1.0", default-features = false, features = ["alloc"] } + +# sgx deps +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } + +[features] +default = ["std"] +std = [ + "codec/std", + "itp-types/std", + "serde/std", + "serde_json/std", +] +sgx = [ + "sgx_tstd", +] diff --git a/bitacross-worker/core-primitives/rpc/src/lib.rs b/bitacross-worker/core-primitives/rpc/src/lib.rs new file mode 100644 index 0000000000..4169196010 --- /dev/null +++ b/bitacross-worker/core-primitives/rpc/src/lib.rs @@ -0,0 +1,114 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +use codec::{Decode, Encode}; +use itp_types::DirectRequestStatus; +use serde::{Deserialize, Serialize}; +use std::{borrow::ToOwned, string::String, vec::Vec}; + +#[derive(Encode, Decode, Debug, Eq, PartialEq)] +pub struct RpcReturnValue { + pub value: Vec, + pub do_watch: bool, + pub status: DirectRequestStatus, +} +impl RpcReturnValue { + pub fn new(val: Vec, watch: bool, status: DirectRequestStatus) -> Self { + Self { value: val, do_watch: watch, status } + } + + pub fn from_error_message(error_msg: &str) -> Self { + RpcReturnValue { + value: error_msg.encode(), + do_watch: false, + status: DirectRequestStatus::Error, + } + } +} + +#[derive(Clone, Encode, Decode, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)] +#[serde(untagged)] +pub enum Id { + #[codec(index = 0)] + Number(u32), + #[codec(index = 1)] + Text(String), +} + +#[derive(Clone, Encode, Decode, Debug, Serialize, Deserialize)] +pub struct RpcResponse { + pub jsonrpc: String, + pub result: String, // hex encoded RpcReturnValue + pub id: Id, +} + +#[derive(Clone, Encode, Decode, Serialize, Deserialize)] +pub struct RpcRequest { + pub jsonrpc: String, + pub method: String, + pub params: Vec, + pub id: Id, +} + +impl RpcRequest { + pub fn compose_jsonrpc_call( + id: Id, + method: String, + params: Vec, + ) -> Result { + serde_json::to_string(&RpcRequest { jsonrpc: "2.0".to_owned(), method, params, id }) + } +} + +#[cfg(test)] +pub mod tests { + use crate::Id; + + #[test] + pub fn deserialize_string_id() { + let id: Id = serde_json::from_str(r#""1""#).unwrap(); + assert!(matches!(id, Id::Text(t) if t == "1")) + } + + #[test] + pub fn deserialize_number_id() { + let id: Id = serde_json::from_str(r#"1"#).unwrap(); + assert!(matches!(id, Id::Number(t) if t == 1)) + } + + #[test] + pub fn serialize_string_id() { + let id = Id::Text("1".to_string()); + let serialized = serde_json::to_string(&id).unwrap(); + assert_eq!(serialized, r#""1""#) + } + + #[test] + pub fn serialize_number_id() { + let id = Id::Number(1); + let serialized = serde_json::to_string(&id).unwrap(); + assert_eq!(serialized, r#"1"#) + } +} diff --git a/bitacross-worker/core-primitives/settings/Cargo.toml b/bitacross-worker/core-primitives/settings/Cargo.toml new file mode 100644 index 0000000000..bf48cd4ec2 --- /dev/null +++ b/bitacross-worker/core-primitives/settings/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "itp-settings" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] + + +[features] +production = [] +sidechain = [] +offchain-worker = [] +teeracle = [] diff --git a/bitacross-worker/core-primitives/settings/src/lib.rs b/bitacross-worker/core-primitives/settings/src/lib.rs new file mode 100644 index 0000000000..bc3ca98dcf --- /dev/null +++ b/bitacross-worker/core-primitives/settings/src/lib.rs @@ -0,0 +1,120 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Common settings for the worker and the enclave. It is strictly `no_std` + +#![no_std] + +#[cfg(any( + all(feature = "sidechain", feature = "offchain-worker"), + all(feature = "sidechain", feature = "teeracle"), + all(feature = "teeracle", feature = "offchain-worker") +))] +compile_error!( + "feature \"sidechain\" , \"offchain-worker\" or \"teeracle\" cannot be enabled at the same time" +); + +pub mod worker_mode; + +pub mod files { + // used by worker + pub static ENCLAVE_TOKEN: &str = "enclave.token"; + pub static ENCLAVE_FILE: &str = "enclave.signed.so"; + pub static SHIELDING_KEY_FILE: &str = "enclave-shielding-pubkey.json"; + pub static SIGNING_KEY_FILE: &str = "enclave-signing-pubkey.bin"; + /// sidechain database path + pub static SIDECHAIN_STORAGE_PATH: &str = "sidechain_db"; + pub static SIDECHAIN_PURGE_INTERVAL: u64 = 7200; // purge sidechain every .. s + pub static SIDECHAIN_PURGE_LIMIT: u64 = 100; // keep the last.. sidechainblocks when purging + + // used by enclave + /// Path to the light-client db for the Integritee parentchain. + pub const LITENTRY_PARENTCHAIN_LIGHT_CLIENT_DB_PATH: &str = "integritee_lcdb"; + + /// Path to the light-client db for the Target A parentchain. + pub const TARGET_A_PARENTCHAIN_LIGHT_CLIENT_DB_PATH: &str = "target_a_lcdb"; + + /// Path to the light-client db for the Target B parentchain. + pub const TARGET_B_PARENTCHAIN_LIGHT_CLIENT_DB_PATH: &str = "target_b_lcdb"; + + // litentry + pub const SCHEDULED_ENCLAVE_FILE: &str = "scheduled_enclave_sealed.bin"; + + pub const RA_DUMP_CERT_DER_FILE: &str = "ra_dump_cert.der"; + + // used by worker and enclave + pub const SHARDS_PATH: &str = "shards"; + + #[cfg(feature = "production")] + pub static RA_SPID_FILE: &str = "spid_production.txt"; + #[cfg(feature = "production")] + pub static RA_API_KEY_FILE: &str = "key_production.txt"; + + #[cfg(not(feature = "production"))] + pub static RA_SPID_FILE: &str = "spid.txt"; + #[cfg(not(feature = "production"))] + pub static RA_API_KEY_FILE: &str = "key.txt"; + + pub const SPID_MIN_LENGTH: usize = 32; + pub const STATE_SNAPSHOTS_CACHE_SIZE: usize = 4; +} + +/// Settings concerning the worker +pub mod worker { + // the maximum size of any extrinsic that the enclave will ever generate in B + pub const EXTRINSIC_MAX_SIZE: usize = 13_000; + // the maximum size of the header + // Litentry: change it to 300 after the evm pallet being fused + // see https://github.com/litentry/litentry-parachain/actions/runs/6168159073/job/16742757562 + pub const HEADER_MAX_SIZE: usize = 300; + // maximum size of shielding key + pub const SHIELDING_KEY_SIZE: usize = 8192; + // maximum size of signing key + pub const SIGNING_KEY_SIZE: usize = 32; + // size of the MR enclave + pub const MR_ENCLAVE_SIZE: usize = 32; + // Factors to tune the initial amount of enclave funding: + // Should be set to a value that ensures that the enclave can register itself + // and the worker can run for a certain time. Only for development. + pub const EXISTENTIAL_DEPOSIT_FACTOR_FOR_INIT_FUNDS: u128 = 1_000; + // Should be set to a value that ensures that the enclave can register itself + // and that the worker can start. + pub const REGISTERING_FEE_FACTOR_FOR_INIT_FUNDS: u128 = 10; + // Should be set to a value that ensures that at least 2 sidechain blocks are finalized per + // parentchain block. + pub const BLOCK_NUMBER_FINALIZATION_DIFF: u64 = 20; +} + +pub mod sidechain { + use core::time::Duration; + + pub static SLOT_DURATION: Duration = Duration::from_millis(6000); +} + +/// Settings concerning the enclave +pub mod enclave {} + +/// Settings for the Teeracle +pub mod teeracle { + use core::time::Duration; + // Send extrinsic to update market exchange rate on the parentchain once per day + pub static DEFAULT_MARKET_DATA_UPDATE_INTERVAL: Duration = ONE_DAY; + + pub static ONE_DAY: Duration = Duration::from_secs(86400); + + pub static THIRTY_MINUTES: Duration = Duration::from_secs(1800); +} diff --git a/bitacross-worker/core-primitives/settings/src/worker_mode.rs b/bitacross-worker/core-primitives/settings/src/worker_mode.rs new file mode 100644 index 0000000000..7eef1144fa --- /dev/null +++ b/bitacross-worker/core-primitives/settings/src/worker_mode.rs @@ -0,0 +1,59 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[derive(Eq, PartialEq, Debug, Clone)] +pub enum WorkerMode { + OffChainWorker, + Sidechain, + Teeracle, +} + +pub trait ProvideWorkerMode { + fn worker_mode() -> WorkerMode; +} + +#[derive(Default, Copy, Clone)] +pub struct WorkerModeProvider; + +#[cfg(feature = "offchain-worker")] +impl ProvideWorkerMode for WorkerModeProvider { + fn worker_mode() -> WorkerMode { + WorkerMode::OffChainWorker + } +} + +#[cfg(feature = "teeracle")] +impl ProvideWorkerMode for WorkerModeProvider { + fn worker_mode() -> WorkerMode { + WorkerMode::Teeracle + } +} + +#[cfg(feature = "sidechain")] +impl ProvideWorkerMode for WorkerModeProvider { + fn worker_mode() -> WorkerMode { + WorkerMode::Sidechain + } +} + +// Default to `Sidechain` worker mode when no cargo features are set. +#[cfg(not(any(feature = "sidechain", feature = "teeracle", feature = "offchain-worker")))] +impl ProvideWorkerMode for WorkerModeProvider { + fn worker_mode() -> WorkerMode { + WorkerMode::Sidechain + } +} diff --git a/bitacross-worker/core-primitives/sgx-runtime-primitives/Cargo.toml b/bitacross-worker/core-primitives/sgx-runtime-primitives/Cargo.toml new file mode 100644 index 0000000000..8ec87045fa --- /dev/null +++ b/bitacross-worker/core-primitives/sgx-runtime-primitives/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "itp-sgx-runtime-primitives" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] + +# Substrate dependencies +frame-system = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +pallet-balances = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-core = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# litentry +litentry-primitives = { path = "../../litentry/primitives", default-features = false } + +[features] +default = ["std"] +std = [ + "frame-system/std", + "pallet-balances/std", + "sp-core/std", + "sp-runtime/std", + # litentry + "litentry-primitives/std", +] diff --git a/bitacross-worker/core-primitives/sgx-runtime-primitives/src/constants.rs b/bitacross-worker/core-primitives/sgx-runtime-primitives/src/constants.rs new file mode 100644 index 0000000000..75eac384f1 --- /dev/null +++ b/bitacross-worker/core-primitives/sgx-runtime-primitives/src/constants.rs @@ -0,0 +1,29 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::types::{BlockNumber, Moment}; + +pub const ONE_DAY: Moment = 86_400_000; + +pub const MILLISECS_PER_BLOCK: u64 = 6000; + +pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; + +// Time is measured by number of blocks. +pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); +pub const HOURS: BlockNumber = MINUTES * 60; +pub const DAYS: BlockNumber = HOURS * 24; diff --git a/bitacross-worker/core-primitives/sgx-runtime-primitives/src/lib.rs b/bitacross-worker/core-primitives/sgx-runtime-primitives/src/lib.rs new file mode 100644 index 0000000000..74007111ba --- /dev/null +++ b/bitacross-worker/core-primitives/sgx-runtime-primitives/src/lib.rs @@ -0,0 +1,21 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod constants; +pub mod types; diff --git a/bitacross-worker/core-primitives/sgx-runtime-primitives/src/types.rs b/bitacross-worker/core-primitives/sgx-runtime-primitives/src/types.rs new file mode 100644 index 0000000000..bad667791e --- /dev/null +++ b/bitacross-worker/core-primitives/sgx-runtime-primitives/src/types.rs @@ -0,0 +1,86 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use sp_runtime::{ + generic::{self, Block as BlockG, SignedBlock as SignedBlockG}, + traits::{BlakeTwo256, IdentifyAccount, Verify}, + MultiSignature, OpaqueExtrinsic, +}; + +use litentry_primitives::ParentchainAccountId; + +/// The address format for describing accounts. +pub type Address = sp_runtime::MultiAddress; +/// Block header type as expected by this sgx-runtime. +pub type Header = generic::Header; + +/// An index to a block. +pub type BlockNumber = u32; +pub type SidechainBlockNumber = u64; +pub type SidechainTimestamp = u64; + +/// Alias to 512-bit hash when used in the context of a transaction signature on the chain. +pub type Signature = MultiSignature; + +/// Some way of identifying an account on the chain. We intentionally make it equivalent +/// to the public key of our transaction signing scheme. +pub type AccountId = <::Signer as IdentifyAccount>::AccountId; + +pub type AccountData = pallet_balances::AccountData; +pub type AccountInfo = frame_system::AccountInfo; + +/// The type for looking up accounts. We don't expect more than 4 billion of them, but you +/// never know... +pub type AccountIndex = u32; + +/// Balance of an account. +pub type Balance = u128; + +/// Index of a transaction in the chain. +pub type Index = u32; + +/// A hash of some data used by the chain. +pub type Hash = sp_core::H256; + +/// Digest item type. +pub type DigestItem = generic::DigestItem; + +/// A type to hold UTC unix epoch [ms] +pub type Moment = u64; + +pub type Block = BlockG; +pub type SignedBlock = SignedBlockG; +pub type BlockHash = sp_core::H256; +pub type ShardIdentifier = sp_core::H256; + +// litentry +pub trait ConvertAccountId { + type Input; + type Output; + fn convert(input: Self::Input) -> Self::Output; +} + +pub struct SgxParentchainTypeConverter; + +impl ConvertAccountId for SgxParentchainTypeConverter { + type Input = AccountId; + type Output = ParentchainAccountId; + fn convert(a: AccountId) -> ParentchainAccountId { + // it's an identity converter + a as ParentchainAccountId + } +} diff --git a/bitacross-worker/core-primitives/sgx/crypto/Cargo.toml b/bitacross-worker/core-primitives/sgx/crypto/Cargo.toml new file mode 100644 index 0000000000..fd8a971e49 --- /dev/null +++ b/bitacross-worker/core-primitives/sgx/crypto/Cargo.toml @@ -0,0 +1,56 @@ +[package] +name = "itp-sgx-crypto" +version = "0.9.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +aes = { version = "0.6.0" } +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +derive_more = { version = "0.99.5" } +log = { version = "0.4", default-features = false } +ofb = { version = "0.4.0" } +serde_json = { version = "1.0", default-features = false, features = ["alloc"], optional = true } + +# sgx deps +serde_json-sgx = { package = "serde_json", tag = "sgx_1.1.3", git = "https://github.com/mesalock-linux/serde-json-sgx", optional = true } +sgx-crypto-helper = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", package = "sgx_crypto_helper", default-features = false } +sgx_rand = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } +sgx_types = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } + +# substrate +sp-core = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# local deps +itp-sgx-io = { path = "../io", default-features = false } + +# test sgx deps +itp-sgx-temp-dir = { default-features = false, optional = true, path = "../temp-dir" } + +[features] +default = ["std"] +std = [ + "codec/std", + "log/std", + "itp-sgx-io/std", + "sp-core/std", + "serde_json/std", + "sgx-crypto-helper/default", +] +sgx = [ + "sgx-crypto-helper/mesalock_sgx", + "sgx_tstd", + "sgx_rand", + "itp-sgx-io/sgx", + "serde_json-sgx", +] +mocks = [] +test = [ + # features + "mocks", + "sgx", + # deps + "itp-sgx-temp-dir", +] diff --git a/bitacross-worker/core-primitives/sgx/crypto/src/aes.rs b/bitacross-worker/core-primitives/sgx/crypto/src/aes.rs new file mode 100644 index 0000000000..0c1414e84c --- /dev/null +++ b/bitacross-worker/core-primitives/sgx/crypto/src/aes.rs @@ -0,0 +1,203 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + error::{Error, Result}, + traits::StateCrypto, +}; +use aes::Aes128; +use codec::{Decode, Encode}; +use ofb::{ + cipher::{NewStreamCipher, SyncStreamCipher}, + Ofb, +}; +use std::{ + convert::{TryFrom, TryInto}, + path::PathBuf, +}; + +type AesOfb = Ofb; + +/// File name of the sealed AES key data. +pub const AES_KEY_FILE_AND_INIT_V: &str = "aes_key_and_iv_sealed_data.bin"; + +#[derive(Debug, Default, Encode, Decode, Clone, Copy, PartialEq, Eq)] +pub struct Aes { + pub key: [u8; 16], + pub init_vec: [u8; 16], +} + +impl Aes { + pub fn new(key: [u8; 16], init_vec: [u8; 16]) -> Self { + Self { key, init_vec } + } +} + +#[derive(Clone, Debug)] +pub struct AesSeal { + base_path: PathBuf, +} + +impl AesSeal { + pub fn new(base_path: PathBuf) -> Self { + Self { base_path } + } + + pub fn path(&self) -> PathBuf { + self.base_path.join(AES_KEY_FILE_AND_INIT_V) + } +} + +impl StateCrypto for Aes { + type Error = Error; + + fn encrypt(&self, data: &mut [u8]) -> Result<()> { + de_or_encrypt(self, data) + } + + fn decrypt(&self, data: &mut [u8]) -> Result<()> { + de_or_encrypt(self, data) + } +} + +impl TryFrom<&Aes> for AesOfb { + type Error = Error; + + fn try_from(aes: &Aes) -> std::result::Result { + AesOfb::new_var(&aes.key, &aes.init_vec).map_err(|_| Error::InvalidNonceKeyLength) + } +} + +/// If AES acts on the encrypted data it decrypts and vice versa +pub fn de_or_encrypt(aes: &Aes, data: &mut [u8]) -> Result<()> { + aes.try_into().map(|mut ofb: AesOfb| ofb.apply_keystream(data)) +} + +pub trait AesSealing { + fn unseal_key(&self) -> Result; + + fn exists(&self) -> bool; + + fn create_sealed_if_absent(&self) -> Result<()>; + + fn create_sealed(&self) -> Result<()>; +} + +#[cfg(feature = "sgx")] +pub use sgx::*; + +#[cfg(feature = "sgx")] +pub mod sgx { + use super::*; + use crate::key_repository::KeyRepository; + use itp_sgx_io::{seal, unseal, SealedIO}; + use log::info; + use sgx_rand::{Rng, StdRng}; + use std::sgxfs::SgxFile; + + /// Gets a repository for an AES key and initializes + /// a fresh key if it doesn't exist at `path`. + pub fn get_aes_repository(path: PathBuf) -> Result> { + let aes_seal = AesSeal::new(path); + aes_seal.create_sealed_if_absent()?; + let aes_key = aes_seal.unseal_key()?; + Ok(KeyRepository::new(aes_key, aes_seal.into())) + } + + impl AesSealing for AesSeal { + fn unseal_key(&self) -> Result { + self.unseal() + } + + fn exists(&self) -> bool { + SgxFile::open(self.path()).is_ok() + } + + fn create_sealed_if_absent(&self) -> Result<()> { + if !self.exists() { + info!("Keyfile not found, creating new! {}", self.path().display()); + return self.create_sealed() + } + Ok(()) + } + + fn create_sealed(&self) -> Result<()> { + let mut key = [0u8; 16]; + let mut iv = [0u8; 16]; + let mut rand = StdRng::new()?; + + rand.fill_bytes(&mut key); + rand.fill_bytes(&mut iv); + + Ok(self.seal(&Aes::new(key, iv))?) + } + } + + impl SealedIO for AesSeal { + type Error = Error; + type Unsealed = Aes; + + fn unseal(&self) -> Result { + Ok(unseal(self.path()).map(|b| Decode::decode(&mut b.as_slice()))??) + } + + fn seal(&self, unsealed: &Self::Unsealed) -> Result<()> { + Ok(unsealed.using_encoded(|bytes| seal(bytes, self.path()))?) + } + } +} + +#[cfg(feature = "test")] +pub mod sgx_tests { + use super::sgx::*; + use crate::{key_repository::AccessKey, AesSeal, AesSealing}; + use itp_sgx_temp_dir::TempDir; + + pub fn using_get_aes_repository_twice_initializes_key_only_once() { + let temp_dir = + TempDir::with_prefix("using_get_aes_repository_twice_initializes_key_only_once") + .unwrap(); + let temp_path = temp_dir.path().to_path_buf(); + let key1 = get_aes_repository(temp_path.clone()).unwrap().retrieve_key().unwrap(); + let key2 = get_aes_repository(temp_path).unwrap().retrieve_key().unwrap(); + assert_eq!(key1, key2); + } + + pub fn aes_sealing_works() { + let temp_dir = TempDir::with_prefix("aes_sealing_works").unwrap(); + let seal = AesSeal::new(temp_dir.path().to_path_buf()); + + // Create new sealed keys and unseal them + assert!(!seal.exists()); + seal.create_sealed_if_absent().unwrap(); + let key = seal.unseal_key().unwrap(); + + assert!(seal.exists()); + + // Should not change anything because the key is already there. + seal.create_sealed_if_absent().unwrap(); + let key_same = seal.unseal_key().unwrap(); + + assert_eq!(key, key_same); + + // Should overwrite previous keys. + seal.create_sealed().unwrap(); + let key_different = seal.unseal_key().unwrap(); + + assert_ne!(key_different, key); + } +} diff --git a/bitacross-worker/core-primitives/sgx/crypto/src/ed25519.rs b/bitacross-worker/core-primitives/sgx/crypto/src/ed25519.rs new file mode 100644 index 0000000000..153314eb4f --- /dev/null +++ b/bitacross-worker/core-primitives/sgx/crypto/src/ed25519.rs @@ -0,0 +1,180 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + error::{Error, Result}, + ToPubkey, +}; +use sp_core::ed25519; + +#[cfg(feature = "sgx")] +pub use sgx::*; + +/// File name of the sealed Ed25519 seed file. +pub const SEALED_SIGNER_SEED_FILE: &str = "ed25519_key_sealed.bin"; + +pub trait Ed25519Sealing { + fn unseal_pubkey(&self) -> Result; + + fn unseal_pair(&self) -> Result; + + fn exists(&self) -> bool; + + fn create_sealed_if_absent(&self) -> Result<()>; + + fn create_sealed(&self) -> Result<()>; +} + +impl ToPubkey for ed25519::Pair { + type Error = Error; + type Pubkey = ed25519::Public; + + fn pubkey(&self) -> Result { + Ok((*self).into()) + } +} + +#[cfg(feature = "sgx")] +pub mod sgx { + use super::SEALED_SIGNER_SEED_FILE; + use crate::{ + error::{Error, Result}, + key_repository::KeyRepository, + Ed25519Sealing, + }; + use codec::Encode; + use itp_sgx_io::{seal, unseal, SealedIO}; + use log::*; + use sgx_rand::{Rng, StdRng}; + use sp_core::{crypto::Pair, ed25519}; + use std::path::PathBuf; + + /// Gets a repository for an Ed25519 keypair and initializes + /// a fresh key pair if it doesn't exist at `path`. + pub fn get_ed25519_repository( + path: PathBuf, + ) -> Result> { + let ed25519_seal = Ed25519Seal::new(path); + ed25519_seal.create_sealed_if_absent()?; + let signing_pair = ed25519_seal.unseal_pair()?; + Ok(KeyRepository::new(signing_pair, ed25519_seal.into())) + } + + #[derive(Clone, Debug)] + pub struct Ed25519Seal { + base_path: PathBuf, + } + + impl Ed25519Seal { + pub fn new(base_path: PathBuf) -> Self { + Self { base_path } + } + + pub fn path(&self) -> PathBuf { + self.base_path.join(SEALED_SIGNER_SEED_FILE) + } + } + + impl Ed25519Sealing for Ed25519Seal { + fn unseal_pubkey(&self) -> Result { + self.unseal().map(Into::into) + } + + fn unseal_pair(&self) -> Result { + self.unseal() + } + + fn exists(&self) -> bool { + self.path().exists() + } + + fn create_sealed_if_absent(&self) -> Result<()> { + if !self.exists() { + info!("Keyfile not found, creating new! {}", self.path().display()); + return self.create_sealed() + } + Ok(()) + } + + fn create_sealed(&self) -> Result<()> { + let mut seed = [0u8; 32]; + let mut rand = StdRng::new()?; + rand.fill_bytes(&mut seed); + + Ok(seal(&seed, self.path())?) + } + } + + impl SealedIO for Ed25519Seal { + type Error = Error; + type Unsealed = ed25519::Pair; + + fn unseal(&self) -> Result { + let raw = unseal(self.path())?; + + ed25519::Pair::from_seed_slice(&raw) + .map_err(|e| Error::Other(format!("{:?}", e).into())) + } + + fn seal(&self, unsealed: &Self::Unsealed) -> Result<()> { + Ok(unsealed.seed().using_encoded(|bytes| seal(bytes, self.path()))?) + } + } +} + +#[cfg(feature = "test")] +pub mod sgx_tests { + use super::sgx::*; + use crate::{key_repository::AccessKey, Ed25519Sealing, ToPubkey}; + use itp_sgx_temp_dir::TempDir; + + pub fn using_get_ed25519_repository_twice_initializes_key_only_once() { + let temp_dir = + TempDir::with_prefix("using_get_rsa3072_repository_twice_initializes_key_only_once") + .unwrap(); + let temp_path = temp_dir.path().to_path_buf(); + let key1 = get_ed25519_repository(temp_path.clone()).unwrap().retrieve_key().unwrap(); + let key2 = get_ed25519_repository(temp_path).unwrap().retrieve_key().unwrap(); + assert_eq!(key1.pubkey().unwrap(), key2.pubkey().unwrap()); + } + + pub fn ed25529_sealing_works() { + let temp_dir = TempDir::with_prefix("ed25529_sealing_works").unwrap(); + let seal = Ed25519Seal::new(temp_dir.path().to_path_buf()); + + // Create new sealed keys and unseal them. + assert!(!seal.exists()); + seal.create_sealed_if_absent().unwrap(); + let pair = seal.unseal_pair().unwrap(); + let pubkey = seal.unseal_pubkey().unwrap(); + + assert!(seal.exists()); + assert_eq!(pair.pubkey().unwrap(), pubkey); + + // Should not change anything because the key is already there. + seal.create_sealed_if_absent().unwrap(); + let pair_same = seal.unseal_pair().unwrap(); + + assert_eq!(pair.pubkey().unwrap(), pair_same.pubkey().unwrap()); + + // Should overwrite previous keys. + seal.create_sealed().unwrap(); + let pair_different = seal.unseal_pair().unwrap(); + + assert_ne!(pair_different.pubkey().unwrap(), pair.pubkey().unwrap()); + } +} diff --git a/bitacross-worker/core-primitives/sgx/crypto/src/ed25519_derivation.rs b/bitacross-worker/core-primitives/sgx/crypto/src/ed25519_derivation.rs new file mode 100644 index 0000000000..25e51279c7 --- /dev/null +++ b/bitacross-worker/core-primitives/sgx/crypto/src/ed25519_derivation.rs @@ -0,0 +1,36 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use crate::error::Result; +use sgx_crypto_helper::rsa3072::Rsa3072KeyPair; +use sp_core::{blake2_256, ed25519::Pair as Ed25519Pair, Pair}; + +/// Trait to derive an Ed25519 key pair. +pub trait DeriveEd25519 { + fn derive_ed25519(&self) -> Result; +} + +impl DeriveEd25519 for Rsa3072KeyPair { + fn derive_ed25519(&self) -> Result { + let encoded_key = serde_json::to_vec(self)?; + let seed = blake2_256(&encoded_key); + Ok(Ed25519Pair::from_seed(&seed)) + } +} diff --git a/bitacross-worker/core-primitives/sgx/crypto/src/error.rs b/bitacross-worker/core-primitives/sgx/crypto/src/error.rs new file mode 100644 index 0000000000..4fa619d136 --- /dev/null +++ b/bitacross-worker/core-primitives/sgx/crypto/src/error.rs @@ -0,0 +1,43 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use derive_more::{Display, From}; +use sgx_types::sgx_status_t; +use std::prelude::v1::Box; + +#[derive(Debug, Display, From)] +pub enum Error { + IO(std::io::Error), + InvalidNonceKeyLength, + Codec(codec::Error), + Serialization(serde_json::Error), + LockPoisoning, + Other(Box), +} + +pub type Result = core::result::Result; + +impl From for sgx_status_t { + /// return sgx_status for top level enclave functions + fn from(error: Error) -> sgx_status_t { + log::warn!("Transform non-sgx-error into `SGX_ERROR_UNEXPECTED`: {:?}", error); + sgx_status_t::SGX_ERROR_UNEXPECTED + } +} diff --git a/bitacross-worker/core-primitives/sgx/crypto/src/key_repository.rs b/bitacross-worker/core-primitives/sgx/crypto/src/key_repository.rs new file mode 100644 index 0000000000..41ca5ae860 --- /dev/null +++ b/bitacross-worker/core-primitives/sgx/crypto/src/key_repository.rs @@ -0,0 +1,122 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(feature = "sgx")] +use std::sync::SgxRwLock as RwLock; + +#[cfg(feature = "std")] +use std::sync::RwLock; + +use crate::{ + error::{Error, Result}, + ToPubkey, +}; +use itp_sgx_io::SealedIO; +use std::sync::Arc; + +/// Access a cryptographic key. +pub trait AccessKey { + type KeyType; + + fn retrieve_key(&self) -> Result; +} + +/// Access a cryptographic public key. +pub trait AccessPubkey { + type KeyType; + + fn retrieve_pubkey(&self) -> Result; +} + +/// Mutate a cryptographic key. +pub trait MutateKey { + fn update_key(&self, key: KeyType) -> Result<()>; +} + +/// Repository implementation. Stores a cryptographic key in-memory and in a file backed. +/// Uses the SealedIO trait for the file backend. +pub struct KeyRepository { + key_lock: RwLock, + sealed_io: Arc, +} + +impl KeyRepository { + pub fn new(key: KeyType, sealed_io: Arc) -> Self { + KeyRepository { key_lock: RwLock::new(key), sealed_io } + } +} + +impl AccessKey for KeyRepository +where + KeyType: Clone, + SealedIo: SealedIO, +{ + type KeyType = KeyType; + + fn retrieve_key(&self) -> Result { + self.key_lock.read().map_err(|_| Error::LockPoisoning).map(|l| l.clone()) + } +} + +impl AccessPubkey for KeyRepository +where + Pair: ToPubkey + Clone, + SealedIo: SealedIO, +{ + type KeyType = ::Pubkey; + + fn retrieve_pubkey(&self) -> Result { + self.key_lock.read().map_err(|_| Error::LockPoisoning).map(|p| p.pubkey())? + } +} + +impl MutateKey for KeyRepository +where + KeyType: Clone, + SealedIo: SealedIO, +{ + fn update_key(&self, key: KeyType) -> Result<()> { + let mut key_lock = self.key_lock.write().map_err(|_| Error::LockPoisoning)?; + + self.sealed_io.seal(&key)?; + *key_lock = self.sealed_io.unseal()?; + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{aes::Aes, mocks::AesSealMock}; + + type TestKeyRepository = KeyRepository; + + #[test] + fn update_and_retrieve_key_works() { + let seal_mock = Arc::new(AesSealMock::default()); + let key_repository = TestKeyRepository::new(seal_mock.unseal().unwrap(), seal_mock.clone()); + + assert_eq!(seal_mock.unseal().unwrap(), key_repository.retrieve_key().unwrap()); + + let updated_key = Aes::new([2u8; 16], [0u8; 16]); + key_repository.update_key(updated_key).unwrap(); + + assert_eq!(updated_key, key_repository.retrieve_key().unwrap()); + assert_eq!(updated_key, seal_mock.unseal().unwrap()); + } +} diff --git a/bitacross-worker/core-primitives/sgx/crypto/src/lib.rs b/bitacross-worker/core-primitives/sgx/crypto/src/lib.rs new file mode 100644 index 0000000000..832239c027 --- /dev/null +++ b/bitacross-worker/core-primitives/sgx/crypto/src/lib.rs @@ -0,0 +1,63 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! All the different crypto schemes that we use in sgx + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +#[macro_use] +extern crate sgx_tstd as std; + +// re-export module to properly feature gate sgx and regular std environment +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod sgx_reexport_prelude { + pub use serde_json_sgx as serde_json; +} + +pub mod aes; +pub mod ed25519; +pub mod ed25519_derivation; +pub mod error; +pub mod key_repository; +pub mod rsa3072; +pub mod traits; + +pub use self::{aes::*, ed25519::*, rsa3072::*}; +pub use error::*; +pub use traits::*; + +#[cfg(feature = "mocks")] +pub mod mocks; + +#[cfg(feature = "test")] +pub mod tests { + pub use super::ed25519::sgx_tests::{ + ed25529_sealing_works, using_get_ed25519_repository_twice_initializes_key_only_once, + }; + + pub use super::rsa3072::sgx_tests::{ + rsa3072_sealing_works, using_get_rsa3072_repository_twice_initializes_key_only_once, + }; + + pub use super::aes::sgx_tests::{ + aes_sealing_works, using_get_aes_repository_twice_initializes_key_only_once, + }; +} diff --git a/bitacross-worker/core-primitives/sgx/crypto/src/mocks.rs b/bitacross-worker/core-primitives/sgx/crypto/src/mocks.rs new file mode 100644 index 0000000000..0e199378fd --- /dev/null +++ b/bitacross-worker/core-primitives/sgx/crypto/src/mocks.rs @@ -0,0 +1,118 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(feature = "sgx")] +use std::sync::SgxRwLock as RwLock; + +#[cfg(feature = "std")] +use std::sync::RwLock; + +use crate::{ + aes::Aes, + error::{Error, Result}, + key_repository::{AccessKey, MutateKey}, +}; +use itp_sgx_io::{SealedIO, StaticSealedIO}; +use sgx_crypto_helper::rsa3072::Rsa3072KeyPair; + +#[derive(Default)] +pub struct KeyRepositoryMock +where + KeyType: Clone + Default, +{ + key: RwLock, +} + +impl KeyRepositoryMock +where + KeyType: Clone + Default, +{ + pub fn new(key: KeyType) -> Self { + KeyRepositoryMock { key: RwLock::new(key) } + } +} + +impl AccessKey for KeyRepositoryMock +where + KeyType: Clone + Default, +{ + type KeyType = KeyType; + + fn retrieve_key(&self) -> Result { + Ok(self.key.read().unwrap().clone()) + } +} + +impl MutateKey for KeyRepositoryMock +where + KeyType: Clone + Default, +{ + fn update_key(&self, key: KeyType) -> Result<()> { + let mut lock = self.key.write().unwrap(); + *lock = key; + Ok(()) + } +} + +#[derive(Default)] +pub struct AesSealMock { + aes: RwLock, +} + +impl StaticSealedIO for AesSealMock { + type Error = Error; + type Unsealed = Aes; + + fn unseal_from_static_file() -> Result { + Ok(Aes::default()) + } + + fn seal_to_static_file(_unsealed: &Self::Unsealed) -> Result<()> { + Ok(()) + } +} + +impl SealedIO for AesSealMock { + type Error = Error; + type Unsealed = Aes; + + fn unseal(&self) -> std::result::Result { + self.aes.read().map_err(|e| Error::Other(format!("{:?}", e).into())).map(|k| *k) + } + + fn seal(&self, unsealed: &Self::Unsealed) -> Result<()> { + let mut aes_lock = self.aes.write().map_err(|e| Error::Other(format!("{:?}", e).into()))?; + *aes_lock = *unsealed; + Ok(()) + } +} + +#[derive(Default)] +pub struct Rsa3072SealMock {} + +impl StaticSealedIO for Rsa3072SealMock { + type Error = Error; + type Unsealed = Rsa3072KeyPair; + + fn unseal_from_static_file() -> Result { + Ok(Rsa3072KeyPair::default()) + } + + fn seal_to_static_file(_unsealed: &Self::Unsealed) -> Result<()> { + Ok(()) + } +} diff --git a/bitacross-worker/core-primitives/sgx/crypto/src/rsa3072.rs b/bitacross-worker/core-primitives/sgx/crypto/src/rsa3072.rs new file mode 100644 index 0000000000..3a63a0d11d --- /dev/null +++ b/bitacross-worker/core-primitives/sgx/crypto/src/rsa3072.rs @@ -0,0 +1,221 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use crate::{ + error::{Error, Result}, + traits::{ShieldingCryptoDecrypt, ShieldingCryptoEncrypt}, + ToPubkey, +}; +use sgx_crypto_helper::{ + rsa3072::{Rsa3072KeyPair, Rsa3072PubKey}, + RsaKeyPair, +}; +use std::vec::Vec; + +// Reexport sgx module +#[cfg(feature = "sgx")] +pub use sgx::*; + +/// File name of the sealed RSA key file. +pub const RSA3072_SEALED_KEY_FILE: &str = "rsa3072_key_sealed.bin"; + +impl ShieldingCryptoEncrypt for Rsa3072KeyPair { + type Error = Error; + + fn encrypt(&self, data: &[u8]) -> Result> { + let mut cipher_buffer = Vec::new(); + self.encrypt_buffer(data, &mut cipher_buffer) + .map_err(|e| Error::Other(format!("{:?}", e).into()))?; + Ok(cipher_buffer) + } +} + +impl ShieldingCryptoDecrypt for Rsa3072KeyPair { + type Error = Error; + + fn decrypt(&self, data: &[u8]) -> Result> { + let mut decrypted_buffer = Vec::new(); + self.decrypt_buffer(data, &mut decrypted_buffer) + .map_err(|e| Error::Other(format!("{:?}", e).into()))?; + Ok(decrypted_buffer) + } +} + +impl ShieldingCryptoEncrypt for Rsa3072PubKey { + type Error = Error; + + fn encrypt(&self, data: &[u8]) -> Result> { + let mut cipher_buffer = Vec::new(); + self.encrypt_buffer(data, &mut cipher_buffer) + .map_err(|e| Error::Other(format!("{:?}", e).into()))?; + Ok(cipher_buffer) + } +} + +impl ToPubkey for Rsa3072KeyPair { + type Error = Error; + type Pubkey = Rsa3072PubKey; + + fn pubkey(&self) -> Result { + self.export_pubkey().map_err(|e| Error::Other(format!("{:?}", e).into())) + } +} + +pub trait RsaSealing { + fn unseal_pubkey(&self) -> Result; + + fn unseal_pair(&self) -> Result; + + fn exists(&self) -> bool; + + fn create_sealed_if_absent(&self) -> Result<()>; + + fn create_sealed(&self) -> Result<()>; +} + +#[cfg(feature = "sgx")] +pub mod sgx { + use super::*; + use crate::key_repository::KeyRepository; + use itp_sgx_io::{seal, unseal, SealedIO}; + use log::*; + use std::path::PathBuf; + + /// Gets a repository for an Rsa3072 keypair and initializes + /// a fresh key pair if it doesn't exist at `path`. + pub fn get_rsa3072_repository( + path: PathBuf, + ) -> Result> { + let rsa_seal = Rsa3072Seal::new(path); + rsa_seal.create_sealed_if_absent()?; + let shielding_key = rsa_seal.unseal_pair()?; + Ok(KeyRepository::new(shielding_key, rsa_seal.into())) + } + + #[derive(Clone, Debug)] + pub struct Rsa3072Seal { + base_path: PathBuf, + } + + impl Rsa3072Seal { + pub fn new(base_path: PathBuf) -> Self { + Self { base_path } + } + + pub fn path(&self) -> PathBuf { + self.base_path.join(RSA3072_SEALED_KEY_FILE) + } + } + + impl RsaSealing for Rsa3072Seal { + fn unseal_pubkey(&self) -> Result { + self.unseal()?.pubkey() + } + + fn unseal_pair(&self) -> Result { + self.unseal() + } + + fn exists(&self) -> bool { + self.path().exists() + } + + fn create_sealed_if_absent(&self) -> Result<()> { + if !self.exists() { + info!("Keyfile not found, creating new! {}", self.path().display()); + return self.create_sealed() + } + Ok(()) + } + + fn create_sealed(&self) -> Result<()> { + let rsa_keypair = + Rsa3072KeyPair::new().map_err(|e| Error::Other(format!("{:?}", e).into()))?; + info!("Generated RSA3072 key pair. PubKey: {:?}", rsa_keypair.pubkey()?); + self.seal(&rsa_keypair) + } + } + + impl SealedIO for Rsa3072Seal { + type Error = Error; + type Unsealed = Rsa3072KeyPair; + + fn unseal(&self) -> Result { + let raw = unseal(self.path())?; + let key: Rsa3072KeyPair = serde_json::from_slice(&raw) + .map_err(|e| Error::Other(format!("{:?}", e).into()))?; + Ok(key.into()) + } + + fn seal(&self, unsealed: &Self::Unsealed) -> Result<()> { + let key_json = serde_json::to_vec(&unsealed) + .map_err(|e| Error::Other(format!("{:?}", e).into()))?; + Ok(seal(&key_json, self.path())?) + } + } +} + +#[cfg(feature = "test")] +pub mod sgx_tests { + use super::{serde_json, sgx::*}; + use crate::{key_repository::AccessKey, RsaSealing, ToPubkey}; + use itp_sgx_temp_dir::TempDir; + use sgx_crypto_helper::rsa3072::Rsa3072PubKey; + + /// Helper method because Rsa3072 does not implement `Eq`. + pub fn equal(pubkey1: &Rsa3072PubKey, pubkey2: &Rsa3072PubKey) -> bool { + serde_json::to_vec(pubkey1).unwrap() == serde_json::to_vec(pubkey2).unwrap() + } + + pub fn using_get_rsa3072_repository_twice_initializes_key_only_once() { + let temp_dir = + TempDir::with_prefix("using_get_rsa3072_repository_twice_initializes_key_only_once") + .unwrap(); + let temp_path = temp_dir.path().to_path_buf(); + let key1 = get_rsa3072_repository(temp_path.clone()).unwrap().retrieve_key().unwrap(); + let key2 = get_rsa3072_repository(temp_path).unwrap().retrieve_key().unwrap(); + assert!(equal(&key1.pubkey().unwrap(), &key2.pubkey().unwrap())); + } + + pub fn rsa3072_sealing_works() { + let temp_dir = TempDir::with_prefix("rsa3072_sealing_works").unwrap(); + let seal = Rsa3072Seal::new(temp_dir.path().to_path_buf()); + + // Create new sealed keys and unseal them + assert!(!seal.exists()); + seal.create_sealed_if_absent().unwrap(); + let pair = seal.unseal_pair().unwrap(); + let pubkey = seal.unseal_pubkey().unwrap(); + + assert!(seal.exists()); + assert!(equal(&pair.pubkey().unwrap(), &pubkey)); + + // Should not change anything because the key is already there. + seal.create_sealed_if_absent().unwrap(); + let pair_same = seal.unseal_pair().unwrap(); + + assert!(equal(&pair.pubkey().unwrap(), &pair_same.pubkey().unwrap())); + + // Should overwrite previous keys. + seal.create_sealed().unwrap(); + let pair_different = seal.unseal_pair().unwrap(); + + assert!(!equal(&pair_different.pubkey().unwrap(), &pair.pubkey().unwrap())); + } +} diff --git a/bitacross-worker/core-primitives/sgx/crypto/src/traits.rs b/bitacross-worker/core-primitives/sgx/crypto/src/traits.rs new file mode 100644 index 0000000000..1d0aef5798 --- /dev/null +++ b/bitacross-worker/core-primitives/sgx/crypto/src/traits.rs @@ -0,0 +1,42 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Abstraction over the state crypto that is used in the enclave +use std::{fmt::Debug, vec::Vec}; + +pub trait StateCrypto { + type Error: Debug; + fn encrypt(&self, data: &mut [u8]) -> Result<(), Self::Error>; + fn decrypt(&self, data: &mut [u8]) -> Result<(), Self::Error>; +} + +pub trait ShieldingCryptoEncrypt { + type Error: Debug; + fn encrypt(&self, data: &[u8]) -> Result, Self::Error>; +} + +pub trait ShieldingCryptoDecrypt { + type Error: Debug; + fn decrypt(&self, data: &[u8]) -> Result, Self::Error>; +} + +pub trait ToPubkey { + type Error: Debug; + type Pubkey; + + fn pubkey(&self) -> Result; +} diff --git a/bitacross-worker/core-primitives/sgx/io/Cargo.toml b/bitacross-worker/core-primitives/sgx/io/Cargo.toml new file mode 100644 index 0000000000..9c358d438b --- /dev/null +++ b/bitacross-worker/core-primitives/sgx/io/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "itp-sgx-io" +version = "0.8.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +# sgx deps +sgx_tstd = { optional = true, features = ["untrusted_fs"], branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } + +[features] +default = ["std"] +std = [] +sgx = [ + "sgx_tstd", +] diff --git a/bitacross-worker/core-primitives/sgx/io/src/lib.rs b/bitacross-worker/core-primitives/sgx/io/src/lib.rs new file mode 100644 index 0000000000..4f6d4eaa35 --- /dev/null +++ b/bitacross-worker/core-primitives/sgx/io/src/lib.rs @@ -0,0 +1,94 @@ +//! SGX file IO abstractions + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +use std::{ + convert::AsRef, + fs, + io::{Read, Result as IOResult, Write}, + path::Path, + string::String, + vec::Vec, +}; + +#[cfg(feature = "sgx")] +pub use sgx::*; + +/// Abstraction around IO that is supposed to use the `std::io::File` +pub trait IO: Sized { + type Error: From + std::fmt::Debug + 'static; + + fn read() -> Result; + fn write(&self) -> Result<(), Self::Error>; +} + +/// Abstraction around IO that is supposed to use `SgxFile`. We expose it also in `std` to +/// be able to put it as trait bounds in `std` and use it in tests. +/// +/// This is the static method (or associated function) version, should be made obsolete over time, +/// since it has state, but hides it in a global state. Makes it difficult to mock. +pub trait StaticSealedIO: Sized { + type Error: From + std::fmt::Debug + 'static; + + /// Type that is unsealed. + type Unsealed; + + fn unseal_from_static_file() -> Result; + fn seal_to_static_file(unsealed: &Self::Unsealed) -> Result<(), Self::Error>; +} + +/// Abstraction around IO that is supposed to use `SgxFile`. We expose it also in `std` to +/// be able to put it as trait bounds in `std` and use it in tests. +/// +pub trait SealedIO: Sized { + type Error: From + std::fmt::Debug + 'static; + + /// Type that is unsealed. + type Unsealed; + + fn unseal(&self) -> Result; + fn seal(&self, unsealed: &Self::Unsealed) -> Result<(), Self::Error>; +} + +pub fn read>(path: P) -> IOResult> { + let mut buf = Vec::new(); + fs::File::open(path).map(|mut f| f.read_to_end(&mut buf))??; + Ok(buf) +} + +pub fn write>(bytes: &[u8], path: P) -> IOResult<()> { + fs::File::create(path).map(|mut f| f.write_all(bytes))? +} + +pub fn read_to_string>(filepath: P) -> IOResult { + let mut contents = String::new(); + fs::File::open(filepath).map(|mut f| f.read_to_string(&mut contents))??; + Ok(contents) +} + +#[cfg(feature = "sgx")] +mod sgx { + use std::{ + convert::AsRef, + io::{Read, Result, Write}, + path::Path, + sgxfs::SgxFile, + vec::Vec, + }; + + pub fn unseal>(path: P) -> Result> { + let mut buf = Vec::new(); + SgxFile::open(path).map(|mut f| f.read_to_end(&mut buf))??; + Ok(buf) + } + + pub fn seal>(bytes: &[u8], path: P) -> Result<()> { + SgxFile::create(path).map(|mut f| f.write_all(bytes))? + } +} diff --git a/bitacross-worker/core-primitives/sgx/temp-dir/Cargo.toml b/bitacross-worker/core-primitives/sgx/temp-dir/Cargo.toml new file mode 100644 index 0000000000..c86fcafbd1 --- /dev/null +++ b/bitacross-worker/core-primitives/sgx/temp-dir/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "itp-sgx-temp-dir" +version = "0.1.0" +edition = "2021" + +[dependencies] +lazy_static = { version = "1.1.0", features = ["spin_no_std"] } + +# sgx deps +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } + +[dev-dependencies.safe-lock] +version = "^0.1" + +[features] +default = ["std"] +std = [] +sgx = [ + "sgx_tstd", +] diff --git a/bitacross-worker/core-primitives/sgx/temp-dir/src/lib.rs b/bitacross-worker/core-primitives/sgx/temp-dir/src/lib.rs new file mode 100644 index 0000000000..f8332fb74f --- /dev/null +++ b/bitacross-worker/core-primitives/sgx/temp-dir/src/lib.rs @@ -0,0 +1,192 @@ +//! # temp-dir +//! +//! Copied from the original tempdir crate with tiny adjustments for SGX-compatibility. +//! +//! Note: The temp-dir is deprecated and there might be uncovered security aspects. If we want to +//! use this in production, we should run some checks. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +use core::sync::atomic::{AtomicU32, Ordering}; +use std::{ + borrow::ToOwned, + collections::hash_map::RandomState, + format, + hash::{BuildHasher, Hasher}, + path::{Path, PathBuf}, + string::String, +}; + +/// Serve some low-security random ID to prevent temp-dir clashes across multiple processes. +fn rand_id() -> String { + // u64 always has more than 4 bytes so this never panics. + format!("{:x}", RandomState::new().build_hasher().finish())[..4].to_owned() +} + +lazy_static::lazy_static! { + /// A unique identifier, which is instanciated upon process start, but it is + /// not the process id itself. + /// + /// This is a workaround for `sgx_tstd` lib not exposing the `process::id()`. + pub static ref PROCESS_UNIQUE_ID: String = rand_id(); +} + +static COUNTER: AtomicU32 = AtomicU32::new(0); + +/// The path of an existing writable directory in a system temporary directory. +/// +/// Drop the struct to delete the directory and everything under it. +/// Deletes symbolic links and does not follow them. +/// +/// Ignores any error while deleting. +/// See [`TempDir::panic_on_cleanup_error`](struct.TempDir.html#method.panic_on_cleanup_error). +/// +/// # Example +/// ```rust +/// use itp_sgx_temp_dir::TempDir; +/// let d = TempDir::new().unwrap(); +/// // Prints "/tmp/t1a9b-0". +/// println!("{:?}", d.path()); +/// let f = d.child("file1"); +/// // Prints "/tmp/t1a9b-0/file1". +/// println!("{:?}", f); +/// std::fs::write(&f, b"abc").unwrap(); +/// assert_eq!( +/// "abc", +/// std::fs::read_to_string(&f).unwrap(), +/// ); +/// // Prints "/tmp/t1a9b-1". +/// println!("{:?}", TempDir::new().unwrap().path()); +/// ``` +#[derive(Clone, PartialOrd, Ord, PartialEq, Eq, Hash, Debug)] +pub struct TempDir { + path_buf: Option, + panic_on_delete_err: bool, +} +impl TempDir { + fn remove_dir(path: &Path) -> Result<(), std::io::Error> { + match std::fs::remove_dir_all(path) { + Ok(()) => Ok(()), + Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(()), + Err(e) => Err(std::io::Error::new( + e.kind(), + format!("error removing directory and contents {:?}: {}", path, e), + )), + } + } + + /// Create a new empty directory in a system temporary directory. + /// + /// Drop the struct to delete the directory and everything under it. + /// Deletes symbolic links and does not follow them. + /// + /// Ignores any error while deleting. + /// See [`TempDir::panic_on_cleanup_error`](struct.TempDir.html#method.panic_on_cleanup_error). + /// + /// # Errors + /// Returns `Err` when it fails to create the directory. + /// + /// # Example + /// ```rust + /// // Prints "/tmp/t1a9b-0". + /// println!("{:?}", itp_sgx_temp_dir::TempDir::new().unwrap().path()); + /// ``` + pub fn new() -> Result { + // Prefix with 't' to avoid name collisions with `temp-file` crate. + Self::with_prefix("t") + } + + /// Create a new empty directory in a system temporary directory. + /// Use `prefix` as the first part of the directory's name. + /// + /// Drop the struct to delete the directory and everything under it. + /// Deletes symbolic links and does not follow them. + /// + /// Ignores any error while deleting. + /// See [`TempDir::panic_on_cleanup_error`](struct.TempDir.html#method.panic_on_cleanup_error). + /// + /// # Errors + /// Returns `Err` when it fails to create the directory. + /// + /// # Example + /// ```rust + /// // Prints "/tmp/ok1a9b-0". + /// println!("{:?}", itp_sgx_temp_dir::TempDir::with_prefix("ok").unwrap().path()); + /// ``` + pub fn with_prefix(prefix: impl AsRef) -> Result { + let path_buf = std::env::temp_dir().join(format!( + "{}{}-{:x}", + prefix.as_ref(), + // std::process::id(), -> The original tempdir crate had this, but the sgx-std lib does not expose it. + *PROCESS_UNIQUE_ID, + COUNTER.fetch_add(1, Ordering::AcqRel), + )); + std::fs::create_dir(&path_buf).map_err(|e| { + std::io::Error::new( + e.kind(), + format!("error creating directory {:?}: {}", &path_buf, e), + ) + })?; + Ok(Self { path_buf: Some(path_buf), panic_on_delete_err: false }) + } + + /// Remove the directory on its contents now. Do nothing later on drop. + /// + /// # Errors + /// Returns an error if the directory exists and we fail to remove it and its contents. + #[allow(clippy::missing_panics_doc)] + pub fn cleanup(mut self) -> Result<(), std::io::Error> { + Self::remove_dir(&self.path_buf.take().unwrap()) + } + + /// Make the struct panic on Drop if it hits an error while + /// removing the directory or its contents. + #[must_use] + pub fn panic_on_cleanup_error(mut self) -> Self { + Self { path_buf: self.path_buf.take(), panic_on_delete_err: true } + } + + /// Do not delete the directory or its contents. + /// + /// This is useful when debugging a test. + pub fn leak(mut self) { + self.path_buf.take(); + } + + /// The path to the directory. + #[must_use] + #[allow(clippy::missing_panics_doc)] + pub fn path(&self) -> &Path { + self.path_buf.as_ref().unwrap() + } + + /// The path to `name` under the directory. + #[must_use] + #[allow(clippy::missing_panics_doc)] + pub fn child(&self, name: impl AsRef) -> PathBuf { + let mut result = self.path_buf.as_ref().unwrap().clone(); + result.push(name.as_ref()); + result + } +} +impl Drop for TempDir { + fn drop(&mut self) { + if let Some(path) = self.path_buf.take() { + let result = Self::remove_dir(&path); + if self.panic_on_delete_err { + if let Err(e) = result { + panic!("{}", e); + } + } + } + } +} + +#[cfg(test)] +mod test; diff --git a/bitacross-worker/core-primitives/sgx/temp-dir/src/test.rs b/bitacross-worker/core-primitives/sgx/temp-dir/src/test.rs new file mode 100644 index 0000000000..8b3ac50c43 --- /dev/null +++ b/bitacross-worker/core-primitives/sgx/temp-dir/src/test.rs @@ -0,0 +1,231 @@ +use crate::{TempDir, COUNTER}; +use core::sync::atomic::Ordering; +use safe_lock::SafeLock; +use std::{io::ErrorKind, path::Path}; + +// The error tests require all tests to run single-threaded. +static LOCK: SafeLock = SafeLock::new(); + +fn make_non_writable(path: &Path) { + assert!(std::process::Command::new("chmod") + .arg("-w") + .arg(path) + .status() + .unwrap() + .success()); +} + +fn make_writable(path: &Path) { + assert!(std::process::Command::new("chmod") + .arg("u+w") + .arg(path) + .status() + .unwrap() + .success()); +} + +fn should_skip_cleanup_test() -> bool { + // On Gitlab's shared CI runners, the cleanup always succeeds and the + // test fails. So we skip these tests when it's running on Gitlab CI. + // if std::env::current_dir().unwrap().starts_with("/builds/") { + // println!("Running on Gitlab CI. Skipping test."); + // return true; + // } + // false + + // The above code was from the original. However, for some reason the + // cleanup always succeeds on my local machine too. I am not sure why + // this is the case. So we skip them always for now. + true +} + +#[test] +fn new() { + let _guard = LOCK.lock(); + let temp_dir = TempDir::new().unwrap(); + println!("{:?}", temp_dir); + println!("{:?}", TempDir::new().unwrap()); + let metadata = std::fs::metadata(temp_dir.path()).unwrap(); + assert!(metadata.is_dir()); + let temp_dir2 = TempDir::new().unwrap(); + assert_ne!(temp_dir.path(), temp_dir2.path()); +} + +#[test] +fn new_error() { + let _guard = LOCK.lock(); + let previous_counter_value = COUNTER.load(Ordering::SeqCst); + let temp_dir = TempDir::new().unwrap(); + let dir_path = temp_dir.path().to_path_buf(); + COUNTER.store(previous_counter_value, Ordering::SeqCst); + let e = TempDir::new().unwrap_err(); + assert_eq!(std::io::ErrorKind::AlreadyExists, e.kind()); + assert!( + e.to_string().starts_with(&format!("error creating directory {:?}: ", dir_path)), + "unexpected error {:?}", + e + ); +} + +#[test] +fn with_prefix() { + let _guard = LOCK.lock(); + let temp_dir = TempDir::with_prefix("prefix1").unwrap(); + let name = temp_dir.path().file_name().unwrap(); + assert!(name.to_str().unwrap().starts_with("prefix1"), "{:?}", temp_dir); + let metadata = std::fs::metadata(temp_dir.path()).unwrap(); + assert!(metadata.is_dir()); + let temp_dir2 = TempDir::new().unwrap(); + assert_ne!(temp_dir.path(), temp_dir2.path()); +} + +#[test] +fn with_prefix_error() { + let _guard = LOCK.lock(); + let previous_counter_value = COUNTER.load(Ordering::SeqCst); + let temp_dir = TempDir::with_prefix("prefix1").unwrap(); + COUNTER.store(previous_counter_value, Ordering::SeqCst); + let e = TempDir::with_prefix("prefix1").unwrap_err(); + assert_eq!(std::io::ErrorKind::AlreadyExists, e.kind()); + assert!( + e.to_string() + .starts_with(&format!("error creating directory {:?}: ", temp_dir.path())), + "unexpected error {:?}", + e + ); +} + +#[test] +fn child() { + let _guard = LOCK.lock(); + let temp_dir = TempDir::new().unwrap(); + let file1_path = temp_dir.child("file1"); + assert!(file1_path.ends_with("file1"), "{:?}", file1_path.to_string_lossy()); + assert!(file1_path.starts_with(temp_dir.path()), "{:?}", file1_path.to_string_lossy()); + std::fs::write(&file1_path, b"abc").unwrap(); +} + +#[test] +fn cleanup() { + let _guard = LOCK.lock(); + let temp_dir = TempDir::new().unwrap(); + std::fs::write(&temp_dir.child("file1"), b"abc").unwrap(); + let dir_path = temp_dir.path().to_path_buf(); + std::fs::metadata(&dir_path).unwrap(); + temp_dir.cleanup().unwrap(); + assert_eq!(ErrorKind::NotFound, std::fs::metadata(&dir_path).unwrap_err().kind()); +} + +#[test] +fn cleanup_already_deleted() { + let _guard = LOCK.lock(); + let temp_dir = TempDir::new().unwrap(); + std::fs::remove_dir_all(temp_dir.path()).unwrap(); + temp_dir.cleanup().unwrap(); +} + +#[cfg(unix)] +#[test] +fn cleanup_error() { + if should_skip_cleanup_test() { + return + } + let _guard = LOCK.lock(); + let temp_dir = TempDir::new().unwrap(); + let dir_path = temp_dir.path().to_path_buf(); + let file1_path = temp_dir.child("file1"); + std::fs::write(&file1_path, b"abc").unwrap(); + make_non_writable(&dir_path); + let result = temp_dir.cleanup(); + std::fs::metadata(&dir_path).unwrap(); + std::fs::metadata(&file1_path).unwrap(); + make_writable(&dir_path); + std::fs::remove_dir_all(&dir_path).unwrap(); + let e = result.unwrap_err(); + assert_eq!(std::io::ErrorKind::PermissionDenied, e.kind()); + assert!( + e.to_string() + .starts_with(&format!("error removing directory and contents {:?}: ", dir_path)), + "unexpected error {:?}", + e + ); +} + +#[test] +fn test_drop() { + let _guard = LOCK.lock(); + let temp_dir = TempDir::new().unwrap(); + let dir_path = temp_dir.path().to_path_buf(); + let file1_path = temp_dir.child("file1"); + std::fs::write(&file1_path, b"abc").unwrap(); + TempDir::new().unwrap(); + std::fs::metadata(&dir_path).unwrap(); + std::fs::metadata(&file1_path).unwrap(); + drop(temp_dir); + assert_eq!(ErrorKind::NotFound, std::fs::metadata(&dir_path).unwrap_err().kind()); + assert_eq!(ErrorKind::NotFound, std::fs::metadata(&file1_path).unwrap_err().kind()); +} + +#[test] +fn drop_already_deleted() { + let _guard = LOCK.lock(); + let temp_dir = TempDir::new().unwrap(); + std::fs::remove_dir(temp_dir.path()).unwrap(); +} + +#[cfg(unix)] +#[test] +fn drop_error_ignored() { + if should_skip_cleanup_test() { + return + } + let _guard = LOCK.lock(); + let temp_dir = TempDir::new().unwrap(); + let dir_path = temp_dir.path().to_path_buf(); + let file1_path = temp_dir.child("file1"); + std::fs::write(&file1_path, b"abc").unwrap(); + make_non_writable(&dir_path); + drop(temp_dir); + std::fs::metadata(&dir_path).unwrap(); + std::fs::metadata(&file1_path).unwrap(); + make_writable(&dir_path); + std::fs::remove_dir_all(&dir_path).unwrap(); +} + +#[cfg(unix)] +#[test] +fn drop_error_panic() { + if should_skip_cleanup_test() { + return + } + let _guard = LOCK.lock(); + let temp_dir = TempDir::new().unwrap().panic_on_cleanup_error(); + let dir_path = temp_dir.path().to_path_buf(); + let file1_path = temp_dir.child("file1"); + std::fs::write(&file1_path, b"abc").unwrap(); + make_non_writable(&dir_path); + let result = std::panic::catch_unwind(move || drop(temp_dir)); + std::fs::metadata(&dir_path).unwrap(); + std::fs::metadata(&file1_path).unwrap(); + make_writable(&dir_path); + std::fs::remove_dir_all(&dir_path).unwrap(); + let msg = result.unwrap_err().downcast::().unwrap(); + assert!( + msg.contains("error removing directory and contents ",), + "unexpected panic message {:?}", + msg + ); +} + +#[test] +fn leak() { + let _guard = LOCK.lock(); + let temp_dir = TempDir::new().unwrap(); + let dir_path = temp_dir.path().to_path_buf(); + let file1_path = temp_dir.child("file1"); + std::fs::write(&file1_path, b"abc").unwrap(); + temp_dir.leak(); + std::fs::metadata(&dir_path).unwrap(); + std::fs::metadata(&file1_path).unwrap(); + std::fs::remove_dir_all(&dir_path).unwrap(); +} diff --git a/bitacross-worker/core-primitives/stf-executor/Cargo.toml b/bitacross-worker/core-primitives/stf-executor/Cargo.toml new file mode 100644 index 0000000000..0b34106bc4 --- /dev/null +++ b/bitacross-worker/core-primitives/stf-executor/Cargo.toml @@ -0,0 +1,97 @@ +[package] +name = "itp-stf-executor" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +hex = { version = "0.4.3", default-features = false, features = ["alloc"] } + +# sgx dependencies +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true, features = ["untrusted_time"] } +sgx_types = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } + +# local dependencies +itp-enclave-metrics = { path = "../enclave-metrics", default-features = false } +itp-node-api = { path = "../node-api", default-features = false } +itp-ocall-api = { path = "../ocall-api", default-features = false } +itp-sgx-crypto = { path = "../sgx/crypto", default-features = false } +itp-sgx-externalities = { default-features = false, path = "../substrate-sgx/externalities" } +itp-stf-interface = { path = "../stf-interface", default-features = false } +itp-stf-primitives = { path = "../stf-primitives", default-features = false } +itp-stf-state-handler = { path = "../stf-state-handler", default-features = false } +itp-stf-state-observer = { path = "../stf-state-observer", default-features = false } +itp-time-utils = { path = "../time-utils", default-features = false } +itp-top-pool-author = { path = "../top-pool-author", default-features = false } +itp-types = { path = "../types", default-features = false } + +# sgx enabled external libraries +thiserror_sgx = { optional = true, package = "thiserror", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3" } + +# std compatible external libraries (make sure these versions match with the sgx-enabled ones above) +thiserror = { version = "1.0", optional = true } + +# no-std dependencies +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +log = { version = "0.4", default-features = false } + +# substrate dependencies +sp-core = { default-features = false, features = ["full_crypto"], git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# dev dependencies +itc-parentchain-test = { path = "../../core/parentchain/test", optional = true, default-features = false } +itp-test = { path = "../test", default-features = false, optional = true } + +# litentry +litentry-primitives = { path = "../../litentry/primitives", default-features = false } + +[dev-dependencies] +itp-stf-state-observer = { path = "../stf-state-observer", features = ["mocks"] } +itp-stf-interface = { path = "../stf-interface", features = ["mocks"] } +itp-top-pool = { path = "../top-pool", features = ["mocks"] } +itp-test = { path = "../test" } + +[features] +default = ["std"] +std = [ + # local + "itp-node-api/std", + "itp-ocall-api/std", + "itp-sgx-crypto/std", + "itp-sgx-externalities/std", + "itp-stf-interface/std", + "itp-stf-state-handler/std", + "itp-stf-state-observer/std", + "itp-top-pool-author/std", + "itp-types/std", + "itp-time-utils/std", + # crates.io + "log/std", + "codec/std", + # substrate + "sp-core/std", + "sp-runtime/std", + "thiserror", + # litentry + "litentry-primitives/std", +] +sgx = [ + "sgx_tstd", + "itp-node-api/sgx", + "itp-sgx-crypto/sgx", + "itp-sgx-externalities/sgx", + "itp-stf-state-handler/sgx", + "itp-stf-state-observer/sgx", + "itp-top-pool-author/sgx", + "itp-time-utils/sgx", + "thiserror_sgx", + # litentry + "litentry-primitives/sgx", +] +test = [ + "itc-parentchain-test", + "itp-node-api/mocks", + "itp-test", +] +mocks = [] diff --git a/bitacross-worker/core-primitives/stf-executor/src/enclave_signer.rs b/bitacross-worker/core-primitives/stf-executor/src/enclave_signer.rs new file mode 100644 index 0000000000..7de6a1ff4c --- /dev/null +++ b/bitacross-worker/core-primitives/stf-executor/src/enclave_signer.rs @@ -0,0 +1,177 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + error::{Error, Result}, + traits::{StfEnclaveSigning, StfShardVaultQuery}, + H256, +}; +use codec::{Decode, Encode}; +use core::{fmt::Debug, marker::PhantomData}; +use itp_ocall_api::EnclaveAttestationOCallApi; +use itp_sgx_crypto::{ed25519_derivation::DeriveEd25519, key_repository::AccessKey}; +use itp_sgx_externalities::SgxExternalitiesTrait; +use itp_stf_interface::{system_pallet::SystemPalletAccountInterface, ShardVaultQuery}; +use itp_stf_primitives::{ + traits::TrustedCallSigning, + types::{AccountId, KeyPair}, +}; +use itp_stf_state_observer::traits::ObserveState; +use itp_top_pool_author::traits::AuthorApi; +use itp_types::{Index, ShardIdentifier}; +use log::*; +use sp_core::{ed25519::Pair as Ed25519Pair, Pair}; +use std::{boxed::Box, sync::Arc, vec::Vec}; + +pub struct StfEnclaveSigner< + OCallApi, + StateObserver, + ShieldingKeyRepository, + Stf, + TopPoolAuthor, + TCS, + G, +> { + state_observer: Arc, + ocall_api: Arc, + shielding_key_repo: Arc, + top_pool_author: Arc, + _phantom: PhantomData<(Stf, TCS, G)>, +} + +impl + StfEnclaveSigner +where + OCallApi: EnclaveAttestationOCallApi, + StateObserver: ObserveState, + StateObserver::StateType: SgxExternalitiesTrait, + ShieldingKeyRepository: AccessKey, + ::KeyType: DeriveEd25519, + Stf: SystemPalletAccountInterface + + ShardVaultQuery, + Stf::Index: Into, + TopPoolAuthor: AuthorApi + Send + Sync + 'static, + TCS: PartialEq + Encode + Decode + Debug + Send + Sync, + G: PartialEq + Encode + Decode + Debug + Send + Sync, +{ + pub fn new( + state_observer: Arc, + ocall_api: Arc, + shielding_key_repo: Arc, + top_pool_author: Arc, + ) -> Self { + Self { + state_observer, + ocall_api, + shielding_key_repo, + top_pool_author, + _phantom: Default::default(), + } + } + + fn get_enclave_account_nonce(&self, shard: &ShardIdentifier) -> Result { + let enclave_account = self.get_enclave_account()?; + let nonce = self + .state_observer + .observe_state(shard, move |state| Stf::get_account_nonce(state, &enclave_account))?; + + Ok(nonce) + } + + fn get_enclave_call_signing_key(&self) -> Result { + let shielding_key = self.shielding_key_repo.retrieve_key()?; + shielding_key.derive_ed25519().map_err(|e| e.into()) + } +} + +impl + StfEnclaveSigning + for StfEnclaveSigner +where + OCallApi: EnclaveAttestationOCallApi, + StateObserver: ObserveState, + StateObserver::StateType: SgxExternalitiesTrait, + ShieldingKeyRepository: AccessKey, + ::KeyType: DeriveEd25519, + Stf: SystemPalletAccountInterface + + ShardVaultQuery, + Stf::Index: Into, + TopPoolAuthor: AuthorApi + Send + Sync + 'static, + TCS: PartialEq + Encode + Decode + Debug + Send + Sync, + G: PartialEq + Encode + Decode + Debug + Send + Sync, +{ + fn get_enclave_account(&self) -> Result { + let enclave_call_signing_key = self.get_enclave_call_signing_key()?; + Ok(enclave_call_signing_key.public().into()) + } + + fn sign_call_with_self>( + &self, + trusted_call: &TC, + shard: &ShardIdentifier, + ) -> Result { + let mr_enclave = self.ocall_api.get_mrenclave_of_self()?; + let enclave_account = self.get_enclave_account()?; + let enclave_call_signing_key = self.get_enclave_call_signing_key()?; + + let current_nonce = self.get_enclave_account_nonce(shard)?; + let pending_tx_count = self + .top_pool_author + .get_pending_trusted_calls_for(*shard, &enclave_account) + .len(); + let pending_tx_count = + Index::try_from(pending_tx_count).map_err(|e| Error::Other(e.into()))?; + let adjusted_nonce: Index = current_nonce.into() + pending_tx_count; + + Ok(trusted_call.sign( + &KeyPair::Ed25519(Box::new(enclave_call_signing_key)), + adjusted_nonce, + &mr_enclave.m, + shard, + )) + } + + fn sign(&self, payload: &[u8]) -> Result<(AccountId, Vec)> { + let enclave_account = self.get_enclave_account()?; + let enclave_call_signing_key = self.get_enclave_call_signing_key()?; + + debug!(" [EnclaveSigner] VC pubkey: {:?}", enclave_call_signing_key.public().to_vec()); + Ok((enclave_account, enclave_call_signing_key.sign(payload).0.to_vec())) + } +} + +impl StfShardVaultQuery + for StfEnclaveSigner +where + OCallApi: EnclaveAttestationOCallApi, + StateObserver: ObserveState, + StateObserver::StateType: SgxExternalitiesTrait, + ShieldingKeyRepository: AccessKey, + ::KeyType: DeriveEd25519, + Stf: SystemPalletAccountInterface + + ShardVaultQuery, + Stf::Index: Into, + TopPoolAuthor: AuthorApi + Send + Sync + 'static, + TCS: PartialEq + Encode + Decode + Debug + Send + Sync, + G: PartialEq + Encode + Decode + Debug + Send + Sync, +{ + fn get_shard_vault(&self, shard: &ShardIdentifier) -> Result { + let vault = self.state_observer.observe_state(shard, move |state| Stf::get_vault(state))?; + + vault.ok_or_else(|| Error::Other("shard vault undefined".into())) + } +} diff --git a/bitacross-worker/core-primitives/stf-executor/src/error.rs b/bitacross-worker/core-primitives/stf-executor/src/error.rs new file mode 100644 index 0000000000..ec46defdcd --- /dev/null +++ b/bitacross-worker/core-primitives/stf-executor/src/error.rs @@ -0,0 +1,88 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use itp_stf_primitives::error::StfError; +use sgx_types::sgx_status_t; +use std::{boxed::Box, format}; + +pub type Result = core::result::Result; + +/// STF-Executor error +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("Trusted operation has invalid signature")] + GetterIsNotAuthorized, + #[error("Invalid or unsupported trusted call type")] + InvalidTrustedCallType, + #[error("SGX error, status: {0}")] + Sgx(sgx_status_t), + #[error("State handling error: {0}")] + StateHandler(#[from] itp_stf_state_handler::error::Error), + #[error("State observer error: {0}")] + StateObserver(#[from] itp_stf_state_observer::error::Error), + #[error("Node metadata error: {0:?}")] + NodeMetadata(itp_node_api::metadata::Error), + #[error("Node metadata provider error: {0:?}")] + NodeMetadataProvider(#[from] itp_node_api::metadata::provider::Error), + #[error("STF error: {0}")] + Stf(StfError), + #[error("Ocall Api error: {0}")] + OcallApi(itp_ocall_api::Error), + #[error("Crypto error: {0}")] + Crypto(itp_sgx_crypto::error::Error), + #[error(transparent)] + Other(#[from] Box), +} + +impl From for Error { + fn from(sgx_status: sgx_status_t) -> Self { + Self::Sgx(sgx_status) + } +} + +impl From for Error { + fn from(e: codec::Error) -> Self { + Self::Other(format!("{:?}", e).into()) + } +} + +impl From for Error { + fn from(error: StfError) -> Self { + Self::Stf(error) + } +} + +impl From for Error { + fn from(error: itp_ocall_api::Error) -> Self { + Self::OcallApi(error) + } +} + +impl From for Error { + fn from(error: itp_sgx_crypto::error::Error) -> Self { + Self::Crypto(error) + } +} + +impl From for Error { + fn from(e: itp_node_api::metadata::Error) -> Self { + Self::NodeMetadata(e) + } +} diff --git a/bitacross-worker/core-primitives/stf-executor/src/executor.rs b/bitacross-worker/core-primitives/stf-executor/src/executor.rs new file mode 100644 index 0000000000..a980563774 --- /dev/null +++ b/bitacross-worker/core-primitives/stf-executor/src/executor.rs @@ -0,0 +1,381 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + error::{Error, Result}, + traits::{StatePostProcessing, StateUpdateProposer, StfUpdateState}, + BatchExecutionResult, ExecutedOperation, +}; +use codec::{Decode, Encode}; +use itp_enclave_metrics::EnclaveMetric; +use itp_node_api::metadata::{provider::AccessNodeMetadata, NodeMetadataTrait}; +use itp_ocall_api::{EnclaveAttestationOCallApi, EnclaveMetricsOCallApi, EnclaveOnChainOCallApi}; +use itp_sgx_externalities::{SgxExternalitiesTrait, StateHash}; +use itp_stf_interface::{ + parentchain_pallet::ParentchainPalletInterface, runtime_upgrade::RuntimeUpgradeInterface, + StateCallInterface, StfExecutionResult, UpdateState, +}; +use itp_stf_primitives::{ + traits::TrustedCallVerification, + types::{ShardIdentifier, TrustedOperation, TrustedOperationOrHash}, +}; +use itp_stf_state_handler::{handle_state::HandleState, query_shard_state::QueryShardState}; +use itp_time_utils::duration_now; +use itp_types::{ + parentchain::{Header as ParentchainHeader, ParentchainCall, ParentchainId}, + storage::StorageEntryVerified, + H256, +}; +use log::*; +use sp_runtime::traits::Header as HeaderTrait; +use std::{ + collections::BTreeMap, fmt::Debug, marker::PhantomData, string::ToString, sync::Arc, + time::Duration, vec, vec::Vec, +}; + +pub struct StfExecutor +where + TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, + G: PartialEq + Encode + Decode + Debug + Clone + Send + Sync, +{ + ocall_api: Arc, + state_handler: Arc, + node_metadata_repo: Arc, + _phantom: PhantomData<(Stf, TCS, G)>, +} + +impl + StfExecutor +where + OCallApi: EnclaveAttestationOCallApi + EnclaveOnChainOCallApi + EnclaveMetricsOCallApi, + StateHandler: HandleState, + StateHandler::StateT: SgxExternalitiesTrait + Encode, + NodeMetadataRepository: AccessNodeMetadata, + NodeMetadataRepository::MetadataType: NodeMetadataTrait, + Stf: UpdateState< + StateHandler::StateT, + ::SgxExternalitiesDiffType, + > + StateCallInterface, + ::SgxExternalitiesDiffType: + IntoIterator, Option>)> + From, Option>>>, + >::Error: Debug, + TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, + G: PartialEq + Encode + Decode + Debug + Clone + Send + Sync, +{ + pub fn new( + ocall_api: Arc, + state_handler: Arc, + node_metadata_repo: Arc, + ) -> Self { + StfExecutor { ocall_api, state_handler, node_metadata_repo, _phantom: PhantomData } + } + + /// Execute a trusted call on the STF + /// + /// We distinguish between an error in the execution, which maps to `Err` and + /// an invalid trusted call, which results in `Ok(ExecutionStatus::Failure)`. The latter + /// can be used to remove the trusted call from a queue. In the former case we might keep the + /// trusted call and just re-try the operation. + fn execute_trusted_call_on_stf( + &self, + state: &mut StateHandler::StateT, + trusted_operation: &TrustedOperation, + _header: &PH, + shard: &ShardIdentifier, + post_processing: StatePostProcessing, + ) -> Result> + where + PH: HeaderTrait, + { + debug!("query mrenclave of self"); + let mrenclave = self.ocall_api.get_mrenclave_of_self()?; + + let top_or_hash = TrustedOperationOrHash::from_top(trusted_operation.clone()); + let operation_hash = trusted_operation.hash(); + debug!("Operation hash {:?}", operation_hash); + + // TODO(Litentry): do we need to send any error notification to parachain? + let trusted_call = match trusted_operation.to_call().ok_or(Error::InvalidTrustedCallType) { + Ok(c) => c, + Err(e) => { + error!("Error: {:?}", e); + return Ok(ExecutedOperation::failed(operation_hash, top_or_hash, vec![], vec![])) + }, + }; + + if !trusted_call.verify_signature(&mrenclave.m, &shard) { + error!("TrustedCallSigned: bad signature"); + return Ok(ExecutedOperation::failed(operation_hash, top_or_hash, vec![], vec![])) + } + + debug!("execute on STF, call with nonce {}", trusted_call.nonce()); + let mut extrinsic_call_backs: Vec = Vec::new(); + return match Stf::execute_call( + state, + shard, + trusted_call.clone(), + trusted_operation.hash(), + &mut extrinsic_call_backs, + self.node_metadata_repo.clone(), + ) { + Err(e) => { + error!("Stf execute failed: {:?}", e); + let rpc_response_value: Vec = e.encode(); + Ok(ExecutedOperation::failed( + operation_hash, + top_or_hash, + extrinsic_call_backs, + rpc_response_value, + )) + }, + Ok(result) => { + let force_connection_wait = result.force_connection_wait(); + let rpc_response_value: Vec = result.get_encoded_result(); + if let StatePostProcessing::Prune = post_processing { + state.prune_state_diff(); + } + for call in extrinsic_call_backs.clone() { + trace!( + "trusted_call wants to send encoded call: 0x{}", + hex::encode(call.encode()) + ); + } + Ok(ExecutedOperation::success( + operation_hash, + top_or_hash, + extrinsic_call_backs, + rpc_response_value, + force_connection_wait, + )) + }, + } + } +} + +impl + StfUpdateState + for StfExecutor +where + OCallApi: EnclaveAttestationOCallApi + EnclaveOnChainOCallApi, + StateHandler: HandleState + QueryShardState, + StateHandler::StateT: SgxExternalitiesTrait + Encode, + NodeMetadataRepository: AccessNodeMetadata, + Stf: UpdateState< + StateHandler::StateT, + ::SgxExternalitiesDiffType, + > + ParentchainPalletInterface, + ::SgxExternalitiesDiffType: + IntoIterator, Option>)>, + >::Error: Debug, + ::SgxExternalitiesDiffType: + From, Option>>>, + TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, + G: PartialEq + Encode + Decode + Debug + Clone + Send + Sync, +{ + fn update_states( + &self, + header: &ParentchainHeader, + parentchain_id: &ParentchainId, + ) -> Result<()> { + debug!("Update STF storage upon block import!"); + let storage_hashes = Stf::storage_hashes_to_update_on_block(parentchain_id); + + if storage_hashes.is_empty() { + return Ok(()) + } + + // global requests they are the same for every shard + let state_diff_update = self + .ocall_api + .get_multiple_storages_verified(storage_hashes, header, parentchain_id) + .map(into_map)?; + + // Update parentchain block on all states. + // TODO: Investigate if this is still necessary. We load and clone the entire state here, + // which scales badly for increasing state size. + let shards = self.state_handler.list_shards()?; + for shard_id in shards { + let (state_lock, mut state) = self.state_handler.load_for_mutation(&shard_id)?; + match Stf::update_parentchain_block(&mut state, header.clone()) { + Ok(_) => { + self.state_handler.write_after_mutation(state, state_lock, &shard_id)?; + }, + Err(e) => error!("Could not update parentchain block. {:?}: {:?}", shard_id, e), + } + } + + if parentchain_id != &ParentchainId::Litentry { + // nothing else to do + return Ok(()) + } + + // look for new shards and initialize them + if let Some(maybe_shards) = state_diff_update.get(&shards_key_hash()) { + match maybe_shards { + Some(shards) => self.initialize_new_shards(header, &state_diff_update, &shards)?, + None => debug!("No shards are on the chain yet"), + }; + }; + Ok(()) + } +} + +impl + StfExecutor +where + ::SgxExternalitiesDiffType: + From, Option>>> + IntoIterator, Option>)>, + >::Error: Debug, + NodeMetadataRepository: AccessNodeMetadata, + OCallApi: EnclaveAttestationOCallApi + EnclaveOnChainOCallApi, + StateHandler: HandleState + QueryShardState, + StateHandler::StateT: Encode + SgxExternalitiesTrait, + Stf: ParentchainPalletInterface + + UpdateState< + StateHandler::StateT, + ::SgxExternalitiesDiffType, + >, + TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, + G: PartialEq + Encode + Decode + Debug + Clone + Send + Sync, +{ + fn initialize_new_shards( + &self, + header: &ParentchainHeader, + state_diff_update: &BTreeMap, Option>>, + shards: &Vec, + ) -> Result<()> { + let shards: Vec = Decode::decode(&mut shards.as_slice())?; + + for shard_id in shards { + let (state_lock, mut state) = self.state_handler.load_for_mutation(&shard_id)?; + trace!("Successfully loaded state, updating states ..."); + + // per shard (cid) requests + let per_shard_hashes = storage_hashes_to_update_per_shard(&shard_id); + let per_shard_update = self + .ocall_api + .get_multiple_storages_verified(per_shard_hashes, header, &ParentchainId::Litentry) + .map(into_map)?; + + Stf::apply_state_diff(&mut state, per_shard_update.into()); + Stf::apply_state_diff(&mut state, state_diff_update.clone().into()); + if let Err(e) = Stf::update_parentchain_block(&mut state, header.clone()) { + error!("Could not update parentchain block. {:?}: {:?}", shard_id, e) + } + + self.state_handler.write_after_mutation(state, state_lock, &shard_id)?; + } + Ok(()) + } +} + +impl StateUpdateProposer + for StfExecutor +where + OCallApi: EnclaveAttestationOCallApi + EnclaveOnChainOCallApi + EnclaveMetricsOCallApi, + StateHandler: HandleState, + StateHandler::StateT: SgxExternalitiesTrait + Encode + StateHash, + ::SgxExternalitiesType: Encode, + NodeMetadataRepository: AccessNodeMetadata, + NodeMetadataRepository::MetadataType: NodeMetadataTrait, + Stf: UpdateState< + StateHandler::StateT, + ::SgxExternalitiesDiffType, + > + StateCallInterface + + RuntimeUpgradeInterface, + ::SgxExternalitiesDiffType: + IntoIterator, Option>)>, + ::SgxExternalitiesDiffType: + From, Option>>>, + >::Error: Debug, + >::Error: Debug, + TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, + G: PartialEq + Encode + Decode + Debug + Clone + Send + Sync, +{ + type Externalities = StateHandler::StateT; + + fn propose_state_update( + &self, + trusted_calls: &[TrustedOperation], + header: &PH, + shard: &ShardIdentifier, + max_exec_duration: Duration, + prepare_state_function: F, + ) -> Result> + where + PH: HeaderTrait, + F: FnOnce(Self::Externalities) -> Self::Externalities, + { + let ends_at = duration_now() + max_exec_duration; + + let (state, state_hash_before_execution) = self.state_handler.load_cloned(shard)?; + + // Execute any pre-processing steps. + let mut state = prepare_state_function(state); + let mut executed_and_failed_calls = Vec::>::new(); + + // TODO: maybe we can move it to `prepare_state_function`. It seems more reasonable. + let _ = Stf::on_runtime_upgrade(&mut state); + + // Iterate through all calls until time is over. + for trusted_call_signed in trusted_calls.into_iter() { + // Break if allowed time window is over. + if ends_at < duration_now() { + info!("Aborting execution of trusted calls because slot time is up"); + break + } + + match self.execute_trusted_call_on_stf( + &mut state, + &trusted_call_signed, + header, + shard, + StatePostProcessing::None, + ) { + Ok(executed_or_failed_call) => { + executed_and_failed_calls.push(executed_or_failed_call); + }, + Err(e) => { + error!("Fatal Error. Failed to attempt call execution: {:?}", e); + }, + }; + } + + Ok(BatchExecutionResult { + executed_operations: executed_and_failed_calls, + state_hash_before_execution, + state_after_execution: state, + }) + } +} + +fn into_map( + storage_entries: Vec>>, +) -> BTreeMap, Option>> { + storage_entries.into_iter().map(|e| e.into_tuple()).collect() +} + +// todo: we need to clarify where these functions belong and if we need them at all. moved them from ita-stf but we can no longer depend on that +pub fn storage_hashes_to_update_per_shard(_shard: &ShardIdentifier) -> Vec> { + Vec::new() +} + +pub fn shards_key_hash() -> Vec { + // here you have to point to a storage value containing a Vec of + // ShardIdentifiers the enclave uses this to autosubscribe to no shards + vec![] +} diff --git a/bitacross-worker/core-primitives/stf-executor/src/executor_tests.rs b/bitacross-worker/core-primitives/stf-executor/src/executor_tests.rs new file mode 100644 index 0000000000..458adc04b5 --- /dev/null +++ b/bitacross-worker/core-primitives/stf-executor/src/executor_tests.rs @@ -0,0 +1,279 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{executor::StfExecutor, traits::StateUpdateProposer}; +use codec::Encode; +use itc_parentchain_test::ParentchainHeaderBuilder; +use itp_node_api::metadata::{metadata_mocks::NodeMetadataMock, provider::NodeMetadataRepository}; +use itp_ocall_api::EnclaveAttestationOCallApi; +use itp_sgx_externalities::{SgxExternalities as State, SgxExternalitiesTrait}; +use itp_stf_primitives::{traits::TrustedCallSigning, types::ShardIdentifier}; +use itp_stf_state_handler::handle_state::HandleState; +use itp_test::mock::{ + handle_state_mock::HandleStateMock, + onchain_mock::OnchainMock, + stf_mock::{GetterMock, StfMock, TrustedCallMock, TrustedCallSignedMock}, +}; +use itp_types::H256; +use sp_core::{ed25519, Pair}; +use sp_runtime::app_crypto::sp_core::blake2_256; +use std::{sync::Arc, time::Duration, vec}; +// FIXME: Create unit tests for update_states, execute_shield_funds, execute_trusted_call, execute_trusted_call_on_stf #554 + +pub fn propose_state_update_executes_all_calls_given_enough_time() { + // given + let (stf_executor, ocall_api, state_handler) = stf_executor(); + let mrenclave = ocall_api.get_mrenclave_of_self().unwrap().m; + let (_, shard) = init_state_and_shard_with_state_handler(state_handler.as_ref()); + let sender = endowed_account(); + let signed_call_1 = TrustedCallMock::balance_transfer( + sender.public().into(), + sender.public().into(), + 42, + ) + .sign(&sender.clone().into(), 0, &mrenclave, &shard); + let trusted_operation_1 = signed_call_1.into_trusted_operation(true); + let call_operation_hash_1: H256 = blake2_256(&trusted_operation_1.encode()).into(); + let signed_call_2 = + TrustedCallMock::balance_transfer(sender.public().into(), sender.public().into(), 100) + .sign(&sender.clone().into(), 1, &mrenclave, &shard); + let trusted_operation_2 = signed_call_2.into_trusted_operation(true); + let call_operation_hash_2: H256 = blake2_256(&trusted_operation_2.encode()).into(); + + let (_, old_state_hash) = state_handler.load_cloned(&shard).unwrap(); + + // when + let batch_execution_result = stf_executor + .propose_state_update( + &vec![trusted_operation_1, trusted_operation_2], + &ParentchainHeaderBuilder::default().build(), + &shard, + Duration::from_secs(1000), + |state| state, + ) + .unwrap(); + + // then + assert_eq!(old_state_hash, batch_execution_result.state_hash_before_execution); + assert_eq!(batch_execution_result.executed_operations.len(), 2); + assert_eq!( + batch_execution_result.get_executed_operation_hashes(), + vec![call_operation_hash_1, call_operation_hash_2] + ); + // Ensure that state has been updated and not actually written. + assert_ne!( + state_handler.load_cloned(&shard).unwrap().0, + batch_execution_result.state_after_execution + ); +} + +pub fn propose_state_update_executes_only_one_trusted_call_given_not_enough_time() { + // given + let (stf_executor, ocall_api, state_handler) = stf_executor(); + let mrenclave = ocall_api.get_mrenclave_of_self().unwrap().m; + let (_, shard) = init_state_and_shard_with_state_handler(state_handler.as_ref()); + let sender = endowed_account(); + let signed_call_1 = TrustedCallMock::waste_time_ms(sender.public().into(), 10).sign( + &sender.clone().into(), + 0, + &mrenclave, + &shard, + ); + let trusted_operation_1 = signed_call_1.into_trusted_operation(true); + let call_operation_hash_1: H256 = blake2_256(&trusted_operation_1.encode()).into(); + + let signed_call_2 = TrustedCallMock::waste_time_ms(sender.public().into(), 10).sign( + &sender.clone().into(), + 0, + &mrenclave, + &shard, + ); + let trusted_operation_2 = signed_call_2.into_trusted_operation(true); + + let (_, old_state_hash) = state_handler.load_cloned(&shard).unwrap(); + // when + let batch_execution_result = stf_executor + .propose_state_update( + &vec![trusted_operation_1.clone(), trusted_operation_2.clone()], + &ParentchainHeaderBuilder::default().build(), + &shard, + Duration::from_millis(5), + |state| state, + ) + .unwrap(); + + // then + assert_eq!(old_state_hash, batch_execution_result.state_hash_before_execution); + assert_eq!(batch_execution_result.executed_operations.len(), 1); + assert_eq!(batch_execution_result.get_executed_operation_hashes(), vec![call_operation_hash_1]); + // Ensure that state has been updated and not actually written. + assert_ne!( + state_handler.load_cloned(&shard).unwrap().0, + batch_execution_result.state_after_execution + ); +} + +pub fn propose_state_update_executes_noop_leaving_state_untouched() { + // given + let (stf_executor, ocall_api, state_handler) = stf_executor(); + let mrenclave = ocall_api.get_mrenclave_of_self().unwrap().m; + let (_, shard) = init_state_and_shard_with_state_handler(state_handler.as_ref()); + let sender = endowed_account(); + let signed_call_1 = TrustedCallMock::noop(sender.public().into()).sign( + &sender.clone().into(), + 0, + &mrenclave, + &shard, + ); + let trusted_operation_1 = signed_call_1.into_trusted_operation(true); + let call_operation_hash_1: H256 = blake2_256(&trusted_operation_1.encode()).into(); + + let (_, old_state_hash) = state_handler.load_cloned(&shard).unwrap(); + // when + let batch_execution_result = stf_executor + .propose_state_update( + &vec![trusted_operation_1.clone()], + &ParentchainHeaderBuilder::default().build(), + &shard, + Duration::from_millis(5), // 1000 yields 0, 2000 yields 1, 4000 yields 1, 25_000 yields 2 + |state| state, + ) + .unwrap(); + + // then + assert_eq!(old_state_hash, batch_execution_result.state_hash_before_execution); + assert_eq!(batch_execution_result.executed_operations.len(), 1); + assert_eq!(batch_execution_result.get_executed_operation_hashes(), vec![call_operation_hash_1]); + assert_eq!( + state_handler.load_cloned(&shard).unwrap().0, + batch_execution_result.state_after_execution + ); +} + +pub fn propose_state_update_executes_no_trusted_calls_given_no_time() { + // given + let (stf_executor, ocall_api, state_handler) = stf_executor(); + let mrenclave = ocall_api.get_mrenclave_of_self().unwrap().m; + let (_, shard) = init_state_and_shard_with_state_handler(state_handler.as_ref()); + let sender = endowed_account(); + let signed_call_1 = TrustedCallMock::balance_transfer( + sender.public().into(), + sender.public().into(), + 42, + ) + .sign(&sender.clone().into(), 0, &mrenclave, &shard); + let trusted_operation_1 = signed_call_1.into_trusted_operation(true); + + let signed_call_2 = + TrustedCallMock::balance_transfer(sender.public().into(), sender.public().into(), 100) + .sign(&sender.clone().into(), 0, &mrenclave, &shard); + let trusted_operation_2 = signed_call_2.into_trusted_operation(true); + + let (_, old_state_hash) = state_handler.load_cloned(&shard).unwrap(); + + // when + let batch_execution_result = stf_executor + .propose_state_update( + &vec![trusted_operation_1.clone(), trusted_operation_2.clone()], + &ParentchainHeaderBuilder::default().build(), + &shard, + Duration::ZERO, + |state| state, + ) + .unwrap(); + + // then + assert_eq!(old_state_hash, batch_execution_result.state_hash_before_execution); + assert_eq!(batch_execution_result.executed_operations.len(), 0); + assert_eq!(batch_execution_result.get_executed_operation_hashes(), vec![]); +} + +pub fn propose_state_update_always_executes_preprocessing_step() { + // given + let shard = ShardIdentifier::default(); + let (stf_executor, _, state_handler) = stf_executor(); + let _init_hash = state_handler.initialize_shard(shard).unwrap(); + let key = "my_key".encode(); + let value = "my_value".encode(); + let (old_state, old_state_hash) = state_handler.load_cloned(&shard).unwrap(); + + // when + let batch_execution_result = stf_executor + .propose_state_update( + &vec![], + &ParentchainHeaderBuilder::default().build(), + &shard, + Duration::ZERO, + |mut state| { + state.insert(key.clone(), value.clone()); + state + }, + ) + .unwrap(); + + // then + assert_eq!(old_state_hash, batch_execution_result.state_hash_before_execution); + + // Ensure that state has been updated. + let retrieved_value = batch_execution_result.state_after_execution.get(key.as_slice()).unwrap(); + assert_eq!(*retrieved_value, value); + // Ensure that state has not been actually written. + assert_ne!(old_state, batch_execution_result.state_after_execution); +} + +// Helper Functions +fn stf_executor() -> ( + StfExecutor< + OnchainMock, + HandleStateMock, + NodeMetadataRepository, + StfMock, + TrustedCallSignedMock, + GetterMock, + >, + Arc, + Arc, +) { + let ocall_api = Arc::new(OnchainMock::default()); + let state_handler = Arc::new(HandleStateMock::default()); + let node_metadata_repo = Arc::new(NodeMetadataRepository::new(NodeMetadataMock::new())); + let executor = StfExecutor::new(ocall_api.clone(), state_handler.clone(), node_metadata_repo); + (executor, ocall_api, state_handler) +} + +/// Returns a test setup initialized `State` with the corresponding `ShardIdentifier`. +pub(crate) fn init_state_and_shard_with_state_handler>( + state_handler: &S, +) -> (State, ShardIdentifier) { + let shard = ShardIdentifier::default(); + let _hash = state_handler.initialize_shard(shard).unwrap(); + + let (lock, mut state) = state_handler.load_for_mutation(&shard).unwrap(); + test_genesis_setup(&mut state); + + state_handler.write_after_mutation(state.clone(), lock, &shard).unwrap(); + + (state, shard) +} + +pub fn endowed_account() -> ed25519::Pair { + ed25519::Pair::from_seed(&[42u8; 32].into()) +} + +pub fn test_genesis_setup(_state: &mut impl SgxExternalitiesTrait) { + // set alice sudo account +} diff --git a/bitacross-worker/core-primitives/stf-executor/src/getter_executor.rs b/bitacross-worker/core-primitives/stf-executor/src/getter_executor.rs new file mode 100644 index 0000000000..b968efc18b --- /dev/null +++ b/bitacross-worker/core-primitives/stf-executor/src/getter_executor.rs @@ -0,0 +1,137 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Getter executor uses the state observer to get the most recent state and runs the getter on it. +//! The getter is verified (signature verfification) inside the `GetState` implementation. + +use crate::{error::Result, state_getter::GetState}; +use codec::Decode; +use itp_stf_primitives::traits::GetterAuthorization; +use itp_stf_state_observer::traits::ObserveState; +use itp_types::ShardIdentifier; +use log::*; +use std::{marker::PhantomData, sync::Arc, time::Instant, vec::Vec}; + +/// Trait to execute a getter for a specific shard. +pub trait ExecuteGetter { + fn execute_getter( + &self, + shard: &ShardIdentifier, + encoded_signed_getter: Vec, + ) -> Result>>; +} + +pub struct GetterExecutor +where + G: PartialEq, +{ + state_observer: Arc, + _phantom: PhantomData, + _phantom_getter: PhantomData, +} + +impl GetterExecutor +where + G: PartialEq, +{ + pub fn new(state_observer: Arc) -> Self { + Self { state_observer, _phantom: Default::default(), _phantom_getter: Default::default() } + } +} + +impl ExecuteGetter for GetterExecutor +where + StateObserver: ObserveState, + StateGetter: GetState, + G: PartialEq + Decode + GetterAuthorization, +{ + fn execute_getter( + &self, + shard: &ShardIdentifier, + encoded_signed_getter: Vec, + ) -> Result>> { + let getter = G::decode(&mut encoded_signed_getter.as_slice())?; + trace!("Successfully decoded trusted getter"); + + let getter_timer_start = Instant::now(); + let state_result = self + .state_observer + .observe_state(shard, |state| StateGetter::get_state(getter, state))??; + + debug!("Getter executed in {} ms", getter_timer_start.elapsed().as_millis()); + + Ok(state_result) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use codec::{Decode, Encode}; + + use itp_stf_state_observer::mock::ObserveStateMock; + use itp_test::mock::stf_mock::{ + GetterMock, PublicGetterMock, TrustedGetterMock, TrustedGetterSignedMock, + }; + + type TestState = u64; + type TestStateObserver = ObserveStateMock; + + struct TestStateGetter; + impl GetState for TestStateGetter { + fn get_state(_getter: GetterMock, state: &mut TestState) -> Result>> { + Ok(Some(state.encode())) + } + } + + type TestGetterExecutor = GetterExecutor; + + #[test] + fn executing_getters_works() { + let test_state = 23489u64; + let state_observer = Arc::new(TestStateObserver::new(test_state)); + let getter_executor = TestGetterExecutor::new(state_observer); + let getter = GetterMock::trusted(dummy_trusted_getter()); + + let state_result = getter_executor + .execute_getter(&ShardIdentifier::default(), getter.encode()) + .unwrap() + .unwrap(); + let decoded_state: TestState = Decode::decode(&mut state_result.as_slice()).unwrap(); + assert_eq!(decoded_state, test_state); + } + + #[test] + fn executing_public_getter_works() { + let test_state = 23489u64; + let state_observer = Arc::new(TestStateObserver::new(test_state)); + let getter_executor = TestGetterExecutor::new(state_observer); + let getter = GetterMock::public(PublicGetterMock::some_value); + + let state_result = getter_executor + .execute_getter(&ShardIdentifier::default(), getter.encode()) + .unwrap() + .unwrap(); + let decoded_state: TestState = Decode::decode(&mut state_result.as_slice()).unwrap(); + assert_eq!(decoded_state, test_state); + } + fn dummy_trusted_getter() -> TrustedGetterSignedMock { + TrustedGetterSignedMock { getter: TrustedGetterMock::some_value, signature: true } + // TrustedGetter::nonce(AccountId::new([0u8; 32])), + // MultiSignature::Ed25519(Signature::from_raw([0u8; 64])), + } +} diff --git a/bitacross-worker/core-primitives/stf-executor/src/lib.rs b/bitacross-worker/core-primitives/stf-executor/src/lib.rs new file mode 100644 index 0000000000..90e04cff44 --- /dev/null +++ b/bitacross-worker/core-primitives/stf-executor/src/lib.rs @@ -0,0 +1,305 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(test, feature(assert_matches))] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +use codec::{Decode, Encode}; +use core::fmt::Debug; +use itp_sgx_externalities::SgxExternalitiesTrait; +use itp_stf_primitives::types::TrustedOperationOrHash; +use itp_types::{parentchain::ParentchainCall, H256}; +use std::vec::Vec; + +// re-export module to properly feature gate sgx and regular std environment +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod sgx_reexport_prelude { + pub use thiserror_sgx as thiserror; +} + +pub mod error; +pub mod getter_executor; +pub mod state_getter; +pub mod traits; + +#[cfg(feature = "sgx")] +pub mod executor; + +#[cfg(feature = "sgx")] +pub mod enclave_signer; + +#[cfg(all(feature = "sgx", feature = "test"))] +pub mod executor_tests; + +#[cfg(feature = "mocks")] +pub mod mocks; + +pub type RpcResponseValue = Vec; + +/// Execution status of a trusted operation +/// +/// In case of success, it includes the operation hash, as well as +/// any extrinsic callbacks (e.g. unshield extrinsics) that need to be executed on-chain +/// +/// Litentry: +/// we have made a few changes: +/// - we add the encoded rpc response that will be passed back to the requester +/// - for failed top, we apply the parachain effects too +#[derive(Clone, Debug, PartialEq)] +pub enum ExecutionStatus { + Success(H256, Vec, RpcResponseValue, bool), + Failure(H256, Vec, RpcResponseValue), +} + +impl ExecutionStatus { + pub fn get_extrinsic_callbacks(&self) -> Vec { + match self { + ExecutionStatus::Success(_, opaque_calls, _, _) => opaque_calls.clone(), + ExecutionStatus::Failure(_, opaque_calls, _) => opaque_calls.clone(), + } + } + + pub fn get_executed_operation_hash(&self) -> Option { + match self { + ExecutionStatus::Success(operation_hash, _, _, _) => Some(*operation_hash), + _ => None, + } + } + + pub fn get_operation_hash(&self) -> H256 { + match self { + ExecutionStatus::Success(operation_hash, _, _, _) => *operation_hash, + ExecutionStatus::Failure(operation_hash, _, _) => *operation_hash, + } + } + + pub fn get_rpc_response_value(&self) -> RpcResponseValue { + match self { + ExecutionStatus::Success(_, _, res, _) => res.clone(), + ExecutionStatus::Failure(_, _, res) => res.clone(), + } + } + + pub fn get_force_wait(&self) -> bool { + match self { + ExecutionStatus::Success(_, _, _, wait) => *wait, + _ => false, + } + } +} + +/// Information about an executed trusted operation +/// +/// +#[derive(Clone, Debug, PartialEq)] +pub struct ExecutedOperation +where + TCS: PartialEq + Encode + Decode + Debug + Send + Sync, + G: PartialEq + Encode + Decode + Debug + Send + Sync, +{ + pub status: ExecutionStatus, + pub trusted_operation_or_hash: TrustedOperationOrHash, +} + +impl ExecutedOperation +where + TCS: PartialEq + Encode + Decode + Debug + Send + Sync, + G: PartialEq + Encode + Decode + Debug + Send + Sync, +{ + /// Constructor for a successfully executed trusted operation. + pub fn success( + operation_hash: H256, + trusted_operation_or_hash: TrustedOperationOrHash, + extrinsic_call_backs: Vec, + rpc_response_value: RpcResponseValue, + force_connection_wait: bool, + ) -> Self { + ExecutedOperation { + status: ExecutionStatus::Success( + operation_hash, + extrinsic_call_backs, + rpc_response_value, + force_connection_wait, + ), + trusted_operation_or_hash, + } + } + + /// Constructor for a failed trusted operation execution. + pub fn failed( + operation_hash: H256, + trusted_operation_or_hash: TrustedOperationOrHash, + extrinsic_call_backs: Vec, + rpc_response_value: RpcResponseValue, + ) -> Self { + ExecutedOperation { + status: ExecutionStatus::Failure( + operation_hash, + extrinsic_call_backs, + rpc_response_value, + ), + trusted_operation_or_hash, + } + } + + /// Returns true if the executed operation was a success. + pub fn is_success(&self) -> bool { + matches!(self.status, ExecutionStatus::Success(..)) + } +} + +/// Result of an execution on the STF +/// +/// Contains multiple executed operations +#[derive(Clone, Debug)] +pub struct BatchExecutionResult +where + TCS: PartialEq + Encode + Decode + Debug + Send + Sync, + G: PartialEq + Encode + Decode + Debug + Send + Sync, +{ + pub state_hash_before_execution: H256, + pub executed_operations: Vec>, + pub state_after_execution: Externalities, +} + +impl BatchExecutionResult +where + Externalities: SgxExternalitiesTrait + Encode, + TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync, + G: PartialEq + Encode + Decode + Debug + Clone + Send + Sync, +{ + pub fn get_extrinsic_callbacks(&self) -> Vec { + self.executed_operations + .iter() + .flat_map(|e| e.status.get_extrinsic_callbacks()) + .collect() + } + + /// Returns all successfully exectued operation hashes. + pub fn get_executed_operation_hashes(&self) -> Vec { + self.executed_operations + .iter() + .flat_map(|ec| ec.status.get_executed_operation_hash()) + .collect() + } + + /// Returns all operations that were not executed. + pub fn get_failed_operations(&self) -> Vec> { + self.executed_operations.iter().filter(|ec| !ec.is_success()).cloned().collect() + } + + // Litentry: returns all (top_hash, (rpc_response_value, force_wait) tuples + pub fn get_connection_updates(&self) -> Vec<(H256, (RpcResponseValue, bool))> { + self.executed_operations + .iter() + .map(|ec| { + ( + ec.status.get_operation_hash(), + (ec.status.get_rpc_response_value(), ec.status.get_force_wait()), + ) + }) + .collect() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use itp_sgx_externalities::SgxExternalities; + use itp_test::mock::stf_mock::{GetterMock, TrustedCallSignedMock}; + use itp_types::OpaqueCall; + + #[test] + fn is_success_works() { + let (success, _) = create_success_operation_from_u8(1); + let failed = create_failed_operation_from_u8(7); + + assert!(success.is_success()); + assert!(!failed.is_success()); + } + + #[test] + fn get_executed_operation_hashes_works() { + let (success_one, hash_success_one) = create_success_operation_from_u8(1); + let (success_two, hash_success_two) = create_success_operation_from_u8(3); + let failed = create_failed_operation_from_u8(7); + let result = batch_execution_result(vec![success_one, failed, success_two]); + + let success_operations = result.get_executed_operation_hashes(); + + assert_eq!(success_operations.len(), 2); + assert!(success_operations.contains(&hash_success_one)); + assert!(success_operations.contains(&hash_success_two)); + } + + #[test] + fn get_failed_operations_works() { + let failed_one = create_failed_operation_from_u8(1); + let failed_two = create_failed_operation_from_u8(3); + let (success, _) = create_success_operation_from_u8(10); + let result = batch_execution_result(vec![failed_one.clone(), failed_two.clone(), success]); + + let failed_operations = result.get_failed_operations(); + + assert_eq!(failed_operations.len(), 2); + assert!(failed_operations.contains(&failed_one)); + assert!(failed_operations.contains(&failed_two)); + } + + fn batch_execution_result( + executed_calls: Vec>, + ) -> BatchExecutionResult { + BatchExecutionResult { + executed_operations: executed_calls, + state_hash_before_execution: H256::default(), + state_after_execution: SgxExternalities::default(), + } + } + + fn create_failed_operation_from_u8( + int: u8, + ) -> ExecutedOperation { + ExecutedOperation::failed( + H256::from([int; 32]), + TrustedOperationOrHash::Hash(H256::from([int; 32])), + vec![], + vec![], + ) + } + + fn create_success_operation_from_u8( + int: u8, + ) -> (ExecutedOperation, H256) { + let hash = H256::from([int; 32]); + let opaque_call: Vec = + vec![ParentchainCall::Litentry(OpaqueCall(vec![int; 10]))]; + let operation = ExecutedOperation::success( + hash, + TrustedOperationOrHash::Hash(hash), + opaque_call, + vec![], + false, + ); + (operation, hash) + } +} diff --git a/bitacross-worker/core-primitives/stf-executor/src/mocks.rs b/bitacross-worker/core-primitives/stf-executor/src/mocks.rs new file mode 100644 index 0000000000..fb4079a331 --- /dev/null +++ b/bitacross-worker/core-primitives/stf-executor/src/mocks.rs @@ -0,0 +1,170 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + error::Result, + state_getter::GetState, + traits::{StateUpdateProposer, StfEnclaveSigning}, + BatchExecutionResult, ExecutedOperation, +}; +use codec::{Decode, Encode}; +use core::fmt::Debug; +use itp_sgx_externalities::SgxExternalitiesTrait; +use itp_stf_primitives::{ + traits::TrustedCallSigning, + types::{AccountId, KeyPair, ShardIdentifier, TrustedOperationOrHash}, +}; +use itp_types::H256; +use sp_core::Pair; +use sp_runtime::traits::Header as HeaderTrait; +#[cfg(feature = "std")] +use std::sync::RwLock; +use std::{boxed::Box, marker::PhantomData, ops::Deref, time::Duration, vec::Vec}; + +use crate::traits::StfShardVaultQuery; +use itp_stf_primitives::{ + traits::{GetterAuthorization, TrustedCallVerification}, + types::TrustedOperation, +}; +#[cfg(feature = "sgx")] +use std::sync::SgxRwLock as RwLock; + +/// Mock for the StfExecutor. +#[derive(Default)] +pub struct StfExecutorMock { + pub state: RwLock, +} + +impl StfExecutorMock { + pub fn new(state: State) -> Self { + Self { state: RwLock::new(state) } + } + + pub fn get_state(&self) -> State { + (*self.state.read().unwrap().deref()).clone() + } +} + +impl StateUpdateProposer for StfExecutorMock +where + State: SgxExternalitiesTrait + Encode + Clone, + TCS: PartialEq + Encode + Decode + Clone + Debug + Send + Sync + TrustedCallVerification, + G: PartialEq + Encode + Decode + Clone + Debug + Send + Sync, +{ + type Externalities = State; + + fn propose_state_update( + &self, + trusted_calls: &[TrustedOperation], + _header: &PH, + _shard: &ShardIdentifier, + _max_exec_duration: Duration, + prepare_state_function: F, + ) -> Result> + where + PH: HeaderTrait, + F: FnOnce(Self::Externalities) -> Self::Externalities, + { + let mut lock = self.state.write().unwrap(); + + let updated_state = prepare_state_function((*lock.deref()).clone()); + + *lock = updated_state.clone(); + + let executed_operations: Vec> = trusted_calls + .iter() + .map(|c| { + let operation_hash = c.hash(); + let top_or_hash = TrustedOperationOrHash::::from_top(c.clone()); + ExecutedOperation::success( + operation_hash, + top_or_hash, + Vec::new(), + Vec::new(), + false, + ) + }) + .collect(); + + Ok(BatchExecutionResult { + executed_operations, + state_hash_before_execution: H256::default(), + state_after_execution: updated_state, + }) + } +} + +/// Enclave signer mock. +pub struct StfEnclaveSignerMock { + mr_enclave: [u8; 32], + signer: sp_core::ed25519::Pair, +} + +impl StfEnclaveSignerMock { + pub fn new(mr_enclave: [u8; 32]) -> Self { + type Seed = [u8; 32]; + const TEST_SEED: Seed = *b"42345678901234567890123456789012"; + + Self { mr_enclave, signer: sp_core::ed25519::Pair::from_seed(&TEST_SEED) } + } +} + +impl Default for StfEnclaveSignerMock { + fn default() -> Self { + Self::new([0u8; 32]) + } +} + +impl StfEnclaveSigning for StfEnclaveSignerMock { + fn get_enclave_account(&self) -> Result { + Ok(self.signer.public().into()) + } + + fn sign_call_with_self>( + &self, + trusted_call: &TC, + shard: &ShardIdentifier, + ) -> Result { + Ok(trusted_call.sign(&KeyPair::Ed25519(Box::new(self.signer)), 1, &self.mr_enclave, shard)) + } + + fn sign(&self, _payload: &[u8]) -> Result<(AccountId, Vec)> { + Ok((self.signer.public().into(), [0u8; 32].to_vec())) + } +} + +impl StfShardVaultQuery for StfEnclaveSignerMock { + fn get_shard_vault(&self, _shard: &ShardIdentifier) -> Result { + Err(crate::error::Error::Other("shard vault undefined".into())) + } +} + +/// GetState mock +#[derive(Default)] +pub struct GetStateMock { + _phantom: PhantomData, +} + +impl GetState for GetStateMock +where + StateType: Encode, + G: PartialEq + Decode + GetterAuthorization, +{ + fn get_state(_getter: G, state: &mut StateType) -> Result>> { + Ok(Some(state.encode())) + } +} diff --git a/bitacross-worker/core-primitives/stf-executor/src/state_getter.rs b/bitacross-worker/core-primitives/stf-executor/src/state_getter.rs new file mode 100644 index 0000000000..ca047a36eb --- /dev/null +++ b/bitacross-worker/core-primitives/stf-executor/src/state_getter.rs @@ -0,0 +1,85 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::error::{Error, Result}; +use codec::Decode; +use core::marker::PhantomData; +use itp_sgx_externalities::SgxExternalities; +use itp_stf_interface::StateGetterInterface; +use itp_stf_primitives::traits::GetterAuthorization; +use log::*; +use std::vec::Vec; + +/// Abstraction for accessing state with a getter. +pub trait GetState { + /// Executes a trusted getter on a state and return its value, if available. + /// + /// Also verifies the signature of the trusted getter and returns an error + /// if it's invalid. + fn get_state(getter: G, state: &mut StateType) -> Result>>; +} + +pub struct StfStateGetter { + _phantom: PhantomData, +} + +impl GetState for StfStateGetter +where + Stf: StateGetterInterface, + G: PartialEq + Decode + GetterAuthorization, +{ + fn get_state(getter: G, state: &mut SgxExternalities) -> Result>> { + if !getter.is_authorized() { + error!("getter authorization failed"); + return Err(Error::GetterIsNotAuthorized) + } + debug!("getter authorized. calling into STF to get state"); + Ok(Stf::execute_getter(state, getter)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use core::assert_matches::assert_matches; + + use itp_test::mock::stf_mock::{ + GetterMock, StfMock, TrustedGetterMock, TrustedGetterSignedMock, + }; + + type TestStateGetter = StfStateGetter; + + #[test] + fn upon_false_signature_get_stf_state_errs() { + let getter = + TrustedGetterSignedMock { getter: TrustedGetterMock::some_value, signature: false }; + let mut state = SgxExternalities::default(); + + assert_matches!( + TestStateGetter::get_state(GetterMock::trusted(getter), &mut state), + Err(Error::GetterIsNotAuthorized) + ); + } + + #[test] + fn state_getter_is_executed_if_signature_is_correct() { + let getter = + TrustedGetterSignedMock { getter: TrustedGetterMock::some_value, signature: true }; + let mut state = SgxExternalities::default(); + assert!(TestStateGetter::get_state(GetterMock::trusted(getter), &mut state).is_ok()); + } +} diff --git a/bitacross-worker/core-primitives/stf-executor/src/traits.rs b/bitacross-worker/core-primitives/stf-executor/src/traits.rs new file mode 100644 index 0000000000..4f7efd1532 --- /dev/null +++ b/bitacross-worker/core-primitives/stf-executor/src/traits.rs @@ -0,0 +1,89 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{error::Result, BatchExecutionResult}; +use codec::{Decode, Encode}; +use core::fmt::Debug; +use itp_sgx_externalities::SgxExternalitiesTrait; +use itp_stf_primitives::{ + traits::TrustedCallSigning, + types::{AccountId, ShardIdentifier, TrustedOperation}, +}; +use itp_types::H256; +use sp_runtime::traits::Header as HeaderTrait; +use std::{time::Duration, vec::Vec}; + +/// Post-processing steps after executing STF +pub enum StatePostProcessing { + None, + Prune, +} + +/// Allows signing of a trusted call or a raw bytes with the enclave account that is registered in the STF. +/// +/// The signing key is derived from the shielding key, which guarantees that all enclaves sign the same key. +pub trait StfEnclaveSigning +where + TCS: PartialEq + Encode + Debug, +{ + fn get_enclave_account(&self) -> Result; + + fn sign_call_with_self>( + &self, + trusted_call: &TC, + shard: &ShardIdentifier, + ) -> Result; + + // litentry + fn sign(&self, payload: &[u8]) -> Result<(AccountId, Vec)>; +} + +pub trait StfShardVaultQuery { + fn get_shard_vault(&self, shard: &ShardIdentifier) -> Result; +} + +/// Proposes a state update to `Externalities`. +pub trait StateUpdateProposer +where + TCS: PartialEq + Encode + Decode + Debug + Send + Sync, + G: PartialEq + Encode + Decode + Debug + Send + Sync, +{ + type Externalities: SgxExternalitiesTrait + Encode; + + /// Executes trusted calls within a given time frame without permanent state mutation. + /// + /// All executed call hashes and the mutated state are returned. + /// If the time expires, any remaining trusted calls within the batch will be ignored. + fn propose_state_update( + &self, + trusted_calls: &[TrustedOperation], + header: &PH, + shard: &ShardIdentifier, + max_exec_duration: Duration, + prepare_state_function: F, + ) -> Result> + where + PH: HeaderTrait, + F: FnOnce(Self::Externalities) -> Self::Externalities; +} + +/// Updates the STF state for a specific header. +/// +/// Cannot be implemented for a generic header currently, because the runtime expects a ParentchainHeader. +pub trait StfUpdateState { + fn update_states(&self, header: &PCH, parentchain_id: &PCID) -> Result<()>; +} diff --git a/bitacross-worker/core-primitives/stf-interface/Cargo.toml b/bitacross-worker/core-primitives/stf-interface/Cargo.toml new file mode 100644 index 0000000000..1fc86aaed9 --- /dev/null +++ b/bitacross-worker/core-primitives/stf-interface/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "itp-stf-interface" +version = "0.8.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +codec = { version = "3.0.0", default-features = false, features = ["derive"], package = "parity-scale-codec" } + +itp-node-api-metadata = { path = "../node-api/metadata", default-features = false, features = ["mocks"] } +itp-node-api-metadata-provider = { path = "../node-api/metadata-provider", default-features = false } +itp-stf-primitives = { path = "../stf-primitives", default-features = false } +itp-types = { default-features = false, path = "../types" } + +[features] +default = ["std"] +std = [ + "itp-node-api-metadata/std", + "itp-node-api-metadata-provider/std", + "itp-stf-primitives/std", + "itp-types/std", +] +mocks = [] diff --git a/bitacross-worker/core-primitives/stf-interface/src/lib.rs b/bitacross-worker/core-primitives/stf-interface/src/lib.rs new file mode 100644 index 0000000000..a265ecd38c --- /dev/null +++ b/bitacross-worker/core-primitives/stf-interface/src/lib.rs @@ -0,0 +1,141 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Provides a state interface. +//! This allow to easily mock the stf and exchange it with another storage. + +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; + +use alloc::{sync::Arc, vec::Vec}; +use codec::{Decode, Encode}; +use core::fmt::Debug; +use itp_node_api_metadata::NodeMetadataTrait; +use itp_node_api_metadata_provider::AccessNodeMetadata; +use itp_stf_primitives::traits::TrustedCallVerification; +use itp_types::{ + parentchain::{AccountId, ParentchainCall, ParentchainId}, + ShardIdentifier, H256, +}; + +#[cfg(feature = "mocks")] +pub mod mocks; +pub mod parentchain_pallet; +pub mod runtime_upgrade; +pub mod sudo_pallet; +pub mod system_pallet; + +pub const SHARD_VAULT_KEY: &str = "ShardVaultPubKey"; + +/// Interface to initialize a new state. +pub trait InitState { + /// Initialize a new state for a given enclave account. + fn init_state(enclave_account: AccountId) -> State; +} + +/// Interface to query shard vault account for shard +pub trait ShardVaultQuery { + fn get_vault(state: &mut S) -> Option; +} + +/// Interface for all functions calls necessary to update an already +/// initialized state. +pub trait UpdateState { + /// Updates a given state for + fn apply_state_diff(state: &mut State, state_diff: StateDiff); + fn storage_hashes_to_update_on_block(parentchain_id: &ParentchainId) -> Vec>; +} + +/// Interface to execute state mutating calls on a state. +pub trait StateCallInterface +where + NodeMetadataRepository: AccessNodeMetadata, + NodeMetadataRepository::MetadataType: NodeMetadataTrait, + TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, +{ + type Error: Encode; + type Result: StfExecutionResult; + + /// Execute a call on a specific state. Callbacks are added as an `OpaqueCall`. + /// + /// Litentry: + /// 1. add a parameter to pass the top_hash around + /// 2. returns the encoded rpc response value field that should be passed + /// back to the requester when the call is triggered synchronously + fn execute_call( + state: &mut State, + shard: &ShardIdentifier, + call: TCS, + top_hash: H256, + calls: &mut Vec, + node_metadata_repo: Arc, + ) -> Result; +} + +/// Interface to execute state reading getters on a state. +pub trait StateGetterInterface { + /// Execute a getter on a specific state. + fn execute_getter(state: &mut S, getter: G) -> Option>; +} + +/// Trait used to abstract the call execution. +pub trait ExecuteCall +where + NodeMetadataRepository: AccessNodeMetadata, + NodeMetadataRepository::MetadataType: NodeMetadataTrait, +{ + type Error: Encode; + type Result: StfExecutionResult; + + /// Execute a call. Callbacks are added as an `OpaqueCall`. + /// + /// Litentry: returns the encoded rpc response that should be passed back to + /// the requester when the call is triggered synchronously + fn execute( + self, + shard: &ShardIdentifier, + top_hash: H256, + calls: &mut Vec, + node_metadata_repo: Arc, + ) -> Result; + + /// Get storages hashes that should be updated for a specific call. + fn get_storage_hashes_to_update(self) -> Vec>; +} + +/// Trait used to abstract the getter execution. +pub trait ExecuteGetter { + /// Execute a getter. + fn execute(self) -> Option>; + /// Get storages hashes that should be updated for a specific getter. + fn get_storage_hashes_to_update(self) -> Vec>; +} + +pub trait StfExecutionResult { + fn get_encoded_result(self) -> Vec; + fn force_connection_wait(&self) -> bool; +} + +impl StfExecutionResult for () { + fn get_encoded_result(self) -> Vec { + Vec::default() + } + fn force_connection_wait(&self) -> bool { + false + } +} diff --git a/bitacross-worker/core-primitives/stf-interface/src/mocks.rs b/bitacross-worker/core-primitives/stf-interface/src/mocks.rs new file mode 100644 index 0000000000..44bda77d36 --- /dev/null +++ b/bitacross-worker/core-primitives/stf-interface/src/mocks.rs @@ -0,0 +1,132 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Provides a mock which implements all traits within this crate. + +extern crate alloc; +use crate::{ + system_pallet::SystemPalletAccountInterface, ExecuteCall, ExecuteGetter, InitState, + StateCallInterface, StateGetterInterface, UpdateState, +}; +use alloc::{string::String, sync::Arc, vec::Vec}; +use codec::{Decode, Encode}; +use core::{fmt::Debug, marker::PhantomData}; +use itp_node_api_metadata::metadata_mocks::NodeMetadataMock; +use itp_node_api_metadata_provider::NodeMetadataRepository; +use itp_stf_primitives::traits::TrustedCallVerification; +use itp_types::{ + parentchain::{ParentchainCall, ParentchainId}, + AccountId, Index, ShardIdentifier, H256, +}; + +#[derive(Default)] +pub struct StateInterfaceMock { + _phantom: PhantomData<(State, StateDiff)>, +} + +impl InitState + for StateInterfaceMock +{ + fn init_state(_enclave_account: AccountId) -> State { + unimplemented!() + } +} + +impl UpdateState for StateInterfaceMock { + fn apply_state_diff(_state: &mut State, _state_diff: StateDiff) { + unimplemented!() + } + + fn storage_hashes_to_update_on_block(_: &ParentchainId) -> Vec> { + unimplemented!() + } +} + +impl StateCallInterface> + for StateInterfaceMock +where + TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, +{ + type Error = String; + type Result = (); + + fn execute_call( + _state: &mut State, + _shard: &ShardIdentifier, + _call: TCS, + _top_hash: H256, + _calls: &mut Vec, + _node_metadata_repo: Arc>, + ) -> Result { + unimplemented!() + } +} + +impl StateGetterInterface + for StateInterfaceMock +{ + fn execute_getter(_state: &mut State, _getter: Getter) -> Option> { + None + } +} + +impl SystemPalletAccountInterface + for StateInterfaceMock +{ + type AccountData = String; + type Index = Index; + + fn get_account_nonce(_state: &mut State, _account_id: &AccountId) -> Self::Index { + unimplemented!() + } + fn get_account_data(_state: &mut State, _account_id: &AccountId) -> Self::AccountData { + unimplemented!() + } +} + +pub struct CallExecutorMock; + +impl ExecuteCall> for CallExecutorMock { + type Error = String; + type Result = (); + + fn execute( + self, + _shard: &ShardIdentifier, + _top_hash: H256, + _calls: &mut Vec, + _node_metadata_repo: Arc>, + ) -> Result<(), Self::Error> { + unimplemented!() + } + + fn get_storage_hashes_to_update(self) -> Vec> { + unimplemented!() + } +} + +pub struct GetterExecutorMock; + +impl ExecuteGetter for GetterExecutorMock { + fn execute(self) -> Option> { + unimplemented!() + } + + fn get_storage_hashes_to_update(self) -> Vec> { + unimplemented!() + } +} diff --git a/bitacross-worker/core-primitives/stf-interface/src/parentchain_pallet.rs b/bitacross-worker/core-primitives/stf-interface/src/parentchain_pallet.rs new file mode 100644 index 0000000000..c89138c25e --- /dev/null +++ b/bitacross-worker/core-primitives/stf-interface/src/parentchain_pallet.rs @@ -0,0 +1,27 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +/// Interface trait of the parentchain pallet. +pub trait ParentchainPalletInterface { + type Error; + + /// Updates the block number, block hash and parent hash of the parentchain block. + fn update_parentchain_block( + state: &mut State, + header: ParentchainHeader, + ) -> Result<(), Self::Error>; +} diff --git a/bitacross-worker/core-primitives/stf-interface/src/runtime_upgrade.rs b/bitacross-worker/core-primitives/stf-interface/src/runtime_upgrade.rs new file mode 100644 index 0000000000..30ee22140e --- /dev/null +++ b/bitacross-worker/core-primitives/stf-interface/src/runtime_upgrade.rs @@ -0,0 +1,21 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +pub trait RuntimeUpgradeInterface { + type Error; + + fn on_runtime_upgrade(state: &mut State) -> Result<(), Self::Error>; +} diff --git a/bitacross-worker/core-primitives/stf-interface/src/sudo_pallet.rs b/bitacross-worker/core-primitives/stf-interface/src/sudo_pallet.rs new file mode 100644 index 0000000000..afd2ed1dec --- /dev/null +++ b/bitacross-worker/core-primitives/stf-interface/src/sudo_pallet.rs @@ -0,0 +1,27 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +/// Interface trait of the sudo pallet. +pub trait SudoPalletInterface { + type AccountId; + + /// Get the root account for a given state. + fn get_root(state: &mut State) -> Self::AccountId; + + /// Get the enclave account for a given state. + fn get_enclave_account(state: &mut State) -> Self::AccountId; +} diff --git a/bitacross-worker/core-primitives/stf-interface/src/system_pallet.rs b/bitacross-worker/core-primitives/stf-interface/src/system_pallet.rs new file mode 100644 index 0000000000..82166e846e --- /dev/null +++ b/bitacross-worker/core-primitives/stf-interface/src/system_pallet.rs @@ -0,0 +1,53 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +extern crate alloc; +use alloc::{boxed::Box, vec::Vec}; + +/// Interface trait of the system pallet for account specific data. +pub trait SystemPalletAccountInterface { + type Index; + type AccountData; + + /// Get the nonce for a given account and state. + fn get_account_nonce(state: &mut State, account_id: &AccountId) -> Self::Index; + + /// Get the account date for a given account and state. + fn get_account_data(state: &mut State, account: &AccountId) -> Self::AccountData; +} + +/// Interface trait of the system pallet for event specific interactions. +pub trait SystemPalletEventInterface { + type EventRecord; + type EventIndex; + type BlockNumber; + type Hash; + + /// Get a Vec of bounded events. + fn get_events(state: &mut State) -> Vec>; + + /// Get the count of the currently stored events. + fn get_event_count(state: &mut State) -> Self::EventIndex; + + /// Get the event topics + fn get_event_topics( + state: &mut State, + topic: &Self::Hash, + ) -> Vec<(Self::BlockNumber, Self::EventIndex)>; + + /// Reset everything event related. + fn reset_events(state: &mut State); +} diff --git a/bitacross-worker/core-primitives/stf-primitives/Cargo.toml b/bitacross-worker/core-primitives/stf-primitives/Cargo.toml new file mode 100644 index 0000000000..edbbf47968 --- /dev/null +++ b/bitacross-worker/core-primitives/stf-primitives/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "itp-stf-primitives" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +# crates.io +codec = { version = "3.0.0", default-features = false, features = ["derive"], package = "parity-scale-codec" } +derive_more = { version = "0.99.5" } +itp-sgx-runtime-primitives = { path = "../../core-primitives/sgx-runtime-primitives", default-features = false } +sp-core = { default-features = false, features = ["full_crypto"], git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-std = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# litentry +litentry-primitives = { path = "../../litentry/primitives", default-features = false } + +[features] +default = ["std"] +std = [ + # crates.io + "codec/std", + # substrate + "sp-core/std", + "sp-std/std", + "sp-runtime/std", + "itp-sgx-runtime-primitives/std", + # litentry + "litentry-primitives/std", +] diff --git a/bitacross-worker/core-primitives/stf-primitives/src/error.rs b/bitacross-worker/core-primitives/stf-primitives/src/error.rs new file mode 100644 index 0000000000..c69514f109 --- /dev/null +++ b/bitacross-worker/core-primitives/stf-primitives/src/error.rs @@ -0,0 +1,118 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +use crate::types::{AccountId, Nonce}; +use alloc::{format, string::String}; +use codec::{Decode, Encode}; +use derive_more::Display; +use litentry_primitives::{Assertion, ErrorDetail, ErrorString, IMPError, VCMPError}; + +pub type StfResult = Result; + +#[derive(Debug, Display, PartialEq, Eq, Encode, Decode, Clone)] +pub enum StfError { + #[codec(index = 0)] + #[display(fmt = "Insufficient privileges {:?}, are you sure you are root?", _0)] + MissingPrivileges(AccountId), + #[codec(index = 1)] + #[display(fmt = "Valid enclave signer account is required")] + RequireEnclaveSignerAccount, + #[codec(index = 2)] + #[display(fmt = "Error dispatching runtime call. {:?}", _0)] + Dispatch(String), + #[codec(index = 3)] + #[display(fmt = "Not enough funds to perform operation")] + MissingFunds, + #[codec(index = 4)] + #[display(fmt = "Invalid Nonce {:?} != {:?}", _0, _1)] + InvalidNonce(Nonce, Nonce), + #[codec(index = 5)] + StorageHashMismatch, + #[codec(index = 6)] + InvalidStorageDiff, + #[codec(index = 7)] + InvalidMetadata, + // litentry + #[codec(index = 8)] + #[display(fmt = "LinkIdentityFailed: {:?}", _0)] + LinkIdentityFailed(ErrorDetail), + #[codec(index = 9)] + #[display(fmt = "DeactivateIdentityFailed: {:?}", _0)] + DeactivateIdentityFailed(ErrorDetail), + #[codec(index = 10)] + #[display(fmt = "ActivateIdentityFailed: {:?}", _0)] + ActivateIdentityFailed(ErrorDetail), + #[codec(index = 11)] + #[display(fmt = "RequestVCFailed: {:?} {:?}", _0, _1)] + RequestVCFailed(Assertion, ErrorDetail), + #[codec(index = 12)] + SetScheduledMrEnclaveFailed, + #[codec(index = 13)] + #[display(fmt = "SetIdentityNetworksFailed: {:?}", _0)] + SetIdentityNetworksFailed(ErrorDetail), + #[codec(index = 14)] + InvalidAccount, + #[codec(index = 15)] + UnclassifiedError, + #[codec(index = 16)] + #[display(fmt = "RemovingIdentityFailed: {:?}", _0)] + RemoveIdentityFailed(ErrorDetail), + #[codec(index = 17)] + EmptyIDGraph, +} + +impl From for StfError { + fn from(e: IMPError) -> Self { + match e { + IMPError::LinkIdentityFailed(d) => StfError::LinkIdentityFailed(d), + IMPError::DeactivateIdentityFailed(d) => StfError::DeactivateIdentityFailed(d), + IMPError::ActivateIdentityFailed(d) => StfError::ActivateIdentityFailed(d), + _ => StfError::UnclassifiedError, + } + } +} + +impl From for StfError { + fn from(e: VCMPError) -> Self { + match e { + VCMPError::RequestVCFailed(a, d) => StfError::RequestVCFailed(a, d), + _ => StfError::UnclassifiedError, + } + } +} + +impl StfError { + // Convert StfError to IMPError that would be sent to parentchain + pub fn to_imp_error(&self) -> IMPError { + match self { + StfError::LinkIdentityFailed(d) => IMPError::LinkIdentityFailed(d.clone()), + StfError::DeactivateIdentityFailed(d) => IMPError::DeactivateIdentityFailed(d.clone()), + StfError::ActivateIdentityFailed(d) => IMPError::ActivateIdentityFailed(d.clone()), + _ => IMPError::UnclassifiedError(ErrorDetail::StfError(ErrorString::truncate_from( + format!("{:?}", self).as_bytes().to_vec(), + ))), + } + } + // Convert StfError to VCMPError that would be sent to parentchain + pub fn to_vcmp_error(&self) -> VCMPError { + match self { + StfError::RequestVCFailed(a, d) => VCMPError::RequestVCFailed(a.clone(), d.clone()), + _ => VCMPError::UnclassifiedError(ErrorDetail::StfError(ErrorString::truncate_from( + format!("{:?}", self).as_bytes().to_vec(), + ))), + } + } +} diff --git a/bitacross-worker/core-primitives/stf-primitives/src/lib.rs b/bitacross-worker/core-primitives/stf-primitives/src/lib.rs new file mode 100644 index 0000000000..8e5ce6b1c0 --- /dev/null +++ b/bitacross-worker/core-primitives/stf-primitives/src/lib.rs @@ -0,0 +1,22 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +#![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + +pub mod error; +pub mod traits; +pub mod types; diff --git a/bitacross-worker/core-primitives/stf-primitives/src/traits.rs b/bitacross-worker/core-primitives/stf-primitives/src/traits.rs new file mode 100644 index 0000000000..eaad1e3563 --- /dev/null +++ b/bitacross-worker/core-primitives/stf-primitives/src/traits.rs @@ -0,0 +1,76 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +use crate::types::{AccountId, KeyPair, ShardIdentifier}; +use alloc::vec::Vec; +use codec::{Decode, Encode}; +use core::fmt::Debug; +use itp_sgx_runtime_primitives::types::Index; +use litentry_primitives::Identity; +use sp_runtime::transaction_validity::{TransactionValidityError, ValidTransaction}; + +/// checks authorization of stf getters +pub trait GetterAuthorization { + fn is_authorized(&self) -> bool; +} + +/// knows how to sign a trusted call input and provides a signed output +pub trait TrustedCallSigning { + fn sign( + &self, + pair: &KeyPair, + nonce: Index, + mrenclave: &[u8; 32], + shard: &ShardIdentifier, + ) -> TCS; +} + +/// enables TrustedCallSigned verification +pub trait TrustedCallVerification { + fn sender_identity(&self) -> &Identity; + + fn nonce(&self) -> Index; + + fn verify_signature(&self, mrenclave: &[u8; 32], shard: &ShardIdentifier) -> bool; +} + +/// validation for top pool +pub trait PoolTransactionValidation { + fn validate(&self) -> Result; +} + +/// Trait to be implemented on the executor to serve helper methods of the executor +/// to the `IndirectDispatch` implementation. +pub trait IndirectExecutor +where + TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, +{ + fn submit_trusted_call(&self, shard: ShardIdentifier, encrypted_trusted_call: Vec); + + fn decrypt(&self, encrypted: &[u8]) -> Result, Error>; + + fn encrypt(&self, value: &[u8]) -> Result, Error>; + + fn get_enclave_account(&self) -> Result; + + fn get_default_shard(&self) -> ShardIdentifier; + + fn sign_call_with_self>( + &self, + trusted_call: &TC, + shard: &ShardIdentifier, + ) -> Result; +} diff --git a/bitacross-worker/core-primitives/stf-primitives/src/types.rs b/bitacross-worker/core-primitives/stf-primitives/src/types.rs new file mode 100644 index 0000000000..a96da4087c --- /dev/null +++ b/bitacross-worker/core-primitives/stf-primitives/src/types.rs @@ -0,0 +1,211 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +extern crate alloc; +use crate::traits::{PoolTransactionValidation, TrustedCallVerification}; +use alloc::boxed::Box; +use codec::{Compact, Decode, Encode}; +use core::fmt::Debug; +use litentry_primitives::LitentryMultiSignature; +use sp_core::{blake2_256, crypto::AccountId32, ed25519, sr25519, Pair, H256}; +use sp_runtime::{ + traits::Verify, + transaction_validity::{TransactionValidityError, ValidTransaction}, + MultiSignature, +}; +use sp_std::{vec, vec::Vec}; + +pub type Signature = MultiSignature; +pub type AuthorityId = ::Signer; +pub type AccountId = AccountId32; +pub type Nonce = u32; +pub type Hash = H256; +pub type BalanceTransferFn = ([u8; 2], AccountId, Compact); +pub type ShardIdentifier = H256; + +#[derive(Clone)] +pub enum KeyPair { + Sr25519(Box), + Ed25519(Box), +} + +impl KeyPair { + pub fn sign(&self, payload: &[u8]) -> LitentryMultiSignature { + match self { + Self::Sr25519(pair) => pair.sign(payload).into(), + Self::Ed25519(pair) => pair.sign(payload).into(), + } + } + pub fn account_id(&self) -> AccountId { + match self { + Self::Sr25519(pair) => pair.public().into(), + Self::Ed25519(pair) => pair.public().into(), + } + } +} + +impl From for KeyPair { + fn from(x: ed25519::Pair) -> Self { + KeyPair::Ed25519(Box::new(x)) + } +} + +impl From for KeyPair { + fn from(x: sr25519::Pair) -> Self { + KeyPair::Sr25519(Box::new(x)) + } +} + +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] +#[allow(non_camel_case_types)] +pub enum TrustedOperation +where + TCS: PartialEq + Encode + Debug, + G: PartialEq + Encode + Debug, +{ + #[codec(index = 0)] + indirect_call(TCS), + #[codec(index = 1)] + direct_call(TCS), + #[codec(index = 2)] + get(G), +} + +impl From for TrustedOperation +where + TCS: PartialEq + Encode + Debug, + G: PartialEq + Encode + Debug, +{ + fn from(item: G) -> Self { + TrustedOperation::get(item) + } +} + +impl TrustedOperation +where + TCS: PartialEq + TrustedCallVerification + Encode + Debug, + G: PartialEq + Encode + Debug, +{ + pub fn to_call(&self) -> Option<&TCS> { + match self { + TrustedOperation::direct_call(c) => Some(c), + TrustedOperation::indirect_call(c) => Some(c), + _ => None, + } + } + + pub fn signed_caller_account(&self) -> Option { + match self { + TrustedOperation::direct_call(c) => c.sender_identity().to_account_id(), + TrustedOperation::indirect_call(c) => c.sender_identity().to_account_id(), + _ => None, + } + } + + fn validate_trusted_call(trusted_call_signed: &TCS) -> ValidTransaction { + let from = trusted_call_signed.sender_identity(); + let requires = vec![]; + let provides = vec![(from, trusted_call_signed.nonce()).encode()]; + + ValidTransaction { priority: 1 << 20, requires, provides, longevity: 64, propagate: true } + } + + pub fn hash(&self) -> H256 { + blake2_256(&self.encode()).into() + } +} + +impl PoolTransactionValidation for TrustedOperation +where + TCS: PartialEq + TrustedCallVerification + Encode + Debug, + G: PartialEq + Encode + PoolTransactionValidation + Debug, +{ + fn validate(&self) -> Result { + match self { + TrustedOperation::direct_call(trusted_call_signed) => + Ok(Self::validate_trusted_call(trusted_call_signed)), + TrustedOperation::indirect_call(trusted_call_signed) => + Ok(Self::validate_trusted_call(trusted_call_signed)), + TrustedOperation::get(getter) => getter.validate(), + } + } +} + +/// Trusted operation Or hash +/// +/// Allows to refer to trusted calls either by its raw representation or its hash. +#[derive(Clone, Debug, Encode, Decode, PartialEq)] +pub enum TrustedOperationOrHash +where + TCS: PartialEq + Encode + Debug + Send + Sync, + G: PartialEq + Encode + Debug + Send + Sync, +{ + /// The hash of the call. + #[codec(index = 0)] + Hash(H256), + /// Raw extrinsic bytes. + #[codec(index = 1)] + OperationEncoded(Vec), + /// Raw extrinsic + #[codec(index = 2)] + Operation(Box>), +} + +impl TrustedOperationOrHash +where + TCS: PartialEq + Encode + Debug + Send + Sync, + G: PartialEq + Encode + Debug + Send + Sync, +{ + pub fn from_top(top: TrustedOperation) -> Self { + TrustedOperationOrHash::Operation(Box::new(top)) + } +} + +/// Payload to be sent to peers for a state update. +#[derive(PartialEq, Eq, Clone, Debug, Encode, Decode)] +pub struct StatePayload { + /// State hash before the `state_update` was applied. + state_hash_apriori: H256, + /// State hash after the `state_update` was applied. + state_hash_aposteriori: H256, + /// State diff applied to state with hash `state_hash_apriori` + /// leading to state with hash `state_hash_aposteriori`. + state_update: StateUpdate, +} + +impl StatePayload { + /// Get state hash before the `state_update` was applied. + pub fn state_hash_apriori(&self) -> H256 { + self.state_hash_apriori + } + /// Get state hash after the `state_update` was applied. + pub fn state_hash_aposteriori(&self) -> H256 { + self.state_hash_aposteriori + } + /// Reference to the `state_update`. + pub fn state_update(&self) -> &StateUpdate { + &self.state_update + } + + /// Create new `StatePayload` instance. + pub fn new(apriori: H256, aposteriori: H256, update: StateUpdate) -> Self { + Self { + state_hash_apriori: apriori, + state_hash_aposteriori: aposteriori, + state_update: update, + } + } +} diff --git a/bitacross-worker/core-primitives/stf-state-handler/Cargo.toml b/bitacross-worker/core-primitives/stf-state-handler/Cargo.toml new file mode 100644 index 0000000000..b06d923053 --- /dev/null +++ b/bitacross-worker/core-primitives/stf-state-handler/Cargo.toml @@ -0,0 +1,72 @@ +[package] +name = "itp-stf-state-handler" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +# sgx dependencies +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } +sgx_types = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } + +# local dependencies +itp-hashing = { path = "../../core-primitives/hashing", default-features = false } +itp-settings = { path = "../../core-primitives/settings" } +itp-sgx-crypto = { path = "../../core-primitives/sgx/crypto", default-features = false } +itp-sgx-externalities = { default-features = false, path = "../../core-primitives/substrate-sgx/externalities" } +itp-sgx-io = { path = "../../core-primitives/sgx/io", default-features = false } +itp-stf-interface = { default-features = false, path = "../../core-primitives/stf-interface" } +itp-stf-state-observer = { path = "../stf-state-observer", default-features = false } +itp-time-utils = { path = "../../core-primitives/time-utils", default-features = false } +itp-types = { path = "../types", default-features = false } + +# for tests +itp-sgx-temp-dir = { version = "0.1", default-features = false, optional = true, path = "../../core-primitives/sgx/temp-dir" } + +# sgx enabled external libraries +rust-base58_sgx = { package = "rust-base58", rev = "sgx_1.1.3", git = "https://github.com/mesalock-linux/rust-base58-sgx", optional = true, default-features = false, features = ["mesalock_sgx"] } +thiserror_sgx = { package = "thiserror", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3", optional = true } + +# std compatible external libraries (make sure these versions match with the sgx-enabled ones above) +rust-base58 = { package = "rust-base58", version = "0.0.4", optional = true } +thiserror = { version = "1.0", optional = true } + +# no-std dependencies +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +log = { version = "0.4", default-features = false } +sp-core = { default-features = false, features = ["full_crypto"], git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +[dev-dependencies] +itp-sgx-crypto = { path = "../../core-primitives/sgx/crypto", features = ["mocks"] } +itp-stf-state-observer = { path = "../stf-state-observer", features = ["mocks"] } +itp-hashing = { path = "../../core-primitives/hashing", features = ["std"] } + +[features] +default = ["std"] +std = [ + "rust-base58", + "itp-sgx-crypto/std", + "itp-sgx-externalities/std", + "itp-sgx-io/std", + "itp-stf-interface/std", + "itp-stf-state-observer/std", + "itp-time-utils/std", + "itp-types/std", + "thiserror", + "log/std", +] +sgx = [ + "sgx_tstd", + "rust-base58_sgx", + "itp-sgx-crypto/sgx", + "itp-sgx-externalities/sgx", + "itp-sgx-io/sgx", + "itp-stf-state-observer/sgx", + "itp-time-utils/sgx", + "thiserror_sgx", +] +test = [ + "itp-sgx-crypto/mocks", + "itp-stf-interface/mocks", + "itp-sgx-temp-dir", +] diff --git a/bitacross-worker/core-primitives/stf-state-handler/src/error.rs b/bitacross-worker/core-primitives/stf-state-handler/src/error.rs new file mode 100644 index 0000000000..e283c657a8 --- /dev/null +++ b/bitacross-worker/core-primitives/stf-state-handler/src/error.rs @@ -0,0 +1,90 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +#[cfg(feature = "std")] +use rust_base58::base58::FromBase58Error; + +#[cfg(feature = "sgx")] +use base58::FromBase58Error; + +use crate::state_snapshot_primitives::StateId; +use itp_types::ShardIdentifier; +use sgx_types::sgx_status_t; +use std::{boxed::Box, format, string::String}; + +pub type Result = core::result::Result; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("Empty state repository")] + EmptyRepository, + #[error("State ID is invalid and does not exist: {0}")] + InvalidStateId(StateId), + #[error("Shard is invalid and does not exist: {0}")] + InvalidShard(ShardIdentifier), + #[error("State with hash {0} could not be found in the state repository")] + StateNotFoundInRepository(String), + #[error("State observer error: {0}")] + StateObserver(#[from] itp_stf_state_observer::error::Error), + #[error("Cache size for registry is zero")] + ZeroCacheSize, + #[error("Could not acquire lock, lock is poisoned")] + LockPoisoning, + #[error("OsString conversion error")] + OsStringConversion, + #[error("SGX crypto error: {0}")] + CryptoError(itp_sgx_crypto::Error), + #[error("IO error: {0}")] + IO(std::io::Error), + #[error("SGX error, status: {0}")] + SgxError(sgx_status_t), + #[error(transparent)] + Other(#[from] Box), +} + +impl From for Error { + fn from(e: std::io::Error) -> Self { + Self::IO(e) + } +} + +impl From for Error { + fn from(e: codec::Error) -> Self { + Self::Other(format!("{:?}", e).into()) + } +} + +impl From for Error { + fn from(sgx_status: sgx_status_t) -> Self { + Self::SgxError(sgx_status) + } +} + +impl From for Error { + fn from(crypto_error: itp_sgx_crypto::Error) -> Self { + Self::CryptoError(crypto_error) + } +} + +impl From for Error { + fn from(e: FromBase58Error) -> Self { + Self::Other(format!("{:?}", e).into()) + } +} diff --git a/bitacross-worker/core-primitives/stf-state-handler/src/file_io.rs b/bitacross-worker/core-primitives/stf-state-handler/src/file_io.rs new file mode 100644 index 0000000000..c0de994cb5 --- /dev/null +++ b/bitacross-worker/core-primitives/stf-state-handler/src/file_io.rs @@ -0,0 +1,428 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +#[cfg(any(test, feature = "std"))] +use rust_base58::base58::{FromBase58, ToBase58}; + +#[cfg(feature = "sgx")] +use base58::{FromBase58, ToBase58}; + +#[cfg(any(test, feature = "sgx"))] +use std::string::String; + +use crate::{error::Result, state_snapshot_primitives::StateId}; +use codec::{Decode, Encode}; +// Todo: Can be migrated to here in the course of #1292. +use itp_settings::files::SHARDS_PATH; +use itp_types::ShardIdentifier; +use log::error; +use std::{ + format, + path::{Path, PathBuf}, + vec::Vec, +}; + +/// File name of the encrypted state file. +/// +/// It is also the suffix of all past snapshots. +pub const ENCRYPTED_STATE_FILE: &str = "state.bin"; + +/// Helps with file system operations of all files relevant for the State. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct StateDir { + base_path: PathBuf, +} + +impl StateDir { + pub fn new(base_path: PathBuf) -> Self { + Self { base_path } + } + + pub fn shards_directory(&self) -> PathBuf { + self.base_path.join(SHARDS_PATH) + } + + pub fn shard_path(&self, shard: &ShardIdentifier) -> PathBuf { + self.shards_directory().join(shard.encode().to_base58()) + } + + pub fn list_shards(&self) -> Result> { + Ok(list_shards(&self.shards_directory()) + .map(|iter| iter.collect()) + // return an empty vec in case the directory does not exist. + .unwrap_or_default()) + } + + pub fn list_state_ids_for_shard( + &self, + shard_identifier: &ShardIdentifier, + ) -> Result> { + let shard_path = self.shard_path(shard_identifier); + Ok(state_ids_for_shard(shard_path.as_path())?.collect()) + } + + pub fn purge_shard_dir(&self, shard: &ShardIdentifier) { + let shard_dir_path = self.shard_path(shard); + if let Err(e) = std::fs::remove_dir_all(&shard_dir_path) { + error!("Failed to remove shard directory {:?}: {:?}", shard_dir_path, e); + } + } + + pub fn shard_exists(&self, shard: &ShardIdentifier) -> bool { + let shard_path = self.shard_path(shard); + shard_path.exists() && shard_contains_valid_state_id(&shard_path) + } + + pub fn create_shard(&self, shard: &ShardIdentifier) -> Result<()> { + Ok(std::fs::create_dir_all(self.shard_path(shard))?) + } + + pub fn state_file_path(&self, shard: &ShardIdentifier, state_id: StateId) -> PathBuf { + self.shard_path(shard).join(to_file_name(state_id)) + } + + pub fn file_for_state_exists(&self, shard: &ShardIdentifier, state_id: StateId) -> bool { + self.state_file_path(shard, state_id).exists() + } + + #[cfg(feature = "test")] + pub fn given_initialized_shard(&self, shard: &ShardIdentifier) { + if self.shard_exists(shard) { + self.purge_shard_dir(shard); + } + self.create_shard(&shard).unwrap() + } +} + +/// Trait to abstract file I/O for state. +pub trait StateFileIo { + type StateType; + type HashType; + + /// Load a state (returns error if it does not exist). + fn load( + &self, + shard_identifier: &ShardIdentifier, + state_id: StateId, + ) -> Result; + + /// Compute the state hash of a specific state (returns error if it does not exist). + /// + /// Requires loading and decoding of the state. Use only when loading the state repository on + /// initialization of the worker. Computing the state hash in other cases is the + /// StateHandler's responsibility. + fn compute_hash( + &self, + shard_identifier: &ShardIdentifier, + state_id: StateId, + ) -> Result; + + /// Initialize a new shard with a given state. + fn initialize_shard( + &self, + shard_identifier: &ShardIdentifier, + state_id: StateId, + state: &Self::StateType, + ) -> Result; + + /// Write the state. + fn write( + &self, + shard_identifier: &ShardIdentifier, + state_id: StateId, + state: &Self::StateType, + ) -> Result; + + /// Remove a state. + fn remove(&self, shard_identifier: &ShardIdentifier, state_id: StateId) -> Result<()>; + + /// Checks if a given shard directory exists and contains at least one state instance. + fn shard_exists(&self, shard_identifier: &ShardIdentifier) -> bool; + + /// Lists all shards. + fn list_shards(&self) -> Result>; + + /// List all states for a shard. + fn list_state_ids_for_shard(&self, shard_identifier: &ShardIdentifier) -> Result>; +} + +#[cfg(feature = "sgx")] +pub mod sgx { + use super::*; + use crate::error::Error; + use codec::Decode; + use core::fmt::Debug; + use itp_hashing::Hash; + use itp_sgx_crypto::{key_repository::AccessKey, StateCrypto}; + use itp_sgx_externalities::SgxExternalitiesTrait; + use itp_sgx_io::{read as io_read, write as io_write}; + use itp_types::H256; + use log::*; + use std::{fs, marker::PhantomData, path::Path, sync::Arc}; + + /// SGX state file I/O. + pub struct SgxStateFileIo { + state_key_repository: Arc, + state_dir: StateDir, + _phantom: PhantomData, + } + + impl SgxStateFileIo + where + StateKeyRepository: AccessKey, + ::KeyType: StateCrypto, + State: SgxExternalitiesTrait, + { + pub fn new(state_key_repository: Arc, state_dir: StateDir) -> Self { + SgxStateFileIo { state_key_repository, state_dir, _phantom: PhantomData } + } + + fn read(&self, path: &Path) -> Result> { + let mut bytes = io_read(path)?; + + if bytes.is_empty() { + return Ok(bytes) + } + + let state_key = self.state_key_repository.retrieve_key()?; + + state_key + .decrypt(&mut bytes) + .map_err(|e| Error::Other(format!("{:?}", e).into()))?; + trace!("buffer decrypted = {:?}", bytes); + + Ok(bytes) + } + + fn encrypt(&self, mut state: Vec) -> Result> { + let state_key = self.state_key_repository.retrieve_key()?; + + state_key + .encrypt(&mut state) + .map_err(|e| Error::Other(format!("{:?}", e).into()))?; + Ok(state) + } + } + + impl StateFileIo for SgxStateFileIo + where + StateKeyRepository: AccessKey, + ::KeyType: StateCrypto, + State: SgxExternalitiesTrait + Hash + Debug, + ::SgxExternalitiesType: Encode + Decode, + { + type StateType = State; + type HashType = H256; + + fn load( + &self, + shard_identifier: &ShardIdentifier, + state_id: StateId, + ) -> Result { + if !self.state_dir.file_for_state_exists(shard_identifier, state_id) { + return Err(Error::InvalidStateId(state_id)) + } + + let state_path = self.state_dir.state_file_path(shard_identifier, state_id); + trace!("loading state from: {:?}", state_path); + let state_encoded = self.read(&state_path)?; + + // State is now decrypted. + debug!( + "State loaded from {:?} with size {}B, deserializing...", + state_path, + state_encoded.len() + ); + let state = ::SgxExternalitiesType::decode( + &mut state_encoded.as_slice(), + )?; + + trace!("state decoded successfully"); + // Add empty state-diff. + let state_with_diff = State::new(state); + trace!("New state created: {:?}", state_with_diff); + Ok(state_with_diff) + } + + fn compute_hash( + &self, + shard_identifier: &ShardIdentifier, + state_id: StateId, + ) -> Result { + let state = self.load(shard_identifier, state_id)?; + Ok(state.hash()) + } + + fn initialize_shard( + &self, + shard_identifier: &ShardIdentifier, + state_id: StateId, + state: &Self::StateType, + ) -> Result { + self.state_dir.create_shard(&shard_identifier)?; + self.write(shard_identifier, state_id, state) + } + + /// Writes the state (without the state diff) encrypted into the enclave storage. + /// Returns the hash of the saved state (independent of the diff!). + fn write( + &self, + shard_identifier: &ShardIdentifier, + state_id: StateId, + state: &Self::StateType, + ) -> Result { + let state_path = self.state_dir.state_file_path(shard_identifier, state_id); + trace!("writing state to: {:?}", state_path); + + // Only save the state, the state diff is pruned. + let cyphertext = self.encrypt(state.state().encode())?; + + let state_hash = state.hash(); + + io_write(&cyphertext, &state_path)?; + + Ok(state_hash) + } + + fn remove(&self, shard_identifier: &ShardIdentifier, state_id: StateId) -> Result<()> { + Ok(fs::remove_file(self.state_dir.state_file_path(shard_identifier, state_id))?) + } + + fn shard_exists(&self, shard_identifier: &ShardIdentifier) -> bool { + self.state_dir.shard_exists(shard_identifier) + } + + fn list_shards(&self) -> Result> { + self.state_dir.list_shards() + } + + fn list_state_ids_for_shard(&self, shard: &ShardIdentifier) -> Result> { + self.state_dir.list_state_ids_for_shard(shard) + } + } +} + +/// Lists all files with a valid state snapshot naming pattern. +pub(crate) fn state_ids_for_shard(shard_path: &Path) -> Result> { + Ok(items_in_directory(shard_path)?.filter_map(|item| { + match extract_state_id_from_file_name(&item) { + Some(state_id) => Some(state_id), + None => { + log::warn!( + "Found item ({}) that does not match state snapshot naming pattern, ignoring it", + item + ); + None + }, + } + })) +} + +/// Returns an iterator over all valid shards in a directory. +/// +/// Ignore any items (files, directories) that are not valid shard identifiers. +pub(crate) fn list_shards(path: &Path) -> Result> { + Ok(items_in_directory(path)?.filter_map(|base58| match shard_from_base58(&base58) { + Ok(shard) => Some(shard), + Err(e) => { + error!("Found invalid shard ({}). Error: {:?}", base58, e); + None + }, + })) +} + +fn shard_from_base58(base58: &str) -> Result { + let vec = base58.from_base58()?; + Ok(Decode::decode(&mut vec.as_slice())?) +} + +/// Returns an iterator over all filenames in a directory. +fn items_in_directory(directory: &Path) -> Result> { + Ok(directory + .read_dir()? + .filter_map(|fr| fr.ok().and_then(|de| de.file_name().into_string().ok()))) +} + +fn shard_contains_valid_state_id(path: &Path) -> bool { + // If at least on item can be decoded into a state id, the shard is not empty. + match state_ids_for_shard(path) { + Ok(mut iter) => iter.next().is_some(), + Err(e) => { + error!("Error in reading shard dir: {:?}", e); + false + }, + } +} + +fn to_file_name(state_id: StateId) -> String { + format!("{}_{}", state_id, ENCRYPTED_STATE_FILE) +} + +fn extract_state_id_from_file_name(file_name: &str) -> Option { + let state_id_str = file_name.strip_suffix(format!("_{}", ENCRYPTED_STATE_FILE).as_str())?; + state_id_str.parse::().ok() +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::state_snapshot_primitives::generate_current_timestamp_state_id; + + #[test] + fn state_id_to_file_name_works() { + assert!(to_file_name(generate_current_timestamp_state_id()).ends_with(ENCRYPTED_STATE_FILE)); + assert!(to_file_name(generate_current_timestamp_state_id()) + .strip_suffix(format!("_{}", ENCRYPTED_STATE_FILE).as_str()) + .is_some()); + + let now_time_stamp = generate_current_timestamp_state_id(); + assert_eq!( + extract_state_id_from_file_name(to_file_name(now_time_stamp).as_str()).unwrap(), + now_time_stamp + ); + } + + #[test] + fn extract_timestamp_from_file_name_works() { + assert_eq!( + 123456u128, + extract_state_id_from_file_name(format!("123456_{}", ENCRYPTED_STATE_FILE).as_str()) + .unwrap() + ); + assert_eq!( + 0u128, + extract_state_id_from_file_name(format!("0_{}", ENCRYPTED_STATE_FILE).as_str()) + .unwrap() + ); + + assert!(extract_state_id_from_file_name( + format!("987345{}", ENCRYPTED_STATE_FILE).as_str() + ) + .is_none()); + assert!( + extract_state_id_from_file_name(format!("{}", ENCRYPTED_STATE_FILE).as_str()).is_none() + ); + assert!(extract_state_id_from_file_name( + format!("1234_{}-other", ENCRYPTED_STATE_FILE).as_str() + ) + .is_none()); + } +} diff --git a/bitacross-worker/core-primitives/stf-state-handler/src/handle_state.rs b/bitacross-worker/core-primitives/stf-state-handler/src/handle_state.rs new file mode 100644 index 0000000000..8dae3c1f43 --- /dev/null +++ b/bitacross-worker/core-primitives/stf-state-handler/src/handle_state.rs @@ -0,0 +1,83 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(feature = "sgx")] +use std::sync::SgxRwLockWriteGuard as RwLockWriteGuard; + +#[cfg(feature = "std")] +use std::sync::RwLockWriteGuard; + +use crate::error::Result; +use itp_types::ShardIdentifier; + +/// Facade for handling STF state loading and storing (e.g. from file). +pub trait HandleState { + type WriteLockPayload; + type StateT; + type HashType; + + /// Initialize a new shard. + /// + /// Initializes a default state for the shard and returns its hash. + fn initialize_shard(&self, shard: ShardIdentifier) -> Result; + + /// Execute a function that acts (immutably) on the current state. + /// + /// This allows access to the state, without any cloning. + fn execute_on_current(&self, shard: &ShardIdentifier, executing_function: E) -> Result + where + E: FnOnce(&Self::StateT, Self::HashType) -> R; + + /// Load a clone of the current state for a given shard. + /// + /// Requires the shard to exist and be initialized, otherwise returns an error. + /// Because it results in a clone, prefer using `execute_on_current` whenever possible. + fn load_cloned(&self, shard: &ShardIdentifier) -> Result<(Self::StateT, Self::HashType)>; + + /// Load the state in order to mutate it. + /// + /// Returns a write lock to protect against any concurrent access as long as + /// the lock is held. Finalize the operation by calling `write` and returning + /// the lock again. + fn load_for_mutation( + &self, + shard: &ShardIdentifier, + ) -> Result<(RwLockWriteGuard<'_, Self::WriteLockPayload>, Self::StateT)>; + + /// Writes the state (without the state diff) encrypted into the enclave. + /// + /// Returns the hash of the saved state (independent of the diff!). + fn write_after_mutation( + &self, + state: Self::StateT, + state_lock: RwLockWriteGuard<'_, Self::WriteLockPayload>, + shard: &ShardIdentifier, + ) -> Result; + + /// Reset (or override) a state. + /// + /// Use in cases where the previous state is of no interest. Otherwise use `load_for_mutation` and `write_after_mutation`. + fn reset(&self, state: Self::StateT, shard: &ShardIdentifier) -> Result; + + // litentry + /// Migrate state from old shard to new shard + fn migrate_shard( + &self, + old_shard: ShardIdentifier, + new_shard: ShardIdentifier, + ) -> Result; +} diff --git a/bitacross-worker/core-primitives/stf-state-handler/src/in_memory_state_file_io.rs b/bitacross-worker/core-primitives/stf-state-handler/src/in_memory_state_file_io.rs new file mode 100644 index 0000000000..702ccac0ab --- /dev/null +++ b/bitacross-worker/core-primitives/stf-state-handler/src/in_memory_state_file_io.rs @@ -0,0 +1,418 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(feature = "sgx")] +use std::sync::SgxRwLock as RwLock; + +#[cfg(feature = "std")] +use std::sync::RwLock; + +use crate::{ + error::{Error, Result}, + file_io::StateFileIo, + state_snapshot_primitives::StateId, +}; +use codec::Encode; +use itp_sgx_externalities::{SgxExternalities, SgxExternalitiesType}; +use itp_types::{ShardIdentifier, H256}; +use sp_core::blake2_256; +use std::{boxed::Box, collections::HashMap, sync::Arc, vec::Vec}; + +type StateHash = H256; +type ShardDirectory = HashMap; +type ShardsRootDirectory = HashMap>; +type InnerStateSelector = + Box State + Send + Sync + 'static>; +type ExternalStateGenerator = + Box ExternalState + Send + Sync + 'static>; + +/// State file I/O using (unencrypted) in-memory representation of the state files. +/// Can be used as mock for testing. +pub struct InMemoryStateFileIo +where + State: Clone + Default + Encode, +{ + emulated_shard_directory: RwLock>, + state_selector: InnerStateSelector, + external_state_generator: ExternalStateGenerator, +} + +impl InMemoryStateFileIo +where + State: Clone + Default + Encode, +{ + #[allow(unused)] + pub fn new( + shards: &[ShardIdentifier], + state_selector: InnerStateSelector, + external_state_generator: ExternalStateGenerator, + ) -> Self { + let shard_hash_map: HashMap<_, _> = + shards.iter().map(|s| (*s, ShardDirectory::::default())).collect(); + + InMemoryStateFileIo { + emulated_shard_directory: RwLock::new(shard_hash_map), + state_selector, + external_state_generator, + } + } + + #[cfg(any(test, feature = "test"))] + pub fn get_states_for_shard( + &self, + shard_identifier: &ShardIdentifier, + ) -> Result> { + let files_lock = self.emulated_shard_directory.read().map_err(|_| Error::LockPoisoning)?; + files_lock + .get(shard_identifier) + .cloned() + .ok_or_else(|| Error::InvalidShard(*shard_identifier)) + } + + fn compute_state_hash(&self, state: &State) -> StateHash { + let encoded_state = state.encode(); + blake2_256(&encoded_state).into() + } + + fn generate_state_entry(&self, state: State) -> (StateHash, State) { + let state_hash = self.compute_state_hash(&state); + (state_hash, state) + } +} + +impl StateFileIo for InMemoryStateFileIo +where + State: Clone + Default + Encode, +{ + type StateType = ExternalState; + type HashType = StateHash; + + fn load( + &self, + shard_identifier: &ShardIdentifier, + state_id: StateId, + ) -> Result { + let directory_lock = + self.emulated_shard_directory.read().map_err(|_| Error::LockPoisoning)?; + let states_for_shard = directory_lock + .get(shard_identifier) + .ok_or_else(|| Error::InvalidShard(*shard_identifier))?; + let inner_state = states_for_shard + .get(&state_id) + .map(|(_, s)| -> State { s.clone() }) + .ok_or_else(|| Error::InvalidStateId(state_id))?; + + Ok((self.external_state_generator)(inner_state)) + } + + fn compute_hash( + &self, + shard_identifier: &ShardIdentifier, + state_id: StateId, + ) -> Result { + let state = self.load(shard_identifier, state_id)?; + Ok(self.compute_state_hash(&(self.state_selector)(&state))) + } + + fn initialize_shard( + &self, + shard_identifier: &ShardIdentifier, + state_id: StateId, + external_state: &Self::StateType, + ) -> Result { + let mut directory_lock = + self.emulated_shard_directory.write().map_err(|_| Error::LockPoisoning)?; + + let states_for_shard = directory_lock.entry(*shard_identifier).or_default(); + let state_entry = states_for_shard + .entry(state_id) + .or_insert_with(|| self.generate_state_entry((self.state_selector)(external_state))); + Ok(state_entry.0) + } + + fn write( + &self, + shard_identifier: &ShardIdentifier, + state_id: StateId, + external_state: &Self::StateType, + ) -> Result { + let mut directory_lock = + self.emulated_shard_directory.write().map_err(|_| Error::LockPoisoning)?; + + let states_for_shard = directory_lock.entry(*shard_identifier).or_default(); + + let inner_state = (self.state_selector)(external_state); + let state_hash = self.compute_state_hash(&inner_state); + + *states_for_shard.entry(state_id).or_default() = (state_hash, inner_state); + + Ok(state_hash) + } + + fn remove(&self, shard_identifier: &ShardIdentifier, state_id: StateId) -> Result<()> { + let mut directory_lock = + self.emulated_shard_directory.write().map_err(|_| Error::LockPoisoning)?; + + let states_for_shard = directory_lock + .get_mut(shard_identifier) + .ok_or_else(|| Error::InvalidShard(*shard_identifier))?; + + states_for_shard + .remove(&state_id) + .ok_or_else(|| Error::InvalidStateId(state_id)) + .map(|_| {}) + } + + fn shard_exists(&self, shard_identifier: &ShardIdentifier) -> bool { + let directory_lock = self.emulated_shard_directory.read().unwrap(); + directory_lock.contains_key(shard_identifier) + } + + fn list_shards(&self) -> Result> { + let directory_lock = + self.emulated_shard_directory.read().map_err(|_| Error::LockPoisoning)?; + Ok(directory_lock.keys().copied().collect()) + } + + fn list_state_ids_for_shard(&self, shard_identifier: &ShardIdentifier) -> Result> { + let directory_lock = + self.emulated_shard_directory.read().map_err(|_| Error::LockPoisoning)?; + let shard_directory = directory_lock + .get(shard_identifier) + .ok_or_else(|| Error::InvalidShard(*shard_identifier))?; + Ok(shard_directory.keys().cloned().collect()) + } +} + +pub fn create_sgx_externalities_in_memory_state_io( +) -> Arc> { + create_in_memory_externalities_state_io(&[]) +} + +fn create_in_memory_externalities_state_io( + shards: &[ShardIdentifier], +) -> Arc> { + Arc::new(InMemoryStateFileIo::new( + shards, + sgx_externalities_selector(), + sgx_externalities_wrapper(), + )) +} + +fn sgx_externalities_selector() -> InnerStateSelector { + Box::new(|s| s.state.clone()) +} + +fn sgx_externalities_wrapper() -> ExternalStateGenerator { + Box::new(|s| SgxExternalities { state: s, state_diff: Default::default() }) +} + +#[cfg(feature = "sgx")] +pub mod sgx { + use super::*; + use crate::file_io::list_shards; + use std::path::Path; + + pub fn create_in_memory_state_io_from_shards_directories( + path: &Path, + ) -> Result>> { + let shards: Vec = + list_shards(path).map(|iter| iter.collect()).unwrap_or_default(); + Ok(create_in_memory_externalities_state_io(&shards)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::assert_matches::assert_matches; + + type TestState = u64; + type TestStateFileIo = InMemoryStateFileIo; + + #[test] + fn shard_directory_is_empty_after_initialization() { + let state_file_io = create_empty_in_memory_state_file_io(); + assert!(state_file_io.list_shards().unwrap().is_empty()); + } + + #[test] + fn load_on_empty_directory_and_shard_returns_error() { + let state_file_io = create_empty_in_memory_state_file_io(); + + assert_matches!( + state_file_io.load(&ShardIdentifier::random(), 1234), + Err(Error::InvalidShard(_)) + ); + } + + #[test] + fn initialize_with_shard_creates_empty_directory() { + let shard = ShardIdentifier::from([2u8; 32]); + let state_file_io = create_in_memory_state_file_io(&[shard]); + + assert!(state_file_io.list_state_ids_for_shard(&shard).unwrap().is_empty()); + assert!(state_file_io + .list_state_ids_for_shard(&ShardIdentifier::from([3u8; 32])) + .is_err()); + } + + #[test] + fn load_when_state_does_not_exist_returns_error() { + let state_file_io = create_empty_in_memory_state_file_io(); + let shard_id = ShardIdentifier::random(); + let _ = state_file_io.initialize_shard(&shard_id, 1234, &Default::default()).unwrap(); + + assert_matches!(state_file_io.load(&shard_id, 12345), Err(Error::InvalidStateId(12345))); + } + + #[test] + fn create_initialized_when_shard_already_exists_works() { + let shard = ShardIdentifier::random(); + let state_file_io = create_in_memory_state_file_io(&[shard]); + + assert!(state_file_io.initialize_shard(&shard, 1245, &Default::default()).is_ok()); + } + + #[test] + fn create_initialized_adds_default_state() { + let state_file_io = create_empty_in_memory_state_file_io(); + let shard_id = ShardIdentifier::random(); + let state_id = 31081984u128; + let state_hash = state_file_io + .initialize_shard(&shard_id, state_id, &Default::default()) + .unwrap(); + + assert_eq!(1, state_file_io.list_shards().unwrap().len()); + assert_eq!(TestState::default(), state_file_io.load(&shard_id, state_id).unwrap()); + assert_eq!(1, state_file_io.list_state_ids_for_shard(&shard_id).unwrap().len()); + + assert_entry(&state_file_io, &shard_id, state_id, &TestState::default(), &state_hash); + } + + #[test] + fn write_works_when_no_previous_shard_or_file_exists() { + let state_file_io = create_empty_in_memory_state_file_io(); + let shard_id = ShardIdentifier::random(); + let state_id = 23u128; + let test_state = 42u64; + + let state_hash = state_file_io.write(&shard_id, state_id, &test_state).unwrap(); + + assert_eq!(1, state_file_io.list_shards().unwrap().len()); + assert_eq!(test_state, state_file_io.load(&shard_id, state_id).unwrap()); + assert_eq!(1, state_file_io.list_state_ids_for_shard(&shard_id).unwrap().len()); + assert_entry(&state_file_io, &shard_id, state_id, &test_state, &state_hash); + } + + #[test] + fn write_overwrites_existing_state() { + let state_file_io = create_empty_in_memory_state_file_io(); + let shard_id = ShardIdentifier::random(); + let state_id = 123456u128; + let _ = state_file_io + .initialize_shard(&shard_id, state_id, &Default::default()) + .unwrap(); + + let test_state = 4256u64; + let state_hash = state_file_io.write(&shard_id, state_id, &test_state).unwrap(); + + assert_eq!(1, state_file_io.list_shards().unwrap().len()); + assert_eq!(test_state, state_file_io.load(&shard_id, state_id).unwrap()); + assert_eq!(1, state_file_io.list_state_ids_for_shard(&shard_id).unwrap().len()); + assert_entry(&state_file_io, &shard_id, state_id, &test_state, &state_hash); + } + + #[test] + fn remove_files_works() { + let state_file_io = create_empty_in_memory_state_file_io(); + let shard_id = ShardIdentifier::random(); + let initial_state_id = 42u128; + let _ = state_file_io + .initialize_shard(&shard_id, initial_state_id, &Default::default()) + .unwrap(); + + let state_ids = vec![1u128, 2u128, 3u128]; + + for state_id in state_ids.iter() { + let _ = state_file_io.write(&shard_id, *state_id, &987345).unwrap(); + } + + let mut expected_size = state_ids.len() + 1; + assert_eq!(expected_size, state_file_io.list_state_ids_for_shard(&shard_id).unwrap().len()); + expected_size -= 1; + + for state_id in state_ids.iter() { + state_file_io.remove(&shard_id, *state_id).unwrap(); + assert_matches!( + state_file_io.load(&shard_id, *state_id), + Err(Error::InvalidStateId(_)) + ); + assert_eq!( + expected_size, + state_file_io.list_state_ids_for_shard(&shard_id).unwrap().len() + ); + expected_size -= 1; + } + } + + #[test] + fn initialize_with_shards_creates_empty_maps() { + let shards = vec![ShardIdentifier::random(), ShardIdentifier::random()]; + let state_file_io = create_in_memory_state_file_io(shards.as_slice()); + + assert_eq!(shards.len(), state_file_io.list_shards().unwrap().len()); + for shard in shards { + assert!(state_file_io.list_state_ids_for_shard(&shard).unwrap().is_empty()); + } + } + + fn assert_entry( + state_file_io: &TestStateFileIo, + shard_id: &ShardIdentifier, + state_id: StateId, + state: &TestState, + state_hash: &StateHash, + ) { + let (retrieved_hash, retrieved_state) = + get_state_entry(&state_file_io, &shard_id, state_id); + assert!(state_file_io.shard_exists(shard_id)); + assert_eq!(state_hash, &retrieved_hash); + assert_eq!(state, &retrieved_state); + } + + fn get_state_entry( + state_file_io: &TestStateFileIo, + shard_id: &ShardIdentifier, + state_id: StateId, + ) -> (StateHash, TestState) { + state_file_io + .get_states_for_shard(shard_id) + .unwrap() + .get(&state_id) + .unwrap() + .clone() + } + + fn create_in_memory_state_file_io(shards: &[ShardIdentifier]) -> TestStateFileIo { + InMemoryStateFileIo::new(shards, Box::new(|x| *x), Box::new(|x| x)) + } + + fn create_empty_in_memory_state_file_io() -> TestStateFileIo { + create_in_memory_state_file_io(&[]) + } +} diff --git a/bitacross-worker/core-primitives/stf-state-handler/src/lib.rs b/bitacross-worker/core-primitives/stf-state-handler/src/lib.rs new file mode 100644 index 0000000000..4b6235f9c0 --- /dev/null +++ b/bitacross-worker/core-primitives/stf-state-handler/src/lib.rs @@ -0,0 +1,46 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#![cfg_attr(not(feature = "std"), no_std)] +#![feature(assert_matches)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +// re-export module to properly feature gate sgx and regular std environment +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod sgx_reexport_prelude { + pub use rust_base58_sgx as base58; + pub use thiserror_sgx as thiserror; +} + +pub mod error; +pub mod file_io; +pub mod handle_state; +pub mod in_memory_state_file_io; +pub mod query_shard_state; +pub mod state_handler; +pub mod state_initializer; +mod state_snapshot_primitives; +pub mod state_snapshot_repository; +pub mod state_snapshot_repository_loader; +pub mod test; + +pub use state_handler::StateHandler; diff --git a/bitacross-worker/core-primitives/stf-state-handler/src/query_shard_state.rs b/bitacross-worker/core-primitives/stf-state-handler/src/query_shard_state.rs new file mode 100644 index 0000000000..11ff46d044 --- /dev/null +++ b/bitacross-worker/core-primitives/stf-state-handler/src/query_shard_state.rs @@ -0,0 +1,32 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::error::Result; +use itp_types::ShardIdentifier; +use std::vec::Vec; + +/// Trait for querying shard information on the state +/// +/// The reason this is a separate trait, is that it does not require any +/// SGX exclusive data structures (feature sgx) +pub trait QueryShardState { + /// Query whether a given shard exists + fn shard_exists(&self, shard: &ShardIdentifier) -> Result; + + /// List all available shards + fn list_shards(&self) -> Result>; +} diff --git a/bitacross-worker/core-primitives/stf-state-handler/src/state_handler.rs b/bitacross-worker/core-primitives/stf-state-handler/src/state_handler.rs new file mode 100644 index 0000000000..6acf2c579d --- /dev/null +++ b/bitacross-worker/core-primitives/stf-state-handler/src/state_handler.rs @@ -0,0 +1,423 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(feature = "sgx")] +use std::sync::{SgxRwLock as RwLock, SgxRwLockWriteGuard as RwLockWriteGuard}; + +#[cfg(feature = "std")] +use std::sync::{RwLock, RwLockWriteGuard}; + +use crate::{ + error::{Error, Result}, + handle_state::HandleState, + query_shard_state::QueryShardState, + state_initializer::InitializeState, + state_snapshot_repository::VersionedStateAccess, +}; +use core::fmt::Debug; +use itp_hashing::Hash; +use itp_sgx_externalities::SgxExternalitiesTrait; +use itp_stf_state_observer::traits::UpdateState; +use itp_types::ShardIdentifier; +use log::{debug, trace}; +use std::{collections::HashMap, sync::Arc, vec::Vec}; + +type StatesMap = HashMap; + +/// Implementation of the `HandleState` trait. +/// +/// Responsible for handling any state instances. Holds a map with all the latest states for each shard. +/// In addition, uses the snapshot repository to save file snapshots of a state. +pub struct StateHandler +where + Repository: VersionedStateAccess, +{ + state_snapshot_repository: RwLock, + states_map_lock: RwLock>, + state_observer: Arc, + state_initializer: Arc, +} + +impl + StateHandler +where + Repository: VersionedStateAccess, + Repository::StateType: Hash, + StateObserver: UpdateState, + StateInitializer: InitializeState, +{ + /// Creates a new instance WITHOUT loading any state from the repository. + /// Results in an empty states map. + pub fn new( + state_snapshot_repository: Repository, + state_observer: Arc, + state_initializer: Arc, + ) -> Self { + Self::new_with_states_map( + state_snapshot_repository, + state_observer, + state_initializer, + Default::default(), + ) + } + + /// Create a new state handler and initialize its state map with the + /// states that are available in the snapshot repository. + pub fn load_from_repository( + state_snapshot_repository: Repository, + state_observer: Arc, + state_initializer: Arc, + ) -> Result { + let states_map = Self::load_all_latest_snapshots(&state_snapshot_repository)?; + Ok(Self::new_with_states_map( + state_snapshot_repository, + state_observer, + state_initializer, + states_map, + )) + } + + fn new_with_states_map( + state_snapshot_repository: Repository, + state_observer: Arc, + state_initializer: Arc, + states_map: StatesMap, + ) -> Self { + StateHandler { + state_snapshot_repository: RwLock::new(state_snapshot_repository), + states_map_lock: RwLock::new(states_map), + state_observer, + state_initializer, + } + } + + fn load_all_latest_snapshots( + state_snapshot_repository: &Repository, + ) -> Result> { + let shards = state_snapshot_repository.list_shards()?; + + let r = shards + .into_iter() + .map(|shard| state_snapshot_repository.load_latest(&shard).map(|state| (state, shard))) + // Fill the pairs for state and shard into a map. + // Log an error for cases where state could not be loaded. + .fold(StatesMap::default(), |mut map, x| { + match x { + Ok((state, shard)) => { + let state_hash = state.hash(); + map.insert(shard, (state, state_hash)); + }, + Err(e) => { + log::error!("Failed to load state from snapshot repository {:?}", e); + }, + }; + map + }); + + Ok(r) + } + + fn update_state_snapshot( + &self, + shard: &ShardIdentifier, + state: &Repository::StateType, + state_hash: Repository::HashType, + ) -> Result<()> { + let mut state_snapshots_lock = + self.state_snapshot_repository.write().map_err(|_| Error::LockPoisoning)?; + + state_snapshots_lock.update(shard, state, state_hash) + } +} + +impl HandleState + for StateHandler +where + Repository: VersionedStateAccess, + Repository::StateType: SgxExternalitiesTrait + Hash + Debug, + Repository::HashType: Copy, + StateObserver: UpdateState, + StateInitializer: InitializeState, +{ + type WriteLockPayload = StatesMap; + type StateT = Repository::StateType; + type HashType = Repository::HashType; + + fn initialize_shard(&self, shard: ShardIdentifier) -> Result { + let initialized_state = self.state_initializer.initialize()?; + self.reset(initialized_state, &shard) + } + + fn execute_on_current(&self, shard: &ShardIdentifier, executing_function: E) -> Result + where + E: FnOnce(&Self::StateT, Self::HashType) -> R, + { + self.states_map_lock + .read() + .map_err(|_| Error::LockPoisoning)? + .get(shard) + .map(|(state, state_hash)| executing_function(state, *state_hash)) + .ok_or_else(|| Error::InvalidShard(*shard)) + } + + fn load_cloned(&self, shard: &ShardIdentifier) -> Result<(Self::StateT, Self::HashType)> { + let state = self + .states_map_lock + .read() + .map_err(|_| Error::LockPoisoning)? + .get(shard) + .ok_or_else(|| Error::InvalidShard(*shard))? + .clone(); + + Ok(state) + } + + fn load_for_mutation( + &self, + shard: &ShardIdentifier, + ) -> Result<(RwLockWriteGuard<'_, Self::WriteLockPayload>, Self::StateT)> { + let state_write_lock = self.states_map_lock.write().map_err(|_| Error::LockPoisoning)?; + let state_clone = state_write_lock + .get(shard) + .ok_or_else(|| Error::InvalidShard(*shard))? + .0 + .clone(); + + Ok((state_write_lock, state_clone)) + } + + fn write_after_mutation( + &self, + mut state: Self::StateT, + mut state_lock: RwLockWriteGuard<'_, Self::WriteLockPayload>, + shard: &ShardIdentifier, + ) -> Result { + debug!("Writing state"); + trace!("State: {:?}", state); + state.prune_state_diff(); // Remove state diff before storing. + let state_hash = state.hash(); + // We create a state copy here, in order to serve the state observer. This does not scale + // well and we will want a better solution in the future, maybe with #459. + state_lock.insert(*shard, (state.clone(), state_hash)); + drop(state_lock); // Drop the write lock as early as possible. + + self.update_state_snapshot(shard, &state, state_hash)?; + + self.state_observer.queue_state_update(*shard, state)?; + Ok(state_hash) + } + + fn reset(&self, state: Self::StateT, shard: &ShardIdentifier) -> Result { + debug!("Resetting state"); + trace!("Resetting state: {:?}", state); + let state_write_lock = self.states_map_lock.write().map_err(|_| Error::LockPoisoning)?; + self.write_after_mutation(state, state_write_lock, shard) + } + + fn migrate_shard( + &self, + old_shard: ShardIdentifier, + new_shard: ShardIdentifier, + ) -> Result { + let (state, _) = self.load_cloned(&old_shard)?; + self.reset(state, &new_shard) + } +} + +impl QueryShardState + for StateHandler +where + Repository: VersionedStateAccess, + Repository::StateType: Hash, + StateObserver: UpdateState, + StateInitializer: InitializeState, +{ + fn shard_exists(&self, shard: &ShardIdentifier) -> Result { + let states_map_lock = self.states_map_lock.read().map_err(|_| Error::LockPoisoning)?; + Ok(states_map_lock.contains_key(shard)) + } + + fn list_shards(&self) -> Result> { + let states_map_lock = self.states_map_lock.read().map_err(|_| Error::LockPoisoning)?; + Ok(states_map_lock.keys().cloned().collect()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test::mocks::{ + initialize_state_mock::InitializeStateMock, + versioned_state_access_mock::VersionedStateAccessMock, + }; + use codec::Encode; + use itp_sgx_externalities::{SgxExternalities, SgxExternalitiesType}; + use itp_stf_state_observer::mock::UpdateStateMock; + use itp_types::H256; + use std::{collections::VecDeque, sync::Arc, thread}; + + type TestState = SgxExternalities; + type TestHash = H256; + type TestStateRepository = VersionedStateAccessMock; + type TestStateObserver = UpdateStateMock; + type TestStateInitializer = InitializeStateMock; + type TestStateHandler = + StateHandler; + + fn create_state(content: u64) -> TestState { + let mut state = TestState::new(SgxExternalitiesType::default()); + state.insert("key_1".encode(), content.encode()); + state + } + + fn create_state_without_diff(content: u64) -> TestState { + let state = create_state(content); + prune_diff(state) + } + + fn prune_diff(mut state: TestState) -> TestState { + state.prune_state_diff(); + state + } + + #[test] + fn load_for_mutation_blocks_any_concurrent_access() { + let shard_id = ShardIdentifier::random(); + let state_handler = default_state_handler(); + state_handler.initialize_shard(shard_id).unwrap(); + + let (lock, _s) = state_handler.load_for_mutation(&shard_id).unwrap(); + + let state_handler_clone = state_handler.clone(); + let join_handle = thread::spawn(move || { + let (latest_state, _) = state_handler_clone.load_cloned(&shard_id).unwrap(); + assert_eq!(create_state_without_diff(4u64), latest_state); + }); + + let _hash = + state_handler.write_after_mutation(create_state(4u64), lock, &shard_id).unwrap(); + + join_handle.join().unwrap(); + } + + #[test] + fn write_and_reset_queue_observer_update() { + let shard_id = ShardIdentifier::default(); + let state_observer = Arc::new(TestStateObserver::default()); + let state_initializer = Arc::new(TestStateInitializer::new(Default::default())); + let state_handler = Arc::new(TestStateHandler::new( + default_repository(), + state_observer.clone(), + state_initializer, + )); + state_handler.initialize_shard(shard_id).unwrap(); + + let (lock, _s) = state_handler.load_for_mutation(&shard_id).unwrap(); + let new_state = create_state(4u64); + state_handler.write_after_mutation(new_state.clone(), lock, &shard_id).unwrap(); + + let reset_state = create_state(5u64); + state_handler.reset(reset_state.clone(), &shard_id).unwrap(); + + let observer_updates = state_observer.queued_updates.read().unwrap().clone(); + assert_eq!(3, observer_updates.len()); + assert_eq!((shard_id, prune_diff(new_state)), observer_updates[1]); + assert_eq!((shard_id, prune_diff(reset_state)), observer_updates[2]); + } + + #[test] + fn load_initialized_works() { + let shard_id = ShardIdentifier::random(); + let state_handler = default_state_handler(); + state_handler.initialize_shard(shard_id).unwrap(); + assert!(state_handler.load_cloned(&shard_id).is_ok()); + assert!(state_handler.load_cloned(&ShardIdentifier::random()).is_err()); + } + + #[test] + fn list_shards_works() { + let shard_id = ShardIdentifier::random(); + let state_handler = default_state_handler(); + state_handler.initialize_shard(shard_id).unwrap(); + assert_eq!(1, state_handler.list_shards().unwrap().len()); + } + + #[test] + fn shard_exists_works() { + let shard_id = ShardIdentifier::random(); + let state_handler = default_state_handler(); + state_handler.initialize_shard(shard_id).unwrap(); + assert!(state_handler.shard_exists(&shard_id).unwrap()); + assert!(!state_handler.shard_exists(&ShardIdentifier::random()).unwrap()); + } + + #[test] + fn load_from_repository_works() { + let state_observer = Arc::new(TestStateObserver::default()); + let state_initializer = Arc::new(TestStateInitializer::new(Default::default())); + + let repository = TestStateRepository::new(HashMap::from([ + ( + ShardIdentifier::from([1u8; 32]), + VecDeque::from([create_state(3), create_state(2), create_state(1)]), + ), + (ShardIdentifier::from([2u8; 32]), VecDeque::from([create_state(5)])), + (ShardIdentifier::from([3u8; 32]), VecDeque::new()), + ])); + + assert_eq!(3, repository.list_shards().unwrap().len()); + assert!(repository.load_latest(&ShardIdentifier::from([3u8; 32])).is_err()); + + let state_handler = + TestStateHandler::load_from_repository(repository, state_observer, state_initializer) + .unwrap(); + + assert_eq!( + 2, + state_handler.list_shards().unwrap().len(), + "Only 2 shards, not 3, because 3rd was empty" + ); + } + + #[test] + fn ensure_state_diff_is_discarded() { + let shard_id = ShardIdentifier::random(); + let state_handler = default_state_handler(); + + let state = create_state(3u64); + let state_without_diff = { + let mut state_clone = state.clone(); + state_clone.prune_state_diff(); + state_clone + }; + + state_handler.reset(state, &shard_id).unwrap(); + let (loaded_state, _) = state_handler.load_cloned(&shard_id).unwrap(); + + assert_eq!(state_without_diff, loaded_state); + } + + fn default_state_handler() -> Arc { + let state_observer = Arc::new(TestStateObserver::default()); + let state_initializer = Arc::new(TestStateInitializer::new(Default::default())); + Arc::new(TestStateHandler::new(default_repository(), state_observer, state_initializer)) + } + + fn default_repository() -> TestStateRepository { + TestStateRepository::default() + } +} diff --git a/bitacross-worker/core-primitives/stf-state-handler/src/state_initializer.rs b/bitacross-worker/core-primitives/stf-state-handler/src/state_initializer.rs new file mode 100644 index 0000000000..5799c20823 --- /dev/null +++ b/bitacross-worker/core-primitives/stf-state-handler/src/state_initializer.rs @@ -0,0 +1,64 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::error::Result; +use core::marker::PhantomData; +use itp_sgx_crypto::{ed25519_derivation::DeriveEd25519, key_repository::AccessKey}; +use itp_stf_interface::InitState; +use itp_types::AccountId; +use sp_core::Pair; +use std::sync::Arc; + +/// Create and initialize a new state instance. +pub trait InitializeState { + type StateType; + + fn initialize(&self) -> Result; +} + +pub struct StateInitializer { + shielding_key_repository: Arc, + _phantom: PhantomData<(State, Stf)>, +} + +impl StateInitializer +where + Stf: InitState, + ShieldingKeyRepository: AccessKey, + ShieldingKeyRepository::KeyType: DeriveEd25519, +{ + pub fn new(shielding_key_repository: Arc) -> Self { + Self { shielding_key_repository, _phantom: Default::default() } + } +} + +impl InitializeState + for StateInitializer +where + Stf: InitState, + ShieldingKeyRepository: AccessKey, + ShieldingKeyRepository::KeyType: DeriveEd25519, +{ + type StateType = State; + + fn initialize(&self) -> Result { + // This implementation basically exists because it is non-trivial to initialize the state with + // an enclave account that is derived from the shielding key. + let enclave_account = self.shielding_key_repository.retrieve_key()?.derive_ed25519()?; + Ok(Stf::init_state(enclave_account.public().into())) + } +} diff --git a/bitacross-worker/core-primitives/stf-state-handler/src/state_snapshot_primitives.rs b/bitacross-worker/core-primitives/stf-state-handler/src/state_snapshot_primitives.rs new file mode 100644 index 0000000000..50c3f00afc --- /dev/null +++ b/bitacross-worker/core-primitives/stf-state-handler/src/state_snapshot_primitives.rs @@ -0,0 +1,56 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{error::Result, file_io::StateFileIo}; +use itp_time_utils::now_as_nanos; +use itp_types::ShardIdentifier; +use std::collections::{HashMap, VecDeque}; + +pub type StateId = u128; + +pub(crate) type SnapshotHistory = + HashMap>>; + +/// Internal wrapper for a state hash and state ID. +#[derive(Clone)] +pub(crate) struct StateSnapshotMetaData { + pub(crate) state_hash: HashType, + pub(crate) state_id: StateId, +} + +impl StateSnapshotMetaData { + pub fn new(state_hash: HashType, state_id: StateId) -> Self { + StateSnapshotMetaData { state_hash, state_id } + } +} + +pub(crate) fn initialize_shard_with_snapshot( + shard_identifier: &ShardIdentifier, + file_io: &FileIo, + state: &FileIo::StateType, +) -> Result> +where + FileIo: StateFileIo, +{ + let state_id = generate_current_timestamp_state_id(); + let state_hash = file_io.initialize_shard(shard_identifier, state_id, state)?; + Ok(StateSnapshotMetaData::new(state_hash, state_id)) +} + +pub(crate) fn generate_current_timestamp_state_id() -> StateId { + now_as_nanos() +} diff --git a/bitacross-worker/core-primitives/stf-state-handler/src/state_snapshot_repository.rs b/bitacross-worker/core-primitives/stf-state-handler/src/state_snapshot_repository.rs new file mode 100644 index 0000000000..1b60a88741 --- /dev/null +++ b/bitacross-worker/core-primitives/stf-state-handler/src/state_snapshot_repository.rs @@ -0,0 +1,484 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + error::{Error, Result}, + file_io::StateFileIo, + state_snapshot_primitives::{ + generate_current_timestamp_state_id, initialize_shard_with_snapshot, SnapshotHistory, + StateId, StateSnapshotMetaData, + }, +}; +use core::ops::RangeBounds; +use itp_types::ShardIdentifier; +use log::*; +use std::{collections::VecDeque, fmt::Debug, format, sync::Arc, vec::Vec}; + +/// Trait for versioned state access. Manages history of state snapshots. +pub trait VersionedStateAccess { + type StateType: Clone; + type HashType; + + /// Load the latest version of the state. + fn load_latest(&self, shard_identifier: &ShardIdentifier) -> Result; + + /// Update the state, returning the hash of the state. + fn update( + &mut self, + shard_identifier: &ShardIdentifier, + state: &Self::StateType, + state_hash: Self::HashType, + ) -> Result<()>; + + /// Reverts the state of a given shard to a state version identified by a state hash. + fn revert_to( + &mut self, + shard_identifier: &ShardIdentifier, + state_hash: &Self::HashType, + ) -> Result; + + /// Initialize a new shard. + /// + /// If the shard already exists, it will re-initialize it. + fn initialize_new_shard( + &mut self, + shard_identifier: ShardIdentifier, + state: &Self::StateType, + ) -> Result; + + /// Checks if a shard for a given identifier exists. + fn shard_exists(&self, shard_identifier: &ShardIdentifier) -> bool; + + /// Lists all shards. + fn list_shards(&self) -> Result>; +} + +/// State snapshot repository. +/// +/// Keeps versions of state snapshots, cycles them in a fixed-size circular buffer. +/// Creates a state snapshot for each write/update operation. Allows reverting to a specific snapshot, +/// identified by a state hash. Snapshot files names includes a timestamp to be unique. +pub struct StateSnapshotRepository +where + FileIo: StateFileIo, + ::HashType: Copy + Eq + Debug, + ::StateType: Clone, +{ + file_io: Arc, + snapshot_history_cache_size: usize, + snapshot_history: SnapshotHistory, +} + +impl StateSnapshotRepository +where + FileIo: StateFileIo, + ::HashType: Copy + Eq + Debug, + ::StateType: Clone, +{ + /// Constructor, initialized with no shards or snapshot history. + pub fn empty(file_io: Arc, snapshot_history_cache_size: usize) -> Result { + Self::new(file_io, snapshot_history_cache_size, SnapshotHistory::default()) + } + + /// Constructor to initialize the repository with shards and snapshot history. + /// + /// Crate private, to be used by the loader. + pub(crate) fn new( + file_io: Arc, + snapshot_history_cache_size: usize, + snapshot_history: SnapshotHistory, + ) -> Result { + if snapshot_history_cache_size == 0usize { + return Err(Error::ZeroCacheSize) + } + + Ok(StateSnapshotRepository { file_io, snapshot_history_cache_size, snapshot_history }) + } + + fn get_snapshot_history_mut( + &mut self, + shard_identifier: &ShardIdentifier, + ) -> Result<&mut VecDeque>> { + self.snapshot_history + .get_mut(shard_identifier) + .ok_or_else(|| Error::InvalidShard(*shard_identifier)) + } + + fn get_snapshot_history( + &self, + shard_identifier: &ShardIdentifier, + ) -> Result<&VecDeque>> { + self.snapshot_history + .get(shard_identifier) + .ok_or_else(|| Error::InvalidShard(*shard_identifier)) + } + + fn get_latest_snapshot_metadata( + &self, + shard_identifier: &ShardIdentifier, + ) -> Result<&StateSnapshotMetaData> { + let snapshot_history = self.get_snapshot_history(shard_identifier)?; + snapshot_history.front().ok_or(Error::EmptyRepository) + } + + fn prune_snapshot_history_by_range>( + &mut self, + shard_identifier: &ShardIdentifier, + range: R, + ) -> Result<()> { + let state_snapshots_to_remove = self + .get_snapshot_history_mut(shard_identifier)? + .drain(range) + .collect::>(); + + self.remove_snapshots(shard_identifier, state_snapshots_to_remove.as_slice()); + Ok(()) + } + + /// Remove snapshots referenced by metadata. + /// Does not stop on error, it's guaranteed to call `remove` on all elements. + /// Logs any errors that occur. + fn remove_snapshots( + &self, + shard_identifier: &ShardIdentifier, + snapshots_metadata: &[StateSnapshotMetaData], + ) { + for snapshot_metadata in snapshots_metadata { + if let Err(e) = self.file_io.remove(shard_identifier, snapshot_metadata.state_id) { + // We just log an error, don't want to return the error here, because the operation + // in general was successful, just a side-effect that failed. + error!("Failed to remove state, with id '{}': {:?}", snapshot_metadata.state_id, e); + } + } + } + + fn write_new_state( + &self, + shard_identifier: &ShardIdentifier, + state: &FileIo::StateType, + ) -> Result<(FileIo::HashType, StateId)> { + let state_id = generate_current_timestamp_state_id(); + let state_hash = self.file_io.write(shard_identifier, state_id, state)?; + Ok((state_hash, state_id)) + } + + fn initialize_shard_with_snapshot( + &mut self, + shard_identifier: &ShardIdentifier, + state: &FileIo::StateType, + ) -> Result { + let snapshot_metadata = + initialize_shard_with_snapshot(shard_identifier, self.file_io.as_ref(), state)?; + + let state_hash = snapshot_metadata.state_hash; + self.snapshot_history + .insert(*shard_identifier, VecDeque::from([snapshot_metadata])); + Ok(state_hash) + } + + fn load_state( + &self, + shard_identifier: &ShardIdentifier, + snapshot_metadata: &StateSnapshotMetaData, + ) -> Result { + self.file_io.load(shard_identifier, snapshot_metadata.state_id) + } +} + +impl VersionedStateAccess for StateSnapshotRepository +where + FileIo: StateFileIo, + ::HashType: Copy + Eq + Debug, + ::StateType: Clone, +{ + type StateType = FileIo::StateType; + type HashType = FileIo::HashType; + + fn load_latest(&self, shard_identifier: &ShardIdentifier) -> Result { + let latest_snapshot_metadata = self.get_latest_snapshot_metadata(shard_identifier)?; + self.file_io.load(shard_identifier, latest_snapshot_metadata.state_id) + } + + fn update( + &mut self, + shard_identifier: &ShardIdentifier, + state: &Self::StateType, + state_hash: Self::HashType, + ) -> Result<()> { + if !self.shard_exists(shard_identifier) { + self.initialize_shard_with_snapshot(shard_identifier, state)?; + return Ok(()) + } + + let (_state_hash, state_id) = self.write_new_state(shard_identifier, state)?; + let cache_size = self.snapshot_history_cache_size; + + let snapshot_history = self.get_snapshot_history_mut(shard_identifier)?; + snapshot_history.push_front(StateSnapshotMetaData::new(state_hash, state_id)); + + // In case we're above max queue size we remove the oldest entries and corresponding files + if snapshot_history.len() > cache_size { + self.prune_snapshot_history_by_range(shard_identifier, cache_size..)?; + } + + Ok(()) + } + + fn revert_to( + &mut self, + shard_identifier: &ShardIdentifier, + state_hash: &Self::HashType, + ) -> Result { + let snapshot_history = self.get_snapshot_history(shard_identifier)?; + + // We use `position()` instead of `find()`, because it then allows us to easily drain + // all the newer states. + let snapshot_metadata_index = snapshot_history + .iter() + .position(|fmd| fmd.state_hash == *state_hash) + .ok_or_else(|| Error::StateNotFoundInRepository(format!("{:?}", state_hash)))?; + + // Should never fail, since we got the index from above, with `position()`. + let snapshot_metadata = snapshot_history + .get(snapshot_metadata_index) + .ok_or_else(|| Error::StateNotFoundInRepository(format!("{:?}", state_hash)))?; + + let state = self.load_state(shard_identifier, snapshot_metadata)?; + + // Remove any state versions newer than the one we're resetting to + // (do this irreversible operation last, to ensure the loading has succeeded) + self.prune_snapshot_history_by_range(shard_identifier, ..snapshot_metadata_index)?; + + Ok(state) + } + + fn initialize_new_shard( + &mut self, + shard_identifier: ShardIdentifier, + state: &Self::StateType, + ) -> Result { + self.initialize_shard_with_snapshot(&shard_identifier, state) + } + + fn shard_exists(&self, shard_identifier: &ShardIdentifier) -> bool { + self.snapshot_history.get(shard_identifier).is_some() + } + + fn list_shards(&self) -> Result> { + Ok(self.snapshot_history.keys().cloned().collect()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + in_memory_state_file_io::InMemoryStateFileIo, + state_snapshot_repository_loader::StateSnapshotRepositoryLoader, + test::mocks::initialize_state_mock::InitializeStateMock, + }; + use codec::Encode; + use itp_hashing::Hash; + use sp_core::{blake2_256, H256}; + use std::vec; + + #[derive(Encode, Clone, Default, Copy, Eq, PartialEq, Debug)] + struct TestState(pub u64); + + impl Hash for TestState { + fn hash(&self) -> H256 { + blake2_256(&self.encode()).into() + } + } + + type TestFileIo = InMemoryStateFileIo; + type TestStateInitializer = InitializeStateMock; + type TestSnapshotRepository = StateSnapshotRepository; + + const TEST_SNAPSHOT_REPOSITORY_CACHE_SIZE: usize = 3; + + #[test] + fn new_with_zero_cache_size_returns_error() { + let shards = + vec![ShardIdentifier::random(), ShardIdentifier::random(), ShardIdentifier::random()]; + let file_io = create_test_file_io(shards.as_slice()); + + assert!(TestSnapshotRepository::empty(file_io.clone(), 0usize).is_err()); + } + + #[test] + fn upon_new_all_shards_are_initialized() { + let shards = + vec![ShardIdentifier::random(), ShardIdentifier::random(), ShardIdentifier::random()]; + let (file_io, state_snapshot_repository) = create_state_snapshot_repository( + shards.as_slice(), + TEST_SNAPSHOT_REPOSITORY_CACHE_SIZE, + ); + + assert_eq!(shards.len(), file_io.list_shards().unwrap().len()); + assert_eq!(shards.len(), state_snapshot_repository.snapshot_history.len()); + assert_eq!(shards.len(), state_snapshot_repository.list_shards().unwrap().len()); + for states_per_shard in state_snapshot_repository.snapshot_history.values() { + assert_eq!(1, states_per_shard.len()); + } + for shard in shards { + assert!(state_snapshot_repository.load_latest(&shard).is_ok()); + assert!(state_snapshot_repository.shard_exists(&shard)); + } + } + + #[test] + fn update_latest_creates_new_state_file() { + let shards = + vec![ShardIdentifier::random(), ShardIdentifier::random(), ShardIdentifier::random()]; + let (file_io, mut state_snapshot_repository) = create_state_snapshot_repository( + shards.as_slice(), + TEST_SNAPSHOT_REPOSITORY_CACHE_SIZE, + ); + + let shard_to_update = shards.get(1).unwrap(); + assert_eq!(1, file_io.get_states_for_shard(shard_to_update).unwrap().len()); + + let new_state = TestState(1234u64); + + let _ = state_snapshot_repository + .update(shard_to_update, &new_state, Default::default()) + .unwrap(); + + let snapshot_history = + state_snapshot_repository.snapshot_history.get(shard_to_update).unwrap(); + assert_eq!(2, snapshot_history.len()); + assert_eq!(new_state, state_snapshot_repository.load_latest(shard_to_update).unwrap()); + assert_eq!(2, file_io.get_states_for_shard(shard_to_update).unwrap().len()); + } + + #[test] + fn update_latest_prunes_states_when_above_cache_size() { + let shard_id = ShardIdentifier::random(); + let (file_io, mut state_snapshot_repository) = + create_state_snapshot_repository(&[shard_id], TEST_SNAPSHOT_REPOSITORY_CACHE_SIZE); + + let states: Vec = + [1u64, 2u64, 3u64, 4u64, 5u64, 6u64].into_iter().map(|i| TestState(i)).collect(); + assert!(states.len() > TEST_SNAPSHOT_REPOSITORY_CACHE_SIZE); // ensures we have pruning + + states.iter().for_each(|state| { + let _ = state_snapshot_repository.update(&shard_id, state, Default::default()).unwrap(); + }); + + let snapshot_history = state_snapshot_repository.snapshot_history.get(&shard_id).unwrap(); + assert_eq!(TEST_SNAPSHOT_REPOSITORY_CACHE_SIZE, snapshot_history.len()); + assert_eq!( + *states.last().unwrap(), + state_snapshot_repository.load_latest(&shard_id).unwrap() + ); + assert_eq!( + TEST_SNAPSHOT_REPOSITORY_CACHE_SIZE, + file_io.get_states_for_shard(&shard_id).unwrap().len() + ); + } + + #[test] + fn update_latest_with_new_shard_creates_entry_and_does_not_modify_original_shard_entry() { + let shard_id = ShardIdentifier::random(); + let (file_io, mut state_snapshot_repository) = + create_state_snapshot_repository(&[shard_id], TEST_SNAPSHOT_REPOSITORY_CACHE_SIZE); + + assert!(state_snapshot_repository + .update(&ShardIdentifier::from_low_u64_be(1u64), &TestState(45), Default::default()) + .is_ok()); + + assert_eq!(2, state_snapshot_repository.snapshot_history.len()); + let snapshot_history = state_snapshot_repository.snapshot_history.get(&shard_id).unwrap(); + assert_eq!(1, snapshot_history.len()); + assert_eq!(TestState(0u64), state_snapshot_repository.load_latest(&shard_id).unwrap()); + assert_eq!(1, file_io.get_states_for_shard(&shard_id).unwrap().len()); + } + + #[test] + fn revert_to_removes_version_newer_than_target_hash() { + let shard_id = ShardIdentifier::random(); + let (file_io, mut state_snapshot_repository) = + create_state_snapshot_repository(&[shard_id], 6); + + let states: Vec = + [1u64, 2u64, 3u64, 4u64, 5u64].into_iter().map(|i| TestState(i)).collect(); + + let state_hashes = states + .iter() + .map(|state| { + let state_hash = state.hash(); + state_snapshot_repository.update(&shard_id, state, state_hash).unwrap(); + state_hash + }) + .collect::>(); + let revert_target_hash = state_hashes.get(1).unwrap(); + + let reverted_state = + state_snapshot_repository.revert_to(&shard_id, revert_target_hash).unwrap(); + + assert_eq!(TestState(2u64), reverted_state); + assert_eq!(3, state_snapshot_repository.snapshot_history.get(&shard_id).unwrap().len()); // because we have initialized version '0' as well + assert_eq!(TestState(2u64), state_snapshot_repository.load_latest(&shard_id).unwrap()); + assert_eq!(3, file_io.get_states_for_shard(&shard_id).unwrap().len()); + } + + #[test] + fn initializing_new_shard_works() { + let (_, mut state_snapshot_repository) = create_state_snapshot_repository(&[], 2); + + let shard_id = ShardIdentifier::random(); + + assert!(state_snapshot_repository.load_latest(&shard_id).is_err()); + assert!(state_snapshot_repository.list_shards().unwrap().is_empty()); + + let _hash = state_snapshot_repository + .initialize_new_shard(shard_id, &Default::default()) + .unwrap(); + + assert!(state_snapshot_repository.load_latest(&shard_id).is_ok()); + assert_eq!(1, state_snapshot_repository.list_shards().unwrap().len()); + } + + #[test] + fn initialize_new_state_when_shard_already_exists_returns_ok() { + let shard_id = ShardIdentifier::random(); + let (_, mut state_snapshot_repository) = create_state_snapshot_repository(&[shard_id], 2); + + let _hash = state_snapshot_repository + .initialize_new_shard(shard_id, &Default::default()) + .unwrap(); + + assert!(state_snapshot_repository.load_latest(&shard_id).is_ok()); + assert_eq!(1, state_snapshot_repository.list_shards().unwrap().len()); + } + + fn create_state_snapshot_repository( + shards: &[ShardIdentifier], + snapshot_history_size: usize, + ) -> (Arc, TestSnapshotRepository) { + let file_io = create_test_file_io(shards); + let state_initializer = Arc::new(TestStateInitializer::new(Default::default())); + let repository_loader = + StateSnapshotRepositoryLoader::new(file_io.clone(), state_initializer); + (file_io, repository_loader.load_snapshot_repository(snapshot_history_size).unwrap()) + } + + fn create_test_file_io(shards: &[ShardIdentifier]) -> Arc { + Arc::new(TestFileIo::new(shards, Box::new(|x| *x), Box::new(|x| x))) + } +} diff --git a/bitacross-worker/core-primitives/stf-state-handler/src/state_snapshot_repository_loader.rs b/bitacross-worker/core-primitives/stf-state-handler/src/state_snapshot_repository_loader.rs new file mode 100644 index 0000000000..88682efd74 --- /dev/null +++ b/bitacross-worker/core-primitives/stf-state-handler/src/state_snapshot_repository_loader.rs @@ -0,0 +1,221 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + error::Result, + file_io::StateFileIo, + state_initializer::InitializeState, + state_snapshot_primitives::{ + initialize_shard_with_snapshot, SnapshotHistory, StateId, StateSnapshotMetaData, + }, + state_snapshot_repository::StateSnapshotRepository, +}; +use itp_hashing::Hash; +use itp_types::ShardIdentifier; +use log::*; +use std::{collections::VecDeque, fmt::Debug, iter::FromIterator, sync::Arc, vec::Vec}; + +/// Loads a state snapshot repository from existing shards directory with state files. +pub struct StateSnapshotRepositoryLoader { + file_io: Arc, + state_initializer: Arc, +} + +impl StateSnapshotRepositoryLoader +where + FileIo: StateFileIo, + ::HashType: Copy + Eq + Debug, + ::StateType: Clone + Hash, + StateInitializer: InitializeState, +{ + pub fn new(file_io: Arc, state_initializer: Arc) -> Self { + Self { file_io, state_initializer } + } + + /// Load a state snapshot repository from an existing set of files and directories. + pub fn load_snapshot_repository( + &self, + snapshot_history_cache_size: usize, + ) -> Result> { + let snapshot_history = self.load_and_initialize_state_snapshot_history()?; + + StateSnapshotRepository::new( + self.file_io.clone(), + snapshot_history_cache_size, + snapshot_history, + ) + } + + fn load_and_initialize_state_snapshot_history( + &self, + ) -> Result> { + let mut repository = SnapshotHistory::new(); + + let shards = self.file_io.list_shards()?; + debug!("Found {} shard(s) to load state from", shards.len()); + + for shard in shards { + let mut state_ids = self.file_io.list_state_ids_for_shard(&shard)?; + // Sort by id (which are timestamp), highest, i.e. newest, first + state_ids.sort_unstable(); + state_ids.reverse(); + + let mut snapshot_metadata: Vec<_> = self.map_to_snapshot_metadata(&shard, state_ids); + + if snapshot_metadata.is_empty() { + warn!( + "No (valid) states found for shard {:?}, initializing empty shard state", + shard + ); + let initial_state = self.state_initializer.initialize()?; + let initial_snapshot_metadata = + initialize_shard_with_snapshot(&shard, self.file_io.as_ref(), &initial_state)?; + snapshot_metadata.push(initial_snapshot_metadata); + } else { + debug!( + "Found {} state snapshot(s) for shard {}, latest snapshot is {}", + snapshot_metadata.len(), + &shard, + snapshot_metadata.first().map(|f| f.state_id).unwrap_or_default() + ); + } + + let snapshot_history = VecDeque::from_iter(snapshot_metadata); + + repository.insert(shard, snapshot_history); + } + Ok(repository) + } + + fn map_to_snapshot_metadata( + &self, + shard: &ShardIdentifier, + state_ids: Vec, + ) -> Vec> { + state_ids + .into_iter() + .flat_map(|state_id| match self.file_io.compute_hash(shard, state_id) { + Ok(hash) => Some(StateSnapshotMetaData::new(hash, state_id)), + Err(e) => { + warn!( + "Failed to compute hash for state snapshot with id {}: {:?}, ignoring snapshot as a result", + state_id, e + ); + None + }, + }) + .collect() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + in_memory_state_file_io::InMemoryStateFileIo, + test::mocks::initialize_state_mock::InitializeStateMock, + }; + use codec::Encode; + use itp_types::H256; + use sp_core::blake2_256; + + #[derive(Encode, Clone, Default, Copy)] + struct TestState(pub u64); + + type TestStateHash = H256; + type TestFileIo = InMemoryStateFileIo; + type TestStateInitializer = InitializeStateMock; + type TestLoader = StateSnapshotRepositoryLoader; + + impl Hash for TestState { + fn hash(&self) -> TestStateHash { + blake2_256(&self.encode()).into() + } + } + + #[test] + fn loading_from_empty_shard_directories_initializes_files() { + let shards = + vec![ShardIdentifier::random(), ShardIdentifier::random(), ShardIdentifier::random()]; + let (_, loader) = create_test_fixtures(shards.as_slice()); + + let snapshot_history = loader.load_and_initialize_state_snapshot_history().unwrap(); + assert_eq!(shards.len(), snapshot_history.len()); + for snapshots in snapshot_history.values() { + assert_eq!(1, snapshots.len()); + } + } + + #[test] + fn loading_without_shards_returns_empty_directory() { + let (_, loader) = create_test_fixtures(&[]); + + let snapshot_history = loader.load_and_initialize_state_snapshot_history().unwrap(); + assert!(snapshot_history.is_empty()); + } + + #[test] + fn loading_from_files_orders_by_timestamp() { + let shards = + vec![ShardIdentifier::random(), ShardIdentifier::random(), ShardIdentifier::random()]; + let (file_io, loader) = create_test_fixtures(shards.as_slice()); + + add_state_snapshots( + file_io.as_ref(), + &shards[0], + &[1_000_000, 2_000_000, 3_000_000, 4_000_000], + ); + add_state_snapshots(file_io.as_ref(), &shards[1], &[10_000_000, 9_000_000]); + add_state_snapshots(file_io.as_ref(), &shards[2], &[14_000_000, 11_000_000, 12_000_000]); + + let snapshot_history = loader.load_and_initialize_state_snapshot_history().unwrap(); + + assert_eq!(shards.len(), snapshot_history.len()); + assert_latest_state_id(&snapshot_history, &shards[0], 4_000_000); + assert_latest_state_id(&snapshot_history, &shards[1], 10_000_000); + assert_latest_state_id(&snapshot_history, &shards[2], 14_000_000); + } + + fn add_state_snapshots(file_io: &TestFileIo, shard: &ShardIdentifier, state_ids: &[StateId]) { + for state_id in state_ids { + add_snapshot_with_state_ids(file_io, shard, *state_id); + } + } + + fn add_snapshot_with_state_ids( + file_io: &TestFileIo, + shard: &ShardIdentifier, + state_id: StateId, + ) { + file_io.initialize_shard(shard, state_id, &Default::default()).unwrap(); + } + + fn assert_latest_state_id( + snapshot_history: &SnapshotHistory, + shard: &ShardIdentifier, + state_id: StateId, + ) { + assert_eq!(snapshot_history.get(shard).unwrap().front().unwrap().state_id, state_id) + } + + fn create_test_fixtures(shards: &[ShardIdentifier]) -> (Arc, TestLoader) { + let file_io = Arc::new(TestFileIo::new(shards, Box::new(|x| *x), Box::new(|x| x))); + let state_initializer = Arc::new(TestStateInitializer::new(Default::default())); + let loader = StateSnapshotRepositoryLoader::new(file_io.clone(), state_initializer); + (file_io, loader) + } +} diff --git a/bitacross-worker/core-primitives/stf-state-handler/src/test/mocks/initialize_state_mock.rs b/bitacross-worker/core-primitives/stf-state-handler/src/test/mocks/initialize_state_mock.rs new file mode 100644 index 0000000000..32ed41e671 --- /dev/null +++ b/bitacross-worker/core-primitives/stf-state-handler/src/test/mocks/initialize_state_mock.rs @@ -0,0 +1,42 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{error::Result, state_initializer::InitializeState}; +use std::marker::PhantomData; + +/// Initialize state mock. +pub struct InitializeStateMock { + init_state: State, + _phantom: PhantomData, +} + +impl InitializeStateMock { + pub fn new(init_state: State) -> Self { + Self { init_state, _phantom: Default::default() } + } +} + +impl InitializeState for InitializeStateMock +where + State: Clone, +{ + type StateType = State; + + fn initialize(&self) -> Result { + Ok(self.init_state.clone()) + } +} diff --git a/bitacross-worker/core-primitives/stf-state-handler/src/test/mocks/mod.rs b/bitacross-worker/core-primitives/stf-state-handler/src/test/mocks/mod.rs new file mode 100644 index 0000000000..4a6fcfae26 --- /dev/null +++ b/bitacross-worker/core-primitives/stf-state-handler/src/test/mocks/mod.rs @@ -0,0 +1,20 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pub mod initialize_state_mock; +pub mod state_key_repository_mock; +pub mod versioned_state_access_mock; diff --git a/bitacross-worker/core-primitives/stf-state-handler/src/test/mocks/state_key_repository_mock.rs b/bitacross-worker/core-primitives/stf-state-handler/src/test/mocks/state_key_repository_mock.rs new file mode 100644 index 0000000000..443877083d --- /dev/null +++ b/bitacross-worker/core-primitives/stf-state-handler/src/test/mocks/state_key_repository_mock.rs @@ -0,0 +1,68 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(feature = "sgx")] +use std::sync::SgxRwLock as RwLock; + +#[cfg(feature = "std")] +use std::sync::RwLock; + +use itp_sgx_crypto::{ + error::Result, + key_repository::{AccessKey, MutateKey}, + StateCrypto, +}; + +#[derive(Default)] +pub struct StateKeyRepositoryMock +where + KeyType: StateCrypto + Clone + Default, +{ + key: RwLock, +} + +impl StateKeyRepositoryMock +where + KeyType: StateCrypto + Clone + Default, +{ + #[cfg(all(feature = "test", feature = "sgx"))] + pub fn new(key: KeyType) -> Self { + StateKeyRepositoryMock { key: RwLock::new(key) } + } +} + +impl AccessKey for StateKeyRepositoryMock +where + KeyType: StateCrypto + Clone + Default, +{ + type KeyType = KeyType; + + fn retrieve_key(&self) -> Result { + Ok(self.key.read().unwrap().clone()) + } +} + +impl MutateKey for StateKeyRepositoryMock +where + KeyType: StateCrypto + Clone + Default, +{ + fn update_key(&self, key: KeyType) -> Result<()> { + let mut lock = self.key.write().unwrap(); + *lock = key; + Ok(()) + } +} diff --git a/bitacross-worker/core-primitives/stf-state-handler/src/test/mocks/versioned_state_access_mock.rs b/bitacross-worker/core-primitives/stf-state-handler/src/test/mocks/versioned_state_access_mock.rs new file mode 100644 index 0000000000..f6dee1730b --- /dev/null +++ b/bitacross-worker/core-primitives/stf-state-handler/src/test/mocks/versioned_state_access_mock.rs @@ -0,0 +1,102 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + error::{Error, Result}, + state_snapshot_repository::VersionedStateAccess, +}; +use itp_types::ShardIdentifier; +use std::{ + collections::{HashMap, VecDeque}, + marker::PhantomData, + string::ToString, + vec::Vec, +}; + +#[derive(Default, Clone)] +pub struct VersionedStateAccessMock { + state_history: HashMap>, + phantom_data: PhantomData, +} + +impl VersionedStateAccessMock { + #[cfg(test)] + pub fn new(state_history: HashMap>) -> Self { + VersionedStateAccessMock { state_history, phantom_data: Default::default() } + } +} + +impl VersionedStateAccess for VersionedStateAccessMock +where + State: Default + Clone, + Hash: Default, +{ + type StateType = State; + type HashType = Hash; + + fn load_latest(&self, shard_identifier: &ShardIdentifier) -> Result { + self.state_history + .get(shard_identifier) + .ok_or(Error::InvalidShard(*shard_identifier))? + .front() + .cloned() + .ok_or(Error::StateNotFoundInRepository("".to_string())) + } + + fn update( + &mut self, + shard_identifier: &ShardIdentifier, + state: &Self::StateType, + _state_hash: Self::HashType, + ) -> Result<()> { + let state_history = self + .state_history + .entry(*shard_identifier) + .or_insert_with(|| VecDeque::default()); + state_history.push_front(state.clone()); + Ok(()) + } + + fn revert_to( + &mut self, + shard_identifier: &ShardIdentifier, + _state_hash: &Self::HashType, + ) -> Result { + let state_history = self + .state_history + .get_mut(shard_identifier) + .ok_or_else(|| Error::InvalidShard(*shard_identifier))?; + state_history.drain(..).last().ok_or(Error::EmptyRepository) + } + + fn initialize_new_shard( + &mut self, + shard_identifier: ShardIdentifier, + state: &Self::StateType, + ) -> Result { + self.state_history.insert(shard_identifier, VecDeque::from([state.clone()])); + Ok(Hash::default()) + } + + fn shard_exists(&self, shard_identifier: &ShardIdentifier) -> bool { + self.state_history.get(shard_identifier).is_some() + } + + fn list_shards(&self) -> Result> { + Ok(self.state_history.keys().copied().collect()) + } +} diff --git a/bitacross-worker/core-primitives/stf-state-handler/src/test/mod.rs b/bitacross-worker/core-primitives/stf-state-handler/src/test/mod.rs new file mode 100644 index 0000000000..e3552cd37f --- /dev/null +++ b/bitacross-worker/core-primitives/stf-state-handler/src/test/mod.rs @@ -0,0 +1,25 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(test)] +pub(crate) mod mocks; + +#[cfg(all(feature = "test", feature = "sgx"))] +pub mod mocks; + +#[cfg(all(feature = "test", feature = "sgx"))] +pub mod sgx_tests; diff --git a/bitacross-worker/core-primitives/stf-state-handler/src/test/sgx_tests.rs b/bitacross-worker/core-primitives/stf-state-handler/src/test/sgx_tests.rs new file mode 100644 index 0000000000..eef2da2b28 --- /dev/null +++ b/bitacross-worker/core-primitives/stf-state-handler/src/test/sgx_tests.rs @@ -0,0 +1,360 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + file_io::{sgx::SgxStateFileIo, StateDir, StateFileIo}, + handle_state::HandleState, + in_memory_state_file_io::sgx::create_in_memory_state_io_from_shards_directories, + query_shard_state::QueryShardState, + state_handler::StateHandler, + state_snapshot_repository::{StateSnapshotRepository, VersionedStateAccess}, + state_snapshot_repository_loader::StateSnapshotRepositoryLoader, + test::mocks::initialize_state_mock::InitializeStateMock, +}; +use codec::{Decode, Encode}; +use itp_hashing::Hash; +use itp_sgx_crypto::{ + get_aes_repository, + key_repository::{AccessKey, KeyRepository}, + Aes, AesSeal, StateCrypto, +}; +use itp_sgx_externalities::{SgxExternalities, SgxExternalitiesTrait, SgxExternalitiesType}; +use itp_sgx_io::write; +use itp_sgx_temp_dir::TempDir; +use itp_stf_state_observer::state_observer::StateObserver; +use itp_types::{ShardIdentifier, H256}; +use std::{sync::Arc, thread, vec::Vec}; + +const STATE_SNAPSHOTS_CACHE_SIZE: usize = 3; + +type StateKeyRepository = KeyRepository; +type TestStateInitializer = InitializeStateMock; +type TestStateFileIo = SgxStateFileIo; +type TestStateRepository = StateSnapshotRepository; +type TestStateRepositoryLoader = + StateSnapshotRepositoryLoader; +type TestStateObserver = StateObserver; +type TestStateHandler = StateHandler; + +// Fixme: Move this test to sgx-runtime: +// +// https://github.com/integritee-network/sgx-runtime/issues/23 +pub fn test_sgx_state_decode_encode_works() { + // given + let state = given_hello_world_state(); + + // when + let encoded_state = state.state.encode(); + let state2 = SgxExternalitiesType::decode(&mut encoded_state.as_slice()).unwrap(); + + // then + assert_eq!(state.state, state2); +} + +pub fn test_encrypt_decrypt_state_type_works() { + // given + let state = given_hello_world_state(); + let temp_dir = TempDir::with_prefix("test_encrypt_decrypt_state_type_works").unwrap(); + let state_key = get_aes_repository(temp_dir.path().to_path_buf()) + .unwrap() + .retrieve_key() + .unwrap(); + + // when + let mut state_buffer = state.state.encode(); + state_key.encrypt(&mut state_buffer).unwrap(); + + state_key.decrypt(&mut state_buffer).unwrap(); + let decoded = SgxExternalitiesType::decode(&mut state_buffer.as_slice()).unwrap(); + + // then + assert_eq!(state.state, decoded); +} + +pub fn test_write_and_load_state_works() { + // given + let shard: ShardIdentifier = [94u8; 32].into(); + let (_temp_dir, state_key_access, state_dir) = + test_setup("test_write_and_load_state_works", &shard); + + let state_handler = initialize_state_handler(state_key_access, state_dir); + + let state = given_hello_world_state(); + + // when + let (lock, _s) = state_handler.load_for_mutation(&shard).unwrap(); + let _hash = state_handler.write_after_mutation(state.clone(), lock, &shard).unwrap(); + + let (result_state, _) = state_handler.load_cloned(&shard).unwrap(); + + // then + assert_eq!(state.state, result_state.state); +} + +pub fn test_ensure_subsequent_state_loads_have_same_hash() { + // given + let shard: ShardIdentifier = [49u8; 32].into(); + let (_temp_dir, state_key_access, state_dir) = + test_setup("test_ensure_subsequent_state_loads_have_same_hash", &shard); + + let state_handler = initialize_state_handler(state_key_access, state_dir); + + let (lock, initial_state) = state_handler.load_for_mutation(&shard).unwrap(); + state_handler.write_after_mutation(initial_state.clone(), lock, &shard).unwrap(); + + let (_, loaded_state_hash) = state_handler.load_cloned(&shard).unwrap(); + + assert_eq!(initial_state.hash(), loaded_state_hash); +} + +pub fn test_write_access_locks_read_until_finished() { + // here we want to test that a lock we obtain for + // mutating state locks out any read attempt that happens during that time + + // given + let shard: ShardIdentifier = [47u8; 32].into(); + let (_temp_dir, state_key_access, state_dir) = + test_setup("test_write_access_locks_read_until_finished", &shard); + + let state_handler = initialize_state_handler(state_key_access, state_dir); + + let new_state_key = "my_new_state".encode(); + let (lock, mut state_to_mutate) = state_handler.load_for_mutation(&shard).unwrap(); + + // spawn a new thread that reads state + // this thread should be blocked until the write lock is released, i.e. until + // the new state is written. We can verify this, by trying to read that state variable + // that will be inserted further down below + let new_state_key_for_read = new_state_key.clone(); + let state_handler_clone = state_handler.clone(); + let shard_for_read = shard.clone(); + let join_handle = thread::spawn(move || { + let (state_to_read, _) = state_handler_clone.load_cloned(&shard_for_read).unwrap(); + assert!(state_to_read.get(new_state_key_for_read.as_slice()).is_some()); + }); + + assert!(state_to_mutate.get(new_state_key.clone().as_slice()).is_none()); + state_to_mutate.insert(new_state_key, "mega_secret_value".encode()); + + let _hash = state_handler.write_after_mutation(state_to_mutate, lock, &shard).unwrap(); + + join_handle.join().unwrap(); +} + +pub fn test_state_handler_file_backend_is_initialized() { + let shard: ShardIdentifier = [11u8; 32].into(); + let (_temp_dir, state_key_access, state_dir) = + test_setup("test_state_handler_file_backend_is_initialized", &shard); + + let state_handler = initialize_state_handler(state_key_access, state_dir.clone()); + + assert!(state_handler.shard_exists(&shard).unwrap()); + assert!(1 <= state_handler.list_shards().unwrap().len()); // only greater equal, because there might be other (non-test) shards present + assert_eq!(1, state_dir.list_state_ids_for_shard(&shard).unwrap().len()); // creates a first initialized file + + let _state = state_handler.load_cloned(&shard).unwrap(); + + assert_eq!(1, state_dir.list_state_ids_for_shard(&shard).unwrap().len()); +} + +pub fn test_multiple_state_updates_create_snapshots_up_to_cache_size() { + let shard: ShardIdentifier = [17u8; 32].into(); + let (_temp_dir, state_key_access, state_dir) = + test_setup("test_state_handler_file_backend_is_initialized", &shard); + + let state_handler = initialize_state_handler(state_key_access, state_dir.clone()); + + assert_eq!(1, state_dir.list_state_ids_for_shard(&shard).unwrap().len()); + + let hash_1 = update_state( + state_handler.as_ref(), + &shard, + ("my_key_1".encode(), "mega_secret_value".encode()), + ); + assert_eq!(2, state_dir.list_state_ids_for_shard(&shard).unwrap().len()); + + let hash_2 = update_state( + state_handler.as_ref(), + &shard, + ("my_key_2".encode(), "mega_secret_value222".encode()), + ); + assert_eq!(3, state_dir.list_state_ids_for_shard(&shard).unwrap().len()); + + let hash_3 = update_state( + state_handler.as_ref(), + &shard, + ("my_key_3".encode(), "mega_secret_value3".encode()), + ); + assert_eq!(3, state_dir.list_state_ids_for_shard(&shard).unwrap().len()); + + let hash_4 = update_state( + state_handler.as_ref(), + &shard, + ("my_key_3".encode(), "mega_secret_valuenot3".encode()), + ); + assert_eq!(3, state_dir.list_state_ids_for_shard(&shard).unwrap().len()); + + assert_ne!(hash_1, hash_2); + assert_ne!(hash_1, hash_3); + assert_ne!(hash_1, hash_4); + assert_ne!(hash_2, hash_3); + assert_ne!(hash_2, hash_4); + assert_ne!(hash_3, hash_4); + + assert_eq!( + STATE_SNAPSHOTS_CACHE_SIZE, + state_dir.list_state_ids_for_shard(&shard).unwrap().len() + ); +} + +pub fn test_file_io_get_state_hash_works() { + let shard: ShardIdentifier = [21u8; 32].into(); + let (_temp_dir, state_key_access, state_dir) = + test_setup("test_file_io_get_state_hash_works", &shard); + + let file_io = TestStateFileIo::new(state_key_access, state_dir); + + let state_id = 1234u128; + let state_hash = file_io + .initialize_shard(&shard, state_id, &SgxExternalities::new(Default::default())) + .unwrap(); + assert_eq!(state_hash, file_io.compute_hash(&shard, state_id).unwrap()); + + let state_hash = file_io.write(&shard, state_id, &given_hello_world_state()).unwrap(); + assert_eq!(state_hash, file_io.compute_hash(&shard, state_id).unwrap()); +} + +pub fn test_state_files_from_handler_can_be_loaded_again() { + let shard: ShardIdentifier = [15u8; 32].into(); + let (_temp_dir, state_key_access, state_dir) = + test_setup("test_state_files_from_handler_can_be_loaded_again", &shard); + + let state_handler = initialize_state_handler(state_key_access.clone(), state_dir.clone()); + + update_state(state_handler.as_ref(), &shard, ("test_key_1".encode(), "value1".encode())); + update_state(state_handler.as_ref(), &shard, ("test_key_2".encode(), "value2".encode())); + update_state( + state_handler.as_ref(), + &shard, + ("test_key_2".encode(), "value2_updated".encode()), + ); + update_state(state_handler.as_ref(), &shard, ("test_key_3".encode(), "value3".encode())); + + // We initialize another state handler to load the state from the changes we just made. + let updated_state_handler = initialize_state_handler(state_key_access, state_dir.clone()); + + assert_eq!( + STATE_SNAPSHOTS_CACHE_SIZE, + state_dir.list_state_ids_for_shard(&shard).unwrap().len() + ); + assert_eq!( + &"value3".encode(), + updated_state_handler + .load_cloned(&shard) + .unwrap() + .0 + .state() + .get("test_key_3".encode().as_slice()) + .unwrap() + ); +} + +pub fn test_list_state_ids_ignores_files_not_matching_the_pattern() { + let shard: ShardIdentifier = [21u8; 32].into(); + let (_temp_dir, state_key_access, state_dir) = + test_setup("test_list_state_ids_ignores_files_not_matching_the_pattern", &shard); + + let file_io = TestStateFileIo::new(state_key_access, state_dir.clone()); + + let invalid_state_file_path = state_dir.shard_path(&shard).join("invalid-state.bin"); + write(&[0, 1, 2, 3, 4, 5], invalid_state_file_path).unwrap(); + + file_io + .initialize_shard(&shard, 1234, &SgxExternalities::new(Default::default())) + .unwrap(); + + assert_eq!(1, file_io.list_state_ids_for_shard(&shard).unwrap().len()); +} + +pub fn test_in_memory_state_initializes_from_shard_directory() { + let shard: ShardIdentifier = [45u8; 32].into(); + let (_temp_dir, _, state_dir) = + test_setup("test_list_state_ids_ignores_files_not_matching_the_pattern", &shard); + + let file_io = + create_in_memory_state_io_from_shards_directories(&state_dir.shards_directory()).unwrap(); + let state_initializer = + Arc::new(TestStateInitializer::new(SgxExternalities::new(Default::default()))); + let state_repository_loader = + StateSnapshotRepositoryLoader::new(file_io.clone(), state_initializer); + let state_snapshot_repository = state_repository_loader + .load_snapshot_repository(STATE_SNAPSHOTS_CACHE_SIZE) + .unwrap(); + + assert_eq!(1, file_io.get_states_for_shard(&shard).unwrap().len()); + assert!(state_snapshot_repository.shard_exists(&shard)); +} + +fn initialize_state_handler( + state_key_access: Arc, + state_dir: StateDir, +) -> Arc { + let file_io = Arc::new(TestStateFileIo::new(state_key_access, state_dir)); + let state_initializer = + Arc::new(TestStateInitializer::new(SgxExternalities::new(Default::default()))); + let state_repository_loader = + TestStateRepositoryLoader::new(file_io, state_initializer.clone()); + let state_observer = Arc::new(TestStateObserver::default()); + let state_snapshot_repository = state_repository_loader + .load_snapshot_repository(STATE_SNAPSHOTS_CACHE_SIZE) + .unwrap(); + Arc::new( + TestStateHandler::load_from_repository( + state_snapshot_repository, + state_observer, + state_initializer, + ) + .unwrap(), + ) +} + +fn update_state( + state_handler: &TestStateHandler, + shard: &ShardIdentifier, + kv_pair: (Vec, Vec), +) -> H256 { + let (lock, mut state_to_mutate) = state_handler.load_for_mutation(shard).unwrap(); + state_to_mutate.insert(kv_pair.0, kv_pair.1); + state_handler.write_after_mutation(state_to_mutate, lock, shard).unwrap() +} + +fn given_hello_world_state() -> SgxExternalities { + let key: Vec = "hello".encode(); + let value: Vec = "world".encode(); + let mut state = SgxExternalities::new(Default::default()); + state.insert(key, value); + state +} + +fn test_setup(id: &str, shard: &ShardIdentifier) -> (TempDir, Arc, StateDir) { + let temp_dir = TempDir::with_prefix(id).unwrap(); + let state_key_access = Arc::new(get_aes_repository(temp_dir.path().to_path_buf()).unwrap()); + let state_dir = StateDir::new(temp_dir.path().to_path_buf()); + state_dir.given_initialized_shard(shard); + + (temp_dir, state_key_access, state_dir) +} diff --git a/bitacross-worker/core-primitives/stf-state-observer/Cargo.toml b/bitacross-worker/core-primitives/stf-state-observer/Cargo.toml new file mode 100644 index 0000000000..d2c0016793 --- /dev/null +++ b/bitacross-worker/core-primitives/stf-state-observer/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "itp-stf-state-observer" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } + +# local deps +itp-types = { default-features = false, path = "../types" } + +# sgx enabled external libraries +thiserror_sgx = { package = "thiserror", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3", optional = true } + +# std compatible external libraries (make sure these versions match with the sgx-enabled ones above) +log = { version = "0.4", default-features = false } +thiserror = { version = "1.0", optional = true } + +[features] +default = ["std"] +std = [ + "itp-types/std", + "log/std", + "thiserror", +] +sgx = [ + "sgx_tstd", + "thiserror_sgx", +] +mocks = [] diff --git a/bitacross-worker/core-primitives/stf-state-observer/src/error.rs b/bitacross-worker/core-primitives/stf-state-observer/src/error.rs new file mode 100644 index 0000000000..914552fb86 --- /dev/null +++ b/bitacross-worker/core-primitives/stf-state-observer/src/error.rs @@ -0,0 +1,34 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +pub type Result = core::result::Result; + +use std::boxed::Box; + +/// State Observer Error. +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("Current state is empty (not set)")] + CurrentStateEmpty, + #[error("Could not acquire lock, lock is poisoned")] + LockPoisoning, + #[error(transparent)] + Other(#[from] Box), +} diff --git a/bitacross-worker/core-primitives/stf-state-observer/src/lib.rs b/bitacross-worker/core-primitives/stf-state-observer/src/lib.rs new file mode 100644 index 0000000000..5da2bbbed9 --- /dev/null +++ b/bitacross-worker/core-primitives/stf-state-observer/src/lib.rs @@ -0,0 +1,38 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(test, feature(assert_matches))] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +// Re-export module to properly feature gate sgx and regular std environment. +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod sgx_reexport_prelude { + pub use thiserror_sgx as thiserror; +} + +pub mod error; +pub mod state_observer; +pub mod traits; + +#[cfg(feature = "mocks")] +pub mod mock; diff --git a/bitacross-worker/core-primitives/stf-state-observer/src/mock.rs b/bitacross-worker/core-primitives/stf-state-observer/src/mock.rs new file mode 100644 index 0000000000..335adf7b91 --- /dev/null +++ b/bitacross-worker/core-primitives/stf-state-observer/src/mock.rs @@ -0,0 +1,79 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(feature = "sgx")] +use std::sync::SgxRwLock as RwLock; + +#[cfg(feature = "std")] +use std::sync::RwLock; + +use crate::{ + error::{Error, Result}, + traits::{ObserveState, UpdateState}, +}; +use core::fmt::Debug; +use itp_types::ShardIdentifier; +use log::*; +use std::vec::Vec; + +/// Observe state mock. +#[derive(Default)] +pub struct ObserveStateMock { + state: RwLock>, +} + +impl ObserveStateMock { + pub fn new(state: StateType) -> Self { + Self { state: RwLock::new(Some(state)) } + } +} + +impl ObserveState for ObserveStateMock +where + StateType: Debug, +{ + type StateType = StateType; + + fn observe_state(&self, _shard: &ShardIdentifier, observation_func: F) -> Result + where + F: FnOnce(&mut Self::StateType) -> R, + { + let mut maybe_state_lock = self.state.write().unwrap(); + + match &mut *maybe_state_lock { + Some(state) => { + debug!("State value: {:?}", state); + Ok(observation_func(state)) + }, + None => Err(Error::CurrentStateEmpty), + } + } +} + +/// Update state mock. +#[derive(Default)] +pub struct UpdateStateMock { + pub queued_updates: RwLock>, +} + +impl UpdateState for UpdateStateMock { + fn queue_state_update(&self, shard: ShardIdentifier, state: StateType) -> Result<()> { + let mut updates_lock = self.queued_updates.write().unwrap(); + updates_lock.push((shard, state)); + Ok(()) + } +} diff --git a/bitacross-worker/core-primitives/stf-state-observer/src/state_observer.rs b/bitacross-worker/core-primitives/stf-state-observer/src/state_observer.rs new file mode 100644 index 0000000000..21c8042ac0 --- /dev/null +++ b/bitacross-worker/core-primitives/stf-state-observer/src/state_observer.rs @@ -0,0 +1,148 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(feature = "sgx")] +use std::sync::SgxRwLock as RwLock; + +#[cfg(feature = "std")] +use std::sync::RwLock; + +use crate::{ + error::{Error, Result}, + traits::{ObserveState, UpdateState}, +}; +use itp_types::ShardIdentifier; +use std::{collections::HashMap, vec::Vec}; + +/// State observer implementation. Receives updates in a dedicated queue. +/// These updates are applied every time an observation function is executed. +/// +#[derive(Default)] +pub struct StateObserver { + queued_state_updates: RwLock>, + current_state: RwLock>, +} + +impl StateObserver { + pub fn new(shard: ShardIdentifier, state: StateType) -> Self { + Self { + queued_state_updates: Default::default(), + current_state: RwLock::new(HashMap::from([(shard, state)])), + } + } + + pub fn from_map(states_map: HashMap) -> Self { + Self { queued_state_updates: Default::default(), current_state: RwLock::new(states_map) } + } + + fn apply_pending_update(&self) -> Result<()> { + let mut update_queue_lock = + self.queued_state_updates.write().map_err(|_| Error::LockPoisoning)?; + + let state_updates: Vec<_> = update_queue_lock.drain().collect(); + drop(update_queue_lock); + + if !state_updates.is_empty() { + let mut current_state_lock = + self.current_state.write().map_err(|_| Error::LockPoisoning)?; + for state_update in state_updates.into_iter() { + current_state_lock.insert(state_update.0, state_update.1); + } + drop(current_state_lock); + } + + Ok(()) + } +} + +impl ObserveState for StateObserver { + type StateType = StateType; + + fn observe_state(&self, shard: &ShardIdentifier, observation_func: F) -> Result + where + F: FnOnce(&mut Self::StateType) -> R, + { + // Check if there is a pending update and apply it. + self.apply_pending_update()?; + + // Execute the observation function. + let mut current_state_map_lock = + self.current_state.write().map_err(|_| Error::LockPoisoning)?; + + match current_state_map_lock.get_mut(shard) { + Some(s) => Ok(observation_func(s)), + None => Err(Error::CurrentStateEmpty), + } + } +} + +impl UpdateState for StateObserver { + fn queue_state_update(&self, shard: ShardIdentifier, state: StateType) -> Result<()> { + let mut update_queue_lock = + self.queued_state_updates.write().map_err(|_| Error::LockPoisoning)?; + update_queue_lock.insert(shard, state); + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use core::assert_matches::assert_matches; + + type TestState = u64; + + #[test] + fn default_constructs_empty_state() { + let state_observer = StateObserver::::default(); + + assert_matches!( + state_observer.observe_state(&shard(), |_| { () }), + Err(Error::CurrentStateEmpty) + ); + } + + #[test] + fn initializing_state_with_some_works() { + let state_observer = StateObserver::::new(shard(), 31u64); + assert_eq!(state_observer.observe_state(&shard(), |s| *s).unwrap(), 31u64); + } + + #[test] + fn observing_multiple_times_after_update_works() { + let state_observer = StateObserver::::default(); + + state_observer.queue_state_update(shard(), 42u64).unwrap(); + + assert_eq!(state_observer.observe_state(&shard(), |s| *s).unwrap(), 42u64); + assert_eq!(state_observer.observe_state(&shard(), |s| *s).unwrap(), 42u64); + assert_eq!(state_observer.observe_state(&shard(), |s| *s).unwrap(), 42u64); + } + + #[test] + fn updating_multiple_times_before_observation_just_keeps_last_value() { + let state_observer = StateObserver::::new(shard(), 31); + state_observer.queue_state_update(shard(), 42u64).unwrap(); + state_observer.queue_state_update(shard(), 57u64).unwrap(); + assert_eq!(1, state_observer.queued_state_updates.read().unwrap().len()); + assert_eq!(state_observer.observe_state(&shard(), |s| *s).unwrap(), 57u64); + } + + fn shard() -> ShardIdentifier { + ShardIdentifier::default() + } +} diff --git a/bitacross-worker/core-primitives/stf-state-observer/src/traits.rs b/bitacross-worker/core-primitives/stf-state-observer/src/traits.rs new file mode 100644 index 0000000000..617e50dab4 --- /dev/null +++ b/bitacross-worker/core-primitives/stf-state-observer/src/traits.rs @@ -0,0 +1,37 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::error::Result; +use itp_types::ShardIdentifier; + +/// Observe state trait. +pub trait ObserveState { + type StateType; + + /// Requires a &mut StateType because the externalities are always executed with a mutable reference. + /// Underneath it all, the environmental!() macro only knows mutable access unfortunately. + /// And since the sp-io interface is fixed and relies on the global instance created by environmental!(), + /// it forces &mut access upon us here, even though read-only access would be enough. + fn observe_state(&self, shard: &ShardIdentifier, observation_func: F) -> Result + where + F: FnOnce(&mut Self::StateType) -> R; +} + +/// Trait to queue a state update for an observer. +pub trait UpdateState { + fn queue_state_update(&self, shard: ShardIdentifier, state: StateType) -> Result<()>; +} diff --git a/bitacross-worker/core-primitives/storage/Cargo.toml b/bitacross-worker/core-primitives/storage/Cargo.toml new file mode 100644 index 0000000000..fb60cd1fa1 --- /dev/null +++ b/bitacross-worker/core-primitives/storage/Cargo.toml @@ -0,0 +1,49 @@ +[package] +name = "itp-storage" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["chain-error"] } +derive_more = { version = "0.99.5" } +frame-metadata = { version = "15.1.0", features = ["v14"], default-features = false } +hash-db = { version = "0.15.2", default-features = false } +thiserror = { version = "1.0.26", optional = true } + +# sgx deps +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } +thiserror-sgx = { package = "thiserror", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3", optional = true } + +# substrate deps +frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-core = { default-features = false, features = ["full_crypto"], git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-std = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-trie = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# integritee +itp-types = { default-features = false, path = "../types" } + +[dev-dependencies] +sp-state-machine = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-metadata/std", + "frame-support/std", + "hash-db/std", + "itp-types/std", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", + "sp-trie/std", + "thiserror", +] +sgx = [ + "sgx_tstd", + "thiserror-sgx", +] +test = [] diff --git a/bitacross-worker/core-primitives/storage/src/error.rs b/bitacross-worker/core-primitives/storage/src/error.rs new file mode 100644 index 0000000000..9b859bfb8f --- /dev/null +++ b/bitacross-worker/core-primitives/storage/src/error.rs @@ -0,0 +1,43 @@ +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use thiserror_sgx as thiserror; + +// error with std::error::Error implemented for std and sgx +#[derive(Debug, PartialEq, Eq, thiserror::Error)] +#[cfg(any(feature = "std", feature = "sgx"))] +pub enum Error { + #[error("No storage proof supplied")] + NoProofSupplied, + #[error("Supplied storage value does not match the value from the proof")] + WrongValue, + #[error("Invalid storage proof: StorageRootMismatch")] + StorageRootMismatch, + #[error("Storage value unavailable")] + StorageValueUnavailable, + #[error(transparent)] + #[cfg(feature = "std")] + Codec(#[from] codec::Error), + + // as `codec::Error` does not implement `std::error::Error` in `no-std`, + // we can't use the `#[from]` attribute. + #[error("Codec: {0}")] + #[cfg(not(feature = "std"))] + Codec(codec::Error), +} + +// error for bare `no_std`, which does not implement `std::error::Error` + +#[cfg(all(not(feature = "std"), not(feature = "sgx")))] +use derive_more::{Display, From}; + +// Simple error enum for no_std without std::error::Error implemented +#[derive(Debug, Display, PartialEq, Eq, From)] +#[cfg(all(not(feature = "std"), not(feature = "sgx")))] +pub enum Error { + NoProofSupplied, + /// Supplied storage value does not match the value from the proof + WrongValue, + /// InvalidStorageProof, + StorageRootMismatch, + StorageValueUnavailable, + Codec(codec::Error), +} diff --git a/bitacross-worker/core-primitives/storage/src/keys.rs b/bitacross-worker/core-primitives/storage/src/keys.rs new file mode 100644 index 0000000000..43de4f667e --- /dev/null +++ b/bitacross-worker/core-primitives/storage/src/keys.rs @@ -0,0 +1,71 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use codec::Encode; +use frame_metadata::v14::StorageHasher; +use sp_std::vec::Vec; + +pub fn storage_value_key(module_prefix: &str, storage_prefix: &str) -> Vec { + let mut bytes = sp_core::twox_128(module_prefix.as_bytes()).to_vec(); + bytes.extend(&sp_core::twox_128(storage_prefix.as_bytes())[..]); + bytes +} + +pub fn storage_map_key( + module_prefix: &str, + storage_prefix: &str, + mapkey1: &K, + hasher1: &StorageHasher, +) -> Vec { + let mut bytes = sp_core::twox_128(module_prefix.as_bytes()).to_vec(); + bytes.extend(&sp_core::twox_128(storage_prefix.as_bytes())[..]); + bytes.extend(key_hash(mapkey1, hasher1)); + bytes +} + +pub fn storage_double_map_key( + module_prefix: &str, + storage_prefix: &str, + mapkey1: &K, + hasher1: &StorageHasher, + mapkey2: &Q, + hasher2: &StorageHasher, +) -> Vec { + let mut bytes = sp_core::twox_128(module_prefix.as_bytes()).to_vec(); + bytes.extend(&sp_core::twox_128(storage_prefix.as_bytes())[..]); + bytes.extend(key_hash(mapkey1, hasher1)); + bytes.extend(key_hash(mapkey2, hasher2)); + bytes +} + +/// generates the key's hash depending on the StorageHasher selected +fn key_hash(key: &K, hasher: &StorageHasher) -> Vec { + let encoded_key = key.encode(); + match hasher { + StorageHasher::Identity => encoded_key.to_vec(), + StorageHasher::Blake2_128 => sp_core::blake2_128(&encoded_key).to_vec(), + StorageHasher::Blake2_128Concat => { + // copied from substrate Blake2_128Concat::hash since StorageHasher is not public + let x: &[u8] = encoded_key.as_slice(); + sp_core::blake2_128(x).iter().chain(x.iter()).cloned().collect::>() + }, + StorageHasher::Blake2_256 => sp_core::blake2_256(&encoded_key).to_vec(), + StorageHasher::Twox128 => sp_core::twox_128(&encoded_key).to_vec(), + StorageHasher::Twox256 => sp_core::twox_256(&encoded_key).to_vec(), + StorageHasher::Twox64Concat => sp_core::twox_64(&encoded_key).to_vec(), + } +} diff --git a/bitacross-worker/core-primitives/storage/src/lib.rs b/bitacross-worker/core-primitives/storage/src/lib.rs new file mode 100644 index 0000000000..3a3b6f2a6d --- /dev/null +++ b/bitacross-worker/core-primitives/storage/src/lib.rs @@ -0,0 +1,35 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +pub use error::Error; +pub use frame_metadata::v14::StorageHasher; +pub use keys::*; +pub use proof::*; +pub use verify_storage_proof::*; + +pub mod error; +pub mod keys; +pub mod proof; +pub mod verify_storage_proof; diff --git a/bitacross-worker/core-primitives/storage/src/proof.rs b/bitacross-worker/core-primitives/storage/src/proof.rs new file mode 100644 index 0000000000..6b2c02c49f --- /dev/null +++ b/bitacross-worker/core-primitives/storage/src/proof.rs @@ -0,0 +1,121 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Logic for checking Substrate storage proofs. + +use crate::error::Error; +use hash_db::EMPTY_PREFIX; +use sp_core::Hasher; +use sp_std::vec::Vec; +use sp_trie::{trie_types::TrieDB, HashDBT, MemoryDB, Trie, TrieDBBuilder}; + +pub type StorageProof = Vec>; + +/// This struct is used to read storage values from a subset of a Merklized database. The "proof" +/// is a subset of the nodes in the Merkle structure of the database, so that it provides +/// authentication against a known Merkle root as well as the values in the database themselves. +pub struct StorageProofChecker { + root: H::Out, + db: MemoryDB, +} + +impl StorageProofChecker { + /// Constructs a new storage proof checker. + /// + /// This returns an error if the given proof is invalid with respect to the given root. + pub fn new(root: H::Out, proof: StorageProof) -> Result { + let mut db = MemoryDB::default(); + for item in proof { + db.insert(EMPTY_PREFIX, &item); + } + let checker = StorageProofChecker { root, db }; + // Return error if trie would be invalid. + let _ = checker.trie()?; + Ok(checker) + } + + /// Reads a value from the available subset of storage. If the value cannot be read due to an + /// incomplete or otherwise invalid proof, this returns an error. + pub fn read_value(&self, key: &[u8]) -> Result>, Error> { + self.trie()? + .get(key) + .map(|value| value.map(|value| value.to_vec())) + .map_err(|_| Error::StorageValueUnavailable) + } + + fn trie(&self) -> Result, Error> { + if !self.db.contains(&self.root, EMPTY_PREFIX) { + Err(Error::StorageRootMismatch) + } else { + Ok(TrieDBBuilder::new(&self.db, &self.root).build()) + } + } + + pub fn check_proof( + root: H::Out, + storage_key: &[u8], + proof: StorageProof, + ) -> Result>, Error> { + let storage_checker = StorageProofChecker::::new(root, proof)?; + + storage_checker.read_value(storage_key) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use sp_core::{Blake2Hasher, H256}; + use sp_state_machine::{backend::Backend, new_in_mem, prove_read}; + use sp_trie::HashKey; + + #[test] + fn storage_proof_check() { + // construct storage proof + let mut backend = new_in_mem::>(); + backend.insert( + vec![ + (None, vec![(b"key1".to_vec(), Some(b"value1".to_vec()))]), + (None, vec![(b"key2".to_vec(), Some(b"value2".to_vec()))]), + (None, vec![(b"key3".to_vec(), Some(b"value3".to_vec()))]), + // Value is too big to fit in a branch node + (None, vec![(b"key11".to_vec(), Some(vec![0u8; 32]))]), + ], + Default::default(), + ); + let root = backend.storage_root(std::iter::empty(), Default::default()).0; + let proof: StorageProof = prove_read(backend, &[&b"key1"[..], &b"key2"[..], &b"key22"[..]]) + .unwrap() + .iter_nodes() + .cloned() + .collect(); + + // check proof in runtime + let checker = >::new(root, proof.clone()).unwrap(); + assert_eq!(checker.read_value(b"key1"), Ok(Some(b"value1".to_vec()))); + assert_eq!(checker.read_value(b"key2"), Ok(Some(b"value2".to_vec()))); + assert_eq!(checker.read_value(b"key11111"), Err(Error::StorageValueUnavailable)); + assert_eq!(checker.read_value(b"key22"), Ok(None)); + + // checking proof against invalid commitment fails + assert_eq!( + >::new(H256::random(), proof).err(), + Some(Error::StorageRootMismatch) + ); + } +} diff --git a/bitacross-worker/core-primitives/storage/src/verify_storage_proof.rs b/bitacross-worker/core-primitives/storage/src/verify_storage_proof.rs new file mode 100644 index 0000000000..fab9fda455 --- /dev/null +++ b/bitacross-worker/core-primitives/storage/src/verify_storage_proof.rs @@ -0,0 +1,67 @@ +use crate::{error::Error, StorageProofChecker}; +use codec::Decode; +use frame_support::ensure; +use itp_types::storage::{StorageEntry, StorageEntryVerified}; +use sp_runtime::traits::Header as HeaderT; +use sp_std::prelude::Vec; + +pub trait VerifyStorageProof { + fn verify_storage_proof( + self, + header: &Header, + ) -> Result, Error>; +} + +impl VerifyStorageProof for StorageEntry> { + fn verify_storage_proof( + self, + header: &Header, + ) -> Result, Error> { + let proof = self.proof.as_ref().ok_or(Error::NoProofSupplied)?; + let actual = StorageProofChecker::<
::Hashing>::check_proof( + *header.state_root(), + &self.key, + proof.to_vec(), + )?; + + // Todo: Why do they do it like that, we could supply the proof only and get the value from the proof directly?? + ensure!(actual == self.value, Error::WrongValue); + + Ok(StorageEntryVerified { + key: self.key, + value: self + .value + .map(|v| Decode::decode(&mut v.as_slice())) + .transpose() + .map_err(Error::Codec)?, + }) + } +} + +/// Verify a set of storage entries +pub fn verify_storage_entries( + entries: impl IntoIterator, + header: &Header, +) -> Result>, Error> +where + S: Into>>, + Header: HeaderT, + V: Decode, +{ + let iter = into_storage_entry_iter(entries); + let mut verified_entries = Vec::with_capacity(iter.size_hint().0); + + for e in iter { + verified_entries.push(e.verify_storage_proof(header)?); + } + Ok(verified_entries) +} + +pub fn into_storage_entry_iter<'a, S>( + source: impl IntoIterator + 'a, +) -> impl Iterator>> + 'a +where + S: Into>>, +{ + source.into_iter().map(|s| s.into()) +} diff --git a/bitacross-worker/core-primitives/substrate-sgx/environmental/Cargo.toml b/bitacross-worker/core-primitives/substrate-sgx/environmental/Cargo.toml new file mode 100644 index 0000000000..354aa878a6 --- /dev/null +++ b/bitacross-worker/core-primitives/substrate-sgx/environmental/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "environmental" +description = "Set scope-limited values can can be accessed statically" +version = "1.1.3" +authors = ["Parity Technologies "] +license = "Apache-2.0" +edition = "2021" + +[dependencies] +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true, features = ["thread"] } + +[features] +default = ["std"] +std = [] +sgx = ["sgx_tstd"] diff --git a/bitacross-worker/core-primitives/substrate-sgx/environmental/src/lib.rs b/bitacross-worker/core-primitives/substrate-sgx/environmental/src/lib.rs new file mode 100644 index 0000000000..7671299615 --- /dev/null +++ b/bitacross-worker/core-primitives/substrate-sgx/environmental/src/lib.rs @@ -0,0 +1,479 @@ +// Copyright 2017-2020 Parity Technologies +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Safe global references to stack variables. +//! +//! Set up a global reference with environmental! macro giving it a name and type. +//! Use the `using` function scoped under its name to name a reference and call a function that +//! takes no parameters yet can access said reference through the similarly placed `with` function. +//! +//! # Examples +//! +//! ``` +//! #[macro_use] extern crate environmental; +//! // create a place for the global reference to exist. +//! environmental!(counter: u32); +//! fn stuff() { +//! // do some stuff, accessing the named reference as desired. +//! counter::with(|i| *i += 1); +//! } +//! fn main() { +//! // declare a stack variable of the same type as our global declaration. +//! let mut counter_value = 41u32; +//! // call stuff, setting up our `counter` environment as a reference to our counter_value var. +//! counter::using(&mut counter_value, stuff); +//! println!("The answer is {:?}", counter_value); // will print 42! +//! stuff(); // safe! doesn't do anything. +//! } +//! ``` +//! +//! Original crate: https://github.com/paritytech/environmental/blob/master/src/lib.rs +//! The original crate does not support multithreading in `no_std` mode, see https://github.com/integritee-network/worker/issues/803. +//! Therefore, this crate introduces the sgx feature, which allows multithreading within an sgx enabled environment. +//! It should be ensured that all uses of the environmental crate within the enclave are making use of this crate, not the original one. +//! +//! Attention: The `sp-runtime-interface` still points to the original environmental crate. It can't be easily patched due +//! to this crate not being `no_std` compatible. (See https://github.com/integritee-network/worker/pull/938#discussion_r952412587). +//! However, because `sp-runtime-interface` only uses environmental in `std` mode, it should be safe to leave as is. +//! Nonetheless, it should be kept in mind that this may cause a problem in the future. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), not(feature = "sgx")))] +compile_error!("Either feature \"std\" or feature \"sgx\" must be enabled"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +extern crate alloc; + +#[doc(hidden)] +pub use core::{ + cell::RefCell, + marker::PhantomData, + mem::{replace, transmute}, +}; + +#[doc(hidden)] +pub use alloc::{rc::Rc, vec::Vec}; + +#[doc(hidden)] +pub use std::thread::LocalKey; + +#[doc(hidden)] +#[macro_export] +macro_rules! thread_local_impl { + ($(#[$attr:meta])* static $name:ident: $t:ty = $init:expr) => ( + use std::thread_local; + thread_local!($(#[$attr])* static $name: $t = $init); + ); +} + +/// The global inner that stores the stack of globals. +#[doc(hidden)] +pub type GlobalInner = RefCell>>>; + +/// The global type. +type Global = LocalKey>; + +#[doc(hidden)] +pub fn using R>( + global: &'static Global, + protected: &mut T, + f: F, +) -> R { + // store the `protected` reference as a pointer so we can provide it to logic running within + // `f`. + // while we record this pointer (while it's non-zero) we guarantee: + // - it will only be used once at any time (no reentrancy); + // - that no other thread will use it; and + // - that we do not use the original mutating reference while the pointer. + // exists. + global.with(|r| { + // Push the new global to the end of the stack. + r.borrow_mut().push(Rc::new(RefCell::new(protected as _))); + + // Even if `f` panics the added global will be popped. + struct PopGlobal<'a, T: 'a + ?Sized> { + global_stack: &'a GlobalInner, + } + + impl<'a, T: 'a + ?Sized> Drop for PopGlobal<'a, T> { + fn drop(&mut self) { + self.global_stack.borrow_mut().pop(); + } + } + + let _guard = PopGlobal { global_stack: r }; + + f() + }) +} + +#[doc(hidden)] +pub fn with R>( + global: &'static Global, + mutator: F, +) -> Option { + global.with(|r| { + // We always use the `last` element when we want to access the + // currently set global. + let last = r.borrow().last().cloned(); + last.map(|ptr| + // safe because it's only non-zero when it's being called from using, which + // is holding on to the underlying reference (and not using it itself) safely. + unsafe { + mutator(&mut **ptr.borrow_mut()) + }) + }) +} + +/// Declare a new global reference module whose underlying value does not contain references. +/// +/// Will create a module of a given name that contains two functions: +/// +/// * `pub fn using R>(protected: &mut $t, f: F) -> R` +/// This executes `f`, returning its value. During the call, the module's reference is set to +/// be equal to `protected`. +/// * `pub fn with R>(f: F) -> Option` +/// This executes `f`, returning `Some` of its value if called from code that is being executed +/// as part of a `using` call. If not, it returns `None`. `f` is provided with one argument: the +/// same reference as provided to the most recent `using` call. +/// +/// # Examples +/// +/// Initializing the global context with a given value. +/// +/// ```rust +/// #[macro_use] extern crate environmental; +/// environmental!(counter: u32); +/// fn main() { +/// let mut counter_value = 41u32; +/// counter::using(&mut counter_value, || { +/// let odd = counter::with(|value| +/// if *value % 2 == 1 { +/// *value += 1; true +/// } else { +/// *value -= 3; false +/// }).unwrap(); // safe because we're inside a counter::using +/// println!("counter was {}", match odd { true => "odd", _ => "even" }); +/// }); +/// +/// println!("The answer is {:?}", counter_value); // 42 +/// } +/// ``` +/// +/// Roughly the same, but with a trait object: +/// +/// ```rust +/// #[macro_use] extern crate environmental; +/// +/// trait Increment { fn increment(&mut self); } +/// +/// impl Increment for i32 { +/// fn increment(&mut self) { *self += 1 } +/// } +/// +/// environmental!(val: dyn Increment + 'static); +/// +/// fn main() { +/// let mut local = 0i32; +/// val::using(&mut local, || { +/// val::with(|v| for _ in 0..5 { v.increment() }); +/// }); +/// +/// assert_eq!(local, 5); +/// } +/// ``` +#[macro_export] +macro_rules! environmental { + ($name:ident : $t:ty) => { + #[allow(non_camel_case_types)] + struct $name { __private_field: () } + + $crate::thread_local_impl! { + static GLOBAL: $crate::GlobalInner<$t> = Default::default() + } + + impl $name { + #[allow(unused_imports)] + + pub fn using R>( + protected: &mut $t, + f: F + ) -> R { + $crate::using(&GLOBAL, protected, f) + } + + pub fn with R>( + f: F + ) -> Option { + $crate::with(&GLOBAL, |x| f(x)) + } + } + }; + ($name:ident : trait @$t:ident [$($args:ty,)*]) => { + #[allow(non_camel_case_types, dead_code)] + struct $name { __private_field: () } + + $crate::thread_local_impl! { + static GLOBAL: $crate::GlobalInner<(dyn $t<$($args),*> + 'static)> + = Default::default() + } + + impl $name { + #[allow(unused_imports)] + + pub fn using R>( + protected: &mut dyn $t<$($args),*>, + f: F + ) -> R { + let lifetime_extended = unsafe { + $crate::transmute::<&mut dyn $t<$($args),*>, &mut (dyn $t<$($args),*> + 'static)>(protected) + }; + $crate::using(&GLOBAL, lifetime_extended, f) + } + + pub fn with FnOnce(&'a mut (dyn $t<$($args),*> + 'a)) -> R>( + f: F + ) -> Option { + $crate::with(&GLOBAL, |x| f(x)) + } + } + }; + ($name:ident<$traittype:ident> : trait $t:ident <$concretetype:ty>) => { + #[allow(non_camel_case_types, dead_code)] + struct $name { _private_field: $crate::PhantomData } + + $crate::thread_local_impl! { + static GLOBAL: $crate::GlobalInner<(dyn $t<$concretetype> + 'static)> + = Default::default() + } + + impl $name { + #[allow(unused_imports)] + pub fn using R>( + protected: &mut dyn $t, + f: F + ) -> R { + let lifetime_extended = unsafe { + $crate::transmute::<&mut dyn $t, &mut (dyn $t<$concretetype> + 'static)>(protected) + }; + $crate::using(&GLOBAL, lifetime_extended, f) + } + + pub fn with FnOnce(&'a mut (dyn $t<$concretetype> + 'a)) -> R>( + f: F + ) -> Option { + $crate::with(&GLOBAL, |x| f(x)) + } + } + }; + ($name:ident : trait $t:ident <>) => { $crate::environmental! { $name : trait @$t [] } }; + ($name:ident : trait $t:ident < $($args:ty),* $(,)* >) => { + $crate::environmental! { $name : trait @$t [$($args,)*] } + }; + ($name:ident : trait $t:ident) => { $crate::environmental! { $name : trait @$t [] } }; +} + +#[cfg(test)] +mod tests { + // Test trait in item position + #[allow(dead_code)] + mod trait_test { + trait Test {} + + environmental!(item_positon_trait: trait Test); + } + + // Test type in item position + #[allow(dead_code)] + mod type_test { + environmental!(item_position_type: u32); + } + + #[test] + fn simple_works() { + environmental!(counter: u32); + + fn stuff() { + counter::with(|value| *value += 1); + } + + // declare a stack variable of the same type as our global declaration. + let mut local = 41u32; + + // call stuff, setting up our `counter` environment as a reference to our local counter var. + counter::using(&mut local, stuff); + assert_eq!(local, 42); + stuff(); // safe! doesn't do anything. + assert_eq!(local, 42); + } + + #[test] + fn overwrite_with_lesser_lifetime() { + environmental!(items: Vec); + + let mut local_items = vec![1, 2, 3]; + items::using(&mut local_items, || { + let dies_at_end = vec![4, 5, 6]; + items::with(|items| *items = dies_at_end); + }); + + assert_eq!(local_items, vec![4, 5, 6]); + } + + #[test] + fn declare_with_trait_object() { + trait Foo { + fn get(&self) -> i32; + fn set(&mut self, x: i32); + } + + impl Foo for i32 { + fn get(&self) -> i32 { + *self + } + fn set(&mut self, x: i32) { + *self = x + } + } + + environmental!(foo: dyn Foo + 'static); + + fn stuff() { + foo::with(|value| { + let new_val = value.get() + 1; + value.set(new_val); + }); + } + + let mut local = 41i32; + foo::using(&mut local, stuff); + + assert_eq!(local, 42); + + stuff(); // doesn't do anything. + + assert_eq!(local, 42); + } + + #[test] + fn unwind_recursive() { + use std::panic; + + environmental!(items: Vec); + + let panicked = panic::catch_unwind(|| { + let mut local_outer = vec![1, 2, 3]; + + items::using(&mut local_outer, || { + let mut local_inner = vec![4, 5, 6]; + items::using(&mut local_inner, || { + panic!("are you unsafe?"); + }) + }); + }) + .is_err(); + + assert!(panicked); + + let mut was_cleared = true; + items::with(|_items| was_cleared = false); + + assert!(was_cleared); + } + + #[test] + fn use_non_static_trait() { + trait Sum { + fn sum(&self) -> usize; + } + impl Sum for &[usize] { + fn sum(&self) -> usize { + self.iter().fold(0, |a, c| a + c) + } + } + + environmental!(sum: trait Sum); + let numbers = vec![1, 2, 3, 4, 5]; + let mut numbers = &numbers[..]; + let got_sum = sum::using(&mut numbers, || sum::with(|x| x.sum())).unwrap(); + + assert_eq!(got_sum, 15); + } + + #[test] + fn stacking_globals() { + trait Sum { + fn sum(&self) -> usize; + } + impl Sum for &[usize] { + fn sum(&self) -> usize { + self.iter().fold(0, |a, c| a + c) + } + } + + environmental!(sum: trait Sum); + let numbers = vec![1, 2, 3, 4, 5]; + let mut numbers = &numbers[..]; + let got_sum = sum::using(&mut numbers, || { + sum::with(|_| { + let numbers2 = vec![1, 2, 3, 4, 5, 6]; + let mut numbers2 = &numbers2[..]; + sum::using(&mut numbers2, || sum::with(|x| x.sum())) + }) + }) + .unwrap() + .unwrap(); + + assert_eq!(got_sum, 21); + + assert!(sum::with(|_| ()).is_none()); + } + + #[test] + fn use_generic_trait() { + trait Plus { + fn plus42() -> usize; + } + struct ConcretePlus; + impl Plus for ConcretePlus { + fn plus42() -> usize { + 42 + } + } + trait Multiplier { + fn mul_and_add(&self) -> usize; + } + impl<'a, P: Plus> Multiplier

for &'a [usize] { + fn mul_and_add(&self) -> usize { + self.iter().fold(1, |a, c| a * c) + P::plus42() + } + } + + let numbers = vec![1, 2, 3]; + let mut numbers = &numbers[..]; + let out = foo::::using(&mut numbers, || { + foo::::with(|x| x.mul_and_add()) + }) + .unwrap(); + + assert_eq!(out, 6 + 42); + environmental!(foo: trait Multiplier); + } +} diff --git a/bitacross-worker/core-primitives/substrate-sgx/externalities/Cargo.toml b/bitacross-worker/core-primitives/substrate-sgx/externalities/Cargo.toml new file mode 100644 index 0000000000..7edac9aa58 --- /dev/null +++ b/bitacross-worker/core-primitives/substrate-sgx/externalities/Cargo.toml @@ -0,0 +1,44 @@ +[package] +name = "itp-sgx-externalities" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG and Parity Technologies '] +edition = "2021" + +[dependencies] +# no_std +codec = { version = "3.0.0", package = "parity-scale-codec", default-features = false, features = ["derive", "chain-error"] } +derive_more = "0.99.16" +log = { version = "0.4", default-features = false } +postcard = { version = "0.7.2", default-features = false, features = ["alloc"] } +serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] } + +# sgx dependencies +sgx_tstd = { optional = true, features = ["untrusted_fs", "net", "backtrace"], git = "https://github.com/apache/teaclave-sgx-sdk.git", branch = "master" } + +# substrate +sp-core = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# local +environmental = { default-features = false, path = "../environmental" } +itp-hashing = { default-features = false, path = "../../hashing" } + +[dev-dependencies] +itp-storage = { default-features = false, path = "../../storage" } + +[features] +default = ["std"] +std = [ + "codec/std", + "environmental/std", + "itp-hashing/std", + "log/std", + "postcard/use-std", + "serde/std", + "itp-storage/std", + # substrate + "sp-core/std", +] +sgx = [ + "sgx_tstd", + "environmental/sgx", +] diff --git a/bitacross-worker/core-primitives/substrate-sgx/externalities/src/bypass.rs b/bitacross-worker/core-primitives/substrate-sgx/externalities/src/bypass.rs new file mode 100644 index 0000000000..dcd5bd9f9c --- /dev/null +++ b/bitacross-worker/core-primitives/substrate-sgx/externalities/src/bypass.rs @@ -0,0 +1,60 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Converts maps to vecs for serialization. +//! from https://github.com/DenisKolodin/vectorize +//! +//! `bypass` is necessary to force deriving serialization of complex type specs. + +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +#[allow(unused)] +pub fn serialize<'a, T, S>(target: T, ser: S) -> Result +where + S: Serializer, + T: Serialize + 'a, +{ + serde::Serialize::serialize(&target, ser) +} + +#[allow(unused)] +pub fn deserialize<'de, T, D>(des: D) -> Result +where + D: Deserializer<'de>, + T: Deserialize<'de>, +{ + serde::Deserialize::deserialize(des) +} + +#[cfg(test)] +mod tests { + use serde::{de::DeserializeOwned, Deserialize, Serialize}; + use std::fmt; + + trait Requirement: + DeserializeOwned + Serialize + Clone + fmt::Debug + Sync + Send + 'static + { + } + + trait ComplexSpec: Requirement {} + + #[derive(Debug, Serialize, Deserialize)] + struct MyComplexType { + #[serde(with = "super")] // = "vectorize::bypass" + inner: Option, + } +} diff --git a/bitacross-worker/core-primitives/substrate-sgx/externalities/src/codec_impl.rs b/bitacross-worker/core-primitives/substrate-sgx/externalities/src/codec_impl.rs new file mode 100644 index 0000000000..b65f9003f1 --- /dev/null +++ b/bitacross-worker/core-primitives/substrate-sgx/externalities/src/codec_impl.rs @@ -0,0 +1,149 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Implement `parity-scale-codec` for the externalities. +//! +//! This is necessary workaround, as `Encode` and `Decode` can't directly be implemented on `HashMap` or `BTreeMap`. + +use codec::{Decode, Encode, Input}; +use serde::{de::DeserializeOwned, Serialize}; +use std::{vec, vec::Vec}; + +use crate::{SgxExternalitiesDiffType, SgxExternalitiesType}; + +impl Encode for SgxExternalitiesType { + fn encode(&self) -> Vec { + encode_with_serialize(&self) + } +} + +impl Decode for SgxExternalitiesType { + fn decode(input: &mut I) -> Result { + decode_with_deserialize(input) + } +} + +impl Encode for SgxExternalitiesDiffType { + fn encode(&self) -> Vec { + encode_with_serialize(&self) + } +} + +impl Decode for SgxExternalitiesDiffType { + fn decode(input: &mut I) -> Result { + decode_with_deserialize(input) + } +} + +fn encode_with_serialize(source: &T) -> Vec { + // We unwrap on purpose here in order to make sure we notice when something goes wrong. + // Before we returned an empty vec and logged the error. But this could go unnoticed in the + // caller and cause problems (in case the empty vec is also something valid) + postcard::to_allocvec(source).unwrap() +} + +fn decode_with_deserialize( + input: &mut I, +) -> Result { + let input_length = input + .remaining_len()? + .ok_or_else(|| codec::Error::from("Could not read length from input data"))?; + + let mut buff = vec![0u8; input_length]; + + input.read(&mut buff)?; + + postcard::from_bytes::<'_, T>(buff.as_slice()).map_err(|e| { + log::error!("deserialization failed: {:?}", e); + codec::Error::from("Could not decode with deserialize") + }) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{InternalMap, SgxExternalities}; + use std::{ + collections::hash_map::DefaultHasher, + hash::{Hash, Hasher}, + }; + + #[test] + fn serializing_externalities_type_works() { + ensure_serialize_roundtrip_succeeds(create_default_state()); + } + + #[test] + fn serializing_externalities_diff_type_works() { + ensure_serialize_roundtrip_succeeds(create_default_state_diff()); + } + + #[test] + fn serializing_externalities_works() { + let externalities = SgxExternalities { + state: create_default_state(), + state_diff: create_default_state_diff(), + }; + + ensure_serialize_roundtrip_succeeds(externalities); + } + + #[test] + fn encoding_decoding_preserves_order() { + let externalities = create_default_state(); + let encoded_externalities = externalities.encode(); + let decoded_externalities: SgxExternalitiesType = + Decode::decode(&mut encoded_externalities.as_slice()).unwrap(); + let encoded_second_time_externalities = decoded_externalities.encode(); + + assert_eq!( + calculate_hash(&encoded_externalities), + calculate_hash(&encoded_second_time_externalities) + ); + } + + fn create_default_state_diff() -> SgxExternalitiesDiffType { + let mut map = InternalMap::>>::new(); + map.insert(Encode::encode("dings"), Some(Encode::encode("other"))); + map.insert(Encode::encode("item"), Some(Encode::encode("crate"))); + map.insert(Encode::encode("key"), None); + SgxExternalitiesDiffType(map) + } + + fn create_default_state() -> SgxExternalitiesType { + let mut map = InternalMap::>::new(); + map.insert(Encode::encode("dings"), Encode::encode("other")); + map.insert(Encode::encode("item"), Encode::encode("crate")); + SgxExternalitiesType(map) + } + + fn ensure_serialize_roundtrip_succeeds< + T: Serialize + DeserializeOwned + std::cmp::PartialEq + std::fmt::Debug, + >( + item: T, + ) { + let serialized_item = postcard::to_allocvec(&item).unwrap(); + let deserialized_item = postcard::from_bytes::<'_, T>(serialized_item.as_slice()).unwrap(); + assert_eq!(item, deserialized_item); + } + + fn calculate_hash(t: &T) -> u64 { + let mut s = DefaultHasher::new(); + t.hash(&mut s); + s.finish() + } +} diff --git a/bitacross-worker/core-primitives/substrate-sgx/externalities/src/lib.rs b/bitacross-worker/core-primitives/substrate-sgx/externalities/src/lib.rs new file mode 100644 index 0000000000..f417c88286 --- /dev/null +++ b/bitacross-worker/core-primitives/substrate-sgx/externalities/src/lib.rs @@ -0,0 +1,470 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(feature = "sgx")] +extern crate sgx_tstd as std; + +use codec::{Decode, Encode, EncodeAppend}; +use core::ops::Bound; +use derive_more::{Deref, DerefMut, From, IntoIterator}; +use itp_hashing::Hash; +use serde::{Deserialize, Serialize}; +use sp_core::{hashing::blake2_256, H256}; +use std::{collections::BTreeMap, fmt::Debug, vec, vec::Vec}; + +pub use scope_limited::{set_and_run_with_externalities, with_externalities}; + +// Unfortunately we cannot use `serde_with::serde_as` to serialize our map (which would be very convenient) +// because it has pulls in the serde and serde_json dependency with `std`, not `default-features=no`. +// Instead we use https://github.com/DenisKolodin/vectorize which is very little code, copy-pasted +// directly into this code base. +//use serde_with::serde_as; + +mod codec_impl; +mod scope_limited; +// These are used to serialize a map with keys that are not string. +mod bypass; +mod vectorize; + +type InternalMap = BTreeMap, V>; + +#[derive(From, Deref, DerefMut, Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +pub struct SgxExternalitiesType(#[serde(with = "vectorize")] InternalMap>); + +#[derive( + From, + Deref, + DerefMut, + Clone, + Debug, + Default, + PartialEq, + Eq, + Serialize, + Deserialize, + IntoIterator, +)] +pub struct SgxExternalitiesDiffType(#[serde(with = "vectorize")] InternalMap>>); + +#[derive(Clone, Debug, Default, PartialEq, Eq, Encode, Decode, Serialize, Deserialize)] +pub struct SgxExternalities { + pub state: SgxExternalitiesType, + pub state_diff: SgxExternalitiesDiffType, +} + +pub trait StateHash { + fn hash(&self) -> H256; +} + +impl StateHash for SgxExternalities { + fn hash(&self) -> H256 { + self.state.using_encoded(blake2_256).into() + } +} + +impl Hash for SgxExternalities { + fn hash(&self) -> H256 { + ::hash(self) + } +} + +pub trait SgxExternalitiesTrait { + type SgxExternalitiesType; + type SgxExternalitiesDiffType; + + // Create new Externaltiies with empty diff. + fn new(state: Self::SgxExternalitiesType) -> Self; + + fn state(&self) -> &Self::SgxExternalitiesType; + + fn state_diff(&self) -> &Self::SgxExternalitiesDiffType; + + fn insert(&mut self, k: Vec, v: Vec) -> Option>; + + /// Append a value to an existing key. + fn append(&mut self, k: Vec, v: Vec); + + fn remove(&mut self, k: &[u8]) -> Option>; + + fn get(&self, k: &[u8]) -> Option<&Vec>; + + fn contains_key(&self, k: &[u8]) -> bool; + + /// Get the next key in state after the given one (excluded) in lexicographic order. + fn next_storage_key(&self, key: &[u8]) -> Option>; + + /// Reads all keys and values under given prefix + fn iter_prefix( + &self, + key_prefix: &[u8], + ) -> Option>; + + /// Clears all values that match the given key prefix. + fn clear_prefix(&mut self, key_prefix: &[u8], maybe_limit: Option) -> u32; + + /// Prunes the state diff. + fn prune_state_diff(&mut self); + + /// Execute the given closure while `self` is set as externalities. + /// + /// Returns the result of the given closure. + fn execute_with(&mut self, f: impl FnOnce() -> R) -> R; +} + +impl SgxExternalitiesTrait for SgxExternalities +where + SgxExternalitiesType: Encode + Decode, + SgxExternalitiesDiffType: Encode + Decode, +{ + type SgxExternalitiesType = SgxExternalitiesType; + type SgxExternalitiesDiffType = SgxExternalitiesDiffType; + + fn new(state: Self::SgxExternalitiesType) -> Self { + Self { state, state_diff: Default::default() } + } + + fn state(&self) -> &Self::SgxExternalitiesType { + &self.state + } + + fn state_diff(&self) -> &Self::SgxExternalitiesDiffType { + &self.state_diff + } + + fn insert(&mut self, key: Vec, value: Vec) -> Option> { + self.state_diff.insert(key.clone(), Some(value.clone())); + self.state.insert(key, value) + } + + fn append(&mut self, key: Vec, value: Vec) { + let current = self.state.entry(key.clone()).or_default(); + let updated_value = StorageAppend::new(current).append(value); + self.state_diff.insert(key, Some(updated_value)); + } + + fn remove(&mut self, key: &[u8]) -> Option> { + self.state_diff.insert(key.to_vec(), None); + self.state.remove(key) + } + + fn get(&self, key: &[u8]) -> Option<&Vec> { + self.state.get(key) + } + + fn contains_key(&self, key: &[u8]) -> bool { + self.state.contains_key(key) + } + + fn next_storage_key(&self, key: &[u8]) -> Option> { + let range = (Bound::Excluded(key), Bound::Unbounded); + self.state.range::<[u8], _>(range).next().map(|(k, _v)| k.to_vec()) // directly return k as _v is never None in our case + } + + fn prune_state_diff(&mut self) { + self.state_diff.clear(); + } + + // Note: This implementation only works for keys encoded with Blake2_128Concat + fn iter_prefix( + &self, + key_prefix: &[u8], + ) -> Option> { + // The size of the hash part in Blake2_128Concat (16 bytes for blake2_128) + const HASH_PART_SIZE: usize = 16; + + let key_values = self + .state + .range::<[u8], _>((Bound::Included(key_prefix), Bound::Unbounded)) + .take_while(|(k, _)| k.starts_with(key_prefix)) + .filter_map(|(encoded_key, encoded_value)| { + let suffix_start = key_prefix.len() + HASH_PART_SIZE; + if encoded_key.len() > suffix_start { + let suffix = &encoded_key[suffix_start..]; + let decoded_key = K::decode(&mut &suffix[..]).ok(); + let decoded_value = V::decode(&mut &encoded_value[..]).ok(); + match (decoded_key, decoded_value) { + (Some(key), Some(value)) => Some((key, value)), + _ => None, + } + } else { + None + } + }) + .collect::>(); + + if key_values.is_empty() { + None + } else { + Some(key_values) + } + } + + fn clear_prefix(&mut self, key_prefix: &[u8], _maybe_limit: Option) -> u32 { + // Inspired by Substrate https://github.com/paritytech/substrate/blob/c8653447fc8ef8d95a92fe164c96dffb37919e85/primitives/state-machine/src/basic.rs#L242-L254 + let to_remove = self + .state + .range::<[u8], _>((Bound::Included(key_prefix), Bound::Unbounded)) + .map(|(k, _)| k) + .take_while(|k| k.starts_with(key_prefix)) + .cloned() + .collect::>(); + + let count = to_remove.len() as u32; + for key in to_remove { + self.remove(&key); + } + count + } + + fn execute_with(&mut self, f: impl FnOnce() -> R) -> R { + set_and_run_with_externalities(self, f) + } +} + +/// Results concerning an operation to remove many keys. +#[derive(codec::Encode, codec::Decode)] +#[must_use] +pub struct MultiRemovalResults { + /// A continuation cursor which, if `Some` must be provided to the subsequent removal call. + /// If `None` then all removals are complete and no further calls are needed. + pub maybe_cursor: Option>, + /// The number of items removed from the backend database. + pub backend: u32, + /// The number of unique keys removed, taking into account both the backend and the overlay. + pub unique: u32, + /// The number of iterations (each requiring a storage seek/read) which were done. + pub loops: u32, +} + +impl MultiRemovalResults { + /// Deconstruct into the internal components. + /// + /// Returns `(maybe_cursor, backend, unique, loops)`. + pub fn deconstruct(self) -> (Option>, u32, u32, u32) { + (self.maybe_cursor, self.backend, self.unique, self.loops) + } +} + +/// Auxialiary structure for appending a value to a storage item. +/// Taken from https://github.com/paritytech/substrate/blob/master/primitives/state-machine/src/ext.rs +pub(crate) struct StorageAppend<'a>(&'a mut Vec); + +impl<'a> StorageAppend<'a> { + /// Create a new instance using the given `storage` reference. + pub fn new(storage: &'a mut Vec) -> Self { + Self(storage) + } + + /// Append the given `value` to the storage item. + /// + /// If appending fails, `[value]` is stored in the storage item. + pub fn append(&mut self, value: Vec) -> Vec { + let value = vec![EncodeOpaqueValue(value)]; + + let item = core::mem::take(self.0); + + *self.0 = match Vec::::append_or_new(item, &value) { + Ok(item) => item, + Err(_) => { + log::error!("Failed to append value, resetting storage item to input value."); + value.encode() + }, + }; + (*self.0).to_vec() + } +} + +/// Implement `Encode` by forwarding the stored raw vec. +struct EncodeOpaqueValue(Vec); + +impl Encode for EncodeOpaqueValue { + fn using_encoded R>(&self, f: F) -> R { + f(&self.0) + } +} + +#[cfg(test)] +pub mod tests { + + use super::*; + use itp_storage::{storage_double_map_key, storage_map_key, StorageHasher}; + + #[test] + fn mutating_externalities_through_environmental_variable_works() { + let mut externalities = SgxExternalities::default(); + + externalities.execute_with(|| { + with_externalities(|e| { + e.insert("building".encode(), "empire_state".encode()); + e.insert("house".encode(), "ginger_bread".encode()); + }) + .unwrap() + }); + + let state_len = + externalities.execute_with(|| with_externalities(|e| e.state.0.len()).unwrap()); + + assert_eq!(2, state_len); + } + + #[test] + fn basic_externalities_is_empty() { + let ext = SgxExternalities::default(); + assert!(ext.state.0.is_empty()); + } + + #[test] + fn storage_append_works() { + let mut data = Vec::new(); + let mut append = StorageAppend::new(&mut data); + append.append(1u32.encode()); + let updated_data = append.append(2u32.encode()); + drop(append); + + assert_eq!(Vec::::decode(&mut &data[..]).unwrap(), vec![1, 2]); + assert_eq!(updated_data, data); + + // Initialize with some invalid data + let mut data = vec![1]; + let mut append = StorageAppend::new(&mut data); + append.append(1u32.encode()); + append.append(2u32.encode()); + drop(append); + + assert_eq!(Vec::::decode(&mut &data[..]).unwrap(), vec![1, 2]); + } + + #[test] + #[should_panic(expected = "already borrowed: BorrowMutError")] + fn nested_with_externalities_panics() { + let mut ext = SgxExternalities::default(); + + ext.execute_with(|| { + with_externalities(|_| with_externalities(|_| unreachable!("panics before")).unwrap()) + .unwrap(); + }); + } + + #[test] + fn nesting_execute_with_uses_the_latest_externalities() { + let mut ext = SgxExternalities::default(); + let mut ext2 = ext.clone(); + + let hello = b"hello".to_vec(); + let world = b"world".to_vec(); + + ext.execute_with(|| { + with_externalities(|e| { + e.insert(hello.clone(), hello.clone()); + }) + .unwrap(); + + ext2.execute_with(|| { + // `with_externalities` uses the latest set externalities defined by the last + // `set_and_run_with_externalities` call. + with_externalities(|e| { + e.insert(world.clone(), world.clone()); + }) + .unwrap(); + }); + }); + + assert_eq!(ext.get(&hello), Some(&hello)); + assert_eq!(ext2.get(&world), Some(&world)); + + // ext1 and ext2 are unrelated. + assert_eq!(ext.get(&world), None); + } + + #[test] + fn clear_prefix_works() { + let mut externalities = SgxExternalities::default(); + let non_house_key = b"window house".to_vec(); + let non_house_value = b"test_string".to_vec(); + // Fill state. + externalities.execute_with(|| { + with_externalities(|e| { + e.insert(b"house_building".to_vec(), b"empire_state".to_vec()); + e.insert(b"house".to_vec(), b"ginger_bread".to_vec()); + e.insert(b"house door".to_vec(), b"right".to_vec()); + e.insert(non_house_key.clone(), non_house_value.clone()); + }) + .unwrap() + }); + let state_len = + externalities.execute_with(|| with_externalities(|e| e.state.0.len()).unwrap()); + assert_eq!(state_len, 4); + + let number_of_removed_items = externalities + .execute_with(|| with_externalities(|e| e.clear_prefix(b"house", None)).unwrap()); + assert_eq!(number_of_removed_items, 3); + + let state_len = + externalities.execute_with(|| with_externalities(|e| e.state.0.len()).unwrap()); + assert_eq!(state_len, 1); + let stored_value = externalities.execute_with(|| { + with_externalities(|e| { + assert_eq!(e.get(&non_house_key).unwrap().clone(), non_house_value) + }) + }); + assert!(stored_value.is_some()); + } + + #[test] + fn iter_prefix_works() { + let mut externalities = SgxExternalities::default(); + + let key_1 = storage_double_map_key( + "Pallet", + "Storage", + &1_u32, + &StorageHasher::Blake2_128Concat, + &2_u32, + &StorageHasher::Blake2_128Concat, + ); + let key_2 = storage_double_map_key( + "Pallet", + "Storage", + &1_u32, + &StorageHasher::Blake2_128Concat, + &3_u32, + &StorageHasher::Blake2_128Concat, + ); + let prefix_key = + storage_map_key("Pallet", "Storage", &1_u32, &StorageHasher::Blake2_128Concat); + + // Fill state. + externalities.execute_with(|| { + with_externalities(|e| { + e.insert(key_1, 10_u32.encode()); + e.insert(key_2, 20_u32.encode()); + }) + .unwrap() + }); + // Perform iter prefix + externalities.execute_with(|| { + with_externalities(|e| { + let values = e.iter_prefix::(&prefix_key).unwrap(); + assert_eq!(values, [(2, 10), (3, 20)]); + }) + .unwrap() + }); + } +} diff --git a/bitacross-worker/core-primitives/substrate-sgx/externalities/src/scope_limited.rs b/bitacross-worker/core-primitives/substrate-sgx/externalities/src/scope_limited.rs new file mode 100644 index 0000000000..55c9a9e4d7 --- /dev/null +++ b/bitacross-worker/core-primitives/substrate-sgx/externalities/src/scope_limited.rs @@ -0,0 +1,38 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Stores the externalities in an `environmental` value to make it scope limited available. + +use crate::SgxExternalities; + +environmental::environmental!(ext: SgxExternalities); + +/// Set the given externalities while executing the given closure. To get access to the +/// externalities while executing the given closure [`with_externalities`] grants access to them. +/// The externalities are only set for the same thread this function was called from. +pub fn set_and_run_with_externalities R, R>(ext: &mut SgxExternalities, f: F) -> R { + ext::using(ext, f) +} + +/// Execute the given closure with the currently set externalities. +/// +/// Returns `None` if no externalities are set or `Some(_)` with the result of the closure. +/// +/// Panics with `already borrowed: BorrowMutError` if calls to `with_externalities` are nested. +pub fn with_externalities R, R>(f: F) -> Option { + ext::with(f) +} diff --git a/bitacross-worker/core-primitives/substrate-sgx/externalities/src/vectorize.rs b/bitacross-worker/core-primitives/substrate-sgx/externalities/src/vectorize.rs new file mode 100644 index 0000000000..d2203902ae --- /dev/null +++ b/bitacross-worker/core-primitives/substrate-sgx/externalities/src/vectorize.rs @@ -0,0 +1,76 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +//! Converts maps to vecs for serialization. +//! from https://github.com/DenisKolodin/vectorize + +use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use std::{iter::FromIterator, vec::Vec}; + +pub fn serialize<'a, T, K, V, S>(target: T, ser: S) -> Result +where + S: Serializer, + T: IntoIterator, + K: Serialize + 'a, + V: Serialize + 'a, +{ + let container: Vec<_> = target.into_iter().collect(); + serde::Serialize::serialize(&container, ser) +} + +pub fn deserialize<'de, T, K, V, D>(des: D) -> Result +where + D: Deserializer<'de>, + T: FromIterator<(K, V)>, + K: Deserialize<'de>, + V: Deserialize<'de>, +{ + let container: Vec<_> = serde::Deserialize::deserialize(des)?; + Ok(container.into_iter().collect()) +} + +#[cfg(test)] +mod tests { + use crate::vectorize; + use serde::{Deserialize, Serialize}; + use std::collections::HashMap; + + #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)] + struct MyKey { + one: String, + two: u16, + more: Vec, + } + + #[derive(Debug, Serialize, Deserialize)] + struct MyComplexType { + #[serde(with = "vectorize")] + map: HashMap, + } + + #[test] + fn it_works() -> Result<(), Box> { + let key = MyKey { one: "1".into(), two: 2, more: vec![1, 2, 3] }; + let mut map = HashMap::new(); + map.insert(key.clone(), "value".into()); + let instance = MyComplexType { map }; + let serialized = postcard::to_allocvec(&instance)?; + let deserialized: MyComplexType = postcard::from_bytes(&serialized)?; + let expected_value = "value".to_string(); + assert_eq!(deserialized.map.get(&key), Some(&expected_value)); + Ok(()) + } +} diff --git a/bitacross-worker/core-primitives/substrate-sgx/sp-io/Cargo.toml b/bitacross-worker/core-primitives/substrate-sgx/sp-io/Cargo.toml new file mode 100644 index 0000000000..0600c8a4b6 --- /dev/null +++ b/bitacross-worker/core-primitives/substrate-sgx/sp-io/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "sp-io" +version = "7.0.0" +authors = ['Trust Computing GmbH ', 'Integritee AG and Parity Technologies '] +edition = "2021" +license = "Apache-2.0" + +[dependencies] +codec = { version = "3.0.0", package = "parity-scale-codec", default-features = false } +libsecp256k1 = { version = "0.7.0", default-features = false, features = ["static-context"] } +log = { version = "0.4", default-features = false } + +itp-sgx-externalities = { default-features = false, path = "../externalities" } +sgx_tstd = { optional = true, features = ["untrusted_fs", "net", "backtrace"], git = "https://github.com/apache/teaclave-sgx-sdk.git", branch = "master" } + +# Substrate dependencies +sp-core = { default-features = false, features = ["full_crypto"], git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +[features] +default = ["std"] +std = [ + "log/std", + "sp-core/std", + "codec/std", + "libsecp256k1/std", + "itp-sgx-externalities/std", +] +sgx = [ + "sgx_tstd", + "itp-sgx-externalities/sgx", +] + +# These two features are used for `no_std` builds for the environments which already provides +# `#[panic_handler]`, `#[alloc_error_handler]` and `#[global_allocator]`. +# +# For the regular wasm sgx-runtime builds those are not used. +disable_panic_handler = [] +disable_oom = [] +disable_allocator = [] diff --git a/bitacross-worker/core-primitives/substrate-sgx/sp-io/src/lib.rs b/bitacross-worker/core-primitives/substrate-sgx/sp-io/src/lib.rs new file mode 100644 index 0000000000..6962f6c164 --- /dev/null +++ b/bitacross-worker/core-primitives/substrate-sgx/sp-io/src/lib.rs @@ -0,0 +1,1012 @@ +// Copyright 2017-2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//#![warn(missing_docs)] + +// Added by Integritee. Prevents warnings during compilation with sgx features at all those +// unimplemented method stubs. +#![allow(unused_variables)] +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(not(feature = "std"), feature(lang_items))] +#![cfg_attr(not(feature = "std"), feature(alloc_error_handler))] +#![cfg_attr(not(feature = "std"), feature(core_intrinsics))] +#![cfg_attr( + feature = "std", + doc = "Substrate sgx-runtime standard library as compiled when linked with Rust's standard library." +)] +#![cfg_attr( + not(feature = "std"), + doc = "Substrate's sgx-runtime standard library as compiled without Rust's standard library." +)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(feature = "sgx")] +extern crate sgx_tstd as std; + +use codec::{Decode, Encode}; +use log::*; +use sp_core::{ + crypto::{KeyTypeId, Pair}, + ecdsa, ed25519, + hash::H256, + offchain::{ + HttpError, HttpRequestId, HttpRequestStatus, OpaqueNetworkState, StorageKind, Timestamp, + }, + sr25519, + storage::StateVersion, +}; +use std::{char, prelude::v1::String, println, vec, vec::Vec}; + +#[allow(unused)] +fn encode_hex_digit(digit: u8) -> char { + match char::from_digit(u32::from(digit), 16) { + Some(c) => c, + _ => panic!(), + } +} + +#[allow(unused)] +fn encode_hex_byte(byte: u8) -> [char; 2] { + [encode_hex_digit(byte >> 4), encode_hex_digit(byte & 0x0Fu8)] +} + +#[allow(unused)] +pub fn encode_hex(bytes: &[u8]) -> String { + let strs: Vec = bytes + .iter() + .map(|byte| encode_hex_byte(*byte).iter().copied().collect()) + .collect(); + strs.join("") +} + +// Reexport here, such that the worker does not need to import other crate. +// Not sure if this is a good Idea though. +pub use itp_sgx_externalities::{ + with_externalities, SgxExternalities, SgxExternalitiesTrait, SgxExternalitiesType, +}; + +pub struct MultiRemovalResults { + /// A continuation cursor which, if `Some` must be provided to the subsequent removal call. + /// If `None` then all removals are complete and no further calls are needed. + pub maybe_cursor: Option>, + /// The number of items removed from the backend database. + pub backend: u32, + /// The number of unique keys removed, taking into account both the backend and the overlay. + pub unique: u32, + /// The number of iterations (each requiring a storage seek/read) which were done. + pub loops: u32, +} + +/// Error verifying ECDSA signature +#[derive(Encode, Decode)] +pub enum EcdsaVerifyError { + /// Incorrect value of R or S + BadRS, + /// Incorrect value of V + BadV, + /// Invalid signature + BadSignature, +} + +/// The outcome of calling `storage_kill`. Returned value is the number of storage items +/// removed from the trie from making the `storage_kill` call. +#[derive(Encode, Decode)] +pub enum KillStorageResult { + /// No key remains in the child trie. + AllRemoved(u32), + /// At least one key still resides in the child trie due to the supplied limit. + SomeRemaining(u32), +} + +impl From for KillStorageResult { + fn from(r: MultiRemovalResults) -> Self { + match r { + MultiRemovalResults { maybe_cursor: None, backend, .. } => Self::AllRemoved(backend), + MultiRemovalResults { maybe_cursor: Some(..), backend, .. } => + Self::SomeRemaining(backend), + } + } +} + +pub mod storage { + use super::*; + + pub fn get(key: &[u8]) -> Option> { + debug!("storage('{}')", encode_hex(key)); + with_externalities(|ext| { + ext.get(key).map(|s| { + debug!(" returning {}", encode_hex(s)); + s.to_vec() + }) + }) + .expect("storage cannot be called outside of an Externalities-provided environment.") + } + + pub fn read(key: &[u8], value_out: &mut [u8], value_offset: u32) -> Option { + debug!( + "read_storage('{}' with offset = {:?}. value_out.len() is {})", + encode_hex(key), + value_offset, + value_out.len() + ); + with_externalities(|ext| { + ext.get(key).map(|value| { + debug!(" entire stored value: {:?}", value); + let value_offset = value_offset as usize; + let value = &value[value_offset..]; + debug!(" stored value at offset: {:?}", value); + let written = std::cmp::min(value.len(), value_out.len()); + value_out[..written].copy_from_slice(&value[..written]); + debug!(" write back {:?}, return len {}", value_out, value.len()); + // Just return u32::Max if we read more than u32::Max bytes. + value.len().try_into().unwrap_or(u32::MAX) + }) + }) + .expect("read_storage cannot be called outside of an Externalities-provided environment.") + } + + pub fn set(key: &[u8], value: &[u8]) { + debug!("set_storage('{}', {:x?})", encode_hex(key), value); + with_externalities(|ext| ext.insert(key.to_vec(), value.to_vec())) + .expect("`set` cannot be called outside of an Externalities-provided environment."); + } + + pub fn clear(key: &[u8]) { + with_externalities(|ext| { + if ext.remove(key).is_none() { + info!("Tried to clear storage that was not existing"); + } + }); + } + + pub fn exists(key: &[u8]) -> bool { + with_externalities(|ext| ext.contains_key(key)) + .expect("exists cannot be called outside of an Externalities-provided environment.") + } + + /// Clear the storage of each key-value pair where the key starts with the given `prefix`. + pub fn clear_prefix_version_1(prefix: &[u8]) { + clear_prefix(prefix, None); + } + + /// Clear the storage of each key-value pair where the key starts with the given `prefix`. + /// + /// # Limit + /// + /// Deletes all keys from the overlay and up to `limit` keys from the backend if + /// it is set to `Some`. No limit is applied when `limit` is set to `None`. + /// + /// The limit can be used to partially delete a prefix storage in case it is too large + /// to delete in one go (block). + /// + /// It returns a boolean false iff some keys are remaining in + /// the prefix after the functions returns. Also returns a `u32` with + /// the number of keys removed from the process. + /// + /// # Note + /// + /// Please note that keys that are residing in the overlay for that prefix when + /// issuing this call are all deleted without counting towards the `limit`. Only keys + /// written during the current block are part of the overlay. Deleting with a `limit` + /// mostly makes sense with an empty overlay for that prefix. + /// + /// Calling this function multiple times per block for the same `prefix` does + /// not make much sense because it is not cumulative when called inside the same block. + /// Use this function to distribute the deletion of a single child trie across multiple + /// blocks. + pub fn clear_prefix(prefix: &[u8], maybe_limit: Option) -> KillStorageResult { + let number_of_removed_values = + with_externalities(|ext| ext.clear_prefix(prefix, maybe_limit)).unwrap_or_default(); + KillStorageResult::AllRemoved(number_of_removed_values) + } + + /// Append the encoded `value` to the storage item at `key`. + /// + /// The storage item needs to implement [`EncodeAppend`](codec::EncodeAppend). + /// + /// # Warning + /// + /// If the storage item does not support [`EncodeAppend`](codec::EncodeAppend) or + /// something else fails at appending, the storage item will be set to `[value]`. + pub fn append(key: &[u8], value: Vec) { + with_externalities(|ext| ext.append(key.to_vec(), value.to_vec())); + } + + /// "Commit" all existing operations and compute the resulting storage root. + /// + /// The hashing algorithm is defined by the `Block`. + /// + /// Returns a `Vec` that holds the SCALE encoded hash. + pub fn root_version_1() -> [u8; 32] { + warn!("storage::root() unimplemented"); + [0u8; 32] + } + + /// "Commit" all existing operations and compute the resulting storage root. + /// + /// The hashing algorithm is defined by the `Block`. + /// + /// Returns a `Vec` that holds the SCALE encoded hash. + pub fn root(version: StateVersion) -> [u8; 32] { + warn!("storage::root() unimplemented"); + [0u8; 32] + } + + pub fn changes_root(parent_hash: &[u8]) -> Option<[u8; 32]> { + warn!("storage::changes_root() unimplemented"); + Some([0u8; 32]) + } + + /// Get the next key in storage after the given one in lexicographic order. + pub fn next_key(key: &[u8]) -> Option> { + debug!("next_key('{}')", encode_hex(key)); + with_externalities(|ext| ext.next_storage_key(key)) + .expect("`next_key` cannot be called outside of an Externalities-provided environment.") + } + + /// Start a new nested transaction. + /// + /// This allows to either commit or roll back all changes that are made after this call. + /// For every transaction there must be a matching call to either `rollback_transaction` + /// or `commit_transaction`. This is also effective for all values manipulated using the + /// `DefaultChildStorage` API. + /// + /// # Warning + /// + /// This is a low level API that is potentially dangerous as it can easily result + /// in unbalanced transactions. For example, FRAME users should use high level storage + /// abstractions. + pub fn start_transaction() { + warn!("storage::start_transaction unimplemented"); + } + + /// Rollback the last transaction started by `start_transaction`. + /// + /// Any changes made during that transaction are discarded. + /// + /// # Panics + /// + /// Will panic if there is no open transaction. + pub fn rollback_transaction() { + warn!("storage::rollback_transaction unimplemented"); + } + + /// Commit the last transaction started by `start_transaction`. + /// + /// Any changes made during that transaction are committed. + /// + /// # Panics + /// + /// Will panic if there is no open transaction. + pub fn commit_transaction() { + warn!("storage::commit_transaction unimplemented"); + } +} + +pub mod default_child_storage { + use super::*; + + pub fn read( + storage_key: &[u8], + key: &[u8], + value_out: &mut [u8], + value_offset: u32, + ) -> Option { + // TODO unimplemented + warn!("default_child_storage::read() unimplemented"); + Some(0) + } + + pub fn get(storage_key: &[u8], key: &[u8]) -> Option> { + // TODO: unimplemented + warn!("default_child_storage::get() unimplemented"); + Some(vec![0, 1, 2, 3]) + } + + pub fn set(storage_key: &[u8], key: &[u8], value: &[u8]) { + warn!("default_child_storage::set() unimplemented"); + } + + pub fn clear(storage_key: &[u8], key: &[u8]) { + warn!("child storage::clear() unimplemented"); + } + + pub fn storage_kill_version_1(storage_key: &[u8]) { + warn!("child storage::storage_kill() unimplemented"); + } + + pub fn storage_kill_version_2(storage_key: &[u8], limit: Option) -> bool { + warn!("child storage::storage_kill() unimplemented"); + false + } + + /// Clear a child storage key. + /// + /// See `Storage` module `clear_prefix` documentation for `limit` usage. + pub fn storage_kill(storage_key: &[u8], limit: Option) -> KillStorageResult { + warn!("child storage::storage_kill() unimplemented"); + KillStorageResult::AllRemoved(0) + } + + pub fn exists(storage_key: &[u8], key: &[u8]) -> bool { + warn!("child storage::exists() unimplemented"); + false + } + + /// Clear child default key by prefix. + /// + /// Clear the child storage of each key-value pair where the key starts with the given `prefix`. + pub fn clear_prefix_version_1(storage_key: &[u8], prefix: &[u8]) { + warn!("child storage::clear_prefix() unimplemented"); + } + + /// Clear the child storage of each key-value pair where the key starts with the given `prefix`. + /// + /// See `Storage` module `clear_prefix` documentation for `limit` usage. + pub fn clear_prefix( + storage_key: &[u8], + prefix: &[u8], + limit: Option, + ) -> KillStorageResult { + warn!("child storage::clear_prefix() unimplemented"); + KillStorageResult::AllRemoved(0) + } + + pub fn root_version_1(storage_key: &[u8]) -> Vec { + warn!("child storage::root() unimplemented"); + vec![0, 1, 2, 3] + } + + pub fn root(storage_key: &[u8], version: StateVersion) -> Vec { + warn!("child storage::root() unimplemented"); + vec![0, 1, 2, 3] + } + + pub fn next_key(storage_key: &[u8], key: &[u8]) -> Option> { + warn!("child storage::next_key() unimplemented"); + Some(Vec::new()) + } +} + +pub mod trie { + use super::*; + + /// A trie root formed from the iterated items. + pub fn blake2_256_root_version_1(input: Vec<(Vec, Vec)>) -> H256 { + warn!("trie::blake2_256_root() unimplemented"); + H256::default() + } + + /// A trie root formed from the iterated items. + pub fn blake2_256_root(input: Vec<(Vec, Vec)>, version: StateVersion) -> H256 { + warn!("trie::blake2_256_root() unimplemented"); + H256::default() + } + + /// A trie root formed from the enumerated items. + pub fn blake2_256_ordered_root_version_1(input: Vec>) -> H256 { + warn!("trie::blake2_256_ordered_root() unimplemented"); + H256::default() + } + + /// A trie root formed from the enumerated items. + pub fn blake2_256_ordered_root(input: Vec>, version: StateVersion) -> H256 { + warn!("trie::blake2_256_ordered_root() unimplemented"); + H256::default() + } + + pub fn keccak_256_root_version_1(input: Vec<(Vec, Vec)>) -> H256 { + warn!("trie::keccak_256_root_version_1() unimplemented"); + H256::default() + } + + pub fn keccak_256_root(input: Vec<(Vec, Vec)>, version: StateVersion) -> H256 { + warn!("trie::keccak_256_root() unimplemented"); + H256::default() + } + + /// A trie root formed from the enumerated items. + pub fn keccak_256_ordered_root_version_1(input: Vec>) -> H256 { + warn!("trie::keccak_256_ordered_root() unimplemented"); + H256::default() + } + + /// A trie root formed from the enumerated items. + pub fn keccak_256_ordered_root(input: Vec>, version: StateVersion) -> H256 { + warn!("trie::keccak_256_ordered_root() unimplemented"); + H256::default() + } + + /// Verify trie proof + #[allow(unused)] + fn blake2_256_verify_proof_version_1( + root: H256, + proof: &[Vec], + key: &[u8], + value: &[u8], + ) -> bool { + warn!("trie::blake2_256_verify_proof() unimplemented"); + false + } + + /// Verify trie proof + #[allow(unused)] + fn blake2_256_verify_proof( + root: H256, + proof: &[Vec], + key: &[u8], + value: &[u8], + version: StateVersion, + ) -> bool { + warn!("trie::blake2_256_verify_proof() unimplemented"); + false + } + + /// Verify trie proof + #[allow(unused)] + fn keccak_256_verify_proof_version_1( + root: H256, + proof: &[Vec], + key: &[u8], + value: &[u8], + ) -> bool { + warn!("trie::keccak_256_verify_proof() unimplemented"); + false + } + + /// Verify trie proof + #[allow(unused)] + fn keccak_256_verify_proof( + root: H256, + proof: &[Vec], + key: &[u8], + value: &[u8], + version: StateVersion, + ) -> bool { + warn!("trie::keccak_256_verify_proof() unimplemented"); + false + } +} + +pub mod misc { + use super::*; + /// Print a number. + pub fn print_num(val: u64) { + debug!(target: "sgx-runtime", "{}", val); + } + + /// Print any valid `utf8` buffer. + pub fn print_utf8(utf8: &[u8]) { + if let Ok(data) = std::str::from_utf8(utf8) { + debug!(target: "sgx-runtime", "{}", data) + } + } + + /// Print any `u8` slice as hex. + pub fn print_hex(data: &[u8]) { + debug!(target: "sgx-runtime", "{:?}", data); + } + + pub fn runtime_version(wasm: &[u8]) -> Option> { + warn!("misc::runtime_version unimplemented!"); + Some([2u8; 32].to_vec()) + } +} + +/// Interfaces for working with crypto related types from within the sgx-runtime. +pub mod crypto { + use super::*; + use sp_core::H512; + pub fn ed25519_public_keys(id: KeyTypeId) -> Vec { + warn!("crypto::ed25519_public_keys unimplemented"); + vec![ed25519::Public::from_h256(H256::default())] + } + + pub fn ed25519_generate(id: KeyTypeId, seed: Option>) -> ed25519::Public { + warn!("crypto::ed25519_generate unimplemented"); + ed25519::Public::from_h256(H256::default()) + } + + pub fn ed25519_sign( + id: KeyTypeId, + pub_key: &ed25519::Public, + msg: &[u8], + ) -> Option { + warn!("crypto::ed25519_sign unimplemented"); + + Some(ed25519::Signature::from_raw(H512::default().into())) + } + + pub fn ed25519_verify(sig: &ed25519::Signature, msg: &[u8], pub_key: &ed25519::Public) -> bool { + ed25519::Pair::verify(sig, msg, pub_key) + } + + pub fn ed25519_batch_verify( + sig: &ed25519::Signature, + msg: &[u8], + pub_key: &ed25519::Public, + ) -> bool { + warn!("crypto::ed25519_batch_verify unimplemented"); + false + } + + /// Register a `sr25519` signature for batch verification. + /// + /// Batch verification must be enabled by calling [`start_batch_verify`]. + /// If batch verification is not enabled, the signature will be verified immediatley. + /// To get the result of the batch verification, [`finish_batch_verify`] + /// needs to be called. + /// + /// Returns `true` when the verification is either successful or batched. + pub fn sr25519_batch_verify( + sig: &sr25519::Signature, + msg: &[u8], + pub_key: &sr25519::Public, + ) -> bool { + warn!("crypto::sr25519_batch_verify unimplemented"); + false + } + /// Start verification extension. + pub fn start_batch_verify() { + warn!("crypto::start_batch_verify unimplemented"); + } + + pub fn finish_batch_verify() -> bool { + warn!("crypto::finish_batch_verify unimplemented"); + true + } + + pub fn sr25519_public_keys(id: KeyTypeId) -> Vec { + warn!("crypto::sr25519_public_key unimplemented"); + vec![sr25519::Public::from_h256(H256::default())] + } + + pub fn sr25519_generate(id: KeyTypeId, seed: Option>) -> sr25519::Public { + warn!("crypto::sr25519_generate unimplemented"); + sr25519::Public::from_h256(H256::default()) + } + + pub fn sr25519_sign( + id: KeyTypeId, + pubkey: &sr25519::Public, + msg: &[u8], + ) -> Option { + warn!("crypto::sr25519_sign unimplemented"); + Some(sr25519::Signature::from_raw(H512::default().into())) + } + + /// Verify `sr25519` signature. + /// + /// Returns `true` when the verification was successful. + pub fn sr25519_verify(sig: &sr25519::Signature, msg: &[u8], pub_key: &sr25519::Public) -> bool { + sr25519::Pair::verify(sig, msg, pub_key) + } + + /// Returns all `ecdsa` public keys for the given key id from the keystore. + pub fn ecdsa_public_keys(id: KeyTypeId) -> Vec { + warn!("crypto::ecdsa_public_keys unimplemented"); + Vec::new() + } + + /// Generate an `ecdsa` key for the given key type using an optional `seed` and + /// store it in the keystore. + /// + /// The `seed` needs to be a valid utf8. + /// + /// Returns the public key. + pub fn ecdsa_generate(id: KeyTypeId, seed: Option>) -> ecdsa::Public { + warn!("crypto::ecdsa_generate unimplemented"); + let raw: [u8; 33] = [0; 33]; + ecdsa::Public::from_raw(raw) + } + + /// Sign the given `msg` with the `ecdsa` key that corresponds to the given public key and + /// key type in the keystore. + /// + /// Returns the signature. + pub fn ecdsa_sign( + id: KeyTypeId, + pub_key: &ecdsa::Public, + msg: &[u8], + ) -> Option { + warn!("crypto::ecdsa_sign unimplemented"); + None + } + + /// Verify `ecdsa` signature. + /// + /// Returns `true` when the verification was successful. + pub fn ecdsa_verify(sig: &ecdsa::Signature, msg: &[u8], pub_key: &ecdsa::Public) -> bool { + ecdsa::Pair::verify(sig, msg, pub_key) + } + + /// Register a `ecdsa` signature for batch verification. + /// + /// Batch verification must be enabled by calling [`start_batch_verify`]. + /// If batch verification is not enabled, the signature will be verified immediatley. + /// To get the result of the batch verification, [`finish_batch_verify`] + /// needs to be called. + /// + /// Returns `true` when the verification is either successful or batched. + pub fn ecdsa_batch_verify(sig: &ecdsa::Signature, msg: &[u8], pub_key: &ecdsa::Public) -> bool { + warn!("crypto::ecdsa_batch_verify unimplemented"); + false + } + + pub fn secp256k1_ecdsa_recover( + sig: &[u8; 65], + msg: &[u8; 32], + ) -> Result<[u8; 64], EcdsaVerifyError> { + let rs = libsecp256k1::Signature::parse_standard_slice(&sig[0..64]) + .map_err(|_| EcdsaVerifyError::BadRS)?; + let v = libsecp256k1::RecoveryId::parse(if sig[64] > 26 { sig[64] - 27 } else { sig[64] }) + .map_err(|_| EcdsaVerifyError::BadV)?; + let pubkey = libsecp256k1::recover(&libsecp256k1::Message::parse(msg), &rs, &v) + .map_err(|_| EcdsaVerifyError::BadSignature)?; + let mut res = [0u8; 64]; + res.copy_from_slice(&pubkey.serialize()[1..65]); + + Ok(res) + } + + pub fn secp256k1_ecdsa_recover_compressed( + sig: &[u8; 65], + msg: &[u8; 32], + ) -> Result<[u8; 33], EcdsaVerifyError> { + let rs = libsecp256k1::Signature::parse_standard_slice(&sig[0..64]) + .map_err(|_| EcdsaVerifyError::BadRS)?; + let v = libsecp256k1::RecoveryId::parse(if sig[64] > 26 { sig[64] - 27 } else { sig[64] }) + .map_err(|_| EcdsaVerifyError::BadV)?; + let pubkey = libsecp256k1::recover(&libsecp256k1::Message::parse(msg), &rs, &v) + .map_err(|_| EcdsaVerifyError::BadSignature)?; + Ok(pubkey.serialize_compressed()) + } +} + +/// Interface that provides functions for hashing with different algorithms. +pub mod hashing { + use super::*; + /// Conduct a 256-bit Keccak hash. + pub fn keccak_256(data: &[u8]) -> [u8; 32] { + debug!("keccak_256 of {}", encode_hex(data)); + let hash = sp_core::hashing::keccak_256(data); + debug!(" returning hash {}", encode_hex(&hash)); + hash + } + + /// Conduct a 512-bit Keccak hash. + pub fn keccak_512(data: &[u8]) -> [u8; 64] { + debug!("keccak_512 of {}", encode_hex(data)); + let hash = sp_core::hashing::keccak_512(data); + debug!(" returning hash {}", encode_hex(&hash)); + hash + } + + /// Conduct a 256-bit Sha2 hash. + pub fn sha2_256(data: &[u8]) -> [u8; 32] { + debug!("sha2_256 of {}", encode_hex(data)); + let hash = sp_core::hashing::sha2_256(data); + debug!(" returning hash {}", encode_hex(&hash)); + hash + } + + /// Conduct a 128-bit Blake2 hash. + pub fn blake2_128(data: &[u8]) -> [u8; 16] { + debug!("blake2_128 of {}", encode_hex(data)); + let hash = sp_core::hashing::blake2_128(data); + debug!(" returning hash {}", encode_hex(&hash)); + hash + } + + /// Conduct a 256-bit Blake2 hash. + pub fn blake2_256(data: &[u8]) -> [u8; 32] { + debug!("blake2_256 of {}", encode_hex(data)); + let hash = sp_core::hashing::blake2_256(data); + debug!(" returning hash {}", encode_hex(&hash)); + hash + } + + /// Conduct four XX hashes to give a 256-bit result. + pub fn twox_256(data: &[u8]) -> [u8; 32] { + debug!("twox_256 of {}", encode_hex(data)); + let hash = sp_core::hashing::twox_256(data); + debug!(" returning {}", encode_hex(&hash)); + hash + } + + /// Conduct two XX hashes to give a 128-bit result. + pub fn twox_128(data: &[u8]) -> [u8; 16] { + debug!("twox_128 of {}", encode_hex(data)); + let hash = sp_core::hashing::twox_128(data); + debug!(" returning {}", encode_hex(&hash)); + hash + } + + /// Conduct two XX hashes to give a 64-bit result. + pub fn twox_64(data: &[u8]) -> [u8; 8] { + debug!("twox_64 of {}", encode_hex(data)); + let hash = sp_core::hashing::twox_64(data); + debug!(" returning {}", encode_hex(&hash)); + hash + } +} + +/// Interface that provides transaction indexing API. +pub mod transaction_index { + use super::*; + /// Add transaction index. Returns indexed content hash. + #[allow(unused)] + fn index(extrinsic: u32, size: u32, context_hash: [u8; 32]) { + warn!("transaction_index::index unimplemented"); + } + + /// Conduct a 512-bit Keccak hash. + #[allow(unused)] + fn renew(extrinsic: u32, context_hash: [u8; 32]) { + warn!("transaction_index::renew unimplemented"); + } +} + +pub mod offchain_index { + use super::*; + /// Write a key value pair to the Offchain DB database in a buffered fashion. + pub fn set(key: &[u8], value: &[u8]) { + warn!("offchain_index::set unimplemented"); + } + + /// Remove a key and its associated value from the Offchain DB. + pub fn clear(key: &[u8]) { + warn!("offchain_index::clear unimplemented"); + } +} + +/// Interface that provides functions to access the offchain functionality. +/// +/// These functions are being made available to the sgx-runtime and are called by the sgx-runtime. +pub mod offchain { + use super::*; + + pub fn is_validator() -> bool { + warn!("offchain::is_validator unimplemented"); + false + } + + #[allow(clippy::result_unit_err)] + pub fn submit_transaction(data: Vec) -> Result<(), ()> { + warn!("offchain::submit_transaction unimplemented"); + Err(()) + } + + #[allow(clippy::result_unit_err)] + pub fn network_state() -> Result { + warn!("offchain::network_state unimplemented"); + Err(()) + } + + pub fn timestamp() -> offchain::Timestamp { + warn!("offchain::timestamp unimplemented"); + offchain::Timestamp::default() + } + + pub fn sleep_until(deadline: offchain::Timestamp) { + warn!("offchain::sleep_until unimplemented"); + } + + pub fn random_seed() -> [u8; 32] { + warn!("offchain::random_seed unimplemented"); + [0; 32] + } + + pub fn local_storage_set(kind: offchain::StorageKind, key: &[u8], value: &[u8]) { + warn!("offchain::local_storage_set unimplemented"); + } + pub fn local_storage_clear(kind: StorageKind, key: &[u8]) { + warn!("offchain::local_storage_clear unimplemented"); + } + + pub fn local_storage_compare_and_set( + kind: offchain::StorageKind, + key: &[u8], + old_value: Option>, + new_value: &[u8], + ) -> bool { + warn!("offchain::local_storage_compare_and_set unimplemented"); + false + } + + pub fn local_storage_get(kind: offchain::StorageKind, key: &[u8]) -> Option> { + warn!("offchain::local_storage_get unimplemented"); + None + } + + #[allow(clippy::result_unit_err)] + pub fn http_request_start( + method: &str, + uri: &str, + meta: &[u8], + ) -> Result { + warn!("offchain::http_request_start unimplemented"); + Err(()) + } + + #[allow(clippy::result_unit_err)] + pub fn http_request_add_header( + request_id: offchain::HttpRequestId, + name: &str, + value: &str, + ) -> Result<(), ()> { + warn!("offchain::http_request_add_header unimplemented"); + Err(()) + } + + pub fn http_request_write_body( + request_id: offchain::HttpRequestId, + chunk: &[u8], + deadline: Option, + ) -> Result<(), offchain::HttpError> { + warn!("offchain::http_request_write_body unimplemented"); + Err(offchain::HttpError::IoError) + } + + pub fn http_response_wait( + ids: &[offchain::HttpRequestId], + deadline: Option, + ) -> Vec { + warn!("offchain::http_response_wait unimplemented"); + Vec::new() + } + + pub fn http_response_headers(request_id: offchain::HttpRequestId) -> Vec<(Vec, Vec)> { + warn!("offchain::http_response_wait unimplemented"); + Vec::new() + } + + pub fn http_response_read_body( + request_id: offchain::HttpRequestId, + buffer: &mut [u8], + deadline: Option, + ) -> Result { + warn!("offchain::http_response_read_body unimplemented"); + Err(offchain::HttpError::IoError) + } +} + +/// Interface that provides functions for logging from within the sgx-runtime. +pub mod logging { + use super::*; + use sp_core::{LogLevel, LogLevelFilter}; + /// Request to print a log message on the host. + /// + /// Note that this will be only displayed if the host is enabled to display log messages with + /// given level and target. + /// + /// Instead of using directly, prefer setting up `RuntimeLogger` and using `log` macros. + pub fn log(level: LogLevel, target: &str, message: &[u8]) { + if let Ok(message) = std::str::from_utf8(message) { + // TODO remove this attention boost + println!("\x1b[0;36m[{}]\x1b[0m {}", target, message); + let level = match level { + LogLevel::Error => log::Level::Error, + LogLevel::Warn => log::Level::Warn, + LogLevel::Info => log::Level::Info, + LogLevel::Debug => log::Level::Debug, + LogLevel::Trace => log::Level::Trace, + }; + // FIXME: this logs with target sp_io::logging instead of the provided target! + log::log!(target: target, level, "{}", message,); + } + } + + /// Returns the max log level used by the host. + pub fn max_level() -> LogLevelFilter { + log::max_level().into() + } +} + +mod tracing_setup { + /// Initialize tracing of sp_tracing not necessary – noop. To enable build + /// without std and with the `with-tracing`-feature. + pub fn init_tracing() {} +} + +pub use tracing_setup::init_tracing; + +#[cfg(test)] +mod tests { + use super::*; + use sp_core::storage::well_known_keys::CODE; + + #[test] + fn storage_set_and_retrieve_works() { + let mut ext = SgxExternalities::default(); + + ext.execute_with(|| { + storage::set(b"doe".to_vec().as_slice(), b"reindeer".to_vec().as_slice()); + storage::set(b"dog".to_vec().as_slice(), b"puppy".to_vec().as_slice()); + storage::set(b"dogglesworth".to_vec().as_slice(), b"cat".to_vec().as_slice()); + }); + + ext.execute_with(|| { + assert!(storage::get(b"doe".to_vec().as_slice()).is_some()); + assert!(storage::get(b"dog".to_vec().as_slice()).is_some()); + assert!(storage::get(b"dogglesworth".to_vec().as_slice()).is_some()); + assert!(storage::get(b"boat".to_vec().as_slice()).is_none()); + }); + } + + #[test] + fn externalities_set_and_retrieve_code() { + let mut ext = SgxExternalities::default(); + + let code = vec![1, 2, 3]; + ext.insert(CODE.to_vec(), code.clone()); + + assert_eq!(ext.get(CODE).unwrap(), &code); + } + + #[test] + #[should_panic( + expected = "`set` cannot be called outside of an Externalities-provided environment." + )] + fn storage_set_without_externalities_panics() { + storage::set(b"hello", b"world"); + } + + #[test] + fn storage_set_and_next_key_works() { + let mut ext = SgxExternalities::default(); + + ext.execute_with(|| { + storage::set(b"doe".to_vec().as_slice(), b"reindeer".to_vec().as_slice()); + storage::set(b"dog".to_vec().as_slice(), b"puppy".to_vec().as_slice()); + storage::set(b"dogglesworth".to_vec().as_slice(), b"cat".to_vec().as_slice()); + }); + + ext.execute_with(|| { + assert_eq!(storage::next_key(&[]), Some(b"doe".to_vec())); + assert_eq!(storage::next_key(b"d".to_vec().as_slice()), Some(b"doe".to_vec())); + assert_eq!( + storage::next_key(b"dog".to_vec().as_slice()), + Some(b"dogglesworth".to_vec()) + ); + assert_eq!( + storage::next_key(b"doga".to_vec().as_slice()), + Some(b"dogglesworth".to_vec()) + ); + assert_eq!(storage::next_key(b"dogglesworth".to_vec().as_slice()), None); + assert_eq!(storage::next_key(b"e".to_vec().as_slice()), None); + }); + } + + #[test] + fn storage_next_key_in_empty_externatility_works() { + let mut ext = SgxExternalities::default(); + ext.execute_with(|| { + assert_eq!(storage::next_key(&[]), None); + assert_eq!(storage::next_key(b"dog".to_vec().as_slice()), None); + }); + } + + #[test] + #[should_panic( + expected = "`next_key` cannot be called outside of an Externalities-provided environment." + )] + fn storage_next_key_without_externalities_panics() { + storage::next_key(b"d".to_vec().as_slice()); + } +} diff --git a/bitacross-worker/core-primitives/teerex-storage/Cargo.toml b/bitacross-worker/core-primitives/teerex-storage/Cargo.toml new file mode 100644 index 0000000000..ca9bafb791 --- /dev/null +++ b/bitacross-worker/core-primitives/teerex-storage/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "itp-teerex-storage" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +sp-std = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +#local deps +itp-storage = { path = "../storage", default-features = false } + +[features] +default = ["std"] +std = [ + "sp-std/std", + "itp-storage/std", +] diff --git a/bitacross-worker/core-primitives/teerex-storage/src/lib.rs b/bitacross-worker/core-primitives/teerex-storage/src/lib.rs new file mode 100644 index 0000000000..706d92fcb1 --- /dev/null +++ b/bitacross-worker/core-primitives/teerex-storage/src/lib.rs @@ -0,0 +1,35 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +use itp_storage::{storage_map_key, storage_value_key, StorageHasher}; +use sp_std::prelude::Vec; + +pub struct TeeRexStorage; + +// Separate the prefix from the rest because in our case we changed the storage prefix due to +// the rebranding. With the below implementation of the `TeerexStorageKeys`, we could simply +// define another struct `OtherStorage`, implement `StoragePrefix` for it, and get the +// `TeerexStorageKeys` implementation for free. +pub trait StoragePrefix { + fn prefix() -> &'static str; +} + +impl StoragePrefix for TeeRexStorage { + fn prefix() -> &'static str { + "Teerex" + } +} + +pub trait TeerexStorageKeys { + fn enclave_count() -> Vec; + fn enclave(index: u64) -> Vec; +} + +impl TeerexStorageKeys for S { + fn enclave_count() -> Vec { + storage_value_key(Self::prefix(), "EnclaveCount") + } + + fn enclave(index: u64) -> Vec { + storage_map_key(Self::prefix(), "EnclaveRegistry", &index, &StorageHasher::Blake2_128Concat) + } +} diff --git a/bitacross-worker/core-primitives/test/Cargo.toml b/bitacross-worker/core-primitives/test/Cargo.toml new file mode 100644 index 0000000000..ff82183e77 --- /dev/null +++ b/bitacross-worker/core-primitives/test/Cargo.toml @@ -0,0 +1,73 @@ +[package] +name = "itp-test" +version = "0.9.0" +edition = "2021" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +log = { version = "0.4", default-features = false } +sgx-crypto-helper = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", package = "sgx_crypto_helper", default-features = false } + +# sgx deps +jsonrpc-core_sgx = { package = "jsonrpc-core", git = "https://github.com/scs/jsonrpc", branch = "no_std_v18", default-features = false, optional = true } +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } +sgx_types = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } + +# substrate deps +sp-core = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-io = { default-features = false, features = ["disable_oom", "disable_panic_handler", "disable_allocator"], path = "../../core-primitives/substrate-sgx/sp-io" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-std = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# local deps +itp-node-api = { path = "../node-api", default-features = false } +itp-node-api-metadata-provider = { path = "../node-api/metadata-provider", default-features = false } +itp-ocall-api = { path = "../ocall-api", default-features = false } +itp-sgx-crypto = { path = "../sgx/crypto", default-features = false } +itp-sgx-externalities = { default-features = false, path = "../substrate-sgx/externalities" } +itp-stf-interface = { path = "../stf-interface", default-features = false } +itp-stf-primitives = { path = "../stf-primitives", default-features = false } +itp-stf-state-handler = { path = "../stf-state-handler", default-features = false } +itp-storage = { path = "../storage", default-features = false } +itp-teerex-storage = { path = "../teerex-storage", default-features = false } +itp-time-utils = { path = "../time-utils", default-features = false } +itp-types = { path = "../types", default-features = false, features = ["test"] } + +# litentry +hex = { version = "0.4.3", default-features = false } +litentry-primitives = { path = "../../litentry/primitives", default-features = false } + +[features] +default = ["std"] +std = [ + "codec/std", + "itp-node-api-metadata-provider/std", + "itp-node-api/std", + "itp-ocall-api/std", + "itp-sgx-crypto/std", + "itp-sgx-externalities/std", + "itp-stf-interface/std", + "itp-stf-primitives/std", + "itp-stf-state-handler/std", + "itp-storage/std", + "itp-teerex-storage/std", + "itp-time-utils/std", + "itp-types/std", + "log/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", + "litentry-primitives/std", +] +sgx = [ + "itp-node-api/sgx", + "itp-node-api-metadata-provider/sgx", + "itp-sgx-crypto/sgx", + "itp-sgx-externalities/sgx", + "itp-stf-state-handler/sgx", + "itp-time-utils/sgx", + "jsonrpc-core_sgx", + "sgx_tstd", + "litentry-primitives/sgx", +] diff --git a/bitacross-worker/core-primitives/test/src/builders/enclave_gen_builder.rs b/bitacross-worker/core-primitives/test/src/builders/enclave_gen_builder.rs new file mode 100644 index 0000000000..85e807c628 --- /dev/null +++ b/bitacross-worker/core-primitives/test/src/builders/enclave_gen_builder.rs @@ -0,0 +1,63 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use itp_time_utils::now_as_millis; +use itp_types::{Enclave, PalletString}; + +/// Builder for a generic enclave (`EnclaveGen`) struct. +pub struct EnclaveGenBuilder { + pubkey: AccountId, + mr_enclave: [u8; 32], + timestamp: u64, + url: PalletString, // utf8 encoded url +} + +impl Default for EnclaveGenBuilder +where + AccountId: Default, +{ + fn default() -> Self { + EnclaveGenBuilder { + pubkey: AccountId::default(), + mr_enclave: [0u8; 32], + timestamp: now_as_millis(), + url: PalletString::default(), + } + } +} + +impl EnclaveGenBuilder { + pub fn with_account(mut self, account: AccountId) -> Self { + self.pubkey = account; + self + } + + pub fn with_url(mut self, url: PalletString) -> Self { + self.url = url; + self + } + + pub fn build(self) -> EnclaveGen { + EnclaveGen { + pubkey: self.pubkey, + mr_enclave: self.mr_enclave, + timestamp: self.timestamp, + url: self.url, + } + } +} diff --git a/bitacross-worker/core-primitives/test/src/builders/mod.rs b/bitacross-worker/core-primitives/test/src/builders/mod.rs new file mode 100644 index 0000000000..610066f015 --- /dev/null +++ b/bitacross-worker/core-primitives/test/src/builders/mod.rs @@ -0,0 +1,21 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Builder patterns for common structs used in tests. + +pub mod enclave_gen_builder; diff --git a/bitacross-worker/core-primitives/test/src/lib.rs b/bitacross-worker/core-primitives/test/src/lib.rs new file mode 100644 index 0000000000..87759a147b --- /dev/null +++ b/bitacross-worker/core-primitives/test/src/lib.rs @@ -0,0 +1,37 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#![cfg_attr(not(feature = "std"), no_std)] + +//! Itp-test crate which contains mocks and soon some fixtures. + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +extern crate alloc; + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +// re-export module to properly feature gate sgx and regular std environment +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod sgx_reexport_prelude { + pub use jsonrpc_core_sgx as jsonrpc_core; +} + +pub mod mock; diff --git a/bitacross-worker/core-primitives/test/src/mock/handle_state_mock.rs b/bitacross-worker/core-primitives/test/src/mock/handle_state_mock.rs new file mode 100644 index 0000000000..3776a0d9a9 --- /dev/null +++ b/bitacross-worker/core-primitives/test/src/mock/handle_state_mock.rs @@ -0,0 +1,241 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(feature = "sgx")] +use std::sync::{SgxRwLock as RwLock, SgxRwLockWriteGuard as RwLockWriteGuard}; + +#[cfg(feature = "std")] +use std::sync::{RwLock, RwLockWriteGuard}; + +use itp_sgx_externalities::{SgxExternalities as StfState, StateHash}; + +use itp_stf_state_handler::{ + error::{Error, Result}, + handle_state::HandleState, + query_shard_state::QueryShardState, +}; +use itp_types::{ShardIdentifier, H256}; +use std::{collections::HashMap, format, vec::Vec}; + +/// Mock implementation for the `HandleState` trait. +/// +/// Uses an in-memory state, in a `HashMap`. To be used in unit tests. +#[derive(Default)] +pub struct HandleStateMock { + state_map: RwLock>, +} + +impl HandleStateMock { + pub fn from_shard(shard: ShardIdentifier) -> Result { + let state_handler = HandleStateMock { state_map: Default::default() }; + state_handler.initialize_shard(shard)?; + Ok(state_handler) + } +} + +impl HandleState for HandleStateMock { + type WriteLockPayload = HashMap; + type StateT = StfState; + type HashType = H256; + + fn initialize_shard(&self, shard: ShardIdentifier) -> Result { + self.reset(StfState::default(), &shard) + } + + fn migrate_shard( + &self, + old_shard: ShardIdentifier, + new_shard: ShardIdentifier, + ) -> Result { + let (state, _) = self.load_cloned(&old_shard)?; + self.reset(state, &new_shard) + } + + fn execute_on_current(&self, shard: &ShardIdentifier, executing_function: E) -> Result + where + E: FnOnce(&Self::StateT, Self::HashType) -> R, + { + self.state_map + .read() + .unwrap() + .get(shard) + .map(|state| executing_function(state, state.hash())) + .ok_or_else(|| Error::Other(format!("shard is not initialized {:?}", shard).into())) + } + + fn load_cloned(&self, shard: &ShardIdentifier) -> Result<(Self::StateT, Self::HashType)> { + self.state_map + .read() + .unwrap() + .get(shard) + .cloned() + .map(|s| { + let state_hash = s.hash(); + (s, state_hash) + }) + .ok_or_else(|| Error::Other(format!("shard is not initialized {:?}", shard).into())) + } + + fn load_for_mutation( + &self, + shard: &ShardIdentifier, + ) -> Result<(RwLockWriteGuard<'_, Self::WriteLockPayload>, StfState)> { + let (initialized_state, _) = self.load_cloned(shard)?; + let write_lock = self.state_map.write().unwrap(); + Ok((write_lock, initialized_state)) + } + + fn write_after_mutation( + &self, + state: StfState, + mut state_lock: RwLockWriteGuard<'_, Self::WriteLockPayload>, + shard: &ShardIdentifier, + ) -> Result { + state_lock.insert(*shard, state.clone()); + Ok(state.hash()) + } + + fn reset(&self, state: Self::StateT, shard: &ShardIdentifier) -> Result { + let write_lock = self.state_map.write().unwrap(); + self.write_after_mutation(state, write_lock, shard) + } +} + +impl QueryShardState for HandleStateMock { + fn shard_exists(&self, shard: &ShardIdentifier) -> Result { + let state_map_lock = self.state_map.read().map_err(|_| Error::LockPoisoning)?; + Ok(state_map_lock.get(shard).is_some()) + } + + fn list_shards(&self) -> Result> { + Ok(self.state_map.read().unwrap().iter().map(|(k, _)| *k).collect()) + } +} + +// Since the mock itself has quite a bit of complexity, we also have tests for the mock. +#[cfg(feature = "sgx")] +pub mod tests { + + use super::*; + use crate::mock::stf_mock::StfMock; + use codec::{Decode, Encode}; + use itp_sgx_externalities::{SgxExternalities, SgxExternalitiesTrait, SgxExternalitiesType}; + use itp_stf_interface::InitState; + use itp_types::ShardIdentifier; + use sp_core::crypto::AccountId32; + + pub fn initialized_shards_list_is_empty() { + let state_handler = HandleStateMock::default(); + assert!(state_handler.list_shards().unwrap().is_empty()); + } + + pub fn shard_exists_after_inserting() { + let state_handler = HandleStateMock::default(); + let shard = ShardIdentifier::default(); + state_handler.initialize_shard(shard).unwrap(); + + assert!(state_handler.load_cloned(&shard).is_ok()); + assert!(state_handler.shard_exists(&shard).unwrap()); + } + + pub fn from_shard_works() { + let shard = ShardIdentifier::default(); + let state_handler = HandleStateMock::from_shard(shard).unwrap(); + + assert!(state_handler.load_cloned(&shard).is_ok()); + assert!(state_handler.shard_exists(&shard).unwrap()); + } + + pub fn initialize_creates_default_state() { + let state_handler = HandleStateMock::default(); + let shard = ShardIdentifier::default(); + state_handler.initialize_shard(shard).unwrap(); + + let loaded_state_result = state_handler.load_cloned(&shard); + + assert!(loaded_state_result.is_ok()); + } + + pub fn load_mutate_and_write_works() { + let state_handler = HandleStateMock::default(); + let shard = ShardIdentifier::default(); + state_handler.initialize_shard(shard).unwrap(); + + let (lock, mut state) = state_handler.load_for_mutation(&shard).unwrap(); + + let (key, value) = ("my_key", "my_value"); + state.insert(key.encode(), value.encode()); + + state_handler.write_after_mutation(state, lock, &shard).unwrap(); + + let (updated_state, _) = state_handler.load_cloned(&shard).unwrap(); + + let inserted_value = + updated_state.get(key.encode().as_slice()).expect("value for key should exist"); + assert_eq!(*inserted_value, value.encode()); + } + + pub fn ensure_subsequent_state_loads_have_same_hash() { + let state_handler = HandleStateMock::default(); + let shard = ShardIdentifier::default(); + state_handler.initialize_shard(shard).unwrap(); + + let (lock, _) = state_handler.load_for_mutation(&shard).unwrap(); + let initial_state = StfMock::init_state(AccountId32::new([0u8; 32])); + let state_hash_before_execution = initial_state.hash(); + state_handler.write_after_mutation(initial_state, lock, &shard).unwrap(); + + let (_, loaded_state_hash) = state_handler.load_cloned(&shard).unwrap(); + + assert_eq!(state_hash_before_execution, loaded_state_hash); + } + + pub fn ensure_encode_and_encrypt_does_not_affect_state_hash() { + let state = StfMock::init_state(AccountId32::new([0u8; 32])); + let state_hash_before_execution = state.hash(); + + let encoded_state = state.state.encode(); + let decoded_state: SgxExternalitiesType = decode(encoded_state); + let decoded_state_hash = SgxExternalities::new(decoded_state).hash(); + + assert_eq!(state_hash_before_execution, decoded_state_hash); + } + + pub fn migrate_shard_works() { + let state_handler = HandleStateMock::default(); + let old_shard = ShardIdentifier::default(); + let bytes = hex::decode("91de6f606be264f089b155256385470f5395969386894ffba38775442f508ee2") + .unwrap(); + let new_shard = ShardIdentifier::from_slice(&bytes); + state_handler.initialize_shard(old_shard).unwrap(); + + let (lock, mut state) = state_handler.load_for_mutation(&old_shard).unwrap(); + let (key, value) = ("my_key", "my_value"); + state.insert(key.encode(), value.encode()); + state_handler.write_after_mutation(state, lock, &old_shard).unwrap(); + + state_handler.migrate_shard(old_shard, new_shard).unwrap(); + let (new_state, _) = state_handler.load_cloned(&new_shard).unwrap(); + let inserted_value = + new_state.get(key.encode().as_slice()).expect("value for key should exist"); + assert_eq!(*inserted_value, value.encode()); + } + + fn decode(encoded: Vec) -> T { + T::decode(&mut encoded.as_slice()).unwrap() + } +} diff --git a/bitacross-worker/core-primitives/test/src/mock/metrics_ocall_mock.rs b/bitacross-worker/core-primitives/test/src/mock/metrics_ocall_mock.rs new file mode 100644 index 0000000000..14cd8e67c5 --- /dev/null +++ b/bitacross-worker/core-primitives/test/src/mock/metrics_ocall_mock.rs @@ -0,0 +1,54 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(feature = "sgx")] +use std::sync::SgxRwLock as RwLock; + +#[cfg(feature = "std")] +use std::sync::RwLock; + +use codec::Encode; +use itp_ocall_api::EnclaveMetricsOCallApi; +use sgx_types::SgxResult; +use std::vec::Vec; + +/// Metrics o-call mock. +#[derive(Default)] +pub struct MetricsOCallMock { + metric_updates: RwLock>>, +} + +impl Clone for MetricsOCallMock { + fn clone(&self) -> Self { + MetricsOCallMock { + metric_updates: RwLock::new(self.metric_updates.read().unwrap().clone()), + } + } +} + +impl MetricsOCallMock { + pub fn get_metrics_updates(&self) -> Vec> { + self.metric_updates.read().unwrap().clone() + } +} + +impl EnclaveMetricsOCallApi for MetricsOCallMock { + fn update_metric(&self, metric: Metric) -> SgxResult<()> { + self.metric_updates.write().unwrap().push(metric.encode()); + Ok(()) + } +} diff --git a/bitacross-worker/core-primitives/test/src/mock/mod.rs b/bitacross-worker/core-primitives/test/src/mock/mod.rs new file mode 100644 index 0000000000..a59c988a59 --- /dev/null +++ b/bitacross-worker/core-primitives/test/src/mock/mod.rs @@ -0,0 +1,23 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pub mod handle_state_mock; +pub mod metrics_ocall_mock; +pub mod onchain_mock; +pub mod shielding_crypto_mock; +pub mod sidechain_ocall_api_mock; +pub mod stf_mock; diff --git a/bitacross-worker/core-primitives/test/src/mock/onchain_mock.rs b/bitacross-worker/core-primitives/test/src/mock/onchain_mock.rs new file mode 100644 index 0000000000..22744289b5 --- /dev/null +++ b/bitacross-worker/core-primitives/test/src/mock/onchain_mock.rs @@ -0,0 +1,243 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use codec::{Decode, Encode}; +use core::fmt::Debug; +use itp_ocall_api::{ + EnclaveAttestationOCallApi, EnclaveMetricsOCallApi, EnclaveOnChainOCallApi, + EnclaveSidechainOCallApi, +}; +use itp_storage::Error::StorageValueUnavailable; +use itp_teerex_storage::{TeeRexStorage, TeerexStorageKeys}; +use itp_types::{ + parentchain::ParentchainId, storage::StorageEntryVerified, BlockHash, Enclave, ShardIdentifier, + WorkerRequest, WorkerResponse, +}; +use sgx_types::*; +use sp_core::H256; +use sp_runtime::{traits::Header as HeaderTrait, AccountId32, OpaqueExtrinsic}; +use sp_std::prelude::*; +use std::{collections::HashMap, string::String}; + +#[derive(Default, Clone, Debug)] +pub struct OnchainMock { + inner: HashMap, Vec>, + mr_enclave: [u8; SGX_HASH_SIZE], +} + +impl OnchainMock { + pub fn with_storage_entries_at_header, V: Encode>( + mut self, + header: &Header, + entries: Vec<(Vec, V)>, + ) -> Self { + for (key, value) in entries.into_iter() { + self.insert_at_header(header, key, value.encode()); + } + self + } + + pub fn add_validateer_set>( + mut self, + header: &Header, + set: Option>, + ) -> Self { + let set = set.unwrap_or_else(validateer_set); + self.insert_at_header(header, TeeRexStorage::enclave_count(), (set.len() as u64).encode()); + self.with_storage_entries_at_header(header, into_key_value_storage(set)) + } + + pub fn with_mr_enclave(mut self, mr_enclave: [u8; SGX_HASH_SIZE]) -> Self { + self.mr_enclave = mr_enclave; + self + } + + pub fn insert_at_header>( + &mut self, + header: &Header, + key: Vec, + value: Vec, + ) { + let key_with_header = (header, key).encode(); + self.inner.insert(key_with_header, value); + } + + pub fn get_at_header>( + &self, + header: &Header, + key: &[u8], + ) -> Option<&Vec> { + let key_with_header = (header, key).encode(); + self.inner.get(&key_with_header) + } +} + +impl EnclaveAttestationOCallApi for OnchainMock { + fn sgx_init_quote(&self) -> SgxResult<(sgx_target_info_t, sgx_epid_group_id_t)> { + todo!() + } + + fn get_ias_socket(&self) -> SgxResult { + Ok(42) + } + + fn get_quote( + &self, + _sig_rl: Vec, + _report: sgx_report_t, + _sign_type: sgx_quote_sign_type_t, + _spid: sgx_spid_t, + _quote_nonce: sgx_quote_nonce_t, + ) -> SgxResult<(sgx_report_t, Vec)> { + todo!() + } + + fn get_dcap_quote(&self, _report: sgx_report_t, _quote_size: u32) -> SgxResult> { + todo!() + } + + fn get_qve_report_on_quote( + &self, + _quote: Vec, + _current_time: i64, + _quote_collateral: sgx_ql_qve_collateral_t, + _qve_report_info: sgx_ql_qe_report_info_t, + _supplemental_data_size: u32, + ) -> SgxResult<(u32, sgx_ql_qv_result_t, sgx_ql_qe_report_info_t, Vec)> { + todo!() + } + + fn get_update_info( + &self, + _platform_info: sgx_platform_info_t, + _enclave_trusted: i32, + ) -> SgxResult { + todo!() + } + + fn get_mrenclave_of_self(&self) -> SgxResult { + Ok(sgx_measurement_t { m: self.mr_enclave }) + } +} + +impl EnclaveSidechainOCallApi for OnchainMock { + fn propose_sidechain_blocks( + &self, + _signed_blocks: Vec, + ) -> SgxResult<()> { + Ok(()) + } + + fn store_sidechain_blocks( + &self, + _signed_blocks: Vec, + ) -> SgxResult<()> { + Ok(()) + } + + fn fetch_sidechain_blocks_from_peer( + &self, + _last_imported_block_hash: BlockHash, + _maybe_until_block_hash: Option, + _shard_identifier: ShardIdentifier, + ) -> SgxResult> { + Ok(Vec::new()) + } + + fn get_trusted_peers_urls(&self) -> SgxResult> { + Ok(Vec::default()) + } +} + +impl EnclaveMetricsOCallApi for OnchainMock { + fn update_metric(&self, _metric: Metric) -> SgxResult<()> { + Ok(()) + } +} + +impl EnclaveOnChainOCallApi for OnchainMock { + fn send_to_parentchain( + &self, + _extrinsics: Vec, + _: &ParentchainId, + _: bool, + ) -> SgxResult<()> { + Ok(()) + } + + fn worker_request( + &self, + _req: Vec, + _: &ParentchainId, + ) -> SgxResult>> { + Ok(Vec::new()) + } + + fn get_storage_verified, V: Decode>( + &self, + storage_hash: Vec, + header: &Header, + parentchain_id: &ParentchainId, + ) -> Result, itp_ocall_api::Error> { + self.get_multiple_storages_verified(vec![storage_hash], header, parentchain_id)? + .into_iter() + .next() + .ok_or_else(|| itp_ocall_api::Error::Storage(StorageValueUnavailable)) + } + + fn get_multiple_storages_verified, V: Decode>( + &self, + storage_hashes: Vec>, + header: &Header, + _: &ParentchainId, + ) -> Result>, itp_ocall_api::Error> { + let mut entries = Vec::with_capacity(storage_hashes.len()); + for hash in storage_hashes.into_iter() { + let value = self + .get_at_header(header, &hash) + .map(|val| Decode::decode(&mut val.as_slice())) + .transpose() + .map_err(itp_ocall_api::Error::Codec)?; + + entries.push(StorageEntryVerified::new(hash, value)) + } + Ok(entries) + } + + fn get_storage_keys(&self, _key_prefix: Vec) -> Result>, itp_ocall_api::Error> { + Ok(Default::default()) + } +} + +pub fn validateer_set() -> Vec { + let default_enclave = Enclave::new( + AccountId32::from([0; 32]), + Default::default(), + Default::default(), + Default::default(), + ); + vec![default_enclave.clone(), default_enclave.clone(), default_enclave.clone(), default_enclave] +} + +fn into_key_value_storage(validateers: Vec) -> Vec<(Vec, Enclave)> { + validateers + .into_iter() + .enumerate() + .map(|(i, e)| (TeeRexStorage::enclave(i as u64 + 1), e)) + .collect() +} diff --git a/bitacross-worker/core-primitives/test/src/mock/shielding_crypto_mock.rs b/bitacross-worker/core-primitives/test/src/mock/shielding_crypto_mock.rs new file mode 100644 index 0000000000..0006ba1245 --- /dev/null +++ b/bitacross-worker/core-primitives/test/src/mock/shielding_crypto_mock.rs @@ -0,0 +1,58 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use itp_sgx_crypto::{ + ed25519_derivation::DeriveEd25519, ShieldingCryptoDecrypt, ShieldingCryptoEncrypt, +}; +use sgx_crypto_helper::{rsa3072::Rsa3072KeyPair, RsaKeyPair}; +use sp_core::ed25519::Pair as Ed25519Pair; +use std::vec::Vec; + +#[derive(Clone)] +pub struct ShieldingCryptoMock { + key: Rsa3072KeyPair, +} + +impl Default for ShieldingCryptoMock { + fn default() -> Self { + ShieldingCryptoMock { + key: Rsa3072KeyPair::new().expect("default RSA3072 key for shielding key mock"), + } + } +} + +impl ShieldingCryptoEncrypt for ShieldingCryptoMock { + type Error = itp_sgx_crypto::Error; + + fn encrypt(&self, data: &[u8]) -> Result, Self::Error> { + self.key.encrypt(data) + } +} + +impl ShieldingCryptoDecrypt for ShieldingCryptoMock { + type Error = itp_sgx_crypto::Error; + + fn decrypt(&self, data: &[u8]) -> Result, Self::Error> { + self.key.decrypt(data) + } +} + +impl DeriveEd25519 for ShieldingCryptoMock { + fn derive_ed25519(&self) -> Result { + self.key.derive_ed25519() + } +} diff --git a/bitacross-worker/core-primitives/test/src/mock/sidechain_ocall_api_mock.rs b/bitacross-worker/core-primitives/test/src/mock/sidechain_ocall_api_mock.rs new file mode 100644 index 0000000000..0210e3bd85 --- /dev/null +++ b/bitacross-worker/core-primitives/test/src/mock/sidechain_ocall_api_mock.rs @@ -0,0 +1,124 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(feature = "sgx")] +use std::sync::SgxRwLock as RwLock; + +#[cfg(feature = "std")] +use std::sync::RwLock; + +use codec::{Decode, Encode}; +use core::marker::PhantomData; +use itp_ocall_api::{EnclaveMetricsOCallApi, EnclaveSidechainOCallApi}; +use itp_types::{BlockHash, ShardIdentifier}; +use sgx_types::{sgx_status_t, SgxResult}; +use std::{string::String, vec::Vec}; + +pub struct SidechainOCallApiMock { + fetch_from_peer_blocks: Option>, + number_of_fetch_calls: RwLock, + _phantom: PhantomData, +} + +impl SidechainOCallApiMock +where + SignedSidechainBlockType: Clone + Encode + Decode + Send + Sync, +{ + pub fn with_peer_fetch_blocks(mut self, blocks: Vec) -> Self { + self.fetch_from_peer_blocks = Some(blocks); + self + } + + pub fn number_of_fetch_calls(&self) -> usize { + *self.number_of_fetch_calls.read().unwrap() + } +} + +impl Default for SidechainOCallApiMock { + fn default() -> Self { + SidechainOCallApiMock { + fetch_from_peer_blocks: None, + number_of_fetch_calls: RwLock::new(0), + _phantom: Default::default(), + } + } +} + +impl Clone for SidechainOCallApiMock +where + SignedSidechainBlockType: Clone + Encode + Decode + Send + Sync, +{ + fn clone(&self) -> Self { + SidechainOCallApiMock { + fetch_from_peer_blocks: self.fetch_from_peer_blocks.clone(), + number_of_fetch_calls: RwLock::new(*self.number_of_fetch_calls.read().unwrap()), + _phantom: self._phantom, + } + } +} + +impl EnclaveMetricsOCallApi + for SidechainOCallApiMock +where + SignedSidechainBlockType: Clone + Encode + Decode + Send + Sync, +{ + fn update_metric(&self, _metric: Metric) -> SgxResult<()> { + Ok(()) + } +} + +impl EnclaveSidechainOCallApi + for SidechainOCallApiMock +where + SignedSidechainBlockType: Clone + Encode + Decode + Send + Sync, +{ + fn propose_sidechain_blocks( + &self, + _signed_blocks: Vec, + ) -> SgxResult<()> { + Ok(()) + } + + fn store_sidechain_blocks( + &self, + _signed_blocks: Vec, + ) -> SgxResult<()> { + Ok(()) + } + + fn fetch_sidechain_blocks_from_peer( + &self, + _last_imported_block_hash: BlockHash, + _maybe_until_block_hash: Option, + _shard_identifier: ShardIdentifier, + ) -> SgxResult> { + let mut number_of_fetch_calls_lock = self.number_of_fetch_calls.write().unwrap(); + *number_of_fetch_calls_lock += 1; + + match &self.fetch_from_peer_blocks { + Some(blocks) => Ok(blocks + .iter() + .map(|b| SignedSidechainBlock::decode(&mut b.encode().as_slice()).unwrap()) + .collect()), + None => Err(sgx_status_t::SGX_ERROR_UNEXPECTED), + } + } + + fn get_trusted_peers_urls(&self) -> SgxResult> { + Ok(Vec::default()) + } +} diff --git a/bitacross-worker/core-primitives/test/src/mock/stf_mock.rs b/bitacross-worker/core-primitives/test/src/mock/stf_mock.rs new file mode 100644 index 0000000000..d5e9ec9b72 --- /dev/null +++ b/bitacross-worker/core-primitives/test/src/mock/stf_mock.rs @@ -0,0 +1,297 @@ +/* + Copyright 2021 Integritee AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +use alloc::{boxed::Box, sync::Arc}; +use codec::{Decode, Encode}; +use core::fmt::Debug; +use itp_node_api::metadata::metadata_mocks::NodeMetadataMock; +use itp_node_api_metadata_provider::NodeMetadataRepository; +use itp_sgx_externalities::{SgxExternalities, SgxExternalitiesDiffType, SgxExternalitiesTrait}; +use itp_stf_interface::{ + runtime_upgrade::RuntimeUpgradeInterface, ExecuteCall, InitState, StateCallInterface, + StateGetterInterface, UpdateState, +}; +use itp_stf_primitives::{ + traits::{ + GetterAuthorization, PoolTransactionValidation, TrustedCallSigning, TrustedCallVerification, + }, + types::{KeyPair, Nonce, TrustedOperation}, +}; +use itp_types::{ + parentchain::{ParentchainCall, ParentchainId}, + AccountId, Balance, Index, ShardIdentifier, H256, +}; +use litentry_primitives::{Identity, LitentryMultiSignature}; +use log::*; +use sp_core::{sr25519, Pair}; +use sp_runtime::transaction_validity::{ + TransactionValidityError, UnknownTransaction, ValidTransaction, +}; +use sp_std::{vec, vec::Vec}; +use std::{thread::sleep, time::Duration}; + +// a few dummy types +type NodeMetadataRepositoryMock = NodeMetadataRepository; + +#[derive(Debug, PartialEq, Eq, Encode)] +pub enum StfMockError { + Dummy, +} +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] +pub struct StfMock { + state: SgxExternalities, +} + +impl UpdateState for StfMock { + fn apply_state_diff(_state: &mut SgxExternalities, _map_update: SgxExternalitiesDiffType) {} + + fn storage_hashes_to_update_on_block(_parentchain_id: &ParentchainId) -> Vec> { + vec![] + } +} + +impl StateCallInterface + for StfMock +{ + type Error = StfMockError; + type Result = (); + + fn execute_call( + state: &mut SgxExternalities, + shard: &ShardIdentifier, + call: TrustedCallSignedMock, + top_hash: H256, + calls: &mut Vec, + node_metadata_repo: Arc, + ) -> Result<(), Self::Error> { + state.execute_with(|| call.execute(shard, top_hash, calls, node_metadata_repo)) + } +} + +impl InitState for StfMock { + fn init_state(_enclave_account: AccountId) -> SgxExternalities { + SgxExternalities::new(Default::default()) + } +} + +impl StateGetterInterface for StfMock { + fn execute_getter(_state: &mut SgxExternalities, _getter: GetterMock) -> Option> { + Some(vec![42]) + } +} + +impl RuntimeUpgradeInterface for StfMock { + type Error = StfMockError; + fn on_runtime_upgrade(_state: &mut SgxExternalities) -> Result<(), Self::Error> { + Ok(()) + } +} + +pub type TrustedOperationMock = TrustedOperation; + +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] +#[allow(non_camel_case_types)] +pub enum TrustedCallMock { + noop(Identity), + balance_transfer(Identity, AccountId, Balance), + waste_time_ms(Identity, u64), +} + +impl TrustedCallMock { + pub fn sender_identity(&self) -> &Identity { + match self { + Self::noop(sender_identity) => sender_identity, + Self::balance_transfer(sender_identity, ..) => sender_identity, + Self::waste_time_ms(sender_identity, ..) => sender_identity, + } + } +} + +impl TrustedCallSigning for TrustedCallMock { + fn sign( + &self, + pair: &KeyPair, + nonce: Index, + mrenclave: &[u8; 32], + shard: &ShardIdentifier, + ) -> TrustedCallSignedMock { + let mut payload = self.encode(); + payload.append(&mut nonce.encode()); + payload.append(&mut mrenclave.encode()); + payload.append(&mut shard.encode()); + + TrustedCallSignedMock { + call: self.clone(), + nonce, + signature: pair.sign(payload.as_slice()), + } + } +} + +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] +pub struct TrustedCallSignedMock { + pub call: TrustedCallMock, + pub nonce: Index, + pub signature: LitentryMultiSignature, +} + +impl TrustedCallSignedMock { + pub fn new(call: TrustedCallMock, nonce: Index, signature: LitentryMultiSignature) -> Self { + TrustedCallSignedMock { call, nonce, signature } + } + + pub fn into_trusted_operation( + self, + direct: bool, + ) -> TrustedOperation { + match direct { + true => TrustedOperation::direct_call(self), + false => TrustedOperation::indirect_call(self), + } + } +} + +impl Default for TrustedCallSignedMock { + fn default() -> Self { + mock_trusted_call_signed(0) + } +} + +impl ExecuteCall for TrustedCallSignedMock { + type Error = StfMockError; + type Result = (); + + fn execute( + self, + _shard: &ShardIdentifier, + _top_hash: H256, + _calls: &mut Vec, + _node_metadata_repo: Arc, + ) -> Result<(), Self::Error> { + match self.call { + TrustedCallMock::noop(_) => Ok(()), + TrustedCallMock::balance_transfer(_, _, balance) => { + info!("touching state"); + sp_io::storage::set(b"dummy_key", &balance.encode()); + Ok(()) + }, + TrustedCallMock::waste_time_ms(_, ms) => { + sp_io::storage::set(b"dummy_key_waste_time", &42u8.encode()); + info!("executing stf call waste_time_ms. sleeping for {}ms", ms); + sleep(Duration::from_millis(ms)); + Ok(()) + }, + } + } + + fn get_storage_hashes_to_update(self) -> Vec> { + Vec::new() + } +} + +impl TrustedCallVerification for TrustedCallSignedMock { + fn sender_identity(&self) -> &Identity { + self.call.sender_identity() + } + + fn nonce(&self) -> Index { + self.nonce + } + + fn verify_signature(&self, _mrenclave: &[u8; 32], _shard: &ShardIdentifier) -> bool { + true + } +} + +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] +#[allow(non_camel_case_types)] +pub enum GetterMock { + public(PublicGetterMock), + trusted(TrustedGetterSignedMock), +} + +impl Default for GetterMock { + fn default() -> Self { + GetterMock::public(PublicGetterMock::some_value) + } +} + +impl PoolTransactionValidation for GetterMock { + fn validate(&self) -> Result { + Err(TransactionValidityError::Unknown(UnknownTransaction::CannotLookup)) + } +} + +impl GetterAuthorization for GetterMock { + fn is_authorized(&self) -> bool { + match self { + Self::trusted(tgs) => tgs.signature, + Self::public(_) => true, + } + } +} + +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] +#[allow(non_camel_case_types)] +pub enum PublicGetterMock { + some_value, +} + +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] +#[allow(non_camel_case_types)] +pub enum TrustedGetterMock { + some_value, +} + +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] +pub struct TrustedGetterSignedMock { + pub getter: TrustedGetterMock, + pub signature: bool, +} + +const MOCK_SEED: [u8; 32] = *b"34567890123456789012345678901234"; + +pub fn mock_key_pair() -> KeyPair { + KeyPair::Sr25519(Box::new(sr25519::Pair::from_seed(&MOCK_SEED))) +} + +pub fn mock_trusted_call_signed(nonce: Nonce) -> TrustedCallSignedMock { + TrustedCallMock::balance_transfer( + mock_key_pair().account_id().into(), + mock_key_pair().account_id(), + 42, + ) + .sign(&mock_key_pair(), nonce, &[0u8; 32], &ShardIdentifier::default()) +} + +pub fn mock_top_direct_trusted_call_signed() -> TrustedOperationMock { + TrustedOperationMock::direct_call(mock_trusted_call_signed(0)) +} + +pub fn mock_top_indirect_trusted_call_signed() -> TrustedOperationMock { + TrustedOperationMock::indirect_call(mock_trusted_call_signed(0)) +} + +pub fn mock_top_trusted_getter_signed() -> TrustedOperationMock { + TrustedOperationMock::get(GetterMock::trusted(TrustedGetterSignedMock { + getter: TrustedGetterMock::some_value, + signature: true, + })) +} + +pub fn mock_top_public_getter() -> TrustedOperationMock { + TrustedOperationMock::get(GetterMock::public(PublicGetterMock::some_value)) +} diff --git a/bitacross-worker/core-primitives/time-utils/Cargo.toml b/bitacross-worker/core-primitives/time-utils/Cargo.toml new file mode 100644 index 0000000000..7052ee18d7 --- /dev/null +++ b/bitacross-worker/core-primitives/time-utils/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "itp-time-utils" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +chrono = { version = "0.4.19", features = ["alloc"], optional = true } + +chrono_sgx = { package = "chrono", git = "https://github.com/mesalock-linux/chrono-sgx", optional = true } +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } + +[features] +default = ["std"] +std = [ + "chrono", +] +sgx = [ + "sgx_tstd", + "chrono_sgx", +] diff --git a/bitacross-worker/core-primitives/time-utils/src/lib.rs b/bitacross-worker/core-primitives/time-utils/src/lib.rs new file mode 100644 index 0000000000..51ac211d72 --- /dev/null +++ b/bitacross-worker/core-primitives/time-utils/src/lib.rs @@ -0,0 +1,79 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +//! General time utility functions. +#![feature(trait_alias)] +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate chrono_sgx as chrono; + +use chrono::{offset::FixedOffset, DateTime, Utc}; +use std::{ + string::String, + time::{Duration, SystemTime}, +}; + +/// Returns the current timestamp based on the unix epoch in seconds. +pub fn now_as_secs() -> u64 { + duration_now().as_secs() +} + +/// Returns current duration since unix epoch in millis as u64. +pub fn now_as_millis() -> u64 { + duration_now().as_millis() as u64 +} + +pub fn now_as_iso8601() -> String { + let date_time: DateTime = SystemTime::now().into(); + date_time.to_rfc3339() +} + +pub fn from_iso8601(datetime_str: &str) -> Option> { + DateTime::parse_from_rfc3339(datetime_str).ok() +} + +/// Returns the current timestamp based on the unix epoch in nanoseconds. +pub fn now_as_nanos() -> u128 { + duration_now().as_nanos() +} + +/// Calculates the remaining time from now to `until`. +pub fn remaining_time(until: Duration) -> Option { + duration_difference(duration_now(), until) +} + +/// Calculate the difference in duration between `from` and `to`. +/// Returns `None` if `to` < `from`. +pub fn duration_difference(from: Duration, to: Duration) -> Option { + to.checked_sub(from) +} + +/// Returns current duration since unix epoch with SystemTime::now(). +/// Note: subsequent calls are not guaranteed to be monotonic. +/// (https://doc.rust-lang.org/std/time/struct.SystemTime.html) +pub fn duration_now() -> Duration { + let now = SystemTime::now(); + now.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_else(|e| { + panic!("Current time {:?} is before unix epoch. Something is wrong: {:?}", now, e) + }) +} diff --git a/bitacross-worker/core-primitives/top-pool-author/Cargo.toml b/bitacross-worker/core-primitives/top-pool-author/Cargo.toml new file mode 100644 index 0000000000..191ae19ea0 --- /dev/null +++ b/bitacross-worker/core-primitives/top-pool-author/Cargo.toml @@ -0,0 +1,78 @@ +[package] +name = "itp-top-pool-author" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +# sgx dependencies +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } + +# local dependencies +itp-enclave-metrics = { path = "../enclave-metrics", default-features = false } +itp-ocall-api = { path = "../ocall-api", default-features = false } +itp-sgx-crypto = { path = "../sgx/crypto", default-features = false } +itp-stf-primitives = { path = "../stf-primitives", default-features = false } +itp-stf-state-handler = { path = "../stf-state-handler", default-features = false } +itp-test = { path = "../test", default-features = false, optional = true } +itp-top-pool = { path = "../top-pool", default-features = false } +itp-types = { path = "../types", default-features = false } + +# sgx enabled external libraries +jsonrpc-core_sgx = { package = "jsonrpc-core", git = "https://github.com/scs/jsonrpc", branch = "no_std_v18", default-features = false, optional = true } + +# std compatible external libraries (make sure these versions match with the sgx-enabled ones above) +jsonrpc-core = { version = "18", optional = true } + +# no-std compatible libraries +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +derive_more = { version = "0.99.5" } +log = { version = "0.4", default-features = false } +sp-core = { default-features = false, features = ["full_crypto"], git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +lazy_static = { version = "1.1.0", optional = true } + +# litentry +itp-utils = { path = "../utils", default-features = false } +litentry-primitives = { path = "../../litentry/primitives", default-features = false } + +[dev-dependencies] +futures = { version = "0.3" } +itp-sgx-crypto = { path = "../sgx/crypto", features = ["mocks"] } +itp-test = { path = "../test" } +itp-top-pool = { path = "../top-pool", features = ["mocks"] } +sgx-crypto-helper = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", package = "sgx_crypto_helper", default-features = false } +sp-keyring = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + + +[features] +default = ["std"] +std = [ + "itp-sgx-crypto/std", + "itp-enclave-metrics/std", + "itp-ocall-api/std", + "itp-stf-state-handler/std", + "itp-top-pool/std", + "itp-types/std", + "jsonrpc-core", + "log/std", + # litentry + "litentry-primitives/std", + "itp-utils/std", +] +sgx = [ + "sgx_tstd", + "jsonrpc-core_sgx", + "itp-enclave-metrics/sgx", + "itp-sgx-crypto/sgx", + "itp-stf-state-handler/sgx", + "itp-top-pool/sgx", + # litentry + "litentry-primitives/sgx", +] +test = ["itp-test/sgx", "itp-top-pool/mocks"] +mocks = ["lazy_static"] +sidechain = [] +offchain-worker = [] +teeracle = [] diff --git a/bitacross-worker/core-primitives/top-pool-author/src/api.rs b/bitacross-worker/core-primitives/top-pool-author/src/api.rs new file mode 100644 index 0000000000..7214e184e3 --- /dev/null +++ b/bitacross-worker/core-primitives/top-pool-author/src/api.rs @@ -0,0 +1,174 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Chain api required for the operation pool. + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; +use core::fmt::Debug; + +use crate::error; +use codec::Encode; +use itp_stf_primitives::{ + traits::{PoolTransactionValidation, TrustedCallVerification}, + types::ShardIdentifier, +}; +use itp_top_pool::{ + pool::{ChainApi, NumberFor}, + primitives::{TrustedOperationSource, TxHash}, +}; +use itp_types::BlockHash as SidechainBlockHash; +use jsonrpc_core::futures::future::{ready, Future, Ready}; +use log::*; +use sp_runtime::{ + generic::BlockId, + traits::{BlakeTwo256, Block as BlockT, Hash as HashT}, + transaction_validity::TransactionValidity, +}; +use std::{boxed::Box, marker::PhantomData, pin::Pin}; + +/// Future that resolves to account nonce. +pub type Result = core::result::Result; + +/// The operation pool logic for full client. +pub struct SidechainApi { + _marker: PhantomData<(Block, TCS)>, +} + +impl SidechainApi +where + TCS: PartialEq + TrustedCallVerification + Debug, +{ + /// Create new operation pool logic. + pub fn new() -> Self { + SidechainApi { _marker: Default::default() } + } +} + +impl Default for SidechainApi +where + TCS: PartialEq + TrustedCallVerification + Debug + Sync + Send, +{ + fn default() -> Self { + Self::new() + } +} + +impl ChainApi for SidechainApi +where + Block: BlockT, + TCS: PartialEq + TrustedCallVerification + Sync + Send + Debug, +{ + type Block = Block; + type Error = error::Error; + type ValidationFuture = + Pin> + Send>>; + type BodyFuture = Ready>>; + + fn validate_transaction( + &self, + _source: TrustedOperationSource, + uxt: TOP, + _shard: ShardIdentifier, + ) -> Self::ValidationFuture { + let operation = uxt.validate(); + Box::pin(ready(Ok(operation))) + } + + fn block_id_to_number( + &self, + at: &BlockId, + ) -> error::Result>> { + Ok(match at { + BlockId::Number(num) => Some(*num), + BlockId::Hash(_) => None, + }) + } + + fn block_id_to_hash( + &self, + at: &BlockId, + ) -> error::Result> { + Ok(match at { + //BlockId::Hash(x) => Some(x.clone()), + BlockId::Hash(_x) => None, + // dummy + BlockId::Number(_num) => None, + }) + } + + fn hash_and_length(&self, ex: &TOP) -> (TxHash, usize) { + debug!("[Pool] creating hash of {:?}", ex); + ex.using_encoded(|x| (BlakeTwo256::hash(x), x.len())) + } + + fn block_body(&self, _id: &BlockId) -> Self::BodyFuture { + ready(Ok(None)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use futures::executor; + use itp_stf_primitives::types::ShardIdentifier; + use itp_test::mock::stf_mock::{ + mock_top_indirect_trusted_call_signed, mock_top_public_getter, TrustedCallSignedMock, + }; + use itp_types::{AccountId, Block as ParentchainBlock}; + use sp_core::{ed25519, Pair}; + + type TestChainApi = SidechainApi; + + type Seed = [u8; 32]; + const TEST_SEED: Seed = *b"12345678901234567890123456789012"; + + pub fn endowed_account() -> ed25519::Pair { + ed25519::Pair::from_seed(&[42u8; 32].into()) + } + + #[test] + fn indirect_calls_are_valid() { + let chain_api = TestChainApi::default(); + let _account: AccountId = endowed_account().public().into(); + let operation = mock_top_indirect_trusted_call_signed(); + + let validation = executor::block_on(chain_api.validate_transaction( + TrustedOperationSource::Local, + operation, + ShardIdentifier::default(), + )) + .unwrap(); + + assert!(validation.is_ok()); + } + + #[test] + fn public_getters_are_not_valid() { + let chain_api = TestChainApi::default(); + let public_getter = mock_top_public_getter(); + + let validation = executor::block_on(chain_api.validate_transaction( + TrustedOperationSource::Local, + public_getter, + ShardIdentifier::default(), + )) + .unwrap(); + + assert!(validation.is_err()); + } +} diff --git a/bitacross-worker/core-primitives/top-pool-author/src/author.rs b/bitacross-worker/core-primitives/top-pool-author/src/author.rs new file mode 100644 index 0000000000..08cbd61ff7 --- /dev/null +++ b/bitacross-worker/core-primitives/top-pool-author/src/author.rs @@ -0,0 +1,582 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; +use core::fmt::Debug; + +use crate::{ + client_error::Error as ClientError, + error::{Error as StateRpcError, Result}, + top_filter::Filter, + traits::{AuthorApi, OnBlockImported}, +}; +use codec::{Decode, Encode}; +use itp_enclave_metrics::EnclaveMetric; +use itp_ocall_api::EnclaveMetricsOCallApi; +use itp_sgx_crypto::{key_repository::AccessKey, ShieldingCryptoDecrypt}; +use itp_stf_primitives::{ + traits::{PoolTransactionValidation, TrustedCallVerification}, + types::{AccountId, Hash, TrustedOperation as StfTrustedOperation, TrustedOperationOrHash}, +}; +use itp_stf_state_handler::query_shard_state::QueryShardState; +use itp_top_pool::{ + error::{Error as PoolError, IntoPoolError}, + primitives::{ + BlockHash, InPoolOperation, PoolFuture, PoolStatus, TrustedOperationPool, + TrustedOperationSource, TxHash, + }, +}; +use itp_types::{BlockHash as SidechainBlockHash, DecryptableRequest, ShardIdentifier}; +use itp_utils::hex::ToHexPrefixed; +use jsonrpc_core::{ + futures::future::{ready, TryFutureExt}, + Error as RpcError, +}; +use litentry_primitives::BroadcastedRequest; +use log::*; +use sp_runtime::generic; +use std::{ + boxed::Box, + string::String, + sync::{mpsc::SyncSender, Arc}, + vec::Vec, +}; + +/// Define type of TOP filter that is used in the Author +#[cfg(feature = "sidechain")] +pub type AuthorTopFilter = crate::top_filter::CallsOnlyFilter; +#[cfg(feature = "sidechain")] +pub type BroadcastedTopFilter = crate::top_filter::DirectCallsOnlyFilter; + +#[cfg(feature = "offchain-worker")] +pub type AuthorTopFilter = crate::top_filter::IndirectCallsOnlyFilter; +#[cfg(feature = "offchain-worker")] +pub type BroadcastedTopFilter = crate::top_filter::DenyAllFilter; + +#[cfg(feature = "teeracle")] // Teeracle currently does not process any trusted operations +pub type AuthorTopFilter = crate::top_filter::DenyAllFilter; +#[cfg(feature = "teeracle")] +pub type BroadcastedTopFilter = crate::top_filter::DenyAllFilter; + +#[cfg(not(any(feature = "sidechain", feature = "offchain-worker", feature = "teeracle")))] +pub type AuthorTopFilter = crate::top_filter::CallsOnlyFilter; + +#[cfg(not(any(feature = "sidechain", feature = "offchain-worker", feature = "teeracle")))] +pub type BroadcastedTopFilter = crate::top_filter::DenyAllFilter; + +/// Currently we treat all RPC operations as externals. +/// +/// Possibly in the future we could allow opt-in for special treatment +/// of such operations, so that the block authors can inject +/// some unique operations via RPC and have them included in the pool. +const TX_SOURCE: TrustedOperationSource = TrustedOperationSource::External; + +// remove duplication of this type definiton ? +pub type RequestIdWithParamsAndMethod = Option<(Hash, Vec)>; + +/// Authoring API for RPC calls +/// +/// +pub struct Author< + TopPool, + TopFilter, + BroadcastedTopFilter, + StateFacade, + ShieldingKeyRepository, + OCallApi, + TCS, + G, +> where + TopPool: TrustedOperationPool> + Sync + Send + 'static, + TopFilter: Filter>, + BroadcastedTopFilter: Filter>, + StateFacade: QueryShardState, + ShieldingKeyRepository: AccessKey, + ::KeyType: ShieldingCryptoDecrypt + 'static, + TCS: PartialEq + Encode + Clone + Debug + Send + Sync, + G: PartialEq + Encode + Clone + PoolTransactionValidation + Debug + Send + Sync, +{ + top_pool: Arc, + top_filter: TopFilter, + broadcasted_top_filter: BroadcastedTopFilter, + state_facade: Arc, + shielding_key_repo: Arc, + ocall_api: Arc, + request_sink: Arc>, +} + +impl< + TopPool, + TopFilter, + BroadcastedTopFilter, + StateFacade, + ShieldingKeyRepository, + OCallApi, + TCS, + G, + > + Author< + TopPool, + TopFilter, + BroadcastedTopFilter, + StateFacade, + ShieldingKeyRepository, + OCallApi, + TCS, + G, + > where + TopPool: TrustedOperationPool> + Sync + Send + 'static, + TopFilter: Filter>, + BroadcastedTopFilter: Filter>, + StateFacade: QueryShardState, + ShieldingKeyRepository: AccessKey, + ::KeyType: ShieldingCryptoDecrypt + 'static, + OCallApi: EnclaveMetricsOCallApi + Send + Sync + 'static, + TCS: PartialEq + Encode + Clone + Debug + Send + Sync, + G: PartialEq + Encode + Clone + PoolTransactionValidation + Debug + Send + Sync, +{ + /// Create new instance of Authoring API. + pub fn new( + top_pool: Arc, + top_filter: TopFilter, + broadcasted_top_filter: BroadcastedTopFilter, + state_facade: Arc, + encryption_key: Arc, + ocall_api: Arc, + request_sink: Arc>, + ) -> Self { + Author { + top_pool, + top_filter, + broadcasted_top_filter, + state_facade, + shielding_key_repo: encryption_key, + ocall_api, + request_sink, + } + } +} + +enum TopSubmissionMode { + Submit, + SubmitWatch, + SubmitWatchAndBroadcast(String), +} + +impl< + TopPool, + TopFilter, + BroadcastedTopFilter, + StateFacade, + ShieldingKeyRepository, + OCallApi, + TCS, + G, + > + Author< + TopPool, + TopFilter, + BroadcastedTopFilter, + StateFacade, + ShieldingKeyRepository, + OCallApi, + TCS, + G, + > where + TopPool: TrustedOperationPool> + Sync + Send + 'static, + TopFilter: Filter>, + BroadcastedTopFilter: Filter>, + StateFacade: QueryShardState, + ShieldingKeyRepository: AccessKey, + ::KeyType: ShieldingCryptoDecrypt + 'static, + OCallApi: EnclaveMetricsOCallApi + Send + Sync + 'static, + TCS: PartialEq + + Encode + + Decode + + Clone + + Debug + + Send + + Sync + + TrustedCallVerification + + 'static, + G: PartialEq + + Encode + + Decode + + Clone + + PoolTransactionValidation + + Debug + + Send + + Sync + + 'static, +{ + fn process_top( + &self, + mut request: R, + submission_mode: TopSubmissionMode, + ) -> PoolFuture { + let shard = request.shard(); + + //we need to save it here as other function may eventually mutate it + let request_to_broadcast = request.to_hex(); + // check if shard exists + match self.state_facade.shard_exists(&shard) { + Err(_) => return Box::pin(ready(Err(ClientError::InvalidShard.into()))), + Ok(shard_exists) => + if !shard_exists { + return Box::pin(ready(Err(ClientError::InvalidShard.into()))) + }, + }; + + // decrypt call + let shielding_key = match self.shielding_key_repo.retrieve_key() { + Ok(k) => k, + Err(_) => return Box::pin(ready(Err(ClientError::BadFormatDecipher.into()))), + }; + let request_vec = match request.decrypt(Box::new(shielding_key)) { + Ok(req) => req, + Err(_) => return Box::pin(ready(Err(ClientError::BadFormatDecipher.into()))), + }; + // decode call + let trusted_operation = + match StfTrustedOperation::::decode(&mut request_vec.as_slice()) { + Ok(op) => op, + Err(_) => return Box::pin(ready(Err(ClientError::BadFormat.into()))), + }; + + trace!("decrypted indirect invocation: {:?}", trusted_operation); + + // apply top filter - return error if this specific type of trusted operation + // is not allowed by the filter + if !self.top_filter.filter(&trusted_operation) { + warn!("unsupported operation"); + return Box::pin(ready(Err(ClientError::UnsupportedOperation.into()))) + } + + //let best_block_hash = self.client.info().best_hash; + // dummy block hash + let best_block_hash = Default::default(); + + // Update metric + if let Err(e) = self.ocall_api.update_metric(EnclaveMetric::TopPoolSizeIncrement) { + warn!("Failed to update metric for top pool size: {:?}", e); + } + + if let Some(trusted_call_signed) = trusted_operation.to_call() { + debug!( + "Submitting trusted call to TOP pool: {:?}, TOP hash: {:?}", + trusted_call_signed, + self.hash_of(&trusted_operation) + ); + } else if let StfTrustedOperation::::get(ref getter) = trusted_operation { + debug!( + "Submitting trusted or public getter to TOP pool: {:?}, TOP hash: {:?}", + getter, + self.hash_of(&trusted_operation) + ); + } + + match submission_mode { + TopSubmissionMode::Submit => Box::pin( + self.top_pool + .submit_one( + &generic::BlockId::hash(best_block_hash), + TX_SOURCE, + trusted_operation, + shard, + ) + .map_err(map_top_error::), + ), + + TopSubmissionMode::SubmitWatch => Box::pin( + self.top_pool + .submit_and_watch( + &generic::BlockId::hash(best_block_hash), + TX_SOURCE, + trusted_operation, + shard, + ) + .map_err(map_top_error::), + ), + + TopSubmissionMode::SubmitWatchAndBroadcast(s) => { + let id = self.hash_of(&trusted_operation).to_hex(); + let can_be_broadcasted = self.broadcasted_top_filter.filter(&trusted_operation); + let result = Box::pin( + self.top_pool + .submit_and_watch( + &generic::BlockId::hash(best_block_hash), + TX_SOURCE, + trusted_operation, + shard, + ) + .map_err(map_top_error::), + ); + // broadcast only if filter allowed + if can_be_broadcasted { + if let Err(e) = self.request_sink.send(BroadcastedRequest { + id, + payload: request_to_broadcast, + rpc_method: s, + }) { + error!("Could not send broadcasted request, reason: {:?}", e); + } + } + result + }, + } + } + + fn remove_top( + &self, + bytes_or_hash: TrustedOperationOrHash, + shard: ShardIdentifier, + inblock: bool, + ) -> Result { + let hash = match bytes_or_hash { + TrustedOperationOrHash::Hash(h) => Ok(h), + TrustedOperationOrHash::OperationEncoded(bytes) => { + match Decode::decode(&mut bytes.as_slice()) { + Ok(op) => Ok(self.top_pool.hash_of(&op)), + Err(e) => { + error!("Failed to decode trusted operation: {:?}, operation will not be removed from pool", e); + Err(StateRpcError::CodecError(e)) + }, + } + }, + TrustedOperationOrHash::Operation(op) => Ok(self.top_pool.hash_of(&op)), + }?; + + debug!("removing {:?} from top pool", hash); + + // Update metric + if let Err(e) = self.ocall_api.update_metric(EnclaveMetric::TopPoolSizeDecrement) { + warn!("Failed to update metric for top pool size: {:?}", e); + } + + let removed_op_hash = self + .top_pool + .remove_invalid(&[hash], shard, inblock) + // Only remove a single element, so first should return Ok(). + .first() + .map(|o| o.hash()) + .ok_or(PoolError::InvalidTrustedOperation)?; + + Ok(removed_op_hash) + } +} + +fn map_top_error>, TCS, G>( + error: P::Error, +) -> RpcError +where + TCS: PartialEq + Encode + Debug, + G: PartialEq + Encode + Debug, +{ + StateRpcError::PoolError( + error + .into_pool_error() + .map(Into::into) + .unwrap_or_else(|_error| PoolError::Verification), + ) + .into() +} + +impl< + TopPool, + TopFilter, + BroadcastedTopFilter, + StateFacade, + ShieldingKeyRepository, + OCallApi, + TCS, + G, + > AuthorApi + for Author< + TopPool, + TopFilter, + BroadcastedTopFilter, + StateFacade, + ShieldingKeyRepository, + OCallApi, + TCS, + G, + > where + TopPool: TrustedOperationPool> + Sync + Send + 'static, + TopFilter: Filter>, + BroadcastedTopFilter: Filter>, + StateFacade: QueryShardState, + ShieldingKeyRepository: AccessKey, + ::KeyType: ShieldingCryptoDecrypt + 'static, + OCallApi: EnclaveMetricsOCallApi + Send + Sync + 'static, + G: PartialEq + + Encode + + Decode + + Clone + + PoolTransactionValidation + + Debug + + Send + + Sync + + 'static, + TCS: PartialEq + + Encode + + Decode + + Clone + + Debug + + Send + + Sync + + TrustedCallVerification + + 'static, +{ + fn submit_top(&self, req: R) -> PoolFuture { + self.process_top(req, TopSubmissionMode::Submit) + } + + /// Get hash of TrustedOperation + fn hash_of(&self, xt: &StfTrustedOperation) -> TxHash { + self.top_pool.hash_of(xt) + } + + fn pending_tops(&self, shard: ShardIdentifier) -> Result>> { + Ok(self.top_pool.ready(shard).map(|top| top.data().encode()).collect()) + } + + fn get_pending_getters(&self, shard: ShardIdentifier) -> Vec> { + self.top_pool + .ready(shard) + .filter_map(|o| match o.data() { + StfTrustedOperation::::get(_) => Some(o.data().clone()), + StfTrustedOperation::::direct_call(_) + | StfTrustedOperation::::indirect_call(_) => None, + }) + .collect() + } + + fn get_pending_trusted_calls( + &self, + shard: ShardIdentifier, + ) -> Vec> { + self.top_pool + .ready(shard) + .filter_map(|o| match o.data() { + StfTrustedOperation::::direct_call(_) + | StfTrustedOperation::::indirect_call(_) => Some(o.data().clone()), + StfTrustedOperation::::get(_) => None, + }) + .collect() + } + + fn get_status(&self, shard: ShardIdentifier) -> PoolStatus { + self.top_pool.status(shard) + } + + fn get_pending_trusted_calls_for( + &self, + shard: ShardIdentifier, + account: &AccountId, + ) -> Vec> { + self.get_pending_trusted_calls(shard) + .into_iter() + .filter(|o| o.signed_caller_account().as_ref() == Some(account)) + .collect() + } + + fn get_shards(&self) -> Vec { + self.top_pool.shards() + } + + fn list_handled_shards(&self) -> Vec { + self.state_facade.list_shards().unwrap_or_default() + } + + fn remove_calls_from_pool( + &self, + shard: ShardIdentifier, + executed_calls: Vec<(TrustedOperationOrHash, bool)>, + ) -> Vec> { + let mut failed_to_remove = Vec::new(); + for (executed_call, inblock) in executed_calls { + if let Err(e) = self.remove_top(executed_call.clone(), shard, inblock) { + // We don't want to return here before all calls have been iterated through, + // hence log message and collect failed calls in vec. + debug!("Error removing trusted call from top pool: {:?}", e); + failed_to_remove.push(executed_call); + } + } + failed_to_remove + } + + fn watch_top( + &self, + request: R, + ) -> PoolFuture { + self.process_top(request, TopSubmissionMode::SubmitWatch) + } + + fn watch_and_broadcast_top( + &self, + request: R, + json_rpc_method: String, + ) -> PoolFuture { + self.process_top(request, TopSubmissionMode::SubmitWatchAndBroadcast(json_rpc_method)) + } + + fn update_connection_state(&self, updates: Vec<(TxHash, (Vec, bool))>) { + self.top_pool.update_connection_state(updates) + } + + fn swap_rpc_connection_hash(&self, old_hash: TxHash, new_hash: TxHash) { + self.top_pool.swap_rpc_connection_hash(old_hash, new_hash) + } +} + +impl< + TopPool, + TopFilter, + BroadcastedTopFilter, + StateFacade, + ShieldingKeyRepository, + OCallApi, + TCS, + G, + > OnBlockImported + for Author< + TopPool, + TopFilter, + BroadcastedTopFilter, + StateFacade, + ShieldingKeyRepository, + OCallApi, + TCS, + G, + > where + TopPool: TrustedOperationPool> + Sync + Send + 'static, + TopFilter: Filter>, + BroadcastedTopFilter: Filter>, + StateFacade: QueryShardState, + ShieldingKeyRepository: AccessKey, + ::KeyType: ShieldingCryptoDecrypt + 'static, + OCallApi: EnclaveMetricsOCallApi + Send + Sync + 'static, + G: PartialEq + Encode + Clone + PoolTransactionValidation + Debug + Send + Sync, + TCS: PartialEq + Encode + Clone + Debug + Send + Sync, +{ + type Hash = TxHash; + + fn on_block_imported(&self, hashes: &[Self::Hash], block_hash: SidechainBlockHash) { + self.top_pool.on_block_imported(hashes, block_hash) + } +} diff --git a/bitacross-worker/core-primitives/top-pool-author/src/author_tests.rs b/bitacross-worker/core-primitives/top-pool-author/src/author_tests.rs new file mode 100644 index 0000000000..3fb0370970 --- /dev/null +++ b/bitacross-worker/core-primitives/top-pool-author/src/author_tests.rs @@ -0,0 +1,203 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + author::Author, + test_fixtures::shard_id, + test_utils::submit_operation_to_top_pool, + top_filter::{AllowAllTopsFilter, DirectCallsOnlyFilter, Filter, GettersOnlyFilter}, + traits::AuthorApi, +}; +use codec::{Decode, Encode}; +use itp_sgx_crypto::{mocks::KeyRepositoryMock, ShieldingCryptoDecrypt, ShieldingCryptoEncrypt}; + +use itp_stf_state_handler::handle_state::HandleState; +use itp_test::mock::{ + handle_state_mock::HandleStateMock, + metrics_ocall_mock::MetricsOCallMock, + shielding_crypto_mock::ShieldingCryptoMock, + stf_mock::{ + mock_top_direct_trusted_call_signed, mock_top_indirect_trusted_call_signed, + mock_top_trusted_getter_signed, GetterMock, TrustedCallSignedMock, TrustedOperationMock, + }, +}; +use itp_top_pool::mocks::trusted_operation_pool_mock::TrustedOperationPoolMock; +use itp_utils::ToHexPrefixed; +use litentry_primitives::BroadcastedRequest; +use sgx_crypto_helper::{rsa3072::Rsa3072KeyPair, RsaKeyPair}; +use sp_core::H256; +use std::sync::Arc; + +type TestAuthor = Author< + TrustedOperationPoolMock, + Filter, + BroadcastedFilter, + HandleStateMock, + KeyRepositoryMock, + MetricsOCallMock, + TrustedCallSignedMock, + GetterMock, +>; + +#[test] +fn top_encryption_works() { + let top_call = mock_top_direct_trusted_call_signed(); + let top_getter = mock_top_trusted_getter_signed(); + assert_eq!(top_call, encrypt_and_decrypt_top(&top_call)); + assert_eq!(top_getter, encrypt_and_decrypt_top(&top_getter)); +} + +fn encrypt_and_decrypt_top(top: &TrustedOperationMock) -> TrustedOperationMock { + let encryption_key = Rsa3072KeyPair::new().unwrap(); + let encrypted_top = encryption_key.encrypt(top.encode().as_slice()).unwrap(); + let decrypted_top = encryption_key.decrypt(encrypted_top.as_slice()).unwrap(); + + TrustedOperationMock::decode(&mut decrypted_top.as_slice()).unwrap() +} + +#[test] +fn submitting_to_author_inserts_in_pool() { + let (author, top_pool, shielding_key, _) = + create_author_with_filter(AllowAllTopsFilter::new(), DirectCallsOnlyFilter::new()); + let top_getter = mock_top_trusted_getter_signed(); + + let submit_response = + submit_operation_to_top_pool(&author, &top_getter, &shielding_key, shard_id(), false) + .unwrap(); + + assert!(!submit_response.0.is_zero()); + + let submitted_transactions = top_pool.get_last_submitted_transactions(); + assert_eq!(1, submitted_transactions.len()); +} + +#[test] +fn submitting_call_to_author_when_top_is_filtered_returns_error() { + let (author, top_pool, shielding_key, _) = + create_author_with_filter(GettersOnlyFilter::new(), DirectCallsOnlyFilter::new()); + let top_call = mock_top_direct_trusted_call_signed(); + let submit_response = + submit_operation_to_top_pool(&author, &top_call, &shielding_key, shard_id(), false); + + assert!(submit_response.is_err()); + assert!(top_pool.get_last_submitted_transactions().is_empty()); +} + +#[test] +fn submitting_getter_to_author_when_top_is_filtered_inserts_in_pool() { + let (author, top_pool, shielding_key, _) = + create_author_with_filter(GettersOnlyFilter::new(), DirectCallsOnlyFilter::new()); + let top_getter = mock_top_trusted_getter_signed(); + let submit_response = + submit_operation_to_top_pool(&author, &top_getter, &shielding_key, shard_id(), false) + .unwrap(); + + assert!(!submit_response.0.is_zero()); + assert_eq!(1, top_pool.get_last_submitted_transactions().len()); +} + +#[test] +fn submitting_direct_call_works() { + let (author, top_pool, shielding_key, _) = + create_author_with_filter(AllowAllTopsFilter::new(), DirectCallsOnlyFilter::new()); + let top_call = mock_top_direct_trusted_call_signed(); + let _ = submit_operation_to_top_pool(&author, &top_call, &shielding_key, shard_id(), false) + .unwrap(); + + assert_eq!(1, top_pool.get_last_submitted_transactions().len()); + assert_eq!(1, author.get_pending_trusted_calls(shard_id()).len()); +} + +#[test] +fn broadcasting_direct_call_works() { + let (author, _top_pool, shielding_key, broadcasted_requests_rx) = + create_author_with_filter(AllowAllTopsFilter::new(), DirectCallsOnlyFilter::new()); + let top_call = mock_top_direct_trusted_call_signed(); + + let (hash, request) = + submit_operation_to_top_pool(&author, &top_call, &shielding_key, shard_id(), true).unwrap(); + + let broadcasted_request = broadcasted_requests_rx.try_recv().unwrap(); + assert_eq!(broadcasted_request.rpc_method, "submit_and_watch".to_owned()); + assert_eq!(broadcasted_request.id, hash.to_hex()); + assert_eq!(broadcasted_request.payload, request.to_hex()); +} + +#[test] +fn not_broadcasting_indirect_call_works() { + let (author, _top_pool, shielding_key, broadcasted_requests_rx) = + create_author_with_filter(AllowAllTopsFilter::new(), DirectCallsOnlyFilter::new()); + let top_call = mock_top_indirect_trusted_call_signed(); + + let _ = + submit_operation_to_top_pool(&author, &top_call, &shielding_key, shard_id(), true).unwrap(); + + assert!(broadcasted_requests_rx.try_recv().is_err()) +} + +#[test] +fn submitting_indirect_call_works() { + let (author, top_pool, shielding_key, _) = + create_author_with_filter(AllowAllTopsFilter::new(), DirectCallsOnlyFilter::new()); + let top_call = mock_top_indirect_trusted_call_signed(); + let _ = submit_operation_to_top_pool(&author, &top_call, &shielding_key, shard_id(), false) + .unwrap(); + + assert_eq!(1, top_pool.get_last_submitted_transactions().len()); + assert_eq!(1, author.get_pending_trusted_calls(shard_id()).len()); +} + +fn create_author_with_filter< + F: Filter, + BF: Filter, +>( + filter: F, + broadcasted_filter: BF, +) -> ( + TestAuthor, + Arc>, + ShieldingCryptoMock, + std::sync::mpsc::Receiver, +) { + let top_pool = Arc::new(TrustedOperationPoolMock::default()); + + let shard_id = shard_id(); + let state_facade = HandleStateMock::from_shard(shard_id).unwrap(); + state_facade.load_cloned(&shard_id).unwrap(); + + let encryption_key = ShieldingCryptoMock::default(); + let shielding_key_repo = + Arc::new(KeyRepositoryMock::::new(encryption_key.clone())); + let ocall_mock = Arc::new(MetricsOCallMock::default()); + + let (sender, receiver) = std::sync::mpsc::sync_channel::(1000); + + ( + Author::new( + top_pool.clone(), + filter, + broadcasted_filter, + Arc::new(state_facade), + shielding_key_repo, + ocall_mock, + Arc::new(sender), + ), + top_pool, + encryption_key, + receiver, + ) +} diff --git a/bitacross-worker/core-primitives/top-pool-author/src/client_error.rs b/bitacross-worker/core-primitives/top-pool-author/src/client_error.rs new file mode 100644 index 0000000000..badd278008 --- /dev/null +++ b/bitacross-worker/core-primitives/top-pool-author/src/client_error.rs @@ -0,0 +1,183 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Authoring RPC module client errors. + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use derive_more::{Display, From}; +use jsonrpc_core as rpc_core; +use std::{boxed::Box, format}; + +/// Author RPC Result type. +pub type Result = core::result::Result; + +/// Author RPC errors. +#[derive(Debug, Display, From)] +pub enum Error { + /// Client error. + #[display(fmt = "Client error: {}", _0)] + #[from(ignore)] + Client(Box), + /// TrustedOperation pool error, + #[display(fmt = "TrustedOperation pool error: {}", _0)] + Pool(itp_top_pool::error::Error), + /// Verification error + #[display(fmt = "Extrinsic verification error")] + #[from(ignore)] + Verification, + /// Incorrect extrinsic format. + #[display(fmt = "Invalid trusted call format")] + BadFormat, + // Incorrect enciphered trusted call format. + #[display(fmt = "Invalid enciphered trusted call format")] + BadFormatDecipher, + /// Incorrect seed phrase. + #[display(fmt = "Invalid seed phrase/SURI")] + BadSeedPhrase, + /// Key type ID has an unknown format. + #[display(fmt = "Invalid key type ID format (should be of length four)")] + BadKeyType, + /// Key type ID has some unsupported crypto. + #[display(fmt = "The crypto of key type ID is unknown")] + UnsupportedKeyType, + /// Some random issue with the key store. Shouldn't happen. + #[display(fmt = "The key store is unavailable")] + KeyStoreUnavailable, + /// Invalid session keys encoding. + #[display(fmt = "Session keys are not encoded correctly")] + InvalidSessionKeys, + /// Shard does not exist. + #[display(fmt = "Shard does not exist")] + InvalidShard, + /// Unsupported trusted operation (in case we allow only certain types of operations, using filters) + #[display(fmt = "Unsupported operation type")] + UnsupportedOperation, +} + +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Error::Client(ref err) => Some(&**err), + //Error::Pool(ref err) => Some(err), + //Error::Verification(ref err) => Some(&**err), + _ => None, + } + } +} + +/// Base code for all authorship errors. +const BASE_ERROR: i64 = 1000; +/// Extrinsic has an invalid format. +const BAD_FORMAT: i64 = BASE_ERROR + 1; +/// Error during operation verification in runtime. +const VERIFICATION_ERROR: i64 = BASE_ERROR + 2; + +/// Pool rejected the operation as invalid +const POOL_INVALID_TX: i64 = BASE_ERROR + 10; +/// Cannot determine operation validity. +const POOL_UNKNOWN_VALIDITY: i64 = POOL_INVALID_TX + 1; +/// The operation is temporarily banned. +const POOL_TEMPORARILY_BANNED: i64 = POOL_INVALID_TX + 2; +/// The operation is already in the pool +const POOL_ALREADY_IMPORTED: i64 = POOL_INVALID_TX + 3; +/// TrustedOperation has too low priority to replace existing one in the pool. +const POOL_TOO_LOW_PRIORITY: i64 = POOL_INVALID_TX + 4; +/// Including this operation would cause a dependency cycle. +const POOL_CYCLE_DETECTED: i64 = POOL_INVALID_TX + 5; +/// The operation was not included to the pool because of the limits. +const POOL_IMMEDIATELY_DROPPED: i64 = POOL_INVALID_TX + 6; +/// The key type crypto is not known. +const UNSUPPORTED_KEY_TYPE: i64 = POOL_INVALID_TX + 7; + +impl From for rpc_core::Error { + fn from(e: Error) -> Self { + use itp_top_pool::error::Error as PoolError; + + match e { + Error::BadFormat => rpc_core::Error { + code: rpc_core::ErrorCode::ServerError(BAD_FORMAT), + message: "Trusted operation has invalid format".into(), + data: None, + }, + Error::BadFormatDecipher => rpc_core::Error { + code: rpc_core::ErrorCode::ServerError(BAD_FORMAT), + message: "Trusted operation could not be deciphered".into(), + data: None, + }, + Error::Verification => rpc_core::Error { + code: rpc_core::ErrorCode::ServerError(VERIFICATION_ERROR), + message: "Verification Error".into(), + data: Some(format!("{:?}", e).into()), + }, + Error::InvalidShard => rpc_core::Error { + code: rpc_core::ErrorCode::ServerError(VERIFICATION_ERROR), + message: "Shard does not exist".into(), + data: Some(format!("{:?}", e).into()), + }, + Error::Pool(PoolError::InvalidTrustedOperation) => rpc_core::Error { + code: rpc_core::ErrorCode::ServerError(POOL_INVALID_TX), + message: "Invalid Trusted Operation".into(), + data: None, + }, + Error::Pool(PoolError::UnknownTrustedOperation) => rpc_core::Error { + code: rpc_core::ErrorCode::ServerError(POOL_UNKNOWN_VALIDITY), + message: "Unknown Trusted Operation Validity".into(), + data: None, + }, + Error::Pool(PoolError::TemporarilyBanned) => rpc_core::Error { + code: rpc_core::ErrorCode::ServerError(POOL_TEMPORARILY_BANNED), + message: "Trusted Operation is temporarily banned".into(), + data: None, + }, + Error::Pool(PoolError::AlreadyImported) => rpc_core::Error { + code: rpc_core::ErrorCode::ServerError(POOL_ALREADY_IMPORTED), + message: "Trusted Operation Already Imported".into(), + data: None, + }, + Error::Pool(PoolError::TooLowPriority(new)) => rpc_core::Error { + code: rpc_core::ErrorCode::ServerError(POOL_TOO_LOW_PRIORITY), + message: format!("Priority is too low: {}", new), + data: Some("The Trusted Operation has too low priority to replace another Trusted Operation already in the pool.".into()), + }, + Error::Pool(PoolError::CycleDetected) => rpc_core::Error { + code: rpc_core::ErrorCode::ServerError(POOL_CYCLE_DETECTED), + message: "Cycle Detected".into(), + data: None, + }, + Error::Pool(PoolError::ImmediatelyDropped) => rpc_core::Error { + code: rpc_core::ErrorCode::ServerError(POOL_IMMEDIATELY_DROPPED), + message: "Immediately Dropped".into(), + data: Some("The Trusted Operation couldn't enter the pool because of the limit".into()), + }, + Error::UnsupportedKeyType => rpc_core::Error { + code: rpc_core::ErrorCode::ServerError(UNSUPPORTED_KEY_TYPE), + message: "Unknown key type crypto" .into(), + data: Some( + "The crypto for the given key type is unknown, please add the public key to the \ + request to insert the key successfully.".into() + ), + }, + e => rpc_core::Error { + code: rpc_core::ErrorCode::InternalError, + message: "Unknown error occurred".into(), + data: Some(format!("{:?}", e).into()), + }, + } + } +} diff --git a/bitacross-worker/core-primitives/top-pool-author/src/error.rs b/bitacross-worker/core-primitives/top-pool-author/src/error.rs new file mode 100644 index 0000000000..1c967a1b82 --- /dev/null +++ b/bitacross-worker/core-primitives/top-pool-author/src/error.rs @@ -0,0 +1,111 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use crate::client_error::Error as ClientError; +use core::pin::Pin; +use derive_more::{Display, From}; +use itp_top_pool::error::{Error as PoolError, IntoPoolError}; +use jsonrpc_core as rpc; +use std::{boxed::Box, error, format, string::String}; + +/// State RPC Result type. +pub type Result = core::result::Result; + +/// State RPC future Result type. +pub type FutureResult = + Pin> + Send>>; + +/// State RPC errors. +#[derive(Debug, Display, From)] +pub enum Error { + /// Client error. + #[display(fmt = "Client error: {}", _0)] + Client(Box), + /// Provided block range couldn't be resolved to a list of blocks. + #[display(fmt = "Cannot resolve a block range ['{:?}' ... '{:?}]. {}", from, to, details)] + InvalidBlockRange { + /// Beginning of the block range. + from: String, + /// End of the block range. + to: String, + /// Details of the error message. + details: String, + }, + /// Provided count exceeds maximum value. + #[display(fmt = "count exceeds maximum value. value: {}, max: {}", value, max)] + InvalidCount { + /// Provided value + value: u32, + /// Maximum allowed value + max: u32, + }, + + /// Wrapping of PoolError to RPC Error + PoolError(PoolError), + + /// Wrapping of ClientError to RPC Error + ClientError(ClientError), + + #[display(fmt = "Codec error: {}", _0)] + CodecError(codec::Error), +} + +impl error::Error for Error { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match self { + Error::Client(ref err) => Some(&**err), + _ => None, + } + } +} + +impl IntoPoolError for Error { + fn into_pool_error(self) -> std::result::Result { + match self { + Error::PoolError(e) => Ok(e), + e => Err(e), + } + } +} + +/// Base code for all state errors. +const BASE_ERROR: i64 = 4000; + +impl From for rpc::Error { + fn from(e: Error) -> Self { + match e { + Error::InvalidBlockRange { .. } => rpc::Error { + code: rpc::ErrorCode::ServerError(BASE_ERROR + 1), + message: format!("{}", e), + data: None, + }, + Error::InvalidCount { .. } => rpc::Error { + code: rpc::ErrorCode::ServerError(BASE_ERROR + 2), + message: format!("{}", e), + data: None, + }, + e => rpc::Error { + code: rpc::ErrorCode::ServerError(BASE_ERROR + 4), + message: format!("{}", e), + data: None, + }, + } + } +} diff --git a/bitacross-worker/core-primitives/top-pool-author/src/lib.rs b/bitacross-worker/core-primitives/top-pool-author/src/lib.rs new file mode 100644 index 0000000000..b0b84b992c --- /dev/null +++ b/bitacross-worker/core-primitives/top-pool-author/src/lib.rs @@ -0,0 +1,51 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#![feature(trait_alias)] +#![cfg_attr(feature = "mocks", feature(drain_filter))] +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +// re-export module to properly feature gate sgx and regular std environment +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod sgx_reexport_prelude { + pub use jsonrpc_core_sgx as jsonrpc_core; +} + +pub mod api; +pub mod author; +pub mod client_error; +pub mod error; +pub mod top_filter; +pub mod traits; + +#[cfg(test)] +mod author_tests; + +#[cfg(test)] +mod test_fixtures; + +#[cfg(any(test, feature = "test"))] +pub mod test_utils; + +#[cfg(feature = "mocks")] +pub mod mocks; diff --git a/bitacross-worker/core-primitives/top-pool-author/src/mocks.rs b/bitacross-worker/core-primitives/top-pool-author/src/mocks.rs new file mode 100644 index 0000000000..d97bb1008c --- /dev/null +++ b/bitacross-worker/core-primitives/top-pool-author/src/mocks.rs @@ -0,0 +1,315 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; +use core::fmt::Debug; + +#[cfg(feature = "sgx")] +use std::sync::SgxRwLock as RwLock; + +#[cfg(feature = "std")] +use std::sync::RwLock; + +use crate::{ + error::Result, + traits::{AuthorApi, OnBlockImported}, +}; +use codec::{Decode, Encode}; +use itp_stf_primitives::{ + traits::TrustedCallVerification, + types::{AccountId, TrustedOperation as StfTrustedOperation, TrustedOperationOrHash}, +}; +use itp_top_pool::primitives::{PoolFuture, PoolStatus}; +use itp_types::{DecryptableRequest, ShardIdentifier}; +use jsonrpc_core::{futures::future::ready, Error as RpcError}; +use lazy_static::lazy_static; +use sp_core::{blake2_256, H256}; +#[cfg(feature = "sgx")] +use std::sync::SgxMutex as Mutex; +use std::{ + boxed::Box, + collections::HashMap, + marker::PhantomData, + string::String, + sync::{mpsc::Sender, Arc}, + vec, + vec::Vec, +}; + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(feature = "std")] +use std::sync::Mutex; + +lazy_static! { + pub static ref GLOBAL_MOCK_AUTHOR_API: Arc>>>> = + Arc::new(Mutex::new(None)); +} + +#[derive(Default)] +pub struct AuthorApiMock +where + TCS: PartialEq + Encode + Decode + Debug + Send + Sync + TrustedCallVerification, + G: PartialEq + Encode + Decode + Debug + Send + Sync, +{ + tops: RwLock>>>, + _phantom: PhantomData<(Hash, BlockHash, TCS, G)>, + pub remove_attempts: RwLock, +} + +impl AuthorApiMock +where + TCS: PartialEq + Encode + Decode + Debug + Send + Sync + TrustedCallVerification, + G: PartialEq + Encode + Decode + Debug + Send + Sync, +{ + fn remove_top( + &self, + bytes_or_hash: Vec>, + shard: ShardIdentifier, + _inblock: bool, + ) -> Result> { + let hashes = bytes_or_hash + .into_iter() + .map(|x| match x { + TrustedOperationOrHash::Hash(h) => h, + TrustedOperationOrHash::OperationEncoded(bytes) => { + let top: StfTrustedOperation = + StfTrustedOperation::::decode(&mut bytes.as_slice()).unwrap(); + top.hash() + }, + TrustedOperationOrHash::Operation(op) => op.hash(), + }) + .collect::>(); + + let mut tops_lock = self.tops.write().unwrap(); + + match tops_lock.get_mut(&shard) { + Some(tops_encoded) => { + let removed_tops = tops_encoded + .drain_filter(|t| hashes.contains(&blake2_256(t).into())) + .map(|t| blake2_256(&t).into()) + .collect::>(); + Ok(removed_tops) + }, + None => Ok(Vec::new()), + } + } +} + +impl AuthorApi for AuthorApiMock +where + TCS: PartialEq + Encode + Decode + Debug + Clone + TrustedCallVerification + Send + Sync, + G: PartialEq + Encode + Decode + Debug + Clone + Send + Sync, +{ + fn submit_top(&self, req: R) -> PoolFuture { + let mut write_lock = self.tops.write().unwrap(); + let extrinsics = write_lock.entry(req.shard()).or_default(); + extrinsics.push(req.payload().to_vec()); + Box::pin(ready(Ok(H256::default()))) + } + + fn hash_of(&self, xt: &StfTrustedOperation) -> H256 { + xt.hash() + } + + fn pending_tops(&self, shard: ShardIdentifier) -> Result>> { + let extrinsics = self.tops.read().unwrap().get(&shard).cloned(); + Ok(extrinsics.unwrap_or_default()) + } + + fn get_pending_getters(&self, shard: ShardIdentifier) -> Vec> { + self.tops + .read() + .unwrap() + .get(&shard) + .map(|encoded_operations| { + let mut trusted_getters: Vec> = Vec::new(); + for encoded_operation in encoded_operations { + if let Ok(g) = G::decode(&mut encoded_operation.as_slice()) { + trusted_getters.push(StfTrustedOperation::::get(g)); + } + } + trusted_getters + }) + .unwrap_or_default() + } + + fn get_pending_trusted_calls( + &self, + shard: ShardIdentifier, + ) -> Vec> { + self.tops + .read() + .unwrap() + .get(&shard) + .map(|encoded_operations| { + let mut trusted_operations: Vec> = Vec::new(); + for encoded_operation in encoded_operations { + if let Ok(o) = StfTrustedOperation::decode(&mut encoded_operation.as_slice()) { + trusted_operations.push(o); + } + } + trusted_operations + }) + .unwrap_or_default() + } + + fn get_status(&self, shard: ShardIdentifier) -> PoolStatus { + self.tops + .read() + .unwrap() + .get(&shard) + .map(|encoded_operations| { + let mut trusted_operations: Vec> = Vec::new(); + for encoded_operation in encoded_operations { + if let Ok(o) = StfTrustedOperation::decode(&mut encoded_operation.as_slice()) { + trusted_operations.push(o); + } + } + PoolStatus { + ready: trusted_operations.len(), + ready_bytes: trusted_operations.encode().len(), + future: 0, + future_bytes: 0, + } + }) + .unwrap_or_default() + } + + fn get_pending_trusted_calls_for( + &self, + shard: ShardIdentifier, + account: &AccountId, + ) -> Vec> { + self.tops + .read() + .unwrap() + .get(&shard) + .map(|encoded_operations| { + let mut trusted_operations: Vec> = Vec::new(); + for encoded_operation in encoded_operations { + if let Ok(top) = StfTrustedOperation::decode(&mut encoded_operation.as_slice()) + { + if top.signed_caller_account().as_ref() == Some(account) { + trusted_operations.push(top); + } + } + } + trusted_operations + }) + .unwrap_or_default() + } + + fn get_shards(&self) -> Vec { + self.tops.read().unwrap().keys().cloned().collect() + } + + fn list_handled_shards(&self) -> Vec { + //dummy + self.tops.read().unwrap().keys().cloned().collect() + } + + fn remove_calls_from_pool( + &self, + shard: ShardIdentifier, + executed_calls: Vec<(TrustedOperationOrHash, bool)>, + ) -> Vec> { + let mut remove_attempts_lock = self.remove_attempts.write().unwrap(); + *remove_attempts_lock += 1; + + let mut failed_to_remove = Vec::new(); + for (executed_call, inblock) in executed_calls { + if self.remove_top(vec![executed_call.clone()], shard, inblock).is_err() { + failed_to_remove.push(executed_call); + } + } + failed_to_remove + } + + fn watch_top(&self, request: R) -> PoolFuture { + // Note: The below implementation is specific for litentry/core/stf-task/receiver/test.rs + let sender_guard = GLOBAL_MOCK_AUTHOR_API.lock().unwrap(); + let sender = &*sender_guard; + sender + .as_ref() + .expect("Not yet initialized") + .send(request.payload().to_vec()) + .unwrap(); + Box::pin(ready(Ok([0u8; 32].into()))) + } + + fn watch_and_broadcast_top( + &self, + request: R, + _json_rpc_method: String, + ) -> PoolFuture { + self.watch_top(request) + } + + fn update_connection_state(&self, _updates: Vec<(H256, (Vec, bool))>) {} + + fn swap_rpc_connection_hash(&self, _old_hash: H256, _new_hash: H256) {} +} + +impl OnBlockImported for AuthorApiMock +where + TCS: PartialEq + Encode + Decode + Debug + Send + Sync + TrustedCallVerification, + G: PartialEq + Encode + Decode + Debug + Send + Sync, +{ + type Hash = H256; + + fn on_block_imported(&self, _hashes: &[Self::Hash], _block_hash: H256) {} +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::test_fixtures::shard_id; + use codec::Encode; + use futures::executor::block_on; + use itp_test::mock::stf_mock::{ + mock_top_indirect_trusted_call_signed, GetterMock, TrustedCallSignedMock, + }; + use itp_types::RsaRequest; + use std::vec; + + #[test] + fn submitted_tops_can_be_removed_again() { + let author = AuthorApiMock::::default(); + let shard = shard_id(); + let trusted_operation = mock_top_indirect_trusted_call_signed(); + + let _ = block_on(author.submit_top(RsaRequest::new(shard, trusted_operation.encode()))) + .unwrap(); + + assert_eq!(1, author.pending_tops(shard).unwrap().len()); + assert_eq!(1, author.get_pending_trusted_calls(shard).len()); + assert_eq!(0, author.get_pending_getters(shard).len()); + + let trusted_operation_or_hash = + TrustedOperationOrHash::::from_top( + trusted_operation.clone(), + ); + let removed_tops = author.remove_top(vec![trusted_operation_or_hash], shard, true).unwrap(); + + assert_eq!(1, removed_tops.len()); + assert!(author.tops.read().unwrap().get(&shard).unwrap().is_empty()); + } +} diff --git a/bitacross-worker/core-primitives/top-pool-author/src/test_fixtures.rs b/bitacross-worker/core-primitives/top-pool-author/src/test_fixtures.rs new file mode 100644 index 0000000000..b46f1d3e7c --- /dev/null +++ b/bitacross-worker/core-primitives/top-pool-author/src/test_fixtures.rs @@ -0,0 +1,42 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use codec::Encode; +use itp_stf_primitives::types::ShardIdentifier; + +use sp_core::{ed25519, Pair}; +use sp_runtime::traits::{BlakeTwo256, Hash}; +use std::vec; + +type Seed = [u8; 32]; +const TEST_SEED: Seed = *b"12345678901234567890123456789012"; + +pub(crate) fn mr_enclave() -> [u8; 32] { + [1u8; 32] +} + +pub(crate) fn shard_id() -> ShardIdentifier { + BlakeTwo256::hash(vec![1u8, 2u8, 3u8].as_slice().encode().as_slice()) +} + +fn alice_pair() -> ed25519::Pair { + ed25519::Pair::from_seed(b"22222678901234567890123456789012") +} + +fn bob_pair() -> ed25519::Pair { + ed25519::Pair::from_seed(b"33333378901234567890123456789012") +} diff --git a/bitacross-worker/core-primitives/top-pool-author/src/test_utils.rs b/bitacross-worker/core-primitives/top-pool-author/src/test_utils.rs new file mode 100644 index 0000000000..4c356e3428 --- /dev/null +++ b/bitacross-worker/core-primitives/top-pool-author/src/test_utils.rs @@ -0,0 +1,63 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use crate::traits::AuthorApi; +use codec::Encode; +use itp_sgx_crypto::ShieldingCryptoEncrypt; +use itp_stf_primitives::types::{ShardIdentifier, TrustedOperation as StfTrustedOperation}; +use itp_types::RsaRequest; +use jsonrpc_core::futures::executor; +use sp_core::H256; +use std::{fmt::Debug, string::ToString}; + +/// Test utility function to submit a trusted operation on an RPC author +pub fn submit_operation_to_top_pool( + author: &R, + top: &StfTrustedOperation, + shielding_key: &S, + shard: ShardIdentifier, + with_broadcast: bool, +) -> Result<(H256, RsaRequest), jsonrpc_core::Error> +where + R: AuthorApi, + S: ShieldingCryptoEncrypt, + S::Error: Debug, + TCS: PartialEq + Encode + Debug + Send + Sync, + G: PartialEq + Encode + Debug + Send + Sync, +{ + let top_encrypted = shielding_key.encrypt(&top.encode()).unwrap(); + if with_broadcast { + let submit_future = async { + author + .watch_and_broadcast_top( + RsaRequest::new(shard, top_encrypted.clone()), + "submit_and_watch".to_string(), + ) + .await + }; + let hash = executor::block_on(submit_future)?; + Ok((hash, RsaRequest::new(shard, top_encrypted))) + } else { + let submit_future = + async { author.watch_top(RsaRequest::new(shard, top_encrypted.clone())).await }; + let hash = executor::block_on(submit_future)?; + Ok((hash, RsaRequest::new(shard, top_encrypted))) + } +} diff --git a/bitacross-worker/core-primitives/top-pool-author/src/top_filter.rs b/bitacross-worker/core-primitives/top-pool-author/src/top_filter.rs new file mode 100644 index 0000000000..25b3574870 --- /dev/null +++ b/bitacross-worker/core-primitives/top-pool-author/src/top_filter.rs @@ -0,0 +1,320 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use codec::Encode; +use core::{fmt::Debug, marker::PhantomData}; +use itp_stf_primitives::types::TrustedOperation as StfTrustedOperation; + +/// Trait for filtering values +/// +/// Returns `Some` if a value should be included and `None` if discarded +pub trait Filter { + type Value; + + fn filter(&self, value: &Self::Value) -> bool; +} + +/// Filter for calls only (no getters). +pub struct CallsOnlyFilter { + _phantom: PhantomData<(TCS, G)>, +} + +impl CallsOnlyFilter { + pub fn new() -> Self { + Self { _phantom: Default::default() } + } +} + +impl Default for CallsOnlyFilter { + fn default() -> Self { + Self::new() + } +} + +impl Filter for CallsOnlyFilter +where + TCS: PartialEq + Encode + Debug, + G: PartialEq + Encode + Debug, +{ + type Value = StfTrustedOperation; + + fn filter(&self, value: &Self::Value) -> bool { + matches!(value, Self::Value::direct_call(_)) + || matches!(value, Self::Value::indirect_call(_)) + } +} + +/// Filter for direct calls only. +pub struct DirectCallsOnlyFilter { + _phantom: PhantomData<(TCS, G)>, +} + +impl DirectCallsOnlyFilter { + pub fn new() -> Self { + Self { _phantom: Default::default() } + } +} + +impl Default for DirectCallsOnlyFilter { + fn default() -> Self { + Self::new() + } +} + +impl Filter for DirectCallsOnlyFilter +where + TCS: PartialEq + Encode + Debug, + G: PartialEq + Encode + Debug, +{ + type Value = StfTrustedOperation; + + fn filter(&self, value: &Self::Value) -> bool { + matches!(value, Self::Value::direct_call(_)) + } +} + +/// Filter that allows all TOPs (i.e. not filter at all) +pub struct AllowAllTopsFilter { + _phantom: PhantomData<(TCS, G)>, +} + +impl AllowAllTopsFilter { + pub fn new() -> Self { + Self { _phantom: Default::default() } + } +} + +impl Default for AllowAllTopsFilter { + fn default() -> Self { + Self::new() + } +} + +impl Filter for AllowAllTopsFilter +where + TCS: PartialEq + Encode + Debug, + G: PartialEq + Encode + Debug, +{ + type Value = StfTrustedOperation; + + fn filter(&self, _value: &Self::Value) -> bool { + true + } +} + +/// Filter that allows only trusted getters +pub struct GettersOnlyFilter { + _phantom: PhantomData<(TCS, G)>, +} + +impl GettersOnlyFilter { + pub fn new() -> Self { + Self { _phantom: Default::default() } + } +} + +impl Default for GettersOnlyFilter { + fn default() -> Self { + Self::new() + } +} + +impl Filter for GettersOnlyFilter +where + TCS: PartialEq + Encode + Debug, + G: PartialEq + Encode + Debug, +{ + type Value = StfTrustedOperation; + + fn filter(&self, value: &Self::Value) -> bool { + matches!(value, Self::Value::get(_)) + } +} + +/// Filter for indirect calls only (no getters, no direct calls). +pub struct IndirectCallsOnlyFilter { + _phantom: PhantomData<(TCS, G)>, +} + +impl IndirectCallsOnlyFilter { + pub fn new() -> Self { + Self { _phantom: Default::default() } + } +} + +impl Default for IndirectCallsOnlyFilter { + fn default() -> Self { + Self::new() + } +} + +impl Filter for IndirectCallsOnlyFilter +where + TCS: PartialEq + Encode + Debug, + G: PartialEq + Encode + Debug, +{ + type Value = StfTrustedOperation; + + fn filter(&self, value: &Self::Value) -> bool { + matches!(value, Self::Value::indirect_call(_)) + } +} + +/// Filter that allows no direct calls, only indirect and getters. +pub struct NoDirectCallsFilter { + _phantom: PhantomData<(TCS, G)>, +} + +impl NoDirectCallsFilter { + pub fn new() -> Self { + Self { _phantom: Default::default() } + } +} + +impl Default for NoDirectCallsFilter { + fn default() -> Self { + Self::new() + } +} + +impl Filter for NoDirectCallsFilter +where + TCS: PartialEq + Encode + Debug, + G: PartialEq + Encode + Debug, +{ + type Value = StfTrustedOperation; + + fn filter(&self, value: &Self::Value) -> bool { + !matches!(value, Self::Value::direct_call(_)) + } +} + +/// Filter to deny all trusted operations. +pub struct DenyAllFilter { + _phantom: PhantomData<(TCS, G)>, +} + +impl DenyAllFilter { + pub fn new() -> Self { + Self { _phantom: Default::default() } + } +} + +impl Default for DenyAllFilter { + fn default() -> Self { + Self::new() + } +} + +impl Filter for DenyAllFilter +where + TCS: PartialEq + Encode + Debug, + G: PartialEq + Encode + Debug, +{ + type Value = StfTrustedOperation; + + fn filter(&self, _value: &Self::Value) -> bool { + false + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + use itp_test::mock::stf_mock::{ + mock_top_direct_trusted_call_signed, mock_top_indirect_trusted_call_signed, + mock_top_trusted_getter_signed, + }; + + use std::string::{String, ToString}; + + #[test] + fn filter_returns_none_if_values_is_filtered_out() { + struct WorldFilter; + impl Filter for WorldFilter { + type Value = String; + + fn filter(&self, value: &Self::Value) -> bool { + if value.eq(&String::from("world")) { + return true + } + false + } + } + + let filter = WorldFilter; + + assert!(!filter.filter(&"hello".to_string())); + assert!(filter.filter(&"world".to_string())); + } + + #[test] + fn allow_all_tops_filter_works() { + let filter = AllowAllTopsFilter::new(); + + assert!(filter.filter(&mock_top_trusted_getter_signed())); + assert!(filter.filter(&mock_top_indirect_trusted_call_signed())); + assert!(filter.filter(&mock_top_direct_trusted_call_signed())); + } + + #[test] + fn getters_only_filter_works() { + let filter = GettersOnlyFilter::new(); + + assert!(filter.filter(&mock_top_trusted_getter_signed())); + assert!(!filter.filter(&mock_top_indirect_trusted_call_signed())); + assert!(!filter.filter(&mock_top_direct_trusted_call_signed())); + } + + #[test] + fn no_direct_calls_filter_works() { + let filter = NoDirectCallsFilter::new(); + + assert!(!filter.filter(&mock_top_direct_trusted_call_signed())); + assert!(filter.filter(&mock_top_indirect_trusted_call_signed())); + assert!(filter.filter(&mock_top_trusted_getter_signed())); + } + + #[test] + fn indirect_calls_only_filter_works() { + let filter = IndirectCallsOnlyFilter::new(); + + assert!(!filter.filter(&mock_top_direct_trusted_call_signed())); + assert!(filter.filter(&mock_top_indirect_trusted_call_signed())); + assert!(!filter.filter(&mock_top_trusted_getter_signed())); + } + + #[test] + fn calls_only_filter_works() { + let filter = CallsOnlyFilter::new(); + + assert!(filter.filter(&mock_top_direct_trusted_call_signed())); + assert!(filter.filter(&mock_top_indirect_trusted_call_signed())); + assert!(!filter.filter(&mock_top_trusted_getter_signed())); + } + + #[test] + fn direct_calls_only_filter_works() { + let filter = DirectCallsOnlyFilter::new(); + + assert!(filter.filter(&mock_top_direct_trusted_call_signed())); + assert!(!filter.filter(&mock_top_indirect_trusted_call_signed())); + assert!(!filter.filter(&mock_top_trusted_getter_signed())); + } +} diff --git a/bitacross-worker/core-primitives/top-pool-author/src/traits.rs b/bitacross-worker/core-primitives/top-pool-author/src/traits.rs new file mode 100644 index 0000000000..b468432636 --- /dev/null +++ b/bitacross-worker/core-primitives/top-pool-author/src/traits.rs @@ -0,0 +1,109 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; +use codec::Encode; +use core::fmt::Debug; + +use crate::error::Result; +use itp_stf_primitives::types::{ + AccountId, TrustedOperation as StfTrustedOperation, TrustedOperationOrHash, +}; +use itp_top_pool::primitives::{PoolFuture, PoolStatus}; +use itp_types::{BlockHash as SidechainBlockHash, DecryptableRequest, ShardIdentifier, H256}; +use jsonrpc_core::Error as RpcError; +use std::{string::String, vec::Vec}; + +/// Trait alias for a full STF author API +pub trait FullAuthor< + TCS: PartialEq + Encode + Debug + Send + Sync + 'static, + G: PartialEq + Encode + Debug + Send + Sync + 'static, +> = AuthorApi + OnBlockImported + Send + Sync + 'static; + +/// Authoring RPC API +pub trait AuthorApi +where + TCS: PartialEq + Encode + Debug + Send + Sync, + G: PartialEq + Encode + Debug + Send + Sync, +{ + /// Submit encoded extrinsic for inclusion in block. + fn submit_top(&self, req: R) -> PoolFuture; + + /// Return hash of Trusted Operation + fn hash_of(&self, xt: &StfTrustedOperation) -> Hash; + + /// Returns all pending operations, potentially grouped by sender. + fn pending_tops(&self, shard: ShardIdentifier) -> Result>>; + + /// Returns all pending trusted getters. + fn get_pending_getters(&self, shard: ShardIdentifier) -> Vec>; + + /// Returns all pending trusted calls (in ready state). + fn get_pending_trusted_calls(&self, shard: ShardIdentifier) + -> Vec>; + + /// Returns pool status + fn get_status(&self, shard: ShardIdentifier) -> PoolStatus; + + /// Returns all pending trusted calls for a given `account` + fn get_pending_trusted_calls_for( + &self, + shard: ShardIdentifier, + account: &AccountId, + ) -> Vec>; + + /// returns all shards which are currently present in the tops in the pool + fn get_shards(&self) -> Vec; + + /// returns all shards which are handled by our worker + fn list_handled_shards(&self) -> Vec; + + /// Remove a collection of trusted operations from the pool. + /// Return operations that were not successfully removed. + fn remove_calls_from_pool( + &self, + shard: ShardIdentifier, + executed_calls: Vec<(TrustedOperationOrHash, bool)>, + ) -> Vec>; + + /// Submit a request to watch. + /// + /// See [`TrustedOperationStatus`](sp_transaction_pool::TrustedOperationStatus) for details on transaction + /// life cycle. + fn watch_top(&self, request: R) -> PoolFuture; + + /// Submit a request to watch and broadcasts it to known peers. + fn watch_and_broadcast_top( + &self, + request: R, + json_rpc_method: String, + ) -> PoolFuture; + + /// Litentry: set the rpc response value + fn update_connection_state(&self, updates: Vec<(Hash, (Vec, bool))>); + + /// Litentry: swap the old hash with the new one in rpc connection registry + fn swap_rpc_connection_hash(&self, old_hash: Hash, new_hash: Hash); +} + +/// Trait to notify listeners/observer of a newly created block +pub trait OnBlockImported { + type Hash; + + fn on_block_imported(&self, hashes: &[Self::Hash], block_hash: SidechainBlockHash); +} diff --git a/bitacross-worker/core-primitives/top-pool/Cargo.toml b/bitacross-worker/core-primitives/top-pool/Cargo.toml new file mode 100644 index 0000000000..21f6f89ea7 --- /dev/null +++ b/bitacross-worker/core-primitives/top-pool/Cargo.toml @@ -0,0 +1,66 @@ +[package] +name = "itp-top-pool" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +# sgx dependencies +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true, features = ["net", "thread", "untrusted_time"] } + +# local dependencies +itc-direct-rpc-server = { path = "../../core/direct-rpc-server", default-features = false } +itp-stf-primitives = { path = "../stf-primitives", default-features = false } +itp-types = { path = "../types", default-features = false } +its-primitives = { path = "../../sidechain/primitives", default-features = false } + +# sgx enabled external libraries +jsonrpc-core_sgx = { package = "jsonrpc-core", git = "https://github.com/scs/jsonrpc", branch = "no_std_v18", default-features = false, optional = true } +linked-hash-map_sgx = { package = "linked-hash-map", git = "https://github.com/mesalock-linux/linked-hash-map-sgx", optional = true } + +# std compatible external libraries (make sure these versions match with the sgx-enabled ones above) +jsonrpc-core = { version = "18", optional = true } +linked-hash-map = { version = "0.5.2", optional = true } + +# no-std compatible libraries +byteorder = { version = "1.4.2", default-features = false } +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +derive_more = { version = "0.99.5" } +log = { version = "0.4", default-features = false } +sp-core = { default-features = false, features = ["full_crypto"], git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# dev dependencies (for tests) +[dev-dependencies] +parity-util-mem = { version = "0.12.0", default-features = false, features = ["primitive-types"] } +itp-test = { path = "../test", default-features = false } +serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } +sp-application-crypto = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +# litentry +litentry-primitives = { path = "../../litentry/primitives", default-features = false } + +[features] +default = ["std"] +sgx = [ + "sgx_tstd", + "itc-direct-rpc-server/sgx", + "jsonrpc-core_sgx", + "linked-hash-map_sgx", + # litentry + "litentry-primitives/sgx", +] +std = [ + "itc-direct-rpc-server/std", + "itp-types/std", + "its-primitives/std", + "jsonrpc-core", + "linked-hash-map", + "log/std", + "serde/std", + "sp-core/std", + "sp-runtime/std", + "sp-application-crypto/std", + # litentry + "litentry-primitives/std", +] +mocks = [] diff --git a/bitacross-worker/core-primitives/top-pool/src/base_pool.rs b/bitacross-worker/core-primitives/top-pool/src/base_pool.rs new file mode 100644 index 0000000000..a6cb0628a0 --- /dev/null +++ b/bitacross-worker/core-primitives/top-pool/src/base_pool.rs @@ -0,0 +1,1379 @@ +// This file is part of Substrate. + +// Copyright (C) 2018-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! A basic version of the dependency graph. +//! +//! For a more full-featured pool, have a look at the `pool` module. + +pub extern crate alloc; +use crate::{ + error, + future::{FutureTrustedOperations, WaitingTrustedOperations}, + primitives::{InPoolOperation, PoolStatus, TrustedOperationSource as Source, TxHash}, + ready::ReadyOperations, +}; +use alloc::{fmt, sync::Arc, vec, vec::Vec}; +use core::iter; +use itp_stf_primitives::types::ShardIdentifier; +use log::{debug, trace, warn}; +use sp_core::hexdisplay::HexDisplay; +use sp_runtime::transaction_validity::{ + TransactionLongevity as Longevity, TransactionPriority as Priority, TransactionTag as Tag, +}; +use std::collections::HashSet; + +/// Successful import result. +#[derive(Debug, PartialEq, Eq)] +pub enum Imported { + /// TrustedOperation was successfully imported to Ready queue. + Ready { + /// Hash of operation that was successfully imported. + hash: TxHash, + /// operations that got promoted from the Future queue. + promoted: Vec, + /// operations that failed to be promoted from the Future queue and are now discarded. + failed: Vec, + /// operations removed from the Ready pool (replaced). + removed: Vec>>, + }, + /// TrustedOperation was successfully imported to Future queue. + Future { + /// Hash of operation that was successfully imported. + hash: TxHash, + }, +} + +impl Imported { + /// Returns the hash of imported operation. + pub fn hash(&self) -> &TxHash { + use self::Imported::*; + match *self { + Ready { ref hash, .. } => hash, + Future { ref hash, .. } => hash, + } + } +} + +/// Status of pruning the queue. +#[derive(Debug)] +pub struct PruneStatus { + /// A list of imports that satisfying the tag triggered. + pub promoted: Vec>, + /// A list of operations that failed to be promoted and now are discarded. + pub failed: Vec, + /// A list of operations that got pruned from the ready queue. + pub pruned: Vec>>, +} + +/// Immutable operation +#[derive(PartialEq, Eq, Clone)] +pub struct TrustedOperation { + /// Raw extrinsic representing that operation. + pub data: Extrinsic, + /// Number of bytes encoding of the operation requires. + pub bytes: usize, + /// TrustedOperation hash (unique) + pub hash: TxHash, + /// TrustedOperation priority (higher = better) + pub priority: Priority, + /// At which block the operation becomes invalid? + pub valid_till: Longevity, + /// Tags required by the operation. + pub requires: Vec, + /// Tags that this operation provides. + pub provides: Vec, + /// Should that operation be propagated. + pub propagate: bool, + /// Source of that operation. + pub source: Source, +} + +impl AsRef for TrustedOperation { + fn as_ref(&self) -> &Extrinsic { + &self.data + } +} + +impl InPoolOperation for TrustedOperation { + type TrustedOperation = Extrinsic; + + fn data(&self) -> &Extrinsic { + &self.data + } + + fn hash(&self) -> TxHash { + self.hash + } + + fn priority(&self) -> &Priority { + &self.priority + } + + fn longevity(&self) -> &Longevity { + &self.valid_till + } + + fn requires(&self) -> &[Tag] { + &self.requires + } + + fn provides(&self) -> &[Tag] { + &self.provides + } + + fn is_propagable(&self) -> bool { + self.propagate + } +} + +impl TrustedOperation { + /// Explicit operation clone. + /// + /// TrustedOperation should be cloned only if absolutely necessary && we want + /// every reason to be commented. That's why we `TrustedOperation` is not `Clone`, + /// but there's explicit `duplicate` method. + pub fn duplicate(&self) -> Self { + TrustedOperation { + data: self.data.clone(), + bytes: self.bytes, + hash: self.hash, + priority: self.priority, + source: self.source, + valid_till: self.valid_till, + requires: self.requires.clone(), + provides: self.provides.clone(), + propagate: self.propagate, + } + } +} + +impl fmt::Debug for TrustedOperation +where + Extrinsic: fmt::Debug, +{ + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fn print_tags(fmt: &mut fmt::Formatter, tags: &[Tag]) -> fmt::Result { + let mut it = tags.iter(); + if let Some(t) = it.next() { + write!(fmt, "{}", HexDisplay::from(t))?; + } + for t in it { + write!(fmt, ",{}", HexDisplay::from(t))?; + } + Ok(()) + } + + write!(fmt, "TrustedOperation {{ ")?; + write!(fmt, "hash: {:?}, ", &self.hash)?; + write!(fmt, "priority: {:?}, ", &self.priority)?; + write!(fmt, "valid_till: {:?}, ", &self.valid_till)?; + write!(fmt, "bytes: {:?}, ", &self.bytes)?; + write!(fmt, "propagate: {:?}, ", &self.propagate)?; + write!(fmt, "source: {:?}, ", &self.source)?; + write!(fmt, "requires: [")?; + print_tags(fmt, &self.requires)?; + write!(fmt, "], provides: [")?; + print_tags(fmt, &self.provides)?; + write!(fmt, "], ")?; + write!(fmt, "data: {:?}", &self.data)?; + write!(fmt, "}}")?; + Ok(()) + } +} + +/// Store last pruned tags for given number of invocations. +const RECENTLY_PRUNED_TAGS: usize = 2; + +/// TrustedOperation pool. +/// +/// Builds a dependency graph for all operations in the pool and returns +/// the ones that are currently ready to be executed. +/// +/// General note: +/// If function returns some operations it usually means that importing them +/// as-is for the second time will fail or produce unwanted results. +/// Most likely it is required to revalidate them and recompute set of +/// required tags. +#[derive(Debug)] +pub struct BasePool { + reject_future_operations: bool, + future: FutureTrustedOperations, + ready: ReadyOperations, + /// Store recently pruned tags (for last two invocations). + /// + /// This is used to make sure we don't accidentally put + /// operations to future in case they were just stuck in verification. + recently_pruned: [HashSet; RECENTLY_PRUNED_TAGS], + recently_pruned_index: usize, +} + +impl Default for BasePool { + fn default() -> Self { + Self::new(false) + } +} + +impl BasePool { + /// Create new pool given reject_future_operations flag. + pub fn new(reject_future_operations: bool) -> Self { + BasePool { + reject_future_operations, + future: Default::default(), + ready: Default::default(), + recently_pruned: Default::default(), + recently_pruned_index: 0, + } + } + + /// Temporary enables future operations, runs closure and then restores + /// `reject_future_operations` flag back to previous value. + /// + /// The closure accepts the mutable reference to the pool and original value + /// of the `reject_future_operations` flag. + pub(crate) fn with_futures_enabled( + &mut self, + closure: impl FnOnce(&mut Self, bool) -> T, + ) -> T { + let previous = self.reject_future_operations; + self.reject_future_operations = false; + let return_value = closure(self, previous); + self.reject_future_operations = previous; + return_value + } + + /// Returns if the operation for the given hash is already imported. + pub fn is_imported(&self, tx_hash: &TxHash, shard: ShardIdentifier) -> bool { + self.future.contains(tx_hash, shard) || self.ready.contains(tx_hash, shard) + } + + /// Imports operations to the pool. + /// + /// The pool consists of two parts: Future and Ready. + /// The former contains operations that require some tags that are not yet provided by + /// other operations in the pool. + /// The latter contains operations that have all the requirements satisfied and are + /// ready to be included in the block. + pub fn import( + &mut self, + tx: TrustedOperation, + shard: ShardIdentifier, + ) -> error::Result> { + if self.is_imported(&tx.hash, shard) { + return Err(error::Error::AlreadyImported) + } + + let tx = WaitingTrustedOperations::new( + tx, + self.ready.provided_tags(shard), + &self.recently_pruned, + ); + trace!(target: "txpool", "[{:?}] {:?}", tx.operation.hash, tx); + debug!( + target: "txpool", + "[{:?}] Importing to {}", + tx.operation.hash, + if tx.is_ready() { "ready" } else { "future" } + ); + + // If all tags are not satisfied import to future. + if !tx.is_ready() { + if self.reject_future_operations { + return Err(error::Error::RejectedFutureTrustedOperation) + } + + let hash = tx.operation.hash; + self.future.import(tx, shard); + return Ok(Imported::Future { hash }) + } + + self.import_to_ready(tx, shard) + } + + /// Imports operations to ready queue. + /// + /// NOTE the operation has to have all requirements satisfied. + fn import_to_ready( + &mut self, + tx: WaitingTrustedOperations, + shard: ShardIdentifier, + ) -> error::Result> { + let hash = tx.operation.hash; + let mut promoted = vec![]; + let mut failed = vec![]; + let mut removed = vec![]; + + let mut first = true; + let mut to_import = vec![tx]; + + while let Some(tx) = to_import.pop() { + // find operation in Future that it unlocks + to_import.append(&mut self.future.satisfy_tags(&tx.operation.provides, shard)); + + // import this operation + let current_hash = tx.operation.hash; + match self.ready.import(tx, shard) { + Ok(mut replaced) => { + if !first { + promoted.push(current_hash); + } + // The operations were removed from the ready pool. We might attempt to re-import them. + removed.append(&mut replaced); + }, + // operation failed to be imported. + Err(e) => + if first { + debug!(target: "txpool", "[{:?}] Error importing", current_hash,); + return Err(e) + } else { + failed.push(current_hash); + }, + } + first = false; + } + + // An edge case when importing operation caused + // some future operations to be imported and that + // future operations pushed out current operation. + // This means that there is a cycle and the operations should + // be moved back to future, since we can't resolve it. + if removed.iter().any(|tx| tx.hash == hash) { + // We still need to remove all operations that we promoted + // since they depend on each other and will never get to the best iterator. + self.ready.remove_subtree(&promoted, shard); + + debug!(target: "txpool", "[{:?}] Cycle detected, bailing.", hash); + return Err(error::Error::CycleDetected) + } + + Ok(Imported::Ready { hash, promoted, failed, removed }) + } + + /// Returns an iterator over ready operations in the pool. + pub fn ready(&self, shard: ShardIdentifier) -> impl Iterator>> { + self.ready.get(shard) + } + + /// Returns an iterator over all shards in the pool. + pub fn get_shards(&self) -> impl Iterator { + self.ready.get_shards() + } + + /// Returns an iterator over future operations in the pool. + pub fn futures(&self, shard: ShardIdentifier) -> impl Iterator> { + self.future.all(shard) + } + + /// Returns pool operations given list of hashes. + /// + /// Includes both ready and future pool. For every hash in the `hashes` + /// iterator an `Option` is produced (so the resulting `Vec` always have the same length). + pub fn by_hashes( + &self, + hashes: &[TxHash], + shard: ShardIdentifier, + ) -> Vec>>> { + let ready = self.ready.by_hashes(hashes, shard); + let future = self.future.by_hashes(hashes, shard); + + ready.into_iter().zip(future).map(|(a, b)| a.or(b)).collect() + } + + /// Returns pool operation by hash. + pub fn ready_by_hash( + &self, + hash: &TxHash, + shard: ShardIdentifier, + ) -> Option>> { + self.ready.by_hash(hash, shard) + } + + /// Makes sure that the operations in the queues stay within provided limits. + /// + /// Removes and returns worst operations from the queues and all operations that depend on them. + /// Technically the worst operation should be evaluated by computing the entire pending set. + /// We use a simplified approach to remove the operation that occupies the pool for the longest time. + pub fn enforce_limits( + &mut self, + ready: &Limit, + future: &Limit, + shard: ShardIdentifier, + ) -> Vec>> { + let mut removed = vec![]; + + while ready.is_exceeded(self.ready.len(shard), self.ready.bytes(shard)) { + // find the worst operation + let minimal = self.ready.fold( + |minimal, current| { + let operation = ¤t.operation; + match minimal { + None => Some(operation.clone()), + Some(ref tx) if tx.insertion_id > operation.insertion_id => + Some(operation.clone()), + other => other, + } + }, + shard, + ); + + if let Some(minimal) = minimal { + removed.append(&mut self.remove_subtree(&[minimal.operation.hash], shard)) + } else { + break + } + } + + while future.is_exceeded(self.future.len(shard), self.future.bytes(shard)) { + // find the worst operation + let minimal = self.future.fold( + |minimal, current| { + match minimal { + None => Some(current.clone()), + /*Some(ref tx) if tx.imported_at > current.imported_at => { + Some(current.clone()) + },*/ + other => other, + } + }, + shard, + ); + + if let Some(minimal) = minimal { + removed.append(&mut self.remove_subtree(&[minimal.operation.hash], shard)) + } else { + break + } + } + + removed + } + + /// Removes all operations represented by the hashes and all other operations + /// that depend on them. + /// + /// Returns a list of actually removed operations. + /// NOTE some operations might still be valid, but were just removed because + /// they were part of a chain, you may attempt to re-import them later. + /// NOTE If you want to remove ready operations that were already used + /// and you don't want them to be stored in the pool use `prune_tags` method. + pub fn remove_subtree( + &mut self, + hashes: &[TxHash], + shard: ShardIdentifier, + ) -> Vec>> { + let mut removed = self.ready.remove_subtree(hashes, shard); + removed.extend(self.future.remove(hashes, shard)); + removed + } + + /// Removes and returns all operations from the future queue. + pub fn clear_future(&mut self, shard: ShardIdentifier) -> Vec>> { + self.future.clear(shard) + } + + /// Prunes operations that provide given list of tags. + /// + /// This will cause all operations that provide these tags to be removed from the pool, + /// but unlike `remove_subtree`, dependent operations are not touched. + /// Additional operations from future queue might be promoted to ready if you satisfy tags + /// that the pool didn't previously know about. + pub fn prune_tags( + &mut self, + tags: impl IntoIterator, + shard: ShardIdentifier, + ) -> PruneStatus { + let mut to_import = vec![]; + let mut pruned = vec![]; + let recently_pruned = &mut self.recently_pruned[self.recently_pruned_index]; + self.recently_pruned_index = (self.recently_pruned_index + 1) % RECENTLY_PRUNED_TAGS; + recently_pruned.clear(); + + for tag in tags { + // make sure to promote any future operations that could be unlocked + to_import.append(&mut self.future.satisfy_tags(iter::once(&tag), shard)); + // and actually prune operations in ready queue + pruned.append(&mut self.ready.prune_tags(tag.clone(), shard)); + // store the tags for next submission + recently_pruned.insert(tag); + } + + let mut promoted = vec![]; + let mut failed = vec![]; + for tx in to_import { + let hash = tx.operation.hash; + match self.import_to_ready(tx, shard) { + Ok(res) => promoted.push(res), + Err(_e) => { + warn!(target: "txpool", "[{:?}] Failed to promote during pruning", hash); + failed.push(hash) + }, + } + } + + PruneStatus { promoted, failed, pruned } + } + + /// Get pool status. + pub fn status(&self, shard: ShardIdentifier) -> PoolStatus { + PoolStatus { + ready: self.ready.len(shard), + ready_bytes: self.ready.bytes(shard), + future: self.future.len(shard), + future_bytes: self.future.bytes(shard), + } + } +} + +/// Queue limits +#[derive(Debug, Clone)] +pub struct Limit { + /// Maximal number of operations in the queue. + pub count: usize, + /// Maximal size of encodings of all operations in the queue. + pub total_bytes: usize, +} + +impl Limit { + /// Returns true if any of the provided values exceeds the limit. + pub fn is_exceeded(&self, count: usize, bytes: usize) -> bool { + self.count < count || self.total_bytes < bytes + } +} + +#[cfg(test)] +pub mod tests { + + use super::*; + use alloc::borrow::ToOwned; + use itp_types::H256; + + fn hash(index: u8) -> H256 { + [index; 32].into() + } + + fn test_pool() -> BasePool> { + BasePool::default() + } + + #[test] + pub fn test_should_import_transaction_to_ready() { + // given + let mut pool = test_pool(); + let shard = ShardIdentifier::default(); + + // when + pool.import( + TrustedOperation { + data: vec![1u8], + bytes: 1, + hash: hash(1), + priority: 5u64, + valid_till: 64u64, + requires: vec![], + provides: vec![vec![1]], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap(); + + // then + assert_eq!(pool.ready(shard).count(), 1); + assert_eq!(pool.ready.len(shard), 1); + } + + #[test] + pub fn test_should_not_import_same_transaction_twice() { + // given + let mut pool = test_pool(); + let shard = ShardIdentifier::default(); + + // when + pool.import( + TrustedOperation { + data: vec![1u8], + bytes: 1, + hash: hash(1), + priority: 5u64, + valid_till: 64u64, + requires: vec![], + provides: vec![vec![1]], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap(); + pool.import( + TrustedOperation { + data: vec![1u8], + bytes: 1, + hash: hash(1), + priority: 5u64, + valid_till: 64u64, + requires: vec![], + provides: vec![vec![1]], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap_err(); + + // then + assert_eq!(pool.ready(shard).count(), 1); + assert_eq!(pool.ready.len(shard), 1); + } + + #[test] + pub fn test_should_import_transaction_to_future_and_promote_it_later() { + // given + let mut pool = test_pool(); + let shard = ShardIdentifier::default(); + + // when + pool.import( + TrustedOperation { + data: vec![1u8], + bytes: 1, + hash: hash(1), + priority: 5u64, + valid_till: 64u64, + requires: vec![vec![0]], + provides: vec![vec![1]], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap(); + assert_eq!(pool.ready(shard).count(), 0); + assert_eq!(pool.ready.len(shard), 0); + pool.import( + TrustedOperation { + data: vec![2u8], + bytes: 1, + hash: hash(2), + priority: 5u64, + valid_till: 64u64, + requires: vec![], + provides: vec![vec![0]], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap(); + + // then + assert_eq!(pool.ready(shard).count(), 2); + assert_eq!(pool.ready.len(shard), 2); + } + + #[test] + pub fn test_should_promote_a_subgraph() { + // given + let mut pool = test_pool(); + let shard = ShardIdentifier::default(); + + // when + pool.import( + TrustedOperation { + data: vec![1u8], + bytes: 1, + hash: hash(1), + priority: 5u64, + valid_till: 64u64, + requires: vec![vec![0]], + provides: vec![vec![1]], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap(); + pool.import( + TrustedOperation { + data: vec![3u8], + bytes: 1, + hash: hash(3), + priority: 5u64, + valid_till: 64u64, + requires: vec![vec![2]], + provides: vec![], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap(); + pool.import( + TrustedOperation { + data: vec![2u8], + bytes: 1, + hash: hash(2), + priority: 5u64, + valid_till: 64u64, + requires: vec![vec![1]], + provides: vec![vec![3], vec![2]], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap(); + pool.import( + TrustedOperation { + data: vec![4u8], + bytes: 1, + hash: hash(4), + priority: 1_000u64, + valid_till: 64u64, + requires: vec![vec![3], vec![4]], + provides: vec![], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap(); + assert_eq!(pool.ready(shard).count(), 0); + assert_eq!(pool.ready.len(shard), 0); + + let res = pool + .import( + TrustedOperation { + data: vec![5u8], + bytes: 1, + hash: hash(5), + priority: 5u64, + valid_till: 64u64, + requires: vec![], + provides: vec![vec![0], vec![4]], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap(); + + // then + let mut it = pool.ready(shard).into_iter().map(|tx| tx.data[0]); + + assert_eq!(it.next(), Some(5)); + assert_eq!(it.next(), Some(1)); + assert_eq!(it.next(), Some(2)); + assert_eq!(it.next(), Some(4)); + assert_eq!(it.next(), Some(3)); + assert_eq!(it.next(), None); + assert_eq!( + res, + Imported::Ready { + hash: hash(5), + promoted: vec![hash(1), hash(2), hash(3), hash(4)], + failed: vec![], + removed: vec![] + } + ); + } + + #[test] + pub fn test_should_handle_a_cycle() { + // given + let shard = ShardIdentifier::default(); + let mut pool = test_pool(); + pool.import( + TrustedOperation { + data: vec![1u8], + bytes: 1, + hash: hash(1), + priority: 5u64, + valid_till: 64u64, + requires: vec![vec![0]], + provides: vec![vec![1]], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap(); + pool.import( + TrustedOperation { + data: vec![3u8], + bytes: 1, + hash: hash(3), + priority: 5u64, + valid_till: 64u64, + requires: vec![vec![1]], + provides: vec![vec![2]], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap(); + assert_eq!(pool.ready(shard).count(), 0); + assert_eq!(pool.ready.len(shard), 0); + + // when + pool.import( + TrustedOperation { + data: vec![2u8], + bytes: 1, + hash: hash(2), + priority: 5u64, + valid_till: 64u64, + requires: vec![vec![2]], + provides: vec![vec![0]], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap(); + + // then + { + let mut it = pool.ready(shard).into_iter().map(|tx| tx.data[0]); + assert_eq!(it.next(), None); + } + // all operations occupy the Future queue - it's fine + assert_eq!(pool.future.len(shard), 3); + + // let's close the cycle with one additional operation + let res = pool + .import( + TrustedOperation { + data: vec![4u8], + bytes: 1, + hash: hash(4), + priority: 50u64, + valid_till: 64u64, + requires: vec![], + provides: vec![vec![0]], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap(); + let mut it = pool.ready(shard).into_iter().map(|tx| tx.data[0]); + assert_eq!(it.next(), Some(4)); + assert_eq!(it.next(), Some(1)); + assert_eq!(it.next(), Some(3)); + assert_eq!(it.next(), None); + assert_eq!( + res, + Imported::Ready { + hash: hash(4), + promoted: vec![hash(1), hash(3)], + failed: vec![hash(2)], + removed: vec![] + } + ); + assert_eq!(pool.future.len(shard), 0); + } + + #[test] + pub fn test_should_handle_a_cycle_with_low_priority() { + // given + let mut pool = test_pool(); + let shard = ShardIdentifier::default(); + pool.import( + TrustedOperation { + data: vec![1u8], + bytes: 1, + hash: hash(1), + priority: 5u64, + valid_till: 64u64, + requires: vec![vec![0]], + provides: vec![vec![1]], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap(); + pool.import( + TrustedOperation { + data: vec![3u8], + bytes: 1, + hash: hash(3), + priority: 5u64, + valid_till: 64u64, + requires: vec![vec![1]], + provides: vec![vec![2]], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap(); + assert_eq!(pool.ready(shard).count(), 0); + assert_eq!(pool.ready.len(shard), 0); + + // when + pool.import( + TrustedOperation { + data: vec![2u8], + bytes: 1, + hash: hash(2), + priority: 5u64, + valid_till: 64u64, + requires: vec![vec![2]], + provides: vec![vec![0]], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap(); + + // then + { + let mut it = pool.ready(shard).into_iter().map(|tx| tx.data[0]); + assert_eq!(it.next(), None); + } + // all operations occupy the Future queue - it's fine + assert_eq!(pool.future.len(shard), 3); + + // let's close the cycle with one additional operation + let err = pool + .import( + TrustedOperation { + data: vec![4u8], + bytes: 1, + hash: hash(4), + priority: 1u64, // lower priority than Tx(2) + valid_till: 64u64, + requires: vec![], + provides: vec![vec![0]], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap_err(); + let mut it = pool.ready(shard).into_iter().map(|tx| tx.data[0]); + assert_eq!(it.next(), None); + assert_eq!(pool.ready.len(shard), 0); + assert_eq!(pool.future.len(shard), 0); + if let error::Error::CycleDetected = err { + } else { + assert!(false, "Invalid error kind: {:?}", err); + } + } + + #[test] + pub fn test_can_track_heap_size() { + let mut pool = test_pool(); + let shard = ShardIdentifier::default(); + pool.import( + TrustedOperation { + data: vec![5u8; 1024], + bytes: 1, + hash: hash(5), + priority: 5u64, + valid_till: 64u64, + requires: vec![], + provides: vec![vec![0], vec![4]], + propagate: true, + source: Source::External, + }, + shard, + ) + .expect("import 1 should be ok"); + pool.import( + TrustedOperation { + data: vec![3u8; 1024], + bytes: 1, + hash: hash(7), + priority: 5u64, + valid_till: 64u64, + requires: vec![], + provides: vec![vec![2], vec![7]], + propagate: true, + source: Source::External, + }, + shard, + ) + .expect("import 2 should be ok"); + + //assert!(parity_util_mem::malloc_size(&pool) > 5000); + } + + #[test] + pub fn test_should_remove_invalid_transactions() { + // given + let shard = ShardIdentifier::default(); + let mut pool = test_pool(); + pool.import( + TrustedOperation { + data: vec![5u8], + bytes: 1, + hash: hash(5), + priority: 5u64, + valid_till: 64u64, + requires: vec![], + provides: vec![vec![0], vec![4]], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap(); + pool.import( + TrustedOperation { + data: vec![1u8], + bytes: 1, + hash: hash(1), + priority: 5u64, + valid_till: 64u64, + requires: vec![vec![0]], + provides: vec![vec![1]], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap(); + pool.import( + TrustedOperation { + data: vec![3u8], + bytes: 1, + hash: hash(3), + priority: 5u64, + valid_till: 64u64, + requires: vec![vec![2]], + provides: vec![], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap(); + pool.import( + TrustedOperation { + data: vec![2u8], + bytes: 1, + hash: hash(2), + priority: 5u64, + valid_till: 64u64, + requires: vec![vec![1]], + provides: vec![vec![3], vec![2]], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap(); + pool.import( + TrustedOperation { + data: vec![4u8], + bytes: 1, + hash: hash(4), + priority: 1_000u64, + valid_till: 64u64, + requires: vec![vec![3], vec![4]], + provides: vec![], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap(); + // future + pool.import( + TrustedOperation { + data: vec![6u8], + bytes: 1, + hash: hash(6), + priority: 1_000u64, + valid_till: 64u64, + requires: vec![vec![11]], + provides: vec![], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap(); + assert_eq!(pool.ready(shard).count(), 5); + assert_eq!(pool.future.len(shard), 1); + + // when + pool.remove_subtree(&[hash(6), hash(1)], shard); + + // then + assert_eq!(pool.ready(shard).count(), 1); + assert_eq!(pool.future.len(shard), 0); + } + + #[test] + pub fn test_should_prune_ready_transactions() { + // given + let mut pool = test_pool(); + let shard = ShardIdentifier::default(); + // future (waiting for 0) + pool.import( + TrustedOperation { + data: vec![5u8], + bytes: 1, + hash: hash(5), + priority: 5u64, + valid_till: 64u64, + requires: vec![vec![0]], + provides: vec![vec![100]], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap(); + // ready + pool.import( + TrustedOperation { + data: vec![1u8], + bytes: 1, + hash: hash(1), + priority: 5u64, + valid_till: 64u64, + requires: vec![], + provides: vec![vec![1]], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap(); + pool.import( + TrustedOperation { + data: vec![2u8], + bytes: 1, + hash: hash(2), + priority: 5u64, + valid_till: 64u64, + requires: vec![vec![2]], + provides: vec![vec![3]], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap(); + pool.import( + TrustedOperation { + data: vec![3u8], + bytes: 1, + hash: hash(3), + priority: 5u64, + valid_till: 64u64, + requires: vec![vec![1]], + provides: vec![vec![2]], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap(); + pool.import( + TrustedOperation { + data: vec![4u8], + bytes: 1, + hash: hash(4), + priority: 1_000u64, + valid_till: 64u64, + requires: vec![vec![3], vec![2]], + provides: vec![vec![4]], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap(); + + assert_eq!(pool.ready(shard).count(), 4); + assert_eq!(pool.future.len(shard), 1); + + // when + let result = pool.prune_tags(vec![vec![0], vec![2]], shard); + + // then + assert_eq!(result.pruned.len(), 2); + assert_eq!(result.failed.len(), 0); + assert_eq!( + result.promoted[0], + Imported::Ready { hash: hash(5), promoted: vec![], failed: vec![], removed: vec![] } + ); + assert_eq!(result.promoted.len(), 1); + assert_eq!(pool.future.len(shard), 0); + assert_eq!(pool.ready.len(shard), 3); + assert_eq!(pool.ready(shard).count(), 3); + } + + #[test] + pub fn test_transaction_debug() { + assert_eq!( + format!( + "{:?}", + TrustedOperation { + data: vec![4u8], + bytes: 1, + hash: hash(4), + priority: 1_000u64, + valid_till: 64u64, + requires: vec![vec![3], vec![2]], + provides: vec![vec![4]], + propagate: true, + source: Source::External, + } + ), + "TrustedOperation { \ +hash: 0x0404040404040404040404040404040404040404040404040404040404040404, priority: 1000, valid_till: 64, bytes: 1, propagate: true, \ +source: External, requires: [03,02], provides: [04], data: [4]}" + .to_owned() + ); + } + + #[test] + pub fn test_transaction_propagation() { + assert!(TrustedOperation { + data: vec![4u8], + bytes: 1, + hash: hash(4), + priority: 1_000u64, + valid_till: 64u64, + requires: vec![vec![3], vec![2]], + provides: vec![vec![4]], + propagate: true, + source: Source::External, + } + .is_propagable()); + + assert!(!TrustedOperation { + data: vec![4u8], + bytes: 1, + hash: hash(4), + priority: 1_000u64, + valid_till: 64u64, + requires: vec![vec![3], vec![2]], + provides: vec![vec![4]], + propagate: false, + source: Source::External, + } + .is_propagable()); + } + + #[test] + pub fn test_should_reject_future_transactions() { + // given + let mut pool = test_pool(); + let shard = ShardIdentifier::default(); + + // when + pool.reject_future_operations = true; + + // then + let err = pool.import( + TrustedOperation { + data: vec![5u8], + bytes: 1, + hash: hash(5), + priority: 5u64, + valid_till: 64u64, + requires: vec![vec![0]], + provides: vec![], + propagate: true, + source: Source::External, + }, + shard, + ); + + if let Err(error::Error::RejectedFutureTrustedOperation) = err { + } else { + assert!(false, "Invalid error kind: {:?}", err); + } + } + + #[test] + pub fn test_should_clear_future_queue() { + // given + let mut pool = test_pool(); + let shard = ShardIdentifier::default(); + + // when + pool.import( + TrustedOperation { + data: vec![5u8], + bytes: 1, + hash: hash(5), + priority: 5u64, + valid_till: 64u64, + requires: vec![vec![0]], + provides: vec![], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap(); + + // then + assert_eq!(pool.future.len(shard), 1); + + // and then when + assert_eq!(pool.clear_future(shard).len(), 1); + + // then + assert_eq!(pool.future.len(shard), 0); + } + + #[test] + pub fn test_should_accept_future_transactions_when_explicitly_asked_to() { + // given + let mut pool = test_pool(); + pool.reject_future_operations = true; + let shard = ShardIdentifier::default(); + + // when + let flag_value = pool.with_futures_enabled(|pool, flag| { + pool.import( + TrustedOperation { + data: vec![5u8], + bytes: 1, + hash: hash(5), + priority: 5u64, + valid_till: 64u64, + requires: vec![vec![0]], + provides: vec![], + propagate: true, + source: Source::External, + }, + shard, + ) + .unwrap(); + + flag + }); + + // then + assert!(flag_value); + assert!(pool.reject_future_operations); + assert_eq!(pool.future.len(shard), 1); + } +} diff --git a/bitacross-worker/core-primitives/top-pool/src/basic_pool.rs b/bitacross-worker/core-primitives/top-pool/src/basic_pool.rs new file mode 100644 index 0000000000..577898f4f0 --- /dev/null +++ b/bitacross-worker/core-primitives/top-pool/src/basic_pool.rs @@ -0,0 +1,258 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pub extern crate alloc; + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use std::sync::SgxMutex as Mutex; + +#[cfg(feature = "std")] +use std::sync::Mutex; + +use crate::{ + base_pool::TrustedOperation, + error::IntoPoolError, + pool::{ChainApi, Options as PoolOptions, Pool}, + primitives::{ + ImportNotificationStream, PoolFuture, PoolStatus, TrustedOperationPool, + TrustedOperationSource, TxHash, + }, +}; +use alloc::{boxed::Box, string::String, sync::Arc}; +use codec::Encode; +use core::{marker::PhantomData, pin::Pin}; +use itc_direct_rpc_server::SendRpcResponse; +use itp_stf_primitives::{traits::PoolTransactionValidation, types::ShardIdentifier}; +use its_primitives::types::BlockHash as SidechainBlockHash; +use jsonrpc_core::futures::{ + channel::oneshot, + future::{ready, Future, FutureExt}, +}; +use sp_runtime::{ + generic::BlockId, + traits::{Block as BlockT, NumberFor, Zero}, +}; +use std::{collections::HashMap, vec, vec::Vec}; + +type BoxedReadyIterator = Box>> + Send>; + +type ReadyIteratorFor = BoxedReadyIterator; + +type PolledIterator = Pin> + Send>>; + +struct ReadyPoll { + updated_at: NumberFor, + pollers: Vec<(NumberFor, oneshot::Sender)>, +} + +impl Default for ReadyPoll { + fn default() -> Self { + Self { updated_at: NumberFor::::zero(), pollers: Default::default() } + } +} + +impl ReadyPoll { + #[allow(unused)] + fn trigger(&mut self, number: NumberFor, iterator_factory: impl Fn() -> T) { + self.updated_at = number; + + let mut idx = 0; + while idx < self.pollers.len() { + if self.pollers[idx].0 <= number { + let poller_sender = self.pollers.swap_remove(idx); + let _ = poller_sender.1.send(iterator_factory()); + } else { + idx += 1; + } + } + } + + fn add(&mut self, number: NumberFor) -> oneshot::Receiver { + let (sender, receiver) = oneshot::channel(); + self.pollers.push((number, sender)); + receiver + } + + fn updated_at(&self) -> NumberFor { + self.updated_at + } +} + +/// Basic implementation of operation pool that can be customized by providing PoolApi. +pub struct BasicPool +where + Block: BlockT, + PoolApi: ChainApi + 'static, + RpcResponse: SendRpcResponse, +{ + pool: Arc>, + _api: Arc, + ready_poll: Arc, Block>>>, + _phantom: PhantomData, +} + +impl BasicPool +where + Block: BlockT, + PoolApi: ChainApi + 'static, + RpcResponse: SendRpcResponse, + TOP: Clone + Encode + PoolTransactionValidation + core::fmt::Debug + Sync + Send, +{ + /// Create new basic operation pool with provided api and custom + /// revalidation type. + pub fn create( + options: PoolOptions, + pool_api: Arc, + rpc_response_sender: Arc, + //prometheus: Option<&PrometheusRegistry>, + //revalidation_type: RevalidationType, + //spawner: impl SpawnNamed, + ) -> Self + where + ::Error: IntoPoolError, + { + let pool = Arc::new(Pool::new(options, pool_api.clone(), rpc_response_sender)); + BasicPool { + _api: pool_api, + pool, + ready_poll: Default::default(), + _phantom: Default::default(), + } + } +} + +// FIXME: obey clippy +#[allow(clippy::type_complexity)] +impl TrustedOperationPool + for BasicPool +where + Block: BlockT, + PoolApi: ChainApi + 'static, + ::Error: IntoPoolError, + RpcResponse: SendRpcResponse + 'static, + TOP: Send + Sync + PoolTransactionValidation + core::fmt::Debug + Encode + Clone + 'static, +{ + type Block = PoolApi::Block; + type InPoolOperation = TrustedOperation; + type Error = PoolApi::Error; + + fn submit_at( + &self, + at: &BlockId, + source: TrustedOperationSource, + ops: Vec, + shard: ShardIdentifier, + ) -> PoolFuture>, Self::Error> { + let pool = self.pool.clone(); + let at = *at; + async move { pool.submit_at(&at, source, ops, shard).await }.boxed() + } + + fn submit_one( + &self, + at: &BlockId, + source: TrustedOperationSource, + op: TOP, + shard: ShardIdentifier, + ) -> PoolFuture { + let pool = self.pool.clone(); + let at = *at; + async move { pool.submit_one(&at, source, op, shard).await }.boxed() + } + + fn submit_and_watch( + &self, + at: &BlockId, + source: TrustedOperationSource, + xt: TOP, + shard: ShardIdentifier, + ) -> PoolFuture { + let at = *at; + let pool = self.pool.clone(); + async move { pool.submit_and_watch(&at, source, xt, shard).await }.boxed() + } + + fn ready_at(&self, at: NumberFor, shard: ShardIdentifier) -> PolledIterator { + if self.ready_poll.lock().unwrap().updated_at() >= at { + let iterator: ReadyIteratorFor = Box::new(self.pool.validated_pool().ready(shard)); + return Box::pin(ready(iterator)) + } + + Box::pin(self.ready_poll.lock().unwrap().add(at).map(|received| { + received.unwrap_or_else(|e| { + log::warn!("Error receiving pending set: {:?}", e); + Box::new(vec![].into_iter()) + }) + })) + } + + fn ready(&self, shard: ShardIdentifier) -> ReadyIteratorFor { + Box::new(self.pool.validated_pool().ready(shard)) + } + + fn shards(&self) -> Vec { + self.pool.validated_pool().shards() + } + + fn remove_invalid( + &self, + hashes: &[TxHash], + shard: ShardIdentifier, + inblock: bool, + ) -> Vec> { + self.pool.validated_pool().remove_invalid(hashes, shard, inblock) + } + + fn status(&self, shard: ShardIdentifier) -> PoolStatus { + self.pool.validated_pool().status(shard) + } + + fn import_notification_stream(&self) -> ImportNotificationStream { + self.pool.validated_pool().import_notification_stream() + } + + fn on_broadcasted(&self, propagations: HashMap>) { + self.pool.validated_pool().on_broadcasted(propagations) + } + + fn hash_of(&self, xt: &TOP) -> TxHash { + self.pool.hash_of(xt) + } + + fn ready_transaction( + &self, + hash: &TxHash, + shard: ShardIdentifier, + ) -> Option> { + self.pool.validated_pool().ready_by_hash(hash, shard) + } + + fn on_block_imported(&self, hashes: &[TxHash], block_hash: SidechainBlockHash) { + self.pool.validated_pool().on_block_imported(hashes, block_hash); + } + + fn update_connection_state(&self, updates: Vec<(TxHash, (Vec, bool))>) { + self.pool.validated_pool().update_connection_state(updates); + } + + fn swap_rpc_connection_hash(&self, old_hash: TxHash, new_hash: TxHash) { + self.pool.validated_pool().swap_rpc_connection_hash(old_hash, new_hash); + } +} diff --git a/bitacross-worker/core-primitives/top-pool/src/error.rs b/bitacross-worker/core-primitives/top-pool/src/error.rs new file mode 100644 index 0000000000..47029b30e1 --- /dev/null +++ b/bitacross-worker/core-primitives/top-pool/src/error.rs @@ -0,0 +1,95 @@ +// This file is part of Substrate. + +// Copyright (C) 2018-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! TrustedOperation pool errors. + +use derive_more::{Display, From}; +use sp_runtime::transaction_validity::TransactionPriority as Priority; +use std::string::String; + +/// TrustedOperation pool result. +pub type Result = std::result::Result; + +/// TrustedOperation pool error type. +#[derive(Debug, From, Display)] +#[allow(missing_docs)] +pub enum Error { + #[display(fmt = "Unknown trusted operation")] + UnknownTrustedOperation, + + #[display(fmt = "Invalid trusted operation")] + InvalidTrustedOperation, + + /// Incorrect extrinsic format. + + /// The operation validity returned no "provides" tag. + /// + /// Such operations are not accepted to the pool, since we use those tags + /// to define identity of operations (occupance of the same "slot"). + #[display(fmt = "Trusted Operation does not provide any tags, so the pool can't identify it")] + NoTagsProvided, + + #[display(fmt = "Trusted Operation temporarily Banned")] + TemporarilyBanned, + + #[display(fmt = "Already imported")] + AlreadyImported, + + #[display(fmt = "Too low priority")] + TooLowPriority(Priority), + + #[display(fmt = "TrustedOperation with cyclic dependency")] + CycleDetected, + + #[display(fmt = "TrustedOperation couldn't enter the pool because of the limit")] + ImmediatelyDropped, + + #[from(ignore)] + #[display(fmt = "Invalid Block")] + InvalidBlockId(String), + + #[display(fmt = "The pool is not accepting future trusted operations")] + RejectedFutureTrustedOperation, + + #[display(fmt = "Extrinsic verification error")] + #[from(ignore)] + Verification, + + #[display(fmt = "Failed to send result of trusted operation to RPC client")] + FailedToSendUpdateToRpcClient(String), + + #[display(fmt = "Failed to unlock pool (mutex)")] + UnlockError, +} + +/// TrustedOperation pool error conversion. +pub trait IntoPoolError: Send + Sized { + /// Try to extract original `Error` + /// + /// This implementation is optional and used only to + /// provide more descriptive error messages for end users + /// of RPC API. + fn into_pool_error(self) -> std::result::Result { + Err(self) + } +} + +impl IntoPoolError for Error { + fn into_pool_error(self) -> std::result::Result { + Ok(self) + } +} diff --git a/bitacross-worker/core-primitives/top-pool/src/future.rs b/bitacross-worker/core-primitives/top-pool/src/future.rs new file mode 100644 index 0000000000..2ceb34827e --- /dev/null +++ b/bitacross-worker/core-primitives/top-pool/src/future.rs @@ -0,0 +1,316 @@ +// This file is part of Substrate. + +// Copyright (C) 2018-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +pub extern crate alloc; + +use crate::{base_pool::TrustedOperation, primitives::TxHash}; +use alloc::{boxed::Box, fmt, sync::Arc, vec, vec::Vec}; + +use itp_stf_primitives::types::ShardIdentifier; +use sp_core::hexdisplay::HexDisplay; +use sp_runtime::transaction_validity::TransactionTag as Tag; +use std::{ + collections::{HashMap, HashSet}, + time::Instant, +}; + +/// TrustedOperation with partially satisfied dependencies. +pub struct WaitingTrustedOperations { + /// TrustedOperation details. + pub operation: Arc>, + /// Tags that are required and have not been satisfied yet by other operations in the pool. + pub missing_tags: HashSet, + /// Time of import to the Future Queue. + pub imported_at: Instant, +} + +impl fmt::Debug for WaitingTrustedOperations { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "WaitingTrustedOperations {{ ")?; + //write!(fmt, "imported_at: {:?}, ", self.imported_at)?; + write!(fmt, "operation: {:?}, ", self.operation)?; + write!(fmt, "missing_tags: {{")?; + let mut it = self.missing_tags.iter().map(HexDisplay::from); + if let Some(tag) = it.next() { + write!(fmt, "{}", tag)?; + } + for tag in it { + write!(fmt, ", {}", tag)?; + } + write!(fmt, " }}}}") + } +} + +impl Clone for WaitingTrustedOperations { + fn clone(&self) -> Self { + WaitingTrustedOperations { + operation: self.operation.clone(), + missing_tags: self.missing_tags.clone(), + imported_at: self.imported_at, + } + } +} + +impl WaitingTrustedOperations { + /// Creates a new `WaitingTrustedOperations`. + /// + /// Computes the set of missing tags based on the requirements and tags that + /// are provided by all operations in the ready queue. + pub fn new( + operation: TrustedOperation, + provided: Option<&HashMap>, + recently_pruned: &[HashSet], + ) -> Self { + let missing_tags = operation + .requires + .iter() + .filter(|tag| { + // is true if the tag is already satisfied either via operation in the pool + // or one that was recently included. + + let is_provided = recently_pruned.iter().any(|x| x.contains(&**tag)) + || match provided { + Some(tags) => tags.contains_key(&**tag), + None => false, + }; + + !is_provided + }) + .cloned() + .collect(); + + WaitingTrustedOperations { + operation: Arc::new(operation), + missing_tags, + imported_at: Instant::now(), + } + } + + /// Marks the tag as satisfied. + // FIXME: obey clippy + #[allow(clippy::ptr_arg)] + pub fn satisfy_tag(&mut self, tag: &Tag) { + self.missing_tags.remove(tag); + } + + /// Returns true if operation has all requirements satisfied. + pub fn is_ready(&self) -> bool { + self.missing_tags.is_empty() + } +} + +/// A pool of operations that are not yet ready to be included in the block. +/// +/// Contains operations that are still awaiting for some other operations that +/// could provide a tag that they require. +#[derive(Debug)] +pub struct FutureTrustedOperations { + /// tags that are not yet provided by any operation and we await for them + wanted_tags: HashMap>>, + /// Transactions waiting for a particular other operation + waiting: HashMap>>, +} + +impl Default for FutureTrustedOperations { + fn default() -> Self { + FutureTrustedOperations { wanted_tags: Default::default(), waiting: Default::default() } + } +} + +const WAITING_PROOF: &str = r"# +In import we always insert to `waiting` if we push to `wanted_tags`; +when removing from `waiting` we always clear `wanted_tags`; +every hash from `wanted_tags` is always present in `waiting`; +qed +#"; + +#[allow(clippy::len_without_is_empty)] +impl FutureTrustedOperations { + /// Import operation to Future queue. + /// + /// Only operations that don't have all their tags satisfied should occupy + /// the Future queue. + /// As soon as required tags are provided by some other operations that are ready + /// we should remove the operations from here and move them to the Ready queue. + pub fn import(&mut self, tx: WaitingTrustedOperations, shard: ShardIdentifier) { + assert!(!tx.is_ready(), "TrustedOperation is ready."); + if let Some(tx_pool_waiting) = self.waiting.get(&shard) { + assert!( + !tx_pool_waiting.contains_key(&tx.operation.hash), + "TrustedOperation is already imported." + ); + } + + let tx_pool_waiting_map = self.waiting.entry(shard).or_insert_with(HashMap::new); + let tx_pool_wanted_map = self.wanted_tags.entry(shard).or_insert_with(HashMap::new); + // Add all tags that are missing + for tag in &tx.missing_tags { + let entry = tx_pool_wanted_map.entry(tag.clone()).or_insert_with(HashSet::new); + entry.insert(tx.operation.hash); + } + + // Add the operation to a by-hash waiting map + tx_pool_waiting_map.insert(tx.operation.hash, tx); + } + + /// Returns true if given hash is part of the queue. + pub fn contains(&self, hash: &TxHash, shard: ShardIdentifier) -> bool { + if let Some(tx_pool_waiting) = self.waiting.get(&shard) { + return tx_pool_waiting.contains_key(hash) + } + false + } + + /// Returns a list of known operations + pub fn by_hashes( + &self, + hashes: &[TxHash], + shard: ShardIdentifier, + ) -> Vec>>> { + if let Some(tx_pool_waiting) = self.waiting.get(&shard) { + return hashes + .iter() + .map(|h| tx_pool_waiting.get(h).map(|x| x.operation.clone())) + .collect() + } + vec![] + } + + /// Satisfies provided tags in operations that are waiting for them. + /// + /// Returns (and removes) operations that became ready after their last tag got + /// satisfied and now we can remove them from Future and move to Ready queue. + pub fn satisfy_tags>( + &mut self, + tags: impl IntoIterator, + shard: ShardIdentifier, + ) -> Vec> { + let mut became_ready = vec![]; + + for tag in tags { + if let Some(tx_pool_wanted) = self.wanted_tags.get_mut(&shard) { + if let Some(hashes) = tx_pool_wanted.remove(tag.as_ref()) { + if let Some(tx_pool_waiting) = self.waiting.get_mut(&shard) { + for hash in hashes { + let is_ready = { + let tx = tx_pool_waiting.get_mut(&hash).expect(WAITING_PROOF); + tx.satisfy_tag(tag.as_ref()); + tx.is_ready() + }; + + if is_ready { + let tx = tx_pool_waiting.remove(&hash).expect(WAITING_PROOF); + became_ready.push(tx); + } + } + } + } + } + } + + became_ready + } + + /// Removes operations for given list of hashes. + /// + /// Returns a list of actually removed operations. + pub fn remove( + &mut self, + hashes: &[TxHash], + shard: ShardIdentifier, + ) -> Vec>> { + let mut removed = vec![]; + if let Some(tx_pool_waiting) = self.waiting.get_mut(&shard) { + if let Some(tx_pool_wanted) = self.wanted_tags.get_mut(&shard) { + for hash in hashes { + if let Some(waiting_tx) = tx_pool_waiting.remove(hash) { + // remove from wanted_tags as well + for tag in waiting_tx.missing_tags { + let remove = if let Some(wanted) = tx_pool_wanted.get_mut(&tag) { + wanted.remove(hash); + wanted.is_empty() + } else { + false + }; + if remove { + tx_pool_wanted.remove(&tag); + } + } + // add to result + removed.push(waiting_tx.operation) + } + } + } + } + removed + } + + /// Fold a list of future operations to compute a single value. + pub fn fold, &WaitingTrustedOperations) -> Option>( + &mut self, + f: F, + shard: ShardIdentifier, + ) -> Option { + if let Some(tx_pool) = self.waiting.get(&shard) { + return tx_pool.values().fold(None, f) + } + None + } + + /// Returns iterator over all future operations + pub fn all( + &self, + shard: ShardIdentifier, + ) -> Box> + '_> { + if let Some(tx_pool) = self.waiting.get(&shard) { + return Box::new(tx_pool.values().map(|waiting| &*waiting.operation)) + } + Box::new(core::iter::empty()) + } + + /// Removes and returns all future operations. + pub fn clear(&mut self, shard: ShardIdentifier) -> Vec>> { + if let Some(wanted_tx_pool) = self.wanted_tags.get_mut(&shard) { + wanted_tx_pool.clear(); + return self + .waiting + .get_mut(&shard) + .unwrap() + .drain() + .map(|(_, tx)| tx.operation) + .collect() + } + vec![] + } + + /// Returns number of operations in the Future queue. + pub fn len(&self, shard: ShardIdentifier) -> usize { + if let Some(tx_pool) = self.waiting.get(&shard) { + return tx_pool.len() + } + 0 + } + + /// Returns sum of encoding lengths of all operations in this queue. + pub fn bytes(&self, shard: ShardIdentifier) -> usize { + if let Some(tx_pool) = self.waiting.get(&shard) { + return tx_pool.values().fold(0, |acc, tx| acc + tx.operation.bytes) + } + 0 + } +} diff --git a/bitacross-worker/core-primitives/top-pool/src/lib.rs b/bitacross-worker/core-primitives/top-pool/src/lib.rs new file mode 100644 index 0000000000..fdd46ff9fe --- /dev/null +++ b/bitacross-worker/core-primitives/top-pool/src/lib.rs @@ -0,0 +1,47 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +// re-export module to properly feature gate sgx and regular std environment +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod sgx_reexport_prelude { + pub use jsonrpc_core_sgx as jsonrpc_core; + pub use linked_hash_map_sgx as linked_hash_map; +} + +pub mod base_pool; +pub mod basic_pool; +pub mod error; +pub mod future; +pub mod listener; +pub mod pool; +pub mod primitives; +pub mod ready; +pub mod rotator; +pub mod tracked_map; +pub mod validated_pool; +pub mod watcher; + +#[cfg(any(test, feature = "mocks"))] +pub mod mocks; diff --git a/bitacross-worker/core-primitives/top-pool/src/listener.rs b/bitacross-worker/core-primitives/top-pool/src/listener.rs new file mode 100644 index 0000000000..0e069597cb --- /dev/null +++ b/bitacross-worker/core-primitives/top-pool/src/listener.rs @@ -0,0 +1,185 @@ +// This file is part of Substrate. + +// Copyright (C) 2018-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use crate::{primitives::TxHash, watcher::Watcher}; + +use itc_direct_rpc_server::SendRpcResponse; +use itp_types::BlockHash as SidechainBlockHash; +use linked_hash_map::LinkedHashMap; +use log::{debug, trace}; + +use std::{collections::HashMap, string::String, sync::Arc, vec, vec::Vec}; + +/// Extrinsic pool default listener. +#[derive(Default)] +pub struct Listener +where + R: SendRpcResponse, +{ + watchers: HashMap>, + finality_watchers: LinkedHashMap>, + rpc_response_sender: Arc, +} + +/// Maximum number of blocks awaiting finality at any time. +const MAX_FINALITY_WATCHERS: usize = 512; + +impl Listener +where + R: SendRpcResponse, +{ + pub fn new(rpc_response_sender: Arc) -> Self { + Listener { + watchers: Default::default(), + finality_watchers: Default::default(), + rpc_response_sender, + } + } + + fn fire(&mut self, hash: &TxHash, fun: F) + where + F: FnOnce(&mut Watcher), + { + let clean = if let Some(h) = self.watchers.get_mut(hash) { + fun(h); + h.is_done() + } else { + false + }; + + if clean { + self.watchers.remove(hash); + } + } + + /// Creates a new watcher for given verified extrinsic. + /// + /// The watcher can be used to subscribe to life-cycle events of that extrinsic. + pub fn create_watcher(&mut self, hash: TxHash) { + let new_watcher = Watcher::new_watcher(hash, self.rpc_response_sender.clone()); + self.watchers.insert(hash, new_watcher); + } + + /// Notify the listeners about extrinsic broadcast. + pub fn broadcasted(&mut self, hash: &TxHash, peers: Vec) { + trace!(target: "txpool", "[{:?}] Broadcasted", hash); + self.fire(hash, |watcher| watcher.broadcast(peers)); + } + + /// Notify listeners about top execution. + pub fn top_executed(&mut self, hash: &TxHash, response: &[u8], force_wait: bool) { + trace!(target: "txpool", "[{:?}] Top Executed", hash); + self.fire(hash, |watcher| watcher.top_executed(response, force_wait)); + } + + /// New operation was added to the ready pool or promoted from the future pool. + pub fn ready(&mut self, tx: &TxHash, old: Option<&TxHash>) { + trace!(target: "txpool", "[{:?}] Ready (replaced with {:?})", tx, old); + self.fire(tx, |watcher| watcher.ready()); + if let Some(old) = old { + self.fire(old, |watcher| watcher.usurped()); + } + } + + /// New operation was added to the future pool. + pub fn future(&mut self, tx: &TxHash) { + trace!(target: "txpool", "[{:?}] Future", tx); + self.fire(tx, |watcher| watcher.future()); + } + + /// TrustedOperation was dropped from the pool because of the limit. + pub fn dropped(&mut self, tx: &TxHash, by: Option<&TxHash>) { + trace!(target: "txpool", "[{:?}] Dropped (replaced with {:?})", tx, by); + self.fire(tx, |watcher| match by { + Some(_) => watcher.usurped(), + None => watcher.dropped(), + }) + } + + /// TrustedOperation was removed as invalid. + pub fn invalid(&mut self, tx: &TxHash) { + self.fire(tx, |watcher| watcher.invalid()); + } + + /// TrustedOperation was pruned from the pool. + #[allow(clippy::or_fun_call)] + pub fn pruned(&mut self, block_hash: SidechainBlockHash, tx: &TxHash) { + debug!(target: "txpool", "[{:?}] Pruned at {:?}", tx, block_hash); + self.fire(tx, |s| s.in_block(block_hash)); + self.finality_watchers.entry(block_hash).or_insert(vec![]).push(*tx); + + while self.finality_watchers.len() > MAX_FINALITY_WATCHERS { + if let Some((_hash, txs)) = self.finality_watchers.pop_front() { + for tx in txs { + self.fire(&tx, |s| s.finality_timeout()); + } + } + } + } + + /// TrustedOperation in block. + pub fn in_block(&mut self, tx: &TxHash, block_hash: SidechainBlockHash) { + self.fire(tx, |s| s.in_block(block_hash)); + } + + /// The block this operation was included in has been retracted. + pub fn retracted(&mut self, block_hash: SidechainBlockHash) { + if let Some(hashes) = self.finality_watchers.remove(&block_hash) { + for hash in hashes { + self.fire(&hash, |s| s.retracted()) + } + } + } + + /// Notify all watchers that operations have been finalized + pub fn finalized(&mut self, block_hash: SidechainBlockHash) { + if let Some(hashes) = self.finality_watchers.remove(&block_hash) { + for hash in hashes { + log::debug!(target: "txpool", "[{:?}] Sent finalization event (block {:?})", hash, block_hash); + self.fire(&hash, |s| s.finalized()) + } + } + } + + /// Litentry: set the rpc response value and force_wait flag for a given TrustedOperation `tx`. + pub fn update_connection_state( + &mut self, + tx: &TxHash, + encoded_value: Vec, + force_wait: bool, + ) { + self.fire(tx, |s| s.update_connection_state(encoded_value, force_wait)); + } + + /// Litentry: swap the old hash with the new one in rpc connection registry + pub fn swap_rpc_connection_hash(&mut self, old_hash: TxHash, new_hash: TxHash) { + log::debug!("Swapping connection {:?} to {:?}", &old_hash, &new_hash); + // It's possible that the old top (hash) is already removed from the pool when we + // request to swap hashes, in this case we just create one to facilitate the swap + if let Some(w) = self.watchers.get(&old_hash) { + w.swap_rpc_connection_hash(new_hash); + } else { + // do not insert it to `watchers`, will be deallocated if it goes out of scope + Watcher::new_watcher(old_hash, self.rpc_response_sender.clone()) + .swap_rpc_connection_hash(new_hash); + } + } +} diff --git a/bitacross-worker/core-primitives/top-pool/src/mocks/mod.rs b/bitacross-worker/core-primitives/top-pool/src/mocks/mod.rs new file mode 100644 index 0000000000..81b1c65ebe --- /dev/null +++ b/bitacross-worker/core-primitives/top-pool/src/mocks/mod.rs @@ -0,0 +1,22 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(any(test, feature = "mocks"))] +pub mod rpc_responder_mock; + +#[cfg(feature = "mocks")] +pub mod trusted_operation_pool_mock; diff --git a/bitacross-worker/core-primitives/top-pool/src/mocks/rpc_responder_mock.rs b/bitacross-worker/core-primitives/top-pool/src/mocks/rpc_responder_mock.rs new file mode 100644 index 0000000000..766b92def8 --- /dev/null +++ b/bitacross-worker/core-primitives/top-pool/src/mocks/rpc_responder_mock.rs @@ -0,0 +1,76 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use itc_direct_rpc_server::{DirectRpcResult, RpcHash, SendRpcResponse}; +use itp_types::TrustedOperationStatus; +use std::{marker::PhantomData, vec::Vec}; + +pub struct RpcResponderMock { + _hash: PhantomData, +} + +impl RpcResponderMock { + pub fn new() -> Self { + RpcResponderMock { _hash: PhantomData } + } +} + +impl Default for RpcResponderMock { + fn default() -> Self { + Self::new() + } +} + +impl SendRpcResponse for RpcResponderMock +where + Hash: RpcHash, +{ + type Hash = Hash; + + fn update_status_event( + &self, + _hash: Self::Hash, + _status_update: TrustedOperationStatus, + ) -> DirectRpcResult<()> { + Ok(()) + } + + fn send_state(&self, _hash: Self::Hash, _state_encoded: Vec) -> DirectRpcResult<()> { + Ok(()) + } + + fn update_force_wait(&self, _hash: Self::Hash, _force_wait: bool) -> DirectRpcResult<()> { + Ok(()) + } + + fn update_connection_state( + &self, + _hash: Self::Hash, + _encoded_value: Vec, + _force_wait: bool, + ) -> DirectRpcResult<()> { + Ok(()) + } + + fn swap_hash(&self, _old_hash: Self::Hash, _new_hash: Self::Hash) -> DirectRpcResult<()> { + Ok(()) + } + + fn is_force_wait(&self, _hash: Self::Hash) -> bool { + false + } +} diff --git a/bitacross-worker/core-primitives/top-pool/src/mocks/trusted_operation_pool_mock.rs b/bitacross-worker/core-primitives/top-pool/src/mocks/trusted_operation_pool_mock.rs new file mode 100644 index 0000000000..72f5514da6 --- /dev/null +++ b/bitacross-worker/core-primitives/top-pool/src/mocks/trusted_operation_pool_mock.rs @@ -0,0 +1,227 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +#[cfg(feature = "sgx")] +use std::sync::SgxRwLock as RwLock; + +#[cfg(feature = "std")] +use std::sync::RwLock; + +use crate::{ + base_pool::TrustedOperation, + error::Error, + primitives::{ + ImportNotificationStream, PoolFuture, PoolStatus, TrustedOperationPool, + TrustedOperationSource, TxHash, + }, +}; +use codec::Encode; +use core::{future::Future, pin::Pin}; + +use itp_types::{Block, BlockHash as SidechainBlockHash, ShardIdentifier, H256}; +use jsonrpc_core::futures::future::ready; +use sp_runtime::{ + generic::BlockId, + traits::{BlakeTwo256, Hash, NumberFor}, +}; +use std::{boxed::Box, collections::HashMap, string::String, sync::Arc, vec, vec::Vec}; + +/// Mock for the trusted operation pool +/// +/// To be used in unit tests +pub struct TrustedOperationPoolMock { + submitted_transactions: RwLock>>, +} + +/// Transaction payload +#[derive(Clone, PartialEq)] +pub struct TxPayload { + pub block_id: BlockId< as TrustedOperationPool>::Block>, + pub source: TrustedOperationSource, + pub xts: Vec, + pub shard: ShardIdentifier, +} + +impl Default for TrustedOperationPoolMock { + fn default() -> Self { + TrustedOperationPoolMock:: { submitted_transactions: RwLock::new(HashMap::new()) } + } +} + +impl TrustedOperationPoolMock { + pub fn get_last_submitted_transactions(&self) -> HashMap> { + let transactions = self.submitted_transactions.read().unwrap(); + transactions.clone() + } + + fn map_stf_top_to_tx(stf_top: &TOP) -> Arc> { + Arc::new(TrustedOperation:: { + data: stf_top.clone(), + bytes: 0, + hash: hash_of_top(stf_top), + priority: 0u64, + valid_till: 0u64, + requires: vec![], + provides: vec![], + propagate: false, + source: TrustedOperationSource::External, + }) + } +} + +impl TrustedOperationPool for TrustedOperationPoolMock +where + TOP: Encode + Clone + Sync + Send + 'static, +{ + type Block = Block; + type InPoolOperation = TrustedOperation; + type Error = Error; + + #[allow(clippy::type_complexity)] + fn submit_at( + &self, + at: &BlockId, + source: TrustedOperationSource, + xts: Vec, + shard: ShardIdentifier, + ) -> PoolFuture>, Self::Error> { + let mut transactions = self.submitted_transactions.write().unwrap(); + transactions.insert(shard, TxPayload { block_id: *at, source, xts: xts.clone(), shard }); + + let top_hashes: Vec> = + xts.iter().map(|top| Ok(hash_of_top(top))).collect(); + + Box::pin(ready(Ok(top_hashes))) + } + + fn submit_one( + &self, + at: &BlockId, + source: TrustedOperationSource, + xt: TOP, + shard: ShardIdentifier, + ) -> PoolFuture { + let mut transactions = self.submitted_transactions.write().unwrap(); + transactions + .insert(shard, TxPayload { block_id: *at, source, xts: vec![xt.clone()], shard }); + + let top_hash = hash_of_top(&xt); + + Box::pin(ready(Ok(top_hash))) + } + + fn submit_and_watch( + &self, + at: &BlockId, + source: TrustedOperationSource, + xt: TOP, + shard: ShardIdentifier, + ) -> PoolFuture { + self.submit_one(at, source, xt, shard) + } + + #[allow(clippy::type_complexity)] + fn ready_at( + &self, + _at: NumberFor, + _shard: ShardIdentifier, + ) -> Pin< + Box< + dyn Future> + Send>> + Send, + >, + > { + unimplemented!() + } + + #[allow(clippy::type_complexity)] + fn ready( + &self, + shard: ShardIdentifier, + ) -> Box> + Send> { + let transactions = self.submitted_transactions.read().unwrap(); + let ready_transactions = transactions + .get(&shard) + .map(|payload| payload.xts.iter().map(Self::map_stf_top_to_tx).collect()) + .unwrap_or_else(Vec::new); + Box::new(ready_transactions.into_iter()) + } + + fn shards(&self) -> Vec { + let transactions = self.submitted_transactions.read().unwrap(); + transactions.iter().map(|(shard, _)| *shard).collect() + } + + fn remove_invalid( + &self, + _hashes: &[TxHash], + _shard: ShardIdentifier, + _inblock: bool, + ) -> Vec> { + Vec::new() + } + + fn status(&self, shard: ShardIdentifier) -> PoolStatus { + let transactions = self.submitted_transactions.read().unwrap(); + transactions + .get(&shard) + .map(|payload| PoolStatus { + ready: payload.xts.len(), + ready_bytes: 0, + future: 0, + future_bytes: 0, + }) + .unwrap_or_else(default_pool_status) + } + + fn import_notification_stream(&self) -> ImportNotificationStream { + unimplemented!() + } + + fn on_broadcasted(&self, _propagations: HashMap>) { + unimplemented!() + } + + fn hash_of(&self, xt: &TOP) -> TxHash { + hash_of_top(xt) + } + + fn ready_transaction( + &self, + _hash: &TxHash, + _shard: ShardIdentifier, + ) -> Option> { + unimplemented!() + } + + fn on_block_imported(&self, _hashes: &[TxHash], _block_hash: SidechainBlockHash) {} + + fn update_connection_state(&self, _updates: Vec<(TxHash, (Vec, bool))>) {} + + fn swap_rpc_connection_hash(&self, _old_hash: TxHash, _new_hash: TxHash) {} +} + +fn default_pool_status() -> PoolStatus { + PoolStatus { ready: 0, ready_bytes: 0, future: 0, future_bytes: 0 } +} + +fn hash_of_top(top: &TOP) -> H256 { + top.using_encoded(|x| BlakeTwo256::hash(x)) +} diff --git a/bitacross-worker/core-primitives/top-pool/src/pool.rs b/bitacross-worker/core-primitives/top-pool/src/pool.rs new file mode 100644 index 0000000000..17a8fcbd5b --- /dev/null +++ b/bitacross-worker/core-primitives/top-pool/src/pool.rs @@ -0,0 +1,818 @@ +// This file is part of Substrate. + +// Copyright (C) 2018-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; +use core::{fmt::Debug, marker::PhantomData}; + +use crate::{ + base_pool as base, error, + primitives::{TrustedOperationSource, TxHash}, + validated_pool::{ValidatedOperation, ValidatedPool}, +}; +use codec::Encode; +use core::matches; +use itc_direct_rpc_server::SendRpcResponse; +use itp_stf_primitives::{traits::PoolTransactionValidation, types::ShardIdentifier}; +use itp_types::BlockHash as SidechainBlockHash; +use jsonrpc_core::futures::{channel::mpsc::Receiver, future, Future}; +use sp_runtime::{ + generic::BlockId, + traits::{self, Block as BlockT, SaturatedConversion}, + transaction_validity::{TransactionTag as Tag, TransactionValidity, TransactionValidityError}, +}; +use std::{collections::HashMap, format, sync::Arc, time::Instant, vec::Vec}; + +/// Modification notification event stream type; +pub type EventStream = Receiver; + +/// Block hash type for a pool. +pub type BlockHash = <::Block as traits::Block>::Hash; +/// Extrinsic hash type for a pool. +pub type ExtrinsicHash = <::Block as traits::Block>::Hash; +/// Extrinsic type for a pool. +//pub type ExtrinsicFor = <::Block as traits::Block>::Extrinsic; +/// Block number type for the ChainApi +pub type NumberFor = traits::NumberFor<::Block>; +/// A type of operation stored in the pool +pub type TransactionFor = Arc>; +/// A type of validated operation stored in the pool. +pub type ValidatedOperationFor = ValidatedOperation::Error>; + +/// Concrete extrinsic validation and query logic. +pub trait ChainApi: Send + Sync { + /// Block type. + type Block: BlockT; + /// Error type. + type Error: From; + /// Validate operation future. + type ValidationFuture: Future> + Send + Unpin; + /// Body future (since block body might be remote) + type BodyFuture: Future, Self::Error>> + Unpin + Send + 'static; + + /// Verify extrinsic at given block. + fn validate_transaction( + &self, + source: TrustedOperationSource, + uxt: TOP, + shard: ShardIdentifier, + ) -> Self::ValidationFuture; + + /// Returns a block number given the block id. + fn block_id_to_number( + &self, + at: &BlockId, + ) -> Result>, Self::Error>; + + /// Returns a block hash given the block id. + fn block_id_to_hash( + &self, + at: &BlockId, + ) -> Result, Self::Error>; + + /// Returns hash and encoding length of the extrinsic. + fn hash_and_length(&self, uxt: &TOP) -> (TxHash, usize); + + /// Returns a block body given the block id. + fn block_body(&self, at: &BlockId) -> Self::BodyFuture; +} + +/// Pool configuration options. +#[derive(Debug, Clone)] +pub struct Options { + /// Ready queue limits. + pub ready: base::Limit, + /// Future queue limits. + pub future: base::Limit, + /// Reject future operations. + pub reject_future_operations: bool, +} + +impl Default for Options { + fn default() -> Self { + Options { + ready: base::Limit { count: 8192, total_bytes: 20 * 1024 * 1024 }, + future: base::Limit { count: 512, total_bytes: 1024 * 1024 }, + reject_future_operations: false, + } + } +} + +/// Should we check that the operation is banned +/// in the pool, before we verify it? +#[derive(Copy, Clone)] +enum CheckBannedBeforeVerify { + Yes, + No, +} + +/// Extrinsics pool that performs validation. +pub struct Pool +where + R: SendRpcResponse, +{ + validated_pool: Arc>, + _phantom: PhantomData, +} + +impl Pool +where + ::Error: error::IntoPoolError, + R: SendRpcResponse, + TOP: Encode + Clone + PoolTransactionValidation + core::fmt::Debug + Send + Sync, +{ + /// Create a new operation pool. + pub fn new(options: Options, api: Arc, rpc_response_sender: Arc) -> Self { + Pool { + validated_pool: Arc::new(ValidatedPool::new(options, api, rpc_response_sender)), + _phantom: Default::default(), + } + } + + /// Imports a bunch of unverified extrinsics to the pool + pub async fn submit_at( + &self, + at: &BlockId, + source: TrustedOperationSource, + xts: impl IntoIterator, + shard: ShardIdentifier, + ) -> Result>, B::Error> { + let xts = xts.into_iter().map(|xt| (source, xt)); + let validated_transactions = + self.verify(at, xts, CheckBannedBeforeVerify::Yes, shard).await?; + Ok(self.validated_pool.submit(validated_transactions.into_values(), shard)) + } + + /// Resubmit the given extrinsics to the pool. + /// + /// This does not check if a operation is banned, before we verify it again. + pub async fn resubmit_at( + &self, + at: &BlockId, + source: TrustedOperationSource, + xts: impl IntoIterator, + shard: ShardIdentifier, + ) -> Result>, B::Error> { + let xts = xts.into_iter().map(|xt| (source, xt)); + let validated_transactions = + self.verify(at, xts, CheckBannedBeforeVerify::No, shard).await?; + Ok(self.validated_pool.submit(validated_transactions.into_values(), shard)) + } + + /// Imports one unverified extrinsic to the pool + pub async fn submit_one( + &self, + at: &BlockId, + source: TrustedOperationSource, + xt: TOP, + shard: ShardIdentifier, + ) -> Result { + let res = self.submit_at(at, source, std::iter::once(xt), shard).await?.pop(); + res.expect("One extrinsic passed; one result returned; qed") + } + + /// Import a single extrinsic and starts to watch their progress in the pool. + pub async fn submit_and_watch( + &self, + at: &BlockId, + source: TrustedOperationSource, + xt: TOP, + shard: ShardIdentifier, + ) -> Result { + //TODO + //let block_number = self.resolve_block_number(at)?; + // dummy value: + let block_number = 0; + let (_, tx) = self + .verify_one(at, block_number, source, xt, CheckBannedBeforeVerify::Yes, shard) + .await; + self.validated_pool.submit_and_watch(tx, shard) + } + + /// Resubmit some operation that were validated elsewhere. + pub fn resubmit( + &self, + revalidated_transactions: HashMap>, + shard: ShardIdentifier, + ) { + let now = Instant::now(); + self.validated_pool.resubmit(revalidated_transactions, shard); + log::debug!(target: "txpool", + "Resubmitted. Took {} ms. Status: {:?}", + now.elapsed().as_millis(), + self.validated_pool.status(shard) + ); + } + + /// Prunes known ready operations. + /// + /// Used to clear the pool from operations that were part of recently imported block. + /// The main difference from the `prune` is that we do not revalidate any operations + /// and ignore unknown passed hashes. + pub fn prune_known( + &self, + at: &BlockId, + hashes: &[TxHash], + shard: ShardIdentifier, + ) -> Result<(), B::Error> { + // Get details of all extrinsics that are already in the pool + #[allow(clippy::filter_map_identity)] + // false positive. Filter map does filter because x is an option + let in_pool_tags = self + .validated_pool + .extrinsics_tags(hashes, shard) + .into_iter() + .filter_map(|x| x) + .flatten(); + + // Prune all operations that provide given tags + let prune_status = self.validated_pool.prune_tags(in_pool_tags, shard)?; + let pruned_transactions = + hashes.iter().cloned().chain(prune_status.pruned.iter().map(|tx| tx.hash)); + self.validated_pool.fire_pruned(at, pruned_transactions) + } + + /// Prunes ready operations. + /// + /// Used to clear the pool from operations that were part of recently imported block. + /// To perform pruning we need the tags that each extrinsic provides and to avoid calling + /// into runtime too often we first lookup all extrinsics that are in the pool and get + /// their provided tags from there. Otherwise we query the runtime at the `parent` block. + pub async fn prune( + &self, + at: &BlockId, + _parent: &BlockId, + extrinsics: &[TOP], + shard: ShardIdentifier, + ) -> Result<(), B::Error> { + log::debug!( + target: "txpool", + "Starting pruning of block {:?} (extrinsics: {})", + at, + extrinsics.len() + ); + // Get details of all extrinsics that are already in the pool + let in_pool_hashes = + extrinsics.iter().map(|extrinsic| self.hash_of(extrinsic)).collect::>(); + let in_pool_tags = self.validated_pool.extrinsics_tags(&in_pool_hashes, shard); + + // Zip the ones from the pool with the full list (we get pairs `(Extrinsic, Option>)`) + let all = extrinsics.iter().zip(in_pool_tags.into_iter()); + + let mut future_tags = Vec::new(); + for (extrinsic, in_pool_tags) in all { + match in_pool_tags { + // reuse the tags for extrinsics that were found in the pool + Some(tags) => future_tags.extend(tags), + // if it's not found in the pool query the runtime at parent block + // to get validity info and tags that the extrinsic provides. + None => { + let validity = self + .validated_pool + .api() + .validate_transaction( + TrustedOperationSource::InBlock, + extrinsic.clone(), + shard, + ) + .await; + + if let Ok(Ok(validity)) = validity { + future_tags.extend(validity.provides); + } + }, + } + } + + self.prune_tags(at, future_tags, in_pool_hashes, shard).await + } + + /// Prunes ready operations that provide given list of tags. + /// + /// Given tags are assumed to be always provided now, so all operations + /// in the Future Queue that require that particular tag (and have other + /// requirements satisfied) are promoted to Ready Queue. + /// + /// Moreover for each provided tag we remove operations in the pool that: + /// 1. Provide that tag directly + /// 2. Are a dependency of pruned operation. + /// + /// Returns operations that have been removed from the pool and must be reverified + /// before reinserting to the pool. + /// + /// By removing predecessor operations as well we might actually end up + /// pruning too much, so all removed operations are reverified against + /// the runtime (`validate_transaction`) to make sure they are invalid. + /// + /// However we avoid revalidating operations that are contained within + /// the second parameter of `known_imported_hashes`. These operations + /// (if pruned) are not revalidated and become temporarily banned to + /// prevent importing them in the (near) future. + pub async fn prune_tags( + &self, + at: &BlockId, + tags: impl IntoIterator, + known_imported_hashes: impl IntoIterator + Clone, + shard: ShardIdentifier, + ) -> Result<(), B::Error> { + log::debug!(target: "txpool", "Pruning at {:?}", at); + // Prune all operations that provide given tags + let prune_status = match self.validated_pool.prune_tags(tags, shard) { + Ok(prune_status) => prune_status, + Err(e) => return Err(e), + }; + + // Make sure that we don't revalidate extrinsics that were part of the recently + // imported block. This is especially important for UTXO-like chains cause the + // inputs are pruned so such operation would go to future again. + self.validated_pool + .ban(&Instant::now(), known_imported_hashes.clone().into_iter()); + + // Try to re-validate pruned operations since some of them might be still valid. + // note that `known_imported_hashes` will be rejected here due to temporary ban. + let pruned_hashes = prune_status.pruned.iter().map(|tx| tx.hash).collect::>(); + let pruned_transactions = + prune_status.pruned.into_iter().map(|tx| (tx.source, tx.data.clone())); + + let reverified_transactions = self + .verify(at, pruned_transactions, CheckBannedBeforeVerify::Yes, shard) + .await?; + + log::trace!(target: "txpool", "Pruning at {:?}. Resubmitting operations.", at); + // And finally - submit reverified operations back to the pool + + self.validated_pool.resubmit_pruned( + at, + known_imported_hashes, + pruned_hashes, + reverified_transactions.into_values().collect(), + shard, + ) + } + + /// Returns operation hash + pub fn hash_of(&self, xt: &TOP) -> TxHash { + self.validated_pool.api().hash_and_length(xt).0 + } + + /// Resolves block number by id. + fn _resolve_block_number(&self, at: &BlockId) -> Result, B::Error> { + self.validated_pool.api().block_id_to_number(at).and_then(|number| { + number.ok_or_else(|| error::Error::InvalidBlockId(format!("{:?}", at)).into()) + }) + } + + /// Returns future that validates a bunch of operations at given block. + async fn verify( + &self, + at: &BlockId, + xts: impl IntoIterator, + check: CheckBannedBeforeVerify, + shard: ShardIdentifier, + ) -> Result>, B::Error> { + //FIXME: Nicer verify + // we need a block number to compute tx validity + //let block_number = self.resolve_block_number(at)?; + // dummy blocknumber + //pub type NumberFor = traits::NumberFor<::Block>; + let block_number = 0; + + let res = future::join_all( + xts.into_iter() + .map(|(source, xt)| self.verify_one(at, block_number, source, xt, check, shard)), + ) + .await + .into_iter() + .collect::>(); + + Ok(res) + } + + /// Returns future that validates single operation at given block. + async fn verify_one( + &self, + _block_id: &BlockId, + //block_number: NumberFor, + block_number: i8, + source: TrustedOperationSource, + xt: TOP, + check: CheckBannedBeforeVerify, + shard: ShardIdentifier, + ) -> (TxHash, ValidatedOperationFor) { + let (hash, bytes) = self.validated_pool.api().hash_and_length(&xt); + + let ignore_banned = matches!(check, CheckBannedBeforeVerify::No); + if let Err(err) = self.validated_pool.check_is_known(&hash, ignore_banned, shard) { + return (hash, ValidatedOperation::Invalid(hash, err)) + } + + //FIXME: + // no runtime validation check for now. + let validation_result = + self.validated_pool.api().validate_transaction(source, xt.clone(), shard).await; + + let status = match validation_result { + Ok(status) => status, + Err(e) => return (hash, ValidatedOperation::Invalid(hash, e)), + }; + + let validity = match status { + Ok(validity) => + if validity.provides.is_empty() { + ValidatedOperation::Invalid(hash, error::Error::NoTagsProvided.into()) + } else { + ValidatedOperation::valid_at( + block_number.saturated_into::(), + hash, + source, + xt, + bytes, + validity, + ) + }, + Err(TransactionValidityError::Invalid(_e)) => + ValidatedOperation::Invalid(hash, error::Error::InvalidTrustedOperation.into()), + Err(TransactionValidityError::Unknown(_e)) => + ValidatedOperation::Unknown(hash, error::Error::UnknownTrustedOperation.into()), + }; + + (hash, validity) + } + + /// get a reference to the underlying validated pool. + pub fn validated_pool(&self) -> &ValidatedPool { + &self.validated_pool + } +} + +impl Clone for Pool +where + ::Error: error::IntoPoolError, + R: SendRpcResponse, +{ + fn clone(&self) -> Self { + Self { validated_pool: self.validated_pool.clone(), _phantom: Default::default() } + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + use crate::{ + base_pool::Limit, mocks::rpc_responder_mock::RpcResponderMock, + primitives::from_low_u64_to_be_h256, + }; + use codec::{Decode, Encode}; + use itp_stf_primitives::types::Nonce; + use itp_test::mock::stf_mock::{ + mock_top_direct_trusted_call_signed, mock_trusted_call_signed, TrustedOperationMock, + }; + use itp_types::Header; + use jsonrpc_core::{ + futures, + futures::{executor::block_on, future::ready}, + }; + use parity_util_mem::MallocSizeOf; + use serde::Serialize; + use sp_application_crypto::ed25519; + use sp_core::hash::H256; + use sp_runtime::traits::{BlakeTwo256, Extrinsic as ExtrinsicT, Hash, Verify}; + use std::{collections::HashSet, sync::Mutex}; + + #[derive(Clone, PartialEq, Eq, Encode, Decode, core::fmt::Debug, Serialize, MallocSizeOf)] + pub enum Extrinsic { + #[codec(index = 0)] + IncludeData(Vec), + #[codec(index = 1)] + StorageChange(Vec, Option>), + #[codec(index = 2)] + OffchainIndexSet(Vec, Vec), + #[codec(index = 3)] + OffchainIndexClear(Vec), + } + + impl ExtrinsicT for Extrinsic { + type Call = Extrinsic; + type SignaturePayload = (); + + fn is_signed(&self) -> Option { + if let Extrinsic::IncludeData(_) = *self { + Some(false) + } else { + Some(true) + } + } + + fn new( + call: Self::Call, + _signature_payload: Option, + ) -> Option { + Some(call) + } + } + + /// The signature type used by accounts/transactions. + pub type AccountSignature = ed25519::Signature; + /// An identifier for an account on this system. + pub type AccountId = ::Signer; + /// The hashing algorithm used. + pub type Hashing = BlakeTwo256; + /// The item of a block digest. + pub type DigestItem = sp_runtime::generic::DigestItem; + /// The digest of a block. + pub type Digest = sp_runtime::generic::Digest; + /// A test block. + pub type Block = sp_runtime::generic::Block; + /// Test RPC responder + pub type TestRpcResponder = RpcResponderMock; + + const INVALID_NONCE: Nonce = 254; + const SOURCE: TrustedOperationSource = TrustedOperationSource::External; + + #[derive(Clone, Debug, Default)] + struct TestApi { + delay: Arc>>>, + invalidate: Arc>>, + clear_requirements: Arc>>, + add_requirements: Arc>>, + } + + impl ChainApi for TestApi { + type Block = tests::Block; + type Error = error::Error; + type ValidationFuture = futures::future::Ready>; + type BodyFuture = futures::future::Ready>>; + + /// Verify extrinsic at given block. + fn validate_transaction( + &self, + _source: TrustedOperationSource, + uxt: TOP, + _shard: ShardIdentifier, + ) -> Self::ValidationFuture { + let operation = uxt.validate(); + ready(Ok(operation)) + } + + /// Returns a block number given the block id. + fn block_id_to_number( + &self, + at: &BlockId, + ) -> Result>, Self::Error> { + Ok(match at { + BlockId::Number(num) => Some(*num), + BlockId::Hash(_) => None, + }) + } + + /// Returns a block hash given the block id. + fn block_id_to_hash( + &self, + at: &BlockId, + ) -> Result, Self::Error> { + Ok(match at { + BlockId::Number(num) => Some(from_low_u64_to_be_h256((*num).into())), + BlockId::Hash(_) => None, + }) + } + + /// Hash the extrinsic. + fn hash_and_length(&self, uxt: &TOP) -> (SidechainBlockHash, usize) { + let encoded = uxt.encode(); + let len = encoded.len(); + (tests::Hashing::hash_of(&encoded), len) + } + + fn block_body(&self, _id: &BlockId) -> Self::BodyFuture { + futures::future::ready(Ok(None)) + } + } + + fn test_pool() -> Pool, TrustedOperationMock> { + Pool::new( + Default::default(), + TestApi::default().into(), + Arc::new(RpcResponderMock::::new()), + ) + } + + #[test] + pub fn test_should_validate_and_import_transaction() { + // given + let pool = test_pool(); + let shard = ShardIdentifier::default(); + + // when + let hash = block_on(pool.submit_one( + &BlockId::Number(0), + SOURCE, + mock_top_direct_trusted_call_signed(), + shard, + )) + .unwrap(); + + // then + assert_eq!( + pool.validated_pool().ready(shard).map(|v| v.hash).collect::>(), + vec![hash] + ); + } + + #[test] + pub fn test_should_reject_if_temporarily_banned() { + // given + let pool = test_pool(); + let shard = ShardIdentifier::default(); + let top = mock_top_direct_trusted_call_signed(); + + // when + pool.validated_pool.rotator().ban(&Instant::now(), vec![pool.hash_of(&top)]); + let res = block_on(pool.submit_one(&BlockId::Number(0), SOURCE, top, shard)); + assert_eq!(pool.validated_pool().status(shard).ready, 0); + assert_eq!(pool.validated_pool().status(shard).future, 0); + + // then + assert!(matches!(res.unwrap_err(), error::Error::TemporarilyBanned)); + } + + #[test] + pub fn test_should_notify_about_pool_events() { + let (stream, hash0, hash1) = { + // given + let pool = test_pool(); + let shard = ShardIdentifier::default(); + let stream = pool.validated_pool().import_notification_stream(); + + // when + let hash0 = block_on(pool.submit_one( + &BlockId::Number(0), + SOURCE, + TrustedOperationMock::direct_call(mock_trusted_call_signed(0)), + shard, + )) + .unwrap(); + let hash1 = block_on(pool.submit_one( + &BlockId::Number(0), + SOURCE, + TrustedOperationMock::direct_call(mock_trusted_call_signed(1)), + shard, + )) + .unwrap(); + /* this fails because of #1488 + // future doesn't count + let _hash = block_on(pool.submit_one( + &BlockId::Number(0), + SOURCE, + TrustedOperationMock::direct_call(mock_trusted_call_signed(3)), + shard, + )) + .unwrap(); + assert_eq!(pool.validated_pool().status(shard).future, 1); + */ + assert_eq!(pool.validated_pool().status(shard).ready, 2); + + (stream, hash0, hash1) + }; + + // then + let mut it = futures::executor::block_on_stream(stream); + assert_eq!(it.next(), Some(hash0)); + assert_eq!(it.next(), Some(hash1)); + assert_eq!(it.next(), None); + } + + #[test] + pub fn test_should_clear_stale_transactions() { + // given + let pool = test_pool(); + let shard = ShardIdentifier::default(); + let hash1 = block_on(pool.submit_one( + &BlockId::Number(0), + SOURCE, + TrustedOperationMock::direct_call(mock_trusted_call_signed(0)), + shard, + )) + .unwrap(); + let hash2 = block_on(pool.submit_one( + &BlockId::Number(0), + SOURCE, + TrustedOperationMock::direct_call(mock_trusted_call_signed(1)), + shard, + )) + .unwrap(); + let hash3 = block_on(pool.submit_one( + &BlockId::Number(0), + SOURCE, + TrustedOperationMock::direct_call(mock_trusted_call_signed(3)), + shard, + )) + .unwrap(); + // when + pool.validated_pool.clear_stale(&BlockId::Number(65), shard).unwrap(); + + // then + assert_eq!(pool.validated_pool().ready(shard).count(), 0); + assert_eq!(pool.validated_pool().status(shard).future, 0); + assert_eq!(pool.validated_pool().status(shard).ready, 0); + // make sure they are temporarily banned as well + assert!(pool.validated_pool.rotator().is_banned(&hash1)); + assert!(pool.validated_pool.rotator().is_banned(&hash2)); + assert!(pool.validated_pool.rotator().is_banned(&hash3)); + } + + #[test] + pub fn test_should_ban_mined_transactions() { + // given + let pool = test_pool(); + let shard = ShardIdentifier::default(); + let hash1 = block_on(pool.submit_one( + &BlockId::Number(0), + SOURCE, + TrustedOperationMock::direct_call(mock_trusted_call_signed(0)), + shard, + )) + .unwrap(); + + // when + block_on(pool.prune_tags(&BlockId::Number(1), vec![vec![0]], vec![hash1], shard)).unwrap(); + + // then + assert!(pool.validated_pool.rotator().is_banned(&hash1)); + } + + #[test] + #[ignore] // flaky, fails sometimes + pub fn test_should_limit_futures() { + // given + let shard = ShardIdentifier::default(); + let limit = Limit { count: 100, total_bytes: 300 }; + let pool = Pool::new( + Options { ready: limit.clone(), future: limit, ..Default::default() }, + TestApi::default().into(), + Arc::new(TestRpcResponder::new()), + ); + + let hash1 = block_on(pool.submit_one( + &BlockId::Number(0), + SOURCE, + TrustedOperationMock::direct_call(mock_trusted_call_signed(1)), + shard, + )) + .unwrap(); + assert_eq!(pool.validated_pool().status(shard).future, 1); + + // when + let hash2 = block_on(pool.submit_one( + &BlockId::Number(0), + SOURCE, + TrustedOperationMock::direct_call(mock_trusted_call_signed(10)), + shard, + )) + .unwrap(); + + // then + assert_eq!(pool.validated_pool().status(shard).future, 1); + assert!(pool.validated_pool.rotator().is_banned(&hash1)); + assert!(!pool.validated_pool.rotator().is_banned(&hash2)); + } + + #[test] + pub fn test_should_error_if_reject_immediately() { + // given + let shard = ShardIdentifier::default(); + let limit = Limit { count: 100, total_bytes: 10 }; + let pool = Pool::new( + Options { ready: limit.clone(), future: limit, ..Default::default() }, + TestApi::default().into(), + Arc::new(TestRpcResponder::new()), + ); + + // when + block_on(pool.submit_one( + &BlockId::Number(0), + SOURCE, + TrustedOperationMock::direct_call(mock_trusted_call_signed(1)), + shard, + )) + .unwrap_err(); + + // then + assert_eq!(pool.validated_pool().status(shard).ready, 0); + assert_eq!(pool.validated_pool().status(shard).future, 0); + } +} diff --git a/bitacross-worker/core-primitives/top-pool/src/primitives.rs b/bitacross-worker/core-primitives/top-pool/src/primitives.rs new file mode 100644 index 0000000000..d40fbabd93 --- /dev/null +++ b/bitacross-worker/core-primitives/top-pool/src/primitives.rs @@ -0,0 +1,350 @@ +// File replacing substrate crate sp_transaction_pool::{error, PoolStatus}; + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +extern crate alloc; +use crate::error; +use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec}; +use byteorder::{BigEndian, ByteOrder}; +use codec::{Decode, Encode}; +use core::pin::Pin; +use itp_stf_primitives::types::ShardIdentifier; +use itp_types::BlockHash as SidechainBlockHash; +use jsonrpc_core::futures::{channel::mpsc::Receiver, Future, Stream}; +use sp_core::H256; +use sp_runtime::{ + generic::BlockId, + traits::{Block as BlockT, NumberFor}, + transaction_validity::{TransactionLongevity, TransactionPriority, TransactionTag}, +}; +use std::collections::HashMap; + +/// TrustedOperation pool status. +#[derive(Debug, Default)] +pub struct PoolStatus { + /// Number of operations in the ready queue. + pub ready: usize, + /// Sum of bytes of ready operation encodings. + pub ready_bytes: usize, + /// Number of operations in the future queue. + pub future: usize, + /// Sum of bytes of ready operation encodings. + pub future_bytes: usize, +} + +impl PoolStatus { + /// Returns true if the are no operations in the pool. + pub fn is_empty(&self) -> bool { + self.ready == 0 && self.future == 0 + } +} + +/// Possible operation status events. +/// +/// This events are being emitted by `TrustedOperationPool` watchers, +/// which are also exposed over RPC. +/// +/// The status events can be grouped based on their kinds as: +/// 1. Entering/Moving within the pool: +/// - `Future` +/// - `Ready` +/// 2. Inside `Ready` queue: +/// - `Broadcast` +/// 3. Leaving the pool: +/// - `InBlock` +/// - `Invalid` +/// - `Usurped` +/// - `Dropped` +/// 4. Re-entering the pool: +/// - `Retracted` +/// 5. Block finalized: +/// - `Finalized` +/// - `FinalityTimeout` +/// +/// The events will always be received in the order described above, however +/// there might be cases where operations alternate between `Future` and `Ready` +/// pool, and are `Broadcast` in the meantime. +/// +/// There is also only single event causing the operation to leave the pool. +/// I.e. only one of the listed ones should be triggered. +/// +/// Note that there are conditions that may cause operations to reappear in the pool. +/// 1. Due to possible forks, the operation that ends up being in included +/// in one block, may later re-enter the pool or be marked as invalid. +/// 2. TrustedOperation `Dropped` at one point, may later re-enter the pool if some other +/// operations are removed. +/// 3. `Invalid` operation may become valid at some point in the future. +/// (Note that runtimes are encouraged to use `UnknownValidity` to inform the pool about +/// such case). +/// 4. `Retracted` operations might be included in some next block. +/// +/// The stream is considered finished only when either `Finalized` or `FinalityTimeout` +/// event is triggered. You are however free to unsubscribe from notifications at any point. +/// The first one will be emitted when the block, in which operation was included gets +/// finalized. The `FinalityTimeout` event will be emitted when the block did not reach finality +/// within 512 blocks. This either indicates that finality is not available for your chain, +/// or that finality gadget is lagging behind. If you choose to wait for finality longer, you can +/// re-subscribe for a particular operation hash manually again. +#[derive(Debug, Clone, PartialEq)] +pub enum TrustedOperationStatus { + /// TrustedOperation is part of the future queue. + Future, + /// TrustedOperation is part of the ready queue. + Ready, + /// The operation has been broadcast to the given peers. + Broadcast(Vec), + /// TrustedOperation has been included in block with given hash. + InBlock(BlockHash), + /// The block this operation was included in has been retracted. + Retracted(BlockHash), + /// Maximum number of finality watchers has been reached, + /// old watchers are being removed. + FinalityTimeout(BlockHash), + /// TrustedOperation has been finalized by a finality-gadget, e.g GRANDPA + Finalized(BlockHash), + /// TrustedOperation has been replaced in the pool, by another operation + /// that provides the same tags. (e.g. same (sender, nonce)). + Usurped(Hash), + /// TrustedOperation has been dropped from the pool because of the limit. + Dropped, + /// TrustedOperation is no longer valid in the current state. + Invalid, +} + +/// The stream of operation events. +pub type TrustedOperationStatusStream = + dyn Stream> + Send + Unpin; + +/// The import notification event stream. +pub type ImportNotificationStream = Receiver; + +/// TrustedOperation hash type for a pool. +pub type TxHash = H256; +/// Block hash type for a pool. +pub type BlockHash = H256; +/// Type of operations event stream for a pool. +pub type TrustedOperationStatusStreamFor = TrustedOperationStatusStream; + +/// Typical future type used in operation pool api. +pub type PoolFuture = Pin> + Send>>; + +/// In-pool operation interface. +/// +/// The pool is container of operations that are implementing this trait. +/// See `sp_runtime::ValidTransaction` for details about every field. +pub trait InPoolOperation { + /// TrustedOperation type. + type TrustedOperation; + + /// Get the reference to the operation data. + fn data(&self) -> &Self::TrustedOperation; + /// Get hash of the operation. + fn hash(&self) -> TxHash; + /// Get priority of the operation. + fn priority(&self) -> &TransactionPriority; + /// Get longevity of the operation. + fn longevity(&self) -> &TransactionLongevity; + /// Get operation dependencies. + fn requires(&self) -> &[TransactionTag]; + /// Get tags that operation provides. + fn provides(&self) -> &[TransactionTag]; + /// Return a flag indicating if the operation should be propagated to other peers. + fn is_propagable(&self) -> bool; +} + +/// TrustedOperation pool interface. +pub trait TrustedOperationPool: Send + Sync { + /// Block type. + type Block: BlockT; + /// In-pool operation type. + type InPoolOperation: InPoolOperation; + /// Error type. + type Error: From + error::IntoPoolError; + + // *** RPC + + /// Returns a future that imports a bunch of unverified operations to the pool. + // FIXME: obey clippy + #[allow(clippy::type_complexity)] + fn submit_at( + &self, + at: &BlockId, + source: TrustedOperationSource, + xts: Vec, + shard: ShardIdentifier, + ) -> PoolFuture>, Self::Error>; + + /// Returns a future that imports one unverified operation to the pool. + fn submit_one( + &self, + at: &BlockId, + source: TrustedOperationSource, + xt: TOP, + shard: ShardIdentifier, + ) -> PoolFuture; + + /// Returns a future that import a single operation and starts to watch their progress in the pool. + fn submit_and_watch( + &self, + at: &BlockId, + source: TrustedOperationSource, + xt: TOP, + shard: ShardIdentifier, + ) -> PoolFuture; + + // *** Block production / Networking + /// Get an iterator for ready operations ordered by priority. + /// + /// Guarantees to return only when operation pool got updated at `at` block. + /// Guarantees to return immediately when `None` is passed. + // FIXME: obey clippy + #[allow(clippy::type_complexity)] + fn ready_at( + &self, + at: NumberFor, + shard: ShardIdentifier, + ) -> Pin< + Box< + dyn Future> + Send>> + Send, + >, + >; + + /// Get an iterator for ready operations ordered by priority. + fn ready( + &self, + shard: ShardIdentifier, + ) -> Box> + Send>; + + /// Get an iterator over all shards. + fn shards(&self) -> Vec; + + // *** Block production + /// Remove operations identified by given hashes (and dependent operations) from the pool. + fn remove_invalid( + &self, + hashes: &[TxHash], + shard: ShardIdentifier, + inblock: bool, + ) -> Vec>; + + // *** logging + /// Returns pool status. + fn status(&self, shard: ShardIdentifier) -> PoolStatus; + + // *** logging / RPC / networking + /// Return an event stream of operations imported to the pool. + fn import_notification_stream(&self) -> ImportNotificationStream; + + // *** networking + /// Notify the pool about operations broadcast. + fn on_broadcasted(&self, propagations: HashMap>); + + /// Returns operation hash + fn hash_of(&self, xt: &TOP) -> TxHash; + + /// Return specific ready operation by hash, if there is one. + fn ready_transaction( + &self, + hash: &TxHash, + shard: ShardIdentifier, + ) -> Option>; + + /// Notify the listener of top inclusion in sidechain block + fn on_block_imported(&self, hashes: &[TxHash], block_hash: SidechainBlockHash); + + /// Litentry: set the rpc response value + #[allow(clippy::type_complexity)] + fn update_connection_state(&self, updates: Vec<(TxHash, (Vec, bool))>); + + /// Litentry: swap the old hash with the new one in rpc connection registry + fn swap_rpc_connection_hash(&self, old_hash: TxHash, new_hash: TxHash); +} + +/// The source of the transaction. +/// +/// Depending on the source we might apply different validation schemes. +/// For instance we can disallow specific kinds of transactions if they were not produced +/// by our local node (for instance off-chain workers). +#[derive(Copy, Clone, PartialEq, Eq, Encode, Decode, Debug)] +pub enum TrustedOperationSource { + /// Transaction is already included in block. + /// + /// This means that we can't really tell where the transaction is coming from, + /// since it's already in the received block. Note that the custom validation logic + /// using either `Local` or `External` should most likely just allow `InBlock` + /// transactions as well. + #[codec(index = 0)] + InBlock, + + /// Transaction is coming from a local source. + /// + /// This means that the transaction was produced internally by the node + /// (for instance an Off-Chain Worker, or an Off-Chain Call), as opposed + /// to being received over the network. + #[codec(index = 1)] + Local, + + /// Transaction has been received externally. + /// + /// This means the transaction has been received from (usually) "untrusted" source, + /// for instance received over the network or RPC. + #[codec(index = 2)] + External, +} + +// Replacement of primitive function from_low_u64_be +pub fn from_low_u64_to_be_h256(val: u64) -> H256 { + let mut buf = [0x0; 8]; + BigEndian::write_u64(&mut buf, val); + let capped = core::cmp::min(H256::len_bytes(), 8); + let mut bytes = [0x0; core::mem::size_of::()]; + bytes[(H256::len_bytes() - capped)..].copy_from_slice(&buf[..capped]); + H256::from_slice(&bytes) +} + +#[cfg(test)] +pub mod tests { + + use super::*; + use alloc::string::ToString; + + #[test] + pub fn test_h256() { + let tests = vec![ + ( + from_low_u64_to_be_h256(0), + "0x0000000000000000000000000000000000000000000000000000000000000000", + ), + ( + from_low_u64_to_be_h256(2), + "0x0000000000000000000000000000000000000000000000000000000000000002", + ), + ( + from_low_u64_to_be_h256(15), + "0x000000000000000000000000000000000000000000000000000000000000000f", + ), + ( + from_low_u64_to_be_h256(16), + "0x0000000000000000000000000000000000000000000000000000000000000010", + ), + ( + from_low_u64_to_be_h256(1_000), + "0x00000000000000000000000000000000000000000000000000000000000003e8", + ), + ( + from_low_u64_to_be_h256(100_000), + "0x00000000000000000000000000000000000000000000000000000000000186a0", + ), + ( + from_low_u64_to_be_h256(u64::max_value()), + "0x000000000000000000000000000000000000000000000000ffffffffffffffff", + ), + ]; + + for (number, expected) in tests { + // workaround, as H256 in no_std does not implement (de)serialize + assert_eq!(expected.to_string(), format!("{:?}", number)); + } + } +} diff --git a/bitacross-worker/core-primitives/top-pool/src/ready.rs b/bitacross-worker/core-primitives/top-pool/src/ready.rs new file mode 100644 index 0000000000..c3dbf5afbb --- /dev/null +++ b/bitacross-worker/core-primitives/top-pool/src/ready.rs @@ -0,0 +1,800 @@ +// This file is part of Substrate. + +// Copyright (C) 2018-2021 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +pub extern crate alloc; +use crate::{ + base_pool::TrustedOperation, + error, + future::WaitingTrustedOperations, + primitives::TxHash, + tracked_map::{self, ReadOnlyTrackedMap, TrackedMap}, +}; +use alloc::{boxed::Box, collections::BTreeSet, sync::Arc, vec, vec::Vec}; +use core::{cmp, cmp::Ord, default::Default}; +use itp_stf_primitives::types::ShardIdentifier; +use log::trace; +use sp_runtime::transaction_validity::TransactionTag as Tag; +use std::collections::{HashMap, HashSet}; + +type TopErrorResult = error::Result<(Vec>>, Vec)>; + +/// An in-pool operation reference. +/// +/// Should be cheap to clone. +#[derive(Debug)] +pub struct OperationRef { + /// The actual operation data. + pub operation: Arc>, + /// Unique id when operation was inserted into the pool. + pub insertion_id: u64, +} + +impl Clone for OperationRef { + fn clone(&self) -> Self { + OperationRef { operation: self.operation.clone(), insertion_id: self.insertion_id } + } +} + +impl Ord for OperationRef { + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.operation + .priority + .cmp(&other.operation.priority) + .then_with(|| other.operation.valid_till.cmp(&self.operation.valid_till)) + .then_with(|| other.insertion_id.cmp(&self.insertion_id)) + } +} + +impl PartialOrd for OperationRef { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl PartialEq for OperationRef { + fn eq(&self, other: &Self) -> bool { + self.cmp(other) == cmp::Ordering::Equal + } +} +impl Eq for OperationRef {} + +#[derive(Debug)] +pub struct ReadyTx { + /// A reference to a operation + pub operation: OperationRef, + /// A list of operations that get unlocked by this one + pub unlocks: Vec, + /// How many required tags are provided inherently + /// + /// Some operations might be already pruned from the queue, + /// so when we compute ready set we may consider this operations ready earlier. + pub requires_offset: usize, +} + +impl Clone for ReadyTx { + fn clone(&self) -> Self { + ReadyTx { + operation: self.operation.clone(), + unlocks: self.unlocks.clone(), + requires_offset: self.requires_offset, + } + } +} + +const HASH_READY: &str = r#" +Every time operation is imported its hash is placed in `ready` map and tags in `provided_tags`; +Every time operation is removed from the queue we remove the hash from `ready` map and from `provided_tags`; +Hence every hash retrieved from `provided_tags` is always present in `ready`; +qed +"#; + +#[derive(Debug)] +pub struct ReadyOperations { + /// Insertion id + insertion_id: HashMap, + /// tags that are provided by Ready operations + provided_tags: HashMap>, + /// Trusted Operations that are ready (i.e. don't have any requirements external to the pool) + ready: HashMap>>, + /// Best operations that are ready to be included to the block without any other previous operation. + best: HashMap>>, +} + +impl tracked_map::Size for ReadyTx { + fn size(&self) -> usize { + self.operation.operation.bytes + } +} + +impl Default for ReadyOperations { + fn default() -> Self { + ReadyOperations { + insertion_id: Default::default(), + provided_tags: Default::default(), + ready: Default::default(), + best: Default::default(), + } + } +} + +impl ReadyOperations { + /// Borrows a map of tags that are provided by operations in this queue. + pub fn provided_tags(&self, shard: ShardIdentifier) -> Option<&HashMap> { + if let Some(tag_pool) = &self.provided_tags.get(&shard) { + return Some(tag_pool) + } + None + } + + /// Returns an iterator of ready operations. + /// + /// Trusted Operations are returned in order: + /// 1. First by the dependencies: + /// - never return operation that requires a tag, which was not provided by one of the previously returned operations + /// 2. Then by priority: + /// - If there are two operations with all requirements satisfied the one with higher priority goes first. + /// 3. Then by the ttl that's left + /// - operations that are valid for a shorter time go first + /// 4. Lastly we sort by the time in the queue + /// - operations that are longer in the queue go first + pub fn get(&self, shard: ShardIdentifier) -> impl Iterator>> { + // check if shard tx pool exists + if let Some(ready_map) = self.ready.get(&shard) { + return BestIterator { + all: ready_map.get_read_only_clone(), + best: self.best.get(&shard).unwrap().clone(), + awaiting: Default::default(), + } + } + let tracked_map: TrackedMap> = Default::default(); + BestIterator { + all: tracked_map.get_read_only_clone(), + best: Default::default(), + awaiting: Default::default(), + } + } + /// Returns an iterator over all shards + pub fn get_shards(&self) -> Box + '_> { + // check if shard tx pool exists + Box::new(self.ready.keys()) + } + + /// Imports operations to the pool of ready operations. + /// + /// The operation needs to have all tags satisfied (be ready) by operations + /// that are in this queue. + /// Returns operations that were replaced by the one imported. + pub fn import( + &mut self, + tx: WaitingTrustedOperations, + shard: ShardIdentifier, + ) -> error::Result>>> { + assert!( + tx.is_ready(), + "Only ready operations can be imported. Missing: {:?}", + tx.missing_tags + ); + if let Some(ready_map) = &self.ready.get(&shard) { + assert!( + !ready_map.read().contains_key(&tx.operation.hash), + "TrustedOperation is already imported." + ); + } + // Get shard pool or create if not yet existing + let current_insertion_id = self.insertion_id.entry(shard).or_insert_with(|| { + let x: u64 = Default::default(); + x + }); + + *current_insertion_id += 1; + let insertion_id = *current_insertion_id; + let hash = tx.operation.hash; + let operation = tx.operation; + + let (replaced, unlocks) = self.replace_previous(&operation, shard)?; + + let mut goes_to_best = true; + let tracked_ready = self.ready.entry(shard).or_insert_with(|| { + let x: TrackedMap> = Default::default(); + x + }); + let mut ready = tracked_ready.write(); + let mut requires_offset = 0; + // Add links to operations that unlock the current one + let tag_map = self.provided_tags.entry(shard).or_insert_with(|| { + let x: HashMap = Default::default(); + x + }); + for tag in &operation.requires { + // Check if the operation that satisfies the tag is still in the queue. + if let Some(other) = tag_map.get(tag) { + let tx = ready.get_mut(other).expect(HASH_READY); + tx.unlocks.push(hash); + // this operation depends on some other, so it doesn't go to best directly. + goes_to_best = false; + } else { + requires_offset += 1; + } + } + + // update provided_tags + // call to replace_previous guarantees that we will be overwriting + // only entries that have been removed. + + for tag in &operation.provides { + tag_map.insert(tag.clone(), hash); + } + + let operation = OperationRef { operation, insertion_id }; + + // insert to best if it doesn't require any other operation to be included before it + let best_set = self.best.entry(shard).or_insert_with(|| { + let x: BTreeSet> = Default::default(); + x + }); + if goes_to_best { + best_set.insert(operation.clone()); + } + + // insert to Ready + ready.insert(hash, ReadyTx { operation, unlocks, requires_offset }); + + Ok(replaced) + } + + /// Fold a list of ready operations to compute a single value. + pub fn fold, &ReadyTx) -> Option>( + &mut self, + f: F, + shard: ShardIdentifier, + ) -> Option { + if let Some(ready_map) = self.ready.get(&shard) { + return ready_map.read().values().fold(None, f) + } + None + } + + /// Returns true if given hash is part of the queue. + pub fn contains(&self, hash: &TxHash, shard: ShardIdentifier) -> bool { + if let Some(ready_map) = self.ready.get(&shard) { + return ready_map.read().contains_key(hash) + } + false + } + + /// Retrive operation by hash + pub fn by_hash( + &self, + hash: &TxHash, + shard: ShardIdentifier, + ) -> Option>> { + self.by_hashes(&[*hash], shard).into_iter().next().unwrap_or(None) + } + + /// Retrieve operations by hash + pub fn by_hashes( + &self, + hashes: &[TxHash], + shard: ShardIdentifier, + ) -> Vec>>> { + if let Some(ready_map) = self.ready.get(&shard) { + let ready = ready_map.read(); + return hashes + .iter() + .map(|hash| ready.get(hash).map(|x| x.operation.operation.clone())) + .collect() + } + vec![] + } + + /// Removes a subtree of operations from the ready pool. + /// + /// NOTE removing a operation will also cause a removal of all operations that depend on that one + /// (i.e. the entire subgraph that this operation is a start of will be removed). + /// All removed operations are returned. + pub fn remove_subtree( + &mut self, + hashes: &[TxHash], + shard: ShardIdentifier, + ) -> Vec>> { + let to_remove = hashes.to_vec(); + self.remove_subtree_with_tag_filter(to_remove, None, shard) + } + + /// Removes a subtrees of operations trees starting from roots given in `to_remove`. + /// + /// We proceed with a particular branch only if there is at least one provided tag + /// that is not part of `provides_tag_filter`. I.e. the filter contains tags + /// that will stay in the pool, so that we can early exit and avoid descending. + fn remove_subtree_with_tag_filter( + &mut self, + mut to_remove: Vec, + provides_tag_filter: Option>, + shard: ShardIdentifier, + ) -> Vec>> { + let mut removed = vec![]; + if let Some(ready_map) = self.ready.get_mut(&shard) { + let mut ready = ready_map.write(); + while let Some(hash) = to_remove.pop() { + if let Some(mut tx) = ready.remove(&hash) { + let invalidated = tx.operation.operation.provides.iter().filter(|tag| { + provides_tag_filter + .as_ref() + .map(|filter| !filter.contains(&**tag)) + .unwrap_or(true) + }); + + let mut removed_some_tags = false; + // remove entries from provided_tags + for tag in invalidated { + removed_some_tags = true; + self.provided_tags.get_mut(&shard).unwrap().remove(tag); + } + + // remove from unlocks + for tag in &tx.operation.operation.requires { + if let Some(hash) = self.provided_tags.get(&shard).unwrap().get(tag) { + if let Some(tx) = ready.get_mut(hash) { + remove_item(&mut tx.unlocks, hash); + } + } + } + + // remove from best + self.best.get_mut(&shard).unwrap().remove(&tx.operation); + + if removed_some_tags { + // remove all operations that the current one unlocks + to_remove.append(&mut tx.unlocks); + } + + // add to removed + trace!(target: "txpool", "[{:?}] Removed as part of the subtree.", hash); + removed.push(tx.operation.operation); + } + } + } + + removed + } + + /// Removes operations that provide given tag. + /// + /// All operations that lead to a operation, which provides this tag + /// are going to be removed from the queue, but no other operations are touched - + /// i.e. all other subgraphs starting from given tag are still considered valid & ready. + pub fn prune_tags( + &mut self, + tag: Tag, + shard: ShardIdentifier, + ) -> Vec>> { + let mut removed = vec![]; + let mut to_remove = vec![tag]; + + if self.provided_tags.contains_key(&shard) { + while let Some(tag) = to_remove.pop() { + let res = self + .provided_tags + .get_mut(&shard) + .unwrap() + .remove(&tag) + .and_then(|hash| self.ready.get_mut(&shard).unwrap().write().remove(&hash)); + + if let Some(tx) = res { + let unlocks = tx.unlocks; + + // Make sure we remove it from best txs + self.best.get_mut(&shard).unwrap().remove(&tx.operation); + + let tx = tx.operation.operation; + + // prune previous operations as well + { + let hash = &tx.hash; + let mut find_previous = |tag| -> Option> { + let prev_hash = self.provided_tags.get(&shard).unwrap().get(tag)?; + let mut ready = self.ready.get_mut(&shard).unwrap().write(); + let tx2 = ready.get_mut(prev_hash)?; + remove_item(&mut tx2.unlocks, hash); + // We eagerly prune previous operations as well. + // But it might not always be good. + // Possible edge case: + // - tx provides two tags + // - the second tag enables some subgraph we don't know of yet + // - we will prune the operation + // - when we learn about the subgraph it will go to future + // - we will have to wait for re-propagation of that operation + // Alternatively the caller may attempt to re-import these operations. + if tx2.unlocks.is_empty() { + Some(tx2.operation.operation.provides.clone()) + } else { + None + } + }; + + // find previous operations + for tag in &tx.requires { + if let Some(mut tags_to_remove) = find_previous(tag) { + to_remove.append(&mut tags_to_remove); + } + } + } + + // add the operations that just got unlocked to `best` + for hash in unlocks { + if let Some(tx) = self.ready.get_mut(&shard).unwrap().write().get_mut(&hash) + { + tx.requires_offset += 1; + // this operation is ready + if tx.requires_offset == tx.operation.operation.requires.len() { + self.best.get_mut(&shard).unwrap().insert(tx.operation.clone()); + } + } + } + + // we also need to remove all other tags that this operation provides, + // but since all the hard work is done, we only clear the provided_tag -> hash + // mapping. + let current_tag = &tag; + for tag in &tx.provides { + let removed = self.provided_tags.get_mut(&shard).unwrap().remove(tag); + assert_eq!( + removed.as_ref(), + if current_tag == tag { None } else { Some(&tx.hash) }, + "The pool contains exactly one operation providing given tag; the removed operation + claims to provide that tag, so it has to be mapped to it's hash; qed" + ); + } + + removed.push(tx); + } + } + } + + removed + } + + /// Checks if the operation is providing the same tags as other operations. + /// + /// In case that's true it determines if the priority of operations that + /// we are about to replace is lower than the priority of the replacement operation. + /// We remove/replace old operations in case they have lower priority. + /// + /// In case replacement is successful returns a list of removed operations + /// and a list of hashes that are still in pool and gets unlocked by the new operation. + fn replace_previous( + &mut self, + tx: &TrustedOperation, + shard: ShardIdentifier, + ) -> TopErrorResult { + if let Some(provided_tag_map) = self.provided_tags.get(&shard) { + let (to_remove, unlocks) = { + // check if we are replacing a operation + let replace_hashes = tx + .provides + .iter() + .filter_map(|tag| provided_tag_map.get(tag)) + .collect::>(); + + // early exit if we are not replacing anything. + if replace_hashes.is_empty() { + return Ok((vec![], vec![])) + } + + // now check if collective priority is lower than the replacement operation. + let old_priority = { + let ready = self.ready.get(&shard).unwrap().read(); + replace_hashes + .iter() + .filter_map(|hash| ready.get(hash)) + .fold(0u64, |total, tx| { + total.saturating_add(tx.operation.operation.priority) + }) + }; + + // bail - the operation has too low priority to replace the old ones + if old_priority >= tx.priority { + return Err(error::Error::TooLowPriority(tx.priority)) + } + + // construct a list of unlocked operations + let unlocks = { + let ready = self.ready.get(&shard).unwrap().read(); + replace_hashes.iter().filter_map(|hash| ready.get(hash)).fold( + vec![], + |mut list, tx| { + list.extend(tx.unlocks.iter().cloned()); + list + }, + ) + }; + + (replace_hashes.into_iter().cloned().collect::>(), unlocks) + }; + + let new_provides = tx.provides.iter().cloned().collect::>(); + let removed = self.remove_subtree_with_tag_filter(to_remove, Some(new_provides), shard); + + return Ok((removed, unlocks)) + } + Ok((vec![], vec![])) + } + + /// Returns number of operations in this queue. + #[allow(clippy::len_without_is_empty)] + pub fn len(&self, shard: ShardIdentifier) -> usize { + self.ready.get(&shard).map_or(0, |ready_map| ready_map.len()) + } + + /// Returns sum of encoding lengths of all operations in this queue. + pub fn bytes(&self, shard: ShardIdentifier) -> usize { + self.ready.get(&shard).map_or(0, |ready_map| ready_map.bytes()) + } +} + +/// Iterator of ready operations ordered by priority. +pub struct BestIterator { + all: ReadOnlyTrackedMap>, + awaiting: HashMap)>, + best: BTreeSet>, +} + +/*impl Default for BestIterator { + let insertion_id = 0; + let operation = Arc::new(with_priority(3, 3)) + let tx_default = OperationRef { + insertion_id, + operation + }; + fn default() -> self.awaiting.insert("NA", (0, tx_default)) +}*/ + +impl BestIterator { + /// Depending on number of satisfied requirements insert given ref + /// either to awaiting set or to best set. + fn best_or_awaiting(&mut self, satisfied: usize, tx_ref: OperationRef) { + if satisfied >= tx_ref.operation.requires.len() { + // If we have satisfied all deps insert to best + self.best.insert(tx_ref); + } else { + // otherwise we're still awaiting for some deps + self.awaiting.insert(tx_ref.operation.hash, (satisfied, tx_ref)); + } + } +} + +impl Iterator for BestIterator { + type Item = Arc>; + + fn next(&mut self) -> Option { + loop { + let best = self.best.iter().next_back()?.clone(); + let best = self.best.take(&best)?; + + let next = self.all.read().get(&best.operation.hash).cloned(); + let ready = match next { + Some(ready) => ready, + // The operation is not in all, maybe it was removed in the meantime? + None => continue, + }; + + // Insert operations that just got unlocked. + for hash in &ready.unlocks { + // first check local awaiting operations + let res = if let Some((mut satisfied, tx_ref)) = self.awaiting.remove(hash) { + satisfied += 1; + Some((satisfied, tx_ref)) + // then get from the pool + } else { + self.all + .read() + .get(hash) + .map(|next| (next.requires_offset + 1, next.operation.clone())) + }; + + if let Some((satisfied, tx_ref)) = res { + self.best_or_awaiting(satisfied, tx_ref) + } + } + + return Some(best.operation) + } + } +} + +// See: https://github.com/rust-lang/rust/issues/40062 +fn remove_item(vec: &mut Vec, item: &T) { + if let Some(idx) = vec.iter().position(|i| i == item) { + vec.swap_remove(idx); + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + use crate::primitives::TrustedOperationSource as Source; + use codec::Encode; + use sp_core::blake2_256; + + fn hash(index: u64) -> TxHash { + blake2_256(index.encode().as_slice()).into() + } + + fn tx(id: u8) -> TrustedOperation> { + TrustedOperation { + data: vec![id], + bytes: 1, + hash: hash(id as u64), + priority: 1, + valid_till: 2, + requires: vec![vec![1], vec![2]], + provides: vec![vec![3], vec![4]], + propagate: true, + source: Source::External, + } + } + + fn import( + ready: &mut ReadyOperations, + tx: TrustedOperation, + shard: ShardIdentifier, + ) -> error::Result>>> { + let x = WaitingTrustedOperations::new(tx, ready.provided_tags(shard), &[]); + ready.import(x, shard) + } + + #[test] + pub fn test_should_replace_transaction_that_provides_the_same_tag() { + // given + let shard = ShardIdentifier::default(); + let mut ready = ReadyOperations::default(); + let mut tx1 = tx(1); + tx1.requires.clear(); + let mut tx2 = tx(2); + tx2.requires.clear(); + tx2.provides = vec![vec![3]]; + let mut tx3 = tx(3); + tx3.requires.clear(); + tx3.provides = vec![vec![4]]; + + // when + import(&mut ready, tx2, shard).unwrap(); + import(&mut ready, tx3, shard).unwrap(); + assert_eq!(ready.get(shard).count(), 2); + + // too low priority + import(&mut ready, tx1.clone(), shard).unwrap_err(); + + tx1.priority = 10; + import(&mut ready, tx1, shard).unwrap(); + + // then + assert_eq!(ready.get(shard).count(), 1); + } + + #[test] + pub fn test_should_replace_multiple_transactions_correctly() { + // given + let shard = ShardIdentifier::default(); + let mut ready = ReadyOperations::default(); + let mut tx0 = tx(0); + tx0.requires = vec![]; + tx0.provides = vec![vec![0]]; + let mut tx1 = tx(1); + tx1.requires = vec![]; + tx1.provides = vec![vec![1]]; + let mut tx2 = tx(2); + tx2.requires = vec![vec![0], vec![1]]; + tx2.provides = vec![vec![2], vec![3]]; + let mut tx3 = tx(3); + tx3.requires = vec![vec![2]]; + tx3.provides = vec![vec![4]]; + let mut tx4 = tx(4); + tx4.requires = vec![vec![3]]; + tx4.provides = vec![vec![5]]; + // replacement + let mut tx2_2 = tx(5); + tx2_2.requires = vec![vec![0], vec![1]]; + tx2_2.provides = vec![vec![2]]; + tx2_2.priority = 10; + + for tx in vec![tx0, tx1, tx2, tx3, tx4] { + import(&mut ready, tx, shard).unwrap(); + } + assert_eq!(ready.get(shard).count(), 5); + + // when + import(&mut ready, tx2_2, shard).unwrap(); + + // then + assert_eq!(ready.get(shard).count(), 3); + } + + #[test] + pub fn test_should_return_best_transactions_in_correct_order() { + // given + let shard = ShardIdentifier::default(); + let mut ready = ReadyOperations::default(); + let mut tx1 = tx(1); + tx1.requires.clear(); + let mut tx2 = tx(2); + tx2.requires = tx1.provides.clone(); + tx2.provides = vec![vec![106]]; + let mut tx3 = tx(3); + tx3.requires = vec![tx1.provides[0].clone(), vec![106]]; + tx3.provides = vec![]; + let mut tx4 = tx(4); + tx4.requires = vec![tx1.provides[0].clone()]; + tx4.provides = vec![]; + let tx5 = TrustedOperation { + data: vec![5], + bytes: 1, + hash: hash(5), + priority: 1, + valid_till: u64::max_value(), // use the max_value() here for testing. + requires: vec![tx1.provides[0].clone()], + provides: vec![], + propagate: true, + source: Source::External, + }; + + // when + for tx in vec![tx1, tx2, tx3, tx4, tx5] { + import(&mut ready, tx, shard).unwrap(); + } + + // then + assert_eq!(ready.best.len(), 1); + + let mut it = ready.get(shard).map(|tx| tx.data[0]); + + assert_eq!(it.next(), Some(1)); + assert_eq!(it.next(), Some(2)); + assert_eq!(it.next(), Some(3)); + assert_eq!(it.next(), Some(4)); + assert_eq!(it.next(), Some(5)); + assert_eq!(it.next(), None); + } + + #[test] + pub fn test_should_order_refs() { + let mut id = 1; + let mut with_priority = |priority, longevity| { + id += 1; + let mut tx = tx(id); + tx.priority = priority; + tx.valid_till = longevity; + tx + }; + // higher priority = better + assert!( + OperationRef { operation: Arc::new(with_priority(3, 3)), insertion_id: 1 } + > OperationRef { operation: Arc::new(with_priority(2, 3)), insertion_id: 2 } + ); + // lower validity = better + assert!( + OperationRef { operation: Arc::new(with_priority(3, 2)), insertion_id: 1 } + > OperationRef { operation: Arc::new(with_priority(3, 3)), insertion_id: 2 } + ); + // lower insertion_id = better + assert!( + OperationRef { operation: Arc::new(with_priority(3, 3)), insertion_id: 1 } + > OperationRef { operation: Arc::new(with_priority(3, 3)), insertion_id: 2 } + ); + } +} diff --git a/bitacross-worker/core-primitives/top-pool/src/rotator.rs b/bitacross-worker/core-primitives/top-pool/src/rotator.rs new file mode 100644 index 0000000000..6cfec05fa7 --- /dev/null +++ b/bitacross-worker/core-primitives/top-pool/src/rotator.rs @@ -0,0 +1,221 @@ +// This file is part of Substrate. + +// Copyright (C) 2018-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Rotate extrinsic inside the pool. +//! +//! Keeps only recent extrinsic and discard the ones kept for a significant amount of time. +//! Discarded extrinsics are banned so that they don't get re-imported again. + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use std::sync::SgxRwLock as RwLock; + +#[cfg(feature = "std")] +use std::sync::RwLock; + +use crate::{base_pool::TrustedOperation, primitives::TxHash}; +use std::{ + collections::HashMap, + iter, + time::{Duration, Instant}, +}; + +/// Expected size of the banned extrinsics cache. +const EXPECTED_SIZE: usize = 2048; + +/// Pool rotator is responsible to only keep fresh extrinsics in the pool. +/// +/// Extrinsics that occupy the pool for too long are culled and temporarily banned from entering +/// the pool again. +pub struct PoolRotator { + /// How long the extrinsic is banned for. + ban_time: Duration, + /// Currently banned extrinsics. + banned_until: RwLock>, +} + +impl Default for PoolRotator { + fn default() -> Self { + PoolRotator { ban_time: Duration::from_secs(60 * 30), banned_until: Default::default() } + } +} + +impl PoolRotator { + /// Returns `true` if extrinsic hash is currently banned. + pub fn is_banned(&self, hash: &TxHash) -> bool { + self.banned_until.read().unwrap().contains_key(hash) + } + + /// Bans given set of hashes. + pub fn ban(&self, now: &Instant, hashes: impl IntoIterator) { + let mut banned = self.banned_until.write().unwrap(); + + for hash in hashes { + banned.insert(hash, *now + self.ban_time); + } + + if banned.len() > 2 * EXPECTED_SIZE { + while banned.len() > EXPECTED_SIZE { + if let Some(key) = banned.keys().next().cloned() { + banned.remove(&key); + } + } + } + } + + /// Bans extrinsic if it's stale. + /// + /// Returns `true` if extrinsic is stale and got banned. + pub fn ban_if_stale( + &self, + now: &Instant, + current_block: u64, + xt: &TrustedOperation, + ) -> bool { + if xt.valid_till > current_block { + return false + } + + self.ban(now, iter::once(xt.hash)); + true + } + + /// Removes timed bans. + pub fn clear_timeouts(&self, now: &Instant) { + let mut banned = self.banned_until.write().unwrap(); + + banned.retain(|_, &mut v| v >= *now); + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + use crate::primitives::TrustedOperationSource; + use codec::Encode; + use sp_core::blake2_256; + + type Ex = (); + + fn rotator() -> PoolRotator { + PoolRotator { ban_time: Duration::from_millis(1000), ..Default::default() } + } + + fn hash(index: u64) -> TxHash { + blake2_256(index.encode().as_slice()).into() + } + + fn tx() -> (TxHash, TrustedOperation) { + let hash = hash(5); + let tx = TrustedOperation { + data: (), + bytes: 1, + hash, + priority: 5, + valid_till: 1, + requires: vec![], + provides: vec![], + propagate: true, + source: TrustedOperationSource::External, + }; + + (hash, tx) + } + + #[test] + pub fn test_should_not_ban_if_not_stale() { + // given + let (hash, tx) = tx(); + let rotator = rotator(); + assert!(!rotator.is_banned(&hash)); + let now = Instant::now(); + let past_block = 0; + + // when + assert!(!rotator.ban_if_stale(&now, past_block, &tx)); + + // then + assert!(!rotator.is_banned(&hash)); + } + + #[test] + pub fn test_should_ban_stale_extrinsic() { + // given + let (hash, tx) = tx(); + let rotator = rotator(); + assert!(!rotator.is_banned(&hash)); + + // when + assert!(rotator.ban_if_stale(&Instant::now(), 1, &tx)); + + // then + assert!(rotator.is_banned(&hash)); + } + + #[test] + pub fn test_should_clear_banned() { + // given + let (hash, tx) = tx(); + let rotator = rotator(); + assert!(rotator.ban_if_stale(&Instant::now(), 1, &tx)); + assert!(rotator.is_banned(&hash)); + + // when + let future = Instant::now() + rotator.ban_time + rotator.ban_time; + rotator.clear_timeouts(&future); + + // then + assert!(!rotator.is_banned(&hash)); + } + + #[test] + pub fn test_should_garbage_collect() { + // given + fn tx_with(i: u64, valid_till: u64) -> TrustedOperation { + let hash = hash(i); + TrustedOperation { + data: (), + bytes: 2, + hash, + priority: 5, + valid_till, + requires: vec![], + provides: vec![], + propagate: true, + source: TrustedOperationSource::External, + } + } + + let rotator = rotator(); + + let now = Instant::now(); + let past_block = 0; + + // when + for i in 0..2 * EXPECTED_SIZE { + let tx = tx_with(i as u64, past_block); + assert!(rotator.ban_if_stale(&now, past_block, &tx)); + } + assert_eq!(rotator.banned_until.read().unwrap().len(), 2 * EXPECTED_SIZE); + + // then + let tx = tx_with(2 * EXPECTED_SIZE as u64, past_block); + // trigger a garbage collection + assert!(rotator.ban_if_stale(&now, past_block, &tx)); + assert_eq!(rotator.banned_until.read().unwrap().len(), EXPECTED_SIZE); + } +} diff --git a/bitacross-worker/core-primitives/top-pool/src/tracked_map.rs b/bitacross-worker/core-primitives/top-pool/src/tracked_map.rs new file mode 100644 index 0000000000..dacbe841dd --- /dev/null +++ b/bitacross-worker/core-primitives/top-pool/src/tracked_map.rs @@ -0,0 +1,198 @@ +// This file is part of Substrate. + +// Copyright (C) 2018-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +pub extern crate alloc; +use alloc::sync::Arc; +use core::{ + clone::Clone, + cmp, hash, + sync::atomic::{AtomicIsize, Ordering as AtomicOrdering}, +}; +use std::collections::{hash_map::Values, HashMap}; + +//use parking_lot::{RwLock, RwLockWriteGuard, RwLockReadGuard}; + +/// Something that can report it's size. +pub trait Size { + fn size(&self) -> usize; +} + +/// Map with size tracking. +/// +/// Size reported might be slightly off and only approximately true. +#[derive(Debug)] +pub struct TrackedMap { + index: Arc>, + bytes: AtomicIsize, + length: AtomicIsize, +} + +impl Default for TrackedMap { + fn default() -> Self { + Self { index: Arc::new(HashMap::new()), bytes: 0.into(), length: 0.into() } + } +} + +impl TrackedMap { + /// Current tracked length of the content. + pub fn len(&self) -> usize { + cmp::max(self.length.load(AtomicOrdering::Relaxed), 0) as usize + } + + /// Returns true if Map is empty + pub fn is_empty(&self) -> bool { + self.length.load(AtomicOrdering::Relaxed) == 0 + } + + /// Current sum of content length. + pub fn bytes(&self) -> usize { + cmp::max(self.bytes.load(AtomicOrdering::Relaxed), 0) as usize + } + + /// Read-only clone of the interior. + pub fn get_read_only_clone(&self) -> ReadOnlyTrackedMap { + ReadOnlyTrackedMap(self.index.clone()) + } + + /// Read Access - no data race safety + pub fn read(&self) -> TrackedMapReadAccess { + TrackedMapReadAccess { inner_guard: self.index.clone() } + } + + /// Write Access - no data race safety + pub fn write(&mut self) -> TrackedMapWriteAccess { + TrackedMapWriteAccess { + //inner_guard: self.index.make_mut(&self), + inner_guard: Arc::make_mut(&mut self.index), + bytes: &self.bytes, + length: &self.length, + } + } +} + +/// Read-only access to map. +/// +/// The only thing can be done is .read(). +pub struct ReadOnlyTrackedMap(Arc>); + +impl ReadOnlyTrackedMap +where + K: Eq + hash::Hash, +{ + /// Lock map for read. + pub fn read(&self) -> TrackedMapReadAccess { + TrackedMapReadAccess { inner_guard: self.0.clone() } + } +} + +pub struct TrackedMapReadAccess { + inner_guard: Arc>, +} + +impl TrackedMapReadAccess +where + K: Eq + hash::Hash, +{ + /// Returns true if map contains key. + pub fn contains_key(&self, key: &K) -> bool { + self.inner_guard.contains_key(key) + } + + /// Returns reference to the contained value by key, if exists. + pub fn get(&self, key: &K) -> Option<&V> { + self.inner_guard.get(key) + } + + /// Returns iterator over all values. + pub fn values(&self) -> Values { + self.inner_guard.values() + } +} + +pub struct TrackedMapWriteAccess<'a, K, V> { + bytes: &'a AtomicIsize, + length: &'a AtomicIsize, + inner_guard: &'a mut HashMap, +} + +impl<'a, K, V> TrackedMapWriteAccess<'a, K, V> +where + K: Eq + hash::Hash, + V: Size, +{ + /// Insert value and return previous (if any). + pub fn insert(&mut self, key: K, val: V) -> Option { + let new_bytes = val.size(); + self.bytes.fetch_add(new_bytes as isize, AtomicOrdering::Relaxed); + self.length.fetch_add(1, AtomicOrdering::Relaxed); + self.inner_guard.insert(key, val).map(|old_val| { + self.bytes.fetch_sub(old_val.size() as isize, AtomicOrdering::Relaxed); + self.length.fetch_sub(1, AtomicOrdering::Relaxed); + old_val + }) + } + + /// Remove value by key. + pub fn remove(&mut self, key: &K) -> Option { + let val = self.inner_guard.remove(key); + if let Some(size) = val.as_ref().map(Size::size) { + self.bytes.fetch_sub(size as isize, AtomicOrdering::Relaxed); + self.length.fetch_sub(1, AtomicOrdering::Relaxed); + } + val + } + + /// Returns mutable reference to the contained value by key, if exists. + pub fn get_mut(&mut self, key: &K) -> Option<&mut V> { + self.inner_guard.get_mut(key) + } +} + +#[cfg(test)] +pub mod tests { + + use super::*; + + impl Size for i32 { + fn size(&self) -> usize { + *self as usize / 10 + } + } + + #[test] + pub fn test_basic() { + let mut map = TrackedMap::default(); + + assert!(map.is_empty()); + + map.write().insert(5, 10); + map.write().insert(6, 20); + + assert_eq!(map.bytes(), 3); + assert_eq!(map.len(), 2); + + map.write().insert(6, 30); + + assert_eq!(map.bytes(), 4); + assert_eq!(map.len(), 2); + + map.write().remove(&6); + assert_eq!(map.bytes(), 1); + assert_eq!(map.len(), 1); + } +} diff --git a/bitacross-worker/core-primitives/top-pool/src/validated_pool.rs b/bitacross-worker/core-primitives/top-pool/src/validated_pool.rs new file mode 100644 index 0000000000..0d66fca8b8 --- /dev/null +++ b/bitacross-worker/core-primitives/top-pool/src/validated_pool.rs @@ -0,0 +1,738 @@ +// This file is part of Substrate. + +// Copyright (C) 2018-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use std::sync::SgxMutex as Mutex; +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use std::sync::SgxRwLock as RwLock; + +#[cfg(feature = "std")] +use std::sync::Mutex; +#[cfg(feature = "std")] +use std::sync::RwLock; + +use crate::{ + base_pool as base, + base_pool::PruneStatus, + error, + listener::Listener, + pool::{ChainApi, EventStream, Options, TransactionFor}, + primitives::{PoolStatus, TrustedOperationSource, TxHash}, + rotator::PoolRotator, +}; +use core::{marker::PhantomData, result::Result}; +use itc_direct_rpc_server::SendRpcResponse; +use itp_stf_primitives::types::ShardIdentifier; +use itp_types::BlockHash as SidechainBlockHash; +use jsonrpc_core::futures::channel::mpsc::{channel, Sender}; +use sp_runtime::{ + generic::BlockId, + traits::SaturatedConversion, + transaction_validity::{TransactionTag as Tag, ValidTransaction}, +}; +use std::{ + collections::{HashMap, HashSet}, + format, + string::String, + sync::Arc, + time::Instant, + vec, + vec::Vec, +}; + +/// Pre-validated operation. Validated pool only accepts operations wrapped in this enum. +#[derive(Debug)] +pub enum ValidatedOperation { + /// TrustedOperation that has been validated successfully. + Valid(base::TrustedOperation), + /// TrustedOperation that is invalid. + Invalid(TxHash, Error), + /// TrustedOperation which validity can't be determined. + /// + /// We're notifying watchers about failure, if 'unknown' operation is submitted. + Unknown(TxHash, Error), +} + +impl ValidatedOperation { + /// Consume validity result, operation data and produce ValidTransaction. + pub fn valid_at( + at: u64, + hash: TxHash, + source: TrustedOperationSource, + data: Ex, + bytes: usize, + validity: ValidTransaction, + ) -> Self { + Self::Valid(base::TrustedOperation { + data, + bytes, + hash, + source, + priority: validity.priority, + requires: validity.requires, + provides: validity.provides, + propagate: validity.propagate, + valid_till: at.saturated_into::().saturating_add(validity.longevity), + }) + } +} + +/// A type of validated operation stored in the pool. +pub type ValidatedOperationFor = ValidatedOperation::Error>; + +/// Pool that deals with validated operations. +pub struct ValidatedPool +where + R: SendRpcResponse, +{ + api: Arc, + options: Options, + listener: RwLock>, + pool: RwLock>, + import_notification_sinks: Mutex>>, + rotator: PoolRotator, + _phantom: PhantomData, +} + +impl ValidatedPool +where + R: SendRpcResponse, + TOP: core::fmt::Debug + Send + Sync + Clone, +{ + /// Create a new operation pool. + pub fn new(options: Options, api: Arc, rpc_response_sender: Arc) -> Self { + let base_pool = base::BasePool::new(options.reject_future_operations); + ValidatedPool { + options, + listener: RwLock::new(Listener::new(rpc_response_sender)), + api, + pool: RwLock::new(base_pool), + import_notification_sinks: Default::default(), + rotator: Default::default(), + _phantom: Default::default(), + } + } + + /// Bans given set of hashes. + pub fn ban(&self, now: &Instant, hashes: impl IntoIterator) { + self.rotator.ban(now, hashes) + } + + /// Returns true if operation with given hash is currently banned from the pool. + pub fn is_banned(&self, hash: &TxHash) -> bool { + self.rotator.is_banned(hash) + } + + /// A fast check before doing any further processing of a operation, like validation. + /// + /// If `ingore_banned` is `true`, it will not check if the operation is banned. + /// + /// It checks if the operation is already imported or banned. If so, it returns an error. + pub fn check_is_known( + &self, + tx_hash: &TxHash, + ignore_banned: bool, + shard: ShardIdentifier, + ) -> Result<(), B::Error> { + if !ignore_banned && self.is_banned(tx_hash) { + Err(error::Error::TemporarilyBanned.into()) + } else if self.pool.read().unwrap().is_imported(tx_hash, shard) { + Err(error::Error::AlreadyImported.into()) + } else { + Ok(()) + } + } + + /// Imports a bunch of pre-validated operations to the pool. + pub fn submit( + &self, + txs: impl IntoIterator>, + shard: ShardIdentifier, + ) -> Vec> { + let results = txs + .into_iter() + .map(|validated_tx| self.submit_one(validated_tx, shard)) + .collect::>(); + + // only enforce limits if there is at least one imported operation + let removed = if results.iter().any(|res| res.is_ok()) { + self.enforce_limits(shard) + } else { + Default::default() + }; + + results + .into_iter() + .map(|res| match res { + Ok(ref hash) if removed.contains(hash) => + Err(error::Error::ImmediatelyDropped.into()), + other => other, + }) + .collect() + } + + /// Submit single pre-validated operation to the pool. + fn submit_one( + &self, + tx: ValidatedOperationFor, + shard: ShardIdentifier, + ) -> Result { + match tx { + ValidatedOperation::Valid(tx) => { + let imported = + self.pool.write().map_err(|_| error::Error::UnlockError)?.import(tx, shard)?; + + if let base::Imported::Ready { ref hash, .. } = imported { + self.import_notification_sinks + .lock() + .map_err(|_| error::Error::UnlockError)? + .retain_mut(|sink| match sink.try_send(*hash) { + Ok(()) => true, + Err(e) => + if e.is_full() { + log::warn!(target: "txpool", "[{:?}] Trying to notify an import but the channel is full", hash); + true + } else { + false + }, + }); + } + + let mut listener = self.listener.write().map_err(|_| error::Error::UnlockError)?; + fire_events(&mut listener, &imported); + Ok(*imported.hash()) + }, + ValidatedOperation::Invalid(hash, err) => { + self.rotator.ban(&Instant::now(), core::iter::once(hash)); + Err(err) + }, + ValidatedOperation::Unknown(hash, err) => { + self.listener.write().unwrap().invalid(&hash); + Err(err) + }, + } + } + + fn enforce_limits(&self, shard: ShardIdentifier) -> HashSet { + let status = self.pool.read().unwrap().status(shard); + let ready_limit = &self.options.ready; + let future_limit = &self.options.future; + + log::debug!(target: "txpool", "Pool Status: {:?}", status); + if ready_limit.is_exceeded(status.ready, status.ready_bytes) + || future_limit.is_exceeded(status.future, status.future_bytes) + { + log::debug!( + target: "txpool", + "Enforcing limits ({}/{}kB ready, {}/{}kB future", + ready_limit.count, ready_limit.total_bytes / 1024, + future_limit.count, future_limit.total_bytes / 1024, + ); + + // clean up the pool + let removed = { + let mut pool = self.pool.write().unwrap(); + let removed = pool + .enforce_limits(ready_limit, future_limit, shard) + .into_iter() + .map(|x| x.hash) + .collect::>(); + // ban all removed operations + self.rotator.ban(&Instant::now(), removed.iter().copied()); + removed + }; + if !removed.is_empty() { + log::debug!(target: "txpool", "Enforcing limits: {} dropped", removed.len()); + } + + // run notifications + let mut listener = self.listener.write().unwrap(); + for h in &removed { + listener.dropped(h, None); + } + + removed + } else { + Default::default() + } + } + + /// Import a single extrinsic and starts to watch their progress in the pool. + pub fn submit_and_watch( + &self, + tx: ValidatedOperationFor, + shard: ShardIdentifier, + ) -> Result { + match tx { + ValidatedOperation::Valid(tx) => { + let hash_result = self + .submit(core::iter::once(ValidatedOperation::Valid(tx)), shard) + .pop() + .expect("One extrinsic passed; one result returned; qed"); + // TODO: How to return / notice if Future or Ready queue? + if let Ok(hash) = hash_result { + self.listener.write().unwrap().create_watcher(hash); + } + hash_result + }, + ValidatedOperation::Invalid(hash, err) => { + self.rotator.ban(&Instant::now(), core::iter::once(hash)); + Err(err) + }, + ValidatedOperation::Unknown(_, err) => Err(err), + } + } + + /// Resubmits revalidated operations back to the pool. + /// + /// Removes and then submits passed operations and all dependent operations. + /// Transactions that are missing from the pool are not submitted. + pub fn resubmit( + &self, + mut updated_transactions: HashMap>, + shard: ShardIdentifier, + ) { + #[derive(Debug, Clone, Copy, PartialEq)] + enum Status { + Future, + Ready, + Failed, + Dropped, + } + + let (mut initial_statuses, final_statuses) = { + let mut pool = self.pool.write().unwrap(); + + // remove all passed operations from the ready/future queues + // (this may remove additional operations as well) + // + // for every operation that has an entry in the `updated_transactions`, + // we store updated validation result in txs_to_resubmit + // for every operation that has no entry in the `updated_transactions`, + // we store last validation result (i.e. the pool entry) in txs_to_resubmit + let mut initial_statuses = HashMap::new(); + let mut txs_to_resubmit = Vec::with_capacity(updated_transactions.len()); + while !updated_transactions.is_empty() { + let hash = updated_transactions + .keys() + .next() + .cloned() + .expect("operations is not empty; qed"); + + // note we are not considering tx with hash invalid here - we just want + // to remove it along with dependent operations and `remove_subtree()` + // does exactly what we need + let removed = pool.remove_subtree(&[hash], shard); + for removed_tx in removed { + let removed_hash = removed_tx.hash; + let updated_transaction = updated_transactions.remove(&removed_hash); + let tx_to_resubmit = if let Some(updated_tx) = updated_transaction { + updated_tx + } else { + // in most cases we'll end up in successful `try_unwrap`, but if not + // we still need to reinsert operation back to the pool => duplicate call + let operation = match Arc::try_unwrap(removed_tx) { + Ok(operation) => operation, + Err(operation) => operation.duplicate(), + }; + ValidatedOperation::Valid(operation) + }; + + initial_statuses.insert(removed_hash, Status::Ready); + txs_to_resubmit.push((removed_hash, tx_to_resubmit)); + } + // make sure to remove the hash even if it's not present in the pool any more. + updated_transactions.remove(&hash); + } + + // if we're rejecting future operations, then insertion order matters here: + // if tx1 depends on tx2, then if tx1 is inserted before tx2, then it goes + // to the future queue and gets rejected immediately + // => let's temporary stop rejection and clear future queue before return + pool.with_futures_enabled(|pool, reject_future_operations| { + // now resubmit all removed operations back to the pool + let mut final_statuses = HashMap::new(); + for (hash, tx_to_resubmit) in txs_to_resubmit { + match tx_to_resubmit { + ValidatedOperation::Valid(tx) => match pool.import(tx, shard) { + Ok(imported) => match imported { + base::Imported::Ready { promoted, failed, removed, .. } => { + final_statuses.insert(hash, Status::Ready); + for hash in promoted { + final_statuses.insert(hash, Status::Ready); + } + for hash in failed { + final_statuses.insert(hash, Status::Failed); + } + for tx in removed { + final_statuses.insert(tx.hash, Status::Dropped); + } + }, + base::Imported::Future { .. } => { + final_statuses.insert(hash, Status::Future); + }, + }, + Err(err) => { + // we do not want to fail if single operation import has failed + // nor we do want to propagate this error, because it could tx unknown to caller + // => let's just notify listeners (and issue debug message) + log::warn!( + target: "txpool", + "[{:?}] Removing invalid operation from update: {:?}", + hash, + err, + ); + final_statuses.insert(hash, Status::Failed); + }, + }, + ValidatedOperation::Invalid(_, _) | ValidatedOperation::Unknown(_, _) => { + final_statuses.insert(hash, Status::Failed); + }, + } + } + + // if the pool is configured to reject future operations, let's clear the future + // queue, updating final statuses as required + if reject_future_operations { + for future_tx in pool.clear_future(shard) { + final_statuses.insert(future_tx.hash, Status::Dropped); + } + } + + (initial_statuses, final_statuses) + }) + }; + + // and now let's notify listeners about status changes + let mut listener = self.listener.write().unwrap(); + for (hash, final_status) in final_statuses { + let initial_status = initial_statuses.remove(&hash); + if initial_status.is_none() || Some(final_status) != initial_status { + match final_status { + Status::Future => listener.future(&hash), + Status::Ready => listener.ready(&hash, None), + Status::Dropped => listener.dropped(&hash, None), + Status::Failed => listener.invalid(&hash), + } + } + } + } + + /// For each extrinsic, returns tags that it provides (if known), or None (if it is unknown). + pub fn extrinsics_tags( + &self, + hashes: &[TxHash], + shard: ShardIdentifier, + ) -> Vec>> { + self.pool + .read() + .unwrap() + .by_hashes(hashes, shard) + .into_iter() + .map(|existing_in_pool| existing_in_pool.map(|operation| operation.provides.to_vec())) + .collect() + } + + /// Get ready operation by hash + pub fn ready_by_hash( + &self, + hash: &TxHash, + shard: ShardIdentifier, + ) -> Option> { + self.pool.read().unwrap().ready_by_hash(hash, shard) + } + + /// Prunes ready operations that provide given list of tags. + pub fn prune_tags( + &self, + tags: impl IntoIterator, + shard: ShardIdentifier, + ) -> Result, B::Error> { + // Perform tag-based pruning in the base pool + let status = self.pool.write().unwrap().prune_tags(tags, shard); + // Notify event listeners of all operations + // that were promoted to `Ready` or were dropped. + { + let mut listener = self.listener.write().unwrap(); + for promoted in &status.promoted { + fire_events(&mut *listener, promoted); + } + for f in &status.failed { + listener.dropped(f, None); + } + } + + Ok(status) + } + + /// Resubmit operations that have been revalidated after prune_tags call. + pub fn resubmit_pruned( + &self, + at: &BlockId, + known_imported_hashes: impl IntoIterator + Clone, + pruned_hashes: Vec, + pruned_xts: Vec>, + shard: ShardIdentifier, + ) -> Result<(), B::Error> + where + ::Error: error::IntoPoolError, + { + debug_assert_eq!(pruned_hashes.len(), pruned_xts.len()); + + // Resubmit pruned operations + let results = self.submit(pruned_xts, shard); + + // Collect the hashes of operations that now became invalid (meaning that they are successfully pruned). + let hashes = results.into_iter().enumerate().filter_map(|(idx, r)| { + match r.map_err(error::IntoPoolError::into_pool_error) { + Err(Ok(error::Error::InvalidTrustedOperation)) => Some(pruned_hashes[idx]), + _ => None, + } + }); + // Fire `pruned` notifications for collected hashes and make sure to include + // `known_imported_hashes` since they were just imported as part of the block. + let hashes = hashes.chain(known_imported_hashes.into_iter()); + self.fire_pruned(at, hashes)?; + + // perform regular cleanup of old operations in the pool + // and update temporary bans. + self.clear_stale(at, shard)?; + Ok(()) + } + + /// Fire notifications for pruned operations. + pub fn fire_pruned( + &self, + at: &BlockId, + hashes: impl Iterator, + ) -> Result<(), B::Error> { + let header_hash = self + .api + .block_id_to_hash(at)? + .ok_or_else(|| error::Error::InvalidBlockId(format!("{:?}", at)))?; + let mut listener = self.listener.write().unwrap(); + let mut set = HashSet::with_capacity(hashes.size_hint().0); + for h in hashes { + // `hashes` has possibly duplicate hashes. + // we'd like to send out the `InBlock` notification only once. + if !set.contains(&h) { + listener.pruned(header_hash, &h); + set.insert(h); + } + } + Ok(()) + } + + /// Removes stale operations from the pool. + /// + /// Stale operations are operation beyond their longevity period. + /// Note this function does not remove operations that are already included in the chain. + /// See `prune_tags` if you want this. + pub fn clear_stale( + &self, + at: &BlockId, + shard: ShardIdentifier, + ) -> Result<(), B::Error> { + let block_number = self + .api + .block_id_to_number(at)? + .ok_or_else(|| error::Error::InvalidBlockId(format!("{:?}", at)))? + .saturated_into::(); + let now = Instant::now(); + let to_remove = { + self.ready(shard) + .filter(|tx| self.rotator.ban_if_stale(&now, block_number, tx)) + .map(|tx| tx.hash) + .collect::>() + }; + let futures_to_remove: Vec = { + let p = self.pool.read().unwrap(); + let mut hashes = Vec::new(); + for tx in p.futures(shard) { + if self.rotator.ban_if_stale(&now, block_number, tx) { + hashes.push(tx.hash); + } + } + hashes + }; + // removing old operations + self.remove_invalid(&to_remove, shard, false); + self.remove_invalid(&futures_to_remove, shard, false); + // clear banned operations timeouts + self.rotator.clear_timeouts(&now); + + Ok(()) + } + + /// Get rotator reference. + /// only used for test + pub fn rotator(&self) -> &PoolRotator { + &self.rotator + } + + /// Get api reference. + pub fn api(&self) -> &B { + &self.api + } + + /// Return an event stream of notifications for when operations are imported to the pool. + /// + /// Consumers of this stream should use the `ready` method to actually get the + /// pending operations in the right order. + pub fn import_notification_stream(&self) -> EventStream { + const CHANNEL_BUFFER_SIZE: usize = 1024; + + let (sink, stream) = channel(CHANNEL_BUFFER_SIZE); + self.import_notification_sinks.lock().unwrap().push(sink); + stream + } + + /// Invoked when extrinsics are broadcasted. + pub fn on_broadcasted(&self, propagated: HashMap>) { + let mut listener = self.listener.write().unwrap(); + for (hash, peers) in propagated.into_iter() { + listener.broadcasted(&hash, peers); + } + } + + /// Remove a subtree of operations from the pool and mark them invalid. + /// + /// The operations passed as an argument will be additionally banned + /// to prevent them from entering the pool right away. + /// Note this is not the case for the dependent operations - those may + /// still be valid so we want to be able to re-import them. + pub fn remove_invalid( + &self, + hashes: &[TxHash], + shard: ShardIdentifier, + inblock: bool, + ) -> Vec> { + // early exit in case there is no invalid operations. + if hashes.is_empty() { + return vec![] + } + + let invalid = self.pool.write().unwrap().remove_subtree(hashes, shard); + + log::debug!(target: "txpool", "Removed invalid operations: {:?}", invalid); + + let mut listener = self.listener.write().unwrap(); + if inblock { + for _tx in &invalid { + //listener.in_block(&tx.hash); + } + } else { + // temporarily ban invalid operations + self.rotator.ban(&Instant::now(), hashes.iter().cloned()); + for tx in &invalid { + listener.invalid(&tx.hash); + } + } + + invalid + } + + /// Get an iterator for ready operations ordered by priority + pub fn ready( + &self, + shard: ShardIdentifier, + ) -> impl Iterator> + Send { + self.pool.read().unwrap().ready(shard) + } + + /// Get an iterator for all shards + pub fn shards(&self) -> Vec { + let mut shards = vec![]; + let base_pool = self.pool.read().unwrap(); + let shard_iterator = base_pool.get_shards(); + for shard in shard_iterator { + shards.push(*shard); + } + shards + } + + /// Returns pool status. + pub fn status(&self, shard: ShardIdentifier) -> PoolStatus { + self.pool.read().unwrap().status(shard) + } + + /// Notify all watchers that operations in the block with hash have been finalized + pub async fn on_block_finalized(&self, block_hash: SidechainBlockHash) -> Result<(), B::Error> + where + <::Block as sp_runtime::traits::Block>::Hash: core::fmt::Display, + { + log::trace!(target: "txpool", "Attempting to notify watchers of finalization for {}", block_hash); + self.listener.write().unwrap().finalized(block_hash); + Ok(()) + } + + /// Notify the listener of retracted blocks + pub fn on_block_retracted(&self, block_hash: SidechainBlockHash) { + self.listener.write().unwrap().retracted(block_hash) + } + + /// Notify the listener of top inclusion in sidechain block + pub fn on_block_imported(&self, hashes: &[TxHash], block_hash: SidechainBlockHash) { + for top_hash in hashes.iter() { + self.listener.write().unwrap().in_block(top_hash, block_hash); + } + } + + #[allow(clippy::type_complexity)] + pub fn update_connection_state(&self, updates: Vec<(TxHash, (Vec, bool))>) { + for (top_hash, (encoded_value, force_wait)) in updates { + self.listener.write().unwrap().update_connection_state( + &top_hash, + encoded_value.clone(), + force_wait, + ); + self.listener + .write() + .unwrap() + .top_executed(&top_hash, &encoded_value, force_wait); + } + } + + pub fn swap_rpc_connection_hash(&self, old_hash: TxHash, new_hash: TxHash) { + self.listener.write().unwrap().swap_rpc_connection_hash(old_hash, new_hash); + } +} + +fn fire_events(listener: &mut Listener, imported: &base::Imported) +where + R: SendRpcResponse, +{ + match *imported { + base::Imported::Ready { ref promoted, ref failed, ref removed, ref hash } => { + listener.ready(hash, None); + for f in failed { + listener.invalid(f); + } + for r in removed { + listener.dropped(&r.hash, Some(hash)); + } + for p in promoted { + listener.ready(p, None); + } + }, + base::Imported::Future { ref hash } => listener.future(hash), + } +} diff --git a/bitacross-worker/core-primitives/top-pool/src/watcher.rs b/bitacross-worker/core-primitives/top-pool/src/watcher.rs new file mode 100644 index 0000000000..dd6626c6d5 --- /dev/null +++ b/bitacross-worker/core-primitives/top-pool/src/watcher.rs @@ -0,0 +1,171 @@ +// This file is part of Substrate. + +// Copyright (C) 2018-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Extrinsics status updates. + +extern crate alloc; +use crate::primitives::TxHash; +use alloc::{string::String, sync::Arc, vec::Vec}; + +use itc_direct_rpc_server::SendRpcResponse; +use itp_types::{BlockHash as SidechainBlockHash, TrustedOperationStatus}; +use log::*; + +/// Extrinsic watcher. +/// +/// Represents a stream of status updates for particular extrinsic. +#[derive(Debug)] +pub struct Watcher { + //receiver: TracingUnboundedReceiver>, + hash: TxHash, + is_in_block: bool, + rpc_response_sender: Arc, +} + +impl Watcher +where + S: SendRpcResponse, +{ + /// Returns the operation hash. + pub fn hash(&self) -> &TxHash { + &self.hash + } + + pub fn new_watcher(hash: TxHash, rpc_response_sender: Arc) -> Self { + Watcher { hash, is_in_block: false, rpc_response_sender } + } + + /// TrustedOperation became ready. + pub fn ready(&mut self) { + self.send(TrustedOperationStatus::Ready) + } + + /// TrustedOperation was moved to future. + pub fn future(&mut self) { + self.send(TrustedOperationStatus::Future) + } + + /// Some state change (perhaps another extrinsic was included) rendered this extrinsic invalid. + pub fn usurped(&mut self) { + //self.send(TrustedOperationStatus::Usurped(hash)); + self.send(TrustedOperationStatus::Usurped); + self.is_in_block = true; + } + + /// Extrinsic has been included in block with given hash. + pub fn in_block(&mut self, block_hash: SidechainBlockHash) { + self.send(TrustedOperationStatus::InSidechainBlock(block_hash)); + self.is_in_block = true; + } + + /// Extrinsic has been finalized by a finality gadget. + pub fn finalized(&mut self) { + //self.send(TrustedOperationStatus::Finalized(hash)); + self.send(TrustedOperationStatus::Finalized); + self.is_in_block = true; + } + + /// The block this extrinsic was included in has been retracted + pub fn finality_timeout(&mut self) { + //self.send(TrustedOperationStatus::FinalityTimeout(hash)); + self.send(TrustedOperationStatus::FinalityTimeout); + self.is_in_block = true; + } + + /// The block this extrinsic was included in has been retracted + pub fn retracted(&mut self) { + //self.send(TrustedOperationStatus::Retracted(hash)); + self.send(TrustedOperationStatus::Retracted); + } + + /// Extrinsic has been marked as invalid by the block builder. + pub fn invalid(&mut self) { + self.send(TrustedOperationStatus::Invalid); + // we mark as finalized as there are no more notifications + self.is_in_block = true; + } + + /// TrustedOperation has been dropped from the pool because of the limit. + pub fn dropped(&mut self) { + self.send(TrustedOperationStatus::Dropped); + self.is_in_block = true; + } + + /// The extrinsic has been broadcast to the given peers. + pub fn broadcast(&mut self, _peers: Vec) { + //self.send(TrustedOperationStatus::Broadcast(peers)) + self.send(TrustedOperationStatus::Broadcast) + } + + /// The extrinsic has been executed. + pub fn top_executed(&mut self, response: &[u8], force_wait: bool) { + self.send(TrustedOperationStatus::TopExecuted(response.to_vec(), force_wait)) + } + + /// Returns true if the are no more listeners for this extrinsic or it was finalized. + pub fn is_done(&self) -> bool { + self.is_in_block // || self.receivers.is_empty() + } + + fn send(&mut self, status: TrustedOperationStatus) { + if let Err(e) = self.rpc_response_sender.update_status_event(*self.hash(), status) { + error!("failed to send status update to rpc client: {:?}", e); + } + } + + // Litentry: set the new rpc response value and force_wait flag + pub fn update_connection_state(&mut self, encoded_value: Vec, force_wait: bool) { + if let Err(e) = self.rpc_response_sender.update_connection_state( + *self.hash(), + encoded_value, + force_wait, + ) { + warn!("failed to update connection state: {:?}", e); + } + } + + // Litentry: swap the old hash with the new one in rpc connection registry + pub fn swap_rpc_connection_hash(&self, new_hash: TxHash) { + if let Err(e) = self.rpc_response_sender.swap_hash(*self.hash(), new_hash) { + warn!("failed to swap rpc connection hash: {:?}", e); + } + } +} + +/* /// Sender part of the watcher. Exposed only for testing purposes. +#[derive(Debug)] +pub struct Sender { + //receivers: Vec>>, + //receivers: Vec, + is_in_block: bool, +} + */ +/* impl Default for Watcher { + fn default() -> Self { + Watcher { + //receivers: Default::default(), + hash: , + is_in_block: false, + } + } +} */ + +/* impl Sender { + /// Add a new watcher to this sender object. + +} */ diff --git a/bitacross-worker/core-primitives/types/Cargo.toml b/bitacross-worker/core-primitives/types/Cargo.toml new file mode 100644 index 0000000000..86013c7c08 --- /dev/null +++ b/bitacross-worker/core-primitives/types/Cargo.toml @@ -0,0 +1,50 @@ +[package] +name = "itp-types" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +homepage = 'https://litentry.com/' +repository = 'https://github.com/litentry/litentry-parachain' +license = "Apache-2.0" +edition = "2021" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } + +# local dependencies +itp-sgx-crypto = { path = "../sgx/crypto", default-features = false } +itp-sgx-runtime-primitives = { path = "../../core-primitives/sgx-runtime-primitives", default-features = false } +itp-stf-primitives = { path = "../../core-primitives/stf-primitives", default-features = false } +itp-utils = { path = "../../core-primitives/utils", default-features = false } + +# scs +substrate-api-client = { default-features = false, features = ["sync-api"], git = "https://github.com/scs/substrate-api-client.git", branch = "polkadot-v0.9.42-tag-v0.14.0" } + +# substrate-deps +frame-system = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +pallet-balances = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-core = { default-features = false, features = ["full_crypto"], git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-std = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# litentry +litentry-primitives = { path = "../../litentry/primitives", default-features = false } + +[features] +default = ["std"] +std = [ + "codec/std", + "itp-sgx-crypto/std", + "itp-sgx-runtime-primitives/std", + "itp-stf-primitives/std", + "itp-utils/std", + "substrate-api-client/std", + # substrate + "frame-system/std", + "pallet-balances/std", + "sp-std/std", + "sp-core/std", + "sp-runtime/std", + # litentry + "litentry-primitives/std", +] +test = [] diff --git a/bitacross-worker/core-primitives/types/src/lib.rs b/bitacross-worker/core-primitives/types/src/lib.rs new file mode 100644 index 0000000000..911282e427 --- /dev/null +++ b/bitacross-worker/core-primitives/types/src/lib.rs @@ -0,0 +1,218 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#![cfg_attr(all(not(target_env = "sgx"), not(feature = "std")), no_std)] +#![cfg_attr(target_env = "sgx", feature(rustc_private))] + +use crate::storage::StorageEntry; +use codec::{Decode, Encode}; +use itp_sgx_crypto::ShieldingCryptoDecrypt; +use litentry_primitives::{decl_rsa_request, RequestAesKeyNonce}; +use sp_std::{boxed::Box, fmt::Debug, vec::Vec}; + +pub mod parentchain; +pub mod storage; + +/// Substrate runtimes provide no string type. Hence, for arbitrary data of varying length the +/// `Vec` is used. In the polkadot-js the typedef `Text` is used to automatically +/// utf8 decode bytes into a string. +#[cfg(not(feature = "std"))] +pub type PalletString = Vec; + +#[cfg(feature = "std")] +pub type PalletString = String; + +pub use itp_sgx_runtime_primitives::types::*; +pub use litentry_primitives::{Assertion, DecryptableRequest}; +pub use sp_core::{crypto::AccountId32 as AccountId, H256}; + +pub type IpfsHash = [u8; 46]; +pub type MrEnclave = [u8; 32]; + +pub type CallIndex = [u8; 2]; + +// pallet teerex +pub type ConfirmCallFn = (CallIndex, ShardIdentifier, H256, Vec); +pub type ShieldFundsFn = (CallIndex, Vec, Balance, ShardIdentifier); +pub type CallWorkerFn = (CallIndex, RsaRequest); + +pub type UpdateScheduledEnclaveFn = (CallIndex, SidechainBlockNumber, MrEnclave); +pub type RemoveScheduledEnclaveFn = (CallIndex, SidechainBlockNumber); + +// pallet IMP +pub type LinkIdentityParams = (ShardIdentifier, AccountId, Vec, Vec, RequestAesKeyNonce); +pub type LinkIdentityFn = (CallIndex, LinkIdentityParams); + +pub type DeactivateIdentityParams = (ShardIdentifier, Vec); +pub type DeactivateIdentityFn = (CallIndex, DeactivateIdentityParams); + +pub type ActivateIdentityParams = (ShardIdentifier, Vec); +pub type ActivateIdentityFn = (CallIndex, DeactivateIdentityParams); + +// pallet VCMP +pub type RequestVCParams = (ShardIdentifier, Assertion); +pub type RequestVCFn = (CallIndex, RequestVCParams); + +pub type Enclave = EnclaveGen; + +/// Simple blob to hold an encoded call +#[derive(Debug, PartialEq, Eq, Clone, Default)] +pub struct OpaqueCall(pub Vec); + +impl OpaqueCall { + /// Convert call tuple to an `OpaqueCall`. + pub fn from_tuple(call: &C) -> Self { + OpaqueCall(call.encode()) + } +} + +impl Encode for OpaqueCall { + fn encode(&self) -> Vec { + self.0.clone() + } +} + +// Litentry: re-declared due to orphan rule (that's why macro is used) +decl_rsa_request!(Debug); + +impl DecryptableRequest for RsaRequest { + type Error = (); + + fn shard(&self) -> ShardIdentifier { + self.shard + } + + fn payload(&self) -> &[u8] { + self.payload.as_slice() + } + + fn decrypt( + &mut self, + enclave_shielding_key: Box>, + ) -> core::result::Result, ()> { + enclave_shielding_key.decrypt(self.payload.as_slice()).map_err(|_| ()) + } +} + +// Todo: move this improved enclave definition into a primitives crate in the pallet_teerex repo. +#[derive(Encode, Decode, Clone, PartialEq, sp_core::RuntimeDebug)] +pub struct EnclaveGen { + pub pubkey: AccountId, + // FIXME: this is redundant information + pub mr_enclave: [u8; 32], + pub timestamp: u64, + // unix epoch in milliseconds + pub url: PalletString, // utf8 encoded url +} + +impl EnclaveGen { + pub fn new(pubkey: AccountId, mr_enclave: [u8; 32], timestamp: u64, url: PalletString) -> Self { + Self { pubkey, mr_enclave, timestamp, url } + } +} + +#[derive(Debug, Clone, PartialEq, Encode, Decode, Eq)] +pub enum DirectRequestStatus { + /// Direct request was successfully executed + #[codec(index = 0)] + Ok, + /// Trusted Call Status + /// Litentry: embed the top hash here - TODO - use generic type? + #[codec(index = 1)] + TrustedOperationStatus(TrustedOperationStatus, H256), + /// Direct request could not be executed + #[codec(index = 2)] + Error, +} + +#[derive(Debug, Clone, PartialEq, Encode, Decode, Eq)] +pub enum TrustedOperationStatus { + /// TrustedOperation is submitted to the top pool. + #[codec(index = 0)] + Submitted, + /// TrustedOperation is part of the future queue. + #[codec(index = 1)] + Future, + /// TrustedOperation is part of the ready queue. + #[codec(index = 2)] + Ready, + /// The operation has been broadcast to the given peers. + #[codec(index = 3)] + Broadcast, + /// TrustedOperation has been included in block with given hash. + #[codec(index = 4)] + InSidechainBlock(BlockHash), + /// The block this operation was included in has been retracted. + #[codec(index = 5)] + Retracted, + /// Maximum number of finality watchers has been reached, + /// old watchers are being removed. + #[codec(index = 6)] + FinalityTimeout, + /// TrustedOperation has been finalized by a finality-gadget, e.g GRANDPA + #[codec(index = 7)] + Finalized, + /// TrustedOperation has been replaced in the pool, by another operation + /// that provides the same tags. (e.g. same (sender, nonce)). + #[codec(index = 8)] + Usurped, + /// TrustedOperation has been dropped from the pool because of the limit. + #[codec(index = 9)] + Dropped, + /// TrustedOperation is no longer valid in the current state. + #[codec(index = 10)] + Invalid, + /// TrustedOperation has been executed. + TopExecuted(Vec, bool), +} + +#[derive(Encode, Decode, Clone, Debug, PartialEq)] +pub enum WorkerRequest { + #[codec(index = 0)] + ChainStorage(Vec, Option), // (storage_key, at_block) + #[codec(index = 1)] + ChainStorageKeys(Vec, Option), // (storage_key_prefix, at_block) +} + +#[derive(Encode, Decode, Clone, Debug, PartialEq)] +pub enum WorkerResponse { + #[codec(index = 0)] + ChainStorage(Vec, Option, Option>>), // (storage_key, storage_value, storage_proof) + #[codec(index = 1)] + ChainStorageKeys(Vec>), // (storage_keys) +} + +impl From>> for StorageEntry> { + fn from(response: WorkerResponse>) -> Self { + match response { + WorkerResponse::ChainStorage(key, value, proof) => StorageEntry { key, value, proof }, + _ => StorageEntry::default(), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn opaque_call_encodes_correctly() { + let call_tuple = ([1u8, 2u8], 5u8); + let call = OpaqueCall::from_tuple(&call_tuple); + assert_eq!(call.encode(), call_tuple.encode()) + } +} diff --git a/bitacross-worker/core-primitives/types/src/parentchain.rs b/bitacross-worker/core-primitives/types/src/parentchain.rs new file mode 100644 index 0000000000..05085ffc06 --- /dev/null +++ b/bitacross-worker/core-primitives/types/src/parentchain.rs @@ -0,0 +1,224 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::OpaqueCall; +use alloc::{format, vec::Vec}; +use codec::{Decode, Encode}; +use core::fmt::Debug; +use itp_stf_primitives::traits::{IndirectExecutor, TrustedCallVerification}; +use itp_utils::stringify::account_id_to_string; +use sp_core::bounded::alloc; +use sp_runtime::{generic::Header as HeaderG, traits::BlakeTwo256, MultiAddress, MultiSignature}; +use substrate_api_client::ac_node_api::StaticEvent; + +pub type StorageProof = Vec>; + +// Basic Types. +pub type Index = u32; +pub type Balance = u128; +pub type Hash = sp_core::H256; + +// Account Types. +pub type AccountId = sp_core::crypto::AccountId32; +pub type AccountData = pallet_balances::AccountData; +pub type AccountInfo = frame_system::AccountInfo; +pub type Address = MultiAddress; +// todo! make generic +/// The type used to represent the kinds of proxying allowed. +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug)] +pub enum ProxyType { + Any, + NonTransfer, + Governance, + Staking, +} + +// Block Types +pub type BlockNumber = u32; +pub type Header = HeaderG; +pub type BlockHash = sp_core::H256; + +/// Alias to 512-bit hash when used in the context of a transaction signature on the chain. +pub type Signature = MultiSignature; + +#[derive(Encode, Decode, Copy, Clone, Debug, PartialEq, Eq)] +pub enum ParentchainId { + /// The Litentry Parentchain, the trust root of the enclave and serving finality to sidechains. + #[codec(index = 0)] + Litentry, + /// A target chain containing custom business logic. + #[codec(index = 1)] + TargetA, + /// Another target chain containing custom business logic. + #[codec(index = 2)] + TargetB, +} + +#[cfg(feature = "std")] +impl std::fmt::Display for ParentchainId { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + let message = match self { + ParentchainId::Litentry => "L1:Litentry", + ParentchainId::TargetA => "L1:AssetHub", + ParentchainId::TargetB => "L1:UNDEFINED", + }; + write!(f, "{}", message) + } +} + +pub trait IdentifyParentchain { + fn parentchain_id(&self) -> ParentchainId; +} + +pub trait FilterEvents { + type Error: From + core::fmt::Debug; + fn get_extrinsic_statuses(&self) -> core::result::Result, Self::Error>; + + fn get_transfer_events(&self) -> core::result::Result, Self::Error>; +} + +#[derive(Encode, Decode, Debug)] +pub struct ExtrinsicSuccess; + +impl StaticEvent for ExtrinsicSuccess { + const PALLET: &'static str = "System"; + const EVENT: &'static str = "ExtrinsicSuccess"; +} + +#[derive(Encode, Decode)] +pub struct ExtrinsicFailed; + +impl StaticEvent for ExtrinsicFailed { + const PALLET: &'static str = "System"; + const EVENT: &'static str = "ExtrinsicFailed"; +} + +#[derive(Debug)] +pub enum ExtrinsicStatus { + Success, + Failed, +} + +#[derive(Encode, Decode, Debug)] +pub struct BalanceTransfer { + pub from: AccountId, + pub to: AccountId, + pub amount: Balance, +} + +impl core::fmt::Display for BalanceTransfer { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + let message = format!( + "BalanceTransfer :: from: {}, to: {}, amount: {}", + account_id_to_string::(&self.from), + account_id_to_string::(&self.to), + self.amount + ); + write!(f, "{}", message) + } +} + +impl StaticEvent for BalanceTransfer { + const PALLET: &'static str = "Balances"; + const EVENT: &'static str = "Transfer"; +} + +pub trait HandleParentchainEvents +where + Executor: IndirectExecutor, + TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, +{ + fn handle_events( + executor: &Executor, + events: impl FilterEvents, + vault_account: &AccountId, + ) -> core::result::Result<(), Error>; +} + +#[derive(Debug)] +pub enum ParentchainError { + ShieldFundsFailure, + FunctionalityDisabled, +} + +impl core::fmt::Display for ParentchainError { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + let message = match &self { + ParentchainError::ShieldFundsFailure => "Parentchain Error: ShieldFundsFailure", + ParentchainError::FunctionalityDisabled => "Parentchain Error: FunctionalityDisabled", + }; + write!(f, "{}", message) + } +} + +impl From for () { + fn from(_: ParentchainError) -> Self {} +} + +/// a wrapper to target calls to specific parentchains +#[derive(Encode, Debug, Clone, PartialEq, Eq)] +pub enum ParentchainCall { + Litentry(OpaqueCall), + TargetA(OpaqueCall), + TargetB(OpaqueCall), +} + +impl ParentchainCall { + pub fn as_litentry(&self) -> Option { + if let Self::Litentry(call) = self { + Some(call.clone()) + } else { + None + } + } + pub fn as_target_a(&self) -> Option { + if let Self::TargetA(call) = self { + Some(call.clone()) + } else { + None + } + } + pub fn as_target_b(&self) -> Option { + if let Self::TargetB(call) = self { + Some(call.clone()) + } else { + None + } + } + pub fn as_opaque_call_for(&self, parentchain_id: ParentchainId) -> Option { + match parentchain_id { + ParentchainId::Litentry => + if let Self::Litentry(call) = self { + Some(call.clone()) + } else { + None + }, + ParentchainId::TargetA => + if let Self::TargetA(call) = self { + Some(call.clone()) + } else { + None + }, + ParentchainId::TargetB => + if let Self::TargetB(call) = self { + Some(call.clone()) + } else { + None + }, + } + } +} diff --git a/bitacross-worker/core-primitives/types/src/storage.rs b/bitacross-worker/core-primitives/types/src/storage.rs new file mode 100644 index 0000000000..ea362dff8d --- /dev/null +++ b/bitacross-worker/core-primitives/types/src/storage.rs @@ -0,0 +1,59 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use codec::{Decode, Encode}; +use sp_std::prelude::Vec; + +#[derive(Default, Clone, Encode, Decode)] +pub struct StorageEntry { + pub key: Vec, + pub value: Option, + pub proof: Option>>, +} + +/// Contains private fields. We don't expose a public constructor. Hence, the only way +/// to get a `StorageEntryVerified` is via the `VerifyStorageProof` trait. +#[derive(Default, Clone, Encode, Decode)] +pub struct StorageEntryVerified { + pub key: Vec, + pub value: Option, +} + +#[cfg(feature = "test")] +impl StorageEntryVerified { + pub fn new(key: Vec, value: Option) -> Self { + Self { key, value } + } +} + +impl StorageEntryVerified { + pub fn key(&self) -> &[u8] { + &self.key + } + + pub fn value(&self) -> &Option { + &self.value + } + + /// Without accessing the the field directly but with getters only, we cannot partially + /// own the struct. So we can't do: `hashmap.insert(self.key(), self.value())` if the getters + /// consumed the `self`, which is needed to return owned values. Hence, we supply this method, + /// to consume `self` and be able to use the values individually. + pub fn into_tuple(self) -> (Vec, Option) { + (self.key, self.value) + } +} diff --git a/bitacross-worker/core-primitives/utils/Cargo.toml b/bitacross-worker/core-primitives/utils/Cargo.toml new file mode 100644 index 0000000000..7c293aa011 --- /dev/null +++ b/bitacross-worker/core-primitives/utils/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "itp-utils" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +homepage = "https://litentry.com/" +repository = "https://github.com/litentry/litentry-parachain" +license = "Apache-2.0" +edition = "2021" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +hex = { version = "0.4.3", default-features = false, features = ["alloc"] } + +[features] +default = ["std"] +std = [ + "codec/std", + "hex/std", +] diff --git a/bitacross-worker/core-primitives/utils/src/buffer.rs b/bitacross-worker/core-primitives/utils/src/buffer.rs new file mode 100644 index 0000000000..89d02ccc0f --- /dev/null +++ b/bitacross-worker/core-primitives/utils/src/buffer.rs @@ -0,0 +1,67 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Buffer utility functions. + +use alloc::vec::Vec; + +/// Fills a given buffer with data and the left over buffer space with white spaces. +/// Throw an error if the buffer size is not enough to hold `data`, +/// return the length of `data` otherwise. +pub fn write_slice_and_whitespace_pad( + writable: &mut [u8], + data: Vec, +) -> Result { + if data.len() > writable.len() { + return Err(BufferError::InsufficientBufferSize { + actual: writable.len(), + required: data.len(), + }) + } + let (left, right) = writable.split_at_mut(data.len()); + left.clone_from_slice(&data); + // fill the right side with whitespace + right.iter_mut().for_each(|x| *x = 0x20); + Ok(data.len()) +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +pub enum BufferError { + InsufficientBufferSize { actual: usize, required: usize }, +} + +#[cfg(test)] +mod tests { + use super::*; + use alloc::vec; + + #[test] + fn write_slice_and_whitespace_pad_works() { + let mut writable = vec![0; 32]; + let data = vec![1; 30]; + assert_eq!(write_slice_and_whitespace_pad(&mut writable, data), Ok(30)); + assert_eq!(&writable[..30], vec![1; 30]); + assert_eq!(&writable[30..], vec![0x20; 2]); + } + + #[test] + fn write_slice_and_whitespace_pad_returns_error_if_buffer_too_small() { + let mut writable = vec![0; 32]; + let data = vec![1; 33]; + assert!(write_slice_and_whitespace_pad(&mut writable, data).is_err()); + } +} diff --git a/bitacross-worker/core-primitives/utils/src/error.rs b/bitacross-worker/core-primitives/utils/src/error.rs new file mode 100644 index 0000000000..5ca7508d26 --- /dev/null +++ b/bitacross-worker/core-primitives/utils/src/error.rs @@ -0,0 +1,27 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use alloc::string::String; + +pub type Result = core::result::Result; + +#[derive(Debug)] +pub enum Error { + Hex(hex::FromHexError), + Codec(codec::Error), + Other(String), +} diff --git a/bitacross-worker/core-primitives/utils/src/hex.rs b/bitacross-worker/core-primitives/utils/src/hex.rs new file mode 100644 index 0000000000..4c167af6f3 --- /dev/null +++ b/bitacross-worker/core-primitives/utils/src/hex.rs @@ -0,0 +1,117 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Hex encoding utility functions. + +// Todo: merge with hex_display + +use crate::error::{Error, Result}; +use alloc::{string::String, vec::Vec}; +use codec::{Decode, Encode}; + +/// Trait to encode a given value to a hex string, prefixed with "0x". +pub trait ToHexPrefixed { + fn to_hex(&self) -> String; +} + +impl ToHexPrefixed for T { + fn to_hex(&self) -> String { + hex_encode(&self.encode()) + } +} + +/// Trait to decode a hex string to a given output. +pub trait FromHexPrefixed { + type Output; + + fn from_hex(msg: &str) -> Result; +} + +impl FromHexPrefixed for T { + type Output = T; + + fn from_hex(msg: &str) -> Result { + let byte_array = decode_hex(msg)?; + Decode::decode(&mut byte_array.as_slice()).map_err(Error::Codec) + } +} + +/// Hex encodes given data and preappends a "0x". +pub fn hex_encode(data: &[u8]) -> String { + let mut hex_str = hex::encode(data); + hex_str.insert_str(0, "0x"); + hex_str +} + +/// Helper method for decoding hex. +pub fn decode_hex>(message: T) -> Result> { + let message = message.as_ref(); + let message = match message { + [b'0', b'x', hex_value @ ..] => hex_value, + _ => message, + }; + + let decoded_message = hex::decode(message).map_err(Error::Hex)?; + Ok(decoded_message) +} + +#[cfg(test)] +mod tests { + use super::*; + use alloc::string::ToString; + + #[test] + fn hex_encode_decode_works() { + let data = "Hello World!".to_string(); + + let hex_encoded_data = hex_encode(&data.encode()); + let decoded_data = + String::decode(&mut decode_hex(hex_encoded_data).unwrap().as_slice()).unwrap(); + + assert_eq!(data, decoded_data); + } + + #[test] + fn hex_encode_decode_works_empty_input() { + let data = String::new(); + + let hex_encoded_data = hex_encode(&data.encode()); + let decoded_data = + String::decode(&mut decode_hex(hex_encoded_data).unwrap().as_slice()).unwrap(); + + assert_eq!(data, decoded_data); + } + + #[test] + fn hex_encode_decode_works_empty_input_for_decode() { + let data = String::new(); + + let decoded_data = decode_hex(data).unwrap(); + + assert!(decoded_data.is_empty()); + } + + #[test] + fn to_hex_from_hex_works() { + let data = "Hello World!".to_string(); + + let hex_encoded_data = data.to_hex(); + let decoded_data = String::from_hex(&hex_encoded_data).unwrap(); + + assert_eq!(data, decoded_data); + } +} diff --git a/bitacross-worker/core-primitives/utils/src/hex_display.rs b/bitacross-worker/core-primitives/utils/src/hex_display.rs new file mode 100644 index 0000000000..f0525b4e2f --- /dev/null +++ b/bitacross-worker/core-primitives/utils/src/hex_display.rs @@ -0,0 +1,96 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Wrapper type for byte collections that outputs hex. +//! +//! Copied from sp-core and made purely no-std. + +/// Simple wrapper to display hex representation of bytes. +pub struct HexDisplay<'a>(&'a [u8]); + +impl<'a> HexDisplay<'a> { + /// Create new instance that will display `d` as a hex string when displayed. + pub fn from(d: &'a R) -> Self { + HexDisplay(d.as_bytes_ref()) + } +} + +impl<'a> core::fmt::Display for HexDisplay<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { + if self.0.len() < 1027 { + for byte in self.0 { + f.write_fmt(format_args!("{:02x}", byte))?; + } + } else { + for byte in &self.0[0..512] { + f.write_fmt(format_args!("{:02x}", byte))?; + } + f.write_str("...")?; + for byte in &self.0[self.0.len() - 512..] { + f.write_fmt(format_args!("{:02x}", byte))?; + } + } + Ok(()) + } +} + +impl<'a> core::fmt::Debug for HexDisplay<'a> { + fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { + for byte in self.0 { + f.write_fmt(format_args!("{:02x}", byte))?; + } + Ok(()) + } +} + +/// Simple trait to transform various types to `&[u8]` +pub trait AsBytesRef { + /// Transform `self` into `&[u8]`. + fn as_bytes_ref(&self) -> &[u8]; +} + +impl AsBytesRef for &[u8] { + fn as_bytes_ref(&self) -> &[u8] { + self + } +} + +impl AsBytesRef for [u8] { + fn as_bytes_ref(&self) -> &[u8] { + self + } +} + +impl AsBytesRef for alloc::vec::Vec { + fn as_bytes_ref(&self) -> &[u8] { + self + } +} + +macro_rules! impl_non_endians { + ( $( $t:ty ),* ) => { $( + impl AsBytesRef for $t { + fn as_bytes_ref(&self) -> &[u8] { &self[..] } + } + )* } +} + +impl_non_endians!( + [u8; 1], [u8; 2], [u8; 3], [u8; 4], [u8; 5], [u8; 6], [u8; 7], [u8; 8], [u8; 10], [u8; 12], + [u8; 14], [u8; 16], [u8; 20], [u8; 24], [u8; 28], [u8; 32], [u8; 40], [u8; 48], [u8; 56], + [u8; 64], [u8; 65], [u8; 80], [u8; 96], [u8; 112], [u8; 128] +); diff --git a/bitacross-worker/core-primitives/utils/src/lib.rs b/bitacross-worker/core-primitives/utils/src/lib.rs new file mode 100644 index 0000000000..d03767e6c6 --- /dev/null +++ b/bitacross-worker/core-primitives/utils/src/lib.rs @@ -0,0 +1,35 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! General utility functions. + +#![no_std] + +extern crate alloc; + +pub mod buffer; +pub mod error; +pub mod hex; +pub mod hex_display; +pub mod macros; +pub mod stringify; + +// Public re-exports. +pub use self::{ + buffer::write_slice_and_whitespace_pad, + hex::{FromHexPrefixed, ToHexPrefixed}, +}; diff --git a/bitacross-worker/core-primitives/utils/src/macros.rs b/bitacross-worker/core-primitives/utils/src/macros.rs new file mode 100644 index 0000000000..69783ff727 --- /dev/null +++ b/bitacross-worker/core-primitives/utils/src/macros.rs @@ -0,0 +1,35 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#[macro_export] +macro_rules! if_production_or { + ($prod_variant:expr, $non_prod_variant:expr) => { + if cfg!(feature = "production") { + $prod_variant + } else { + $non_prod_variant + } + }; +} + +#[macro_export] +macro_rules! if_not_production { + ($expression:expr) => { + if cfg!(not(feature = "production")) { + $expression + } + }; +} diff --git a/bitacross-worker/core-primitives/utils/src/stringify.rs b/bitacross-worker/core-primitives/utils/src/stringify.rs new file mode 100644 index 0000000000..e514fdbecb --- /dev/null +++ b/bitacross-worker/core-primitives/utils/src/stringify.rs @@ -0,0 +1,36 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Utility methods to stringify certain types that don't have a working +//! `Debug` implementation on `sgx`. + +use crate::hex_display::{AsBytesRef, HexDisplay}; +use alloc::{format, string::String}; +use codec::Encode; + +/// Convert a sp_core public type to string. +pub fn public_to_string(t: &T) -> String { + format!("{}", HexDisplay::from(t)) +} + +pub fn account_id_to_string(account: &AccountId) -> String { + format!("0x{}", HexDisplay::from(&account.encode())) +} + +pub fn account_id_to_string_without_prefix(account: &AccountId) -> String { + format!("{}", HexDisplay::from(&account.encode())) +} diff --git a/bitacross-worker/core/direct-rpc-client/Cargo.toml b/bitacross-worker/core/direct-rpc-client/Cargo.toml new file mode 100644 index 0000000000..69631b5e40 --- /dev/null +++ b/bitacross-worker/core/direct-rpc-client/Cargo.toml @@ -0,0 +1,53 @@ +[package] +name = "itc-direct-rpc-client" +version = "0.1.0" +authors = ['Trust Computing GmbH '] +edition = "2021" + +[dependencies] +# sgx dependencies +rustls_sgx = { package = "rustls", optional = true, git = "https://github.com/mesalock-linux/rustls", tag = "sgx_1.1.3", features = ["dangerous_configuration"] } +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } +tungstenite_sgx = { package = "tungstenite", optional = true, git = "https://github.com/integritee-network/tungstenite-rs-sgx", branch = "sgx-experimental", features = ["rustls-tls-webpki-roots"] } +url_sgx = { package = "url", optional = true, git = "https://github.com/mesalock-linux/rust-url-sgx", tag = "sgx_1.1.3" } +webpki_sgx = { package = "webpki", optional = true, git = "https://github.com/mesalock-linux/webpki", branch = "mesalock_sgx" } + +# no-std dependencies +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +log = { version = "0.4", default-features = false } +serde_json = { version = "1.0", default-features = false } + +# std dependencies +rustls = { version = "0.19", optional = true, features = ["dangerous_configuration"] } +tungstenite = { version = "0.15.0", optional = true, features = ["rustls-tls-webpki-roots"] } +url = { version = "2.0.0", optional = true } +webpki = { version = "0.21", optional = true } + +# local dependencies +itp-rpc = { path = "../../core-primitives/rpc", default-features = false } +itp-types = { path = "../../core-primitives/types", default-features = false } +itp-utils = { path = "../../core-primitives/utils", default-features = false } +litentry-primitives = { path = "../../litentry/primitives", default-features = false } + +[features] +default = ["std"] +sgx = [ + "webpki_sgx", + "url_sgx", + "tungstenite_sgx", + "rustls_sgx", + "sgx_tstd", + "itp-rpc/sgx", + "litentry-primitives/sgx", +] +std = [ + "rustls", + "webpki", + "tungstenite", + "url", + "itp-rpc/std", + "itp-types/std", + "itp-utils/std", + "log/std", + "litentry-primitives/std", +] diff --git a/bitacross-worker/core/direct-rpc-client/src/lib.rs b/bitacross-worker/core/direct-rpc-client/src/lib.rs new file mode 100644 index 0000000000..6de127f7df --- /dev/null +++ b/bitacross-worker/core/direct-rpc-client/src/lib.rs @@ -0,0 +1,262 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +// re-export module to properly feature gate sgx and regular std environment +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod sgx_reexport_prelude { + pub use rustls_sgx as rustls; + pub use tungstenite_sgx as tungstenite; + pub use url_sgx as url; + pub use webpki_sgx as webpki; +} + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +extern crate alloc; + +use alloc::format; + +use core::str::FromStr; + +use log::debug; + +use serde_json::from_str; + +use itp_rpc::{Id, RpcRequest, RpcResponse, RpcReturnValue}; + +use itp_utils::FromHexPrefixed; + +use std::{ + boxed::Box, + error::Error, + net::TcpStream, + string::String, + sync::{ + mpsc::{channel, Sender, SyncSender}, + Arc, + }, + time::Duration, + vec::Vec, +}; +use tungstenite::{client_tls_with_config, stream::MaybeTlsStream, Connector, Message, WebSocket}; +use url::Url; +use webpki::{DNSName, DNSNameRef}; + +pub type Response = (Id, RpcReturnValue); + +pub struct IgnoreCertVerifier {} + +impl rustls::ServerCertVerifier for IgnoreCertVerifier { + fn verify_server_cert( + &self, + _: &rustls::RootCertStore, + _: &[rustls::Certificate], + _: DNSNameRef<'_>, + _: &[u8], + ) -> Result { + log::warn!("Using NoCertVerifier"); + Ok(rustls::ServerCertVerified::assertion()) + } +} + +impl rustls::ClientCertVerifier for IgnoreCertVerifier { + fn client_auth_root_subjects( + &self, + _sni: Option<&DNSName>, + ) -> Option { + None + } + + fn verify_client_cert( + &self, + _presented_certs: &[rustls::Certificate], + _sni: Option<&DNSName>, + ) -> Result { + Ok(rustls::ClientCertVerified::assertion()) + } +} + +pub trait RpcClientFactory { + type Client: RpcClient; + fn create( + &self, + url: &str, + response_sink: SyncSender, + ) -> Result>; +} + +pub struct DirectRpcClientFactory {} + +impl RpcClientFactory for DirectRpcClientFactory { + type Client = DirectRpcClient; + + fn create( + &self, + url: &str, + response_sink: SyncSender, + ) -> Result> { + DirectRpcClient::new(url, response_sink) + } +} + +pub trait RpcClient { + fn send(&mut self, request: &RpcRequest) -> Result<(), Box>; +} + +pub struct DirectRpcClient { + request_sink: Sender, +} + +impl DirectRpcClient { + pub fn new(url: &str, response_sink: SyncSender) -> Result> { + let ws_server_url = + Url::from_str(url).map_err(|e| format!("Could not connect, reason: {:?}", e))?; + let mut config = rustls::ClientConfig::new(); + // we need to set this cert verifier or client will fail to connect with following error + // HandshakeError::Failure(Io(Custom { kind: InvalidData, error: WebPKIError(UnknownIssuer) })) + config.dangerous().set_certificate_verifier(Arc::new(IgnoreCertVerifier {})); + let connector = Connector::Rustls(Arc::new(config)); + let addrs = ws_server_url.socket_addrs(|| None).unwrap(); + let stream = TcpStream::connect(&*addrs) + .map_err(|e| format!("Could not connect to {:?}, reason: {:?}", &addrs, e))?; + + let (mut socket, _response) = + client_tls_with_config(ws_server_url, stream, None, Some(connector)) + .map_err(|e| format!("Could not open websocket connection: {:?}", e))?; + + let (request_sender, request_receiver) = channel(); + + //it fails to perform handshake in non_blocking mode so we are setting it up after the handshake is performed + Self::switch_to_non_blocking(&mut socket); + + std::thread::spawn(move || loop { + // let's flush all pending requests first + while let Ok(request) = request_receiver.try_recv() { + socket.write_message(Message::Text(request)).unwrap() + } + + if let Ok(message) = socket.read_message() { + if let Ok(Some(response)) = Self::handle_ws_message(message) { + if let Err(e) = response_sink.send(response) { + log::error!("Could not forward response, reason: {:?}", e) + }; + } + } + std::thread::sleep(Duration::from_millis(10)) + }); + + debug!("Connected to peer: {}", url); + + Ok(Self { request_sink: request_sender }) + } + + fn switch_to_non_blocking(socket: &mut WebSocket>) { + match socket.get_ref() { + MaybeTlsStream::Plain(stream) => { + stream.set_nonblocking(true).expect("set_nonblocking call failed"); + stream + .set_read_timeout(Some(Duration::from_millis(5))) + .expect("set_read_timeout call failed"); + }, + MaybeTlsStream::Rustls(stream) => { + stream.get_ref().set_nonblocking(true).expect("set_nonblocking call failed"); + stream + .get_ref() + .set_read_timeout(Some(Duration::from_millis(1))) + .expect("set_read_timeout call failed"); + }, + _ => {}, + } + } + + fn handle_ws_message(message: Message) -> Result, Box> { + match message { + Message::Text(text) => { + let rpc_response: RpcResponse = from_str(&text) + .map_err(|e| format!("Could not deserialize RpcResponse, reason: {:?}", e))?; + let return_value: RpcReturnValue = + RpcReturnValue::from_hex(&rpc_response.result) + .map_err(|e| format!("Could not deserialize value , reason: {:?}", e))?; + Ok(Some((rpc_response.id, return_value))) + }, + _ => { + log::warn!("Only text messages are supported"); + Ok(None) + }, + } + } +} + +#[derive(Clone)] +pub enum RequestParams { + Rsa(Vec), + Aes(Vec), +} + +impl RpcClient for DirectRpcClient { + fn send(&mut self, request: &RpcRequest) -> Result<(), Box> { + let request = serde_json::to_string(request) + .map_err(|e| format!("Could not parse RpcRequest {:?}", e))?; + self.request_sink + .send(request) + .map_err(|e| format!("Could not write message, reason: {:?}", e).into()) + } +} + +#[cfg(test)] +mod tests { + use crate::DirectRpcClient; + use itp_rpc::{Id, RpcResponse, RpcReturnValue}; + use itp_types::{DirectRequestStatus, TrustedOperationStatus, H256}; + use itp_utils::ToHexPrefixed; + use tungstenite::Message; + + #[test] + fn test_response_handling() { + let id = Id::Text( + "0x0000000000000000000000000000000000000000000000000000000000000000".to_owned(), + ); + let return_value: RpcReturnValue = RpcReturnValue::new( + vec![], + false, + DirectRequestStatus::TrustedOperationStatus( + TrustedOperationStatus::TopExecuted(vec![], true), + H256::random(), + ), + ); + let rpc_response: RpcResponse = RpcResponse { + jsonrpc: "2.0".to_owned(), + result: return_value.to_hex(), + id: id.clone(), + }; + let serialized_rpc_response = serde_json::to_string(&rpc_response).unwrap(); + let message = Message::text(serialized_rpc_response); + + let (result_id, result) = DirectRpcClient::handle_ws_message(message).unwrap().unwrap(); + + assert_eq!(id, result_id); + assert_eq!(return_value, result); + } +} diff --git a/bitacross-worker/core/direct-rpc-server/Cargo.toml b/bitacross-worker/core/direct-rpc-server/Cargo.toml new file mode 100644 index 0000000000..8b88b1f3d5 --- /dev/null +++ b/bitacross-worker/core/direct-rpc-server/Cargo.toml @@ -0,0 +1,55 @@ +[package] +name = "itc-direct-rpc-server" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +# sgx dependencies +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true, features = ["net", "thread"] } + +# no-std dependencies +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +log = { version = "0.4", default-features = false } +serde_json = { version = "1.0", default-features = false, features = ["alloc"] } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# local +itc-tls-websocket-server = { path = "../tls-websocket-server", default-features = false } +itp-rpc = { path = "../../core-primitives/rpc", default-features = false } +itp-types = { default-features = false, path = "../../core-primitives/types" } +itp-utils = { default-features = false, path = "../../core-primitives/utils" } + +# sgx enabled external libraries +jsonrpc-core_sgx = { package = "jsonrpc-core", git = "https://github.com/scs/jsonrpc", branch = "no_std_v18", default-features = false, optional = true } +thiserror_sgx = { package = "thiserror", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3", optional = true } + +# std compatible external libraries (make sure these versions match with the sgx-enabled ones above) +jsonrpc-core = { version = "18", optional = true } +thiserror = { version = "1.0", optional = true } + +[features] +default = ["std"] +std = [ + # no-std dependencies + "codec/std", + "log/std", + "serde_json/std", + "sp-runtime/std", + # integritee dependencies + "itp-types/std", + # local + "itc-tls-websocket-server/std", + "itp-rpc/std", + # optional ones + "jsonrpc-core", + "thiserror", +] +sgx = [ + "itc-tls-websocket-server/sgx", + "itp-rpc/sgx", + "jsonrpc-core_sgx", + "sgx_tstd", + "thiserror_sgx", +] +mocks = [] diff --git a/bitacross-worker/core/direct-rpc-server/src/builders/mod.rs b/bitacross-worker/core/direct-rpc-server/src/builders/mod.rs new file mode 100644 index 0000000000..ea028434c4 --- /dev/null +++ b/bitacross-worker/core/direct-rpc-server/src/builders/mod.rs @@ -0,0 +1,19 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pub mod rpc_response_builder; +pub mod rpc_return_value_builder; diff --git a/bitacross-worker/core/direct-rpc-server/src/builders/rpc_response_builder.rs b/bitacross-worker/core/direct-rpc-server/src/builders/rpc_response_builder.rs new file mode 100644 index 0000000000..9cc85cf369 --- /dev/null +++ b/bitacross-worker/core/direct-rpc-server/src/builders/rpc_response_builder.rs @@ -0,0 +1,64 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::builders::rpc_return_value_builder::RpcReturnValueBuilder; +use itp_rpc::{Id, RpcResponse, RpcReturnValue}; +use itp_utils::ToHexPrefixed; + +/// builder pattern for RpcResponse +pub struct RpcResponseBuilder { + maybe_id: Option, + maybe_json_rpc: Option, + maybe_result: Option, +} + +impl RpcResponseBuilder { + #[allow(unused)] + pub fn new() -> Self { + RpcResponseBuilder { maybe_id: None, maybe_json_rpc: None, maybe_result: None } + } + + #[allow(unused)] + pub fn with_id(mut self, id: u32) -> Self { + self.maybe_id = Some(id); + self + } + + #[allow(unused)] + pub fn with_json_rpc(mut self, json_rpc: String) -> Self { + self.maybe_json_rpc = Some(json_rpc); + self + } + + #[allow(unused)] + pub fn with_result(mut self, result: RpcReturnValue) -> Self { + self.maybe_result = Some(result); + self + } + + #[allow(unused)] + pub fn build(self) -> RpcResponse { + let id = Id::Number(self.maybe_id.unwrap_or(1u32)); + let json_rpc = self.maybe_json_rpc.unwrap_or(String::from("json_rpc")); + let result = self + .maybe_result + .unwrap_or_else(|| RpcReturnValueBuilder::new().build()) + .to_hex(); + + RpcResponse { result, jsonrpc: json_rpc, id } + } +} diff --git a/bitacross-worker/core/direct-rpc-server/src/builders/rpc_return_value_builder.rs b/bitacross-worker/core/direct-rpc-server/src/builders/rpc_return_value_builder.rs new file mode 100644 index 0000000000..126d58e985 --- /dev/null +++ b/bitacross-worker/core/direct-rpc-server/src/builders/rpc_return_value_builder.rs @@ -0,0 +1,62 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use codec::Encode; +use itp_rpc::RpcReturnValue; +use itp_types::DirectRequestStatus; +use std::{string::String, vec::Vec}; + +/// Builder pattern for a RpcReturnValue +pub struct RpcReturnValueBuilder { + maybe_do_watch: Option, + maybe_status: Option, + maybe_value: Option>, +} + +impl RpcReturnValueBuilder { + #[allow(unused)] + pub fn new() -> Self { + RpcReturnValueBuilder { maybe_do_watch: None, maybe_status: None, maybe_value: None } + } + + #[allow(unused)] + pub fn with_do_watch(mut self, do_watch: bool) -> Self { + self.maybe_do_watch = Some(do_watch); + self + } + + #[allow(unused)] + pub fn with_status(mut self, status: DirectRequestStatus) -> Self { + self.maybe_status = Some(status); + self + } + + #[allow(unused)] + pub fn with_value(mut self, value: Vec) -> Self { + self.maybe_value = Some(value); + self + } + + #[allow(unused)] + pub fn build(self) -> RpcReturnValue { + let do_watch = self.maybe_do_watch.unwrap_or(false); + let status = self.maybe_status.unwrap_or(DirectRequestStatus::Ok); + let value = self.maybe_value.unwrap_or(String::from("value").encode()); + + RpcReturnValue { value, do_watch, status } + } +} diff --git a/bitacross-worker/core/direct-rpc-server/src/lib.rs b/bitacross-worker/core/direct-rpc-server/src/lib.rs new file mode 100644 index 0000000000..b05a30e67f --- /dev/null +++ b/bitacross-worker/core/direct-rpc-server/src/lib.rs @@ -0,0 +1,158 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(test, feature(assert_matches))] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +extern crate alloc; + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +// re-export module to properly feature gate sgx and regular std environment +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod sgx_reexport_prelude { + pub use jsonrpc_core_sgx as jsonrpc_core; + pub use thiserror_sgx as thiserror; +} + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use crate::rpc_watch_extractor::RpcWatchExtractor; +use codec::{Encode, Error as CodecError}; +use itc_tls_websocket_server::error::WebSocketError; +use itp_rpc::RpcResponse; +use itp_types::{TrustedOperationStatus, H256}; +use serde_json::error::Error as SerdeJsonError; +use sp_runtime::traits; +use std::{boxed::Box, fmt::Debug, vec::Vec}; + +#[cfg(any(test, feature = "mocks"))] +pub mod mocks; + +#[cfg(test)] +mod builders; + +pub mod response_channel; +pub mod rpc_connection_registry; +pub mod rpc_responder; +pub mod rpc_watch_extractor; +pub mod rpc_ws_handler; + +/// General web-socket error type +#[derive(Debug, thiserror::Error)] +pub enum DirectRpcError { + #[error("Invalid connection hash")] + InvalidConnectionHash, + #[error("RPC serialization error: {0}")] + SerializationError(SerdeJsonError), + #[error("Web socket error: {0}")] + WebSocketError(#[from] WebSocketError), + #[error("Encoding error: {0}")] + EncodingError(CodecError), + #[error("Other error: {0}")] + Other(Box), + // Litentry + #[error("Hash conversion error")] + HashConversionError, +} + +pub type DirectRpcResult = Result; + +/// trait helper to mix-in all necessary traits for a hash +pub trait RpcHash: std::hash::Hash + traits::Member + Encode { + fn maybe_h256(&self) -> Option; +} +impl RpcHash for T { + fn maybe_h256(&self) -> Option { + let enc = self.encode(); + if enc.len() == 32 { + let mut inner = [0u8; 32]; + inner.copy_from_slice(&enc); + Some(inner.into()) + } else { + None + } + } +} + +pub type ForceWait = bool; + +/// Registry for RPC connections (i.e. connections that are kept alive to send updates). +pub trait RpcConnectionRegistry: Send + Sync { + type Hash: RpcHash; + type Connection: Copy + Debug; + + fn store( + &self, + hash: Self::Hash, + connection: Self::Connection, + rpc_response: RpcResponse, + force_wait: ForceWait, + ); + + fn withdraw(&self, hash: &Self::Hash) -> Option<(Self::Connection, RpcResponse, ForceWait)>; + + fn is_force_wait(&self, hash: &Self::Hash) -> bool; +} + +/// Sends an RPC response back to the client. +pub trait SendRpcResponse: Send + Sync { + type Hash: RpcHash; + + fn update_status_event( + &self, + hash: Self::Hash, + status_update: TrustedOperationStatus, + ) -> DirectRpcResult<()>; + + fn send_state(&self, hash: Self::Hash, state_encoded: Vec) -> DirectRpcResult<()>; + + fn update_force_wait(&self, hash: Self::Hash, force_wait: bool) -> DirectRpcResult<()>; + + // Litentry: update the `value` field in the returning structure and connection force_wait flag + fn update_connection_state( + &self, + hash: Self::Hash, + encoded_value: Vec, + force_wait: bool, + ) -> DirectRpcResult<()>; + + // Litentry: swap the old hash with the new one in rpc connection registry + fn swap_hash(&self, old_hash: Self::Hash, new_hash: Self::Hash) -> DirectRpcResult<()>; + + fn is_force_wait(&self, hash: Self::Hash) -> bool; +} + +/// Determines if a given connection must be watched (i.e. kept alive), +/// based on the information in the RpcResponse. +pub trait DetermineWatch: Send + Sync { + type Hash: RpcHash; + + fn must_be_watched(&self, rpc_response: &RpcResponse) -> DirectRpcResult>; +} + +/// Convenience method to create a do_watch extractor. +pub fn create_determine_watch() -> RpcWatchExtractor +where + Hash: RpcHash, +{ + RpcWatchExtractor::::new() +} diff --git a/bitacross-worker/core/direct-rpc-server/src/mocks/determine_watch_mock.rs b/bitacross-worker/core/direct-rpc-server/src/mocks/determine_watch_mock.rs new file mode 100644 index 0000000000..c01730390d --- /dev/null +++ b/bitacross-worker/core/direct-rpc-server/src/mocks/determine_watch_mock.rs @@ -0,0 +1,52 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{DetermineWatch, DirectRpcResult, RpcHash}; +use itp_rpc::RpcResponse; + +pub struct DetermineWatchMock +where + Hash: RpcHash, +{ + watch_next: Option, +} + +impl DetermineWatchMock +where + Hash: RpcHash, +{ + #[allow(unused)] + pub fn do_watch(hash: Hash) -> Self { + DetermineWatchMock { watch_next: Some(hash) } + } + + #[allow(unused)] + pub fn no_watch() -> Self { + DetermineWatchMock { watch_next: None } + } +} + +impl DetermineWatch for DetermineWatchMock +where + Hash: RpcHash, +{ + type Hash = Hash; + + fn must_be_watched(&self, _rpc_response: &RpcResponse) -> DirectRpcResult> { + Ok(self.watch_next.clone()) + } +} diff --git a/bitacross-worker/core/direct-rpc-server/src/mocks/mod.rs b/bitacross-worker/core/direct-rpc-server/src/mocks/mod.rs new file mode 100644 index 0000000000..011b4d9905 --- /dev/null +++ b/bitacross-worker/core/direct-rpc-server/src/mocks/mod.rs @@ -0,0 +1,20 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pub mod determine_watch_mock; +pub mod response_channel_mock; +pub mod send_rpc_response_mock; diff --git a/bitacross-worker/core/direct-rpc-server/src/mocks/response_channel_mock.rs b/bitacross-worker/core/direct-rpc-server/src/mocks/response_channel_mock.rs new file mode 100644 index 0000000000..6a612d6766 --- /dev/null +++ b/bitacross-worker/core/direct-rpc-server/src/mocks/response_channel_mock.rs @@ -0,0 +1,55 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(feature = "sgx")] +use std::sync::SgxRwLock as RwLock; + +#[cfg(feature = "std")] +use std::sync::RwLock; + +use crate::{response_channel::ResponseChannel, DirectRpcError}; +use std::vec::Vec; + +#[derive(Default)] +pub struct ResponseChannelMock +where + Token: Copy + Send + Sync, +{ + sent_messages: RwLock>, +} + +impl ResponseChannelMock +where + Token: Copy + Send + Sync, +{ + pub fn number_of_updates(&self) -> usize { + self.sent_messages.read().unwrap().len() + } +} + +impl ResponseChannel for ResponseChannelMock +where + Token: Copy + Send + Sync, +{ + type Error = DirectRpcError; + + fn respond(&self, token: Token, message: String) -> Result<(), Self::Error> { + let mut messages_lock = self.sent_messages.write().unwrap(); + messages_lock.push((token, message)); + Ok(()) + } +} diff --git a/bitacross-worker/core/direct-rpc-server/src/mocks/send_rpc_response_mock.rs b/bitacross-worker/core/direct-rpc-server/src/mocks/send_rpc_response_mock.rs new file mode 100644 index 0000000000..bad5021bec --- /dev/null +++ b/bitacross-worker/core/direct-rpc-server/src/mocks/send_rpc_response_mock.rs @@ -0,0 +1,74 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(feature = "sgx")] +use std::sync::SgxRwLock as RwLock; + +#[cfg(feature = "std")] +use std::sync::RwLock; + +use crate::{DirectRpcResult, RpcHash, SendRpcResponse}; +use itp_types::TrustedOperationStatus; +use std::vec::Vec; + +/// Send RPC response mock. +#[derive(Default)] +pub struct SendRpcResponseMock { + pub sent_states: RwLock)>>, +} + +impl SendRpcResponse for SendRpcResponseMock +where + HashType: RpcHash, +{ + type Hash = HashType; + + fn update_status_event( + &self, + _hash: Self::Hash, + _status_update: TrustedOperationStatus, + ) -> DirectRpcResult<()> { + unimplemented!() + } + + fn send_state(&self, hash: Self::Hash, state_encoded: Vec) -> DirectRpcResult<()> { + let mut states_lock = self.sent_states.write().unwrap(); + states_lock.push((hash, state_encoded)); + Ok(()) + } + + fn update_force_wait(&self, _hash: Self::Hash, _force_wait: bool) -> DirectRpcResult<()> { + Ok(()) + } + + fn update_connection_state( + &self, + _hash: Self::Hash, + _encoded_value: Vec, + _force_wait: bool, + ) -> DirectRpcResult<()> { + Ok(()) + } + + fn swap_hash(&self, _old_hash: Self::Hash, _new_hash: Self::Hash) -> DirectRpcResult<()> { + Ok(()) + } + + fn is_force_wait(&self, _hash: Self::Hash) -> bool { + false + } +} diff --git a/bitacross-worker/core/direct-rpc-server/src/response_channel.rs b/bitacross-worker/core/direct-rpc-server/src/response_channel.rs new file mode 100644 index 0000000000..b1fe6a3fea --- /dev/null +++ b/bitacross-worker/core/direct-rpc-server/src/response_channel.rs @@ -0,0 +1,26 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::DirectRpcError; +use std::string::String; + +/// Response / status update channel for an RPC call. +pub trait ResponseChannel: Send + Sync { + type Error: Into; + + fn respond(&self, token: Token, message: String) -> Result<(), Self::Error>; +} diff --git a/bitacross-worker/core/direct-rpc-server/src/rpc_connection_registry.rs b/bitacross-worker/core/direct-rpc-server/src/rpc_connection_registry.rs new file mode 100644 index 0000000000..2c83986fe5 --- /dev/null +++ b/bitacross-worker/core/direct-rpc-server/src/rpc_connection_registry.rs @@ -0,0 +1,140 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use std::sync::SgxRwLock as RwLock; + +#[cfg(feature = "std")] +use std::sync::RwLock; + +use crate::{ForceWait, RpcConnectionRegistry, RpcHash}; +use itp_rpc::RpcResponse; +use std::{collections::HashMap, fmt::Debug}; + +type HashMapLock = RwLock>; + +pub struct ConnectionRegistry +where + Hash: RpcHash, + Token: Copy + Send + Sync + Debug, +{ + connection_map: + HashMapLock<::Hash, (Token, RpcResponse, ForceWait)>, +} + +impl ConnectionRegistry +where + Hash: RpcHash, + Token: Copy + Send + Sync + Debug, +{ + pub fn new() -> Self { + Self::default() + } + + #[cfg(test)] + pub fn is_empty(&self) -> bool { + self.connection_map.read().unwrap().is_empty() + } +} + +impl Default for ConnectionRegistry +where + Hash: RpcHash, + Token: Copy + Send + Sync + Debug, +{ + fn default() -> Self { + ConnectionRegistry { connection_map: RwLock::new(HashMap::default()) } + } +} + +impl RpcConnectionRegistry for ConnectionRegistry +where + Hash: RpcHash, + Token: Copy + Send + Sync + Debug, +{ + type Hash = Hash; + type Connection = Token; + + fn store( + &self, + hash: Self::Hash, + connection: Self::Connection, + rpc_response: RpcResponse, + force_wait: ForceWait, + ) { + let mut map = self.connection_map.write().expect("Lock poisoning"); + map.insert(hash, (connection, rpc_response, force_wait)); + } + + fn withdraw(&self, hash: &Self::Hash) -> Option<(Self::Connection, RpcResponse, ForceWait)> { + let mut map = self.connection_map.write().expect("Lock poisoning"); + map.remove(hash) + } + + fn is_force_wait(&self, hash: &Self::Hash) -> bool { + if let Some(v) = self.connection_map.read().unwrap().get(hash) { + v.2 + } else { + false + } + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + use itp_rpc::Id; + + type TestRegistry = ConnectionRegistry; + + #[test] + pub fn adding_element_with_same_hash_overwrite() { + let registry = TestRegistry::new(); + + let hash = "first".to_string(); + + registry.store(hash.clone(), 1, dummy_rpc_response(), false); + registry.store(hash.clone(), 2, dummy_rpc_response(), false); + + let connection_token = registry.withdraw(&hash).unwrap().0; + assert_eq!(2, connection_token); + } + + #[test] + pub fn withdrawing_from_empty_registry_returns_none() { + let registry = TestRegistry::new(); + + assert!(registry.withdraw(&"hash".to_string()).is_none()); + } + + #[test] + pub fn withdrawing_only_element_clears_registry() { + let registry = TestRegistry::new(); + let hash = "first".to_string(); + + registry.store(hash.clone(), 1, dummy_rpc_response(), false); + + let connection = registry.withdraw(&hash); + + assert!(connection.is_some()); + assert!(registry.is_empty()); + } + + fn dummy_rpc_response() -> RpcResponse { + RpcResponse { jsonrpc: String::new(), result: Default::default(), id: Id::Number(1u32) } + } +} diff --git a/bitacross-worker/core/direct-rpc-server/src/rpc_responder.rs b/bitacross-worker/core/direct-rpc-server/src/rpc_responder.rs new file mode 100644 index 0000000000..2b2e41c5ef --- /dev/null +++ b/bitacross-worker/core/direct-rpc-server/src/rpc_responder.rs @@ -0,0 +1,363 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + response_channel::ResponseChannel, DirectRpcError, DirectRpcResult, RpcConnectionRegistry, + RpcHash, SendRpcResponse, +}; +use alloc::format; +use itp_rpc::{RpcResponse, RpcReturnValue}; +use itp_types::{DirectRequestStatus, TrustedOperationStatus}; +use itp_utils::{FromHexPrefixed, ToHexPrefixed}; +use log::*; +use std::{sync::Arc, vec::Vec}; + +pub struct RpcResponder +where + Registry: RpcConnectionRegistry, + Hash: RpcHash, + ResponseChannelType: ResponseChannel, +{ + connection_registry: Arc, + response_channel: Arc, +} + +impl RpcResponder +where + Registry: RpcConnectionRegistry, + Hash: RpcHash, + ResponseChannelType: ResponseChannel, +{ + pub fn new( + connection_registry: Arc, + web_socket_responder: Arc, + ) -> Self { + RpcResponder { connection_registry, response_channel: web_socket_responder } + } + + fn encode_and_send_response( + &self, + connection: Registry::Connection, + rpc_response: &RpcResponse, + ) -> DirectRpcResult<()> { + let string_response = + serde_json::to_string(&rpc_response).map_err(DirectRpcError::SerializationError)?; + + self.response_channel.respond(connection, string_response).map_err(|e| e.into()) + } +} + +impl SendRpcResponse + for RpcResponder +where + Registry: RpcConnectionRegistry, + Hash: RpcHash, + ResponseChannelType: ResponseChannel, +{ + type Hash = Hash; + + fn update_status_event( + &self, + hash: Hash, + status_update: TrustedOperationStatus, + ) -> DirectRpcResult<()> { + debug!("updating status event, hash: {}, status: {:?}", hash.to_hex(), status_update); + + // withdraw removes it from the registry + let (connection_token, rpc_response, force_wait) = self + .connection_registry + .withdraw(&hash) + .ok_or(DirectRpcError::InvalidConnectionHash)?; + + let mut new_response = rpc_response.clone(); + + let mut result = RpcReturnValue::from_hex(&rpc_response.result) + .map_err(|e| DirectRpcError::Other(format!("{:?}", e).into()))?; + + // Litentry: + // connections are per trusted call, but if we expect trusted call to have a side effect of creating another trusted call (callback) + // we force connection to wait for potential TOP execution + let do_watch = continue_watching(&status_update) || force_wait; + + // update response + result.do_watch = do_watch; + result.status = DirectRequestStatus::TrustedOperationStatus( + status_update, + hash.maybe_h256().ok_or(DirectRpcError::HashConversionError)?, + ); + new_response.result = result.to_hex(); + + self.encode_and_send_response(connection_token, &new_response)?; + + if do_watch { + self.connection_registry.store(hash, connection_token, new_response, force_wait); + } + + debug!("updating status event successful"); + Ok(()) + } + + // TODO(Litentry): it seems that this fn is only used in tests? + fn send_state(&self, hash: Hash, state_encoded: Vec) -> DirectRpcResult<()> { + debug!("sending state"); + + // withdraw removes it from the registry + let (connection_token, mut response, _force_wait) = self + .connection_registry + .withdraw(&hash) + .ok_or(DirectRpcError::InvalidConnectionHash)?; + + // create return value + // TODO: Signature? + let submitted = DirectRequestStatus::TrustedOperationStatus( + TrustedOperationStatus::Submitted, + hash.maybe_h256().ok_or(DirectRpcError::HashConversionError)?, + ); + let result = RpcReturnValue::new(state_encoded, false, submitted); + + // update response + response.result = result.to_hex(); + + self.encode_and_send_response(connection_token, &response)?; + + debug!("sending state successful"); + Ok(()) + } + + fn update_force_wait(&self, hash: Self::Hash, force_wait: bool) -> DirectRpcResult<()> { + let (connection_token, rpc_response, _) = self + .connection_registry + .withdraw(&hash) + .ok_or(DirectRpcError::InvalidConnectionHash)?; + self.connection_registry.store(hash, connection_token, rpc_response, force_wait); + + Ok(()) + } + + fn is_force_wait(&self, hash: Self::Hash) -> bool { + self.connection_registry.is_force_wait(&hash) + } + + fn update_connection_state( + &self, + hash: Self::Hash, + encoded_value: Vec, + force_wait: bool, + ) -> DirectRpcResult<()> { + info!( + "updating connection state for hash {:?}: encoded_value {:?}, force_wait: {:?}", + hash, encoded_value, force_wait + ); + + // withdraw removes it from the registry + let (connection_token, rpc_response, _) = self + .connection_registry + .withdraw(&hash) + .ok_or(DirectRpcError::InvalidConnectionHash)?; + + let mut new_response = rpc_response.clone(); + + let mut result = RpcReturnValue::from_hex(&rpc_response.result) + .map_err(|e| DirectRpcError::Other(format!("{:?}", e).into()))?; + + result.value = encoded_value; + new_response.result = result.to_hex(); + self.connection_registry.store(hash, connection_token, new_response, force_wait); + + debug!("set response value OK"); + Ok(()) + } + + fn swap_hash(&self, old_hash: Self::Hash, new_hash: Self::Hash) -> DirectRpcResult<()> { + debug!("swap hash, old: {:?}, new: {:?}", old_hash, new_hash); + + let (connection_token, rpc_response, force_wait) = self + .connection_registry + .withdraw(&old_hash) + .ok_or(DirectRpcError::InvalidConnectionHash)?; + + // leave `rpc_response` untouched - it should be overwritten later anyway and keep on force waiting + self.connection_registry + .store(new_hash, connection_token, rpc_response, force_wait); + debug!("swap hash OK"); + Ok(()) + } +} + +fn continue_watching(status: &TrustedOperationStatus) -> bool { + !matches!( + status, + TrustedOperationStatus::Invalid + | TrustedOperationStatus::InSidechainBlock(_) + | TrustedOperationStatus::Finalized + | TrustedOperationStatus::Usurped + ) +} + +#[cfg(test)] +pub mod tests { + + use super::*; + use crate::{ + builders::rpc_response_builder::RpcResponseBuilder, + mocks::response_channel_mock::ResponseChannelMock, + rpc_connection_registry::ConnectionRegistry, + }; + use codec::Encode; + use itp_types::H256; + use std::assert_matches::assert_matches; + + type TestConnectionToken = u64; + type TestResponseChannel = ResponseChannelMock; + type TestConnectionRegistry = ConnectionRegistry; + + #[test] + fn given_empty_registry_when_updating_status_event_then_return_error() { + let connection_registry = Arc::new(TestConnectionRegistry::new()); + let websocket_responder = Arc::new(TestResponseChannel::default()); + let rpc_responder = RpcResponder::new(connection_registry, websocket_responder); + + assert_matches!( + rpc_responder.update_status_event([1u8; 32].into(), TrustedOperationStatus::Broadcast), + Err(DirectRpcError::InvalidConnectionHash) + ); + } + + #[test] + fn given_empty_registry_when_sending_state_then_return_error() { + let connection_registry = Arc::new(TestConnectionRegistry::new()); + let websocket_responder = Arc::new(TestResponseChannel::default()); + let rpc_responder = RpcResponder::new(connection_registry, websocket_responder); + + assert_matches!( + rpc_responder.send_state([1u8; 32].into(), vec![1u8, 2u8]), + Err(DirectRpcError::InvalidConnectionHash) + ); + } + + #[test] + fn updating_status_event_with_finalized_state_removes_connection() { + let connection_hash = H256::random(); + let connection_registry = create_registry_with_single_connection(connection_hash.clone()); + + let websocket_responder = Arc::new(TestResponseChannel::default()); + let rpc_responder = + RpcResponder::new(connection_registry.clone(), websocket_responder.clone()); + + let result = rpc_responder + .update_status_event(connection_hash.clone(), TrustedOperationStatus::Finalized); + + assert!(result.is_ok()); + + verify_closed_connection(&connection_hash, connection_registry); + assert_eq!(1, websocket_responder.number_of_updates()); + } + + #[test] + fn updating_status_event_with_finalized_state_doesnt_remove_connection_if_force_watch_set() { + let connection_hash = H256::random(); + let connection_registry = create_registry_with_single_connection(connection_hash.clone()); + + let websocket_responder = Arc::new(TestResponseChannel::default()); + let rpc_responder = + RpcResponder::new(connection_registry.clone(), websocket_responder.clone()); + rpc_responder + .update_connection_state(connection_hash.clone(), vec![], true) + .unwrap(); + + let result = rpc_responder + .update_status_event(connection_hash.clone(), TrustedOperationStatus::Finalized); + + assert!(result.is_ok()); + + verify_open_connection(&connection_hash, connection_registry); + assert_eq!(1, websocket_responder.number_of_updates()); + } + + #[test] + fn updating_status_event_with_ready_state_keeps_connection_and_sends_update() { + let connection_hash = H256::random(); + let connection_registry: Arc> = + create_registry_with_single_connection(connection_hash.clone()); + + let websocket_responder = Arc::new(TestResponseChannel::default()); + let rpc_responder = + RpcResponder::new(connection_registry.clone(), websocket_responder.clone()); + + let first_result = rpc_responder + .update_status_event(connection_hash.clone(), TrustedOperationStatus::Ready); + + let second_result = rpc_responder + .update_status_event(connection_hash.clone(), TrustedOperationStatus::Submitted); + + assert!(first_result.is_ok()); + assert!(second_result.is_ok()); + + verify_open_connection(&connection_hash, connection_registry); + assert_eq!(2, websocket_responder.number_of_updates()); + } + + #[test] + fn sending_state_successfully_sends_update_and_removes_connection_token() { + let connection_hash = H256::random(); + let connection_registry = create_registry_with_single_connection(connection_hash.clone()); + + let websocket_responder = Arc::new(TestResponseChannel::default()); + let rpc_responder = + RpcResponder::new(connection_registry.clone(), websocket_responder.clone()); + + let result = rpc_responder.send_state(connection_hash.clone(), "new_state".encode()); + assert!(result.is_ok()); + + verify_closed_connection(&connection_hash, connection_registry); + assert_eq!(1, websocket_responder.number_of_updates()); + } + + #[test] + fn test_continue_watching() { + assert!(!continue_watching(&TrustedOperationStatus::Invalid)); + assert!(!continue_watching(&TrustedOperationStatus::Usurped)); + assert!(continue_watching(&TrustedOperationStatus::Future)); + assert!(continue_watching(&TrustedOperationStatus::Broadcast)); + assert!(continue_watching(&TrustedOperationStatus::Dropped)); + } + + fn verify_open_connection( + connection_hash: &H256, + connection_registry: Arc, + ) { + let maybe_connection = connection_registry.withdraw(&connection_hash); + assert!(maybe_connection.is_some()); + } + + fn verify_closed_connection( + connection_hash: &H256, + connection_registry: Arc, + ) { + assert!(connection_registry.withdraw(&connection_hash).is_none()); + } + + fn create_registry_with_single_connection( + connection_hash: H256, + ) -> Arc { + let connection_registry = TestConnectionRegistry::new(); + let rpc_response = RpcResponseBuilder::new().with_id(2).build(); + + connection_registry.store(connection_hash.clone(), 1, rpc_response, false); + Arc::new(connection_registry) + } +} diff --git a/bitacross-worker/core/direct-rpc-server/src/rpc_watch_extractor.rs b/bitacross-worker/core/direct-rpc-server/src/rpc_watch_extractor.rs new file mode 100644 index 0000000000..141ff21b54 --- /dev/null +++ b/bitacross-worker/core/direct-rpc-server/src/rpc_watch_extractor.rs @@ -0,0 +1,131 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{DetermineWatch, DirectRpcError, DirectRpcResult, RpcHash}; +use alloc::format; +use codec::Decode; +use itp_rpc::{RpcResponse, RpcReturnValue}; +use itp_types::DirectRequestStatus; +use itp_utils::FromHexPrefixed; +use std::marker::PhantomData; + +pub struct RpcWatchExtractor +where + Hash: RpcHash, +{ + phantom_data: PhantomData, +} + +impl RpcWatchExtractor +where + Hash: RpcHash, +{ + pub fn new() -> Self { + Self::default() + } +} + +impl Default for RpcWatchExtractor +where + Hash: RpcHash, +{ + fn default() -> Self { + RpcWatchExtractor { phantom_data: PhantomData } + } +} + +impl DetermineWatch for RpcWatchExtractor +where + Hash: RpcHash + Decode, +{ + type Hash = Hash; + + fn must_be_watched(&self, rpc_response: &RpcResponse) -> DirectRpcResult> { + let rpc_return_value = RpcReturnValue::from_hex(&rpc_response.result) + .map_err(|e| DirectRpcError::Other(format!("{:?}", e).into()))?; + + if !rpc_return_value.do_watch { + return Ok(None) + } + + match rpc_return_value.status { + DirectRequestStatus::TrustedOperationStatus(_, top_hash) => + Self::Hash::decode::<_>(&mut top_hash.as_ref()) + .map(Some) + .map_err(DirectRpcError::EncodingError), + _ => Ok(None), + } + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + use crate::builders::{ + rpc_response_builder::RpcResponseBuilder, rpc_return_value_builder::RpcReturnValueBuilder, + }; + use codec::Encode; + use itp_rpc::Id; + use itp_types::{TrustedOperationStatus, H256}; + + #[test] + fn invalid_rpc_response_returns_error() { + let watch_extractor = RpcWatchExtractor::::new(); + let rpc_response = RpcResponse { + id: Id::Number(1u32), + jsonrpc: String::from("json"), + result: "hello".to_string(), + }; + + assert!(watch_extractor.must_be_watched(&rpc_response).is_err()); + } + + #[test] + fn rpc_response_without_watch_flag_must_not_be_watched() { + let watch_extractor = RpcWatchExtractor::::new(); + let rpc_result = RpcReturnValueBuilder::new() + .with_do_watch(false) + .with_status(DirectRequestStatus::TrustedOperationStatus( + TrustedOperationStatus::Ready, + Default::default(), + )) + .build(); + let rpc_response = RpcResponseBuilder::new().with_result(rpc_result).build(); + + let do_watch = watch_extractor.must_be_watched(&rpc_response).unwrap(); + + assert_eq!(None, do_watch); + } + + #[test] + fn rpc_response_with_watch_flag_must_be_watched() { + let hash = H256::random(); + let watch_extractor = RpcWatchExtractor::::new(); + let rpc_return_value = RpcReturnValueBuilder::new() + .with_do_watch(true) + .with_status(DirectRequestStatus::TrustedOperationStatus( + TrustedOperationStatus::Ready, + hash, + )) + .build(); + let rpc_response = RpcResponseBuilder::new().with_result(rpc_return_value).build(); + + let do_watch = watch_extractor.must_be_watched(&rpc_response).unwrap(); + + assert_eq!(Some(hash), do_watch); + } +} diff --git a/bitacross-worker/core/direct-rpc-server/src/rpc_ws_handler.rs b/bitacross-worker/core/direct-rpc-server/src/rpc_ws_handler.rs new file mode 100644 index 0000000000..fce836591e --- /dev/null +++ b/bitacross-worker/core/direct-rpc-server/src/rpc_ws_handler.rs @@ -0,0 +1,226 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use crate::{DetermineWatch, RpcConnectionRegistry, RpcHash}; +use itc_tls_websocket_server::{error::WebSocketResult, ConnectionToken, WebSocketMessageHandler}; +use jsonrpc_core::IoHandler; +use log::*; +use std::{string::String, sync::Arc}; + +pub struct RpcWsHandler +where + Watcher: DetermineWatch, + Registry: RpcConnectionRegistry, + Hash: RpcHash, +{ + rpc_io_handler: IoHandler, + connection_watcher: Arc, + connection_registry: Arc, +} + +impl RpcWsHandler +where + Watcher: DetermineWatch, + Registry: RpcConnectionRegistry, + Hash: RpcHash, +{ + pub fn new( + rpc_io_handler: IoHandler, + connection_watcher: Arc, + connection_registry: Arc, + ) -> Self { + RpcWsHandler { rpc_io_handler, connection_watcher, connection_registry } + } +} + +impl WebSocketMessageHandler for RpcWsHandler +where + Watcher: DetermineWatch, + Registry: RpcConnectionRegistry, + Registry::Connection: From, + Hash: RpcHash, +{ + fn handle_message( + &self, + connection_token: ConnectionToken, + message: String, + ) -> WebSocketResult> { + let maybe_rpc_response = self.rpc_io_handler.handle_request_sync(message.as_str()); + + debug!("RPC response string: {:?}", maybe_rpc_response); + + if let Ok(rpc_response) = + serde_json::from_str(maybe_rpc_response.clone().unwrap_or_default().as_str()) + { + if let Ok(Some(connection_hash)) = + self.connection_watcher.must_be_watched(&rpc_response) + { + self.connection_registry.store( + connection_hash, + connection_token.into(), + rpc_response, + false, + ); + } + } + + Ok(maybe_rpc_response) + } +} + +#[cfg(test)] +pub mod tests { + + use super::*; + use crate::{ + mocks::determine_watch_mock::DetermineWatchMock, + rpc_connection_registry::ConnectionRegistry, + }; + use codec::Encode; + use itc_tls_websocket_server::ConnectionToken; + use itp_rpc::RpcReturnValue; + use itp_types::DirectRequestStatus; + use itp_utils::ToHexPrefixed; + use jsonrpc_core::Params; + use serde_json::json; + + type TestConnectionRegistry = ConnectionRegistry; + type TestConnectionWatcher = DetermineWatchMock; + type TestWsHandler = RpcWsHandler; + + const RPC_METHOD_NAME: &str = "test_call"; + + #[test] + fn valid_rpc_call_without_watch_runs_successfully() { + let io_handler = create_io_handler_with_method(RPC_METHOD_NAME); + + let (connection_token, message) = create_message_to_handle(RPC_METHOD_NAME); + + let (ws_handler, connection_registry) = create_ws_handler(io_handler, None); + + let handle_result = ws_handler.handle_message(connection_token, message); + + assert!(handle_result.is_ok()); + assert!(connection_registry.is_empty()); + } + + #[test] + fn valid_rpc_call_with_watch_runs_successfully_and_stores_connection() { + let io_handler = create_io_handler_with_method(RPC_METHOD_NAME); + + let connection_hash = String::from("connection_hash"); + let (connection_token, message) = create_message_to_handle(RPC_METHOD_NAME); + + let (ws_handler, connection_registry) = + create_ws_handler(io_handler, Some(connection_hash.clone())); + + let handle_result = ws_handler.handle_message(connection_token, message); + + assert!(handle_result.is_ok()); + assert!(connection_registry.withdraw(&connection_hash).is_some()); + } + + #[test] + fn when_rpc_returns_error_then_return_ok_but_status_is_set_to_error() { + let io_handler = create_io_handler_with_error(RPC_METHOD_NAME); + + let connection_hash = String::from("connection_hash"); + let (connection_token, message) = create_message_to_handle(RPC_METHOD_NAME); + + let (ws_handler, connection_registry) = + create_ws_handler(io_handler, Some(connection_hash.clone())); + + let handle_result = ws_handler.handle_message(connection_token, message); + + assert!(handle_result.is_ok()); + assert!(connection_registry.withdraw(&connection_hash).is_some()); + } + + #[test] + fn when_rpc_method_does_not_match_anything_return_json_error_message() { + let io_handler = create_io_handler_with_error(RPC_METHOD_NAME); + let (connection_token, message) = create_message_to_handle("not_a_valid_method"); + + let (ws_handler, connection_registry) = create_ws_handler(io_handler, None); + + let handle_result = ws_handler.handle_message(connection_token, message).unwrap().unwrap(); + + assert_eq!(handle_result, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32601,\"message\":\"Method not found\"},\"id\":1}"); + assert!(connection_registry.is_empty()); + } + + fn create_message_to_handle(method_name: &str) -> (ConnectionToken, String) { + let json_rpc_pre_method = r#"{"jsonrpc": "2.0", "method": ""#; + let json_rpc_post_method = r#"", "params": {}, "id": 1}"#; + + let json_string = format!("{}{}{}", json_rpc_pre_method, method_name, json_rpc_post_method); + debug!("JSON input: {}", json_string); + + (ConnectionToken(23), json_string) + } + + fn create_ws_handler( + io_handler: IoHandler, + watch_connection: Option, + ) -> (TestWsHandler, Arc) { + let watcher = match watch_connection { + Some(hash) => TestConnectionWatcher::do_watch(hash), + None => TestConnectionWatcher::no_watch(), + }; + + let connection_registry = Arc::new(TestConnectionRegistry::new()); + + ( + TestWsHandler::new(io_handler, Arc::new(watcher), connection_registry.clone()), + connection_registry, + ) + } + + fn create_io_handler_with_method(method_name: &str) -> IoHandler { + create_io_handler( + method_name, + RpcReturnValue { + do_watch: false, + value: String::from("value").encode(), + status: DirectRequestStatus::Ok, + }, + ) + } + + fn create_io_handler_with_error(method_name: &str) -> IoHandler { + create_io_handler( + method_name, + RpcReturnValue { + value: "error!".encode(), + do_watch: false, + status: DirectRequestStatus::Error, + }, + ) + } + + fn create_io_handler(method_name: &str, return_value: ReturnValue) -> IoHandler + where + ReturnValue: Encode + Send + Sync + 'static, + { + let mut io_handler = IoHandler::new(); + io_handler.add_sync_method(method_name, move |_: Params| Ok(json!(return_value.to_hex()))); + io_handler + } +} diff --git a/bitacross-worker/core/offchain-worker-executor/Cargo.toml b/bitacross-worker/core/offchain-worker-executor/Cargo.toml new file mode 100644 index 0000000000..24d1fd896a --- /dev/null +++ b/bitacross-worker/core/offchain-worker-executor/Cargo.toml @@ -0,0 +1,70 @@ +[package] +name = "itc-offchain-worker-executor" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +# sgx dependencies +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } + +# sgx enabled external libraries +thiserror_sgx = { package = "thiserror", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3", optional = true } + +# std compatible external libraries (make sure these versions match with the sgx-enabled ones above) +thiserror = { version = "1.0", optional = true } + +# local dependencies +itc-parentchain-light-client = { path = "../../core/parentchain/light-client", default-features = false } +itp-extrinsics-factory = { path = "../../core-primitives/extrinsics-factory", default-features = false } +itp-stf-executor = { path = "../../core-primitives/stf-executor", default-features = false } +itp-stf-interface = { path = "../../core-primitives/stf-interface", default-features = false } +itp-stf-primitives = { path = "../../core-primitives/stf-primitives", default-features = false } +itp-stf-state-handler = { path = "../../core-primitives/stf-state-handler", default-features = false } +itp-top-pool-author = { path = "../../core-primitives/top-pool-author", default-features = false } +itp-types = { path = "../../core-primitives/types", default-features = false } + +# Substrate dependencies +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# no-std compatible libraries +log = { version = "0.4", default-features = false } + +[dev-dependencies] +itp-stf-primitives = { path = "../../core-primitives/stf-primitives", default-features = false } +codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +itc-parentchain-light-client = { path = "../../core/parentchain/light-client", features = ["mocks"] } +itp-extrinsics-factory = { path = "../../core-primitives/extrinsics-factory", features = ["mocks"] } +itp-stf-executor = { path = "../../core-primitives/stf-executor", features = ["mocks"] } +itp-test = { path = "../../core-primitives/test" } +itp-top-pool-author = { path = "../../core-primitives/top-pool-author", features = ["mocks"] } +itp-stf-interface = { path = "../../core-primitives/stf-interface", features = ["mocks"] } +itp-sgx-externalities = { path = "../../core-primitives/substrate-sgx/externalities" } +sp-core = { default-features = false, features = ["full_crypto"], git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +[features] +default = ["std"] +std = [ + "itc-parentchain-light-client/std", + "itp-extrinsics-factory/std", + "itp-stf-executor/std", + "itp-stf-interface/std", + "itp-stf-primitives/std", + "itp-stf-state-handler/std", + "itp-top-pool-author/std", + "itp-types/std", + "sp-runtime/std", + "thiserror", +] +sgx = [ + "itc-parentchain-light-client/sgx", + "itp-extrinsics-factory/sgx", + "itp-stf-executor/sgx", + "itp-stf-state-handler/sgx", + "itp-top-pool-author/sgx", + "sgx_tstd", + "thiserror_sgx", +] diff --git a/bitacross-worker/core/offchain-worker-executor/src/error.rs b/bitacross-worker/core/offchain-worker-executor/src/error.rs new file mode 100644 index 0000000000..2c955d3e00 --- /dev/null +++ b/bitacross-worker/core/offchain-worker-executor/src/error.rs @@ -0,0 +1,40 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use std::boxed::Box; + +pub type Result = core::result::Result; + +/// General offchain-worker error type +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("STF state handler error: {0}")] + StfStateHandler(#[from] itp_stf_state_handler::error::Error), + #[error("STF executor error: {0}")] + StfExecutor(#[from] itp_stf_executor::error::Error), + #[error("TOP pool author error: {0}")] + TopPoolAuthor(#[from] itp_top_pool_author::error::Error), + #[error("Light-client error: {0}")] + LightClient(#[from] itc_parentchain_light_client::error::Error), + #[error("Extrinsics factory error: {0}")] + ExtrinsicsFactory(#[from] itp_extrinsics_factory::error::Error), + #[error("{0}")] + Other(Box), +} diff --git a/bitacross-worker/core/offchain-worker-executor/src/executor.rs b/bitacross-worker/core/offchain-worker-executor/src/executor.rs new file mode 100644 index 0000000000..5cf3e778b8 --- /dev/null +++ b/bitacross-worker/core/offchain-worker-executor/src/executor.rs @@ -0,0 +1,373 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::error::Result; +use codec::{Decode, Encode}; +use core::fmt::Debug; +use itc_parentchain_light_client::{ + concurrent_access::ValidatorAccess, BlockNumberOps, ExtrinsicSender, LightClientState, + NumberFor, +}; +use itp_extrinsics_factory::CreateExtrinsics; +use itp_stf_executor::{traits::StateUpdateProposer, ExecutedOperation}; +use itp_stf_interface::system_pallet::SystemPalletEventInterface; +use itp_stf_primitives::{traits::TrustedCallVerification, types::TrustedOperationOrHash}; +use itp_stf_state_handler::{handle_state::HandleState, query_shard_state::QueryShardState}; +use itp_top_pool_author::traits::AuthorApi; +use itp_types::{parentchain::ParentchainCall, OpaqueCall, ShardIdentifier, H256}; +use log::*; +use sp_runtime::traits::Block; +use std::{marker::PhantomData, sync::Arc, time::Duration, vec::Vec}; + +/// Off-chain worker executor implementation. +/// +/// Executes calls found in the top-pool and immediately applies the corresponding state diffs. +/// - Sends confirmations for all executed calls (TODO) +/// - Sends extrinsics for any parentchain effects (such as unshield calls). +/// +/// The trigger to start executing calls is given when the parentchain block imported event is +/// signaled (event listener). +pub struct Executor< + ParentchainBlock, + TopPoolAuthor, + StfExecutor, + StateHandler, + ValidatorAccessor, + ExtrinsicsFactory, + Stf, + TCS, + G, +> { + top_pool_author: Arc, + stf_executor: Arc, + state_handler: Arc, + validator_accessor: Arc, + extrinsics_factory: Arc, + _phantom: PhantomData<(ParentchainBlock, Stf, TCS, G)>, +} + +impl< + ParentchainBlock, + TopPoolAuthor, + StfExecutor, + StateHandler, + ValidatorAccessor, + ExtrinsicsFactory, + Stf, + TCS, + G, + > + Executor< + ParentchainBlock, + TopPoolAuthor, + StfExecutor, + StateHandler, + ValidatorAccessor, + ExtrinsicsFactory, + Stf, + TCS, + G, + > where + ParentchainBlock: Block, + StfExecutor: StateUpdateProposer, + TopPoolAuthor: AuthorApi, + StateHandler: QueryShardState + HandleState, + ValidatorAccessor: ValidatorAccess + Send + Sync + 'static, + ExtrinsicsFactory: CreateExtrinsics, + NumberFor: BlockNumberOps, + Stf: SystemPalletEventInterface, + TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, + G: PartialEq + Encode + Decode + Debug + Clone + Send + Sync, +{ + pub fn new( + top_pool_author: Arc, + stf_executor: Arc, + state_handler: Arc, + validator_accessor: Arc, + extrinsics_factory: Arc, + ) -> Self { + Self { + top_pool_author, + stf_executor, + state_handler, + validator_accessor, + extrinsics_factory, + _phantom: Default::default(), + } + } + + pub fn execute(&self) -> Result<()> { + let max_duration = Duration::from_secs(5); + let latest_parentchain_header = self.get_latest_parentchain_header()?; + + let mut parentchain_effects: Vec = Vec::new(); + + let shards = self.state_handler.list_shards()?; + trace!("Executing calls on {} shard(s)", shards.len()); + + for shard in shards { + debug!( + "executing pending tops in top pool with status: {:?}", + self.top_pool_author.get_status(shard) + ); + let trusted_calls = self.top_pool_author.get_pending_trusted_calls(shard); + trace!("Executing {} trusted calls on shard {:?}", trusted_calls.len(), shard); + + let batch_execution_result = self.stf_executor.propose_state_update( + &trusted_calls, + &latest_parentchain_header, + &shard, + max_duration, + |mut state| { + Stf::reset_events(&mut state); + state + }, + )?; + + parentchain_effects + .append(&mut batch_execution_result.get_extrinsic_callbacks().clone()); + + let failed_operations = batch_execution_result.get_failed_operations(); + let successful_operations: Vec> = batch_execution_result + .get_executed_operation_hashes() + .into_iter() + .map(|h| { + ExecutedOperation::success( + h, + TrustedOperationOrHash::Hash(h), + Vec::new(), + Vec::new(), + false, + ) + }) + .collect(); + + // Remove all not successfully executed operations from the top pool. + self.remove_calls_from_pool(&shard, failed_operations); + + // Apply the state update + self.apply_state_update(&shard, batch_execution_result.state_after_execution)?; + + // Remove successful operations from pool + self.remove_calls_from_pool(&shard, successful_operations); + + // TODO: notify parentchain about executed operations? -> add to parentchain effects + } + + if !parentchain_effects.is_empty() { + self.send_parentchain_effects(parentchain_effects)?; + } + + Ok(()) + } + + fn get_latest_parentchain_header(&self) -> Result { + let header = self.validator_accessor.execute_on_validator(|v| { + let latest_parentchain_header = v.latest_finalized_header()?; + Ok(latest_parentchain_header) + })?; + Ok(header) + } + + fn apply_state_update( + &self, + shard: &ShardIdentifier, + updated_state: >::Externalities, + ) -> Result<()> { + self.state_handler.reset(updated_state, shard)?; + Ok(()) + } + + fn send_parentchain_effects(&self, parentchain_effects: Vec) -> Result<()> { + let integritee_calls: Vec = parentchain_effects + .iter() + .filter_map(|parentchain_call| parentchain_call.as_litentry()) + .collect(); + let target_a_calls: Vec = parentchain_effects + .iter() + .filter_map(|parentchain_call| parentchain_call.as_target_a()) + .collect(); + let target_b_calls: Vec = parentchain_effects + .iter() + .filter_map(|parentchain_call| parentchain_call.as_target_b()) + .collect(); + debug!( + "stf wants to send calls to parentchains: Integritee: {} TargetA: {} TargetB: {}", + integritee_calls.len(), + target_a_calls.len(), + target_b_calls.len() + ); + if !target_a_calls.is_empty() { + warn!("sending extrinsics to target A unimplemented") + }; + if !target_b_calls.is_empty() { + warn!("sending extrinsics to target B unimplemented") + }; + + let extrinsics = + self.extrinsics_factory.create_extrinsics(integritee_calls.as_slice(), None)?; + self.validator_accessor + .execute_mut_on_validator(|v| v.send_extrinsics(extrinsics))?; + Ok(()) + } + + fn remove_calls_from_pool( + &self, + shard: &ShardIdentifier, + executed_calls: Vec>, + ) -> Vec> { + let executed_calls_tuple: Vec<_> = executed_calls + .iter() + .map(|e| (e.trusted_operation_or_hash.clone(), e.is_success())) + .collect(); + let failed_to_remove_hashes = + self.top_pool_author.remove_calls_from_pool(*shard, executed_calls_tuple); + + let failed_executed_calls: Vec<_> = executed_calls + .into_iter() + .filter(|e| failed_to_remove_hashes.contains(&e.trusted_operation_or_hash)) + .collect(); + + failed_executed_calls + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use codec::{Decode, Encode}; + use itc_parentchain_light_client::mocks::validator_access_mock::ValidatorAccessMock; + use itp_extrinsics_factory::mock::ExtrinsicsFactoryMock; + use itp_sgx_externalities::SgxExternalitiesTrait; + use itp_stf_executor::mocks::StfExecutorMock; + + use itp_test::mock::{ + handle_state_mock::HandleStateMock, + stf_mock::{GetterMock, TrustedCallSignedMock}, + }; + use itp_top_pool_author::mocks::AuthorApiMock; + use itp_types::{Block as ParentchainBlock, RsaRequest}; + + use itp_test::mock::stf_mock::mock_top_indirect_trusted_call_signed; + use std::boxed::Box; + + type TestStateHandler = HandleStateMock; + type TestStfInterface = SystemPalletEventInterfaceMock; + type State = ::StateT; + type TestTopPoolAuthor = AuthorApiMock; + type TestStfExecutor = StfExecutorMock; + type TestValidatorAccess = ValidatorAccessMock; + type TestExtrinsicsFactory = ExtrinsicsFactoryMock; + type TestExecutor = Executor< + ParentchainBlock, + TestTopPoolAuthor, + TestStfExecutor, + TestStateHandler, + TestValidatorAccess, + TestExtrinsicsFactory, + TestStfInterface, + TrustedCallSignedMock, + GetterMock, + >; + + const EVENT_COUNT_KEY: &[u8] = b"event_count"; + + struct SystemPalletEventInterfaceMock; + + impl SystemPalletEventInterface for SystemPalletEventInterfaceMock { + type EventRecord = String; + type EventIndex = u32; + type BlockNumber = u32; + type Hash = String; + + fn get_events(_state: &mut State) -> Vec> { + unimplemented!(); + } + + fn get_event_count(state: &mut State) -> Self::EventIndex { + let encoded_value = state.get(EVENT_COUNT_KEY).unwrap(); + Self::EventIndex::decode(&mut encoded_value.as_slice()).unwrap() + } + + fn get_event_topics( + _state: &mut State, + _topic: &Self::Hash, + ) -> Vec<(Self::BlockNumber, Self::EventIndex)> { + unimplemented!() + } + + fn reset_events(state: &mut State) { + state.insert(EVENT_COUNT_KEY.to_vec(), 0u32.encode()); + } + } + + #[test] + fn executing_tops_from_pool_works_and_empties_pool() { + let stf_executor = Arc::new(TestStfExecutor::new(State::default())); + let top_pool_author = Arc::new(TestTopPoolAuthor::default()); + top_pool_author + .submit_top(RsaRequest::new(shard(), mock_top_indirect_trusted_call_signed().encode())); + + assert_eq!(1, top_pool_author.pending_tops(shard()).unwrap().len()); + + let executor = create_executor(top_pool_author.clone(), stf_executor); + + assert!(executor.execute().is_ok()); + + assert_eq!(0, top_pool_author.pending_tops(shard()).unwrap().len()); + } + + #[test] + fn reset_events_is_called() { + let mut state = State::default(); + let event_count = 5; + state.insert(EVENT_COUNT_KEY.to_vec(), event_count.encode()); + + let stf_executor = Arc::new(TestStfExecutor::new(state)); + assert_eq!(TestStfInterface::get_event_count(&mut stf_executor.get_state()), event_count); + + let top_pool_author = Arc::new(TestTopPoolAuthor::default()); + + let executor = create_executor(top_pool_author, stf_executor.clone()); + + executor.execute().unwrap(); + + assert_eq!(TestStfInterface::get_event_count(&mut stf_executor.get_state()), 0); + } + + fn create_executor( + top_pool_author: Arc, + stf_executor: Arc, + ) -> TestExecutor { + let state_handler = Arc::new(TestStateHandler::from_shard(shard()).unwrap()); + let validator_access = Arc::new(TestValidatorAccess::default()); + let extrinsics_factory = Arc::new(TestExtrinsicsFactory::default()); + + TestExecutor::new( + top_pool_author, + stf_executor, + state_handler, + validator_access, + extrinsics_factory, + ) + } + + fn shard() -> ShardIdentifier { + ShardIdentifier::default() + } +} diff --git a/bitacross-worker/core/offchain-worker-executor/src/lib.rs b/bitacross-worker/core/offchain-worker-executor/src/lib.rs new file mode 100644 index 0000000000..d30a11ba0b --- /dev/null +++ b/bitacross-worker/core/offchain-worker-executor/src/lib.rs @@ -0,0 +1,33 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +// re-export module to properly feature gate sgx and regular std environment +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod sgx_reexport_prelude { + pub use thiserror_sgx as thiserror; +} + +pub mod error; +pub mod executor; diff --git a/bitacross-worker/core/parentchain/block-import-dispatcher/Cargo.toml b/bitacross-worker/core/parentchain/block-import-dispatcher/Cargo.toml new file mode 100644 index 0000000000..1625bd31b5 --- /dev/null +++ b/bitacross-worker/core/parentchain/block-import-dispatcher/Cargo.toml @@ -0,0 +1,50 @@ +[package] +name = "itc-parentchain-block-import-dispatcher" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +# sgx dependencies +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } +sgx_types = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } + +# local dependencies +itc-parentchain-block-importer = { path = "../block-importer", default-features = false } +itp-import-queue = { path = "../../../core-primitives/import-queue", default-features = false } + +# sgx enabled external libraries +thiserror_sgx = { package = "thiserror", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3", optional = true } + +# crates.io std-only compatible libraries (make sure these versions match with the sgx-enabled ones above) +thiserror = { version = "1.0", optional = true } + +# crates.io no-std compatible libraries +log = { version = "0.4", default-features = false } + +[dev-dependencies] +itc-parentchain-block-importer = { path = "../block-importer", features = ["mocks"] } + +[features] +default = ["std"] +std = [ + # local + "itc-parentchain-block-importer/std", + "itp-import-queue/std", + # no-std compatible libraries + "log/std", + # std-only compatible libraries + "thiserror", +] +sgx = [ + # sgx + "sgx_tstd", + # local + "itc-parentchain-block-importer/sgx", + "itp-import-queue/sgx", + # sgx enabled external libraries + "thiserror_sgx", +] + +# feature to export mock implementations, only to be used for dev-dependencies! +mocks = [] diff --git a/bitacross-worker/core/parentchain/block-import-dispatcher/src/error.rs b/bitacross-worker/core/parentchain/block-import-dispatcher/src/error.rs new file mode 100644 index 0000000000..b5d73ffe54 --- /dev/null +++ b/bitacross-worker/core/parentchain/block-import-dispatcher/src/error.rs @@ -0,0 +1,47 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use sgx_types::sgx_status_t; +use std::boxed::Box; + +pub type Result = core::result::Result; + +/// Parentchain block importer error. +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("SGX error, status: {0}")] + Sgx(sgx_status_t), + #[error("Two Dispatcher types assigned. Please double check the initialization process.")] + CanNotAssignTwoDispatcher, + #[error("Even though there is no dispatcher assigned, the dispatch function is called.")] + NoDispatcherAssigned, + #[error("Block import queue error: {0}")] + ImportQueue(#[from] itp_import_queue::error::Error), + #[error("Block import error: {0}")] + BlockImport(#[from] itc_parentchain_block_importer::error::Error), + #[error(transparent)] + Other(#[from] Box), +} + +impl From for Error { + fn from(sgx_status: sgx_status_t) -> Self { + Self::Sgx(sgx_status) + } +} diff --git a/bitacross-worker/core/parentchain/block-import-dispatcher/src/immediate_dispatcher.rs b/bitacross-worker/core/parentchain/block-import-dispatcher/src/immediate_dispatcher.rs new file mode 100644 index 0000000000..a58383bf05 --- /dev/null +++ b/bitacross-worker/core/parentchain/block-import-dispatcher/src/immediate_dispatcher.rs @@ -0,0 +1,107 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{error::Result, DispatchBlockImport}; +use itc_parentchain_block_importer::ImportParentchainBlocks; +use log::*; +use std::{boxed::Box, vec::Vec}; + +/// Block import dispatcher that immediately imports the blocks, without any processing or queueing. +pub struct ImmediateDispatcher { + pub block_importer: BlockImporter, + import_event_observers: Vec>, +} + +impl ImmediateDispatcher { + pub fn new(block_importer: BlockImporter) -> Self { + ImmediateDispatcher { block_importer, import_event_observers: Vec::new() } + } + + pub fn with_observer(self, callback: F) -> Self + where + F: Fn() + Send + Sync + 'static, + { + let mut updated_observers = self.import_event_observers; + updated_observers.push(Box::new(callback)); + + Self { block_importer: self.block_importer, import_event_observers: updated_observers } + } +} + +impl DispatchBlockImport + for ImmediateDispatcher +where + BlockImporter: ImportParentchainBlocks, +{ + fn dispatch_import( + &self, + blocks: Vec, + events: Vec>, + _is_syncing: bool, + ) -> Result<()> { + // _is_syncing does not matter for the immediate dispatcher, behavoiur is the same. Immediate block import. + + debug!("Importing {} parentchain blocks", blocks.len()); + self.block_importer.import_parentchain_blocks(blocks, events)?; + debug!("Notifying {} observers of import", self.import_event_observers.len()); + self.import_event_observers.iter().for_each(|callback| callback()); + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use itc_parentchain_block_importer::block_importer_mock::ParentchainBlockImporterMock; + use std::{ + sync::{Arc, RwLock}, + vec, + }; + + type SignedBlockType = u32; + type TestBlockImporter = ParentchainBlockImporterMock; + type TestDispatcher = ImmediateDispatcher; + + #[derive(Default)] + struct NotificationCounter { + counter: RwLock, + } + + impl NotificationCounter { + fn increment(&self) { + *self.counter.write().unwrap() += 1; + } + + pub fn get_counter(&self) -> usize { + *self.counter.read().unwrap() + } + } + + #[test] + fn listeners_get_notified_upon_import() { + let block_importer = TestBlockImporter::default(); + let notification_counter = Arc::new(NotificationCounter::default()); + let counter_clone = notification_counter.clone(); + let dispatcher = TestDispatcher::new(block_importer).with_observer(move || { + counter_clone.increment(); + }); + + dispatcher.dispatch_import(vec![1u32, 2u32], vec![], false).unwrap(); + + assert_eq!(1, notification_counter.get_counter()); + } +} diff --git a/bitacross-worker/core/parentchain/block-import-dispatcher/src/lib.rs b/bitacross-worker/core/parentchain/block-import-dispatcher/src/lib.rs new file mode 100644 index 0000000000..2385075644 --- /dev/null +++ b/bitacross-worker/core/parentchain/block-import-dispatcher/src/lib.rs @@ -0,0 +1,125 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +//! Dispatching of block imports. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +// re-export module to properly feature gate sgx and regular std environment +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod sgx_reexport_prelude { + pub use thiserror_sgx as thiserror; +} + +pub mod error; +pub mod immediate_dispatcher; +pub mod triggered_dispatcher; + +#[cfg(feature = "mocks")] +pub mod trigger_parentchain_block_import_mock; + +use error::{Error, Result}; +use std::{sync::Arc, vec::Vec}; + +/// Trait to dispatch blocks for import into the local light-client. +pub trait DispatchBlockImport { + /// Dispatch blocks to be imported. + /// + /// The blocks may be imported immediately, get queued, delayed or grouped. + fn dispatch_import( + &self, + blocks: Vec, + events: Vec>, + is_syncing: bool, + ) -> Result<()>; +} + +/// Wrapper for the actual dispatchers. Allows to define one global type for +/// both dispatchers without changing the global variable when switching +/// the dispatcher type. It also allows for empty dispatchers, for use cases that +/// do not need block syncing for a specific parentchain type. +pub enum BlockImportDispatcher { + TriggeredDispatcher(Arc), + ImmediateDispatcher(Arc), + EmptyDispatcher, +} + +impl + BlockImportDispatcher +{ + pub fn new_triggered_dispatcher(triggered_dispatcher: Arc) -> Self { + BlockImportDispatcher::TriggeredDispatcher(triggered_dispatcher) + } + + pub fn new_immediate_dispatcher(immediate_dispatcher: Arc) -> Self { + BlockImportDispatcher::ImmediateDispatcher(immediate_dispatcher) + } + + pub fn new_empty_dispatcher() -> Self { + BlockImportDispatcher::EmptyDispatcher + } + + pub fn triggered_dispatcher(&self) -> Option> { + match self { + BlockImportDispatcher::TriggeredDispatcher(triggered_dispatcher) => + Some(triggered_dispatcher.clone()), + _ => None, + } + } + + pub fn immediate_dispatcher(&self) -> Option> { + match self { + BlockImportDispatcher::ImmediateDispatcher(immediate_dispatcher) => + Some(immediate_dispatcher.clone()), + _ => None, + } + } +} + +impl DispatchBlockImport + for BlockImportDispatcher +where + TriggeredDispatcher: DispatchBlockImport, + ImmediateDispatcher: DispatchBlockImport, +{ + fn dispatch_import( + &self, + blocks: Vec, + events: Vec>, + is_syncing: bool, + ) -> Result<()> { + match self { + BlockImportDispatcher::TriggeredDispatcher(dispatcher) => { + log::trace!("TRIGGERED DISPATCHER MATCH"); + dispatcher.dispatch_import(blocks, events, is_syncing) + }, + BlockImportDispatcher::ImmediateDispatcher(dispatcher) => { + log::trace!("IMMEDIATE DISPATCHER MATCH"); + dispatcher.dispatch_import(blocks, events, is_syncing) + }, + BlockImportDispatcher::EmptyDispatcher => { + log::trace!("EMPTY DISPATCHER DISPATCHER MATCH"); + Err(Error::NoDispatcherAssigned) + }, + } + } +} diff --git a/bitacross-worker/core/parentchain/block-import-dispatcher/src/trigger_parentchain_block_import_mock.rs b/bitacross-worker/core/parentchain/block-import-dispatcher/src/trigger_parentchain_block_import_mock.rs new file mode 100644 index 0000000000..a4953a4fbb --- /dev/null +++ b/bitacross-worker/core/parentchain/block-import-dispatcher/src/trigger_parentchain_block_import_mock.rs @@ -0,0 +1,102 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(feature = "sgx")] +use std::sync::SgxRwLock as RwLock; + +#[cfg(feature = "std")] +use std::sync::RwLock; + +use crate::{error::Result, triggered_dispatcher::TriggerParentchainBlockImport}; + +/// Mock for `TriggerParentchainBlockImport`, to be used in unit tests. +/// +/// Allows setting the latest imported block, which is returned upon calling +/// the import methods. +pub struct TriggerParentchainBlockImportMock { + latest_imported: Option, + import_has_been_called: RwLock, +} + +impl TriggerParentchainBlockImportMock { + pub fn with_latest_imported(mut self, maybe_block: Option) -> Self { + self.latest_imported = maybe_block; + self + } + + pub fn has_import_been_called(&self) -> bool { + let import_flag = self.import_has_been_called.read().unwrap(); + *import_flag + } +} + +impl Default for TriggerParentchainBlockImportMock { + fn default() -> Self { + TriggerParentchainBlockImportMock { + latest_imported: None, + import_has_been_called: RwLock::new(false), + } + } +} + +impl TriggerParentchainBlockImport + for TriggerParentchainBlockImportMock +where + SignedBlockType: Clone, +{ + type SignedBlockType = SignedBlockType; + + fn import_all(&self) -> Result> { + let mut import_flag = self.import_has_been_called.write().unwrap(); + *import_flag = true; + Ok(self.latest_imported.clone()) + } + + fn import_all_but_latest(&self) -> Result<()> { + let mut import_flag = self.import_has_been_called.write().unwrap(); + *import_flag = true; + Ok(()) + } + + fn import_until( + &self, + _predicate: impl Fn(&SignedBlockType) -> bool, + ) -> Result> { + let mut import_flag = self.import_has_been_called.write().unwrap(); + *import_flag = true; + Ok(self.latest_imported.clone()) + } + + fn peek( + &self, + predicate: impl Fn(&SignedBlockType) -> bool, + ) -> Result> { + match &self.latest_imported { + None => Ok(None), + Some(block) => { + if predicate(block) { + return Ok(Some(block.clone())) + } + Ok(None) + }, + } + } + + fn peek_latest(&self) -> Result> { + Ok(self.latest_imported.clone()) + } +} diff --git a/bitacross-worker/core/parentchain/block-import-dispatcher/src/triggered_dispatcher.rs b/bitacross-worker/core/parentchain/block-import-dispatcher/src/triggered_dispatcher.rs new file mode 100644 index 0000000000..77812331b8 --- /dev/null +++ b/bitacross-worker/core/parentchain/block-import-dispatcher/src/triggered_dispatcher.rs @@ -0,0 +1,374 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! A block import dispatcher that retains all blocks in a queue until import is triggered. + +use crate::{ + error::{Error, Result}, + DispatchBlockImport, +}; +use itc_parentchain_block_importer::ImportParentchainBlocks; +use itp_import_queue::{PeekQueue, PopFromQueue, PushToQueue}; +use log::trace; +use std::vec::Vec; + +pub type RawEventsPerBlock = Vec; + +/// Trait to specifically trigger the import of parentchain blocks. +pub trait TriggerParentchainBlockImport { + type SignedBlockType; + /// Trigger the import of all queued block, **including** the latest one. + /// + /// Returns the latest imported block (if any). + fn import_all(&self) -> Result>; + + /// Trigger import of all queued blocks, **except** the latest one. + fn import_all_but_latest(&self) -> Result<()>; + + /// Trigger import of all blocks up to **and including** a specific block. + /// + /// If no block in the queue matches, then no blocks will be imported. + /// Returns the latest imported block (if any). + fn import_until( + &self, + predicate: impl Fn(&Self::SignedBlockType) -> bool, + ) -> Result>; + + /// Search the import queue with a given predicate and return a reference + /// to the first element that matches the predicate. + fn peek( + &self, + predicate: impl Fn(&Self::SignedBlockType) -> bool, + ) -> Result>; + + /// Peek the latest block in the import queue. Returns None if queue is empty. + fn peek_latest(&self) -> Result>; +} + +/// Dispatcher for block imports that retains blocks until the import is triggered, using the +/// `TriggerParentchainBlockImport` trait implementation. +pub struct TriggeredDispatcher { + pub block_importer: BlockImporter, + import_queue: BlockImportQueue, + events_queue: EventsImportQueue, +} + +impl + TriggeredDispatcher +where + BlockImporter: ImportParentchainBlocks, + BlockImportQueue: PushToQueue + + PopFromQueue, + EventsImportQueue: PushToQueue + PopFromQueue, +{ + pub fn new( + block_importer: BlockImporter, + block_import_queue: BlockImportQueue, + events_import_queue: EventsImportQueue, + ) -> Self { + TriggeredDispatcher { + block_importer, + import_queue: block_import_queue, + events_queue: events_import_queue, + } + } +} + +impl + DispatchBlockImport + for TriggeredDispatcher +where + BlockImporter: ImportParentchainBlocks, + BlockImportQueue: PushToQueue + PopFromQueue, + EventsImportQueue: PushToQueue + PopFromQueue, +{ + fn dispatch_import( + &self, + blocks: Vec, + events: Vec, + is_syncing: bool, + ) -> Result<()> { + let parentchain_id = self.block_importer.parentchain_id(); + trace!( + "[{:?}] Triggered dispatcher received block(s) and event(s) ({}) ({})", + parentchain_id, + blocks.len(), + events.len() + ); + if is_syncing { + trace!( + "[{:?}] Triggered is in sync mode, immediately importing blocks and events", + parentchain_id + ); + self.block_importer + .import_parentchain_blocks(blocks, events) + .map_err(Error::BlockImport) + } else { + trace!("[{:?}] pushing blocks and events to import queues", parentchain_id); + self.events_queue.push_multiple(events).map_err(Error::ImportQueue)?; + self.import_queue.push_multiple(blocks).map_err(Error::ImportQueue) + } + } +} + +impl TriggerParentchainBlockImport + for TriggeredDispatcher +where + BlockImporter: ImportParentchainBlocks, + BlockImportQueue: PushToQueue + + PopFromQueue + + PeekQueue, + EventsImportQueue: PushToQueue + + PopFromQueue + + PeekQueue, +{ + type SignedBlockType = BlockImporter::SignedBlockType; + + fn import_all(&self) -> Result> { + let blocks_to_import = self.import_queue.pop_all().map_err(Error::ImportQueue)?; + let events_to_import = self.events_queue.pop_all().map_err(Error::ImportQueue)?; + + let latest_imported_block = blocks_to_import.last().map(|b| (*b).clone()); + let parentchain_id = self.block_importer.parentchain_id(); + trace!( + "[{:?}] Trigger import of all parentchain blocks and events in queue ({}) ({})", + parentchain_id, + blocks_to_import.len(), + events_to_import.len() + ); + + self.block_importer + .import_parentchain_blocks(blocks_to_import, events_to_import) + .map_err(Error::BlockImport)?; + + Ok(latest_imported_block) + } + + fn import_all_but_latest(&self) -> Result<()> { + let blocks_to_import = self.import_queue.pop_all_but_last().map_err(Error::ImportQueue)?; + let events_to_import = self.events_queue.pop_all_but_last().map_err(Error::ImportQueue)?; + let parentchain_id = self.block_importer.parentchain_id(); + trace!( + "[{:?}] Trigger import of all parentchain blocks and events, except the latest, from queue ({}) ({})", + parentchain_id, + blocks_to_import.len(), + events_to_import.len() + ); + + self.block_importer + .import_parentchain_blocks(blocks_to_import, events_to_import) + .map_err(Error::BlockImport) + } + + fn import_until( + &self, + predicate: impl Fn(&BlockImporter::SignedBlockType) -> bool, + ) -> Result> { + trace!("Import of parentchain blocks and events has been triggered"); + let blocks_to_import = + self.import_queue.pop_until(predicate).map_err(Error::ImportQueue)?; + + let events_to_import = self + .events_queue + .pop_from_front_until(blocks_to_import.len()) + .map_err(Error::ImportQueue)?; + + let latest_imported_block = blocks_to_import.last().map(|b| (*b).clone()); + let parentchain_id = self.block_importer.parentchain_id(); + trace!( + "[{:?}] Import of parentchain blocks and events has been triggered, importing {} blocks and {} events from queue", + parentchain_id, + blocks_to_import.len(), + events_to_import.len(), + ); + + self.block_importer + .import_parentchain_blocks(blocks_to_import, events_to_import) + .map_err(Error::BlockImport)?; + + Ok(latest_imported_block) + } + + fn peek( + &self, + predicate: impl Fn(&BlockImporter::SignedBlockType) -> bool, + ) -> Result> { + let parentchain_id = self.block_importer.parentchain_id(); + trace!( + "[{:?}] Peek find parentchain import queue (currently has {} elements)", + parentchain_id, + self.import_queue.peek_queue_size().unwrap_or(0) + ); + self.import_queue.peek_find(predicate).map_err(Error::ImportQueue) + } + + fn peek_latest(&self) -> Result> { + let parentchain_id = self.block_importer.parentchain_id(); + trace!( + "[{:?}] Peek latest parentchain import queue (currently has {} elements)", + parentchain_id, + self.import_queue.peek_queue_size().unwrap_or(0) + ); + self.import_queue.peek_last().map_err(Error::ImportQueue) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use itc_parentchain_block_importer::block_importer_mock::ParentchainBlockImporterMock; + use itp_import_queue::{ImportQueue, PopFromQueue}; + + type SignedBlockType = u32; + type TestBlockImporter = ParentchainBlockImporterMock; + type TestQueue = ImportQueue; + type TestEventsQueue = ImportQueue; + type TestDispatcher = TriggeredDispatcher; + + #[test] + fn dispatching_blocks_imports_none_if_not_triggered() { + let dispatcher = test_fixtures(); + + dispatcher + .dispatch_import( + vec![1, 2, 3, 4, 5], + vec![vec![1], vec![2], vec![3], vec![4], vec![5]], + false, + ) + .unwrap(); + + assert!(dispatcher.block_importer.get_all_imported_blocks().is_empty()); + assert_eq!(dispatcher.import_queue.pop_all().unwrap(), vec![1, 2, 3, 4, 5]); + assert_eq!( + dispatcher.events_queue.pop_all().unwrap(), + vec![vec![1], vec![2], vec![3], vec![4], vec![5]] + ); + } + + #[test] + fn dispatching_blocks_multiple_times_add_all_to_queue() { + let dispatcher = test_fixtures(); + + dispatcher + .dispatch_import( + vec![1, 2, 3, 4, 5], + vec![vec![1], vec![2], vec![3], vec![4], vec![5]], + false, + ) + .unwrap(); + dispatcher + .dispatch_import(vec![6, 7, 8], vec![vec![6], vec![7], vec![8]], false) + .unwrap(); + + assert!(dispatcher.block_importer.get_all_imported_blocks().is_empty()); + assert_eq!(dispatcher.import_queue.pop_all().unwrap(), vec![1, 2, 3, 4, 5, 6, 7, 8]); + assert_eq!( + dispatcher.events_queue.pop_all().unwrap(), + vec![vec![1], vec![2], vec![3], vec![4], vec![5], vec![6], vec![7], vec![8]] + ); + } + + #[test] + fn triggering_import_all_empties_queue() { + let dispatcher = test_fixtures(); + + dispatcher.dispatch_import(vec![1, 2, 3, 4, 5], vec![], false).unwrap(); + let latest_imported = dispatcher.import_all().unwrap().unwrap(); + + assert_eq!(latest_imported, 5); + assert_eq!(dispatcher.block_importer.get_all_imported_blocks(), vec![1, 2, 3, 4, 5]); + assert!(dispatcher.import_queue.is_empty().unwrap()); + } + + #[test] + fn triggering_import_all_on_empty_queue_imports_none() { + let dispatcher = test_fixtures(); + + dispatcher.dispatch_import(vec![], vec![], false).unwrap(); + let maybe_latest_imported = dispatcher.import_all().unwrap(); + + assert!(maybe_latest_imported.is_none()); + assert_eq!( + dispatcher.block_importer.get_all_imported_blocks(), + Vec::::default() + ); + assert!(dispatcher.import_queue.is_empty().unwrap()); + assert!(dispatcher.events_queue.is_empty().unwrap()); + } + + #[test] + fn triggering_import_until_leaves_remaining_in_queue() { + let dispatcher = test_fixtures(); + + dispatcher + .dispatch_import( + vec![1, 2, 3, 4, 5], + vec![vec![1], vec![2], vec![3], vec![4], vec![5]], + false, + ) + .unwrap(); + let latest_imported = + dispatcher.import_until(|i: &SignedBlockType| i == &4).unwrap().unwrap(); + + assert_eq!(latest_imported, 4); + assert_eq!(dispatcher.block_importer.get_all_imported_blocks(), vec![1, 2, 3, 4]); + assert_eq!(dispatcher.import_queue.pop_all().unwrap(), vec![5]); + assert_eq!(dispatcher.events_queue.pop_all().unwrap(), vec![vec![5]]); + } + + #[test] + fn triggering_import_until_with_no_match_imports_nothing() { + let dispatcher = test_fixtures(); + + dispatcher + .dispatch_import( + vec![1, 2, 3, 4, 5], + vec![vec![1], vec![2], vec![3], vec![4], vec![5]], + false, + ) + .unwrap(); + let maybe_latest_imported = dispatcher.import_until(|i: &SignedBlockType| i == &8).unwrap(); + + assert!(maybe_latest_imported.is_none()); + assert!(dispatcher.block_importer.get_all_imported_blocks().is_empty()); + assert_eq!(dispatcher.import_queue.pop_all().unwrap(), vec![1, 2, 3, 4, 5]); + assert_eq!( + dispatcher.events_queue.pop_all().unwrap(), + vec![vec![1], vec![2], vec![3], vec![4], vec![5]] + ); + } + + #[test] + fn trigger_import_all_but_latest_works() { + let dispatcher = test_fixtures(); + + dispatcher.dispatch_import(vec![1, 2, 3, 4, 5], vec![], false).unwrap(); + dispatcher.import_all_but_latest().unwrap(); + + assert_eq!(dispatcher.block_importer.get_all_imported_blocks(), vec![1, 2, 3, 4]); + assert_eq!(dispatcher.import_queue.pop_all().unwrap(), vec![5]); + } + + fn test_fixtures() -> TestDispatcher { + let events_import_queue = ImportQueue::::default(); + let import_queue = ImportQueue::::default(); + let block_importer = ParentchainBlockImporterMock::::default(); + let dispatcher = + TriggeredDispatcher::new(block_importer, import_queue, events_import_queue); + dispatcher + } +} diff --git a/bitacross-worker/core/parentchain/block-importer/Cargo.toml b/bitacross-worker/core/parentchain/block-importer/Cargo.toml new file mode 100644 index 0000000000..96f85eb7a6 --- /dev/null +++ b/bitacross-worker/core/parentchain/block-importer/Cargo.toml @@ -0,0 +1,69 @@ +[package] +name = "itc-parentchain-block-importer" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +# sgx dependencies +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } +sgx_types = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } + +# local dependencies +ita-stf = { path = "../../../app-libs/stf", default-features = false } +itc-parentchain-indirect-calls-executor = { path = "../indirect-calls-executor", default-features = false } +itc-parentchain-light-client = { path = "../light-client", default-features = false } +itp-enclave-metrics = { path = "../../../core-primitives/enclave-metrics", default-features = false } +itp-extrinsics-factory = { path = "../../../core-primitives/extrinsics-factory", default-features = false } +itp-stf-executor = { path = "../../../core-primitives/stf-executor", default-features = false } +itp-types = { path = "../../../core-primitives/types", default-features = false } + +# sgx enabled external libraries +thiserror_sgx = { package = "thiserror", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3", optional = true } + +# crates.io std-only compatible external libraries (make sure these versions match with the sgx-enabled ones above) +thiserror = { version = "1.0", optional = true } + +# crates.io no-std compatible libraries +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +log = { version = "0.4", default-features = false } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# litentry +itp-ocall-api = { path = "../../../core-primitives/ocall-api", default-features = false } + +[features] +default = ["std"] +std = [ + # local + "ita-stf/std", + "itc-parentchain-indirect-calls-executor/std", + "itc-parentchain-light-client/std", + "itp-enclave-metrics/std", + "itp-extrinsics-factory/std", + "itp-stf-executor/std", + "itp-types/std", + # no-std compatible libraries + "codec/std", + "log/std", + "sp-runtime/std", + # std compatible external + "thiserror", + "itp-ocall-api/std", +] +sgx = [ + # sgx + "sgx_tstd", + # local + "ita-stf/sgx", + "itc-parentchain-indirect-calls-executor/sgx", + "itc-parentchain-light-client/sgx", + "itp-enclave-metrics/sgx", + "itp-extrinsics-factory/sgx", + "itp-stf-executor/sgx", + # sgx enabled external libraries + "thiserror_sgx", +] + +# feature to export mock implementations, only to be used for dev-dependencies! +mocks = [] diff --git a/bitacross-worker/core/parentchain/block-importer/src/block_importer.rs b/bitacross-worker/core/parentchain/block-importer/src/block_importer.rs new file mode 100644 index 0000000000..6ffa524d49 --- /dev/null +++ b/bitacross-worker/core/parentchain/block-importer/src/block_importer.rs @@ -0,0 +1,190 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Imports parentchain blocks and executes any indirect calls found in the extrinsics. + +use crate::{error::Result, ImportParentchainBlocks}; +use ita_stf::ParentchainHeader; +use itc_parentchain_indirect_calls_executor::ExecuteIndirectCalls; +use itc_parentchain_light_client::{ + concurrent_access::ValidatorAccess, BlockNumberOps, ExtrinsicSender, Validator, +}; +use itp_enclave_metrics::EnclaveMetric; +use itp_extrinsics_factory::CreateExtrinsics; +use itp_ocall_api::EnclaveMetricsOCallApi; +use itp_stf_executor::traits::StfUpdateState; +use itp_types::{ + parentchain::{IdentifyParentchain, ParentchainId}, + OpaqueCall, H256, +}; +use log::*; +use sp_runtime::{ + generic::SignedBlock as SignedBlockG, + traits::{Block as ParentchainBlockTrait, NumberFor}, +}; +use std::{marker::PhantomData, sync::Arc, vec::Vec}; + +/// Parentchain block import implementation. +pub struct ParentchainBlockImporter< + ParentchainBlock, + ValidatorAccessor, + StfExecutor, + ExtrinsicsFactory, + IndirectCallsExecutor, + OCallApi, +> { + pub validator_accessor: Arc, + stf_executor: Arc, + extrinsics_factory: Arc, + pub indirect_calls_executor: Arc, + ocall_api: Arc, + _phantom: PhantomData, +} + +impl< + ParentchainBlock, + ValidatorAccessor, + StfExecutor, + ExtrinsicsFactory, + IndirectCallsExecutor, + OCallApi, + > + ParentchainBlockImporter< + ParentchainBlock, + ValidatorAccessor, + StfExecutor, + ExtrinsicsFactory, + IndirectCallsExecutor, + OCallApi, + > +{ + pub fn new( + validator_accessor: Arc, + stf_executor: Arc, + extrinsics_factory: Arc, + indirect_calls_executor: Arc, + ocall_api: Arc, + ) -> Self { + ParentchainBlockImporter { + validator_accessor, + stf_executor, + extrinsics_factory, + indirect_calls_executor, + ocall_api, + _phantom: Default::default(), + } + } +} + +impl< + ParentchainBlock, + ValidatorAccessor, + StfExecutor, + ExtrinsicsFactory, + IndirectCallsExecutor, + OcallApi, + > ImportParentchainBlocks + for ParentchainBlockImporter< + ParentchainBlock, + ValidatorAccessor, + StfExecutor, + ExtrinsicsFactory, + IndirectCallsExecutor, + OcallApi, + > where + ParentchainBlock: ParentchainBlockTrait, + NumberFor: BlockNumberOps, + ValidatorAccessor: ValidatorAccess + IdentifyParentchain, + StfExecutor: StfUpdateState, + ExtrinsicsFactory: CreateExtrinsics, + IndirectCallsExecutor: ExecuteIndirectCalls, + OcallApi: EnclaveMetricsOCallApi, +{ + type SignedBlockType = SignedBlockG; + + fn import_parentchain_blocks( + &self, + blocks_to_import: Vec, + events_to_import: Vec>, + ) -> Result<()> { + let mut calls = Vec::::new(); + let id = self.validator_accessor.parentchain_id(); + + debug!("[{:?}] Import blocks to light-client!", id); + for (signed_block, raw_events) in + blocks_to_import.into_iter().zip(events_to_import.into_iter()) + { + let started = std::time::Instant::now(); + if let Err(e) = self + .validator_accessor + .execute_mut_on_validator(|v| v.submit_block(&signed_block)) + { + error!("[{:?}] Header submission to light client failed: {:?}", id, e); + return Err(e.into()) + } + + let block = signed_block.block; + // Perform state updates. + if let Err(e) = self + .stf_executor + .update_states(block.header(), &self.validator_accessor.parentchain_id()) + { + error!("[{:?}] Error performing state updates upon block import", id); + return Err(e.into()) + } + + // Execute indirect calls that were found in the extrinsics of the block, + // incl. shielding and unshielding. + match self + .indirect_calls_executor + .execute_indirect_calls_in_extrinsics(&block, &raw_events) + { + Ok(executed_shielding_calls) => { + calls.push(executed_shielding_calls); + }, + Err(e) => error!("[{:?}] Error executing relevant extrinsics: {:?}", id, e), + }; + if let Err(e) = self + .ocall_api + .update_metric(EnclaveMetric::ParentchainBlockImportTime(started.elapsed())) + { + warn!("Failed to update metric for parentchain block import: {:?}", e); + }; + + info!( + "[{:?}] Successfully imported parentchain block (number: {}, hash: {})", + id, + block.header().number, + block.header().hash() + ); + } + + // Create extrinsics for all `unshielding` and `block processed` calls we've gathered. + let parentchain_extrinsics = + self.extrinsics_factory.create_extrinsics(calls.as_slice(), None)?; + + // Sending the extrinsic requires mut access because the validator caches the sent extrinsics internally. + self.validator_accessor + .execute_mut_on_validator(|v| v.send_extrinsics(parentchain_extrinsics))?; + + Ok(()) + } + + fn parentchain_id(&self) -> ParentchainId { + self.validator_accessor.parentchain_id() + } +} diff --git a/bitacross-worker/core/parentchain/block-importer/src/block_importer_mock.rs b/bitacross-worker/core/parentchain/block-importer/src/block_importer_mock.rs new file mode 100644 index 0000000000..aae92293e7 --- /dev/null +++ b/bitacross-worker/core/parentchain/block-importer/src/block_importer_mock.rs @@ -0,0 +1,65 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Block importer mock. + +use crate::{ + error::{Error, Result}, + ImportParentchainBlocks, +}; +use itp_types::parentchain::ParentchainId; +use std::{sync::RwLock, vec::Vec}; + +/// Mock implementation for the block importer. +/// +/// Just stores all the blocks that were sent to import internally. +#[derive(Default)] +pub struct ParentchainBlockImporterMock { + imported_blocks: RwLock>, +} + +impl ParentchainBlockImporterMock +where + SignedBlockT: Clone, +{ + pub fn get_all_imported_blocks(&self) -> Vec { + let imported_blocks_lock = self.imported_blocks.read().unwrap(); + (*imported_blocks_lock).clone() + } +} + +impl ImportParentchainBlocks for ParentchainBlockImporterMock +where + SignedBlockT: Clone, +{ + type SignedBlockType = SignedBlockT; + + fn import_parentchain_blocks( + &self, + blocks_to_import: Vec, + _events: Vec>, + ) -> Result<()> { + let mut imported_blocks_lock = self.imported_blocks.write().map_err(|e| { + Error::Other(format!("failed to acquire lock for imported blocks vec: {:?}", e).into()) + })?; + imported_blocks_lock.extend(blocks_to_import); + Ok(()) + } + fn parentchain_id(&self) -> ParentchainId { + ParentchainId::Litentry + } +} diff --git a/bitacross-worker/core/parentchain/block-importer/src/error.rs b/bitacross-worker/core/parentchain/block-importer/src/error.rs new file mode 100644 index 0000000000..856aa84ef2 --- /dev/null +++ b/bitacross-worker/core/parentchain/block-importer/src/error.rs @@ -0,0 +1,51 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use sgx_types::sgx_status_t; +use std::{boxed::Box, format}; + +pub type Result = core::result::Result; + +/// Parentchain block importer error. +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("SGX error, status: {0}")] + Sgx(sgx_status_t), + #[error("Extrinsics factory error: {0}")] + ExtrinsicsFactory(#[from] itp_extrinsics_factory::error::Error), + #[error("STF execution error: {0}")] + StfExecution(#[from] itp_stf_executor::error::Error), + #[error("Light-client error: {0}")] + LightClient(#[from] itc_parentchain_light_client::error::Error), + #[error(transparent)] + Other(#[from] Box), +} + +impl From for Error { + fn from(sgx_status: sgx_status_t) -> Self { + Self::Sgx(sgx_status) + } +} + +impl From for Error { + fn from(e: codec::Error) -> Self { + Self::Other(format!("{:?}", e).into()) + } +} diff --git a/bitacross-worker/core/parentchain/block-importer/src/lib.rs b/bitacross-worker/core/parentchain/block-importer/src/lib.rs new file mode 100644 index 0000000000..3f2fd695bc --- /dev/null +++ b/bitacross-worker/core/parentchain/block-importer/src/lib.rs @@ -0,0 +1,61 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +//! Parentchain block importing logic. +#![feature(trait_alias)] +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +// re-export module to properly feature gate sgx and regular std environment +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod sgx_reexport_prelude { + pub use thiserror_sgx as thiserror; +} + +pub mod block_importer; +pub mod error; + +#[cfg(feature = "mocks")] +pub mod block_importer_mock; + +pub use block_importer::*; + +use error::Result; +use itp_types::parentchain::ParentchainId; +use std::vec::Vec; + +/// Block import from the parentchain. +pub trait ImportParentchainBlocks { + type SignedBlockType: Clone; + + /// Import parentchain blocks to the light-client (validator): + /// * Scans the blocks for relevant extrinsics + /// * Validates and execute those extrinsics, mutating state + /// * Includes block headers into the light client + /// * Sends `PROCESSED_PARENTCHAIN_BLOCK` extrinsics that include the merkle root of all processed calls + fn import_parentchain_blocks( + &self, + blocks_to_import: Vec, + events_to_import: Vec>, + ) -> Result<()>; + + fn parentchain_id(&self) -> ParentchainId; +} diff --git a/bitacross-worker/core/parentchain/indirect-calls-executor/Cargo.toml b/bitacross-worker/core/parentchain/indirect-calls-executor/Cargo.toml new file mode 100644 index 0000000000..e8e018d334 --- /dev/null +++ b/bitacross-worker/core/parentchain/indirect-calls-executor/Cargo.toml @@ -0,0 +1,94 @@ +[package] +name = "itc-parentchain-indirect-calls-executor" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +# sgx dependencies +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } +sgx_types = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } + +# local dependencies +itp-api-client-types = { path = "../../../core-primitives/node-api/api-client-types", default-features = false } +itp-node-api = { path = "../../../core-primitives/node-api", default-features = false } +itp-sgx-crypto = { path = "../../../core-primitives/sgx/crypto", default-features = false } +itp-sgx-runtime-primitives = { path = "../../../core-primitives/sgx-runtime-primitives", default-features = false } +itp-stf-executor = { path = "../../../core-primitives/stf-executor", default-features = false } +itp-stf-primitives = { path = "../../../core-primitives/stf-primitives", default-features = false } +itp-test = { path = "../../../core-primitives/test", default-features = false } +itp-top-pool-author = { path = "../../../core-primitives/top-pool-author", default-features = false } +itp-types = { path = "../../../core-primitives/types", default-features = false } + +# sgx enabled external libraries +futures_sgx = { package = "futures", git = "https://github.com/mesalock-linux/futures-rs-sgx", optional = true } +thiserror_sgx = { package = "thiserror", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3", optional = true } + +# std compatible external libraries (make sure these versions match with the sgx-enabled ones above) +futures = { version = "0.3.8", optional = true } +thiserror = { version = "1.0", optional = true } + +# no-std compatible libraries +bs58 = { version = "0.4.0", default-features = false, features = ["alloc"] } +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +log = { version = "0.4", default-features = false } + +# substrate dep +binary-merkle-tree = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-core = { default-features = false, features = ["full_crypto"], git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# litentry +itp-utils = { path = "../../../core-primitives/utils", default-features = false } +lc-scheduled-enclave = { path = "../../../litentry/core/scheduled-enclave", default-features = false, optional = true } +litentry-primitives = { path = "../../../litentry/primitives", default-features = false } +parachain-core-primitives = { package = "core-primitives", path = "../../../../primitives/core", default-features = false } +sp-std = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +[dev-dependencies] +env_logger = "0.9.0" +itp-node-api = { path = "../../../core-primitives/node-api", features = ["mocks"] } +itp-sgx-crypto = { path = "../../../core-primitives/sgx/crypto", features = ["mocks"] } +itp-stf-executor = { path = "../../../core-primitives/stf-executor", features = ["mocks"] } +itp-test = { path = "../../../core-primitives/test" } +itp-top-pool-author = { path = "../../../core-primitives/top-pool-author", features = ["mocks"] } +itc-parentchain-test = { path = "../../../core/parentchain/test" } + +[features] +default = ["std"] +std = [ + "bs58/std", + "codec/std", + "futures", + "itp-node-api/std", + "itp-sgx-crypto/std", + "itp-stf-executor/std", + "itp-top-pool-author/std", + "itp-api-client-types/std", + "itp-test/std", + "itp-types/std", + "itp-sgx-runtime-primitives/std", + "log/std", + #substrate + "binary-merkle-tree/std", + "sp-core/std", + "sp-runtime/std", + "thiserror", + # litentry + "litentry-primitives/std", + "itp-utils/std", + "lc-scheduled-enclave/std", +] +sgx = [ + "sgx_tstd", + "futures_sgx", + "itp-node-api/sgx", + "itp-sgx-crypto/sgx", + "itp-stf-executor/sgx", + "itp-top-pool-author/sgx", + "itp-test/sgx", + "thiserror_sgx", + # litentry + "litentry-primitives/sgx", + "lc-scheduled-enclave/sgx", +] diff --git a/bitacross-worker/core/parentchain/indirect-calls-executor/src/error.rs b/bitacross-worker/core/parentchain/indirect-calls-executor/src/error.rs new file mode 100644 index 0000000000..2973f984f8 --- /dev/null +++ b/bitacross-worker/core/parentchain/indirect-calls-executor/src/error.rs @@ -0,0 +1,111 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; +pub use litentry_primitives::{ErrorDetail, IMPError, VCMPError}; + +use itp_types::parentchain::ParentchainError; +use lc_scheduled_enclave::error::Error as ScheduledEnclaveError; +use sgx_types::sgx_status_t; +use sp_runtime::traits::LookupError; +use std::{boxed::Box, format}; + +pub type Result = core::result::Result; + +/// Indirect calls execution error. +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("SGX error, status: {0}")] + Sgx(sgx_status_t), + #[error("STF execution error: {0}")] + StfExecution(#[from] itp_stf_executor::error::Error), + #[error("Node Metadata error: {0:?}")] + NodeMetadata(itp_node_api::metadata::Error), + #[error("Node metadata provider error: {0:?}")] + NodeMetadataProvider(#[from] itp_node_api::metadata::provider::Error), + #[error("Crypto error: {0}")] + Crypto(itp_sgx_crypto::Error), + #[error(transparent)] + Other(#[from] Box), + #[error("AccountId lookup error")] + AccountIdLookup, + #[error("convert parent chain block number error")] + ConvertParentchainBlockNumber, + #[error("IMP handling error: {0:?}")] + IMPHandlingError(IMPError), + #[error("VCMP handling error: {0:?}")] + VCMPHandlingError(VCMPError), + #[error("BatchAll handling error")] + BatchAllHandlingError, + #[error("ScheduledEnclave Error: {0:?}")] + ImportScheduledEnclave(ScheduledEnclaveError), +} + +impl From for Error { + fn from(e: ParentchainError) -> Self { + Self::Other(format!("{:?}", e).into()) + } +} + +impl From for Error { + fn from(sgx_status: sgx_status_t) -> Self { + Self::Sgx(sgx_status) + } +} + +impl From for Error { + fn from(e: itp_sgx_crypto::Error) -> Self { + Self::Crypto(e) + } +} + +impl From for Error { + fn from(e: codec::Error) -> Self { + Self::Other(format!("{:?}", e).into()) + } +} + +impl From for Error { + fn from(e: itp_node_api::metadata::Error) -> Self { + Self::NodeMetadata(e) + } +} + +impl From for Error { + fn from(_: LookupError) -> Self { + Self::AccountIdLookup + } +} + +impl From for Error { + fn from(e: IMPError) -> Self { + Self::IMPHandlingError(e) + } +} + +impl From for Error { + fn from(e: VCMPError) -> Self { + Self::VCMPHandlingError(e) + } +} + +impl From for Error { + fn from(e: ScheduledEnclaveError) -> Self { + Self::ImportScheduledEnclave(e) + } +} diff --git a/bitacross-worker/core/parentchain/indirect-calls-executor/src/event_filter.rs b/bitacross-worker/core/parentchain/indirect-calls-executor/src/event_filter.rs new file mode 100644 index 0000000000..ffb9882f58 --- /dev/null +++ b/bitacross-worker/core/parentchain/indirect-calls-executor/src/event_filter.rs @@ -0,0 +1,33 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +//! Various way to filter Parentchain events + +use crate::error::Error; + +use itp_stf_primitives::error::StfError; + +use std::format; + +impl From for Error { + fn from(a: StfError) -> Self { + Error::Other(format!("Error when shielding for privacy sidechain {:?}", a).into()) + } +} + +pub trait ToEvents { + fn to_events(&self) -> &E; +} diff --git a/bitacross-worker/core/parentchain/indirect-calls-executor/src/executor.rs b/bitacross-worker/core/parentchain/indirect-calls-executor/src/executor.rs new file mode 100644 index 0000000000..0c2dbcf74c --- /dev/null +++ b/bitacross-worker/core/parentchain/indirect-calls-executor/src/executor.rs @@ -0,0 +1,519 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +//! Execute indirect calls, i.e. extrinsics extracted from parentchain blocks + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use crate::{ + error::{Error, Result}, + filter_metadata::{EventsFromMetadata, FilterIntoDataFrom}, + traits::{ExecuteIndirectCalls, IndirectDispatch}, +}; +use alloc::format; +use binary_merkle_tree::merkle_root; +use codec::{Decode, Encode}; +use core::marker::PhantomData; +use itp_node_api::metadata::{ + pallet_teerex::TeerexCallIndexes, provider::AccessNodeMetadata, NodeMetadataTrait, +}; +use itp_sgx_crypto::{key_repository::AccessKey, ShieldingCryptoDecrypt, ShieldingCryptoEncrypt}; +use itp_stf_executor::traits::{StfEnclaveSigning, StfShardVaultQuery}; +use itp_stf_primitives::{ + traits::{IndirectExecutor, TrustedCallSigning, TrustedCallVerification}, + types::AccountId, +}; +use itp_top_pool_author::traits::AuthorApi; +use itp_types::{ + parentchain::{ExtrinsicStatus, FilterEvents, HandleParentchainEvents}, + OpaqueCall, RsaRequest, ShardIdentifier, H256, +}; +use log::*; +use sp_core::blake2_256; +use sp_runtime::traits::{Block as ParentchainBlockTrait, Header, Keccak256}; +use std::{fmt::Debug, sync::Arc, vec::Vec}; + +pub struct IndirectCallsExecutor< + ShieldingKeyRepository, + StfEnclaveSigner, + TopPoolAuthor, + NodeMetadataProvider, + IndirectCallsFilter, + EventCreator, + ParentchainEventHandler, + TCS, + G, +> { + pub(crate) shielding_key_repo: Arc, + pub stf_enclave_signer: Arc, + pub(crate) top_pool_author: Arc, + pub(crate) node_meta_data_provider: Arc, + _phantom: PhantomData<(IndirectCallsFilter, EventCreator, ParentchainEventHandler, TCS, G)>, +} +impl< + ShieldingKeyRepository, + StfEnclaveSigner, + TopPoolAuthor, + NodeMetadataProvider, + IndirectCallsFilter, + EventCreator, + ParentchainEventHandler, + TCS, + G, + > + IndirectCallsExecutor< + ShieldingKeyRepository, + StfEnclaveSigner, + TopPoolAuthor, + NodeMetadataProvider, + IndirectCallsFilter, + EventCreator, + ParentchainEventHandler, + TCS, + G, + > +{ + pub fn new( + shielding_key_repo: Arc, + stf_enclave_signer: Arc, + top_pool_author: Arc, + node_meta_data_provider: Arc, + ) -> Self { + IndirectCallsExecutor { + shielding_key_repo, + stf_enclave_signer, + top_pool_author, + node_meta_data_provider, + _phantom: Default::default(), + } + } +} + +impl< + ShieldingKeyRepository, + StfEnclaveSigner, + TopPoolAuthor, + NodeMetadataProvider, + FilterIndirectCalls, + EventCreator, + ParentchainEventHandler, + TCS, + G, + > ExecuteIndirectCalls + for IndirectCallsExecutor< + ShieldingKeyRepository, + StfEnclaveSigner, + TopPoolAuthor, + NodeMetadataProvider, + FilterIndirectCalls, + EventCreator, + ParentchainEventHandler, + TCS, + G, + > where + ShieldingKeyRepository: AccessKey, + ::KeyType: ShieldingCryptoDecrypt + + ShieldingCryptoEncrypt, + StfEnclaveSigner: StfEnclaveSigning + StfShardVaultQuery, + TopPoolAuthor: AuthorApi + Send + Sync + 'static, + NodeMetadataProvider: AccessNodeMetadata, + FilterIndirectCalls: FilterIntoDataFrom, + NodeMetadataProvider::MetadataType: NodeMetadataTrait + Clone, + FilterIndirectCalls::Output: IndirectDispatch + Encode + Debug, + EventCreator: EventsFromMetadata, + ParentchainEventHandler: HandleParentchainEvents, + TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, + G: PartialEq + Encode + Decode + Debug + Clone + Send + Sync, +{ + fn execute_indirect_calls_in_extrinsics( + &self, + block: &ParentchainBlock, + events: &[u8], + ) -> Result + where + ParentchainBlock: ParentchainBlockTrait, + { + let block_number = *block.header().number(); + let block_hash = block.hash(); + + trace!("Scanning block {:?} for relevant xt", block_number); + let mut executed_calls = Vec::::new(); + + let events = self + .node_meta_data_provider + .get_from_metadata(|metadata| { + EventCreator::create_from_metadata(metadata.clone(), block_hash, events) + })? + .ok_or_else(|| Error::Other("Could not create events from metadata".into()))?; + + let xt_statuses = events.get_extrinsic_statuses().map_err(|e| { + Error::Other(format!("Error when shielding for privacy sidechain {:?}", e).into()) + })?; + trace!("xt_statuses:: {:?}", xt_statuses); + + let shard = self.get_default_shard(); + if let Ok(vault) = self.stf_enclave_signer.get_shard_vault(&shard) { + ParentchainEventHandler::handle_events(self, events, &vault)?; + } + + // This would be catastrophic but should never happen + if xt_statuses.len() != block.extrinsics().len() { + return Err(Error::Other("Extrinsic Status and Extrinsic count not equal".into())) + } + + for (xt_opaque, xt_status) in block.extrinsics().iter().zip(xt_statuses.iter()) { + let encoded_xt_opaque = xt_opaque.encode(); + + let maybe_call = self.node_meta_data_provider.get_from_metadata(|metadata| { + FilterIndirectCalls::filter_into_from_metadata(&encoded_xt_opaque, metadata) + })?; + + let call = match maybe_call { + Some(c) => c, + None => continue, + }; + + if let ExtrinsicStatus::Failed = xt_status { + warn!("Parentchain Extrinsic Failed, {:?} wont be dispatched", call); + continue + } + + if let Err(e) = call.dispatch(self, ()) { + warn!("Error executing the indirect call: {:?}. Error {:?}", call, e); + } else { + executed_calls.push(hash_of(&call)); + } + } + debug!("successfully processed {} indirect invocations", executed_calls.len()); + // Include a processed parentchain block confirmation for each block. + self.create_processed_parentchain_block_call::( + block_hash, + executed_calls, + block_number, + ) + } + + fn create_processed_parentchain_block_call( + &self, + block_hash: H256, + extrinsics: Vec, + block_number: <::Header as Header>::Number, + ) -> Result + where + ParentchainBlock: ParentchainBlockTrait, + { + let call = self.node_meta_data_provider.get_from_metadata(|meta_data| { + meta_data.confirm_processed_parentchain_block_call_indexes() + })??; + let root: H256 = merkle_root::(extrinsics); + trace!("prepared confirm_processed_parentchain_block() call for block {:?} with index {:?} and merkle root {}", block_number, call, root); + // Litentry: we don't include `shard` in the extrinsic parameter to be backwards compatible, + // however, we should not forget it in case we need it later + Ok(OpaqueCall::from_tuple(&(call, block_hash, block_number, root))) + } +} + +impl< + ShieldingKeyRepository, + StfEnclaveSigner, + TopPoolAuthor, + NodeMetadataProvider, + FilterIndirectCalls, + EventFilter, + PrivacySidechain, + TCS, + G, + > IndirectExecutor + for IndirectCallsExecutor< + ShieldingKeyRepository, + StfEnclaveSigner, + TopPoolAuthor, + NodeMetadataProvider, + FilterIndirectCalls, + EventFilter, + PrivacySidechain, + TCS, + G, + > where + ShieldingKeyRepository: AccessKey, + ::KeyType: ShieldingCryptoDecrypt + + ShieldingCryptoEncrypt, + StfEnclaveSigner: StfEnclaveSigning + StfShardVaultQuery, + TopPoolAuthor: AuthorApi + Send + Sync + 'static, + TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, + G: PartialEq + Encode + Decode + Debug + Clone + Send + Sync, +{ + fn submit_trusted_call(&self, shard: ShardIdentifier, encrypted_trusted_call: Vec) { + if let Err(e) = futures::executor::block_on( + self.top_pool_author.submit_top(RsaRequest::new(shard, encrypted_trusted_call)), + ) { + error!("Error adding indirect trusted call to TOP pool: {:?}", e); + } + } + + fn decrypt(&self, encrypted: &[u8]) -> Result> { + let key = self.shielding_key_repo.retrieve_key()?; + Ok(key.decrypt(encrypted)?) + } + + fn encrypt(&self, value: &[u8]) -> Result> { + let key = self.shielding_key_repo.retrieve_key()?; + Ok(key.encrypt(value)?) + } + + fn get_enclave_account(&self) -> Result { + Ok(self.stf_enclave_signer.get_enclave_account()?) + } + + fn get_default_shard(&self) -> ShardIdentifier { + self.top_pool_author.list_handled_shards().first().copied().unwrap_or_default() + } + + fn sign_call_with_self>( + &self, + trusted_call: &TC, + shard: &ShardIdentifier, + ) -> Result { + Ok(self.stf_enclave_signer.sign_call_with_self(trusted_call, shard)?) + } +} + +pub fn hash_of(xt: &T) -> H256 { + blake2_256(&xt.encode()).into() +} + +#[cfg(test)] +mod test { + use super::*; + use crate::mock::*; + use codec::{Decode, Encode}; + use itc_parentchain_test::ParentchainBlockBuilder; + use itp_node_api::{ + api_client::{ + ExtrinsicParams, ParentchainAdditionalParams, ParentchainExtrinsicParams, + ParentchainUncheckedExtrinsic, + }, + metadata::{metadata_mocks::NodeMetadataMock, provider::NodeMetadataRepository}, + }; + use itp_sgx_crypto::mocks::KeyRepositoryMock; + use itp_stf_executor::mocks::StfEnclaveSignerMock; + use itp_stf_primitives::{ + traits::TrustedCallVerification, + types::{AccountId, TrustedOperation}, + }; + use itp_test::mock::{ + shielding_crypto_mock::ShieldingCryptoMock, + stf_mock::{GetterMock, TrustedCallSignedMock}, + }; + use itp_top_pool_author::mocks::AuthorApiMock; + use itp_types::{Block, CallWorkerFn, RsaRequest, ShardIdentifier, ShieldFundsFn}; + use sp_core::{ed25519, Pair}; + use sp_runtime::{MultiAddress, MultiSignature, OpaqueExtrinsic}; + use std::assert_matches::assert_matches; + + type TestShieldingKeyRepo = KeyRepositoryMock; + type TestStfEnclaveSigner = StfEnclaveSignerMock; + type TestTopPoolAuthor = AuthorApiMock; + type TestNodeMetadataRepository = NodeMetadataRepository; + type TestIndirectCallExecutor = IndirectCallsExecutor< + TestShieldingKeyRepo, + TestStfEnclaveSigner, + TestTopPoolAuthor, + TestNodeMetadataRepository, + MockExtrinsicFilter, + TestEventCreator, + MockParentchainEventHandler, + TrustedCallSignedMock, + GetterMock, + >; + + type Seed = [u8; 32]; + + const TEST_SEED: Seed = *b"12345678901234567890123456789012"; + + #[test] + fn indirect_call_can_be_added_to_pool_successfully() { + let _ = env_logger::builder().is_test(true).try_init(); + + let (indirect_calls_executor, top_pool_author, _) = + test_fixtures([0u8; 32], NodeMetadataMock::new()); + + let opaque_extrinsic = + OpaqueExtrinsic::from_bytes(invoke_unchecked_extrinsic().encode().as_slice()).unwrap(); + + let parentchain_block = ParentchainBlockBuilder::default() + .with_extrinsics(vec![opaque_extrinsic]) + .build(); + + indirect_calls_executor + .execute_indirect_calls_in_extrinsics(&parentchain_block, &Vec::new()) + .unwrap(); + + assert_eq!(1, top_pool_author.pending_tops(shard_id()).unwrap().len()); + } + + #[test] + fn shielding_call_can_be_added_to_pool_successfully() { + let _ = env_logger::builder().is_test(true).try_init(); + + let mr_enclave = [33u8; 32]; + let (indirect_calls_executor, top_pool_author, shielding_key_repo) = + test_fixtures(mr_enclave.clone(), NodeMetadataMock::new()); + let shielding_key = shielding_key_repo.retrieve_key().unwrap(); + + let opaque_extrinsic = OpaqueExtrinsic::from_bytes( + shield_funds_unchecked_extrinsic(&shielding_key).encode().as_slice(), + ) + .unwrap(); + + let parentchain_block = ParentchainBlockBuilder::default() + .with_extrinsics(vec![opaque_extrinsic]) + .build(); + + indirect_calls_executor + .execute_indirect_calls_in_extrinsics(&parentchain_block, &Vec::new()) + .unwrap(); + + assert_eq!(1, top_pool_author.pending_tops(shard_id()).unwrap().len()); + let submitted_extrinsic = + top_pool_author.pending_tops(shard_id()).unwrap().first().cloned().unwrap(); + let decrypted_extrinsic = shielding_key.decrypt(&submitted_extrinsic).unwrap(); + let decoded_operation = TrustedOperation::::decode( + &mut decrypted_extrinsic.as_slice(), + ) + .unwrap(); + assert_matches!(decoded_operation, TrustedOperation::indirect_call(_)); + let trusted_call_signed = decoded_operation.to_call().unwrap(); + assert!(trusted_call_signed.verify_signature(&mr_enclave, &shard_id())); + } + + #[test] + fn ensure_empty_extrinsic_vec_triggers_zero_filled_merkle_root() { + // given + let dummy_metadata = NodeMetadataMock::new(); + let (indirect_calls_executor, _, _) = test_fixtures([38u8; 32], dummy_metadata.clone()); + + let block_hash = H256::from([1; 32]); + let extrinsics = Vec::new(); + let confirm_processed_parentchain_block_indexes = + dummy_metadata.confirm_processed_parentchain_block_call_indexes().unwrap(); + let expected_call = + (confirm_processed_parentchain_block_indexes, block_hash, 1u32, H256::default()) + .encode(); + + // when + let call = indirect_calls_executor + .create_processed_parentchain_block_call::(block_hash, extrinsics, 1u32) + .unwrap(); + + // then + assert_eq!(call.0, expected_call); + } + + #[test] + fn ensure_non_empty_extrinsic_vec_triggers_non_zero_merkle_root() { + // given + let dummy_metadata = NodeMetadataMock::new(); + let (indirect_calls_executor, _, _) = test_fixtures([39u8; 32], dummy_metadata.clone()); + + let block_hash = H256::from([1; 32]); + let extrinsics = vec![H256::from([4; 32]), H256::from([9; 32])]; + let confirm_processed_parentchain_block_indexes = + dummy_metadata.confirm_processed_parentchain_block_call_indexes().unwrap(); + + let zero_root_call = + (confirm_processed_parentchain_block_indexes, block_hash, 1u32, H256::default()) + .encode(); + + // when + let call = indirect_calls_executor + .create_processed_parentchain_block_call::(block_hash, extrinsics, 1u32) + .unwrap(); + + // then + assert_ne!(call.0, zero_root_call); + } + + fn shield_funds_unchecked_extrinsic( + shielding_key: &ShieldingCryptoMock, + ) -> ParentchainUncheckedExtrinsic { + let target_account = shielding_key.encrypt(&AccountId::new([2u8; 32]).encode()).unwrap(); + let dummy_metadata = NodeMetadataMock::new(); + + let shield_funds_indexes = dummy_metadata.shield_funds_call_indexes().unwrap(); + ParentchainUncheckedExtrinsic::::new_signed( + (shield_funds_indexes, target_account, 1000u128, shard_id()), + MultiAddress::Address32([1u8; 32]), + MultiSignature::Ed25519(default_signature()), + default_extrinsic_params().signed_extra(), + ) + } + + fn invoke_unchecked_extrinsic() -> ParentchainUncheckedExtrinsic { + let request = RsaRequest::new(shard_id(), vec![1u8, 2u8]); + let dummy_metadata = NodeMetadataMock::new(); + let call_worker_indexes = dummy_metadata.invoke_call_indexes().unwrap(); + + ParentchainUncheckedExtrinsic::::new_signed( + (call_worker_indexes, request), + MultiAddress::Address32([1u8; 32]), + MultiSignature::Ed25519(default_signature()), + default_extrinsic_params().signed_extra(), + ) + } + + fn default_signature() -> ed25519::Signature { + signer().sign(&[0u8]) + } + + fn signer() -> ed25519::Pair { + ed25519::Pair::from_seed(&TEST_SEED) + } + + fn shard_id() -> ShardIdentifier { + ShardIdentifier::default() + } + + fn default_extrinsic_params() -> ParentchainExtrinsicParams { + ParentchainExtrinsicParams::new( + 0, + 0, + 0, + H256::default(), + ParentchainAdditionalParams::default(), + ) + } + + fn test_fixtures( + mr_enclave: [u8; 32], + metadata: NodeMetadataMock, + ) -> (TestIndirectCallExecutor, Arc, Arc) { + let shielding_key_repo = Arc::new(TestShieldingKeyRepo::default()); + let stf_enclave_signer = Arc::new(TestStfEnclaveSigner::new(mr_enclave)); + let top_pool_author = Arc::new(TestTopPoolAuthor::default()); + let node_metadata_repo = Arc::new(NodeMetadataRepository::new(metadata)); + + let executor = IndirectCallsExecutor::new( + shielding_key_repo.clone(), + stf_enclave_signer, + top_pool_author.clone(), + node_metadata_repo, + ); + + (executor, top_pool_author, shielding_key_repo) + } +} diff --git a/bitacross-worker/core/parentchain/indirect-calls-executor/src/filter_metadata.rs b/bitacross-worker/core/parentchain/indirect-calls-executor/src/filter_metadata.rs new file mode 100644 index 0000000000..22abc50bb3 --- /dev/null +++ b/bitacross-worker/core/parentchain/indirect-calls-executor/src/filter_metadata.rs @@ -0,0 +1,112 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{error::Result, IndirectDispatch}; +use codec::{Decode, Encode}; +use core::marker::PhantomData; +use itp_api_client_types::{Events, Metadata}; +use itp_node_api::metadata::NodeMetadata; +use itp_stf_primitives::traits::IndirectExecutor; +use itp_types::{parentchain::FilterEvents, H256}; + +pub trait EventsFromMetadata { + type Output: FilterEvents; + + fn create_from_metadata( + metadata: NodeMetadata, + block_hash: H256, + events: &[u8], + ) -> Option; +} + +pub struct EventCreator { + _phantom: PhantomData, +} + +impl + Clone, FilterableEvents> EventsFromMetadata + for EventCreator +where + FilterableEvents: From> + FilterEvents, +{ + type Output = FilterableEvents; + + fn create_from_metadata( + metadata: NodeMetadata, + block_hash: H256, + events: &[u8], + ) -> Option { + let raw_metadata: Metadata = metadata.try_into().ok()?; + Some(Events::::new(raw_metadata, block_hash, events.to_vec()).into()) + } +} + +/// Trait to filter an indirect call and decode into it, where the decoding +/// is based on the metadata provided. +pub trait FilterIntoDataFrom { + /// Type to decode into. + type Output; + + /// Knows how to parse the parentchain metadata. + type ParseParentchainMetadata; + + /// Filters some bytes and returns `Some(Self::Output)` if the filter matches some criteria. + fn filter_into_from_metadata( + encoded_data: &[u8], + metadata: &NodeMetadata, + ) -> Option; +} + +/// Indirect calls filter denying all indirect calls. +pub struct DenyAll; + +mod seal { + use super::*; + use crate::Error; + use core::fmt::Debug; + use itp_stf_primitives::traits::TrustedCallVerification; + + /// Stub struct for the `DenyAll` filter that never executes anything. + #[derive(Debug, Encode)] + pub struct CantExecute; + + impl FilterIntoDataFrom for DenyAll { + type Output = CantExecute; + type ParseParentchainMetadata = (); + + fn filter_into_from_metadata(_: &[u8], _: &NodeMetadata) -> Option { + None + } + } + + impl, TCS> IndirectDispatch for CantExecute + where + TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, + { + type Args = (); + fn dispatch(&self, _: &Executor, _args: Self::Args) -> Result<()> { + // We should never get here because `CantExecute` is in a private module and the trait + // implementation is sealed and always returns `None` instead of a `CantExecute` instance. + // Regardless, we never want the enclave to panic, this is why we take this extra safety + // measure. + log::warn!( + "Executed indirect dispatch for 'CantExecute'\ + this means there is some logic error." + ); + Ok(()) + } + } +} diff --git a/bitacross-worker/core/parentchain/indirect-calls-executor/src/lib.rs b/bitacross-worker/core/parentchain/indirect-calls-executor/src/lib.rs new file mode 100644 index 0000000000..199ee448d6 --- /dev/null +++ b/bitacross-worker/core/parentchain/indirect-calls-executor/src/lib.rs @@ -0,0 +1,52 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +//! Execute indirect calls, i.e. extrinsics extracted from parentchain blocks. +//! +//! The core struct of this crate is the [IndirectCallsExecutor] executor. It scans parentchain +//! blocks for relevant extrinsics, derives an indirect call for those and dispatches the +//! indirect call. + +#![feature(trait_alias)] +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(test, feature(assert_matches))] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +extern crate alloc; + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +// re-export module to properly feature gate sgx and regular std environment +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod sgx_reexport_prelude { + pub use futures_sgx as futures; + pub use thiserror_sgx as thiserror; +} + +mod executor; +pub mod mock; +pub mod traits; + +pub mod error; +pub mod event_filter; +pub mod filter_metadata; + +pub use error::{Error, Result}; +pub use executor::{hash_of, IndirectCallsExecutor}; +pub use traits::{ExecuteIndirectCalls, IndirectDispatch}; diff --git a/bitacross-worker/core/parentchain/indirect-calls-executor/src/mock.rs b/bitacross-worker/core/parentchain/indirect-calls-executor/src/mock.rs new file mode 100644 index 0000000000..38189f44d8 --- /dev/null +++ b/bitacross-worker/core/parentchain/indirect-calls-executor/src/mock.rs @@ -0,0 +1,247 @@ +use crate::{ + error::{Error, Result as ICResult}, + filter_metadata::{EventsFromMetadata, FilterIntoDataFrom}, + IndirectDispatch, +}; +use codec::{Decode, Encode}; +use core::marker::PhantomData; +use litentry_primitives::DecryptableRequest; + +use itp_node_api::{ + api_client::{CallIndex, PairSignature, UncheckedExtrinsicV4}, + metadata::NodeMetadataTrait, +}; +use itp_sgx_runtime_primitives::types::{AccountId, Balance}; +use itp_stf_primitives::{traits::IndirectExecutor, types::Signature}; +use itp_test::mock::stf_mock::{GetterMock, TrustedCallMock, TrustedCallSignedMock}; +use itp_types::{ + parentchain::{BalanceTransfer, ExtrinsicStatus, FilterEvents, HandleParentchainEvents}, + Address, RsaRequest, ShardIdentifier, H256, +}; +use log::*; +use std::vec::Vec; + +/// Default filter we use for the Integritee-Parachain. +pub struct MockExtrinsicFilter { + _phantom: PhantomData, +} + +impl FilterIntoDataFrom + for MockExtrinsicFilter +where + ExtrinsicParser: ParseExtrinsic, +{ + type Output = IndirectCall; + type ParseParentchainMetadata = ExtrinsicParser; + + fn filter_into_from_metadata( + encoded_data: &[u8], + metadata: &NodeMetadata, + ) -> Option { + let call_mut = &mut &encoded_data[..]; + + // Todo: the filter should not need to parse, only filter. This should directly be configured + // in the indirect executor. + let xt = match Self::ParseParentchainMetadata::parse(call_mut) { + Ok(xt) => xt, + Err(e) => { + log::error!( + "[ShieldFundsAndInvokeFilter] Could not parse parentchain extrinsic: {:?}", + e + ); + return None + }, + }; + let index = xt.call_index; + let call_args = &mut &xt.call_args[..]; + log::trace!( + "[ShieldFundsAndInvokeFilter] attempting to execute indirect call with index {:?}", + index + ); + if index == metadata.shield_funds_call_indexes().ok()? { + log::debug!("executing shield funds call"); + let args = ShieldFundsArgs::decode(call_args).unwrap(); + Some(IndirectCall::ShieldFunds(args)) + } else if index == metadata.invoke_call_indexes().ok()? { + log::debug!("executing invoke call"); + let args = InvokeArgs::decode(call_args).unwrap(); + Some(IndirectCall::Invoke(args)) + } else { + None + } + } +} +pub struct ExtrinsicParser { + _phantom: PhantomData, +} +use itp_api_client_types::ParentchainSignedExtra; +use itp_stf_primitives::types::TrustedOperation; + +/// Parses the extrinsics corresponding to the parentchain. +pub type MockParentchainExtrinsicParser = ExtrinsicParser; + +/// Partially interpreted extrinsic containing the `signature` and the `call_index` whereas +/// the `call_args` remain in encoded form. +/// +/// Intended for usage, where the actual `call_args` form is unknown. +pub struct SemiOpaqueExtrinsic<'a> { + /// Signature of the Extrinsic. + pub signature: Signature, + /// Call index of the dispatchable. + pub call_index: CallIndex, + /// Encoded arguments of the dispatchable corresponding to the `call_index`. + pub call_args: &'a [u8], +} + +/// Trait to extract signature and call indexes of an encoded [UncheckedExtrinsicV4]. +pub trait ParseExtrinsic { + /// Signed extra of the extrinsic. + type SignedExtra; + + fn parse(encoded_call: &[u8]) -> Result; +} + +impl ParseExtrinsic for ExtrinsicParser +where + SignedExtra: Decode + Encode, +{ + type SignedExtra = SignedExtra; + + /// Extract a call index of an encoded call. + fn parse(encoded_call: &[u8]) -> Result { + let call_mut = &mut &encoded_call[..]; + + // `()` is a trick to stop decoding after the call index. So the remaining bytes + // of `call` after decoding only contain the parentchain's dispatchable's arguments. + let xt = UncheckedExtrinsicV4::< + Address, + (CallIndex, ()), + PairSignature, + Self::SignedExtra, + >::decode(call_mut)?; + + Ok(SemiOpaqueExtrinsic { + signature: xt.signature.unwrap().1, + call_index: xt.function.0, + call_args: call_mut, + }) + } +} +/// The default indirect call (extrinsic-triggered) of the Integritee-Parachain. +#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq)] +pub enum IndirectCall { + ShieldFunds(ShieldFundsArgs), + Invoke(InvokeArgs), +} + +impl> + IndirectDispatch for IndirectCall +{ + type Args = (); + fn dispatch(&self, executor: &Executor, args: Self::Args) -> ICResult<()> { + trace!("dispatching indirect call {:?}", self); + match self { + IndirectCall::ShieldFunds(shieldfunds_args) => + shieldfunds_args.dispatch(executor, args), + IndirectCall::Invoke(invoke_args) => invoke_args.dispatch(executor, args), + } + } +} + +pub struct TestEventCreator; + +impl EventsFromMetadata for TestEventCreator { + type Output = MockEvents; + + fn create_from_metadata( + _metadata: NodeMetadata, + _block_hash: H256, + _events: &[u8], + ) -> Option { + Some(MockEvents) + } +} + +pub struct MockEvents; + +impl FilterEvents for MockEvents { + type Error = (); + fn get_extrinsic_statuses(&self) -> core::result::Result, Self::Error> { + Ok(Vec::from([ExtrinsicStatus::Success])) + } + + fn get_transfer_events(&self) -> core::result::Result, Self::Error> { + let transfer = BalanceTransfer { + to: [0u8; 32].into(), + from: [0u8; 32].into(), + amount: Balance::default(), + }; + Ok(Vec::from([transfer])) + } +} + +pub struct MockParentchainEventHandler {} + +impl HandleParentchainEvents + for MockParentchainEventHandler +where + Executor: IndirectExecutor, +{ + fn handle_events( + _: &Executor, + _: impl itp_types::parentchain::FilterEvents, + _: &AccountId, + ) -> core::result::Result<(), Error> { + Ok(()) + } +} + +/// Arguments of the Integritee-Parachain's shield fund dispatchable. +#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq)] +pub struct ShieldFundsArgs { + account_encrypted: Vec, + amount: Balance, + shard: ShardIdentifier, +} + +impl> + IndirectDispatch for ShieldFundsArgs +{ + type Args = (); + fn dispatch(&self, executor: &Executor, _args: Self::Args) -> ICResult<()> { + info!("Found ShieldFunds extrinsic in block: \nAccount Encrypted {:?} \nAmount: {} \nShard: {}", + self.account_encrypted, self.amount, bs58::encode(self.shard.encode()).into_string()); + + debug!("decrypt the account id"); + let account_vec = executor.decrypt(&self.account_encrypted)?; + let _account = AccountId::decode(&mut account_vec.as_slice())?; + + let enclave_account_id = executor.get_enclave_account()?; + let trusted_call = TrustedCallMock::noop(enclave_account_id.into()); + let signed_trusted_call = executor.sign_call_with_self(&trusted_call, &self.shard)?; + let trusted_operation = + TrustedOperation::::indirect_call( + signed_trusted_call, + ); + + let encrypted_trusted_call = executor.encrypt(&trusted_operation.encode())?; + executor.submit_trusted_call(self.shard, encrypted_trusted_call); + Ok(()) + } +} + +#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq)] +pub struct InvokeArgs { + request: RsaRequest, +} + +impl> + IndirectDispatch for InvokeArgs +{ + type Args = (); + fn dispatch(&self, executor: &Executor, _args: Self::Args) -> ICResult<()> { + log::debug!("Found trusted call extrinsic, submitting it to the top pool"); + executor.submit_trusted_call(self.request.shard(), self.request.payload().to_vec()); + Ok(()) + } +} diff --git a/bitacross-worker/core/parentchain/indirect-calls-executor/src/traits.rs b/bitacross-worker/core/parentchain/indirect-calls-executor/src/traits.rs new file mode 100644 index 0000000000..ed1850b0c9 --- /dev/null +++ b/bitacross-worker/core/parentchain/indirect-calls-executor/src/traits.rs @@ -0,0 +1,59 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{error::Result, Error}; +use codec::{Decode, Encode}; +use core::fmt::Debug; +use itp_stf_primitives::traits::{IndirectExecutor, TrustedCallVerification}; +use itp_types::{OpaqueCall, H256}; +use sp_runtime::traits::{Block as ParentchainBlockTrait, Header}; +use std::vec::Vec; + +/// Trait to execute the indirect calls found in the extrinsics of a block. +pub trait ExecuteIndirectCalls { + /// Scans blocks for extrinsics that ask the enclave to execute some actions. + /// Executes indirect invocation calls, including shielding and unshielding calls. + /// Returns all unshielding call confirmations as opaque calls and the hashes of executed shielding calls. + fn execute_indirect_calls_in_extrinsics( + &self, + block: &ParentchainBlock, + events: &[u8], + ) -> Result + where + ParentchainBlock: ParentchainBlockTrait; + + /// Creates a processed_parentchain_block extrinsic for a given parentchain block hash and the merkle executed extrinsics. + /// + /// Calculates the merkle root of the extrinsics. In case no extrinsics are supplied, the root will be a hash filled with zeros. + fn create_processed_parentchain_block_call( + &self, + block_hash: H256, + extrinsics: Vec, + block_number: <::Header as Header>::Number, + ) -> Result + where + ParentchainBlock: ParentchainBlockTrait; +} + +/// Trait that should be implemented on indirect calls to be executed. +pub trait IndirectDispatch, TCS> +where + TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, +{ + type Args; + fn dispatch(&self, executor: &E, args: Self::Args) -> Result<()>; +} diff --git a/bitacross-worker/core/parentchain/light-client/Cargo.toml b/bitacross-worker/core/parentchain/light-client/Cargo.toml new file mode 100644 index 0000000000..4573a60065 --- /dev/null +++ b/bitacross-worker/core/parentchain/light-client/Cargo.toml @@ -0,0 +1,71 @@ +[package] +name = "itc-parentchain-light-client" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "chain-error"] } +finality-grandpa = { version = "0.16.0", default-features = false, features = ["derive-codec"] } +log = { version = "0.4", default-features = false } +thiserror = { version = "1.0.26", optional = true } + +# sgx-deps +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", features = ["untrusted_fs"], optional = true } +sgx_types = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } +thiserror-sgx = { package = "thiserror", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3", optional = true } + +# local deps +itp-ocall-api = { path = "../../../core-primitives/ocall-api", default-features = false } +itp-sgx-io = { path = "../../../core-primitives/sgx/io", default-features = false } +itp-storage = { path = "../../../core-primitives/storage", default-features = false } +itp-types = { path = "../../../core-primitives/types", default-features = false } + +# substrate deps +sp-consensus-grandpa = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# test & mock dependencies +itc-parentchain-test = { optional = true, default-features = false, path = "../../../core/parentchain/test" } +# We can't really make this optional due to feature flag complexities. +itp-sgx-temp-dir = { version = "0.1", default-features = false, path = "../../../core-primitives/sgx/temp-dir" } +itp-test = { optional = true, default-features = false, features = ["sgx"], path = "../../../core-primitives/test" } + +[dev-dependencies] +itc-parentchain-test = { path = "../../../core/parentchain/test" } +itp-test = { path = "../../../core-primitives/test" } +itp-sgx-temp-dir = { version = "0.1", path = "../../../core-primitives/sgx/temp-dir" } + + +[features] +default = ["std"] +std = [ + "codec/std", + "log/std", + "finality-grandpa/std", + "thiserror", + + # substrate deps + "sp-consensus-grandpa/std", + "sp-runtime/std", + + # local deps + "itp-ocall-api/std", + "itp-storage/std", + "itp-sgx-io/std", + "itp-types/std", + # mock deps + "itp-sgx-temp-dir/std", +] +sgx = [ + "sgx_tstd", + "thiserror-sgx", + "itp-sgx-io/sgx", + "itp-storage/sgx", + "itp-sgx-temp-dir/sgx", +] +mocks = [ + "itc-parentchain-test", +] + +test = ["mocks", "itp-test"] diff --git a/bitacross-worker/core/parentchain/light-client/src/concurrent_access.rs b/bitacross-worker/core/parentchain/light-client/src/concurrent_access.rs new file mode 100644 index 0000000000..fda60d74b0 --- /dev/null +++ b/bitacross-worker/core/parentchain/light-client/src/concurrent_access.rs @@ -0,0 +1,143 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Concurrent access mechanisms that ensure mutually exclusive read/write access +//! to the light-client (validator) by employing RwLocks under the hood. + +#[cfg(feature = "sgx")] +use std::sync::SgxRwLock as RwLock; + +#[cfg(feature = "std")] +use std::sync::RwLock; + +use crate::{ + error::{Error, Result}, + ExtrinsicSender as ExtrinsicSenderTrait, LightClientSealing, LightClientState, + LightValidationState, Validator as ValidatorTrait, +}; +use finality_grandpa::BlockNumberOps; +use itp_types::parentchain::{IdentifyParentchain, ParentchainId}; +use sp_runtime::traits::{Block as ParentchainBlockTrait, NumberFor}; +use std::{marker::PhantomData, sync::Arc}; + +/// Retrieve an exclusive lock on a validator for either read or write access. +/// +/// In order to hide the whole locks mechanics, we provide an interface that allows executing +/// either a mutating, or a non-mutating function on the validator. +/// The reason we have this additional wrapper around `SealedIO`, is that we need +/// to guard against concurrent access by using RWLocks (which `SealedIO` does not do). +pub trait ValidatorAccess +where + ParentchainBlock: ParentchainBlockTrait, + NumberFor: BlockNumberOps, +{ + type ValidatorType: ValidatorTrait + + LightClientState + + ExtrinsicSenderTrait; + + /// Execute a non-mutating function on the validator. + fn execute_on_validator(&self, getter_function: F) -> Result + where + F: FnOnce(&Self::ValidatorType) -> Result; + + /// Execute a mutating function on the validator. + fn execute_mut_on_validator(&self, mutating_function: F) -> Result + where + F: FnOnce(&mut Self::ValidatorType) -> Result; +} + +/// Implementation of a validator access based on a global lock and corresponding file. +#[derive(Debug)] +pub struct ValidatorAccessor { + seal: Arc, + light_validation: RwLock, + _phantom: PhantomData<(LightClientSeal, Validator, ParentchainBlock)>, +} + +impl + ValidatorAccessor +{ + pub fn new(validator: Validator, seal: Arc) -> Self { + ValidatorAccessor { + light_validation: RwLock::new(validator), + seal, + _phantom: Default::default(), + } + } +} + +impl IdentifyParentchain + for ValidatorAccessor +{ + fn parentchain_id(&self) -> ParentchainId { + (*self.seal).parentchain_id() + } +} + +impl ValidatorAccess + for ValidatorAccessor +where + Validator: ValidatorTrait + + LightClientState + + ExtrinsicSenderTrait, + Seal: LightClientSealing>, + ParentchainBlock: ParentchainBlockTrait, + NumberFor: BlockNumberOps, +{ + type ValidatorType = Validator; + + fn execute_on_validator(&self, getter_function: F) -> Result + where + F: FnOnce(&Self::ValidatorType) -> Result, + { + let light_validation_lock = + self.light_validation.write().map_err(|_| Error::PoisonedLock)?; + getter_function(&light_validation_lock) + } + + fn execute_mut_on_validator(&self, mutating_function: F) -> Result + where + F: FnOnce(&mut Self::ValidatorType) -> Result, + { + let mut light_validation_lock = + self.light_validation.write().map_err(|_| Error::PoisonedLock)?; + let result = mutating_function(&mut light_validation_lock); + self.seal.seal(light_validation_lock.get_state())?; + result + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::mocks::{ + validator_mock::ValidatorMock, validator_mock_seal::LightValidationStateSealMock, + }; + use itp_types::Block; + + type TestAccessor = ValidatorAccessor; + + #[test] + fn execute_with_and_without_mut_in_single_thread_works() { + let validator_mock = ValidatorMock::default(); + let seal = LightValidationStateSealMock::new(); + let accessor = TestAccessor::new(validator_mock, seal.into()); + + let _read_result = accessor.execute_on_validator(|_v| Ok(())).unwrap(); + let _write_result = accessor.execute_mut_on_validator(|_v| Ok(())).unwrap(); + } +} diff --git a/bitacross-worker/core/parentchain/light-client/src/error.rs b/bitacross-worker/core/parentchain/light-client/src/error.rs new file mode 100644 index 0000000000..8f0276d133 --- /dev/null +++ b/bitacross-worker/core/parentchain/light-client/src/error.rs @@ -0,0 +1,84 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use std::{boxed::Box, string::String}; + +use sgx_types::sgx_status_t; +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use thiserror_sgx as thiserror; + +pub type Result = core::result::Result; + +/// Substrate Client error +#[derive(Debug, PartialEq, Eq, thiserror::Error)] +pub enum JustificationError { + #[error("Error decoding justification")] + JustificationDecode, + /// Justification for header is correctly encoded, but invalid. + #[error("bad justification for header: {0}")] + BadJustification(String), + #[error("Invalid authorities set")] + InvalidAuthoritiesSet, +} + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("Genesis not found")] + NoGenesis, + #[error(transparent)] + Storage(#[from] itp_storage::Error), + #[error("Validator set mismatch")] + ValidatorSetMismatch, + #[error("Invalid ancestry proof")] + InvalidAncestryProof, + #[error("Invalid Finality Proof: {0}")] + InvalidFinalityProof(#[from] JustificationError), + #[error("Header ancestry mismatch")] + HeaderAncestryMismatch, + #[error("Poisoned validator lock")] + PoisonedLock, + #[error("No Justification found")] + NoJustificationFound, + #[error(transparent)] + Other(#[from] Box), +} + +impl From for Error { + fn from(e: std::io::Error) -> Self { + Self::Other(e.into()) + } +} + +impl From for Error { + #[cfg(feature = "std")] + fn from(e: codec::Error) -> Self { + Self::Other(e.into()) + } + + #[cfg(not(feature = "std"))] + fn from(e: codec::Error) -> Self { + Self::Other(format!("{:?}", e).into()) + } +} + +impl From for sgx_status_t { + /// return sgx_status for top level enclave functions + fn from(error: Error) -> sgx_status_t { + log::warn!("LightClientError into sgx_status_t: {:?}", error); + sgx_status_t::SGX_ERROR_UNEXPECTED + } +} diff --git a/bitacross-worker/core/parentchain/light-client/src/finality.rs b/bitacross-worker/core/parentchain/light-client/src/finality.rs new file mode 100644 index 0000000000..95371a8863 --- /dev/null +++ b/bitacross-worker/core/parentchain/light-client/src/finality.rs @@ -0,0 +1,187 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Finality for determination of the light client validation. + +use crate::{ + error::Result, + grandpa_log, + justification::GrandpaJustification, + state::{RelayState, ScheduledChangeAtBlock}, + AuthorityList, Error, NumberFor, +}; +use finality_grandpa::voter_set::VoterSet; +use log::*; +pub use sp_consensus_grandpa::SetId; +use sp_consensus_grandpa::{AuthorityId, ScheduledChange, GRANDPA_ENGINE_ID}; +use sp_runtime::{ + generic::Digest, + traits::{Block as ParentchainBlockTrait, Header as HeaderTrait}, + EncodedJustification, Justifications, +}; + +#[derive(Default)] +pub struct GrandpaFinality; + +#[derive(Default)] +pub struct ParachainFinality; + +pub trait Finality { + fn validate( + &self, + header: Block::Header, + validator_set: &AuthorityList, + validator_set_id: SetId, + justifications: Option, + relay: &mut RelayState, + ) -> Result<()>; +} + +impl Finality for ParachainFinality +where + Block: ParentchainBlockTrait, +{ + fn validate( + &self, + _header: Block::Header, + _validator_set: &AuthorityList, + _validator_set_id: SetId, + _justifications: Option, + _relay: &mut RelayState, + ) -> Result<()> { + Ok(()) + } +} + +impl Finality for GrandpaFinality +where + Block: ParentchainBlockTrait, + NumberFor: finality_grandpa::BlockNumberOps, +{ + fn validate( + &self, + header: Block::Header, + validator_set: &AuthorityList, + validator_set_id: SetId, + justifications: Option, + relay: &mut RelayState, + ) -> Result<()> { + Self::apply_validator_set_change(relay, &header); + + // Check that the header has been finalized + let voter_set = + VoterSet::new(validator_set.clone().into_iter()).expect("VoterSet may not be empty"); + + // ensure justifications is a grandpa justification + let grandpa_justification = + justifications.and_then(|just| just.into_justification(GRANDPA_ENGINE_ID)); + + let block_hash = header.hash(); + let block_num = *header.number(); + + match grandpa_justification { + Some(justification) => { + if let Err(err) = Self::verify_grandpa_proof::( + justification, + block_hash, + block_num, + validator_set_id, + &voter_set, + ) { + // FIXME: Printing error upon invalid justification, but this will need a better fix + // see issue #353 + error!("Block {:?} contained invalid justification: {:?}", block_num, err); + relay.unjustified_headers.push(block_hash); + relay.set_last_finalized_block_header(header); + return Err(err) + } + Self::schedule_validator_set_change(relay, &header); + + Ok(()) + }, + None => { + relay.unjustified_headers.push(block_hash); + relay.set_last_finalized_block_header(header); + + debug!( + "Syncing finalized block without grandpa proof. Amount of unjustified headers: {}", + relay.unjustified_headers.len() + ); + Err(Error::NoJustificationFound) + }, + } + } +} + +impl GrandpaFinality { + fn apply_validator_set_change( + relay: &mut RelayState, + header: &Block::Header, + ) { + if let Some(change) = relay.scheduled_change.take() { + if &change.at_block == header.number() { + relay.current_validator_set = change.next_authority_list; + relay.current_validator_set_id += 1; + } + } + } + + fn schedule_validator_set_change( + relay: &mut RelayState, + header: &Block::Header, + ) { + if let Some(log) = pending_change::(header.digest()) { + if relay.scheduled_change.is_some() { + error!( + "Tried to scheduled authorities change even though one is already scheduled!!" + ); // should not happen if blockchain is configured properly + } else { + relay.scheduled_change = Some(ScheduledChangeAtBlock { + at_block: log.delay + *header.number(), + next_authority_list: log.next_authorities, + }) + } + } + } + + fn verify_grandpa_proof( + encoded_justification: EncodedJustification, + hash: Block::Hash, + number: NumberFor, + set_id: u64, + voters: &VoterSet, + ) -> Result<()> + where + NumberFor: finality_grandpa::BlockNumberOps, + { + // We don't really care about the justification, as long as it's valid + let _ = GrandpaJustification::::decode_and_verify_finalizes( + &encoded_justification, + (hash, number), + set_id, + voters, + )?; + + Ok(()) + } +} + +fn pending_change( + digest: &Digest, +) -> Option>> { + grandpa_log::(digest).and_then(|log| log.try_into_change()) +} diff --git a/bitacross-worker/core/parentchain/light-client/src/io.rs b/bitacross-worker/core/parentchain/light-client/src/io.rs new file mode 100644 index 0000000000..df3ca294a6 --- /dev/null +++ b/bitacross-worker/core/parentchain/light-client/src/io.rs @@ -0,0 +1,386 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + error::{Error, Result}, + finality::{Finality, GrandpaFinality, ParachainFinality}, + light_client_init_params::{GrandpaParams, SimpleParams}, + light_validation::{check_validator_set_proof, LightValidation}, + state::RelayState, + LightClientSealing, LightClientState, LightValidationState, NumberFor, Validator, +}; +use codec::{Decode, Encode}; +use core::{fmt::Debug, marker::PhantomData}; +use itp_ocall_api::EnclaveOnChainOCallApi; +use itp_sgx_io::{seal, unseal}; +use itp_types::parentchain::{IdentifyParentchain, ParentchainId}; +use log::*; +use sp_runtime::traits::{Block, Header}; +use std::{ + boxed::Box, + fs, + path::{Path, PathBuf}, + sgxfs::SgxFile, + sync::Arc, +}; + +#[cfg(feature = "sgx")] +use std::sync::SgxRwLock as RwLock; + +#[cfg(feature = "std")] +use std::sync::RwLock; + +pub const DB_FILE: &str = "db.bin"; +pub const BACKUP_FILE: &str = "db.bin.backup"; + +#[derive(Clone, Debug)] +pub struct LightClientStateSeal { + base_path: PathBuf, + db_path: PathBuf, + backup_path: PathBuf, + parentchain_id: ParentchainId, + _phantom: PhantomData<(B, LightClientState)>, +} + +impl LightClientStateSeal { + pub fn new(base_path: PathBuf, parentchain_id: ParentchainId) -> Result { + std::fs::create_dir_all(&base_path)?; + Ok(Self { + base_path: base_path.clone(), + db_path: base_path.clone().join(DB_FILE), + backup_path: base_path.join(BACKUP_FILE), + parentchain_id, + _phantom: Default::default(), + }) + } + + pub fn base_path(&self) -> &Path { + &self.base_path + } + + pub fn db_path(&self) -> &Path { + &self.db_path + } + + pub fn backup_path(&self) -> &Path { + &self.backup_path + } + + pub fn backup(&self) -> Result<()> { + if self.db_path().exists() { + let _bytes = fs::copy(self.db_path(), self.backup_path())?; + } else { + info!("{} does not exist yet, skipping backup...", self.db_path().display()) + } + Ok(()) + } +} + +impl IdentifyParentchain for LightClientStateSeal { + fn parentchain_id(&self) -> ParentchainId { + self.parentchain_id + } +} + +impl LightClientSealing + for LightClientStateSeal +{ + type LightClientState = LightClientState; + + fn seal(&self, unsealed: &LightClientState) -> Result<()> { + trace!( + "[{:?}] Backup light client state to {}", + self.parentchain_id, + self.backup_path().display() + ); + + if let Err(e) = self.backup() { + warn!( + "[{:?}] Could not backup previous light client state: Error: {}", + self.parentchain_id, e + ); + }; + + trace!( + "[{:?}] Seal light client State. Current state: {:?}", + self.parentchain_id, + unsealed + ); + Ok(unsealed.using_encoded(|bytes| seal(bytes, self.db_path()))?) + } + + fn unseal(&self) -> Result { + Ok(unseal(self.db_path()).map(|b| Decode::decode(&mut b.as_slice()))??) + } + + fn exists(&self) -> bool { + SgxFile::open(self.db_path()).is_ok() + } + + fn path(&self) -> &Path { + self.db_path() + } +} + +/// Same as [LightClientStateSeal], but it ensures that no concurrent write operations are done +/// accross different threads. +#[derive(Debug)] +pub struct LightClientStateSealSync { + seal: LightClientStateSeal, + _rw_lock: RwLock<()>, +} + +impl LightClientStateSealSync { + pub fn new(base_path: PathBuf, parentchain_id: ParentchainId) -> Result { + Ok(Self { + seal: LightClientStateSeal::new(base_path, parentchain_id)?, + _rw_lock: RwLock::new(()), + }) + } +} + +impl IdentifyParentchain for LightClientStateSealSync { + fn parentchain_id(&self) -> ParentchainId { + self.seal.parentchain_id + } +} + +impl LightClientSealing + for LightClientStateSealSync +{ + type LightClientState = LightClientState; + + fn seal(&self, unsealed: &LightClientState) -> Result<()> { + let _lock = self._rw_lock.write().map_err(|_| Error::PoisonedLock)?; + self.seal.seal(unsealed) + } + + fn unseal(&self) -> Result { + let _lock = self._rw_lock.read().map_err(|_| Error::PoisonedLock)?; + self.seal.unseal() + } + + fn exists(&self) -> bool { + self.seal.exists() + } + + fn path(&self) -> &Path { + self.seal.path() + } +} + +// FIXME: This is a lot of duplicate code for the initialization of two +// different but sameish light clients. Should be tackled with #1081 +pub fn read_or_init_grandpa_validator( + params: GrandpaParams, + ocall_api: Arc, + seal: &LightClientSeal, + parentchain_id: ParentchainId, +) -> Result> +where + B: Block, + NumberFor: finality_grandpa::BlockNumberOps, + OCallApi: EnclaveOnChainOCallApi, + LightClientSeal: + LightClientSealing> + IdentifyParentchain, +{ + check_validator_set_proof::( + params.genesis_header.state_root(), + params.authority_proof, + ¶ms.authorities, + )?; + + if !seal.exists() { + info!( + "[{:?}] ChainRelay DB not found, creating new! {}", + seal.parentchain_id(), + seal.path().display() + ); + let validator = init_grandpa_validator::( + ocall_api, + RelayState::new(params.genesis_header, params.authorities).into(), + parentchain_id, + )?; + seal.seal(validator.get_state())?; + return Ok(validator) + } + + let validation_state = seal.unseal()?; + let genesis_hash = validation_state.genesis_hash()?; + + let init_state = if genesis_hash == params.genesis_header.hash() { + info!( + "[{:?}] Found already initialized light client with Genesis Hash: {:?}", + seal.parentchain_id(), + genesis_hash + ); + validation_state + } else { + info!( + "Previous light client db belongs to another parentchain genesis. Creating new: {:?}", + genesis_hash + ); + RelayState::new(params.genesis_header, params.authorities).into() + }; + + let validator = init_grandpa_validator::(ocall_api, init_state, parentchain_id)?; + + info!("[{:?}] light client state: {:?}", seal.parentchain_id(), validator); + + seal.seal(validator.get_state())?; + Ok(validator) +} + +pub fn read_or_init_parachain_validator( + params: SimpleParams, + ocall_api: Arc, + seal: &LightClientSeal, + parentchain_id: ParentchainId, +) -> Result> +where + B: Block, + NumberFor: finality_grandpa::BlockNumberOps, + OCallApi: EnclaveOnChainOCallApi, + LightClientSeal: LightClientSealing>, +{ + if !seal.exists() { + info!("[Enclave] ChainRelay DB not found, creating new! {}", seal.path().display()); + let validator = init_parachain_validator::( + ocall_api, + RelayState::new(params.genesis_header, Default::default()).into(), + parentchain_id, + )?; + seal.seal(validator.get_state())?; + return Ok(validator) + } + + let validation_state = seal.unseal()?; + let genesis_hash = validation_state.genesis_hash()?; + + let init_state = if genesis_hash == params.genesis_header.hash() { + info!("Found already initialized light client with Genesis Hash: {:?}", genesis_hash); + validation_state + } else { + info!( + "Previous light client db belongs to another parentchain genesis. Creating new: {:?}", + genesis_hash + ); + RelayState::new(params.genesis_header, vec![]).into() + }; + + let validator = init_parachain_validator::(ocall_api, init_state, parentchain_id)?; + info!("light client state: {:?}", validator); + + seal.seal(validator.get_state())?; + Ok(validator) +} + +fn init_grandpa_validator( + ocall_api: Arc, + state: LightValidationState, + parentchain_id: ParentchainId, +) -> Result> +where + B: Block, + NumberFor: finality_grandpa::BlockNumberOps, + OCallApi: EnclaveOnChainOCallApi, +{ + let finality: Arc + Sync + Send + 'static>> = + Arc::new(Box::new(GrandpaFinality)); + + let validator = LightValidation::::new(ocall_api, finality, state, parentchain_id); + + Ok(validator) +} + +fn init_parachain_validator( + ocall_api: Arc, + state: LightValidationState, + parentchain_id: ParentchainId, +) -> Result> +where + B: Block, + NumberFor: finality_grandpa::BlockNumberOps, + OCallApi: EnclaveOnChainOCallApi, +{ + let finality: Arc + Sync + Send + 'static>> = + Arc::new(Box::new(ParachainFinality)); + + let validator = LightValidation::::new(ocall_api, finality, state, parentchain_id); + Ok(validator) +} + +#[cfg(feature = "test")] +pub mod sgx_tests { + use super::{read_or_init_parachain_validator, Arc, LightClientStateSeal, RelayState}; + use crate::{ + light_client_init_params::SimpleParams, LightClientSealing, LightClientState, + LightValidationState, + }; + use itc_parentchain_test::{Block, Header, ParentchainHeaderBuilder}; + use itp_sgx_temp_dir::TempDir; + use itp_test::mock::onchain_mock::OnchainMock; + use itp_types::parentchain::ParentchainId; + use sp_runtime::OpaqueExtrinsic; + + type TestBlock = Block; + type TestSeal = LightClientStateSeal>; + + fn default_simple_params() -> SimpleParams

{ + SimpleParams { genesis_header: ParentchainHeaderBuilder::default().build() } + } + + pub fn init_parachain_light_client_works() { + let parachain_params = default_simple_params(); + let temp_dir = TempDir::with_prefix("init_parachain_light_client_works").unwrap(); + let seal = TestSeal::new(temp_dir.path().to_path_buf(), ParentchainId::Litentry).unwrap(); + + let validator = read_or_init_parachain_validator::( + parachain_params.clone(), + Arc::new(OnchainMock::default()), + &seal, + ParentchainId::Litentry, + ) + .unwrap(); + + assert_eq!(validator.genesis_hash().unwrap(), parachain_params.genesis_header.hash()); + assert_eq!(validator.latest_finalized_header().unwrap(), parachain_params.genesis_header); + assert_eq!( + validator.penultimate_finalized_block_header().unwrap(), + parachain_params.genesis_header + ); + } + + pub fn sealing_creates_backup() { + let params = default_simple_params(); + let temp_dir = TempDir::with_prefix("sealing_creates_backup").unwrap(); + let seal = TestSeal::new(temp_dir.path().to_path_buf(), ParentchainId::Litentry).unwrap(); + let state = RelayState::new(params.genesis_header, Default::default()).into(); + + seal.seal(&state).unwrap(); + let unsealed = seal.unseal().unwrap(); + + assert_eq!(state, unsealed); + + // The first seal operation doesn't create a backup, as there is nothing to backup. + seal.seal(&unsealed).unwrap(); + assert!(seal.backup_path().exists()) + } + + // Todo #1293: add a unit test for the grandpa validator, but this needs a little effort for + // setting up correct finality params. +} diff --git a/bitacross-worker/core/parentchain/light-client/src/justification.rs b/bitacross-worker/core/parentchain/light-client/src/justification.rs new file mode 100644 index 0000000000..5e6f21f78c --- /dev/null +++ b/bitacross-worker/core/parentchain/light-client/src/justification.rs @@ -0,0 +1,229 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use std::{ + collections::{HashMap, HashSet}, + string::ToString, + vec::Vec, +}; + +use super::error::JustificationError as ClientError; +use codec::{Decode, Encode}; +use finality_grandpa::{voter_set::VoterSet, Error as GrandpaError}; +use log::*; +use sp_consensus_grandpa::{AuthorityId, AuthorityList, AuthoritySignature}; +use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor}; + +/// A commit message for this chain's block type. +pub type Commit = finality_grandpa::Commit< + ::Hash, + NumberFor, + AuthoritySignature, + AuthorityId, +>; + +/// A GRANDPA justification for block finality, it includes a commit message and +/// an ancestry proof including all headers routing all precommit target blocks +/// to the commit target block. Due to the current voting strategy the precommit +/// targets should be the same as the commit target, since honest voters don't +/// vote past authority set change blocks. +/// +/// This is meant to be stored in the db and passed around the network to other +/// nodes, and are used by syncing nodes to prove authority set handoffs. +#[derive(Clone, Encode, Decode, PartialEq, Eq)] +pub struct GrandpaJustification { + round: u64, + pub(crate) commit: Commit, + votes_ancestries: Vec, +} + +impl GrandpaJustification { + /// Decode a GRANDPA justification and validate the commit and the votes' + /// ancestry proofs finalize the given block. + pub fn decode_and_verify_finalizes( + encoded: &[u8], + finalized_target: (Block::Hash, NumberFor), + set_id: u64, + voters: &VoterSet, + ) -> Result, ClientError> + where + NumberFor: finality_grandpa::BlockNumberOps, + { + let justification = GrandpaJustification::::decode(&mut &*encoded) + .map_err(|_| ClientError::JustificationDecode)?; + + let justificated_commit = + (justification.commit.target_hash, justification.commit.target_number); + + if justificated_commit != finalized_target { + Err(ClientError::BadJustification( + "invalid commit target in grandpa justification".to_string(), + )) + } else { + justification.verify_with_voter_set(set_id, voters).map(|_| justification) + } + } + + /// Validate the commit and the votes' ancestry proofs. + pub fn verify(&self, set_id: u64, authorities: AuthorityList) -> Result<(), ClientError> + where + NumberFor: finality_grandpa::BlockNumberOps, + { + let voters = + VoterSet::new(authorities.into_iter()).ok_or(ClientError::InvalidAuthoritiesSet)?; + + self.verify_with_voter_set(set_id, &voters) + } + + fn validate_commit( + &self, + voters: &VoterSet, + ancestry_chain: &AncestryChain, + ) -> Result<(), ClientError> + where + NumberFor: finality_grandpa::BlockNumberOps, + { + match finality_grandpa::validate_commit(&self.commit, voters, ancestry_chain) { + Ok(ref result) if result.is_valid() => Ok(()), + _ => Err(ClientError::BadJustification( + "invalid commit in grandpa justification".to_string(), + )), + } + } + + fn fill_visited_hashes( + &self, + ancestry_chain: &AncestryChain, + precommit_target_hash: Block::Hash, + visited_hashes: &mut HashSet, + ) -> Result<(), ClientError> + where + NumberFor: finality_grandpa::BlockNumberOps, + { + use finality_grandpa::Chain; + if let Ok(route) = ancestry_chain.ancestry(self.commit.target_hash, precommit_target_hash) { + // ancestry starts from parent hash but the precommit target hash has been visited + visited_hashes.insert(precommit_target_hash); + visited_hashes.extend(route.iter()); + Ok(()) + } else { + Err(ClientError::BadJustification( + "invalid precommit ancestry proof in grandpa justification".to_string(), + )) + } + } + + /// Validate the commit and the votes' ancestry proofs. + pub(crate) fn verify_with_voter_set( + &self, + set_id: u64, + voters: &VoterSet, + ) -> Result<(), ClientError> + where + NumberFor: finality_grandpa::BlockNumberOps, + { + let ancestry_chain = AncestryChain::::new(&self.votes_ancestries); + + self.validate_commit(voters, &ancestry_chain)?; + + let mut buf = Vec::new(); + let mut visited_hashes = HashSet::new(); + for signed in self.commit.precommits.iter() { + if !sp_consensus_grandpa::check_message_signature_with_buffer( + &finality_grandpa::Message::Precommit(signed.precommit.clone()), + &signed.id, + &signed.signature, + self.round, + set_id, + &mut buf, + ) { + debug!("Bad signature on message from {:?}", &signed.id); + return Err(ClientError::BadJustification( + "invalid signature for precommit in grandpa justification".to_string(), + )) + } + + if self.commit.target_hash == signed.precommit.target_hash { + continue + } + + self.fill_visited_hashes( + &ancestry_chain, + signed.precommit.target_hash, + &mut visited_hashes, + )?; + } + + let ancestry_hashes = + self.votes_ancestries.iter().map(|h: &Block::Header| h.hash()).collect(); + + if visited_hashes != ancestry_hashes { + return Err(ClientError::BadJustification( + "invalid precommit ancestries in grandpa justification with unused headers" + .to_string(), + )) + } + + Ok(()) + } + + /// The target block number and hash that this justifications proves finality for. + pub fn target(&self) -> (NumberFor, Block::Hash) { + (self.commit.target_number, self.commit.target_hash) + } +} + +/// A utility trait implementing `finality_grandpa::Chain` using a given set of headers. +/// This is useful when validating commits, using the given set of headers to +/// verify a valid ancestry route to the target commit block. +struct AncestryChain { + ancestry: HashMap, +} + +impl AncestryChain { + fn new(ancestry: &[Block::Header]) -> AncestryChain { + let ancestry: HashMap<_, _> = + ancestry.iter().cloned().map(|h: Block::Header| (h.hash(), h)).collect(); + + AncestryChain { ancestry } + } +} + +impl finality_grandpa::Chain> for AncestryChain +where + NumberFor: finality_grandpa::BlockNumberOps, +{ + fn ancestry( + &self, + base: Block::Hash, + block: Block::Hash, + ) -> Result, GrandpaError> { + let mut ancestors = Vec::new(); + let mut current_hash = block; + while current_hash != base { + if let Some(current_header) = self.ancestry.get(¤t_hash) { + current_hash = *current_header.parent_hash(); + ancestors.push(current_hash); + } else { + return Err(GrandpaError::NotDescendent) + } + } + ancestors.pop(); // remove the base + + Ok(ancestors) + } +} diff --git a/bitacross-worker/core/parentchain/light-client/src/lib.rs b/bitacross-worker/core/parentchain/light-client/src/lib.rs new file mode 100644 index 0000000000..64b46c480f --- /dev/null +++ b/bitacross-worker/core/parentchain/light-client/src/lib.rs @@ -0,0 +1,111 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Light-client crate that imports and verifies parentchain blocks. + +#![allow(unused)] +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +#[macro_use] +extern crate sgx_tstd as std; + +// Re-export useful types. +pub use finality_grandpa::BlockNumberOps; +pub use sp_consensus_grandpa::{AuthorityList, SetId}; + +use crate::light_validation_state::LightValidationState; +use error::Error; +use sp_consensus_grandpa::{AuthorityId, AuthorityWeight, ConsensusLog, GRANDPA_ENGINE_ID}; +use sp_runtime::{ + generic::{Digest, OpaqueDigestItemId, SignedBlock}, + traits::{Block as ParentchainBlockTrait, Header as HeaderTrait}, + OpaqueExtrinsic, +}; +use std::{path::Path, vec::Vec}; + +pub mod concurrent_access; +pub mod error; +pub mod finality; +pub mod justification; +pub mod light_client_init_params; +pub mod light_validation; +pub mod light_validation_state; +pub mod state; + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod io; + +#[cfg(any(test, feature = "mocks"))] +pub mod mocks; + +pub type RelayId = u64; + +pub type AuthorityListRef<'a> = &'a [(AuthorityId, AuthorityWeight)]; + +// disambiguate associated types +/// Block number type +pub type NumberFor = <::Header as HeaderTrait>::Number; +/// Hash type of Block +pub type HashFor = <::Header as HeaderTrait>::Hash; +/// Hashing function used to produce `HashOf` +pub type HashingFor = <::Header as HeaderTrait>::Hashing; + +/// Validator trait +pub trait Validator +where + NumberFor: finality_grandpa::BlockNumberOps, +{ + fn submit_block(&mut self, signed_block: &SignedBlock) -> Result<(), Error>; + + fn get_state(&self) -> &LightValidationState; + + fn set_ignore_validation_until(&mut self, until: u32) -> Result<(), Error>; +} + +pub trait ExtrinsicSender { + /// Sends encoded extrinsics to the parentchain and cache them internally for later confirmation. + fn send_extrinsics(&mut self, extrinsics: Vec) -> Result<(), Error>; +} + +pub trait LightClientState { + fn genesis_hash(&self) -> Result, Error>; + + fn latest_finalized_header(&self) -> Result; + + // Todo: Check if we still need this after #423 + fn penultimate_finalized_block_header(&self) -> Result; +} + +pub trait LightClientSealing { + type LightClientState; + + fn seal(&self, state: &Self::LightClientState) -> Result<(), Error>; + fn unseal(&self) -> Result; + fn exists(&self) -> bool; + fn path(&self) -> &Path; +} + +pub fn grandpa_log( + digest: &Digest, +) -> Option>> { + let id = OpaqueDigestItemId::Consensus(&GRANDPA_ENGINE_ID); + digest.convert_first(|l| l.try_to::>>(id)) +} diff --git a/bitacross-worker/core/parentchain/light-client/src/light_client_init_params.rs b/bitacross-worker/core/parentchain/light-client/src/light_client_init_params.rs new file mode 100644 index 0000000000..114d684382 --- /dev/null +++ b/bitacross-worker/core/parentchain/light-client/src/light_client_init_params.rs @@ -0,0 +1,49 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use codec::{Decode, Encode}; +use sp_consensus_grandpa::AuthorityList; +use std::vec::Vec; + +#[derive(Encode, Decode, Clone)] +pub struct GrandpaParams
{ + pub genesis_header: Header, + pub authorities: AuthorityList, + pub authority_proof: Vec>, +} + +impl
GrandpaParams
{ + pub fn new( + genesis_header: Header, + authorities: AuthorityList, + authority_proof: Vec>, + ) -> Self { + Self { genesis_header, authorities, authority_proof } + } +} + +#[derive(Encode, Decode, Clone)] +pub struct SimpleParams
{ + pub genesis_header: Header, +} + +impl
SimpleParams
{ + pub fn new(genesis_header: Header) -> Self { + Self { genesis_header } + } +} diff --git a/bitacross-worker/core/parentchain/light-client/src/light_validation.rs b/bitacross-worker/core/parentchain/light-client/src/light_validation.rs new file mode 100644 index 0000000000..9fe56eccf6 --- /dev/null +++ b/bitacross-worker/core/parentchain/light-client/src/light_validation.rs @@ -0,0 +1,266 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Light-client validation crate that verifies parentchain blocks. + +use crate::{ + error::Error, finality::Finality, light_validation_state::LightValidationState, + state::RelayState, AuthorityList, AuthorityListRef, ExtrinsicSender, HashFor, HashingFor, + LightClientState, NumberFor, RelayId, Validator, +}; +use codec::Encode; +use core::iter::Iterator; +use itp_ocall_api::EnclaveOnChainOCallApi; +use itp_storage::{Error as StorageError, StorageProof, StorageProofChecker}; +use itp_types::parentchain::{IdentifyParentchain, ParentchainId}; +use log::*; +use sp_runtime::{ + generic::SignedBlock, + traits::{Block as ParentchainBlockTrait, Header as HeaderTrait}, + Justifications, OpaqueExtrinsic, +}; +use std::{boxed::Box, fmt, sync::Arc, vec::Vec}; + +#[derive(Clone)] +pub struct LightValidation { + light_validation_state: LightValidationState, + ocall_api: Arc, + parentchain_id: ParentchainId, + finality: Arc + Sync + Send + 'static>>, + ignore_validation_until: NumberFor, +} + +impl IdentifyParentchain + for LightValidation +{ + fn parentchain_id(&self) -> ParentchainId { + self.parentchain_id + } +} + +impl + LightValidation +{ + pub fn new( + ocall_api: Arc, + finality: Arc + Sync + Send + 'static>>, + light_validation_state: LightValidationState, + parentchain_id: ParentchainId, + ) -> Self { + Self { + light_validation_state, + ocall_api, + parentchain_id, + finality, + ignore_validation_until: 0u32.into(), + } + } + + fn check_validator_set_proof( + state_root: &HashFor, + proof: StorageProof, + validator_set: AuthorityListRef, + ) -> Result<(), Error> { + let checker = StorageProofChecker::>::new(*state_root, proof)?; + + // By encoding the given set we should have an easy way to compare + // with the stuff we get out of storage via `read_value` + let mut encoded_validator_set = validator_set.encode(); + encoded_validator_set.insert(0, 1); // Add AUTHORITIES_VERISON == 1 + let actual_validator_set = checker + .read_value(b":grandpa_authorities")? + .ok_or(StorageError::StorageValueUnavailable)?; + + if encoded_validator_set == actual_validator_set { + Ok(()) + } else { + Err(Error::ValidatorSetMismatch) + } + } + + // A naive way to check whether a `child` header is a descendant + // of an `ancestor` header. For this it requires a proof which + // is a chain of headers between (but not including) the `child` + // and `ancestor`. This could be updated to use something like + // Log2 Ancestors (#2053) in the future. + fn verify_ancestry( + proof: Vec, + ancestor_hash: HashFor, + child: &Block::Header, + ) -> Result<(), Error> { + let parent_hash = child.parent_hash(); + if *parent_hash == ancestor_hash { + return Ok(()) + } + + // Find the header's parent hash that matches our ancestor's hash + match proof + .iter() + .find(|header| header.hash() == *parent_hash && *header.parent_hash() == ancestor_hash) + { + Some(_) => Ok(()), + None => Err(Error::InvalidAncestryProof), + } + } + + fn submit_finalized_headers( + &mut self, + header: Block::Header, + ancestry_proof: Vec, + justifications: Option, + ) -> Result<(), Error> { + let relay = self.light_validation_state.get_relay_mut(); + + let validator_set = relay.current_validator_set.clone(); + let validator_set_id = relay.current_validator_set_id; + + if *header.number() > self.ignore_validation_until { + // Check that the new header is a descendant of the old header + let last_header = &relay.last_finalized_block_header; + Self::verify_ancestry(ancestry_proof, last_header.hash(), &header)?; + } + + if let Err(e) = self.finality.validate( + header.clone(), + &validator_set, + validator_set_id, + justifications, + relay, + ) { + match e { + Error::NoJustificationFound => return Ok(()), + _ => return Err(e), + } + } + + // Todo: Justifying the headers here is actually wrong, but it prevents an ever-growing + // `unjustified_headers` queue because in the parachain case we won't have justifications, + // and in solo chain setups we only get a justification upon an Grandpa authority change. + // Hence, we justify the headers here until we properly solve this in #1404. + relay.justify_headers(); + relay.push_header_hash(header.hash()); + + relay.set_last_finalized_block_header(header); + + if validator_set_id > relay.current_validator_set_id { + relay.current_validator_set = validator_set; + relay.current_validator_set_id = validator_set_id; + } + + Ok(()) + } +} + +impl Validator for LightValidation +where + NumberFor: finality_grandpa::BlockNumberOps, + Block: ParentchainBlockTrait, + OCallApi: EnclaveOnChainOCallApi, +{ + fn submit_block(&mut self, signed_block: &SignedBlock) -> Result<(), Error> { + let header = signed_block.block.header(); + let justifications = signed_block.justifications.clone(); + + let relay = self.light_validation_state.get_relay_mut(); + + if *header.number() > self.ignore_validation_until + && relay.last_finalized_block_header.hash() != *header.parent_hash() + { + return Err(Error::HeaderAncestryMismatch) + } + + self.submit_finalized_headers(header.clone(), vec![], justifications) + } + + fn get_state(&self) -> &LightValidationState { + &self.light_validation_state + } + + fn set_ignore_validation_until(&mut self, until: u32) -> Result<(), Error> { + info!("set ignore parentchain block import validation until: {}", until); + self.ignore_validation_until = until.into(); + Ok(()) + } +} + +impl ExtrinsicSender for LightValidation +where + NumberFor: finality_grandpa::BlockNumberOps, + Block: ParentchainBlockTrait, + OCallApi: EnclaveOnChainOCallApi, +{ + fn send_extrinsics(&mut self, extrinsics: Vec) -> Result<(), Error> { + self.ocall_api + .send_to_parentchain(extrinsics, &self.parentchain_id, false) + .map_err(|e| { + Error::Other( + format!("[{:?}] Failed to send extrinsics: {}", self.parentchain_id, e).into(), + ) + }) + } +} + +impl LightClientState for LightValidation +where + NumberFor: finality_grandpa::BlockNumberOps, + Block: ParentchainBlockTrait, + OCallApi: EnclaveOnChainOCallApi, +{ + fn genesis_hash(&self) -> Result, Error> { + self.light_validation_state.genesis_hash() + } + + fn latest_finalized_header(&self) -> Result { + self.light_validation_state.latest_finalized_header() + } + + fn penultimate_finalized_block_header(&self) -> Result { + self.light_validation_state.penultimate_finalized_block_header() + } +} + +impl fmt::Debug for LightValidation { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "LightValidation {{ parentchain_id: {:?}, relay_state: {:?} }}", + self.parentchain_id, self.light_validation_state.relay_state + ) + } +} + +pub fn check_validator_set_proof( + state_root: &HashFor, + proof: StorageProof, + validator_set: AuthorityListRef, +) -> Result<(), Error> { + let checker = StorageProofChecker::>::new(*state_root, proof)?; + + // By encoding the given set we should have an easy way to compare + // with the stuff we get out of storage via `read_value` + let mut encoded_validator_set = validator_set.encode(); + encoded_validator_set.insert(0, 1); // Add AUTHORITIES_VERISON == 1 + let actual_validator_set = checker + .read_value(b":grandpa_authorities")? + .ok_or(StorageError::StorageValueUnavailable)?; + + if encoded_validator_set == actual_validator_set { + Ok(()) + } else { + Err(Error::ValidatorSetMismatch) + } +} diff --git a/bitacross-worker/core/parentchain/light-client/src/light_validation_state.rs b/bitacross-worker/core/parentchain/light-client/src/light_validation_state.rs new file mode 100644 index 0000000000..b86a242677 --- /dev/null +++ b/bitacross-worker/core/parentchain/light-client/src/light_validation_state.rs @@ -0,0 +1,68 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! State of the light-client validation. + +use crate::{state::RelayState, Error, HashFor, LightClientState}; +use codec::{Decode, Encode}; +use sp_runtime::traits::Block as ParentchainBlockTrait; + +pub use sp_consensus_grandpa::SetId; + +#[derive(Encode, Decode, Clone, Debug, Eq, PartialEq)] +pub struct LightValidationState { + pub(crate) relay_state: RelayState, +} + +impl From> for LightValidationState { + fn from(value: RelayState) -> Self { + Self::new(value) + } +} + +impl LightValidationState { + pub fn new(relay_state: RelayState) -> Self { + Self { relay_state } + } + + pub(crate) fn get_relay(&self) -> &RelayState { + &self.relay_state + } + + pub(crate) fn get_relay_mut(&mut self) -> &mut RelayState { + &mut self.relay_state + } +} + +impl LightClientState for LightValidationState +where + Block: ParentchainBlockTrait, +{ + fn genesis_hash(&self) -> Result, Error> { + Ok(self.get_relay().genesis_hash) + } + + fn latest_finalized_header(&self) -> Result { + let relay = self.get_relay(); + Ok(relay.last_finalized_block_header.clone()) + } + + fn penultimate_finalized_block_header(&self) -> Result { + let relay = self.get_relay(); + Ok(relay.penultimate_finalized_block_header.clone()) + } +} diff --git a/bitacross-worker/core/parentchain/light-client/src/mocks/mod.rs b/bitacross-worker/core/parentchain/light-client/src/mocks/mod.rs new file mode 100644 index 0000000000..4dedae8c6d --- /dev/null +++ b/bitacross-worker/core/parentchain/light-client/src/mocks/mod.rs @@ -0,0 +1,20 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pub mod validator_access_mock; +pub mod validator_mock; +pub mod validator_mock_seal; diff --git a/bitacross-worker/core/parentchain/light-client/src/mocks/validator_access_mock.rs b/bitacross-worker/core/parentchain/light-client/src/mocks/validator_access_mock.rs new file mode 100644 index 0000000000..c8c775e5a8 --- /dev/null +++ b/bitacross-worker/core/parentchain/light-client/src/mocks/validator_access_mock.rs @@ -0,0 +1,66 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(feature = "sgx")] +use std::sync::SgxRwLock as RwLock; + +#[cfg(feature = "std")] +use std::sync::RwLock; + +use crate::{ + concurrent_access::ValidatorAccess, + error::{Error, Result}, + mocks::validator_mock::ValidatorMock, +}; +use itp_types::{ + parentchain::{IdentifyParentchain, ParentchainId}, + Block, +}; + +/// Mock for the validator access. +/// +/// Does not execute anything, just a stub. +#[derive(Default)] +pub struct ValidatorAccessMock { + validator: RwLock, +} + +impl ValidatorAccess for ValidatorAccessMock { + type ValidatorType = ValidatorMock; + + fn execute_on_validator(&self, getter_function: F) -> Result + where + F: FnOnce(&Self::ValidatorType) -> Result, + { + let validator_lock = self.validator.read().map_err(|_| Error::PoisonedLock)?; + getter_function(&validator_lock) + } + + fn execute_mut_on_validator(&self, mutating_function: F) -> Result + where + F: FnOnce(&mut Self::ValidatorType) -> Result, + { + let mut validator_lock = self.validator.write().map_err(|_| Error::PoisonedLock)?; + mutating_function(&mut validator_lock) + } +} + +impl IdentifyParentchain for ValidatorAccessMock { + fn parentchain_id(&self) -> ParentchainId { + ParentchainId::Litentry + } +} diff --git a/bitacross-worker/core/parentchain/light-client/src/mocks/validator_mock.rs b/bitacross-worker/core/parentchain/light-client/src/mocks/validator_mock.rs new file mode 100644 index 0000000000..ed33d59225 --- /dev/null +++ b/bitacross-worker/core/parentchain/light-client/src/mocks/validator_mock.rs @@ -0,0 +1,79 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + error::Result, state::RelayState, ExtrinsicSender, HashFor, LightClientState, + LightValidationState, Validator, +}; +use itc_parentchain_test::ParentchainHeaderBuilder; +use itp_types::Block; +use sp_runtime::{generic::SignedBlock, traits::Block as BlockT, OpaqueExtrinsic}; +use std::vec::Vec; + +type Header = ::Header; + +/// Validator mock to be used in tests. +#[derive(Clone, Debug)] +pub struct ValidatorMock { + light_validation_state: LightValidationState, +} + +impl Default for ValidatorMock { + fn default() -> Self { + Self { + light_validation_state: RelayState::new( + ParentchainHeaderBuilder::default().build(), + Default::default(), + ) + .into(), + } + } +} + +impl Validator for ValidatorMock { + fn submit_block(&mut self, _signed_block: &SignedBlock) -> Result<()> { + Ok(()) + } + + fn get_state(&self) -> &LightValidationState { + &self.light_validation_state + } + + fn set_ignore_validation_until(&mut self, until: u32) -> Result<()> { + Ok(()) + } +} + +impl ExtrinsicSender for ValidatorMock { + fn send_extrinsics(&mut self, _extrinsics: Vec) -> Result<()> { + Ok(()) + } +} + +impl LightClientState for ValidatorMock { + fn genesis_hash(&self) -> Result> { + todo!() + } + + fn latest_finalized_header(&self) -> Result
{ + Ok(ParentchainHeaderBuilder::default().build()) + } + + fn penultimate_finalized_block_header(&self) -> Result
{ + Ok(ParentchainHeaderBuilder::default().build()) + } +} diff --git a/bitacross-worker/core/parentchain/light-client/src/mocks/validator_mock_seal.rs b/bitacross-worker/core/parentchain/light-client/src/mocks/validator_mock_seal.rs new file mode 100644 index 0000000000..4c7e4f25d3 --- /dev/null +++ b/bitacross-worker/core/parentchain/light-client/src/mocks/validator_mock_seal.rs @@ -0,0 +1,64 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{error::Error, state::RelayState, LightClientSealing, LightValidationState}; +use itc_parentchain_test::ParentchainHeaderBuilder; +use itp_sgx_temp_dir::TempDir; +use itp_types::Block; +use std::path::Path; + +/// A seal that returns a mock validator. +#[derive(Clone)] +pub struct LightValidationStateSealMock { + // The directory is deleted when the seal is dropped. + temp_dir: TempDir, +} + +impl LightValidationStateSealMock { + pub fn new() -> Self { + Self { temp_dir: TempDir::new().unwrap() } + } +} + +impl Default for LightValidationStateSealMock { + fn default() -> Self { + Self::new() + } +} + +impl LightClientSealing for LightValidationStateSealMock { + type LightClientState = LightValidationState; + + fn unseal(&self) -> Result, Error> { + Ok(LightValidationState::new(RelayState::new( + ParentchainHeaderBuilder::default().build(), + Default::default(), + ))) + } + + fn seal(&self, _: &LightValidationState) -> Result<(), Error> { + Ok(()) + } + + fn exists(&self) -> bool { + false + } + + fn path(&self) -> &Path { + self.temp_dir.path() + } +} diff --git a/bitacross-worker/core/parentchain/light-client/src/state.rs b/bitacross-worker/core/parentchain/light-client/src/state.rs new file mode 100644 index 0000000000..e21f86e2e4 --- /dev/null +++ b/bitacross-worker/core/parentchain/light-client/src/state.rs @@ -0,0 +1,100 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use codec::{Decode, Encode}; +use sp_consensus_grandpa::{AuthorityList, SetId}; +use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; +use std::{collections::VecDeque, fmt, vec::Vec}; + +/// Defines the amount of parentchain headers to keep. +pub const PARENTCHAIN_HEADER_PRUNING: u64 = 1000; + +#[derive(Encode, Decode, Clone, Eq, PartialEq)] +pub struct RelayState { + pub genesis_hash: Block::Hash, + pub last_finalized_block_header: Block::Header, + pub penultimate_finalized_block_header: Block::Header, + pub current_validator_set: AuthorityList, + pub current_validator_set_id: SetId, + header_hashes: VecDeque, + pub unjustified_headers: Vec, // Finalized headers without grandpa proof + pub scheduled_change: Option>, // Scheduled Authorities change as indicated in the header's digest. +} + +impl RelayState { + pub fn push_header_hash(&mut self, header: Block::Hash) { + self.header_hashes.push_back(header); + + if self.header_hashes.len() > PARENTCHAIN_HEADER_PRUNING as usize { + self.header_hashes.pop_front().expect("Tested above that is not empty; qed"); + } + } + + pub fn justify_headers(&mut self) { + self.header_hashes.extend(&mut self.unjustified_headers.iter()); + self.unjustified_headers.clear(); + + while self.header_hashes.len() > PARENTCHAIN_HEADER_PRUNING as usize { + self.header_hashes.pop_front().expect("Tested above that is not empty; qed"); + } + } + + pub fn header_hashes(&self) -> &VecDeque { + &self.header_hashes + } +} + +#[derive(Encode, Decode, Clone, Eq, PartialEq)] +pub struct ScheduledChangeAtBlock { + pub at_block: Header::Number, + pub next_authority_list: AuthorityList, +} + +impl RelayState { + pub fn new(genesis: Block::Header, validator_set: AuthorityList) -> Self { + RelayState { + genesis_hash: genesis.hash(), + header_hashes: vec![genesis.hash()].into(), + last_finalized_block_header: genesis.clone(), + // is it bad to initialize with the same? Header trait does no implement default... + penultimate_finalized_block_header: genesis, + current_validator_set: validator_set, + current_validator_set_id: 0, + unjustified_headers: Vec::new(), + scheduled_change: None, + } + } + + pub fn set_last_finalized_block_header(&mut self, header: Block::Header) { + self.penultimate_finalized_block_header = + std::mem::replace(&mut self.last_finalized_block_header, header); + } +} + +impl fmt::Debug for RelayState { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "RelayInfo {{ last_finalized_block_header_number: {:?}, current_validator_set: {:?}, \ + current_validator_set_id: {}, number of unjustified headers: {} }}", + self.last_finalized_block_header.number(), + self.current_validator_set, + self.current_validator_set_id, + self.unjustified_headers.len() + ) + } +} diff --git a/bitacross-worker/core/parentchain/parentchain-crate/Cargo.toml b/bitacross-worker/core/parentchain/parentchain-crate/Cargo.toml new file mode 100644 index 0000000000..760544b667 --- /dev/null +++ b/bitacross-worker/core/parentchain/parentchain-crate/Cargo.toml @@ -0,0 +1,44 @@ +[package] +name = "itc-parentchain" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "chain-error"] } + +# Parity +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# local +itc-parentchain-block-import-dispatcher = { path = "../block-import-dispatcher", default-features = false } +itc-parentchain-block-importer = { path = "../block-importer", default-features = false } +itc-parentchain-indirect-calls-executor = { path = "../indirect-calls-executor", default-features = false } +itc-parentchain-light-client = { path = "../light-client", default-features = false } +itp-types = { default-features = false, path = "../../../core-primitives/types" } + +[features] +default = ["std"] +std = [ + "codec/std", + "sp-runtime/std", + "itc-parentchain-block-import-dispatcher/std", + "itc-parentchain-block-importer/std", + "itc-parentchain-indirect-calls-executor/std", + "itc-parentchain-light-client/std", + "itp-types/std", +] +sgx = [ + "itc-parentchain-block-import-dispatcher/sgx", + "itc-parentchain-block-importer/sgx", + "itc-parentchain-indirect-calls-executor/sgx", + "itc-parentchain-light-client/sgx", +] +mocks = [ + "itc-parentchain-block-import-dispatcher/mocks", + "itc-parentchain-light-client/mocks", +] +test = [ + "mocks", + "itc-parentchain-light-client/test", +] diff --git a/bitacross-worker/core/parentchain/parentchain-crate/src/lib.rs b/bitacross-worker/core/parentchain/parentchain-crate/src/lib.rs new file mode 100644 index 0000000000..368ee69967 --- /dev/null +++ b/bitacross-worker/core/parentchain/parentchain-crate/src/lib.rs @@ -0,0 +1,33 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Reexport all the parentchain components in one crate + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +pub use itc_parentchain_block_import_dispatcher as block_import_dispatcher; + +pub use itc_parentchain_block_importer as block_importer; + +pub use itc_parentchain_indirect_calls_executor as indirect_calls_executor; + +pub use itc_parentchain_light_client as light_client; + +pub mod primitives; diff --git a/bitacross-worker/core/parentchain/parentchain-crate/src/primitives.rs b/bitacross-worker/core/parentchain/parentchain-crate/src/primitives.rs new file mode 100644 index 0000000000..97aff7f724 --- /dev/null +++ b/bitacross-worker/core/parentchain/parentchain-crate/src/primitives.rs @@ -0,0 +1,61 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +extern crate alloc; + +use crate::light_client::light_client_init_params::{GrandpaParams, SimpleParams}; +use codec::{Decode, Encode}; + +use sp_runtime::traits::Block; + +pub use itp_types::{parentchain::ParentchainId, Block as ParachainBlock, Block as SolochainBlock}; +pub type HeaderFor = ::Header; +pub type SolochainHeader = HeaderFor; +pub type ParachainHeader = HeaderFor; +pub type SolochainParams = GrandpaParams; +pub type ParachainParams = SimpleParams; + +/// Initialization primitives, used by both service and enclave. +/// Allows to use a single E-call for the initialization of different parentchain types. +#[derive(Encode, Decode, Clone)] +pub enum ParentchainInitParams { + #[codec(index = 0)] + Solochain { id: ParentchainId, params: SolochainParams }, + #[codec(index = 1)] + Parachain { id: ParentchainId, params: ParachainParams }, +} + +impl ParentchainInitParams { + pub fn id(&self) -> &ParentchainId { + match self { + Self::Solochain { id, .. } => id, + Self::Parachain { id, .. } => id, + } + } +} + +impl From<(ParentchainId, SolochainParams)> for ParentchainInitParams { + fn from(value: (ParentchainId, SolochainParams)) -> Self { + Self::Solochain { id: value.0, params: value.1 } + } +} + +impl From<(ParentchainId, ParachainParams)> for ParentchainInitParams { + fn from(value: (ParentchainId, ParachainParams)) -> Self { + Self::Parachain { id: value.0, params: value.1 } + } +} diff --git a/bitacross-worker/core/parentchain/test/Cargo.toml b/bitacross-worker/core/parentchain/test/Cargo.toml new file mode 100644 index 0000000000..dd9cbb8535 --- /dev/null +++ b/bitacross-worker/core/parentchain/test/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "itc-parentchain-test" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +homepage = 'https://litentry.com/' +repository = 'https://github.com/litentry/litentry-parachain' +license = "Apache-2.0" +edition = "2021" + +[dependencies] +itp-types = { path = "../../../core-primitives/types", default-features = false } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +[features] +default = ["std"] +std = [ + "itp-types/std", + "sp-runtime/std", +] diff --git a/bitacross-worker/core/parentchain/test/src/lib.rs b/bitacross-worker/core/parentchain/test/src/lib.rs new file mode 100644 index 0000000000..b0ecad2d23 --- /dev/null +++ b/bitacross-worker/core/parentchain/test/src/lib.rs @@ -0,0 +1,27 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Builder patterns for common structs used in tests. + +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod parentchain_block_builder; +pub mod parentchain_header_builder; + +pub use parentchain_block_builder::{Block, ParentchainBlockBuilder, SignedBlock}; +pub use parentchain_header_builder::{BlockNumber, Header, ParentchainHeaderBuilder, H256}; diff --git a/bitacross-worker/core/parentchain/test/src/parentchain_block_builder.rs b/bitacross-worker/core/parentchain/test/src/parentchain_block_builder.rs new file mode 100644 index 0000000000..5b7ea5e081 --- /dev/null +++ b/bitacross-worker/core/parentchain/test/src/parentchain_block_builder.rs @@ -0,0 +1,62 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Builder pattern for a parentchain block. + +extern crate alloc; + +use crate::ParentchainHeaderBuilder; +use alloc::vec::Vec; +use sp_runtime::traits::MaybeSerialize; + +pub use itp_types::Header; +pub use sp_runtime::generic::{Block, SignedBlock}; + +pub struct ParentchainBlockBuilder { + header: Header, + extrinsics: Vec, +} + +impl Default for ParentchainBlockBuilder { + fn default() -> Self { + ParentchainBlockBuilder { + header: ParentchainHeaderBuilder::default().build(), + extrinsics: Default::default(), + } + } +} + +impl ParentchainBlockBuilder { + pub fn with_header(mut self, header: Header) -> Self { + self.header = header; + self + } + + pub fn with_extrinsics(mut self, extrinsics: Vec) -> Self { + self.extrinsics = extrinsics; + self + } + + pub fn build(self) -> Block { + Block { header: self.header, extrinsics: self.extrinsics } + } + + pub fn build_signed(self) -> SignedBlock> { + SignedBlock { block: self.build(), justifications: None } + } +} diff --git a/bitacross-worker/core/parentchain/test/src/parentchain_header_builder.rs b/bitacross-worker/core/parentchain/test/src/parentchain_header_builder.rs new file mode 100644 index 0000000000..926f15ce7d --- /dev/null +++ b/bitacross-worker/core/parentchain/test/src/parentchain_header_builder.rs @@ -0,0 +1,53 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Builder pattern for a parentchain header. + +pub use itp_types::{BlockNumber, Header, H256}; +pub use sp_runtime::generic::Digest; + +#[derive(Default)] +pub struct ParentchainHeaderBuilder { + number: BlockNumber, + parent_hash: H256, + state_root: H256, + extrinsic_root: H256, + digest: Digest, +} + +impl ParentchainHeaderBuilder { + pub fn with_number(mut self, number: BlockNumber) -> Self { + self.number = number; + self + } + + pub fn with_parent_hash(mut self, parent_hash: H256) -> Self { + self.parent_hash = parent_hash; + self + } + + pub fn build(self) -> Header { + Header { + number: self.number, + parent_hash: self.parent_hash, + state_root: self.state_root, + extrinsics_root: self.extrinsic_root, + digest: self.digest, + } + } +} diff --git a/bitacross-worker/core/peer-top-broadcaster/Cargo.toml b/bitacross-worker/core/peer-top-broadcaster/Cargo.toml new file mode 100644 index 0000000000..f2c870e4b2 --- /dev/null +++ b/bitacross-worker/core/peer-top-broadcaster/Cargo.toml @@ -0,0 +1,47 @@ +[package] +name = "itc-peer-top-broadcaster" +version = "0.1.0" +authors = ['Trust Computing GmbH '] +edition = "2021" + +[dependencies] +# sgx dependencies +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } + +# no-std dependencies +log = { version = "0.4", default-features = false } + +# local dependencies +itc-direct-rpc-client = { path = "../direct-rpc-client", default-features = false } +itc-direct-rpc-server = { path = "../direct-rpc-server", default-features = false } +itp-rpc = { path = "../../core-primitives/rpc", default-features = false } +itp-stf-primitives = { path = "../../core-primitives/stf-primitives", default-features = false } +itp-types = { path = "../../core-primitives/types", default-features = false } +itp-utils = { path = "../../core-primitives/utils", default-features = false } + +# litentry +litentry-primitives = { path = "../../litentry/primitives", default-features = false } + +[dev-dependencies] +itc-direct-rpc-server = { path = "../direct-rpc-server", default-features = false, features = ["mocks"] } + + +[features] +default = ["std"] +sgx = [ + "sgx_tstd", + "itc-direct-rpc-server/sgx", + "itc-direct-rpc-client/sgx", + "itp-rpc/sgx", + "litentry-primitives/sgx", +] +std = [ + "itp-stf-primitives/std", + "itp-types/std", + "itp-utils/std", + "log/std", + "itc-direct-rpc-server/std", + "itc-direct-rpc-client/std", + "itp-rpc/std", + "litentry-primitives/std", +] diff --git a/bitacross-worker/core/peer-top-broadcaster/src/lib.rs b/bitacross-worker/core/peer-top-broadcaster/src/lib.rs new file mode 100644 index 0000000000..eef091de21 --- /dev/null +++ b/bitacross-worker/core/peer-top-broadcaster/src/lib.rs @@ -0,0 +1,374 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +extern crate alloc; +extern crate core; +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +use alloc::vec; +use log::error; +#[cfg(feature = "sgx")] +use std::sync::SgxMutex as Mutex; + +#[cfg(feature = "std")] +use std::sync::Mutex; + +use itc_direct_rpc_client::{DirectRpcClientFactory, Response, RpcClient, RpcClientFactory}; +use itc_direct_rpc_server::{ + response_channel::ResponseChannel, rpc_responder::RpcResponder, RpcConnectionRegistry, + SendRpcResponse, +}; +use itp_rpc::{Id, RpcRequest}; +use itp_stf_primitives::types::Hash; +use itp_types::{DirectRequestStatus, TrustedOperationStatus, H256}; +use itp_utils::FromHexPrefixed; +use litentry_primitives::BroadcastedRequest; +use std::{ + collections::HashMap, + string::{String, ToString}, + sync::{ + mpsc::{sync_channel, SyncSender}, + Arc, + }, + vec::Vec, +}; + +pub type MaybeRequestIdWithParams = Option<(Hash, Vec)>; + +pub trait PeerUpdater { + fn update(&self, peers: Vec); +} + +pub struct DirectRpcBroadcaster +where + ClientFactory: RpcClientFactory, +{ + peers: Mutex>, + responses_sender: SyncSender, + factory: ClientFactory, +} + +impl DirectRpcBroadcaster +where + ClientFactory: RpcClientFactory, +{ + pub fn new( + peers: &[&str], + client_factory: ClientFactory, + rpc_responder: Arc>, + ) -> Self + where + Registry: RpcConnectionRegistry + 'static, + ResponseChannelType: ResponseChannel + 'static, + { + let (responses_sender, responses_receiver) = sync_channel(1000); + let mut peers_map = HashMap::new(); + for peer in peers { + match client_factory.create(peer, responses_sender.clone()) { + Ok(client) => { + peers_map.insert(peer.to_string(), client); + }, + Err(e) => log::error!("Could not connect to peer {}, reason: {:?}", peer, e), + } + } + + std::thread::spawn(move || { + while let Ok((id, rpc_return_value)) = responses_receiver.recv() { + match rpc_return_value.status { + DirectRequestStatus::TrustedOperationStatus(status, _) => { + //we need to map Id to hash in order to correlate it with connection + let hash = match id_to_hash(&id) { + Some(hash) => hash, + None => continue, + }; + match status { + // this will come from every peer so do not flood the client + TrustedOperationStatus::Submitted => {}, + // this needs to come before block is imported, otherwise it's going to be ignored because TOP will be removed from the pool after block import + TrustedOperationStatus::TopExecuted(ref value, force_wait) => { + match rpc_responder.update_connection_state( + hash, + value.clone(), + force_wait, + ) { + Ok(_) => {}, + Err(e) => log::error!( + "Could not set connection {}, reason: {:?}", + hash, + e + ), + }; + if let Err(_e) = rpc_responder.update_status_event(hash, status) { + error!("Could not update status for {}", &hash) + }; + }, + _ => { + //as long as we are waiting let's ignore all status events. + if !rpc_responder.is_force_wait(hash) { + if let Err(_e) = rpc_responder.update_status_event(hash, status) + { + }; + } + }, + } + }, + + DirectRequestStatus::Ok | DirectRequestStatus::Error => { + log::warn!( + "Got unexpected direct request status: {:?}", + rpc_return_value.status + ); + }, + } + } + }); + + DirectRpcBroadcaster { + peers: Mutex::new(peers_map), + responses_sender, + factory: client_factory, + } + } + + fn new_clear_peer_map(&self) -> HashMap { + HashMap::new() + } + + pub fn broadcast(&self, request: BroadcastedRequest) { + if let Ok(mut peers) = self.peers.lock() { + let request = RpcRequest { + jsonrpc: "2.0".to_string(), + method: request.rpc_method.clone(), + params: vec![request.payload.clone()], + id: Id::Text(request.id), + }; + peers.values_mut().for_each(|peer| { + if let Err(e) = peer.send(&request) { + log::warn!("Could not send top to peer reason: {:?}", e); + } + }); + } + } + + fn connect_to(&self, url: &str, peer_list: &mut HashMap) { + match self.factory.create(url, self.responses_sender.clone()) { + Ok(client) => { + peer_list.insert(url.to_string(), client); + }, + Err(e) => log::error!("Could not connect to peer {}, reason: {:?}", url, e), + } + } +} + +pub fn id_to_hash(id: &Id) -> Option { + match id { + Id::Text(id) => H256::from_hex(id).ok(), + Id::Number(id) => { + log::error!("Got response with id {}", id); + None + }, + } +} + +#[allow(clippy::type_complexity)] +pub fn init( + rpc_responder: Arc>, +) -> ( + Arc>, + Arc>, +) +where + Registry: RpcConnectionRegistry + 'static, + ResponseChannelType: ResponseChannel + 'static, +{ + let (sender, receiver) = std::sync::mpsc::sync_channel::(1000); + + let peers = vec![]; + + let client_factory = DirectRpcClientFactory {}; + + let rpc_broadcaster = + Arc::new(DirectRpcBroadcaster::new(&peers, client_factory, rpc_responder)); + let return_rpc_broadcaster = rpc_broadcaster.clone(); + + std::thread::spawn(move || { + for received in receiver { + rpc_broadcaster.broadcast(received); + } + }); + + (Arc::new(sender), return_rpc_broadcaster) +} + +impl PeerUpdater for DirectRpcBroadcaster +where + ClientFactory: RpcClientFactory, +{ + // created new map filled with rpc clients connected to peer from the provided list. Reuses existing + // connections. The list will not containt peers that are unreachable, so following logic will automatically + // remove all dead connections + fn update(&self, peers: Vec) { + log::debug!("Updating peers: {:?}", &peers); + let mut new_peers_list = self.new_clear_peer_map(); + for peer in peers { + if let Ok(mut peers) = self.peers.lock() { + if !peers.contains_key(&peer) { + log::info!("Adding a peer: {}", peer.clone()); + self.connect_to(&peer, &mut new_peers_list) + } else { + log::info!("Reusing existing peer: {}", peer.clone()); + //this is safe as we previously ensured that map contains such key + let peer_to_move = peers.remove(&peer).unwrap(); + new_peers_list.insert(peer, peer_to_move); + } + } + } + if let Ok(mut peers) = self.peers.lock() { + *peers = new_peers_list; + } + } +} + +#[cfg(test)] +pub mod tests { + use crate::{DirectRpcBroadcaster, PeerUpdater}; + use alloc::sync::Arc; + use itc_direct_rpc_client::{Response, RpcClient, RpcClientFactory}; + use itc_direct_rpc_server::{ + mocks::response_channel_mock::ResponseChannelMock, + rpc_connection_registry::ConnectionRegistry, rpc_responder::RpcResponder, + }; + use itp_rpc::{Id, RpcRequest, RpcReturnValue}; + use itp_stf_primitives::types::Hash; + use itp_types::H256; + use itp_utils::ToHexPrefixed; + use litentry_primitives::BroadcastedRequest; + use std::{error::Error, sync::mpsc::SyncSender}; + + type TestConnectionToken = u64; + type TestResponseChannel = ResponseChannelMock; + type TestConnectionRegistry = ConnectionRegistry; + + #[derive(Default)] + pub struct MockedRpcClient { + pub sent_requests: u64, + pub response: Option<(Id, RpcReturnValue)>, + } + + impl RpcClient for MockedRpcClient { + fn send(&mut self, _request: &RpcRequest) -> Result<(), Box> { + self.sent_requests = self.sent_requests + 1; + Ok(()) + } + } + + impl MockedRpcClient { + pub fn set_response(&mut self, response: (Id, RpcReturnValue)) { + self.response = Some(response) + } + } + + pub struct MockedRpcClientFactory {} + + impl RpcClientFactory for MockedRpcClientFactory { + type Client = MockedRpcClient; + + fn create( + &self, + _url: &str, + _response_sink: SyncSender, + ) -> Result> { + Ok(MockedRpcClient::default()) + } + } + + #[test] + pub fn creates_initial_peers() { + //given + let factory = MockedRpcClientFactory {}; + let connection_registry = Arc::new(TestConnectionRegistry::new()); + let websocket_responder = Arc::new(TestResponseChannel::default()); + let rpc_responder = Arc::new(RpcResponder::new(connection_registry, websocket_responder)); + + //when + let broadcaster: DirectRpcBroadcaster = + DirectRpcBroadcaster::new(&vec!["localhost"], factory, rpc_responder); + + //then + assert_eq!(broadcaster.peers.lock().unwrap().len(), 1); + } + + #[test] + pub fn broadcast_sends_to_all_peers() { + //given + let factory = MockedRpcClientFactory {}; + let connection_registry = Arc::new(TestConnectionRegistry::new()); + let websocket_responder = Arc::new(TestResponseChannel::default()); + let rpc_responder = Arc::new(RpcResponder::new(connection_registry, websocket_responder)); + + let broadcaster: DirectRpcBroadcaster = + DirectRpcBroadcaster::new(&vec!["localhost", "localhost2"], factory, rpc_responder); + + //when + broadcaster.broadcast(BroadcastedRequest { + id: Hash::random().to_hex(), + payload: Hash::random().to_hex(), + rpc_method: "submit_and_broadcast".to_string(), + }); + broadcaster.broadcast(BroadcastedRequest { + id: Hash::random().to_hex(), + payload: Hash::random().to_hex(), + rpc_method: "submit_and_broadcast".to_string(), + }); + + //then + let peers = broadcaster.peers.lock().unwrap(); + for peer in peers.iter() { + assert_eq!(peer.1.sent_requests, 2u64) + } + } + + #[test] + pub fn updates_list_correctly() { + //given + let retained_peer = "localhost"; + let added_peer = "localhost3"; + let removed_peer = "localhost2"; + + let factory = MockedRpcClientFactory {}; + let connection_registry = Arc::new(TestConnectionRegistry::new()); + let websocket_responder = Arc::new(TestResponseChannel::default()); + let rpc_responder = Arc::new(RpcResponder::new(connection_registry, websocket_responder)); + + let broadcaster: DirectRpcBroadcaster = + DirectRpcBroadcaster::new(&vec![retained_peer, removed_peer], factory, rpc_responder); + + //when + broadcaster.update(vec![retained_peer.to_string(), added_peer.to_string()]); + + //then + let peers = broadcaster.peers.lock().unwrap(); + assert!(peers.get(retained_peer).is_some()); + assert!(peers.get(added_peer).is_some()); + assert!(peers.get(removed_peer).is_none()); + } +} diff --git a/bitacross-worker/core/rest-client/Cargo.toml b/bitacross-worker/core/rest-client/Cargo.toml new file mode 100644 index 0000000000..668ecc4b04 --- /dev/null +++ b/bitacross-worker/core/rest-client/Cargo.toml @@ -0,0 +1,47 @@ +[package] +name = "itc-rest-client" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +# std dependencies +http = { version = "0.2", optional = true } +http_req = { optional = true, features = ["rust-tls"], branch = "master", git = "https://github.com/integritee-network/http_req" } +thiserror = { version = "1.0.26", optional = true } +url = { version = "2.0.0", optional = true } + +# sgx dependencies +http-sgx = { package = "http", git = "https://github.com/integritee-network/http-sgx.git", branch = "sgx-experimental", optional = true } +http_req-sgx = { optional = true, default-features = false, features = ["rust-tls", "sgx"], package = "http_req", git = "https://github.com/integritee-network/http_req" } +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true, features = ["net", "thread"] } +thiserror_sgx = { package = "thiserror", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3", optional = true } +url_sgx = { package = "url", git = "https://github.com/mesalock-linux/rust-url-sgx", tag = "sgx_1.1.3", optional = true } + +# no_std dependencies +base64 = { version = "0.13", default-features = false, features = ["alloc"] } +log = { version = "0.4", default-features = false } +serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] } +serde_json = { version = "1.0", default-features = false, features = ["alloc"] } + +[features] +default = ["std"] +std = [ + # std only + "http", + "http_req", + "thiserror", + "url", + # no_std + "base64/std", + "serde/std", + "serde_json/std", + "log/std", +] +sgx = [ + "http-sgx", + "http_req-sgx", + "sgx_tstd", + "thiserror_sgx", + "url_sgx", +] diff --git a/bitacross-worker/core/rest-client/src/error.rs b/bitacross-worker/core/rest-client/src/error.rs new file mode 100644 index 0000000000..8dea50ccfc --- /dev/null +++ b/bitacross-worker/core/rest-client/src/error.rs @@ -0,0 +1,58 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use std::string::String; + +/// REST client error +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("HTTP client creation failed")] + HttpClientError, + + #[error("Failed to parse final URL.")] + UrlError, + + #[error("Failed to serialize struct to JSON (in POST): {0}")] + SerializeParseError(serde_json::Error), + + #[error("Failed to deserialize data to struct (in GET or POST response: {0} {1}")] + DeserializeParseError(serde_json::Error, String), + + #[error("Failed to make the outgoing request")] + RequestError, + + #[error("HTTP header error: {0}")] + HttpHeaderError(http::header::ToStrError), + + #[error(transparent)] + HttpReqError(#[from] http_req::error::Error), + + #[error("Failed to perform IO operation: {0}")] + IoError(std::io::Error), + + #[error("Server returned non-success status: {0}, details: {1}")] + HttpError(u16, String), + + #[error("Request has timed out")] + TimeoutError, + + #[error("Invalid parameter value")] + InvalidValue, +} diff --git a/bitacross-worker/core/rest-client/src/fixtures/amazon_root_ca_1_v3.pem b/bitacross-worker/core/rest-client/src/fixtures/amazon_root_ca_1_v3.pem new file mode 100644 index 0000000000..a6f3e92af5 --- /dev/null +++ b/bitacross-worker/core/rest-client/src/fixtures/amazon_root_ca_1_v3.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF +ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 +b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv +b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj +ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM +9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw +IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 +VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L +93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm +jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA +A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI +U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs +N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv +o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU +5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy +rqXRfboQnoZsG4q5WTP468SQvvG5 +-----END CERTIFICATE----- diff --git a/bitacross-worker/core/rest-client/src/fixtures/baltimore_cyber_trust_root_v3.pem b/bitacross-worker/core/rest-client/src/fixtures/baltimore_cyber_trust_root_v3.pem new file mode 100644 index 0000000000..519028c63b --- /dev/null +++ b/bitacross-worker/core/rest-client/src/fixtures/baltimore_cyber_trust_root_v3.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX +DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y +ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy +VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr +mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr +IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK +mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu +XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy +dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye +jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 +BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 +DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 +9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx +jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 +Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz +ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS +R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE----- diff --git a/bitacross-worker/core/rest-client/src/fixtures/lets_encrypt_root_cert.pem b/bitacross-worker/core/rest-client/src/fixtures/lets_encrypt_root_cert.pem new file mode 100644 index 0000000000..57d4a3766c --- /dev/null +++ b/bitacross-worker/core/rest-client/src/fixtures/lets_encrypt_root_cert.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/bitacross-worker/core/rest-client/src/http_client.rs b/bitacross-worker/core/rest-client/src/http_client.rs new file mode 100644 index 0000000000..e45f6a3c88 --- /dev/null +++ b/bitacross-worker/core/rest-client/src/http_client.rs @@ -0,0 +1,584 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use crate::{error::Error, Query, RestPath}; +use http::{ + header::{HeaderName, AUTHORIZATION, CONTENT_LENGTH, CONTENT_TYPE, USER_AGENT}, + HeaderValue, +}; +use http_req::{ + request::{Method, Request}, + response::{Headers, Response}, + tls::Config, + uri::Uri, +}; +use log::*; +use std::{ + collections::HashMap, + convert::TryFrom, + str::FromStr, + string::{String, ToString}, + time::Duration, + vec::Vec, +}; +use url::Url; + +pub type EncodedBody = Vec; + +/// Simple trait to send HTTP request +pub trait SendHttpRequest { + fn send_request( + &self, + base_url: Url, + method: Method, + params: U, + query: Option<&Query<'_>>, + maybe_body: Option, + ) -> Result<(Response, EncodedBody), Error> + where + T: RestPath; +} + +/// Send trait used by the http client to send HTTP request, based on `http_req`. +pub trait Send { + fn execute_send_request( + &self, + request: &mut Request, + writer: &mut Vec, + ) -> Result; +} + +/// HTTP client implementation +/// +/// wrapper for the `http_req` library that adds the necessary headers and body to a request +pub struct HttpClient { + send: SendType, + send_null_body: bool, + timeout: Option, + headers: Headers, + authorization: Option, +} + +/// Default send method. +/// Automatically upgrades to TLS in case the base URL contains 'https' +/// For https requests, the default trusted server's certificates +/// are provided by the default tls configuration of the http_req lib +pub struct DefaultSend; + +impl Send for DefaultSend { + fn execute_send_request( + &self, + request: &mut Request, + writer: &mut Vec, + ) -> Result { + request.send(writer).map_err(Error::HttpReqError) + } +} + +/// Sends a HTTPs request with the server's root certificate(s). +/// The connection will only be established if one of the supplied certificates +/// matches the server's root certificate. +pub struct SendWithCertificateVerification { + root_certificates: Vec, +} + +impl SendWithCertificateVerification { + pub fn new(root_certificates: Vec) -> Self { + SendWithCertificateVerification { root_certificates } + } +} + +impl Send for SendWithCertificateVerification { + fn execute_send_request( + &self, + request: &mut Request, + writer: &mut Vec, + ) -> Result { + let mut cnf = Config::default(); + for cert in self.root_certificates.iter() { + cnf.add_root_cert_content_pem_file(cert)?; + } + + match request.send_with_config(writer, Some(&cnf)) { + Ok(response) => Ok(response), + Err(e) => { + error!( + "SendWithCertificateVerification::execute_send_request received error: {:?}", + &e + ); + Err(Error::HttpReqError(e)) + }, + } + } +} + +impl HttpClient +where + SendType: Send, +{ + pub fn new( + send: SendType, + send_null_body: bool, + timeout: Option, + headers: Option, + authorization: Option, + ) -> Self { + HttpClient { + send, + send_null_body, + timeout, + headers: headers.unwrap_or_else(Headers::new), + authorization, + } + } + + /// Set credentials for HTTP Basic authentication. + pub fn set_auth(&mut self, user: &str, pass: &str) { + let mut s: String = user.to_string(); + s.push(':'); + s.push_str(pass); + self.authorization = Some(format!("Basic {}", base64::encode(&s))); + } + + /// Set HTTP header from string name and value. + /// + /// The header is added to all subsequent GET and POST requests + /// unless the headers are cleared with `clear_headers()` call. + pub fn set_header(&mut self, name: &'static str, value: &str) -> Result<(), Error> { + let header_name = HeaderName::from_str(name).map_err(|_| Error::InvalidValue)?; + let value = HeaderValue::from_str(value).map_err(|_| Error::InvalidValue)?; + + add_to_headers(&mut self.headers, header_name, value); + Ok(()) + } + + /// Clear all previously set headers + pub fn clear_headers(&mut self) { + self.headers = Headers::new(); + } +} + +impl SendHttpRequest for HttpClient +where + SendType: Send, +{ + fn send_request( + &self, + base_url: Url, + method: Method, + params: U, + query: Option<&Query<'_>>, + maybe_body: Option, + ) -> Result<(Response, EncodedBody), Error> + where + T: RestPath, + { + let url = join_url(base_url, T::get_path(params)?.as_str(), query)?; + let uri = Uri::try_from(url.as_str()).map_err(Error::HttpReqError)?; + + trace!("uri: {:?}", uri); + + let mut request = Request::new(&uri); + request.method(method); + + let mut request_headers = Headers::default_http(&uri); + + if let Some(body) = maybe_body.as_ref() { + if self.send_null_body || body != "null" { + let len = HeaderValue::from_str(&body.len().to_string()) + .map_err(|_| Error::RequestError)?; + + add_to_headers(&mut request_headers, CONTENT_LENGTH, len); + add_to_headers( + &mut request_headers, + CONTENT_TYPE, + HeaderValue::from_str("application/json") + .expect("Request Header: invalid characters"), + ); + + trace!("set request body: {}", body); + request.body(body.as_bytes()); // takes body non-owned (!) + } + } else { + debug!("no body to send"); + } + + if let Some(ref auth) = self.authorization { + add_to_headers( + &mut request_headers, + AUTHORIZATION, + HeaderValue::from_str(auth).map_err(|_| Error::RequestError)?, + ); + } + + // add pre-set headers + for (key, value) in self.headers.iter() { + request_headers.insert(key, &value.clone()); + } + + // add user agent header + let pkg_version = env!("CARGO_PKG_VERSION"); + add_to_headers( + &mut request_headers, + USER_AGENT, + HeaderValue::from_str(format!("integritee/{}", pkg_version).as_str()) + .map_err(|_| Error::RequestError)?, + ); + + request.headers(HashMap::from(request_headers)); + + request + .timeout(self.timeout) + .connect_timeout(self.timeout) + .read_timeout(self.timeout) + .write_timeout(self.timeout); + + trace!("request is: {:?}", request); + + let mut writer = Vec::new(); + + let response = self.send.execute_send_request(&mut request, &mut writer)?; + + Ok((response, writer)) + } +} + +fn join_url(base_url: Url, path: &str, params: Option<&Query>) -> Result { + let mut url = base_url.join(path).map_err(|_| Error::UrlError)?; + + if let Some(params) = params { + for &(key, item) in params.iter() { + url.query_pairs_mut().append_pair(key, item); + } + } + + Ok(url) +} + +fn add_to_headers(headers: &mut Headers, key: HeaderName, value: HeaderValue) { + let header_value_str = value.to_str(); + + match header_value_str { + Ok(v) => { + headers.insert(key.as_str(), v); + }, + Err(e) => { + error!("Failed to add header to request: {:?}", e); + }, + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use core::assert_matches::assert_matches; + use http::header::CONNECTION; + use serde::{Deserialize, Serialize}; + use std::vec::Vec; + + const HTTPBIN_ROOT_CERT: &str = include_str!("fixtures/amazon_root_ca_1_v3.pem"); + const COINGECKO_ROOT_CERTIFICATE_BALTIMORE: &str = + include_str!("fixtures/baltimore_cyber_trust_root_v3.pem"); + const COINGECKO_ROOT_CERTIFICATE_LETSENCRYPT: &str = + include_str!("fixtures/lets_encrypt_root_cert.pem"); + + #[test] + fn join_url_adds_query_parameters() { + let base_url = Url::parse("https://example.com").unwrap(); + let path = "api/v2/example_list"; + let query = [("filter", "all"), ("order", ("desc"))]; + + let complete_url = join_url(base_url, path, Some(&query)).unwrap(); + + assert_eq!( + complete_url.as_str(), + "https://example.com/api/v2/example_list?filter=all&order=desc" + ); + } + + #[test] + fn join_url_has_no_query_parameters() { + let base_url = Url::parse("https://example.com").unwrap(); + let path = "api/v2/endpoint"; + let complete_url = join_url(base_url, path, None).unwrap(); + assert_eq!(complete_url.as_str(), "https://example.com/api/v2/endpoint"); + } + + #[test] + fn join_url_with_too_many_slashes() { + let base_url = Url::parse("https://api.mydomain.com").unwrap(); + let path = "/api/v1/post"; + let complete_url = join_url(base_url, path, None).unwrap(); + assert_eq!(complete_url.as_str(), "https://api.mydomain.com/api/v1/post"); + } + + #[test] + #[ignore = "depends on external web-service that proved to be unreliable for CI"] + fn get_with_parameters() { + #[derive(Serialize, Deserialize, Debug)] + struct RequestArgs { + pub order: String, + pub filter: String, + } + + // Data structure that matches with REST API JSON + #[derive(Serialize, Deserialize, Debug)] + struct HttpBinAnything { + pub args: RequestArgs, + pub origin: String, + pub url: String, + } + + impl RestPath<()> for HttpBinAnything { + fn get_path(_: ()) -> Result { + Ok(format!("anything")) + } + } + + let http_client = HttpClient::new( + DefaultSend {}, + true, + Some(Duration::from_secs(3u64)), + Some(headers_connection_close()), + None, + ); + let base_url = Url::parse("https://httpbin.org").unwrap(); + let query_parameters = [("order", "desc"), ("filter", "all")]; + + let (response, encoded_body) = http_client + .send_request::<(), HttpBinAnything>( + base_url, + Method::GET, + (), + Some(&query_parameters), + None, + ) + .unwrap(); + + let response_body: HttpBinAnything = + deserialize_response_body(encoded_body.as_slice()).unwrap(); + + assert!(response.status_code().is_success()); + assert_eq!(response_body.args.order.as_str(), "desc"); + assert_eq!(response_body.args.filter.as_str(), "all"); + } + + #[test] + #[ignore = "depends on external web-service that proved to be unreliable for CI"] + fn get_without_parameters() { + // Data structure that matches with REST API JSON + #[derive(Serialize, Deserialize, Debug)] + struct HttpBinAnything { + pub method: String, + pub url: String, + } + + impl RestPath<()> for HttpBinAnything { + fn get_path(_: ()) -> Result { + Ok(format!("anything")) + } + } + + let http_client = HttpClient::new( + DefaultSend {}, + true, + Some(Duration::from_secs(3u64)), + Some(headers_connection_close()), + None, + ); + let base_url = Url::parse("https://httpbin.org").unwrap(); + + let (response, encoded_body) = http_client + .send_request::<(), HttpBinAnything>(base_url, Method::GET, (), None, None) + .unwrap(); + + let response_body: HttpBinAnything = + deserialize_response_body(encoded_body.as_slice()).unwrap(); + + assert!(response.status_code().is_success()); + assert!(!response_body.url.is_empty()); + assert_eq!(response_body.method.as_str(), "GET"); + } + + #[test] + #[ignore = "depends on external web-service that proved to be unreliable for CI"] + fn post_with_body() { + #[derive(Serialize, Deserialize, Debug)] + struct HttpBinAnything { + pub data: String, + pub method: String, + } + + impl RestPath<()> for HttpBinAnything { + fn get_path(_: ()) -> Result { + Ok(format!("anything")) + } + } + + let http_client = HttpClient::new( + DefaultSend {}, + false, + Some(Duration::from_secs(3u64)), + Some(headers_connection_close()), + None, + ); + + let body_test = "this is a test body with special characters {::}/-".to_string(); + let base_url = Url::parse("https://httpbin.org").unwrap(); + + let (response, encoded_body) = http_client + .send_request::<(), HttpBinAnything>( + base_url, + Method::POST, + (), + None, + Some(body_test.clone()), + ) + .unwrap(); + + let response_body: HttpBinAnything = + deserialize_response_body(encoded_body.as_slice()).unwrap(); + + assert!(response.status_code().is_success()); + assert_eq!(response_body.method.as_str(), "POST"); + assert_eq!(response_body.data, body_test); + } + + #[test] + #[ignore = "depends on external web-service that proved to be unreliable for CI"] + fn get_coins_list_from_coin_gecko_works() { + // Data structure that matches with REST API JSON + #[derive(Serialize, Deserialize, Debug)] + struct CoinGeckoCoinsList { + id: String, + symbol: String, + name: String, + } + + impl RestPath<()> for Vec { + fn get_path(_: ()) -> Result { + Ok(format!("api/v3/coins/list")) + } + } + + let http_client = + HttpClient::new(DefaultSend {}, true, Some(Duration::from_secs(3u64)), None, None); + let base_url = Url::parse("https://api.coingecko.com").unwrap(); + + let (response, encoded_body) = http_client + .send_request::<(), Vec>(base_url, Method::GET, (), None, None) + .unwrap(); + + let coins_list: Vec = + deserialize_response_body(encoded_body.as_slice()).unwrap(); + + assert!(response.status_code().is_success()); + assert!(!coins_list.is_empty()); + } + + #[test] + #[ignore = "depends on external web-service that proved to be unreliable for CI"] + fn authenticated_get_works() { + #[derive(Serialize, Deserialize, Debug)] + struct HttpBinAnything { + pub method: String, + pub url: String, + } + + impl RestPath<()> for HttpBinAnything { + fn get_path(_: ()) -> Result { + Ok(format!("anything")) + } + } + let base_url = Url::parse("https://httpbin.org").unwrap(); + let root_certificate = HTTPBIN_ROOT_CERT.to_string(); + + let http_client = HttpClient::new( + SendWithCertificateVerification::new(vec![root_certificate]), + true, + Some(Duration::from_secs(3u64)), + Some(headers_connection_close()), + None, + ); + + let (response, encoded_body) = http_client + .send_request::<(), HttpBinAnything>(base_url, Method::GET, (), None, None) + .unwrap(); + + let response_body: HttpBinAnything = + deserialize_response_body(encoded_body.as_slice()).unwrap(); + + assert!(response.status_code().is_success()); + assert!(!response_body.url.is_empty()); + assert_eq!(response_body.method.as_str(), "GET"); + } + + #[test] + #[ignore = "depends on external web-service that proved to be unreliable for CI"] + fn authenticated_get_with_wrong_root_certificate_fails() { + #[derive(Serialize, Deserialize, Debug)] + struct HttpBinAnything { + pub method: String, + pub url: String, + } + + impl RestPath<()> for HttpBinAnything { + fn get_path(_: ()) -> Result { + Ok(format!("anything")) + } + } + + let base_url = Url::parse("https://httpbin.org").unwrap(); + let root_certificates = vec![ + COINGECKO_ROOT_CERTIFICATE_LETSENCRYPT.to_string(), + COINGECKO_ROOT_CERTIFICATE_BALTIMORE.to_string(), + ]; + + let http_client = HttpClient::new( + SendWithCertificateVerification::new(root_certificates), + true, + Some(Duration::from_secs(3u64)), + Some(headers_connection_close()), + None, + ); + + let result = + http_client.send_request::<(), HttpBinAnything>(base_url, Method::GET, (), None, None); + assert_matches!(result, Err(Error::HttpReqError(_))); + let msg = format!("error {:?}", result.err()); + assert!(msg.contains("UnknownIssuer")); + } + + fn headers_connection_close() -> Headers { + let mut headers = Headers::new(); + add_to_headers(&mut headers, CONNECTION, HeaderValue::from_str("close").unwrap()); + headers + } + + fn deserialize_response_body<'a, T>(encoded_body: &'a [u8]) -> Result + where + T: Deserialize<'a>, + { + serde_json::from_slice::<'a, T>(encoded_body).map_err(|err| { + Error::DeserializeParseError(err, String::from_utf8_lossy(encoded_body).to_string()) + }) + } +} diff --git a/bitacross-worker/core/rest-client/src/http_client_builder.rs b/bitacross-worker/core/rest-client/src/http_client_builder.rs new file mode 100644 index 0000000000..1b51fc51a8 --- /dev/null +++ b/bitacross-worker/core/rest-client/src/http_client_builder.rs @@ -0,0 +1,112 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use crate::{http_client, http_client::HttpClient}; +use http_req::response::Headers; +use std::{string::String, time::Duration}; + +/// Builder for `HttpClient` +pub struct HttpClientBuilder { + send: SendType, + + /// Request timeout + timeout: Duration, + + /// Send null body + send_null_body: bool, + + /// pre-set headers + headers: Option, + + /// authorization + authorization: Option, +} + +impl Default for HttpClientBuilder +where + SendType: Default, +{ + fn default() -> Self { + Self { + send: SendType::default(), + timeout: Duration::from_secs(u64::MAX), + send_null_body: true, + headers: None, + authorization: None, + } + } +} + +impl HttpClientBuilder +where + SendType: http_client::Send, +{ + /// Set send method. + /// + /// Default is calling the default send of http-req lib: all Mozilla's root certificates + /// are trusted. + pub fn send(mut self, send: SendType) -> Self { + self.send = send; + self + } + + /// Set request timeout + /// + /// Default is no timeout + pub fn timeout(mut self, timeout: Duration) -> Self { + self.timeout = timeout; + self + } + + /// Send null body in POST/PUT + /// + /// Default is yes + pub fn send_null_body(mut self, value: bool) -> Self { + self.send_null_body = value; + self + } + + /// Pre-set headers to attach to each request + /// + /// default is none + pub fn headers(mut self, headers: Headers) -> Self { + self.headers = Some(headers); + self + } + + /// Basic HTTP authorization (format: `username:password`) + /// + /// default is none + pub fn authorization(mut self, authorization: String) -> Self { + self.authorization = Some(authorization); + self + } + + /// Create `HttpClient` with the configuration in this builder + pub fn build(self) -> HttpClient { + HttpClient::::new( + self.send, + self.send_null_body, + Some(self.timeout), + self.headers, + self.authorization, + ) + } +} diff --git a/bitacross-worker/core/rest-client/src/lib.rs b/bitacross-worker/core/rest-client/src/lib.rs new file mode 100644 index 0000000000..8a397cefb8 --- /dev/null +++ b/bitacross-worker/core/rest-client/src/lib.rs @@ -0,0 +1,182 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! REST API Client, supporting SSL/TLS + +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(test, feature(assert_matches))] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +#[macro_use] +extern crate sgx_tstd as std; + +// re-export module to properly feature gate sgx and regular std environment +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod sgx_reexport_prelude { + pub use http_req_sgx as http_req; + pub use http_sgx as http; + pub use thiserror_sgx as thiserror; + pub use url_sgx as url; +} + +pub mod error; +pub mod http_client; +pub mod http_client_builder; +pub mod rest_client; + +#[cfg(test)] +pub mod mocks; + +use crate::error::Error; +use std::string::String; + +/// Type for URL query parameters. +/// +/// Slice of tuples in which the first field is parameter name and second is value. +/// These parameters are used with `get_with` and `post_with` functions. +/// +/// # Examples +/// The vector +/// ```ignore +/// vec![("param1", "1234"), ("param2", "abcd")] +/// ``` +/// would be parsed to **param1=1234¶m2=abcd** in the request URL. +pub type Query<'a> = [(&'a str, &'a str)]; + +/// Rest path builder trait for type. +/// +/// Provides implementation for `rest_path` function that builds +/// type (and REST endpoint) specific API path from given parameter(s). +/// The built REST path is appended to the base URL given to `RestClient`. +/// If `Err` is returned, it is propagated directly to API caller. +pub trait RestPath { + /// Construct type specific REST API path from given parameters + /// (e.g. "api/devices/1234"). + fn get_path(par: T) -> Result; +} + +/// REST HTTP GET trait +/// +/// Provides the GET verb for a REST API +pub trait RestGet { + /// Plain GET request + fn get(&mut self, params: U) -> Result + where + T: serde::de::DeserializeOwned + RestPath; + + /// GET request with query parameters. + fn get_with(&mut self, params: U, query: &Query<'_>) -> Result + where + T: serde::de::DeserializeOwned + RestPath; +} + +/// REST HTTP POST trait +/// +/// Provides the POST verb for a REST API +pub trait RestPost { + /// Plain POST request. + fn post(&mut self, params: U, data: &T) -> Result<(), Error> + where + T: serde::Serialize + RestPath; + + /// Make POST request with query parameters. + fn post_with(&mut self, params: U, data: &T, query: &Query<'_>) -> Result<(), Error> + where + T: serde::Serialize + RestPath; + + /// Make a POST request and capture returned body. + fn post_capture(&mut self, params: U, data: &T) -> Result + where + T: serde::Serialize + RestPath, + K: serde::de::DeserializeOwned; + + /// Make a POST request with query parameters and capture returned body. + fn post_capture_with( + &mut self, + params: U, + data: &T, + query: &Query<'_>, + ) -> Result + where + T: serde::Serialize + RestPath, + K: serde::de::DeserializeOwned; +} + +/// REST HTTP PUT trait +/// +/// Provides the PUT verb for a REST API +pub trait RestPut { + /// PUT request. + fn put(&mut self, params: U, data: &T) -> Result<(), Error> + where + T: serde::Serialize + RestPath; + + /// Make PUT request with query parameters. + fn put_with(&mut self, params: U, data: &T, query: &Query<'_>) -> Result<(), Error> + where + T: serde::Serialize + RestPath; + + /// Make a PUT request and capture returned body. + fn put_capture(&mut self, params: U, data: &T) -> Result + where + T: serde::Serialize + RestPath, + K: serde::de::DeserializeOwned; + + /// Make a PUT request with query parameters and capture returned body. + fn put_capture_with( + &mut self, + params: U, + data: &T, + query: &Query<'_>, + ) -> Result + where + T: serde::Serialize + RestPath, + K: serde::de::DeserializeOwned; +} + +/// REST HTTP PATCH trait +/// +/// Provides the PATCH verb for a REST API +pub trait RestPatch { + /// Make a PATCH request. + fn patch(&mut self, params: U, data: &T) -> Result<(), Error> + where + T: serde::Serialize + RestPath; + + /// Make PATCH request with query parameters. + fn patch_with(&mut self, params: U, data: &T, query: &Query<'_>) -> Result<(), Error> + where + T: serde::Serialize + RestPath; +} + +/// REST HTTP DELETE trait +/// +/// Provides the DELETE verb for a REST API +pub trait RestDelete { + /// Make a DELETE request. + fn delete(&mut self, params: U) -> Result<(), Error> + where + T: RestPath; + + /// Make a DELETE request with query and body. + fn delete_with(&mut self, params: U, data: &T, query: &Query<'_>) -> Result<(), Error> + where + T: serde::Serialize + RestPath; +} diff --git a/bitacross-worker/core/rest-client/src/mocks/http_client_mock.rs b/bitacross-worker/core/rest-client/src/mocks/http_client_mock.rs new file mode 100644 index 0000000000..454165ac39 --- /dev/null +++ b/bitacross-worker/core/rest-client/src/mocks/http_client_mock.rs @@ -0,0 +1,144 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + error::Error, + http_client::{EncodedBody, SendHttpRequest}, + Query, RestPath, +}; +use http_req::{request::Method, response::Response}; +use serde::{Deserialize, Serialize}; +use url::Url; + +const DEFAULT_HEAD: &[u8; 102] = b"HTTP/1.1 200 OK\r\n\ + Date: Sat, 11 Jan 2003 02:44:04 GMT\r\n\ + Content-Type: text/html\r\n\ + Content-Length: 100\r\n\r\n"; + +/// Response body returned by the HTTP client mock, contains information passed in by caller +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +pub struct ResponseBodyMock { + pub base_url: String, + pub method: String, + pub path: String, + pub request_body: Option, + pub query_parameters: Vec<(String, String)>, +} + +impl RestPath for ResponseBodyMock { + fn get_path(path: String) -> Result { + Ok(format!("{}", path)) + } +} + +/// HTTP client mock - to be used in unit tests +pub struct HttpClientMock { + response: Option, +} + +impl HttpClientMock { + pub fn new(response: Option) -> Self { + HttpClientMock { response } + } +} + +impl SendHttpRequest for HttpClientMock { + fn send_request( + &self, + base_url: Url, + method: Method, + params: U, + query: Option<&Query<'_>>, + maybe_body: Option, + ) -> Result<(Response, EncodedBody), Error> + where + T: RestPath, + { + let path = T::get_path(params)?; + let response = self + .response + .clone() + .unwrap_or_else(|| Response::from_head(DEFAULT_HEAD).unwrap()); + let base_url_str = String::from(base_url.as_str()); + + let query_parameters = query + .map(|q| q.iter().map(|(key, value)| (key.to_string(), value.to_string())).collect()) + .unwrap_or_else(|| Vec::<(String, String)>::new()); + + let response_body = ResponseBodyMock { + base_url: base_url_str, + method: format!("{:?}", method), + path, + request_body: maybe_body, + query_parameters, + }; + + let encoded_response_body = serde_json::to_vec(&response_body).unwrap(); + + Ok((response, encoded_response_body)) + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + pub fn response_body_mock_serialization_works() { + let response_body_mock = ResponseBodyMock { + base_url: "https://mydomain.com".to_string(), + method: "GET".to_string(), + path: "/api/v1".to_string(), + request_body: None, + query_parameters: vec![("order".to_string(), "desc".to_string())], + }; + + let serialized_body = serde_json::to_string(&response_body_mock).unwrap(); + let deserialized_body: ResponseBodyMock = + serde_json::from_str(serialized_body.as_str()).unwrap(); + + assert_eq!(deserialized_body, response_body_mock); + } + + #[test] + pub fn default_head_is_valid() { + assert!(Response::from_head(DEFAULT_HEAD).is_ok()); + } + + #[test] + pub fn client_mock_returns_parameters_in_result() { + let client_mock = HttpClientMock::new(None); + let base_url = Url::parse("https://integritee.network").unwrap(); + + let (response, encoded_response_body) = client_mock + .send_request::( + base_url, + Method::GET, + "/api/v1/get".to_string(), + None, + None, + ) + .unwrap(); + + let response_body: ResponseBodyMock = + serde_json::from_slice(encoded_response_body.as_slice()).unwrap(); + + assert_eq!(response, Response::from_head(DEFAULT_HEAD).unwrap()); + assert_eq!(response_body.method.as_str(), "GET"); + } +} diff --git a/bitacross-worker/core/rest-client/src/mocks/mod.rs b/bitacross-worker/core/rest-client/src/mocks/mod.rs new file mode 100644 index 0000000000..404a1b35d3 --- /dev/null +++ b/bitacross-worker/core/rest-client/src/mocks/mod.rs @@ -0,0 +1,18 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pub mod http_client_mock; diff --git a/bitacross-worker/core/rest-client/src/rest_client.rs b/bitacross-worker/core/rest-client/src/rest_client.rs new file mode 100644 index 0000000000..187553abc6 --- /dev/null +++ b/bitacross-worker/core/rest-client/src/rest_client.rs @@ -0,0 +1,354 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +pub use http_req::{request::Method, response::Headers}; +pub use url::Url; + +use crate::{ + error::Error, http_client::SendHttpRequest, Query, RestDelete, RestGet, RestPatch, RestPath, + RestPost, RestPut, +}; + +use log::*; +use std::string::{String, ToString}; + +/// REST client to make HTTP GET and POST requests. +pub struct RestClient { + http_client: H, + baseurl: Url, + response_headers: Headers, + body_wash_fn: fn(String) -> String, +} + +impl RestClient +where + H: SendHttpRequest, +{ + /// Construct new client with default configuration to make HTTP requests. + /// + /// Use `Builder` to configure the client. + pub fn new(http_client: H, baseurl: Url) -> Self { + RestClient { + http_client, + baseurl, + response_headers: Headers::new(), + body_wash_fn: std::convert::identity, + } + } + + /// Set a function that cleans the response body up before deserializing it. + pub fn set_body_wash_fn(&mut self, func: fn(String) -> String) { + self.body_wash_fn = func; + } + + /// Response headers captured from previous request + pub fn response_headers(&mut self) -> &Headers { + &self.response_headers + } + + fn post_or_put(&mut self, method: Method, params: U, data: &T) -> Result<(), Error> + where + T: serde::Serialize + RestPath, + { + let data = serde_json::to_string(data).map_err(Error::SerializeParseError)?; + + let _body = self.make_request::(method, params, None, Some(data))?; + Ok(()) + } + + fn post_or_put_with( + &mut self, + method: Method, + params: U, + data: &T, + query: &Query<'_>, + ) -> Result<(), Error> + where + T: serde::Serialize + RestPath, + { + let data = serde_json::to_string(data).map_err(Error::SerializeParseError)?; + + let _body = self.make_request::(method, params, Some(query), Some(data))?; + Ok(()) + } + + fn post_or_put_capture( + &mut self, + method: Method, + params: U, + data: &T, + ) -> Result + where + T: serde::Serialize + RestPath, + K: serde::de::DeserializeOwned, + { + let data = serde_json::to_string(data).map_err(Error::SerializeParseError)?; + + let body = self.make_request::(method, params, None, Some(data))?; + serde_json::from_str(body.as_str()).map_err(|err| Error::DeserializeParseError(err, body)) + } + + fn post_or_put_capture_with( + &mut self, + method: Method, + params: U, + data: &T, + query: &Query<'_>, + ) -> Result + where + T: serde::Serialize + RestPath, + K: serde::de::DeserializeOwned, + { + let data = serde_json::to_string(data).map_err(Error::SerializeParseError)?; + + let body = self.make_request::(method, params, Some(query), Some(data))?; + serde_json::from_str(body.as_str()).map_err(|err| Error::DeserializeParseError(err, body)) + } + + fn make_request( + &mut self, + method: Method, + params: U, + query: Option<&Query<'_>>, + maybe_body: Option, + ) -> Result + where + T: RestPath, + { + let (response, encoded_body) = self.http_client.send_request::( + self.baseurl.clone(), + method, + params, + query, + maybe_body, + )?; + + self.response_headers = response.headers().clone(); + let status_code = response.status_code(); + + if !status_code.is_success() { + let status_code_num = u16::from(status_code); + let reason = String::from(status_code.reason().unwrap_or("none")); + return Err(Error::HttpError(status_code_num, reason)) + } + + let body = String::from_utf8_lossy(&encoded_body).to_string(); + + trace!("response headers: {:?}", self.response_headers); + trace!("response body: {}", body); + Ok((self.body_wash_fn)(body)) + } +} + +impl RestGet for RestClient +where + H: SendHttpRequest, +{ + /// Make a GET request. + fn get(&mut self, params: U) -> Result + where + T: serde::de::DeserializeOwned + RestPath, + { + let body = self.make_request::(Method::GET, params, None, None)?; + + serde_json::from_str(body.as_str()).map_err(|err| Error::DeserializeParseError(err, body)) + } + + /// Make a GET request with query parameters. + fn get_with(&mut self, params: U, query: &Query<'_>) -> Result + where + T: serde::de::DeserializeOwned + RestPath, + { + let body = self.make_request::(Method::GET, params, Some(query), None)?; + + serde_json::from_str(body.as_str()).map_err(|err| Error::DeserializeParseError(err, body)) + } +} + +impl RestPost for RestClient +where + H: SendHttpRequest, +{ + /// Make a POST request. + fn post(&mut self, params: U, data: &T) -> Result<(), Error> + where + T: serde::Serialize + RestPath, + { + self.post_or_put(Method::POST, params, data) + } + + /// Make POST request with query parameters. + fn post_with(&mut self, params: U, data: &T, query: &Query<'_>) -> Result<(), Error> + where + T: serde::Serialize + RestPath, + { + self.post_or_put_with(Method::POST, params, data, query) + } + + /// Make a POST request and capture returned body. + fn post_capture(&mut self, params: U, data: &T) -> Result + where + T: serde::Serialize + RestPath, + K: serde::de::DeserializeOwned, + { + self.post_or_put_capture(Method::POST, params, data) + } + + /// Make a POST request with query parameters and capture returned body. + fn post_capture_with( + &mut self, + params: U, + data: &T, + query: &Query<'_>, + ) -> Result + where + T: serde::Serialize + RestPath, + K: serde::de::DeserializeOwned, + { + self.post_or_put_capture_with(Method::POST, params, data, query) + } +} + +impl RestPut for RestClient +where + H: SendHttpRequest, +{ + /// Make a PUT request. + fn put(&mut self, params: U, data: &T) -> Result<(), Error> + where + T: serde::Serialize + RestPath, + { + self.post_or_put(Method::PUT, params, data) + } + + /// Make PUT request with query parameters. + fn put_with(&mut self, params: U, data: &T, query: &Query<'_>) -> Result<(), Error> + where + T: serde::Serialize + RestPath, + { + self.post_or_put_with(Method::PUT, params, data, query) + } + + /// Make a PUT request and capture returned body. + fn put_capture(&mut self, params: U, data: &T) -> Result + where + T: serde::Serialize + RestPath, + K: serde::de::DeserializeOwned, + { + self.post_or_put_capture(Method::PUT, params, data) + } + + /// Make a PUT request with query parameters and capture returned body. + fn put_capture_with( + &mut self, + params: U, + data: &T, + query: &Query<'_>, + ) -> Result + where + T: serde::Serialize + RestPath, + K: serde::de::DeserializeOwned, + { + self.post_or_put_capture_with(Method::PUT, params, data, query) + } +} + +impl RestPatch for RestClient +where + H: SendHttpRequest, +{ + /// Make a PATCH request. + fn patch(&mut self, params: U, data: &T) -> Result<(), Error> + where + T: serde::Serialize + RestPath, + { + self.post_or_put(Method::PATCH, params, data) + } + + /// Make PATCH request with query parameters. + fn patch_with(&mut self, params: U, data: &T, query: &Query<'_>) -> Result<(), Error> + where + T: serde::Serialize + RestPath, + { + self.post_or_put_with(Method::PATCH, params, data, query) + } +} + +impl RestDelete for RestClient +where + H: SendHttpRequest, +{ + /// Make a DELETE request. + fn delete(&mut self, params: U) -> Result<(), Error> + where + T: RestPath, + { + self.make_request::(Method::DELETE, params, None, None)?; + Ok(()) + } + + /// Make a DELETE request with query and body. + fn delete_with(&mut self, params: U, data: &T, query: &Query<'_>) -> Result<(), Error> + where + T: serde::Serialize + RestPath, + { + let data = serde_json::to_string(data).map_err(Error::SerializeParseError)?; + self.make_request::(Method::DELETE, params, Some(query), Some(data))?; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::mocks::http_client_mock::{HttpClientMock, ResponseBodyMock}; + + #[test] + pub fn get_sends_proper_request() { + let mut rest_client = create_default_rest_client(); + + let get_response = + rest_client.get::("/api/v2/get".to_string()).unwrap(); + + assert_eq!(get_response.method.as_str(), "GET"); + assert_eq!(get_response.path.as_str(), "/api/v2/get"); + } + + #[test] + pub fn get_with_query_parameters_works() { + let mut rest_client = create_default_rest_client(); + + let get_response = rest_client + .get_with::( + "/api/v1/get".to_string(), + &[("order", "desc"), ("user", "spongebob")], + ) + .unwrap(); + + assert_eq!(2, get_response.query_parameters.len()); + } + + fn create_default_rest_client() -> RestClient { + let base_url = Url::parse("https://example.com").unwrap(); + let http_client = HttpClientMock::new(None); + RestClient::new(http_client, base_url) + } +} diff --git a/bitacross-worker/core/rpc-client/Cargo.toml b/bitacross-worker/core/rpc-client/Cargo.toml new file mode 100644 index 0000000000..fc06593ed3 --- /dev/null +++ b/bitacross-worker/core/rpc-client/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "itc-rpc-client" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +# crates.io +base58 = "0.2" +codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +log = "0.4" +openssl = { version = "0.10" } +parking_lot = "0.12.1" +serde_json = "1.0" +sgx_crypto_helper = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } +thiserror = { version = "1.0" } +url = { version = "2.0.0" } +ws = { version = "0.9.1", features = ["ssl"] } + +# parity +frame-metadata = { version = "15.1.0", features = ["v14"] } +sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42", default-features = false } + +# local +itp-api-client-types = { path = "../../core-primitives/node-api/api-client-types" } +itp-rpc = { path = "../../core-primitives/rpc" } +itp-types = { path = "../../core-primitives/types" } +itp-utils = { path = "../../core-primitives/utils" } + +# litentry +ita-stf = { path = "../../app-libs/stf" } +itp-stf-primitives = { path = "../../core-primitives/stf-primitives" } +litentry-primitives = { path = "../../litentry/primitives", default-features = false } +teerex-primitives = { path = "../../../primitives/teerex", default-features = false } + +[dev-dependencies] +env_logger = "0.9.0" +itc-tls-websocket-server = { path = "../tls-websocket-server", features = ["mocks"] } +itp-networking-utils = { path = "../../core-primitives/networking-utils" } +rustls = { version = "0.19", features = ["dangerous_configuration"] } diff --git a/bitacross-worker/core/rpc-client/src/direct_client.rs b/bitacross-worker/core/rpc-client/src/direct_client.rs new file mode 100644 index 0000000000..5f7acab959 --- /dev/null +++ b/bitacross-worker/core/rpc-client/src/direct_client.rs @@ -0,0 +1,369 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Interface for direct access to a workers rpc. + +use crate::ws_client::{WsClient, WsClientControl}; +use base58::ToBase58; +use codec::{Decode, Encode}; +use frame_metadata::RuntimeMetadataPrefixed; +use ita_stf::Getter; +use itp_api_client_types::Metadata; +use itp_rpc::{Id, RpcRequest, RpcResponse, RpcReturnValue}; +use itp_stf_primitives::types::{AccountId, ShardIdentifier}; +use itp_types::{DirectRequestStatus, RsaRequest}; +use itp_utils::{FromHexPrefixed, ToHexPrefixed}; +use log::*; +use sgx_crypto_helper::rsa3072::Rsa3072PubKey; +use std::{ + sync::{ + mpsc::{channel, Sender as MpscSender}, + Arc, + }, + thread, + thread::JoinHandle, +}; +use teerex_primitives::MrEnclave; + +pub use crate::error::{Error, Result}; + +#[derive(Clone)] +pub struct DirectClient { + url: String, + web_socket_control: Arc, +} +pub trait DirectApi { + /// Server connection with only one response. + fn get(&self, request: &str) -> Result; + /// Server connection with more than one response. + fn watch(&self, request: String, sender: MpscSender) -> JoinHandle<()>; + fn get_rsa_pubkey(&self) -> Result; + fn get_mu_ra_url(&self) -> Result; + fn get_untrusted_worker_url(&self) -> Result; + fn get_state_metadata(&self) -> Result; + fn send(&self, request: &str) -> Result<()>; + /// Close any open websocket connection. + fn close(&self) -> Result<()>; + + // litentry + fn get_state_metadata_raw(&self) -> Result; + fn get_next_nonce(&self, shard: &ShardIdentifier, account: &AccountId) -> Result; + fn get_state_mrenclave(&self) -> Result; +} + +impl DirectClient { + pub fn new(url: String) -> Self { + Self { url, web_socket_control: Default::default() } + } + + // litentry: moved from `cli/src/trusted_operation.rs` as it's more widely used + pub fn get_state(&self, shard: ShardIdentifier, getter: &Getter) -> Option> { + // Compose jsonrpc call. + let data = RsaRequest::new(shard, getter.encode()); + let rpc_method = "state_executeGetter".to_owned(); + let jsonrpc_call: String = RpcRequest::compose_jsonrpc_call( + Id::Text("1".to_string()), + rpc_method, + vec![data.to_hex()], + ) + .unwrap(); + + let rpc_response_str = self.get(&jsonrpc_call).unwrap(); + + // Decode RPC response. + let rpc_response: RpcResponse = serde_json::from_str(&rpc_response_str).ok()?; + let rpc_return_value = RpcReturnValue::from_hex(&rpc_response.result) + // Replace with `inspect_err` once it's stable. + .map_err(|e| { + error!("Failed to decode RpcReturnValue: {:?}", e); + e + }) + .ok()?; + + if rpc_return_value.status == DirectRequestStatus::Error { + println!("[Error] {}", String::decode(&mut rpc_return_value.value.as_slice()).unwrap()); + return None + } + + let maybe_state = Option::decode(&mut rpc_return_value.value.as_slice()) + // Replace with `inspect_err` once it's stable. + .map_err(|e| { + error!("Failed to decode return value: {:?}", e); + e + }) + .ok()?; + + maybe_state + } + + // common helper function for `get_state_metadata` and `get_state_metadata_raw` + fn get_metadata_internal(&self) -> Result { + let jsonrpc_call: String = RpcRequest::compose_jsonrpc_call( + Id::Text("1".to_string()), + "state_getMetadata".to_string(), + Default::default(), + )?; + + // Send json rpc call to ws server. + let response_str = self.get(&jsonrpc_call)?; + + // Decode rpc response. + let rpc_response: RpcResponse = serde_json::from_str(&response_str)?; + let rpc_return_value = RpcReturnValue::from_hex(&rpc_response.result) + .map_err(|e| Error::Custom(format!("{:?}", e).into()))?; + + // Decode Metadata. + RuntimeMetadataPrefixed::decode(&mut rpc_return_value.value.as_slice()) + .map_err(|e| e.into()) + } +} + +impl Drop for DirectClient { + fn drop(&mut self) { + if let Err(e) = self.close() { + error!("Failed to close web-socket connection: {:?}", e); + } + } +} + +impl DirectApi for DirectClient { + fn get(&self, request: &str) -> Result { + let (port_in, port_out) = channel(); + + info!("[WorkerApi Direct]: (get) Sending request: {:?}", request); + WsClient::connect_one_shot(&self.url, request, port_in)?; + debug!("Waiting for web-socket result.."); + port_out.recv().map_err(Error::MspcReceiver) + } + + fn watch(&self, request: String, sender: MpscSender) -> JoinHandle<()> { + info!("[WorkerApi Direct]: (watch) Sending request: {:?}", request); + let url = self.url.clone(); + + let web_socket_control = self.web_socket_control.clone(); + // Unwrap is fine here, because JoinHandle can be used to handle a Thread panic. + thread::spawn(move || { + WsClient::connect_watch_with_control(&url, &request, &sender, web_socket_control) + .expect("Connection failed") + }) + } + + fn get_rsa_pubkey(&self) -> Result { + let jsonrpc_call: String = RpcRequest::compose_jsonrpc_call( + Id::Text("1".to_string()), + "author_getShieldingKey".to_string(), + Default::default(), + )?; + + // Send json rpc call to ws server. + let response_str = self.get(&jsonrpc_call)?; + + let shielding_pubkey_string = decode_from_rpc_response::(&response_str)?; + let shielding_pubkey: Rsa3072PubKey = serde_json::from_str(&shielding_pubkey_string)?; + + info!("[+] Got RSA public key of enclave"); + Ok(shielding_pubkey) + } + + fn get_mu_ra_url(&self) -> Result { + let jsonrpc_call: String = RpcRequest::compose_jsonrpc_call( + Id::Text("1".to_string()), + "author_getMuRaUrl".to_string(), + Default::default(), + )?; + + // Send json rpc call to ws server. + let response_str = self.get(&jsonrpc_call)?; + + let mu_ra_url: String = decode_from_rpc_response::(&response_str)?; + + info!("[+] Got mutual remote attestation url of enclave: {}", mu_ra_url); + Ok(mu_ra_url) + } + + fn get_untrusted_worker_url(&self) -> Result { + let jsonrpc_call: String = RpcRequest::compose_jsonrpc_call( + Id::Text("1".to_string()), + "author_getUntrustedUrl".to_string(), + Default::default(), + )?; + + // Send json rpc call to ws server. + let response_str = self.get(&jsonrpc_call)?; + + let untrusted_url: String = decode_from_rpc_response::(&response_str)?; + + info!("[+] Got untrusted websocket url of worker: {}", untrusted_url); + Ok(untrusted_url) + } + + fn get_state_metadata(&self) -> Result { + let metadata = self.get_metadata_internal()?; + Metadata::try_from(metadata).map_err(|e| e.into()) + } + + fn send(&self, request: &str) -> Result<()> { + self.web_socket_control.send(request) + } + + fn close(&self) -> Result<()> { + self.web_socket_control.close_connection() + } + + fn get_state_metadata_raw(&self) -> Result { + let metadata = self.get_metadata_internal()?.to_hex(); + let rpc_response = + RpcResponse { jsonrpc: "2.0".to_owned(), result: metadata, id: Id::Number(1) }; + serde_json::to_string(&rpc_response).map_err(|e| Error::Custom(Box::new(e))) + } + + fn get_next_nonce(&self, shard: &ShardIdentifier, account: &AccountId) -> Result { + let jsonrpc_call: String = RpcRequest::compose_jsonrpc_call( + Id::Text("1".to_string()), + "author_getNextNonce".to_owned(), + vec![shard.encode().to_base58(), account.to_hex()], + ) + .unwrap(); + debug!("[+] get_next_nonce jsonrpc_call: {}", jsonrpc_call); + // Send json rpc call to ws server. + let response_str = self.get(&jsonrpc_call)?; + debug!("[+] get_next_nonce response_str: {}", response_str); + decode_from_rpc_response::(&response_str) + } + + fn get_state_mrenclave(&self) -> Result { + let jsonrpc_call: String = RpcRequest::compose_jsonrpc_call( + Id::Text("1".to_string()), + "state_getMrenclave".to_string(), + Default::default(), + )?; + + // Send json rpc call to ws server. + let response_str = self.get(&jsonrpc_call)?; + + let mrenclave: MrEnclave = decode_from_rpc_response::(&response_str)?; + + info!("[+] Got enclave: {:?}", mrenclave); + Ok(mrenclave) + } +} + +fn decode_from_rpc_response(json_rpc_response: &str) -> Result { + let rpc_response: RpcResponse = serde_json::from_str(json_rpc_response)?; + let rpc_return_value = RpcReturnValue::from_hex(&rpc_response.result) + .map_err(|e| Error::Custom(format!("{:?}", e).into()))?; + + let response_message = T::decode(&mut rpc_return_value.value.as_slice())?; + match rpc_return_value.status { + DirectRequestStatus::Ok => Ok(response_message), + _ => Err(Error::Status(format!("decode_response failed to decode {:?}", response_message))), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use itc_tls_websocket_server::{test::fixtures::test_server::create_server, WebSocketServer}; + use itp_networking_utils::ports::get_available_port_in_range; + use std::vec; + + #[test] + fn watch_works_and_closes_connection_on_demand() { + let _ = env_logger::builder().is_test(true).try_init(); + + const END_MESSAGE: &str = "End of service."; + let responses = vec![END_MESSAGE.to_string()]; + + let port = get_available_port_in_range(21000..21500).unwrap(); + let (server, handler) = create_server(responses, port); + + let server_clone = server.clone(); + let server_join_handle = thread::spawn(move || { + if let Err(e) = server_clone.run() { + error!("Web-socket server failed: {:?}", e); + } + }); + + // Wait until server is up. + while !server.is_running().unwrap() { + thread::sleep(std::time::Duration::from_millis(50)); + } + + let client = DirectClient::new(format!("wss://localhost:{}", port)); + let (message_sender, message_receiver) = channel::(); + + let client_join_handle = client.watch("Request".to_string(), message_sender); + + let mut messages = Vec::::new(); + loop { + info!("Client waiting to receive answer.. "); + let message = message_receiver.recv().unwrap(); + info!("Received answer: {}", message); + let do_close = message.as_str() == END_MESSAGE; + messages.push(message); + + if do_close { + info!("Client closing connection"); + break + } + } + + info!("Joining client thread"); + client.close().unwrap(); + client_join_handle.join().unwrap(); + + info!("Joining server thread"); + server.shut_down().unwrap(); + server_join_handle.join().unwrap(); + + assert_eq!(1, messages.len()); + assert_eq!(1, handler.messages_handled.read().unwrap().len()); + } + + #[test] + fn get_works_and_closes_connection() { + let _ = env_logger::builder().is_test(true).try_init(); + + let server_response = "response 1".to_string(); + let responses = vec![server_response.clone()]; + + let port = get_available_port_in_range(21501..22000).unwrap(); + let (server, handler) = create_server(responses, port); + + let server_clone = server.clone(); + let server_join_handle = thread::spawn(move || { + if let Err(e) = server_clone.run() { + error!("Web-socket server failed: {:?}", e); + } + }); + + // Wait until server is up. + while !server.is_running().unwrap() { + thread::sleep(std::time::Duration::from_millis(50)); + } + + let client = DirectClient::new(format!("wss://localhost:{}", port)); + let received_response = client.get("Request").unwrap(); + + info!("Joining server thread"); + server.shut_down().unwrap(); + server_join_handle.join().unwrap(); + + assert_eq!(server_response, received_response); + assert_eq!(1, handler.messages_handled.read().unwrap().len()); + } +} diff --git a/bitacross-worker/core/rpc-client/src/error.rs b/bitacross-worker/core/rpc-client/src/error.rs new file mode 100644 index 0000000000..f5ef6541c8 --- /dev/null +++ b/bitacross-worker/core/rpc-client/src/error.rs @@ -0,0 +1,48 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +use codec::Error as CodecError; +use itp_api_client_types::InvalidMetadataError; +use serde_json::Error as JsonError; +use std::{boxed::Box, sync::mpsc::RecvError}; +use thiserror; +use ws::Error as WsClientError; + +pub type Result = core::result::Result; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("{0}")] + Codec(#[from] CodecError), + #[error("{0}")] + SerdeJson(#[from] JsonError), + #[error("Validateer returned the following error message: {0}")] + Status(String), + #[error("Websocket error: {0}")] + WsClientError(#[from] WsClientError), + #[error("Faulty channel: {0}")] + MspcReceiver(#[from] RecvError), + #[error("InvalidMetadata: {0:?}")] + InvalidMetadata(InvalidMetadataError), + #[error("Custom Error: {0}")] + Custom(Box), +} + +impl From for Error { + fn from(error: InvalidMetadataError) -> Self { + Error::InvalidMetadata(error) + } +} diff --git a/bitacross-worker/core/rpc-client/src/lib.rs b/bitacross-worker/core/rpc-client/src/lib.rs new file mode 100644 index 0000000000..59c9949911 --- /dev/null +++ b/bitacross-worker/core/rpc-client/src/lib.rs @@ -0,0 +1,22 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pub mod direct_client; +pub mod error; +#[cfg(test)] +pub mod mock; +pub mod ws_client; diff --git a/bitacross-worker/core/rpc-client/src/mock.rs b/bitacross-worker/core/rpc-client/src/mock.rs new file mode 100644 index 0000000000..d61290c035 --- /dev/null +++ b/bitacross-worker/core/rpc-client/src/mock.rs @@ -0,0 +1,122 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Interface for direct access to a workers rpc. + +use crate::{direct_client::DirectApi, error::Result}; +use codec::Decode; +use frame_metadata::RuntimeMetadataPrefixed; +use ita_stf::H256; +use itp_api_client_types::Metadata; +use itp_stf_primitives::types::{AccountId, ShardIdentifier}; +use litentry_primitives::Identity; +use sgx_crypto_helper::rsa3072::Rsa3072PubKey; +use std::{sync::mpsc::Sender as MpscSender, thread::JoinHandle}; +use teerex_primitives::MrEnclave; + +#[derive(Clone, Default)] +pub struct DirectClientMock { + rsa_pubkey: Rsa3072PubKey, + mu_ra_url: String, + untrusted_worker_url: String, + metadata: String, + nonce: u32, +} + +impl DirectClientMock { + pub fn new( + rsa_pubkey: Rsa3072PubKey, + mu_ra_url: String, + untrusted_worker_url: String, + metadata: String, + nonce: u32, + ) -> Self { + Self { rsa_pubkey, mu_ra_url, untrusted_worker_url, metadata, nonce } + } + + pub fn with_rsa_pubkey(mut self, key: Rsa3072PubKey) -> Self { + self.rsa_pubkey = key; + self + } + + pub fn with_mu_ra_url(mut self, url: &str) -> Self { + self.mu_ra_url = url.to_string(); + self + } + + pub fn with_untrusted_worker_url(mut self, url: &str) -> Self { + self.untrusted_worker_url = url.to_string(); + self + } + + pub fn with_metadata(mut self, metadata: String) -> Self { + self.metadata = metadata; + self + } + + pub fn with_nonce(mut self, nonce: u32) -> Self { + self.nonce = nonce; + self + } +} + +impl DirectApi for DirectClientMock { + fn get(&self, _request: &str) -> Result { + Ok("Hello_world".to_string()) + } + + fn watch(&self, _request: String, _sender: MpscSender) -> JoinHandle<()> { + unimplemented!() + } + + fn get_rsa_pubkey(&self) -> Result { + Ok(self.rsa_pubkey) + } + + fn get_mu_ra_url(&self) -> Result { + Ok(self.mu_ra_url.clone()) + } + + fn get_untrusted_worker_url(&self) -> Result { + Ok(self.untrusted_worker_url.clone()) + } + + fn get_state_metadata(&self) -> Result { + let metadata = RuntimeMetadataPrefixed::decode(&mut self.metadata.as_bytes())?; + Metadata::try_from(metadata).map_err(|e| e.into()) + } + + fn send(&self, _request: &str) -> Result<()> { + unimplemented!() + } + + fn close(&self) -> Result<()> { + unimplemented!() + } + + fn get_state_metadata_raw(&self) -> Result { + unimplemented!() + } + + fn get_next_nonce(&self, _shard: &ShardIdentifier, _account: &AccountId) -> Result { + Ok(self.nonce) + } + + fn get_state_mrenclave(&self) -> Result { + unimplemented!() + } +} diff --git a/bitacross-worker/core/rpc-client/src/ws_client.rs b/bitacross-worker/core/rpc-client/src/ws_client.rs new file mode 100644 index 0000000000..690adc1686 --- /dev/null +++ b/bitacross-worker/core/rpc-client/src/ws_client.rs @@ -0,0 +1,168 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +///! Websocket client implementation to access the direct-rpc-server running inside an enclave. +/// +/// This should be replaced with the `jsonrpsee::WsClient`as soon as available in no-std: +/// https://github.com/paritytech/jsonrpsee/issues/1 +use crate::error::{Error, Result as RpcClientResult}; +use log::*; +use openssl::ssl::{SslConnector, SslMethod, SslStream, SslVerifyMode}; +use parking_lot::Mutex; +use std::sync::{mpsc::Sender as MpscSender, Arc}; +use url::{self}; +use ws::{connect, util::TcpStream, CloseCode, Handler, Handshake, Message, Result, Sender}; + +/// Control a registered web-socket client. +#[derive(Default)] +pub struct WsClientControl { + subscriber: Mutex>, +} + +impl Clone for WsClientControl { + fn clone(&self) -> Self { + WsClientControl { subscriber: Mutex::new(self.subscriber.lock().clone()) } + } +} + +impl WsClientControl { + pub fn close_connection(&self) -> RpcClientResult<()> { + if let Some(s) = self.subscriber.lock().as_ref() { + debug!("Closing connection"); + s.close(CloseCode::Normal)?; + debug!("Connection is closed"); + } + Ok(()) + } + + fn subscribe_sender(&self, sender: Sender) -> RpcClientResult<()> { + let mut subscriber_lock = self.subscriber.lock(); + *subscriber_lock = Some(sender); + Ok(()) + } + + pub fn send(&self, request: &str) -> RpcClientResult<()> { + if let Some(s) = self.subscriber.lock().as_ref() { + s.send(request)?; + Ok(()) + } else { + Err(Error::Custom("Sender not initialized".into())) + } + } +} + +#[derive(Clone)] +pub struct WsClient { + web_socket: Sender, + request: String, + result: MpscSender, + do_watch: bool, +} + +impl WsClient { + /// Connect a web-socket client for multiple request/responses. + /// + /// Control over the connection is done using the provided client control. + /// (e.g. shutdown has to be initiated explicitly). + #[allow(clippy::result_large_err)] + pub fn connect_watch_with_control( + url: &str, + request: &str, + result: &MpscSender, + control: Arc, + ) -> Result<()> { + debug!("Connecting web-socket connection with watch"); + connect(url.to_string(), |out| { + control.subscribe_sender(out.clone()).expect("Failed sender subscription"); + WsClient::new(out, request.to_string(), result.clone(), true) + }) + } + + /// Connects a web-socket client for a one-shot request. + #[allow(clippy::result_large_err)] + pub fn connect_one_shot(url: &str, request: &str, result: MpscSender) -> Result<()> { + debug!("Connecting one-shot web-socket connection"); + connect(url.to_string(), |out| { + debug!("Create new web-socket client"); + WsClient::new(out, request.to_string(), result.clone(), false) + }) + } + + fn new( + web_socket: Sender, + request: String, + result: MpscSender, + do_watch: bool, + ) -> WsClient { + WsClient { web_socket, request, result, do_watch } + } +} + +impl Handler for WsClient { + fn on_open(&mut self, _: Handshake) -> Result<()> { + debug!("sending request: {:?}", self.request.clone()); + match self.web_socket.send(self.request.clone()) { + Ok(_) => Ok(()), + Err(e) => Err(e), + } + } + + fn on_message(&mut self, msg: Message) -> Result<()> { + trace!("got message"); + trace!("{}", msg); + trace!("sending result to MpscSender.."); + self.result.send(msg.to_string()).expect("Failed to send"); + if !self.do_watch { + debug!("do_watch is false, closing connection"); + self.web_socket.close(CloseCode::Normal).expect("Failed to close connection"); + debug!("Connection close requested"); + } + debug!("on_message successful, returning"); + Ok(()) + } + + fn on_close(&mut self, _code: CloseCode, _reason: &str) { + debug!("Web-socket close"); + self.web_socket.shutdown().expect("Failed to shutdown") + } + + /// we are overriding the `upgrade_ssl_client` method in order to disable hostname verification + /// this is taken from https://github.com/housleyjk/ws-rs/blob/master/examples/unsafe-ssl-client.rs + /// TODO: hostname verification should probably be enabled again for production? + fn upgrade_ssl_client( + &mut self, + sock: TcpStream, + _: &url::Url, + ) -> Result> { + let mut builder = SslConnector::builder(SslMethod::tls_client()).map_err(|e| { + ws::Error::new( + ws::ErrorKind::Internal, + format!("Failed to upgrade client to SSL: {}", e), + ) + })?; + builder.set_verify(SslVerifyMode::empty()); + + let connector = builder.build(); + connector + .configure() + .expect("Invalid connection config") + .use_server_name_indication(false) + .verify_hostname(false) + .connect("", sock) + .map_err(From::from) + } +} diff --git a/bitacross-worker/core/rpc-server/Cargo.toml b/bitacross-worker/core/rpc-server/Cargo.toml new file mode 100644 index 0000000000..d7f22c184e --- /dev/null +++ b/bitacross-worker/core/rpc-server/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "itc-rpc-server" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +anyhow = "1.0.40" +jsonrpsee = { version = "0.2.0-alpha.7", features = ["full"] } +log = "0.4" +tokio = { version = "1.6.1", features = ["full"] } + +# local +itp-enclave-api = { path = "../../core-primitives/enclave-api" } +itp-rpc = { path = "../../core-primitives/rpc" } +itp-utils = { path = "../../core-primitives/utils" } +its-peer-fetch = { path = "../../sidechain/peer-fetch" } +its-primitives = { path = "../../sidechain/primitives" } +its-rpc-handler = { path = "../../sidechain/rpc-handler" } +its-storage = { path = "../../sidechain/storage" } + +[features] +default = ["std"] +std = [] + +[dev-dependencies] +env_logger = { version = "*" } +sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +its-test = { path = "../../sidechain/test" } +parity-scale-codec = "3.0.0" diff --git a/bitacross-worker/core/rpc-server/src/lib.rs b/bitacross-worker/core/rpc-server/src/lib.rs new file mode 100644 index 0000000000..1386f0de4d --- /dev/null +++ b/bitacross-worker/core/rpc-server/src/lib.rs @@ -0,0 +1,81 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use itp_enclave_api::direct_request::DirectRequest; +use itp_rpc::{Id, RpcRequest}; +use itp_utils::ToHexPrefixed; +use its_peer_fetch::block_fetch_server::BlockFetchServerModuleBuilder; +use its_primitives::types::block::SignedBlock; +use its_rpc_handler::constants::RPC_METHOD_NAME_IMPORT_BLOCKS; +use its_storage::interface::FetchBlocks; +use jsonrpsee::{ + types::error::CallError, + ws_server::{RpcModule, WsServerBuilder}, +}; +use log::debug; +use std::{net::SocketAddr, sync::Arc}; +use tokio::net::ToSocketAddrs; + +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + +pub async fn run_server( + addr: impl ToSocketAddrs, + enclave: Arc, + sidechain_block_fetcher: Arc, +) -> anyhow::Result +where + Enclave: DirectRequest, + FetchSidechainBlocks: FetchBlocks + Send + Sync + 'static, +{ + let mut server = WsServerBuilder::default().build(addr).await?; + + // FIXME: import block should be moved to trusted side. + let mut import_sidechain_block_module = RpcModule::new(enclave); + import_sidechain_block_module.register_method( + RPC_METHOD_NAME_IMPORT_BLOCKS, + |params, enclave| { + debug!("{} params: {:?}", RPC_METHOD_NAME_IMPORT_BLOCKS, params); + + let enclave_req = RpcRequest::compose_jsonrpc_call( + Id::Text("1".to_string()), + RPC_METHOD_NAME_IMPORT_BLOCKS.into(), + vec![params.one::>()?.to_hex()], + ) + .unwrap(); + + enclave + .rpc(enclave_req.as_bytes().to_vec()) + .map_err(|e| CallError::Failed(e.into())) + }, + )?; + server.register_module(import_sidechain_block_module).unwrap(); + + let fetch_sidechain_blocks_module = BlockFetchServerModuleBuilder::new(sidechain_block_fetcher) + .build() + .map_err(|e| CallError::Failed(e.to_string().into()))?; // `to_string` necessary due to no all errors implementing Send + Sync. + server.register_module(fetch_sidechain_blocks_module).unwrap(); + + let socket_addr = server.local_addr()?; + tokio::spawn(async move { server.start().await }); + + println!("[+] Untrusted RPC server is spawned on: {}", socket_addr); + + Ok(socket_addr) +} diff --git a/bitacross-worker/core/rpc-server/src/mock.rs b/bitacross-worker/core/rpc-server/src/mock.rs new file mode 100644 index 0000000000..172c1a7528 --- /dev/null +++ b/bitacross-worker/core/rpc-server/src/mock.rs @@ -0,0 +1,75 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use itp_enclave_api::{direct_request::DirectRequest, EnclaveResult}; +use itp_rpc::{Id, RpcResponse}; +use itp_utils::ToHexPrefixed; +use its_primitives::{ + traits::ShardIdentifierFor, + types::{BlockHash, BlockNumber, SignedBlock, SignedBlock as SignedSidechainBlock}, +}; +use its_storage::{interface::FetchBlocks, LastSidechainBlock}; +use parity_scale_codec::Encode; + +pub struct TestEnclave; + +impl DirectRequest for TestEnclave { + fn rpc(&self, _request: Vec) -> EnclaveResult> { + Ok(RpcResponse { + jsonrpc: "mock_response".into(), + result: "null".to_hex(), + id: Id::Number(1), + } + .encode()) + } +} + +pub struct MockSidechainBlockFetcher; + +impl FetchBlocks for MockSidechainBlockFetcher { + fn fetch_all_blocks_after( + &self, + _block_hash: &BlockHash, + _shard_identifier: &ShardIdentifierFor, + ) -> its_storage::Result> { + Ok(Vec::new()) + } + + fn fetch_blocks_in_range( + &self, + _block_hash_from: &BlockHash, + _block_hash_until: &BlockHash, + _shard_identifier: &ShardIdentifierFor, + ) -> its_storage::Result> { + Ok(Vec::new()) + } + + fn latest_block( + &self, + _shard_identifier: &ShardIdentifierFor, + ) -> Option { + Some(LastSidechainBlock::default()) + } + + fn block_hash( + &self, + _block_number: BlockNumber, + _shard_identifier: &ShardIdentifierFor, + ) -> Option { + Some(LastSidechainBlock::default()) + } +} diff --git a/bitacross-worker/core/rpc-server/src/tests.rs b/bitacross-worker/core/rpc-server/src/tests.rs new file mode 100644 index 0000000000..4c99081804 --- /dev/null +++ b/bitacross-worker/core/rpc-server/src/tests.rs @@ -0,0 +1,56 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use super::*; +use crate::mock::MockSidechainBlockFetcher; +use itp_rpc::RpcResponse; +use its_rpc_handler::constants::RPC_METHOD_NAME_IMPORT_BLOCKS; +use its_test::sidechain_block_builder::{SidechainBlockBuilder, SidechainBlockBuilderTrait}; +use jsonrpsee::{ + types::{to_json_value, traits::Client}, + ws_client::WsClientBuilder, +}; +use log::info; +use mock::TestEnclave; +use parity_scale_codec::Decode; + +fn init() { + let _ = env_logger::builder().is_test(true).try_init(); +} + +#[tokio::test] +async fn test_client_calls() { + init(); + let addr = + run_server("127.0.0.1:0", Arc::new(TestEnclave), Arc::new(MockSidechainBlockFetcher)) + .await + .unwrap(); + info!("ServerAddress: {:?}", addr); + + let url = format!("ws://{}", addr); + let client = WsClientBuilder::default().build(&url).await.unwrap(); + let response: Vec = client + .request( + RPC_METHOD_NAME_IMPORT_BLOCKS, + vec![to_json_value(vec![SidechainBlockBuilder::default().build_signed()]).unwrap()] + .into(), + ) + .await + .unwrap(); + + assert!(RpcResponse::decode(&mut response.as_slice()).is_ok()); +} diff --git a/bitacross-worker/core/tls-websocket-server/Cargo.toml b/bitacross-worker/core/tls-websocket-server/Cargo.toml new file mode 100644 index 0000000000..8e4ca66442 --- /dev/null +++ b/bitacross-worker/core/tls-websocket-server/Cargo.toml @@ -0,0 +1,70 @@ +[package] +name = "itc-tls-websocket-server" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +bit-vec = { version = "0.6", default-features = false } +chrono = { version = "0.4.19", default-features = false, features = ["alloc"] } +rcgen = { package = "rcgen", default-features = false, git = "https://github.com/integritee-network/rcgen" } + +# sgx dependencies +sgx_tstd = { optional = true, features = ["net", "thread"], git = "https://github.com/apache/teaclave-sgx-sdk.git", branch = "master" } + +# sgx enabled external libraries +mio-extras = { optional = true, default-features = false, git = "https://github.com/integritee-network/mio-extras-sgx", rev = "963234b" } +mio_sgx = { package = "mio", optional = true, git = "https://github.com/mesalock-linux/mio-sgx", tag = "sgx_1.1.3" } +rustls_sgx = { package = "rustls", optional = true, git = "https://github.com/mesalock-linux/rustls", branch = "mesalock_sgx" } +thiserror_sgx = { package = "thiserror", optional = true, git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3" } +tungstenite_sgx = { package = "tungstenite", optional = true, features = ["rustls-tls-webpki-roots"], git = "https://github.com/integritee-network/tungstenite-rs-sgx", branch = "sgx-experimental" } +webpki_sgx = { package = "webpki", optional = true, git = "https://github.com/mesalock-linux/webpki", branch = "mesalock_sgx" } +yasna_sgx = { package = "yasna", optional = true, default-features = false, features = ["bit-vec", "num-bigint", "chrono", "mesalock_sgx"], git = "https://github.com/mesalock-linux/yasna.rs-sgx", rev = "sgx_1.1.3" } + +# std compatible external libraries (make sure these versions match with the sgx-enabled ones above) +mio = { version = "0.6.14", optional = true } +rustls = { version = "0.19", optional = true } +thiserror = { version = "1.0", optional = true } +tungstenite = { version = "0.15.0", optional = true, features = ["rustls-tls-webpki-roots"] } +webpki = { version = "0.21", optional = true } +yasna = { version = "0.4", optional = true, features = ["bit-vec", "num-bigint", "chrono", "std"] } + +# Substrate dependencies +sp-core = { default-features = false, features = ["full_crypto"], git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# no-std compatible libraries +log = { version = "0.4", default-features = false } + +[dev-dependencies] +env_logger = "0.9.0" +rustls = { version = "0.19", features = ["dangerous_configuration"] } +url = { version = "2.0.0" } + + +[features] +default = ["std"] +sgx = [ + "mio-extras/sgx", + "mio_sgx", + "rcgen/sgx", + "rcgen/pem_sgx", + "rustls_sgx", + "sgx_tstd", + "thiserror_sgx", + "tungstenite_sgx", + "webpki_sgx", + "yasna_sgx", +] +std = [ + "mio", + "mio-extras/std", + "rcgen/std", + "rcgen/pem", + "rustls", + "thiserror", + "tungstenite", + "webpki", + "yasna", + "log/std", +] +mocks = [] diff --git a/bitacross-worker/core/tls-websocket-server/src/certificate_generation.rs b/bitacross-worker/core/tls-websocket-server/src/certificate_generation.rs new file mode 100644 index 0000000000..0a1afaaf0a --- /dev/null +++ b/bitacross-worker/core/tls-websocket-server/src/certificate_generation.rs @@ -0,0 +1,172 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use crate::{error::WebSocketError, WebSocketResult}; +use bit_vec::BitVec; +use chrono::{prelude::*, TimeZone, Utc as TzUtc}; +use core::convert::TryFrom; +use rcgen::{date_time_ymd, Certificate, CertificateParams, DistinguishedName, DnType}; +use sp_core::{crypto::Pair, ed25519}; +use std::{ + string::ToString, + time::{SystemTime, UNIX_EPOCH}, + vec, + vec::Vec, +}; +use yasna::models::ObjectIdentifier; + +const ED25519: &[u64] = &[1, 3, 101, 112]; + +/// Create a sel-signed certificate, signed with the Ed25519 private key +/// Certificate Params are : +/// - alg: &PKCS_ED25519 -> ED25519 curve signing as per [RFC 8410](https://tools.ietf.org/html/rfc8410) +/// - common_name : the “subject”of the certificate, which is the identity of the certificate/website owner. +/// - not_before : now +/// - not_after : 4096-01-01 -> Certificate valid from initialisation time until 4096-01-01 +/// - serial_number : None, +/// - subject_alt_names : common_name. Required parameter. See below, subject +/// - DistinguishedName : +/// - issuer : Integritee, (The issuer field identifies the entity that has signed and issued the certificate. +/// The issuer field MUST contain a non-empty distinguished name (DN) ) +/// - subject: empty. (The subject field identifies the entity associated with the public key stored in the subject +/// public key field. If subject naming information is present only in the subjectAltName extension +/// (e.g., a key bound only to an email address or URI), then the subject name MUST be an empty sequence +/// and the subjectAltName extension MUST be critical. +/// - is_ca : SelfSignedOnly -> The certificate can only sign itself +/// - key_usages: empty (The key usage extension defines the purpose (e.g., encipherment, signature, certificate signing) of +/// the key contained in the certificate. The usage restriction might be employed when a key that could +/// be used for more than one operation is to be restricted.) +/// - extended_key_usages: empty ( This extension indicates one or more purposes for which the certified public key may be used, +/// in addition to or in place of the basic purposes indicated in the key usage extension.) +/// - name_constraints : None (only relevant for CA certificates) +/// - custom_extensions: None (The extensions defined for X.509 v3 certificates provide methods for associating additional +/// attributes with users or public keys and for managing relationships between CAs.) +/// - key_pair : rcgen::KeyPair from enclave private key. (A key pair used to sign certificates and CSRs) +/// - use_authority_key_identifier_extension: false (If `true` (and not self-signed), the 'Authority Key Identifier' extension will be added to the generated cert) +/// - key_identifier_method : KeyIdMethod::Sha256 (Method to generate key identifiers from public keys) + +pub fn ed25519_self_signed_certificate( + key_pair: ed25519::Pair, + common_name: &str, +) -> WebSocketResult { + let mut params = CertificateParams::new(vec![common_name.to_string()]); + let now = SystemTime::now().duration_since(UNIX_EPOCH).expect("Error: UNIX_EPOCH"); + let issue_ts = TzUtc + .timestamp_opt(now.as_secs() as i64, 0) + .single() + .expect("Error: this should not fail as long as secs fit into i64"); + let year = issue_ts.year(); + let month = issue_ts.month(); + let day = issue_ts.day(); + params.not_before = date_time_ymd(year, month, day); + params.not_after = date_time_ymd(4096, 1, 1); + let mut dn = DistinguishedName::new(); + dn.push(DnType::OrganizationName, "Integritee"); + //dn.push(DnType::CommonName, common_name); + params.distinguished_name = dn; + + params.alg = &rcgen::PKCS_ED25519; //Signature Algorithm: + + let private_key_der = ed25519_private_key_pkcs8_der(key_pair)?; + + let key_pair = rcgen::KeyPair::try_from(private_key_der.as_ref()).expect("Invalid pkcs8 der"); + params.key_pair = Some(key_pair); + + Certificate::from_params(params).map_err(|e| WebSocketError::Other(e.into())) +} + +/// Generate the private key in a PKCS#8 format. To be compatible with rcgen lib. +/// PKCS#8 is specified in [RFC 5958]. +/// +/// [RFC 5958]: https://tools.ietf.org/html/rfc5958. +fn ed25519_private_key_pkcs8_der(key_pair: ed25519::Pair) -> WebSocketResult> { + let seed = key_pair.seed(); + let private_key = seed.as_slice(); + let pk = key_pair.public().0; + let public_key = pk.as_slice(); + let key_der = yasna::construct_der(|writer| { + writer.write_sequence(|writer| { + writer.next().write_u8(1); + // write OID + writer.next().write_sequence(|writer| { + writer.next().write_oid(&ObjectIdentifier::from_slice(ED25519)); + }); + let pk = yasna::construct_der(|writer| writer.write_bytes(private_key)); + writer.next().write_bytes(&pk); + writer.next().write_tagged(yasna::Tag::context(1), |writer| { + writer.write_bitvec(&BitVec::from_bytes(public_key)) + }) + }); + }); + Ok(key_der) +} + +#[cfg(test)] +mod tests { + use crate::certificate_generation::ed25519_self_signed_certificate; + use sp_core::{crypto::Pair, ed25519}; + use std::time::SystemTime; + use webpki::TLSServerTrustAnchors; + + type Seed = [u8; 32]; + const TEST_SEED: Seed = *b"12345678901234567890123456789012"; + + #[test] + pub fn test_verify_signature_self_signed_certificate() { + let signing = signer(); + let pk = signing.public().0; + let public_key = pk.as_slice(); + let cert = ed25519_self_signed_certificate(signing, "Test").unwrap(); + let sign_pub_key = cert.get_key_pair().public_key_raw(); + assert_eq!(public_key, sign_pub_key); + } + + #[test] + pub fn test_verify_is_valid_tls_server_certificate() { + let common_name = "Test"; + let signing = signer(); + let cert = ed25519_self_signed_certificate(signing, common_name).unwrap(); + + //write certificate and private key pem file + //let cert_der = cert.serialize_der().unwrap(); + //fs::write("test_cert.der", &cert_der).unwrap(); + + let cert_der = cert.serialize_der().unwrap(); + let end_entity_cert = webpki::EndEntityCert::from(&cert_der).unwrap(); + + let time = webpki::Time::try_from(SystemTime::now()); + + let trust_anchor = webpki::trust_anchor_util::cert_der_as_trust_anchor(&cert_der).unwrap(); + let trust_anchor_list = &[trust_anchor]; + let trust_anchors = TLSServerTrustAnchors(trust_anchor_list); + + assert!(end_entity_cert + .verify_is_valid_tls_server_cert( + &[&webpki::ED25519], + &trust_anchors, + &[], + time.unwrap(), + ) + .is_ok()); + } + + fn signer() -> ed25519::Pair { + ed25519::Pair::from_seed(&TEST_SEED) + } +} diff --git a/bitacross-worker/core/tls-websocket-server/src/config_provider.rs b/bitacross-worker/core/tls-websocket-server/src/config_provider.rs new file mode 100644 index 0000000000..04d561bc20 --- /dev/null +++ b/bitacross-worker/core/tls-websocket-server/src/config_provider.rs @@ -0,0 +1,45 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use crate::{error::WebSocketResult, tls_common::make_config}; +use rustls::ServerConfig; +use std::{string::String, sync::Arc}; + +/// Trait to provide a Rustls server config. +pub trait ProvideServerConfig: Send + Sync { + fn get_config(&self) -> WebSocketResult>; +} + +pub struct FromFileConfigProvider { + private_key: String, + certificate: String, +} + +impl FromFileConfigProvider { + pub fn new(private_key: String, certificate: String) -> Self { + Self { private_key, certificate } + } +} + +impl ProvideServerConfig for FromFileConfigProvider { + fn get_config(&self) -> WebSocketResult> { + make_config(&self.certificate, &self.private_key) + } +} diff --git a/bitacross-worker/core/tls-websocket-server/src/connection.rs b/bitacross-worker/core/tls-websocket-server/src/connection.rs new file mode 100644 index 0000000000..ab456236b0 --- /dev/null +++ b/bitacross-worker/core/tls-websocket-server/src/connection.rs @@ -0,0 +1,344 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use crate::{ + error::WebSocketError, stream_state::StreamState, WebSocketConnection, WebSocketMessageHandler, + WebSocketResult, +}; +use log::*; +use mio::{event::Event, net::TcpStream, Poll, Ready, Token}; +use rustls::{ServerSession, Session}; +use std::{ + format, + string::{String, ToString}, + sync::Arc, + time::Instant, +}; +use tungstenite::Message; + +/// A web-socket connection object. +pub struct TungsteniteWsConnection { + stream_state: StreamState, + connection_token: Token, + connection_handler: Arc, + is_closed: bool, +} + +impl TungsteniteWsConnection +where + Handler: WebSocketMessageHandler, +{ + pub fn new( + tcp_stream: TcpStream, + server_session: ServerSession, + connection_token: Token, + handler: Arc, + ) -> WebSocketResult { + Ok(TungsteniteWsConnection { + stream_state: StreamState::from_stream(rustls::StreamOwned::new( + server_session, + tcp_stream, + )), + connection_token, + connection_handler: handler, + is_closed: false, + }) + } + + fn do_tls_read(&mut self) -> ConnectionState { + let tls_stream = match self.stream_state.internal_stream_mut() { + None => return ConnectionState::Closing, + Some(s) => s, + }; + + let tls_session = &mut tls_stream.sess; + + match tls_session.read_tls(&mut tls_stream.sock) { + Ok(r) => + if r == 0 { + return ConnectionState::Closing + }, + Err(err) => { + if let std::io::ErrorKind::WouldBlock = err.kind() { + debug!("TLS session is blocked (connection {})", self.connection_token.0); + return ConnectionState::Blocked + } + warn!( + "I/O error after reading TLS data (connection {}): {:?}", + self.connection_token.0, err + ); + return ConnectionState::Closing + }, + } + + match tls_session.process_new_packets() { + Ok(_) => { + if tls_session.is_handshaking() { + return ConnectionState::TlsHandshake + } + ConnectionState::Alive + }, + Err(e) => { + error!("cannot process TLS packet(s), closing connection: {:?}", e); + ConnectionState::Closing + }, + } + } + + fn do_tls_write(&mut self) -> ConnectionState { + let tls_stream = match self.stream_state.internal_stream_mut() { + None => return ConnectionState::Closing, + Some(s) => s, + }; + + match tls_stream.sess.write_tls(&mut tls_stream.sock) { + Ok(_) => { + trace!("TLS write successful, connection {} is alive", self.connection_token.0); + if tls_stream.sess.is_handshaking() { + return ConnectionState::TlsHandshake + } + ConnectionState::Alive + }, + Err(e) => { + error!("TLS write error (connection {}): {:?}", self.connection_token.0, e); + ConnectionState::Closing + }, + } + } + + /// Read from a web-socket, or initiate handshake if websocket is not initialized yet. + /// + /// Returns a boolean 'connection should be closed'. + fn read_or_initialize_websocket(&mut self) -> WebSocketResult { + if let StreamState::EstablishedWebsocket(web_socket) = &mut self.stream_state { + trace!( + "Read is possible for connection {}: {}", + self.connection_token.0, + web_socket.can_read() + ); + match web_socket.read_message() { + Ok(m) => + if let Err(e) = self.handle_message(m) { + error!( + "Failed to handle web-socket message (connection {}): {:?}", + self.connection_token.0, e + ); + }, + Err(e) => match e { + tungstenite::Error::ConnectionClosed => return Ok(true), + tungstenite::Error::AlreadyClosed => return Ok(true), + _ => error!( + "Failed to read message from web-socket (connection {}): {:?}", + self.connection_token.0, e + ), + }, + } + trace!("Read successful for connection {}", self.connection_token.0); + } else { + trace!("Initialize connection {}", self.connection_token.0); + self.stream_state = std::mem::take(&mut self.stream_state).attempt_handshake(); + if self.stream_state.is_invalid() { + warn!("Web-socket connection ({:?}) failed, closing", self.connection_token); + return Ok(true) + } + debug!("Initialized connection {} successfully", self.connection_token.0); + } + + Ok(false) + } + + fn handle_message(&mut self, message: Message) -> WebSocketResult<()> { + match message { + Message::Text(string_message) => { + trace!( + "Got Message::Text on web-socket (connection {}), calling handler..", + self.connection_token.0 + ); + let message_handled_timer = Instant::now(); + if let Some(reply) = self + .connection_handler + .handle_message(self.connection_token.into(), string_message)? + { + trace!( + "Handling message yielded a reply, sending it now to connection {}..", + self.connection_token.0 + ); + self.write_message(reply)?; + trace!("Reply sent successfully to connection {}", self.connection_token.0); + } + debug!( + "Handled web-socket message in {} ms", + message_handled_timer.elapsed().as_millis() + ); + }, + Message::Binary(_) => { + warn!("received binary message, don't have a handler for this format"); + }, + Message::Close(_) => { + debug!( + "Received close frame, driving web-socket connection {} to close", + self.connection_token.0 + ); + if let StreamState::EstablishedWebsocket(web_socket) = &mut self.stream_state { + // Send a close frame back and then flush the send queue. + if let Err(e) = web_socket.close(None) { + match e { + tungstenite::Error::ConnectionClosed + | tungstenite::Error::AlreadyClosed => {}, + _ => warn!( + "Failed to send close frame (connection {}): {:?}", + self.connection_token.0, e + ), + } + } + match web_socket.write_pending() { + Ok(_) => {}, + Err(e) => match e { + tungstenite::Error::ConnectionClosed + | tungstenite::Error::AlreadyClosed => {}, + _ => warn!("Failed to write pending frames after closing (connection {}): {:?}", self.connection_token.0, e), + }, + } + } + debug!("Successfully closed connection {}", self.connection_token.0); + }, + _ => {}, + } + Ok(()) + } + + pub(crate) fn write_message(&mut self, message: String) -> WebSocketResult<()> { + match &mut self.stream_state { + StreamState::EstablishedWebsocket(web_socket) => { + if !web_socket.can_write() { + return Err(WebSocketError::ConnectionClosed) + } + debug!("Write message to connection {}: {}", self.connection_token.0, message); + web_socket + .write_message(Message::Text(message)) + .map_err(|e| WebSocketError::SocketWriteError(format!("{:?}", e))) + }, + _ => + Err(WebSocketError::SocketWriteError("No active web-socket available".to_string())), + } + } +} + +impl WebSocketConnection for TungsteniteWsConnection +where + Handler: WebSocketMessageHandler, +{ + type Socket = TcpStream; + + fn socket(&self) -> Option<&Self::Socket> { + self.stream_state.internal_stream().map(|s| &s.sock) + } + + fn get_session_readiness(&self) -> Ready { + match self.stream_state.internal_stream() { + None => mio::Ready::empty(), + Some(s) => { + let wants_read = s.sess.wants_read(); + let wants_write = s.sess.wants_write(); + + if wants_read && wants_write { + mio::Ready::readable() | mio::Ready::writable() + } else if wants_write { + mio::Ready::writable() + } else { + mio::Ready::readable() + } + }, + } + } + + fn on_ready(&mut self, poll: &mut Poll, event: &Event) -> WebSocketResult<()> { + let mut is_closing = false; + + if event.readiness().is_readable() { + trace!("Connection ({:?}) is readable", self.token()); + + let connection_state = self.do_tls_read(); + + if connection_state.is_alive() { + is_closing = self.read_or_initialize_websocket()?; + } else { + is_closing = connection_state.is_closing(); + } + } + + if event.readiness().is_writable() { + trace!("Connection ({:?}) is writable", self.token()); + + let connection_state = self.do_tls_write(); + + if connection_state.is_alive() { + if let StreamState::EstablishedWebsocket(web_socket) = &mut self.stream_state { + trace!("Web-socket, write pending messages"); + if let Err(e) = web_socket.write_pending() { + match e { + tungstenite::Error::ConnectionClosed + | tungstenite::Error::AlreadyClosed => is_closing = true, + _ => error!("Failed to write pending web-socket messages: {:?}", e), + } + } + } + } else { + is_closing = connection_state.is_closing(); + } + } + + if is_closing { + debug!("Connection ({:?}) is closed", self.token()); + self.is_closed = true; + } else { + // Re-register with the poll. + self.reregister(poll)?; + } + Ok(()) + } + + fn is_closed(&self) -> bool { + self.is_closed + } + + fn token(&self) -> Token { + self.connection_token + } +} + +/// Internal connection state. +#[derive(Debug, Clone)] +enum ConnectionState { + Closing, + Blocked, + Alive, + TlsHandshake, +} + +impl ConnectionState { + pub(crate) fn is_alive(&self) -> bool { + matches!(self, ConnectionState::Alive) + } + + pub(crate) fn is_closing(&self) -> bool { + matches!(self, ConnectionState::Closing) + } +} diff --git a/bitacross-worker/core/tls-websocket-server/src/connection_id_generator.rs b/bitacross-worker/core/tls-websocket-server/src/connection_id_generator.rs new file mode 100644 index 0000000000..dac5431cb6 --- /dev/null +++ b/bitacross-worker/core/tls-websocket-server/src/connection_id_generator.rs @@ -0,0 +1,76 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(feature = "sgx")] +use std::sync::SgxRwLock as RwLock; + +#[cfg(feature = "std")] +use std::sync::RwLock; + +use crate::{error::WebSocketError, WebSocketResult}; + +pub type ConnectionId = usize; + +/// Trait to generate IDs (nonce) for websocket connections. +pub trait GenerateConnectionId { + fn next_id(&self) -> WebSocketResult; +} + +pub struct ConnectionIdGenerator { + current_id: RwLock, +} + +const MIN_ID: usize = 10; + +impl Default for ConnectionIdGenerator { + fn default() -> Self { + Self { current_id: RwLock::new(MIN_ID) } + } +} + +impl GenerateConnectionId for ConnectionIdGenerator { + fn next_id(&self) -> WebSocketResult { + let mut id_lock = self.current_id.write().map_err(|_| WebSocketError::LockPoisoning)?; + *id_lock = id_lock.checked_add(1).unwrap_or(MIN_ID); + Ok(*id_lock) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::ws_server::{NEW_CONNECTIONS_LISTENER, SERVER_SIGNAL_TOKEN}; + + #[test] + fn next_id_works() { + let id_generator = ConnectionIdGenerator::default(); + + assert_eq!(11, id_generator.next_id().unwrap()); + assert_eq!(12, id_generator.next_id().unwrap()); + assert_eq!(13, id_generator.next_id().unwrap()); + } + + #[test] + fn next_id_is_greater_than_default_tokens() { + let id_generator = ConnectionIdGenerator::default(); + + let first_id = id_generator.next_id().unwrap(); + + assert!(NEW_CONNECTIONS_LISTENER < mio::Token(first_id)); + assert!(SERVER_SIGNAL_TOKEN < mio::Token(first_id)); + } +} diff --git a/bitacross-worker/core/tls-websocket-server/src/error.rs b/bitacross-worker/core/tls-websocket-server/src/error.rs new file mode 100644 index 0000000000..3d86b509dc --- /dev/null +++ b/bitacross-worker/core/tls-websocket-server/src/error.rs @@ -0,0 +1,55 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use crate::ConnectionId; +use std::{boxed::Box, io::Error as IoError, net::AddrParseError, string::String}; + +pub type WebSocketResult = Result; + +/// General web-socket error type +#[derive(Debug, thiserror::Error)] +pub enum WebSocketError { + #[error("Invalid certificate: {0}")] + InvalidCertificate(String), + #[error("Invalid private key: {0}")] + InvalidPrivateKey(String), + #[error("Invalid web-socket address: {0}")] + InvalidWsAddress(AddrParseError), + #[error("TCP bind: {0}")] + TcpBindError(IoError), + #[error("Web-socket hand shake: {0}")] + HandShakeError(String), + #[error("{0} is not a valid and active web-socket connection id")] + InvalidConnection(ConnectionId), + #[error("Web-socket connection already closed error")] + ConnectionClosed, + #[error("Web-socket connection has not yet been established")] + ConnectionNotYetEstablished, + #[error("Web-socket write: {0}")] + SocketWriteError(String), + #[error("Lock poisoning")] + LockPoisoning, + #[error("Failed to receive server signal message: {0}")] + MioReceiveError(#[from] std::sync::mpsc::TryRecvError), + #[error("{0}")] + IoError(#[from] std::io::Error), + #[error("{0}")] + Other(Box), +} diff --git a/bitacross-worker/core/tls-websocket-server/src/lib.rs b/bitacross-worker/core/tls-websocket-server/src/lib.rs new file mode 100644 index 0000000000..919e0526dc --- /dev/null +++ b/bitacross-worker/core/tls-websocket-server/src/lib.rs @@ -0,0 +1,177 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +// re-export module to properly feature gate sgx and regular std environment +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod sgx_reexport_prelude { + pub use mio_sgx as mio; + pub use rustls_sgx as rustls; + pub use thiserror_sgx as thiserror; + pub use tungstenite_sgx as tungstenite; + pub use webpki_sgx as webpki; + pub use yasna_sgx as yasna; +} + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use crate::{ + config_provider::FromFileConfigProvider, + connection_id_generator::{ConnectionId, ConnectionIdGenerator}, + error::{WebSocketError, WebSocketResult}, + ws_server::TungsteniteWsServer, +}; +use mio::{event::Evented, Token}; +use std::{ + fmt::Debug, + string::{String, ToString}, + sync::Arc, +}; + +pub mod certificate_generation; +pub mod config_provider; +mod connection; +pub mod connection_id_generator; +pub mod error; +mod stream_state; +mod tls_common; +pub mod ws_server; + +#[cfg(any(test, feature = "mocks"))] +pub mod test; + +/// Connection token alias. +#[derive(Eq, PartialEq, Clone, Copy, Debug, Hash)] +pub struct ConnectionToken(pub usize); + +impl From for Token { + fn from(c: ConnectionToken) -> Self { + Token(c.0) + } +} + +impl From for ConnectionToken { + fn from(t: Token) -> Self { + ConnectionToken(t.0) + } +} + +/// Handles a web-socket connection message. +pub trait WebSocketMessageHandler: Send + Sync { + fn handle_message( + &self, + connection_token: ConnectionToken, + message: String, + ) -> WebSocketResult>; +} + +/// Allows to send response messages to a specific connection. +pub trait WebSocketResponder: Send + Sync { + fn send_message( + &self, + connection_token: ConnectionToken, + message: String, + ) -> WebSocketResult<()>; +} + +/// Run a web-socket server with a given handler. +pub trait WebSocketServer { + type Connection; + + fn run(&self) -> WebSocketResult<()>; + + fn is_running(&self) -> WebSocketResult; + + fn shut_down(&self) -> WebSocketResult<()>; +} + +/// Abstraction of a web socket connection using mio. +pub(crate) trait WebSocketConnection: Send + Sync { + /// Socket type, typically a TCP stream. + type Socket: Evented; + + /// Get the underlying socket (TCP stream) + fn socket(&self) -> Option<&Self::Socket>; + + /// Query the underlying session for readiness (read/write). + fn get_session_readiness(&self) -> mio::Ready; + + /// Handles the ready event, the connection has work to do. + fn on_ready(&mut self, poll: &mut mio::Poll, ev: &mio::event::Event) -> WebSocketResult<()>; + + /// True if connection was closed. + fn is_closed(&self) -> bool; + + /// Return the connection token (= ID) + fn token(&self) -> mio::Token; + + /// Register the connection with the mio poll. + fn register(&mut self, poll: &mio::Poll) -> WebSocketResult<()> { + match self.socket() { + Some(s) => { + poll.register( + s, + self.token(), + self.get_session_readiness(), + mio::PollOpt::level() | mio::PollOpt::oneshot(), + )?; + Ok(()) + }, + None => Err(WebSocketError::ConnectionClosed), + } + } + + /// Re-register the connection with the mio poll, after handling an event. + fn reregister(&mut self, poll: &mio::Poll) -> WebSocketResult<()> { + match self.socket() { + Some(s) => { + poll.reregister( + s, + self.token(), + self.get_session_readiness(), + mio::PollOpt::level() | mio::PollOpt::oneshot(), + )?; + + Ok(()) + }, + None => Err(WebSocketError::ConnectionClosed), + } + } +} + +pub fn create_ws_server( + addr_plain: &str, + private_key: &str, + certificate: &str, + handler: Arc, +) -> Arc> +where + Handler: WebSocketMessageHandler, +{ + let config_provider = + Arc::new(FromFileConfigProvider::new(private_key.to_string(), certificate.to_string())); + + Arc::new(TungsteniteWsServer::new(addr_plain.to_string(), config_provider, handler)) +} diff --git a/bitacross-worker/core/tls-websocket-server/src/stream_state.rs b/bitacross-worker/core/tls-websocket-server/src/stream_state.rs new file mode 100644 index 0000000000..ef53a14b61 --- /dev/null +++ b/bitacross-worker/core/tls-websocket-server/src/stream_state.rs @@ -0,0 +1,105 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use log::*; +use mio::net::TcpStream; +use rustls::ServerSession; +use std::boxed::Box; +use tungstenite::{ + accept, + handshake::{server::NoCallback, MidHandshake}, + HandshakeError, ServerHandshake, WebSocket, +}; + +pub(crate) type RustlsStream = rustls::StreamOwned; +pub(crate) type RustlsServerHandshake = ServerHandshake; +pub(crate) type RustlsMidHandshake = MidHandshake; +pub(crate) type RustlsWebSocket = WebSocket; + +/// Internal TLS stream state. From pure TLS stream, to web-socket handshake and established WS. +pub(crate) enum StreamState { + Invalid, + TlsStream(Box), + WebSocketHandshake(RustlsMidHandshake), + EstablishedWebsocket(Box), +} + +impl Default for StreamState { + fn default() -> Self { + Self::Invalid + } +} + +impl StreamState { + pub(crate) fn from_stream(stream: RustlsStream) -> Self { + StreamState::TlsStream(Box::new(stream)) + } + + pub(crate) fn is_invalid(&self) -> bool { + matches!(self, StreamState::Invalid) + } + + pub(crate) fn internal_stream(&self) -> Option<&RustlsStream> { + match self { + StreamState::TlsStream(s) => Some(s), + StreamState::WebSocketHandshake(h) => Some(h.get_ref().get_ref()), + StreamState::EstablishedWebsocket(ws) => Some(ws.get_ref()), + StreamState::Invalid => None, + } + } + + pub(crate) fn internal_stream_mut(&mut self) -> Option<&mut RustlsStream> { + match self { + StreamState::TlsStream(s) => Some(s), + StreamState::WebSocketHandshake(h) => Some(h.get_mut().get_mut()), + StreamState::EstablishedWebsocket(ws) => Some(ws.get_mut()), + StreamState::Invalid => None, + } + } + + pub(crate) fn attempt_handshake(self) -> Self { + match self { + // We have the bare TLS stream only, attempt to do a web-socket handshake. + StreamState::TlsStream(tls_stream) => Self::from_handshake_result(accept(*tls_stream)), + // We already have an on-going handshake, attempt another try. + StreamState::WebSocketHandshake(hs) => Self::from_handshake_result(hs.handshake()), + _ => self, + } + } + + fn from_handshake_result( + handshake_result: Result>, + ) -> Self { + match handshake_result { + Ok(ws) => Self::EstablishedWebsocket(Box::new(ws)), + Err(e) => match e { + // I/O would block our handshake attempt. Need to re-try. + HandshakeError::Interrupted(mhs) => { + info!("Web-socket handshake interrupted"); + Self::WebSocketHandshake(mhs) + }, + HandshakeError::Failure(e) => { + error!("Web-socket handshake failed: {:?}", e); + Self::Invalid + }, + }, + } + } +} diff --git a/bitacross-worker/core/tls-websocket-server/src/test/fixtures/mod.rs b/bitacross-worker/core/tls-websocket-server/src/test/fixtures/mod.rs new file mode 100644 index 0000000000..6790e464c8 --- /dev/null +++ b/bitacross-worker/core/tls-websocket-server/src/test/fixtures/mod.rs @@ -0,0 +1,22 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pub mod no_cert_verifier; +pub mod test_cert; +pub mod test_private_key; +pub mod test_server; +pub mod test_server_config_provider; diff --git a/bitacross-worker/core/tls-websocket-server/src/test/fixtures/no_cert_verifier.rs b/bitacross-worker/core/tls-websocket-server/src/test/fixtures/no_cert_verifier.rs new file mode 100644 index 0000000000..50e05527ab --- /dev/null +++ b/bitacross-worker/core/tls-websocket-server/src/test/fixtures/no_cert_verifier.rs @@ -0,0 +1,51 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use log::debug; +use rustls::{Certificate, ClientCertVerified, DistinguishedNames, TLSError}; +use webpki::DNSName; + +/// Test Rustls verifier, disables ALL verification (do NOT use in production!) +pub struct NoCertVerifier {} + +impl rustls::ServerCertVerifier for NoCertVerifier { + fn verify_server_cert( + &self, + _: &rustls::RootCertStore, + _: &[rustls::Certificate], + _: webpki::DNSNameRef<'_>, + _: &[u8], + ) -> Result { + debug!("Certificate verification bypassed"); + Ok(rustls::ServerCertVerified::assertion()) + } +} + +impl rustls::ClientCertVerifier for NoCertVerifier { + fn client_auth_root_subjects(&self, _sni: Option<&DNSName>) -> Option { + None + } + + fn verify_client_cert( + &self, + _presented_certs: &[Certificate], + _sni: Option<&DNSName>, + ) -> Result { + debug!("Certificate verification bypassed"); + Ok(rustls::ClientCertVerified::assertion()) + } +} diff --git a/bitacross-worker/core/tls-websocket-server/src/test/fixtures/test_cert.rs b/bitacross-worker/core/tls-websocket-server/src/test/fixtures/test_cert.rs new file mode 100644 index 0000000000..1b94e7a24a --- /dev/null +++ b/bitacross-worker/core/tls-websocket-server/src/test/fixtures/test_cert.rs @@ -0,0 +1,139 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use rustls::{internal::pemfile::certs, Certificate}; +use std::{io::BufReader, vec::Vec}; + +pub fn get_test_certificate_chain() -> Vec { + let mut buf_reader = BufReader::new(CERT_STR.as_bytes()); + certs(&mut buf_reader).unwrap() +} + +const CERT_STR: &str = "\ +-----BEGIN CERTIFICATE----- +MIIEADCCAmigAwIBAgICAcgwDQYJKoZIhvcNAQELBQAwLDEqMCgGA1UEAwwhcG9u +eXRvd24gUlNBIGxldmVsIDIgaW50ZXJtZWRpYXRlMB4XDTE3MDQxMDIwNTYyN1oX +DTIyMTAwMTIwNTYyN1owGTEXMBUGA1UEAwwOdGVzdHNlcnZlci5jb20wggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCa4nonCxArES+kBBf9mZoaQ2GBMg74 +Pj2ve4RKJSIBt9A7EgJ4hFznFQ11O11Xvb3dVQGOK+pFRxh2xg0DJvV3lJytpvKe +mviyT5KSGvp6Hybqmx66B2V3iDfrXhhySqG5tKEeczFBIq+62dAp0+r0oSdpZKGT +1YDtXonjcbnDb93K7g8arEadFKYN3MAjBGQ3m5fsWJJuq4hLU1+dpmAfxmYH1dlc +n89LyPhYh0I7R5v17VrGlNCWIWD1emLtM8vTS94eMtp8R6MuMIZTOKgBTrIpU4G5 +GPcR3flDzzLsCxEttjjMa41zStKXzieUIwirRAzPv48V4JlkCCUPv97pAgMBAAGj +gb4wgbswDAYDVR0TAQH/BAIwADALBgNVHQ8EBAMCBsAwHQYDVR0OBBYEFNn77YZg +4AGguHBKVggK00dtRvhCMEIGA1UdIwQ7MDmAFGuwcG2Zfyr92yAiXU9HP9rBYC6/ +oR6kHDAaMRgwFgYDVQQDDA9wb255dG93biBSU0EgQ0GCAXswOwYDVR0RBDQwMoIO +dGVzdHNlcnZlci5jb22CFXNlY29uZC50ZXN0c2VydmVyLmNvbYIJbG9jYWxob3N0 +MA0GCSqGSIb3DQEBCwUAA4IBgQB4xB9IPNxkJIA8QtngQZCCSPH5SjfAibcLfwi2 +NLHe4hO4HvoIVv0ru7CODfq45qNfH7sUj8a/JBU8BwcJ3xPewWFdavtCP8+dapmd +pr831+Xx6p9tNIdW16WrCXEV8i9bHy43Y4pWbNdXQy5meI0qvSM/ExedZqqVeJJT +oXL/aCtMsBixlwlKvrsG9ZvIAl1ics0wA5kqQWVufe95loI+HUcPc9s9689H+/ON +lH8rTLPwyufk9h2dTb9Wzw3qewlDIqgoyX7k9cOwrJqA4D6typCvb5dWfQlK9c72 +4rGbqHSx7mrlaZ4typfAMdEbynRlDSgIIZGXb7RaoV3NT2XuVFd8+lcXgBiJMvPk +STejz77EPR2+uKvQ1gMJXpEHCBUvMMyDqhpcNzb0DaXgf4eYI9RqfxU1pkgYnfxe +DGDGI2SdmO43NwSDyEQVSlRpCIBj4ZDay3IP7mbdi8MLxR9H1BCHnN7D04UrTnuA +c/cl0RMWL+iHtKU2cCxltEQQ9qQ= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGnzCCAoegAwIBAgIBezANBgkqhkiG9w0BAQsFADAaMRgwFgYDVQQDDA9wb255 +dG93biBSU0EgQ0EwHhcNMTcwNDEwMjA1NjI3WhcNMjcwNDA4MjA1NjI3WjAsMSow +KAYDVQQDDCFwb255dG93biBSU0EgbGV2ZWwgMiBpbnRlcm1lZGlhdGUwggGiMA0G +CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDCX7V0gKGQBS64QKntjDlBslbQJaxq +EL8Yyq+qjF6nkOoqENKWSzeNyQ76kPVlzeV03UCaIgTF4+FeQrUr7wauEz0FGmDh +yx/B4xy9ZXdBIftPB8iz8Q/KrKO6YM6tkj7ijvL8hP3MfssBkA+VoAxamPSIikfM +9kyttemjYizgM0ywebzKmQGJbEINZ80Kp63ayR/Uo/cORjlH3xbmtTsL3pd+k6Ro +xOMZKm1RIwOwGgxDW4ea294A4lXHwfwHGMsP0/xmqTZ0R/EpxLKeqJAQffTiVsBK +YEFzANn3nol1IYrdcZcgcs16KTnc5+XyL87KSdIgDgG3wmQvRCdLX5G6GChyP03Z +qQSYMkwGSNgCD1v4m14Z5XT2su7iilHfjsucvT4OukCe63nqeXIZ+w63YqbjTp/a +HMgrXVg1wMlSncl0OIKcjLOgJ5vbPOGk9DvF93JbRFp/9sAZmK89Ur4gBmgpq2Zn +bknK0LVt+aerP7rf8CPYE89olPVUW0owwrkCAwEAAaNeMFwwHQYDVR0OBBYEFGuw +cG2Zfyr92yAiXU9HP9rBYC6/MCAGA1UdJQEB/wQWMBQGCCsGAQUFBwMBBggrBgEF +BQcDAjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIB/jANBgkqhkiG9w0BAQsFAAOC +BAEARD9wwIHsAFWlzrRLw3JcAUDB906Ks7KZzdqe3n6FkbyHHP1N5JG25wXwgADS +qux6sZU7h6+q209IEqXWbw+nbxJs/3D1hLN6X2tVgsfkSflGsd3DfNPZI8qKUyOD +VYlql/EPEMBixXOeVpwxXc48rX/yVjxqCvhY/A7eIiAc+bzQtwozLppChyVitQGI +MViXRdGdFiybwTKoJMYXl6ztamk9TWhdvJ9znirol12b06Z3J0Kz0c/kqY7VVZqL +ba76+IAJjvWQE7PYEOqpFHOLpilv9j5d/0kBR4AgJaooFwcYnr6aJKfNUgGWEmdn +ELYmfa0qORllAM/yGoewRfWGLZBNgT0QFYg2IFjnp0W0wIXFRd7xVqldN+cTmMqk +szpVV7bqGvuk6SQNFjIZ8VIVc/mXua4WlwBODDRzKqU3bIgBTODgVq1edwqp6UjN +ECLAOe1p03GGMr4WSPDoFjlQlHy+NLUwZg3RI+HsAkow9WfP7KqGN4vFDC4ru9Pg +2uD28oTrOgYQpzKjQJSH3kC5feOUdrsET7zic75XO1J33CAlgbIZ2TSQDqnH2cY5 +bQsWSNA2Lle3wBbeHlCy7ACiaoeJS23TJV9n8PcsRwSmHA9NgT4WSavXwtZ0lBhI +60GY80VXo9ziQjvVTMZNymZ4FEqCvULHGhFI08Jqd1jOXjnPLY4WEARqkicBJvI1 +3t4sBLDU+PEqH7m8k3lCZd6D7XVDcc8bJock+DjXZIMbZY79UMuzyHocXNJpRfRT +cqS0qneltFe6Pea7y0PN2IDttGBLb1CVQpXhRkpFU8jtyXh3ulSZSJEeqLVRFgdv +PVwHWAhLPewVGDkgTrlWVNfiXxp1LWVTFzQFas9xWiY4byQk/DNQaaFwHpGoZgVc +qAzUVk20Msm2u9xvSbPcBGk0dL4fdlnOkyeq/k/fnNrGdRHJWuJe7QR73/N0u6fy +7H76xUXvcwwrxL8ma8nV9K+A7oM7YUiR1wagD9cnoDDBgQmH9Izvfw0PxJgqnLOe +lQGPVGRhmXNtLLG57dqgjrvERGy9u5NMxBlkH0giZTFyQXPQ+N75ouM4S3RL75PM +UaTOBtnyCj++5ysnDFlGqEXgy08rrtkCbbNfd9dnO568juXS6ExC6TEL/pUMhy+Z +ooIJ69Tt7R5dOLaKRrkX/nKHfCfLfXXnjyDmdRHRYrXvTWusF038OsqY89tb0F0u +S4Szv4/Bl1bhzx/XYMZv/y7XL0va8FQLiRTuvqJ9hTsE/Xkd4ZFrP1LaP6HzVR1g +tsFs2Gc8j7H299U3WLjNon0TL2uPXa77Vu+9h7QCi1W9Uzsv0xMvZ/KMEnXyaEBd +W1lqo85ih1nnfxcW+lmAz8QNGQ== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIJCjCCBPKgAwIBAgIJAI+QZnVEkxq/MA0GCSqGSIb3DQEBCwUAMBoxGDAWBgNV +BAMMD3Bvbnl0b3duIFJTQSBDQTAeFw0xNzA0MTAyMDU2MjdaFw0yNzA0MDgyMDU2 +MjdaMBoxGDAWBgNVBAMMD3Bvbnl0b3duIFJTQSBDQTCCBCIwDQYJKoZIhvcNAQEB +BQADggQPADCCBAoCggQBAMD0iyFqRCNhvD5T9WXO8caNGb5ecrlnqSUvfcc+6Xh9 +sShtK6DX2DZ+6YT2WWOZTk0I9T+XG2kujjyfdCbEDMWcT9so7/gPeIG/qFlhONCu +HC+zntuZrGgMEYpF3Xc41CyF3saredTJEo1J64TPEke8mohezIGZYM1vTtRnqW+1 +RstSNTu8a/B0VaG0iA5P5RuSGVmxczi4EWJtuXFhcbgqICaUt0vJdrU0Fmrmq0Iq +ZEIpgZKYirx5QW8b6Q5tv0YsnXNasXvHZQve4GgF449ewk9wWfYevD8UttHUEe2a +QeEKb2l7NxqyY6trGyVtTRlm4SnoOH/9VodTKUEmS6pds6XFtjRflxgom0TL7CXb +uJ9b6fkXQlnf01FqAbv5HC1sjgGlSZc7Yk8k09nWOR8mZMoHC+U4KEq+oM+m87q4 +U/GsEk8UsPslGIIHHK6W/sdU6zA9bR3QYmkD40Z7FbVfKVvDmKPlwI7NONqysD8V +UTPoB8aE7FeulZhlTxdK2EcW14AsjbFiPQ4zAVxj4bRj39RLgJYL+BvAF6PfRHb1 +Xb7ykbuTvT7VhNYXLlQagR9EyixT3Wu9WCWUc0xJKSATn1s2YBLNM7LO4MkYO9WG +YrejhNHG+54a7rtnnlG04Gs7OhM32baMH/DxT+EEAX4j0Dfww4RaCZcfq1gDPsVe ++RzqsjjqF8+IzE25SK38xgwT/o3n9r5Ele3/zadwy695KCfbkhVFSDAPvhiv8um5 +6NNP+dDymFRXGzV85xSK75ue3Dpj+MoSScmIdGLEcU5EqYcBFLCXGLYPDIW8Lb89 +mG1z7TkZOLIs+6v7kp4rrvyijsyLFZ+EKUmabAK42qdzASZ1o6ETDDfFBETMxjWA +oMmGmRkhsyfBTuCr1ESlTBQHj4vvxBrgXgHtHwUinBw/sofLbkFRZ4wz/cBOtwqW +HIu88/o33l6ywMowcjaoToIbK2a4rD/KFJiwLliGKZG2veiESRhnNUQyjxT/PIef +0gqx3i1eBGWvfQs/wUC8qI5UadTRhjMFCwMCDVycevZE8lcQ+7zi9tVu6mXife5J +yP/jxRNDLzpdM6C6puqk0XieZey782XZ7sPpDpS2tphwakINF/5X3t1qZsssZPqq +F1S2VIsL8qm6Z7HDHXex3o2tDUhc226YSp/T7D+IWP3UCs0NjJrldakhnAd7ykxT +b2cDh09GDYSbji4Y6WmgIbSAurqk6kt4MWrfx4yfEAlp8ujH6788lRDAiXN1RgzC +k8r21IOJONDG7Qk1rS0YUV4XyGz4SEpBdPTI7RM1fl5bDn4e+OslBcfWh5XplZrz +4D4Z9YWVZ8X6d7CiPYZIg35oo/45KGel3Z8algziVkMCAwEAAaNTMFEwHQYDVR0O +BBYEFOWXlO0crUtBejJo87v9lwg8PlE6MB8GA1UdIwQYMBaAFOWXlO0crUtBejJo +87v9lwg8PlE6MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggQBADUQ +YqVmS9o3/5Qp7Tr+JL5ZNRP1fRmV5kAqaKDC5I9ONKRYahHvoI0ojE/j+pmwI4gf +mp5mW8EgsNfooovrzVSHxJgBO9r1ogMlL9AvwlvVxLbexvLXpM/1QjD/7FID/TaK +1q5mhSBKaEYXqQ+8DN25aVsI/bwHx4eP11Ft6YjhPqaX/WutE/IIAMbgASRFtBlO +foTm++fpdn8rCg5LiLLpWrRLC3zUSUtFd7if3wQ4vcDdck09v9PjD5Lk34aYkowJ +oARbVmBMpAxwkMXaThP1fT7xlYPDhAA26UXksT5xUNzFPbmOVReuFT0drhJlF6e6 +SLTjy2BcrYuz5ieBmmY6QboBYH3SzUFKuamvnHLSic3i3u9Ly68XUjOtDKgYB7Y5 +oZtfZT+YFmz/R6eLUcGRRfcmLJ+i/OXjgyKVkYBMDafW3RI9fRp46Yr/lvOv5gFW +Vrn3Tfc9cSbYQgE4vuKXcs4aVVeX8uAyjcucMV3eLdxaBLUAezTpJseRfqtH2kCk +3JIV6m2y6Tm5EhhaSiHKbe6FtPFKhpu7m9AlquUzhBU9Aq59mbKp6jtV0mWhYwKB +K6REmWQqqAOtHIs7UIXDeN1ZByJ7q+et57RvMgMHc5My0d6a+gQAUssH4i73sVTz +Uej57DW9L7hK0GQpzGzGIO/9lYTzWMVa8EZG1Fa5nUgMh3N3Oy6qUQIqr8E8xT2O +IbKKV6Acx6lBiwii4JkruEMgVVEdsDWDVdP8Ov5lJvvIPLWLqnXsZ2sKCyZrVkgc +PTXVtYBLmn7Tuwody2MSaBONSqleJ1oPQJ9lsAKyqX4xpX05ZJu2kNhST2oq2127 +378GS85DqKDM3P187mjU2G8moqWaGKr6byiIr7ea5TkqIzpC3tKW5QRHvX9aanz0 +akQx6F+l3l4L8J0cXaKasUJTaCk3cWPbbVzo8tQwwdxd0/MdJWrmitK85o+4gLqG +Cvn9VA4mnhjRR0XccxEtzmhSxBRWXoCF1+FnfDmXhPji+AmAhVqRwPkqX9T9H+54 +YG2ZA9Trxssme+QFSFCPZrHuw66ZI6GmKo6h+Hr2qew7LytASN+x2QyvRf7tSNmf +oUgmiD+CFpaH6exjrCC0/hcJ53Kv3E5GBvQskvOqgsUkW+nmsrm95YOosn+9MoQc +PIM6zQCmZ0N/6jHrEHnOnSnz03tGHsvPs6tMB6DKhQz9FNqlrLG7UHhlqhFWj9nv +H+Zh0oOwbcgcoxkk+W6LHLDpA3UpC1tlOzTlD2ektACvQQr/2A/fecpJN/7iWlX9 +BimWwRTS24bO5dX92Kb8V1TNO6ARd9TqOkPXRatysyh7it/MXpc5I2+t49hqlXoV +9Xpi4ds6s2cT8zZGDKI= +-----END CERTIFICATE-----"; diff --git a/bitacross-worker/core/tls-websocket-server/src/test/fixtures/test_private_key.rs b/bitacross-worker/core/tls-websocket-server/src/test/fixtures/test_private_key.rs new file mode 100644 index 0000000000..0e3ad60d01 --- /dev/null +++ b/bitacross-worker/core/tls-websocket-server/src/test/fixtures/test_private_key.rs @@ -0,0 +1,53 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use rustls::{internal::pemfile::rsa_private_keys, PrivateKey}; +use std::io::BufReader; + +pub fn get_test_private_key() -> PrivateKey { + let mut buf_reader = BufReader::new(PRIVATE_KEY_STR.as_bytes()); + rsa_private_keys(&mut buf_reader).unwrap().first().unwrap().clone() +} + +const PRIVATE_KEY_STR: &str = "\ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAmuJ6JwsQKxEvpAQX/ZmaGkNhgTIO+D49r3uESiUiAbfQOxIC +eIRc5xUNdTtdV7293VUBjivqRUcYdsYNAyb1d5Scrabynpr4sk+Skhr6eh8m6pse +ugdld4g3614YckqhubShHnMxQSKvutnQKdPq9KEnaWShk9WA7V6J43G5w2/dyu4P +GqxGnRSmDdzAIwRkN5uX7FiSbquIS1NfnaZgH8ZmB9XZXJ/PS8j4WIdCO0eb9e1a +xpTQliFg9Xpi7TPL00veHjLafEejLjCGUzioAU6yKVOBuRj3Ed35Q88y7AsRLbY4 +zGuNc0rSl84nlCMIq0QMz7+PFeCZZAglD7/e6QIDAQABAoIBAQCEe5i08Nehnw+7 +Ie1LdSnFsUEj+6emW8bz5ZlguqZ+BbbN8DfA0qeM2gsq7d6IALr5KY8tBw9atteM +MRhMS/THloz2VMlPNYvpKftbkkwSTbdCEfGUemMmfZQnddM/X+s6J/FxVGMbLgpW +r51JSgW9vmMx2WwEQioH4EfeDxcwvZi3LF7SAo89eMSiSDqHZaIfMRmS0cSpoXav +u7gKDt7H+zSeYdLC4FhD4f8zRUpZEa4x5GIIm2JHsvIWuy9XKyepakaObJkWWqR1 +ATO94LtM2+RRVUev+yOVDDOfJtDzEqZrbokCHaVBYXgliAV/XkvFox1ZINyeGFq4 +kAvqfiQJAoGBAMhO/tAz2TpWeETMcujBekx1JmtDEUITJroDT0DvFDV5QRKVopxY +ZY5pPbwtk60KknBbsXrswR3Vh1q3xfKLT3Ln4x121ufltIwN7eopY9dXVqh830CU +QymtUz5VcvG3foWCeABcyklpZIdhHyDDDDP46URfFr3NnQiRnx7qb6yPAoGBAMXy +bSGgnBPUOWHtNW4hI5vxiOiCGWvCq7jERixybGMU8+kP6eRWUEAnOdCibq84A6gv +GLO5EW+bmL8l7L797w6ZN9DhbuR7W7hQVwdkyQS8PUgmTfsaba7+9hTC0chl+L38 +A7NlYRju+JS99SqarGA6WMvo30ykiMGwxw8tHOkHAoGAPT6Z/oK72nBx2WdBgxUV +FaeEFaut7Sv53UoBw3LWFPt7//isfW0xr/dRnuW4j2H6IEyI2XLmIP8WoZAq/9vE +cPeho3KghsrfByuDIOOC2Wak4mM7x30NhAKwvxBVUr6t+phHpKS6XPPSfuodIGFC +q+lhOTxxsZradrI/mq5HctUCgYEAqo4bYeIVGTC+0JWmd+Gt4OvYXx3Z8XOmqmjT +XfCpWyXuk13W1ZtZQi2KLy4F2IuW+w65ZgGL+HJExk5TEq2RkS6LXTsgZVW0zbbL +hd9dJOtckhIPFtDKuQGN3o2OW/EgxfGi7qvnYahmHyMdXzwuUitz3x4jaNJL0zgS +DA1+33kCgYA1iAZ58XXJPh6YObvw+kg21dCLLelxp+mCoRBSbY6wq+R6PmKg4a1N +oOc6Rh/1teyBVWJ/KnkXBeh9//XLfhg0r6zHDSCsDKabeM0eoB1AKWlc5f6bWYHV +60JHDgby+V1AElKT2yQT8KVv1hWJH4XQ1/fTQpQDDoo6O+nj1r4q6w== +-----END RSA PRIVATE KEY-----"; diff --git a/bitacross-worker/core/tls-websocket-server/src/test/fixtures/test_server.rs b/bitacross-worker/core/tls-websocket-server/src/test/fixtures/test_server.rs new file mode 100644 index 0000000000..6992b27e71 --- /dev/null +++ b/bitacross-worker/core/tls-websocket-server/src/test/fixtures/test_server.rs @@ -0,0 +1,41 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + test::{ + fixtures::test_server_config_provider::TestServerConfigProvider, + mocks::web_socket_handler_mock::WebSocketHandlerMock, + }, + TungsteniteWsServer, +}; +use std::{string::String, sync::Arc}; + +pub type TestServer = TungsteniteWsServer; + +pub fn create_server( + handler_responses: Vec, + port: u16, +) -> (Arc, Arc) { + let config_provider = Arc::new(TestServerConfigProvider {}); + let handler = Arc::new(WebSocketHandlerMock::from_response_sequence(handler_responses)); + + let server_addr_string = format!("127.0.0.1:{}", port); + + let server = + Arc::new(TungsteniteWsServer::new(server_addr_string, config_provider, handler.clone())); + (server, handler) +} diff --git a/bitacross-worker/core/tls-websocket-server/src/test/fixtures/test_server_config_provider.rs b/bitacross-worker/core/tls-websocket-server/src/test/fixtures/test_server_config_provider.rs new file mode 100644 index 0000000000..7f267aadf5 --- /dev/null +++ b/bitacross-worker/core/tls-websocket-server/src/test/fixtures/test_server_config_provider.rs @@ -0,0 +1,43 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + config_provider::ProvideServerConfig, + test::fixtures::{ + test_cert::get_test_certificate_chain, test_private_key::get_test_private_key, + }, + WebSocketResult, +}; +use rustls::{NoClientAuth, ServerConfig}; +use std::sync::Arc; + +pub struct TestServerConfigProvider; + +impl ProvideServerConfig for TestServerConfigProvider { + fn get_config(&self) -> WebSocketResult> { + let mut config = rustls::ServerConfig::new(NoClientAuth::new()); + + let certs = get_test_certificate_chain(); + let privkey = get_test_private_key(); + + config + .set_single_cert_with_ocsp_and_sct(certs, privkey, vec![], vec![]) + .unwrap(); + + Ok(Arc::new(config)) + } +} diff --git a/bitacross-worker/core/tls-websocket-server/src/test/mocks/mod.rs b/bitacross-worker/core/tls-websocket-server/src/test/mocks/mod.rs new file mode 100644 index 0000000000..fd5dff2b6c --- /dev/null +++ b/bitacross-worker/core/tls-websocket-server/src/test/mocks/mod.rs @@ -0,0 +1,19 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pub mod web_socket_connection_mock; +pub mod web_socket_handler_mock; diff --git a/bitacross-worker/core/tls-websocket-server/src/test/mocks/web_socket_connection_mock.rs b/bitacross-worker/core/tls-websocket-server/src/test/mocks/web_socket_connection_mock.rs new file mode 100644 index 0000000000..24620c9af2 --- /dev/null +++ b/bitacross-worker/core/tls-websocket-server/src/test/mocks/web_socket_connection_mock.rs @@ -0,0 +1,103 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{error::WebSocketResult, WebSocketConnection}; +use mio::{Event, Evented, Poll, PollOpt, Ready, Token}; +use std::vec::Vec; +use tungstenite::Message; + +/// Mock implementation of a web socket connection. +#[derive(PartialEq, Eq, Clone)] +pub(crate) struct WebSocketConnectionMock { + pub id: Token, + pub messages_to_read: Vec, + pub messages_written: Vec, + pub is_closed: bool, + socket: SocketMock, +} + +impl WebSocketConnectionMock { + #[allow(unused)] + pub fn new(id: Token) -> Self { + WebSocketConnectionMock { + id, + messages_to_read: Default::default(), + messages_written: Default::default(), + is_closed: false, + socket: SocketMock {}, + } + } + + #[allow(unused)] + pub fn with_messages_to_read(mut self, messages: Vec) -> Self { + self.messages_to_read = messages; + self + } +} + +impl WebSocketConnection for WebSocketConnectionMock { + type Socket = SocketMock; + + fn socket(&self) -> Option<&Self::Socket> { + Some(&self.socket) + } + + fn get_session_readiness(&self) -> Ready { + Ready::readable() + } + + fn on_ready(&mut self, _poll: &mut Poll, _ev: &Event) -> WebSocketResult<()> { + Ok(()) + } + + fn is_closed(&self) -> bool { + self.is_closed + } + + fn token(&self) -> Token { + self.id + } +} + +#[derive(PartialEq, Eq, Clone)] +pub(crate) struct SocketMock; + +impl Evented for SocketMock { + fn register( + &self, + _poll: &Poll, + _token: Token, + _interest: Ready, + _opts: PollOpt, + ) -> std::io::Result<()> { + Ok(()) + } + + fn reregister( + &self, + _poll: &Poll, + _token: Token, + _interest: Ready, + _opts: PollOpt, + ) -> std::io::Result<()> { + Ok(()) + } + + fn deregister(&self, _poll: &Poll) -> std::io::Result<()> { + Ok(()) + } +} diff --git a/bitacross-worker/core/tls-websocket-server/src/test/mocks/web_socket_handler_mock.rs b/bitacross-worker/core/tls-websocket-server/src/test/mocks/web_socket_handler_mock.rs new file mode 100644 index 0000000000..26d9b3d61c --- /dev/null +++ b/bitacross-worker/core/tls-websocket-server/src/test/mocks/web_socket_handler_mock.rs @@ -0,0 +1,68 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(feature = "sgx")] +use std::sync::SgxRwLock as RwLock; + +#[cfg(feature = "std")] +use std::sync::RwLock; + +use crate::{ConnectionToken, WebSocketMessageHandler, WebSocketResult}; +use log::debug; +use std::{collections::HashMap, string::String, vec::Vec}; + +pub struct WebSocketHandlerMock { + pub responses: Vec, + pub connection_message_indices: RwLock>, + pub messages_handled: RwLock>, +} + +impl WebSocketHandlerMock { + pub fn from_response_sequence(responses: Vec) -> Self { + WebSocketHandlerMock { + responses, + connection_message_indices: RwLock::default(), + messages_handled: Default::default(), + } + } + + pub fn get_handled_messages(&self) -> Vec<(ConnectionToken, String)> { + self.messages_handled.read().unwrap().clone() + } +} + +impl WebSocketMessageHandler for WebSocketHandlerMock { + fn handle_message( + &self, + connection_token: ConnectionToken, + message: String, + ) -> WebSocketResult> { + let mut handled_messages_lock = self.messages_handled.write().unwrap(); + + debug!("Handling message: {}", message); + handled_messages_lock.push((connection_token, message)); + + let mut connection_indices_lock = self.connection_message_indices.write().unwrap(); + + let message_index = connection_indices_lock.entry(connection_token).or_insert(0usize); + + let response = self.responses.get(*message_index).cloned(); + + *message_index += 1; + Ok(response) + } +} diff --git a/bitacross-worker/core/tls-websocket-server/src/test/mod.rs b/bitacross-worker/core/tls-websocket-server/src/test/mod.rs new file mode 100644 index 0000000000..0d2c1da1d4 --- /dev/null +++ b/bitacross-worker/core/tls-websocket-server/src/test/mod.rs @@ -0,0 +1,19 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pub mod fixtures; +pub mod mocks; diff --git a/bitacross-worker/core/tls-websocket-server/src/tls_common.rs b/bitacross-worker/core/tls-websocket-server/src/tls_common.rs new file mode 100644 index 0000000000..c2061abf87 --- /dev/null +++ b/bitacross-worker/core/tls-websocket-server/src/tls_common.rs @@ -0,0 +1,70 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use crate::{error::WebSocketError, WebSocketResult}; +use rustls::NoClientAuth; +use std::{io::BufReader, string::ToString, sync::Arc, vec, vec::Vec}; + +pub fn make_config(cert: &str, key: &str) -> WebSocketResult> { + let mut config = rustls::ServerConfig::new(NoClientAuth::new()); + + let certs = load_certs(cert)?; + let privkey = load_private_key(key)?; + + config + .set_single_cert_with_ocsp_and_sct(certs, privkey, vec![], vec![]) + .expect("Invalid key der"); + + Ok(Arc::new(config)) +} + +fn load_certs(pem_content: &str) -> WebSocketResult> { + let mut reader = BufReader::new(pem_content.as_bytes()); + rustls::internal::pemfile::certs(&mut reader) + .map_err(|_| WebSocketError::InvalidCertificate("Failed to parse certificate".to_string())) +} + +fn load_private_key(pem_content: &str) -> WebSocketResult { + let rsa_keys = { + let mut reader = BufReader::new(pem_content.as_bytes()); + + rustls::internal::pemfile::rsa_private_keys(&mut reader).map_err(|_| { + WebSocketError::InvalidPrivateKey("Failed to parse RSA private key".to_string()) + })? + }; + + let pkcs8_keys = { + let mut reader = BufReader::new(pem_content.as_bytes()); + rustls::internal::pemfile::pkcs8_private_keys(&mut reader).map_err(|_| { + WebSocketError::InvalidPrivateKey( + "Invalid PKCS8 private key (encrypted keys are not supported)".to_string(), + ) + })? + }; + + // prefer to load pkcs8 keys + if !pkcs8_keys.is_empty() { + Ok(pkcs8_keys[0].clone()) + } else if !rsa_keys.is_empty() { + Ok(rsa_keys[0].clone()) + } else { + Err(WebSocketError::InvalidPrivateKey("No viable private keys were given".to_string())) + } +} diff --git a/bitacross-worker/core/tls-websocket-server/src/ws_server.rs b/bitacross-worker/core/tls-websocket-server/src/ws_server.rs new file mode 100644 index 0000000000..cacac43b33 --- /dev/null +++ b/bitacross-worker/core/tls-websocket-server/src/ws_server.rs @@ -0,0 +1,518 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +#[cfg(feature = "sgx")] +use std::sync::{SgxMutex as Mutex, SgxRwLock as RwLock}; + +#[cfg(feature = "std")] +use std::sync::{Mutex, RwLock}; + +use crate::{ + config_provider::ProvideServerConfig, + connection::TungsteniteWsConnection, + connection_id_generator::GenerateConnectionId, + error::{WebSocketError, WebSocketResult}, + ConnectionIdGenerator, ConnectionToken, WebSocketConnection, WebSocketMessageHandler, + WebSocketResponder, WebSocketServer, +}; +use log::*; +use mio::{ + event::{Event, Evented}, + net::TcpListener, + Poll, +}; +use mio_extras::channel::{channel, Receiver, Sender}; +use net::SocketAddr; +use rustls::ServerConfig; +use std::{collections::HashMap, format, net, string::String, sync::Arc}; + +// Default tokens for the server. +pub(crate) const NEW_CONNECTIONS_LISTENER: mio::Token = mio::Token(0); +pub(crate) const SERVER_SIGNAL_TOKEN: mio::Token = mio::Token(1); + +/// Secure web-socket server implementation using the Tungstenite library. +pub struct TungsteniteWsServer { + ws_address: String, + config_provider: Arc, + connection_handler: Arc, + id_generator: ConnectionIdGenerator, + connections: RwLock>>, + is_running: RwLock, + signal_sender: Mutex>>, +} + +impl TungsteniteWsServer +where + ConfigProvider: ProvideServerConfig, + Handler: WebSocketMessageHandler, +{ + pub fn new( + ws_address: String, + config_provider: Arc, + connection_handler: Arc, + ) -> Self { + TungsteniteWsServer { + ws_address, + config_provider, + connection_handler, + id_generator: ConnectionIdGenerator::default(), + connections: Default::default(), + is_running: Default::default(), + signal_sender: Default::default(), + } + } + + fn accept_connection( + &self, + poll: &mut Poll, + tcp_listener: &TcpListener, + tls_config: Arc, + ) -> WebSocketResult<()> { + let (socket, addr) = tcp_listener.accept()?; + + debug!("Accepting new connection from {:?}", addr); + + let tls_session = rustls::ServerSession::new(&tls_config); + let connection_id = self.id_generator.next_id()?; + let token = mio::Token(connection_id); + trace!("New connection has token {:?}", token); + + let mut web_socket_connection = TungsteniteWsConnection::new( + socket, + tls_session, + token, + self.connection_handler.clone(), + )?; + + trace!("Web-socket connection created"); + web_socket_connection.register(poll)?; + + let mut connections_lock = + self.connections.write().map_err(|_| WebSocketError::LockPoisoning)?; + connections_lock.insert(token, web_socket_connection); + + debug!("Accepted connection, {} active connections", connections_lock.len()); + Ok(()) + } + + fn connection_event(&self, poll: &mut mio::Poll, event: &Event) -> WebSocketResult<()> { + let token = event.token(); + + let mut connections_lock = + self.connections.write().map_err(|_| WebSocketError::LockPoisoning)?; + + if let Some(connection) = connections_lock.get_mut(&token) { + connection.on_ready(poll, event)?; + + if connection.is_closed() { + trace!("Connection {:?} is closed, removing", token); + connections_lock.remove(&token); + trace!( + "Closed {:?}, {} active connections remaining", + token, + connections_lock.len() + ); + } + } + + Ok(()) + } + + /// Send a message response to a connection. + /// Make sure this is called inside the event loop, otherwise dead-locks are possible. + fn write_message_to_connection( + &self, + message: String, + connection_token: ConnectionToken, + ) -> WebSocketResult<()> { + let mut connections_lock = + self.connections.write().map_err(|_| WebSocketError::LockPoisoning)?; + let connection = connections_lock + .get_mut(&connection_token.into()) + .ok_or_else(|| WebSocketError::InvalidConnection(connection_token.0))?; + connection.write_message(message) + } + + fn handle_server_signal( + &self, + poll: &mut mio::Poll, + event: &Event, + signal_receiver: &mut Receiver, + ) -> WebSocketResult { + let signal = signal_receiver.try_recv()?; + let mut do_shutdown = false; + + match signal { + ServerSignal::ShutDown => { + do_shutdown = true; + }, + ServerSignal::SendResponse(message, connection_token) => { + if let Err(e) = self.write_message_to_connection(message, connection_token) { + error!("Failed to send web-socket response: {:?}", e); + } + }, + } + + signal_receiver.reregister( + poll, + event.token(), + mio::Ready::readable(), + mio::PollOpt::level(), + )?; + + Ok(do_shutdown) + } + + fn register_server_signal_sender(&self, sender: Sender) -> WebSocketResult<()> { + let mut sender_lock = + self.signal_sender.lock().map_err(|_| WebSocketError::LockPoisoning)?; + *sender_lock = Some(sender); + Ok(()) + } + + fn send_server_signal(&self, server_signal: ServerSignal) -> WebSocketResult<()> { + match self.signal_sender.lock().map_err(|_| WebSocketError::LockPoisoning)?.as_ref() { + None => { + warn!( + "Signal sender has not been initialized, cannot send web-socket server signal" + ); + }, + Some(signal_sender) => { + signal_sender + .send(server_signal) + .map_err(|e| WebSocketError::Other(format!("{:?}", e).into()))?; + }, + } + + Ok(()) + } +} + +impl WebSocketServer for TungsteniteWsServer +where + ConfigProvider: ProvideServerConfig, + Handler: WebSocketMessageHandler, +{ + type Connection = TungsteniteWsConnection; + + fn run(&self) -> WebSocketResult<()> { + debug!("Running tungstenite web socket server on {}", self.ws_address); + + let socket_addr: SocketAddr = + self.ws_address.parse().map_err(WebSocketError::InvalidWsAddress)?; + + let config = self.config_provider.get_config()?; + + let (server_signal_sender, mut signal_receiver) = channel::(); + self.register_server_signal_sender(server_signal_sender)?; + + let tcp_listener = net::TcpListener::bind(socket_addr).expect("Could not listen on port"); + let tcp_listener = + mio::net::TcpListener::from_std(tcp_listener).map_err(WebSocketError::TcpBindError)?; + let mut poll = Poll::new()?; + poll.register( + &tcp_listener, + NEW_CONNECTIONS_LISTENER, + mio::Ready::readable(), + mio::PollOpt::level(), + )?; + + poll.register( + &signal_receiver, + SERVER_SIGNAL_TOKEN, + mio::Ready::readable(), + mio::PollOpt::level(), + )?; + + let mut events = mio::Events::with_capacity(2048); + + *self.is_running.write().map_err(|_| WebSocketError::LockPoisoning)? = true; + + // Run the event loop. + 'outer_event_loop: loop { + let num_events = poll.poll(&mut events, None)?; + debug!("Number of readiness events: {}", num_events); + + for event in events.iter() { + match event.token() { + NEW_CONNECTIONS_LISTENER => { + trace!("Received new connection event"); + if let Err(e) = + self.accept_connection(&mut poll, &tcp_listener, config.clone()) + { + error!("Failed to accept new web-socket connection: {:?}", e); + } + }, + SERVER_SIGNAL_TOKEN => { + trace!("Received server signal event"); + if self.handle_server_signal(&mut poll, &event, &mut signal_receiver)? { + break 'outer_event_loop + } + }, + _ => { + trace!("Connection (token {:?}) activity event", event.token()); + if let Err(e) = self.connection_event(&mut poll, &event) { + error!("Failed to process connection event: {:?}", e); + } + }, + } + } + } + + info!("Web-socket server has shut down"); + Ok(()) + } + + fn is_running(&self) -> WebSocketResult { + Ok(*self.is_running.read().map_err(|_| WebSocketError::LockPoisoning)?) + } + + fn shut_down(&self) -> WebSocketResult<()> { + info!("Shutdown request of web-socket server detected, shutting down.."); + self.send_server_signal(ServerSignal::ShutDown) + } +} + +impl WebSocketResponder for TungsteniteWsServer +where + ConfigProvider: ProvideServerConfig, + Handler: WebSocketMessageHandler, +{ + fn send_message( + &self, + connection_token: ConnectionToken, + message: String, + ) -> WebSocketResult<()> { + self.send_server_signal(ServerSignal::SendResponse(message, connection_token)) + } +} + +/// Internal server signal enum. +enum ServerSignal { + ShutDown, + SendResponse(String, ConnectionToken), +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test::{ + fixtures::{no_cert_verifier::NoCertVerifier, test_server::create_server}, + mocks::web_socket_handler_mock::WebSocketHandlerMock, + }; + use rustls::ClientConfig; + use std::{net::TcpStream, thread, time::Duration}; + use tungstenite::{ + client_tls_with_config, stream::MaybeTlsStream, Connector, Message, WebSocket, + }; + use url::Url; + + #[test] + fn server_handles_multiple_connections() { + let _ = env_logger::builder().is_test(true).try_init(); + + let expected_answer = "websocket server response bidibibup".to_string(); + let port: u16 = 21777; + const NUMBER_OF_CONNECTIONS: usize = 100; + + let (server, handler) = create_server(vec![expected_answer.clone()], port); + + let server_clone = server.clone(); + let server_join_handle = thread::spawn(move || server_clone.run()); + + // Wait until server is up. + while !server.is_running().unwrap() { + thread::sleep(std::time::Duration::from_millis(50)); + } + + // Spawn multiple clients that connect to the server simultaneously and send a message. + let client_handles: Vec<_> = (0..NUMBER_OF_CONNECTIONS) + .map(|_| { + let expected_answer_clone = expected_answer.clone(); + + thread::sleep(Duration::from_millis(5)); + + thread::spawn(move || { + let mut socket = connect_tls_client(get_server_addr(port).as_str()); + + socket + .write_message(Message::Text("Hello WebSocket".into())) + .expect("client write message to be successful"); + + assert_eq!( + Message::Text(expected_answer_clone), + socket.read_message().unwrap() + ); + + thread::sleep(Duration::from_millis(2)); + + socket + .write_message(Message::Text("Second message".into())) + .expect("client write message to be successful"); + + thread::sleep(Duration::from_millis(2)); + + socket.close(None).unwrap(); + socket.write_pending().unwrap(); + }) + }) + .collect(); + + for handle in client_handles.into_iter() { + handle.join().expect("client handle to be joined"); + } + + server.shut_down().unwrap(); + + let server_shutdown_result = + server_join_handle.join().expect("Couldn't join on the associated thread"); + if let Err(e) = server_shutdown_result { + panic!("Test failed, web-socket returned error: {:?}", e); + } + + assert_eq!(2 * NUMBER_OF_CONNECTIONS, handler.get_handled_messages().len()); + } + + #[test] + fn server_closes_connection_if_client_does_not_wait_for_reply() { + let _ = env_logger::builder().is_test(true).try_init(); + + let expected_answer = "websocket server response".to_string(); + let port: u16 = 21778; + + let (server, handler) = create_server(vec![expected_answer.clone()], port); + + let server_clone = server.clone(); + let server_join_handle = thread::spawn(move || server_clone.run()); + + // Wait until server is up. + while !server.is_running().unwrap() { + thread::sleep(std::time::Duration::from_millis(50)); + } + + let client_join_handle = thread::spawn(move || { + let mut socket = connect_tls_client(get_server_addr(port).as_str()); + socket + .write_message(Message::Text("First request".into())) + .expect("client write message to be successful"); + + // We never read, just send a message and close the connection, despite the server + // trying to send a reply (which will fail). + socket.close(None).unwrap(); + socket.write_pending().unwrap(); + }); + + client_join_handle.join().unwrap(); + server.shut_down().unwrap(); + server_join_handle.join().unwrap().unwrap(); + + assert_eq!(1, handler.get_handled_messages().len()); + } + + #[test] + fn server_sends_update_message_to_client() { + let _ = env_logger::builder().is_test(true).try_init(); + + let expected_answer = "first response".to_string(); + let port: u16 = 21779; + let (server, handler) = create_server(vec![expected_answer.clone()], port); + + let server_clone = server.clone(); + let server_join_handle = thread::spawn(move || server_clone.run()); + + // Wait until server is up. + while !server.is_running().unwrap() { + thread::sleep(std::time::Duration::from_millis(50)); + } + + let update_message = "Message update".to_string(); + let update_message_clone = update_message.clone(); + + let client_join_handle = thread::spawn(move || { + let mut socket = connect_tls_client(get_server_addr(port).as_str()); + socket + .write_message(Message::Text("First request".into())) + .expect("client write message to be successful"); + + assert_eq!(Message::Text(expected_answer), socket.read_message().unwrap()); + assert_eq!(Message::Text(update_message_clone), socket.read_message().unwrap()); + }); + + let connection_token = poll_handler_for_first_connection(handler.as_ref()); + + // Send reply to a wrong connection token. Succeeds, because error is caught in the event loop + // and not the `send_message` method itself. + assert!(server + .send_message( + ConnectionToken(connection_token.0 + 1), + "wont get to the client".to_string() + ) + .is_ok()); + + // Send reply to the correct connection token. + server.send_message(connection_token, update_message).unwrap(); + + client_join_handle.join().unwrap(); + server.shut_down().unwrap(); + server_join_handle.join().unwrap().unwrap(); + + assert_eq!(1, handler.get_handled_messages().len()); + } + + // Ignored because it does not directly test any of our own components. + // It was used to test the behavior of the tungstenite client configuration with certificates. + #[test] + #[ignore] + fn client_test() { + let mut socket = connect_tls_client("ws.ifelse.io:443"); + + socket + .write_message(Message::Text("Hello WebSocket".into())) + .expect("client write message to be successful"); + } + + fn poll_handler_for_first_connection(handler: &WebSocketHandlerMock) -> ConnectionToken { + loop { + match handler.get_handled_messages().first() { + None => thread::sleep(Duration::from_millis(5)), + Some(m) => return m.0, + } + } + } + + fn get_server_addr(port: u16) -> String { + format!("localhost:{}", port) + } + + fn connect_tls_client(server_addr: &str) -> WebSocket> { + let ws_server_url = Url::parse(format!("wss://{}", server_addr).as_str()).unwrap(); + + let mut config = ClientConfig::new(); + config.dangerous().set_certificate_verifier(Arc::new(NoCertVerifier {})); + let connector = Connector::Rustls(Arc::new(config)); + let stream = TcpStream::connect(server_addr).unwrap(); + + let (socket, _response) = + client_tls_with_config(ws_server_url, stream, None, Some(connector)) + .expect("Can't connect"); + + socket + } +} diff --git a/bitacross-worker/docker/README.md b/bitacross-worker/docker/README.md new file mode 100644 index 0000000000..7f9ddb7a86 --- /dev/null +++ b/bitacross-worker/docker/README.md @@ -0,0 +1,116 @@ +# How to run the multi-validateer docker setup + +## Prerequisite + +* Make sure you have installed Docker (version >= `2.0.0`) with [Docker Compose](https://docs.docker.com/compose/install/). On Windows, this can be Docker Desktop with WSL 2 integration. +* In case you also build the worker directly, without docker (e.g. on a dev machine, running `make`), you should run `make clean` before running the docker build. Otherwise, it can occasionally lead to build errors. +* The node image version that is loaded in the `docker-compose.yml`, (e.g. `image: "integritee/integritee-node:1.1.3"`) needs to be compatible with the worker you're trying to build. +* Set export VERSION=dev +* `envsubst` should be installed, it is needed to replace the $VERSION in yaml files as docker compose doesn't support variables on service names. + +## Building the Docker containers + +Run +``` +COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker compose -f <(envsubst < docker-compose.yml) build +``` +in this folder to build the worker image. This will build the worker from source and tag it in an image called `integritee-worker:dev`. + +## Running the docker setup + +``` +docker compose -f <(envsubst < docker-compose.yml) up +``` +Starts all services (node and workers), using the `integritee-worker:dev` images you've built in the previous step. + +## Run the demos + +### Demo indirect invocation (M6) +Build +``` +COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker compose -f <(envsubst < docker-compose.yml) -f <(envsubst < demo-shielding-unshielding-multiworker.yml) build --build-arg WORKER_MODE_ARG=offchain-worker +``` +Run +``` +FLAVOR_ID=offchain-worker docker compose -f <(envsubst < docker-compose.yml) -f <(envsubst < demo-shielding-unshielding-multiworker.yml) up demo-shielding-unshielding-multiworker --exit-code-from demo-shielding-unshielding-multiworker +``` +### Demo direct call (M8) + +Build +``` +COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker compose -f <(envsubst < docker-compose.yml) -f <(envsubst < demo-direct-call.yml) build --build-arg WORKER_MODE_ARG=sidechain +``` +Run +``` +docker compose -f <(envsubst < docker-compose.yml) -f <(envsubst < demo-direct-call.yml) up demo-direct-call --exit-code-from demo-direct-call +``` + +### Demo sidechain +Build +``` +COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker compose -f <(envsubst < docker-compose.yml) -f <(envsubst < demo-sidechain.yml) build --build-arg WORKER_MODE_ARG=sidechain +``` +Run +``` +docker compose -f <(envsubst < docker-compose.yml) -f <(envsubst < demo-sidechain.yml) up demo-sidechain --exit-code-from demo-sidechain +``` + +### Demo Teeracle +Build +``` +COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker compose -f <(envsubst < docker-compose.yml) -f <(envsubst < demo-teeracle.yml) build --build-arg WORKER_MODE_ARG=teeracle +``` +Run +``` +docker compose -f <(envsubst < docker-compose.yml) -f <(envsubst < demo-teeracle.yml) up demo-teeracle --exit-code-from demo-teeracle +``` + + +## Run the benchmarks +Build with +``` +COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker compose -f <(envsubst < docker-compose.yml) -f <(envsubst < sidechain-benchmark.yml) build +``` +and then run with +``` +docker compose -f <(envsubst < docker-compose.yml) -f <(envsubst < sidechain-benchmark.yml) up sidechain-benchmark --exit-code-from sidechain-benchmark +``` + +## Run the fork simulator +The fork simulation uses `pumba` which in turn uses the Linux traffic control (TC). This is only available on Linux hosts, not on Windows with WSL unfortunately. +Build the docker compose setup with +``` +COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker compose -f <(envsubst < docker-compose.yml) -f <(envsubst < fork-inducer.yml) -f <(envsubst < demo-sidechain.yml) build --build-arg WORKER_MODE_ARG=sidechain +``` + +This requires the docker BuildKit (docker version >= 18.09) and support for it in docker compose (version >= 1.25.0) + +Run the 2-worker setup with a fork inducer (pumba) that delays the traffic on worker 2 +``` +docker compose -f <(envsubst < docker-compose.yml) -f <(envsubst < fork-inducer.yml) -f <(envsubst < integration-test.yml) up --exit-code-from demo-sidechain +``` + +This should show that the integration test fails, because we had an unhandled fork in the sidechain. Clean up the containers after each run with: +``` +docker compose -f <(envsubst < docker-compose.yml) -f <(envsubst < fork-inducer.yml) -f <(envsubst < demo-sidechain.yml) down +``` + +We need these different compose files to separate the services that we're using. E.g. we want the integration test and fork simulator to be optional. The same could be solved using `profiles` - but that requires a more up-to-date version of `docker compose`. + +## FAQ +### What do I have to do to stop everything properly? +With `Ctrl-C` you stop the containers and with `docker compose down` you clean up/remove the containers. Note that `docker compose down` will also remove any logs docker has saved, since it will remove all the container context. + +### What do I have to do if I make changes to the code? +You need to re-build the worker image, using `docker compose build`. + +### How can I change the log level? +You can change the environment variable `RUST_LOG=` in the `docker-compose.yml` for each worker individually. + +### The log from the node are quite a nuisance. Why are they all together. +You can suppress the log output for a container by setting the logging driver. This can be set to either `none` (completely disables all logs), or `local` (docker will record the logs, depending on your docker compose version, it will also log to `stdout`) in the `docker-compose.yml`: +``` +logging: + driver: local +``` +Mind the indent. Explanations for all the logging drivers in `docker compose` can be found [here](https://docs.docker.com/config/containers/logging/local/). diff --git a/bitacross-worker/docker/demo-direct-call.yml b/bitacross-worker/docker/demo-direct-call.yml new file mode 100644 index 0000000000..504b53fdea --- /dev/null +++ b/bitacross-worker/docker/demo-direct-call.yml @@ -0,0 +1,27 @@ +services: + demo-direct-call: + image: litentry/bitacross-cli:latest + devices: + - "${SGX_PROVISION:-/dev/null}:/dev/sgx/provision" + - "${SGX_ENCLAVE:-/dev/null}:/dev/sgx/enclave" + volumes: + - "${AESMD:-/dev/null}:/var/run/aesmd" + - "${SGX_QCNL:-/dev/null}:/etc/sgx_default_qcnl.conf" + build: + context: ${PWD}/.. + dockerfile: build.Dockerfile + target: deployed-client + depends_on: + litentry-node: + condition: service_healthy + bitacross-worker-1: + condition: service_healthy + networks: + - litentry-test-network + entrypoint: + "/usr/local/worker-cli/demo_direct_call.sh -p 9912 -u ws://litentry-node + -V wss://bitacross-worker-1 -P 2011 -C /usr/local/bin/bitacross-cli 2>&1" + restart: "no" +networks: + litentry-test-network: + driver: bridge \ No newline at end of file diff --git a/bitacross-worker/docker/demo-shielding-unshielding-multiworker.yml b/bitacross-worker/docker/demo-shielding-unshielding-multiworker.yml new file mode 100644 index 0000000000..8581311a41 --- /dev/null +++ b/bitacross-worker/docker/demo-shielding-unshielding-multiworker.yml @@ -0,0 +1,29 @@ +services: + demo-shielding-unshielding-multiworker: + image: litentry/bitacross-cli:latest + devices: + - "${SGX_PROVISION:-/dev/null}:/dev/sgx/provision" + - "${SGX_ENCLAVE:-/dev/null}:/dev/sgx/enclave" + volumes: + - "${AESMD:-/dev/null}:/var/run/aesmd" + - "${SGX_QCNL:-/dev/null}:/etc/sgx_default_qcnl.conf" + build: + context: ${PWD}/.. + dockerfile: build.Dockerfile + target: deployed-client + depends_on: + litentry-node: + condition: service_healthy + bitacross-worker-1: + condition: service_healthy + environment: + - RUST_LOG=warn,ws=warn,itc_rpc_client=debug,litentry_cli=debug + networks: + - litentry-test-network + entrypoint: + "/usr/local/worker-cli/demo_shielding_unshielding.sh -t first -p 9912 -u ws://litentry-node + -V wss://bitacross-worker-1 -P 2011 -C /usr/local/bin/bitacross-cli 2>&1" + restart: "no" +networks: + litentry-test-network: + driver: bridge \ No newline at end of file diff --git a/bitacross-worker/docker/demo-sidechain.yml b/bitacross-worker/docker/demo-sidechain.yml new file mode 100644 index 0000000000..449d882707 --- /dev/null +++ b/bitacross-worker/docker/demo-sidechain.yml @@ -0,0 +1,32 @@ +services: + demo-sidechain: + image: litentry/bitacross-cli:latest + devices: + - "${SGX_PROVISION:-/dev/null}:/dev/sgx/provision" + - "${SGX_ENCLAVE:-/dev/null}:/dev/sgx/enclave" + volumes: + - "${AESMD:-/dev/null}:/var/run/aesmd" + - "${SGX_QCNL:-/dev/null}:/etc/sgx_default_qcnl.conf" + + build: + context: ${PWD}/.. + dockerfile: build.Dockerfile + target: deployed-client + depends_on: + litentry-node: + condition: service_healthy + bitacross-worker-1: + condition: service_healthy + bitacross-worker-2: + condition: service_healthy + environment: + - RUST_LOG=warn,ws=warn,sp_io=warn,substrate_api_client=warn,jsonrpsee_ws_client=warn,jsonrpsee_ws_server=warn,enclave_runtime=warn,integritee_service=info,integritee_service::sidechain=debug,ita_stf=warn + networks: + - litentry-test-network + entrypoint: + "/usr/local/worker-cli/demo_sidechain.sh -p 9912 -A 2011 -B 2012 -u ws://litentry-node + -V wss://bitacross-worker-1 -W wss://bitacross-worker-2 -C /usr/local/bin/bitacross-cli 2>&1" + restart: "no" +networks: + litentry-test-network: + driver: bridge diff --git a/bitacross-worker/docker/demo-smart-contract.yml b/bitacross-worker/docker/demo-smart-contract.yml new file mode 100644 index 0000000000..c6541b6d09 --- /dev/null +++ b/bitacross-worker/docker/demo-smart-contract.yml @@ -0,0 +1,31 @@ +services: + demo-smart-contract: + image: bitacross-cli:${VERSION:-dev} + devices: + - "${SGX_PROVISION:-/dev/null}:/dev/sgx/provision" + - "${SGX_ENCLAVE:-/dev/null}:/dev/sgx/enclave" + volumes: + - "${AESMD:-/dev/null}:/var/run/aesmd" + - "${SGX_QCNL:-/dev/null}:/etc/sgx_default_qcnl.conf" + build: + context: ${PWD}/.. + dockerfile: build.Dockerfile + target: deployed-client + depends_on: + litentry-node-${VERSION}: + condition: service_healthy + bitacross-worker-1-${VERSION}: + condition: service_healthy + bitacross-worker-2-${VERSION}: + condition: service_healthy + environment: + - RUST_LOG=warn,ws=warn,itc_rpc_client=warn + networks: + - litentry-test-network + entrypoint: + "/usr/local/worker-cli/demo_smart_contract.sh -p 9912 -u ws://litentry-node + -V wss://bitacross-worker-1 -A 2011 -C /usr/local/bin/bitacross-cli 2>&1" + restart: "no" +networks: + litentry-test-network: + driver: bridge diff --git a/bitacross-worker/docker/demo-teeracle-generic.yml b/bitacross-worker/docker/demo-teeracle-generic.yml new file mode 100644 index 0000000000..4ff30dafdf --- /dev/null +++ b/bitacross-worker/docker/demo-teeracle-generic.yml @@ -0,0 +1,68 @@ +# Teeracle Demo Setup +# +# The demo is parameterized with the interval that the teeracle uses to query its sources. +# Set the `TEERACLE_INTERVAL_SECONDS` variable when invoking, e.g. `TEERACLE_INTERVAL_SECONDS=4 docker compose -f docker-compose.yml -f demo-teeracle-generic.yml up --exit-code-from demo-teeracle-generic` +# Set the `ADDITIONAL_RUNTIME_FLAGS` variable to for additional flags. +# To skip remote attestation: `export ADDITIONAL_RUNTIME_FLAG="--skip-ra"` +services: + integritee-teeracle-worker-${VERSION}: + image: integritee-worker:${VERSION:-dev} + hostname: integritee-teeracle-worker + devices: + - "${SGX_PROVISION:-/dev/null}:/dev/sgx/provision" + - "${SGX_ENCLAVE:-/dev/null}:/dev/sgx/enclave" + volumes: + - "${AESMD:-/dev/null}:/var/run/aesmd" + - "${SGX_QCNL:-/dev/null}:/etc/sgx_default_qcnl.conf" + build: + context: ${PWD}/.. + dockerfile: build.Dockerfile + target: deployed-worker + depends_on: + integritee-node-${VERSION}: + condition: service_healthy + environment: + - RUST_LOG=warn,ws=warn,sp_io=warn,substrate_api_client=warn,jsonrpsee_ws_client=warn,jsonrpsee_ws_server=warn,enclave_runtime=warn,integritee_service=info,integritee_service::teeracle=debug,ita_stf=warn,ita_oracle=debug + networks: + - integritee-test-network + healthcheck: + test: curl -s -f http://integritee-teeracle-worker:4645/is_initialized || exit 1 + interval: 10s + timeout: 10s + retries: 25 + command: + "--clean-reset --ws-external -M integritee-teeracle-worker -T wss://integritee-teeracle-worker + -u ws://integritee-node -U ws://integritee-teeracle-worker -P 2011 -w 2101 -p 9912 -h 4645 + run --dev ${ADDITIONAL_RUNTIME_FLAGS} --teeracle-interval ${TEERACLE_INTERVAL_SECONDS}s" + restart: always + demo-teeracle-generic: + image: bitacross-cli:${VERSION:-dev} + devices: + - "${SGX_PROVISION:-/dev/null}:/dev/sgx/provision" + - "${SGX_ENCLAVE:-/dev/null}:/dev/sgx/enclave" + volumes: + - "${AESMD:-/dev/null}:/var/run/aesmd" + - "${SGX_QCNL:-/dev/null}:/etc/sgx_default_qcnl.conf" + build: + context: ${PWD}/.. + dockerfile: build.Dockerfile + target: deployed-client + depends_on: + integritee-node-${VERSION}: + condition: service_healthy + integritee-teeracle-worker-${VERSION}: + condition: service_healthy + environment: + - RUST_LOG=warn,sp_io=warn,integritee_cli::exchange_oracle=debug + networks: + - integritee-test-network + entrypoint: + "/usr/local/worker-cli/demo_teeracle_generic.sh + -u ws://integritee-node -p 9912 + -V wss://integritee-teeracle-worker -P 2011 + -d 21 -i ${TEERACLE_INTERVAL_SECONDS} + -C /usr/local/bin/bitacross-cli 2>&1" + restart: "no" +networks: + integritee-test-network: + driver: bridge diff --git a/bitacross-worker/docker/demo-teeracle.yml b/bitacross-worker/docker/demo-teeracle.yml new file mode 100644 index 0000000000..d71b36df6b --- /dev/null +++ b/bitacross-worker/docker/demo-teeracle.yml @@ -0,0 +1,71 @@ +# Teeracle Demo Setup +# +# The demo is parameterized with the interval that the teeracle uses to query its sources. +# Set the `TEERACLE_INTERVAL_SECONDS` variable when invoking, e.g. `TEERACLE_INTERVAL_SECONDS=4 docker compose -f docker-compose.yml -f demo-teeracle.yml up --exit-code-from demo-teeracle` +# This setup requires an API key for CoinMarketCap +# Add the API key to the environment variable `COINMARKETCAP_KEY`, with `export COINMARKETCAP_KEY=` +# Set the `ADDITIONAL_RUNTIME_FLAGS` variable to for additional flags. +# To skip remote attestation: `export ADDITIONAL_RUNTIME_FLAG="--skip-ra"` +services: + integritee-teeracle-worker-${VERSION}: + image: integritee-worker:${VERSION:-dev} + hostname: integritee-teeracle-worker + devices: + - "${SGX_PROVISION:-/dev/null}:/dev/sgx/provision" + - "${SGX_ENCLAVE:-/dev/null}:/dev/sgx/enclave" + volumes: + - "${AESMD:-/dev/null}:/var/run/aesmd" + - "${SGX_QCNL:-/dev/null}:/etc/sgx_default_qcnl.conf" + build: + context: ${PWD}/.. + dockerfile: build.Dockerfile + target: deployed-worker + depends_on: + integritee-node-${VERSION}: + condition: service_healthy + environment: + - RUST_LOG=warn,ws=warn,sp_io=warn,substrate_api_client=warn,jsonrpsee_ws_client=warn,jsonrpsee_ws_server=warn,enclave_runtime=warn,integritee_service=info,integritee_service::teeracle=debug,ita_stf=warn,ita_exchange_oracle=debug + - COINMARKETCAP_KEY + networks: + - integritee-test-network + healthcheck: + test: curl -s -f http://integritee-teeracle-worker:4645/is_initialized || exit 1 + interval: 10s + timeout: 10s + retries: 25 + command: + "--clean-reset --ws-external -M integritee-teeracle-worker -T wss://integritee-teeracle-worker + -u ws://integritee-node -U ws://integritee-teeracle-worker -P 2011 -w 2101 -p 9912 -h 4645 + run --dev ${ADDITIONAL_RUNTIME_FLAGS} --teeracle-interval ${TEERACLE_INTERVAL_SECONDS}s" + restart: always + demo-teeracle: + image: bitacross-cli:${VERSION:-dev} + devices: + - "${SGX_PROVISION:-/dev/null}:/dev/sgx/provision" + - "${SGX_ENCLAVE:-/dev/null}:/dev/sgx/enclave" + volumes: + - "${AESMD:-/dev/null}:/var/run/aesmd" + - "${SGX_QCNL:-/dev/null}:/etc/sgx_default_qcnl.conf" + build: + context: ${PWD}/.. + dockerfile: build.Dockerfile + target: deployed-client + depends_on: + integritee-node-${VERSION}: + condition: service_healthy + integritee-teeracle-worker-${VERSION}: + condition: service_healthy + environment: + - RUST_LOG=warn,sp_io=warn,integritee_cli::exchange_oracle=debug + networks: + - integritee-test-network + entrypoint: + "/usr/local/worker-cli/demo_teeracle_whitelist.sh + -u ws://integritee-node -p 9912 + -V wss://integritee-teeracle-worker -P 2011 + -d 7 -i ${TEERACLE_INTERVAL_SECONDS} + -C /usr/local/bin/bitacross-cli 2>&1" + restart: "no" +networks: + integritee-test-network: + driver: bridge diff --git a/bitacross-worker/docker/docker-compose.yml b/bitacross-worker/docker/docker-compose.yml new file mode 100644 index 0000000000..fedb1e549f --- /dev/null +++ b/bitacross-worker/docker/docker-compose.yml @@ -0,0 +1,156 @@ +services: + relaychain-alice: + image: docker_relaychain-alice:latest + networks: + - litentry-test-network + ports: + - 9946:9944 + - 9936:9933 + - 30336:30333 + volumes: + - relaychain-alice:/data + build: + context: litentry + dockerfile: relaychain.Dockerfile + command: + - --base-path=/data + - --chain=/app/rococo-local.json + - --validator + - --ws-external + - --rpc-external + - --rpc-cors=all + - --name=alice + - --alice + - --rpc-methods=unsafe + - --execution=wasm + environment: + RUST_LOG: parachain::candidate-backing=trace,parachain::candidate-selection=trace,parachain::pvf=trace,parachain::collator-protocol=trace,parachain::provisioner=trace + ulimits: + &a1 + nofile: + soft: 65536 + hard: 65536 + relaychain-bob: + image: docker_relaychain-bob:latest + networks: + - litentry-test-network + ports: + - 9947:9944 + - 9937:9933 + - 30337:30333 + volumes: + - relaychain-bob:/data + build: + context: litentry + dockerfile: relaychain.Dockerfile + command: + - --base-path=/data + - --chain=/app/rococo-local.json + - --validator + - --ws-external + - --rpc-external + - --rpc-cors=all + - --name=bob + - --bob + - --rpc-methods=unsafe + - --execution=wasm + environment: + RUST_LOG: parachain::candidate-backing=trace,parachain::candidate-selection=trace,parachain::pvf=trace,parachain::collator-protocol=trace,parachain::provisioner=trace + ulimits: *a1 + litentry-node: + image: docker_litentry-node:latest + container_name: litentry-node + networks: + - litentry-test-network + ports: + # TODO: maybe not use 9912 as port + - 9944:9912 + - 9933:9933 + - 30333:30333 + volumes: + - parachain-2106-0:/data + build: + context: litentry + dockerfile: parachain-2106.Dockerfile + depends_on: ['relaychain-alice', 'relaychain-bob'] + healthcheck: + test: ["CMD", "nc", "-z", "litentry-node", "9912"] + interval: 30s + timeout: 10s + retries: 20 + command: + - --base-path=/data + - --chain=/app/rococo-dev-2106.json + - --ws-external + - --rpc-external + - --rpc-cors=all + - --name=parachain-2106-0 + - --ws-port=9912 + - --collator + - --rpc-methods=unsafe + - --force-authoring + - --execution=wasm + - --alice + - --node-key=e998e728d8bf5bff6670c5e2b20455f6de1742b7ca564057680c9781cf037dd1 + - --listen-addr=/ip4/0.0.0.0/tcp/30333 + - -- + - --chain=/app/rococo-local.json + - --execution=wasm + environment: + RUST_LOG: sc_basic_authorship=trace,cumulus-consensus=trace,cumulus-collator=trace,collator_protocol=trace,collation_generation=trace,aura=debug + ulimits: *a1 + bitacross-worker-1: + image: litentry/bitacross-worker:latest + container_name: bitacross-worker-1 + build: + context: ${PWD}/.. + dockerfile: build.Dockerfile + target: deployed-worker + depends_on: + litentry-node: + condition: service_healthy + devices: + - "${SGX_PROVISION:-/dev/null}:/dev/sgx/provision" + - "${SGX_ENCLAVE:-/dev/null}:/dev/sgx/enclave" + volumes: + - "${AESMD:-/dev/null}:/var/run/aesmd" + - "${SGX_QCNL:-/dev/null}:/etc/sgx_default_qcnl.conf" + environment: + - RUST_LOG=info,litentry_worker=debug,ws=warn,sp_io=error,substrate_api_client=warn,itc_parentchain_light_client=info,jsonrpsee_ws_client=warn,jsonrpsee_ws_server=warn,enclave_runtime=debug,ita_stf=debug,its_rpc_handler=warn,itc_rpc_client=warn,its_consensus_common=debug,its_state=warn,its_consensus_aura=warn,aura*=warn,its_consensus_slots=warn,itp_attestation_handler=debug,http_req=debug,lc_mock_server=warn,itc_rest_client=debug,lc_credentials=debug,lc_identity_verification=debug,lc_stf_task_receiver=debug,lc_stf_task_sender=debug,lc_data_providers=debug,itp_top_pool=debug,itc_parentchain_indirect_calls_executor=debug, + - TWITTER_OFFICIAL_URL=http://localhost:19527 + - TWITTER_LITENTRY_URL=http://localhost:19527 + - TWITTER_AUTH_TOKEN_V2= + - DISCORD_OFFICIAL_URL=http://localhost:19527 + - DISCORD_LITENTRY_URL=http://localhost:19527 + - DISCORD_AUTH_TOKEN= + - ACHAINABLE_URL=http://localhost:19527 + - ACHAINABLE_AUTH_KEY= + - CREDENTIAL_ENDPOINT=http://localhost:9933 + - ONEBLOCK_NOTION_KEY=ABCDEFGHIJKLMNOPQRSTUVWXYZ + - ONEBLOCK_NOTION_URL=https://abc.com + - SORA_QUIZ_MASTER_ID=SORA_QUIZ_MASTER_ID + - SORA_QUIZ_ATTENDEE_ID=SORA_QUIZ_ATTENDEE_ID + - NODEREAL_API_KEY=NODEREAL_API_KEY + - NODEREAL_API_URL=https://open-platform.nodereal.io/ + - CONTEST_LEGEND_DISCORD_ROLE_ID=CONTEST_LEGEND_DISCORD_ROLE_ID + - CONTEST_POPULARITY_DISCORD_ROLE_ID=CONTEST_POPULARITY_DISCORD_ROLE_ID + - CONTEST_PARTICIPANT_DISCORD_ROLE_ID=CONTEST_PARTICIPANT_DISCORD_ROLE_ID + networks: + - litentry-test-network + healthcheck: + test: curl -s -f http://bitacross-worker-1:4645/is_initialized || exit 1 + interval: 30s + timeout: 10s + retries: 20 + entrypoint: + "/usr/local/bin/bitacross-worker --clean-reset --ws-external -M bitacross-worker-1 -T wss://bitacross-worker-1 + -u ws://litentry-node -U ws://bitacross-worker-1 -P 2011 -w 2101 -p 9912 -h 4645 + run --dev --skip-ra" + restart: "no" +volumes: + ? relaychain-alice + ? relaychain-bob + ? parachain-2106-0 +networks: + litentry-test-network: + driver: bridge diff --git a/bitacross-worker/docker/entrypoint.sh b/bitacross-worker/docker/entrypoint.sh new file mode 100755 index 0000000000..cfbefaf9c4 --- /dev/null +++ b/bitacross-worker/docker/entrypoint.sh @@ -0,0 +1,19 @@ +#!/bin/bash +set -e + +# Check if the first argument is "mrenclave" +if [ "$1" = "mrenclave" ]; then + # If "mrenclave" is provided, execute the corresponding command + $SGX_ENCLAVE_SIGNER dump \ + -enclave /usr/local/bin/enclave.signed.so \ + -dumpfile df.out && \ + /usr/local/bin/extract_identity < df.out && rm df.out | grep -oP ':\s*\K[a-fA-F0-9]+' + +else + # If no specific command is provided, execute the default unnamed command + + # run aesmd in the background + /opt/intel/sgx-aesm-service/aesm/aesm_service + + exec /usr/local/bin/bitacross-worker "${@}" +fi diff --git a/bitacross-worker/docker/fork-inducer.yml b/bitacross-worker/docker/fork-inducer.yml new file mode 100644 index 0000000000..6326b92815 --- /dev/null +++ b/bitacross-worker/docker/fork-inducer.yml @@ -0,0 +1,43 @@ +services: + worker-ping: + image: worker-ping:${VERSION:-dev} + devices: + - "${SGX_PROVISION:-/dev/null}:/dev/sgx/provision" + - "${SGX_ENCLAVE:-/dev/null}:/dev/sgx/enclave" + volumes: + - "${AESMD:-/dev/null}:/var/run/aesmd" + - "${SGX_QCNL:-/dev/null}:/etc/sgx_default_qcnl.conf" + build: + context: . + dockerfile: ping.Dockerfile + depends_on: [ 'litentry-node-${VERSION}', 'bitacross-worker-1-${VERSION}', 'bitacross-worker-2-${VERSION}' ] + networks: + - litentry-test-network + entrypoint: "ping litentry-worker-2" + pumba-network-delay: + image: litentry-fork-producer:${VERSION:-dev} + devices: + - "${SGX_PROVISION:-/dev/null}:/dev/sgx/provision" + - "${SGX_ENCLAVE:-/dev/null}:/dev/sgx/enclave" + volumes: + - "${AESMD:-/dev/null}:/var/run/aesmd" + - "${SGX_QCNL:-/dev/null}:/etc/sgx_default_qcnl.conf" + build: + context: . + dockerfile: fork.Dockerfile + depends_on: + litentry-node-${VERSION}: + condition: service_healthy + litentry-worker-1-${VERSION}: + condition: service_healthy + litentry-worker-2-${VERSION}: + condition: service_healthy + networks: + - litentry-test-network + volumes: + - /var/run/docker.sock:/var/run/docker.sock + entrypoint: + "pumba --interval 3m netem --interface eth0 --duration 30s delay --time 1000 litentry-worker-2" +networks: + litentry-test-network: + driver: bridge \ No newline at end of file diff --git a/bitacross-worker/docker/fork.Dockerfile b/bitacross-worker/docker/fork.Dockerfile new file mode 100644 index 0000000000..3a2df5bb85 --- /dev/null +++ b/bitacross-worker/docker/fork.Dockerfile @@ -0,0 +1,26 @@ +# Copyright 2021 Integritee AG +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +### Build Pumba image with dockerize +################################################## +FROM scratch AS fork-simulator-deployed +LABEL maintainer="zoltan@integritee.network" + +COPY --from=gaiaadm/pumba /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt +COPY --from=gaiaadm/pumba /pumba /usr/local/bin/pumba +COPY --from=powerman/dockerize /usr/local/bin/dockerize /usr/local/bin/dockerize + +ENV PATH "$PATH:/usr/local/bin" + +ENTRYPOINT ["/usr/local/bin/dockerize"] \ No newline at end of file diff --git a/bitacross-worker/docker/lit-ii-batch-test.yml b/bitacross-worker/docker/lit-ii-batch-test.yml new file mode 100644 index 0000000000..b11860e3b1 --- /dev/null +++ b/bitacross-worker/docker/lit-ii-batch-test.yml @@ -0,0 +1,24 @@ +services: + lit-ii-batch-test: + image: litentry/bitacross-cli:latest + container_name: litentry-ii-batch-test + volumes: + - ../ts-tests:/ts-tests + - ../client-api:/client-api + - ../cli:/usr/local/worker-cli + build: + context: .. + dockerfile: build.Dockerfile + target: deployed-client + depends_on: + litentry-node: + condition: service_healthy + bitacross-worker-1: + condition: service_healthy + networks: + - litentry-test-network + entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_integration_test.sh test-ii-batch 2>&1' " + restart: "no" +networks: + litentry-test-network: + driver: bridge diff --git a/bitacross-worker/docker/lit-parentchain-nonce.yml b/bitacross-worker/docker/lit-parentchain-nonce.yml new file mode 100644 index 0000000000..44e967776d --- /dev/null +++ b/bitacross-worker/docker/lit-parentchain-nonce.yml @@ -0,0 +1,24 @@ +services: + lit-parentchain-nonce: + image: litentry/bitacross-cli:latest + container_name: litentry-parentchain-nonce + volumes: + - ../cli:/usr/local/worker-cli + build: + context: .. + dockerfile: build.Dockerfile + target: deployed-client + depends_on: + litentry-node: + condition: service_healthy + bitacross-worker-1: + condition: service_healthy + networks: + - litentry-test-network + entrypoint: + "/usr/local/worker-cli/lit_parentchain_nonce.sh -p 9912 -u ws://litentry-node + -V wss://bitacross-worker-1 -A 2011 -C /usr/local/bin/bitacross-cli 2>&1" + restart: "no" +networks: + litentry-test-network: + driver: bridge \ No newline at end of file diff --git a/bitacross-worker/docker/lit-set-heartbeat-timeout.yml b/bitacross-worker/docker/lit-set-heartbeat-timeout.yml new file mode 100644 index 0000000000..b4e271ce4c --- /dev/null +++ b/bitacross-worker/docker/lit-set-heartbeat-timeout.yml @@ -0,0 +1,24 @@ +services: + lit-set-heartbeat-timeout: + image: litentry/bitacross-cli:latest + container_name: litentry-set-heartbeat-timeout + volumes: + - ../cli:/usr/local/worker-cli + build: + context: .. + dockerfile: build.Dockerfile + target: deployed-client + depends_on: + litentry-node: + condition: service_healthy + bitacross-worker-1: + condition: service_healthy + networks: + - litentry-test-network + entrypoint: + "/usr/local/worker-cli/lit_set_heartbeat_timeout.sh -p 9912 -u ws://litentry-node + -V wss://bitacross-worker-1 -A 2011 -W wss://bitacross-worker-2 -B 2012 -C /usr/local/bin/bitacross-cli 2>&1" + restart: "no" +networks: + litentry-test-network: + driver: bridge \ No newline at end of file diff --git a/bitacross-worker/docker/litentry-parachain.build.yml b/bitacross-worker/docker/litentry-parachain.build.yml new file mode 100644 index 0000000000..9a1df4908b --- /dev/null +++ b/bitacross-worker/docker/litentry-parachain.build.yml @@ -0,0 +1,104 @@ +version: "3.7" +services: + relaychain-alice: + image: docker_relaychain-alice:latest + networks: + - litentry-test-network + ports: + - 9946:9944 + - 9936:9933 + - 30336:30333 + volumes: + - relaychain-alice:/data + build: + context: litentry + dockerfile: relaychain.Dockerfile + command: + - --base-path=/data + - --chain=/app/rococo-local.json + - --validator + - --ws-external + - --rpc-external + - --rpc-cors=all + - --name=alice + - --alice + - --rpc-methods=unsafe + - --execution=wasm + environment: + RUST_LOG: parachain::candidate-backing=trace,parachain::candidate-selection=trace,parachain::pvf=trace,parachain::collator-protocol=trace,parachain::provisioner=trace + ulimits: + &a1 + nofile: + soft: 65536 + hard: 65536 + relaychain-bob: + image: docker_relaychain-bob:latest + networks: + - litentry-test-network + ports: + - 9947:9944 + - 9937:9933 + - 30337:30333 + volumes: + - relaychain-bob:/data + build: + context: litentry + dockerfile: relaychain.Dockerfile + command: + - --base-path=/data + - --chain=/app/rococo-local.json + - --validator + - --ws-external + - --rpc-external + - --rpc-cors=all + - --name=bob + - --bob + - --rpc-methods=unsafe + - --execution=wasm + environment: + RUST_LOG: parachain::candidate-backing=trace,parachain::candidate-selection=trace,parachain::pvf=trace,parachain::collator-protocol=trace,parachain::provisioner=trace + ulimits: *a1 + litentry-node: + image: docker_litentry-node:latest + networks: + - litentry-test-network + ports: + # TODO: maybe not use 9912 as port + - 9944:9912 + - 9933:9933 + - 30333:30333 + volumes: + - parachain-2106-0:/data + build: + context: litentry + dockerfile: parachain-2106.Dockerfile + depends_on: ['relaychain-alice', 'relaychain-bob'] + command: + - --base-path=/data + - --chain=/app/rococo-dev-2106.json + - --ws-external + - --rpc-external + - --rpc-cors=all + - --name=parachain-2106-0 + - --ws-port=9912 + - --collator + - --rpc-methods=unsafe + - --force-authoring + - --execution=wasm + - --alice + - --node-key=e998e728d8bf5bff6670c5e2b20455f6de1742b7ca564057680c9781cf037dd1 + - --listen-addr=/ip4/0.0.0.0/tcp/30333 + - -- + - --chain=/app/rococo-local.json + - --execution=wasm + environment: + RUST_LOG: sc_basic_authorship=trace,cumulus-consensus=trace,cumulus-collator=trace,collator_protocol=trace,collation_generation=trace,aura=debug + ulimits: *a1 +volumes: + ? relaychain-alice + ? relaychain-bob + ? parachain-2106-0 +networks: + # to be aligned with other yml files => same network + litentry-test-network: + driver: bridge \ No newline at end of file diff --git a/bitacross-worker/docker/litentry/docker-compose.yml b/bitacross-worker/docker/litentry/docker-compose.yml new file mode 100644 index 0000000000..5d7059c16e --- /dev/null +++ b/bitacross-worker/docker/litentry/docker-compose.yml @@ -0,0 +1,87 @@ +version: "3.7" +services: + relaychain-alice: + ports: + - ${AliceWSPort:-9946}:9944 + - ${AliceRPCPort:-9936}:9933 + - ${AlicePort:-30336}:30333 + volumes: + - relaychain-alice:/data + build: + context: . + dockerfile: relaychain.Dockerfile + command: + - --base-path=/data + - --chain=/app/rococo-local.json + - --validator + - --rpc-external + - --rpc-cors=all + - --name=alice + - --alice + - --rpc-methods=unsafe + - --execution=wasm + environment: + RUST_LOG: parachain::candidate-backing=trace,parachain::candidate-selection=trace,parachain::pvf=trace,parachain::collator-protocol=trace,parachain::provisioner=trace + ulimits: &a1 + nofile: + soft: 65536 + hard: 65536 + relaychain-bob: + ports: + - ${BobWSPort:-9947}:9944 + - ${BobRPCPort:-9937}:9933 + - ${BobPort:-30337}:30333 + volumes: + - relaychain-bob:/data + build: + context: . + dockerfile: relaychain.Dockerfile + command: + - --base-path=/data + - --chain=/app/rococo-local.json + - --validator + - --rpc-external + - --rpc-cors=all + - --name=bob + - --bob + - --rpc-methods=unsafe + - --execution=wasm + environment: + RUST_LOG: parachain::candidate-backing=trace,parachain::candidate-selection=trace,parachain::pvf=trace,parachain::collator-protocol=trace,parachain::provisioner=trace + ulimits: *a1 + parachain-2106-0: + ports: + - ${CollatorWSPort:-9944}:9944 + - ${CollatorRPCPort:-9933}:9933 + - ${CollatorPort:-30333}:30333 + volumes: + - parachain-2106-0:/data + build: + context: . + dockerfile: parachain-2106.Dockerfile + command: + - --base-path=/data + - --chain=/app/rococo-dev-2106.json + - --ws-external + - --rpc-external + - --rpc-cors=all + - --name=parachain-2106-0 + - --collator + - --rpc-methods=unsafe + - --force-authoring + - --execution=wasm + - --state-pruning=archive + - --blocks-pruning=archive + - --alice + - --node-key=9e7aac1fe73c65be5c937fc95fbb9e24cd31f605696a6b4cbc34aff6a7b43968 + - --listen-addr=/ip4/0.0.0.0/tcp/30333 + - -- + - --chain=/app/rococo-local.json + - --execution=wasm + environment: + RUST_LOG: sc_basic_authorship=trace,cumulus-consensus=trace,cumulus-collator=trace,collator_protocol=trace,collation_generation=trace,aura=debug + ulimits: *a1 +volumes: + relaychain-alice: null + relaychain-bob: null + parachain-2106-0: null diff --git a/bitacross-worker/docker/litentry/parachain-2106.Dockerfile b/bitacross-worker/docker/litentry/parachain-2106.Dockerfile new file mode 100644 index 0000000000..eab9c2a4fe --- /dev/null +++ b/bitacross-worker/docker/litentry/parachain-2106.Dockerfile @@ -0,0 +1,2 @@ +FROM litentry/litentry-parachain:latest +COPY . /app \ No newline at end of file diff --git a/bitacross-worker/docker/litentry/relaychain.Dockerfile b/bitacross-worker/docker/litentry/relaychain.Dockerfile new file mode 100644 index 0000000000..704c6daf3a --- /dev/null +++ b/bitacross-worker/docker/litentry/relaychain.Dockerfile @@ -0,0 +1,2 @@ +FROM parity/polkadot:v0.9.42 +COPY . /app diff --git a/bitacross-worker/docker/litentry/rococo-dev-2106.json b/bitacross-worker/docker/litentry/rococo-dev-2106.json new file mode 100644 index 0000000000..3f7995675c --- /dev/null +++ b/bitacross-worker/docker/litentry/rococo-dev-2106.json @@ -0,0 +1,138 @@ +{ + "name": "Litentry-rococo-dev", + "id": "litentry-rococo-dev", + "chainType": "Development", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": "litentry-rococo", + "properties": { + "ss58Format": 131, + "tokenDecimals": 12, + "tokenSymbol": "LIT" + }, + "relayChain": "rococo-local", + "paraId": 2106, + "codeSubstitutes": {}, + "genesis": { + "runtime": { + "system": { + "code": "0x52bc537646db8e0528b52ffd00587c8a053e4507031753105809291df0e50a2dbd8dbab06e5e90557b3cc06cafa2aa2f596e8cf0e5d212e561567462b405fa9965fb7751da6eff21e53c0ff4a602b3945e925bbaba6e7aebe55a5855b97ec36e42f696644bb9a59449a6d91958153716de04fd551d87bf9a721cde97f4a612ee4b3a095eef0b7a09aebaafa6eada7d4157798edba4398edb04fd5582ab6ed36b73785f2a7f35e9dc1774eb39f7557dfa0b7ad30bf6d157fe92f7a5baaf1c6f6ac7f197ca5f38fe6a6faa7ee3bea45b2fe1366df7b5f9abc93acb9da2afee6be5d349b8afedbe726e530d9fde94b98dfb9af755e3bea443df1ce7be56aef9aba9bbafbeafe9d059f7d56eefabfaeabe72bac56e9dbbafaaf96bbb2f7b75bac55e5d759ba86bf735bdded7d62d767a5fd97dcd5bbbc52e6f775f7de5e581ed406902fb245f3abb7c92b5875ed43a7b279ffaabc7fb92fed45b0fbd54fe249fe5ec39d953ff2af3a77ecdd9752cbd2fea15720e9d6e6e6575eaf1be9ee04f97ceced343afe8acdbc46f9c20810f2b8e5bd0fb32b7da55f7a5f2cd5ea9ba52bb4d1104538481c4bf2abd955bd0b35bb9d51eef2bfabc4d93882c88c8e25f53e0cbbb710b7abc1bb7dad9611c8c42588628e8582aa6e6f9cd7f3a9fb965453468ce9c2143464aaa5bcc6576811042870e85c8b22cf3ccb324babbbdbd9d3d8a79c5e853fae37d4d911f3d7a74f6e9452e5bb658b1126331f6e68d1b376bd6a8518306cd993364c8484989119344124208d1a50b172e5bb658b1d2b18ebd79e3c6cd9a356ad4a04173e60c1932525262c4249184104274e9c285cb962d56acc0188cd960c3d39313274b96c000c30b2f18c0004444458a78514cb7d8db8b49748b3df3a210dd62875eecd22df6e88413451401064cfc12bf0c19e2e3e3dedec752df2ebd8fc9bcf4e809e9b28fce6f827493ae79c8538c51dd7c9c268d87ce999a876ed9f206bde899071f42785f10424e83e68c9929ca64fdd97d656a9cb812fb57e653e46797bb95494e097bf1d9912243827e5aeff3d0797874747272ba6edb6a95727e53e4120ba3bfaf8e135c60fb12ce37f19b180962ff6a9f22bf2f77abbdefe70feff7d49f39bbcd3ce8d1fb32872eb9a2ec2261360fddc22d7a934bc2627ce69343b2e39be0671e338f1f7af5993b8ab16f276a21ddea3f1b3e3ac758ec7ed279faac5eefc767a8d3fb3199a117ecb95b99671ea4cbfca167f763a95699a087feb19987ce4d84dbc757e4a1cffb319a87f76b67278a2cf5d1d3f9fb750ffdf3670feaa043e73ba48c2eaa797ef6f6f8611496a0c6f2c4d9ba25bb057d3e4bc562cfdd82ce382cfb7cfed12d13a0b33c808e1d3a7345ec9033b221168bddf07cb95b5ffde8b63766678ebb159da8a127bb15fda7fbba8fd1ad4e0ff147b770eb7a88bbf5c9678f9ed343d0a3ebf410fce8cc15496fca8e7fc1eab7d7efbb93a45d7abbbc4ff0e1e56eb13387047adfaf1d3a511335fbc4abe4cf3c600da119cb3d049fddc28d7ba8a1b35b16f7103f3bf710f4af3ebbeca1e8ec167245d16df42cdca2d7ce1c74a2b6bd31e3bcb004986ef14fcf31e48c4efaf4e8912b8a9721ce0b4b24d12d7ee8d3e36e45b7bd458f21ce0b4b24d12d7ee8966edc43fc5f7d762bb7e8b5430e7a73f2ca6e458f5c51ed5674e656e0874ed4444dd4444dd4444dd4cc15b5133551c31ac2205806f240079d9d5543a8e8326fae500d911ac266081743901892c490160c514228cd100d0885211404a13384c0104243c80821371072239403211f084123a402a130423410f2426805425308b14048034237082121d4859014426820f401a11d843c215445088b900f42a6848c11da80d0152169848e10ea41080842b8103243e88d902e84e040480e84b2086911ca4188072155089942c81921658458218483504cc88a1033423a08b942e807212f10020321461042042133426d84d0086d31840721690895112246e807425c203486102d84643124cb900c0871420813428e20d401a11e085942480b214a0cc1228403a11a0851215486101941b2a03fa06604a92288144164106406419a08e2441016040113244b901d828420080882f010844b900d04d1d2b12023040121c84dd00f82741094260807413708e222c88a20334136081a1384445016203800b206c817406800640740ce00d102081840c2004206102980a001b205102e806401e40a2066805801a40a2035005206c8143f64001903c80c7e7c0084073f39009206089b1f2a80b400f900d403100f4059807600d200500680b000e90074052807a02480900002011017202b4055807000ba0128f633f573841f230001013402a013009900a8cb4f1a3f45f821c2cf107edefc08e1270840470019015404100b8056001406080c900a808800fac04f9b1f337e80f0f3839f327ed6fc7cf103c64f183f63fc40f123f55344c4e247cc4f077eb2fcfcf0e3819f2d3f4bfcecf0e3c34f0f3f5a7e78f831f523881f46f891c60f22fc78f3e3083f86f0a3083f47fc68e007891f237e5af093c48f123fccfc50e2470a7e88f9c1c48f257e9ce0870a7e0cf1e3cb0f227e78f921043c030a017af1c3871fb11f39fcb8e1042f4ee8c109629cd0c40963823011c40527c4e0043227441144cc09506c6ab82e72248143033d46f081c40d1ce0d8e107063d45e0c10437031c3aacdaf4686295825ba60b73a38b550e5b9a1e3282fc90e3881d53ac2adb0e70f4d0730637c59603136a90a30537b8f010dc507343073e8e983cacd8683dc831020e8bee051d0c723c714308378260c3f4402268054161585598400416153d623bacd85185094530210d138c10c4811b6104e940100e006008004813c403386eb08a81093ed8a10208041e39645b340b7a055b17ab3358587494d8bcc8c004d9d2034bcb052730e1679c200647143c6778b2c031c6cd7021ac8e30a18c1512269c8143ca87c02201ab4b0f12f4c8400f1f7af4d063043d3ad003053d84e811448f12f430418f13f4e8d263033d7ee891a507084c60a2c7077a68a007971e40f4e0408f10f4d062821326b8c084264c10d383871e5b7aecd0c3033d44c0e3081e48f030c2d358156135d56c5a8c0ea3c1e835fd45efa0bd6835ad83ce4177d1691a077d83e6a2d1f416ad4567d167da068dc5b56235c68f0e80f4c03be06387a00eec30b3830a138c30a1881d48f0088387183cc0e0c186c71a1e3de0c1831d36d8716607163bb2d871c50e2e76a0d9b1821d4cec10b3c3053ba2d891047cb3638821620861b3e30b8f2d38d0b8117678f1aa04e5a0670d4d226eb971c6a522c70a70e0e04160656015c60932e0c6f0b0e11123c70c72c8a0470b4f173c697870c073031e2ebc1d3c0df07821a4062c2b78bef8398367075c0db2333c28d0daf084e0060f70f0b042e3870b1c3900f162670c1c59785e6002105853e090c2841ff850e29ed94ee01b3041091d5a5c29b62e5d11565978b6e840b1238cbfd90186070b13c630a18d09649880060e19b0622b216420f8916225062b87150f56445805619506ab079606583eb0acacdeb0b4b076581d21084d1018d28b24421a218f606501d266070b74ba98e066f5831b39b8916645c68d1bac8cb071f1d303ef071e207496e081c1f6c58d3539c2ac8670d9781ff044307fc8b4e89912f2e6061a377c20648b1e1e0871316dc0999924e82e2c1d84d8e0061baf032b205c1e4c20fc071e2e38aaf40441c89a36c1ca8c959b480321489cd0438f226e7c214402aa84c7656ec1f1c48d363d6c846e2034041e1004f98007026f0beb8a10334ee82294c50d2f76b4e1aa10c2e246193958b0d3c6a9886184b6482e4376105204ce0a213bb801841b3db841067745c5a2a70cd772030cd9831702cf03377670c38d10318488e8107840cc11e07881902386f4e055080143c80a1c2f3829c0a1020705385ffc08f4890e0c79841e1f6459e0c880176107193dc0f48091034ce744f784c7014e2a870a56604417dc40b3e3073d58d083881e2ad851464f1837b4b8b1458f157051781bb891458e2f3dc258303d86c8314464c2aa20071133cb8d33ee86070d0e28582570344c3063e5825512ab25564aacaa5831b16ac14accea898c8b6c082133103a83c30a10317a1c914309160f37cc085a230408b2063f5ef48851af8835e0a09858e0e0c289c063031e2d78b6e04cb0a3080e055c0ab82e267cc149d13306a781d8044703d60d9c0c566b563e302108ab1e641a30414d86e5c61859067ea6c011ebb10407031c583832eec3aa0caecc7645cf0f565fdc10a387113d3da84bf848011703160e386e10cac08d1ffcd4408a00c7951e3fe8d1c66e61b3b057582b2c1178b4408680a3e287063f6a2a9afa82da447da24ad52c2a0c2a14754c25137fa851686c38363a70d091438f1b6e07ee072e0b4f06722cc1a3816e0ae70a4eecaae0b6e082e921828e2a3a6ee811420f10703280e3011c0d9cb0c3092038210bce07704480c3c5843326e4c0842cfc06c7e238f0149103091e237cfce063081f1ec8b1450e3139d07860bc25bc305707578caba6e7083a623d43e888e88ae854b04d713870317fc2a5f017ec58916dd9317365d06346cf9b1e34b821acac7044e021058f333c68b0a32647113b5ee8a4d109634b621ba383031d3174bad85ab041b129d1d34687959e34b62d5b09b61f7070b92eb831b84d74657444e87eb06960e3c0b6436745a7456766e708398cd899e27a9009c1f940a78c0e1a1d297848a023840e181d14fc9ca94ad416d02a6a1195057408d5088a05bd829aa91fa823a821a8602810ea11940c1a05254357404b40b7d01fa807680ef4065a25eb41e6838c8d1037999490263225b228322832277ef490a100880fb21db22c01a05682da4c23cc3333077307f38c19c624638231dbcc32a617d38cc9c5cc62f6606a317d30d1983f9862cc34738ca966ba9937986b269a0984c96676317930bf985b4c1c4c1dfce0616a6066405af133468221bb9069e41a69464621a19063a40ca4147206b28c9c42d2402e2195902d904900b1810c420e21bf4821e40a2412328ce4800481f4416e204323b5c80ec82c92879826f2209a8938883b88398863441dc42ea218118cc8266611bd8041885f4435710b084484412c13c704551163106510a5e20c6810918a38c50f0dfcc012c54424e21234055189f64147114dd02f8827a04dc41da810314bf3207a80be205a895522968843cca161009588b196010a439a06310411043d03b84497a12da028e8277acc8f2c415d349826a281e81334085a043f32d041fcd8a151d0256821e011601a9008d008300b7806da0016016201af80328053b00530086806440282018f80646014f005d009d8043c012c01ec0245004300498044c392d237c39318a030253969224305982ca180042600c3806e607040035660c00b0b50800b0868818524478d031c821122290cc127341f58f80c6d24a1c5188549d20c4848b6a313a6242424158f22184a0c4c4f909a2041898109c9d21294023c35495a5a8252801f3c5830c3930c4c494b2be0a4a41890a03c213dcd20c393a41a7cf008c3afa5274c9e929c202525c5d08307188612839426a42751fe892989060b3071c179a880098909d293289fd484090a029e98a44069d2e47b7810c1afa4274f4f9ca0ccf02485890d1e43301394274f4c9ca438417a82b2f4427a12e59938497912787cf17878e197059294905e48494ea034497282f4b240521226284f5eafa405ece09102aee14994774a527282e200251e0f143052929424290978c149920c323079e1070f21f8a5d42469862641f0eb8929c9296906a627cee3044c4f62d8e161026ef2e4c5f4c4e9c98624240b2c4589c204c929c9c9d213292ea52650965c3c3cba300d484c9e983861f294d4020e8f125ce0c91293274f4f989e90c08304529a24d9905483c708d8094a01b61e2538e9898993274b4ca6acaa0af8a5d404ca52058408393d35f12a119c5387600b2429213125213d25bd906a98010a13d22b89095214a3231e185d408ac2a4891021264f90a2181d7115085e7ac22406a427a7a4d78b0912125745c0484c4c9e38614a424a5a7a7a7ab2b4a37a80a902b66ee125293470809d386152a36e604811a10b3cbd9e3091507dc8a93db00c494c3c6461214816a800d294274e6ad41db802525e4834242d25210d29221485ab5133c04e9c307945313a2aa16261a7195e48b5eac02485a75ee1a7274b515e4828d400c509d3931a72d41c58881092902028203d35495a4ab251ad2ccd50a502525ef5064e6ac2040509a7c672e814333199e109e9f54a5a62d1233053d294242370056878b284d4d13478898994a6244b8bc0509c922e8094e4b4a244e028494e484e4d2f9dfa03273561b2140589e90992059c92688022c31313141a982ab043879043df70141aa2181da9a81038089cf4c4c449142936281afcf484e909120a4142412cb00425ca193c64480a921392132834204d617a82244312131253052a0eec040a0d484e9c30415a80ab05242c70740c76e284c992141a909abc7cc0af1ab4074932f4a03c60284f6c588624261c2a063f3d3d212d49a181041a06274d79f2c4448624a6d713a4a418742818fce4943485e90952520c6b929e984c79e21405a9c993a50a48890244bf6027494a2f263420357982e4c464890900e80e38a9891427494e484d5e39d48ba725293438315962d2e4d5436fc049343c7972623205e995f464810ad000c52906241b90a2181deda0673889e9490c3130412281da8093962a20058a131c8a053b71c2e40aa7a697197e62824203d292141a386a0527bd985400c906ad8297a4d080f4c4f4044a14a421148444595119f053d28bc6802d10a5090d493420b1b0800520a0c805252b86494b1560f2c4242506a4a726494e4f9c303d4142c2d1009c2fb4cd9a3362ce3cc32c4bc5620f61d2cbb431b4316014482591a9634e223a74e89873ced994c62863730d4a29a5109031e2e0a64cbb7b366ddadd92768c9199635366ca94366b507236a68de6a1b93d802bd318e594d15aca54464b63431882581bad0deed84d993b4a9e1542c896ca8e55b28d4969ad95994acaea4869c75aa5acb5326dca916354d5aa499d5a2d4bcaa6dd4cbb086577263bd362a459edccc618553636654a99e31065664d8b51cb24a47452382765967252ca5b6f316a5a6cad5bd3ba1b328490ce0ae190e8799e37b5a8c509e1a4904e4a994e66d95d29953d2c6b8b29a5519395630fc7d833d29e1da5569935baeaa831cbb6a17100689cee6e71b703203733502b76039a3245817d74773ebae3d87577dceca37d745d77ef745dec60d7cad311a8290fa5b1070f4f07b4d3a3e389103684b01b521825845c9b6bb5927543cada32ceee1aa54ed348bbb94ac9b556ad366dda911976377338b45b3d3dabd8dddcb065c4e19899656400939031c3ac9933fe819d71c63f9c317396c1ceb8994b602e812309cc2434f30f84902384cdcdb035be415f60a6515b00670ae8c1f194b56353cadd3bb03bd688353a46d9355068c831b7ec986394b1ca39a5bcf75e1d9dd8daa431d6e88e71520a99524a690d1376708db19b736acf6e21b2b96db7ec1d5c7747ae4da916234338594ed9554a9ecc3ce34a93526a1a73ad52b25892c5d2b4186bd5345665455963a59436e3e8285946ca94b904c41a2c9bd2489bba4029c71f38384a7afc8831c61fcc9123c7dd10c6a8c5dab2e39c31d65a2b646ed8cd08e8d8cc8a3a5c0bb1b204c2196dce5431daa051b3d1dd556ba14a6976a39987c4e698258ee6703ae270370e8e85c825e138aecac8314a5637eb00d132737353243272c76e4a01402900b8352963cb4969ec1825a5ddcd9167434a3b76e45a2184915ba19bbb699750239552ceee49257394514a66ae3bba46c7aed1354cd8b1234a137698c0b58c12f24ecb8e313237c481a3d61a3520acb5d6187b87ab1c73c71aa34ad513d927468a838383c3e1c458bbb626355942e4c83146cdc691acb2e2a810767794ddb19b76536ee60819d2d92a66ee48bb3bda8835cec81ce39c2d6365293b56aae5c8183b464be9b4b2d638279d93c6383b76741a73727258b26c8e30b68c578bb1e39c94bb5946664a99d2d8b23bca8e9152cab5d62aa38cb2bbe7a494323777addcddb165b36ca6cc2c5b870ec93176942d5bcaee6696b15bc696dc31f64823ad638c453724ede689b152aeb2a38c31468e4c65e4ca4d999969339592327777777377f4ba3b364b2a9b29a5dc9472c7964d69edd832ca8edd926bad4c210500cf9e323253eeeeee5a9956ad8532eeee1b2ca78c94ca49a5843046dadcb24a96ad52d3a95996c558bb69949aec8edc916f307364dacc94d2c9dc33c6ee8e513237addcb125773333c7d82da90128a534a7368570d202b83e2787a8ce6983523a2915a24290201a14a70d392ba591d54d23a5274c4a5904a851c61aa99c3152a6cd9429a534fa34776c0a296d8f314629638d14faf8b48c9aac7293348b526a324ad99169ecce5a4e1a9b328431726c1a650fa1628815b1046e0b3841a12189862c694ad212920d4c68d8b1c3416480b2842403d313196480b284436869e98952d0007a1c69a5d0e2f1c3a30790138c0495e064e9c90c4d909e96a4308921c9890fb721c909d31324a6274c666812430c502ea481394892530c484e509e983481b2b4c48447004e929c929c3841411a424148d213a7a7137a94f0c271020b8102c5298752132615585a82e274421126494e393c4909e9c98624261dfe9424038e1cafa7d7131b9278b2a7264c5e4c4c9c9e909e9ed0400394160f16f2248624a7289749d252939d123808535294284c90926460e204e5e9c53364a8c70628353ce95062d2a44992d2100a423c87902048429294982c55004906a62733bc98cc80f46443924f614a8ace416878f2c424c5e91689e1494a0f093624cd80f4c4f4c4290a5293d7131a78b22727264faf1e42786c1c24e98989531253920c4e007092f47a253945a1014a13222b16e29434054a93242724262f274d929082280da120244a93a4d78f6a86264f4c2a8034c3eb09d393185a53989262003808121394272fa7a429481688e18953d214240104410105214e9290a03c45b1805313a4271b92784c614a8a3f2b203a4645492a6b9484d12c4a15151515b925300883aca8a8a8a8d84ba2202c2a2aca251c1545e3128eb251112a2a0a16392a0a962d898a6a5854146c4645c1a828986a09c3a2a260148c8a8a8a8a82c5b80406a320ac2e6198b684a3a27a0947410a8b8a82c1e6128e82f51286c1e892a8a8a82818e4250ccb6073090cc260bd8479090cc2607209c360b0b88461b05e028330b88461704904a6112033431a5e4c3833d3bc882434b7bdcde99a5bb6bd5587dec741d7bc5a449a38db1928c2e68aa8470e49116aa250fa72ca5b19c88a16fb190bb47ce6d4212b51cf1c7247ed1672d077a04baf6ea165ce881948e54872c060c05fbbe6d0fbda25e4e0356aa7d4a56b1e996fb74ddd887ae6cc25a19e395d419bdfda930c145d7a734ae895624199ef9b414ec9f6f3560692976385f108cb6c24aa81dc11a301466f516e91336237d38b97ce9c11a3f1f236c1291ec897dc119bb1a67f72470c46cce46af5c1b850ee88cbe0ae154aae35561f2c044c231056b291b80233df1ee343679754a2ccdec232ac6459571fac876f8253fd913b622fb8231683be95dbe48ed88cb965dc117f613fe38e188c8c33e220a89e7247fc03ba55ee88dd685fb923fee29be094c61d719bfa1a77c4604c954aa592d9ea8369516d963be21d1c3118bfd94d15571f6ccb4b6ffec61d311a2f5de38cd88b97d77291e33839358e638ee32097715cdb70f2e839f9370341b72cc94664125843d9011bdd6ad3a985aed43e61cfe9d12c83aa2c7315f4bef8ed2acfa94985a7ece1e704b30c76069b3a734775b6a7dd6f7aa59ed2f4b6b7461fd1897af36d7a4cc33b48b46fd7be9d3b804fd9e974b5566f39a534a848509665cf5c5146147d445f21fa88319b733e73459b737701ecda6e11ff6752ca186377bf949c911295f4e24b331a1561d28499228c1360c8d02a0519f8f5c3d80ea1337700ff209a602d6aea23567304146133734d32162bc13fd52437bede157c2e7cf5e951b7d2fba82b4dcfe44f9756d64a6ba573523abd7a4eb456e97456e9d4fb2ef34ffb9c1e7409a3b03f1095a277676e2357d4d456aa893de58c80624f9be00fe18ccfb8ea969b46b8b3fabeaf774a7d1a392430f6d42d4321abaf86a7d5edf4beea4a9947f9994fef93464c26167b7a9d9ab84d2cf64f357eba53930acf0ca4393d6d3f5d73e6944479fac5cf5cf39c9a54f866a0cc9fb2affef5674e3da7ea4fd94f672e09d25327eae935ed6852c1c28d7a4d70ea83f432ff64d389a2cfafbd3d5af125cdff7896fac282b70cc5742ce585cc03e0592a054fbc65b1a6a3f28fc73f47e5e564d42757d4753353a93caa793c3272453a3a1fbd464a33afce2cf3364e0965428b2fb19fde5c510d1dbff556b7a2b717c4bd60432c168bc56e704141c56245a28fe8904bc70f46093aff17f49947f7e921aedd6a8f9e79d093d736e48e2b796b4f2a2cf42e504c1381265a9162a60ad8380483e5fbdb79a0c79eecd6106dbef52cf5658cb74cb3296343ba6206629730b09f947ee89f94fe25db826757829786cffbd897a2966f8fde520d1ab0e2fb7ef099a296576a87cf1c02582a26858345f8291da07e8d314a2865c757ffa87f137a8cf27ef0a57b4a9a92eac6cc6732462dba8c9967d56ba597068fdc6b85e89ff46fa9c9787977a07f4a4b514b539cfa15f8e3f5d27d613388980baa3d4cd301f8a32f805ded449dd110b5bcbc52b80cc613c8283df8b11d426861df6f296af9af5dca8e8f1c20f351cb4324fd83e9d2e3b7d353ca7c3ae492543756bc0c81968f1b78d8dbdea6f73519afa4e5e1fd600f9564d7035d94969a8c8797a9c978e9f2eeacc05f45c3888f5769a9a261c4f7a5a1a261c437191336082c942dbda3bc4ed29f6419aff44d892f6fbb644d4dc1fa86879edd0ff60f3e5ea5cca343ae85ec2a4d892fbdcbf87895daef0e92f8ed7d9be84b8ff76b82531f7d89bed3906f823f45de7ae589ba6111c621a6e7f4c43d74a02dfe8bf27d9d98813e96ea8096df5981bfa8e59b6b61c7d4d47f5d2cf650a29687b1852ee31b03967d3ed8e533b75f8fa4cffb6d3fdd4ed830d39c3dab9b64b7b633cea8bec61b8ee62b876edbfba2d333fcd5a3b783447e75f9d5a943ee00d63ff8540b988fae795763933da43903a125cb574fe3a3bbf9eab6046b636befd33c25cda35b4df376abda2067a479f4a80d0942a779bbecd6746d7a74bb6dac8db3b15536c2cfaee33ad26e31c6ee868a52b956ae6aca19c1afbef2da2dce56d94854fdfd5cf81c9b6c2261a2afb8a2179595b38b01ec5ab96d6f73e6a2a714bdbacd2161831ce7cc25b14ed4f12d092cb7acce57176ae5dd4ad5dd1a7d64de451f997ff53367a99a72d7287a75a2087fe5b16bf3beead4edb4389b92fecab0e3ea63c0f76506a25dbd3fddca5add6afd78bbda34affacaa35c51dfa68e3a7ced0eb94141273cf5606fe1563d1f4f3ded6d6fd56bbd8d5bf57abc955bf54c783bb7ea01e06db655cfe7a917dfd2ad7a3c9e56ef3ef5e05b6dab9ef756b555cfdfdaad7a3bde6e5bf574bce5b6eaf5bc5d6dd5e379cbdaaab7f3b6dbaa97e36d8fadb30c08e8841f55b42ded6906f83100111706ca21003f82fc7803d1f2a339742ba4813823cd6bd52ee4c240991335bcd245ef9f2d7a9ab7f7693ccd3f6da33cf55075e856a8ab3f45b1277047f0ed8f0d72464c86578fdf0a013510ec561fac04efe49ae7e40f190868a30eddfe048033a20eb7548f9ea685fe047145f51639d2adcca9742aa7fe15f92ccc18af7a6d8c8f0e6dd0d69e0bde0bddca3ccaccd090d0102c1dbf046040a3c167c8058806a25bac81ce49b7325fd2ad4c0d9121518aa0b0b5a7b9e654d908dcc240291411400a47a2441f99db30002b52dc267e8c470460046e30acdc1e1940c358f49139c4127d644ed9b860ecb3cfb28f3e808d9b889530d5afb82b4c3462646bafc22d3d94c2d65e75c81db5c32dddd2e265e596005bb52a6c958d68db0b5df4a016eda3f7c7225bf4e0966e65de1ee4d2adcc2357a4412c1dfcea9a0036cdfbe06b6e63751f5a75a2662268ce5c51bd4bddca66e856e66c3e73cbb023cb95e656001b97001d5470ad2e51f491f90ad147e643d0e838faf8586a05b17f8aafb90e2ab856beba904bf491654e3925aad7ae92ea3a69f0033f3090ccab0b802ba2176ae956e69a07b73050e6f0864e7bf8d53ff8ed1fd4f299dbea51583dfe21ee280096c896c256d94816e5cc669665144208adddb8152b67d504a77e27497ccd6dfc07f5dab500b89eefd0d1c3b3930387ce8d1248c8b1e15383070072569081a66b1e8eaf9ece53afdff668c8357acc1533d0f4c89c91f4e9d955825d6a5f1f2cfbc0296d94ac448a0b6ef8ef025dc46606648c9ac3d7fca3df6ee9f472b668a19c51061d6ac99a1fe2d0494e23b9a3003c3350743bb7f6d862692c59c36e9bd39d79393ad187ec49b9a2e9d1db9939fd75532787040622bdbd1b0c24bb6e498f40c7523e4cf19a4fffeacbf890336237efe49feadbb53166a05a7dcd63e59fd698422d46a2cf97543e3d3a750b37cd539a1e9de7dde98f4e5b68cde3cf18a83d9b31bacaa3b792fe783fd8ab8cd8f22a87af729efcd34ed4cd21e957f9f4e44fee08e82503b5ca893ad619e3d5aa47a95c50d4a3542eabf294541e5daaee8efce8ad7a2dd6fbb9f0cd40edf47e2e3c6420ad9943223f3afbc82808a3bdf2e2ccfc3287b105b98304fa0692789e67292d4ebc7c2b37e68eb2fbc1cf5cbacdbe3a67deaeaa6ee906399a51f662dddacd55174ae59b4adb2e471fd36bf43155aef2e956abf7cb1c4a8e5ea34ce5b14b6e2a8f5dd2e755ea66c7272cfb7cdce6a55bc946e612066a98a5eb3bef9248d4304bd76a3e080497b9493602a343e7b2d4ad251fef0c70872e3acfe9f14ace28cb820562812c0118b68a66c2580f65d5ce2d7247197499795eac4fcf89bd33441fb273baf6e8195714bdbd9d0875e88c3c4fb7a4eb38517faaeff4d4fdf4ccdb3f9d970eb9a3fa4fba19a20fe9d069e6253110e9d39381819abafefee86ea3a679bc3e9a53fffca5db4e765eac75eb41b1ccb8acefbc20c4058500e65e10e2b2d6835a8de1b26e9b61afbea857d2dc52afbad53c7ecbdc11eca3dbb8699ed2b4d9063995339724fae49074e4879c97cdede68579c17f2d80737ba1ac73be0076d93b9d3905e8a0828bbbade8437a50f4219da887a0d1b14be5d241059775eb5feba56f1ebb984ba2ddcf852f7ae69cea1a45ebb14bf3f8ab57a45b527e8cdef748f4e95f919744ed396120c91d65ceddeaccfbe077d1acd24527da69971f3f5e03c0baf68fa55e529f818d44a7d02174ea79b11e3df867be3d83cc3f44bd0132bf651efcf63ed8370c94324fd2df9e793b48fadbfbdbad0715a57259875c122d5ec83fb3bd2a70ca152673e694a8b2fbb9f0cc40f3aed02de9d18bffccf565a15b52b291cc8da65396f2c189a76ee306bbb091e9d02177449f426fef834f6df420c70275e690743cfa7966207a9714f575d22de9449d7971de191848ba952ede17ba25bdaf0bdd92feb19a970eeba2b7746ef3cc232ac268b3a62e140c09ecc246da9be0d4b74723faf1c22e0c84a50a4fbd715d892ee83d265684162bb12b2ee620401b4ac0d6e74804bad86b24023d01d06bf4a100e8e2e8839da819688b962d5ab260c98205c660ec05abb4b4d928edb63923e947f0a3c0971ed3f6f14608b7b0db90860d5136eb8282c1c03ec31833ac79f6256b96a4797618d2c070e6d95d38e3c214cfbec2142b2880c93c3b1191028e3c7b912345863c7bd090a09f676ffdb47c9edd7d5c87470702cc2d80db6d7bf53bd68e11fcfa5d7b5ea033d700d6d4d4d4940baa42276a081d60b9873e28fd06df0edd42ee0030183ce41fc81d2559c14e679f8fcd783da081366229a2e5db7d7e8acc1bb348911efa7456e8a1af3b42d40310df6e001674a24f13f1582c1673b5ff3411198bc562585ced43d848fc58df253cebe05bf29333e2343fdd665be48abec88d2631b72a956d5a0f535899c139b9e79c91c76a3a8b8d402861125dd4f1347e7af05b3dc4523cbcf9f69f1e6236f3cc0336612603cb90888ea57860f3f3598a072dcf35dc40e9442d6fbd510a2bc20774cc1d00f652de7a194203882900929b353132b737c702ec95f836453dfbd7cedd1359dc3c4b6539f35f7de93f9ea5b27c79990d71c39eec16732cc05e66b0cf6e6d6fc819d3ade48a66cc7c27bb46f033cf6e7de83ef22a753fd804283584de103710b291e507748cf3c21260229433f31eda316a8f1e95a24bcf32af3dd49f49597e40675912deea96a5a48bdede14f5922b60b959b5875cc228a3042cf750b36779009dfced2d8b7ba8a56b79e839c16f6f8f9e6c96ca02c4b71739e944cd523c5879081f7a7345707be68ce46d0f26218c81659f2cb1ffbadf3ea887be23de435feb480f498ffed343f575daf87cdc6aeb23912efa60a576197d4489c3dddafcab10ba61174bed30c5b343cea85d3a51b7374bb1b3cf0763df0cc5e8588a0b370f63cf6e9b8807cd744ad1f93f18fbef59ea06561e3e74c82981373cdf0fc6a4b337453ebbf492b09e3d7a1fec3fe93b48542f6f13ebd9798b0ffa8fa7fe8bdefcec71b73c8846071d3e370aec906bf2a6eee3d5e91674186b2a2cf7d057db8b2dbfe9d917bd3a6436c22f37ee2169a517dd3264b6b2bd1da3e8177247b2ddca29e5a45e3dd8db6c9bdece74f8fdd5db99d748f5908132877dd43377c43d75e68ce0c7ab243d3a51538f1928bb3b48a447ffa4b7f34f0fbead9b8a2b6a290da079e8b287de56ee887b66a0af5d7af4326fef8bfae856839a06e5ea9bf24d70eaa7db3652fdbccc405f76dbe39f1e339074c9c52b6d47dbabe8904b22e36d8fbb056114ca20ae94b95244f41863bc12fb18638c54d2e44026072cde43ad1efaa44e0f7dee53ad9c81defff17cfc6ffb7618b34c80aeddb2a2db6f83446af461adfbecf6cc6d67ed5ffd78a7f7c35cd1a7f33a9108471fd1a3c76723f51255210203f17f9c02bd4455de3010ceeee675f1db93f42bb5d7ae87fa7ed0338fd2bfcca7dbcc9bded6adc8ffd5166a0ff5f4e85de6f1dbe68a667be45880fdf4e033d7166ef2a75bd1a1e7d3adc82f3d4e5eef56846574d13fff88c6b28f6518838ea5aa807996aa62e5bfd6b70b3d4be160e6992beab6aedb7aa887a6b3dbe9f1470fbee50174f0b3b84d2f73c815c5cc996301f699851c92e9d9fda64fb7cd15c96e49672e09eb277c1bb7cc9b0e39eed697f3d0ad652ebb1fec338fde57a7c7e87d3273a2b6d1fb0cc0d1fbd9f0d463f4e97ddc2d1ba2ccb0e4a37fd1bfe8df74ea35c5a9a9a9373f3d7adf9197d2a7c7cf9c12fa31f39ae8cffbc18fb7094e4d4d4dfd07fbe8d2abdd82db430f7ae601cba0d4c1f20f08838ea57090aa3a589ea57010f3ec3c39cfce3e7c00cb2a74d0d959ebbe317a7b73451d21079d3979a59d9b8d5b7bf09b830e39f885e5ef76d87d611022b1d0a10793e8a46e68f39ffcf60abd568e44b87d74ffd56f973dc40ea19323547424eae8c96eb51fe9beeebbbba3774708bd3ebc3b49a04b87f17ef03b425803cbcf3e5ff7ed36c790333ae8dff62d800e7a84ae142f515be8f54bfd948c8bcf6d6cc409aaf929dbcaf6063df95b9b6fa23fa506b7ada6642c36e20463dfa4c2436f670e898cc51e22e9625b0ff1371b81fe4597ddfae047af48179d9db9239e7acfa7af52d73ab0dcf6630849b02c754311cf747a1f8cfd8440e870d19cde1a74a01615cce2af0c5aa130b66d9cb3e5fbbea46bfeb2b7069d575dedd6a0f3f2e6402cfea2b7069da757d6a093c61b9a8db1460de7b851a96e4ed78693950dcdb2ac861c2f6ba8c19366e5d175d0a89c669d99cd5fd93645e62fea759364347f715e5f2b99790ddd739c7346a5f2d766bb9c57996f7fd5d03dbd2fe9d45f5a96f1bc74cb621e9292eb94f755bbe321b9a9ea7dadfc093e747a5fd69fe06fd49fe0576fc8dcf997a3bd3896f7a7f3eccc43aff68ec5dd17e770755f51aa5bd0b9fb8a64ba055d762bbe62996e41afaf38c5f68a66ba053dbbaf7826a75f114db7a0f77dc534dd82fef13c7b906eb5f229f2572a954f91af62b17c8a7c96a6f914f95ae753e477383894fa14f9d4a3943e45bef4e8d3899aa787e41129fd9539478b7c67d45fd569965d9d6e45ed47d3fc457d53f96499bf542bfff697f4d7ca55aafb5271b722ce107f555fa95437a75bb10bfaaef5beaaabfc25399e6e457f69dbd6ade8aa5a6fd7adc86a7ddf6f3af497530d394fbd86ede97db5537f65525e7a9d6ad0f1ed4d7108691c01065f4309d1a307e9a213c5e9f1f3f4d08b473a8e479de99bc71cead623b73fc9ae875e5de635bbafac6afe247fd3ee4b7315cbdbb5cde77d4daf99e6d567e6d3a354ad3aa7f745551eb76e417f597f928fd3b156b7e396756ebbd2e6a8b47a2bd5f976795fd2b3292f73ebe3f9f6e82c98c3080ba1bf2094c0cf589be73730051d4bdd107bdbdc11fde8d0fb280c59f6c29c2e48c9c2cf1857f8793ff6a77e28a53377d41f9da8a5f7b57c49698097137ad3993b821fef4e127ef3d1e35582ce6f9e9fa8a7f7c1874d5057aadc20f54fd9477f35c17488c28b36ff243ffa2b7a930c670c218d7fe26e7d4fdda493e54a95b7a19dbb255d4225fc86b9242c848ff78bf2b008b31dd8ad875e2438ac0ea7bf9a6eb8e611de17f597f5570e6fe25ccefb6ad271e838feaafecabca97d55efab693acbabd3fb6aa2be79bcafa6cea5d7f01d9cfbaaf765efabc97af51c6fb281e32b7fa93cdea6e825f84e769b6a38cb9b32c7715facfbd21cfaeabee47d69be79f55713cbab93b0f212ee0b7abdaf1202936b017adfc9b5d02e3b59e6277b4aedd2334e89f4f6cc8b9f050dfeeb7e7e3c3faf529c5e7b28faf44d07dee80c53e87ed9437005f85406894a58ee21e90cc49fc9f2edd106697cfb0755ae79732c48e93ee855d1552a2fc8f6c1575279dc7cdbee173d3a515425698f5906812bd0559dca15e8ca32a7923985a13a950a4375ea442d610cc1181fdd42659c0dd27cf4e8c47bcf525880791b80799f67291b60c1e2cd749567380e2f8e673ebd28956b3ab311ead98d52b932eb96e50216cde7f4a04bf3c234a9c08245742abf423d8cf598ca355d3acbf9a30da6f098546f7df390a8de76e58ca0d7d8fcf39496ac6fce39dd3c26eb5c0def833dcee62c967755ea8582b9542c6c97a5b4f9caaf50872e6bbd1dcb712bce2dc7714ed43b76e5ed588ee3b8eb050b85d73ad1cf0b954da75a76a3e6adc2ca1c2aaa7a76b1b82b732ceed26e549dd3a7d3cc378f21102cda4edfb132aa427536b0d48ef5e957dc351dfa8e9db02e1fb9a2e8703a732ecc1a9575adc2321cd3b1d40bbefc57596a0b36b5bdfed72d49b7b28760479647c1e0ea9c73e61ac079548e8e6f4e85731d87dc0b3abe5d281cce39733070aec3795b561d062828178e77cedc0bedea1c025472f8e6543acfe1907b21876f9739183acf71451a1f1d7ad6420e022bdfe12c8fcaca596ec3835ab90def3c129ca87778502cafe19b07b5f24d05bff36c38e7502cd7c1e1245ca8ceb91a1e152828ce3bdff176e97854a0a03676b13ca81e87f21cca5d87dbf0a8784ec59d8a0e878aa2c2e3359c0487d271a8121c2ac73bc8bdb0721b0e39183ac771a1602e96e7f0a0a08b75a10781d5741b974ae738ce66b8f860e68b193166ca98416386cd0bb87ccfafa0932fcd6079bee2a15ff1c5cc9b2bc43c8e67a92bccbc9d1e930a89ea2716407c748845968f9e5d31868d8d3908e0701cdf3cce571e151cbe721b1c046ce05ca89593e0cc466c6c1904a036dfe1502bafe1ccbd60c3b90b85c3b795c7ae1a1e95cd6df80e8f8a0dc7e1247854a036c7e11a0e07c3e6362e15ce6d380ef7020e5fb98e07c5b98d1b45e586ef70ce330f02387c75a96ccef2e69444d7ae650e86ed56b1e19bb36e94ca85e39c17a5727546e6fd82de0b96956f374ae5dafc83de9e398ec7f2cee3a02bf3769244a7de710de8aeac3c4c769596a66b9e398e331bb1f70b7acd55f783de1e9922ac62c3712e147451d9b950391c6ac7a1709ce53bbc7ad437af8a0ddfe1373c281dbfe1760056a4e40ec2c43e73fad95b98f1d0e5b90a2d190f5d3ef3f9f26d6fd44ba27da6facc550f9fb2795e43d97ce6f1fb9943c26b3ea3ae7dfde69000d9028ad8670e392403b022f559155a56bee3424117947777740ee53b7474378ac739efb9513c585470e5007375de39098ec5c31d0f8bbb366779512c27c173dca8cd49c0b9512c6739e7363c28980bc7bdb2ba619a5478a58f21908fa56260e6a14bc7c3e25055a0b0b82b6a73ce6df80d0faa841bb56d4e824345d570122e1677ad3ce76271978d1b55e36271d776afe0dc2a2ab8707c73ee5661c5a0ccc3fb24f98a879786ae8affa697227e89cdbcc3ba7c6f9baf5c3a47220d5839cbbb48a40acb57170bce0d9339cc25b3e94507155c5dbb562ea3ba0a0563b1ce06968ac5eecef4955f71d7ca33ffa27c7677e6caa17f4b2211d6a5c2f9e6d22b4c015e1d0804366007129c8fdc0bdbb6ba1de7c25602ac360e0f96610f7cbecccc4f9518e65a88ae8ac1978faee2f2d1a1dca2ab5c722cd02ba5fb4fc662ffd43d75ab6ace08063db3912041dece0ff6b0850fbe127495abaaaa1d7e2a6f8f763250bc932bb26e33c8bd6a82539073cd396f33136ab3ced99b47ce36eb4195e3f0e82a301d4b9179f3aa1d2e06583e5a6ddb7cd354398cb8156b83b067478723e97674f470b0088eebe138af27e37a5ceb61753676767a1acdce4e8db9e325f84e87b34198b343c676ecec781c8edce1fc86736e396fe74b0fedf8748bc3156d6e739472b8ca833e8733cae12acfb824d1553e3924395ce5d6c6d6bdf5f0709c13924d876f9d236179f54dc7e65bb77965715ecf6d8afa9dcd83cfb3b3e344dde3f16f453b1e793cfefad3eef8dce15cc739b72c71781ffc1cce393c1c383c7e9d1bdeecd2ade92578f14db738cffba01ec7fba06a781fd4cafba0e0ecd24350c67e3a8c6f1a8d4a7577904457caa453676e854cfa971ee27cbae538ce9b73e8ad50c039193664d0902943460c992f6c2d1677f9e6aef20be576fbc9b9dd368ee3dc7254f3956bbe5af974bb5aad562a15abdb3627242a95f5ea2a95caebfdece6b16e53d4af1c7a99a7f2f8bbd5ca89fa59abd5eaee24e11c3a77e1976e4d77bfaaeb518972f7fc7accfddeebb10b56d5ceb3141074f0b6b70bc364ee44ade29448bd40cdfb7878b332dd9ade5c0bdaabee57c3abdc7213c0bf5ab445d9f0d0ddbbe22e28f728d725c0bd31bf56b85170aff793d6b3237cd0ab7ff34b849ee62a2fbaf53e1b5e73e65a68bda68a9e95e921cde737bffc74cd6b33dd9a73886e7a9be9a122262ce2e7e6ce3f1d7ef1e9b509e03e25f782e7ee917bc11deaba5fcf33333bdd22d31e7574789e40f331e7a302604ffd697a94504e53aa67f7697ae61fa7a4f55fd44b1b9a767c853da55eeac11c76e1e66ddaf1d07ba0809c510b881e5c4cd802002d3e597860b91f1d5af1fca343edf8e836e8f8e84f3d1f3d0acf475fdaf9e833e4f8e84e707cf4253a1f5d02373e3a0c257cf41748f8e82ee47c74166c7cf4156a7c7403e07c74a2eea31f617df422ab8f3e84fbe841db47ffb11fbd15dd47fbe85e3f3a0ffde83ad947cf991fbd931f7dab0dbf1d26957048fc21d4674431fbdaa3f794f970ea615d0111399ea580f0a505b7ae43870e1d51870e1d6ee5a643870e1e1e1e1e1e1e1e1e1e6b6d4686cb47cfa1927a99c3d92b4c817a4f146f3e7a0e68a360d34306cb47cfe17dfd3b4e3d3612f42ccf31a48c6ec783fe833b4ed4cc46827cbbddb61c43cae8320ffaafbffac621f9fa39df81cff7e304006bd8436fc7330ff62cefcbe1ed1aa724fa8edb8d53d29f79f5a3ecd60b7be87d500f9de59cb3e21b2d9b5c10309d43c0f46cb5b26e599bb54ed4d56a1a97d9e33d3d3d3db36787efe8f19e9e1d3d3d2a2e494b875bd761ada6c12f39bc0ffe8e471eb7ce73bb4cd0f3f8b7e3ed1967147d073e8f5b9e1e4db3f18db55a4f734a7cdcf01ebfe13d5ff7f4e8b0a1f5cc2e5a4f4f4f8fa7ad7abce7ee24d171cd755c73cbe339c9e7711d3b72b232d6371c6e1d875bd79cc7abaf22a1e7057b7b9ba2681bf83d5e137ccd999f2bd2208fc7afd27cc7cbe1443187c78fc3a36cbc4caa5bd26fa8bc0fea3fdae6835a791f54ebb5bb93a404b75ec2d5b45cd88584f81f6df3d2352db44d8e0787e8fa299b1e8259191b1e1ca2839f49f510cba5e7f86697363da452a95444adb2728b6d7a48a5f22ed3435dd7755dd6c5ed835fbe732bb7ae53e2e3bbdb65bad8a7fe243d5e73f6a06735144047f340cf52517c8982cb135163e6879ea5a0e0020596e71923cdf73c4b8d71620c98313ebcb5b151ef8376db98abf783faead0f33c2ceef236e85d28cfa3542e6e146a75bb6d9ec72ee849afd0729be6b1c629f1f19a33b7c2ccbc1de8fcb04f523df3a31adf64f07e500f1df6902b824e149f5a890feb2cb7ceba934bb7a475e9d63ef55bff342e497abce5a057879cc3fb39bc4ffddbca2d73acfb453dcb33efab9ce78484f3ea486a38773f2724d5711c49e7d5b91a97bbc539773f398473bf26f89cd7fb752c6fbb4d516f1d7a99c779fc2baf09be75cee3df3c7e7e8d4bd27a7b7792a85c73d58d6dba255d7395981ed25c7a8e4fe3f2d2350f76c90185e832174208213a78a9f3cb213aa5eadc6672c0213ae8b25e28a5fba7eeb3eb058bbba03a892e549820e68a926cc684000a17150b40c15c3eae4390eb4a902b0cf4c9a587e29b1e820fc4f33bc9ffe29b6f332fbdcff0886d78b49926008f2f2f1d82b95de6c22f4d80ebd2730c29a3835c9e5ff6d04ee639ef64f785cef37e4800cfa5434f0702813ebd1cfc037d7248705eb8ee79e45ef0dce742f170cf9b7381c7eda20fe89073e15ee65cf0ae4aaaa783c0d8e8ecf3c12e3da479bc43cee8609797ce16bae6f0aa1c7a566594457e95b7aa9b8aa56aafaace32b7d6552aabf2693de899475d95f9acdefaea56e5adaeca37ef835fdd7a9f34e25ce5d1b35ebdcd89a2ddf839ef7b6a96f7c1a7feb60c513039d7bc9d24f7b31e5dfbaabf600fa347a174bedf74eb05c7a337dd32b7dd0fea3795432fe27858dc85e3dc04c0b950519d73a3a072286e14a2e378f495ca63dfbcc7f1d8d57961365f791f0cbf83a4f5d65b6fdd3257b439f4b8bb5957799f8c9ecaad573fe944f1a97f4ecfbcefa95f898f216574b0cbf3378cebec892dac340fbade58ec3b6e7ae5048cd12e2c25b5f843e8a5eb6729186079ee687b9f842968bd651ba987c55d74a3f442518f02b2051431178d1472454db54b0f3597a4c7472f75de0ff6b33d2070f9d647793fa897de5ef5b0b8ab6e158bbb32cfee270910459d1b85965e139d9a9a4ae3257bf5d8d5f165dfa6483d6e143e967a81987f6a1fdde2a886cdce8060b9ade50d16737096b144e962b1d80d307aeb9bba0bda7c8f27b83c3fbfcd9e33a844ba149d7fd279a7269efaf627fa995bca2561fd94cc21a1fec5c91555242da0ffa4479f5c91bcb2760b32b53e12812ee8b2bd3da6d6c70bb9a2d6c75b51609fa59a1013c5b399e726b490113a734db4c99ea59a60d3040e60a4cf524ec49eb567292774f08ef89be09414172f78ee67292e60f0dc245f8781e0e51821c740bd79b04cc7524e74f94fb2941321f8afb2a77a967242cbf310578986e57ea2ccb3d413629e7b967ae2cb7b24f25324fd6b51679f228af4102b890e1d7a3bdc7a2fd20aea21966a42886f1fd243f079aaf7fde0d7ea0575abef4fb75ab6ba65824e5e27f9ed39f6c7fb60cb04ddd7fa867d57a562180b47d041df981bb60d2211c8ac04de10e392b03832dbe6205b98f9c8c4c3f63ed0715f588681a2bf3a966282cbb3940b80f80fbe9492fef38cbcb0886e25e1c567b9fa30f0f042310c14fb5f1d4b2d71e6594acc972fe2a3d748845dd1619719a2d810c3a2858b8eb7828a10ade0020c4b7ae80b1ba1662216dbce33880b741173b55fa08b18e45a982798fad91e150bfb09b9163e2f5061a257ffa0efc4db7400fe8c4a75305916d3f8608b177b6f9841c45cedd395f8d282229eb917fa42d51ba6e900fc71092925c44085810ed50eef0162c6aee87d5eea6b01edf542556f5f00bbea05b30315c49631621f1df6e5e8437a8d3ea40f41a363574ce3832ded7da1dac3645746f7b293b9caa968ee624e01edd65e2af586a1ae830a2ed5a5a2dd30ec6a0f0a8c941d9f3995eaedd1db5981bf04b457678e85e8d9555a62407bbd61e865caae1274e823254cf67c42cdb7dbdefedb8eb48f860d39ae8a740b03ddc769be9d47023de40980811aa6206fe0b85bed44d1473b1530d1d9f5e97ceb4422ada8f391c8f4f6562432ef91f6d1def07ef5bf5611066a875bba788b14b19ad8b104967d6a27a10609346f216743c74f653abb74dc67de16cc05bd63970d99b0dc44a27f3c86f48fdd4cff388dcc3f688556ffe016126c383b472239aef9c7694a70957f39d63f9ecd3f1fcebf9f957f4358fe1de9fc633238fe7113a901bbb0112fc1eb3c1b5e0d8fdb078ed779dc3e58471efa8adb2ccf435779dc3eb4340f9d04afd26c4a3763bc0f5847d9e8e884659f0fb6d924119de823c214e0334ca16fe01200055d2be79c39249a13757309585da8282e511cf183980b0a4ce654c0b48799ceb5053a793fff793fbb74e4ab76bfcd35cfe956f4ba3d3bbbf43ea8e63b00d7742bba170c14d530500fdd57e4a37f508d18279c1073e433b7b08824922822caf6050c982f99c7748489470b535f21f5f0b62017ee48c6a638f57605a319a114ce0bf24037b96994c24fa91e22d343746bce687ae6d083471e5e28d5ade864a21cf9e9f0870efa57645e2f3b0b6017cb590e358388b9581e3d684a37b9157e98d1c4cbeb85f3e62ed4ca7550c1c5ba54a2661031d78a73ce21f75ac0cab90bc5f9ead6e803fa02d8c55d30cc46701888361941d954342f0e03c5e944cd29802b9a1b4c00f4ac097e07399566376b5d6e9bbd3baa14bec2987d6ac326e1671fa55bd0610f1d74d846d52a225866231a8bfab7c4a3e5bfa5eddba32f1d79597d66ae79fdcd2589697cd081a99f578b697cb025f6d44be1217774e4a992234fef4e762196232fa11518db3ea66143b7be2f74c8463a0aacc29ec252b7d433ca3c865ee6ccde0e99a74bee884a66eaf173463da3f436a93095b57793b1ec533a27a3e945d27b85e8bd647e4cf49be2d4d47fd94bb7ed61e0a5f75de0a343bdf43e0c7c742a2fbd0fea63d44befa3f2d183bdec408c0367de665b335d45a1ff35d1871e3db677818749d85cac931175e846f461930a9fc1d02de8d5a7c359a44875c91d499fed2b70107734bdd52d68ab272f0db3a91785fe175dfaf4a4a8beaf9313f42755e6cdd9f7bbf2dff479914c789b580fbd5ef97999e84f24f2ee2089f4e36da22f85fb78026623d53abbed8e70f6909d415379f5ae9e4fb7a073eea97c7247d3abcfea4de7d1e18eaae774ebb34e5df3a668ff45a7378af1f136a930f596dfaa66f5aac96e49e9aa6fde1e230c5c916523aca9201be19f5cd6b4e3a7c88f53b4e7becd9f6217353f25fee69672472ca74ea91735cda9c7721557e464b472e9452a577190634ec51d653e9db922ea659e15d52a79745aa005fa48aec87d8a87ce81cefaf77394f9d690870ecc6781ff9268f34ffd2f69fd5ba21f3de3940c7d74ebc10d744d2a4cbd7673b8a2ae5bed4f103ae58ca80b47da5639a8a5b31a57047de8a07f3fdf4a4d2abcf9a7c85221e8f253e2438742530f9d9eb4aff0e14db27dbd08e0b79e7952b8b77773c9290142b359b7d625972473eb924bd265fc76977077866e41276acbf9763f96736eb9ae736b39d6ca372fe23a5f6d5eb472bbb965b9e536ef63b1582c975c11e776e5ccd9ce83cef29c5637a85bd0b95bc46b79dc6b05ebd9fda2db23293bde5e2f38ee058bbb706e14af718191dedc0afcb2fb99d3b6e69c8ca22b2df19ab73ebd48f3cc9b1eb923f84e46d2ed65e2353fbd4815b915f8a7572e097fc1ff45df41c232f8e8ab319e65e62587005e23b922ea4e2aed7ab720bdddbcdc2dc9d3ad1f5677392051e6773c4b21f105092e4708f196d541c077900237390881153f7041754674c0052ce0c18b105ccc97a38f1944cca5830a2e761f6801db5b5e3a74e993a5bf60df1fece59532b6bebae6c1879a073923e8c1b772835ebf9d1bf4e07375d8ab0f86af1e19c655e6edd3330f3a516c8f1fd66e6d14b60c2c1f61e5a3b3ac4d448623e0e7a69994984ddac033d6b49967ac21439e61149fd7f01775ab4da4e94dd61dcfa86de5ca4df71a06a3c6951b342c850d61952b37dd6440cec8bfa3924739162067d41ec9b0b61eda911e5d294e5f3a529d1f3e06669644d3b6eaf1773c3e3f73483249e5bb63e59268954ba2c17921ffc456b14a327714ef079f4302c380a1e09ba00ea01345160fd634c4d271842cbb69e0469c59f3c9872e3ff3fe8c72144d4867be09eae0619cd497542f7de948967d17f8f69a591684d59b0c04955dc93fd0332a4746beca1bf9073ae490c4a7d26336659c1b6f32fe8426ac3e980a5e5e996561d9077a125fbdbfba10d2db95fa7e4b3c5a345f523d75e8fccc9ef4968e44f96f4905631222b12a19ddda8d5392241e0049c60cc440dd5c513bf476faf6ddba251daa9bfec58754bebde39c4854da6d06829f0b4febfd60dfdef4da8c1bc6d5c780f899c755b0220c7634e228219e3a07ade85ff77d7728bd467d3f388554996fd7a05499d7da3017aff626478be93eb946b2e9a14f7b23d1f4d007a53e4d0fa979a3736489952f53c0365bc4749a981065e2185a64126ca43f888dc4296bed3a1d1df7d67f2d2d8d6eb59bd129f375ffb1541118a8fd1322a28ac5209c82360406d2cededa1b066a6f26ba6f7b58672c1261974ef44f8775eac7db750bfa156a9278be420768fe9368befdc7f3153a00f3d0396eaeb669e9d4d4141a5b1021ba9d26f8b189f5947ae69ff6e6dbb3297027c9cfab147d7a76eb7d82aa2e76b5c9363dd471b593a43f2a1f2f9ca25bd0576ab22e8199e910602c778c2c255fd9ad18e38a81b0cc73cb731b9662f310ca35cfb28b04228d3768ce245144d412b38cc1a60c1921ba4bc7d24883464c12700bd4d26689a758c1055d0b32641f0662177492f946ae288881584da6832eb923f87cd9ccb3db56b7786b32dde48e649471053dc26b61c6b1e95ced2481b24db7d871929840bce4da10f9e696ccbf9985fa37ad3cacfe49374da4a289b24d0f45879f66e51b88af6822910aa67db4cb34eafdaa1a2b6cb2b4995bfecdf71862342b1032b7be3883ef28f5ed9fe69303994814c33eda61134ca4728d22defaa55bedd28b62a0e8be28e61b4dc3331d83ae5b0ea324c22ee8b677f88d8f44987d7cac8f4428fb88916174f6210fb9211fbd21677432e38ae0953ea564ae4d7686cd3cbaadedb689ee93b0b922c90d63942b66a0ccad75e959cfbccfe733eefcd361ad9c39fffa37674e610902b1001117a28fe8198edb1ade2ee8b85b35ee07f563b038f15dc38eab1a4df16b5c66209cfbd9f0d45d8844ea124cc1070289ddfd7458f7eb9e5797bb5f1bc59f6e45cd230351798c9af69f4fbf852c7bf5457d8b81a237135d647bbfeda5b313fcaff5d56fc9672749f7d4bba7f376701e81009dec568cfcd4adda826e7a746e65ced9952deba4f7a993c02cbb3b48e69dd22fe398228a78ea8ec562b1a7eea288228e7cf423316e3986940139e84472d1f99b39e9908b2e71b09009e2e1d291c7c142e6040dbf0ce14a43022eba8c20cc1822d0028c0e0d96972eaaf53245192cac17ee798c0e2e1863458831559c18229b73d22c036952b0011064b1e24b9b2cac24f1ab9f63ac40f173cee905c674749ec7e870021db8fcd7391b1e73e5cbe7e0c4b70a3a1ea303961f7345cc181d62df9e33260732dfcef342c75cd43912c96864672ff44aefb3e1e59c9ed294f8ec369eaf170bc821193b4e4d750aafe4049fa57fecb25b554de7557a4ffd5e3267ae012ab8b2cbd14774494618d2bfca63aa80a9d16bed24fbcc2796fb4b0a825210c425073ef8c18a178c81860eb0b8c2f200bafa416f59459a9dd6eae9785db7b8643add82d0ab10f15f0b4eaf734c95217e7ace0cfa2e47c79fc754c9c0ab9ec7e030f55fe752d42ccb68067050250756baf83c06072fea9aceb22cbb12860f6cba1ef4dcc018ff79c78132b0b4e1a1ebe7313898f18e839bf6710f4285590b6806eadcc0941ec822103e3fe0204486031135c7f3181cc63ceb790c0e47502d26a514478ceea16301417b1e8343acc304e9743e3d34fda787e69535f3584b872386cff986088f4bfab43e7f6fedb0e5bfce75f8f29da5cb791e738313df3974389ec7dc60e6a55a3dc4eed3aacf636ee0c17f1e64061cc1b7e4b3c48dd511aad0e18e10868ecd4a0deb011c55c75e34eeaa12acfa3e3f74ad810ee7596a2a060ffdeb3a2d54999acf5c3e3f484dbd21c1e5f3030f39ae2ccbb2ee40579fc7c44cf091c810355c3e3f6c3c26d60411ebf2f981a54a781e13bbe2edf398980c82a055a6204d45835e35b97455a5d5cb14f434fbae3986b8e93297cce4f34ad16bb7ba6a3073e9df92cf6bae72ab79aa0f74d4f9dbe5b3a684c9e7b3ab247ddeee10e8f8c01763c83041143800c1150ee808c34f10bba4ab25211b01cbe33c4ba5518226410721749d6f29e8a9670e6f4eb7a08ed7758b49134caca1020931c078c0cc1943f3b3379f5d57966510769ccd3631cb2084b566af20c00882891b9a6041cc8acfbcd3c1673ca010c2ae5b393fae7439cf524708e2b567a9238460e660b9cc17e4fa8a441813479ca0082ede9c7106181757f1dbb31349c5628c043d06c518638cd023ec6e973107411febc74742021159a820a8b7745ff7dd2d7fc9e7a33ff523e1110384350f5d7a4b3ef2a14b98a6210c424bd928270d6a60332948c04a81cf39b339e79c73ce39e79c73ce39c970d1428b307e004110ae49c5cfa9c54fcd35e78c2c805290809502186950034b593085460faa4882881bac9142ca46d9a08c3158a459b3c5144f441438a5f783936694524a29a594d2ae5bd2678e4ec753665e348f44b4db451fd2ef579f524a29a5598ef48eafcb304d831a589ae3081da61083031d8600a206613a1430ac4a1a665cd1c5064170c10e34a881d5b2ba7d96f17ce699cbea99e7341196cadcd988eab3fb2df97ce699577fe8328750ca1e4881c5164c5c8185085ab832293ecbdc0824f8cc799a08cb95b94f0fc9cfbc1589b02bbbdf52d0cf5b3dd0f143e9153661a7208d2d69582982118a3084229c61069b2ede0c219edd3e4b9981458c6648d1411e102112dd62a9aecca7f742c75c99b3f7453def20815d7eca78bfa8173a57f6cd799b582faf978c9da86319f3d5e1e883bb15d104412db6c47e8f73be6730f27d05f0dfc6c542345da479e8bcb231c618a333072f47c8451920e4b394195d9ebb152ff70962e4c855e0942b0c03556121129d1964a20f06c05ced54a2e7106f5443b7524aeff19fd41368266c5e265e13f5fcc54f67affaf4a437a3116d5cf5c6b80a2d9f26bd1e6f217310e8f1ec1367b70d2cf3082ca4a2e31a7439b1e3f883bdbd26f8501006fb2d23b1fd58c8cc8c83813a879d75a4e858ca8c2cdfe16023fcac67292010e1b95b3db04df01b0c32bff9f4f65ef05bbb4ad0b5767aaadfc453469bab54ce77aabecca76b1ea340fa064612bfb9e656e3a2e69248df9c39241a75eb9b472e4975083fcdadf7b536a57e533c42ec097e7b19d0332ca878e999af49e333a72d1760f83224c6560fed64ee3ead2533ac115384c81873feb498b9165853cf1c57d86058d24393cbb0f02cf5c59a205966c87fbce667b33f65df3e3d34dda961a5defcc3f7cb9cb922abdae007a9b757ef07fb3ed8674a1f75f62f43422f95acdd2dec2431497693406f9d1ea23e9dc77e11fbcc9b37fc579d59ea09345d8e8ddc51f58fcad443877d43ac52efcbb21b1e7ac7361a175ba392f4e950c6ea444f9dbaac53c6ec7efdb2ba928a00a2afae7149a8675e3de61fe97ddc118c7d13947a66a01d24525a5cf1dfa4d98d517eb1df9167222ce754cefceceed3eaa1cc61aff238af1ecc5aeb44bd793bd535147479e91c98cc6d112f3ddb81d44bb7d2ad8541ad09aa29be7596c7aeaab49384fa94744593d015b55edd6a2ae7d754d7a8bae65a9b97ae72352f5d69ba76b7cc48a552a9aed2f44fbab6b952269dde2ff3ed6a4e14b53ab7ccfb64ec2bcc415c56587e853948e6d04514b7cc8bcef2d8e535c524da55e25c0e0942c779cc38b777c73af5c9dd1d7bab672aa74ab5d2249556d7ee97b9756bbd6ff31dcd7947bb46d3addbfbc11d9879e9d66d6f4aaa2ff3cd379f46d6ad7538a559b7fe19fc23cf2ef6baadd80817898230556f3fd81538fa578d68fe69aff2607cee1859c8b22c5aba7107d834af5fe5c19fedf4aa1040f4d6551eb46e559bca88e99ba2b61d5f3904c0babce58e605dbe69c95b3f00cc87b76eeda6714882b8cd5bb7dcc6b5d01febabc3b9faa2b43cf51cbadd3655c3d5ea8beaf2d4adc7fcf35987be714adaedcd3824edf4aebea887af35874436c557e2327aa311ece75582de04f55fdc49f25920db009ba8032e39589303326c280e3b44d3b17f1b7bf0b9957c9e52e39999d9a85760db2d0bb247ee76dee2833ea314477a8f8f4e9dbde9cd19679c41e70ca679e8b5532f9b6fe79a0e7a731966ccad3de8137ed141b79333e234196dade00dfae6db75abc747f8b00c462b2c6cd3350b9ea5d844f15d8267a9308400e30c92a72a6dfe8339f149d9800c9e1dc2365d84e2598a8c1e1e927996f2010dfee759aa076640ee4845859a6f5af26d48f04d51abff9e7690039d12fcf7f4a5047607ff3dedb083f9df8b8b16fc708199ff5e62cc7c8dc59e79ead99f7fb04df0a5196fde7befe59d6760f9b9c5c316a2f7fddc00f1d2e507db4932b778e9738bdf81ce45df0bf692a7a6a6b63c9c403cecb9c54b6f0a09f03eb5db3e82532f91408f7e043dde27fb6770f9a7fe79bff629f0a3c39ea889dafbe879df4ff6d96113b619533c64dd8f0e616c95b792135fbd1d36f5aa7975dbde577dc7bafc38adca6bb72b6f67e5fc1fec57be799f4a759b583fa5396f67e5f0595c12d654ffb4dedcd1ca398fe59bb7729517376edb3c46e3b9cde3223ce7f55bd990279519ab7df3d808cf4dd872b59384a3989afa604ec462df695ee5b5a772d67ccb82cf4aedd5bfea7c04bdd6dade8da4bdba5d792b8fcb6033dec66de5b11beb2bef457f4abf0c4ef0adcf1bdebacd38b8a6b3ce1cfca2b39c66460865673c56d6575ed0ab3c2613c3e1ade6cd1959ce72bea934dfb48d6a6e3da78d533951735ed38e57f9e6694e35d565fed12e7d768a732f07290d2c44d3451168baf92cd54512cf4000e0388ee3388ee39c7d3837d65a6badb5ce3e960c4dd3b4ead4354dd3bcb211f6d1ae5207c3f0b4e8c0739379ee160351a7dc04a7a05719887a7b508a4e8a4ebac6468adaa9cbca4674601942d17db2b3ac237e9f2cc62cc61833ea99b73110955d74f9e92dea4491a869e6947a467b04f6889fe5969999991b3a7bf021b3360662797bb04cb772ce37b70ecb742ad7bc3a7558a6cb58ac7616cbdb63a1b10c7fd0498fd327f5ec7e1baba4d157df9bd6a3a8de5e2655f7d7be0483f0f332a9545e554df055698af8f92a25d99c9153a9e6bc0d81649ff16bde5e432099db086517b16fe7b7334aa59152e1db89baa893b0d4f7fd88beddd66d3a4b3d4ba5917a1ef37d65a6799ffcafd28f4ebd6f69fbe9d99d5783130636ba8d1e0b49e659a679578f9a57d76c36057eadf1eb556aa16af783bd066d8d31c626a8efda5ad7aeedb07de39de8d08920f5e871757adafe29fb285f9d39244bbe56a3e9f1c86974eab66a2a8f51e5b5c6e89a4aa56997f9c76816d5eac1c95d8554caf2c489d3ed6c77590aa12833b3537ee835413d6c8af0b2f7c95a217b312a819c51efa33ea5fb2519bbe1bff6269ac60d533fa57e9a671e3d25c9b7bdad7d0a6d729a42ff05e3ccdb55b709a6f172074973459d4d3f80d1fd9aa05e3a7fb0a7d0da6e87dddd0e2bf4a6de2eaf524c2293408f5789bd19a6c1ca0127f00816caa0eb150ed270269d0c3a3602bd231482856e59996fcece41afb16af33224d6aa58e5ec9005dfaa5c7376c81545cde351596f0a7ce84d81afc91857f036c5df31baf0ada75da5ead625d7543f737b79e26c2e5d73c8c12dbae857bbf5abaea9ee077b1554d9fb41bd8ddf7700e89a13c526a88f9ac74df123d4a28bde7dec2609f43ca9b7036f13fcec4709b440339dd2f41effcd0bb3748b3fba855992d88853d07b618771180e9d6f76ad5c0172658ec5860bba7498a587725efad2c32cdd921ec46024805d5632874ed4b08aee8359c078e950ec5df48106104b94608922d4c462ae2e2655d04310389ce00997952b402ee8586cb8d8ad5c6917752c047041cf3c2c0470418579013a940bd187f49139549817a867178a28fa904e2f549817328717caa30fe9d90de2e8434a0afac76638083a48e1858c1b10e10516feb01fa6788204241883e688335cec62ff825ef058cc05b3f4500fa8a55b066159ded2a794fec5fb457d9c5fbcbc934aa79729f3a568449b9f37f32fc6245c850f9f5d2fd54effa27e0906c4674c03b022753f1b3e3271155a7efa778197d935f3e50540b68042e2600c1c54f15ea0d8a35853535340b8b458093930a20a0f62d2a197580f0f28d8d2c509005d585026f61485d7bc9718902da0884d198015291b625c851617fb77819fce9c0233908fa56e70e59fe07bc181d7b8b41be3352ebe50ec514c2430011737a4118bb9ea8d5989c55cec1f94c72f0abbf4a237d700205b8c9972b133d7001c2f7290822354999a72b1e77c7c819d6cc6851ea22efd0518a4db964f8b0a087d5a3f30057e19e430857eae9e74182010e92f744bba0b2e4422ce66beb0190fd3e1fcc77a59db834f9bd20b2c99218a0d309659b173f2381bb148a44bc845e6167611539cee83de14f81f9c026d33e496ab799be2abe055821e3dfad34bdf7e0709af799bed589f2fd9bcbd0c817cfd1336c7154320d3a5b7b4bdcc3cbbfc2c839e5df382de5519e4a283aef2a8432f73ada9be0e03b1578fc7730662e73b6fbddfc637a491c599b7acdae6a54f0f4ed17d55aa4af550bbf44aa68732975ecbf4d07459a778e9f60a58458c910a08bfba457ae5d243fdd22b103005ae5d5e562d3085eea7de664427ffab5aaa5732522c4cafb7968101a8529148d5127d4897522f55d04daf52554bb7200bba797790742cf6d1a362b157ba1f093f459a79e95fd5f2d22597848d00839f7769face0afcb3fafca7b7916147097f7afc2fea67e6c1cf620a2f58547069dedece02d8a5b954163578e67a7a0fd8c874e9d54c24625d7a4513894097f4aa261259b9f4ca06a6505dbaadde97f94ef51aa6f81a6af02b6fa73a74b5cb7a3bf526a0c6fc0a745977e89ade0bbe97cdabf7d540c5c7861ce5ea83fd8e0b2adfa12e3ffe4e0238dfa1d705ee56e1a9379acbef874fd49b5705baecddc96e136beabd6019e2d2bc0aeb08ae2bd0a5b9951a8ba96e15aa31810efd09e83583cda667d828a743486b36a747af5e376733a1d3ade9919b3178eed67409b979e1cbee33967d4618ab47cd23f59879742df39a458f99c7187dea742b73fe39e699e238f41db923a398f4ea742b93b23210b71896a74f36029fa75bd397789df8551974edd0753c1251f9f45624627d7a500f693cddaa3ebd081b7106aa5dfba8aef26afba8376a9ccec3e5a7df6023f0a7673addaa5c24f194294eb5f2d577e80ebd525c34f12cc50513dad5e9569d0cc4ce9c6420f68f4458ee219a83d847979c91bc9f077f4a6fe9344974e95552875c930d5670b821f64d5f0d4530c2d467fe827d765ff47ed27ba95ac1e10699a45e69943933d037e994d2a3cbdb04f55f94c2520bb9229e38dbc639b39195676e579e11ecbfcdeb4aa93afbe67df5598a8b227e3250d69c51b7735e759507bf5df334e718f2fd34d7bcf636b71ef3dddcf6668f54ef7673a2e6eacda0dbdcba0a49fded2a69de9f96a45d73a2c89f79d3634f768b37183d20fdd3f9598bf4d00c1a325d67ba7381e567d0cf9f9f3a3fbdcbf1e91b4bf960992133ac80658ab3752ba70b2dff55339270a1873e97ca19fa8fcf489f4eebf4a79e79113af4d5197ae82bf2d4a91fe9a1214fa92f59f33447329aad8750f82fe7bf22dd6a8fbe815b4c3d6d34373c75ea393d249d52a7ce3ef42a719b4803cb10069d0e0fd72db67cc306dad26d68bebdcbd161233ead8285683a1ccb2b0bd174b2842a838d5d628c43beab773e5bb7841400cb800d7070850656a8694119179f8023b908baecb628d3df7e6413f2ed5b6578e5b3d416625efa60194a75ed7533c1326462158516625e3dcc600b363400638917b878b3d0a107dfc20dca6e41e9b55bada4ad7fe695298eec16bcdf537c769d1eb2d4524dbb9fe62ad7a8a4903b0af2dbfd3657397d8aafb985cd30aee8ab348fbb158930037d3c0fef97d908a10a4ff3a10d9f3933531cee16b775ab15bdda2df84d30f39e62d7adbebd751e89c8e82d36125f05e707e227b52842674a88848418b0f2b987a6fb88f9fb2c95c596cfc2ca19357fc64cdc785c7245d2b98794bc3da53ca8f469140a74b1204641c7109aa19126012463146030382c148d4743224d96950f14800ca2b4525aa08af324c7611432c6184388310000006400606466d80058c7b76f9ae5f43fa19457415263f4dff7fff0c9a67e95f940dcb9dea60be3793610c043ce09f8d7f96980740824f1b4f43028af632f295f0eab3120eb62c747040d92a07caccb380d0053a366dca3f9dcca550c938fb2b84ca6fe81dd317f29869bed948ec1533f097507e3d02e087e292db7195103ebd7f588cb12e77baa04d1b9e8c8a039b0f04558081855fc5e12480a5703de2c6d89808870aabf28d698c9c0f61f503b3f415d3058d02364743b54d17e4b493ba8873fbe8ff884cea76101688b349355ec4e02b66ce3f55955a14f492e57292559de082cd2c15319497255eaea7526d256eddff971d8a92483b2f45354b66b454e5da0c064b5079a0fda4321c59ba1a0d5dbe93cd0ff0033a6ad5ce03009720e85d06d855454c18804e3ddc6c12aa4bcace55d5815be307d40a8af2ef3afde024036b45dffe4ff7ee99af40315eefa20d0948bb7814e09bde64a0ec3a38c026ddcbe9beccb198286f40fff3fe77b96d188ff474f7ab77cd2280885fcf23a926f13a08242a5745415f081a64c421f6e400b77430f77a03fc81468710f34e3163423b3d10fee43076e439371877f7dc496d9d0b5cc77f58d02b72bbc92d51ca67481f6ff608cae9e43dacf23199e8888ff3e95fa184235a298ce3087a2385e88915bd233ac2b198caae4bfe5a916041533a45fdef02ba9c7bb36cb23847cb74b0b14e644d09d15e288f0cbe1839fce496bb149504f471c8f0f7451a86cbaa7bd29889e5976bc404890d47c3a8576e04d01084bd893ecf9d6543f58f22906ed02466b0d6bbe5b5204ccae9def650f7ccca7ce89b6e194b2a8072ec568c26cc56aa6fa768ec5a13230cd99b15471170746a92a3b7f81ef09401798325d59dbe6afafb00c444ccbcf8753ab2b5c64371ffebac8f1fdf9d54e69ed66e04993f72e8b038a435a09e0111f00b9a9373ba1a9270c493da663b40ac08f969d40f08d3246cb256af1f239bcac4af872d7a105c466269c32a386d09ebc66f5d3a8a28ea311573fc7421768c9681006e6c63990aa58a216e0599702192e6800d739c28ae610dcfc15483a379988dc69cb43e5636e74c6a407e8ca8bbe69b1db12309a26edc1dd43e19f2cf80dedd303c8e65ae81ae08fb304d05b6e3b1333fbe5c2bee7d2dbe79ac766bb0d535747a472604a04c00450a617489c1292c1818ee29c6a98308aa6f64ada19630f911b3e695eebc7eb57da3574fecc9a8acd468b8a8a4a0920eea90b9d5499e0491587879095a14450720b9d0fc1283300841bbf810f31e4f4bca1f5e48ea6341ed8a31a90b40c6a910e00760f297a55c4bbd38517da97b42e893bf4607740be9af2d59fc43d19f87e532c0d1149a51702738d0f6847849b77c481cc4582f3451b2e9bf69e189de77799aae9c3b128c0d92f08d4e315b3cd29a3a56343c31265763e21a56bccaa28171e2dee41dea6b6d6666cbbf3a0201881eaca8f0428ee1c55efd335d3ec7cf2c88125a064625d64f90fb738a48d77b486c8c3fca9a2ed2387771e7b769e69a2f6394b053c7782167842834622d1e08ca1f24cb16d5a5c0912de2cf7752d6bceb60d180b324e370b8e56efaedb2fdcafed825536bfa2e763d796dea2a3f1d3e984824cfdb0d1a48c2e6c18a3cff6d0efc777ceb3edc71f099640628bdda7b6f32c0a199d694801465b473ab7e74bdda9a77610d3fbe610ab9020e670309a951322e87b3eb99b0f84a9395804c23e14facc5f2d2c595fe1489f4b4ea0d3e98526fb00bcaec8ef452aea9891a38ab95db264e8e718f974e6d6be38f63753be2b7370fb1e97c88b96598409dc9fed876f08ed2c4966504ac38c0cf52ce2a852f3101a11f63964fce7dd20221c880ef434ccf4b947b567afd12f4ccd69a34479bbd69885ce4205c56d5007b6b8298fbb94f6b7d9602b6d733426c96eddb838fb4404580a355388d219e3c88bfb31a71d3301b080127c1707bc4b360c11d121a8c5d48440a01f771bf8d2e8f04810bd0affcd62dbe0bb7ef7e1055b7f4416629b09472cbe6a0e1134afba5a80068396123065da8b651cfbf62e1ca3371d9293af12de6d8c9d4f3769d39b23d174326aaef54af01f319af9f6d2821fc0e738c0071726209d26fea1af31d7afc611628b9017a020b32d7e6d9484cac80f89369c70f48d2218dc08bb12d3b16c93242d72af5991dfe8e486c92d2b8f36f698a941bd9bea7975f474e643263287eff51f9d438c25029dd1d9bc6b9395ae8bde79ec86b169d41d591d73456302f65822418c703e290f32984c4c654c4fabf839daed13edd7b12277ee3eb73a8d5f1db36527063a2bdb9417caa50bfd93a9682698967b0666085b5c8dee0503e1dc418805865c47845fb6de15ef40cb2a5671152e9c70ccea1c60afcf7683daf49c9a3dac1e60401a30ec566c00a82a2284a968366b8c5d83ad1af9854f00868c0ef3aeefd114853d191db38963e1843d26a8d08edf6b884367b10dce2bf249e34ccb6ea010d1eedf34fe86ee2ba442defe6a1b745eadeba38b44fef1a3aa57824d8dc1b10620c07a8d4c0f63c376febbffb527c91605f83b315a650afa999cbbecd553a79540b3998f28d2cb84d4f553b0addae8877dc112bc927081ae1becf37caee8beb34578cc4509c89434b8bca9660f8b9f58e8b028eb6c6cbe0a58209b879b98b72331d7cf603ef84108bf316574928ff16a2fe498fe67e30e998a881a37727bfd666fba2c03610fe7c71ec2cb4ddea726551f388cddc530c3341bde649aff3f57baeeed81fa754a1ccd68c4d850b86ea669628d02e883fe761336a1da41d98f4a5ba0dec5330070fd219a3f48cb767f1506b7fea2f2be6b1324cffc8ce800de4f68f8ccb24d030f10b1ac43abdbd37c151e4a96a86bf7fb1c294505c6f0e749282adaab4b0152b994b49ae3b0f42f7149f138e7ebd23fab4c3015ff55653029bf6231328c276f5493b5e98ba50d9c8784502365e7009fe7d58f62cbe6da64c51a784ffefc804cdf7173a5cdc877b904c68d374cd07e8828d9f0a77df9012e5781a91bf0bad69aa1112c69b805e3034ceb7bafe296c76a33397cbb835e9cc273b73be7593980cf6a99a5a403e870d7ba141c4c015db92b428c894f48954dbeb8e6a5050c7b1f96c3cc90cde317672f6f2800c2d9b5a303c544893fdb3869b197a9bbdc014000fd6aa7610dbd1bc788c4f93fe60a605c1db73af926a593358501dc9ec9497fc639ac322280ec98ba09a3ca4dd37cac9b2eecec9a261978c634c5b8638f81754d7be83504467c50705ad74bf8cd0cabd08522bb2e10fb1d1530fccaa93024d2439f6722c930de0c454a5e773867de85a8a8f6d12e34bbbb727aeb05e2420be209ea6ad293a914dc6cb4a6f41f52d95811c9c411cffbc8d78ad145d23d001a1cad073560babc9278c3ca8e738f8e984cbd2ccdbacb2cac6786ed5e4b9b59ac37bb9711d51ca40f974209c7d5ca379e77ccb36b95c9e8403e734e0ce63c589da736bdd2ab163286c98446df35feb53b5b61161df4ab6e5aabd47a735b22e47d5567ca725c4782e7e36d7a723783cbbc0a2b09cc3a2889f88427bd0ab6cb41758fc2988167f5cea05c91e00d91f80f5b173b6f2ab509891b8d5ebb1907e36e3d8cf477f562dcb8abd7275f539ef7faa7baade056a61b8ddbae7372922a0f8727852e081689f0a03f388fb100e5cf3062afb5339e0bc810706f3aeef0571c7e3c3dc672a206094c8dfc8b55929e63f24ac40ab7cbe24311ab84fe6b2f926eb2d4f69d219884812437e4375aaf105e84307b1d06e2ca6f04b1da4f935fb42a6cd0424d60cf9107aed09b7c1905257b852514ed7ece022fe4e805c09bf5a43022ed7a5dc401acbabbde7a1da1341173cd669e6669288e65f11a8119b4eb5a36d4ff75429a4b4ea78e5641905140adb5171360337292dd605234392193b4a3bd30f5ba48b0f1b98e25a36f910561aa8f1dd5e3b5221de219924eba84b24f50cba53ef3328d5f3237d33c76de97a425265275097d95fed23b900856574d8e648bff82e2d84068f32f7d2b5673fe43499453506ef57307ec02ba2406abc6d004733328c22cbca945ccaa49040eb5adf7b2948e4d86f9354b2f80c1feed4c91c0dcdcba427b7b6e047a54959dad9e29b104624fa47a652da9828e4fa754405295da4872a84e6e1db7ed2387f8a92bbc626be51a8f75b36f043ad78f202b6f0525f77f99951f83d580cb4c1f47a9f4c4918d040b059b7068b9f931aba13bcdfe8b6362f18e5b26157f39b92ba59c1a05be30d43e1be261e5db6c6cfc127f9fa79415b9238e0f3021d5801347f5041a29f1913eeeff1a99b2b38233f73b14cbfc8b9ae968a2ca6d1d97694b1264e5a37ac19cb0aa661470fdc7e3ef1ced59a9baacb1939fe4dce6291b30d4f07d3c3af91567a7c15cc2724a3731d6f21e8a03fe00ea47b1c208390d9a518abbc97d65b92cf1fff96e600f8a943745255a8b2fcbff8651e5141795b04294ef063f9dd76bd37dac085973a49d37c1603927b612cb5d9456de9c653ca04ce5e6486c61d3345b4b817421057aebb366b17783ba57d7d2b0e6f377f459bcce8d420e12a6532abfae995308f7feb17d9ddc7faa67ea59b64690bb7555fcfb6bc5112ecdfd55f5a3c357714541a0b66af9926e13ba169b8a57a5e1a59aa67cf192f2089a7261262a3c4f62723ccbc579fafd978af3ae674337a79a345ca07f20e6b128f04717e246b99544e16495c1c3bfd15c8ec092f73fb4ee9b0263b97295f09109d56b27e80b261b6090a6e908e5375c06bb69eb980dd2df21d1397fdb0e300b5e9f121b85ba42c8cd1bcad0f470f352349a5249a5ce7be5891f611c8148406b1c3d7350922dd83ee4613030043a5f8b2711982a9646957fbe3fd31ff6c07ba460c6d1a087fa1b375b6d49c115f4d316c98899eb5a3ce7897ed0a7d7c2f394782f4d4dadefad54123b8e5462d17a7ae7c01f51961e4acc93aa013975c3210be430afb67b00591e815a878827428e8015f4e1427e35f7b48e92205855d80aaaeb8ed3a54e9c97ec02301693a30859726b2908bbac7412bc67f84977c7b2a822cc86ef258582a5764a398741514a9e8c9342167d90336198297e699a0c07ba4385b0c23e78cc878c00e760821bf2b207ca15f53c73316861e40ef4ffe055b96214d64a10621cae6047738ec4f1b71d3256d69b38121badbc1e2e0c350989f861324b2cdc26e6a1930326c73f82efd570b41a8918113ead45125107677eccf8239c3fe5d8562b7f9d2448660bad6392575964798af483f546a6aa87a2f0e3bec5a42f0f862a3ecc9428782386442133046cd1f2353106f2b541c000b5e2050ca87a5ba5528cd3e0da025d3cf9b49bd078b6fc477ca9a74f976c246bbe219e5d9b00866c2ba8be0f14ea5c38a2b307f1729ba3aa14b1a7f142b4a8c74d03dc914fbdcb9a7d196468c7a50ef0b594a5ef2eba74bde87009b0134c3b0c7b8c329cb46b9e483777e71375ebed7260572240ca3cefd2328abd7842863c2c27902c0f407c8c647e4764f3d8d344060787d5ff63d42a97c3298f667e8fadc4c6bdaf9d74f458cf88283393a7e709fd2182f61e536a90a5f645f3b3ca00c358cd4924265e8d5b53db5cc5ba32d4138d939572b5292580f595e582cca6ea7daac17dcd5772abe9e522cfdc02c9d8adec3cd8029b050e0c0afba63e523739a24774be19013ee633fdb6c2220dc03ae5264287246190064ca44983d7b060038785fa70c52758b23b387c44f1868ce5008155ab22a911a03ffca11dc7260a8032533e9b7a4df88b17e88f8e5f13efecaa56017c9c11dc88c1cbff167b18a5fc20fefbf013a26c18ec3937f6071b78dea532ba3107e49341ca58d3621b372afd98ce883bea8a4427547e9d6a2328b4a26bd88c528608c80ba40223c2764913add97e01e847c2171c61f4167cdbfe696ce3c4eab6c43314e77d7407216296d0751ff51f283a7cd166c9c65ea9b4c822a5c85992809dc8f6a263b48b9744a1e7ae23d051de42964087590bcd3dc715c9f544ab32d92a898fad51a0fe08dfe881dee21425866eb53ba262c6c969e72c112194c708c300004b2a5ad0bdb8bdf7e8650c49bf2a1df9bcb24bf701d2445c1e650243c5cda4c380afc756e207fb31bb9067ef28818e2c8c2ee73c436cc6cb8f665dd1a528653e1977883a963842607a1c398aff58441bd2239b1c20ac25ffd56cc03740b5595943222b51853d639f03cb1251596f41d919cfc65c0d8a539ac659fa9aae48ee976db1a68ac7b18e9f88a3a431de54a4a1abe4993fab128213ab90fbd12e0219d50cea25b7dc0b08d9bcf06d3ac36c9c2e671188e21be3c844645b09a1a55a5101bea1fb9d8752ffbda71b4cb6b9d01392be33e7df43ba4168191bac3b3a4b6ec2264f5b9b2daf709ddfe698f0e70b1039463138fcdd3f1c98a0ab7f6fdb1703dbd7d3ff7f43d1071af2090d323d03615b7d25d000811b8791be1f83447f0b9bdca761b8810e96787008f5780d94438211df3ea19740052aa51ce2180fd186843495b11de24d9010306a485dd8fa90315dd686437ceef8a05f282d2e89f69026711462b032c411b51f89cdc8ccf5b28fb3a25f1282b8582a027aa9fd9d9976ce265a92be4e0f91d4c930dc95e845aa6645d252dfb616494742732dff45402fd9419a791fb37e8c1dd4531c5e3ed068856a81c6eb013c96f4670aeb08bc0c93606e8527811617d6b53a9857331a2edc238f9e7e5e34c470ae2a69719b5b1aadbb8c78d6ecd3bba14d8f3f06a0382420138123bda7f97217e1fd9cbb6cb785af0832c9c9c1741d8ff3b633dce14cb69e9a54a07c7b9e6a02bc4730ccbc02b877f9af468854354ae719a31345481d9ae5af425e8030ea4edc4d768ffd039c7753d465095a688cb949723bf53f423a8d2521c0232473fd44a86dfa8873ad471f840021b306a0931404976623d2ddcc18aba76fed80425d5f0360b84f01a6c55335380d36f8266dbb075a5e7ca748aef8b464536fee9740a76273b3a7c59f310e46f2bf0bab0c26eb70bc8d4393f3e84c05f09a870efd18ed70e96976aa65872f14378d1c01ada302e4b3f093a51df6db75b283af3b05e5cb48192cec5d5b053882548c837079ef2d078c4be4d92ff6a9c7193fae763425e698891169c2073b37545b001db85047105861e02c0c477808c7c2596ac8954aaf47ce5d7f36d58ddb5247346db00aeda72f1d1a50c26150a86fbf115210444eca6f21916887182a559248e76f9920e1c2fa51346170e766e66b682d78318a5639a0c908fef089bf9bdcd3e69e2e039bd694a896ff1b74f3d0630cdcc55059e3b01f2a034eb753202441564ed363cdfa621e8c5dc4c83f9623adf8b4dd0a314b712167016d61e69b1d5edec8559360673d467dc5859c106dfd6f989607d962bfd110e1523c8d6b400a32a6c5d1b49d96e5e06a0c47feb80224d753441370c0db8b7f31f840b21259374baef3bd3834b4f99f5e6ea4f511fd00ca840dba44842f3fa83f0ad37a46dc4a5988722c8ea1f340e860274506906a2627cf2444123b107dcadbc3a748323f278028509b5f224bdfe3c2027a11e9bb1af73a2023e45bfd6ab7bb7d5caeb260df67fb8530b8ec8d36299e97ea50e3f0acd73fa5899b9a406cc28df7798f2b401bf5d39e086a58646953f4f64c398c52e3f9c18137c6ac3b8996e7da111ccf83ad467a02f3a01944bed52a808e84c1972d743923296253fe72893b536f394015b4f58a159581744d06638c104699b3d991332d159c302d1b5928e39eee10ccac50980c490c85e4153d118a7211777f482d77012aca14c72022ccf5c26af7cefbf50499e632eb4e8ab2d76b1d29c3ddacd0b5bc1409ead1467f1615dac21d27fdfdd1a150caffbb2ae2ace49754ecd08eb1721f03ed448895c1f4486fe8539da9f188e11d4b11acb3b46beac833616b4038dde18c95075c0041c73a079a2e30aff410a94552ad828e79c590e4b1cb54a201e2dda91fc328a8017163ab7687fe29d0ba5cd98efafcc15da16985683f8ffba42d782c6398997e77d4a09bb419cfc8352ca9721dce40689c7707c8c52347f18044813085ea552131657cc90b66111cff864318af67f4261a4a335ce204e6ee2251f812a80b70faecab4d0c0adccdc1dfaf370c411b0e6d8b6843287e5ae3d84dd99670d90bb2f2b1e9e327bc2e626b52e4d751ef2c52811de5d2e79e3e4683bb327c090b684dec8cf2065d474a27397cfa1a7c019888854f8c306654540f0f06bb7722ccd7c4065b015af83ab27474708ab368ce4c5ecf3eaa91ac9a58cf07f7fede2342eed11a37b0d12691e5a628cb602b94d7def6acb76b01dc2becd6b07c7c3fa731b6747cadfd449f8dc1661d05c6926afda8756698fcfc0136a65a04d9d20350719fa449fccc8cca2162eb02fb0f15187eacde4ebfc7d8e7cf79249eaf65d8a5b0a3d1ee1af4485b44d5540e41c41eaa86c3ccd0d035b939db3317df54791e2e91962c3a18e4310992a147d61cd670eaeddc3f2532c45779082ff25f6081bdab80cf15bc4bad84d776a5b4cfb1ee1fca09941a81c4736deee8562a77c695c5195d713988c55c3878b2865945ad5abd482da30caed992c202095c15b1190e778d786ff3598781e53fe6c14bd82b431ea7de4c7521d07cae6c2902f5fab83793a441b0295d41c77d0bba91a8ec6952bf0f8f13ea276006710fa9f3589b2a2028e1d2be58b501d517dbfbaeba8e106a61bde22d77aecfac16bc0af87cd2d9c6f3d8419f513d336d427337aeed32fe86e0747f9e78bdddae1ee8279bb09884b495e8595084be80c416fe822d40922f688d372f3e383cb39f6ea9d976a04cf5ebb32156c0177d387d0c8093068a6f9336c3d40825c12fe549b8f6cb6624f9566bed6abc94dcfe207af48ab13488c902f6f4692bd78dd83a0d332ff243bf7e3a6c3682f987be31acec29f3626c80cef1fedd4d79ce9fd16394700a6cd7a4824ba1fb53d4e00baf2373aa351d89d046145843019406ce9570cada115cdfd49172452fdf15e0bb70b06212b8345a2f5dabdd8f8a45a7b0dc762f7ff55ee203fd0c196cd4486bb60e8c4467af85d5f867d5a111fa3a543ada650fbb0130132df7ab81447341c96fae088bcbb4f27cdeac1c84b72282249459e70426ed39c6c765bca5b0175514f21897a2b2cae759bd9e7e1bbb14da2e0849d1c9bee2ca63d97d3a41a12bea422427e5f6b9a497d5c97e83e180131d6919a11eeef640216eeb56c0d3f601e4f38aa519bdfd54483e85edacdfb35d388512e92d03aa44c7a58138cbb7614f763d7c2ed112112c6b3517774858548346cfbee6eede6363c41c25401b3098971a6c750889d0b21b5895bcbb74c52f82342f6969f03350073d045721753d3f040f4b04a73e8a16562a8db0044f3486336b6354d659a6bdcf31925e12e2d1c3c01610c11a41c7cbf4ca153eb50de497fec4deb4d7c581fb7ca2367ed79ab1bf2b8a6b09cc758121a7267aff483eb6bb3f2a98670b98d585466967de1a699db56020cd7e693e118b6889a02631b5f6fc0a9926189176a11017154abd4fa6f442769680ad026e488d218ece50e015eb5a16a783cc36775cdb638b8135b194fa60282bf9c5a936416b9e82ce02029cec195aaebf967ac4cee09d41f10cca3344cf542dc057d9939d78347724ab437f5b1d9b3328cd80b7d1547b6e632b712217485638d324b31260760a3f9fa86432eca51890c77136b325660eae778cd47e4ba62e670d7634e6e287ca98875c86fa66009e636df04bcf142249a91498e3f2e5121d0c8655b1abb00ec0753c1e399801c375ff03f4e74cc82aa7280b35520d18aa2b4226d2c0d01565a3f16d765c781f3482031b6830160bbd73763bb91b27ef899846ee13e8f40cf3e4d1c23de8c43c7ad078f43f397364db8c0fbb46686268c6c0dc0c1846f3bfd0628b3b26aa45382b19d48c6b597e77854601b814fd51e8d4f152b318a3b7e5a083896f57d9d207a9b19050120e0e0cbaec18e85d131c5b23afefd82248292dd4084b384282f2c055c5be1b0fe557dd209d9a35c8be676db9b428d2ac9d1fab51206f79c95b711de4c8d0c5bde06373b43d1537b80a29178559df4c79c98a69666b81b06a0a2cc7a196190260bb9c81c14e41eb665581e85fa7fff749a5449b953d55c50e110d211bc69efa43a08f9305d98cbdb609c159779a12a63d627d0427bb493dede741366ae411a3c1b502c81d6f085525f958ef2835ef26511b2c2237079bf848fd002b94a24f890538b11ac3775701c73a51397c4f56ea3b3e5a7c54b71d8734f8619ce709008ced47e2112195dbb90758fbd7341f7ed4d71665564ff5b4cfa9780ab2da340be803c1945e9c321145661137f14980b261e415ed418308ad76f9c268cec61132ced09e8640acac29397fbe5dfe95fb98f8a878afa835308fb66c4d126ae075e00f027da9e7ead9d98249d514b852f4974a382e82d1f1534d4446a6b25a3f3b145774e089cbf19c805dbf7ee66a2ab43d1270a732abdea9b66e51a96c685a93ca28b170837c9128f42dca863cd6e74745c8ecf561d78a3133edb4c4cc0ca7cc68823e90cdbab636b7b8c80369380f881979db35b5822e4f50c404bb40219cbbb04778d88d2570ae023bfc647a5cba86db5746eeda98fe8a2ce28ad8935a7241f9ce05a577f2168d76da9c1937f3d540a1e0d182de40550c4749d4076917f2a3706b52e251ec9dcf6a6a311049bdcbd22f7a96c4ccba510ef3dc78c56399858f0bd6450dea515a4bfa6e21dd492aab6554ec2e40aea2a3381cfa799ef08f175d75289ac6170fd7d26d4aedefbbc0458efed590c0d13d96031d05eb87a67e99d16e64e7a68ad65732aa62c5a582ff9aec8b0bff965946d85784f2030eb2db858ed8b59d7d41cb8709b5e7288fb1ecaaa4315b8c1d05499ae6b6b4161bbe90a6fc36ef3feb1408cf8ecd8ed0c35c8e83518147aa7c3de09328f4f0a4df6a07cadf3ac35fff6874a0ed4a389d0254cb63721a194ec2956c5dfa42dc051f4debbe70372f0fa3910463da35f38e506aac8013aa349db71368c8fb0375076b23f5640390bb6fc869d1ca292dd5bc954f9fbb1c3c1d79c3406a87935fb09bc842eab9bc6e09b23c207e51ec15a418afb3e779c6c2d31a8db137a93579d315e7d61afae5841f7498832205b450664e63abb34240f227064a3dc902db280610f692ddd44ef9bb3b4c85748fb736c646c2b8621794ec95998df4f2f495f3e3d8db1f2c2f13a99e0171eefd8b8af38a33274631874fac24d5f3c62827a7ce78f9925b37d850ca146097fef33a94f366899729fc2e362516e809c40ab4ce43279ce7ae891cb1771a563e3f45817302c595554bb8b614277f5212ac81783d1fac7a93600825656618fd9a88c6e5073c794f55f91f3fa1be24dbe753f83d837b96d2cd3c07fcc7fadbb538f0c1ddee7b71e2a3dd91b221cee6709343a36b3e25a0891607df40d390a14b36932de04ecbe102e7e71fe2f1bd1a45aa27a16a4e304b5d991df0194ee51167032fd48125798089dc10ade81e1c1de1061ce2b9656b6ce73ce00a09b702423565ec0c03ef7f96ee06fc3ba54c242afbecd73e263df497c22ee1b0268d80a4d046d214727d11d94e34c125e758a647776208915a9e0ca29125b4414b046983863021d9520dd1b6a713127c3aa92c9607d31125948f60d6f1686aa8669d7c85bba8f530382508d2826e96dc5de43e997956d175ee52471cbcb33476b17e8f56e2f25207e5c4831505f0cd6e5edeab5a547d95af92f4d829f45720598d1a77fd754cc05c226bc784f28805166add562479a1ca56270b200645f1e4bda3b8d3e960bf8a85232270b4c9869b2b5c19530b00e14290b316a902ae80f2b22cbbb5420ae4f3ebceb503ac06adec7a3e2a73577cc8ce7c9e254abcd1abcdf8f1f1bb113316a7ebf651ac06c9ff21ad77cc353fbfa1c2c14ce3549ec562af5e3fffab56e73a8dafaf150469275e9d6abeb8ad06f0f3b2bec6935115e49c577e005422009ed03f47d02c6c89ae16fe88080fdae121dc88304014017a96e2ccdb8bd396bb544115a54a451176a7a597af826791aa94339f1946f5a6f80605e15835c70ebf3a78df2307cbf2a086ddc5202b03f82299eadae60e741428a576eea6b17d9201af0ef62c42fc21ccb89a1846ca6624dd275cc76600276e15c0be28710fc4986ab8b8bdcebde1de8e8bee7c86843b64dee718896c3d300f4b773d7645f09aa8e2e841baef3822c752ad4ecccee8e8f8fd6f88bbb6e1c1bef6f2bd94fdab1feba105d7be7b1ccfe1124aa4ccedab17f6c23500fee0cbcbd510a5e2d18c384d850bcae28e6a9cfb43c4271abda68283f1a2b4b605197cf06ad8404cf1f3229cb50165e7ca6a7db6fcd031c0a5fc312175a55e974b9f7905a709e57e78b96bc11e8246b6b9a70f5befc45492ddd2780fcb8a4c0255e7782235e72e29cf941be1f65a5a2c8f9a846c3a18e41f06d01819a89358ef68bcc801443d764e0d8c4e846d5bb898a00a4450a12f3468ff660a50f2998a463c8858326b79ed554506d748ca0d627072f2464e92ed1e8b5c7b4f41e27fe499a8c60d2f1543ac4c2e2118ea2da45d896ea746e5c28c24665a28602276660147fff1ce4b33031f2df7213dc49d6cce221a9feb63cdbd8015c7522275f667b500e48231399aedb69e5c1a7308470ce98a94c96aac059cf9bfff14b9e0ea8e2646256a573a9172db0eacd28aa88d912625a3c8de69adcf058442dc79632a9f6809598f6ad8ee97f3fa25bc0f1191bdeb3ca7a4043f2228de5547e27bba57ca14aede788d2b0d7ee5b2afe3948e99083609ce3c1063835850c1d3131b74be3d8cf46f262c9727332928a4ef5021bef079b62828062c87b54a8917ec1b2ad9464f4b7a670abef85d9507903bdf30310023c48e0cfc181903a50598b984188ab2593057ce177033302d248dc69b75107b0c13bc8eea356b53f451d91c020b83d8a323205a073195f0d38daf0d0def1f5e0f4e6c41e35c908304888fd2a84a4e3612828692a5cde7dcefa01cc6f76fe562160701721b72584549f642d9082e63bcca902bdb6bcb86f8a48d4702f6a153366f61948077a3c7d310c73cb190947b41efb7d98c6a0025d34ab7072089df75001cacfd586ad6451b0bc25c3e901ed8bfb97d175204167424145a208c721dedb57872fb0497c76a29595ae849a5278c0a13d8b8cc814f16fac4aae40669c08a4271c02779c794d86a44a26bc232179466d8549210870b4f05d71497876b8ffbe0261f8c9cb4d85f71701f684dd1195e8593b64b3da5ac55521cd41990ad174edf62ce113a443583431f138ee0654866807af8e1db3aaf6ce65c6b0a09a03816e26709967ef71385409280a904a7c4acea95af78685e053ea2383ee3230e4a6e8fc4fe07f803a56e12d676671461a81b794561394a22b198bea625963ba81a9799ead8cd0888feb65821662faa446d7c6fc3011f4e89fb4f30028a3aae8a5a552a5c5249ecae88a09faa5587848a7c5a5062c7534da8c213479811061702e80b24606e3bb920ada8174a5611f3174009401f75a130a7f42b867e95cd1223a39b00b21335accc1d56ac5db1af400e2270757d0dec4f999168e5ee1a9c1576b1aaf1bc726f2eecc2932cd07fdccb3dfef78df23586a52e2eb41017f03d9149da950172d101091899840ec95e2e6ccb37d55c933b90f2ebe7ba3fd21049b9e98a7d26c413b9192ad4edf40d1aedabcb896fbecafdf9e56ebbfa030cae8e3fdf2c96a2887a377649b173cae3bc4737f46e312b17f2fc5ac90ebb1410cf091a81f24f0eb33e27f921d453efb4a35a559eedecfbb499abf223cd2f126ffd4bb5fcc65266b1e41b2e70321447252be72ade795b4a1935fc263c880b36310d47f537bb72aa36cf0ee78f547a68505c9a8b3b0008310f041078b36bc6d684dbd59fa6b1a092cdf2a700118724503908b564f33da4ba94bbac2943a06b5489c309acd3d66c3c67506e643d9f3dce7271470513585e1d80ef6eb0830d6c8a58f8811f6c92aae2ee9eab4a506e266aa1f370341f57125e41091a2b041e9dd5c341bbd7d9cd952a0328e9e25a0fb280ebbbbc5bbbfd19b5a4ec50242635516e410dcebe7707f63dde80d15b16bf0778c64c16edb6fdbb1a5acb61f096fced497830f83d2f96882d1d6c806634399aeb8753e5b66d933e9c27bb7ed732e859c9c328511023442b651dbc91ed0a165c194bb312e78b1c25a32c8f1cd6f606927498ce65aaf35199eb9c335b46695c6dd868596f865428da2a018327d2609048e7ae78a45ea9941b07a0bb22b7f67f55ddc64b746ecf7add7a4ceab36b398a957d855024fb6fc339221adf2e6d8c465c22d8672584aae994b900a91b85be0c41a172860c428b91e9084b06c77cf2b74fee71c51b9e3be370dffce082573c74c6e1bef9c105af58d0fb96938c81946d6c0ecb41cb72c1d54d6cec2d52e54c7a3f85f5db65d7fabb68368f0712d28685ea4ef805b83d636dc1ff020e6131c0672054a931601c1412afb3e16761eb11cd5f65242b96c2789d83d95c7022fb96e00c6fa61e6c9256474f9fcad2133ee31c60e227643bac1bba275806290034ccc76fb8806e9e210fdd3260568cd91ebec672fb498f302ab94b6c4a00d1b57198b7f4d70574a97412acb9e34f6480079e6c106ccc1cfcf98ee8aaae5c886947e7d7aaf7f9f6ab0b5ba1a965face16cb68610c8426049030ef6cf13ef33a6b136d00d0d90c95b480041510e48d99db0b150bf2fd38977a5a7ba3da4a5a7ced344f18382a9df44eb91199dda3a28bd9008b612e2ee9982f9a36fb1ffe2ec5448b6a6b30f433ea0ea67beb45bd22888704a33da3989c6deed665b0a2b6ded7cb6ba95bfb519e3362682c60bf2584fb234309275a8b89be852c53f05ebca3685be7529924a0699529a2d2772d880f1249dcd61e9a785e14a1377704d97fb238bfb7d4613120c81a282d215ce3e6ab3c100e44ba1fa504826b651839c6eda181366d653e630e0c968bc897541daf4c1c520953bf0ea7b5aa86f6ca53fba0f592dad7919296be935e7dcc9c3a8ce3b917af48a0a142c3f0e7513a9fdd805a2808196d33c7765654b6b12b739d699755cca6935b7e81a88d9ad786c2b08eb442e00cd83f948763b05bad5e8f2d79e9126f7481077cac81fc5f92905208cf58ea4824118eb0e93723f05b7cf00431f59c2a3f22cc49b1468a336da9beaa8ba544336182292c510483e178d5f8f9437f7a01485f4941552fe360addde0447fb23008785679125c88826e898dc892a43cbcb8bf8b14879203e32e6854df586c45263d1fe800c25cbda04558100775276d6765e100f38b451be375b7bdc4247988ebc3b68251649ac90dc35105c83914d31d0e22b5484279c3df3f2e56f2431400074b633ea17e17a4ec5c84543271ed310ea0d518d355395b36f5015368a588ba2510939d02bbb15153370caec64ec32de9890db1d0644ec7a0eb8019df840958b1f8b95a6debf9c989ba1282d791a1c47cde6cd2e991ae77ebb6fb7c4bc3eda6dcc3b4753af854b2f30cdff1fe125184992e31e6a4715231220af6ec13568bc9dc6704fa84434141263018f889dc2a13d0d65b99419261f4585a800a47d81b4e92ad32d2fa0505f4ba9f76d21e31b4bd01515c24a593e7c50758c9f7b48db8d70724fb9f455f6864b2859322a54c68e158a1c6c79e047ff63a1c00a2a5ee346f4aecf13b50426c98e71d09831b146ff063dd6c89e3d5a88498ce1bab5b11b7065abc26d7362208a3a46f2accd46117c77e698b61a990bffa0c9820e14381955139c20174ac48f220d3f54dea53d3309c500eb126e9fd056d52a946415b96abab3d86e22a45e66cad1b9922687ced0a759f95e60fcc4fcdb03df6855cce9870bc0705531fe6fab1840b99ac7428261e00516f9ec3582dcc6c47a027b2c2f2a2d20c8800f75e3eaa02a45548188db969a100f8ff40d7af80972301efaac95425f18619334f6449a05ff3afaa2485e5f0eb059c62f9616d6ecc5803c2e487bbf892313d47fa1f325e926800d7cdba827d4c24ca87f258b348755957caea008099cbd5970ee32495b6cc25408eaab75a994093009e30ad6b2d5489ca445b9b2d2aadf46012569d42d10c43e961cf6a8525e164e40cbc5e7593eb5d3f85f63e7d5c9f119582f5c2379314166a6d6a98adbac1b648aec9000b0abb286cc2561dbdb4338c91c13d2d431ee614f58b78ab2382c8983eb6cb9a7dcc1a4f82624b23cab1f984beb65318854a9318093d4f087be47375d330ae38cf923fa6674db98e58de480f0904093c805d7e36720cb04dec4ce38fbe82ef69fe73556c61456b10d372120f4b90f567a9e1c8da2fb363c8a21fc0a8da1b49a87736e714d63e9c393eac7c4a1ee085baede061ba85cbf92224c304be24ee53becfdcde35117d316ec6b7138ba87351a1b7a043565e432809adf6f471a3182ade2fc0f8fc2a731f9d2dd0a269769da611089c21746a00760a176d2c9f0168b1611a817393dcaefdbba4f283b20c0d02d7aba9fa3952818600bb55cd11b3457f7cbed77eeea13de04137a0024da2adfb41b57a9ed40d05c90128a2d313b23e72212cad6fbfc1f62e591a0385981428dfe4e06b4c21181fd6c07ca44244357bfc104558629156bc8db522d373a18f84705306ab40aed49d649798de33391a01c9379e3b1e38913f510665f2a8d387858342d3890454b0b664c4b6e3406d4f021923c3fca9d9e96543c31322170a00d663b668dd33547c7b0811c0c87c7776f71e0abbc4820984618a7b4bfe30a4ac28112bb1dab4fd43fbc28f1fe779fc8006091765d43115d4db204bf90432d5963bfbf569260b9a02915ae022b8728b92132f01fc5ac80e338d36ad8c29e0409b9bbda781b7a86247630a1c7c35180e903d1a29a865864ec5373a8bda333abc70090feda7479a0d842e2d5d36411a200b5340b3ae3b46132cd3a0d7af1b2d07c31cd99855d186b0aa5e0315fa6504c2efeae0aaf81b5cfc0a25d20374d545cc947d55c4a702add29641bdc97a64acc8d441a00babffcd1f663ff6cb84451098987ee862fc9c7d746711d414e301ac4771e62d7b251c04f73d655248dab1c67b45ff2618b9f108ac4e8fb2f65807c9e7aadf3ec81c8214f24e44f5a531df963b2a9e0cc71c39f981f4d2a29497ca58b8172d1a4fa80b419f71a9e3588dd5bcc3a35643e3b221a2ae8b4f639235bea2863c98183ae73d1044d92e89e6dc60e4e008129ee0e91133a8c482861751d6efc18c39f41ae2b385a1e53dafc4f9e041826cdd89e36e3c0adcad0ef66bc6ebaed219c7eeb80b24213d4a6f6ab23073da0b81239489073251ede98a317738d48725ade6635758a7450579b4fdce6b8c7dac295b00dbc2a5a520cdffc3b5568e7d66112ecd18570e2904b285b339c6cbe2e685fca122158f90b0bb09b3c3c2c47e04f3119aa646ccf82619c6dbb162d0b2dfc89f35447dda833afded033df963856f82351fff19a8fcf138fd75a536a20cd5a08d4a3feff8e7109272d04ec9ca59a5028360c7a8dd32c50a3b5c04f9568176b29219bd1b8421f87f5151385368f5c43b4c8cdb4befb25eb1222b04d85c2046bd25e815df3ca89b88c8c6088287cf07112bff491103354bcca2a00c87154dd6bea9a5632e9307e26b804807454cbbb87eba173b185addf07c7038fcb884c175e42dfb527e12ac1991165e17876602622861a98275119bccb60a296dd9ed2758c924def83d68e2285f307cf4079cbb6e2c258492dce437a532838ffdd4824070f8fed8a14092bb70f1ada0ff5e4b8dc10c7e36a4c2b3e95c29b4178c7bbb065d3e6818acf4d85d6d29a80bfc28469052203d88a6ce7a39f99bc023620437a6aaf586635037220d7849370838386c8b2c69003dd05304a6199d3c1681ab4e73ccc5c5aef21d4d7da407357b6d24c0c6abf3193d115f65ca32e96708dcedda4aa3f924bf6b5b2f004af4b8d597ba3de3858053d394de9722a3bff2afa46f941868409ed08ee31d1cd58d8903f28307738824c84908a035f04b0136ff0fbd8aceef4ac645a665729802947bc87de6b6428a4ca98b0d370c6ec1a923c0da534d790e96a3f144cee13d9a598bc1cab7371102e663a1daf73d7904a787e8144d18e1f089758404e1fa956e2ad5e11c3f36ae7d22ebf6b43f3c824308494ebec9134feaea8d21408347c90fc7920150ffc34421ed926e309a6899071b2ab40d3d9ffacc4118c85829f86f27caee0cf430895f8fa3326b1ca572a612f74786b684e21adedd1c0110e30ed8d86dee260b09885177a304c7b663a1e920b480c7328d225e58d7d170ad551ac53ecdf9929562c4587c9a23ab068e69d2f5b738578a510d5d82452552953e59f44e73659f7feffcd91c7a9f670b06eae68950ff831e6fd197e27c241a2b029383143adbd76ab82524ecac10d107f6031a5727bea6c40be56b3959037ad25403f4cb1161133f3dc8514d3a1a46d08d3ec1eb0da79bfdfdef20152c8222f2e57488c937014fc15a1eac565709366026687aadbbd55958b9953265305de202dfc9483eb456e40551b1852045b70a3067ebdf5445a12dad6a63beeaeab4bc79dd1857263595ef440fafe8137175557824a95ba84ad71c285cf2c79d683fde26578f556b1e05b3034ce7d15e9bdaf4044724d2d986028ce52c9396d4e28ed0481bf86d47cb2fec0faa870d08f259dc23f5202048d6f1a700284447ae18404bb024678309de40bd202d26a7322eb9670770069db861bfe33b13955be23d53b81695a0327a1d79573d1b8764a9b0490a0c2787eea3037b29d6e2ac83477839f6b0a280225e8d341ccb708c18a161a22cd6cfa1db2814d0835df1d0545e9f92974fa564cf1f9662d7771cc592a24186b1198273fda943475a0da2439e2d20d256374fa7515c5328ff3614cea17f76cdf92d9c15f239698b87f7873ff1d29ab168a5e424d8b7f6471a99b3e5711db5bae93ed6afae6d231b18e089677164e1d9b1d2089c8ed0f4687f2bec0683481bb2b7c0540719983449a81b628a3fbdbc1152602299995388309ab4c1b97683f51910d1a0562aa5b204be0cfebbeacbdfefda7ac4673719bd2717f2768559d492e4e9983c5b9340a44a6bf5287420eae82502cd11a85b1590201f1264c82bbf9c1031342bb6190e2ec91429b704af37ff68f9eb647a4881833ba0fa1884be8633c8f8091d958ba0ffc2c65063d30175888e28c236e8a0a65a5d6f5e5adbec0271dbffdce13b3d56ad71000b5324cfc4db548c16ed469632b712e28002a9ce83e5921ecab02e4dee2f8734bf48947c7a0a3494e43a42681433d63df377811e87c5b81709b980c51a7811ce1523096a1f26d8be9b275bb4d9780390197751007b33cfaf194b36d4296bd8e50b08f7fe88b1ddeb7dd66c856024706d12bf2d707cd5390d163a7331ac1b5426c73e39d971ba998aa84e52fea08ea64b4401963d5506f8df04dd511747a454915a24b0dccec7d977304f28a5fc730876ce3b9489dc7829769fa129b100e89dfb01bd42ac1d2c798a3df5b936515e07b549a7ba8441b6f183d04af062da256db779fa49a8c04963a7a760887bca84bb9f1a961d334cfd11f3a012cdd2a0b4781f22778b7694f89ceef325eeb4226424da54cf993f7c05af4632afb459b3141c0a9a1e4cdc44a3b63a3cd7cc7a89e50838498c47b777cfc5e390d1600dafb08f853cd6969846a6fdd355ed82d982aa5450c81845ac1fc93a262e5f88880e8287f6f64783cbc440612c8522b54c062b01e44752a41d2a2c7ea301997d531bd4f40b9b31d7009ecd33778d62e9fe1ed2deeb4d9f8746c8384db26782087a20940d9299429d5efcfd07724bf9cd28eb6b4d2cdcec68424e24a54e7f86bb6c9efad5cea0421215300e7d0d536e9a5d068913800ea4060176bb5457c7df878e570a822a3e645f9c8b14d25476c6a930c12fdf6d2c0b814f90b6670de90f969a02f0ced8b22a59c515f9ddb22ec12b2b223c5c8151074411280ba800fea92b421230760e915d391034e583ac2a6e6654d989108116382fab60704e24aed63a47ad7f243442d2b71f6278850b0830ce777b0cb3ea91badd4c0c53a8d1ada3f6e44128e454443280676e4840d38f770d40b3a138840dc332c1412845ca12d9793105aa1247354d93a9b007b761df15f2dc65e74c0b32253a83d29f2e9888d3b45cc2521d1f84f681bd758ea6b38ad8e0e69fcd5fbaffc7deebf3257bb901b214d9c21397d0ab0c5566287d4f0452a59e5f98c263813aa8957faca0c2c0e1f619ec6b60dd3a8993d5db2b0803248b3f8fc02a7f5f7ccd55a17b8bd7a950a6369f59c9d5106d8b4c6c12eb71ab1ce58c1c8476c14ffb008f986dc44b54024526ac74d88325ccd78469159340bc7f77def16f1e78fc494e5162b7977ac7ec08d868bf6b1f1eee045801c5333bf714336daead98e76ff9a0e1c135c566dac2943afd26f4f1f93aa5715e4b1c234b31e85791fd008b99a0145d8ca267d5fb937df14fdf422b13f200aee39c14f5bc0d836c7acf6fa66e5547bd6152ac514629b75dafe107c73a260e2873870bb733bc5b57d829229a1107873b7b2cc2e02874fe374ef711c4296e0ad5c036b0f233da94392991cec6f397bf373ae0409d644f6319296b734f770251a58653fd7a30d62862e50cfb6a985f652cb0791b9b5327994f661206d11d3aa1e9f407a9fc928d2a527061dee4639c1bd5764843174ae440d50b43cebef441a5b9dd26a79ca171210ff28f2957d5c723073e73783413871b8328f71772608c82c49b6516ad8e38a1c986d6a554cdc501d72aa6249ad49f1a75fbed5ad259d7b5cf0553d4ae0f24f073d3f13249c5edfaaee6715149de0117732606e53be0e224269463e986e0f863169959bfe124b54824d870ca44434a67396c3c388186560df3a8af37da818afebc4012fdf6f36c5eb935c54b42de4c087800331cf20824649c5934cdc942133f624c104227870ebf3af5f6aacc1a02520e258b73212766adf33826f32cd6e654560ccd6ee7ca359c37606626e1f8ef818ff43f14f7ad6578e05cc167df9514530be33598c49d21b19e09bc952c4f650dbf7b2339f8040cab5cd5e2df925b94cea48b33e582495149d40ae030052b94522c6b0d96964f5952a2847d38701b3f63d1dade9e696043d6abd88a3650c634294b18102476ab5064937a8f136ba2c0b2366cd9b0904f3677b74209460ddc8fb8e611cd6e850c43702f3333642a443fc962d66a2ad51d36a19c0c9c2f14ebd33162fdd8c2a9a2fec932869e3840498ca4e80b9a1f9f05bd9359977bdbe27135c926e4db65f462395e090ab206506862c69a9919b1b6d13450e8ebba5aec76ea8187601064c27256ffc4782e17b06b482ff28c96e51affdbcc88bbbe0377856b20514f2a7b8e0292a1ab054c98b730fe9f820eb9bad61c4dad53b4a089e4bd62d70db9da1bcd0cbf6a067670dc3436596b1ff2014e98e1af13562d53a6f7620fc762f011602ffa7838873ff534c8c22ed68d920e68e11194136e97fe8670c1f2ae958b1cdca028e28a9e691bee291a12f82f992e8023aaa7a14d405a5e400ac71063441471c61c0157dea39fdcd51c73dc1ee67279aa902569b55329367d9bb6ae737aab4031693ae67831c83d62aeeb489c8fc0c4172b14a1038f79f56144ea8a47b97ed69f4127854e6cbcc8583700540913a402fc3881e78c7ef8dd73b09bf698ef1d9635022cf6a08c6d93b807b3f6eec9ad21225161c695ea7dfa5fee89d273ffffe5beff67f7634ab51c88c78c991ec866a68012b5be46027d1636ccd01dbb338e2c09d06bcfb6cf2dbff52e583bac1552c9a518cb2f059918c7515d1544584d338eeabd8965a7182ba559f99b0fc0a7ae7888ca09fe14b648956bcf1e75b903a2d0231b59c4d5f3383d0c2010a4f54ce53188a509a37877aa3a284c425336d0a6234bb6669d0b0ef3f681aa5dc33e8d6489d35f878e8a383a93e5e664a84d5f4af37aa27dd99f9c673590c038d625538114e74548020fa9521c95184b5dc9bbeafeff92c0636e927a5a361d3280b7ade82cd12add5a190c4f01ec627805e46423e03861f20bd6c92a9a481bd8a74a2a0987456118e2809ec8e02e9815dea57cb78198ba21b46d0e54f45fae73548c948d9f0f6b620c24b2e6cadd9f71dd6b9dcc7a281b791e9f7df97c44e73592a49ca049bd072a7df91987c77ba92b101d4aecc43641d29c55334b02ea864b926182981558ca4195e9221efdc15c0cea5222b37d38dde0b4e57008259896b636300f242e1886919d5e4ea1c96715d456362dfb522c6e6065ef9ea6d2a3b6ebc4e73d235f8c3721c174f2a5f37517611595b0a0817bd40b0af7371436a4f2d65437d1fdf421b5a50470b24af3b4e5243b7b305bf260badcfed6694e43180b1690f4deb230c6f6a6f0ccb6ec640f7eef46220661f1cedb66667cb794081c60edb4d873221c833b04e7c967cd7e417903c54a4138759a5e0b588e7baebe08b20f2c2408b932d12ef494803fe64bacab673664cfb998ec3f6a370478903e3d745a4e50194a80a7e39000a3cafc7d18c7aa1206e4b2824212fa569097b7104a409c076801ffb5c6b2b7963c3cddb7821c172698036887d99f4088419de8a547b8ed25dac2fa656a9397eb85e9af9352f358d69cb6546b9b79662812e3ac9845f561d8acdfba6e0786ddf6fdadd73d01cf16b806ebca182afbdc9230ebc9f47045149356b3ad97971c1f4dda0703523b2b59cab960b8db4b30299a796667a57d306749242d8231cdc2e9b743e9500648f3b2ae22d32888799775b38028526241c996999bc979a36844045b40ea4167a123df2c1eca9810092d449c24d91f935c570562018388e7a1af1fa75cc1f75accf39836b7a8e074369baacb4980eac022dcf3fec24bdd3cf666b73086827e6750b7daf69cd895b2aa95fe2c33b05e894aa01c06de1dce52bec46ffc814c5310c12f7e9c5b4d24080727940fae20b3496c3ea682af356f53c721a6f179d5cbdd7261d25eaaf4305e0c0f3eb18969617f95598ac8276a794baab7040ac8b2f9e74766cf0b6383a03f7a6fab12d5638de53ff03f57acd6037ed7f8b6609f65384a5fb21d178ec08871e37c68887f919b18de7842c5d604460fbf4a9c7a517fcaf075fa47a01f1f9d750105d448047a1d10da597a1a4f088f30497c97648b92c3016dae7e4941f9932b589de7b25637e460016815b14cb01bce4d7f10b1ca96b5a685e88bc21682bdd588104592a81a7260dab10117f562555713d6de414f18a72d3c4536044ac2081992b100c952df5d9102207a059a519024ae1558160ef7ea4036bc0dce31fff2cca804dd6857e52cf9a6cae10e110ce4ac317c0ef57f61707ba1ef4c0e9b078838784a27c3fbc7646329b6692bbd0bd8b76385cb9743ad54793856543f4ee4b9f77446cc6a186d021bdb434255cfea03c5520c1b74201d9fc30c524a5ec001b4b4a8827db3112fb8ff47e32c81ad15f55688219adc9c1bb333f8a99c60bf52ee08603c498b6c899736683993cbcbad890ba95c02c4942c721979195dacf9af81f6348d9bc814b71ec15ca05e734538b7839c449b88051a61170fcc1bf6a772089fc9131274cd6fb98c121b42fb3f767899829c1b5859896fdf764db7fd4308d6a7c7105c063eed4b72b1087c51cf05059667f07601d5720695c8f440d4b3cfaa5684995bfc6bf2d462a33604abf3a3031656745aee41f369d0aa63cb3ef6785e40e6aa4ea5a9bb915963a59eabf6715ffba6254282e1803d6827db8236ed262d8b2897ba944e381fbef0d1a9aa3e139a7d23ac24fe004911a00106e6277d441065043813d061ca72276434281caa9120d5c0ff34f253b778d92bb69d6b002f3d4bc5ba8863a3d1bf7b542249f9968b7508f11bb88b9f2d83f162d2e5122379a419c0e1920a11b260efc4d483acb1bdfba4453c455613c6a74213e14a27f041aaaff3da9d8215929f0496b7c19280664fedae217d1e8772d521a67cbeef0923f319416d6b2df43af0b8f264827fcc97c9aa350e592c409dc9a502b614633b1e77c5ccb84f0c01ee9680ca8ce9afe474215d9bfcd687a49f089454786faef617307e2e90f3872ebc4001482269b2ca5a00f2123a77cc85e050cafbb37579a61c776152897f641fc27901846fa464679ee75d9d8a61ac3748c07e126d81ad83adb8410dbd611d06c4cd45d4a5396e6a3e3dc1a42e814adae94aa531b39d83ee032ad6cfcb9256f4cd507f510209603c413685a28783d3624d9043825f7666731049a0ab2c74e1adfcb489aff305c55886d2f00c4dbd24e64b7cd0f134e0b10148bedfa1e4c5326e1178c5165ad8e79e2a0fb8394b4a4b43cc5b02e8d3f80777972b3f2b74abd8c74c1af8e41141ed2a0161b5fdccf99196d7abd015f1ad4b6b0a619b64bbc93d8ed17270c5a1d8979e4cefe30157d3cd51270df1208a7bd9d9a06f9ab863433118787e44d1e98f901b986a4507fda2ae7487251c0134fedb8a9ae98add3e7739aba3b01cd106dcb03bc28cbaa2c87b4ebc65c903c53f247826de7e1e19fdeae0f22c276fe8421d00e37185d517756d056335b14f141e0494c14767ffbbfd331be1cda519cfe9136ea6047cec64caf3e75ca644f8801b535b0ef17b6c79bd81d618607357190ccaa1362e24ea442945449396503cd11738f01a58202a900fe39e85ef66eb50a2f38aedda8f12ee3de8d6e882da84417bd0fbb0353faa7db1e91808d288d1453c89c94fd935b351147dd884f0d36808dd0ac532949c03852c50b44071ac51ce0dac2c734a439fb06170a1d27b2c338532ba651104c6ef1be80219d152845b9b81a0ab63ea464f6b8b446f84d25ca88523828446271fb5d03ca43870baffc4a6fd8cd807b783c9b0a2811a39bf972d78e8bfdc622f6d06c65921d1acc664ae5610cb9c891a1136c1394d0c9fbe0e7bce064f1c1ba636a65a5f8069ce17fb915befc9208573df2c0f637979239e4306290248f7047a7d7c081bc2aed2f2d7a34b8d30e835b3bfbf3e35dbc00248af11a12f56d1a911d8d1239d4f27668f816a59c5d876fbf3fe3a84f80390c80867dd40efd397e2a588c013be990abe2a378a86e22c37f23bc1b000f039997c4a777e800deb2c2029ee66c7ef439467b2dbcb903e1475fcace99ff87243d55be078b5c5fdb9faf4065b8515084a6c8b6d70183519380a5b8b4529e8ec2905c5b1f38c68412aa7cb84efda5400226125a52e97ab5021f045db8ae9b0a5b7ea83e6b6e55cc5fce3083f9bb001cba8100f544e9c8c3182e0fd9dec1bdae7863e14ba9a4591bc0aa0ba6fc72d9453f6e3ea30b55917c310b7ab4a71f764ba1f472d5c34ea277d9babf991bab50749ca1a097c58cf4430610a4d689a04346432fe3e1c5fbd3fd8bb5826d2dcc020d2b04c0a89349ce1c83931399f7c1ac3d131ca91ac3398cb82854f0147ba34b112db2a1974aaa4273c6acd212d6db234433a75c60f8842d8a0b70758f9b14d2836a31162914d0adc466f3080b91a646c4045e4e55acbb180443e580eb4c3844bea261d55169806ef88f8e42710e04c5dd1fd2c1accd00ef1617b8e36588e221e75ddd13e7c4056425a3920d4cd21006ed386affc302163d14712950389cb7b10bf850be17b4edbc37e702a4e3e3297778de5967ec3f4a23c2c38cfde54206fc11c2fb9f5302acb0aa43f893e85329191304645a5c227cf79517e5f18c7413950ff33e3035b9d905267bf50ae08cd34d732455e6650a1f7dfadd7d4f58059d29250fa20f50a1412b306229974d4686e0173b53a3fbfddd8c887c00fd65004a88795280d0ae6c8331651c1b531b4c2f191f997a9b7ac8d33153d84addfbd2f89f88ea3ead4c00c3fe7e594d0a906e5ed651caf29dc71a2f76f60397d637ac630eeb789c3a5d6396a0505c40eedb8d55c6a7d78a3fd0d868a4445cc163b22370e14b4ab0e1303c58531ef69cf01882deca26b8762324a1d4310b993a6e5a094de9072df41c7fbcf7bda8f6d8a0a0269450ea933f5d5d935a4ae28c9eecded770a06223743271154435141c88077162a02370d78d92df8b1f8f03b06173a05be2f44dac0d42bd5cc7510430f04e518b0a6a4073e18220223d2178a1ee63ca20c70959bd319bc1fa84160121b7dc359216a28285f5967083f0a8426a6f251b617b790e1494a92cc7e306dfb68762127c56ad3e2bfd691038e0c0597b1226f778518a9e00715fa70e8ae620aac20df852dd15a9eb3ad68a3c9d30c810b13b58127cd942b1fc7ae24c4011e0b2b3dc3f8a72c97920ae3a991052afb8b41ad892d4e8b8b6fd13a20cf0007fd421d0faeea81768001a395e22ea2575491183f02464a18540a02392872370a67b1a4b6cd9d31e21400423d23288071bdf7504c1629b63c97e29b431caee722429d56ffc14db85ae0729045dfe8cb02a71774d2e98c5d90e3716e177168a3c0cfd014d30a601b95e0c5232e9e32866cd98012483fa4c1fab942e90362f1eb4aaabfa6eed09910e529d92b200d5397e72c8c6f89b594c8cb5ebf7e05646a425f4fb5949dc11fe8effd37b3e176e64338710ac576116fb2ebddd538d5c1b10e11dd128d258c2e49826e1d8d96619f011592b68c32b32bad30fadd5cba1149bbcdf4563e1c49fec0a7cf51f8a4f08208943a9b189c7947b5b37f032aabe50244b539d6a8909231734d38d3975392292ab00fb6560a59f3481250fcd98f56a71a06138df6520712633231a8239ec2403bc997aa86b82a59ba40db468e620077dd9f1ebb3f470104903391f34761b695807290fd2c9b941080910dcd51e9a9ce7e23aa2e9aa5c40fc8b64cc649bee895256f0ab5453b01e5b695d9c59ccbbc364499bd4973cab43d94313d3fb40753501d7456e74341c924e151f3cf97c19e22166ba8a33c4f7329c547c42fdad7c57eb529a686c95ad71946737dcb48139d3c382615a77248736e4dd864ed2bc7fe3ce273563b190e60df2f8bf8a26d3f60c1a1d7a291974822e6a33dd78e6f7014b52b42ddd809559c587e102f577203c311832fafcb15774e369cae5eb0af2a829aa259d518835ff3ec63ef04f5d81ca2a9a4ea9b679f2db08aef6d38b6c561b4b252f600ac631afd3c1833b99327ec3fc569bacebc94554952aec39e12c3c660684239867ea866778626abb2825cb34e4a49138516fccfffca7798111172d01a2ed8544ca36de2f17bfbc194db7b356b3d3e77d1e720a8e5355d9e87aa26543d72ba99992c3240eb2261055ab82e911d73df1f49e3f19ad71e61162c028cc2f5ac8df655612052d6381bdac1f90d3987ddf6a85af830551ad47729941b58b2b027a7442886132ae15229d31da01c6da4fc1a13988ba1317018b8ba64976b24985598c0151f6a19ef53b07e8fd11b3d5c86a7d7d2b57724c41f9ae5c1d5504f8344879b7dbd16c581b1c8975350e64689a8dbb99ac7ae88a6d9397a3666be1cefe5b6aaff98d6f20f13af9e1d01bebc3db832cc6cf86b20c6aec23c5bf23f175f0b9cf5a761d3e753cfb84f4ed248b22dadb35f17ca8ce7fbc8965e465632573b2573b59167a92fddad7b2a70e056e0b80cc8c19469c9c4437cc16550175493ac93708d42c19c21e8b70a4c60882acc9dd9c9448b7d5f6aa5c43c62035f758008d8623d2b3280ff20e7920b52d7cca9c121613dff9a1002a32fd00e27318c9d1876f828d9700f153483b8398b1c87b1072114b81b913641e3d061ae4551742596474ea41ea2b82274b46434594c340d4fbad4090ae9a8153a1469060ac2c076fadc76b72467aff2dea34996aa73a2de2c240c2cf8c54cc04a4d1aa9e451da3164155c044a9bcb931f885a57c97b239299810616e626d68ae1c57462cdc45e47619d7037650ca0fb951850df3209ae5f1362224f6426cec189e457e80c49ca5c4240490874c24027408484c01528b2a2154a5a7c23662ac4a3de2f258bb1bf63b75c344615adb35ce730de6879b6f435eb64b8ee779288deddf40e8f24419f6a924b370669fdbb8cb4a135051ff8fc7f7d93a99799f4dbe7bc39bc24cce0878da267c24ff6b41a84d89fdbcdfffcd813aaa9db6a17155083c004da0b76462462027a87309f12a151f87947ee4e8fee7591473760663044428686409a1adec542583479d7a19838e0d9234628abcb499a84f3100ba3038b1da33e55600f78c2f5e3e6449653d3148a0034b75e2ba344c6896c0f314f29d565f91786df562387dadb47cd81794c803992c7a521f28f32e0c2a907589e2ea6790707eef02b0c1225ebbc5399a4247db6e4131aae0a38f5d044a327c73f3f84d8a9204945b583166bcd8f8fede80c970a54129d814a5584903aa23e23ddf4f1c18d153b6880faf391dbe42fcf2461ac5f9958cf5b6534768fade0588564deea072ad6a0cb68ce19188918a43f1ca4b6ca6eb754f2e6a68e6e8279c4aa0cf5ff547b846f87e0189748263bef4ae09b8a7cf3d4851f81dbc32ab25b2c49c209036088a16fab6d79430d4569638e0008adf5f07b65d0a291dee5f36c0aefc85848597a236185e3b97bfbe19f6199b5366f236772bf684f63ef472557b22f86e661792333c6dddc5060c4f885ba39783b87360be2bfc0cdb508b011431d01ce0c2586381745f90a2ccdedf1267bea0785055a72e38247366e715e7fcd2c8c8b117a87e2c477fbd431eac43277e5f476c3bbc08aa5a1e29c1440ff358fc542903aec700ce3a0bde0e25e08d2c5f2fd0f8e4bc3ac953e100095ec03bb0444f6b094a55db893ab70c149bbf1bf76fc50e10a952a713c42baa347686ba41614e9d49fdfcf8ec4d5496833490de1aaca848fb3c7e91c98d39f3ab26b343336f70184be163500ead74221e8567b92c80066e301124b1b1abcbf3aada61659d8fe301a3604e7a7fc906399c77787e82a94570f2e6804bf4aef2fe8091143cb7bcae8d5ff7d83a6c6edd990be25fda39dea205ff49b19f37b4a77f0e5388159cbfcba7497db4f250b7fb38d7d78c8ec6d75d6359e4723d4f12516b58df037a6b3065ae05db1bc0ed7d4d850e3f616e091d15e04cfbbb8a73654cdfb86f95b1fb32d73fe38330e9a94807f25ba2d2bf0ac3c313702f2a838fee0865d82a1206cef1f7122ff5183d40cc86dcb4ad1f07d7bef5b4baa278c9a03cc600818b8a28e5417df4c49b03934ddb98a649a83fb390e045eed4826bb148a2086d6567cb975416a24b5986c0118eff4b9300664082e520dc09aea1ed2c0941e42324a64ce07130f91281a79ab7a5fe1a9970cf5f00549460d0cb6fe0bca520bc6ec4d5f8c267bab9c0f205c998203f856259d622113e36f5209917867227be7ece89805ec26ae4b82886d904fbd7ca74b8658131296acf85c83c8ca5ad1db8aaa4efb5375e74aaaf1986ebb8c3eb75d28202090c87f0e1950398f420899a0ff3a8d6218c0f55862bb27197db3c603bc48bfacdc20e704b3e3eb2e15dd25ba154b7e55b9876cf63a6fe621cb50e2e6b90d64ea261dd14f41c4a4d1d36a74dccc7e9c10aced4023880831822ec63c263560ecca8d92f2a62b50d8df5f9ce4ad8e73816aac2ebd079ce96b4e225979064ce0d102a4b7d2b2b74faf98a254ba712ab68c4f3c23140449a8c0a2af8b8199cdcc704e3f03e6aad5237e6f5f66c44881c4f2ef92030e47b3d29ca21f65be8ab02b2ca7c98b09e47306bfb43b4cd04aea68524f3c67580a9d5442caeefb2aa4571abd2c95b4f6aae14ef5dc9e17a9f6984ed286ffdc40d27f28c68ce9205f84cb30ffc92761e2d42961eb3bd94859e3ba7c0a712924fc2b4c65527b84e6f2ab15799f946ec4ee37b558890c0cc1c448e7a91be3ac773c58551c5dc06c91248ef3fb84c55e2d741ff089d8c2a3287572f500961371427c6445f1a9b710c1e2135e64716fbde2967b5fe8d132d33cf868340a50276ab6445461fd230decdb3ab0362452c2dbff97b17267aaa2c7f8fb0a6c3fdd8a824d860a850171c8c98d5fee81119098f76a2fe2582e078d8dbcc55a8a7705294aa9a5bdc2ad180de97891620e62e2e14de8d59c6152631cd095398d69117060facd8c614d73d926480dd53f90cc9824274c2885a31d096f7c37b5da63b3611cd3b817608260115a579f94bdf8f49e47fb4ecd1021d8a62bb9ee7bff21b09d417e575e62c5b256678598734c7e1a560cf67277d91e3e2f14b34b68aa0d3e1d29efd9a7ec583878ccfb21cc1b60b3a1a71a765404e4501bf335980939fe550319be5d4d71391cdd904b3d7604cf841db469f47b8fa02d89ef4bcd3f1125b33326127abfcd05077505c7678bf77438879631b9503b2ae1456c1a27aa16feaed7fba66470547a039cc6d7321b0e103918e39f8c396cba094104dc2abf8893951427465c8ae6967e8acfbca762142e8755534bc15ce537cac80fb7167854f072fb3c93517d8e59a47ad19fb0f36b2e4b66c86937cd334d8c803f2cbd6e6fd195312139c02646036ea685dbe1a00c9b01eac20edceab2d1be2fb862a4ed033ebb44ae858185ca028600f5913dd005494da36f88b5d0a7c79ab33e05859b102f8f2fab4bee2e2b0453c1bd027c0a044a89ebb25720d23a4a07468e7f1fdedfdf73aba3d7f3580c2dfd1d383dcc3141b3504ebb092bdaa1a7ad7debe75d9107bd634d4e8418dd5961eae779d69a7b68e16a284616245d8b36276f3598b0686c966c42f6de64cb2da59449a68d0a1b0b590b1fe633689a06873ad730cc34bd9ce498a3d309769d5d02e110f519302ba5cc47af71ad566dabf16097fea8eb44173c8a39d4a198a7141a51a31dbaced96b7cbabcadd465ba059d7ae7355227b5986ed538f53eea56e7d4db4bb7524ebd95a20bc70d1e3e5a8ca7de649e46978ddfaac2e1198939d46b3cccc63025ba6ed89bd54a656dec956a3ce653381780bef39437e5b16e4a790cf53709ceb37ce0a7dc07f5b1c665a8b14be0a7ec85423a5be631ef817686fea86ba03fcaa1604477e5d1637e5949e3a843a79d36599d36d1d393363946ccbb4a57692bddc29c3af3adcc9c47cfc680f22e87795b4d9d9c5f52d0fc85464ffd76152331ba2c1d75eb7292972188d4ad0ba57495e8a221e6504ef3dba19cbaaaab3c753844437431477bbb485d865b3f86cc939e91d813886b56161adc569d79906ef56bce42b75c8841734dd3344d839ad72a25e8b9ccf3eb04525e831cb45af5ae1cf3d0fbf387f2a8bf3ba5f054bf8c491831c668adb2a654af1304fdd579cd2f1f69aed3ad5b7d74eb763fbad5ae7910e723bfabefa06ff6d68ad9db755ced5d69cd47af7907e94f83ab87b562ced659e816b5b0555d73cc35dfba556110c5228820c03c97f132c1d0f38673e3d2f7120d943ef3028ca0cf9c7360d90fe19fce97078e8e87656e1c238e611495d80595c8547b3bcaf160de5774502962e9afbd764ad37b8541a568951e2a916120d0072ec11efc0b9947cc9b0c03e12299c72c7a4bb8bf0db35547f6e7e3635cc2428b4eba6c95e3213344c105ddca9983d1a7bbad2ead4e0f4cb0f4267793a6b9099a20520e3efacc6fcaa5137332e85427724e3793df9549e34e6dfd55c73e731f2ab89079966559a6f9edb2f6bcbfcca1f7f597394e7f990769e8994fd769cf32aa79a66df6769f696eb27735c4df6472cd21347984cc495e42d2b8e5988931f788544e29e594610cf5dc62eca6981725a5534e294b10c1f0e42823476666e6c892291674ce49e99c53ca3921857042090642082184104208218410420895e4849027ced05f56a5b74b8bd14919cb61484e9363be654e25a50de39c3a62a4a458ad55f62755a82f98579f93d24923532c71464a79a6daa59473ca29a58c33ca18a38c3068d9734eed28c628659431c6ee1865c7eee6c8bd0dd5eab56b11f394c0a7948f7850e3631ff9e0e459626d7963999a5382a9b44eec48caa654469f53d249e99c52ce296593c77cd2fa43e59452c2096598325262b18403ccc6f589a1896118a65aa5be4aaeddd22237ebea231838e1aa899842a269dbb66d1ed42066656852ef28652a86feaae69857e9edd3e4db363d73ce39e7e1e6e44c5e8c2e63b7228d118c339c53a34051c68e5816888a0ea5f4e881007e046a986a18e34ff7d7da0c613764c8ccfcc391614386cccccccd700baedf954f069026877a039293fb03021a92c2909b4dfe76e51eede5f935a4e5e39095070339066c4012933f25d0f2fc0202cb475f3dbf8090f25a94fd0131730abeec4f5a9e6068d2a527e625f78811c6a0e3d70e48df9181301030a83cdb2dfc832215645145e889228ad0416bdc68a4c4e934dd45bdf4ebbf8a31461d8c629807698a751addf5c7b08e87e374aa159d54878d35bef454f5375d0a1942b792524a380694928dee762f57dd922ebd53c92ae79c6e05a45b7fd4993a1add5d3dfdba75eb4a87744abd3aa5ce9e0f3398bb00f450a762ccf104690cb33d9865f9acb5d65aabd75a6bad5996d596a2b5a9ea9cc9d3fda0bbfe13a75bd2a1e7c34cd714521d1d00e1e40920e68c1f909ec7f3eb07221e329be95ad0d4e84ad036c941808108ddad1fbb950534c691b2bf2981d0ddee67a79a38dda9ca7417f5dd3d6c0300da107e08fb4c079d51506d9d139da3106e950073bdd4c920cddd62972e5f8822c6e3bc86a223f830d3b188c869efc428ed227460a030a24da42a125fbc24d1f1cb07f6e88350b4d2f1eb8728cf3f3879e9aa95d14b1e76e8b3dbbb696dec0468af745aa5538fdc0b4de42452c3f468affce8851656223704a815235631d72a56abf589d432330083b1078da14f4c83e63b08728f86fc4d127c63de9226229550e61e904f98f336f98e1fa34bb9a43f724462c378c21eb33ef1e994dd8a96c2b642eb7835fd7e3a9ef9edae97735150dd1cd339dbe3a3f9adbe24fe15e9fca61ca23701da892ebd1144bf9b578f1c8f111de00ee658f41c9ad890c60a32d05883084548b313fd09d04eb44fa863d666db35f299bd329d7a36c09d1c38cd8e508e9d69a1449731baec38a7fd49b44d4c2b30028a215060850caaec487f12fd897cc29c635731073a10b561f64ca2cad7501c5d50a43f99727acca95fa7f370155d50307f42313a394a0b65b5f344fab450eace131b580c51119e801913a68d2c3b4fb2b44beb5cf497c7c354d75f7498dafa8b56cafe3ac5478c90bac582dece2f89f8f6fa370cf11c3142ea8f5f02c59663b0e8f4c8401a8329406d8b5b7f2d830e7af55b99814853a34a1852c257eb53c2571fb8f4d5072a7ddd6273749ea69b3c66a6159eb922d36f7f1cb2c257ebb3c2c60ca4db9881cc9f51ca1308fa97f0546c4e4b6fa08596a7ce2a427d3a7dc9001a2f4c83bd401d4eaf1eeaa995f0dbe310d44b0c422e1a3799170f679e5f3c047df7fc82b2f430486b451ebacb4b9ffda0a15bd133ffe996f4cc99fc68cf1c49b738cf5c866e4dcf7cb5ea3afef159c66332d7a6b779b23fce6472a6dee698378771ae71b780bacf34678ec7c4c2bc764bf3ccb7cc335f75cbe49967aed3adf6cc5e1d9f79b7a66ff6fa67389ff520f497c767fc30483ea1716b3d576cd0d9c9721df372c0bcbabd34f42c897f650498f3cf26403b98b711cc4a7b2bc71c1af4d38344fa00dec13c3ae6e5001474546548c8090e3704ed3c01da899e65da9d440dfeae1ef677912b47dc4007658c204ac10e0ca301521c71c5111a5ab680b203dd9f39ff2334c6b97269e3b293b3c32fddc29eb50d052730253ff5a5fbc0341f0f1da8d42d93478763ba45a65b9a478765bad57974b8145d351eaf0a298ffea35b28cc93af49ae887494bdfe9ca7e06f5347ca4622fec9ad56317ae48a48ae1e7d7b9db5524e08f6986f5e75e92da95fadfc8d5fd0442d5e2cf2a251172f1ef5173de5452ffd45477911a9bfd83d67a577b797d910ff93cb97942b12addc93659dbcdacd371b894eb62ef1413de7288f455d90c0289589679c0c1141a4e5e8cb986ef547d76a3c27594e527ae45cda6b9f7ae7c1f6d30b8ad29fdcc4158956fa3bd9cee337715d527344940ce1cfbcf3fa4d1c0fca329f70e9c9992b520351b74e1efd646f3ff5c915e16cb47297c0374149fa66eeb179ed261e2d1279d105dc2366d6074622c990a19c27291df3e2d31a84cc973616718ff8a5bbb14ad1478f55a2956e11758be55256a469577bb6520a113281aefb4bc0e985a22b3b1373da9d6855a59bf243b5769d47ed47b7a6b70a3f023667dce4d1fce8d24bd217316094a4181fc98cf9688f32a4e85ac59c5e654819525c6548da8f55ad314ef767e8838e5f425f3ebaff911329cf47399039caa1cb7b01468c978eb9966d623822393f83fa8b5e86c6949dc1389ef611ba4b7757ab69b333441c99c6086149d2d498d265537242f4855b3a496120bdf5517757dfdbe6c34c27979e4f4631a7bd4a349d936c4abb32a498d35eab7444aba2ef0cc9e83b5b3afa3e19c55abbee64744f46dfae4931f28c1423c598ce4457f5768ea9235144e9cfeb624e3b0a5aba38adecf084e8170a411ee009d0a7f7af153879139e5f54cebcd64c88601f5d8be283c5fe1c7a7f6c020d87ba3685d45a6bad59e63ea625391ee5577a96993289927ec39fe0f0272a88c34d0e39ef1a798d6e9da3bc6be4512ebd4ea66abcd6ae5badfc6bbeafe1ba349f724d7e56dde4c5affd955a530f93adce0c44bac9bb99a39218c93c23693ee3a4330331d92539324f12a9ef331d043d9977de93494e561be76c6a75cea5f49ab9b4c99ccbac8fc95e8192c39fd8d8e4c8e13636396edc66b5f98dcd716caef227b3c6bb49741e0440df39e79bd739ffc9b9b3d2397e94c7f9cac3e1ddf09edca84ede35f21c754e831be7973ae7d435de5c4d9c701b69766c3c07216ce709d08e8de598539dcba413a12e6f7d813a56b76e795b7f9bb34f8efae6f5446711a96ff224d89b32cfc7199dc9619abf996b0e391e8d48e6d55e93cdec151b8fdc159d9d2cd235595f3e0cd020a181c194251cb840ef01ecab037d73444e5fed4ffcea53bf4aaff6c2c718c022527f7a12ec97c4a7d2060c28d5352fad3a77add56b9598bbcff4cbe4ab5d92e3a9df18638cd1fad011e20a3a1ff858b5f256eb33fd6223804666f56efd5abdfa9039e762d687da2bd0659046bd1ca863cc14a3545aca4ce95667432168ecde02f8d92f84411a5a1f76c81e3333841032744984a36f8f4a743016d145bf70080ec5222dddaade5dba055fcb3c7a30a8bff6cd6312739a7978c3f968af913769f1878786373520b3b7c99bac0cfd358f11235c74170ec1a1e8d28939edd5646f97c1a16f1ea6dc1093b347be3acfa3b9e6f91042c79eb9893d1f662ab78d55ba5babfd81cfc3deecfc355ab6120c47e1570ac63c7b6f118f9e0140d7e03845015a9ca2691d3509bf3d08a7c0a70fb776ed932dc0b7d7c83bedf1817e3b737e9943f526403bd5336f04f562b63dd36aadfe0468a7da2726d72c73ce6deb03bd9d0619796abd2bd54dd542a99ed5ea19998f39d19f54a5879ef993ea4fb2a587fec49e118a31fa6c9a4614e2cb9796b49ab65357a3daa0076ff09ae2e49f5f42acf110420861c71b56a49493e2e082611856b31b5e689bc96432699b8907a78de338ee84a240486d5dd775a99a9ae7d79435549b8d8d8d8deae60405c776e3c68d1b3856528811b61c3972e418819552c18e4d07c7a343870e6e085471246cd16367ee3545ca6b082c256cf035041459dd4e6dc303bf5200e635441933a82881b23a5ab8084360bc40082c4230d07206116a507162b57091c5156bcce00447547ee0450e334c5a80b2544181182b7030030538942142cb46c4514a8b019e5f425cf1379e5f4260a142cc38849fb9c7ed2084104268821344045981e2076ba4c16698f1a28284105848b1414cc340648cbe719325a4e14f4f487f74cd8725044eef87fdd453f5d743bc7ec84d1b859873ce39adea88856e5d1f467e04f1d1c2d539d22d1f3e3a76c53032006306415974b0c54efb8a5d4052926e8064822d4f70f962a7ddd905a4822286d006181d706041113bed1fbb20c0431758a2dcc0065cd88055584972e9c89c535feec41622ca4387f3d965cba86a57a7a35d2bce89ce5f4c61497de8ab877eb167e78dbf5899993f085fe8fe26e9b687d6a709b431c618a53fbf828854147ddff8d65b733c4aa4a973761e5f7fad011952383678d2e426391e253c3e201acad91ba670d2e6e6a35d32c49c7615ae0e176f9edbaf0ed717a537ce64574d39733c38ae9c61860d2a9d3430830c336c50092ac5266a2cdd3a594f712befa7bfc699e3a9f1208dc373259dffd457b90cd1c53374cb051d7f8d37b7d92b7f731caea3d39f0ebf3f587e5118c16fcec71c7ebd12fcce935f4a426b87dfcde59e835d3f620e4b0682f2e82b0672e3d15e1efde93cd145bf1f37e4d3893e4ae009ed24783ffdb5eff098f4d7ee5e93ce69e93aec2d8165ef6a841f396cdf58e8847bb4a33c1a7c40c12e28af87fa8bd282ee42a215a23822a9056cb8e0b5050141666680254b0a8cc0d8699741032be73c48dc9e7c3b0eefa76f784a7eba3fb912947f3cb61b676ef39be86db7f9ec90470bd5a8b2a9f1664875db26b150234a1842f04112184070a51e7e88f2c5071ea0d869971203dca3fde465009ed01e3d0dc8c03ddae30fdd854452a95b3dd451faa341a70b9432b484d941500acc60342ee8f24510133f2419311082c0b20508a231675061242c5e04a161a28ba0279b6b264ff3246f568910f93e4d7e7322f2373b3d8885076281443dd45120d14fb7ee0783a050b72e2bd1f03169029d744be9fb7448638d21965480e61504a01398400637f02028a94ad111208250a68823c840828d0400018619a2216c60e6a531248a39edd22f8edbd589e32f24c2d19428ba78071ab5f82fb4e2db6b563dcc8b927b488744d1355b8715372f9b7b29437fd8b3f390f3057f29513a4a7fed42ed3c437fedd813dd6da16fef28dd9ad2ad0b89aa74eb7e42ddba43dd82ddba10a989e8d6ad56fe36d1b73751b788d805347406104db0c0cb1367e860a71d16b16bfbc1146ba031c410ac98d9698746ec02a29203335e51ae50d1c6163bedf0885d1b280289305a0c31e185153bedb1ca7711094d8210a0632c4abd7a90e3914aaa63ae514f55bd8e7b4cc73c1bee313dc5274cef283ad9dfec2eba5bb7950a674a281ae35c56f958f5b801d8b1a4a3eeec37136b712b334529a54b8f1ee413a4e41ce93e4ae8b10ac04bbfdd0e16a2bb52c540fa2547d757c22de167f49654239cfdbe9f9c74f3d09388f8df9e8f6d55dc037ac71b5796dc6ddff2dbb29d47fa9b10428ed01ee96f1ae90f06ddf48f104b81c6ad8179f6d7104acff6a7bf00a8e91a733c3a5c93540af1530a71ce74288b640fec62728297958e20b7f6d249cba4bf291d3a730d4d9bd436b8492b559c090369283d485759248b20b442a057d75c33b9ba09adc9eba24b1ac9a4d54a16c9a21dba1cd1c94f8f41fd4ddf9cba9822ced9c1d9efd8b1c327136c63d22dcde4d0733894a2f2d9aacb43056f27e5a01c8f4f117f93dde198b3e669cc991c7ad24dde10e8d26d75aaa9bcbbfd8e1d1ea4b58c61164fcc3d767810932650bacda9df4eaa72f9cc0e4b67aabb8de6670ecf61ab58119a9d546bd7755227d54ed2494209f539d43687be6990e590e3d1e148600ad4a74f8081489f4e016e693e49f05b5d25f8ed80d83843b771cd8bfc9d00bd7ad04bf0a093e0c1cdf361a6abdf74cdab4e74fcd54d74487578b322935191960ba7fc74264d56199cd22dc967e0d084b08a11235a0ff7b7434b912cb245dc098591809d1a56aff6a739852e9c84faeb3639f4530ac59ce9f186eec66f380e5f79f492d46ed4ad954f9747d28b4cc2e177fb2b8b7ea2fc4aa49ff28bab04bf12cc4f8aea64912cea96166914357b936c39fcf299ead22e810f6d8fb4d5bad0df0edb7345c87748f01ff85748706851a0d25d59e5a76f0e9d40a1284c76b8b4d22e127cbaac5282d7483167bac65147814a771949e87b8cf437fd07be0bfd4da7b64746118aae7826e64c29f4d3b5d3c6dd6a1225a54d82d75d3a6712fde6b793601428e4e44c74318939738766cf8c406d7bc9a283cef21bbfe14ca20bee4c8741d0498ddf9af2dbbd4ae5cc2a678ee7862cf2a4517f93724ca20b49cc995e590ccb4a2dfd4dc7e12bbfb2e80626dd3ac117500e3d724550cea45b279fde04063110ae08caa15c025fdacbb9c983c42b24f8cac3e14dbfe1717f99df78d355de8551cefc741b4f098664d6d85b4d9eb2b763ea9d989fde4274916027d24f479d36db69263a3db3f7fbdb557b75e4528e1e400d34c6b9bdf4d4654dd7df4c39e7d2bb0a03895043a1a8f320dd5968143dd939f4a0518c5648f4ce3b1b318fd0a85b1816ea2b2ddee5a997a7ce180631e73c4e75d1a51373a8d71a314ff6474374ddf8a6e5b8f120e74457a53eba8dc79c73336cf28d4dad36f6eaac3293e9880e0e45cba303104278bf8f5e129d20d121741578a2c3662f3ffaebe8ed0156fa9eb6b6856797de5da3839ee4c733b31531081df41887441edde5a5c7e851081df49884bdbc9431a68941a2941ebbaf4840e71a4a850fc24090700fe8dc55e17ce6fa24fcae5e71e86ef7d164b58afa78e8442d094e9111845922ccd027d171e1d4c51c68821e7cce33a188eefef8e8ea3c7a90cee3980337134797aa73babbdf47fecda3ebfc50218898e82c74ab7af416569cbd3a36cdc61f6ff2c815690203135829fa242ce64d12622a484ee29a4691c1a28ec75f9c6f21e64cd78f9833dd042a9dc49298404467c2105db7fd81cfd37588886e557b25fa7458a5d1549f2e437475acc44a56c98422bae92d206120d4a7cb1c32ac4217ec65303f31fb23bf75700fcf8422ba76f9a4bbac24f4d365d0404d42a348572148cd1c26612f454890c41764943e092b3de6c9a7d375f48c3e1d16512ee49d734e8e94995d87aa63cc6f8c3162acc303c7376d4e1957d007f7e0098420dc835dc72a069c3349707eaaa0eacee8a494778b68a05b0a84c8f676778b677885143af5a6b672128f870e75a4d1b1bdab879d998eeddd60778c52ce492956399ecbe4833aa845a7e5864dd5e49a6593e3f68357aa4b55fdf953cb3e2b745b4b09799e24f768ef5efdc8e0fc42f3d15fb3978fa1090d57c7b39704e7d9fbc7376b3f787c98ca67921facc3c3fbeb24ec45d6688221744ee6e9dc73a58e9594524e7b65884b1d74f9337f4ecbfd49e9b2674a26d2a2651f61fa65a72fb0f750343c7a908e6874d0af0ebe6774d0ef0d1d4e4332e4738c3136374bd12217518f923e7b68fcf837abf9ab7d8c4d047b6989c038973b833133c9d4263d3331900a04f36d53a974e8c0c179299d3d451d73ce07a6f181697c609abaa453a264497de84a7c7eea43ff593d9c36456d4c750db4435010f4092dc31bfe480d0da318e6177b56a974e8c0c1e1c1833a7cad37d92d1f98a637acd65a55aac66a90133441417f9b04e5f0cc11896d04fdb5210887a0f7010afa086f78a884b907e63f750837a4bfdaba79d3f38120000a7ab9550692013df9a0970bd8faeb2985c66da446991f2c4108bbb744d92244b71c6d21da4204e42d65b694f1cc263160642b5148f90b9fa1fc163459b784e9c445a348269349d3a200130517518c89e28c571442e88c868c7a30dd5065c362c3509c812208cc4690e7867bb4cafbf277fb06b3a5c808e9553746553cb78c6e6034c6c609ad95feca6f28c43c44eaf80505d2dffa82e2f582a24b8ff9e8b0a17f3c1c875fa0d598710a2cc0133a7ef4f859802930f7e820f084de313d7b94f4d959802948210c04da130f1e031840095fc2ad386e453d3bc62e14bb5ce81cd711a48a54912a920b9105988203f8847635be323e86c5689ec10bb287d608f768c18cae19084c217afb0e0391f6c43d600ad16ef0845a838eb9870bfdf547db427f0d860a515b8b956f57c11f9e0341389eb94707b19627d06850cbd03317417b70a3ba737ef44bfb0664d433d70290157085200f7dfca129b3a94975a813b76915a353c686cf319012543637353737a99b9b9b9b9b9beca6623737289c2ed0fbc034257ccf2034ca9c42fbcdb467679802f4bb3d9f4e2c040ef1816984f048d764928f984b6c495dd2bdb43e30cdc3341d45834e2098970bd05c0cfaf0cd2f17147d148c81c44c8ae4a84cd412149302cb8eea09061907c6cb89312f27cc6c3188ab399c7a409950d0c915a45248d4482d369b91cae4e5868be71baf27c2dcb851d9e0e8aa8416c5b0338e96ee329919ba9584866ef1eb8931f1fbf3a1020b2d44973398010c8081c81efeea50125dc992faec3efe4bb4e7a30fdaf123ac1ec76bd826bd1bafd54d7a37af659bf454af9936e9d9bca66dd2ab796ddba487c26bdc263d9dd74e9bf4b6d7509bf44caf759bf4b2d7529bf4e86b359bf4e66b369a6abbb9a1b2b9a94975a813b7ddc0614f60e7a3dfa40e5aa40103611691f648376fc8bd4044fab45dadcf2ac269d2f4d787dc0bb569a0499628c8406aac3cd1b2ecb807742e45418faaaf49af3210e83c243aad906380ec2f8b2e6dddb8077408466bc82b5ef1eaaa5c38cd40ea0e5641e823815510d2d9c1bc5d87c72964dfa7761c98c2f40640c7cd75a473da63e89c761e3c707074e8b8ab6deb7e7aac22b34736c700e9d36e1df75881e9e4d6a9da5752da3bb9176010fd298974509cf60ae6415a7afd921b923d668f1819d35d28f43d8581446997ec9cf696028439da2ab406dd14521ef21002ed26d9defb6bd7d15fdbc8457755f7fbfe62ed1cbe31b4948e44464f75c3192c5db290799de75796281e0b16265690f4dbdf38ed570ba6bc019e5f4dbc9a28faeff9d584d0bf9a387a0d8e790d559dbf6a1a657b577ea3bdf0c415313de5b8213534f2479710723cb03de678a8d5a857e39a46ed95d1bbdd4788a47f7af37a546ec353efc68f367ee543dfa26f3648a4b06d36486f7cb3b75fe537f6f67370ccc30e452d54514b3957f9dc2eb4577ef714036a62038db7867f7ae1d25fccbd9ecd6d78189fa348a85f48e6af4ffc0b83b4e45eb8fd9b0dd63ffd9c47b7178e79cc7ffa6174cea3c7c8d91e78598ccd9b3240059d73ea41cb5279a7a8437b895088a45b0d5380bef90f1c33e6db6bbc0bbfc651dc901b87b607fae6a71befc26ff779e31da4d16f348d9873dc10ea3ddc0bdb53d789376e2c85de1ac7a349798d9bf4551e3f07a143f99cf750c50da15e937294438fe10918e6369ee638bcd5d778351e92fe301c3e53f6d6c7613de64c3f75f6d64759d8d0333fc1b63d9953873980295c8845e731bf592cc9f69adf24db4df2135fb33dd4db370e6ece05e99f88b970383cfdc4cffc6459d537c7dca34bc7a9633e820f335d92eda1739bf561a6fb890ffdfec4f7d54f7c6e08f49eb69b4fee85ede1b41ce6b7427b37d71c09cc014c815f73c9f16863de34e957117cd6a74b82cfe66744b46f0b73004fc0707874d6f44d09b8442a504c40594108a1101d21899450110d61444268da5fd5cf2f4bb4f1ab25caf89ae7d71260a416655e4b04cdb9041637b8420736c217466362d00316a05185100bbcd880915301316972d8c14a12521401eec082292916b0804585075ab0450c2ca84883832ea00e4469b02087a7373b94524aa973f7c2d245176198e9811636e013a22f28221be0a02200cfaf258a58020a0a648cf1932e638fb4b53f2f5d94b2611990dead311a041350518510be2869814ebcd6178491459bb083125414017308e30056f4d08330c278628b2d1d10e2c116635471e58627200eaf2b452f25b48857601856896ed8887c881956064c8e1fa856b7fb8e2976643303a88339c4320c6239226a82888a1043ec60f086c7ae9cf1187665088fd51d0cc368a4822ad1a58c1253acac3c8ee797123e6025d0e46bccadf61b218cdf8aada43f36a720bb3bcd77bbf46efde9b88377d70a6175d95fad257c4fb517e7bbda2b5bd56f74d9d13bdada5fdf55b54a9d3f737de9a50a561a934451115bcc103981415398f9a20d3286e0c5132e69b840d2a50b1923d018dcd03056b47456bc502b5d0ce214bf58c12165e504118583fa441b674c41881dcf40454bd00e720026684c1b4898a12111843884126cb411c40faca8828a2284c1952179f8208b52115bb4bc784083c4170e092cb224647044d402b4841017a3233344455f8880041424862000a38104191faabcc00a218cc1c61167e61145d85a704499a7f4e911693ca594c616dc6c993b24c182d71162bc8c3013a148a24ab6539444111d8b9278f2dbf3cbca10a21843709dc1c3183ca8628330bc704174c28433889862881e7850c2ca199d269a884de8602f23885e558e221395478d58baeef965840ede881b4c23bc3c7d7e195145f40146a2305e4618d1c882b844bc8213afe040af1d724b59c5dba3de0ea33cecf2415a935008b2c7f26f128e87d220dd4d69d39eecf20241a7cc4c1dba0f842f271364c420215131bac28b956f6701b6fa9d701a44558a5620e535d495cf30462cc6786d7849afd63e2d07fef5275d7ed15d4e4272c36f9c7a97c9d32337a89bfcc64e266f72ea79d3a7c993ceba9c844dcb829e794c52f6d6cede8ab227695b6ef64ca53e9d078bda16be4f6a0ee098633279d4bbd3a1f74f64f22cef3219a2b9669d9abcfa1196c9d2d01fd6535eba91e8824b31477a9564742656754ae7527fb287bee8ea2992e865d14b87b28daebf24420797e41098a143f759e794a67ac3afdc38666f58b8b41497e292457a291d22c12f37ec480a97a606391ff8cc3da6b3661fa5fa46d094cff1fc02531486cc1767be7a18a2cfc218bd34b2d96e239b65b54bcb6a8f56c8e49b9c6874d3bb9283e65be69bc731273ee6f027b2aaf70468470793cde2b3c2b703ed3047847a6659b54e8f596dcf3c00f4daf47aae888063ce750fb4f3d153cb8a724c18a1330e21f4b85b96e5034dd1e4d92b29d6bc82726872cddb39e6547e81491252a577eb65f25890886147509ed21cf3219a631e312328cda75f96740889683e2d4bda93c934e7c9f3813f894cd79c359db39bd5d488d3a577eff46c5298b24dea9cd08499784c26320e2bd7b3b9d33c59969d96d53618f3af1a78f96803313e4214cae39823797a32ff89dc04509ef2f66e7d269fb2dcf3136f6604e51aca7393a76c13944b7bdb6a5bff74aef3765861a7db61851d546751b6862c476a3ce529effc646ba8b129db04d5692e3d934fae761eef4c8f1f55592c99793ff1ab651161496b59d36e4da390d1850c2230a43c009ecc4f37bdc088a9b5ebd8ef4f7cac4eaffd297513e827c733ed854358d3dbab65d6644d97df5edb5f36ecc1fc274e00e59c7374a1ecc56c11193922269fb68caec7e4d22f6bb28ac8c7dce4d2b226e7657900ef689ac9b4e300a01dae3269027738cb3d3ff14d2e35232714e7d98679c7794db21ce1bcf32c4750ce3541d926598ea0bcf326598e54dbd92628db847329bdcca7471de39df67c6cf81dcce3e78640c8a3b126ab3ae612b35e443ee344536739d09899dd640a528ff868c579ed5d26cde49bf374586187ebb657066e0621b8c3e9b0c2cec9357b69e05c08ee9cb420918b5ae63bd5f3899fd986928caefdf6653233981c660e9d86e84a02e175c811996ffaa1631575ac567c14a4856a7293b77311a73ff66aaf777475aa7eea6ab5b70689fd39948185fefab2fca5e696d56eb2574e96d5b688bdc2396659709dec0c3107fa6ad5759c657b7db503db9281435b6f3208236606565a0c96bf52cc1531559ec5246dd1ddeffd8bae08708e743274ee0f5ee3e599252f928cc8205f5e2049311e06c51ce9be060c0a8241324c1723248a44b123c0ae4a46c783151a2f2faf013feec111993f5d6ed13d3b748ae4d9cbf2811246c7ec15132bda22f24ae6d4422c9028ba4c76b5ea3a7f180483a24bee487f5611f9d3a77530bae8ff718f98a5f982cc43cf5c43d5da75ecf163205f7fd1a1f420cb07fe0398c9dfea3fd1649905ed08aceab7b2a46b12075d4fd69001d2b2a4539f9625ad65555bc4d6fe5c079dce91cb238b5544ba4c427a815cb65023890a2e92ba24119164826e523aa5e8828dbffe5374d186e4d265cfafa41b1ce74be2c18b0d280e303258176864d1c50b621530303dbfbaf0228b28279982d845955a451744b48a2e9aa85574b185061f1082073118c38a298490420a2e70e822872dc67c41e38507083f042d7126a98c2e465bb051a9d8f1fce2a20c2ec638b2a223125775c1e9a2d3a50b9c67b8a8820b325c84b112c51428a421841a446c2104251b6c7181e90a9e09184388881f68d08315404194d9d0835218389041121a222d25e85e3ee7a473ce39a7aca29bf30923ba3859c14490953476e0831d88009de082879f2f2ebccc29a9f0f2a5cc73984fa9fa637574565b21fd18adabe8ea3a877d3ebfc9ef8f9f9dd3de5ebd9587d198047df02c356ed861a158c623934674d2078e61483c788c716698327ee5381f7d7e2129fd759d08e60347689860638834889660962c8ec8b26022870e152ee306439851010e3818830ac492c596051036483188448058e69c589753ab56b3d38154f47307ca23bad3f30be988edf98514c4cfe71752096ed0ee1519ad0d59d8b3f42449c2de3634106c215aed5657e80c215043c805a6812f7c18e3873464600609c8c40a42c90456c1c894342c92281634801069e8575a0ce1bbe797163df86205b2caa494ce9c1f7210c40f9c74a1020c221a90e16d9195806261d2628c296aa54e64272a402e2802134510530061460dae781182f362e544258c971c4e5e4a40b9a053342daa60717445182604708426cb135dc648410fa01258f8206271c68da4f2d2828b162de081c63857267dbb4ca2bb4df4ad145d940c5ca23029e6d02031f48c92101c624224a4135d984bbf3fa6867970477a1e7330c76c0d34697a2c18a44f9fb686ea72877734b861461c0077309fd2316b0375e935c902033c038a1804ed5497dec406ea98576b03f51a6cc0a4cbe9d2db0041678858304ca73e93583048a72e9db64bafedad2f93ba312174a2ab77d83514d1c31ae69034c1bca329ae861ae79c77a886b9e6bc93aac19452641e3a5dbac1e13c576ea4688d73986b9c6b9c6659d34f1d94049dcbce3cf4da425da5877a8a92e89c86798dd7d81a528e39ef708e8405438ddbb88dad817a8df38ee643f8a5ca6d52aeb211d2861e3a07a73c7495c792ae52d20e02f83e4a7c90a0bc3a12939f9ce74ae63e3034d17c7394c360720d7248528e39e49050af71234e30afd91cf31acb9a8e0312318939d0535e106b5a2337b648e571ccb1f158d21a317ae8f0c8ebbc55cc813e2080efe3f300dea9711f242c184c8e7294ad417393f30ee6485830a06ae01ce5bc53e34338e5d4b514f5aff9c16ac71c68478bbce3c4e4288f30686ef2e690708ef288647393455956f45392c71ce89b57630e74cd63456b24b362308fd5d608d08e0d3540b26b178d9bf217fe9245509445482a4610a143040e1144d039274de28328c84093c68715c00022c145098c7b7e5d7174c516635c5174c5115dc552ada28c04c0f3ab0a1854d105ab020b0a660ea164c9cf168bd12ff51a1f0440423277ea57c6db8fc187d05e76e99a07894cbdb25f6a721000bd74593dd95f0f44a83b73b147baa9277ae62dd9f3b8621815495d96ba90c1eca5b6a70773c845c7a4531c481e24148df993cea2ceaf2f527eba11557f2c6a85bc6440f4d28f2089ae7814a4852988bedd85d71447df9e84c9d0cee531ef440625fde52527db52505050d04e167e4db1f497d11c31fae23d008b92f7002c63bad550c847b7e011df46fc55e18851527ffc6c71ae60b6d3a1467f7ebbda2d761f8a42e38627c00284314550e412fb021205178935b458b94248872360115c5250b91c2177f07245182788c07c01c41448858b10171fb81dc24881860b0e72072e6af8206b25ad0694cb16528c1465a4702283e092a5b9b4204a212e5b5e3ebfb838e1058ed44147eaa05384eed6e9c34cc75efeae9e8daefa6525196243313e30fe65a582cb47e74cc76aedbad5ea7b9d2a85da2b59e5ced182611886615812cc032ce67b30af987759ccb3981fcf3f4c6ebde1d0de199efdbaf07c8dfc11242abf31b0069edd634e9bb49b1a8fae1b9ad778affac3bcfdc6bbe179e760ae795f7f588d7bb7a04d8d266b7f98db785b7f98d778294fa73fcc3bcf477f98a33c16fac3fce4b5d01fe69ce7427f986f9e11eef1a37330c7dca459ee0ff391e487aaa7180db3f7fbdbfd7c9337a1888eab27ef8b3927efc67c73c61c730df39307391eced6b8417bb7bb6ddd5370f6ea88f9622fe7cc25d1790fe87ccfe6a6ab13ef66311bbf4c1cd638db9be453333cf4ce53a1bf761bcf4774d5d8b6dcdfad2614d1659ef23ce67038ab13c779175d2994b7a73c8e39287bb79377b7bfabeb7f93e0bcecc1bafed856896ad984d498199a818a44e8d84f5cdca4364d346366c810c2facccc32faadfdf173674a819451c32085dc63ca26d23729e3dce2191a16030e1092e0bc740ab5bac9e04b537aa5e1651b3f325e0cc3b018b52ad0abcd83c612bad05f1bb93a1fa345a8842cf5a551cc3ae6cc8800400000006314002030140c898563d1703853173f14000a98b050604493479324486114840c318018428c210000638c1918aaa90ac6ac4cea287ae0f8abe449dc757ccf8d31e4de0d0000431c0d8c198c4a027c2710a563423b08e5542885e638286f334dd83ed7a2e3b7a6c614174c6bf81299b3a01ddbd4c071d477dd45608ff78d50861fc1021970a4571f26446d555946c806fd7d054f33dd14aa2538a5284fa6e8242a208489afc4480165c0e8e04e01a89ca802b1da3d1eac0f8edf856c3b5b29508c6a74a711a21fee161c2c82186c4a0e9a1707573581927d90aeaf424031c8064f6f6fc8800016ca4083dd839d663d82286f1d4912e16c2731b185c8a99a00e8353d06b24adad97cbbe63a26645e8a92a83443b3f5b9713896bc76a5cb9f85cccedf120356342973bafacd34cf12626c65374e4bc8261acad615669f95a71c70f678f62732126960e2016753ac5ab835ea0c7c53ec88ca8c0ec42d9bc755ff3508d754d4544d028bb20154b46039adb69e801bd6d9d4b6244719c6bb58131c7675d43cb00747a64583b1324ae7c8df5326bd6b8c5394267de84bcaa7598941848d14a8cafe542db200a37f7d09a5e2f7f5855fd1829dad66ccaea9b814e1179e59886a6371dc061f5487c5af6f69b6ae1fb858dd9c53c3ff54640a90103c89e0980d035524f9050697d5b3e0c8ae25ef96f80728171c3d77a84715c4050cce93101824332717d2a1d388e54971a0f9f4c3b633bf5fa286a29d0760b931bfc9f7fefef758e233708ebee76046f895e826702c5c00ce807040a26b503c7dd1ee63ca824c75a2e3ec9f9662321ec966a30b8845cb6c09f153c66873570d2011d2098d236791887a362ea2175b122f645c071c474bf83de35580fdc5c9235265160ce7f268c665eb84dc004034a3f58ccfba6b40cb2d6ad83c8082de066d49dc8b5c490587c82495a3bbad96a1c7482defbe32c12cfd578288e5ed63d6f0b0f0ef43b77a1e3c3f2225c402d4d5c90d322163d11d3e9304728ef0b7b8122add16916c39bfcf68a7af5071e2c8b08ae33605503d176ddb42ca86bac7577af2f1b762f27a6fe40200cf1aa204e3e15bf1940435124b4590842163abee93227a90aadd9f00082d5ac1b47ce62790e5e44e9965de73399110c16b9d379957c4e2899fc70050c702d08c94bc5b76b81037a873710f37a40e4d9e516e6893fe672c58c21a0e1fb8c9eae318a07fe89cbf370978a675c3807d70ee1e8df5bd5f7462f642fd7224a22f23a24609359e1870c8c96994308cb1608e0d58c359e51b925ef2679de39e3ecc3eb606d10d24f910ddf7d2e344891690fbb4ac1b0e89a382da6afe36b192c469603ef09bb0011ce485164e981ee8a54faf0017e8839c343747325e4b374010012349a5e0f128f797227031b0215897e3071a0c8189fc9e66b80ec1ac6d1f56cd313aebe387506d673de11c994ac23760ef800e3270bf383c0990b245f114ca19f966d1ca87635e35ca1c0a7c68686d480ae99b400901b94d6e356c9a451148cdfd3c404655f79f21606587260f8c76839527b0844aa713c0885a5480b887e1580090df5c1d3a8833124a9688ca5a0abfe0b7f144883eac4b8d450956c7c01d3060be49ecc8180370415f1a1fe34337ad0d2e4edde0877ef02880c634c147b0cb47ded6123a2ad3ecfdcdcd9ba2271db77652c241197d5e39af48d62ee36f4fa546e42a38608077a6e095223f118b526be2f4bf6a45d1b1edc5f54c1ecd1478335728a95fa6500608aa31f4a7b87a5a07f127b975f671750200784b12f46ecf76c1735fb9e620f0073213b40e7502058d79fd01a3d1f641f91e1093d52e43f4406ccd664d6890ae6f6d2cce848156fa88976c0e9b54f83f08411f2eb8a6d65812ddfb4b5261676603a16f091fb552804aeda63e963359eb952e1dd4d1b91366132177b40cb8a43c1e00a679d88d4b0b66ba042202e7b8e5253812e82b19660106ac848a657d7f6fcecfe1410639949a730a2b9c9d3a92e40073addde5f1d1bc9f9534edf15d8d470e90577612f56922581b72c5517d16c6a22f2c8feaa97a241200ccb9d5d1ffa8894103ca9a5e20aa668e5723ba1bb066a08fc0720532624c9afef2c90c2235f6ea17b89fcc380efb5303621e2e6646c49e443f14657ebe25336806f9af5517f4424550d64dd5c426ea8facd16ddd9460d42691955e2771c450f89a3ccb71c740c2d6ebd6e9e2119bc1fa476a2110b70500e6db406835f8e5b0937a088a61b49bf996d870666b6d276dbffd7c732d363ef0b7d0c74ab4f2af40581e8f5efa3689e73ed5f987386560732bc16f66fba5ca7d7823d64b6377c464fb8f2e1a9dd9ce736b8e77eac574642a0e073c73a82a67fb902da23de55687a1b6ffa40a24fd0dcd434718b55321c6dd04e5058a1766cbd3d8fce80c0407f8c7603e852286dd9a2c3956b22256f4ce99ae9e67ecf642738e96c1423c3a5b217134f25a5e24fc71d7e472d2e1bdf1f51f8ac814153a60c10291b04e8fcc91c6d278ac6cd3e5307f5cfbf54a4086db5c0040ba73747f4804397b0304a06400f560c10a611d08554417f4f06af06c283db05a5dc783207156b137e2b6039f06cb8a8b2c060939c47c6a6f1f7b78b909d9434115b3d4ad2602b9883a8d3631684476d26e86c782a11c1e87a8893fc21f015b92b796b019e0508b98470d0b1bb528c55cf0af9679f4269f91d06b043f970967df1ff947d024c659af24ac51efb31fe603bca5a84ffee92c72365781c9a4039b0b1a7c99665275fa756284aec80f6db95208d327bae380884007cd58c6e040eabe6780d82f4dfd012dc59f151e2512c78ad056f07123e8b4d51adbd768889e31d76da06a36878d64c9848f6f7c76eda4dd2ab6b118148082722bb1a10182f563e13d2a20bb821c40c809fcaf54200a529d2e0e8d6445d9e77ee3727384880f583d24d252e703f5d687d8335a2d1eaa5e1532ee7f370aeead89a9f7c2c461d80172422fd8b595a9c7971a620d676fe7ab3d4a8eb87bea77045aaf41827ab4300c8e86c4e6610bfadd329b4f5f7e88236d5a024e90cd9e9aac349fbc62faf06dee4f198de1883c48120af87edd7b7a12f1ee56f78582827ab63960d48add1c1730d7bba2e0ae3e9c3f16470c468b9b49533cbad92435bfad8627a0d3d73ba72d420b604f7d3ba75b6432fb6f73468257a6dbcc726fbf418ab568fd63f1dd17007d812241c290a5bd3065209f25ce45fd4de1c7bfbd9304fd7a9a3b6bb6f263c7a3699e766633a6cb381be826b0411b2a905f0acbe5a811fc82fd7cecd973bb206a2e2dd8c119036958d92e580cacfdfed483a0249a4fe8015da0c5a4ce6e7eae660aeaad9ec706f84eb7dd6601ee90c16e210fd457b13c0abc656e87f4fa6bfad81185e464a9a57e92702bf3578cd4f16c01197e18616dc3ddf7adce6c426cabb9a9a62f28809b7461c5d6952b43c7d9e490cafbe0316b4ad46503fcbffeaf29e534f1ef7de835dffee7093ec31ac25e7d0a6b2ac4e627947e8bc3a2186b0ddba7f4fbf83a5e2ccd46bb8b914ba271f25440c7e9ecb1e67f8486604541d34b5173167903c6f6c8524d2b13539fc1066cd7bfba5cffa8d158d7c3c47aaf467fee7260aa6b78437feb72522f151adfd41a3d812eabde72571e13636a8f4445c1bbe8509f4524780064b132c7362636d48b431f5c78da0e0002c80b08c303802423acdd4437505f31e9378c7914b540506cc30d7b6bde5e2e9425840985a9f46e87455f1386949299896fe787b3ae87f18576368656010e8871c22282cb0645781fb599c2f04fb3fe8ec53d4890fb9a26e0ae9c05e566d6574262f05a07c56cebbc967c2821ee190cb93ff04217ed95a8898ef32980d0659dba022f4312ce98bd71bf7de990d4ad61718c7a396e57160288038af1bca213d49a39ce6d50b5726cdf31b8b2b252015e1665ded85dae321c959f49fc9ceee0c2e48ac3cd9880422bbf57df04e770c16e8fc49c520594580340aea4e89a940ff4a08d075892bd6744f6e085ce029febb4d308323e98bec0c81379f4a984d21132db6a50e935285023069cad5185b1b32156d5b01d010d7d970c2317fe89cc5fb3a31a1b83d2a07925d0d94ad9ee47ca6ad0ffdbde3357dcc0fefae142ac8c5c95b31d8640288aaaec39553fdb52ac22f03f5e4ad94693783c818525851269309b47f61a126dda8b5b7a74da9b9a6f775d0f658071cc23bf50fba0d3b1156e595bdb2f8dca195805b6d097551d33168648c232564cd0a655d6b623c9cdb1e961f01b3d7a0e7f27a8b80eae266cd8a3ea352d1db3260cf184445d192c2da03287a4b459a17119874505077211c4b7467034cc79071ea7bcad7de41dc85d8e5c0f747c788f5da27c49c3babfdedab934c30f45a027c19fb9ffd03721d0a7278c584decfb3248aef90d3251801345713b03fefc616ae57ce472084f070776b4d4abd98a09ee9b8e9dfc9e1ad5c4477640597a8f71dffcd1921668ff6b3ce9c1fbad58c43f257f45f1fb9112b7a974f14502cf42fe38ede8ec5fc484ebc2a228f14c941f222584109ef242655ac00588f68ee0e8da085373267c69fa4f05f5e12bdfad851652d0e58ea997fc9f7fe6551371aaa04c7764c841d3c529e422b008442ad9024f795cc87348b5ab011a0bb2c527536da4d65f94cb286ec1a0977e0eca0cd6c0daa33ebbbf3548ee82d8c975785092a79b6c6f99202b0c422f770d56af47cf46729d7fbd74bf678272b8d7c1264aac1e01b4d4d9a8f0d06d0473738ff0edb51c9d77c6b609c83a6361b124ceb2d4e2761e312c67d102e0bb063e7e11d50b2214923cff1d68a60eb9387043be5e7a29382db134b84327c03454b8f7d1cea92cdad64ebd1702d04c59fdc01fdaee9cced8a7ad10823e2d0a9053e7f244e557c4912f42fb729d2024ca0cf56bea500fc6ac11bf03e318d0559beab199c72af2b08545498ed21886fdac021a8b59d1cdd8896cb9e04dc5e0986c280b2172d718c5c71ae75d7acc4caacfe77b23621ffebb71e69e07b0dccf5f8467a87426715f02b642a02b2ac7bd59fe6ca8e85e9646276e7eeed71eec5d2606688440283be3d8abacf39fa0d400e430816a5eb375f64d83bdee7a900020c154dbb7586fc666c3a056608cdcb662242b7955047dcb13da37dc6ed6153b9f76c9bad43e0f4a99f144dd750d5287d86f8e28b8deb6d2e79a8e5e171e28afdd9a7d5033d5bd91316b96207615feb394eac3b36ad39bd7310125b978cb2ca19bd4ec9c15073b81eb2929452f93b343a61777f930fe5a6ee0d4bafc9455a1e16deda137d06bf736ac7b905ed902ad3e8524dd58f6dac1c7571894d0d7a38750a03ecb7a208d332c5d83703229156e65a0746c429847df001ce47a440b4cd6a92f21c27008a05332fafdd929b10bf3f684159e442f21db24cff3845bcd75ba3f83583f7ed027946c1cac173ace3d9b73dc842eb6536145e8767d0f57f133d0ac30ec9350e0398e4dd4314036df855887cedc27977b28b2a33cd7f3b8b12df974ba89ae33618a87daf6f24ec99017069297f4506e994fe043b815eb581bb9cce6e85afe95a6e015d1e3edcbd84963d16db18b09df60858f7b33395dba3f8d8345776593cbe2238a7a4dbb09b85e96031d0430717119d992efe029166d74ed08e416ed8a0ebbe49f2ff4f8edfc6419f286210fef43946f34e0c782f9daf8b2aee36f2186ab1b2f8b86cfb78bb651285a6ecf45a086846e263fe82004a52476bf2d13aebd4d4840d435cebacab184a0376c5e4a307b7dffc15e42d39bc1104d160dee41e4a1f4168268625a352e9953a61f95fc020f47df08c7985c79f3fb538453045aab263cc3b9a8d6d9b670a173be7954b34bb84a2f79eec84e14f160b4d6d43aea8713eca1da8a84ed82ba23302c2a3454642dc6247dc4657aaa6f22e9d28095fc9dc0114cc253d6211232b250fc2eaf1171328776cd2ddd84429730d216a1b01f0c71436c7a3265a09cdc1c4d49450ad942354733bd46b0cb0e8b688ad4cd1c8d868a0e9635c1c081464a113a4ce7df1a299356cb6cac88af7a177b988f184061f63cc021f85a40e699e3ea82183c5ba4549c8f112020779f36213619f9eef84d67a68d2a18da72c2bee33c66d1ddedbd9e2dd25cba5c2c6aaaac678b10d85d105b2be6261285e8ff34f1067c753fb815a3476cf0c4199f4002e9ecbbac0f129e7ccaf2889e75baf89a2456c31d7c1c2fcfc65a9f96a59524febfbe8e7205bfd5b95364e956aa4797dffa672c7dd0014e711c14fd4c525fc8ea8982c76e7234fe70d6674f9fb418c8bccfc2024e4c331a203726d6fc190def65e3beb10a5753bb5635dba90a5b7a3ce84f1aaeec17f42256b175773caf74f5e80ae89872361e4f66c06c479299e39b2c6ea855a0df0ec76238cf651ccd1321f470dddc99f67ac794eebbdea43de271b94a2b08d6f8626539a4058aea5dc6ac1450b1964cb9b1ad0806bab3565706baed62290c7c54c6d00d145e0371e8d6ce66df5f0731cf0ce0b96dfb44ea63b1c6fb592d5b45538392b76ca267ceee14b0460574df94c1cc673bc3c42e63ba923c9114d31a0a92f41c2cf4e0c82dbcb3d67fa2d1768e0afefce74cf49667274e90f8179fef486700ad346a1ec2280fb4ec322f58870183d2a3c8c603a20ffa5b0a832a961402836558d0b78e58d83052478aafbe280d5913f9ff309ea2f74388c787e9962eda0be02524697854a86d21a3410682b5672e330ef97cfbab276c72cc3c244c98841f7e9a301f56681b326913d4752434c1ca0b2e147ba58695cd91c90851bf1f8001a26247d8540b20bec78633204b028b5c6d3805e6494059b091502ef6eac665192443ccf078f3a23125ae6ce6e849cecd262b206f63e5cad787170f8f91e6dc71ef49e577956a74587651af1a8aaf92f1f0d855144e0f429a1d2934fda1ef9948e4de61413f56b03b2bb09963fe88f05970bee8afa0061b5e491fb87169c4f858a9cee560b05f76a36f4e76d0752f098425024de47044d421d6e4d08c2c75b28f070ef7d4f749a6ba552a50501b41be97c62f51a1b9e349f3d8f7a51e07094e2a496319260a8186616572204662195a58d336e5c722c35a0653ec5b004639da8b4f0fe0e8c2c62235cd972c987751e944b911aeff4dde1078605377c1bddee08122504e37f9f18f0a24c36db305f018879464266ed6213427c4c19da2038e197de0ced111ee041de09c183d1182e08ac2231dde2e102c51dbc4ffba4f45f2746e5054681eae8857c5019c40401ec37be21b5a58963e53b3f9fd10c1c689a10a44a32e36cf21835b831f45d02a6fd0f2eb245eec399d5d02c04c4052e6c775fba6fda18d72016ffc0a709c80cd13604b0f703fc9a8d66555b3f01abf6640840cea590f286de619e2ac7452b8f9cf90948f09ac139905f665fe77c124c83b8a7d79403f83a91c71a8d67488555c8d8ba020213e0e74365074971a1953e03319ac636840f6832903832a58673182484e944662241456f011b0778ea876c81f6dea69134687b37f8d6186e3ac832e713242a9912079eba75097e656eaf1be7837d781e952b7892ccc2e67701a687b34c63857b529c661517ca263778f3f1367c4c771150613444e6af0821bdd26111a685f3a07a20ca2bbc6e8ed12a3f4f95dce1b6f5a3fffecdab13783e88fa571e0ea5aab1a7286b3ffe0287754c45a93f4b815d7b4cc2b1be14e05b008a8d8c33831baf842d2dc1e01e9fbe8df46a3083bb5b275e965f13932e0c49ac0e0b556b586760569cd0de99d09c57e7b01bd30a52e57eafe4e3d087fea2a5f2ef410ffa7ba76441323bbe84556b4c3924f3ac20c26c88f7fb20d4d5c74703921c815d9a3461f1610ba3441fcfa74e992676f48a6e7726ec14ee40f18470ac5193a1c1a88e8190c7033a5c30a74e8bb81d38bad32c4bf24c02aee2326ab4e2b56a23fa12c0bd808676e239ac2a0f0f638d23c77ce262675cecda3f36c2518066c5558f17fe3f7e37727a1d0e96caef18f17bd3ecd5f9fc8bbdf7aa6d03e3e5df4a72e8350a257a92ed4a7950f805651d08a3f6a9554007dc2f9db6683bae2f454fb2a1a2bb38a7313edd41114d913b1ec0516ebe66e7475a8f4550871a8fef25a19f6bbfea7bad511a73ce962eb6d36469bfc36df2e170034be9d344d013a3f4f9395226c64f08fcaf7e3d98fa060ba7f32ae0ca8cb0f260e560c2b9e9165ef2895efd6ea145843fc38364445c5820b627d83154fcb8339a437a5d0cfb0c21958d07c6f92561e84dea778cc7b47f5047c06e9ee30078088843372bc0b7f4b650cd6995225e1760d7ea18a519482ba5c96919e00be75cf64c26cee9d2862b2d33a4027484f11a971785214d3dd316d388d560e61c064ec26e8be19a1e3af94a7a3fc4e059093f3150e2bc0fb1e3ef71abc4f75808af9db47c40671f45c908445797b43c85d341e5479a98d3efd26aecfcc513fa8976bc9d38b6da25556ff9ab51b5fdeaed0d33382bf3f1fb42511ec33a50fa4ec6f74e87c100ecb4fa5f0e4fa081a849667f586d3a556a9dde9938f1147d8aa8f65997d210d36f4a08324367cc52f17f5c19b98013c8f5a308feeb4faa3495d308de70aefad3be10458a099de9bcf34209d0ee74e5915a64d14e8e6fafd1fac5e448b7766a98b51c4a1ed139188955f78000d24f25e0f590d5c94ac2f1132359e3afc2471543ddbdd8f91ba64b26d9e9b54742fc6a1d4f70b48c47104f9a5514dfbaf4e71f9ec6173cc340fa3418356e37f7a5ffeca4b69d96c9dd0f17f40abaeb087255836c1e72fa6e2eeb56a17c0d4aa834bd8e06da5ab8bf6f8933ac01f9dad53f248898830ddc9b77be29231f288a4b0580009908b89c91e64be0cbf4550a3894302ff155ce3c28873952685433647cad812762beec69356e619798cba70ccafae1ffc3fd650fa71440e713106839f6e1d882ee0d293f848d7ca3f82d0ddc9089849c4c25a202e110829c657f4b205fd82241e88ff7f0cdf8cdd3f6af507559f50d021e67104bdc0d2d99224b23e252e15fab251bd05aa3e98445f00e9c839f21397daea53561da8f985e4afb54186573657c45ce039e853a643ab3f34285a54540bd52ff4cec259dae0aa58978d0532addf7165287e6388c27339e5580eeb42754e2c94e856ca08c366814ad255d383a9ca5b77fb5b9e47390fb9d2034a244f52852bc89e1181e0732e822d7f9aac4e5ef683b0c4df4fa3ebdaa8ffce283f660a565a5dc3263ea1d33756d9fd41f9242b7ac65948b4c432a04c133543882569643863b9ec852b6b7fe448aff28103c2253b0042b7814a3f746c7b1098977058eeaf6bbdd89c7ab82d3a1d739fbf14fd88911bcad82c1b58c88158403b305e9d7c8e28eef262270d7c09b851ace69cc2bc9760ed40f7abb650025086669f66577175343bba7481480f996cbb9e6d9949f025bd48abb79d345fb519b8f3fe1ea69c7df5d31cb98f1107d9ed8abc9b14118ea20be952c10e914fc87f7db1d33cd219977053b5655f8e1a9b6cd8afaef4b7ce3568da4d940810a2cb407819fa611014f06e9d5ffce04a0ec9599d553f478d777c3e8f4361585a2a5cfce682d2a5bdf8d8feac5db5f3560dfe8504cd5b6c062853d159da9862d0cc79c5e9028d3078452f49d6d5a556992a4e92ac395bd7fe955e63ef7f190a028ed72d438bafa9f7e4c4c496d28d4e32fac643d0b480c1451b6f5092e04d66a8b3a374fae523da9092f3ea5c3905d35d11a29eb88ec352392dd51246f837c654046ef582ee0b458db9689aee0b10261329845bed7980207ef30a02bff02c6de47781ad2acdc004c0cc9b20f6f4d2d983b3b772678cdc3e2b254bd4adb7d6619392d6ccbccfedcb53c1af9cc5c828fc69148eb241ef8bf4f80667be75d445ce305247116ea5d86f956195de7811171d5c3260bbea6e117fc81cf258bc8064e5d72630cb6f525f885ebdea74427d09042565c74e4d462f53e0be855427a4f7a98b4f4051816e1c6c616fb66d8542d2734c8252910fbd8f12713ac160c02b620683085c1d6b31a7a6803347e3cd2cd3f5db86e7bc74e436181874eb3aafeaac6de862a0f38780012bfdfa3f4b490f246080488a4b168b41f432d86da29cc134bb407e6562a270045491bbda757f09b51ec67126dd9741ba3ee0438cede40dca9cad94a6200475f107149dd3c1fe16fdaa6018cb959640d66ec3a9be52a0d44f075aef29b8780ac62d9b9315de691694312acf6648c3adc165a531ba47499e1897577ca8246fcea5c457791cdd44b362636af819e09a33472e588c7b80312885c1e82ee6ae684798932e62a0a4c3953516c724ae74dbd9eca44bf40a28367eaba7e0854553cfd0ab77dde809b0b85a2359b144d209e7a90daed2aad86ba68fefb2a3268572229772fb5aa73cc921ee98489c9d2e4f4ffc7884912a7981a21ad6b1c1d21980b94b6178882b21db72619071c224aa861465e9ec94b09412859fff39a3afee56f3d52bd9b29034587735b5b1c4b0817b9ac25f8e950a7334a3b47d941cf4606308d69daff36a387c0dae43c3c8e951d7ec2212539398c01fd47e4934608fa8d4f1f2dd9c4df7fec8a7f5bf7931229ade6cb6a4ab03884637371323f90e97908208ca120245ed623740937611fe52418b8ec720c8406507530211d23566db7d80cefbeff4c93123664b400ad4dce80c8009016c11ec77334d7db1d7cb3468969f6778890aba03d477b1e875186ce7e8b5e9580a52d29075a1a116cdd74f65c3db7042212c998f14fe8cd8187b184e30609b13bc30e2bbd9884b3a9c0d45be10d5c439e98daa062a90c6c08030c82855a0e86eb082025f99c4266b4bb830cca9a308ca5f5d75fcbc538b0990fc229abb1d858dea943fbc9470512bb98a33eaeb6cce6ce1a0653a175e520765ff7e89f0030b3a50052757b06228eb666036c5b9a1342842174c8a8073deab1dfb6427824e3354d3c5fe4ad8f05d28ee80d2f1e014330ad062cd0c98dfa0e03aa59c726a1f56b899e2624613c4838f9257dc3352395049b18c422085205bef0afef065ef008ddc1494fa5b59173b9f436e03869e51fb4fe6eb8903db7a60e6a03b20a13103a30418c995d4060b3863872e2af22a0312b99ec4cc787981b606f9b35f3ee4866a0e205eee27e281209d2b3e49bb2724236112a124f2f5e2e66231d148f544c17c172d29371e1f885d9001ebc643cd991ff1bf32407195033ed565a5863df97c0f84f2f0e17a3830b9e028deef1f1907e48cbbcf01cf4e5a67321b61453a044c2d93e9f822363b4999b6f547264f8b4a2e12b1b5561ca6073361634ebd637b017a4ffa5a969c5975f986bddb602b4f9fec35952911a38acb18a3e4b002772cd783635ddd344de8b34b503cd4b406e49d8478a75c8dccce7b6d3af4ab3ed9b4a21e758c403cf36cd60c7f55acb8af1d8f4389c30441e176f751ad30dc0fb6b178a74925c798b2a4713dbd9880bb8896353ac403e26a5aebe88805283823a16823b8dd2a50f00f60d21efde02ff81d1feae3de49e66ac02d7906ba53faf5c72ef29dfc817794ebf95b82ac309879c458363622432d5ac02fb756324aa0e13f281ab2d9ec0b60fee3660121125a3800404ef1e1400854e3a25a3acdac7765c24c29ae909ef0b8e1cb2a7834bd80ab3c4fffe0858a20b92a8aa5dfdbc486ef6382adb9dad77086bc3b831ce87f25c2cf356eac0c10b66b9656007f2347d9dcfa1a41f98f02e85f8868793ffbd61adb8c8470a0fb75c298d0ce73b76563fa6e8c2b1503d426f14d5efd49d38ae05188dd501836149f0ea89bcce3604070e9e619d694e5b1c7b2b1f92c6660f8bd1d14981decb0fe768c917d297ee8d887a3fd5a82d100e5700cf8aa5b352d5c38ab3d10e96aebca1a03f0b5c3d2bf12e7510eb584f5ae6d05a3c9d12e8c6041610c356090b90ecaf79956e4c1ddd4e6accbe52ad38757694bc08cbedba4987f9d01bce0b1442a4484a43cc5cfc17ac468404f5d14e066e130b3e2448ba8fb3544582eea2f080383ad38ed3f1011723cd472e2999f39328370ed987955ef64e7207e1d4cf50238ec42c03aad7dd6ab6eb68d6698f8701b9f9c96073e40d456b360ab936fa6df5427fda751c00f3f05457e5bc6af6235903914d870271e69be18c03202dcfbaa439a81799bb1e011501b735237310689ebc8eb8a5a7032080b06ee68e0667b5c32e4f0d46c4e86f22fc2626b7fc3b2f0fad0522c2886c58f8ee7cda7cf433a03017d77f440f2da24b14fc5a6048e432a99c2eb64c7f80830ee970a3d48171e064f7f7042cbd5f90dea6957ad83c7691cf70945931185c5f3a9c43e4ce110ceefb6cf09ea75d1575ebb186e00291934c2d1feca11a4ed573a647a581caf6aee832c821d63154cebb45824deece0c56b1ef2418c6c92027407ae37d0f5c0840af00b23430476619e9e903f02ea51f34679dbcfe5fe970c8f20513f1e884fd80d048dcde1aa6084e5aa51c9148e64e1fd13884156621b3b89b2a2cf246c2662fdeae770f69e5bbbec0ef1170242fbed327a1493de792f31f9c750780bde1579be9d75255de2c1ab0c694aae9823ec74e7dcc514f53ad81a58f691c695455d5f65d0528cd3a641a162e3a61751225a0f27644d93ad52c722bc0a6397ec920d9c814bd43cd96ca882a9a081da5ca79ac7e9a04ba05db0f70545a55215626730a2092bf7ab1d3051e62ba99479a8ff10b23ddcfb1f23758298018a43aae512fa378625317a834e8727cf992618346cbc67742d6bf878b0f0a7d8d8aadcad504e3106b080317cfb9f187645b3c333fee57201fd890ccb5328138e52b62585a7457c0e43d1d4e1b567685b7fec6517d7f98f709056c9f8a6899d591a1ad0f35405996537e19dccc24c1d72032dfd1846b7b413ea0cd8cf3a34bfc2e0e466bc3e44dee01a18249976c1793cd7bb93b6f689236e6cb0431e950db3a0169d7f9c4547b7620e4b867d452fb31f70d4e4f0a812c2c6385a9aa7f6e84c9c8a19037530e08dd5f7cf37a276d84a91c41d2d23af303556121053af4ad5a49f3ac7bf47df318daaaef0151711114dcadd9b86c21c00ce0f8001b96e335bfae36c7e055666ae1d6172d2ae68abcb12d1cff08490ff9d6ca5e74317f66a3b6202456ae1b77006fd8a5aff0ac5a7aadca17398359ffdb0b6743b32b566beacf911f9f2aa79dcf72ed8f136f140a9b0e1ae0993d2c93f867c0a5dcca506f0cd656f1167518da4e3a0f5853961539fa7895dd75137b788989166c3300f42db16c5a2e57e6cc97c5bba6f5ee85edd1a5a770221efbf723b00c6c849acc33d1294ba87d6f8d2d42cb02026f5f969182b0c5cff49cfa0f9bf0ddf1ca9a6ed41fbea3c2f1f205b2838e0ad93cf8f61d41a24681b224a1869632868ec81010a3e92ea89c7dad43986db8f1b0026a45d8ed90a97fe52a1def3b7874dd3be21572b93122f819e6753ebbc68612d18c12f796aae1ad7a8000cabd90d02043a377ac7aa04de160ecdade8f512671e8207dc8ae232023b39f96f34e55df5afe3f0ad87181341b0b527c8a704284614e1eb5fe53626fa5fd25cfaf34d47f9a66e4b3a37a0141174a444fc9529b318f7e0fa958afac347c619300e67a4b3892afb46b85d23dab1b4576dd99dc7523284f5e8291c76dad8ad5956418c0209d4f3fc034a82b7bd9a6644ca16aa4118220bc9ef04304ec89e5b721b5c8bf05dc35115f849a414aa2db132b780818ccf1fc285a68153b6dc4707ed2ad181d2d07542524fd35b4df353577adcdcc9683a0a7796479459a88e542299a9129fef4acde5403cbe50050827180e9a19ce442c4b045ca203da904c8153d911c432d8f12d9c1bb46f962ee43240bfbe1c1c9e0d50f147adda756b28336e0d9c4ee47d209b8b7ccafe246d9114b2367b48ae4591d4c9f0cfdef878d92191ee733641bc9df84c92b2ce755f14bd32ec028dd60f2ae5a68ff45a0bc9c27e23775ea6fe5386a170f4e559259305183168b421aa23d5829acb0115bd45f594051bdc3fbd96a4832b1ae08fc486bfa684173e7cffc9c4d61ce3c83523f766efb1afdcd64d3669c2302b302ece62ded708e20792198aff31ee82a1b0cb6e6e15c5ad33a236ea96db30e64151891bbf6093167b742332bc7715dfed13db796990fa6efc43e07445ce3ea4760f68e0efdc12567bff462f4bfc030f196eceac6f4ad691dae0f97f381545cb22df1557f9858dcb87cfbee24bb30a554cd935aa902fc7985ed915ef71e068ec3a7118ab975d009f521f619edf7e545e1b728378c05ec97c5b8412c2d60c0f0d95f3645217b90ec8e95bfcb99758f7b4d79fc48e7bfad6fd9636a8cae2b1f47a6ddc07e09ed85cd59faaf5fc1ba45ee8baed463deb5ff61832d74be40a9be7db3f8524726d1fa0b12a3222747cad9d376e5e4506942eec0366f9da85445d570d2615444c84c510ff3c1fdf969bec6c4eea49a2a60f277ac0695a14e6a49b5f199f6be7df7e4719946dbb3aaa865dd969d598b3b2c337f50236fea468ad4ecb5bae6b0e76a4b669436dd14a38720450338665065c783f4b6c34342ed0deedca146878750cca2e47361b1310ccca0045d659cbdeb863e7d133f7f428ca52524242f5727858ea980f1be414d71b28a314105dce3239f2f265394035810a725cd543725ab842ddcb8758b970c7f849fc881e13d5b58ba553021bb5c433cb16ad75de85425880490a467059a6ebfada32ae06d2d9d5a43deb5c011c3437b8ecb0ab614f4b15a53013846c593e6b7a832021d0b6143275f505a6b65105f3aa2cc05e307f16f4a698662e98f7c98fc21f534605f8f72261881b2e3bc674d0dbc2fdcc3a04409df507b2d364624d6b43daaca0d420d427bf66c2603ed4e96097f0bd2ea15baabee21e75c523d93729478e75c7201433226afd36db5d0a8710ce24946aa6502aca022016b5844e98e302aa70baa3d7acd843ed04dc7646e5197a6d270fcab14b471f91f240fddec4b8bc6bb5bbd1140797afcace155b9a862c88ec367d893f283af9c05c53ac563c50cda54bd423d72833ff48c2359f4c8b05fa868f0f0293ab7b69b8777a0f7113f48a9a88b85c60f2dede33ef1789afe113bda7f11e669a65b518120f44f89a886e67fe9aa62fcdd491d1dae8b3cad6d8e1644c4cf9e9a6c7584daa12408e01c8dcfbf462b18cc334196953625f6b81a1be8aeddd05f12cfff5f1fc78af892050a86eeabf27499cc41a6ef4694dee70162d20cc1c20e6302a848c8e80ff40ddfec28fbccc52a3050dea98d3441033f2d8901f09b3e1d9340a1d4c57bd55bcb7a457b4ce404f05ff6230b12e1f6ecedea9bae6523abe331a4220146cc87edff7e116b6caf0050111094ee6e07a322680614c80084b8a106f53df2f9a73a704430e7619d2610fd1a4a801896409ee6e6672264d1a29b614833f11b9cddefc611f854a9783325905820a923f6740d78ff7d18c8cb9917e28e010e3f8c9aeb91669abdf873b7184f4dde0e6d64574a2b767312a3165d2b60f51328b8aeb65f11adba09d33ba5d0579ddc923a7c1781be897991c0429472ac953a05182be0456a9d3aba3da14d967fda605f5de21dd3e6025734fe3b79cc5ed7bf323e4bd38670b1e458c80016be287e7224bcff622a0dd963686f525108ded8fa7a4b7896e338fd66190b4f1fe0725a71ee1e7c1b2274c8c581fed573c8d9b58e6402e2710fd752df5a03126c3a1b7d20db8961069206758e23344f761ff631d804a7e0b962c60db0d3c7bebedab43743075c84f84cd320c94bdc22a3c805c2d70e2238054dfdcc0c225953e1149f6003f3fd5dd86526a5c90d65594eb9e621e9c2fa87d427a0d742902147b010aa6c2c3dd627eb1bbd8be6b103cbb3a049c67ec12ee614aa5337a128df61d94a3fb8848924434051b6c44d3644d33b9272711a2a3663055e3c3a9de5ca16fb215224a83de055106c34b1cfb8c6a1ef7d177c6697c405996c9a2cc1490fbb623c2944980bf503ffac2169f7c73e51bdfba71e11061746646a6cc8f8864f1764a87db2b09ec9c29cbfd389aa858ce227da2c38e630a5119d363e3d06f1ff784a047fbff6f93e75e48a386d11b790b843f6286e28edd73ac1e4b1bca6f861e1244bb96ec160c2b520590456077fd3c6e5b2b9b861284e6936c6d65cacb948bef228cdc053ba773d60cbc7ae7ea332fee5d701bb7ba5ec6ce6ecc3684226ac4951c40d65353f01d12765a81bf35110232bd0e17916838d8e808928f7051509227664275cb4908ede32f34acdd1c890fe25e632a3c96d6033ff9f12f0d9eb2e8371dc058d1e9337fe5019329bd543022dd770c025fe3621c37313574dcabbb16375dfabc89e17ca63ccd799e5098cd633d2bacdec62872e179b5e2f932f13ad082ee9c56639fc207a6dfcd4fc8836b2b14182fc7c9d29c9ada20a937b4c93d7daaac8448b2f7a70143593c576d6ff5e2cbb65db63219ec69faafcd28faa3385ef3b593dd5424acc15e39c9f31be3afbd0fe5463e1e8cf42a646050944360e6dd4571d3b6c3d9bda5ee6be7010317feae0db6d6a55baf0302df1bf78f52c2e9536accdf40d42c936eefb30d77708f38bf740766757b184d172d7b3a64d12bf52b06a14cb2db8f9a1727051e3595e54d724da525bb8b47eee837fae93b928aef8e4d2e417b1565dbaaa7caebc313d8a2e8230eee23e1c11e97b900badf8c62e29306bac4c3912c73ca5806ee0dbb607327d0a3cccfaf771b6b46b3244723372b8d3acfd481fdb4f00d59f4cef54412de461281d6ea74dc083e5c91e53f9aa69d33d3aee03eaacfa1a5506818bde4beded321080384cf132fcfc89f96e506ad39ec757f97241ec966add97d4ad6fc20c1777c736e3f33708ed0caa24880e9e965ce4d43c0648c6290fc1f26a11bda6b2dbaae8fe7d6a455836d05a2f118a22797f53bc50af2812f65b0be77242e60bc53936221f524e1b51acf421c416baf285e2a6ab4ecbcd7d93d410d5db13c575fd5814a321a615cac6c39fe40723195047d2dbf5aab1a054b1454f091bc315b7eaa5887d1985f9ccc0fc2507be2cb695fe7f706de95cde511e9cb97ae9b929b99f28bc1615556c02dc79714c7954908eb895b199326a3ff625047dfcaafc3153d9265d41353caafaef12d2ba268b7b9bc32f2425673bb763b3a4dc3d7be62bb340327ab8c6b8b3799d68b38d07295e0d430e684021ee427cb08684b209b7122e0888c4b4e1487a21efc85a1095c966e46382d13eac483a6a3dad1f4754be4147264daa79b4414a37dd0174b984453cee5a2d50e2c92bbdc3c04adfd3c5348bc6a37190e54a9fbcaa458537c102235e1705a6cf2e337752ff0fae964d8bbcfee8a5ed909c45cf99613d2288acbd975f1a971cb158630b3f24091da9a7f5d7eb346f88e048a750d7f9155b0203e83a724550f1d5d583ea22b1b1fa1a32a886249ccf7f78c5eb8dfd575015fe3ff4e087320096761e1a0921b372c0ccee792bdd992faac2fda1d1147f5cbe6ca6c60bb90b39d9c90323d4bc4541b3ea8246a0e4c3267e377d98628eadb8b8d43fa20aaad6a46ba3990f39e9d16e6db561f6a3a9bbd873cfaf6c5754b547039610964ad0b1b03491f6259f9b0b088b218c2128c7f49d38dc341d95e098a670345ffc6dff31a44ee07ed72a9a5ebb3b3b3730520d7c3524fc1ca53348c755c55e0351bdcbbc8d6128621203426b883c9aadf47665888b72fcc360ce8fab3d1d06d05d07f7bfb15930ae2f114a1e81b15bfbeb012b1ef5daf63cf889e3a80cb3176f107350d7961652df2225ecaaa7133cb1b75f45c5e58075cdfb84686d8b7f9ea3360a918c7b637acb06452b602bceed1d4227d91fc0c615fff6151b8703057a27c52e5e44371aaa9e7926ac4b212c9599712d7a1e594d4ce40565a2ec4ff60f03e12365527301d72030c45bfdc6bc8e517b9bb6fd6da037585d3c26af40706a0c4b3f310a799dfb392509111b8803b70893b70352e39ee1671fdcfb16c34bca0811c5800bdb9cf6a6e7b52b8791edc78af860e15ad5aebb71fcb674e1430a93f512ab57dfbbd7ce646137e6ea966757b971fcbc3dc478472bd16173fcb7fee4174ad9a762ebff7c75c8f689a2a2dee3f73f9b08bac83258a1c77e17e6215a10527ba246ee7f5141445033f823eeec6b1a791671150fe9339148a50c11801f14b3df6c1d6d08a74d442c9e76087699f7ef4e31839eb2fc987a8181011be6b9d576f6b885610ca93fd0fcebcf639909b1156a195aa14cc09cb71a9a1aea83726a4896fd29f1e80032a154973d05bb98431087f2b4e86d1bbd8b6aa939342cd41813c90c4aaccc43900dc0af6d4728ad27f0ba8d1b52dfbbbb41340fff30445ee1b4929c1da3c6ff602bb6691079193809e64b0d8836ca4023c3faa20f3106308c64807877ec4b881b8cc8f4dc21a075f3325ebd5d288b65582ce5bf3489bc6a9645989ae91edfc51f5f24a758e225144b7e7adeb3fb7513a456798e2c87b46f5e72e63d8be6f626ab82a8834a3e7deee48346d014d6b67a419869b67258c6223c83266489e7ff69088bec1ec185bc1d387de4f84504a39e715a6a6a7af1a0ce1883c35adda840d117d580954982bb444135d1482796461c7f43632e9c02384d69399d7c8bc79ac30f1ea855fa1eaa42e1e4aab0e752d07abb13f84f11e3347a98bc489e46ac4296c3fe20929135ac0542c20febe6a21935d264caf8190dbe0a74effebac8a962011a88a38ef3ece089cdfb9cc334d91e8e3e9c5b0bb35cb322a60f5101a103355304930458049f1263c2c09551b6e869a5077a2de4134156d6e4e7d38505e20fa2e96ec40074d58e1e84611e295062853fea19da939d301a4e81d740b9deebd4df5a223eebe1f8668d9e1bc6b063335e956755076baa614c489c8e7a228aae3306093e7b1fcf5d9e24169c066efed83dedcbd54bf2e0959c70c6342f0613a9cdeddc066c6c49e2772b1b8bc48c2b1c8b9185ef383a6c47f86f9165161aaeab4f442ae89b7cd038d0490439badc9df916a7ad12309ebf63fa431fdca25c14cd2042c607a57c938d8840210d6abb472943f8ca27911cf01d6116e27a8b6841f41d1c557eaf213413953909363339989082875a8391932aaad10b25a2d9784a4ad9a5de528c30641dea5651c8757e2411e1fabce55d52687fc69f4ffdfd6d3a1bfb32e8dfcb85469cde495235a0ceaefc672c423c9c1a5ae12a8451e01b5dc3e6d1f5c8ec6ea21962cb95b58e28dc711df0770a16966a69fc5b355bd82bb190ec9ac93676ab4597d69ef37e6aebccc0c90418414eb4e012cb140803a6b78459e2f5bd0db1f1fa039e975988b4e37f58faae41413d93f09b06226c2706a33a43e9c89c2795fcecb52f382ca1b708287de8ba1d2096d84e6767d8062d0b205a4c61b53021aa342060a7fec97b145bb18a01b2039c99e897534fe38ca49828f71ba8d0d269ffad4da6641aa806ac5bf5afe5fe82dcf5e494e76c7e33e4f34ca66faab311ae07fc08d39ba14119eb15774ae5261e257cad5b330b7ad25b8b7ba8e23544977b0efc74f9568146248cca35fdb13146a6b250ff2f308f6dae31a7c007534eef0ef96d4ed9e4ffa86d5a27fe0775d44cc470a142766ed34165def6b1858ca8a93a001f203d0158c81286196e411f723f9e18d816b94e693b155d507ffb128ac6ddc0cdb1bb7ac7dc76d95c7984156bc8e84c88a1c41e021b3528d306ec6b862bc46bfbf746ecea2374ba54cab11e941afddeaeb6acdc1f48bc374d70827118a84644193ef9bc01844dc51bc3d757eb708dfff3adc97940b91592de5bda811f6fc0f27272144d52f7c854f92374293642cb90e918e8fbbe080e43b9c06a3cf0c3f1ee2ed17a6c0a8d5d166d4f0767af962167cad7d8b666e8fc1c46c5c1fee75702ea475fbee07f10228c094838ed2f0f8e42c92dde475f02dda5c7ee7143ce3372d7b3379784632fb04872fdf2110f7cc9df76c0be0bd0f18ac4214b4d88702ec77d94c9694c4ff7f0019e6ef9b194d9c3799d2c13171a37b5dfa5293a19ff0b304c6d63dfe7ad729efaac0414cacbbac2c141bc96692cebffaa3704eeecfe0d9a182dd3f589fb9c6bc4fe7ff41ac6d641f96230c260e54828dc666fd3426a8f7870a015d7cb0a0b7c89e80caca698cf10c130140a90e1d6b279d8ea0cbc1ca106ebd7efac3114a9fddad5e6165cb93a1f56ec9fb1a4dce395eecf7b31968cfb6ac2fb70666187ddcfd625ad918ceeb9085e9eba41b55a0ede85139dab7aa7dbd9ae0d353a84448d0896a5881b7d1b0b160401a7eb7a903c23d44e827f322915f846895fd4c17189810c7336a9662a48fdadc6269247b7c10e3744e01868a014a24b06db37a5b24c7e3fc345c231650ea8e6194946ab177853b4b8b8780aa53d5644e7158fa659626b1e3d6bf99c742a50d58c9ed87d01fb8c8463e4e2f046c1ad721229f9f3a7f1c391da8c207a10f355a1e01c998fdfc7ba9718e620d1b62ce28879c4812eb7f4351a99e529c151e995fa0f9a26c482383f777ba78fda3ac607adb58ac33878ea282e9ca8d5e69cd13c43036c503d09d36bbc9ad84e485ae1865070f892674a0432796dc3f720628207e7923657190601c7ad2f39a1bcf2e725dbd73fd81e04fb774412d3b22c172223763fc147e8f0711df1613e0df05932e68612177cba9b8a41bfc4ac6fce14a69399ac08257b083644ca549ba47928b938fc6343422db359e57b917cb25dcdd28f0a430fc99687d2bb09727740905acbbe0e4c4a7812948baf2cc8781e0a0755864a3aac514f38f844e2038213ab0d8e18447a0722a0b56b42abf576eac355413668ef0c705a1dbe6a7e378d064471ffab116311871244d3f775c67be1640c71a8343c58be30bc6d331a2ae178dee03a7a319eb979b354c5187f77ba30c206df233d06c42d80276036ee108cfbbec3615b708166f05c3cfa0be487a5e53c22a865937e53b733c590b872a7abe45acbd921f9ae55ea9754928a661ac11e2a489a26b2a80d6c63e126159df6785f534a0b3186ccbc93e997bd0559bffd3bb161c27cdc513cd202fc2bd8aa8e170c6a25dc8bab8438162893af906492eec5696e9a1f22a9a569d84da88dabc6800b8cea7b929c28a701c713966d0e067a751e5a58a9b259f7d702cad33cdb6e1236f9ebf3aa8f96c05603e7cad3fbe4869f492b87c0c279c6d8b4f0ae2c8f800991827bd59c2d9c90aa1bb5303c23b9eb8f76a7a6033103092dc97074c8e329036a639a62d603bcb03d56928ee8e112532558175a48b77a9fa281e28d39103eafcf6f92b2d57fb604b4e6e28e0208a3473747e020b0377982e512f961ea26647ded5d551923be3d2b862787d35a9f06b7d3e8a691d7b3a7be7b34ae07e9e8c6dfbfd4fccfaf40eb5a38061391845971ec007fd9d3a76c4af60fb72ffd40de8ad2f81e9b1a2a7deeb94d82b32659acbd452672a5fd688e7e7705223ff18e625750707bee1875f97b5cb78e3a9c313f6e1796b6419beea4538fda6d0f71a8e9215091f2c1c611e0314881b22314760445439035cd51be87082a94a0851a631f0935453afba640ee034c6243b0156f65dc7d0fe393544ac9e59e75f74e39abc904c36af0594bab6d000da24717c0581bb2da3048cdcdbcfa1cb9b13321a96b9f5a91b27520562801d4de84d68510b616867ac08012773ff73e62544875992e40229cff80919d9f440489d37f03c4149d5baacb4c3c56400490d2fd56ac0f82fd4901a2cc25d5ca3c00f77dda0f34a2e08d0397a9cdbff8cbb362670ddfb1e00d4b5390dc8e45060bb00fe035b11052ce9126fa205d997b4664d3bfcd9b557357bc991abd73e7f66418a18dd8b141bf9db2950aec399eb61f9e32862ee86b711d4c4a7bb6f1fb03861c2f2ea05f9a5b19200dc060505859e8635e7076984572305c524a0d9766dcbb46853655d8333458eb6e3ec41f91570255e7df45ad7c55a845b7927f5c417b46851b619360ce0efd9d6b8275e8c54eb14e4b68ed75db089ee87dd06edb91f056d8a79fc9925c3b7170a20854b0dfaa9966c5b5948840caae835003f22b1ca43420d9e10c706ff3393b21b9dc9a88fd483b95d6c7ec13adaff7e91c9a37b86791909f93b623e2a13255ce5027cae106457a0aeb6568749c72053a1188e295c381e88415a9ef4aa097508b7f52058b6db9b06140c2875d42adba1e4db427293659a87c8eef31ea7b09a60ed483e6dce821864ca327ff769be32bea4212a200f52d0c359b17d4b597b88a19298e6399441440c73e4844b1d26a3bbd1aaa44cc5404eaa12fd45c95a9467afeee34fa7d8ab3a2a68324757f4929cbbfecaf44e1f6ea14c6943dcd95e489b51a35b728b61a52e824a83b84073b1ab77138cd82a3f3c40e52a98236612669f54fc797ee69302e0b94841e205977097c061eefb42389260b638b50e976e083a5c6b835d527a5ab8eb3ae6328958c37c803fd666ddbcbebb81b2b275b5ebc1c0c8c6395df02d5bc58ac5d2b1ecabb495cdf87171dccc01c36d40c90d6e53fb0618c7625d7e247af58c55ff63ca0455f681736a639a4fba21cfa537f49a42c00f49278631df0f0e9f4e83c03e662846bdaca07911c38885a384820157f33262e0fffa2d90ea836064ae3f616f34a4c496b1bfdc4f4687ffe859be0d9fabf3ba6b2a757d12cb905e8abe5a8fa7fc9934e5545a7e42337fa043a2d93fd480ca6b077d985e4e7913c510f4a5bda920419cee4091f427e1fc76c80017d2b4ecc955f24f0297f3cbd267e999cbe5a0dc7767fa42e45eff861b120d6ef5bb6223897cfc32423ece5d421d57a9bc0d0fa25448b750cfc7aaeeabf917051e6b19dd30e152f4e44dc0444a72ac3eaf5f15c4c75639af54cc2939b95e5b6edd3bcbbc73d7f262c78c3956c32d7e318dd1ec9255a21ad97c500c0cdeac755c3204d9aced61814c3e3f8ea564e5fa4afd6af3528e8e9689317cb01116e5d830facdc39099d147316241b8f8cf25b954a67f81b32593e73dabce9d24511f98ae144a51eb2234a30381028f70ca759dcdce9a0e61655a76277caba0feeef0a76055f99d11c03ca126c34d66f7aeb417ac0aa76ca7b7b6541c7fbc4fa7c48f2e1b3b000643cadf2137cd33e8ad3d8e645c3cc6c603fbe3c32630204acfcb5d8f69c2eac6370a3e57dcdb5ac9029d48dd4e4f52104e58936050ed38c3c57dbd27392a34dd941ba0549a14ab0e5144e8737cef0c75755d9ab704d01dbbc0380a4733ed0b5771b29b14403cfe9ea194a4a6d885504d475aee656c976439cca9c3ee665c6af37003f4d62b206df05be7400341e82d10cde6823843d11bbbf2debb355224ebf5e7349255274fc80c3985a8f4df940610102e84e4dfc94fa5ef71142d2b1b5dbf68f9f10d2225d9ac9139276d17765847c13d00528a6329271a396e6e465d7c88803453e89300b4318d0fdc0fc6409e6fe41b0ca316fd10e950a72f8811e50181190e0cd0201af26d8f04026f6c52575267f889891bca8474e6660a08f0fa92f8ee7049fc6f9c600d83f209364b585e22a336f9705300c05656beb626014f8e492ae31d40fb3182f94f02ec086db7ee972d355c21951640ac82c43741e369ed510b8ee195cf53ef16487941501e27d0fc1beb2694172e1d30f0f1ca8b23b0050b02148621f766e57255c3b23859506a6499c1bdad3001d2fa916e4e9b75290ec3808e9227c4317fa13d3d5195407c2a9e746f8ed0f5413c952944b00b0e480017b89307e94728bbcecfc623324a127029ef91183baa07189dd3bb69600e881b5372d4703e024918892e46602de09d2c463cc788a70b7df7f161c6f29f9dcc32c0f42ed58971996e2ca19a24e4f6f4b4992d4e8cc70edd12a42eba26d78b1fb8b3cf073b03695006014232e6865bf2c619857276c6caf5148129ca0a15adc1d262b956993fd653945a40172e1905fb80e60db103372641858538c1dd6e576ea12abb6dff273d4c6f27efcfc2a5cae361dbfe12a00b11dd4c65fadab48e4c708fd4f4f1c1539f5fb578a94114eb1f6dcaf834fe3154ad37597358f554ce8ac5c01ee178405d3d27852f6147d306a401b45774663de43f055fcc6afab216cde1e458f5f435c5896169bf373917b87dc9b2209036be7616f44b3e6e0c88a5fc7514ab5f27ad770b84878765a9e12df044a382d30ed091c3bb4d4bac259f988f6ddd081d8e51837e7bae58f3c08f6f64099233b78083e702877653205324cb66acbe2e24f831d8409a3424209c70efde7dfcab41032e19aa88fd0af13840158f92cef5a1dc8a9b9b877382cb26336289e33a694fd2dbc905698c791b9d96e9cd605f69718eb082c41adfc6cbfa6f8a788dde7b7e9d5d9c1b5980dd090b7ebb0faaf99677789c6997129a1a5a6f6d4548bc98b1a65d1d360ebc8dedd14202821d28ce3e3980554c725975b5ef818901ca23625cfa6aa7ce5d636ea7bb95e370b1c58bad634453544bc82ede5c547b2c513122c81d5982877b3b2d82f2b49db7977d3502c452774cc1a066ddc5bea498b4ee69b61c19483ae317824a8c25e86a2318bb883a64ee1a8b5caab2aea9400f54a90dc34cb063e24e8357fbfd6a067d7a23d05c80a780d5cd58bd59479f6292d5d8bfc029d1b2e311a6e6b6b0a3c7f94d0d0ee735a2edd06824dcd136a0b4064be632809bac9db256ff58bc2d792c23d11c8c4b26e385af1a408b8b4db6625e68970bd7fe972d349997fbcef63e02f2544a7e701c95000aa5870494813d2ae911cb86d4708ab823c84c8991815b217d80973b8554078a3657774134c023558c47cedeaf583e4f331b9940df664a1ebbc1dcf40c49c8cf5b6bfe8f17fe9698f39cf098199de2404666201052c64554a9a4631a75f46e49286105402f258c5a89b99b48c5da32416f97ee6915a9bb49ec2398fafce2ea24f911beae4c303bce868da5dcd539cc478d26510a237a6b218b4724ff3ae408a926ab035112a63681c2b5b8e83a002475222fdad754b4e58ecfea1dde316010fd657fa174148f2bbcce4f5d51ff3483114349d502a6d560373b52c7b522946b3aab73ba484de49ee24f847f7fb09678991464acc3636cceb4dbdf38e7ffaaa197383007a54b13cfd81890ea1b1109f36e0b27df19150bcc3e4548b962666fafe81ea0905f5358e6b1e77cdf876ae559102deebceccb196c1b33a2dd6fcd4252d1dea20cc6e5abafdd1e1154efca02e92856aa200a64e5092ba9168479bfc05a0059725c14b74d2b44b4d63ddb1b23e29b9c486ae3ae088c453073f3364ffe4f61abacfe64c96eab1b6bc19cf97c353010212f450c07987d46ef1db9ce3a01755bc0fbc3a225c590b990e736152630a0109edde38b5bfe0b1d31977141c713328f53d9bebb1def9d0dde115a77382bedc9a4736f3c4c5b2dad6c49b5564589ddee2649a8666e7c6738a9277a02bb8707f2efdfc05f370ec1644c00984b9455ab9e94cecebaf13a8f2386d3cd4a5f7674ed350d6aa335a7e22485e289372b78b69441fe6877d6509a4a2e761712076e0fc9839cec7b2934a78dc339fe74185c7c5ca1302e1dd6d2f59f8daaaee00a3598e543a89b5a215adf1ee521a660ed4027f794c1fd62efae801de0eef4658fd192e57ae4d63bb37ba44ddea5a16d7c44850901c961c28287192809858faf5c86b11f7ca604c83164eb80cc10a9d02010f01684329e9ccb916aaf1b01962589c9920e09a85ba3c666fe4a0a3065d3661d61677719885fe1782d7ea1bf9df1e32efb4547aabd78b0c389e421dbbd5143f91977b9e73e731a6eb46a623d80300235df9ebc1ae60fb4b47a07a5b7228ca03e57e34d069c069916035166aa6b104db4bf7516bb328ea10ed0fe097d0794a67c9b21564d84b53891e028044f453e072aafa94e0e999ad231dd8eb6a819172824834ff66f8bf44d9d30b81f8ca09b6c58ffa4352da4f5cc00f923f9f7fad0a0b6050104d1a1527f031f3b4f2493a7c374b351d01ef97b6e37db22b553afe19f8294cf9e06f7e11970f06e398635803635c11684fd4e7b84c16a1913f7613d1cd9691504616fcae36af02bf1bb740da0cb623ec692309f9d18bf33ea8415d71c49f5034501236ab6b71d1d2d7fd1a9ea8df45f0472cb736a989cacb0df8726b6a7adb6564ac435f698f798514a81c9cb3e5dc8cc370f2ee321bd8a714af01b8534faf406e8faa5f27eea5be93cb48a0abe621a157292620a07c8c61c91a729a4b43a481dd23ba4f82d4630035472c4f83651d881dec8c0449a27c1c7b0f242708cfb51dcf6f6b100c48ce25b2513f6180e51edaeaa91c98a2f26cf7e79cee3d2a2c80bbedb34616a02633bc29900ca36231b4b1a91ab2657d52c4c2e0d6528d68a5fdc6b82cdebd46ccccc528d6f1f5cd4f59eb8f5531fb16e1d8b6b7f8d36a21b9deb325b0b2c061196ecf0e12897c8de5abc369bcf862550ab64e4a9b8485b763fd9cc9bd2df044cc2a3fc697b4a67e29d8353fca43deb7b433eec8cf15d26bdc92df3f71c3fbab99ef514d19dae024a801bc2ab1a7abfe904b5dc9a2616c84dbcd46493351f11f25a8c29e0056c0557a1724b5f3e418455bb05b9d8efd7275634ed4d9a0abcabc5bf2a3692b1aa8507f4a5ca72db1dbeda500a3ef028d8093aa713e209e5fd7c695675454a68ab0d5b1e5869d500db412562922f1c8474fd3428eaffbe179fb66b78b886f3e6590c914b13860b5ebe1016827007271bc6cae5bcdd729736af01edbb3fb0aa8ca4dd63a3acce03c05d367b0137e774f47320ed4853e4731137f00d9b387f11ab60a68c76627bf348ef3adcb21e4550dfe276c143a7a89706d9e4a06dc94b9e0ca7af62cb4ba9711b01b7a4149c43fcf30db18339e024466ca0af5e89150da5afa32c06c61835285e0fb0173092e02fc23c9b603ff4e0c29cf0f1eeac929e6ac7d8689e2ec80df3de9d83693b31163148f7c3f951699ef7a60899d5e0241fe67d43950ab4010c1cc8aab0df8e802a8f5a3e9ffd1a4965272b3d3a34f90cef57f00a813283e75934b77304e3963ae79e59aabc09b06ebdcfc26df12c125db093854583d27dc44c5a6e65efaca7c99e0ab384e4b193b5530caf50cbed0bab51c72ac8eaa7ed09365d860673b31610441ae999e088dcef8d69bf1932c3982fc8d52b056df5717df080e5cac7799f37c2680bab493b327d151afc5d43fa4e0ca3444fedbb11b2acb2218aa35c2275a710c4ca8319930a1b896ccac0862d918a6dedbc1cd39179fc4d6b5bd0d3c8054e1c422d19ccce50c947069c1a0d3f9569fc4452e9a18e899b568f8a0bac6cb2d118d8b8fd75363ffdd243aa72898eebbed1b5872ed3d4170d469f533542dbc5f5dd7882421951e8a802bc0bf9e4437d1292127205ae1c53e327ccf06efb5207c7fafed9273b01b13e1006e05cf629396b8129d0ec2e522110cda6c515d2df3a14ad33aad535fb1723bcd56d3cfeaa3d94581258321f48c9e1a081ff3716c55007fd56438c4c7e1912ea8150a08b78234833d9571e644462834cce9f10341f5563affa38511ac6ab10c481600a07b7ab2af29be3b30902e4a0966ae0b24e2c1ced9f4ef9cf40119909798293d73c80d9d850d5ea7a8943a88abb94470324013e06064cf31bfe44e816e86a987ca298b922a921e78c487c46d26367cb6e565b2c1cd77382e58b6af690e5ba7d8925eff6fea4e73ce92cbb1d11b82eab96b732bb19bb95bc61029f4c95a8c82b97373a328cdc3ba661407c22668fefd8830e862b666b4f0f3cd63933504985914b03f55bbfc143036f62797a79ce738a93dfffc34c83412d5041cfb739acb8c883bdf01cb6348d4e37f033bf8d82559f3eaf9189740536f01a4fa3c0205aaf19e893cd0eb45c7cb69b6793539592663a22371c9baf26ed0c36e8ad8766e67cbb0c64263c41a3aaeceb47d5d7b916e241c8930be4d2740ef51b450a44b5611a2afe304440dd61f6d44c0f9193bf0aaa437351ae0d8092117676a7083fb78a938094c0074bcf90f07b1d24550c3593b5f2e3646ff1c0e67277aadbe9975a0338792ed93573321bc7aee4cb7d0a60f05cfc770caa580b5f4817cbcd1df0bebc53dbf2303f9ce4160ac7891c1e97385b8fce8e9da57be1dfcab0be677682ff5f0c0e7b966eae835f6b22a9888b745bdede0c6c7c97c2915273cf305ed3584d948a0fa0380d78f0c90be144ba234979a73e53b2307a9c2a645181e4a5404c991be02d7cd6e8ff80bd8d2673d4276c6933d0f531c3305c5d8946ec18b14456390880b8892f1ef9d2343b8dbbf50284b75584a3fb087b853ba09f572958b680ffb849e19c744ae7e7772f85303a4c4310f3da60d5d1ccf3feec964762edd34f4bfcac776aa6da3e219c3e826331c3a8eeca0e82f31cf3de5b44a4a771eff91f4f636a145187af76738be4438cd4713be370e65e3f95b2a168159c5062d510537efebaec25f10f0b1c71205a59f03dca48f0f44b87cd3cab898075de0d99039a7972e158c5449d0cbbf71eb248da2bcf9e2b45155bf71950c882f8abb9fd504ecfb789c7108526089c3b6911d9199d19f7cdd16d5aa6a6b8bd5be8cf2bed03c6f0b2d038184a41372df8732b584ba623a3e4864bc1ddb7e3cca1ab20541de411f9612787b8bbda70963c1251f7a1a874f0684aacf56fbe2bd467891676098cff20eb46d043e00346861280a542b3df38b986979eb19906ce44bb2ba27ac141be0484f2f114066c03aeef3e4ed01f32f0753b231d43134659c743c164e10fb781513326226f4102dea169f6bc972730f502357b2ed038b437d6b997469aada906d8861b7548d5828d17c2e2dde106d8fd41d6fb339c90ec8c2d92b2d25485b58eb53227d7021cbe53c938b212abe0ceb4b0386d576dde95fc9b029fb2edbe78c2bb47eb41f4a03aa9ee45fb95f746ecc2e25d4990ffb72b53058be40d65c31616cf9d960dfeb90e33ec5fb7e7dbd85ceaebd26e5fdb2102d084bb6d06b41615f10e243f02cc351cbf122071bef8ef74566a093d67591848175af8fdf4a923b3342431733cb9f8c78a72d011c61c852d738de3b05043701024b48e9b7a3e19d31a3b57d142a76888a26d7b08ad61b9bb4488ca37ef4e908483c5a961326f769ef368539d5fbf154d89c38618ad798f06e2d578fbf021400539aa3c0a14c8c99ed51862220728d110922012655580f9437ad229f40b8137be5e223d04dd6340dc89fe53b856298f7bf1a0d55e34a03770aa65142731095f1b2763011c0b99f134640dac608b27eeb4ceea0ce2188167132323a75054df653cb992a596f6f1381a79f9f727b525efa4911c9f11d20d3373ebd760aa7dd9d78fdffec5e5f4d20e928fe16e109afd5a9435d8f814ab3c5367a3143178e079b14429194a7f528d8bda4e7100e7b469c03de74fe3d62fc4266a0a7261328941468aae162176ce5f891032d83c3af573eda3ad66f962e908012eba3a795d0ce9491a179c52073e414cd0432083c5a9359ac9c5057813cc32d3ed0ace714ea0498c7213ea95643278db2173a38083ef19725ab409287da76da06563ee049179ecf995f85cbc262a9a47daaf2129baccf8c2a3c2d8a36a24e1cf50bc7beea10e0e9192857807674677885b3acc29d822da40920f7cb26d31124b03980b66871f5dd4f7b0e886d22d0743f6c0a957ae2096028e0376d24545728ae06e141d047836405ef4341798cf9fc137c10c18edcdc63b0c392225d4f619e713a582a1644b932960982c57711ec93a305302908e02514a8a239cda8e01613455e9dfbcf7ef98c25f067aa8dcdc20c6e0b4bfbd2a2c47ba9754a223ce1e0dcee3fe8c2e26c911f0e41ef46adefe5d1d0303592402bbbd8f0502e4ff95a49865ee3413cc9f911ba72141ac6a134b8ff3ae27086e98634d6906146758c9f8c044e6dc16d53c40851298e02a5e2bf2b9be35a75232887923003bf7e63a5b424b208c9d06260e06793e11656e5b3dcb82c852ad13331e88f4b499e0bea169435860fc64e2d72910ae4089db869d74c141ff9190d700ca02ee653972bd047f5c34e7115af3543cae9dd78fce1460968902687a62187a98f6cf91d0a418d3cbd441cb384a97c6c427fb9669a3fca13966cf6896b4f46517a8fc9e426c29a0e04bebb2767e274b7175c8f935ba05757e601f880405a4bfd27e57741ff770ebce24ef77f38466d1f089692432bc0cd58ede15e62feaadf9c70060e42f295eae6c7897930e791eb4f892087ecb28791b753d200760f286c6e3125690fc100e1d2adabe88e271c261fc733f1a66fdfe629c8382ba7c03c7ec54aa560e0a03defce019f0f4fa650c4acfe7e4a5d769c3f9ff60c234e3347b78a732397061ca62c18f3250e6bcd49e626e4439a9112263e84b52c83024f8bcf7adf932701f264c90fe4356f10a9f31098efc035ea4df333c5f17431d7e0180c2f65f543667c1c733e2933ba8e30cfd15020cc3030a26eb9d57309c15a5c5b7d5ed106530e131e6bccce2b6f273338ddfafee955966e3684c1176ce552b6ab54ec4f377129eabe0bcb7c7194352513419cdefa2d4981867a3dc2707cf1fb782432372e381ff6d3742082112b9b4b27b0754095909bf0a95930a68d6a8e8fa5301554015500554067b7a8aa23fdbfea35036ac80ea4f0584dd6bb51b15ec2936ee2b2cfb570d5dd8b3ba830566b5fa7aaca2b39a1b0fb96dfeea0ceec598621189b6b576f40aa8d5de5b7baf1622fe409bf8e8736f3c65da6a9f2994881967ffc5d3e95e8c492458eb4976987665c76e662f6b2f7b4df7ac642492141448ca90f1b2d75a6b2d0a756fafcd326b7ffe5dfc7d3f5e3b76e52a549bc07eb96f8a65f54773576bfafabdaf4dc88ebe27e53d2baf3fb366458ff4d473e529377f3f6e56444fd1dfa608cbf850c3a11d739ba637ed1a76a74092051e9af9dc6fa250f79ba89dfd6aee72770631213b4a4d50ae6355662f7b3f9998413e3ef5046d86664dfd9933f4f7620efb95690ebbaefeaef9b0034bf5a70ac9cec7c7c7c7c7c76706d15b9ba8424c5c33c8c787deeb3763114e7be9295406284356015654fdd96107992449fda93f523e99a87b312691fe899a286a42e1affe7c55f4429e6447a94976f4ab3f9b6eec9a706ef75ca5552d7976a00d570fb710b4e1aadc90f3f1913388091feedff499414cd4b89cbb3e51b2cbd7a1ec2efd71d7bf6914386e977f137551d38422bb298e6cb8a18d1c2d67c92f276596af64d261b45c25a7dc25af9ccb2c6fc94082a4bce52a27651c180fb2f296bf641cd283b0bc45e7a45ce5a40c61e4e8ad64e86132e332f45c72f46c8e5b25af3ce52a79e529b9042b3ae52aaf31a79623e59a8ae570d05dadc66ace9bd3341c110a29a048e18518a048b1643bd9f597895d7f29b1e3be6240b2e1be8edd8c42c15c0f73d7efaaf530b77c024b5af2c012fd858255fef2967332c80c78fe9b281fd3a63fbd1cc322cb61ee92554eca2d4fc9a4afe494c36420a4a71c46c6e11ea4e5300eb51b168df3f2202a877192061224e530744eca57bed2236e7bab49007a29e732e92d19c65fb2cb5532cb6b4c961c3d970c3d1b3d181248f98ad6617911951c2945547e6bf505a202514ffa39d1bf08f49180d07ce044d85972b705ceb48e9e94c39fbde9ec2a79080d2f458b6045eb4c731990ec826c7b692ebb21b7d51cca09ca679a64d0a6f7020a26a67082551b2b311cb13756f666328968d3b2d1636ad6c82458b469594ccd197aecbab59b35d510a66d22fc8a46a0941c4f7386aa5cfd91aaa1965bacbfaea53bcb492b24ac82339d92af785d4040a79305514e825079ca6bfe71a3728f69362c87d1c7d3f50b746938ae5f9ac37c907037a29cc088c1b9d9d7aab91bf5f63e546d75fc81257a217b0896e865928b6f4d520ddd5b0da5be6a285563cac6d4a6a75bc0f109b4997b0808080868d39edd039ad386883fd0063b7d3cc5533cc513167dbb02b0e6449b0e624625042cd10f01511a44f5f01997feeec5557f2492d5df5f0ded199b9eda29a0487b6fd645d3e91630ac66e4fc988076367d3ded6a68ced01ea480220c48ea0996e8691970fd570d6dfaa8dd43cd9ecace6a0eca8a09d964105a601db49118d2d9c93fce7c4aa5d53d56cf7d15239d5508c391289440e10fc3cad25a29ad6865754fcae3e855af9048f330c68a524aa925a9bcee2545c71b66619a156cf54a2557aad17b528e674d8a9e1a6bd2a5b37f9995f2d14d8af6d1d46cd8ef47865d9ab3af9abbac5bb6a230bd0296558cd51570fcac2a57c9aa3a46ab48a58c1db4a1bf17cfd813c66a46a53b5892ef6049089f3e0e07ddf295aef40765dfb021133038b9d59cb594528a0406253627a53f76bc502401299d5c885a477ddc2e66e5eb178b5af6ebde5bf697961dcb6ebd925b867286026db8a13c00ddb5aad66ffd77a69bb3b5d65a6bbdf76a4d77b5d5fc07e6e3c57507ab5a87e8d5b23276ab4149e3167f5dd3dbad8c719be6a216a1901751ec6c4dcbd7f5ed35eb10bd7e4276b438eb40c843d41f0e90203a10f2506474fc1a13e786e83564ccd57186de280fa1e1417980799b79cc2d12bd861469ee868d2d73f15acc3217ef31fdd1cc4519638c52bb616fe9b034673d9eea787d805b8707e8e3eba3fe003dad6e8637794d9690685dcbbaf75a362c21f1d6a53db86e6130fe52467581e1b421133098b6003664f20528f6154202062f3636b3177aa8d1bba746ab86e37abd6c6a7453042580b051a244e1625b1be30aec66560f52cadb5b59bb51d94c5fbd20f5bf7acc66eb424a2776fd62b7d9aab799c3ae6bce7bac667bcc4220ec2bb4b4cd9c8491833b6a372a10571731b70e79ae7e434b5227dcd73933843c62ac87b7740f56afcfaaaaae6a0bcee22c8d83de6336d743fa9b635481eda10dcbfa98fb46ccc989f534f75cbfb9073b9c3598eeae97185560b8e90fe40b363d13287430119491d69c37c6b905a157077fd861d6fa757b870edeee76d569aa2ac6aeb66db1fb98d8e3766b375d7dcc0fcbb11ddb32ce76abbfaa7dcc2d6d6896b5dbb669204a6e46171d47c94d1d6991c6d95e6bcea445b7fbeb77fe9b8fffacb58af731f7e4748f88ab3ddba5a6dd9ca94eaa2aaa6b5555d583e8341f228f08b30feb5c3d1870fc175fc12dfa36b51b913dfd66abd570c0d79038e8ad69511cda0f21dbced72c94b2028c524a29d57ee5efeeeb5acedf0f1df53ee6572fb58b8e651c21970e1f19629717691ced97fe300dedc51bbbbe652334bccd080d4fdbb40813691c0dfecaf237c37d33dc9ad5dc8d69c939051796c04435574a6bf7c3fc3aaebaf4c17e6aaed2300aac63daeb4e76520664b28527b6fc8dfce1b45a2b69855353ab93f2dda69eb4babb1ddc90b3ffba6d350efac30eb293b65a4bef63ee6ae3ae66cfd540ee0ff14a5c46e665fca951487634343443324a947422812450cf269dc80e68d66c5a02d1d0dc48a5c03cb3867a53c620a08d4c62cb3d4f336b24cf49140bf85e8cab80e1fe2ca03d63c86ec62ad6fb98f16a1ad9cd68750c2ee07af818b29be7a8eed1deb39d66d66cfa6292b55dbbd9b4bda65f59ebf5305737fb989bd21b567396aee7e0fcbd189348ffba38244fd0660650c6957247be2062216bf08c43524675644fa903a10c0c2291f2270ac9aea2b30bda899a0f735fee4621d9cd47142ccdefc86efaec2903c28901d8cd1c373208589aaf32cdac89e18434a702f6fcac1162cecca3ee109ef3940a1cf7471f77049a35347366fe624c7afcd9d3669a86d2c426a41cba648acf4f1676f61787648e94411f535930491dfa0c8b16fdadf5d7abae4054bb51ed0e7655657da40c025135a7f294575cbc32a7a271d09d825579478e1fc7df2eb31a927e0163213bfa1e969e96b7e81f73863e3b366bb4dfbb4377b326f2e0ee07a0888a3cb366455ffabb2afac3dba6e88fb4adae78e817b00edd48bbd7bdee6874698aa63c107fecb55d5be6c15537f7c08c3ef24276f5a94e0beca36eeddf7d722fc6b0d11d613c225701c9ae621845cd1a9b4a9166f5a92a95b5eb68958215959a4f48ccce1136e9a842919d95158a4976f57502c98aaa2aa96fcaa6bedb1d58aaffee93542a954ad1cc1834bb5ecaeddab6eb7afdfde898fd4dc269acb2ae532f64b75527d9d514ce150f2cdd2416e83eb9494649aeadf44db2dd78ab3bc2acc4ec29654f2999c9cfa62cca3ab140a99b64df24b3867af577497691a85e4fab4f129b52209b82f9cb0dcc8b9695957256d87d427648eccde669965cec7aab45223b49ed0f2c559fea537daacf2b9f5d6f29d02948ce2ae8ab28496765e5317cd26e543675929dca312a68c76b28a0d3bebebd0ca67d5d43a536f60d9564e7b02113540ecae7c915aabf41778a5da1d8f5e5658a6e065e0e23446ed6251ba1e1b91cc6080d0f888b86d1384094dc70877139104ebbe09798b96c364623cdddb8aef24be5f65a6b7d6457b50dc7b961b729ccbae82cffee93598f2f09e840f5f74917b0b6f2d108e31ba3510d89c1cb4a093f33766a4729fa13b247bf328f3c351ba3c33829a518768f240abc1d7a181d8db01136ca5f8523bec2b0975779ead0e163e294e39691cbb42e76e2cf9c7239f2420a8a21d99279e00c9f92b50eedf11a8e782d5a1f8b644e7b9a23eb6391c8aedad316587416fdade8afd2e72427b7d3bcdd9ea48c89c4fa4819394615b83ab4412b6bb3487a5e3e730fcced69d6c0e8fb045770d3a11dbbe886a68b881bc370166448f45e0e7353f45e8ad0f05e6e8a1e4c8db9637f221e731e1b652fa2d3acb240a389adedba4155923baa3e179330adaffd0164c67c8c2c5ef40f73a6c6400386def45e0ea3e19ca9d302592716283567eaefc5d6962f16f8ab92dc27a97a31e9b69a767d35592774565535a7ee799174565900a5eeacae8a92fabb17e3a93f12a9a282b834506f59236c34c2b0db5f417784d98b9e6e8645244c4f348b21ae85286a054eb995795e08cdc6ac1f14b231af666392e6615c6a52e6e6bffba4cadcbc7d45d2dc3cdd302e61dc27445098efc2cc976c03e986bfdc44c3b3e19c0bc65172c3fde538d033d1f05c7e832e123d1b34cecb395d247a2e1ac264e8510993a1f7422f690f0c8cb6402dfabb2cda5a15fd7d03018db00b3567eaef7731c9daaa208a84fac8ee243bfa0945a2e80f2cd11d0b64514fee9015f8b3a9af4a629d5894ec2c90eceaef134f64b7316c0896eacb6b487bbad6daa92d904c722d9e95cfb5f046ba7e7d681af0674fbbfe3ea9bf92ea6edbd4ac99d7d2dfc5bf36354d5fb73f9bdaf53425a1902919e4844e1d5140553673761c82252b70f5429e52afee81194db21b42767594e51e8d606450f361fb1de52feed1310cc2689c97db5b5a0fd531d1a196e3e51897a1f7925d8ec3b9681ca8bf0ad25b9755fef04bc65172f3a2e406e62f9f33f52f1a0746e328b981b90dc7c15172f3729843cf068d03a3715e5eb94cafb2b80c3d320ced984d33dc2f197a51f321eeeb3a46cfe1a26bc8aae1881fbdc6b4197e94e1c69a28e72db7c848e5dc240f2cd55de9b8034b32890f3d5d81bf789a26940cdab5d6d3215c3f736450268364900cba32c8fa259fc42d936417c3eee8f7d23efa75c9934a5923edd72d0d88ebd9e3101010101086ddf8134ff75691e8c6a6bfedf8d8e30eb4e1b8fa2166cd7d7271ad8f995239d2f789ec6a7587f47d4276f537b4ebd88e8936cdddd08e65fa03b7fe0e55796357c5d91b95b454643ce20bd8bd1893483ffcec9833f2f752716467d766961dcbf28eec437652a39a0d4dd3ae69dad47c70da3f3884658ed34e37fe8677e450b94a36b17c45537991966b2a197a2d59c97696ac84078dfc69e7f077e8d0b4286722e8fee0907643bb8e3e6c6c1adb214fa7d3b6690ebffe07d949edd0db8e65b76d7a45086fdf32cde12dfa6c190258cc76dc3b147fa871af99e90fa2b6ca658a95fd98fe4818c37852c970ce3c66526a9ba6e10d6b4ebb48f7ac603a2239cd9a155d434aa987b6dcf1e36724b7175ceb5a5146a9a3badcd2decab2aaca56f656d60cec6abd1159af341bf5566b69bdf66af49895458d56dfcc969b12ae8023ddb40cdb327d5994ab959cf33aaa6301cb8ee65b2b3f65ede69c73ce39ab7b3becd7ed0d76557a6bb5b6b2d2b24194f69bb66971661db4d171289a35233d03d348ef2591b07f336650ed66cbae5999b5aaec63e2b899b3acd78a6a5575c9ac0913de0afcb84376f337dfc86e524b297c62e9e9f6ab45fac79c992791b6ae4b492a638c514a29a515f8ebf68f27f007534feec538ea1c73ce099f88ac6fd7b296b59fd984d10afc759dd4328c5e017ff049eac725f56ce76467e9ef5e4ce9b108699c5ba5238fa686317821062fac106da22dc62a4a2be8135846d16605d60515ced0b93819c3129d087b8c31c618a3fc84104248c980e92b2ca59452caecca7e6597bcae4bce39e79c7366bfb22bcbe6e5a7756ea329a3143d92d6a594f6dc684be1467a9b96d394524a29a534464ab36b5a1633ddc15285dd7d3776e70cf60ac3b6675bb63d3bb755d831ad8bbb833658cb5dbd833622cd5d97bbe69edb24a594721b166d58d35dd56f9aca0e0a898a20b4ad6318bc2085198410c24bd34c57fae8132316af48638cb15e19fcbd97e6aee883699abb4658b4dd4aef28eba0971b422cdaae8e3e97cc22d528a5f4906a2669920253cf925492e46396496467bde6ec57765d5aa669eeba9626d959bfb6577d4476d67b56dec372699a352c7a8445f85ff461f9cad46e58b6af5c36b69b2f91e8dca5dcbdae28046d745c972bdaa7d64aa310b4f1b9a64cb48934b7fdd210962ea86bedae2f602c73d885fdbab00ed3164e6b0fadad21a5e9624c6bd9b1532eab34173f2f7b989df68c84e10b5eddaee085e1f8e8336b562eca22bc54748a1ee98f84f5a6e1ac28b5feedb0e967acd37778ce58a7940e3167acdfef624d7f38d3d6b1eb169cd1753f8984311d72ff18df2b97c09205846d7d8859234d73c6fabd185b264bfa5014ee6009fe93a66d5dfacc9a28147de68c752be729d21046499f7b312691be5ba76ac0f1d0929894a99d152b382308d951934c3265904c9992c8a8ae801dad864cb0e1859835f4a2f01009a6363ced68eaba9f44c23875ef0cfc43023ac17812921dad86a40c3a24bb2ba00c7a05ec68dda9864eb386a6e815f5f4743ae9a8a8fd934818df8b8225fa9abf5a864d4f53a9542ab5e90e10cf2e5540b3a69e2a1e9f2f2125c13c154fc553f15457d0612ff7477f9ec05f3c65d590dc8915aa72520155403d2a980e461c91441cc3175a7c51450f3028236945d305923dda9009174b6ca851013be0874340ca985aa693dd6b38e0872f82be7862fb40617f344ac0d68f10be38c21230b621932f7e80ab26f0002f910320f028518321c4e0072c38c2111e7c6164075f9420076b60426ac9154ea842076578c2143d000312b8d0040b3e7882832818c10944c015932470c544133e415822c99222289184244c24a1848709929ed3ae7ed6f082fd7d3470ceb9869d3de79c0e80f089858435e4cc3fc14e4420e90423f697f78d09804d18314fafe198afaa4f1d1fe3c660dd202cc5cbd4bdf7de7ba961092f76ba78a20b2773ca39b718751505ed06bd0d0b966a7583d2dbfb6a891468689cd000c90055404040342a48c001155f3481841d308881658a7c55810b23e0200c4260f8c213522001230726bc30221405118c208430e01baa607204164888c1132aa2980f6802062a6431841b6cc1074271093338f92491451315f08041cb60a17e200c32e04292296e40862f7c81a782c111575081123c39e0818f179018dd3861e40c4610c30e7e10032878381de480480c524a2083145ae882c6851cb0c063e1138828d1f3852730a18228b690054106378822083a3c98422a08091b8e78826405413b489e98420b487c0728bcd00523b6f8c1132fc8420c16ac2770e840144cc4c008330445b9c1155a60b68084284e40848b256ab0020f4c0d967082d38e125cc8c112aa8084a80535d002111748d104912c3c41826e4c8c50049e9e2a6cc1870a37e0d04511d0a08525967003294c2105fefc20b18209162831540429bc2ce00a63f0b1424808573471e272052bac4085263840411986100524ee01b830d540075218b1c1129c90354988e0831f90210a1f04416185460ba84051f18411ca8003201e9290223f47fc4028e7082ba878021258b58012281081c7065c28018913a0c053abad55adb5d6ea04ae15a276bc7802055468f1059326400109242a1b543690490da54c37e022874a1e2e7468e4e1c2542b1e2e8ee04205f5d2005f1b32d9e28b196ca1c516555c99ff32dd165ad8c209244e37a834a8b668c1dde204778b125c2dbab85a6841613664a20517b478f2846422d2c2c7aa56c00212c905a4aa05352a19e00fef386b62ac6250d9aaaa5690050fb2d001165a24c9e2e794c569dbc9426765676865078b275b6302290058e0411648342a50810a68b040820e1642b83a58f0c0d2c12b242c90c42b8ea421890a05151629a84c5865432657745119b9820a960d995c81852b9c604224328109d6e600e2152630c10281c510aa60841954c0459326964077606da583ad8d4c60828d4d8670656107d914ae40b2adb5b61a52f5542590a94a4013854691b1020b2b8eac210d48cc341c4173d2408359e58836649206146cbb219334e854465ca8d9a821a95529ad5476908487bcd8d5e9c528a593524a6f5ff5ca29476ffdd2d36d5f6d8eeda6eca2bbbd8876d1a6a43ecbf2f49c3dd64135ee81de62d7be5ecdd91a05a687de3763cf5aa58caa8a247cdce159732fc6959db2883a2f233028f3cd9f3d9fe3743a9de4cfa9b2eabdb556b5b2eabd17eb32cddf0f1ddee52231ef84e8741862d398eb7093cc3bdddd24731d74bd325949fdede663f2101a1e901c0ebd4e4e1f24b29b3c684c93ece634cd2372684e1f12dd4176d8d5c4cf4f133f5597e922eac658beb2ab2779a2a77a4d124aada8aa56a87ab584829a35323a888bbad08042c3112ff6f50bc596a740680842039221d3de3664828614bcf52cd35fb7a1ec89f9cd367f3bc43c26cf9d6b21ed02c339334f9666e3da4a33cc341bd7a222949cd9b242ba5d762d2b5372c80b89454c4c8673467b968198cbc8681c2192bb216f5d6ad98b4b978dd0f0bacb18a1e101e9b44c8c06a2e44687cb7407a283eeb2c764e8b99cbba13fecd73be892b9c8498dded257c4b018296334aca49523e6584cd641bb8cc6d15bb631b9bb921b99c7c4681c199977319739f46aa77164344e4c8c761d74f008b2adef4c8718eb2e9947e4e8bf08241f84f52dd38892ddbc8efbebda8dfb4bcf6b4076b30a285bfe5efb2f0ec99e18193d4f325697a3362d02cd733036dc80ba246ad6c893fe968fc3afcf1b6c70913e525a526afdddac3f7c793a0efab4ddc8e8188e47dc56661d51fba08f5ac7bd3c17bb799ab9287764372f654fcc7b6422d0bd417fd706fde1ece274ec0203e091e78af088f4e2f271f56a37e8e5f78f94d144246916189aa545b3e4c89979162de4e32e4b2682ee954c049dc7b20e9a65f1be86fc8123c4d480ca4cd1a7592387e6cc9c3c3367cfa3a0cc23b29b7f89510596b7b7caf75093f7f76a3820862dec5e6b22b97494414d04694656d59c8cd5add6d65ad5ea563bdab4ebe7475a53bb5b077dd41c00f2254f2363c88e46522f30d63d396811c44091325031d9d26c58328fb9918999d66b45abdbdb6cc68650a43f12d49a9652fecc9a1a254aa3e804a2571d88047ff08c9f79fa993e13c93ccdd344324ff3344f529e24cf9542b28918e54f8d55fec89cc6bcd2b209f9336b64b44491244afec4e89f3de769cfcfd3aca19edc53a22c9c3f970da002833219247d9fa771875a3b5b6dd7a351f9a87790ddd43496ba8a24b6cb864caaf8d93f8068d56364a1497346d2d71a33ab34877c0d595992a2a0cad93b6738c3926d5f67cea6bf1d4d8e207ee4c88ee60a76bbfa84cf5f574d1d75a2508eece8ac73d699ab7b49742477e449f22481e7ae945bf5a6fca668eeba9ab3b880b16bbf9eb1a47ce531257f3fb666e57a5d5ccd32770f6f6b54b9d77a8d592bc4c7a266c945a8c702b5ecb2a65c62ad2b45df1a81aa7b4c0af0586e75341d678bbebd86c4d92c587f42f6a6f1a5e8524a11e47050953a5251812a106a186b14e2a28e532c59b26489f594c7a768bbc4ca3763514769127d1ee8b01c3520a2ca8d425128a2b2dc9b96f988b4cdc72788091f1f26644747d867f38931eacf03ddae8f3ab2a3a62a70a539d1b464d6c49d14aebef2f98f66c725f1057127eec417ec9076e20e95578e9481377dd491324c2b39084ef456bef22038d15b79109c20deca71a2c7a283b09c45e308f156aa9c0d83368de77e680006c1200d04b104062d81414b826050c511758cd3cc1a1a1a5392282484420dc5548e3461d2477654066db944765426a1d6d26baf491e119a33f4f70a0909090951fa6bfffd1eca4e2e914bc88e7e1405ae879eb59a0a617baa3f22e2aeba88baaf279132201342b3e65e8c492421edc3ee6a098de88afa93419b3ee6a0d6d2144bc7a099352d5a2e21d14bd3912bc65787de77f5ebab33e44081028a930d37dc59ee6131994c264a973c911d954173865ee8a2f0d0a634b5e9e5920854e77a58e89239437faf5c72a9a5e5921c43a2c7cd2904cb032051ab431c74437b688653b78310317eb8a99fc1848ad4aeb4cec898ec6fa5ea00b56d0e82367611d0c6ae99c167636648b2b18ba030512b144bec0a056ad75a4fb7f6df1f3f443bb6fd356f6b4c8d07cec6da8da9cdcb860f249a2684251ba1e1e90c69b9ca87e8b4682242bc96ab9c0587e540545af4eded2fcdc6559d85c3aeebfaa0f631b7d59c3dfc2fcd59284a9821b5a3064475abb92b6a36a76425561371c37218bf51794bca55cec261578b80e85673184bcb615c96afe0c06861b9251d7a157aa41cc4e62f3b84f873b254a263af5a047256c938384a6e58de7292ca611cce1a181aa745e3b0a8dcae5c765be1f095b9d1a30aa6b91bd8ab9116017c43a4bfeada31cd6d974e813b0aacc34229e38ac065690ebbb48f28bb7b592bcf1983a6db010e6552e30283329f95b3eb291045c9510669c15aa19a02fd698204d504494e2d528d60bb6193238cb0f11068c46f72c40fea2fd3e1b061932376f6b66193234ab0bfd31ca925688224755523ea10970d9bd06068d74068e30d9bd4606857c7d0467e9e88d4573d11219ecc337355e7d0a980108f089d5947bbe832eb5819478817354ebcd41c119c04f0e53390f2794e02dbe56fb6cbed55df609f1a671e3bd43809e06fcf00d69b06825334c4e23fa395ddcad18a315a8fbfb11ead1835901a34f11f8da573fc5807b2e15b17651c211e8e929bd1487355f491b5dd7a06f04796657da471e62da87112d89e3d03dbe819c05a883953892ed2393167ce548f8186eb4044cf3ed240b65f59c651829f69aee25ce73c2de368e7aa26829380e8d733b01d9f9340f6eb37d92f8da3a41e473e7bd537d7b1c699bf1ec4132201d1b3df8874a6810491d79efd66d37167ce549ace91cf0ebdf92feee490c7b21c9fe42894238f50d6893f57d6f9e1c1fec52556f5185b60ab79c0285b473dfce2935d3d461a6625a7e6eae781cc434f041480b386cc80442242bc7b2b47cfca40e42376a17601219e111a9e9cf202aaaea4bee62270d89f90ddc34d007af138f0c713011121de7611444d448897b7478d02d093d89661dea896430703d09bdecfa61a05a0378408fda6712e4dc289b76e455df1a85e896025554463bce2f6789a1a5b8c3922147a22b5633f521ba1e1c5c77ff04976e85d15f62b67a7f93a90ebd0ab596e2be3440fbb968dd0f02800bdf9f9ebf3d0135284a1277272b2f4e6aff94b87887523343c7922963642c39b971a6708173d219e111ade1022f2d68908f188d05b9f1a27ee54374243fed238910787ea5bddd2384a6eac4b8db363ce54b7f43cfdb533b5111a9e67fd8b3c70681e7a968e4b620ef42a111c3d68050f8ef85912af4b2e913b9247eec81db944f200c151621d7a5706821d7a58c4b48e92fa2251c744c3bb2f123deb32673745ef7acc5cd51588de75f9ecd238113567aacbf822d1ab303d2416895ed41168ce005df1d08bff62d0ae6a6ca8e5d021123dd98406506c016c0c484fea2a3567a40996aa4a247ad0fb626a5716889ed4384a6eb0478da3538486276f8a5e3ca687442fde143df92bdf5b1947c9cdf5ab71a277dd82de6565407af55cd539e473c4d79044a207bd18e9e7af1775e4e3e51532e576a4e6c8d49c5b23e00f6fd9e398d65aeb57aeb5f63e48db9e243b79efbd0fd2be1ac20e1e064a29040cca7c7767c393640d6e328326366c328325f6774d4d74b8d850a34864a705fea869d3d324b3461ae151934e09f6773735491947a48c797aba2365c026337882be60d3575be08f26d9940049ee0e11a13d3f0425766943264b28b131296395c558a3b5b5a2536ea83f2979fcd8f635240f6bc37e1e6eb821dcd006c21fd0865e7c6bacebe6cc9979edde0c638c44ba7fcefd62ce0f68236f83fd412e20171bc2d48c29580a0016669259b3a33c913370d70791848eea8668f6374f3b4aea8997932367e0fe66d04c624b1974c3ff788d960116032e0698ee5d98e6d7b639ead44f4d6a8abf152843b43f12fc0f6843a768d34326517e36d47c10edaa66bbe7bee6a8638a2f9033f35636ddbb3a1e913313899c995762cf272175cc893ab233cd99f9b8137364373f852aa9f960a2672b30327b0751f6a04e66c6078302a005fc20660dfe0cd1f6ee8241586b605ece64a4231f69cf8f4493c2f415a60962ce4cb942a2f4da510353cc9822ee96455c1b9e8350c6558130a00c1feae611a3649ff7320ba1010e0ce1811db2f381451d3c6dc40caf10e1daf2904914226c9aaf59054c33698820a060791e2b2d25363d60dc1e3e429b1ea46f8797d0a687cbaf1e2fb70eb9633d609e1dbe429b1e365c3bbc85363d6eb8e81087639b1effe8f017b4e9919f62d3435fe530e62b87d7a04d0f99b31ce6f09643112cc513407ff0d573c81b2cc5cb640d96e26372064bf13a63b0149ff3054bf1c7215f588abf215b588ab72157588a87c9152cc5732f79c252bc4b26651859fba0fb8b169615959411166919665d9b8dfa87cb5fcfc29c45187df8f6995fff62008efd930278f66fee70ed1f2d5df4af1ac0f1bf4a808ffe599aa7fcbb3c5ce59f5580affcbb0c70967f588cb7fccb0e7018ff341927fddb6aeef24f34e32fff3002cefd1bd91ce65f4a026ec33f1505fc867f2b0b380eff5818f0ff6ba1f1fc0f468deb7fa4fd987f2ede65febd34e039fce31c7000fc83b1711d7af4f0eedf0d1bd2c061c3fb907bb861c7dbb061c73b20bf01dc8ef75e76fcae4183012d3b7e012c3b5e010950d9f136293b1e01336a443b5ec6b6e30fa0edf81806c0767c01ae1dcf83b5e36932842502e41d3f80bce34b19c2d20e19c292007200663284a52e330096e475c80b802579006405c0927c0e3901b0242f936d60493e46e719b0249f730d2cc93fcb8025791cf2016049fe861c0396e46dc806c8058025792ef3004bf22f9926130096e4497900b0240f2397f20eb024cf9205004bf22b3900790696e453f228eb9001004bf2a29c032cc96b592626eb9c0f4bf20680d9f0acf9e1fe90b79cd12b6568dbb6dd07696ffabb2eafe54b6527657c725f1a578fb9deca45643c620f52d322058564cde78cbc8c33b5a38e71059e42fbf60898884e110478d9758a30c0cb3e44484e8e8e8986a7bd48f474748a5caf5ea708f6ec758a90bc7b9d22f7aceb14e9bceb3a982e32c3db5e84c6c35e5d66da64bd4d1ff2205f247a9ad6d1d131cdf0b20f995fe478a6cecbae33c47a9193e399485ef621a2d4f5b25f2f237959e7652f32c3cb743767e4e5b19c210ff257d69007792bc7401ee46f96813cc8db9c03e441be6600401ee4abac03e441de00125f42a69c44c0228ccfcf2aa57ccbe7abaaf2790babaa7acae7adf9eb98a5419b1e45dc7d6dd0a64711788ba04d8f2248fbd2441071afeb3e48f310daf4a8ba08d29677e9017511a45d9de5f317daf4b07411a4f90cdaf4b8ba0852862f2d61bce7754c8d8f4453d4a94e1fc4acd1d1d11982c310e6bd7c080d21ce7bb9ce1018a58800bc97174980f7f221337272723c53e5bd6825f37446c11f1ce2b16357af978768472982ee98355348cec4a74c433b4ed4d015f8eef0f1fb1cf2e3b143e6aa0bfcc1211c29e323fd90328aa03b7c5cbc7fbf1d76641eb0440f2313014bf42db9072cd1b3641fb044bf9241004bf42ab90858a24fc9218025fa51fe014bf4388b0096e8457904b044bf6512c012bd9671600996e8b11c0496e86d16024bf42498cbc81abc8301f0079fe4b809420352c6bd1014a22ea4b093bf19c2ce3ac539e302cfc31d7b1e5f7c5b240684c3666117f84ac4025323e9b6481b545628a7a5d81baef9328231e506324805fb831c1efc20d91e45061576228a0be87522cacea44e444141259d88a263e30afe620b2b0ba418ab29d4da85125a85428e0e9a40a16428a2eca8140dd655414b99522300000000000315000030140e07c6c3e1709ea7a2f60114800b80a650745c9708c32089511432c610638801000000c010100021aaaa00001fd0e53dba07f6d3f22eb2502636eefd13fd6939ba98f8def398e8a77d4549a2088fc86281c639610bf21630642883034ccc1cba1622daebb741766f1a6e253feadafd0c0ea4ac8292fff36bc232071840b4148d2d0e3870012d8d0b33ef549142311c28e95cb89df50d922531164f72734dbfb70a7d1dc29b483f63836698da9f172e4ca96122ad9cb830fdc2fd1cc34f4d8e23525180c632c7ce7a0763e78e8a9ed08f2a32503888bf092f47761d893fdefd34b4a0e166b05778e9f724e4ffd0f9b9ba67af4843b149da9a4629cb36abaeac27b961b86b2a9aa59e5196616af0645b85af598fe476c9a7ec0436d3da3c30a69cbf567b04d6f60a4377fe3acb48dc6b9a9edde6e80d3d25773d116be8d2ff0ce4ae282055993411d8a8caf0c2fd0f92ae308c16f74cc08c598275b3b6be30a62656ed2b8e70736235fba9b66324c3537e41d842d7eb80c4cbe972b9e8f84f78e86060003d3b209bb3309cb57f3f9e7f46f8da7c10690bf7fa7f071766aab433e8a7a1a5c42b4b8f0cdc4bcb84c0793bef88a86a2fbcd0f238863afed021a351b8933c7b91bc8281d572cb9d88518b137400872647dcf3edd6ac94c59e0b4443c9fac1aa486d1dd6439404e0439abed91f53793ea81a0c6f4c5a3a338ba224a4186be5a2b98ff6c164a8d2e7d6098b9d5e3df6890974c8eb01d0dbe7822e1bf6c244d9d6d2d64e8efe443fde1f245c4d0e92f142875336d693c487763595367b3ac7458c0821c8c878e299897aeec9f4b08810e4cd5509fd385920579c0b9ecbc73360484d3382883fce768b90ff022dcada0dc5ebf782cad4bca7d017d270d3093393bdebe9baaf7eab9419ffd5efb0cb8a76fca60fcd2d213cb8a564fae2340a41e215c9c4569800b302036bbce61e1c0003378ff86f7aa13958a5b45300117092e2366cdb728a98aac53f38785ff10d847c62d58d5c3a5283101b64c198ccf5af280c16dcc4b241454a730b2b803e429bf8cc1d41f2ba28baf9e666a9a8a9254ba9919900bb09b2ca11554220761893e88f69974cd195a744dc7010ff8de4fe00bdbf4a0259136ae22893cc4b9d1353da41eed5e142fb3bb5157d9984fd7521d474557ada2726544820a18f326cab86d94c7b29d2e3f646b14633db0cee8aff8fc08e04b95f0195d85a8ec87f5f20c12976a8d862be3c4f61f269ad363d7b12a6c1d7ed1e6e75cc20a7db92bde3a0ae3d2249dff951366d5035dac1d8ddb04f33a66c73b0ac48f41a6651bdc1f06c9aa8eefebe4d780514871f3e51d6f412a812d421ec9f465a64b26470c913a118ede82584711d1dbeac2aa1e8b0e8681a4581dff25f56227105db0c52037abe36cd41a57338a53a95037845aed01dccbdfc611c77e9604c1e978f3ee16894b13e0f7b68c43e4fd51169a250b67040dca9503563fd56c7a9c028fffff5d2f4950648d38354d5ef25f02d2af9d99b22902c1cc0f142fef3e17dfe9de70ad341551143196383b680ec129c641a834d31fa36e531ca40ec14472a39b0b87fe8977462f46a242d7b2f23edb810975ca06c04d54bdb5511c14746d996e7ef9f00971f5cd28b859de33b65a90cc49631ea1735dbc193fca2755b4eb94286d1792eea443725474ab25018637c57a9651fec893308114bf543b11d1925f729990a8560aee469cacaca5b3703ddd4b0488c0dbea09aec8b5a310419c89795323bcc174160312706128bab60e4c6649a5e66a3ac8674aec7d49187471ddba2face0595f58a22501a07f5b6ca6c9f11143465f62caab4566e2e417cc25d2b8dcde56ec8076d9985b143ac31eb5e493362bf6b4ebe778c40554b04e2fab08ef46684823fd60dae036970e00d980807adebf25b8adde46c6ee9b28060a95a79f6d92ed526add08f4a21e2a5ed8c5f3ad9fb6613b00718b460a767055de0a28f5e50891a1a56980e67df6af38f28e1525bd097c4b6b24b81932eefb8be799642098aa21b303dc38cd1fc9f1d8c4adddb9d4d1528cb61d8e87518e41e1840237ff185b2bc383e9f89d25a9465e5e40d8342f92e5b13977fe4977e96997973f4a74b3d7f3a7a337fba90f2a73984e14cada4fb030da5bf024ad49f50456187fcc8932d59eb59b7795c40a14b3e862d5967bfc680088697a287323ef35b1bddefb088651e687e942f205ade8d1c8f0f37c197d58d2e3e3628faff7194618aba2f650a824fcb85614f552cbc9a771695e9ee8faacd88ff2c1a133be5d20fcfd9bbbecc36f368d19f2a8b8bce2c5fcb4d0b327122d9cf359c86767e00d7adb1d5e09276802323850b76e94e7b76d39ee08cc15ac35605376dd934077701e099e46f8e41f58af2a65545e23b488c0ce3d85e43c2924896b69a9ca22e384c307c4a389764dc394a5f71af4c4321ed90e1a592b670d2737ffcc4f48877a4595011ec1d1c4a969ab0dc766816301f7fc66b51a486ac9566008b21b1fb8a7544a9524e78e8c7ea5831807e595c3df7388f7f1f55dfb6017dcf50081c4d98abb0f2a7d9d84f0011a8a825e366b7e76f872b76d735df5aa2b1f80d870598d5e599226793ea94186e33113239d1465d36b16068f7597e27e82d4d1a548345632e42385a93488dfabfb1d0fbe8743ce68463f45cb29d6b2fdca4c50c372b4c5aa52dc326fd1c212ed49c906ca216bee3994fbc59cff76a968105371289a6997e5c4a96fdb922da08b0a85b854cf4d51123ed3b8d71bafc46cb96930c4869328370848e4f002f4bafaa17d235ceb866d012ebbe1a0061288e0d1cfa7b9857b0a3154b7a3a21e00e9fbc7fbea9a4bd2927a614794b417ee2cd0e6b76c820edbae64e1ae08ed5e5a180debb9a1e1864bbacd1a903b7a3753c2c20f74605dd5e7d9e76ca4b3022ed88c827144e45452d5d7cd239932c2d3b6267d47af2946c0a710dd2e6acf54555f096970b089b36ab37a29507c408c8a20a2e931d4c18a07a62fc8c88b1fdc4a42297ce778b88488423b843c834be3b4a8da3db685fdf8ff2b4f5ad78acfb7575c4f67be418ed833732e88b868434af11bf866510eba4d27e2d51c15297f60d289a6b1b7ef743ba990a632a2d0863329ce0154475a2c54698f001c046d6c61975e456235ad1605acafcd30287987ce6555a962bb004870a04193d26244c8a08782c8220a3c4996846e70288261ef1d447dd44319c7949090e937e206d2be242bbd0f858159530c4952d3b19fa480d3acb429176516494cfa634ad2903a55e6bd99c7333ad8e49e5d32e777287188e657efbd191907ebebc41c69921ce2344c820c1d960ef5a91fb1de0a82ac22dc66cbdbc8c296e47e5aaa69659c2ea68c12a864646cc0f922e1b9f065b4492689db95c29cff39b7db05d4c2009893710895d80f722be205bc499cdf34a4623d58722815673bffe1e51aef9a5b7a9ecdc549b8aa0ddf6a7b2b585db3224151a1c953de7abccd1cb08527463b529aff0b1ca106633977efd3a12f7fcdffb3390eecadd1ff04c688ad8d436a1e45f91179d151bfaa7404370474b79f2d54abd69a08c2e6a1db5a360fb47797f5491b7f868eadae870711568de6bad35cfc40bf7ba440b80155a4755a29fef549490db1d65fb6591876138a4a630a949ccfbd06ce422f0a9733de6d8ad0703903e8fd3f8ca0902ce0b07791986287f5fa73e5e091bf79b8a4239cf7d4c2bb81403c6110be46f5eb52bec593f2ba3cd66603ce0cb1047e394eac90d14dc181d30939e86ac0a974a712cf112a324c151dc643e14f4eab68d0e5ee333a1a3a01bbf6e3660ebce88c529be0b41720565a38ca6aa942d98603a64bad36e46b8635adca738f402d71656ee38be89d7d22d0f9dfddbb111c5632533fcabd5856e1c3db01341bc8d8c2bcbc571e5dc2c178e8b7361b938ee9c8be5c671e75c2c778e9b7361b9386e9c9be5ca7171ae2c37c7857367b9386e9c9be5c6819b31ff889167907ac29a162ef5dffb8186a279f8896f8c9fbed00785d70e7611c79f90de46798107103bb97d8cf0ffbb6bdff367dcce67e70dd6439a25a26205bc86ab037cd82f923935602ba297f53ba037cca67e27d0eae6903892257f1fb4a3b7f9370e7a3c82f7895f89e5c307dc97d69d76f923552a618bba7564b11a3ef8806a9076ccc00d403ca06eb986a4eb6b4af455fedb905ca560561051e94de4b151e33e4335f0dddee3e94435e31a017afaa7aa0a7f72f2009edd9f5288a628877c50565ec74550cd18e05e8ff9a404865f4c228738a023fd1cf48ac8c6f55ba506e0eef40421ab5992e69637d7002470bf96b4347f1f067d51fa7b54146690964d03384660824b3123ebf0b061cb285d48ab6edba41643ef7d867031af3a950fdff306a3a25eb5981dbbf41b1c93e9e7b66d756976e8de37088b76aae36c389c760f769cea8d6dcc45cdcd9b55501e029807c7e822289d3b93d9df65059e1c700b4d93d5c2591c75eb3436cfc2f1a2d239c518fd7b0956209bf5dcf661f965765e09e02a6764b98130d5117d4160341779b5894931d257398491598f8a0b710d9b31daa93224a8a8182d79e848d1284e2262544e7a252dbfc120125ed6753daf2081e43a4b5daf47f53d108819716f47fa5623bfed1030ddd78b24d4a46a9c2f8885a727e38a1c9e305145e4e05b89f0d5a7e772e24ab2b0834ebb8513b434efb2b0c3b7c3984330a9c3fae68f5a2307d4bd850ad055bad484db691b500ef8c2213c08e158c939a05dc1b58f9aa3e1a660b17cc6609d03542b48549da3a219fff42644e1d410730ede30be92c958d05d5a160e2ed55721fb88022d3e0972bed3812a38ddc1d511dc99c48b90d3610f50f97deee04d34dd14ba05b7284d22ee12b803bde081fb59812d2d16149f48ec6c00fd8586514fadd6bcf109dcdd24adeb2d2ae663eb9338488e9c4c73b3572a45322112d200113a6774104531b6ca09a737f4a8dea2c156198b7297948ca488e98cee2085d88c5b2a2d8956ac139d5bb4621357494907cd49b12a5a7c35a78aa5b1e4e319172f63c558693f9e22314956642cf17cc6c4482bcf727a76d20b3e8d870f24c139f4055be80f4c9cb4d244bee74ed2a3d684fb2916937e4556683d9e7131f2ca939f9e9df5fe10ddf080319ce991ca327bd4269c6f4bde35e99c1b97257626d10f92b99f35d938897e96ee361647b613f10f995bc69d93ce6e5cee9c7476e3e6d649673714774e3ae3c66db4774ca20f12f79336279ddf3eac3fe91cb72a4bf44e629f24b9d968427f32fe90de4d255ea70634ed92277493eb88ef99bdfb049afe5597b45d1ab650b0a8170c83415f327ffcef7b1b1c154eb4657274579078c2433f968e73c059f0efb18f286ee5edcfc4df63201b2b6def065036a9a34112959dc8782b7a483a40e407c04a82f04bf8e00f414f4bf9fbed590839f567a0ab31f3087fb31f3440df1524ce8acfc5e346452008a8a67b0534b65941aadf6b4212a8acfff282a11e2c6e360e95aee33f8f8ab32998e304402a6093f9b8ebae70f0ff8f3482d0e85713d699811107f8a734568c49a3015a89b89c8ba4dd264cb3595ac68c9a5060a28841b7bc0b5d18b6f38eb6d2476115e1d3204bd41394f86c5fa58754d2d2906b2a26c359d975058af0377bfa4577d6a7a86e12039ad08815b1bf9e81980f0e2cef36e7406562a983445cda838ea74d408882b12bccca156efbc3e819f3521ded08def13c63d1e46e8e10a4e335bcc28188761faf4f66bbf6abc86704d7b66ce614d108f33c0cdf670adb4eef0ab630c7e532d06cf325d24c286783cb618119accd16c8f036c707de77af38f7cfeab1dfedea86645ce618277685c8ff8c7f42818a9464d3a19bcc555f5b7a15a1ec9b15aa5ed2c5fd7d500b0c2e1047dd4c035b596e4ea7c887ff8f86813f81c45e51844a3a65c6f57f0ef5111d9c987fb743d79215c1cf2c8720089732cfc83c2d02e3f0254c9c1a2c878b8aee4dde4f27c09692e893b132ab36bdffabac3a870967a7713d72a9d3c281cb36249ea8adea8125cf1a1ccff1d44f252bec5579a5442f356c57d7676346b068cd6ee6150252447c96f46be39e0165f2c1e37d4f4a0b9f81d26f8023b93f2a02373b8368f32efb2ac043edbf7a15b340513d495ddcec66b60abdc06091136e3bfbd83e7d56063e9c7950775077523dfcb0c965452fc3a17fdcbe9d88a2222b93cb1e67288bf3c2b1e9b312426ee6833e2814de994cfeb11ed23c54ce96ceb1e4febacf7af34907e7acb54eccd8fd514449c93a3509ad5e8982395616f55ba43e6d57dd089ba9eff105d989dee49f83fc481405bb1e55403888a2b83346831a358f446dbbc15f3fa646d403480919da511f4035a32733f07f546a9eb2fb60eb7ed92eaf2076bfc218d281a8bed008d89f1685c64e1d010ae6ab772dd0bb569c7bc549143640c55f1d2308f6e8860286c628c61c4ba1e4b160cc80a48892013568ab618e7586b4c1f0e339320481b8fed1cc8632e8fd32fab71d150ea4909edf41ae79835e2bb941d4a05b151017e2b6c210b79d05e1ba0554c69d015ed35c24a12d77ab46efa8f78d020d25da3503d1ad1d42b1091032316cb59d868accd021ae3fdc1a1e25bc9e438efb4d24cc6acb62381e52ef8bb1b4a19e9b24b1bdd2adfd73de5ef140bd6ec59e185558acf62ec679b051416934c2ad87eeb4d5e98ed3e41242324c10dd90f43abc069f5576725cd3ab405e24f75af52ad94b4f5a384a2bbc44c4ad16236c31bd940b97f6ace2df621fef6596a18d8d6cb369a22eb353917e56365e4f8f0be85e9e9223702ffde2d6abef1cbd3ab29997c0229645acc294a49c722d34055ead17ba45b0eeb4901e9847ed35b3fda5d3e1fb9b5a6bb702613400e0c7737d216cde2e996085fba101801609bce6c81fe835123f95ae18e6ac30da7145a8f00aae36a74d97f16dd04266272eb2ed5c1d6f38663a8b48add9c1b5fffbce5b08a34f34bb70f77d994fe309d29e3fe241b58b57d3550b1d95665ec2d67fdd8a2aaec8be74b5210fa10726ef279288f50528706a45c87123497bc5ecf65fdbdbf13388649bb011438e1d10666fa362a6ebaaeb164a9a3c64da4d950359cda45b2495dbb1f2d3f171ec439773f70df218377f9e248103259dd50d6763ce2339f938df5d864aeb00700f8a992c000fcc7533b1134bb7e04ab26073712dd86ae335f2f8b394a78f1ff9344b9ba14eb835b2d42ca16104cd34221327ac3313facffbbd5aae710620520371c4e163e2135f7d9e71a36bd1afdba37a85a874c0baaa56c983221e94329206b3e6bf578a08cc07f156dca0b8886a223b9e1bdbbd189acb0bcac57c98ec20079374bd20a527724a9cc0f383a01b384e132644b386175f6a67d48d4672e85d2bcc62a7464a5aa8fe398f3e27e89207ba02e7562fedf7853e0d993dcb310d30fc429702561759694a89a31846d45315e0d9589315c7ec0d78d48c81ccb047de40c21228ab976c22267fb4b40b9efdd572ce05cf63a9a7fab18c79a26087eae0a40c402c2aeee3e474d1747d3954fd83af1de9eaa343e0175eb8de87e14647f341f8622d160e36e8053a7349289847898b6ea28ab634221132229597d300771149c4d6d0b87446daba3897d848d284b5a1b691f13057be2d3551aee871144c3b1d60a034edf9d80aa43122e420da3fb994d8e7cf1505368e5134849c2380c3deecf7b6d28d1d95b09d2077c39c15e8e89a58484cf519f4124d3470dfc2e8ff99d5b6e18273d34414c284fad3bde0b853db0f137ef931d1ed1041c784e592ec3e08ce48606119a95f2e51801b75be9972d6a3af3308488dae2385f7df0e0cf4b1934ab512f1112a15c65ce55a111ceac8d2a817ff8420655b6af47802a29236b21d2e999c138487038b365e3666d61a4b467651c60392d91d7ce5ff87041eded92427e5f8dff5c2be12bbad6dffb5ca45d92d9b185c5c4fb8a6a5b23628f878a06ab562664d9a31c71edc6d1d4dc9fd9d34a386d71367d18886fea452a7df281cf9ef41b4635462f18b86b83193ebced0618ab0c892dac294be7c94ab342b882d1a761056d2aeba5d9429395767152601e54c24ca2413c28d3407b17b16cffb12a281110d6274b95a75d67c8790ee8258b59581fa24ee267e05ff328e4ddb5451c01da390e878a11758b2857ee93d1326a0cfef01a2b88c2ebea894158cb812e51bc86bddb250bde7835f1c468d9b9ddcdf986db9f828c17da4a0f16c908695f27d1cc5769c926257e0566ff41dd3bc740dee3aecd3698134b74086578459613f1dc9ca5f186c7a79a21fc0538633a4642652a27a184466164480de55552c18f8cb8d0ccf50d0150754461a08701f724a874d97c11bc0840cd54b183ff233752a3fe1237aa38dcef5e396436cdf9d6cd5a9e580107b923853269a420a06c9a4a624119dc7e4d9eb9c0a6885279d01639e34b4faf2da559d3c0c085741411cc7b9cedc1314318d4b84665fc95d07b828509d395656251166a8a2a84a2227308afb1a898a94955b452d9d6a38b74f9456a2b8471a8b8b7b1adb57e967b7d23336f4ddb6ba753ae4dd200cf48bef46db6bf22f5be6705cef521463186835ae212d3481e902e6e60b65829c208e65213f4d6222e053af20c2beaa63a10dec04a4e0070b676f5111e71a0ef9f20b1f2c8f62d92a6beba3a49d332db0d9f3889a5f3b4bab4143d0b7e526d51372131292804192aa736a10adf148b9f48bf441dbeb7aeb44b2f01c72519b851c26ca2598bc2ddd91b6e0b7612ac0c8788e8c012eec849547e614a6a89414fed2a95bf6bc13264cc5cad3f0c9a5874197f4b859d83e07fdefd7b44e04efffb89d33637c201d3a9ffaf6927a7a73613260dea5f809c71cfd6cd4c969e10a29c31568c8eded2535368a2f897fe4a399938214d0e4060b86205936979f4e0b0262cabcd1893a9029e39a0bb6ba4654a09174bd93a131461b78c41da9ec6fc389f39d4b8116846603647428816d6b1b7421b615e01850e007e29215189ca3303c8a3e78374bcfca7da5851694a13e94c60ae82235c469dd7f3c77224cccadce182bf6157d7db8f7a02e85551c44a50b0eda6108eb2add6e8eff6cd7c4bb51098b182aa2f639dc9f781456c8757ad716bd45fd3b7f324ca5cb1455b3e831ff582a21132106e06d71daa98e56dcdc7b381ed643f91be77c0699456e651190b0f3e1049f2475623debc6ebbf035566aa9d025c6b57dbd417645270ed7713030cc4436d60b3904867d8449ae1c537c0066faf0fae951052ed2a7315057ca88d79881ff6185fc090c70ae3f068c0a9b20fda3e6de4e17b39d58d1b3269393ae604f4a3be05a7268d34b0a1da57e12daf42c63abb310aa2fd90268065479c98d92e0f5b60b73b805e0e5538d4395ea2e9ad308403219a4e0792080b5281157d9b05a93b950358d243b01932e0c25be8a80ec70550e32fd0eaa0868a8c1d42d7dca8f26695b0a6ba4cd7c05fa460e6a3167e4611db468d5a3f1a822c3bcab76848f667848de900fbf95a1840f5531779604e46fb51cae3b9db5a50a993941881d508649b8917845f379c857f47f182c5d945ce38cb8b71c074459222e31758e2c2ab5f5c2399e2ecd09e5ae44e7d9af682e1b8d83d07c04297d87a3c215ca0bb274cb8f5324d07c0ee76a579638340308ca9829811c379f5b0d404300201247be056d7828b161c40d68d7c7743fbdae91b27f1cd90e985dd37d48bed6b4e1423b5513c3b46ec6af869b7013526d4057901252bcb5419c1674507eddaff2bca410d8d095018c45ef169c671f24c53b95e88b6e6a22c44063cfa2762529d77139b05d75adb0dc5fd8eff58b0deec2261b56235d368f7fddc1208796f91d72b953cb0e234b234b53a094a33d80db9e3c11b0c71ca81c4be3ec13b0c37ddfded9702ebd627f44a7a783db645c349c85bb9dcac6d50625b60489619ae22424137a16027c528710f9a10bc99ab4bd0d5e401eecc3f22d5cf953ccd04efd20ef59feb3401074d7e87847b909ebf8c13baf2177326efd70dfbe556fe8b858d40adc26ed4eb4ab49a6b57c338cd42792ce760388ab5afc12d10dc05d80f8e6eae92b0bcc82305abffb0ea7837d0d3605dc5b3ea334772061ee40cdea66b04836ee9d1c06c380f028ed46aba4d5089dd25244cc8de2ff8cc120835e54ac1f28c6f4d80aed0bcb2d862584d44c55d65feb15f8830f9be6950f6f6cc69108a42f51890a787b961f4623b90b04cdba18063f560c6e407fce6b8895a1852240a2bc8a7c6816640b8dcbb7b7ba04a4ccd9fb674bf9619a89561f6bae0e402602f5b36f1833f65609e74ab45950994c71dd2b8e86efe9fdce68ad908fdf691bbf0dc06142899838e0504772f59ca1a4df6f94f72983d2781581e18130fe28a5af71905a114444707c2c580a50efb3c6823e83af5364abd67a16e7a992e8837688b6ddc29dfc77306225f089d9d0f6157d341df28984860fe89f031768b8116a0e2910bd92352ed8b846a9fb2da935887c437256507749f95662d553a337c5764422eba1919b69242fa84572b50880aa57a85a834a2d925b0a950419d3b56245c7e118f3327047ade0120b0d1cc095354ba547809d4471ceaf78ffffda8ce4148cf100651832a4c74704466d180dd16316f02b8602379e8a7a69b214b444e9903fc5d97d73e0254f8219c0a526653566ab008f9dbb1765dc30333f8dcfb8c84e281ce78050212d9709f8816d060238e5d4771c395134b2915bd85504b85f7c97d6c39b87d22c21bbcf85de7450c6c0cb07c1455386dd08c6088744afb560310a613fea83cf158e3be88c2b91afb5abd6a3a580101a6f65c62cbd920cf69bc27122d305c37c53b3eae8b6f9fd83e14471f6c6edc33bda4557c4dfa22e34804193d2fb3f313270602fdd1a452f46c7f2335138791c32996c486e60e96b4c03d196f19b09fd8670e79354ee1f09491a4d13110853cb40f64d4bde9b11037fa49c41f5cccded3be0d09b72e90f749b7f7715541c7cc458dd0cca353b1400467cf056891400ced36071e904f6ef7fe9e4c044e6ec04b80b19e2fadedba2f8a120d347ad42c26d9cb0458f278f9d01705ebe1c4d8d3ae7bffabfd90ef88f55523a87df8c1dfd70736c39051879aa7f446d05c8e97611c09419f205ffc469a8f47738da3bc2ffe7279facddbec95a7cbd2af2090754df3b3b1c23bead465dbbee257baf75b21b13c56057ef305714a7ff9402a87744c0872984683989175242ce94819a156d4ee4161a0b995564354a8095b0ec50257edd9952ab146a77ed136b7d9387d1f5c4c6061efa6c2fe3f4739cc15c086bfe3e916d2c3ab34ae643650bac1d68b104b9e3cbc302325cd7cd098617835f1754fd1de68042f4b80840f30a76c674296183401ff33d1c01882119fb4c22bb3b0eb366ef89aba48c7ce82426a6e421f3f3900b2059cc560bd7389e2cc1aa8b7eeb3a8e15f754289dd7b656b3719dade0eaba004e846c390baaca1ed7c2fdd70d2ff9f0320b16269b32a47a9c99719f2ce41fe31666a780f123dc0d42056550c292374b1e86a37ff3220c4dc69d9d3d9f9aca9f8004e0f88ba5df8ee163ca5be9a88ddd25a7154ec253b0c91eee3810fac25331433d518007f652370538c423500e17d22f3679af0a9577a692e45df71fb80a903d213ea030fd94c9a3a9a976a5076130b5d3687e3e70689ad4297094ea26cdc2ba17aaf84abc6da9dcd4864bd84ec9eced08eec4116534bcf59b646ea8f7a57dd656ab6269644f3973957bc6d1c8b508a85cfcf97168c1c6ecdc56d497fd79fc79f800dc17bd5363974aaee507d980a2167fda963ba4b047d7bd93792cc99b0902d69692c2ce931c2f8e07e938303db248b6d92aa801072afc71d40e53d7fde68486a9b54f4831c2a3d1fb7623a684242ef5d16f56c11922e2f1e89750afc2028065fc49f7dbfd71d59ca4c718a94bbe512e09cd12a0f7cf01ff81f80095ab5cf7ecd3ce842060ea975c14d57f28b1101ed98a9c70d53a599189b58ddab1dde2313b7f66ed7f1d903bc8c29284cdf027dded4e21f9a3d601122a988ff8a0291b07ffb6e4d49affddd6040211817f860948558007f3db9654b1144152fd614b9d7c05e5d2d5f03320cb8fcd7b3803304c2c082520d71ecd711c318b55c27e92e6a1c03bb3a4b0190f14e663a383d139a58b52f80b324ff0d98d39dfe0f2ebd198cea26b27107eabe7631b8802022bd196deb347091c1456e561c952c0886b89d6140991a1d034798985349cd6f4d57bfceaea5bc8823106b7425afb66cc145c305843c5a530bdfdb0cb7135746c5ff00c927a9c4a224edf6d83dc2c7255ef21d52b5351aeff5da96fbcac58ccbaf8b9f9f1e36afe27c6d6013800257839a6fd8d90eba94544a76462f32a035a44ffb69ab4b6c056c8197be6b54d3a6d5af1d5e48c12802afe644554012d8bacedb2006e7ddb625e08e6413aefc41b6cb71e7e0340446e8786833ec8c174557fa6d0f7d26a34cdf48299f43e1de392e5dfdab20a6b4a96216f98d0ae8ced2297de9c6990297568e7a0aa4d5bf0496527e8d86e8cf014ba353a2a33340fda89b4b2d7fc9a59408712927a21094ce4d7112c512a5e02f2c3949789c8be24abd7547bf0888488bff029f8a1709e5fc87431b053a6edc2bbb7c0b5c8094c9996b3d1b569fccfa0e192d85ac001fd84d80180ba5a30b1cf6363abe4a99ed6ef3bc3de7b6343613947e79c2e67c7f48d41f004582cfeb8d07f1f7fb1e7dd0b4b7b708a32f25362d87c4cfc97fb7bad7bbd0c8581d072a98e84710508973c872f914fa7a75a9a2621403d99c1e66da09e7d27e90c37f133e6319120f3cf0645445308a082da7e6a00e40ea2ceae39172b9626c3fbe41654e141321a446b151999d23ae9a422f6777c4e388b6a4cc5333416cc490669934917746323d1de651a4e866088a14e2029b8988a454041fec967b129e4a474c0674da10665125e11bb7301904fee8ca8eaf5c3c6dd84dd536aa34cea2a7769246c5f523bfff7cc9c9e56c6d5229176f5d054b47aa43c192ffb7680684aff45ba93a50e6b2c5d600088b6eba8653d33fccc45008085311ca1d30524105a3fbc976f49fa01a39997bab6edb649e4f2f57099ad20b1f7fff7bad710b43dc9d77e1f7efe055b71df9fcd25207ffb6f0100d141b862e6f2024274842db8c4c41084015e56143bf1def9d6bb373b32aa60cac3c9bbdaf6e3c20da3c7f961f6f0cdb3b744920b94febad441335fdfe7fd44fd9a1f77d354915796b4e2a0eab33616f8bbe2b356932c88acd4e564c4fa151ece53b4a9f027b881f2553dc87be7ed4fad522901098bd2e495057ad7fd6110e45af3d7cd51488461a5925b76a5bc26336a58d4381d4ecd55c008ebbbccc737f4c387fa1d1de1ed54ac0029e4c1b74b1ac3d022efb4e2bcd20d3a973e81e3a956a7c35a79f8607dc523d7dd67203303d1f9d800d648a5d7497c6f04996b25cc991fdd76ecef885f13815ea34ed8b310a5a455ab7ff91208ac6c2127811dc51484eec33d05f51e18a1ede806d2815cbe6b4d6963117f0200f184a36b30cefa02f980ecddfe1fbe93f0e834d7a405242370000d19086a2938b161f44d382fc9d4e3764c3de842ffeb7a64e28725fcf9042144176987cf1eaaeaf3edacd9b063e2e92e5bdbf9f5ab1126f4e05455134bcd6c32eb70cf4c952f0ea12a0822a40ce1fec3bc608359fa394f647efcc9c715f3dfc26a6e2beca75e0638688aacb381b2fb59ab56e57d12b675f0bf22a5690287d1781a027fa69f97ad65a77d0042328f08cc3dade9453203bb8333da0240e57d5d4fc7bad114b822c0090130cec914983adaad640bee1225548e0a41580622023eeb9e425a5756826e2b8c4257a5500d5d14379d6b00eac76c251f498639dafdc52b04d0731d86f3b96b0c068c366e63c4c39edc01847b90bc0c58b6e333a3832b08b27b06164f938f0930c8f6d74759fd8af0bc3338979559fc018bfc11e24cb2973ac941075e700f66c818a764d2c381a4ce96a67d48e7d400f3281515f6a19014823a8c5b5e9c70f2f686db4ed4308cc76fee9f73bf91b0c701090cc3e095072de76d4d92d939004638cd4133bdb7b3e2a242d39e174b80f0545cd2259447125f9f93a0ff392844224217681be5b10f0587677fcf716a3c470b9e51ff729f82ead9456db2398700e6cdaa8b607954e422f678a899c8282d4b40daeeb2ac16239503d2a2545cbace629c2d0ada00d3136ceb6c6d8b099fa3223781aba6d9da32d897fc987f1fdf04f5224c28bff0797f096496de154a2a31ab3f56628dcd02710330a1e958857a552eaf04abb8e8a3c86194c9ff959a6f337b9863de39b1200fcfb990d7c2a9e1e78266eaf45483805f37c5f664f46bc5e5524dc549c894b5f7b32072b8884c4a65b7bed75045a80616018ab940bac565f5df35eb03c57cf6cb0db763191efc972697627e06710fd558289fd70d997067860c7b4232d3b6f3fe8677d3bd4feb7d9c58ddade3a8b7ec8cacbc6c1acfb1e03367230ebb93a124ddaf83296919c0bd8a10e68629618fe45e43fcf2ec03a2abcfb497b36edbca8703a7803374f10f5f56a8078fbf224985018ac19bf34668077d09164e1402ee2171ba96152688a255cf15e86a4d0203229ecda2d5a28e0e00baf8fb7e7c290ea23a9f539865605cbcd39e1c9bd70c5f2ec2b1f7509c149a361ef2b156fde56a75595426367ad2697afc06a5f8779dab06352ee1170a792db30f8835ce347ade0cfeef6af083dc865672aff496b273984d06e5b91ce007a8dcb0a2b2b5e2c4a1f9a7521921cab34923e50555c2306247d977293e282cd58a3fd54f14fc46d3ecb51175dca963fa356fe1206345b4fe1e31a263d61330e10d6fb2ec47cb6f63008fdb4c70f22a79a9a03a34dd7743427590ba6a91b5a079aa34b984eebca7bca1ef0e7b4d24832947e6f07931fd77eec241227479488be371a4029470560cffa882841290f41db2c0626a2a96f1245f2e39dab7d8acb6b263aab46da4c1922890a8cadad8916fffbab2b95e7a82c2bbab1aa6b59ca5934c14fb3c4f36030de1e5e967c8e93427c41f0ed51e564fa38c9810a2792c3c6302a21eed88ed7a2955d9cc0df485f92745910f4c8b26a0ec6a30d65dd9c634730112bbe664407fe615ca77be86da8d8d46771a41311ccc98a2693a940e05a7993c716192316a83f8634768f30bf3f6dc60e2ef4931f898e577ec29b9d24fb08cbe8a0f700fb28431cc0ec6e064356a6b637216123bf480c3eab2c4dcf3b3fb7a3889d1177110df79b8c264867d3a0bf78e301fd654e8e108ad845070b792644431626e22d0323dd57a975854d72038404a00a5df33e478a24527ee955e514983448eb01e746d4d33ea1c40246a43b79ce49ad334eaac8f3a4421a27f52c79642772f1f0516d1954f446e44b91b35b087a832f17341399ee0daa29c5ca2967be973d2cf82b053b0057c612e9b4c047b7bb0d7df103651c02a225017e3f4e2fc491d36a9069c9b6d3513bcab75f21b58719ab75b285ae95c14112cf68c973c40f9f447bd05a00226267216bde84ead8907ed6f06efc46410eeb41808fbb67e9a3499cb91da78482dc7782b7a0c1833a18055d7611be34174e438351172aad13b845881224943e0034d260f1ba14363140975e239c20b25c35154bc413e8da1058df327503d55297685d91edf337df77b6022f093b9d19b2046ce082ae03560c5339dd4ed6a4f9ec5609486904b433341b47d957fc4ff725b01b6eb0a353f6a2cc42dc91a6029711d225b80c7029e516cb49658925337df29c1fa0fd7f2d49daaa1c79ea5e3537e85e20b7a91dc4bd1da498415406c5004bff4bc74c88b368369968f1e1abf9ae8bb8e1f6f36dbcc582ddc8bd44fd984a3d9da52376a8fc9cdc20e1c078249a7dae98c03256da4cb0cb873a318fe0b30b382e6dce4bc33b8d0f430189410471a1f12dbbc5860fa2ba5a63e7e8859e53b973bb4a50f618646b924ce6a4b2a4267847807d2ed09d4298b5866469d3b346f3b5b5e05f750e8f2f841fca881ea2a3e8a072eedb7cc3b0348f9b5ab6b213f547d3e56709f6ad2abc960767f8bb35350f4bda51be8bba9a8718a839c19d63a4e190d3b2e6463141a80481f5818a2d48d13c2f0447072068cff158ac333be36c2999f2920be63096373afed0e1ba144da10a96829e63ee38fc8db5ff821712bda2850f642974d2b604a76acdc9ad49467763db1c9bfbc08a3be064548a40bd9f144b15a3b8c49bcb62e9faf90f0a18cac264d5f744814689ba6ba41a4a1904ba21ffb53f5915084f882c55860834f4e827e222b897c04a20a4c45cd43005d9e10c2f1f0878363d95d83404962070a892da0431b0227dc02d8ae6bb89f1e1a8408472569963d5cf85244c267a14723c47242bf24a215ddbc3af4d813dca31808cdf4dbd593f93e9dfa436d88a3508ced791d59654d2d02fcfb6cc960d7e2558b24bac7b1eb388b380a88b2e838fa49af4281052b9c6829062f1166245a818686ad190cdaddca19e09d713a58ced5eddc71c67a5e2ec43268851234e07a94dff2cb6f6330a7a68cc4806bd516b7d8f3298746aad99788ee868c3ee1d392b9475649651e7c81a58dd5e26a460f54a0403941fdb7bf1a1a37a54683ea16123370c1b47ae46eda1e1720a1287c9dfc53eb7229620f79e40672d0dd9cf16cb515b678a677ee6d82319f72950cdcaa23c63650831784a9634c1ae7c5929ab0de74570f79abfc7b951f119f881227f96a1e26edd4806b6e621279073723bda0cfb75dc37196c725b5498a304d37a318ec3ee49df6fdb215291e842c507ca80ba18edb7c39da46e0f59f41514494d2ea19c8ed96cf38d9bd62215d4fe9adf73f08dda04b8759ec4ddb771e426e71f8653980883a48a0cdbdf68da6b3729784911273e6762f4fe5e464ae08dbcb1c9420fe36f75d5372e196803e8a877d0b87a029b77c35b8698a28344510cd998889823573b93f082c3941ca9ced0a2d58811c1741345054d0aa18b50bf4adb2929d8fc5c6cab2b33c5327b9b3a129413c52a2c41e2020f7d1cd465c017c73cf96167a05e1634ef210f00e84e0fd8dfee5ec15079c27741625fa7dc93f7f3d23f1baa709290c63b017e600406812bb03400041fca8725a13b51768040ef8141baa65138f354e664367af3421cf849f1107803b07da0eb8f772c13f9874999e8b2ff3e8ec76a5d06329cc5a98e594a709ca4d494e8529c4402d44d2558fca41ed413ec1bd169cba43ed706e243f2fd00baff90fda4a1601a0c5ce1d979a1f6c0607ddc231e572dc9ab8dd0c9cddb38b522db93c5af6c611a3592f6e27dc0ab1d4035953047d5bea9026117edf8efb4e167b1165f10056afc2de307aa08c289d632c5c4a3aa84a4ca3830025cb69f2ccaf2c7ed9d98ad3636828444eed78910923f682af01c9574cfeda3b0c20dfcee81b2dab28a7408ba57f52be12b93c42919158ead446ff9b9d1690689d73a4d328a2b2ee182329a6df86dec8b6cac517d5367f36a421ab4fba7369088865aee90394b6460c16b091cd8e1e66a5732991feb3b8a93932e2d622b7ce1738d5244404b5b2508d6311ef3f232c00b018f7126428c93a4314e8ac9002ba23a2dbd3701d10b0e74dfca2aa162412ccee33e3aeb544a60eeb694381254751031e73a79eb78c40a46129a87d26369bc38c358baa291c18dc4d000e8ee826d517080149263f83d15fc9244ec9cf81c6b853338155cb316d609fad0f0f253e738c8c01ddb122fc1005a1731f327b2b3d5baa78b21536fe6bf6353f6e6ad910d8103247187356f18d8573d36b30fbed761801d455aa41fdc3c4c20d0630152de7b616bfc1dcfd1d009f36ddfa2c4fb249c3e75865af7e2d78f38b362a6dab25d522b9315fd65c16485abef7a3179ba88a3f099060d76e27c8fc316862f6349aca1f67b86f2ed5b228cfa22062f74ee58c5aef3582b72a2b678245055be5376505c587ce0928cf54a132316798bf3cb68bdc2a4bfdd06c74212adfb6f27cf3627b878c7e566fea8034be45bb3710744798933ad3b55739a77a82edb89c774bfc4b3de819c7bb7ea84cc2dd0e88c1d0d85408e520b650df7ef0c793d52a180613bb8138ee5e517ffc57da0b90e2fcfa2bc2c83aec7c90370490b974136e1eb080055cd24348813146c41fd991525cde4763ada96e979bd648f0795db5c13e61edd9f558fcb573ad7c93321bc69038a4a5679d51572a0109d804fddaa50230559e8d5d97064d95990a62c7cc7ecc186aabd6bc6d42db8a4240d9624151918c2d1cfdc32e0937734f677609c6ec8e959368a9cc46c8d6ba3d1548f9002eb0228c427bab740752f1e23f6b59704f9d4c32f0e3afdbf58852a0cf6f5ba80b268ff1a62495b78e5960c8724427fbd3213c0692c5436c66f0ce345de625e21891fff033772d9388a1405bc26127fa5f8ac583900ad6ebc312210b06454bc82a487d8f77f292ae332e3cef83ad34d725c663f7f176bb17f8bbbbd0a7ee574f3cac5960fa0a38e8008832deb35d95aa83ef283f04dfac61e78ce9286a404ca2fa0a08fdfdcb35478807b3c9341bea8133fc142a4908d519afe88fd5cbce581e04de5d588d5ac4ca0ce3c16fa22a224615bd9c4e2819b0a6e5b1c8a9dc95a74f232508d92ea385995c5e2e319e1775df48c57acd47940bd930560050c8ec2631a0badee3524595b0d3c111e47f0cd3cb852eacc6f3c7b95a40aa9bd987c855da64e4989c777e84e537c3821fa318f452066eecaff3126201f11f8153d76032eae111e163348febda9537cac59b2ffb3a65c36967edb0637466e916da61e53b22bd676584f1f48bd23dba4032c72be855292fc03bbc5f67e4a59a388d786a4c224c81a0306b5d561babe984e2b77f2953db4b6383a5d5eab7e94062f08abd3541822269400980c2f0f7e280072afbfa372d5426049af49d29e7360cce6af6f29784821ad985d6888e942a9c0388f3bab3551298c9d3f6ac452abda7a3788bf3213306f0b91ed91b905d06b52ae32ab1e2643696c0363e301be7f1ea0f178509fb19ce592c970f52c861001cace3c230d451f29f7b7a16ea0d65279299491624f05a1a7210badd8983b02ce3d748e6f11fa49e6f547f6fea9d35d53e58cb282ea0b9c3bf248b971167ed6d3d80bd96843890f308fa168a2236f9225dc744f3253dc5dc606060d3ef8f302a6df3edd822e426d70ba0ffb7568323cf4d1841d68582698c698b388ac25e765c2082df63d5dfeac1fcf26928e2fa89c39cffdacdd772d3ff6258f1d6cee65cf20dec6847f49d44d76a43b392397b2ab19a65781273a2d7f9d314f3c63dd48b0676a16d7a5b80bd98ce1462f691e97081190e7044385332622cbdbb72cb475b1b92485967a3f4ba752876073e636f7b374573e1be4fadbf3a001229188c1729b831161e476eee231d7952919ea9bda00e6f03755a17b4288c8ca553f366b9bf0f410140eb42e68a0c82813169059265c3fa6325349435fd597333cb819e209c4f0503d749978098bfade7417f0702ce4a6ed96a6513a8de6a6999ff3757aca30489e904587896dba01a9ee4f21136717b4c8e1d47a4e931b34898e305023c9d6249f2f7c59955d3769a7e78cf5c46c9853b7b6e3bdd06ae407343dc228251e885fd0d0ac70d55b2e49fd9bd5144f85716fa17c9c711deea17fa11bca8a8011744ea57c1601a1fb160f00a62ade91183a4ade0b164307febad02d30801229cd5b8f242c37954d6b6108c36c5da1705bf85f57cf00f3b232d24a209c4ab7697b02f8ca870018c743251d51a4bb4a6deda4e2954d7805ff5c5a96884511424ff437380dd95f237f620b1c0f8a36f3cf158eae145f4cd883f6caa71a249835c9dea581bf953aa097b8d54d00d72048844592f61c191607647d5889324d10311ee3395d6bfa94df959c9038711e7ca3a10a936e5767546da0af6ae8e9df531508f78efb22aa2bee089281d473124374450401f0fa1da29f85063579ea84b4bbf3fb4de3d7b7780b37e5652357697e370365cc6ab1447ea0d21e2292351c9ffb262638708c7c7a6a75ec6af77a7aba978843ec8a640a6d74b9573cc90fcd18d7c2bbb650df1e75d3a78e3f054b96915482dd5f254bdb3f8d76d096eb9ebd409706e533005e0b316c05a872d54d1813bb58f1a8920592fa0d7fdfeccea3dd039e2dc1e07ea6a43b47ea40d90e70e7a17796ae40dd1ccfe554a0fe7faafde579f9e8a1d99e2f3d588cab0b172f02eae7b9e2cad7b08b86795507bab777abe420fe4820a9470c6384cba891af81fe6e0336c056d1bf2970b70ebf4fe5b3029e3e3b3356752f6eb0537cdd0922300baf2929b343b19f9e65b5a38a07e399b1e5e3e41c7f66994a83ed39d002e225980454686effdc506a85ad3e4ff4a8a81332d0df491feaa7c06f9c06b67cd350e3cd73258e83105576ac3776fe911a6febc551c4da3ac2e175c712abb0e83793b4e8ba23d79e16536dd29e0e4e04be7eb6423fbc7f7d93e7d3b06d8e2fb1e7a07d331bedffe2ec2c32f94dd683351407aa4a27b9fa64c394bafe3c94f22c3a41ad13b8c424754064bf1c5a3929af24c343994a5f23c962ad8a33b8293c29db54cbbf17bd6de2df094a27c0bd4093957d7e9cc60fef798ee8f3300b90449eecaaa321d632e20841075d900238b86bab22175ab1fd917a7b3c060e70b925cc6fd6c68b3bef7f44481a098144b06663d07a1b5451c83d8205df0f4019bcf6837095a89f5b4f3b05ad0eb73dc1d35918aedfe694e87cc1b1fefe88401097265789e0100bc1fbb125664d51b4e50950368be071022866f4cdae2e0be925ffe74a412bcc60f936d30e043688ca82f2caafbaaa139878e22a4435cdcb7c4141b6baf9b9073394c028f646bfe2c487b0fdce424ef1ffa2c920cd06f1b217b3024239e5f0e2cae89bce47b87d7d02d1ae52caecd923bce0a238f448b8d38ec91e1e24c8323607cf530e235f576a1343cb484aacd2347b3ce14c4b15d435e27a2fd4fe3b30b7e88d655711c623aa1ed408e76a92c7e9863a96a5f86afb1dfcebb40b85a384c7e59a9601e2e2e462e61487f8c7ca91339d6f01160262187f2b72c681fd6c70efb676543a17444706fabe4e5c5f97f65075fbbcbae9e0c29f62f7006c0cf50c505a7f1098f633610921af0793f8d588401f861f85f919ac73cc99e181142005185992d6d4bd43e16f4e4ab94cf467c87ce3e325f656c21deff45731752a4646d75b2f191cecfef874191c70c5a0812d7cfe60b4ba153e417c117434100543f2fe3df55421fb8c4ee80073c1241396ccaf3247f096ae99b89428fb7efca22e32f7a1064354f194cf4afea9118c1af490b90626d51318c0ecc9d277756f341d0b269e0c38a7121321f1783429095a9e00339001f26bf46f3f6c953fa7b8ddbe1fd645e4a6839ea9e194f91459583e46c4383eb878767fb18903b8ceb27930630cf3fab1b02725eb42ea1a828d404d76380b553a5b907b3d93dc268d9a198398fb3b978987121ba5631a382671f0d07eb9a94bd56e8da74e1c216f94bc0fcef7043865278df6956cec6b85394df7cb9c7956cf1ee408452e9757b82b261058df3b54e40d54d8750b5bcfb9b910b6a1b7e76c0b20303022636acf156a2cf215022e8dc4af9e6e08b4bc3378ee7ba04da1decfd99ae0146ee617eaaa4ae4505fff8466fe369e9e3a22162d98635365aa590f8a224365b740516b167ef4a1fec3059310473a2316f7a54506cf47593e8c56a3ab97506945a67be488098600a6fb4ba6514773ea954104c4458cf623adb1ed72fbf7480e2b768d80daa993579f513ba61ca45558530d5a90067d46312eb80b272b92d82f928d225d7f1e82fd8063ad849de4486e884b7e41c46291005ac9191a5a2cd381f63cc13623f2bd9cf070ea2bb934134414f0909341953f3f748ca8f0eb33916f4ff5e32aa83a866519c7d5fdd83e060cb9731d3e1a8647f3814e44dc84b2787ba963a1a6cd8e160e35b4b42e551dccbcc10090d785fb8adcbf807774267ba6a9a71341db2aca392a5b1bcdde4e3d2d078dcc6381b8b4c8f7af4a07f62d7fb433a5ad6fff4726bc5dfca5c70feed5cb6c03e6e91d6691cb5c8270c8721b141171c32d18922a61fa45efdf4effad96cfd0f2117fb8e11b5ec406bbb5b485a01dffc4af7c9f997e9c451c07215ec121e29c2a57f7079ec217f9421be6b67c2b7ee0f484b34077ead5538951e8cb4f2d4fd37292fdb108be9d2fc1eb728e9d54ddcba7731b5cd77d2d442a6621aca448e20d76bb09eec23a3adbeca648938d86e759c54fbe312cdf2c5f809e9b98472481f400f2e93e533d94d9768f0fd7f200c543b4c9023ad9a93976047812c995b13561d507860c6569d1c49ffc4ce24825f5c27eb0460d2b880721216cc760dbaa9506c28b84671be511c59d8c3c14ac5c37f64ad8bbce117ce312f253f1fa356b8a99ff8e0f2d66b0129b32cc998e42aa79e8ab3c007aea6bc8e6a1b2c6a68cecf0d33655cc28c06d99f6891d5e127dfe25e8649fe7e15f077c95a7e0160250176162e6c28209e4eef916a80875182fe963a432c7531f6e2e0ef8949bf55e9123549ae067c335518bf72b1fd91ee6c67519a457e66e131fb9a155bc76d24304731d0de790d1b6274a68cda662da540a349ebe584671bf763195f7f1355a33cd92cf7c815c8786cde852236776ef5c523e9a24e97eaffb073a411b68c1fd6467bdd50eb8632291326c18517361bbad855404b8c9d10323a49a7c09b7c172a0cf3f9644c16ff4602f9ecc1922d6469079842eaa8bdbec94e6394a661a8fccf222f082e882e3d17ad26f886ee19dda8db58ac3bb61110b436964489c1e2dcb9d4426ab00d34c0fdad4f3e2d90a30493f10683a75d98319d6411ae0e9451d297fd981f72249d4b67393c18904b747fb4b1299f579e8e2ae908a1e7760231f797c2f1f04242d3421464d1d8d78451e9c1f229e9ecd280ff9032936794dbf114f07ec72132ddfa540e0c47e55c1b603c9af082d7e6f6c912bf3ac112b646e280e894930c028a5264a6b003bdd1ffc6170187aafdd786d603cbcd412291da4f0c09247ef2674913d041246d42205a45968abaf6e4e5e675fc75a61d81bf27d58fbdbeddb76cf42aecbc033d232ea5253b5af17a18c1711ce69eebd739b542cc094218e46f0262d0cac4c7da81e5a1d2b8c0fda737e4b1ff58cf90ecff8c7a2bdb4c16c0b71879244bc0274bb7251be46a3bfd2d29426ec1e15c904ec359af22fe864ed2fe542e042a520a7e15f08d067cc2e93972caf3f0d9fabf4f2e14d9867008dc6694db8864c119bc5caad545c4bb21d9c0fa78da81efdc3f9a6115ab6dcc9d7a62943c39e1dcbda242fadd953f0d762a29c38588e858daec53ff6d40721e4d13665d7005f5a5ef044179eff415a622fd4a1a53afb0e0dc37be7259ccd3494df6172d4a5491ee84f5f7d848a737e1c35f656054d6312d507f8622ac9bf1a205ca8a721f3ca60e55506c87f096830be9509f2401c85b3f4c1f0d1fae71cfaba276121487e691e87f58ec884548c20d66cba0a1924060b28358512239776f40b32778261098bceeea65f529aae74d0459e9198fe49bb113ff5a7822194172d66d64a58c2f7863f502955f7d48e3cb36623c1aec3d483d0db14f0fc38c2bcb781625d69dcba2dbc5beb98708c034b5431e6f8154af7adae20c3059b6787935dff169c7c624d88678b63e0310c9cceaadecd2492b9f48284735badafb0067ecda02df8e3c4d62f4e83f95840945a9454fb03f0a657bdb7436e733bd840d71409482b5b79d67f92ab5ab9c6093d056f6c81639f974be378c78df45dc1f923a16db01a7e9fdbcbd0461429268a28de7347974818a39c6602ca81069f4a9bb5e9783fbfb01afe63a64010d26c3999a6c033303e7e1cc2bd3c60d9fb607a6204f09b8e04392148554cc836ed21ff4a2cc0af8fb0b0e4c242d0f5c29071b8c2c447e4cea0f11ec7b1810061f403a01adc4c70505ccdce6702a79182d9cd874358ad9cfc1e22d1c3642c3e598ba2b2ff6f52e39ce948542ac0724864daefa7fbaf412014c1634ae86cfdd72059f1df2931057f5f284821bb797c0451a16ec39e863cbef984543b266f99e84871e1776bc3332343bb7146f1a7aba794b8f06e671042e5e4ffc26b1837c3e07ac479fd6cfc00ed221675542aa0fa2ad3a67e5c8049dff89357d5e5895d754f1d0fc8f2d87210a2a6389a69b0d595a213e8db46a4b050b240fce94e7574f66134d407bc44f436d7c01fec82aad42168840b731cb03cb8bc9ec8e3b41946d38751feee4f70b061e986398c4206bdbc030bba21965393119ec77a8f39064f0e2caa36b07d69588694bebe32eec3f2ef7cca67629e7d32328c8eba357840acd9901fb20aa0c0c8962e5c9a0b44479dd70e07a342583103f75bd25af75ebe2ebd42b2bbb6ace7dc40e0d96a7d85ec9632a51ef27141d15afdd27805cf679afd23f8275f8139cc50c9af703d05263364484ff4127409b7b02e01ee76c9ab481d1efdba8738021df1f3983edce8f551601dc18086006228a7c064db8c91ad4b3a4330f627e36360d06c6657c0af430dfb72dbd1393fa630bb64a9f7e087958b234ee4bd6b32f552fe379eebc926df040a9643f5df404a5ee1da08336e60e73d7cf77473ca15af66a4aa49cda99d718f1bf65af493f2fe2d8708b82295d863e8847f8f9ee30c3eef0a50c73f93a7f6cb9c36053fa05de8096d451f1e7b8d017a5a976a77dd311b4a0be48f0153aab108261d48b323f50cd56d886af34143973e70c1cc2f02a0fc42f59f7cb6224fe9588698a44ade8ae735ce0d96509ad30983d4d078cd352b30bba2ceb1a3016ae3066fc2d87e19a153683af92503951e7da566b86230b5df726a95ed9ea9d833fe90a4a7e9d29a3732985e8b427c7a9a94375a0204e58eb578e5686c20a56f0a609daa8732f5374502fc04ec57250f64fba7419cb488a128883a914b7333496e258a1beb57646a05466d4247e837d7e51caf72044ac55da0f2f2366cde278219c6ef7a5e73c77be1d272969c7fa9d6925a535ba040ef235b10cfb7a24185ac42cd350022dac4256be154b16af319b186b6a2c9b558d01dc26a9e5eecb3d64070101313e91a51af79f72a3e223706f6cabb9b40dc88aaa542ef0017d024a2e72880ef6e1b827894205e45c375a0aed832e1dcb091ea20a91ba0fd2cff4c44d75f609506649b52876d7ea6bbe1b7c1439176ef3ce7f5deca68c724065c503b5fdc67eeac0e3a920315fb72d422800122741cb2773bddde0742a313440810891e678d366a5f41c1c2bcba51a0b6f5c06a45d875f6c1b5a0866de03141ceb29b1072ed7ebb1e84d22f332cc7061eefaa1e3c26e65e160116b2223a4a1aa127f0faef07174ce1505590d3573b629e667938eca523ce34980acd65041e549436fcc00933a2502eb06f6a7836722a1f4b7d8e2fcf83b1801a40f7f9bd011d7286005577c4cd23502a7049075ace07f427687c4c48a408aa2b433b6d62615b9af7480da306e49dd5f3dbafa2d44c8fd41e323a85b511407829aac141694d4a5332ccbc75819cbae1157b304a90b29b9e65a28132612df19772c8717d04a0fb09f73d4c4b0211b6402b0c964344343e68fc00a03928075eea591e7ff14347a62212dc9f41f41fc83680e41220453ff4765d870a80b06014c05509285f100a4a005aa85cbc88568ab07050f56bfe1d3b10b3d30bef485a8ab6b4d8da6110f0ee9046d9dc1a058409313ff3144720f502576e96b983b107369cf20c9f4dc975188ea64a995211483c558bf0c4d540798b2a6eb232a776af9b883a9d04608163f5e286eefb82f370e5bff3dc36c8775b9ab689e16ec88d1a245ac1af30d145e8181e529b64eb06e568dc552e0ee3ed0aeddb16b846f6e49b7f66ee31355ec3e1c826d804d4d8e8d2c083551592eda359631e004ecf55609a974f2bc32ae8589ade47757bfba05311d16cd793bfe7aa84d166595f64a80c99e9492371ae522b987c0a25e16a55a2c6cd8658d9d99cd13563a8c9a02d4aa5d7a5f10aac8d9e29e87c45631227bd10ba4c60ab2da956dd01e6819e018195301a83192dc360a89d466ee73dbb929eeb359c741e295a9e52baf740675fb60c4d5f5fbaeb3d932c865c1c12391000012c8ed83e18c05644c3b4611c89ad6911c985560c117d931c6e9649771de9a780213898e5c7d864c28909fd5e402bfc490816874cc1d492b29b7fb92b761257fef20531761ef025f0d6b74fd4b5e2307f8a4f5473421435c245134be4004b56808e1f399a29f2394113f3904968cd328698d19591fd14738a9a89b38c18c64dfa2e5672e8a560a5e2db07f82ff59c6c6cc20c622b490c87cff9101eddf3f825ae2840b306a440a74c34010dd5941ebb1252b82181d5215a8635cc8d1eb466a0ca4fde1c9c51c8484a2cec1b40a3fd5c963805ec153f743e6a2e09286e9566449b0109280133f08ec3dab2188191db055430952d28db868b424ea7b2ec68b9edb848a0944c1f748d238c5a13d138685c4ecdb7352ee1e7a340133509388e89ec8efaa31a07d06d4279e3327a733d2932f4853d0360d2a140535cd395b92272af16e5b0763ca3c52d38d939fdbef8185ee1a230678c1ebde71201705e6d57ebf864d282a6045dfdf03519a3355940d0accdc4b131229090e9d649e41c3ac4f16aee9a4e87a36be57a08beaefc253c69c3eec8f8fb3cd06687de23dcf4f7e04e2a19813287843b8abadf8824ddda673caa57c5b0d3764f988a9fb882d147e63029611c672c06cf77d0068d44ca15bd80e4a19753b5e88b748445f8f9ca0e6852313f35a7c2f16826a78f6522f1bcee690c88f5b733b4f1ab9b323d0c1d6333d69b01242f843f0b052034983f2226629e2b6e18ec59f6c76d84717392867b7103af648152663fe63566a1cbccf1042806752b91d7e373b366941656b17282ccf7b1b0e9f1c9bc78acd7d8b3602f698d4e00c5411f180f88c431608042b6ea230c6dd94eb80630e66d4dd62189768cca6f4d3a83634168abe353c5af3fb6b094cee4e7574041ca0cabe84daa23f728a840a26ad9478818d34ae8c27c407bed290b55eb5d108b95c913bd5c03a78845fff71c50e4798daa9d319ee588bcb80883434220bb460b474161f03444dfd73662f7d6f154568e78f77115764edd1d1d4e682941166d05b09d43d91967df7f2841793c1f176277ccc80ba040dfe6d1d94837b2a4d879d1c6e4204608f65c17887338a49c97eb4ed78b36eeffc274bcabc6ee26ffe462bfd2c78744fbf4632c2979baa72b3336bb6f67d5a8ccdef2e33d197041ae89b55a26e70a2f37cb1ecf6ef67f1e9de4468b78a0296f0dcb95077cfb6c7c77f27bc42bed09d508c144e57c664bc81acdd485e32b870516b045c2fd8c0808c59b6ba70a12b2a03e4bd35878f27e96fe948faa2a9334aa89d169bc222ce718ef9df4634a0cd82a9b38312f67023793e8a84a84e2f17c038ab6b25a776a7b13fdcc2aa8dd2a256f00d458729ff65341aa326927506da81a44d40a4172697bceaa0fdd4addde54213d51ff043a697372597745e8b77095dc7f68474ae6fbedf6385471003182a04e295558ec656bd5b3500fd67675c684c172cf878cd1f7e3839476bb2080cc35714b001245168109578657a22f79ffaf3c61c063121072891417f2f2e855ab6cb1c0a6a249056a82bc4d7d37b59ebbdc009c699834403fb7f88ea309f399fa7431cd7d774996a5d06132690fa4fe48fb49ac35d9bfef0832bb05a3100059e80ca218c75363e33346c93b37813751448eefae89e426b7b3f7c24e5a3f1e4fe4a94b5afb854ce07495f4a663d558ad8f62e8409e96837a9b3246a0b366feb4f88e6cb2862f9729c8c6fab20c97c6736333987351ff4e7a64f2496111f8a4b4e16f6f18d9b0ef3cb5de6c0d19686149a5f34d065b0fa416ee347da33ae428e6fe9f5c30dc63179a441daa3a373c59c4b3fc2f4a3470961402bcef3526273f713f14ed1ee2fafc57fa451f4ecbbb33a2979c5d459abf736dc595791fac27fbfac852517a86c7214e3245d6b0a2a177514a9c017bbbb5e5646f690389ce90b84bfbfd5b6948fb064b3f93074f85efa7f3876e1379b20c0c193388812dc0724839302e66e0cbf235f181424e69e050baf9740d43579c69c03a83289b7a2bad651439ec38111e6f007e393c828c6d894a3a2b7d99754a9c1b374ef0197d9891fcd9083673d643c73f741172f0d2dfef42371f13680fdb38b3e62b1485544b9b58a33700ce0f6ba9997717212e01cf3958acc7fdb2e925ed2b428eb69fc9f3a48fb781b763c2c68f45bf798fd6c868972ba63c09ab617f81813af39f215b1ca1b200e1eb2fe318c9fb23a6757ca68e04111d73b061fb44003a22c42023a1f7312ad8278768c94f55b5571fab412f358ca2310c3ef37adcf1af462d19c1b6837aec761cedeff827584a0addc157b7b569eaeb4eba2f11210171bd05fca2f782e4fa50ca1bdc430154bb7e350fda63fc65b043cad0c201f86f3f564e7bf805a1f5dd051bda62312146a540dde60d8b1a6e578014212c6f76696e8b3ef9dfbdf992e606ac78ed3fc72540e997ae7f3300eb032d0046eb62a9b054f7ebe9f8d84050cd036eb6add8208c03b10c1b86c0e881dd24dcd6860024c6681447b03f1ad471a336dbf017bf50f7903973b1863c5a99a2467c688eb809e7488fe6a96ee28d221d9ac642587777a7327d82f48fe164ff42f4d5d7e397fa57b75092ca3ac748f648c729ae8b4d24d1e12670ce205d4674837055ace193dacb53e81de960b25acb7fcef01f26dee7d2ff816f4f7b8262c9bf31233e8a4e4894b92d11d4c258ea42ec90eb77640273ae7cde2ffabc7bf6b2594bc3ddf8aad076e921645a05dbb892c1a5e75fb81c795d671499d1d55937e42ca5c8986fac60a3e0877cf66228b8ae39f1d8e4a95468eec3010415c484e176f3470cd13363af1cd27281f9293802bffd39bde5ed0badb3860ba3e432c3fe2d33a9464687faf3815ac9c372f32fdf20b4c2030a627bab5a3098554d3ae536bfdf39bfede6b73bc7ce9ff66187950203763775178845576ffbb78de8fcefe263ffed236c873dcc5afa17607f08d053d0df6125eb56de1f2b357b70412562dd0c98db88c96e6f8d6c238410b2c9267bcbbd03ab0a870a890a75f647ba57c5d2ac22e952be6c2471dc6e2a6d78f42abe5eaf579d39dea57cc9f1f0b8dd74121e37572c93b64c2a6d9f24acf2e11efe9a65c2970a612d7df44e4d2f892a132edd3573a9f412c6f1308e115475e38e0dd6934455c53e6cef93ee9a99847136ec8a65d58d7efdab5d74c992fcf2fedafeaa27fdd5c3ccf6e0787292922f9a76142945be688735caa6225fb47b2af9a25d13c1073a9045d646f7c431c758f3769238d24c3a28bec0bb6686ddfc0aff2afd556fea5cb7c36a36bd6613ac73e9e3cca44b59f3d2b1b9b47d7449a1b96486431e5d3a64d8cdae512e3192eb71ea6dc8a30b96431e7bc8f5a36bd49183a13ceaf840ae778d35723d066e76e1d44ba18aa57ce94cfa0481e8eccd9344fa9188a3d379bbcc20e8619c0e1b51b12b96fb060e9a370cebdc16bbe847733482fca5ba85436f3f7da0b7d847aedeb5df729c6b7a1e3ee15315e6185a30088dbbf7d16efffe611cee5b876f70df388c63a737411eb89013c32a2009691ebfed63c8dd879a37095e59430550f6ce7d823c58fcb21f6116d1b5c39347f76c152c92f076073f726f8c83e62f739fa0c52a1f54372c4e5681f8c6bdfd78b1ca87ed02347bf8625887a761b7ee276635cb7ebe593d84f334ad40b3bd67ef477b4540246f406c41d46c39acf2613bf70de3dcd37318c77ebbfd46bf1259720fab66cdbf6ab366f5a89051d193efabf8c9979e4258314efd7de3b1bee7bd5acb9ad95c08edeb1b67bb3d4eadb5e2142eca35cb709024b7573fee32b27ccf2359fe246f380ccb3e70d7f0c8d22c1c34c33a6b8775c641b38b660de368e3178496b96b9cf7f1bbf16da26b6616f9523fc15721a459fb88a39d7bf7f179246b643838926bb6130c82e278b7991ec4f1bc7fb2e6e397ebabc538de6bbe51b38771b86b0782bbcb9c21731d5d3256b9ec7779f87d77efd27222ab59da377166efb234ab7aa2a649f0ac6f135916b3f410ce9eb5562bc44241d374644f0381984bf2acfdcd19dea67db60961cd33b7266bee35fb8dded840b63ed48c63afc13a5f1f6abed7c499bd49af0da9149e942dadd66bb8af6c55297b464bd952ca2683f65aebbd2acaecd52a3f2bade00dedf514f4819ebe41aa6109ce2c56f9d22af0e4a401bd95dd8028db57296fd05beb43d82c793acaa4e8824c8a2a849ab5da90f0662993e2093f90a209364b9914480862a9b042855aeb83f094a54c8a1f2978a04411c51850a238c9521605922b5821ab20cc680f2c0c1443d00009103ef063095c10210c4e683640c14031c4063e4c546ba13065298bc20859254b5914427a789265d466b96fb308e525ed41815020ea83f6a03fa80fe5b10517444a920c6da124c9912155ab416bd5346bb75befa53def1dd5902f7dcdda6dbb973b42bef4b77b39aeebbcaf7e1fedf9715de779df07168dea68447b8eee6826e93eeffb4070f46344ead7f8b600b9c8fd11d188d40fb70895e47e2989a99a4cb4a7e98efa887ce9f78f2b843450ffe8293a48fb28e91dcdd3451191922e8a92cc298a10192254a7a843725f13ca334817b3c7e43179ba9849cc9e23fa48534944ac216737a5b56ab65a4b7bda3bbab329ad55d3ec56b78df6dcaa66edb6ddcb75b5eb68cf6ebb1cd7759ef78120ed0976def781e068440a6b18d29e21382291c2b0543299541313dad3c464723a9d9ca05028a99a4ad19ea9d247c557357d5434b9c9e9f451f1e43bf9a888026dccec49948a82427ba2dcd1952ffd934b29a63ce44bbf12d91d84e3ad44b2a6cb7d8d88acd1662f3ed070344a69a548d449ad16554aabc5ac5a2d01376f6fb4a5a4b2524a395b03af6a76bbdce5ee6667b4a76a76bb1b8d249a4043ed635635bb591a49348186da47d5ac46238926d050d52a8d249a4095d28c92a8df238d1553de0ce4c6b447bec85321f9728d70d7026193524a29a594524a29e5d6f7ced4df4c3f330ebdb4459e52cac9eeed0c9e6419c3cccdda947e570badd22a422ecf2a425b45b8e52a426bab08b52ac29a671521cd538bf164ab083b4f22e1fc58caf2b68a70665b4528f30ce58b3ca5944a293bcbaad9f06432a90542383f7ed9a395d6fa5a6b78bf32042d4bf9522fc7751d2a0102989e14eb2e2c5884521605501e5351f8c8e3952a21ec651ea2796eb8877466b3bfe20e24686eec7bee0dc7e15b8352230490a5c43faa6aaf81adb62aed2f802e59d233a47cbd942a8985d85eb102e4cd06f30319143d8200591d84520645910c0a20191444b2204164012417520099681e326c01480b27aa48620b60380289892ff84491021cfc30b204211f2eb4607221b38114400b85943333555e652f77f4dc9b7bee1b1234379b66b110db651e22bcb9d66af8e7f2e42b6ea2388940b1b8c13c4e0ec2befc38e51592d98087ac063f960adb03590d66508318d828e61764358081ac063e8ee0d91c5cd484e4cbc41ec912219437e59635897cc181ac063db2a0225b84d99552ca7a5a44e22b3f1b04ed66f991524aa77ca9a7303c99d099b29f0582945286db776f18ca53ecd1498d10ced75396f7b35a4829ebe5b81eaf6a76bbdc9547c619a47b86a890ca238966b7bb8df2c80cd23d3d4485541e9addec16a47b86a810ab8df24890ee19aa5ad582744fa535488ba33c628510cecbee6e929452decd72dc08cb8d6b9279659e9435b30960a79002264890203041412c4149809ce109205dd4000819ba06a32c654094004162032020469ec0821338983f4f6061ed8fec092064269860e979b725422a854fd8504821961682684fd80046f644134234d8e247176a98b98c2167eb863cab9dc1c6c520fcb2943df1020b83275080c33f8104484fa9304c0109a1219df9c32da49a85f5b392f4e7267362286f5b98fa09425b64b656066198a5cc8999134434ad092d9a18b241f182a905cd6aad699aa6316184892d424dd3344dd3b4ae439235eda92b644dd334ba44ea8841cf0a66c0052514010a21e082065480996c095a20021021255b6822886d0207a32dee9c5589267a38ae0531f821410f27dc800754985a604289c64417401738a105b60530b21f53c86600049b022ef40a643faac0d915c87e14c9374bd98f21560556052c3e64c0e3e3239360469b2c4fa486f604f7decda220ecb294cd40680642688bbcb2257090b52c654b40d14508b3474ab5b5d6f208adede91699b9cb9eb9598247b77061f6b1040dbe1264c104242001097c7ab294c560481eab8f4c2fb3c85216039e0c831710f9545953b7bbb14a08fb1f6b6f718c014452daed47d68db1f2c874629510db7f36ac12c203319e1bfa188e4d1aeea33cb2d51edd4a12d371e35ddedc8fb2280bfd4c21d3870f21403eba0e074d7c816691d7e0cf249ab34c3f6793680ecdd93d12f8e6e2a00e427f1a684768f18e1b9b373b822ed03d405ca08fe942286f2e700320f0f0f0c0b8b14702df581cd4d85ec331edd6bbbd03c06f1fa2bbc531fbce7b7707783847cf8400710bf5500ff5cc7b0eeedd1563f7aaf6b87b7740f7ed4368f770cc36118f77213adc44355a23d22ea63c78badfe7e07274bf1f298f1c0bf2ae5deef70ee8ee5d9500edf74368bfda1b0f71efe1d8f67beb009f1c34086da1ffee800e539fbb9dbb66354c8582d8e7081ac2bbf61c5d0eefdabffea65dd5ddb53bc0fba77d08ee1a8e05f5637608ed1f8e6db2c3b31c3b437898ce680bbdd6611e7b6ea43321ec39910ed116fa4da44462b522114fe5c9f43162085d7289bc9937315e08fb9428d3c3000644f98af3666bfca39a3c371ab038e8a6723aba4943f064cb5ea005ab699ad5344dfba9355296b2173c2152c6830bc5160592942784854a68099e9012b888250a28454e7080a24dfc30d12676b00270073de8d6848c0753f02008de12644478f0231f268b24b9cfca62282677df236d42c2510ad5d8c100ff0624ac97da65a14276d528bd1b15e17cd3296c4f6c414e6c4dd04d8a509ec6a55b309f315fb3beb0de1c86423bd881050b3628b62058a4000b136c4e60c143868508805880608080369f255b1214d915b3d4d6822b760002da3a905dd123d301914c07486c32681e19163cdbc6021859caae88c1152ba0354f29bf178452d3c65a6bfd266a17c797e7646d30a8524a2965e39e092525c3a6c4e6821fa250b31f354dd3b69fb0e9bc1fe7c7eeb0c26c3e58361f3061b06288cc0a215618915961054a69ad561459f185253cd91e0cd1bdb9efc5d0645abbb55d7ea719f224ed5a6316967c49358b5e31c6a6c60eddf2c3bdf7621cdd426774ba85dec565b5a2912ff473460ef48d614610fedc8f291899d63d7d0ccd00606ce68cfe8c4cbf5d8c83022f5656b266febf978b3314d149f39c93369dd32ea13306a02dfd7bc330952202c384f0876b00318426d87824911578b4a701cc206dc89796f207b96fa3035aa594523a2ba5f454d6d42c65be9dabacb1b02dbb260f7863b607b2d75f4bf0003ab2a59fd57a345ba33db5a694524a29a594522089b682a96db359bc9eb3dad98d4755cf877466074abf7b72016fcc37eb6a2c96d5364dcb82e7a7d46b3ca55667f938d6479bf93125b7ccd03eca8442c8177a7108063c8088222880c3089dc962fdb0b047b9d9b3767505316f8f475c79a8e120c843df64bbc965dec23ca656e1e883dd90e5ccc9474904d9846c52436fb7644d21e9f22cdd1a813031b2a6bb3c8d4dcfb0502e6f806ebdb8fc0fb266a5bafc10dd122fcf0059f3cb3fa05b2b322e5f84aca181a545cac819dae58190334e52ae72f906c819a7d4615c5e027286097ee984193f0635c172f3be164bdf44d3af08bbf7dac951af263f99ee7d5fcfcc430df7ba6f6e58880ffc1250a790e92320b990e92d2073a03f348b82fd99b51c1e1adb9079be866e0b0619348852beb0d4c7c0a2942f53fc4a85910ac38b9528e58b4a2525b5b14414cdb60d83d79d439d88da0f52be7c2731013287170e8ff20899be7628924493f843fd014b233636105831255fe843717516112646bed073224b9cf429f5864cdf2273a0d76632bd8ccc81fea2c817fa139903ed1fe85d5c585856324f7c922ff49b68ca332441c83a8a9c0942ce193fcc1ce8e951b20601f285dec5a5861a56565050249131ec812e219cf9a7e7372cfc43b7d0373640b3e8684e3cd64f03411fb437dec49bad28adc5c13c3434ca13886c7321e9f22959f379adee29aa2c7f1b732cda3c90a4b6088d14c9b2f10edda7cac2323195cebd90b01513cc1a6f18e29195bae2cc616e5175a3cf9d03c2bef110cda20cb02f9db07dcc21a9b0c835126e8184070b640ef4a138799ab563f64c1eb3c7f4215f7c9af583083a3381688b1d12827bf4f4993fc63088f829fef4b06e4a9f3cba753f795cae609953ac5c5ac8906ea11f5793878fe9821cc6c9824c671249843fa5cf14489b1e960a4aa79f3eaa38ca930513062ee82bba8572b985348bf20847494448a60742ce80dd20640dec075933dfb7625f13470d86600b01cb6c6a6badb5fecc786f7dcd126bb7348bf5a7ba69adb5e22a4e2c9108a7f7da5f952708e7ed0feec3d453620c8d4d0d3a14debca3031ec872c6cc2397afc671f738f5e3c5b02d8f9da78d39ce5bee9e8af357b421e5cbb89d204f100152bad84bdb23edbd235b3106df0c0313d3333892c024814902930426090c052afef13ee3e2c2c2b24aa2d32d7d79cd4a49cfc919334b0fe3a080cf95303d718d3612f66fb3ba7ea74c7b28034d17617f629ac6443f3c5a05590e9498737603e56006390742344dd3ac95e560882c0758a86ed031566de0c926eaee9ed9a15bfa344dd334928bf0a77e5eabe7e40c2ef728f7ef472bb6a9d134269834accdcc81868686864607d9d297cd651bf94243698e10de6649220d0a6f5b81ed6ddbb6ed72b709f3ce39ef762fc7ed9ec295cb90b0de7befadf7823666de4eed66cbc0aad2e5521a532435241c2fab0c236be81261f2507b4a856118ae4ea966751886a1b5d65a92b59b952452ee53cffcf09dc6300c3319c299491b162ca5dd6aadbbbbe99535dcdc91a7e8832ce2e1e1c9e3113c3c3cdad4e6265fdae29c3ae7040fa0a3e24dbef4a6d5a0ba20055e257de1ec371efb8d7168bd316dd87a1b5a9532d87eefe9cc56ecec6d5fadb5d6d36a43f3b459e766ebedb479b3ecd69535579c6f0bfa70cf7d7e629ceddca5a4a774dae0b0bc8eecddcf87ed9748c886b6dea3dff6cf8e219c79dc72ff5ad58d2ddbf969af8d0dcbebc8d66a376f423ab02ca7afb6265f5c9ad52e303134cdb2e916232f9292590134c11e66d66ecd9a586fc59b7d50ddb0afb79fd774e41a0d041840e00a629eaae86158ac77dfdc5b07c2f9054fb28cf3ebd63ccc0c2d820238423ae3ddc3ac700e115144cf78a740ee14e115a921f7477bf4dbc4ce9aa5b1016fd49686b78ffef66d9a554304617fa4a1c1353aae1a80d24984882f843fdea90426e09dbe0859d3a7af809cc1653aa280ac19657a1c73469f6621d36b3d70f8a7e737de6b349d3ec4ad0421a5524998330dadd1ad7a6db578b4b66edb666fa36fb1adf9a6a0aa6e609cdc33bc2197a7e8323b37a7cddccbaf66b6cb1427c2d0161a9b6ed9c100daad5dabe8c27205d19f9759087ae46bd6c4acad27a41308593f8190746a3dd8b851af5dc3383af2506deb03590e92c831b29455b1237bfd75cbbe7e44e1b8816c3f6ab4d29e365ef28dda3f854ac27af0869cddf0f27db96c3cdbdbe0660f2fd9e22264d75cf2e53b4220db2b205b2ce50b85c165a56fd006d81cf064b768ef548dcec9812dd0ed6a5f17c2e6f2d878cc809409294396201037f7fcd818e7b3f1b8813cafc572bce8c0aa9e798a3352e86ef245035bd4f2dd7ae29526a6422f84fdd193522684284f0c7c8ada5b54f5651e756872520f8c0943e8e57929abe2e71a0fb4eda3bd9ff6bae75ed6b5c3b389b0306b38a65930b4a5c6348b368b6ab626d73af303ac148e84342f2b00f9420f0099037d4c58a7ac81dd3c67c0c25c4fe94ff4230c65692bda90d96e9806260aa114ca36cda2a76916bd26c66821d47abc298570be4a94a792276530ab2416120115320e54ca9313955161848a2064294db294512184a620a9a92c65549841bb29b0f6059b36830b241322a42b6529134284e338aef33e2aca2913322413b2842c5b0b8227dbca1a15cc66927cd17e4f806c3f2f1ec917f054d61020dbeef32338721fc1ac5de25430881cd9a323f07ea3be60df8f7a6257c50ee370db862f40b34e8f80f366155ff539f2e572e2786f2a6d1dd7711dd7e1f17278bc77e32e773f56ce0a5d77cfebba779d77cf89aa8bb9bb26c6a95ef7d7e53670c471f776df94a477e2982377e36bbbdce5c2b181e8dccd73223d89e3a1c33820368266ef7a783c0e9a390ceb7c715cddb97bf55e3cdee3d864d7cc1da6b2cc1d87dbc917dbe46e129066ff81b03a7ab96c3afb71a4eeeea7e4c6de77f023b7853087e6ef328fdf476ec3630e9aa74d86d93cfad82f174cd6a9f91e563369ce111e691e65167974511a5d20401e5df30602c8a3cb66d8cd38f629d96258bfcc4a5f451c572c7397791b378cf361236827ba68e62ef3bd00cd1bd6e9dcd41ed1023393c4b9248fc4f1a0384aa10f24924244b95e650479e4fa52f72885461c72a848387e423e5b37bfa10cf2803e99e25167284f399447985964ce8c18e7111d3b799c4956cd9a494d2242750649754bd7da2dfd0d657969adb24a794733529f8c78789443249122331d4676b2d6b8468d6609658a47297416960b4c0c8d8d8d4d4c0c0c8c8b0b8bc5c27270bb5cf7fe29b5eafedaebaeb9be2dca7af9b1e6ae315b08e55096b97578b33ec1932c63dd82106aafefee9f52a66fb75aad45dfbdbb01ba35dffd07218660c003ba65c2e32ac424ee233c9e40ee1ff6b84bc97dc3f7c57d9442a35feff327f013f579fa57ba47ba04bd5f91b664eb6b8d5a61cbd43a79771b141e5779fcc9099bee4b783c857774bf2bdf99bc746ffbc0f0d224e528279d7c643a18febb9bc9bf16d822b54aadd3bbd4bbaf54db53f0b84aa160d4e994bb9f4c780c73f7d2f7e5eea411fef0abe6ee2afbed9ed8c557b880f142c92a0692211b9a18181716cbbbf0b8f894804858582e303134362dd3a9de32dd41fa518f0ed9d078312b30305c9a86ea05ab69a858de34301ed1212dd3c91b5aa465ba96f9a811ba45cb7423bc85e8ddc8ca07a3c80b70356424d432ddaa5b238c334a8d4ee1d7346e95a32bc4295680bc0869bd5f784aada84fcb749f14a41fa5d427d5d1e6a13c9487f2509edcddebe2d55cbc5ac9ab91f450b3babf4efe3afd85facbf417ca5fa5bf529eba047930d122ec2ebaf5bdb95801df4a9a86ea851cbd91340d8ce7152dd34d212dd3cd295aa69b415aa69b405aa69b402dd3cd1f2dd30d092f6fba88bc6923bdc5f4e91e0a87eee54d88c7abbd7de88cbcb93edd53f08feadd7b47b752f827460b05ffacdebd7b740b857f5ebc3bc138de7f6262f3e01f182f6241de53f0cf0b1c8ba52ebb55c23fab185e1018340a2205854125251f175e17b12053d029e8e4730849cbb40cea28293ca9e641c13c72689c1385c7dbbdbc7ff7a6103a930a426742203a73a78f3695e8168857239cc2a710874974ab843f13be275c4fb0ec7eea6e12674fb3ba97c49944b3ba87e2e4d1acee2471f66856f791387d34ab3b284e259a553f4e1f4e54a95457ada8543054aa17315631700c4cbb00291723aaa465ba1509e788512472256fe48dbc91374116e7a0432dd3bdfbdca25b3f31fc9edffcdde791a6f158907df769a469ace0ee73d63460e09e17691a2f70f749a469a81e53bdfb1cd234563816539d460c1c536195c538472c962376448e98ce0ace113322470cc70b9c23468196e972c48a68994e358448919991232db345cb743807112dd37d0a755a84dcb56f3f08cf9d0ed1197943c2e31d610ae2a1dc7da59b1e9e7413f1957295a78e72d44f7eba49d7755dd7755dd7751d11dd5077959494789bc51d45fc9ac571dc4f62aa59dc4dc455b3b89bc49228deaf59da4762781253ab66695dd7755dd775975dd775972c1dc6a9a56e49c1939fe7c72944440221fc4624ec6f5a84dac703211ca78f1c1af2199a43524868484e1e211f9e4d8bd09e3ed62d9ced3fb1532c687b771d53c93b0e7bd934401c8b81ff5e44d318e118787b229ac6098e8158b59d708ed8ca643bce113b95ee635fcbd8c764cb70230b524a29a514e7902d636fbffd20ac1f1f6ab79f43434343424242423e3e3e3c93e7c3e3f56a58a19b1b7278bc17a778b6be55f0563fb12e150beaff7429a614d38a6331eae118c5aac60a90371e5821ca115be588a572c44e3962618ed897237673d4fb85a7d4ea2687e426e5c0683b292c5ddef4fca67b7753890b499f47abdc38add6969952defb7d61783aa5562dd3e1a442d47adf46ef4aff489737a887240dfcbacd5279236f64a3e8c8eb3aadc278b14a39a1a8a870787169a12165c30d3503a09189a9a13583a2230f85bac935a8e7116064c3a231d2a184a25db22394e78db812a7695a7753dac3a6f1607b0754693412bdd0abb80028aa69dd4795b8d1ada352b775b86add303407146a2b9146e0a84d6e8c92a5a8d2664b288e725cdd6cacd48f99cec900c0fd6aa3fa1a3de14efa1abe00f8385a7a8c7ec4dd3b6bc66b10c05338c63a0a8ea57ed218857750473d76b2b2f569e9a8c63ba59700f058005e1f93f17ecc0493034b0c136e7454e9a3d147a731f0393c06f3188fb1dce4b11e78d8a10031efa8d2bdd13defdef65877f131fcf0b1ef3dbfb9c9357498718f2afd9ec3201efd7abff7f62b8ffde063dc7b7e5333009a1900600160eda8c70280774ad79e038ec1e0d1b56fb8f3aed97bcdc6b6778fd958c52c8c7a4c06de29bd3e068eb1e09dd1eb451cc378c77bfd0a5eb91bd65e6f861bfa58fdf658e314eea3622778a784ab098e99f0ce08df96c1240f7f20fef0cec561cb4c0eef68f8d432d3e29edf687d0fecf45b7e8787c942054fd67aaff6711fd108699468a8e6ed69a82616c2e4f2e63d3731edd8e1b989e9b8e9f94d09dc7278602eb9c961040f0f0f07ead5b86e04765d373a81262612380a4b28d48b952a8542495181018306961887b1226219325e5c6e60c9a8a185b3c1eb4ebc8dfb380d070f87da75323102f0340d2607003c1600aeb394fb4eb45bbfd6d6c2d55602dce633b68fa6354307706bb4cf8bd1dbc762f5b17bfbfa187dcce28a29ae9f57e33ad07c6fb4027c87f3e071b1eff43eb6c5eae9b7c7bec7680f8fc562b1582c56dff39b135a2da5d6528dda4bf14eb57807f7fca607bc233f1e7628c04dae4183860e3468d0a041a312c06646cd00685aad9956abd56a5d9918010400003038e090030e38e080030edf8bcb0d36b4b064c8a841860c1932648434b0c4c0e261c058810103060c18a793f7fce6c54aa59292c23194cfeb38d4b6a14eb6d4f69edf9c4c4ca5908463e0e8c3f2033d5cbdafc3b7f338fc719d86c39dfa8b4f2dd3dff04ebdc5ab96e9ef54bc6a99798a532dd397dfd961d690852725ca93ddf535a8a7d9b69d1b8fb5c672bbe8b4c2939779943edfb2d801cc0e7ae0334488ac3dd08320574836e404b966291b32050d9e279e3ddd01700890213cea674e78db157d65cb3ccb3887f28a6644d3344df30edbb27d4e8e3d6ce61c8b736a35526bad15e7e49c5efac973727272c2d1bbd6ebace4fa1c6badb53627c7e4a6a360336b46344dd3b49c1cd2bf73da737260336bcfb938a73b78ed1236b3919cb9050acf2327781ab138e78473ec4df014da70ce9c358bde84734a780e9944745256b2f61c1d1859c33961bdf6099b791a9973ce89e715233ca70071ce87a70f9e3f343c73605bae7f4e3d6ce68aa78f4e9c40824c22b9e297d5fa38b6dfbfaed58e637bff958363be9f83a3fb7d0e0eeef638b66bcfc131e7b50ebbea1226e81992b5e7e0e0b0ab4dd0336409d71143963041cfbcaed933640913640d5795982d84924896b947b772ea7974129c1138ba55714ea540b7349ca37d3e873ba2569d948e96b3fd9e7ef2d0c7b40710515b39ff61b3d9b66ddbe8bdf7de8b398ee3388ec31cc75d2795b953fcd299b90f9b9dfb2eca8222559022c33a377ee9d4dccdea564e77cfc33938e7fe0befaa5b398d6fbde2779a274bd47a6fdb344dbbe3f6bb6d9be679976e74dbf078dfef1ad465ef689fa323f7b8ddbe7ebbdad5b4eddb66bfddbbddfab1d6b0539a13a42b08fad6ea274e5f0c423bd55a0b42e5033dfd973dfa519c8953e7d551678f618342d364434290354dd39c306529e381188e4828d8a0074338c3073c8083263407b2a8e26691c597850dba2c98e0b250419845cf48a88bcf2407a26cd6fd26dadae2c4305eac6851116bf51777818949b15ce52a1aae7a0d385c060de257a953f8ddca92aaabfc292914d4c989061935b05a6cb8c1e5058717971b6c6861d5208306961858fc0a8c172b954a4a0a0575723ac2a4c854542a0a8598cd662c164bbaccb003189632ca235d58ec22c1541ed0ac2334dcad442ac6858f945112f550d151c283054c42c3d12c1d7a56b4d1dcc1616dc3234c25379eb88866d15a3285f6db0d777978557f7e7569fa8bcb6ec1b83cfd1215c3e5068c8f22fe70721257a28d788af9ee6645396d105b44d92c19220d22cb5442b41d99df8eb0524fb9cac3abf0e8a27ae92b3cc2ac6efa0b3cc6bcb8c961e09106868d148ff218d88663f12b305e88a1f8ddca922847fde4a79bdcf492188a97af38c6c959504e43ca65a85ec38bb35a64b36cc02ccd62d520830616f1bb55b260bc58a9e0141e6d50f884c7980cc300981b9ec7864942829c1fefe8c9cbf99dab5a173b62ce15d78809a9e330c947ac90f0612a241a0e53c995e545871558ca8a4afe379056483015529571984a8f2cbf72f9ecd1acd661a51e96b27257f229152e7491c01a0e53c927a794cf249ac5ddb5f2e2210e87a564f0f212a6926129f9c3234c85759d996d384c258fb0cecc2d1fe78e2cffb948e2612ad9c3e30f4bc95dea2b9f3bf0278dd8796b82b5a19fe8f00d75144a94718cb3a456f008fe3bbeecd6572ebb25e2d1e4a3ffb25b38e091bb76f1b25b29783cf949c5058f28475dc5e5b25b2f7864c13fc4c0371c45dc6165738af95caa77356dca4b93633cba8c7efa0a1e61567ef2e331e6475dc4234d96c74f3d05c778ca5530cb55ee826fc036884334abdf220ad1ac3e4bac419421e2a081458c21aeb09812c5d3c57045fc6e95305e882bef2aa26c568a984241d1c86699cc69c2e32de1bebdbdccd00742f2288b8ab8e0828848899224499020191a9a42346bbebbf7effdd1053cfd08337afd18430a6f3fdae850faf6510aad98fc7e94437438673a999442d208fce67fc54d13ab2847fad3a8fde4f21fea921bc1a78ca4ab5c96ae1a4dbeba6435eb2b91a5596f9698124fcd9a471151e28978eb7c098fa614b2a18981d1a19ad6c7b3972c63fb6c971f65110bcbbf5aa5522b2795f086f7bb29b9e2e0e8b06409137a806d7945beccbb669e459b128aaee091a576b2d6c45a9555c2defae0ba733d3cf0e3778f0788c7d1c12448b8f4313ca9844f3e9a5e3a3999603ca65ce523ca531f4f8efa68f2130f735604231cc1081292a084b9042634610b273c010a5190c2149050a10a56902ff4f30a58c882122d6c810bf2a59510c252baa8b388aa94b0bf3eb8773dbc7f3cc04e42a53a0ee9e1fbdda3521da7d43b545aa54cc8382af8a6e0c993c21f0a9e3b50389c3d277cc23309933879a4668fa984c79a3f2c0f8f3c22910004349b4d21cf8a6640b359d1ac0868cec639e7049a33a0a2a2a2a2a2d96c369b0101010149987ce4c81198dc87e169d68e247afcf0a359fdc154b26b05874986a564174a1f63d17ac952eb755eb5cb95a61d47b3b4eea78ec3a30e29f9e27165c99233e411a6922d1e7558b264099901cdea3fa059fd229ad59746b022491ee5119eed42746b1ad902c9cd2895e46f4908e7373c79ba19d0f7c891f6c932860e95d902039f2372369bcd66b3d96c369bcd66b3d96c369bcd66b3d96c369bcd66b3d96c369bcd66b323720a1909b3a2a2a2a2a2a222393b12f637241c515454d417a267ac66b146d64f8eacba290aad94c6f889c9f538fa1dd331bd8978418f53f98e6a3512b79b944827d0e3beef8c52e752606829934a25e53bdfe9050c4f4399c2d177bfd39dbe78f1d88e868356c0549b542a5f4aea3bf7f4c50aa5e2d88e89ca4ba49bd25058beb33a3d3179eceb0e7a8fed682b1abe6322a9585462fc043eb69322dec3f8f6d88e3d4a7f670785831a7f5fcb340df8e2da3293055712bea3189805c4e187f13d7cea44bc4adde3145e59ccd232f3da0adea930f0095fc7dd51f56581b73ae5791dbb226b5c72498699f28beff230be2e85946127b9af5355640dacaa640dec6b0e8592e775284ac3c0afe6e59a1996cade75669eb02dcf1a18c9db4ab206764db20616ca1a9849aef1ec3f5897670dcc04be5c2b197692e775b4dc309a1be6bd5c5b86a1724f1898271e6129b93fba5640acf296bc5c34c35279c246b9f10853c9f3a3cabbcc128fae95ab3c0ca4e48ebb9bd52ac5231129b9f168040f0f07328e6b2ec9381850c9138f44f0f0a020cb7abff0945a9d25af2c59a2852cafe39b36882769bd2bc5c3c393fbf57ae98cf276d708952fd7ddb5f28269d9fbeba5e365f02ed2100f4fd631c9305e2f9d9b497fe98439bceba672293573e92f9d2d9bfed2216593bb5628f9f482d17cfa4b47cb277fe98019751789878727eb9c649417ac6694bf746a4efda5f3e594bb56847878b2ce29ab5cc26c56f94ba7b3eac5754cab978e299374bafc82d90c03eb74065f8830565eac542a29f2651e259e8828f265fe24a2e4cbbc8978225fe64d624934912ff3a1682289a59118829fd77177b35aa562eb7831ab12ae1595c3525ca49f1e951b93bb4829ae150facec98dcf4a4dc985c9ec564e68df41def61974ca9c77682b687df219dd47291543ecd7bc984f29defa9cba7bc7a9392e9f4d80e0a0eea77df39394acb455201e9773ab9f79df0e05d2b23d5633b9f0dd2e93c5f323db6f39de020eda3ef80f75ca4254b96e41f9514951b148eed98a0782741386464fa1dd2518feda48437e120eeb19dd477144662dea0be333ae9b11dd34d1edb293d0507c5765238681e0523316fc2c77648f71edb018faadff97e82919837a3c7764e137f58b68c0907e9cccce15bc24117d770c3241c64716c67848370c8c81a067150c53b1f0ea2f8849198371d0ec261921bbf59261db7b52c6c45c29619e54b5d47a63fa55ce53253ade2d7eb757294e9a517f8d1bf7b77275b9514f124a2a04e4429de2f0c49e2aa59dd07c5b3441711861363686c9a95a2a9d965f0b620842bde56440acdf112076f23b2fd60f6744bf711ddd246e0685617d12d72878e699866f5b38e712706791e4892fc72dd9d212274f8d09e9e9949ba65660e75cb113d338f74cbcc9ce1e8d62cd2ac9e99431ed033f38a39c50f3d338174cfcc1eddd293c7cc4208a511a600ca2fd78e10a783e7c5b96ad6e90ce3b06bcb77ac0143777c3671bc85e121855a76b79c486641b297a58c084746b35c931c199a1de996fa197056c46648cc15dd52c129baa502e9960afee8963a032ad18347b348f74251663049cf8c880cf5cc48a85b8ef4cc4848b718e9d6688a66cdba350ad2ac0ad433a39e20e014a010f00a50081c0212018b748bfaf4cc88c747cf8045ddc2a36740a26ea9afdebf8f20d10848b72856d19b8e33ff934365c32afaed3f2638160b9ab736a569281dfe3131e1b08a9efb8fc9e93193fb9f93c9eb4714bb662ebdffa3c23113ac9a5801f24605c7824c78042465c323a094c5a31f281a1ef9a03a3c5222472c88c3231fcdaa41178f7a34ab1ee7086a3c9237f51bca8d4741a6107285d0102245bab5c516478e6c468cd8d94c2b52a42342841b32c4fba87ed463e463a4c4c867f4a35b40dd32bd7e04c4c6c64443b3c5c4581818cdc5a563b138963c3fb2e41124fa0e72516bd1bd6017dfc713863b4ea79e546a94c4b8ca2011fd1cc1d13533c52fd7a8c355c23814c3b41c1df2c4aff93b45d7924cc22f57671d12e945afd3d935e71932e97349a6a2a437ebd01bd30cfd9a778559a72ec9d3c6ec2293a6e87ad6d132ccb5650aa399ce3264d249e2a883072334d283108e3e823cc2500ca2a0a8e48a5a6a7b983a45230020002000e315003030181009c482d1284ce238d90714800d8ea64660521888c324865118658c210a0002880100c0108099995255a1ddb2039b943e0405eea5f44434f1fd11e015228979692bab40c017a25ae0cb7fa0c8001a1ee234f8f26a20c735409aae0df3d557432e32b92793ca73058aa72c21ae36fcc20edcfcb732c4412d2a9b6b1d60f9e2a60fa0bafd4a6c4a4bda9082407a0a224f29d0965230dfd67cb2fff5995c732b1b5c62f0b2c199b156ebc40223e8ecdfc58e4e4d30c5362a700cf0ac216760680eba60cbca93f70087fc6c58e4fc4e457f16ea077cbd75da136e233fad461aa7f819971f8ebf642479e18020d1e298116ef473305438a228af2e75cae48b017c57019159fa77c2de9063047128a04fc88718738e2a8a7e0b46d3d6972f8a403808f8f1bdfb4f6440ee3a6ad2eb8c28041478fb8d369b771bc6e67dcb3a578852ef3bc5d1b33ad2fd12456f3c5853c7dfcd470effa8c175882b5d3c1509fdcb4685f50c55a277dd1f1dbe060fa6884139f66d7952e8da0716e177b52a527a81c00a8a84a32dfc0f8077acd4ab15c1c2497bf46aae55731e0bac191b2804700aea303088e2940ad5219efffd77c6ccd310c75d3bae952a6676a55c87bd64d426c8ebfabe078fbdf2a7884bbd18d62746b0eaca3be9a74eeda269e18488aa992121ad1deca8883fea51b3e7c72c6b1b3a2c1e97383e7ddaa05e6788093725f2d132383a081c4b66fb6bae338e8d80b2a3fcb03e99f8b4aaec3e191167dedf1071ce5b88c4fefec057e41b49bb49838c88f252c022f870a637c74aa77ed949e8ab89f88cee53eccf67604508fa35c48cc29d20e1f5bb0d20ff0980ccbf6253662b6afe83bd7cea5047b693d8cac3d94ed08a683b79bff86eee59a289a82deeb98c20c9122359e4f5291cf3c3c4bef2b74fd03e79e500109e325b28b6605620c659562871847d695be70c256d47698384fbc459b1a49e201222396545d510b63ab0b68f97668c37d0c66b2a42ab1f678384e01f391f344a43ef6503d8f87f8fc9db203d9b88355d26ba155a1846347911ca5f41268d46cb2ea2d7a128fb7aef9b4bc0d59f66f5fb626f6041bc54cf340ebe05170fca87a414a23ababd0466b0fe01737b037a6c2d36c425bf62652e433995bdc57226e5856b3331be8fab0890070bb019c99a4ca13113c3c8cdc394a860651092e6d4e9d517f3a5ca147532fe9db2621f5faa58f0ed117527f83dacb03417c4095039016690679fbeb66cc0def1c3855b9ee12f94e8236f0a2e7ba1efd19c59ed99508359c8d9a1b2c46f53c6e27e47e9732cdf914f8b3fbac5f13db1548ee825d8047dc13cb51660709306ac566aac2e3232de161355a737d96561951d38c4c9ec7d1b4d7d99e03da086c43c4364700bc19b461766e76309c49efc4acba9472228927a085df0eb8aa822d63a2fe4b1f43f26948c76d73f4f640c10ba0ab6ea98bee58c20a510a16a5dbc050d03615afc934c01a817d331374f3d9a746668c33b7238b7990b545970f0b029e392931c099eb1d078297320b375aad63967efe11267a9c06aae63a80f0d72b79c5ed63c95f0dcb36860d8f022c2eab847106d685f946b308e361effb9e9ef2a42f9d4c4dcfb1560dcf7fd9b8223b9e31d7afcece764a27a44086aca13113ea580e9a02b695997c3d9d0496862b154945c6b8adcc44dfc8ae1782883b15da1aa902b3c4f8750792b8e8a41b5224174561c4a35a18a15d3374e5ce7559c310d44a18c542b136b33f8a50a05ead5775e1789dac369eea5bade939312ca0d3ec56c09672121be24564245b8442788ea0ef08588d823877cb339a6f443c33dba743bcb161f8ab4d0852401ac3a3131da16b79d01495a189b25328283c3ac8ead6b00ad729f402d86a353d3db4f0519259ac9410bee464f5d0e9cc70ba19d857d9e03e44daa7528dedb3ef04034a979465cc66f718020e0d7db32660dae91d5444b0117158cfdcda16d4761dad6851992eeae4f498b6c56586d04703f0c289788211c843586de53623e78457a3d9fcfa661839fddd6a60be1986915dc5e6f50df9165bd9e2ecd64ee05ea1ab1881b218c10156ea7064e8cd8c04266ca614506584ae9b3e7fcb44105267b4fd0c6e0dea02a88d4b0ff941954d5aecae6b0820f73fbfd919f926a920eaf6ee96e8e208693ace00abcaee4830043a8c6409dad9122628fca9f4d0dd0d82ddc5343cd508cff4e175e6c759a2d325a0b9d878c6d026dd88194945eb9022b0acccc9b823c492d30c4842f1ebd33233735f683525a67b2faca46424e4bc51e41eb93201c0228637dd23032e0797993d6a999fe6a31e370c82fcca833915b68adc5a6df3b424d67b5cc38d0bd631dc36205b89ffc4739fa1226b4d562ac628de21f1ed6d11f3dcf74f3d5bc8329d146a9f93522264a232bbdeb7f46679025a0891faf876a113d1d9c029e0e2f76bd6a59574d6e61b5c105428bea17ae9b365bd5be6c4b811557ecc552d574a93a2016e719a7134bc55173c816b3e9b2a2806f8ab918c54c646338a81373828d30c74fe97d8a12f3b82198eee6fa56ac05fb89e1d95595abc4781d5669f723a5d8b2154452475c6eac369410a4cac788773c48915f73f9ccd06f600e9b0dc287377d77f7e0bb450182311b08ce990237a1c8a6022e2f87506a1a350979497e7a48f87ac2e622edc9800396fb45932416ece75531d868e5ee939482482d64d2a30aa8893f206c6ad81c2eb533997c0d6a6681d2c1cd8e313be08ade140cca85fdc96a7cb0e62fa324f410e0fcbca975ea54adc758f7691e8aa21db6556d588d6592bc0e460e86c35a23f9d747242258b1f0b5d1dd7f45bdb8262d5c9ff48f3e3660ce4a7ec6965694bba36bdf583a62c9e071dd6513468c9ff63dd6345e70fa79a5fa465391e23b9be833fbca49e7cfd6db3714da28e06da650767317879075b5926e26bd7bc6248f7197b124f0647d8ca93a91a217e0824645be6e09f5ec65b49943b119f2e1845a4bbc248322208b7e86b8c70b1c75f80995b31cf8712f053576f8b4760256bdb99f7e6424eb83e28957e71164948ac0f591c41d5daed2ecb92c7103816a094a8e835a5b7c57a0ce3fef839cb3b2ed976987334f22a8440a91db19b4918067e6d0a6a80bacdbb333e861b1fccc037e898dad56dfb6f021dc5019efe8655259cb86b630d5ac25086ca239d2189506a2d39f7dcb0a914ffe65be7c95f8e661d8da159ac595b4b8cda5becdc8efffc76f4719dfdf6de90ecbc4eb29ba015b8e4fb3c3f0ba8533c85340d328882eecb984927d467a98c358a586331d1f8293b066550ecdf53f9b58b2226f41f8c446306db6d8a5c0f93194d1c8a456491e1c8025af2a74782bc84fc42b5be20bfa411dda260ee8bdc4db5bc8f710cd43a86837da9c17cdc7625e4b8ea3e46f1cd030689358a82b1c7844d8f5e104cb60cf7c1a5230f486a650040655e5d28b0e0c2239432c1a8e3c0efc5b9f183ebed8eecb6c18deac16cb70250b7d480c96197cefa183258f70243c96396082812047775057d17de457c1f764f88dc1697cdf7637530618f95ff2e403703e3a5318b382f8c907db2f0c6cab8aff1c324b7ca39280b44d822b56765ec878f619322ee653bc656cab8bbcc93000c09c726de57ceaef44315a2a25264bdc8a861d76248b9bce9c6a73e37978265cdcfd4cd55d2f3a31034c6c6285f44ea6f9116692f1e8390233f4ef53ed42cfa3bd0ff5cf1c8440d7301a02d7b64b625974800ef5707266bf369f3ceffcaa58c214bf1d7ec95f70f2b6dd37da2be7479cd16422701a4c8578f0038260f92f9f75790e50eb8daa60db50956161a9361f08ee1a8aeedaa393a6d54405398de0ef7e975b00535ecb593fc3732c88c0459926f38ce07cfb8299ad5224f60267c84f6df8d2a043dc85b10611a9fb4af6aafba9da1629f8bac6e87e72ba0e75cf68b92a4dcb2fd4f26dc90dd647c96ff90c7aa833c8273b0951d3468d701defe41a2fb094dbb2fb65fc951f2575030aab606f2b8a451286bed9bafa1b322d6410b171d2355d3122d4d058e7e0664bd1cf474b790b8e38346cbc032eef8913b765716bccb38808071596891a006230056ca014ebd8bde8842b70605d93903b35ea5378a69d9d928f9036e74e7fa96971c996895ee857844a168eff8ea53c4a436f66709efb118ede59e0cf6a6d287e03274f6379134433330e3be4fe72092674c986c20b13124651faaf15e911cd331de82a9bb313bbabbea4f9e1dcf03a968da126651f0f40ddd9fa9ccf392ffdde0068deaa7725ec785a4159d586a8535dba730fd0674fcfab444b8d5e1f029cd72f0067affb4d2a0f11d2802e054418e1f1b0ac012a56745aa4de57bb52f3003ce6f1745ffa2b8f2006daa64bf301b505e525ef952dc29599e4ff75d603718b3e41239cbfd93cd05348cc2eeb7178f3fff7069c77811f3cbe93f28db4a6ae12acc71294878a14ff4d91db8a6ba05f4bff9fc7c61e3970c6ebe14bcc6d60f8ac9754627c85b34a41ca04406c56d1d304e98f3fd2df21ce939b482105e16635428b8c8c859415293b7ad9e517cd3f913682129e4b42d3167b4a11b3a5e9a8a559c4917d58094d34c0e6940bea0d502643078258ff0d1067b407f6c42673915bfeef4193dd487a587b47a9c253f7841fc3718ebe5f96a8c0ce79300f1056fbb94d810a148537b1259703bef33f7faa9d6ae9119e2864f875ba0a080b7cf3c9d630290ced3361cdd2447035281aba146987709259c7e194f0d502f8239acde675f8b9e691ec081b74913d701fc1d2f6a4148b2677f6e23ed7e3f5a865c095ab11f152dd67f614aa1ec39211119f6a515816811570f6568d587aeb794f3ea649baf4c73ac4ae5e11d996d1a392f357c30c50634559e7477168e775038fccfebeb2532e878dd742cf304d2b6c1029ab85f6b090c7106eb45550b1c9403da07eff03c0c094b1ab55c8a356673c04cef8d4b5a74ca51235a366159c0b56a6c933c015a691b32438b5c41c7a7e06654bd6417dd71a22bc9bd4ffc067d97ce8e30031942d571bf153676cf1fb7c8ff67b0b687c4a0b504f4c6575d8744a4d8f83a689dc11bd646dd2fb5dbc22507db39bb95504d8ead3f97e867d6b821355a9743188eceb5b6af75c22f3c6b6caf5aebc0d1def30f59cf9cd1a2e8c306e047435fce4fb9bd7b2856b7bc524b4d7c5eb4a6d286e4a6bc12c542b5c0eeffe4d0411f08dd8edd0b91f6667f14f5cada14d944067e923d2da84bc06f71dbc9cfc9f1e727f4691d28becbe8821d0bb2534f31c2d8c4773e200340b77f4745ec19a1c5779ef009d8b3de3941f8f38db5a081bc3d65516cb5758f70334f29bf4432816406295c249094a5e387da5fe38172028aefffea4fd4f5b8af0bf09e3499413d60a0f3cee9ddf9e4bc4a60244dbeff17f25961105071027330d942db92887d0011eaa6887c03240402c29790a4c62a0b9dfda4f2f194fe9341a5ca673061cee50b6c6cb2559f01189087fe8959fe1d8310ccb1b126163818d64386500828b63d48302fadc9341eca6d4b1e26b69a802985c99dd5c6cbc76f7c5122eba243acecfe865c7e076ea50ff0bb409886ee6d3294f4301dd1718c354c622e9d7af20550baf7b0794a433e562e079e48018354031b9b54322c5c3119a8af9886ffc3ba92c688a58e07aee3884d9e13157225a73282de714e1a0383c0983b8727686d5c997228d4a37230232c728a2aec94026808cb842fff9e3ec306973ebe70fec47bd23b8f4e8f8482481f900bd3cfb40460a42a46d9fd10a9d3587ab038764c37a9aabfa0542ce6493d30670290ac74173adaedec52236860ea75b04f19ff7de9fdbf67b6c40d3fcee5fc506009e45bbd16ea59bf02bb240c77c4dc5926900187901626a2e9b825579e02a6b20040b1d7cd67606d3a12f64c6ae90b4cb32d1918032133e83bc79da1fe0c910afb950ceef42c4a44d3ad0da166059282cb743ac5e9dde6c2e3bf3ae8f187e526b3559752c2f59506b6584dbd24051867464289034c6d464ac2f696fc35607cd1277386e3b926e859ce91b43a07c210c66f887db2900e2c2023d70b25601b91b1a4272592475dcb8dadeddea4c0e7bf30641e8ab4180efe7d1e6abbab62c571ce1a760c9e7d1298b60f340c107f008b0e6f0b50bb9d4e4c2d0362eb56376ed7d618435718a28340d87baec7337144884c103aa1689780d0c0b05ba74e93082db8aeaaee2ff652bda99c166c7401b69d0c6077565cf405a14cd1368673dd2a23bceac6ef19c4eaec694965e2f4d7a05adb95f41fa329c8b23b4e69832e9ddf7810c3511444cdd5a049d531c82cd50e17fa63d501a5e500610a5696fa7e6310733929be91bb80047f45a540f055baf6be907485d85d9a5d989862cab4b201cb4d8463a66ab44839501e663b0a5097c3513bc0b83f28a035be4bf288a22ea34d2deefff5d10ee42c0bd5f1fa5de5e75abd547847b54e771f1d994e5b2426c9aff11ef32352580db5af90d7df80983bcad2acba8865ab22af8dd04e6f610657a88370febc796f465aae4bbb726af98943e49693bb8fef1012ca20963987a45c024b8fa042af8ad82835addd184ad51624c4142e8a7948b326dbe170e09c523ccfd412e8f4e80318c5fb1258328c520a20eae99a51001a6bf889811e9a19c105756cd49d491be4ed304fa4ce4a64fc4e61a45f84c08c2e0954329788983344c950777b90e67f5a36ecd36d03575151408882e0294144b77c8aac843d5f1987b1ebd12ccf7b8f00bef76c50340de801a50757ae630356d142a04644361244a85ea830dedf697990aa74b8cd977059b3e56dfc0b39746f3a220cd8870a30a033a8b688eba0b33bd419f4fcc7f6316fa7842c61e8f2ed185b90247d7620b31549fef29bb28c2d41c924d4488331ae8f1cb4f82c660330d62f0776566fd155c554e01dda60e091a6558ce84f4661a46f7ce19e0a394d0a3b8949aad7062c5dede26c5c2c1e454085463fc978ab80ac17c54be43440b8d5b0736a426ef78342d535da4110504c6bc64c5dfa7486c6fa4cf5a548650335a3a0e4a3a2f21baf4ae42185b14cd0c8016b29ce63b9d2a2b5b2b6a21778c1625639e22f83cec65b324ac9c8251d0399c690d8d8f121562ca35494087aefc0366d12c59faa470904bb10cacd198db8e5d0c590949e5a602b136e3fe3a2db7959dec40181f63fed4231c151c8eefd2d81fb87db84d413d4b7e405ef79de1c716f311ae0dcfa33c3094fa037aa2be200a96791632b579e0e828e19f9f232604755ab8e88e9f07e81c9b3a663ba70a572295d026f6ad27603a2347f404d420994a63dd4cf20c7e97bb210453b3c0f5b4569a5150e0fa891c306c8c5aff31226121bb0b3e0c28b3eaabe5f5d8801465f9eed30d12292f9889e34d96b3f4cd6ead425322ea55f728eabbeae7495bfbfb2efcb8cafb648544b82eaae2bf8b4d7551fc773fed5c74a320638630bb39d4e30ee3010a270b3795572cc534a195a9ed4bb04b012d0fd87945ab41064c8af45b4b5b133a7cfb95d1338d0176b897355a5de286ec19b2113612bb1c0c673b1fd43aa345a573f906ce6a33bc804dc84e2afa241a14b8bd451443ec03f148a87021b32daf5a692c55e0164889e0351e0c8539245733a3dbf312654ad59fffc6eca78eb97dca327c2df7cc4cf4bb906e92b246af7a937b1d8094896c8b1313a5789884410605c3c52daf571d52c1e3116f25de1ddc6b8f3dc75c2ed502ab24628f7192952801ea0617be8e9548b6dd7327a624c77c106e59a19bdf6186a0770cea25d26d4d03be3f6229e919221823ec61a76ecb86b3c1ec608ce0b9c32cb609046b8597991101aff0f36c6c820587a89820f122cdf989a9d42f12d62ba25f3ba03ac833f3bafee1bac40ea9648fcab5d7c727d26ad1f6c67c9ebd46d99c77ddacdc9715391506afbc5025561d56bf483c7f8bad2d030b77f27204f2c5b9b244269e03ac97cfb3704374c548feb4bf4aeb9b52262442b282139bc55c30fe39874104f11b2b902aba74eb4430088b1c2ece2ad0bcd751a3849ac0f4ac0f7a9b26a7503e5e0069152e36590c401d18a70c7b330b90536eda54a17a769e1e8100c5efd00cabdf536b122e499e248d1d5a8f3464cc1431aa8c9fb455a7d3cdfdea5633cabaec16faa23fdcf5c13423cd3f0861210bc5ce038a321200e41f187a10c80c1547b2f089444485a5da02fea7d2a1224383fb63afe5cfbb9c90f4dba1a71586922e4ecbe1ec4ecbec850d648c6f94c9efbe10ea9b1844d23e1aec43ce4ddae84fa32392aac9f49eb5bbdc55922f93b3e3341453797f8cc0a60ed553932fd048324f4434006e1233f2a7ea76607b74f0702acd6fb93f238ce461b2f92b4850e3023b8b07cefc374c37f1de1315ad72cabbd99489ef3babd02a6732a7f4389dced7711969d270e4c13c278a0c033218879e9e1a2448ea40cd107f3c2178c913bce12d109b2c3742a05140feb4b19490ab74af5669284437e0639b92837fb946e4676ff0bc75f8a181a258dc513aa411a073ce5260bc9089cbf36879fdde426f69c7c6d923c570d9691065928be54d6996415facf95e11504db83d514f423bb56bad173268af40999e2b213406401c17ebae42f868dece78d0d76052b08c6323e23ddb9a169cc8b0e1596f15a4f1894e74ba5da3320feb60a2d10161cdf0bc5db3ec439eb2d75b538a6d578dc725b1a8611dde73a8c40fdf9a3e663d2356c2262a3eaa343a2a125420e868443dbcd6d78ae633bf38426567148fd1b375177ba0594b3cf4742c1078ddaef167fade811be000b2ffaea72589f11c9434db71aba03a80b97bf884d7837addae7c46a9594c8c40b94b1ca6bcfdb8d1623403c2c2acf7009554091477e9f4c19c4c8df594e0c9307e90d49f4c1943363891f1003957ab2fe02443d25bd6b8330e793840ba1a2ecab5c43dab100b295510c7960e91400225ac73a0d4e003474a64d4b58ccd1493e9657931d7e1fbaf10ae3e9354de89782523d0355fd7c2732b46bcfb887a2724bd43ccf31193c3974089a9fd7723aae4c8b43f226f59cc907274c636ab83453ac90206d08e041a8d09de408769883384541fbe3e527a5f098fb0cb6bf9e0d5fa9d01afd34153f51fa1333d745d004d15f8fda88a23450a7a1db38d0956704bcb76a2398e26600aa3637088504e752aa8f7544118c2528292e89b90bc2f9960b1660294cba4f2ff8c0caa3398f7ddf1a09f3f0d7c90a64a554ad55da8c76c9e8596dcfd212996c9f61f13776b0ec71b90514a1b37cef78c8eb33d87835b64ac4a8c3dae7d8fc66f9193c36d6494928f1bfffda3e369a227c5d1a243b0f0437b2978a1c3293560fcd6989ca9cff565514e40bbc0c5ce4173476c6fbd71fcec522211626bc99b197b7d014b0805fa638b9181e64956f438a708c4c7e6dda82d00b79ed1d2d1e1ce37d2e274267f95de4777ab4197da17531147af23d22469a446f1f481c1077dff8598ed898f073a4e693de65abade292a6a06ea8134e3bba0f3b9c2798473dcde034724488df3c6fd95be783f08241f6884d9af62132eaaf528b5a49a840612ab21cc2c3605c13d9dfd9a69b406049b846ba4a1c3c65067914909c5d299bb28046ec7154d2195051182a5593164b0497537c2a8ea13337e15fec568ce36fe2a08e94684067213ba414bcee102aeb13fcb6d61e862368717073379e4b01761c6acabc0f2ad41dab9d5719e4bf2bba01d7a7a4b1f6d04a1901a58dabf99603474d09b24e23d52804bcbbb0bef9602fb4847b8772af0b4f126c11cdec0070984eea50696d6df26384919f22789f89e34e0d20a5f449ed113b0e5d67e3b63b00044b4fc0a302ef8e91c0e483237ff1d6b490d70316b06968ba69584c4cfcd1c8d3946d6d09423612bfb703cd05b8b62a415c0f18d551a8dff84b5e8e22949626561997a10072de339dee505dd2135c40af20cd2b871b3486ded1422b1ac1d43f3fa3142d21e951ad98d942165ffc99b173d0a2ee78dc5f2e793678b0e080611bdc385e48d50882027e7cf7f7a0559ff5204bf422002ddb98d42d5307f9f3bdb20355764a2cfc1729ab80328ef0cf9cd042ab11f9624eebbcaa82c2514f43445ac5d7d30f066e931391bbea14c4bf85e6d4efae53d03a5ee81e0656abc5ebe9000846b0e70550052cf0fe717767d625a43f2428a285db48a2c7eaebe00a93f160af6c792bfdf3e87555c7f54156220897f611740784109dee83297fc0b09082824604630ff00cefa837e15d6f0775d59986007fab8483373b052f71fef7ef9153c1fdda0ee4c1fa9160f9e204477ae8401a55b92c3f5e15f7c0328df4560859dc4b34c651d939825ae8d9c6d5da291aef894f5f12b553466d40d488339a1147d9ae3a2e13d9718d2a5fbf1f23daa56e280b10fd9bae4087ce2dba3bca84e320ceabc5b8a27342f1b1eb04507d0465ecf56162dc77b112be9bf660fda9e80fee2e4f49beb176db89914a5a6f3224ac05505f319c97a955ac599076965a84d1d13e6031b02bbb45a8ade1d034844ec65c0c3e31bece89452b397de41dd02e3f85e092a70967a65c8abcaf5ec438f36c13ea73bb7b6e92c32a07584580a65e72f117379a9aa46abf80976c1f610b79236c129132df1c1449159230f188a64a6890151fb788e61da6d689e83e54f808bba51150a981e6de492525502ade0d23d2e0ede7562dd26dacce3a96446ffec923ab04595f6b3b14112ff1f489511eec9e840d582c861b9d0b3d728c3d9079220e512639cbadb862eec4acbb4855c619425e159745dca95a3bac11bd70cf0f97ace54a1d52467bf3b7aeaa160ed59ac46f66d35c85999598f321421f2a1a26814ce6956103574ea08e79b2f1df0fa996e1742b7af5fd6235ea305864c0fbde462073d37b809a1c84cd2ad065866f1176919f925c8bc6293eb241917f8abde1f2a0fc57a3fa275a874dc3ff88f1618205e6657e45f927d3a5060e7476c927cfa13c6b85d60f2509285385f01e2b88c19071ac719bdffd6ff507ea507f4a9355a34a066a7fd2f02e34433ed498000235217c825a7ab43ec37461e62c47509375bfcf251d8675d6cdac845dc2c242a1b46cb0ad223b5fc13e9aac2509449ee50f685868a61fe2b961c2352a3a3510449488b9226ca6ea4f978114b0cca80c24341d1f775e4614fb9446e194bd0df26b5d1b5cdc7edd733c68b87919c5190e545b1651bf49a5120de1c8a2586facc38305c3c856ab74ed5b6e4650ef8587d74a599c897d1cfc22b084069822996c0156f26f2bbe2fca273cc5e8aa86af9347bd6e976331bb209ba0d9d7fa43488ceec0aa65e14b4451844614d5193523a7401b0474b870d9dba51a34891dec7b2a8f595bb03301f87edb23c67a1a7f0aa6d27fe0fa70db89f8fe4f81e5fd9cd5c30240118b42682aedb824f061efbe8080f1a7cde0ef6f4390d3057d2fb9f01e0912a7b17f521c8be51eff5219dac1eff391cfd87acbd440e82197cb82564a061e72ff3ffd836043a14ac16cb418554c87cd65eef432a0e35f8a210299cea52f98704dcfbcbbd7f53e28fde851ea284c6a4de222dc978aef3f79bec5356c040c69cdd5f7250dafbde6388213ab73cc40a205f760069fc50e85ed4ab324023a48c8146e0902f926962b3c88e300dc441b2648a88edd5687f660b65357e8f5b678b51c6bcc0d604d37985add60d6a95311c34756a66994ae69c6079573357a5a417c3a4cb18f67fc0c8fc6df62a5c31f1bc5fe822dcd735fe6e3ae60ee1557b4d2537d904e4672517c94104c7524bb6a50ed724d473703d707a9309f660558d9226d5f25b11b1d2315efa3a14e99a7d2cd3859ac605a33ac6a124ee1894b7fd28c3804f1900986e10a8832ffb71993ff22896d9f185d6db17d63b9b039c9bb100dc0f8e1eef71afc1cd4cc86bca40479083d1b6f680b6f17cb4a176fa1ed49d4cc48807d598b15e77548b708de16e0ade450d6a79264db5e69b35b17d549f240906a192000566ffe361b5c0f9b3fedeae4a116961d75e60be0014f6e638d9b1bf90f2d333008819008318e66160ee10f37af19ecbd33ab1670660d8f8dc8c7ee534bc66d500719dc38dd8d509a3610ee744c0c62ef2e6e8fa5944c33aa6241333f1ad29ee65fac670caac5820416ba8470b6f181819dfabe1ea754fa1dcb3ce692f8289f6f109e2eae3b31ed89f9b00233d5f51e2f4cabe50cdcdfec9561f7895b9bdbc4e7737f51ba01cfa8ccae0998fa2cd0600827d9d4642f96ea48ac9eed2f51c544208c607f49d11de0ab59dde0a2ebdd94b688c734bbb64c4719f22afa3483b8d5f42d7365a6ec2a94d1cb015e71cb83a102428358fa36b3e908e6cb8913ae3803b061d9498787fa86598e3a6df4083ff6c78e9cf3af715e86cfbec256af0bc47d83b343b2fee7e067459c0bf6d20cd646fd7f036a88418b0e424dd27d98a2c4383d942425ae00b16cff087217aed227a9983edd26bedaed96750388e7a8f6fdd2a08b8e650b1b72f0be3f5f9da6f825e52fa64bcaebb8d08e8ade1eb1c550298e216a78cabe3816a673818eda94a94c6117c8634c7ffb131d9315309100c8be617423f7e5dbddb6bb5a5fa64a0ba967c41c29827d5da21f5824edd398861b132fed212b2a158ba873a363ec74fd22c16e8ea46d98fd5065246d2359efc30b1cd8a1b3db9c3e27599a70f8daf7103576e42562bebb1fb39a5ed24ad1eccf7d1e01deb62b3c685c02fa3c5dda77097f0f996efcf181b961b9cba14a57e29e78ee58e78491cc5ffa13b518409eabae12861559e912ba7602376660048abe714e57d0527cc9d1eb2513ed342ad0f16f6dae26f129e8820a514304222e7dfbd2fb9dc51802568308b927efe79cf359d611644a0cb2c215f268d2d6b21e3c116bdf941343c95c6f60156fb6e8bafaa47fc14bc7f135329ade6af1387752aa4d16056d24729873143ea9400bdaa8e65601d3ebffdf7dac7ab6d541c89858d32c0a674d9c8275070d738087825672023552606d0bdd50a542b2bb6d30e4e3c00642223a7f4cea15a202b954c2ae9acef88fad81c909b188f53ab000649a5bfff50373ba5c5f27cd8b4049237ad401a5b20303181d130901d0375e4b53fd13610e337c9588c4ed51feac7f4130c27c2eb5c0f7a034b17119e10c56847dc274ac61e5e33fd7aed41ef16f9bdba29874f4bcb197eff62ba2f85cb57de781df5ecd3caa67647e4b39f99173a2bd4ce2b00e95b569e66ef619d560b12bbcb72489b6042f785b014e89e08f85428399f640c38c00482c91cd399fb344c3db6030fda3b1af1a95579675b7c90791ff1ca23d5d7dfbf990664e8a669d61f7a6b7dd7c5d9a80a9d024abbbb5e60f84976544faccb12eb132d625cd253ebdd366edbd8c184f4f65b2c875abc8e20feb3fc73523b000e32f2d04ccd19bb23e281eeab49e1893b209d812897c6ecd8bf903c88e72a1385a90b4730d2093dac58d4ddea832d4c423c655c4c68f976e9802d8f93860f81287d378de9c40986a8500729a44b9e018455101314a82566802eb1af828da56e01bd81abc5a0ecf91f0204840a1d091c097702ef7a9a7479f0ee0f13cb919b784992628ceb15324110b1e90165eac48b75997a0dcccc4f09a948213af1597d932d059e8601131dbe76bd7489d289ef8a43a41f10d16db7e640fef1fd298a468c978170f8a203a74614f9554383337f2a64434314951c736fb6062df0aa4a5678f64045cfc12cd12a84632733ca8d3822f4f69cc4c5adfb2337c37c287afcb30bd099310101a473a875d67705270a98cf8b901571b91ad6a15f0a0ed4a51660cd4cd29e8fade19e74ad1717610e173be9051dbe6498c488f9fd54ca7453de839d6c486030f0c5a619553a3ab9eef77e998296500962c8537e6c6d259b53a8386f2e3871c9ddffc1757d0e03fbf761509bb119e14c61d2f5243204badaf6cec3212156114353a0905f1f3e6a64b78f83d339bcc720f30e65dd836c0ff6a524949fac3b76cf22b00634149af808677122468fb7b9d497156605a2a9c1007c3cecbe91c59f4ecd957305e3113bf0466788530addc62b1ed15deb97046f13de46c28f385025a62247723f5881e81f9890f67bde7942c0562829cfc8a925f662755cc20efc8db2d6cd13085ba0a01d8389decdbd949fde871ee9afb75762deb978fd533044820c2295fb8359323ff4257ab47a07f601d3b5af43a1c844389c84e01e8f660f02c8694b40c72d149ddd9efb25e9d20d7c03559c4bbab374d0112753737586ddb03c0561b0b7b738763822d57a70aeecf8aa5e29acfb3fd972690f5a9070ca830880f43d7ed61d6e1a4a62024d8249685a294c7b138a936b5025c66fdff0219d07e9dfeb152cc690d0343b72933d3650fec43fb322f8015f437db5f1b7a3c1ef7825abaff22cfc288ea3e744220c7373679c79a2d28c4f63e0f1fe384e305266b87a0e41b7b2f3cad0b0a4063aca6450a391939f8368894a2024f6181e9c0833135abf1bdffb0da540f979e40a817a2fbbb83dc26f7f5bec5153b500437d33dfffcd42e895fd508233b3c07896ee85f1d8ce810afcf7cba7e7545ea59433f3c94fa840a9a0507450e334ce4ef12f6dc7b67d4f76bed26919242207e360c7e1e267bd01bf89f55d1e3384e2b00323523bb6342d6a940e03fcf16e8ed6aa0669578d31e3c7102efa713c365a6c1d120a823213342d8d98f5e96c2d33df87b653850977e16f6c3617ec0c73a440e05123c5f643a38b0d66ea88865c1fb5cbb2833d825df63d756ef51b3c7f111191e15a33aabcc0d5b7f625b0f8e6fcb444b3dc01c17f3e2f0244f02e3b14a4f12f7af6f68cdb5382241bb2ee118995d0c9c4d575e389726f6fe44c65b01cb6683b22fc034b09ef3163de2a163f6eaf181a744a3d8d2797ac02ae07e2bd3cf74d4e7f9225155b0ac009846bdbb1ba3383fbbba05804abfa352c7ac1ce947f481b79feadf4bf5b95f124c6f3f1e40d549eaa6449597c1bdb35678c54fc62f270a86b780af93a443c675d5bac1fb2be9cd178522dd3abb094aaa085453f50e5b94f5f9cc49249f6629670f25fa5ee30add19d7a20013f9b9b9b9f3cec0061170d0cc19c007306572ec782d414f9f81b96485f48d6dbe44f25140c70d677e12e4133f23b7686b8e66e697ccfabe62cff54c966cff614006b08e38f122f45f7b81687510106b7517c1d5b5425d071314eb510aec451f484f559d95023db5d67e25a04415509e4b5e7a7f9bc29af1ada16d024c115e7d3244adeb7731912f38edcf81c28d2dc6d0277d4196fdcfe1f5b7457c167eb6b15b175b0539b773a4b0bf10aa62e7c3404a280f1064b04c55b375de6689e08053d307110a2ca3cb6a300db3d77d5a69cc3e9b739e84a1df83e6bcfccc1bba5195d96debeb0f347f0c308418704e0f5b92db9e576e26b7448d72964da81d22230ace63af4073795e462e49faeaf24053be4e8e85bb67cf3d042fdb3b3b916b586ae3731db9f4fc2d891d2f763cc83bd40af678371271a4b9013273a14f748462d6c91dd8fcffa1efefc6976663748e3fd90e90db59ea39d7d00d7ce4a70138d313e4bad4495f5fe58f95dd47890757fcc5f5ba84709d25e501287730a8820ce096c4dd5ae46c27a314b2aa2ac911d5688f09070fd0f1115c872d67929b906fd962ee7b42fcde05d6929975b367e7ba0c235cb62a1c0d53313a1cc8267be5ee36796307deb05b51ca205780eff767436dc10add0c3b0fe81e5d42002e83fefd818a215fe1a77acc9f0015a37c9a902df88959976ebb16d645982cd3f68dee4bd819288c92190d5ec7feb76499ea4286ef9cb5d5e09719f329f4c2315969518807fae28f90315b751c0ddc43f149476e68e3d65ed4a7f643a2b956f203aa06030bdaa9d821cbd423c829c8beca1424f5e986c6c98c9ef9bbe4eac9609fd75325997b3760a85a65ea7b3e747672a413b707f18f423eae5099fc6319b1bdb57bb7b7c7679730d0d6b86dd494b481120598fd729aad5391f31b5d566a9ca0d7643e169bed5a450adea21ec132a1a936e56074f5286906d4806f2a99081d4b24a6629b2d71ee4d839a6c7d196e190341e6bc640b6d073b82e314d88a7490e64c28527aacaa4750fd6de473feb89a067c54044ca31764d9060dca33ea8c785a5e5311472c026bfd26c0de636bf8ad31fc2e8577c0fbae40990216d89d1409cbf1bf5ed71f8d9e55566e723dfe8d448051c11b796d09003e4c3e093a033417314bb9529dc80b1a14ea17ac9b28196106b19c43d1a4ede94bed2e41c24262a3f1ef1d03a53504c5198600aeadb3b19b4409b27d3374ed708047a0047b02f15ef8aba7563036674fc8a43fca68dfe24bcdedfcda13738c3c330890142d2591913e6aea009d8b80483c33ea3b2be8bfeb8f93b9135ab9a9453229a9451eeb544f407c0c1d9d1f003787a36b240a3b5740777f5715f6959c401726184f63f571998e17a7de22ab0b30b93080a6b74ceef0e7fc7ab542ded09ab46e29b95d8c214cea5bb960cbb788c7a036f75bfd5035887560e1904f9ff28557ad61ae8b11236c4a888ba20b13979e1221702ad271674422ce6e9d9268be13a226880ba78215dd64f6801b355ad1442f8353017609d18846214c9574f7294372fb360606df435521ca37ffbfa490e176f2c394f9615260c6515147689c217797205db50921cf97186d6c4c2afd2ab594cc739893d84f193abda372700c1db3901d56f8a1e155e2b13a3a8adb16ce8a81c8c81dbcc62bbf1d69f0c46dbc7004b8caf06964b32ecfaef43f80c51a7dfc12b8057ace9f7e4ed53ef081a662e70bb8906434c41970dc370764c8e1191cf8d9899f50dfa4436efbf8fe22ef3d82881fca87098d721c7b46d720ee74102dcdc744a2428d74e8469f0639e8f2a41cb2d658ff99c75863422ee98a7f6ed1fd583d7a8414e72825bb3b2105cfa4713ce6788e017e0589ca8c5f9b7cb79ee200ab747296171ed7e3d4200cfb9aec0e70332ec7bf52838f27a55003c5e828db8e937b8f1ebaa67517da9fd8637f58184777dbf5f494e56c01722348ec90a25ffa84366284c3729e2e0460e55e48d26f11fdd2818c18d09f5c15a8524ef718468d080f1d0bbbe3634587e94173dd16501fb08e5446b62166196cb20777ee482884349301f6e88087a99825d0bc1c8aeaa5ea4ccda962371b329dc163921ea9e2f61aafe02f2def2d7170c0d991010a9a30fba6d558f790e2ebe6ecae251d22ff2bef9b874523354bf3d5fe4545a804d80305618b9c878adada9b71ebf248f2fbf1a8ccc6aa1a631c77f4538d6ab9ac35215af699f1fcc25caec58241fdd408b3446029069e54963b728c1811393e5468ed1db295e5556eb0d9ecad293582e21e3b88f8b0f8eb08e5ad7ddff6f2c8fa66c50c73276a759d8b82082f813530e1f3f50209644a09bb5a148c95c51b37a0b7b942e243bfab82131270674e7a34a5b3ff59758f8d6fca9473d0fd3950189d4841ddf063234f0453d9ab2ec9245409a3e98197da141ed89cf91bd3d17f9492f1e96ae414f28fe42c1b289ce09b6808031ea50fc18b655296f5cf6b1625cf538438c62df101aa73aebc5b32ad790a5dc972b8f0da39e717179e89dd737dd5be02d2bda8d18edeb29b2d14a64b9e922f09ab070ade31d8ebeab39cc8f5c2624c12500d518033acddf976ceb080d8a0a745c4be989e2c6e53b163a18d628e22685f8d1059292387a6255f35e7817ceac0a3a5e4cce9fc1ecc49aa8d24f4269339fc5476a61c1b6a55d97bc6d9612dfd034c1daf5696e8b19cade192be23328255d8208d5942d6774966289622cbc1401cf4e41fae02c94472c03ee1421bc0bdc19426e00596f1e257144c0a45a73cc51826fcd9d56aa26653ee4de92136e2c0ff96e37b003f5b4d437fa61e3b4ebef3a400aebea19f4224059875d4207758a1446f71c45c56183ce19c17aa8321c2dd44b6ea961aba91698ddbcb06968be795815b6783b4a432e57e815da1651a982fe11ed19fbb0127a38cb83aa959f843acc798f80a98ea6aebfb78cb0907f1058ea2d371ff6485f525885bf6afe15b52029ef736b69721d33cf6f000539529901ed216d6f2350ef866352d53ad424a89f4774a8054e174c99a7973f232f19cf317da68f46fa3a83ff9cf1f0efbcdb5edaeca29776de189f6eb107dfcfe5355ff21da34bce5ed80a90b20cf6cb3112f8962150e1e614778337430da7f88dee0d7225e36fe0d5568fc482a9a6bbdd14a208c189fa930465770175acfaf14d5b9fcacd3cd281a7536266130cca97b262bdf5af482297d360c9b48b6df9cb50bd70fc03f83210f3a099190d2696558b9f2ef7fd1e8b01e18104b0bbc30f5c3a868d0e063d05a88dc15cc22f4f48071da97c9206eb20e2f8dab05ba823a92ba5e2af3df6542dd167a32c45688925bf6cccb850f29f04e1847eb1a8efa08754be6144fee91a70cc428f9c174f4445abe6ecbacfcc7d96309268f7c6f9a269c3c30c983cd6338c435961d01382d982242c419e5aedb33a543268b1737249a09409c130cf60072fbaa18dd6d1dc4b5cf3b44a0b3da5f19fcd6c61a733c80cc70bbe10041b40486d9066a3a9d1d669d5f836253db13b33d6527497efdaad277b65adf02edc91fe82b26135409a5fe4869310a5b4a2bb4a712dca3a2f0e0f2adeba4cd8b493b08dcafc1663d2b6d436162acd2c6a8529a0bb8eacb86a18ebe3a5c9abc07bee907cf3dd35ae88823be9234e8681aec7f0fbb942a9cdb9aed61c74fb330ecc35b1e631d65d6fc187983cc8a36042b3411e4e0734062b1849d31e630c90e95f0650ad3b83a8806e42fe05aa236ee8b0f83963a5c729a3efd3f40cea935aa67fa5c0f618d3dd18a54e835a3b4107e5493eb6d1da30fcb4d427cfbe9e013bcb80953d75991aa0fb6e2ef3ad3b4fb0dcb1245021b4f39e7a79d898363f2f604fd141045f86581e19d21f469d755757344460068c0c7a312e827fd6de5597a2b11c78d58d1f36e9a643609d12db63472202e6dd39c7618be173d6b3d37f4f624173eab3d7d0b26c1c118b315379a20701be81a8543a55493365a126845d395b2e15c76457d04d1d8d56cd99a5e8976dd1d091977f1ac6f995a48a64ca2ed07d495df2bd1468a7c0034dc42d1ecf4994f4e8d1f4266353ff67b17aec2eb9ffee1f09f2f50d6a1e290990fc1383369605634791d7642d7841ac186ed0745cc127899176c36e01c4f1e39c132115f36d0a976ffaf2e0873a9df18046e7b2b3437b91b9412c0272e8bb33633d57c2e52536e9e1e3a21483324c2db07b47eb67c6751c3e883a340ab644e8a34fbc44f870a7a93dda29e229d84a8f9ebc0a6aaf4f37051ec2b17aeb03cf901c92e82e171884944019d7a10e998da45b4e2fa78a0674243e36b68064eb85021c3f3c62ef0d0c4b55e71f1eb34b5abd67c38ce892825e2d96468ae398fb2042002615cb0cffbbded05c89f0c11f597639e499590e3e326d9115c8459b0e45ef6e10b2f7fbc7b4e34599a0b5fac8e31b81fef328e8bfb46e5480ffa6a0e55ad65b2348d5b23ef6ce881e7966bda0a0cc8582b06b250f90d87444a052c1a7c354ba522ccba749956116695368cec657428a56cacd918c5e5f9f86ffd6cb6bfdb83e23bc7d3bd54538ca8af9bc6771f948224c25177082ff2cd39f852528c24c0e4c84344503139407601895c71e2fecf96944499f53a76113ca7ca064e01f7942ec04048d39a1e028905ff47c79848f63acb34e6be1fe79f953108d1ad0dedb334fd4fbb5be046e2a57f4f7965b85403e8aee4811d5609e8a2af2c2c8b00c2c7d168caa29a4eb25d72958b87d96d8ce504bd580609876166806bca63be5fe01de4d3822a32ce00b561bff8e86fe006c479618eb7875ec46a3ddb1a49ec8b4e1b3575639c80d108003b5bb6a58b892c467405c464b3df14b4c65668177623e513b8d25a2ce42b3b1ca3256c55069eaff13c15bc1f3ea260563ac777495b350c5c915282336c2334f5fd622abc863791bea822ea650919bad84781d7cb8e58dc95ddc8210aa5520d133b954694be13d7794c58a95dad678f4bb3a242219657ffffdb8ca2108ecfd0888f4e5443731b5f8d4c5d7d7afd6185faea9ef29b6c66429d3f2285c1d9bc354f1c600fa9a341f76db499bf4d28a0fddd0bfee5528530ff0b8634000afcd624491320e223f4ae7644cfcc7cc1aa17d4cfdea10c01595bb6ab1a3bbe8b98c394a8552a495cfbaf93f8a57bae770423ecc78ed9cd94abb6361459405abfe2a2791109e877bc6c0488b2820969aed61100e65d098507d71d954e87f128f9193b406907340492277b6213a23b25e2885508610338684e3b3fab9c82edcf2fa4409c2ef55021e92ec05769fb4283c5707eed2b2379c3b731d475e748c35f572fc545e58c925324dca3c48015c00dffd6082c906b636775ed412b9e2def08dd469f084365d489dcd96c7e59fee60e85b99a0ac65a851d91ac30e0f661f65537754369dff1ff060857ddacee0e1e5f2972b916699017b60ad89b14d85be7d97a2116c09ceefa9aac731c92b1ac71ccc4803682d2d57785e494c71e9229531c4c37cf68971dca30cb857a02bf1f691b89006c8ab24310535dcca5549d935d20edceca53d95b81cf8852014d5c67eb06c6e48bfd82a0ea67edea9b898189d3171c187fe6124173044cce59fdcd9ef3ca2d821295df527967c0ce97b23d79c0541e4ed9bba72e45bc5faf9c01eb072b0221c872286b2e92403c412d2c4b6e013ac5971d6f5cbced6de491ed59162964f0e75bfec366795e53cae6f47bc5452d87487d8f2f5d372d6d54523f47a55e3de96f38ec5acd1a2b3813050a19962d9ea51da828d767fcf6379d8cab295ef8605daca564cdd28a50f879f80f666cbc25002bb7e8b4bccc287979e3d7c1f3a6ba63d1a5531ea063c297e4554d76e40aaf82589e2883d685e43200307ff249d317a6f5bd0d448263a7c99a288dfa8c43ae7e7736d62fc201ed37cb2360515eaada352aca7beaf2127d11d8a3dd709ee080ccf8696feaaa2071047e4d8350192f9b7b1404e42b0ca35837d13abe8343ccb74e12ed08eddc0606ba17578faf3d517df222f47a223eb7586e47a075cf9dc4646b809f992842bd50e6dfc919f3c827e6d3451c99ee07ed770842ba76473c758878f25b75a1a2907ac297045ab5979f7557fa05f9ce71acc4fa5e525e3e3fd64b6ef1b73ea8c68ded6401ec0bf1de4c8494aee76f85548957d4e9894dc0e8247d2232ae337b6fcacf6e67d23232c8a125958071e00fdc0048835775f7a26ec80aff0207c1a992734088f999dd1d98d5f7964efa091122fe727e41a1bff689af27fc61cbd3bf478f10fe2c6b359af0c9bc368a08f1aefa491cf843ea5cca87eb95691ceaff6782df4118dd668b858a0df0c25223bece8c3d8bc0b9066b5614d1ced33c24289c6f2002cec88967eaf84829deffef87bced87801a31925f6883744404dd59722869779f01a9ca1792e4f3f5e1d9b23b657aec7d22d41524efb17cf9a353aca3772f7f899d6b7efb4523109bd684ead4b55e65ac656f3fc9f8be09f2beaa8b7d4bd3667a6d045cad2c30cd2dc8d3a1efa053660cd78c5363a13f936123e0f0be5514b451365e54f2890d9e22cb269292a3fa91e84e34487019aab49e16dec1995c4afcfbd4124a529449d319264f350313056f31b3ed5067c0e787b7ac0417f5a83b8308b285dfe9ea388e63f38ae03438f8f3e0721cbdfbc48d96951af4dc0f66d733de7ae79a6b59c7f9e20ccae060a49c060693980f54d223d93881e6f98c8533070dc7faca84678bb9fa3fa578f59a65fe039c27a9c0741683c11adb2048106744c3e1f16ca1d5fb31c9fe852ac4094b82f4a2f242fbbf845e72410306c86152b92ed581a4aa299bef093fa9d6d301c0cdce06c803f8e80ca19d55f3d86152410d88cc0394345ac7e9afc8aa49181b5c1baed5ca91e11cc90341683624edea960980e08b669c387cb865c81fcb063916ba4a4a508068257648bff0ea3ea103022c3363e88eabddd007528bca6053272748983151086796aa052cbea2e5b05cac80a8d6b12db213b7cc44505ae67d95891dca3d406ad36321d20b47e0d56e1c952f7124641059c35a01401617929b952da2c3320336d101c2fe88a0f44d45be824a62961d8261e853b7092206a098d0170640c15857e17b9deb20115a6568b1495b27fac3598a8dad8782407f2f71454bb6cb94ad0198747cced315230395a89ff2f5bd0b4b4c5a783e70e7817673ec6061e14becc18db6adc64e2ed5e2a47e859b68521fcbfd0b1fbd7c15487060f1728df74ed5fdef9a26f82130a25052ced68ae968d6a4277f9277efb7aa66ae1be1cfc161abc21ec4cff69c078afac9b4ee1a226512479ec8333f4a003d9a50d01351d14335de61a559339179c43a3af625a769c99d03a4aaa633a3d8c69a13fbdd6e5794f8bc50bf82955c039989ebedd3de0008cc470e2da3368401945903efecafa867c72ed0c1c7bcb07920db3e0682c7cc961385eeb7776cca7cc925f6ef85edd42e5c71c4d372f60c0f26a23eef78d4811183a38239bc0d6b747b57fa5d9240dbc6bbe4a171b7b87d8636e42dd0b1fad91748ce5f1df85c7b26b2076ce3ec462d26119409916682c8ad8d61cfcc028919d00f7c52a218247c7e8e919e959cbff1d46e73b8786e516037ce3d426e081b9398e10070fb7446fe0d96854df3bcb260aa9a06435ba3a87f875c300398b14dd1ea0a0ee09e41baf1ae4042a0b2a6c78f375e8147444121342eed444bc2bd6ee284f9a78a1b123c3d85d521ccda09eb13fdaf464103a7790a6a3cb2b5127d9e4918e0ad43014c26d378e2c9aab428e3f0b61c13de79b5d1595ec708015c8e5aaa92a6a2cab28c56705d25dad990bd70229687ba9115d7f956e15351b6ac4ad8d05fc5d8eebff1dfa7c7cd9a298eb0058fd9fbf4fbaae8b93adafed9d693c85c95cc7e66fa080496c84aef42927c3dab99f5e73d592a591e9a0b129c530dc98d50b27b4c7b591b3b4d381499a926e80878c960917b35604fb3e9deb48f92fc2c84015b617903035bb1108fc55bd942669a9dac7c65254529d5ad0589f92518ed39056ab2b71d3b147132323c0ddd023908d3d44f98f698603526e360f98ca25223973b41e60d326e59f9bfb434f7ece8e7ced446c1769cb84a14c4b3bcadd530568d8872356192b7462862862c5620b8845d2968ee1659398714105ed831f94fb74e37c9e98db8354909e61f3ac918939916a58829b4e21c2c1f0d9c8b83771441c18f926eb76d8b229b9c239e79402ed337c243b4e41f8c4fe31deea147088085281f09e2ef620bb486359ec7be0580dd280253783f8538f1600d80e2f5f285dbd3c974b9276ec20a42d5391982bd86d8d2739ba802afb13fa8a8b600eea203cf61fa96c2047a4e0c95d01c83f2d0338a80efcae7f5bb17174cdf722281feff71c3c07c6ee1cd9ed8cb8a266971339e5f69caa6de7534a09b169207e794846bf3ba5c25f189de1a846c1f61c0dbffef6c70a20f7d30752a3bbf059d8a0f7ec64c2918200284260cd3c1c2bc316b72e67cf14a11dea109368b099e6e252b05719abfcda9de63a011f8f269730e461aa8fc92f3641b707fc93262bb1a5f504d824c18882a1cc799457d5d1ec1f82a950f52946516d4e908ad38efc436e5ad0b4aa31187ec58a273ded43998414604d24d0e2d274bc50800f2385e27c364c6b0cc052561944b170cd10e698b4ad4bf4f1fe43d21bbe51d73dc472708c06a42035c6f3c9dbf5610e1a9692539b7ffd06ec43837305a5e284a59a31004942ca49f8de79fef059b01f0d4ed3d4673e2e07fb20c304f95474f7a8fb9cee9530d13bdab95aacc7a8d4dc8cf0fe16ac1622da2f34c43115e4968cea0efd3dbaea912f602a509252caaef00f96b67f261f58ccbc80ed3bb437e490a429e6fcd7102b5a3265cb28b19f796d52545f3b8c075d1d15ba2193528761dafc95fb1445eeefd20665b05e949c31b104b592c1482a1e0127cc3b5f396e554b01d72bb9d042418bec61f6078ca6b4cd0ac957dee05dd0d2bdc1a64836effecd5fd200400c3d49be9e9406d53381491cd79b4344bd2252eaf107a25ee8eab5cd265f7fde6ff6de9dcad71e059756292dd8991226967f0bed319f4aabff7d2bff3d040f590171408804cf8478784a4a7ecc7439a2964d26581fb084966c299458b1138bc78dd5c41b06f69c54ceb5867fbd61ce1679acd7f1b80aaf1e95052055be977c1acc4662ac7d22986890aa52cdfc2c917d043ce39726bc03e3ee56db549eb223b59fe8df4d130b4da0a70a5590794d0bf80747d7266d16525689c83f1c97d118d62e1455bb2b27ae6a1bf29ebf7c5635455f1f93a9701c5761e2959e34353134d68bc90710238f29ff1c210d8b61e954228a0436701018c55f3261e5ebff80171b15592db634df01e77cb9b54c40882489bbbad05cf09bfb8af58a4b5d9f78e9b08d3bde8eef3e8bcda9677b1614a0158c41138a369f494efc51544eb45893403bce022ab80429e93823e0f9ef1f3c735e1334a28356dfa20eecbe71d3b75410765f553f48a9b0a986d9c1cb50d966684d06cc2ee612c3ab1104812c379eb4ae58eb4e5170276c546d9a69cdc45bbd6fa5a27843dba32d310c30f4d23499aa04093b7db51c1570fa464757816ec383e9fd226ef767dbe491581c9d05881fa565d3a41886e95f50ae77ef1de847c9062b4dba3ab660a3d1192545072e43c7324c05b71886128e59989396d7ab2534b84721aa6b332b3c4d4861aaa6dad2f0c94e727d831f7f5c4c83d25449c1f5ec8117b46a6ba5207198fc0f80f92d432a74d95a47518f67c78b0007c24a8bbf566aebfda76e75d52fd681d20cfaf576c0569ad0758684ca16ef719e004515c3ee992af5442bd3b5200d7ea67b46dec1ca2cd12b92f1e2451e8a715048c1703672b375f474532535400afaac18515d5d80acc3e2a6e300dd48a0ec1320cacee170fe7a0cc9ff61879a46f1dca9820caebd024ea23d719790cdd26ea901847364cc95df42c245b20aa279df897f0c37d2a3682080e8cc2812cc824bf4e98ceda3e39be702b1397ec396a40bf1bcc7f6c15e5536152c59718b3d767197abea3e1f6cfa4c09d459bb18cf6aa9b1acaec91363c70a66c5aae4e62d56489b8c8d9a2a20b79ab028d99b159d6e27bfacdbf09dfd1f81a7d295aca26f08379806efab0fc3edf81d8efe1143c97f5880c15e0b283ee7d8a0b392a433249af899e112a7f03797eebdf1ca6ccfa2ac882ddbaf980f707a58e9828e19a967f9dbc363ed78b278966ed1bdc3d97af2a51a2b70ef2cad2b827b2c22dc7f79f50c5048f5465fece720902fbbd7e69679676ef9e576000e7596668f48367f48d1de288a0a59e9263879836e721e65a4632aca2120239585a14829af43d760573965eb77f51364eecd0183c328f91fac12d9d929366a4ba0a362204fcf151b7c5ab93649387da878c242a3c49e41b4bb54c6ef8fd5ddbf42c57043d220da62e80dd1e7fea06bb395a49e9a3b32d12a9c02e81707a106154fe0a4256b0efe3b831e311d35d04439061fbfaae2cbdd24a326185a05b05ea9433c4448a4c565ff8760213547ca91cd5ae469129090659fc220c6c1f8708d0d388e5f9c10e0b83a66a6a4bb1bb710a4b1d4d8fd90659cf99c7339e9a02effee6b015a2415b9eeb5f3c25c03d61dd0811ad48206d126f7520ddf35ade2d9211cf5330c75cbf95e1c9ec3ff56437609a853f40132fc9ee3da49e07498298cf910cf8351f93c8b0946d7abb9cbc0de13e700c714ff8cb6524fb0a77675155081c60012d17082232eac90aff11e040aa1f03ad36a1a8a6c8ae8acd2309ee3fdff03f138551c0ff723c0932631623e71c4d444743bf1b8061d89fa74fd4cfb8065be93791c4d569f22a5616c50c06090d7a47a937bed66092c66038a484b322892c9c7c367a8a3b70de23a57c4320120b7b9822a2d0f2675459aecf8f201a7d2bee83daa69707785b67691a0ed56b2105f902ac6769e680b1ad5df5924720b1a6ccc0c211140b7b1e38d9f804057e59d87387b90497b53081e87780b02ba920962dc98835bf538a4c1aadcf30ec642a937371888ddbda2729333df1f01b66d2efd5b0b776f36ae951f73b154868efe8584fada38d91aabee831bbd36ea2f46ae754a99b1dde3b9b9406fb582760a3c3516b033a5da216331059f0b7bf5d03ad12072b7e7077711a39f28bb82548ff2a6065cd5cade7b6c19695f759fcbbbd09d451d8ca9008c6a3ed1615f8c5958d547cf05372ed31570415c0e322c617a17a7b3393ed382eeadd63f5c53b2ca914c51148ecc8139c20c4eb20121bc77e0d6349ce1088cc78f849397e3c61f50b2eee957dfe5188bb62b5275fa89c6ec2dbd7caefdf9a5d3dda1509f081460d04534a776f8fb8613d105e2d8d3e1c062a12ebab4c0e91d4f917141af4c3e93bb0cd39476d7c9a025d1680688031c58a7fa79d63c4c9b6489648bb516cde27cdc6dcab390e7d3c75d245a176be04df26484390cb14f1fc53d04b8a4ea6626744d094ab2a30c9a56a0808cdd042714335a6274402a59e327bc5d370ed90c8ede2e8cc0661f22afc43fdf478847ff9e59a84fc0824a3afd5febf74c034171eebaf20ab293392b827a73098a2da38b04568cdf3c9727d592aec18bfca147087b900ac85776b40110042983c4bee49c77c3efc1d870e1333a0161c7c8acdf63901d567336798d8037bc8dc0f0abf51df3e262505e3fcf2053f28f75a529c2b4db856d49c71513b11d5613abe3e3fa02d007df28318cdb9caa7f74e81233981ea509d24197f95b5de80167bb34071bd79f46bbb971ac85fc1c501fc74f0b7bb6e93e2cb7b2485f75b8c59e2a73c91e46974c65687fa2fe8aa323eed25d168ec9822b82920430074b589034ef62bef53760ea22970ea991f6a5cc23ee296afaf3bbb032ba4def1cfdcffe5d840c1aced52f38890903d9710158938deb082eb82f7618fc6acda6e5a7bc6890dfacd22824dd831096e0d0fa06daecb296f110f9b95e5f7d38c62832a3c3152f746856a4ac86bb123aa8f9570824d9700e165166badc95013fc81b0eeefb4a7391428488e4c68a4dc640c598f0d224df21768b7c91d237fde6189f89eb8636d31368fd72af07df66a8842804930f5b282e87ab8aee21c09ab22a60d56350614302281e3963fe510a701f7ac8d88d5889f6b103ded52b8cac73c835fb8ece569992f832ec26d46f3b1ad949b78b0fe8e23c43ed56be17a480f53e8055ce05857c1dd1201dd423528299ed953dbe7599b37c22c45a6f4719c5fc534f48d500e45c8480a14286d1fb5f71aeb9a1e147e50909341168eed76c761e5b68f713e39eb84531a9213c0694d7e0e53d48c88b8a5292356db3dc09fa0573c85cf0e7252493c5dafc33d9dc61860f70b48336169bfa917f9cd9fea172c332f6a9bf20d77684037b4c6abd07eaf6c011c8a7b87acfda672a632ae0aefd489fab3ff6bd5958cd1d55571809cc1fa4899ee42e14349bdd177fbaac6c808d70c1f0ae2995b6bf2844447cc2772d29810d28ce9b39b2e32c881d21eeeebf66404a3fe61f01113cb5a7ab85d3a977425db11cd3271081bbfd02c48aee925f819a0fee7b446dab7081535c62e5a5faa991faa1ff51b98c2183823c6bf6ab159eb58c7e9754f35f68868f2dd7dd59e5bb9b82be81cb60e34aea84c56505bc47d50012d0d8bac73f562bd670e7feff0f2bb2cb29cd9c834d067261e1b6425509045712b916c279d46b440dbbab2b29f41bc376c6cdc2e8a04045919680dc58924ee999ed434544bea7412e6e2ca7df7805c43104fafb53cb5c4016956d3a3971aa07a75cef40ad77a0d663d987eaa7346c9d42d921d6d15e3683b8d6a1009bc0201114f1fb111fd0e820fb12f017ec2ea633fdc2c41a6fd52121e267add671b198dada1d4ff9480f9afd7fbf75935113217bcbbd03c40c120ce40cf84472dd2d919cbc26f5d66d2a6cc875b74472f29ad35bb7a9584181dd86b16d85b5c7c1760615d8864b280b7b1b8bb6b0a690eb6e89e4e435a40b9fd216f5a329e4ba5b2239719dd2166d5b51f195e7b06627b0159fe128d8063bca7358b31533a858c1b61ad2ab016f52a14caf013fa648a1428da6db6ac09f94d876729202dbe8cd5662156a3485dd6bc07327f1225833142ae6097c8c23073d72a4fc5f29266225b0adc68433b918b7458da690eb6ea9c5b5ae0bb72d29398e09e63ad4680ab9ee9784e4382dcc75a5db5a246e7b611b8e1cf6c8ebaee7b066e56128787c0d78540d78d5c909099cc39abd5cb8e6bbea27d896d3c2b61a96c7024bb0f7a14e37855c57f24a2009f6bed58a55eb55ad4a1d6755eb5158855f28d7e974540a9f288b5a3fa2f089c9d8a75a6f1a710965d167d1162d9e455bd48738c7ce56a1cac39e58eb391ce21465d128ae619445fdea2fbea5eed780f8abc7f16a7d87eb89b2e8e3dceed3d7c0d412300861d4c50cbd65da0d196624c74e5550be03a099bb38b6aef2424981fd4489cd1231142b295094a722a7a49391d311e40d89f392cc8195a40e9492d47995a4112693158ba44414eaf1a1423d3959c435a89114295034813ca9d08fa25c831a094d43a79b76394b58252a848e689793a484c4284834d22ee70a979414b5798bf0285338493b5e28908cc0471ce9a0a430d2d12ea7c96a7511a38ab4797360b0db30c9abaffcde4474c26997d304af62242598a6661317310aa9cddb77e18039e24b74dcc032819d6eb06693104b24ab93e934b1ea0456a986a878a85446a87e543a543f54382a1ed54d4a993251a686dabc8f9529a3142e5345dabc2fcbd4116ddedb949b4c4e7c55c23c4949db8ca95c34f19787a1c0fe0a3a534327e24f549178d4454ce3de98784a4ef283f5958fc94d4d22d5bd66f88a1296897585e7eba773053ecd804f435ee5e9549e6e27a3539036ef53e5a9a83c0139119d4894a7a1f2e47312ead38ff264529e784e44dabc6795a71e6dde9394a792f2b4d3e63daa48053ebf157c9229f0d942812ae251c1e767e2933c81cf560a4fbe57dd98c0e717c32759029f2dacbac9f7a921fb4267253ebfe39384e1b385921acad723452790678dba936dd44cab65c8e2cf2b725884d11e11497f9a2cd5a12673f8aca9c9e239129b2c965c1c63909470652d4316cb7a24b782563eb2f502c154aa54e28af20d4395eac4233d2790e745a180e47b6f8844fd746f88ac3f4d3744641124242323222221a1ee144ebfc48ee89757917e5921f58b68d42f1755d42e312d2da8a27cef3181ec9ff7c6c464626ab5c2b0e23831b0ee1d0389b5ad56ab656a91b8a9d56ab5acc945029f2e530b9fa3099f5c49593349d99955dabc2a6f3629b95ccbd36424dfb36f6c798a4527b9637cab2c29cf9a4b4e529e3593b0be3a11458e1345eeab7a82cfca619a0e9f67170616c78981e4de3194587b84adf5b7d567678e0cd485eb2602a847b2886bc5a77893588a37798f326859c434c49f9c8e32c8ecc2e709a674c5e422a6a9591431a52b159fe3ef6b194c7a2cc7c3b0632e293b939436b3ca9b57259755e5c9ede4fb54799676f23daa3c4df2b4739aa69bcce1b3cf7ad62c9e52175de63e62eef5e2d9dd4ef0e60c6f270deac81b79db39ede46bbac9b77493e9fdc9c52d5a20fb26ab826ab042e825c1d6dab5d67befbdf7de7befbdd7761d42e80dd16aa64f02596bdde1cb1d6bb3be5aa5ae56fd22b65813d0bda37700794a206b92eaa922f6f576371f4e98239ac8c3fa1548f57e2512d42ee7156a97b30eb5cbf955a2707c9fa67ad42e275991dae564e1fe0af767dc30f5c7b2a423f7c1afc3a937bda3777850a83be68d03f2c9f65e12c893255b4f09e4797372ae8e76b937e2ee6897d3ed2f8fbb737b5c9efba35daaf81b6188694a2f09018258885b9b1eae11248820800001c4cf0f3fb469da5b5fbbd887364d7bdc038f8c4c8f19337676d881870c193b70c0c108183074626274c0c0e4b474ad4fab98c6779a6b8def0dd99b681ac9dbd34ba9385652b6252f2c97954cd5926a81c2341800484c3bb110310d366216fb3afa58711b2759f431c36ba8b28f1c93d98bf89c9399cbf81c9399ccf49c93590bf91c93590bd4734e6600483dc764c642f59c93190bb6ad9e63325b310366bdc68b8190594fc9452c8e7834618ec44230540a7b4fa9b06a55fa8a85c3b34a30ae4247186933a86f861aa712754e51eb306a9da33602a97724350fa00beb3c21f0a5f022cc74c3aa913aa98bb4cbe9f511ed7296ec4dbb9c224ebb9ca69c53a5d32e27cb8876394b702562639515e8461d42536f4204a901e40788b2fafc50d61f3e549e23d85b0de239b97d1379ddbe83dae52c1de5f62dd42e67f8dfbe87b0cb493e76fb2e6a9733f594dbb751bb9cab9b47ed72921cc5d97a8adbdb3d5c3fbbfed12ee7a97ddae514fba7c4ed1b8889db779013b727ab87d49b891dd22e278cbbbd05ea88c06c90783b84223a7155918a350a6277b4cb3df0e70da59007db1fa44f8afd31bf4271129714b896e22a6e716de52a56b0ad12016a1714b886e22954704de52954f00d14b8020d691713d7ccab9cc0b51357c1b4fada094c93826b293f6102d74cfc84092c440aaeb718aec56ea204ae95b88912b8460cd72018d7f04b94b856be448983c0b802f96997e3da5fc2700df612868138ae3f3eed82826b2887ddfe877621816b240e23817f40c12e2f5c7b9d840bd75c27e1c23ee0fae304d74eee32c13593bb4c700f27b8f2f4689716aeb56e52826b253729c132b8f668d392e01ac94b58b8c67a090bcf20c17567856babb354b8a63a4b8577c095874de15aea2a14aea1ae42611924ae91479970cd749409e380ab1176c4b5f12611d7c49b440c63c421ae999e237ebc0571adf4f0846ba793271cd3e15af7b1c33035a725c4b4fa1a875b2a0e0b886915d3a8d53eba98a5dec0b0b50cb9ee584b5f3cedf2a3f2549eca5379a80e1cdb43cd74668786c4958704d21e7d1f49daa3dbed76bbd91d7687dd91949494946481be96bdb5d65afb5d9f148d8b10be3c122836260735b3dc49bb1adb72d0225825362627a445b04862707252b31c3489083beb984e8cc9e1661d9333ce3a7c11acd369968396a19bd9989c9ed9989cd2cc760c4ece6a9683c6e478b3eec0126a96831ab9b32e26e7ce684c8e38ebbac6e0e4986639e81122bc59c7e4d459c7e480b3ee2e4763707254b31cb4e8e666d627a267dd6937ebfa8bc1c9019be590b3ee4470b38ec9b1b38ec90967dd793139dfac8be9b02d07ed4c23b68921b6e1c86153046b56c245b08a60b577fbfb7916db70286d2172b7ed7731bced3b8e87b98eb2a0174b9e077ed8fbeef7c645b0665cd7bd5f046b867a05ebc5367a13c312f82b7ab8148a6109bc0ddbba3edd76ff795d7b230a47c292c7a97834d593b05226d1e46258022bc95961eaa617c19a99601bbd615b11f7bbd7ddb084fd7ab78dc709312ed195c0da7ac971e4b0096f2b611c396cae025f8f238777e4c8919b9b9b99d82ac1b6be2d87352361e1d251243e919405bd5d95704a4559d0db87b781d846bef42258b3950adb8a60cd52266cbbdf6d4f1971cc84776fcbb13315109f30bfdbaae9230adbe80dc4b68aeb0c2a280bfa22583312db7040bc4259d0a3a02de86b1040821cb38b73ec0d2fc013f1cd6ca02ce871cc4ea5db8a60cd4c23b6e5b06622b6d1dbcdcd2cc45ddbe3b0680bfa1cf4889b9b1911e42cbcd7f56ddc350971498bb2a0b72f615b8d39a481d9d78138a686cfc56b093b665efd300d1ecc892f5461d65d0fbfb874b29a00e830c2acbd190e11ad59875b280bdaa24fc437030065416fe3f0ebe2131765415ff3ad8008338b73d023478ecc727a864304396bcc82b2a0c7a9780565414fc43763798d4965f9a84797f388c8d30e212119191111c9fc204264c8ed668fe89758917e7921f5cbcaa85f44a27eb9ad3244a91d3a5bf26987562e4eee16c08b5cbd21f2fbf7f0de10d9fda4404d20cf2e2a22295d392bf81c9625233b6ace8c1b3a5017a77d2319110911c994d221312d2b6f9194de90417c960c78d5da0c78955ec23468352933a0952d089634dbdbd9b966ea8276ca257c5a8f0219c9270532f3598fce7a64a48b8c64da3f392910d9ba99cf0f26f72dbfc07b02d943bb987ec3103e69271b2730a12245c6242424f108667443110d7cda30c9267c564af456a16fb50ab5e7671ffbcc36e9a7031e4f884f970320dced767100646f10d4d9f49316c106434f206b359b300d9a9a695d842045b9e21ade2dd7d3245a448728113a24a665455cbdc819f485ceea0b9d513284b45d6a94be48525246423a3a3232ca4545444443433366b4494f840810d09021313130302d2d2ca669c662b1ffafd7abd56aad562b92244551fcbeefbe466b5cb3d8cbb1c3a9eebdb6aeab3c4af028b2e345938c8819e1791d890ea5a3abb8eaf0bc0ea54b795e6772f8a2a8b8e29cb8ef20eca7a697a7f72d9313d7099a1d525e52d6c00188181fda6c91599961a25029a9682256be625f3bd85e326eb470a059c110828ca921c204d16f0122864be01f4c379d9e9e9e9e9e72a7f73fa05ca8d47bbaf7a8deb37a0feb3d24ef29794feb3d27ef31790f89a3a04e4d4c7e22f1c2b4cd1376996ec267d57162d232611197882126115925bc2a61d54791baee34357f3885c23fb4494f5373872906efbdf7de7befbdf7de7befbdf7deef45b9f6ca8a69c662ffebd56aad56248945f17e17e5b7f3bc8b9905e56441c13555c74a8e892b4eacdedcf6bad1a2590941e21a62eecec14a8c6b82f8381bae01a265f528e7dec1700d0aa62debf0693d96932517754556cc24338614fbd15f46af56516b45b42287485148fc823e1494e3e8a1752818470f6d00363e8a24211d1915110d09e5a83394f770eff9dee3bda7f41ef03de27bc2f798de33bee7f41ef228b887e6f6743a79c23435d62ee3681a8342d18640e06986785eb5ef7717962f66a9d9e21f8a8a8a8a58ec0b9d7d640b65f3ea21c210b6c0c4d01bc95ddd075e1c4da7170914d84b5c2266e2f4e68953fa1f52373ad99e2c5d6d9e347f4fa27499277154e96af3a873a5ab6f9e3b4ae96af3284f95ae365d7df3dd61a5abcdc3ae2a5d7df3aa7ba5ab6fdefb4b57dffcbf2a5d6d7ef5af74b5f9ef65e9325f9e55bada3ceb60e96af3e0712b9bc727295d6d9ee42523d97ce9254a57df7c899794ae365ff2b074990f1f331f7bcb7ceba279f126cc9bb88979938fe6c7a798d97cca4f4a57dffcc94da5ab6fdef413a5ab57b2f9137795aebe79d74fa5abcd9ffe2a5d263e2d0c7ba2a439a5a4d94449732ce392e6b2a4f925cdb092669492661234bb686ed14c52d2cc2a695e9534ab32cd27315b9b94fad1c0c7c8ca8a69c662ffebd56aad5624298a345bce5115bb66666666c4872fd9a04a999f222da3992c575a566f9a49b118d27ff47a19b55a45ab1511490e89a2d0f7b5acca683e615b119f9d8ee3b6acca28f671c2387a46197dcc5c296f7d599bb16f118a3818f0641128b73277916565255fd3cc3716cbf7b774efbdf7de6bcfdda2ec6d20fc26b0d22e679136a94d326348b11ffd65f46a15b556442b72881485c42fe8bb97f921d226cdb6db326aeba135f621f303d426cd5f5959314d33168bfd7fbd5ead566bb55a9124298ae2f7c9eaecb6acda9aae5c0ac295d33b7ddc159333635decde5fdf69fd57cb9ed620787af2543aad45efc8912347c2d3fae129added6239ed6f7e369f53e4ed8d6339ed2dad498567d9cb0edeb3151d3a909df71c49f188a582c85982c9d96f00a3c885bdfeb5d8c0696ed4a9bf5d5666cee47e883bbcdc3366cc335b7a71f9e68cdad37422fa4c1b28d7a7a5a0a676cf4e969b04c43a3af0aefc392ebacf1fa8b3d30c467e7f0dfe947978873b0ff2dfd96306ca9acef162d905dd34e36a55ab51062908ee371f4d0be876009470f0dfc875d03b0c9f736792cbd4ba55229865f18bee72b896159fa404c69cb28b501cbde670396c5d3d47cde3ce39da66611970ec3d27bf6b73c6d4db5019beb000dc4a5e77d14c57fa2ad8706621adf430fc461f7f55411044be0e5aa8d57d37d251086b45fd0f6baee387a6827533579387a68fd11cfd0f0d18d6f71f46eeb43cf84cfcea69f46517ccf28a3f9249663638a3d0c8ae2c3eef465d3fb4587fb379bf0884f4f0ccf9a7c69357ff7399f71d8d6604d37778512675fdbda5289b3a5d271f4d0387cda72dbce7609e3e8a1dd972ca6690b72b760f7cf7a1d7731ed4aabf7ce71ef6e77ef69936d6e572b9c68dcd31cde7eb56dad7dcf3d6d190ccbd5f4ad36c616c2d326d3282d095d39645797e7bdddb97f23b72df72967c3bd6b72dbd3263c310096695008b987fdda98a673e6ec975d39d0ecca81b6d9302cbd4dee4a5a4d6e5a6d7ac25a6b6dcbd55b6d321deb0bc2f0e2aeabf5d55a91d67ab7f5f4235b3759ba1a7e80de7278fa364052a19f5bfe3215fad9c964beff32fd0beffcebd56aad5624198a62e9eb6cdf6bbc1720b6e11a0fd396d1d37e64eb26cbbdb79c6b389bdb9c9090568fe4b6b6b3b7bdceeb3c7cdeee7e954228d217a72f8fb791caf6a32f4ea429dbdb10eb2f7d41a26eeb99a159e57b1b5de64ebe4061db0e2da3ef99e9d3c84e256d996928533c43f3de1dcfd0c0f769a57717cfd0eefbb4f0dd2d9ea171ef463c436bdc9dd617f169936dc072e373e6879b9b9bdcd98b69b0123e417c923f7c72d9c3270d5683cb2d2c6daec46811b24fc5b62a084319dd694f5f3d5b43bba001e6d6eb9276ca7daf0849314d88dcaf476432d73194e11521c97cc26492860ceef4dcebf129c3621af464a658469bd6f60829dd81be9054a5d6b035b8c634d89132e4c833345d6e8b69b0230f3892676cac729f8606bb1d3992676c98b23d4d2adf1a5cee8e69371c39926d7c2d2b1deb173a93816930f279c3b56dc3d59a8552e922c029f763ed42be06573a7d4b892ca158442c9e502b394e474e662b6a0a5f73325724b6d5a0beaa29a91ebe66bc785b8d0af7744fe18bc23ddf49dc6a19edf16ec2af9651ee21ae29e11ef0227ecb68b7ccb6ccb669e3cb8d5bda745133527737659acfeef7bb05025126339943dab64d27cd445d984c63fe4a531ea98b7114f3578e59a42e4431cc5f29e690ba08c352feca3097a88b5229f7579632485d80fd9560fea88beff3f2e779b9efe58ebae8badcef32475d70dccd5fc9e54b5ddc6bf357de6ca90b6b73fe4a9b9bba68aaa2fe5de74a5d7c2ca6194ee9447cd6e39dcc707ac236e969b09cd50c27c49796f04765d5c697bd96f2a4c162e52944eed3f100332e0548810757d8020598f033646602a9800a219801145240831925bd0653107689839383de727ad6bf2dbb8ec3b61cf6dc73ea2c02f61cce417f318e1c756661585bd65baee16c6ad708f90152a654050b09c8f36be9c09249aac2aedab43f64fbf0a3c70feb5033592a6d20bcf76a0e92c54ccf238b46d8915532155204a45c041d59ccb781886431d71edc3215d2039f1e40201b01288bf9e3414ea6b77c870094eb4d38c6f8be0f02247d733b204c8ccf04a4fdc9ed88e35702f2f4782c152204247b3c1e8fc75364e76672412d6dda7ab32adebf8f245bad7a3badcf37f230668a6b52d24dba4937e926dda4ec9ddeec8bf7a3553c8fe70b420e6911c99ee7095dfa5bea4033c52c204feb63a2ea116dd28b55a7e84627d3c6306707d91bcfb337f6c6a26e077941de6ff9a16e9ba3776fccc1265b7b1bb4d7bb2d6d3258d26cf9be4b9aed461ec6a45c93be8f249392fae1b9b69fc7f391640b7ba6e95d1cbd24b29ee6918731575c937c80dc8082c0d2fbe1f9b4e9bd547a3fa30e9cd0500c8bb9202ea85db87be784da65a85de8e9f17843f6c5fa783cf685dbb12adecda107dd7c60be8f245b2d1e8fe7eb21cf7abbd8fad897ee86dbb12f4366d0815af4e663be2516e5999b4ba3c733829b746f37e916f958da543df5f36bdde382ac8af714fe3e14c6277cde334d4f0be4f70f7ccd2e01dc12a6691e1ec6fc9d1b2a3a4ae28cd4a436413cf24073902f86d1a677d42605ca272dc27407f919b9f780fcfdc6fb57ca6873289f5c104ff6fe79c0dbf174b2077493693eb9a0ecbd0bfa86c8a2d6d1937e7646f2e9dd7cf8f472b2771af3bc7eb520fc3e119054880f82649a494f874bdb04cb93e682e557ba66666632474b5a154b9b4c9634dbfffac5874b1f58c40d33226033d471f4d0c8d336f169cba5ee050ae3e8a1954e62d4b9873ec05bf03e043ff0b39ccf7a4e5b060fc396b49a5cffd55cd270f49430a52de30e9635d73146f7fb51882777ff4e3795a77fe5cd632980d24fe2bff2acd964d34b77cc01c427cb19f0f4345f26c1323c792acf9a4d6d2a691e05d0dd05fe96f0a903cd20a6d95c6fa36bfe07819036d8755d17821d08a6badb751deade0ebc240882a60eecbaee045e10bc6307de7b41b1bb20085eaf03ef05b9ee822078bfee5e8c8b60cdc2308572711a6e3a891e077697bbe04f77bcf74efce861db1d6d8deb276fcac29e9ab4cee18fa8db3e5c046b560a6bf877d54a55fec4752427af3179eb1e0a90c3de572239794dc95b57b9dc77dbfd0a0ce52400701bcb6d38c54bd28423870d886f4bf1f236f3275e046b1672dfc596840a359a42aefb2d919cbc067c0b86f2c76e2b81f2db5e29246ee2e5b61638c54b98b7bc06179714afa106d96d3560db0afc5ae1428d26aebba593d7786fddb6024379fd45b0662e687071394c4bcb4b0b6c2b82c56d2a00c0825550d85b2239790debaddb588ea3824753774b2427af59bdd5e23600c0566e9be1368ce22a295edee622c32d33b4000066a1e22896209ef9f07d85b75f956e696aee7c88efdbf0d55dcffa18cf811ef621fe76631a079b1302cd2957bb35cf46e9c322f78ec32b8b6b17b76c9025f1251b9fe89d23c50c9ea6954b33b47a2473a7d932dc90b9846ba58ba5db107309d76aa58ba592982bae7d0fbfdba87d2f7d9f2dc34d06bbeeeb6ea33369d5c3340b85243173b856e3de615ae7f30022b9c6dd46f7912d9b52e57e00c9227dd34a29a51ce52103618c962f90345dcf93c5dc660b0540d8f672a794aedc73f7fb216bd7dc22a98afe21b9da3ec25a761d0f4da98a2ad2b1bb622d3bef97ae78b81bb95beb143aa22ce6a6f9a47692bd11b1545475385c753c5c753edce18a39aee34c65cd34be7702e8b81f8a8a8a8a4ec5b0d4a7f5966f23bef54ccd5e876fa654450781271042841d9a0aadb5f7d485b679da31c69da9f92e20a4f4deef1345925cad5a2dfc6a2320b9dee43b049e2000e5143c74102122929477e47bee0443c8c935a85f83da8e04a837553c4d772c812fbddee374e877bf35b897f002b837a6f112882b5029c47d7f1c63dc7734ba9b7d1165c28bd8d6033ec4b74c3f4e87fbd22faec11dc40bf8c6181c96d1e6872b509bf5de48807a937f6970b80661faafb91184b4fe001a3a329253642a24083cecbb73f4fb48b2d5fa4df39769d3cac80d82200882e089809981f81c0be065fb6bc70380cf01eb51e63b886d3df71ffeacca0cd2aaf4d8d3190cab52bbeede3b21088127db5f21ecc833642a2408474118cae73327a266b4d979f76eefddb10037b23d8d1d5e09607f5fed62ef61196dc6b8b6b1dd3da0a4a511bef0e4db0079de2c3e7c782a718de91f58d610b76881a4c9de4be1771a03fc3c8ee6eb3e42e83dac1577dc572ae1f366fa9ddeee5c49abe96a36f9c334ecc18725add65aeb38dab1f48dbfefb12fe1b3528fb3b5ac99f68b8b4f9ae66805a8ef3ea303cdf6e069f1e9633c5831f7d2fb647bb5348f3d35bcdbbe475375c81c9190c6ced42ddf1799a6e61355714f73d50049bb6105183206487a6ac3e6fa1754c53d3dc9a47cd2db1916ed74b7f82c05557c9e744c372778f376b89b24fbc6e7e7a35393f2fd9701f2c4c08e7d315bc55e7b7b5bc5fe04a9432a91b3f208715fa35d5680e89758abc8ec80038cfb5ab66881249332cd2fa80b1b2d1ba80b1b640c7561a306eaa2057d89a5a02faf12f465e5a22fa28abedc3e51971ba80b9aba9920a4b12f036410a23bf42394b9a01f9d3d8b4e07d4a34d3b84567fab3c624eab87c9d6b4a7554cc39f05c89325cfea8e5c772acfa7439eb49aef6915d3a8bf21d32eee5b7cbb13f83040d6d3a336ed2d6a93e88821234245829288b4692f509bf677489bb736ed2f9036edfd69d35e9f36edfdd1a6bd3c6ddadba34d7b77dae411b303c68836af4e9b48edb2fabdafc34eff5d2cbd23c7b38e4fbf329da4757ac963a77f916fddc4e98fbac9534e1f4bfde4274e6faaeebae912ef0aef9dc457da2ccdd85fad15297eb7cdfe3d02c0cf9a63c4e267c52962f9596f8856fca4470ccdf0931a1152f19316095af9499388a4f849918050fca4474354aa18c4fc4989809cf849877e527e52211f133f69d00f223c257e52a01ef8271db253fea4371eff19b3035651aa58911a9f2b3a8e00804f33c788053e6338452cf83cd10a7cbe8e189a019f2d23422af0b92a12b4824f3289480a7c8a484028f0f91d0d51c133f7762b0a62e2738508c8097c9a433f29f88c09f998c0e7837ec4f0f922c253029f2da01e189fab213b253e491ec7a718b30386cf0fc608143c733bfc7975ec49b84ab38cb5ca576b458aaff296b4458b162d5ab468d1a2458bab13c48720407a00f223f3e333c3e7c70e3f7864f0f4c0a1c70e8c1d1e313c76c0dc1d2d2c3a46745787e3388ee3388ee3388ee3388ee3388ee3ae4e8762e97c7e8004f1d1e2aad2870d4f953e628e2a7dd470b2f4e1e2a7d2070d37953e603e963e5e5e2a7db81c2c7dc8fe953e5a4ede2037489b164812901fa41f9f239f1f463f788a787a10f5d819dae121c46347d08e17f7c58bdffe06e9172ff0e9c3f6c286152036a46c88317f62503135c47c6a206b70f11f2e4e2e6878f1d060a201a6d50366847959edbc945e5c481e2ea08b4cdc71182f60601c6fc1385abcc0386c5747cbeccdb938f7c6c511341881b12f492e48b2a316a31bde73fab0c1bca4b0cc4fd28b0b0acbfc20b9c8482cf373246b3961991fa3161b4c58e6a7c8869811cbfc10c5d450c2323f4335b800b1cc8f900b1a3eece117a7fd14c1386c51cbec61605e5e5c5c641807c661b361478c1135e8b8d081c366736c16e70748905bcbacccca6432993d87c3e705cccb71f4bc7871390e9e1efdc245761c32fb42b6c383c78e1d4618a1a3a343474ecb71c8ec8b9616c761715a665fb4b8dc5fbcc57b506f79cfe9b2f798eef29ef12fef291de63de06978cf7717eff15ec37bbac7bc27751bdea33af717a44a6583e56997145661b394f9496a19cdb6470ac76a90d1eca294f9396a19cd3494323f462da319a694f9296a19cd2f329a5d6434cb6434b7942d4aa095365124a639ed72baa66f144be48e7601575feb7ae7388ee3388ee3388ee3388ee3388ee3b8ee072082a82104cd0d5bbda93835a7eae84e5d57c4711cc7711cc7711cc7711cc7711cc7719dbdb13836c7eab03ad608bbc3f2b03bb687e5b13f3a93e7755dd7755dd7755dd7755dd7755dd7751de7dddb1d72812e911b7485eed025ba45d7e81e5d246ff4bc9baeebbaaeebbaaeebbaaeebbaaeebbaaef3c47beb9deed13cfda37dfaa7817490bef590066a22a1f5e9a016eaa1262a323a424a2a62e455d25c0a5b64d169310266470c0f183b38f490c1b3c38f193e323f3df830ce8417e90f17882f08b10629c48aa6d5a6fd0ddb6f626d5a4b840810d09021b75b90204080fcfcf8f8fcf8c1c3d3a3c70e111d2b2db37fd162b6ccbe6889ddfa45cb83d85e406eb47e68563e42903f6a883c417c2db33d80b0bfdddddddddd6dbd9515d38cc5fed7abd55aad48f245143f590bdaf511b78f68d31a49325204a948d251129211d251d191119151d150119110d150d0508bdb422db3b72d6ecb11417c3002a487223f32493e33907eec70c423c3a8070e453b308878c40ced8069997dd18271d86a8e3ac3515b4a2c2d3031307090b1c30c991e7ce0401d2376f0d8e9c1f3c3e7074890cbd2dd860011a94155a80e55a25a548dea51456a99bdd56901b34283e92256c3635e36b45eac6e205f60882d9e652d3e0b4000ca9ad432fb59598bb4cc3e97d548cbec6194f58896d9df50f64dcbec5f948dd3327b1bcace69997d4cd93a5a665f43d93a2db37751b6112db3a7a1ec1d2db387299b47cbecfb566badb5d65a6bedeaebbbf8c2e7cdafd37ed1e26469816baa8e951cf3a5e2c4eacd6daf1b2d9a9510e44b0dd17379c135415c99d7226b71c13540b4b87765b705d7b478c9fadf61d88f4411c7126365f293bb3e3333431ef5540cf0df4b37dd73ef4ec2a46c9590fca04aa1c89369144b3f7835e776bbdd6eb7ab7375aeced5b93a4545454545d6c7fa581f7b7b63adb5d65adb3b494949494949b7dbed76bbe9e8e8e8e8586badb51f8b6dd95b6b9358bad6e00534491491c2f0a84441f0f4fb4e3def94e34eef3db5f6b43bcbb832e293128974288c85f8a442a597f04983c0d74781bce6e8edc6588b595a467bc58cfdd55a91e277695da92b6dc6fe6aad48f143a92815065b8199b018ecb017ac055bc1489808a3b7c2601506ab305885c12a0c5661b00a835518acc2601506ab282815f69519564c15666c25f6147fa178b5545aab132bd224c514f1fb7d772f66da75d5da6a6db5b65a5badadd6566babb5d5da6a6db5b6d6ee274b77d7672855aca440a172c24c31112b51afc2fd8477137c4a7813e363a797401dab5eb2fe971c761c3db47a6252c252fdffffabfc44bd69bfa77ca59b28898f89a6973091c764ea656af5af4e5333c96124a7a92647c13d333660b9f56e8ce13a3129657e8a94664952c6647e90caa7ca97a95cc9fc1095a4cccf5029cafc0895df2d6dc7f9e0430f3dc8c8cc98b1c30e3264e080030c18313130302d2d2c274e6557b957c127b0cd8796f5c99f78ce67a6896d3d9c6e3ee7b394146c93313dc504b6cd186fe2399fc562d8b683f8d8fb3264842ff13e0e38948e61c050015fc6c4a87c57818169597b4701bbed29501800cbdd492cf353e484657e924c58e60769c4323f472296f9310ab1cc4f5109cbfc10c9fc0cc9fc08c9fc04992bf84f8182a24505a68cc1304ae010936162879419a64ccbfab0639c1cd80c867b68594f877dc0394e2898e564f161a507532636a365bd43cb5a46cb1a8796358c96754ccb1aa665ddd2b2bedf0448f2e4495b75742839493938483847f5c666bb7183864608216ad408c21644cb1aa503a265fd430fadc33eae0e1d2b3939260e4eac65b5dedc667bddb8d1a2a15909210459a3861844105fcbea0b18c66103a2c463f7c1871e7a9091993163871d64c8c001071830626260605ab8a7bccf3243c30735719896f5635ad6c7a1657d192debdf443fa56c69599f2b7de81718dbaa8e7e0f35c76c597d5fa6e2c45a56fb33eacd035f252949b56811b235a1b79724b354b74ce5f0c476a5d7a5445431414b3c293211cb1d9c882739fe945c223983973816f19110cf031411665f1fda946c22df94c814afe07ccb1326c234702f9a6e4ab14410165d55f84a882da31b13c5209fd14d11c5202744142a98a2c84345c43940219260f85288e205564472095785f8c299411c93be15624b88ac95451c7fc86ef9903585052ea85dea6d775b5b2b4eaeb46cf102c905f53f214fe091bbb91086d4eaa83d327defb4b53a5687e591e98e4c7384908e8c8a86848c8a82ae1097eb3cda79a0105405fd0786a5708cb91c0ca5221d4f30a2d08894e94d47a653d16928d393426d946951a64154057df3e81f9fde691efda3797a87d111468e8c8a2415d51f7548a6ef5b5b20d627d3df1f3748a6e780e4ca438b541d39b748c5b91122a6dbc2d46e6befe5686d6befe5b8ae76de077a1f08964a6128d62a8ea6533d551295a2b43cef8b96e78a92d092f3d65255cb73a543cde4f9d59284b2be5a5782f0f180340969edfba240e407966288f5a4474056fc52ad158bc51ac718b4b66ddb223915d9c2b2d271accbb22c56b328ab8e279292e418034551a831c6b70352f5dd6f07e40a25212121a161492d292929a9b11d90b58584c492909034090949154da996eab482c1e247d2f50ac086e620d377fec09248cb1898d829a5e5c9f2a2afd7eb45495412244890a8288d828282d2300b83c160b6339d35ff6f673a6b322dcdd7ab541a59638c8a2949b762b0461dbcfc917c548549bb5c2e97abba4091962dd204759272d1f27551244bab462bdb775c58cb8e66b3edb06da9c4d48a5c89aa950db24472ed32cdcb8d2d1055519f4355542f734054056bcc8124114b28263992144d50c55309ee15499224494b730b6f3ec2dbbd11e4f99cc0f3e97b2f29b6be3ac6b8d70ae479335e952410597f4455d412a0cb35c6a8e6eb0433228764daa40fbbac34f7bb2425222104b29ea2c0ba5a122450505631aa08437fb12c498c4b6881ac18e9c80868c8ad47ae3b9547bd39e2860c4c0bcb8aaa385581ac78956253525252ba52138ad8828926630e21f9faf004e63a2d4f919628614b94e812f5fbcb129728118b993091522286718912319615b15352c4181d9aa74dbeb618291769191b7568b23431ead038a5c66aad5d0a2e458a9b22450adb35458a1494c4a289947ae204bdb11413274c95afcd30e5c409d31469a9a282028548cb1429445a9ea418c3a42a545450a04891a215638554a1628619605a5858b0b05d4114638cd8ab159e5a0850737d0a152b6a7c6a50ae3fa580ac35b8ba825a9666a99ce9b4c997aaa02ac61864abcb1b96a79831cdae1863d0299015b3b000408b31464bf54ca74dfe4eab1833f4803cc5dca22a4e32536b3a6d70b962c58ad58a15aa152baa0a0f44b5765bcb75b43cbf22e9077a1dc85db071424ae98d9076d80f4f9656896e728f3c984eaf37373bb21d7920c915df0684dda803984d270e4a6f9bd68e33d9c3e853cddd68dfa237c6f02a679f833dad7663570eaa4c5599e24b55506b6fb51d08e9adb562b6b6fc84b8b854057ddfd6143415abcb8c0e367f7545bd0ea8bc6ab3561f61cdf4a6f080a49f91a3ce7272d423b39e7a7acbb9d109d2c08c8e3ca0c8b43a0bbf20643df56abdf5a8efd671923d68889289019136e90cfbf27d24d96ad13246cb17c8ef94f9867d310055b157c20d14b2b5d40a65a62a646210a44ddaf6bbe525425a7a951b9096fe9b81bd79442828a847460f18bd30406445140e13b00b96801203b2de0e4189810e3c119501b29e0ac9a7b5d6a2d8400ef24929a516b543d6db5b0a136eb8a1781d2f44f125031b4651a5478c493c79c2cb492c75e0428a780a32944802693112010095f81da8f856a2eb082858222c03289744343de05522962082ab25ba8e4e3e13b12505937b228a36687d2eb1258592fb12451b90901051aac0421145108c30110b092f925f287da528ea74582499c0dd12e22b0237268e5cb026c49611c87a9b228e30b8f484d87201596f290dab7922c544ac042d2161ada82a854231667b9ccf996cb18918c27a1a0bab8b0779212cc103f93c8f3669ae14d36071801142780d8140081b80810584fc98a03580041a2a7401081a18c208862a40bd7c7ee57cae740723f0d9810876703282480a3e54189002065859908204564290c28d2a89ecc440020492c488e406246b0847d001c913483153215190814f144e60822870005585286060a58128e44809214323aa1f64d785940c480f0b2916909f1492c892127600052194c4000a3b28f1011460b07a01144cc01222c74c854041074b0ce4890c2c219028a42a141da56e45452e205f990a291a821a0399ca5448d10b542c2051990a2922c190a20fa8b4508481214512200922bda41bf23bd251c40a223926ac50407e4650e1901e11161009ea8024022498a99027d0405502b2cb54c8137e62a610268cc10a3df93ccb97145e1a54005cdbeb9d87199a2f37a62afab3b95fbdd778afe30e1cf7cb5d1d609906cb67e5e1b31570e590effd217d58028f6cc275d66bcb81dd95405bb97b39aee3bab9ae032f67ebb51ce6c07bf1596f5c0e03d9e1b383ba243190f496cf9bc4eddc6bbadd6eb7dbed067e644b47270ca4edeeee1e2a3a4a32e263717777dfa406cadd407237cf4d3aed4d52e7b44eeff0babbfba6de806a501d2a0a43d5a93a7547dda93c3e40c01f928cd49cbe6581a14034c8130359737ffd6e716120a9102520e5fa93e33ef266bf83565c08bd2190f5a806f282480f88a4f526534a29ed9ddef9ba5bdd49f5d6fd35d9b7eeeeae3ab4a8bbbb4f96eeee66c9dddddd9d1754df414db65616087f64eed63ea4f50791cc3d4816c54ccf5507f58ed73ba6e7b53ccfab4949499ee7798db2b81af1cc16d79df33ccff33c4cd36d3dcff3bc3be660933baeb334b6ecacb59df7aeb4c9dfb5b957b678813c69dad2d41a36df53db5530094908ca627ea9834de9caa186b4529aab4dbeb98300593f08588aea5e398ee3385487ab913629e5388eeb6a7f60c312e8b95cf1f50cdf7aa93c4b27f8d34dbe7affe42ef225a5e924e5e9acd2e4aab29f2a4f8e2a5d5f95adf25ce1f3f6a750914cfeac0de19de4fe10acdc3f692e9cfa893aed04abde78f5738666f2d369a6cf9860929f70c96b4394b2e9b52150b2e943a8b209b70ec3b64a2729c1b3caefabd2bbaaec9e2ab9a3ca7bcb267c2acf9a0d9bfbb5214c727f08536e7c9e7ed2366b364a997c6d8853263fc4789a626260605a5a585856564c3316fb5faf566bb52249f1a366fda53f196f1acbb39660f97da55892abb2f56ad3a48248b8652e559e9d5334ba93f82473288ae2f77da6679052d384cff0f42188cf902cbbef04dd3f1ef25f0fb23b6dd1ddb12f74b6b3b393bddb23dae5ac16a95d52a9d4695a39756f66c6462b87e1695a39c41629c9beaceead30a52af7deef0d35c37fa4179e9ff775a99f5f267f92390c55a914189247d54c624acf550ecf570e437c76f768066bd2ad8b414863df09c8d316fde321bf1e642dcadced51e6ac91b100dd3d7c4fe4c17f37fd64325d71146fc35ce5dab4f6cfebd5247b9369099bb1b616c27b1a3b6f7a89d2d728c5b51279b27c5fea6c19c795ea278c68a39567681ccfcdcd4dd0cdcdcd4a9542911c582be8d5ea75b57661adb76c0241d479af3dd519ebb8fa5ac771acd54f33e4382e5471c2a7e97dd64a954291254a4b1fa5df48e928522ade1e863509113a6a5197918e2fea2252f2743c5d1c4de75c4a5474f9c8ef7b51938aa752a965fabed5485293beebbeca8595e3c0ae725e377a9ee7dd86983dec1df4bc12c7791c573f53eeb1e338ee350ed76896fbbed249536bed2a8ae3790304c1d7c0d37c19c4359acd366e0e5f036f329d4e9b876b34adecbd464366ef35ef3433364eb2775abd651ba9ece19996038ea08223a820d3d45cf36ea3cb211622d485dc9dfe5492ab4f4401696fb5e0aa15dededac8b43c5f2ed4c5c39486b84623ced868d1888d69780f74a998e21aa5b721668a6b35fa4a2916c3d2474dfa19f0bbd4a4b5fa4ecc15d76af55cbd8d2e574ca949dfa5dcafa30e7d9355958584d05a6badb5270d97ebe9b30543db8ab6d9e36d27c4a98617448d0322dbb778d3c5eef4daa933dd917b4d144baf953a8f0c3defb5eea56fd526cdfd5efb5e35ee1d4d97b9569b3ec8fe8702b26d5e7bbd5aadd58a14c5af769bb17e1e085faf566bb5224951fcbe3b049969f601b7229f5c3f33c497693622723ce0d6d241448e5c3f442bd36891cf0e1dd912095be0aa46ab2779e35a786bf6a70fd71ad74e5ea3a16a3493f74dafd14ace9d3c4dbd468e5fdd554bbd66c23510d76860edc3b4efdceb275cf330adf53eed542be1dac534d559af9930cdf4970bd74e702dc4b4f0de53b8464bbd74167e9f04d7566f5c2b9de6e15aa946f293d768dec59398467e856bdc09a63557a399bc755feb12ae71b572f7de3e5771ad7fab09d7c27f97fc5ed7e6556f0dc2eefb5aff36eefe3e1bad4c5d40f07b0dfc17a7a2a8d4a95b697335ab189a0100000000d31600203020100985a22049a23c917b0714801184a65c62489649e41888518810a3082100000000008c00489d0800210ee800a2b8be23a083d82da9808de0459848441bf284c38d49a73b9fc282d4698245050e8708c73a98e3ee157c357d99e877f6aa5af80e7262066836b99995059957a1e185bd1e7550c5e568b084d251f93ede1e878b432b959db8d637445d3d2dc2846d01369fe39304aef28a833d5b25d4af1b7762e69c6606b4a77996eaee5cbb908ff5a1bee58bc06ec28f80eeeffd08d7509f971efa1e699812c97447fd268086eb73a0f12c7ab061d59ca5791983f42937c6acdce6d41531c2a2fc7ab6654dc5d132d9d0a500bb429837bd309803c9721bc27d01c0303a5e88e30b97b86e8ec313feccd3aa95ef16901c831a451c415640f7f535c7f17429691d3db4727f538d5361cc1f1d1243ad4f99999bcc9f086917471e546554a18c4351c80fb2a036b4097d5de4932b7c0bd49075378df0eac12554811040939f0ec2afc7da57f0c08653d87433b776751760d9c5546d2d58bd289e16b953c40637361debdb55d8e03bb5705b56b99bbc37d66367c1b8b4464ccc48164d9ad0c8577c34c3e9c9be39732b2a4efd6edb7716bd8ff86e96f711c5699e049142ee9e635d73d56bb33397592bbb916e5f06a12bdc95a1bfd86d08dee12d83f5800d56f7e6e55e86a97a67ea9ff105a8500f3d91a8271578549f9e973150fbf0ae8591f203de0e8a7fe22a28f7800faa5d73e8f836c8e2672eef4059f3a0b5e34748175e8544767d33330bd060ef145b55de47b63c57d9c8abf0a34257c47c650f1151025d90e2e13455b2e949e557024b5bff496596f44470bb3273183c5b59b68882e7866a7216cfddb5cb36eabe5363214f3ecff8fe4073075cda2d214531db1e34f7f55b64881cf58a046df5e8c90aa0824aa02bbc9a136193e03b98cf2301411446e3291dcdb5c3f6110b795195aa854da6cfc61d5210b51636f9785dae34371e98e49b2efb603e939eaf57d034375d0f8b42ec22b62cdad58b5c8437e432852c94dad1a41b6d8350b79882649b0e451726d35119cb61c499f0bba7e6fa3ab6e047a01b8adac23b1863332e8e7074f3b19a1ba8e2f4503ab52189ca14b8ab726eb28f9aa81ef1d8be925d9920d73feeab2cbc7cbb56759f8246cc9a5b83fe20456498d46f7ecfefd62ef79361969fc27ff2e22ab5025698a34d26b3df53a0c52e469d6c3ca6cc288e5ba48d9ea7bc2a6dffe370a59ddad6ec9d06078b200fc7b5b1f5690ff106718d9a5f99a738979d998294cd6e7828d866f2960d8232fbcfb1b9996116958ccea5e29c1254c674f05fee1c3a62e32832b13904c4e3844bf841421022011672801e1b9aa61e0d3d1f8b7f7ab6a04db6fe8f175ed93aa79e3966179dc2d851b8e87b00e7b111d7d54a7fb9b09e98f03706678220aff8aa89e7366c0a167254dbece461ca43bec54c98caa051eb964e4d8f61057f808df2174b68ec35e18a17e2f2d2bea7317cc31a0b1069db9923f3e3320c9d46238aa7606cbacbe3634e9dff1e7c2685a65bc824ed0eaa5217fc99f576dc68a06f2b78bfe31b011917795d4751b9f941d53f0c9344a05a7f34ce51043dfd5aa0a588a59dfa6922fed8131b5eec2e7f5bb2f99664610c84fbbab8da2f8a36ae81988598e088125294ef1bf2a2344d6d605f9b275e3f570010ed02c4520aec0c75bbcceec8a4acea93ebfe1c9dcb7e30651d2fdb566496750018c5cd30432f0de1b66f9c8aeb408e5e01c7245b3b8afb0ff1b19d3c8fed59f71a174c9a3dacd17f58a16369d6bafa4be467dbdd936ca0ecabe3c4b18ad18289a28c13540941a3cab9433deab7e5765ca5c9e97cf003b833d13ac7a892086c22db583fbf21dad8e49a9f00e5ba72ebe6021639e73b1fd4792f99827536518e65689a982b85d29c65d9d9d943ce6ee9f5bb3bbfdc1392803721c465a61a257d3546ebe56b85894a53c6e6d3193ea70842b0e240b8fb4c3d51154b6519b94baf4c3e9d065b104917c18e237048690e54f22fce712ae0ef5b710b0d0838580e1c7710a7e7eec0ad56679879021d7e3583185c4f1fce2cb61090e217c8b946bb0d7ce5f70d070fa5a8b0d2f59442d7336d4f0396b70b272f97c211829e5c35a39815e70365a7d6fc58f2502dcd8cbc1c78d5b83cbed89328328a7a08865ef2173c76f1999b641eab77a6a7b6fb86784703264fff2d2272e1af5ac31bd1716b7cec5800266f0b988833a3eb60c95e2c909f94a54b66857f4ff9a6302b97b46dd9ae7e38feccf9d747620854cbbd2e2242a17fdcdfdd4ff6ea1b6b9cee1dd453eef62981fecc64566f7a3f06cb3680132924d0d512baafbd88943ad44443828ffe63cdc7c437baca202e4241d745fc297dcb0fb6b80940e372cf5b0367f4ae3bd0c075aec9499aa7ed36cdbdff38c2a74333423080718dafcfdd93c168e0600789c5ab0d4819554c4754efa23765fb15f66b1c6432414c3fd04b8e769fca0f421435e8bc51abe6b5df290ba5b909382a02b331a95c3a0469f8f94fd5ed009b5e7ae3e41445712c5af24de00c5a30775703e8344d00301395fbd20de098c976502965cb6ec04183d04a98140148ac23b580e2c43c8f7a986fc5a5913cc2c878ba98978550dd60a05d999801f6d3bb1534b053953c60e08caa5ec44f025f83110f880bf625f968769599b4b018f73470e56e63c5a65bc161984b45662fbf8485ded3428701e9d6bde0aec9d0dfd4f99f74cff6762091e5703894047ac939d5a69a6ee9eeef3320bf02f7302b0b037407df73729f037f7ce74c27263927152b8df98d495ab06f32b261dd1cd5e19f8b22461c18da87b07440748a9ced5442ec2e66a84ba2e318aa1bcc9b0cc9007b8e3739e720c05a315c22746d02948f0f0255f473c1d648f375bffa4d3c6f36f2cb43439c0581f527578dbc26f1dc1f530710c305714532095755a479230a9e2988e294161c716fca16f3e0dfca708c2f399435602b8df9947a73e938bd88351a874f14fb2b42c745156757bf5de5e524bcb0a21e83695ed08f91e08172fe45026060f4c1a7d8e11e16cacb4414afa5cd90fd6f54907ffd04861139f8b24546e2a8abb9c94af59db610c1be641f879ac3b8ce1e7b464b559e408a332135506457c8b3610a9fd523717a470ec5627ffb82aec65190b54ad67f78573243ae10408b2487635a1092e1617352d714bee330e9d3e8276e7c39e81bed6d7662a7e058452b6a6b2c752f03b276d615948350eaeab5fa2e8e0545b44c6503876416408412db6d22ca60164f2c8246a2df907e91fc36f7ce52f17faf6290f91cb0e49633ec9165c04cafa2c9fe600187347c9fe5f42389e4dc790608fd17a5a62872935d3b94e3e8c39e6d9b2b5a09a533c242563458aa1c4017dadadf96e5bf6a78c987ba539f3f2e5038cd53440350cad335f140f653bbf8a7c1451c8a5e44deb2d94f7d0f89ae154518cab914cce4228ec38c8992774905313a6140f97612d79d1bd2a805db348cf720bfabbaf5fe09137dccb035f55ad36f4a03a81b6eed26ae893441f5065a68b05de3574b9e5d5e2655c993be0bb505a1ce35977d508294b8db14543ac1026434d53c9513a176231c13bd8bf2743f7ad22d96c9db2745dd2342eaf72b8d5ac1a63230b430c4d407193b4fd7738d294dc0f188c65c03722437b76daaf52f7a212260ec9f218c1cfa6ce0db8a8fb7f367794634c270000959051dbfa65723d9ca8f26213898f6025d48ef2cabaeeeeedfe2ddcf2f93a1adbfcea277bf7d8ab46121c15df5764162e22a354e03d35631c5818a580757b015363722ddcc23497dfa979ae6588af56356fc4546a5af5620909eeaa7b07af1482215507855fa826e33f34c5bd5f4544d2428368a9a8b0425390772e54170e97eb32610202425401396b9fd3205279bd98dcee7345bf730515fa2fcea22afce56d55c8ef5f9f40b7a70c987cde64103528b02583fef13cd934374e0de476fca914be6446805ebfc928e2aa030d8c8dcb11a6e88a4eab91a6931c5900ab1d61bc76e20714d4abdd1e37cae8798fcb7b5b4bab6445c1bbcb689e1791c5c4c1005c482e44ffaf16c6351def7cf331c483c83ba805a4321e3adf80a4c3463400b244b31216d8ac26572c8afbd3845250c4e9df37b4b60f2c7976a8f0a62a3994bab78ce486a64b3915d72a265ba92e995d6c40bc6cb99a8b62af72dfc2b31914a18b4d6ec5e3978142587e0615841c01749356618b6231aeeb4ead9592977db7b1af51425619af71dc8c576336dee9f0bb446d40c2b7379b601edc77d8cc7fc86eca34ee86211dd5d0070077a74714308c5eb14869b3f0f94c0904f429a532d85b916e34a074a82526ad31a7d91bdeb3cc7b7daea36708c26daab1bbc2896e9e3325f06e03699a35b641cdd900574ab8cd06d32003765c50853770f27ebe81699378979b2846e947943cc9561e0b6ec829b32edd0e6c9b439cd23d3e7305fa69dd33c1980db650edc9671705316a0db326d16f36504dc2ed3c69ae3650108372fe3a2c5bbc91b3d5182235ffa5ba8df759330e9ce229f3ad0a20c88fb1aa6450c26a8432fcf66cb4dcaa06940a2d0a773bf6be4b6139e95e59a06cbff5d5b23b7a2205ffd1b1fc09c031cf063212a45d1ec54947fb266862c63c63f21535917c0759a286e8a047342854b8b2c5362e546a58b7814dddba2104ff04ba52657d9fac5f4f2f1204ee7a25ab433ab217f1bc6dfb14ba485c1eb99b1b3587c47e76f7b87e66c2dea6bcecefec9ff8068d1785a57269cbab9a6a725a245892695a4b6c1781887e64ccbe96f9cdcbf49ffd67bee0cf9e18fa6a17be051ed2bdd1fd1f6e5cfe83a22e8fc25b872166e93627727d7b4950bdaf473e97129e7e95f78829e832f9d43b828f463d2b96eeb6cfe2e267df6950fc0972a8d2c9818ab42cf867462c417d483e306ef58165e12627383e030e0174305865c0b9474e01bab0aa4967bea90837fa35678a6c51f385cd7d5463df9cdb39a7b22b468364a7926c4a062d1d0ed4a1568f191185ffc6843ad7880cb5c16c7758d5524fdc7a71ad781ba6ca517bfe92711cefe7de9aaedce17316eed61ae5e41ba36726484c81d45b94f6004ad1050796e93e6fa89051c2001b0ef275da63fce4b29ca03fb3ca335bc304d2370682f29b4d89d28ddabb165fd4fb6ac0dd14f5cfc1db710161e5226bbf2ecc3061ea0518029f7327739d081deaad20b9fb07666d68cd73a52b1d5527961201ec528681bb7d8a92df98d6855d89274c7378dff2f5ef8ebe4d9db1156f8ebd94af835af750ff95701ab7a5c6e4f76b4f05092b2b2059ff0b2d685df17ae62641ff1508cc63fca62ada99fdd06a04db0a7d22e01f69ca884c2309115c389d393745554784639f9c0221377b71a112c66d407516ea45d21e11ecf4775592b127e60ce9eab03d379b5b794204fee4b5f3d4a3e1e7ea787b024e5b49831f4005d676a05ba58b6e80178ddcb972e9ca7120df8ecbd58907daf457364569236782990ff0e6eb1684e14834cc3764457913c30057a9bb927fa1192f3c4d738c777d6282c5d246a10c0048711db906049e366bded454efc3da48efbf2b0ade07b87120e29803cc18860620488742e1a8e9e7cd1818594d82fc9af076b3c896504e23a7e8ee2d04d5c5b7a9d41f5056835f00f72b000eac3df81950b236b12642a6bbe624425460431e9b4cdd694626c1de368972e534085bb7b39886ec75dd15f352ba49b6f3864bc8efbb082fc99a350633d2009e9b61389285a00b14de145a2eaa3d878563819d1e97a18c5f0ed5fe18cf48b4a1a6c622061b646ce842418cf41f3a15373e152f2df8a687546a74bab6cc91153f08cc20c42ee2e968e29b29984b26069e6da268824d828647c09dee4ed70b9529d44427b11af0789f18cbeb6f7f6b5af42e02a777a4104ad897e66b302b68868b5e91bef392c4380c9df32d11e4371e9a93e83727c563b48fbb130caf208d075cd0a11e39c3d5487137f7b8a76cb0c62a2bc5132f9d7a44b6a85561451ee8ab2937c761a369d83cc5b0877ac10ed3ea55484d11665e7789048444da25ef3ffacb943b03682a44c4476eef7647da50d3c868243a6f5fb61df2a4a37aae021de09c2b339c787364aefb9b1baf36306b50e54cf23371a6669405eecf7e4de1cf73b38d50a470660e785d4d616e7e70095c954fce5c71bd36edd944faf0049ef8eb144cc9f0cb7732e0646ac71c2cbd4b97e47bec79f3f0866c6eb481a005f9a2badca7f0a751c4a7c9c07569b720133f4b6178d2b2cf8fb555c4bd13ca9b572081473e056513671d1ad1efe40cc8bdecc7658b6f61589d510452716c08c2d0d75308bb7d19dd5460cbee44edd63b54ed543c1b9b9c69b1f395f0501b38ab3994ac567452c7fbe62f9ccd985aa5c4b2527a644d0ee93009748ebecc9f1c412954802b3626359361329aec9f561d3d1143644643cb14347452e6db6763188f15eb16c1855ed8bef2874ce5beaaebcaab4568bbf1d91c869bed4093727d00d1006cf1b9badc69701917a7d93a11fc0c59b1134cb44c4d508773677abcc12de2c7917ef5cef85d01664c10bb025c51f1cc6c0d3981f8058dd5cb3b3f26559a7445f3871093d17afd4c45f0bcb0f8a773297fbdb3a88c25b04e2f14a60368ff7a363ce6fd4094ba25d499e3eee788043644a900b5896cc70830b7cdb08187884b6672a4b841302d19b88b3f8a5d043a3ef3e3fc4131e6b2e91480c25a6f126c800d0ea6bab8cc03be7923ee482ff8706e32d88b32dc7e8232f249772a330154e22fb39d39f1a798e6e133aaeaaa2f4667ece5e150ccf130a48b50d53c0110dea1468b6a859adce1129770525db4dc4a5dd72ed6fb52a08561ab2de8ca581ce4f434decab0ffe9856091717702fc01cd7ab27b127d04bab570abf82d375946a5aa0ff72035e3daea8ab7c5ca9b7f2f8507ff1d1a13ef9715a5df2b176a8efa5010d84e5d7dcc95d4cd0979e9f26ca2f3790bfab7b6cc60dd04f3b262d90cc878a63c629218010f43f9e421f54debee9d043075203b680ba0677bb9549d60057e11e951faa282bb151dd91aeb8c956859626e145d111047404d8d93b70433c161cfe44c4f4fc55b0903c49f9f54bf9cd3e0147ca694bf386050646c5f64006be1aae7e64cca0cca709ad44346a2e4b2898ff4b31592fe1d782c19f17f7618525c92d3a6cf8f47a9baca2c672f918211586320198637265c4f906470c22bb06b1e4a2040c2b256385a10866d6a2c439da9cf3c34f7e7772ec8893596001388d3f5f838845813f2a60403db0928b72b2708d09b2a3b3757148875a1e1bf8cb5248c4e85c738a337c407a49fbaab1d329f7f288b8732ba6fab970bba2565b920ab61e82531408e23814db262cb289911beff6a23bee88d6e87f86c97157259171ef71885f318e63664120e8cdb19d8fe57c11065a3513acd22b76b3865bf0086ae72c439c48fed85358c8ed533f8d396977594a417dfc54a2e49cc8c72ff5f4472d8ea8ebef65ff8a1028fd21d701661af2b32bdf67046560f1809b7e776b4dee45b0d28c5519943c78d117f7758e8189395931a61d9dfe92bc6c4077899f258d1492e5f9c6cfec348826afe95eff0310db2d65ee90bb180a2d71c9543c9266e3ca4775f2b8bf1ff6dda5323757055307c1ba053f9b35e4052845abd20bad138bf25db1732e2133156b02bbc4110fb9db3aa7ea6c348f7b7201f34d4dd37e5d27c773e283d3b5caf4b75a29e4ac2f992aeaf1dabf2373d8f75481b82fc765027a98b7e39931bc51ff4cce6d7dbce14f063beaf1724fdace5fbf72822d45efa7d8f59283efe5e8e7ce24737eed36a5b93989cc22bad4872b44556e9eadbea5737691b1af85f7a278760a5a71876c789673d7fd9f11db6c506c8dc2fe1fc819f81c91b15870df45cf283b4d4634f6314db245dd1edc347948434d0210a911c3776841894c407314628729fdc8649a0286e60dceead45e8a31772f2781d8215885cde12624c05178ee3660a99f3cb3508318392022e86892bb81fbccea5081c1d939b8cb1c6ac423c515223dfb31699b50cadb7fd8a6b0c24aa9085b0090aac55fe057f2b883606ffd7a3e7365931e79f551d4c08cd3d66b65a99aab92d40c468ef5d950d525f30a64d9b169375c5a717bfd2324652be4b8aa905ff60531150c45003627c9fcede2c0918942cf6276dfe74c107c7aaba7490bebeab7300b0a8c4098d50f95c5feaa27f2de312e1ff637ed7c35c5cc4af59e984cbb51523b76cafed1f27d1fefdde1a8a0e303c1d67a47487c8c61eff77aee305383bcb20851e080a693f0e21464ec244e3e47168b9340f1a0e1e17ff1c8448cecb05fcdd24970b70973cafec58832dda58e926903a5c7e9ab92d15862f28f58c6c98459c6facb7be0b68c085b991eb73e0b8c5598acc784166e4085b3bceb0ca59c4961803cd1ad7d570fbb81de690715753a5fc488ae2f2654e185ea78e85f084c5549ed6b47ae842b61c5e3ed6ecbd22cc75b9906eb5ad1860ddf794ec6bd315d19246b60cea4912edfd50914ac3fd45f23af88ae45036ad7ad051f0a466d90a7f5d064d9e0a4c9b2e168930537c05192676a40a946fc6376fa867f4807503961286b7d043488b4de9a871e77929cf29573f41a0ad2dfb95165d1d2bf035e33dd340ea3e4fadff152045d12243afd8f3fefbfc95dbe8f8646ae3ce8fd7aefd00f77069638d4673f55a42b431c0bcf33820f92a01d5eedb7c34fb80ab8674b6ebcba7fd89dd181a4e33333b55d2967756018321a364367ad912791dd9a1bcbe2e02b8428aba9f7a26967d7b8838be125162d182d6edbc7a5e82da4c88d1b0f6446755368ed0b150556bf5b18954a15a3654e57d596e32e06b1508bf4cc918abdd386c64ef438b8229cccac435e36558cf3c814b52b2ce1d92924ec7fe34d50796f64b982904864b7c5aa505f8c183e79ffd20b974d3379cc968cce25490b9d350e287036384a4ce173d938097e799c4d51a38649f0074b299b30ed1c4b4e7fb010a1b5b4faf7a1f1819b890d57525073b1572ba8eb2fc2f437d04dad46fd6d9f7891dc725c569c788ca5953a7299e829becc007ca6e0c206931df025fc016d448810263b7bfe1e2d3c8efee009b12730c71d310e0e302efab6b07ecd306f99db72faec1428da0be0fc25760d844f911b25dcfd0e521a1030712fea49726a1086bb54ce33748be9fc25eeea2c44d80f9e66ac9bc0f9a9bcdb161cd096b858d02ba093fff14ea006eaca3be8fe77b089e86864569c628fb793980c01aa354d90f0ff17a388c6fd1c929f184554974a641c24d668c9ac39a0f9395e161d020c9ba447a5d751108423526e82480ce306d9a108dcd3772c08fa0b1ca361876a15571135dd0197d88f30a9d67417a8b76bec46391b37b6340528933569523f253bff81bddb44c22a239c0d2770728d8d81e90be187dedb57b8c6e03d0f3f467135aa9f0939d87c8da00273f2333ed4f4286554aab029fcf71fc95c7432143ef0dc5d9ba1b588500a8cea1bce887be1de50a8a192a6d515e796d5ff67389e716f3dc9b21c15bc0ac6b50c1e48891e42f12344940f67014311b10a8620f9703a21ca76c7c45dd160994842972d3eeeb724ef6abeb9212b12179032be6368dfc4503df8c65209e314c01743bdefd1d6ff4f6e160ba1bb4acd0a761c2d007d99d0c844bc9f0842066908834d056fc07cd74f2847ff71c205220c35653b2840c16b311131a986d329eac1a4fb2a87825510d145774f6379b4bac46955d589ba427a1c75e399189dc5476ea10e68c1252226ce04b03e69350c9b141951f74010f07bb5580f846aac8e6cb0d28c2d99ec55d18fc424fb2144832366816d7a1a398f90fd2c92336220fcb419ec0d90cc2d4925c6025699c60623e654766cb81b8bcac3f2d01bb269ee91b681e01605ce6897f597c4c5b7c445327f3a8d8a298d5493bf0cb4a3eb5289760b452f0e424c326b9d028927f6cc8df6cbf806d4d1fa1cf639652844d5d9265f219d19b6b3421f3adccb9bdd763d50cfd57859c0d3d3c32f333c49626f18557840bf5e88dfb3416bd719d67cdfabf67f4df7e028225f2d988f6f1c2a86699b93d1baf8aa5569eb22f12507656feaedada993203477af6b18b254559e198af621b1341aba440fed15c5a8339229937afadffa74378c68c86d0a91944ce6c569329330194392151280846f550df251de1c90471b3341a555ca5148a6b550be2fa55eeaf141fdc5a3a3fa257cfc611f221549207fc09075a5f878c25c9f82114e6266c198c30efb7248e0419b4cd5ef2075438c89780103403d01d79c025387bb63ad5621cb8302253198a9f05804a036ad66b09385c837ddbc54996811df791441377bb857c06a17579004f3a4c6e263151c08939e91d04d93f45d308b8c606d64d617fb92081e76eb1c3ee156937319ea983a2f8724100a2735bbc251f009813d45c52622a7e67a4624ad18b2b2d3905d764c46c9104b43d87649411eb3e5816d8973fa0df6dac1ad41695342ab5c61746ec3afeed438018657059e99d54c459a2bb309526342ea7359aca3edca85ef7d078a66c662eca81b34d694718478825a8ba3dbc30e512b38ad92f9804701927a6794befb80016f1de3674243a31c34fa2a7c64b431cf7627351c690d582493d3f590530fcff6f1cec51d5b224c38510ea1fd9b424a7b77b93640769aa5d40ba8f2e1df2ca5aa192eb50d6cfe503821435c126e8da7736045e5476c420a940cfd70ab8f59b03949365f0cd1b94765fea401570643ab2382931fc62a1c111322c856254cf3a76d26b8463b42d356e15eba8dce7662602bc3e5d698cd6d70800aa5c93b924222527c0cb7542fd8383f11d43bc7154d955b05eea062357ed218581b1ebb43f36dbff234f6e98cd9c856838919c5360785fa0acec9c4c9299ed00e659d88ef0c38e536e51dc104b50726e99c1a5830d1ce285768c7485ee3cbc98e637688e87a52bfe1f66f27ccc4b485f0d89e0ee84eb5792a15a0de2e6b51526b22fdd97018b6cdf35030aad2d9263eb4ccf8037da12ba2ec5d517063c11f7c3cc120d929b1ca539085231e9cf4f82b3423879bb8b232ee31e5874ac10476837c2f4b1d295db2ac75f2a7a5dd9ae6a3a6c1975f18b99478e0850f557b1346cadeadf3c0392745b5e45c0436d4de083cf9148ae5b541058cf448e69c3c3f7acf95d576b3a5c7746513ca06b040ad9d4d0b90cadf2f8607de8415e314b643c2e6159db1eb74e4d7842438f3272e3fceeed52b632596896d3e8dd64fb9fecc8bfddefcb7906586c0af727c5f4ee2db0f1ce21f46a735903bc11991ede29cbe04921544275365d13995517936cb3524b9d4298011e1014504ef95b44b82a2eb26d9f49c20a252a3d03e6406cd259e95b45d9bd4ad5f6068daf0cab91ffd2b8930e884d255ec4154ba09ac26407aa92e8d0751c724db72654b66739142b99742df34a5850b5da1ceedc9a0eae50ed35061d018406c34ca0b983cec552be21d4388fc1790e935399d106c0b35f572eae870233d54002990e5a05c01f47954127dc2b713f79e067752780166834c76c58e428f21760b29820641d4237a4de9d9660a170aa3daf92569f9f2fd35ec046cd829b0619b60c3fe60c3563c07a344cdeccf75d6e96aa9442cbb8081e404d887971fcdb44ba46f4d0f66ec77934b95ccc91c43f6557b29d54ae3ea95915639a2803d935c7ea918b89a6e0cbacb6730b90c1386cfcb16e9642d052f3e4054e46a1bae0bb2fc737af05cd42d1aa37fee9bb19035c6ef2d5a383dc8a2ed8b2566628854e2e7492989b66274f44e4ac8c67508b316b58abdcfa29c4d2a97aa4afa2b55498ba04e8c39c1a912c151ccc9e6a2ee6a4c90bb0dac6318c3a04387a8d49b12af27d7fbab5a56ef83ebc659e739774a548b5bf4cc793f2b019db0bc00c37828ee44e28c263814db9f1bd80ba427ffc810aaba4422fc389f36a37e8f17155e9331e661ca8af1359298f6071e46f4f5c284a5441888a482a9905716517ccc10a566c747b3b906115b52d34d3b8ae9d0a130c879cc1189b043c56f294e8ab1c1895856706cfb1344c7883bafac1bea4a5307f0a9cc514c50b0e417455f7839d269a2e1ee592b998260de2d57677957f71e54fa83785574a9e104cc6b3d434c11fe2913fda364b05a0eefe90c3235bd0525651dd727ce5d3e93babe699b56972025d48f02a624ae550da707d51ad9093338bf3c6480cdc285bc087186a15767237561a473be2a92e0fbd0d4038c36855e215eb40d8e42c2c79ce4d08b9f94cd711e3ec203490f3ea6f4224b5762591e95acc13f4a3b1663d4662610f7257cc3dca99297a77bb985ae4f3c1d4af6bc583cc1de3d656b3c41b65c896fc27102fd34c25be0f7d52e14935a31f8a458f81d832f9592d339ba29adb92ed12365c176197f98e2518ad168c4b6186a5eac66e67120bb4d5052a67f2fe5ca123a4327aaaa56b001b243c84adc2d613326f6be7e588893fc61b4d4f09d510cfcf6b86431db56666c316b4b676d3523cb59623d2bcb59b5382bab5937527ae71d3f16747e515af1e863686f46e64806fb40a58b56b2d2eb6ef997179633f31fa1cf082d6786d829e1bd80c637fedc2846d4867c24a6d6464e16215b636e0e31724179dd82380280e2ac98762b2a093686efa5299e4792235a3c66cf77ba76856f04176df51bb1b5511fc370edaad7006e7de537827da33cc6e8de15d708dedaea199e33266b98e62f10c69a1c84e8d006789c4e42199ce9580ae4696a878defa54624efdafb3bb697270227c5dcca43decc9e3e886c1141b215ac7680e94de110642bd5c3cec218b8bc02a37318dbb6991a33ab7b554e2582e92c9ea942b0f91b646accacee5841ce7d57bc90ad52720a42a8cacffeacaaf1bcadb1d4deb4c0b92cb5aaa3b4eab900893069763c2e4d6385d5f97e70a833170e2890109ea91a60a4cb4e2bc03f768fe7d09f9953b3eb3cbd65d177620267a145872481338fc0798ce0fb2302370f85d85b3abcc03c204220e5104559f92c2e463524c464a84926128ea5332b1636c03c7280e6d11f827e489b1b7228182efa9127a74779833df8605644fb3198d6020a56dc4a8abccd0a72ac099641ee77c1522942a28e3430a9832637969c03b6672007ba61e4f8c6c38d0117314b80af8e3f348edcacb053d4635f98aef66402499709586b264026d781b631185733d359db800b6ceb7b7c6ac04b58190dcca1c79c817186088129d77f706b751f0bdaf840a846df500b42087a74f01d7a19caa4953ff9fccd2f5ff9f5cda7dff9f4c1e72f7cfdc5ef8f7c8ec630adc695b624a619a0e483b69c6fcb596e39a85b763937fa6735d2fda3c3c76d6b676c352bcb99623923cb196a3d23ab195b336bab195b9dedc618901be83de245e909461f881cd4d6ff0676892c666dc9acad666c7196ad6665392b1667653da316676435fbc6e8b552cdfd8b6514469fe8ac79ad44c2ff21ecb9653dabd6b36431632bb3b6326bab33b63623cb19b19e29ebd97da31a9f42f699a7131bff3cf48ef798b93eef06368ee0f4b4c517514021687d4b17a847dfb9869ef0e11c66e9536b2521a15126e0013e284a689fc97c4e47bbcbface8aef8bce53bd586d1df28b33e0822659c63ebc43f47f2c9381ae6f56565d2d7d8b4339df8db21f22d8180220d9b5fb54994cb4af37fc27184c933c0d73e952b22a86d103209ecc3e05bf9261c671015d37e3308c07e82a2387e37820ae1a701e55bceaec3695b156a4068253d018ec16a05659a867f51f1070a0aa6db74810316ec1077d99b3f981f4caebff26f876f06073c1b2dc11c37deffedc2cf68ae73463f8aad8f7c079e34dec09f5c6dbd02be88db7414fd44fe546c7ccddcdcb1bfd1e79135a24181096f6665746c4c45768576e3827fe3b9647f6dff9d8372ead015c1200b948990db70725b670cc5d644f1520fcc71251567401216cb1f71e10abf60782a02093022945d07cf7e6c922b26ccc0521a16e644341aab43372d7911bed5977ac0691880872d3029ebaed9e88f9441f18036b71708b87142ca4e3c02d1656b0808e05b758a0090c6c6b77a9adb02b3d771773e8a971ed8d55174ad157c9f9661fc307af9bc7dd05bc4bf42ee8bbfc7759c00b042f115e787839f1a28a3f74c5479cd80cc36be21eed988aaac88a01165bbc580cc12be21dbf15530f604c7a97715e284c6e8c1a68543f4f46d2d941399b046793e26c529d4d0a9d9543d29ca939e049d69b0cf48bd22b9b1b7baa62155d96ddc65602dd3f8a19fa8179f2b6ec957ae24dd01bf50cb7b137d46337b084ea78ba6925e85c24418d5f3cc2cae46681a9b9a5f8f77460fa62d0a96a6fa7244bd4301ea833a551e04806841d00398628b77362b7b43e02f7dee85b8c2061921630d780c42d48f3007561cf77ccb47272ff555e5436c78c52a1810abbe5a4dfef57eaa420c620452c50a75efab36647b28976b4315101a8c7946bf3322ef626bd646f0792634cd1b428ad0ff1d8d4f64149f31c5b8250dcdf9e168036cf00dd11f7a52bef43d5ef3bd6c0056af2ebf165f8bdb8f8b95fe4d764cb4fc3a91503c299b97a6bbfea8272be50fd81ccc5e5a33beab82384323a807a3ea30fc356df69cfe456c16cd03219455e956881be8ae881921bf31988154c38f10f72caded39fcc20121c87ea1abd7124cba754c3ab6ec016025cc301191f151fb6eb6e53a0fa20118f40fe2cdfed66fa07ce5de3148f4474d4da11a6d17738456d0031cd576552fff8a109b3369ab287c37b00855eec3bf682810056e9e760dca57be7216561675cee2b2d6ec0027160ce6e436be691fddf9cde2209372f1a1131c09260ac37620ec6bb4cade59945131a013b4aa94ea180f68270687f1f28d4dc0a0ad85e080d9daf0385a65ba5800d606d31e9aff623fe208d06d979a629959cb7109fd2502e499abc7e8f14943f9eec914168f247fc90898d157744324b1f3cb860c660588d8cd4951b60c64f746a48ae4bb81313f099d1228b222db368ffcac792f951ed3a17bd701b7a877ae36de8097ae3ada037ea933bfd334044fa8ba175e1aa58db5cf9e84c1ca33fa28cecb2acd15df91ecf1e007ca419b3c34aa5703a27c4ceabb1c31bcc04e515585e0603db33e2906fd9e7d01facd193dba1f4169c05a3fd000826ee84ebc93d5a1643e9a6f23757a6835be0b2e777cd7015a27b892338a853520b710da74929e4b09de042f8d776c25461bee59c29853cdb58d30194c0916728bcaa35d38b88dc07bb4a961b5fbdd60286749576be0aae92f840ee093b0ca3ccc67343c8f0d4b38d54a5d110ae3f6637f88dad2f00210f209ff4d2c4925c5b54b96aabe3de528bb5419d5bab3aee0df5581b54b935abe3de50c5b645450db5237909346e020dcb8a58ccbadcc23e9259d9ba20485f439093846e76d53b4f11dcd478e216b3a320554878e418023673135c5a751808b4e6e05ff33cfd9f6a63688718252c8e1e2447c23d4e6315e6285506b16ae46961053e0bae90268125ce85aaa89e8f8fda6846ee9e6844f0b5eafb8137d9c6f4c4a6818795d758d1dd3c5e3fbf820d768b42f9d34188b16b7c090e21a9bac607d40d0bc7e7d66d036902627ad8d18d79edf94c8a482643460074d13140fc1e01b012877d66f9844637ce190e04b40b7422557f4555eb8e28b29e194debbba349fd45762429005db048082485f4386b35f9c29fe0b4c83c1f74998e5d8ab63f00379c8a5e1e318a54b0a268c3444b6a6d9d46dd7bea9005686508de71a3c9be8fe2f269e13c1a9611ac9c5de435f9c84ea2f296dd1ccff2ed0e7d03963b39eedb9d6b59f11a1bc4c1c681276f161cdb64f1de4e5e851f6f7ff718531e152d2fedb286716129fa56c0bb1f986cd06f005d53c34cbb9999f1140967461c26211982549a9c8c15a648c2b1bb9eccdfa6d5f0d5766dbbb5d9edecf666bb3bdab84d1c5c23284f9a915a8e499f102f1f48b686bfa35ae7f528d600ed6f2f429ef802fd646fb4cb1537d2e995d31346af1cbdecf4d8e89c8e9e6b6836004f061df88d7569d01f25f08706f74ad6cca3f7f44e8cc1a6be61b92b701af9005c8816fc2944c854d9e3c89ee293813914fc8f408e2d9b53c2a820078ecc480861851abc22dd2f327c51c46330c80885f3f3094b127c1fb93499d26311d822db2202d6b937573b710b4bf86d64f8c90006608d49dbd3c9bd9e1145d43ba348dd4354a9738d6a588bcce3fcec1e47f463311dc71e4dd13902f7cdcd243e9ccef8f016f2e160813d4a5a55b9b048d2d17fe1c4d3291c0e0efd934d78c7c63384a907ef47f711e6cd5add8e299048d8916e8f3f71af4132b56dc3eeac0a6efa99b057e79541af6de8ee7e44f7f9826ce6e547fe11c381648349acd079e097d5cdce495400aac632ba761bb37e6867be62d45ab60cd69365667afbf037db75ea2036c7280924517e6b459dffe2d260b7272d7165312ef3d09dba9d0c53d4d0a91af0ca5681a45a71dafa92d0831075a6b856bcc4f22df0fd89390287bef60c90004afc2f77c4d10b705c46cc54b02961fcd9e010d78eb64f354103e21c38c93c7969d7a262ccb6c952b12ddd8e814ea6940aaa0b738dd5d215e1d26d1eb1dbc32a5cb8cf62ccad5881dbb01f150f0ff57299f6318c8f59ddb57e58ca14a46442c507bf0b70bd1fffadf5e07be78fdd352c6fc0f70a241280b72280afa863deb8d5f2c95756f5e939dedc91a1f7abe9910cdfc30ad4fa659e2fb219818c0102bcd03a3fd4c15d3b78232c5adb5a3c92d254a3409014885702790c632ee7c4ef6a7d58ecbdd19918418025d72e00804952c6109e800c095fe6c9ce0dba111d35e5b51e7728aca4293c549df9c11376a81c8fab4cff22fc71f22abc2121a729d1b17a40b211f516bde41d80a5a89472273a0032511d2e9137e3511ba3fe0cb7324aba330ed118f266de6a63445c50330e49be061ef024030d7fbb172e5a83c06f8371af48b683c4ebd06a91790f61157202e2c9da590ccff3527b004292ff126185337187d8602bbc8db55c914a91ac3c9bdf093908294bb89f301d627f9f15cd9205995f8f1440a2be2cb7829b70b5732a3ec9cc5d33c8e0b415dbe8c11b3f4049e04d5177c341ab3bccf2006744f27e54731d16f00c678d559c1d75378b024eaac2d6b05ca78ceb287c9de04b2a7edb37409ac9f23b7723aec34878a43986296f344cc8bfc3d47fd13031e68549356f8d3d05a198a18845f92c8cc70f00e229f0e76d4cea72fe79c9247868ede01d2021635a124f282a5e3cf9e281cb708aa0f1fb054320f91370260995aa0186f681ea02e114235f4a38f5145a6cf323f70529ce61c521fef6d55f87020ea9c97d76a8719043cdfd618101c526473d6fce2364f8928db4a56f3039f2b98317157a73d0de27737f4bec63c67c275709308e2b352cc40c235e90d7f069e8f9959c97b2db87b757225f1fa56952eba766246a8d455f470f434fe3e4a28ad78297c690e4be8bb47e32505212e0c13315c9c0235cb0b9196e9f70e292e899f728dd570302027adc7dbaee0ff7d82632b737b787c7d24fecb9d1c2a3e7b247ffa9ff40cfd0d9c37a04fbf91b83fbc2f871ff7a8e088df716375946ed0df5f7433f0182391b28a4a776c9f50bec42b7942d36b743ae19c95b4831ac626fddc364709fdba8d2d2dfd1b886f599fc16e75efea42be5d0bd6f4d4b9b73b99292af583f33366180ad4fe5510ed3baf1f5523c08caf1090ec25497b33fe6f88316d05006af6e295363bfa5d21d2a73416b73a7506f995af90ef1b0b95218d7e0a29a577dccd32e2aff30be1ca54a6457634cd15bd0483fb8ffc5e265b6e7e234dfbaaa4395e08ded05c61add67937e075d0b5f155b3dea8db7624fd41bdc78adc2e8654f9bd3371f90f177f2d5b1c47dd74ed2d106001ffb07465d57d3cf72e65703c0baf0272eae278074e05fdc5d0e0475829f4ab9f0e92981f67752d73976cd96824d7851168ddaf988dc107a2002c96a9f843768d99e721dc304f6341bb1a04862a39a88cdb111cbb8c1a0b5389b38d483092e1802cbd168236a7c202a1e1691784402f0c1e58a08d5d3b81cdf10f324e8ba3307500ec90519b820d26d9652875772de4793b319e6f483190b1de00b3c89eb50f1e73ce43a5374fef24ef5749773bf51e513adb64bbd460519d1d076d425e340a70ddab125bd6d7d3cdb0f7a397a92525eb0e014da9c54ce9960acda1c6e4aca7344f3501add7d925c397c1ec02e418501bd6a0a6bea4fca58d06a68248b98892bc1fb2ad9480890e4269e611d8ad21fb757ebe9679c26df7d7698749bdd2af14e60b3eb0da28acaf947d977052bc4c274a7f29b33591b21e6fcbcb3aca827483d83b0698d58f39cf29d33768de812cb6cf318f6aa4aed4ef4d8677afc1893bbfbec17d91e21f307b30e67234f453abcb8e26d744e31a3afa86ba95d88dbddd49269f185bdfe47af9259a78c84af908636d989f79a9efa0a2b63a1609b8f840f909497d54a6e3a5bbb09929b188aab86a2140a1ace4a51209a40ab8f1b5ad6cf951a97cb27d187824cefd4089f5d6a87b53489154c2b4305b6d0b3078bb3b86977cc035ff009810ee38d71f9b0972ac13a9b1388ce75961cec1490b6ba4f2a9c3ce55ce794c8bb13e9491704cd856888f2636e7cb54fd7db2e1d27352b66f1d699bbd48e5281baa90c80270d6db5f4f13a353a4906782ddcccb39c0b1f57edce1b276ee2d55b5dfe1aef98f6507de8c04fc240148a5c2621e0ade209c27b1602bfffb88060e5877b56137820a6f5e6b503c3596816c7210d55441786af5ca2dd838927cb4c3206b3de0d7ad343c0c76ea61fb813207e5d7c6d5f0777192a2043974a30c339108de8bb2bef7638a409d27c02d64dc6d77d1085fed63ebe20067d1c3485c82102e55340938601379c433e05aae875cfc431dd4a98471be91dfe80e2d6aabcb5b5be568b2745672945257f78134d76e4d4ed0a14f4b71ae5c4be05dbf1fb98f0d2e2fef5859ca8129145711ab5853e76a3c1ce8a494061b91ab8991d15d464846c30bc36897c0da08be3aeb417dfae82f6ee64bd7345bcebec002fa6168bf14cb853128324998dd8dff878f943bdb6f4d903376ba6775ae239f5324e00d2c760068a92467efffe348652dc27b457a48536f7f28b817f05ffdcb4b89c03204d0a97ba2cf42086cacf8970e4c07aaf0fa529dfca5a6944acb594daa344d508b4d082322bf9161a127d5d9f33732db314d580ed8e7ed03a59ce9cad380d5fea341cca42b8e0148840c92b926fadb08d4ba358e7dc45454a0c06df3cc0c3b738409e84d5eee501639076d750a1d543a63d716a863ef24881d7d78e978561269cf8795b9bdb35dc95bf01716476206a643813f0e267496672aeb54d0b74df5dda2056ef5d6a8b831bb0913cbd726f52be6871665ad03655d151487e6a6b3756430b17198a94c8c84f1f5780054ca2a2e3643aa45c3be2d7906d84f453bfd8ad4807d7084f50bfa9c5a9245fe5c2d233c04d3dbfdc6d454c6181db84858f982014e3810d09f9046e47a6dfe80fd7f0e4fd7e2fcd8e4b4a21d8dbf37a51f232b46ec18d264f048e89e9eb3164e33880d35c65a6886f598a2d32dc384811c3d0ac4d28af0e6aec7686e88de8934d4244ca119985832e08a484d5d8942c1b7141d4736a6f7605414e60355b067f1c592fa884ce5878a692ef31b71dfb95171c79e45481ca6e26a8e34c760153ea87ba82213c1f0c799219af52b440926b96a98051c41fe9425cd8e46d2e02d1b89927503ef800e5034fa037fa04ca6340d4f3319b37b57c42a921f7748b18c5e8b0d5cf5e3b987504cb62c5e8e84b4012ccd6278fb957f2d9176745ee43bded38d0a1e06bc9c48c63ebd7abce4717a0eb6a3fa4409f0a05fbad5d952307b7e69ff13722d5d029a05ff686373a6220ec120076058cc486d746b2bd196f106788ded2bf3fd5645f1843bfffe6654ae3c89779d6a9d7d3b7e8ee4cfe41b330e9e1e17da49f3c2963344a56d1eede3b3a1779a346d66d075722ed3bb399d80ec5ab0fa1bbd442676318100aaf320504d95103afd24311570d5d678fd087ca4e290d9b7d775e40baa0fb47c79535618de101d2236d8fd6d4f0c9057fcba02135d1a8ea051dffe0bfe5e774fa1357c17ac4182d9bfd805bce340bd2494558279341a3e32c8de3f34034436cd91c5c7929906de83d8160eff12fadb45240676fcb1a0676e52e49e590faa60d4549c0902ade1ec8d57739721dbe71fbf8cde5b6e3947930ccffd6fd2776dcf4fb2b7eea808ab9e6fe440e3c86286a299ee45a13a019a6783a43ec6ca0821d06618dd92ece297880ac8f44fd18b3565f04ea2b6f5f8d86fd1a458edfb4aa7e538e7b5a257c7334c25490d397312d7658837eb3e3974391379742fdba3b26bfe3ce94c5d43d44d0249e4f0afbc07a7ccdffd82159c4b13ed91acf4b1198de5cb70e45918b1653923775e3b1de2d8a9fc1f2b2c0dfd2dde758e9cf136f0a705ed92396cd7a0f8ccc53402fe28c90ac22870525648e204d2de20dfabe330370030830a0674b7edabf32ec318485a255d7d9d63216a4d9531d2fcd89162bb294755fa69eee0a0f588f2ec823a55a85c0b145677663b0770305474d20a136641bee4c8eb0fcef7513075675ee01970c7a9aafdf0a7092976ad6e934171457d14b9ad530105fe26e9531699b32cb4487f8590089adea3283ac1f33bde494f3855e02b73267f65160fce8a0bfdf28399b66ad6bec6651782004ed5b70d73454b0433a4a69bdb90d10e7ba40c3797c8b269fd5b30bce3c9ef144ac5b72ab72201b06c9ee34bed4f96719199a55ed172b7c942cc7d889ce7f64d1b7a5fb38fcd5d8de3808e24c6e7671d675abe596675b7adbbf6dfb2cee7141d5ddcc5243765151008c698c4d69b268eccd95d3ec48ab9f37452f83e4aa636def10d096af51fb53d4a689fce04283237c7b6c26b9c09dce40f9d1ac5c96e1d6656a8885ce4df1354477405ca83103cba5c6277ac7052152c511b46bb02ee2e3df082f384d860412d13bfe6333185fb71b345bcaa3a2d70d6cf24afa3be31eb4e9f0cdfa5742db7795671d420a2d5f6c81db2a6ad3d2f853b4b847dc0d703e9db2f173b0d13b5c0af2d5fe95cc1723b062e8307d291bf1a873e1cc29a96b9c5c66e41fab77905977914bd711966e1e85fa63e7f7b58b92f6d0a576d16328cea88bda02494fb5b8ee39442c693e1f94390fcc8194404ba185602dfcff7aa487735af8e09c63760022c1b2bae2956001bf7adfce6c745a5056ac5b0e443f49258c6d08562d999aae164e3cbe3b657e8092840a59eaa8cf2aa2f546ddea2af3466761c28b799dba6f7b776e2db79f3d0898ef8bc1e5e708c00bf733e71c7417d5af1f7581a790b74150905a00e40345fe5041fc035e73a13a3e97c6f9b868dd5dbdf1bb96be0103352638e06716a91687d0a3ea773d8ef18f433afec08027a4a219784e245279b14c0069dda89b6f5f551939e90b50567653e493a7846ed7df3b1282b45a6ef35654b15fa7956878b290ff71779628038f22fff4d5aa7541458528205271e867e217c1b1a31bc39ef40b1fd565cf4afc76d2244925f66cf77bf814d31a73c7380e1895a844d64acfdeebe0840bcfa86b89dbc04bdf1a209ac970538855d1ecb012a058c5e855cc337b636c93286feee689cbc91a0497c8f4bff408bf8386505f4f8509c667050ed805b700c0eebb73a3f5ca10100c17b3cdfdcc164c6838f1bf0468954683a4cb69f54f67bc0c9ee859627d697a837dd9f9a0861b729054c717ede7e9e7a88be838a64d2c853486147b5915869564a9a0aef094695772f058d06382a58c7b04434e669cace186131b9788f9d77a6b262fecdf0e4437ce44a6f1a879feea57ba1d06d6fa05af4293f455cb7f5d66cdf8a21eca3c47dcdf167e7e41d2672c889eaf3e261bae4db4e1c3fa8a64ec97d15e4b13757204419d47442f1c0d70f33aa1d116177f3b8708512295afa5e48b8fde19592aeef9adb886e061a4d7ea57d0014cad5c6f46ec1a07869b85059f0bc96c8ae558374198ccc30e52518242ddbbf07d94ed5bb8b23aedfcc8d548e4331c6c247ae4a18521fecfbdfd55016f4aac554dc39f2e9b19eea4ce600e198444a14dcdedad3c91d72d15a2121051ea88483e3f64522773a82c6462445cd2685774cc6c2a1061bbc12b1015f480e5b33d7e13ddd6e8a5d23a5b6f403f6f05d4da2ce4865db4c2413d2dc2f6df1e8782695a267a1243e04e668d460bcfb9c720c680bfd5208dbf47631d3ed262279acbc2c76f3631ac9861ec5a906990ee386fe29465053e1acba8bbd4e15bc7a2985ccac3bc906c29c4e2721f06c1bee8d3efac6b17e0b8b71223a65c908adb5e8f5bf9318fb9a04327826e767f93e4bb1c6d09dcead1c44b9aaddde1dfdde0d111796fcb808384a9819660fb724aa3a089f2963a52b6e59e1538d2d818d12ac01b651039a91510b0ab036dcc1571e8fbb3c1a410a41f40b6d736efa69cd7894ce640478de6f62fa32e86aa20cc956a999bb21a93a23605c3ad67ac82943ea87b2862e921fad3bde85c9eed541256696963acd05f42e2645b368595e67d2671a13c3cd5079b5cd3ecf406bb1f0aa3e1b4184be06d665e146669564b586a02c83ded478e44ac0064b804377cf590e83aa118d44f25cda3a2273b35a33ae0c452d4ddc071c496c225abb2949348c91af1b947299fe4128cf2923343a3845b89d862a1cbfda3c5ba2cf2b9e4b1d1672d2389ae224b3c57766486cd8d5decbcf6f53f8b0798e4e0ea21009e3b33b4dca11c1ec39818aab0e37cef5501f4da316a07e00c4d1a1775938d216c782065519105aba05f64e78637a40734da7b4fda86cd5ed2bb15b86205ef6a8d9495f06e252aad9a2588d34b7d8366fa223038a1b16120383ae66549bb6addfeb062faf5b070891a17be457cee4550aa8c769206e5236557109c65d8b6eb46c392ce424b53184140993a2db9497f143de3469c39a7cb8ce4be09e1369128121b9a6e1d94d25b0c6d238ea8490e72d4e2899f2526e2031ebea73800fdca0dbd8f9bf8a5e659e560e2c3c990e51ab209208b045955c0f1748047cb044416c2afe3a70f983a2c10ef6571447e13adee40a82c340c5e66b842ca8e39e41b20ca203e89c1091303e503ee067e388466146935b735218852f630beb870cdda2a35b86ca7cc81b88182eff008bcb3874727b3cc46545a3f6275d8bd8bb88fc27e8c848f1b379df18037240ac38361cc85561661abe8a138079a32a2039e2e603fe81c12fcf85924f61e826bb81e937f5b5f7a54d4348f4a8b2af067abd1b4e1f2ecbe13823b796c4212295b60507ff863634515acbae04e10cddd9fe0235a26856ad3b6c64ef1ea1ee194d49ee1c37b2b09459ca60da303e692220fd364b8b4375f331bf1585336d79e25919b38119d8a4293086184b4d25bf1423b223c74bff2dd0ae81e57a0780c909345594bc5807699efe6f854427bcc509743f4b00f40717457e438b0ccbac3093ebbc503368e5e7d134d163f20fb4ec0ed0e7391bb5b0f50ceafcb4d04c4b22a193143133a900ef8eaa99a0dac7dc43aec4700933b9296f4b816573d08f9cc35088e6b6890e534d85f710a323c66224e3498ea8b82b865d0d1cda0857bac5f0a2a4bfcfaefc48c2e8f86d6a56157069299f3a5e2f98602e0267a2ff363e7fcb746ed500e350a9aa95217ecf579cc4fc8100dcda343668f2799ecabca5db9338d60c2184082e3bc55538f1fda0bc0d0039a3c27233ae681766e042ce0aeb3c3852da9ba0a30981d38053921e60bcb6e9140dbf02f10f506cd6a18f10bbf67c7c5a23a02951a88a8e62aee01768ae3aecf5d9bf2f66057822bfd3aa7ee65ae681e734d18bb9f7c08500be4df8b5808ca60111e676d352a28741b1117e7d1777a40c2b9184170644ed465af40f127ced809866e0a8974a260744d45d25fe2ae72f64354caef4ad6f8d176a4f511fa30b03099a9ce1f4d9cd8ce69dfc928add2a92fd0e854ba91f6ff7f6f573eb70bee6ead546a35bdd86b0dc79038d7e310f333a58e59a1c7b1903a7dd39749ea3849cb2caee368da081cbc8e0dfcd377dc680730018af2d75c3658afe632c3fd15c7d9f56e1a199e674431c5221dcc93c95914e6f7019ac98a7b57f02f66bc7fcc01a3897d27be901fca7268974539c22e0b7268f1ac420b4ae8a7e5a1d364602839afaa0e4abd58e135138694b803e420cfc70cf968dd6920375c3ab30cb9bb6d074cda631a283c4faed39075c6433da97a248b0c2683295f941176061b929ca97e039f154ec8ea9e99877596fd6a5399fe91ac7f98a0fda9d9b0ea21ccc0bfa5083275ba0d4530b63ba2edd9757430b16e44299938795530c53f2df333cc75460a8d6788aeb21796d2d4e1a2e886eacba88a87054bf8adf01a2c607fff19b30bdd82b51fc040b78406fb953679472a1a82d1fbab6816f93c19fab7eaabcc600d6983d25dc0f1aec9a01ab073fc0d32f1e12ce1de83b6219660eba2daa22dddead924ff0380647c04868fcec76bd0712d967c5e0fdf36600344bd0dbc0a30ec17f0eac9586818a7cfbbf546dc6affb7312936f7091401abbb8604868dbe8007309a7eebe27b90468dcc62438028b77b030d2f2267d9af7da1c9f10e7d3bbf832deaafa616c1568c2e7c2ed38aa6d886a0699247752d691ee6563719c9c9145427df1f29a8d7f3bf2250c12287f9c05cedf83d3e9efcc05c1b3e2e43fb1e92096350ba47581a31d8b5073c1ddc21f6f0b14f43051461b744054f11890607f48a043890bf2df2de3d388f374e36134eef87f7eba1da7eb0548e3aec87242002a09a4698171a201a576fb0188c0c919e17588714bb000574ee3c5156da6067f3e0043dd8cda007805e9f64e08b543b8dccdfff0615d639268cfc33b9ff7fdd65e902119694622d0e7efee20cb5f1004c5fc671cfea1b2cae8d0ca2058f521a00f6c06f1f08c768830b8c561520c660a213aa086dfe44eb545459e66083a776974b80bf266e9cbb76dc38bd6ec89ceaba2da3aac45dbb3f01370fd74f42e60938f3b8788dfe1cc9f286a1e7321831729030acb1552001287d6d8da3f54dc268ebf768a3fda48e0b3419239e72aadab22bcfa2d62314ed064e28554f146bdf4d4357dd06351e7296ca5a9e289a2e2456add644423d2a999b263a49749119bd69d014d3a5c953fc377c10cef533ba4b7c3bd60793395973a34473cbeb36e161ea91b802b9832451b23a181f73b816ecc491fa084dc0680d19dd48eb94ac8d87f6fda97a001905c3e144ca2fae9ccd88d6e2185599633dbf1883e92fd2b730abcab6dddfc2c332b3ed4cefa0bdb685f51ad4ebb4f05e827ab98fc7c15f6fe2667f88c050a61a7d8a22af2aea0eee236ab50647c599c450f01062ee0d3ed58d3aee191c16185ae91700b7100968170815ee6c806e5efaed29d6231efb5632a37347bfd911dd56234d8ea8bf3d51e47fcc60d88230ce1f1147a347799cfdc0676905fad2f6ef0d0fb38231cd33ba71685733901b8676378bde7828973047495db2fd9f2d14666c83d243cc1f90430c9c500501ab0104be3a93f930d6a965951e9a1dd0e515313f40126cbf32042ca6fe44f5370cd7e37f7df4c52e0c93ac5148e69eb62fe759528c9addfcc30a624e8c4c9ac143e79b7815644e7fd51c480b6f94fb19b8be67e031ceee69eca74bc31f7c2d7f8819017011474d668926fe41e6030b08756fee7202931911ba31b9d5813128a14fa036bab39afb1313707e85b5af451c78985d871c48278dd3492872b46022cae26a5637a2e80586549610316ddef98fd6d4bcb3752941d98c844dbcd15f75bb25404d9382d299e38b4f49297d2a8bd8b9e5bf09390bf4a71a8afcd390c55c894ce546049e603e36b47a1fe28fbe033fcd044b2473217d5d9aa2294d0fea0192ac6fa30d17f437cce5deac5d0586804ddc59b2d7dc69008b4828264835425f1185161a8ff013d78d923803119962e989a43947df87c049df425946dc355131075d8609952553d313653a13d8b988a825609330e09b2861a64d97e51d9560045406208a3d51bd0bfab2dfe3660591c63dc8c195569357e7e39286a92cb24a9ab36a303fdb9bf402d27d8c114b2ca09e73c5430e0e4f5252d6bb2044c693275f25355d4facc3a8a07089365f61d6eeb0266faca3788356fb379fb1bec6bbac41dd9101daf782b54d825a1517334c4d8ebefba2670ea59ac0bc017f4a4412c4e481fb6d9e62bc28faefc3cbe575f5b5bb6e1f1f67a78ba94dd56d398981f101a5855387b9535329ee1baee7a9f6d413317673b3e42c925c36dce24eed12d53ba1bc14e2f32beb0a716de11beff8bdf0c562581f0fed78521a9524df8e5e5446678c025750bcea8ac2a35ef2130e3e63894b8ee8f964775b8d25e82ac79e8d49b47bc6eb91f5657ee69f63b84d644dc2567e47e5c818359dd250837299244b0fb8d4901936d76500787623ddd2a04320666c2076331a7f0d132f8160370a03641565e6889c34f9052d2c84e3b497d2e577c4fd15542cf758903dbac7825eec1e0501e9bb8be60ae6d185cb5cc01bda8ae6a33b804c878677f36b0236017a1ade22b3b9acee34705296f571568971f24d958e707ccf2347998de92aa95e066b68a7c698cae288e320094788b557c36d8b834243a58365ce86084b041796e8bf1c976eda3f5fec15b3d29499eee8dfa1752293aaaa0ca8f17263cf594c3fdeb92a54bf61b1fdec07828aaaeb02dea021b6d56785125fd905918faa296f24ddfc2adcceee8e48d2036e309d130df8c1b9ac7a27e4dcfe4c6f0985a35ea0fceede13af8abf6ee879238d6ed94b435b6e2f9f86a838d53f5ae1494a952d0f03d8d6784d5c85be4eda29990847eb897fb75c6fcea1662f2fe53021a068f4e07a449d4d7674835fb3d3d70ce867a2cbd78c5bb4689a155b2f5a742b3ef460b4e8712c76857f574ac5672495de460c6e5378d016a09b361732d8236cef8482c735efac0266d0b65269485b8a088bfc5dc51b411c008f7c8d205f934bd2aebb64887c1c7fcf30c6f7a8605f7cac63ee05568a680bb4a16954562a581edeb8f864b9b87f6aedbf635821f318e1e3db208e929d9e7daaa19d178a4d53533cb96c92cfa486ed87e81f81aa6737949117400458fcdb789ffeefc3a4c8bd53953024e524a404f457efd5292786152d20b2cf581580c38c0c645b434a364316ae8914f7299696a4f8b2159205236eb8a40d465ad4597c756d0cd5b7d51c29b4c287afdf4f978bf9b466fcca13a816e8c8f2404d3c24df4077b49e20b9487d980bcb6a09709e48ebcb3d046cc96ab3633cd9afca7b6f1157100d1ed7438ee5e7d6faaa49b69f41669c50396dda9707118ecce0e43468eae137ad0560b581eed7b6e2ffe438bf046f5606a06c0d613eb9c555208803e8d95f51ab905d1b3215e15efe84fbb37983387a98ed1c1382287e36fa132a453e1fc396e2db2fa17eb72acd3a9e106b1ecd00378594cb458b9747b1bf08aad173bb32ebd25a8190f13b4da404c00bb48205773b9fd91198e333d3bf08047d10163452a65a031333f605e85a82f6ac65df9c0b0d1425444e892ad4bd4063827d187b9539c576d8170550b6d489cb03ea5283d27cc3c6b3d740ef1a085b83527293dd8c17e46765850589f7a9dbe2cc3de415b4b0f8598bd280ca7d3d15eb33e5466fefd7f4d1a9a44d9b00d19becb0f9a2f230f18229ce5736067accb484f131a9a93c55e50eaa2313241c59861f8814dc8788881d002e20308f8c4704eccb1515ba82e9f5010f1f02d1578b0001830b2202f7af11f09c1ff10f08ba4240763efa924190cb67f240404adc557d7c509cb62c80390b1839a05854ad25a374ed81cf1b1652a57fd2f1e0c353d59fc4f8949cda0af9ff0a05176c495af1e6d4a2696e62a927ac41ab6d443678459a6893c3ece6e3ac403b2c60da1d3894f82e2e1d70b9ff2ef68bb1651375144169d1d24ecbbceddcf898c511b16b8140450bf81279567dbcf683a4393dd30a890d0a2eac3dec57a200a6f14002024088a4df03180e341b8a6e6aadbee8557e885a7d94b9fc63ca9ccbfc66eb199508d8b1faec49d7ea7b041f1e35c1e90fadfa67420b082bf718f59970ca94bcc298309eca64d0fa706a3b2f3206083dc2b3ceed5fc20a6e67d07fed538887a8a1b30108305a2bd29985114410a5ea01462fb7422476663031a4124185470fa2f4484df4c4b555106eddb6d229aff949e59781937faaddcbbe7592b140ca42ab11d88912fb664829646bb62909a9a922762160ed5cedd679024dddd6bf5e246146878ae2fa8da0de45f6f847be57d1eb1f814eb48781010c1eea404f8ad52ef6784087458a642e988e010d8419135bb8b9528ac79bcc8cd6f81967f8dfc399259c9cab7349e7920ada5595c5b82000f7d7599e93231559416d6f2951c6840c5a4d5bce227c142c33a91ba413cab000082b23e7b3c192d1f50c98beefff3816bf58c8dd0755cd28642c0ffece6ba5809a895451e2b1d1c8681a4f4adeeab79885f95c01260add501233a16a9893b70836a518092c0e019c4e515abca3821906d45cb5fa760202c23aad08d9117495d96dba502f39c5a9361c43f65a21b4dc81c2d2523f0dab86979eab57416bdf8cc9e131b91cb8b6557e10a29cb5f3e0274097d3827e0e8ba02196876b64ae553627cb81e82a526780a9e28a37f9f6460087022bd281b629f8e88f315b224a05fbe922a7404b0f8ba5d078aa2007a930ff408b059f6df63d15d8094c4c68ee89d9ae13fd28bdb8f2c453701ca23073eda08bd31fcde3903b02bd4260c2ee6effd0adcf61965b5c2b31f46668bd58fdb8d70a3eb3dabce0b0d71e77e5bc1f2c7dbd7e8b754710748a9c759a70156b185fd7af91fbecd744e3ff8163b23d6cf7ecf2fab89cdd202715c81e3816ecbb247bd2933bf8c6fcb7fc23ba19cba06b487ff48cdfc1eb58d09c8a845c43d17525dcbb8517b2de7ac585f74e2404c06202a591c3d8a6d15c6c3b05a84b36013a562221ef3b668a3a861c3bed98f3a0581abb73cfebeaa764f7e8638a85976a935dfb4fd3bef216eb5d06ca95fe2956da4e1f8a2b6eadff140f77e812825acb70e655f240e6c16a8119dc92df3469f12ba9fc9bc8b3a7160d209c0041cea85de8a06700cdcd031a389150aa9d8c8bf06dc11484b9b7774d20e7aeee1e5d7a161a3405b8068ff2a6c0b10a398a36a38b1f11088b281fc7d4e40df9b2234820268ba77f089b9af95993befc0b1399e761f70ce8035679a8b89428ce69f8421da45fb9545debc1d31699af8bb6038ae4ad2b23290cbdb12e3739af8c46d8d936efb19f01a06f11c6ccd241f7fba01922bdd8a70c3855e229649f22db9f82bd26f089b1901406afce1858206dea11265d78c6cb4db49aaa6b97d5df3001068e61359837224071badbaf8697ac273abc6585728ec8590833b771c28096d99ecbcca78cae26433c2ccc869363a38614389dadc6193b54fae5bf2eb160a6fea4b49198ffd7d6ff136a0b31efd1e663da1ed534e6b744266ad56f8e90e8d8aad8e63fdaf62f565b25c0d121418e4c166914121464511838f2f1d16fdaa770a74701deba3be0a5534441bc52db0ef2b47ffb61a23cf1933ba9456312b72bd0552a8c1b4396233962bc57f422202a175b515a591d647f9b16d32e543ebae33d40d5c2ed269ed950c713fa23a7e1e92ea6870590a67f9cfdf779fe0cba0004f37d3c6f02f6cd1a64a0adccf80b324cdd358fcee310000ff40f52c02509676e8589e88b43589bcd8c4f94983f1cae803306509940e757823be800987fff032ecfc7ae18d1cc180ee0f15e010956228090f6a046348084352bc420d67ccdefcb5b89fa92ab6eafdde9c2a0b7fc4e03baf472a05d30d0514e07f4bcf45964ac440730fcb0af8e88d090920c0cbd3f8f34d34d91e32c933d5c20cd2007898e9a18a8520158e6a8612b73d4a83017e46056a995c05deaa288a4a9f2556767ba57c0df107c43ff437edb7befbdb794524a2903010a6e0a030ab8d43bdc4e864b9e12973b3ba3343bef89d15f5c8e695c68a07289cb9c25354921971d68666884e41297198d0c2fb9c465aae90c945ce2523c7314954b5cea285c14132ebd28a4a8a3fdd465082733de96392fe28cb9c4a518c594f38319991919b9c4e566c24619d55310a0cb29d30b619e32bd60e66d0974c3ff5edc197389cb9b32327451b9c4258e2e894d2e7189c97821c3b4c291919191914b5c06e5129739b9c425ce18a83136a0ef32789b4b5c7a63666374a0b8b1440d2a97b8fca072504db9c4a507458342c273503ac4b0f9e88a5ce2f283d2924b5c663148627289cb4f8c4bd76bf0f6c514632e71e9352d891c3c5142881236fe9a27e41297fa2f5418a6bfe609d6830e28cf8e4a87b2b4c625f8b7a44c3158795bde7f91eb9e894359c4d9ee18c5920aa50b6c2725850d7aecfe25ed8e1f85d2eef885ed4191ddbf4d731266f627dcd2c6cf101541a0c06103d805cc19284006edbce67752fed9c4234362802aa766603f6c6785e1849419508f0ba70a266808fbb6142083765b5ece3e1529ae37292dbb7f1e10f911d223e6c945e98b4650e820ca76ff36f0b1fbb775904576c738555020ed54969b1e22f66dd12c6372303a85603829df1501f667d8d6da17d57a3d5a50e5d4885ea2dc80c1fd7c3902078c0be2979b2e3a6c2f32a4a88449d0e8f9c2464cc229a70b101b0c0f631239f8e48c18fa3226e5c5859b2464588129c2e3e50b4e97a8118c0f32b7c3061363ec822505668d2a09312b1a373bc42f3f6e90fc7871dd74a161fc321bf272658391d2939b72f3854aca0b1a1c20632e09f9d4e5e37223e37ee03c01b7e4bea7241e172a3b381fde162b1f9721395bb2645c0e7751e1dbc2e4717932e26e804f36482e6b34ee869ca79d8873e2660b927eea11eabc2d4cc0a723780beeebe20263a4b8e8308af19e8610bb14c13815be272674971b3c41bc27a7100785c365c7cdd30f200e4ad09634395cbcf0e0709f65b19f72aa33bbb4192e35d232adc66e4cc7bed835e2e437ab1f4da4519ca6e9992b55679bb455b0d2ae2bdb2e2ebbb4cd5edd67e4e98a489bc5cdc4d890d6518f466db7e5e128a7a97aefbdd75a5e6f4b9ffcde3598da4b53741d7bcb454a2bd3d32ce5e9496999a794521bbd750ac4d314ef0c787a00109e6e8d0225d2599e327b96e171bce59fa97b50bdc27f5fd7479553cababc8298d75014390995a8f3f0bcc6972ccb9edc4fbd2e91e5a3ec12594ed23e6eb935c2d9fc24bf6ecbf22cbb291e9ef135bede92a43db3ecb672ed12d12c8b8ccb4f288bdc85b22ccf3599e596d65a2bd5a9221b03f9bce11dec389d282cf359ca571c4ad485b2a034e32c0b90b22a2f77ad5da26f934057e8b2169970d6476d0ea255007e91da5aebad506f293982997dada5e35b7cf93736e19a6d758d58b70e6d51202456083112287cf78834a2152994e8e4883037858802a5814f9d1c81d3ba9b1c436ac861e48588da19fb020d780acc0610c2305974288ab095be032c0b6b21400173dce068637e39106de14b65f7167fbfffdef219084077d64000ba13739b05f7d44916243f80a74e8cd8fdeab62050497b01cb827e51122c0910302c2122eabc97e8ded8b6da3b43ac3f81f07b0bfea32bbe8b2fbe17df8a2fc5f85edc41aad3e05f88b7ac35c8c6c2ce6c67b693b433db99edcc7666620fdbaa623f3fe8e874743a3b1d9d8e4e47a7a3bbe60e8e835d8d0977bbe16e6bbc5fd247892abffd0379df3cb0cfd499e9cc7466586756c6729292feeac831ca31ca81e5c464b3bf2fe0e47072383a9c5d8e2be7b581d8d6b661dfe8410e37b79bdb0deee67673bbb9dddc6e6e37e0b8a56c8b6bbf0196942c254bcd52497f798a268e4365c81c992375648ecc91393247e6c8dcdf2fe48d347a0f9046dc881b73236ec48db81e743adddf19449928fbbebd39cd5da2be837b9ed725caddd6cee6c9f1e49ec7f5a2b983eb80062c080aecb6443ef2cb796877c3c25db8135de12edc85bb70272e85b7f016e2c25b78d319879e088d6aa1916afc7902e2401c980375e0ce15be84a46cab0574fde522cf486ad916f6b78967fb6c7fa578469e9107f38c3c23cfc833f28c3ca3bf33744dd77e465149bff44b1be9977ee9977ee9d75f199816348a4cb22bbbf22bbbb22bbbb22bbbb2eb6f0e1aad46a3d168349ad0282661fc5a6dbe62f122567d0d8de58f6bb48bb2edf7a368c3b6dcbeea6db2adb8c1f80365db9b655b0ad09b6bdf91ab58414529fed6dac158044227c08e8eaeb89c07561180cd9d32f483cbaf99bfbcf3f8f89ddcefe03cb027e5e4efe0e88acb45b01cc476dfc1fde54134cba2f324fde59727f797f324ddd677450fc61e36dbf2afe220db063191a6e8de2ccaae1663fc62b581115b7fe74f85d8bddef2edede5d5cc36cbb274853dbd99b521d214bda752dee3ea71f5b87a5c3dae1e97d08f6a6fcd41777453aad18e8ee47131e5aa996aa69aa966aa996a16d402ce2a5c01747060de6e1d1c8cac099093cbc99936686f00341a8d46f33eca7360daf3726039b01c580e4c08870fb47d8daec071010c3f78caf7abcd07df776dbff66bbff66bbff66bbfdeeef450712314ab0b02789ada4da936523b9c2371cb8af2d42d8b157dcbb75b07584632004f790a9682a56029580a9682a56029185f814c6d01c053927623694148a91bca49922449d2882876b3d3103d1d63baf147cbc6a1c212a988b7928693a302e3c1532ed26e4b9bdb76892c57a52143abd1def25dbb44a6971c1d5511074fb968a4bd0f0445a34f34f244232d1a65d168e3a0e23ac5531eea6e4b67ed79a14e87ba1cea62ecf06c10ca421987c2d3b34383e5a3c2a97094a88bf99483bbcd64776afc4e0c55132a332a315fd3e769d9dacb5b9ea1c05d14b8db35cb02b2b26b3db58a2b9f2dfc29089ba205840d19f9fc90197afacda8e466e4133f405484bc1c152fb70115a020144fb927f3649ecc9379320fc84b108a208dd338144294eb988ee9988ee9988ee9988e29090d4da1f936a5966f6f7764acb80c71ca332cc3322cc3322cc3322cc394780904e5f88643a34d1a3260b688e22dbc81b7ef76bbb5c037a12e2515ca71ecb6729b3464c0f47cdf352c2106710cc7700cc7461fe6066de895edaeccf461831460a8ade16c6d9b490186ab9e665385bb4496b3b85db258f1f46a6c81830eace8d8a66c2c1d783062c979caa9bce5b69d23cb0a82e1010be7292f81ced50fc8312ca2dda64511082c225eae7ca6d056e6b609331eef5bccc7fbee02f2beb37cc8fbb6127e1fb8f7b75a79bc88555444570841f4dd56268b8860837ea858d5706a6f7907776d53a1add56d695385cbbcef2d6f39a52dcdfbc6a2bf6caa70ab24bbf2aec2bde522000040c4f2a0030e526c60967c4808850a152a54a850a142058f0a153b2a543a2a54842ac04f850a15d695524152ccef357b66d736608cc5fd84fc987b7f79380a0d217d5423cc8b5ae0efa3975a16ddbc61d71b1c7114499d9b7d83b3c3b379cdb1383b4745f55faee2a12258ccd3a3716eecca7346153b74469c1f5535766004de0981c7477fbe3d2b9f9ffb37abdb6dc5745b35e55649b9152d577bcb57aedbf27ee4c8520546114a2c9d60beaf5e37a617647cf14125ccd95746497c5fc102f084114c5f8c680922e9ecabd897ef2b595fcd5648e08f1e35e00f36df7b763228591498d47303937a706052ee2def89dd167d42040e1c526e38bc7cf1f2bdc76b33c416031730325e67efe9f2bd27294997254ffc8014040d67efa171f9de53eb3d36fdbda7e953d3d3e353d373c4e27b0f0c74f5d870e9008d78d880463d32de721eda6dd12e2d3c79e2098c935dece93b4f4d8905178a8861c2c30c67e7b1e1bef3dc569025178bc185232f0471761edc96ef3cb9cea3e38972e25172e259e241c283f49d2709c703c3f1c478646ff90eeeb6fee50447132750392c7122f17d27c7040828b5d80f2ba4a0e4ec3bba23beefec844cc9b1c49822602073761e9796ef3cafce63c4a3a3c98e53931d2d3b4c3b4d6ffbceedab59a2cafbced257db81f296efb86eeb0061665052228d10251bb27cdf118ae164c398282b3d9c39fb8e11df777050a2c86d08162e5f9ccebe1373fabe2373b267df41faa1e6071ba8a8b75db593a96e32d52753a962b7f5828853ce09236a924459e1f65d15654b122f37a2986101c9d955332cdf554906c881863132dad0c8b2c2d955b422beab6a5d6513bfab9a3e18aa1e1f0cd591cac65b1ddf55b0efa5f3bd54df4ba543bb2d13072d548e581265061bae7cd7b951732591516107133d9c5da7e9bb8e935ad40cb819b9990a67d7c159f9ae93eb3a3a9da82a3a4a5596beeb24dd90dc90723ab09c4eccf21cdc6dbd3882658b10516a80899a7112f13d870332a526970b3e5ca1e1ec3955bee7ec2c00cbf2041133440d5172761ddb779dd75bde758cf6771d1d4a729c94e468c9616afa9e93748992726849b5172c47f696f79c590e9277438d77830d54d4db8eb3f36038375c4e96547bcb3b8e0da7a90c4e8f323847383670747cc781ed763b1cd7eef5966fda6d51a71a538eb020f1c30c5c4e2adf776d09269488c19059516ae2ecdb36c4f77d6381181c465cc0d2a3093167df3821beef5cdfba1d55652b55d94b1b09d2dbbe936e97287693e576bbeff0fbd64a375ae9e686a9e96dbfa1ddd034cd755b2f84d05a80e1c40a3bbc50e364fa7ef382ddc866486f79bf4922bfdf2ce91e2935ba478a4d0a2a15f5b6a776b1d42d868be5def254ecb65c689105519271048b12e794ef29998b261c3a7c7002a3870e674fcd82f89e4acac095a520617e08a384cdd9533429df53b59eb2dd7c4f358549f50873f4b6a760ba940d5d4a8776edb42be5d2aed75b4ed26e2b03438c722884c1e2c3e9ac7d276b3bcecc8ca6b499bdb0c4d94920be934dc060ab22040c57d2bc70761217e53b99eba48e8c5222959496be93493412098d44b291301b19b391630c371441e4c66397e6851fbe8f2c8658c271e4e5864ecbd94728df470ac68a13311f5cb1265dce4e3ef94ebe3a69d4491d5985d129aba0e56d1f6fd96864326a8a8d49b131c7c6d1755b3a41c06a54e19105470d27dfc710c49c62500304983340ce3e36f93ec2722c0183ab0c0cd90e671f63b4efa3ac8fb311298ca8268cc8468412a37cbe8b3b9d78d389389d28d2f094e589325dca0cb1840fdf4522ba15686c96d8f868727671d6c37731a90422b4293a583862063467176962ad8bb6a0ef629314b18714f148b421eaf82ec26ce1ce65135f6f7948bbad19087123470e331c35118687ef610d474a8c21499270b11275f6d0b6f43dbc7126197078a1c6cdcc8db387b830d7435d1865245432122e854842a4ef61d22c84cdc21888bbad174b5a58a384d6c4030e1dce1dbe83b9118c0fa3305b864839727650c7e43bb803c08c068ea82b4f517e9c3d74297d0f5f3d34c2f91eeac02a804e5805500bc80436bdede00d1b81494620cd755b3f43c990a43040880192f41d4ce1346627a5851f51709c1dd4e13b0803818c1a1bc6cc2084114dce0ee6f01d04412427354e6cdef66f8783c245e9be9b0ea7fb726ff917bb2d0a041246ec30723283cdd392ef1f16345556c8c2a346aec9d9bf19d2f72fa9842a29242942e3468685b37f341cbe7fb5fed9bea66be4eb718d7c47e4f70f36fb6ccc3e1db41ded73d168b7f582c98ca62c2ec418b3a371c3778f0440741082cd111a477c39bba7e4bb4744ed082f29c89e78a2cad9bdd977af7b5e941a4f498db7347ef792ae0b890be92dbf445eec1abdce01404853921966a0e1717476adb3e1bbde79afb7bc7b4639df3d1d52b493142d6ff5cda6996cba09a793709a8673dd161d0326491351d2c4d0440f49beebd70260455c01220683471067d7465fc3773d440f67c2c43852c30d4a9c5dc7907cd7b2ae67de778dd4434d0f366ff3cec632948de5283bcb375ceeed8b1b708822c40a3d7c90d274d2302605588da81d5c64ce9e8f7ccf2c307214a60928356650397b36f23dd77ab6e5260b23f7b0308e7ab69175bc7d65d7078c78b2c68923686028917dc7092042081a475a620822cdd97191ef388514147ce8d0c40c5b82383b26f21de774511def309b2a58a9cad25b9c74bb44486e4835876131fb020720416829824c0c060d43bedfdc073b578ec78e898e2767bf3a21dfefce65a486518d314b6db49c1dbb827cc7af8e8df277acc3c87532a2e56d9d31cd9adef24b73dd96016ab627646c2411720390eff7a50026ae300112a3855c0c67bf463fbe5fd88b241c5e4e6c5051220a9bb3dfd8c7be5fd90cc94abf4b6f6fa8f91b6ca0deeaecaec22c51b55edebec8e1048b528ed998570ce70cdf2d14354bb0ec589022e489b3dbd9cbf0dd26c1008976238249861c3bce6e6931746bf3ce6e9b4240599577db833e1dbde51656026be36d4ec7cebe6c0561e941a04a6f55e0c487991cb21a3cac398fbed7da015c388ad57648521b72f66a83e17bbd3d093b54d9c530e508c7ecec15e7e37bcdf5aa8b7a8bd402c9f75af906288bfabdea78d1ddf7f84e732f963c31b5a981089026949cf6dbd6720dd010e85b27579a82f8383a63e3b4b0b7695e5470c81802852688d3f278b143131258d0a4a1b1c6c66939cbb2b03b58e8b75c4886dd49da3fe96108156b15b4e331ff3c72844cb2b0836cec3eeabef2a01abb93aeaf7cbce5f1f6841336b386dd47a11b761f8594ec2ed6d85dbc7de52a58d85d4cfaca717cec2ebebe729c1ebb8bb0afbc8322760f735f399fb17b98f4957319bb87b5af3c0887ddc3d7571e74c3ee21ec2b278a6177f0f69513c17607735ff98f11bb83b2affcc7697730e92b2f95ec0eba9e76ff90ecfec9be7212c6eedfeb2be7b4dd3deec3ee9ed2ee5ed257fed365770ff6950355d95defbe729cd7eefaf6956bda6d91b44ac3e7e7f474082e287ce605a871664e01cbd2341a9bbe97c75c57d7508ddd75ec2b27d9ec9e755f39d9c4eedaf5956fc0b47ba67de51b4cd93ddbbe72a02ebb67a3af1c884be52b2476c73aec8e6d5ff96acbee3869776cf4950b71a95ce8698f3c76bfb3af7cdc51b90f8fddafeb2bf7d9b1fb35faca4d1e2a37972adf4c54be75bb5bfbaa9c080d5923faf2e40694149f79de4454beabec20d7ee75f6958c91b19188ddc9d8d7ca491a0985ac310d918683cf1c933232765b751ff1f7e6382f0fba4f8ac0e1557ca62ce89523951ce1ae9876a7b9af7c15c40e8aeda019f64ab622b2fbeaab35694fe2469005e312af89ad31c2258f1a2fd87651235303bb257dc206f6b6e4012017cb4fe4ab3540e60ac8e497e8f2f0e321074d4b5a809e9f49d4a4af562aae1cb9b27a137611dee91172ce36abca15d0bd572bac5a91d95706983dcf14753647fcd1400d9071ce5bb5b227e016e0c79655f30b6366dde42d5228228cbde2ac14b58d666f2196225056e614f8a80d4afdbebd694b734b699fbfefa3506e683c79a2501e9b3555f50bbbb3b16409f971e62f72ce3a7845d6f21434046d5db0962829eb9b014957e4cc2bae9c3fe6225c5b5db03ef34a96e0c394040e1805acc726a6a17b8cfe7291de04d91863ddc6a20faf7917ab34a0e8b3c9cb887075c982fa7d6bd3854896409d66d4320a44da9bd0637461225dc0f6b878f37d3ae791755b24cffcb6449e39cd66c7a2577e5eb64702e58012653eaef8cc770e4d9a0d30de09d053b463251fb01f73b1ac670aba2273f079bfe6949582d73c050effbc430d7075d94b78cc29bffcb32c287d8f5bfd376ddab4d159176132224d61599506b0c7fcc6c8b69c96dde6def2dbdb26cbb2496f4dca5d58bb138165553368456bad58f594e214a52b544f79e6a2ee9fbea32e3ba6d55e9c52d9129b34b7824b29148a2276fffef2cb59a21d4bdeb9e545f889203b5c4061868464e4c20c4e2a2cf971040f66971c2135bb72ef03c310fc3c9d83f63ced7d9fa76738d1ba6aef03c1cfd315a8a9c70462577ea3cd105a6bef0343711cc510fc3cfde5496bed7d60288ea318829fa7993c69adbd0f0cc5711443f0f33413adb5f781a1388e62087e9efe52bd0f0c4531043f4fc36867b4aedafb3e4f571bb718daf3b4e76928adb5f781a12886e0e7e92a5a03d19ea73d4fd770af00565c1313e17d6018829fa785c0bc0f0c43f0f3340b3192685db5f77d9eae4bb4aedafb3e4fd72536b4d6de0786e2388a21f8793a870c5a57ed79badeb4d6de0786e2388a21f879ba460ede078621f8791a081249daf3b4e769934b8e8eaac8fbc030043f4feba03d4f7bdfe769194fb4aedafb40f0f37425c2052784d05a7b1f188ae32886e0e7e924705a6bef0343711cc510fc3cbd034e6bed7d60288ea318829fa777d05a7b1f188ae32886e0e7692f55ebea7d60288a21f879ba470f515a57ed7d9fa72b0b4e2868cfd39ea7c968adbd0f0c4531043f4f53d13a88f63ced79da868c6d03aaf4d4367eea248c91cf317224486df7f0081384c78d89558b65a52c8cf9bd59e35a9564fc558c535b7ba3b76f6ba444285e3556cc6f05bb0b1e73caaa9e0c1ef39bbfcadaded5bc6fbdcbc7fc7ee02bbf19bff85abf1ada449aa28baeb75cb435c09c621d034cab4d79a6ce351a42e615ebe82dac352ce9191ac97dce573394d1159677f065d31597067f79bd95da54a52b32c619a7b0411e458006ea076099292b735c82949579a6dd164935f071fbf399f72c44e61fbd69caca610bfe5acaca587f8e81e6e205bf5b6903692bd32e91c5b4d58a664d0ffce5afeb4d61b3d215998b60b5d96bf097d72cba461bf08b8c7bcbc59d4f2cc4e6bb3f7d7f05ecb92fd1be01679c476bad5094f6464a37b5ad4d57ac98883a637db5f5b4d6ba1a20a5f90e4172846cf69a717a8628e4440172b9f621d07bef5d099de159edb02bbfb406d5622222d0f7b98330fb691d090803f5b445ac492f51ad3d54e13bb0277e3841040a45a6b0510a13444f902dee76725b33d1c3647b627a58d85f971e1976e8a4bab1c521787c6cf0071d1c7b2f51c5b617a4099d18393da890725cd89121678a2a49a58667090f3b274d4e10dbd3f5d490d3e9b23f1c9b9db33cedcf4a0cdb63da49e1498db1861651a808098397201c5a66ecca290e4640012d8ef5b1d59add5a7bef139c1c4e54f0d895531c20bb72da811606701a0b084a5200d19004fc3cb5ca252ccc240c20b4a6009b0901c8d70038102b287f15433a845228046d40c4ae1c0828cc8fcf8ad39eb1071ea1dcce1803e9d363349f887b178e37f4b6928352c064d4bd625747a88ab2de729eef19786b6e175d41399012358c8c2bdff726ed49dcd7246a5409628993f2ab665f17fe850e2ad08a30d103111d9438e98b22383464c6cc78e264775bf8ca492d34045b5f69ae01d67fd796d974c1facb3359b3ac6c925048dad22663a410cbc2f2ca4999652180afa492a9442a994b261213a9b6f96e26d14c18cd8c99b2b7bcc4dd167572a68997126c7e784a7359f85ee6663491f1220416c6c6d94b1bdfcb9d0c98c8acfc808b814494b39baeaff1dd7c75d3c8d441f6289dc81e5a984a5bd9f4b6bcc5caa45849c6cad2755b1e70097245499a1e8c90ee0adf4b1f3a5e58b244951d4ece5e5e15be974238e1848d1a3ba0a0f972f6f2bebe97bd2c91c2703561d8bce5bbb73aa8511745ba2e11e9cabde53c765b115833660b142d449c8c5c1adf39151a94546842039a1b33cece679fc2779e64822640a4fc8852e668c6d939ed677ce7b5b7bc731b6faac27b54e147dc06d7f196c36edcf57acb8768b7a58232b4353ec468e28aba32be0f6569c24a0d2686a032c40c671fb27d8cef43b7324ac784871dd6ec40e4ec43b847e1fb504e17d58776436c940c2929195a1a423284f476681c1a87c621d95b2e84bb2d0e188901c99230b720455c18df857238603043e4a8890697b30b5dd777a15d949a1a1b696239a439fb90abcdf7a1571f321ad231de10721a6f08691162126a7a2b741b6142234c68840909b96eeb0434663f045143120c294f7c17da81e99543932c46aec438bb909113df8560d40832493416cef478ed7076a19890ac0bcd78be0b21955153860d54d45b14bb1d8adb0eb7cbbde52862b74501da0fb42c3bbca8d972b2f98e421681303ba20441dbc143767614b326bea3484212454a2d49172e282c9d1d056dcd7714352adf51d876bea368aa82a247151447286ca0d0f116054cbc05893714e20d4510edb634c086071b45c090715341cdf7a025272cc8d0830a6896cc387b906df73de8d6429329638478ddc21c9d3d0897e67b50ae07e9a24425414aa292a0a520246f91de06252505c19282623d48f69603e16e0bc770d8d4dce0c21926d07c07caada9a103861cb4cc705a3a3b90eecf7c07da356961a67404486ecdd9835c51df835e3dc868fc1ea4438401e424c2d0f216e826be80985e4d220c28498401d1441850ed2d0772dd16e5724649131b98c81dd960e63bd0cb8c131f28e8f8e1880d9ab30331f11d08089b2734209e7690393b50accc7720199721df81664048e1d38f9af0e987cd0fd44fd4db9f5d98fb09733f61eee7a7c9134e40c19c9eece0a1fbfe83a2489a2168a6204133e4ec3f64beffe4bc2c2d19b30346161f67ff19f3fda7fffc3429f9e9a1e4e8ed0f2c4cfab11126fde8086b3ebbb0f6e30a6b3fafb7dc87765b2f8c6a483e1c1184c9191996f8ee535b80982f4f357081d1c375761f1bd4779fdbcc8a1424498eacf083d2d97d7062befbe4ba8fce272a84e1a314c2587aeb9314be7c90842f1fa410e6030b613eb1ee237bcb57b8db7a113bc20c911c56f84206e954e2fb2a27812b379ea0863853a3c8d957ba0ff37db51342e409277e5022052150ceeee3ca7df779751fa3d4771f1d55564e55b4bc0d7dffbdf7de6a39c615ddca55219714f7de7befadf6e2e84dacc2047643a2a8e8ea5e3f665c1d13b96483a24c5a5240b9a1f1840b14246cce42d550f29553abe556d73da2da309a1c4e365a4b1d2531416b48ac5c942ecaa1183a1f9cb22e09911b59f7f29e3c957921916d3587194ed986c4f8975f198f689123444a8e162a02f46002a33a216b2791469d664811694df82fa5e12302d00324ca860068b12c6fb602140fdeacea30901b2a541d12f62fb74714b9d0b20a0486325a86848b958a2b478a58e59ab9cbe57262855c4ed07fb9dd2e97eb042d024f674a4489aa99827deab29e79bf09d75e5b8554a0faf4eb6f5af9dde6bfb8d505b795352572c1d33397f5a4e404beba024b0618db1b5a1150e907fd726b390dfe727c2760df846b5fa69a7b2fa739f39bc71b75b382fa960fd11594bf781441403f3eab1e9e1d954e0ecebe4991a318829fa733be74055d4157505ee47211ecf7ed6bd6a0beb0d67a11294580b5b57e40f98bd6f779b5922052cac2564a02ee9bb330e6322ccb7213f0bd97f36b7d548c712e9074645914538c31c678c43ae7aea516f3d89dbfe594a7b0d6567395dbd5de6b93b0b6d2106cb50d68011229feab1c344dd03ce32afb332fe55f36ebb5409010298b4e29a5566b6e35b9859a2fc0e509f86b9632f51ad5dac5596badb5ce69f467130469b41eb747c35ed110b4e779deca8712e5d56715e49d5ff065ed69cfcb3709a6813d1ecc36f72211bfacb5d6596bad2d40a21937b62c1e78d65a6b8d4b33df8284d04b62d65a638d31d638e77c75fe9cbae96aad96b17a92672652a58c90c750814b14c552e753b4a358e253bca358de53c4a358daf324cbb114e9831f17c17e1cf39fcafb2ec0980d54c28d0999efa726d003f67f9cd6cf4bd117eb5702a621d0ca7109d80413e8019f39fe4ec8d5f596e7d7676dc61863d385cd8dfbfbb0ad5d974dbaad9063f3da9cebbaeebd36e9fb4a56817b86a6b5a5b1379bb35035669fcd655c86712dbe62ced98428d5f57d4d948932e5caf475cc9e873d0a250105e829c83100ac1621287a176092d1e2021a679b7ddf98a9164f8e6253dd9635011e5e0dfc90f9f40d33914b36314cae9bfad1a041430931c8a7066bb16a953d2fe7ac69c023378207d8376e9c4b128399782364e35cb6f929cc638e73ba667b8cd16c8cf5ade97c36f18eaeb89cc8071c26db729871cb3671767fa0ed1a5435aed65461fc599b7a537c5566f86d15dd02c0b03f9b98aec038633c03ba55ab5a6bd82b8faa566bf65e79e5c616638c31c658634c79a6a68ae672b42ca2d9ae5bb5fab0cd30985230c2845a9025707ca28ee65c7e7a9c9d7295c73c4ba12dedba443553e770a773d659679d2fcfb5ef5bf6579b39283be5acab49881865dd8b3aec12f593fda65429a2fbf626796f41f4b8fc7c686bb5a22178ba254202c418df5e0d8da29941da67575c6e1ab1290bd325128013f046788c4875ce6403827c8a641d45c6dcca6f599544fae99c8378e43b06e39b9ab3ce3af337015badb3d639e77cc6d4bc7a5af33701e30c05055163ad39c510c0a2dab6d65a6badd5e65aadcd55ebfdad3e50b5c25a8f25dc3cce44365cac5a7d7c0713679c71c6d8666eb5277ad6b39ef56c57bdb51ee8426586c5baf99befac9c72c25945801e4c49a83e3ba1b5fae4a012440ceddbad228ac1ac2c4b7b18638cf1ca1cf70bbb7ee35ed1b0914b693d4381ced4524749656abaaccf684e35a544fa7bc1e7e5acb5a7b76e367e18a7c0189be3a6501e27e071011e0760ab1ef7117f29de2faf14011da81f5c5a6ba2031ebfaf7d6fae96afbefa2a34313902a8ebcbccae5f598206e8d72608e88acb81ccecca3bc58d98d65abce52d6653b2c733c73404cdb3999fd839db155ab3b49e01d61ef5b2a6f7cb606c61b83830c53c9738e79c4fb0d6c4142cbf2f7bf8a31f50e620d7a60f8e5d29fe6cad3a624f389d33bfb174b0d95a9e2db797c7b7c99b54f839670ed2107451985286c863a8c025de943b29149f39e00059e691dc3b26e8540bbc0f79fa635e3d8ec907ec0739264bd0af4333d31531c895dad46762ba22f3aa359e012efa6c6ea1cb6b8f8f2dc2a2a28df7fdbebec2406d7392260894281f808492e8f307c204f6eb0f3c7e00a7d59ed4b2f8f4e7d98cf4dd7b8b6e8ba822104628e220e02d728448c1d1a20806cfcb5cbbd025a63ae79cf3f7dabcb77287eb8433119b35d725c6ef005adb94b0826be50b79ce7c73cb42b3bee8149b82e5576e15ec6fdfa594ee06c8bebc7b4dbc19946783d724c8078837ab2b402d79b394ce82c198e6b2b7e033af5ae49a7f34848f6b0ed21042fc691929149d941200e432449e023fc79a7925806769f0d93e186230ff470d80b9251bf0e23396c1e38ac7c726c647b56e4f6badf5e779fade6bab976faa0b9d73ae59d78a73ce799539ff3cf201996b911ce1d39f35219bdbc456c6822856ad371e41f3cfec2ec6d75c6559ab337baf4c28bd7102985b96c69c8ee0618cc907e04f23f93259c256adb28bf1f19523578e8c3688ba62aa29f53cb36b2d86746b7d4d5b02c61ff63ccb750bf2d7bf49dc19c6bd3e559b1395ca6bdeebd3834c689cab0da3c971fbaaedce76785ece1f0d415317a59a10fc4aa05ecacf31743d7cd61905a51122ad433af7c08febb283e0b538921e07c12456d9343db2045cc2073e73fb811ee8791f68b620bf364dc8f8324171187f2bae72006e711d70bfafa601d6637cc263153ceb710acbe24eacac3566e11dd671dd3eda46dbd75ab50c06b7af3068fa0a03db9fe0fad1f599dbb2e6dbd7dcf435dbc80a7867d9b5eccdd162d17edff6b4ec733bf69af75505bcd333c7db25ba3c2886dbdf14f54aefac79844f636cbad0b2cf15080cbacf3a6bd9ebb8b0cd42686df613f267ee25b1b3d6fae320e0f9f590f674465237ab88a461191dce2a2a6ad162f3d1cca1c06de9983926f881df87416cb3b556fc82b4798b971a79e637654996f41689380c33969c12652e96452ad88f8b768e5c39f2153d36717ef1d9ab46d49977b14a11bbe90a323937d558d00c5ef062e945d293505f203d362994c731c798d315148a87710ef9011e83f098a8a4ad7d390d01e4b80586b15bdc96063cb3af5226698a430f9a2f004ab32bd7c0c51e3633dd3d806b8c29946f86ae7e54a4e32547b84fd7e95679ccaf14dac2ae4b54bbbcbbb83a7186ebea95e61e76fdbdb5ef5be68d9424416b9e31ae617c2364e3fb7a6c76cdab8b4bc2f7d9bcb7d249bf4e237cfaeaeceb878cab7b6c599e2e578431c62e56abc755048bcb6ae21554cdc3329e3ba864cad7f3929c9911812900021400131700002014080744426198e66916cb0714800e6b9a444e48481488037218464210062114cb20600800c00000409129a2ea00261719f6457f11ff845c882bf8177b9ca0540df7fdf14db0731bf8ad33c5663b7bc7989b18a81aa0a67c0394638f116ea2579b3aba4224e3922142491d4f7be39998ed2bf76538e4c324e7fe8f64840fefc06629cf8453cef69a847239fa09063d1465f1d02b7b45c9f98c287b5fecf9c2a3b5ea2f21fecdf78af5e2e18466fc25375ca521138338b7f8c0f20ae63bae0ab5f1b702d28c497a3ee0aa520f287a32ed239d010abb393fa46312199c77812b980b2d488cf0e8411f15a1644ad5982958281197125ef99374a98c2179bac98dd59e253cdfb0c2769a0b1743f3105a6ec3f8abc80f6e9b1d7bc47fef8979c8550a3fb5f2d059b6685232945c627fa79bc6452a9567308bb5f41d482272cdd45754de11770763414b7787aea8c26f40d3e8aff74536fda1b6a218bfe1d0062643b0833c68d32eebcf1e658337ad04ba75bc15ffb09f5732c8b6a52a642f1e0d54f69f8a4131410d0d3b8c435df9f4fa77e6bd5f9134b1f9ab6adf010ee85dac1d50552388bd98869ee318393c989ae697c8d149d8da64401af1071b146981f9e430961864e7041116f32e1c83606352793d4d9c341a7de86bef7c0a408610481e496154bbf789579ef789636660251f0eeb257bf5d9810237b14538ab92143b0f1f8125ef3f47940881fe3ace1f79136b8a41130961ce6f5e8317328c1d44efbd4ff4b227b2a88dcb074c519c14b2630ca00636538523fe619de599d0cedaa8d606ef31e8c04a8dbb0396eb475651a278c348c25b5828f096e58c186f0612c9934e317448999d91ccac6f559c49a962577364cb9040367374c02404d49141ed72437455e452f39c9aa701af9635002a7dde4791eb6489e6fe071985ab04c940e142c14d6f04116fa068dd6421d64281a0b21c54264f7f23fbe37d3e9ac0eda39fa2f0f502f38ba2e1642e82215e9ca56d8b12c369d5a50d27dc819d093a6c229b7c0a1bcda078b4a8504d550ad0860ac1f79bc655272cebc037e4da345625377458ec5cca5d87d0ff68ec58ce5c874168e4274af35f143bb041de8c9a172f0841483420aff203a3a4180ef2d320d8364c728e6b1b40399fd1ecbb40477af03f329e3d2feb9fb9414a360cf9a69b60e44cb4d9ae2c74c12328660637cf48d13ada2187234e5a932c4c5097213a251bac2d3ecd4d0ced5ff550b6f880e2a114bb1d42ceece5d35f5dc990f1f1e02b9b767c3e5eb0810c1960ae5c5d0d07603373fc5633f8d2da430189faf7190185745dd5e9133c1881fe3fbd2fec02675667da79cb7b964bfcaabbe3c89fb2058a3b208afc8ac9b6d656cd6e24d92fb421dd9c3df745931d39abcc640e78ecdd76773c460a14de2093e453ef81d66b8d220ee5584911bcce017e6f9caa00c3ce26f7c5d83f9ab9b799c5eb086c34e083e36496d182129c6592a6503de9a08ccae0ace54e90c0a1c2a30b05c56915878a59029f60daf9fa190b1da7ce171217ddb1be1d1ed83727b06d134a9525ba07d006cbb0e9e280e10d51260609e28068c90548a953a17ef9f7a1af1328b57dd2c4ab7d7b3a5e9b65dac031f3cd1b23e253145703c7e69bdfa800ece04a5e2fe545b68cbcff1f747daf93cbe5bcf78d4244563a98075a27bbcbbe54b0b809bd31d84ee45f850e11b983259529d2e4ad7ccb8995c13d01e0288ba8ab0c27caa186bd257421fca6525189992e94602f11caa30f24a4cc7663625dc70195a215a6831e35450abca8a9f581029ee8254856e32620bc02bc9db2856fdd99e425f714a21574605ef4f34675fbae3a5f0693f7a4d2d7928c5068c0da6ed68f68f03dbbb903f22745e8e29b8483cc877adf5286f5a23c6919a01239966f8110739678bd0e041f5da5d16d7d587bca7c97fff7a2dfe08ce611f46229ffdf7b494ee7a83ef527ffec63e8fd643b6a8ff79bee40b72383d9e4c3d9cddaf747a88f85c57ae7d89b9f99f4ee9ff8481673033834cde911c6d3d0e34c2366d26377040c6a74ff07628d4b4deb4a530dbbcdf95457c3a4e1a0717f007984db8fb269ce08f9340b1feefa7a9d5afe8bc963243b2c2b28091c61a8c6e1aaad139a562fe71799195f35ad3651fc57ea4f7af265cd6cd1e244533720678b95916a083676902596fea31210e8c0c746769804678f260cf8443c92dcf7ddcc1e1672e629d2c7e011a44dfcf8478f456c26fc9dcd9c1762c634e03e60919c809cb445b5777df2d5843b425f6f3db1466e8ab7b5ca7bf8349f7a6affb46e94615728dc011ec723eef348f35411113fdaa962c781e2e201b189af082b31ccafa49ba47c16d27fe216493c92776eaa6c0c9217fd76c2b247035c64accc811aba50bef57767ae0dec03342da617762f687d82772b651b7deae36cbfa1cdc3a9be3f5c4a849a33f02dbe0a1297450b07ae07b748533dea5059071a96c28449558c7014b7a483d757568788d23a8409c2a4f61b392bad889ecaf89e54d80023c5a2ffe730dad1a5e23033e08be0ce2616ba9ec04e8533dd6124a0c2953a569a4b878e321f1a75796a899f30bb935cea2b520356c19e5d3a111f2750620c3f36529b60ff3c0e88c9679c7f0061c4b885617aa573e1d83e3d57c2f1110acc15c148e2ca45d5e1a45468f0f17a2f1a74b1cd91e18bc0577af868d9af2b67ec0fdf081216bdaef6aa70bbedc5680058549c595ae8ee55d714e957b131295e43e7c1d270b6ca010f6a2abc2e1ee18911264c470a998f51421eca53ac282a3c26f0c9fe013f9d95c00ba957a121d276c7f8b2668672ee7603f8f8c881d26387d9b14e85b86ebf8b02b06fc728e10fe3bfe33a4d48919abe33179bfbccf627407754b99926d6b7dc1ae8ff961b0993cd8cf8c08fa1d315a13e4e5e0cd4d582e4d5b2f9af7680cd7587180d29d48d4092aa5bebe0cda609a2df842e49729f35831301623575cb30629d53d35e36331aad9d099b2e50d08fc9c20c82a465d0cbf0646dd4fe62d4ab4bfdc145b94cc4d3ff634a897fccac470e8dc71c7e2a1800ffd2c1e570d8e97f9ba0b7ea41ddb8a07176908c6e7970f9bad740e93341e5d99be0a8732bd0e391edc942ca2b597043208d469520c8acf565aa243484a57cdf2ac3e6a51c875f36c4b251600bfb3191353269f603f32a105c4366294afb61cd82204e5612223df0ad59fd4de1df64f37bdc198f6bff326fbed62f0c82e92047190d097d88beb7aa838fa9346136876cb248ce9d228d12e3c59bf348a928b253cf937e4b94461573c040d4fb7ede8e7a002c4331d9c6dcb6373ae651a6a1835a7aeede040de973f6897b6375274000be1d800aa3db73df3f60b1705dca8e42a96cecdce833a3d3f4d70e105afb49722407f607a9c7824dfc3176e74f956a735372ee44a02b5a4fa8adb7f53a55b47117b97574b51abdff11ee8dfd20fb391741a320290d7b58b42a54e3741a43a0718a0514e83d8ddd9b73ee0c67b8df92eb0890debc3387fd3696b0af05e4906b9f85b0c3194564b1413a407f17b4de60396223a3606e71bc5752391812b146dcdd29f7f9a8cac391d6b1f3bafb89f6ad9ba3b078ebb13eec7b2056ebc8a94ccfcdab19ad9230043fc529031d1d47f3ad0cdc8786886f10056538d6833f6ecc81bc98fb4284774a0402d4b3441a8e08be6371933f320aa787c9c3978f1bcbe3ac144f2bf12a5eb623069323c8042ac0b228cbe37192a3c9e114c1ad128257ee64942e8e3c56c3b9aaf98a2a9e00a305744739d3b74e250525732b810ca7ff6700daa22dfc9474a931c3524fdfcc6f9e5a188347fb00da07f6403dc4a5a00ab8f22195a20b053a278fad923aff462a04e6e379e7f1f23b56aedd3d5cd31d93d3f74af6284561477ff0742b11367c072f0cb6311f0685568f8fcb84bb4b750b836f511a401176ee4dd90250b037d732cfdb572ad981253fb36560df36b74da03becc6de0e2ca0ab67843aad05e9483c7f80218a06e8c05faf10cbdbdedf4224702b7aa0abbadc6c8ba08a2e61c8bd684d1aa0acce059ad000ace9fce670bc4fd590526007f7ad9602958415fc93e2735e3dd16d528a6b3a2c76c092f58f42cda4f28237b6a95587285bc6670a1542872d2bbf40519a1061f11d7206cf504c527487056c457990a356dd21267f710d46998d3bc71f0df15f660dfcfbb6e6b451a7be56952e51f71483ffd001fca55cb978454194f2e22f0fa8dce2377fc4807982bc07404661499fe8eba5c9777db8f76c5552b337087ddd59e51b874dd072ef03563abc39797328299cbf815582f8dff8b287aa6257890556c140d778d57070d3e4689cfa84288d0898dc74521a3baeb6da76698dae12286b04cfea59b3d716e35611db31530238344b2b19d3f9aa0a54810aabdc835c758511e12766703ec14e5faa199962304a69574ddf6b4c3da72c5052c24c9d70bc7c21f08a02df41a8c41a35cb6ba19c35252d685a51e185447605e40047dd7f6e706760632a157943766681eae57999064c4f15ca72e9749055e2df87260c3647b2e4b8c12ba4250b995cb2bbd47a17f5cfed92513f0255639b3a0129ee8756babb55898efd66fc05adf7da19302406f3d80cae69c67a8664f8c5b6139997cb8ab92cc3237c3ae9a98c2a3cf0d4674afe8fff6d9e59f99d03ae8f5df5398e4c4cb22df500c0c1f71ebde26bca5285282b35e516dc0c00efc28545eccdb88429ae4b81f1b22a13972a31f7b1eb8931469831f681048fc5729eac0892c83a66a0a7e759f6a750e6f68179c94fe800c6997d6e2ea5aa6829593f431c2148b29188ef49578991f8bafacf1c6551a313ca562a0d007b5cab88eb72928e1b0b137338b3df26a3ffc45392e892934594bdf1c6be9ecf8e4bab1c8f07e569f3242e7961caec9d88c86e691da4d30bccb7e5e52ead52380676c2ae9fe61c5ef58adc12f7e49eeba571e53fa27d0b750c2bde7df847b86df30db11de36d49b8cb64eecbd0c11a578bc8860fd37033fbea24d5329a5406f75a0b2f555dbd356ee5d2e1c8789f63d5f4fe6ea5aff611b4156ec7b4bacf67be90f775da3c82f3d9456746ee3ce1ae474d6032d766e6c50db858e1a9882306c626abc5ac32b56fa540166dfb670cfdfc66e89bdc7ac3bff6f6de1c13310e2a109b40b36d20872ee58147e7947f8adb9291b26a6d45267f22863e9a08f5da14e026d0fcace79d66a3afc2388a5262a87c084d9795a0ff178bd7e4b30a5e38bc4051bcc37c6bb7b2603230a99ccbd549b7481238e1327cee34a991f988300f07a0d0a1e21c431647c245131a4c68d78a6b0928f4dda6b16ee67f0051d12a7ea18b09154b32691d9a191449b25d68167346c81ea243b96f588c181f396e2010e4c3b7fb14db3e5e7c1808c9318b343d809cf0577182031ad2be5a2a380ebf91e13b3788c4b475412dac035dc9da7c93bdd6881dbc3f2edff53569e497bea0788d5f311b5914a4e89dd6df42eeb2d1a4435740a1d76820948ac9c6d2f074883278bba7cdb34e19b961d9bcf6517332f4a470a7ce370c82d6bb4de1a8a1c10934766b90c74f1d91c76e76ed8fafc81e3109653248987eac6b95c28cef61506053b2921ac8c4238780879c9e853eec68733e15a481da270e7493ee53e0f4ca50a15b1f500b738346dc49d7d81061e4b38a0a4baceda651c2d02138d522cc8c97512240a4ed4eb5bf0dcf73d6d981a652313142223373369774338e9809cf89a20b16181f138798fda90a3765303282d88655962bae0737d892b9323b1411d13b85fe4d2f0585143545dea69150dbaec4863f43880a9bf25fe67da4b7c8b9eadf92595ff23b178da44710a843f92f09ee81ed484144f5ec3dfe11992e5c9fde6f609665bd6fe4a65a369b6530ac45b217aaaa4c24547bcbf359ee5d96dccce87ada3842aca1a30fb5019697d239c08db79fc2102cd014eb5715c972c5a2dd23a8fd65b37e0bba0f99fddaca81bbd6d8d454e75b59cfa737f98fb29f70a3d5157533104ace001c5b2a797ee08460453f0d1a8bb5bf02a8c606910ee440588ac2f945038542b74775d83393d39e4b6cad6ab002d4c8f6932eed55e57e20145eafbc5293e3ed147122c6036e484aa7644a0014ecc029771d9fa35e3c75c3e68de5b2beea2227e7be352ead373cddf59c59fe4d98088420bfbfbf76610d0c77e91aa34123ffc26b5d537c01ea36bbb8d9c393769ec2050d771be9f0ad95409ad87559abfccb7abc82d67625f9ddf96214199b1b24cc9d35c703dbc58fe607be860a746f67b127381169cd3f8c4b8007c5cb40084d06498864c002ed4884aedb228911fe0481406026a8754ea8e07abbc3026919f51ca820234471b463c737190a21979ba1364f8c69748f9ee9486be7472bddcaa102018527167c1438f4514f0c91f78e672a92118bf42a784a1152a58a7016d3a8b9512ae147cddb8abc6ef1103b38e254dbbd806ae9ba22340244fbabb6dd6e8c00e1f95a5c5884f92e711701bb280799eb1609c0201910738d874ec7336903cd2dbc2257964ae3fe0c69b0f1a4d3ddec167d5fd4c42bae496920bc7a1ee81d16ffd5a4b886b77246f84cc45b24785f27c4f06c4eebdee60de106b696db540e89683e53574b4434f313d00e21ba2a334fdefcca4271a4ac5ec6e9cf8e9479f3f20e4483be6f5008141808645e6d1ddba05babb2b98e3824ae72369a6299c715c17b036c120993835bac686b4afb9947f9c2c53193746f47f21c0d1d8ecfedadf5d4261dee0c91ba1c67ff2a52d444f4a967714110bd15e578c3a27f0a65a816d3cc9a964ccbdc981d72e56f6db0376a691cf83d3f4e382d3dc95bc63902ec406788b7e177a849008e750091a816fe56386fe55d7caa61ab91b943bf643e7ec40be4ded40f522aead6929ddee2765c91b0782acdb72cc737f85e627a1fa29651343db72d540e722783f82b2f5d637e23bcce292c464741278583f9a9a62d25c75d852174a4b5841515c3409ce2532b6ab992742ccbabb127a694d9e63899e1977565ffeb164104194188f4a29da9ed924a29e354d361787cd2714afd8c84d3481e6141fa98fd836060b8dbd07604a065e7f0a2bdb46cb4638da2492400777c8348b152307b77dba68d90e22d558d42d910d5d7f4d23d2867edae3e5547ca6eb610cdc3f804c998dccf7de5eedc304206e754a7cc393d6d881cd33586a8c41818c816adfecaeac46f6a51e825c1c57eb65ab05dd87c45b15f7e10ec7216795cab43758dd711cc43d8260821af2d9c599b87da3b3d4428638acc17091e550d296a8d36fb0453a38e5aae03a7425b883f33f599849658bccc2a0a1701de09f6f5da5b8a246a87bc2271312bc791d7230cadc9ee9c239aa232c49021c4de6c7aba563d7e0c3fae8cc5dff2736612fcb455aecd8901f84de196ab9f654eba0d43b1e937b754259c14c2181ebadb98b8ba669a5a678acc4c007bda276cf7911919dcdb44e46918ffd364cd8193fbdecadad9132d195029d9418aa6d807675c093557e6f2b12df2458554ce74fb99cb304aa7cbd43d11ee7e6a0e22bdd448a39ae72a03d1a49f8824ea51b7101664544e2989c6837aef3dbf0ba4a70e49afd36c26d5a992b1383ab4aa04a768545a1dbff5284bfa4c0b9954b781d60476b11c4d1604959b81b4bfa3919b754bcb1caf7b4adbd13b24c5dec30465384ee4e46fabb936f3404f083c09e3871c39dcc12100842cbe2c0a35e91c0ed9c81f820ba04544276e1f2f5b4443260b59f20fff5b09e60c7ed2a40e786815088dc02fdaa21ab54d2ca5ea2150ca661e2f20450cf38332c8b3143147ee9ed65e97788ce5ca1e86f256ee6133eae45876ae34f110a8472afb375256030793b1a05c1712958c6fb7e9309024756c385152f308724b970d11b42a043a584d5647471e033c408c7b92d1a288cb4feec5d20edee573b31916635c536501fb08e1e3b08fb143bce488fa55c39d0594a5f9f2a02386dc17a4207874b173baf4d173040c8f938eb0bcfb7da3487cf75e290eed654350e70981c70a3ad04722330a9bced19117740b2ea68c6bb6ef3c2c3d8c3ccf4e368009fe1ba8e065cc806260e62483865b21e462fac6ba91d32a50be78ba981f2357ee0a68f4377b448459d00a262b4648fd350ed4fe958bef2dc17e7a90e34ebfbad2d45dbced03d0b24e67d756c8f403dde5b780d5f426121db00dd4f9ad5f230515d39a9ab40ba24f2817323633e064c22a8c393dddbb5072e7e3179701da5e0d7d01004704b780b033d3cdf36e20240bb39d83b20152f4a84b3304f421e57d503ce87c09d90847511af204879d7d8d26d313c42644c9afdf1180d63694a5d8030a1271ac16bb81c766e400d900d075e556e2f9b2665c5f4b772d3a3e0ea415d9402c0cbf7987c0e67e30fe06e48e8469a0efd3bbc39c64e1199a21d7f873a8f2ee05cc3dce46ae14bb7bdd59670b6334c689cd2334fd3b9b1e1c72b66b2204e95df12503ef32b1d9db05241debe495517319d08f0b48c83a3ecaf6c457b3cbb087f0c322cf48c6c5cbb203514896726492888a934b42083180f727ada7179d061ddf38c6826c2d8411fe8d6ddb4c7647253d0a590c02e6dee4666edbf249249e74c41ad98f5cc7aef7de94015b1370f6f554508af2660e9285f0beb2d848c82fa802e1d786483694b3c3c4f97960087bd4fd60c09f43eacea5a50c313668ce0322ffdf4384ec64bf2f9838982ffb459b0f82363cc8cc04f403d02a06cf847de2351e9059a2c3ef5f38f5e639b366d043dffa24030d136ff6cd794f1673e3e81a50e732090da12c83959d224cabb0da0d1e78029ff00a48b032319cb09af87fecc5894baf4be7cb5d80909ba3ea24811f5601ed024a44d51a9587aec5858228588aead7abf6bfdd78fdbdbcb20337de95850b18507dc7eab96b93b2669f41bcfbbe0c77873a341a4fa73780047042c1c6d0690d3e2bd34575391345df3dc2f03bd6491372a6045ca9ec8cb66c6fe05721b2b842c65ee6196c6dc4b4a099689f4d1979e51ce994243b39af85d60210697c6503c351651f35aac673dabe5dbca5917162301703a5419a827ff58e4f3dc578ab2fc9436a6a7818e73811ca399865cdc04c20270a8bbb59ab27211c82f07ff04bbf955ac88f53c63db77d69ce84470bdc53abe7f82edfa7552a8c934218406a6093e70c2a29c18c22abf395329630c4a493d68bf0721e8b7d84d515bfcf38af1f750b46539f24b5d9508b29fdf9b4b4b459ce7d1689d2bba66896b0cfc22cf5aeeaf373ab67c9c0912f23e652253d7532efe5f36b4645a36bbc5dab59343ab48bd7628c27286f089ab7e8454d4f3d43ded1104b2116430d3b957c05884bbe7a48ce491cd0be762e6374e9db94a14325edf1fdb73519aa7c78bec02b1f349ebff65ccdc0ff95aaa4af2447fbbed8adfa535490a193c15f79206ce71a43f7cce120160fc88048d7f2b043e9d791dd1e731644dbe505ef87363e19177d54e8f9779d1cd2ea1d69cbdd5df3c5da9064c893d3d7c44109cbe93eef9f7d1088ec88f8731ddb51a01fd7e869c89bcb504d493771a2f89062d46a0249467871b61fb0ad26b8d3dee8ba89323fa5f8dae318ef7f962f982a89f5d7a8f65328f500138dfde51f60dae372fd2ad4ba8df48601fe903c2ed1ef73989c4469f3fad168962f9d38de521012d33d007c975eecbaa875290f9ea4b047a8abbcba7a5a43f88bd7c39a733c124cde5a5c887bd120c20ada1f44ca6f5c272fd8e41c92d4bb202f5941fbcffb2c3537cc55312d8c50c9a13c71a068a5ab982b157d41c48f7154bc5ce3eedaeeb4462936cab82e0ba8f2b45a6497db3829778100ad96029ef533fdd24b5f60249169e84cdf5b0bb09580b1d72f423a528213b2cf2890814818f8bb1ddc97aa13800df76037b0f1d0cfa7080726a5e35fbc1a9da22f6d2de81feb55bc16394ebbba03bf5ddaeb188d7f4671f2b40f7b460928963147d6bf6fe93a8b1480be87a874a831d9d1e730436572c00d0299e06fef18050a72074233652c5f7d737ecf4f84d1c7da080cad84bd321ae64e000e51f06a34306b98be6b007559d9b334ef68c94df4d5fef74b0ce8b2ce35d155253778f47d24291d0e5c25650a52e65aa70b15fcbb66f54536f7e693d23532c6ebd0e735a53ce22dc74ee67594f2adb694f0e27c50e5ff27aa537083f4524c11a4c2d43a9f3efc905b6f9817f6db069e9556402402ebf77228ecba62a09b0e1671100e688147434b8800dcbc7bb6dc50d84197be85cc5853784c7b8f0c5f762ddf7c89ac24df37254241e00533f23b28804a8081cd45a63baa74475ace8c94a900bf03dadf688148a52d55804b56558dbf790411c5c8d8051cf76cdb4ec6736e7322c10b1fe46943e43879a2326a3d45a55186cb2a9c3cb3e014e21f5b1a3fafcb1bd5715e86d48eaf4e18d918487916f1ff631a80ae8e70efff402c2bebcb6541937a5ab5ffafebbb35804a97b06d54b99c907e1152b96fe882f3621f1ceea1cf626383ae0be0e87115b3dca041924df254642ba6055357280b6fbedcb29f5e3c0e02674a5e5c3fa2fefec4d1f15c6917c2e380b91085813a60bccd53be100dc071a2696025105acf87631276d7d2dd60a5b7fd97da8958948a6d86707bd8bed03119a1ab0ee5bc4ea726c773348e9a1613230052cf0a4c31115145d76019ebb9c8257ec99f8f6a4c1df74abd5c17b9e42824eca0342b4c9996cd670a4431041acacf373287829d713c81cb64455bff2dd1d1a156b5b2cadd136e506d9e855b0d7dcd123412a9ced812808db8d84a2f0413a7d58d387c9eb47bfd03f622edc43ce6ce2a5cf7146601b1e5c4595e6ea6d1709de1b3217c486fadc8d458f4d019ae2b7a27e24f94945c5b90172b3b75cf8723c18cf2a9bbc1697b3e314d3ba40a25e247459eff379f52850deab0e1490ddf2064eb086c5cf48573431519ec2892fb6f900688902489fe740c6c610ee6656f0633c4bfdf5018683b3a6e565f0c42a5abce79b1be96816ad5205c3dc43b33e233268b9fb0c00538b3620f4f4702c7038d876a8f206c3ad5a11550dc9da9a74fde596d8eb379fd935e923aea4a33bdb3dc6afe5d2ca7b391a58a561d17859917f9c14018682f3847e1b28e33d452814c7d01497cd1ebe22981c9938407a8eee2f9eefa27ae3ee57186105e33a51e5cc4c37ca7d48e53c23aa5385224650b47ddab000e611e485271684e38dd49161edadc9a54e7fb231105a3d437801b8c9c9baaf9fe64eb3900509a277807f30ac2d981096fb06d4baead40d408683e53a198b34fd0d3540c7232cf2c5cc0763e1071279ad858004cd704baa60b1e1e080a5aa1831e0959e6c8449124a7a81712e36ae0fa8f5733623039fb97a70826d9555c0e2a53c99d311608c5536d58f11371fdaf91623a6f481884d48b2847f4c4bd4b455d6813d1e39ceacc7f9be6e38fd2750b25fb0ec6aa8b894b3b718a6065987d2726b816230db81840f528fc7d640769a6b1a2803fe45e49515371356a951053a64475460f5a193838aa5f068648cee246126e12a04a44cfd39d91fe37d09b2c3adaa8cfae4eb0ff0d1539b2f31238cefe7c9218437dc38ae72be98f978a1a0ebde22ba7c69ab1497c9181dc903d811755687006130d9264e5ae05fea219e0ebb643918f886103662ddf7a4748ca8a894be7886adce8127d080fc15421e2b3582f7a34dcde574d37f082ae7f9b5000d443586b0ab3de891406381fd1383768055acfcb88089f61a5263299ecdb8fdadc8e9ec4ec0d9116c520089f764143e2fc9273541015a72ffa0b92fface613491e26433febb71e6837176d7880ca52e2baa0ccd5ec35fee3c38b358f2ab14c4d11c7579287aed87c5719f1a6d6eb1393e1f6202be6e80588751fa3ec8da257de67b5d7c9f8163baa9eeec3dcde3e93834f409df1f2c66f20a637516372aae40fcb89efde2799b4ace86fd9ac0edc9c8bc4b72b1ce594d67d1cbc0b810e284fcfb4316f3680d7bc5b6e472afb9d44cdec7f765c9533320784f5bbd30496cabf968b7f9106102f8eb50e118694fd3e41b458f0bf5c24acc0ca6258ea88682699cf13a4c5a5df8b45c01ab9113f2618fe974e5ea4f073a9fcebb4f92f727489cf8421687c6f5aea076179f1cfb2585ce6eef32d463919df9b2a84cf5e69dd0758d6410ec11efefd7769abe8384375e1f24e205b6cd55a4b2de22d16c159ab1cc1dfce955a34fe2914bca999e7f265e3a227f022cf6e24b0235ae463358881eebff855e61f1aefba80102e96fc2a0d118fe95fd6479808fbef974db050febb1c2bd00af7e929233da6df492bc8b1bff447a8bf14681c07d77c3eb16a7b9131322742dcb4679d39e5e0d80ae5b37dbd8fc501b64bbeab9f65b256668c1b91c6ef4b252d2efe5856fa555aff19db2766eb2d3d197dd50342b250fe6b5aeec3e3282ee7e1ec94b3e58bbabc6a6031078f57d80536ccaa1a041a039a2d2c73b0522267b48eb344e69cb75f4b4abf4c9de5202d62303f3293032c2ed3609c95149066b2296fdbe08d4dcf8ca9b3ad160854fc5c473e3b2e9729ef2b229898f70cdffd6f71f99785a2b300abc565a3849299b478ce1196167f2c2b9a25b05a4c162a2a4c6993ef8864b1f0cf82a259236fe41988c6fb66122d16fc2f17f92dadff141829d5793606777aba6210a5e79ce571d99b86ff74dcaf25137b24d85eb684f9cce23280d00485983c526de08b75d5d9d74618fe9f4aeca413279e3e1d6f52511c5be809eee55ddd427e308c96ea74c3708dfc8950758aeee2bd1819a827c8ad4727f057c43a728da560398ac51a31f50c28cf9bff9a3a5d41fc6ed1fb3c9d0ba8e4b3e5907ba4666be22a5ab126d9ff1dd8c3e30e829112861f4be95ec1c3e33bff736677a36957d2081b854948785830fb5f14c69341f0c38c850f23b93dabfac81f8d35d1f575838fa42c0d1823d96073c9cb316d3ccb53d787344a962093908cc10bd3939550e76b7985670b93345ab2303750d00cdb5999af7b38976cad95bbabd7b8d3b6d39899c7ef1bc8765ff33ada86c547c94bdb86e28f22811cd0f0be4be5769b140f08322ebd11b25b6aab6b0d2917dd7cd99bb383c85637e529d73417929fc47bd6a6d90ca447d6bbed2819d26c36c6549bc17c903b71c02944933289f0b86a7736f5f35f4406121d08d15366a17dff7ecd38c53cedf71c20c83f69e3413ac684ce36e9341093179dddaecce0f91288dffa79ee5ac4469bc0c4dedc399ad8f246174405d923a5fef7341b620f79ceaa6c2feaac89a230847ce635af0e04c945a1013d9db84fe9cd53c4f3cd678c40f34c5bb3081a97e554f43142e2bb8b97a634cd4f6ad41384213ccb6868eb92beba603a05dc2feb916d35fa111597d6ec0020df2037062c2099144238e0c1f0b0bd36323032759b09ea476e3b931322009c5ecc564b6f908c085386441d28d5860a53c7c20780d7ab3f421ee54088a63e89b33959c21374f951a6cc2c3ea89daf7751afc9441fd75a30af9578b9345c435cf6fdc6448b751f7af2d8ab70d54cf636128d9e236e83ab5e780415ae320cd4d5f5ca8e383c6090d8d0e90278604c92e4e6fcb56c49980a2666fb0a8ddacb9596097dd194a28ff954b1f50ecc12aee91e10fe5604a348a7d5f0390ac49bd0a3b1c63e67abafe0830ad5b63d41b408402029004034ea1f5594cc62e1c5ea0154ec04ccd601025413b9ba87e1bb4975f4ac2dbe933b0daf607fd134abdaf848de03ed62d1ea660d528b8275a28df3d42b1a3f038d712d5012ba2ce7ff14b6fbf8fe9583d6d973445453f21ec4ce3718b53207463d3d15b4ef0a559b31d9d97bf2fc3705ddeb81763cd03189475bd7604cb107b432afe17f0241ba720c1c60f687e1967d79067e692cadfee3b3e6d04092df5af9307de4ba8df0620cd3c3e06df8fe5a834b8b9ed20988e4185d7c291340b2f0b11e3280075535fb131a6a18468c30613371bbd2a1d0ac25e12d80ff757a8559013df2352fd98629902c0949d7a629999ca8a57dfa4a4b46d8c74c341a89a646a807f85c5b6181523e7b5c3504783aff870ac345a0699501ca2ea57cb9a529516a79e557cf2c5775003d592923f8473f469b80e0b1610c7383f7a0628625e974501936d38a0f91428adf549954e974f75226e281c43bc7d111cd29d3124f9de6be6ca532158d40a863b0cf53d5733bb120bd3b360a9322f36e1e746b81aacc7f73745866265dfb90de935e329872eda37a97148ddc18d196714b2516a3c5db328e52490630f1486ac92a318d9d3256d29903910d0f911dac74c819cfc851c8bc74c3322c2fbf2385a1448a86b67561e0a1c0bf2d239336a5c2788f5474a675c7f25f02ffdcc0f67c74d29fbba5df934e323351cd877e87d492c984ea1ff03b524936b3aaf9c86f919aec8e4908ee8669a0e9d2817601078f213495fb337d9c1fe7bb3b8c00c3a03c92776d87547faa1bbb6cf3948d22c18e5c3875a9c880d2be98f27a343657a0767487e099fb21e26349465c854d6b9c04611c7c7f4b19c3d78e10b5a4a37cccfafdb00981583fd14fb556194e4431c6893a50a8712ac71717017b1fe49edcb8e50b88a2a72e9dcd7acd3131d647dc838c27fb2457c452b629121e6850936909d1beced7d118615b80d927a781751f334d8039f25807ef75b64288fefefb7b3e182ebed008533e8a9941c9ef905c8031855e39ddb4e4e27372b209770c518b51e016b1e6c2a52f30f6934f73e8a82351b0f20b88abcbb72b5304a18c6f5538aed6fda185e788bb383cbd40733e7a443abdc74337e535f8a03cf8d13e8c2efe2755c1fe274eb7e505a90b40dffeef5f4b3cafff21fe6f391e2460405704f8e23e36e67e940fba4bbfca10095eb263f88a037993914c13d68cf2efc31a7e1e2472051dc53c7b19561424f8f17dc53bb74891a8338a7816f055ff34a0263f3201e1f4240b25634a0a1881e0ea3f5d0f7c3768cb6e740e3f5f6107e87bd7b0026394a468f13e3ab74df66332c175789077b0927274bd84314bca0f06ebb790281acf853a6a2218dc54a8cef0808fbc87829058cbf6b070b2fb8dde1babc86d14e7fa5e0aed07d10602e6e7c964f50a320bc257eea376c4f531ed5b45f239e0eecfc4533e6bde838edc3fbcdaa1a7dccf87bbaa9b6e26b38bbeda0a28b95ef48eb64c9ae368eb3b594cf47e65b3bbc7b9297e93f28a2985e2837606fc2c52a9eab05af313a6651d3cd54630a6a4d816babf9bfc1d662fe4687055d4f5c9820e1dd45d504249d363b6021e6b09de9d7cf015f71a99f34d503cd6e28340a03d4358ee233cf870a5afc9673c5e214157c85287d9d4012621c51add3e6d8494305521049a81b9ab66ef9db20c6196256f113f0b3c3c6d03e3ace173f737e42b7513dd00f60303c129ec014c02f8f271121bdd353e7507ab11e3f4359d7ce9ad09e82f9b7f30daaff1804bc2387cc39a37cbb160b4de4ca74c1cc79a7710d307f217aecff2a35e96643449f7c7724924b640c03a7683861f07ed2a64054b9f76b01ad3c197d294bc705ea67f801cc6a5ca9ec61287b1ca62411178769eabd6251f71d0c5458377377bd5ebc5f99644d1a6c2260690f6b4473b044a1fde8792b313c0e88d40d07ef19afc2d8034372205a708fe98d67d290d953ca992a13f21729aec007678cb81b980457ec91d60b42a00825de54a0ee0114931734e9e192bdc1c8ec16b5807aba21fb2933e103671296a0191cdc5010dbe8eedf6fe3fb0d26fbbb88731fe17e3034f025d8d6e578d645a20d267478c88314e02633a1884f7b31f76db2f8dd6e2b80656223cdc40d76eb7e08ef22eef13176011f210569d2b5664180d78ec47da36abbd35c46066c8ac18781feeef1d4d095b586e75ded396eeb2a04f91a5deba1ed00d8054f9645216446d3c65d3c0a8132d7ba365b4954428ab3cf50fe7d224474eb719fb5973f19b7e5af81712734a4500edb27fab693b1dacb9e6e2b347c5023128f598e119c54f904af683f4f71b92ed75ac72a694d0d887d50b79ecbfa56a9dfc08e00598b269a61396091e9d84b7c323b9cc4f3d9d1327392f41aa90d9925b4d7d926b4aa698cca009f81d8091edcb32c205164e00fea45f08b0b11cd01135d29180395240bbd5c166fe6e728ea7e62030de8e3c5a36df766411e0da6e0001a2cdc5499f7b33db9f20907632755256d5bed0b723c9e1c6a10b3f173b3a4bf353e1c619ca668f3bef6ac7af2add7e28b3860478dcdf36a2f9c9a4567404d7215e010c21b3f2924ffc5e64bd257c9a50020dcd2d7fc1c64095bc2761abfb3ee9a8382d9a823acb9a4e0e20c38fc8a0ca503103bca21c3ae7a87a5a5bbb8a3b103830e8837500263ceca1b4c718f1cdec152c4e9ba71a313d33ec63c8d8ae1378a622f5fa4acc841dc1674dd805662f8f1da4100f7305818febbbe2dd012ee0dd69d4dfc17b2a190946e21565b93500577572904f262608b29b6e8d8f3cbdc182b48314c391d91c5ee6d36c463e161c60644e1087204853e985ac6030bac461c5992f7491f71ba22b9fde183e03af810d454140d28de6e4eec288fc93e16b8c792bd4c25d71409e70bab45e12c86a4b76b8f84dc574a9e605e146066e2501532985574f067475440b15f51b8060cea038e0fff133ca1f9860dc971d405c467aac64909176e1f131098cbee436e2f4bbe5db61772cfca58da77c982f5db212d11c0d658480e7171621528702de34f45350896b7dac40bb93ec117f7dff3a4330ba8992505d0871c1599254da6103ad6eab7621bec079ccf08897738a00552a1f3956a4c9a11094ee4ce9275edee57de26c430050c9e74d2277650212fc3945727213470ba6897a758fb0535c05dc10ff44022d93e916259f5cc3d288ba377d81487bd799c9038052bebf8ffb0e1d868d6096c365ea03efb385101a4fa58f01f5df0d731dc7828a0626e1bd3776f487e7c68eac0b009ac2c253417dafb51ba839c6b4fcd36a3da7c5de8a391edbbe610e8ed8a6cc3ca8df7e6ed4bc4523ecdfa864f847375f28075d87de475fbc022ffde2f86bdc9102de5e62da075dcafe421aa500fa17af4a829f74866f8d3604a1d1bf9a6d87775577436bcda06111313900e64807bee8bcb432e97496826e9f925bc8941f874b5dabad7202b6ef4211c72246cd69ed310d58184ac3b1da8b46775642578e90ec356b599b4b5364db21ab2aaefbe87839b65289f52a5cacb37a9389097efd74b103409558706bd511d479e32563b50fc7f15277e74cc0acbfd8e6b30a29721668ead318ccf3031ac0fd86077a487b45abb2b733bfa2acf9413097909d83ab536a053402886c4aff9f9b6592b8cb5a35886e85f5e13fafb4a87062e099c405f276944aabaa264891461b50765e8c78451071bbb673b53b1e0aded059e07e0f1f82a19afa2f1e321d2c0176a79be9ac23534f669600b461a9c37c44cca2a95f7575d80b27698b23ee7b1d28b9453c01149ee716e1f50e0cc951029b7b1f3563f747628a92fc63a1d503987627acc1b67f6cae85bb5fbda57b5f4e0382140903e07b9601c333f02ff649a30d6ed7877f2502fff50d9934f1d70674b44ce1daab342e622f9a907235c18d74c51e8495e7392e8082d6135dd9a50f7b971996a4ad7d223321c819af8a36ec6015bd817ba152acf476ca181fd880c726d0ec8f289fe7d09f2732d045bd4b820e5a1d7ec41048f960335298e6e1460a7b1563393e3366e8fe9ba9ee84284bcc3853f417b7348be081ee5cfdf246146d16815897356068e1da44b7b7e61137f115cf0780177e044c8323af325772b9c388e02db701c180d13fa95aef910776130242301472a42d8d41a6e79e231ac7d9ab6406383be0337ee53e8a920c66361801edf272334116aecb73e13d8db86039139841ef27ca60a1ce0c0f4447f0ed0ba03b680cd029e805f3180c7b29894fc6f3cf777f2a74497ebf61d8f38c560013c9bbbcfe8300316476007a62984e28ac2765b4a013f60689f7c2c7a8f56c024d69f8f61904dfc474bf29b5d83b15c4db09ee3ae5927d77e5f83c834bb08e65969d12a7416c87382e4f78b9c2e833d31ceff3bd7fff360643ad41533a7235fe4ece64f606b71ae4ff5aaea2c98de2e2356112c1ea4bf0e00ef28c8b2e61527bd73e90f7fbfb43e892c95a6020b5d5c4cc7cc31030ddd1c520e44b976ef97f6c38230c3025d6069100a3f3ab24feef332d147f114489ff0cdef79f8f67c784ae895004fb07b84604c809f0e0290a76887515c168be1a6d338930121a7ab850f05bcf841a4247c44562f732a847f7601c19119821308194dae28990578445419e68e4e9e029934a1f1e1985996c6ff141055922602803732681a9ca74dbc63aabc6da5a7e60a143811038148466efa5b277eeaa3f59db2c0bf51e489fbe8e3c762a229b325f2285bc783913417206d205a9cc62be0c2b06d01617ed0cb7bc24e16c0cb1704b87e2725a8423aa5088d64a2fc788aa242a81c5ff6550cfa071e45fb81338869fa3dacfb4292c487c0034607be2968cc70944d0dab796dadc836d21cf252de8c9a12cf4597ab69c5ee8f605af314c12182aae5a3471f1b0ae56af744503b591c343a555f9db9b1d6409f16d3bfa60d46b04d32a34ef3978cb7863e08fffeb0f0a64e108f66a8d8735c5e7068f6eee410e2dbe808de67b050d5d28f86008016ca608dac0020307e610c4660d600d26107861850146b6f1f41ce476d10e8191bf69ae62298716df4a806f44ec34ae377c7484203a7db3a88674df21946510833b932a94351b41194335d433144279b311983192a11d2364a5727e614b37080de935db7da85a7a5692ef09353afcfb18a9f289e18e7911c81baec0dcb09d4d2fd85c6d9b0a0e810dd4093eee447e703f5b6577be6e7f28253c8eed3973800fd813a60438fe56339af8139c259fffed399e7c421487f23d370c2ed6339efd03ca5dce50110e2b055ecedd705e8ddce237e796674bd08f51301748c143693692db52481db50d1a938a546411e8b4e9b410fef7d92f5d81c6651c1d65ef8f9111ee387963bf6f78a74f175eca81e8f0b1669a37e32bb44f59c0148bc5fc8e67b8274db33ba21142dbbc7d9cb1e4471ac8f1d6b89a659466f1296698d08230ebf9bb5c7e7933bc22e1e5305876e9be06d2c96b241804990cf4a1dfab1d3d3d93519583b1f485b229ac8ff986d9ebe259d066fa673f7884bdf68e117a43d87be659e03eaec1f40a650bbce05e305af28bb140e5df72c4d46ad1b5c70b1ed97a919013a99509599528d5c85a0a5a121b918d21f14ecae23578a89878f5ce48f2daccbf6eb803a4531dc5d0ecc466e129e4486c3544309370c296a68cf918ae92e1dcba0b7baf2858590cf4073b460cc63b5040576124b7208411113a87d0958200838bdc9ca66d1baed92b99c84ca8ac30c461d64b1041107a65494098f53c77cfb9af8d7d16733159adf68568764ed38fd239187a6877caf773dfa31d53326cc8b81f77a564b5f5cb801b16910fcdeeb908be1a9e206ed29bacad15d0e5642d3a1eade810ec661fb57b42816262890c10e211e3044172097683db61c273adb6ec46921dafa4d3d6252dd07bf808018624935d5869d57ae30ab66b9325792192aa2f6b10841e8caaf3ce0e2b7f87b334ae12e9e18f85b5e1eb88c16e59c1e2021bfcd788c7e5cc061e2789f9c659660336e57c93a395dd8cb696d778019fb04cbf50bd253e90f985c6ede2c77ea0db609f72a8436a0a880166200d2124ee58e5d2527c9b82570165143834ee865c519b913153ca3a304a67e3bab44b148f02520621f48850d911af1885c823e80c6de688ef26a1e6e9ba1dd130e502f28cb3d8f1f810ed65c3d88ed07b23315c4fd8fd4b1041564b0e72d2b0b57c9bfd73e73a3b68d57fdc166b0fd0ced85713a538a8ed861e68ea8768d17c50d9f426be3ac001026e06fa318818c803b613b0ebbe7168fc036c12d6a8bfe076868abc8ddc26b4722579c8522a3782b3cfdc7a23e1464eb425fa2d6ded34ee2dc8d1d06c07957656bbf7877511beba3d2baf482ea2aa9703b0ccc5461717f0d41d12c03e445ea415d2b0de21d827c7df49934ee8b49eba707ead10265501e2a57306abe7c20951fcd096a67112fded2a5bf20b968413ee3d357fc15ba0ce517ec50c37898b0a5af1e79c8283e94411a4358e634411a1ea8c7a2961e2d1f6258e1b403f62571dfe19da9edb8599fe04741e1879ac5b6de497ddd14d4d516491850e7dfda21b102c0ed3f3b7076b47ae5b370eaab339f43ecb41d0e294f3fd1b24ffc47a63a84b0403c9e218995aa35ab22880a397113beb2088c86ebbccc17cca86f8dd0997c6c2f91f680d48089d8ed3d9d4ce240be15540b802ea8040b481fba09452cf80d1d91e562d397e2cb3432ef57fe1beb4815f82b00bc6638b7d614ec78039368c1b63c9ba51b3c309e93565b9efc38b9ef3ba90157a6b29a4d43a2533b440e8fb4e518c28768c5b24ba57195b6f35580fb9f5d4810a9d15e7b9138062cb59bdf2d64a2e462c88cf877151b16682f4257d7e34617e55ae92327f1466330b26adce6165019195a64455c2589aac9039126c95a813438e5234c9890f43edf37a0233cd6edc2843b98e15863204453e03a6f5617643ae9b600fabf8199edc060ccebf8995046b38d31952a0e42827691d9d34c819045afea27bd17b374bf4b2ae42e96fb8082de730028219dc35e30994676ddc74af68a74b502cb6c2c9752446d3307f0f7cc2f67b0a58de038f445c9d13feec8d8fdd5ec0bfa2d8d8cb72209aa42a43ccf77820cc4b1b5b4cb1c407f11872f2c41533bda98f8acd5efe4cee63b3112629e6008bd004e0cc1b880c55057469a68b46a62d3b19e39ce412fe72af17a1fc52e625ae2ae8d302f45d3cfd0929b523d4bff75c24420903eeb8ec89d2b023ad60380887399ae3814e1e244b15a4eae6ec05c588286d8fd6d533ef3d528629a4eb7dd599b714f0c091d70c019507abc1da7772c3a0db8c02a34f668e49aa521b10ceca7aab674a332e9cb3c8fabc8e949f490b5f762e7e11a4e5b4a9eb0a65f1416566fd55b352d2998d5e903d7c9501bd60d47ff8de12b3a1ad956e6a162224624da5a994d694312c06a0763933f21c1d7677f0c4598c3811ea74a0c3429169eba5a1e16a1075cfb8ee822beebbeb515eeb0d5aabe0589c6da895e8a79d064724eb80a97c28af9a069316e14cc2e90dae878e3d77cd52c5285e5cf14f3ca2fa810ca21c5a4874de4537dc415175a2da9d45ada13d8d6502ae375887430d745654095214aaca9f28bb873a586b8d8998f8573bb56f90575114c41f56a086b3df2f007c6212f909b13426364f7cc73db2e045b3f110bac5dad69dde1056f2e578ec362e1fe8e430c982a93e5be43c2953f6869a483c7307ed8bcb2d86568d58acf963a0f279bf9d8445c050fc6f6b0f40e0ec8a5114d158d2594cc1835358788433831b7e76f3bebb7f1c42cf97ef8428570946160792c2784873a47cfc42aea1cc63758af78cee1132c5ec4f7a59c90c0f207bb22318763489570bc81047fd490fbc18a179845ef291bf28d87f0e58ce39fb2eefc8993ecdfc6f2c34258b520bae71bbd72973b415fab27f6a8237e6a69abfddb8b56ddc7d1a3fc076c8cd51b1db95dfd4d7d81dfb909009770be290d89a678e262e1728bf0d4d99a32496154118cb2c00094eea96f353cfc97159b5b0c6bcbaff00d33ee1246c9c6eb5529dd8b9e7939628d4b600f3444c2ee2da1b81e72bc4f577b9ac06b5d5045473b6ea157953d30ebdea71f84e0183586acfb57bf90030e3b94372b8a29e7a406d9890a2cf1675090898ba878a8da63455952978445f9da569f1ddf9d74840f816bff79218f1812708178b37bb9fe5537cd9572ae8606b9d8e66c0389c63ca317750a93f2c09f507420ffeb0951cfe06b331124e1d3f9f8f805e28e153d8f839affbeb900f26ee31e1f51880b008f5894f769227cde9bfa22b7a3935dcc668b015e918a710ca56e23b8b87f1a414369a40d7a8a28392543c170f62c734912017e540df4149dedd15ef87ec0ea7c24816168e301d7df7346d513af5d5a57e754f3d3c517732390b0b3783182a8170a3839a1ef83590d9a1770ed03f2f5f6de80c9293f54d746a979e3d482c718626a232e5af96d9894300f689801a0464072d37765fde3e43ee8043161ab2538985049f78e54169c80b7b12ecf81447b3fd852570bc043f40867632ac4598ae7dd5b4a4ee5cf2b6ca01017618142ab4e54e1f9a0a8f1d0b40f822fbd1e4cda4b673ddd488eb6a65467ff2712d58236839060b846f4b813e7588584ef46c2a57082b0bb41c18a03c55be8a49ec14c8842762a9875fb97e08bd358dc59c6298ed1baec17d62707459e6f185139dc50304a03e1a32fbcc73d2c5e8cded3af0f185d48edb1236c23b5f46a99a3b690b8afe08b12cae8d9129ba7c4f122372a3fad161a4cbea014ec7acba6ceb2a17821dd5dcb9816135ce5a367ca6ae25a9af5ab3d4d02abb0d0c1d426d5dd7f76f4f88a241eccf0064f68104c91de6e6d655e01c5e70801fde0af382992c9e93984647994e4d106352becc0a271f0af3447182587f72d3654570830fbce0eef0b006f83c1a6411f885e4b796a0fd081e9f9458867073b7df2dd955815c6bcd85469826b4346840a7513156596987bc16a067e133e27ff2c2260ffe6c12a4c08915f0abd8dc0cc6c245cb336506d3c73c69fc09df0420c18b4b45d552433c413cb3d4f0ad34f466a9b5b72c850ca3b05188be7245e17657ca5e330fdb3f8be85e38961814e7935102c466b41629bb0efa1ba371c6924d9ccb835d352e3b204ef74e8fb37295640c0f0c857558ea0fb3a5def73a2cd447de7bf989f9e4fec061e10308c69a05250c7c28185e768ecaf6898325e28626aef09f814de5cb974a188ec2303bcee87a8cd8d164f912820d92668f1c48889d741e84a8d6b0955475a1c904aa1b40d84875a9255eef74e103d7f438ae6da3775d62a8155ed200d4b24b181b279f08a29c911c53e43466d1d4aec37e142bb549c7ed0e0b6798bdcab1d29a4cc22af48cdb938b4651b1f68775e48cb51e1cb38f549d22c2dc5211e8532e0e630289c6f1783b3029b7f0b343d4d3ed6dd659cec02a9a7b179edb78e3a16443009b5ba851173707205050c6b3a55b7125a231535310df3da186e35336039c222ba7e0a6f01e30a37f9c50eb987ff6f3c670dc882311f47c7ed093689e4f6fc2ac64b5189706ec06d5390fcd213507cb2855d1ff19fd1ff32a59832261f9abe868de61e4d6a0f01e8290a6e5f43e16f0d664fd8cb2e999a0a4285602fe245cb6a2d57485307c0dfae22ba77875cb11aa1fbb8725b28e5a6150200a91560c2fe537e0e3afcc1178d1911f811741ce17a54409ccaf3366d48e492c9c62f1658b74069c31659b9eecbca9773a23e980091937f19417a70475a8c9773287bd0a27c7e4b7428b64bf7a52e219e4927bf6ea9811e3b7f56db5e8e2c2b8f6c8e43763bf4a77aab605c939327632250331e81eaeed5b1b3b12260e82f02dc5a066ca20c9e2e1c91436dcfc724557a5ded87ecd58ec192c647bcd657c1ec3f408e90c1fb46199ca90944ca603baf5f2925a40d815a703e6fa6af3d476d48acd493696812b04074a7133f589c833481cc09c8853bce21398a374769823f701da456305866128322ed4becc43538352ef0e1a70921e8918d1a1292f535596fc447665db7befbde59652a69464830880092a0994daaf7bf9dc4348af508ce7d40f3fc9a021bb62ad8a9d41e3ebab3cf634bcfa33ea8beacba81666b1a72fbbae721e8e2bd4ee3f900a273fdececa54dbc9fa335a21fd655648dfea44b332f5bbcec264a49c1a65d0c334f385179671af71b4aea8f6ec59cfa9af65be964acd1a380de4412f52ad368999dffddd889c50ab4099993f2dc8b2a66531f7fcdc755d1777434b7123bd6a29edc2a0bf3c50afba0be69662f56ad55db4143772b95796d55dcc97bf65dc5d58bde214b4e19e6d97b48935eea9fe82afb4532efd5eb513db529a445b8ac4aca4d77ff0abd07e5cf7fef2b54f7b197a68bfbaf218f62b74e5e187203c85a0fd4417c33ee4ad782a1e7ceb411ee83b0fc212663ff920eb31ce0a91b7675b990d3266bf7ec2b2bb98df5db00e6865b2a76f599afd68b2d952aea4b6bbe01b525e212b731afaee3faaf25fbd34e4ad782a5ee759f3fa41996dd88652b6d1bc3bfffac922dadedede24a6973eb52da5bbd0e17a6506322726e1385d84d9c6329126c9f9f16b1b25fb53fa306a7f7df4b4eb312cbee65dda430fc7152ab541b44b5f83d6c74bad4c6661bcac4c3f8500c5ec47d356c6aa9a8cd647cd7aedca58da1225121982c8104486b868e47c6bda1b3e1af9d092d27a1877efe20c5a6c13257bd03a6d314afb2d34a2d8c943f08df871cac83991c10cb6cb333bc06f88e6a3914ee45bf6868f864e27ed421fc67ddb117acb0bf9602664705e78e50e59e4d2e8dd8c31e50e697d3449feb4d4931f06913b800bab00c349183b61247133d7e49428c57f1a0a5f6ae7fb68979565274d9adb7fecc47afa6df3eb573fc5787f57a1d47eda3775d17f18cbd317791096a85d79150f366925f4b04f322c6c1695b760c85b51f995d76c10ed6aa05fc171858a59217e351b342b03b2f3baa4dc5e5ea96265b69f6f7d6f109856673f9aedbab54e2e6526f00dfa304d6b96411be8cf97defc1a520609dd6d07fdac3edc76c8a7bed5505ffbe9b17831543e6b8dc58bf170eb1ea6e59603cbc778b9eda8a5fa98479b547ff3bed0ad5feb6f9e566193543a0ffb181ecb8354bceb3366d8dcfc6c6a4ddabef36893b68fe155ef0bdded411e6f35eca89066a12ed5f826b548cf5ad26302df902f7fda48a3d2752734b59b6c753350a5945d898ccdbf6d366cdc30e3225bb54b3bdc3ecb387797d120c89de39ccbb8601d0e9adce4b2d56d7766cb6e65a4462d9a99b6786650a6ff0f972737e716ac633ecd9c705bf42d6e66ab6b7d7f5b9eddfc41b48c351b4b42805a2b7ce325a9f54febb9edcfb6f88474b6853c44bba8c3b35fc68c417fdb6c98a1294347c660b17286b61d54bbb919c7719cc7acf9fc4de53b27dbe2f65bd6833cfa303eb357f13eed761ebcb473b8392df6754ececa60cfcf56c6c5a67d37b771da6f9f31c76973623feb633fbd2fde6a47d7d3b76c5a7324d291700fa2dc6fff45f9c5fd82c02fee7c4b3ee7702f8368191719177cc37afa907ec605a5d92ae342c60b3a9e8248e4aceeeef99627b365dd96353339ad9f1f6522775a19ced8e6632232dab61f8336086057f2739b0ded22b9e5c321886cdb0ede3cd824097ad86534333fb0cd668530cecd3c194dd6cf24641bd6213fbbc9beb76a65b2d7567808d6b15529432bf6ebdb9dfde88d5bf61abfd69fb51d696ffdab48c936bd6d4498c8b6d9d03d5b19feedb5cefbf85a2b2cfb57b2cd0689c9b862257f776d3b2c9ee26699a679f06659d69b03b2bbd1cd06f9a09f1e7f7bf3ab673d8c4b6c08d631b72d88b6651be8e3b3d8f7527cee9b3399ebe567d733c7d9d16f51fea49bfdd8326d1f7fd37808f9d910ac03eeb0885ccc93974a78afebba2e6a591e826fccae5598b0245e2f2241d8c3cf34ed61bb68efd225f70971ca13dec93e7738e5c9ed3433d4a9573867affce68577da5ee7a8eb8f362f72fd3fbed57db47d756bf9cf393de77ed60b6999fe1fae6bd6537e6afdb3a65683d2f8affe7cfb79916bfde6d2e5d4988695f9aaa5747e6f561b55ffedd9fb5eb042fa6e4fdd8eea7baffc8675f84fdbeedfbff59f17b974be5b19ff9e74bea5749b5406c002c04e7feb5fb0fe345b72dd937373cfbddd6ffca7677d0d4ffb91c765bd6ad20cfb5191fd2a0b0c88ed8a9592d952ec4120a656c67e46d06ff7acfef6d657ab55ed37fbf1ad0dc29e822cf9524a49df7a8de96fffb593ab3de74d27d7fa49ed26a9a4d27e42e447af1bb1bdf6bd6a1778b577235807bcdac76d8756e4669e9c188661d845ad1bc137ac1a2c6ac20bcf843fe24cad660f3fdbb8dfac57ab4864f1b6d940e3b987ed62c16b7d8c3738dab6a3399bd1f82cbb9a9fb3387ece7a18b784f4e5c9d4f81f2e7b9f5f6bd47f597f3537b2300db71baefe84f427a4ef0b375cf6eb5be35fb05f5f50c4b9cd6c9ffbb8bb394a33f383f5f970fb2dfb4523f7a3cf7932d7ff709b41f1d2e7f8b924ee474137f053cb7e11e752cbfacff2e9d327c8ca345bd1a7cf79712bff89ee7b6993408d7ecb7efc0991a31acf3dcc0c918c7ee6defa588475f45b57e3b842151271aca7dcd3f03ebe341eb4d9f0c25fcf6d37681f5f2e4648a5ad7fcee2def3a0ff66d98fb519000b006b3ffa2fd88cc9cd40f2d698a06e90d720af6f3f3f8d19a25f91f12cffc522fdeed98f9e3762ad6dbc69126df93943248097613f514b15b1d84fe4167a296457ac8aed66bd32ae5e56267bfe84c8acb57804ebf8b8ff7af5f61312331a38a46cfda06f295588bc9b95e1fe7a0bc40fb21f5fced2ac354aadec6bb523ec414f4f3fbc345f2452594f9f468c97fb0b24e5e5ac07bd047acf03d94f48ecd5a52fc35d3fb81789b8e728db8f0334d7fadf3edeb40bbcdbc7235807bc1bced53c9965d9671976d97804dfa0d69a568653a3a6e1a40b4aee5f5805179e18b9a60babf842cae582cf2dc085556ca149156010a9020c2daa0023892aba00a58a2e2ca9a20b5f384b2e9c2f238cf8e0f6cf8ae45cb8bbbb732b21b6bbbbbbbb7323c1dddd9d1b09eeeeeecebd12b9bbbbf33b3717eeeeeefccead047777f70eb74d5507e640c0ed7ff98e40d478647e08189564243f66326a14df076b14ed485a1cf8e7c7c0b7de07fef93e24711b6359ea03d72daa49003aa8a60bbd902289284794c081157e509688fa996af8e1c3e7ef6e7a0a9a9324c75a725989ce4204b4f02270fc14014b4a9f485de287b243916841734cd6172f35d7fdad1e1ebac46ffddc1ad6136b752d9f3b25c536d1c7c7877d72487e6226ec139b7394f8d8c747ce1d7ec0b044cfceaa468b0c3fba675a34c06841b2280e531853b4cb57554b2c1d6be7d68025fe161970d42ecd004bfca625b4c9e54a71784062888d0aac6003a75c36d0466ce9a25a3aedf2718e9276611f7a615260d6641d68c37f08eb68cb5058967378065bb89e6393714e12f5631ff6f1f1c1ae8caa60d821414a082708418f1f4158f111b861a841bfc0e450f90c6a4f0f1f1e5bbe88120624e4bca91f5c7d2f2fd38aa1a6c2743de04a1514c6a07e704557abd58f1fabd5eac76ab5826108b547fe7c7f1f555eaa4e492aa5c5cd0544164c70050c47c000440c4bb4206921c28a01053b53a89658ef6fa59e647ab126c3a003beba1623a6504959b292d22ef1fd34db45f42493979cc6a08445e4ba4504e65041c8d0468c2d36a81f5c7d73ca14734abb444d5a31c54ebbc49fabd56afe989ef526ba22ad4c3f2aad8d82a18bfac1d55cd22e1f5431993b7e227989ffc95cf9dc1e93ea465405130c02b8c7c40dc312da5b82e0d48499620c49fc9bc42458a2a20288a4a806f583aa89d32e9f2989ee69a4e7133d95dc9629deffc55f7a242fa9309f2f78769adc1917f2ecacb08072736b5c8885145e7033d0ec3bea4183825fa35b487414ffb19fa1af7d0e10259a4f837a9397feced7fefa1c20eab2339ae9aba0ff4c120551da4fcc1bc5efe1a5d9430e10c5c3cec078697e46837aec6950d48e62b29495240372d476a4037dec63aedf6692b48da18fd9194651d9c3b2a31ed68f62b2b73e7beb19e5db8ef8bde10051d967d1cb1842764ea7b62a95baed849e18a46ee611c00af5631b22e603f193f6f379f013a3e6fff013e8e70ff113dfe0b04d93e6f554fbd8a6474d4df6dbd760cffd283ee869509aa77d4ca681ec0c67d9c64bf3b19fd932cccb3cd528bef6342826a3684738648f7d4cb431d86b7666ce4cc6f1d2645ba18df8978d6f59e93adc74061977a97ef63a40ef06ac30c048894a502fbca249915b73608e035cf9dfc34fd7941b16aaf6db5b991db91db9ff8c3f6767febdbe59e68de0733e23f8dcea423bc201fbec63b0cf6692401b933d676566f292e4b701de1e447f3db6799a07330f7aa318edb3d76c66316f149361965512be97e0530f7a7d2d6bf212ec52dd120699f559aa3fa8a2ef278c519857bd64fde559d6d7245478473d6afaf9bbcb77247f467bece9f53428df76f4a8a9c15eb335d767967ac9facbc33ceb2f3b929f3d0d6a6624ed5f2fffb2d76776c69ab11eb3d04b34281ad4d543d4a81927a8d586f492fc16cba3b8a00a42a4b080a20b224cf8a94b2f8625541ec58754342972bd63bc7478f00831e5f2e5af40bbf8659e9a944bca944aa58e407d104a4dbcc1c97a92409e21a4b835b08ebefcd1bd1c9e5879393ce1a449df9bdae563c883cb9e73f969601dfd382eb7e07e33d090529f592ebc22ca17f7833e5d4c0d92e07afb7ebb8cfa214f1057dc1eb515dd1a37f51341b1d959020abab04517a6a082a30981f41386810aee1cb4e900773e8cde0beb687b0178c3a7ede1a51cd7553d5aba50fd6189ffeaae8b1c429740bce4f34d9044219c167aa9513120513ff864aab4d9d37de940c4130223171ae1421e10f8e06620d812b56a85a6a7d12aea47efc582da5ba89f88a5889817e0a4e709502c9102155001359d0577f2000547b459798a80c253032baee8421e10c8200516c57902940f7cd1832941f80a7c408322361f4051247421cf9111cc0f98ba41265812c6dc410e082c8b074418913b6801938b9704713f19aefc2680800644ac5050021b78122e58e2ca2b5f241fc2344c07e0429e267470e3853c2e6041c442651d7c19708510520ce049e207d55edc02358ae9f7f7b6d54bd01d5edc8d225491faa19981e80ac391275081fa810508549208af8842e3600aa8e9cd349126523440fd008311a8247de37cebe77c4bcab7e6cbc93a5980c20e4a7a90083940496f4401f9d64b3be3508c80fa41a2e45b76c689d802158246c9e79f764401ebe75b7686b500052f8240fd40c140c99f7686a17044118e40fd000019a0a4a54165519bde2866529f9171051336a81f3a2da0e6d3a0a4378a910f0014a0a4ad5eeaa7415d4e2a6dd20c0e1c2b5452503fc415f84009d47ce9cd4055103740fd30830c50d3b67402784a8610dad8220445c841498f85f841b5b477ac4106797e2ecf12bf1c024725a9a91a2052281e937bd2cfa81dd9002fb538b25029cdb5e8b4c1a2b9d132139c46326b6155eadcfb4f96651977c34f4e663f9a9b3d895060fc6917af9f79358a7bfcb13c3e8950e2931825fec89cebf18965c5287eeae80aed9f4be94b1e5e9a1fa358f189e4e12720263f857e6a136427953551a6fc8b9ec297ad942b3f3e6917286ce34ffce1ece7a27e6279b61ffc4a41937f86877de8077b168b81ec27ba73cb3219d9f6343c084b99c8db3ec66641f6ab37b3fc218f7a29fb191ef4cc7e41443588e842ee41366bfbd653ff78a195e1ace80b22bada67f46314fa310aebd8ac8cf6525a9a9571a73646e11bcc57e7cd98f1fc8d9af17e8ac1a2eb697833bcf792976478d04b313ce8a5d82ef4278b257989feb434e435eac64f2bf6d9c64fddcf07325f54ddf28643a33aea31f37fa4eb1f5fae7b4ef6de759ec94b4d7af272e7b3177f6214c668b85ee102a55f102d0ace7e33977be9ed44d12b3b51701cb783935da712afe9aae0b82af60ba27551ecf093ae834a60143b2bdf792bdcc3287638a80446b1c34f20c74531a19226773e54321f2a990f952899e2ce874a58079cf53fdeb9df56776488850b71fca97c4729e8a9f69cf77262b1ddf6335dcd7e4134f6561e7a38ae502f1b44bbd78afd4cd7faec3b0fc715aaca7f1b779795e1aecc7ea6fb55dbd92f7214ac43bb2c47016a2e0ade8942621953a6944ac9492a25e76d006a94f3f83abca2e01bf2ab7784e541de914cf3b2bf382f8657bd147a7f6843f6dac7f042cf7d1653593cfad99c730aa196f5d4db00b4e148e88fb03c6dd2fc90c7692c5ee82def0a794c699334ad86ec35edd9ab306a355cbef2ef44b11345d72a3ce83a8ed175b4ebaaca579597af626f58a2f2d5ab95d2fa942e89f12a5e0cfbd1dcaed6e76d47ed2aa595769d1b7182590939b9f2dd08dabd9f64d890fdeaaf88ec57af0cfb7d0e9548a211cfe924a4f422d9e745522a34b693d2a96e7ea4112430c70f3f855e3e101efc04eafee577c6e28d78c51b717f4cdb99246c6342cfd1a04425a86cb392ff03e6a0573e103f712b904a4e710ce56f9707b4215f003da8920446bda6c57dfd9a7e508c5ff99818bf6247d8af3c0d2a46c6b358ff958ff15fb13349b09fa9ef8fd998d0affc8a9d017de86950429a24e58c7c19de0f2fc98fe1491602da909d8a4616b56382d96e095cf167216f847d08b3231cb8f7a7407fc85a5406bbc37ea47da7c2bc505f0dfb217e12fd685244912b3f8890764925418471a408501fd7b80e13f7e31b2da7891139b81f2771e56b19d6a9da053631a2c895df2d6917778d07cc91bd7c07c0d305ae2c6a21cf87900f2cbff270db31c2c19f7b1d422c5e929890ed5ff17c709bd91fd0867c22542f5d96963490477af1eaa13a0fa2aa0751dbc3340ab579955695146dd00a8a9f1bad385f9a14e9fc4f662f233ff999cda426e56b518b5ad464165fb35ff6da6799261fbba4a67d26b14bdae8c9305f82d4db1fbf879ffc51507768971ff181b4cbf741e44b29a5a4604419653ca2feccbeb9f24b3da8d64f39ab3f7c13cc111ffe01e00cdac3ca8198401b92d62a12f5443f45cbaa264914f7f889d25a45224ef5f45c0a737cdcadae6420c0124471bff635d96fef1ea3781be1d03de8755079eedd8328274d92bc6228f255e47712e505b421396f149f7b1a54a79a34c2a11ff431fda019edfbe3f6d9d3c8a2b68ded9a26499215eac73a57ca672670067ff9ac5a92d91eacf34f226db54ade09899ad4036d481a5ad48f53cf5cd32e9289f04d9330cc46a83529663d442229457e82289fd5939f2ba75ce922f768fca05ab63649fedca2faf7cd6c8c093494a8d08a423e9ad0c0489326aae8eeeeeeeed8dedd111b42dd2ee4318273bfd7e213f5a3371221832d9fa92b8944a29714ff23f995af44652354e726b44b435294e2078a274eac94f0a1355a3461e40a2480a81c9fc01c2b1732f1137d4aef0fccd12fad972f8310ab309db0e2caf722e0c9eaf1121398835fbedf84a19a22c50f144f9cb8d25a2961f53812202f8270e591fb7140b8f2bfe904d7036dc89f1e6412061553a4f881e289132b57c2a70695db163425062758dc568f9f38246049be123e4eaca07822c58f5331e54a8e491857dadc2fbbb1e9eeb41c0eb4213f00d9cd95cf5e076db0ca757658a10dfe3470b3fac3bd03d4e34d85a861c4e3dbdd40bcc0c924c928afeeeeeea69436ed8eeddd1d4d32947850fbdddd71f03411024aa314a265ecf93d5fa6bbbbbbbb637b774f1957fcdddc42a89f36d3f3c127463cb6d7788430c6c8d3e31b396b287081081318155a32e92f1b7e2f17da8f5e7e1869e0e127ecaa9d00f9596b34b40bbcf179bba15eb7523e7653fb25e06184d14192fb4b991b5fde1053a90edef087fe32c88d2f67bf6f0ee8d4dd80f7a0f265e0c6971de80a35e0f5c6f80db9dd859a34f21eb9ada1afc77f3f7137f2891a9defe54a2aa85072a50c57ea5041450fe47b6c9e2df8c116bef881155eb0048f912646e04b9e28b24488235c26aec4855f03e6809787656526a8fc96c5a35d2cdbb45ad2e3e1d100d3a4fe71f9dbb708d0c0b48ba36450d1e0f2e1b83cf899d8c2fd44b73f24aa0c620a8783bfb181e9cb8bddfc317e16e163188659f8d01fb3d404f52f8440006efc884514b43c5e9a444393fcdf7fc88cf511d3243b848e28a842da85877a87ccc48f3c466870e3092a17196c9194fe29c2484393a2298b3a5ffe679a2ffb047374a02600eab03e3e0e9843fbf82fed927dfc19da65fbf83034f0f013e8e3e661cff1a25edf962154fa9fa81ff3b6cf3cbe2d43a820be229863bb91468ba3499ffd670a03e4d5803e8ba6207e10314a41f511d3a462361e7165b8df9e7ad8859b0338db83b65d376bad650875de4f742377798b8fe3c59b0186068f8797620f42ea3d411bf113006fc41f9da04e5bafa834eb094416655b625b625b625b228b9245096d4c6c4cc0964f259543a3b4875085c6852f2a41ed72600e035c7f56029e2a8cf62349f150b0257151f9ad8ef97e4e04b547115e99bcc4d785662f62e23e8c32f9a94767ecc4f5ffb6132b216f7bfc9ad728938b44d4d61f54ccce24997981044d14c949a1b49c24fcdef78b3bbc37f9a9474fed113544f9d65fe4d8dc91ed7ba300a3363bf3034ab38cc44b2bee611d2ff933e9e9a18cc3459a448241fde00f0f94d6ea3f9d8a6fd991a52aa90c046c45e5cfe07f327460ffb15839e87fa12bab0982c340651f028ad26c4428ed5f58076679d0c03aa48d814bdc3e00dae8170f43691d03b4d1ffa95cd10a94015d2cdac237fa631603cc215ffb18bed1af591efe9ef7657babc1b211a1b2d73c1e7e02c225d6c129be4129104490f36d9035b8669100dae8cfae08f9baaebe2ec738eca325026df427711b4703f2f10aee656d3c005da26522dc8228dc189454be362bb8fd161227b80d05a481409c5970fb9b858090491fbb808bb8cd3a6c9331a9d047bbf0bd15e81389244397fae391eca17f66a9bbad52d48fdeeb04aec33dac45c6b06fdc764c6d66717b60d8376e6eaf2a788e1b6997691d4993fa9d7ba0a8ecc35a74cee5772d7a246cee884501465976e60714fb78a99fdd91b89126b9751dd9a4e7f206befec01c06b8fc3cc01c2c12823f00bd80cc384cf7257a3b006912333feb0cf1937cfe18c965d28bd89abcc4cf6c599e42786812d35c12bf48c4577a6c624a9945fc4c62a9d324bef24a3b845f7034899fc44485ff99ba9e9945a7b35df8196c543f0f300730c0ed375971fba9b8fd0a70130e24353eefe86e3a24469ac489a07e5d4e87a4cbe967af2be275383cb4cb17e793fa0547934cfed084a3493128a9b3ff0989f426474dfbd2e58c92d40e4993fadd76469ad44f62a2f27f5dced5a1465ce0022eb88007874909a65c88850b79487073797856f3b3f9b08ba981bc108b2d88b856a4f3392b2cfbc11e2d52518dd6485aa89fe9c29e7a42e4b5469a27f246daf3d56c8d2154d785581c8109f79a158a0a037fec5f2c91c87aebb18fdb0eeb337f4b88bc8ea35db09fdf98471f6eee26dffcbbb37e745191755dd6c7b44bdff93efc04b9f0331f86870550eefc08b48bdff93fda05f2b4a0e6ce0f32c404a8be69010a261051ee77514bf4d3053a71e13b5f86989d9e4d02d0c90dad82460ae99c735ea31aacbffe7a28332a6ac89b5e9552cade26002fcf97dea88617b54b4f2de49ea88bea3146da764acc06e88df8e1a88aa0caea6e6e7f660aee5e658cd1a7b47c69bb44cbeeee30423a44d6cf573783e3128eeeccccf0074be98ee4892825e3e83ee7f42c70fdf98817441b408eee178da219f4a2d606010b7837ca77d8c1103c59df967dafd112a537c739e9e600f9fe961646d47ae617d5b0d124117aac22572c42ce5c01f5223c91b489edbaae2af259c52a03edbaaeebe165290f9f1c2fc193057370afebfab75f9016ca4398e2252832410d898c506535a2970f7017b1aeeba256088caa72214f8ac94a166abd9027758415302ae8429e54147e50b70b79525e34c0881d2fc13be7b790786a8e28e2891372ba37e619a72b8bd12dcb7abac5eb611a0e8007d2a75dacef0fc7f51742effcfe1a7dca6cbc14126532b44d536b654f6fb0c66dcc9ba8243940d4f5d3eb003572a5139b88798eca6cfc94cdec066defb6741b2d8536aecbe660d992cacd3128a97e21fb10f83384bd1d80f490bd107a7bb850974d77c338f1035d0ae63000ccd1d71989eb5cc4f517a26d6a13a8c29185fa9cea4ff5a7fa5ff5aa57c5a0a4466e77ee88f6bd51a051da478f5598c779acf3017823889ca79a2531a1ea56b40752ca28230ea0b86fda472fdb764c7a65da74e95a865dd49a5182a165d845ad1965bb0e645721e3a8dd4dbe7bdab6c3a2cc9031ae8644970cf029adf196d7429261ab41bbd1d21c54adb6cbe6dc327ac724b20fbaf76d0f829c884caba9fc5fe4231c6fad791631794d4afd6393e8bc2416a1b66931c6d8eef123df1823e70eba82c864f4660d88cadf5ff422383b1ff10267e7235e00722268299782dba39c16bdfc890bcbb48d63674752dd9fe04015d48e840c0c30c000038cf61c7020a8651646af8b6299a5d18f6a1a6c972d720e0a5551e3471b372455eb60bb80b84dcb402adddbb0e4e851b69c3c2ded04411736e98b79e167d39ad81585ece5fac6be5ffaf869652189d4243923656812a5db11510cea47224999336502219530821754c0134d4d51d118fc502450a0394ba03846a03740a0292b765eaa131308d0078c004a319912832a2059028e6aa722f94c5765c241537ee2ac8025f9403002bd5902c58102cda902453285c640b5130016d4cf7455261ca697faf30243038f08ecc0838f2da45cf91e49325c503c2142df0f128102acc308126e7c7e4cb5d6fa82c3c781681113c407884f8d8f8f950b799a38023e810826373378f1328317476eb018c20d1657c0e066207ed8f2b7422c8cb0733fa8b2d74e853518be04800bf5ab17bae0feed87a9542a9583822f03cc01bffaf76bb9f0a36c62684db0047b3e2082cb76a77e14c62338fc61827187eefe3f1c4208e13bacb0c20a2b74a7f2c70edd51556bdcddd4d313c4ddbdbdbd2d9c0cb6f003b861ace7a7168659561661e699679e31866517e52cc3f8a2cc568471772458064be033a9e52d4bfaef7bac7ff49e5aa791d218194266869099b904f7333d71e4b328b8d584139cd0f6524de8016a11eb6751607a88288d3624a20ebf8ae2bb156b7c18af8f1fe347514854433e2ccb8386989eea4d9ac0a777a58232c6478f5b9fd107a594fa88e9d124f7e117ed1103ad081d428c0a13080308a2c26778858cd2c626511958795cf9f0b52eaa3f0cf272a53b3c414beb9d1eadad6248a9d7a4f2c3239a6647321a4ad4eee1165b1bebdb7a997eb699cccffeff90fe6553c3a97669b73e78d010d3a4940d901d7e6a5c4e71cd0f910f4e619b1f1ffafad5fe1bab67172bd20b33333fc6cdcccccc922333c3740cb31126cccc3d311560d2e3c50713263ce2085613062968b2a2595c219585156c322baa3f0c3cf580a5f89449a5a61713c7faf0648acf2b8e429a14e1cf90214d8adfcfcf43bbd01b7f88901d7c7bf030818518656ca246164419148931c618638c33ca1863af6a4d77f76a8776e90112df638c31461e234f5c9dfad196510c2a6d42ede71d70e4cd54eba2c2171ecd76070f48937a2503ff07101a38aef031bb0c036917b64d6915c192056407e8fd80ef42dffab103ec013343049ad4ff3bf8fc85f02184107633b91ff4f9d12eddb08644752621d571ed2b211d242a954ac52a24acf213a5b58a4452b2aaa7078853f9d0ebc0f22bcf69d1c35bf08a555e8242e78180515c28c43fa18ff10fd5ff151a59549ec2525825bf913cd12e11e638c2d9e080fe26a74693629402e8419daca36d7a7ac01cdb95f4e1caeb10fa153be267791a940e2bcff231fe2c762609b38de96779163b0382e91e360001befc394014cbf7b0837e0f16202e69f45c03d8a2f6c0019e30f0a40703026dc8ffcc593e26f42c76443fc6d3a062567e44ed0807968ff149b49f61d13e067d9a4d9b19a2795cb3ad78407e421e44ad14016dc8d758c54c78097c72d1a77f65231c563ef4312b1f0afd8abd6c0ccbc7b0332bcf426df47c584942df87d07be8fd6342d69fda98957eef4d304e7a61e29c88a3577348600d6d2096e67c07a4d3d3d3d3d303ead10179cc03ad5d0ffce97ae00f48c6d7b07c8cdf40efaf437dee37272b283fd5eb7a403e9b17d086dcdc7fb8cb6079cecec47896a7914505b243a773c11c4736968ef12a2ffd25e3d426c51f24d90f0f30c7bcf287b0bc54652e653c5bce730ffad0e0130feca2df5cd40faeba1e6f03d0867c1e7eea814bac03f28d8f81805bb8f22fb53c401bf207e0a47e5dcf950fbd848d70e0429ff106fad057ee63405c65f1a027c30b7990c58328191e44753d7e02d9117f773e7ee22c08549ffb994e42144a877e7f1d583ea4d5a488dcb0783cf41424a00d497b7c3a2d9a84fdd7f5b8d6241cfafd63fabd6d8c7fc8ce447a33aeaa80ba158e2ad12a1c26f831c2780987a765d8949f2675aa53a94efd74ca4f94d62a12fdebe0300ee3f0f6351a63af437d2c2bd2379d44e774f5ba9d8036e4633afed8c7f863dbbbf6999d01bdb6a3a453f2f9a75da44c81398eac407688f622f6619b1e91fc18c2c91807e6d846fcda882de869f8ad4ef19cfccc847576788a253d5b3cd16205e5ca9f1677aa6b463864ef9f841f6467409f3d6fbf79206ff28e9fb49f367bcdc33e631cd6d13657fe16e33320b723fa30463bb23d44690fd33a9be63d0d2c2aefb0920128a10aa00775de8f7104d0836a5dd65e077f4d07ed9734899b34a98775bc8d006dc8a7b453910ef9b9e1e12529f9a66130989726c9c7a132a964572ae44b5869e57a0b39cb0a922da75375aa4ed5a9b64e95b3798cea547ea2b45691e8bf534114f4af5f137ad0f70887ed575e87ee59be3b2dba9e6e8b2675aa8ec9951d14a00df92060d4f6f1674a0ca18aa8540c5895490c15a211080000003315002030100c088542d16834cef34cf30114000f87a250684e15c8c324c85118658c318400000000000018003344844d001c06bdb14274db1c07489a5bb307b00ce9fb24a2392b2aa3f1477e452030c00ca57288e3982ace1cf6306b9c76f03e3f7c19403418d6074372274b96f820e7943066cb51e3c72022b287638a4e76564984baed0527e47408e779ea9bfcb020849342da3c3392bd745dcee565382a2acf9e29d92668d863abcf6260cc48d1bae6214da402b9abac80c83584aae540c9eae7448d00bae5cb8d12bbdec245e0ad3cb44e6c9742012667e05aad89052074075481b0c12ac6a44be1d6e16b7714bb34868e11fd53a7e9a64e5a4fe767b819c20383c00c1170edc0a913356e11c0dacd32fe015cf301b3a73beb4317fe0eb47a90d060586de4eda2bb53906b1c8ea5efee03ceba9d2a8046666a659d26642c2616497e3fbf7e2bf9d0dba96d46b4d0f8cfd51cfc0a10a6ca795ec31270acd59f4fe322ec42c0537fc67089dc30ae83d33a81c9f4f518aabf368084172d9c5e2a770b6f81d4f3e3eff02f381146e907d3870ae004514429de22ad5bdceb65451d5d91beb9b50375ea7082866d5edd2334f45c5fa2e2d428fe5f1e3eb90686739440d212a39fc5d91ad6da67d55d008a6d075377a2818a56e7ea9ad222d5d216a960955892b585ab798bfff6fde969e6663886e90b2d927d44587dd0ecce8003c59c247705cb55fecaadcec022d8be52dc8c92759adb128f37a3986ade2340b7fd44f0bcc4ffe7c0335ebf83c419d5e5e740cfcc69121caa6b2dc1bb914a5c723a3355916b71847c628b7758a4150b69f192c4f33e9475a5f741ca99cfbbca88fdf5445a675a9c6192a311905b16cb374a5695b0af5cbaae752dab170bf56203bb2d7768cc958eadddf8544552acfd6cbc4f6b555b0369dd0f89cf8a6da4b2f083ca133d03cfcb1ad112a61410361086096eece32eb1c2edd214c5343ebdf6d5851c40c976e2d933dbae0bba772fd96b8c2ffd2efdc0d5d0e3d768f93081ce944e8f1a8c98bed6d0466505fc73325c6bb6e8d85deadbc417557ca2aa80180da6b88a96471dbaac777ae0beb3c82134b0a829ed4e81e61a61548231e068a8d3889bba94cd7a824a8fd89f2f4e91777a0243c83137c6755c4e35ab24e4e35de28c432a74c18564fa7bf22215427a040a02dba9881e7c19cae0c511a9be65485526f70bd5953b995931876a7269b4e8d50d5e8ac32d0bf15478538c011b85abdcbcd7a5a6c084598a4800d4c0182d38a93ce1190b140966d37907d76e56ca2d620fab846156c827ee0868e0bba3f1e29bfbf481ac4bf78c55aa56370fe1ef266ba0b79f86d73875fcb68df18e5c3eb3f295762b582363850c0c8bf26e044b90536184a10df3aca7d272e876770ca319f4c9e09d52c9ec3710916d7447d7e9a8471ffce734a7563f5b9b3d375a5c839683929ed7ea5f6cb6cee97a33adae543c29c705674df4b8e2aac96b2ffbc9e3e9e65f5041364b7f0a9a0cfa6ca5ab9e1b49d8e62f272f384108765807384ad9ef113cbdfdce191bf74a8d64cc907bbe13850ac79678cb54d72571c449dab78a2719ff8c1e609291dbd420cf10f4a6c554528f207ed084d3eddc918de42a4a17b982cc48c2944b021bb78d1ecab2499e5347ab26f10b67b761f5d45cd8f7ae21edf651e30cf6dd624baa70b65f02523d1d2df596176de69457d32ec4f0b50b92b6ab1906e0cff9a19b58281f558a1ae34640bd1e261722e219a7c744ae036bf822cd6c5c15e3696a40309e3497e3daad7f2d7d4f86f4a45cb7956b8c3f8922bcaf6711dd8e8705a2b16f9213419bd7d80bf728a64cfde6b9a98fe1dafbdac1760a875b9f0cf1558813aa242fcbfc613a1a23fe8a55fac08c94b1eddf7d5fcb1c73d7f17b1f373fb0d2f6ede5b7fadf287079b82161eea238ea829785cb0d73ff69fb619f920ecbf5c68c6ff45b3fe83cd83bacb1acb5a214f6535658890715f1121045133054196151521bce40de21de56d2d309f57c4ca1ae813e0231cb1a15c405cf93de769e08037c2f18d88162c3449146e1e877602c9e8d822bf5511555c89eca61d5564bfed2c250cc4cf3613d3801d003042944462f938c0b1905896ed12ea3432182b465f15a076514b9e04fbb41f2b561798e87d8d9a39435f749d4359726e756ba7bc9611ddb241ecb94d2f40e6ec209ec7c5a5cf504f422908e247d29d01b24587a0f68ff55254906ae29796607b5ddf1915c6a1dc456c535287bc2ba9621693ef22868897f4705d25c8e276f991bffd8dc2680c32f01fcf4708d196abb1f5b0d604221301a134141b01b0679b8d0348432712e397bde5f183d808666d254be06280583d500ce4688bbb10f2957da02cfb3c75d27e8ec935284f291ba386f5ff5cede3a7e4a89cdb284ae07a228659be44166050e4a5ec5bc28fb74f11b885048da76594347c12527c3c144cfd9760ebf8d77ae0d59fadaf7fbfa109340bd2fb8a604cbcf8925782b0234de40b13bc22b7980369c4939495027fd8f1da20bad1cd8ac32e4431f1892227c4b6cc9143feda425c0f6067e5886c2193479a3258212d3a366d6d7db17ac464a0f4a2e1394041920543ac9873643fb5bbc05d308308ff3a80582470274d13d1110ccbf46a38e133d359a1be5a9c0174aaf1b18359cd0b686740cc41f10c5f1cd91f7d5db8dee68e34ac29834b18679edb34ad68aadde2ca44228ce805c1f3de1f0d7a957dcf55963e70f8205146d335752484dede162b7e492302afae4779c0c4a5b0e94a428eb474ca9b02d2d78fbfceef7fdbf2b19ad8321d38c7f234f942a7c3c0d3f27f62e29baebe11582b77102c3cdafb4f21ef22884b023538c39b2f9f50a6898803aa83208d8de00f3fff7c741de0a429b8062b0608ce38d863a800d4439b2cadd8f26680de940d75c01f6e7688009dd1f99e4b273413ec964b6623302aec71aa1ee790ef3fda50a3c35cc95cab59f873974bb3a70642f813ab47e7d9201d54087c52003820aeba9b1a7aed13d3bbf20a2ad02acec93b2685c5fa73d7cb1291e19d4bc826443219a2f6e412377f8d5aafa5476860499c28a6fadf13372587381d9542b360590ec5847451c3a766af300293ecc1f40e3dc6206a6e6989947aa5612f327a1200780dd807a814d03b2981ae522a6f561100ab6e028a6b8040a00b13e2802e32d8213f827b02340228f9788a74081c858c3f4c42ad665793f81492844b9ebf28a6962556d7fc58c44a573ac5d46b8f3441bbc5a3b309f00d294798160028e32d2c9266ed3cbb84bb98467aac6fc5a1efef8b083e6d41c702fcf5c37d13f050eef77f10e2941e09b60e15004f1ce744ea83180db39ce59bb472df3e0c1ffc55a5e3010f48d001875250ccc097bc4ac72096795d98e091ae48d30e819807e99c7b2cd7dfe37842c9e250bf2a0684148a74c4cddfb078695c6e8e50bc45256d959da52e9f4b17be1471837abbc2b71d124ae2036f26fe31f5575fc48af7763b0302a310009bfeb9c47adfb7f51f55787a9e98bfca4786707ee4d20df6d2901dfa1890fc634693f02da87cc08aa9d7c534b0f0decfee0c024fcb1cec2ed146c0dcc294292dea21baab8388aee03e9f46caed2039483cb3740620a6351e0e10045cf8d517214cb5fa2a5097e3f6799f214416aa5139bbe22bcd96c4f676d07140837bb57b6cac41598ed46e33c12fddf1f942668f5199e0913ec4de8d36e9bc517512c9f5532ec621fa50c21fea2ed23c5f4375287d5600c4c10366c60c26444f7e172e3c1d3bfca828325687bf670dec46ddc5d5bd72e06262ab3139cf9074a3ec2159fd8ddb4a5046295ce34cb300cebbd92352cdd792a4976d359403bfb66146337fe66dd814c833836e12545e3841e2800582249384347d223f4cbdc5bdcb59b11178ffc6b97aa6deffa5b48e9c9ba398daebcdf725bfe602b358fb9bfe8f3b7f5c85f7f8c9e08650bcffc80264fcac5ec3471c550476e40f3b41f94233d4f99e3b99843b5705373e76e868d3b4d03f2c8437b103200d34dc3b716ceca98c2317d17a8642132a0d2f233c94d8bedc874381b7f3ce2ed5dad6cc3ea59f8912cb8a9e6ecc9810e0080331651b58a7b0112de8794146fc1d9d28761cc7f52b3ba7f856e49839b832945a3816634f62440d9faf80872bb144b746970384bdd6d25a30f7e9c8a64933a0c93339b585d4ef2d517f08593b90d840ec4c7cec5ce8db4525571e0bb77c1022de6a398dc99ce7fc46500c626b683deab277f0a98e40f0e3aa92205a4cd01a5adea897d8128602cf0288ea576dd5b7f11d781fddfe9e552e4c5496d72f2d343d741548fba06aa96106370be2b1f8f6cbf1957a20745be51fc753a4c35bcd2cefb451fe730329fe37fcb0f67bd8ec97e5e777121b1c1ae17e3a1593232cf89078689c7486dcce1f2849d29e6884e6ab1ec750961c41815dc64491b14200ffba3a1f75f7e3705c60f90ce78a41a443acac847faf8f342a68860ff5c3f8a5e614656a987aa46278b136d847d3554bd72bc1ef364a12f63a067e8a7c77be10fd3f410ad54844ef077171427baad00c44274cae66fb4fcb27688246eafcca62244f4ef832bbf94769a10b372e7a4009257cc27917729f5d7a742cb28fb6d4272c15346f57a0b657e23a08bbca6c451f3ab09f23629fd6a8e71b3c6c4cabe4f028dc948f31fdec4834147b35fe965961cc79969b939a570fcc1e34868d30dbf2ad37503ddd5df1f967ea50a7ec574f918aff6997911a4fe40fd5f9d0a2fc62bb1eade61b2aaa84bd96bf5835e11977d5e5300feee0f79d2b22ea12b476e34e15a97687ededcc8a3fca578b3a8d4eb8f304a41586a9d5cffa95b2d9b54a7fc4e1bb2bf710853914c3e311d05d79871933945781da62c70525e83e32ad233b48ae7aba30a6526896b89471333c47b05826d3a143b380d7ca6e36826cb94d4f933a7e2be28e39c29df73188bb14b079e147496335d03ae47c1c700f76a60fc8647f49e0672a19282d14edfb6f0e921f9e2de7f148ef032bfb70aa0621a6aa637fddb3d9c04c24a75a3943e2e01ef08b544fd1a672466538162336dd17f2c54694017654d9b9d3e6912692311b48e4d2de93ec2202a049e49df637dd296d59057fd6c36533d3e648917cb1db15210066c668dd45123c87daf4d3251595bf72b010873c7a7183cf725f8d311650e075a8ed099fa4f938497c383d71179973c9edff816891758c874eb782887556f874884b4f6ece3f85e28aababff650911653e9dde3a8ff59011905b44848b2366ca294e28d96ff3d58f7ad9495262f7ede2aa28d13d8fae0695eee2c197e838be0762b796db77717f3d9f8971a95e02ef36c40201bc36797d3540e5ef1219b233ddac14adae5475355e81fb4d0dcdbbfb2ef1061b8994dfdd05404a89941461c1836d4f57b016329a55e8d5717d1987c0d24ccc52abdeec04761b5e2d190ccd811ca40f6ec56b87060d13cb8942db856fbfe71527b9a050cacec34ccfb20304502f867997c049e24e6c042d48d6a7251dc96252a02bf160e2a9ebdfed8517f4fa8822656c04bb9dc1dee9f8e0fb0a396f6a58847ff6f05216f779b04e487b6fd3eaf2337cc4fb3700fe728571317491d158cf992bab9a58b0862f23859a803c8b341a3f11b11bb7f849f232908b35a3fb493a8cff721f04451a6c025a71f917780006ea0e39a79ab45a268382c0b687a0e674dd946e05cf6616e628215fb3e590d5883c1d5003d378fddaa8f4cef6b5aeba4fb7648c335fb4541c01065d3b4101ca68429cfe19cf7c7c89eac4ae7296b0ba6c5bf6aad4a5a36ccb06de74525a90bd3a7c91a45bcf11fba84ab1fd1690b08fd997e492481d016f0542f4f219c79545775a07e715041f0461d3171d3dca274a2f03f38cb95670d8a5214f02f058805f2e9a52a3556d8524381b6235877861ea0b7d1545130445de6fd4895920ad92550603f29a1da80680136c79398d5809e12b256b4185482564c4106f82070bc22709368a375475bb963f95f85020f1ed41020759f87902c5c472ab19d1754c6ca72b0bcb35eac37736f1599f3fecf8dcc7c63390f9f46254834ab376883016ea382debf93aaa2f7c521bdffb583db9250fab6a9bd1838c608bba271008066172b67b474b5d70d7ef82a28edcb58e55055654772ea64d00c75f1e38856a70c23ba094b8eb41ec2a1552aa888b44555e9f5be033a3b2891e83782ab1d246bcc0b89377f78a4c561f22aa51180a3d6d682dfc289812dafaa5363c0198588703dc460b1e1be552904573c88a80736ef33cf9a868e2d3b962b202ee220b6b080f1b363fbf4a3a6f3831321e7dfda18892ade529578f16aae54048761200c42fb21476859e322e7cae0b811545bf6db9005021285f083421622cccee6abfc7570475544c3c24eb84f594cbe8e24c4049b3382198cf15eefc638beb0469ae1d379043313c591d3fcbc944ec543925d3c5c7bb8f3ce08b011667c211ccdf3b6575808847c487cb02c9ee9871792cbcf1641647d2f5eee3554576dec717cadf70b66afb0b5e0a1d870029f0559f33b2e2ca73f8843e13f6e0c0a335abfd28d52c3c096874aefb050eae55de739e1b7b23f3d32e3b51a1f2cf508a4392a66a7053ab1c97c90c1e29fdf132c1249a1f39ea1151e0a529514aa91333710a6a4f9b1bc47fa12029123d68d061ea1cb43139230946ce05982f61c039b9c502cd01c24abc31e677cfaa9aa0168aeb05fb1df7171e7d868960dc8d7f77f80625dba6dd705686b32b68dbc672972d7adbfd61f3d3228d4fffd45b19591ef085f18868025a6cd0b85d72794cd8c594bfcacd951c603ed4ecd4d5cd3c271299dde688b91cd97979b9e87119a08d297254c060450a7d0541ef7f40342e3a500581a172ef34077f39cb6e1d5952b5e7d65fa22c4ff8b20c06dcfc52b8d8ebf9cf31818325d8948628cb6d33e5ad64a30e563eba27d0ab0060dc0c40c5da48a8b85c47f22df807d55415ba67fdfdfaf738b35a1e51dda888468f3992054742d3fe28aa8ed7e7f9c7d4c8bd088d24f90d5c5ad9392a8d144a7734d41fe421d9717c6dc6f6e93f393b9838956322f1478fd73756d959582bac298973366f44afcc3876ccb3a258b82fbc88cf777e05eb37129368936a97fb2c46b0bdc3f48441953ae93beacb982c1165ed93619e7c4761b104a645816e700621fdf62e9d44ad0ef8346784caf23b650cf3f7e2bf3f22a6c3dc6e1b5b9b94cf08b688ab8b70d5711c574fd643fa01b360cf6995701469179fd06a50e8c06a5b713680a79aa98e1b200b2c274842a0f763b13f3ea4e7ba92a79466ea98ae43614e7e4d3bbd97e8ce84dae87aecede898c08af30c1d45d36c2974ef2883205707b091d6ac52a6f236500e55d8aa0df51a36a4a867912e78ab190a5dc72d337d3641724c45c00cf9fc34d296d9a232570aa2d8263d08b8e9605d4dbd1104551008d65169894f2453d1a0a7c0a3eabbe46a0134af1663e1595cd9acf0ada4159f7de89eb59c16818827f4c767b0f7d5431499747a0d3bc94bff7f18111687a723bd6ff72a682f94fd5653e562ceb948129a1e1d911208f917e5bb29ec4f334356a7d0464a59fdb31abcd80a19418dfda0e0134364474826fca2848045030005f77b79a7276494675a32c7120d3a0552383883d0d8d8ce4c39c725d9ab9622f69ba462e857e3a8c609ebb7fbc07684313168d0cdb133188394c232aa1ce3a526f2fbf4bcc070931ce146e40e23a85017036b50de73a7fb7c3ef131c67459475b3d6e911549ee8379c6c93fc0d9b3ac544dbd59195d3170e230727fff5f8c16f3616e79b28dbf39764e0128c2abbdefcb153f3c11b83adcfed51170ef8adddaf6ecc3bc03f62f2f593b55a86cf862185f019761fba9dea3a63cb517faf5c0c90099435da8e420d545c556169f89d692013a072c0742e986a989e7126187c70739b5c1a721bc24324647cc21093f98fd77011e063b24c78d5c89dcde5c385b4b874b89c9aee175a1c12f3223c7abb1d9ec2840fac7bad49e235c7830d18fb485e3df4d22038e81ebc3fb60747c7448a3001555425467dfcf614793c3b7a79de6439a9796740016e4bb1bb34e7e988432fc0cb5259976bce29e48cb420b72d47e93c8e9fabafd1269d03cdbe650de7577cb2445e59dfb28624a1ff031b4bdadb2afd55b00ac2d7c50f56dd23e7ec30e365c28b3146a8688d5866b0a4042e1f129857d401792f34f01c10a5d443502cf9d017963c060e1bfc5cb24bce7276c9e337ff8736154f27938fa3ae409813bf0b0ad4582a2a545908889c6bba04358fd1b2e1dadd992697082de90ac52428428d71c22ff63eab776ddf9505e0429dc46c0fd1b0a04d17f4ab1605049e5f5324fca343b591d36e0f6c244eaea5ac7a0c87d2fb4e1f0baf67008fda44378d6994eeb9ca0fec647cec686788ef82a83694afc3705db4a5963f63a1519fc5d765e4613f7666dfe5dbe08188efc034f641a6c99c2781bceeadaf459d1047c4dcf2518d4ef7b0f1398a9ca7b31d13dce7e7957d71f578d2a3339712986a36ff990ec2f84ea6c811bf4e7f8ec2bfc0808358506a1454bf9c6c392541f3a40c2ed04b00c9a6c0788454219c6dae8171afceccf7084d47b18c716df335fe84bf6a7b850c83e043b57562cbd902c6ec34557fc0c56633718b015646026b4a85116f37e77c9955cc12b2180429e8f0d18e75a80761466ffee1e49c01b7f6b2793abd2d4536c8f18ff2786fd967bbfc26db7c595e9d9819bdca7d18dafd6251b010596d59ec7fc36e02979e30856897371a69c0e0c9eb0fee77490f679f8d9e6733d46807d895441eaea253b006dcd8fcaacc911f6be0ec71045b14b11567c0a8bca65b3637dc751d6e680f17baee8254a5a200a3010f1ec5520866736cae95c0df3c880c3241617b99d8527036c0d2b091932088927674824d73283b37b4a3f3c208b49f922cc46c1f1ba4a1e494735d6fa749cc6821ea86272e154eb23b17948f8902a6ac6ce360f7f8952ed431199f4399b51293d3c4ab2be71c5edc031ae777b8727f15434d1eec8a15a6e72113945f10490625ce285733cc0bc71346cad7cb3ba411c4b0a82c8a237901f1d22d1cd1026de502f7d8553486c9fbcf9927951f74818575bb9afe73b6ff7a1c6c900ab553fde760d6150e5ff4192d380d85f143f8b064696b63df6abbc515a4858dd91ea968575780f78f03620f8e818dbb7380cf38fca2c35342ec7a12a30a4abd8b9b7f70033ec7f8c9bf66ca8cd04289a228014e7a1a08400ff3a395532faf8cd53685e804aae6eb7b3deae1b535c9153f4984aec4c8d5d92501bfaa72fc414fdd26260fdbe0c8154870e704a629c5403f49abce92c23ee703c690c38085f0874d2f219f0a8b3aa0533b6cd5ee888f7c1da123349b2e9c32751415c649005c7d5e61678bd72d9fb834837a9dd9df71004c37967a558c9303f4210211ab438e7a22e3d36ca20d2c1770c491140192caf766aa75e19bf9d1ee6f4d0f3c9340bdf06b2d0c34f10503a9f31e39883f8455955bcfb4633e967711bee1a945602f67b975dc69ed34f4e9cbf569223964994cc201a41846b478062924516514f91ca634d20506a192bbf6a3ce1a9e9be9a40aad8ed74f22418bed46b5acfb332d93355a0829a6aa633b0b3813512558e9549df85609b2764a1216ad65c1b00683b81c476d917aa24c729ee8e129b1998b9174ba661d5a51f24eda4a22035af00a8057e30e3ed82fa77a0674aa00760a5825ccafec1fde22e71f96721e79f39ffbbd0c8466b13511b2c0b8c5877f883d6674a75c395ee8cd2ecb0c964e5ba194ef8eb8f491abe2dc96eb16e412a0324c42de2cbb24e2073603e3ed21e8ca709cb94c8c00966b42cda0941a85bd3949c1993e8e04f3f448feae548c89ed852c6167ac93f31950a4d17f0e8f25669ee439e1c2c08c7387ed06a00e2bcca2f39a1cbe084dfd97d548acb42aacf5295a78a490263ffc4304bd8205c8bc3aabcdf6f123db00c26b54ed48f7425d1cb867748a1ea42329e0e123fd1b2b1a974dd22bbb016e35017ced5cd1d024e3954004cc7ff5464c2add8be056f6e2609856cf936a0e2cb9e55728a009ef086a39a3fff735e135a3df630c9ac825502c1af9ca703a0a191472990e3ece87c14071c1fecba6f9e9cebad10c2e48bcbb8190e9f1050f73bea5cc870df87204843471aa658d8e13e91399641221bdf1737b3c6cfc444b271a800354d01e87fa620fed0120ecb8d1530459032aa21407be7a57c373d2db711c2f7449db87bf25ab7445bc3d9b0c83b09bda91f3d1a0116902776fc6afbf2fa49f080c51bd33c2602a3d8c31801269c64ccfd67ddf93b03f1cba067bed0734252146c169a0fcef1aeca8528f100f996cec1f9a51d1a31a5121b14f77fb545e639713cb9cfadcb042519c1df3b523a61cebd2762ff073369d881f886c76c89a4da245494586e92c89e79d5fc20ea2d021e1457d8f049accc99d70390a0942c7004b8183b8bfe4a417409196fdd95252bd8124dc7dab617c97883e02a794059f8650dc5ee194c9243397314a31586b55fb093c486773978af0c8499bc89b2b132c5e8d15b515847fbe9ba4d3188d63c728fc194b998347e98d406e4af89dcad20b4244c9aa718aa82018aa6d54fc1c19449675ca97c727a8d8f0a1356ffba611f46c223561c51a6e7029ff8e20aeea50c22ce9ea32e5f793e61b160e2c7caf7c0d4bc5afccf6ad61773b983b8201f304144d1c7928d778ab7e12e2d93d41c10463da697424a676e1c7a09bd13a10e21bea67f403f922dc4f5d77d9b0662fb2eb338dbfceff42479d066d915ac79ca08817cc2debd327c7234a27c24847309fe4038ec1580f27f01e36c72efab3214b3b4cfa5ed21e6e409f9b36b58360857cb0e034a4ba3d08ef0d3d696fed217d2fda89834de0b355ceb554423e1d177f7125aef54f7a67ce450e507d83443c5e8f35246a09d193ba83c85bb0a5569712828d51b42aab47ac34a613c729d525cb35f8154d198a95b36ee8e6810dd023f851012255ad43443334b52887c331c3286815cb240ae9329d290c61255f98ccb8b372aecb5b3834369bfcaada9de2f75cf83176e6e32e5c84982ba8aac4c2249bba8615c5c83f46cdb0fc5c803a3957258477e3923bb30a979ff2c8376747fd3bd309eb8ec8b56675e19cf34a48a90d49bce4de0d9fb9078914b7a216b9f8a5b8d3efdb7de0295617610222600ad7a1a5d674b1853ca7a30855b4245ffb50b0c1db90d38bc7fe2c9179fc941f6d16e7038652fc073a3f4c68e4d30ec452736b6adb281c85104dd15d5f79502eb12f9d56a87f6803c3c9ca7eda7ca3a92bd8ee638b633a868fe908178f8ab4ec30868d572a086d1f93c29777e5f6fb03f222488e65e14ff955600fceb5230c616bd0e171179cf41cc7782dd37605a645c7057bb3a1f8b25612ac4cf7ff150b6e8c3a2e0878544dcc17906a9d8e307361711670915cc5c51e98801776ee932081754fdee8122d9caf6360053565678b3b14f9904cc56fe03f04b00181a7a4d020e2a5d39fbcf28a51636d11598ddffc25f8ea952a45c8f2ee7b70ce9caaf0a83e4ae2dc3cb87e26be62002a13b9670b6d1f9cb319cfd6adf21af9dd8449a701224719808fc58dd1257f74644027037640d690e86a32ee51ba054f75df579791ae726bd0358a6ad57fff4d3b360954ec45813588694b7e084a2dcac5795c882eef1eeaeea73abcb0994b28adc126d3e3cf7fd9c9a3bc665609e1744d99ecf80f6770d3a36bc9e769bfc7c78bb8a738667a54d2582d066105987c3c6df88e9abf9a4617c26d92d0d08e611b8ca9fcfcec9977e26562fc50cb9f928fa5fb917b22cfe6f7ea1576c3855cbdea485f92811e5a2306251514bffe1eb0beda3e2779187f9476de1142d32f07a534b8f54072fa277a63c3ef2d54d1836bc165dc15b4ca89182c6e5aa889e365bdadc37298afcb169c8b9718982fb99c2cc8ba0d853bc4f139a278de0dc01d4e908cbc939a188c929f1e698afd56662266e8be8b5c9a316d8a754a6a76f789abc29ed30380eac27a63d743645b16ba3701df9580d926d9e337d5463d875ca508c20111a6222f80be45289af51be735b0164bf7d5f52d8c6c5329df355c96240a0da64dd8562743d3147e5dbb84d9baa7d64bfce2a55f0ee7f77c1e2fdb31c879f5b91d172fef6f7ce65639772ab5a54ce6c3a8e99dec45fbc2932c285d2743fea9dbe76c421021b5b0de57ce10f6b6d299a39f06065088f4603f995b65fe61974231e44388f3a7eb0d2eb1454d6f16f4366062fbfe1902dac49ffcff203280a8376939b66d9f37288daa7adc5d1718cb3d8eb03c1b1958c63b4c7d808701b4bcd3970c1d613e0f7ab7ccf692b556c5b7277d072f4b51f2d25c8a45776eed8da914fd9ae6c1009a322d1cf4c7c918d0c5bc40ef12f0d8826300b57a8cf2b19d08b3b09b15ba782288b1ddfb0b8352d10d140897c704b21aa2600d2fbd266091f40d6e314683cba3b4126ba0a76089cf925d7f81dff423c0fe0825e9dbe4232b5fd1e83c9da70b3a5efbd4f871961e9105f128244858dd13286215c598a63d7574ca7cca3a9e472d495d0b2e76ba6066f3a9173d204a34135464daf35ab4a679fc945053f33b402ecac3c093dc7196841d88a51e6ac83000c67b0ec2d073e003b54416a9476a5644e4182ac1accd89d05501b16cafa0fa7c0dbff1361e411a9d79c7e6a1d8fbc217303343e8de9e1323c3a2c6da9d390620438fade3fd2743fa75c80409aae4f06e2f05a0d4f9035f09c041be02b2aa2e830adf80feb95dec8efa2448316a75c73f0f96d48203b9cdedbc4967b179d52e7abe96ed866f0787a7aa9ecab23275e740c17f79a0236807235968105f75747a23d92799fb754d52c81f2015dc7b29d7329f6528cce3cac8d1ce92a909168e587edbc2c9211412d1b75c733f5bac6c29284cddc50fd6c95de53d9b6b383a8ae85eff4cb692fc08d5a20fb005440bb4e5db8ec42950352413997a260989d3798f27a2b51b26688a49ffb684af5bd128b11ba74f6f8adbe6c665ce9635c23126c9146b2d48ec42bf2fa3197ddc63be6cad20fa370cb77b8749e0bad47ac5224061af8c8b6fc2c0beb775ee910e1b153a338e15082472e1b66b7a69cecec0da303a2c8ce5acc05f2d9969ede9c0285b0ba3a9e81c9034630e7276f4ee8ae2690254b9fa1cd86bad340516e9878108e93dbddaf8c0a963b9a7b9a191de2875230aff6f148b600cb9c04a96e4c5f41b2c73631f90e1e306c5edf32a11c16130a9bead71a1b17fa85e8caedec28c6d35626b962de39bdd0d20926fb084c0fe349102dbd14306358070fa94235c49f37d81c61148dcfe65f895862d5a11e506d5f64c98fb48a7cb6a84148cd48d8ba69d2a1ea272f5b875dda5f8ce743bc01ce3fd8acca917cd69d391bcaca8eaa3785ec51a49c34bd5941125c2cb3aaa67d9c18f3676dea4a04cd79833fff69b5fe85fe49293ce1d71186efd29b1114bb995289824dc6c33fa110e503474f690652cbed85ca04401bf1793b56b8a6d0d4767931b01d71d4f8f165693cdc39cf48ae34b2a6b390a532242dea12ed934f92055e42a8d95ee8cae282aeb17c107985435dc578768ed4fcdc5bde2f4623705589207ba0cecc7f1845b75f60df7ff4ab825e885aae0e7a2b32ad3c84140aa54d08bbe1285f8835672571f20993e3c7d53cdecee927027e54932890de0490effda166d9577ea81a6a6a16562063a28041b63534e44f80d434537777f848b2e94ed6b42bcfb7e0e3c7224ee084948752a307866b101310de2d7c4d1e7d1d34cf8b624cdd2a0cc1607bb9daeab2b81fad993489473b94da8302c73d9075d83995c33a0e7e0987a3344e6a3fa9f55d5379a02f36b4feee8de6254fa2257e9fc1c8ea639cf9622a8f605d6ae9840379ea33b0e46cc20a0183b9fc998ed673be86cd698df3ef86184049be703ee4dfa2fd1492a7f4b8f6f44bb801fea4eadc80a5cbaddbd482b914a278c5bd9388eb477d20f630bbc04cdd3eadeff340874016ec99cb9d639d9866d3694032a97c15dcbfc7789cfcae87139ddc0f44dfaa736f16fba8d7878bc71c7881abd03a2684ed00ac8883f08e57f2e213b21f2245856e2c0402c4447d86b9f78c70a9cf1ec1038d4443adccef1362e7eda28eac61193ed7653d6d8ee4f78fac5968ef904d017614488f4c003da381cb5dc51e317a1642a0202eaff513532a8fa10f74394b4f472f09e123f11c4310df6d0a5d90d553a6d842c5463216e3523e01f591edceb49952824f8a03434ad6414bac2047dd17764a6d7a5d52e8e42cfe0c5b47e89e81ad3c7b2a352bc075a85afee09d1f0769dfbdee58099019da5518c196fabe103cbbec97814cafcd90dc466137df00010f95ea4e5e3e2007a72743140ff9c480277c0cb131591a8c606d9c624889205f941b998f40ae51efd02f1932c3c504cfcecc1284b0f55d341001fa336394dbb78248e686041e45f80918d40d06a59af386eac2bdc0a941b7795f2c181bfb89deccab4eebd1c0ddb98f63edb79f222c66d41ec518ffd4e4524c2e66f63f9813687546b7d57dadb938b1586f25d52673860dc81e3e43a7ec215d1fcfc4bb469c5be5f8715dbe1a7e2e9fcc07931f7a95f31edb596bb1516c4922d0702162698ed02f510138898223c84ae76ec0adbf163387a13227a168216585f128023c76311cf71cde8d7a6d49a2bf1e0d326137b36c89c5c218baaff794e42147029ecd95abc0acb7089eee48056120f3460ccb91a65cd457340e01b498e8f9827b51e563ec4cdf16c305110b81def99d247a252c98bc0442e57a90031c0fa5bcdbb4811fd7a29ae7569ec6b2f5a34d37f672249deefd0b0ca0c248593d21b2ec5c655a6eea38c6c0c470b2753156490757838a95112c3c1ff2a8327951f180dad55664928194c4d1698f62fcb484fb53e0111b73f4dbf48602a37e89e0610a1ebf1529882e7e2d892606d0e0fc189280fef58b62f77f6459f789a50b215b9609a100d8ad4460433f9957eb350833824a3803cb8b49600fc9ad4866cc53b5a832772d7206e4b2bbfdc3121ef8a9107de51a7b4353eb539db238b50df4fe156bd3a92360fc3e0664dd44821740f5b613855f4dca7e7fdd658fd4916a610874496a8bf08af14ebe564294ad8bc41c0002d2fe323c22d11d4f3cb964841a76ab7b3847720aa70318228e6c18aeb5e5b5875680c7487085c71f2912fca95b3d402d85b982eaf95664fa099a99031873632705fb4535f880310eeac2967f742364c138c8f85762ddb06b2313791b5092af3cb35e2f596e1f83f26cab57cd61fd9c0154e0ce70f56d66ba4e5fd0769ce2c3e172623d608455b8370fd4e5713a92cad44212579045cc65127412b95ba096d0b0a29aba4b76f21b2f6352a5c25acf51bc0526d798902a2a14e143520ca68aa34e89fe95bed8849f60c8c4e528dbe2a2c9e19e9b65fa1f2e5ae35f4f1928e4ab05fc24b64def4a4712e799b34ba7459960f132544694289c5273b2fbac2f466b77883843f822aae0beb2af3295e70300232ff7350bc34de7f3e8fdaf857009a0d0b6952ea79ab757aea7f62c1413334ae4deaba1179b646e0a3b6be5941442953259ffa7e896fe1d7572c6d2dd61bac5b2c238b45460e46caa0dea5362538cff6bf99562240514b10cc7649c2d15dd0425ec4883c4baea4381789d8166350de32aa58067064730897b11808c9bd9c69d55bde7939a56ef56f832d985cc7edf9d243519d386ab864248f29a4ec4d2ebfe398047671e917bd0f8ee27d894d7c436c25d85ec128371c3e6248f0fb138f902ba9853b0f26c6bbd13c6117f544b174ff3c9fd16b226a80e012ddba351ea816172befb25bc78292e5318b29e94a6dfb3031baa9e93a18280a161f25130dea9f8604faf7980b767240fd05a2b015433d06417279b615e4768556ed3ba3caf721a779b12a9f9d87a41b1d28d8075cc5edf75e7ae4771e59cd589a6b48a8f75ac81096ae8e6630c1c3d4fd63ca690a0846d50e7093399e1b083d58f5d36c511a64aa507374fc46d98164e04ae29224529b2478b1557cb3f1e9b03043b5ad9d6392c757cd6945effa9f9102a3f1865c7deaf12c861a817219adbf055710d0ac5311c4c422132b0607680e0151cd54ab164d9708cddb0a886e8e7fb92045876d16f7a2b9547e24693bf7da63d153edb010e1d5669924793be29900f93888872a65df9c3dd38b0f645f6cdef0520c0bd6e3141aff374bf5c455cc413d5611555eace896915431909354b5f387c012652bbbb912bb93218f39824535f508de380eae71c237821d386c63c2dd4c55c979643957eb0f12a1b009de00388009ac3594719515d0c6db498035fa274d482049c226883f536e64747bd31416c69bbceeda4b1c101dd75e3b6e027525d71b1f355ea8b6f3bc23023c26f697d1d2899981a97ad709c72c269efe8c3386bee4d8d2a461158ea2f7864eec83ed8da02f31fa80e2e7805e4743affaf7fa27bd1cde5ef0ac170fef756cdc2b6a473359a0e2514a60de90346e5d5ede0135d492141948053f518c9a3b02d3ee56d7e840c327238fc03c2d673a1c63663217fa6c402ba3f23997dbe042090b095e8b9db8a471b898223b94c1dc5762c95cf0b77b8d1ba533ebf56abf2a52430593975401e7b585b9dc2d1be7d4868efd26b5c235915e778c82499b6b6e72aebd611cef6c59c94c13039ea4c0f216e9361662a2a6cc6bff7b7dd35f1177c543050dde1bddc7c62a8fbe5fc17d33769f55747030b8e49c3f4082ffbca840ddbca0f976c52e2a2d5d58b8d6508ffb830183ddabafbc6cf8c26cb01b45f7b9e2fbaf04763c99de15b740585a7213f9ac10cacfd486324ecd1c8bb21048f59fd5da0cbc223e1cf485f38a340466af848927935b038f6d130d9b1d3cf4ab986be5bef6515097db693591f090cab936079c4066b4b2e6bb4ce179c621aa37ede9c46316a5c385429405b800f3388d6a20c3f740f9bb6df02aacd71871c7c0370add2c0eaca18c6241038b0778d581e254d1901bf038454dae90fec65c2563c26209cfdd380792ec7af365f10cb29f2cfe64e8c802612df717d3ff2403363e19f6bb6954204dea01ff924a223cc89c86ecbe1b8dbdb00c85a2cc02499954f816ea0c62420c31dcba6c0ba34e7ee5c7cc21d42ff667084e73297ea81a6d910ccc9c3fb45aa84f6260360988b25a890acd354429d67c5b1b128b5970bfc530f0f28abca046968352d907733172b8fbe3d24cef739341a012c0ae4569cf038f88d5be4a472d594fe389ecf729ec39608e7293ba377a057737b8c92e88188f24b5ebf530525932baf8bc87842724796218a0c9a94b5a3cebdb9b8b4b51c5b25a28eec36d1ed4b4e88e11ddeda62a40fbb64d48b39ffe4d020da3fd5e1ab6f7e7c98aefd3c2af7b8b82bf70295871081ec5b47566b30b0ca09cc85fbb530e11d35fa7bf57bd70fe5b196a19d5cf0e14148b28ec173eab41d260767d060ff12f6ab05defb9f448b94259c359c18a799409a6ccaa776d7bb1f987fb6889a673a634796646432a30022012e99fe456518cf58f9ebb57a2bf175081f8d62c54900d8f81846dd72a971852b0b70abae78d45da3cc93d688251b85e3f27fed9b93c3431c42f148c7788335378715cdc160da821bcee6ba7413b52079076303d6f00d79dc3b00c16c2e4e63f772728e104f02d4a2ffb73904abce429db60223a3650860436ff43d983584e5657433faab93b83794d0f0b98c5cc11657e45225b0e75d95bc061e2df66487467829242315a461b8e8d6bda7aea248b25d81c9cc958342a3b2f637c8828d7102e1a8a7c11dff60761420ead0b75635a3ea88e39d523bd13a530cee315eb0e1d4aad79d8819f74cfd51fae1850a3e9b36e72713cab388a14e5a8c0f9c551094aaca4b1c97fa79bf0a328b0d255e5ac21f35462e3912fdd475fc0bca7fa4beb6789178437edd0dddf7b3a3cf6edb7e85bf0eb6c8131beba5f413a701ac5152072eec7271056e1543fe396feb10ac5df6d07f672941da0f11280e6213f609fec13ed74334397ee71899d89f0e6a7065ff9adf7bd2f6fecb7acfa7a568c5e3eb6fbce393607fe2d939f2ebf2c1bdefcba13b9cca40080145339c5f10b6c097109ab0200d217bb083918955631872f5b9cf159fb54afb3f3d0ec98a18e5efbe3316d4a5c383d68d646080b0100c45365c7a871eaba2334ff1ff45813af1a836f4ed91c2e718bfb3fa2d4c9995564675b5a775f38f7328b19b76b74947ae5eab93c27b2a0416022699d8afde7b16758605be33ff4edd640fd8c7e16faaa630eb069bee8391595d41929a7b38afe9e15a7173ec63ed6f5937b87314118cf4b0823ae980a6725913b92fd7f906c2286cd8750ef410cb43027e606cc31d8bded83c0b41b70aed4ba7573f5ef220cccd266d482752e0797748081c424a6deeb719cb349cdd38b0f8fc2840602265799428e01f4e632656d885b849357e5c690fd7981d7b687493054aae8593dc303302a9d6b856badc59f244663da817d29264fdbaebd91ede60acaa262d7acabd88ab2c656242d871a700bef61950a6aa033a5085f993328973889b6629471a4e80ba18696b1cb24e2c877334b0e0d18b9005d36aed8629ebb7804d47f8f269fec70b15b29a9f33c5196bc249e7e89d509eb6044235dd87ac950dc630bb813932e94d0f63ad26c80f57d1edaa90c481d57512bfc1f2adf794725fa29034a5709b3eb4cd3bf2ea113256cb20fc50e053046a615c980a2618ad21b3c4beddf4c788c6398d774cf652a801e8a7bc1b8ea3e552ef4d2982ad2c7cb5f9ce45fd52cbb8630287a370c8dcda903c5111b2b4eff90fe57e73a2a63308609edf9a528ad7c723eb1be48ac7140480ceb2e8c918423a17a490f9b0789a8de0ea5b35184eb0faafd2ce934a180ac8d78c0642981fff80d53aa3d3262e210aa91a570a177c39414b10b60e8f9dcafbebb95dca6e01684e9a0da1bbf79c5800ab69845bed6b94a919cf1132e643b6c8772ec4aad3a11e27859b7e862a3758bf62d15611f8484364f4c6795714d46681bdc58169d4bdacfd3803e08255ec6c9648b48e84a6ab1a08669aab9d3ecc09ab2aa0a2997c76353bc04580c35d869e05b1b521d2dd95ccd841f6db41a292807c22e8102bc17ea03f07fc0bed6e810434255f15fbdee6e10686042b805a57ed476f69f07aff6358e53537e2fdab69ad290273049bb511d208309d87a17e20a9f51eb39c22d6b99b884e0f0a8c118e4b8183d547c8bbd562e67c65d69bcef0aa70acf039cfcbf7277fd73a96176034fdf2931f96dc724190ff422cd8c96ffc43d494ff711e7280f685a93c54e4b6980d004b1bffc51686c4527e2417b300e74b20d9b1f7f63cd72cebd085bdebe4d26428a3297b0461a7d5e439172dbb2b325560195ca471f3197355b292277d63c87997125a8e252b9f857b7c95a17c630b39eb0efe9f13e4637dd92a05be945d0ca030b80998fca9615d9a897afee815c9b43b27557dd591a070101a90d6622e88a01bba5891114bb67560f85f1ceac570913281733d6d816eda46b17da2542308031cbccc2bd3739fe3816b54aec917002f72563adb040b8a46724e70a0707cab975ea06d8944c5f15d6e3d1cc4ef5dcb293933f0c96b1a81eec665328e4067a340efbb4384d676af5ae28eee2e644419b67161698eafbcf3b7861ff445e46ed208c86ba04c4318a1d6923611239f2324b0fa7b2541728985616c30b441fe7e780b9ab3a2456a17ec961029e1b6c0f6ea3a837153620cb8aa5e39c671945119f57220f41cc3090819ce991b9d616060c15620d0a1e0d7faf8704aac11d374ea77ef442a038502383d00135a05965e408de227c8769e1af62e5b8091ccbd4b4fb20fbc1692c94142e593e26269178c178a872b6900f33aa67919e877ffdc1a8ca8c107d61d4f5bfa12b42837cc3c49e81daf340191622895f37f168e1a5c59839868bfc2fad58b96ca4c8f5c83ec5c81439c35f5cb55520928caace7ab4651f1e1106f5524c330effd10e58cbc213ca6f4ce5442bd5e78916124a720578f576253ac0d744e49fb90ebe5d383323cdea8260965df13b425869ab63eccb150725047b16108a714c38727f665cf209d7df0ce22ec24cbbce342cfab8bf26eb7f9c03e96f64aaa6ed3d074887b99a052e5369e5e4eead605d5098589172f12ac1977b0b7ba89fb9cde5c07f17f75695a930b46d76456846060a312e42790f445280f853c8899662d833530207e62bccc497a89706d07b2873c02d308f2a18e311c8c073a2b9f4ee9c649f3c193d81bdccc9b65522e1fe4b98eb6a80b24d6c0ef6124b57c3fbf3aee284492284b4d7110ad46d50f23c6d26350e02b5c58e3c7ef57b23c33a49bd2a76e4006c748551cf96f11219b13c9b55ac82b5be1a362f8fc174bba5132332dc3abec321752829bb678f4a52b2bedd2b386cab6b4bcb83dec907e88413a6488e0bd8b3693d6ce2b6ae386d3dbe09f0db59e744c3aa19bec7479159ddaba75be255a5e8e38da1fcbd8be65fbb393627877d7e382a54be6266f11adbe93bd06a68a8ef33577a3ccac0f2a6a45aced9614b684ed955c2d2188cde2d851905af9e9126dfbd026e5e8925f2480cf36a528f940f42297fe4416368fbe09ca8f83473dd5f724b1d804074cf17a2bce96edc0acc4de717d81cd45a2b7f2de5e65101c32d8f66b13193319e6e90567e4122248da0f1a41f16c16494f63f3d9437a14b2700749b6371d0f31b4eec4200fe6b72caf198ee396fde0e1cd5ca5ea220a2c9a1ac9d77a52402c60bec5601d4ccb1b7a2cb4046f29a7c4876ff5c21cf48443389971d4de74df949f50bf871d2080609d61761966024c99b31c38cdba31d51943180e80fe64ec151dd9174108053716285617a1a090bb3cb1049c4752ecf319072e805b18ae2a44986d36d0de5329808f4e187e25f2715b7fe6165866ca5000480057ba51863c02003d10d58b564c6e1158b68228b225eed16fc15173ea94a250af41c5e11c26848d8d63068775c08ba15d5161e10f4bc2801294e57100b90300dc913d5ff7ecbe6754b606925dfa0bfffe901efe0b771dde3968a54a73931a0ea1ad2203aa6c23d20cf5a2e7cec2d971b7a9fa815ddb220194f4d80240032d676605c00aa68656b09760742c1d0d5ee07dba50c68798955beb4f823e69586503ff9e865af03e536e28605509f006eb0c5d0c820ba204614a113ffdc6da2b41488a1ada1b266c4728ea6c917b3861f7cd6c795a6a2efbd0e607ba2a648d437205d116a48fed9c75ff9c140150aa9530c9860f54493d40f83a809d4a9a124822029408be1e0349992905ee4064b2dbbfbd534aa4c3b1620ebf73f0449fd0d725d8a1450afcc11912f100af89b826c039cef7df1d0b8798f9858b16fa78a91902442898985b69928b05b209424fb7b391113ea9473b72ced6cec5f6ba12375730f11cefb9220b654f4b303e48d07e6b38894d8264fceff5a0c70fb6cfa83e2e00e341a1dda23f86a5f117ab85e7c417bfc111ee4a69b93250dde0075195a4876519f18ec815940e5048a9f16e11cfba918b41326c5ab1172924a41bfe6c4889ff4c549be8bf83945ec5e003724483d05e0ea4bd7921c0a5213a5d1add62c9bd2135bd695aaf191b1c19bc7179be29e6ab4939845a9d8203a4890c42c97d1131c2b182ee775cfbcbf5b3ecb9f842d4e816ff1b938511e74e8e00295c5368484270c32963bed8e9b63198c83bf750fc30129c4ec7454afb809021dd9da6567e41c78c383d3bc1b249813d997658a51760e6a7c84cfbee03e948c87367bb2664320ab3fb265159a03d66041f68f6835d016f2d86a255022763b79b8c97a0008984ce7c52049c8d1ff10aebe950716350d1f68f62e458c0f7dc7b2429db947c27b34f0dbab599ba2d8d5254e131a060041589f01fabcbf0f81686fd178a7c652fa2af9ef661648d588bea0ad17fe9a6a89eddd9d92fc4c673841b0be6979920d9f3e5d2af4ac83be2a1001f3737eeb0b6999454ef1be4e9ff7a4e1cc3828fccff79d6a212967352773ae69a98e17f25d6ff2850a6c49835f41ee732bca501b5327b47a9f7d8ef555f15ab9773ac5904181c26050f7e5eab9505292be22e99fc048503179d22a0d8e839ac79898847d244f777c4cf8ab87a35bdd37ee0e93a4425a94e713a84efdffac8ba721ca98ae7c0d6388a3cc6dd4e4aeae6f098006ea774bf09e407aab68822bcb76ea003d7a2fb72a4070c69ad99af33d115839b00ad7ded0be78d60271bd1420f00b875ce9112da9f676a9a794e31125f14ebd469b0e306c3c4301ce2a16e9a4406cabd71104eae05a2d4db34e1016f91261a441c10f0213aa75a65bc00ece6e00715af7dc097f05c5ca074f128db73ee101bfc657beddddfaa3714c8dc114ed59838a33a479507b044dc5bb7b1f0b41e30de7fd2988cf4fa15a7b3a8c5957cfe9bfbb3f62c298766a215b14b1f9ab4a0d6b835e7ca6b51ef0bc176b0ddcd93ca652ea4676a6c3cd47c2a187dc1469dd32952c2ce3d9850049e8325536a9665d6943fc5c1df21f8f898e37f6ca15e8276c74fa401bda0c8f2201560ce0be7804a129d9f2b463de42565aecaf4b03df9bf6f46627869f405422ed5e1323e4e0b07ae24012fdebd28962ccdbbfdf9c80a9c12360a03be5703b2fca7c5a3a14e6a17adeb3eef0f51a8826ea2199d100678f1124fbd44e18e4276a4db13963f207620609cd73bb42b80809f083d912104770c1ad99ab33f0dfd0488bb2acd0c3a88d940df9e6d067d1d2335ee9ffed5065fb2f1a2de17f8bd1c85bc4b75a4afe6ef13cc8d73d399ab26dfb66a2c56f961bee17044b1703ecb1ac0cbbc6b695dec02dee084d4e92c87ccc1fd04efb52c0e3644afc23fff1d797ee6d0ef978c7db42ff72feca0288a521479e6da98ea6cc6790fcf2744627457bf5bf530b4b46705713590e24d65def1fb514c1e9d76f7c24c430da8ed52882749d2292607b0109756fd4767da7a7f19f1a285ed1b53d386f0bf643d8c2569644c79daa6562426bee82d121412665e6337f823920870de81f0df8812d5133fa4e043931fb1715ada49e672ce6a505837e735c16ce44add7a937075cf87b9c2c28732b915eb27396083e30b979d106c56464ae9a833c384d6c1f8b1dcf9e27d67ed66a2f62ff17c7badbcff6803cdbda11632cb6f5cacc5a5bce322e2b822a0fbc168fb78a3c7936d5efc0e2e719979e01d9556b683bdfc9bcd978e2bf72c0305ef39c3d155d8bfe26a27558e3fb47113dd50b016f00ca3fa2c78f6d9257f7c145310baa3d42681cb694815c19b851ed0e648380697a29614e8dd90b49ae7b2511474c92734f57e59538f6520666abcd702a4fa6ca059f0f835ab7daaf2d76f92e8d8663a69c91bb73ea45d54b88d975095a0d2e4d591033c46e3ef8625b8065c4671dfac5a3c205e0f7da6688614c5c475b3947f1de12cddfac9f67199c1d26d85ebc454323235de11700b9415a77155aea90ef27cb78822922982c35ba6fa1a12a9df704bd7caf0f782d3623bcd333e4cae6c1b3618a2d60790ec81ccb24704ccdc021f05b55dd4ad2fe6c84fa9b95b6dacb59e05066384a20d909f68295033bbbd1269295c7e5777080d4b625d0d107489fdce25870e4eba09b46a2f0064787007f6980d8aef001153ea59f5f6bcfe71914849e5566abcc2bdab499525d886f14a97c4a2cf7bc88353dde20b63fea0835e1812a9f8fbbf842d2a0270797e0d03c37fc199844611c4c28fc85349bdc56d5dfaa006bc4243f1719c4d7af630b08a4a8f66a13190b45e044c1ccd3202c9146c259973c8c8acaca5d6d1992cd3386f6ab5cba673c48d6b2184c156958df2033b5997adca95079c9bd5c736320f8975aad4232f4c092c0068d7987a2e5ec92c48307a1f1b8b48634b7dbd84729b7f728a1da9cc802a72d2458bc944668f2088c3e8929894becbbf34307f30077671d1abbc516f581f93c8c09fa46b4f63477959c24fd3246e39513a2882984fe5655783a80694415a5c0d99e5e9d3bb58fb680921623ad97f442d3bc3c6958075b11d93e4c6a05d7da35a135c32048b8343e807c7a9f97c164f44ed251d0f639355345c2ca1b48028522a9237ecc371a4d927de6bf249d395b7671e277f86612766600c8d0b18ea2d095f7b63a685fad81bac8eadd95508e3bc804088a79dd4ada2aa40e20d2986703f1982be9b2b74d0bce93080e3580d553a3b21a943e0bcf6e105a687eeb39e7494a15eb8617444847754693c863124f82fad47be96305995f46b6b17629fe80330c01e339fc4613dd44dcb5541a517f1503dc68888d84b5daf0d37c4625337c1391f8405476588254ee705e00162570ea3e7839bbc271bb6e6eab828a84cc26ad7e7932d5954462458bc6a65e3819546b14e8a87282907b5be0d33508e00d1071cc25e96f800189d06b52a43e192dd5277061fd1958ebe138d98c3cb13e52c5e025593cd432d1acb8cb89088acf04ef604e2245676feab6f21a1612c73fb7edebc8d6333fed6a2fd2b7d28a84b61413022c36e49378b335d32e310f8e4aa8414165fe2a5aa945785a4b642adf61a3759a6fac6cc5575285a0851cc14bea24eb98cf2e75ba3692084a82fe496aa97aceb258510c0c7f1538768521751226a340086ce5de751f8751976626741238192a6867ff010267225ac1e9c482255fb5d640c1c3afb197965a3d4ffb5d45e3da00cab44a0c65435ff5f63482ba9805ad12e348ac0ad8942d3262e08da0a30100fb96d8660021b7bc4b62d357926c7fe4843de48959192405db05f02e1f61d31b0b1a51b5a984cbd6cdcf1b827c6957b722ec496aa0cc02e047bcb4061c678f2893ba331c8d514ee89359afb21c88d95b245b0b1201e7273fed83a11f4eb2bce54c3496e1523931f359c88340fe1e9ad5956b071e3b9bd0295cbcd8ffbcf6367cfe85397a97253ddc789d77cb05c2143954620cd74f38e95067fe624c4c155e37381b68169ef9603264a287613b4e93f8f29e4e5068ebf5c6980d9a849e53c6290b8fa789e0e5664a8ac3bf505781a0a8584ef9c54520def2564accaa72ad91666463497add7b3b61a7db531df0bc990b8011b0474032eb0efa6cda8589dda28c366d57cc450b6607ff468aa6ba5b4eb40c9c54ee210cfab83a0536720b609537d9a4e0b158b395975af327cfa6991f5dc13972a67d7a53595aed8a12753ebd3fd906f79c782ca802009ec87a754c7a6469907ee496fa96489c2d0ad40c01231e63ead6d3647db0a66aa988c167b3704af18a31a78f27b4f0fff62fcc9ffaf39c77a2c46555f8ec7b048b9bacd0b3fe4c63481c8dac386613b8ccffa084c4214a588d2235fecaeb7fcd904b23794cf7448b600a08913c39809da376898b8e75d2aea81a8be55b1fc9c8cbbd577a9d43ce31680b58574279ac052a98e8fd381a8792c6541d44d14cb4047c2ee79434e1aef64ed72fe238bebf91e5ecac3e2236f6e12b417d0574c64cdd4edb44451ab34bebefa2b13451d86d414f291f0218c983fed7194728f013387f25578684eeb08515aba92131955d615547ca29b2f78e0ca8b0c8c591be1f4d6c11092a57bf5c1cc98512a4266181cb6ccc6407a94f82700b54da2bf49ba19ebd71f886010a097790fc162dfc4897120c7589d4d81c72ada2bfe879a1659f8389ca3a8e9c18957e688ea3b1e4888402ce51741cd70cb34769be989a20119975379d4d1a983a29159ae987a498793502a822ea08e18b0a90729a71f9567001643d10ed3afd5f76f3c69af51f06f0c96b4a744cf46330f291484bb9205e90c0f78b778b5cc0b32ffc6a541d2381d95ba64c8f6adbd80449d851c42607fdfe8e0b37a38c64dfa5f74a759898ded7e141472a80b1e7089e9eb00a2225876dff2274e5a1465cd0967e184c5e56039f533299b4d1509fba96e1f2d939473e761cfca7e1630761fae09b83135843c49dbbef84ba679cf06150fe6e0ca4e853b2bb75ec5f51503d9eee46ed0e8d8cf6b472dbb9034b5e80c627dcaea2b40f404a6db7b8859e170c7856dcb972548c4f74fe8da13e0f6ce30be557aea1ce210788ea6d465f56d6d15bc79dcb4b6231cf8b05de9a6b9e21bbbb4128137ca3317efd92e3403b8988a92940b38eec3116e8d5b0865beeeebbe45709451c4b103b3d2414e17d1c50b3937521a1c871b38b805a7b1d43d95b67001d91127534ece3da48138a8853cae25188d6c08bfb70184bccdc9c44deb9917dc85b7834c431facaff1dad14124e23afc7e6bb4ca81182ad935ff3e926b69aed8dfc9a0fcecf551b6836e1cd7d1cc8eb4e35b28083d390b79364ddc8109da21cf44656120fba81dcaa54a8f7d4494b77856661ca33cd684f2669ac8cd5d7349271e1a88c552e4d589f53a6530035be24a27393433ac4b552be9bc9fc0d6f129d9c6d83317b76247fbd22d7a386dac8662c0d42383572c0210a82e70f659a2e5870a10881a5ac40fc7c6b017738609feaa1ca242aa418e3924b9a2874b5cc94144568bed8281fc1d3e876002679cc5dae2ba923b30ee5ac0925d380c1cfb071a7f642a60915ef3a65410362740265995ec35b563f86b94cb7fcdaca63124542f71a7ea77708fdab08cb1ea1c2691e65f95e2c1807ecdaa6337ebab204ee01da1b340e3807149b987f5edb441e4f8137b98c706fe7436c2bf2ce384e36cdf8f8c7927fc1e9b3693f6723b532cd309832f95faaae99f3f86bba1ee044411ff21f59f39e896911293d18915acbac0d6e90e4dc39970759c42049e1ec97ccb27ff3981a74a5332632ca2af3b6c3340c8d4902be5252292aa5e5c54bd3d4477e7ce64c3dac1b9621c6bea5fb449ff8f4ce39c8176b8f83e0472422c8abae10afba4969821e339ec085faea29c91a8cc591959ac11151ed8ec248ddc018d1e0ad5a2daea4b61308a7982bd8d0b4ac0116c382ea599f0ad9b145ec64fd0f0cd5c9166057539dc176b48d1446a3ea725e990d5440aec4426c0f8d994c7dbff3f40dc9795a201fc2c576f1b07073a56367e5baa1b02f07d962a8188aafbe10e170f67f281752245cb9b545b1123a1b9418028936109d6711901a2848d91e6e6735067ddebb2a961822ea2c84a97e84778b76425aa509f2459c5672b030458ebdc5f6ce3a58436c5721d37722744b05cf2f088eaa581dbe35508101089e03a0589f91dcf1d4c055f3d81e6987bc01cf2406773acc0c0c429b0b1a1d678e896512c360a9119126defcbc3f9a8b91873258197d298904be4f76ad4357d0d0f9dd6ac115b4cdf620da192740286d90e96f7ce808f172277d1393a374131b7c57b239dd3e31c9baf4abc79970eb4c351711adedaf3f54a0d883dc704560fefaed97c27dc567bef8066c5fe22ff26d96658f2e53619c59e3798b2a1ae7f961104a394a2731fd0e7da7d413abd99c1be17b9b85fe17047b1636e6d8eabdf094cba95d903298b369abafa870bc4e8859eff712cea9933299bbb8bc85103108983bf8f0b12e881eb3f527f4f55f81751c2bb380c1642be1164f72b31fada3a95cb6cd06cc488cb41ca3889b5974a39c7e8c245609ebd1b32b04c682b839e2aea4bb7ff44b9c8de3d6dab8582b45004cec9dc174977e21c1da676bc594aa2ef6978e342c4969e197710464bd1e889752a6afec281325a7d1ca557779577c7bc10f1374269cb720bc5325d93f9c758f56f7174d3ed24a824fe87783bd58d868e7ffa59e5f0797f12feb8eb0ac4f352a08575924f920a5ea4994cacb72107a9f5e18e1067d78336538aaf138c0329c0684f44ed25af8bbae7db712829441b4c8c86c69bade07b1e9f229d941b7cb6d01f7a218775ed8cc67cb1064f03cb630976c9c5a155a60f09b1af3f9cd519bda7022e2a9eaad60591c020c165d0e99b82948737ca2bb58c65b06e52687db002aacf2fed9f3b26b7b0510dd13e43f948c37a62b1145e84a687c20bf1f029693eac704fd42dda7c2e3ce3dbb4d6f7494020cd99b20cb63ff21137db20f89a228245b388fd3f7eb8ddfb57ee46d566fad8f2678056e924d767290189c56f1b03305a2053a2b51652fc35830d98df9e1195db348654bebac0f2ae72782a8b1f351be2d7b66115954a0ba12a1bcba75fca0879d58b70a912e56057a10503dcd792279fc3b75ddc04c796f429cb4f3a6183edf7ca3caf44542465c55667c4086231aa04c52b8373c0315146dc4c0af57bc4ab28579bcc81b19e7632a163825cbdbd98bce9fff4fd5f4c7819c2fc5e41c1768ba40f026321a4516305b0efb0672016b0791bb270aa69a0734d2af54ab2fa034d7a368dc3cc101b1fee36bb56d4f4f2f7c876ca45b4df5638b7decc6b70a22ade82180d00e9d104a6b516b2b2c5c02481ec23c04e4fdb691a28025d596475d20d6a0e3d6e16f0510c945feff330891927ad521c4d38c8ff0a5e78cc1d5331f526ec7ed5593a5f928e788d2cf8a44b06f02edb73e5e781d2df8b01e6491abf38f1bfc729741ba5144c9676144055ecf3f7c3eca699c76661a166483c6604acb692c99c09ecc1b2f71915e2941adde1e0fb44e08dc6ff6fdaa20bbd3364617b805ace61c8ba9d2c770d0f313d1d45807e6764a23a2554f2b11ff2d4c4aa6dc96982bceed283d321d5718aafe5be69bebf225561e3e7c691dad9ac830f6fc4433efe059056e45b20d6f1573e48e476823b05ca87a0e0958b0d5d23b0352e4d4c758ee10435eedc67ee576cfe8602d946f240c08f455040ac7efc3592052a34a63dae9ca47060d8ca811785eb526cefe0ce51311c185fe91f289564eac413aef36511d7140143308f06c800154aa4bb491a8ba9e55f8ee7cb42a1aac4450bb70f143f9a5a6050bf5372209d97b932db794524a2903870c110c120c39b4ec0fe1b05d7bc6f5e4b0b7d75ab7161ceeb71f326c99ddb5bae2a05d0ec10445298c1fb01186d2d0de620b4eb6760d73b18361c71b72225e0ddfa20b391e399963934080cc8fda1e649c17ac0632bffa1a2e3b06c80fb9dc39ecc51521cbbdc59aae0b72ba3d0d5e8c86c32e831793e13bdf389bbaed725edf24d7c26397e392e3e6aa7054b6793a2373b7431c9559657e77e83ab9cd115110dbdee62ea801950db3a30bce08da6c3899b22dc7f94c57ea673b7533683b75cebb4f795a97b3c1de03ee782cd8ce6ff80d3ab6f31e70931cd724d3146f0eb06b1e405f37d05704361fd4c868f595d21cf3a355eed0fc8ec8b7e83a99df0725734c94462b916377821c39e4983826fa8ac0371fb7b1397d043c8d24db3cf515be6d5dce0a309d027a1620b38227b7d605b1b7d1ac20f636b7d138c868ee2748c683dd323f0af35830d823f008e8d8f6469311087b0f18b3831819dce5d8e8d55459964dea1d93b5b1b13f34281e3589b2e93b20fa8a4a46311e1189b1e596db0890be4362a873426f93b21cd374857768b3315dd4c9a69f7788beb8a11b6ea81b62c36f78ec7688fc0197dd4e037ec365b7c3803f407337781c144a43bf492ae50d9b1be2e8c83c3bb7e13cb013391ddaf4dc12fae280e865f7c2dc1c0a4be490e38036e7b3e94bfaa43ccee79eb53364c8236883c7daa9a18647703b6db1b39afb999fb59aa35244b6d65ed2cd5dc6146a6c05fba98154ac9947b6fd0a41681d327edc391f0e86ba57487da39cf53a30772a5294cd6d7406e84ee9186bb8e3cc9fe95a41873d9ccf7471db89e37ce8b99f8ee30ef322b8c999bacf74b1707aeec7c6c5bb431e2b5fee7cce63657d9d6cab6f911c99b3d11c95f96d55b20f1d535e8d3556c7f9bde8b0eed7b715ae7aa1dcf12208f32268e7d7820eb9ddfde5857577df86ccc7745e838e1139ec43bc9dc76e473e36c377746ce73e1038cfce25ccf381c079887cde7642220881c76e06ed75939d9079221fe2c9d7e07de75d063620b7bd2ac57129eee31ebbd4e374ad18cd8f0ac940aec949def19bdf21fa829dc2b40e2a499e3be49ab8a67813374765ba3a1de6d0b251cf66d05c981fadef66386ddcc166386b47d270f908e20ea673d6ce3cec30cd23030eddf12248e4c60bf1de2b2fec86363d04bc9804857cd34e1c0d5e0465f022383bab81d4af783562407b3dcbef2d07a6ed5ed0be725453cbe96bcb3b24884c1982840c5394cc8f2ab1a12ba30625d31596315d55a782981f0db26109912ea9866ec8eba9100d4ba62b14922169ba42a19d5f9d0242294d5728047bd5a920ae0986d37354809c9eabf2e3f4dc1c2ff8b4f0c302908f206e4edf3199ae9084ab70fa8e89e90a4738014ec3d6534c9c4c5798c2534ccc8f16e53c71014a02a280a7ef8c56387d27c5e6f41dd2ebf49d18ddd27485351fc0e93b35a62b14c0799cbe6342e1f41d1b34a7ef9af6e93b2a38a7efaa20e0f4dd1c07f049fda080ba20b8d313fe9d02e001189a7162c2e98ed3a39e4c57e872fa727a5494e90a57a7ae6fa706b83dedf17b5a00d4d274851e4a8de90a4b40314d57c842b11113aba0034e8f6a6a69b90faf050afce5ad2081775e2a02bfdee619c91e701626408485a7406485ab40c4070c44801049b1a04320f74144052229bc2640e4379e916c0b5180051b5638016c68b57ee8d0c75978e1a665830dadff10f9d0210132203a7ce12d6d04aba0c31626a20201ee83880f1f3a6ce137447cdcfc87e8051d0a11b90a36b4883c051b5a29fc8788003af4f11f2de8f0e62f9c8509acf0970d36bc5ab081051b5a60e13f442be850c886bf2660c3045420400b87818503b94d912134a1cd07a08de0213f4439435c18925a61888d0e73fe1a42e342aac8906709d8d4a07f88c2019c873682c3d45140e1751b30f3380ada48ce35fc87e8a5c3013c6b23d92b27e028682338478728e021e000fe1af27ae97085e721affc1fa204e85068c8c11a5018721a6d24a340f37a0256d0613e789b223c743880bb5043ea9d36826be0d1ad50834d0d2bd8fc8768003a14aae13c8aa46a785764003c56788ecd5d38cabb32e81fa23086d0847fda089621f51fa2cde1e8f0a2ba4f06137418e4296d24cb60020e0a029d0ce72280a241ff10853b0c00fe10a0c3ee286d843b00f7692339003afc3e1380ff10753a3c4016c367b4111c4487335806041ce09d0c5da7c3d439193aee3f4431e850488623808619196e823692674cf80fd10174d81d01291d723fc05110d8c7a1e1c334e06c4fd180a22185fa0fd1d6a1100dc781c047c30300812f00ff21da7498fad621ea38b737c062b4118c63d0f6c2b6ab00ad8d64d80262d8b491ecc25e3d23d9167a80ddd13f446190f097d146b096b1ab5af575a82fa38de49d5f1dbe640af80a16f305682318b67af985dd5b61b7fe8748013a14825d662706f6d58e4ccc7f885e7478ff52bfbad546b22d74834b10bc857634de8961c75d76361dbadc6a23dcd6487e88b61fa2203a14da790c37681d0a011ddcf9b591bc85800edea091fc10e9ff8768870eb7ef70d1a1bdcb71280c407ebc90e34202c08d838003b4e0b9ce826780fbf07afcc62bc057f05cb7f10cf097d7e3d92bc0539eeb28cf00efbc1ee7bc027cf35c1a55c5001a35c7fc74caa7003a6583b773221eec35783b1fe2c14e83b7a3533278309d52c123e104f04678cb633d050fe603f06a78780290a0038e82c73a8d07f3cf3b21001e0026e880cf78ac9be0c15c7bdebd1262147480fe212241a796cc8f8ea05348cc8f5ec6636921d8633c182d047410a6532dde69afbc6bc60e6050d92f5e1606500da6d82e1e3562072420ec1dde4dc245930e5a34aa89c573216e202a2f03ddb77acffd98bb7bd521c680e4800e9bc6eeaedff538b4eab0b56c6bc771a18e7b207157da059175a09aa60b55e59e6ea74ffd4c1777fa14502a8854d074a54e9f52925a425f114c31d95c1b917b237e71c422c46e87d82189dd0ff1a3cf5e0782f8d126a6316ea03473b525f20c1e5785d2d0eb789d10af036280d72da134f40df03a265dd1fc1ae0551f1f90015e04c3288d201f508837411d2f82337817044cb82696cf1de29ab61dc79832c70e2f141e5f500ebf4d6fd11d9a1fbd4e64f02218f3461c038da65d7bbcc55241fe0eb9a64d3f061a4f76d561e7c302d76433824b4f90f942161ba5dc1206d8a27d2cea00098cb57610471168354bbe7ec0a8088c4924557ca1041ec92800c1184d13a0d54452c6b045fa8ab9e5d26402a095237247841d4c7246459503365c56902d0f56964883064928bd5091e41247c63b1e2581d4032b69bdf4d24deb2fbef44e2ece8499ad3e64640ab3b5e56a87b86e7aea72ac662fe564ec969f91c9f6acd1a2eab7457b7ab26d71bac2f9bce79c7335a79e13cb6c5e6a6bb574cee97d639c73a439c6d58ed4beeaa596f411539af8ea6539e7ac73a26ee04d5a25a6f5d5ee3a6b2837bbc3cbd4b3a80e4af865d58dad4a3a677745940e8b2c1f723b464a07a54eaed5e25a2a287542a9936bb5b8960a5beba04d68b5b662612b9dc2de26376b92615bb1b0d54e816d9563b3d6059cacb684be2fc374e633caacbe5612a7b4c43403c46e203159642feb843a294ef6e2acc93eb1aab8aaf64cda5b2a4e5796edf888e5331a5bbe455e66bae8eb726ef9968d115aec301af94849da724a0f2062b083219440630b16d0263b8c4c4c76101522c050fa84124806c9252590c17652a43d9450a26c19bbd8810c1bbc21842796d0823eb143897404133b94493f524ed9524a2679d9445f2c5fe040a9088d2b7a70833ab14359e50820489f9f2d653883b69c4bb64462cb4f26f2319c434908118300c3f964cb28f447ca87538c2d393276d8b25370ec54173bb503d04e21a1c54e1dd5e5a450efba1c546dc1ee8e18daddb92ea7e3be75395cdd96f6f65397b3695dce4997808aad1d77399a0a3533628c50dbf751c6576dc10f3c18818f0f7855bb4e578cc19235b67d7dd5e348bb9c1aa5ec4a8f9618b12909a46ceaa23d24ed3965d24ec94d4926c193d6bdda85e99236f4d58384174b548e7e40c6133b002f1ddad70629d81a8f2dcfe35108fb00bb0649294b55a68f8f8f4f121c6d66a1882b76a50142fecc7754c4927d2f0bfb3edefb7cd318c2be2000af920faeb86d9d2e9a5d83b0eb2a538fc324c821ce54905b9936022158ea1a48778ee0042e93561cf19249137776269326ee10e7115c91d7681a81173f7e50340232647ef88069ae92303af120001e66a6f15819226a31e3270a951f2e569afcc000889fa5244ab079b47a785249b0b1e6efe3d4ac9d49e5e88c17d401d2efd8d7efd05f1de349128589221f90be6a1e22d8a4727446920f48356b5acd9a5ac73ecaafe5c5246509244f9822bf6f4e99064b939ced78a4648b1d3e4ad6763c224249cb0fb2ddf1480914254522302a23524a29b691c618a33743395f40e5035f39114c45c412823acb61a8b3a0a0c0504b3485a2d243948929a44b38cd4304ebce0df1749a05cba9739ab5939f7ad63122960e77064490d33bb74a113e482209241f1f9022511afa18d004bb1fe19ed2aced28cdd2491df5088614caa6efd146a6345489d250fa1e6fe408d22849aa18630c353acd73c467fba6593adcb777470108f98280c9a63ba44c7788d2d0a3bc5b4469e859442cdda11a64b1c3dbb4299de32ed12ada1239a455367de755ce434adaf428b8917d6a100fa41c41b06b229a41165a04f9f880118c60587db2969e3a5d88e69c53ca39e79c72ce2f64cd1e75d8d272ea38f3a4618c57ab5af1950ae8d9566ba0ee25b6cd815a6b95b156b963144196e78935b1585384494709a7356daa9552ea30bff8fca29fafb54eefc89c19508cb1d66a6b7d0844bc3db5d1fbfc6296d57a6f5edde3f8d2bc3855b0c548638c3ab416676a5e2bd26cb3365545ac345315b0e0849625f9ee78b484156864d4908b144c82586204494b80e02703ad24b120458212d215d429c961dd335e460439c43922a952cf5be4224ae5a534b6e96c5a9cdebe63bdaa05b02aac56bbd2d41935567babdd191713af529271cb068525aa2572580380067dc98739d3ad7557323e3899c27ca8fddb31cf6c5f8fd3bcbb92a295ca5c5b338f66ded46e3d214d6f199709594f08eb150138e923446154d813e38d005a4a5fd2915636f4d3b8dc72bad7ab736ba72f2bb592bebcd2628d4b97535b3a05f4ecebc97dd230cd108aa629e68cd4e75991b9137214dc6966db3639abbd274fd3b4a8a1c29e5d594c403a129d2bca43531daeba9cfa22a596a7b5ca6dc7a79410440eab4b094ca80fbc9060021939c42b560f60646258ac18a4950e725865b8158cb0fabfafd5b2b18924ac4102ce79b5fabf2f9e3341092e55b494ef58c271e4107b5c955465b3fc0e6fcf11b67c48b373bc917b3e55460e2b4b2e2387d5042ec69b952de3c34ce746e566cb27d39ab1e161d423cf208735001c9698be726c92e79d3545f393fb624e78264f9ae7963fd19598f97dad968dcd33aba960e4fde52ad2448931e52a5735a38ce7b765b7936748b9748bd828a5b4de4929cd42d639e78cb5524aafad73ce796dcd26a514c79a38e79c73ca39e99c73ce39796473d239e7d4e19c7386dc9cf345c976d2b693f67b6b566dbdb6ab029fb478d2a2adb7765784f9de7b354c2dbd9452ca5d31717705c622534aa9ad2e7b4a676282b8b65a7b0e5839a3a434d52464c9ed6d9aadba27f86bd91c739a0a466ad4564a5fa9b7edbfd6ea9eb85389ebbdaf77db8d9b188ccc7539f576d36f14756d364f49e8a528da714d3aee0474c31025264c9830c937ab28d7843b4541b52658b5622d6e72ef856185d2dc84a585b6719bb050da429be036585a5caeb6e113c59b76ab772f8d8c8ca43c5549519035d9915340ab76da50a9959755dd4e43dba905881b58ba214a4c983061a2ca2b312baa18c3aaa7a1532eaaa9a18e43b164d2c8f2dcd049db3699982e67b58d4082fc22d7b764adcba9d2c80d122e7f617654b56a17d1c8f2131bc5f81849242d04da5009272d17a12eed295dd5de0c6b432d2836996402d3e93c89e4e5e890f0c30d4394983061c2244529a59716017ad09e3baf628f560b87d2cce3ce060a05cc0f88cfcd513ebe8ad0532a6ff77db3d24db55ad13967e6f7ce49963468b3c8a42d3b9ab48dc2c8f2f393a767d9e10a63669c37dbe33f1ead93a441f338cb20a3d3e7c948bb78d3f34a23294ab2ae73d282afcdea924a490a895150c52231650824a0bd8eb5d65e69060a90b0d65a0b0024bab8a20a1b2c514114501891553bc6ae635c6badb5d65a6badb5f6deba7282284a2718a226c7fa404ed905d8d65a6badb5918e2648daf8400c88c00515d0126d5b031c6c6badb59609945d53701a0137c4a2c9c1b469c182e7083a8d913322246936c0b7084cb0123b580952a24a07431c4c4c909c60906388c91029188800c4c4042a284ca2d0609843e9598d1d497ea02223b3ec78f4e3460990209123d71d8f908c800a238640c2640588bced7884440866c72320137491fa316b3cd5146766def558f572d7b3aa6e6121913f1f62231c38e184500f28903003a8470f29847a40e104126600f5e82185192d49fb7e8b5bb6c45e7b56cf82d1af7c45c7a8514cf6e00a140c01c68ea842a00370e547301d207d0f583d16ac561d8b31e8028c1d9148e08a8e1db120254abd07e48672289b26877a54592fcebc751c956d44d9146fec65934a37713159c60c42f005183bc2c9810270e54726157580f4d58bf5f8016347e4137045131d995d805413a526672d376746717606eeacf9b83b2fdcce9a3a03f39dc621e4be4def47dc59f5e9793de0e8bcb04897bdf338603747e340da5dfd4169cf1dc364d4d4e99026d5eeacfb981ddc967b1f5ff431d2a3f8a29a529934f757c74913d239c2d857ff9051ab8ec91a1401083d80b1232b239002d21f21610ab07ee53d20ab45c7a2176280b1239209481feb61829528759a340d70cfe251244a73bfe251a3a11c7e734f3da4464d4dcf9d359a06b8f7e2eff785b2b3a66d449afbab19d117eca658738f3339a25cff2de3ea0ebf237dc5640ba688a3098c1de9886803a43f328fce006b4c004e80b1233208a49a48fec8fc01ab264a3d5c5d2bc9216da2b234bfcba4e9f7435ebacf4d4d6d441ac3fac3be75877defa911cd3ac41645e7b867698a37ae7d4f9be2cdddf794ca745599aeed281dae94581de620c2fa3303eaac20a9eda9c72dbb1950e73e531e459ab469df9bca997a85f22895f9dd771ead527fbc1a34bf7bda34bf4be7a8d8ab41ccef569ff9ddec52a4fb8f4a4b123761923861468f96136648b14550c564dbac65468b6c59989fbd2c41b6e80be39cbf574264fb1be62b46165a04883775bbfd8a4a6423aa11cb88625cbd5dbd61bce999e95ac03dbe8c0cc6186b322bed61cbb65c0433fde9dbcc0ff3beab7defbdca93992eaa17703ae5fe7ddbe67d333bc8dbb167b9eb61dca1eca643ecd91d1609bf25f5b20c20a08e3aeae0b6ca454dcbfca0c775afa62bbbbdcc7cb930b5f82240a4b1ad58636f6570ae2ae80359b544a5647ef3a12a88c9696e5138e184134e0001cd843469dbfb45d624ddd9bf77ea65956e875e3685bf176fe6185b03736b60eef0b28d58636553acb1d488f544a64854cafca85190f94c53c1a0fccc2faee667edf7034d0a5242e507579346229b11f3d2fc6466d2598df9c97f404b91d2951059fac49b1e5bc36ca9da52cb277061294a4129d0420b179c30c70d9c40a1f73828def4d8516cbc89bb7e55c6b6624c19b47a622313db7eabc0fc2c8c96f0812c81be2ea8ce74b1f0d0d20ee5d0d2e6804c57d52cd8c477c7316e60c4a64be48f8c4c2dc62273c0d3a1ea23b2e6ad64e6671fb383fc31d218c2aefad7a4c8d98c1379ce38914f5204b1c63ea45b461f928898827d7cb5f75e90c3af3e9399963f3e648c73fe96cccf5a8c85bd5acaa0a6931a4aa8fce0aa816b265c196d7b16d561d173efcea25ac804b99346c20c7a8396e8ab647ef61f9096539e6db24c7689d254a13a88afaec69b1a0474e5c38ccab6d90fb6fd0da2af4e674b335bdaa91dde205b6589be6c1c172808dbeec069ab4469ec0ddaf6d88c1cb77da6c674cddb674cd325b3259a31a991ebb3a56db31bd8bf2cc59b1edb3e5b8a37338e6d7598dbd856676bac8c92f430430a279cf87c6009daf434ec81c39995cc2c9a34f4f5519ad8eaf1f1f88ccc5e0991578ae28d0b6956115fa7d3cfa2785337fd7c126fe65bf8b361cbdc62d317a1af939e45995eb23a4c7940620d0c9166ae00069bd28fd2745bdb41c635984629c8f49b3ced30e3192df2dc61deb148cc54b02bd838618e1cc6258c8b9ecc2f8827180b28d355f4aa671118399c45bb882508c7d1b4e7b16542e354c1aca002c68e474aa4ec6d729a874cbfcd4833cda2f530ed208b9ee774aa59550bc9ae1d965d3b3d0f113d4f76ed3c4452c3a60e73a7c3dc998e94066b2094663e467de8c1174f10c009987dab7a882c97d4ccd811b9821cf0d004b43f22c330020c25c0fbcc8b1d69610ad06a22edc311167e005e4d44bd2a2d254872c97445f047bc89123f82f33632bbfdce3d96996649693db9846a890115c0218026b7122207a982e28d0a72e87b087ddddb53cd611264fd93431974af7bc8a10ca23268822c1009d6acaa33b8d22c7abf639fb1c09dab5954eb64bf3163e4ef201e3268caa0e9139bdffc0953721895649052139d5b1b5b9c094748a24186c74b4e6639ecdb82430e2d7b7be0081e8ecc6a63e156381597398b7a76fbec282f6e9754f6d89d5028ee855b71305c0c979d1503b37a4175f728ef749a718f9de54ef6845b2e5f8f166ff3c0995be5897db0c599707bc2c98993f166c3b8dee26a7fab6669afef01bbbfb034987dd53122ed31ab61d6de0dc88d3a6d3fe1edf4f9e17f52f2c478dbb64d6e72db30ce7bdb62a7618e8d1c62b9c41eaf74271de88bb51de611c4aae34db384dc0ba375a8bf0f5b7b7b954b5629bd697caa85ec75c0cfba4a14d95d814738c618638c639350cca9be1cebb0b557eef2ed14e6efd349f0581e8ca7f224f8e2b1b4eab1b4d5ea3b2fb79ac7fee52bfb97f3ac46f058da598fa08cb73acc77568761c168b9b4023ffac2196f32ca749d8e776c2d1bcb96dae2cabb6fdd71b752372f8756776c6bc5bbe3acc812ca746110db5babc69406e32ec51de31fe30bb4f2379df258dae54e7df5c7fa97efd8af34cfbdfd8b3d4ff6964db5727ab52cbdd22414fac238eb70b57d07de70e8e675b9cb5ef7d4b6fd7482b1f565b59dde1dc65b6d7fd95eeda65af1b89fbcb8555cf7ad3b3d76dac96a96e3668cc835e39da21d4a67e087dccf400e4bc8cbed612fb79ad5c15667759a25c4be7ec7d6c31c46f35cce1382798c17411c6f249458838ff14f5f305a26512dbe241691061f67bcb114637ef8db92f9612618e39fbcd8ea4536a566cc11dcb9d4b329436a5cee334664e943ca7cebf7362c4cd7e9abbffca652de4a04a38eadfa8a3777ca8bbb087dc15c3e05e3e948ed8766450eafdd6e6b6d95c6413b0af54c872d56ea727797790461ee923a4ba5594262cefa0ecc65344f3dcc599a8708560f731efb984730e62dcff20a0b095acbe8486956c2550f38f2c53967dfd92f8b0ebf1d5e7ba769cfbcb0b5b3abbcb0b547782acc3bf570b5530fbf9d7a4ce95075ed2378321ecb8b9426c68b14c65bb9ec5e503de3af78dc555edcd9a35a967aecb2149771dc37c9a13cbc3b4edb782d16e677bff2fee2693918d71b9354132b6df81bc6da65e6873fbb1f9ac6dfb8fbc1cd8fe658838f7fc0c41a5cadb5d3da1fa76b5dce067260620ddeb458831fbb0dbcd06d4daf620dfe0bdd3ee9186bf071266ecad9392e08ad168e38e2d8e91c676e48509a6609b1efbe539f9a31225b94a74369e43b25ac718849756c51245747e8e4316df1b215f9f9d8885cc28ec79ba5c343049bcfceb3615b144bb8a062e403f2cc67c7dfa1c77a673ed3172241a32a30ce79b5aa95525ae99c73ceef8bc1a1be95807c5f7fffecdb96e43e6e47c2bd6be1486b1893c20ec86c760ba0d78e3d5ac7ae27cd6209e9be1d863af70e094a9f3a207bb6e65dea619cf36af5e7d0baa735f2bdfd762434d339b4f68db4a20eeb8eda346c8b71faba07d4a666a43a1296af7c38c8186356107b7cac75989ffca3e41d6739102424a41d7f391296bbecd02cfb1d2f9885e53b0e63f90e1616cce2a2691eb69aa6c9586c2fd4e4b3f58e474262bc316567cfb4a05db3e39190127bbb9cf5ce460e231290eaf21a9be666b5dfef783dbefc86bd93f693372313ce0eed73686d7ca9733a7539d9678cc85609d4919df61d2c1ecb57dfd9f116cde3f21d5f691e2298cb779ce7c57a3da8e41c5a5bb377f1fe61fc0db95e0576b0785817995fbde2d4bd20374fac61697f790459eef29d1d7fd13c31df719798efd018e62c3aebb4d4cb4cd7b597f12ed0da38dc638c31fe36feca7bf15cbc486950dd498231301e520f38ac02a60b5fd3e16a656f5b382f87d67dec328b35966f6bbf5f79f1c525df2e67bbec5ec03f1d87e2cc2d87d6cebe5d4fd370fd9c19ceb410cb414c5f40f046c22c48f886f9d57f2d1e482baa6ee61434c59b1636bd5c42a9dd743b459d461b269b8a41b9192332093b3e4ae51e05034d74c688fcdde56080729d4d4bc3bfa659423a4ef3f09cdee96fcb779a673ba70f4aab8ba42c405fdc51dfe9baed9d66559ed3855011b4280fe79831d2d062d7a69a24870fbfcd2a52012b9a6e80831d4aab25c9d55bcd4f665552b68320766bc723276af00412dbb5e391134cec2df549c9b3b5c413a5174572686daa435fd16a17e6579574221029bbd6ab2ad3526592c41d65a9544692cf4a881ce34d0bfba543e552bc61d9f59289beb24f265f52cc3c0c509a7a9928f95072287d92383742bcc4c4fceaa120ca614c1223e367118c01c874492f76bd6609d138cd93fd94fda45bf81ad36a69db83a194354530aa6caba554b1efd9364aa42d94c9252a3e12e8bb22cf87d2c78a8dfb5890b94d87e9d2011f676e8f92b59f5e04e3a91d7524a76f2c8bb239694dc74efaf25b2cded8673c9818d8610ead4d23d22b82f2e3d820cab1e7d6e029426924132b9b70f060d74cb3849cbe7d473bd63c3c44307c4d674a53cfa3fda4c1cbb45ada160ea56d8f9bb69d7054d956d7a3a1327601763c1a62b2b7c9e11732a62d849fe9ab08a5a9c7b5e6645610f94b224b9f6c28c3dae5d6885bce1923f2cc0ef18ed9f177322d8fbfb3e92254f324b84dce8652dce29872d8dab54531ce996a393cb146e26787591d7eb0abdfc596e6b43d6c78baeeb5624da59f3aac74ce1974174e5f276da339ae705c00325df7b10663fb4883bfe122274ff35c9818037b7eb38746c76a45dfd8363931c0011d62f1863e9e27ded89fbae3d057ea3af145628c2bee8c2c1fe299854cb04569e661a864f6762fd04d750db23c8be74274a2f27a641b25e5cd44fbda43019bb6d0991b36fd677338e59d84a78d6325a51447a44e77b5ca811715479bc53c14c50b7a9086102fe062082dd8bc18a1b529c1b192b2e2f8c428ebe20d22322d6c59b17481448417488ab0c47434c6145ddc271f483d09c129ba01041c3c247942e40229a4203113b36663b6a298406e4aa58e04c24a663c7a58e3881f274bd288821e6dc1841aa81e12341182cf162150430a272c68c110906454f04043450a55040d2690fc6007212884a05c34e1a6b80235451725e4e0988a2c642a8e598341c50c3a3732ad1f5035e9ac5849398511570a106040a748819502031b6cd9144a644f9cb62886085200440c56529600067514914419674801c2480a34589aa418424b06c614495059b66a020e4bc646f7dcd793f193d6a37b9b9c95915294a572f6b0b160d851c7b027457db04216a4c87bef8db78935f6bd6f99f7de7bafcb947cdaf1a889346c1336d8ab26a4c881093d940106164c00c1861b2b68c2091c65064df490adb5d65a6baddde10422f0c1a20e2abe58022d55c1b674b8f0b67aa6b239e7c4d16a3cf389e2e0b10a183e289002b40514556c31843a9a14418409b070324494386cca8a3c310f5bd42aebbf5a817099808a2806136104d524a21941ec1084358278d04393163d4ca2a4800a23aee0428930b260c2c90f41960e2e9ae8411319654ca1582001616903e3891a355ec0600635cb3b1ea580083e47f2430f2728aa420935a09830458e347c08420b4a80744513d95be6b7aa7a95c32c335d71cfaf665e5128b12c5ca0060e887882043bb06249b7f1fc6617b3da50da53c41155dcb0819212440455717b00c5142558430a326a409fd43da38fcac3a67416f5ece0032150e9e28a376c3044c5ae6520595a2282133f208132851c4a2a1213f4109186cf075870832448554891ca01164080220437ea78428bde403277987b4bc55a755ca869ccc1831b4cf1220643b000ac51ecfad77ff52d1eec2a04586badf566c7e8e3846453399348614c639c31c618a31230c6c7a8b100f65c22ca9e938629cb17618b72053926c121c674c598f406129bfe164d574c8263e846b94f361090c87c3615823a7756fdf6ae3b77d4d67573ca773a16ebd10169880594802031452518f74e75ee56c3622cefded3758ff3dc77e43bcdd35d5eb32a0fab82369905c78860a9ab746c4569282c3fa5635fb6c11a7ff311e438eedd77b8779a8708c6f2ee3caa73dfe958de3d82a10d91fc148bc7b22aef761e3ee7652b12ca09480e2d813204745a72872e94bbe25da3958772c9cbea31ef01611ec1eca184a25d3e4604ebae7a8c084604c3e71e290d8ce59a8e492298ea2c3a2695381d93489a43e2943ac9f25056b950bcc9aa7a07a559f53de089b5b35deafc64c7ef01b52f9de1840a1c4c7c7c40d45b4e045bf9f66d3beab0eda821d8f6161d9b407a0e2dd91e4ea06d88023155a1404cf430de85426968bd1cd3acd23d82dd430ae4f2b9f218116cc7372004802d30d45d74cce5a8afe818116ce5281d73d17345293677e81e24d8a291548fa0eae184a2d22cfb1e70355934cb6a9d9f1ef0425162ed4870bb04b7b3042f160e220228272d92b4644142b0a469a1c40817096b1125db204300655a88f1128488242d7290a0202d8420130404485186c8740c4551424547d26a0545979963f5e24392225a90554ca490c22509172b51248101193c602923091c4b0481442584ecb2e351104a76fc04f1c41a416471c28e474128b5b1cd4cd79430cc3d5da747ceca8774f79029d8c7c7160b93c69e02a0460e31ce5cd11ed2c70a31057b4d87323bb4d161cbbe3adef8883536ecb1eda7b6d166e44bef9557d28839e963832450e62187b926fad08929cccf31f63c8f7fc660cf8729ec19cebcb0d68933c968b35d4e46b33a84d22c26654372ce79c8fc7492e88758099315365d6134da436ef83192524a5cc55da2d6e3d7c5d9c6817e94c6f6a8ad4a29b5b8041eb27c95f79115e4844d2f773864c77f404e2c69b5f6669c7c327db06199654a69b578ba22a594564ae9945d1555509b29d572d1c665181765455c87ca302eca8a50a90ce3a2ac28956f0a955516d5a9563cb0edae2b2c2d3b328c81f8e5b62e88110d06bc71d8a1715bd6c4cdb2286e1437cba2b851dc2c8be24671efcd6cfcece365ad37cb8068acbd34b2e9e51653d301526448acb8f57d383636178a09c5bd37b3b16e59773c712b0c501a0b501a127e608a1506bc4f9a8f9d63b5c30757b8e08a26427045112c43c8da8e475780e08c2b3eb082841575b0c08a2a4c488c333e6eed6905d30eb2c78814c84147113f58810ab6884540e389105215ad20c1aecf5d4ec54033ce40c91060cadbb51ec3510512bb1e9595745dceadf51c0dea182e3b94d9968a216ccbc5b6f6f909946dbfaaa2876dbffd45945c9044173bf8f1620a174c99637b1417044c40094e50be0895b74004c38f8a2976ad3fbdd1ea61430510dfb6d7b00a8e88e0d8828b25592c118389335a72ee9ce28c14767d7677b5f5d65a6b6dc114444bd041833ae87002ac61ec5a5776adb5d65ac60eb9996d371ddbd2601f4521832227b63d2aaa63c7232964b0ed14466cdbba3d5414418e39866882031f4c0163dbff8b268a60494d91c3968dbc13caaeafa732495690851b499688d2066891c640a349d56a1d0a11002036ed7882573b48a23252439228072b32e21c20519a8889deb831a8014444841870b004850f5c0c39685a8960a5c960b58374f1e9210c97255966c7a31ed858d1417e0142810f4ad4610592bcdaf1c8873058a2c82d3b1ef990839716e41d3b1ef93084972af2ca8e474041b4acbc1d8f8058f245d0be66c4d75545f1e600fbca36f6956bec7b6ff79b9dfe6ebf9749f475fabd54fafd2a67bd6511c639cfede4690bd8dae9254e4b1f4a73e9fdf66deb59c0fb368d34f7276f8b34f7d8638935f7d23361671e0a9b7a1a907bca145a26cd95292a32bffb1298c8f6f5614c7a325d4118df7bef755c98df3d4ee68120cb227ab3383f7a5914c6a538868f2c8a474eca8832e55e23eee37cdd6b389341452a305dd40b4af345a62b411a10523d8151fccc27470e4bd8f3466e99e1446bbd6f863ebc2bee9d699e78af970337a36de6d7b2ff7ab462122d21dec8cfd7232c5e22d753d5b35f2f5b7dfb8639b4ececa96fa9f9a57e5359eaaa94cc2af36c5a3d5a90a73e23679740db050f48958ce2beddbe89d2be5ff926395de7b7f22de5453a72ea757eaaa734b7e28575ab74c862739c79bf7145c87167ef7272ac69794b264497b35def90c36814351929ea5ecdba1767729b773d7a3aae39dec496cf4608d9d678d35d75d56797a3da36d5531e0ef7aa6f77e535a5f256529a1524f5ec3f523aceaf45872c3a8796bda2c39b7a7ea6f252bf3de697d236f393ddf64d8b525254a763acc9e8c9f358f7b18d3dd6d53a73cfdfcfcfb67ad8c82062deb03129ced82c3726a298b24dd8710c28d8a83f8156e3fc093b641ce8a546350edba5c661fb45853970606fbf35d4b0c6999c8703fe955e38391c650a51a6107a400331d6d09e4db5adfd4c97dcb76f5baa63a5f40b73a7f2755c404d7fb2e779b4e2ebf30a68f97c2a773054e9e34a1fd247dc39bf4e17cb737ef64e41c8f9f1dd33f55859bf107758b7ac2b18cfb0a273fecc7a667e1167e6af49463d7ced72582e3b18f2bbaf78ac95cbbd72168fb5f2b85958bcecb156e4c89f9ec9f4f419a075d49153b9841f322acc1af66287638dec5ec81ee7a7bdc5e32cb77971ab8af2a69fae99b124c599cc6090bfb7ba65afeaae5ee621c699b93253b669bae68e74670f39a12b5f59b44c9e55637ef4d22ecd8f3e6b91439b14daa54d6f93a6ab8cf9ca244b6652eb648fdb00f326f9f8f8f000c67af963dbf511a9aeb14653bca953367eddd3955d3f69e4ca96af5e133da20922ecc845136f6c6ddf1d5f2d2f1e60cfcb26e2abc563cf4b26b6a8624f1b2239918acc8ffe27c8af38287b45a457010bcc6f4a195446964b661c39fb913e75028960e2200f030202fab99977650a18389dca3869f993ca22dbc3421b634dd029c847498c1c59d33a649a035b1c790155a6209fe92b5390414e90d82aec78d484696f93931598dfb480f42195c8fa4df32c707a23e31f384849c8811d66f2f5a7ad48052c305d9a964045a8fc61f190bfb2e318669060b7047df39b1c30da8c8436a399f9cd2ccb212b9a210f984eb780ab65f490f9950094e969c2c8e169a4d44983aa428c942665db69449bd8f898cc22f9efb3b149c520df87d328a39402d1170bd7c35e75ad9a856315f78015631ded511ece1c9e99cef499b816e490fa50a0e98a940dda343f261ff9951d4aa61549139ea67cc91e481afb287902914a9dce0f924ea78e84d372c9354fae2199628d65a22fca4469ac3d1365caa80fdd21d2d857592d2d838df9d9dbee47a6a952f78352f9b1146becab6cfb8d7259e570ed72369043d7883596c220d6d8d36e032f30c51a4b39cabd91bf5d32514ca78fce55ef8e64e5a8c72ed32c21214d92435442a134b668fbf8509f6d9fa7a63eb1c63e9c3ed127fa74cf6eaf65de048a359d9e3e94c63ea33e4932a53ff3fb8432f541b1b2ae3beaa82ec3f8b17a0e6b4ec7e837dd039ebec54e3bf64e3a6ce1675ea843332f87d6960762918e718ce1577d5adc88d2b2e3593a18e45979b7cc6fced9a253b26013438baf1d69ec2d40b351bc6961dbe300a12f7afb21d6e6dab45819d6993ee918bdf6ddda6cb4d3b39f748c28d3f427cdca1e35cdcab4d0d6039ee2bcfad5f7dbf66a9c5863ebe9ebb5c3eab5471046cfba5a67c3a134363d8ce6c3d63dc2a8132705232121e52c333f9c354d2221d1971cfaea2f18e74c578f399297c31c67b2b4afde03e6c7e417cdd25e563af60293b38c92372d8d9050b807dc7498376623cba1632de63b307fd13c2f8779390c3e0fcc573a82db59ab55ce55347ef849a3e9d2347e828dfced1be6aea6855e2412a5c10ff190ed729220e12f31abc3ac34b67f19e1d4e508c9e46ff925e71c93f3595e4cf717194f8224783de0081e6b5b1df6f2558c6883d1b289d2e01ef0ebc4d8b1692d9bc6b269dbca51d72e91a60b758c92f122a821a19e554a252a82dbc6e1ae3ba134cdbb40eb9aee3cd676b9bbd55d1e5939e7fbd563d95f5ab495fc97d5107d598d35143304933724244d43d290b286a42169489a877a17a250ef4e9a97f3cbb6da60b6984dd3613eeaac18cde6d3f386daf263b7e55a4fdf50789f4e3beee53b2bcdda76585b8c687b2c83d5bffc45c7eeabd0cb91b463b9f40512929c323fac0d15cd0f5fd3d2c9fc30c6d307c604f7de7befbddacdeecdeebdf75eed6a17e36757cbb49be955acb97fa1db58c7eb837bef6d2387ad7defbdf7f4d8b1b2efd86fdfa9efce80089ef40f924c0bb1df3ea4bebbef384f2776ba6e3c21c60401cc6fccafcc5918cbdc1e09eb231c67b25297790fa8ba55b134eb3287b1528fc19c95d230d6652ea36331678d30e1c85658a9c7a4344bc8cb610e7b39cc5387d131a29486ad1ea363f5abc7ec5fde03a674d8daa9b364aa72d911e3c17872c778ac96b3de03c278ac16cd1202f398c3601ef396c7e818518b86c59ca563f5313a660fa37bc0889fe93075ed2d5dc8a29338e500753a187f7aec344b3b7da7fb49f39c679b27eea76c7a27add3b49c3d94770a75b6a6c33bb3b0e73853152587ad6fe6337b12c4d9877c9a436bcfd7e9615a6b93d34fdcad134a6a765a7d3c5a3d56f4b57a39f7ed1bc7136fb84bd4376f6e8ee3329b65dbca63812f1e8bd32c212d2d3b46f88e11bee33c326fb98b86d23864dfbeb5b66ddb369617e3c1acf25f3a174f06a54da8d4ab974a8b9a658ccd08000000002315002028140e87c5629158248b945def0114800e87a6446e561789b3204861983286106300000010204000608686680338914762be71e50111a9bec614e215b2e050e215145f71e0f8563e05ca7e350352bebc9f878f2e0934f015785c4798211137aed4bbee53e77cc7f2f968dc597d22414b3e78546daf014ef2cf63c5b3460c50b623a95df5243d18f29fd56d542a71b65de142d6a37a8fb226e71d54f577050837f51ace927d9f9b7c025d1b512b69bbd2c2312a6705bc82c4aeec404676094a07cc5c757405b224e343334e242609d71b084b6523adc9c18b212296a101edbd4805841ff35963c2855e11234c2c9d146d3f76e245350e76efb57844a002e6f5d174fe14f0410b80df2e2405cdc0450cbb9478b16237c627735000a0ee22dbef5ead9632541e9bceef65b775379025d833333516e97b87a35e3afe32b669dd629c4a0aa479ba8e1993d82a48d33f18425960f41581f08907c34815dcec9cc507c360e00b9b843f9975d80243bb718e707c6f4a65f4d90e6e2c1866ac8647ae72ff7add759874a35c5da48ba9015d397eaa0fe3559289ab4cb3d63a768e1e5835c7d81d32e38cf60d1dde6fe2bc1bd76148fe738112614437cd9ce7836799c799db06e30b0fff9f6afcd58e3c381362eae2ffe415edc679fde5f945e481999b4759d8f826c4dce08e91ce05c0220113621abe5dd48da17bbb2b498489a44dc11290418597b542d571c1e091997d581d480e66f2586196e56984c7fe8f5d43cd7015aa058cd098789b92ad61686b47848aa432b9b951c47bb763ec8fc08608d47e09c10a480c54ce1d164f560f28e2ce8062795bc367d19043c08ccea326d360225f7f184ae33dccff9a8220e98dacba0d71acc09dba68429001a8038203ab277acfa0dff3164692c6077b6211a2867ba7efadaebcaefd11f515c5d5babe8809a919ee1ce93637d680193bf8df9f83401c9988127388aceb658b14a21d0b5d90036efeab33bfbb831559dcc304fe6e32454a6ac22cccadf332ecf06de460ec0d4520dbd3eec79455a88cba6886fe5feb86423e945858830983acc8bd97e90be5bc991ced8d11cc985778286b381291e6409912c6f0121523ea458108d5318ec72c3cb7f349ec1b1253177ce380f4b247843dfbef20fa77ef0bca02771bc1b21405ec6b8a04c128f8ea0d03d477a3ff7b77ac1dc7fa7849b80b7e17669d569788dd2c923e32abc56b3443a97d50394da3d92b7f290d698e23fb5fe609edb58674ee02243ee882b0ad700d5e560accb636e898852f6437c17a5726efc2ae3e8e6a32e744d6fcd4c7e2fe174c6e915f77c0b5f7f6ecf9863040fc88b2aec22dfca44f69990896cb25af3ce4215a707f7fcdcb7f7741d3b1e1ef1abfebcee4df83c749d6bfa8226819e4bed5c1097ceb8682598b607904e01558e263a0b3f79fb00004928f7fa59c79ac83b3d8b91139ac9080801f17d0984dccbce4212fc6ef30093e292ca6275dc6033df9db1fff62f59ca50f558f56b8c605c0770bc818ef722813fce952f30f452a891b44d5dda848093cead08d6a2ae13d00472246888651f14d46462d7c84721327e5909250872d271a96dd7aa4dc275f40673d0606b44047c4de11da0eb62f8dfbffbb833c0dad2d012d1099e0a8e746410febbb2daf8711178add2bc322a78b3cccd9f260848de8acb0938e68e40f1de62ad2514d2b2b8a4b91a41f07879978e122b18d625978cae76829f13dd1565bf815d3870e7c18f2ce384fed63e09d08d8c1adff70c0cabc69c1a5bd8ccf9cad669296031fbd26890531fd329189901f69e68a80cc8157fcf4dd0e0dbe58f2a752e6716079d6c19b8739b7463b31b2c703d649b7c3a27a3c15b6c07d87b76f6ebf83a66ea74a0252fac18b741aa28ec42a9efe921d89c0b423d911e526f6cb9039ac22172d3990c360db298a5ac0775a329eb2cd478165d63ab431963d50f028d060b7aa90b7055b8550f71d05e5cad9dca2759e3ba73882e9e546f4e46e468aad32771a1d7a34682689ac843f2f8aeb44887e3ff2d03219df495b0b97ccfa204ecef0f811d716b84ad5f28e063fe1cdfe4f98f89ac82ba2ead8258d93dee0e99bf2f0a1c4173d0e1646ea4e4b40b161f1a6722c58a6bf29299b7a34c2c470d8b8ae7105c3e98884d5dc70f788d0a4dbf3c8b07a55a059154d098dce3abc2304157e92d90709d69897e80857d3f478cf62e9a97c9988b0082ac33f1c761c8a8c0de1e5d2b6c2c879caf4d3e1fc667c33a5243ce389edbde0065da1486ccd2b92b0ec24064b0b69c22448203e91898764613434c533219f8f8ae818e7996c16b4e68d33f25865faeb27679dd8181535147649ea8639d8203048f0b128c608799d5554685003957ece2d3ded4a448e58c367cfc595d63b3022d28b5e5087c4909611d54da1128253bfec71158942a2e1edbaa762b86aa67cb0191558f6dbc80469a48b749a0a1f788f438f3ea48e355d2dd4a37741d76df938c5c606085ead12b54185f0e7c36b858d12a3a037e3cc8e8a95815c85b0d0d88130ef244a8f7b7c7759f12b032ddf41aab695c06a21f229ee819a2227104322b4d2fc17f42f440ead117a5cbdd62b8ae3c956ae32240ce78d7898c75919127bc8e8d99c15b7679e4577b56df09a8307b052e79c8933bb255e03c110934ce1af8d1186cc89d829c498bf4f8b60494acc24f7e481cc47924e66784a7e13cf870f9d6ad4c4983371cd36a97473eafe00796bd10cf3af6ed01d0fe687fcc6bda275d98a3c0ee15f6608a8651d9c96f497745a9a66cd7d57f222ab0ec1600d382aefdbb6a522269f55a361356f2c3a0fa042b547620a0face689402be4fa212e1aa7cc952637a845eef32b0f77edf226aea9618d88590e76f5a62c0eeabdf0857543fff9be72dd9f6069b0cfbaea7cbfbb3330ad5261d483e7258c578f47b67212851cc8737af22f5115e7413a9144d2d450d9ae73c7ad68973b31fdef517b898300ce08eea1ff1dde9aba8c827871719a3ade003e977c5327af2979300575734c7412d4da900073c0c093805b95846b7adb757ae254c879fdb25d89677c2d6619596aaa258931f489369e2a5e339a7b7c5dc9b67253334e035b22fa09f7cb73b42bc85b83915a5f720be4a8b8cd28e51adc3fbc1ee1e51da5eca36d99c256528ea4a0a47d43dd7292c7e0045b7cacfa85709a2a4e0d51ef5d1bbbcc02f89445108fabca36652a8bfa379b8cde91e59836a0a7a6f0f6ea0ab3fceba39526b6a999b909c6fd1f9e9a12e4529da99b68c0d687e9e52fd5ba0719e84dcb77c9b0f5565af01030afd863fcfe3d0a1f69fa3ec86a70477b1fa3fbea13fa9748285b09b80a62d195e252b4598eb5dd2a0681241e0dfb064017954007a2a25e0236343128858376ab70440a97e93c4bc2142018c5291b761daaec49696f06d0c8e0d06eeb5f46151263c35c92b36fea83d0ad256c6abfef93084ea051b417e5945a181521c160f6ddd79bf697f1934f2659b886e0eb3973463a36e334648306ba3409a91de44a5daabb66711c379e15eaa3875e1f3bd689f95b45df1d3472f30413e709bc04e0308652a348e2a58281e13539d447ab9a57bd7d360fb295b1237725f0fbd6b882b576ad939a8fc86abe8bac74b890bb8b4cbb2f3b850e7650cc189aad19f7e004534a824dee89a2284913594d3a0e5a283a149382c4c4a0f851ab8eca64ca3f6e1846222d01ef7e6c6d76a7085089f832eda8bee86b68b83459e6cbf2a84d034f045fe8e2a081c2afee5de62f73c3289f0a8dea20dce94b4e8978dfc6f642a9adfce3381dbd7d40591696dbd49c41a2c9878c7ab5b00c5a1d32de3a9ddd15aff9c2d5491868749f6b688688aeb7e3ee940b10fef1ab2441d5b4950f92e56214f35997a638eb22222de47925a75abd512d25ce33f2e9612e7e1097c7c6eee7cabde2b93532177704564ac0aa2096edb45a59761668d96d33adb6568a67dd44a1b23cfd2525db2287f5c24039260715c0bccaa30334e5fc5705fc71212098537956cc3af8b41bf6ba93dd51c13eab7975f5760afb016deff36a2aaf0bd97dd46d51d615b56ac1283823c9cd0015fff68d5198c7b423f9a48bb414f76f3db94e2e882c736c4f0722653f961fb88126e2370963a900d18972fd7bf09bcb74b57c2a0494e9e3e9ba8262b16eed3d524f7d6e3bd8d30dbf0009a51b3f25e5d2c5644b091ab23430d0ade0902d175c2a2866a30e4f8de4dc74d3bfe62827b12b4800c9e37d551e749f592ecec4627b26312e7c24a04b8fe0785f37c015970ff4ff54f1ba61b716d74ceb26ec885e5a74923964dadeaf2ee60f2f7b210cdff4121853854c4eaa641ec19c757a100db719da08ac0b7dc02a74269ceef884de7ed3081b6a1430e3e245766f04bbb1ba98427c4af0311eb8750388638ab723ba8992051cc7322d60f304654e9d32e0a3d262efa7f9a98ae8af8d6020256e78f7ea2f5c392cbb43827ba7291cdadf591a8f8bfd8eddbef93da4813bebeff97ec0c7f80fda49c14ca278cd4c4d44fe0bae651b7bc1d36551c03e02b2e33cf050b0c9f54b237227ac2143149bb73df875eff949862cac31b30d3aad7aa4b4e80cd1c42b87bf14f58d294de8c67336ff17475b7156764b05df16c02eebbf7ba489622aafd2896b5bec5887945e3e92edaefc9be39486e26e7406a58ac4ea749135fe61af229541ff1dd87aa1d7b663c39ae81adabda4470d905a0683725c9bfed0d4742bcb30eb1eb06a52a509ac1f8cfc9f42d61f0ae6676fa8c28746ee276f2e4e3df9c24b3b9ea87955ab8918f67deed05c903fe7fcfd4c8f36c786bd16109fec0dd3bb02b6545373107978c2ed0f1604557b83d970daa50d9bea03addb2568441e80ee3ebd4f81583ee7170cd402c555845e2296bd796d8960761b603ff38ddff14917bf169837afb46085180a25ab6c04e2af873fc2d2ba463eb83be7c328ebb21d4ae4b609942795b98f2ce5d10b3a6f6aed227af6e732b48b1847ab55907caea75f63bdd29f7882a4d30834fa354fe70033e706e5314aa09ca81e8d4e3732a56504747a1f13da2bd94e451dc0dd23f052e636014130619a81733304a8804296627a04fbd8831e807f341d363d7a9410a2877e07477dfabf88caaec7890b0f4eba94b967338077e9f027bb43e9d76c1f9c9d5ba8a9e6840d001e4e68cd04651123b17e95e98d2ef2076005c2a6a245a4dd05fa6f5db6aaa528c412870eccc2e625463246c3b05a92c73492d468395dfbe632ff4498cbc4e122a79d13ec829531b0d0459159161700a13f5d684f93f2606727ddc093c3476332917230739f6ea308db5e432e5fb74c508c9e303852368076d538ae2697e89ae31768aeb746a1cfe9210a998dd3987448d25811865e2592a677e9fa67c69b9cb4bfd51a4bd0069ed149a959669ea93d747f82d1838de3f972853bb70fe05ec1121b5ff97c99b93b32a056ad9e3bb7c785370b3ac5874aa9a2bb5e6b96e282a34bccdbdfb2a6335ed0afe23dd05a3475c41b36b60dd69521f13600e91010e81772b1c1034e866c2108a7041dfb5ebde5e90fa465ccadc693c1576ce03564f66cc1466b3f8447531eebb5526847abfed3f2994d6c1342e8654d131913be0b43a80e795f3c41b673f0b3f4d0e5c3070d64ffc56e469f13d13bd9693f33188fa502c64264d4b9aa91428e0db587bfe1ae3df25daeb42422642238a9fbedee8f923565642a24af5a6d827ec7721983be88b2622eec4d78123855150aba7e2fd84986bcafe0194031872236f6180545c824c63c5b596d7d1ceabf5ae0e13bcabd70b4d21e6b49fab85a11d27607d82742b72a02a8bf7c13e1cafe5022575cc7aad774c1adef4b02a6fdca85a8b73ea23f9b6ff426181b66d4d970bef455fcde6ce04fca48b50818e1e4d2cb8ebd7b27764a808815b9bc32021808e7ab80305a97ccf6cf2f6e409c882e48101412968e913c6efd25aa2d3bb9468736b1426cd98b3de80603ed0b8a651a5184966f5024472ee55a7c5b8d64537c7c30007bc7872cd971250e9e629c563dbecba59a21d54805f80d61227cd2cb4458b328fca29736d40ae880128bc802b1517e7e6bd0d270bb2a21da9ecc45a9e34e7eac929ff266c4d75d3b278ded98e5fcd43192d58fde5ace0e79120517de5f40508c8c70ad6e70654d4be8ce4bd1e4b845fee57d3deb32e28ba19dbcc5bcf0f6060a5017dc7ed97897bd5278bf3659994b321e663126a3a3f6fe4aa6f2af32357a452fb731d26b707f8694e0efbd45eba0bc80548d09ea26f909d7bf2eef4421b40905988601babdc7a99419384ed9cd69833534e6695baac384396c4a4b26eae158e09b235d2e6715050bd39c9932885b276144d3a6be9b371f3532d49d381a0a4ab60b764d8e7f238cf81b3b5a38a1dfb0fa0beabfe3718a6a24fda7435dcd309686c2f1739e186c1d3e9493484667e01588020de35e5f456bc074603f500a7b7248be629ab2ca9322b35f085ec6db65b47f16a728992f7bc95181c4fc4a730396823e91029653feeba9ccfca96a390bf8d89cb5bd08a20f8911b43a464ab2db1dbb1031ef6100bed8f60d596aa7236290a379d3e20d5d6fd14ade9e876a36f2a886c92bdb928aacaa29531c936dc9232b8ba5c1b24f763a1b571d0d75a551f80be5554476b626551e96b5a0f202a38ac5b74d13b71d916f11a629ab4d0c405543708a91a0be84b283e6f5f27987659a4f6390618715775ea885771a5762d2a374d90d5b352e48b5dc1721f86bc22317cdc943527a8939c530814118abb39091505181278986d05b81aa03eac5f55be2599671de1b54cce4e79c0083425e21e908c2e89d8df12345e77d9c7e0d073d36a121bad23306010cc39bd13ee35be0df3522835c8c68f08376ac5be10817d806d7bf0267aedaa146694d5e83955d040e36e5dc8a4a42a587a64349d4607bb044104b15f63d0d1a387eb0a95809df81a506b1c7a2446c916e2f087dc6be2b64c8292b85c915c8ccfcc08aff124c5bb38922baf779b3dc264369f47cb0f827509a3e8d87586c7625847917b180775792ba975fdbfc007adbfdfd461803a87752162826c29dcaa2de92c1765ee2cbd15325d16be206a858348e09d6b165f4f4e661b0aba09f98a8fd73ae65fe4347fa6d8d424de4aa721373f44b4be781242bedfb609bb17909917baaba433892a483379243e19590f48276d07dd6dc7293a8b3daa414374648bff90e53eb39d70615e20034adf484f9dcab6adbd015aa84c8a38d44dc5638d84f70b6d2b0fdaf68c09c8cd2e58f492765b21a8fc4dc984bdde9dd462cb0db4acbe246b0d6c97ad4020163ccc2f035833bb1eab22619a883f986356a21a25f2f8b8d1bd02a8b881b650001966fa4d614c040a862d2334c79fb6d78a7613f8bea17cc752548cf622da60f8496ebb85cebf2cef65673aac541f1ab3a197940679de54dcbdb29a29f475568e572db7be66189698bd95323b2284f2cd89397e538c5852a675649bcfb6400061ea4ac92031937638ab6b15c3add49d19eea232adf6805122fc65a6b17b5e36463deb256c5a9fd9eab32d75ca2c5ac255fde7e934bcae44e98985868bbc6418acd1ea24ae322fdecdc7cbff3c119dbb165ef69085327b1f35ce366787fd48cf5fa7e405f692c91eb16b62485719c975d60f5fe05d677a0a57285750c6c3c10e7f4fe8b5f06eab5b4a17c64808dec7723d0962948257c62c8715bc97238596e92d5b919ef82468635801a8d16aba2dfe75af63daec166f9783dc2a989d7625f0a704a13c69872504908c65ec74821480491d7c878978dc3cf569ebb84b6f07524a39be180d45c0a535d54f6e5fbd1c08a4c2dfbf2d748a10389f33e460a4c73eac43d67dfaf8fc0ea410874c4be1d1268f0911316fb53f9e3758c3762c6480092f3db57800c7d5b2de844f37ef73c5ab213131f30bb5fadc00623e8f461f8e417cd6fe61adff9decb2fa96bb138c16e8c0772a4b40121e0b40ead1051dbdcb32f03429874b9837f041c096ca817604c1126f0021407acadf739d21ccf03b001f13ce07a8500db8288cc482040520aa751afc90b81c09f3d8a2515bb5553de9e300fa1dd68874070a66786c45dc96ef042e901d666fa442fe4b1ec47426bf02da5207ef460f7975ee4f4ef50165f6dbee7f174489b7d7c38e55940c91fbeec0aa6004e987e83a39d6201f1d5ab2575a45a02df2f715f912bcc6fe4d9423811c369546186a190fd9a2c4c8993d86514df2ca8e084b3699730240b46ba7ea1b892dfd377af95f69fc4718c20490d6b1623c6e5fb1a46c788a97dc74ce7a9d5541d869a711e8d36ce800be169fe5fa78a626c60c3a9c8133ff775e0a4ec867733f09fc240e8e3a528aa7dd358ba2e710bec6cba710770b0d62950cd8477e9eda1b98e1497b5f496d4f567e1de5c9ecc8549609492caadb8b252951810cefd643275346984aac4a2e3eea3eea0f21474e0128340ef422badb12fb361747ecb853d3406f4e386debf0b3d8b908d8a4d139d164a191f3d4728002c4e7a8a2d29b2acf774085cf661230ffdce37b177cbe49d3693ba2faa60c76e9356d221219fb90bddbe05000b77a4e412555dbecc0e198516ff13e6be4a539b234900a6f0869cbcf62f65eae3d322cb972679a04271a4158b8d9c52972be8af7862435ee9b39393fb46d6a4ccd1170655b054001a83704628c650bf0393e512c107fa20b4f1b41d4845207ad9a1de36e6c398f51d29e497165d70cc800815de9d2cd6043136c837f4f884643a890149322542d6d978de65a38454abf4e65694568307793fd211d284412f12648e39e9bf84c60747a844ab88712f3d09ce347fd3589388d057f3c21853a1446d935d04fa548556654cfc41a01a5e940825280fa9ce7b6823f3ae7f629470058aac3ac68a2244fe4c6a1c49af822d2aec53f54b9488dfd138883a283d250068573efeaede4c85bfa24ca7c8c913fac23726fde0f23926c31d6ff7ae4ee3ef4a53bbccee1e7756115976e6562496d34f51c4b7c342617f19a291c3f4a9e27ae0bb08df1da93ce3c1f0b7e6826768b0261652f46554a2a769ea1921f8ee6cf488eb2a1c131b05a54fe4c7faa7478965e5c45df35c65205d716c9fd67ba77c6b78b4c9ea306d85bb8e8c88619b5692586edfa1e3aa534b43ce834c22beaf16241e559435bb536e1b498d7e2724a38009582a8ee2b5be6757b03e8e10e426c7e9f7a78e220af060a73f1f5259700918a9b1de109b5c338908a3dd78abfe995c5dc967ceca279c0005151dc4340ff66ef4eaff0700319c960e94240e028877a41ed287ebd54748473696dc5e94f2ef9673aae81fd7d990993c3e0b7c2833a01026ae8ae42895e4dfb0fbc2439355c227148bfa1a91319d470fa86e0c305d7256b51eb594a60b20d0529adc34d7ac4d34c772d8baf98bc3e69c75ccb1734fbfbe2cfe61894c3b90efa26663d8cf72df01b22bf7f310297dc194d07d1bd06ec206d8c63b5714ec434ff9732b3096631d6a00be36f324063468912a91b5b45adc8f2c3ea26ba42255f10213d4fca4a28b7724e980826f81655334778ba950aa4b3cb29798286aedc1f33ce125ce42e37f540669519f89aeb6872ac2b6660d4eafe153a7f4c13e647e3444ac6cee7c724f4fd50f5e5dd52404ef77f35a767148191b9a54163e69963ecc432c69899eaeed38428d61263aa7dd0dd402dd3e76624c232f1a0e262e7f358cf5f27e405f29990815128d203e775ca6e0a55a7a3fad19b9a291602d00631c3163a7d9579ac5e4111e30d6f2d4e06023730830882ab691e06c3406675191dcbcf43a7a4a4905437ffab2fc00c8afe6daa861e498f77a72ac8ce7ede7b0cd6e54be1b4a5f7a4a191e895bc4499ec8027bbd8b53ca39fdf144e3358e03a8ed81c9cd266bfa89a985936283a1257b05d3ac7cd101ad60ec57b192f92e7f81c557c99840768698d7dacf61a6d4c469b5424135b77aa5bf88ac5d3164464a10e2061c28a404ca04e462892f14e1feb29f336f5c9242a8aa960ca8878a23a9baf8f0856453042f5a4e26c67fb737552e0082ba47d91f825b51b9e67e6aa6c4b19e164780ae4c730035140d3d746fbbe2ff1e6556cb2589faf356ac26324545e07bfe528d0bb475fc98eab6871a29d1a64e6e7aad425d2082564ee805b34d99a82fb0507e83891f0c249d90d67a162aee7d4d9ee4b103915ac3f78c65ed70f8450cf6f1d93771f83598d8f7b8f50ac0e1a3497c92ccfb8f4eef0a2c07ee449fafa8fc56ef0fda4a49ced06aae1c82ce3043080a8d06ab10399ec3592207327d6877b271dbf9f49b287d360a26acad2f720e1444815111a7f9b285a91ca7e8108773de367b395d616508e00fb0fea2a2af73ab1615c8435833e2d980d5b282568e62c1ea026678a3168741afde56dd65952979b0b57495754c2174a5494e3c44e1559c411b5325e948fb85a50658d4b2eec36d49a4b152fc5861b777e7c63c689f5aef0313f6e2df084af6e0186a23f27a8b4a9ca59ae0855808b72aaaef0ffe12e76b1af24102e0a2a6c1224a2d77831169214a0d98506ee797d2cff475b119c714726cb85dea9b58e770b6697ed27c9566a07db8fffc34587f2ca1adc5e308793cb32d11efaec825e8663951478fcb03242344d74671d630a593a09d5af4cc86fe0e4e3c472c3d457f496bd76f8de8b0a8fe89b60fb9d77de18fe9838eb13e9fbb4b13f534fa56430ba10d35fe1d4e19f2cc7b51f3e64f7f516f95707bb4f93a1c6efa7f84e7dbdbcbd74323fd16da13607bb60975e6de24ea03e525f50edf6188c9f96e541e7a2de62680d18c1a86885c1d16d61ea53dcf3959b1f183378d6c54535ae80262bb1e8a0c9cac95feef179d3bab984aa6507daf99f272cc3994e363396054b92efede45aeacb023ecca15f17be01c710a154047e54a037bb47079deeb0ee8a9ee36f51118e423a074f76558b917837f792f579c0da446715ddcb4331b3e8fbd0204f6029f3902d514f73134fdbf9f78c3503a3025afe683f20af9df710a8a78dcdbe32d896113339219a182b2cee82f434934914d81e2237e38ad0d5f78af2aa786f785964bf709d4ad859434b8ab9240bce11ff0b56ab451b4bdc92ccb6af82db6f44937560d7b57a7d77aee2cc0482355935056a51407f76bb3e7bee1542768ea657a66506eed30a39421a5f9d7b27e2a9bb68dcf5170b805a641276db5b25ebf3376d71a36e865af7d846858d0c98b01b2be594446f9e75c282538a317e17be334b2512d1a7ac327fe77f4121ee0cc4d2e2974d71ace5a44665bfa328153727bff3a285e50d35faec6d43e04d9fc9bc1c6addbdc2f8191615aab0ae6947dfbd06379587cc725f755f3871e50228cd7696c9de6592653cb63bdcb706bede5a14d2cd58b4b149a486c45c03e05d997a31de668312f8e2cfe741e17c76688cde6c8c4bbec1e07e41192ab319d68d62eb43939f24ade57da3365d44ff4b1031a02d47623381e3bec59b79c1cea747cb4d14ffe72ed7a4dc700490821098e5fb0989b8ef3fec1e9a822e00887ea726647003f248a797dd0820aacbf525897c10a6bf2df4394300aa208fbb251c0bd7817fb7b010daca79da20cfd52bf9100386cf06b03d79e34b3a533083bd51f9c6202618029c43476b875184b34931d07e004ed51ea65bdcd23bdcadaba9c4b9be503dacdb4dcc91c0751d0cc8840e49175789178e1bd75e9aaaa40fa9d80c29a61c48f2a609e86be912d4e7ea89a67e422c043973147f71be0eed4b0e945c5b3c97e44d3109aff81a0176afe6047e85221dc007c35c95dc868c2b3c74e3f789fa24ac1b29f50b7efaf2c490b86579b08fc58e0d3ee3565348c3348e075d5921d02940c67d7c154ed64ef2366d0f36999579282b4c7acd6c41136860b7843037c02751f55a81f071e901d0f84f808bd9242bde82109c324d6f31888d93129c2fcd6e4165576d1f5c067fe5ca88354f18805716709731aecd02b36c28de621ffa9f6146abe99c66781abd5e59120342a6a9ff38610d37e78a9fe9ceddb3bef06b25a0edb5d79546c57f53f3050650ff66e4ea993eae34675ce4d152358f8c6aac6df6b44c95cd028dff78ce485f9e0f9f85c2a9a9fc431b253bdd2f92eb28d8d040aa414ff29f53d800d1320b4da8520d46363598c38a17b0447c75ead05f98dc85b3b965708a9506580b0628a6e4b5ea135ae1ad2575d8f90f9b5a6bef5ec9a63c80fa169feeaee01c9f4616ac50abf0363a93422f279390d2c078aee8c70f9ad69bde578f0a565e3ab267e0fbf1a9029a91addf755d0235b1948997c59582c3c4d2fd0b818b2f76cd8fceb4e7a33209e79cd4fcaab222f554a798d4813ac353b63c905a67a1499e9030ae73e9ae94faadd68721a5c10410a4ae6146c48f0ba8fbab4b3ef138a6d0239aa0ea5a6793843e5ffebf66371cc6b2b8a97bb10829851c87a8bc23b6cdfa72551541531cb9177a571af68375e54640b3c6a6d46bc9267c7c009ecc2d2e5f7e7f4899655eb752d5deaca5f80367f59c35d213d49f403b0ab2db2ce207695aab53c4df7bc3c281bdd2253d4e83628bf95958b2b2fdb2300a81dd18fcd142e6932fdfb2b21378d786e2a2ec452317112a04018a3b46779af628198999b97ad3d1b61f867b368fc096073eb38b1a0195cfc2fae4f3020c52ec81d886e217d9ab47da0639f0caa2831027fa806e46afe2c0f6e36405d4ab927e8db8e5ed93f3e2c6afa871e10af854fb18123ba9e4d0aeef452c9996b8e545132686cd000a43dabf2fb9a1304dcf5b4834aaca47e6897c01364037e058206bfb14daaf4edd93821c9b199684e5405c9c31a6970a75116ad544067b9508c7c4c4ab7d8b37c413a2f836f65869a0f92095b8e19761d505f31bd2708500741424e1e95601c260ebf7827b962f5ebe43429fa27914db61474ff08cd98326f79fbafca230c274c4b149a82aa3d5df429a28a5a7f9c6611f6a1d3e0fbfab1a74ccee5c4aa98c8ccc0e1aad093f693d728b5576ed54f43373f031bcbf97eb5b114d0d3241b0df62efabb91c469c89a0b8d756cacc5ce0c51566d6cfc8b0bad303195d4595d7275c11c16061a81d46b68f948d84ca8202f27f848ec8380589652cee0a3220f9c8db4af44cd4f8c77a119a9c563ecb30435d9dbcda3e28b1e1c3f5c40cb676b62b15ea20e8e27ab8cb3f3c7f97c67260a8c18b0eb271e93de9a3f5313e51099160cd7c1bc5acfe434da460ef974eec7868d87994b591a97d42d56211556aedc1b509822a934f9af052d7f9649fbff348c7f81ff6fc7a07b5c316c6946873743d20aa26a3ef475e5b03c0884ab612d91f6cc7c4855dc2721d29aa28ac7f8bcf3d7833611e323131dd5e3c284ecf9d4d46178ca7c97a0693ece9da077a1dece2781929f57197003ca95e744ac3c2ebea3536e1097615f0c4e9f0c02d0a058075bfd796d3e0cf4eafce271b4d57aaf8fd3f067c2daf6cd0e98a965770f136255e75bca99a2e791ef761e4d392797fecaed63f0e34f574f0229a6c43351f3844825ed03b8e27a6fb2ee21b1497dc3d8ae11dfb3b6ae98c33acf9ae6b016963af20f20b58aa40581a64e3c8b181f4bb0010f246f1fc06643fd7bebb0e0f20539028c07108391a86e1ff00ee01a7dc7428f0d79e5bbe4ea55f788fdab0aad2e5e0edd2f734bf13649a26479dc42d9accd2087efdacd7a8ddd67a4fff2a1b1d6a59a3003148fe0c2b29fc99181ae46a47e39b2832b5fe635fae62c4a46d48945a9d40f8ed002ce70d611e9e3461eb7a2419c5bc0bc20388b940c29a2a09887fb326e1bb9ba880e626d1dd7f70421144db37729623fe195550c5da006ed3c00346cf8c7d2362c0d0806e444420e6447100e984b0f78559b0ae8edfb1dc39c9524554d35c4cc62b52d12570653da665dda6ee4159e5f1b0dbf63d7d064ebe851a409ce202858d45d3a0be97334183e1d9268630de5a24126f780bb4cfd926feacc378c6886de0b37bdcec33cb5c57e2dfda840ec2ef29f7d85dbb2a324a3a7b834b35f761432c36469b8fdbb773e4e87f06f7cf2e262799938cb1828ab11357202cc83ceaaa2a5e42b9b4b0601dc0db060fa1ca4c2988393ec1d6b67b8af48bf7510a4c200a7b14850b8152c77e8a1c2a087a1e64122862e464d4f13b1d6fd59c5866da29fae99defcaed0d1e65dc77fdc4a3bfabf1f7c54624057f9b820b6c499be1756abc02162b92eec24109a58f79b495dab5272ff1dd69433f99ebe44eb4aed6f8c4015a9c0431e0499066eb2239aaf6c338eca122aca35bacdb7bedbb008f182291adde0992269dba11e0d011a4700984a3354df803eab4276bdad2e3fd4b14009def788a230a8d8c1f0aba40b2c6611e5b6b4a52064dac91480fe0bf4bf2d9cf6ade3a87b97a1210860b87a1771ebc1422c5a5ca9cf01142713c5d1e30a057facd00ab78d499f8fa78da43dd3cc5078d8d93788a6eae4a914c3661be3f700a574fb76fc23d01bc8183cb41c07d3a6facc3a25493699f6df216e7ba0c600da9b43b5c0dcd652be763b8c8e40a00b7b6c54050d1ef4791e569e028f2009e5c3948631d77ca5b847a2b290d80806af4a92af7a83b9f85e3ba6aaf73aad62bd6e63264fff1fcf9be71770399de25fa5d3ba3291e4d49f68e7e2e495cdc3eec55b848c2a49c4c321a0d01122b17da0e49711ee75a2247a8c5ae6c06a4683584032df972af5631db2acf7c02d7c39e8562e928feca524bc7e2dc83eb6cee630d7be96e7e2b3ffffc2c08d321387ec4117f0dd82e5131bd73b96ef557a81f68bc3d211e96c6232515a7dbce1e56b06c959d26e6547145d15599370c55db085d5bb30075b3a8424ef9b19c9501500f46cb8e2a402c51081f0a014b2c6bb945ac4f3d78c8bf798dbecd0680216c398f8dc2516fb528cc31cbb59216281cd357a077f6407e2d55b1ab5ceff7443046ef38fae4f87ad897e8fb8f590de64414ffaf32b38ad9584e2ca710bc5f0656e2a8eaaa55a5364240b1145abad2ad3bfefd775bb068db531b62b2c1859385160a2a4ca7de8341a0fda0bab3646d602be20057828e62821ee25b6898dd38808915e1d2473591feddf05c2e5994006b854bb152cf022ef1ad5b98413490eaf30477c5a82755d778f5bfe2e00ce5ef4c1cf241f323bc4205730dc63bacd0f655019f0998d0c349c0eaabe708ded31811e6bc90f0cc739416109792dd9ddc47561aabc41e050b5f6a2d02d39273e72d602193a4ad552722444d414ca7ed72fa2fb8fc5f69489295c0bb9b12c2eee4a03ec2355747bb68c3f971a8235d10440ceee2dffbe2ddcdfcb576f08e9eb43f0c8f8c2e0b015014453d323e6d0ca28ab71b50019039f03bf393b10e1849e5123d51f9ace41cbcbee0aee047cfdbf94795d3418949854ba53369042f468f77a46d17caec59e368b7d1b0c064a2694c4039af2e308ba8487056b7a984b401a33bbc56176a89c0eda1c2617816abe46ab5e75efbe915fd2bf8be408879de4af6a4ac275b1b6832982aa7244e0c105b03e3758889b46b9507b45cc9f9459dc4549dc3abffed0e26a06b15de47ad8a8aae2e5949eb950a60a511194a389cbd6881bbcd1f6c5e59cb7a7f35c2aef30f2ba80714ace5484ed2823c96580645ebede1d9a191cdddf21f4e83374c5b46a3cffcc039fd3e5359866a33905031556ea815bc89ecb8ce556c90a4fad015b9848c51dc93d7584e394a4fc18adfb2da1c829169744e1f746a9e98ea939f6f8f4608f09c65b57a3a038f26ed3eb79ce12f6c6f9fedad83d8549333a727dc07ae4ed68080eae38a4a773ee8fb4885de48c2114a70f5a4b2f39b8d271a04dd3bef83f0b065bed2321855122517dcba99a43acf53354441144709fbc3f5c0a114f1b0e45a5be2e2115c5b790c36bf9c753b28b8e6bc2aea36f3ee57bec480eb033ee85ff36e0a74ff5cc829a85fae9a8661378baf236b55a723b38ac54301fb3bd76147e624e0ed05495cab637f1c730ba1fc0e864e09a41e7804de371a20002d8974eb12b02e46139b7ed415796d6645acdedad58a8141d7d42208daf7aab00ac187237de3f268203cb388b9ca203d7151eb6f20eb23a78b84055f842829bba831b3c58e56325bac0018a17ee87fa428320fc322eb2ac58a845deaee786299461a5196ce3ecd4fe8a3c9748dc5e7e2d2b6aba826268025c18326fc9f344dc435cc0bf64ca798f7a5ff340e5fbc19cc739fecd1156e2d9059d83b6c18de544f4af6b3df5bee705e0a9cdc04c5f502fde150023e81c68a63c69aa9a1e69e0aa5056776bafaafc1281fbd57213a1d651499b70bf50619eca6e3b979391a5e33265ec8c497fef48a99be97bf84b1e51ebcf4ac2d675cd83f7da3bff4c9f9e877e7a46e08f386bfc156c02f6a0e6166832a5409c8b36d4b1e7dfb431ac2d2fbf90a691962989713c00f934d247ee76a85b3a9427ba4c4762816259442f0afa9f14136694889b640bbba4cc520ff7b321192e41c399c128620464f0ff0746a8783d6483d7b1a1e19b6b4cfb17b71d89e88ac41111d34fd9038bf8d1d1a5c011997e56199da02c86adac1c7e40640983cbf25452e4b786be2d8377b21f1453603960fbdb0937c5bdb367bd3e9b8f637e10203d49b1b982d1a794924961936bcca0be7cfa744b155b3d268b52e59f45a0f04699be4810ee5a346b4f12c695aa4ea7dcc47e9f82b16efbb99b14959ebdef1a28133e24692cdb5b1dc1070c490d88fb6e962729cfe105a6614f313dc92c09bc210b3dc789f88bfa3c629402821374ae71f0d88967a60948906662e5cfce4ce0b79588de59be046ee2228f736b290200e09ccca9d546264ee22b529ce315a8c6f1ca501ac3c70cc5a94cf29d945fa1b650fa087c03a44bf8b56c30fdd12be225472f2215711286c762af8b4195c15c8af2db3bc74f345a58fb1d218e4dfedf03737867c15b9a20870e04a9880c986e3ba4164e616593b83f684454cbd40c8b980414c36ca7b056332917531c6de2e6a46382c579dc7c35bcd7d60540c18c145a069265f096fa4e6b0cae304c1d9062ae0661e7d0359f28aab45a8a707726065e56006c0cbfef8ff280418616f6a3ccd60a599e671dfbf2fe90f4eba0811f0e87de0d3b4f3360b51204df5ba063363997f14c3bdc5692fd3e4b93f74fb6943e76ccbdc577069e7f9dfd9643ca63e094ad2acee0397067befa0e88db334d89ce28c27ae6809c69eefeee5d6b701b1b8242db213eb63deb5aed4a5d39f92e991350b7194c2fdc99aaf43f3a67218238436f70c6530d3da86f2af5fcee1a7a275649de25faf24c68bdc416c84f4efab28ce1915ca1679880825dfb3bb8154a7828f94456f0a137d16080954efb6cef148c81333b7cc7aa0515c658297b303dcfc633a2027e18a20b260299cf149dc44ada823a094dfbed7b8f62f71c5e89c71a354b6ef9f86d58e2070a39819efb353aad6a430f1c73cbf05f2cf8b5e9e34db574ae0dac04616c8d4951ed25841c79bee84f5acc93c6dfb168fc56103ae73b269e94d7b8b21beec0441193055f1a54b81c7555dfa1ef9c28beecd11a568472e454549ebdb2db84f1600ceeaa4fb0e520cde9e7f7fdb2287c93d01282f68e3ff861871842d3f901e6419f4a60d8b097ad377b36173c2ecae56c46b3d7356c645bc3787c72190a8ac75b7978bb657224c38c9b382cf60c1feae279b207bd51ed2e54d72ad4e2483ae4ba5eb1f421fa579089e76b322d60f15f831e5799804573f8c55471ff83594d99a2dab47c3db8c91740a9209ae4143fe781a3072105c4718afee3f2b05f4c6b9f95d55ffad9cc0f19de52a6b280aae9036eeb7ad16ee175e7358a1ebf6817a2c3f454688087fd0dd34208b0553ba1203b36c8f62943046dc958f57a92a7346453afe17df4508ab34c7945842755a0343cd6d6f4c0ae567e568597d0e68d830b24bc7022a802097b7a296627440c73c0ca2985575ad99dacec753afaf4ac2346648237cf9fa2f979701fc91a1de973482eeb82cb6da3b0490583775df4e5c285e6dda381fdaa0c2e40bad957004e5d9c5e470b87373124b0229d5410a8a4d1fb3f3dc82655d0941cd8b1be361078e52088d7660b72f7432c272cdc64a21be385e64798f9d08c05e1d0e30713525623ca18336eda6cf2de1c7a45000d3ed2c106e8ae752c202e2fb02d01ea72b2a2719fc4fcdfb0adf871d34dbc20962c65f004e7a988de62bbd95e36c3a1aa8bf48afefd9d9a4316c9d58bbf591d9e3ee402f8ca541762e57d63e81cdb2e058441ef358082d893f8605ea27565c6624f290fa176ba6a6e44fa2718ea8afbfc4a36e2eaf5d0945d7956cb6fdd0b505b92960b719d1b86f5c17bbd8a875f5a1e62843267c063e722646888332c316c1e4bfcb1fa9071cb63070bdc8c7c89dfaa719772c520a98cd5669682b04d12c76b44f4ab94c62c2c513ccc67f545e89b29421361d72a73afa5d0459c532dad2d3b82bb993b281b2e982d9a75e2d1289454970e98bc5246b279f7cafe46493c08c0a856a69eb731a41da6d271035f115f51b6d294fd4ff0c3e6d54cc5a9cfae8cc390614bc1afd135a288f637759af7a488682b36077a21cf261fcc60b2b3d753f20cb41a7ebb9536903bfeef5a68b405df05092306ab605c70a3500e2cd75dfb5bc85674b23cb2a97ae16ba899416c5aba24aa69956f9a2e7d83a02792e7605e330a71cba5c6b6d18891a243a9ba3e3dac5b56aef839884d295a1f637685a50d5215768eb2a693ab015e5dc94fe86bc51068dcf68f331b4536e8135656cfff71f02c32d497a91981514ec23a11c30a5929fae1a5301a91e632f05c024d4120ef6c0890d41e6810099cc330e9072ef28cbe14580575163f9ba850a2b3e30d1a2b805dba5e5f9045ba96a38d54efef866e1306e78b3c7aa4c7f45c70bbe7cd06d6a3bf99ba6a340303c05d3142f92ba0c0e7128cd41d93fe81a3ccbe6ce36a40b1fe705ac63330db5ed8cc276b92d5aa33cefd28a6288256d419e57ba91f9b263caf2166fe0b2f429cfc26d3e20278bc1aa7cc1e0c0a8c1d270157b6f4df2a1de2dd7a67611761d100393f83885181106732302880fb00dac5fa94ae872703f227c9cece287956bdd1ec785e2c61128369b4511af82a356b4127c7c17b9cfc4c9cd66ffd771df3cb86e83e9086ef0309734cb2dee1d58633a3cff734375b6620e544c53d9825d6267437ef16ea4eeacc2b5ae50459abcfd05f286a43d8859ce8a3afe3fb88f150755372a297f34dbf9ab06ee3b4e0eff6eb5d96cdf9d5c23faa57272bef3a55464db02d17da484a13d9fb4994071d6b0a829c879c245ef971ebdd9194b5648ce036d25871c8d885490b682dc3b1f61f30135839c475766dccc3ac4fc9cf95edccd508a9329a73ac2498a4cf0bb4925845e1bf69abadccd821bb34f70fcd5162656506756d251d86b9fa909cbad72b0267b737fff7ec371215dc6fba69462a22b0d72853a5000bcaa793f1e378c879dcd57084df873b14b187bdb4f04e5ffa338072dc7b746bbe3ed6506e82eebbe2af53ca91e9a3e161351255ccdc9639709baf604b4be293a3d5b33ab12768190b07aef069d204074dd7a96e5d2bab958863e2a522ea809a7d09f7bc84068ad36b7c2a7d7b5b28e4532b5e9ba832482521467079caacc7a03915d3c420b0d5821def9f5791668f403f662ada6aaf09727613ece2108b3a3cef543f1ce802a282d137064751ca974d9c72c37c50235cfe7e67bdf3fe5597fad9f388ce63fa4b618f09b64b8da54781aee65a6fc9dbe3950e030ebaad5a420b2183c3790af5f4a0c2f6a478c20f6ea110f9e1f15fa3f83e335fa5a2f016f432e65da806c2fef6a3d58ad4de617838d259c96d0cc41fb0d699376540cac2e193ea51c65252eba3c0a9206d05b291f5b3253c1ea7e32cca03908a504c67c36808325fa5e9c28fab332f3109bb383cfba34b39a0b61ffdafc5cab9ac33b3f0d8bbb64c347f2a0b884ea07c1d789d8e1cf756b54ed17217b8f358c5b36aaa48f0ad3645e094d98f266b7f22f9681e6fd4093fdeb28d8a6006629390ac817d1290a08a7bb55445a3996a890c7c24118387daabe94855ab95cdfe34952c5d9b8c9254116e2c7a60277555d5e265b34ff5fc8831c27bfea7a7eb6f7ef41623f86ded6dfadf5ec237fc206c0c56a042605d0feece8bec4d29e1ff2c5d2221d6cf5012a755d820e142cd6c5c44e0766476e3d8ec5b68082bbe1da038eaaacd8ff40b0f9d42e6e0eaafd2ad373303be948e8b18e9edbdb299f99ab2079688fda08df9d1fdc3953717fd694d425e827565a18a7fbf8b37c3330c3ee92d057af7c90e4e850c4b2b74f90c51745e86e035670f70198a5633fe1b9f2687084da778e3438234d02136cd212dab191239c0196ab0b45308fc7de272bbdc237dbed3b82d31502f85642f17b7f3a349521f9a52e66911e459fe65d7b05a93b483549d4e949d5a53567f6bf5c5bad35be54c588c1fc98a45541f2382f00ccd82c2e5c88e055c0ab56d915df42363b148f6b08929e7a85391742ec00b9533e4d4eaf637c7db6a993ae912ce3faa190d46f5496e358b5deda07d96baf6622b597bdc5aaa3da74645cfaca779e55b2758005894c6d0b0c14f2cfa3ef81881721da6923d4f90f006324ede7c85bef5c3571061f79d396c3f1e1bf44f09061996e213d58c0fcbe8b69e71395cad8041e9a5abda2fb01ea09b3ba8719ba85c6008c5a964100299ac9b6b965b3cedb01b40f7a1909430225f8e61689b7ce050c51d5e2abb10364c802e4e0c9d5600d6758db62fb8116cf64ad414c1846e76c8480dfc413f1e1e30c1de91f1c6b760d60c914e9ebe4a9edfea64c05dae081c92c189e0ab27e5f6a20bf1153d3245cd0136f8ef1a8018ffb0113b6263d63a836b782ca4d0424df16a6c7e384d2199e90f17c6b0566866e25264ae80ece9332883e4ac5045ef30a0e7015609a5a73330a9c87c5ae155eb285d86187ed992e8eefff799bb6105115c61b915447c583943b1045026d287a4516971c5f3fdef651f553b0e6608d74360383cd0bcf78ed33057000b572e63a62a78b18cc7b8f54500af32694b5e1768c79e94a951ff4e58af1c7fa5eeac8aad1487febf1b61d94fe97e271125843b23a854a731d406602d78003e85dc870c486b3211509d18771700cc43004a6e0470081b7f173125e066e00b78d260a7ac0a2485ddeca399487382868db28fe042f846d4fb002595b862ce2eb8aa2bfc08adda708a392cc2f95840c32726d5469b111ccb546905db5f88b08e95f89e475bc1e9838b16201a88ede2ad2b730e41cc2128bd0ab5ea803c2d128ab5ae6af69086da0d86629120182411b0c0ed53b652a3ad626e9f9c0811ee82ae82c4a0af2aa4ef664a0527d692d96287558c815aefa04c85a253348b21e1b3c1e2cdbd8f8be4e720a34bfcfffa2cd18d4ac808f6dc4e66860ddebf66d5a9a3f77a18f194df5022e7bba58c48e0eda42c08d3fef4482ed84857bef56bd2592f4b4f387198c6eb4fa645be7b2f11ff4f1e1e48b16524a5cd5886034cc74212b178a84e59f87538823802769a886ce18c4f00ad1b3c55c6ed5aae05b6a1e89f8c7356e46dc5445f0bcb5b8c9f0b88887bcb1905744261688923e2d81757672231c815a0431935d85e773959821e3dcb40d2f2e89a1d8144f930636996abd4131caef539f24438664a9ae22dfd9f0faa29ac222cd36b7bd83fd84088dc9f25e86f7f035cf1c934d85749a82be2954acfd61bcbb2c5e0382df1b804bacfb3f7d51babb43879e5fea9dc7a264f1fcefe79b751dc25420a00e84f1338415a01e8ed8761b68a2fa901c47023da4b03ad1436367a699d1b43e0d6d1289f0f428253c919da4313b19b05cb19f148674533e19f330e1b0c6b15eb8e14dee556eca438a51cb195a0146f8613f0173c03e2d5c105943ee559e2f986dea6d10ee70f000aad7fa0023c860cef59972eac15c10d8415bbf848d512c40f15c841dfa40c69f5caf42840392f1b023ca52ddaa158fa2db951676463ea8721cab526aa4757ecc725edd4bc35f22c6796451ff8ef0a293e06f9718680585f0d1799d65eeeec02d4e6f10de24b00a12fcca0e21a4e32bd82cad6e57cf5377365073b3904d5459128288785ed72b5f2e19a1c35cec195a3f230506788b3640553ba0984783921f41fe194feb2d2d82dbf00a987d4da2bf7537f8fcb5a43fef5af649fdf4b45dd004211619581bd923db446c36d15a2e103c60bb9fa80cbc2665a80493e4912d0cd1a5346a197fbffe1d2102f6f584dc57f629e4660067aa0677e1b68b2907582162842f6b0749f3403b13dd9b16640e8fbed82e38ea0e2385040d82e44a8706d82175e81543b9bd033eaac83a2cc16f8942122b082c5a3d95304cff260e043240a0e11b6a84e05334398005b08a48474791157248e3b604725709fe4639ab3f2d5038fd61f2f8f632e2073cc0d7b4a88683768321d166ef00b90154a52804bfaa901f1cbf89178507423848dc0e33f3b15b2e363cc0fbd190f80a5bb2cf19f4a130328b14a8a31d0fa07a8822ee0834177be1cd428c4fc23df024ac4f8cff6a733408bd8a40a87d59fa28545bfcba46bfd80deb8c229e581aa6bfd122fd082505cd9cd830d4691c1ec86cc4b5a6428e6071658053f0614d63af65dbd1fdec51db14f9cedaeeea7cef44ee62f1a3aec77939e586fcda1f33e0efaa3af2d12995cd6d891085d224a967eb0b0120bbcf9819b72138dce8d66eea915aecfa4582a62336b78216cfaddf0201ec871706d71218242f967ca936ab86b06bc2b344e9133d04de9db20af1a01e81dfb6d74540e7311f352548a4c86e221b07eb7556ecd0063659a0a50287270e24d0cfe50f288253b2066065a5f7665b8cb8df35eebf11b53e8487594b994171ef96862dba0fe495f9ddeeaa37db03e206fa8e8d5cbfa53ad27a02faf570283e8d7ccc31df26665af32bd556a2f4886259a25fdf9bd2b8b079578ed90192b948c4950442388f70fcbd9d6f93b0309ba038e71705b6575094f1358fa2436302b1db1fba0cac3355063a416e737e0b5b0ae5b972b00c17df26294b79486a047de14b8d778b048209108e6acc6b782b308a682672a2b1c74d028e300713add0d36ee7af906c1bcc5e6d3b91d081f27185bb9d61d905a97e7d28b336a2fd17430f4b025395e03d10a39e47b34a5ec96bd19b95914b042ad64b08aca5bd17f66d77e7fa312b812386a4174f460bf87e4ae49842e75db50363053f01fa5f10371e99171a83e0a47d31b9c569b7717da75f25c494fd6c11a3f339a04b6024ab74a1a784592873aa9ce6564307f22b30547ef74ad4051144b12cb930e448d7392850c8c59a58d4a6f2647f5cfdfa34af70c668172bc0efb2cfff13e38fda90a926190c4daeb42dbfe3f5bc5b8da2f28fb03dcb96396ef43f084146be64482eed9409dd088105b237af1ca1d7ea21f50ad4a5def20a3d2c361b6e1927cc0f7ca02be4107e9d1c55949211adeeb2159ebcd12890541defaca02950989f3d4cf1924ea6c19d9f1f2b02f606c8fced0eb1485fcccdf71abf9b47841f2c554b68df1a75c9c75da597035790c5d8674668293a6e58b3c3e9be22a49858d004294ebb444d838077db510989d73f0c97e303ee72176798d9da0d088af90fbd8ef9c8b56d508cfb333f103904204e747dc446307d2770e0a7e8f4f099b19f40a26a7999fa593c5f3c6db1939fe97a8034119322db2ec845f33d2ee4aa7fcb8434bcc331efd060a718e6d06e03081c45185562565d9d8173edf84e510b6c486395203b915b176e35a5ca9bba2f9ea2563bb2a94b617421a7f1cd468c3dd3765f58f6764b07dfb63f724ba871a5444efd353fb0dd99646d4674dab0fff52fa3cd603b419619d58fcb5e6331cac53744eee84a2796846639d5a79b0c60437f8c737646976096092a37c46d88b35a48d887109729346923419203b87754c6e3eff8439f0710bb40bd2af3a6e42dbfcfd5682be86a093e9c35ad3bff630fe9ab0faf6b80c02c51289a8390336665f037b01b4d6ab58e41ed9bab23028db2d272a50b4d303451a488f4549c0f311493df91ccf0d7a84d482d85ba54971920386a458511adcf6140c61e252c38d5e8bf8be4edf4ffab80af8ce31a0aeaec513016d96951360b4c494eabd94ec8ca98060f58ca05140e5cb7a1660fd894234581179276596f75f89758ba9136321ebf2905c4d09a276a41392fd93383446d4ad04c33225c21c5caad818dc3611c0fd02c98dde04f535fdc738592d53199c3c5ef7ca80f9d46c5e5c31506fa2229048528ec4a9a0e9418322777e9b1f027581318c93b26600d3694f84a658718c72f915de3821aae0b633d1326fa9316283876a5f00508b8490f7375459e009f055a0c78615d3fe38296c90971d635485b2a95dab85c5366ef9e21717eb57722a60a4a6fb2b947bf51f9689d83ba9185bd53733eab25d4a6a87d10b824eea291d172009fca1034d3941bf85b60d5969932c111a321c8f03ea8872f128344491029479f55c462f95e083f393224dc52631e0786cacf26324e96506ff38cdd88acc220a98fc1a4bac3c4ac4e128dd21a5b80f9861fc7aac20bd2f220d576ba02915277b5d40a43932304f0958bdde2cd009e019a21be7816991d9702a2e74d0093cf139cb894b148326344003d96d082ccbb28d392f29fe8456237b4970b34102f02a59a22c7265860349d6dec468f76e425c3529da34800404b0dcbbfb805183e61c662b51d1a963417f0b243a97bfc1656c018a875aa14f6ecd35f75c38527f2317b3c95d3717920bd3b1c6bd57cec94fb05c088905ea37e0d1a68bdd24b36ef07c62d7652ffc6cffe980b87557edc6b66efdf9804e8b461ea532fa859fe5e643dbb18ca5eac7b72d81c8334fc7c2ed24f43fb0981b2ac8b0fdc418b1084c06efe918e5c3b0a5f8762f67c1a96afebad23e4dbaeef8251779887886cf964f5a82dcbd531a216810a66a1960bd828803ccf5bdc9ebf6bd4806c2e47dca6745d080e01a3c0cc4ea533c39f2495ccf172947b6c7060ca3381e621d99aa5dc4fffcac8ffa123958287d26f4216238667b3677e2d33b93cbdb9c6477a1d96afdf3a2226b8848042944448f3c79f7702d72d5816d409e3a4576213d565109ceccd81ed9725b868095cf088c5efcd589c410dac3fcd3229bc2153169fa8e8ec1979ac23db48623d9c5d7b9148b0b1f89d8f16a27c9956f11b78729fb92c275c1ab0fe407d1a714a3e2ec53990e680584a096d141ddf46413a1362160e0bf6f01f25f2c5271570906678549873e8106632fc605f4560419019969b67ed92b59f9095edea50e5c363c23d90b34834c37965737bfe019cfbd45c1e402d8c3c0c58aa8130f3dc44359bb0e79445c46c2172e51928c958867ea2e9fb86ee6cb1d778312eba3b90104d1ae6355fe1aaf2cfd96c37cf428b58b17a3c0d5a5c1fcdc0b119ab6495a52a93c42ef3fd7052bbfaeab2305f1751e3a1ea30c839b215de1c296b263968f215d3f07b96a281611470e95b713c23ca2cf5821beb12ad3108c65c9fb39116f2bad605cae740b57da82fd648ae7bd4f4c2ff2c28b37589028013320fa7b1e9123fddea01059251b10e6707cfc6374dec8f29eb17cfa1ae32347899fda3cdcf6020269fa51ad8dc7dcf0ca93b3c3d125dc898ae419cb1ca291d3e79b22b5052fe5f7651dbfe861f9b34db28699740a811c86faa8cc851c02d1af3c5a9fcbf58dd0a7f1324be326edc7dadbdf41a893a7c7b2a9efb536c4320ce1f8d2a68382a1017b415db80c3017d465f8fea996390061f4a93282eb53b61c0587db114816b4e729aa2452568e53e337ee261ad683ee846de25957890673aee6507926273987be2f33e34ca224eacc348419c66cff2dcfbb3578ca79d201a4a7437141a7cf8c5ee05b38cf18964c03ef35f635d844f1cebbaffefe6f4602aebd7322171bd9c29873b47eca821d319ec9200f8c0a4c6642e6600e6548a1c4711bbc8a7c5cfd41db9107b6340177beca69191dd8fadc60bc67002884309fcb94466c29d5c4478f0f615f481c0e25b70a9ba419cb48e648a74f5a6544dddb7484cce7d8e1a91ef052e74787f159722a6ce5a9bb29a340275012ade2e48c44734df3c521e6cad38d21eecef99165caafeb8ca9c5bcd4cd29540b69338bd3368a7245ac8260e72fff2e1fdb2a0972739bda006c2ded49b29e1005e75c4b0e4d56cda12638c9fcb1d756b039b3484b51175aecaca0a3c14014fa3b3000f8187a15de52a37321e2f51f7f9f43c1fdd4de2d48bb3891e83c6ee242f58581f5b68d613dc00ae52320cecf3819ac5d130a073e1504e333624f983c1b313e9993c6671befc4288950407bd9030063c629337b1566ebec50c8d2ed6adc35924f44baebcfd8b31a01be8af4d999fc8051e3951e1436d684a8d72ccd14400c183d11c1773bd4b2e48f3514db84f201aacd2bf09b053ab06180007f0f48694064053cecbab09132034870a8a81af914bc4f5ed94d6b7dd6d86af62dbd81e617d0185bfe642923dd80f4672a97cd68e32a3c8dcf6dbcb8032e85f4ef868b840f61e355c002299d1a20ddfd4c0f6dd7d944455fcc14d287026bae859a09f42eacbdafe2ed683468526748a7c248a93f7d53768a949fbb4017e665a490566aa19ad99a4eadc711d7842a8dd0b8924245571805cb5459d30535c6719f73377609de49b48b4e4909e06fc38876cba60ca7582911c603f52379e076499337808700007c9eb6044946f72d5759265584e2ab04350cc15802348e56e9059f40db812873e03604e50b22ba87b6e3d1b6dc9cfe3b2378a33c11f991eb106a06a552ed9112d93780adff31dc82547a43fd20f24a1b4183c803d1048e11fea05492592ad26418445933e890a2161aa06140dbc7a9e86fc645ac579d338f5c2afc07af410fd5c9af2774d2a754079657e270f2122b442afbc6db4f37cf06e80246dadf40c258d0161d5e2e9f8c639d991bcbea3ccfb736c6fe8dcd652a864359d19d220d9968b48005163daf92b4b4df7c72751b78f367e0544ea4dde521e7160881ccb9052315681909265f83557cae4796c618f8a711eb4347c1c966cae38e134b4377b0d77cd7f6e233b99cbb179697da0ace294737258107467eebc0f46de20d1b39bb4f067250a08fcb28ae49e776931cbb28facd72b83006797973d7e7c641546c0a87a03e8c09de9d3a95f67231a1533020638994bfd137ad9d39f31b4d314b4f5121a7db997886c30b7c709b511cd3673e7298de712dc1ab3d34782ce4cca622e19a4a2130db6040537ce5670cc104958c8eb5318848b11a1a6959617ec35ab4be574f60a816955547081e5c1d1a0f95c69f32f9134d1e65465073328e0218b9e12f6075fef1a8f219132c8acd71da1dc32fa85c53a76e22b6377be326bdcfdc9e46a9f494d64d58a9b08ea84ac243e1c7c942a84afb5ec752dcebfbd00f9227e5ae9d6421e045b35867975f88889a3e8005c01ced967896d44f9088f863f34811311825b15946190a1f60a0ac2c2170b44edc4b536eec5cb4a751e21fe3f2d9f2b482bde30a7e1fe79d8466e7455f4bfa53f57b83d258f0ddf422a669167b1122b9d8936327545f4a607421304a45fc4eccaa43f8130081f548e9be1e95e69fb25be533f5d1581ca6f08e7284b4d8181208f9d9af416c58b953dfcbbc70238c6ea0e0f4ca082da9bcb6d05fb83921dcf3b73f546522a8d6232e6bbac0c901150cb305c348c334f35451ca7fb733351cc7d78b5308b650fd89b2e8ee74c9b28cb427ceba8287bfad080ec0efc922222fae7c7648eb2302d6d1125deb4c12fcd1651cac791cab2658e2b6bf3c1504aef737478efd380d8b26fb4b476a465ad03984165a15b152d81b5d37d629fa8857ba5750e3664f621f65a812221c9589148c05b3a7fe27cfe7c0bd75cf77b104694f0196658ab5b98e3d9b211d5115fc9a11c0ca355685f3efe8c0fe516afeb1bdfcac77b0f37c23733254d08dc8bde35058b2025a632eb6095cc20ff7e994e378317749c405b88f19834f6282435a36e91f8f03fdd6739b4a43833b8c7dbc947d471e88442923116b88845c4dd6b77cee7bb87745a5a63f5b7e78af8639416bdf99803fb50c5723f03d812d52d70a848d2d3de71bce417ac08d3edc26a4308e32b83d73c60f8283b746f73e3173bf64dc254df0f7eb6855ec4480cd7e86e2ff7f19819faea01cb1cded087d74852b45c101bc1f04f40c71fe015a63f6f365a46a00d8072a7cef5310f48aaee7e181ba3fa0d1eb5c543b15d9c7c6055a4f4129a78ec94b8e384d339ebb7a9e0c06230d197ff9aafc860cef967ee34b25f6b9107de6343f95c282dd17b8a57f9bd6574a2c540a1cfd53cc8512eb7a24c9ba5a490cfd436d4b847bbd3c84ca481ed63df19020c7bfc9998d076821542aa1c9bb66bdcabe993ccffa9d53e5f9061ae5efa97b28986503722e322c2fb88c6df3a7965a2de8cc96540c347a3d4c9f25db50c7440dcb60dcf6f5a7fc69613c15b3ec69fdaadd80b0447ab76d260dbbe83d17a9630c006cbb6d13876d6e42b989bb4b0474b14245bae4b6fcea0f045e937724aa4a0b7c021d722ac25035c3691bca08b3ff548149fbfc10e362186723ae570fe0034cb67ded2f400dbccff13495e1c734657ce76c821f7b9a5a6ae84abb2e8bd84c0a3883d2b259f49c0c09c9f64668541350854877dc63e88972af7f220c48a8deb604fbf5da26144104ce73988872e0848547b14f0924b0c4580cfd46f640768c4a00077d5def5d18e8d3216244302fdd459d465703dc3bc3214034e0aae87ff9d43651211fd6dbe682371a3cf0c2e243ae1d9d9f46a31067e4e685bd4129edb4345c42b1b90ad531da3f8b51f66a4eb65a9a51b01855e8cbb3fb33751459ec30aa28e1d0bdc8b7eaeaef92c3fca017c298384944933a93b4e4e5fd0711b36db499540a49af3bf840774f82c6298faf65e4eb112d60418836f72098a1b06b6640bbd0e7cbe43f4017ca4066d589de6eaa990d21bc9d74a5a3f84b714dd99e7602e10dc468c8de2613c4c9f9a10aed170e37a6860b19daa046083a84d62589b58541e67a2accf439e11ff92a521b3451e20928498978f3d9962ecc157064c2c8270128c1172b16cbee1f080b4bc6391b18959aae73a44a6c4bd358e983982a71e7e5e9baf529ea1901f4a269a274269b74e741a64332287c3db941aff66f7ac809ffba95b244a5f4fcd686fde985246933ae2bce4473e85bb88cb7da3f6f13a6a272800ec3ff98551cb03c730fdb1f85e666fb15bc4f20fdfe3f5bccdedd168ebbc3852f94e7c270f517729326054b689e3f94714f7dc285f08a20a7250697fcf2746e1dea25645e8c6a98f5983d0e83c3dc92e9f747bb1ce8c6e7b986b1e00ebc2494c4f97c70b4af630fa6cb87bd9acba550bf2041d6c6c4288c5826a42a5b560b49ac78ee96322ac269abe2018b0d0ba327ad30f9f300951c5c1c54fc35b55e7528315d318f9215ce93569b3a5608e2ed6272392b77639df5d1ad13b6cd5bcd0e6de1fe1d61e73d0b0a2bf6daa7d3338c24818a08e78651fea6acb037548f7e77498d9e91573126331d4ea1d218be752ca162378a02e71d56f1f4b37711ea5445bed5964e2590b1f4ddfa8064cce21e0decbe450e7cbed4941e022114acfa9109ac6a29b64e658f7c9f8b5e5e844f854e1216ee2d5cfa446251f26437f8daf384791674a3edf71cf05af75e52d5a70f203c12831af002db671e63c24a43a4c4975866efbb82eb512adf9e578996169e8f8039512cc9d935400fee012c6282d5c61755c592482304acc0a525ffe66ecf8f30b434a72691171a6eb1855d9960cb9ab4dec8a7394b19f42d28bd81e6f4329e257df6a9a36a8f994340c6d174405f8c660f75bb3f11df4ac2dd0d7fcff016682c3e8d0d0e2b4a4f967164905193aac840bfacc7f512f8397fd21d9d8ec1ad59f8926c096ca93e10d468380031718ff5d127deea3884da0a8871b24b2ab5dc35a2b49464fdf266643a405b2fd5b563a0506b6509c8d8b5f4455ced5c8f6db238c0d0371b122541a4c839729a492792aa694cdb324788ce8f385bf852a60a63c146ab8e2fa391e363efe1cdbd32b0d5ec0da306115c1c4891ad135615275463cfa19dee1039e87bdb7ad502806a0573f03da0f24a58c043bdcb32d092244a268c92227af9c36461e5238512d2a51bdda54338f157a0a8a59f261f15b6405b2d752c74c784e95e0ca7aad5d740fd79287f58e1024cc665a268bd81cfaafea19caf3f667f7a08765bac125621d660d0def046e58f68d3bb92f16a7162b73385c6b7b57c5c0c6e116489a7e08f2e08fb6470f005c0956b52ba2cf0fec240a91b119d5abffe3bbd81537c1b21f67c697d72f351604f7a76a5c7487f7f5c7c832e41dfc9e4fb03cef495953a148b08452dd4beddc68fe0045cfbd5708b504286af33f8dd40a85c9a40d14eae2200a1dba6ed7770de49cdaa077e651058a3883a9241a8bc378b49bb443f303e890b6498d09e93afab3c550f36447724454ee4ff7168c5e3a1e781495753b57cdf806c20979c04062bc1f6b7106ab29f1feda090a7ad0976430594efc17f1eacda36d84db69ddcc205fb8d49aff501b7dbe0ea3f890663869367c0070d08d3066638c18a28e9233439286e5490b705370e1d976a9e432b8c9e2a23ac165375b946d83c4e42075bb8f60dfb899df850237f10e33d4b3f143731a47166d60e316f71b6ef04a739ad2a0e344b3ccb246709bb3427df747103dd87f9c98672491b6a9657ecd6ae2fbc68c795728c9e28939930a3cdb9c38d623281cfe88d0d27e2690fc36ba0e199303e782e689c32f1beaea4062f346549914ff96e3b745819af00766245cb9278e346a028f235d178d0241cc594d11df41ad40cad9f4386b7f5a39f3980dfd839c36a67e12e25e2001ae1785ac2cd9a525a6a9223aad27eb20a006a2128e0a5832a64cabde278301a338c547e283712d2dcfd0c274d4a8f4496fe16f17dcc9516b8d4da48142b8043d2648d4421af41357da657f242b16c50628427a51f943e4b8e5935ef99a937f017d5e1715395487aa0e03efdc4f3b87d3f250ee148e1131f6db716c359153b24c2d1dceb204463021322fcb020e625a751aa57379218fc12adcbcde1e2f6cd0bae10dbce5bd768ca2be75b3ccb383d2b64e9348d5fd8243270f2827e92b45e8b95977c91fe691a44ba88475afa76e800d0bceb1ee90e205da3012f3d8525a37631c5cf76997d455fc4d9f084c135cfd1424ca86e685f6442cfb068acd75693a749db49154c9b94362447317a51bbb01606c238ee26adba430c2e224f2bbc5ee8d761c2f259521102cf8a4dd5a6a85d9bc271e5668cf56eefd4af68ecc494cfa51c3f0613bfc5f89ceca91c540b2306464da4ecf01a0b3517302634014dc99d42902318c77f60208b1b9747d716a139aac91012cb8f9f8aefe7eecade24c3f80fee9a26b550a7d9bbcc6e1b1cd91540fecee65cf70e9d2e5ee260c8d06cecabc6884387942b79174024df26572398cd0e4e6969e813991c1307fecc2c72e714812659b422725375b9928fc7fc3fcac0f0c0809b31da5321d35bdc4521aca9422b74e98e9e38e84881ef755c4c71da91eef51a73633ad9889ab5497059a8193fade6d6c1ab3ef9fb87a693b75c421310b00d022cf25f6e3b01fc1593bbb47042530015e6684981a332e199a2f2cce32f1b28bab3505edd9dfd841d47e733aad6af91418cdc4f7948af32e96fea0a599cd747c9974acd447d52d7590e0a8377be57cdade2b889b5e2cc06deea9d49c2e3667ce57ae48c187eaa07f255533aa3ebc57d7688704ec74a74ad8ee87183042ca6e32e831823e1e0cc506e60530109a1f912c6c4044d311f100ee9420539d8f83d49deebb758f823dc16da1cbeefef50fc6d89354a54010571f2ef41b48fb0cb10e9b38a3b0f3938a600ed4e3137e3d63953b6a6039cfe13a98eb7f1736663c28e5d5dfa50fcf8c35206461c9bacc719035df36ca5eec94750a69e74fc35a0aac6308e0908bad470eef70aa05d40ea31ed28dc5cba646410ef88412341c3cd07c0e2edd7964fce05adbcb65c5e605d19874b864e81e1aec16adefd42d100a56a4fc50edf0542fe1bb7aec822fa58bec35531587d651adaa250319f3baa0d7aa83e28a06b9128192b7d4d13c1b211c604a46b8160cdfdd991b62de9bd4e57c15387e51a7f5e080fc1a2fcc787c6a92cb75f221fc45a67c2aafb99ad00e1d97430251744920346c06583bfd66b1ffc8001dd1439030a35d6725a3090ba4a074b998cabb74e54b1b2c2fd3a0d157492f973f605f292cd6712e1be36a3ad8499cc465dbdc8e14b89f9f78627c6702bc3b9a228cbe2cba41c43ce6fb05ba9bb045022b10fed3143ed6f2911d97b932db794292599021907160705074129ef9bdc862468cf64599665d950d1d190d0d013a22029464eb0d09c594b941d41b7c9e44c508534bce8078782a0d030f48488861b6651aab3acf948ba07320b498d5f0291aed8c3a1e63c5ecdd389006bca0ec22166e5ac888048282860136bc73bf26047986707f7c0ffb1da41353d05bc8570fec2f9db2dd99905fd6010f3c05a999815e1eb89970e936d130ac120e8a408f8ea800afac117dc3205855b2abf10dcb2408e98c5cf9321121e98a0a622f76e4f75438e37eaee51f3e88ef241ddbdfb831c28fc49a3d7eec3dd7db35077c7b029b3c8b9c6f5c631075334386634cd636f1ca57ffebcbb657c9b9c7763c9cc2beff66367665473d5b627ea3a91c44de8ecf09133b7db384ad3f45a6f3e51308b3e757cd1f145c7170a5fbedc25971ddb27263dcaee5e9d59757bed4e42dd9cac67dc89c989499a710651db641e6f17954d8d7168712965098890252062e3522897d2e7e4382c4555b267f40ceb8275f129bb383939d1504c4a33524a554d64125d25258e32a41926b060524a196d6e54bdeae82a33d2637653836bd8b051d5c8773263a3b48e455262177174dbb814ca27768aa6b1383389f29c9c9c9c9ce37cb6a7aac6e294286de3529ce2364d3eca4b36a3d75e9c9c9c9c682846a9aa2319b1c928ca6a6822cd993335353637353af2102c03cd804e3982a013b39e601932e8d00e719b1b9a484306afda5b653735b8860d1a4a696c689d16768eaa9bff4e23da74e8e0a8e77da083d2d181071f703e1d7540c83828a8c86accdca13f9058589683d3c1071bcd7fa0130200787c737e759667a2987ffbcd7e5334f4956c0f7ba147958b509a14b77190c75ca4a30a3e62248c580ab3a0308b79887f8b98a5a30a272727a71c3870400801cb81238410381f7f5f9ec8d3fc3a433d3cc44f5645454545366adc00002b328143d06988c99933373635581b8bf3f117006c82586c8b1fccf624e977824e5387ee78c2b3723c94238621266216f39333b8876df103318770e21654864e3cb43cf84521ff10ae51d9fcc043cb27d81ed5f36a7f748fca834e4270199a33285c35afda6bf7288d2a0002604a23801d5b8fead7a6a349ed40655a8a631d11d464c1c4145e50aa848d0c688da21a1c30d1438d2b9890010a4a33831b28f4a68b1a5b509b2d45d0d50b8e904e200609884a1859b058a14a03cb13366268aa6e1296a3c804842e63bb8ced194ac676bfb1adbcd0a56fc72a92b039dbdc1a0e6dd5373a07ea86dbf01a7ee338cf6265af9342a1e64d9ca9944d4c61353153452f60c0ec17d99e22db820fe44796ba4a4c0b264b653d45a0f7ed92e7d45447f3a9d7549c4f2acb20877aeeb3d728e793a150a82658b947d1703e28c91549713e92a394ab336716be92ed51d241e4389f58c4bb7a95ddfae8c9d99607319b9cf47a43411278b5713eeec1a71e7fa0a5608e922654e1f3bbbfbc0faad09d97ba4cfb54a404892a16d4bd331cc4ac757787ecdd2d811e0b35f14f0098e87e13877106d7f942fdf7f7a9f600a56e133dec3027a5abd5ef34214399ba4dc800a509a01ad4d93dbcc4ad7e2f14cc6acc97a9cd66d4fe11d0c92aa8fb71833014d8a37f9fbb86028370dba39f6679f47f5c3f8632039005e5a2a2be527b0a2630f5e32cb57fce3973f81083ae96baf5fba16e2dd12cead62a800ba36eed5395d91c425a1efd110d6ac343dd266440a37eacc42f568adeb7e31d2bc51b503753b7899fa0fab1529317232cfbe89bce17aad525ce070ae6c3bf0c3be5dcd217aa62ce4788bb737375db855c7927f30bb581ce9ddabae676793077423c00152c58004514415b5c71cd2ae50ab5c2be0ed45869f5bff962595bbfe649c0125d2c6b7d30f8a8224d68ea7c5851de95c02cad046e7545b895802a248906b6e52f24a42e0f7f0dac96ea824346db1240fcafaafe70ddc381de56559dbd3c42b86588b38b0f7fb9bbf9423f5a57317b5f65dea4d517a375878a57f52c2909a86e521345b5a76e12134f55eb4de70b8535558b9bce174aebfa9801bda91f73756781c01d44d81e48826df9bba360c80281352c0f1496892a3530c2cc0ac0a8fe4154ff6fb9a8fe265075aabaab43f55572c1c7d5de9647efc0ac2dc22cc7b09f18e6526a0bd03e05f9507a5f8ebabd5f9959b6b5f5a3095862697b6c4dc012486c6babf4b4cda19311d01c753922382afc8d5bdb73d40f7bcd33c22c2f720685750ae69bc40493d01051f7382c1a6284793ae88459bec3174ab36d0ff7d009736ec3f27058c3f6f0df879c095c851e8703f50ff2056ccbb1a0e551c3f2f05f400ed56108208e099628ff2be3cc06ccbe0414f7e8eeeefba2a2072e6ea8e206199e70819284bb3b5cd1807e3b8f29b1a34689b9476f72022b50e4604490103f20f1ea2d5094a02b0228e14418a88409aa3f37b173bd51bfed8a51172562a01899b9fa400d2735424e3728b8a1ca41c806091515c1508d13353442462a2a57d448e1f044a9945971379ca1015a6d23084a3dc9d144f0e28e74a07430daaa589123fbd1aa3c717352962e3da8fdd14ba1ffbff67050a8d063cfebf73accd3d28b1aaa28a37633d30a422f36d4d58eba495dc4bcae2c35f921880721c6b8828b981ae018481ce0c5182078517942e9861eb3330b0458ddbfb373c58bfab1ba2b106609a533ac7491c6103b3b5772d0c505622044860b0aa0c8206697b434518153125586e852060d225c908b0a9feed293a6263124484a62488c27a0c450436f510419242f65c4a0882e4f44682153c51632a6742185abc320693013281282881184808a2028828000a042e13784bed01dfecf962390f0855c15ea5a3712b7d134c448ff910824e9ee06aaed3f98e7dbf1d1596ac377187f347f6dc59a91618c10870aa26e2a8e82880c735e478bfaed9839a1d04238d49f8fea38eaf7a3ba5015b49f55dd91ba9baa82aac1ad4ab581a26abd412e4f84ea72394116953b1ce8f5a83c55f38d3b3a821b42544dc2959d5ad2b5571576fba24195aac58d8992ca40aa4bb0ba01afddd56451356ce36e2b536b7c3853b56c831d4efce5f2430870e2df6cdca17ca89ab6c12efeb65c8a045c85dd72f9a1a8462116f36cd54225d3026125dd7a8a61c154ff6196bb4ba70a8534066caffd91ed35cf3bc2793e8c906098670cf3ace062aafc2fd6a0fac7330b04be33550cf53e94bdcd3bc157d2312c0fb46159fe3e7da0ba6daaf0b5401be25285efd1a9c2ffe090c69e37c4c38132e91ec8d130bf7d0e0cd81ea88393d0f2f0d751042d2234bbe54320d12b8750d2184f9e4031baa28404d2921125244b47c00c29a293f3d80fc6aa1a92c0c83046b8429ae2688a5764c8dab1b34942e042fbf97d70cbbf07201c35abacea8180b692eea16032f0c10f34d960b15832b824d151042dc169d52d771affa3b113123119312cae8cccc06444cac284c50b296187b503aba22177f36b89a97be909082b4d495288f1b1f3c37fbff59245fd164c167f7e56cdea7e942b372a95755a1e516979f05b801b723777f0b4c508a42d46412568f241688bd19586dc44010bb0ea72552809acea67d23588922879557f165c601eae9e2446d10e0e9d547fdde03f5c5650e3b763aaff474211e6692caa33f95b8004ff9d2792429525abe213ec26d64e154695df070943aa38aafc4748e0fa7609690aaea90a33d51bea30cff74f66304f5f51d98575f1d52df84b66647972058d2eaad63ed169c798fa8105127f3667d6f27b37e3277fe2132da21f35417226d4d425d03b77e29a28526b81b457fe28b440b4ca1f9d7298c540f45b55f6a2d2f29042318da96528cfe6a3e6074d9716a3681eea6797bd904665d8bacb38bf03198f7bf3bf7f96498e50e098826e9296a3242d45384632d463b303c23e68ba50aa3a3feb827eb8e6a33aa019a66b7ed00f57f61a93fa58b72e6cc9aab696a15fc6ea0668760956db4b81f6bbb048ae0ec8ef80ff033a8d34f8fba56704fbf61ab02ecc7b00025c929dbb0cded5767797bd9e41c31106f50a1f4e4e7a41d3956158e751326fbe909e9d27a5b4a93ceecd417a02e9dd28734af63070444137494b925292968d296c925211144e2a27291155661ccf0457f4827ef04eb3bc6ed0ca155fe3d86d1075c5f55ae86417345df1e30b61ef34d2e0f842986799b5ddc59b483f376d6e70d3166318a04a03716eddba6677c4e18e103dd021e1366dc907b115019ad0645da8ee5b558e30d47e9772d3b018637fc3ea3fb5961dbbdb398e6f6567e3b26d833df827c320af03c2d64d698115b0831b827afd76bc0e08dcc61df111b86e5ddbb6dbd234a04281424e0193a63905501c06a49e7b2429ef91ecfba0015db267d6a936713571f1d6016d4f3b20ee7d904197ec19578aa3dbb75271440a503787cccd6c07d3415b9d297ec447f0406feece0118b736a973f20683649ab6c1b84030cfb4d7b40e4813e18b2f2547a47f466ddb1acb382236157b4ddba6641042544c655e0b5c31c73cb8f125a46df74cc1b2233e421f81705b1907666b5937c5fd9d1f4e895c0fe747123465c98f9a020b5de7b36a1357f6da722878d601653fc394253f2a0b2d4293139a5497c3509d884d95af611be416d0b51fcbba292d30d6dd33a6a6d702fb7b4e026f394bc7d89ec32be62d300afef3537d564cfa28a32bf65db1972bc0ba409a1500d3a8d193b0077f5c5380ff55e87d11c2e89db002ac9fac552e10d8b5c0ddc1dad38bf23d05ef84f5d2e086e086e81ebef900131922970637eef605e5072397c61b772e2c0ed2287a72c19023846b79846fabb0d737042b98a2c805038418d0b6ce6eaf360a8d30dab2cb778e487c7fee5290ece10cd9ca7fc256f67ccce08bdf026c52f9b73abf09b4501ccbda7ad081180a5f8b8f755fbfd6599d727ef439dfa7e7c528a4b3f62cab737632f3badfbf58e3fb482298170f9498f3b7fb623f23a036471ab4d5e9800a7e08218c72215602a5468e5076818de88bbee88bbea811a00eb4a010fe306116bb0004f5950291908c9a2061d942bf3d03b154177eb02c106c9b5266111193d799d79e794109435f404829c85a7c9931093c389fec7770459ad45dcf864a6124f80aed6a689013a11d869aa85baa0e1ad11c1a3a154706fd26d53a252c708b593f42dcc127dce24f75100a8f00c42da6a95c37bbd5a52ad645d99eeb9430f1cd5b114151dc134644aa2f6bec9fbda3fa91cfd913dd7f638edb95aece66ec58d49d75deb1db158bba67bbb9bf63c78ecd952b83fa33ec0161a5e9827e5e2152160495ee76864ea54c7bf333843d1652f95e12687346c11c452603e81e0585adc96db9f53e188ceabe6aff8c82c2ed54802fce67ffc34101b6114514c1663c4a299788908df53423c5c0a6944b444ccfd6ccf6c8504b98cc8a6277bbc4301648778f521a49892ddd9d8978cb6c0c4c4a2329180cb2ebcffe3d3369447d379495149614520a298594424a614961b949319162627b6161d00983c85f61eee37266a858f79130dd50180406f1b9b1128af3f1579d49ba150685dad6757fc856f71906f1ffa03c4ab23d5e681623096cc11e0cf3e4115c32e491bc32432a9bdb0fd0ea0373ce6cce6945943ae79c273c1962ba42c9730001520d24a408238b153e64e1428b2c646eced05837290b0dae408a42072b6470a19abafb71851d8be13f8b7924941e4ba7e97b5afb9f796a6af70fdbda9a002b78d8d656afd96130eeb2b5582183371856d490b593153a449a251814d9604608271e62f02507180e2f61ca48f1b34505462a2c3571118229ae9061c4aa9bf4c58a5a5337e94b96fa51953655da10d49e683da09850a190960713aa6a44f44c9205b2f145cd98d40ced414587342327ada85baa9f2806f3381365b18aa85f8bb220c700cc35bb25836450f7c4497b5fd3918162f56361d2089347120966f103e00c85de0ab07e511a55fe8d329bba67c626189b3a3655ee6e89e46505137c7cc226fa4df8f2e337b309cbac139ac62773e74e98de5c1eeb7f0216bd2898b79e6c2f8af4ba05365e3c9da5c28f4c2ccf462477f250d5dd495beab43ce20b615a8682b12c9a03155a1e5a9146d42dd8f48959eceeee6e4389fb824e2be8b74d145820ab1155ee76797c3690aa8d2cd5061335c0a8fc5fa6fa69c0bc827e342a55a667bac5df9a426dbc240ab83915052a59c8ddb6f85b225436000d94fe3febe70b528daffdbe680799b6385fc7150c43318a7558a1fbc4fcd2e4c731dc7f37395eb815ffdb366655e8c117b3ae50ffeea0978d6f2a7cbd98159f5382faf3c3d99550040cd665157a8160f863d538a683a72d41ee5b88d88945ccf39100a5d4f811f330e9c157085da87f0b9137754e9b3a678783c90e07d36447ebbc51a7b6aa53ca9f527b4dca6eab53763575ce0e07857ad9e1a0fea666b343bdecb2d7541bd60dc1861608f6113b324a5a568c48580f35c6a8bd5e98a44ff34728f62ed03cf58ea89e3ef55ca0aff2567a289aa73c50a2f69fa452e3cba305223f66a98fa89fcf7d94d85307a39396074ab1acf8112a31ab7aa93e37662949213fca974f837951697948cf0243e2beb82dc55c0a7bad4375d04b3721273baed22ea810cc010a412115b82cd91e58056cc5c7513ff85a61de07bb90de07b3e06012664d2aff83afd92434a7cc11625a02f3141fa21114a11935b2931a7f62f28bb14c8d1f9920941adf02497e908021ddf06177d75bf739b78dd29b9bd52a6773842c90feecbbbb593baaeca84261dd9eece12f67c28fbae407eab3b801045457b9b0ce8505f1806668d7f4a6f8cf90b9306f852c0ff850088d5fe32e3505f5f1a0a0c8f9d03839a5a6a09f0bd53eeaa3a6260e4a4d41e1afc466aa6a345b9733a13d29856d00b9bd7f53c23c18d002910f9f09bb307be08ae9c70f6bce154b29a592432498a50c6c59f10c1b5c09f330644e616670a84d8127214a44b44d4d88562b4a5bc86af52feca8c97d71f08346864f199ecc88019310443ca80006490e058eec898a2d9ea8a208a0a5fe62054636643027ccc8ec081c3adb324e34492760e02f1afc78e5a04cfdf6c5a586fa2dd3827989b1137fc8cb8b17182d05daea7c98902660083f90c170e2767ad2ab24382619249f20b440a291f3048439a9bd483bd4c69eb84e0d2216639ac69431030d0ca862dfafc469c8b6e0bfba690729041fb0a96302a49ec9740cafcaf09bc142a3ce18b22784a040f88279c0241132c43522bae98cb135712024f133e1cfcffc59b17e660827a01ffc914cdd4dedd44f8d1131f398c1a8f66b2f1803e82188000ada35bc2b7ee0afd7cb8891d7eb65e4f57a7941bf7d1509814118f680debbc30449771b81cc1c41488376c31ef0a5116d5a5a5acaa24356c34bca962e5a8870a2df0a9a9102ef08a5b4b7e47c3c092f3ecb1199406da4667fec972750fd5b7a11fd26d24426f031d09250c0024998a71f42cf88119311d34ef740253f564fd58b13f0058da011141212125a254f4f4fdba4850aa920e87eab7053e142fe76a528047686cd772fcb7eb329e3cfdedd3a9d67b3df6c998364bfd976963db6db14f82c9bdedc19bdedf9824905eee328517e28015adbb703ee3e99f987bb632be6e89399997b325ce8de7bd0cdcc3578a0e2be3df3e7ce39dd71a0a0f0e5e7b84251565014d0ae30b5cb69dab6bdbdae71bb2d2386b9bb7777f46ca76e866277389d6a39ae601c0d8df48edd282b5660158a89c2156065611b6edfeca2b67062b81b4709ded8558adb3454467383b4ddb73cfbfcbd0a9ba56e7b94d8cce272a1fed980fc5fc7aaf5e7d5c8f299a1b4ed61776b4d9505aeededc01a31c60f6ed277175543bbe1b6ed628cdc51a4e17c9626a5d1885383fe76976a37356c2c8f6d8fd2b09ca1344df38de3dacfe86d732eb5b59fd15e854b694c3afdea1e5e40546698c3b6180a250171c6e836c2bd2a712ba271da022fa22aafe25c91ca6b5ae0453536a9b5f19b1678d14d0d1b2a28b271a35dd56ec68d5595558daa5f0037b612dd230da25b89ee9106d1ad58f118c388561c936284142b53ca1152e011523234a4b4eca07e15e7d3d2bd1e9a81159a1a56b4d581de3225a5c42687c5c7e666988f1d2d96918eac5088068f50c8931e959969a8baf343695ba995bac56d44b641da08f6e0df9ed473bb2dec398a61dc095cfb9723e241c5324f49b7c4781ada41a9db08068139f02b41a9cccced4d0e06691a926ef17760445548b27352ba5afd3c6264c37eec703b3b4586ecaeba2894ee60168b6668833b68a1dbdc1c2a7cae617bdae839a89568daa87b3a87fa41a13eaa3c543f28d4582a07b5527b3d8463c666888d84a0500e2ad486d2dc3c51a72b3a2951d5689679ea42315cf08f106c3a72d245520e42ed050f22b88234c6052378fee20a9007f5db2a37464be5deb99f5fa0e0a6fea8aefdcc811729a09ed4859731f3db2a56bf9b8a3df633898b19346559e9a8224c52961f97a4528809039e310651d108b5c548a93a5424a183d28f8b99f38144351bc3a22b314fbc38298179fccb7021930408b2bc804c11648670776705a07edb4d14f5cb09e3a5cb1651b6e052636b4b8c154251bf9bea393a60c40c20cc0dd57ff5cdaa2d323aacf5a2c5114c606c6875a11fed0a5fc777ea77938595e95ac4b0d2817e734752b9c972946ca87c08b261daa2030ac4991aa61b4c9a209a5431c8149110ad21c5fde044c3836a03e22855c469444c5c121cca07a7ad892dfb2148a382ce1f90505ba430a10c8b32347288103f138b1ba08230ca4206d40f59e080227acaa2874964940510d387a22c86a819e283124e1436454e84b971822f6e44e1c418589ca8818d2328f76303aabaa1061345f9604387a29a2e28e6821a55d278428a1a219aaa9bf404989a304f9039a25bdda427c820e2891c7ce12405086296f87dbd8065205f7c293529e574aa3cee0d617c975bb73f420e056cce2897b1eef3fee0e3a00021a4a2b596f8c08c0a3fc715caf02150f35185cc4e554e895896faee54945f0a3ea7e744a98e06a56ddca49486db345486c5b98209d9b5d1c320e44e289a462f26861e52f73633f596a04bdd875c3608dc413ee44ee00a5fe3c2a0cd99101f87889782336b32abf26b8160cfbf028444740e0d41247ef2c11dba14137d70885f4f60105e823df8f909644e45a1791acf05185c292f063151f744a32f5a0d556697f2f22287c22ca22518c4817021bc8ff97388a0f03f27c2f8a87bdad5ec62ee04af4b368ddaddc6ac278db11b682bf00faa7a2098fd28ce0b0d0bf59fefe1405f3082a47bfc852947a687a3bdfcec718ec8cf5e7a38fbac6ea19e6583272e677021840c9a70659f69de91fef9ede1acb7c2917e7fd4331457c880030b42e00318d7fc897a1f63d075ed18977b2e4c6f35fb5f881ecebefcf993e55a57e6bd8079380c903f1f67bd23f227ce91f8edea6e5d5a6f1e0ea86fae01d085e1ecb3002bcec63dacb3c3d97f618af62f648ffd0b5338ef88fffc173a9cf57036ef48f69887b3efd2bc23f3fd5f700f673d23cc423d926ea128171447c3d1b07f40f698f7c29423fdd93f60bed08bc2fe47f7608f7ad48f7d72260dee72f32e33333b179186b907680af67585efcba33d84d9c3dfb1f3a37b520f5f48f7d43cfc2146ba677bf848ba873e840f37880ab007c3d7b6ebd1f1030f1a23f878ac8b42bd5501980022285920ddbd82d725cb35b88358b56dc3ba2836df3ec1a07e41a4a31a9a27433b0801318b49c742a76479f0ab00832cd90d2c0f7ed50eb4470086e8370760b364e3855bfc667c99ca2a21e8e71d8f10c2d0cf7754ae03523dd0f6da546daa8f8fb9505b7fb75637118b392b961ddd63ab3a76a93ae86a158da2f23c6786b92bc2a715e0cb30197d0ac0e635cda6f37671ecd299a99b6e6dd4a6832e541765fb19a62b5b11f673e4e0b8f12e00ada2560e2d589d0be83701107d5361ab0e848d165163db4ea7625d14d5a3ba28db7b17a55bd3a0eca26cde2a4adacdf0aee511e2cb2e8a6adbf6ba0f5e6976fddb799aa683f3c972ba31ef6fb270ebc683540c3ab60fbad06f6ed7010e19facd1c9c2ec7d6e1d8b0ac361d60d06f7637b6cd87ce069397857e5367dbae860dd24d07956c6e9eeb80a6eccff0ae29d96bdd0ad86b308ba8dd0d9ef4a8844db825b5543433000008024315400028100c87c402814824cf945dd50114800b6e9c426e5a301588434110a330086218638c318418020842862052a6ca3800f8bde78943c4442b92bb5bebf424cba8accf0b91da796eaf98062e6cabb340647b0be7f24cdca6489faa30f9eeaab8af9fb01443e1164ab0e0e5053c2c638c0a273c5afa36d7c5a13e09ecb3386396e9f69cc98bb7a84ace6e629024686c302b19dc205e47da530e24825d1cf183643821f53d25d4d4a8878f413045fb70d95fad7e7fed1707645295426efe037d3bafdf009bf1632bc5730df215e6b071706a80c511c5b663f4adc1d97625cc42ffd1c3858faf06b4c2d1461031162cd39501c440f6db593189d7de9b653614bcffaa5261e29f739eaadf2484695e80ea2b451e3adac1b35a3fd1c641f37b8607eeef6526e6fa71fff630b6227f23e385af80528747543e6ccc5ac57404a5db02c072dc36f55fc957aefd7df1fb16da19b49b2518d9b596c121310c0918b7d5f1e12c48a4d3cfba441c00631ef0d06fd70e0817ada5a99c3c01317b1bc4c500b05265fc55c329c31954eabc32b9bc8022f66f749622a4a2184d86f231d06ffc0e46e163d060e6bf01e3170e4bc6f399b87a0ed28dca82910922155cd250f5314272cea61a9c7dcda9b41a4a62c940773378cde7540da7ea5b7ca60e11fa1f395f8e1c40ea8bb10c356831a70987b2b00569486c416be472d38f89b5e162058c206375ff2d64c0f29db0745488044e603a2c8cc10df7ec4b7201ef2fffe275fb54509b61649b9bc05621c4b3a0ac603d3f83c470491f6af2d67f1c614a8aacf73a493f069d87e9fd0bd825354afced7283b1782184e8f0af8fa813c15699e976f9db20f25a84241a01add101738317633cc27bc5576a132d9b99546665ae581906e7d610749bbcaffd643d1fbf7a1e92f9c54fb31d1170a4e6b10ce1eefacfe8046e515db2c9a15090db3d89546720b485eeeeb88f0ad46dab0c25f8bd90b5c5c705d76a10bf62a37771dcc241f919ba422a00ff0c4fe9ab2a287a61fb7bfed3a062d2f7f81b9152ec254279ac14a10fb122ef9b9fcbf92e8592360f8b4686c67822944868f23abd1ced068c616b1d1db9a06ab5185e3b9947b1d514c8f88b0395768d9d78002a8c3bdc1ddbc7f9dfc3dcc5b72ec7f3624da3cd36abda4e1cd0246f8a66c5eaf17cca87da62c8f21ae525e52d05ce0155922793283c02102995144227d928f60183ef321d644619e12a7a685f50b2764b5e1a21fd6f3ee458908f786bf32deb7e25e815fb71d5f92386ddbd70cc771ca2f556c6f96a3e2cba30db1f2d75bb31c5b2723a1002d894753c7c9157a6fd52c87a9b9e38e79e6d69e9301e9fc1a6f858b5a99c462b353e57b745514881e37743590885012e0d562bc0f85b00f5ac2e979215052534d32e9215afb5916ae491f786d311af95afe33fd48feb3c49fea9a049735cb2e5c7bfb2e8799279712cb1e9f7ed3d9e5fd8d0bd24be0594e31772a886e3e122f8e2d40b232ce1b48ddd2c7fc2b5b8d301a1bcead33dd0706d883ccefa3c550d0b380934b34f35c9cc297dae72e8448e20dea44ef6e05e4e309a87abcc2ab0e843058332b187a9f9a9474f3c7f1d80e8feb824744dd67de8a409468e097d2438dc608f65351d757f08cf0b5d6ba3a92ce927382212f48e6b3f300b41a8ba914a66d53a6509583b14d90bcb06d9de585f0e8a64fa722c1b1461c1a66defbefce4709d4aab97394a2242eb5e5455b9b0c8faaafc44ab7c5d18aa4886b80283ea121d3b98b96e893918da223a798723a3cb3215aaa821dddf349d0a6aa621f52237177c20e6ff798a2f937f27506425256fe0101934a152c907c1ac2511826f5b3ef4a25753b39eee0881d5a9488b96b356260b8a05c359ea8702a07de596d8e39ce76e2800bd476e8bbf9656f96fec81d8be7a184889c9d711555b752f065a39be0b99e901f928d0063888dcddacb3b2dff6c943b0ec455652b36835765668a21b22540cf765c4166718508af50ab430cb8b96980b6e69464a19018995a794041aee3269e8e44df47b0a5e041fa3b9b0e79767a34eb31c8dedbbaa9c5e032de0ba1a1f31806126f73a4d49aa5f325e707917a2e56fd060e845278f1dc9bd8b0dd5e059e004002f1157a32d96153e20e7002bd6ff0e865a473ccf26bb859972c5f2c000fefab55ad9f1b771b368738abf2665c37f2c3dcbeb8f7e112ad7effac71e6410a6efde59d4a2a985700638a13ca12e310cdbb30a2ab0a714f9ab85b58a0c246b7137735d8707ed25c4fe6eaf1316e993ca0c2aec10585c27614397c663a260f23070367711d54493182c5cf110481af5bd3e85e4b7f4ff7e2f5c37d62814622b4efb86e618d160d37f8ff849738172d3d0cc0803848aaabb0ab094f045f6a33a76920da5972e9a0a3a1194d0e8fd090744c07cbff665e3a3f5cb020aa464f3339f22a4baa248441fedf403753ab9f66e8e1707c8ab7e1f1d71989af4f595e737db018d5ab2a1134052050960412781d4859357731144cae6e826fc82668c916d8c5c47c282e41037c86d2c70a64afecb9eec50bda8f740b4728ebed4e821a6027dcf6dd4dab570d0aa6600aac82a7010d81470a28cc19617fd5f2f96e0fbe305c7e2de2f86273f1181c7c2f2ca44ae3db2ca5d6a4c215c301eb6bee8f42036c12a13b5e5d9f0d31485a6b06fa5f537d4c2bc89350e2949eeb4d1cafd0bf08347a4d1363a1f9a893a4a88315ec508b80b047a81e43ce778990e7ee482cbc342898e885c7cec01235e10ef4e3b917fcb09072301279b4261dc88202f8b2c48b282c903add9801ad176fc5a5221d40e97322a0bfa00e493f69b9d66bc3d6893a0bb4c921d63cb3ac71bfa5df662213e8eef14066c590632cabddcf0f0b9eb6f9f77f249740e80b2de56c8dd7b30c6dae420a33c3859624aee4ce1f6724064e3fcec0de1b53290001ff59ceb88c948d6053dc87aa276cabc87d3e59af05a7dddf67155bb438095a0ae71df9385fd95d2b9e9c929e135d34fd7df74ab4d23a77e60c25ae1751d47648188320ea026cc890c39ccce0f5678cd4ffd3dfb7543a4b6dcc90a5fc2bc43c5afcbfc12ddf9f5caf6c370c108055f60bd7e6dd17d6d5daa0f96fb3af48b665e8609f77543fbbd0e15c1eca128ecf89a3d28977498ad21c2b57f55b2a7bfb0c6d74889443508a7d181150bb7305ff3a528d9ff172b2f920f1c1847ae417a2631df5fb52fc5d76a20c2ed84959d44d4045e5ac7c9b3b2cc8cd3b3611e09cf3dd4b357df06a73795f7c6af13e99ddbbdaf5ceeb17ed5e2a6c7ab3c85eb0c2e53b71eb15d102e18ca5936d4f3f640a31bf3f99f903cfdb0059b4387af289a3a9861f5477986bad36e16847ab740bd23d69564fa00ba460b7c9af6494e43d2b406caa174ac10d673c85d9d65aeb44737abe9ab7a92784bbfd186dce8e7c5fb0f7cfb7dfbda2888d3dd20d1cb6ddad728f8b9b485a0acbe87d0f7e2fd2b85aaf43445f360be5a9fe8e680e5bcb33224c01daa92e83a033121ef590f29f8a12a16ad2b8884bc773dc460875459b4ce2092e5fdf521821d5015f175829894f7d6860cf6501589d60d42d2f1005480830065fc97b8e0f7f3ac3222536f23fe95e000147886404dcc53c0084379a404ad44081af0a1c095be62b2053b180ac7b16ecba86597c4b364a912d0786a8282a6fd0b207e9372d07808e0c966f9de7f491972d2a29d12cf1667058ccc21ff9252d865bcb9c6e5cc04dfba67b4d0e07903c55391994a628741860d7fb51c7a0bf6dec24b76d8e958d86f6cb921c6cdf3d60b8905bfce811d6642acb88ef3a5019f4eee8a2b58b0d80ce1dd48fb40324bedfa6de35c2b783bfae47ef27e5c7859ad5e5a177df81161ea0aae5163e07170d233d14f0c14bf17a47f2eb10e9f17e8038eccc705c3928a73baf8a160bb3a59d757ff4837e7bff2e2d55705e47a4ebb136bdbe54f74e862bef5836387405d3926bcfa7ff0703bab37569c5a6d8b22dedf26ddb5d39e5eb13378d4e82c2e6cc65abe02c3f8656550bf62f66021c94b382e7cc47abf957d9d3670abeec689333bce3188ddb70240d3a56f357e2be4f09ff98d53c00c66f9526c6ac50e9b13aecb77be15fef2c7023e4ff18ef1a25238533901dbcd0c081bde62e5457a0d1a697bbbcecdf3836f8a0b124a7a39797d8100772075796caeb99378d6be0bf61495ce53b06617e82c28920f1cae219b5792b1e2423bdc1ca8373b30f81fb0c62256bc0766cfdcf180bbb511a6cbfd0db00437c612dc8bcfda7d5c5c7372837454d678cbf973c50d38184804331d1da0f223ad0800b2374b1cf2db0b0b5f0453b58f0b88f415961d4cfa8e69c77e77956e104d6d85c0964a776ab8d33e3cbfaa9aabd27d95860d7388b7565320decd494cdf0eaa90d5c879c5d2371b0fe78aef3c7589b55253b790feccf48feda8e7c40f363c37281cb9e42bda0b141b00591a9a3a95162e625cb7ed385d2e5b03683b5a01cf1f176d48c75b7860ae3902a6e01880b90df9135b7e6c0fcb810384bc3899b5fceb1f038ba059fb960dcb0ea8bb8a94208303733cbab26c2a843852b2c92e5008174a65aa1bc5259ccb26a8fe7af8d04dca143c45d4d4ca9d2c8474231b98e0fc9ede412d0c9b52885aec908ab03660ad212819b496b6fb6adc1eb796abfd34639cfa3a5225465c7e35b2f5d2aeda91a04ffa980401a7e3d5657ac7bdcdcc4e9ef50f23112c6d1fbe86008e8e8c1aeffaa6e0746edce9c2d5a5604bdcf03b8790e27c8ccb05e810924003bcdd3981037509b7c5eb3dad734520d043cb4581185286140ab987980ff2622d64739c4766fa07fb019ac718d9f773cbe11fb6d3d5561411c2fe5a6d60650672b7244578b09ef0d406f0396b24ed3384f045b0a11b5f266fe8da8b4230c4f8fa554bbd53de01a8ba240e714d73835bd17def54b46c2943675843c41f91dbfb224b07f470c233d2116cbabfec4b7a988d475af891040684c7370705c1d34cdb1c3dc3f635306204cabdeba0fa4b92c292985c9d3501ec0bc15363d5f3a4967ef236631b62c510ef8b8f0c43f1fd8402a34e277328e08a9c17e9271f7a9443b795481d793432c6a88e81f796f8d0a4323514c850584ee70724d9c6d962ff9a2ef34b7c0f9b422be336fc325970c9b9031d982f976d2b96a6ad622f43782129228d2d7951f36336b1b970d6e25120bb972092b2c29aeb55f6eeae9e9bdfea1c2d5bdc68c53635653fe248bdfeaf185c624ef2c9a0d1b8802af3d990302faa8e933606fb48a0f4b6c32404f4ae88ed90c4533afc733712d3861f205d7047788e3237eff164216f73171d87214cadb0cfd0be7085a5f40379c42e2d13424cc4a25954aad86ce58b849254d2cf4ac655c93758359c22a9528bfd5227a0784b418b695099b76c6bbbad66cc0c4e7c13b098741a50646eae4fdaff07c7f9a12e001553bc9038d36e0a1d78d99425564c5d18a4fcc15b02e30ede8a2045ad819bd2baf366712412d70849c6756d16b5017a129b7bb7bb7255d1a99d066f6b4c98a8070f7dc1e3680797374016cba1d36a92283b594e3fb8273cdca458546ba143de7c613629f8d2ee6d2458faf130800e33cce7a5d3ff3245e499f038b6867e55459366ab195a4ed5a9a6ca85a0901360c59076ea81ff14ff0ce2670f37df24bdad620798a9f94ea2fec7796146926c9983ed232f621047850d8e3b44386651d2dab3c28e19217a43a6a771fef2ff568acff0e6396a92bce5f6bd3bd02f80ba1da007bebf80c34976d6509374881403d488c1a6d6cb2c3c4cdb26ccd6b377ccbea93f924eacef38c6aa9b508fc48afda0c8a638cf5a4ec9256edf164104755e4eee989762e17f15e472ab135a21341e14386b1ed865a08be3989dc8b5cb5757e6e3455921d651144c2895e344c4482550553531a93eaab515214c222b9f1b1d88689b5a0fceb62c5e66d5d1250d0c010f644571d51b866389a162da7a0a19ea155ef6f81c5ab6bb3b0e209d03d163302acd881c46b9b1e78871cb1df566f28c24ad1492ed1091927da349c5c85455f4447c47ebbd3caf59389c12dfce60d13db8a82226a942475c9b961774b435ec632303f60c328cf26e2436cb48726d5de0ada743fb74008cb194d1f86b4c2f21324a56f8cbb9e7688266892dd6dc5ba675c9b7ce0e7705a4eb43f20318062c128d1f8cc2006d2eaf525d8a030a468f39394ad84649fd53d68b0131f2bd0506d0d5ead66250ccf85c72362370a18935e20dc077cefcd668e3c083d1fc5cd2b6d9fbec17a4c3ee31fabd9182be7139cf46c5a429064e1cda12e26cc3527dff82580d39fdc4fbf0f9d5713536384462c42550cd1b0044e8ab80bf7d870979d7c5147c64088cff00f4cfe656000eed0e8648f8788df38c36d756346226c11bccea5969c33c3802ffd26ea928ef03e456f3c389fd8076bb788b2d53f28800bafcbe55fc62439b4f105b79eb22ccce296d0be3bbe0e67794993c56d677a6456b615873e6fd858f5da925cd0a5929b02ac1e20c25c6d129ca27bf86e04cf0b0293bcab382ba6d28f5c6470962053c9d149d921d1109caae9ed72d247d2c8e9379699bc3f19f5954e9e547a9a0e176da59ad806e1648bbcbc01024bcc25a86a9afcf80b740590fbb084ed3a92ee8e29a3dcf03cef94543267fd46a3cbdd35a4a89aea3f52da4a5a893c097579688672ea4b69d6a0e3961aed4792903e6cacd5031e1e7932d0368bd13e0131f288ba5748df4d3b7757f22235b68e22269dfe6c0fa0ee3f32f948dbbaa2f5a4244fafa1c75bfacf0e425e14648440cff16bd9c035856cd774eca1220af4f12b79feb34db39e84c52eea8cc00758829c6e715e1bce864d046636d9600d82fe3f7d20a6a1e2330cc2268a3cf3e174c6d9d5e490e1e22b0f1a62cb075bf9e3134e6456f67e2949177a84eab8a99d32627460503d09b84a9f73a7c9093f20a009df25b4894a22eb3209004a8119b238ada758c9aa2247361ded8b724c39db2f59741b2cf209f50d7e0ff77fbcd7b63cc55f7e44e59ed5e03b350735cea318971b5c2ad87987bc8278fe365c6c0a0bc5c64a4d292046b1aa14bf6e12c2d2e0e940eb397b79bddaa51cc72403edce26eb62a0f9632d8a02c4bf8f44a3965ce9e7572833cc5184e54a9025fff3ea45cc23ba1d73c0633a20ad91eaf68ec4d7864a989bbbbc67e79aac2ccf65bfe26c751416190b9c340a05856aeebc30aeb82af3f7efdeb254f64cf1fddcac40dee9c85bda1aeb9745898af10de2ab328ab234de5128134446f979cf050dd839d8222e3011dd094ce49cbceab9bc8729b464ed1f17799691e53c8f1404830a23064e7aac253ff3ce015503fe07099fea75ec827fd7468a5ac20bf1a03e22fb42f7bd46ab6478e93500a40aed2075e717caf4ebf4e5bb4c87fc6300382aadd0702e6085f220ac098d2e24160a6dfd51f52885cf7c71301e4574c9a7182e5b2c770841ae7973e15d8aeecd5549d700b28fe142479c69e428fa9c40986c5b1ebf683733824842740d17834d5643f18a2acd0aaccf905d479722adb1c5403aa462280a4ebe48160b4c59262b21de6102c678837045e1238f8f58a0c82aaea6382e10c646943ea29ecc49623ee405374b78f5a75626b29c04973e986724ab4563b9a04a2a0e5085619159168731f3a43beeae325973df8349b76598b90b6fdc9e1e4b3ccda57aafbb59d173b5bd9da28ee5af8e906d27a7478e962a48a469d95e74766ed69361297401b40a6edf866b38c897dbc332796661f7d8abaa08a6bb6660223ff1b50a369a4b48a02e1c6ef4a25329f1777e66f6ed4caa320564542467da232c4821fae8b40b28463b772b4da623f8879a369cc20211b3d0436436eb83e8c07514c7f849809333eb8bb474537cb621b702b1bdae1dde6bf0cdc83b000e35dd252eca776b94ba3fac6bd73e13d436024e46065e0d8ff3091d81c12e29c7bde435d8632dcc93c5c7b1f371bb2a3573ded81d1c1d95f2f898cef6e74d748fe46a5d0b6648684f8f64600901c973170e73caf5ed7f7c6f70c125c02650eba01c58ec53b2219498dfb7adf59d8d44565e54813b382199af6269761a9049388922bfad4ee47add819b521bccd658ae01214e393009adf572ad1aa44ecb640d3bd35ba98bd9fd2c0616239f59a46926509381350dcf62fe969b52c17eea9c3b4cce4d938055f5c689817658345da7cdbece5238e3433f45518bbcfcc56bccfa44f9725524f9d0c17561217c77fa8a195612ac6763393d0601efa2f51b56664d9aacb36ec815c988770996142d8b8401fa19f48e3bdd63e1fcc18b47d987b81d2003bc96c1b8a845b02e4faa69155707fac0cdc3a2698373fee8c874aa5d9ccb790b68d65493f2f0c8d17ba4288c2bac7e48e394d3adce858888614ab46708a2a45d9186ef529c0a2b6e4564fbdd1e116a20bb46ad8bd4c217dd3aaae43a700707b702f2bb8f0edc0d53bdbb3d5ab815fd5d42e547dd79004ebac82420208a5038752efa29baa719ff5ba9c2a28dce746d7547e618423cd9e772f8cdd0a82aa0d9d6f3f84904df71cf8e6e61a64e8fc2d249af8d0cb525e6073096965e1ac4bdc9f6ce9d60ccadd079a801f96edab2f71547d957c5f316b99574f8e8d64d9fe0977b3886baeda3cef2050323c3aed2d1da724fcacc5f93806f271635def59498ade023731bad642c142d0a565c4ababd708d74a4c0230bc6b8643a2d47434b2cad32151f1c661cbb774b6ff6eb92533c372a7b5ae704189d14a902cad4b4a43d2afdcec188cbcc9f305e4a922503832c4be341919005bf34c4e9a4037bf482d04463ac893cd58f6c54c8694fb12ceb3b1a1514830adc51b4daa208f6fa955aac037fd8d005207389603ecf0ad12e0ca204f028610fe69eb17d3864e2fcf71c99b90a6ed9359018f572f1273ed3f6287bcc0c7c9215e75594a5c9b197208e1ee339c13265836790193f91fe719f2867fe82b60ba165901bb5abcebad586ecd8fa7daea27035c32c01e643bcdf6806fc4c87bfadf623a8e51c64bdebfc36c8be356f96bb38accc08fc15b7a91044e8f7dbbadd009dde9a1c5d11b8792d24486a32f885385dcef1e09fc541c64f6407ae2bf33dc7f5b63c4c4616e062ed630362654024b72505f5054d4483c02fe0a50d57a60e1154deaebdae23c93e3b98d8f8c190a3ea67c54be8dbe1eddce3f8b8d0e7b0fb95d475793203db9ab0c2ba13eb00dba1ad62407e1291340bb6283cbe99dd45eb013d8dd6f1771c50924c0550d2f73c33e1197244b208ba008691b5b39b3320976fbb38a71e45d6b1b777d3668635b3ec8b39ce0fcab7099bd8ae68790a0afb235c652f3b7ec897c037047e965938238d6d7cfceed8d8b35398c778834f194dcd3c107d53c5831d8f6802e4b60a047923b8f0e37daefb879a888f365069f413450c7eeeb47b658c858a8415767655aad3a089ad1eb7789ad53aeaee4be528a61dae67dd64aee3232c3c199b9d2c4e3a1bf33ced1bd1e31bf49c9426b7272ad9f2add612dfba9f843d4eab7211c5331d8dd4723388d204fa1d7eab28f11d2ca5197a1c94045b186edff6d6ba9e3c232c00ff0f5764ee0893ee95f6e0a70149cb91227aced19f1b8d9ddd3e594bb16f6b82fffed7aaa6acb14f44b7596764a60f2d4a76de97ecc36afddb92c11f207f043d0ba4a464d04d74b8e667fecd99a56894494be2b9d14d2d810f5e121954b052c2c15be0d3b9f7f5c38b1c048970ac60cfd51b24a265e2a5e676450d8801762304155032632b92225872b488cc3c1f17bc25d203215b53421a125af19289861c1a362c29de2a0b0a29ace647ce8f8ad0fff15ccb93e8103080c99b70f9716d836ceb4fe9d497a3560a654ed27e70c7f52d4249ef3fa89feaa7a67d685dd434cd8e393cf35c62742a6435211d093b932a763a49b27382b70b9cf6226ea3bdac49fa63d5385dd12d366e3dce280b006332e1e8c88366060baf0100f1f504c1290a074fb767bccb2cf190e8d9384a60bd5ad1fa23872ca806048377a53114fbb70fcb4c2096f1473efb78da1de6a00d43cb665847b4f119743e93772cd75c8fcda7c23e39d5d60c90d5de3b9ee0a1c8069f7bba70d73f030b8307f4c4804743a6683f835e02cc2e4c0655d916f627d5872385b845c7bd70c56458ee94c9b9dd7cf93679adb522dbc4732db0632bb8c5221f898bc6ed476a6c16727ec981d51729c0af1fe02083c833462e9b8418c26ebbf75dcd3cbfd98d5d9c516376464adaa3825c28787f242eb02296c9f3e56803e86dacae0a8ff5869f9e94aadd2f2bef0e2691555d21f2b2198aa94e8159d925a2878c1af525ef294081173e40b2975cc40f30a593abf1a3717a55ffd0715c3b85fcd07665eb95ce35728e738363433af94d2720faf1acfede763c1facce0de37796ff1165144ff9d31ede6d5008419e64f3cd316aa9857c80fce82eca3323e52f1bec84cf9e8570fea51c29f8a243470adf24a78e7963274aba381457e358d285a6e151630a4efc3723fcb12416e4edd4aa41ccdd57b6d5d44a4849f718d1e842d52ec34ba0dad7a7b563a2e1dbba34f573927f02fd94244402ecd6bb9699e07bd205878d5da7deecd45887c8dc4aae2604f9c0dcbb068eb64902c4afde504a7b148045cbce00eacd4e19cce2bbb610dbc63124b836dc87873ad0e120b56c156f52574683d301800c073f14e936d4de20ebcc70f082a4f198ac49cdf2e65a056294e9dd39abd72ec2155de68f3e8f24f242e8e75ef7ef7663c5bd1cc6b0c3c96d7dd9ddbec094b5836f4d0629ed9d71943653226e989d9fc8bd5f9ebb903efea38b1d712d809655aa456a38be64b41b4020859cf0c558117f77398e6e5793bf6ea2683dda630b60def79082f376e434d66f48fb1ceda7823425e6069c0a06f108602d03d2ab17fcc158f3f164e4857cb49b4fc121956e3a29d103415e364d583534a2c5e567160e29c12a25278759708c6b87f0b1128058d7fb40841d19d3c18e763fc5db189857eeeabcd3aea561a00ebe4a3ea22d0e9cfeff1a1ed74fcabbe655a5386656efcd9b215f84091b333889ff87e9ed5fc84b37bb4a2a0d8ec7be4d30fd42a9f68f26e1afc2be5ff3d98418dbd6666f6f35b62524a040c1aeb94ea11414d09f39644fdab56f9d27defac2d1b25654709ba9618230c368e1f440dd5353eca3b72a8cf2b907e7c0008900361ecc7a05301d6c99079e7e9bb5eaab08ac44a409d2665f6765d0dc5d6762d78aac361cbf3a8d5caafbe2a07d3d22fe14bb380747d9f290621c8c179919484793138d326f2435d9949054139fac2f8de68d85b511822dc86aab7e26df3a911c6bd1cca897a6ef36251004a26d4de0f9425f15c3bd3e1bf922b2953104aa74b7826a4d5a61c5553bbaba624d7fe121a20f1496dc3c52ef981cbd31ae1d4bdaeaf31d8bcc68f91abe2584cd9fe98004f0aeeccda04cf23948619bd54a3a524a1c5379f343a661efdc269ebde74398ab620b3a1d0e06d99164ca5d4590357abd8dc9b315305b79f73542226438a4a38202f4cf2d55c4d4eda144665f71338a0eab07e8adca6fc4f351eac75c0bc5a723ad5214a7a0b142779b2a2cc3435bb13f4177e59b82f9cdc7ebd8ab79d480df72be7219798704c98fd101264f95c423320483c1502493598ffca5ea0e96d7767f058cbb4aa7b6538c695a044131899bb958ccb1cdb85f134fef5047faf9613f43014e8d7290d88d37714df0694092795ee54f54e8dbb5a068bbffe3645b11269e1c5b23ac6f2819670ab85fb6624258140a3b84a5c6431f7a3fa9da3bf7f710cb8c20b5d5ff197cbd28aa78fdbe42fcd5a25196b6a750b89727e1d818e54fc32086c99e9ab5de8ca8e5118776a9275ca40f8e5c1a39646a484ac0f9a89769bfe5f158e8e78a01e2ba18b8e307b81c7db61c326952d96f9b62cb8500b561d455422b7120a288f0303d2a10684c6ef1cea76484d43601c2667bf7033c812a908aed05018ed21fd131448ee892b6c7e3c2d474dc4392940e49d66d6a5bdaa6a8835ba80908ca59509057b0336536307d6a83c6654c44323cc3edbb2adc1ee437aec509ed6471f5c0c1368175f6c8442a396d65bce07d66733f735fb5ff2f3ee638b53daeb0a9cff1744f071cd9d708da10926727021b7352a09ab56f494390858c90783c9e1ed8a5e42366d975b1eac6f26fd9d5a2949b26eba5e306441c1388cb688c1b4c46f8adf2db23ea285826b99bd22c63c2890a11ffe9a50554ba9dbcfeb5357cad7f195eb2fa10056f6cd9ca043410e641923a902f87659bd814f19deabe9c2f568043cdd2aeb8200e12daa077152d93a2e62fd848c7b7ed0eaaab0523c70011fba6c70b5bccc7003942d57c7ec57b64ba1d3fe1def16de0babee2d2a01667f0126f2d25f39ee0e2f53983c62ee5aa4ea17063844fc7a21ddf7d6d6ec25e9345f3182f415f8863541e823b10f69d83a87ee84c13b11019129c4a882b9be912cc7a569e45d08aa1e0d75916196e054a1345f49aa83f503a31f450cba3fcb5c2b038bb9dada7e68ad781adc61f7640ab851a133b63c20f56b2c51c5ace6450541af207d9ab74c69e0fe15efd06a4a1696f5cfd75d1a584250f55e9189b47a5d685c730da4ad030d37ab066d20846887e606a8545fcdcd1c1042e8af437bef5f7c53e72739543eb0bf24c3ef0040658e608f641067c5b559cf48b7110e184b514bac753ea36cef8ae84c2d84df0697a080508a6e70ffe6fcb42e1f275bdab71ec66b4896a73c56afef56ada7f352ab5215898456380bce36cba40618aca7fb0c14d040493cf2b629510c99f7c277a3727da85707efb859ad5d4b26c4a270a2620a39e577fe09205a2ffe82932b5c483533384267526f3ef469e4d82a2d82642f7b90c3fad74feb0bddd1b162a1f9696af939308501f13a89282a8c52e610c28a71e2d8287fe0c7c6605cab52287c08ccc026af5e0c1574e48dab382cc777aa6670c7f8750bd3b7fddc1437116d558c80811043c8dee31909d85bb2131022b5e6249a858d01286e7d44ae7d2dd24fa0e09eff7521df51578cf15057826cb4c05264a694934d5ffc1df0ebb9de7cd68296c14d824c6752135d43c255f10123b6c18fdf248572a66b3f85afbd168e85a2749647c15f11c16e1af864771406a0571b128865b84411b84048b154d8acc5bd90c0860ee57035640835f26372d2c0ba3d3a1443d5070cabdfd8b5129fff1a7e73732f6912d947ca1c1b1ddab096fa1f26d90f58c9fa3b1f159c72762a142b6a8f0edd694020c33eb04c1e158aff1303367816739741a8b506ce79d215fc254bde2a06785a3945512f4021c7ba44508e18602f7bf64761695477bc4bc40cdf0db2cca700327e3f8e2fcda1102e4b10cce7280466b44f399560da8b44355f26cb212dc175d1dde05cb25af653658c0e3864ee45751f788eedef03f183028edb53a862bfe527a928922d9ce65d529eb3dc9fce28f75d2292fb13d21a099119260107b686bd1d4457c8eb08ba26d0f7f3c710769f901a3f6c5c20e4ec97a7343a328d7d9602277a3c3683d978bdbe6397c8a648f9cbd29302a4da63a85fb58e65c724fa23ff144358cee24a24f98e84cc66f9a32ef9d5a873bb2583261df099ce4d3db3a1f8806840924ebd0352c879b38cab96522122f9ab07d3a638f732640c5c763a8484194d772af524bd7b46c4cdb530a0012e0d57e96c7cf270c3e6aeab3171b31358e32599af6e02da741f4aa76be19f0bcb920d2a5f01811751ba0778fae509870dc774f4d2ce22dcec2a1cb43706624303b1ee1106a74bb7d53483a9eb13149497b0057163a2610522ca123c99f2d519677c64e2b08367545c9f712eb28b03ffa0aaf05a19d0c9d8c352d571bc5a95f7a05b40f95ba0947636cf89d515adc947cefcdd64b06c5e0671a03a1cd3aa485acff5a65c9d01622c25bf65133e98aada16e5aa8eac9dfcbae17936835e2b2d48bcc1a018cb0d3fcf771d5b0a5ce535c07f3f30b8a491df450c49f186cfd342b7c1098293c003d2d67ff3fcfed71c5dbd97f4b439891525e40302e4e0986027819213e828106659bea58212df3387fdb7793899260a854ee88fdc47e8fcbdb1ec87eba504ca6de3f29239da68b937941e3f7063957b0f4fcd34e8bd623a7cf4493fea060e1432b19450e6668d2a36a0c349fa4672f0f6ce879c383cea2b85ac2ffe89121c8fe86baa62f1a2513b0ca236bb73c31ec7266b9f20f374c059d9a55cb57b2af2fe68639b141721fe93517b52d578fd3fe053b0e6fc9e39492902c2dad10e31c42c38f9d3c9aa6f27080c3c2896f0a5a7cc2602c8fab95160f2efa92ba6590a34269bd3b3947a764b550031f9bb3e77167052a6034a0d50663d5dd36b3034968ffeb24843c050203620b15e3936f7561bf88b88c0f3050eb1202222d883590652c44454b21bf8ddfc285b2b2909f20d42366a9dd548500857002b8590c93580f3fe40626f2bafa667122ca372a7efd387210e2695249aeda02890906ec7af9a85bec28676abd189178214ff9d1ce62e3d57aac95ea7a6b7cb8c1a5319fb2fdee98fb6dc6cdb59160b22595f204dc2a135b2f72ba64b77213b4fc004f7852384aa6382086acaac242e6c5078adba803c9eb4b3bf9716b71272f8ae69228fab09f6c41e28990b564b83645f6770ff348494e977d30788edc36fc76f03cfe6d9ca41e7ed4f68315741e1cdd07396393501ec31aefc2d3309759ce95a8129c17bfb1d165a530d8ac948bdbe1b59f6cbd55ccdcfea502a104c69d902610abc3b76f8481c77d1110d1ef66c2f49c169af7adad94cb28dd27087431532bda4a4cc0f77cfef83aedcec62ee8d7fc238342ae9eb5ac6982802161c1c4513a60f8eb692598c4f090db692dd8f5916196c0e0cadeadeef4fc1a3b275deb6296dbcdc26f34229cf20461c88be6dcea1feb58ce6f54c61a3abcb52e0a9b65a72003a725f38a380b1fa36bb28ada0b59ea62d464345874c25b6d6933784baaaa9db9a98884019690d04335f9dd340f97c121e4933e32b314b078413ab9f00f310e4f58bc3a710d3f0355c9c088870ab1f2b361227fdcb516c21895c71d8b77c33f12849bf531b19fd30648b7cf67fd70dfa9362a2aed39baf792467c1346bb6d4f8148a882abf88e9335720be5945d6957fa1b5fa4be33ec5a35325f1c4db973eb8873f9e14cea1229b3cef36d97d6b1fadbad350462ede4258aed93730810f46d8e08e8f9e6e0f4986219fc0a9c9273e590265dfcf99b1ca8975ac1241c35c973cd2d4a5d65d2ff2f5c5d9f5be248150c03f59bc3b4f8c964ac1f0368fde5ec391cdc43a8346d7d870db3259800d618956180912a043bf9e846f205fd2b468c66c046598af4836cdf99c05ce758083f5cd50db3a3a15afcee24447b6d488e31b8585c4e38e6ab8cface1e21738d96911e45df5575368e15acab03fcda7f080a8893a7bf19232729d5e01bcb3b3e258c168dd0052283140811afe388c6c0ecd5698b3cebdad3646a713484e541e35c12281ff1ab5daa0177b1814292f9c608232bb439a383144a2e5f0798d42d68e2caec1016d91ecd3a2c93a18ae8ed64bdd1b859b02af14a376eb217b2746530881398d56ff90bc9d0e35891310442fb1ba9a0f7abe54b717542b02c34ea90327f95a9e619936459ba82a3af1b8fa014091471d3f0c91e60129346b22864ba2ccd29f33fc922fb1defb786cfb7915c3b967da43ae6dd9b767aaa7de2475c2aa108c69c739449c3f70d0efbf66ff64045b96f0b058be18ad8d94a7895512f3e24b4e8e652d51159c5a1774a192f0d63af95dedbb86202a0ef4ae4880bd3a5539f167c5bf9c9407d1d29a4c406c7f71b937d12b8273ac1bd6bc9cb67f039ec459defeeec97084d139b7b127b97a32104fab8e8ddd083c67000d56a3a8180997cfa23601a1bae53b9916ab25dba2e275fb3736b407a24e826897f45566bef8df969aa66fd1a3c4a062727b7f9117d8611e9573ed1e65bc5705b13f1c8718ac7ebe22098be6017f8d6563fb23c82693c0c04a2a6d41c44c64d0315a101775bc4c1cd75b6c1a780de77c22a8b2906282b72cbdd68cdf4a01ab2d52aab240e13b64e37ec3eb6860792e63778189aab2e935dcd7497f7af2d8d26254684411122bfa1c103a758810d28f8f0853f4c700270d2098909e360e6363a9806855107fcbd09e08d1fd08e5e64d084532fe04adaea91d675c94e07ea6a34046477f97bc165a009fe1999e62dbf0f146254d459286fc4c5462c85e2a43ceb6382c3ae83e223222e567e246400d29baefe43d3bf3c31b5e335d1d445a781b7e459833752006808803a4ef766fca755567b52e73af1cb57fb05440838f123156a638d2d35baecb4bee18aa1608d1290ab52d4173da13742f221e39f68bec66e3b50e523676cbdce0b5008aca494a205ff5cb5fa8069d8dfdf80bfd2a71eb7da9bf00be7da8fff9ac5b7ee9600036258ca94cbd90268dda552898c6b438f8388d327e481c8072f6fd0571721f2f26e66d352e9a41ffccc1984239363ff5a6e783594038ab674b3a3058f66f389814df0de7d7af491f4aa4334351a4b7f3cbf61bc4b7ad76c7c577eefb92f92d02f12a95682d99085754e28599c4424e6358ec05df1b6821817b870cf004035e5c510b057935acc4ff2f91bc8a59490c8190cd551a7f30314504a71cbfb942669ec674987671e1b8c97039c8bd2f0271eb481cc880a4e86bd70016d5b447f2abc62e00f525ba6f7258937787946839c857620e90e64b846b483d232d0f4b6beda67d24f830859b69532aa509aa12b909d3e8c36613f7b797ef5bd4495dd40430bd574df6dacc3300b8d21e0ede88ad048fc2a4c8c25e7ee16b7b9c57a20b79ecc2051bdf74daaa6a457e416444a9122c1e2c005c8cfc79c7991f6b61b81849fe2a5eb8067993c4e89f78062ba49aa4d57af86d4fcaec164394f3ddffdf2e20483326b9441b709b6d2c93c0622bbce6599a627f7ed9bfebcf49d2050a6ff36164dc25f3133600a6046369bf21652a188cd23bcb0d2e59a01606ee6c736c84e12b1c657949a84866ea9e63f883445626991611bf8949fbb072d0c9fbff9d0653153494f924bdcb8bb5aa54040679c764eda5f4b4c84750452373ba6e89cb55297eb928e71f933c2a5d28c770c67fd8bf6721660b2f7b8e19b0b3e73bc2756e7fd9d1099052e81d3c9deb456c461c6d71a5d222af4877340aeb6e79692c1bb72f40e4f94b8ddb97e641f07217fb12ffb7312849b6f8785ecaf7a63c0286a548fc5c72f13219b03e0a50e1157f9ba891aa80e7427baa80eb80d51030fc98e49b8095639b9508416f983f817607e45c10fc3cf87cf56843f00369912d714ad126762d064dd8120d97663bf2fbfb34ec48a0893a043012245ca4ce280af67cc6587aac1643c6329a5fbef225bdd7ad28fcf6b1835256c87c758d97be3d7ee8fde70fc9e6f5775983fdd85b0ae62d8b17d47a531a91eecf5b2bc0d376d622d2cc1ba4abcab5aa6504b6bddf79eae2abc925158b9c07be023c5b7dccd3b3db773f69313bd45a631ca38b03ede2f3bea46c4f9c10fecbc808579ad9ba8b73024fbb0adedb4121fc6f32fa11122f2d347c8e2c015931b6a1f13d612d1d2bf6fe4869b16294d84d840eb6057508b4d5c4dc5ba80c5f6f21c7fc3f2e4009e11e9c70ebf5511376105ed8fa22055fe29d40570528f3492e5f1147b0507e045bf2483ed8593239fb64014f7f7e6c98c372189e8ea09aa79631d0a379fdf959bdd30923aa85379f9fbc6b8a4509a8d4aa5d248e272ad0798cdc2bccfaeaaccbd3d0cebefaf3dd43dc64463815152574cf3f3bdb98c4eb33f22ce4129e59f8fcdc6c417719a309374633b55ae73d6ff10bb03d3a065dbb48d14d466bcdb465ac22c98549c67d59811a6b1c4976bba62fbfc7ab24e622112548bea6e2b9a54941614408c2cbd694c6b01c08fefa47d690527290b94f08c34261f2fbfbd7fdb7328592038ff191c4f7a6302ff5c76f30830a61556cc052394e1b42e033412d8946b7dc16a9722c22288344eb4cface6f99948ccf38309e2f089741d475e7ca5aea098dd2982ebc8f677e15c0ebf01c8b78fc7c6f6f1dbeba91b5cbc6671c318ebd3f895a86668ee24c236094554460fb9fae386fb32f17512f5385f3056da14a2f49e45c53d0215c4f3e9d03baca0d0d96864f9fc127ca5aa9df0577aab04fbc2e55e6f61dbc2ebaec7b63355f75026f9f8b16aa4b689abd3f4e8b6423b78c88c414c12e6fe1d1d26d48a4e9e26c8d5431e5ffa4b366d9bd11060dcc009f4ccea14bd0609b0af406d13b59320213bbff99a0ce09a9129f1f43f0be8f28dae95326c32a7b62d29576abb68f05d201a3b9855a5885d45103c9ed6d5ecb0ce4fae2b329aed04fb54597bbb2700c20c0bb5c1cb67226083cfd3790d6ab8fd8b501848dfdee9bc2356d5601391eff4b10b85890350039c38e6f3a9942a7518084528951f78bcf4c1df1e561f86bf6b04e0c7c6948c8b8bda01df79a6fddb25b12a813823e6bbe7e5eb7070f22c11c672c2f820da7b564b1e22468ea56bd17f75cfbad18d40d036ab2aa21b15e247153f784b2cfa3c20670a143409333f4523ec3e40e06b2214d3762a17d8fea50a427333d77cd0e33536c83c437ce081c19a0c7d0fe4e7432ba72ae7d4fc8ec74178378ed1e8769305255983cf4eff87962e62282c4377a125c73aa959c4122eed79fa4e887dbf43f22ef36cf23876ab9c2bf87c2d2af816f52c2fb40b88abd570701072df03491813187452477e5c161b56756dd4ef3eb33cde464389f270bb55a838b89eb297e128c8e6952237d4b2e938b1aa975dffb515a8d192e48280ab90195c8de6fc4a4b80d8eb7c61c970b216d2fcecf429d389f5759e08a934ea65fe62b955a3802029c821ab0be0aecff12624c8ba8d4d38a75468fa1eab7f7c001ad301f74e81ea2e4a633d544161580133d9310a0d5a7f381ea040a4ed152819f51eed7e9bf0bfc9f63063dd80a6383b27769c04fd4be9cbca4252551fa7749b4afe832c4f0a6cfd34e2ca68618c47558f06d3814c2d5c846b94e98a7938356c0b393595db9e5da5753d935cf93d4dca98fd12f38bc7a2387b92c64080f1e84100da2318f8c08bf1a96b27d9866b086998ad9d8118ff93b62ea868d1eeb261726fc92c851fc8820d18afd4a5fa2c2bde9db7929bd998aa5379c265352c7a418e77a7c0e79fb119ea195a84af8192258da3e6287c19bfdcf39c8b3bff1851b7259a15b31a0d7a0529e3164d754ea68ddf97355cf68783f265ace58766bfff9a5e91958ec4680d28ec6abccc8249a6b0515c3bb5c6212198ae2164692172daf0ec10da80560908a127524c885f801087b8bb94a91b937098ca5933a738f2b60d56a0e27cb13f19ebc2e169bd89ad80c1d5d5fdf93d31e25835649e538aab51a4282f5e257161502ef2fae2762e4b4d94c744fd29830599e58d3aa7ac85a4ac7f28c5a1ca74630a858430a7465cb8a21c6ced2460096b27aa4f8720c55013cc3d1e4f7e44af58c3cc569e1adb88d6340351410cc074879ad7b3ba56d05e11ffd80569e888fa1c662cf7ee7a3c62d48f40fdf9900e0978970d3ad9238c72767430e1d420273dd71840447f2291052aeb99c2d75482e8ece1b0732969551d0c9cb9d85658bbec9900b8df636181f2fe6dc1a0da33ad76b3550b8dacfd40e10d693d3a2695eed3e5f2dad76cdb1bb969632a95dd2918ceb131c07858ce7d4d707ec50ac63707f56e9a92088b435faab5836112c33588b32a8651cc10d00e971dc29f6a5d931142cef4ce73ba6cb9c385ed785f53cfd189e3824d048b8d9e50299d8291b7170c51df3d8030da3d63d9c44ac26d724a3ca770aaa754c21872cab63984275ca97c1f87ca812dec17e3dd5c75da7f123112723cae5474cb28a362716b7a8680c184f56adbc7f5dd348c67acd50ce496b98b216076c5001d1f8cd23c51fe36b5d9802ee2722af35f7a7a4e5e255d13fa537c08e1716ef0342823066f9de935fd69605584c01e4f1eed4ca01775de6c4c72ed3d521364714fb009b5955c1be42387bc7da12e9cfde62c98f07b58d6f9e834da1c4ee19388c9bd21574efaf199e75b4b1ec1a2dee00de0bc27ee82f053bb046d2f98ac037b46d5053fd96497d0bdfaba12cfc00420c0985d23a41bd493377209b52d648711745c6aa505d9d3687c46e437c0ad9bd64f826bbb91c1003fd46da111f7a5cdfa519cb883f96589bc366d3844c02890f0b69b4df02c40ea474fe7ba5bb56943f737923fed760dd7bb499171150686fc051c9a8bd41074106ecab4ffbe5eb4e1f44a3b5a4afbfbbeaccf70e87c2d552f2bb50c48e5aa43fc23b227b6cde2627b477eee5708ffe4286a1e5b8e4b4ee4bb3444841e93124986cd2330c1914ff876b3c926c65bc0987222a300b75ad8cfbd73f0243728df0ea68c0d6d0c0e7538748cddc7690a60098e7c35ec2c26865e73794b8892a938aa6452b0a3fc88e49017f22714915303e3acbeeb8b887555c8853ea528e92de8f28321dcc4fdf514646d6070f424161548218a1d4f010c0ef0afef78ba4a23951544fcae40960372a5cb8499995ea31ae1bc525188717a96aa933682d84bd2b75ae9b6d36b840292b4ca73ce42eaadd0bfbc40a4c623f2f75ee1f5cb9b7942c2b08d2c9bd6dab2eaef30201061d4304553b6b07c7001a546f387eca82c4f928e3d107bb408272338ed92dadacfc108ea653a8304fdc3f06e5f75e9cf0c695875efa9856af8789a336d21b5f23206ee5f1af7e8b7a4d26624598bb6f0388d96b2ca911d882435cf36f5489bd527b673f68e1e50c058e44e987a13296ba1c72cd7f952f002f530e6c87580aadc421959f5cf583fed492be506b309055b33d4a289dc6b160e0764be60b5ae94f017261d8c34f11304f14eb95aef90064fd9f67675e1a04456c07c1da83b70784e0a1262fa5313c0fc192d8266f0cc78550bb586278348f6ba9add7147b895260128ffd1dc7c333879001835ea8e9df61761ef5eca866ab34f083dffc132fd73fa48c92900f019ed0a9668a51fcd4bd4117afa9f8a0df78b504292cf61c833003e4d30d8d94bfe4be5b3f53b76c6804a421f02516a4e692753eb80a23c64cc487ac36b3cc8a08309937978f0e1a0db6987b8dfe7f1763efc0b192340348c8885d4512b073c94ed660a25b01b400a1a62dd7ce54f300751070b3564fb318a9ffc6727f2b3f160fb8fa6a2f9d86114eeb2a32fb80914020d2d40435012f9baece51b5356b44da2ce4700e3e1c3d66a4f76cb61d5d4e5389034a804b7699ab127cc2613a92cbdd45b3560f870a46967046bb3a6385d5828d81b755fdf8fa3f265184a8f1026435c036974802b4dc8ce7a0de0d2bcb170cfe0317d276ef1e9d41dd8e01011574a1dc03a1245fe4109a8f0bd7a04f3c3bdec6b72206ed6f66a8ec0fd112827dde2619625b39bfc37597278ee2892dd075ce49c3d806309642a128f94ffaf86199a50c38a7f2b21940ced401f4161f9c0087e90c5b3dc1fd160051583ea8300b038014d238cc6be12b6d3c02421ca038e579497e0356c29634762adbc3cd46576bdc59938b60b210652ecd6ea44951100fcfa7ef4d11e2b918439cf8d198049ec7e9693ccaaf9a1aae55e259d428c1bc03f5aa8366804e86fff10934d364cf8d95e7f699349cd3f9d4c5761735af8c9e7471e78f2ec9662d8677da0f9377760fa600f382789dfd15ed710249330e8ec373efe05f4baf1fd3501ef7e083849b1a58fd9ca5208c0679ae1e75d0205066eb43c150b1f82a6c874d9b941a01e1d9ded979940e246c22fbcf04f9a6b96238ca8db7cdf9ec0951081f02a4020bceef404eaa55337c1d10b5bd3877e477481551dc3e809cc6c43373b694bb3cfcb50fa99fbb2d451434353fc17d26cd0fa3c5ccc396903783b600a4e3f9d3427b0aead88714c0e3e54d8d3d5171fa211a00b2b401e4cbc69ec406da3b135ee2cb69b5bf8cfea0ccdbd440ff21811b0c62a276806d932fc7014ca916cd46d3fff438382290ffbc92271c0f5307cbf36fdf25fa2f31958c3b66ff3ed5a25437c3e13e44505a4eb5e85e1d43f6345c322ef95976a0c22340e8278258a2d6c094e9593bf4d4b1127e7168cbdf7744f413e1c6901f4f167c42c4ea0d5de8f49e4b1a088646134402f3662ad3606431e34049266ae816c12fe4f82927a1333fbad92a390e1697a13dc5382dd8c8d757b6fd706783f115a0f4e9bab74735aac14131d769f52f7de0d79ae8e0f694335434ce3e1aaba5ae0b80ebf95b3d9e8d24af5c84f48078ca64c1ea21f36775edf0ed882d19f939b49d2f332a45a265d9043e61784e24381876b82f26c001abb3034b1252c98608f39310e281f3d95f8408612434a20657c7c85f06a0c06eaeb404e4a80dc3e63d12c55dcd2c85a61bcacf3b253044b1b2793d2904faa55d6abb99842db30c5e92af944485f4918f0c3b6ae87da801440f315a94fc7132d90d4f05b66e1bd8afd67dae57fc611bfcd176531509e229a385f24f0e1456f43c1be38b29054b512680de9ec8dab59291ef8d21843a61ef497a34859a55d3a9ea8ea47debb8b6b8f3da73aee78e25c53182ae274090562050f469b7d880116d465b173d40e1c4c34308700b5782d6de6c2f725c27145c4b9e46f1aba14d3c73f8fb879d957783b0ebe0ff0e6cc71749dd920456e5c7160ef9a8f0970eb650ff35c4f796c3def371b80d290c03a7e977a6f90f7b2874acfa6bdc92c1a89ee1eadd42e7d4d67d8776686bbae91e5801dd88a822d1b78dc52d37b15a1bace7b67a8249cbb69fd03f5b290bdbabb43616d46be563a4dc36bedd5f23f115637ccbdf102544d53d202eaa0c358ea3eda8d2e558862b3d9ed11caf72a6bf2f6c31cdb0c22b1b0687e63fee5a50b0f9c41020913422ccea27f0e85665a0c4cd51196cf688d034c416613ec22702f37bf1222d230515e3f1baaaee1190cbfeae93d6956db1812729f23a75b8bb5b602bb5e289a8215d0b38a7088a4d12bc6e7bdabdc4e1da06774c1cc28a12bc108413847a5b64a4b768d0a35455e4005169f117b4e8d058b67f27f66877c6acab2846eac36a553495728808d83490bc9100c8f4c137b5b184e7798b18b04b004fad25a992ea1873189f395257c5b5b1b7c52f312c90c938a28566824a294670165ed28235e3d68a99b00e09bacf5e46e8f749d88d1dfaa75abe47bc200887526b68578069500299ff1e6c6aa01b7b7413870e9d01475f72707a5a2c9b841ff488b4edb26087bf8667cf9ee601aa04d436b2ccb1e313cfe313bc09ce285a3d630a5bd861494a9f5e68bd819bb983f688242b481cbd52f0eebf22b001a665ca0dac4e7e915298e524b9e92c27c94d673949eee6c66745a1ff43c18c88632b5617bb1a451e10af3addebafba138a8b7e1ecdc44cf04001e2f73b2cf59342a6f00a2583f28fb1494647793dad804b2474e3c4c3868ace76e60c346be9d7b2480531351d2904f1e5db85e4c36fa6a8ac23922b04b0cca0c2e2151685305b155598e258a34d85ec0f960cce2abd6bb51bac134cab210f67aac2de30a33947eec294290523b83f83b1583d09e4e56f0a314bda11c19495552f55457a4f268fd07fce422e87920a450218a2caab63e0759c5d7e19f68bcff394fc3a345732031d781059067cc3271817f93f353111fd6f46943b1ba2bba32baac821e4c56dde46d9d6a90867c5ae1fec81e4d4433ad3498aa28a2538ae443afd054ed07b6e2a91542b1c0d930b5a3d0d83b2dfcf4b6508cc6d09127025fb91e38cb157124d98673de63de54f453cabe5ab73b495cef49af532d9668e7b4a8fd8ee2cc36cbf53b2b11e7b00d81dce57dcd4fa0c87b5a0946d192b112bd6447e3d3edf2035b7a361ba3c8c923770e7a2dd3116d834708b06bd33ca379d1212af446b79ddffdd25b1477b29b036b4a5ca60afb531335a921d111b0d4320a26561d464ecec3988f89f68318892d19cd812c6f531d1d70e0476701436d1c845adcc0ec265bd72827444856861c62ca051e03bb353eed058696666d9745f36b5a8dcc58263b172279379d17582e39dcfd88b63a78c4f28fee7cd4c2e3accb0982e19953b189f21d6e310bdd55656a75aa87b3f9f6be63a38cb67e3007c8ac80f0018c0687b8171b6f235a9354fb2f234c6af76a56c1dc47775f6d0aec2e771d5d9ae8e5aff7e885aed57e719d1610c5dd10d969372972a943859edd664fc83ce475e8d00fbe0c91aacebf1933959ee173fc66de3b182b53216a22d9cf45779bd49ec791a9e080a751494994cd31be0f235ce71758d33c20386cb5d19d73e7d4f68926ac35bd727dc2f464ab1010603c8d874a07aebbeb9750f757310d81101806d14dc93db9532544c134b57041fa25b16afb4ea6ccbaf45c78ebebb246300f67110c239b51519431e8c8605f3f9a0e94e50912e980d4ee35a4480f7b029dcd7827cc65d50de6adecb73feffd7ba9a97f5e7d8768d04e9cf6ebe1371e94cb5e9a1e1c1dc570830ff6a0c777aa31497b7cd6df36e0889acce209f3ea68c861be9d2654de1acb255c87bd0b4b401f661d5844399f68a9daac6963f661bded80dc0fda2d87e6aaa47900faa85fdfe8e01b5ffe1ff614ef0268b69d21f19695d97f9479c5fbae4c04a232c65020ddf95914b4cffa8a82dc20f0ef2a251360e8b70d10da3187f6734daef515375de1eae3ca87b5d278cc579dd626ebcf88225797c53dfd959a10383d680a12bc4d425c675e0d04ed7c989d01ac08ebbf7fb958c3b521b231d5f6f1770a25534ea41d189def92d9f87fff182d196d8f7010c7334da22ae56c7da80af11becb1985d408cd5749f95af385872c9ca318743a08cb83d8a1158fe4771acc7523830e99d91194864d68c6237d828db1a6cfa31c764bf3543acc60905f2eb71fa32b13469ea6aeadcd0b3d5b594608e727f56c8b41181c8ff887fab8e9197e0d682af8a65d47b42d1480a344a45750f2e16070d0343fb5a184795ea23119b1f413a18b80d2ddf7b838eebecd9ce0eb1ec3df96419f31391c9e464e5acffa4d59dd66389b8d5375af1c7e066a8c49be6b947dc6dcf93ee895500a178284daf1ee6a9f401b97c66af6f21e7bdf2f9f452ac41dc795cd2433446766f464c6931c6aab36c661d1ef5ee687941e4da6b18cecb5fa9cd4b95af9d0875cb97b3abefb65a8301a74df93e5a77810bea847a87d75b2460f181c30a1d6253567708330b922b13ae33128b8847c4042a0d8e4bf322e5abb6731d6eb0bbeda0f1a179dccc2c56926eee02fe995dcc72f6ccde00f89c010e92a3d7b9e0f90405f782f301221480184914042740955791586d40ae5a3ad29e1185ea12f6ac6c95ad31df6a0e7bf15d2da2bfc30fc169763eb9d6561bc0af93088e6c35877fd0ad3a44a0a5901cca7dad89a768a44c6ca81f420c5a31339d243d0fcd0b72e130fef0948a775a47520a10f7e0b450869821a7533006efb2da9795fc64001be28a794796eeac6a1ecb26051016336e80d451ea166a265aee987493417e1d79739dbbd563f3d6d85fc8899159ad21f1ab8001ee7c1cb9301c1cf36938e033169274250347c475fd1930157e0d104cbc441d29b02a9387ee521bea40da7acc98a8264477b593471f9d69aeaeda50c313c9d7217e573fe4e778f84650fe1521d46e46d1e922bb5ffa2519774d6c699c597f144a667709238d1fdf2e98989aebbc0adfa0bd4afc2aa37880089dbd0423c452fba12aa515ea02d31002d2ff237035bc3a407173e4f9671e39f7fa97b04ba6b744dc7f4ffef1cdce9f68055fe01277b6d2823b9a118879d58006bdffefe0235867d6abc1e3dcbbcc1c3a5d68e3af91bcf853f0a0e84fcfdd7750cb19d162326b698aed6168112438656f1111aeb4c985d8116f9809d5ec3a02c97c68ac212c84b39fec809ecc78fec18cdc5b5166df8d928d59ca8dfeb5592d5bd902e4216595a8e79a730de3e1a7cb022a4f6e2b9af9a20ee89039a6f9f82daf2b6c76a9fe3cdae5c68e97fea6d0b8981b9a984008fb352aae6cbbf035ee0e8d20f499ef3e20a98d6507b48a9244b023cfe7c844872455fd1f36a5ea5ac4d582ea5a99886be790e4b07b37b65f1a013d122f2c87cea5521e5dd7a37ad7777f416d636eed9228ad9e4e3714a4c167787d0baa68e3b936403931f06e0f1c7d9fcb407e51e3b487c018a215d68df69cfb5e13f9c6e95077c2a8f882eaac90500e3054b332a6db5b752a51bd0d74d0e5dd31dbe280a987982d3b237b7c3e22b42047d5ecd8788d5dc3d92c62e07f356c4ed9285ed4effb808bf47da6ab470f7a1baa5b5345d48fa0413b97b5b42b16534b7181c2941d98e48808ab1c1917097cacdec81ac54dcfcaa725a1f27418051f7f1e05d6bad2751a0c6a809eeca8ec1dace01c1e0411144d157b37aba75951a4026a76b7ab96dcd596cd884850f347b42e59d9863a1a4a43172148c6ffafbeecbb7f1ca611010c7ca286362cef83a8c67c4ecfdeb002b4b1054225d1f876e93ce767f53a19c33786c274f16334746ef2bb81e62d0b6bbe0170eb601261fdf7ef6b574d2dd999729392807a92dc9aebf5d8afb7cebdff7e3315675767919caba464f14f62cf393b422a9166a8045a5265858fd499be8dc89b953ec9d67d53a06d53e13bf4bc265601b0b5021b105427c86ab2245779c10f2f63690c163b04ad2e8064788bc7b0b443c0cab4a9a8e3821f3c62d10e2335c1529ba830372e101543c478003006f3efc1f035a006a66593092b24a07179b065b930eb8c2d118e12680ae8d711ae52601b8380e8ff7cd1520e6078a99c979c55a551534e737e601c3dc1104b139fe574f1b73d6f7af0a0a1e5c90299708fb30e0c1f58f404d12b2f7de5b6e29654a290569062b06c906a81c32339e0cd743958a991386d2178f7a1c14bbeb2fa869d2bd1d337d3e15fe4e16bd5f1cf7ec9983828238e8e47d35d4ecf94d9d914efd643d4570e8d4914e01f1108ea3d49dc8d4f20ac73caf10e124e98a15423c81866c815c1ac6126ca203b85a30e9e1c2824909aea51e16481c41063f5a9534d048638bcc81bb400a16278a2a0b75b1386154a18030ea6840f49ec8e7be9f2b0d931fe8006e12eadd08a2f80e2038185ceaa752827e736d66dd4e4921ae981146e587df1d8429e04ec6ef97447a89f2fbecd4eec26622642bde8d712e43132a11387703832e4312a420840cd5dc5cafda0871a7077f17fec2da83eae2823bd7bba4fc6b462bc6e8ddbbcdb651dca27f13386e4e23d2888431d851d9fd238a53e0efffdc632f0e85b1396e4ee979906a3baa43873baa1f015eef4782a8e684f4a475d735af2bf30c5ed373ce0dea7ffd5c1ded2feff27dad83d9fb9c59d765d9cff73ccb20840ba15b5bb4a0710a5cc34af7ee9e60d06e4ba642cf48acee351116dc8f9e7b0ba743f9d3e34f2b2e8f9360dd872cce3e5c08dd7d7bc48f91ab82fa16d9b72f2374e750dfbf3391967f4b29a565414b4aef06c81f0318bf87d0a3737612894e1caaff800d55534a2925959223b337b78727ed2eb99df42a49f5a4efe9957ce7c2695fc475a6159d1eb407986e98ae2f3e30850ce9c7af6148357d7c1ad4e3f4f163904148af4efcf129d02b1afe5595806e1d037197c58a16fc0c851f080606c67b519180d293f7ed15a6b5c2450caee0e5428a2c5cc4804a8d1b230cd1854e452d84f46ab3d41eda1aa05ee6b12b7e8cf1a21b326a7c8e32389447cf80525ba85ba50c372a1353119437f8bcc4931cc51ab2184c44d08fa1575e0c9df28f571344d58a5b468c5fc30a5d28f6977cade341fb2d46edb7320e3e9f0c9aa77dec620e3f95b3df2d02cba35daf7559f7cd7a59c18ab0583fbb330d0aa34412d4ab25bd924c7a25d4aba15ed164bf157bebaf8ff25aea6e07eb886214560bdf5f7d9c6fc8685eb0bcb9b345236e017a338853c8c1fa606f9a60711ca8fafca953091efdfc731ca5412a078a01fd3a49081eb9cefccf813e079a4146d57f1209651a91eb6453dcc69f4823d288be0b29c84abd01568de842e216b230a609d6471684c5f10cc9660afa654444ee0ea1f77dcaff324307b5cbeab096b5616e81b9d814ced8551abb8a5f4612ffd76f544783f2b352b656d48fd7b03a365a9c2caa6e9e4d68f240004b02105a1f59f5258c285b41551b9243eb8389d5913fac8dff105028a9ee5289eaffc92a6ae4ec06cb8b468bc3d2fb2eff083b7a14955c47c7cf11d2d0162e71e9485084032889249250a414a3285edbfb221758eabe0695a01b6a54a4bafd5fd6c26e0aedddc05a687c964bd168714e31292ef5ea8b470d614c504c8a4a6868a9fe718d0887049249a4904422974827f1a857fe9da296b8e514850e51f5d9aed1c01ca59c25d7e1947290729292b054ff1c2e396ba8f093a4a5faab8044852545f07c2a1c813f4969d44f8520a13286a89f0a4caa3fe4eea6f4aaa3d2d124d12445c0e2f1818cd09409821e1d557f1310b43f0bba09ca2260f1dc0069a1fe5bbf1ba068e52608888ed0e278ddad866e4a67a5571f8f9fcec875648aba295dd11947d5bfbba2c3d21d755b3aa54e4b97d4adc1e3870750afbe45e2718407929595f5c12f876a8857aa0b200502e8a618b98e0586cb9ca357a37e3740aaa5c5b901aadd0d90db780e520e524e18ab73bde79451bf1c35aa7f8e1bd57f0049ae73793993e37206902473900690c4e36775a5fac76365a50422fadd0005e93a2916e706a8fad3c49b25bdfa7290aabfc77823e43a51e80628282809939ba15e6d953392aaff8d949ba21b2848671ca17e375382b830a9dfcd15d53f42ce419a92d4abaf9322674baf9c4bf533ea97c3a53a77eddf49b13efa5d87c8fb5444c6b941b97704e1eebdd3293a3d57c51e4eebfa1859dcd9c568675c5d32c3e841114067aef3ce5b9bf8180a35fe7f2a38c0abc0f21575c8a1bb0580a1faf354c5f8390fb0c089c816c917a8ba93c839a7942425505f242551fd09a9a41ec858aadfabb290e142c6185950982c3128a0f48a99a128b0f4e00739dc2043053b9011740419239059638c2e4863248d81248612129be0c2defc04ae86a2549aaa612a326cbfe2adda91840ab2c03e6fe6bc96bf7358a82295493af7c805a84a515506d9b5eba5fb540cf9e4c5b64217da42f553d19c61da7e780d6457ad8f56a9d607e72c0b5b451b951045965d34eecf92e3a44b7777e8f5951d1889269440420dcd0e1ddc8c4c0e540ccc0b3d99362dc32e2b2873b76484de8c05dd2a4a54ea14c8cc6850f81a7c38beaff11f0e618c525ad68cde61d8160466de6d28ba6d30ae96cabf478bc3de9d608d468be31ddd7e709da21bed14c7d8931575cb30305b5ca3531ce3c608475caadea1221c4b6bc8c8b04c34d1c090aaf0bb7e34d4a854390685ca2133936366660615033333f3426766664e33a66d66668647569415654564d4a133776b9ea5f23b7c37d234186394cb32c6694dba250949cb16a423cdaa71210e55c3d1c599db8f49392e4a495bb824215d12c98f388b6bf123f83b6d8357c8996840356d630821b466f7261ad0ec74da7e9c7073da82d4544a5f5e208c65bd9cd3729898171a738a898989d161b2882a7bfbf55844957f7d2ceeb0d8d0b0a92b7c326c61a4cad0bbeb51f151b6ad85142cf5632b104b972c15be1652aea8504b857cd2ac1a54d3a2b17eabccb2eb6334d22968a46ab59f041cd0fe1df7ae47bb01764c248569da83216afd4853f00e0b752f2cf3a46c4ef7de6d7875a6b70fad97756dfcaf17b8b338ce3b92fc39f504ed0e3a916d2b05d5fd21eba31f09e9e86853efbbf94ec3ae3b1dd8a7bb8980f0ba2e081fc5d5b83b841d9c1a45719d2af20314d4ddddedadd085128d201be2fd1ca5bd0ec8da0f8b6cd8a96d43026a7fb6ccf36eb3f134647d7cfd10bef7478f08dca29af4cd6f4006ff874c0e62fc359aa6bd11aea653fb578775a86b3b6deefe296e9fba23f44f96b4b6ed647a7a3a9d4e262f4a6fedede26c90affd49dc8d7070572b4d4af94638baab5dedaa6657bb7190db6cc53eeb6a3a0ec86d16353d0574f9719bd52c050c61c96db6fe90eeeeeebe8981e9f42693a699b63f994c26d3a6f126612d9dc8be2ecc54b8a2421696f50484d0870d0d1976492965747f63eeee46096384bbd277678c10c2382d2bc618239c1e34e1a517bd0dd8ee0e638cd0b29cd961b37b847257eeceb676b73abcb453137ad553133aeba909b5eaa90995f5d484c67a6a4261c576008f6e60786a428d7485de873a3509e3d484723d3599d6ae84d02184106e53c4dd6708210a15c3ce652622deddddf56d082185d04444f923338410dbc10ddd0b21dc192f58f6bc306d43c1c49665f99c13c6ab2f5e798c715e168661bcb0535695d0aa5f0edb940cf59c6f52326e87515a3e0750e103a0c2e7183dba38ec0ea31c40f5ee030006bdf8b5e90619b7c3189d80515ab3afd4d3986158b62b108a8e6199b6994c9b966151a29c47b0df2a916437830c46141072b4809d22170cd0034a0f8815d92d80cf18ad091f32c4ba14f6ff82e7df4cf3972d6221c7a6aba81e141f1d691b8c18758246c8431b8450f552bb29f79847a1cc8dd50b9b2db3027959539beb36448e3d945ed0af1ff5c03529f662da5ee2126b0596f53e358312836aa0066aa0b5196275aeef56729dcb93a29fe827b5bf95a8fdb5926d4e8dd28ce3b0ff54aa8b8a7248879e6328c790cb0cc90cf9ccd0cc90e48674480d2c4e3f6ab381eb901c18d98a5cfd329bf482c2fa35f4962706062533c3ccd6cb88c3e5d85eb01e7a9f67bd6f2f58ef1f63713aa0d0b143e3f51103b383c687f5571cb5975850621dabc34735a80d062228543d3ae2233ee2a318189a9cba3675bbba3a5ed64717f5100ff1a0f005ab407c1a488d152554364b3d3e50420368ecf722b7e99f93528efb7840d840ac4d3f11568e304a01111940b06a790db438fda62628bfb43aac9e4c24943054c2a609e9e0c2b91d6a18b73a5b2f719dcdd3382ef396bcb1a9fe4f5bf552a7fa03bc3d29a2848204aaf762f3eaae375e7653567c775ecc04a39c0e7f6614107294811a90e00c3218a0478f05ecaca05245937b0e7de078a2f40ce92d40cc66f3366692de8fa9713b38bba4d4de0514e3f5c19b69da130728004e0048a8fb399887d5e9269640a20821503da8b6d25634b75c3adc1e954efeb19264124c8a499ec43f9b4953a56cbee374d017fef96175b6c7134f6ab312ab8487e0299372fff1142059ed256f9cb4c532ecc7ea1c44197028374884a85801839fca54883e1e82d26d851426b59f8384503f4dc78a535e6cb14f4e25dff297d929239dea1ff20dd860b0c00d0c22a810562243969108f9f102f5f7a7d12302238aca2fe8c4810b31c83083100ad49081213a330ff34203a8df60094850ffbe38d4db50a4ceb72e8bd6cbfb8ad4e911b12c9969b353f22dfeac270b908c80220d2b4eb478920495681a927e80040a78d89244139884cb4048130c80b1841115416812842b9848c186111b4cc470620d248400f1c30f49c8c0082455f400a280034656506182a48a173e1cb15c8424c087329a0042182be0d2c592951453b4e0063e98a20d1228a1a14f20e488244c1c218927547c88021a21640fe06408255c840832011727a0810a254a48280902154a92d0e08382259450e1811247803184225640858a234164e1228d2513085931983882951ec0a0c8054840228c28811069bc200b29b49c20484808beb8a2882094a44811c7103db898f1060cc4f8228917c012d0e030f021e288113801c3065a6c300409d68c234b5c2cfd18610b25480410051180a08549105b8821040deb861855780843491a4baa04e1038510823072620848984802e2075ab43cf94113406d4020217ea58ba0000c88a83082941f24e9e2071a14a0e58a22aaf841891668f8e0248c2cc61072038b10b0e841488e13252e58a208662019e3080c80e08511526c4982063be04188290926ac6cd10650145b4670811ed810838d2e9ab0d205dc81060ba4b1c6135ff8c0881fa8c01fa22e461881134320e9e2893884c098e10334803801104578720447a2a1afac68fcb3eee0185f79b71e6224b2f93dbdf90be713b11ec95f907de0e7e767874ae4890e6bb46d3f80c4cf3edbb2bf280a4533ce7ecba2f6946a262de63f1fa26ad2b22ccb36ea43547703c271d40bbec9a499506f4299502614b05098e798cff43d3e44557b98eeeb59a393e6c137bde94fdde743544d2693c98442994c7183db8f6c7e6f2f5c3fbd0ff3bef904f5bde9b0465920c102091643ff036a5d98c090de137ef893e910c2223103274b78320e628ae5c1cf1b423c996bddb11449d418e2c9a0d65d6a8b2d929278b2a875c72d40c114209e4c5291258927b364f00489279b5a775b032a4b4a3cd97584058987a734e1c50f0f0f366ef013c493695a7738f070838b1308f1e8908038cc38ea8e9f8881258927339d4c8b832f7820e2d181b977c0e84bc72d4effd545ab0747538cb24081511ed103a46959db46510fb8f8a08d6869584783802e2650e2b9bc09e795ede27095a507c4610996887870c05e07df020a1b3c51e0fbf064578775bbfd981f75a8564733c375611de6d1641e0eeb36d28767c76517df068e238e388e703d80abd2860d70557adf90ca9fe940c44696656da16ce4793d5c85cfb19bdaac38b8b04cdbb80cdb8d5e934dd08a69f909422b862855c2d1188d9829777636a209307c406e87bb351cb7cd284a77e510b2369d53cf27c0ddb9d07b77f952c521329afdadad4d33e807b50dc2fc24cae80e61949635e71251c66b892ccaa8699b69fb01a38c27ea5c92eacbf6831a71ff1d97f23dca0823e5bbbbbbbb7b8cd11d85ca914346ebfe1d87595125d21efb48ded4c8dca736f36c33582f3d1d98477a401a8ff5409007ce00792ccf071e4e3cf29ba781f8349093f43eb60ab28c8fae1d3fcb651f8ebafbbc6575a67553539deaa8b6c42955f68bafd18eb4ed7074c3772eb0692cda2923b79157d4cfd7a852ebdca8bd6f4a47a2eed15897e7be05cb3c47ea94fc1bb8d6b441e75faba3fa3ae9c9cf38e95272d771596ee4479d7224b791cf21a9dce1d4326b33de7eccedd4e579964ec95ff8e27ffde746973b12b770bd7c07c2fa08c23a19ab737a799aa926d4f99f09f573246b6de6639d6f2a85f9a66efed6cdf7aece6e4a78796f3ebb15ba508f5b0c40be5e97273d22716b92ed0973fef535de151a80427a75d0a7f267d923de7ed04c3a9b799957a8bb454f8bff71ce7da7187a962ba46cb12744b80ba1e57dbb5d5bf1ad8825763206be5eba862d6281851c1b165a25af5b1dae62bf7fdc3b6807a7606d3c28aa7f7b1f074d51fdd963a11a36be1eadca70e48fc215fa7d0bc9295da9bf05bef771e6284986e0a46e1535a0545ab78a1a4c5eb2f89d05b8003fa5eab112ae842b6f4cffad50a1013ad8a9f8a96eb34eeba096cd2abbef84785ddfd74551f583db298b467abb463bc8e0132d84736a1aa52814c7d5d460dd58ef1fc91ec3e6c4aecf300cc32e2fcace8d0efd33ef80c04e6d3d7ab45f17e4384a23a573660e5f5ee23fc7498ea3b4b720fd832bffc741b0ca3a3a05010f622d841042ee69f2f5b8c5e11c08dc8240198ef4d0c3dc82c4da417871f883481396b905497d2f7535de80f4e753e9acdd11b8de21b005b1b8a769ce2dc855fb3153ff4e7b7b971240b881b833936c4ff8a651f5cf948e3ac51c7fa7a7b84e521759b1626474c44895bd29de53b20d0d7a94051a2d8e3bacb0973ad549f58b493fd19b425f2b55579a464e036d42e6f8cdb8fbd1db6e7e3d1a0d4df4b679083bbdd33b3dd9602a4545281723d088a6a547bb01c36124dee0a75bf5964fbf3c73cd373779cd4d12ca35723a3d549af7a532effb79d32b203e72c3477ae5365faab9fe9fd863588f8afd54d82b20d6a67b4ca18222750411537ba6827e6cd4937246ee71b3d2d6170e58a866a7fe4ef592fe66357557e765b33b6108ca157ea7da85cdd3bc3dc23c2ee297f4d88345d5bb1b6095b280e9dfd3b9d62932856a118f6e117b42a4542a134a370935a035563e6eadad2facc88208566441844ca36e23ff8240e651b7e9a30da4a4f7a5eaf452bdc2e2cb6b9b677a1c68b68ff9e54169da9bbc1972d06c312f01fa315e0e5166403dcce710f3f42500f3322fe3e5807a985f9e1c9e0f8fe66da7ae98a72f04e5c1fc6e37cc80fa180f87f5d66964383d4d0496e7442301e679798e89f1a2d0c040247b6e794eb353dac34e693f517f42a7e44b47bb1adaa9fdad6114394459568484b32be8062ca22ae897196546eba3c622caa9fc1691aab25742d110e88742094515859fda99914594199550944384d3241455f959e6610f49d0506bb696eb4542cc0efbb30afdaca2a28f24549effc5ec10634d656f0711e703fd3a24a2da21d5b68a16a78b8aea0ea2da6f7953c3118a6f64b3571d8858ec7657594d8d3f75ec6a3e751dd3c7e7d6c7f571b5ab6353f2bba3914f5ffbaba36908d0d79e467a12a0dee9b73f79df1b10afd973eba3e31667ae0df53aac5e3c3bd565647bc2e715d57e5315ba81aca8ee9baad0f9d6cf494215da9fcd4e5567fcb2a2c742bf999f3ae7eff8667e80a8ea2ecef456e8421b7a9a57f403637352ca71524a2271c220fe2e6ccdb2ae571563c056f95f8f6aae7b1699940f218c0f25b718d9ecafc7874a85d8f33376b9e60e5fa08181c85a709df7e20e8292656957e6990cc228864dba3d3637aa8566270ec32e6cce180f9b260cbb4c461a5719c3b08c2b01eda10eea24319fa452a53ea69bd18b8f03443dcac321c687878862a1eb36fde3360d610e51e2e780fa98b760fee5ade7186f3d7a33c078ace4366dcd77c16d3a8b83f6924ed2a9140f34ebdf71ece32518a553634c4e314e38b909a184ded7d353e34b965f0fca22c24f1738d660d27d2c54668bd9b22ccbdac5014195bf559a208bb0fb8818e91a7f43f50589c0f7d8f1f4a0cd0ef4a31caaa657f1afddda5d407f865008cad579069902605a9096f5d49a939e60eabe331cebdefc62dded07dc3ee2bebbbbebbeebeebebbbb50b0771f27b793fb3fada2a7695996e57dfe2678fef5f87acbee28608c31c61d87be11c208b94f10638c31c228658c321ae90a1907bbbcbb93fdca843ce82ed1619cd082bfd1dd6165235b239f56a3437777778f38cc1e6ddfddb5bade164f365f814564c5ddb2dcdd1d721c990e441f1bb1d1044e4c9c9c9c864e43a7a1d3109448e9290514456914fa510d0a3d39a10068666666f61ac2ee7e6fc80c2184104208e1f880963d9aa1c70a2a4835666666786a41134db4a00902687035ef144e2d80210e5ededde55dde5ddee55ddedde5dddddddddde5dddd75dedde5dde5ddee6ee7dde50ea6bbbbbbbb97777977bb973b14773218972c3b31337b4b64d98932378cf0c402fad10a4fa7937682b0c722aa10fec909d5e80b84ef317a8f738c0e4ed9699fa0683b8cd29a4bd01f4160c5b25d6d777d77f74d272e49dc7af612b3eecfcccccc3da9ca71a9bb98de47eda336470ecec11c833ae590c9212323838a81919179a13232322719d3262323c3437323a3ccbbbbcc5d8cdaa44756c7219d9cd0130be8e7461857d709d5769999ffe4049ee052faf2b27099999919c2dd5e6f724c9685ee4b787b77bd440d24468a2aff62d9cebdedd416d9ae8f3e80648efc51c697525a9617bd49dc76fecf1dbe3ba451a331b22bf175f7ce3b0b08219cf3087a84705acfb1658430423884eb7a0b7e8414fa67fdfeb5c120fffab9bd505f700f5adb5a2b74a16b117123193b2f468eccf303b399888dead5cf777ef85da815df627e668bd9832e57c2e8459101e1654298aad0adf02792e81181449a83d07ab5b4535aa7acd59a356d6a9dd2fe8a103a84fe9de2b73a6a592f3d78059e93a8340eb814356468480000020a0315000020140a8744229138241c14a6513e14000e7492407e589b4a434910e4300a828c4186186400000018620cc94c4105008ad182d181edee2e2dd3951f3bf4454cc271643f3c40972cd965ea66b66760fcbc988141e1b781f113e2c03cac2c4921ce1ba18de22e60c78801539405e2b57d6b16230c9d9b65743ca926824d1adaecc05c5c3b984b26bf4e8017efbc865b3a42f5ce5cdf3ddb4260e34cbd638b4679d91ef77a42473a652aff32d44b1f952fadc455ee896a6a9f5fbbb6504bcb58374c8e4de4a1b4bc08f825b51f4056cec801d86f316c7effdc06cbcc03199d28a3cbc865a5450d11bc3742ff4a88521528b710b05c90281f3ea2941179d2c8421f7c070547a0da5a0dc11ab52a08a1ed118c01f5f23a16d763ed0ff460191d0d59b84897f5530a5022bd7290e4819f5a9ea18288463d31b813b62e94d84a4a7727cd449b94dd5f8a49003afb565f0cf83bc49d90abf94b2fe19bc11bda17bb636c88b80f70add6ed67a875dbde373ef42c9d419ae635b4b42dc963756b40864e38fdf4885b94adb326d2fe4aee04139c2a9a64e98472d292e381200d96c1c8359ff68a0e2b3da6da1c47dca39471b677061b122e7b06666cfaa2bf5a06801e3a47118d57e68c5da6b4a17e93348335cb3cf14488efc96d66d6e26f8eacc4217ca4398731fced0c9a564d4000dccee85bb9647833b382e3b10d2a6ce789c5a6b59abc478e482da6a963f877ed654398b0c57c9352e401093a3858fb35febdef3fa1364bd6458ca569e7342183b237e2904f263a0928fc1639bb4ac2a60726fa2e8dbd9dd2fe07d520218cc39d270f44886d02000804c3a306cd1a9603de69709af9c97b873babb868303c0afec4792975cc93fe91fbccb08ca8b5d837e4961d90613d92f2e260e840e489e956f0aef035c2a877306b631bdf2d8942ddf4cf14e63446f44832519108ba15574555738c36057be2155d604e7dc4390f4b034557f9b2509446037f1f4da53c6beec3a6d064ebf142bf44677b442048d77a7d0c280819271091e699781ec65c19d68436aad2bfc2852a0a5558ad78781a9953cc8a03c4aef758d5f4ac50e0e5aa232f4ab53c789ab2174aac53b1ab360452ddc00be5678de7965e3af453731bf871b21e602cfcf3eb790e41b87b87f632fe478e2d4b2af66fc63ea6472fe2da9eb6ba328b36a44f837bd8ba6911a9d6698672ddb8b9b0c4b1df8fd9deaa132081a6077f3d57387a03b0905c70c45150f9ccef30fe93becfc241f9ae279544ecaf467051846614e9ae51c90af5c46079fc5d08e39af54ae5f7c608aef7a7ccbf0f422246ed44bf17382bc984919fc9c21cb4ffe130d5c3c3938df47a3c6b13822061c1ce4e1e2add91719d587315301a7ce879d1daa8c5642871d1e2d2e17ed70acf62eab3543fc659950d697b4d3fb79e817848e28db61037e32e2dda1df5047421d56a0393fac786d4921e3af3e272016c991f922832b9587bbc3d790201b06962ecbbbce08b1c89155ecb2a9ff94c2159ef6925b914cd49d87a6a6892004c85c0cd2c7e054111a16133fa5b005b42bf811a314701d4b2f9d5116e68bf74ab9e788955e42370d1f2e8830092f75bb83c60e56c18f80f5e6e978526387ab96330cf53021f01570a4bc83967cf42998049eb7667281e1f5f04c6603b4eb04545bd6e8162791d9f13a0d87b19c2fba25b54b0946b55b4fca6581b0eccca530a7ae7c2a504514e4185b721ca2d7730ff087b1f9c9968c93c7e6f6e5627a0e1eb62607632eda69bfb14a4232a8a1cc29d753194adab8b8592c0a7c21d2504bd3bcb47c579adf1350d66a80a4faf84664e73c918e42b84a6ff50d01e50916acd3cd2c687d8f7e025b3c44d8defcfe667836e43768c564cb2cf6f19ac07aea6bb146895bac9937047f2620e516c66ed3b57ee903944e746f04cb8248c29fd98cc6a7ad2954cfbb3da2ff632a57850c807f3a24f19981cb0b8bb394fd5a3fa007c20886c8f966c57a50845dbe8e82257b704db32e00fd6bfe6ffee49755b472042c6dd1b9a0208996e78b556e8852b29763f13df539d6c3cb2025568bef09d958b3182aa0eaa2d94bc7bcbdd6def42d57b7ac4a1aa71dc012f799ad1e9c4e10a957b52f6593a5d09e4c430d32a76c42c9d89f21909ea47ac69b5c1259d7e6639c0831f5e11115a8623f81044be3d9fc895b998792a3d68e7d18d6ab6d14f022804a0cce90c07cc3c8687d184ae1e450df34b36df279f6ced6d20e8b7c10f13081963e02e643a19ee53730025018fc14101865057df44eb9d9fb6b00005fde24869bb31a61cd6bafee89919ad5ef6d65d6af1daaecc85a440a6145fd344dd2bb24fa6e60a9dc0e2f06c40a384d68264c6434be977f3644ad5b0efcb373a93f4a4fd5de22747cc3e06dc87515735c55bfce1d1d2ec6376f01abea914235bd250437dac42465399d42e68e4643a171388b83c56d0b308b09b3f8b2b555ca12b3a25671ff725677068d66d9d2c65ffc71ef5d0939b4f6beb87479c3acc047859f425897206989b907881729b3d011491a1991523b96f4cf322e1a43ba3394e4ee7ee54cfa523f20c9b1d61d8dd787eace2e974a1850b76486084ea60f1471e89da360f1823254f33624627762807a2be605695c5013a06572b94cf02193e25020d9c17a7a916b8b6755a661908829d4389a5f7e4aa8d63c6ed3a457de8331306ba749e899c130235e1025adb5a1e12f598aa0b0946842836d7c0609d96ebf308b6be3378335497cbfc4658e34151f1bb42219de5ef413f29d71b5ecc385ac5ce267af787eeef056dd3d8c3dae933f9f83986f6588a911aae490b645a2e64602e8708487e0828c7117505d80ff00e960fee27acbda12a241c437310e33ce88b0c681b6cdd460cb5933941a57da008580ff53b1e28ccee5dc38186c0f07028a9601310658fd5f9f87f1ed271d5d14764a3da3acbd296a2648d43df24f2b5bc20362e364324d329ce8893fefab210a698b35aefc77bc015349618578f8623bb95bb63d11be4c1a01c4d80f517546d5c523715ae0ae94f176ef2f4747a0a78ff244fa2f6bbce0c322ef0d806641b441b73f40f00859cfeb986daaad4215c09b4cec45110bf1e035e956209462c5e6392fc65af0d82ca0fb65d2b8cabddb2c6837cbb51e6299f42cab1239b1a0dabc46deed8eee90d2a5481c7006ba80732a2ca245b7ba3c93dfc826ddc3353100e4e6e238721d5c65e947902075778b78fdad8f37e4587af97306f107a2a2567a045aa3e7bc32656b04d37e93f081953c485d738fc2a38d96d64a95ba04a5b6ac3dbbf0a57848237374787e7abceced1ecee958b1457b8fdc1c66905c232d4670565108b96616086d948f2c70cdf7e8a56f5fe9275b077290bd64e653591f822cbefc7f733cc630ed637579a5a79494f97249bb99ac55134a4cc1ec378a2237b9b4ddfc0851c361827c0c8c563aea85cc3140a6bdae02f7d0e7538127b4720488ca3cb5ad21bf92791c9c074c4096dcafefa4e205d29a1644e98f89f2b8216e19ffa4cf2de71bbdcc69100ee38ebfd406143a4d12c00ec7a9cb40329cca9dd0cef9ba1afa9d83c68ea458e32c0dcfce6cd30ff49ccf6864adfa06868313d4f328a06d6544b4aa617e13464bc4ca0f9e0c1183c7373df31026552fb9a64dc9fd4990750e0e3da49e75835c55337e0a56444c6a77f7fddd3256a92498b14416550cd10a83c6ee169b97fc8f02354986280957434d079369ec04810b99be4a1323421c9dee16dc89a7b202a93343c023dcbfe438fa9862dc5485d4ff096a6bd5a3705afc806b9a704ec7136620282165598a355a050252985bfec1c107644fa247cfe5fe54043f3c06777666de446147ce1990b178f85c2260c564d6528a12cbe0906ac2217fb3876a547e9de672a0015ed970f210df2b3fdef192aa3e5082793982353862bc98531dbc9e5000c56a0f3bc21afb1a2aa04288904b7c055f8a3f38388e1ea4d57eff53e60ec60990d0a80ae5a79ccb5c971274334c2c43958994eb1bc505a2e558ea2957e006904124ec5abd3fa699978ccf1fab77e0e12fb48a4abaee46c58f570badbed7b5253341b3bc3ad8fc28489910cd06e913bcf2130d3418858612756b06053bad323946cced513642476512be3b79ac675c5f2fa894de9502b881ff85d0968a133013893436a34a37412f7ccdfd6b57d186229878af94b78537d8aab4ba1ea61c19ce850f3cfc4a378844ebeb0d44113470469a4cb4855f13617e0dff9a97c017ec6b0fc5f2d8ea67caa3142ef5745a85427bf53525e12e1d0e4c9d2ef1884f7414b15fcbdea7af906a706fde4a14c074191dbb4363fa5ea6d4903cb882485aeceba4ac0b468fea67fcd66b34d5e89a075c8f64df5142641dff3cebe2e4084c3f2fc9eb94b89f22075cbbc5787acebe34043f51b10cc2eb84dee01c0bddc1d5590cb45db47e5083e02fb62fe8226d97dbeea9d4cc7219cddde2876dac4de1f93f151d2f262b6898c0ec494fb60914fa7094823f35581488d5fd2f127682c777306342167aca46cb2ee96b9d35a03510ae43334887f8585187127dcd7050a015c3508acae0808dde29654afde864afa1e2700f7dfa7a0003e2ff0d969909739bb5b1466225be1ae0f3e9509f9c09a4d1ef081c86e7272faf13d023628f069b3040918773a38c9612f29216dec0b3ec95368ec2c8de5630bd71bca7d8b81eee32637d280ccd26e9def9645316ec8243928067e08495f4ee0833b06d97400461dedd5e189ba35ef906f44502982c3d4aabdacab63d1faf5de24c72f68ca9c9f7b4916decb2306e78571144e736a0cf3fd247775212310d51461e1213c8b0fda06b1f3162c4037b9237b1b2affa3e9d75dd384636584a1892ac9635dca371bab13491e8e4bb063194338494271241b0cef24c1df3eca04fa1582cccf8c2e1289958e78840a5d1701bc205db47888178984b152781053e83913645d6f66c4706ec398d655f366d117a5ec3a1ed745397ea1cfb531f093101e13c19cbece5aa16ef66343c1196844a78a389394b98b34df0fb852c4dd1339e605f0327e5ac7bd31d14d132230be76b44a9c4fc439880bfe95ae8c73ea251c62f01be6c5c23bd7529bf7e19b99984f796f8c104abe06d5983cb02d81ee865863198f6923e14400835b76170b1a798b86abef687e466f59bf4d405d1393b9590e8e3fcd823cbe0f163c11cd9440e02e68fa283f839253a7fc3b1c4152fd5669e0ba9921693c1636d48414268ce49105176a25442310ee47b94c8eb6082101cfe0ae5e5505a0edb62da237019ac1730af0c0b08f832b6ff516c6b781c36970b11f6f0f87069b481d31b7591c43cf653d8f5ebe5c0717d58902b2651d9292da7c9cdf625179604a3eb095598ff6d5e5c452d96f5bab8b4ded9fe8a26be766bd59a48455750d8e5d869d86e484ce283a9af54c5e786aea81093b7738d85e2f31bf7c1bf8e120980644f147d299b7cc44663da5b7589338c747f6f14ba736b08b96cc7a0417f8f86031d958060cb15f25d1c76f9bcc7a0d49d0885e3d2ca46422d4490cb2337569dab28c14412db02f0225fa8524fd611bfd35cbd350125580d8191899375f4767750b17403b8e591ea4d471ddb99291c1a0808564a4d495027ff435fd4ec91c14bf4c594e2875383201c6c9e0ea93c1a4fa8432554499da28dfb53ee50aa2a9453c71e6b3206c267ee9f9c1795c808611062bed8bd54e2f4b36acf1fc90f58199c8399fb5a18c33e1bb82c6ad194099ba990aa833e10d9f48e9604baa77ef903179d27d2cad3b73eab6a88e04c80d4b66feb8a242176e49e20cefb6c36350f438860f4c6647f43532009a16661b152b4dc20f54d644cb5a56b09e51d41844703165437884ccb1ac253eca6d3699726e63184f065673a5e2a2768494ac9a503d0984466f4feacd5ce0dd3396677ce03e88b16d41c0299a10c1505a2b791afcb2dab1b0c881b8b12138e587a275b25d5e3eaf4407b57d53f2696b2ba72154372feeb696d87b88dd9a554e2d45b6a0a8cd9263115dbefaa3244e0405a9abd68b7bd2aeecb04a0e1ef29b3ece76067de36082c784b518e72088b27e6bd599b0d23d02d71e1f6c0d1bd8f9becd8fa1b307467c043f08df085ca173b276c34d9a6f793cb814b2bc911789275a39011b917373b9e37ed3772e10516a8a722010710f51a7caf368d02664cb77bfe815adc9f83ba989a5c10c893a158b65663c26fe6ace34158b06d0efe391906fc283a7064f06bfebe5ae7e65181051620b02b2bff646068a016a0255407da004c89e9a5714d1ce0942c655132fad6b6058af31caa592dedb2f5ed53083ed68846b0a0a2973027ed3fd5d1955c24e7a03cca92dbe66f1b42c24c97b1b8e5a31bd92df3f5857671a8a7302435d456c29e8d23be26ab450c7a54e05982353406efabadc3891600bdfd5a40520ae66c4793c51b463038d1a20682d0fcc219df1a9a51f31ddd983e7f3670398137634a88039573f93caef8b3ba05b8d405b8e62d4da6c7cab57d426cc6b797df62bd1f003451e7bde4fb6b25b2dc3110f64a245c678a643f46b37895dc6c7b3a04007ae1357ac65e8444558ed73cd94f804aebf3cd5ece330813882f0a0136daf34549b94a336b2f0011fb4b735a82032382674c3d8632b447668c771279fe811636ed22193eb086b7c4a4d15e5872cb227e0b1352a9e088f0e5f3079952dc55cbf364cf58f57b79314c1a8b783e821938b64fddf0ac5c431d011d79715f8e58fea817bf6321b3a786b422e141dd1380b9550a5e63706518274641dd8a8fc40162260810926b4be265953467a4188fcbb85bc39e460cd4c8ccca607b3c1648217f287a3719c92eaafbf8b7cbfce126246f8f3535e8fe078ac99d590a6fe3a81221ae66943622c69c4c2cb2dad7dc936a405b71a963df02166c960567f7a0e426bb0c4571c681bfdd6d454bfa75153f8ad0aa523b1ae86603476dcb1e8983cced6715d059893170edaa0a99142c186266f32b7306b9701e381c6224615249ff23192e0273ba6d1645c387c5063c32a238913ea1ee0d048208641d69b902d0dc64a122f5630c8e0d6bc3d5f6a30fa28cc82f1d60fdb2889c1dfb696246937049e23a9eae52ad10a4c66a9b5f5cad7a05a1ed8135c5bda9af056d1c2f1cc900d04e21c7f3b8b62d4a4d56798c558663a2a3f469cd14cdbb667d33b0ece602a13edf10e045f5c8f8095cc9a3e5a849903b4c1e6c874c5496e7cbde8ac1cc138b76f78e9d0b5a0f28c5100e36b94006fa8a664d9324640ad1da77b8816eed475f2ab7196af676fa9f12a64980485c5957b943259feb53e41a429b44b88877f72ab4e77c4521f8adb3109fb012a3c6371b47cce50db6907920ea5e5c3fb189fb768dc5fadf3847c65f7e6fbc463573909971250547ec6d016f2889b6bba47ee1d96c9baa8e575f53a3c5f4d9b99d424fde5a257325858893873920cf732792dd4e926064ee467c6f509ba666a573cdd4acf720fd0eb99ea6be9bce9e2e1c965a2e05c0e5a53fcfdd7110ced3b1284882eefb30879ae0f346762a8f07dd428473ee065460fa5a074277a7b496f57501090d35a02ffb336a0dab849faa26f2d5fbc983cb3ca57015dbd26d4d1db2568f424f287e6e39efb878342f28674fb7e3406613f0b257fcb7f86a96e05ab6ab5ec936339d99fbf604045063f437a0f46dfd54d14b38bf51a789bab4bedef1b185ded9bdfe1377c1696f3ce71a21a13bb6c3110d38a159fafaf2d1def2a8ba050caef9011a4ad75c5cd8e6ee6cd907784ae6618e19a2b8e3d2ae64ce806517a1355738688eada081691e9e5902791a63bb37a14444db2909d5e852d23dbc6526e6ca2fd2eff651b131562d45eb878dd6078ac8b62cd9db95dc7e082d1376003d5ccb68b5ea29b6c5bcd74f916db2d9f072c65a2e438f91553de895ff6266ef7a34235442fca257c5b7fd1c68a759a968ff95c54438faf558f893e77ae9ef325cc6e21339d479e6e0668a829a9ca60704e9ff7bed2b9d61f1a2cf2a6cecbcb530e379ed840628e185f6c12f17debadab6d19c8d510fb41bf7dc4ad54c35e8ca9ba80345c27eeef8a83e3498beb738d09583871858dd099f7ef32428e4d78f4b65025e3da4d36ab717d5f623fd09bf2235d6875bf7cf49df7f7d814d36da7edbdcfc1da5a27311fa2524bc6ca4316542db07a5f028c1356c1471bd44995d68ea57433795f5a3dbaa4556bf3296bfbdc5441ae3288934cfba4065d2a6d8e6cfebe07f1a3b0f042ea5ef4d690267d489352835efa69eda77749933bf4812b652082f308eb004dd307b3f5f84152a47880eacb479a859a34ad3d9a76eb4563d507d68ddfa4a917d9ae34d236aa47b74eb3a69af8fe8888b2a7d1fa1bd8464d60ddf84d346eadbb854d93a7d3884e785aecbb5667a01b026805ffcd9582a5fa463157a77e337c02ef6b5d4dc0eb985555bde621a987a1e88e30d156fe619b2e7efcf79a0544bfd24a97318897af11d0d541d04ee362420f451a88fd93e1d3c6fd1fc2a918d1b88dfefa3930762f1efe7bd0dcce067d4fa6ee079803b38e864825a0e7ed7d46721abbebff742c31c63867d06319947cb84e81988c1f155d0168ed264cbda11d1d595692f1e83e3c83aeb8c203a7cea0d716aed1322c828e7db8d0e06e16c1e8b1c7386aef8aa30d266639601eb1ee7e4683abe91c401e94b51cde885278cb190d4a0fd2b78ab14f3c45b92ebf633ddee6ac4e92067d72cce9634b7ca07a4721283fb39059ad70aedf1f7dda2269d0e68a93768ffeffe7bd72446a3e0d5eeb336cced95b22e63ce78c88abb4673bc19cc6ac286a70f637ee1b1792ce74363f712a3fa5e7d6b17b3dcf524cd147e21d5183a7ab0694b4d12e2681d09f6cb89fd8bd96c19443f06cd64f103b843818dc8878b035eb0a0697202633f1f97ae5ee123586ce8b6591a4469b457aa9849812b348660f985bfbeb3ead2fba2c99dba9301a7b483ba00ba7c4fea84e244ef4e1bc01e03e15bc9552d3e891b2a5d8033a240f6d43417f1a61c9feceb39efb10f23030ed4848e372e92d1b19b04cae3df78840959fa2f5a8f6db77d6238b28d4c85fa843e381b1e19512aa1025cfc073b433c9f5b4d597ae7d00a2651bdccc2238847a29b5c4588e3bbf4b4be0a261f0137e3e2d1912162e80789fa969b856b43623f98d9bae13f0045e66e9cdbeb5693e4108c52dd21fdda6b032a39c46173f9f55d4076d4a0ebba2d70b1fed21bd37de07d223daf7a6738e6dac09ccda400ce04caae2e454bc37355fc4f2a98129ce418289c1dba4556eaf9445d8cb16d602f2e6fb4f174eaa2a0162af9955607521a95e557bbcd98d4804f7787dd5979714b132132934a42ffb6fa0dc8e30ab14441a4cd43e64a8ee7aec34e4a996488769301a7e24482690fa5aee429aac08c9d5c844a14b6ea1c5fed789bf8dd947e58c7c040991a3f16252ba0cd80ffd1cc4377c59ae544ca3e4f090e96621a01be4b41cf608490ccf39ae33ffad6d81b316a3e00cfce7604c41ae166da6f95cb56bec1b457ea79653508eb25960241d003f4fa058e6ff9444438ad100ae1b0a0ad332d0f591824d00881ff19049b35a8389cfe3212d8f3f93ea38c60879580188efe8513b00cbef33c79597fee92cd08ede6ec75172ac1a5fa22b0cf8b23b448aae5527ec3523e9475c3ba0264bf619065366130c7b9d5660d7271146cd600dca426453a2560a6ac80a132794fac1f1d3bcccb695dccf5308b3af7e817a3c312a9cfb29db03d8dc125b3b3ce1bced9eaf461fe5108adc3c4d6e3b5a11bec6c39a1bf9378f18c0d5cf9e41d3da0808094d92868270de04fedd8a3672d90377a7e79477753b082e7f37d22b0043aa09e03b991d4ed5dd3138dc240642b07268c00bbc75074064db595f3c58c2420c26e9f46edaec6e928d459d5f076d33a7d0101ec115300f39d058365d7e9aecf3a7c5c93e2dd31d21bfa1eda57016af26e7701df302509563447cb5151e116fe528b5f95d4b7cc87eb0847321fb40d03ad1ddd5c4ab904125229e1580e4bac44505800037676df85957f0518724c8b0a60d4d717d241378ecb0bec0b52d97d13ac2d4d186e9924e0272e44570df3cefced5f430e5e3f4bdc77ff450e638810816ef26b400aa17bf6db1893862f040cfabca950d2c958385956a4eb893844f2d82fddaf97fbdf623a3d90874e2363090c1546071f1daf971fcc7ab8ea9f42dea4db44a14800f8ee6d6f5c083d2878c8abfb4675ab450b7419370d10190e75912ac44d095465d692abcb5f626cf7513fece33f8ba51a7b5d113c1ac804d5b847ed6c01ea9bbab1b5685048ae9cce350ead3457ae18e6f2d92d689a8f48ddddc7e581a1dc75d6578343bb3a0a8f6379e5cd63ce74088d075860b11648d278edc131ca9c1408d5dacd1a0e310387a49e923a085461e6e95c0b3f51f47dd668f4ff639bd392ead3a02a12a32624bb9593a56b34569d00f70a49484a509894d52e44767e3498053309e379c50b4493cd9d282746184479d24ed716d773d1d54c9366bbd83a037c6d2a50678da522e63b7898722f2b15f6f3aa4464b652ebae08354b41e1ba45aaf66c78624b15d89d5506d4d009cbefae30219c0f9f7d78cfe90d53558d2fc700f94204673e0078712fa1dd65babcfc68ee93558a6dc896fe824eb99fa60d7b6b39d6b3017ef26c34f4984b34244d1349867d0d00d4f6029181342e19e64dc97027628f0be5fc1e425e69280a3fd06af328285b806d0fa054eabfb120184bf4df457c8ff87b77e3a8702f20c7801e3444fdc28db40d8366f54862141193add828a752ee29177107bc302fb8dcfd90d54ec36a0c75f6acbc4df56a306f51cdcd971becbdb69bd1efcb065648ebfe033dac5e1939c1ea25b3fc7bebdfed9fb7d1170e9b4d68c6c9325413690f20e9b93c9971c80ec59fe2004b5de19890e5ea59682cfbf5346f80f3340833d7754c672bf45dfed19f0dd4adbcd982aa33fa8103f4f0a5f4f74b9d3e619ccedd61dc75be68e81040cbbbd452d0284717c08579e8969e1655197282f400c7a5fecb20a0242353d88bc420d80992cfa8f9c127f4b5d20f806274f66931270eca82b2af51b57c71d155776d0c0fdee0a5cd266b675e30d0d6e4ec3ca5927499e68db4a4ad6614e032d483f4dbfee532befdc41f3a1bd4643347990e8dd2b4a16ee03a3b69ef1a47310b3e8429084cff33058c7aaf930e5985a721bc8af69b4e7cabace4e0a8f54f9e94193471f35e294a7b8c7dbe32ae74055a09a7304d9596bda4a02563f7852b10c540361b1f69a9ab45f62e7bbc834502d501e142e63ab8cd4d7d7a8f641b515092ddc5133b2a4eb5489f7ec79e3131d9552afc8c872a82fe915662de0c55cbe41c9df8bbc6838a3d7898949f1c507dda57a056686f3460ff265267b8e4f13b1606f66f6876e47cd4987fd695fd868ef7a28ac0eeaac1fb5cd1f7ab62817763ddab9680a9ca8eba04a71fde2d43a1d2f137b61cb5a82e8e0e3ce6b12c0440378ec9fc72a9a9c831e76ec8428230099149b61f9305b636db67a759c3b08566ac55c1a6c6913c015c963384925351c92f9ec97045e7f6c892a4bc15b2dd692a512d82e377c2ab594366f1728ec51c8a35cde9adc713b397c0ca3a76f70303162231f5d5faa482e25d1b8479c0ef9b07851dabf5e6d0c5a86b1df891c149a32926a12077f9efc0d4ddf2974ea77433d5562a28f78c8b422e62b7b49c841f036c570903077a404c3a3e70ca75530952255fca0af5ddfc35e1045cddab901bbd22647a8095e0ad63365b71a756bbbff11c6e1ee92137fa5456b0512e23eefa2d61936ce82f09cdbbae7c979067196b92baa6e6ba0b2342e9498ff19cba01f45d332552d926aa28a282b8a886186451f283aa4c8a0a20115095374838f8b8e480eb04341a92d70f9c221c82ff0041c44b0f53b0f028e51fb7cdeb029579323aa22b2557a4ec0edd05abe1a2c5cd2fa9bd763d57f89be99905171713d13b15f479a2e3d7892628a7309c69940dee6490b515e43553301dc13b3c76772cc48eef89069b85725415dd327bfdb99257196dcad0cc4b1ee341031200565d75bab200043300c343ab533d8f961e52d4cea035751fe4f2f071de894749cf89a6e7dc0c7f31475c0c918a0024b55a223389d9de66210673b89edb95c119d5c0323549294c50303ee42f368aecc6b729ea1b0a927c97c40ff7138d70388455aa0321624dd51fe19c23c006e15efb669b89f8c3887c7ad988797e09d6661b61018a1bf9c08cd8d84febad149e7e5cf8592fb9ca3d599a0ac13b278612371b0e451c1fd24f38823072e335159d65abf38a0d91d633a08201a3907ce0936f39662832ee2c0d7fff15f81c3e61fb814933f0f99216487368258829b1a9ccaadb6628a87bdd31bfd0016cf509c3bc701afc6fd858d8b5340ed4a547e81013b283ab93891a658499c1d3cdf66619afe730ee5948df8afeae8047fa48259ad5549df73439f080d6b8730e2de2c94598e351c55e4396d567bc1c8c2c1d38c245e8bd37300db2798bb2d7619f1296a46ab253ddc22829abcda146fba95800a0eb57af92b5fd8ec747e252e89de17b1959e698851c7e19fa195fbac3c8cfd8720e7ac2aed52dc613a9dc19e22bc0a172cb3a10aa900a34f61e1ff42ac907d230cd015001c5442898a59160b3c650ef5c0bb039c30a86833a0eb04c66131440919a44a1fd8c800ac25baaeebf12c8bdd29639e8ae2b09942707c69a57b2eb825ed5bc87ba302ebdce37046c900ad9dbfa44716ae6ff664792b5ebf298b419ca7ec9e855b8a46119aa7389cc4287c6209902d8739c3905a4f7138f3f1415935dc91845b2d233053a83df486b6e9a917e4a417decd2b72f2a9250cade6068c8d385d5a665526e3d3537a2d0aaf4bb1eb297a3d8ad74ad1b529bc16857cb244b4360a945c8352bbc65e2cc0874277801e99947cec315cd3ae49e0ecb293d562f2a38ae2352984d719989e018a8108d435b764bee7826df841270cd8bc2d749f85f151f700b046aa6d94e192edf9b9a5df94e223f9c879013436e7ab718223cf7a1394a0eb2f5248d0c8cc5ccc3879145dab8ad8fb4a5d63e9db52e0d750814ac5002daa489004705ed3689480c9867ea20ca9d66410f0e8ab4f7d82b1ca43cd6a4d71d3d2d33c92ed6de1e03206a02b757291674df38a37ab5750532fb1315bc4258991c37a328b2a4936f0b2f352805eaeee8abac767dfb601e30c1188b7470e0f552d68dd6edaa570153066d55b543d3d0a1639686c33c057738e8702009c7bdf9be3d22cd4eec2945dcbec8cd1b5b590d82372850089430238bcf6252d34f2610abbaf471b9ed29525ee27244424319606b2d4cdcd0d5437fde7d5fcbfd097fbd16b2048d700c0b281218c44790d0cd1d4219afc0c8cf3865f104672ba41d4a4afcb436a6fb73eb2eb8fa6f650caa0539d3213cf8ffb4a34af37338352d126898b57dd62d0de14b229b8c264f09f0800dc04422e5acffd4413b8719f544055b5b57c975bd3ad519e6eed4846ebb83fd24f5342461c0e027e48a947ab59cdd224b9c00ad4d81d442bf07efa6eaaa03edda115d11931194a4c94498aa916d3b430e6d3cc6a936870b1791bfc8227fcb4245e742971e944950db041c00d01e6260c0474ebd836ee89ed4d6f6cfd34b2f3abc754348d9dce14561905e56d9f258d1105cc3106e326e56702027b7bdc425c2d938c821409232b7a2e015812476e4d2c207670dffa238784e487a273f9b70a035b8412fdc1defad5d0b09df7cb08c64a3a0f9178eed485c2f52eae25b1e3c6ab0aaa406b10cc20d79a7ac305d3ec9a971f76e8a96e54e4d09d9a0f0a5260954e161c0fa6d975f7a523337c7cc5c7fd2e21aa1bbcb479deea0025d637252fe508fd8854de5e3ed76b08c85a96fbeebdb73e3329871812adb417e80b8d3120bf26730289eac6a50a97d3d619a1b423d3392c5781bf0754ac9d7f8855422bb4d20153a730499a01c466f67d174a622f2431a211fa2b4e3ad2e536d60dc4937ebce93a1ba2665b092502d79cef20faafa2e73211759589fe4511157989a4fe5098d0bc717c637e1e3bef49a900a6593b5163fc89efd04aedc57d9e22a48d8a2e3e1c0fdf978a3b216acf74ee23989f8e0e110d60faa913d5bbf310b3be4e09d5b4593d5b348289f0c06d654b4ccf907a119f0a175d7bc989d304669bc604e9299ccc1fb7f51292717156174a688b7b05aafcae0f66caf5233b4b0cab0809159cb68fab39ecb0cd8d3ee2bf73639fc8c2d3beb9dfc89c7691e73b64faaf53c2cc19374d31d8b61fa4f69e13d9495dae9efab5c196e6da1cddf226c70808b36258b900d5f582b0adeb294b0b265031cb16d3300d994d9c3c6b703ebcda1368dfc1696928eac8bc5359c7d870bdbc9db435527249e83d7f3b4a517da7547cf9025ccd7c6bcd4d30ccf0474428b8f787171efece3173b597d0a7cca4e6ed2a30f4c0438e4e64d8ebd9231a9c044b9a61626912cb38dae0ba3a0958047326ff2912bfddb5cb668b05af4a615cd7232abed9d44830a27dbdcba6cb006a6b65531b22921178b3c921fd425571a8d97b06c7c4f078c4ff096ddda38bf07aa45d5f89ba1c4c745f9f4781a0791dd072723264d99cc391271770f57e8cbbe53afbe870d20e6ee908ec27a799521cf188f644601abf0c354f3142fa082c1788782c64cf0da0e5d41e1c81fd4b895c31e50fe404fa2186b07d9b95dbeae32b2dec559d5497d9a9dd9752f896c296f4a41f54eeae925992b095f9fa3df0e834eea023c97c9e5f0e4db1b1e0713fb3c173e2b403393be8064b4d99abc122cb0cc283143e0d0ae2dceece0cbd8faabc95b3125b03238d20d39c55155d4a07957876e1c989431313bd5c902ceb9bf25625a1c96c9fc4c789470812a96368e705f8e5396267df3e584c83894b4fe1d05ab4cde675848c10f78eac7d25345860b029ad32570ab868e74804262ed83b2edacb6cee95c83562d373d84866c86a6ebf01e60515178c1426c991ba86b947267585363b3f4268bb4de418c979e50d6928c00774c60d449eac9c502bdd5d85cf84308a8399cf265ecd88a17342b2bee61b2039cad99a0cf9dacc18c423f5cbc14be024e079235e486ee4720b0811a79fd38207b5b48e06542b3e809442a3dc19e618e0541de1ab9a2b42e1eaf8c86a5885ca02742c5a355ac4fbbc3acca2848d22497920ca524caab274e25de10d2054b12b05931160017dd35b63d56d2b4a287af698df5e9c48b6e1c2377d9ef0c38e7ff1192a9624941db93d3ae733fc219276897c36fb04289f6162cc01389bb3fc6aeb76f5d81cd99ad476c56578ae9ee2e0197297e227bec505606f56cba9ae75cc2462dea125ffc2f13db1e86402ddcbe6ef91c92538f23a7a0d78715a8c5d879e35f812a232e38273d144e111e492046e521c145d9c6488ef582c331981d48e92c0112319ccecdd3212e77f762167e4fc1dd387ec20407267f24b26bc7c7f77b2043bf6fde4b972006b41dab6394326c746fc04727b147b525ec1206cb858f2ae94be85636d8a8320457ea012d6a613cded57e5311cc1be9aa0bfd9ecaa85245380ee8fa10347da0caa7271c0fe02b6629b35875769c1acd47e58cb80ba1221770688cea10f9d47e0b8b3eab87e80d1ca95147180208f54e8e7d33349720b994e0c97f6f51730cf7c4da61310adff11d26a2859e96ae32b1cdbd3e42a03f21aa296fa5e944c3d0a6a5db78fefd3c135efe3b40f851645e299e994231f256f98b51161da353e71b5c1fca222bebdfa7a796bd0b8c35d509b12e764d125ee3ef20ddbc36bcf2373ba9c3ce596d783c464e23cf67445217cffa4d3c87cae0a09d68f8962a1b0e79a6b7f70f3bdaec7a850aa1da51ed65a4ab81945d45de509bc045803586f3e576eb6f97771fc29d4fce9e14d5ac3fe7c7a7fb20128fd46d124c05d0808fa75d0ca9d867ac6ede4ed2c5a08e6795208d5c4a0d4a563d02994f219638466b41087dc8757f45c88438b48df0f35e37068a9eff866d1a1d5c43e322ecdc5e91177ee82f7d4966e30eb51f1ab92d14ce25061fa0f1ab5e770faafb421e091c5ee6b1b5e995c41d6b5f98e3cb48c957240307f3c034648410ad6496c15fc4ea547b94d166a87173b85c5727af04cea79040617a385eba39c2085aa92a623845483002b7f441918e40db6ce4165143001e8a6db8c680cf257315de0ffcd630184f4b2cef1379dbdede7e2d017ba0960542b64ee8dbb0845ed2a91445ce76e74952be58b73271787b4c84f70b5678f067fe33ea8669b9097fcabab60acf929e1bffa08fa69c77b2ad76b2f071b1cc46344f841cb80d238860465d502518b5d22557422d196875c05c6e70c765c1ab4cfebe21ccd1ae939df45a6af7402467ac2aa78672b15cd0b727c8410cece2a0268eed955f5aa547975a43bcf0b2f52782a9a70d5354be98328e16771358426bec41cbcb45a31d5c14b9ab87dae02c01129aaa815d796264f451cf24966a7724e9f05dbb2a5feaed51a3aaf3238bc7637115a4a6993ff04c54a9ef7dd8eeb636e2669ad19e867056905e17988100cf30811aac0ca5d1a33dd11cf088ab83b8610721f9a20505607ee77f736d255335445c40dca8997212533fe047b465c47b10bb33b18ebae47bd4cf6731f8d0ac88cdcd689001dcf39ab1842cc27f955a7ce0ade539e962bb3068f4c832d264c2cb925b94ff6adf5fc1faa1da5c0a8598116d80ac0f3a39d237108acd5394758c0be0522149533bdd14538131120d348cbcfa7a6e50608a32f40cd6e67d825cda34ee9387a13e1638f5d0a1c94244cedc9e484b933d206aef878386c96dfc5e16ffd7c4b1ceede9a83e7c78b111867da2ffbb600b5d9a7c2bc9620d1dc83762a24fcbd3ee3d57a05299019a183c311d3db81289087f4374ad0449b0ec3896587459a2266439aa8044e4dbcf502c69d25a677f549818c9936756ca8c669a8b11a07bcdc8851055a7e58760c4a7ebc812dd9a9dbc6af3e5c1e5c6b8bdfaed7ee3513af240db09319562fd7d06dc7333eb761f1004b253b88bb687b86db16f0c1f14bc5b4674767dba5821543db3e16bb2cc670b4b44a60b5bb23d38145ecfedd3834abbe508bdf055bcdb51bcff66556163e6ca7a7ef745637572076b79a43900db6e6d2d82ac73c918adac9185b5a96285379898aa750e3d06df60562d129e67c41ea30fc66cb66199a00c37b8d444ce0a1e6dccc7cc781b8c30272b13dcd64ebe0c09bc63e6979f08843fb6bd0bb831ef6e7a19b73750f175c3f07ad3b477e36c99a65b9ef3c65b225f19bb4f78ee63bf5be3a2b21c72c09272f0630a82ac740136a6bca21e1895eb5bb1b94411a86ca8b298d83f8b4cc4e2b630b4bb99d8816fac4e9899603a73ca5acba4669589a5c49ff49305bf8c067f9d61ae3d4f5b8b16034cb8bb6977dcf669342e734f83ce9c039441fc714c4e89a716418a833cf6a0800b149bca83a7a6140d19100ed72a06d06c747b9945d59894a08e24d8bfbc2dbde5c017735969b64bc6f521c700bc669665f95221766d81b7dd4425a1b27e55bcd5a01b1aa872f3b4688f609f1aa9cb3491c1adcdf9dea3ec416ab23f5422a7c903eb2f4f6863602f5164523a39dd1278ba7cd48ab7d0ac848a4ce3d2f15559c7350c0730e7f01cd2d0c9636628ba79a2c5d4648eab01053aadeb6fac84c2080ee96fbe3b177a9f3becff2d43584b9145b53453f7d37d30b998bbcd6e63ce0b1fa64976e6f2a954ef64b209f707b57b3c868b08d5f134029ffc8f475c2ba05b322693502badb856602f6192ceae9f5d7391a2042a2f156da08a05d3ccc18ae9e807667ae1b7df5416a3ca5bf9476a3764157911536c3d1454b5c8df5c787a6028a50a8e09a640f86a766fcc41319d2ce5cf663edd74155eb65c5c27efb26c72ccb9546285f8c9c8a70690962416e013859525bc6c2647c238772fdbc00f282a8d62001b32e7d9ee7abb28f6b68de4ec6dea82b084c3b9de111b9dcbf77d0c148b25786c77b1ad91f09bc669ea66a2282eecf7ae645ee98aab7145fa24604e9fc1538f807b63eb9d1b598acd4cd1adf7ab8c7397d283cf124b82ac1232a2e58c8513f86c80ad1394bfc0a4c3946f00ecdc0027df8a6eae86e4b7bb09a74a5a400911e2d8458be57d19b85a8745bb512a570849a2106c4e056d2daf808aa2573b0821de97a5912890506c2152a9a1ccb13745b0b29b796ebd6da7bbfb10c1c855de4bc63508af19d90456432978e15e2b5834d92dc8d282a24073cf5cb8673cc30215d8125f2af9213dc454076463dc6638466e5a36a81edf9a022e2202a6c61148bc6ea0b2ba944b3ea86b6bff0a3cae8f3f03ae4bb7d62ee5fa843cc001e76ce6c8a476e927f619fbdf6479edd70467867442f00842d32bd6a1b4958adae5d63ce298e84f22b9f032dc219b9e250dca5d0e64ac607b94d30a0fcbd8c44434ad171723adc01766e2cd99899ac4b47f99a6903029b420a069147942873df33faeafc50da8ec411d843e94f10eaca4ea6ca653cb82f0df6dab48ab3311f418c3ff02d3b78efa4279584767bbec2355e81ef2f318d0b35a47642320934ff9654adf4b1b828684c0aaefaa0d939e6d865f7a6c3f5ae656bcd946421b72d2c4832dac116cfd9362f83d76fd1c63df75485891786a8f1ea564035d666712f5b2463f5198a62337c3c0899ddfb828491f6f0d9ad2c95dd24a73b76ed08aa05da7cb058711bbb6c7a56a5b8db75b11e160759dba1b5213288ab8ae7372936cc6a9a268ac474877c944e880f1c74af37643be85e4a6ba836ee38a15df682e282bfbe03ee1c3ee8c85eb08d3c0fd38682d23d42b33dc688fc161f6e16f9728748fe19de4fc3a0be1f4a535cbfc72dc3780951ccb2cdb65633361781e9954102f03b42b8a86eb99c7b7cab8963b4a757b3dc8ede50f5afa4666c9272da34422b257cbedcf1307531a1142ad5e06dfdd757f655be405dce58401bc3eac8567b72710e1622d91b88b1c8e3252b9997ca62a4c79aff613e24fd2f2515c26bdc37f7fa78aee26bba19c0aaa0d8e25118566ced9126ebccbc24f541fc015a022d0400dea8a4f107fb81534b219f9c24d0753667106a64b9a84a4862d818875d57240757c04835e7916515125b54f87291f411eef73da17645817f45ed521dd95a6c89b9016aee3e194953cded56a7de7ea876833188f26116a6700fd1fc94658d4be5b1d12f492d00940966496a110a64497b6745028f12f95aa0fc8e9800bd953a390d48849aff826db51cf0c8a550dfa8c6a5704f80d86c24c0b9c8cb1e98b7e249f99ce6b2ecd7b76c68aaad1abe5ff74137a89bd2dc2a0bece4de43e454d9c84d9190690fddc10b3f5b7a4dfde21b2dfa2840ef418c08c153589509fdae339a61e4d6eada4b4518a7abe0a50e0e5c209a3f00e06b6833b020e14b0ee1909a8108f7d5c491b2bfcb8ccbd9514c3a41de40e35762a92e8ed2c992db9db4f6e24fd68862b79a0f252386cd458491c4683ec68c9c6d1e60dcf0e2ffca4f8e63fd70e0f98a26b660c3d1c2ca563cea0fb1adb3e56b2c087e4dc9be6f4e64ae805a821de95d2b891abf601ae52e80759397891412565263d81e3d0d09c4d8a62d7bd0ffeeaaf1ebfb3cd904d7bb80b461f6b796d2d16bf8ecd082348a9c951c952aec592cd0cb8858c38e8408c808f185da066f846ec351a90f18c8d9903cb1d6671baafbf80f79d6988e22bea664538fc4928909e7c55c1a88c734d35d43800d195b020fbe5a6b03530a7c5e7a40681d974a019a1bd2e1b5951fcbfda65502c772808923bcffd59f487b3bed8b1c8dcc6dd8ef2bec9c61dd21db6711a5d5001e199124b7073e4ccff57ed02191fed05d9f71edd0170c6157124ca15695a72a8dcbf1e0db5117cd69e0a6ae3714cba01c3447b3ad44169dfbdf43a4e0896c9fc313c8275be0577ecc3d634f5fe403ab3ba9e5dff9a057505d6219397ed084743936bfb056abf9609eb070c54481c0f495019602a084f69d6cf4b0655e634097e9b00d5faab2f95f3814b136733cc18b0d07901c1fa39fe562152a84f397f40849474b88dd752279c75a968ffceba79755822fba81c4b2b3fa557f19e9c6e801533661789d81e7722e5800a554d04b3197609c102e57d2ba110e163c35674292723145ea2201cebd5d99433fe4107ae96116c3c6aaa67f104383a7a66ae0c94e37e19aff0d01157e4b3c120c0e8f21417ef911940d14832fa52b862c158b09f3c0d91907d27fc5e81d9d750cb4292c95ac95110f0c72034ad71aea1bb96624b934107ecc4ee8c15294a00d9710b8e9bc118150fadf6cb94d913344c68e86e8ab2b42f55ca8889291d6996e99ea72f0b34e086844e2d8d48be56bbd2da9846316ebbddc8b26b65be040894cef9391fb4ba3a6901480b621b6ddd71f45e5535868c01a8e4aaf94231d4244915c0f9b22e0a6fb8bae3bfe12291849e08fa891925eeb9b1672520d4931dc89f43044df0240508cf5bd7e03c62cd847d9d2c9839caadaf02f3ea52db535b11057a89875f2f44db775165e358796786e646957cbcc133a47db777d9f5c37c18f52a8838bd84f4670b80321cea3948c5ff967c667ed899076484be64c64b2386d6a09b1bf02168933d6c014733886f250955046d52586eed53b0a9b05b04131405c1d513ea2a1651588b8b79ed3936b41b060d728227592a496fa0d2d5cdf28619adb69905e888996e1e30dc710ab201110ed35b7d0a06ba6ef27bb83c8f902a981a05cb552616ec8886aa4c18d074eb8dc13428eabd0dd33033f55c92df9f37f4679c093e3bc87ce46169172c8cdb3204dd4a445be613a6142155324e56c3fcdb7444b846bda146be77e3aa94dcaff9693cea22e8e72b36a6e102db8a9d9c56301b6997d31fc3372da20c60d887d2c78996ce02843065f0a95a392e609384ecc20f7e65e1213650cd43bb992d55cea151ea11a22ce849213013ea286c6fd0e4063361970c2e4a58c4d6f2c0ee7d3e15d942f496faa9dc3762ed302e984e56526cee4d4db2482c629cdf85db89311e9ef96747553a2d6a023f9de6c5c33d04f5d5103c3d18a3edee6398818496ba7a094dfcda7de3f1b19a93f90bcb7ea36791a076f683f77acf46eaf7f4edfeca70c04449f27fc55749151672788177d5294b0af77a4a1c10ec3e3ac83c5c4cd454d2f5be1a99ff47ccb5d91440b2370ac2edb81170ae946d2f43506b410aaa57f2c50acdadacfb21fe949c84d6643ed0c35b4fb1a0b090b8a7f2a226975ae233ac8552a5f5a8d559fca9f2f40770600cc27bb614efd19240d57cc1c805de6b52a769f9809112b2f29339b25d793ee6f21623dee3a95bb8b9e1e9dd87d07e640b66d3db683722c3fa4924d52cb09e15a14e518f339ed202a0f405e33c251c69147d9e296c605ff121b47922b838c807de58223210803cacf4f63ed6bc250c160ca8b0c829ac44f8ec3d46f7b49dbe7c3549954eabbcaf1bd09ea327b194231b3bb86e786d9252c2266cba36e0f3c9603fb26424f20a92606af85afd91807df12e5638460c0e32d7a14cbd803ee508c4bd8bacd98cbbacb1782b9746da1928e2beb1be9924682d97364b924198554107f416e51056ce0aaf68587d5e3299406c95943622b45838f6dbd83ee8ca2c5ff4e1590f6004df4894de68f4d925b586716bd0a883bc84fc5023766835a971719ac5f01332dc70b996df2b7452cc8bc005b5e7120ecdb3fad4b00c1011c62fbb8130b49b4d62b536826668eb0027f964ab0494062297bfa24c7416d8dff27ddac834973395df5c83e869b9d854d329c6b95c5e182fee2c0aa0160f99ba9462181342489a3002dc329bff9cb93dcdb52e8200638ef182284e52a27f8badab838f672f86406f3d570270924d30dad28950a5cc820a9f69d9d7358c8530e9641a847a9e24e1af304c5dba78e934707533497a290185c62cdd590488cec9f5b738095464f5280b78479dd9f700a35786f5f947c206ab38040060d3f5e32ce7aae9979ccc7103cda4ae7427a9290a93f924036df7237414619dba30fda24158b7d9eaf6c9d80bf5cc227de00f2b6f33a954ff662d2fcabe5a280c79393a84590ee517ce6cb8c71480d6b661303eeb7a3fc8cd8d535804ac104fcb278af55bc16a78a68a249df1dfbdbeae72f2f64ed89d3084d57f1d2314a8fbd882c4767e46793db05846b1062b4b6cad34602bd3684b80c95adc90e03403c6107c9da5800ba7755b669f6df84582a5d1d0e3eeb6b3939b800e6e7dc13183da185c1b6bbcbbf1b7540f8c287aa6218e3da4a02472cb4c753fa47422124ab067afc0c4c97af92f306d26cd1935bf01913b45c4ef8c811537283b0f6fdf46e6683419d194723a013f4bb8812342627ec84e6bf2d8bde46cdc598b3645d3b39f20be70b6d980ee0243265ee46e45cd7507396647fa8f4f4b078bb3ad098c409d9f844ea54dd3feac9a4c08132231894b94d84eb0f50988946998be46b0c1df81e0c711aeaa6d5f8e18ee153375928d1d61af449d5cde51c7eb4ba9a4da3b432fc4c9970cb81b6759f09f2c510fd18e766744bd2ee2201a24cc8752305f6e6f8dd81f0c4bcd06aa0933f98f1d3c7bc74049f07389d2c0ea9d4ec6a00e1f3eb85997862409b67682deaf6be268afed76d45e65e039b46a9e55a6e499889589a98c1d54fabda8cccd309e9b8c618ac454553714d2f6314a3e4e1f98cda1c52bd8e86ad9626a5146d0c99df4e93af485b106d808cc0419a27c3884c88dcde1271a10fb25a539357f12fb8b358eeb36ce11149192c2f27e5210f6c2da48dc66c0517ecc6d88be01a83913652af7808d9032748ae3f39336d382deafecd369867fef9d12914cc515d6f19d8c9fc2d985a9c49790a0415f3991da90ef4f6a59bde8825f9a86fbd85169f8f63b813673802b9b682347d5c441230b05990c2805c4be808fbe44574149f528433f9c765b72a11b522addfe8601f06bb31052d7a426950975026b90339bf1712876d35addd9d9a757f36368844bf6d30a287640e52bc299b8e12ee80c8fb7e0ad443298715534ee2368da5cd300dd0c91557d9fbd80e9ee90265d6f0ff3f18a7e18beaa49c06c8419f09f84848246a40a304554a8ef57cf455a06b8ef27035d811114f408d7d23083e0d5cf201416d809910ec02b909ec4dcaa2e36fb242980a0d66cc883a8c290da16fde8492797bf2e3d5859fde0ee264bcaae1a5f660455ab2dafb897f63069f240500db4b0f3641dcb19e4be9e16abf66e8d40c272d90707605cb647f8c8f9cb367b9ce5bfc451fc152f304ceb59dbbfac029009493460bb529ddd64312abf0e80b62a9c02657add86e53a10bc28faba79d465a52f95bcf2c84c1986134efe4c8def989c4cddcd3331cd517a9ac7daebfba26446d2ad7404b520db43f495e6d1e2bb134087b26501a735111eddac6eda5e311a37b1176a3000f2316f20f1bcdb466c285ff9d802c88452b260cc202d1c302822dc1c3018b5e61a0b542a15d6bc80f88c34f44e4af2e69471a571aa8a188393bbec2adaa01fca82959acf4a7bc79d77b4f2a70f12e85098c81020d4a4f7292d4afb9637b1a186d50a321747c5e43c2ca86fc5a354d7937df44ab6c7c5b311f2f9d325836be2698dc2c61b4448f3630221221cc150669fb1336e498e19bc75481b48bf1c010e59028aa7f18bda6503092ca8900aecd9f33416f24673289240abc068a93b41622798902212e14f347e7aab016379fed817ee6ec47836dc77b966299769668a93081901b678ac7c752655643780988eb9bbeb58ea5b2a9f57a44eb3e782c5f47387eadc1b305f8649c001310a068cda5b85e3d42a3b3a2057d732b6ec6f70201288909fdb904b7a6cd26cf7d3f19662a725adac0b6a885d31a4d07c4f04c3e21fc37de6a0f025cd7d67dd35e992fc45d8de9c39b647478bbbd2ccce5ac1f2f3eea89cbc0abc156d1980dd13e9b3a4bfd63375470b2c06586d4e83f3bb6c915c71a7e2b135c523c0ea1b199b0273b7a68f64638db81fe0f4fdad49410212d9946ba1ed13d188702d96dec41157055273c86cf7a638d8f3652d1fcb3a8bbcf468eac8460ee2e02a96d8af81ea07b39e4abd12e7541ab94ada3d6038f51c8c5a02378e9b02cc81093d2811f215534f60cc001009c7029017cbdf644802ffb18514ba7fde37650bb9c2867d9784815290626aed4fb3da6309eae9c9f05389a6d77c951e2f2afa90f437e7432d87a4c547a84f44bbb6d6e41f4e4021cca122023577930c5720be83334b8cef5106c98d05ca5048ff82a00a179686208a019e3c09715cafb3efa06880a218ec528ba999a3c987783b647fd49c24e3f92de33512821c844f653fea0e41f05666159d8d1c6e6be8c62b0af1759f2d59b3d051b4e331ba638e59ed81e84140d4e54c758ce24b6f8f9c4c043b891f9d53c0630742bd7b16d0d1d4b28849f92c193df6158b6c20f05d0b6f7f916785b33311988c8b13bd841a8ae47ed96f7381a97d4c115592407cbebe8718b2f1fd10eb5199e4a9f2b29f3630f85553b87c5cd9403b35bd556be29eb15eee596308995dbac1c700eed3b9019ba57278580d2ac0cd7d4d933712ae57f9f50fdf7cc9acff55338a4f9d7e692bbeb5b31fc5a3f4ff14af7f4a76581a5a4d81a6089b9321508b3c11d0c68815b5ef9a550049ea0eb5764b8eb5a18f19a20bd01d0df3c3cf31995e00b607e6f3f0c781f4c5c347cba5b1aa218df8f63704c0d78ae927e6ce25c0fcabd71c372e671c15010650780f15fa6165caeb4298882229365f21461d4a12042492688d2aa513435c124556d228bf380cb38cdefdf2cd76eca2fd7ea88b4eb7b83f066dc219340900103049486cb629fa6fc4b52aac615cdeab43dea128e287d83d79676471cef2dabca57e6d8c54f4fff3a40cb5d71ee87ee75311641d5658ae6401bcfba809ada21a947fac6d741bcd67ababce379503bbded614b4e5829d95cfaae7a667dddf4e840bb23abda50c529af1c507a21e031e14f1338a7fe29da19762042b0d383d8187bde1b7830db3abefd0413cfc7105152f848e7d9795b12a0dc9074ea9c4f76d0204938fd56d9f6eb3361579e962a8c5712e35d85c906a40e5200a5e44c705bebe694d82e2bc623af5b9287b3b9ccf9d8f99440a4a1a92b34fb13678790747ca82d95c1f1e7228fbc5b74c32a03fb143935fe538ed5064a0a790ec1ac95713a27098c1b33bb36b15f8cca3d398f7737d83e261bf2e81e52e2a99e8548ec5789b39f93780fe4f82dc916fd21bd285fc96005bf8cc1c0c6e262862354c7a7a2bea157cc1f20e0a83780dc172b7a43ba4683add0627004f9bafceec7d844b899af437dc0d9a390bed3c1653f42f4e38d029ad174c00214aeb79e9af18d64ea10afd03bddee1246115d4a16f6a84845d5ae8bdfee89640bc9eb988d09f751c1071d4fcd7a888257311159a56ad06f2378b1af325be2d8e28ea9e56c331d269a923eed55431ffca5eb4ea55b0be8ad4dbf63971a929ce5c0197dd3ec52f99c08d168b3f6aa03d300d8ec4943221ed80ca04cd3350d9314c08e95af9cc4c537b88e6257d8bbb4bf51cd622b6ee3a7c94bf1fbe84d2ccc306903b06eb510e80dc00209d161694384b5f8b0edea2374b880c6b9c62e8e0bc0cd182edc0184d6d717c81700b40258f88d5d3ba9ec7709652e570158758af6b47e1d3d4508b30afac98916495a94f64219dff302e8bc6bd40003ec7683d4200698f9608c482b678907209863959d80ffd8a5bba9521eeeb1c47dfa2ef0122533445f2a515333b8c005cc7227c34a9d6eb4c56518ff37688287b2dcbe2870c830fa37249080668f8efdc11dec255e61477b70c59c7c5e7d47595cf797a881033473d81e7b2d20d260b92042b2b9be066c7425a7b1a008c1b47e402153f52148ee5d9ad8ef3f2e4a11ee98622ca140e05c11ebe21004305fddeb78896bc8268169d9df760d38316a25f9ce36d408bfd02faed17e689020490e624516f7b032f812844833ceaa89104501f038cd0a9dd287d8aa85c3bdd1aae170bb0a4f6f160388a8418384d0557555079048f320ec7f353c0c08432a3c0fcabc3e794c5f16212a3e0882ddd63248335918ca4d22909408494436861c838005f9c857f3c75126abdb189cb89a0c7610335a6df99158b123870c26b65fb80604fd2429c17eb6821a755012d488c6cc2178fa62f065de993efcf229ddc47bf325267cb7e1e74c2e9c610e72f72c2c2634c760c89822b671b7263208db68840b180f82b975644ba5c01da7568566b21cfdc5d6f3a673109330608cf1eebcf638281af273dfdd82d81c60627d9d4eeaef9159259b21bd9b8f272f5aa3015d601068743bf5e186f7204be06194126093278d30a70b42cf7e6fe64c8056bfc64f0d9b78e906e5d9a8b9464adcbbab5ab015e3c03816969f13c9e495b1fac23390348f5c5e60a296b497f6a9a061e27bae8d1b4e884236a500d400817e40531e04a0420744a6ed5d7ead77e08e78813b4d1205c667cda20f5663db40e94abfefd9818d5e01f4e57dfd6429100a505ff7603e28c0e7194a92f1705a40407021298d3dc629a5c0296d59d5463ff5904075b15b5a94e2b1133fa8d63b896aaa56e08ebf6d3f14a047f3bb7b5abcf3cf600e861a27d10c3abcecb8a285a2524c952dcf831ee1f3731a5fa91ff5505cac795d34046291f39a4755b779f25cd19c99e442926d80147e248cc000d0f9173d5880ecb7039aadda8a5d3054aa9a0e39eae8db1c58faedb4c9a2944dd88bba7c339cec2a0bdd39357adab535cd3475c50858c67fe121be8ceefa67f4c101200cc1a030931f8f94673f85ad65e2ccf4fc99d55fb7522e8d26a3264256f626bbdb965b4a29a594016c085e089f0849808ef8501e2a0e41633407295494302845435cd49ad082924c2586c861caa484c185d0bcd921c3f22cad441e076ac8b4f46ede4dab9c42d4ab326fbdda49bf9b1a327dbd6aaf864c5b0fefa4dff551b9b0f60b6d2c04dd9c2ea5945206117fc2204339a594724a94adb6da6a2b6ada8c826e4e29ad95524a198e732221a79c524a29a594524a2967cca1105151ce2ae26dcb991b8d8868b36d86dd9d379b0a9434caf0d96aeb4d2965ce92e3388e931c2747a3914b131de9799e27f137d2f005a0a2cb7b251f6d2bfdbeeffbe6f749ef1ba1b4e7e27492a4fd5a56987efd860078dc755d87bdcdf33c6f2365128944ca1ff77ddfc79546a552a934f2485f5792a88c1a2fc079df94f493927ee3055eb29ea4e594f51b67f8ce4927a4350882a0c9743aa198dc202a968e68932daabd741549e732515121e9b85051666bb7bc8da20ac9bb378888d276afd83da2585a5a66cbca0a49a7935151ce39a7ed783a222fbd636161b12c2c95b24c16a9027f3af8a30b3f1d1c7160064190821304a5e91b479586c9960c6718e9ba4e863248100d809f873ba48d2833837a41d918f9e8f29392a6cc0cbbd1beb6be2ed9a06cfcf4e92f76d48d66e697645ceedba7f5bbea7d5b4a525ec9ac56ab6a3b6f7f9f7b4e228dbecf2379d94af932c87e43007d4152c2e51226978a8f96c6c70c6a550a2553f3f251ce5c3a7c94351c1fa5d00340ca702c6d1c6eda328eda307eda339eda25b86a93e0369b00aec27e790abbc6bf2df3347b87c67736e0394bb47c0339c7866dca9429394bc47887c34fbe79df7e7ddbf56d1da056a550279b528e6fdbbc6aa736ea6473f3ed9a24dfa62179dfce91526990a694436ffcb66ad27ce9c97dbffb3cd34692a373ea390080a3c3f5aab139a1522a0dd2a87c9e530f24a2bf5d02a85529d4c9a6e655caf16d1294501a6532f2714547df6ed1809a66671b981902dcc8e060e98dc35f5b05afd92938cdde726066760571b87619191cae82430607cdc697665b9a5d6936cdd1342a1ecd9e39ba047023b10949d84868c296cc9652c411d3c1883099667fb863030db09cf71c8eb3731c85adc37d6c97a3b6e8a7fde2a96dc355fb86dbec01b8ceaef113f6cbbf8de34d1b098d973a1d1b05776d1f5eb375fcb54ff02ec7b639a0a6c9b16b0ecdb961437c19400da8695e2e1d393bc79e393736121bb7b17fefecd8509a22a5862939cfbd3439e81d8aa403b234a84d343663fe6fc7a4bc6fc36c51c5fbf6cbeb1e0e6e243d425991e45879dfb6016a550a75b229e5a0d9020877960805e0e0aee17abbf8b75ddeb49104c0551b090f2f6d4f6d245d00022000010880c66b6c243401e0b16de1b76b805a95429d6c6a5eae168f66ef7095bd1ddcee7ad370d59ee1df0e4d30c13b193b159e9cb54f27d78ef24e85a4833ab99f50a770efd892c2f8de266c4961a40c3f6d96a3b4c8f2beed34a056a550279b9a974b074e29c7b769805a95429d6c6a5e2e1d380028e5f8f60c50ab52a8934dcdcb55caf16d19a056a550279b9a5729c7b759deb75b42efdb2c3b3389ebdb2ba056a550279b9a5729c7b757a056a550279b9a5729c7b75540ad4aa14e3635a51cdf56d16fa740ad4aa14e36352f970e1c009472885e6aaf5cef16576d16ff36ea4d1bc98d976e9c66ef2c2155360c268f21ff8a7713dc528a2d5bca9bcdb2a51457561b0609d000fb15d8dd20e9e8190d9a42295b30deb74f47a81234898ebd3395e870dde4db2650ab52a8934dcdcba50387f76d10d4a51c32deb7b5caa6d931d019b42a45c466cf214a396af64e4dcbfb76e969764782f7edef4d1b490ea7017529c7f785b93a0906a1d9e4a97341668ddeed56c7b7b20829819a1086dde30e705cf9481eeae77cd7aeddf38fabbf9de638aeba5d09b283da5a6d155ad46dff6e9eb3cd167b9306b49bed9d6ed6cd24161a08ea66d939aee8ed1d3a49515391ce5bb30943dfa0e77ce452a4a274e19022f5156925e220fa481d97f7f55b748b4620025947ad1137e8572b087c75ec5bd0df95c5944ae1de80af9b84913286ab82c770694c993245534b29120f8e428e73172df3b3066b7f3ebafef6b5f5c12da5a8b794a24c0d16fdce7aa50d3ae825d7da3952018480a12eed70e41c7f2e5b965a3df2edfa6857e7c6199246296985d20d506ca1679ec10fa62e43eda7af64a1afd3ea7307d97ee8ac3f7403230f35ea261539efb85dbf93ae917bde71241d2f8fbc119727e568a62e52371aedf9a4f99d97391f8d4252018478e17c4eca96fd4cd2c9d52d6a56bfd756bf75e6cd8b45cba3bc933cb69add6325fa39b4e2630daa351fab5000a2e011f39cd077c4667242077dacb49f3bd589958f15a865d623583e562364f8588dccf0b1f2f0b30ef994cf13cac78c698463ce33c2316b19e1a859e1e82da30cc7127ed47b6709165fb5b885d597856dfce1dec02a6789161b30748baf9c85a5c5b7169696705f2ba3442bdbca1cb53ecb0ef7ce069c8a9f558a9fbea21f6e29758c358a953dbf6537596d09d0007b16bc7dab9595bdfddeb2c2f816fab952d928576d10e52a3f9d508e512794caa65073ce5ec2671f4ba05276ac6c5bd4b63caabdf1c816f5524d9f7ce4da55bbf4a59af65cb5bf21d5264d1fd5f64aaedadd976a5abb6a8fbe54d31c0ae5dca7f60c7c542865eb1492744c0fee71de30de8cb268fa0cfd686523e5f9d1ca68ed472b1b6bec472bab437eb4b23a670d52eb569b41c124e6ccd58eecc8ea6cd46b0c76ba24d51983ed424afafa48d5abeef22498e95dd7715cf5387bbb3d7fb451a2b8c2a40b13d41f549f42e87461b3dca8d6d1b601438fae25e9546b7325e9e4cd574fafb5732564346b13d4e9f53a33f33fb2e67c64a9d71d64e6299db55297d433a5e10ab1945a5185c81e8f6f6e6626b76f66c4eb9b5b15340c06fbea37065c22615a5170efbd9925933d94c554a3561b49c7decb5ab95ce67799a463f1bd2e33cf6bb94c65cc408f284df2465c584b9e8f39b5e7750130894303665940d4f539901d20b12ef4085e8ce44716900fbe884091c431c928627a5106f62508888a99355342c5ac8700ca6ef70b93971a155f82a609b40609cc294fea661bcb0f6a5a083d5b31d9cad7c8671f6598f8ec323f12a7e4b97ef1d9471c433e3b8e9720120aa7b71e1793fd823e9f1d8ce5ac3518fbfc2371ac676fe5273fbe047df61a0122d9ca5ee301cd02fa5cba3c9fddd6a2cf4e825975405b002671509fe593d8670702ab2f2f94808ff9a35094adec3e64be0b91685fad3210580b120d240664878ad9b9503481a63eca27b0cf62f8fef93969edd2d0bb57caef748dba7413eac2ae8542c985269aab0e68af491c196f35ec4719a46b2b14155360f2ae9958f758bad039fc1116808a560c41aed5a868bd078d61811e75ed0691909016a2a2686b7756d3422950d7885ad528d130d8b7a9811e75ed758dba681321ea1ae282481751574b76a9c3d051b414eada8981398044a8ab858619861ba2f6cf36844eba8c3885062c199a6104a530a90bf428879c00d5d775eb154f5a6dfc54166c6061306d8f27ee172c7417bbf870f510be21004ae147f641e678605948c10623171df4d050ea88230c21c508b21614e8080c660323394a942980a8e8a14921449b22f6338ca1e6a8398aae3edaa774e6da4393a1e2a4da872972d0c57b5018617ae090184463c468cf1845f48cf1e3639a309f7246a240312265dbb65a5d8e76b312e38ab79c3d7b994b42898c849579f1001e97b79e33cd73c88caca3ecf23cdc4d78b3baf11b1716d0173af0424fdfd1037d23e56fea0d911fe5d0037886acec0575f630bc69cac712506ef269adb556c8cfce23a950f8198ed86d107afe7516aaa71c159a1c0c479db32679dde8ce23beee7146c1954adff77d9f4be944b87d2eeb27fe37f39f0340befc46e254ffbe70f65e5f330a2923a2620b2c461f8dd3891fa72c0b49212934655268a6863348b6642cd14e61283ab281f43cc6808b481996b33cfbbdf7e6ed426166643cc8ab11b39c4519dede8d47868a332ea27d7c2bbe5ba6529af66e4030f29efee87276fad09edbebd60b65877177bdb3ded5ce8679f331a33eefea522349113ae9327a3f4ff7a82720451b8e407eda10023f9d65bc40cfcf70bc1e0f99013eea4d8b8ffa9bafa8b8838a79344389e626f0027555ce473cdbd1ade8b8aa9eacf6dc1e777843deeb797a6b7dad1c50fd46215d0da0e20df23cdc36be2eeb75795b54a8d2a757172f267bead5e9cc93f13c750748570e4f9df65418eade10cfc7865eac03babacc192bd01385d3773e41e8e9fdf4208ff2cadd3956400a9a6c216a4f96313691057ab43e730bd427ce2eebd66ed800b36505ea5269189848936618d124a7422c781cc0618b0a9be32d9c91b1a102a8483db3f0420c32cc707d445d3988a2906ecbb0e70ea506e04299192ad217a888fa18a8489d148e36eea2383343579ef7824f5cdd862de3fc1670b8fa4aa46b8c0af4663da7eed239e7f9396fbbcc360a572e5b6419d1351c5939079bcbe86877e1a83f7fd9bd1e744b76219fa77b0f8a2190b59e1175fe1b11e6c2b9712a38ddb0531cb6ac601fbb7d210f012ad650afc857100a537b5498ea7327871419c6571ff7575fad665a82f2a5ab8336f46446ccd94ca6250d4e70eb5d9a2187839ecf717a055acfd5b1cb9ce7d7dfe6266eeef5f5f9b6cdfedbbabee8a8d63993bdee1c664c49928a75b31dbd5b0c75a1002032b5b6ce79b37a206ad7d61bdeeab476c1841e678c02a187c2a89446167ad4f9a6ded470bc32bd235759ce5acf9b5178c3d53c476fbb995144d76b6b18561b6445230b3d5154a47a643da5395018ea44b2124f3ba7e18f4ce90e0394e891c25ed6a0501814d99a5184164db913e622cecc69a79d734e3b67486f72a66e67c0328a65818a52ca0d15690bab65868a54868aa897249de9a3c698e27b696d0015a594dae251b1453e05ecb7c0b2b5d65af1756d7138bef0d78632f464d88045455ae2c17b817705e8814456981b9b1f6666f2df51ce7adcd8af7723a101454f1fbd6a217361ad347539cb586353d27eb5aa95cabe9b1e03688ca169bd4263cf9c5b89d3013d3f67ec37fba87be7abe7892d252560cb919788e7a33dbafcc833ad7586afe13892baab7872158bef6556ce39cb5e57e9717a0b4706702bd0f83cdae76e92eb5d49c6165a5b9bb714c6958f151569dddb66593fb79747515cb13c7bf7da6b9ddab0c5ba90b7e138df3b3a49d85a0fb27aec780bcc8191bfba4d72f32ba5dba90e6ca58f7c039ee339e7d9e366ef6da73aa8e72d52d2a60129a58e6ddb36dbc3b9f5550c3f6598e1e7a88a266a2fa57c87514574cb74213fc33a5da6e0c2ea5b18f9ecb69d5d4439a707597dc676b6e07085fbd8712843683991f54e27751f8d46239d11e79264395b6bd8b2c2d7b0a5ba90afbbbab0fa165cc84f928e6871d5a910f8b17aae2bdcbf5e656ca16b580303b6b926756e7bd0f32729d4ee7d4eb5cb3a371fd7b1b51686692dec57a4cc01d3003dafe3ddfd788c85e0961f9f2740e67ce1023a12f03811860ba604f9f1d5ed0ba1f428a6a244bd950c8f6778cca1787c68ec1dced8cacd51ef69cb71d9b7916fde71dce4726873761ac85aeebc6e3df30c6f5d661b8ed5e3809e3f6da8088476f96eb750dd664f7b3eaa3db76fa898431b20f4669f747663a75b5a5cfa89c2e6a30f7abea5347b78d21d89e34350f22b2fa11842edbb5257bdce07ddb2b9105761f39c47a10bd93939a704e8b7f8a0bff9f46da3203fe750029bf7a0dce69c67e7421736cfb3fe863d061bf4f3a5d002e8e5cbff36dcc216b6a890e557922771240ebd3985861a1efb06db300e7d8cf11666d9c2610bf6eb38f441bfe57bd02a849bb8882092b84189277c72f039228969b30a73ce39a712407829549084b6231bb447451f2aa5a4924a29670f6aed62bfdbbdb549257ae9ddd04b973f498ef8549e1aab39449102a5c8c950adc98c169356c3163f82b5b73f3f824ede3abe43d4356a276f475006ca6ccf8f1acadbda5d617dec8c7c916cd9aee8c149bb59978306916821b4a4d550647a3b3d84714574d2659453fc98bca3b45a15d89300fbae7ad776b7bb1dce495a3f54dcbc95a405c4b58e5071738ea4d3eaa1e2e65206913580b43540ad55cb578f56d35da411029d7295a75ce5636ba612e337cf019238d955846af5a58524cce659cfeaabf5536136f7d96fee22d0928a33fcf9cd5baad4e6d1fa69fdb492b480a86b3e178ffd6e215d1d913b2471a6e33be4b1df18dee2b1d7218c316ef5d0c7d85723c7ad1e10044150eb52a98be22f86dd172b9e48d74b8d8a5dd05fbf4412e7e457e5f75ecff2a5e2d7b57cadf8f5957cadfcba4b1c945fbf2ab0ef56f6aaebbc1a407ac32a7be6745aa804027dbd47c53b887642c5cdf5d0cae621b4e231f48347adc26c5e030b3d6a212da49d50d7f5cdfe06a640cc014c220ef9eba328fb2b12017d14637f9530f1d7c7979fbf261f7ef3f1c690bf37627f4f2a88e2af8cd8df564d060ccadfd68cf5e4af8733894322f91806fdf5914534b28afeb2a2fc1d5fb0300207e5a34acf4ce28cfeba0a4ce29c66cea8127b15d95f1e57fce6f2c5a3266136bfcec30af90a613c6ef05bab8704d8b76af5c5a316c2a8b88536f0a8d5d7cbacc2c0a8eba516da205d2f569c12407354c21a253dd465f2cd7934a12ece37e721445d9f6faeda353d15667b99d557cd4f85a951578d11f962c5cb2c063fbe60f19ba34e610d4f137af6236b36001e8973f22d843d5108ab303f3790e22624a130f29b84a2e7b7148f1026a46f707dbb3e86b1df54d753a71bb6ac70fd73f9e3f52f07dab455626cde715ef53c8fa403c3c96400fa5fd83aed96a9d5aa4296c085ad16c7b578d4b891470d4c02a6005462f2e1a482dfbcd3fbabb556b082fb734daa25926f3e4bbb72bb1b225d1d9198c499be1591e2e6e3a57542d827a31f89d379170427b898c4f11c86b98d8850576b46bb22d2d582428a9b6f4242b0ba208424239be4de9ede9a51d775428ad4376f41417bea1b756246a24f3421f49b133cfce6236bf632d9826186b0df7cb47974610e508b478d6bd57ef3564de28cba70d7d99ebff1a8fd9603245b3c6a728a1fa775d2655481bdf43ce79c73727189a848ef50e945dc13e223d7478c3b2228d455d423716e3c75bb710cc3a848f10e9611a12e9e39573daff34a05164a7b99ccf8ae7a757b4ffaeed23be5b577fb4adf594f9481b63708ec895d26a20934f6f18e37c862f008e8a3225241e99138369efa2dba44170a15e98dd213450a75c16ced41c31fb4246d6f3b2b34f5fa6108bb2de8a4cbc8c97ef38efaacd3bbce5a29dd1b8dba20b5a823d500d2a31a407a540388a3f7e691d700d2f5e515a2e2e63d68b7f968c31083fe6b856c6d7eab90256cb7091537ef6e6dab5981cedbacf5d624cee6c3f4351ed02ef8713ebdb5df9cdbf36f4de2c8d016c9d6e63da8177a7232166c339ef45df5b6fdbda42e4ba79576fb64e9bba96486bab69d5fd5b1a7f0a3bea8636781baea638c633807ea925117e7d8e5b18bee420389199b9b6d731b45e2987ef32b7487eacbfa567ff35b445d323fcbdb77299c40450cf6542374d265ce404faf08d8d3a3544a4aadddf38180248ec8427d65f1e666f6d8417dee21defa103b7a5071862d6e6d8f1d54b49eb3a651d1ea1915ad9e6dd4b1a6bdd54aa8781363023ddedc20d2e34d8f1e3da8687dc78df41bea926ebd9b5ee8ab84bae4df5498a0b75e43a4af8f3748b2e7f086ded0104167a0b71aa8be727e4a42e1024d49bf52169afa0d7b2cf5b028759a0859d2444ca0972496b59675efbc5ead9df3de39e7b41205d657180f741ea5b53ed14468e95d2973a30e44a1509e09852fcaa22a0a455112f59534a803a88309d4019592a9542aa59a2a954a05ea904ad5542a4553a914683aa15029d309a5e3e22a920ecacb2a2b928ece0a11baa58543a550a8546af5a994567a056431b578dae48126944a1311b2b0b4c898c162c9903163060d1aee7b872e2e366c541b366cd85099e1ad684c1a2b335868b4b0c2b086210d69d070df6158a3868b8b8db0c6de6158c3c5c5868d1b375e50da85a463a386f6668d39278eb93131362626a6d219131323b5e762e3c6bc71a3860d971b365e441b376ebcbc88220c4c4c0c8e97175184818989c1814346a6d59a9981918991a1b447a53bc8ac1d51022d4146a64542093339686a5a2df00812482001450209271248985a7475c5921b8599e2574cc2e33af17c9ca9abf5d24b34d0a3f778f33c54cc4b2f65a147ef615ede29710e3506093d9d5249a9b593526adfe210938279ca3129d0d3e7cbf1d6ec147009b8045c022e0197804bc025e0127009b8045c022e7119a518439fbb4e13a1e56868c6334273dec92a27b51daa1b57282ae691f579a583b276daaef31169ce21e6688fe0109f9d823c20112ae66d9b1b28a36257bdd89095e94214d2d592b910d4f2e1074c5a41b4be8012c5670f1224713a6fa19bbade2d3344725d3bf6c9499cd2c85d3fdb055461720c66ab4761864290c4e1748fb6ae3da081c0ea4b839e1d880c084f7d61cf0ea407880f9023244ec9553ebb7ea88eec738faec7aed967770db988240e97009a7343974f17a3cbe7330bc84628df40b38066ce02b0832e490bb8ae4320b00a93379018177ab41182d9d1b426a653e320d0238dd0a869846886240ee7d969b6a021a2117a4d13e573ac0646c525406012877e76203b4062f53573563e9f9ff47c06651f5be1a0877e94423d2ccc20d02371529f5d17a1473026d4f3d99bb3256bf9d00aa2f545093c9fbdeb76553d91a20a8a8a07d41315916ce5eaa14a862292ad4c841e411f950c0be952155940480586149da8c610a386b7ae520511b0f8ec15105a450983677a1ec3211a520f07b5561fc3211cfcc802226921f49db586c8916797632b87b736429dab8aa8cbe5e32a30a4cb9584146b62aa3146952a3e5c45d99f777be42e1ffa852b09e992327b36120e15491f3cf9ec634dec2514273efb6823f4d9bb1d64265be1d067a79d8faa0b411608975eaac46c369bcd463660dd0d2078a86b3639d09bcd66b3991f33b45eb1881ee76c3667e39cd55be78c48c6660729d3bc00f663cb406de2a0fab6511cf094af2c5a123c65a7ded0c0fc615d82f4286b3e4afcd40a546735a8767ddb3715e6ced46ab51f3f6ab5da8f5aad6623067a94b57183299164836d318965a8278808430748d43004255524d991c970f0811fc05c644044de41b762814568162145e2c85098e94ca2485ad10ebd7271522d68b531449430798583aeb25ddd206235f3b2d453425134ca62e2d244bc2e5acce06b500d0be1a14780c449c034410117dbb8218975055cbc31d16dd9c60f9973b9a2ba9593cb5b8ffc399c81e2ab8fa3afdbcc6846073dce24a1af4c61a60b499c4bb512baa25167223699b2254730961d6ab1da10ad8826efbda40991440f312f694514e1bf9734218858cd59f2016fdba684154f7801a5053b28b047922cb1c52049124c8bf848e004de36ea811f8024b020a02d5104a8512bda123b2cb123842488604aa9ecb408228a10302256312dd02d2ad4bf5efffa4b5297d94e7e10f2e8d159733ed193e4abcbd8d06a50f295851a821ce9e4abe33b89a86b643d1416754dec54ec428b3674e9ba58612250159da94a51b69eaead99587ab8526aa15aed05b1aed56ba591814f4a5949416e5d415494d6f33237adb56eb3b5b57a9d1c9138961ef4749c377c65b994e5a6b3c4a413e413c0887e6be5642bbf1fd1d3e5a8a2f26b94b77b3b95177e83a88bbddfa09776ce28829eae4aa154be5a71ec30a77857d65a6bb7cd5a6badb5d6da6db3d65a6b375aadb5379e24f1deda1d74b389adb5d65a5b023dbd5a6cadb5a1ddb69ced0564d5594b6ed1a433c8360c6a11d55e1e49ef62e47532d456926c3f241de9d3e58d277a7a35424f0f298a051de75ae498146cb55abc12434faf6f6f10e9e9d686d9621d536bdd6ab5f55bb026d0d3b537637c6252a0a71c8d70d0d5eebdf7deadd67aef95b5eed3dcc8fb54463737371955024f2bd807cb5cb0ac5e4c64fae0237888eae3444fc73f2ea752c9db6e42aff0055658eebdb3daf4e9aec0322c1b45f15864ad850424f8516bf78115cb8caa93681934ae0dadac0607f4bca34e46ad7546b5d3e7d22082e90db10c89de4dc8ae6602e3c75a5b6bb5d6d65aadb5b5d6206b6dad15ab489f6619c566ac002904e995d2ca744ab1e68e8422a104b1f505fada7c0b27eca3d4664f1627f4d882522d2c2d9935e56479e1cdca1732fc5e0c56c7b5ee16e819443448ab199128e813410b6b5794d632b64ea26984b67ab947073d3dac413896645b6bc3a01a33a8bee691da53a7b3270444417af610cd9e201c8b49a2a7d71f96111e6aadb549b40995f248a595110ba3142aad3e15d084a2e38e71077ee52c6b412fdc40dcf0e441b1cc3b4b2b2cd95a1d595b3cd6ddec66a9abce3bbbe2e62b8a36ee0a71dc09f488817e46b65992468081bc7ab305416669f1caf25975123d83628c5fa881839e343c4ab2f34ef7a4f796be369e0a134e124f7dd151166d3d4f638ee8224bb4f16c3c4f6d7885648b7a0c0af448c23611a529f1b5dbf542cf6ba7b51b90ccb1453788685aa21b5d4c496b2f32aa2882f8a921081f140e7af59216844cf501ad1a43a5862e4e33fdbda419114ad960040a941346904049a1512f694680d842ab4cc141a778a032a24959a828a14935930e3c4d20e139e2084f911df4e9258d67277584d62f6945c230d9a0f34b5a1128a92134e925ad080e4e5068d34b5a11295241d0f625ad48142a30cdbda4159915010222086f917fde04ea5975895a3b279e78de694bb84a6bb5c0a244d2b1762b7da47c6d8d173a3bf51a229d7ddbb6c69b639cf3e69174b0776f474b5c68ec5b69486387e162eb94c887c607dafa68899efa6671688946249d5bc3fca3da81aa5e2754a4524a2efe0ed5cac55b5aabb5d75dfc550d22a93073562578154ff40cc7eb040516e9001d4f8509a2abaee7a9d7201adff50ca1a78f34fc942d6a492898fefa9d628aa73e6b9d5a48e260a7ae876a78ea1a2671ae834d60f1d43f23e4cb3424f4f5481c7bdaa1168be20473224a13454c3c35117d479c783a6ab704dac18828a71b5a22d39b86eaab0ea93c98aa115335c2548f3855264e15e874b2618dd99b5d3a952670528ab512a1af63120aa6af1dcf5f0d93adeba415eac310437e2a5bd8b98a81b78e69e8e89743bcf1f840fdf51523cc24cee64dfcbdf7c4f3f77a1d225f7aa8c2dc98c4c17e77008a3101c28e88624411d1df1b7ac744134fb9213c5c6c8727516a38faa1b2657a3d545fb90a09431d0ba22d74d1932f60358b117bcac176789a6b348ab05556d9ba6eebbc2e554f0a05336fd84485be2f693ef0fc30e4071f32c0a40722fcaa7b49eb01ca0856da730f4e62767a3872f3d83f960f0f44243dbb19174a8ca10329b0c0c17e20075dc85e7870e2ed09a364f2980256c8a005444a04f520075b231e67f1c4e3d4638c317652e347d40e4fb0903df1968722defa88a4633d2928b5f1a3f798e8b193c73bd074b07705ad03481e7b56e8d083c78e7dab81623c85931f3c96d425a9f8d1a3e930440eb4b77e8160b1cde247d4631c36c0f2128a28441ebbee798c83111ec6075fa462f80630784d4cd2b9d972808a0f07adb58a75c316f74494baa10bcf24460d7543104620cfe90629273d849929b683572202181362f48151a443403a418c588909d03b41cacff7c4d70d9168ada323bd2a825011863c7d831117c6acdb410e026c140b02691b43c6c5a67c589675b89708b3ad8795fd42084f244cb3ca1c46940a335d49a62744b1b48e4e0e8c1cc020a2296d08151d1da230f23721105118f963d640cc346a755fd27e88c10ca1958c0a133ffcbcaba6d0345ed27ee00145d3fe9246e40b0fe8192f6944ac503142afbca4115141101a7c49237284ca0bf4f69246e403261d689597342238a8683e4881424739c187229a0f468c51c4db97341f88b0eed2445e4c0ce611d0e7fa084889ad2f3da4da42baba1ea468ddbebc75d51051d1dbee072ede8eaa216f5d159338d46d5774a4a794851ef510066354d4445b874145ebdcd651a8683d6f2d6583b00dee58d7439a0b2a5a900815ad9d13e40189c404e91194813250f67604c11fea1a3508545f930445600f75491fda5b0795a02e4abba109135008a4bd0da2faa8540d9755390a99a11100000014004315000020100c080543c15096258aaed90314800b76904c745a9c4ac3518ce3308aa21431c6106208210018446466868825013824cd887d3a488ce1d8aa9a05ef48260268019a05632603e8cbfe1872aba73e1d9457ec2903174da4f1dfea75ddd595e045f47008ba639b2870b30d4bcb179572e2cbeacad3695cf6219b58c5061caf25e8a5a4f2753568b455e45a9751d2ceaa224a12205747a39bc435735e0ac7793aa065d1c91fe7cd6da324da72a824943ee5dc0229071346821a4c483e1acbb91073d738313a28b72fbab0afa0852f8a392238448097a346b85d994db43526d4468f891bd698449668265141cdab1226752f5edca1cb9454de0f77323c902d3cd2a8906e180746e306e3bc96bc5f974857185e89bb45d10ca0fd03585c552f9d51efbf2722dce6c8cec469e83678ffc13a649bb537da0ff0a0a76d65f61369ee0da8c4fe04c216ae60a74db00e2637bdafec4247925eb4c8e585505c51734c7332a62132f5e434c9b87b578936976f23aaaecafc0d2c642943de99a5068f799e73e5bceecc1ce1a7616e886c1981a1f15b74483cb575e3ffd72986e5bc2ba8f850c2543237ac2f025df979055d98c5f45f08f6874f498b5ce8bd8eb40dfdd27caa2cf84cb461f64e1945854a8e0e60e389c13d8cf766e35507e2d2ac772dd4844fed77194ca6ce27942e1ea7aa9bd36e2c034ce0fc812853f546817736108f0de26042c196bdc6b574a5ab34588f6512c1cdfbb0dc762c0f25b4eb9f0520faaeaa579543fd749204d3ba2fe8db9f239bf0a032b83b95cd1b16ed5913944c577a20ea66c3aeaaeaae5d69bada6a5c884e5703eef3352c82a258e8c67b33f414dc9adb63a991cefbc05ccfb10af63ce9bc8521a4b2a734a039e8209f4d5cfa884473f694e14c3296e36e5c46d4f6fca3eeebcfe8d0cf1ec65459b00f49d85b3c2d50990bde83a70ddf86f601dcea1932db7e78b81a86902c47ca70a045e45ccb6ba2264c7a413b6af9218fa2b0b96ad0e98765aefab1904134201ab2fb0cdc0a7618dfb12de5affc2666889dd32f432f93e1b81b17583c4a5b70506843b7c27e505c73c323c35b602f412fc337012be82d98f0c8296f9f13e4a56c8fcf4081604571ee54bd23b3110034b65193ff6be0c9068c4ff75e495bd18eb9712f801acc9895eb780dfb67bb42f1d3490ed677791cd66298f3ed0f0ff1bf7f6e900c55f4f66a6798a3554f6b6694c7c173205720f0026fabafb279efb93cee194bc9f30165377c18964fa50d7bf709aa289a5a579c297ce3c0831e461bf240fdc04f0a1f755aac80d7506fccd7312bc549a18a0ea53059a81d70ef8a0c58ce3b7a3e80c854bf1afbc62847e3ea2220e5f6b08d7f8bff597c38c567e99c390d9cf74795d1d42803324d3c0261fd512e68a0ed0f399c60b7f09a6836aa6dd64be274a494ac0e7dfcbabb74685987997ab1056460d0ad6b49237f9c2ff21566e83e955f8e2aa0a4f4d956dc87187c5380e2652ba3c71388aa33fc3c304cd525fa958f13256bf8c7e8f1adef37004b2861b401be602f5c884027913655c42c09239c12273026366c03bc6f282e836ed4df60eb7d0145fac3bf8cc7940d4b9af4ad679bbdf703c9c770b36ef0aa84d1ce7d73684874f127af9c36e41b82320a8ac0669625c726ad42bb630124482211b5b951bc7af1607f5c876a78fb81316933fc60c384fa6cd708287911ea8d4e1541d209d954837868289a2208e6fb4970bcc13a5912d16fa34e1e3de292e175f30f2483c7ed0eeeca24dbf758e9c42f134030697a203ecadc552c2603f91b8d87eb40e9df15164024989164b6b409e125ecdf15e544523f7d4e044b15b252fb69254172b4957cbe9edf298ce9b66c0ce74a58923a7745adf951a08b1192ccd0c2be97cafc56ea0b91e84196423b209be0e716659b901b086175455b8ec4adfbbe2e8471d5e602cf5280d4fb2c67af5a9d6f22ead438830f7873d13503d9b58997e39a383ef6f524452dde549a97016947f5269fd29a8a563b3700850a3f5d8cd9987a0ebee3c94a21a4ef25a7655b601a1b951af45ffd6cffdec4f9571e28e04b5966ae1602cc448452e4fd7fea82f72f29ae42ccc03fff2a71a51c2f1183a9ec4f8ed3d5c6d36dc26c95ef3af884dea22ae758f6beed0b0593a8a05c97ef3ca19600047b71795e88a81d59e8ef52e4306e9acc04d5cbb8024bc1eab3c677ee20b0202b9e4e1bd6c73ae4639535ea72e6ceb7a947a348f2aeb36ab0d52382744d19198d4cdba874785c478463f5ac403e05d995a974f9844de0e4ffc9dfe13157d2add2442696d5c0d04a1375b966d951f1f7031fa783acbbd840773e843a0ff778401a0055d52af60e24511fd3b0af8bf478d2bdd82862595e2a83c3bc95e3233b814a9dfb5855150f4e24a8ddb60b5282134ff82ed4119c3c253a33ebeae42de8c30313319572a77afd58c346175461c48a417ffb6e09e3ca8571a7aa234317e2eaf7bdd972413ad2c76b64ca5bd8cf91e5a6b16f2291526bbfb211d2d870a00b888d7d17ba3102c0f4981b920e7021b408b3a6b290cc8a9cd468f885efa604c3a9050fc8cd8c5126dc5f11034ddb121492a19f8bb88ae2dc2d96571de348082a9604ac3cc76e1019f6b9d43c588bc7b01dd1cd15819ffe000bcf1631f9df3a7676f325ed2ac8fe85ee410b4c946f861faacda34828640adce391e6136d81d06f88f8b08064bd062c54552ccd7c6fde6ea1902c81150bf21c89d6e222a145fbe3f468a66f4c4e94d343ca609aae4519780221f1ce493953a8fd862a89f10b1bccac2ddfbecdffe1969a7652fe508285fb522637a49bef198ebf8eb66d2b211321c6620ecff184d54dd29fdb2131e14e66a2e4828fa59cab6ab95c7be80a0e28336d51e91f102168c0a27ba52e13c27c21ab7ec4ba75abee8d0e20ca6f481bb9a6c95ae0db66578653686a1d0183891b691aa4f3a36ede69278fdd596c11ce9d8cd494e01e47e198e789c689bd669f6016ff65d651098ce07693e1a7cd7e833cb6b09f91d6a68f2e6dc0f5be80211efcb6a2f645b2536d8c6cb7064410a980536796ba9cdf97f06ed8b28b2cdb8bf7db2a7e9a11112e7f3eb5e457a5b40a6ce66b634e38c1a46e35cb28409679cfe5a380186b9aa0b5869b28ee1c0b875d791366d3a3fa69512ae328c772f85293da41a41e393458287b5c68bc3f4a9aa929fa8334e19f3bc185567fed911491eeea0f0cfe663f55d58e5c52758ccfcff69959ac896589923114b87bda7dc6135b185edc87e46493d80cd0118be0f0038a0fe9934e047199ee5ab548c0e6159c882579281e33d9d10dd6452b166aaaee693a9eec4a527b69348dd1de1ef11c2e80679a07cfa722486a7a1f5e73a88a769c3107caebc1fc1087e16be94ffe5861ab8a9778cf3e1fab8f24dda207b6ab0f81fe1d1a79715355268b8ad79d32578a29a56b66c407b0f69985ccf6eef1d882a88759201673ce0af2023b33d45ef3f6131fa20bf18ce671082015752f82c4904980f8b36127067c4a7cffafd93cceb1a73a49632ca4bf11530da43f439a3162f9b079a90e0ce4839728a02bc85c80b3f1b70391ad44e9ac9484cef129480e0f7188d9dc9c233e1e72f7d2b6b739f23a2bfdb8345aa380177555d1268592f4dc6c3c267962eaaf219525503a0d53b70f155ed7c9068b5abff8a69ac66e8b45efb24135aa81e00416bda4abbf2342b7b7deaff147f4c702705c3ef0b96a5637eebb34fcc19da2e2335f99fbc8e92fb76c146b3bde7f51bae9bf19d0e222f8f13f6afbd65dee2790d94eee76f84ef9aee94fc59d955709c07d05b177f9eb412c5d3b99c83d2866efac02a277a3f8091377d5a13f25eefc6493b02710e69725bba1863f449a58969e31eda7dfac34a11481676f1825bc0470870849a67ac3f18bc45fc937c7e29f18023c82baed6401408f0f1588300ebd82fbdc15c94f18bd937425070ba7c0a3328281d2f020bfeb33015e7f414ff516e2a757202cc86a8a621602696c721774dedfdc945ebc0138918bc6b9fd5e5b8bfef1ca44fdc2d6420e1520aa2b9133cf340c9b9fe8a9d023961ead4666a662fd9f118ba81d5d7214bcf4c2224b7b32bd5941da1f46405efc84db0ebdf95af20b67be6cf5d448351375a5f6e30791b9b7e0317ca1e8fa11f2ac3040d399be5920a13b6e4f4c21d836d78f236921e8a2f4d8696c9f9f4099cf310fa694704f1ad78bb8abd31c01126490f10e7022f50c32e53e104931644f83582e33e4ac94c83ad3b2ad9862b4886526224838c80b906013818a2592c1c00735d62cb7a37b4c9630831b4b17274d64962bd1250a61fd0cb44eba6125c925cf5d6e79cb3b8915b47f14b91c73c576fbb38b626f147e58e7bf90500c9f5b5cab1c31ade839363c0596f603b32318d55ae88e31c3d4ea7c10796552a26b2fcb47e482012bb33575e3149fe7fe7082e2eec46cd338181b6f5ec557c705ddbaa65ca535d974ea8751f4e3a9fd3527a1f4e67172de2f9a2fc242c39698fe6a71e8f8d575c65a88bfbd0bdba39a48a978204dc496442dfc8910bbe3c2c39452a6b25a35eb1247ab1e9e2b94f1fee746c31df94ab0d6d2117986a2b833ab710855e55557735816b205ac77e5491fcd7ea6c02d75f56331f0b508c0af534df0ad542a9731e79d61efd1abece0ef8acae47669ba8b69a7faf684c3d737814a54219f1b44403001778d08877549b2b8028baf18f7878cf1fdec056b88049c6765c15b8d5418dc6ba600970358b4063a30dfbcd07c63591720af58f0a3b726c5ee3a0664cda590e7d2840d769e2b5372c97f7bee4a037947ee17e798aa3c72e04725464a6ff3ac8022e780cb08a20bef830babd669c94ef8f299e1e07019256366bf6b06413902ba0c0a25800538bbb1027687e9e3413a7ec347d30dd4ef207e82c3b7b593578312248beaabb99078f39ce14b07dcbe04f607533d4bf1bf807644e4a16ce1920873a67f71952af7f849c072e43092c3bf175d6192f19f1d5a2748e6c70e62e6b265a167f2272890a9476c0d194d0cfa4e809848b870164d9f2df0a7ae974d74b57f2b55a087eac569df4e81429345f9a299d92fb0d3b6968af57039d94ff2d3f8f1730abda0433c88f084accc56e739ac44c69f189f27160e653827546902279574540d831c8bd02873d20a950d20a1475bd06bf4c7fb802b88f573265047a9644f22a34c70dc52cc271fee2f5b30afabea8e025b48f7e01241c69be698871c2402b0186f8fe3f5cd733876efd9df7428bc4d0639f642063412c8a627ba37e61f8d3413b1ba233a04cb67b36db5f4e40813a18610e05bc7d4f03bdbd1cc100369e8a92a3a37dc93189bea806d46b5512c8291d41dd0ab6aaf5838e5509bae0a72e410f424de15a5febd4ecdfad4c2c7467a7adb8167d42cd0bfca47dbb75b47fe284f8e98d530f1509185fb49cae76a29640936242ddb1fc770bf2f264ebf475e75013b27555ed29c5193e5aa9f289394e9336a93e5be456f5c63289e1af4ca063a5de4be0a6dd91118a717ac1b8b4761d2170d600d3911d3619c8216729e503564d2d7ea17791896f46cd222576a379e0ec42aee2bddadc0ff9952e63867b60935567f4570d935fd84507e2e0825cde7e8dff4bf7df6c94097fe6a65f6ac3592357173520f05ea279f4509f62aeee64b008bbccdb30a7c47bafd2f95807e92de8799f8873d592fa664df8069c60e5d60e5712a8901f10db4d8cd9e63a6fa333af4deb15abde018f2fc21c6f33acdf8833c81bcef20839a1273bde82205fab7a3e4dca6ddf031824d73f7f01585fb891ecad443b7aa1b77f90e1d70a0ae365cfab4945e1171000005dcd78ad768de5539d3cb455625347828db0e6dc1f98614400e1196f3ec7cdae044687e80eb86b2a675c57563bcb3ac55dd6582648cbaa977015d3a078fa75546b9c2d92fbc04ae63cb0652d4dd8dc9b8245e12fa3cfdba31f475682e7e38c6a4c415e7cc9d3740649dc3de0ee63ce0511644f29da36cfb7031c852dd051f584283cf6f8b049612743bbac202cdc6d4c9e5000fb732ab8016f914a1154c1f4aaed248db69c5fc3e9404070bb4e32417879cc12a03e75558ac29ffbe2177f4d0bdb4b08b293a7f6c6ed962943178db2417975599bce10ac8cf442a2a9b7b3ed7265072f5402fcbd6a6de2dfc2337a0139f6ca761baaf57a8a94be9cb75c9dd89513fd9cf546f43f6109130ab928d29c048e5c16839e8351398f1bd86d12c31618947ae1cd9a40b939b05308d72de7d118a37e5d2791a92679b6d9081d825015ee4bf8cba0799c007f68444fcd11e744c2fa161442edaa49c59f4b8c2933332a99fd38c9660d309e04a276f238c9493dcd6770277633a900ad15d2042c13dcc42c7320b6556c6b30e88b7f7ee8cca84386de85413c88be640f94775f21d56027b36177b5b6169df0100235f790c8ee810d618137d2aea7fc6179a6a725d7ca12d3db01ba34bb29e177b50e8567a50f61af3ebca23ca87b95d2f285a18fa3c02515a2c30336a4149d07a7b8de84989f100751d3155f360601b1c25109cf38bf5b6a4a021abb0034f6af08043019865d82c0ef7cf56a5db4f9cf74a6b77cf1a7dfe76b70f162b082e491d820e5425d2141a222a20ed60e9aece2c2d3584547e71d186cb1bf21cff50af8ba5f3dd8b42fe7226cb0276b801125f25ce999ae3a38491228f5b99f79540bb011052ffee982ba792652eb482bc4dd9f0d80e857de39620da56d1d54234819e0cb3e3f6bea4f4c10daecf9d12e4740566859e5555173d20f2fdbb19988ac6413f129856b41200628bd67cca02a469c0c900a1035be2372644918b5999afaea6910aa6229990c96a91e6a856d5c53d6a3f3120373fe215aacb6414a6ef40227860cf1fddb0ae3896892aea3ed1a79480f64116e3b7b25645bf68dba8f41592d08c95db78b7a426f0b0f2cf7f3402a810d5130f5ba1310b9dfb0afd8a7ac86535670377718ccc367b82c8d580915c89e0a0772983df00dc6c804eecfcbebdd9e39640a617a108c49c2902f4fad0f29d0846d871ae98d594f600b2b7d2469ea36625e4968aa87022752134b52e7460b302c03d827af869a5036615c435262585442956a56615a410650c2e99332f9c5cec248961cacf4a37127894080ccb723fa138194130325512db5e1600139abda768568a5bd0a65a3b008e12c80435010484fe97b39c84da686105d1107b664d81dc50a3a03e2abd09f68eb4d8a4a77dc723c527fdb9f647c3d57e51e279597e1b306e3967a19464cfbb6a1b01bac404a2590614d7879a184f26c60a95f471a7c6f7f2fea9bfce08f93396b2f13585fb0502b235d257bdd95cbf3ed281013eb51e220d5f5a4743b6a953c422dcfce8682d39f9622355f47131be1acf51c634f4c1e25c750c5213317392fc0e71ed6538a9fd49af8d414a4d26f21a10750f42bddd1e74f0df8c4504adf7b883ba79ade285240db0d4026f3aa880b15a226acc08b7d109af1e81e73caf4d15fe8bea7abdce4bd5439233d34f69d1dc60245acf4031ec8aece4e00482e5083a860b90b01c81c6e02204cb09744c172471390685d9c5d5bf024094b45eee3546f0f50150fa2233de38753ac0aa211d99f260b48a08c53e9323f4dc93b7a0c42ff580aaf4f173d96c6417d0a9aaf5726e565e0972fc86131f9886b35c896d041029afa5699d06177fb30796236ca62f32aa5d8829d702318d9162fab34a25a865281b573008926ac145b257b20e76aa8c67c1f1d50d50ab952a356480d51662c5d80bc394dbc010f24f110cc43a7f3eaac94109bc42fa0695e5853d14e45e6260bc2f5d09f5d1a81a984167a9b3ed1a226894c9aa4c3124bfb0041e5527a8c662526580ea3cb38b9541ff0469b53bf25bc79f508e163114268f63e146067d27cfbb42efe65d69e259778fa1337cf40724039d57c9e8f13f8bb84ea3b49394d5f1849a549be894737df6fa8a9663b6812c13920cb7415e2974c72ae3ff81502a37c3c184d078e629cf8c001bb0e788c60365403713b8fd6bc98e71203e6e7ab2bb89e1a4286c5508eae757922482f1fa170b58a90fcf564b87e85b51ee1467ccfea595404280ff3d1e4e51869803d914164caa2fd66ecb29087257df7d1ea4c4d75d07d2a337338bfeb333e835c91f5a242179e12206bffc0dfe701a48fbe77568f4e7e36d94ae96db5011ccef54a130609d764ac501438d4bfc2bd1c62ed953764c6c0ce14bd3b7fc4e896b88b25ecdb3d5585edfc1c8eaef6395daf31c092292fcc306daf577ec9a5b2ee6ce1d1fa7462fbb7ee73b5a953eeb2e1775600c1112cb8c6d3e234e43c211e3bbac275979feb28cb6791e7136a694e0c697a2deecb1ec7301e26d222d3fa9c531a60539e61671d5b7f6838cfae22ec16a1b077bc207e0acecfab9212ba2a20bc435a0afe470a735ffcb24419cea202d904e0ff2a93f64eab59bdfca8f25ee1ac12761bbb525f500d0fbb2ad80c5f7f92b8f797dbd33ba7cd924c9de54dca174d389e80e0599492d82e968547ba7ee636d7c58a9a90f147ddda211dacd11033489ff0e0187f035816ea4f5f9c0a0bae4f8438195d395841e2183e5e667b6960d65543c221becd1c714a1dc8462b2e20b32569a9a2dcd57197d024c7187692ba606786008573c91fbb136bc318727599552990c83d187e91747820790bdae2a45987de783f9a16c800171fe8d7745989230efe4a0ceaebf30d7e38e285c8e1a4e305721be263af3d4e73ca7bcd498136fe5c8b8c75c99f07fa72c5c9bbb81ec8ef0ce6e2937e1856f1002b5a6594bf35fc3a693ccb560c51b696269433e1b117343143723f0258a55e4770b57541aa38ad2dba622804e0636518201e24776847d234163178da6eedc3763c66e67120915fba95e94f1655dec3744a2174bae3723b9ab7522c5ac64fe4a763770c92c273bcddd2f9dfd8acfc1286a70f798233ebc7f9b1554bdc1a7aeb41e78802576a81f41766f149896224e4d2209d404a5d07a568691e38dffcd2790e48798df488dee417a912438856eaacc2d448909365bb95084ba78a3264bf69056686427c490b67be0ffef9d7a4410b6bb8546da872a54ec3db823563886ad2870b1121c480c0b7850667dae4baa10ea88cbe5564958757104fb846bb1d1e9eaf77ab36fcaa4c522693882a9b0e73833932d3fc427b81cca021d4364e453604cd8f57e1dc5e98e8f95eb3795e67773ac77e23cc5d8e8f370aa46a490b0621861b2ffe940ed29000934b4c67366edba908448904d29c6b2db2d61e9d8f88d142d0cf40862146884fc0dc086187e3197104c89b0d6e2c6935d085c14566575d752dec1c1355231be0158388a97163d2a197b9c360b07a408d3722abbb9b5c44ccdf045f7892b5544ad93595d3321b5b6ef025bff16748968a676f5fbea0d57f125435d2d46689262e9d9b0e98ae1f34f97ca587145c5a4bf55dc0a8dfaa59d7d4262deaa45e15e9e3ed12140f1cc80db3f2ecd978bce982bebd3284ef52de150fce0136a0c9e393e4b9740502fe92703a32435451bfd9dfb75864742505ac9712656b94289df8d9651fff83bd2bf04332276b71905e0a6fd09ae1042767cd0a37ea8cadc915de814ce7dcbfa7a4ddf122be65bb212f3820f264d5d7d40b3125ccae2c551e21d5c8e28c55bd5db8b9d1a0ca206640bf03e5360c10998e02d20a1d2cfb26975faf78856381d029043199041ef3ce0c852557639300e0e3c764580f9d9d2495423e3975c7c0b0b42a006a511c7336176cc9d5ad00664075d8abd068474a19733e88daf8f2f1c96786e93028354c610bb5d06be669b48bdfac400c6b04272cf4705b765b8b7ef93957aa4702f418a412c93a06c39b15e0270f606a49332449f3391ac72e6628b84ae404d1ca6caa1c1dc70a1763c2195be47071217891263f6d19edcf8b5051e667817d493a92b9ca297cb23e6fb2184d3314129d430bfa9d86b09a567bf348656d0ecfd86bae211a7327626d3f6702a60828f7c0971b83d8e10ea2b310113a21ed19990ee8e3e2a624c302af2fd6f7cc56cf44efc8bcf0ad0c8d790bd2504b94616cc16ea06ad1e3efcee426a5d25274f8e6e81f06bbfc0d2a63673e18925d7fa81ac12222c340286e283f9ba3fa86065ce2b0500bf64cefbaf1f9baabe0b08215f7742525ff0a4d402634ea5f2b1dd8e2cc5124c96eb7056e9841279e298b0314c2fd638b7a52f274c88792f1ddc5331863c98701dc2841c388c8352128d45b90fcbcd67d6032a9f4faaf9a9ab1a094bcc8b74f20c5f7b21d922721c4519d56a1f895a89dd924f036ba810c20cba4c236d085bbf5f6d4bb844f4a8534b5db8f8ce4afaae03a4c9706e43c6245caf1b1e3d4701d6142bcd76ba2042ded8632e8f9afaaa2f413669b57629759e9260705dbaa0bf46627793de21a8af31812816fc3f7328adfc31f09d36635e786bf38f7f4bbf0f66d1b2e6710479f8572c8a323c57169da333ba2466f69b5df214abe46785fd1231390f978a4e2c87440dedb1862cf6eebe2814598a24061175cc13abb68795720d251f0f6be94d8e5cd833627bb524a97d60fd75a1408c5145773293c351437a8a5d6df59670301dfcce1a40c2c59f4c26d205e7622398ef0714d7522ae2489383d08298856348291b4bc949e27543907847ad1ba219364626990e919f57110f03bba99aa228376e288858d29a6724c0940540446b6605d940030681ffc31db2737f28d477abe0ba6628711815c4692187270ca2eaa994144652b578c914f18f673deeb76c97596739be7ff0ab65e96af0fb93500d6eb0a1271adaffb62b69c454e131c8e2875bea876745734d0b752734c78920ed15f2751103d167f18a59f4d5714718c7924d90338a290488a9b001a45e2c1cdd24ea01aa3db15f7209617ffc8103aa91b49db7005c96b179259c13f5f8ec629f7457fcecddd4ae40c715f11dc077f4d800e207aa2da01ff1a99725862bcf7880bc8f0c9c8e08a2a69b50dd58cbe400aecd63ae1ea4248cf88fd3b8c8864cc13552c2b555e02611eebcbc289efcbc1e57e7e11ef4b6dc22696531e6ac755416aafc049df63b35b0eea02ed133a1e099b6088251551f11b6c600527345ab5ca63e172e709de81e3c8ec4638b610fde84caa71973d540ebb492d120c31a991946d5ae02593cc996a6bf8a83b490bfb22453b9cedb0e14e4765b4ab7ab72b7bbceda82ee3ca7100c4b4779c079590992a22260bcb0f9e92e545915fb75594b7240a3bdfee17db6555f52a78a7cf3b895e3bb8d91135c6b29f58a86c90688ea78b95a065645dc6cc0ea80aebf4ea962b1967fde245d0e64e1f7172b80aa0a64aff09293dbe751205af7b477bad891ac02c27747b40a4ac35953249ff5de9747203536c2be1d032dbbcef7b993231bffd208e9e2a10b355fafc01c94772659d60bc9589445d8dcfb0cc0f2941a0b00434c2d59cdd3681f4cfca9201f30b4252e8fd296468541a7442bfc922ddeb9908b87d408b6d3ce2cead6430ea9d7178ac973c577d55a5376d11eab396e82854821e68b6600bb990686ecc0738fd53c57e1668e8bdc890ae981763d08ac47713ce158d67d8d0cf149a3c5f4031c9e134affdc0f2beb4645931bc5970000071ab8f13f8bd1ce75b6ce14451e408b31d381b382b5611d565c78f781089affcb6db60877e96d98b15e0878e25b106ad3c92aea498c8cc1c701dc3f94107e5ec5ccadc02d9dcc897d851e87e82d0aec2323d4451d467ddba793e0873dd08151783a1a54b5c64bca1cfc74e6e85e9fecb33ff039af99ffa3dabb7dc1e835e067d48146f481f204d3e3cbedd78f995c7ca41f96ca9250620ad4eb4149b3ec8e72525be81697f48f4aecbf4d83104a757c07377ad2dd9bbc6692cd0b34b40e0fb4c9fd376879bba779c594b8001a13b4ba3760e9899f88aa308d87940f1208e4b7494b7a6e4bac0a8dea3ddd420b8cc887476d260e1a07ba7acc6a0b7df3bbda6b8e92faf35d317305bb6439254f56e6e081463d9e8c35da51d34a39c7c9c90018f1d7a190a176e05f23ec66bdc2f1c4d8a053598d7fbc753a820187d9ea7412c2e1f389b140ab7a35f679cb7470011e773d99c507d0d64ee1f0d0a4de167144664fc9631b348e9d5811fefbc026af335c19cdb281c6fd9970427669ba88e891d31ee21e731e117f4d0c7ce4706aa97da03004b4f8138187c15fdc8cbbc1288296fbc08d03224de71a058bd59787b1c195547ec9714a8e59f2b98a6eb6f7bf4835a206f4dca676c072e125adcb9df871574d2bdbf35375af01f522730cee385eb9b8091d3452fa9c4ec443ddbd1297cc3a8bc1ea4aa14868026a0224663717a6c48d7f7c3a03cd280f196b9824f722ee4cb80998d5ad173c3d595bbb69e181e6bc4431a67805eb614c7e58182d90f50910fd56dc503a07429fdaced088a45aa3bbf9826e5bad7ab2ee9d5aa9c830c46b5f5621a8fb4c8ead9b1151da8d5530e23338e95c5c0fbecddad42a81a37287a53365a042cfbe52450adc09c44d5716c50dc2c40990af7f90818829e392042ec2b1a3f5328e27d4ffeeb71c47acc272d5a25fbd646354ecec45ee0f069525e0722967e2ae655820bf5615333e0812969a4c1ff2a98e572232395745efe73ba488108c336f328b1888c38cb430d30a11bfac5b451e8b4d12593a8fa240b5ac378be3c11c47a31af0c99ee3c4f22c63ed95d072a90b1a4d3e780774049dda26ccaf23557d962c5e44cbbfca849b55d48022879dab12c0b97916b2b4f548f9091ce4dc8f4cd4b0594409e14a26b1dcc2370a2f39c141f558a76d0ff2a8e7972707075cfd723383bb899cc072dcbf2b1da5f3ff9bb89441d05bb97407791220510a887d32264601a9ca9271b927080f4f7f2aa7b52a9355e0b5ec494e7472fe91d51c9e7656e15c4ba978b0e6e1bd0efe77e6fa2f8ad992a2c0e0d6852b99ddb40684117a24bc9b8485ecc39759a655ab7a5e81fd63daf29735fb23829205d76f45816ebc0fd7bf8929f44d9c1ca7b17b73c088d965564fb5f73839ce109d3f65a6d2c7f0fdcb4995223f8cbf64fe4fd23c0140a7d5163046cb83b2cf1edfbfddfc51a4a70d1beab44c107366f7eb0700daa2c5a1170de4339e31ee1a07d5c72e02ec80ff58e325a3ea7dc6d2bb743546e5f4a6171db79409908bc8fb8a8fd12d0e59798b563790ae1afa0b4ca7ea604d6b4b8b8ea1642641f0e06b8193bffc9bea1c2f0086a886e185e50dec8f9b1046b8940884570e4eb4f2cf00d03ac4049650882daa7a45de3e57acf0aa02808593335807db56180c3d484069d6b5ce027cbb75fc3588f80262543702ae4611587e12b05d435638dcac0dbf0d85848cc45081f8770e982e51d73a07d7b5308e3aaad86b2164f4f54e76784769eeb4acbcf136563bfcd3ffbd8decf0980d11ef083a1ce393d56a1c81703b819062c1284368c063d8d7378532f5ada88e00e2873010ec8d9fbdcd2ad37b215f79dbe005b57b78d60463b6279a0f66e283d5941808c1508bb3e078b24c314084ff59f086248dbf60ba1a3938a95574e69fdaad07a6535d0afde7b44dd5bf1abb4c81797f5189b1540f44b2e6957b968c7f5c55e25edb4a1eff1fef5995035196e786a99e720ac3a710bc3aab2ae444d08999006c89e9c466703ef7fde75cb981b7ca899660bebad881bb4eb3cbdedb7b4e1607e3675b234157751843298b93f3d7a52066f845633b89000c0d281dba52773427bbd10e0049625403d01310759919c6a9e57dc4d7244e932849155ed8642fc7c3f2f02c235708ad1d1824c32508910822826297bea11a64a932b24b75e64c91ad9560e252b2f7ccb86b46171a29dab05af0a8e2c534aa564018dae6b8742d96fb6d04b9fdf09dc42b90ff4beb09136df17345f373f3e954b2f1a636e46dd7fcc462d60e46f643eae9424a96d3b5acf57089f001ab580a8e64f4851ce6a990d4806f453231ce32b9e2be12edcb8a907118ff3a530e544a0e0d51454d20361618a8d2ffbeebb90d35406f797b86be02cdf966e8c210abdab1f7ec61771643ff31c2a6c392721fd8cf9a4caedea889a7ac0f88152d1b0d8ac955a6554513ac537d268370058f237ee58b2aa83e82133ac04356398dc64ed0548e239f2c8a28538ad93f30446651a20302eeae162ec2cc89931132f7619d6a577443e9f7ffbd248fbcaa041584dfdc877e1bf7ff2b6d64305c4f050b49eea2fe66c0c92e8ee3bdd1fe70f07afa9d91a39bf844b44d53c4f57fcb7800e144f88eebfc13fd230d4cf255822e0fee4338cfd6592164a4ff5ea4a706e7e0866e5a013130e30841fc705ee28334d6628f5e1768612b0b66e546dd5a1ea6da6852ecd17272d88de3483b5746dd292ac28796bfc0b3cbe39d86bae95daa47edb75a499e8930cf1b646b4968a2ecc298539568d581ad45aff5656d47096b750020a6498d2c401b933e0059ade4165a1ae18439c475958b714acdbe4c7b989ae5e179c2a62f2e3d42948cc35359e4bc5c3feaace86b5a2bce523c7e57b77e1b8a047c863e5bec00561c9e6cfa12e449e16aa51077a3e16e0122af82f9e62f095636768e168fe2f15e8092d1dab66befa31d0374864b474c4b6fa1ec64cba5e526f5e6222ea0fa68bf532da85f106bf6947d609c985991976c2837d2f031689fc47898eacbba86eab6fe8ccf84e98d664016fca696a81e7e5d5f46ffb30145ba30117b82a0b0aff4ca53cc0bd936fafd99a28ea1ffab005472881b852aee6475cfbaa69a9e0c5eaab80e94a1e380849d3993c12c39403d6ed63bb452c577b826fc562674307227d7376babdefde6cbc25eec37402990e5810edcdefd7040e01e17403775e1fe5d665d193665f9b3fb188bf0e0821b61f05eb7198615eeb21cf2b642b6f97cefbc1fc731a5bcbce91e2258f2d815f5d3a4ddc0971eadac28451c0247f5af320b0fac0de05b53c0484f135cc18683b729cf77b887bd86bb7d3accde0615c70204e5d37797b40187763340cc4c2f65afdac640fa6cb5faeb3da0069f19d955fd42cf5b991f030e1ad9bfd446ad543e905a08a1e543e81abe4c60ebc326b1a4ff6cb8ff9cbe3c78d0e2c9a78a6d9a9ee58571f69fc79a2b638104a9c379f7092a01fe785f970163029edfd2b01d549367ffff0371c79b648818c445941edc7c8ce26e87ce870f418ee4873e24cd48e638c6c6ade0649c2733ec480b201f0e4a25cbe846a99c62477b76f568e0d41771ae7870cfa3049cfe8e2695273a12344e2432296bcab77553110fae3a3baa1ef865d428afca18cbab3d9077423a5a504d80ff3d6add1053817aeb4ca455763aaa292b8f829f4abaa31dfdf46b418177fea2ba5b1bcd598d67b9026aa51f57e0f24e450ca7c615cc6ad2bd3caec0e5ddcc1af534850da76674495f053c1d8e547c4c3c3dc06acc68f19d5fb9c9cbbc4c9de245a9a9225b3bb0acd8751c1b34deb03f76d1daa31651e5c0da69d3d9688426174de54f0dad156066c58eb6666a3b95adf8dcc39648569b2745232634b434ceaba8514e2de9614bebd9b6869ccd05eb47ba425900dc4f207699159367904830bb2519e213d2d39e7d7b711d8d2ca0261dbbacda182f3c26982b94f17329da4ec4d777135fc058b17178a62e6247b79a0292987bcfc344307cc90dee5015557499874042b77525d31ee7540dcb7af895836d1e9ce6274f61b170713e4a5ef02658df9da0888654299a8f0d98b8f309b1635d11937eda385f61c5c4d33f3bb03e7379fbd285f2cdfdccff48b3266d59cd44674566bb70fa3730759f431e47849acfdb10bce5bc9bc6bc69604fefc2259a5914276830ededed172a0ef8cd5aae536842f46050fc43109efdf3f53c0798d98e21e4ac54d9ba36bcf48ca5a5c52ca255685c81c5f74b914a5a1a2ce1b129dd670a6f08d31edf3d52ca97a31ed58ed8e538a7af305f240fa7133fb48d9f721d69fdcd92f7f61ea424ca916f86fff172e68651fbad8fe59e47eac6750673b3351dd8010eca5bfa1541a5a5bd178e063d9229b553a164a840380fa35aa473cce71fa1e13047a91adab6ae43a48851b550235835efd01ea69e5ace4cf950b8cfbf0aa41fbcb28def5051a24c7049a86568c52d48cb4cf9464c259b691cc091f0ad01dc7e7463899f74c092ac2be50586223ca0dc77863d59b0e6dada709ca4573ce1e4d9de6a3dad427c7bbe7e59fbfa17937371e89b2b0c6c1213799f880eb95207cba6d61a07a9022e29fedf635b2992ab7e505a105e80dc27a0fec13747005be44aa2d161141f59ba4b68dce3c7da9555afe4e64d5b255a05ed375c46b68ae5dfe8c85e24a53e87a684e675a8b85019e8238be0a8ba2511efad911d47f512feadb36c5759a2edec55ec8f49a8e73a38d1e26a73716360d57fa44d8a8c8db7126ed53e6a8a95bacbc0d71650039af6f3aaecf0294f836d53d311d50387814dd2be81faf7652eabeaa04e4286084548e47fe5b1eab9ffa0ba66fe32a26649c5dbae48570a0642b6038410b60f66cc867b72484c32869228a6544aa21049b0d18596c2c3e2357e1aa5de944a137405b0f25765451cdf72c98495846884a3e1a4d8888078bb94daf333c6e9600210aaae5ba6f45eb9076ce7f4c93b8ba5188b7c368f230885be71ec95813a34b0fc6e1f79c5cacc0aeada2fb65e031709941e1b3df90bbe34dea1a2890cf0a760dbc7d92b361a70d2bfbd342f116fdb62c3f1fbd1c7071a393778c45d75ea92b278514c0b8c30af3c1399d7ab2c991b7f067b1a4b2d8ab57ad77a13820c8a59493ac877ff1bce95d5a9e7d49bf0b4e10ec61c67b4d0ab4acfa0ff347e9101de907cb23ba8c18bc8565cb1c914f9c44e35e8da2534226dd00b74a183326850403348132f88dde019b6253a623dc93b609222e6a4035aac9588f94f7a426acda425841266a94eeccc3cc58f3f9b3d438a2967adf4717020ce9b2c44608a530d0aa43e223d6683396a2cee54818b247c5e6c6c6080d131291654962a09b1e7d1f344deee4e3d16269e741dc80890bc0db3cee47f793cf85a87a7a8ea3c9832921547d60e5aac08a9de25880834fef62d0b854b2a23d6608a784bf4687ff10dc4fe281496519344b622538a19b8b1cfe48aec935f2604c1a0f057b284c16570f6753b19dae040e6bf34fc7fa7578cccee94107c09b3c22a9a083e9e670466b4d75f1c1644b6a31f7a7adbdec6a5528ce4f2ece89bb40c6d5a1692f058162b26311ecb73a2429c4ace72497c32a70eb5aee62034270fb2f47427b98ce925dc145c4a03271f29723d63d7f16c91bde029af86a4e9245d0488dc1b35f019b54d72d9998133967398fce682d333395dde680cc03b7f0026ef0d2b70eb3d30240f249b6a953b62931e80f56272a4a260865b1e1d8716ed4a2edc2256e7e906f777bd1c47a46cbd5a9bf973fb599c6c138747a4ae9d83d300d55d86cccc17a626aa45a4840591a0f589c249a8ff7ff459207c60c8e2ff67b26878f0a3f119e58e3979b305cf58e03880a4e0a427412713008b883a0b70ad4e1c07c15a65880b470f36feb57270db751cabc3545baeb7cb0a0b2dab24a22837f142ec12765a6ef8dc0cce062e6fd6b60eee612b368b25fecea071dc312716db327372702ebc73caea2a4e5bfa40a7123544539b23a2c41bbbcfd2059eada5f11221a0b01a5ab0390a205c08eea6f660bd05722604762ca20405470df0bc38ff43f206657a6d4265c249e7adbcf7caa0876fea413e40caece753f56ee67f7815b2afc1e2bb428c977b1bdd7361b30b032f0c4cdbcec2f63b001af6fd392cf464302f3efbc5f723579b1cbb86ca8b35a0129e876c08fee68ef03a6ea5666eb8993748610f4896ae3b73126ac7e7c293af490bf3341df880504da1b7cfe92810551c40e4bcc452d092c294ad01e4479379357a634344087a7824ad180803ecfefa57ee4ccf8b531cac7aa4c41de71cda659051b3cfca1e1a2acac9625521b09e2fe26c93d1cf3c48a48cfa8eb8e510b7675ae51d48d69b77265e396c2bfc2f97b4a98af578e6c17636892a3230222175aa9d483cfd6882a8a6e638fdbd4b480c9e78d6324fed0d268c1b785fe935788bd1b37669db2577e072ac13e5cc30fad9da3e7d690847ff6e48c1a18c0344d692dfc7d8df76c859ee63851e3c3d97ed861767dc16e89808b615e3d13760af29d1970b51e500ba9602d88a0938eb8e0ddca8ff6bb8d3456855a414badb80aa9564fac52be7f8379af419989423f6a9e4e948017507e1c0f5fdfe97ea7c5642d3e03a5c8745d4863c1ee00ed18d297a09f5ebe341b4264c6494aedea0b2c5349fcc6b61059df61b7f36b88c143c058f8baed19f648d64ea461b044a993b274913b7082445c1a6da2faef7c289600efc6c4d4ee7d2c2670d47c89a7b1362d94a1308b4c8403e28f902843f490ba547b3bbc376cf952193e231d120ad92cdded04161719b3aa1869bd22c4c3755e97c856911632d009d9c61ca0f04bc72982c31e5f6285dbc10e5971b43cef069b946190a8044fafbfe944044e01caae2ee7a4a2f8478e183d20f738d4929eccc22145b802e47f8e4b2ef866db64fc53feb05341fed9c18d666609bb5be730b8a72c28bc9d4ac91a58f2b55829b92819b7659b624a69d8ef4da16623220f5e695a64df258c413356a7f4c8c12563d4cfbf78946274001bd7c2444b6870fd7c3d3caecbd6305d83157eb43d7ae6328aba63ec4b9cf839e222144819b05710859997b37351370a813ef61025bb55f2b1799f8c7bb9753fa0161621f81e4ed4544ae0ccf3b52201728ee9961049adfc32d0296d8cc4d47af29ea9311a005a0ed9e5d39292811034a6172570eaf49d24a635d34032b38809e36096227eb60043676b50e08bfe2d342a7083613685582401cd57aa659f91f35ed7b524d262d548612552659ad8c38d6ca9c1481066e42cf8297261686553fcfce9ac7e1191719d618b23412cbd7b2755a58cdd74411e2649a1b64c39eccd649e23738b7ad5dc3908d2de96aa8264303c83549b58096b80f12ae595d0a89503267d15366d13a1b6474d2d7d26b0aeeb713c5aa1e2992c563e0789f385a3c3d7832be7b9035bd670f6f105c8c259ae6eafb27cccb760c3b512241b09072f50f02de56aeaca1d6e1efd4cac2292af17fcc2c7dffc0afc3fdfb57a98cd5384bd1e0eb60a60236db2729a8fc37ad3044e150cc1277a87327a4ea66a1810ff5a1db52505913fdae08a9026cec84b889b19a937995d33c4a167b77b8eda3a36f4150396d2cb65e5803b47b194a0bb2ffddcef220046f84a2a707c0d6e7a81d8b62c232d8a22b15e8ad1981a98ebd11fbcca3c3144fc90d66280814cc3c4e71093697035429cff647ee956c2a612890bff09edf64b92d93c713e8ec935031f1f7c3e4a31767bd70f25fcae41d5c5ee6638fb858ca88f59a23bb9f7fab04c14cb8933b8d8424e14013facd35d86632a7cc6494193d1e73657f4c3dd1c45236b33ca885c03b8071bc65ea6bff8faca0c358544f56f449cd02b3dc1de0b3505b5a3ac93adc898edea06b3b74918bd2aa9fbf045e836f4b286882c0aa135c6ab8384611b937531bc2a181289a873151cecbd4f36b5cff86126e79c008077f07138af9a28608e3dc44afaeabcf5e582145f5ea03a75a871368ae6e38611ca5749ca7c56efa0e9b323c013e0386c8588512a368b36797c67aa4d1543cac5d6dff9a85958eb14dc10852ab63896ed3b76ceba9bddb43f6b14feeb633a77f3f48aff1a99efc6c4bc50e6cc5d96e1adb76c4e087bcc1354593478760212fff688555085b07431d1d98ed6ccb994827c3a3bf7a11ccc0c7916df7a01c644437b339d0446b85defa82ab354356f4a49b52ce8df30ce223783244cc70950ce14150d6257b1002503c82078a71f9ec2bfb2f57025e82693e1c02460bd52b63239ae2baf156d92c23d601e9577c3da82a98f35b8c9c13a0264084b99f98914eb72d730c5be384809bbc8276674fde5552b04f948cd40ba0e1156dcdde0920596e96f9584188f5b2d0fda9f7dd8481dedacd72698e6196d76852895d16f54619af684c58f0ca7a3bf013d2689f8c332510c3e2dc7d19c4b40c671d7964d2061c67c6f96bc1a2527e3938623b181ac90c64a34a2bc3e116868b3e68cdb6e9ec7cfda1ad4f636c102a17ed25fc2825efd4394260b8231ac7a61d3750d784420c9ab889c269c4593f6a446294b6089d4d412dd830e5abb3d959047a04e9c88f3583e4b14696ab4c1597360c4ce590cc4f3b0404c5017a7574c32495053f02615aea866044a6203477af087d48878fca325ddf181154f53b526433fe7364e290fa5ad50941b358c7b769a49e6cd8f8bf6fe201fc75f62c198e45cfa239e187351cf4e5e1c64b079da78115c70c9cf77761ddcbc91c25c811f7727bec2dd3b31f2445dd28a25bbf69165fb20ab9af0269e639680b770d5fb997dc0ad7a34e3240271d12ede4790124f01858450dfd1128fb931fd6c4057f15f8b4e852677276689fd05ac2de8dc1e65e1aafa770e8a1898ac3aefd3a481130007cd7b732c761a88da87b62bf4ab69cdc390c323ad0c96e605e65c73159f8f3176ca96db233a06a70ac5a23e6e14021ae15b36ecbe17ac25b0cef019c655d87575dcf45ef6e00720b24840dd51876cd885771f827b01a303e2c543e71a08a727c7ff53fec4a51d0df6a45ac8e8117a800d57e6f3a47a380236ff4d02cc4a70db2b2170833c93ce0f35a00c9e2bacc3405b1c3fe4a03bfae6952285982ed61b0447740e305bf9b1a37cf4ae57d186b21857708d80e9350aa1b13952d6ce62bf52106462c9b9348ad254671c8d4d2ab419de0242b7d15360c1f9dcb2e4b61f603320876c74097def958c5167d03a524e01283588ebe799250123df9becbf0b106a7cd4bb89bd1012143efe50b32630eff173c0a1bdbc5456493358e7a17b66e6c45cb2ee14a2c91cce2f0d9681209f4cbc1aabab0bfb16a1277f52ea3c11043199fc5cf10e58c83335db9f8ff8ab198920f4c3a163e764bf3792b8a456386b1c260c8d5cd0cbfc1c5f599321cc4f4145ef1dda05f736dd5c73f082aa9ac03cd94f92ca47c12a438b64df5b80064d0c30e0524e00b63570ea4d32ad5d731305f287850268982f32af1f1d580b76a70b7ca1bc304aac52b63fd5b115e0f332a4bbc4c72b836f835474d9d3ee62c6e75b57cd8ce409b6819f6aa1d8aa72653b3062295d732a8d12ff7d5eeb234d8926e8dbb773f44f2b4906890507f9714ccbd31249401d30a6a1d5fbb33af8a66f92c7ad0ef71a247eac854bad05020578a7f109d5db38205c7b5e139c01fc5b7062462e520b5870ac4c5476dd59e31eb6a21484c513fc7be35c2f40dd220da1c1addbda0412c985828bcab2ce72ae8d03294b49f7ea4315f771d61769110b52761b8961b8905dd77919f61a7048aad67d7dcbaf7b51c68c706e501fd47c277827d35650b796138401d717b1787f4226f3c8092a5173baa1f0db353168ae08f894b64d12b6b23be567c3aeca06512f4389d3e08a8db662b6bb4c4a2cc407a81bea19e1bd37e44be156f23fe64d7d0101750cda517eeb15053588a8fcfadad8faff65570b2192dada52019280afd2c3693f814700d5393670b98ee980302f506ea9c660e6b5e675636c89bda6ca7dff3326f522b72e408416c5541ebb13021a641dbefe9616f33ec7a9870ff33060c64f38453ba470cb70181e49a0d75ad8a71db3f160903f410668f9e019c18541ee47c20002f6b7187e3f852075f1b65ceab9ab020cd27b275b997c140aee9330bc750b1aa48a4c166475b80adf109f547802e98384f32719f79c30a88ebeec27ab0a12cb549fa8a7731570d756692e97f665d8483e2e587cc102def0fe4e910e19c85885d26f3049b4b4d0f8646bfeca4b6487c922af310387cf0358601d863a5cb782272d864932dcd552768c64894a229975fa3b0af0ec655ef0b5ae65fdc5f7f84482e2ffede79e3078c23db30cc442da9548231fecf051c7a0659a3cea83dfbb0d8d7bf858b6e35187f2a74032e241b883adb1a3c04e6e5c67ab79caaa5d94f0a3948098458db087dc68eb950285ed08f2f4e54bc71c5ea74015449b19591d62d71665539281aba8e1a84bbd289c5477408d1122a9bb84cb36363d091512efa5956e35bc63bc5df43cf22404958d90aa2c2fea36f1e6ef0cdc82128eaf07ca79029dc0585719319cf77b8ac31fb823d233a4e07a9fd0521053ab78181ad4b786ae4c59f062c10497b28e5d83a3bf45bc8cf3c63dee70f1679bf88d543fd24ba8b015cb12532f146b488d93958b66b327740924aaaecf18eeed13b14853e0eabcf347dc8b4c07e43e3413c0ad7b7fefb484a69c195964933865fd6f485be71f209734f35b4d237ba2049aef2291a3b1f73e2a3956a77b6570abdbf1a85371dcac455086b35fe3062a5a0aa7a0a0fe36495462650455853f3b4afae57673e308f80ac82d60bc9aa84fd25145cedc1bf49d8c1d1e96a6496777d590380b3f1c1b24a3d7ccd21a02538d87014cb0c15acd5960bb69f5fb4289da56ab04d2e68107427511f73ff66e646acbe4f083291c75a4c1021aabc4fe0b9ee0decf2c24e26a227b5e96fe948a358c79137debe4072277d12f184afe48a0b73970a37fff79bb156a9c42082261d84154dfff41f6b00bccb7d9f70f0b90c8d30d5c91fa30021d8646d5b435fa40b150905424e50181b3caf1f94f7f0fd7e05a3329c2ed5ed093502ada249fc220f32d202f7d4a8daa0b75658c6a2ab42a591b984e3ea24fd4f52e5e2dc3ffece347d39c1e90e842f4f96951c922e9bec7aa719eb926a21738a007e6b00fa2d68df93bf6320fc2a92b5487a679e34bcf2cd8c46deaad6064c1022f1b451a56b9924c8c68cfa7f9057ae530892254169cc8e384a866c4ea3de60e4a4538c3e0cd6eaca0c66f145eb51b45099e1f50b38ed2273f20867e18dd075eff22f9d95c15f82434dcb0f01f6887a26ba816b1398fd7dc24f980bfb7318f7e73da2400fbf86270d6de08b1d7767878ff9284e3cab50b8859c423006feee1fc18c1eb7f0ba7f22c58d41601cbf2b86db2c9dd6cb0651ae8e2ca1dd98661a9986a347c38360d3e900a9977fda4835cd6b39839ff69a9c60a57e254f3028f765250a1ca3129c4598fa0961b4110145f56a5ec153be969f82912885862ada136f257b346b6a1e26561f46569384bb6ae6d022d5e90dc0b0e56011cdf05bf968c3301c7a7e11126c6ba20dfbe42526e441b4348fcb1076f101707554f63648fb240b7bcafa2cc7c38a40fb552a195be7da2c84a303af81d9b397f1b2b3f79257eeeb5b5db030c364b75fc01fad08beb08ad17eb0580b125f3c6c18ed40d2ca2c9e8b53a935ced0a357317543837f3012c24a85ae8185c112ac2902f79c906ecb83c4f21a8849c73e0cb7ab2351832191451f350b183bb4ebd3b9498d8afadda402a5450d6fd85546580be8a026874d8fb9757ecfd677a2ab11de0a9427221d08b5822951b8b2061d03a2525789d4ccbc55b9239924b70b0546527f583193b1462c5297e921713db5a2bf2624a02fd556de6b1a9814431a803f9e0221138207bb80f8607d6cdfb08de419803cff01a7461f3cc4b18c4891a1cc486d03e8a7d430c14560e70c794175a377b3410b61f40953207236ef353f09f730a5d60485dc55bf1efa94e9adc81682dacd954561f10c3d5d4608f535f9eec10a20b826051da22bf24dc231c9f877d9eb194d474937151622889352e332ea5b8707ab930ac9f2964515a1283a53662580ec30145deb735c246c719268b3e7ce2770de71da617b36fee062433d8f2acc106917f01516f55f1d5d039ffcd6a16722b2caf55a36f64edd01a1c656af7dd7bdfa596dd0d226083fc0da5b164a5f52c46b4c90237142104fef6497fe86acfab8338626d26ab18a2e17561760fd5723451d0b025005cee148025f863d9a6e717c7bbad4a81f4b3515f3fe14023070b139cfdb9be783d85074063e80492567fa8ad91b102140fa089986fdbf01739421ff6dbc83dc69e4501dbcbee780822c793b63a532172f7308306a6672c5f962c3743239e67a2ff740695b429c2ea09655252353118aca60ddbcf3abb7d5324fa495350a89e1fb4780fb7c67534cf18bc04c0d100c8059138d6434b7a61e138fed792264b65f2c1d617129a1ea577f6dc3533147d11ba6c7873279854c6824153537a9ce552a43c647a052c1f566d9fe03c601133bf387a15b960c1e4c964c1c2c55c4ba08bcb14d91a79b13652a590f04efe30b9fd2319166561d894437d13121316fdcd866bf5b4e0b84c22d975b5359aeeef1e5e52b788b7145502cbd68c65f7ca7dfc008706f056a213cdd88627803477bd9ca65d3a2ec500ba2ce346dff1b447a01de6efaa2bf58b27d86ce48bc197198850c2e8af6ed2eca56f70807b2a8bfa9ea94d8d6c0f4bb5c26f4611a785ac600fed436ba7ac9d4a12b3f338890e6e7a4f1d2425e44488118246b33bfa5edcc0f07b76e744fb672c03892ed9ec3c0a0715264c493dce20c972c6f5992faa3a2cf07dcea885aec3e2c12e6588a7fd2b551294a39e27a9a56f81bf4fb496e62228ba33a4ab3d9fd455ae65cc7000e30db1e363a60200db430fc5eb6cb0bc035315bce034f4b135dc94bf51140cf65ba995b9bde215227b5d27015f59be4fdf81cf9b86302c389e819eebbdbeedf4d0c7c42144b9659336c2400b111dbd80cb7ba1145d21206d9cb807696f49b1504014babdd46bee6508a8826b1f7494f92fd77f4c288698c23d26e5fb78d9cd9a6dd9790bb11aed3cd3eb3c7f444216eb0845b8a40408708080d45bfbffa9153074b06dc2bc26d658cd63971e28b4f690603f373c7340e681751f0a06f4667ac0a186250bc735b2ca013a19f13b3caa30b6c18a0f22e816f89d135087116e2a272986029c6fc3af8e2e22672c526b9d67ba2b9ea53b405ca8646965f8d755c31bfeba29d7663d59020cc22ab8bae9a93b20ac5f825a3b352e428f6eeee48ef46993ff1f76f08a20ca928132db2f6fcef00140478b5adafbee0f536484e4151545154fa94d829e1a285a0449e1a4b1724c321f9aa8c9c94bb7e0bef1463ba4f6ca615aaf8dc23a9fa4489a2ed319db3e07967f68aa93c1bfc0a5af3a984c0703fb53f0a3e01eb930d7d784db81b921e80f8e06d84ae364a9733ab9799c32e955abc1413ea0872239c314669f2a84b282742962f15030dd9068b6526921119abf60b5bc397ea8c2ead4a4dabf681ee16114dd8fd656c9100183df9c134d643ddf1cd62d1e387174a3a58e114ca405d38bdd8b681135ea3049bd23a0e46f405eaa831d5b1fa1cc6b1c7f4327ecc735a18469bcb54cd2f39a011aaf74c5589a44b2cb11e51215dd8054d75e0d125199a4ad122d97d4f93ba4442d3fa8ef8fc1aa55f7e0e29bbea7d20592aef01118e4c5391796a48b9658346702060ae917904accab3efcdfa586c920753be10c94053852bbac6dde80531cfb2764c8ca199ef7bfd6d5e5f4322ea62814283e2b56061c9e5342aa5a9f8c622d4bec3af760e2e2a4df5fb27bc08fb62f34d23a32dd9668873a1102b9a15c7f5a181bde932fa4741a1af5a277d4415e3b47c6c28ac3a68b4cf660a68ce3c4078afa9a2910dc2f52b22f189bc892f04c25bdf86aaa77b6ba8ad7d96df593a235fa6d5a91047530d462d2f90866a401ea1a4d707bf29f7ab57e7a71914e4549cce8cd66faa0fe4be2bf3beb33c2d28bf7e4e6cb60355612a389fbc4160e987d86746844f2080ebd3c1d2c454b1db8ab7557b277b39cf6c3fbd7e6bab1ac135de4fbec4e2f9c3e5d3e9d46a70f0a5ea0d42e7bc79af7ff10a8956f8acc5b9974ab98b199e9475e700b66725f9be549d64f28ae28bc98325e7be544247a34e9dacbc70df4671cf65ee74f1f1ce6d1883dda3682330eab8d04b454a4cb1ca2fb30446ef35839d4b08d6832c1c2f1e571c54096ac7bc02a3dd2f0e90fc17f72abf0e6a4cd37153904b75c66958e5ee8b22c13b7743514d475ea86f81b04cef782ed5d1b0817d9b0a43b9fb3f9e61dd3b7ceb6d687c1165c10ab25495b75bde17caa0490480f2338fffdd601dcb92e1fa851d5371177f54dd44a69aaee082d2f0915e884f847821884af5c90064a8d9475c514e414422ff051028f927e2f7a85451eb902a92011b727c88aaac3b4cac057854aa2e5add55c90beec8cd1528f32eb0a01e45a5e2ac2fa9721a40fd7d300ac741755932aca8a1d14a2ba9e42b3aec49b5b32855dd0f373ac0b981476afb82d51a42758684ab5206ded33667b68dc008db3adf934a1554a35093bc7c31c64d509e06f281b827d5e06966e50b5de128823083d0bbde4a49bc6381b85f3200944a493bc9ead2bf756dde4662c6151ad2796ed820054ad59a75d4a0bb1825263751f7201e14b7203d36acf1de8ea7702ce77d51816537a86994d15694ed74a251cdbc875457f9dde6ae6f52a90678b58f5473dfb23668253c28ae2fdd53c91c8e494984e426a9722ce2a7f69f611387d7aca563c7245269d20edc0595d1de462c914acf43d0e1097778965231ccd465a29df3261c98e22941dbd0394d55bac7586827750fa4ca85800e6f8454bec50bdf1efa7eb725cc171605a994a03c5d09e96c7be2b85ea7ed375112bfbfc2df47db0ee9746fc9f0750edfde1dcb9b85f4f3d573a3b0ec3c3be54439fa0841fdddab26f8f0500a3ab4ea36a10848597d9aa0bd8daf013d5c648c49da05f4618dad04d387fb75a3f25e750415efe29910e46775fd3207c10b67a950e320d48e93d6a49f7590f393ce3cc48692ac539df25bcbc77d9e3e2dfedfb0d3e47bb0c829472c85f34fb9f3979237a89c5c68f3e5a172697f82204cf9a6f10882f09d829f6b01d2f0433bcf03e6b2bf574a5dba1cbb2495681908610d82c4c6e34e47f9b79219641b5d355c8c124eccda6ba6379f3aa0f68b26d686aaf9d7a119a37ac57c10c6eaa1cb49864512ed63f1e18888390af7727c2efd1c716ef1e1e0133c11962e6511cb1761def06bcf2b9099d18d4392c10db0ee2964f40f93cf3eab70cb2eca0cf850fb4c632c7f44bd6a6f62ebfea06dd0aeab52c6acf348ed4fbb4ed480a061ac1304ceddbcf8a1583d9ebce8695ba44e2286e5dd310b6facd97b78c5f7ea4e44b210c60830e91ce06c7d7f02cf7a50b7fc211aaab2c0f94798073c08fa49664edfb817192bd7a1050de2d112403e950b4ff5d461ba2f6798f9ca7633704d6de214a664de1831d270b7efcd618703d41734f68aea253d7fea825316559ac3f16b511db7e9cf7d98c9d8241d8dd026b7fda9e41e60f180f32793bae38a9bd4284732b56291960c76b102b066dc9201c6d8abe4f6d4e0685d4523589ef1b4c3161b361db043d65d9124e9d6c2b6ef78b7688f0148c2ebd297f478aa7e74f04fe09c3e7ec14f43a3540dd8c1d2a506be43801d2703a12820535dccbc8e3b2f27d431ab2b6be5329767ad3c3531390e2cc044c63fc6a98ae037e0c884383d1110515ef7f2d252721194e49622d1dc8716398b8802b5219c84d91500ef8a90ed71084facdb025faca14d3b1b78f1f2a17a27425def4be7a08766a3d39c502e763ea4aef56e39faeadad1ed689e39a28266c3b893dfa5bef751fa81ce8be1dfd18d83dbb73bd947597d7b1aa266926ed80511f994db2f0431d54e21f772536da2810678175c6e8a4dae284230493a0ceaaaf81252830a95646b36e63e65b9b99f9ddaf8bf45303f630f9eb22aa375fa4084ef81dd9ca03a0fc4d8596aff3072e7ef23a116f0aa06033607451fd441ebb65b74dd7d14969522c520f4cd9836200a50c43d733c61437132ee674f06a429f8d68bf160ba680e4c40ed55216987f10832157ca427ffbbeb185e931b761ba45026e7e90bd94242cc7e79ddc6a8dac1bc7aac23c7ee2fe5243b76e555c14c5aeb85e48c2c50f02efa3c1890bc799030d9f055b83760507686033e713c48e2b7792345161798306d33675a584fa48ddb27b98fc030a280e6f8d416f10eb650da69d1129d1c88045cebd37c1baddeda554309d2780186814550aba76f4bb6e88532ef7c1ae677cae1da9b11536fb87dd33f02ed7002dbdb2542c53e72c45631eecea58e0f171dd125b5ebc5af392f26dcefea5bfe451ce57847e25e1e81d0398266e8c1c3a139fa22a2717e810519250a184024312ba77e2f0d15451b6ca7128be04011a19203cf3e7cacdb86058e3c523f71f7f00f07c866c260580eee6fae79f774a596b9e1073e48bc33d6db0922d3c2091e729a111f47e677994bd93c3ad80334ae03c99e51c16c61fbcae5c8b37fb71e229a974caebce3c04ca2d8bead6639f6af8e21607165dd813a9efc6c8b5c8f82e7cb5e41a72d0f735d5412e269ece67fbae62f44c677fcc7b9a8840ac5c47abaf34bd347fe409ab4ffae8a165b9a376258469c3411794c26d40d258f8cbc93acd116c7019bb90260a0f4d297b352cfd00f2cd8f19a1bdef5d0932248ba44a6906bcbfe355f2fa35f0947b3626ac4081f09999c4ca359ff1ac45334e24e7d31687ee3760c68ec21fec98d01d9603e0ed54f3d74b4856fd07218ff4b1fb4d8074485eb139181c9423f14c5e1b1bb3853a2eab2f82f41f797764fa9b0bff17c461d291e288a00aa94b1de77ee5830c08e65046cda67e96a9064aeb96e5416d6ea3c2719923dfbfb7bd2c345acefe77be2ea2120ea5a8b697cf93bb66277e67591d52a538a5a38342e269ca81a70869a8895d62a5c68e07bcb45cb5c5b95ba610f3f746ced01525cf6e632bdb78a492b3acec7aaadbd991aa370c51f8b329e8b6451fed908683d60fd24b2334f3ea14ee9fa68a379ebace8beff862fb4d0e9d05402741f1a55fff7d405e6fd57395bbe8f71912a69e18a7735e91ccf2a25242bf48d4745f75ba9e65156bf79814fe019eb9010e8303f465d242d8fc51611ac0e362608deaf579b621519e5e9ae21c54bb604b8ef04e06b8d095c47a192d6d3c3f512cff0822eae18172ae0fa7049ac5479c562f7aadc8a74f168bb99c29a38fd595999379bb79a6e9ae3d35fb459e93cf311abf6fd78256c841868bfa43e849ccca3eb31e8ee691ff9971a5c74e9894ce3c6446c2639703fec01a72a47c52dd3d4e4b9773ff191393ede27a12a9512d3f58399cb0ea92aa3c5f59308930a949f7014ed3fbd447ae8977c9f7ff68b185a5444ea1ee50969a3b5095cbd60a26743853bc5e6e7b45c6e4a9e663d4836b47282bcc6448263b04028f340fd175a08fe8df226bf6a30e816c866b646a256a2cc904e661d228747cb2fe7f79ea5cf0d243e57a86d32f14a5fbc48ef4e2f6a6799fee6dbf5d86c60c91115e28881d772490a8e5dc15e47d5e8d130f25e6cb45e26dc434fdefbab81d4c1f17b796362a3841fdb97103b9e50e04ef20ad3b159ef08fd458e8d05804f3a3c9124cdc6ee6143e467b30dd1eea7a016fe9ab2e31613034037ceb94f9a854e24003f6fbd22a208a02252f829b43a728632169614d1c51c5d2117b2e222b52b7c868b2e19db6d9171943fc6132dc1e48732de7d0c33408ba5eb3db651f813c77e96394ff624bfbe5e42d42085b807528fb3998b3f12940f32f74eafdb3cc0adf2b706b4677a071250a4c8a19f72270ab34e450b8cdea3d77d6087132a08674b9cf5b6813e48301587e86576e7b13d9c03d0b8209903d9203a6c0e86a1169451e61532df2342e73348e9993c408b4553c90ce7fba5923c4060ff215e6736e1e5ff411471f32af11c4b1a24cc3e30643a733250dc3f8784119ad96e39423340690f7e82986d87d28942d3acd12679d5de301f0e1a57de70cc14aef330bae114d83dd91c7feb682ee324b9e427005b24bc274d82fa00c7627892d545df3e3a1ded3d02a87347184564008c74b7304856a68c84dde73e31ff6607233241d36ca6ac623a3acc07c8f670a29dfb587a52823e45afacda8574d78c3bdb05736fd8d186d61c80cd4b6b851aa6f688db7c1be71844439a190dbe97f7e6f864d3e64abc28f24ea68021b710ae7c31136d8e2129d5c59989f4d83de4e41fea20c8774303af1901e5034472d5e2d0c83dc1bc06650426804c9cefdf272d77fca564bfa7d385361aebebf435fd72522f4a36b1a1b2c8f60a0087aaa35fab1c4395cea33308945d6dbc9075be4ff8ef5398656178f8d0355c8f9751580d8422ca59cf6f2735049f76f62703ec742b8a2d09a7dddbc4903a3e3182f15a80e17bfd96187699c65c247281f19f2aa893f5ae9b2eebe293c6e7c5df51c863c8322e929d24fe046fd41433bc2b2a094860fbeb06df79e8300cac82031aff96a0561040cba83a55b3f60885c93f0c0eed7728144479357d5ea524c76b1f189cfc7767c80656640a67c85c2ffc016ffc507cc7788dc335b63e91a590743ed40d799bc7babebfed448728d650603a16617dfb7bef2d659232cb09b7093a0aa6d35c32bf54475760deccea9634d121b33aca533e6bd2f469594d4fe957485695a5a3545398b80ac447be4e56206630df989fb9ddd88d89398fa03293bb31133883fa2b4c1b69d623a42fe2ad38f071c9d76b54f588014955d8a5a305508142430750dcd248e7da33a16802c31b358cc2e4eb3a6a68a992affbccaef3c03e2330b2ed275f35b45c916d2631348366904bbe36f9baf9a5d3bbcb5d1a1121b7cd532655ed11ba4d55df24451fad93df2e4a0973bb0d7118ce9accb56cef956ed4d005267627a488bd9e38e07762031337399da061888393464397393295529b94308b454e501216c14ee61007270430939c270c9936acadf0b66180d3a843edb220c79d159889f101295075776d72b42740919dd6daf3e3a869ca542a1689ee4e014a74d475314c4735d076beb70ee6b6d6c6b88ccf5ae65a995b7b3165636fde95e16b6dcdbbe16923bb5cc1d24c171dffac67c645773106e7232e8942b258b9a8d034f3b4c1cef50d298b569a278a9e124547159126d4cc5b5af32a2d893985d94857e634bce6b591288a33d6f733e711546816e9c644a51b133d7c7bd4a8e8326f813726baabb2306dcdeb2c9d1a756ac3d9d2a9cd1e35af53dbee8303ecd72b4e0512e2c8918d3a0393a89338628b4312638c313ea14ea63bb6f8c3c2b455a5264e7068e16ecc81d151af32cc2e7ba582e8d79784bef2fe950ad751d751ab25ed296f4fa1bc3fb51a62573de4ce35cffa64ddb4b2a85fd19a9cfa939037368ea393e3382ef50350b5559e2b15488ff125e3e831d6cb292b7b7acc39e588052c29cf29a3c778ccb9b253488ff1d295c478e9d6cbd24de711282ffd8894c738155aa5551ae94226f74148f4242697f14a237f6543f1f1753bfa92181f4b9fb2b27e7272247d65cf252727574b4ca3971e3669f49a8f4a994ee3451ac91c9fe904075817d32ee1c6bef1f4c2b491d97432994ce1189e629c42710ceb8be1189ec2303499c8314452c22449f813519556bda29c88166772515cb22651144511259e7cd6a9476f4c3c893f3ae42719555288e26a874e6d39513a15dd89288a4eae722a10ec3d918a6d5c83aeb49b1ac5167d1c451b8ea2b5d65a1339bbb124e26f0bd356954827b687a5c06fe391ab75b2c750c4573cf10dc3300c2120866118866118de300cc35b6b1886e10dc3300cafb592b3250cc3b09a35b0b1f8a7aded431e7def45f18dedb75d8cb1d96feb7edbd97eaf779871631c862a8c71ca92b5d62ae3ab2161ee6cb3ed6edcb8a63bb4d97c92c9917814c910e39014a71895204552d4824352f4c138144d61188a238947528b88b138aa522754883a85618847524b2862d28728866e7d14458c7b31682143d18ad5c76d8c2f2a954a6941f5687f42516549129bbc9ab498ee9dc274dada83f4daa9b5236922ad965f795b3181aa2d6bc971249d8a7932351371042385f41f2533a1cf9444da9bc4a65194d23f4bde8688b062c40c0d0d0d0e530d0e1c386a489b9a9a52e87a8dcd08e4e7748641ba2d51189d749af2480d367f6246d0261c49a0121026f707bb933842b03eda5137b614eab199316d6e526d8358af9e637db4db94a72ea6b58d31e511234423d8a63c82c53235044775c39a1c393939a669cae4e810d8fab9222a67b52a85aeaf72788ee4e8a4c79442e4352398235aa0121026f747a7140a5d07f713620e1a98b7a11f1b8488ae928df819c28ce04a2c533bb277d640d5963d9d7c7ca87c94581f3c2a1013f641dd945f549bbcb4a71c27ccf147c98f9b4e26b5477b4e04b0caa782c9adf2c138144d61188a2329a65cc4a4d731570fd12a2b3de5a9b73fa39b50579935e50d4d8e732d8fd86cc2e388c9f0da11859bc3f17451a9540ad5d8ea84aa712ccbf222a93d30bea7564ea8b23eda636262eec9aa625a47d91249984fa735dd30d74ec7fb4d7eae0c07950df242f5d10ee64b0cb547ab9894f5a6c33391a31a662ad7a3041449c03ce57a94802f9a480016383060a2723d4a00145e20c08d1b868b906caa54686e6c2fda83a2b11ee80f104818c1023cb0bdb5da4a9a9abc2d9d9a5a55280e4b9bb0b4d921c35a8e40011e3f3a35a912b5016167848b9a285a5610765ca9b6412eeaed8da226084e31144563288aa2288ac65014454f3b4328149338456ff7bdf7767fdb0ed2b6ad0f20b7bbfbec768c316e615b6bad75076eba4962d4c3c8e130d2b0c2175a39000da0e81581a1054680972d72b896a54e2ebd3e20975e966ec62097ae42000572e9a952a84c991ec8b69a8c729b4e463946a1c4b04046156991513938991d383ac017a3d7df987a163d2c8544ac864d87846c6be5300c0de0258757c8c8e151184f1c19c001686d830d9f955c2b068ef2bd0520cab7005df2fdf98e02a290410f55844d0220c55a607a0b3d224017310f605b6aa704b9fb80c900d34676196e9e74c070006a343281d143894824181a488543d4c08d6088c0848d4430945077080772211831207b08971c06630dd2f66eac92160c1d8c76c806743756c91218015326573470f0021c0fc041f0868b89238a9b304cf28b1a2f0e60330336c648820d33da6003a90b3692682c6086b9a2c18609c20cf12a901ef9bae8802ac3b502b15ec533ae0c9f22eff2b24cb6893af1567b884988b97cdd264e205ff109a51b233f60b66a0f914746ad402ca945a440be5eab88019c5cb38171d70a842369245a0ab1c9e40a240e29730f62981c656ffbf5244c8e6859cd102e62b755251caba8f55baa5069a2fb9c585f1226378b6bc7887163a3685f67d62a2e4c5bf5615e9882de9dd25316a0a41d35ba0db3b5b836b932945fc76eabb4ac06e764882b2375ac645c97c8216e05e223636fe217ad6d329d4750a1f98de19f1bc31ebececffe22a972a64d2763a70d0d997cf4a13209aa89931bc34b35e833d116ac8b697be8f69de9dd16bf9089f9c2b4b9f7f5108c3072b822532b31a705a699ab069220906da459d368e86648147aa852e510d71e1315480b96b12d8798eb75d520d8fa78a1c63026bdc54281796d1057f5611d33a93db093de33561e95dbccd3c99347efd3c6936d3d19db3bbe0f368b222e87843e3afe8d67abf630b9f83ba0590ccf95d73cf68ca70c3537d9c20cb1c34660dec61d8c37108357777317776f9987cf4f0fc9675b5abad5e54a00498676cc3a962ce3513e21c90beee9e9e96112e2766119afa0e36ed4e5ee0fae8773e2237f4ae34b099d160f9f9b298ee2cbca28ae61f200fad4245c35794f0fae07e7c331f9e9c1f5e04ce7ac692e1a38a9c1d07472c900672249d38904a2586f98368bc3e1aed5e150be1d2ef8a248690c920dd2b501df98d4325b5b0f26c797192ff99abaf0e0d23d4119bc7cb1ae25b28a97117849dc256ff95625f426b13d9267b4ebed90725677b3bb5bd12d4a50c9de942c4ee9f5c475e57018032f4a27e8d8608faa4b12b9438d590f3374bbdd5a1b10d5185117981b91e8a6ca5131a56a140a9268dcf57c54974c996a4fb3f2c2d3744c9ee6c2d3963cad85a729791a0b4f5be1692a3c2dc9d3903c2d85a7a1f0b4139e76e469263ccdc8d38a3c8dc8d3863c4de869c0129ef6a5f6b425dad3c0e4a705674f0b13f4b51bd0d78648f81a4ec8d7aa04f95a0ec8d7ac8cf035a29fafed645fbb927eade8c7d77cbe26c2d7b2d440f85acfd77c7ced83aff5f81a8faf79f0b5a4affd6a5dd0af297dcdcbd7805ffb82f2da16d5074d669d355dedd13e3a1fdd9501e1ed3ebb2b0bc2db7d8aae4c086ff7e15dd910deee6374654478bb4fefca8af0761fa42b33c2db7d7c577684b7fbfcae6c8ab7fb285d1912deee03bc322adeeeb37465f6e6ed3ec12bb34978fb8fdb955925bcfd07eecaec12defe23776576c8db7fe8aecc32e1ed3f7657669bf0f61f4557669df0f61fbc2bb3386fff617465f6096fffd1bb320b85b7ff40ba321b85b7fff05d99ade2ed3f7e5766a5f0f61f4a5766a7f0f61fc02bb35478fb8fa52bb3396fff11bc325b85b7a7b72bb356787b8abb327b85b7a7b92bb356bc3dd55d99c5c2dbd3dd95d92cbc3d2dba32ab85b7a7bc2bb33a6f4f8daecc6ee1ed69efca2c17de9e225d99edc2db53df9559226f4f7f5706c5db53a52be3c1db5360ba74653d787b1abc32eb85b7cb6eb22119aed3186f975591e53a35bd5d66a5d3196f97e93aa5f1761951a738bc5db6ebb4c6db65573ab5f1765951a737de2ec3d2298eb7cb789daabc5d96a5d31cde2e33ea34c7db655b3a5d79bbacd7a98eb7cb8e3addf1761952a73abc5dc6a5531e6f97f93a756f972575fa7eb24e18ef4fa4d377fe4ea513782e9d41d68d8563e5583ad68e55c4e2b18c583d1612cbc7fab1945840d6122bd8bab570ad5c4bd7dab58a5abc9651abd7426af95abf96520bd85a6a055d3717ce9573e95cbb1b7315bd8be7cae232726d71f55c472e24171797cf95e4fab9bab8945c5e5c40d717d7920b8c2be80af3bafdbfce7fe11aedcf5f1a6df7cfc0cf5e3e2b35dabef359e7f3ef7352f67de6f219e9f351eee52dd908c7e72c349f79194b2eca57f22e13659d179f7bf81ce5330f3f83f2b325a29f01bbf89912173ffb6df1339fee67485afcac97c5cf8cb0f819cfcacf8aaef8d9ce8a9fe9aaf8592ef7331c153fbb4df14141293e68a9ca0701a3f82025283ee8f7c407f9701f84e4c407f59af82023263e8837f441454b7cd04e890fd225f141b9db0751f9a0db8db523f140c11b6b9ff2404b473c90110f54c403fd6eac9d88071ae28184f8201ec8e8c6da817820de8dd94ea417aef6689894d7f9e1757c789d251d9c132937ea6d158d2e6cf5a84a6e5ddd0019b91d974370649d1b1ba60dc5d1a1583a15d12cb97919c592516a9837dbd0275f69b60cadbd3df927a8696c31c9b4644271dbcc0d6504e56bc11b43198563925971df1ea2f000a5872824699a3696cb1dab121b2a8562371876f027e74ac91387e250ce239e38151a0a2503f3ba2dcc3ebaa5dc23dc4258c3bcba5ab0d17ea2e4441b408d2768c66e5716c579701b0925768bdd723b8eeecaa29c403c902494b3de188eeec9891302b3661b10ef64eb8db9fee42b6d005f6f6c87bf9d6840bd1b6bdf6103d306c4d3f17208cef7547da5e5b0617ab60101f18078b9bd7220972b0b091f2a02688b85892d2280c32202b71b1398630e6a5e60d2b800278b0968b4004102ccd0c00e8d0c9811019c2cccc8f2060e53a11d5362dfd478917aa8caa3c7e601497a2b469a44277d7e5a3121f9761efd3a2a1586e7e96f88b66248b97b3c7889a493385f2c8aa4188aa493e469b30e797fac9bd0d345491425d1134aa2e809754249143d89cf45f7ed6edbb7bb7beceebef7e2bedd1de2ee16bbbb556278efbde7bdf7dedbdd8d31c67def256d775b7bd67b2f3633c656d58d529181326bad350c7dc2100dc3300cc330bcf8e2b36badd5bc169746b66bad8d313e4d6da046b1d61bab2e62aff5dedb395b6aad18bd5db5e0f0a7d452c530c27a5d1cc791c4a16872d3353d11c3dd0d9b419a4237e1130a87a2bde478c5d04f1895c2373c99c8513c99c8082f398ad88cf144a2ca187cc393491cc993898c511c45d28cf08e223623856f7832a5502994c760324e2632c28bcd2885ae97384606dff0642247f1642223bce42862336edbb16bac8c178ba38f6288b58c23b6a3967114435cc62886584b88c511876219210eb1a8650cb196712ca38c51cb388a212e631443ac8586ecbe58aca66a8a924d6edd92d6a656091b8661188a23dedda23801de420f49d34d527f3252887944e4c12d5ba2d16f3a266224a9e18e68b76bb7e34d779e36cbe5a61bfb87573e7a499390183efb2149d274b2e895a1ea58df7c31c8de9646fba8636d83582d36a93d02a1bd19895509c71226266dee5e2b4cabc504386f338ab2ad2e5589420b0bf218828c841c6a3980d10bb2898f8cb2602514d96918f6fcd45aabd7dbb5d65a2f5965a16991e86ea0ed994657d6b06dc4762c21a620b044fe00026464d114552574aa324d222a0550430104288095161c608886c3c8d8b7441b198f19638cb159834a23cdd0d6e8b478d826b2cd4748168faaad2675c9a2ad7a11c56cab6176b2cd0ed92a59244aca367ba504a2cdb2858c34b2e8a293a6cad11e0d64d17f66459234e0974567d20497457712c58a2cfa10658071cbe205ae0359749bebb41e3520298b6e75bb2c66144dd1693d72c00b8e1ed000133891ab00194319fb3dc11652982fde09b29408e851a2c7e822031c2306420d18030738483c2984b2f87859d00839e0c72bc24144034bbc08d810e1c2e3f96288a8d113d3448f125da3c50d2e380a3053b9a2e14609b0b8e10218b8d1460cdec0d96c79e30ad2cd68008f9e2e5cc0008c8a29af4c244fe3e9644f23ca628c718953e5a7ce23a8d0d0540ff949c4d08a946d6c74cba257201a8f898ebaaa5c356a57e3363e6b9b1a2e2db33aa44e431b1ab7755216ddd63c1c8e4275893dc6975cbf1e737d4accaa8db298a47737263a2dc727ba151d26fe8a16f3ab3e572a602f975ca739a7c4f8f5f29cb20025312739c5e4b29371d37b7765a6cbb8e8d674d124b31b5da765a58bbfdb04e8225bef804aa813a97772f289d41e459c5c9befdc8d895ef33afbbb16c76981b74645a779bb7463e250c7c4de89bb5def76bbdd99a4f49dd16e577465342e8ae24e9cf9de95464537df22dd984a0bb3a691b1f0aeecaec4f4fc0c21721bbb1c03555b49c2eadd4bb6edd0124a7765abd04fd694f192f1744e69d2a75c6b79289b1b9dbc32d26772a55bd28d2caf3c4fd569734515880fabc3b8c88de1176ded9e3111d2248abd8d2a9d69ad589d904e6d9a04f5b499cddd988e57a4845ea5f55d226380b4d4c668bac07c62721185aaf57c2225cca67285d3f904bb58b30a28af3586cd4c9a4227737857e189c946f38a00a60a1df2c6c175940e6e632489a1036126304d1e5e9fd9d2e9c9af8f7e3283b67eed82cffacbe219ac391f73d7ef7099cfc15feff2d3e6b37b1fa246db47d768db14103fb814b7440f848f1fe3f759445d70b185ce6d165858b9e2ad952bdc5691a3620a29aa4401c51338279a606268092592b8bd4dc2a9bcbd39124fc5a73c127ec44f7123fe082fe28d7022be081fe28970217e080fe28570ebc50711e57b407d0e3e3c147ffdcde70fbf80d1cf979f2e42516c2886934173507888d283f52208218620a208238e988204955b124a2c31c444134ee09e80228a2a524c4145ae0a2baeb08245165ae8ec16960bdb85250282157383a3ca91b3d2d9d1c1e36fced0e0a8b139e1d02a5337d8d0a4061a66902106185e60e2c2921694b0b0820a4990a480c209474c305284c810a1126ab43c0b02d281042141808cf0234b7ff8881002083d3e3ee8c1c3831d31179a43ada03a191919191919195c0eb937055e05f2b0eba31bfdba2d76eb9468e701d3762249f9c107202eb62472f3e9305ea73a9c3e4821b30ede8ee23a75a14ea4d377fe4ea513782e9d41d68d8563e5583ad68e55c4e2b18c583d1612cbc7fab1945840d6122bd8bab570ad5c4bd7dab58a5abc9651abd7426af95abf96520bd85a6a055d3717ce9573e95c3b57918be7cae232726d71f55c472e24171797cf95e4fab9bab8945c5e5c40d717d7920b8c2be80af3babd865eb8579557aed396b7bfacbc742fa2d7ee75e555f4c2f2e2bdb2bc8c5e5b5ebdd7d10be9c5e5e57b25bd7eaf2e2fa5979717f0f5e5b5f402f30abec2c06eb021180e560596835981e96044b01dec0aac08860596056604db02ebc18e6048302e301f2c09f683758129c1bcc080b02fb025181858101606bd75ba03add2690ede8e3abdcb73f8f3e8dc721a9d594ede89e52c3aaf9cbb93e8d49d56cedc59e5c49d43e7edc37cf0c1fcd27f792faff45dfef749ef7b2e8ff447dffb2d6ff4599ef758bee8affcee895ef7563ef7551ef7437ff3301e7430bee45f1ce85e5cc9bbf8cf93dce75c1cc98f1a6dd479f68220b38ccba13c100fc808a80784c48ab9c151e5c859e9ece8e0f137676870d4d89c706895a91b6c6852030d33c810030c2f307161490b4a5858418524485240e184232618294264885009355a9e05019120240890117e64e90f1f114200a1c7c7073d7878b023a60329038587283d582f82106208228a30e288294850b925a1c412434c34e104ee0928a2a822c51454e4aab0e20a2b5864a185ce6e61b9b05d58222058313738aa1c392b9d1d1d3cfebd23242ebe2494ccf9507ee7b683dbc9ede876763b453bbc1da39dde0ed28e6fe7b7a3b403dc59da09eab8e9c0e9c8e9d0e9d8e928d2c1d361a4a3a70349874fc74f87920ea08e251d419e1b0f8e27c7a3e3d9f114f1f0788c787a3c483c3e9e1f8f120f90e70bcf120f189e204f18bff990e3bc8ae7dc8aeb9cc877eeeeeebea5519b1a1c3433e63b8f8e1d9d554e0e15ce4d0c2be7322edf824b40a59f0fa967c42bdacd74b3dc0c37bb0597804a3f1f52cf8857b40bd205e5827041b7e01250e9e7450f517880b27373bdf71e78be4e50270c1ba6ed26884392a679f3bb01a38689baede6877ecd24ec9d08200022afd12b8d36bac317cd929b883f79d51426e929f02a9096df04637cf8010a0f517ab832ebc51d75c8e10647952367a5b3a383c7ffbcc1862635d030830c31c0f002131796b4a08485155448822405144e38628291224486089550a3e559101009428200495d29c2c2cb62b4a57784c4c597f4eba2e405f865094cb016a6d3d2db4bb85d99fdc2db4bc075eac445d8b9e5d6655bec7613bcb29b5fcc4df071f0201e0817e283f0215e0827e287f0229e0837e28bf023de089ff24738123fc5a93c126e6f4fc56d126f6f6e95789b84db25de2ae176e8ed126e99783be4b689b74cb875e26d136e716f9d70fbc45b9c5b28de3ee1368ab750b8adf2360ab752bcade2768ab752b8a5e2ed146e736fa9705bc5db9c5b2bde56e1f68ab756b8b5f2f60ab758bcb5e2368bb758b8d5e26d166e756fb570bbc55b9dcfbc8c9b1fe3968bb75b388e9f719a37dd76f1960bb7791c5ef3346e89de76e13a7c8de7f036fec35b226fbd67fbc50ffd100047fdf03a68289bfae1533454a77ef89286ba29279f8307e075f01f3e8656fa0f2f436beddc7670ad736768e7a6c3fb10f9ec7c78209cf537bf9ee5aec7c1d17f39ec5dbee3518f3dcc79fc0ef7e063fec1f3f01eef81f7fc07eee37b7808dfe320bc0ff7f9105c8407c1d3f7f11f2f82ff7ceab2ffe1a9fff1115ee6413ee5407e0427e183b89007e2414f8203bd10cf1fe4b307f2da67a7fdcc85bee6253ccd89bc900ff912dcc813f1223fc48fbc1137e18b380a7fc44f78131cc9a3e029fc09aec223f1249f82b3f02af80a9fc45b78165cc9afe02e7c0bbee495f80bef8233f9251ec3bfe0303c139fe16370191e06afe167701a5e06b7e16bf0264f839f6f83dff04ddcfff4ff1b5cc7bbf3fcbbf5c20797e23aafc3779ec77b78eb858f2fe3f7633ce7757cf53b1ee57b70d5e7788e5f390f1fc56f5ee5389fc3a13c0faec3df780e8fe33f3c146fbd8e2ce51770dd875fc0e82dafa5d04db0d1cec15b6ef3c1e5761f5ca7b69d5beb6f72789cdb1aa60d8867bb09e6f69ddb9549f1f61ddc95fde0ed3bb92bf3c1db77749ddeec63f4c5fb6ce998cf961b201e9011500f0809c807f403520202022d0105836e41b8a05c902e68175414c40b320aea052105f9827e414a41c0a0a5a0e0ec36c3cd7233dd6c372b9af16646b3de0c69e69bfd664a33e06c6916ccb78ccbb9accb447997afe4a28c25f372966c94b7e45e3eca48994bf6e5a4fccb5db252f69281f94ba7efed79a9d3d3db33984e6ff0f61cecd4066fcf613a6de2edb45ba735783b6da8531abc9d86eb74066fa755e954066fa7e53a8dc1db69563a85c1db69ba4e5ff0761a51a74cbc9db6ebd4056fa75de97489b7d38a3a6dc1db69583a55e2ed345ea72c783b2d4ba72b783bcda85315bc9db6a5d324de4eeb758ac4db69479da6e0ed34a44e51f0761a974e4ff0769aafd323de4e4bead4046fa7fd3a35e2edb42e9d16f1769a52a744bc9de6a5d321de4e03762ae4edb42f9d96e0edb4a54e6bde4e03d329cddb69c14eb3b7d3c2743af3f6daadd3206faf0d750ae4ed355ca724787bad4aa742bcbd96eb3488b7d7ac740ac4db6bba4e53a70f299742e696b7d7883a1dc1db6bbb4e7fbcbd76a55399b7d78a3a4dbdbd86a5d31fde5ee375eae3edb52c9d8ae0ed35a34e43f0f6da964e41f0f65aafd31e6faf1d75eac3db6b489d7ee0ed352e9df6f0f69aaf531ede5e4bead4036faffd3adde1edb52e9dc6bcbda6d429eaed352f9dc2bcbd06ecf4e5edb52f9dbabcbdb6d429ebf4410a9901e0ed35309de28074c3a55396b7dff89ae583b358529cf583b3582cc7a510eb3a14a74243995e2879429353a7a71c85c3a355d021f4060b030bc2c0c096605f604098179812ac0bec074b82f9605c6048b023580fb6056604cb02c3022b825d81ed6044301dcc0a2c07ab02c3c18660b7579857f005e6b5f4faf202bebcbc945e5d5ebf57d2cbf7e2f2427a1dbd7aaf2d2fa3579617ef85e555f4baf2dabd885eba979557ee55250832b31c008f02f140bcfc252fddf0198c0d9f834d3e87a9e169371a9e3634c3d370323cad4a0c4fcbc1102d1e3e3a29d38c9551f54ceb3eb51ef1284ba1dbe50caf8793fa970f76774020100804026160491b98dd864a09bf5000c75be4e58b0c8ede9e9e32635d8a095cd1e92cdc0d062b856e88d1d80e1e6314e190bca2490155d6bfdb00232323a3d6e97011c6dd58bbcff7f329555f0d8c3a07b993c8ddc0beb83bd4e8adc38069b41bd8bf0e7ac0c8c8c8a881fd821a6bef1a90f8b4750e4edb4da2752f07605c3b49e25a0d3c9f15dbe1c189757a909a91c204028140d3f589ad83256e14454545a48f29247864023353c3ec5031eee5009f78efc537bcff448db557416e6112a0dd4becb3c16030a8638787470ff287baff7ebf9fce6aa7c707a391ca34323232cac9b1fac0474a774fa713985667752a9c1c3e7c9442b807877788601008fc02042ee17c0906833635373d20609b1a1042b835384210a14f3868aa083e40201008bc54c4a0ca92ac91757097b851f4e07b8471388cc3388cc3380998d66d1897db7b4c193c83f23165706fc7ef34cae0e54b05834f6313781cc751748c4913198a360cad8874c5d026e57276290cc3dd6eb74385384e1af8400986ac69aa5413488561e80bc3b0c686e4f1783c5e2eac28390469471165f1bd38b4c10db10deebd27550e9c23e7e6acda5a7bb1782770c55bde1d5550a86aafbdf75e8c719d91c23e218569bbbfbe384ccd4c617690043d1e4033e0b5022a206608e48e5cfdb4b2eed9587bad80955df264d2a8725a053b6c08599563d25c19bc7c31dd325f60ca89f3f73b69489d8e92b9333435d7897b134d783c79ad7e97a8b2e6dd28aaac7339abdb755d0a2edddfaeee9478d6c8f690c2300cc3367d277597fe71e1f57c9deb5ce77ab31cbab54bf7daa5fabbb22649d34ce5eedd1d0ac7a9063c238589f67abd9ee9ee461caabc985d6e49f7e613e2ab32ebdba182a96186503b72b7bbbbbbcbede5bd5d95ca346f149840cafaac38d6d8903c1e8fc7b3b9196dcee66c0e554541c55c20a3884be292b884c249e19442a12a4c4359925eefa81b09550addb38f6a90e4ca54aaea3ba972e01c39b744a5ca142a67d5252a55a650b6caba5763eda4c99c51e56817a05165686b20777f207797600532b2d6a34b9af7a7ba682adf5fa36798d649922c756252a5c7e01c6c07a404ba842a306da6ab321986e10aa6c8e1151687a7901c3ad2081a10866118e2d8926933ddde1260dac8059c748c624cd5349d910629c6c94d7e3acf18c1cd08c692e90c1ad88838724826541aaf48e1215f2c8960ccdc5b2801de4cd7a84c60fcb0042870da49400332267cd1936181caf22093b0a6f1e4e3783221e00d187c79038c698e6fb8219ba18d37da09a5044d1426ce14384060d68481630db3d432930253260d1b9c190302dc0abcc840b7031cdc8c81c60e767081db133ab91e11ed4e5684e28ca22c2a7d69c2e638a30ad9822a090063add8a8ee711447711447517cb1cee4daee99244fa7cd34d954e4e983cdd8c684b1434d1ecf55e8351769d4b1c53f6bd4fa2a0b0986b20dcdf6de528834e5a831eb1568ba55ea15c0b4994a638fc70dd2961bcc600b0acec018638cdba95c7bb5705be4c6663756fdde7b6fa501699a8991314fa85439d3a649923f3d68358d782431eedd2494b7aff7839a33671153a59231cdb2534908a0c6da6d1e8ea5d0cb01b1af3566339d501002848c918447848865fc64f46b22c79a1375285b91aacf732d63647e2a5a512a552f1c23df588d85150424f97e8d856e4b23b15bc5afa13ea94a616306380ed6d962c583e09609b8910ff0c20adc58208c4e952b1a6f604100aab670ae1bc8d86da8b23aab406cc6b8be6004b503185891d5ad752657868337ab47110093b19fa06487216e5b863276acf4c61176b25a6fe103641b8f5cc7f04c32afcf3c453aea3367575651e7793a499e6a20e5f207b91e5d4029440ab9746a72f174f26757fc4cbae83632e48284147d3cc3598736294ad9567d87d097dbd76080e1408ee5b5147a39aa08de6539caa6be04a60d88c703cad229eb098ed78c82f2950d2e5870b319e7b4d98ce3e3a3d9477700d306c4ab2d552039bc97a868fbcecd76def8caf35205725de533b7a6c0cc128da3cbf1bd7c5539aba5dae3c64777e3b32e87e0c837e73d6da3b366bdf3b69b754e5bb87adba540cedbf00b681ce79fd0b8cd285bd2b861da4ce32c1ac772250d2f376543e33117f0e5181fca1ee47a9486d1116bb3ed072a3632e378af7972e3a3df388ea36edec6697c4ca3288ffebaa33470d906c403eab50319755a8fbee0027404b4c5888edf1c24a3df20c9e12abf719c9cd3488e53755ebff1eb382ad761392e4fd8f11cc7e3db6e5c75faac61de388edb382a4ce1fc7514aa9647747cf49c1cb7250a387ecf550eff550e57f9e839bfca71deb8ca55a7ad085e1efde6b415e1b39bf3b61c2ea3f25a16b179c767ad73faac618ee70ea1cfc66b8964c755e7931dcf399fbcef7077cedaa8e938e73bcb6bde86b3fb2d8fd0f8e85d1af913376a9ecee3e369c371e246fda2a0c3575e4b1474f8cadf86445e5d5b1ab979551eb1ddf8e8b5515b938c73b12d51b05dbf719b949b759cb629646af5bdf259df9c38a78dc7a5dcbc72db3da5dcd539e33c3eeb2e8d749ef1d5cf78ed74f4ebf7b4a178fe3a8fdf5f398f5f3f5f8acd2b2938afbc96444e5f996e98ab23396ecf95111a1f9dc6c7d376ca2a3fba402ff3c8f5e802569ea83cc6515d1a3921a1a92a8ff159d378cc91058239c65538a78d4ac61947bfb101f16e3ec7c7b7ed10fa703e085b1e31723de6b4a9fcda12499df58d799bcaafa3eee3380ac7c7cfe13737dea591d1739cb5d19bce376fbb71174e974690728e8f3938e71395d3bced4675c638cb6777f535c798d9f5361398592ef3b6227899e56429c4534b219b1d425f0ec2661ab7c934a7cfeef535b39ecc5082a85146af96acd38768a5b3c936a0dee83620de2fedec0c752aee54e914e38c421d7d9bcda8e3e070866ec234da289bbab53eb0f6689b0ac029001f9c1396dbc8ecc407283c44e9c17a1184104310518411474c4182ca2d0925961862a20927704f4011451529a6a0225785155758c1220b2d745b70d1852502e20647952367a5b3a383c7df9ca1c1516373c2a155a66eb0a1490d34cc20430c30bcc0c485252d28616105159220490185138e9860a40891214225d468791604a4030942820019e14796fef0112104107a7c7cd08387073b622ed44bc684ee4428ba3211884420caed2c1fdcc7f8cdb33cc7e3b8ea6f7cf5393ce755bef32bd7f91ce7f91dd7f13afecfe3fe3a7ce6dfcd771cdec64f5fe3e5e3e0ad3ff90d2d6ff237b80d9f721abe89d7f036b80c4f83cff035380c2f83c7f033389387c15ff8187cc9337117fe0557f24bbc8577c1577825cec2b7e0497e0557e159f0143e89237915fc844fc15178246ec29fe0471e052ff226b8913fe243be881379235ec20f71a127e2b42fc16b2fe4b3a779fe9a03fdcc833e3b090fe43a7c900779125cc8ebe0237c1007f2425cf623f8cf03f11f2ff3f47f5c84ffe13e9f3a082f8287f03eeee341f09e0fc17bbc0fffe07bdc83efe13cfe038fbd07bee379382aa0eba884cbd9ef814ea1190100020004200000e3160020200c0c068462591604719aa8f614800c59ae766c5898caa34910c4280c42c610638c010410008831608664883a0af8c533dda34588252028f4701dfd7f46fe33cd62e46fbb006eb15648ed2320748ebad76665ca8e0032e64b58e4d1e776e2af84324622a6f6a95c3a508c9c7831c4fa27f6d35e19abbdcfbfa6bf44611df52618109bb8691e3a5eb726d997b2acfadf19dfdc890f5a4556ceadeb45a878a858924d4aa530146828d394d96002b329a69c80bd66b467a6b6b0f26455ca4a40415f2d1f0551854e558e3b1df9eae8aa6699c8878e7fe6867770a9b90a5671031082bfba0fce3404cd68bf27f63f82ed055b46b74d6aa68615416c227e88b4e78673952548dcdd375d2e3389f1815d2e6b40b4b977f94369578121146652226ed52ab008d74ee334c71d96fe08f75820b43bfc20c209d6350e4376733b623e30e86bc49dfb61bc3d9f17f76ce21ed234008ab9376c8f889ab3c26749b377f625307a9fdd759100dfb0e058c7f4101ca30eab2247a534508b7d96a87dac465eeb270fd19e5b7c8a152ab61755307ed82338f1b8f41d69476025951b89315dcd0f1bd9b45f36f9ea50fc6f98dc2c114da1ba328bc62c52f34ed841a2e29e091f440687c2da04a64f50e51872330c0b853cf9e6674f279cac165f7b3a15dc0d2bcbac6cb4d27e8b129320611d411bf8efeaeba9e83353ab0b318e0e878d93fa9cd3ec603f59f81dee810a7a23daf565b6aec5b090da4e57dbb64b2c1144ff3ea1467bc8ac83f2e437df87b105d39b8edeed524461f680d3702a6265037ed6e8787d5abd761136eb6e16c0a93682a1921151be19e6189840c4eaf9d3e1e2b3cd9ac56b1435b72e414055dfe4d883c50e0325e7014cb12d738cfca779a61a007aba22dc6394d2b38c79c3e1fc6eb139203a2358e6f97a51c8fd90b7268a5d4b98e14f012a01a72cd700cedfcfce1bbe47fc420e7777df814d057737939ec8a5eb056603971d8f565830d0f8d08e5c03efe4df40e41bd34f349bcabbe7fcb2824506bb7510152d776de7fa0cdfb28f8d854bc1507121def452e3b83793878d08585ffbcfd7f40c6be01e966c08295e31e953da5d864e6d6c4d8100444caee3ccda26c4fbfda2fdba8c138bd16eb79c26939b9c725bcc7c7bc74c33c0b8300420b812e8162c581553484c71713d5b5d9bbdc47d5fb44571385cabb44798eff6d17b8ca9d916a68f0897e7d5a48c48b3a908a1ff8fac9f57301584bed1a01943c7f58f5eeb6c27f82ea6da12332052031324a6f1ff4e5dd9e1d535115658dc60ef6a22fe2cb56d6f73f8c79eb2cc57a2ff638718a75fafa92f73c1045e20c25447350c00a23a5b0079ecc8cc6032400a99d8b662f0bd87ba35d45e272d16b59dc79fb47bd00d51d50b3a1395c99588121b2bcaacb017a92df865639d12c5a34edb7fe248da6e785d56394ee11c47b90d47fb03a318fd11bafa3f624127b8b6534347a33baeffd7fa9f481ed5d1e219402adb697a114bb443359a013c239dfd1de14658476377cc34982ac8234f6750f408316073421b62358eccac67cc67701bfa24e5961df2ec69a6227bac59af9a5b29480c84bc7be682cb58442bc6c37a7999aad99e2077493015c2aacc5c79ae54563146550ea3df4fb9066539cc6cba3bb4a520fc6d0b375fd94b1f33e652e6196e041ef62f740f868c68512357a734c3aa35e24de245b6aee5af0d240d01faffcd7ac0bb490146e872d3550e914ffd6206dcd5c32ac4227b643def5af84d5bf277e3c118256ecb0d26c32f26c05d05ac429c62a55c2742fa44d9e7bf984452fdcfa7994a9a9d848f92e7d5401e7c57b241f3ccd7ebb0cd1bbf255c0e30e712c9ad00720189578b8779edc66a0f8bbda9dc26c21eec39530155862f11c504eecf42b5f58b5462d89934cb5322a52f82b3f4f1d4a391bf6041a5ffd6729375a200629508e81ca75aac78c6152b552dc41ed065b79084f5b4e768cae762b39c180f35a65b4ae23b76993e76a91e9d392535a99525d35c3ebbf8590cd84cbf69f56bf10235019cdcd7a40fce6bab58687e6b79b890cfd6016bfec9f575f0c0b616f93381c70cdb138d5a5e604f8390c27c318780eb4546b9d5c5a6c9f5d57db3d6bab7827257efa2e3debfbe692453e7485b0a83da8ccbd42d44c6836a7b09cabac88497fffefded47e04c7464812d61fac55412f6a8e394c3c51131a4fc29ece4b33a4a473ec809893a955ca655dbc8cfe48353717b251892a2e918048762261a318f86064b588e47e13ab936a535826a8b406a5e22075db72a87fb8f581f454c7723f75a5ea160f30f430416b67c7edece8208d4a1116a25c0c2442bf40b81399e60416e4e400273b4d716fe5e269168a0dbf5072c7a0718037b721c49f4942c82334e9381630f1e90b0153aa2cffcae445511c5bd098ec6f32556bbfcfe7ef5073874103b7ad325fc7406fc219d62cbb754383fdca4e74662ac06b90d2ce1333c093ed09cba08f77e42d474ae164f7734631e44ddd78c5229e25aaf870931553b78b2d52a6f788f73bba3973803af307c1d5068200c942d5ad65831b25a80b115657f4647a214f2d64b490f818824a2451c562493885b1bf03a96aac9a386fd0fba3a8fea1afd8d7fab265134f9417464ed2e1ec59ad1cfd276458371305bc3fe5c691366d0495724e03db7f61daf4c68731074cb6850bff7e2bd0b3bc1390e1ed66aae5043903f7962323c452f1ce63c0a4d8026878ab16d056f282a955bd578be9de434773f166276925b662a7d62daf0bc6134a63049382b074adeab0059bba20ae1348bac0a5753f0a436148be6e1c63d205a07111636e72bc5df53816d86803f6588306a514cffe14e095d7352e32681b161c2c0b6c6c462dab253bdd1f3f77c0be2946672fa5ac431f674e6922383ebb6a69c9c833584830cf12c88bec88112edee8a0624762590312655cc0fc3435117ac76e27d51123904779639be9b8f568821803d6cbba173dd3a249751add3fdb1d92d5668eb3eaf15b707e1022d4050f288698232136c85144bffbc9eb1004a83a0918449731b8a90fd60e4127c10a40f5de5904f0dfe88fede855fd74d63f39456392f87dd6cbbfddce843cdda6aed4095bd7545310107b2a78a4deea38f721ec3efc5fe8d03d20412e7a4fa8976d02082f6d97f5c608374ecf79b413cf39f6cc64a737b417ecc49e7b19bb1ee45ab5cbacdc877ed172d4890af276eb68ebb52bbd5167def5e6bb30164e6c030dfc9a29c157b373f8802205518d043e919938e01e9892493117d87b4b9f770091c7771ba365c29a3196bb535a2628f363de9b369126b07b5c2a96a9305103eb1633fc4ac73c5836df6920d51c725ce917e56a5f4d2a5cd43b4255fc2766d4ba335cbfba4f05cb2e29f6a09ff6bf3dac357c5830f058c8942ea382479ea1bb63a670b0e1eb950f59fdd60f609985cbb3557515ea8125ed1c8f95c91b6de42f80e55a2257bb2de4ce55ab267ce966c127cfb0af1b7703b605df1ad8b50f5f6c62f7e64846aa2af068a6eeeabd30a3cfd86ce86ddf0815125a6a42b429521846241fbe354135a6182746334b1a25b314f9396bba245d79f3a97e5a8afa2172f1a03d425e9d4badcdaa28c6cdf9c1d8ad790336064addac17c8426c5b5edf882daa71d32e10be9254560b6c9c5cde2d76a82fe15339b4bab4204291861b8c62dcc503ea93dd4dc2a4bf6a0841e8c776715eba5acc5369bf3f5e9f8775095419df8f0e2992036e5ed3f1eec49733038547319f52600d9b213e31e270115e1335079e4a01911ac1d2bfe0137b5a30f01c0379623eb44a08fb4143f80ada097fad928cfc28eac51802a4cb75e5dfab4902645d1a431597372ac38a7697ab2fbb1160f254945a27f3daaeecbf0b9d031204b314929a4bfa5ab7b44664f4286e81e5d7423ff27b106ae9e8c0092a93e22d71fd12826bac083deee4841bc7f455907757c47e7fc9b1a642d18a7bba4897c99df0995b7f600db4ed0c3aac3fe903d46a7981e81a19e8d089e4084cde9cc1db45d81af63e84f5acb32f9157448501338bd18b32cfe13208813ecb50563cebdc35e9822990d54ce2f4699961267df8566e8d0c239dc8e1ba61a67be68eb6689387f2de93d762aa0774750cee1db790657d56986af8c11a755c316bae44491949b72c2e7d4cce589b3aa91460f7b8285fe982bf103fa7d36eeb40c4c24cba8e1e147965b8678f0a34ff1d9b983f8e9d4d52f5775611e3bc301b7243d65884fff54a780d18e2c6f6e0f34519bfa064fac98322f276ceb62e7ac2d468044ef947faeeb92acecc3a4032813eac183c89a391b2c5e15361b347ec7b21dabb61444c215dd48a3a0ffa4967bc3ec03c68d834950e39ffb254dac3dcfbd2663e241c0706777211e8e986fcbd50f19070a1188c6aba52aa4b7f54224a2c5ecf7df8d6bd222bc82a332fb8a4878b91f9c285860a8a6c88a876a36884a70b32c89fe8ec45faa6a480ac005166fd5bfaa929cf88f36e2581ed4458452a2a440f2ace19488ae4868f1487be91cd7068c05d82915a81198cfd9860e0183025a06674f45f9d303d6fbe455095bd039df1bd2746583d5500023cd4cad00da0528af7ccc57ecf89892fc4e8e5ebea8d39c17403e049d9f3ea05f4c9d5202a09e96117ee17bc9418765dc1d1ff3c56042edd20bcfbf90486ad78423a3a0bfbc36eb3256d0226d4eeb7e5fa5b498c35f84836273555cf2cb588523fb744b25e7235858f39c9769b1cee5a055c1aeff6f1b77c6e83aca3c9ab835658736577a0ee264dea3c14d445e38474d9e146a4f4af0448238d996cf762d0e491e39b857fef315a728a1517b9916ecd0df03560e11f25fcfd84d65fa8e621802d46492809367105ed6ec90380c8cda4ad7f616c9d923d911639456feb551a213d4d45417410a023a4790fb7cca23fdefe574583168742d33fa975be095b7161deea2acd2abcc5119beb5282b07998c85e961216868126ed6dc2e94f4e7dd2a8ded6c7cc3a5db833a5ac447b611581c6d77eff5c39d54cbfee8190209a72c5c3419594719262f156b164588b0d7c7651202a69768b09b34588a32a9eccbe40d20dffc7c209b586b78619b536063146dc61239b69dc39e19b8a83d8e3c1f82b13fabf78210803ffd1a23592a1b6fabc5bb5411f2fbc6227985113c653ece97c93a8b6970f2a2d18b68478f8a46731439a79b5e2ba0a380090295fb8deda593056a554f2915bc32e2c5ca7b2fa88ea3f90ef1fd05b78c1ee5476ef677f632f7c2c48cc50b1b81190343afbc040aab88057fd79dfc58138f1a99909542f7699da788f858716880a996aae432a685f4c3857f78503051330b25f2b767601031e6d677eff8ab8aef271c2862fa92115979c5c02efdf4f9653dff4c7d9961e0e308bcbccae9758ea3ebcdaf09b76b05a3475579cb0600d2e3fc859f3ee9ba3c576af3f6822e812abdb012583b7629bf77f5128be2e03192f8ea6e680523865c11f634544269168fcd1cc39e09e003ec9001609335656a84e1b5633efb7532a766d6a73649f14f20fb1e6b7cf482cd759bde412fde50335add4623fd1629de45005ffa7e3a7b27d919faa8db39060b2c3d90bebdbfa2e29944311a0cfc36e34fbab17712575d5c7812b6f0869074fed006e53d149d12e57e77174d4ce9f66a712ec3171eb4854451417a4c98cfb9f7e2ee4d9c7798392cdc3416042f403a528ead350cd6b80d54aa152264591eb49cb697e3e64ca379d900f43feea10a97dfc7c1c9ecde08421b410b390407323ecb68a61f87be787e1b96ae45a1eedc5622d79c7e1277f449bf42abb9bb6d0e1472849d9ceb2b1bbf17d0669fd30d16dcc8a2a30ce090b00e11f9628798dcd7d398a6e7d77d5b49a6ad58076319eaa6507e3e1b9a02ea6a1b58a652373ca30748bedd8597acf7f198895b68e22a4b6a793258487a32fa1b019d51ca0f834e73343cbf472dda36b366d812c3e27647ccfa75b28c82901431c70951d0edab67bc817c1e44d660331ddb8544c8ef4b7c63cc8c2ff753b87748f8de1f430c00e5627babe7dac254a72c779cb6a85f089adbd4467da7ac861d099e10a4d9b7790a718d64bf7534dc71a6eadb217450492d4ee431ad099d0428ea6106e84c8476ff04031f47aef1113487f675896edfbf4f89a00173ef8aea1858c3782b5a9b96955a89dd9715a4c9a2f883ffcb16dad875d6cfb68d1f66930541e599c41b1eea1c0459074e7abede80a08158b984086e458c82849892f3f18f4c0db8cbe2934e3a75c24db8270451edace1ce538123167c86cf1c3fa078d07e226e0046fcff60f363f8b09a6379fb64ee2fc0e42d12c0e896dc9a124fa97fc28a1cf0e304bd8e60e2d6f179094aeb3169879ee39e849468501ccb15a64dcabe8ee1948bb63afa73560e57084640e6aa334db11e82fb404c67878fd0acb96c859c7a9e2a696d1b3fb845cc9888f0516094874f704346cd64800aae6710d91517389fd8327aaf24e8ed537bfb67eaedb9b0245a0265668e7f41ac73e8d5e13fb483a97b956e0ab5e4134459a41ab892e9050ceb7ee31e9f8a2eba32f24238701403ce7930a3a7400f5560ed9c9d0d8f7c6e811ba94d8f68d3b1711381b15da8852bd8cd57815b37f7e5f73ed072f2dba8d8b38e42a66134017a72e8ac1e4abb7b74ffa6a21d60c22cfea2cc703bd2fa848f3a6c42fdffc2f8666ee1950f16d45a5f8649f61b7c9cf402f792eb5d3c3350e29b055829264f347272f110b8ff1022b91a9704f0a35432aeab3f11daf8b814c23d63c58bac0fe28e818005a95b72198222eff7f3a5acd1b8440fe9a84745299925076c747fbaad71e3bd0ad8efa88138368f68453c5466145f658fce07afa737c0e704049a8f5cfc50fbfb2537e638a345ae0e6434e0ed1a06f3fba06ac47a2ab5934629459ecba22be07bf70dbdf43bbabf189c30bfd9546f7329a829b3ceed4851012e842b15ef4ed5d1efa65ca12dfe04bf76efff71083e179c04789fd930d332746ff4e224fff882dcb9be65e6887de3ef1928585811fe76e63a81063d5db02a1f2ce07e4b4ddafe5dfc31a71084921331b8fcc185e99061ea5f9b46a69db48124fa8712ad7c0b10541402f4458c424fea2c0641a3e0f66b211ad1d6e428fad92de1641705702530c285e096fdc16fcb6c59292a07769530a807bb0591d417b88a0cb583eca20ad4c7ba4ae4e2c15c290deac06e2504fd44ae2053e5015f29827651ab50a1fa30578b9938c06f3c9290fa7ae5963ed33f04aab3a0efa03605115e4385be7f5fc4446fb3431a0ee395229a51ad650ec719080d6106e5223a06e410e1881f344707d3b941a4d9a1c1113168f0b8a058f694001a111ce4032de1c06c3210020b40cb04ce8140231a300f0584cd01a04280e304a021fe9bd93f19f833970bf51bd442a259b4c4f4ac9708758b0950f152e2040eeaabcb65b3032436f65f6dabae37ebfb2e6f169bebbd3ac3555f7d1ae7d489b2fe90cb1fa7132deac4547622bc3b01bd3cab227d10fca6bdcd206ee252102e224b48f2c4ca788e4b3c1d0b4fece0394ad7093982815b50497f91a9c9507bc02eaa407dd42854aa3ec0d4ca241e6a165ad057e00a3ad51eb2b522681fb48a687612aa8719a54ad407985a99c443cd42cbe8137d7cc9299828a61bb362a73058f972a268b7f4bd89258a86b39ba4fe07c37c95a01397ce4abfc3f41e5071384df8f44600614666755dae902cb1000f1e2f43a2d3bb240d6dc75d908cfd64367b96e959edea281c56fc517a77bf150f40bdcb04e04819163d620f23ec836c39d51df765634a157d614e569e21c9077969e088081f710f1f5317236cc32cae85f1afb308a54ce54c9316b415ab96c5628b8c34eb76072c0d43a5842890fcc32e07d480b46f95e3b630d275b4c29450ce69d2202b01c6322ce21a1c53ff1f9b05f0059c7f99f5a81a9465caaafdc1dd24022d717fbe4b237c24aa9042ae04da8cbe2cc99b1675bd11070b6d2a228d1f6576212c27ba26405637544144942268e24392dd29ca85a089d3d335391050a3686278d062675272a764a284744d07e46428960ade44d891865c9130410aba250032fa81242f1775b211e748db43a471475912c284a2cb83ac5fa8e2445402344943923945cf10c49f9e5cc8914e8d6162bea08531294950229b10ef747c053218adadbdec0783189129d6588ba95dd48ad9b0e8cecac5a8e29914a583f29d535463a2704ace871475461411911b84e2e01f6aecf1483bb12387a26d7c204d34c9501930ee6e89305728578a3b9c446d12ea29e25220d1c083026f38c38c68baa01a2adc2d112521a89183033044de09d479e0ba0aa2f54d6cc5d32465e394d394b2d3b4c14c93e7eae04881779b69e4a431ddb846c199edb4a56c7a65d8d360993eb85232cd365939685c338f2931b63bac28bac69750badb7114eb1a0e9edbed863513626d30d609c9da6b211a2d901d605301111ab531056bc980719dcada21b2c6c0580f47acbd8635021eaec6e840bd98aba466b1a690fe34e30e81360fb34ea6230789f10729841662063194f1ef592fb585574aa90db1240604f1569404145740479d22aff88319ca0563ba175c7d4ff3ed15581ff96b956504a08da19a0c67914e836bb247ccbc4aabab8cd1665bc4e45e9688302cdb6d8d96d2267c19b3291b75c3c73324fda1d7f082fca2630597599b50a7fd1339b22ee9a05305111ad7d132d0dad74dfa4a1601533d11e2658fc1451eb6095cf69b59a02968294664b3c65bfeb59e577ba86afff4f6d128d835edfe430ad6a948722842d26f5d0d0545ff8522d22775f39bff8af70a3dfa9a535acd6c379fd069516629c283ae58b5c210cf3a3b6444b6de0a155e59e8e59fa0d9f36a7ff2c64368691ba1948dd541a69b85469bff506dd8049f7c97ef75722f0301751ae98434ee8174f973ce2151525d4326fbf143165326c3495314d826a3147a2bd96ca1243526a798ce4a6d54c425aa8609bc5399048b5600889c4a07c5437c5125e2624d2545f540aeae26f141b3a6827c22a3d2a970905b5541e413bfa353600718014d4d64e94146ad66ea416f41b944ecf25d1063a5b961ef74156a6f84b99512fc50f7cc69ce203007374b56058bfd1aac50a27c66d7646c572b3aae803af11a95ca562084dbe527a0326ca0096edd3ba0a12114a0fc0bf929c7f4897deab524c6630775e80b69d91de273a5bcf627916517a4130ff21ab86f30237097e2f29154f3a6d78df4e1cc4bedb92b2db44a548501413d8b2419e525c051224cd43d70cebab280ec08eb6bd57c5658a462a676a4097b4a71162860012990bc0777f1c2dba2f8cb8a0602ea19a1f7ea777ad0b787b759945510037ffb318caeb5e9bc5eabac2224aaf0887d2042b75df8f094b20886f11c104504e1e400ec021f9cc053033a4e80b309b61c80361bc8fa030613b854078b63a8ac002adb30cd01a4ce10c11c2075e1e1191cdba1c92830b386a521289005899a80f00a876d6064108a5901d10c064882a01604bc01600bfc07810f05744980130a360fd07e818c3b6089c0253d5826503d0f146f9872004932440600fa1a1ee6e0240b8d6c60bcc3f215148621c91210a1e03009c65f41422c0d7917cc753426ec9a7c6996ae966c5d902c5d72d534557733a5a14435e7e96e34e968a526887441469a00519b83eec41fedeae9889ddb1847036d7a16cd75c868d92fed6cb9905634474a7b9b5c4d126d24d20220d7b843036e34a78cebb8d0da146d28718520b48743c32fdcfe049d3f68dc0b2e7a40e706f4f76f43fbf9753f4bf33dbdc3d4baf29d73376e45dff9da9e7dd7bd4d43ddb3ba2d33ebd86ec359f73b61bb4dba85d22cc07455e500130580b79fdb005efbc97b8f7ba1200cf9aa2efb98d416eeeab2895a405f5c30470bc45f7aa15331c1777f7bc06b3f79ef712f7ae649effde2058fd2e26085201a9e150747c9c9ba302e5e1144db956009133d3b30ecc12fa2390e7e19fe1a654aea3c3b09fba611a914b25c380b896d216fd57ee54f10163dcf930bf83427f572a0704f6f19d878a0151d61ebb19f7527462b524024841a64e02282c525c88d0093cb27b4781ed2fd1c1c335ecbb833d117f39bd7c367f15f55ba43093b26871fbdaa213ef749341df9aeb159ccb366e12c94141f91ab0f636bfc93055b4724a3064946b014c13bfe07d29efeedeb241b3cee505013c7bcb488c6b91b7a7ee3f3ad0c94d1186d1235b99d85210dc664a79919cf008741c537449c2d5a4c0b595cc010155b402657583f3144bcb51cf3b83d5ca7abd18d4ee2ee9fa2ab5be42053afcf78870988b05c9a235c3fb2e0d57f6f323ad26c00abc85c1aa6849365ed0f8fb96778fdaae4256511afff1272876bc3bd9ff8873c683b081a8f2c2c0906dddfbfbff1f6ceedad5b6d6ed8dcbd75c3d6d69dadb7376eefdd6a73cb60f3dc0bdbe4a83a59ab79dab2ffcf5a057f165cb92ee798a76416c1676d2b1b1018f501e555b51dac505e6b03532a77d9905878e8f84f7fb090904c62d85799963527ae3f07ad82b92b2a674ad8c3cdf54fcd5725f21637564631e3664bf8db383141fc017f75c5a794fe72cb48fa0f6c13bea241c727cb2666b834d1052e39b64c755e1495f46cd116ca172167ed03f166e40fda9eb12303643ae527de33dd54967724660019541e1f0e9a04248970aadc60fdc672d1d4b8d0f562d8af10e3bbeb481d2164f38d28d57630d237069f3174375c0fb3f1378a3b6cdf722676b7c6a1712499d9bbc8fc5f9b567d9088dc75ff7a941553302e1add2898e1da6430285a1a3138187cc79e0b3c9b28082d096e48e3f9eb17d2a55e812d88e90365232dd35100f22ca008b303dc67e448602d839e86d4b34155c06fc5a90a853426626e46e7b64e51f34b14a937a61fe198a40b6ba364fb1dbb2563c47d14a190dab318a252071c1aeee6733113ef879911ba26529c87aff14490db6542b3dd1b4a5432812b47c8af978395ac92122a40057afe2757d67f857183ce91dc10cce4239249600116e10ffa5e60890b9a630342d03f065f0195128b22137b597aef57d8b6a293d6569c3f75477fb18ef9466f77f43552f8fc6fef020baa7ff1b4b61a32356076dd495ae48ca46114d1fe7e043cd2b716bd4ebc7b1ddbb98ddf9ee8dd340309cfc4c5cda78b773bc8c68d04f78052bc8827a88b73d36db32eaf409f92f1c2bea30d16e2331a31a9ed85b1eeaca3f5351bd69433e444602f1b5e4398ce7e68a520f0492dc4a7f51acec0de2bb7fdd57c5b0c8ab08542c0af5cbd63f63965487710fed9c16736b345a9f49f3476c7f40c519d73414b13333cd03b66600d031e3f813556ba45dde6a09f8d7eb785d0957c0bf08b21f9a9c8b80db4218e2eeeb6a27f62425117ad6574b971e4948ee63c623c29f1f7e367be0d9d4718aea324831dcfbe7a3494b1e3829cdb07bef22d1d44d8873234d5da2c80b5132b3a01aa3772d3f19ed4178f5eaca2c202575fd34c7ce472eb99e1721495ae80be874a454a2482504284e040ac81c490e1eef640f7fa855c93389023a2a3145e60d4eca128068025ea315d4feebdce80b6d933a6bdb8e28e23c9c965931d0831c8ce165846b05f57f1e72b448753c25fa08baa6b518a9fec91eca62277ee405c07631f750397ae499f822e5fb7627d11e0ae56ed5e15406b488440a20388092e13beb05b5d4823f79a86f1865b44a3879c023ad1ee845445f02657a509353ccd888e44894ab7062fc87f3276f4a3fa3c3787fe9fd0088411414b131f5c8f085ac8a9a5c8c76274158164f55f1b68b48a7bf9bfbeb7ece620f5d9351c3ae52fb5c4dfae3db33409210df62e52611b2a68d48b2845c2759183ca8fdf9c8044a0112f2329ee8501770fdf8e9cb9680e501924a9941bc7e0d647d2434c39c5219659b58a73f5dd722364349b937d8fa09682e4d492daea4011b63642f6f25091b38b94ab99b6de0ae22cdbbe245d4668b32795cea3b8ce60dd878da283281af52917773414d057d7f3bcd159c7d3fe7af0a5bde2119000c0e6cf62f65af7d7c095ed596da37b93b22b396429ccc0871b5f82fc6991490225dc74532fb874cba102e4528cd22b600756ad9625efab3a600f23073d5355e3dc1a1cd39d183d47d8cf22e1ff387622a976cb6f38ebdfacd5525e229c932742d6e0a36463c8418108e7619c1837d9644251627a6e24e7c68b704adab6f395dbecb5d25aff3dcec20d94a40518bda6c701ca10ba3c8060ce27409fc68cfbde211de594b954f09b33a1f56f61d5007161c516e6701d96178939e86bd0155e2b98e9cdf7d4dddad166ad7fcceb86120630ee9c01d056bb7d4fc19f134a2918d339503a38105acd75225a1d5f98d4085cc0370c2114a5274a5da886873ea8682af507fda575d92d7bba6a6349f191c702ad0a3af4faff1513acb150d8eaa91e82048f63b44f3608415f56b4d3c781babd71f811c2ea3dd0d61d5fb5e53d9be1b12c806c231d20f8a461c9f374e10036a036093ba816d3c4a19a55526ccbcb25adb06332f368ef3e9dbc6cc65b126cffc2f29bc6a68416680b30bb6145c8a09cc1e157bb78347e74be5a713f876941b1f818e2fd8931dfdc48ab2a62a23ababb0d6345b735d16fec688ce9a90a222731ca3e3df051a1de0c57958ef96a52e67a14524da8bc40ec1f35dc55ecd99477b7ae66c72183c0e05ba2469b694b0f80d436311e9372db0180a73774b26125e4772529d6a214af658cb6b531174a70069306547e575aa818d9a02802137eb78c0213b306ea395e21e3d0eddf24f8da0b86973e042d16951773d4a0ca6147905da023a6090ee5c26cc53282a26a9be8c5168099bc8026347d2a3aed78d605953abfc479f478343c93953e77d8204abf8b7cdc5724e2a027844004de6d9caa06928720239934c33693c8dc7db3c4786412b08df4cf24b66bc04d7b5c091405a65f64a441a3013e16d50d4d0e26764eba99be204cb354fb897c2cf248ed88d148e6425898975b4f8e3b347748b0e077b3f3571b78aaefe615c0ee566b92136aa44668e1f8c7b18aeb96492b4370ec625b2dd6ebdfd485ba9879a509f1a6d14058b090e1a026ce21cac340599bfd90fc74ef376b201ae61d87af4004618c48f893aa40508f25776429161607bad40ed683430c70251f0bb69f98e4c8016c446416dcfd3c32b71ee91ca2c69f95b43fe4fa43a4ca17b05e90f1cf0e53246d00bd4a55892470397cc506b835c1d61b5c6b8b1abd5ea467d6fe2dfdd4b5169408cf0244df55fbcbf430a17c6a3a52bd998298dc029e1dce645848284a7e80815f07b4915acd043efa201e1c681c0dbb0b72f59523ec7cebcbc3c061a9e5b6c43cf38cf4866a64a22fc26027c696e49aaba230768439abfd8443348a75919c6d0f8bd3d5cb058d2c2a9276a017dbe4647b4eb07ac8b95f0946de6bebaf9376f4e325b164bd84ba4c89fe2c95eeb9ecb14c51502a934f3e512e56090b7f8cc6a0f16b28715c74ab274c73c415e508c32e5fc0cff7042a03c91a233a59d7159cb062ad05f22fa918d8720c2a847d0a535eb950fbf21004b986f264db021def6d3046644f08954e649a336ea465928e2437b333b4c8ce9c11f750c8e5f99519006dfe6a8bcc0924fa60b24c91fe6fe3b87c5a08870cc713c3493407cab483516de188da2900be01e672da2f058c1d11abe0e3f8cad7d44e3000976d38a6140c4bbaf33520c3c0e393be06783392b20fb6d7e7d94e5579aeddd216f26f4b0c4811a9c339b8977cc21f3e928bb74841b40638153a7acccc48c2a8ec5100564ec3f6ac83a8faa113230bac6c31066b1ca1bc9cfd99482857607acbdbbfec5c93615ea33bd64fed3ff4abf31892019a7bff79d30f2c451afa94fadc2d7f4e3e8671f498188f0d4821c50cf466ea686bfd1dad111741c4e7d8455b24ab8061a6c9ab86a269ae8d920f8124b8dd8d89106eb7478d3abec16e2443a9dc6f4a8bf0ce7771973bc2462f1e0aad72268141947054036691dc284bd206e1e4b9a9259f3c31d1d81bb96b079ebd21735b49953b99307d552fa5d42262c4615d825f54190aa37a68d281fe5586c75ae492419b496584e0e330f0aef1a3c8d57e856ae88e64f1576cd9d8db7f60e25e4ec24bfbd7e2acd89aaaabb06d20b19176bc2d92cd2e18691a57628e4a5f209550a9571a140bbe37c1260ea944197b77bf27288468938aac0cb7cb9be43525af2b4e8f99573fbc91700b5d4e4244f453b4f4b730cd49a5f4e8ae07bc361a72e0db0edc7783c49b3e3e2e64b28793ecabeaf9b9a650b2ec21710cb6b859cadd782a495cdb6212415e4f848122788e819129820a5d390e13719cfdb62c921abcf9f731e3c13d7b58ee18bdfda984cb360bf51a3fc46233856ec4d5165f090f7736d65cae44a2133c33b2dc04ee39f2d304c38c30d5a829327d4087b4a6a2bdcd88dda2c63f556afe3a71f112a2acbc20cbf748f6da826cc50f59f77fc7635dc1d99a3232d105737c13531c9d728119ac4ee9d7cd683a89d5e428d1c89e1e19bd8e9aeb321c74c033aac247807834b7b4a40e27fe5f4c1c12febe03914e22b9984ff17edb236cec6aaa5f4e549fa4ee2b0737168d89e9587f3c00ad5de7108142c017b6abf058ee95958da3b7e36721c1e355b40e2fe9b2e8596d41d67dcaa53790ebbf78de66288328b2f3159f70a280c279a79ca573e15c129cd353b210bff2b0e141bccd94d5fb741183ccd461f4946a6ea74ff7ef38d5b36dacb3d6b93bc0ecd21889fdfec90ff4986df7e4a6d5b77cac0d391994657a857e99b7270fce3d2c25273a46a1d9d756b2b959aad0dcf0ed9868f29b9ec9d30a8c6808bb846cf9413d1600c41506b935aedde0bcb6cb1871463f4369d978fca18585a3a72a3a90e2ab60674e2dd353e05057babb6e9933f1ad625c6408eacdff554ded35e579dbde9a5e0b495ca70d344c46913a644eebd6d383d4ceb50c98e1c2be7e13319217961496b50a726353a7186d7b7dbfcaa9e526af1de878b99f41029c89b80d43b7f0d679d142a3ff39b7100810636d288cb750a425a7d64b1f2db8fa0728dfbb05b21e74514c636a4f6173eb436f645d004c578295abba4f60611d6b8ccd9305b4fe11e791c65562646f3896f35c9d4a1c055bcc24a25e297f1f7b514fb6b4936639cf376e2974a9e58a02e17392739ad97beed76289bf3767f8d9d3f333a46b090f8f941e0ae9543e1355e4d93edcb64fb802609c3789f24c198492dae7bc436d14d048175717cf98f21c320a0d39de6de6b87ed160113ca05d1e7f1ca9d0228a5ecfe3fc02166fcc8aa4e2fbd36758379631019410cfa581cf68e327b756099551bfeca6d9c4ce4843f2800448c3aec58873a573437cc89ba1269c534af995ac0186da331bb0581ae498a91f59c565c85299fd40a5a2b7c213e9307a320a51fe192455d5376a4c90c579ea62b849e68f688430b41af712cce30852fb7d060f3122cd83c38d04d9ce27ca8603474a86c7774928d2a96d22c8a6849339e7df0328fa12212ae2ec27dbb49bfdf3cd7aac0173a1ef3c250837da39a53de66aa5c6fb6ad00f5f612caf37b8a195452e67fd03fa37a76eefbb4599e2d77331668d0e35540890b2be78302de16068e5241e07d1e33fbfe39965e4f53e72e863d848e31dce7ceaf7a26853475ad13a170f1417090ecbc4152b912720bf5d32a823186dd618f5303074e2f40e7ac59c5832337afe20b2ef46d8633420624ae7b363d741d8870011905051093fa68614a2464c7098cf4a8111cc7d8330c7a1b423ca539861fd389fb9dcd4adb57b800f090b0c592eb085552a69a22f9465437c06ff6ef7c0f9266d4993aa5f38d9de8a2cbe2513f8d51400d501fa19532901e8ab082e2cc4c99b57884dc40007b2e7e6ab2b8a18ad2307e70bae09d8240412be8af4b4dcab72302338247d915c8e00f4a5762a0a06a63057a50f5d8558b01aa55fbb76115679b71061133c6a35e101d22554e0c0d444e434dd6375e84536b13cba52fb972f91bc6c8bca5a443fcd378154fee325351f429827c584923fb58e19e24c9bdb5e23f11913a627d7f8dd0dd745fa821487f2aaee861bff3d53f4b31a3de2b4d3996c4de98611cf6cf34c7943e3068192e4546090b365ad2e2b725f4b8ac05216410d180d0eedcb5a1218d424c4d92b04a6b15c842df4494976874c89dfd2db4cdc7a22942e70a9f8b9713893b23904ced07bc240b8b873521f6eb26ce3e1e210627dba353f2000beb976c180678771a6609395478da7ed6313eaf8e8360dbd823ddc5186e3f690399d0610fe4b6055faa0cba1e2018c5b8833e2812c17120069eada3827f903f1eebc08e4c763c439a63cf4fd800a654bfb4cd5fe40318687ee738b44d3955493282e8149dd80eafc7277f020026020b4d231f87e8567001a71663d2f23ce2d8b5e15ff96ef73c4c2fa8788ef0b940751411aaa43662cb94f27fefa9c6b602d7732211a8d9d4eb416a413d640328d5777cd241dad2e9996e204529546164510b1207f22b23534652e489ff6facbb10fac92f840c44bb7284be98652fb0a2a5fd9a109e9bd18d900f58c68eda36dae645c9f0be0be4bf3859ef64550ac6d072c3fa0ff35dcc33250e85b74e5526eaa7741f386a0354e9f2b94139fdbfe1ae7533849761f3baf263c52d802de1eb44059a4c7cf5f69fd8c524aabe1622f5b5042b22544ea14dc36f8528bd9253610c87bdefef3d99338d44a3cf68cb5fc5aa06c01ffd60b4e75dc06d3293c2a6dd8158d4da31542d90ca683139dad1a0dbbbb736d3947633dfe93c0930ccbcd800ded024854f04b0a782d1d755fe133c79951771b0c7052acfe1b8b90971c0b0bc1ac6cda3011bd4f72cf80ac575b7c6028408763735a201918611143204286380742791345ad19b6c078905acc90615587024dad1939c1e0da905a115b09ed841868980585aefb096688fd47afb3424a125afd0c3b1c2b769b358fcd4a7ae6b4e43893572f557a67e290ca09116cbade1a04a0f52105477cf5e53b3921050aabc7aaa79a84c1762d55072ede7ed51114684d9555e2d649a062f7408b4ba978482f32892effc24921100fd2aff3d7c0707221552e8ba0ab72e6451945c39ee467952e5dd8bffbe0df33d70774bfb94691304adb8b1d91d149a3d867482311e88f7639103362467cdffdc277a7c55d5c1376c6f515687ab552463c37de7290f36f246dbc7b77689fd2860088eb83e8cab4fece7d878b693470240c79ff45473214ed3590929a9931f104836215b47a3f8d1959254d13a78cf72abcb0428b141d0deed4004b3cc71859604dc0ee7b3a56817c23860e7126bfc71d1a1bd3f1c33793b62993283e0aba47b44cc8579388a4866d20200665ce9b60026935d150a8b287ea975df276a19621739e8c4f03053c59d8991743642ecae272eab2a5310b8a52ff7700695e34875727e0e495dad3ca5a2a7e93afd3ca70b00f5bbf3f87083c1eb03c878b677f4debd421fb0e83cf76b48ddc910e70c5519745ca6cc6accc6b89ecf0d9c6ca83f566b6a112b0c66a2d5f72ffa1b43375b7ad0f263b88edfadc848f84f2db505259784ca4cde6735dc03fc88b0359608cda697ab4072b52111b20018bd94a62ab1b490a1419bbdf42a6e099d8977f6fe9ed98ec98a464a00e547d9c6a371319a41b538051fd1875175d598f4154bb9560c6d99249926fdfd4f23c80c11fbc1ee8dd1f36a4784d33386a405b1ff43dadb54efb5400b32615819284e4aad6097cdcc4086f981b5e7449b25ba055faf00b8aeb8d3b03391b273f1c18a7c6fd689ab9417c8698ada61389e5f2d3ba8e67e6236ac5899d10bd02e6393594e21ea8995699b06ed6956745a2103e03f407b1c65acfe17b468ac9b071bdc007a3bfb0e6a11b439a45adacc1f5206d642fe57d8900912c40fc9063ab1834bc32bb3960e0c59d21c303764d44e3e2fefde563e4c9e90833d725d795313cb434aee3988a7f261b430b43626db807eea8fbde06b74cc9b1a28c71e6d7351ca8d1df888dbcead0f30d12599edfc32a88fc9617f63c3af436605a83822c224e8f1617b6a59388a9cd216504103f69f293ba3a8e4158625189596fa573b0f0db8a2e924340b0d49ab21df196197ab462d3ace340355fc73fd661af5f7341019a13f22ee91212399d9a16249971c323e9f2f4a0e9836706b1850ba467cbba1eebdfd654564482cbb875b8cc4929f6d77747ce9be48371047c34aa5c134dbaa0d3779159de43c876eaa3ab7018081deeea8ec02fbd56fc453fd1e2b707aa1f53f9d6cf6de17c4857abb25249eec4a0e788d0bb6e845b8871e3e8c70d6c6d1b20b11547273af0b36f14c7fc071da6576e35a5369e1e47404897fc4dd2cb502f0c11309d94d07c99613cf5b7a4e45310907018c4d65b342a1e1257453231f21a388b82381329d0034abcce6f1e61ae06252f3b90e3916eda6fa5a85b4885026a5010cf7316a961ef404fb988620467a1424670f84b624206018403ac6cdf62e3817595205ba6cc55662be65b0dfe32e851e439a49564ab8d44c4c8cdba16187aa15b1b69be120114c65c7bc1762be265df548988a9071747544df3186cb541bc6e1688e88b2a1141d20bee962f3e907879e4d7b401b9d20d6260035422167ffe50bab7ae126aad83fe8fa5083075844ce187d51c25e232d15f4dde7d04bed9714a8057ef2ef4859aa569843754f812b45137e17e0f8656857a28b45648157911a43e08aeb8437c01fb04b587883d25407f0b37f8375549bbbf5849660352962acd01dd345448a00fc4b44b8a1d68d76560e932f84ee3c17d19acb445d9010490a8454c81570785f735031d80ee1b0fac54079d8f4e73d0e880fa5307f1f34233d1b083887640e690c3797dd9df24bea8e72325622bb75be381f95f4eed8bcc969ec610e671205432a9704d003c5f03771713bd71d9d1cd5d4e10d8857450232bd4749a1cdd3432368a61a8ab9dcc03f8a6279fd00532c667e0a490616840937789e83ab6bfbbee43aeca11e991495491e17abb572375b8e98d2092e3c41340b6f44432a816d42e1f7303ede4817f042d3dcfd05382f4a8e75f060466c3f96ad7d77543fe71a774f9354439f1d5ee100cc9a98dda7867e028eed614d687b5489662ea7773e0247e2e556f15b501e1a38c2c8b419bccc358aa541393c29f2c2edf42a5a40488dc0c64c227509e2667ae1028a5193f30d3ec0833ab45e43ce7412e926b6584a005d1d21dedeaf1c3d413b75e9a7137185be258362e55ca5048881c9f3a7683e11b924133cf468925cdc0c28e7a7c4f4d090e46f7a4e3e256acd689f959c146cb99ef5831f72fc4ae8e25c5e1aa4db12f7b2130450552eac44a9e0c0093fdde2182d6445f380dd3ac1a7231da04d4b1816c987a9ab820277703476348da53dd08e0887319f711dc552caff8b8f3f060c055a402e407607ed03606e7bf461c6b570a60bf40948ba4bb7a44449d955b041f9340c3717e5d7d5d7ea2b12af16268e11b5e6a5ad51db6cc5f2a61acfd9065ea1f0264b6935d30c6b69722f8fb93109737f83242524f725c05b0513ed3eb4b4d1efe3081e426d274b62e5cea37cc164e2ec46fa62cd9b2f84e64c5ff1fef4dbb5dc35a27c35a39bb87c186a89ec0c42e47350e9dbdcde049cf3db8fae93289cbb56200c847d0e791cd5908a2d06d9fafe7d7ae1cefd3d54c3ffd17ea91371b67f378591df6d1b8abcf54cceb92f7b41df59266f4f919bf2143f876bdefc34fc78e66b39c934fe00ccc7ab1ae9404713f7aba840ece240eed159fa64cedb76213e3ef5edf65e35de512f61c746d05a8005b3eb61d333c196686d26c82337fd63033f4dcde2d0c710ed6e4a625829f295749d35e3c23d8dcb0c77b35ba6dd0ffa202f5317ab97f0a1b5df0a2226fbc399aa9a0bf46b95f9dc06403f511ab176f9ce4889de3461aa702a2d19f2489798fadc4ebaef01a6d488b4986bdc74bd7fda124d2bb0963fea47a460fd0dd4a58b772768fb2dcec032ebf8ef93dbed160e9b35834efaedb0f9a6aa4d1bea58cc5ef659087b33a68d223056c0e84f9a17fd56e996c7e87889c936020c80239ab86ae9447cae6aa9688ace516325e84dead915b98a8f15b357a8cad20e72279eb005af26562b8a027535d97d1bf013b5d4af552f667586e4344d355f76380a028acd08f51fad1995c71e7882c71ac01df744fc4b702428119dcd09db906caa3d9adf02a324bb6b0b63e99d4271ec5980bfba68867065eba32ae06241323afd217811a774ada887b9b822700916e1530df53626bfe84a8ad41b7e6c3ff40eb16b2bf8fb4ceca00fee9deb35343643bc21fe71fe8fe7dc8431284cad82dba817665b6d35c2c33bde8247609b0322dcdaae8e6b1d804c7b12b6a8f841e65dfc1d9ab046073c9953a6b97ed32ec1db415ad86797207e9b93c979c02aaacba2a3bbab249dbc1800e355a1e99dafcc439d38e235ee95b5aba3035aae5aa56b7b065f8821fb2c870379378e2914580ba60a28b84c7570d036c682643d32d0c92e1550e40a54529bbc55f6b04dc468cee2f20d9b40c40e02fccdd90328eade4dee64de4a99e0ea2deb8d6294ce0d490423fa0af32ed7ed7f4a2f68b7bb2962783e30ec67ff73e334160b812e65a1a7e4d611150b690b8b08ecd14f3ccb602d73c0156f2bcf619f0fed018df011215d416b202f838da6687b74445a8371d3b4d287b98f0b00a7a2a728d0294d2d2510b06d37bdce879963b4131e0f393721a72438d7b9cf0708d016944c35a5ecfc5482ef51100e3c14f000aea92a4f2c181d4e97f0ae9924054bafbbce4f0a350cead08e36348619126a6cc2b1247a4fdf84c9acd0f281bf38133607667f9dddc10327d62d898719c2731212ff53f93200d8a936b5fc3a2820088b7cef8471e99bd00e0e006a51217d04652cf25961c31f1915968ae88aef44ce160b9313bd990f11009904e9efe1b1d48893e46b6766e1b1b4980fa0e80cd40c5fc01dad9f7b0a215c94078464f69a23e0f39a493cb62156767d79bb55aaf38488ce49358a2f518465756cd700ea3039481cf4053bbb762cbab09b2a202e9efd3ae8039c759a33b726df5363d63f760c39b856ccb70171d439393e177f400b0c9afe1110e6e263bdfdf36e0285ebd63d5d16d38afa0e527fb425b5d36d38915a9ce0aed1f6376229b3830cdc1bd5bb07e43ca5af82b4b32bd46ba79066cbb022ad6a4d17cc3d51f16eb75a25daed55f0154d4d037a4fa0aa748d95d31343bfce71318a933fd8cd6834c48ca3a9527130ac205093ffdc3e86b04562e743f0f8b2f1d24a823f8913602f1393620baf51aeb2de3134fecc46a8ecd3ee067ac4fe6e04c4f9396e5c72ccb162cd45978d04fb7344462bab4d70da2e1e6b224d94f65bfc0b2ad403c6994738c43112da60c9ee15c16012bd4c10891cc05325381d7accf3f001a4425d79afd298907af24b005ead6f994f7ebc2b57409c5e15dba2ae58537239896f31710f0ca62ee2974a20fbc82e6c42d98fa92dc318b0558a4079487d3df067ac7625289c12455a7aa97f35e4c1e8b7c5721b3edd4548b2af079e1f1b50680aa322de550203ee4087fda5159e829108080aff8cb065928e97de2c13a00aaddf75812046aec819e3a2c7f2844c723e9544c88498ce2556ca6c4f0ec279f986ec4bb05e8715ef6b43d3f4058a5a26b09f811f2ed2f6004b4b98cf3c43e4a9c1d15ab27b5841aa6cf4e8def68a36210903b91a887a8d8b78c1b8b94475cdcf46acdcbc94f7cd7dc54cceed44e5d9a3d45a62b7ee983db16baa8ad2c4b4f1711d282c7848da815874bd08082ef849ebe17763f468b166147695d162bd05287a9e33a44b8cc29222125349c05fc57a2d326e0476bbba250315e655b08f0011d0c383614d1dbac4cb7be9350550c2462c1dbdb0ba6aaf0135655203dab8aa3fa8da93171028f84266d23a5697abd5005a09f19bc07839a0745c5b42e1b0bc1f0dbaddf49e37f6febefb4440ba2bdd69f7d4c0d45ac64c4647fcb04f6e7bd85d7bde2ca00a8ac1446da25087534224514f489d71485f98b2950a894c1c6bae7362e6904c8ea286bbdc853f5b75ce244bbad7cd417063bc7ed9fc4a8c80dda188aa27c4d695a473f287ef0fa9b4efaae486abb087eff2c2e257fbaa7afb16bfd401377e8f9523563e33d1d5caac071ce0157bd661b8422a3b7dad4d9040c299d1703d5e92ff0c3baffef04c03fc2b757181baa73bd3981681d00d4a4bf350c2e35ea275ab348b53c8175f8ce769c26e0b537f64539411ea47f79338abbacd386fc2a492d8644053dfae08e3af8aa0dad7cb211ac31f578d7c6b095d5b9f2a43513ab57321f1174f2bd2154d41004b58e8027ec23d0ab0a6b8d9f371d4d62c014e412957094c70dda95dfb3fcac82819da86530c177b8c93e420f7d636b13fe417b5dc1ee4ef72a8b2db2acbb5a429127240988041cb2e7b3870cf6d390308a74f2358901df4a70cd23b6a27c8a7b4a98882700cb330d12069d6b1be75ff598c3ec1bad30307e721c6486bb41e3dab09fbd43257b7564b4c1b1900ef992403bc409c42660cc6138fcef090109a00dd4896468265c5cd7f210770064dc4302c1e219cfafe3dca232d12630284c8854870bc316b45962348b5496b122aa0d865ff31148cde174e15c27e6b962ab090c1ce7a24d3e2ff81fcee6a42c7402fc0f1b7a040245938b64c1641d8114d26671334721ffc694c8d1a120725c4f14c8bc6daa4590d91dc67440d60de6bbb2f793ab570c0c807bb332e10d7429ccc080a8592b0dc5024972e679590b885fd0e7515b40f046f81ba9e076e5ac5ccb288158f3d96c17e6c42fb0a5d13fc8dc226e2d5cd3a754c14890bc372bd4b46593b6c6c0c0519579559401a4b3e040042123e804b52ee179a0803b5ebf01270223ec0284017380aa31b56657a62c4e955a6725e5feae7b6dd8c6698561f295b3e6325b13a477508e9b6db84b25528bf5fc6fbf9b4aee1280ed9137198c28f1ec6e97b1bf9495e15a1ef0377003cfe767c99ee512a5c9396d45b522e11b3782e7a1208f0155cc4f88420858597c200f85780cea623f210442c8caf022a22d8152969ffe66adac5a40f5e5d1cd775f1a237d6aa107bc7769e859513803d59a350ed6d85e5b3a2c0cadfe82c495a972084403e5a3dbfa80878cc0eadf650c44ebfd03d2a4c0a18308a78cac874a2893b04c42d2e47c9cef9b08c1a9b6f6ea44305886062ac220415f07a287e1491962d3b11049513acd83346d67717f6418e3fbadbc32b6d58c199a19e9b2ae170a38e73514a525ea0354a726050182481f1a8c7d2b35c411c0af4fbcdc74c8bc362480136cfed73a097ae86c356bff56428ca888ea9cdc1064792e7ea78201efa60a6543c8775ba1c80e27885c5e9074030e0575e10f83c985146315f79b55873a8ba05fdba78c08ea624331fbf9d296ae24aa1ff97f5f47093dd011014233ddf5c8fe8452c141311d2817648584cde558e67198247abb3686bcc0d1195e6a95377969833f4008003b07ab7dd67b06a3d27b0c22af38f7bd5184276227a396f910fff87e8bf2511638d381dc47119111441380800ecc54b1b8b293e1c0e98302c1f76ec752b378417c2576b8c1005615c87a8469fe5ad6ccc589ccb37656c4cf3b3fe6d6416b1ed466ab6283e9a12679c57ac8fdfce0c2c80d91b3c03a9d8f67723ed8e3958096d987081999d11a8001a973efed348ed914801048096e74d119c393fa280229de1ca35e6081eb8bc8c24b5b83e09728c58cad80251b4fb3b29ec3be16d8749d4a52980f637029432c9783e74dc976256523b039f6705116b388247cfb40eb07569ef48553c83df51f37ccf9b84f540b87d28880b77156970a19a490ac9adf38165d25f488d218f14c02d77a3bc3cb2910ddbb4dce317de04a0fd8270404202ce4d2820f8058cb2a7f427b230406499fef63285e84a4167d9bcbcf41f9d418cf453377c862dc0dfb90e8305635bd366c7a5de9c41f6b69c6f0128fdaf2f9c472c38c404928d90bdfde9e4ed7b4a522af57976f1862ceec8500c78a936b374e237b9e1ebb63b3bb5ef78459ce5b01b6c7a9af6b6f8b08a0aa4cf38dc18c4db137722d03ac9846c673e91e724203baf80aa3a8088d209f63e79ad2005c9e2d207958059e26d2406ac7324d0d31f245136fb583227c051e8394f0fe4885c974b47fd2779a5f5d6532a2d3c37a890d0e2e9b5cc425e75823e006a4245cc0f45b7fd1fa2fe69b605df52657c70d5f878c4cb8e0ba98e5fb37a85b03e80673867b7d0a9d748a43dc3b8f53bad7b212a0882ed925fd1ec5d18f4b71149e7438de403d8f2ca515c8056f31b3cd249eafd99ab3d1dac343e8d02ae4bf307588a7427b9c7320582b6d065735d07ab71288d2927d7645a8e0a7f1fe64da35be3af262e46fab4d28a5b9a5eb019fd3f087eed55a8a2826f25a9cb53229a6727c45d635dd72ca3ce24436ba80e4f0e054a864b92c8ca894750f327dbbd9c4aef741d65b01afae26caa6278447d452a705f334533fa6260857f71f5d676413e9ba7ea9cea27760426cd89aa2e28cea6cbaa6a41304413565fb506ae0d84dac030a25a0d70b2c5183113ae1059d1b89ff11708a801d18e3ebc6e1e1b6e04bf958ef853b397b8582d49334749e2208d7d44e27d01b46af09424f8025f4ee50d8b9dece83a166f72fb9f32dca7bba3a3180302dd16442253290ea56f8f9fb0aad48fbb8fa96f5bb8fe51131f3372450b812bbbbb1ca60b23c8d5161573485322b267129bb24717da221a4970707780a98709fbd81d42d6962f7924d641948f2e8b3b88d18589f6262f6f567b37fabe252f571a7f1d044f4005f64b413e1a86c31682ca7a462cc0586291c7abd47cc897753787b6737a95f397b645e7ee949288044cb90e8c90b08f43a4644357f528bc0dd7f4db4204347f410138181a30f93b1a18f47b3c714e7927e760c17f1032ee0782d716f5d17ffcbf8de92431208542ed503d25d7d6d9bcbb6142f055778f33e42dc3496306b460354f2f7248c6c517f9be48f349103dd44b0282655d9f1f5cd4926129710274fe72466492a0a6e60b1d5838b0530aa183f826e7a7b4bc6374e423865f6c489f83a284de3289aafb6761421973e74eaf5ef09975b8251a1b3c3b5516a213426efa949273d5e3c75a6b7c70a4dc40e620fc97016327f9cd823b18927daf0c3306abaf8a2272ede746128967ca2a134daa7bb326b92e192d5024897728af28a682bbd8f564ce62a31a67a35bba698323045af03c47f0cc979b452d60b9396bc35da4e21673bb2bb66095b9088adff676d2b052f58fcb5c81eb01c288f213a72e4049964a0d62ca4480e16da8d8362189b03fe4722791ad08b268516d013237590b747302163369a042ab48486423099dc0b74f01b44a61dce422316b7c3fa6caf9438c0c6dcba4a26bf4a6500b4a9296eb01bac46356ae2904e79e580448715a0ae024b328d21de5ed7caaad632c897f0d75118f40d0b5f15eb0f92e65486f9b76c34529de4a72d62a748a682aa426f82d506b6248e608fcee061c6722607b5eaac444483567b351f67e1dd9023c35f961e7f6afa1dc283a9fcc1e843c9206d1dab20d59e159f1cb1d16394b1c64f227928d866eba26d109e5350ba4df88e9891e135b7393ddd0706ae72fdd6e464d5051f68e3f842eb180f65bc97fbedbfde3564194361c9329fb6982807f9cd319152075fe7639fe1a88ea0b076eaaba0e23ff33353cfa1db34a8e7600cdceb7a69a59fd2b811fe26069920242761b234f853f6005c5231e8b2e1ef262fadc6f5379102493e89a34e89839108c0b6ddf734cbb0164f0a94f9bfabb9771365b2cf8a04d9d42c3ad1b268ccad26aef2e1faaed74ac4ff508bcf66a1850237b6f0a40a40570e4c0e7cf1023a64127859405c11b5c5099e61cbdb628ca874ee3b2f9d0021804e75ab41e7188dbfa44e74e25ab48a5959d1b07185f9cef80834ceb84ae09e802629e09fa816481bfb6354a122b1703a664bf0bfb5d65a6bd64d22dd44c8a8496477f70ead07ec06f906dbdc4a27398e928693a4e3debdd3df8d8dcfe9efc6d6be7ddced86f377efd12faeb857dbf87cd32d9f77ba45f4cd25656fd7f277b54bca1e1d131d3ecb1fdda1cf2c877891a6d234d2afdff4779d4a53087bb540b74016666dd7ae6561dfae81ac57cbc2be5dfd552b6ac73492a549afa45b99a4bfb82dec9aa63fed9af57aed1d4bb32ccc834bca21eeed716fc774f6a84935e2b1c76317965bae575b155f5dafe2abead7b58ce5efc6c630d1431fefdd39d2e15c21f9e86eec21ec1f8f34d25fb5b58b421aa4bfbba736bfb577fafb9e264d3b8ca9f510f7f6ed31cb21d6d85ee9effab665ed51071d608683dcb2dcdf964972c94bd9e458fe52367699912240f292e3207989c68ea374130d270a2637963fec94f68d148192931c4789ce4ea2e1cc8e23cb34dd727d987649d997ae0eefb6be78ba5169caee21ee78cf14c9cbc49d019416072e213365208a8c35660601ae449b24aa13a670da168e54a936bc68c719ad3d03eefa3a455cb82faeb8d166dfe0886fca2021572acc942a9694a1d99fb7bf1a53616a4ced316f2055a8ea43a2c8d71b0cd9d4b6748d296171a39e3fa449c64c991a53636acc4462cad4212a0db67cf5516362aa8f3a84cc72b02c15860b134103448a4ea4d589556ba2ad93da3a6977cd29278c89222f95c9415823e42c34ca3957e00c84f3a29d6ad98aa2208410da0025ad539f4813cb4d292c9f8e877ab4a31c05595cc535541d718991d2caea50d475ea17ce6450f6ea74aeed5876b3b6d2ac14521021420da968444a315bb54c46d5d49c9632419448222931b90490036cd320cc3039a594534a2925b5a494726a5a99e89c734e59e79c524a29a59496ce59e98c998c525e4ab95d0d00479861812816086e7cac4564add5ce7f544a29ad7862c38b92452184d7e29260abad2c96076e7cd531cbd359c44e9b3f6aaa15d658b4d65a6bada65a2b8410c239af33c1ce1aa94e763dd0a8c549cc0103188ac415598491235b3e5a788899385313675866ea67ae34cc9ca2cc7090bbc589004c1d9392072000998c590560b266e20c49a9240001c8646454250094015801863a6d94a9286c9379802462e488dcb40cab7414d2b148a392c994c99895a20c000c60a0959ab6aac20457644a0f204e9822ce54d6468a20333351c81344a111ca007af418a2c74c9c89343d7af4e8310002f070e261c890210420800d2e0a600518aa802b37ca703ac9534a4a4a0a4d0c2224ce8080c632c8d394075239618a539cd184a4a4a4a470b873a1849926e0cab53f4571e27137eca200f2628ec3f76e053040bc98e3f0dd346880165a460011504b2dcb4cc7619b85ada9a9e95c96966998eb4c8a5aab8a93a2156a5985598a555ba65de862e448c7e1bb6919564181da49516b2b4a591de63a0e5b15452d1460606060acb5c25a1163792e806f6eac155d61ada0585870a5f807986c856487b98ec32151ec30d771b885490d959471c239e794b3c25a6ba59585d65a0b2d6d028c120b713f294f29910c910c11453e1a89457c910c2a509a8af8221924cb4c8d1059c4773b356ad0353030b1c60250cb34cc75eec59cd5b9379381fd3a0c893b8fd6cb6ab2885944fc01e362e4088faaaaaaeaf19b26ab0cbb880c91429a6c3a5c2443a55445c9222ccc65d9900614026de69cf1860a2271881432da48984ed779313145c4c49a1824d6442131484c4c4c8ce7b94084b9c0cdcd8dc7f30185ce408632c0951c6a98b5a6a68685852547102318186992b1a6a6a686e5038a4d8835300ab8722f6735af9630110a5b6a18f943559d878342923bfc676dcce11b67145d2d34a8c9019ce153058d1328286957b81e1a8074617fbf02cd13441bd2d014e143a303cf0c0d0c3c40a031c11a9ac8a169820a277c40d3840b689a18014d12ce70d34f4c4f50bac9cdc4901d53c023095fd81f9ce1d183092b76cd99093151b3271cb2a709e865e247ca02688c189052210c9bfe05a59452ba51869c986698c0040d7e70bf0a08da831925543c4320e2238810acf0e9818f2a3e23f0a10614aca0065e16688400028d103334424841882ee80d6964c0c30a595114655534333ea86c531445519e11cc8ce023733b235d918bb7e099e26e5208b999123c47dcedc7678aabed00dbc8600417db90660915705358a204f7074b78a0082460f1e102128adc5014a28d945e934e1b63b596c22aeca4945927d65a4a279d934e4a77bc3a95856952432b488d3336ff4f6436e5949d8a08dc01db34e64a8e9923470ea982cd8b76aa652b8aca9123478e1cd026478e1c73259e76902c5acc24362fdaa996ad28aab256edd0783a4995910cee678aa71366c1a5ffa0cdf70252b123e4e608c69a2813a3c5bc1035679ea121868881b1870eb8a2c3c6e6064489bf66c7051ae14184081554402968686a65a950c856d576acadf50135ee69d10e8659703fba5f2cc006881233cf0f2aa9f24108e185109bf37a413b2795ca4ae5e88739a7ac1565294a4a295368bda31f7eb89146dd639352421c29a594724e26200c423529a10d1aca6942789339b10f5c9109087f30d9271b4608a1561285d8e32694e26095524a2985705a55052184b06282524a6b654355293be7b47eebfca413a3b4d65a2bca96b04a2184b02329dd590c3171e32bbc1056940ed95073060390832803e53f29e5254f05c6e3caef85ac7993b5c61e30a73a45181d1005cb1616c56c1e16d9a19968034ae8a68b12bae9424929c54c01f5587f224da549c3affa8bfb0381cf3b9f0eee58590e9ff7bb75f0bddf8ffe38b03baf1d7c6febe01beae8100dbd93431d8c753a577e8932ee75bcc425c5dd1d6bed143f1e6338b760b73acbbae5d1df8eedf9963f6fbb6c2f4369ba17a4bfcfbd7327c9587fd937782fdb7ff2e7e2edeed93ddd61675d6635ba5b5a6636f025842161c8ceca1ff6ee5797b7eb2feece3b973dc7f9f336fe35b31adc8c7bef30a4274369baf76897bd7aa7e15f4f9ab4cbac06d6740bf6f84cbb781bd3d6e1dd9d0cb765fa6c70f19e8694ea866e82372cfc695dff53a20e94d0b45b79584a4a4a55dd1b0afd26d38daaba3714fa4da61b37b6a6699af64dd39428955252525252526ea488e4d68394ac410c1eda67ebca326de92a23c125a76072a22109eff89ee792354f7f2ef9e62fdf7b37ed9e6701ce73bc6d9bf7f1dc73ee16b0804b76c92ed92527912df0833cff785c72cefbde9cb305f2ee21928185bb14655934ac65b12675651ba6b87b37d6b1ec2b8ac6b667e9c172255d43114a249a7651e896bc8dfbc682e58fa5aaee0d857e93e94bd9d837ed9db669f9a3b1b56dd3344dd36e77eedabbfcd1d8f85cfe686c4dd33496ad7d636121d134fd958ac04283f411c9a8141a7d42de87d35fcac64a9aa4bfeade1d0a792a534a09cbc52ef6ab3f968d612c2c2c3458760f91246f1014457514555514455114754e07fc946a5e1645519cd501eb4a7f378850425282c384e4961c47a984eb38229494e85092494a8ec344974870542bd55751940fbbc2909444407d957b40593aa7151742e7891eca30fc0bcb2eb71db3a1fd3ac6e91a9ac6d73df9d3b8edf2f8b0b94cd27e91345d03bb0f9f0ffb9efbb4ebdb367d2fddf239a75bb26fd73bce324f140a81402010feb6813076b77befbd5708dbc58e5b5ee6ee9e7d3b962974b1e94cee7bb9b97bf850aa60fdbd6cfc6dd31fb6756c2c4d9e50487f3e7836b0b1feb443fd611c06a8ee5a7e99bbea740f71df6983e330ce32ab81e163194317d3f32167bf48cdc7b5d50a16f755ef85523b6fb15ebd63d17a5b790012040cdcdd0389fba6834a930e1d2ff485bed0205c1d2b25d7b4920c25ca0bbd974ad3c9486a39b817666de8d8f49d0a9357a61245c7b65407953240e83cd041a37fee095d7469f73e64ba0d27a46ffa7bb9cd6cdc6b36b8e36b58a66b701a4a93b61d9f34fa757af3a71d6b1be93ab6f648babe1dff66d275eef7abb6a64997ae214ddc39fde9d8f758e3d161c84b7aa11b7f1b1deb96d12fdd12fa7da1fb7ecb9fa65fe81e69284da063a1c38b3c3704f25e7ff5f7f178b6cdf3bcedd82fccbbc7b4edf798b7fd6ad837acebba5bb9db8e6118866140d8b6c76fdbddb1b52d7f3af6c61dca95d2374e7fd5def4576d6c3bf652861205cba876efbd31b371efbdf858feeec6b6e37b377da589eac0d92f5324312287946541485114f568bf5bd082d0da6b66148c973a8df80f30d1ad5fa1771f91b66fa4d02d79a15bf23cd7b84d7f18f8ee61b435b06373dff7f5ae77bd7fa17dbd6ff7e4cf0b9d3bddf4bd201ef6764d933ed7ee1d67d2e7dc7155ddebd1a48ff6ce9dbbf7ed9c973f1eb6e685f0413af44db784bcfc52b17ea93ba42f6944ba44dd682412894458842f8661b732f683cdcbdfb7779c3f1e36d634fd9978aefe4c4a4a48484a251269341289422110e8f3f1783cafeb380ee37bb74dd3b20cc3aeabd3b1b666ed0b0269cf3f1942171e6a6b271dc21b3cffeabe87f006d04324fdd9ad5df45186d085487f76df7ff20de98f8b8ffee8d6eed91e95eef8d774f587dd3b8ce965789ced3becdcbd19ffdebbbd30c3fdcefefda5f280012a8b2cb218b25fa8287a485c17fcb03db353d65f76a5e6abaa3e236198f54b4369eae8faaa3f178c5e00db7145e9841e789ed043a483fe1971c738ce08514cc8741b4ebadfedf736b3819debc1646b60c7dece5d9b26696fb9be5ddbee3df77bee5dfe36d2af534ee3539c2ff62b7f2ffb7acc20807fcf1dcb24eddbb1aed3244d676fe9ae3debaebde3ae696efb76eed7b7fcbd6cecdcb771a40fc334e99c6e21fdd22da2732ed8e67e49587f2ed8e8c3853cafe7f407fa7ceef5783cf7f817f670c798e79ec32ef63cef56f630c618e31edcfbf87b394c7f3bf6cddfcbbedda15c21d9b044b9c712051fc6c439fbcd9f0b86711cc7c5cc013b38ee1797354e7f71cbdd6fc68ef3876dfced2df7d8b77becf7d8f64ee3c3985c961b7334e02671e7f40b335c520d7c69ba1a4a53f6ed627fc1e6486a4afcc2b6aaee85d332edaaca5f0a353b94d5425d86dc75e8b46957e7349c28f63a60fd941d43ec0fde25851a317f611e485ae32bd22dd0add1479f5f0f899edde3eb661abf6d17c5a854de9ed3bbd65dbbcd6ce05f12771d4a27b90e262ff9ddb00ddbb66a63a44e93381d8152e9e230d1251a4e947bed98973fee019f77a7dbb1d3ace5e039bea649deb57bef8eefdd6636bb7e948a927f212aa48af77a187393644f9a4c5e926d58402c21dd695a4369a44a3dfda1e90f9b908dcdd6b0624c9a4c3e2f65c5579a8cd8953a11fa27196594b19e524c320e8644b9626db8d1af2b961e8a0ab0af1fc11be2eb4950a5e4f5def039842b1444a1ffbc244f68a2b754a66f41afe963de948ef28b2bb06a0b17eef8afdaf59e7b432c928754a176098b3bcadfe8f631ff44f6ff7d69b60ad054ebc962c1625163ec186a0feb850a636d2c15accd9cf11d273d3dd1933455cda4f2a8a77a32d1f424552ad7596b6dceb726269d8c31d3921293d3d33c12c5904d4fd1dc42ffc3fb44a582009aea89ae238028f5b5ce40480b17ec7a0d0454403d4503d4fa01e8a2fea3a79ceb29ff947f9af1fdf9e75bcf534ff554795498121206e28eef816eea18d83645f2cfdad053ba87b8a3fea8164a533d8f4893896e21e2256e22f4a33e801017744bc4de48155a8412912bfaf57488543179bdcef18844a9cf59c23ccb1f386de6b4d0d33c224df52f712f409aea690c1575d208ab46ca416e7bba3f0e6c7b931c73a4a916b143ff648f07d0c80d88992b2c25313850dbd29354a15e874c94ca79ef4e87785e2d1db22be92479e64853bd287f718bb4b539e9983465d7628c7a3b44aa9088e07fd521f44ab9c65c61a4a9de66d6a622e9ca439aeaafffab279b852196f56863a9b043aceaa38fe00ee52f04ca3619a07606e8676d76e57206e8feeaa9ded09e3933c76443ca0b698a99fdabb98344e270020c8466094758021273881f368c30c60a5cddae8ea954f15edd57d52fbce1da5e65adaa30d6247c0b5d74b71a4217f53bf0ed67e05b9b71973fbaad4c46f65b5ce798d5d15fe71077b48bb7b7c353df0e2c2aa0631d4303d85bddb65ea637631993a68bc4e1cfe8748e35842e766c4ecfe83467659c3febf79dfc59df7e516c66d9ad4dcb740debd82dcc5ed7c0a52f74616f551d4daa619dfa0c974fdb938b96082550d43bd43b5455955e3239e92392977870f6ee7d8e2986b37798d5f0787b46cc0286d43c4f93aec601eb743a59d699d1fdbacc0e338ec3ba46f7ebc2f81a96b5c3ec0126bf8730a44723f1dc6610f08e35493bfe754f2669cfeef97c3449d3387cf4e7b23fbf6e9e91ffc34cffe69885cc388eeb50195bd632a6758dfc3f5f7fb65d039be7da0636d6b6e73b360c0963da8c46ec4ceee9b0c935dd62f2ab5b48aefd93e171fe4c3494a6d23d92c3979094489a56892a4d545556a55555a5dd4adbaa2a87b6ceb76adbeab75bd55659af947049baa5433b5a6ee99cc21475eaa0fc791bb4afb669282dfee15a68fabb4019fe667b2e7fd73d8ca953faeb1c77b25f33c3c1623393715d3867d9f1b5ccfdaad90c4dd3b44c46770dd4e91af7da2fe8da76af81ab2f74b15dd317ba007d3b36be76ee33b40d1f93977135e99e54c373d0677834842e3c0769085d7cbee31ef419f7a0efd8386f199461fe644f5663eeec986639c89d1d77d8bdfcc5cd715a93348035f64d5fe802fb0e528dedda676c9af6a409fb8e9d5ddac5db1d4d1d5e17eff2d384cbdb15867d07ecbbb53694edb083cb09139c105db79e8d8e91b48bf340c79fe3dbcc8676ce8667cbdfc9deee76b7bbdded7a74b7dd83f2c77557c3df0efb624dfa685a5571dff7257d740d284d9d477f3becb0f1efd50ed257b784bcdf934fd32e273ba43f9793ed1d9e8479d935c2b0ebba2e8c2f8c4522d1ad2cc218638c9de8ba73f93bd9dcb5fcedb035cfb7adb443a1cfdb5b086f2f6dd2f4d19e8cbd6bf05ab6271d8731779cbf938d398c4fa4e904d6ec70a285e1d757e95069cad6c34c874afbb027cad431e50e2aa3144d78b8870eca8e7dcbb28f3c9c1dd381e42512d638f486ef71d74e33bc6ddd7dd8d8a66599a4691c242f1d9f440192974e52d29f0f3b3bf6795d86b16ee1aee996d1b3c3905ec64d50361281409f4ef62bfb743ad9f60ccb7e338fc7f34ef664b1892c661b3ef796ef65669986f527377ef72d67f9d3b1b3ecde4b79f3aa9dc189a265f97b991dc74d4b7fd8ef75d8b652267996bf0a4729cb361c255d4d949779753c2be24a4b559eb43405a91c2edeb160e29add2dd145a167a393f0b70b8f3c6f6b9ee669f857f338ea69daf5cd0502daaf2679b64cfa7cfedd8d499e0a63ef1086bc3c6fc46d5844ca46a22e94655dd7751d0804ba95414c78f2dd9af67b3f1ffd55dbe3d15fb5bdcaabbc7d6fe5799e8bf7cc08f2f4aaa475482f0bd35fece4cf65776e595696edb0b35777d9d96dfe72ec4c7f9d4ea6c7ea61c88eb56ead199d8e86d2f4b964e94d21be84651db38eddfa05af1a7ae7f30e76d1b552f75f5da6bfbc7c6ff799d9d8deddcb9fb7bd636fc5fb85f1bef75dfebcec1e66333cef6ef700ec9e477cef189f846dd9732fdf5f57cc72c8eef9e6b2f1c5ddbfdb65ee9ebcfd66ee57cc6c74d9f34b625dc601f4ee73a901daf7ddbfeef6b56634e2c69ed9c03cbacb2edee6910e5fd230e9f3f59e54b19410832f7892a8b5561caa3899613ee10222b04ea70847d440668a9c2bae30023261880fea892136c8acb516aa010b3c0032040b42a018a2082eb0f1aaadb5d65a0db06badb55a237c14c10458b8e00920e88f285ca00436050f03ae80c40984c0852c9250820862c8c1c6c0e3b232325666a603850c14d6450426625114f5841980a89c40d5586b6d1533530083a946234af0c11019bcd6072c9861c21245a400033a842744300510a78eadd65a6b251434c081a20627ef0757a560ae4130285028d4b003cbb256a0004411225042073ea4a042a811e20315152c8aaa4da834c220636baab0b959f8c2166ba870045c6ba60843c707b6d65a6bad750342553110814a82add65a6badb5b6b5d65a21331305cab02c144b10622971c2a8b8e00a9ca9b5d65a6b707cec7aafd65a2dab02a1835dab5d6badb589d75a6dadb1d65a6badd501a79b4a91d8f589278860d7ea04cd15bbd65a6badb5d65a270ee584185e30ab509493da3645511445f3435a15ad81f921d20a563545ece01104115080530513b620031a04206e0084084fd8d44b278841452f1492265a0348952a904a71738aa22a65a98ca2288aa2a01344e46c8abad95306360821e8743a4720711db144a733bd20062c50278e08d282a2e13671822a18010866e80210bc708414530648a060902be0800640d8c00b37580105821554912c8b2c7410862bd46c610c4a50199a310011451034974651a5f0441451108288cb0bdd0a8510339ba29ec82267535b0c8aa22cab0c29a028abbd4b4f817c40497c20081e8842103378c20f702ca9126368d3b9124ff4e403f107980479061a21a01544b4b86850441a6080051e4960e183285aa0068c036b6da863adb5d65a082427886dad053204246c9b852394d442c904333e54007345112250d01e38210320aeaa2b7aa2320795462aeca00a4a4029ac400a2105554c122401041431684e0010312e1a7d1051862b62d00ca5b082185705724418ae8871d920552c4182181755038511a0887155d8124a50022fc4b82aec095ea0a9418caaab89224978c4a8fa42d881416cd03082183493f088214f70b684592665a48c49caec2923553e9344c2a43571e1ae7696134f5d4e9226ee355353e6fc32914c862365a08b8bd25353bb5c335bd3707262b00f4ae14af5acca3687b6ed6632ae17ca70329cccc8bd3bc6dbc7d7990578433dcdfe44a50c8b0c07a2c88b90d8f2d9157005a661cb0b6355de11e382b0c3322332321c69929911699239d22445335b8a7e4c14290118432463e51d31aabc23468b285cf9288d90583443a9a866ae509cd3e9e4811812fe38c211684442a40a6c020968b6bc888a1b212229b694324dd89f68c896571348a085fd898a6c793aa15491446c19b31152e587901ca96224c369018350394ec18e78479a2bc8c6313b1ef7d891085b24035176bca908fbfed8316e29d85b8f1de51504284a424684190e1916639c5526e1a874754d382dc5e0753319f156de11e3aabcd74c99802de265fd679232dcbe2456f2822dad389570a08ba94b62321cd1a435673822995db379452f886468d59fe80715553a69bed264e51d31ac8cea78aa320aee2792d92299792a45322219918c549993be6e22992de3cc9534551a0a8542f044ea96139c173ef855515a6f2805015cd1515113ecea66a2d47f15566f28d5f58666fa82dd469a6ee00d1507e254958e97ea34ebb8aeeb78912a9d15593351ea85d42336bb5e0ed97576b2aca98f3d42b246eb087291a8aa1b60494949c9bf90acf586c22e935b1ef5724bfb43576fba7fd647bda947e8c9a4a4a42b2929a937b58834d563265cec56c84d09f67929619c1a35b989b6a1e206a594d2d336518a6ddb5675eacd858d48bf218109dccb5ee579553e46af0c3bb6d241ed1b1995fd75d1134661e889f2a84760769521cbc24895aaded893e5b17fa041439a6a89ced0d35c993854d2d82e36b9c9adea0d8d0648157ab10e797139bbea982815dfe0be38e8a18f0631040f0a437bd020542c3d7dd447fe476f80e9899e280f0a53aaaaaaaa375555691a0df8ab0d9cf1bc1aa147e6cac4a954c895bf9a7c662bc1191a0d9838b108ce0b692a49192424cae79fac893ce26903d2543f71e68a0e9c8973845a559bebaa369d4eb5199d24c3a874bd19bdf47a5371aa11a912cfdd7ba3ea1452a6f466ae5477d69b91a6a07ff526f48f562b70b6edf1a616912a71c3a034559ffc85b697334075eacd953340f78dbb3253555555551549a2a00107a181c2080d143034408cb4e8711d9e5cb6094ad90ac4aef50451aacc645410c0159669822d55a8153d3ec289cdc0cf86251b7e72ff9828f59a3fe6b4992b5a573f6e333d3837d88ed8a6260537bce8fc11aabf72761c86b439f68854197d45dbb0cc15ad674ebca5a89116b788d670a2d45f9262a1d46966a14e718f9965015265e4b139fba34e3367527ca5c9645a1b6b05b8327326ca1450c5f31e6580a962576ab9c03d9311694c28e6c65c1846a54acc9465ae9c1cf4d0e1ec7e925b649d674e282794734393523008ed71df913de64bf477dfe914688aa743e6f7a2447f304808d274a01457a706369a0ab9b0cb86bae757a7ea6c34357408b5a142367a4389cc6c1aa5280f758fee81a236d4469a8448958fde73ee9745290ba3317345f69828f5554547b164c6581247d6a6478cdc47aff7646a049ae243393bbe56fd8560763c554f3b9e3e60c7fe28ea2070a4e96e10ba88fa733919bd07ba3dc7c0b63ddc698d34551b9e73ba87b847faa3a78a6c2ac4a3691069aacf0e4083fbd1991d8f6beeb56191d268a2815623e510bf0e6caa298e3479367e55c9212c39cdd44473bd0a931cd22c21c8476964101ffdb1eb47039c1fa8542bdb297b1822a21100000083160000200c0a068422d16096a6699674071480106c94465e58281488235112833808c21808320619420001c618820c423765037c90fd8947b8433f93f13c16c914f061c6d59377aba9945632e809821fc9d762c3c08ae08fdbbb58184b299feccd4c525c35cb8db6e365b572c54ad22a1f1ef6eb5c4b2a2b88d84bbcb2be958c288d6ccd56e1de6f2d62324004f5093e97a9c0bb1d788f5845909a75faf864c8444c2e7d5620e154361dc499b96c532897456c6c921113633fe6ac153c6fa5069c33c16248ba7c41a10eafdf0fe8385aca7570c172b79061d1b9f76d8803dd5d474efb7002144aacdcb322f3e36f756c1239a5632c8fa1b03f6f7fa414fab6e4b92545738c6fa653c62fec35c994e805c286e566a92ee1d4b4a1d1a3faa3b97b56ce84484686e2298650a7b78d3e900b0b069385bd77843cd5105399dde70764e698eee20328a9b6c0cf1304fec897d8a7cee758d4e07e45499ab4b9e54a7e01c8da0c99c10a184bfa8e175b2f55e97bbb4fdb347528c9c7678bfd7eea48586679715ef545c12940718420cf90a3d3b52d3d374f8c9986a7689c8350488f3a736bb9e12e255e11ca2535e7d7963e196fd311545e321694d7e1df967ad2f2bc946a322a5bc6c5246d532042f6a4aca6e1ecd7c74b0330798614ed245010dd2388d846bd350806d7f502204db46e1d340502e1ab6b8c111a58410520ea3ac27c33ef4b00e2891b2bd946d3c8d2d878ff35d5b8bfa63d061324b9bf43a07848625f595facbe8bd6bef68932022f02645de15414fb19dee4116d2d42a895805301912dae3f7dd87e8d2bc0c09d513dde0dfe4e4a62a0f2c95121f53aedc828753c9987ec3417a8ed2949eb599682d97fb91a4ada83b1c1d79a23fc2edc2d95ad389cd269d3aa9ceb23eec3cfb0e8da8b4586b802a26f450c6c2060a7d21eb9d7b0af8bf4b38f759d8b1d976f58ea727666285265f2d50378086d6bf89ee9fa49de6f1acf4a12fdcdd5ccd411fa4c0a1b5c3b2987ef5d8d710562cadb639b58cd7a6a133906fbe6e87db0833aa414a04fdb1cd440519a21632ec7d47a1335256595a18858013386aa967f1beb845df74cb49b903e1b3b6753c627f7943605b94129ed2c975a0d88090319e8014a362c611cfc0ae671fc04db72954cdc0008eec11dd0cc4f18cd4d94106fd0040110accf4fbd0c7f7a2341ab6e495906e91e02883f794860b76e29a8e06cbc716e15b92fdd8c0ed05bb020c7d9b198a1b49354f05bab9081bbef44424bb23ebffcb92ca1d082773e727bdffffbc743f098cd112abe9b79dca11216f1629a773bbc3d80915b117d758542e907c4d40c69efc170d6eb8bdcd17d4460491e00b7b854578d6c9030a12323d2ec5856ed088edb80a6bdd55fd559af7d4dc9a2443d1c6f740ddda86cc55d4ba5468925783a22b09a9de57a749030b152dc74008858a14e30dd3fba6ab83b44d90a895fdb8dca549bb289b36c3107fb188d605674845a10232bb18499225c9bebd088b0ceb09b23ee247b397dac9228a5d7f22a096fb142b1a883146c92083328f7a651b8d214a8e4f3b4224a1b248490bc5c563a4be99d89a6271cf05b7a47dacfd4254680c2429c8adaab0956d21fe8907fdb6cb626461cdd2d17c23c5753f5aaf151c19bee056b22f371033dde2031685dc5ff23eaaf0fd5b9822f3aafb254ad542bbf83b6d1be3f29f42a55d1bb5b130a1049630ac8d837f4b5fb818f02c405d375f6e558990dd9700d044c8745dc89f5028d9cbf1aaf07c5484e154e849886b694f16c92c0a2e6eb0bc52c4de1db1547496b081c115dbc744400227cca950c83b65d638e245065c995b79da8f4f0d2d3e3bcb43c2b83b079441256d6d29b135e647892bb9b340faee72573521e6cc5ff35cc0f811e20aaf24b8ffd663a529ab1debc4acaf0a4669146218125ed6741f9404ba2ca6aa82f3b2f693e5133a796d6632d8d207c3e416efe038cab12b92c15e7c0a4c1c1180a4ceb2440a5a1a1dcb020aaeb422a825f9f6e5d8f84ba243b9e91e9e06d8c6fec02e07affcb117263c7b6f5f6fb5e98bb96d6ac69433502f54d1d5933e46b47a5f6a2da643a8ea24d136a06198aa5e83683903fe6b46e0629b5b387d4a5496f865b450e7a93c664836313106041c9eb0c28e9dd9d643412f1fc083c7d8a2d7eb620f167fdc95bf9ea5dadabf0a62ad3d8ff3811e7df8369276ae1f32d9f152f4a9cae402d301a7d5fab71f081eb7732132867e8d3295ce47dedaf4ca1bbb55a988114bef568497dd666090bfb156dc7fabd639c2f1002c8c055adc1e60640d4a743b1e33783de3acd9a3f207b91b78385a8ac4cc3031a98b9f73fd05103533d29684e40fee98fcfac9e0167a627618f34f2083ca8c0838429ee72fc516d11214f1d92f46e4c1ca3aa7ea8c2cdf0543b6c11dc4c07b49255df233e0a8a2e72ce4cf18f7096798c91e17b4112e3c7bec3782f5097e44255d35c45df7e1c6e9b569c12e3e177e9110cc422e09f732dc5c03d7d052c8d92ab14cf7cb4b8f62c33edf635e5aa8c91b2a9700f68828ba5be3dfe8e7e8dac08140209664a318a99aaa79cc9edc2504fe3aad41579540363cfb40ab030245b40e8e1d9756e934127202afdefe99a48d5ec95eb062c6b79f4a494c840a81ac284bb163571c582389112f0c44829ed8b5348bf68e1ce251e0c1f243b0f88f0085aaf502d91fac24a2c7e77de1841d2b3c548a22b2ea148c199401d20d57184d07b91283b063a88b973c92387793198402e8aa8721d8efa268ea09e2b100170df23916e10d0f1179077ea032308e6a14380328460231e0add915d227aa6507b745b9fbdf4016c553be1210712b35bf092433b30b2bf63b06e5487b396180b716c41426ebac639f662a7fce5943e4b133efed1c922817f0724161a4e21a2769e6c7538f2b70ec45da7cbf0fcf46ee0741bd69ab2bfc14d39ecafca5abeb0449893b2726c7c5722002860a689ef4afa27e2c303b2c844d1d5fc8fa02904738810b98670aefd3de7bcf741218e7f51dae42755ecdbdece71c9d91d724246c470f2de0bfd8dff83f8ab780b8a098e3b0d1c0ed4647742e5c9cd59058d5e860d02880ae5ceb5c63441d3d437c7449516979d4676d7cc6864c72ae92069c5d00227cb37017264fcd16de6449a11a8fa71560c0211207761aa821985bb8b40cabcf172acbf0d31a682a6eac4fde42dfbc7f594e3569cc124e7771953b5e16bb2684a7a7a4952c66b7191dd8ad055448527b3cb69bde7c3acc3cd9c5590caa03a97121a34a2215c71a430b628b31b52fc8f0e21fce90c12f0114c71dd2146614b7ff8c989525a7ca6442cffb9160504c875b24dae8ff9559807e7cecbe2e831ad7265f31c875b4652b122a96aaabfa569dece2e9ff2ad255c3431d5f57c824616b6d90ea0b4a37ed48a2d14008f80743cd1df0e1cca6e935852f9c444cd4ea56a17ade5f5bfb3f19286008c16858c80cc7dcfc1b60072f7782f243ed2c8efd0037eca0934f9ae2178255ad2003feab7e7de1455939d4226cba877cf6d41003a95ec4b4115e6367989d900bf9682a21fe6ccc70be9575bf53525ff903a0bfc7514dccf7f5440c51f1370669130b95fc0927f60828acbe51963cb504ed2c4c86bbae71020829bec29ed589c6b4a0623fa2360679f84a8f4801e0326665bd3afc5a094d49d831fa1041f3510b0254cd0406c33ec53c9b09edf804df4a2b61f061c65560689742f2273dba6da32db379741e6d0f0580494b2020acd987cd7d56af790e605417aef86d46258d5eb14655b0d2cab676a56537f54d6bfea62b7430f36037b1b002e726031ae727c1278495ca9cda701d2067710d83570cbae441d7ddcedc531080509f7c7419b01b79204511e9aa67f88a4c56d6a85e99336ded6c4a29b03827ec77b42223236455e465e6e3b36f08437894671375324bb71f43fc071123686d874531e3b29f5b945b70a498c02c84f37fd5c960ae3f6af149db2a258b6a7d80f5bb5ca2051b12f6a51124fcaf258e15c5bc777662666a8c085b170f38456195b9b8a2ac02a6eb229f67e985d79502c1112c8b58ac52a7489de1b909fb88af54c762a172ffce3bc2041b3e1baee377fd7b28be2073d2d0aa08718f649f1b07b17a2fa720b8bf74421e50aebbc7022846ae7d3ec203203f5399fb0bbbd6ba414aebcdb1964c2275536ca2a1f6a7f0e92b041af244fd8676ca1619d901069db09eb66f3764a82a51a77eb30b0ccdcc84677c9c6d3c1622b890642be6a491d59545d07f6afd78a3a524b31becc9ad472a13c73d128bb82e9c4f59926d304d6ed1586d43bbcd1267c640ed403f60a3535d49280880d88417cdac8356d8452fb80ad0f965668f25c210e7eb2dad898ac665482d1166137abfea1fb0d7c0ddfb6df425cd79ef7b86e6d87f3b5b72855d6577d40393624c745734f4e2772685ee45671decd21ae774e1033887421b8c4fa592a611abda522baf497ee1ebd0b4797c3ea66a1da6d21627e812c4c38ddafc6998fd8c63efa54bdc7f54597ad04b058042dfef22518c845f1adbf038a6a69a4785ab3c1dc07342ea331e92e91a340209d5c530e834973985146c880a702c9e67ca9deff172d663f31f28e0914873705ce7e08c28b6fcd4ebae793680d604eb66603babb4a8341b84a0f2e5525610656d62725ae172716213b5a873bd38d2ddbde04f4cdfd3a705da8b53821a78e8b4b04b2cab88144aa5fd5ea6bab02b841f174a37033d05fe43c6780e810416f631010d44f2d65696d6edab7cc32d2608e868369be1a26356b20abf8803ad91d54892b8184da32e87c211d2af3b3d8b22647e095e1515efc93a67edb5ec2a62d1ac8aaf1f3a46a796df16c9e480b8f2b33fc268711361a6b12fd615bf5da791592b4ca3bc47c8054bdefc104aebcb7b643e85ee846435bfd619948848077298e4bb6a5af9e2019b737cd556aab81b707e75b1ac45526bfb5854be327c20de4737ba3d13fd916e5154c442dd6a137b5ae505e30a52d524523fc15ab6f08f19819629c1bdd15872f47a295f8974fb1f720cba5eeb3f7f757c284ba7464381a9996214a20acbae805d267a986c7622a9a43132a674349beb1b2f527264d8681653f4c7aa907cd70201ad8b3afd17b68b66a525058a7e66367abd1afcefb0493db89ad98e62b7ce4d2b0da075573a126aebf6dbb93e0f2005fa274b688496b849b2dc980e7796bfd9c4af672152b2c29d7241d8de1573d38296e47ab5b2966bd7e6abd892b39cfd4146444989f85fa11f749968342a51fd7d83b03a5d4506defce55a507018993f92f6e43e36b12d5c7b50178b76a5f4956bec36b5b4af397b4408433cd8b7831529c6ef75ad14ff7bba7409288bed3901c29225c05c7d9216fb5184be3828944531f9afd8f3d247f7689eb14b6f24104e49871ee598bc921cae637b2cf3439a05eb63fc0b9c10ac5352086a65fc3f6febb5c6eba6c67cf74411fd58f3460ee180d6c98f0159435fb7040f401b23b700a98c5168767217d67c6a1c7b3a30265b8ee35d40c16eb55e3ed1344aea8fa30eb8e0f01aef66bafc3dd16c246e94baab885a8a5dd7710fdbe13293c181af05b8c6aacbf39eb224174f9d22571e9dff3b4ae57e8c2d58faa8415507e4943fd3bb41ed5cfb85e1ae8995a467176160161225d26dafc1cc61331ec3e797a5a01bc11b221407d9d4761bf4ddda1f3e26d40b3bea26270a032ff4e2cd86e1ab359cf91781c8216a0064747be610ffb8946de621d153883017588d5cf88cf06bd49abdee3b262bb8684482f44ac00141d59c991bda3322fe0177b02369272cc60805c4c2f2a1671f4df7554e68f4ab64fe16bef057a731d56624a067522c81f81703dd76da00369c497be56cb26a80e843c2d3f0e58c0bd38c062769b73c01cb47917ce4a0f9a54fe2efa8b8756070ca6ac19c1dfc17bbef2af7ab9b2a788497f92915ac1ddcf4bbac0ec254d21ef9226f5e6028f0538af4ca3624afb158d562813302d6708e9e71ba5447df9b6ec15c05e7502edf45612ee0976078824e14c5de474a5253a9c37fd81c4e4e8a371a1d60b518509454fc0f73e61c507c2e295f1a10c2657fb15051e00bdd7c4a329b06a68fb3ddb099d34cb6bc6e91c46cbde47e5b838251e150fb58bbc0cfeb380580cb5b965f3ef416dc033dc7453482a1fd3fadf0de0197d24a30beac24c18555e53e1e8070d7b7f2c1d419dc0050e550195310d956dd1002067515893e5c1b458217b46f4ef9f86c8918e67883f67b2491e2a8990d6673c4aae3463daf355f524e3711b48a5066c9db49a9264115ecdbbb886a7d83bb3c48232d10c6a24be4d2913caaa198d1feca7a056e9aeb277d1b959b70664a492abe39cdc6f9881028ed85ccfd54194bc25384c9e86817a54d0a454f17b8d3f5437fae819be146b2b26be1098ccbe643f22315dbccac8ee7472251f9b2bf54830ea82502417d064ce662a1104531a9ff27e2237425822e0bcfe0e28ddb7e1f0f3f9ed1eca0649daa523253b75d3374a4e636f9f18e1c24fccf47d4e11b04dd90aa5b40ff5b8ca409c729df4ddda6838910656c122e8e365dbe9cc72fa6abd6499356a6db7f28e148f67b3bf49184f8dc96da21341cb5efe501dece976c932e5b27e11855ec829bb342b45a656fd66b3081aeaa1ac1fe988765bc8c67dbc92b010b73daeb135efdfd52db61576d74018f3e880832c6d9a3fab045479ba0b00d379a2a3db461d4e0150d4a88e16376480cf244d090996acdda822a1611234c54425b2428552670b001020f2809fc25fa927292f0bc95795fb86d5c8d05fc4f1bad2e725167eb84d38ab7c0088cf7ad8e5a72b531b26ee27a7655844eb50edddb7d0bc58ab7abd3a809b6be33826a13914c4947d0fe692a8c61be5d8a51b15144f0274246abddb2edd2ea14bbbe32ee71098ba040647612d9cef97659d0de9c359e8aa93fc232af5efc4d7270a797bdc4c560dfb19a77485f153a0d062df46db1a88292cd3424a4a0f95257ea2566917aaa4d3baae0b673ef4b90a078a6467a7035d113b4669745e2d92604fb11410853d937bd0d8dab1439baa9f4d44da0508a19d48ea8020366e8cd458f6670cb6733d2c5565acbd09414c3e0a37a658b6b24c483c8502f69fe077d26f1dc48b88ed5d5273e33f1c4e0cd6b0e1ac36a07f344f222c42cdbf9e3eae647b4b7e71bf727da7cd2504abd0c6c0202814e5665e501d407939a95a2f0435e721dd7432f50709a38674bb5e319624e61d2eada984c8376cca42baa99070182d5171053dfccc8033e305f59cc1052d827de34a48200d7d40c53e23407dce6595d845c3661fa76ded90ca4c4a1934cf3cecf8283539d2e524bfe1783379ad8766655658ee35275591066bc7647a31e725f59f10a77d09624958d059271940005c3c7572106fe92672ebe034fda7f7aa99885e9ddadb358f251d9ba9453d9fd0a4e4ab1490aa8709a639e8edb67583cc3e664abd1d12992f4b22099b843fd658df75ccc8180dc8d04c9adf297e881b49defecbb0fe70c2a2ff3eb1ebe6a8bef68740f06a01b96671a7b588a79a9d7ae753492d3298ddd43f079026e967591e188922550b9a2d4f0ec5bbed936591533c6b2351579ce778846b9e806dedcc8b083abbefdc89bb5969f557dac47848c16a2d58413b0609990cea2a03d2efc633fdb44508735518a2a740fdbec8469d3644fffe935ff221e62cdba5bf76ff40c591a78915ce9a542bd732ff537ce5e81cb7e1e4eaa66196027ca311e756ad179e00f6271c2facddad2ce9880d5d97b33d978539ef9597df84c90bacad7184b12a66f00125354da5f2dc32f701401d3a41ad261d1f2b54f781c14091c67032b4409c5a5e5601854c4c560dd5f474849ee48bb8af5704226a960d6210f42bb822da1aa50f4a0f719a42182621f2983ef4135339f1fe20f81d7b22240a7f26297e17cf7eaba8214fd2e73dc21819ed0be142693a4bd52fdc481e17041da1424dad90865b5aadcf4211f80278440b27fde00c55c56516109aba3f2841f52f8d26a904f5cd987540cd8afdad2644bb1bff0a63392311bbfbe6301089088e04ef1218f389721b4561399e9440f1f6127f89fb6c8517883933318bfc7576e8ae327d182268e3b14477a1e50ce6da322d0f348191ee9a9cafcbde08666fc41284adfb8e721b0094b24d55830515bd112e7b65f23a31de994f5b180da9597e93daf941cb7c232c0877bfb7a35680d10c2f3ffbb8e8c8cbeb8310d64fe09716011d3a070458d0a3741d34e38407512350c1a0a6d5bbcdefa465bf55d612f98deca5c38a736f575f68c9396ea143745bd0ecb1b9ffcd1472b38fe58b98c7df067e7f64d6429e950a898c6514abf0899ad1a798cdd069d0e67dfe68d5a732abf388cc590abd9360089aa8f6c9df9d64c935d3a25752a59b88b5c3386f1e9a625a3d822a78ed9f265e5c2bc74b188f0996f8d0eb340e3266901da533e1fd382c5b544f06156be724b9988b8c8e08ac316054d811e1233536416bea3662990eeb9186bad20b3d5636207956ada5ea560b12ed4a682475ed671eee73a35cbdfd1ad06babd82f343c39d505d804d9750714e57060b01e0ca5c931148b7d3ddef52cd0f63da9ed3ecf61ee88ea729cb7eb3c40178d41a0fa2967a78ee1cac608e01c1e8b8df242abb67e746288821197ad33455760011a69d58b7407bc04c5849d3a0d66aa47ea5080260ab7783841c187da37b94ae331d120aef2e52022ce7298ebcf6165903a2e680143f6f596aa363a826aba6ab62b1c4e6f508d1e8f9732297f98a996fcaf092ae64fa505e0b8ff00587c2a8da38a5d0303c047ce864e359818defcecaf5349cb5cc7d70c68a763e742821e60bfa05f79e9468b88e72dcfc6807e9561792768e5007da221b827a2b2a8695c7d02a9aa6225f0bedd93b944f7d21292184b87dfc9bde3d02a284bb28cd92cfab3c4beac6840e7666480d2e1c1ee71ecb76536ea5a402eae2588209642d71a63df8519d87f9b244607594ff7dcafc4208a5347e1f926d3b20e8e59d062b1d9a9b38afda3f8b0c5dbc3c460505f2ac2c3cb7e07245afef1a32255ea48e789b32a521f8ebdc26fda3cfdb63500ee2bca2504c75d3499a02259c43c85c1e05dc21a5c8d2347c64a4f7521d0bd6a035f08583d9e62e4a69ec8630ca56f7dc4f67652e92a43ddfc7220b4ec95b913acba001314f9091cec097a951322f2aac2ba9768a78675ae6bca039a20cdb908dc4654f7e5333d15d0d1f3eb9c507ca8c7b6272c2740ffe181afc3343a8bf8193a61477de50174285571aa5fa9bb3fe941d18f10e47a33ad3711bcf33ea98f03dfe5e0c74471517e682699ccf94441404a1c4d0411ebaee0cbc65a02d4448ced9c23093bfbe8cde50552b41723df72e11e52b85445a9b40dac61462c6b062e338a8437119f00d3152e82a08c0043475056897608eeabceb98c983f2bd6850ef381951f06d15b024202ffed639de06a4ed5e6248d2193b39610a6601455a2705a950017b184129a759a52a6380181b19b1351ab7b15d400392ea0c50fd12545c010138b91a90f7f81ce6d15a79181b859ea51562b31c9ce25a6148cb4c529e26bd847ac00bfc125e03648c114527193f27a1f56280d844863cc377d1e1c34581573783c909695bf3b35a8820f64e6d721b44d1c2054411a3d93c6b9f4e7608ed0157de20cba086c2d1a3cf615e18cb5036545016dd2304f6e7c26bb3833cb8083cc9e7643bcf0f155d16eae4d5157549c1eea296d900782850fbe710a958363ad209ac4eed59425c5605438871b546d349394dfe69e2cc759fb0470c50655fa6e00bc2f6b8900f09780a8a00b0bb9524c76a92519e6f5a22c2a294f82afcb19d72e3893c1a40fe63ebeb121bbf8b161f37b1bb976d61002a526e60c460b2416ad198641ebca090107a100db594095a25854e8e2b16773bb811c4f5ad074188802d679c13c897b317049cc54711d32190e04c3cc615f89c94ce07015e5a1a86b4f670275a662c26f3528907c69b6e9895932461db5060fedd3010ca2cea2caf8079c0543fc47c252d49d899c27469dac54156361a86342e1321aec856b743500de5fff0559c2108533cf325fccd6ec664d8a9bd83a38f3c7c466c6d6de8b1f9401a439980e022f3a0e794dbfdb05d2a2836f3909e898ca6ed39cd08ab0a49d9761868774a7356e02ab00594d14305b91bdf83f3d1a19226da1b46a4d9e428cd2cfe45f1033254f48aeba10d7ae3f4655d4c9ce970a51fa063156874e5e6d0213b4dfaeb0a2e704e99e8534b5f950f3b0143d7bad1523f2b258916b27d46e4a3288f4e7dc44e7caf3da7a91d48000c4d151c836943bf6fd93ce93b8b404b51085bdd2e1affdab559b3020c660f9244217659c4f1065d15442c60b079f6d3ce4687866ba94993351a1b1ba978ed2f800e114e6e63e2668b8793177b998218a830ead987ae63b4684fe1cdf7cf66d7e1b72a9d2546c80670a6d71920098149c9ae8a9d97082cb40250f4df591c0a84aaec5f55931c1a8ce4b87eadfc515e68edf5cd8609d7a59d1281777ef214ca9555a449d0db98b3b312759ddcabb1f205f5b1f98003892d3415bd486562b76277d24893dc8d802173fcec3fff7658a7e7977a1710629c34268ec17690236bbfc80dbbc83add9483a711ec02d3c14ac3446d42ea55ae244686815735751cb9a5c577ef5364c9949e097ac2d92fc86ac60b5ba37b1fb0fd79388013e0cfdcda6cdcf38f5effa85d37f667871d874e36728fe399851c52e22dd2d378649b1955f663d0da161ad5039ea5fd344bc5d7ac113574d660190efd44b3584e41c26f36bb4070db8fbeede596f996ba735866d06190b617530d5c79e0137400e4841d7293b9261563dedbe0720f36626352f9082542556f4423674a09d7281b0e6ccba1684e9d71567dc34353f34d5fe32fe71c0705fb95d9e2c73e79adbd508444aab7c91d386259b3329e388293f46e8dae9cd17c55a2c7ac47ccf6ac12ecdd6706b5513235cd005b69cf61a2983b3e3c3d39639f229068c1993f0baf3a1b59d1c79dee954532d43efcd08992333781ac4a6e5bc52ad2b078a2b1231df4a79c90803342d57d18e9bf314afb507abff5376842690edc4aed190684c1e0f31f7d084e681eada4044007b4ad7cf29bf247c9c04c1981d9ffcd38c4cb8125286ae448f4fdb6e1f041d0ef49c5ccf2d9c55868b8e93aff4209486832624a2772629c1c44fe3e43e87802589afc700198672c011c4927338ad23e2a071fa31e99a7c2c64fba18f61510a8592eba4e02554de3fc15a3f5fcba8413ebd33e2025ae82458aa3556c87be12b517210d504fe194cf1c4c2eebd02ed7d77c0dac09886d08e993470dfd8406e392e28584e80ff849c582204f1c7a3f6fcc3105f08693bb32fff78d454fc7f52589fabbf9473259e14785f8d1b4b25ffe8c7a6a9cfdcabc126c45b80e8c59b1665463db00280aa82c2f1988a76b187b70d15bea98e52990ab010eb62736a4820a6c48aa10176968202b800a2d88e0c072d2b05f53bde30212afe459202f1537c789e620b94f1429bfbe24f0b0b503017f7ad36969cc414f48c40b4b0c8867a2a9317eab2fefd085bd97682e02d4bd5c41fad825b011237037edd0b00b41594d49d00341b4119e7fb526eaa41652950e3fc62be0055408503370b426d67d84f19fc80a28a3eccfb59b018399946185bca420abb02be58bea607c2eba5f0d86a5d41769ed1387ae640259607ca560ef02536c37b13f8fed30425881bd76660ee798fa23d068c2e81fa291dc4e64a20604a77d2ec280100367b7847434f441d1f8ab6f38c82ecfde69c889780837ba4d38017588d3bf76f1cd8be40f97d73c7cbc6ee4f507a3cbfad359a0c06cfe052792ec1058c781298fece7c20fb47d56e9abb008ce9ab670ed06190b564706571aa47f004b7b66a3036dc50fa9388deabeee68bc125a436f410f71bfc762788abb1c0e3195c3f884bf4a10b5cdf53ec9e7365fb23a4a51ec55c8ab50657da42b8f52979bd32b82486b85eed5b6c1d2e59f3d0a90783cb0a13290089efbee6e18038e08e6851ec1250d7815ccaba2937db27ee2451941042a92d6414e5821219065352a5640bd1f1fbdfe65ad08ea81bbdd0fbfc2c203b078acea13a41347413671b23ec4c3850d402411bc141f0342378928ab5e941c4001390e6c26725b904dda62095d507ed890639ee4880129c4a97cb0125c76f8a02bce6ac9de7c9958cf93126d03c41610de46616930e4aed5cdd1316d9e758c3d5c9ba878894259c64a06057171ec44b60a14257470a8e7c5ec38b03e95a8d0974fac6862731f8266b5aa117a2a49104d176256c1c5645f586943735a9ab630170beff80449970162e6f941010ee410f896009071e7f65569a2ea4fbecd2ba0458d01fdfe8cec1b29789b0feda6c9a1bedb3c6378e46718b56afb424885791415ed2577085fec5a09fbb84c7f5fbb8bc1e82c179272e245cc7dd5615c70c5056e8a131e453de11d612d3b381b445c4e480363420f91c478853b48a4213c802c0b9da20b457c706e5f4442f014935248ce2fd054820cb89739c640985140e6364a666e57727407c261bf19eb6364391d4bd2f75643d747ffd8591e9726c4fbf9bb9185264851b62b50bac7ccfa72ffbf6c092e0df8b144ea164f4d8ea661434a8f06f120515187557c0119f4a79ab11d5567fb52e4e4f819d26fa3b84ec5890a4f36dd71888c15a1577a50c98580bbe891a0c61af6bb3ba2b01421b7443f5db0be300f13f876246b8e0200d9e2e956760e73e703712f0dff28ddd5c92175ec5a2ef48b990de6a385ed02ebaeab1d617b8883268e3706231f2e598d2d61bd9664e2797733d67e253a412515c102366e09d6c223a9331ba71068e3b1655d011edf3698fffe306179867e725b2fabe78263ec417521ea71ac4d8bc65a517c3a08305dbe1f1e4026a0d6f748a2d2240d8f12d9fe9673ac02440850aad4e490293eb84a94302815caf5cf4b8bb13ecca747a432c6369078e692dcecbf2416d107d134d6885fc0ebaf569ba3b2b5f80f25fe3dabc627aa8ef46394932142436bf5bcd480243d3d2cea49938710179b119dc00199e646f5165c42aa3666ded4bda742fb11583ccbf91e1130b74efd2319306a5f7c12d77ee05556402bc53d74840e0fc739a20f721303dd11a0606289b4592a14ca1bbfcdefd2b3b6821b39830d7b53c7cb0655ffa8e21104dd3d0e7983c1a3449a6402a79702c4c3c4d9bf3110e8b0026e22d6150152e12c6edc478a39ca4aa48c6895db542131f080322dfbf25e4e1c9d96121fd7ba23932c698df6363fbc4780f08ebb33fad1183bdbcf2a8b8406924ed8a14c143e642181c15a3251df4632b6982c77f4325f0456c2b3ef52e6af9346447dd1c2eca3111600465651cf4214bfe6c299f9a5a9ec232f3a9b28881d6f536264754b3eebe63be6b334ef08560079ed2be661eabef6f7cdad54a149693b38d9111a462ee99728fe566d934d7c48c336ad67f6a5ae249762a32882cc30709bbabd95fecd4f2d8eda08f12c172bda397ce1ceeaacde6221524dc957d74ffb11a77b5a5098c3bfeb0d00537e1ae807e3f65c2dbdf2669255362a497c933d24b2173c59e77575f5b08007500331caf099428f81d31293da052a16cdea7bae839a255973cac7858f670e961cd8395c7951e171e163c5b7ab8f46ce1c9cad345cf2b8f563cad3dc3c58396189800b97f95701117cb33aaa63c5da4b653f5471e34cc5e8483b144b011a23b7214704a3511f25e24a4fb95ae7d08fcc79190f48032bb71b8d248c50ad48fa8e01aad67e9495ce8e33d74c85c4812826ad8ebf021cbe297501404e3122c77fd366241cd1a9194ec627d101236de47b6bac0e2b0c1b6d5f9c605c96d358a4d6c979006a8d4f7f3082d17518172383d2b9365e5bf6b9510856dd440187d08d683430f13a04f2578f0fc932398c95bd76c969488de17206ee0915ca865cf4fbc53706c474199282d25070da852c1417a10f6b2d8e42f82deb0d180025c60ee58095db854f347a500607902ddde4ae13e8d3e8ffaa3ad9609aeb5a1e7b81a0da496cdcae384de07827d25c03fbd5d53f122de668e3cea2d69a60be6e870a0cc661c853f1e943ace7fd58c488abf549b6d6a0a2554d250debc1f88fd0589a89d3fe22ea87fc81243466c96d25107eb0a492c49d53cf846a563e11fede4d9c762a0a91da78ee584ba280a9397355f3f77a5c897ca8eaa1e10d5d120411527fb3766f74a48c18ce188cb0cfd8e1223d7b51b4b4e918e3d75c3b621f06c9bc26912c9925ce74acaed9d38f6c27c10a2081f80a5916037058542fdd60809c227bb3c0767a3eaf620e94029260af8994bc809a1db0dcd6bb73f2c6fdd9f9a958461e172bfdd6952fc0ff9674e8e95895188ec71f9627312fcd21a7402b030a94c3c607c3452c747807eaf5977991427f4d75eeefd6fe8d480685e0fd4bc9b6cb37d9c73d2ddd1a3530625488ac66759665b9dfa3b3392e3021b66a20336a93410237764eb881ca8a570adf16841d2152a4aa88c8e306ba6921de2af040afff8b22168b2083cab958189e20bb821d2bb23c74a810a8fbd52ea34962e99b717bf8c45229a0c0e65d26a1b6d681c5099bf79e76bf3a4dd159d2e9b84787845cae0059bdbcd90a5e2dcc2358af035d7a8baea1a4d1e8c526e44980b9d6ba421a3b796f283a5c8712f6b6ee72a42d7a88584c8e342884d5b2b51126808d573178244945432d7e943d69474e4aa208f1d82dda5090aa34a8a48dde9c6e1945722aba4e947a9e9cd77d658637e50a284b781e226a9508807ca74a022819485cbe66e7522a951a4116c1f1bf7618a535103d30d1e8df0feebde3602c461dc9b89e3006231b00b3749f42a3923371e1760a0cd024d39386cb3599685048f21250ae5f0cc428c65cc72a46d59dc488da6cc816260ddc322d3bbc8233b8b6c27c313f2214d8bbf16c13944a8f9f10dfd51f3653f9604cf024fcffebeccd167337011eb9858c0d21a4c569b870a90c042014492b29740a91e8afa3b01a76a76222aeb193413a569f79815818048a24699818cf3013f502085bb13352525e74f06443acafc42ca17f95f6e43cc32340017352092e0ac2044a1f3cd8d0d88543b8968569a4532cc471388247651308fa9cb329465655e8d379852dc2aaa34143584953267d6aea0d64f888a2c0a1d2a65b629ab2c77f8de6eafd212cab0f591a3d3d53bb1ccb90915988e68ecc68a7bbd991f6e8ba56f08ea08bfbeac4372cb5a56caa831c1750ac9696dde6ca445134a4c6bc8d895133d26501721d70552a00d509ff5867d91e53d547b0225677b4721315106cb058a8ca869a38d4da0cbf293869a4183b226108ae874876cc62e2878ce387661f312220a5e0f6a5cec489fba5781ef7002cdd61748b4c4db0e6d8c191c55413d51583dceaf4554c84c5b860224601f4ccd25dcacb03710dc48db93531f4438c10066e51afb90cc2ad456830d9d763f56b3d67feb6e3e15339a830e68e333e347f1d819c9dff720783db1cfc82d5771b47176dae254d8c59a69e136c7f05222839c8bf0e9bebdfda5fc0808d5b06501fcf1d79130d2b2fd4cf71beb601e3e4d70f2419f830b23d67a7d898156b1a073473033d7f7e1c046e1fc5c914405f6c1f0813ba45710e8c2483e39d7b5857b97d630669a8305db0ad0552828c398dd0b7e9944acb4bd21b8e4f32d8c59179430f3e00188f649136f07d2eac6ee911298cc6d8432311f837a61d2ab330963b6cb34b34d228acba683d0d25cfd60328f42209d2529bf7793a277aeda892bbd50fb2660cdd3d5344096d2094895455f211f73b023399e66920412d528337843357d9f9e00ecd5a678e573602c4b021f16144c9040e227a18a6594ec600b12173cc9d8d68b933de3513155506a2c6f7bc43bdf2c3c89d616ec6da0b7c1d4b3dcb070430fd998a6e13e423f196003cbd1313c6e9969d89a86d16e58dd2c741abbc3e272846afd78b9b506ba0ee6881a5a80d0a6e9825589a182df8f99d8cec7eabc943b1e3876a3bc768ff9f683a0bac17e396225477007c5f4d742b85dd56d172c2a9756cf01e181bb12eaf25e25ed5e9c006829975938626d1df80d4f7b2b904021868ab9218dcce22088198d46d030b312d0a323c8ed4b39ca2fd7bfd48628c2e299909a8f71163076650cc8d5ecee7e65d8ce0410406a48bddea4f6ae046845ddbe364c0d9de9864f954a93b0f5beb8b558a8c9eb75f5d48201db0c3c130469be0a3b4c312d6fbd62072456a4672212b5114ed758cd7f88dbab802b67a5ac6498336b47e021dab971b7f7f31da629462f5179fe5611f1f07e97df6abe3281206d66d1ff2adf961db1dda6b00d51c2758900ad2b4356abad1c492f0bfaef8455d4012cb97af93ab06b59cdb08243389d884d52ff9b12157160fac7bebc0f3b5148911da29b97601a0c44d0206aa95c91e1d9d63e437f6644a6a9d49e3ac38b6b9356e823c38e48f9be4028c5442460692eae35bd392c52fc50a3ead1611a2c20c2052a5654eb090f8e53de741d23a320c0f6d6e8a743add5583213d69c3e81bd138c5e10ce091e0ddb21360be0f6381ac6e1a88c25975dba2c9987aca3caa13e4c8c35f3b1bd1e60ddcff3fd2c4044a8aab070a97935726ad0931a376911db5e0573fa894711e6d87f6ba4898aebfb255bd7f777053a04351836e3dded174194c2037d43acd996074174bf1380c0a454bb1d8af2155456c58cea4283357b530a3c7de7817f6b6003593b819bf209d7e0dbea5d0551a0f89a808d3fd6f71304bf9fb0773cce52108f87fb036d3d9fc980843b5892937c2d8e1583efbe72f55530bd84af02a8efed061e7062ed5520aa2cfcf94326ef6d19499d06e4c3c90f3bd49cc3cb5a241c86de51023e2c42ce80573543bf304c125282d80af57fdbab2c77b9ee9a45f79060999362556a4a194dbf9036c6f9bf0eaee765ef05bab27a3e39f2af712d7da883649550cc47629c2684f015e0e7accc4548e182c2cc6fc1a7c3c14edd6012c6f70ff8c7386b00048c9eef376e544035277c8a946ed749db6d27c8571c8df192ca90accc417fb74128bffaf970011b2f782bc51786dc99e766c739afdb2a8d6c27356c26d3454cd534724d74adbf5dfa9785d7a85e93fc994d67295ff9761a91f3231debc8a52bc2c73d4d8ee43bba0ba07e574d373211b46b63e48c84f8f318245aae4e2920e38e5a1e50eda587cf6e4848a76b917b527bc2b0e690522b9c7e93844202b212fea6234466a5d65c8fa777b26c63d4d43442cb8c9b8c3e3800d77d8071a463ec400e39424e97201635d98d80a92d3fa976344a83ce530dcdaa233c5082fcc88650e0ef0d5cdb31bd308ee4c144d409f7cd2efc48e1d8cfc6f8f1af62d7ab3de8092c31cd0ba84ee7a87826a8fe23bc6067817728fe6132c027a74380280dc9c638ad7bb342da3f455608149e1681fd8818935d4814761162eeade419c7bf4e535472bcadac5b34248e39a783b93f1c889131915bed8dea997b9825c91f28a678eddc6131808eee37d13d25e5da8a9d026769267e0e8147076de0540cd218eaa3083a299ab18a3f15814a341b1943c841a69aa0431a25c8443b9bea529695ec89a8cd43b3e1a1cfdc2f2a2ea9f845cd2b35ce8f21b051fcee03604a9d33190542f5247438fa7273852c46246bccce090b3ed9d9123898d734c2542dc7a6fdf6ed635a7ece7b9a08901568af0eee93a667516f06d0e1fa80509794006629be9fd3441038d86676553731ac9b066b9c6de9ae591baf9ac1392216cdc55663b2b6ba493fe945f65978926a1cd791633c718fc3e8d17cd392ae8b7e3ec193fed8caec5760deff158023608142d570a392581b8c9ba1164e490c1680ab01f6494fc7ac579b5a82e393865d68f2c267637b2c6513c1432455180ea88aa552b9617d6f4569255a25d6c850fe3b843caea8aa7e11c689ab5a033478ea1235c3a5932b99a9b73980e0d637459c95c6ee970e564ce14c55f796a52aa0fe49c5169dc1e2100ea66746023ddf01b14d28e86ba7b68a35952c13b8a2f7f11abc2f5daa9635c2e02fd7ce37867c9026b50580682ba2e3094ea2c34243bf1f5321fbe5d69d406598a787030d5cdded9b92a0cb695b036b1f015b27859c15ebb92703388c8c969f40024aa11caf6c23389dce45e52b69b4bca4b877135b3e128251ea1e869f6f3631d12256297aefc96cd26a53c38a66da5c2c175c6f42572e64bc6d510d3157b155e183b48a3944a57b455fab07af064275c2b765a4666e817e98b4554e4e44f1a1ae769f0a5e5fb72fe0416bb98c086b9121be075106ae9d3bd490ff4f91cc91c4ac2d7a4119c2d2a2e0f4d243a72779e956660836c000cfa87f48d22dd3384047c7f6d62e6e179fa0cdbed2d75a317e466c8df100a3b314358c0d4521e81b6699e5021cc406ac5c659668df006c3f9923ac5953e52b1253023ef7b80b4b1c6f8860ae0d9630a62a2c02f80521fd9c9a2b7ee50a75dc9af0dabb6026e187aa328d8e5ab22525c9b026509204d937a9d680c45f1a3ce28ad18681708f01efe0116b4f6578db617886f5988efc7f0eadac51e019a20348f67160c7b9c83a2010485ea1de9d18d3ca2837c40ac6d9273171ccc5a81089c68526d9f90507b267b1dc5755d995e36590ad07b57f1d57b829fe0e6d4ebed22dfb177142d8b13084442a8950d3086256c3040d346c6acee5b6d46b35479c83432f19745df1475ffe2394d73193f28f2097fdea0902d56498220c164e8500d142a5d9c2ca075ceb6bc638a76d29d14bf2119c383fc187bc40ff89ffdedb9534c87351c01c2ea544dae76a85a270d89dddc0b80643d30a920d86347f1853a1a7583b8d90d147f36d325a4057c2b43c90969caa3b17e49b09be88af6f0214c91609392c74b4e8f5274fe8f1e3c816142f9d4621d69a9e00c55c0f05fc08f3c97eec4f92981c84afa7e29e00d898dc9f17e5783e0a0ce64d289e30101a9c441a36535ff84c69d78a955fa8556cb6a068e71134973c2aa952f9aa16e8f3a8565ac530695519c4108cd2eed574feeec6fd2e20f674520c61ff3b8dc2436d0fe2763c8621b8f2575f06ac51f4480c599d7b4ffa2fe7fbfc32b34147d4781c3c178465168e9606eaeb10ca0427b2c50791c07077403e1239a0f3b039746f9c77384a0cd802ed1324fb1b587f7bba8bec47fdc0db8b41b5c403c65fb56a91abf6926814b7e03309eb650af4ca8a25c03673716a42ca2a4d6c6080831c11d3c61109a014508ec6074043422cb69f41a6dbbd35bd30bee0072482487f8e47be5c59792576e2def6a475a8d7530ef4da8b4e89ba8a1eec340eb32f000195b00ae4c01e1eba8961e05fe17403bf1224c223fe8de23261462393f202b2e7ea189c308a4cdb7c6960f3fe72c8e3f0e93049057642336171e9ed9f815e891114981b4f22fcf54e31b746a968501088aeca261ec16ee5dea80cde0486cc5a27e39315b64de4b5b866bda216898ede07020566bb2b2827aa44645f6e4d5ab387c0c33a1c453c96142c59641985562d88e2d35e8add8944e77180806a9cafedb35671c700681ac90366c155fee9c7dacd984090b17696bf97076de23c032685125d05036ea002ba31ec0e5d2a002440843390004dcb2a8367632f7555055d2edc0dba24f3c814451c24cd550688f5d64b1927047a12182da4fb6f2e0e8a0b7e288655455c410a7c1576fc1270800068adf8e5c5a7c66381e66963c0e50e56fa01dc07bb18c62c286c4705b19454208edb0e4304f889c87f48c906a53b6555a8013542ef1f6d5a039013540810060dbc8b7e9d217fdfb73bcd3f296d3eba0c3408fc876caed952b5f25e7d599936c19875c104c845033b3dbd22f544c5d28eace860586008795d90a81035ed66c671dd4ce5113595d914b9196d6ae2a3299c16c429c1fb4de0f8d2d08a50917ed3b4922face40552627813c47d1b2daf0bae1c9ccf948ac1132ad3d2c95fcc2cdc3223cc8a0d0a03e4196e5cdaca3545d50321af1e0a84d256cd699f0104a136a0036eee3f58625fdc0cf8ce03c256a7a493eb017357985aa94f00f2d1530d07451e5d5d94deaf500cc803c9b96c9dc697d17d1429dc4645353dd68d33a103ad22098817f66e47616f9cfaec83f388f61a857789dd14ba26ecb39479007afa647dcfa5fd28cf9bf6127766186b528c2bec9b79c7312d086183e82b6f9220dc2a9ebbe092e80740b9056aa937618d28fdf3f04ec0139d0f463c6b8646d73c0a9f1ee1e3dc0041a165b05fc3c5449eb7694471efaa8249e6dc87e25417f4ca58dcdfdd1f095b8e545c297e923184869c26748ab53e4f68067237ca9e787a7c3252d1a15f9dc0c773c9d5e84472c0e10ccf947ae416403c7a9b11729d7a487ea7ddc208f25914405f40a0c473841f5fab99e0f3952849cf054be2d097a01fe661ae11e34befc5100ca5c0d5288687f241f89cbd031942706bf5d7c638f1203c52bbbb21eb979488e685b8e56b15b575376ae755202f6a215e0704640c38006369ee86f201f72016a67a05c4f470c4e27679993dc7c65988340b13605ba0164e44dfd7632c25588bfa9b5b5f5fefb3ec244c9fe77cf9d59883845f389f6647422a82a6c46b1427c1b9e356724c1133091d1209f7382050d22121e4cc009ae0b4604471c5807d81410a0399a48de7f02043ec2f12a78a752e98c40f60d9a3d757edf60f32538c0d61329150c7ad78ff4ce5e5844286f27f40498038845a2eb5691bdba66be9fad7d6388bf731ef6fbd5ff7bee65b19cbfbd6f4135efc8b42dca7b6d190a985cc9ff7f8ea2b96e43e94abb84213b8cd8c740ede041b1978ea0825f3dc802514d50a0ec780d7d924aa50ce93b2606ce937cc3a02aad5122029a5e390554bab2932a53eaef6e4d2bb29d446a73ae8e86fb2146b4f36ac16bd9bda8cda191eafb0f69fcdd7ca44d896f3d3b3da131a33c0f6a17a613f8652e2fbf49f040dd388708eb0df2fb0730e64600ad787b30b88f1e067cb8c412b8a69e3fe6d77761c9998d6f313aad94462ded0056bab7f27b0d91abc6c6fa7590a1918f004e89f9982c4f0201cdc4214863fa47335c02682885bd040bf5fd3cb33e6f31650aece983a8f133aa10218aebeb1da459fe0801f9f060e3e44b9649f48d45783014bd700446b7a845e4c52a0b99c7110223c53d203e1ffd2795e88085e6e410c187013ae5037501a5a4241c9b9702b887d9da298b0cd41607e3c41b553945cfea4f5726e9ed4c4a09435ca9ae762b7cda757d829448a3c88a52d449f0cc21d01886850b5d21e0346279afee6fafa6570bcbc82e63b6bfd87c695d8a9367605cbc59b79f1bb01b6b71df3525b003c9861144773246c47c206de23f4e29e9e7032ace13dee71ca0b17d8ca1ba8505b004aaef36079dc9ac94db01c29be4813106d47a466b6fb98fda13020b09e831216d73716d3e42397f49be71e3484cbb9fe2f405915acff400980abdc84d07035ba04d0236d8ee290bfb4db65e60db2de85489d15ad4ba76e20469172b51dbd456367c32c382c7eaf3c0acdd5e0fb809d314b47a807b252d1dc5d82878526c5f9b6ffe441f9948944e4778274483ae54965dde15142af7cd7ef008095f2a10dab12033991b7a0b86b2783fae0e82a9a24f827c866e96a3f79bcc5fd752b2ff43004ac78630f1e44dd105657b891b0328b0dd6869dac7897e409d64cf115c5f4141f3dc3acdeee88a71a515ce9c637cee13af46d47ee1591530ebd244a15afb0866eaee5c59486f9611b88f8602ffc11e5e6246433caaa95c0ffacdeeb52ddefccdbcf77eb6a54c843a566ba1ca34ec4a0526b4365ff7cc06c12875a01b172ac470c110eacf199c6e5845ce566422937682e8b3a0c9c4b4062ccabb4808d7201a05a2e1acc8fb3523c3125cd5e8e85b32a065d39179f8b097a595c34dab3c1822baddc5b83c474efe0065e8893fbd34e95c868065b7fcb3e5b41f53cdc9655d511190ed720525a286345f09becab3d7894095b4252214cce11be29a7f7e17a18810275a752bb4a8dd5d883f4902a7d5fc9147d448c292c5ee0f0f318f78a3fdcf7ac099037ff8a0277a08ae078c4a7b286c04ba1fc47ca5d1750e58312c226d5cdaea2dbb69c2e357f279b73711d843c4da2d1a934f9580cdc15b6b39f6080ed4038aa135687e67513560a33327ea58594b4a4348bae73a460311f15cece232f7515624107171429aa529cca99ac9958bfec5604ed3cf0a603ae07a6e7c2482d63e77b42a5a5816ccd6ca53b50ad26a4f17a7ebf058a4ab066581b10e5118e244194619729570fb5e7cb585eac51a168ba978e86d827ddb89845da8e9b83590ebd0b0eeb9119e898e0a1272a0d84d4ea515cc6ece97905be6a99d226e6197203ac2d915c6865eef54fe9608d5e6b67aa7a561733291d0a0f84993c329a98ff5848ac583b03852aa3f71565be1cda33403c9c1011a2d76ebea21eb4be711f5e82dae1f703aafc334dc18942f3a99de833b22b2b34c389628c8adf6590ff3052a2c378aba895f50021544f9360c9640b8042348d615d4664705e7f00e29827f648e697ebba1c15011053be5f4ee352346cc5fcf5db80849914e4258e621e4241fe205d282a0302518e46d440176128f6c39104a7f4691cb98828e6eefacc704356056990118a64a87342c4b4fa71746c685d6700da712a5f34a87b7fccb97dafcc7b2d3e083064ba1a6884dfac20d7eda28508c89f199ab6288540f001f284f2940b3aa29a1fae9b91608dd566439a8b4bfd4098a2065d1bd6e3464af1a40f0c3679634823fc921491f7308877d9784818b603e188ec3a3010b20489ba4fb7576d7335625b822de4ced7933614565ea9e0ea4b4738b21b624a30e4c661aa3bf9f915a3c8b9c1c0811c2b451593880b43eb352818b250eb4ddc3a27325e5e7862ac49b3b6bc97cd9af209a12bde26f218895f4cd82d76c15517fa39b066050fd84b54b8c04a9e2c523c6624a2a35b1e1aa30961cd883fe5ff60c979c44237282e6083c4ef77e9d503846c015a07fd355c708aa4a374eb06c858190d9c5b0c9402757b7b85befca1ddd200ea9458a4e13b81134ca0617a80712ed4adb95bcaf7488ea80906cb7224476a749adcc8fe690a107431ba3a292bfee51bd52cd9f4ece27f5fff97fcbd899469935812630349033d0333489ca6850cddcd56146e415aa87e484581b5d49435cadc212f689b4021406e569f25de6777777777f7f1e8c874f4718c3a561dafcc59d350eda865958e1ac040502e608708ce122217234158ef656c23196c809e16970e26285c8daae4f314167477777777d726dddddd583aa68d3c773ed0ee19d6962cc6a46a5509b7aa4c3283991e9fae1e17477235894b4404ae9e0d1e7362fd52f2d025aa7fbeff8fd51521e2ffffffd1fa5305079110c0c05af27302ab229918224300b0b2d8e06c7276027bcbb6adb56dadb53378a58dd4f1fd8041d63083c8cccc5c0322350c893fbbbb2b209dc21c0f224811694184b303aaab1e09af644c342e747747c97a19ab0364e90628986410b70b4ab8b14b2201522b8b99192583f6ff1f4c49a49a5ffa07fff7ff1855ad2cbd5fad1f285fafa2df20b50f98924202535258c447da2a2405e5791c1676ef112ea3a20b1a4a645654a3e09190646a42ae1f587785f28ab1558b5eec202439e99abde071d0ff7fe3e1fde891fe475941e9ff3bf992d0962e68e9ee06d2f9ef0c11261abb972844cba0fc3f3168dac8f3ffffa0ce944cc9859f9999cb476923a3aaaeec8d4beb82a93bf92f17df6f2ebf9f562b2f044bd8bd0d4cbb85417a6977f7bdbde46254efedeeee6eb4752a3fb976777788a8d246565dad84d0625beab8e14abbccd0dd374c0d6354812326929680266c744a284316321ae082cfccccccccbc52e4c748465a080b8e4e06958357a33ce59bbbbbdb360b226b67a4dd4dd26a05c1b066884dd2649a1a96b64a2193ce9942d4537432410a9bfdecb6cdcc2cb455dac8165c4ec76b161b3a18520a4a1be3362764e7799ea7f5299263e76148949f0accf15d1c6a24c7a8c849ed3b68659962c4b337231dda928e9613495dc920440b8c2da2d550c5a1a2ea2c6f38b68ab44cd4263181527be944886092411c343aefa49c145605f0bcf7c91763547292f9e9a9378e68d8e0d10d09f79a3282465a0e92708874df4b76198976f8947677f30693058d83f216256bd833a6c0071c0670299e642e18ea21ac444b564ed025231c66310d4944a61a411cf6fcffffffaf848007062f48104b87476b486b2bc6a403cacccc2f2c263a19131719bc26e0536c54011c3b9bffff312fa58d9c396968bb945e69e3989dd246a61bb89032dbb3da90a94846222a7477dfb6512323ca0b144c564e1542614c03bc1e4d846496546cf1928aaeeeae65c9642933b355da2a7260c491c4d5d0081404e426a6de84904045728b63cd77fecbff10d2ffff0f676fbf1ccd76c1ceff7fd9fb1f82c8a57fa7982633f34b94d246bedc3accd894da93ff3fcb8a166bffffff519356abc5d25adbdddd6d518e62666666d304e299c2242c5256a9f5713e12740fbaefedeebe4e4c8aa45c236965d9dddd35d345438375ac5199b229aafc35f0c286881f40386ce839f140711ef168e1e0d57370fe655fdddd44a069234f2df47cbd1eec68316de4096566939a344003500a0d51644900077681053024a029014d5a6d86967210425ef07741bc6829c69f45a4f498a8890561ffffffbfe57ff6e6eeeeeeeefe80e26929a783898dcd8f4a04c1da1900606b1720e4e2088383c106403e86ba13301332772909e85861070c27a8e1111715207a0c57d07a6a39b59299997d67bf6d9724aa4a1b79a5750d24a2bccc5c43099aff3c9e90d1034734e2e7a1f210aa5012babbfb393bab430818b9a1953513c2fc0ea435bbf7af455034832821c4d18c135431844b7ab569427974c334e1b4d65a6b81f84a1bf90306894523b300393233194d25592e76feffff41faffb7c04dd1a846854f8e0c329c98feff7f6b65847090629ab5a1a8d24656fd36749562ded01811a36946efed464f73f2bbbbbb59466cfd43de9db27b7347efed69eb002aa2ab1cdd9b1b4277b72e25917a7669b72e55726bb5a042abd957111e356630071198b53a52dcfc000a259a27333b0d98ea0e240c3aa8c3854279fb213564419b0afa656666c66d9536b20597d3590d1f78d6c63081b5c18d0a164a3ab2549c40435a0e8cc92a44a451e4d904e1a83978e60d89c50ba3221a0b943095d8a0c6dd00a9211fd659a3c866cce4a118989999a97aa58ddcf9193ab2155031591bd3395f2ff367dac893678247a29a280324f0104c6265638510a33a1ce26045d0cccc1c9cda4a998f1c3cee6243b666ff7f32d8b593fe5be96b9cd0491f800cffffcae4ff95621ca5bf950924fd2cbda945005bf23089483104d7b352225af275f5526ea6ffdff2ffffb8e2ff4f2bbbbb3b08f7a01cad435117d4cd930e1d4455d8631831be33255aa3a2ddddb1b6f97f962b9fb546df6f4601cf9fcccc1c6357dac8dececfd011d34a0c1f13a4dd40aefbff0fcc0c7b40d58d0f397c66666666e60a82f85a88d2909023119725304122c47b8f449523127957a6c4848648ba38360ad4dca2596bc684624cbabb293063fcedd9c559df8b41edbeb7bbbbfb1a903a1743242f4beafcff65af9c9c6923cf9247fdad6518c9d2a46359d48c49ff3ea94aebee4e5a02a6ddcccc5c7b72bebb3b87f89f5d4b61d6e1d51d8e142d2b70816bf3558728597a10c1ed465690ffe8859ffc7777d3f84a1bf90306998586c86cd1b2bbbbbbfb3de7049a72ff7f8ffebf8622a3b567676068171484a2ca048c0caf54e96d56d13febee9e8012e2dfe8bd38eebeb7fbb004944d29962e5499314221943fd13ced9e7a4f3b4f3f484d55b5999999744c3cd38c40c33171cc4ddae88c14fe1fc9e927fd5fbb979b99d9c93a5927eb649d2eec8c297a0014e7eb12ef43edff7ff3e8559c3021656666268539c0e28caa9b605d6b9dc8b86daa7841828292910a4614258d29a964c4291dc1c1fe7b9586b9cf6aad4636835e4a1b3943b3eb5596a09d5acb102e66f040f239f66670688044d0a68996720a38159c224e15a78c53c7a9aca9adcbb551983d6d3700285071b5bc4061b5e3a91a6b70349cf2778003bcca5f4a06857a2905e599c5c3ddf73ea025466ec6d08c1ea1a90e339a019820c816dc42076566ce9918b29aea4005e545955a013bffffff7f0ba88913a7ac1f256084fcd65a2dde0fd1d41007981a5513a55e52028e50ae1c19f9e8288b7ad690a10c06ffbd4c925c4f1af0b078572d764005955c61800024374bdba22f2edad8ffff3fadbebcbcccbcd0bcec5e7a35074cd676acbb9b2641dadddd37b8d246e6cedb8d8e9682567b6fadf7d67b2d3333372ae375c1ebc662ebf684d7e793cf5823bb371edc09badbbcf1951b2b38781826f8ff0adddd6dfec2e633333333330b69145366e6aa522b33339260692389457b4362b496d98c290b004522490d8b1e5d2df5a9433b99e169a1d1ca96943c3a1a2928cf1bc9ac629eec8810eaa9da90a183a4cd4a6a24dc3be10238554b1d7a41b476d462d5995084c1b89892419897f42ffe26ca0fee86323e379fcccccccc42b25ac8564bb5b414aea5722da56b295e4be974dba82d14feff4cb5e83233b38e2123398c9caf17a9a3336de4b943a32a33f32c56394459d214a883f32ffbeaeecef1336de4994378369de8bd12eecd110425408b99696d089869e3d3d6b6b5a610955a3d449536b2eaeae6414b652986d70e3e948c565ed0be1a357370339949c260c10d43bfde15591906e6d70f5d6923793a3f7c293f7e2a3f801730f42b0238a0a04e0b51cfc7fce1a18bdddaee6e18066666e69e636923b3ac869ead169cd5e093b3f6b4ddddc223c6b49b56bba0eb63f776570df8f5e31f358b3395cacccc3d684a1bb9ebd90a7aeca8d4fe7f2517a6f4f7006c732a094f55f280d4efee6e24163ed2ee77dfd8bd95c982056ca1e5ffff1f66edacbc4f1f640e7cba7db4277383120724e7ffff3f21fdffdf8e8cfffffff7fe68041a3cb82439e65f1aa1d59ba6601536314db8a810171fba7ab462e46a6010b4600e394b00cfee6ed408446142992a23333305704426995fca32392298b62720fd101d39cc0ecc0fcc90c94202c8cc6ca34c9373ae70dee7ffffffff49eb29cacb0c0b14cdd9ddddff4e2d570c4ff45478f98c9036a43592511e18ac6bffff1e59ffffffff6f9637ab1c33f34cd6dd4da45d960db2fade5a8d4db7099425872e346492ae1f56504c414e43ea919d5a2442d923d2ee59eca54446ca2e4f623ce7abb5166d6205feffffffb31b126e5041b4c9185c4c2e46777677c3acedee9e41a8017461c0ee6e09ddb3e361594d48c47c0881871bdcf145d3469ebd996c879ebd1c4a5329ff7f3853dac821cd496ba295e56b57f6684dc69a69ce5434e6ff4b046d09a22d51b45600162ce16155d4940286111b9a7397febf07a0701d1d77258466ac967431966c804000c164ad317b8f1401f0e36972f5feffffff6d82a58d24168d2b3647bef0ffefeeeeffbfee81ee678a07494757070e4050b50810efc2f8ffbfc9718ae7bbbb5d8e4a1bc9e4236ac5a5ea6c2011fefff7de1b1b01eb000c902769c3918e4f9530828cac3faed6326a4b242daf62b5f0336de4f9b6166d618aa8cb6a7088c9ee8d55d37766e699cf3c9eff9fb4734c7fa21dd2fcff5f436935b3870f74104c5a2c46f350b9f9004e79a81180316842779b26cebc974a46752376ed0e4b6b052bcacb4a9299fe7002435044c3890ef1e5d5b1b49159f576b5d5526f58b80b1b04c0866f4893666666a5aa999403928aa8344cf807c70014231a58040004611c8af34066600314000815daacdcac401a8bc3615130180e030381501810008081c12000c3500c80d1204836491f709e65abdfdc288489dcbe7d003a896a4b760d92cd0b885298e5954d7083f682485a120483783d47c82044f95e0e11f6ab9517db7a5d786f22911a289c89044067cdce2d4334db0ffe6f5d06d177548eda6f918c6a046f2f921bc2a2543d42cb55b6c421ec3eb877b74513fc91d94724d4fe23b2bc02df7c67fec3ce250dc854646c6f589b06013b26820d727a13de591292ceb597ae8c42a92b735bcb6ed239a37c125d5e8b7c7f64750113b49399de5440f4ffd480ce20603e4e29712bc80f36702fcb965a5b30eb61225f1f3bed86740478ef85a4775b7581016eb308602e20c320219bae1edc5ff1eaa35c5b80438163bf0f6bb2430e2d554318103714bef6b3dce2298587bc9aecf109624ed4f0677db751935222498044b406d1583ad4c0b0b3ab45279d928375065229e1b88c6e8d3b4cd0d8be975792d25b10bb6fd75316a06b9805c8cec6f9b4d11d800da40614ff1e3012df4a4e81928ccb9371002cd14411c83d17e9d1b60697423608a9c3aa92ff0e76303f3aa411ee346b8d5533887a000b0389b76f428d292f7fab5337e657ac21a084f8654c4666eb9e4a6f895b50363a7fd7cf9608ecb81dfcdff5cb41a36ee136e4c43b482b208fc52507b1b8d556d5080ad0001ec3431923dc1d1e605eeb1db7cf66637d618d9660f8789e027738c414f64f6d233c572844a1ec0c5b010cea0771e40f44ae2312afc9ec028affda73c01f270fc84399ef8d9a0eebfb43ab1872c0dbb6ca0057390390f35bdcc0100c5c04eafc51b649030d539b1e31424b3361e6c08accd32804adbb60dc769f38f054efeba04fdfaa12cb8fa026d63a0c960ebe89878625de9906f4d4a4a71419aab0262f8ae92b70b01ddc62a1d25cc5f8399ace806d887c02c4469a8796ac278ae9456f8860056158224fd216866d0f2c048a6869f580a003b43c75b09c48fd73fccd0e21849b6509d09165073996efe2f6890a1d797358c7549d228eeaa04911eb5c1b20f74e7c6216ced37f8ecf2813af0069abd2411fe4c5978d1cadf695c2d39197d5279ed16616ac0096ec9f87b0160b606b004df429b4dd62d4b8dca7264cdb11ab391f026ec206e247b991114e852413a8c54827a956fb053a23b7c3c1b127527b09cdd42e02a1ee81a681ac3c9955fbc506af6f5fcc34ba2cd9f90c426e86550bad6fd2c66700c73f99166e66166fa22f3515dbea11ae62ed3d1cb601e8bb36ae4b0fd5087d3d32d8e83a9f98c42a922d5306c651a822733d86974a00fc1aa408f8dbf437f28058aaca73380bd039b80519afacb27b0dee9988ba873f298da7254da6687269c51a95ab6b43e35b1af6404a46fcb72acd758ed7d8cd93e6325d8bdb42aec9e281ee22de93eb5896a6772c0d7d11ff60c0ed68480f740a7ab2f345f2c13e95b414d703d98f647d10f4d35df96ac5ff74652dcc30715ee9ec747190ed0f5ec9941658c5d95179afb324ae86c4003813ba7c6d0230f6436640f1349d8ee0e435b51ff8d078a620296a6f9effbb85c19008ac96d93168a0ff395bb0d1df3a939921f237824a8583301c00cfaa018f7d5e1c284c1eb17cb9212b40c6c24acd8ec5746e0632198a9e71254132dc27f6858388f76c4c8603c0975c7b8e3cf0fa95131fe8ac1575b204d0c98084cfc986da314acc7f8f0f25d111fd9f782d783d0ae022a34e5ca058d29a834b8956c157ebf4c4d86a1ec4c02515807161b2f578ca6068542051fe8a1596188d3328c62557ef78e6ddc77cda202fd7b56aae17793db2d7415063a98184fd7299fd1e42d4421c6af4753e3b60c874d2cb503234228cd34db9e5c009c00ae49a2e358611082e7301b9df7feecf12f7daec17f2302eafb2958f08c30d084c09818142a948326751042ccd210e5dcfb5c595b1527e13939f2104fc2482a49f22bc629ca501d52c67ba7162a08503433ec603a032bbbbcd8ca9111bcb4d2de6d48c1c67143036fdb31d0d84fa5fcca8e82c08595d094a2acb16bbbda46c2689028dec572ac34c9f60070fc75b3e691a07c01dff2cac4499674c5b07f4c161651b4c3557d8c6c2723cf88f49b449586ec012d1224f346cfe82eda26ba6c20bc0920adede6006d020048d24c316ebc76395f7a1473e904f3936b5899ed735c01121c0b701dc4e769ad6c3f5051e0504fdf11f2b82c4812c3aa5618c0757fb2567516e38cf363bf658a6881d75df546e0e17d2be1c3d74cf6a0b9d7d68435be7665f6e4ade12e4541c32c24c8994543c00b2528f0da325dd7ae304497cf44d0b0065e625900ff39fd277334749fbbcf14359ead9bd4a2ad64824f1b7a3e93206003d22f233f0f68c395c454d380091515e3e219c2eae660de2254c7cfc3cdc68690d325889064f3d6086df728c889cf452adf2622a43847cbe169b7d4f11c50ae1b6dde0c6646a317f28dbf56602984dee72660aea99049cac28f2f061f67295624cef91ee65a244570ebf319a7ad7d26cfe9b9a410552ef552027dde3df731caa9cd7341f0d9e28b3778be05989fcacab2b3ffcb5962337d5d7d8336935965ae78db9af0d626cdf1b895c230013efa510653240e6d21b430e6618025b1ef0d0b09e8fe4ab91a979a357b29950b1ff84941d7b2190dfb88c3c6cf8a2f6fb823b05880494a9886df38c64c58135d822f3db06050998866ced55a527bada21e2ad2d088a0bc412a105b89c8d9368f3c78e3154c436e407710a70f7c2328a050ad06518be20c86d21a230ba5d5080a68360fa9b594c756814c4519b2de7aa42f79382e6e5000deccb6669cf1e4e1bf865e2dd1a181b238eabd55342ed02e2535c827d8746b0d7f19643731650008387b9b04d6a54296d13ac79459752d34e5a54c656b49ccb539bc22a0cf8055fc3de246f9fb3d1a65f6a03647237851f9cf357893fd450d1880b9c643d42a7b82b8d370a01922bc643e35e588110ba7fba29935c77ce3750f01c13c1b5c4e42885a5916f58e235d430254ad9d48df02aa214daa8dade837cba0853b826fa99934fa9791be222bb30aa71c8b5d78be5211fb085ccb3cb874778eb6a00fe60835b45fd6f12a60b3e1ab25d42419cc2c41eb15e31054dfd523547117aefc302e3fe41862ef70822f5d557207f068d643b22e9eaedb571ed58fd46cdd16cbd7693cd57da719c9104e8bdb301035e50745662eb3fb9f3bc5c334502abd12656f5f3bed4b51019718636f1d6c06a713251cae4e80da211902542cb546370b2038bf816fd2882f835ac2ae9bd205752905355be88e180bc49a9dd20f5e9e83a8147d5bec1930361bbeed50f0cd25b3981fb4e3ada36e3a4377cdc93004dc18ba2d621389a4476a956fd44508ce60c6d4aa97ca229055338e1db5ddbff6d2ed02fbb8e44f1c03e679908d78891fb22c96cecb1c3828ca43e053a934737145c2b758077043cf4c1859f8521edaafb847ee37de103f8740633a68f48c503aea1b2bc0fc24f2aa4ba1e91361bb33261ee98a4b03142ae9f6b1235e88e372c043b0a4e57e0caf3d87180ad4eb7947e0d20fa6a01a69b6b5cfea8e29f56481990dafe1b56dc4a4228ec314e54f30a6302ce15a186d1fc26abbc9ace436b5ccef42edde5fe5999a054eadaa1cb586ceb2e0168b46582228b85b0086662b461ae25bdce8f44161669f99038d924cd2cbed00889635e5c02fc18a8674409e9ad419539616bc9b411b92ebde20e22dde3a7312efc79428a00586ef0462f635a78b1beb7966479a0c6ca9b7cf8b79d3c1454e51730291aca67a9cd9357cb29c905f0e45afdf436ab7c6f11822f527a5fb7679c939a37a2155d45f30bdd5a3f563d6d06bb96693dc14f8e8930e6f680d28fc1e8ecbeed56bb1e42c9dd67c09db842b78c93cd148ec9265bd9421a2323804b42e8e3b817f4d0137e11f54a998ed9e3a4e2eaf5f0585ecebab5100a71c2489d819ff61b5a813fb03312f354435dd764f81fd055df049ed366733299010817241c37d20223723d2636d6ef86dd65699500fcd00714a7402051d353b938c462e89369e75ff714b1de606a074a06894e3e4bb141f876701a7c805d8c974294cb8450b08883d9046f5f655d57e95174713f6a20c0c9445c9b54f6b2cb72adb335b5472f72845c78bf87e73f4a3c23092856b5c8417a87549aeb794b6f85594504467f3d84fefe4eedaa0df199fad87aab0604fb2ea147a444edbf7a60070a9f2d07e0db0a129e9ecb96c287d38640dc7f32dd6afe1f8783f30636fa47792b3f32b9250aa4ce1c04c084c286498515b7e0f721a118f68ee8e0822fe58365d5ae0daa648631c5e0de0b60c55e053a0d480d4a690c2cf54aa1bede6bc97c9dd8ec40998a03ee8fd548d8cbfc5ab4c34903d29a7d9d654ca29b619290b5e4fe23f3db805341eaa4b5fdb8d39a8dcaf91248fdf7296e36ef45204742f52bd337bc107f24bacbcf8e1fa7d6055191c138fd32ac2540e9ee4f7567021e7b77cf43aa7980f9a01bf9a0ef44380b41899fb25f7b39d2ee3d4bb6fbf72028dc727a2386c38eb767a19b679de8f7e6d7b741e0bf27d4b4c692a11d28349ff0760e3ff5aca5bd335f210565a13d95d63d3ff9c7cf5032be24d83185a69030cf215e70cb700913fb01695f9e448f67b9e36a5545d760576686219be6a79467be9c70019127f842a9086b3c376ddac1c1ae720f4626a21c8faa937bb40753bc8154e406dcdb6ef58aa4b2ac975cf4644f9058853455e2103341fded1ca889da8c6d889656a1c44ca5c04ec99b2f343cd2947a178ba8a8cd736725a06fcbdf07e8d6bab1390a781390dfdf9a7fcd58997f92811ab1af2b50389f3a9fdd9b6898a5c58d3e7b59dded3ce2a6ed26736a2687feeac17b07b5bd8333c04adfb4b38fa9199658c4bd274291a4892210782cd37338099e3d15b4cff4eace78980582134b84ba4a054f59ad0cb617a0edaceb9ae90962ec403eb1cc50c5e5ff0d4a9b0f5b868a71d329e04b9da9e222c95875a28223f8efb33d708c07064de80cbe24a40f3beb2499955b6acea68f174d2ed067892f60ed132402389ddb5ea0a35683e2b9f7293a38190a65ce371d6ab8d69e9ed1fa8a0286058b1c9caaf9975b51255d85119b8e4e91d384f769a393a587a56f6868f0fe802c216c6536591544a22a232c5066cfc6854d6404a09f6a988def85dca18ec6516f490053c1c81e6ee925745f666cab0b11b3d6203e9aec699b0008498709132181e99dd8f4a85faf20503caf20823108eacd55e251d292e54d4a970d49e5fa455168eb9be00a00ed774594930dff33021c80e3b41ec0d9652d101a40d74fbf52f8ab226ed52c4f6a43245f15e96a09503afebfe293588cdadfec432892eaab7934a246d40aa3c0a095d1d387ae4dce567686364cd6cd06fd27537064f273fe6200a000a03364b240be04b2ce07d5f71a15197c1625b5615cd5ced3f4f21aada64d03a09ebec97f94771c950192d3c4906c04692454465da07528019eeade26bd689a1a7d3937b58c03c06135ccc9b883f7c08ddf8c68470a610b3a62a499eaad6067868e95433c08c3c2eb1123697f7b6a63d5a23cd8640bc3c1d82242fa81563a0d6fb8d489f0d8eef403fbe09995b782a224f853e131eb1a498184aca46e417aeb0832ee84d6e90d0885cf44f30661abb9026d14f95c933b334e0780e0d3f407463fdacc92529461a782af5cdf2cae11b05425eb9f283eed8dc2ced0991e40b196ced92885ee643305dcd3495d772a6640169323aff4dd1f4297ec2d97fc1f07d528539332886c5b0f5041b3f837c772debd0ea53fd8bf2b14166abb63f0b219372ad59cc54dfd921b29bd702c6ce50b316c362a2690427267428e988a5fbdfe4d318fcef35d90c44799c621b39dfcf888d2aec3e49a42c203ca4d831a478cf4e9782e4da5607da92c5c2901097a4c80145573180ee7f849082a2c8c3eaafc6583f50a0da3193f21e5f675fd70624d8d1ee415919ed84bc36b1e024baf800c9a94782c08d4eb3285415b2cc800513434162d8db6c7549cbbc8121dbe2e888f50d9896a471333df4c981c849b12c209784f5e078d795f8aace9e2ded0846bb5b3170c608d01d0650077134252a303b050a52c4a0d1a462682431eaef5594ad0f6a7e8f37d7efb8154cb24740b7e280487d0b7302e097b69f403b4fd17cb4371c2a404c688bda509476d8c1d12a0520b8d01e19209c6cc1caf08d258a9b97bcc4fec39c2622f7e03a34f3f457729dfd95bd6dee0c39efb99ce2c6b4ba7a0d4f30ef5756447db9d6efb5f8d8aac8ab2530de595946fba57f7202bb8352ba795ec260bc619714a53e9bfa0c7010643be03c5f5d7a569183522ca52ca1e323fb49f9ea7c8fffd286077a47424462382c422290bd7d27ce03cc389d527bc86aeb670af3a63c958225eb1165525f5465ee00d4c8ca68b747309b4ea0b977a29aa216a52287b917440163e7dbcb6c71073f11c53d90f30a00c83a64c5cb7c2454d283b9848823afd8b7aa8c35db1c871771d4c3cb7257f91a93798a494dd80ea333f676a5be1f44e38ba7c8a59fdc4eb99ac8c0e5c5ba1a8ae3c76edc640abea8928d7023788aa3a28db7f592cc4490a006c273347fe13ceea6faf10120c89825be7e23600f078d87760b4c051625324824954003413290283fc1c10f8505562db0d3adebf575c3ad8fbb8a15e2e614cdcab5de03462c3b77ef8299cc9d7f018b281b6b01d979a342326893045596b4253c70e7026e9cd8a0d0c6c354cc0903f2ee7ce0c653a38fcd9536752e32778618494ff9a89e5eba7cf1606fa68ac6d00bd691bbf5cd1c7fe9d63d326f1eaa0c5aef60ba51b0a1ea11627d46fbaf67c2f536e9f2a95aa90616e6ea453f704bd3350cb58baedc4e8524df28c24484af65a8525f753e3d268fefbf0dc30c0a989babfd346345e2360d5a40e1ec4063d6fe59459c48cf5f693ca0a219881e6cf9d82220619a2a70565ba0bdbcf36784bf3007eaedcca8004c1426501f332f8e500b8498d1829d28b31fbb500e3298149ba7d189129943399253d4d73c16989f97be05d3954789bd824b3095932d0d372d13b80c7bae077963ae3f410f04a26c200c7c2828b53d3b949e8e012b1f5c02f062aca83c02f39054005cf419901fca667a738320d8de5b40df0b256a94cafa4c8e70742c7691897403e8ee14ebeaf0d6e8ae3d85227c5f83bfeccf3bda76a08462351c9387acb726d352808ac65b13d8adc8ca2c8e122d20a893227e30570911a98c4e454fe6133faa5c2c631ce7e4bc64177b99721c445f972eaaf31a6594b1c22474cd8a5da649604b149352961396106f9c5ccc5a7d48c54698171030726909af58eb2078f42713143e3e212e80132e95810f54cb82f458ce61ef09fd7b2130a8978dac6ad62ca021d6723799d4154aab7105c3fbcc6977ab3d60e11910285be865dc4da8a52232aa5012b42b032f460fe35183d17ca6236521af3365229f1c0b1e8193841cfe2402848599163782352bcbc82da809b3d9dcbb8073c400c104d69c3500d9997ef8e0d6197a343b5b50851ab67bd0a644dd413698eb8624aa82a41445929488f0d59132dafa0d2d6d737fdfd48bcea934892e770f781087a8f6e521fa320093a429220801fc5559885bc0644cdd1ccda66c377ca3b054e2236d021ac6d32c06bf08b067ff89b8d880fbce7430137a3cfcb5074e7b537e0789c4203282b5b91246d6c6d8195ba8bcef25cafdab54c432bf7b1c4c62bab1403267797662d98a86f6d7f765c902a498dae8e3582391f9990130549ca4c800259d38b78214a5d1996d21a61b8b5ef45887b43ed39064dcf04944b1974aa356aea28403afee8affa5dc66966d8388c153ee972c9cd6a1e9b0b0f167d02e1ee48c445cdacd50148b38a6226a52493d30d77279aca904628396b70e04a5e0c8d8dcf30bea32e49f8926fc183c6b47d2d2c23188f1eb094df3f4ea38a63da04d8afdc6415a34647084d3676e13c06c9b863467d913d1245315ab326dad6a0efe2432bc44da5366faf10dd1f2deb025227b4a9aba3b88b7a2f4b99b9272e11ab312fdaa16025e1f5419d1f9adf9d0c18362a9e38531d2e83aff4bf9d3a29d5575e14cfe1fe07cae722606ef543bfab562648e52b6f3c577380f3627500ac9ee36568994513cf2ec37c6283964928be9a94ba1e08666d710fd374f66e8202864f42493bff8962978b44bda146b83fd9351447b69f311345dec285ceed5f37f1af1ea4956781a39992c151a02ad8924da32b8cb46390cbd0230801bface01583507f2c59a5ddb801a267187e62809ed387a0af9b193830c5283422c0209287ed46e2dacb6d13fd30f9720a5126c37c316a58bef9d0ddd9c04257d0d369f2180de9c81eb14814406dce6d660b1b413be90f1c11a90e88f54a2832a5b108850cf14bf8aaf24bd38fbe852692e50d26274f6d445d8a4e194e724d087100fe104ff02aa0886c88a200e89ed80966bc0f2183d6f36584a059ab714a76b723180d603df27569bed2fd90de7af205ad9dcdcbb9e7cc688329fc60163fe9b78b4a61a680d7b2a992072c0b28655fd8681ff6692ea8d5ab8009faec32da426160bb3952616ca181cc1d810b5693ff70a33a2d876faad90a06c93239e2cdfac39a18772e8d91a9daa8f884671da474a7fffe999ab6e00c5c1af7e3bb9d998574c5d623856e77babc47bdfad6013e9d28fc0ec91b07a6b65f6c4f76145060f36de70c0d7775a4211c3a4b59b94e8a6a13d0111d508e84b8d3dc0ca43fdc626cee847162e45df392dfc107b2d1cb1e3016b92c1422dd32726e4fea3866c224102e9a4a161a871350f14c9898a4656b457dbc3f2a39958510f00dacd144d2e918d9aa6adfe12bdc928c2568c6b0029aa92014b9a2637bc56a7a8fb33485181459d3c3ec21a599da9e5efd365113bae657e5ba4778aefa2afe9e3b301e6b3c8fd5013a632bfb6f13083d73afe9ca221ae0f8aecf7db464055352b4b21c8486af322956c047e87a5b3b11b23157e830c6554634d8501a4e2357adbfb0180cd9ffd49ffe5eedd9e8d743b4ac7c13bf0fdde51e0970e0265884529065a005010eea4271224d9dc319e6b84bf388addcff9a04d00c20298304e564cd14f28e2495920673d42583ecb13b1c388883166093023b3bb99eee48f62c8c92a8653d3726e062db453fad503d3733fea2f8b84301445923152afb5f242417fc24b9cb736c875bb9081d3a9d8d769a776ea4ef1a8a988dc327d673894ca836ca9e9661e04987b1bc8f626ba11a35bbcadcf357e1ca10240aa263740083e73126d16c249f52833cd631d7ada8e3a774367694c74c02406a8a075469dd76d5e72b5a0929f19ff7ec61fd851097221ca565d151fcf3bd47fe34c852890a88feb37835a84bb83d604301b4008970c523484193bfa7fc7c05af756698f25d8e6e1ce58c30609950bfca0809202b9a504389dd25b01fa964c467a7a45d2884258e33e394c0f4f141e35d61072e5751b4c51f271660a4ddbdc053204150a75c2f38d09d9aa25682a3b92284c11aadddc80af77c819c190f2be0cc72279f0c3fd12d5866a917c993348528d3e484400719f6b4c456d09ff8a2518e0c11250ae7b74831002b7f48d161ec8024f0eceed0e7013e15387d5eb67fcebf68523626996bb67964bf94d6a6b365bb8d07f3c4b8889291696523710b5e9467f4d936452cb3060b4626b0e5360a84055c6d7c12b74e27f31ea4174dc3a685720d0bd0d6b3df27c323cf5f407936e19a331393c808b45d9f05282e61a268cf49a8909619062980b2f431f383e511a78a4328e493443a4e1d0145715a00552a262374069b5c01a99ece20e05b05536965794a27d665e427512ad98d44dd731af796c22769e622bc44f580619b38ac090826a6521de339e5c5441594e0e7849b68b49429dff7d2f8c995da544cdce15fee56ddef83068c4cce1c7f2bbdba3b132748c5dc728fb88367ba3c48da3323c1e526cc627e9adebb0fff2e47670467119281b56ebf5fc6b369dc4c99d582e68f2c6611e3dc814c4c3f04b4aee03e48b5724955380652445e997a056414442cb2ca4ae14502daf36cb61acfe1d47597ce5b11227818cd2a11bd293e7f7b12dbda506e7c05f27102240522119c527967df8af65e4103647cf9704935f1b920ffa034ae91bf1b9f26a26dea3178809c382a5b2b532d34894eee37ceecef332116fc1062da5a555fa431031820a30926781406559a12ecffedcc2caa35525308ccb94cd11560c29fb1f72b3c00ba9943949a9725514bd360567162c60010ff061a5cf9d8e9a251840405364e0de6498dc30f6e85d000cc2ae10de02fb4309caa78379dc481a76392eefdb977c408c83be076c2feebc243d3549d3e105220c37422971c6223e93b062d8ccdafc390e01bf87426f3df26fc885b01a56806dfdfc76a57dbe818c2bd337c706fb7229b11d78b6b1e6d51f873d00468a2cf328d25454dc1855033b84d49cad59cfaee83fd507d97754f5e406406fae5483c509aead95632fca230ba8e8fc65884cbbaa3b226760372c782844c6454fbcf9b31fb04c214ca96581d89361249a1836faffebf0fb092609c0a0918c1d8ec7f5f264cfc692e3d38601a661c9d2c15ff49960c693b1f79eeeab2c5e49e721eb31fe6d1b3ee5323cd4a961e8be2f13d07b8fa6b62861bc36045cd405061999359516cdee1f37e008886f91d003dd6564342408b89203d3067d513feaccac14f808787f81b920f430274dd84f9c8c8f617d3d72891bb2ebd2b4b4785a4fa918484ba6508bcba7b14925304b8a41785556c03b0dcbbf73e083935ccc9cbe67f7553e467103233731d0ce04e15af7437c4993e6471835773ff3739135808245c761ee00bce0210b121158385c48e660811aaeb5298de41cbdf83273772e11950424649cac99821ec1db144aa8b7855a570bb7c66514f8f47689d66ae45780bf891975831a8a9b927ddec31e59ba5781ff95ce3f5a751a7fb458b0f5867c64b88f5a5ef5643eec473dc12c42c52f361c413ed610c6186280110ab756092de551697b94222c95ca4d7c8d7026a8a694f724c6352d57142276dc845cdd091aea4cf74d01b4719a2ab249f4060f9b131448feac08d5de3d08f776838580d9b72245f33e0186d011381184b50fd4f8ba3d41db5a99361d22da5d47d790c480bf0af0cd6d9f4bf22039a619764012128dae543916fcb5573b60300dd4ec59d1d25b96dfab0b01140af3a2debe98afebf82bb56c739f5d4ac51845946d578ee28276cd6848831cbf2ef30b1ae14149697f318366a1cf65b08fe51558b71308623cd531fc3c855c71fa04a5cfd01eeea4dfc0ee9526e5a916e238d2107ceabcd8b6a6e435a0a37adac802caae2a76b40545cb135e7e039ca92ef0c874a8c505b25e2d1cd8c132433a27e810f002d9424036318fde1177345cb643290b157eb948bb93e21e8970475551cb9c6f5afcd2a892472bc5a2e2b0d544a011ad0ec295580d4d9340631841485da51184ed31f8c63938c71702ed1bd31219a1bbb9e077e4185fdfbb6c37ad2836524bc0aa5da2ee7749fc330dde7304cf7062fcca999e2864a66e12d1a5fbf3723a9096db2e54e29ed6dbb0396026d028c021fffffff7f1d2730f6e93c8d3aba974e950b4cd2c82aa10c72d3ce9a504985d5b381949e240d7c397e84c8e2240992a2a12a5359685ee664ddccd051c21225840d71875bd5a6a26021f4d0592285051e4f9e50a10243a9006d6062022227454d7e1891e38938f5b384266328468691cb2f66ca4d68db4d3d32fb0810b310889d49816ddb4d7ac0d807244444fba049cd533d288e735b9e52860951931facb2c989b06412cf8e560b466c5b1a3d30f6e9d08c3a362f9d1c1798f6ffdf1291d584076d6b6c3b061018fb64624cdbe317b302afe34f923fa963e5129cfd7feb4459f34a42f4892d72ce92a3d05ddbb66dd7e811773ed850c2ffffffffe912866d4b439a34a549bbb91466c2c78e0d1cf8ff73468dc921bd6e3b49923f0895ac27061035f53b02c0a31a3a5e3e164ab5f1c8482b213042f4e20408848791161708b4eddfb66ddbba6d2faec212d964ffe7f49c22404de59480e40710241e5c4af6d1c68db16d574cd5562d492aadbe5a0c1a1bf6cf39e7cc1318fb74552d667a34362d5ce43805397dbc94e2083543cc906263679343e3c1459dec3ee69c87b5ed5f761c7188706838351c269c262525c6711c479678438624faffffbfb28d9fa8c09c3722bb18c57aa9c288d6fa003f663f7a202862a347287324c7840792840072947f0980cc10de2c91a39f21b270e65649b224c922b0155a1d2b243017c6beb10487829831be00c09c38724e1b1049b4e612ae254b2a4e82570357ab8b20a440149698737a8e73ce71ce39a17060eccbedbc028a37537333a3cd552d567203700a9027aa1f7ade5b002a79614766db5631e566ce20981c6152828548d40b0f2c46c92c4edd30b6ad0e48d6be2ddbf2762f95c17273e201fc24f9ff2eb02b03d1e0036ca662d6956d9b75a52d8e7a6cdb0540cd3567026d4b46093b087a318494941f5148c42296e60fb0396d4200e048f1301d2d8aff9f45098c7d37a70fb23cf5ff4f84841fd95f1fb5bf094511a10b49ce5ce07092d1a38690290f6e45f69ad0c4e5c598140453dbb66d68db56cad9a5efcfd8a53873b59db375d9b66d5f08dc99f560feffc64d18fbc69224ffcf169ad30b85260ac6db6e5b1572c0d8c7f31334b442052353f089adc9bfbc0a82ff4f0357191ab9974d0092fc92ff3f49b3ba6bdbb66d1e3710594a810610db8d86a0206d5bbb846168a4078c7d4093851121975f99816166407f6830e118db76b62dacf589ac398fa2c257892116606e0208e0bb917d4932985ea0f810351b02440a3e62d58ed1ced18b6bdbb66ddb361d3f32fbd53253b6e6dbd9b69d9524c953825f924d524e10193ca072f038923144b5c464498a00a00e022693df054b8a8ea87c312f10d93091a7dcf1d5d8776ddbb65d043525c8920e9e928e1f47dbe681098c7d4db8dc6e050fbc99153dd4fcffffffc581e19cad6823432b8c187de22c0093a4d8f232e73fc20e524940d924e98031850825f28739ff396790b7c8f8e474909d849124e8dab66ddbb60a5d8d4776b710b27319d09c6daf1ac2d4a2185ee181b16fa6e6c6306d5bb17479069136a7e738e71ce79c73071218fb946e4e4fbaaa163b7aae972433a5eddeb66ddb86d30d779075690ad61a0204aa8832bb2bfc6ddbff415c4e0bb0d7b6a19cc0d8f7a4ab2a0ba81e8d4d59e472da55fbff770501ee08914a0420a984f3818e6effdfca1018fb8c909462acdc7888fc93a4ad851bb0634223714fcaff490f6469a58a6c312427b6adce4d18fb461424e9030819e268a38d04655cf4d8f6417f4948933435d57f8984e506140c5ab2d8fcc020730cc53143d494702ca0d6238622fa54ebe803d46fdbae2b0aee51a0fedf36dc3361db1d59f189c234b482c9a835d9b665b90279807ee62cd2b6513d1d8dce4697334e2d85ffff181cd4f258d0a4c3911d8b470d3b39beb7486004871828b88004d14ebcd25486668d33c5f87e7e45b32b0b1ade4ccdcdac2c4ae849f189759235890a1631c499e1f4d85a4a04204ba404a64ffc5190e411e3092113263e21d99764741322616fdbb66ddb0e591a92b5b8d992e45b6301a8c50148c5c7050770b88461dbfeece60f6fe29871695798734e202730f63de9aa2c03d4a3b1b18c0c3976052e706c2df44861851d11091968a20c48aea404be21478848cd2ce158e2730496a9b1425673aee69c9f493ab7deffffffff6f636224fbffff24f9ff2f243957bdffffffffbf6bcef29f588a73d66ddbb6edac6dbbeaf8975580aa085521aa42ab52abc254a5e9297a0ea0904041d93949da89bca432c9ffbd44b14c0500012ac12157762fd8cab68d2234f9dfa6315334d155ba34a265dcf8e3c8db03edffd7f8a9115463a886510d243fd0b635f8dbb6ffdf8596234263931cac2449dcad24c9974140de8d141668e0733ef54acc990312308a9454b1e5808411bacfa09a2dbb030e005b76b2af3634d3b2234d921f5b80b022ebd8b62407bba3a3aa14ff9c73063102631f92d2cde94945105db884841e306631a5c975d7d3b66ddf82c0d8771b1a6f46af1b920b0c0a17d088670707a10d1a3c25429b9ccdce86076346941a3f731d7770c145923b14ad4892e429592e1484258a7ec0d8175434f472c1b56d7b6ac0d87733eb69d10314a20043d5046ddb9b323734afb60da35cdbb62390d9b6ed1f44a11db0d3784a5653dbe63081b1af49260737e545440a2706309f06e0ece9e54928d5538eba26b049ed77fd7f05e0cd06113197305ce52881b1ef96e3f472e98b1a576024a9638922ef544d5438aa1cd58e8ae730c9a766851a62186ba821063bac6d674c61ec1b89c81d2900c8ef1387f82a660388d2ce4e91214f5c1c29e2c50b0b14167233cca023a486ce0c098024e980e9e398c8b5feffff8fe281366c1a02639f1192524cd3ad08eb33744d615b2f10fcffa25a551431fd3f19b2441d9d4e9548bab66ddbf68d9ecc86ddb56ddb360f986bdbb66dcb2cd99f6ddf76db429901635fcdcdac870514a011f6ff6f756809a2c93e0338543aa89c14f5f0f1216699c124b34892f3258d1c38e4b86b3d1b8dcdc69613da760e3d7e89184f317431aa462bbb296385375333658ab89973bc1dd278a0b5ed5c0f8c7d34363953460acf9ca193093ec80cc6b8c03445d1b673c0d8c7f313340fe0c1860913f480038ff84373b5c2dacc58b75528f46aa9296144d1b6734e60ec7bd24d15b92a98cf39e7931118fb90946e4e4f2c9e74ac5795587efd3f0ca4c2502a8c5b3b47f12c92d4fd460c20e384937295361a84cab67db5bce815d95e30a248149a247cfaff2772834ef63724c91209a7f0849a9394c8bb3dc40e0a47acab6d8be0c2d8373ae151966ecd29ce29cec9530265006507054932f158e42f86a1d015182ae47a3de078580ef244d615db16a23485dce6b4cb2892acd3d36d056bdb95eb04714e1d47acc4d7ac019525962d2db64d14c6beb1fc77843cdb9e210c1e90a4a4989d9e9f737ece399ff4c0d8476393c3f3b34288207be5c36584f682b5489224e727ceb99a73ce79d891b64dea05c95a23498cd0e9b09225484feceda4927654ad1639864428c510711b894e90a493b29544fc7c6a7c6e5eff719420c9c35e947cdc1091a3324866368c659122a8b4cddea6a6000c00131a680400034118c9e238b70e0614000613ca94e4b83c301a8983c2703824048542813020000003434100100c0e83404090259b261f85118b88c4dc9b1a38f506443975d953c75687b874e860078b11c0d4c3e627e8bf1e101b2d236374e9fde983dafe2199fe8a352b09ca34fcbd5d9d96abd4b6bda5d8c178d9e19d900d408f01e7a67edc7dadc12145933f8507ff8a2bc4a4b9b52bda2a800a5d52d2716c8b27ecf9e5d010e22ac031991d8d11a56e6554ff0dad9f400145ae9302b7bc45651dc2722cdbd4904031e2b179e3709ab9bea2d144555b2567920088aec13c1b885e29529704b049e3c3e07e89e12c9990448e017f2fb24d0a82a1efd0c45731ef4d50d0e9a665ac83f7d03a0be9ccc4c561a9012786b10742d046d5df4f456261054ce709f9169badc6678266beb3018e513fc5837ca856ed51f64f7607c3edd1c989a76dfddbc145d381685d5ed13767623ea711c191391d14e6c945a7627e0a366d0b425289b9027c11e05605b8effe8a147e15b9f11771e8d093c8dd19eeab05c05db5825876d53b10eb8ead725c82953ae7737224d9e1b8a6757ed2f9aa93d053bf92b721489e5676f9ee3ccaae5d761d4074c3a8b584d41932919bd0c15f980104bddb026ade1474c4ca89274282a1edb98308aec3c34b41b30e587f080965d029fd1f26dd1822197d621a2ac331e1c682a16786a3d580e268f62921dc5a5fd1d154ddb4547ee631b5d8d0b3809e85c5255a80f521136ccc33490d2981b9f3662da62e9aeecac08516e0202c366a1666dd56fb995a24b397458209085220df7e5d766e67ef27775a0ee229be9f07496006a147e740ff1bd0eb1a65e5fd7753a45a996922f2215ed7d7f0fdbd07e250b493cca41b8e77fb3eec263b4f95d4eddd90e52acef76c9e3e983e6d2d3ed7f7131ddadf04f49830d52b3f9c9aae21b258ca0dd44bc5d44905d482bbe183b3a5c5d8ada047eae23df0504c5604ad6a674fbf1d714a102da93a1946b22d86b0dbdcbfec987dfa3f0bdd041a938e5a104cba23c79541d48e583665876d4fe511fbca508cd729ec59abffe15c1d4e56564af3afcc5462f9689b625df524c11ca56313f994bdd9f1a02f7ec7641f2a801bd3b424cfe9c4e4780f1079ecf917e8c59ee81769ee4b5211ecfd0e5083de72e78e9527232398c64fba64b021a9095f0903a459dc7fb2a788dd73523d710c134f1405768bc606a2b4124dcb08c59d9490296fc4cce892eb6f483e402345f37712801abee46b75bc837453b939a2e2b8fabc210d0274bac1890629938473ba01a74560d9316e1e5bf79a320db6927168704edf0fa651e433f98951998c333553129a6081c51bae6a8c30ca035c3929197186ae3a0bf81bb68712faa886ec5e221f4acbd71cc0acad5beb436fcd79a5eaf1afb0565c74d15103890bcf1280dc20e885690046abc0965f417d41518347ff31ee8490b785fc410e14441a875ba2aa65d4c4f51cc3a960ae0d82ba1d3266d916eb82098769a92a1a4092cc4eaf1ff3ed924288542b6b9a083e449595d09c69b3f1c8dd457a9a8cad2f25dadcb0d938c8e7a342ede5cf248d0e32c6d0fbfe5b5fe40ad515a0271b78d205318ed9d4632ffda47c3f8112cabb62624dbc4dc06d35e1f120701d17a21fb65d8fc461c24b292876176602e0692894f6d656c982ca41d77dbf346204945502860daaf69dbd33120a3d0bface3c243f01e67a6621c49881888dd17a13ac9916e3ab026c5dcf515d7df47569fbc8c51c01c2c7b4ff98ec772ba6d80b291a87f6f3289e49ef05559ef24a4af4648c3535d4c0d19a2fb7e045b758a7491183ea9ae78179fa3e7ab582141f5165150832a00b79b47741b751cbb732afa0703ff8c2141a214dd9e474caa86094df0b24468c52f8f5962f69400ba6850ce775635f527c50724d0a12f8f04a76d8330c10754a7160391b0603985cd185879bd966c23de9ff2b084ea91af00601f4f7cfa39b3c01635ca27352b6249e3f3ae0918e71ac5a351f67e9154a352ad4a799ef8cc1cc09808f936fec571f60a85184221982905a205a3582a0958410fdd72995bb7df1c5b1ccbd24efd1d243fb0079626430173d320eacfc39bc0c1d473823a7e71e5f5d60e8ba02d2185e042e6e65e1b545e63cd0f849a0ae1280c0e6c1552ce057dc79184e7cfcd16c7ce69abc9ad4051d17b064158d2a5b6aedcf6d9e273b807df1e79164fa53cfa68a6f72c8871907487146d4f8ef84cb2f799ae4f3226cdf5eae8670007273c44a82648875f7668c5f51efeb746cfe634db28a1c23b1cf2551f17e91f698fe0c95fa4d20078d350ab12b913440d2a16e2704614b0d5c347461a852798da1b4217a6cd48e910e5ecbaf289dcf553d42b7bea298f743b55ba40c28e8f54c206e60584a7180e6556d1c57568824911ed7d35de7275e0ac7ed7c09593ab7f9deb352aae248c2f3b86d5d835927277d4018ea114abf468d94ce78c5a52077ad15b541551867d0acdfb226c997364d2b0b6e75f0381537d728c5029c0b0dca69aa06a7d751d329d08e3e1774c91aea1f1ddd6506a3e579334a31dbddc73d592324f892cc327d6b10dde6a40082beee347a608555253b4f2cdfc25549a54f42084597f826e98792058b51f03d129a5449d4b75c00e4fa3fcf401a2f125efa218ad7c65dad23bf809a5727084f87f3af282e1be4733218a5b1c97851db378c162dfe7e40b5d0b1f38838cb3acb5a0248ee412b812002367e449b905c8e11e4a8a1765233c548d2d4a29fb0736b62d5a4384b0e62ed20832bd5c9aecdb30c173f413af92e90852d664d885f471a7c53488a608d67d403dc4f91740333af7cca2e005398256d0dd091bc88b26104c556911db5aa71aa0d42010908c5b0caa613d0533e2f61d2523c3c09dd14dcc31d3f24abb4cf2b1e5bee535026622a4f56a3b8559cdc210f159e707274182cd2be40955ebe02ad7960a3f505801273799929666015989290f041bda2305fdf33eaf96a029b1f2a595479d15966c434603c76e72dcc33f1a9a168121211f18fc25e0c5b18a4e47bdf95f345516b19274440d52fbf004772c73532ef6201e6e25b69508d918e9227c201ca5d327fe1a75bbfd0006be4f54892a2689cf79253bf2bd1ff8bd83e548dc6a77e313b59b9324a023364625a7e8dbe5759549b06010e06cebe15c3a1c569e2959a031fc4e6fa76c9c8fee9930bd00b23a782022c6eb0e4ea230ce2447a399fe840e2b996fee4cad6ca1cd8e398beb8b1bedc9e314e18eb1a30f9de91208665041ef9d31d658a0bc066c2ae1255f0e7d5f09fd66c8838b1fc5a31e68b7989626f61b570610bb2e6922b74e06bba52499696f4c8bd0dc96b556f3da080dfe2d2cf4741443abf523c566f8be33981b56038ac98c2a18a14f422a2f204bf82481597246db7f3de0d00912717e888c6ab6c087a7caa96ddb8e0d18d05955fd261f4e3f2687ccdae60a98c7458b6699f6458fb5522d0a672907d17af718320fa944d510800ce6a9beee1f59fc1ef59bb4caa9d187ac6c199c14369ac2654fc5bc6685f7d96f11d52141f2065d2a52b53faf3f85c210888e33fd1af52ce22ad42561eafc8fe58de2c4772af1e573f73ae6800d374347adb7baa450f070744754b3dcfdc26cee505a015e17b313171e6e168146066d409e9e53322f2aa1d849cd9566f0bf3165376b010d4c69d15929041271e115afc8be1ba69628a0898f88e043971d15331477927dada39c3ae8e26bfa8d9ae888617407fb8e6e657bccc0d0c07bfc975f15b27a889315de18f06f035f13e5c9f61d39d8a2c5ca49a4d52b49cfd2b5d17c9265580627fc05e61b3b05cf7d762e78e2dcab9e8f107d02fd78876d4328507a44f3396155ca40a4a25ceb27e31f21e4843688b58c603d65b2133005405d63819ff231ca496ba1c4946be17f8145c6c84a727d1cf62869be8856a0891382f61b5d52fabe45f5a882e49ee7cd24aa4e96c0996f06f196be263504547719d3fbb1a13291968a14eeab24ab36ed8786189afb6a80027af2e0390735bf1ee7a1795f1013ff0b4d14944d87775623c88cd63d5078c09244010d7f17ce8ec5b2e5f668ee96bd40ba5c90537812c90d9a88731dde94e30e99e58013b80b18bb718111a7b6c21b4a1dd2765e4ec7920f8be11272eca8689619779c2a6d114344ed97a17de02ea9dc00ff74ea4b7335527bd9001c6812e67981747629cb81ac68364703ab211829575986ca002c9ad669f8572cccc1d417da610582ec638df25454215fc4dde0aff56dcd5663b09fa06af6c959e90d0b4bbb1ab5d30cc45d94a0c2ebb9fc440d18034bcc3c66fff0c7fadc3011ad180f7c051a366019787959bc5c2746828cc2b3ba5d619881438f696dfed0258cea256b21c739c2df8412655b00cccf35b4a84dc8f776dd9b8a2fdd31f98db85f6ad4c644b914d2ec0ad20da14c9a6055b7e8ef803a2e3dd8b53646a579b63c45e18e01e99a5fb883ded3939ca983061ad6c1475bac6b6150773a5a80d78803d767a3275ed0aa814448e8f73cbda8896e50b82d86cf000fe5708d008eac932d9448688216607b6092b798db7b66289022edd65d3a395ec7c3083099d58f3fa3f88b8ca083039487e5c9ba5e4488a06cd09a829de5dc09838b57f7a40e384c0fbfd082d871cfda1524bf5f771e8e3e12ab181e16c34a46404a816626196370b00d8b0586fc8fe6140bf664008f36305e5241a77540d5ffa554b54d8d76f3d3a9ff16300fdc29222c1c43f5a606f34099316d0d29e4fe34bd27057e53101df0e21b36b2ca8ecc244ab616fa843b0c3831b83c0ff998d16579e4cee9f4aa414708b228d3efcd85617976847ba69c4aeff04e80cb0a427bf65c583c6740ad1737242172dcb4b7fe574ab4a2895a9919f37512f1ca4cb9984d4b3b582097b83aae6f17d0483831294d819f3e5b4c44594174cd049e85f908827afdb3955922173e1e8dc9ce95ccf1547901025a2a49b60a1949d6b9fe5495c6d90c32a280074dc2150a7893811f45bd2093215c84c17b1695760d9c55bf356a4a5a78ff771f39e704ff83473ea01a050365813e2b7e31780594959c203639b101eac90ba806ea18e5d05529572fd97b108bc5392491b0d50e08241eb8a7d749421555631f83730060ba58c4721a82429f73b95578db0725901d554bd299ac7e123e3f91860eef3a1b016b380a9251f197b75fa88870dd7843e58b2f1fd7400ed86246d2409098133902ac1de8f0934c26d33b81c9cec6490581082c2b4bb54fe1d4661d759105a2a4fea0a9c7685ef9f5b0d885c5c86651d348b53be3e64b83288d9d51e69b8bf1cefe219ea0b27d24dd46d1f385eaf1d0b15739742fd913d4de2aeee7a863a7baaad1d046ea0934d7c249b4a5d6f98eae206cf8c8cdf65d4624e8276d4667ebce5b4a817111f9535dd19b526cad0028a13637e412e7d977be3c841295103c0ada4f264fac6929e5cfa8d32a8d14dcf43436dea37a861d720b0e45533655a772533d7b849e85c37e0a50170efc64221dfd2861c8339216c2a3c01a0de31ab304565db5e5423642c6a46389d6694ee0b584d561650dbef022543359985950ecfc6d88b68ae5d76002cbbd9d47172837025a514cd6fa7f4e49c4c38fa043d689d0e56e4e899d9f058da29ad12aaa5e89aa7d9f49b0fedb5545fb59dba6041e5dd141eca7b9b8ab5880574dba1d086f4a0aa646709da2abca320d1349e8c8afe2027531f60b0b9d3d21199f306db8141823956d6ca2ef1e148ce414e1eaee19f7f3eee6080a41068f1c0f511be81408500d9846b6f8225a80a43d1308159a3c0df63793dc636afcf458b97f380b8b2b0cf38361d09880987c9c4716e4bee138279c9b23aead9eb6807f4c4b921485161c8498444bab4b07ef126315629a88f8c8f8cab8f2c89552905062bee151887d121dde85aefa529accc530fe8b838fee766530b12af8413c6f447e044b56476bbaac93b8c64dc7cdf63f6bbd22ae8b961f6a8c93e210dc8a145278438fc8f20c1a5c693d50080eef8293db2708558e5128f7ab09b4e2cccd3376616d73360af7a6495fcab9747886fd9019617cbd41c762e4c4e36a227290b2d263dcbfb2fb90ed7fe9b0385a020bcdeaaa6a34200a193704135aa4222c06011346a960ab303d0ea3952888ba904d4515c1f9ac7eabd2c66a8d2fb5444aea4b325d5c9394b40cde9add7379c435f839a4ce45e45356304baf107929f485d33cb08ac2fbc38ebdab089e292778da7b6d190120f441f50265642dae5f0cf179cbcf40901e7c151acf74be6a5cb64c531af18b6e99271b81ba290b6c809d9826140b75009aff034a4ff3c4e7364a13f869facbf372189d9c24893b6890668b93084669ef85443f40907c5ea0753cd888ae1bba7680fa73e9f3165db565e3b96a60557e71d2ebb2c838201cca958c12ba87e44246d325bca9a694b56e919100e7274039369865534c6f295ca84370f8c4ccc456c70573e0a06d415a6c7d384e8e46baef1df782aa780b8013e94e7e460a1bd0eae65bc41412564fb793108747a0c56b98077bcfc347844cd2c72d5b0aab5e99a4a367f99d61624367db91a076a3a9aa0cb5ab2d9e6bb243a742e9b1f29f7cd0745a85fcd4c3676c60280a30a9d524ba1d2699d5ad9702b3b5ec99495893b6e2c210f70836c3520aab0d8548353e1bb26cbfa16c320775912d0c1e75f05382508493390ea15eec1f8bb2100a94e3476ad3e11033d04b6502e01a6a0690939398e2cb005db3785c87854a24c89971714e034b38d7f7f35f1c92b374c3b3a6d59ac96c49709ffa956c377c11d5d7528e28c704d7b751809912b1915ec4c5be9494ddbe64338e750cb93de88a9da7ca0b7c699c0ad004d1b91794c74ac19dfcda5900a20bb54531a9086d844c23e0d819c115ef5f13c1a26532a38661ed45e0abfb838005d54582601b601f4a12f48ad9380832ad7d219244f9f0b979503d7010c5530c9678dd63c5f733094f80976f9f0d685e304777e41698f7487c4bd2ea674554d7de49845d77d954abb4255cbcc13932f817ab257beac802ff7f3e1f1a438a268598c08bcc200eac912c4f5100ed4ca3da1c9d91dfc7431709a6f81940f193400b622cb8fae2739f80906c4ac4124531ce2c9b8548072f3b78b8d68c1b194416e26bb8de67e3997056fac1050ec28a46c9f917ba7de2bceeb70959ba38c6f6fd7d44c90498462d63017c68b26085d8ffc1bb0c9cf03f725d7e375760cc23d6bc9e3120ca58c297b0b606d43de78a5078729d62e8a84b6cdb03094961ef078b98ad187b810c509faab8a70be177acd69b0114c67433b907e390007157c5b2d844ea7658a2055cabe801112f941ebcccec71ea3db81cfc27201ef531bc62b2a8f2c4b5492bc36b5bf6dd95d940168cfa6cbfc36e491341a374f126c4a6c35d33f4aa5ea18a7b5a19e476eeeed9d9c300dea2933ab08fee46242ad231dd8480febeb9563fd64fd5fc10e2dd0033202382e804e43fa8a3547d6420904143597049f83cffc33f0a40c3a549980cf140d1d3092cac94e10932741e5922689eb0a02dfee6eb05c938d7403c69eaad00ff55efa566722debc14379ec1e08f3710f562cd19525b4f929486cde2af7be74c194e39b297ce2389f6f6eb5b3c85d58f9e11555e8e4989024d130cf64a7dd80e7fd3240e20854e3a2d16daa20362a9a43e3d1106e79aba601245144fef203df0b9fa698d5f5cc107220830e2e5303d020904a59c59f2396c146e13c251927d05370adb1da3dfc932aaf13aed68a66746cab0b7d7aa20de01807667eb3d6f05fd96394fc6194232227822122e597b32197d129f96cce055703618335b22a0b102b49727a018fe686ba980c18ccff3f3eb477a910050ec1d72188c03ad777778b47417885419a903c08b569c63b778241a4f123f68e34eb815f9a0dcc9e5877106397830588d6e89aa96698af80a43d6b65ccd27a740c580150f25926575e8b8ca906dc171ee150b6c44ffca541ba04ef1aeebc7a9bd9c4251013343df906d85006243029b0e96bd624c441669f9cc63614d1826a8506f7628d440a59a4197680ba1ba0be41d32af7401d04d17733931e13b1ba82e3d36329fab3daac8b21077dea4ed68511c3ce1b033a817a7754f970e94bd7f83a31dbc6c1ab4558779f91aaca36a8972604d261971f0200a40b719b58a3f08133a4d1f46597324e5592259137307407b5c8133b1c5763a53b98b5a0f216ae1d7185da5dedce036a05354a3436ea36cb4a92c995075b0923c098b202ffac23f75a39b854c7571ce828128b71f05eadca04071101c40cb371248bf2f86dec897ef6a72fe17d001a408b5070e7cad0d6e7b87b4a6c1f23192d25a603d4762a340cc6f8f9ef2fad5a6865c66ad69dd272cfa2e61bff43d3cf04b96648f0d6dbe4e327906fc8d2f7d38fd55cd81935384d2b8476bccd5f6661c4c75420cc331538f2ca6cb201ca29eb1435500975475c0b1bc1ae5ae7d8d783fdfb378fe0a6650efca09b4088160488573b6e1835d3c52bae84a517272877d167c5d5d045eee284663bc13fb2a69b6000f322b90550f64def3bf8d21159787c47d1c4098485d715984160a850532a7b0243be54cbe1ca898a0a52ccb0d6de2bae2cf9983092dee93e0e7ba9f2dbace6c3c1092b1986d0ee34c9b50e103161ef6f212328e0844a6c36e9ebb7cddabaaafa2412770a0e4b1fe3af65c631805a57950fc610f5834eff145ba32e1f588179cacda024168d42ccd01692d485022a70488e6671db3bec605af6e37872f6069359937eb427eaedb2644a578c83dcce150295bf5db8fef14e32da33ed6dbd5b715dfa315b1a78c42bd00f1b1d1f31c66cf033b361eef055be2ee27991262f8a1aa6223bc9e9fbc32d5b2637d45f313eca413f1e0d898a0d0e7c5b7f9c0c3fa3115f7778d3f67679ee3037074589b1b49219f40d2892592f29970f9cdc18e489908ace1ba64e93982320396008abff829a4de7c722795a1f896a64cd2c09c8969226ad7d043c13915b85bfa8de544268a5a00d81568acace382300fa5e1d0dd087802f4c3b702e949988cef86df2c8d5877dc74ce3128a9d4965143195400a03d5bbef3fcbbe75a869915fb2a687e1ebc2fc5cee12800ee3f76995981306f14952b429012675e773a36d154e6c73b03e128a8f47510f2b03bf52c872ea8e4050f9fc8f1a7ccbb629a94824c04783fe31d2dade5150b81cc4543bff4c725b3f1a3cb1d0d6f970b9e2901d82ca7f49668c63ee71a10e42a0671003e4d056444b076554db115cdff7f3c36aaa42c437f6675a25695ff33bbc816900103ce65de18a900b054113de134ef5225a4645be2ef44c005ee0caaae2e128204a0d761ee81228a972fcc713ad261e936594d06a0a32e18466c3b5f664b16ef13cd2225f68c6c405200ccb95f645f8b05dfc0470e9b99f26b8b5a3416a88c230d1470ebe41c757f2b7db1777b2e93fa8e71bb14b28b55e2aa8423e5269d780ae5303d1b4486d003c9afd254c812449a9a4e940f6fec40ed89c43c2701aee338b8408fe5c56b4585ab2ad8767ea184636c52eb6492c772fb593a06165c4b0eaacc18c48f6fdd23fa3e25f810607ce06f8aabccf41458a0bf27f44ce39a04fb0e58677bb3ce464d337c3367ed23aea1d5816f915a6ec037687010db548078b301030f9e1fbb6e2d4e7a11dcae8f12cbab6b1d2b3de7e1b9aa8a3c0b793468840b6c36e2576c3464f5a682e3841893b9855a5b32316ede0acec335805a54470fae065bd88f5005832ca9e962eb1196107c88532a1c40e98ace8239d5a823feaad46e09412e69353cf48d1f6a6b414f271573c6f41d0518b1a21fa3394ee80e539802499459f1922a4b5478b9ff47314a66889f81c2ad0847ee1b2d9d7e495724275569758376d5698f55635c16b991dbe83a557ccb5bd4a2010a19754270a121ebf5c75a2c763c71031d2f723192a89fa4945e653f1c9d266b419d5c0d57acbc87aa9e47772e966d9386fdfffca72c7e5a256664213e49ad4820fd66a0d0536283511a67265cd1a40b28944f48fccea43611299b029815f32a6175f601cc058633151c52d832167a20e9b5fdd6d519386dbe08266627037007d8a08623294f31b0cbe501ae282dcfd2db9b31f63828f664e94514fa345bfe8085cd6d73a47917c15e7014fb16c01ece549d98da21bf5b91014dcf7e3551cab25549d20779422c16da11ce641da186d8cfb4404683750f8ce97ff6c1dd88efd8cbdaa7d2f7454aae294ba2ab92e29621716dbd5573aed1d676fdcbc88760aee929b65e507ad64ad4ec156aac0954897168417f890a888b204b00baa2c862935ecb5a0a582e869dd828c0909bb6545bc045312cf209fa792c7d5e1feab835416b44b75c422ebf011e6ea049e3c41bfcf0fd5d95a886bc2138cd3fa5007407bd9217ea9a53871401011c6da126fcaccec262932e1100d4129ae46ef0292041513d711dc30ab5f206410975eb6849632527609ea63a8eac64560e50444ce795ef3bfa5d072c05827237c768c4d518eb07a3f9e9fb04ca2031c8d53d46f970218500e475dc68b911f44bc657aff064a708e58ba4268cef0ccd8f8b47c4addbd671cac95a64e39b814880ef9cf20eaa5074f704a758eb6472d5c730be57185ed470f1f3054ad8339592f7a519fc08bf0a61c70a126fa72770c0cccd4100f761d394620c8e8509d2743f51a79bcd52274ec4410c29e0945d4f4a8dfdd2b02b146b4025b0290e409cb535afb22dc51956726981dad7c609fe979636b9e38dbfe08c2f0a6212b3147022fb89795e88fe6a4cc5f89e9736ae09e9083d614e074c34359582fee1edb2f2325bb53027a5db007e2509126b428fc46146d63b42597f99292849b7e66957978d2eac507db310f97288350ec0ad639bb61a678600158807c6692b366986cce45a8986aac9a88a5a46dec6cf9a5c3f40a618b42883eca5958e534afbbf971bc7e281bc855c268227ffd810485603802456b95c91ba00505197a508b3b837a9b4db108b03bc076ca242ae3e272a9c9b72de1189ff3cc3f83a9005a4a1010a3075415e408a59451d8ae46e75253793177a0fb0e60eaa0e3b61fd8fbf69ceafd3316b6d147a51d6c132060eb1c5b41fc3d609b21459e186fb67d8fa67c03c51532cbc1b0905d7a7158ca3e44f30038b0713d4ff8d2981244fb8db965ba6b4b79432e003fc032204f75ed5bdf7ffffefbdf7fa8efbfeffffffff3fe567dbff58e54cb99ece47dcbd185f8c6dca4bbedf7bef4f9451f2de7b57586c6edee14c4c10265a0d1d3e3486c985d1ddb12ccb7bffff9f36adbcc4c8fbeeeeee5b725646756452deb936d8c2d3b11a6e794b16706494913135833663c855ee3e9ae012ffff7f666465f408af9821b9aa1c69e580c95b870c89cc88d48e86a6bbbb0e2b3cd1e338f228d3018f295a07280cbd1621821c43a4210e23445e03851e42dc6ceef7de7bcb0fb59b1b5cdb94e7a3b6ef93d40c1fbe334d5fa4b8093f8afcd089b8f7de17faff8f1afeb35675ca0c201dba39e446f08690ac3b7737c727373a0677d0f0b3f1c4057845072fa103bb95bbbb0ee39c8d3b8fed6e43869211aaa0fc4699dfff7fb6c92d8c552d263c64c762084fccc5bdfeffcd243127ddefbd17ca131428285da0444129f331bfd74b92244972fc5602a27820f1655094d17befbde2cac94b62126ba159191d222a323a42ba1b687972af6f999120499224cd1b281e212537e523f9efee6e925961cc83b2b180ba9133b4caf2a6fcb2aaf1a4c6528d5e8d27a9f2de7bafbbbb97008c7246e9a8c8eaefc5f83136f1e5a009e7eeee6e5225593054ad27fc82eeeee27330e6e7aa771b7777cc5322daeeeeee13b8dfbc8c2e32a2649431a3dca2e0a2e456620b04435cab55e236e50d751958d2af598269f915ddff7f4d9451f2df54b5802449d4e8b473777777f71c72a94b2fb74cd5ffff8f7852e5dc7b6f8e1e47ea34b3325ab361164e37fd3c2dbc6b04cab744011a3a29559024c1a062d5c8502b42f19010369ecd67fbd5116333094dfaffc7efffffffffffff1d30011ef5d821861c40c0f4c20e41efb5fd4d35166d81854725cb6cdfd1171367b8970a96a10d799dc464149c95d19c2e69c7f3f98a283f77771a54feeeeeeeee2e7483e1c99a192cb810e44e63fb65020263cff99274dddddd6dea8a303ba22d42d129353cb288929a6163a7a4dce8c14cc21380956224fce556007a18b708734b08cb7c6c8500ad0de34419d621a21e195cd4184132450489cf9892038c718bece71fcad0cae8ac66bbe1153bdccfffff04452ec65ab4686192a4cb962a4e82dc656c1fa53c30ae94a4a0c304ac1780258f87ad453e297a62e4eebe8627caa8bbbbbbfbf5fbf7de7befbdf7de7baf59ef04185ff387bc8fdfdd9d89cfcae80f181472c06448de7bef7577529768e8b1463ca972722c7861c2982366bc0835d9418546038e6dd1c391f7de3b418c75efbde4bdf7aee179076b7cf9279a6b6828efbdf79a94478a2dfc6cfedaf0f8a79419babbb7441b222e8aa68c6f8c9a1c5572c8e242437cec3ee8d0e948d2b1ab5f6656466bb61b2ec7c117ddbfca1403d401678e9ace20e99ace5429854d66724d66d6d89a1d42924f5b13810bf2a4f35164231acaa7b5dd8a4ec8a7bd599c35b238d7393df99a72c81dbd904f9b0321da6a40acce2229c9a7d549603b733b83eba5a1ed7a3b3cf5cc243d31924f9b7433030987dd120af9b4bb1ddd88c33233815882f07a37f2697938c05913941ab009ca145c130c3d3479e0c9f7b4423ead6fc44cb8e403cc0fcafe4a25a1a1126da644e3a1538251b340db05483e2dd0c429a599529aa2cd94863668a35ac8a70d662bd4a12b5a6285b68c2e9f56886765a68039704a55723c3bb4532ce4d30e5b3e265f528f684663924f716633ebf510673ddc975d8f28d8bbd58670f9146b5bcced5ac59d88c2051473f604d515213da14c568c76d5e2933d41eb4eecedba73b24ff1b7ab8ac94ad33ec52ebb8a274ef1b6abae5a34aae38da92716d913391626da44221bab5ab489c2d182f043456f3d1dbc518a1a23524803b8421315a53437d741034629038d1ada4e4192e4ee2e1bc9ff6f85a0b7231e69a3c2e2ab21ca66c9361d69c601520dc9669a2172a28c921240728403529aa899790e1b4cc0b1a188848966655e1aeeee3423da110d89f6e4b5fbbdf7de968fc5922465c64380a1a1c3cf890b471f701c44682c11197de4bdf74e5096ac2a062726274677dddd7d690c6fbb97ee119885e1528313af27b45da81f9e0d532037de15821befbdf762104bf275c7eeee3e13659474d1dd9de541801817b9e9822f0af98ac0fa136594fc559698bffbbdf742d1ac8c0e1115fd065046ff66129641c9e8d76ab9fec9134015d60cc11863dbcfca2830286c611bce5ae0a8d934d9d9dd6b626c8656320680b90075a5062632a890440b07702042ab0885367e73b10283db260763b25cf20255ab2d00b1bfbbbbbbbbbbbbbb5f53c9adf0feff6f7924851a13fcff13d130d1102622c2f8ffaff0babbbbbbbbfb942949dbbd1cffff7f8aefdfa468cc601b8c6518da58d9605cde8bf13503f5feffffffff4f24f7cfbaeeeeeeeeeeb7a7c254ca9aa41b159731470565f54efebdf7aa39b2328af4644903353dcb83ed5d0cbee05ed04153a6033453b4a10e8644e2ff7f30c9cae88ee7d320f87be1d08df0074e0684940cb141a50329bc70c27dffffffff7f31c90516042a3081993308e9580ae1b8bb7bcddddddddd7d8531c642495646773cdf0fc84228f843de7ffceeeec0232ba348e493bce4ea690d804fb11f1c42b01d87c42dfb7f2908b818a4848637a6ece0378c3ca0a32bafbb7b1877fcbb10f66877a3a2a2ca444d8da1bd979b4d120c6226ca28499a6569416d8621a9315690ceb80d2ebe1b2e37505077d8b069561966653992ff5bdc55830a1f881189b9db126e56467125e472093a5709493a86446cb90945b67bcbc97befa50d591925a215659a918b76a4630118ebf275bef0d50adc00fc1a37a527aaeade8bafc518c348d86863972c5760db82d41576df015b025f0e01041b7b0aa0b2a53bef2da82e40eb6e7b7de52c80eaabed1637d575a1b5c8a220eb4dbb76e1fad1df8ffee893132033e9cada15d33fb193fe5cdb4997b85db19f14bb2017a2cc6b5245b231f7aadb41a92cd5a25845b83a7ba98f8d94bbad082aab772e5541c79f1d1506297a72a44851c2c6442dee20adc3483071b974ada2a63b3090794ce33b419367c6cf54b756abd58223883c921764a1e2192256f285124c8d63034417c705d0dd111a951d4777f1f5d5e3aa85b8e25f8499b9917c7f775d5d8fb031cfb60833abcee2aaf506e9e2d994294b9453131be5a26375f56ccaf48463ab3665723aaa614090f1d6d387232e156246ab033fc66471a90f358c09c22015a2059a8b1802e248d1981a8ea3c5a9e4561492540820881c6344c8c0e1c106a956935de591c20764d061061d30b8dc6684e0f243889811d074c0018520cec36eaa338889579c6c61c149044c3c2d427ef2db6bef63c00a13e091932d46ce778019f34cc33425465afda123a937651ad2b0cb4d99864e3d4c48e9a0cea0c043cd0d4e24e28a88183eccea99f3ff341a4e4e2b0465a0e471d39e909097cb4d7b425fa0caa0ff7f9ca9ac72b2916d62c8424c562d9ca11124a11f1f72c879d39e908d9b2d59c499a2bc0ad3ea61061068b2b9692f46152659b5692f06940f56622cd935f76214f9ffbf1941c6d9b4176307ce0cf9c3a6bd18313e10c92336edc10803024c2637edc1b861486e6dda836105c8ce0b0c17707ee4109bf660fcc82183d8b407a3e694579bf6666954e4cccb76cf2dd7ddb437b361ab36edcd84fbccb80b8e1277554aaf383669551c0226f666eb3c14d92e261e4ed81f36651a4e0119c0ae4bf54b88c0e5715326a79cd1be7aa43bd5dd49c8763139fd18a3a23ba7285461edb0abdd2cf5c3723c781248fdfa45d08ba0174194053cf382f00c15d2bce5932477a370e8f7355ede91cfec33dc91d1878be22271944fd791d677090c3c62258ae433f673818d7cfe88baa232ba14182cc9678add52110e979481067427eeae8491da8105037ce493f21e0784f6331f4d1ce041927c529f69e341ca7d3dd87011011cf9a43f73f685c788f3723401109ae4930245a09021df01f1852042513e6970b4f5ae8079f25d012366d45df9e2bb026685113030249f540802d8c389e9eb61450324f0c8271d621d102747335a0af9acb35dab365156f52b483e6b0de7a404a6b915e9c867bd6d5cd539473e2beebe6ad2927c565de9b3e26568e5cb6c9c5991b2e1402b6088ac7869da2dd5c867ddb9f088102a95577b2ee4b3f2ae70490a52aaaf3ea990cfeaf3d901814ce176a4a9bf0a4594cffa1b7d3a7c4bb3a30aac5d82e4b302adb086d50eca0e3558a372f9acc15df5bfaab00e4559d5004092cf6aea844c3174538666b41cf9b433162e2fa0998d593d82c4aa3be2bc17c0b4d92e1a598295c3748ac85c4c55f8f8a5b63d2c9868a1c708053271a4ca30a08e910e4ea7e8a802e9f4d0f1417dac89a36d35099ddb0532746ef566d7d1bfd456972fa01ad06cb5e26140341bb3da02345907674f9c22b870bff6b148c7a88465ea33fe8bc40730c5176bdbbc8a84d0225e3d3851c6d2f7d573a33e2cfde1253442082d2af514f5f4285f3c41eac3d2e48b07487d585af5e2f9511f9616792c2da6e9c273c328c403b5599ac5b385c6581450a0354f18de6601f7e962b17488170c28ca581abf60c1a088021665652ccd8295d92c1ddc2cd8053b6431b7cf1e1f88574febd5b313653d467a9434d9560360b581b84d584f8efa584d42a87333e102508c6d49134f4c28a14040b35a81a52aaed6f5e4562b4658cfd11a133a13482692788254c58aaa30b5d53c61280a99122ab31a078e1e3d5658c18811254a4e18b0b6add6596d70b35759af5a58f2625dbe469f8effa84f8da95e24d4161a7a7a562baeeed13b2b0e6e760993e9a88fd50650924f133db96d4de8b6d5aa9e558bd7b85d6760abebdcc6d77d550d34ab40945670bad3aedaea2cbb02cdb6d52a20537c610c4cf145aa10608aa38d91660626a52a48641b16ceca5495d845def7b1848f02445b6f0850c14f010c6001095690f82a3790ed5fdcd80acc7a8a7872f0d4f0c173a547051e22e44ff953975cd5f2b0e20e1171dc9142da46df31e1c8c2ce1d7f9ea17717795d0198cae74f4d4441c5bfe1b6f8b9fd771c613bd4a76a9f0d1695a8eb5b7336069d7ad3acb2ab0f3ea041d1e5634e8bd7565b588ee82a7a1f5f94fad89d18446537dd39339a5cf5033004aefe0d604d6abf186b52e3fbcaf1e0f5d54f49187d18b9e3d5d61aaa0b98b3fdda6a324fa36b88576de2285aad45d8c823c937ebca85fed50758ffce3636f24a5899d5549f64b6252c8af176616709879d3bfb8c9d310c60e705268c840a4645d9b9032bb14fea13b259b366cd1a0bb888b9702ec69f28b33a29675cc1c69b28b31958555d7d403f5069ba72c04a8bb21c0f4c96365f04a94c6dc24830350b46ae3c6069158c04951e93b61d73db8a7abcad2050834a4d570fa8412b98f9caf180a4e28b20d52bc703ac4b55a9a2aff36f8ff3ddb6bcf235d217299264ce2e97d6b7dc3eeb2e28a2187da2d0c5738dbca01583db064514344814e6a09127ca7064bd479bf7b85ac0a8aba8735ab8aeda6139aefd624bf025eb8d4396cddbd1e7b8c26a2159dcf4ad4d2dc5082a847527c62f933f1764b53d365c883b412abe19f14316206b0b4a9d47dd93824e11fc3d91f9855001172e2aabfac737661cdf911a35e9e465d18256206a310565fa14b57ca6e07dcff7644f8c215015ad6aa3349a26ad403fdb3688c6f2b61a5b8d2db62a8d4992c4bae2913ca894ecdaec418a440a00082800e318000086201086711c48a224a8c07c1480062ec49cdcac401a0fc62151301c0c83c280301010004120084000846220060661aaa765035dcaef41911cd866e3f7b65662434bd9da4a1c08a3eec9beb15b442d71d6d0279ad114dbf4e4714f4328aef2f27f838196fd7584598910a60b7664596f47266a320edf80d82dc6ea4eeb783f16a9dea06fa1237f27a412b3465b449b62cf65cb09e6b8ddc8d2f95b6c6381711bd83965a9d230bdfd322a5409d249a054cf38d6bd3ec73278b6747c229b3722084c202791166fd79c10d8f573901d1c00728248fc9d6484a3a0233c0207b1f115b7101553c57b42f395dee96baf115ea2eaa35305836c7c479df160bcfc7511b15ce363a28cb8f64b0982383173dc6311065c9e16d9768e6ccf496cefd179180308337d3897eddd56675322dfcf63b897fe7d1413db1e28374fc4e2c464f978b48d02e00cfe1b0063db250520be23ad83a89aadd1fcac762e2d65ad91e7220a50ac719823726bfa992ccaa07ed914dbea5931edfde2999c2b430c121982eb26deef0410688cae482c07dd5014eb0610dcb766930cdf58dba7bf2e46945168aaaa8b3f6a19a065282685612c076484dba95ace8426b896e5c2575a672837461d2bb43ea8092b7a59bf70d9f5f3abc8473fc763eac9b98eeec20525a3d7b78d72bfd7cbbfca49728d0e03ce46175c5150909f9171e8b3dd095a2b67ff8e5a90cdc88f06c35afdc85172565d85fa62bccb041fcd4cfe6602598d46448fe891a1eb490c1aefa205fa0e5ade4dec86a362678344a8156a2a773f3c8516bc78705bcaa9eb7e71702337fd419fbc8e3bcd1271fa9566dfc86ac5588bb4c4b84c73af62e955d733d1496cbe4175ff928d0a207a2c5ecc202b5c60a524e40657c405a55330bb422b8184c8ed8dc579e12174f1a22eb0396d0f6810da817ab1ab1dcbd2a9f18f2ec2cd27dfdb7fa38faecde79493c2d2449fe928af099569d413fb01ce805ee46ca5328dd3cb6b6a6f8e92bc3ae6322836e19293b390aa67339fa9f10dffdcb691e01e7c77bdb0ff109b8b70cc4fb606fc9267caa4235eee74b55ed2df4a315af228b0ee33a264bb9813a7b681e218ce2d56b4fa1020090bb8dac519e41416625d946e85ccebc0b6a2d06885840b3ea70c030f3c801004586f0a8a880476ba5db675252efc2664c2dd1884352593585f8089d4c45cf27f01254b1b9050ed4d5e11d1ed56ab3d9705b83e00505b8c018b86de542c45341d738a9e11422b3842227a708fdbe61e037eaaa6bdba1fbd679c264bb308f9b44b1a4d8ca786d21e24ffb7379c01eb1fe447eef7224311277cd3d052133f355356069cb53fa4ae0c5fbf5f3da7d765abbffd9e5d6d5c1e9f90a093c17145d66761038d1fba75a668606070d03df3c32365337bf258833e67c2f771a5a0c1873d3644c186c23ed25c91535146d9240925ceaaccc0ec5fcb9f486cd221553157026a8296edbf914a911c0d76c56a277bbd5dd9342f965a9f1282accc6e2e9c09f5dae0328bad453880d64da2febe60d953a6750a7de78c19214cb65ad690b4a27138ea28a512a2e66d7c2b748ee56939b2343951910230523cb2bc2be0ed3fa4294c59898cb1a73a61373b661212a9702fa73b5a9c448fa975fdb5c8500f9075968ce88f90352dda0ebb3ce1a4666335d8ac9ab3a65190c75a5e2c4128a9e5db03b10f3abfee704b9acceabf7468d08a7f601b83e4b4cdd11bb138a120e1470baf71cbc31cc025e9bd4e97a157e7b7649e51ea25501a95997b617650e7713324cdeed271cc242412a17cb0d92e541bf31d72c73378484a02c9711ca7a31d728b81ed17aaf278980e4279f3271603964be4f55ade1649e852315348edf19fd9e45035098643d9980583c8fa500b1dc24173b8f0c98fcee3a6dfdc229db3bd8f9680338ccdfac74406a4f052fe39db90dbfe3d3ce54e7fb1cc53949aa7db42a8fe864329ee8b3f5f1747dba6f535d0f4e5fa519710dac4122d4c5f810ac25607047c9970ca4f07a149b1e9c0371846f221d9adf7a5eafad68022379561b4faf505a2abed96c271599b819eccbb10a480945ce1e3c280c74f1cf321c43a3cd39415fc9d80a51fabee0f65301ad797d8a46c4aed15e983c281a5cb9dc825d99e05cc44ac88222b15db22437251b08ca40f326aab7bc46beaf1f919d610d0cec4ceb5135315c8a269f900842ac41a0edfa097720170628b7073a192b0ae58b7000092cb7b1d540737ebbc2b25a1ee21be805daf0f56470a5535437d222bf39b5adf61820b207303b19d79d8734ca11a6ec59b1a11080521878cf22f141a64ed3d965050456b1e42ba5bed21c65c2169e50676a067797ae570b075961f1785d3852fc911260a87acde46449d098af16a4c911ca0288e8b12b621c07f78442a4c80654762e860144b131f4df8ae167370616803508358816416d5769e239a1973d0348891d0e880867f2f8474cb70f050d2ea054733218c6cd7cc9e43b3f04dee51dd3521b221373c3cf248bcdd7c507e0b24cd067bf2d8062160db7c3a85001cc612ab69525221568a404f32b398bc1f7bb16c2a95e8bf15c6092c855c9aa2438235ca83758e664d8ea98814eb7fef6e64ae453202ea7b20863a6a9914f9436003ae067f9b77a8f2a8ab9885173c7366104d28e6c8c8fd831b251a3f72449ea6265d5fc3bfe15249cbd3a8d204bf6af6edcc0b1a09e92cddc0b093e92d43540ac84a49a9c9022ebab632df0d07de6cf2dc8ad720a63a2c0dc574fdc8b81a2421ebca585171c66102ce3bbd8b304b3cd576e69308c08f630b8701541464c68d3ba43676d84e19347883f85aa4713d065015c1832ddf14255a13221cd49f54e52379cf288fc46382a8aa0e39ca0c39ab07fbb8650cd3b98cf1dfd127ba414622c8e4ec446f2846e12797e15a7e41089cfe9ff038d4fc867cee8b17325d51ae7497af257941202d926c40fc50c5bdc470a0e82e8012450b2ece9f07b25640cb28f9c724283c1399120413873c7b933bd5b3da01000574fb65f8edb16a5125d91cd95063bcee6d2035934bef252161bdd7aa9d33522c88bb901ce4913357033019fa4763c196db5666210e9bd44ede9820c9df363832a317629cd823e21c52f13e2427b3f47d200d912c0965969853268ad84427012daf9865bde04c8374b38f834dabacd9fcc31f4414b41f0f3697c3ebb3fecd08cefa623253a973b95a1e92ccaec406c2a137463bc5ca01b2d5b861f3b1ad32953832f21b1f59a2d3dd7ef45e7fec01dd63e7667c7b067530a050ddb3b80ff95acd383060c8ebfb6a302b6c289ffd7f340766bcdcb6b37c0c816564123be407468e0e90254d0644203cf291ff8c7eaf2e44e4f863ad998bd7a10cfd6a1a23707b3f39ab73ae53c7607745be1f635643ac2b4361828e93a2d503682050c85b73a52540f28342e8439a450f0a4f6d500c7666556f47f24b54d92fd5169209bcfdec976a23ad1b60199c99c2f40c31d1071f6dc9d5d7b6aec491c8123574761c4dd0b75b536a9a7db40d85a46fcc360a9931eee6733a1205cb401357fa4204ddba11f3f34ac9f56b9daecf5026ba41ce16873d43dc516f8de67ce19082ec2cb4cb3ae057435a1e36441b3c385469d02ef31d2682d55bcd6882c972b3bafbe582b379cf85b5994d4bf8023e7f35bbbc4c3363fbf66a00abab904c41e04a2cc1aa164d811bb200023f72e49a579b876e0a1872e0ce2b8a5040c5dce73916322cc4748446e85526e5d18dff5da8faf6fc053d5e94fa22907eaa1734c032de85aa49f8b3da76e0aab630168a44c65f7ac79b1d4b1af420b39835acf8049a0ba9830b1c9114e18bcc1fcc905fce2348e6a359d02e0fd20db8cd78bbc62ed72906e14add5e660e81af97b9ebb6d3a0c0876a8886190bcf92a0237c798d83cc97ec929fc4e29f87d957a4477c60e4cc88ac7c108ca997468cd1c42edd1d4ef1b30317a9446aa1b4406fca5b022ed8187d9ae7d45c430fc649bde2c94a0739047693b0160e4e25437b0c0b074fb87b5911d006d24071e8590f5309c861cd5b2552b2e0cd44a2f904fb2afc0c3a6fdfe8cb256ec397f5613d07f5418966f69f561ade10c06694fb33ffa110164604f2be979fb8caa240a799bbffc8bb025d0ba8831b9c429ff9d0159a0e9c41e0579b3ef26bb2064e813698219ff988ae79d28540faaff6273410d8dfd0b87dbe34236f2698417f3e9747f3ed6692fb23ef3e690d9c0f5670f2830806d7b10267dc3e3fd45d719669b4ddf1f8b38084a59e2b062bc342873b4b30077963c6e0fd7dc4cf7c296e520e2883d517fa63d0c9cef59861281ff992ab5c890b6af9ce1f3ae121a277336eff99afd27125b7df82fcfcafc5475a65fd7c37308c67b16772572930fb24f995b0af000461a6b71a1132e1eba710d74fb1138754252c3143473e54fa0872e5b6875730e34ae386ef305113fd87e80017fe05afc40fded7972997cb7011db7d96afaaf85cb48c240d1c21216c5c0861e29f35ecd7f73704d2d4cd8efd17a17fb6510d1e4970837c0c73a6951aac982ea9738ca0a094490a085dee605e34b0734442087c302fc3d481caa4a80038d1301b0de0c1c1168f8cad175655e7090fa387166349dc9f3c3aba8fae2f773ac3273e3cb6abc9a09f77a8b2fe9aa1a9d3ddef039790bf9aeb21d27fe2d50b0da4d15f0442347dc8ef5106867dea37bd64ecf027efbaedcb13d72dcf73c9c79250eed8708459550496dcc356312092ab1eb140198d22842e4386cbf040f883d7c9ecc02ae29ce06a5602c75e173d4b08d6113e64d8fbfc436f026212c0998a2e64b0e13c554a61fe2854864abf82f5351a0050e2714d88d7bce94dcf7beabd1e89e64588f3e0d4593ef48a950582d88d14a26db1e187901bf946c1b5adbbc114e931ac708afa75e78801befdc31e05631e9ab032597aded3ebdff94d7e0814c50aeacb90c0045e19d972ef79e65695bb3a7c460dc08544ded09a82f05a2ba21059a3984c44f189dcaafa296374c1836e063656c3a58b83827724b9987b60e45ef958d24ce479adf051c2085e57390867af7e606e8987459ef422cbd70a0f7c24d42faf5410d4f65b1d6220d97afc9fce741ab969c2d9df67ec6cac5b7de9514ad8591e1d1adb0b3f46aba7cd92bbc777293fd5fbf798eef4ec39ee2c2d38d1891de39427e6c8ee4bbd539e1360f8c1d5130bced59ef9cf828548050386cccd24e03490151be3502bbf33842c49cd1bb2b2fbb1e1e5dfbd87b435b24be61f405f5965fb5931492f850b3664f10a2e4f346ae6ba5ae4c2be86a71150ba31374ede981d8f6205ac63cf3bf6b1b4d21ba465737159c2e7fde5fada3348af348e023b2e42f1b711f9e8342e01559b75880c60b6c4234cba9f5f6598a1008aac3b2614bdd751af0208c0ba9f5e402f912d46926f684e8a6cfaf6a0322ad9d9a49f3b90a7988d7b4b0205a619995957149f6f6797f4245e5e0df89ab53ca6a2cf62d3eac7c5c559228a9d05ad1902630fa60c1306f9e183c1285fadcedaad9d6a15978eeb56e1c11eb31d3b6147417b99a7abcba82c88eb98823549aa5192b9a78132c50571cf1d5d258cb147dc410e5b10d8547b5665117d4bcc15ad664c138c7b671988ca82a2713bdcb7f57c53651a4eaf527a717144a77831b62865a106c10dd0a35ce27a69ae015ddc9105f1e517b104856fc9887a8a9d888096b959929b7606d39b14974131a285d6bbf44bab7162384ad8008636532ff148ce1dd2f5d5969714c0c47199993b05d52e63f12f96d3aac69822fd24a7bb5473ace810f70998b579c51debd1f587679a86298fa84ad15ac22a630f12da4ed9c986a9b75fb27c5def2c0be592eecf35240168e268ffc6bceb0ade694de6e7f56f3ced94f3637d426fd65ef542693a4e4d886357ff92b75cbeffde2b55d069d34820f4e92dcb024cd53724a33050fa232ee7746b6a8d4694fbe1c1540a8a3a3adb3412a4667d15c2837657085dde29f614d451032a5ace750633701ca6cfb0a36fc16a5d3054a1234fa0d5c827ddb6bbd4b26c2da7ae29469e64c5eb404be57aaa55bf86d75c016ed8f72d07d2219cb584ad3983d851fb20bc75c1a012b9d27dc3cfbc25456a273d9ecee32aa5536e4002311f8614bf4aca250b9bac9398a59fea92947d405cdac42a2caefb243fa95fecca2ab84af150e42170139bafd17ca55c744ed421dfee09a9aa4c6900ede48a73a77d5ae6a509e715f7d5f13c76b7e40ee93cc23a87969846a1d7d901b98bd6ea2c9cca7e85fbb612ff881ca57d7af06c18efbad585d7cb915d21e35758a28484dc0552a9c1324c3a0905d9954c32fa3326cd49d0294060012e72ce8d0e16cdd425c3a4e77a855167a3429ee141ca30694b0a6e1e66141053340f264840aaa069ecc57ee3dc157a813bcf53886098e28c6b4b98f68d56986b090a34881b2453e77e1f0611a36da4b03bc6d7c0582d59606d98fde0cbb5fe1d5b268b43c830d5483f74581866b0bd1834aef89427e9a3a3ed909e31107840836e252077f6c34f9d08d09acac6059005b700850044b9171334a278aef02e13c18491299b31b92440ab2f063dfb86c71860f56e26a6959fac8d1f757009744716ab4264aba3061e2880a98c9f680eca3b3729f82e54ba15004a9c3be65b26002a11843482c07cfbae2c94f6119fc065108610b0a2c81031d4718a30c5a9ac9e12bb1e0ee019ee7f5450aa5b3e32dc0988d5b180e2811c87a66bf4ffcdf7acb4116b23e8d75131ac94b3cc297a3a31503a8b02a05b93cf5bbee87d85b953aa302dd74899c9678b016b46236999a4c5e216cc59ca8299d54a2418972329997ca53a783f7eb97670b734800fa9d0c311a9e55ca077ef057be69d755f0a51ffd0ef00e54d98be913609d9ae7e194f2110d884bf6946f4b80e8bb3abb033356f324b4a9f8e17bfbcb10b525368183ee35e8810431cfc9c139c39d4d53bfe98d829c82dbeeb99bd07d94a0730dfb438f602bee19b33f4db8088fdc1bb710310be3f208f99bcab20568e05342cb6e7210ae9a36a69895296c344e9d131818b03636677985b0af03042f81d3bac0b0ce89d9202fc626e8a24ca8d6e610e01d5b5bd987a60bfc3f70ab51802e044976de6b09808234e9a0c82685c7d673af1a2249c8a4b515528266bd5296675021c310a4ecf73a04b7c78ff01ff20403e42a1a631bf061589fde19a56b6fcf58f06747c8408ca85c7c94dbb589442b1c708d0cc73439286a11b7e7f9740412332b112a76f168399eb7919916cff84b89e71cf97fb94b0695980eb7cd03867205e3999f2f87f83dbb63cd8617e3b877180503262a06324c909982406cab9fc6e37536a7afcba7edcc5947a625ab3a43865ab2705ee0bc59cc18bcd94763c02329e0b0a286901ec17e6e3a708106dfecd885756043fdec4e1db5802b956000715f58161e6a123ef54bc668dfb15cc10ed4f76133f94f05698dd955d40c3a821169d64252aa032310c6eac00847890e9c1e84ebd0c3b22a1a34040cb4419db11717c1f296313bfd76490b17bdf8c5606fa900b5f0511105d37270916c004f00df41c82d6b99f837ccec71cf89494aba8c603a0b40747e83719eb8ceaef2e17b6ffc3af5cec38ac9241ebc9435e37d0761bf90b54b956542cd253d7081ab0b11f126dc885d0114db905337f57320b56f61d927a6858c72d5488fe2b110b8d37da07fc90dcf68f069ee7ebcc31a8e3b7ae0abb27adff6c9597c7e9b7f932106718f05357deb22a16d78270f37c73b38487843d509aeb0c8d8dde96e2d18bf615d2bcf1869dff3aa824faa621c33bad8260c1cf1fe5311cdc66304a945640037c098741580b6405805067afd27743a957f3cf3e9567348de97e7453ac79f9a68490cd6447e35600a5f838778fe286a08446ed62f36babae4d6617e2633cef833ad9fee7ee7b229adfd939ac29a2320d2ec0b0d3f054069ed9ce2b03b456d75838dff67112ec3b4fdd736eec4e4808125a32a1b4a0568ba353f58de455c5abadfeaa74ca0ea9980a349119627db4ac393120051c15f236f7bee14626aa7e7cde61d6b3b592b98ed287a1a378ab82ceb82afdc440ea25d57c17888cc7eeee9a22a2c6d35ccc5c2541d411e2a913591c208c0cc0bdcca8eab7b088cbcb5c3866d9e73f1e54e55fee21bed5cd0892cd9466bce47a6ac5841a93ea916c1cc9d5d81da9aa210becadd325775481326350e4a93117447578207a84b8d444fe676054019cc199281b03d44a35fa6958a80908c96adf5272c973d094309153333503ffdb4b08c0c42e4e3f0f7505b6a98f456c0c5e85e9ca965ab2b0ca00c8086552aab371530d787a7c5e09c1f053c44cf5f125dc0138c89c86e6dfa9bfc9b00c860a227869f2fbca3306de64098bed71dc15b6be18cf125fe41334423b2903ac26e5c31892cb64d32e7a88f474448e28236f74df2bacefbd585a06d41389abbc541ec54c1745fd9d4cd5fc6c4561f048377055de56340bd5af104ce331c126cb6a05e394faa4d91a3b26596f5739f406ae129e7e271b84bd74f30bf4fa74bd1d04f3d167960b47668142232cbb9569f30d60942b99cddc1b906c37c8bc883c0b37f0480589aa22ec4e381727360e819a36b5943804ecbd6d6648dd14ae908e10556ac4d5a7e71922f1bdac47753edaddf1c3aa9d0f66152a3ec60d89361fce0fce12ee94f59e6edae1b3b399b101d0eda0a4e5298f29cb94fa2cc665e2db8b5ed78251167e42a4ceeb27fedc22ec390f9c80c3fbf838d61bacbcff2bb03a06a9c17cad4bd87cd500f2e3013b450535f95e90e521ef174c7571bddb1ad2d47bc9adf2cc09da90b49d83620c1d6e4a3fa421f8fc9689527a503ded912de684f26001ba970f590f85e8f9c007224069bc2d6a692ee5ac6a162360ba818287ee85cbc4a7620dca72a288f8a883b81232a294deda21873bd210860b4a55c0ff168ae901ec24bc957bb97a091c56cbffe26d6bd02b9f56e99951a55f019739632ed6df467fc7e5d8ee34e253e383df569436983b05c148762d64713726202813a74a63a282b7474cc42b6bc9a3a5abe6874d38155115abb4fd7dca8215843f6e508e90a71a959ee308b6571dc47c36c1389023f56dd97fbb0700ddc1a3f3e5d3aff3a8a15e62e97c1016a060429d79304bbced7ba80542e652089f73d1e4d190244eba33c7800222013dc6348b4758b09220e6adb7fff3ac291a464dd5352118e3110e4ae3a294ac5b8b58e7f2cbfe1bcff865f95b421a63d764e1d4edf08e04bc24fc3a83478ce01628729307ebcedfc56fbece3d62fb9cbe952bcb5ebfcc08a3063865b601f047b18dbed042ba200505c1875639891b3145355b28fdcf93aa4ab71018486ffdcf6c4a967a780436b4d0f08ba907866bfcc44b1a618f131dbede4cf21bf023d437b72b88b3889b12784335628901f174117659a075b2bc04850a6dc8beac8910a02670d0a913e21313cd00164a249f3a676d7c9fa34678ea4f0bd0c175a8f6e8cd5d1e70004e8c27c56f7d26ced41160f8241b5881921734f5a9c3cf2f0115b37fdbfa38caf760080008e91ebd008db8009644f70eb5f59612487232ee621059801696cdbefb0126652794c02b640e2627de0b88c2c0a609ff3a23693567c4877b44b9dbb1f50c311802209569eb9a0ad236a6c2c49af8a8a91a04934f66609c4ab66271998888edf01b9621d78b176a068e70e1286925a2d0adaa9b9a7e125c209fb0c3c0117f2b0e8230e759608a50fb4a6fc7cc2ad5ec283a994234c1498ce4f62d180d4db116e27030f98580316191b29bd920baf42f2599f49df2213d462a63970a07a5196a5977bb204c4d6751f49e7c107c9728343b4085d6693ad470a35b2f529e0c6e67c0cfb263820d15ee50ea29364f3e1b0f700ae1c75bafae4440774e850ca5c7afadb38d652a48bbd9a6ba17bc8b01711be063ae1ee84469153e65ab149645e634ffe9711c0fe8bcecc80b556989de96ce04d1018a03060c1cddd06cbcac002c60d308d36c46dc5f2cc24bc1b29092ba283189c2b621c6d9bee715d33671a1bd61808a9eed32579725a08db5394cc8eb6a1580077a794c21cacb174bde2ab2b897c1854949d69b077f3c1b2ea265a51d4ccd3ad6833749014026fe92ae18189ffa48ed8e4d52ce983370c99e48069e86f3771d17d5d1223b70b93ef5b59c3277a8df58a79e442f5a9da5867580cdebd76bb6cc9e8e6fbca06d08f87a71c004449187a466055e254d89f1b4747d392883eee99f22319cc56229e22fc9814ff0be88f4a1a1eee88f57417869718189605e2b945735eb274e42ad15708403b0091e5bac93dfff545365f7c6fee56ac2d3587f9bd79c39e8726ffe6799e08541731d4c856bf08f62248806947cc4a4aa953db27c6572af45a854ef374fb1afffd630bd881b2a099d25177b682d9ca12ee62e847143c83dc467b75b1d5c3047db034a8f5970d79259ab05cf26edd8f394de89b6129c2ca2a5095756c3674991264df4d6bd0a20b046632f098691a2ae6ac39d74b958767e17bd853495aed6fec17f1327bf002730ff1bbb0da0160fa2f9cfde1ca068cc70dd9b9908f0d330667a8fb538c802d8ddec8bcd342ce11827c8d43723617764296fbee61353f77d5a9da5419a15710bf820a50856dfdda49d082743142ad9f8d881a0f76d42578cfcbd58752f2e8a327edf6ed16fac59024f14c37c3dc80427261447ae2850ab45121feb91ee8399d5835d9145a90ff081f1c2f07a5382e4399fd144ede122baf4c68650c74c21748c2352a22e2299a6458be912f3f1c10a81e4a20c913c6e4f5654f393afc026189004497436fcbeb66095a6118d003831e58e00f10d6b5842bd2ebc736224113d9bc6a494a6571be6a89a67d01bb3a5c478e4fd533913146690860ec2869a84ea29199a0d288558a9dc8867988445bad6da28386eba43662717e174b8e9575aabcbec8ec9dfe1acedb4f4b200de5559dba7be27ac5b1c5cd0ae014efe5d19526592c09fd3b2a3e8310339493a7ced27d494b2dc1633d36de6aeff35f32794f800bba3fd4b87a9612986d9837315330decff9ad169970be87aebb4d94352f626dffda9d936bb4d9fe7083e41dde0d5f9980b3930aeaee584475c3b90ad44e3bb5aa895b5f6ddf289ec8a3dc75cb405f03dbcff82f590635ead3c84fe00b63ba77165b1fa41b0259d0a0ba9c5f1e76c71fa5509b06b8bd5ccac04bd20cd2d3d5233513393da03cf253fc5ca3d510f5c51250fbaf29eb2ee9cf9bcd4584a45997fe4dc4ac791650b4ff49ef0a4163964a3d13a709665876fbf0422e0baa85e39c10b2a84b2109fba88cffa6bc07a125a6b52b04ac68596af2099e84f119a2e2ca0cd0dbcb52da455965b12e325ea50131e295617cefc671e152780c3a8cc96bdf607b2c6f5184eea003445d013fd2cd6886f7dbb552a36bd37e3e8d5dc0fdb13f0fd1eafaa6c0b37611cab8a9cc4eb1076fe8ee594243123266e6eec3709e81c6f07c245c09ba572d5072d16d98394addc09c85d09f18b4902c47c8728bed90f8c4c41772e7d13505f2102496c0c43166f0e8c90c87c044652b75e9ced6f558ec1662258090be57b18fa10c716ca4e6654d9d5c88e21a7a272b1481c2f6d64576d06a8a3297676845b89ea895a8e691e562fd22989e74c723972b0888ca1bbceffff7c19137f943a28f8241b68c99db2a723c494c00fd7b3fa6eb029c725a4918039e32a89235e2ee5ea5813293ebe98be91ee97dd8bfeef4c66041028b114e6578b9c058c9d1328ea4b4aaa16e0d3018e0da263cf348d8b0580a86910bc2d3289012108b6bfba3252d02d6dbf00f4b552d044a743054c2ba6113f835ca1b7fe149e1618a4894114c5bfb518a86d9f00859dfbaf685f88d10955d673572bdb824fa204b47cb31e0895ffc0dc5e42f892a5f49019378b941824d07f1099835cbf1da09daf4932aa3099fd5ca1d6aea52af7c5c09202322a81fa4bfc87611bed546bbfa67522ceeae5023f22af2a923149aa5ade0d5adbc365bb16e13b534d7151bd1c869241e5fab4c111df0a3a3fbbab8a8a7ad35cc35e53b4935f8a3292b43cd384c586d133b2ca7818c228ed04b433af5b0feaee785a5e5aadd540fb142e785518f9f5cd5cdd0ee3165aafccaed106f0fb187262d2a96c8e572dc45a790a45aeac3c9ecc876f6658986f9977d7f8a0856ef5d8fdcc2ce85a49eecb6032a21ba655edb0230681e7274e6bffdfd0a99bfc7b0e9b819959187c62a5c6bbad72386172e0d1a3c01ca024a19b9197bd045512ec297c343e505e3465a9445ce5c149469ea6097b5c5d23d19a7a94b19e561856244c05f61cedd2cf9c1642b0bef9d2a089039a6c54b2aa95e1c481e7057bfa114074b001bad110d90057ba64559f2e1395b69c902d7c932c0b6f07d33f7fb7399a9bc72cb89696e3311282c39c76796e3ac1cc42c92c22fcb8c8d8878829f400d0aaac36bf84bc614281f7cfd6616884d174eeae97c503d2d2bf8e4e5954bc962866b6896389c6632b5a4197e984f1dc82fd739f03267b10bbd5d756f7e084da5aa2e9076c93fb3525bd6ebd6178331f434238ccda3161f5677c3896591aeff45cf4d0712a485eceea53cc1401ad7b3127efa8890c3dbd58a487da74e816d606030300aa3138f8b349af15c273ce5358a343a86693cf91dfcf467a1c14ae25df7ab5b0888be7ae3ea82b72fa273edf5767f0b45e7ef5da6fa05b5e7da4de77e0485bf9ab7fb3efddb783c414dd22530854895495909f9b2f115bb4428015df85b1089e14711aa071cd16817d058e1391aeb06388d313b3fdf6887aea6acc9c993d1e358193bf30d349e55239e4dd94b5f080611ec07db1e388c81e547fff912a645e03ba9d26855b301a12a7037d80cc12b952f777b4f6005764d25bb7b8c90b4b32c30969fcecb471787784242c19008d92a243175d9f7b01101ef478e1201ed781820daa21fc03cffe2a22d1067fb16fb27b4a346a2224a4edbdf796524a9992940134080108980836ae0ac2c2c969b9563e602ee8d6c630d8e66f3a1a9c4ee4f9d289397f077f41f939e79c27f3ce6c9264ce86bdd25feeb3ee49a13779a23ac2a499f97366fee4396958c0cfa05c357fce393fd4e49c7eafa37cce594923d4e9b5bf29fdf6fda5ea37439a6fe34d0f6435e7b7f8cbe827e969b4277d3fe9ab49d2a5dfbee6f4da0359c178a9feadbf6f5b9fc7cbda9e73ce39e79c73bead1d95a860489e4f3349af3d0de9b5af26497ff6d5989e94fdf6d58cfede2433f84fde8f243f4c5faaff4bde8fd197ba81f4dba7faa349d1d16b9fa219bdf6a3d44afb6dce39e79c73dec913b4459550ce4819e3e1038c67c70e367d8ee1168a67ffc4bb7ff2b4377999c8dbbee4859ee47528272fae4ca41d3b3c403b8078f4a783fc68623a480c81bb9be9118bf9e8c123f6ffffee233549a31efda2d7d130dadbbfc9fe9246ef2f83bf8cbe54e327d91f3d8dfdd1777f34f77e8d16c69e9a2434a3bf5f8dfdd1d7dc277dd892dd5f6e92f403715749e6c7dc9ecdb24bcd984ca7af5fcde84ddffdd2d3dc2f697f9f7e34f64b5ffa6ab2b7f56b485f5ad194be66f4a5d575d5cd2c50813fbb1288641b1285c4cc0de99b718545dfcd4df6f67dc858383921e9e201aac45eceee50e0cafe50e0e27e154349e55031c0fd5bdc2e073e81c3916be43f4242be57060efa74b2ffa7d32ef567f4bf9dcc28270658e6be30577f239c764931c0b3ff286754241c1d19c5efdf6827d3bf75ed5ae54fd912066583b5450eed4e86fec8e5231efff9e1c895bda5416ff95c350723147640abfcdb93239e179280474ab2ff4849bb8477e52e1a5ce51fca568843d4a3c0649467fa93edb1a73d8ad73dc9e3bee489fea6e6ea04c58b2b921757a58d0677ff95fb0e57f937eaf94650e800d3ec63e6151d60cf3e66fe86c0f3b76f081cfeccc75820b2b0106117902a9f5ed8e29d0522cb0ce0102c9f05b00996f7628c42fd83608ef7461fa979b31efd3efae987435ce16f971e38c4d5cdcdf6a6bfd1fef4fda5e63773b3f707fb4f6054ee97ea2f7583e9519ec6f4281f82a6d3bbfbcdee2d20ac1d1063fafd1f105f2c1055fe1ec1e872631836fde96364a9f667838df238c415ca9fd4d464bf7dcc3417c5ab4942b53f7d3f487fbf667bd3f72309fd1fa5cfde7a377b9ad297c51f89bcb81a79127f3737a2bfe9fe66fbec6f427fc3eda0794136222fae7660aec117b818bd688b30f82877f724fca594536c29f93177cbcf31764048306e4c810a1255c21858b9ff37299b9e00fba49d0c366db3da9f6c5ff7de2ccb3a99ed374ddbb6ad9309fdc6715c37c3cc5c27d3fdd67534c45d91a89311fd8639adcb7e1bc58e3e2389567b338db4c92e24c2a31a9ecc5bddae27f3b679326feec9bc759eccdb860a02cbdf70d763fb01ac49ee21beccb1b301a544ac058617f1c55b41c0c28ae8e23c36fe72faf8fe135f503ebec3fc3546eef84fc4ff894518892fdee32aaf421122d1c5834810c1fed087e4f84e44749936393146fbf88e136358316683b5a0fce52689e84b2f55c6188cbd7872ca5d88c35c89db4edc14794850dfd5faa11354adb5d6aff56badb5d6596bfd5aeb11b87a32c7486bc7b92769e84e4fe6cc93f97a32c72ce2dcf32cf290a07ce72139a17c0770b9ea004e1c008a00e4ec3bc04e8345c035c8e5571b5650c7b20ccafd1ba5eecdfc84c5d13ef3e2cd4460e0f08335cd28debf5c65bdef892cfdde32a2bf5fc94bf138cffa204fe48978211fc4f378632318a7c8911557832b3ad76e1911445270b89f0fd62e7549bb50cdc30d5aee87736fcbaec8d23a42a78808f62481139e48e1a15e0a4e4eca91065d4838b15d13c7659187c4f4d6fb576469d363f15644b01f871559da4811224388e8796289238a40a5e0b82a2527e5488a8ef67dceb581e2c1b2e63273b2732128b2687f3d17629145fb4d73610a51a5bd5646d65e8b4204b5d77e9240bbda4a4ed6342259fbf0354dc35a0b41b2a6fd89f7c55ca57de67d410d6a1ff23e260d6adf693fbd6f4983da53ed4ba355da6b2ddc644dc3c9e10aece7e5aa959d15d7ca122b45ace844f09f5869d93bb56ca5853fd807b3bf698f845252ac41d7d9389ddc1365144f15530145963ea9986415183bae566469d5122a245447a8745445a8605af45891a5503db1d25a71b9aaa5d3e04e834e4091d3200ae58fb32f9192d2c9681ff2621679325311121c5f874715ce7aff13592c17af85fdeb7d4e64b1d6ab60df83ee17be91dc6feddb6bb115d911c95afb27d65a6b6d4e0e577e5e3d2bad1557645939225c69adb446797a1f5083b006bfaf93b1ff7533cc8a0a82c30f66a39b010a4edbd9b2ed53b90a70448ccac882b96c8be107fae39449b6b95ee0dc2b699e8ac753ed984c27aa27544ea896f05ceb17aa8ec8fd1dfa18b743fff195d70a142b4a4ebc959d159e15241a5cd14941f15272525288443008d125a548988293c2adb0602f66557d1118385c69ddb021f2909c5ef3bee53d4e64e9d3ff91dcdf6f2482fdfd45727f3f9108bec690e29ff05079899fdc47f49fbc941c2f05c74b6935d8293a2948e4ee97f7fb1c1c40b9dba5d2899352c40068581c402cd443727aec49d40c89bc0170eea5f28683738f85dc79484e4882fceaf4e116cebd1ad93d94bc595bb5149c06bb56510df0fceaf1605d29395927b3d2120981a75d260d69cc5d7370ee99f2e6b2a2d3c9acb45e38035061d47533dc6cbf149c34fc05078fabfa7bee0bffa070c0542ed511f1e58da4e0acb452708c182962fb565a292bad149c2572b8d2729e1caeb452705c3a069032fdde0055c0e1015c26efba4ac632fdc20fca1d1a9130ca4fc9e4a820987edf12fb3931867effffc498edfbdf487c71018c0f46848891d117ba9122445fe845f811394cc149c1f960a78e01a34c3f1780ee4e4a2bbb28a0ecd6ec06ef3de20b0c8f6f7581f34404911b6604b9633f0462e8228bdc601072b7f8e47e8f8710264a20bc78f8b024f7d7dc4856bb441f2cca90ad500ec9cd630412f44f8f9fc2a696a15da80b7213c9618422f70378d824a7801b45ee2a72d3910f54f5eb2a97cd93bb7164712fc6bc68bf7a299a3bc31e3cbb7f37f48a7e51a52710ef0c678bb33d7ab48aeccdb48d83c9f136a2080887332463f4281767f449b9e6388ee324c7452d1402b9c1a974608d287038a7b748241289a44814b95027133fd387bb50cbd0ed62d7755d160a8542a04824f270c418e30d638cb778ba27addb4222aec3200871d10b678ea4de7e46245249498e7f8a27289d0c55d2205843a24e80e36fa24e66633512a9a3b754766da5a9d44ca5bc53299296912e89446a9224915a9e9821860e175028725f46557c7947344a00c9f265430427ef953166342023cb2f6cb1f111157998d8a24a0b5b609752c618d365df9d8cb689b6c0f363143dd9fef41ae708042e569053fd72069c53fd22e9f9985fbf451eca06e7a9802d0dc22cc033f570b38f1e7046892cfea788a33fec0181c3f8731b741ea411617e0513213ab1a5a7edda747a393cf0071c02c9314629bbbdf8c993eb1b32626afc4d1b12856a5109017f0e5bc0f9a9b47ce73a05cfbdf7deeb82699dc04830130cc6c2c92179f74b788e304ccbc503c3b303762fc628d43f08ca7b3146a1fe41b0f14847db8a47a39208bfb55b4bebd1c0006fd2a76eb09fbd0ef5473fbb99fb239ad8e06cb0bb991bef07fefa76f436cbe6d78c68ee93be1afca91b466f9f668479b8299ca08c4a279c73ffc4b36ff2b41f79d99f36ec752858de3bbf1fc0b7000c760118ef29d32355e7df92e8fd55447844123dcd7dd1f78bece31a2d6cc9d77ee439bdf54c5fc292256d44f74f4f73ff24fafbf5a3b1d4e380ccf3b76b5333a298bf991b7b336de3ba101544a352a896e82dc947f588c57cf8e8667f539f34baef6fe3fe083dfed17d1afbf7fb6ffdd25743fa7e84fe7efd9aecbb3f92d0841eff8fd28f42a9fed0df4fd184be547fa4cf3e84b1a3547f8a26f43990bef4fd48723ff435f6b3cf21f4a42755dca39087a9d4526062c5a8efe04c1b1c0ff298c3fce7b6e7b88be6e484de5deef21de7f11e11ceb4f1ce01ea1d9c1c7705951193990d2ce8878707c68f7097bb5c07ef47b8cb5d2d030cc86d2f71ee45792e775bef47bbd0973b274b58b7fcc7b57d992757eef217978ba777bc7b7678329999ecdde53d2597bb7cc75fb0c3fc853bc8551d7395bf07e93c67b9e7c4c28e0dc1a1bbda89767128da256c26d97dc7795c9a9badea9730a098bf6c5d1096309bc3de79dff197ff38cc63ee0255dcf526d6254d21b9f60b265ffdd3b8258be5f4db3acff4cd62b16ee3e4e46e752f67f9eba759f3eb207dd3acee59fe127219720f23cc65638f8e26a259cd6a157413d1ac662520c360f6ce47aab91ef47dd02fd5fff317e0c2a3877e691b57f96b5fba614c83067fd9be739427fa1bed4d2c7f912eff66b82fd5cf3d9015f734d973a22ffbb4cf3e99e32a7fed932c57f90359b58dbf34ebf3eae148f24ced598bc46a568cc1f117ce5d9afcf18ff225815ce5dffe2114113d24a75dc216d2acc611d22efdc5eadd06b7b9922cedb7f7972ec9d3923b2bc9f297b8c2c969b97678b2ecc12ffad077dc6f1ad8389dd3ad7635abe546b3401207a436e907d23aa055f27452328d48a1fa274f54f2ae16f2381b6a29edf7039672b6fb7ffd9bfba2917d7f8b55da258a3e34aa6f9fe6ade8f157735ff435d9d7eaee363baa05837836e800194307b15f9fc67ec5f5455f4d68e3b4ec6ffd5bedcdb48debba1db0d166c6e661a061a20ee904ca6be6e434329170f628de3de97ee4718f3dd193bc908e3e99482dd70e1a16d0cda02c00835d0085fa07c1961618180bf4b7c3dddd1d266e5f6b45655ffa9bfbdab6997c7b9a6c337d35daf783f4a5ef47921fa5df3e73984c7ad3cf493d0cc8183a884cea869393d29f7c4d7a53ca7ee9b72779a9fa299b22d5ef86d26fafc3f6251db427452e763b264cf274566c2d3aaf191d787274e0bc462f9417f79241248bf428aff4289ee947dee94fbadf3cfc5a67231d38393d3a7a66ea0793e3d5326f78bd708891c5e73713593cd6bf2fbb1ef3ef8743d7a37ea9d6486ffa9bd29f7e7ea9fe66b46c8b9d3d0de9b3efcf4abf7d35a72f7d8de9493897bc548bbcee39fb45107960f097aabfbda67daa6acf7df69bac1ff252f7b70f7de7a56ed83ef4a9fbe5b07de839eebb1eb27eedc5e9f5884d9a34b9420f99ebd7a3e7899784fe69fbb8e2e8671f57242f45bf1bfc37a28f3ab8ca5f84232c76219b99972b1483538c491b96c421ddd08967ffe4656f1279da87bcee6f8aeae8fb7a548f58cc470f1e31169c86eacf39ede3bfc97ef4fda5e83713fa9881ac5afca5fe0d859ec67ee8fb43a4af66f4c51afc4d9128f43737f7ebdb9b719dabf2b8d3fb0ec1077f912bd4ab6786a7461029e824082568245d353ced515ef6271ef7246ffb91172a79f84f9e4847d7f0e20a85729ae1e9f999f9f141870c527e20b23808a4caefc51885fa07c10f4416f721b2380f0d0dde8b310af50f8227d3d7f7fa5d3f1ce26af4cfbde6af72b36f31bdff562ac90d66e6100646c6d0413c8499210c0e7175fa526dfa52ad75b6594e76a999d39bde763d6c90b93e0e7155b2dd4ce9e4994cdf6fca1ee5abd93efb1a6ea634a1c8dea3034ddca6ff8a0e70e71bb4377d0ed9973e7633a7df7638954c2cbc300f1e82ec871168a6664e6f7a9ad39ba2d39cbce94d5f0d7655bdfe72faaa79da5b943f3d0dca9fbe9a2416659a5e7b1a9356ca3eae48a3efe6066b2f032db4a0a2e2820b2b2da87c5f0b2da8b8e0c2caca0b2f80a617e40b2fa8acb8f0c20aa85a79e1051054a96080410002b801822a150c30084000376eb0b00c60000420000c2c0260c151a3a7468f9693e76ff25e8c51a87f20f9a97670d839332701d15561e3e4c9ca01bab873624c97a78c0105e12012c855f3bfc0a1c772ee117098056553d04610b520b246c4eb68e97596f36774682451357f0757cdaf2247f103d444b58cc13f02c918fcf398c411468ca19f931159266a07587e8c3174e0664a190000207038f3e790d7ab1ea81160ff6e24f145eee420cf5622cffe4114797e8c5504b96afe9dd89b2d57cd17793bf32738479f7d3b3b248c472814fe9f2fbf99e3aaf92a25b2a019b3207f91afa8920f82934effb95f88b3a03c652ccbd772b2e6b2d8a28a5b2ae5c493ab3de4f9218df172f929dd414404153ffa9c4e3bc617fbd24a4ae7f6826d8bedb243b6b36b9d3de4f81dbdd0c403ca7efd707eaa866963635383ac61a2b20eb75e48bf739d1fbd90b3f6a7fdbcde0097e9c7039763e5c210dbe9b472d7bd9066ff9a85598e1be71e17ef45c2de3ba91137f39ff3a9b5d66d0edb19a16d3e3062e39c78ae9b761ad1c5d0e47e9b775223f0a51f3711be5c0d6e04d68488d727757b39d734ad9dbaeb7455fa5baf5f75ae93a9b4e6d0e9424174422219bc88e3ac0d1d7c4df273f9cd06fdbb9bc7ae069dd2964575794f94ca401d26ce8f76d607d4b78fd2d91082706ac0fda4b3a13efd5357833ff55396c33fdce2b306816f8091ea584083fd1a12acd260ffd33a4da5926603b555eba4d16833e1e881ec5e3aa77d5167837ffd5057c3ecac06e887efcdece5b64e0801f8bd576be2038511c07097a30f933272f83a58cbd18749189995d7874917f9de7b3f150ffcfb8141ae1ac5b028471f262f921018e7e8c384e7c11d1f262c7a0339e59453727e31973b19ff9ef3eb4254844be7bc9dccac33bb54073fe79cd3dd7dce39dddddda7bbcb3967cf39e79c73ced931aac06f81093ae79c3572fca2ec24a2e6452cf2c81de99238b1514be025b094ddb2c5007bcaef066292fb652c94414ff821876d933bec1b2161b31aa791b81fca1d27a0d882093b20920407645481d22087b2078c17e450be2416b95fae922dbb6710cbc8dd5d6389110ca115860e84b033831c4a1c2032e4911ce5fbfb0b9022c408e2ea990117341062d54680503a021a3f000da1e70541abcec9ed0421b99b0a1b3672a802c60c60004349182e08e38b1860c85a460151c1c5cbd9d74e269301461a8143540442128ae410e52fa817902bdfbfffe17c1bd3fbf9edf2a18d8c72c214d95a2ba9c0739d800343ce010efb0543d9d8d8fcc033bdeb2f0d837df19373642f02870d6b01bb2470d8b07eb54b88026a17984de774ac3b43f6c401c3e1cc12c716389caea385b858c27cd1836f540284ce0ca02081094c000595c11639424416be2f8e64fa35eeb715b46082236be67cbe5041139ac8f4513a198aad00bb61813c29c212a04c2f6a01333126a200d0032904210518558861831874e143ba1165941e02514896ac20d39fa64e865e2dd349354a29c5f43113204760106362843544092d56f4c176a13078d9104129a55d17b4d4c9d018d3d58569235441400e6fe49021cf5884f95d782105ec064a86c0aaf082065e9491e7b7c02c21499eafc3c687094590992fd809a40b9e5dec20d3177d2f6b73e4d046a892c31b5901408079fec538c8330966e4f9df2ef3c1227c9450469e1f8201819860490c709e604209a610062d82e30a1c8259ce49844c9f3e77051738b091e76f375e100182c3f7e20a042430c9f4332af8200106795631554e406325cfb74c2802abc6d71648327dfaf96c51822fcf9fb8c69e3c9550818416d7a0bcd83b993a804cb3c85415051c5e99a58f163088c17702e19b6519125c8c47244386ab8a92cc1838be8414c7a8584eac2513c3289f2c9ab09065592685205260f96d9f1f226d7ac944e107a39804f17902d0c6d336bb61fa028f72f4f149721a820f72f82ccce5e8e323c54914f096a38f8f114e6660941c7d7c60251de09ba38f0f1a274ff8f83459819214d8f004d8ccd1e70942e68ec81f097d24b19f6b1da404da66153f9bf93107d964453f9bf9a57290abf97235bf9a2438c4d50f11c425abf9f2e7671397ace48c7c1d2dcff0c9e2488e3e590cc9e1f693e5dfc8d1e708401996e54cf5648242617cf1ebe38b167ba130d7115f6e8fab821a8cb916bb5f88e947b33324c83f119420cf0f3b48a69167125804e11338fda24bca5d2fe52f73dda6d3f03ae8076f5244957c2bb27cf941307f892ba058d0ed6101c8512adf2008b6f427034c83292883e336d696d32ea1ecd95afe226de0da5838ed127d8e6093e56f45daa5cbf2b723ede292bf39d12e3d9247656365f9d3658fcecbf583046c092026624dc820276cda256c34821ca15d08c9f2e58d2247116394c850f6482e96a8e450f6c832641a32e450f6081992e50b72d845b21422877d44d36e4f4cfe2c60d321a45d2eebe2b02eebb22eebb22ece65b55c7747f63840eb64faa3413eedc2dad8201406a3e9fe66e89176a12deaa22ddaa22dda5ab976787ae88b02ae43a311635034c0a9d82090d58cf61b0c64789ad75f486318b03bc2e6c9af1e540f2aa87964f764d93f59364f960d94e5ed7195e4a1ee00d4048f1e6a7c8841597e3311633610999037e044a6f02fb524b3206d84cc2041b2fc5e22c6f86fa087376992e9f7f02669641ee2923c95b0b0c851891058c0725432042cdf9e76894a583c59fe55d22ea32cff6649ae91a39212d864d93da07f03feb3b34136c9f4db807f5f5c92fdfbfddb40c74c9764eac92192d520ab484b1c1c24728ec8d6912dd972b99048c99d2c9d6850beec2fc45942d1a0ecc952bef40dd6c208c4e022fa94313e0a85845676cebd236e245bfca812a51370e4d1a28c2c6d5ee40b6bc0e50872fca2d781698328c7772f041213ce4fd5a0e5f9f510e5f8318bdc92c3d82487ee456906962f73ec037cc6d93f544fd1af12c2941246061eed22bfc6b118a3cafd32c75f02f4094d343144674e5b6913d356da44134d58269af34999a095896a55d0b2fe76de6c3abdb63241fdda3a99b8191399c684e6ee844d25c66ced5af499514e25c6dc7b6fc41a63cad755ade2f7460e15d972a3c11cad6a15883f01a880d55616960055688e7f3146bd806064e9075562ccb5a1979537091e483484b4c8a0848b215c750613f2fc7ef101164b204308364f4c81b6e08c2372016ecb6c023d2305a106a0e08c1764b0338e740d38410f2aa725bf5541cb0f9dc5eb0b9c21812406d696984af0120345c8ab0a5a5801841578053e6814f1b1020b208831c4106230f1a080232063f422463072c728e408349d07b87d06d924f7904dea47f933c42cbf1c67e01e68ae5fe8df0109244fcf871c45557f7bb93ff7c79f8c0738bcd2864a37e30a1f3382aa30238c1214b028471f339030c40c2b8030438a520ece72f43183c78c9dead4453e41903a2a0b6cff3668b9ae2fd836aa321f6c30be777bf7f53e7747396580e9eeee48626d9bb79207906b0ec92ab68dba7b0ebbbbbba7bb7bbb7b4bd9dbcc9893a5255ca7fbe7bcfdb43d1bda861064f5bbda10823cbfce6ffadd52526b6dca0697f336604b95393c6ab080aa612325c523c6420d1b29264a472878c789a97402400038ef03f02f773a9391eecce89b7ec9853c5f46faa4918d0f7a11475b837dff908d264983c0fd5c64b1aec8aef6d5ec4a176649b9d55aab37a25ab665dab671d9368a24af0293ba1054ca17965e04a90be1046aadd35a21aa945a04433e32af4ead0393d21054dafd6c914670ce7e294390fba5ec50bc41843d0fd85377b4743bb8adc5bb858af0886ad99669dbc669d9e8dbc5f41f29904c65665e9d67a8c0fecd193bda2b3f4af9ee734e539537d32ea519fd4b6bc86e8c209d757eb68a9a7875e7f83065c420bfc096021172f790ee2c328d22d3f70bfacb05a560932311b263144c0e528c248870df7e4dfdfbd988a0ade004ab7e36f5ebd72f4a4474afc778e79c73ce9f5faabf98b37cc69311fb269d14f5938a6a4251940f0e5b5a6614a64800192820c38b3c555a482a6354c8f4afa4942e6922d32122aa4113a03097040e6fa663dc7b6d663ddc2012946709528e0f0a15364bb853f4f8c4c2c05400a1ccc8b98112041548a2686294061552e4107f11541801e5092a9840041560bc7652b0459530e565abfdb820af5f875794c09d235324cfef6e0699b9185706176423166360c8f3dd430d01b7abc1f93b5f45a2c1f9e00a3017a322cf3f0217a4041bee8b3c3fec82e43945649950607f2e274f8e08871359e68736eacb5f2a4eedf197fa135571fca5bae61492431b39e4625cd0e562ae1beeaa7e5cec052638ac3d5cec85d32e292f3ac5e6eeeed4ddbf071b7f5a1d8c78e5bbc7d9e4b9a591e71694a706b6ffc04f762fdc1ea07de16fdef6851ff0a8170291a9af79e1077e72f685fd77a6728a098cfa4c2f4001ab484a44c7566badb5def55afb5bd7b6f6adb5d65aebf6abfdb6f6adfd6badb5ed5598b56dbf2631dfda6b6dfca5da6aabfd18d69cfbf27e3344b7d7deedd65baba9ad0df7fbbd543f90dcf75a6badb59dfd81f676151f250466c51818b2ff060e016b2f9f3af52fac40e47e100c2c1d75b7dfb6ede3d578989a77eb7bc4c1aeb8d73c2d6f99f694524a35fa012c32fdc02bd31e6c32e56cec476b3f7a616db9aa6b4bebee06bba25a2d40aedfa5d85f708cc365a498c028138602963fbd5a82c8d236360537c66364b9771471dc8fafd3dc8a12586a2fedcbf6d1b99038bccaa455f7371ad30901c3eedf5b61d9cfd065edabb0c872df7ad2ab4bb89031542f228efb970991e5be7f615542beff92e19434787b22cbb55e5dd2e0aa06b9eabe8e8602fbd7564d22c6c87ce9dfaa936f75e55b6d3e078573bb06b93ec043a022df8ff2c3ca837c81f4b041e6fe66885963c0ccbc78e3c5f9c64df3c29b6d28b3b5efdd0c5df61525b0e710dba0bdbff6aec974eade1acb97fbb0c2b4abbd8eaeb00681ea9206efbf10052cef4795fb69a0cbc1c2dd7d6a9df9ad5c8cd165e853b7d5ceeb998a3bf5d9da94eeee1e5ac256b37b83fe32c6183bc6d831c6ee6e4f66dadd928b5c2836186b185c6fd2a5b774a44b6fe94897aed54047baf4968e74e92d1de9d25b3a9352a1a533694b47baf4960e778472759b5eed9dad9bcd56a66d736a27c073b6366eb6b82e345b21d19cb3150a8946a4e9530042cbbd1de66ce1d19cad1149141263a31cf5a93980861e9af1d05badcdd030c2de9aa3a5856af34683a0b6f9cbdcdf381bfd9271f7e39bb26fc795c14015f08e2aeb8777701b28142777d881817bb96ed3b2cbc53415cc215e34cc7740bb8cf2fc4d443faad8f88b800448fcbd9349cd4457651adcdffd7e1ce2aabf1cfc43b9aafefd5233f55338c455fd6a92d0d4bf6f5faeeedf97ab7bf32309cdfdfe1f3feed72833ec07e3aaf939c6c071b5f26f7ff939f447e7e209436c7c07db165bca96b9bb5b4623883e3aa3286eed327ac87e9e8154d92cd86e8c313611b6a8b28afe42eb8dfdf71fa6857a34e8d061c6552d575d0eb06c19d4252363503106114c381be3892c479f3176e68f87aa3a994c629357d4a05e6184c43e6318c938479f3188a049bce56fed75a06b9836365b64f1cf220eb7c9f3f351bf879bb3c8323d99feb2083a951df564626eaf039deb6fb63b084cdad243f3ebe1b97e37b24c4f265503cdf3a994a139bcd9b9dc576c5165c331057e4924ba4490fe266d247d19440ac18931b67f071d19885f477ffbc21b37b22816a3aff96b9aa6b956230f5c9e3372b35b02c0042819c3fc78481cf4f311599ae3e19e1c12a4419690eb2e8b642211a44f631227b250151378aa7a503f56fa6940e65c1ba082d488a24614a820a820ff9ef7059f17a8848c785541eb18a21100000000c315002028100c870382d16014864980423f14800c6f944a6c5a4295c6a320876118c520630c328410400021c6101a1a1a0e089da9041ef4f0b11c378a2eba35e8a898251c3387f9a0d07894f8585cde44c5b1be07af24067f6ce8160385cc199e2b92f045a2eea1d788a571472de67038f2e244516cb0ca1a8c0075ef7fa121af0f4686461978883f334ec869c4ce40ba22f401891c11316f1de8cde3481d38b010a7410b1486fa2704cd6196445c3c911f8e4e8cff3067a33e2178360ee3450bb68c1b8727903c0f99262f9390a02ec42dbc327a6e55ec74264e9edbcc9db2c4a45d2d83534c59e3843076fb2b2d6f7720dc2d63fc8da0f4994afa1c4a7ba2d6d08f0212e5112e5c5240df66fec702e6ec603c27f04cd10218ee85fba7567f8e026880ddf55e21734ea8779e102f12d394177b66aa73293b9b9700dbf5882703f2f78155716fa40a776da20d5b1e964849e1ab198b1d4517c5ed7273b65f8e0baae20275d1505bf7af636e25c793c3852e2f42e2e54851a62a597860b3d72d0c7912f8a9f1beddb81d6516cb184d0afd8fe47fffc2c59a18bf153fd00b019b7dffc175e40e62f13767c48cab2330827a911eb8d31814a6409bf7813b7a3f139f31398cbdb7f30bdee053c6186f4200c11d55a86b9a0565b54190b8d2c5088deb1993a0a5b08692ff78cc854c999f996446c132b684cce061d43e59b5491389f1c7c78b476485eb0f4c0db619d238f406d1a3821c81fff40f207478738bf73fcad83554cb5bdd83d28ba4dec86e6939169c836ec0cd8e6036071e1e5eea37851724a68332441c7584579ca627da682eafe79088004eb68d5bec6502f5af0fd9555284ba8a449a00a45b4550197a56afbb0b7e6575c2f25cb1892b5a04bd0e593764b9c3e7909d63c2295f2c40d27a03699104fb1e4b14b3d7f7985f9d10e6c0912b98f3e4c1e670c35ae8980f9eb0a3871942d32cd0682a59c2100b174129d80229106ee91a07db508ac07a94159bb951eb0ae932a29c0b4cb0ba533a5bba363ab30cbddce518d56a3ef9e7e77eea491958fb6095f9380c118733687f4ffab536a2c37a8768aae265e74e8d5421a49d0dfee4a524cb8038cfc23b723e62f8b9a0329dcebed36743d48f81b8672b3254c3e504457327f199e3476fd3314dd6fcca7032262729704974998a82e84837621c8534a2a6fe8bc06f4bd8e206bbcddf8bdab6a3b1d3a3b1d7e70e42dae8c5175d732325e40d265469f242d1c8edf34472e70cc278f9882c8ed8c62c335d092f270abeb6d5355cbb7229b021e84f4b577f87eb7e67c8c85ed0fc938122ae1e0265929d077466ab9e04831ebf953fe3b744feb9f4808d298a65cc04fdc92fed08a0f1e1d2c28d75d6679bf361a9db44459a452982e49310010f890438049ae8ea17dafcf2c85371f8c38b1838fcd48b66832f3858cf24e51614caae0e3204bc0f407604e8bea90ba34a17728c062412b66384464dc43d094655764127228ff2f86995c6d3f269b066f129c688012092a366c4fa0892ab321291bc1d99322a648337fd80db081c2e30de314c9b89f58093d0f676f9034f2286e9832149aa91c053192be6625b650aed1e8934452787538114331b568184d6de30a7796b98fd9ee71293494210ddbb471030301197cae9ca59c91982b999f157705c125bfa66922bff9b85e8cf89bc0e66462a36941aceca474f3c131d86fcf8d855e315764c114264b65e135cef488ef16b8e4e014ebd024529c8577c55b1fa4db94be3d39291ea91140d65c34e817349a6c65d7bc70f247c9751a1f8c529db6d06b2df2b5fa0dbb0db69b013b7e135239336adaeee97d98e099c14767481b76f9bf5bfe5c21ab8359d6e6dfd73cf6a341e4030bda34d05e734d24965dd57e1fe0e17278f98e0d9feb2088364d15207d00d3d709a28fec0c60b105a347e9ceff83dfcc83466f14c617db964bb7889450abfb41742bf4d3b479aa7a08718676a05ea2fdd9a49aa0b6c866974aeb922c220121b3b20ff2bdae0ef6c512cc20bdae1c7f49dd488c1cd1b1acf4c3843550cc503f739df9bc7709ab3d017477c92543bb4743e262da49064b9f53fb069a373dde459428e5f92444f1859109e489fe2f7de61525272663aaf94e96fc21bcd094a25ea25c2a8b8e41ead722a1a1089d7519724821b2439fcb266c11bac063a2a4560c06e0bdcac83a78947cf1c7e3b4fc671f32d33ccd8a3f4e4608f9213bf6f55c642060512d2edb0859f40c29c8f74e202b18b52aa62099466005c339069d3a9b00ca7572471ef4f63b5e7f4593e091658b5d2306f07fd8183360d63b6e1aab7e4ddc1b06e25d950e0d3e0104b08cbdd2db6845c28d78751778c70f7844ab178f9141ba7fc8d3c1fd51feaa2c55bec2547f8d8171868fc4b60a03140974ffa38868ce2a380126a02f107108414fdeb81fcc562674260a9af9f0c11792f6688347132b0309e7a4418d59240d776630cd10b93a0c92e0d32c463ad63951ef6db0432397483e1c1278d46537c92129eb5cddf9ba1e1d23a278646eaa4e86a33564ed3c6d448e48ed5f8fb176e50fff872a7c2bbbbcf630c01a8c4ad209536ac957b7114b4eee78ba30f4b4c6efdfbf90142242c9d13d0c6bf8f468b93790766bdd38c99b37ad40e8dd74617fca4098dada6832d288f2509ba935f5aa809cd482461b6e673d40ad09c4463df96338193f2c68ff60eb0eaf8399db5be120674b5b4306f665fd0ac4c6cf023c5523e18a0125f1bbc36d195b8e298c0105f2fa61ef719091b3f2495ac6440350270c078568b8da7069a263a4d8df446dcf54dac9fc73328f9a2ed805d3bb7e8913cd6fb0a320b85d52c4526e8faeb940090f4c68856021eb113fbb3759c8312b35a770d771e447d9cf587ac27cbda1038ae5677e4618942294ffc80d5b4002aa08e9474787d8b4cbe05f3d76068f9276a3996d98d927c98af16117bc5be108102b4026022348787d7357b1e71989f2a781fe72b42590280b95e64ff95fe1083ae765698b1fd0a653708ee6b124f1d42f27faa1ed033ba9c6d949a6acbb93e280dc48e1841a07cc2eae74ff9eed0634e0fabd354f78e64b242f316b832625dedef8d0a0edd258c7596a3b3c3dbe91d2566008f02e11d0f085104526e793315b0e38d4fb1fa4aaf2369e66795b558ee3b37b67112361a502fa5a58dba178b2a25dd84c366b1ae51e73339986e21a0e6ec2d24c1e98c813745bc9c0e6bcd641d7f32003b2fed6693dce51205e0b003376c5b2b7d3e558bfeb2115c9be88cb08aa98df22baf49321cc1f8425611a662b811f609da247d37945c99e12c25850429575617dfc82fcba03e7e0639b95e5b7566cfb4ebb794b9323b537bcaabc80b693083a683f59f3f23f8d67a44d100c661513fceb43148d14922e180148366696049dd66a32d05523ee6511a07cb544143c33d5c2d2aecc7a14b21cc600af41d9e708e070011276ab4fb063a8a8628198760110269d52ca7a60f45482a59cffd7176816fee2a08c12fbb1ebba949d06dd3522fb3181a3c60947bd7f20748240bf3fe859183f931d71dbafb43b60316c9af05ccbcba94ee00a492d36c3991e8142d2818335bddb0c14349dd9cf47b2e7f7fd4458e0ffeee60c568febaef4f46fc374181473102d15f49462f81c85f39b558b74e945841a349c8188d7243e9d1d7a6a54391db6ff25e3dcd0be8a5b36889f91db4ec9d99622ca1a7b930f4a5d0bbeecc5450269e0672a5b0874fbab9e31cce7f9b2e9e1482acb20d42f6f447228f55e2f3357eeadf21a615d1741c5595109c393645411e6ce39473eb3c9aee4b01dfb09d2f31693d452ee7d1f84b65fb5751749026d29ab26316363c5d4a724c5ab399fbc25b6e4f1968a1481fdf5a0b7ff436b537d61aaec2dd8689a7163716b15e89b80c55a75aecec97aba070c31b5499f3ad802a559b1f4065953f91d5b5c34d19ca553c392ad5b1ad24ead5a7b817da86a48df6804145f1d4f4cee1248403528a62e13e097c0b5e46dc823770ba57b407dc8808af8e5f854b1de4d249e20b057f60d6152789f09abf239ae8f52c788725574e3de420d0f8397c7bbfdc7823f08c229897a9ab0f5af4e08a4ff4ebd3becbf08f2326bc55107e0181d427d65f73f4946700e8bc5addd54f5292eab1d1eb2b0040b2f94aede97855d4ca27e46aacad632baeb61edd52c3d96d1ea222c7890a0ead58554b3694a4cca8a08f6769a5011237b4260971e7a9ec8bcf287194db9834baa2899024d14167912e9538b164fb32029908026e400c418cdaae8d2e91bc5936c6c304604a5aaf24395145e1ffa49a2e1367dbf734347a2934ac6911ba8b4f13638ce130ce35b92b4d4ed34b80fee7d0326985d9ebf95e7ee52319e1c4216e1c73b8f6ca237108118ed764001976c3fba972f6592d2b5920603fbe93eba0ea9795304ccca5766ab1db4f829a9100b80fb23d8d9b37230ab66dd6b6bc2f05dc89f4b0ffc6854542d624c973e55a6a755a5ce73344969a0244c16d2eca67702017608f5e2671d7bf4b699fa6cd0aae9e4e9bb4b558f60a96f8f111b4f15a6b47a950f74b827abf3062fa544a3068b83092e07ef9fbd0a3a842cf9f18dc730b69cbd19060a9a965575554a988f997b2bc14a646510b200c70342cf6a7dca9e7fef81b2acb8e15e15c1f4fa0c783be3d88229a266530d78052bb1bf463c894cb75fb8e6c1e633e8d61924ac6a73d6521514595ba9a20a07714bad6c915ca5506740dd821fefc484e100d44d05d74b6aeb06f7ed7bd22c68d4cc3cf86b49f9b845d713c22e4e842b78d25645a0fb231c7cd8b5f7cd8127a7edfaa4f7c53049e4a4f5e50bff9dd57dcb62b809fd19945b0a59b26e04f4b6e126b1fd8e8f8b4261be4a503ddc47ff4cede3701225b687d4eb5c24bf34549d40c0d08d3cec7fb7ca80f1feeb2deffd0ad47404cddab88dc2b57d166fe091d9bee02be11a5e44155e94a269057ee844c1f2e5fe1cd2b809be16e0373839b6cbd9f040e11b2a5f8a3190b01cb1d55a08f2b0b01051ca7118310cfa249b7f8309fe566b8937f54768b392eb18479d00ad1dce146aa35862bc146fbeecf1945448df253102d7589c343f1cca74c9f553a41cc054aa6ad5d677d1782a84552e14cb383a9d17782f28dd31129d342e4ba60eebc88f8943364375fa2b2489a803608cf6f1fdda644cbd0bd88b979231e481c8680640e856b039de7b0ff2249caeed5ee0e676858568a4a9784ca5963b108d84680c83c433d6a42811aa6295bbaab2b930d2ba3a40cc8f7fb8a9bb0e69dd4f2abb3c4d21f1e83d4098338ddbab0ca37aba89dbfae3492956a10942b076d7c9f06c4ca834a330e41e1d1edb5887268b1646e256633b776edb009e974aaf663d60b33d5ad49695abaf24056192192cdb24396c41f6a4d9d44b9f5f8b3d60878c873e4abbe1f92d692067c348b8289ff44cc37614dd1ef362154bbc7ab3021042a1b519fb2a0304357baf02dc67aaa3acba45f0dc521dd743760f430e76e077f8c56c5ca3521357e06849ac58932c9cc598f593f5e63bbe59e459545abd004e804e1430c81902790f088709392a3204ac54b12a949debc04a50bc6767146466086559f41742fd9634719fdbda8b3cee71d2c1434e77864100a2a9e84778e37c01648031ace131d82893cd346c4791aa6a13a76e09c424d970ace2ad0212e7137996e85c5c53564268b1564929cd047a062179713f460a0c79a7d2eef958e23d0a6e0ed543c1e0232c70cc9ed39be036c334fc2288acc535a702bae717a8abec87f0112d23f4fb12d14a877b526be2150d49e4e09d73b4f1f03bc31902bb34600405d6db2c22c258033953d8381d2e4bc656002fae6d0cba800d9305b86266ac0ee419471d0d269e9744ef04bac18766b4b2d1781bfaac8e92ca4aff2a1e384de8485d10eb07916b5be409b034df714400b526fd27ff76eed7fcc66bd9039cfcac6a104e6bc2bf37023b1861f9944fe076b08551bb88e14eb63a9f2e5928ab8d250944743f2f322b97d15a9e849a63fc17b0e2f15ad175f97c5b746cdb39164c812ef6240a602edba2387afffb8c3009568d0375d6b2c68766f0c592b5682611b8c50eaee35e5a43a40a770ad6bee8d3508280bc948e886b8c9dac93e9656cd80dab46d0efc2d458a21dc5f9b966d68f4c362e273f91660209b54ee8a65003e32d2f3155e1268b8da5395bcb43b0bd17dba0c3cb3da1d36ccb70d3e125f2d462744c9cf12416506e4c676f99b4f3a06efef16498962b5f486350600a4cd2a062b95c072dac6b32120efe5e3638055dfbb5cd3e8f2e2f7fff39839a2bdd7b062d2cbe8f77e39ec14a2df892b9d8952bf6f7e6a36e83a77b3321344cdfb1485d403deed6e2035f5b9dc7345203050370bbc9e7126bdc66e38603edbc57fd1590d47633c11ab466e8d192af259a313839253b0f08500aaa965b8c46df54fbb81ce6164e5daacaedd3053716a5407cc8123770ba6e415ecc90f94268afedf6fd6b574ce58cc6196babf0d9d6d74aef7785e08d9605eb794ab15c143e8bdb2602b4dd9fbf4de450974148d6e378e4aafd0f64ecc0fcea23a3d13937687a17364777fae2e870e033302ebb2a5278898a74fc9e2e4e29598381acd98e57311a3aa56350394c2a881194d0d43cce2c376fbdfc046175dcca98a018c5bf62a19a5e80ff63f36854b649873a7464c3763da9fb860026b89bd0db5e16a60e30ce83819a4a5aab43ae53bdf4d0273d5f9f4e6cfcbaf9e722137b9ea9287f020b36c0f2563700c9f4b82bedec2238b2aea0681ecd4584170895f4c8cd67a05cfcbafaf7e50576bef2004e3ecaa08cf12397c611806b4b139fdde225c129de369cbc3e72228cdc89d3829004f0d06c7c9138b84df496c40e9f6d910c654c9ab464661b19cc807bff09b0ea2e5f986ab06d8924a075358ea9791d5897ef2045857f9234bd4ad5e36d35132e51ac61d4757bebd54d8263848b24eeb02efaecbfbff72cfcaf80c7c3f2852e5f1fb6acfa93526d39bf0c0310deeaafc49e50eb36db714ec1a1886e70a331417106b18a8405b8fb82b2a1fa0dbc369caca8934b41eefc3dfee01c2a827bde2a3e9120a21d38ce39af42fb38b1a331e01e29dfab3006d03100716a69cdfc3ef0afbeb70e5d94970aa8f0b876ab487dab83d22387e762aaa091f125bc4a2b89c7753553d41cc281559a447c3c4715397044c560035f277402eb425b748f2c85eee310aac9e604db7cea08bd4b3f73ee426bcb0608994bf069dcd83da7f8a3a2395eb11bfef7b6d138b1f8a798eae0cfb75ed43e01c6adde41f7a0be6bf135bc11b704f9a1ffa948e6ed4144ce8a66a4d843b1334f2fbab9af63dff7d855d2b2d7861602d9320259da865163ec675bde9202395961524dc1dd18fe90a53e2683bb74e07f5c3b557f1958ab2b4454e906446f4047fcad0fb80f0b8050ad312f4bad16b80698eb097f8a019d8f49337f89fc46b1337b4853bf668f1e36c8059b131c143c9ee6759040df5890c259e5a477a5c1b83e7f28f8cc8970757da6ebb1216b7a681793ce6e2d2fa6e760e428f8b27faed2895d427a9f162cea3dc0dfaff481ed7f323c250a06ecc861a33eb34000512c03c95b447269161995475ea9064149f25466d189ae0fe53d203dacec4ef5158f72c922ccc84a66af5127db30874dafc6d7f7a4b557aadace77808519db1a2f1520fc7f4499db4c5cc923ef3c6d9c0f5a7053fafeb074fa75f485b2160b49cf930515c7c00947b441ce1c8d32878f1d087b40748788cf32bd8221470eb9e4205c09285b18b49f223f10f7a8ac3a44c331fdd591c01d21aa28d1be1052698a84714b701f093d81e18744538b64c34dd335be27b520242e5e38bafbe4b7f62533c595adbd35fec3927386745a6b1788dca53f5ca64b206d77e252a3147bfaaa962cb349b1ac9c3d165613c656179216db8860c17d295f8731febe316ae58e71f4e4b5ee88396bb45dc8713b2560d4553ef917311c49726e13dafc2b2960a5624410d2911b7b7a597a58f5ad2774d771fec7f639c2c52634c920d61e01036f076739ddd8d078d548ed87a68ab741f9e53e517773f4de821516477369f32ae2876432fde88e2d7083dda3cb08462578c99a7f2a46b0e2d2970c063f9f7a334e9e106a43c406c0a8115afef6c51b5db3327ef756fdd54c7ea900e298a141e64cfeff24ccf6752d21f9ebc9dc619a3690e8f1494b05e40c3fd07b7d08af6319358695c14f62cf450328610c73eecf72860fe08b102757f70c73e75bd59a4b701bb31aa060cf964f2341c8c4f20ed4d5ac4feda2191a151ddcbad56a199fca0c8523da5a74b373bbb465451c49c61266d423309ae5ae78e60983ea3470b2f081b33cc843e3427e65a230382f94b7828fab481e4993bab2cd2cd6e0f3e921bfe56e27534623b38bcaee77e7b33e7c51b92670d6686663f11f749a64f727e82f34dc926ebefab5a7f189ba06b0eb227f549c953e643b1a7d25d6932bc032a8b4d69293afe12020017b2f6ee8401efe6dffbb7d4bdae39a9f7e95ee5756831bbcb037c58d2ff69af1325bd2ec0b0a99454ac243361e824bd92f7b36f090fc5a1ffa739bd418361194134f869585eef780b4e142d58b78b0c4186a0c57ad32d74fac9cba35adcabe0e3133d5f6fc640308e268c6264b091afae7220a51ff57139acec8964c105872855e6024f2fa36eaf41c19c853f2adcf2ceb9142c1967f21b7fc3f4316e42040029e27220569307c0633522cd372ceafd1cfd04f2014979e60183f81c4d0ab75c94952f4ba4b98215ae0864e1ba134db8f3daa074a788f6ce23fea4fc4b434e26f691622d48371937322ca9756ced6d746f7ef179f5a6030654e0d36f6c8f614e528edc68cc383645e513494361a798fe88f912b795490886670f9bc039ddadee0b231bd8069163d8a0ba810c948cde316277e80aa8c590d37f33bcb612795735e81cc66093110be62ac07643d37ed2b153b01bbfd59343867c58db253bb7dbf320b79d8eb333280752be3be0e8127d5c7bf34f61b9271d218a2095ff9aa6ad6bb135817a51acf69c0e352dae4f7271ddc974c90491b31a82d13ecf4ecf35b299898ed291f9af45e57ff25631398e8de5b8d38280fda25874b6cb55d0be224fd655694f40b6656378ae54b17668f10786c1ebb5e53c56f3731dabe9fbdbf297ac492c347316a764386e16e7e37ba2651c6cb93c810c2c3c1c3639ccc05f44dad9920297be32d686e0b346eef8ad1f858b891b1547ac5e69d3bf95f83f354f76886122551bee4d93e6de782ad190398e516a9daaf4bb52c4a7f95771f9fc05c72cea445588182970a5a5d0120c2e11a457cbe60f4235806a561575ed0084a0391d7a904274fb6f9b3d9de4456470a19a637d7ff47a2d221843544a538c395cf00628bf703504fef7096135ae0a22fcdb03c9496063fec5be03d5c3d885ec32af7f0825baf9cb2696b1005194ef1d786f51884fb03d7ab7078c36136045451d167246bdc406b35c1f29a14ae052e42c925a3083a2b1edc41d6bdb598ead0c2affbb16e61fbec844ecfe509d4e07cc313b434355479574e28660b432748146128d5dfa8fcd4500e4fc860e723d83d3efad806788c30d10a9229006b985d936920784806db2f89a03b0bde20782ed49257896a919629d8e9cca7570edbb3b01792f303035e124a6d5533b3d36ce925b33034b28b38a5ec373813c7ab2777d5f5c4ee39106ddbff221b69ada283625c029f9acea285c1d8696886941398805e34724e39032c813580763a921f64b51075b7a0e7cc8476b1fcc9d437e6baeaf5d213b57509fcc0882766318970f75174b99d26642ec2ec28b1c0e11fe9f3316bdde194ee12fbc72726325a1a76c70220730c9fc487c9b1b56df410ea5fa82647c24664dabc854ae69198f95f25c899f0826511251034b1ea4f886ccf317a8736553c4b8b0b0c4c5c069158f81e1739f6403d97ec029669c4119739b7e542407417ed6406140ea913684ba02922472c1645b3f08b82e380dbda77293a21e353aef67bfbf40b0298951eede4010314d31c5177151ae14a7c774c29dedc5ce41ab78ef91bb705aa1cf1380c05efdcd91752b599c5a3dc51e65984f649366f217d494d9a15d9e768245e7bc874aa7c3a184ae3a4caa11086c0a4e79bd41f96fa3bfb5e93b38314a3e6566157c17b0fd64208b02e32bbd0972d1aca2f2d334c163bb4b2f83a52b6950396ca9f27d36069d1e05c639b6aba73e7f2b155700af1cba73a7c400fb205c23690614d53fa818ffe435cda30489f64054a1835cc330699269d8b40182d1fe66de7e89476515a92b0ebe47bede836fd0e0013f4a9e709251fd141b33e2bb498e8f4792437d14b33b0272768f835eaf8c31488ea17b7b040dfcc483c72d28938c48597a7c393b2de90c070c1d871d08ef4c7b68438cb652550d4ed55a0949553943d8e0f8e0ae51559ed7b3f754d28729606812ed739da85e08ba3950fdc64d741f486703a5b06599e87023e5726b6ed80658b75a29665a103c4afcfe57b222637bb1f10f36a331f282b8dc9f3357828e45e3a3b4feb389a6e834ba487b348e5c2439198349a5fc1504c0aeabf4079d6ed2642be611dd1397aff84b6575fff2bb4584fab9e25549c6e3bc217fa2073a27ed342053bb84f36c02c776d501cd8d76d3e827260a547af6aeb4ddcecc03bfb7e16a6bf6252753447757ea44e08368ec90720dacf685ab335eb6875f64c8cffd392e0c2f7542e005ce50f40957fac43667f9bbe4fdc6368a68a86612a5473ab4bb1c78b586359fc297a40828681209c1c13a6ae8a18bda3a79f389066fbb5402ed027cb5336b937d6c4a174774ed4052b94254df859e42dfc9b1d7147533fd5c3a18245bcd539253458f48be05149ffe7a394d0ce6ea20cc9360427196f60b523e3cdd1f86fd50216c03a6c3511468558cd5375d089ab53191145a82584af0250cd643ea5a786c12581928ecc5191b1889210c75c82445b61644f549f9a82bc1c0a846390d62ecd31fd59cd1c9d6d7d74a4eec47c2e19168027c5d3740c80b9af648bd269770a24f33ae0b208cd8e32c054c42fb7d1efad27cc7804512a2054a3b56ea1763bf840bbfa1b1c6ee2f4a9adf1ce96228d9734ffce18fcca96fa62c229a9fff26b015fbcda589b437126d3ab324a6d023198b4a55fcd6546c6a3a7d0a56cfe8e79b7b1d2920a47947c0912e1c62665ffbe07856a9bb83b1fc057bf11ca8a4808300fb03c72406a2c72715dfe850e0d39b84f054b831a770b2f334a81aa3a308807f19b1497a819532d0ac1f2c01e28e3c959b4c2aa8c513e898bab069cccebcb51a631a45714c52a638eab5d894a52da290a784ed4d20943a7c1745d440ce42696a10f57e7664699d17e61c81ba4992531793638587b93ed58e8d3b3b0cad3bbc8c31feb2d0b3066e63547ed042e440007a10ae07144aa0e2460331941bd60ea6c06258e99030db27eec35caad4177950e761ceb062d9684869c9bc51a931ab0fdc0c5d4cef18a9ab58759bd31ab9e5a3f304f1ff3ea122c86920fb88660f6fd20fdc5d11ba986173f904b78acd7010703e80269dddc576347c4e25c6190512c84403cafb52518703ba2307a15d1230b5c9e9837cbb52f99944cb3a26ceab649813021a48d60b23b5fc4d7e18cee11e008fe3f485d9d3cc0d8e871541382829f7e6733b064ad07b5da4ff8f11f43b46f4f8d5ba895ef26127c05358a037060516f1e0124b99b3028bd8e40e0f066a62d9246850f0a74395dddaa9dad769848fb04e41c2ce94f759800156a34d29fe73f3e1013e18b1da3848f3c64c9710de305e6341b735e450a74b9ea8d840681fec408e83e1c43fec99a9b64cbd244929cdeefe1bd9f884fd76f2b6da8636e150207be0a86742f19b8a3daf198913052c7ad6365490495b990678951f3f269d68c7b4b152c43864748f118763706f578bc31e56548a31e69d31aa4c89975fce79bca05a79e3477a73898ae475dffc503852f0c19f9e1c83505ef900948290b343b27c0779ef11d608df20e3577c380fe9a865dc72e08389698655357df490509a8cc184f5caa493abe039afb57c35e82030838b417f56abf4932fe1250027800407184da5229d65cfe617a27544af41145491c288f8f15f437d0b5c6a16276640ba84b505c705a4599a915e2468a78eab760664e5986008c49a7954bc341f219d02de98ceaeb2710a1004e7b877b88d2a0d8d614a30696e8e8753498bf1aa763ca3478d9941d94b860a95a408a3f244481a70f526a89b0787891444ecf6dee0c5ea1392282008107f116b8aba8a4a8f5099f0649376b46349831a5ab9fd813c3ef8ca27d47302db4da6094ed7e1107f6f4afb2724e1c424347916b0e93a357359bfbf6411e8eec81295a47c21198e3d319b4e43a97ad5e6033cdf667d0558aa1772b0f57b915f38a531a8e59b010526103461b15f6e8826073a30dc3c8e1c9a3634188104bb40724645ac125d88fef091080a91dfd67c46a6efa6c10743d8ff22104da195f8b96a7a66c8c87f854592271c90a729c92dda5d07c70211346c329cf7293304f03b7ddb9ea9dd94025a33dc59a67298ef05a4eccbe73da2af544404788612efca1c8c78ac11d32e6212be4470b4b96e193ce90756c29108a8b739456c4a82f83640331ad4f9daacde31c884815cd0be88115fe5c5da25b2f2e4b01d36f351644f2038916654c4474651c025bfc4b7d4962098091bcd64cbd52e394cfd6e5c32c34879df39538808266aaf0de8559ec0362d47383c4003ceca68c9730f9773aabd7a5a7b365a273566689c441c92fd0757befb09e80be057132bd1af0884d1765c8a262aa603a158849a3d88546387733391a36a19cc892ad0f962dae290bf970e74f7a31dc3ec9ac0f8ccb5928e904ea86449f5b67d3e098f8b1bff741734937916a6252de992205cc3717896ba67362e604613a58a03c935e82f87b04f7ff07722a84ff29c8d8eb61e7f7443385940980e0d683bbe097880047cac981bc0619889a6a2d5add62112a4e739b02aad14477046e870b09ecafb7b7b96a9132408cf9741f7ad8b95aebab8199a37a33f1482d6bcf02bdd0841acbc79b3e9be5d04c5650ecb50276a8f3a34c787166a022b6327bc4f76c4a87a7c10d7ccdcd292cab63366262941e70fe719673c6a2e0499300b2c9c9f519c3aaa56966cab0c0e53b0491329cfb21a23d965a81ae20023e1394466cc5ec6c14ab38897068ca89b3d6027388dc57f216c2644dd1c14275c49b28a02e7482a8e9e14500ffb60cb7d4ba802a0f24b7b5e451444d4bc95ec332e8812daf699bae6d6c0f531d5a204c42aee95c66d5ed62445d370a9c9f4a704d2d410a2e487ca8afd345219408550018c60a3c0a9e2118bced5feeedbf958d7ff674ac479ecefffedaf2a63dcbf2194c480ddf8d73cabcb0b8175368bf26300e08ceb223927e70951868ef1cf2cf7b67be3d9aed6222d7302a89089d19153307c72340e9bec6649f01f7b655536a1448e834438d347d399b3bd63afc0ac184e556591612a1662b7f7026d15a92692d2d1ef2bcdd198de13a50450be52f9ae3131b3a44052457b35cac0778020e4bb6fbec0277930de5e3bdfbee1d1d16437734879e4bdf2f7ee0d63d67a5faec62049fa17daa6e1eb2f83ef410280eee61a8b4414b8df636347cd8b6a0d28b7a163ee1dd601d24d2aa5d1588532a876ecde0c9efc52610acb197e5786351ce14c3117d8ec3e04a6e24bc91ea47d7241a7ae4ba4fb7a520988944ef2b628372bd52550913d6c016e12896766c3216b991945dba0737e0e29e0b8ef0ad25804928e6dd0cb6baa97b4ee1c35a8ad620a236a50db6ecc1491e3f1b7b3ccbb7ad38de70fbb9a35549667e19d13e4053f27f352cdf562435a7f8122a27f0a398d0467288f092e04b991b645525ad3c9f4bce3c2d3098689fe657c7a03050c3d8d3a3ce913356437313e3c47a5804460665120e5ce7a737812af0b5228b55fdb09272feaa2672b3aecde0fa24dd2796781bb51787d8dc28b508a436c56365cde8f26479c6746e69d2ccae69210604660f70d4dc39ac375b91b0c43a5e9832ce5bec7c59eea51b75131a20ee26792b959017563586f5725f2b1fe9b032e15939c1db83852d7ef7d17e939b71251942fa620978a75746f310d263c24c7924514072550fab25237b0c250d86ffd0dc064329dbc09d3c09bb896a0a4b85119b7cb9fb93fedacff5c6c402627643a2ff36562a1569363a7953ea5d3fffefd5f9775c56904e2a6a2fa08be84e3a0f82c1e263c2ce5a7e29b6ad16b939f7208ffd3819d26a7db1b8c60e36c9d6ddcc43e920b1065f062646e07fd59587759cafc7d644b42ab69d6993a24d858a02801d07741ec5435108e9cd3eb0f3a6e2ac633fd7181cfc08d036482c2d5dc835e431aedf2555773972459ead947b2f245c2e138514e3f41d8aa6619c00214a04dc7c1726e8d919c265f12e7541b0eb5f83460931f71aa6f70f9b83c8db467cb409d5b8715ba658d6a7c11fa7005f7f23c10439ec18e394b792c4a3b0d150e1ddb0b2d44eea0c0d91fe97d2e410268c39aef7a7f31d4d4b41b4233105bdff45b6f97d2fade2f8033e5fe263d5651898d656cdc1f6f729dbf5b912787444acfe10b1d8d88049ad68382fb97da08daccea79a488be49f6ff95595fab8c248556fdf000d6c03fc732fd9a2ffc658b4937238370f56b36098d3723d3bd636d040ec31e6524f45524e0f020c2aef211f68305ac637febd04841961a60e0e035472ca522b963881b17db0faecb554673047eed31d668afd105333cded3f88a6a2af86eb7f8d7312a9992d923c72c2ff331cc9e4201ee4edf9222932c206819ead6811051a9a346ac10bf7bf04ef8072afb128d069e5c37d71f1e994e7fe59d83c4c7c420cc775f6f18bed1a299985d5cddf20a5e81ad59f3248e466aef4baae16909f60e1d2f958e2f10b7dfae187cd02bb0a2bfd5230b007c76a283a3500309889ee3df3150711083f7bcee501dfe291308224b3ceb1b1d8cd3313d0b66f1a4a01c98f2974318e680ae75b93ad6faeb16b0423d4b3604e145fb48ab9f1c01a7a210a54e758adb5ff2a5381aeeb16f9c238e8bcc92769861d2833532a3292161a4d99e008eab9d9dc2106e39c98ac27f80c341d5b293947f2edcca2bccf169c96f2e713812684874a16c32a21b84d9d7412f48d72c848ad66473e2d897e9d1a79a350f667193fc7f3127f63fc4d85750c6be3ba7b5b54535c1f765ad2603987436f66591f4e54089e59e35a2072ed44c4cd2bab8a3cba19a6df2d80f8a68e414c289a8e5fc9b9f4261d4983f9a8a5962e874cafead89d1f0478911c2baef1bd3ec6dc4e2e03387079537f2e382367733d9210b963abd50ffd27eab206b536f9300ecc4fb73c656b210cc4f0450e8d260baaa741d7e1e28dedde594431f5da8873919ead0c259d1f15d7c19652b7c9dc1e6eb2f1b015a38aee53ef3159518f94b240dc571a11bb36fe18a75201b1b4ea05cceab58b6d12c30709a14db6741c10b1ae003a33be1ec3da0e661c00901edf2706dd2c322bf42e821230e905a250c414e469d6f55ecc5f5783c3a18b3ca211ff421fd31162d8c60441eb3b0207a8a5e77031c55bcb0de79182f41d95d6b6400d400fc9dd60b3384ec8c12eda34c8c9398e77103547558bf41c416142371a71163331e2c3c3593503d17935e44580a1634da91e476d80c09195c021fa3113d773918ba0702239e146383967984dde4071d1af012639a8302e86f49c6722fd3b9aab4c6f990c867198d44ceffcdaaad3a49d7b4baaf6d2af02a7f8fec352ea7fe2cd31f2b2f732993983b26c69a7fd03d069231cbfee0eadc464fce983bc50a463c00770423bb15c5fbd8233fe9c26643376a788a111980f99f54b53e58c9df8b7ddd8c18c1e405511c0fe0d420c6b790bb6529fbcc081b4ee1468ca3568d9d8d18a5ffb71f9730365961ebfaa3914050e693b12f46b346cfac90df8c02115cb038c6dd789f47ece15a731d89cdfff20028065ae6647d90a5286ab9b7caa18d401ce39abf8f7f1991c1e84e05b2ee3b565be456ea0666b740a94d26835e35269a40f8cfcb902bdbf697283f3e6b22a37c3abd9e65ec8f3f6eb6d05acd050740eedfe225d0977cdea2946948cd0cb1cc70b34a615249184cb4b92a80e4b913180fa2bda189d0c5246700ef9e191eda54d5b5b314eb712f46c19e9e814631323f124b426b0c4de08a2d57a3b60cd8cf1b61366f2c7a16ea71f96f4390f3f6644882c25ddc6a9e118106ec21df209ec293824da6ad793ce6aee2c4cf4334d9a3f520dc406c72f98132e1d541dfb478dc0d9d604bf53573eba9b383c80d12f85a63059ed1798b022af919d2d8023826e71d912d963ba5cca42c22f870409f78372901298baca1a264bd88a768379edf3643379c8aea1a8390bff55a361ca88c17c2388916265e6f01f4df6b02556906f357a0e3ae0b4490b108f8389c9cf505022efb954d0da3e254f5950f1396b2f1aa5c89cdad36e7ca42d23cdede0c3ed1bbd19284cb6a33474b89a7278453c4f4f3a1b6073d26ade3ea08a00eae0939bb9ac04168bde1c1ee1e8010504b7cabfe8ae4fa77a70258c4343009010316d13da369b8e4b2b238439a8ad735558524b1eb0490593cf6bee68c8bdd9bb98e81feb0800f2048f5e513636b8a31ff4ebac889b20cf33a730d86473ec0cfb50f2dc3052396d206fc268a759d0d2597e52ef493fe26133872e8b6d5b00e8387d535ac46f5deb03224c7fd473c7cf13a9c7aa78b30410c0fa52d89155301afd1022eca4a321517a56e8c237b57b5c1af495f316dbce19e3a8a47780204774545459fca08a2e57ea0847b2e9791a16c9058afd2d3191f09cce2fb51ac5b766f0f61468393a6e5cb93cecc69734a8c189a6a58030d468760c1e5f1bea878006e22054bc03c82698790dc18257fe3c674246b3bf585e03f0d9e7e82768ce0c1268fad46e09b0b16aea4d3335d960aa813eb3b6e2b48906fa20f07fdd408c467d03ddd4debf838a4c5d06f7fd0fdbf755042fdcc5a31fa52a4acbd6feb99604a5e4201f3962a8dfeb6752e391f2d2ba8e462ad244dba0294a6c28aa06c3cd91a87ba43b20ee4c04a339fd257d2dbe00bea8c181cf69f478bf26ad702c3f6d3fca8a41711bda64eaefb902b563260a86579be435bc32e2ea535eab0f1c7a4a33d9c4f555a46f1d6f1c63dbc9d4f58995e5315b4b22678d03ba7ee0d1b2ddfd7a350e471662f9f8dd7a594733b22ec036fb45216194017f8f751ea551760d4fbd12d1caf37b76c0309e054aa94d035706e8e982248abfd70b56529f4babb08fa9249b1e3f132c635032ec2c1d7d9e24c7d574210d148c5ba32a4f48b8904c517b6cc0f8124e432771ef3c40860accddb796f10f97d468e18125dcbe7f3ecba9e76282ed1586d0982f4ede78cd0ab83e1338193df32aa9a333dfb54249b64c35b0b39afcc6ffdb505925100783729efa1dac0b5e903d4b7a59cca00bd55001c95a4afc36245f6e92d861b39def13d0030ca032ead10eec4bfaac815fcc94aeb44537ef50cd5f795009ac4fdd3cee521f666e936ee5168a781065efa9bea30e88d561ddbffeff1ee24d4571b2605f00d2ecb86a113602129f51a62a425ad7dfdc577d295bab27aa324bd105e16d0bc5d7558c2aae7dab2ab7a39c13857b78aa986ec9fe0c38894ec6c179ab844c135677f93d9b8b4b73105116168d74312ba0c3d1144c1bcdd44e00545bf1e3312a5ae6ad220d9a49f373c04e19e81bf3c0359442182c99319a9a787ec831e651a609a0238999b6460bd639588ae9f0e76f0d64a74e037a5b24d9f3485243055651897b09652eefd4ff19a16fc627022f5eac252cd8b572c153127df7637053e5f356d0a36419afb7775996b8a17f2fcc4993b755d07413aea42dae61dd0187794215e86d1957a63a174c93b6a4acf54207b0556e1d9579a3d1c409e1097664352500240a270de9445dad7cb34ba99a998400055844f1afbb113139cd96b2f0339a639a5576897ad564f92ca46b297b38d82f44d817ae884ab7124ecb1b80b3259416299907c4272cd80b2fde86ae41fe4456a8ed1748bc160dbc3efd4f61847d773b8d742417916e507b5f8ac4744b6f4b35dc1e9f1ac83dceffd0cf1c4ca350914690a2000c9ad370a2ed808b3dcae83b0391013f743004ea88439bdec91ce010e630a93b3d5a9a336e7da4981559c2bf413c3acaa66f2e27812e8ef4bfb8f10b2b83dccd6f37e1f92812cd8bd79c611d7bef41b9c2b9facb3dd58ef5218d46aae2e5ec73f453bd7792f43e686ca6e3291c5f9bf9d18f36c93ec40a449010079f84ec2e73768c12e326b1da0f5a6c351d2229b248c8a2cc8b562ab6dad8ce1cea8d8ba38381ff0c834c87a193fb0305cb260ba81c633e42a7d2c5fb2260071be43a8192c9ed3f4e10be73072c6367cc3fe885dfc320161af37fe1065862f2096f548b8b679c82865eec6abbb54a287d307acf6f503be36c31290bc3acb655687bcf0a8918623ca52b515d0a6467fc5063d739d6579155d3ae858c8947dd96ad78ce315b78c215b42deac2e732c4be07c56cc8cae7c00f370b8c7ceef83caec8ce6027440c905425bd1ab8a2d207e5719b484dce8c2b44c8677a0538a6004d0e908140cd7a47cdc8f8d9a91f5f75d6682c81b9adfb3469ca80e72720b3a1d6322dcdb51071d6752f2195de74ce06ce94827858081dce6be6c64e3ccc310f7e32f49a678c01d8d226b3e525a6d569ea90ca056645a1c8878fa52ce9b4c0122e2bdd6c8c38171a2c051b652f27b7c4d65963a25bcdb9234d068bef87d16ae7f68f6cc813826cdd44be891f033f216926db4ad5ceb3633c38dd9b4a57cb45b3635a5807fa352e698471f79650768bae049d4bdd3f8b1296d0bdbec2237c28f534157a586c864655e6248e1ce9cd8d060bb6317a7a3a1ded7f543532d3c457cf400e99ac0f3b5e5e166fc4069c2d5649f65ef9337e83b8f1591a8b036209bba92884513a84cc023af3c11eeda13c98435be8bf1857a7122e4bc8cb0ec1534f1e9816b2c0ef8e3711d04b9b085b283c4ac11780eb4ef6cfaca2cd391d9d9be45f83164391c1e6da6cfdef34dbdf945ac27dea4f10b687d87f097162b01ede586b3ff2bcbf93d68e620955ebac1a500e394492a62ddd96aa16e9a1d701de31dbf956c3a233cfb25c2380bae40e7e0d4704ac497cfe7225e7ef884958ac8a0d9ba609bdf341255c18f2b840b636c23afc3d2f19e6d0fcbd3e464b43590af6b5d6ee94bf41ff2edccdf69f22e692b4fc885e0fc0a254237000a8cb4f8d92b447ae4b8baf2f4b1b80fbc985698f581a6b6ae491f5d2d797cf273e5375330a1ce3477be79a7df3181f9392c1064350d72ba350a02bbd21a59d3d43f018a70eee7dd78703cc45d306367e3dc9425fef38d5abc025f147643879eac46331362a3b9e6a36297f1526949aa02495333f75423fde89407b60c17aed57088fdc29a875ba76c61887fc9791af39c4d18f4469bb002014b5d5180f41751bfd886a53a1977cb1b40a26f29c87928bf4cd8c988465e3a8e8a515ec4db7a5dd08a5b9bc6a3ead499e48341343ae28d42b9d7bf3cf5513f9da6695348913eff7d3516afc3661563a4a24fe8323d95ede896001b0dbdbfc2fa7112d099dab7b8549ca1c2450e05785a314c756537f312c655ec3f1d575bb69e9ba9844c2ee30ceb1b724265a6892c19e47c13899336a52456457f8d4dad69d7cdc5b5ada9906be07a3413dca0447fbb6493c136196d65dbdf30b19f5d40764504399d8f54bb347617bce72836905e58fa2981f730b550a211fa9ce1e3dd8eb47efcc73bd436454e381c6fa097f43101e39fcd92b91b39b49910d2d207652c3420142870a82c10d6ce5a127dac7261ae54027a441887a0075e96f82cfbace1163821df8e8b5eaa3e7266f5299b09821952e4570e4577c286269b38753d9244d95fd40e64c47a6172b0fb6511cee9116b51eb4aca7563160737f8563d56591c8c1f91629868d92c45dde476bb96e60d8add6c7c9cd15df670dc47558866dd46133855eba7bb99d90ff3f7776173e6280ffd8f88ffebc50715aa90730b3039c8a99c89ca3dee05dab8b48cf86e0bd89b30d18fe3dd6358396c5640088ae9128d0797ab8ccc7854725d46369c153d6bbcc146f8a0679a333335bb2e1cce19b4aeb0edebf711d3a9dbc05a09fb599b9562bece478de097eecd14e568b11c70b3f1a6595b724fc95ecb3d4b7005335ba2b067545601a24b81a6b7b541357f9387f4531a2238bd29f98c1614be86e86d1662624c6ee9f9c7673f3ceb4d87146dbdf91d4c5e2db31e98c0b20d57b0669904368adcaf62cebc2438741d89a1ecc9950fd99dd791d86bcbf28557f815b548a6ab8e7fc95820a5b554988b402e528a692aaaa2dd6f6049fe114731e1cadff0716295affccfe5bc8c18524bd515e53992781804b4ecd661d7304f4655af54d7930366526e5d30ea08d924c8100c1df40d911c04fc68b7b520772ba491169caa1f9609b1beabb3d98dc4126b616ed51007fcdfbe363177d96f756ec6b27a83539ca5d6049709365847d42629e72ee60b7a4ccae7b553e76725240c9d4a5d769b1ea727eaae94d96d83a7a3f9b77d465abc21d04b03325a65823aa2cc7856664517dd515cbd72853606a1649e56ce98e09ac648b91ad72e61acf10e5871090fa70f9021cb77724b30d3e8bc91e4c344973e1ef44ef1ae2a4fb65e9a5f19adb0add50eb4f9e254e759b1f578eb15a04a28cfda74b98c2593d49a7d1675a90d60e9bae756004b7687780509c2e63588d33eb3abcc25697c4a6a08399067a4039f2226a6df633543ca290b4bc202d10f7624ee5717c9a59f14f89075f421c8129576f545f85101de9aa524ce730c88750d4e54182b14dfd4d4394554208c60089d21b746f02b41378aa3f4933f2aadf5b9daf8b60ac10fae00942e686b9fce2bb00940ee7f169fa3901c5882e99bb1a7a62c5321606b22d06ba925f718c7ebae05c9c606c20a95fda4535ea0aa30a6f600445718ce10be9a1ee1d8889d2d864266398ed39d5c65cfb9e038bdb8923ef541c95694481af959d184d10d1483124b40fc9adab5a5288b8862aee63273222734134de05cb6905558dd9d74a394df74422be362c096d8250987ce374aaf177357f2f017d6ca5f3898f980a834a4d31bee386a1f9548fefec8ccbb19f0d704422a5bc9bd60381c640a151c34442ca6cc58a110147db112605a359b4319480e9df5110c5beb3bf4b138c53e1c6a120713fc689b453fbb0308a66d0afeb5b6b60b10cb1246990151e8ca0b6b96faae4eb41e70b198b0ba312277f45879ffd51c8ac6ef2d0c8b9fb032139c3b2b270444327969df16d78d7b24ccc5f650c9b1d2cdf42234a938d76295781a22e3ab768ea94e415cf27d798e3af991aa1e245f6185b94d4e80060fe354955ea5ff77bd77b1436b38f31a15f2c6272ddcefaf71f4bafe961c1c788bb7bbb34875c1775b9ae8e69cc2a19ff4662fae178a73465c273078e850b50f8e369c717088ab46a25117bb693944fa17128e1a5aed369350418bc93bb974e950ef1f014a77e0c3ff503a5886e27641004d27c542e377c9a7413dbaefc3aeec962f4f54800f511429babd13b31b22e6c1a2c83d2f910aefd5df179b7888350de9c58d226e237cc242f81e1aa3c11817fee35df6e19bb361169ae5ed7c49c806915afc21ac1a7172ef034569391f7016e636021614c3e9facb7b0c3f21726b2cb0b74a508d11787ddc722c425c5298a425859dbdbc23ab115466a3ac7a0afb42a0c446c35eed15614565b7229257f0155ead30ae131a47e5158e002eaaebfa0a029b0dda8d71deb0066902440fcbb6b2932660ff4da6287f912ad2904384fae22e49c445fbc6fd6655752a711a735be46062171c20be66dea5f994cd1b508d857a99f8dcc4d17e36bf790de6d8709f3c1e6f89286e827e2952ebc29b6222b007e82ef684de6023c94a21e23695f0286aeab7c4c01b670de896fe163c381a5b887558c8af2fe00ba400847867386130124000d882327eb76311a3e246231fd9a81b40fa8e5cd16eb888fb22a622bb904c5560c8c3efb1a9895a7969b2bebf9c3f9c6ee2b177408fff7141c860025b4862e8d3057d16092ad3a2e45fe03773bb36f5d1af5844619a5e4cc216ab37352923c922563d657527813698e485fe4437b206a4af7679bbf8683183a8457b46123cc422a92f8ca726200f501f91b272f24171eab29ca786f6389b012c2a97e1f76cabc566cfc93ce93543a73c2b964ff594aa879b948a21f1914cf130e68950be724475beb044c1d175445c437b6f4358e47fffb7aed7bb0fb4bbe8fcd009cff13bac21f04e4285d718fa96d025ac0ffda03eced6a6142ed9e8e3ebde0a5eb39046174348fcaff2abfa5c2e3baf5935a6bb246bce39d73d0e5ea60eff2a05c950e1dc0653cd443ed030c38842a70106448241881f3fdcb68cb8f66c1736acad101f3154075a768860bd3e80e16b8ecab0a1f3bf16f6ba305237833012123943f212f71061a6e21a4e4b64aaadbb2769f707f0d814cc148aeae2a675d3eeb0a770878761b678c6d84dfa3918458d42bae5600456b6286cc52ad3d6fda5c37318ed4c79c2a59958e01162195693eb11437993d1f68f40cbec0bfbdfd41c32109a98f4cc8f3f8c0b569e81004fba41f13f8c43a6edcd08279811994c20c6ccc2b225310983262303bd72bbd91b3325f67c99a836fd6b04d19b30812bdc460c81c8e4d09d4428e0f26deac4ed6dbc4608ee82fb54d8ae0300dcff5a0b2d17510c949e5635db2ac70678fb0d3e1cd9eeada85eac96107a8548c941a1328860ca17865dcba235be17e57325d379de6ab3ca8c7c7e1346cd2b2601a442f3624c056ed1cd0c9d546459ec10f1f7a77be18c57d4a3fee194d29d015b64a482ff30b3f46e63feae5f3b888e6e06e38072295ef4d78cfb143b170253a5cd3cb9b2dbcf5837a6bf9fe34eb5c122be5f475f3e7b1d6c1bf80869b1a8eef1ee8c668fd5bfc898755597da184cdb1500d4253b4a14bd9355f14db5109365cf72aaa0e4531bb1524fa3eaa5700b6b4d9c49aea640a9d036657b4e19f04e1e8278790ad0ad8e705052c6f67f6b79789d7206152ec1eabb05f26487ca211e0d7b86c94e43a82b25fee06a40796ddb27fd82b5964d1525dda5d781666bc9c4931f361707f72000089dbaf449317b718fe1ed007ae18d3fde70e095848c1d9d1577264a7a477de0a124a93a27ee1d1b87f50cf577f985e18482ccbb076d958517bd3a7d7e54419d41ec76b263751007d1195367d508dfd1ff938ab47d0d7c8d03bd598d992da41ae6bba5b3b8cfcd75338ee0b48fe1733739c32ec1acda35f27c60ef78eda46f0e7dad8642dad48bf382b644bdd5f9c16f4c7eaf79bfa43a8555e0ae9bbf4b8252bc25b011ded2fcf2c1c3a8042349675cbc4a39ca44653c72d2c1087a92ae6006d9a92699aca5249965b2f62dbc0220b24527fcfeeabdbb109d8be8540ce4cd18efba5cd895731ec2ed4ed382a29191150314005e74fb78960ac70beaebc11541ceee63fe1e51c4343216d9efc3bdd5a76a64867c78d6f6670e98c7ca951d81c7db4419cb6367eb9067a6180376e178ba107267ffb2b9a143ec27750ad1012d53d4a9a508bce113b26c0eb9ffba174359be1bddd7d6a6b64cd9a8326734530898766fcf7d68895a28acc43dff330664e5dd1d97d9949b728f23a1f25bcaf595bdd0e128c29becde7cfce32a71c5e9b52c5ad9fbdd300526533f4ed39ef3ebdad3ba0e1c5b72f59f9bed0888fe3ec56e859bb333e7dfd33169f8d2e9d5c75d1bf3608851966771fa1f51b6e5e4ed5377c66af00b792909720ddf6376e4ba020179997a2037edf6bbf653c3765baf861664b8e94d5384a8ee43ff79ab350f231ccba63d5b390795f79c64c5a949dc9b9037c7b86d8bc8763642efe8f05e12e5d0ab5f7d51c3e1087ccded00d5bd15feed1e02e403d686f4fe0cad64bfeda97808d112cf656c23c5f83dead09860fcd6fb8006fe3a463438aa730adbbf68f55f645d318f649bba8f1428ab5db26be321e399aa4c37f7cb5279ecd94083d07c899ac27161fbe4a2b7c4a87d9bc44747eedb185bcd9379140f6594a4b32f890cabdb536170096ef777aa95ab9f94ac203cc95940414dd74585bf91cac2bb2fd42d748434c59bd199ec20a7b8ebf7ff61c47d899dab68bf7a5175bf0576eba37dadec663c768c0b048ad133f454df0304ecf16d9330fc1d75a09d77636dd08f3efacaa09c9fc7185a789d034990273c16dc3084184be9a69eb811cc4c20ee7050715ff805f3c776577a0fec8414d37a753b916757b5f1ee407e41b5f05837402b2854d95e96e6fed7094ec6c4ee6f86b26683e95742c767de469cf8ede0e17d281b93916e6ed2aec540763726ddb01fde656a203789726bf8603be63845a2997ac54832a58af8a01fc44f43c21cc2b386db919aec78820498fb98db41de0f8b562dfd475fd95e68c6f0bbac9d2c12b4aadb77628b210d614e4b0597f753e5fdd8cca8af31fc001f0379a70d61c7c8fca60b80e63eb27a50fa2294a0632ccbcec42682361875c4583f4b65c7e5e22d7476b956dc5b044a041167b224d6b8ce002e9bf6cd1f24ba8b98af274d07cbf3ce493fab7716351e930687227e9a17dc5f33803d0025787567b6a3520a790ba90d0741912052e4400e30e3a1ac6855f3c01204f5fac95e0aba22ac50eb2954a0ad4248e5f5f159a89675d78891416302daffc05d54895365889d8e74124952c71002f8a77360fac6e21e0d21cf0bd618628a190f715e3d8d882bc068c8986165f84105f35e2a298649b564dc0c0ad269ba38c7430cbfb536d28a9b1811859071a05a22be344bd0d8ebc22791f0c350a264219bf2fa13c34858b2640ff016222de6bc17be88026de7f06fc9671f19f2fec9a393b10af110c14d2d10574170da6d3f418e8284e07a55ab20d0ac3f1cc750503bdb6cb35ba20c82826e9d9f59a42e54a8b74319c571d301b6f010e026d606c320bbd47d4fb3dcc760af0cee314451377d037f2c7c1eb798304fc441ad19b8e1524ed93578aa966ffb81801141b11fe9f6cabd3785b4669df936c7ec13cb5b360e848f3a06518aedd9d59c0e5ea4c7ab18f95b412bd17f6825750db904d7f2b4efce7341d2b7d01fd345bf486b5fa1a01fc9748198f8ecf81026c35dffc8a98489d7856a7ed326054e913d57bfaf07a91c0ec29c66d58898d6f338490c6307071c718f62ac26567853b41bd242a8a3a6134840d16548040064344e7ab4b7a3265fc74e6c7b00c36162f2620773e00d310ac87f0081cec714d0f40604a2e29f18a2ff8b75a8ad7816d738b2986d1344c93758fd8999e95c2137ae30ae2b7b667c3240d636aa48136a4cf76c00d5fe00eeb96ac19c7c0411bdcd64a3c2ede577069b5eb2070bbf330117cee305c9e422ec45e8e12f14c41bf7cd89cce91ac5c316b5c00769694cd96a352bfe37f858d7abe4b766a014770b665eac59c9df487075827b51df49a86acc269c847206edc63f3476704dc59831f94069382b0baed52f6e6440853a38db85fc0177ed438876054f7c553fac5c57b72e2f17043b5138635ece2fd9d1a901894719ef013afc8685d647665191462727081ea38d9e939d7348415bf1a3e59e8268f8bbd07d0bd92d4b2eb1390f7b2628f5007a07e3765948906620219ea24780743046b5b67673d3f608c55bceeac031c2cbe26722eada9662b63ccac2549534beac0ca33b9d35d0f57f45de70ec84f86fc88399395eab1d11b13959c6ed1c5fa5c59dcfeaf42cc35c5312b7266bf9aca76912edaa76aeafcfcb00a994ee50ad828a900cf13b43e0e3d60111a4586654256c9a87538fc761c53901c12850ae605b4ec7a7d42064fb1151279927b5d27325d87a2528ae6ea952fdf98232688c19f5b07d32479a0ca4807ac647337090384a7f16004007699183784ff61ea3e0bb5547b085ba382aa28c35c585de0959a4a020b083249edd8f311be06478e213ecb12a814415be8f8979337e06012019c800e720890cf91ac2c79ffe447d0909173e5e8f3de1e8ad8c4bb9416eaefb6b1eca381744e6beae23a92587e8d65aa8256bf1ff76838e73a32874f13e260eab3ef3d64d38779c01470a8218f977c44233fdf5ca3565a6f54abe3278eb634413dc77fcd7b10ff01acce0b7814f70733c721c7577c916449804a63f4d6a08a1ec87d93a3cd650f24da47bd5813d0b24c62852648b0efdf211abfa463c2ddc892affa9bfcb103e24217a180e317081d1afe2b9cc315a954abd7cc3316a55bb4fa00a08395078a80fb57c4d79b9542d766d9df3ffb45db61004184b5d3521f0290aacf87459256184fd42b8f411251a1cddeaeb40cba622723db394b63180f431673f4f361a7b166ffa20358fa980426801ba7bfbb84f8fddbf005170addceb364bcf5bc581d9631e001f7ff8926e8670fa34f16dc61e08a2f405336ba1cdbc4c330cf6ffb154145c9fa060851dc0d7db7bb44b4835668ab5a8b725c353ef40bdb1dd1baf820c933dca1ff3f3856255962aa9faa83258e4127972b0c1028dd9c831b9a7c6e56fb6009abfbff3d8dca4a317acb0d19de36ce50245d208b1f6bfd9dfff026b965c5319c06ac1ff5fa86e28027bedb41ccb08b97701c0c39d9b29443d844b7852e436f00351d6d9981f7e10abe7d9688029372108d4e6029a13e26a5f50d0596d0e569c311c39bbf2cedd40ce551ab32d77c5bf03bb73a7487194449621e55daf0fc471919049fe6ca41a1f1664f51912d4d1ef7610870000339180dcc6413341eae32f2fee011a6cf066b08085a7f79f6de9a525a3255bca94640ac906d1065206f672a48e86a5cb75dfe5ba8e86dee5bc8e86dde5be8e86dce54a1d0d49dbe54c1d0db5ec722b3a1a629763e968582f87ea68482fd7d2d1d02fe7d2819ceae5e5a322c631ea57095c6f06c0cbe55e39cce5ef2c0b4bafc69fd17aa592c140c0f10c896298760a02b1d846e23aef2b59d30966050c8ceaa57f52ede324e8a54b754ff3bcbca860c00eb2b3a3728969e91fcd8a618162920e76be9a73582ea78e58d26b611ec5253797d7ca3071ba523bccf292a622eda2c21180e2a9c869e6d0504b0be5259946d3e845d5c204033363a7d12d2710ebe9c06b314d7b4df0cd66e43d717b33f29bcdd59baba9f488830cc6f3e10befe876abceadecb137d746a585c2ee85a9889f4c274e8afc6e2ef105eebce8b270facc1e54909764fecc9f7105b3c807b76576e0c66dfe6005d971e14d1e9e1168af7a44e08500175e50008f1efb693c07605bb9551941871672809e0a7e5d0461782da24b6e2e8c8769fa5b546e3f7603df34111690bb14853515b118354aa87ba857a8211414134ac824c564b4c28d12ba12bc1af54b4e58195d12b79fd44c454cd32618f00d4a08254483a1dbc91811ea5515319e1ea1a0867a85f56042309eea9eabc7f65a4702f05a34647429239634155d981acd92e3351505612af29bcde534fddacb566eae544d8d4dc380a08d099305e60476e695e7f25ade0c192cb9b93cd746652b7b6532eab1bfa8c7965283a9e86adc091d4c0ccc872a9a9142c502fd4a5d54b1e05877adc9e860493bb313e4b2129789ac10b92a167af00a50c0038be41edcf90c98482c39d638cd7c2a4661c91a97279860e9b057e3a50a1247f4e0ca25d906485c1a482cc9423ee0445baa711ae65ec202f38765c4112f606942dc59c462b12c80baf359fabbeb0e037718d767a070bde42413b570e723b771eaec9ec425a790ebb3c8f5cfff08299e5c7feaaf59b1e1fa5bd08a91ebff56caeb3f4e01c4f5af615d7f1c1a4b5c3fe28a0fae7f0b2ed870fd1590e4fa114378e2fa47a0c88ecf752aaa5c3fa28a237ac5442c1eae371d82144002e06a429412e050430fac08a1884ead21b835886f1c6744cb47f440032cc880055c2538524150835b7f0607a6ddc00da590ea7335cefacdd4d1f1702e878564fb5c8d9c4357d34680c2762117aab0f7a1430a559898e387d63e745e40ccd71ec690651dd2bfd3680f040b4d127b58b1c57ecd0882659d6ed65161624d8fdae3388df62f48d89f52831b1bd05ed39e9bddd7ea633615e2a6d16d274ea36a4cec41fafa3d36f0b5b048d53dd869eab3a0d5dfc41e55ece143c7870e8fa921969a7458c7abd3e4588e318fac1b31b589cd39bbc3b0aa516cf6a0699ac69aa7829661b31379acf488ab3a4c8465079ca6e9e8a75fe9f7d3e76cb0f352cadb3cba140a97404f0f625832c59223288610c2a9d061a6a088e50a02941b7210d4811cbe882386308a12850c920882954313408a62c06148088810614237a003152e242e22a080c10f20a8067420e189097c50d0b2c10d2a00a23c3abc943c31030e76406589202cd981884c153fa8a1881834c182223f104141c90e0f21986283264f9414d1840a70706da086a09f1e420406296a30031139c400073824a141872ec9eb060b8cc0074536507da0472e1151b460062aa07243901e0eb0040f452368c24813508abcc8b2a7c8872952fc005291501bea1386520443a142e7a4623f4f9ea5d9820f07cb1f5aee776f85b9b538a390f656f74dcbb25a297d99ea2d61999aae0be14090b14969e52aaa5c9cb75e191a63e6ad6bff99b3e9ec9e33098e9999ce593a0cc700a5b0fcf3c197e56f7628649d14fbea74d65aa9b7426d1c685ad6cd23b4f95aff0b1496bfbb2790bda924b388d5165e94c0f23b539fec3d9de0381a2a2bb6bffe24b5dbddb49b07d6ded26e302d63a7e96c104add3dc35a785102eb357c61ec1c6cbb7059172edbef94369d1d03ed28ad9492a2f0d33aa90fdf3f318c39cbb2ccdd356650a32d036ea36269785dfe1728ec7c77c77e700cfe972017b8fc93b3596562581531ec5db859ad3d6a5e318f210809e7349217188c3614d6929e2fb73d8dd63edb5efbd2f527597bb7b777631628d6b2fc60e77710a9db054c299d31a899de24649b96a15eb65e3ee24544124f40a2252682ce6908aaf3f5f3cd99f1ef8f1a10a2f65f44bc39807ff170334f43f9e36ba7d26892135a4b4224acb7ac44033ba1a0305e127df1a08b507fcaa8992102ce0bfa99e7d520514aa2353d8383c20b1758d55edcb0aabda6897eeb6b615f0f6738c6c4b0ad56c7b47fd0b962240ce76bc019cea998c6b15cf7fa5cd7859d9d2ce341e7971370dc14b18a651aa92b96691b57a39f76b786d2b61a52c75e3f8deee87318d5364e64f176d78fb1ac63f06b3a60b35217dcdd9f597376779f71f7ce5d9c09a5163c763ef56ccc2bf2d882c7d6ffe63b3731f6ceea9c222d4d4d386691a43eb4c8959791942641cee341a8b886a020699f665d9962e4a4c9143279ca1798e8462a05967b9c49819d3cfd9b9f29615d7e22213c97451e2770a5cb443c47a92be3d57a34c2cde7995006375cee91aba98372b9478ed1a19d21388db04c74812997ec529cc39cf3c3df3b5070ce1a9028728265840f11200c01ced9243812c4142340011538d426496234bf4cc4a2413fe1b4bb2f57e2019ddecc4c359fb4d21a962c59b264c9923997f8f7f420ce811cc8811cc8811cc81d68083a63d6d4b4cff83f8cf187d63a35ad62d5378d096666666666666666666666f688d05aa3736366c698703c43d664d4b9a971a40e7bc658fcf04c72f7204d9a803170dcacb7332fd1ac96b04cdb485ce77d256b3aad6041b5b8801d92598764f38beaeba7dd4dbb9b8a80ab1bd7c4cea7a7d30a4e816085d784a4accbdf2af09a3033339d9599a9b85a55c6035d4bc7792ea5cfd2ccd57297bb7b3530bf9e611876d462f0645af13e30a04da1585a68ad152c5d2515173fe69462efdacdea6dcd8e323f3fa7eabc2653c60376ae260b19a694763d924e3577161dfd98a65e93b013bd1afc220d9962b5babb7badeed5eb57af492a0476bed7c4858b172fc65005763e1dddbf1b47198410828d701cb2bb978cd85483eed2724949d8f9252316c5b2e274a7c996ee3462bfae64c47a2523b62b19b1dc9dcfd9ae64c4924e2b58502d2e1dd8a5ba97d4e75fe79c3cbbbe5ca7945228cd32ddf9e5faf7251370b3273866e61766eb0d91a29d0692cccc586dc982c8829e1da5f58a99d91b62b28c58f6177577ea0d5133a321e60924bdc5914c1d57ddddadbbf7ecf4f3acbb3b35fa403a44844805f586606676238eb4794490cccc1e11cccc1d7dd2dde751c55c8468a1f5ab4b27f3013bdf9d7a49692ac8cea7ef0dc1ccac39f7926ad5cb8481f20cd3e248997ac1b24c158405a960302c080626860525423b10c34206c3e64422f31836279217d50c966541574022984b104c8c08732221e27285858c8b39911071b922828b393be07225145bd0502444e88fcb951633349408fd71b932d2605916e442046b09a219a1c80824381d7f7cfca1e30f8c1873fec4704ac28f93f04349f8294106c57668c102254a680009ad700368a89258fde3c811a08a55ac32611d19385a022021cc390f5317bc2ed4b3e9eeeeeeeeeeeeeeeeeea9ce290068bfdce9d3a74fdfa600ca66285b5cc08a6129269549eaa556262f2f2a183fe243625a5aad1816a82254510a8885cc570cab4b5ac2138029b16218132b2d472a8ac98baa85bb9223282b303133ee4a8ea0acb498711f82b222f3e10b5772c481505642f1851f71209415172f2a8631411da92c4c5e8c3e230d751740d40590bb001a8104772012a8d300511a20a7018211c36b919a6202fa0f3062c8a8517264480925c8a8f1233e44464dc5a8634ca8bffcd414931a9b5add87d89850abfb10134ea8d57dc809302d8660b35bc73d1bd3c99cf3faf0f98011fc9419c65a0644cdc2d14b023a0dc9067666d909c68663f4cf1a147070328cdea021006eb980805ead5ae830070b43b62bfad1b2324312d46307b55aad5609a65ea64b355b41b3355fb32c5530d3a8080626860518c342a645e651747b6d574a55b60f4bf155563124e48350eb47aca2f5d252cdb4ca1f9f209fd64bbc023a71f192cca01904132382c9e572b190710184b519b55eafe9bdaebc4468b988a2dc86a6680df9cdf67a852cb1058d57529657a5c5da5edb158c75bb484f4f8b191ada9a42787e785ee21570c7d2741170800b91d74e8b9764b282583423bc84aa6809b98ab446206133400b21025c68bd307a0a8011230717c3338a020e8e0068ac408277e367a80419747b6d2f2235e3482fedc1cee74af04a1936266c2f20aced8a0c7cd341411a68b58e90985ae56d1697a9155453e3953626e8a0d504df78a5e98997c722e2957eb3bdbcf24a076c5a26386d592d486bf9cdf6f2168561f160fb31950927b48fcf0933260fcf0c006c556caf6d0abec130d1d422c9704d2dd351075a0080916151241ab9f85e0d1d96d4cc18cab22ccb9717150cd83f483ca41d52909e8f45daa9c14959962faa16a6d7ebb5b98c5ce507e4637d327c1a2849413ed6f7a324f1dc26ed908294a49db22c493ba41aca57d9ba246907e792763e1669e7639182709b4ca62a290f94e5ab6cb98eca96a98ae9d58aa20402dfa05aad29483b1e4febc855fddbf958df8f8f5596a5df9076483b2569a724f19076ca2ae5b7f3b1be1fa49d8f75fb493ba420249e5e995e6595b21401098e63f621666e00adadc3cd4ec53aadd3fd31c7701daf00c735b30d983b93e0a64c67344c5df0ba5c2dcc4a3bb0968bba5a98314d2dedd058ee8acb651d0d4fa1e9725b47c33d9fb85d8b5c5a85dc5983dc597f5cb9f36995bec95893962417b919cb6f307ae5923363fdc878825c72b6b29e5b85643eb79fca7d00bded22d6c281edbaebe98af48d57763dbdf2daf5905dcf9d474dd3efe26cf5cf39e7537e9712df644ce3cc32fca7875e8d493f961604c7dff77ddf77fabed3f77da7ff4eff9dfe5bf1df77fabe53e9fbbeeffbbe2f6479f00be735994ca6effbbeeffbbed3f79dbeeff47d9ffd4ea7ef4f2751b5e2fbd34954ad389dbe5f21aa4ea73f9dec77fabeef7bceeb68c7629d9f25449d4e618ed2f77ddff77ddff79ca9337ddff77ddfe9fb4edff77d3adfe7a7ef4d7f12c9ef4fdf9b9efb4e9f29a47f0aabe9e9f72cd21ebf678bedd43001e78310c6186318e6363fb6fbfb27e738fdbaa374b1a759386f7d77fffa343aa358e5771b2476b6ba6bfdb01820e61011902977e632111027f7e7f2e59bc5d86049f03ac730b13a78cc146ad81cb1a4a6691ca91bafbbffd8ab98202c096a8fc1d4eab50ed2f398f785f166e3a671d9fb7359b8a374b7d7b22c7bec92886098d7bd7e2cd4918dbdf287d12b1da49a199cb3bd3f0038a7fe8e924780fad8479141d1c54861a8c1306f267a355ad53ce3d90f389eb9323df31bcddbc5d92251b16119fd0f368c19c52c2710f86646a330c5edef9a234bceb24aafe6a522cc6cf51892e36cb9c63dd997d4a39f4c19fda45238b349e562df937d7f3fd9bec5ed39f149cee8de28e0d62e499ce591255ba8dce62c2b393d154a978ab3d5e39c356405aefbfc6f6e2bc8d8f4a7d0f4dce9f9f434da743a997efe3c85a629b28c7e434a8ffe26530f413ef8f05cfb2725822ecb9f8880f45cadd398ddb19deb7c534f61ad524b8ee19f6a59dbeab1c854cb546972e7f19abe1a79053889758a49ac45363e581d94d5a33f8f79ddc607abc3f43ce63dbd29ac42f5498f363d749867a33e7df0f4a727ece90696fce94eeb773a89154a8fa44be9f124922fccf29e9ea55727310462abab477f2bd6a3ec8f3217e798fe664e984495b51e014e6f32892a9bb57aa532954ca637994ee20a4cd8d393b56c953707e798de9f3bcd1b02b12bfe6b955e9ddebfbe38c7ce5ba5e09c1db3bccc31dc8a2a96cf23c0e9ad15552c2b957df247c7717a1651654515c66bdf8a37deabf4e87f0a6198d78a2bf4e8d74f7f0aeb0f3846d9b24440762edfd3735d9732ddd39f4e3fd9abd1f77b2fc5fe4eb58aebcf3066eee9f42c936f6ac934fea7d373b4fb42d4d3b0f4d33bbd97154aaf4cef5f87fca6e5f976f6fd5349589fcba3afc9f464fd5ab68443d77b1ba24272deef0404bec9e134ce758aebd935895da4477f1b10905e7ad92b969bacb502d33ecb5ace22d6326b6d2bc4d6f58789c29a441da737bd15c9d3db37bd3d994a22d95dea71514856290f9877ab240bcb253d7c00bda497d7f90698952b1a2543abc50292302e20cb0726a9e41cd57c4981b4b62881a194c8fce537fe624a596ee4a5cbd563939ef0a1cc27fcb95990d3905cbdca7278825759111efb3b7382dd9214fb007a95f000134aae1342dc7625320f6439d02c872877663e7ed3ee95c732e14cd4fa53baea5115bde54224d30c2ebf1b293195f0b9dec2a61875489caebf69883a0559b40fc46e70f9bde54cd46f8a391318f653bab0238c0449cd1bf2219edfef454f7a945b7f4e0e894b663d3e94ed70a360459c63bafd251fa2df9f9a92f55cfa2c9837d9f417dc7629b5dbc0ac47339d30cb393c8f5c6cdff8d033975a96b43fbf9404e792f5908c52532c699d825e547ff3a1194f42fa8d94f5643d43604ffbbcb0b83584b9a5242c3933349b9d52f16aa1e634f3b954bfcccc874b495832eb21b90facc3245c8a0696f41d5b54fabc8e23715234b0bec3395bf77c6a84812244301084ca108d9cde255c8e351853863f7dd23592488297866f21a9699b48f6b89bcfec993c93f5ba52bae6aa0eb1100e59336180a24679559f9276478f3bdfef9c22e9a3bd1a949643d3e77569d18e086497acc08eec62e274188dd559b4bd88b890167af467a14711dcc192204f76f7a396db300330a790dba17bf6eeee2e64237d5cf76a90486fa33ee97ddc0e494af6084921f7bc33e4d15713dd8629a38fb869f89d7907ca4c293333a594524a29a53578f2a208ebcf0c85871b482492486e9b6fef9ba8815e8599293353caccd5293f531f9792c4882c8888e8c91320209e2620892e5044f3d666ea0238193004cc55c8e5e79f9385660d7a7459a847fe394b336a38468d9138975fd3b46d6a35ecf69aa6699567d4cca03fc31a6ed65ab5eb4fe6b8e4e370359cd12316d6d72a7dccbbc1778a3ab2d744b2e662f4d65083808fbbcdc7b82cace9117b2da4218e5723cbb41b35a81f62f0c39ddb7b4d8f367c78385c84808f3b7f7baefee4481ef3eaa83524ab8b4f7a2f85f9cdfe2471468fd877a86313716e468363609fda81ad338460fd79064eaf3631023d622f80a6c1beeb5858606066662e86f1e811fb1e31a1d905a9dff77da22ac725bbaedbb6edbb19a45c6ec165a2197e5c6e7a36e8e51eb7a73f9df6f5fb5adfabdfd5efbcefbeaff5bdfade0c47d7deba52f1cdb22c237d5c98540b8a85e5643275dea7be5aeac21da5efbdb0d4755dd795baaf863abcefabc7bd74dff7799ef779def542ad476f10c6eaf0c0947dcf6a9aa7f1f53cd1865aa8c3fb707862ad9ee751805251eb42be29607707d6b7af0a87dfee5538fc9213b89da85defc95e43bf34d4583c0c70e66ecf240ccbddbe23fdb6711bdd1edb8ebaee3faf4607f6e85fd4527a356f2bd17d7dafa2cafb4980ee6b17f58a3b0f47f7de78eb5771ac30cc5b4518507a457a5f7ad7e1bd50afbaf7e7489e0af3eb7721dfcdc3e1755fb9f6f1ed5177bbdb8b7059745ed8b7f354f86ef570946ed5de6b8de318dbdb20b13d58afabef792a9442b2bb5d065a979fb84c344395cbb1e86c7cb06451f79d4d0fdf3d8f7975743d390cebb76ddefd692249014dab21ed0cabb5725c4fe6e857eafeebde1335a7d15c48824797046f8e4b6a2246bb345958e17886d4ca5b27063439f630da63594cd449d73daa7bd40a14ed01fb0ca8e766dffd70312e8f4ab9b87c0bf65a14269c5cedb7204097a35c26ca40793917efa5707aeec5382386e18a4fa150dfc9782accae63e1e1407d978dd04dce992d0fb6bc80dff212ce2b534217feb3888979a137cb7e6659968529d04be1f4df732374b4c741d05ab055c4694eb840af00f4b377696947bd72b9ac78919c974cfd8ad7a4f48aec9ef4f535a35ee9e8b4a25ea154985198f841857c593c1c9d0bed11f59c16e437d315e437aca3d9cee4a5d0d705057e0bf864f62f5f1f0b72f9fad80fe7a8be3ea68473505f9f43a1fee5b3909c1765f208807a179747a19e69b40a25aa60a017fc1da50b3e0af520f83e39674e1b24b67449ecc766074b623da8e76a6c8c30d613a4c8a50f7e7b38e883220cf3eac868b44fafb217914ca54a9f872325a242be2d21eadb7b09f9bab763de9696a73ca93fa1be7a3674b43c8f795d3e25da18b124d673eb633ebd6a117b7a058a644b915eb9883a5adee53d0f478b8b4d373927051da7671b2396a4d84f0ab38acb8325e90e16947ddd1ea8573abccec3b189d96b22c7b988a4868599a8f588ba98082c221983ba99cbbbd967d9779cd37d567a2e0b390cd8cc468d48a3310cfbae57a4c7f89ec81917e74e52a301a33c748706e915ed9d0fb5201ca37eeff88d4603d3d4efe15623b70eb9b572e20fd0ad4f0a31a01eeb4f8f35c88956b52641f5a913bf611dcdc25065aa4c5461bce3a539aeb63dbd59d6f2c1588f5af842cdcd70aecb148d50b9acdb710e110c28b79f9c81b94e9bdc50a0a6e99a193853ae752a97753b51fba4d879c916ba4365d3f0bd43af1ef9f6df201e9ee6199b758ba0e078a686d9dd9dc3176a68ccd0346b41f071346fd21934a8a6590b82ff22066ce0f44873dc176aae7fd3b8417bd29eb427ed497b522a52f7afb5bbdb51e8b1e6523a03c7983b1c837e6a071ab69aac8e7ed7b1b0c0c0ccccc02061f4e8913e8d4e393179362a8a20ca810ebe5bd61dff1fd0c7f6f0689ab520f8cfe180de518752446424a535e5fae63aba2ecebb0df98da6590b825ce3809ae62ea6d0f5ff12ed04b9fe7f857e08b92c24834b48862757ca944b3b231c34b7b27a4c41b1e4f612c118232c09825504b7571121d2d10e501157a5e749d7faf1c339449cd39f10cee914146d06102c2d2f7f9afdb1bdb69ebed9341ffb0306f536e4e4f626f439714db3f673e2c488be7ac5d4881ad9f897df68df1fda34b14eebf3cdc414fa0a4da1ad64cfc5edd563cc092c3dea1509ba7a456eaf6bd4ab6e6d2f1005cee9fec139cd39449cd3429cf414169221a7c2439b704edf1acef1677a59d3ac058dc6ed4a8ffd45db6b9be284cb8fd33724960d0760938265b48ba9234b7697ea80cea0b78d8fd26a7239ca1246245c8eb204d0f52addd1e392dc751b84eb6e83c43ae62a156e1575f8578dc7bc241fbf611d5a3aedc9f35b2d527c3d7d707bf69c73ce96cf26029bf9a8a4ec596a620ad58c0040000000f315000020140807c442814814a5a922a13d14800d7388446c623695c7a21cc8811005310619420c00c018638c31cad4cc1001508209d9025c4c5698bc193121fba57cdd2c8c82d0a064948c33526d6e914b03a00d0c461b52b30fa9aa9efb6c0c76b92aa861d2d14744e35deb3e4e142a8abd14b9595eca9bf938e33313393c8d2e6ea22bfa5342fb14f5f14cca73a8a8260dcf366d3df30cf71ad4602ac46d5af8757b32c3912a0de47eaabd87a4c6f66ed34a1102999e835f4d11a91497311cc94ffbddb11ffc14e4d2e7c299e9c001bc049beb090736612e4e9307cf0d5d9b36025ae33160f572cadc4bdf85226f1381021e154b2aef5dd4cf6629abbb0a6fbb19e804de7fe73bc7ac7a00f68fc884cbb186a390e56b6c747ba2dea9b75f3bc0b3205133456f5d0cbc06aa6a3a2f7ca10922b9d32e91ace469d40bef612f316cb3d29982a5ea56bf210b77ae3a44ee47d368876dc371b262cb130d6fe61f644c9dbf5422fc850ba0860633542392192c58e56f97b51d1eadec7ea2105bb05abede55aca0e3b3b4c8666c4bd8093740b79f9765d956e827ebe8309afa8a1619c9757b793a52ac8466ea82cd9fe41354a6ebbd6f8a5725cf9a769ab08ed48dc75536eddbccc9d493b173fcbf2656361e00d410aebb00561eff334dc038c63191b40eeb214c4a241b2861193d8a2360d0e4f331ba42985e6ef7990f01e3d8e510ebbc9fd341131777005bc1f15ee4fcfbb7eb5923186a8ba5dd04020d7d86428d45c7743fbfd26857f5a012df7b1868b368826041e1ca55d5da18673e02c4f7ace32092afc7cc694b01529f1813af893342304ec6921c9f2025e595dfb56f767a2fab5e3183458df4e5b18c241714d3204aa1ca9bbc8a64a5b9befa229b6b6dcbdabd2cf73828aa668a1cef9804da0aeabe967ba7adf49153db5674808d16f413c1d9026d7652733388ecb021fed9d7ed12fb2b374bea5db2740404d4f21ed2139df3f039fb92a23112418b3968e6394200e6bc4f101b85d536843312dd4fab54d375e41956a342ee665ae11c0dd7241d3ef42b86482dbd508f6f240550991936fac5aa6381133caf0368b5c43dc8d6dbb9be0cae8bb7c952e3c5904cf18bece405ede463fcef77a34d145edfcf76d68849b85861de981d78d83a9b32e091e54fa527850d62a10129d1702b2387a40bfc882cdf142dd14fe964053822e2eed57fca503bcdff8d2112f9140e5e9b65811485b039aeb582819a21e002cc0f46ad826ca062ec45a0aa58fc3b527c02d4048061cdd85104ae83142b775c4fb72f729e4f1a5034ba5b20c83daafa3047523c6546c0ac5667d3054659e2377298b27d98f50d8e227fa446115cd6eff6eaf56845c541f9b27dc01a3ea64a9752c2ef4b7fd7055a04f06290a486aa66b14f3153f0467b8dd9c3b5de4cebe7f4396ec19e67e077eb74363201ada92c64b5313b972f20aef2d4408df13d05cb37768a2e5204a11cf7b58f42c9cf0470dc841cd29d462a5da291df5f4950eb042909985f0e63a2a6d7196abc5eb081530f595b31278239cc90bb375957734f70ba687ea04bd1387b07ccbae13bcfbd595f57b296f7d0731881abceeeffe67178604d2358ae0109d84803662093e3c4dbe34c420c3ca1c6aadcb928bde51afc18776b3b0269a68c79f021b3af2347f5d3ca8ebf8123a96752ed4374479ede6efd252270eb97a4ed57cf38fc82c859ae4adac0addf26dcfc58ee2cfc30d0c92d5cb3cb29f9b719e05899960b1343f0d5953f074364c5bcd936b29b7696266d83bdeb2446c4f82723ccc06256db1b3e1daac59c7085ddc406c9ca81cc17f4b5d7d1c3500d89fc872a8e235a7aaf296cbb304ada7fecacf481936f9ef7c734f0956460e353497e6d7dcd44c04507d46a3cfbd8d2155e92e4fdcf68c98698624938f2713e359349b0bc04cc387f3c133c12d082e8a56b193e61581a1d70c802d31db9ce5d5cd66a6ed27c5b217561b97e6bf68fc9ab5d64d3a265650d4caf719a69763838999fef033694e72048334c563ee01a8f0952f99ff3a7c7793827b03b59fa295c836e9dba6a19e4d6c3af946f180a31e960c8fcfbadd58d4444584afba3164a130458570d3f8008abff676f9506a62048aac68d06b2606b6ca7ae71d091cdf2f636255d9d41653ddb6c5f5690f44c530826738d319df2d5257c74dd1e953b72169e935546922cd142b355f1657e2c500daaa809b04aaf04133b2e6e12f6c59f58282dca0df89fd0859f0ad7bd5013a74df96e1049057e96f8e1fce2195cae95ccf94d8222369e8c5c7e8354184e86b120de6018e9de6a1507235aaba807a7c0190da540d7296eef982cf23ea018f18774ad7f88c4f266c43f44e6b385c61507464c22b01e28984e593201a70a0ed4a6dc5711be08fbdfcb8a504cff3055fcc116b5f8b67b01f04476879ffa3488ba2d007256117411f36762bf02aeaa3bff2e2dcf19a7fbd188dcc223680cae48ff04acb853de24f1bda9c1ff3e5bad4d34f62295916429942943dda452ed3e457daddb22d4b230a9232265f11cec758df2900cc70eddf9acd0e2fb336522e118298b053c22ea5e076cc8dd2881e1214706852dd78d40594b2f0a8c292953112238745b59e93c4cc4b527f6acb526c7909e0721efb8b194b30c8e13d8d5f02920a9eeb59c3f4a46a9562d36051f5db4aba3f6138e50048c7ba58618412d1ab725d529a76f49fd7edded3b5d89418f52c66a34751bd238479e2e6b941b04f7cf2bb68f3442d832559380ae00baf29f7f233417b8286e0522961dd76bb38041442ab877134ebd1687ce9d9159528925f1f519ff6dd654e9270782f00d8444af3a46f481aefc2c04b5df27464b41eee3596063407f823dfdc38e8b337a8983c08b43c8c9b94b1ceb1486f44776ccd30fb25e804adce7cf0464648ed8c6bf2c98059629c20bdcbee7ba74ee840dd980ffa4bd74ff3d88214cca1708733becaa5521b31368b6f266cda49b4b7d73687fbd652f724d45d3d8d448711e9c9f1ec486837cf61cf2cb514e71e47850ce3b153929bdc519e2753c894a1525a34ddb733662237636fa9e11a24fed1365f03fda89de0874a735858e653a286bf180e189b48323931f63e409e7a9dd8d71379a2d2477ac318df113fc2681b6c47d237b444663c311c9ec49a16d12fa54babb8405b343b3a74ba9c2e1e96b65c492197ae666b995d093370431c4f873f0946ec813c82104dea1d7a5c37d9fa394315e9f842fa7bcefd4af8331b0b2ab789e932d29bbe9e96da97e9918a174550f76b585471be759d6afdc42e05b2a9b6a7e6f86738d97c70dcdfac47c033d10ee82930b4376c5b9ac292511776c653871784545330aeb66e54ba3c3fc57e911178bbd931a18b18f2aaae27205938c16d33f6bf94fb4664305b068a9de1f1e482ecfc112dc5c089c28c5b3118034cef6ffde90944a72dd9d36d7788e0e11b670991a4eabcb512f8851a65f129c59b5603738a134f675a2761b2fbae599637fef991fbe4063fe2ed4805589987577f16cf81369ea8a88b0f669e7c70513e9f99b872da1c8e118786bfbda2051f29f16bfe4f620eac1c8422721664710e967345b336da01d667c3e825d2bbbec5af0e048a61e3ed9a5db5da43be9b5d1180c869853699ae5a286f69ca047a4f63bbe1b3caf291efb3f7747d282dc82df41244daf9ac417e601599bfd8bdf4f32fc8ff0f4bbf17a35c68c1dcaa596892a9c26e54dbf18c05980c67dd487d7441b5f3f23e43bd34dba7e015afbedc39ac7585fa1398805444a65ae6b14e8e882b899abc5e9314660ed1879adc3767ff7e38b511ec1ebe03a83ab97523daafae1185a3b6862114ad6ec0d8d9c0c414b68c91bcd40bb068e429447ed58ecb22ce0afc0de637c34ed454e9e7caf02456673be78aa820f386e7a92aeb4995268b57b2ec26524a38f4c1b9cc6e89cc8aeb6b8c21ffc865fde9ea58fac31f3eba98616da7d4dfc656b8a9fcc3b0187cd4b63470973719a97ac96239227ab6a7bd9ff4d1b9809d875639141619e1a9e1f527809b3af6a4661306c72ff836f004ddc290cc6311c3f1378e5f029beb1ff8ac7a24f7ca1617ffe36d6efb58df84f7a629506e32929cdef4e52da24a311dc68b38e817f287d3aa5c2b48eda2279619c87947dd3169416cf2a19b677be61d84249e1330f7e5565c61fb201b2b78882c5f916894be61604fc389c9a4dd7089aad466cf75c77d94a3a66ce74e3472da3198698c622ce3e88182ce30041a6c2728a4fe8247222f29b74a8075338804c6ad9c85410e3a1a460e22de7ee42fe4bc7f5f6de4a52b756f89111c538e6a81abaafe5cb7d96c99db5a5618a5ecfc1b84794eecb2ab879def07784f6ce8dc25e05adfa2a5051df167831ea2477d630ec931a1cf3cb286a9557cc9cd3b9a204f10059920ef58f7a48be58e3205d0ad41e06d4387ea176b5ecf770fa5e66c44d091e82fefeb104141e506028cf2698b17c7493482971834b94e84a137442006785c5b5bc97b219ea8f06412addf7fca972e891f1b25574778142c2f616763742e61df600a08a35285f66841c1edfc74c9fd0e19fb61ea2dbe59dfeff0127f40df742aba9278155f6260e83114df06397fca9cf0fac06d19978daa5cb38f09f3aa946727f125b22343f91162980aaee8bae90a6907f8cccca98276482717b5cf05128c8e0516966c135954832b821f78f614633204277e833db60bcd185632544c78f6cd1d71b1c3990628535122a4801a8f100a7412e1fd5a1725ec85921402908da368ca2f4d2e538879e4a6cd3f670afb77e26a3746c43d405dcc3564d22cc7f8ef4f02e41cbc61b6995853c800049a01916599d4b440a1cac0df2804848aafb676af76a4847f033c3dbb2604cb12cd40f78c151f6cab27d221fa59794e6441f1e945c0ca84d142206653f3da50a3b6dc70bb408ddbb43c265b73b68cb062b98c5621bd1a0b8f45b42c25c379f8f2a22fa1c44da9254c7ff1e55841dadd451c2206683ff15258b10e9180a64e4db9fb81a116af398d3aeddd95109908c9742c1dbabb0b9f692d40849adcca7218790ec9c5a380b708a5ba490bda5d1e0ca041c12afd4e607455c1095b7978742619fa3bba10c25b5272dbd9ae43f0872824ae97eaf44a68d21ecc63000fb19cde2e64ae4f5fb5336460553c359be2026b76e3e25620579fea81654419643eebbea2a2bd08f21ff2fb575795f931fcbde138a4e57a81874179a90a54f600a83946710fa745a092f319a7cc4c0f76570ab96e40d6886b3103c60863c9d8e10e8f8f674e65fb0bd41a2da2165cd120b595fa94e5a34174e8a39cade89cf01dedb8029e2bf0abe683c94561c46a8e2cda0096cee283d21456764d0483a1d5a3be4d3e360be005bddb020fde14ad02cab1a748361199c921440e951e7371ed66bbd0f4b1106c2bd9e3d66ff23e59ebf679ec29d6bd4f840dc2fa27e72d9ba8039cad2a4e5eddb1f4c7c44542d5605a56addf84ee65a04cd4015a3757829cba240517033051c3c4e662cd8b9e193c3ec176a8738ed2aa7c87e127826ae25ac69f2aff7a7ca8f5f50d0093ba4645425708e7e32e4ee9a7fd245d06ab58551802ad894a245c327197e619623d73872a79f58adadeebc7b5d5bcf78b617e6579ceeb5fbb75b67386362d15c6a5d721b37128d2050326d42bca078ae89a06b016428282acdec6f75705915715ebcf268fd2ab72e4a576f2c2076d9957768659522d61ab686d01d605045067dce7b7485456596eb7af39ff31fdbba03e000389ed27696e1a3bb2c77a824f8f7768901cbafa23958cf38cea71c87defb93a7d041cf152c5bb97890a72b0529d86bf1f95295a64ad0dcf7b42fab835442fda19c42445ed2a59e32a2d1233cf073ecff305b9ad13908d9cddf352d781a49366f2bf0be2970b2442598ae94783e6fe229e123b9e6e61f26f7b0319a2b2637c53b5bd4483b41420c9ab01e3c1e5171c967bbc6643a9a2a2eacf4ae2123160f979704cb7a1b1e7193ddd2d14780640ce1dce9ff229faa7d2ea07d845b4284165d539990395eb75525a4c5933bb410b0a7c751f0ccf18c132fdafcf5f6a37db9eff12038b6ea88a277b82b932ef6220646115ac8cc703dc08dd92d094d6fa478b162e8fbe67d07dc2a8be8b1073ebc6a2aac32011cf6d5192c402c7378f46bcab46d7708289218b80872bab1014c873f797112b448e775aac7d07c92272ffac9e237461e8eb5b9136bd73ff36d542eca654e9277c3186c90ebb81c1eeaa1b3d67a6086da77ba7ad174ec33899c5e76cd85bb42ccd787f60703ad576f88099e8895c76d81e244a4d69111ea5212d2aa17e8127904f8df8eb12441bebbe2989cc78bb5d16a6aa3a7366b54547ebbd1904c6114f89a4622cf7e845546d4cea91dee0b520664e8114e8e2f688f102d6e4532ef77e08727feae41d945714e43968437238b4318f270686f9debb00cdbbe7cde61841ca604a52b84c8bdc2d34dec367f0114db72f009bd398dd26de2489f7974dc3e1fe5aff269e050c4c326762439f0ab109bfecf53d1b3c3d2889eec3ff547058436248db994ff98f33eb1cbcdd92ad2d103143fac089ed104619ed3ba0bea80b87717dd1cf0057fc582e0d4341648f3c720ab8a1d92eed5a9657de3cf2656b76ff57d06d91558f38424d78ef7887dc6b20ec8f5b22c88e3e442bf643b745320f81930f853df9ba3a74d99555855c1ca182c6bfee7d82d6040f9e7453e7f444dcaf8f9d829b86e9ed09d48b7f87a9b4322373e2bee92c2f3a3282cd6c268dd3bd4d934bc17eec0c61f65f7a04ce21d4a0f90a68d3a603f47534a8beefae20c785b05f39bcc52e3fcf1ee3843bc92ae6e50336ab891f036082a274ea2a3b5ee2c3485df7d18591c00e5859b5cda238bf097309c42c25d758744800f6f02a7b109c56788514f99cbf4ab47db906925ff638a2525bbf5c72a3e861e2e0624e9f7b938c19c9a043e261d194b748c685c57bed28ca995b05524c3111a7bd8ce9ec6aadc36dad9041d3b54fbf4871aa2a6893281d62ab99158483b6e3d178c9c8f8917488f2595a5dcbd6152ae8aeaaacdc627e27e0fd9e0c16fc94cb6e089cf682e242d3c4625e31952995018253e416bcfe4245eb4297f2508a2ab14138a7bc980235c0c6f3fe9a53257d26771911223c362a9ba0db9eb923e9fc6382e971a5d9026c5b38b6ae3f40d7daab97fb5a99ca1feb275d0f594cdfc15b74a4c9a87ad81481d5af101e412aa356740faea982f96574d77141d17d4831c5651e30aaa7ee99addbe319251da8c55c44a941f7fdee151cb73025b6de97462216bfe6b610d272b3f36376a515b207b521d60f2f38fbe5676540e967f70719f326da19768897673a13723e39698717fe34f1d12525799e7d3333ba00f3f35e081fb209271f299ef985047dd4199bf369a929537e042d29daf3ef846e2d5efed50f7f31f61b6265d249b185a49103c2361ddafa0c0055f991d21870da6c9ee93ebcf6661f508eb519c207f4135ae3b7fd84322cdacf742b8ddf3368469eb7655ea61cb67eea9128793ce6ed51a734162665f6c2418a9f923424ef3eae3efdc6d3727515720f75a94a5e8d7f01ad6fd1e37702a0dfd7db5bd79dd9f07f6023989fa2fa82240f2861a1ff1afb5cc85540ede15973b9ffa3544e3ec019fd97962eee446293387d417952a109c5077025e08a1faa40366c926515a07503656eca611e2f7b63d82b3a82eb28a4c143857bf94611010e08da37b8156a1bc586735229c25725836f0652b7576559cf62a24aea7f84a4d65bd1a0942bb0759e69df030fb4ebdf72d58e7a240706512f578c5af38848f79122dee85dbcf20545c565c46d9778446dc15b23bae2f81d21eff886002b5c06ae0188bc8a2ac1e4ae6ec770a9ddac2861259107b16f24a39b9a42ac4c5e9b044535dabd0969dc80a675041bb2182083827054638645281a7b7c2904c63c17c5217c11938d819b26e18a2be85022563070d4964f4a7dbd6debc1d46a8d6a662a914f2f5b9de01502e61dfa59e1fcfdabdf55d472f14f14e2662a5a004209aae45e920f7aa475927f54586145e7b89883090068020a35826268755f7668b9008a34c9ec9d9e5dbce140bc82e36b8e873d837495f004f5bfef69c76bc35b97a9890f44d331dcadcbf4e57213f5366f6e9704c1f0f73d9182919afb801df882be97adf78f085d89208f3dc5e526adab2dac9279ebfeb3eb5c5260a93f2a22f537c828f67c12d190feaba0a8c157a1f105b89e607ea1c03b498cf6c92f97538e0ea1ad8f4d61906f6b924e67520d4c68da4fa8aa81765b56a1e35ea5c1805f7d077b38ebd23f3fad7890f72af959a09f64e6db28fd85f6ed8d95e0497c4108a071999d8566ce052af344ca3d5b252e23fe5bad38f3a4793bf6680ee4c70d1712682a374404cdbc240fc97516967f364a089f525636d8740774f2a3205399de3fe93488940f44f6c135a37a07d4b9db62c663361ba3b4a46a2496ba896a0b4b2fdeab94c56beff142d3aebe638dcd334d23f3b7f8a51594404ec5c38c29632eedbbfe942297edb2c89d8b8727e02472e2003ab89b542c5036165055f8576d4909eea09b8fe0c6a09b8be580a49d8a4e4c806f1e828a37a660e0a46d165d9b2812eae83d92f046e572d7034d38bef5955a1d6ec9d03bb117869801ccab2f54229ce952cfa289c5dc51454876c7c46012cd09ba7267bd7e75422612a035ba2430ab24cfb52c03f3266db072af440186a6edba896496b82e083c615ca8f13e3cf6898685e44db9238248f03c39f0ac93a732e59d5e92824194e357122b8b1e6ba3f4e2a38e5cb8a49a195541ce415efc1284a004506e8b47bcfe2549ffd0491133297d4c768d180be428eda78204559710fcfca2de482e534426033f82541b3269e05024434125fe901c684a2126d0bcb3a4a483865f374c556ad472bf0e0e6adb49f325f1d6dc8ccd428fa97f51caa171bf0d45bd36659304d0422c0e1d3fb4234f23c5f63198add59b93722fb6acc13fa5252e813a98b7546c41c4a260f69ab418436a42383765356e6515eb3d9e7afca5960b44392e07ab2fd112ad5daacf358225243196989d9b1ac0c443f21f8723f0839d33787707fd522c0785900df801d855a98d2d40105f9b3ccf12b327d2937b589d2c387dec7c8d67aa48e15f259f1b064f9e0fca48a0e2a89778e56d37ddf725c4c946e5dbb67771d931231f645aca0d46cfaeee9512b584f720811db193cfe294248f5c0828bf47b40fe272ef56cdb437ea59ff3768e958bd03a2cf90881d962348fa08d67b3e28f5c417efef628a169fb13c500fdd34fabc36fedf44d636f88c8745ce69a2b1acf54403f5c294de9ad1a63dd941b1e2fd68a7c51a8682561f2ca65c70d407a6c7cd596b5a9fa57db1de5803db3723982561b903b86b512ce628c2be29e0b4a7a8a593388ab28a4853d8aaf003650469053555c000c013b42704781444447c88953502708d5035a2cc6163a5bec15f8e5904caba380602694d53074a30e0818c69ac5075bca34289e26141064bf80561c4dc357cac3ab6eb33885609eae658becde2d96ab5780a6bf6466a57540235928e835280782dec1f8f2543f92784ef54ec009ca4e514a24b03799c964184df1230cac048ff969442e0aa77f44e025938a48b66d18d9fd224fa3a06c002bbc1bef1325033ee1a032d4e57fb108720a09680189d4453ed0f19221cc08e864d96065b03bfc38bf04e1b0deddae2e15695f8f4db2f10c6ef61a038addf55d5181d8fda97bd8ca81b178b6a91afdb2135cbe7c33362491728233a05350c2bacfea541005a4c55a71c3ad6b79a56660e53d4dc8e8f39581d5ad6f2919b8308e68c5a1e72a525b50afc818fcc1004a30502ef626d95193cbba12d0e0cbba8215cfc256e3951df9e2918c1d03d42e9911a4627a94563a47ecc33b2f135f09cefa3b1c39b162c344694ead9503c8d5a314637d31b30f9de991ac59974798a5997e5d356217e6127b12c83845e04aace6b8e8331bae661b7121872e44aab692d1ef942300c55777c3ba0ef5806eb1edaa5a6e6d94fd444d0275a2d09035b199d288f23a8b2b3a1602a117cc047787b47a205ecab058fe6d0801ac9d5282a93f3e7b785e14d78d77f2c2add082abb815dc6c4a5e54979d88263f4edb87e29b752106bb675501c3a2653a19918ee6a3d0fef167837dfba853376862e6f46b745b66faf10db849f0ee3dc02f54c3743ed8b337a0933789e390819c9646952828dc07164322ad70a355c786dfb3b55641cdf7b700c28e4d0c561f3bfa3f744a22b765db51474fce12c6f928717206440f04a89045862f3c9f26f390f6f2e925b3e9b6579cb8ecdd763623ba7706d744b0171bcd18436f248c411516c644ac872a585bc310653461841375298265faa5aae40d2f4e6a572578bf1e5888d3fa69dab4e00807d563a6a69be523904ff3f536fcb3cad9ed175e0541b65cadcf29ec478fc76976458cedbf646fbbf4d4a5ddf6ed1fecd61784801741a4b667c8ff7e739afc40b6e22788d0fbb7f6e1ac7eaf94c53c0420da13a462576e5c2901c13dc0586f4911977616212f5bf7a73bddcbba684e1fbc5055d0469368eb590830a9f8334d181a06275aa0300e05acb90ee30f0242908190df7f6596c5b04c44d60ccd99ef238bf4f6fb2b4ed6d7afe1446c6922837a0d3bcda454319904e1567fe2e4baa6e9912c36025da0a752d943e5018d82c2c0a720a224a8447918d5956c6296f27f6f9c954ebc2dd6a0ed20ec6f649f70368a0f61cb59b01f02a4d75952eab65e2e2878abc5912704f790775f3f34a7494c336498efbeb7114bb5b6f0784b75df659abca7b387b97309abb81892e721c0b47606c6637252294b35d50ef902927d56a8a1c910a77a97a3dcd16535411e7f3102fd5b1ad7acc79dfd71678dae69d1f8671dd7d9c6570bef7ad6c79dc5f8c0ac7ceab2d8968d87b4a4eb3a6df3317b15eadf455cb26ede4588756488c714036ef0e11207db5c0f82f496277d13aed9a12008e3eeebc023ca3fb786192182229b75f3be885da94798fa3f9f772fe183b711d5387547442ae6c0d34f813d0adc888992d797c2f09ab44ed8a6cb303c5451256dbdea20bf987564311121b28c6965276796e5d3eb973026fb847f00213611c08dddb887e6b27c9f6c5e162fc65cdcd83a0ec39755f70a2e5128355281444e25254844a5597a1d446499fdcfaddcdff295d347ce082efb7d0a73adbde756ee4bb0cbe399eed3c3aa504888f66fc786e96eec0ebd1b7d3bbb7d037bfee229e4985e1a03a11d1b34bb5e45049e6d9cae0986730f456314ee12b0d0bf3c36424d5977778aa52add53b2cb472a6af92548aa02377e828e159540ec42318b01db136ff89330324d0ebe67410b6c0599e4bf0accf2f5e3b91dd4f4ef1f07deca4c201c70ed752015f8033edf4acb85522e073fb0e43fae4ed95aa8cbe4a726a25f196b3625801a937f0e7bfdbb7d59e8f0c3ff295c4f62fdc9df495ed69bc0467638e4937c9380fbe1a3d5a46b30522fe628677e67c520135f42b0fdac9cbf77ab3b0784a667b8017d05742dc723396b2198e170faf36d535bbfa360068f1390e3e0171519cf7d3692d1cd47d868e3b688ef6bc52467ab151486a4ff9faa08051e2bd683477fdf4d0ee02cbfa788051c1b69a68be03e0296c871e6303c740f800a2138d17e5cbde2df58071b61c6f131df604543a41f8b37bf09cd13ad240ddac0c446613009010c069729839201c81dc7ace2917f91510c9385de3c1e9ce5a6fb671ec204169437f238f0b32b0a15a7c1c3bd7de706c2e1ac8c4a643d96cdc969d3aadc1f1e8bddd825f4539ed101eb2f4a1fcb5e7c8622df7d17aaa65729eb980a0666f8b3ed5c58a7fcd41621401573de1f1b810553017017bad57ebc88b045a5c207250281580fafc6348156677bdee47e42be213d7fece7572f00d94b1cc1bbd9a5582be2a6bdf8dbd983ff859a09f73cded1cc301c63266cf3782100d693beec511d19182497645abd85a804ee5feb2303352cc82277b74625b3d395a19e6786fc16163afe48d1e73db6d978d2ab35a603c7bab041733394aba536c0d0a86a9524e7553ede63e26c38a7cdab6fe30928a74b96e0daad3b635d9d6f4e3b89f086c4ace98d110240984e1de3bf10262b2e0c10eeaa586c7a13f85dfaa22bcfaeac7fb151ddafe28dd150c7ef6e4c8bf37776004ad50f68a5d68b6f7091451c0165197f537c9f8d6a4f2e990bad009b3219c59468831e1904888ba885fc23faa5e0b4be5df7b894ba3610d19b293971facfbeb6d2f942fc3cbb91e7d84127e5a54c334c350fb519c4e3c8d4755c428e7986cf04dcab25570bb20ce3c934a728dbd283ab8ad6dd739013c2d0b1ffc070beeba833d22a01e470bd55cb20cc7ef893cdce22ca60f303a260ff781b83a70750537ada57a1085da2f2dd5a3f059413022e05c65165c2626035cd54db691ed654c40c5e734ebeebda3ca7c889d24edc5341fd16ef86fccc682e43606a5f5367d44ff89bc5de23c7405128e588dbe7bacd6c97435b1b50fa372389f5aecd3a2904190ee4165bfcd74509def0f8e53a7c161f35242a96e4a262af031b78e866b643c88a2664d9b85a23eded4c17c5cd553eb03cb086da0b07406590263e0039092dc087d3045f9e535533d6c0230bdf413f0f100d0a8cb2bb03966c9163831e70168f9e9ebe359241b5d0ce0fdd9eac73f5c482595602e10e4915db45f63bc1d6137e71d74d03e86efc2ff1132419b5bb236339c79c0eb68a739459f4c288f55353709686bc21dfa91f11828c83beb169e5175787fe44d114e64d904dd364273005350feade79b1617b87625af7f60ac71eb03718301e66856659e65560e4ce60c4d981bb023b01ae260ded66e75ad1e4f3f6e59a0e43d57e9bec7407003e7b1d684c29b99cd2dff7629e84b0f993c2a648e18b1029942b3f567d75063d040dac96407c0aaeeff38098ac2fd3f5e66647f40f6b718c837c49d59c610c8eea404c56d652eeb29ef700a31eac782bbdf7ba10f9badbb0bcefc9d8ccce1132ec99aaf29da9b61f2a2cc1d597268b02584020a68574ee4dfb39373027b185dc87d0372acb63999f103f153b93a329b2b2b6074154a828946124cae892e14e0aa9e40ee5d87a50f05dce3034cf5bc0ad926eb5c387383042f116440bee4b782da895365c102b6ec3f71baa89c06f582a2e5350eb06760b951822ffd6cb160884702ba8d2720ba204dd027e1b0760f5cc0066dc6511ee239bb190a790b3ef33bb17b1f1218540c0fdb6169d06d2b992960d772c2c17b87084fc11b65765c3f41d47c84bf53947431a6d6e3a79ca0fb5b951d8c20f05529571d2b888fc9c6f8a35d28f0ec6c985f478879a2363434e5dee47ca075d791a753387df850e6944dd39858127a15c635004aa3645afc056f05f11350f31bb2534276bee9aaca2fb72d4c3f6518a260dc6b4524fba79a826df7758dc4685f0e1d36a8cb7b02b2c5fceaaa41da703879a18a9818064fb799e565d67dd07fc8e1ae55819a1e46b59e987d1a53bff77f4c31ed7fe72488655af39717854941dc4b56f01446b1a53fa180b4385ca79000108bb888b55a50ff7f8e77d11c49f7641baf862d3c4d85bf3f0f1c0cc041f5a8104d9d061337f48c9afb0acb4b224dadf1d7724700c406d071fd98df60a196717fedcd2f2dcde95e0d6873b74c22700ad9b40435e232a22d7022ec58d311f9f82c151055f28167180cc8c38821ddb33c1cfa459af750537ae48db7c9efecb2bde274635cb9881292edba2ce971392f8d9d0ffc20f01fba7f1d2f82a112e318eb2a32f297014e012635d28069e651bd21111c4c66c12696b362fbbbe037c0e3d41a9cfd746a016ded1455b9ac2f805869fc272a7ee4093080b343e143dc34c751af394cd6aa64d43508d1cb5b2725c2aafaee6ed11a7df28a3a099bad448e80ec6768247b2453241b3f98048d44503dc2529b7bc6c6acdf0e0f92d319097c18ecda824f06a2c0cdd8069a48ebfd5e14681cacd5a50d60eced8cba3c475344b8d70788f5685998f58ed1b5eb05298f7cd75f8a5167e8b168c9a7849a12a07fd9689e020d648502a68c64e28a6a96b959978316a106e09a8f192edd08bbff74f8cb0f49c798183343cbe27a6bfbd0ecb065fa53d36897413da0079bd7edc4bdf028e25e7c372b20fdb0d55b16a54e84b5ccfb672cadeebeeda191c481db73f669900c11e43c40ff5bcdc4e4a75e47a39e2b4022b999ce6d338684af995078bf8e97898aeff6023cfd3af3d6392397e5e8b2289f1baefb93ac6203c1369f1d8490ca79f2eafb09911e2b042fc7e371d93b1ca56b7b4df4887a911fc3f2b36e9845bf4773324e6368f8fddcceba7c9e664418642ec1fac797ed62d3872b6c1c55bb6d174fe4f747854fec37f6ec4dfeb6d0121a1f720db3a1b3144749ea28f748cf9e28e12c974f9c3fbfb751309faed0c1847a23ff974a2ae57cf0160cd68f973cb42499fcc9ab7f47524f6a64acfa935c3b79303237c6d804748d4890bd3dffa069e181b8d1dd34b44a8de2f7d17b5b66538ac9c34ef20f9bae2f8709b2f05dcbe8390adca0d015850b6d707ee0a1d0730ae5b1f69b20257283f0c9bea1978ffa317a18af1834552fc670d0a7074f77dffdb9e2563790277df62e04d8fd6dc3189f4b2286ccb91ffc6e153b4022498793a4eb0032b18543c7586c830b940cc50e6c79ec0a0289cb6b5b47da2716f02404025d6520040bce595e428f231c7ff54188afe498b875c9323aed566bd090e0d2738ce010c6d570a7cc411ec4d9cfebbd660613dc814ee633a7b9d3dfbaee1e6573371e2dff550a37935a89a786febdacba34d8263021187d5c47536e9d10a9b292d162b88e5f4d3329b566145cbbbedb4090581fef162179ea405773f4da3dcc4f0f04dbd4d3a6c4af6a428883ae1b2d36559a2237b7b8cb0a25ba9e0de54b74bcd27f8fb4abc8cdeb451026c03b7a9392a987e7b1d001e19643dcb28993d345e077726df4b1a4cfdc0d35a6cbd1a85d316c8981b4e927b694eba2b0b02f8d379985bcf305e66e7e1447dc34d566a1d760d231cb812d55a89a00a6b5b7f97d873687cbea538c081210d4a25a6160195d676f57c9f2d18646bf67d3dca577349a4156710c92039ed653d8b662bc147454fbfe005ec9e7796e1e0b9df9688c5b8713d29a122e20b38659bea54ab51005fca173b2c9893ff3e765f8576b8feb894efa7cae98ebb4ef3b693689a4dfdad176ef9cf9bda1013073a58d9a6a58c87eef9c1af76b202b7dfb4acb41d715563a4453c2680baad2b3ed0de2a966c1439f1b6b94f7075eabf607ee9b27bb400a6324023b7a105715adc0c142a9db2a085da3bea318a544e1a3e48bf6dea35d044ac269086a28f788777fa10a45010e8cb25da38e53ead8e13130ed8d73fe307ce244bfb6cd23bad6981b3dbc22925da5ee5db64d151c428adeaa234a11b2f8e387a620747f381119f0a48b993457125a6c2bfd1490a6c1f6443e9f92848087dca2637f32af6018677a7c15d7302548778941465966dd6653a4d73171046fb8187b621c47f1291e81cbb37d207b536cfe102200211f68ffbca31f4c2e9c0fca07a3a06b6b3302afe7055eabab53043ca47aa6bd5507ac559ded51c7d126bca9fb3a9a329d93424ddb5ec9a897fb81f922319c76a2a2d1b467c271f00f83763b0699079443c8fc0951a4799c35292862459533f65549a4e56fdf0e6c4ac9eac1b8b2b69e980aae7af4e3a39dfb24372f1aa1b0cda15f9dd716bb4a0bc3663b67a25295338e73c267943f2e53631846a92f4a30f9e35059dbd101a6095636317032188a43994344180304e4f2a09d2cd48bc5e9c0c3aeb70ea87221178fb367da1053a953b03d784425e23c7e0a3fed84cd9c95334295b092f91cfd6bbfeb409d554a8b055a86bfcdc5199d4016b8724c8861abd36754a0c88e507bf871cd8d06707222326098eb0b2875e895a5b382a10784898be57ccb53bb8cc0602e2f8981e7800574c1a07ae65c030839cccaeff259f0f6249f3defe312a6fa04e2901d75d5e1ad164465f6ab1856fd34d949bbe08830d17e461d96f512ba5c0a14a4666c460867708c6a3471c6538e217d9e1756442b4c44b53622b144d0a01b32d13e64b2f7834e4b9aeeda17a66fc334c0a5760c6bdb65220208630c2269c8608850d1ee953b91c7ec60e2d946da2d3f5060ef4e25534b877f1206d0082ab4e11f1e36b371e2cfe7200a2aaa311f6d5c8e5c597574d31bedc78412735c33fb6faa539e62faa3d085f6177949cef3074fadac5358716ed1c3f0710f5af177abed93cdffc136a5833c1f50b1bf74d3c53ebd81984ca4a172d2deb4a8378cfd18b2b8f7d13ebbbf4c19ef5b0c44d8b9520ad340924b12bf1563dd811d0828239ad6ba0cf2732c024a583fa5b79944f050ccd9b61c839b9e584d686539674cfe877b396f7746ac389790ea3484efbea64cda6a816bfa3853f3ee1573f0c08fd2848a4bee377129a2a3f296ccc2f408995fca8773e83472d3b61317694c5fb97892df9b6ec38704f08e349322da4350250f6db5cec58d372f3d0d5ea3eeb08778251e2a9f185dce7dd833f1788f14d2408a73f1d517761e2fe167aacc8c8613bb27dc53310e2527e2231ccb4344ac7fef1a41166bb6b895466db8c57d40a873d535dcaa0771d4564aa000d574b377a377bdeb926f5a59619f68215fa0bb6b1dce4072f43d559885677945dcc524f044db944a448d7fae81e29505997cec33edcd7642e65b9ba4162693fc856b83ecebf065f9863f432725e65bfdef58c40c0bd21dc2b88cb765978e5c04d1eda15ae0f96c543972bf15498212ae10a05f02a600eb2319c33cb7807a7015f8ac2e2cebea9641862024b4072a74b633fda39ae150abca13e8d0d9b75ebe70828994ac68a89ef1cc8f0828b67906d2039578a17726f4e5e0e66e842a8901efb4742649df65b1fe648ba09e2eda96323072c3fc662a85e8f2306f4ed13071bd0c6b17c75b7684e4929685c5220bd9b1ec9557f8a11421685200f0195d28bdc72d34f9e2d62dcfe86170f49833ee9fdb7d7461afe14e8462eda91f833c34ed7d5fd651dc5a6e54c80271dcac260193342d8452e031cd831a39cc80fecd804499b8e5221d9100ba2c331e179a21babc77b0b177584584d1d56fdfc2abbeaa97e186b36fe109677cf5faabe3e1269101118a8f31dbcef562c150d6af53608e8ed0db7c9b749170b4c11df94dc002430c0e6594fbd07743615cac42d1769ca51ed6b8a2dfd069c58240abdb106836fc56ff860fecf80139406d9260212e630564013ff73dc34fc77f4660a138f67d5e5d467e8b3a48afb21798283849b244b67ce814217dd3db093ec8c7afde582eac6e7c2d4826bac75659c9bed2369435bce314dbc9356f49633a26d36145cfcb1c96a9e885fc0b90cc5f9b73519fa1bff1afe85da2417c024039dd55a49f2bcc833fe2868a8af31db2a8188297df6e1d52a6b93896ea2e419d2db4c28ae68122c751bab947ffd6c042c0a9f64b897d180b77d4311280ddcceb7beb73f25489222bb29011fa06c2221a5ad944e53519c402ef0bc818ffe434196a37787d645e5a57428e468de33bf72f3a461b7b62d272a73190e741da4f3ca2cf963138e16e4116d24b477c0888bb74cc74bb2ea40b4046a7fc1c10677389f41726d6d755bc5fa9405dd438ce6909f14c3d8da7a7db14e09633b3804b422080f901e2560e43ddb6d70031f0e1f0e8261ccbd2d23ebf4bba10148e46a62ea26af66acc100788769586b016375fd27795234b4aba2e70aff8917c10f91546feb5c6741479dc49a6d5385945e5801f9f4ecdaf67f811b26eb4cc1c1a1fd04d958f4730ed0d1deaa05acaafaf6021d1b5b18db2bddcf0ecdb682f9b49e50c5938b08a89f2c954cf1ea3b2ce7a442e53308eb0c0d3b1a2a82fa24e4541bf8f9c45684f3d4fde638b214b69c3c840fd465189e2ad2ca1138eb78e2146225efc1d8b8c07b4b0021f82d70a2e09147301d66821089749417785e10575e780d04329680c4c5978ececec8a3e500ee47d89727fc27dc35f81d3edcad67954105640b86aea0310d18cd878efe0b3d5ad933a3b050e3519e40b482f06d6f07e9ca968a6049985c061e81c54bc9fbd6c08f7b7627a4eda6e6d03091b61c119420fbd99e0ec415bf223f4580073ad76413ede89f27d9054446c560fea1de4a4b924afbdf384df32270d0dedde8c5e7fc03666ccf5777a4937b1ed2072f51f39ae9384509b71f73f4a454551af5310b9d6c29a271ec25bf3a07ca374b0aca5ec9e89cdea154849b4cae77fe1687efd24ec8fbe5a9b2545ed74baf0b9b203fde32df4dbb65703acd297998b67a4dea9f2de1aeec6151dbbb80845441365803a14a008e9ec59ea75f10b4f9dba6fdc924c61b4e2be57872cd17e1336ba90461100ca1cdad0d429248a81af7c5d484110078a27bfa5148229cfd560e72435ffabab9aada1591e0fc59e9944870c5c9d34ab7fb292ae4d2a2eb72462bc140c1b96221f4d5494846451f07410573d1bad355f11a42a36a379c3ce17a28c720d13d8275f63ae438295fdcdcbdc863a879158b25f49418f2cbae986d8decaf8c6046cdba51a3c7527a3471ddd8065857b822d50df545537abaebc58296f554e4d180b6dc3bdf7327586ec580f65b6abace08f5a14308abd461d20dc6cd636a28d68e063d669c1fdca0d1ddd2cbbb4e2bec0feb405119de424b911e7e1abe12f5e0181a00479f16d8deb8c1cb0e419a1b428641f169c5840d477c97b40187538d132412a888ada2cdd8b0e12ca8961d0e946551afb0c61ff31b2ade835b083559d79762e30632769a3946077321bff867bdf7464ec72cd14dec2a0231876e686e1103eef2c01e2dde2e6a04f2815b2ea43bd6b558def3fd91fb1e11829b67dc371b72a32c0269b4343cc4c637ff6f6ac4da4ab68ad6a16fee515530631f0c682b669272e3227a5557b36c0ff5fe646c06e77235f1aff5d7d16ddc3c2203941c34164dbbdec0b49d19cdeb57ae8687b06599b2e722469b30b29d90f25f218b767a7b0887d146c65e57f24ec38ecd1acbf806e9d6e1d386e603ed37255c960a783dc745865e776d0c331aa551575d8e36a25b11e9923afc72b41194eaefcd4f7d75dea3d99edb82d37e13f6bfa8fcc9d272bed36ca89f68113a3b5864d39db8c00e5e50891e98ea40b2251e9cb688802b13ccdf32eeed201eb030d731ef9610c85c42e334f09bf54adb10fda05d6211f05755d3350ee2c334660963d294d3d947a8a623b6f64d07d4ec313141a170f527df04235405c6dc8243b555627e5f8877a2c4666c2d7374b9b2c3fc6619e6a9248ddf71c959a42135091796b29802d00587a83d5c35325973c16773cd53f3a0b45f1a5dfc95aaaada7068100c826aba8630755274355ee45ac31150b44cdfe036975eaa9458bee4722c01076215bce1ab3b54f98a8f4490c65d2f815bd33997e9c158a23c942a6623fe8c3fc1e27796c0eac5afde7fe990d392b306af98635e44c431fb689f417c0d40062bdc7b3e3bf25498105da50231b8c80b64e0c63173667625c44c0c2e02e717079258630de0e9f6a82e631ff6bec98eda69532bf09b170b52943554eb14fee6865daa75226d1a84a0987525ab09b5490db6e59b08f0c17c4528f55d35cfb4fb01abb9c45c57266972a907eca09e1e9a542b0829b6610ffbb6120ca83ff4b2aef68e6d547007570aac31396bdc00568113ac1b263623b9a8361122f9bc5f060047d5f99c30e2ed6df63d69dc0d748ec260ccf26acb74282528d12680b6c6690cd21f7f0b787e69c210258b0443eb850aad11cc29d4145856fb261dea94b3a394ccecb5cf46a26a8bff883ed0c37089aa557e7c4729f93ec182dc3ed54764f23f144313015c9861488fc0538445f66e345e4c09d9c8df8827400bb1746fb6325e2603a71df5259f07da82660afdebb3094f433b44a8c9d116a5b907f57643b8f4f654a32260307676e6f6047e178715943828ccf556fb8d273970e03cfb72e0f0366acc96ccb79af81b9fefcae8cc9ba67f9814078c31ec01fbdd270d2b2e7f0ee6530fe44bfbdde341fe89ffed5ae6c6db25e923f8087bcf282712e26363b1ba0b7f10b2bc772b152b379cc26d6a9388644126815c977fe4ff7067c39e70a92954eb5e46206f670b6267a39f811d20f54322037b1ac5495d04a2f361f8499bd7a09f893c9f694051440087ae64d38aa948e72b34e067d3af101e53b271c56a748d670d37b49d824b1c5dbcb2e701781448030826c1a045010e3996dd956bd19cdda501418d3ecf6f3d2137a26b8caef1d68043e7a8b7cec7866e2572a6014f89d3bb964f92805c62acdc66712466fb738e868f57cfd12408b777de3e1bdb57ae45efd6809f4d47b2f7cec7866e25722d149efac5ee0a129a758e0e237a697ce5b4a0b1e92b59d9b2e02adf24adacd22fb4e2b6c7d98b819bb4a89f237f82fe22fa131a8a2a6eb7da0be1b64c3bba5d2a111ae5e3216aa5f0676a4a458b4049dd6f0252c5567f79aed3118ca14407bb24c59f9d9035288efff570b35b48fdee074b334d2bb1e6cf74c42a7e407c340e4a84fd84d028142deb20bed0a558fb122abe8c4c41912407a8db2209ea1f385ed0055b818e02f8c68c72aa9ea2a424a14bf51b21acb373f6dd67255550a845077a8f5a8b174273e077f3ac43eba2c39d23ea3082f34d96f7d7e6701e1c1423f4894b12900f4ee57813ce3a30cbcd84bda3a0a8bd00bd0a4046659d8645de69ce33e7b3721a818ff491cd8b34ba0b1a99b43445447da7c092a37866ae75ec41166da19bb05d7312b6b72b02ce0ff935bdcca34656300615ad1c7580e3c987c0a5f052cb7a278bfe174622aafa236e3b8dd50c4ccfd8199247e19ba6558ae8dcab1b2fda41d6c95f8d27ffbee93a2381228b56855ee497bc50a6f19cecce8275d5582a0ef18857de1c3aa7d1d7568c900a6c805412905a2101e2c3e039e34383e62d0939e9208980b6847408278560a54c98943633688b94743f0d391a2b3db723f29a5464bb6edc4f33cc6fe2db240dc7ebff91ff75a7b11d88ff947164b84ed1806d27fb909ac2c2110d18d370f3dac1d12dfc1b21e26657d98e832d3bd6f82481c43c898636c680c0af6c98728d73e521d4d76ca72834e2f2d97cf209f13596e92736cd3b9bbe7edec0b844fb0237cd9d2346d986ec402081cc171e3aac678d16cee43b512a2963ec732a4c55769c3516c034f91b262f57b4ff11b21d9f042c39fa262c730074e879ed83cc3fe7380cdfeed3b730f4d9efc1cdb47f585b84065c9626726529276afd0e8be5810382e0a8d23ec8232e1c8f8a9c1283381d803aea5f850533530e2994fc3d69173a55e59bdf16c2218a8f1199f8b9c94d3e8e5992a04c4161ffaa80783ffd8e47a700a65586530d445864558e25f81c34eb303d25d21ca99aee91cbae21937bbbed72117ef8b9a612c6458fcc0603c28aa9da51a15eae11a158eca4b8ccef52208c4c2710caf54a9d64b3285299001376dd668f811766a6d669ed13a5c4f37c7e403a7684be8e8d0394df045c4e0f2c1be501911102b4180e097ec19c372dd2473b2b373cb52589fc6b5c1aeddd55c49413315333e493623840c90470984ec31ce9ce800c85c2f2c13d5588510360daad7c394876f11cafa245b51f76f998b7c92c91d1c1fe4e8567ce45efcf777cf294b82a5d841a9c53cc42e365d896c088c9ca56c0a681aa5c934fb2d48801847605a7d11d450545d29af49eed4bea9531ced2b1c9c4895d3229b3199f3321517bf2899eb4efdb12a61aacf8a27e1ff4341bca2fed094071fec450a2b526d27ea8ee3a291ab425024bb64019a3d19c4c90fd0a8598c7a977461d7a05b5ba508e1c9a18f6bf3a8aeb6e800238a0d3133d88855bed8763d9e035f34a0e6cf4fd7b80cc2b28c2a1aca7a31ea73f84c8e98350bff8487e52762d7313f510caf2f24481c1d9e2d2ce3232432c27b151b417a5b5221ae53b5cae4a1ccaac7c67aa34af97c495537368ebfb52d24427018701eaebaea355818d1f6506353d45689ba4a22d42d6371a739792b000e45a47f62837c2c848763131728673ca6db8b492573174ed3fa159c4b469acc203548d115a7886297362b6446c88207fda653430394cfdd31b3519ee5685c7d54bf7b392448f623a8babe3878bbea00100f463cc152d27284777a5adaf94d0813470af7f2963645f289742ad20ccc925f16e504e76ce61582546457f9115ab4e837d4ead8332ce6b199cb2f06e0ed17f2e910662266ae957716f795786056cfaee284f65250dee81b192f4e6e0642c9c91475bcd1871725bd4d136c92d191a38c9c811007c1372f770055da7c57179e488add301518f806f8c944e5f4df539fa345265ec29c29d52458066044d5add20003d88c8b3a9305c7c9460ccf6dc423c76bb9c588b1a269ba636894f5c565da2bcd50d88b218fa49c47cc2737746ac3326d2a95595ca3b7b08407b48bafd83f107519d06ea6dd472e5f591219abfaeceb23a4177c656cd1e21140cafb59b3cca9bba5435ba3a9f8241fb48cc9300f1b822909d41ba2c18f9e7a69e923ecdeae1789e49519f25b86499649243db84b888b1af2febc5e59a0eecc99a15009f7b74e05ea11567d3b9592be80534292e4ac1e0a928520f3cb2d77c9642b93b9467fb83a6156211adc7794a13cda3139f01299aca735a80d2154175c542d610ae96cabede82a159c151b1bfec0b044851241dd311389860fe338f1a82a948e49926f300699a259322bc02c40ffde72e1dd3763e6605041c0364fb36f60b004f1c98779a999bc07fe666b2d204f05711624f3d3d3741cda437e9b052265b71b3a48653cc3f53fa45319add308c0e00fd1a25a77526ecf2ca561be2314f72bdc7319759350ef7a066bce665660626a15c94ca202870ce8db66cddec8d30aed6319b08393eb7ccd76cc0ebc12f6d2c973b28407e5dd198840a14dfe414d20a8e78bd13188ea99e0d6c27872bcb58145d035150e33fdb287e686bebd7d46a47906141c4650b0f09c559c2b6f1815e0729cfdd3c583f9944fa3c98fc015f4b0073978fb92d5b06cc602a430249864ac2ed047b883ca0c47b4203972dd27cca9c89e237cbb07703a21bd64193384a398ba9d51e1b9b667c9b92448d45a68f986ace783ef6e8f1b7af34ed64b96ef55a4dacf21a257e5e4e8a8a8274e185a4321481fc7e1552ef25fca2be31de2ba80d989786cb13e418750017687ff963940474d6fdf5414eab240e50f11cbd1163fc67ab0d0384e90f9a1f87a5362e7fb4678b90a8101238628d07b54645149d6d362725294d835203f303cc8b3ffbca959b84fa49bcf2ab47377701902fa72bd0b50aecdf19209ae6b5e2f01d1846627a7fdcf15f044396797d06de1c2b5707e28e902972c0edf1179b589b8f74b47806a4d36df6af257fae79e77c42691126731a1df86ca112ab041607740163810b09032dec6ec1eeda687b2f45aac27d8ca5261010ac608861a957483e0aae52cb308b1b885ad183dd820b22c276f1171bdf95b971465932fb6712b9717e291b6092c159d018a0c47eebeedabc9280a68538a4cab5fa69c77bd38f2d7d113181959c52421cd53c447d59c15ddca3c00dd352cdebe190c4e687be9c920715e0246f848781a1b95bcc34dea884aca8cd753cd0471b22391cbe8b510b0601ef4906801e4b8d73ec2003ca4fee775a20d0ba4a07d56730c0295a541a78d2dffaf992f0f858021a96c9ccf1b1d563edefb7bfae26568582da180a85e400db8c030a14152c81ecc2f97eb2fd8bb21ad0ceb896c83216d2e656d6383f0c37fb9290bdf7de524a2965923258070f08150740fa860afa86d10c7278539453474bdb1f47913f70ba31f2570c3d5e06bc751ee8fd6fa2dc1cf90b85f79c6378051556f888807c37462a7c442ca1fd2abc0a1f51d077b33fd6108701b17ec8dde37b498594269874810e48b026309f0f6f8a7a1e059dc2eb68023c9076cfebd19382c6048871767103690be36807a4b1b81061fb75f40c78f7a8b2b7efa1ef1807fd734cd3d10378204d07d0e9e8131e484f588e4d9d9ba2d84d514c5f14348ef5d0729fc0fa5b46e4e17f53147adb6f8aa68c3db46fce566a67eaf6d071ef9f3b64b2adeceffa68568fdf3dbe1888c01ee889fcf8fd40ff73b43f2269e43d8d1173fc1fcc87d2a829e5fdf0dde37b27ee890c801e3d400823af78bec7bd31228f4ecd87fffa7025800ff35ef9f810efd547a92357ef3df9ab84f7f7b6f88b84f7f7983c59e4e931c2989c0f573b3ecc5b25c2475785f0d17fe64dd01ffc859a3a9b08ec55f8e8454fe8e755f8887e3e7a3e54f20fb94b78a0b87974de84dff9509aa049d071f77813f4009ec8f68e965ba7be6c9a3a3603b2554ff861d7afe161d77f7de14acaae2f802fcc36875ddfc75782ae24e8eac5c1fa3d746d8a39f55d3a3ac8e30babd3ae954cd1d4a960bc8a3ca172893cea085f88778ddaf573be702566d7dfd1c5ae2f42fd10aab4be1027dd0ec717ca8de3a7dedfe3716e1dff2fbd166e5ec7177fe4f8894323d1f172f74c10e741d052d664e7d038efa359f581dee77f34ab7e31a420851360c0887c9e49ec82c4171896f9fb7f7e264b0e6f1e27e4f1b4fdb3931cde14dd14f92b00dfcd43b931fa74dc5ab3e6e3f8e8fddc14bd66cdc77bfe8d66cd2f4fc9484749e41b1cdf0d0836c8f6c39b2222b0df3f3d223f1ee8a727f4f3fb237227b080fdfcf4847c1ee8238a617628e202f3f99e7943c76da39b6c960eef0eb67f8d0edf47ffe88903980d1dda1a563af4c8d8fe00d0a127dbfe1f5ca8f0860fa127e668fbcfd0a1ad614687954bbe50a1370b6f86a84e3bfcc0a972d9e1073745e10eed426d0f6f8aa41744fe94591cf41ba9a4dae1cd4dd176b9e4a0bfd0c4010ca602ecf7fffce8088bde0b03a0811549b01f1fd0fff879a0efc7cfcfff7c373e3ac2b65601f6c5f6ef2fce567883b4fda688476e92828fd00317716cac6431d1f30fe3c8418eafcb649b1f7274c9a2595942dc7befd14eb384baf7dea3de4c74103f8bc86411710fa96c470f083f27830719818b9de3afd203c263d7c79a0564845d7f845d916c2dd4d74219bcbef6f591dc67fd88331ca26650b4cca068d9568fb0ab1e1a21bb175ca0ba1471d9324553d2145550b139d58d309c12352a68421be130c2a1435e01084664a6003c71f3458d142024d5fca039a0ade0e84096924319e35270c3839a296aace450c66a9870228732a67918228732e6c12000b21a261a9df7835340e3a0fcb28379ffac62395c6551e1a43e0539fc1d0eca0f44814b4c0e5d5c113838c011c1a1e0a0fc769cc8e18e8f8352c6c24f1016238791498f206fe186c83be450c66a64399431ef4bd6b49083425e90c3d8b4805ac4881c462707e032f543182efd7b63f493454dc45beb17c2b0daf439237abfcb3038288fa82087f149866f88c8411c441f26c0b3e52ec17037344c707fe1be38049d2b3cdda5bb1bc1b9aeb2c8c56da53ee79cd6ab53970e8626d3d0903e271929e5087c62f71070bf315ead7cce29e58e313a148e46ed318756393dcf618294d2e952d2a0c9be9f5e22cb3293d259eb153a9bf83011a51336f5df2014d8f6ab0eb55d91d8874d1d6130987d6a6ba530f905a5955a5bb5d44042837b985768937dedcf79ef4dcafea22fe586e264cca1f4e38c545280ab818426040cd0ef361005c1c5a87d19bf48132c25118104c4919445f2e70d51c7a76c16459efaf2f3a50d88ed9a932f5a2ce630c51cff38c60461247981a4c5d117a3c9a5885a8f79d11737d2c28fc2f0243cc64d20ab1f66a2414313b34ac898536598fa3182af13a8f65add6ba9fdb8cdb0e9cbabc596fd1376f0bbded686a5b77e72dbf66dc86d1975c24d1c3d904d31dacc74f583d098f6d2d8f46942fab180d06cfb34db7631c7df77f4d78cfbeb7684c2518aa30c572328eb279d7c4bba770eee5896ef0fb65ad60bf2920c77026ee5af991a48b6bfa4339b4a60bb64229b4c9732ea2f3a299d744e9f5346ea2f4e16799ea8175eec38c39f6462b8bb47d083b86fffac945b20a941e3d2734069ddf16df6970ce23f9af89e76732abbb928bdee5c29b5bb4995b28b2c6adbff8db7c7dbe38dbb553b92273938bd4a1726c741da4f5ba3715652ca3d7d2a5ff829fd4ebef00fc5d4fbb938c9d0c6ba40f06f1febdebff2c3f57328de9a56bab7dd53a52c4b4a44b81fcd169d44441e7c97f6f6d7afbc5b15b2f86e784a2cebb828ad7aeed3fbf43efd41b6752b0f486bf73838a3a4f8bbe8fd580bc4359d64a800f42fab7eb802e7df1e07e7db38883f1fecfdf04d3fc95f48ec570de3d730c658f32287e2460e26e5d091bc8abfc28ae454220ffd591f2751ed3abd0250baaba6f40669168438e845a96ecace692a6ce251a283d4c75f3b1ca41f047e8d986c3f646df6636d5f1070e3a76f3f8c4cda074e1b35c8feae4404171f1196fb6d9f7a22ee553fac21f5f3bf503f7facfb1196ff05faacfb5999f1113623911cb4331acf96ada122cb2459e541c822fb0f89b027f5e91f993af2ed0f4d1d7ffbdc9194b5bfd9e7b4d7d4753ac2906a589087609060b2bf7cda3195972a5962b12f4568359051a54a1553a28e88f0242b0e0e687d7a9271f5d473647ec673a544119f39849834c7183ad80b185aa154ab63087b755809a3972d2b2770c7355bb7bb16c4adfbad44d8ecca09e7c8225e1bdd622d4c0ee3936c764c458cc81c351a50f840e609281a06fc3583dd487470d15ffe313a18e260e0c1a9f3120a0a6ac805656428cf1d27a81d27398c503b3638dd0aca678e134c5bb1ad1fe592c308b5d3b4736cc9df5941714f5cc429e1412331673e9627d0af0142b8c072074d384a06ba7ab0fed9f22179c8e7a48e2a39944550514d63c870794beecc204fa0d7c68982ec83240f10270a287f8571cc9efeca1eb5125344c9d479700cd63b240fd095718ea61ccaa230568cbf28a64d36fd524c7011875bf5d44499a4814cee641356e09e1fce7030c716d92ef5ccffa14c88018334860f5b94b8b2031fb25ca184c35089b5208a16f71a05dd0e48312c54d215f72caec5b7cca02cb6af693a68079c2d293d0c1dd05b524c94d92e2c983d5d4f4684303131050531313105313131e15c617464a1ac18374671b41845998d3c12aa090b557338c96164b248b12afe0aafd1926c33ede93756647474ddf3172b29afb557bcb832a50c2ec8e04204b50534903233cdaab5e34c690c1519ec30ff9c33c618e9a454ce38e76a85f1b437703430d0e0ea73d63a67d2956a639501455283a6fa9c1a6e22a9dacd0b426b144fad3ee7acd46da4c8baadadf4082d43a0a04ab64a3424c85c0966d8c0c30734f8800886c801cb1035cc38ca790c1a50e4ad06337cc833bee0004b152a80a246174ce880e5881c6051024112150a2956d8808b1557886145d38c125b5811441065b8dcc780a95145d6769c51e952459e3bcea8343591eb8e332a5348917dc7199528c0a0a24437a332858a110d737271426bd0f7de7ba30ebc2043839325ac7892d5a6307ed8a008432985293b7680d8714bb7e36c662485e9e79cc1e28b1438f8e204155f528077a22c6e997d5172d52f1bd8a91d675f7ca0b1195f84a161860eb2108363c71909a0d8a08abd4734903a60bb16bc8c29c24b0e88e80109d875242152ab19165b36de718685162f1ab621cfec38ebe2c5125f6a9091f38eb32e5114d165099a159207e6938b06874113345c4c91c5035668ae646fc7d91421c0618a22251d8091533bcea41003b280256926450d5c331fbcccae2c5126562f11ecbd345f1c418307447c0025db1d67450821dd034104b17a1c7056c406f6dd7156c4931dbe6b0a41b5a0549461730463ab33d0688e57685d361a1f66b315dc7c675380d9da8eb39916ec2504e794fff2a3275d5a26180dac64bd54de9757fbe4cfb668c19eb1e36c0b1993cb5d5a9a957462022eb04cd1728321bcd42a88c86293b0a1daa670e181c6a4e5d9ec86396546258b2b67c7191737ecbce38c8b2b365ec9644e3d681a889af1a0341bc10b8cb090b2450ec28012c368ea4c6fc20b17495ec3126520c15cc7261bc05c071c288112cc75944115586ce2a18c0ce61adc01c6512745642089060fb8400673ddb20116653928e104734d0306c6ddcef58a07d8d444520b324a14c198d0a5a89101824b12cc35eb0a18139712065cc491524e298fc2e0f95197184b59dd9d20529f7efd228dea220a2ee280619409a153168bc59cc0bcecf92cf86bee392b42cc9e0f83bf629eb322c8ec30427550b32c537618c9f86b52b127951da200c33ac6965bb9ff53e77ed7e688a8c904c7224224bf5082d5a72f83dc2fb0e48f0005231a010ab6230eaea1927fee4805164e88c8123d8994c3d5f6d60e5e36b8e36c872b7b87293bce768092b3e34cca15db5f471dd7d2d1105088c0881644c1fca517e4c62e366725c7991417380bea8c8e30ae6a224d160412302634669e04158331412188255200ab4f3551fc02c6243e01ab1f166e46e941128c898b0627bea03b3cb10463e28ac284122cba00c2aeea15910441164e3026948b1671ca0657684982513dc1804d2db7cb28a534a45acc89b24ccc899f0e4fb6c401c9284aa378db718a315c9c2776b1a74b200c481d647e5322c1a16966849759099ad84f78400931000b4082603eeff311fdf09950386004f3f9160a10640515f6fffccf4704f4fb6309fdfcfef91da9d8428ace7109720b3a9331802e80fe11e4a33ca5668bfcfbe668ba8ac82ce89d20bae7034074d06cf9afa08554f12287f968eadcdc105bfe3a208120e9868ad2f6205dc4c8189806d23870f0432bd13256254379f9413d99f66d342b88cdcfb7f9b8a707c4b7cd5bcdfa9e3e0b88a5af03f0fffff36fbeea24f2e67b7c4ed08b6cf5cdb31037f8d1fb40901e8ed6571d1a098e8f9be351ebd61776438772df784e7a2e4c015565dfbc0ecef5d1fb21a1c7a743b9bf2118b64df8ad2fd46c794c3fde161dcfe9d03a3ece764a0eda5c71506e1bba43709130e34d98e1d92475343efcc069c88565a52d9905447e8f97df647b5025bccb5b396e4beef1e14d519633e5c9628effcc4fefc673ab2272983abaa234b797d9253c473bef2988dc257c1e948362805639dcc86eb6f84bc7fbf3288a3c216cff9b2b91a7c7fbdf60893c25441ed7f930efd4ce8778a76c7c0836de65e349b0f1267c21de9c8de73e4a1dd5d36cf98fd426810416090fe483fccf03bdcfbb5c3f3f71eff7f91e07d22c123e2b401ff42bbcea6947f5b4755441b40a0fa483fc0a1ae87be81b25e81b5462cefbc7586305adc207d1378eb4b7837c376201485c13ea5b076ca26ca26ca26ca26ca26ca26ca26ca26ca26ca26ca26ca2ec1539bd9a2f68cd17957a40f567ad355b1aededaca7694c5c09777f2fb86860cab6b123170dc436dd623bea568fed6f23b6fc71a61fbd55cd530e72d05712a421536da57ef4eab78486faf6892e9125f7cb1f96f9114675933d350b774322ac7e58480469f62cf32118f7b5f75e6b05639bb899f80fcb2262467a76727c216373e6a8410e650c6a9389c58694ec4c9d9dd8922f6bdac1b6066520f6db996206a63b51399431277f8538ccd38338d20d06dab431e9fc6ee3987f4bf9411e676fae60a5f4e4aa656665346185073ce06207d58b1639a8b5d65aed4f4cf34a4e6cfa518904ab954480ac04318879c1841307ba642186dbb1e547a52216072ab595d24a6d0d7270f6f89c20d75a638ebb17c4e280b4eb3783effa5ebf18aa940fce197cd7f002d4a79c29fb49e6aee1a435061c9702bdd50ae3ff9b7777ef1e74e5158e8f2f37e7dc7634ecee776746197d4e4a69a574ce49a7fbea6ba5945297ee5fe82bfb34523ae7b51edd5529119e585acad65a6badb553d6903eaade95524a7d66ce39e79c732e654a29a594d29d2b96da2aad903ac31886d2ea4e69a49f9cf3a35422f11fa504672bfed571b6e20ea515ef5029addce675aa942a7502af3b81578a04b7799dbd5e77ed8ea6699af6d16b91881dcd093eadb5fe578ac4e5b69b638e953f786853fad18b04addce6755e57e966adfd68a54854ec3492a4e4de6ed453a5663e36cd644c842a450487b724b88e4e4f9552a5ee7778c7b3bf712f7f4b6dcf6dcfd9e7acfdf247b53be5224dc153f32698fa548a4a227212a92ef2c89752aa542aa09924549256db7932552a672a89e8e60c8e269127be9bd768b2b5765efe18b32e325bde34e43f4e919062d2e9a4dd1b79ba5aeca867ce4e736c656767dbb6cf6e6fb78f3a68efb5b8ded8d19c298f3af5d9c9ee2d102a672757fb7a1a634edf2f5e6a2b520c2956198646175a412e7e54a5bc8ee3aadc6989b053ef4a6923b325dfeb34c7596b6db5f8716c2ef539af946ff1b472b313c4e0066210dc408c6373e9ac9adff0a269cba28749198408a4a4526e404e499d367562533a690b5c190859e704c40e9bfe0bacdd3285f8c40e71765881412eda7416e569d35912149bce80c0a17381471183d7a656746865911f079cfd70040e9692437c82cf2ced6a3671cba578d3be2e180ba85995327296d80d357e8c30e2010045b31218f1930569754529d3ac8458f252802404161a5142f4a0450828335090bb1d6742c460e0c39420c6cc82f82106008a3c7794ee4fa47e4bb0e05f525f7b7f22737aac21f5b517aaaf3dfe0cd3340b7f4222ac8605323bdd97a494524a2929a5320b230ec058064abb56b96125b989fb95d1d4267d6a634e7dd76cd5971a48cca9cfd5d734aef5ddce6fc585c947fc551f865cdf7e334a6462944b47124a3ecd392b76bd9a2d8c37acb9660b1b8939f85d58fbc21436f662632c53ddd28c117e691479e2c65347f5aba68d31d65e7bed356ee33e591473f090bc21e6e0576923b3853fa595cc167e4fe3c7ef3a68b6f05bad0817034c71888ffc85795461dab2f1e3f9f8e5149f200ddbafdec6bfe21179eee3c7536706ea8ac155dd751c273fc238effd89a45e7e4be4a7debf255884b68fb0d42fd19e88bfb6691f6b2ee9de3fd6942f243f222cf31392af7d44dacb27dadebf092cc2aa0fbde3607d4fc376cca9b3a68a89690ca98fe107bf90d46bd67c6fde6af5136b883ff7449c7bf94bbaf72722e5d7345d5bbdd6ea3ac2e46ab6aa924f0607abfc1aa35e5db0c9ea165cc98b377ead67b6667c0a6315f356eede2b5593dedadd7be5e6f7effdab618de28edb54b74ac78200e10a4ce1caeac1a943dfda27400a740e51fdf62f74cf7da725d28f9e209f261f117d15fe0d7f422ad6557dacfb09e19ac038aafafc58b427aaafca4f443f7f2cfc5999f908cb7b26ab84789d44ea7258814c42feea22e89f7f67eaf404b120c45f3882fe11f40f87b6c49b7ea4dd0d70e8c71146c038e755186521c85728c1f93d1f0462cefc283904f7fcac5cff6c1cc478d58a3cbe6928d634cfd4a71531aea64e06679451cea4c2a19d6468cbcf748afdf9218dd559edfb31fefd86628efd957d1b03c1becc9b64428a3cd1896d677e15263b591b25cf9f60993c7fbe9431098298637f460fc9222b5464b6ec3d325b57e4d09524bbd202a52e599c96bcc82a4245ee173475543aa9b7efda71e1005c4ab3eefd88b020ddbb829ae910e54a16299b4927bb655b251258a40e2c615fc3b70351b67d18ecfde836ec8f08cbad7f3fa2ede9739b0ddb8b3657bbabf1e66c7735b6617397569cc2777358dfe7a4c71ae2bdea5ff05ef52dc1a2fd92faaa7fa1fb25f4537f75ddaa17aaaf7aed13a29f4ae24284e5e809c98fb5dd5822d9d7744d537e9b999ffb17667e7bacef965aa44c4b247b778c22e6d85fd55491df06142387f1c913aa9f1f2fa1bf3da79760c19f105db2bd90eab7bfbf05d391a217558a433f72281e4556e1fad1c2c8a1c73ce64f701c69064b20bd7df9b149337235ed6af8f6f8cc704b29873b56b69fdf0e07e7f6ad5620ce2ad374e1c2704c1c93bfb8267f859c1337e3bcecb063903b0f91434ec6c9b82d930b3365e0ee3f32e6f86c620738716cc99749946cbf9d15b4525a6912a2ed7e9dc8a169c85f4b4b32262d5b8664701d39c55bf3c98420d491831e16e090bfbc8946014209e78d441e07778e50e8015d3e82848ccc9cba38f21752d2942a461a105ff28608ce07bf10dc1fcba134028da218893c4e2550dc3656ba8f8016255f562a6b95954a772bd6dda70f5a195a29ad146b390a3290a8d4564a2bb5b5565bad0f73ce4a71b0716a2bb596da4ae9779d8e2e74b095da5a6da596fedc3c40c3644fefc7525db7d517d8b54e4701b5955a4b6da5b28923891a61a1825a5d413daba54c8f9094937a6e97eed25d1eb7874d248d33253f2bf7ce6d94c50e7087bfa2533d522b0c43458c3840c913a348a6a8621ac48297e7bc7b7f55eae375e78c0efec8982ef73d77ebeee5b8efb9dbbdf78ba1e37ebecfdd6ee7fdf6be2473771ff7f5f834d95788833e98052c99c9bf1579ec10dab215734220b2652b82fe39e101b604638eb712b0254e04bdc8961f415fc02c2257abef49bd4cedf057ec9038dfc0c892929496ae640163c720b72be6bc0872289190aac828ae14638a2bc558f750a97532486d9bc1a7e64d604e4a29fdb046818d956cff0976fe8a49e0a9033a8dad452de4bda347eb01289d5a1e9a9696644c5ab6c4fbb966abeb22875249290bcef5a6f99484d20e7027f2f893127f853b44fef22343334a28f2c8561c9d50d8b21379b4889f53fd912fdc91527e2eadc9d6461c9c3e1c9c4516e094635a00767d1d79e02c604fed65c05bc79cfaaefbdb0cb5cb112a60dfed2bf7f14b32b7f6bddebe9001fbfe3ce2aada93cbc1a1cfc71107270c525c4f34e6f2e1f2e1f2e1f2e1ea7ebe0f9c6e27f278a7f906452db596daa2683fd0ba9d63b2fddfd6a974a3a8d56bd36c6437ad6f3a2bad7bd334e2d845b03eaef6abb5d93446829fbed4be7a40f053ac6f945847ea7e4220e17c93117f852e071c11526401434445513226a1d4393fce383f6c99b0a38e6beaa83324da6d3f960d39045dbf7a8a9445471e1ae4638e7bf744b603ecf157b75d3d3e41ae1d14842c207a89616ca549526966a1df451760c07899edd0357397cb5dee7297bbdce5e33a32786bbeabdbf41b4a59c9a144429255706eb7652a88d4566a2db595baffe32023d3106c40c927cc50930c4a8c1c9150e4917c228d6ca54592842218acb167827ce70c2353100bfe02c11d1f8fa35406dcf15718c7a0d003ba7c0409199939d94a95f88b483e0141100441d74b91c308352403055304837347486c7d74d2d9d1bed085bf10bc5fb8f2d8fafd23366d1a0ad9f2e39764684b8eb323c821d7ea3e67a595d62c9e488ec65fb1da5aabbdd5e29e201f320d12bbf099983323022ea9ff4ce4e14f03c39cc1377dae76354224f52ba55f9e91c0d631c618e629df3f2b57de57b43dd86aadad34ae936d1cc75f1f41f932846153ea2e3b7a450ad5eecdbe92da4bada5f6520e83e08e9fdf0e9f5a03469e3576edbdd77a4026981a4471b2fd8b1194fb097bb1bdd75e6c2bbe73df19461ed602db7f5afbd4ce98436be7f69b11947f2bb0bd05baed0c3e3d4eec3147fe0eff195dc60dfacb9da6342d8c5686ad4bb97a059865cac808ca97bbcea90537a78ed1dddddddddddddd6710b9e387f37d98e3c647e4c8abc732c638271494524a3f5b6fc5b5627729719c734edb0395abcf4e3de79cf1bd54ba7cd0a4c900fd1e9018d09732ca29675294934bb63ea79c3b2e1f2e6336ac66350322a52e270838aa5dfbdabe0f32b27d0cc4d1f88b629b9d69fcb5bdf4f0471d94d4570e4a7cebdb2f49ae3f344d36e95d80605cd97106c6942dc28eb31b406ddc41c5f6ef380323cce6bcfafd8aefb7763540dcfed5fbe1405a9b6559407cfbcff080b049dc3dde824562c15dbfb904184d7b66c719185af687376aea60a42c7a358bbe7d16c534e6783fa4f61cd5f7dfe9e3a758b3f047af85fbdda71f7e70d31d59f8031f3ba824168b39d936c8486612273196aae75e68e6f3cb40cc079a366b48eabd2792fa88e4da71afe9d44fddfda6f3e3ed3d8d9f13b284fb7a5263709f9f0b336424f5ddfb7b8fbfd3dca774e74284cd788e939e0b4bb0d4df7e6a4eab3ccd3dd64b3c214f2f899e9019df93fc797409f63146de3e45dfc718f8b3ad017f35e46fe6fbd9dabc0f7f77cfbdf6f3b76ffbd487a3e6b87bcef512edfd37d5e30dbf6a9b7a091621edf35fc2aa2aed23c2dbfc9eb9e908c3b1a2e95f81199bef5cf458d7d3450ef33ea59f64800030ef8b1c96faab85786fb50b11e67dd544262cf59a5ea27aed3b4d8445283ff7113dc9400ab0ee8b248cfbfcb12a51918475cfaa7a091621d5c7aaaf5ac2aa1f6b48d68452af7aa2eebd9e298d668bbee7438c9c24f6604fa0c9b6c27221c236ed2bb049a3fd64fb649cbba8b40c19aa72d02945030000000401c3150000201008060422a150200e14558d3e14800d66963e72522e9807034114c3280a8220886118648c21c618640c420a95a01d51849649122bb982f0a6cbfbc2fed9fac3d3c417bfe4543cecda8b843639bd02352156ce20c40b70d27543d45da3c57d4d74f348c09af6f4259b3f8da7b636b1516555e5ab36ef2b614ef18ed637fa0fb374c1f03469d6d82af49436e6d64abc6fa2911a39c54a4742568d5158941d2d2d044323b0dc39bb7ba738277fa1a290b634eaf5fcd65fc0c2c0d61b12665d5866b55dc65af96553a0a9542338e8b201b117e1a0652bb22a6dbb501552d8fa93d84f075d8cd70d7cd85e1a373cdaf6224d64249bc5706444d4843a29374db7fbd5c28ad03dfda13bc425de76fcaebd68ea2fe9ce23f78757e74361c5bee656e603582aad03f2a5f419eb86957986b9804ce729aaa7ceb6665e878ca8a2aa596f5ffb7f1195be1e22e83cebf8fc0fba53d7cfd306e43a232ada03c37a40d353d5f0f23d82a431b5390767f055a2a9bbdcf39129c9195bc227b2f77d945f78f2db38b0f9ffaf1e230a685ab85bca963441cc37133fd65bc91faa438a95d3a2b0161aa5a2ca0df10e0a38da5ab7ec4c6148ba7bdd029840f85dcb35a5b288a7e5bdc89a9d25330ff04b284bb39006220b01346aeca82c45d01ae0e38e8db6bbca6edaacbb7b1c755aec1232ec5150841ecac2179bb35bdca266e66cd9401d47adc0edfc345fd14da1086ab2da244b67cad42de4d43f98fe95424473ffc19d8c0d832213b1e764d5eaf5d7ea54c2d75fc06d96f30b9ac4d7cb52fe391bb35ad095b860a3f384221d14016ecec61046228a3049dc39f179b79e47736ece043d8f25c6d55ba9d0fd0753c6eb219567e9943821ed78576a05f011ac743f35e935ca89de82daab38f4769fdea7af617657fcea6075f60ed56bc0c382b06dcb05d01428df9aa87e3be1021a07355067c43c64c6ab347cc1ed4f1c705af8f643816f2c535fe0d371a6de1ed5389b1790e3a7a9b7cf34e902af3b01d6fbeba3908660e2e9e5f136599c5eaf3e6a5799ee8409d0da1a32b596510fc5d586f7f5282841c1f1b790efc9f96c996fa70dca326a3ecdf1d29542e71ba5c2f5c8e23f29fcb5a8a15afc22bb83c94a7534429c38a750621f8a0a5613a291684379b871d59485e5def477160b3e3e595b68b25fa13106618df46875c83e3b3a346168410dbd8a8cb66f968d359027ebeb52a0b92786a5251c5c91982622fbae146d175f5cc75d76d88dd05814f9fedfcda65151ccef11dfdfb548ca25dcefdfdb6b9a5118fc0549b57ffbfc26c45103db3c5adb8aaef6e59ef2fb8675152ef31b7eccf52ebbc2aad3b31997ae0982b8245cbfba160a9c754172f8f5c81707da8ad5872e8d3b4c5ceb747783a2955e8f935741f42527e2baad5f7c5d273ad900ceddcc89479d8648309d28445c89842e05949bec09d4b314ec9adfd9ee0474ddd640cc1b3bf99311142bd35745497162884122f49852cd71d1bef39db70c938636b375644c1121c816944d7b7039c43eb13a4aaa1974ff4131769841674794da9c495f91b3b354191b18a3cbd6069a8640d1dca6fb0419ce1f84e5f83869ecd021af9ee5244a8bc50efa656fa6698d96d99c642261a114c1584cc2106351ee1412eca0b0a4301b63681b289b373eed33add7693fc6386fe8a92a7d6a37a318a1628c45ba3a3a0cff9e493038d600633b955000c19b4b503500f8aaae4e2279437c6e79c4a6a60bd9454635f16ea5a57d4614ecae8027354fc1fe6cf32ab128c2bea170362cd111b455a4ad0d329e087e7261cd4a65199330d8208cf2688e6cd5008c631e281652a34a12860a3576d679716a8a63b3c4ab3880a77bfd3e0a689291c131b89fcf35b13566cb14fbdab4933e1c216c23e5182a5d8bb846b6f53466a8fa09a1b5c57a2d4d03f6aa394c69aecc576948f69fa220c78837d1c2d760297738008c63648bbd56c81a20868119daa74f125caa7bf6364e2ddcc6bcb1950a0d71fbedd5f5a1ebb301c4c43501761fa3e0b879b6f8d351c83e138476672b8e16528bb426012984f16874045a2b164648460f3591b859d69903475d78a0ce635f6d6eca7285119f3feb2c6e21bea5114f8762f3cc36f51bd251293553d278167250c222b01709454e3214d4a3582ffe6e581a83deefedcd7102c38bcc5cea2506074244071472acd43b95125b9f10c7d115146808f0281515093ff71d1ef318c024ed2592bfb674184e4fad4bd751e0c9ed9c201e2dcbf76b77a49464d33af2c3dd6dcc066e75ec6cf610bc133ffd0fa295e0ca1fad4f67f65e12fbca1070c73d49ae65ce226aabf1c9d3fd3bd9a9469242dde0d40a77a4b4d4569e7902fa7366f7ba96e34edceb84df7a9894b274c9043be164a27d239739c499a57c93ae1838b1ba532046be863c4397b235a6be4e7f57c9620aba1eb288110e822ee0cb406616ab80136f701487126d211b472d66d26a2f22213821a010d7b4f917526a545a1f22e2bbac7087cae46970002ae4f7c3b636949a7b2c7033a519718e594b3d774f0958e8ff34f0c939c17297f7858000838dccf3f121891d94cd0d43750eb1f57eb7a4a8546331bebf28b30babbdaf24ff6660efd5f3af4e45a293dadac2ae2d4572b92e035716a3fc7b05f76fa407c32bff272e2c7b08fd74af6e3799634ca3e7fdfc3d6a968c49e8af2c07e6f708ebcfeab5c895acd475fc91614d25cf24c8f306c567e6e55cda5157901f3acd87c93be3bb28c80cf5f82e6d5ed5302991e101c013e5bec2f4ba5eda07659d3390f3d9dba259a9e218acc6458c2b616d3066827c85e8dc6093f673145dcd491d245405510bc5917643992ac6f57946a8e0877198366ebaa15518f8f80588f226ffa24e5edc00006fca955ae669b832c7ff5a419ad0a4321513fdc2d12214685045a40f918262f468128915749030b93ddc82a7119acb8444fc13fe9971f312d03f51ac15ca73b3a9204d175dd20aef0af215b82f4a9ced665937fc25a285ccc2cb128d52828e25e83d57155a86f77b96a5181abde4ea9d34b384018871faa794ecc3c2c7b88bc5f9ea07edbe8ce3ea86f85e2a33bf2f95c23dd2fb71e1592ad991357ed5067fa840c4f428e68e5669986297a2bd1ecb6f0452a8983d3d26958b232cec74eea2ca0b5522d1fea9d860208e19470b068fb8aedbb51d31a19d86fa7ce84367a440c4dd3f6c5829d1f188f3bd2ec8022dd138838ed0444340d226c6191579ed962e7beaf2a2959f960bea7ba6f03db3ac7410870f15a39375dde383331000d58c52a9d19af29a8bd128e5e8e4017bfb9b116c13d200fb32cb50abd929affc1868d2a815b6f4500cb7e80de0274300b28d6b122c591858f51c2704ec86ce08401a62b2c39efc897f6a471516bfdfa7666fdb5670c0ce7ec5d2f0869a4ccfa733929289adaf90922291b995e1e80bb7dfd47a1e8bda7315bd7c3f7c3ba31a90d5a6f28ca8e0ae1050222079852ceeeb9c8f3515b4cb8b0b4dcb439da602103ea7aa1d7ab029c286117b11ebfd3b9fa7af8c1d13a513af551d4342eefef84d03b54c632046dc17b2ac9773b454983f1fe14a758a1a95d30abb78796f206a497ae8430598b31b22c340a73d84766e28319f7456b9c04802f8e0ba0d65fd07e56e61e3baa8b88cc008ff6f28dc560c5f4c5734977b47dcd9f176a16e12d990816647c3940665ff4a526489ebb827772f99f57ef516290e1b502ca31a832b637c5260c101e10670b2ea21422645bc9a8700032fee6be9489e6ac0a8e90832ea02ef3f11673a7500a1778189ccb68d3237f70de7df210b024d87541a3321c63af6b5b7415da2a3c0851d2d81fce162ce43d01781ddca3eb1363d6c2bf25e60f1c0dac3c4815749a5655a71bd69bc220b90021b86bab4cf7a3fc946b29b5e8ac4aae036886865e67386af0947dd734e2b67bfac410b871dcdf94bf3b412d586f99d1979040eca0e87c135d0a88fc3f75112515209090731a2a1ec5c8a2c61381c1c803054a469eaf55803bee18801de6f9ad9f32149ce5890cd99cfc4d46e005ced8bb432232f5381ee33dddfff3c627caed6ab449fdf2a2a2b875b790928d6ee19a161846ee236324017adb89be34056a515033f6ef53784247bc68181032e66d06c4e1926a59900bc2d594a93e56d7b1aa5cac9e21e9477b9e622c82b113ab142212a5d4d224ddf5bb858823c240768f943ab3aa768dbf25ffbe51e1e8862c72b2f200c257cdef042ab790a3dd29aa01f61d69db524feea4e1fdfee0c019ec9959704ddbfd8b22ae1d3ab828d228c3a19c2cfb784edae74b2e695762d8a48535d815e8f924a372a72623e3d9192d8ebff6eb487e07fb24fa2ab21374b7c127ff3c737c4263de5405e2eddd117a8b17863bb35faab31a58b86ef2f00a44ff5720796a7cc2abfc4fc0f7c0d8775927dfac9eb59a8abb5abeb5dc5539f1cb88cc7a8af951832dca34116eb4879602d1d322b3227d563cdbe4d45562ddce6f9c9799a88b44c09df5a7a6fac4233893c1f5c89242fd61cc4242ed896b8c92509eb99ca4956df89bea55c4fca671b97a9e30f8cdb2154a1e1f75e5e12e45e5b76ef92d75fa40221fe716996f55efea9e047c9379404adfd96d731526c2ac6ee672f5db8fe6315280be87d2ffd2efaa626bf6bbbb0bc9d4b938ee2624118176f1ffbfb94cf7c87b538fa5f64a1e7afb48b12030a1411028efc73021654088a48dca2db914f1c126909fcd1abeae259a746cb9ad6086922a2e350533becb54846bf4739343af04cf6f6d11cecc11a3afd18f73e0b429457325c9017084537e04211764f2758513bd1f152eff6e2c308f52f9dcfed0fcfa26acb2b0d3b9e3f6cba6704cc83fb98db007c6e3951b34b956ad83c3d376bc603e8c406892e915166d82f9df428e2f035e10364665026c45fd8706c350c3866d53594d9bfcd2b1e5d9f3b08a4ded4b3c20ea024bbc03692d11c81d46f05ff0e3c7c7de162511b2e51a64ff6f0eeabf020d636d24b7463e58df79101dc25c978048c3640303211d01d4afe485847e269630de60dd9ffb90050b241d766339b9b9db996701d0d642d37c35a312119da20753154c1087d6b524a99676aecd0a909aaa386bf06d9ec7443918c72e53197c639c8e8b2c962a01d8a5080884f9abf8d44f5f10bfc2a5130bf10fb0ab9dc4446a82c4bc2f94ca9b2ace815f3ee83d97a8af9bcdae7b9922843b50727504f2f7bffdbc322657c94a06032544db5ac86682f0f71f36565d778b2183ef7be71990747df1a5c78ab33738aa137883f2afd22cb367e4323961ed68d34be8a58ea60f68ee2bcfad49561311eb8ddaaa0d9f1d7cc1d98cd112f4ddf7408ac04372a9ea956a4d3e701c20150beb4f18c6934d5478f2f35b0e7f71794f2ce9e460cd849c1edc4c41be0d28dd5e6bdf16d455cb42a6d624c5e1fa802aa59ee405c8a35993839b57945dfb148e5b32a6ea4ac1c8a1e1106d618b5e0dbf73c3dab299dffe558cf6aa2d1785490738b7a3a85b7fd2fd29bfa85d86d180c1116f1dc7d8d7df9382631a3444523db4aad781871fbe1ffb67edc88ff1f8e6bfd77bbc91f26a0286fd7188b041c9344f1ec1d63b504ce54e4f1e7f6bb0423f587ed66077f279a7ec7a5bb1b29c797871017bc74b53fd3095fd40f6976b3d8eaa4356aa2ddb7eafdc173fafe5c5616d45d2613d3c61288e4440bab52480d5a24e3a5d511b1710c74124e1ad6781297a972123b53b6e4c62527f423f24a494969b5ff4bb6a28853044a878422de62093005dbdd934211d0d24d869125d47ae1ab2b890016b87d7f0600cc9d59e34350125bf95d8b8951421de477299bdcb8c5a48b21066a9321bf5719dec3e61ee3924e51402520bbf1ae62a876b5f74264a9d52f1d1e74823858d99a2b69518b76f8b5b4e048a8eb094b77098b30402986dc3c62f913bbc2f3b4bc2fe0b5cc621a42e7585cde2b5b6fc2c41ba02c6fd23436984b17a966a35edf6a2e3ebe0298d68829c023f9f38e4b9d84ac1ce2ad2746b71242c49192a4f1e1f0b779632fa0b6c6948a91a3b34b256d08d73bb77443930988592d39ad1196fc71688f77e9ced8550ea32537877016ac9fd74ed1490ebbc8b617bcd8a2063ee04108b2b312759ae27db35df104bad97c0689b6cd55a923fcf07bb23df2357e399caf49468c7b4023ac0c918f18c6aa4d91e15298a8b0446ac3ee855d9e53f6300b149d94b0e09d944898679ee949cc7f3bbd731dee05e9c5a8fc970ece0dcbe23d4db1a747fe4d3105d702f6882bce88a9a921055464893a4ffd042f584da745ef374cb7a020933baac16bd1c410ae920021cd0d3f5f51bf7181703b6803b6b866dc0577ba21f11168699ef25a96df95e22d0e52adbb40cc1c94e3ccb3dd001c01a34e11327872db2069c130574cbb18b46689070228f57fa082a1408a348811e00753a6c931db643f35c4732e74fc2a1a329f93b5e64b74e70cdc9e68e69bcb55e2988436fa8889e0b7e8a0743e9940d3093649d979273143691600ca436d098cc968bafc1ccbdd7adffd46dbd27ff6147db6ab2a6d4d36c70ac6bc84648b77c7edddea48d022bddea2c4367718eac6e29a66c325423d4fe9ef7969447d4cad0131546b0c53cef29e50b0512c35d8d61ba35224cbd0346e7407938760d8f217c0b7577a86fe5c1f183cc00077016b6902effc18e633a447c51dda5300c59d95a9a685941d832c593e002c0cd31eb4241bc8b9b6232846482c4e8025f86a5fed3a444da5bf4dbe414ab502d833e1782a3b1e5f4e54decdaf47f023f171a45ae83952001f740eea71fb493c7c97039ec2250077ccf942d4853f940c23832218a3dc6cf9a3d2114009ad73b8be77ee0134925e63b8a300a5f0c95ba5ebc365442e5a1c5e051b23b232dba520f221531c696a3dbfb6f909dffe405e68f1a94a90360739bb109bee46419374349bceb24e6f7f3d52735fb19052ebe4139a2008a212e85b201ef120e28691c7e2f53739b6ccba46187b8d5d75aa247a16c2280ff0e129ac6f1dad14ba749d49f5e94c0a112bc245d88e4c0d738a87fb64055b645610ae3bcc22e35610686bfcc05a957251947439bc41142d39b1d843699b1412e0b473c36640e0b952684354a08414a50a49726db031366f249026ad9b9b61c7d729042cbe204430b00d5aeff3dffecb067175eaf54c712eac252eacfb861bbc60f574d77987005058fa6eeaa307c3c55cdd91f6fa3ecac4f0e031de8599a9ba7cf101d8170229075c67d02a60f559e8d0e7c6c020e4673e5b9787a6c6a35a5162d8c88a7901ded1844e3cdc73a630e6df1f0f1b30e6c7fcb323f081af316b3f7b809767177710df5a2130c02fe2eb10dae596a3324546b2ae4b952a0c77e6bd53390bc2c31e9bffcdc0a10409d008d9551f48dc9266b4680908c8e9bca79c0c20eaaa147f6711816121b333913abf67dbabe8294502a47ab825eafd12ee9b0ccdaf051c2ca7f3a2ef3c67208623a4ef675bc9feeb7cfb5b0c8dd4a45b93d7006780557961555f00cbff85f54f52c9bda863e8c5dc4e92dccc028b9c10b56207ecc3159e569e0a2dd5455e7e240947839b48f1459570e644173d55d55ebd0871d993a158c0939c54af8810abf0cad938e102faa09c8b9fc2e7a81ee9f8b4c532b8130723a113a4b563a743e8c220ef24733b87229b16b3f1c592d69d8a4b947f410ed8e24d951ea4d208e24c42c33db745284c20a4a7d31e733182d4e91cf2fa892b38de9695d6ba4a4b8082c95012e51c0eb37682c7c4e3c4f359e6e6f1fb71471ab9b2ddc1011ca053b10eb2a274ff97c0a08439250fac34e25ce7098965bc8ce8a3127d1d18db474d51944fb7f8a4b0a6a8a93b913628b66e7c9dc20f4f2460a92c0f9eb19587d3ec95f97a9e8c71a7d0dc138912fc86e2046b8365ccb38fdf1fb9770ccbafc1e972640953c3fcc0e2c7f8aa1458af68a1d03d58ff0760240c81be8631c5c96ae14e4e6b3f12c0e2c5caf56d2f14fd9c284c0395828a031dc5909437775b71468571999003e4a735367dfe2b33b0e1896fb360882b675c0893b93d56817188e5b7ad6ade2d1d3706ffbcae3e72da0231c902ac0c03c9b58c2001b5e6b1625346f7f2ddb5c5ccb402b84f10b2c20423ccb78385f9f6c1011ec74686ecf989bfe705857ff260f36e372139755d751169dad01d3f474d60bfe0d006bae3b232e919ad97877f1288534d96012032be306a82f814513f18fc81836129a42b7db8c9a3180fc2f7a032dc48df7bae000d308fffb6184a6aa11faad3a3e1340da3f4473294250729cfa69e43dd115e6851cfb07c607694e6c353783c9f67f59b916489624af380c928657f7c534382a17a214565fbac284af416c1c583e158084acde6453ef13e82766253276a4446688fe615618cc3b748471b6d3bd3bc8882cd5639e98e8b814ad748e162fefbf5b1b655ade304d89022d78e8532388bbb192f76c80a2022c575596894e98edd573d7a79001f783118b1460362a9cb32fee1a31b26d9f3a83d832ea1a6584bf05e0f1d82276a754821217ac5ba9c9d17b2e8615345ec7ba00e01f1025962d14576f2c5233c593965824fa9d64a2e752f39b7f9350037933b0046ddb0423aca1d666621b714d2678533f222cd19fa9a4158f10a24006ce667972ce9804d3421e289e9112c2c2c4517cad25070dd7ef17d64e01b55aba2020b6da66453b546408b3fc87db840beaa03d8124cbcef56fc74be183d24955fa3efbd0f51b2fd593d65ea89508ce1a6d29d3f7ea96e739d2c208b545ba7a45fc085d0380756c441399e232cfe047649bf9bb033e3bf41d2f6897017048348b3e510b04a747a932735aa9e94477f6a6c79bf83062fb53a08cc5581824b498847ae24d23e4a072c9b956af0f6171016671b52a58e61749ac3d499c0986970d9b3827982b8669c3dfb91b00298317bf172faf6a62248d5b71e8c18c6643e5f36643c9cf60f9fd01d19046274931cc21eb6af73c30c1318660dd46c30bf2c7cc0657530d136a0fb1b93640400047f67412a8e6add5899d61b10adf2436bd80976a80e66b9e5f018db1d4684b02ebf9808bf8855f28c43172206604f236c659a7cdcea9d9bac1f226e81e81e98acdf46b050846683f682872e6e3492e62cb39035627f0034793915409e5488636afaa6e58f88af8c48b9163261f08d60721565480f2ece7801474eb4cd252442080391659e965ee9e9936377d09d4814f05c8da756f7a401aba995892ae073b753caad43a6b4c26ea14db35e8e44701591ad1047e2b9b65fa1423d802d266e9833c4c6155258739721673668ba3d3675954dab56054a50ff147e7a045160a51eea7a34110557406923231f2625b83aa5a594abc8e72b0b381311f0d76a1919230343c11e3c2484dd56cf66c130555317d21dd14bbcc055d9e60f8a0015e1ca39293f47bc8ec6c0c0ba77b42c2ad6a4519a7e205be67c29478f322493c8d2074585982c9c6be4d036bd63593ad06701a7a3ef854cefb7b5ccfe98fbd4f6e6a62e4b7d9116db086fa188e44548b1c1fa2c7a65598fb0c022013801ea456d5ad44c33f24013594eece71b75505e489e90488cbd54cc9ddbc75f423699ee3fa442fb252c513dc1396e7394dec4402e9b5b49a7d4655184b0c11b1752c91608cefb8f97142aa82cd7a993452c4845492a7b0256e885e194e2db093929aa3fff8fc6bf6633c96821990f91b2857d9002d16b015199dbc1d0a0f21caade5ffd402a74081bfc80d14c0170fda471f9b710f74c743e22de594bd9e74dd4257770e63e07811dc930f48a05ce807cf60eaacbdecb1265d6e9a069961c943492b7e79e0782e1fb07037dfb358144ddf9ab3cee2d2e35c22ba6c1c6fdcd39999ffd35c6b92428d0b908afaadde4cc0bcd39fc2b7739bc13b335697fc2c3258856e86fca427255feb56dbeea933d240f42d10cf93794c4057c1898abeefdd413f516c20a81c1da423638b85eee8032784d5dcebb1f2780b0470582192b1c9502138621ac56ff04514e8a7411bb8314d063089f1186bcb2f8511b9c96ae483a80fea0284b705c76836d6d64083358594bab0cb22bd288473338bbef6737b84c92d57d3b89675d58d4675189f6d736f53576b58c0ad2837792012c11437ff513d86294ab2c1d847a2858f88c15ae08eebd2c185c6205435f6e984e7cc8d9ac8ae041df963b4bf8a12cf40d520b41dfa4ecfd7d4c14cfecfb7f9afa9a190c79b56cbe0667b1c1bcc513dc335c0642ee54f61f0e0d68f0563daa06e85a9a199951ed455e1c367e40f305c35c7f01670bc695d6c14b3913293ceb60e36d4b4db0f767431e859d208161d7616768af7b15ae050de66ed36cdbf3dab09fb9aa2c50c8e9b6b711c677d64f643ebc8d4362c5ce2e98f76db8e592259093ff194c950d75040e9095602ba633504f94172d70ca5d4bb189fbf5d09da5f85ef02ff82436eb7b2c935803caeae8d24e65ef215829fd522df7d7d06d7f11496fb87b3ce6856e01d6d9da0cf2011a1243b74c2d5e437d6334747d8ad3b8ba4ab656959cdd0387d06858e7732582204c2dd93dedbf0a6a13c0cc260cb94726265d8fa56e7869907aa391af98e4ce726ebbf26b5ce2d376bb53dc97e33ae722762c57e867bd5a929443dbdd681b39d8c632454e6689c05ce6e9967c2f192c30f75889c0ad9b51dcfd338695a71ead44e9110ba945ab0769cbf282ffbd6bab2e7a813b97ce7db68f08fb6bd1646c3021670539a72c37fb22d0d6ae701e452f9f133fc8336b8c6a501c6087bb66dc22391bc56ab1abb30000b2d47fe0b0a1a95ffcb6a14267e50b954192ca59dc5f96a504a24ae29ba3d2120ff6587ebad14c941ac74bc33430bc2e615da78e6b172b215ee0921c9a270c912c73e43b975fd5e6293806ab06a88b7d815af1a44bc30e825c3776264fc36e8a94d0de944b4a344c1635f0b08e0eed209758db07225a695b3d6f9405bf96c057d949dc131419f6913b8495d7e2438006d302f18fe5b39f1c6a2be0698ea65163a14928f3f53a10fa17f674d6bc434a7588f20e075923062a5c734596b70ac88ea4b798462dee5723da373ebc6efbdeda8782b9c15ce606db43745a2fad06a281925d9eea8114a23bb4b45a1565861b98d246731c54ea9f9dc505c16527c3655551196e6088450b325fc75971abf4a0cd645809b7e702e2bc4334de8ac9e1aa7f9204d01ee60366300024ce30872a6d33d0fdb8bd7e71d5d8e9a00cbdf408d47e1c765911d6e2f2391e6d92d443f4b81767b1662b758d0520a02d5ded5aa4007178741d7f4f958da72102ca1b8d9b8ed65d4057cb76f621083d5d56f090f1fa8cf67adb07b024dd27491bd8023080e698353b33100fd9c661831466db2d7e35d920094549554e0ab54da1244d834a2ddfb5cd876bd030b65cdc6d21c10247335f4a13c556140dee1fdad48bf21f55d0be3203bc03f78a09f0ebbddd4822f43f612d1035024ae5a001155a4625b7b16eac33545da1f49b40396ea50700c4465eb46b873f3fda0e02d79765b1625fa64c862b321c013752cd7c782c252b6f843d303d6e2ab2f99cea3b1f777f56505476c13c05f443eadc5aa976e70cce32c0bcb5e4436e1abbbd469a522e26864411f5c6a46aeb800f111693ae85c083db23d94da041ee3d7ecb05ca8f79a4c0b60f832346020d0cd706b453ed3c9751a12e0279f0f7cbe66a32ad606f9ae68f653ab97066f149e19b838433543925b54be09e10e0b4d282c2561825aa4871986a6ddea15a212a9cd6cbabdb1fcda2051bdf3d28c5aa29d96906993c49ef1c598df066e904fbe61e1394f03065787429698d5f8cd2a8983662dc984c735529620a517540e29e0ce0231f64e1888325e7dad545f83baf70bbe2a8443319feede9e9a23a3855c90be74839101d8053ff4f4c4fd854bbc3745b89d3aaeb074da4204423523b5f485114c1d0cd4d6af4f99c3b69406ee3c758a37456338a76562cd5a189ac8b42c1c86673f722101134619a9868847996176e33129da99da2e81c34cddf91a6530edb029c41b03a79d16e1d106bcbe5b75b3402060434255bd4712f378081ab0f918819e5d963f871621cb7f92d02a3628bf84f0471751bf1de4a0b1ed7f5834354008eb95b8d2098009d916a341a1ce459192104f2a957cefb8eba30573cf711e6342aa6ad8df09377e0676004615c90917513843552844d7209cc3a60f6e69fab31645f51765117f59814acd667587ba7d96ec75e8e378fab14053b54b72bf6357034d01c260b56f965caee5f366744163ff671ed791ef458b0f99490f8a0aa025156e549f4fcf5481bc6facdf0f0dcc1c3744b298d9b15c5aafe6114bfb04e6dfda461a70c18e6a51fd40c054d6f1a87a9ea5de23cc943b1da2e7aa66a484a22e6a6657a0065a101b30118e5606b34c2b74c47f3c8b75b5309af2e1b133f60e58b1171579c31f3a6fd48ec8b144d2d1bbcd1ffbde768bb7e1dba51a069a75856b596ece4c9eda407a8aadf0b4d06ea302ae1a9d4edab900a57050b8a51a6194611f702d61d3fef8701f18a8d5162ee9ce4114112cbe40eaa5a6107efd204d264de9ccd15c4f8d979cf1415f570371d59c7aea5757a441651338feb1a16ab3a0a460c06be9c3e8625b7c44f42754d9cf2c13221286793dd5ac40c12301ae7edb4ea89c3e475e43408a2e6f0afdb3c8061b2f7f30c13084d032070c29889490848c5e299bd7e9fe00d2cdc5cd26e4302d5233434093f5a6c5248c12f46ab92d8a899692432aae5f215142fac40368e2a6a31eaa277f122e0fa45b463136f66ce16c0ed8260ca3060682ebc1850160ca150b520209009bace91cc2730ef8428b5ec0a637a2b6ae5c77fab836850255a6b09512a35f0e7b6640d26fd22c03b3faa5a8a867a691527270e9ee8ed9a1602c28833531d3fa029cfa7e2f642ae4e2e12122c3fe79fef7b824f98dfc89f13aca3370e0bf3e62d3af066708e1652fea2eec13e78d7f1ef8809b2a7a956f3e5d2f7f093c815ba2077aaac5406fad390a7f82e65d2ca4e2665f3996a1b781bbbbdc71f023795fed97193abf6203f9643d3b6afd099bef22e448f8ad327532a0b211ebff9b02b7d5edbc1cf4e7c2f0a20fc44fdbc375b935290fc0592259ecf7feda369317371430c826c3d571698c163fa6c5bc35740e2030fef24a16cd8e4a83b11413f8869e862d7c9aa2f8919eb5b03cad4f811fef42327c7aad5cf8502229bf136100a61e300aabc2dd51bcd9b1a1be80605df1a1f45505aee97da95c2736ee358049ee475c21888873df29deb7bbe5672bd6ed979e09ef54525bd7bd037face6073329a28114cfb83fbc91bd74a3899606f70e09945fbfbf4eb18b770e17ee86954a6abff8246800f3a52010c133c1c475cbf7aaa040bc4b8bb76c985640bd577c8d5ef4269ee1d9e73c58b10b062cc743601bf0367bfa1676d01af92224907c6ce12290b8aa77871709ce1807c859b3e3cb1efc81620b22a7a40e1d581a07328e825300e16549e234d7c8baffa6e79849bca852988929f87884400330509e5e4e92682165359818cfe6d8992c70e7073bbde1ceefec5dd4a6fcc7347a0433248f469c08cf874773add1542ea21ba3e9e2902a9a0d8565d00d0a4235304d6b5d15d47ef6c0259aea556f280977cf453a24bda528c5c21a4652d1eebaf9d7b45c0277313d7a209f6dc82040edd77db0925a404cef493da27599ce5ca87f10a13b3851ca157c3fa6b040f5775dcbdc3a92f6b664c101922a5a85f99f5a58f56f0705e0b9fbbb27fd7c1c11cd977b00f3086701fd5a7140a413aa20f11cfcc04ee37ac9d3502ed5efc4bd11858c905650bc22a932f1a2890fb89a481d95044a8e6660ade2ed11dc51f600c18500e3b913ddd3a3bfa217c561ac7f97991424861caf976a694c3910a8a961227149b55541853edf22fb3ee0f0050f296bb406c8035830ec932786e052a63a22d88cbdbb85117900623a10b314d8f546e8fbc02d16069d053f44fb885019fbc1e05944ad55b22523a269105c51197077b07648b2d53cb8dc21ece92fe3904041659b7a73195da2f9ebaf2b1a876c54e35eacdfbd6b6dc69706391d00775fbd0f6b5b1ff87da95eb969241e5b4ebe18e8e08977af772c2baae10aa849f3ea224c4a7ffd05baab397cfc37e4c11474868fafb6d97bec99e083b7a81d258301df520ae2350bba78057697f2b6c8f09878aad2084c10a0c874bb0e38227f60b0077e3d9867a7c6718ea2f64dacf8dd564155efe736cff8036bf33596019d09d107d5cd7103de138190f00e95b10de95b0d024b4311ab98956c917e791aff49a0d704a3cd07f091e51c482116bb1c095f27ce78604fa71c74a2922138f320ee4a7b50fe4c698c973a01b42d8c44c68b7c1422be0252cdfe99ee38cc0b04ce3fa9b4b5730c3cc686ce20e86594407f14c8402048acc06418d14b474d4d00761df816fbcdca0bf25cafc1df5f4f9eaa61a5b0b69fbc2f3e648260639c8aeed59389fc425e390a394048555c72f9e5b2ef86c6479af5d0e388da6c4ce6717402980ea5f50ab835f0266f22ff7957a5674ae31ade2961573710b75278a5e792729e81be5ebd10570d305b770f6af19f31889b10fb95b15576c1282ff7c72759b8eb7d42c1967d3452878e93ba02851db93c271146bee10f5c3f743ab8d8b94f680b9a875ecb0c895839064db72b813b18aa3d06cbc49a707a51d17193d0bae71d5f3b382e8859c023863b7d1c5b4f387de68229bbba5594507d82cdbede66d0ec81350f9becd96f20b147c0138cfae2f366f621fd1e3f550572cb97ff7a1c055f91b25606038bcb94875c7dce46a5977859120d29075d48282ed3f20e6c2ceb3c7c0718dea1b7be1c90fabaa0ad3cbbec6fc093d2e386b522393631453b89f22106c45bdeab146c5af3fa8cfb44e20da29eae95d0494c32d66b881fb3d8d56fec8650601881dd9cd7462bc800ebd35957859c67d68a0e62507702d1bd8bc0b1eb6cee2693c5340a7dc10e3cf0ffb48a7402322a3da7e0238bebb1f29e98a608cd2a90e474baf1f164bf9b1a652146235fe2701f05ab03e4d6f9937104a1a8c7f9443ab2ff10e17cdcfe2235dbce7f29702fc33f82958e74ae651fcd9e815176edccbded0ae1042aefb1f84c26fccbbe384baf325198c18c2c9e9aa38b8ff77ef8ee717863f3f9987efce405c9849c3d27377f9330c3cb87361e0d67fa44a2b9b98019c1996d3400704952fc1ce0565320199e873d08382e91f022bbc56c75832b7a36f776496b6df692e608ebe7346a2b2c81851a86f1b4afeae66529c6767daaa1eb176a7938ef3aaa325c5525d0d8e40261f931160ac53add06c35b2d1413c3ee0fe60f66fce1cc5e48ba855fbaa076bd16ed82c5e6b9190d44b0aae81af47a13ff59ad34ce9af81c122e94fde5f62620c2aeac4b72a2bd609be7f2e993678d495685ada8ee79be24c1b2422d336f31517b59c9c18202df64146bda277aa421c70c201bbb0ecf506ffcccd13b0e53b49ecb03e1ef08508ea4dc00dd71243e48c7dff3a65df993f56acc53e6d42894ad1cf2182a8019db2b91e46117ba8a0d3604a0a2176e10024acdc0a8d09544464d5acbb15363910a9340e6dc56fde4247d8876623ae6e2f5ccedd1221a79de46f175972ddbcb4b0b07ea6938435d191e0b683e7548dd6b3ca21a165e680714800284458bd84b4562bc11398de06101a6610e06502e17561aef547e3c5ea15f2087fd1459d7e9523aa3ece8f1e6d5bb4312766bc4531d8a60956afe37b18e73a1d722d0730135ae6b16376f32aeaebbf0879d5b0983fac4692f1086dffcc83c13532d659eb527e904a565c5449bcf027bb892b49705d1868bb87ce6e31aa7b4a0812a0d202ca935c22dca9709667337c2e6113f9ba9de5ac3586d0d1d6213e2986d6e9f16ca95d884fc3f54b3f78c682141c45b79b416a3611ba0e6776103a9c6f4dcbcb1c313f1dfbc48554e604c490a1b695211368b7eb5a91c99506579e84fdb5e7d61811ad4c4be4703e9d64e3e3f5a6457fb6f0164c552359d710387f5fecc32288f5f9ae7f54357d52b363f5565ddc81650b2fb57aa0b07cd836c6806980942b08a8a846f8a88ccded450168f5a8a090095168bbc9b7901c7dea1e5863450a797b6b72d5fafe6fdf77cc7ca25c2744e4dabf7a66fd6009c8c15388181bdd48268260c3338c9fe802c8740657bc664a04f7687c40f8574dbcde1133bc05339655eeb910a15b5e2e60f966f2207a156ee4edf0d47f9187502c67292921a472cd7f73e4d5485a73081681fc4ae76e429e32d189e75c0bb48028e45290b3735173a9a56e04c603654cf2fce060b815cd7952330281c74f83ebd57e8211c4f91642a53bb8a0dd168e4e46f20fb60b29a598f9faa41117d0ec6d0a0f3e485e53477fa1151520fb4b748fa141e8f3b5a394d6e08e9c74635f44d0ba08553c9696855f43c32c8d09528cb7624d5b61a11173b3c51173fd4fd7988cf4f0903408d9dd929934f2885adf8db7cc18a3ff3192e126508de67420b7d513fa1c79fcea43cb6f405f48ee930dc0f80ba8670df6f4a8744e9e9587421d3c7d1003a68f1e39c74e3944e6e20b4d5a28d1876f9c309d8cb297783886520e34b4eaac1328451957bc6ca65ea0e5c8c9b733322cfe229c35633ed69156bc5102e0b3330e55128d8ea1e7a65fb6d761ed18aa4a7f55563c738d960f3c7a1f669e6da08e6708133506fb6a7b2cf66cb669571761be9af4b900d43f25ab6a173acb5aa6498d6a4e9ab0325073983eda9e3a15950ed82963f650c51a2374d12413e15bdc06837ff90261b677909a288264e17c26e524420eaa20f8ec3314a9f5aeef55a30c76ebe8a387919cae579beee42590702e58d9fbe2adea6d87f61cdb3fbc4ad160990e8567875744bd051eb28dea97840a5a692dd51c6a11a53bab7e9a4f0c12be2cfbde340baa818bbc5c3f4c311abe94ea43181424386c4cacc94ba78ac669715170917e797f293258cb5382eba6d442a3c2b1b6c5bb034679121ee7e6ec5ae82cce9055f58a46a753cf169f715542c93f744bb563e4255c165f6a66c12912227283abc50d0c36343fe249db20acce81ed99f58f81cc3f694c135ce3367b8b8098cc0de9b196c1e08b98c8848547991d3e0a8d1088e2430b919df65cdba4ea21ff09631a2a77bd886220a6f6e8959ea4df3d42ae511982464bbb2557ebd253298403151e66d66000ab88ddfe254d9980f99fe917b2061955cdae91345ff90caec5fe0a80a50d25aab11bf239512446088c2fb5f0138f49ec246b08ca349ad5e463d03e183e488ea621639ea260797a82130263f88f0e440a6a8d8ab576b7f18398292d7a9060f615c7047d27466f536e71f9a045321b23cfceb4eb00b58f82c6e4c13c514a263b68876461561a7101337821215d39f602a5c770ab408a2b64d01526d5b8e9f1cf7261b11ca3003d29eabd7c90809e14f622027b855afed059bc4b149e279b288591ff3270bc26de46d5e0733ff39f2ea9641c9c4687038c7f248bed577a0c0f56795868751de0fe64f7a43942e3536e769cc875e9a233621c4ee964987841b983f846079f73f1eaa9f3facfea74ebdeaa9cf8ea8040cab6254d5ede9272cdc01515ea3ebb7783e3ad13cbd4697cd01c6b8453e42c821f1f3f2d137d3bea0c0648cd686cb946f293069bac368d24c9c2931c2d6a32ddb82ae193e8bbe0bb361e69fad50c09ccab6619fe1acf9ea1fc356c64e9c273b50a0997820c5694383d60f9222377a32b95da09d95c4ce5976364e401fd7cbf39583d8d6a8f37c9c8ef6abb47ffeebe034ca287cd993577a52d91190044c0f931e488ebc433621d2fb99d04e55bc211ae41e5cca4d33fe80ebc64fff6503ee02b6a21b18f0f3439fd6e8fa8c4e329f5d74ed666bf99858c0d999dd48aabfa94a5a743ebca01f2047afd8bf575ff9f707aff7de2f557e1af5e47dc468edaf5c3c41ad0460315452dd6a4082f4ecdd9d0f73ad452a0fb8f08895e8082c114828138043bb538785998754d29c956b73fab6da6872fd67ddc7f157ceff009466f646b5b40fa8fbf9a60ab0e42ea918d5da9f77ebc5c38633e052c7024659e698f05cfe18376c06a1386a27422745345a8cfeb03247ac43af36e761b44136bfe17bc408e186c09dc0c54ae037d0996635a2532e9f9eb66eac85150330dc1e9f805333c946d4cb6800ebdc30540bc839059d28e40cd633e184474c5c13d805a93391c4971e4e19875548748d47a541264b65c1ba3c312349ed3725b6cd1440b4d6c9c8ade7f3ce01c2156d467691a41457b503ed2eff51e1276de5077f4acf11e5b2fb39820a1ff6e8069da65ddf82276d07784ae8386b468a1c4e04346aeb1f498444b48e64712baa16e6ec88c05f9ab6d3feca3f8b0f7341b2a1a3a65f85ef2680fb91de216a80a790c87e1a5a43bed7518410e2475619ce91a026a7a932f67b6dc82001fef64279cddf48e8830d449e626bc06de8abf6923d07ba1d71916873f260a6995aea0ff7413a136f3b105d1ab31455ea8f400275b514f7f82961dc56227fd8af4f967d7783c81122abb794a7767dea2858d25ee55f6ea4b91f124cfe5ecce270dfa0bdcc447121ebb4b8768683d153f569e1e81f773aaa3b24ed55f9fc124e3f6143c926fedb05eeb144f17a4465bdb8b9deed684b08e3c9f5a7572e5abc4fe98f30fe9e5c477872ca274eb4338a5178b791e8b61f80a921021ad1af1c2300c3aad98d10c5ce18cc0431e2129a66ddb8d6badb5d8dc053a40367c83da37e219a584dde9ae40e20fd687495dec48d82079fabddceafcfe130a7bfffb2b83b70db3da1ddd568b1e304e2e6a7f6185b5e7183783f5ef35d32fa04d0f40764f34082e2a1906a43fbee9e8c01c91bfa1d8884113eac463c1570a7454ffde1b8c26b92c4b125efa8eee32bb65fddfe8be7a4cd9cb8a4da1a287afbeed2034c9971cea43748db73d2bf41e85285f71cf79072dffa5e2a2301c09c0b987e37560c691115a947b1549a22eb27c41211567447a6ab62fbac4f74a747e9be8a924e628724dac1eaffe2f33634424f8866f428281b9d338fa15449d6b56da2b41f241ed8d186b55cfbe949a866fb9049ad244f6bc82e4822bc701d6292f4c4205e52779f1fd3ea8575f72f7857e63a1f9c1f62b2272b035cb136e6fbbfa8254e491cddd3327273b93d2ba94bdd8ca3617d546e4afff8156b7e84608c48a5b6940774768fd1c370164512d20cf00a810de139bf0251848d5418278ba5763e0100dba8a06f996e3f723f7c016180aa5e61376957b0fdd9315e92cd504e3bddc51911e9831a1a817e736e5ec87c1bf2b0afe8dfbaa4897f6160877cac631af59ea7b5e54b47607d11f5ae2a05d48e33edec68d76e2a572cecbe7a233379de56d6a1710bacbe6da4060e09d0a0b05050754588ba94fe69164f9163c2157df3f48604f71e4808be9eb6f74752d982def3061ec139b89fb16d7940a42a6f4199c14d96d362060b561c54281de8c7f8e5daa5297afa2ffd1c1b02c50d6b59588fb987831182cc7e26293397c77bb83bd930f32fc6d8bcea98d00e0255c1b09440404429dc165c844fd71ec460386996fe7f96654f3589b5b4389898ba79de0f452c4729fdf4cf246de6e405bc642fb4e05cfd34cd2feed393832a10bfaf618e2c1d5cb5d561cf265043c6724acf5676f160590fe4968c2b612d3dd7dfeb56d00babfea4f7ac5a7fc8b0a0c8c9e0f1db6cb1c1ced93afa1fa9d8a18043e38849ded45492112c530183f7822bd33b720d5de5042ef1ff2b22663dfe70e0188d132da84533dee0e2c7ead8fc60d921e8f30e40f82fff6b5a06728de8a09dfa3fb8441e0ffc8e5cae7877889fc69a09f716d62d42555cc0d6f5d9d1a5b656ed1821cf676c82da4c020bbc1d9768ba5dfe55b273f9da6ac394cb8cc3ebc84226e64fe1cfd8c10142ee80a41ac3f5465067e324b107d81c7a5152d79b1f4fddb09efb068ebe6046a2b303c7dd0e7924c8d2230d0d774a38e06dc650d0ca6a8721df40b1cee77e1564d750e6bdaec8f4aabf3ef00ecc8a457214cea33c4a1b7cee0812ce3b2a842b030b7ef83b562e19bdb226efc37bd126f9800b42340dd7be09f50b2061f68c271a304e6d1ff9d6b718f07e6a624a18d2053258a93a045a4d215a0db03c199fdaba9d7f4155fb47eb9f36a3bdec28ecb270e758cf5ace1b6c51dc7ae7886bcced1d30f0179a27734a93346ff325512211ff4177229b8acbbaf8380a6fe4faccd3c611bbc8c5e52edd3c092de978299f63b9b8ad3e4d9cce27c5ca1fa1391a200ee8f4b577bdf16312bd89f7ae915be6480ef4d9d56d07c8688e7f217676a93b6306744fb52164e8466c5b361622384e0eb5ff0ce7dcb89f1350b319f8836e3ee608d3f6a896efaa4491f2ed0c3659f14fb620a0ef2530e2e1c439000876d647106e8b441aa7728e6cdb2388172d4c1082b92f6b39695f904f53feb9df6e4f19e94c8edcce964a6e0f029ac6436d2aeff96d64d3c3ecfe673f378b1b154af04fa39832361c78740b04ae9ed945ed637a0f382763fe0e0d84007ace72fda58ccd29c2cc3fdea9fd46c06f0ac6e0f15134a3b042cc1474bc83262514b4111d837077bffb61ed4730fd933a6f670d211071998e3af12c57a177ca32f7d78d871aa786c1d9746a6e6f1980ffd62286c3bd07168a5fd1f76bd72e3bda71eec0f0c748b85fd6aa1be3713a0c40a4a867c60e8fff3f9d0e10bcd1951eb959e5ff9d9368cff13ef55d7babd7c39637b214e697f19fc2d58627852ca4d6a5caf7e45e0936749b57288f5a621a08cf83aca0c031212b3b0ddbee364090435dd97dad7ea587780566397491a1498915c5941dc0290d1b444ecbb02c80caf35e2b07713d3f5bedfcaf8f44ef24a086781aec81919b8d66bdb61e541ee9a0b81d608ec00fd0114be82c295c6d5df4402bd5d922e3179daf0f015f91a558056a888d255353ad2596c1c3d7a68a8ee9f49579f66159b700446832fb76c1c2a3b505c9002257697c479ce2cb85e1aa66008a67a49f9074ed496cc632d9f6ddde9bdedf45d4e9f5dadbc4a58eeaf492e22efccdd3fbdaddd4abfb8bca7b989bc3377fde0b27629f94eee397f06933d7fbc5a774d697228735878e2d7c42cc8f2f4a50b3793f364f25bcc4be666aea80e334ca8082cebb3e314f3b49cda85932478040211509191b28651b936ddfa607c33291ad09bbe95ffc42c3b15d6c652924d20e88969883c9978897dc9d890d5e03d37841b3f4a6efb3bf059dba911cb3e15fef73b34be985779ccb3d597f2eecc99bfa1b9c9efdd3f193a0557cf850681e73b05a8a8c8b8848661764eb5e1e2787c113c4d851c9be187e6365643508281260612d250000eafad78264d628e1ae0ccdafd8bea7a35da3caa9dacdebbfb3de3337d9494a4caba9d7eeeff59d4496585299027a88d6560ec24d659608b85a594f0656e2aa5ac308aeb7b426931f1cee6f00ecf0d1576b5eb83d500589a6720027909c0bf0094c30f2fef37e4f373c742f219e4659180305d2633ac67612b55846f155d98ccaa0aa89f08991d5f025d240f63989a8cf3d1fe6cc4d5b26d2dc9faebc1bafec14bf35e283e35f078968d98c5e69dba30522d4db602a898438146290995dd5b6b27ba179473b9e9d93025e425777919d9347a3a668522ec60565ff58d190fe67a46f17d9f6a5d07a1c566a8d13e07bdbd39fda63ea40d203b0480c943de305dc78246858c2873ca0fac5b8879bebcc2a3f389ac8a8767f9c8edff7385eeb664af256486a3cc96fb82c213ca75662e94defcaf7891ec3594187001677436bb36841950f3088c0c76b5581f753003dbd2d4442acdf217c7959a50dcd02488e2fa100a13e60b21f231c83ec1d204ff559405c7594d0061e5f8e3fc29ea8fa1a60cad0bfa7269226b91316cfca8e1f060159ceebbcaccd1181502fb504888f720ff8f2572cdda0fed33f5114ded705e2c81d00f59a9a7dd16385860220ea95870cf29f336641dffac553eeddd6ba6b69d20b97f1347692926cb165eaa592ba3aef6e92aa4296736af65a7418696265ab008e6abbfe0fbd8479735418191f05745dc4bfcae61ee75f2b01df091871dbf0f447180f768c22bafd8c460064de319627d8e13ced97e20f56753d2de2a4b354cd6673d9b645a04207cfd9f7efe7cd91fc0b1b48062904cc40ebd33d0de2d0ad2f4cd40c38cb14d765752dd9d8c76a768d59b40063a757d70a9eaf1f9657df2c7b3b0037f7dab525e7ecc6d608351bdad158de0ca248449bc09bba7c463ffb42b887afc71dc893dda0d3939b01cfe17be1331f9505075c687bbc954189bc4c9125b132572cd57c0dbf736c586951a2106234cf6e4a8f8ed092cc296db6bae5c3aa7759a3b62875de8f330f49b4e5544bd852a0807183a0615542f34830b58744a6ae5b1bb380bb925c7676346effd536571610008118955284bcb7ff38c45a1b95e4b5595e7ea15c7a728945f1ff2db8ad7f73d790d78a70a3b862a45fc4d3f8a051a9efac14023a257ddc0ea789a349f4c3b2c6e1dfb7ad2397029ea4fe28e6c76801086fbbef32792bcec723f92941fb12ab14f01efbde56ce42580cb7caf94c6f65e9625883ae30fd6e2200ddc008c2ecb61d0a95eb44b050f1ca95b4190495b8fc218d54400c869013ce905a1f2d759e3c84b192020d895f3005276d89499bf9643eb6a0fb03fd36b622ca6d098d3ab8bb81b217c499b2a315583618283df4474c2c81a82981dda9bb6f5e6e562d78829d90e9d69ec9f1b1a8aeb4f7f027a9617309850f42392a0f9e78469aa21ce173568bd1b63fbee8f3e2d4ea38ad507a3c633bf6fc9e40bb0df9797bc25b03f703041c8cb187301e7a4606597e53a0a701424bf9304bc6b53beea2a46d166ac6042f968704edb4a9a4016081fa03b5aee872aa0b8ba25fd1f8da4356d4bb3f68a1fed16cb335f8492033862a391913ad51a8dbf05accd33fdd2e52eb8a83b090eeb456d8c1f2787d35b4544e0b066fb9ca7ff8c73649f424c2a56aebc941dbb7dce98231b8c4637e2beaa7537ae6a7331c0a04fbe83371b8bb2a99616520233abd97f12adaab5a92a05a09f9eebeb094ee5c7915049546dec62c84041204ac9a148a911ed532c53369a7b4762d9708dbc3b0e14aa5f0fdeeaa095a9d16faed2e584670aefe05b5efa482d0ba1cd1b66062f7c04dd580dcdb0fdeeb958bd90cf7ff879d3b10b66de162cccedb591573c1c532c1e95a6c4b93965826b8989a4a17caf7cb7b68203b21add69fdd0578cf7f1aa5a3ea4a7049fd541a33418255d22f1d57911b4c710c4c29b7c5dc3b95a73bf0223c58355d42b782b0950c5ecc2d5b25f2119f834c196a74c023de3f512f5abe715bd622d5777a887b954cd43127de2caf428289b3ffb453f5cf57fadfeae263357f3b5b1c7986ec1364c1d6b457c76d0bab181d39465c70a0c15fdde4a3d64ca0bd1a426610d4f74b34883c8875dafd7d56d4b5d66be0a933c323e15ed9589fdc335653e59459350a152f32864573c1ab04ff011c7c5877e35ad9baf4df438d14f92be1c178fc0c107ac29870a15717c78eac9a7dec62ba7947f059b518fa0343ad29715ef5db95961765b5972e05ed6a9ea75e49f8d5eca17bb61a216d5da82294fc0d5f66bb6efa10caf387d894be538b42d9c06581943a9983d8803f50e71020a25c80d478b97de13f634e418f389c319d75674f5e5d3eb0021e8884ba950594a2eb9b51d690ce867a05b3c6c6055fe39ccf3481a2f311122f946c598fae0f71c534e07aa148408c4bd9df3b4444de7dfe8910d370c137401ee4a7a2920d1f67229f1ba8c872108a0cf520d314558945487a632164812043008c8d4bcc2e42872965c5f1d1e986d45278bee430e8986bfd61594c84e99fb6836bb58fb037ebc18ddea90c4fcea5aea5a7bfc3aceb02aa95ebb9a15649b9e87c418ad64634b59b965d85a32b0430babfe5dc603a0601c97af56772549707a909ff58549e742f75590f55c35d622d5fb1bfb1aa303a8aa48a362e3eda8e16b9ede20beb01783a63733406766d0a2cccf8e2aff8562d2002b3ed5b6c469cddb55fd000a48a79662519e96c08e11b0be44d79c65bc86c143b57eb8a7c5075b4a416778c4100355c959c81255a38c0e74190b6a42bf238058cd9a6c317c6ae0be172fc02c08073ce4997acfc8b023a6ab95633ee45168f768257a39585066911ea59a017e323c2a52979dec2b960eeb4bbfcfb97eb531244693799fd63d2216739810099de83f952eaf56844229e58bc7901ed6dc0fc6fc645aecdbce2cd69ad62a4dd62539cc5e981d18d9f54f04c370690e340050cb3782a801aa46d6ded4603215c616c6375cf86106d0f03964428393ddbb949ac08a1f3bee0938490bff07bd2fb4876d7ec0a84c9a22d3fd0f3f196d2d4b1f9324fc4bf0b45285f6f3525e583893768021c15bbd4f170e6e92eed52ee1a1c0690d92dfa6aaaf79ce29ae2ac35c4cd22bd406679f92f52da22766055d416d9ab93050dddbfac1dbf8cc494f39510bb8664bbfab02e17b467c27973fc12bd6789c6d40e5785bcfe081eafd573fe10f425c1af984ac12c23c83f3b6aad93e52fb58086eac902572d8d63408fce47bc06412b0acdec165dab2222b9488c5276282a6599dcb0fd402e2a8a3314ada0d4573ec0ca98d4c4a70023b8bf9b209537e2d4afc6d7f48a19efcf7cb69cc7656cb60bcb66a4c876500112d5bea0f2b5fd17972d9119317c28ae82222be376899604071efd5b55cdfb19cf57f6c76d3b75c95bdfdc465573d946300b1f69e0a2a714dd713a0966fe3ca38a66b4768176e126445165c4d9285cc6a742b52ad10f5b6a65b6a618aaaec7cabd4596424c001e8570f0208e40800bfb5fe111493551bbbeb922466f68864ac556b696ce82840ca8121a1f2e557bb1b87d51a99ab79bb9364c9427da2dc8462d42a9c622cfa6b6af18ba218cd041a05fa980ab10ced2a316f7082a0d3703e80e968ad6a95e88c9de0a260139b7117c4110aa15848ac379f818f37735307e3d8b78205cc09e0061c8d4390c1e0fd0292ecd120ea907be83502e9697f0fc06a409c1d584239a04b35cdb764e54ff0750569161405ab009865f9d5c5a8f8425593de84c0249259703aacf5d5b861c7a32511bc915447eab1c1538ece539bdbc749969543ecbb6f169dd7a9f4bc9d0a6423036a135a6b5a3054b24a0795b6ac745ada4e0696bee6fd9a7763755bc5fd0027d25dc080f49f08a1c7a29e94bfa4367570e91015fb727b00084bde0d88f1346dd3a677141bec5b9df89b6978f97d9fe6411311bf0fd9c8de7b6f29a59432a5148c06b40696063b58ce2e2e75c30a3b54c43da155920efea188e7f4c321cfb1b7994ad26d7f499fa55851c1854fdfbbb887025349ba10c8b5b8e74fee3511f663ee67bb1fe7079be2c166190b37e39cf954e6caeff1b969edecf8ec18c3d2a712b22073fd21c322ea580cfabd1e02837bede45ee7d1dda3cbdc983577ede45e3fed2c493825d1a4c4122592300c9028595097a910ea72ad125694a83274bf061a1262d516f6834e01208175e1057ba11965f04dff57952c2b21e1743fc8c549cbede78b4490e77411199230ba1fe412855a2989a258240631d36eaad1ae87b70308963e7d661bb689373db0b0fb58be8f4327cff1865a3c47a65ac0180c90285ad8ec533ed8187660fd6307a107dca9fbb0afee1403d18da80bb9b04dbf8ac94224a8a3b94b970064699d23c1953bf60c61278463c19d9e84b8c7ef5ce50903248a52cbcafd1aa81ba8853c87955a551a09e49241ee8b81e842a7eb34720cea6be132dbb804fc47861bb5b0e04e97dd835c66e0557f0da92339dcee679882706b2020d8c5bd964195451c92b9c21c63f4628c31c618bd8b49ab14a318e3ac325e935609af4a2794464edc21b3c3d7d1b974b09ace07d6ae4535e03cfd4a875c542fb6a581161abce0caff915ccce08c005cf9582a255ff603d1051031c6f84204fc686a8c30ba524a2392ae9c010bae9c410eae4ca55c061f68f0e10ad214579a98c82b6e2044ddb99f86e4c314482929a39452084d37b06125850cb8c0e0c6972929a39452ca2dccb8b248cc21832231b86205b6634746d9c1084836216ed02b4a0c88a2c860f6105a8a82c46614e5891a80a274a1f5a821297504516a86e6b28230686c59c1183d74a95901d97e1f3f5470cff9e9bee028fc8a751cd02b7fdfc27b5580eb41d828a18fd80de95e70f7cb85e05a09416c20003610f22a56ac5c38bbcf87bc13c282530eeb5c9d1749c27361c7befa1c4910d673b8f0a58f287f3a5cfd784fe924f78a5a4b8a8a5a469eb3815ad4b9d0976d6091917b4b260a9c10a38b0bbd95d453dcf382b05fbd492ea060effce84255983aa9a7b850edafdeafdea6ec0766a7ed943a6ddf81f088a594d249299d74d25aa78cf14865f8353edbe7f4d93e6b9d4376c250f93054ca48ab651bf9f203979c72c6c8e48a570d0d3d5dc745c4ea7f1886613d17fb88e1440cd6b8e717c33862d8cb8eb14e4e1abbda718c5132e15164ccecad9921dbdc49b870157f18a14452c58925cb9879a68cccd0995bf64bb5b28bde3133d3e8439285efef459c918a8a85954208a323391457ca20842e650fb9c336fe3b52a9393bc8524a873588dd33b38ecb2fa5f4991f587feeaf9ed3eeb1920f4d29699c5838a564cc39cc36de53be67e54359e305dc73f715d8867fe60731887bfc38386c239f5049b1f03d03d9ac5fe3d39f4fdfc79b63b6a9b5a652f3651763a4b0729f7ce6f8317e8d2c703742140c014b5f1f2a02361cf20bc0d68f2a300b970e723804893ce7c3814860123f621d9c77212f62af8175b07725435007eb7c077c755b2e7c2884a20b6110131f419a3c4b2a0cfbc122b048ec985017621ebf1a3c8a4ccc3963a4efe329789ade0e538f8f87b3c30790159498b030794e9393152c2b50b91f77d1c55c41b15f2f2d55994f70cb0c06e038699c334e1a29e723d906ca3a645e2098518aa0d67a021ae68173ce197f849608cc03037c03ff026cb36242290df3c01883c070e434c173ea5fd9d61ba7f5d599c09ecf94daf5b0d72ddaedf4cc6e077b1dbdde81632d865197376889fa60f0712c8e7098273ad521b1d65865acdc446710ce912b304ff6fc04fb20221e6b7b22cfa2a1cf603da9414229d6f6443c16ea9995722e8ce961e61cbaa36f6068c08b71ded36cd3c3e4e569700078f9180e7b1427e4b150bf714144522cd40b79ac97dfe9a93e7047f358a16b58112c80495e618fe234adfb3a806197086e7d883dbcd7fb160178b39d6ec7bddaf5086101eabce02b340f0ef00d2c12d4a503ecc5850f37c036f0677a60bf1df98920eef4ec6c0fbf67c3619e88397624e39431ca38e50f56629973ce89434b7ef38dd401a994b1367bf2258434d2eb7dfc6af664c3115c8f73ca0ec3a8192ce39431ca3865431c11986c0b899085f55430a185988779071c1c085a69414fa432521aa98c383b3d3e0f75b0f7987d76787f027a4ca830013c27540534a046fa7427013dae3fdcf187298e95e906ae97ba7df5780edf585bd026784ef7105111242fb8a77d61bf06c2010aaa31a108568883e379a91ddae1a08840568c5a34f55052d212935295a13ec2396d442369243d84544292fdbad59ad2439106086b7bfd2d08b10159ccf0f234647bc045c22046cb4530b2eda6938cd0ca0f547a208478830e438cb10409a9046260f1148327a41a0c8a1f40b1a4b140f536fe5197a4485a90ba349207e5f8af9d46f8afb3a8feeb27a52fc0b8fc8d8579707c512f795133799108fc5f27b5129425a03071f9bbc53c33cf42355cf61c2ebf0b318f8c10274f5098839a07fe4ec23c359e997a28863b88b39871f987308ff69ec5325b69ca02085f91f41da0a700971f08f36c9ffdb763e74a1c4b636552fc1af3441c9cc4c14530a4c0c1c19bd4a505861439b8113815d7485a94bef0a0a42e170c1c3938d5ab3ec78f30824aa57a1a81eb8b83eb9b230b16b6e9e7e40c1739783b8a081c8d8d2e5a82d24b1c31b55133e13f230267c3c6ab9ee6336dfba1b161a3fbce707d5549ad994c86ebdb4250a786dbed9ec315629be66484b7895ce7fa6fc07042fc3ba8ff88db50b8b412ff1a9c5f1898b7f1376efcdc5430ba5cdf1a5c5f1bdc924e5283e32bc3c9a871f03699c1831814178485329ac8a9879accc8e217158342757d1b68082723072f6361050b10bee2f66bc9e23657f17bfd4c766d3f2f136505c8d538191de7000bd8217bb8d55380dbcf4bd70f70fb3f97dff7dac1c92227bd26f532a9add171fbe5f6c7cad19f9194529a9a288d6dfaef0b0e3a47af8ee8186d751b61750a67563dffac53147e170e5cdd14cd4553b31cf16736a195b1598e2abda662c7b6da1659c69325849d4786eeeeee32d66861bbe7b0fb0dcc31c61861d33899e3cc113260668e51c2cd889923a4cb186334e2aa4ca29551327394daa4f5c2a494936accccb55da6b294325e1aac106e48f51a820f9a7437e96e624493ee26dd4d8630a24977131f0c71ed3069dc12a511ad57b7d185751b61599c41ac4699d6bda46d51051fdb16512fb0d229a310b2d21967003123d44bb7d18bdd920961b52d052832ae91d70b42f889561636e0442bd5c00a3c898888882e1084e9c224901f4b581655f07155c6c9ee10b8f28b793492bef1e770e0ca1fe3987817d7454b3a0722273b202122f269313f375127c9aeab28652b4c85f90a8fae6bca0d47a69441d4dddd8493bea1bb858a0cd5000c2278741ba98f6835a142c59126508e1c49eaa2e5494acee40414f7f3a61b589f63f12cfee45cbadc7e08d450e809a66e4124b73f98e4360e34ba7d3fe8e4f6d378994be3d90697c6d3a0f1d689332e8d8fd97e68a4b80574631e66fb898119e3c2bcdd7e60acfd97edc7be2435715f1eb5fdbca0be6edb0fca6e54bcb89bd6d9f319377b6cfbc9b0c7be59c7c615216808e3ba6adc785d3508ba570daa6822899c1c528a6e553571e4e5a8e9ce97dbcf642ca6fc70a5ec02e846c874746934ea0a2692a0d19d5e9c71e7cb9d73cec9040a78dcb8e2a20301cc20258a3b7f3eecf93be69c734e1c293c1499472aced61ac28621692890b98a322557ed118613e65cc550ea214695cb2e5104a683d6124a684b4081ea914568092d7600b44418da0f0e8096d8a2ede048ca0c343a70fa1817e090c06bd8a0e486272f58c11035bc509204174fdc388112a61e9438f9a0840c121495008c228a904d9e03d3c43c3b57bec7033b73ea129626f7a41396eff76cb05f7b50c7b693e77057e19aa9889defb55090213ea00e74ba5246cfebe6c7ec27eb98083b2f7fdf6ed2b60e06116c700192222096d6f5635cca3d297fc716cb2c663df196945fc47e7f651f9162c41745402cf8251061e7f5bd954004fd19ae7735fa37dfdc9816755a021a2068b0d060a1c142838506081a2002e0cd961180cd006c6ebea0f9e23abaf1a51f350fd9c5221f827960175be07ef96354576c0a8a85ffcdd65c629bf8f67a178a57378d6edcb858ff967b514a396badb5ca39c5bdf87326a576b0df8c3f5b916f5471fcb874e36cd5eb88ee9045666666e618619451461923c72e424f0ceb4576ba1deef58685e5ff70ba17260ba564fc2a2dac8abb533ed81ab2a4fa8b2e5a056672b0fc195786fdbc057520f310d9f8b18b215e31c8bfeac74e0524ce8fb0db8248fab053018157e7e39e4f951cb322c72ce84bdee40e8d5c0daf2798077643dceedc919a07eca2517711066ce3efb0998705ba9cc5d2f599b4b4742dbd6c37a5c47e33e9aaf53a4aba5a6c23019f593c275a714f0bd6b996e0ca5bcde34aea9b55fc0fe7098c8b496ce37f3d712d419de88433c13a9108be929a473cea1b7fc7b9c1f5d5fda2d1b5944ad1b81d8d86b0f0bfeb287561b15f2aca993497a692878455f97c479a40404242435fea1a7da9ebd168769308aedc063ba39193e5723dd526b4c0a2db3bb0830ebd9919d248238d94be8fd3aec7bdf669829922f74321ae0c1fd75ff5abf603ead44e05a47e565f06d6f632b0faa1002c5cf58c0e7605f702fd343ef40e401d7dc30f2134424a18e39c11c2cd0bcd3d5869dce3bfee0b72be0a3e7757556b08e7c78fdef8f369a4f4c29f55c66cfbc9324ce30dc2aec73d7e290429b6d5dae3393568c8d40827e5ab5e9f37e2e3238626664ba5ec0bcc0d1b37522ad4163f95b20ee535eb7602cdb2b4eeab5947bb18182b63376bb56c4b518a638606b5d108af5951db0948a8c1f6ad9c099c11ebdf7cfaf467c21b6e3ef93306e8b703f25511d2fcc0c278038cde99bfbc4046196bad15d3a217e49251ab1a6734a95476cd28378a097976cdcebeb3cfae7b6919cd361be79c51522cd384483062fb334cb2775df3d9bbbeaf8975cc36578cb20552c66e9fd88226db10d07b52a99a1d1fd9c33ebb2676711c7db847934a5d3deff9681ef02f88b363a7c7c7840ba32bfc9532d6464c7bd5151b3fd36874340ff82c6ba6fde6e3478e1a3a128ad8fe2c93ec61cfbda110bb0b7b9fb9dd78d426943d9f1fc9503e5058df52883e4ce01ecf99b04973549394c5e92108a9500205dc34ab7082561cdd8f9f8c54b1e5522c325528a1e2099dc89ec3b56a74fce0c2b7704905172e597121154a94a6488223c5b034512cd93627845ea210e2456b620815458b46e604574ca3b0027514451799511459aa5147a1a536d72696e08a6b145d68f87082498a14d4f041858b144b62928ea48802e3e3092729a8c0f8a80193144c2f3ea02892020ceb2389c986d0961dc8a8e182244462ccb0f5b252121c9892f4001f72d6d27cf71119d77ca9a25b9f256fdd83af658fdbcfcb67bc3446521640542ca8403965d3681659ed39c676288d698b72298379b047daa50251336e7d95106c8dd0a136a5c6cf66d5e05440e6d7f81a1d0cbc84136b762af8365e22c1557d1958945301a9a1a26fa393477055bf4607431bb9c117519e644d78c07a023518d8091964518505c308628cb09ec0fc082e2725603db96c9060bddcc73e7a6aa8d6cf7a60b1e85028a39aea8aa5dafd5e75c56e936356dc7e7ea868c894115b81601d6a74a3c53c3a6efd5ae439dad7af4b6aad48b756216e954cd408ead09677469348728175166553e8d6ff3ca64cd6a55bffa353dceac4ad343a590457f52b16f6934545d207206cf759216ecd3a236e5db24a161a41212d1546fa3409ae680baeeac325b8c43c978663c8d2f80fb6986462bacf56d595ec8bef318fe950791785ac6b019e0151546c7fec926efdcf9b726bcbbdfa00a0c266db09a823b40e89cb6e16b577e2d6ff5697e9d2ad74095ed5ffb86b83def9b88dc2d20e86271d0c235c61440c202016edfa3fda623274f8820678c6192760b737762fd62eeed5974cee55d9d4d5cf7080531ff5319c0b7d85fc803a7009aeea5754f7d50e8663b804759a57f22fa65b65ad7c9f79b61a2c3aa4155f552e78482df8a6fec74179bf8ff77bbe00a0c2f6a52dda621bc90457f553296b5f70afc27443dcabff827bd508fbf1537d2155c6edc776cc36b2c5abfa290d95c4b2521424b8f52593b422b1340ffffa2f9cb4e25e7d54dd3ac9c436f55566d80f2edd5a9fb630dabaf5e1525d4a8171702fbf2dc054d1e639baf4d6f3e7c7973102800adbcf27d48e5e1cd60d81be3b80302f6f2a34f7d5c738f9337eb1639c10ce54cad6cadd43efc185d886bf6f0d2efc8264fbbda111f3673172e652ceae26ca08f99faa84f40832c8980922830c32c820030a2775a31c7ca15c8481b938ca197b2fbceaee980b058a42fd0ec8872f32ed51d2fa0054265325a068d7dab2e569cb962d5bb6f460f49caa34dfedeeab7f4d7aa950982f7fc2cf3cca0807c0016012d8bbe195ff5f1d38021de17f7e9425a0ba6b698c31709431c618638c31461218f81057f88c5db3d2ccbe68a80d468646cccfe0a8f1ed71564c43390fcc63e34dc9cc28902017f01cfb30cf0f83e76ccfa88f79a6f12cf3fcddea299e83e3f93bc973669ebf95fc46aa97601e8da56ffc6dfcd74ce8f4111f0d21b182cbdf46ae33c403f368277de3ef24c8756a918be4f287807974912eb282eb38f153200b25771d2515fac6df4be9f0ba9d30ef0c8e242c4937ba4eb2d17d4e6bf411d46927a73e3ac1efcc0c4f34b8ce3204755a8b93e6d1610415790fc918aeb3b496224fef1f61eccb0f16505bf77517a8558aa3c2ebc0b37ee2b47887e141b78b0ed8c6bb9dd8c65f6502db4eee79969685efe44397499001475b271cf7d86507730c14a4f4fa747499a1cec951964f5636b5c9495646b5c9455656b5c9415676699373568669936b56a53005ff00cc857f45da1549c918ea4cca51d66449566441562635c939ab59946b5ec59fd151a0238032406f2aa96e2bf409073e66682ae28a185e3c3591014896958498b2044add5abbb4e6344d732242511048d73d88a4eb6ec3511057e8d0843614e3d7f680694391f65973d9fb1681ece1c7ed04d87dfddafb40be71fbc93e721dd45d0b7531425d4c509cacc4fa5b21cb4a3ca8d2466c5111f3f434f3c0d760add76380504aac908c7d1102756280600cd09310286c0f797f736fddf7c2bcd85721ccd3832fecdbef25a1f154eafa6b37d8cf63803c4776355fc41428432c290289d4e4f6d35d4cd00f5d00a1a3bdac6ba1bf87dfee5044f38a153b1a07e77afca6d61c1397a5a7242748f4fc909b1f5b60c0196774b9197c8d6b01deac6b6104ac7be1078b1172cf638054f4fbd22e26c83d57edd03d3b48ecb7a34aa23b137c62806eb3146e24e4a435a4c434a5294ad1f58fb063803cc73f26c873e416d77f082d295cc073543e5428dac20c20459fc4f6fb78cc908a862c4a1983802c175e26a5f3c8f6ec25f5498f6681a06261031eae5003f5d3746b321fcd165a2e845fb9a072e1db1e2e54b2b2e5421d023cb17cfb3952b7c0f2737631c59d35b44d1137c62b33d05073e3915ab9a8324d58785989072bae52510e6abe3974bde592ac22992417d9c573b4ef9f409eb37dff0c9a25601efaad4566b1e23a0bb0e2f64b2a98474fdf10619d6a0487dbb387dbdd8f7cb8f590709b0065f9f0a0e3b737c4a20f13dc53c1577eb1ec5861af010b809dcf64a8633938b59e40e02225fd4a5bf3069be538250b2a080090223e89314218a1c1e005f1486a713f5e62923089bc6493d216407105f974afcb4a5a80711d91f552ad955bdb346d7bda59faf3b1afd8d72e5e98c7f004c60ec8f559e7b0a670ba0e7e0cf569fdf940eacf0ec8f5b4b370c5f163988fbdfc873a31d0cf5412988f3d90f958e701a19f750f57d9c5a9fa1faebc3b1726c0ac148401eb18ae64607d8f754ffa9935ffb33b7a609985b1266fdd35db54dee413a9ee2928c582c6cab67bda226ae145151096000921e44043910c864881640a424241659452ba515a939e98544a332daaa8e20b4a25d502884ae6b1db996de0acf9940e6fd6ce10a2bc6b615cb6e5a995b106ffbaaeebaab5c2bfb816209313a624383eacc484e43a4fe9f9895c92f5e7ac58ec790bc25d6131ec7a11c3da1edb097d02fc7a75f063e1c2a732d73baf7b15948f5b9388ae4d5b529710c218a54be85d8c526a1c690c62437798926908a1162b841036cde060fbe73f84104aae910fa53b7c7b196ab142082f29aa1f0f57fcd7eddb1edd3dba7b8cd148538411465abd6c679380b3b20e87088e093e538589651b8cb4fe080244cad28f2040a42c19fda09ea66d8e9259b21baabb2be438887bd89642066e8c3f8048f1c69e1839317262e4a4e69d3cd4e1791896b991132389035e829059cae78a71cddab8e615cd3e83dcd76b593fc6f9ecc0c1813d3b3b15d6895dd7a7f040f7ea68b69a873a3c0fc3328f38646f658c38c8a56b8aaa1359590405ed64a1c3fdb8c5c449f17760b154b12a150b338620458ad7a6e8be02881bab131ac07a7dad971541b0f40437a60f2a79442a56c10163657b71adb2835bebcbb8cba4ba4990aab5562e86bcd62b74b8b5d64a6997ad2035431fe1c186ab030f3e2ce121063fe0810918786d6a23d9f7fff00175504322e7f077bbcee314e99b2ccb28a793f3eddc0d09026854b023ce944cd21020aa51ee00502867004ca35c017ac80720fa48e7e0031099363926f6eb27cbe484048fe06a89e41ac990237cb3a482365235cadd08808800ab259ec557fe9f7729677325295a42391d17e3825e3e287b22cf7a4287b2ca8299c6c1a72cca49ce6e3335b69fa9820c5dca2794314a9871cc7ae19cd52c9a2a4056071089e0fd1e6a22a873759211d4615a5a4afab8c5fcf5d011fbfe52c55a16c06a860b8f8b62bf1aa3512e0030664cae0e003608cad5d020e4e5b32c6bbf2e395f38220f25c7e4a5bbae6943a31c099fd46d5943e17a89b7135fc19a238da88b8caef7587ce52b5f591c1ae546c84139115462d0cc689453516e0601341ae570684a502e95230ccaddd07ae834cad9782e4f246894ab7127c7c47ee69b2dc37e9572328f71414fe61379ce059ab5cc3886f5e282883c8bb9005fcde5873a06306947c38a23c01fc52df129bef28f61824b70e5444986409d6ed198c07eb5048d72303fb05fb577724cb26747e2498ab6c83119e2393de5e5625c852bcaa1eee4985c8f713d045744ee258957d7453dd4443d012ed8e1793bbe2795b2b6b9b0cef9e81b48e3395e73bd4b33b97fe1c2c21ca711b2ee4e4696c1b93939843da7f49feeb15bc121849d4ad5faee7fa1b3bfdfcf85ebdd06d8861f6688ee6ed8e1e8801042088bd8d9d1e3a36fbc081a23369e02a8c44d1825b5646a48020004058315000018100e8604227138200d345d15fa14000d6c94406c6034960863418ca3288a6218630c00c61862883104208354d15100cf6d212c741c9762cd2af6710da9a047220ba34a2a266b870434a88864d82015e63d5ec866a7a81e0732b49520a06dd85b492f7dfc4fb81cada71f7910579e36ca0d019e26ae24cb596810517a1fcc1d32a2e72627a8f4274fe0a8604aee7b3e6da7a2104b78aa2079b4c9f15580e60f842085cb633d2d709ca0f017a8510b4ca65224f225d4d13f66b52a2b8df981fe78fc79ae399a8b2994e90345ed488f23c32f2980aaae9e73c554b74a4dbda8972df2d5a616c3c6cc0e9215081ee71a3d31728b3e365da296c39ffb25175a3d8cd54021a3f860cba248b30c95a9c569bd0004211643330639fb978de2847e41518d76c48624de52d7af9c64b9a21233f2a3ab15c6b87a8990086e4c525a7a3697640153766f1088416200401bb7bb1ed40c4f68e6e7360898a62571777013318dfd77fcb2db2856f2ee0db54c4fcdd5429acedc02096603c4ed45cfec9781077e8c5d1765495622c8b486f39e86c1f0a70fdc7d02d7d98314f1949c345f8eade16092b2d2e021d54ee44e9064e3df7120db8652f3a4d383a98fba6a3992d1729cd0beb8b2536774178f7eb8592a3564339105c1d400eae2e0652936ae717bd3f20ff6a3d7349c0955b40678e2c60e51343f5b7cfbc9a15d9405c805c91d5e1bdd94e546b5771b1e4715d3e3efacc6f5c8bcd070ae2e8a1666464a6db08d93a8d76b2abe5af07a744fe630058801d916d9237e3e4caa0bb0ec1c6cc671c27f7e67ece190a8f473c8213b507c52fdf84d27e95e961a0c47b3f3790b5a07519b1d25893cf322dc364ed60fe170fc70b0ecf4237a0ea2a7d4207d0dcec034f3ec8c6bc8eaf1444a6e4ea2daef13d4a4696048b12fde22a0c2ff1d6582eb294c888320841b59e21cc5c1c8af6e20ee2cea07ef1006e2171c2b00df10723a50c093da32de32c79f7cacfdc38b88ae90349540268994efce5e9acf7e0bfabfbbba652663dce0abed22b6226b168e284a68dc24a14962d41f9367e8563ee5154a9101abba1adfa32c19f4ab11d5f524fed4c11e9c6001153ed0c507d8c781f9616eb0e37d5eb9185589c17e64b3d3507289f8915060a2def8c51848f44ee421afbb49fc26c0ebeac682885a49da702fbd9597ed2b285d66c7e9e5cfeed3a97ff89921732aeca9a76ade3f85995b99cd30a456110154f9b137bf97053a0e40492140301a0138d2f396b2688d262591609aea69d489f794eb4fba46cb6df23eee4b2a0bd19a391cc1649de293329ea62ec6be0b5e79673727b7c6dccf3b70986fa36ecd11546d5dd6ee3555f6a80862c94155bda3a11e93e26c90eb1b21437588e7e1d9586948806ca9ac7965bf84576877bcb3758e28446813b0c442ee2d40be8012b978c2e8aeb05bb3438c01188d703831e6d160fcf9c954d48f0b5c4859b2a7643117003115cb3633dca25d556f7284d13247618d64994b321ca202b875426704d1c59ac573e6ef7b7f7860187cac070f457caa2f7debf79ed89fe5ab2ed1d2b4fa1c7d00a0429dfad5d459f607b3dc30964d91504cac406b86940bd2ae7215041dbade70e73225552836b298290a6074010ab4b68de6d7117c0923a0006405ed235b862064f4f5c91052ddcdac073e64768c2af66e41d882a04d98a15ade024fa77fa738c07ceb3eb63a958fb8822266756d5fdec53d59d3878b48895486926c9cf2e0f6e98f01f507ce994025221d61fc58a30bc45933e8268f3803658536fa649cc02975ead46412ba41824566f46b8bc9fc4fefb9d85b92812799ed68a37dd5f7a84b78fb4ba0f619084023bb93e17ab46ddc82b07373de6cdf69eb0661b2050e84d390f162c285b3cbfd87f19000748661bcfa12fb9c927f1118ec1e6b7be016e952b827cb563d6f9834a7e480b5e25928db1844b4f06fc60962ab56b1a9024c2943923f61d8d4a4da0d6b194c523d6f6d0e65c471dbba7578235d9bbc91861c76e5194fbd1c777e72909b633b76e133284e60c743df1dcac530f4b01dd9510cfa916b2b431bf32abe17eb420d09e6e40d3fda3faa7384e14299221bf07405bc385c9ee13b17ba673c4d07875fe0cc9246964edc0ff759240622ea97002d9e8230d42d280802161a9b64a4d3db0f925c7a4315a64cda588218484cbf9020933badc965545a4f38bf7d9b33c9db5dd0737530fdc722079b1a19ba51d815c965052c32a26ef606f327ba8a82d2a7b81b03deaf12a0f21f5937039a88f10a998af99eaae17d09bd483ffba9851eee0393ead4abddaf7ce3a4b42d6342fe4139633d07ec2dd6738881e29853904a4990b3b9d4cff659eba43f400fadabfa7085d8a7cb37edcad7bc4f15b34a2a1f4098a4fcc6d2d39838a5bc1b34c7e470d0a4b77629131770329859fef5ff461ccb0ea2523b851e3a69e5a824a629330595070dea89916f13505794619ab8d6f38f3e5655a8e0a09209ebb43f044209bc0ca30a983f9e185c23c852aa009ecf722b9fa7d6b544876504e2b36a17d369cafd91052df18dd6e7277ababe51c63941561bc7d826f33b8398422eeaebb93d95403181bf8a565bafc282c5f8c6e61c0674b5be4ce5711bbda28b6b6889b08e8d0db36abc5cbae371a6ffdee1abfc9ac61c35e9a73ccd54e61514baafb1a53e2586e2094d745672fe584c9ce040c6f081790b5b8a3284459e7a51b15ed971db254d56c71a262b69f0f3fcedbf93390546645ba76a4ea12757ff6ce43ceb313fb929d14807a83ff9e94b239dce37ff5398a17e5823ba0fb30f93b76c8aa103960750f9b8d7c0c22ec949f3542210b540e14af344dd25ae0e8e0385fda11fda5aa270c6a4dcbceb8fb99cec33a5ac765cee72462d7be468f3596ef22b2330c644242d410693d553c656325ec91b85c15e0bb875e457816e0300446b4cbcc55d00bbbd22ef6330350802ff090c130086c0e217f35706f2bae3ea816047d24ddb59e0f3c76237503afeb7fd599ad12668043de3d5b77692554fb1ab82374b913eeb1678691d7b2ab6b19209dfcbefec7c9e40644b8448f6219afe645377b10c6972f225e68c2f6e93e7b8aadbbe29f40eaef477fc7656caf9961cf0b2de61b24bbee42c9919c61433aa0a094fe7439a0546d68c45099d20842cd0d761a548bea3c545931b1060be5f7a5a13ae65f849036639a2c0478cb45ea4c04c0f80790c0a90be4a089110844a995bcd036abe1bea060ba339086da5e6e2067a62a8f8fccc81e6f6375cb0c69a838f532864ea2763dcd3a03651d7bf3ac84c55d60de16f0a6930aae7667285dfe4814b47e6c10a337deba6f99ff923f682d8265a324b841f7283631a376ed282df3c087bff6c4c7a84ff8bd5c7825fc85179b17f156af734ad67c97919417805cee242f205d0b270e2acd09929492e5359caeb1f5e8007cda7a82467e18fd719de6abf7661b92bf3239ea664d18d9e359b8be09b5b810f1ec53e49f645cfc1adc02450131fe257b792213902753a96a4661b34499cf032fbfa1d841e78b32c2f6ce9e5dabc189098e4499feeddd5d665a5f2c9c1a7a7500f6e546489a659e373e183bed1c9280c58195be8d5a677052b5f490fa9d4f18e510d7232be23995c71482e947e045a3ea3580659916c0aa7b2b1eb093903503fc3600f282684f7682c2cd80a91a971f51bc64bbdb3515ba870cc45794d24df967a80a1d9800b73b28b97cd62bcb94531f8cc2839e6e4d70082074c9ee9e044aa8f55523c95c8700a24542582f253e10638b1b95763d632307fcee006b377a0e0b2f3e4acdef43a8a97ff8907405de31b6f6e00a5d1cc14f6d37ed2048f22e76332395bf8554a1c84af09df5d4247b0f5af3a06fedca401134e86c6275f58c236aa28f49a9f7b329a6e7316746754421e54ac04e7631bef27a5bae0ba7c810b25e8b816c9bf0fd9dea3f0d6e20dc7719ccc35202a0a0a9fec8a541be38721b1e48543c2c5ea963ee48a377ed25ea5339d397814501e17339f3e7c09233426c7e8ab0c82d768e8c3d5b5cbd4d8b7a89ccdd39172df7aed44f2fbd9e7f99f549b25ffc17e905abf45301be893b7de0daa959536aad466b7abfb84a59e1589aef38593a9dbc2d5a6008fbc4f77566a439aba00960b7a45b2f29746a2d19a1e51042dedf488627cffe5eee11dd94b1cbca173f0ee70c72021ce4910fb754a1e220d58f696c406f18d7be1a28d9bbc4b8e81ba0a00930d2aef7d9043bb541fc3684a8edc3f5e909eb8045a70910cc9acf03139c6d46e7c54ddaf6700dbd06d5d1848d770e6c05e07ddcb7626bd89e46b2db180a37f8c3cf8b64120cd5088220b8ea147c73e1892f05ff622c86f05ba9a93fdf89902bda9b5c6ed485e3710fbc89293656c6ea2aadf22bf0b5dbdfdc8f05b65e2a1cf0c45b8b3a515c412b983a66a84b3ec7d6736fa676b22f66968be2273b9004b3134ae0ed2ac2a07229658dffff9115f6d27da1d8da6ab3197fd00a56c4ff4e0a98d492cebb7a68c90464ab51b73a9d168d999cb2106ae14abc9403348f699cb2554970a1eebbf5de525ca79fdcb0502504fd4e4cfb66916e95b73c140b8102a48abcb7b806fd8449bdbd911f9b4bd03bbca4797ae69b12a6dca17e8b50a386e4133fffe50979ea635eed5064d0e7bdd4c4899c25720d71be8536cdb128c9cf77b709c50510f6b9812c34254be0139bddf73af6bd7368722f845306b5937571925393f5b374fc39d23b8a7d2db1db2c29874cc1967c380741a0644b4d282f0ca19cc4ae0c9018367b2090b4f71f454f6dc1453dacceed45375d77f468c4acd72fc96831899356897838251464be7e5a1928f829e562c810e58321eda8d572cbf9b0c4f7478a370507293c6b828be83d9a74099d60d6ef5ff0cc6abc9c9e8d875c23da133fb06170f760df160be6f7fbe9de2ee4fb017b69b21ea1c2521ae88180ab6c036e21d4c86f04b9cf5db88062e674faa204132cbc60298650083a8308b5516975d0af48423ad9bc89e84528c6ccf6b131ea38d4a09f0b6177346e774258de6197be8c579c075e4e9b0c90cc68f4a263615b597e2a97d835dee306d725967886a0f760a8defa8580d9b509372b4b684bd66d7312e06748276e1c18ad3f19f1056323c743c2b3cd88edb92e541b4273cd805fe6cb03e6657d17bccc3223bcee7d929b8987873ac61051eac2ef5087bba5597612a0bb24b3bcd64e082bc2b785522434e5c90dccf227fdd883e05403c169024e0c9958dad6da792c88d30e7928dc61ca2e0a8e85ff8251f9c424b0026a984957510f35440d6f5580693df6a31501ea541e791496940dab62d7fecfc38f056d978a0b5f15cb4b36019622795996facb2d12b72e14324681a1c1718b5352d78187abbdd559d63a9ed7319e58004ff0d7927d1dc1e52a3012e2de768d9726a009eee2f1968fc611a4748935b5cd7848feaa1d2b6cdfe489a78a409f5e62db5856f1fc4dc14166179abefd2c3450927d5e18d380fd8a88d5d94041a5559a304736393b602ffac860d1d47cc96eff83e58c0800743d8b5f87274484e0849cef47b4092d46e8680e4a0123b80a5f7c19c15a673094c7f465c4ac0274a821b81094342b24ed3b44b4e2dd0d595c064b80c179a745edf1ece05e075599affb68af0fd883dfcb3c2d95252a06089eebf51f73b8ee99b485dea7e70b6dde3f47066131cf5d35da76c82b13ddd33938fc9ade782ccf0ee70cc4df54e3055dbf27049153bda9ac2daf912f2a2a208c779ee6783f35f3b3a051d39614ea7aa4f369572293677acfb12cec122df20d01821cd4e36f9d27bb8255811224afee4c4978f7e9c1e246fb26083c94985a3e000742b6d00e18319339d14b1e7ceb280c0ec0489edf2f9e58a6a1fbe644cfa208318ea6dacaac7cf6199416d1b5483a215b8d5e3b10de017e651ae779c30f095888d830c21c2a2320d8ac30444a8dcc8240b37fa05061ffd2935b43d5cee535b984d0466b9b629df22ac0fc9add865f74e6220d7f155be5e45cb1eda5fea6d523b14c5ce326049d38b1bfedeb104764f32bcbc320c67c030466a5f1ecd520133b1e7802ced36944d03b42ff18710f2ffcf50d0048314fe6473df804af84a510cbee83ed974a8668c9033a1bafb41c91db9060019be45b9c8c17bc42b04b49350369b6a1fa18f58ef2d9afdfbc4fe3d22795d90a53d70858012495ccffe27a809d90cb31882976251a1b5f831a65fcc89bd6f4aee938d0e7cf3fc6741438f7335bb2e343998e35f734eb0b0cbb2a9cf54965191e07083df7f8d2e6c42fec4429b6875b31ac0099b23978f249b979ed7327dc6cdf47258c55089ca8ee46e9d6167c5fc1aae281b4a06225fdeab50c1c216ec87033b3f6eb75abe540e32ab252ac1df4215c5a4752b58f1811818db17653da4f7c5d508b66bdd035b74706471303ab048b78437bcb91b8c2f8dceecb329c1f716ccea21539064c01f1a06d82b38b873b108fc031f1e2e76ecbc677ec617d270f1dfc8c6818745f7e77c7be229418151fb6aea6edc0025611968a44551cc114880c01f4c6fb9f342bf7879d03c8c8057ca661786b7288abf0ddc8eaacfb4810ff06cfc65ed847266ecb7942fb2fb2d56b638ea9087556b5df94426b1437eebaaea9948f616283429feb54f2137fd964e3f21a297a4b8327ac94c23d0420df3983ed523a42539baa50106146d86313876b6c92916b931bbbc6ed3e9f63d1a978304c776eb043049ba389bdd9f7551e3d8289e575d92a781b27ac3d2e52b557470629b11063fd1d172def6288d631933bc5eaaf9146470084ab1721fe14a75b6d0292412bb64204ded719f58e725449046a3cb2997bf4ce14ae4e20b8c52e50723c2aa5fe24e4b7e80be434136653d8e61333ab402893406292a64b1ac275641a4f97c920c3d071044e9dfefbf471bfec77d8e2b853817ddf5bb547755d450e881cf6a7c07aa60a26445fb072b4fee9b6f63328d1f59b5e0fe60c445b6133169b786d6e4b6fe545785d4b9108ed8b95a514c7338e41218efea00823d656c096b9003a40bfd746b588a31544063c0fa23df7d34b512f54c16c8cfbcd50f55060f404220439648cd2572e987c7980c200fc4f2a04a8766a605f635958d40cb331b15803acc0124d1c54a78d7f0e81489fb23454554a4f1dc81ed3e40097fc3138139392be645a7ff9dfe0a22fe30ab5f209a647464c53754f730200dfb304266416199428a346b7a248b26af5b27b0f82c23e1da3cdc93291b3f290010377038e078532fe6e8bf7067a14482bda3a519a726c5bb76771910fc5bc8bac4115525b42fc58001b393508977462ac7ed70fc0e912fe65d50a91bdf56de87e44be40f750903c25d0b756c10004b6a1f24d4eb32688e88c9546613e18cfd91c4b1c31efb47c93ac6eb6118f2d1f607c10dedbe8999a41b19341cfb2b7090d5bb02ebe4af23177483308a385cd9e12dbc63f12e56051c43a26936e01d6ec966470c06c839452485721c11287b94401ba3ad04d5ac128791143a7b145fc7ab9d28ff56b7816610364df2df509f1aa8bd3dcb0dcba5d1ef38983d88012ef8dcf5d560898e517a7c398966607874476ead4d0bcfd09b8b0ca4a121187aecc09f3138c520d57ebd66c1f595d99905624f4618b07aaee2256766946adade6989aedc0dceb4aea10ac93ccbfb89345db65f27f9495b18889040d653a0c1c0e0f4a65fc35539211c0f8fefd176fd03a829a07e455a713058b5c622d61229343265156a1ab1b8b088a310ef0a7be2a78a92bbe7f5f14d71698ecbc08b71f4ae1d3995ed95d17532b64074eb529e72094cb9039023219371c75035dc67eddcf0da246e41eeba8b21c26d6e560bb07e63073bc61f10544152d9ce28d4a20a9ed33ac952d68ffbdb610935ad065083cc5ea47dfab7c0d5114b1e4202d641e0d2fcd45bbe82abc02d7874b38172293e676ea2397eaebd64b310ca632fcfb8e2bf90cf19d2dd9e754396ccfb8cc690361b92fe37172ea23aaacb9737cdba4fb2b07880d0da410d342025a768df6053a2e5577c46c7a9177980e8028a682e30705c61572a91a1cebe09a1c8cc58a1118a0b9a88ed256a50b37e1d5f473424268d50ed1a587cb41240ffcd4fdaa7359b194b95271190863b96c10f7ffd7988d89c56ccf8ceec91f1afb51af4b4726d712b2605a545b109dd63dcd694e0e8355c3baf0c23c695a50cdca08e952bc22ed83db8cb57e541711ca3b5d9e8209ea08bb64b4c7ab378c53c5f1e4f571a9b0aa934f6fe49696c5b6b5d2130757bd5b611d960ada6e9132e8de03234dcbcb1a4039255cbe0a5f789b190bbe06d5c4a3246b6b23ebf40a2dfbbf396c12448dc882b31996abbe89fa2432ae604c0a4b6c2f4b4b22a9f5450acaa601a75d81227450b21a1573b6c01ca3bf0753f7c5708aafad5800a043245bfbcd565ec351d8cb028469a0235ef88eaf71a32c522ca9d532de7befa5422b3259be8bf657918042aa733e88b5298ab2001bd42926a26df3ba6e5f569d06a4bcca592340e6010d6f148383c0282c81c31f4ff281fdca00411863ea034226134ef5004bb978a5e4291305013b26e172cbfff24755397d88cff05021b5f82180da7214dbb4c523e444f22bf4558dc8cf3103fc4581d1688b94c877e9b10f0dc3e74211a1807a460f81fb8a477ea049c7d5959819c5ac5df48c730972929417104303ee5050b82adde3897a70bb4bb8119a00084288476227c746d73295047d561d8fc2d85a7791052b6e298670ea848bdb883e0e9e873b8b5fdaad4e030b35e0b47abb8f62634443dc6f67cad0fa33eea3bee20327a4a7b6b9470259b9808e4e0652e055951e06fd9ab662de9eb87555497d471638095c2721de0e603c6d5a8bc1aa8ac7ef778c6bb8a5628490869a0793a2589684a9e622e3d7d318ac77e5eb1780c57f264963cb99ea46b010b042ca16fa71c1b1b07f6566d1b78225b65472a49fa3a9a86bc44f42a78d897d377c2770c343e0b2b31bda94ab5961fbaa09e4544fdc7427f5a8d05efab2268071d6fb325d4f279cbc3bfe249be83b1a2a5852cc2a166c788d71bc57c94fa873634229a12f70a7ff52811cfe4090115d76bbc2068076074a459c675c1cccd4fb81f2f2005b9bd602bffb3c625493361b6c5a776bc0aed5bdc4828c45bf25d1c6f90261bf97b3466d2708258c7c1cff0d540ab4fdb64b67729132c2f0f12627519263ef2e02a02e70997364e3981c3ef461c9dec7d32921a04f4f32c789ce90ca62af7057cd3e37a3395e6e51ab0de94a0e567ce0bcd087a548d3943c5402d8c82e2841ab34be661066905a55129711cf3587ce0700d0d9448d3133ed5f9fb4039af6c7a20fedee9052ad081f676ccf2ce8b935101d32ac29293a46056d7c208427151a50791a4962590d90fd72482f83d99af01284d6bf708747307dd014727b8752fdd0036f57076b859774740723e496273f55d5a4716a2ff210e59d04c8c33aa68dd815abc3baa3255823b75f903e15c5da68110a76ecef9d027c7eae026efbdc5409ad3ea9ade720486ebddb639899b9d68eb76778372e5181660e908a22b1c9683ce27611fd8397165f2f1e3f8db1b9d6a203bd1b4cfe93cdc135a5ebeee48089f4ed0d2edc30d097b9c770b905954de742fcd7dfc7fa042f4404d6fdda0c6c13ff7aa47acdcac7b13a5b45e9e829a2506ffef0c3526295dd295c1f8d7db17bce1a450ec3116215e4b5b13b19cce8fd83382a82ca66f2cbcf89067a307b9e1520d3bfcf43f4d8fd62db5e567d57a2ea4f41330844536dd22371da9e7082e484a6de144da05d107e98d1fa14de75f4b8b192c4c1b12cd3902dd1cec97c30fb425dba506d7b0eca643d7b1995dfd7c58b5f0b68a10756e8d3056834722b607680c2d4a871a449701eef5db60366535fbaed88aba51a920ebccafff24fb4ddb03ecfad695b245f2eb61ac1a7b0f572f8301ece160f016e92928a6cdba1f200c9bcedcd7217f3157a39d1b9f81f7bc7f4d2e5593644d7c448b7d32ee71603f0f8cfec05c23d8a8f05b135d618fe6d388df83a6fbdd0d8e12f1a398c9c8d6e31e10a93dd7ee6b9e4bdf48ea16b9f43473ee9fc12148585ebbd641eedcc7940b59d7a57273c7615f5233ecf9bdde30c7e1fedc50467237971977b52d6d7eb47ceb62e033c3e018b91521f63a6232f07499ba4e24ec9dfa7cb3567b61b662d92c45774954916b84d794a60b8e3645c7e24b929541e88a0b7f5a1853755fcdbaf48b1dde695784974b6ed5217fb82cc605ee343e9b2b72ba84037ed25a48ff2b5a052a2500e83646766e49ddaca05f34ca15b3ddfc1d626e67cab0c7446069e6470ae4bd88958077c2ae27567eb89019ea0a0f243327ab3761ab2026d6c42d29d2d49802649738f11a558213db3038b459ee2e749e1f6b1d7ca80adb5b059e81e63dec17be2ada9f6d7ccfd8d1d4b3be7dfaced0e79f53fbe066c689efa178dd51757d4e3e4dcd30e8fde325e3fb01fa1b89d032df1384d11d5d456857a45b4337fc760d014dabcc4ee5cf8e82b36211f0f730d56fca141710bccebb8a2d41eb4fdebb67f231132ce2ea19027c5669582d406dc2ec65d6c8c02d939ef5f5466384ecc6b29c558297a16aa4f47446c96baf91da2e9f5f4d73bc9582de5aebde77e3494c4b814bf1e248b597e1937b1cdd48997392c73e994d46ef5e66ef415e04d2b69f352215d6894a516589675a6b1f12a6a159c233a5ae33d8b9421954ce3754e0d0d0660336f959ab75adc1cbfd2ad98f93fbdf05eaf2274cdf8dd79cf9885f783873ba845805ab4db2325d2e2e4259dc79b5a3629a318e9de32935b703edaf6e92f58ac429743d8616034e1e79cc438d4aa496e5b3dc584b12ad427c0ce692fb0f2bc03bfb2d08eb3739427c7ef3c5070d31b7b86927242a31930ab13a09a5bbd65a5bd7fac6af6a2f6d41ced85b1ea80c8d1cc0f86cebcceacddd4631d38847c4c0d2960a3ad2c607d8e95b9f578b7ebab70669e91f4d82ae4904444cae0114e9d822e84cff02dd1f7d2ba422a5c64867d089a93b14615bf4ed57f8080e46d7037b88dabc0be6bba631932bd36b4c30dfc220dae37c151d7a79624b095ea4be7d14d9e7e4481def907c2ecbdde4a4bcedd2d4e0bc4c88d167d515173a27b887d1b6752016731dad641b5adfaa0456ccdea9a62a516449d00edd6cc3bbd62ef8368b889a6508a01bfd033eb5d95a3e101cbf929fca475ff2979d939fa30780132e2043b0c0582a717062f86231d116a324f59f98c61219cfbbbbdf8d28071b1884f0f14cc92f63af2a4d8a8220774c83b503b6d9e379fb1950f76e97114b371a606d97cf2fec038b570785db494584431d85af6384be8ead4c55dbf4beb3106b64d5ea7d30e12f826ac87841f4dceffff414352f78bfc51c4c47ee36b56196ec9c47b213655628afeb8baf597b0da1ff9ed0dc3c914509ac458e93797672dc901a3901808310674ce12441d430c243b8af25c4bfdbed13385904fa0c362b7f4ce6777699cd867d20fe8ebfee60ffda60344958be6d5b4360f0a637e2c13ca5eb513417e3b35a729b10edc3c368879e86b5db5a7c72ca8e7f76321fe34d5c79709daf13659e518e315a4164ded3b1aa1874c4791e735e16a1af9fb755fb865c0fe5fbbb96343b8f3a4ac09b37e51098def7efd594a6f18ec81a58bfece9a1547bf199207fdec6cfc1255e82630f36b0df0573d7b874cbbc2951e79945426ed9d9c97dfcade279b765c4742855742ce9c29ed24161deeb95d8375531af325d2c5bbec7024875c04bdb280f56e4a5382e757ca72791c249c0a6231eea3bcc20de2a5d017e2411c7a9482eb24c8aa0c7cd526e91b3f8e9406b83c3c7d008158b6187f6ae3e93302ae485ddc0c4ec69ec4518e564b871470c76115858cd5c6dbd834baa54bcef0e7435ccc924a5d000a2163ba4c50f917bad39cd63a222f6791d1541708a92d682c9e49896ec20b8c408e179b4abcf57dbaa4c43aecd217685015b4248de217c735f548906959c7a07ac894480a1f15398f7bd527eb38a7996ebb1ccf23b16417a02fd347daaa333655f35247788b23229d1d6cee16a5eb1176bd871144326404c42193615dcf673c2588b3dc88ddeccc9f2f60fbb4b05b6b641aba330a23cceb15a9a0f4028b10d000552533736b80d8f94adc98e29546fda618fccce6ff34f1e550475ea367396a4112465e93d9e37469041ce179fcc9a4c438a646281a046664efde3b5ad7fb41ea45d47a0f695f86ba8bbe6b865777c0962bfeaaade1977460b7e69b05943ccdee0a92cbb5f4163f1df6ed690105a8baf48248c0a99503ee6fd9f06f0a8e14a1028f632443f3e871a2a68737a24f745cb42b97813a63d5e896d6315fb2ed76259e55b6c82f606b421937e1684db84fd8dab4deeaef7c6092c4fbad240dbb978460e23f3d014a73c79b8c31558e144d1eaceec96f861649ee3b511ef1c15a0631870d3077bb68f3f4540814ea3e10608f36058c3045f009f07198d5e61bc2e73de833fae0bc87113aefee46b1c1fc74494b9ca1bb285b859c8b3f8b69f27f4158445f354e33aaa794f918ec0542d0b7275afd38eb4b299abd9da95aa4e19ba5265f72160f7a57bc1b1534dac21c206555ab79d14ee5c3bce98e12a0938352b4468cb52d1ad5879c0c360238ad68e3ac4e4cb3ec996e1d1ee0bca6d703f61e49b86797da89d7bbec410f78bfb451ec4de2c1548c358764e0e8ea1d593577ec0213c6b97f247968f2c57218cc609ebab4416238848dfb666820ab980ae2cd7c1cadd07f05f1981c227897db51440f6099e1e8d2be958e1383a8dfbe69a7a0230f7a34fb5294ddcc6083e34e361582d55733dd50938906ae9dc91eeebdb6523e13c6f17c12c689d12bb5b69892ec10ef0044332f86e614e40e1e31d87896add1e4487228a601339131da32a092b036f0ca99ec18ed611d0f73b83479a1a3963a330ee29f15aaf78b42f97a39c0a2b1e17f35689be66bb7b404c59e6636eb764c6965d19dc924a3de913dcfb1a3f1b9f00ed2fdde5553613ef39889882d86eb361f76d5062906b667a002f9ca03c2682024a4b9685b6bbed4a2b2a9f570897a53b94499f6e6d21dd61eca6445f549a096761d3dacee2d67ed851762cbbb81a6ef82904da65b5da50033a329d62652eb4679e0f8dc18ea7c5a50404574319ef9f87a79a67f25f881302cf6c520e4c26f6e45292e3121c24e79b5b40176d3daf63e0a4c15c5ff49a4cbb22739e0ae86a9616d3cf851bc90ed928941a433215ca09fcd092367428e9e3b5c4daec9915a5185fd1f88578936f86d294772a7f5551fd3b39e3b9017f3571f6d2619b03261a77710591a3d0c77607533e66ba5d26b709aae481fbc92a05e4af1c841273f04def50377ac8a925f7f405595cd23d3ad2893fa6a2141b9fae0636447f2754a1be329b32ce607a758a0367564643e9c617fe453331e36abfba59fede9978e4a90f6e29c633f8854001101bce20083cc3807f3caeca63714db9541ee99ef1ff0c336909209acd2ed0f232dc29f78f7764bb491b84c53d3455a1da37a97cbdf7166fbcc73b829302be1f9bf234481339cf833cfc8e994c39510a637e626f1b72b8f8b5cf313e95be5d10300901e1feb09206e51faa94227bea497913e15a8fdd018fc2582f07e2878c4d24e29b9ecd72097dc4cd574328aca1bd5201ae4ceb25b972c10e3d6ad9714d5690c20c03c2ce204de3ab2b949f2ffbbf7e5811d21b597d4278e87c2f671ed3b15ecef9f63bf7b2c32c2eb4d129189b964afac06d557886406d364f9224f6252a3562b60b960b437af4a3fbdece3e9919d748a35890081d8f7c359e7985522790bc2e8fbd6081560d37c69e9dec93258e434de1c0262c7e4e41de2151cf2ba2b7af765b8065db15f5d6c0bd3ccbad46a9efb13d06a97334c5a7d262309e8096f1b0d882d5cc8cf64d3e1d0e84308c872a407f3c617e2e96c5f0e6cb8944f51fb8c3380ef6a1d2732a3f5c595f2f86e594ecab7f502479624f79b300f878a82e4de557713f53b69f8225aed45836b46cbbe410fec329910690eb5c3b75d8747281bac443c7dae474ac18bc25fd36c2591dc51f8c7d185555ce7dfa77757f96b9eed06c967669491784d11f4c710e5918433342d06acabf617fb861196947c0f3002d5d039d15910456b16752e388f258168b44c0331996a7ab9c7f737df9e3f250f3b7ea095fa827c606b0ba46ea1a6dfab96a10db8e00249b91ec76087d0319e738f0241816e053e98974111621cc785217fdb1d167c4a164fa3cd3b2d2d7fc81096bcfab24978b3ec6b9be8381a2fb8fb76c0ca860960157bcaa442208e4b9d8892e50d66e94dc18bb64ba1e20ac1256de5e8ce44f2f173997c60e96094df086030cb445a8e4b99eba757285548e42af772a930eaf6abd7111379a2c2846c451143072de2a14d1035400385c477f38ab6da8689adddf966de5a9409df7abc293845fac56f570819c043950691c97bbde06a1c6e95c15d8531276c20c5c795f2265623bd25a80e0e439c116294539d80669058fdf6a57843ee874624b5a461ce0cc77c2859e19a201b5c2e147ee496e7ae474f1186004881ad88d6914aed9b60052be310a1d0061d7a462872f7218f5c25c1296a93b60c35cfc09e2df6e02a30c81900456e8fb9672c2661e03422db14bcdb0ac182ccc9d28aa7a9003c5af4c61df340299b348745b3860d08e462215d29e24d5d219663184f58b64182e0512802e324c2c508eb14bd5735d3ca44fc542df8a08abe2ece29cb269142fc20ad33ebda3aefdba484357ba095650b30d28e8caa673a0b46256361d9aa922bd78e8ab2cd62fbdff9492c1e967b2a14810b6d44a24f76030bbb73ef327ed9478b243af939c091881d44ca283f892051ddda5ecf10ca0598f425efc068d0f1f330b8190368888aec5edc69970f824e7bcf24926cb309dddaa5dfdae70e607b41f5d4c21cd84e5d2b5d3a3e6ffb6ea1a41db07730170f810158e2a51ddfe8a43750433c48a7638ff1021ea825095afe23ddd368e86b51215830bedf1e0461b073bf18288df60a6b24605db162f434b9d9f92ded6f69d1443ee41bcc04df4fbb85e019221923aefa352420494481805f230b3a8d81630c5e9911d4a8cf270ea312bee388242bb822e5016f2debfd895d4a0bdba686e83ebc98232fb904f600404ac350d49c24396501dfbec5d16d4b91329ad0489d7635ee7f031aa8197629cac67ee5183e66bc2dbb0afc2ae46eceb5f695724168b91dfcdaddc9d72e4df13cffc444be3413c0048174635d68621b22924b1a7d4635a83dc874c070273607b8b81cedf72429a5aec84aec9f9a344209f1050daf71a89f96e3ad64ff8140705ea6c994b6e029263a999359e16b5647b0269be3e6540f9c9925ef05c3aee9f9511eb21727f19dc48298363e0fa93d6d8074c01c86b73d7f1318a1012c3ec25093305b6e1e69f01f0276d70bcb67eef9aaf73f98e6bfdf6f70fa0a9f0d59254c2bae83b21c7af2bedc7c936c779412efb2a3453a4bd4d7231cf5af93d58e84f22e5f1cde58ed8b3f6bdc730bc9e97cfda8bc348a5fffffad125063d25f946b6dd4a480fd34bee1bb6a6bd3fe1ffb8d4fda10e0a7e4073fda4d615f55bf336ae82600c2fd3be1d66ddd014f43f8ac7393d5aaf424fb743363d68b2985533254752eb836c2492b1840442dbccce87b7fd128eaac5c59c002d03032e5348257cf27a3a7505becb2f6398f5142fc0ee6a5582cb5533d31d4f06a46136688ddacdc387ab9c92cd75e0c824e00ae68683c60c8bb1e6e6a101bd34358fdf30bce5043995d08e3597c7554946ae3dc3245bafff848eb8a5c97762afb21c4c8b2f17f7551923c742e8cf45bb327f635f1dedf5a636355ec161bdd4391e57f1d3f10a84ea05bcc116a7e32b88993a1ada1234180e70557bbf953d592734e5882c3c6591feed67aa3b74ce78077901ab8d23b701f580afadff24a019e4b66af37f6a3e2d9c0520291f5d9c8736c191469c45321556ff788b6e4d3e9fe1815027a72c9bd54329c53cc12c1fb98abcecf6893690467d8269d9a33c06762f6d2d530898c1147972262b055904cac1477a6088b09dccd8674ecf64edaba1ebce01bc8e3c0f1d9bb54db42ed6cacfb29fe2f867716d85afbe39fd26be51f5f12e0b1e7f177947cf59d583a8742dd44c0ed099f42c84326868a18c2c2f4c457ad70ce2581f9b6707b88dea581a88cc8cf4736a665465ceb00beab21bbdd8e989f3ac3f11f9728ace30ed3ddc69fcfacf3a734ad735b43929cb686213fe0856d480cd496ac91a15d273d716a8135369b36e09fc4b12cc0d118fdb5e90e20f85a9a5f721966aaf98819a4cd1728e765e5f8f690e1ba4dfccec852525359ad8c9006fc2472a060a1b259155f448e4365682d7cfb1f715cc29b56d28d251a610576f5865f69a97b880b0fc58da3a6603dfc823cd70b68eafd2d5cf4c7ca675690387db2a5a54c1f928b432190a0f9fa6255d47bab9a0ea9fe188b7fea5d434e6bd913dfefd837341245b557353fc8b4aa6f5a38b46717c64088b84276f157217c26c63b4c104952ffa52d25830baebdcf77182de59b42831c7595baedb5511b760b7f08193f1a66b485c30a33e63c8bfc54f047025ebc0f116aded89f72d72c35857ce4188c0f1a5160e46f655679cad619d9d96275b14a77ab75df83dd92265c005e19ba1daf5edaa8eb788d6b4077cf70987b5b367ec44cc2d86b06c6d21412d8650c03e56e9d94ee9977295617d626ad2b54500d6f5d614d77190012bd2574951c9e57581ec0ce44d5492c9e4143c2863379bb37d891ff1ad81505015af1e75b7fe672e75b531a757b696b60e2185b5570835b0031e235650a5e4752f70013f63d453c83536e28aa2fa868f93604ed700457de85cf21c167a2b843491735b8488f26f37ea43e3390ef0655dabbe534567c79af9ed0d298080ea36d84d03f5616b6339b741e4d4ced952730b4b560843515f0d6161355341fda31f2509ccb65744cef6a60200128cd8f3ac3aeb9ed6b0ebfb0e4ef3ccabbf3087fccaa24ee19ff97550eace56b267164c56e131427457504827004c7d2d7282765d278d27c62f4aaf2735c1335f2c5f28db0a3dc101e04df874c9a503a1c12c20c66b169582ab4320a8ff996d300da75885128beedab12192e402cdbd73001d3c3578a056bfd913770815c6bc82ed58b815c4bc1e771a0c9e8a296ebc14e8ced4663e9473b51a891b91e0430bc866302d429834aa8793645a504d44c80cf601fc7c85ad55dcddc94555a7ac9fecc65e13a134df7284dc90e481cafc3a916682c67bef851ce722dd11098abd9c40e51abf4aa7f8d7f50db4fb94551fe0a4057e89715395b752843b5dd526473e9347469d8b7168946d422e3900973236db063323d97c8ae324cc8482302ee572ee8d9509c3d1c8571d1293230e7c5110128dacda0c09ec7f8f23dc1d21797244e78b332f5f203126aeaac911328b9085f640e43510d03dde52c9c886513100139cbafb92342c5a37124ab585a2c06694efccb9a2a6f1bde8243c94b17b1c97e2645bd62b76e1e9fcfb6d2d5d482ade7f74e3fc74e4be15d10dae18731f51cbb4af6865dfbc1e219db225edf05455223928dd8ab174b21b9914a9be14942b78d15777551d254e09f48f829515ff75c498d0866ad1ede5619125fe74bad8465aa1cbb7be9251fc21c52a5e2f60313af9c63a4aa472c0da4806891b8b63eb92999cfe2ac4c542231a2a01b8fae3e0e8f78694b0a1b1879bb0e2cda575d4e2c8369ac5d32d4307c6256e0dddcf658fcc471afd79026b68d773e8ee04805274b118c37114245668c91ae619ac9262b0475ad4369916d4fcacd2e8f77d4676f2415271190e54c548260e685d3c8331d828248dc537fd067abbeb5319b6ccdbad92e3d2d3146a5f590c937d5f2f84de2be76e31fde1cc56a4a19216b8d303a3d8bb4afb309841f1f947081f98a6c17545a8df6603ad01433f02597267faadda3bd81b60704f5aae3447ba776954b975235cd8c97b8809b5591938ec19a602075249ebba544d5c45df192a8ff046fb4bf21226bd335a9db9cf0dd02df302f60ed86c690e5ed5da4525adb1a890cec13e719ca499f76f1dc344f7d6075f33a9dc087239f218a1ccce7091fe75c9cdcfd820b5483e74c40050f3eb3d55604fdb5c252dfe921a0cd0283a9b0141781152faddd45ad8e2e3f1e8f9349f863f95e217a0a340a11e2d3d49f538fbce15a366df79b4fde42c98b248f6dccdc520e652c0fa0fa6f5b96c2053f79cfc2aa2d38cee04f8e6ccad801edd291dc8f29bef0998032cd1c07b027892ae41e486e422e250b34e02f5256c9483f8124a8b2e4953a398c7d202b3069457ae31ec635c81fcf024b666555679071ba79a4f5fcf42df24791ec26ff9aab06e7d5359873764d243048962c1e364f42691632492b740aac65337e87c1bdb9fe1fea05e4b7b8ac8439ebf15a39934f1f22f283777f8aca70f996c18c951cddfd51be2473eec4bd48fa4a732f2e8276018c31059aeebb2164dc3a9aec42d8154aea1fefaafbe7a583c258b95c8732394452fbc93b73ec7b59c63653863b15350a8fb049dd3aa68688ecfd5a7c27fc01c69611e5cb895adce0d375e26bd039d677b7aa054fe18f9e45185e93bbb4c0536999a4a21744d8fb13f316bded31696730afc55e5c98cff29b4cc68f3082d8e38cdefda347e07e2acf88726cbb436c6052d828340d3b7872b5b8102023750ba83413cb947c34b288c0a61f94117cc713d1cb2781a3933b4c5028deb200d8f0b0b605e4ae523c8d1c2ac3644e63ba0a68d2892b7dd2c4abb25ce38cc28d6b6a5fbadeef95b94235e324506d349bf5efac2150a979c9671f348420d861583a9ff31fa436dc3a2141a734827d1d64116a3ea42a3150f61aa89bbb58ee1f4cb6da250777d6bdd674d4f7e30ccf5d0299ab2cee7bb6db69c2c898849c420a9c009761681dff518ab32f6e2d64ad6f7d3c242cd7ff358f448d4cd3a1f8e6d254d46e58d3571d2314b2f0af97ae148609889ef8859976a9d12660af65ac550d520e97cceb166787c3e7b442a8b9825e339794ca06999fba317bee5314e263987ca42e312db1a28b4823f48f38b58e65b682274d4bdaabf6a85da15910959c8e86074c382b6e30f1b355553016a3adb77f05cfaf2cf9232bf16c56f31046e22cb6eb78029cd289a1cf9258bd75948f26d914adc574ea85b433775db4c62a4746ea8fc5e5c76e584bf39cc6c80d81c488f99a97c7c5f8aab21f1d2097c81290cc504d4a2f55f6189e8502920b7b2a2ab60c08c1531940e087495b0ef8aa02f8fd5206e5132401742355c8fb4f8487da320e89dc8568e6410c26bf0b1c0c595350edafb2c1836c9a63499aa2634509ef596b2f08aa625847809814cee05d6472493a14956cc1be03eac6a9ff553e18d4d6e53e662de2db05c869486a5ba74227c61bda889e8c1fb9e731af34dbc93edfc00ed4aeba9143fb703239f93ce117928fddcd755306a5853e944d3532d98d358bfd9a7f74ff751902047e126d4e4e8c7d2565d4509c14283d6ffe76577a1cb8a56c364ee0932af8d3617ed166a014bf77e417069a180d884f53871ce2b4ced4b8614f00745cea925cc7fb03117badbbfbdc4cc8f00519219bdabc12f5261bbef76fa3d56e486499b1f84b5be92fd0e3a2e32ea026ec387057402ceb0ad6193911cb6b785485db1c3f6b854877bab67fc15b1480b0f3d6e682ef20cab351c417d207aa6503047a576af542762a97009afd58fd1ba5389dbaf25e5e93df9a2f0d3b3527e5e634de03a42592905e92287a6306a4c8a15565a6ca6860cf6c62f58aedf5b575920d5aa3d79bfb4bc8f3e3bc02ca254ef2c25f2babaf09d44b7cfb4a320c102f4472c70612f0892430c3782d303b50cc12304632e324409143c543bbb6b764eef42bff5d7980d6950ba035441bd13d1798a4b4a1ce8c802667bb9bf3521a0ddfaed2f9f7a55ba912e21694dc0529dae3405c0883429d8e161e5a82507abb7d205ed2d4cf007ec47576ec9584add448cd30834d4f4c9dd5fc30fa503bde9234512d0764b840998c723101c4dc34a07071d4dc84542b97aff5935aa2c110f9116a8a9680d6ae53843f4cb109612159c02e335e60201fcca643f249f6d62e7fb03e8c621beb6264e38da93c1dd1606c428b6cd7d4ee03c49455fa0ddd1741c93b16ca1b8dfcf0e64495630156d84079bd07a27bfb1062cf04e99664960c374fd79371191edcc739adb38cc5b10ecc91c0f8c5fda056602c56aaa1ae4a6b8528e071ea8f164caa3ade3ec71f60f97bc05e2dcb00a2939cb89aa694869d51d0cb63c9936b80f20864ae14e5d6b84201c51ce805e9b67961254c3886c24bbb708fb8bd22f5f8dabe19035fc0712c8b92e72277116af69d621583e019ebab6ed405f5ba032c55ff1a1ef4d70f28e5b1c0806624e010a2201d33bad5e483a8e7568ad78a5af43a27f324bde78a46d099cb862260ef725a188083ed897b84fb2fe3841d3bf48548b028c5679fb2f0b02b56d281322c4ddb62557e3b00dc0977b5d1f0aa8891bd759b57a26bc0b1ba811e927fb632653761012117e35fbd91bf0498a214785df1e2b7bb008223b6f632f54e862c402dabb91634618f876f1e791fecec8407d369a687c7c77a5bb7c1ab7a48abe59e75bfb8dfade31e95a684b0c860705a5f088c6cbd0f86e57a7cf881c02195471e518f29b06b841631729d94c09994fd4f9fc1536c1ad1bd5bd3a95829642d9e6fe7702c4c66f0eeded75af7248b92e0841057242cd3be9f37eaa49a5259b78c8202598fdc66b88b778752a3f9c0b30d98fc602853fae349c1562221bef3cfa999926002cff9e522194ee57e625c25114d60f8ab25b51d46f8cfda39caafedad683d998f456b32a0e0237510b741554c8ae4ff5f096f5f5ca30f2028bbd48b310fae55485fa136c0636abb57a67ef8552c055421257e50487eaad5633490fdcefb5076535fe97c5ffe28a9edf409bbda3b90a50104e3ccfbafdb8fa4f4df37500d7422db75c1153ddf6a7e5a2d9e8bd00e112339eb768c61f8f024340fc43aea90f014b3bc8e599016215b8ea541ef859206b17d8520698eb0353ce827874b1821952efb0b6080538a944abaa8a8b3da3c0709a04ad0a6623699f32406a6977683cc9f22d278755d05d5dfd90d5b423088f362c6ffa88c37e9ed22c60dd154c430e4b14bbebd09007e051fa247aac7f1fbddc420134f86b78d8bd2b876028a2c8c6a74de9cec85c689588bb497a786aaccc73568a06c65b537c41458d243ae2b04a234f9d0a86164b784b0ac28b1ceec05ad6f67e5a568e9b07369507769bb30c1ef11e8a1d3ee2deca22c4d1acada9a6b059e37b6c435858bc20ca066693e388d71832312e2d6853109090be3a03e1d719f3e584e58b7c0d745538474ba761545d01f6f34ba08dda0a7d23aa7af9b97dafbee1dbd67da071b7f96cb510bd4797ab7b3130ca3646ecb1b1c91fb6c007b9c7550afa7f3f94bd36ab177a73b377f20102f1b93fa86a5aa59e75e7e5ecb7836d0d7f05bced9b0c366dc36aec2f6cb35802cf386c4bbfb191a614d8957409e98daa11db7c3a775fcc8e630c3bbe880c98cc78d54620f4e0fd9bacfd114f9b9076f0a1688efc3fac5bfcd647869fe9602ab7db18f95e9bf6614a6a58574f0d53aca826be59e60144ef46b53fcbdcc7d7bf7e3f12412b06506fbf5315a14abca76e1f87826eee07fc94efc7492052059ee3d662531d8af5a9a0cdc50e932e98b0c051859d79b1191125358d81202e3dcb1140db9bf0f1c303a6574c6f2e39094ff8c9468802a629243ff76a410482cd9940977c3c59f400f78c6fe5231a6466719156abe50c09fb01d4409a40c69b812b8d9a0aaf9c0fed499ab1b59929c1919a48856f7d682cea55528d824d22f4d8906048612478dce5a42eaee527a5d3896bc88d7558e58b74d2b47ac6113379ed910e6931c06855c0a3020ee8a0e2b566a58349cff32643128fc92647c8c5f0fb8878cf4d141d38492c494db9a51db82f2408650d7832b21f47718ffd60f8cd6d63ea094b35aeb904a7cfa8e2386849fe3afccff87d062dd8a4ab13a426dd232eda7a6d9438bd1575515f07654e58e0791e20773b8624c3f96c84c7b6b07fad10c84d767431ed667fb119d6c41aab4ef171753f16d115a84e49699bb767e98f6e95d9a32a4e1e7676c46ca64cb94bce994a19b54dd57d3e4e76a6631938f9a1c628731ad5b86cb076e99144b2470c19eddd8873444b20fd8c0d3310a702c2517b59de72014c8e4a15767aeea223e0617f672dd99cd91bee65651cb2978ac3eb6ac71cc327d599c1fa5511ad6d5121b2ed0478e5b8f50dd2b48721ef71821cb1f0728272803a07b04b8d479efa1e944d1112361a98944193c4137e879edea7d17739e591e3a7e9eca4c87542ac7e1cc67f9e8cc8c66486aaf3accbd3622ece5c3e4b377a76fdbc622f3d16d630ce356c1359ce488b151127df26e9708d700598adece7176e7625c271b8569e9b3b93caa7bb91382381ab0ca73e5bfd4914d76d1f22ed4fc43089fd26d1d630c32c82d5bedab185700d64c75a7c7547643e89087a148ffd27c889b279b2716b3d6a82ed48ac03e8a5056bab174b27d261b263b55f4016fe171de7460a6b43248b77c2b2a793a8f7542a1c076d93673c36ebe7638248dfcd69e7fb51166c0ca0894c9584b89ac63eb5d3bdea7e271a3bf5bfce0e9dc435c61e83aa3122c81d765795e1febb02a16717585f9f9cd61b8c96c09871e1a41ba9de29911bb1c4f4590f58af46fed6288bdc9cb919115916f7d5dfab91b481ae912d9fb8f4e257be884706809c9606a0caf85aad20e3584bad61f47761af30ac329ae468ec9482e65078be10fa4c024533b58c652f94fff73625755626dd2d20dcac14d665c9527b9f4cfa839727d9f61de3d740bb6a84191f8d31193b4ba53b2dbd922dc607e9e382051f5c08c58b5a10f626f3d6b675a1a7f25d3c358e1a0164384e127d288004e8e823f9b187fe551210a7da5af89ba12fe471774a82e211d58ca1eada6fc99b9dc6e81c446604a21dd5077ed811e6e8e07159cc5805c09c224fcf114a36485391bdaf20ac454fe40d591486c796d205ff8bcbe04bf758c6a5ec372c38fbe4220d64ccf497403088755eca3e2a393226dc58a6637638a81f19e3812101385d1fc294cab1c15e7ba508f8ff1aa76774ba0fd09d4b9dc9f8b116ab5247898d23c2cf508327c100a07e2e0f4fe57fdc0f3eb9f5ca2d8385597b322514e95d7f290936ce0f5b6f0bf8435a1cc87c7ca2048f8d304356fd41d2448ce52fd9d0142074492454cddca8ba20547b0722348edaaff46c04d0217083e235f4862df7b4043d1035c083b61ec044c372d81d352e7f88328640067a33a6028775211be46d620832cae8bf4ffb6aa91f0d044aa658291134cd5d4e3d78f0d9430074f408c0d1a419c8560cabae86b050735637fef79b86cda7587e467af8e4426d4bc25940f3d94297fa088af15b16f06a559bf074e3bc68d20ec7cfa6cc577ad35b90d2ac007d6c6fcf86aaf41ea8880f1da17c6a5a469fbbac3963809753c05b61a5078e7f54199e9cd38b5a744a6268007f3927d6080336b3696ec30299c54476b1876a3123d0c7e7f7cef7084898607ce1e4c1b75cc63b07ed904f1c50fec07a6280bdd30821ab16f8ffd72ec20eb04ed256b49a490965cbf64608aa65a6c5630e6821fce3d3ce5fac8f92e8c638550bf07a052e4a0e4d944a380d0e68da285380a08d1e97528cc7c77fa25169808c2d332105769c0d5963520f98d637d5f13e1fb90d3878ab5613d53abe6eebead158166044d61871603f9499f5bfbde7f71683098d0142df4f7fc8252f414d52c8b615775b7a092b76bed446f01a28ed0312d3df1550a278fb20da2f4e80c8665c007f63ff00647149528084465e22ee314ee2ac053d36ec21ddcb387a7259ca6158a92fe3cd342e7c7ce5006436649fe04023b1d3af5c735740b57f00acf5da354053a56d1088549897bfbd9a26b5decdc98e8fca2459ab7f4fc649ac59bd44191c22fcb547d0145d4f4246d7921fb31ae0a7ad15b803a342fd07d4b5f499f05465d6f9b5c2f352a9c935cc74e45e431bad8afac200b5310a63be3532a8566735f24546e8f7364501d053f77328a5b1f8c28e91017852b7fa7fb51e6b697970d10a4e5960d7f93bbb24acb894d2307a0473000ae658c2aa78caa5fd855e8db51a1e0dd9348713d42dadd394bcef5dbdf7f8257125134d17745a8bdd7760fc1b31563b166d7efe3fcd6d445fed59abda9c3c1fece7638682e62c96a1f65dd27613b11085afb4d472fa1fa46f6c95f036721e8d2897a47bb6c8114bc38c414ea110fa30546636084b1ea94b8d1d0a724b9c05533c034ab5f41d3cb05253b3d7738e43bf659ca32fbf678eeb769086c78a8ccfe505c600150e9febf0362249e783ce6ef5a1f1ea203528a5f351747728197df7130a3591ff4b2992d6f75a4a601d0325bd1d6fd07be04bcf59f0dd8943361c76a431696211de445fb1f5f2b2fdf7958e99662f422e06f7c7af628a5c8b6761181e6696c288b075fb21271cda0249dc901631702a71d22c99d16a786078df2fbc80298a6574bf7b6455f7258f198f24017b4bbfa7a447fe14cc401082ef04d88bdc0ea071bd979600c85ac5f8293c7061bd4c78583ee58dad82d6ebbdf5ef1b19d15ec11f25de95032ae4b0065a31aa88afc338c95b74858ff5848814616f0c5a5c8a6936f516702acd089444b743d05b48320e926eaa9254f4a2d92d49fdbd2bad72963575eaf83d01886d307327b350fb242a5d5cf026392d82fa485524dcba26727834be78d809d31f3f537b4ab676c06ecb4b96917825374e702456e1588fcfbf1ad70505314f99941da5eb24b179370948aba864d4bfe789d5ed25ed4510e4445a4744ac86672b30a82aa7b21028cdea8d461834ee2a160533144dbe6cc7b8c31f34a1801096e9f5e8ce741c434212c0fbc91a7f5b29df742c98e3f617e289563ccbdeeb37f96f3814cf6af82b97008e2725f64d28b12525765924568c8c3a5196513d574e55c635fa0956a6c9cad3104ecc9551c24fc6f22cf56515e2f31fa4092033542551b78d11cb7cef4f2a3370d6fe86b63999a0b4fd65f59d2259620b93a06f0f1f66d965848b4064bc5409e60ccf568e9d05497166647e46976a45dec829bf87625f794972e1b89fbd700eed5db584d09cc3f149749c81e3fc669f48f9e1940f643d7b770a88448824753238ced3edc866c665ae1d370deaf23cff4149526e9d100d39682e1e2c81125b4c8e819b65eabae35c433b98d22eca3e85426965b75a9c95626f37870370148895ad66d466c048fe0eb7c434b6e3a22f1112fa8c1ea06b774b8fb7a1cfac8d282b865f4fdf23525f544365f99acf496b7b16080649d702151675061dcdcc271edc20eef8ad67d18785eca0b95a61857b1b6e76c783d887e5f3f92bd7d7decac6cf8d180d4bde532594e1cb2446571f84adecb53d15a40ab6872f625e2fce2b84c3d42197f3240fb6872f28431e8289cfd2c04f1fdb62df276b8a6756c8ea2f3e3034e64dc76d6100dd7fba6193e6b6cb742cf39faa3d0d8210e96095b2179d785ea0fe60564d7d634c3edce2518b991efc865ae2426d4a95bbccc4618015c3090e5d150757c6386cdc453b38bbe666003db1ddfd13cb93693f354ddfe3dfaa52e3166107060550d77073458ffe84c0280565b312c106c78ddc64be7ad369b14c89dda05e121a35e422ddcf7be50d7dc4640147e4040ed5bd7a36a40c885808633a63c2b3e8ea0daa5e5337cd70ffe144456f8b9a3badb0fb631acb6080354f2311ad24f83790eea6b122d7cd232fd1a1af0d8351a3776da017703b1b0aa81514c9c9001c1b894082daf94d04863614a091a9beb4466b24cec750052889ecacae4ff83b11503ef1d1e86a18478a802efa724048f6767cea55f7c0688702d05024587c8daaa5cca0e6a1946875aad3b6c7a026af3deb956a8f58609ef478836a725490e83dd47aa6424d3e9b871da6d01e3e1869e7fb3f3ed3f21ec4e0c2878b3f49ed48a3d2170b8e7a43898d43e9e9f5245f7bfe92dd6121a0d4eeb90684c3116b7d65ab7d242b43707208e714232305a9df460edc4705cbcedaf8b5f90a4c6f32fc28a85fd3d75b3f798433b033d88f072760bafecd86933d0266c6d50d1754cdbf19928f5208bea9d22eb053b68cef6bace70aff58ec9745eda50271d638c8d0dca1d7cbb1aeb1f7ed1161b57019e910da92e9358eb5a665339bccc5515874a8395024355063cd8fc7a5ea2adcd19bc300b86a09f8dbcff355ca5868d158eec275307f9db2cd01b7c65b2d5b36ba481bab052c88f9df8f41fba68ea26f7d224e78c0585f8c28035a6b367a59305c554d074bc7280b4c942c30a37cb82611ecf8bd9e6aa2a9b4cee55f51ecf855aab947a7fdcb74120040601881a9eb1142c31003a03a056555934073d16a3a606d3ceab3fdb55c5d536349266c15edde7b6fb9b74c3249199e07a807bf0723164caa049ef379549cfd2bdeb0fc8ab39088fb2ec1b385d582ac7273e36c9298f2a64bf1ae25e574e95dbd6bed01a9cedbdc56e9cdf49f148aeca3cf51aa3af1324fabbfa55c3fdd17369b24bcf34237d4c17ee6a90bea827e82e6a947cc33dab74bda0afb322bd88af34c44c1da34d361775f8827ada3a2fb8ef81dbce7d767f6e1a0db8dd366286d0755461e726605d395cb121eedac083f9146eb1feaa260b83df1e96b9e3a162889d85e888ab211e251364258943c41a47143bfbc6902f477ac7992e958f3d431e104ee145c3fa7742d3a6fa60a076ee6a9230c876fdad431a4df0f0249c40a703fa7f1a06e55fb6ef8b0f132d4fc6287f465c0206f7e54b0bc9924d29c48b464d7ee78ed6ac4c5ae82ddcb7a66cfbcf407a461c993ae7b7e37a74bdc33465c626977f4e60fa49493462923a64a8830f16bd5e69c26aa8418c6f739e79cd415e90721c61823bdf4a79b73d200b32d1c3c40a81308b6dbdddda953f993aae03929757f971f2bdfe577237e744e882ccbb296a448a83b0ea44482e360ce9f1309d4a79c72ca297780039fa22391ac95ad098595fd44d7f249df12b9cc4b59962807098b822c96c7c25eb2603948582e698c0e5922225b946a53d7d361c7ea8e2081c5e39212e12bb41569a89665d40993854bd29719ddc0c8301cf58e47891a4f1c2bd82e612975cf529488de40049b473744b87f7433c219a9dad4fd74ad0ec90fba169eb259b868107c66d3a7ec02494aafd105477a02c94fd53296193ce183b35d6184277e80f35bd2bf845aa802e777e14c12e14c2e0123f566500f8258a17e18e2a915a05a7e88afa00b28a7419400f5c3109904d5f243e41228bf496c2358a43100ce91b048c302672f97449a988b8cf5719e5afce77d2806dbdb673cdcaf7cf69d334f2b3ef36e6e15ceba9b5b2885f30c0695d8d2bb7c0b28594d80ecbb500912d6bdcb102cfd2c6155fab4a9fbec46470455025bbef43cb47ce9bf2fdd1f92f82e0fa577b93f94eccf2f7deff21155bab5642f0e8953debff4954a2e3704a49f5f22b168b960146c676f9b205b5a6ee9fb96effa70f9d24794ec7129b5b4aef4913dd2d561f62e4049cabe050b304e98286c96b03cd303eb9b799a5fd795ba8fd87b14980766dfd20425f481e7dd679fc34c2cc664a32b6113a6ebb2b9712f2cb02d5e586059bcb0c07638bf0ae8bf02ce670123aac69e09937d36f2b159e507672fb1f651d3fee769bef6f4bba149d87761de856db08fb34924bc791ace2f5b3ee111b67f72601f6916d83c392b6049fb952782520147aff23c8c54545ee5f6cd0ffea31b124191e68944ca6870c2662f7b64add6baecc1190b673effc60fb00e3319934333389330ffd1f3e03fba25ed471f513ccc2f69d7c7caab7c44e5d0332b443968b70621db9f675c5daf98429765dbe49eabd98f6cd2928e88e9e78898de1a63b040402112635acaae37a6b77693d007367e03755d8c9af69ae6ee9a77bbf69996d51c59f6f389607feaefde4ef8a328a1e2c607352423c20950f687021c41fd30242241d99b04f743a216502319ec0e900390a7182d4b26d2c4c0fe3422cd7c9214560bd10a4bf08f92063b8a566ad9995b03c565b6ff8ff1b47df7833ef7db67106fdc53ee39fa28dfa9c04a4c3d20a0f6dc671077f1bbd1691e05b9ff1843efa370dfe74ac2ddf61af883fef61dd552b494cf1171caad3106fb19088835cc3de5eabfb0c052f9718f6267759ff4e42c2d4d421fd84ca9073af423ec8fc01a7b3f3061dc07f73009db9f2d87052db0fc522a85a14e086796267076c101e797570a6678f564a108f6ff2c8421ec4114ba396badd333d9ed630b53ca6e4a63b5b2561289524a29d53229a34f31dddddd1d06d3a759f6d65a3a279d54051186be068e3a60b325d460ba534a04771b4acae7c5143bfa3c3bf254ac4c66059979a936e2d1011ad8adb5b6e354be1bfe926befbba1dd2c714caf017660645736d6748f72769d89f14453ec8cd6ba8c34d1a5897a0692a665d9b16e3d60b519bc7482c78d7894fc506646099c634b05d1e5041346b84c71e109268e40851c8a9a45224cd4a946224cc4a82678224cecf984133a32124915809a8815492842892260d0ba401e13ac4e97a14537f1aab309984a3741869174a2042859144c6430c516667013c58a29ccc048b78cef53ec191de94df0e008840521ec5fc211080b45c013085c6b873d022a6577d38fdf0e1d7da0b87f76115d7cc9be826c5bc6bb6976b7c4b25bc6882ec653d5dcbd6a7e3bc6f8ec2b741ed580d77d1fdf031f9827cfa6e438830b0b96c13daeb1bf09a0bc778794202051de4b94776f3c14cadf38cac67b212a4a461ada7fa044f5c783a350ae10352928ef1b159dfbccbb31d2d034ee06a704e67e09224425647f3b624bc73c75739f653a72cd452e7271fb4cfbece7b7237a77b31bbe06eba494de5f8e260aa906e6e9262ef1e2c5382377b8ede577ea953d53a8408c21e0904387336390d184d9b1707fadad301801c54b05354968420f8a0a6122475400841096e00008503041d12b60fa3237c0d4aba494fb41c26652c746c91cc0344553916605f7cf6fcc12c30e9080527c9be6f793e63cd9cb22e3c5402e11c3d9e2974f826129250f945c22d8ed095150e17e949e9637a46ac49262a558f10814300c36cb95943288b21f850636aba4c0276c962a99d326794b3b3213acb07c942b5313a66555a7a6523555753c0d05c5f3501a6b2fada8d35e99f67a617ab5224c53b2ea36782a37d227ad3561e8879f9da775a4e513e3fdfd05d1862654c3678f01f6779d4843c3fb7baa4f9aeac6559e53d4a619aa8836341e8da70c4491e6df7f0af5298bc5a692d890ff0c6a138cf79f5388363426b0ff4c1269ee9f3297abe5fa71b98eb409dc41b4910d0df58015695cde7f16e953c6624d1d9611160ff6d2fb4f13441b1910d02402fb4f9c480380589f3215916aa6e6cd509b5e2c8936321e1e2ec0220d09a84f34164b12138a29c1dee2fdfb08d1468693e17412b07f2389342defdf3b7da22e96ab7bda489b5cbc7fcb20daa0434334c0cef2fe6dd327ca62350eab8bb074dab4d224daa040404f8a220d8b579fa84a0553c58884dab4a20ad106e5e1b94250a451f1e993c7624862406edf5ffa20daa03872060502f6974722cde8fd654e9fdce5922bd78e4c01f69745441b3e2467f4c95945405fc0fe1b88361c08e5fd3dd0a6fafe4022cd84b98f3eb96ac250ff1dd186f3c819f42392180e323aa5df730d1867ba260c7df721396386ef86b7700cada509b55a1fde1a2e0d2933682a8d4753bd68aa189a8afecba3c0886938b1988683e9c7b81f98b9b2a1ccf5990b46e6ca8630fdebe295325606c462b180307d1000dd8b4cc593a95c325529e3c1f49fc4b58865383152ac450c46cbe6c235e422b95ab886307d00b0d415ca02a22c16ca5aa12c204cff050b6d0555511ea72a16aa5aa12a1795cc7a8ce2782c4c63b118c5c1f44b232a5d432492bdae214cbf05c5abb31ce83acb59ce72204cdf0519ecc45963792a1ed23b4f8a8b9654a2a4c0c633e638247b638ef3ad4859566ffe98641f930e5df354577348daa83e72062591567408d3ef3cd02bb29a272fa7fa481b5e1171067dca22797585a9a7dac1d44b418ca1d50747e3ee7aaa1843dfa50adb9fd29aa7ef4b6979afeed5b83f98d6431b6b472648fa3e267d9aaaf90df509e68b30fd2e7cc1f3593e2af2079b131661e84b6d861485bb01a05f77b0766d87f461409686a12e9d2da6f4e3027d9abf29843aa44f83075e913e75afe9a9bcd7077b61fa0d350cfd8fc9f7c1708aeba533f092a163ca65deb2a7c4df0ef2b39bbf9ff6c7c385a32452d83eb8dd79bacf806242b833d96a93966dae3ea17cbc9dbc597ab8733caedf0e2f1d567fee861dd62484f49082e57b592662191c74f050810fd8a874768eb480821adb24d15cdf7c71d7edf092596e6893b0f346901c11d7cf21b1378e7146cee4c7a379840067b3bbb971f63cb8fbd2a11089b51810eed7b41de46b5b4823c64cedf62071e626ad674f4951fc33b04687133ba582cd325d6f3988d13cc2f133253828d061053aa4dbc8c586a2c377434787b46ffe2e0373cc06229afce9e8733dd98ff100292f5f0dd375f7eaeed52fcc7d37501cec234f7415b840e7d9cb11010ab468ebe6fe566e56ae41240706a1b8823ed0f8398a0e1dd28fc03cc50a78e01056760fa63fe79c73a210c1e2e8282b760f22f5bcef730f616b1f6da21d7dedea90813ea0b0d99743878fa628f7deb669dc391dd2edfba66fb83b67cbdcccdb365c916e558794b6c0f3e3b7a3056e9c0e69df7c6010cf9dc282f5cffdcd8bc24414ae10fb37f50a4003a66e14949f77601f6918fa3e983e7773df20c1f4b79b2d10a62f84e9673787b86f2be990fe045d2cb13906a522a0810f44e183f30b7d1d3ebc4f2a4f0f30fafc6227d847e649a58e9e6449f4bf667548df8513967ecf8e71bd4204ddc7979750c618196372f4fc1ba7ec28b84cf181143b4fa40923b3af1346d28f37c4cf88e01c3f1220e61f708ecfc3e2bed903b83f82dc6b5bfd1ca6060ed10215c4900fe207ed397777777f2166507e8704410495dd20b61ee1a637461afd3c24ee2fcd213168a7491049944cc14912a81ffc83181253842d20b1c417a030832014fd2ccbb22ccb5e881954768710398209119001072eb801105140d950fa36355a512a85ba21a1e8d7a410a55229d44d45d1af284a4251fa42cca0e80dc26f127d4bf3f218a211e9fe98d0752d35a6b03ec88f299589cd3ecc2b7798f7c7fdd12f7f871ff37acd496125112a654e6c3ac127909cb1b1eac6636402491b9b0c361a4c20dc2fe5dd56bded74b8eab0372354d83c5daea029d426e9452f0bdd94b181b0b449569c1b7c6cd798c2d2188de1f8c5700e6d01f91d677dfd038618c2c11168894dbbbb74c146a0252f1c819604e1fea45469c152469a2dc238f58f3f35c6f8bfb8b13211a8416fae3860a7ff5a752e52d8fe88e5e7d2b9bbc796b7a44f644ae9836e90caffe1024547f739a54ea909eba293b27b4e774a336de36cbaeb92dcf0d15946bb33d04390c274cef76e1970a0eb62a86c04f34b8e8981ae4b9133a6c824d3bf1ca1d710f22f87fc70c8c864a3c83bf39a3f5dad249286bf0510c1fe4d644a394414425208e1b8048e4052e4e0ce050aab9a27e9aa2cdccfd9344cbf1779918d0dcea52e50d8ecb27bcebe54d5219772b1059b670fb5a18e7b25815ca7c7e5d3a7b9d3d326a73daab9334fb4888a1669987e1ddc5f0e0dfba52aaae2582e56b0d9c31dbaaa58c10d4eb8c1093182513881111ca370821cfcb20bdd0d504882951f5326919e50d84c4fc56b4c61690b36a30e5ab0eb5bc0c11148c90ad3c011e8892ee0160e8f689022d6018183228d7c0ecbec7307c42827c6cc568ce96ec8b1f55904e530eb28d60505df0d226f78d785046406c7fb1dd20f7dec80c132f5c2c73e7d3984c4d19e667904d3c63298847d883dc4c03ec8fb12e7d0f208e8319b36f1d0210dff49a496215b3f976e69bb3c42dcfd9364644ec1b227cea04f036406cb77e9903e2175345836cb1e4c89c81ee9d3217dd9f3c98be33a0e225882b2e726082ccdaf2fbf7d74485f4687b48261877d131b463e0bb8024a0d49d47e6a607626b4880c4dd3344dd3ba6ccace3ccbb40ffda9bbcb2051d09b1f8196c42240e9a4112ccd8f38e23932411144782fbd9745accc3aebacb3ceb2cfe368564279f9db9750ee97655e165ee9b1ab3f9f93d13470764bdd3bc8f7babbfb1df2bd5bea2e8f1077df75dd95d1615f30bc4e03094bdf9ff21441c408ba9fddcfeea7f65d112b5f87d6536bada736356f44c306367bf92f4e60e97f9747d81fce937773ad7782a1cb3a7ae9b06abfc968d37ced6522cd7cee330dac7dd8a61de6775c89fbf9dd2d71dc7353060d245eb42168966519754a9f669452fa2f82b094524a334ab38cce4004353535353535353535353535353535353535353535353535353535353535353522883159672cd5457fe9c213e6c21108f683bbf664d46d89aee2609190805e25b4d5a0a20c2d2658fe7c4271e608c6f0e9407368915887d95b325c7287e3b3b88d2a67ccea49884c88a1742458fa13228c1412039ece842301cb6f014abcf582789a79c067af81033e7b120df8ec3ef0f1d9fb6715f154811e9f1d08067cf62176f8ec45e8f0d97370af4c219ee40d8fcf130b0bf83cb7b0e3f3ec828eb63b8827c9caf1790601c7e7a9c48dcf53090a187dff34413cc9a0047c9e46a03ecf17e0cfb3063934ca9278ea14023e37180ef0b9ab30c0e709040e9d728478ea1d1b9fbb09349f7b0a05f8dc5898e96f242d83786ad7e97323d13be820d4e8af493c35d10d9f3b88017c6e13d0f8dc46c8bc2c9255983fe3b3cc82003ecb25319f2518b08cefc2cf3209cc67d984007c9647903e982f9f254b2241c36729831a5e16114f3356bfb7189f25cefd2c83884da21870ff06e2c97326cc74f91c612f621560f4ef88276f01d11169b42fbd010810c3f2d10566f179847bc3fd33c493bf449ad83d334ff501375707dc06ccf898d95a3d6eae0cb8d9ee7075b81906de7278dc5c1770b3ddb1e5e0f93a6e86816b2cc7cd15c7cdf6062b86e72be06618b8fe24e0e68abad9cec7324806e1595708b8b91ee0666b80d47c1c521a918d9b2bcdcdb6003b4478fecccd30a6e63add5c4d375b02b86a10cd93b63361e6d71b6eae96c69d2fd344786aa919375701dc6c6356326e8681b3a08c3561e6db70730dc0cd16e666521684e787f3a7d67073b534dc6c67b82f37c3c86ebc354ff415e36618f7661298f339306eae00b8d9bef0968bb7f07cefd960f3444b37c320b5dc4cb23d783ec96b8b9bab6571b35d713355e1f92c9b4a0d4fb2aee33a3146b69c0872fd1011ad808d55c0c69ddd7280c0dad9564992c41819db88d86eb614118b156364630b36ee50ea4f8da2be6a50754d9c89535745e861edc06031a6318a4754575055f5a6a61a4923f95c5f80bd827ac5693b5a12b496d6a3b18a8a628c96d262a0a9b49ba020b0311744450cf63a7264633de1a795f5c418896b76931d91ad32d5109018a3bd9e10c560f147478c918db31e265c3fadfc825f628c0437588c99db920833b91b1d7040ec5481e77c30e039ff0510b6dfc1c6a70de6356e75776bdad85a72c66c529b48d9bbbb5b2ac17e7b7877ccb2dffd45caf6cf0db6b52697c2737e8ce1b9844c8ce9152f708c30f25bca4ec24ace85630c220d9014a04293a2264f9e3c79f2e4c993274f9e3c79f2a44993264e9c3871e2c48913274e9c3871e2a44993264451c0b09d15c774b9482499d94df0ef68c0ae7d75ffd95ec79f31a4f185bed39417afbd700437a94d2429a9b05266cf5d9ab0d971df0ca447dfd8616947fdbe35b609cf2884dde90d5d88b2ca9bf6d73dbf5ba519d1c5979ce2c294fb7ab26146883650e3305f4e73d5d369a6d58debbc0f25c57a0b772a2b2c2b58b420b96829b9bc00000cefbd6fa843fae0b70306a364e017fb603388f48a88333e263d4945b20aebd5d5ea5e2f03bd9e0ee9db007aaca92ad2303c3d4630d5d9e994f429004fbf7bf16c00bd0ef4541f6cce191c141b4c5f2b6aa181cdb305d39f8f80f9fe60f366942298fe6ccd16a6dfd140836703e865a077d3bd56f3d4296918eaa9264c57446262bba155add69248ff61d742039bebaaae8cf4c906af7b459a89557d809b4705e0e9d3b881dd3e1f1180df3e88fb60fa5243d76ab55a3474424260b75aad56dff9f8c4e852a9dbe9bce05eafd7cb852b2a82c1b55aad160038212117dc6ab55a91381f9f12974ab5703a2bb6d7ebf562d98a8a5a6cad56abc56213125ad956abd5b61a6d3e7206f551d952a9de74eaebf57a15d522dbc956ab555b13860a55a1542a5553d9e7bd5e455507457ba56845f5fd57bc68da87b72534710dd753b994c1a6d0d0126acdd07a690961fa2b20ca77abce67d5adbe5b753e98be4a8cef76a94ea74b815deabb54a783e98f5e782eafa2d7cbe55584e95b181d005a42ad172d979610a69fe28223712bce67b55af960fa28a5ad854b713a9c0b8ec471df8acab2bdb6a2d7eb5584e97b2d586cad4d686badd85a2c5b6b13c2f4bb95328cb6d5e6b3ad46db6aa55286de529b4e6fa9d196da529b0ea6bf59efeaab16f57dbdeaab1661fa5596c1ce56156aad5ab55585b2fab9a702d592388e7bcfe3c2240f44e21cc67de45e469f9c0b31f798fbd8957b8ef3607c3d388ff3fef36e0be5bff73efb90e7f2edf8ae1089512ee7ad18a3bd073a920e35ff91365c8938e34522e52c863523fcf06eab70babbbbbbbbbbdbc577a33bb06f5a481f8efadd77366c60c4b3bb7fceeed9f2f5e8d93dfba66a2eacbd83d990276ff8af80192c8b75a84d269a4cf8f2b3bea9a46f87bcad9ae12d23b25463ed339bedb5cf76daa47dad46483a59910ca7fbfa39e3c992f44d87da93be1d9d87e3476d150e78d3a5adb46dbf6d2a77d53c85ef4262b15eabceb47e5cd28f60ed3bfa910eb518afab68b80aa4a0e7386bc2f474e8298831da7b117186eb348cf6cee346b00b16353c094e7ca32a02dfb4695e128eaac5c783cb605a770e5de5aa064d9ee7f8ca77bc47c67309442006c5a0181483b0f62dc4717f0f29b8bfc1d883fb7aabca55dd0006f0ed80c1eee3f2b25cb33ad47e05ec9d567588d33760101f2cee12c418ed7b8838a38b744822699a466fc356be1df376ac87baeb4d9214900c5cab4ae82689960222ca62192c858ab5665f3fabb566302efb79b321ad08ce2a178470f7f53b0278b5fe0a18513db1671cb7e1bccd6d0b127225f9016a61ed250e27c96abb451b1177785bb16ddbc66a93f7f3f3f659b2b4e7c09f57e5dbe15d21127f377b2c5f8fedbb2b44e24c2bd2a1562b0d9ded7afc0946bcdd0ce405978438cebe87149c7dfd4e071d2acb0b901f1da8b0f3b98fdc0d5f1f4472d919ed678d97417ab12e323561b4f92e4d70a3af47fc9efbef721707eec60e5f7400511d0992452b240dc3612599448d33d07e3deaeda1dcdfb114cd69530fe13f892453a9ccfda4399922c1da4fedbf6f47bdd93379138cf8c50ab4cf5a914662eda3f7b995c0285f8feedb7bad1569787442e667b553ecab8023b063608ddee1f46019ac4d3d48dcbd1686ff24cbfd771be6ddac15d9d15788c4dd73df55d0450c36d31cac7ddfd01c1dedbbccf3b457614dd334ad15bfd270419ef148b3a118a3fd0433980a64165e91a6bef62e5c8c46dacffecabd10c7f57b48c1b5c35c6fbd4224e6be1df5fb47cd51ef8feeb60f1273b7871a86b58705712f526a1facbdf61d6b93ab1aa6df56927dd2f70dd65c85036e24168894e443edbb0178158cd841fa3309ff0cec41e2fa3ed430da33c1da67456f83350d076b9f65b0f6346e60f37c2e83614d2b82b5dfc088fddb31af1089fb3a2871a428d56b0d3c810437522cb991a24da529a50c7a7fb4cb36e59614c4ae295558f9f1613061e37b956694deb62ae8e24ba62cec35982fbf08cb7769c206719cc3149f0ebb0dbf28819dad4ed2a7ce69266d4a79ff266a13f7a421ab447b940eec56fb744853347565b6077239143aeb9f3695e837a593bed4efd0e3a97fa2cb3ecefdd3ad2e5a399974e845b07f17e9d063fdf2f2908de9165d3591e0f96d8356bfecae8201073f28a4e21ea69314ee6490eee76fa00f126f2fc1fc82b7597fd69ff3764fed01e2fa344572d16a71f119069e2df04cc1f36734d59f8d67123923b6b4802d2df7b17fcbf730f174210f8a31fece7a7f4fb2f2eebd8b9b49b7c55d79163749bfc42c1e345948449a565151f9c91acda19f0e3d0fc1f5238e5f37996160951b84f60a18447a60ab4df95b543e05767bd963fb1ff2bbe773627befa494ad5691eda4d76095ddeaee26d2f2f6e9d0a3fc70f4d7a760b73af448333189241b880a83099bbb47da903df3649fb01167c742642682bbff31df07b9f2b4f3cacd32f7fbaa43f76e0f12cf1722b1efb80a7844e93b90a6606213cf98af46d78db4a9de3b5b1f63526483432af23ad879709e31961f699309473963930bb68463e21ee4137c44a974676c28d3146eb589aa6eda148162469cd5a61ea72916cfbec23b91c626285b7f1a31b0d957be9abef32205d66152a66e549ccfa10e3d886369d36153231d3a4dc518ff1728b03ffa83e6c95941d87fc88fb589beff6c1269b6f7adbb59ce0a4aecdf0ef22998e54fd6b7437eff9cfe2e4336cf1decee9f9dd5a692fc205e92d27fb63ab4e9536c9a7a6a043bd5695a043bc5c1de30fe4e5453f686a4c2fe3307fb7bdd6a93c43d6df269939473a74df965f2b4c9a505e51130056c14b0c106ead0bf73fc3bc7bfe3660b499bf2f6ddbbf7fe53894823dfb7af9d047b48e97e03b394f2ebe3adb47d2e6df54acf217eb54b5687cea44ddc95d13ff3345be13f89d4b2fe2d43767e2e6ddf4f7fc6d90aefcbd7ddfdd9fd0ebf430fe2acf006cb1dfaa7fc1fa51d137714cca14330d85c03d778a1ac161d8385ceda5a47d0a5a8e853ab3d29d234cede25c6ab43877750072dd18be3bac41407114c7dea6893bf74cbe81aadb583efd795605a64cbd1253b8b5986b26deeeeee9e7d1db174b87d049242479bb2ec4974031d2c69d63fdbb6dfb6eaae4d2184ee6a472cf3abd7d1e8d26f048210a4b0790965dce677a3fbae931d6e22b83390193c9f0333f807f7de7753079e2f7d64231069b4a72a1f47366505968f1261e47fefcd9f20f72a60ae017f3f02f36352ae389c8aa8448d15564550aa19120000001000f314000030140e88442291502c8c5351d63d14000c78984a765e1f0ac33808829431c6200508000001000000010001510007131e162ebc46ecfd76ae778dc01a3fbf3751f47778dcaa5589f54100951aa95ef4fac7887dd8c4a2f2651a356b58d37f8119ebf3922c0cb2a555d1a406d42c07a050f707d4600d0726a1304041cd03e9b1d8881a8ff2d95d48af19513d2bd4352159c43825f683c86dcb88dd6cccb01f7e710ec638e609b8e01252e5e13d2dbd5203c4616527d6d9df59d87315a8578c4cff07133f13b6908a976ebda584c00ce3543ed2708b4ef08111bdf9c2a7b466f3a3e63337b32cc5b2ded0d8b0cc0d9b336ed6958ab33e1ab45e943f271ee1e9ae9f7b909494937a1110fd528a314082131d9a76bdf718486563c9ffa49eafdacfb2e9755074829c25f1d0e9c7b8ba563668bf46b6bf293460b9f90a8656d9494741f3942fb5c707a724efdd306303ac9b546d949c7879ad4d107ab378d0a962c9725e996825e7cc033a26263b1e4b25c6fde747bcd5e436474f1efdd5aad10531a8aaa880abadbd02a35e509b69c5887ec21d6ee7a8f652f100fd64eb3e791e14688f70b14c8106a2eac5469c85cfa2797742614e9c4b1f092904db215f26308cac62473357a18c32d07d4167d5985f79a93f575a671a62128487bbdcfcbc7b104d68ec2f0c17959e42b19bf705d7ddd94861a3bfd6fcab1c2807eb713b5c00e5dd5d903055f1af0cf1c7ac5f75b57b888d6ca83eae56e496ab715f68121b7feab1dfc07c153e1785df985f5fd9a9c9533b441d2b1148290df16bc1433105779bf215d62dc47b123f270175e51e89d1cbccaed7997d56eddbacde8f4a39298a49ce74929f86b0b48467e0dec11066b9c969bbb14c4b7605bfca0f8399e3e409d6e17e48ff0cc08aeb0d808d0974758b92e082ebc890eb09189b218699e9421472205450e591a3ffe91c745c78625bed1d6a683f0dab80a356e040e8521c0f379a8cbae94e752cb280945ae02df90ed39afe62620b421fc55d6023a26d0189f7325d39f23365ee6077d4dab3ce415c112a23c187b901bf5040d4f250b6d1b2b08f43b7225906aee00038f8adfc41930a1cd3ed2d1d0739b7b4e97744ee9f258dc00bda05bb6f7580dc0614420d661a2aee9747398f3ea2efc4e58a8077836aa93aee5371b81cd892c575586a617621578f2313eb4b64b8f57f2b734e4ba0f99280899373729140cf5f645c72adc1379ade1c923a283a5dbbe73ea0aa98f54aeb62b6bcf6cfddbb5c4ccd79a350d47badcc97062942c8a81c3c369735853a43525c3dfb6483bfd6ca85614674913166fc6b626299e9c7be68fd9a7133809bc1d1d6b812a2b9e266a20c1c31a53d4e1b085ec74e9da08f4b98d20dd07ba25bd1747c8ac30ac77c427c279cf5a84c063de77c12ce183cf353f7dea94b20a8af1d0e307d5e94c9162b250650f2fdd7e85ed72553c35c8dcb8821834c7234e0dd2c4650468282d04910d63a188e8e8db08dec06f1103b1b5df140640e0659f07cf87bc71d7c7e806497dc1c12f80a0b98437a71f2cc996950e3f86f316aefb5e0b537527a70a7f08027af1e8af54896f2bd69664d3328fbbe6d88de73694e1d4eaa97515d7104c87070fd2aa0d8a69c127c1f1018173b1876bafc88aaf9477f36af1edcea857762f5683bc83ecd69dfa0d0236841b4428faf9a3d4eecc37364c6c11db51a026a3a63f22411667fac2068de81cae1d86b21797d0e805624ee0fae0eac3511e24ee3228bfc4509f44d0f972888fda0f71b6c319761799a528c981240d91978c5d15547a7cbcdd0c59d2c88bb2d5bb2672d6d29c9815b6236d6acf560d19a4675fe69f94b9acc8b8a80eb348a9b9e143c8243d29d3f7666c72e75de053d9d136c4df712b0e463e27a97d6c3d22aa13cf3ca7d20ab8f59f85323e653fdee30e0d75eb84b22815ecb266186b6511446eb495a6c7c1b594ae8bf15fd4f69c507974fbc109d882aa5ddb3aa84ab0d96d2330da69944c2b1a869301e11db2a9f85e02664b9911f4e3fea185fe82f7be0a4524760939692613504973dd2cbd0a8ba0a0ff3892734c1e9dde991b140a9ef23791fafc10f33dd89c153d286e6019a1c85582f2506c41d65e6a8712894916c844cf08f3c8d911431ccc26420a44d029503c79664a071aa021e5a37537a1fd8ffde6f05a980cc5f5df7563349a4b69987d65cc8aa021a83b2a39b809f35ba4abed57dd018454f0af91a579b5799f66af8cd538aa6d3f8ebf8bc63302c2ce395c8b4f4e65e65ce89e9f1725f62df52fc3065bf03fcae2d2dfd7ebc031552caa22220d24da54871b8fc0669957899ca077b9cd282e755acf1a0fb31e66a78c6985a970c85bbee6f1fc551e230e056dc0fc4e59e026a7a3703b48e7c774f5d2f2eee9195c583311c2091e1712ed5db6eb85dfdf3b69f2ffe560a12642f4ac01a4f9686acbf498b5208ac16b6b4749b63d45222013a3fb45926815a293ef0aca2403f3ad82b69dca4b3bc595fff59306ff20ebfe71b1f1043ebcb7ae9a3e157a96e5188d347a2a82deffc79751b49ea2f81a446f7086998c4161c9c35fcd30e1c8ecc4b2b848b1b231c3bc4fc31e9402f414776c2de4b7388a286c9ca817d12061a0d12c06d0306eeb0cb70def72172cd7ee3d4365ddcb877af4a0986b66df88ee5aa171db95f07eea5b797287bcde6de37a34ef19d5c7623d21920bdef70fd2ed719f259703209972dce640265c3884fd4d8918932f1f3e872b7f19c67f2cf95c7cdf4cfc6b5f0392f6ed9131186640c4f2406cc8f10d5cf6d7d313c5a4ad9bbe9ff491a5d9d87fd86a64209c9fb781352123d53b581b13859fac2194d92e25b7878d980edbfb27d7e8b5f6469211e2801aae77bb7195884311b4bef4277bd816ae1d602c4b2d27408831af3a040c384fb3cfb4c679be2d962334cbf52561492c900816f0714452421dca08bc43fa38d0c261c1b12083f2cbab632873d532d3ffebeb3961ed6021472a15a2aecaa838f5ee82d741aec297998ddc11eaec3c750e93459505c002d2b04c45181a6630c32e8ce7cc4cbf15eff8f90620b1345660c156cce4cf88863d27b051f747afdeecfc7cd4c6ab667c74c41e87ed59ca73baa4e5f76570b334fbb11eb4d30932bdf2e62e646ed8be00907f98dbc40ffc484b82e62ea3491672cd3416e45fb666bd22953c48bc64e2c3e1829b020729309c39ccecb9c7969ba79a75eee4427e8755e0d32378d6a72b04f43028aa742f4404ab09f2b682fe8b31375abd6bc2082082d5398bdaaac9dd4684d40d1fb579c2801872657468c1b87268b45fc8f0624a1ae7c5ff098d7e9e96a3892df2d1104b1661f0be40dc31839700df8d6bfe12339094a81b43cd5cde838c03749465007e5bd0f44695c2308d985c9262be3b2733b1d60a05bb86bd634f13133eca82437a1081023da4282d36940d200402a22fb1593839fc8526f145b1873862b5cc52f0b5c5a30959029baa3c651403a56bf7bed6b2d67d17b1332e179960caf0f5b2c52ad205788065565cfdabaf6e4080090f8ce22c0879d8b35705d2a7a51d85435c7a82601699ef71ca826f4aa65a9bec42744990fed4956b2bb056c16f4282588ed93fff462db4b3caa7eca69088456562446925aa8b5e10320889ea955ce66a581928540419499fe019d540c6974b8587d865e654c0b965880893b510b0955be690a92a0a29cd20325361b81ba70d96fc379007d50f62fc9da6169da0dbcff601887c32b6c88726474c6fd76c752484cb7d2b3b882d221b17b2ec37639818930f124575cf264e19f1d7ced71d8566b8cb81a997bb8bcad4e615bc38c7c0ce9f9333c160979aa3cd07eb2b582032bc0f89b4dc06ccefaee0d13ea8d0f2a861584158b2b55d4be747308fb827af16780d3210460250a57fa78fa476892dcd1dccd05ae23d087b65cd83b21ca5a9ae70bc20fe6e3357084b25f28c7f76f3b0609bc34c726fffd49167a405a006a6b791c373e780c813810233202f41a357370739d790018338bfc6448fcd324314dcbe70477d05ff647f67c17b99e294e1448130fbb6596c0198102a9975f74ff3ef87c8127c379b4728f77277b05947aa5332b996f454c04278e6324002babbb2939f0a90a7f3c6152ccd2c30015eb00e8822308173586d90d9df08b8f36b90867fcd0f482cff29cac9b10e625581fcb89782550e20b2179baf39e4aa8e80de23de6df8ef8eab8708bd23917548cb78cd512935b1646b0cc2863a597cc488a036a419ffb71c914ae776c60229e1b867ce24ac52e71100f96a2f5255239e0a81a91e189ac359eec77991e660631a58028774606336009f1a66fb30bd4108b446804bc7a080be80aad446986dafcd91a60ffb9b26b9f0c46ce444d7520d2b4195f874b0dec6211531c8268509a99ca049b44429f47d0cc7a69b07a2b5a5acecfd5d35c85728e3f6484de11e52679468328b39f27da71e1f7bb9fec356a4e2499e7d288adc6a506a081f0231c7ca84ff62b618c46cb8622362151cc1c154bb6c8650db7359d04c4b50ccf0bf1daa9a41e7dd7fe0c10490b283a806264618dcf4fae3a491ad7350cb115f06b0a59c1f14a97d95d0440845b62ed9a6f604b7d9529676c6ee3741f407241f710f994cc87c341cfa0ac344120e80ead9f0a95fd52b294c0daec59bc24acd3c86a45cd07173d075e4747bfb54131eef9740ce5c39d39c389f64eb27a8afcb1dca3a4f19101cc2e08c19e3f106146553d5a37e9868165e0a0da6c6777d343039fa586a361e1d40817b58e49cb42da61d367127b6e086dc4077415a147ed8306282b6b5dd7a10987bc19292a8e4f42bac01747de553098c9526b8ccc055aa3907c0b2e3a93b226fb255443449c443bbb33a56e8a8ea8efbeaad15166747def67006188edf277c5d7d230706f882b92a8e660d1359e9432912197f33f8f1b9b73407f72ef776815c256ced8a30812c8a78d71e5aaa1ef22ec538b8c53061d6f43591b703312e7ffd05ca25796e436180d78371b730d89994a4ba407d0db6e816d1057f4f8bff090f0b52e18363cfe93b9d028ccc1e0357d2135a3ea09746520df6a8cc38b896f35960fd7d3b775427378588032efa1c47bbf624763d4478ec28cbec600b9bcc4afa0a5f3b78646ce8b5cb1e5edf4193b8c609d9931099de9c4276005ec820fa5dba4b0287dd1ab5de9f1ba2adc8b80ddef73ef20649aac23faaa17f355b689c943d466c2e1327d963308dc5cac734a6b5540b152ff3dea7b72d5dcd3782c24cd720cb33b6e4842cdca79e843e2f6952ede7b80ee523dfb581edc43b4ca727acd821de7d15ca7d262e812e33700e457af396fdb808af10692cef0c7c95a4c6d6b2b2fd6fc88c1c17b87b5e226d7159102eef4297051aa56b796e9aaca57488cb044d968846c78a166e2304b8cfe22cb05a506a1410caad4daeb866784676630351128246c012678c09a50a7105cfe22e6d27319f4bad41e1577c441afdf9712465bc9561a37e97a58fdc938088b4694f4026484dbce640dd934c155338c564b32f1c0966889512c10e3b6db9e9d01823bd2e43c7e766642ed9b51042417d149f9ff1b5913dafaaf8a8f15ba9a00d1c4a1babdda522b883d3c86ed5a293a1aee7a3386de37e5c17e139b8363410f2d4138719d530f9735bb14445de679688af75a377e6a441beff5c0d67ebc4f989be5023852d8f5d4ed477870b401d54c4af296b7cecd449a2769aa04dfc89939598d90bb280294c0bcc667878a2fcbf3923c0f2de0fae2a14fbf6b99fc53cc10b3ccb920288992dee330e22e4d12f344386716f0aedd58d79167e02155c66396f07d9607a0ca22f48ed8799dc8125d912db0b2beb4254cf6063c337568ce54fd2ed034e114293ccf562ea4cd0219905c9de74505fe0985e67f3ceaa260c84003509138d522985bdd11e43204c0d130a3221f9a0ad70c9339df585c0632816bc75e62d4ce0d5be6c152822602af4873750caeca0abfdc3d76058dba1686df11027295ea88073efc91d3e4ed9dcf805ccd6eaa6b8596129c09c99991b415d31f7afb298ac961dae38db948cbb2e723b79caca33de16d41ee7080058780533e1b514ac2aa753e261ba04bbc1da88b490e99a170698c9bc0004f66cb54135da05c28d3252c4633e040e3229209dc451cb79cb51c5c9f8f7d3462ce20193f15597a6826ebfe804900bb5a0b4564cc08381afff0ffd9be6111de6fd07641e299be12b9e5228caf2c1d67e07065d350568870e46dc8a9887be4818562db26ce74f1ed169f4a1d5a4bd9a8a892e8c79b2538ccf0b3284d70c83af316f75681aa580ebd1170ebae87884b457267675be453e442538f2056134693a74de8003745040afc0182ecc655237ac8159d0ff0eff5ac319bd8a69a9e63041d805bf4c9229729ba02d9a7200cde8fcd9f4900bea5ae26a4c5607d3584b8e73b805dcbac088c8986e00a630060ad06a66846d5de2c1b9406d0bc611905bbedafd2f931b7eb8e0709f3ba54588f2c3bba8db9191b87b0d6dc60d726af42c696954b445deb60f81354da53993943e5d00bf4b71b005585244c1ae4aff2da446fc18545903ce558466be1b9cd42d21de098050e08dc2b44a1e0d4e857b672532e99b66dee00010b060747cc5e1249acfc8d632158c4aafcd531e2c624d6df5a69ed6dcd567106e149e7ab27ac658e22f59044b095c952063422ee54f36e9d7c7b361773ef88a319cabb7701d11f7033e1ffe4bb2ec1f3587fb7adff26a085b7eef976a40cc39c51a887851ade541af6298b309bc78a417540a81b1d56ff7f9020a22c9bcaee885addf167e0c6b3b1e8e4dc2dc0dd5897069ec8bb690adde5b2bbeb0bb73a2e1932dbdb642b922ba0b073637ef885896bcb4ef7a4b648fc925ff5b0eaa378a3b1f5305c0679d8d4c6640306a13acd98f7462b2b2f72eb5ee65f95ebf3b3dce4a059d8b62bcbba53bedfc978a90d62dee9d91a1e19dd67f8261a19963e1274e6247950223f76f7f9eca3bdec15ce0425f55d59c6ef9e5626abc010d0bc554dd95392843586429412c572f65f27846cf0bc31f9f830895fa4785cd29825f729bdf89f9d472bbf24643c2a6fd4a5df13c8f08600b8da0cb0ee043ff35f6c18509640dd1230285f96df59eba61238df3e63ffba9a0df363dc61350b8954588f1486b638054ef53e00ff8d720b055150b3417f2ade2c9d7cfa7d8e8b7cc1439645fe5b94b5ecc121814b6fe50d2ec1697de87dffd315d4cec1125998b2b2b3b01607841920a08e174e3c0192fa0a7d015326de9b5a136ab3f066c086cdecc2a8f3b3fc2ce9678f5c18740db05d39e8174dc929536b8ecd4413266f8c4d9e1f2385034236704cfbb3a27b87a7ca6fefaa8cd09c698f1d0bb22eb7d5e323434ee546dfbbc1346aa3562abf24f9de22d5f860b8ffdbb37e11099027b65803bf22c4b3ee9f6dafdbd93e6ac866d9922b9cf5bc8fb8175dd3dee5eeb3f2eaee5f7175ff5d71bbfbae7475ff57baba7d57b6ddbc2b55dd74daedafeebe2b6ef7df155777ff4a57f7ef4ab7db77e5abdbbf92d59d7765eda67ed78b61f7cbbdcbace213704a08980cef696aa7183dfbb4d003d9e73fadf1b06cd7b87da1c2b82661b94232dd246633c11f081e7731854ccf71f08a7f7a75c532a777da20f174e2c26a27b41a4ea58e1aad66bc5beae445ad5ba7452c418b5da6a847a056df5578900356eaeb932be1bd081625bd7414aa3207a664cf4e86b54b0af93c227b2629868dff78069e95fd1f4c3fdd0105820e8b4f6a1d76d9b0f0837cc78b0267523e740713776e2639d1735164d162d6d3ed9343b983e04d1aab2cff955fe2e2bea54a2894831e82a468f27cb8d494af41e0902d989bda8325b12b97e1997d3bcd4b9b6e83c25f86d0475297b385046ab10c3e51a1d0681ca05f199d541b5ce36784f0c6724c2b2ac3f345c2ef89b85ca4268bd06d9b509a56c830a3b4238bedad563a4b388bcbc93e011236de7715d3e30fa399b214e8628de30f3e1a3d1a49af95dfe09d2b3b3cce66d3963a983ea8284180fca11999aec6d38d980977d166fc13199f5e0fa9816a68820e35201e2fa0d4604e6450a04ed197c59c45b9157392f120b0eae181753b3d0cdf2cd989c749af2494e5f7642611b8d07fbe9525c15036d38a3be775784d4b1ebf335cc3de3a78cb39756309388b1239595d46ed88e4603e9ae3e6290242a5ddc290cd7577d546da1acedf9087472a57c45c372c952e3e558e379530a4f76234d9b736fe9be6a0c1593095245db2c059308e5a674b2ab9146fba6aa5eacd3675be25420e5308f4b3ad650409b31ade7d9355e074ccd013f4334f288e2102772bdfee3d10c174abc3aaadbedbd9517fdaf17e6d4674babce9c0d5605408b2d4dae71ff7be0bde4ebbadd938421dd355c25a1f4281ecc31ba2aa6c8339ec6bbdbdcbd426931f64717e157fcae5032af1087e71157a1c1baeeccda469f78a0e5f74cd59730f40e4823bb70fba8217ed36108cfa2e9e288ad39e16c58555397285a6d1d2dac6dcc4c959c7a982376598348a99004202fa130d5cbf47e3ebee8d30362fec15e1720a161aceee6938db95a3721d0d63f52e8bdb766200dc13c72b950d5ee56e27de36796e97edf082fea537b52354b3cce15468736c5cb197491f98908a78d12e206f66c1a1ac87e1af09905e442e0392bc0c6c628923ce54850ba4e0833a20457ead1055effeface2c913cc12eb605eda9c070a0c4637324eaee356de4a47020b38168d1bf50b38244d536d0aefab3bea492d7cd988efb86180ac272829f94ec08089cd980fe398ba7aef9d51484be89b2b7d523e8f05f0f8666f5607463d920e8fbb8f5b87977027e45dc6b4eea7de42676bfe1b9758a9aa1b6d5268642df381154a764f5cb3ed9388f0bac365b0088f644fd06701a3b058c0d45b17eba705b885e0f99750b2b58af1654690ff4d618d078dab19851d8d0a3e04be486b126533d97485716cd455123a3277150c61a4b6ce816fec2b2ca41007eea52fcc520949a6bdbc140b012fa1866e8d63c09d386e3886b394be52f0a80b54c0b4065a8e16041566444877b4c9a1e5e5456ba17496aa4131f0aff9366618922337eeb11a6775e9b67d4a9ab30054db88444a5eed3e5173c5a701d7525b0e19da1f9ba15e5dbb33e22ee7aba9648c3e202be9d373778dea8170c2d54a05c09a099f97719ce4b6b11b62480819830bac3d71a376deb5047f77f6d6c06f1b16087ba876efec0e7fea1e51cdb6cfd506cb00e62187832150a7e8271f3a0d549778502706401025a2b1476c22dc0ee82000f6bc085a841a23c3b47d50779bbdabf2e55003dcb70de2dbc6a07f08affeeeb4623ca39c040882e7c3533a49e102e0205817a00ef691f8f0b3b11b46b950977617adaf49d0a6053d30c91fa0560e30dff3532bfc71633b84643a2897e35f20c9c0527181963589e8fc39b8577ba33a800275028225ddf716a7d025f034c0b07dace09fd6a30eb3bd86277aa3aa2fc96ed6b7e1cdbc3bcfc34b0b1a554911de819ea81c6fc3afa2bd3c5ee281cf5443ebd931203a00516d06ba95ec1fe9ef2b89fc0da8f7cab803e921e856f22e815d213f96601bd487a0bdf88a09ba437807bb9d3cbcd7ae593db47dc28441759af829b48740beb15715b884e643d05b722d14dd633ef4684f51ab97ae1aa99ffc9852129d62855975086c7210a926a1e8f5575d0c7a0e3537c5d379e4c8fa28d8c12628b819b5c937ae7ad501b591e3a2f54529334a86cf837e8593ad1064ec1688849bfc1f9fe1bcf5024209d6f2beab20e19539084e80a1fa95c443f05aef8939d761616f3d71865ab892dd93d6c8fe5d464195f379b7569a2b4f60293455284b3010b0d8c4c7fe4d509a97f947c53879d297d85194d9d67a47a2f615fc52a179a49b3fb4cc23f9d794cc059611d5411848a4350b538fd8da67f5a1e9ed4808d1838021ad88636850e8aa20355027be0c5c98ae86c2ee699b8cf6cae0354c3f5fa423590bea300918146361fd82442e00b93a758fb0ad69465733183228ea964ead9c4d8a13a283db1d3eba7929d103144997394fc0a721a2a6edeebc2b288e6aa1539a2d8f6429dac95bb38b77968e6834a286040d42e3f618c1bca98c3adb2d69e28dc4e9dc1badb4296f189f691d0be56c694979b22b3c230c9f897fb20e31377d85703a50ec85a9d9149ca822c73491cc10811e7358fbaf07eb6a154d490d38604dd1ba2eb2e7a1865646c512303b1f0305152fe4a41ab467fae0abc0aa1744cb29d1de03328f0e4751f9d47b26974a39cc1de5d4935adc7da1c459ab3ffbe61ebf518dbd8684efa41b9828b9c68d6e5e83af8cf85aaa086391305b03ace5891b8ee3cadca9b87c1b41deb485bb596f55658656d2bacb2b6352b592daba2def98b6e582632cc28e3993295714619cb3c13c984d5cc839086fb9c11463d9cb6375f9afd97ae3f7c71b540974ab5c117f125beacc4265a93bf7c02041253606336547a9a54b4cd70f50a4e2178d1251c510baba164f1418470b3971873a439c2f8a5867ead0baaea13bd05dc6d45989737c1e19852f3a9844e95ec1ffb6189c5022f36ced8ecd1abe4447ffaacfa0fe15458e032bdf057694631fe94910f9fa339f22861c893b040d0ff6bebf736c51ba4bda8b5c62e97f1caea34d604d6a4419bf037d9c1b2196e854ca28899a6e286c1b03e507026d256396c472497136d3c738702fb33f70b4d62dba134b6d95b75185da8ab25ec0f91e8c86aec966c7c17b870c2f583304dfae429b2403fb059bce4c3dfca47972bf231f1174cf5ced3b4895988b5ef42f75dabf62584017fe0747e76b67e9e27534241c04f73b8251470fffc94fd7af2fdaa13594241e6a73d33436c2f4e2a23029e818adf8f19b3f2ffeb22690905223f3e9f9f3ca39fe4214a2860fd734f5c0905999f3cc11f9eb4dfddd99750b0f2ebcf430905af5f9ecddf9d85dff3c8945020fe51777cf859b9cc934e1774807541775fde20972019564ac85bfdf622fc8d9dfde9b75042b5ebefb5d24bc80bfeaa02fcc1a6ffddeebe84eab5dfae3e4ac87bfd4669feddb15ce02c218c7336ecfe9d1c1bf5f5b8ac884c440fa5a2a451b4c2bd957d45ff49e18ef3e0e0c339306304675342efb2e567ab65a780c58a4ac8c3033edbd743879afcde21a881f5d920d8fd466876ae1a7edab57fb1f04290428d36175a50c554cdd4b55e554a2c21915c8596b74e9ac8e225ae5ce0dedc0ed271542f9cee7f42d869edab194ce196c4cac89e2211debb24f94169ddeeaba4731f92577e209cfd73b13e52892d40472c0d158b45066e461730f9d1e487b20a2c582163590684228fee87ac271823028a342e2a714d266242a0a60e3f22235ca0302c03b456433af4a1eb79b4809dc32674ef6f516faf4a917920bb931931adade54c096d3c086394a448fe095593eab6c37774e92544981fc70f6d4df82a5d1507c37c4bfc544608217409a80397ca1f2134c791a901731b6a39fcdd337a1054c83ad97be8b04a24bf983ff0fda93aec1d0aed35d8eb410cb2c7b6c211424c2910b99dc7b097b45c30017307c5b36901348c7ff15cceb14212c01d582b84f547fe4be28448664b9a63d14a5850a344bc0e39ecb5a190107a37945088ac11a510d1c664aeff0f8a929986fbd690f45d83eb0ffcb8a371e3fa14e449bde841a241dad3f5b70e59f30e46de40840179a3d6505d31c7ed5dd5db7f42f3a584c078f8e647ca285353fd5105593b48790dffb8fddb2eac1ccb47a9fa4f5d9958a7b8e4c15d896b2115cc60ba11e4739e43d90c019fdc642064c5af93fc2f9bdd4d2fe3bd366329d3379bb4a4089c783b641acfddfdd82e68342d891523ddf956c6af11c4c38aa824620cf21fcd0864326047a63183ede239354ffa8fe1bc56578ab8552adcd75619a325f1fe4c8eb37c04ff12e60071f40a6512f5c765ce68fd30520eab78949fb26adba11e92c0be70b39ce217f760a3e8de40d87f8e45c79fbe289190038ffc0c82ab30d71cf44431aabc8afa32a8c1dd6ea9241f5ac7a37d81dbd5a14713ad43e7d7311ce55a2ecf013db806c0928401344c5348a63a5edf5c767e009ff19e516d8c2b0ea3cdd3bdfe75b6e28a3d1528df817fe184c9185ddb7ddc6ceff64891f796ccc834a02eeb84197f3281e056257396e2e2fc6ddf7a49416ec0fc886f8af6fa5e501119deb9ac2172df70cde43eb1aec6b39bc587418567679c2068e24442b82078318542f74669d7b08a259539aa63d55be5424f13a5476a956b080d708350924764b5034ab70555e55d9b92fc53b6b35f92563624c8760ba9b2ab46d86066f991cf3b17fd9b05e7c68cccbfc2ee9dc238c510fe79d6f426cd9fc1674e3cd790ba0adfb1a439550b57f19f4bf0e43a561a33e4bc28bba6766939ec79b96e5d98e43f89ee3a5d26676782de95b6aa9056af8f00b55add6546d6e065f711847478577a475555cca5a1462527daf69dd86a116f4b3eb8fe9a18355870602502f25d194dcf063110b583edc081f4f3d1d9c175e4e537d7a210c6f93659fdb24a25368d33e476d985a740a55eff3e6505c30519fc861ff0697193436cdd7fa8fa8f0c18f2af2b608de141f65b01d4198f0da6d61439bbc6cea73a108746a2a6ac7685a16bd3c181db6bcac5579b98c808546dab4cc90fdfbb2c445d6d8bf182c8141401d098b02c47e62b53f97bd9fe541c571c06f22db58c0c68b5602b110d660d9810740289896c17bccf920df37e1fec3553a68a0c57d69c8ccf34461fc4bc91960a945e0e50ab29f5cc34392c3908668cf2949ad690b793610d41471a00be632c817c8900bf6aea7d6d57ad3d0333377b281d805f17cb9ebba1ccc93e48f00b12787bcad954599b14b5fe8b72fe54ec9d9614b4175e44d530673f972ca17b009db380b382bc26e25371ce7ad553b8943fddf1cda9613eee4ea92e45a7911b8df23b3afeb4a27755c0f76f409903ba1495bb14125394c4bbcde68fa29481e0d619ddf29aed6520869550841b0c596104aa809d0ed701a52baf14296a1420b1669ce01a571a1e4282c8f039c5d46ef9cab468d1fe9f4ab483cf73f1c7002c5ee5300b9d967c9623ec1e2233d79bc2fe17d0f73268f7b2efe8c39497b22c55c41a71ccdbcd87bb1fef1505d50781f95966ecbe5f9f26e895b6d952f0925a9ffa9c968e1d4edc8994388bd6a8f5731c339eb44e29d985e127764345952e985e23a6f374352a2fd7134faa96cd541a1a508fa6b023b78b9e056aedca5f46320a913112980471de7e0338d3148a0262ae7a7a29015c6b7fe947f4ab2a0a2476c2bd9310fe812de304af77a1f67b1be288bd0bba127288fa2bbcd3cc7536ebb290588000235f5e9ae61dd2da4836355c0980717c79a5bbebe188ca5e546f5c43f350499f5dab40f2681ea086a50de91240d2a29e9cb02b8be510057c8ef57fd42a97a7e56ac98f6e06fb1904ae32ee815b1551090f40658d6c432bed57546a19e38182fceb653a13aa7582a2730e287d94dea4f9288327a3a0a1c12ed4f4138054f51bbcdeb950d4d4d270f1477ea3d412b375c01c53f8616e88671476c363ea461c7cfb499a13766420c129e2a85ad5bc705f1be190b7b6f6b48cf74a2ca8bdd879cf4035fdb470835b4e70e1225dd9e879b7e399d51da32003ce81d0b00a2cc0d1602ef110eab711867eff73f41006d15534a037a5442232c87f5e11c8572c59b6f35fa5bb39b1d7148ce71f84677e4776c3b2106db591e951143ec2cad498282e8e74de54162aefccdf038a69c4fbd18dcbe38d58f15a0de06b84eb2da31cb7671c1c6034a4ecc7e04d4ac23f33b0910781531a9428cd44ef091f21e1fa72d3d4b62fd9ed9e6efaa09884fdf4e6a84721bb9b4ea33b6aa3693115659c33751decdbff545515ae67c05f4669fcaf25b920ad32bf3baca712e9d1906ddd41871e7092e040c54ccb6684e6f00b2b890b560eb4b9e3eda0e362e8bd8d8a87a88333f03b9f63c7d66190a38b6cc6508eae2c9fd8ef9d640762f8f24b3647eb1fac1f5a628feebcab59190767acbec9b34322ec37f325ee1279229a4584877d4704d967a8b897a210aa9a38cbe89993998aca7b31eab6929b361a8d3083686cd8267a89c8b5189250bd578b7d99a23f7a9f97551c19f175183bf349e42809999937a9a1bde3edc67fc88534b31dad837a1dd5d1f3265c21b3ef3366b8b68294f55db5462d64becc065caf4c385d1b9bc856f6789be68d7d2d40c7155620ed40d57629a8716ece260b08875c7fcb90f14038298f5e9a743eb69b730c6d1d648107293a6a32e43d5103ec040135a367b74a76287d284e439e4f17fc3924bb9f2f745bcfa1f27741c07de4e926905215966429eafb5b3de44aafd3917afd04e971697f16d3a6dc19cc0cf98d21ae00320c9139299287a37955afa7d060e273d8dedabffc04d83c2879fce0229d0a6a28bc809051da198118d26558d7821c2af465550429a1dbc75f899ea0e0cc9cd714a73fbc86c8d3527c7894e14ee1c34bf6bdb6be9b281aa8fee1a6adb29a8e002d97ac84fce063f90cdc084d99e3cebc18d3ec37705f9dc738d05c36b4074f8146ccafe48eb661a31d61079e32c9876fea271d93cc12bec6b2a9fd5f1172706cc66d24ba0f6ac9ad4484a1f3862eb91c2036edac8855f845348309a356dbd1d6e65ba77334c764dcef54ef2307ba6e1e7dcca1175865528c21813134cbfbf3e58b9608e7ed810fec92ed96075cef555defd07b3c199b900686df425d32614ee03aeb8bcd976c78f638f259024a3bdeb96026a33dec93c17a30efd78787d3dc62783010a4ac43f4eddfd3a7ee58fbcbe60b0c55344ac8298bf148ab5cd06ae82672902529ceb0b4e2c6997649331d92705c0be368c08396a54eb2b6e62b2c46aa9694964db751fd1a4fd2172624a7cc5e117f76c32fa1a62faa25a550b953a7e67df57a9b4f65809b21457012a6a8ed6eb89125ef47da878135f2a1b1f3ac6691665728e158dd94230b31b7f118250ae235f871ed7f8868188e37629c8a8f93034d91abf26d5788fc8a22c91dc0e2ab829d800b0915da43bfb512956bed8cb34191acfa4d68c2ef7543a6ab19beef1082fd606924a58f16a2a9f0106c39c903e1bf4c521f85aad9e6af7f0749514a7da18729a52694ffdf391da3594d61dd1979a25616bc3b24e9a64a95954ecc03ac8a66f00e1279cd29970061c66b99bbcc449b1694c32d45f7e32d1dceb8d7f520445a1c25e28ec577ff83ef0c99871cc18d948b1789bf534012fd1a27458ae0892b1c8cb2ae06abe28e94f74e133585947c57abc25072173e7990612151ca123d854fd42cd3e0719491371cb5255396805a83079dc41dbc280e1c3080ed8f886fcb323c0c3befa15227142a24c466e458085f5a97328fd89a3bf70b2f0795d57bde9cba34cc5ccbd4aa31181b862842627c6cf9eb3e22b8b6f99584517e1a0912de8b2013adc46aee57483f5b85cfdd5931aac32240378eca54fd29951031c46dc3fb84f4778d5f2e06c0fae481b2ebdf9e82d710cbf9f11c35153dfde31317aaa07af4b7a677d0c6f6508c01ad907612c8df81d21fc74989726a13acc24f0019bb412f81d15bf8dddf17d58f8371a2a32ec60fa10055b9f7f943b63398d08459da2070294770b328efbe2ff0035b0b36c84b176c54f7a7fa333a17349ddba2e28ba5be27d29569d9c3c22b599873939a193891f59ce45d632e820ed9be51c97af46d8b8134b7f3979b87f448abb981e56e3ea187d69556a4e58780bb30c435c01b2b2286f7a0d7008b7f3854fc587459a9ea3806b394e194bee3fc4223893cbd8a328d79845aeadedb4cfec415c6a682c1f6e07c7e94fed20f396414544e2c0a6aa9d41552d1d9b3920b9ab41ffae4e17a97a8010989a3149c67bd2cdf55428ae8d135efb49e7350d1381a4fe65a6998efd4f0af86e49e3b7fbf178582c1e8c8fc480ee630850944aee2c0021d12829d0830970b9a95a7f2c2aa616db962dc75f0ef0dbe8f8a8ff5bfb929d5f443f5624afdc0b945ebb1038c1a8c71f1ede7ec009f5cb54e3b7c52b4ed25530b436c71a95e86c3a5207ab3b0f2af3c1bcf8670ec29b6bae89dc5e4d6a97edca3e1dd8b06acaf0aece863cb426cffc41cbf02dcd16960ae97a6a50174aee4ecc5387b097cd739ee37062f3558d1433a9d3c4876096435c26e79f5ad1eb4b0c827a9c152956a80a3b0d1468ae63d503d069e5259ff799789dc285bef19c24d3e6ab0506584be0ac9846a87913acc21e023b3346a2419787f14fad1b400bc9c7742031000291f9a0d6dc8acbd0653bbdb6b16616466e88fe1de0070179dd852ffc9cf4a0e95837c3354a898148ac20a3d650ae9db1a0a59c2cdafdcb583c19e038abafd5308cb26fd58af42801b21bbba0862028aa2497a670a693eeaae5f3eb85e2ad81f5abe110fea073a255735ad4cf5fabc880e6c5ca8e99ab7730661ed0930cff057db39bee3b3579ad62ba72991010f07781b9c7c6a517e7426257b4caa2982a2ab9199ff5068fc48060d45eb27f40c3438d543c3ace01fa041287a18a619819d31a356b3d5d6195b5d01fe615902eb421d1bb1c71676e01712d8833aae320a0d8823c0d7d661c60297b64501e6a1275de9f2680fe77e223f5cbe609bdc019f826012da4bfd6b05bb2a131c63b883bfec81cbe23c6bbc853238bcdf77a6c4b018cc849cb4241c40aae2c439fa31e819834a7c56e733c087d279c9ce9c83baef9d518b32cea75e2f457efe81bf9fe02e372d45aab35e80f6f17e52d70577f7d35d749f74962110eb01cbfbbab444f0d7836524cc609fcc022f1a301e995e44031d4f195b84ebe6faef584cb9bad61a85cdfeb5253aa3930856bba5fc27eceb5658880a93cc35f81727e740aa128971771ccc64bd8071cfe65025a05121432b4c6060ad7034b61f1be3cc1acca3529b15275103eb33cb4dc99c8db3a86babd56a5e9ee0b9f4543dfc8e42b1207a2873e06d5d125154091aa595b6a0732cea38469c28d2423c73e8ad2c9c0b740aa0259009bb8387ba9417b0e5e49b29c4c6105596e1d2ddb2e37775686666d09cf630057eb1a8fd2ee80cd91c47d85c772915a86e0c6cb1969e3f2ce4455afc8a2ef8c2506916e8b888919bffbf9288d5b45e746134093098069663278622b1e42f17f98a063b5d0b8a1b5f205e4a7628666c07214937f0af9e0d9a86a98c113fe8583d9878b6247dd31f72c889f77a61a64393ae9f56398a667cb958e64ba59e190f901bf102fd79693367d8caceeb7238e4fe57e4f88508b67268750c856ae53c1202b0992e526b83423fb88a25707e729f604f8fda3cec651c4bbef4f6674b2754b6280913ee0d8ea26d4e79a331ec73d88912253fe7324bb77c59259ce7865b6d11c503291944b405d9233e9f6c57c2d7aeab6b4df9bf48d8628541120eff36c246894da0be3a3cd0ea00ff58910b49ca715c97e0cbbb131ba718bc6fc6e8149a1bd8886f048a41d61039b94526104d1e2bca32d0f2e4095b2de1695b4f9b4ea1a221ff80794f8f7c64e3ad420c44ad527a2bfc072bcc83168ac6416f2c5a7d4f3d11bb644bdde523e54feabee3688683c1be9c2333be8711e6136608a2f1e4c2e5c5b1c2cc80b3b0d111fc1921d828a196fd2df2d0b8c2c5a2e272e7b074f20c4a6015ab8000f8cf5e5e5285842dd001eb7c9132e4ae10f7b81fdc62eab2c731c3610e3ed29601531c58aaff8f85fd71df491970805b2723a36a966a047c27e1df10ec820700c48766eb4555408da69009bb946a98e44422024e8696f856e4d8aab374ea65f9ad3e82e00e3d5b549cc5993992f8788117abcf9a433f7601f97a0dac4b3440b90574b9c8de9365b77e7d3478c34edb81d6b4eb34b90907a31428415d932c5df717dc6905e3456680e8adcc1e8cb012320b210ce589692be72db18e24fb16dd7f4dc649352b2da79118392f5e2df25cb8a9046b803cc96c3145b38ccb784e4aa6f1a82805d0df2bf2af94447c12a6585e04c695efa21e2581d24e0d98bee684d314f2e06c3636e98fd90758e69bf852b550ca39d7bf8d4997238b6c022db6453507f00f0d92cae5059dcf301b4a8dbc8e4ce31b08bd63e0539010d2e5d1c55a34bfd252f9c83de6dfe823546b8be01be6e4ace2a8875da6107074e58e522c3e2f8d16c556aa1b6cf2b80757e44c1003dd6fea80eb13c98ebfe3c4f55ead7839ee8b1d0e683a133e8f52c70fe25b0be600f54a094a81601049f1b3b1f316cc42b5e9f6e82cdace0956e8ff9b08eac14d88e46e02e96cab0b2cb21fb8572d77748e9f35cab2c6b1151c797335090e9f56dc43a9827e148ad4c0d5b45bbdac023d027141fc10fafd864a9b62b424ea1cb7cc1b668759cee599b9b43ebdf48723cda1b55a85a3706a4f291313cf22b9c38f6488016e8eeaf7889f2caff51d86a029c7bb5287fe2306d2e02b4ac3b6169dd303d767720f658e93e72089db5c971d58fa2e27b661f5c091456cb90481bc8a3922734bf7eed769a8e1a25d916fb55c434cebf1a4c59924425b33c0bfa310bcdb987f9788473fec1c4b04aa865728b931b69222073824a14c100ed89c28364ca635941bc5a5bdd8a20d3387e631c4ae30d99d0c4c2715eb3510d03078572d6b9fc9d39249c83fa7ed4f3404997cddb4d3fc01b45d05d4ab88772dd0b5a2fc20b69838515fa09900f6f905a788c7f70c54d1fdebebcec163eb3ac9848150b003c340a2016a62c83375bd855bd6a234384db59480b07109a9e492219695400ed02fa319d98eee8e5944dfb438f25e1728f08b896a52276ba92d1ab897450db0bd342a65df5e936c434e19684246e64dd30a49bf503d90f895a53218dc99b66520637f32ca1e81741d864ffd837b55a0fc4b0081a43433be1f86edc676ad70844026130d766a5c41c94f7b5a763f3fed4440bec61a031eadc125fc44f19a728c6b246ddaa6812309183a54827f6619210229ef73ee8ec1e3e43a644ccbdc219ad4fce0e577830311b5d64e16ba2df96089f6e4f5b1957d829cd90432558b20ad538b03bf1232b426443da65da61f9e943e95f7719dfc674dc9529a8b4eda0a8d15a95f4e5929922084d8a64f0e76e790fbd85dfcc9d8c3276bb60ed9435b52f5be9334b060d23fcd55ccf111cee78868d6fc3327d891f13c34e69b408b9ee4489a1d11cfdbb709280277a12d5bddad8e7e1d744120c47d0b3dac9b20bb237e869ed35df5fade30beb61fba29dd43679d78eb79aa6832b10a9f0ff37ca76b62f862ad3f202cdcf61bd8aa9429af8aefb7f9b33f27a07cc0023b673e163e87ab894c81f9382cdbbb27d5504702e413531e526c6030260e13a45389fb083b8b3322a933beae339b28136ca6a818658b1c408175924706d5ad47ba3d72a9cd775fade5855af91063ce482d41bb5000a4df9f3805ab8b32bce9e96c237f9952851ce0139f46167925cd119bc9dac716c43e5d401c8ad8facbc5b896e11e0d2b27ec9c0c8832e211083bd8c359e574cb830430bfdf510ba0aadb3b88351a1a96f758103f019c432fc55beb883b44da7a80aa63fbbb95fd5959779707451ddba06852a14945469a86eca8dc82e00be451777a2128e0acbf19f087b93cfe68451ef041512562a35d0b2d0f9b4fb62d223d94272150689610039c320f64eff4b4c60d3376a5d6cf77e7e3d91553c198075893eb8133d44375077497363f14bf8cd1c09b98f1888ce654235bdcac830b03ddc57f560344eb3c802130fcc70a18b9da61b727ee31bfb379257fa1cfd51eb878e0d54e3aabed595f4261712ec460dffcc56238185a686f95062758b621c4085a923f7518f1e49d508de6113e5c37898230db1100a948b905253cb07706d0fb6ddd4061a40cc60b69ee01cddd2952f04d0b97dc24ee20d1467d32dd20b4863e3283a66c80c8a4ee0df07606429914801836d36daa145009519798ae5de4ea109886016a4b16f45cb997cbc306bda313752f66dfc4d3dd6ac8cd5b799a2ae8f87d4912cf02c3da68187f9752e5d3b3277e3c12a75ed65b34da5afcf8dba7e2a1b8c75f282568842a79bbd2ea410f977cf7e32a48c04a77755b2e1f0fab75d8aa8a20bc25e434b80dacabc4e141627083b02fa183b6c09061bce41ce95e6ec27d1628bf0c490b2cc3a50f2009b12a46531d0277d545b4fa8fb7183a6997d3b9867f040b2d20da3b7a5deb80e686119e203e39a5e2190bb9ceea9a7b53340994c93f9ed743785ed1d494efbc9744b9409cc02a3f1f5ed1f016046080b6d06d1a80641cea62609755140e00b23e18ea61b43cc30429d6ba80bf170c2e345f97824225846f51186fc785affc0e2f754a6c34d660e406e0171cedd786a9f070ac9832fe969b459f5e8c2f7b8f11c315a1a049e3cafd51a7ef19e6d96212b6048e59799ca900b29e204643eb185d9cfc5c86a085473dec63c7db524414656eec6f4445de78a032e6a28b6bca1ac3c7f563a4addf966c34d4602faacbd6167cb6fad1ead3c212801ba6c169c541b8eb05a8aee2631ab11c6aca5bc8729287b9367bf0b979feaf76e4824e3e268a19295765af9beda50b4a3f9cfac87c26a153788f64d0117c2b2120e4eddf567071438218485c4c968a40c31f740563ded7c86b6221f3463ff69a268ae5e8832d8537fd2279cf1676e9a79c1df8dea8a29a679795a9e18f7ce4467b52469b545d276c4555977fc7910cc3f96215c595ed27de3fb1ffb929124a5612b0150ffb911c2a2d59a87ecd7af4d5dbbd63fb0897b9c107b82847d23f7653ecb38b54717444344e874f752805803ed43016d3ac6c9505986f6d39fbd392e0009158b69b3ec0a1bf20b7611fb8a42b2efb53a8d6c04169e5846a082be4d8cbe2024ef24db998ee0c923df1f5e8b712970f0bd108ad5a7ebd1ea757c301f760d94ab83e99975f793d3b89e09c6e9024d36752520dff8a04ad0954a3e2929fabb52596d9a3cc6b837ce0859a1f67f43420dc22a99843288fffd7c20cb20e2df83e0e709cd9bf9338c0e703e8cd4a5f7a400f59d072a85b810bc8e161fa30b054e3d86ed18bddb0fb26e6248ffcf31b520627b56344557c81cb9fdd85ca19a85fe8da47623e30965d1ec61faf7968df8d9ed7ed1d2d5718d99cd52738c60a52f06f6b21d1018034ffe5d111f04266f08be1d1c3559e818edd76ae855fb5a516e28c95a23764df4456676cd45e2f00b041fadf1eb1257ef97f2bfc147908fb2ce1849e58fedb60877394301defa14b90e3c9f32ec47d2f718ac1ebaa3d1fe241eb4e6f29eb9b74fe9d0e95119580082906851e4fd085a957f42ba365a20e8ecc2c623fb614a2b51cc2efe6e15cf8afd6fedb396e8fc012e7c2e4102afdbfbeb6e737fec0c97ba5e38a9e5889d2f106a04f8e4db4d6a004df15f25410006c19665e0d004a9776373d39799799be3621eda1188d84cb0a64b96bda01578e89d27899d1278c273aeea961de0e0c954600ea4203509edb65843afbe8f2b44d501779942b7cfe92292b20875b9648e3d7f070435fa7687bc8fc12c3577bf5bd4b49d6fb6f1b7a5787aa136239f6d8e690b12f6cc7b5122bebd81778ef044b507ba1c4bafbe81dea8246dd378b831933477a3821ec32030239ec5b025203371777274d2fec9d1a874b1371be02975f2abe745ef43c183b958154a00fbd55ec25e0799c68aec1f2b0dbd10520594e45545cca10e78c49deb689e79aefcaaed2c0ef3fb7e20e465d79a481d516f721dce63828c49cfb7c6921231e60f4385d63bd826e05257d19470ad7ccb528ec34b40ea23a8cfaee9063cf3c17a366bdb4a0351ef6e0ae174411cb88f95cbd4b4fde120da60af6d925f4fe5c3d8579ba604834654c2030adc2c959a4ffce2bb75077845828982b13408ecbff8640c695dad50607f92f5a0ee437c1a350d1e7bf21319f03c1351c070dbc433fe4f1b125bc5299d0e11f9412e6654e16efced0a13e008d721cf8c179ae3584cad401bcf01bd31f622303d84f9d019ef17c0300108b645d7df9ceaf7b69eaef274a890e770f1e0f094d6efe8f6c701375b40e16946c1d9819d7fe8115aebeea28b2d3b6b92a5df216df6af713f0387c763ec5066344ecd2056ae13366b1c7cd5880473aa5465d1349eb511fb9311516af31f034f990d44d78749cd291fe30fd0a4d9c8defbfc90b89e5c02875073a08054dac1979412a8d81ddf87fa6b6ffe8a9bca206fe47c12d2bfef8240c81ffe18c79eec2b63be1d032b2e463891b0948052cf99dd2301548f43a844e363ea304442216d2a5755f5d44cf2f6c405aaede6402236f1849f864b02a5c34e34b5533910748ccb02c45b6df8a5e1b1df661f7ebd11a7b0e3976e5b0d1718364bab483095e09c7a75a39207920f8b731569bf3faa8edcf27c33f82d1f78d7f88ba01321301d7e4b9df6a020db483a927464efbd507167a1ca5c5b18a45f82082afef46a39b54dc303b84c5dae04388e29c987a5198a1380034b8b97b0c2c5ff61fa0d7422055799cf97bc86479a11530ded45de412e1501f586c79b66b4336c1fd9225f4654cc3f3e56f58827568c1a0a1a10537a167b1bfba15be1faa113da569f444d381bdb04d12a1ada1ebd9c4466f5e28670244062742b7da674e5851d42498e0a8b0d8ede709118ae3f51807ab4ce2e6331149144c8ade919234cffe9805b5ef2145fc2cf1eccfd4fc73a6517f2eca3cad8b9f5120d387c5ae1fe6b86fb4aab2153ab461e6f6fa8a0ed787b9433cc05b90e353ca737a31cd23cce1546b273606f3c49c00c503d81d91ea11d5e3a34fc8d7b1a4848b98f25091068f000dc2e2907fd5e77c06b6e6db715dc2498c72d77ad288a2412c458b97b37cbb3e3212177595f2ce0f2e40176e6a02b7b1dc830a940d136406cfdac8715d4be2d634a156e75df4d1bebb53983aca2144733ba57a427415c2570b38a0f8ae1d6fd7be4cb1b8345cbc5e6d215b229f169f66f17abde4820bd3ebc1e88dfcfe9dd84caf1ba010269d3f7b936e49387f6048e6374b371dd0f9c5182302e04097911e3190dc5a304196033a57cba231e2aba392a68fbf126d9f0810559d95c26cd40b11b290fa2357f94fa7121b58059349848cc8d52953ae33e3896f050fa061f6a3f6ac7a1e43b422d547695951326cf4cb86a78cc4d34d3f62dbeedce8dd7501a75fd15895c83b603abce832cad2860beb6d1e9d184fb131856c7fcc898a24d548136dc4de858a2b16ce3932978224f937dafa8fada5fb48e3d9aa31b0ca76979cfd9dfc406732e982adc412b5d359a7aecfa731ca676b00ced9545331ea112652062078a45a38b839ee0e853ded2cee97b5ee78d517375d6171c11c1906143dc952d20854acd50a9791269ac83ddd2dc635e00eeaaee7d141ffc825801be563c1dccc403a3acd89698650aaefc13c356a328a73462eefae96463515df5122e43eab1a996d668bcbccf6b1639991a50e4d9bbd6c73bf3bf08523b925cc0b0d25f7d066e627b0b621fcd3c62451d66403cebc5c6dbca1de056f8539b5811d95125cad9ca3449a910909cba5d48cd371fb479ea7fff3b93ea5bcd560c11537743f5bad597896f20c11bdab130755bf801badb3c382d4a2a8c3671ae4d855d12cd7bf6134a99eaef3e50dddb65ea8d37eed5028b1ec46f29c3200dd10e67ae8d3adedb841d8d4c740429c77dc92efc8e71d4ccd073de843e8be861d5d2fa147fef563a6765cfa385ca8bf3f3e1f2164a661b771c68e680d18bef4b6ef1e92f2d9bb91d0976e43ca9aedb2004ae7801139f5f152484edcae4523cf1c1c8ce92843ae7f96c4f70deaed26593463152e2d5ad12118d32a5b6a475456e352bc3c8b44e693e9bb253d2ccecf3d4cb1ce60483511119887149358a4a7bec9bb2de08a4ce652b63d820673ecc1cf98ef5d628b4edf8aafdb4fad7b9ba4b03a914ef1e9fd90645a4d3e890a96c2995d01da175d4a1da8b80842d2ca931e257d8392ecfe591615f3dde1968d3d01c3d06f2e0873a072d058fffe4494a88fd1f70a98c94edfe5122238645b574c594f2c1f8d88e85baf14a7f6a711010b5f685560e10a30005ebbf481903a1efe3f552558897478ef1354c9085d335070504b4e66d2c1a4bf38073be72d6fb2e428f4a8df60879d1bda5edd78b89c26fbdf08cf02d0fc35b0a5a236839e8e9527f123f992575ed364befdc76062493c1caa5c4e0fe1c280e00bd6e1fa799874463834b8c038896bab91250847c4d9ad36e70d442c7492a4181152ecf805f60259db84380602f71d03947b7f2c21ea272d9afdb3028d7b125b8a691488ca2540b5d45b7fbde5f03b570474f89d7b52fd460eae48abd451cb6f759c88ef457a5120e725e61fd6f3208ce4441ab680d1e47250ea4eb3bf210fa486e227598d7fe3be338a9423f47c55baa326b052c71ee21ae828b7f9f8baa1f01c6171bb0fc42504ac9c0dc23a0074d095a1ba2f18d50191acf9e77e1e1baa1cc641bfd3bccff8b3f449f83fd975e0843e9f24e23abe8bb2fbf1c6aa7eff6beab79c284e06ce0d53baeb30c6a1dcfd26a42f92d098f2aded751cd072b1023f5f9c9eb6e7ccfaba941098ee8acd48e991d1420508619297290f8c7576107e9b9093ace3ffa50a3bb4d292e3ac34722db535e0385a9ad462d680b35d42deb68ce1b869b3d982317f0292d4d4362759284a0d9a1c043e3142e2980784edbfa340e85bffe84850aa7cf2be0a60dc20398119fae38353e0a7c5eec67b49110a7e8b87c53d0c5bc82614409edfe4242ca2c870f7293f582d355b62505cb96199d6c7b1d77f84d912e415490e32261b7eb6e1a892ef717cb5fc4f289165ae56ffe9dee8f36d1214502e5c96bbb1658fd8f884f3d0d811a14857e2120ab39aa3b68390fd059bb0edcbf7a01b9f084291ba3e961d55cd20fb421bdec242316a9ec7c041c46e85c678731bb424a0efb514f4b764cc91e8fbc67296949df7d1a3957dedd352b0212a08d10cc6dd7a44abb8c5b089c87888a1da58b697c52c98175c68ce30687fbc7e7f9e9d91692e245e2ea8188cbaba8be43fefef561257dfd484c6dd9d24c7b9427cbbbb83ad1eefd922d8eaff057f6cef3e29a02be442fab43e85c6f1b0340284e0329a863606567de6931eb923cce907cc74b4bee190a9f1676d13c94d02d694cac3e6b4ea1a8f77e4a451a752c96e4e12c0ffc6a1159a5074f584945a9d16dddd4b6f2a826397452527860646bc6a81cce465625870db8ab111b3852b603502d76589e164036dffd306033df4ae64eed04de8c1aee4a79aa81c09fad769509f46447d1d002945001968cc64b073c8eacd0e2bc10f22b8275651f5d6359e1398f605a0ef1b1b897ce4020a0c4e665b409a19a5034d59dd6458527f4240037f4ed679117702530b59e1e8b635366b2bcb59b932ec3a046f0b0f14d099636ca57f00340a56056306e451d70528068cd838258e817330feabed18e8993154f2892464efbda5dc324999920cf4054906720695e7d4f3409ed3a8275ea451eee4fa3f0d17d40e82e082e0d920bdbff3748a4b429dc2debf9ff44cdcb4f7937be5ba19593c95a78b9e13052617759f4f1488dcecf57b1c479dcd2df190ebb003d7bf8358d0a5af8a4e44b587503b53497541cac747ddaca565d30173b187428d7222d74bffde49642696c52b246ae93b1872acc73041ed9a95e3acfafecdd339882190e296e0cd401e11bc54e9fd630edf805178867fd00b80925c8f2728954a3b4a25f9504a19ad4bfcea421f3e6d5dff242f1eb6a050cfbc788f444edfc5212e1c4e69f12f385e41c89dfe5d9c4e2d957b368090fec70e77e53b9667b1cc047edd704087d605ae227cd4353d8b89e3a00b24527f3191ebea5f41e2253d6c75eabd5a7f6e35f1eb8ef8f5fa4e87ed83f075a941c128f1dd6f84a49fdf62a301fe0b3067ce5a4fa717aefcee85d2475bbf64bbf8f379cb11856ee57cbcdc78499674391e7931f34997644745a34bb21b94ed92acb6ca201192c586b04bb2f409bd243b7b675e92bdaa5c97646590bc24eb4cfc926ce49c17334916fa7831f3695a01f09246dccb8b9924cbef3e18a60c1d0c371239cef5a2eb71da8ebe78128e3a21d7816a3320a8e8879b1b3febeeeedbb07bf4cc0ef1f9a608861415720d371eb061ef504f537b33c80ef2e1778f283e221ea29a448af091f8d446f7eff19112749f8f14d6edb8c8478a1217770e6a546c54648e7153bba4354afef53df8c778ba6c2e7cc85299a3c492c7df214ade9c7b57e9d4a8bfc1a0a0a363026fc928c426c339a94308b31688cdc813d3c12686611886d12ca39eb5406c46d6c9e8a418866118866118866134cbf846d7b1bee3a461aecffc3b17fee89c0b474a1d7a7ce8303eecb2c7300cc330182ddde86f19a57446ead4dd9dd277ff4c669aa6654c336ccee9590bc426ebcc39e79c93fec428a594523ae9a4934e4a297d4da394524a29a593524a29e528a594528a61d0764b36278c11ce19638c10e280df924df6f8943a16194e233a73ce39b1efe86318c5303ae79c139bdc92511c9c172d3814677bcddddd378d528a19c128c530b7af8527b35377d82ce83a104277994e3d1beefed3dd25cda8a4f0a785d78730e8eeeeeefffc9fbbbbbbbbbb3333b34ca77809eb65f07c6676eaee4ea5a5dc5bd99ef2f0971c0f7c15b1cd6a142f41fdd73a31ea5c9c4be6a4f4a19c2c2f52ce39e79c73ce39e73f29b597afc9292784104208218410420821a434dbb894c6d69b65fa956dd3b44d63693122d5d2b760d14cd64df062ae5fb16ee4cdb83eb3ced3629de55aa992469953d799b66f5b37e24e3ad5f98e63462846b50c8bd675e2b7bc2ae6740c574d8d291263171df6fa93ba0245703df9ebcfda7277bfc7652e14e0a7c6b7816f4c9ee11389eea94627135131c617a30fb24b3e84307e3f9473bea2c890878432c89f1c0c174a79737d07f54c174198eb6fba757d0ed1bd747ace5a7da8003fb58bb108cec4d3a95618f49df3e05ed7fb8f1d2e7ddf786035d84318a3e41e0fa69f28422de280b048e75e2c7bfce1b7b73a0581c41b2fad3f5e3d3bfae39d5c37617853a8f4a3bb00726a37efc5dc933dd2e788dac5a27b3d5fcf4573d6a29bf99d73e0c6a24e4db8f198efb18adfb8cf6bb555915d9fe5d0faa0ebaaa8372f1e6a8d9ea932de8c8906c25c3fbfe3cd5e4308b3cf1e914d07cc6d268dba7a9baf8de6a45946a11218a5975cd79487551a1587dcebe10a063deaf4f574fd9b8f5db153dd5af67c7a26e634ccf51135e5a59c846a947be540c1fefaa96dff8374b7ef9f73d639e79c1315e5a19c7c6a17cd229939e79c754e1e738e5efbd19cdaec22d2e8b5efe66b5d0491dcebb7ed35ae87361d238e07be24aee36b8b18cfe93a5546f2770e73ad7d13815bcc69cb43d4c9be8b46eec6637ee7742afb94ceb3f16a26f64d29a5947e67acf8d763d86b9b149d5b80fb7ca020e5469161d8a49fd1a7afc1c766867d470174636830b61c3db996827143f4350e482422a56b9cc66956b3582fcd9221d330fd3570b2026898bd7cae23579186b99ee9145924ab482af77a69856be7ba813d289e679d564a646c39fa8ec0755cc78ff8ce4f0a2ffdd933fc6eb80ecfb89e3ecdfe07e966df5a17269616ff665ab81e0f6652eec5be379fc5be37df64a137bb8f38e7b28ffe9c3fe9b6eda02fe7d3ebe5f5b386a59913861974c38b992e3eaecb03bcf32167713b665a92d1a8ecb908c5553a25a3511c783618f062f8f9fbe6d9c0508dca5eb319f7d22848c43358fbaeafdff06c60cf4c04795648c437501ad735fbe4c43759206cb8f45f0f977234cd621b7b9a3659204cef838dcaf4d1a2f8c68e37833e0b01b335d8ac6ee7e3015d1d78803749064cffae0d26cef4d4d237d9693b0ddcdc294d6d72dba917eddc411574ee7b4e548327092592484289249248428925963882895cd7724889af473de26354ebbda2ea435f79a9faefab7f56228de0bf775f96456dbe9b8d6caf1aa883e0cda4563fd68ff5e368347af8a487107e56aad0a704a7700f0365dff1102eb6f1a81f9ff674caa75340cfc6aba9a52f719d15cf060ff536e1dd80df43ddea94d0b331fad34d8bd5e22a8dbac9361e190b4b8b51513f1b0f12d59b13ce8aed555f120ff38ea240e10cc6481a71dc53b22d841f27ddab1c7c4b5c0e0de33d238e83d1aa55a664f6075f0bd8dcd11327d1050db1e8a4c9e0c5f8370cde0c7f1a9f2797931a4c9620e9d17a7ae06844da788c1e8e46d0057835cb51c2d1cccab331aa902b71ffa2041ffe0fd285dc23c11ead55166fec14c985665be8c5555cc199de0182458f3ad5d364d1328ba33e0a753aadde1d8d3e6ba1ad581eaa9689b251bc1c8417e3cf3f78337c9a85a254086368a4cf602ca15ed988d42d4863a77671d5f29fb3f6c4d588ebe6aae7c5c0d034cbb75a9843b22dd4432fc61f3614ae7f3b01166918afddea56b7bae539fc96b86e1da9cfb928de11acd2333d65a84f4e3e314a50219093be8343b824aeb4f1203dec89af86c445d2cca16785fa3f9d48248e7be6acf574facf2c5f2d87465514843d5e8c731178b3ab678f3a21b772d0e7c5f8471be304fdbdc358bc999567a37e577af8d9af587e30a379a3ff8bd1c8ebe8eb88877847ff8374475cbc71e39171d5a10e73c4e90081a00f0d24aaf70089cda4513e10a853bd6a540f9ce90161fceb3cd5560ff7709186f13fe2bf72b2615f4befabeee473d67a3a954ad5c29e2dc7f53490a82e9ffec5e9503f089c7fb94aa734ce45fbec5db4294022f4a91cec59b1249bc3838213aeff68e5ef9ce38f4df19daaa407f6f470c3d7e223ad2f3f7936302e07e582f075a11523cfd6395070a98028052da80222f997fceb1b42785d8df39119621b8f0b5e3fafebc2998139443a4a70e6ac15faf78b387db318479be5cb03e49d44e2fa0efee5f3fa6de3316be6ec29a7f63a65a3948657c311943cefcaa45f128e60248a5286762a8c92e52f0ed4a38bae366d3be475c938d4364a71a03874fd19c77182dc8f70813ac543fe0504a553d7252f79f525af4b5e3b2848e288248e2069da43ad519ce630a4a8fd1947fa9006d7674986273e5060805947618aa5c17538e9f32b8adc19f7f944e1c99df3c97881c1c2abae51cae954ca4536ab59dc6ae5399ed3ac2874fd631ca97c3be7098272f2b9ce4eae738b899e572eb4f39c8bcb2971bc1a7dc75ab8df71d1139d8650f5742a7d27bff4956312739e541f28ff357a3aa8864e7531c6af357a8f9e6956ec624e8cb1c61aebc3ca755c3fd63867adab6f168444a56a732711d18a6805677aac5e110ff7e4f01a55a5e69587556d3bf9d2bb8e916763f4fe1c03a601cbe0f18a85c03e6027aed7d48f75c6382f97d2cf2fbdb42ea5ac645d485f7a2844428dbe34fad2e84b2f638ebb4ba16115547af991d5a937b23f769072db51fa916f3a4a3f922f390e62229d8cba8967a3f42efdb352e9474f2da9f4b2b552e9dfa6a3442ac11ceb52a281a4f210ef0f26ba948692faee0c8219ed8d074b4e963e22b9ef8eae182265e89652d0caa656b2a7f7928b96162f4e25928b96162f4a3f4877f43f76b872351b87949c36a575912ef2495f928fbad963322b914af4bd9812d7456af98e46a552e9e35f5b4de963e9e3c71f8d1e48844572e407e11fd9cee646cec5ffdae24b3b2f1b214fbff3f44387fefcb716f7c518c08d1793e61b58e22ad7fd7de1767ca3c538529b05e3d9a8ef7f8a11396888573c74005d4c17c34bfd57573458c040fc4731c61823498b31c61829b5236a376a356a336a316a29b593da8b5a49ad53ea83ae68320670dfc768e1af4b5b8ecca4994cd98b574686273e50a2387922c3131f28ec45361d308f3a7dfb4cb36a04f5f9705b536e6d6e385a795ee17ab8f26df0c1375e2ad79d8032f89a95d128f8823da4fde28f1755681708532b7dbcb8d4d47b7dbd5784957d0b3054a1c22bf3713b7f1fa77ba19cd3040082ca4f03a9505404afc722d3e21fd7f9a8f1c23e080361b477615b6cc696f4d994db05e96a3b9b5bf9d9f499c9f434bc6be24cdfbd273d6f39deaf3c0d6c2bee0b5205cf203d0dc4b8d8282cebeecebefbeb6920666ba3de531969b3d211882c768a5fcc230095dacddb0f8483781513ef9fbd9e9d5cf7e23607e46aea855f85dcdc20b6f3f17261b4dd04ee6cd4cbf9a16228422142125aaad703233ea0040545c512842ae3250881c552bd298a30c2c34714a1e22364a08affa0b0019222d583d2841f0ca9daba448ea1509db050bd34154b3481a56acb5258a28a2f78e28996aa6dafa05065700452081189c06941410520f84cc1042b5499944214ab4b8396462bc80652a8da9688f8a8b2a9417b802738a10211a8da969280a37a58b07670546d4d2dd50fa028e951b57d3c40a2ca320dda4e41950fb0546d61f44095695944c1d934686954a9c233d2a07d403e54e051b5a5f14495552df280c91538aab632eca8b2aa41cb508298028eaaeda362a5ca4a1ab42f8a245000a46a2be307aacc6432f11344aad051b5e5265350d9b090ee68640a43f7f9fc1421c07d3e5338723392fcf7d2f12a7bdb1d24ce588b9e8f4631777777bff71e510d1068147f9146e9740cfcd712b56324fc1f84cacf05e1238deae722aca476fc7a8ae8c0cf726eb4409887c8916b1fcf10532eb48f87089ffb36fe9510d90bba4597b920f0d29036e6062ae8c30322ca41162a21b38505d5eb183a99981ba824f7b2a70a3920422584de14a172215081c5119510fa022454cebd8ec14695b5262f1c98c85a70850b0ed2ffe2e49c10460cdd09da53853037b8733e3c1e0c9962a5127205a1f2b751bdbc5b54c7d03f9ba8cc690d3bba207bab1b04dea722000e90e4b066abb9c08d2f754c9b28849b85c81e9c6102080e9c85aa3f421df3422e3e954d0be15e408c8fb18c0daec33690716e817810befdd95f4f2f19ccd579aad7757d76d1eb9a7c71965ffc4ecdd9f5cf397598d376fc5d3f7ff78c173f38baba311ab0a7e9b62fbbcccd819e7c83af9414ce4bc699b2a4c4538c31c61863fc1a32a90b5b0f3579b8043d746a32a5efff2edebce67abfe28e2b8b2cb2a872bbb5c0d7f7d029f93054a1fa90d73fbbb4882309ceabb06f9b04e3849c544ccc975b8ef9d7bf4d87bcd3a5c6afcbe9d7dfaf733d9800062de8c192ae385870f02fbc448918e343c93833bf26744f09447caa08badd1b8a30f80d8d8a50e6c5c47e9cc3044e5077f0a1030028f06ec0e8716a4ee65f1e8c08801b9d6511249040e295ffe4cb95cf921ff57f3abdf737dcf0626c6894fc1846aa7cff299fe533ce032600676420e08077a3069e215f7e0324cbd420f99f9437c439ff05cd6b5eee5e0511420811841042c8a772c6a7f1a9fc39ffc27e1524d4a817853b5f13ee7c4ab8f36f7851dcf92d2c8b8d3144ed9a4872d1bc6a3c3d0bf5d0a39c0d2f23f328d7443634ea79bec97597e47a086e351bc8a40cc2d77fda8c5aa7b487b0a7b435371e16cec4d669a8532bdf05e0521797be14d6f50d1fc233f8e95f6f26b6200c7f0bf51043ee07bc47628bdf87bce715ae3b95b82b37e4cae74b72c2b321038045769c112c824494cea773235920ee3a3a439d1241c57ef2c05e87f9ddda96c0e373cea12af5b25dbc7a8852fa367c5ddf428de2abf5344d27fdf897eda12da31f3f3b9d78b2e4160fdfe016bfd6e12f2e026d3be847fa114807cd38ea5a0c8262ff6a7ca3127c3120a0e993031a7191630967e24edc893b7167e7c812264ee24e2bae5a4271272ae1b81363488544494c4470c6c43537f7749eaba4535d04ba0ce43dfc114aa7523bec3871c771b63f7d3d1111350c3b0e914e25313dbfe7f40c2ccfc2c19c1d252b169ca203e6c6238d021219497c32e3ce8b611d2863cfef477ca7534fe4734fa77c9e8d9f67437b7ecd6df7b88eb71df365363fb6b26881308645248de2211b851a8501e9dbc51d2596e019e7f1c8821883cbff38d6e0f6cd0667dc8812977fae00069d07b9ce8be18fd1813afff13073175b77c89f3ce11e9fcbf3b76f75ca651249ce65c623979f29078976903033d46918fe9dcb4aeaeac4facb33d4ad9e74729c13743dd39f3490d429e5b6f1882f391ed8a5e652ceb9c8457e1751d445d2bf2c5ffeec09b32e33cf0d3f27a53ddee349ffc190a2528ebe89524a5bdec49cf366024310419313ea8bfb7ea0c02102117df91a22563218cb20c8fcd0a613d495fb7e866025a9f5be9f2194a086ac64c8f02441660b75def7d364ca4f932b6eea75df4f132b6ef795e97d3f4d9ef84c0e6032b882e8a14e94644d103913284a0c21767e6c80850b354000b50864d75307ae0eb4ba3a31c2523364e58409538d1044bf517131e9cf4debd4281c3b083aed00827559aed0046645185d4888538cb22044e13d4349b6eca789144d7e9a5439028588915abaefe7064911eae9be9f9b199882aae9be9f9b9f9f0eb4229d3f38442b3c3f9c72a82067c7103b3f1e58c272fdbe1f204a70bd65d241d5eef3a1b2c3022a4b9250b1c14a0ea83c310541258a95105069b530525beef3a152c513a8546981a2ce205c34514b1f58a9a26e2730d5a0624b58a8d45393962bea8b276260c5128aa8aceca046214c59c08208222c8cd8f1c3d0cf0f3a5918f2f34311598213b30c2264c1c10920d01942931538d981131941c53d5596d38f1150eebcefc7081f262cb2ce285010f2bf8ccc0d5e3f8124faa0dfd79780bcae4b8eb21dba02794991fde418e127c70637bbef27677583824bc8b48cc02a0b247870108509ace052384129b2d0441658d79fe524441656b7abffd3c5e62b46d93931eb77eef10cf86c9bc8e5eefe00cfe0f77f8f3ba7510c6d3a27c2eef7de0d41903803d00c706ee7e355b9f2639c314e8f9eb53c8bd252f08c8cf92767ad46b547661af860610a137426158d75f3bb49ef94f25949e94beadcf33bad5f1a8875d83f9b510c7b8a4d9f925e382f74f7c97537ab1f6cee32823de137a67cca97b92c5613264d5856d40fb056d50824446978b9b3fcf2bf6c3fb47e3db32e0f4926177e3ba469ffaefe78107ef775b1dc2fbfdcfd62f9e5b289bce405e1e53e9360a520486cc25a6de19be0d77258e4c22730e7be6e4977e93146180258c4611197ec1c80455a485e5a3004459e04d59b560d32d950da0640c8e3baded03694cf42b78b78ebc2bf1ad028d81b733e361cf0bb877bb564a1ebba5a08c2776602cf80ef300a11b35077c36f94cd643985507c5e1ba544ed60fc0b8a45860c8ccac24ce01b4237dac753536ce3c442edfe9b00b28951845a43019d8a7dfb9f0c9953a39a7f7311238a0a43bf8c8b67e3dd8e409097df81de0c36a57a4fe7c37beef5419815f987865aadeb3d70c67b200c73d7dff982f8f140a101a229024d104d50ec5e5ef8dddda5fb0c40b70bf2349df10c7fb633f85c9633f4ccf004cfe0bfdec61f25dc5c03dcf79304a19bb52667f8725d0f6e330350a368e08f8b2777fc18391708b91cd08166632bd677cfddb9c9753eb42bdfe7a473da4ec87dd73f8e2fb9c733e6bb8d3fede3217886672fbef7314e26f2fc5d1046e843ca0211b636f7f1eb7763dcc18d926b80fb7480051ffeb6f29f75e967229d45c6b9ef25ec2b371c4f4a1b1af8e3bab21372fb89dc2ec8103ce37dcc9a3febe1f6b32502dfa6e3baf26d2e9146d9fcb86e3f7fb4ce77b68089dc8cf46eeaf462612c51653ac52f43f08d47e4463b97d4eef57011b543a1ba575530773c1951e3d920fd93e1d14ce0a556fe7d0e3301f441d1bebb310323460468bf82a397ef76f4d9bf8d06865b8e15ae470214d0a8d830f16550a8ffd3a9d69eb3d62ccbfe75b6814b7a9aee61ce5a4fa77f92a57931f1b3a1195426e278fb17a0d4943f77d30289b79b2f075dec5e91941bfff29dfb046043e28b05fe5a9d99ff547c1a323f8857d58e21b218e1e92f1972b5756447872e1b0ec9b938e73136ecee281f76d3050839990d75dd857559f9d33f558b6df9178f838b3fb9f8171cb78116aed616fb542eec53b1fc83332e2b9ccb025adec5dfd0f22e381f9858791f5ab87943cbbb78179c0fdc9b5d0fb7bab03640181f452253677349cf1b0ed267b0458b164fc3bb2ddee6b2d8ce7613b82bf54db6e36bfa1696a5f46f5ba169163b7a93b58957f08cd1d7d1d340920c9c991a9771a8dbc9d096540189d2c89525abd83fdc74d0a891c1ac3fadc1e6cafbf852991bb3e7eb326e386810fb78461753fcbce1c076b894934f03697a72b5515c27e5e8247b2fda65c8c2b69e1a15e3cb28b9d928f9129334337377777737337337337377676db76d8bdaf320aded7b120df1d95ab7daa897e4b1b45d61666666eed7eff57b8ff9f57bfd5ebfe7e285113f735698628850c61d5d483e962d9f5a151a6ab5a2045d304e2cefb59314a2a121a1d67b0188419530882194fb8026009678af7e00625049a45a5a79c19c0b1ec4e089e1440c27e4bf972c8bc123a59452b3325acb3ece2ab7effa460d03d786520c27029b09a8c4adf68439ea14cd0000000000f314000028140a870342d168308e5359121f14800c83924a6c549c8ab3208951144719630c41c0104080012300032333540188e931ea869d68e4e4eb6a1e8035a90ff560a5bf1a2cde0231a78a85f6e8f8dc5e5a3df4906e97c6600278c9cf76eb4ea57d70c331f1fcbe08bdf6bf6f4e07084d97c2f6666e4302e64b740d1b42a5d15f8351373a0b0aeae718f1ec8f38cc8777a925ddc1e0f503583ba82c4f05bf40531a46f2eae6fe0a4078a99e1c15c163d8a28e2875a5811a686fd2e2b7c780350d85231bc66c8e9207098db025ed6d12f313e1ee839429709fc820a11ef4addc06055bb00e41989b64a350ed7b5183d5139427b66a3fd87508c564d87abf2099e40984a1cb761614c619e381f0b718b0b869939fafd9389664481a40d4be5361c779e604ad1f64ad6c5d2e3f48487e974e0c2c9835f61bb85f881aeada007dcc710413549b3ac7acd9579240e51704bc1341e460cad6715c5e461245d0d6ab78a86a5910650f45404272fbe6142d821f0b8754f69622c59a54606eea00878ab49a80717ac39de066a368f0523fdc2e7e963cc84a950388d43a01a36d1f0e308f063b451e8d37849adb8aed5a07df6cfd1d245aacc31f10e21c9ce531840a1eca1c84d753d44d6c62b57c7ee37304f82de7adcee473de08384a4fb8a4dcf5457ea941df7517613b6ed38a2a5b77e1d54dcbbff1dd1e8e02d69343aaec1ea5a14761620b93c89e6fda4804dc19360c26f0bb2b9331e7bc64c5de28a02c8a276593b84be673fd023ce1e7e9682d4916f76b3dd7efc04fea6efa5c7750d5db697e3fc4e22c7508e97247b18dbfdeee5da330e7bd9d39bb4f227186eff03b0593d2473a7906c919402b0b071f826f044576900ad254b632d67c1c71f0de3ba4a28e79458e87d4d8c1ded71a30d65c0743d038c8d44033a4c48df684d5ca69e7c8279d2def8a7d767302290a0609a334add61ae655d1b5ffc8a1f324c3c22c97192b966fb18de1f69d391b700b208c07a37044639df72a38f7f07e8e69e6ce6dfba1e0cf46caa1e573ce5e0a5bf1f2d1e13c6d81c0c19448375e82ea653f22d74a3ecb839fdf99d85b5509883fef65f7d8141becd71af1d0fe62146b628917cd16ba1672a413eb5877e766ea202e02dd9984e50050ba7af10147f683c323d4bae929bddcb3eefffbd7d4b89372eb7deed8df1df6afc072badf42bb574fbffecf8ca854faeb55cacc892da4efb22475fb4c18ee0fd8fec3b04349d4675dc0cc396ab0e23903ed829fd1d15ae8c11ac1226e789160063de7f5e45be7bd5bc8dc5044228344770073b1056dd27779de7a48e3c0facb979d5db45485584e41b60e743af1dce856bb113bb3ad01f12191cd9af0b5ba74fd7966cd83ed7d4526d75f4b57f65a8e07558682564260b7d9f01e5f306671b717e2a1248a9fa1c4798e36698c1b8aaeda2a261b50a3484b3dd7b894c170983bf7dfd8684250be4b1a76575cd660d8f280bbc01805fef0c4007565bd990e6255f84f489e281c709a83fd20950693d0ec216c182f6b93fc3d701b02bc6a6bd004021e88a280a62584596ade2d1ee8ee0c28d7caf9686eebe6b21ba83f7c71dcd779c941029a86b7970dd6529dc9b61545ed528f5a04b58e48cc039c328fc25f141adc218f2eb7431488d8b4da523978cfd4ee29a5c64bd7d7650aabb1c3711ced29dcd49ec7d21799c786d5efaee28e1de8ed8a53fdc7c3b610f2c0c06be7368199d3e9c6533be51e87676b910a4483d730dfbd5d9b4e4c7d83aa53c95232002eff4cba69dc176e75b04555b81da4e51cbe0a045d6a6dc8af4bae3967949cda374d4f1e370226a45a477835567ca55aac771bb1e2f3664769ed045cc58abd25d78977578435fbb61b5f359d75efbef1bf96b591884dd9b306ab8064801daa150c017b1dd6f82ceac12d3a2d138f6d178f76bb62c8b0d7b4e2a0388a720bcfbf42af9664b6358289a27c60fff7c229d6647fc3c4c34605d16ca82ab26ea2422b220438c1df43d4a42b48352e621890cb747f0968ab705a39b0044f2679569c64b69c84c47060f6b98f981cec6a5d55552c27ba4263c0cc7065313569eee3ffca89556eab6955dc59635ff48f80ab625fc0f696f2add54dd56fd604bba13971a3a0971ebceaf4e084524798139394f5acf53a4d9dff7d25ed57428a51674d86deb9b73be2b41a249b69e98eac28bd7b6abe875477296286401c62944190325455359b4452b44b6bf1de7f4b7f7183de4ccc02648c70c56a50443e3103032b72891d9539ad9c904f2c7aad35ea368e6d34115acd211ac3020ccd94132fb860f394ec4ecc120eb900654738d48f41e98641bd64e154e7bf393160c88c42cd33003a4ca49b1fc68abeff3cdc22237724f722e774cbf544921db8a7bbd5165529ce6fb4e8f9c122c277a1c130fc418ff7ffe4c992baf9b91322048cedf1b2b9b49b4914b0fd876226aa6625ce2ba1fd432e216c8eb3f8fed178bddad9f9dc96018b82fc291a1da48ea1ced451cc1adbef978b45948ec912ca2e9214d22750bae1963b8a721819c49ed72645fad23b2b41958c5dc080e31d1b436a202f030f2a140ea23d3016394db45faa4c21b03845a01a3d76719a9759383c42cf02a150dc1951f567d42f30fdfb53a9d73d5f6434458ebc1c6a2f0208a849581feb38840860b5458c61e1ac241f8a645c9e53f4c5446971be747099e669b737d38950233861ac61895142e7e84d8c3ac6d0e438713f4d75a1152936905de6d866da0bfa7ae94e21a28609e6525ef65121a48196e3b49bb628f1d01f14fbf7003469d0a060b7d9eda01315cd6e70c7080985c90c94fd7a7d8cf430141cb8daff3e6c9bbd9928760184c9d7191367ffdc9ff9a69fde7cfdbbd93510eeb7ccd7166f1ad217f13db6a6e6338b2c221e9426ad5b7b015ab3df931b0ab09582cc53bc6af35755e7a3f4bdd85c9b736988a95ec003b4abb692e6d8c601522c7e4ec755fccdc93afd1c81b95d60d47bb24da327cb316920a2c301a9325588c0d25ccf52f4324987c2fa2ea236ba6b148d3ad1c8383a0d5ba4e9ada51b4dab882b57af46ca8b314121acf23729441b3346491fa164971f2cf925962bb408165f1f64a13915d470e8b6ad5c2b26012f1a58f0c96b6ab6d7a8b27aff5c80eee490e47a747af3f27e2b0f9722a3dd177e35beee8255d26f256646145a036cb24056fe8c720e4f9d253d3f5eb6b6f8f08fd8473443692468708d77d5e4d58e52907b8144313e53a4599f6b39962db4e0e482fb375783114285979765b14f991eee4e13d975ad636334b7e6c8dd930ab97a29f750ea82a1d606643612a27a7fc831412c901237e89f1bbf2d5cedab721d631ac656f232262bf42c46563d03fd8067861ef9a9d1b0e7c4590e89e73d83cc37c86546ffd2c0530c2d7fa72294c45ca1a14a19fad137067aaf387b45211268a37e4fbfa0a88cb3a1e275b219ff45296d829d3cb95848c13b40cc5d9e843c6553edcd9cd1d2262b59d0c4757f08a75fbc662fb03f968e3e8233fefb704316f65f85c3c08a2eccd71ad34da0018f9f2d4b99d2aea11402d54be48b460d0465e2ab8820f035f2d15d6ebe0cb1d2d2c6285fd8f5d22f76b63a35ed154d58a0647d4c01124136f24ab8dc546886538260a75154455a0519d8a2812212411419881a02d57bd4acbff716180a8954d80c3c350953f08606cdc778d4d1bf978e1dae3a0155a708f8003eedb826d62dc9aacce9e5383739610ee6acedc39e3305e16e0d0d3bc87753780d5e9ae4e6256f19cd394d6c1bdff76c7e811ec17b65565ddb4bea954cfa774c764f3054388d0511fb49925ac1b86d5b9606ac6487d8955ed630e1eef5547ccd1144aaa4a0f83a1bcaa629a210b496cf859615349a70923d3484e8897b4c43fc316190e65daf29329ff5ab143a6be38435c02af7c5c4555872a1231688ee186040190781cc99eeebb218017a1efc71153774f7c483f0134c7f45a11cb1a0b9189fe1737bc40236e2271f1d270b35408db4c792edec99da5f0431ba9011f126322bd661c54600756c06f5cf2419c938a3f5443248c3a8debe6df0b04b3f249a0e3040f45237abc3d091904d049223f394eb8235cd7001779fa718fb558c14ca0e43b678895e6872f4b7a3db8b290ca4951787fbce1ec7966b29a170f1f6495e7812832c1fe4565ad759b951b35ec06a626fafb0035864bf647bb5262d33aa8b6beff6a9ea06edecb80b3051616c25e988bf5cb5d7e0f88602b34f8bd78f55e667282eda99aecb2a4269a13dbc58853d8fdbb0831b35f32972309e84d598ac1acb59a5cbbe88a54b8ea5e5b48f37331fd7a0524e125463729a317701effb71c07eb402ccf3c78dc2e81f647d491e1d13b686d751184a80801c84b757e31028100279485ac4de3e1cb2548e5739f5a527a5b11e0c09387e7be174a8366635d8079e588649bb23e22d65c2a10eef301ca73d7734a405be5732db480bc1f823fdafcebd4c47ef94793364240a994af808447ccfed0172f6b23a8b72b8bf59cd591092c2df2efe8deef2e243a4fab5e7e6f253a592c3597529f76d6a1b9e86eca2b754512bf60642ce5e1c4e20c28abd4795197e126e205cc6f42cb9377c8db4bee7ac86e701e712c2e46d441c3a354d473dae6485f37137d8004e4622408722cb3b11c0614da922f5adf3275e5f2f133e0d9a17cebc22ed1ad38ce229b71df733821db95677ca3d891be934f9c8e1bcaa2331edd54d0cced21086a39091b61904c447089245d56bee25e0de5e4825a9df35b1c2b55ad94034609d8818fc4b962891ce8ed5a0c8b472457d1b9470157c9f4dcf195147e9f3d6bdf914e4d33d77bea5d6645940cacc6c10a754becad4cc03ee8261b0db667b84fb0b07113f23b244460f25bfe3fe04ce5c464aaa887952b5ebe0c879e2c6f518f9fa7f2156f2c33c79cc525cc5087c83391a1631a47e967238bde541fe65b51b304f1ff682966c3de0919649adf7112d4522df576d596ea86b843cd89f59dd27e9f67202ab956014ff1fd51ff384f15f5496d3bb9a539ea408d915f1ecde80a35f0cba74a50725100ced8053da45615f727da082d480db65ac65ac2ceb092ac2e0520f4212bb147b98d96d39f3625ffd7ea9e94b99a52558f9da3b57c15a59b887d54b886d3c1a0ac6e6589c64ecdb6891495f12c2c315a3f11466bd844f79507ebda8904ac1edd3e90e5cffcc4da8b312673f03d07e2b3cfdd7223c4168848a36318dcfad86dd652495fd25825f958333ef3d1144b575613815bfbca316f2f2851df143406d096c63a2356761bae10a97087fe7e7609b42cd7b112a62f5b49870cdd24b2e5de277e61ad4614d3881dae3a3ca3883e9442740085990bbe3d1d5b313dfe5536d38617ffd24956938c91f2484b5a2d4f414683ddfaf1123333a073bbd724bb8332ab9b5fc17adf22d6bb59e1bfbb92dbd75f9ed9b6bcebf607c750725f153bfd68a3660d31dd8990fa4c4cf122cd76c228f1b59b3f47f22e6918c3f6c1ff2c78960f7c000d71025cc599a795cea9db2e9faacdcedf91f5bef6b23a3c6e76ee3e50184001483700cc39ab5433564aaf7df60979b88bac229ed1b147f1f44be3e44324e27b2744875d2b35c81814c74d9d780753bb97ffd34f42f53feaabfc76aab7c925e1c27dcc9acd5693ad72a727b04987f00f0ebd9a21bdc1fb50de085a67afd1002f3b98de38bf3c3611267afdcc69b5a6fc5155beb46b71e3335ed8962b7960e87d3f75bf64d7d45e76cd75ab116cfacdc5ba05ea1aff0e9ba4398411dd76977cdef3ee4a4a019b6903ebd106309ad3beb79647870f741ac5bfe973830c5575ec30dcd546ae2357070a82b5299e84db16c0e6962d2ebaab19fbae4a60bacaf445d9a0c2e01314f7f502b1ace2d322391671aaa4a1dd4242339df4c58f1e3642ab4e2aa5151f2fc0669ebf87d8991adf5ab2cce0980d5f3fd226b3f7b75013d111eb8f4a0c0554e6d5f5f2e7060bd4012052ed0b76e8cdb9864f28d0251ff99577f985dd0e2a0fb6b9673aef9d38e98f3012962dd91ffb640e2393d51d7f3f04a9abf2f731ed9bd0ec9ec1a1701a9d2c14a4ea5dd686275ca46c0011a8fa9954b94894db4e5b8a64348cd17c6e0cb675348944ca3f9b6501f06c7fdf37e940a7c84df8de3c85866c434cf55b7a2b74cb74d4db000d0a4183a13cc9aa5539771502d8eb411411bb0b1c41af4edf39c8d07bea1be5ba2efe7a3116849f43686eb36771b376d919d532fc491ab67a141fe3e40bf5d7b640768d266436eb6feef4b6b0f4a8a0506ef10dc94f19883a0240bdeded9b7cb60e80ca26e4544f3366cf9f6159762c46f8fa527b2f0c32b3a761efcc6540f9f1e4cd91bafd8d098be048aa8e2a7131dd24409ff0f9fdd2d197c4e53a058ed2b6d81cf8a6f690e1ceb7c4e2b50acec234d81674df8f2d31e7856defe7b56752bb82d0f01d3a5ca9326983f37b74c4b39abd5a9922c0ac97453004adccfaab9eba52c2bd00ea04df2d432c3028035ed037165f6e398c4d2bee5652edb517d64852eb711150ef108ae4bbeb40f10409425c6be2b8ba91bbb99c711f5ed96781bb4ee4248bcf897a811a71c580bc1e9d51d0da142cdfaad7c0b702f6d9e768d87c79adae98490e9e207b9f250b146ed354f3625f0bf48c9c3806710591729777d68aba49dc1d97f8bc906221ad535c0b62fc2427a8ffebfd550b27b8b5569c030a16c2ee5afeba61f62b924813165b8024c94c0b6c1ae7a561fd30c851099461f6404421efe6d2181b48b486ad54cedc0a7326d83cdfe6163bbb45ae1d9e43723d330209a840b9b640c1abb24d147806a9f6794cde2629c5f53fb1ed131209502c99e4ff77783e07e2acf6c043634e00de7ba89131ea1bba006350ca4f3144d33686bf740c3cff7e9d4a3bc016c1c47edb98a040b0cc11cd72552f6de1190e2ad529f17fb0363a7f8e65880aa45048a4586d8b600f0d68b8c418d14fc8b4257b457d26dfc6abaeb3960220838f2ce15d8bb4ef01b88b7b70a0b90961b918aafac21be85bbfa3b0c2f8632978cbef205e83d66f9ec6a431a212c986be9658457bb88e71396090a86dac67f0ec7ce7125403e82846572298bec8d079052f28939be0b3c6591cfe7e732578faa8cce673debc5012b29e1e7b40ddd94d3526893b3d8caf6f5271bb378c29a33f2aa95e0d0119297580bf8004a5ed7a2ee70d340993c34e4710ee9abfbc1def8b202147bc31b1b18f8f9750f6aab45faa7c6b6536613bdb767bb6ddc885b3b06f22ca126abea6f99ab27a9e8a1126f8129e0d089807bebe130050b9256dec545a1a87c3e2d869c6e749be4fda0d76a23af7bc531182558ff271d8ce7f18de3dc2017c487aeeac21dfc03bdf5d4d53bfa03b75d15d76199a8012d55249f02c55aa400bbea27e2444868f503d61c67ab0b80f44e8c9d542f6573a3a92006de0fbfca86abbaab4b57cbae1ebcff2a71045f9bb0c7f37259fada0b337cf6a2a7585ac88a3ec1227affb3cea0597fcf4225f60d446bf23784f24cfb5ba3b34891ea902a4d0b7c7b59ea5b0d3b3faff04267435a48f74632dca775a4ce4028238411a06cf3482c8b5b5a1292841692756407bb8419dc8e4d80b526f9cbab7b2b5cd9053d80de06434d9cf47c4c131e553dcfb476bc0a43e963711c5d6e0874aa64bdcd01d629d55318bf64bce3054a3290b384daca772e55bcd67a5a1e488ddd3ebee84cff76cf7e7b55bffdc2e51d60c6b58e4f83f6f922f8e2c38afc6e3a1282a6bf086a0ad570488bd51e8a5aeef112db582ca1e3dc96448d8a6830f45903f59943ec94ea0a605ba74e64343388e2192b39d9a29fd625047b9a1daea4ce4951516953cae1e1a15525485cdfb5d0877ab2f7e60c5f3282582549ac5a73a9b4debd0fcdae885073bdc90b88c624f2fc1fa8f60fa6f925f906bb7504780648a3a3a90dd2a82c660eaed5791a181ec14f21de52fcbb830cf91e01bdc347304db30353ce0e8c1f745e94ff23ed2069df8386b3a3427857f06a3a3010bb42b607891f5f02141df40d07abad9388e0a773831d6cbd6997d2c5db3b74b5c12e2bdb1ea10de65c5fae7e536477c75064afe6734da0fc4d2b8c72c661becbff162d00126a7b0bf2246dac89a7742c7a535ec9c06c0a86186fc1c21b2a99afe717a599169a04a703aeb499a05d0a5c0b9d21a7e77d9003da593764617e8f62d06cb7ede8270c537c7fcd8a412f15bcd2e0969a625b6799fb2f47602e194b5abfd06eea1e953c627a5612cfd9cf6690f49cd579ee0f68414d3ada59d86b170bca3ad128d0073b09fc17f1ddba6719823081cab3bb205056c619fa829e91bcaf6a0f2fd357ce87f54e49999a8f8b5d7f65b3e4ad6e8381afbe0101b9df8475a7b95624a7e517f5ebc5ca92a8d5d81c232ad1371720a3810b88a1a21995bccd0992c912a03e4b1acf1e667b6402456fdab1f29090c5b57700cd936618fd01cb82c488b5bc4112324762cda6bc2ef3113966b16f3024a0fdce6ebc5c7e8910dfa513b14cb02d87b8f3d29a6f2de60bf7a5391311365f72cef24e4dc7bb7cfe2d3767a9c7302feb4389c9bf7775d25703d322856a9f401424018e9cfe7a94808f49af2f88be1139c459ddedbb6a7ac795b176ef71923108afcd5bf1bc3592611fc8eac80135bb2b2521f55dc5b83122c8d9a95d44e1b033372d4b6b423b958b8c61fc4c5329334b3253ecb4251ca49bf56928a474dd048a8a911977dd13df2c09f6c952726e5ed716546e53159e5aef6f14e986d76806cdd180879e1e5797bcab4209da92da58b350554ad2f7c7baa28bdb47e8d09e580aecfdaa8aade747aa4ab06d9787049a07a1272eb76a62cec496f9335fe7424e494aa73be0e6f05eb11a25bf1cd8ddb1cc7c6f1b3b6e705d3dcea6ab7806f948d53c04808b6c3e96c099cefb539088e80d0e0710d36039b5405d1bec7f03e89cb07ed041b0bda7c0b2a2744d1c2b77437557e7d4ca2b2415ea0c32495665cf4d65f062087772f99070071dc2ecca4c40ba619914d37a8201c547a738b78b81e900c4e394f4c67716db908d8f935099599bf0d11b9c3f693b6af315d1e7cb80be13d3a1591ee3204887f3770320e2fc7bd59128fd586f0bb03fe16707da9e4311fac984f12eb347ce4163b5813deeffe83695a5234564735c433231ed67247229ab3dfe942831cabf72cd0a8bb50901d0fc141b9cc65396a88a8f234ee514a399b8dd14edac4c642a5278a3490795127d702cdaa4ea1e3cfaf2fc52534e8f098fac0e1c958e5871c738f562ba223cb009a3c9892831b614c0a652a93862a06cd8133c0184a534daa51c89e6bffa9c244a6a492053da8e987788434b752cf646f3ddb7c1679705c718b5902995d4e5340a0123be504a2980e2b245ca778780ea2c1a2354141b33861753e45ea325991ad08e58ac6ac16247f0169bfeff7ad87522ebe21dca8717b027379eafc34e6c16c7b23e003bf6579ea3588ec711f4ab02e47d06fc43bf420f2b473d20ba89cd3e895af57050a265b9998448c671eee7f554a54dee0f7151fe92336c706f648563527b34d1ae3453bb2af805fff38aae233fb17bd68f23b759afb2b4410476452318dd89418d51f30ed655f2221dac5a9eb1ae794be10ed4f9ad9dd1d035d2960d16a562634a9ca634762caad70fc39997dd2a2604e346f4d16036fe69babd2ab6085fd6e9b40a5306e83059709a72ca9a875eeadb119c20052214374634be1ada52396425bf30a2b27be5366027f596d47e11cd0933facade34b39eae1a74bb950cca5e80f17e44b97f4cf8ebb997cb8a1f678a38ac8690418239edab4fdbbcf6d7f6ec22f4520e979d27003b535cbd78a93041adbb1d3913f89ace4c31f4da9f7a5561d6c02b7275ae2c60256e436b4cea9f0dbd2db344d71bc1f97563862df02bd091e837572cc880edab337d4cc8e70cd6a0a0dafecb185ef4643267b0615d3eab273480f325b5937fc59b887ebc9d1d7f164b46e9c47e266e3fae5404d5e9ae5c614ec3bf0437aa098f0c930788c0f59f972ab60350afdc194d9c240af66abd494b0c142503918d803fd774d96862d891fc9d5885e6c3285264b1acc6107a9d5006fa110b84214eae437b8bd4f4892a5eabe1560a79465061116b89c36c0ceef46797935a32714407fd1be3bf417c2b46520a84ce59602e0ca9f3fb5e0b0f152d0da4e9860f3fa09a2fa62f352945692c851a07b2d055604b79d0dd2897464503e5cb9e5a527a97a72cfab06b1339d1fea5ecfd4300b756cdabff36b33698864a8a04aec80268beaf56e12b297d11300a7e456b4e4f2ecbb1106ee7e454e74e8ff9170bddaebc013acc370e11c48667fd0116dc4e2f4843a3a28d94c34a6b39be6d86a8dbfc74a879fe898a76518e1c150d4c5fcf38878ab67e5fcfa0a95f5d7cfa04b96ca506381c49c318c570eb234225ebd0aaf6bf5697c2c3518ad6fe611df96f38986820106da1e2600d5aa6bcd1eafa7a8aa61c742293730abef56621f73b9ac637c673ef6a3b754182c5856b8eb0b51113ed2b95534b8f334ff704e938b55775b90b5308f37c51dd1dbb0d342a22c52c662fc3e899d0cfed8aff3143841b87bc3cd208c532ee0fd3a2d089263044bbd7616d94ba398322dbfd807442814f947833322768a3ed96cf23292b47cee2d25211b788a36e10c6af25f4247f4d3636cddbd4fe828a15dd0bd7c82c128b3eb995fe5da6f9ec23910a92c7e668ee44fb2241f720599fb4872f8c4eb4780f212ef5f95b16923834ae5f15c2155326301764b225953e91a0499d6808c30b199e2fb94a0785cfcca0713422beda0a2d55c26b9b70bda209a56539be8552a4745f4d32b8698f3a0bad4dd1421233b73f6174aa8ce814bc760eae4af7187e06931a930f5a47f9fb06b644df044a13e53c921357eabc57d748c3694bdc222fb309283162f42c3e8e816d675af364fd8339b7d9f6d327229476dc9e40ec603fcc652a0605504b354b55de1c1c886a16fffe243cf7f821c72a372d6197d05036fc85160eb2a46b67561ff6547556bc836251f25faf141622e2af3c301ecfc05bea4c8ee2248b351b31fb462d084a785f5f4966f97864d7e580ca588874a8549be0292808a6cbf2fb601d3b30556792f9e874bf19e21ba254f8adcf13f73c695c4752ccc65a097820356349800fa9e64b01a6ea8ca3b30209a201b62acfd55abb087afb2679576dea89bb38b2f5cc416d466d5514c1edbbc3bd74460ffeab217b3754c3c015e9ebf37327ac3da4239fd8c453f23439ab17101cf2fa226bb7930d7f9ed87b20ef9102f6f3350a8b64f04ce5ad726d1885029c9edd7ed6f77a0bad75dbdb590447ff5fe88fec418e190658da7856570b5385ca93eef7594a96141ccaf385405cb26656e0e59b7146349878fd93974c32dbc7f4af60b94fef9d714d7f9b651af8d4a8e9a742f7f52c272159b9ee65d8aa95941acc4fdf67fd6c113cf4e9a58f4f8b8e8d897e7b0282439a800e5beee7daac01a5677e29082836bf0cfa93427ef54f54b7a4658c75e4aa909bca20634c78aacad22cf92671a6f109e05bd96f601eecab1f19975556b63a6426900275845f62fa556059c7d2cdbe376acbb5c896137c49a338a4d519c3a6c7ac66402f1139b08996b9409a3df85e8abea5dc8c7c2ff57adb1a3fdba7e1c563ad3b632d61e9c7313c58bb2e5792902cbbdba80d794fbbc17a221e2ded42e6892039da358627e295fe15ed2490b951a0cb6423226253f294f81706c7171b4e008f304da091ed322fb06c3b89c46cc275ed0cb5c3206c72d71843cc521cfe566c1cd0fe5301c3efa7cd3ab19db9b085d9f982cf948a9da7844908e5069633a97b416418f14f9a3f30288492686665ade51f2be45caf449c8f95b96fcef891a202707ff99920c941575802b6c99b6b6645623e96420bf6ee87af92d2c8b1b7f2b9b1a5ded0248f3c09dcca821f9e5b8f83cf7fcfc262cff49118393ade3c4bb5947552b10e7122c06b18069721a11cd92a6ed092cb4c4df33492206c55263a1eb33495afc1e8c0840570e5c7241fb79007c98aa156896ca365208bb2c63fb5a08d5b8a43b4fd10710b21bd52acc9b388903583ba40941e9406938bbacf13d728ba669ff7b418e2fe166ebc9805c9a4afec45ae400f72dcde954d8ebba85282ef4a571cfd001349f6b0d518db8500e943b7f9100dc78ef2ed71218032a34c0cece0f6f94680468a4ea0c23d4dd35f21612a6e8d8c1757597eee472d4bb0adb069d080f74e2dfde7d8a253068a4a2c4b7712d5c76389337adda6482316460a07c3e27b34e9f1bf8414f95ccb0d2f2778c8e5a611377dbc64ce53cb20f2f3b5050586ae2f41072bba48c42904c93f5851c021343e14b1b70f3fd9843242580a7ce4d88e7fda91152e9159d4a13138d51219ab2877de12590c28d774895c4a95125e7ff614fd4af02b193c9818857fb03d22a8de2293a0c8edadb6affdf26a4f9100f05c2d191508db2b8c25374b156a7132f5be5fb1a10cb3e3c7a78e0e59a8a0fde3bcc715e8eb2e23aada3f3d0f4968204f7fefda0c46078a447213108b95e17aea59c9b7c632603a75d49f040e1134d966d529f2072d005b632d21f5282af9afdd07d51a75bcb2ac82bf71f0896f485e373f3ef07abe0fcd4bd63ddaec0662b484ce82d2595da19745d91740d9f75db35705dc3749f897de4b99f0a7acfb0e1b7f8af00ba3ece7ddc8a2bdc636adac9cfb6a8f0579f90cb3bc08fb67ded1d1725f254aef1a346740dc368592b60f89401dae66bc04bd7a304ff7a637c827d5b69cbfdb36e6380a6d30d39ca15e1a6dc80bea695a3bfe66868c170a1a6b4311499d107b6cd90588a7a7f3989890ed3247f1a141897136f4ca0823c594a9c0686e777da4f06e20e559c5d22cd4f0866eb939e26579180dbea224b0e4bf30cbc9208b4c292702a7f98df102a3894e2c1354415165c2111076d8161d67355e36f7017261ff1f3282ddc8a89e2548b02c36862620bac24e920596b98fc6bcc5eb11da66817ed6c4b25d07699682fd5aa11afd25649b25b971b47943a2d70d893812e06cacffa0f55a2b60d229633426412dd53cef6aa40ebb2ff75f38ee88c82255a9452a8dc3c35fb5b3d1e095238b8b9779b21238fdfc01a5f84031f4b9073e67e4d0379f973015fd8611d0620ae5e396bb3f78bc36afab0eed303befeb9ffca86e992c4b8cc9b995d36c5769f4c0dae4510a299dcf7de345c3adf25aa13a526508c435abb62c613c945bd00996310d072c174bb6f611ad2b1c0f198bca788fd9d62a843210d503ecf5234983af47ad92e7547114cb4341d019004859265614af4cd798e5dfcaa948798964ab97bbbcc679ea6a792c97d7519bdd351c644478c4dd08d3501e9c4a47a133bdc6e20052afb0d3928df1dd4388d0f27a8df4ef1a62709e635259e845e618cf65d2a34c6672fc62003956c11e8d23cb5df4fb394b08e8cb3a1d6191e4518ae81901c507999ab1300e1587bf7bc83a78a8c828b388bf61444f0155235322637a0a9240740f10de6a5ff9a4906d84db0d18f3675ce93a50e996a36cef3431fedaa648f54cbfdc4ec1f8ebe1aa8db76c5de9988bc2a7799cf0549482234f5aa446c8902dcf5224f5986015753837c6aa44011b0d812779b7d569b3fa3681c5695394166b2f23b1735a969be85f74ba5a76aa799601d14d4c2f5866d26c1b6b82632f7b0cd977aa148f8c039a75cd69dd447259bf00422d5065ee3f04111327c9c318000bea99c17d27c229601b3ce110aa082e4221b962b4e9cfa3ce852a4272bb16706177bbb29a910c7824e1b6dd28fcb8903619606460afde95a4b27ee307ac05710ed077b4f5a45c5280655179fedc049a3532f419e516f9bbb79818a3939d7c0bcb1904aeb95247aff557e1edfb9b18ab0d1fc7400da64f874722442c15c8f42947e65f261dda6c47fed1d05bcd7cc09027bf1a41e373eb455dfe203d5289afcc8f3f6ff9da79774a9ac1ee19a2dbe6676f339b84463be5a113075ab5f722a3c3b07dddd26008aa8cff95b4bc6be5cdde4d2d1348b0c5a21d7021423f26210743a911a89ec70b82de727b83e0caf63b1c747d58dcbb381fa03930646ae9e2f481a48017ef93a9ce7a065e0dd4059e3e1666c011d84ae1e1b97c74003c4dd43397401ff8671204792d5ae517972ea6ec4b02b8239ca27f182bb53b764bd1b5db8c3c5a02a2f51377849493ccf324e37854f8ec6c63173ed831a8a8dcdce843f29cf38c98e0f4404c9f0aa3df058603a6697caefa10c7d04388b20e51c29c27cbe63df16b75316b82cce048238e5a0e292a0bb3cc98d0df7c458a3878b0596481000b563b8cb22f8ceb42c91bbaa0216a9b88cd89ec6baa2d36e9980be06e2a8592c581877ecaedaf249b174781e3977dbc60c2e5a8583747db501a9d4ee9434952a6a27f00a9426fe1c38fbbb694445ecdd63b37cb0679b06ea7f71550c82a5dc9ca1ae457a53b455303fd4337807802b2eba342493812f447f82d78abba97426509fdf8375646b9f8488261763826b19b57fc3859b726e0e60f08448c06c087f5f142fa6a45fc4c5c5e9ed8ccf90f6fb08c47fe8df0c0ec0d1704a2ef2561ce548b5750091697215422db8dbd6b9c276237dbab2248b92b07935493d3a2dfde9aabff942f30465583327e6018d78529212e646f9b0bedc183c1b33c9fa16286d104afe7bc8818f570cfb14af44a97e9de1b8a0c466024c6dd14ba390f50f834f9736b6308e1e35064a582831d9a5709f494d9599a093da5c8703ac20645feb3ee08c9a1145d5a42d9669081cf1d0f6056098f02d358839714752e625c1bc397b9440ecfb3e83ba59b4dd896f1b1d990d1efe819de0c8018269766aed6c9b6aadfe0fc32fba05995200e5f75241891ac5c2edfeddd17d2a7e3101fc64cb739e4e92e89187c9ee4cb23f0ba62bc2743c5ff8f032db6feed263d3af1b224f56f291064f9d27d28b17323f56dedef85e8f95a05f7845114977e758414161b24c2472a489edb09f365f75b0496c1ec64e54f21146141755e56256043b0fd52cc31da306f55be07418b323f3f81bcac7ffab05a1969a485ea544820f3871c879369b0790cd766a94ebaf09d6236ce215d5c5528c5549525819c862f4a4b7227a3e582c6dc8153febba01efcf59b8eaba04b440a548a03702078c046d68a4c5482ca150ab21a351846c2a3a4ee9786154a2f4969616219892b6f7e02160a5a2acfdb6ac5a7a21e31885e9e8753e99852355bafc85c37ef2ec2cf1942d96153f63a671899aff395b1b9197a7508bcb796bedf3224b669b2750f3ed9d80aaf41ea801a141c619d154ca19065f4d63282ac0230f4dc1a1b3aa488a64371a36af0173af037e815b2b8941256c8e8c6ec8879d9b4ed5fdfb6f1577303f3e47a876ce37a9acd885e88b29824db07b004c90f2be662e733caf5221877016d0b8a41dbec3cc5e819a5cc188dbaaa86f498f6af838598640d0e1abe29a68194511b17064e46c1bd8cb2ccd4101b3b818826ca629858e9f0ea1f1b0cb45fb38115f047cba8737aa84cc73d618aa4e8719368d21064e4fbc6ed4c2b55c615a506f015d077ba1c7e9a5fcf528720cbab5ec3ba4e7c9612073fd7244ad5cf71158a8d6daaf3623712f8d40a1b3c88107a1af9babc6822c4d330af910a005042980508002de5b57e3fc57aeb3b17b7e40701a4a4188927b97b288ce72cf158b20ccd2764d6a015a3166d27cda788a8d47bc84b425aeda3da8a0cd1328ac915e047efbd851d00fefde036cddfe146a0a06c48df7a2ba5ac251fda2e3f44373ca3580230e4703ac08292b034bcde614f6891772896d022d42b853dd02e4a27301a1024ee412bcd2c2ff5562045b1dfb8f6e81e8052eead4f2d2cf0dffcdfb46031059b222b851da30a011df208a11d99c47ae1915f97d021addd0c85fc758e9cc70c1506a4ff7d38e71cec42a7284db68ada6770c802ea4c7c0006116d228aa36fddeea050f30c1ec3cfd5ca7eeb94a4824ee0e09d165fd9fd9834a55182f5e9c2b33aa6ac98f4566386c7f5be62c0bea86c3fd8a97df3efadbc23d9d7a5fbebe757673d0f1dbe7df585f20a93a50cf897d8d7ee55264286c8be21eb413fa4b0044e63feb5a77f45ac102b30793c002dee86b7cd2dceccbb7806620e899aa0e7ca38ac66edb779eb6c876a067aeaf53cbc9a3f96adcdac313aa148ec2a0d0263dc73b7b5eab20ced3019fd9798fc1ec79ba63b486ba2dd527375e72be64117cd05a3f9103e548cc258c3d586d06a732cd16a139643c6601e1945a02c0de83c71a1831a1cdc1483ea3dabe2fbe80ca63d4edb3e3d9d24184e6b9839e3ad1ddfa58619c44722a00b1d03cfeb27b9d9c095dc0e5152646a54fa93c31bde53e4d7dc4c496e70ed35ebb6a2d5e40384d57abccffb9955ee25c3b5dc9c6596e7a8646caa6209396783caaaa547043ef5a73b59c1138869344b2c4bb1dcf253c78b3a573161eb024dd1b014152909a02b843e7ddaad17b1ebaa8a9ef19b372c6c25906098e10149dcce30b255f16b7a839793a694d86cd73c604b369e33c4a2cba63e80cec7d68ded68ac9da782572515e7f16049feb44741655de98e6f4d958f19bcfd84643b73060d825b2219c9aefa94d5680a3ac8dadd87d73c5fd6dd9d142ec2dd720a8d628853908066c0b87c575c657ae9abfb21220b0bd7b04c7ba5104f1005a50984451ff29333ebdf7a2b3dbef5a4d3af0c5989514626dc7c70850b41aa613cc2c4e54e467583cbf34b959454213886acdff891f36a16009d18e0f6a2fe38782960f7f8b0b770ecaee0a28093d1684d3a306aca01da703511a8b603d26f7c74881d58e65bd7daad7dd02420948f363b6335bda44cccb58f6993e46efdb8eb2b2d3501469cbdb04fcf055b8490f19ba1b576864d76c54d8edd1072572b4f1972bd58b765883fc21b38628482867a89742ed708641a4e130bb02e36a6695db1b7ab8a1b835108e6480cfe4037d5872e84fb9a5ed044d6eb91eaeb1412c93a02f65e27ce40ad556f47deda3c7ea1ce237ad039acc2b8122c9494980c7180a159f4cc5166ff55973763214875bb952309c67508c14120b301fca33c28ccd70f63711461c44bc14fddec17d13e23cb11860585e0ad5086b71c712cceaf6a9748c67a754283c5cde87941040a8fb919eff85dfd32b58d598dafd5cc09aed6a535299964bad4c75429d35a3eb03844893f6ebe1fbfe761ca53d96c890b818628cb6dcc6f66d02f28dd2313af9d4a606ca8ac8b338f096dc35cad4ae0678861a8ed3b3c682782b7cb1115d819e43df31d29ca7d3b8ea1f6c9a657676769840c15b9d12fb0063ff9100ff5f6657c39a264bd22a168f1a5f8a4424ec2c8a0ff3b43255a2628d11880ade64ad74eaed6d2c2277c02e9f00cfaac3ec7fc7a1acd62d3c2403e7a640fd44b6757649cf912d628200175e9de12dd448c6658f29410c6e15442f15f60d73a796171fc39cc0de84f10fb656bae30490d238b88a3057949d9113820f359da38f457b0cb22598519b13f1744cfa60a675a91b95c23e7a5684f16e6f613709ae10ecbc214edd2208f39359c1966b6af3e16e6a6f4f56ac9dce36f6a020138c1c23445b15a8ea7851071eaff936b4ec5e1b082f1fc9073e7203a70b89e0cf5c438e56f68c773e3c71097cf912b608bb933f376202668b6a2276430c436eac5659e66c823ce188d9f87636c92d4bdda638db373f9ec8b94daa672390caa915c6bc3e3d2d8f2af95b5ec8ab1ca3b666226d824e58345881ead41cf82e9161a5455897bcbbb67baa84375c36eb82249e914b50ddbc3fda30c3d204b010c86ae6a0406334405d1a12ddf3f966a203a2c270ae805f5fd3a3f41f63aadaf00b8782350d7b304035031206f9c83711768f201478fe301f630ac7d01fc975f5ae9375e45123619dd1a6fa9f743796341dd9d5e602e2bc8e06557cd2301e7519a7687553c4cce66f72d7f3d8eff8436ae255c066c5e0e64769adbfff546c0dd86ae423e900433daf42cb6deb8e0b12ec97027e0b9cf9c50cf485ea1145ee5a6e3b1645d2aee5372ec12a8e79102632157ddc6198a4ce24e56553ff84b7df0fa7d7e5bc369694468b2ff79aa3de3d7d23c548a111eba20726e9bc4ac80079c71c952afbf2d54e40ac19469020068e9f1311d7cf90e615837f914cc13ca383171dfb119d90e128d6078439615450729b1822b9f9637f98ba84fe68b3a2b7aafc5820e6f0d1d2794f0446deea48415a4753e669c51c29afcb4e1f59a267f53eefde249441e5b6c4d5e2972906160de45c4534cb327043d53db08e6ed625c2b05d0aa75f9e024c4e693760e989c0bdff68fa86a7a2e7c5b67a73353eb74a87ea77655ebbe175697d499ab1b9a03ac011cbdac1bda97d672eddcd6a7e61ed751c7d5f5e8b75d05affb37f475b853172bce6b616f48ece833eb314d33cd3dd046b9ab482eaef31563939e6c8c0cac2a43476c0c795c92b24729b4082e093a596854413dc59be4738ca611aecf3ae3c007ed380c3e24759b5ce1af675b7c0f26341d5994f5c83775c4fbc89c61558b4767a390302df00b5cb0c0052ea8374aa31474505df09e81526b3c0ecc6b23f916452da80c5362e5c19bb2e9e78345f2af3248f20cd67b4caccf47e7c6579eb306179c810c46f0f79ef250a5913ac7b065a61bc2d52a942ecbc69e14eea42ad229fbf3b66237dcd97d8cac6453169c3e2abf288e3bd95ae3ca17cdad1057c67a15105a0ebc1bb57a76f7f65032e64e856a25751d82c9c70848853190e76cb6ed01ecc2be86488d167d95ef7865330490ffb1d7db7f0b6b62116ae1932d3a54df5167b7a2f5d860c96caa02846e8cd6eb674aeae71985f40e6d02131a3dd289fe9e85ba6ccd96ac6b3818c91b9625a52a2720e41f223afde8e9fbe5b485208713ab43a866923d7e8d1523208774632a7a54587903fabba39888fbf905b5967df61cfa07b833ceb0e3b7b6c897a87812eab8d37c1364d0511feb9abc35c6cf44b86a694bcd17041111960e9f2e149d5017deabb070992f04d4f713b20958db8e90780757833f09db25b4222dda08dd9dd8d8579ed63eb960aa1a64d4dc70b10305ebbc0b1d47932a386f6bbbc9b4a7296bde916596f09bcc30cd3b42b493a16c762cbc8f952976861647c32ae95d0c6617b82eabcd4eb6564df7e915a89ca8db07638fcdcdf0f719307e9ec4358ed3d7a18e1984498b881ecd48b035cbc1aa46db03aba5c8908c3b1f820140caa6a570828fa9c6d05ee16324df2fc2df285cfa58859ac37c5841541be49fde694c162b612d7a213e336165b8aa2062cf40b99751e1c56157320a69f12716fb697ef3b576188872f3a23ed969ed86c1a890d18d6f5f275cc31d3b81fa9021fe26c38bac4686649975f2283ead3e99e0c81bf369d39862c37505e08859967a98b45087ea40d7c503777543226d59362ea83591ea95223588a75724a42b22fbd5593fdca555e8a3b48a64420f1d05affe4641791518a82d27c26d36054fe1e479555560d98c68b81ec7e9dc188a393714f5f895568cc54acb510a02108df81c64a1b0d00d662674a7f13d137617c98465c1252dd553eb6c9fdc1bcafcb06e7ce9478775fa9dd102d50df7e0e13334a8c01c91baa5ae8d4d734d9635b4d8175150cfcb80f0970b8e5c94bd80a93faad2e25fd8f47d01c95e78ba589853da8befab70556c62dc3a452361ed74b113ddfbd420184fa29f91a7371246dc31e8e6ec098aabba4342a57c1eb298f945dded189e91b3c75d8f481eecdc8718a36b80962281e20bf8a1d65e6ec1a5ed451309e74a7a164cf7b9042926d3ba75039cf2cacbf2303adbfd9cf474e99ac4dacc7f05c863ca84ab39d70b06e15847d69d74b12a381242b879e74fc3aa0c4e5783178693ee54a4cad10c5f4849c242ecb5873447790ac1508611844f68836d02a24404609942ce29d0f2cda87a867d6c7d781ca1f0370f615dec39399eab919cbd7c743eb55793e66e9b656537c36e735327252491fbebe8f7a2c8e532b8be583215ba4bc1549ad674ff56d27eae49ad3ab99b29efdfce743dd440b2ce31dacec72d96c1e9aaa57454c1431ddf97dd40dda43c7c56d3299232fdd8599021dacaccae87d168d463b4818445a17054f134baacd1b50bfa72be3ec67d64055aee3ec8c822e28762052b70ff41ac2a0cb8c4862bac675d562d80b45411c1d566936e1c749a9ae4cefa4f7804fddce9a50845d4feee357b1c9491664440f19f4ea5430631c55914a674ece593ef2b273d1ded14eae04318762a7fe7fda2c6d04b30b708a962d302975a0a8b1b2cc21b17fe925b47cc0bfe556cd4ea95e55e235ce75934b799f1eee7ce35c8f09f9d71e4ef47814aee0daa33c59723ec3570c0a2279eff46865ed23d5ea1d7f575a84a78db7cf09c15adb94cacb5b486f23ea002a2d323a1d22208e6b998453a9d060f195cd7dc37f4d1a643978a3a8db4b1a2d8455ef923cd83c89b3c02ad7b4816feaa4dff0ca506d6d2dc49216335e35f81248f12657fb65646d6a6cc04fd51accafdcd95d5e44e3ef7b75886bca08de66f9b0f6e1b5e3170c64f627750c68210810a6d15c8d8fb95a0af0f6132f423d9dc82a1d3ddf577548cb45864a58aa204a82f6ef8c5b69e73fd127ff00775eaba9ae00e6d569eb6889f22a490a3740d33b5e96e4cf5847404fa7d6d7fd5f9a1e93ea3b7cfc45fee3d2cffde987394a49c8005b5342fac1d7f8af286b2a0bdcf319f35113b3f49566e0ca8168b5c9c7bf53baf707636bdae01bfdf94ca42cb512fab01717f61b372701f6a01895e2665b13f7bff2388914e8813897bc41bdf8fbee1c47a4ea4da127bffa5845e19766ab758137397219ca434c77d899ff3313ab3f7f5aa072df7c0fc76fbcefa54d36d88b990ed03adac1225cea0b109748ae4aebad56acc83168a2365ef97aa36844bb8bbe2ec0ef0d6519604afab83b2f7e19a882e9dbefe93d30d2b231f4383f2990947c159b01cc16f36a955fdf0d2511aff0ba038f6ee9db8a5a83a137687c28348a0691bb2b4e8fbac450b8e025faeb03f2b5c4f7f6131cb01dd9ee4512ffe1585ef2628bf2b7b1911a5e30a8338add4f04602bce6949db257dc6efe7ba11887aa2b321b3c355b35b8dbc954322e25f3b87a2ea5ee8ee8a13077f662a14505ae6f707ea0bd08962fb8065f7003298ef5b3b0f0080d2bc515a6cf7ed47948c61cfda85fff4d3bf4f9baf3f04c8fcba03d6f01049481e81e049cf6816b97052165a90d42750e5a6ff2d8f3221963744e7f95b281acbbbcbcaf2db0ca1716c08e4a43f338d3d61248901574cfcd37dde0f8cb17d3daf638a8cf0419a0dae251d70af90eac2eca9b4eadba6bd58d5793129ff305bc491e41085be47aedcdc01623497e72b967fd3d35a9c7b5e267e4fc488fda1f986ee5ec29eff5d9c804594af82ff3cf900046c06fe099ce8ff1bf9fd48019154bd79c3c4cc88650475bb76c85b94733d2015e43e1b3e14898a7d7202d6f047d1d9ea3592b12044df3a160a2abb7a6a5336fc6ef4be79383e082095c11ce3f83983ebc846e738573d5cee53d1bc905647aded8ed4ec38384e70d71bf1950be84abb6378aff14ce38cb6baae4bd8770e494f71e829143cb879901173b0aaf18180093524c6e88b07153c2c72a72a56624754ba33d270f204a3b19c67c1395365931038fa8915f282529b671fb40942a5c3b66508222a89c48ade79e5052dcbcf72ba6cf5e68af6aa76725f7e007409c2a7fd0c766decff13db474cfd3743356fa67453f41510f9935eaba64d2727b7bb6f3c699c156ba45df4f1485d1153ac49f5d130df31ab82608e8b07af0a647924a36f88249010a35c921709c9cbdd9e8d992b96e8d161ebd1c9920a7b0832a00cf26578a6a8c926b6976f97a289dd9adbf05ee0d496e8b177984554c2b4b0115087d69227966587c086b5178bedbf9b055001429ab80fbd17b00d40a2044a9040416bb065b868cab16ea3929655a61a0c6669990cdbec401a88f35647813eb9b2ff7fa488680936008344f0b8cc2dc14bde9cd26c4e6fedb4741bb8e637ce28d679b19f232dbe35b757cfaaa4982d13a03b51947e19d7cce1da778cb71b759173f0f74fcd4a18a0b84549dcf6329d131bd7cf84fb8a559e6e203807eeb680058dc3c8ec600b19d122605168929cd63f59ecab7b90af3f78c42a2bbf965afe9eec6eab0f856149d9d756443552363a326832d97c05e6f5ee80188f5f8c64637f822ea0f19b4b977d62f22db6959b7f98dbfc16d9559adc67c4e2df1993ee2e031d17b6f05f2bfc1ea6c8ac1e184528112316ecce59508c4f2c61911df06162c76d64eb66182f500e91f2df3941c18b52a4d645578ad6356478ad41dd7594bbaab73d5a0e3e8719e29b8df2c5759abc6b18bfaa3f91646983ea4822f86640abb2ac22022943b2837f7135b6d4c0d6794c96547fbf52573f2497f0d6f6285f2510c46a9f47baff5875d092f51727e7722a07d06040921d813b28c791a336b65100442583a1a0cb4e57dcbc2c9ed2061a3aacc251c1e324df52c8e357a986400c736f871d91c335a03dd336c2b77ebb9782e96fe4ba5491ae81fbd3f698302844b179a1ff984f46c00c99caf3ae9aef84c7c1ec54159bae578c989cf9e7d5dc610f0a97bd6819bf3284981f21c378a81a27994690cc67fff019be1417d5fa451a7a3075a980c94d612930303b145c2be66466d6bed4d75f0480ef5e9a15c9165bd3b4ef1d9382d65f3563315c6b4d77984531398d276619d43a61d88f0a0839b0fbb90ea4bf72638d31665659e03e46ba765645a893f204f0632f421376e37b2403e2ab1777141ee832759326ebb7704d89581ebaff79759c45f8afee0215fb9a90942816225f04b54e056e1b98542872e79b2522e2362b06aae888128d8b796e2bf9017b7919dd3f4e2f6dbbd1632b601c6fff06d74db2ba99d687376904331908e61f7d4cf59b8a48d5e844913bdce6b6c0f64718c32df5e6d9411c005a0f010596e6754a1e1a83acb23f2c578a8130089d5684cb94cf0c2c01db7195ea1a3213528f9f15947f791b18ca999e5634a1c81324b63f1a0d3b26817a4278118e39e7a6b66875803ba2a564e1224b921cb0cda181f643117028d8f98ca6797b9a86786ab099baffd5d06479c6e9996eba125c6500b6f4693d8a0488518b586558448e4df067840d902a85467e963b8dee44e4f8a1654a845cb7c5464c996a2f9357f0911b3db4dbee59997f52637b2645467e7eb4e1169ec84623a9f1e53abb51116d763e68c37ec4a309362973185d19d7c1c7d89147713bbd6b0f767d284a499bacdc01ccc42c5d898241836d5489ac90273e12e99f1b658ee4898028d098d4561a3adce816352dd95b9e438ba2b58240079897f138b1c2abcbc48887df935e6dc51174f675fcac87068141bb80197b7b810125a8a8939c64bae0cd1d1360f54510c2413d0c2fa2805e6ee78d9e3b8cd0bf4756e70941de742111ff194618f0858ba5a7a3cdf1ce3f958d5ce848b8e6df7962d8b52d658b721b26527cb6d4b8f24eed8b34f072aee97d766e2e9f8e824dc86353108d9b480802e18d48ed9c0dc64c5a9af8d5c3245d184ed0e18991a110c1ed082fd817773d921a62cea898e91dbe54605bd96f819f960f74bd46b081edb9f61f8a371befbeaaa9488e0a1beef96b427e3066c449af573d1c149407b3957d57980d35c2da1e7222cae4df0d554d3781d6b9ed91e9d6e61e7974b149b0e37b2e51b61d099620d9f2c02bad8bfb442449186583ad1afc0505314699f50d678613d30459301ec058a270cc556eaae885b5b691555755546b51f089cc36f79fa3addb115b749109f45a046bb1a324298b6a00209583995028940d4c3077801607433958d654b1036f0c6902e1a62d6b9f85fe78f7515786f7a1837754b70b722f9eec283685a2856da9e42ba473b12d0625acde91595e039450141e9f1edbf4513998c8ed9fae32384a951bf276140b459219e9d02b2e2094991c4f7ed4b96c85dcb8d67cab33dd28430e93a60a49eae9a7a9e766fd1f84216cb0df544e49103ba7dbe825e3090325207a6a24652417fcd0bdca9bec93430bda6fcad498a95c43dcbbc0a0fd89e74733c9d5cb22f15c6c6982ac67a72d300a000142e1735488958b40211c06825e041a65262ebb63aa97ba91166e007d96682d0de25646f29a54c52064c081108d8077b5c99c63feda6b48ef4237d69244f10767d5c49b1f1c3a036f706d4c4306c7f10c7b0b5d4a0e2f01fb263b69fb67cb11d1fb9bd3097974482240f6a529b4a7b7e57b93c571a8b51653407db200483c0b158412860f5cfd3a63cf02ef5a03615046ad63f1de786dad4a736758cbc7128c9048e50e8f3c6f667a1def0d8183bdcf72975a9329ae3670dd7cea175872d4d28a20a35e92e4d28424771d4bce9e8e3eea88d75db2d080bae3238e8fae16c845af481dbaebb7e0322d082c7288c5eb9bca20fe8f6497b62e0b608758775b3d6bfa904a83bfc75ecaac33e79b0b4e08c5238fd445e5c79adaed8a0a4264442a9e5604267ab89ff05fb1b7f029ce0cfe3160de9f065ec419ed8fe29918470dff2ddf4d50c8fbe2bda45187aad6f7fa4945aafd4ab0d63c9b52c27babe0f31445d4285506b2db6d66f89abdb8bb383f7bec0debf165f9bb35cbb04e76fc9672d687fc80292ea679be50816f3fdaac520b697c907929890ee2d7d2068fb6ca55aeb83a34e25754114d25aedbd388b3380d0f4431024914aa53014c5eabb4d5f87b3179f5629d5b55e55aad2bd943c456b71a8f32ad43a60e6ebbc91b1eaaaac4ae52aaaaae38ba65a6b133551eb7276c0641a75660ca14f133563087d0386c2c0c0c0d030a6c6c4c4c4d4d710fac2c058181818878181a9a2cb0964fb6985d271c34cdda854ee30ee241a850725cf94088e2eab1479c266e8cccccc0c955165c89021a3badce572b97c869d3163c60cfbf7ffff6697b3037b66a6541aa96ad4a115c66fa85ae9a8461f1f8c18b3929161f9cb8a45b6c8197a4d335ee260f6cbf7cbab9446a9c494bbb5a5c5511b7f77ff36c6511bd52802f5188d67445194e11281309ea1f56b4d6aecff8202ed7e31d662b97293fa4dcd0042fb7b097e9906beadd6a8d33275648832a4d02b33f4bdf56a65ddc1ab2f91cb05d2195eaf0c19325c2ed78c193344d8a6ff224992ba2c4b1a33d05e9ee27677777777777777771f2f5a682fff9f24c9b2a44183c6ebf5a2a1a1a941966559d21069d000af9b26b5a1ef618d1a35461fce81e86e6e1a2f7dc3aca9a9b161c3067843dbd8d88036c4b083d7cc4a87eda0031b1b1c38ac55a1e66ed8cc2042530b7358c536a2cda81302407477778b2fc6e5b538448a85f61206f3c0830f3ec89163d4d1513f9713c8c601136710a14f7106113870e048e1c081c281a3d6b8d703e042fabaf9d20bfb1078e41e945a9c989ad93d7a7ca26b076d41cff7566bb38335901e842b509cbc57a038d8afb5575ce1650acd8205e80a14a76efab2c7d08648491791e756f29b19ec87e9c5d67e26596c2fb6a10bb4f65e4b7ad0da7b2d75d1b40de92dc1a0c610e471ce39e79cad2cb1b3077272ce3967725f18746016fb36d9fe05a843636cd954894d89c608628775ac3fc293ca43d5a1f793a54ad95e81e041099595deff8795795d4223a043969f2d665650d96eeb96318711fcde4a2291de07dda407b241f2044f1776fdc00d3e251a43c90ebddaff4820bb922dd89d4bd2d2107a0b56d4fd383ab272f4e3c84a787361dbc12a04115784186765d4967ecb1dc541854014022e84dc1e004f08b810725a84a7bbfbf8cc67db6f3ec3e23e2b10bce5a9c24ad4798f78245a118fc4a33d16f11ef1483cea85baa07d9f0a3580e238a906d24842241e484224a14daa02d2484224a12d5b2c5fae93d76d5865fbe77bedcd38b48e6dc5d65a7cafbbfd4bdaf71beab638db3dfcdeebd7babbf5fb5b803183decd06af28427d73baa934a8fe6e6b60efcbe8621a61e499987f6e3209f479737343733621c0154a26c855f2551dc829148b24d3d0ded708a65439bab22f7ef180b30cfaa43c5cee13f2f17c783cde0a2bf078bc15783ccaa38992898c743bde1e81e2dc374352a906084ae12b0495640a451a1d51eecaee416beaa766ec0b8b2394d1e50584cb0b182298e299e979fd8b7ead404740023f4c3f78d046b113b4a6117e50268c4e344dcedcdbf67b27f1cbb9f7e51a641087aa21a44fca03792bbc7ea6f6f07837b0d30d19c4187a010d6100218613274e98cc006ed9228444122d608f9f9081b60657bbd85efb5fce47b6ffc4b65fcb5353d9f63f2bb6f5c7b6acf78215db0fafadb5da1e4617d7ea98bc98de8c367dd2b373a18f3a18e7efe13d8cdcc4375faf0f03671a046b78e0c87d1bf4b706594bb3b53d73bbdd7af4b865ac833ee94d54c110438020d86c3ef8005ad95de9ea10f8a221bd221e2e76f8425f21b5a2bfcdc28d1de5e8d57516aafb5f6da2b21fa4dda9b7b5a2417d437114406decdb0781ca16506bacd12a06262bdbdefcd8e9b74de89ad81ba4a9b5d5bfd2f010210b94da501cf57d5ffcdd7bedfdcf664a29a5f4fb9efa83abfa325d21d0f4e9574102bd5842a74b9b160531028c20434f90c092141530619a42bf34a9a2439b298bd634220dfea09ad098062854d0a353410c4e74b869910d8c15cc000b2c5222d0a44d8b6c556488a288cd09120816d99e8020088260945dea0b5b939b2d88cf67a5c56c070fc81045ab362d8a414b119af72fd3c8100cb722b078a1c74d8b8ab0925ab1983c014448089cc0010a1323447cd1c5c5810950b36ad841c70cf1c8d400c40b4c13deaa86279f6ac92ca606dc4b6ac9104c0d2f784135c9a96ac8023cd15235ccc03484aa618c926692238189d149cb30be18b133d9607219c26924a393dccb902822f1e312050c91301a894a4e78a20d2f20dd76a10dbb11ecb125d0062f4a240ced6c18233412c1cd4809502434b119f1c144c2113dd508122f55af8ca0d91a0a1a69e28504247a46a800493062c89fc616983b131e488193a01e0d8844c9c938cbce5ac8200f4581d16c5a34a4c376d9b468688814580c8e7a4214c68f8f8eb1f184b144aa041a65c5a9091da3a50623805d25ab9b4d4f1e38d7ac4d896cc06475bb10e3a4050b235014edb4c94911142768d5342eb67515a18b68db9a76e3116f7ff461ee5bb2744db40c28763efca1101efb4a24316f7f67c863f8f54602f3f677a2784cc411796c8ad14e959d238fbd94ad5c2251bd69cbb6262da62ba69d86d27353205dc22807a567db3f9d4e32466474d8aa950c1214cac7ddd547476decb3743413cf6336b67d931697fd6cb64fd34e8f40e7ee2ea5c56322500f684b108f893d1e43f90415f158ebbabc7d14c9ba42b2766e6692a5c3892daebcfd1d4fbd7cfe4bb2aeb84b79a6b68e9cdea1edd8766eb40ada4ecd63635964db11f2187e9dda2dd4ad43ab70922acf479527ad22cab6b40a229d9b47be03aa0d519cf05f6e7eee86a9230b16b8312db29d7458494223090df3aa87a9ca560a2b0ec4c43c909c160aaa1760ca560a3039aa0792533face30fd5c33c1d538829612b185245a2523d46f09f9f23457ae07682dcb49f1a7576686edaaf752768a7564375c20f2b53fdbd2a84b7a05d51b55d1f45dbf57768e09f50beada928c4fcea57e50b314072bc3ccd5d8d807427b8401f99766eb2748eb15f9e1327648832029d157c751e3b4d3bd691dd77876669152a11f498b9ebc3a0626274453d6bd479793cbe0e2bdac5013d4a651fa5b2d4532a437d0f1b92e70c914c9127b83315dbb67f479b0a98a7e32f6405cae91daec7c4542b99ac8362fede181246755b5826b35aa206d2e737dbef7f8eb85d7b76f51608d1366d010f5b726cda02255cec96cb895777779ffcd547977d8e761728670434a526f9a93b41be43a338778badd6a85381726ec2ec2b3490d18c0c17a35422bc5dfd0e9161ba600e50457363fc7ad4d1f185c69f7ff481ff8e08b82302549f69beb9e69cb3cfb89c19d8f791f8f5814bd0e59a31ea549fa34a6be92efe30a2d7cac2dda68e2fb48f5b0aed0a642351fd2d479a9bf57a8def66cca8a26b059aa50382018bb869bf1c757ee6a67dcbdabdecd3b483c2f3b2edafc0b34290157a3c56b75de99e9599edac4c66666b8cac2ce6fdb1ceca60de63b0248ced7f7b56f5eee16db3745cfcd4da92182cd9b7acb521d79096f9f198cdb62f73c4653e576482f4fbf06c8daaac3e47acddf6f24909f4b665ab1779ac1dc9523d65e9dcdddd64ed606aec67152659476eda5b56a0dc0b133fdbf675fe6e569f231f9db93d86d4b666f5332bbbff32ab950ccda883eae12558a08eb131a0b7e9471f16b0638ce152471f2ee37f8fdf5317144da97d5fbb99ff561f9f23232239a3497124dde555c5d0ce6300d8f659575c5681722c2ddb7ee82c13e8730c725e298e636dd731688fb497e8b16fd7d187eba64fea05affa1c79ac05d3e126c5a1b00aa4dbf6665b4abd78d1d4802107438ea686b57687666531a5e6c0b54427f9a138f78774b6c6c6053db24dbb9ae4869a8496a4c685ffcecb33bc61fbdb325f9c0bc1cfb67fc99fb08612a23895daf89be208c3aea30cbb8e43760d43ecedfa1efb0e745c48e821385d8ec7c04d6d9ee65a9dc45fef0a8b3ee917b896082708f4501bfb4e923b4709f4597d74748b0c43a46cfb75088ee2a09e6ed9f58e5c566118b235f6fd537f561f2d5b7c172b300c09b1eb5b27bb7e856168db28bb7eaa3c2b0cb86d5fbc4186dc3e3fba65f7d9dcaca832ef4c6f5fe000e0291713fd73e4a99fed22151a55c962ca92025551953ebd231f9d9b51b4ea61fea43a05f0e814b0ab3e3a2a3cdb7a0c45a4a6e348d37d56a0dccc0cf449a19881b6068ad9a61e336531262c541653eed46c8d7d180d4659056d8cbd43731d51056ddbaf40398a53daf6593a2bb32cddc9d26d7b7eb65d7535d8a8036e0f461d5005f31468074356209dadb1af222b9091ad09758875f4714bd2ebb0a20d300eb9597f8ce272c273cf9efd753c8fa11e488722e97607ca798ca43b65e4b17ce4b1d42e4556a02915a84a053272b3021db909b4ab40392374287ecc9b5ebf3ca53bf2bb602cdb7ffc2c83ed8fb7d8fe2bd23f86f4579152b67feec9318af06d5c7a79defffe449a484dbe90e7bf6a6cb13cb1947dd21b797eb35dbf449ea34d05186edd5284d3cfae7f7eb60fe70f24956e28da132aa5828959c9b05a33325c3ee3c992c6abd2d4e0c0acb1d1c10d1b00e0a03097eaa3e379ecac40b9ead3b3329c1c0f0f0f0c393a8f519f1def8ac77cb478acf2788c16f9f47c66f8c9d519826c5b99d8679d61761b4114fbac331c01ca55a0291ec334d836c9adeed083db1e39cfb847ae87d12d9321e042c0b90bee1fc618e310723433095d4e1c6377d1ddd5449fe0adb55a960e2bf468dbf541f7b18416ea46cdca121cda758cb2ab639142eb3ff104152a565881719887b5948ca2e06f7b89711f2851caaf6714073fede5f2d443ea8cdad8c721c647251cc509c1a31d8643614df3c208e9ebec80f7a5be32ac798dfd1ad6852ecad8f69411d65ccf20659ffa4a6ddb9f8135748f54b92bdb3e26551f28a17d66877514e1b46970d4c6fe074968fc1dcc284efd2a6ed867abb76d07b31c3768fff375dbbeee8f8e6870a72ab7ed5f5ca35663a8466d9f76073c2342facae16404fa5cadcc5babfd99bf01bbe9418013506800dd3a9cd8429f58f7bd153f92cb7f9f49f19263a923a74facd3f1324f12fac4ba7de4b173b5f3d889ab605dfeb03c53fbc4f74be589b76c5beac0f2cc41b6bd4fb3cfaee2b1bc9aa147fbbe0db77d5616faad9518569754d3a7ded80aa694527aadb5368f3a3f56617f83e06a754b6a2d058f7c5fad3b28c1f6fb6add41c957bf9c960f2495beaf5622b552582285e2f7d54a44a463257fcc35067b7b14b9d8922f2ac325cc7564f15bc96890447172988619643842a44ffdb9e62a4042ced5ae2420f409427112f8f47b509cefe97300236548f84fbb71f9043f89026e6144a5ecd8f403fa6d4bfffdd2bb607affd6014e9ffa1450af7a973f6badd6ac4c55ba606bfc05f5e189a4392892e684d44dba65229d3ed326937ea70909e1fe1ec5df8982581b9091c7288dc811dbdf44d24ea4c8f6f7223a2dfd29fda8079243a2b9e93d5880d91aff5379636b9ee6b8cdd69c4aaf7900fc698e07f9ac65446b5304fa96a4b3e2ced53e6bed0422c476e114a24aa7d1d2850843e3a7a412ac3f2a50a29dcb96ad5585dcf4afb9e9df22a27fbb0e689f8e829f404aa24f9a43f168474511d2d7699aedfce1ab47bff46854fed37a4071e4a77f002aa3d403aa6d1427c7ce0fa338dfce5bec6c4596429fec0c820e180758e8aa4d8f9d2f2f8ff9b77250b8dfb2e50924d88dadc9ff91a69bf96df982ed5c29ceca34ab6ebf08a995511dd2d7295231fc7b2ffda1046b4235212374064171832dec1509e2b641890d4a7c28c424fda19d795def58c20536e94be4296ed204483a0545bb0945838e25a4b5572a8160f84273e8695eb7d8332ebdf657fa1cbe25bf27e5b7af4915d8df7f4fbf1c20b903d79d0a2c802001cf56adb5466efaea4d0a8651e821fa869d4f907bac0c2c29ada9df43734f8e9babdc137ea41697d0b5d481e0bfce02f65cf9675bf630f36cbfb55ef086a05b6b85f6796357938308f8724029f4898d76a5426bfcadd05be02c8e04bf91956110d47a6574710e0137079799c7e7f236486fbd29c8a59b8dbf9473d2dbdfdd0cea0eba2f6f9b86f4a94baf3d76a341ed02d32fa4cb0dcbb735a5c73aa5af2adc94a00122415278360645b86fa94862206f5c4e80de1ec2eb24d7421281f4296eb7a4ff4ccda2eea0bb6ad92626fad4f63589a400b9bf3fbf92247e6f2959cbf3fb48ba417b4378effdb1a31ad421e076efbd9706489fd4685b5dad43b3baa3967509c561df96b5d684c8b665fdf15aa321aab56a493bbf3563af3f75472d25406fc581a6cf7afb7eecedc24c2bab3ce2036ccd7d0daef4fd616b2e127dd69bf81db9ac005e73ff0a4db9b2effd6c56763fda0f9937186edf7b8df6fd74fbdef26e7a795084a16b075da76c9af320ac958ae605c2d0e5449fafdd44535ae3729212ddf4ead45a7a03d7cd7573dd5c37d7cd7573dd5c37d7cd7573dd5cb75a6badd5d64a29a594d22db0b5d65a6b79b8895ff6cdc620d01a7c436d30286abb9a7159e9f1bf3cf672932491da9ae48d9a73ced9b1cdb95cb98979dcc42d58ab4278cb3d41dcc49927f3f06c8ccbb3bb8290de5b5f98980185a443e88aa26de88f97b4802dbd14e1b4ef4e8490bece1dba9ddf52faf7de7befa5f75a6badb556948562898dc2325b71df7baf8cb6f39f62ecc80b89648add406b72ecc7c6121a3605666465e1e787e960472e133f3fec8ac76057dccc9f5f2461470e33b2301e4c8b9bd9ddddc28cdca4f77bf88b97309d9b39079b4213c5cd7b2f0d8e268a0d9c3e69686e345168701eab3b976ef75e7ac5bf7ffff64a7fef2892d4d6dc0fcfd26de79df3b3c2d83983ac25370c550e1b5959ebc76bf267ddfad91983276bc9ce5fa545fa73c60d6cd4a84da6b955a1b9b999ff0325f41780216a93ff8324740732ece0a138fef93bd8edfca5f2bc5bc8b0f3d9c1cecb1d3cdf9fb0dcce0fcbc1a61cedd2e70fc010c509dd468de2903e3fcd6de70773ceb1dacea53f633f3b3fa9a64f706752499ef647eed0b9d4245fca479d93b5a3cec9ea6c636d3a9260238b46bdb88ce127863d4f9de088fa10f51fea4554b699743da5dc7e58ddb5027d350e1ae6635a5bf5f8ed8ec446fe3b34f7ac22c19bf3a8137e7e9713adbbcfc318638ceff75fc7ffc8f39ba98079df726c42909b26d0dcb4e3f89574d1a7a70a6d89d51369e1cbe8f2e358ed98e3891669a8f2dcc06ca3dee544f34ecc73cf99971245318bd9e78acf4e4b6fac3e5674f8a748bb2a609e094126d45e483dbad0b67d138a6aae1d4c3b6a639d6c3bb4833e65662e9cfe175a29ac1ee6614a0eb4fc572fa38279a195828a032d2f91b05ef531af7a9a03132343561f5600ec48cb912d122a4fab84cab3ebf64d8389d620a857abbf30d712da59e44ecd03605f06019e0312d93ce642780cb37431774b2e05985795d567676b5250fd4a668689d8dc2422849b7687a6f397d3a3f18ce2d8f699b209c9cca2f85cb93243ac90294263d5e7889ad6b44b1b27d0a76977ff44e1b6adba7dae76aa4a714e3bdf6d6efa334d80bbf25c4ae893a533ed4c57bcc65691e6af446a14e712a1819a7686db35ad847e1a646617d50ac2d2b1742c1d4b7753e4897395c8d0d90ab22d912495c86ddbcf97b563f13c76595a58bd168ffd5690564fcba755c463a9d64feb482bc863ad0b34c528572357a3e745eba4300a65f2a026a630c6d5839a139d3d7b8c7bbe37ef644f619407b50e8cb67d2b1af515a84671dc666530f5e680cb419777ed3b9d469d70d314119bf8397b2944a7a2ecd03c669becd0eae803b5eb101c0e27ce199db48a28588aeec4ba7dd61c74dbfaad5e6f6da5b0a71c3b908eee29a870d433d38ec76ddc322e2bd07467fba8f70f51a4cc8f4c111f7ddf6566324e43cfe104ef78a9ef85413c36f6f814f1d828ce70a4ce10e4b116b62f923b42e10e6d0b21f1ad7e115d523f86a7173f45ee0851374deebb02f14e1eb76dab780cf5faa53c531be5529e15e8ca589e1f966705d2b22deac5f2ac405b76683a8fe1a73976a7e6260a45ea94ae55a01c2ebf7c79dc62bc1dc3736269c1c2c20223f5a851c725f595ece1ae24b4c8da91992d61e4b2edcbccec0b11975478dbe3a7bec8ec963e5a6c5bdaeea3a33e57509909342f0a2609f6d65a4b223d1541f08a9f982deee25ad15a2bfe0d631194d2b1883a2e6175a1eb5f4ae9856daa0a816f5adaf4b429fd7b161dd1760b5e37def553332f11db9b022fc662fe28a55483a452fe446b2dd5f7de7b29a525916492a9e24dcdbc4471d4b1a538d6b3e5dbb431c6263b0ec15834d028d4ad621436a1eb62124f17e78c743be76961c686132aa542046abb940a673b83c003dccc329b59933ea954aa0b133313f6ac4f1d7fe07030312f2753cc0aa7050e8763c1aec7c5155340d1dbf576bd5deded7a4c7a2b1956bed8f6f008d95e47b83d749904559c8705d68709ae35c3e3a6763323a3148a25ec908f33aae0bd6196a830758c302a1ce7b89309e59ae1eeee331e870587c3e15890693d2eae98028a1ead47ebd17ab45e96de93382c381c0ec702177a5c5c310514bd5eafd7ebf57abd2c3db2bcaad42d69dc1c0e87f3b7e16b4fe335e3e6eaf51a75f0bf2e4d8d8b2d09fc72cffa806455812672d3eb14eb537754291487ff51af8af3b0c00ac165c1d1a06054b61942c4009d9af87e16ebae5589c537e7312ece17831fbe994422914afc138a2fb52ec9f87edff75d9c67172fa95549ce39bf2a0d35714d02b625a9896918e09273ce58b060b93f4a6a557231be24f0cb405c4c02bf8cc51583e501bfa7914c43325e646c923e74ecb482d605dca41301dcecb19b796d93ced6b80c18aeb33a5bea74b92496461f4ef57612f62a206ef2138a2ff7852b8e45bc0f954251f4a1b0e4b8c9e822be8438f86b3561fa65e0aac260bbc93496f0b2337611e238afa24d251d3098e9f494b44d6248e280db67a85949b83f3fff807d7fa778b5d69a52adc0e3f178aa4a65f50709f3c995cc4f0946b0b25259714d6a105549051a419cf342181a53a9b5d6fa1bd1a6d15c89322f33eadc9761b5eabd31715ea5e2700d82e09120f72a7547bdc1c96ae76060cbfac363a54e28175bf2cc48023e785715d40f9ae00b15374522080e810f92273e478624a98190d9597b4aa8d842ac500a6ae881061f94e8b959a15ddfdaecd00f43f6696f3e39a725b239d59fd6c856b1bb1f8ef66979560bcc9f760bcfaeb7c767d7bfb3fb6343c13e2fed06ed7a6dbbfe7987ae931fb06c19995f8d3a32ab8f197556313fe4b0631e66d4898179d5a803a37adac4567d6ad451a502804a027563a3503d106d940f306c540f347a5424d4e4eee0c2dce21e537bfc3bf240c51e950c51c4434f2d2a4ad283a6425bf0da2412922c9b54630249d103a008090df9f1a893414841131b833665b36d5017db16a1e0886d534064dba743a60c10998a5202567fa56f502d9739e713740e62e7ff2bb918ac288981a5282915fbbcb18307f6dde1887d5f03fdecfb2b5a04c464dfa22023fbbe1e756e4d13b5169d60c9cef95f469d6c83c7459a92293b3f38c6ceef32ea647d7327dca4d061071bfca04382022fa4f0417c6ab3d2f2620d6b9f33263042071af62dd261b72f6dc8be3ffee78c23d65214260822072af6fdf0899b5338fb9cf162e7bff908d1ceaf8b7200ca363e40a194000436250822afdeabc430836cdf079dc00104fbfea7a5100125ba81c9be354536b6cfd4ce57ecfc9906538a6e08b273cd39d32e827a18e20a1229441ec8c95a76ce39e79c4fd03f3becfb64df209a0a233bbf8e1abb023082059844550be9ebb4bc7d29ed41da2fb2d4295596b4573b6b0de751fce6de13aad9845a506a96c74dbd7516da77cbfff42dfba60792d3f24f7dcb5a5ba2907a54a557d8aa10116183219838caf1717bc9810bf459bfd63c768a4236213c565b0748fde95348952d7cfa145ab83c41f5a8a739a9ff6a2ad267a489f4590b7fea5bf951f84fdfca65eb00a7d39b3ee712f5a97fc1f4a70792c3cae96aab42291245ae48176c0ff3655359a5425db0b25aa34fa49c7a97c25383a0d6ab1eee51b9350995599bfeb03ca9ae549e940aa93ce913607952295f79d227b93c41c0e56992a67dd69a267d466deefb136bb3321f4229d5bd94a74542c5a53ccd27a4ac2c08a5929aa4f2b42f205fc667a0977ed3592fed919bf73998a2dd2d6f7497293ed2a90225a2a34e09044190047e89f414a0447b02e067cb83c7a9e3f59a9949a5ec0d2abb215299486b6cac7eacf4f4edae64b616342905a969bf528b6db9ba7ff72d530fbbe16aadb5d65a6badd569a55ea97984aeef454cf19283299a7e85e930cd92a83dba4475944af8277d824aa14fc03f41d8d4b4260fa8ac46c0ca3ca0356e75d58819d25dc97720d4256876d7126654a7703025a713ebc5e1bfbc9a85e26831f1ae6d5fa17d6bd4e6f6dcbc5fb5f02a8fd6a35b73754a3562dd3fe17f95737feb5af9ea802598e4235397777179ea7b7f750a6236bb623bd9b687db3e31c563f67ba88ddfa6e4a0e89aab46358a9bb87a9381ca60b63a44717a288e13f4041476c3109e5fddea2d9f35f7e59acbb99c29477172d41faa4d9bb8e2886dcbb3eab6bfa88d3fcccaea13b4c6ff660beac4f69a0b43fbb06f98e6c90656e1dac6c35f64f7493a85126d37b9834768ff13f6a6997bdd622544a3e0af6f1427c7be4f00978132d267bdaf066d7d7f5ab250f7b9da970023b8a957d4e65af308ad539746c46c467368289eed5f7fb6a5449652fc97b4807df5e850f451b70e5d54b47ddfe1d75d77585b7555dcf47a6b6e9ade82a26911ff99a2ed5723d2fb579dc7783cd622a1fe07bb3e90edef413c26b3fdbde747a592e35bb4b8d1151ca6a8b95fd57ce631d1548260ca4d56165a0a116cfb33bf4aa257429c4671824e348a93633bca96a7dbb6dfab89f4e97ced42edb1b686f6c22dbd664b0fa2bdf0491880eff8fda3d9da4aeb4b89f6579e404012cdca44afb1f6dddaf0f16842f8b9f4211dc7f20422c41ec956a16aa8644df74539eb188900004010003315002028140c078482d1683049e33c997b14800c74923e7a5a361546931cc7711c0421638c21861843083088c80c0d1151100010d037a6ac0e00ce389c80d8af25de48408dd6d93cca2448dc2f1af12387edc5ef618981a2d6c2ae7dfeea3b32496cb4635c554306c80819734bc9114c4aadcc3a6c24c1008e25b40785d895674c135a1c721d41311a4623f008f6fc4155bf74663de7436d0a91e398501378dd30a857cebd422cb5631338cd198812e6326737afbbd611a9ea2266258209893be30815bb9b2569d87e03236a9508254824964f4092a4aa9bb83aaac4bace1fc8855338374392035617ad1c125d1be2fb6a62b956ab668b0285e43602dcd45e557030620fcdc0b7cc86402dee2d2fc7d92f0f92698929aedcd96cdaea79de14311d3a88e1d0db86160e560a6492882bda1bf0fb6e2880b8a14e754c84d4a3ba5bfcc83984c3e64224be8009c3b865d14b18c6a75c03c0e851f811597cf4e2f387d6cede61d3c97c656ed5c92dfb65da67b56ced785ae4f49ef60f9195ddbb09626bce2c44047f613c544c83f9227b9202e8ed3eef8d9b84464786aa458372b90c44a8749d7a70463f4f5918e04b839d6a7fca8f214315c001ab8c5d47928fe090049ab9e32977f11244b097da229e629f930342bfe6aab6b87c591205b8ecfa5b8b1954865a52a66c907ebc0430c3fa2fe4f21e9ac0428f0d66604f2bc6880162e3cdbff6d3de962db99ccf8af4de40ff5899a1d35f1ff0d022abfe780ff99b23bc748d46ffccfb99d27345debe6811cc2c65de1022ff995bb509233e5904692f1cf7863bdbb5bddd703c75196cd3c89e132b15938a90b1b2029b32b5315f8cf196c5547da0cd25671b860a220fe5e7e725a79c681b3fdfba832669f61b15f3b7a4f2d089f52d62dbc9bc78707727456e0393b71cca52620d77b5a8e4eabc7dec75481ce3afc88568f7c965dd6b1ad5496bfe3d5d06b93bc425a2cbb3a939bb965f38d0bca3d113f8c76bed7b5015fedd5e5f25955520a749e58a7c92410205f961f8c4e588fb32e530dbc3958533cee19d8440e77c6c0ae7cecb9a72618040ca0daa5b1aa0966066ec981b229a0ccefc1447e7a2be327abf7c1f3c06e0ae223be7162d2c4c9c00a27c2c5a41e73669c6df74a14be2c0e8f054a35b0ced6c8c7430db7524290ae7e6181d4b2544109219b8bac805bc91e180630f47744983b36c2e22b7af12a68802451675175cd30be47c11284c21fe8270e9e4682b9ebe373d30b872ec1777ee09e497fe4d11344e658444a33365b9a95cee3783fd6e39b7e80507192d6c975c7139b347a3a519c72ceb9a62da3edbe35843c0a0b96158b495b1e30e52b5457d5593a120d2fbee612a4606226b8dc7d4b511cc9894bdc51359a15bf5c55ab71d29b51bc90f2270d7722ccec8158797d4af5e267a00d08b894465caa8ce236d718b2c32dc6e65e78302bc7ac7e3302e5ad813ae34e55d7a816b95e2820942a9178e7d50d66f648c2c2ca34cdf259aa7067820b27f008812b2f2a90313da47c35cd576a77e52bcd52d6be80f0e8e59dfe4250d714f1e3c5b65333ef842b5884379d9c5837bced99a2db8e404436e11338cb6fa5edc1291b272cc38d7de4241461db4e3b8eb1365c5b404093de955ec78c064d25ab6f19d7dc061d3c8931cf7c08cc62ded67d2967bfdf9f3045cd49c7cdaaee063bd9d8e71b61de80077e11bb615a7bd7b5ed74ee9b43386b2aa739d73097e1f1a3e576358e962996fe3a0a6c8bba58ebd1f326b0a7a13b8fec19b4d9957e6557f082aa66ff1eca1b15e1dc2153f83650fced798a7909e4cc9facbea05e10abfebe400bec413ccc8c80e9e43825486472433f8ea263ed66cf4b2a75e7ab057cc3f0dcb5db360763b817acce92c7dd3426270872f04b0f3cf1b8ca5117dad0e4e1c4420aac73dee668574cd24acd7008f0b0a7087e6fa389ef5e190ebd2aea9c3a6de2669b35908f6266420cb215bb09e93c035e13b0306b63d796a9ab6d2f469760dee09eb369f75783ecca669b8470270f2f18101fa928905957abb426bd50eee187f7e09966b066d92ee144a2be71813ab5017b0e2040b97942cb9d92de224e71a862f320f5f4b3e65e882815b0588d58232ea226598bac54fc93938e3bb52cffb6250069e4b69adc1181a31125b6af522db12f94baeb3aee75cb20860b29d96bdf4d006931bc02a77312449230d8fdc4254fbfe9050e9aa4412d62b477f2e2fdadf79b821b90041c6250af362f82654e7c54f97475665e4a1aa6ff82fc314ea34ec9a10b814b125a445a0dc7645b16dbe36ba443c67b90637217ac5d488ae721b0c1a0e351a8c561ca701a97f936111614c5357e4cc0479e8d11f33fbf107666eec7ad63dfbc4ca611aa3c46e8be2f58bb2bef6fa63db3618225cd57a443c4225dc7b3b7dfe1eb494ed6d9bc892ff659174d8caa7ba42d9a649093bc1a76c4f6affffc2bd0c0f08664ceb057ce3331d5776b47d06f95682cfe3b8828510865d18ffefeff5766c19f0ef31808efef1d69a3bff0991f416aaadd8b8118a64b48038af5e5dd0550c6d493456b905091252d7dba3d350dcf8574a1fb068578e106f054690e92c127417162e50c9e554b1c9b55161c9afafd195d549b74b38ee72ad0bb46a72b43855d374ed4e5520492f20fa7234d44768429f2c94a8311accae148b8e9fd766d08745880a65265616dc41b14c341223aad687361f5f8fc17aaf86304e7807ac7d4c40bff94d3ef0685f77e9122b13bffbe5af2344c48c84e8cf4d668fb6c9c234343706be9754a407ecd9688b8d17d751a3a283e59b98cd5daf7f165546daa782c1e1d19641cbb95136833613431e33d0fc935814b5387c29efec15fe716da82736622efd9993cadfb0074333f28de6681adcdf557ed848a9b3d6546f4174f8df82ed90c1272211f589d3b95b4059ceb4e8b90184aa78bbb46b045f36db0300e676726959688ba66c05ea2b099b110ddf58590c4ab3c46e2324d91853ce4ec62d9b438f09470b0b6a502d40d6ae1b68346463daec9853ad54e3c2648374c4795af419db1c4ab2bd25a18eaa8464ad87f8a08443a21956caca5fe29e7122b308404802c9d1894a02adbd99baed4b9372ddcbe35c2da4fbf502b12602f6b8911476917a06de620af90970b9dbdfa38f374f100d64980c04dac4238e57bfeb0a270ee9565c15a09d5b2704b32c27b6cd3c1825669eda2f53c47a14d9344dee4e10ca2bb654089870d8159ad029f7964a428bbeba3d54fc423749864d3c977b884932e8f06f957b01018584ffdacab36f4e721073ef90b953dcad569ded1ee3d35ed856e8adb35b774c22b66d941c99ee80a46d4dc87f22aadceb7197079872efa3e9bd2d0b63ea575e5903a008186e41c8301ff79a11a7ce0d3f59871f8dfa832865d496b012bd56cb5aad80d88d82cf6aa848d1afc1e570cb2b7672a93dd5db0ec383f58d909ff6bb9622a6a2314a7130e24c57f825fff479f2fb688518a15a710d7d5b7a8174b0309c2f71eabdb5befc7301d6aece70fd2824f5c60b3a699e3e4bea2d1d37d3878442760a61ea1d560a2bf4d45b039b1804056cfa9bb4727424c79b669cf91ca4a8b7c00897a95c3c8320102455ea4504fccc9d7a634bebdd33f1e5a1a9775457baa8f9b5e3931fa6a12a364dbd07ab064472f85e9b7ad983ae23cd1e927b264adeea247f6e3618ed0d294cf8587266e5957eedcd66cc6b9de6d7957fa02aa2de90eb51f552706c211b2db861380986f12f5b07c9f7b1dc4aedfa22f5cc52db43e1765d2012301769286660d121565633830f859e46e9236b893ba33c38ed3757a9c0d133486e6db94fc569398825655b95e5357a014c6f31060c496a085631f25515c313f8749464478ed01f3d9910b5c6fde6659ddea8f802f77558f0346d12a43820cb03558dd240dde74bb542c14824b571206f58e93fc5ae32553e0eb13cb7fda62ebc54adecabbbc6ca76be5a387a4d109f250403056e615dd37a261d01af8c75b89e3c75f29bbcb84e720ea7676085a74fd893afe3debca9c8001d7ee7d298c64ccd1ab025fc5f090921d346e184f3cd94846412325a6adaa81767d305a81eb21448565a43ad97fd6021457db65f0e67d6016af7803c66cbd29a7bdc60db0bb2c87be0fe43f773a23debc5d2c920b3e9a2a2662f9c50c57acd48773ab6709582376cb29414cb2b8001164359d92d4ed28870bbeca4889046c95868763902b9dd4183f85833e67432e9e78ed044a995b52cc896d676c75244f24cf5cb06b4f92f271e8996f1db9b059017dd5d1ddda74da1864d67a17c400eee201b16e71bc6db76ac9ecbfaab5f9fa1356a35ee92710ebfd96aa51244435f002116a3860fc53a3beb339cb167fad2fdfb217fad647c54d3b5aac8d4b2bd3738e94852052d190046f3b5f094adcd61d32d2179e1049c1750f1427fd1787b9adeddfa94b9dfe1336be44788ac58b7a976c38b4ec0f5afd540acc55c84e51c77dc314f9e96809d6182ac7be7c0201696f0670e291c317d60df321b027e299591c7f590428887f85d7be7645a1116d480be7cb8edfa4aac5417c896cc54737e52d6d913574acc2a2359d1269e848fd298b639184e5ab319d6c67e7864e17acbb735ec15618e6a906d4195c00b477f51a1bc9229f8b59c36b454e29d84e76cae2dec6249710d00299b594b05b4d1677cbdb79164ffb9c3e24f888e465c4e7e62916b9d1efdd66ddd14c4f0ac5787e1bd7eeea8060693e39b716d2e6bc2812619acc763a94ed33d9a3107ffdf847e8ac58eab3a8343e460b2a54a6d5df95f6c4ca02e457e719829d238042d954b9d1d1247bcbf3e2beec99603521f73ca0aea7044bdd202722a7a65032830ad778c043aee467ddfe3b765eef871027994aea99b669d81211f0eadbea0c2400810d43d8fd57c4ad63360f7c62db28746c63b8288f091d18c76cd6ab85b0304f96e81171a6bdd6817977b3db48b2c810264c230dd8f30fb9d7ad0e731a7bbe7617a04f626656743a2b3d283c196a5df70f978521a1d72596fe2ee2e4143ade15e14967aef70a89e81cce482b1aee93ec6bca3c4cbb4fe8303a7191440f706a011c59042dd3072f34ab81b8a17ad9e40bb7d172c4a8478b87014c6efacafbef5cb19684381c689b60e8173222c0facd4ce259ae1cb416398360117c0e2f1449f86e0e30b60f64707ea35d580c7237d52bf79a0ea420f4240b75a3aa6843ad5eeec18f8fce043a6b253b1292094e208d9ebe6ec5889290aec4f284c360a1481afa25c38c58d02ffe191ade37c8aea6a83508506e92d9c043911cfdf898b63200348d9e34baae168a824e0fe9788f31236d2967e230e49b9a2218c98862e527291f70ffd1995aa1ae76661147c53864383eaab46385368c22a4469b81e304ec7155fb9c6f1c93f404d8eba3175c7885ef8832e81fe8ac27a8d2d791f72b3a8cf020d2d7e8198b1e0c77a5c6193aaf48fa68393b156f279f90289c5ae367bc350a0e936218fe5a90eb99646301e0d71afba7fe92dc6d1066e5054dcaaa48a79a6eacfd09d50c43154dddcd9d035374a06c91e7813f4b4da2deaa604c2d1170df6b8921206136bf6c16de81442beb668b0fd68625a97f37e24ab12a7f2a1af300e6c67a74620221020c95ba203efef5e68567e44bdcad23c4af504027c150033b9340bac1499346c9b847bbc61ee967b536c21701e239d296cc74dbede856d4e0f052d8bad18cdc2a83d1cffc326d1467bcb2d64c7b81e37b66c47d52f5261795f8aa9389279f514fde1ffaeb7f20fa06c72f293e5e1438ce088f9254fd104517eb448dbc22a4d73e1e2ab1405d386a941e82100f2a23ead060ec09dc44721df2ebd6c040818c65154e9c909197e00f6967b90a5f3f732990ba2d6952a4da27ba7c8a38bafcd0a914a721ea80dd33c07a128c2de5d4137633d4833ab927bd65fcaa9701eb420395f44b196886e5a9f4c408674c93854cc0fd2fa8ade5f27d501aaeb5c0e5a328dd30d199a820935e624a03a1fd38d72954a4c8573d7bd10818c54b9429b778bc1ee5ba634e26e04d7da1015cdc4e1da57e7eb7e06a3835d62df48359465f927c02083e6e422d369a29457ab2ae5fdf0efbfeb7e95024b18729f5e006c43c84d76a1710746fe8b75b28dc44c0793b288cb1b3e81e577801260b51f9edcd937ac073c85cbb87fa158791c038fa2f39720fd914f7e87414ed151272760f9e523c1171c0dfc50737dff0777369e3e4f77561f90ca97132cb7777c30198514fcd734018e8cf7442f012591803baad182770530ddd3bf1d0f9a2acd006323da393837524a5e02cf5ad952236cc825c5f458096ca8e6a6c2cf87990422dad39fa11fc1c76d205a293c8b064aa3e987668a3ac8c47069be50982741ce6f6a2683dba550c5e0b1886a11099f82c8cde1d3a4a17d080b401cb224334ae64f11bd0199c27806ab52d62d51de2f0cedac8863d1cd406c1a6f6a82fa10f64846dbec71117cc4513d0ce0c78c1a45bcd0b93b788355b030b9b56bbcb82b69041f73b9000151afa625f1902ffeb7e2e23274c1c4d2295c328d56e589536b54802e629026bb3b499df2e6fe958ab4e4ac012660c11225ef031b7f40befe3b13d5479aec02cf863d56182a182c7345e50803b05e840dcf03b8d135469f43c7b7014000083aa7de9ea236135fd9c46930953017cfcdc44efd1948ec1ea7741ca50cff6ac3384a16236a1af41527f26a3ae7631b44eab84b0191e14c8b49e8f34f15b8b828de7a097d567b98a602082cbacc22fd90a0619d46ddabbe33fc59da01a02eb12a0ae102e0a20ba993905c8444049e6ddd2d1e6c8d147e35d8f8e33ed87489b5b5ed280732a139b3cb7932bc32e07dcf8306383af8cf1747c55db0a90575d34b14325f8099220b2a9e43e11464230dc8fbb079730a35160b7b82e4199e158f6c09e27f82685e52788256c3e67bd2be185e56c60c83195dcd180620c5a7b7c94e521d2907e8fed840ccb695512dee894ce49f7bebaac2dece3e08ae80420205655bcc62fe5c68620c438b7225bba9b284b769180565a93c7389140257b5f7658338f17e85bfeb0805313c86a76c0023edfeb5874752834f04c022e188dfdc2604d76ca164b518eb4a09be17af9025a998190c37326b363c76d14902472515536b0d70b2bfa58832bb496dadb97b7f6c991d0e2ae049bfe52b37e3a4dc0ac8cd17b93eab5ddc65b1fcc092845bc8d41ccc59168aa03eaff56fa2e5e2b90cb488e9f8bed64b85225996242929c4d8b02fc515b69c776d2062049447d7c42cc9f0f3b28c46c6120d4c18a21945f2a7b21f9291802aa535a9b314715d0b189151a02353ee2bc78dd13a09a803d71bb2b6c1fbb0c422f4bdec3ace652709f4b52f6c2d804fe9934ec4caea0b88222ac1d1d9349a6ffa2b11a9c7ee92d5ed10a3f508317f335d58391a4827b13c475c87a91ff1004820d3b759d8f776de43c72e66899fd8919f9fdc0ac3e64fac13cf611b8c611d08051a74517b33649189af3bcb86884e0d20eee99e4fc78fa0b13c9e1db90e611cdf9dd99fa0281dc2a8a42cc248fe2a6c81c7b7830fb175c4ec004bd86b04719e3e28a7c651a870b975266c04156052aa78a8768d5d91dff5a3056f0eeb4e76a84e12bc729368c3793c924c99e7c422d59ae6f657a36671d41bb13a1e1c5dbb973e431afbd33a67932aa086b699f25ed4f3d2960586353e16dcde82caacb4ab2bb122dc1056c04ad9fa9afd6100b54a2a70799fd1ef5541679453115427d98e2e4151260c0d4f8f8b78e3971b61c8df8705c4a96ca57c329ed79878cfe21a8d2e78094d7c7e904b18dd6a0ec7dcec2972ff00b5117590c6d8fd1b6503e03208149397326fa0945127b7ed08412eb47926a75f40919650c1396464a0d833028ff4a73cc7b831a03dc6cf15b7872acde609284b88105b0f4ff732aa5b2997936e70fe37887139d9f94969a5afa865f12a26ddea14790f6c553cee62bcef16820dc0b08492b828c5f8d70db4a924a82922a4a902f0d3ecc6aa76634993d6bc900bb7081bd182d30250bdaec8314f5b489cc2c03372413d5ce9c1156299be30440795a25bd01873af6b72c2cb418ed29e2b9a62d2d1818dbaa2fb54346a074056d7b9aa3138f4acae5f199461e6bc2b1852168759fc408c206a99391aad25b4a0d1d89f0b884c9075a3ae44de0dee09cb5ef73755d67ba58b0e28564ee529cf2fcccae9ca84ea7ab5ba2d25d0f55c1e570f19b58ae2922e139713549a6f4b12091340905b69d603f515a3331855faf770e306ad31b456814bc4aa2fb7fc86f3fdc63590d334f3678cf45cd7dbff02ed842b38941f851525aa99713c4139b313850860cb96c29e60f5e253cf8080c019b0af96a16daaa8ccae962b566e1fb6506589c11edc4c440defb45d8c50a9506c46a61dbf3b000cbf9fe528d62d1fa2cb35d6845fc394406436066240afc735698fc1f27804ad041eae32a5f68e1ce7be0bac91d197b8179a87a354eb927ede33a8f1fb1ae93108cf203fd1d75e43e3983a96000a4e6d12cda967f14cf84138569f3fcdbdfb810d00e252054d368eb379afb51e670d3c0861c6fb53288384f0013da5156d1644ca3e4caf76e4d9fd27f44724128fd510dd771aed377b8cd24076d797d6977d7069f490c62a4fe283507dfe7e60ef47caa000fd9745af63f978490445353ac96ebbbad35e914d5c470ef61e1574730d5f0738808215ff48792735735527aee428ed9110bccdb9241831cd7180f7fa3d26cf3f58e4a3114e0e04ffc4ec8fac616e1582c99681795969f856a9605d632d2aceae08de95b446ad59acd1d0462968a1996c4849bc0244d9bd237f22792529d738cdb442c3d2bdc9033e7d397669225680d06a5c2e159358e7a3e5ac0dc10f6c3136d4af970fadb216bd620b9053a51365f9bc41fc1a96ca3ea37a67e36bd641c334c11c1384fbab00bbd0f120b8d64c629a203157922eb556ed753a4bc928d90c07579c1777b6ed1ff476fda84703d6151e64cb12e0908987376b29a6a36c59e9ee88d7dbf387ccf61c189929362c425a39663b9938044077648f8e8704e26e3ec3d82301dd964c3a072fbbee894108f97af6fb50c4aa923be449930bbc59aa044e5d6b8495ea36d8bca57a38197cf673b520559a5b55a1ae2fc6486c3105371c418a1311f092531b6a937f2ab26758a0359493c80642159e7e0576f64722d26d404ced0583e05939f026b15f5a32c5eab22078318b0af4695f46d6887994e31e340dd04beec9b3bacda4826135460e251aa2c3c49d592419a21f540e4922b273d3b0c4f803e6aec24a1fc3627cf6a8e286077bb5e8b4d6ed25b051009f4a21118796880f44c37ea364453a674473621bf9e4f6857ba044cf10c135a9da9e5b432983b791f7c43f6062ddc6e897c3d650f05028ccaa2a2107559e97317e55b4024f351e6ab555c3ece381ea3fcaef3f5637f77f90e54e6e8f6d93a17c3d79696a353a37d10bc4c4b0b9b08bc75ed142cceebbdd521ee1e9aea446fe27f6d62084f326aca89915727c40f931a91b5e49b54e070b50274f4e53725b69bd45486eaad11c93247642bf692bfc46ea164ecb3d23ecea38a529e5fbb7bfc8ad39b4111466a20304eb19c281e4be3e4bdc1bb7eb3f21e9d162cc1281954a48478d6ad61c5a20d3cdfa4f82f29de474eb07c82f446aee9c7ebe9bd9e5d2c57403d6228a0d5061e017202af5ff3f095e6d669d7bb875270426c2547a7369e70a20464eacb3749fdde8f06b8f079c4d64b23cff407d7f7dc3ff586f8bdd1dfd9922a1e400b297ef4ff607fb9f73a2ddf3e73714908add0c1dc4ee499776f95796aa374ccd5cdaaf070166510b9f958cb67b47fd20516ae95c23bd1e9fc52720094193abce9a663b3da97230d9f33289680e894fbc3d2751d4cac9f9a9cab52b4fd46f060d1fd248193c8c8e7f26ab96470370b7229b2d1b18ba765803dc19d765d86487a1c3003661cb17934d741bab1cef40832c8753b2547c4734c54ceed936060a6524a7c17e9202b4de48827b69c97c9a88901587853e725bd2392dcf4e2e8721e9e2e6d0df7199f4b829fe84ed0bdc0fc795c8b6dea5c98c9af43f774e5459218e7d8202e7f17bb83f5a3cfbdd01843c0a25756c3a3a5ab57d96c9812919f41ddda118a8d4beeed19142194bcaa87718b5582f4aed01dd00dc5c5c56c6f5fcca3cd27c7c1849d26e0279d975a8ddb2531ab8e315dec10543b3b8f8fa279ea545a45f38195ec399ef5d0530877279c917cc545411faad101641b8ce2bc391f6c0813122673a638b5684631c75f3f76daa12608f7dfb849df4a577ac1c6ea8d79f3aac83e9bb34c3b32273a3f7f8981fb773915b8288e2c448eab506f99299daa60054f3a9365e35ff0a37019021c7f57ed06a772671c1236b5cfae8ff4fe5338f24403a6ba844e6ec283ddb65d32eca7e9684d8d7aa522c7280c079380e6979b44272b3d28239795d07c07609fd0b03acc1e2a7d48903b8a19057de59957d3288ab8535646d72f7303c68f31da44ae6ed11a7bb348800a93e9dd72b34231a9df56059db0971bd44fa596709660e5b105f20c8bb5469cb48c6693319e4946bd9d3800bb585288b2da760fc6f913e8d36b22883c05ca5ac4988e089e56f2096e6fd5144905ee5802b3fdbeb45169052b93e95c01d30652f12fa0e2213c32bbde7cb96238e806dd8042e0d73a628389e807380ee4ae88d053341df2f5ee08bb13e23d5cf7311b7e254147dd0eec4200cf5e8d821d38b5100da8fabd0a2883f0f1ed69f9c54bbc358c93691b17f7828db86a47f9361bb10188f0a4b6688119b6f182e619d90dd608d3cc0656ae4911da7afff72d6c4f436d1e8445d839222efbfa99952e6ad2cab7c8a14a63f2e9b574f8795d103df16bfbf9ac979780c107ee956a075c7d700b143cb988916b53941a40b34c9a985876bc086cbdfaf2b14f670be0f0a53b01da7517b28831a86c4a844cca4cd5342dc1ad83399800bfeeb953c58d86f9b81e7f1ddd7b60b184176efc80dbff3f2202ee322db26032189238eead7c109a69793500768fb25d5d215e01800bed0a2e84b5f2bf4730452309b950464af5c039c77bf0f47e4ea01cb5c1f425acdb3e7df81b623bcd3ffd11425d85a9ab5ea6bd8ee16b3a046b9a2771bd01d730af7c567df57a4826493c84a20dd0f8c60f0a0d718e049bc066440095f5a20c4af7bb63d69b10d0299c382872254b5a9c600510850e0febc49c64336e8adbbbbca7fe845128ae8531cc911f19baf1e6bb23c762603a4bc2c4118e62d77c19cd03ce16de80d6c1883deb6340d6882db232f73b7bb2a9481487b8145f8709cecd0ae3ddffbdb4071a459b61b7dbe6f49f2d0805c583c11d696bff0758f0fe657dec7b09fd4e9a749e72f00826ce5dba309295e484bd99dc9442ba9a004f744f0bc321d3ca0901c917fa92e44f71e1ec1db3617920ec929aa49b716a8c951f43e3f1b6e2677eaec0927d702af1c898ec84b470921f0bc238dd5c61e88fd123a22355bec4afb3999314e8eba8ac5e21059f6ef3dd4d21ee87322b94d18be1a048302d59f9de5e490c0063b742b8f5e6e358fe106077efad85f07ff9dece0c8124f06e0086162f843a7b8e26a38acc6fb7f55477587355871174792b32c2101a5b83f441abb011eb0f5b6fd5156981123e91b979847e2396403b93186cddcc7f76fdade62c172ab9af09d38c1c4eb1487082a1e83234557a1a4433739efd3dc5fc2ac8648c8818435fcef26ffab9125e53305222f1216730aaf2dad5ffc16188ba6f0d418660f7ca24217ac6c45216d70fbe20aaab0ec64139d64afaaf1a0cbb629db610bc2e149bd57fb87ae2956ae737dcf1409b8c71e631cb6c8893f03a6403064c04f309b1436d75ae2932124d8c89b1496656acf03a9bcd7bd70c143c732adedcd87b1818e0f267cb04fe0c08d361b0bdf2a1f57c488fd21ee15394c2d76e9f08521b25600a32d69107fa579ec10476ad9e243c971a9325b23074bc9b5f2d5102c12563540458dfed40571c6d78d8b133f3ace04410bd2f641389c0942b890c616acf95f5336a96349e2a8e078e1dbe7be2bb6cba9c089abc044cb7d14b9e63748b3aa1ab6e91c834ce98d3b20c26fa557b4264cb046f0ffe76a1286f27c53ef5c664158cf02d0f2268487c224b250a6a4c58973dff16e64a404708dd6fe5cc06e21ae6495631cd4af389868c866e85cee848e04119011e9bbfd99ebe73554475d016368d3da67cef1bb1b5bb2cbaa9be4f8377d8cc236a1b5ea107eb990be70ea0d59c7def9ec84a8234a2a25cfcd90c4c36512bf8f748f5f3bd5dbbdb0a5a9945e087a133009fc0affa1233795773396866d0265dcb1606c7d4e42be3eb4e07df5d3a85b3ad6cb8efa68d8ed8ebbb31e105e5c428d0f342a719b080dee305a424b03b9cb9a244f371a2a0161ed1268b90dd24753a1095f70e9625c37737f4a3c03d02036633237e4ce3613cbf9d4797b5388a6e66f57323b6c0060bb963ae97264801d4cc35b411da9a7cb9a7a116b4ee2313c7386dc1c0d1e04f7bc14b3fe8522d7cdf797e678e5e5c97f72ab54b8c97a94d6f75c006e5eccc823ab6ccd2b8f4af211b2f586d56f6cac23ad47aa29b5e9b08f63b4535b1b377f568c612987fdea1a903ee4aba892a1b84aaa6b959401e56262a6b00bb3520404ab4a99f61337ba87744b5e4443ca76896c6180a7af9fede99101005d2017e420f341f11e61b462ef6c65fda1d1eecce3f0974ae57b3968ecd3cfe84b0857a8f84dae16af48b9057e506853fba7aa1fd98c66edae9ecf15fa6e5c88c13884374064b9e0e27ce68c0c35776cdf1382d43bceab37721fc846217910d6a2f4d1aa8f26171104590cfdd1c95d58c0ad652f73a02caf408ca33233156d9b49648a211167a55cba1f31d7374d551ce4ed56077fc0c51c5397ba89255741e2b143a618187989d47721ed99dd8178a9bb8faf1212260a706e3527029714824f451b42dd1625d3968e4794a7002808e3a67088f89a41980fcd7d7359724099cedfb2041fecdd45821083761c40db1a952dfbc92faba7287570e1e1c75df5641475054b15831df6e76eff74b509f74e04bb3a0a44fd61123047a275b34408c47ab8a977dbec524aa52a13d396ed5f6b2122f4ac5e592652b17883f543ea85b28deaa8b983ab5f77ce8b702c83226f8ff847653e00da4eddc03b935982b2c3690076c14f8e2dc086af2e6450080a80afbcb86b152bacd50702d2671684b45cdc442d5346105dde075558570efee1568a8ca6c39e72a62a8221e15ecf6257f357df51da5b83760b527b62db679968412b9902cf06830c697ce264c70e703a58bc34a08d8256b0808b90583b35c8d9e151b8a68169fb5f56de332117f2a8316e42d72aeb337d541294252c404ed039fe056eb904aff1260c01838c92e43ef5690012a0de8741d11973761809e8603f506b55d492abfa1071d4c0dea998d48e495841dc2bfa7854373053ece00a8fa780385cf69e07cba348dd835522206765fe2f8ea96c80367a0a27da7171990efb325b96984525f1d5ebc49aaccba347ebc5276d56b3955c780c3fe71a8d19a30c138d2f3de6f2be0db027d1a644af1d7952d4ee4ea270be3c20640300bc43154e303ba30df8fe60bc62f7f896ef69b5e364ad347346557cfced9dbae56c11d28edd67f49a557c99d8cf5ced7459e1c9bc805f07b1c722b3fd0284d30de49331120d9bd8ba857d4c5c6510476f672be32fd7e0198fdc2591041618e6220996924233bb54f9725eff8e4716ee8a3483829b9331e13ac6685c5587ed84e86c5107532a75693d0e1b60f3f1efbf1d88718722edc41039b09804512acc14aade699b874bad683e13d6dd9e8ffb9452020d3632d19742017bb6ba53c60144716ced0f487165ef1a2e97714f564f974e60c504ba8487d826be1caa2b853913b307bde83b31276a32bfa0438ad91b53bf6d89fc086c63ccc19599250f5bad9d65b838f71bc02725dc8b49c1331f87fe427e1321d96efe12ba28cc554da25c620cff19b48b712606847203433a141aeb15fe8ea1480481bbbe995ae81e3e6cf823f3b3190502b429902d5f8c619073a7c4e1cac71a0b3812984c6863175cdeac518e4c802b49939724cdd0e4b14c92d85571de24d76a35f1d49c54157589fb91c731d5125a8aa266aa5604464d363aff4bb3dcc03ada0eb38ed21ff60a15dadf01108d71469f51d987ce70a6667676724e20ebcafbc845a477b2c31b15dc102f615341d35d626b2f4d5a24d85f89f0df513c7492231939ed80a34264b335deb8a71997896395deb44d6d93feb9283e9260b2ec5d9ffedf76cac58d2633e9ee91bcfac793ca490ab129ab147ad244013f79f365daf410b096d35d5dac774a9b5aef832bd685c7e622b9d0251498c0366145cb692788d02615f7a01ab02492945dff2440451ad8385c08e33ec203b266677791b2317abfb4d2c4058f784d29d53a03c93a859b33809e453d888d907d10601432063a72d7c985f687518e8508cc2d55f67282db973d36c9f12082b3e0c80bfae1222768ed2a74f7e345e82e088400c917ad89a47322294e25de2f0066992a5e019954a55140ef6a622879a3916abe9b0746e5ed6a5c26e97fde25f5839b8063c0577990574d884718f3b214a03be692c147d748fe24831d509db26d41aeab244904d8da6a3ff8440df432e44bb2920465e0b5db645307e505005712ae492d5d76e8e7aeb7338684413e48df45449634970a0df47a081609f03385fa824a37e5b122db11bffa8ad08ed2faf42b566e8e356241dd24b7125236a435f893100e2fc066b3bb8da93511783b88c31a1437cc8c9f5e668f1c78b1eb1cf1d53889d15e450280a902c762626d8b4aa632f96d97d47bfe3e7929b346dc5cd5a3aac43a97be618b08c97801698bc9f018230a8dbd57a2211adedf465488c5116137551367b213ef3233a2eaca7bcc187aec439eb9448c9b3111f28a008769a6e722ea1773cc11e76c0f7e54550456a371d43a78768425a4459356ae4684c2be0441a99dc6ee30e0b9723ee68bf897d14d5eda2cc114b462ac9b8741aabd59ebcf0414fbcdedc94a5850328bf704854068f2cd9dba5b3329363e0bcb85ed2ad67677ea43d0a1c3bd241769595dbccf060a83f3b1f515d3d7c6e1e25459267537cac6b55cc775cba92e7aadac3f072593ae45edcbece9e0c63cf68b8ee150f96573c2af8ffffac8c5eb104aeabab64842d384fb95b38dae2b04e6c9a3107d19db06bc9220fbbff184eb657efc1eff379d43c4a3708d8260455e518e68fc9d0857454fecd162ed284460715ca9e590c9627ed23641497f63c82ae506500d4fd486000e28a507966f06f5d985da858e3863930d7ce8e73b0fb9e79789967f57b16917ee23da526c2e70dfefa69aee321b03c212586baa62130b849bb4ca38a81449859453390df5f92ecd6773524e26253862a0242262ace9243d4ada7449e7e1a1cf18a9f26cc5de3d8268a6cb58b559de77c9aa9e46311bbc6be679ce51d481051c6f0c0aa58e3b15815154c0cc3b49b93f68d5a6c0969af80e5c4479275228a97e1c5cc2faf3ec994d831ba99edbb071ad54ac39346f99dfc59b04cc97fe82eeb042ba744463a8db9e5d462a02dd61fe700384987bea0ea0922510094f7d2307c5a689dd876765279a572ab58273d25dc1a237c447142cbfb01551af5166d711b503509be46c46de6b94bea343507d338b35b39edeab7b8b6f21a055b1bf37c9b233cd44c76baf83c20b50168ce1011f784050413f64b34eb616d042ee217e40475f61605774565668d87f67fa105e92b0164c3e7085d9b4e48209ce3228f05f47910581bd9fa7b0f426f37afa8ad74df725ef6b4f87d89f36fe57c878394c0892c5eb2fa601996da653c4a008a9093180810af86f22cc10b827fa222041f139b510580a3156a1e9ab3c1b1b7271a0d8480ac9d4d0a65db9ee1fdf37661cdeb254fe2bbaf72d60ac65fe2f30370933c54352f7094ebb4cfb13ddea5ba152d6c37c411a4f730b9ef046211647cf17137ef9c1073a8f74d25581b18016497c3d6f1032be1b875b8f0524c0b098c004133d141958086799aa268a74582fb841bee43a0e5353975c885ded95bd88c7dfbc085c74264012a1b48f5ec0dd7490c2f5615f1a6f16ab229dce19da67132cdeec0c745a9e485a7cc35c1c8181e105875a818fc3c2329ea53099d09a2662b4bcb67f5f63e2951c5aa55d080652945f41f19fa51f3f588d0346bc0b89d782ff1c62eeec54cd4511cac8c2a2384db4d9d628ee1291d5b168e0820e239edd1f1a7dbe6001b318c380e497d1d1e2629922154cf9b250ded8386eb87622b0400d7a0fa08735fed450302bc7d8674790387b6e8b7dd00eca1809d92719dc1118256f7e80cf817e964989619ad55f0fc522861b07b4a3f23c28749e6b8bb892768a847991d401696170145a320a630d581ab72f941dee5cd48541a175411669eacfce6718c033269aa4e7eb16b2b4999864cfba1c0cd080f088cc1492841354308a8301254cc713d3b93e132be78f2037c451de7c81071a3f058eb8b02eeac4d632eb27348a7f08a3d37376172d6180c05fa0dc14e7be00ca844cc16bd92e25e722596fd21522aa2d2e86dab00ea875e48bddeba71b81619ef1dc2ba0c1b7cde2c852e77a13ae32422e632baba4f534af475dd5041994d76ec59fb44c82557678b546f0a3ad1f2ff462a9a34c6350b3fd7fc3e4a64aadfbb9478dd65c51b3c27e790db601c527ea28ec9d3bb8b9c7ba48bf1cb32e8eb08c5c5701855cd0edb5f65a68613b0a6b56bac8cce7018323ecedc9e98f33e69fa63c50a538659dfc975cb1cd69bdc9f8b6123501f1f3aa22cb1c600bac5383ed76f9b20cf8358a373e8c256607fd2c4ddb68d216792ecddae4285cfb5ca679918bddfeb6fa1493cfb66a8f82c231da0a30fc2ecb0c3fec5ac6df142545f4f55d9c6099df0516db6427791b4f5fdbbde60ab64c857a2e665707e976b3e8ccea5afcdafaf19ca524fa171dd4981904632cec4f4a934afe9c0429ebd43810031c69e18419158f82035d33b0b8b866df413a60750e36ecf351268948cb5369fe4a442553bb7f1c1ec162ea827272e824f25ffa9b1bdba4d4334030d0a78526b0275c9caa4daed40c2609700ce149b2f3b802e00a4aa63aae702702fe467be59fe0d8154cc6310d834de88157602cdaa051c245c59d401e583be6cf87c863c335a2abff43979cc88abfc3fc2ca5808157b623f502b4c5bc94f383001a7156458d560987625230350c158a1ff21470abef168a8e4918057723c1e8b87c563dd253032f5345ef2e614e9653cb849a66346952980dae17b152a42e62430c007cb34c5a5b06da66c0fcdd334806664f1bf08bbe910d7caf869a81ee4674d8e817062f6b89219968f9a00a5e070b8018bc163ca4d3a545ff203cf6cfda32c3f032b056a44c18a9424cc93c6946a581abde9066a5c4ded284b78bd564e13119867f996886714c8fe852240577066e76cd2718244b7187a98ca1d098fe2bcc66f0b9b53690716e58ac5f3172f41385f985d60ed868b597ef64edac924819d0e5c1251561965fe1c2fbfb241c8eda51043b310060f8ae2038affdbbbd63f78168d1f13a13041e8018c0a944b27bee5cbbdc29c617617adf8c5bf70846cd205f0b0f84bc10682c90d08f9ccb2db11535838377cea78ea0cfce3f75b22cbfe3ae490d584649c74f25a891c817b282be2e5cf567932415b982b2c37177bf7b3022869eb2e385d18a14293d5a4dc400d90560ac1be4b916aec2bde5077f0c60d04049e5bdf84c6b5a19b5034d23b66de0c5b2d5b66bddbd40548f3ea8659582f0288bfaa9e96cb91343188856ce29b2dba1682612a4cd198d5faeb8926c54d90b2ae84b78d86d520567aede7d0cda6b853b9a52abf2e7b21632952b1f669eaa8899bb0f2bce553710d8862d4d12132872fa15515b77c582826e4e80833dd38fef5ce4a1d6edee992ba973ac7da2ec57ed207eabe04e2c1fbcbc8d4620e522a97a829788fa7a3fb31f156fdd08e637915539d5e964c6f01d2701285701e84a2978a95979561f7cd8fc01262ff3a79dac992cd454d10a346827a72e5804fdb9a4f3c31abb961a4093f106ee6286e0ce88b0a30b9e5276af6b0d83e9a1e9f3d3f45e2f814b4e553730969f93867dcb14771acb32b5efcb0f71dc6083d215ebc2c5d1c8ac7d841a09d91b29d7592a075b05bbf3a0c851139209aa78ea8be2ee70135f95fc22bd13c11e19fe6f9d16cc3a7aa4c9e3ea48fda6e180c8f32ee065677639652a4914ccc31b7b33175121ac7a2ddd9b3315a40377f665b6737f91ed2dccb7325be3b6802c15dc6dc21fba49407068fee94c404dbc8d689dc697441d05627bc77d577787b4d9d0d8d8b9bd6eb47afdb12dd0b0696e1612d3080ee2b39fb66ac63bc7aa686805028ad9aaa158105175304b25167107958eaca96e31d6968ff2c89828e8639404d43fa49cf312c579299b984b2f859ec1cfa6bf40146b574d82034f830ba0933171bc48b18711315881288f7cd7715abdb352693c1216bdcc897055a8dc13152d4976c5de708d00c4960b348287aac807cc397711e9b8955206a90b5c3465885e18867c16e6dd629eca45f6ea13bdf6c667ccc823da54721e54a5d0b35fab9e354c0774f7b294f95873b5e22c0aec0a4bd328dfcacfa2801cdcccabcf6b9a9627d5085b6a81598ab0eb29ca94285f140f98bc8b51f0ad921021447d0616f90cd7e3a9d4a584b3e9408173bca5f987aa5b9c470e02709b5c462753f61d8ad7f76f20aa41660ff96a91681da68c22c77adf62d259160de286583a4ad5f12c3e53340ac96449091b356f5f33bffe8ebee028cd206cba365463d687be9b7255713845a9c190c842142a5da14634e2680359283029954b0abfbcb70e7aaf20a241cbb954ca4d9a0b389a5d36a9235405462e8956b56559a60ff4d38e98164dc0a9abdc3a0eb2937ce72275b810e99b289bde270e4374aee9946e8f3fd5d25f7237e46540e69e90fe9a2f6532f889d8f6bd2f416f3e05dfb86303f38c95013717aeee39a0bb54e37a4ead8e32e85a7bb5d14d4b2b893b9d6e15cfa6a7151beceeba0c6e0da8dccbb5ea8289f31cb1c33c712a2842d9ee64a6738d19c1f5607d23595d751e64873da754199d32f8a03276aeb00702b94a651c4766bfcfab73f9a8f56b3a21cf529ee4fc74c5d741ac856cd76cc8ee23b88385e1c2c3be4cc8fa9b10602b1f8f7d11cc0a4a498d2495bebb6d1cddb36ce2a3d90834481ca6542c9c888e74ded6550dd97aaf4db0c25b8f4f5ed80b701c5f777301239bc8aa1487eccf28392d8d5ea656d8b94d616bd0d87b6cd620cf03ea059504b1c3693c952a2fcb13339f73a69bf57fc8e6e67e9ed464831d7834c832e57390fafca28b336f46198c7233401753ebd7d147484e9255014e6e0b0d59df348926b67f1135d2190d482d8a6da0bf75b297c6602c1c2e18577b63226b4511298642a820b4a9aaeb0bd8af105ddd614358bfc36e0f78d66bc3ca28942052ae6608c9f4842df9deeb28b084cfb30670ee240bb99c56a9c8a4281d275ec5b3e636759c04f330db911ac15d43b8268d77fa396322396a6951064620bdb6774646f6c2d148b6e105c8c12fae432dfea290c31b5754a0371baef69d1fb0a1a179f801f232e67789e67965cd13aa590f44608d7c5b026d2cfe4fc2e33fb841060bf1de10432518c883343bdc256215e1ee02850a191621d8977bfbb57d5400f0d1de80d54330209e0b4cafbdd2da57c365ab2e98a7d1d97d39aa599b180f25e6010a131be53131f6fc706225f1a849e0fc3bd174615b94fbf0161d640be5ca351d0f2f94b44622075276d090c8fe7bd74764035d6f6d2cc9b480da52c2c4a5a5ebc34924619e59bb16530e3c28c0824fc7d740044c10dce255e0d9ac93b7a0fda1b4fd0730bc0ab04baca02af6db62eaefc7f858cb9ead4c018f3e45ba1cd11858795d676ce20544cc32f29b645cbe5bdc48b1561d7e22a6263a90ff94123dc6923ec2054d0846d934c8b15e5cf0524106647cd193dab797cfbabb8e2837f2b22198c6f73021e15bfa5b0ad0eae1c34aa6f33c2509ca5deaba5acbcb924a22dad7b893daf674b17fbb12ba9e595c412d5564e31ab8cd527a8ff893f082b48cca289ba53e606bed66382a4745a18fa1bc4b1118e2635344251179b10e9613264a8c19f45ecbad0d7d34203a2d5ee66a3b6801a97f22d01e253a8629960987e952f3669a666546cfa69a2822b82b2e6663705a90cc1fc0a1cb533724e90c6c388286410fb81c4a5e45e2d702f12c4f2342cb33fe3d9dea08403cde6d51da68044d9b0dda937287f611569d9012f4fca6218029a696ef1231bba668785f02ca00fd3885ff26338ffb8d0c2d99f5d362fba7652fea528a3c4ab91d4b873563cc68a37a034804fdca7fa44d8becae7e0d4c4625d9286ea561670e2043913a2b0f28cb1750bcab412e555a369bdd157c4503ba16ce2f5d009427c496e558c3a2d5e6e1d0fd489094518bc849055f4c295706efe1a84626eacba30adadaf30ddbb10178987ad742e7322a69e5acfa867bf8fef2aa7fbc6f71618e56558656413a6c93e771083279f0a9dda78c2110844833e3c8ed02d5d0ce3d93a4021f50f193857871512cd163540a12781114cced56ee3da179978a3365abf48013f3bf340e4e1b408c5c826dc1a4d359289931b47303499eae96062fa5a575e4129a37b0ff4aab43311b80d585b42e8d94d60800d226977322c51fd9a53da0314e726ce2068658c12d720ddd6892dec6c18ee7604973da54ecea4504572d72c105c7eb43204143253684aa831204bd1abc1d4cef47f0eb78279a4fde9e56d66e973febfd58b98e4c46a64a80646480e3552a0a21c5c1014fbed605620fc91a64f2ec8754ebdd82d4a093dc122d3a4a53ed9632efc467dff3ef32b264f26411f22b0909242eb352daf6a30cc630dde6573f42d4a991b7cf90a76ef0653590947e47fea4a2b04810cfcb07102e4a449326f4540b6e59643041ea5e7100130ad9aff8ca072a0e543a99c1ac08fb6e26681f6bc028c01161764519197172ce9a5e196bcb9e0af77401305c1fb55b6e5b74abe148801d020a8ab9958e6987d84612417fe655e61c4d9f6c04f24e063f1912d5c0230ee52a80df1038700239249deecdfbac879932038dbbd241a96a41217b4f202bbd2e700ebd77a840397ba91c7c573b1c10c6b0790c47e212c19ae3e0ecb17af9a075d2cd0590514b4c02bbd0629231ca50d45988407a8bd68a932158d749cf4c673c01093db5617dd2afced9ee58d81882adf4e513cf10336ca649e72bff5575d6f046895703113d984567c02e75185cca6e3bc503a16b6174668bdcfe3568ac4b0ae9397313a1920e6dc99e75cec9f20a491cc890c6404124efea49ba22744c6ccf4e51a7546ce54af33703411f4843be6045b2bcc2e64e7a86df96af5c8f7c994de24261d6222ac8539143d362257e28eedab6e08abef35ab1a263ced76a1adb876e9b06e0a7df9888e9aaf385e351c8a2c7823d8cd000ee65c618cfe12b2d59f8f06c3eac78f0091539e377e21d434594c258a562c1dd925824ef1de055f316d86a4bf3d8376a919809a9b401ff79479839e8fa8688dab95123c66f908fc892e6138ad8208ece36819c9902c7d85237cb67034e0486ff5612bb136514432b2c6d8a195a2c4df4c3a084936c05b88f126476dae7e903a4c6c08b5a09bac13fc6bc3757dda0b7e006d3b314a19a6b984389f04afc66d4b16889af5def11d921124b24a1a0efae012f138b5574ad9b6c705ddf497b4596fc8ad284080f8b300d1305cfd218c6f5611ceeee06324fc3fa65cc0762911c9912f340acaab8f8a5338f39fe1e50d618dc532ec2f668180b4905427c84eb942de4a5e6727ea313e2485d087c1c0e04c807cc0a7fe6abb25d8a6e4dee4997f313ba3603e42138c9d8ec06c934a419fd2cd86073b0c34b38afc166f94a6771e46813b5618faca44f51bafe00044b8232a1da2969eae191703518174078c06fb2044728834b8af887292e6eb2457b3df8c23ce5c677f511996cc429a464004801f243d0700094cb07e36c17222a33d1ee0d18978a65f1f95471fe94a3d9794e834b68af589109f87eaefe18352052453c0f80548a7154383a97b51a40977d584c1f62aaf4200d4d12425b7535e7c38dc05e434a38a083b5bcd93ac4e802c870f7c489aecee45dac98f02df5a00a1f13361aae376de32691a06720415431977943eca2d6874bf5a082f1b0d3e53d87f60ddfc3ad6d95345642c26d415ad3923001847135e855b28aa860d4b23ea0cae42e98769519b02a64a1d9a694fa1ac5aa7aa1eb87b8324182a0f2f582670e23d1fd8774473f42c577abd000f680e878195c1d53f84f1aeeaa608fb3529f6c675de30df2fda5c690b8288a4c2b44d98d10fd856bc82081dab489950364d4d63907b30f4af70f6058372f8ee272d4cc685bc989cec3f672c563d3d3cf6881c01cd4304972573bda15f835e5fb20d0f062994881e7b86da93ed45d5624ab9fbae09868642338a02281701323111c4a014769054286d0c1e2b314b11d1337968cee3ea247b1ec968897c970f9918efa491a9d2aa03f00d02ec471f8709f3ece0c8ff22bd2c9b84ea17806fd62404deee0243a30c506a1259da16d7c17c5af6199e20b23ecc316df39d8935c54167df1ef8efef66f163565e00c666a1ef774b5d77014e584127cd38b35617d0599d7732d3fcab41d062643ae35ebc7ac4a82dc774c22ba9c52e6e0690d7b60b7855a73970520818a24c1f66cd062c65fdeccd3e0320995518136e21147898bd9df4414581bf3ed39c78e2b8c8a5cdd7ded5f1fd42dc18079f6b95d9651d8a43aa56572a19508baf50327504f08f3f13fd3486c803010e3a67bc35aeed449025412a4d2f1e6f703d912c7ca4a4a3cfd9f56e58fc69b38cd4062d58f7f61f58de5fa4017848a9d174348a09f909ea07797eeb7bf6b2d36c1c148503cb1bbde01e381cbd3c4ca167da02f1f6704ba2c3d39a068161305dd10c8460ca703fdcb8b2412df484669025ecc79a1793b692f4e73d5355199cb595db9c6c4d23d5bff0ce5221af6e5674d26393292c378b9301a0dfa60fd5a6e7201eb74020be793cf308512a230c9e8af20c856beee1496190227a721ab7807e0e7960191cb20e929af9e991c96b59024eb502b1c255e571e91ab5c26e7a0460bfa2dae7862e510f9939f459566d6717cb8e42e92108ff802ffba91324283808112425b3f909ad27a4e7489a7fd054eb32f02379080588df8f8b20855054b820ab54ee8c1351ce104c137f159fab498a2942065b608350c6ede141e557d89481143b07b8387d5d04451783073ee667f217ad2dc4ef74a7b0301e48e80ce3374dad30e402f5169ffa301e604ed447ba0ab80de780bd441b294813a131c5284bd49087987bfb09a5c49a5ad415e3b53d337574f47a0216668d3672f3c94c045f66b3211f4a96ce94a4dbb6b9236274b2b2e7ceefc267c9ea8c9fa74e3ecc291a8bacd025a283a4554a22b0643d1a349a7f719bc1c4827c8c68beac99841353b621d3f32316ab2750fdfc245f61b194ce2869dd8025cee7a6e30525613ac6c74860e50eda16c29a707563c90c16ef9437d88e70c29c029e94244e5ce4f058a7ad36a4c8795bfb0b24177fac6fc8df09e81ec0438b379c45150493a9aa8e984c6433234026345edea322e492fb930ef026a5da2cab09d0271e2d6a42398a62509700cada3acd7a7c7c3a04272513fc04bef2023018e0f54ab8c0eb59c0608b224b56a4e95828b664aac458023f29a8f84593c09e78900d7ce0766c6d66704a0dbcc40a14a20c8134b0584d04ce47bc275cab8f0ba99bfec3ff1a188716162ac58d49233794387492166cacea258fec2d67842bff219bdebb86318ac3e14629db6912cf29a7e5d4a9678e53aa53b8fad206868c0cdaf25ad5d01a746258a004da63945a007054727ebd225229f391f43e445eec5203f5c58f850da432a9a57ccdd123d4905e71cd12b72e30b1e4edc246d50b937bd7bee33b93716ed5b2f648ae86ee64a1f7b93aef7ea1c77e8ac83627849f38dd7a87d6d8553c555e1c70c266b575aa06f457f2f23794ab688f720314e5d0de17a426941ce10b2fe3bb0d06bda90d0aa61a2cd08d8e6efcd1c2ac4f49cba6647189be3011c445db79607d8bab40c24baa4f505c84f15e684286e8b90ea72f18e7df8ae42bf96c4e2d29afa72e6e199a6a57eee5ebccb5d3762dc6bf82fab978e027be9948046f3e95b234fae02999f0cb296eb52682d143ad0c6257d09822a85c98ed0d087791123148afa1409c219e2119527033116240b6056817218aa74dfa085656459f2f9671d12afb845139b3d24eec5ad0e4d8a70cee6ed9c93cd81c9093a3d705713ef0e67941959c65e6d0c0e146fe814a3c2224cc69e4d3992f5da104b7210dcc833536f9f7e8381bdfeb318e1b4fd04a7c45cbcf160ac83c507b7791e8e4c7935644af6e20f83c9cc57237ac5c78ade58a93baff5faa7bc83cfc1930a75a78c930b6c1447cac203acebf1bbe6777fe67a07032ca0561a50995e21a9ae2373813d974b8f3cce4d8963b33f07153c0f0dd88edf14587e378d103a9179e9f961bf414590ee97c0b3d05db7df968a6ab5e34798b59049cfe32bfbbe7cc8320dc72c43aaeac93889b3ecb3ef4123cb48cbed96c9f89c05e94883f4d29762b2fe73b7054205641d64999fc9106120894d52481d8593aeb78f4d6b252920c8329e838df2e3f1166b6ce5482ad75ea7a131d8aa9af90ca55a58334c94ca0fddbc9db8e5179f8aad20042aad9917a5f203d72625f9a6318ded4d1cc074aff937ac3a065371e95e295817851b99de213e0ad356c4ab3863e6e1519f03a26c28dc8747d2b65c54acc6436b184cc23605777c6133c15790dad22e0d275c80a7730879278eae9d5164d8567599ae933a6c73636e37a04a0ca66f3f39566d8e90bafc9afcc5d48bea1d93016a0e08a03a7e84a8c7f43d26cec9385622e2db9288802f1688b4e1cab76713314a36d021ded9dba0fb83441cc07e1606a58c27bb107d172d7014bf1e092d47811bb4ecfbfe80523a97f8eafe4240c9ee9a9574768d4e5e88794ef24cb4690e16ff0cf6d5032be47b6c68bcbe24b542db7b7f25fbaae571ffcfc69824e8c5851439a7def005a047990459b7f990d3a73f8b5f63a308ee7f6e8e45b9da4d981382b935a51eb9c892a91708e5220776686b24089afdff1901d8e1241cf3cd42a97773fe7a5939287763e3a82666d93da59a99e7c0d029ccc6a4cb5d54dad99d8183b4faa60505ce956a8f51437f580e4d1d2d86e13785ee192104e1bd8c6214a7b462fd49b943ad6fa619e470d5033ff3402f801685921975605f290e3a97ae44ce0daaa3d1ea59deda43539c455a5620b444593b750d9676859e50a0c44f80a65fa8eef391b4f12bbec4b1cbcac57e6feb976694f64b299c2d131baea0745bbc8b3cb08ade2d595775d412cd2707b74498b02e25e23178c4f65c500507f384549a7e48098c8373c3113fcdc0dfbd92c79511a33b7cdae74766815e049a707f81d07719d82b99b949b110d8590c06cbc0dd80c2eb5117416fd20cb90b3c6219c58247bb5070ca906daa369f8a39f05083ed09174052787fa0d44b4621c449dc20af140f4a1115ecdc9c12fb093db15bb248c0828a661adda3c6d2c95ef15fe9cd3a470931bc2d6eb1ab03c7fc7de418e885e00ef307c4d9c76256770b21b69bf0a548156fec0a680ce83f2a5a1a23b7e781aa7fa0c9057bbf4c35b50fb92f5df0d0e14acae136cd2482b68e7c5cd8d7edfed09e73f762a5b71cdd6efcf74681be173d8a057ecbb32dde80b983fea34e4babdc9ea47f89fbe430bd2ea4c70c1f51fdead807005db848fe4de08b049091de44729484281b1e7d2e1cc0e761925aa6e6869d65e838b684b1299568204e6c5ddff626dddf4fe8aff234e50c637de6695c2bde20e29b75ececb6a38d379c0477bd740c0078f2da24729cddb0c9953e1b0b7a2547f4722ba366804b08b6faf76c8d54c627b006a69d605ea96b2e435eedc2957190cb3a2ccd867ab26c83741611ebda338c63430e35b535b196c5332c8b467e4679f2909ced1996a85ec43a8e9e016d00a8a8abd5fbc8ed11fdb17a27dcabd8f462e406de6da81b7ce9fe1b9ba7d67ba71176a1d1159607a2c7bb0de3827a5fbf9414be212363ee95be0ad0077f547f71da29a6d077d877434c4ba11472e39318ec5262b0b35c16cd217517279e9c8dc76c64a5624ffc05bfcc4eb4c00e38f5d00053e7fee404d62603c43cb9e322537c11d465175eafeef52832c9f5fb304dc13fc2a58d678a0fea8864fff1d0ec90dea9032b6908100bbd8ca9ab86b17a0ad7c8f5580d2a4ed1eae876d4df11f9c4fb69de9112b960f2f8c0e1db99ac143895baf0c87296298891cbcab66cd2533ed4035717fe5172f0b7ab630abda706df54155fe518b8fe940be99706fce1e8159eeb7a479f2749832d2ae9539bc6c5a5027db3d597992f5742a860c7c0889b9c474c1241d711be491e0ea86b7cfb16957fda1eba9561399a61898961f4051e1699688d98dab25d33f0d6a6001e1f8fc52f0c8c338ce8503e5dd534841ddc40c98c7c6c54a8b75af1289324daf2e125488876ecb82d9be2a100156e4749d524caed8323e1b38e9e6e6c09a329c73a06265aa46290ff274de865d133bc54db5ca87855a11011ef5ea7e692876e9211ea76480fd53b98d2dd21faede9faa204403fc301d53080a27c28c80c0ed2c377a0b16ad9726332cce43a067f546dbc872cee691a284dff404d8d57ac114d3685ddc686ef8ae6e59050787ed4e4b55aa8ad5d76818dd2d5007016e569c9bd63d13e2f8c26b0a51d8b81ac6300af1c923ed77d0a3c0b5386ac1e623adce4b7e76b89adf0fc5f6fdfc6dd160de4ae64f9aa2284e57f42dea1a4b2919da27f4ede2b29806f1a17f2917618b64e0c29c8c74022ca695c82dd55385df21d7f6fff9bec5a5972ddca4264e83586e3d50c8f89c4fdab7070f63c3672bcbeea8d1a96e6b539fb2faf6ecb27a130911b8f21b070d873255a61c26dbe40e32fe49a8105544c1379d8f80060d7e8b303bf8cb02122fa1e012edc8bdd284fa156424e11ded89980d2b0d1d2bc2c281db22c2fbeabd5b096f8692c8760536032e0531537008dbc7747d3abecbd0cd01c65f95cb49fb62cd9eac3bd7f58510e5b6abe4115336b6768617a7ae96dcc6f1e3673752f6b08c1e8e3f0fdcfa4bf00a1d6d1bf66c7036d24e92e33343d49a0cde8ced0949e526b0fef90483c22014b25b256014f0678fb914cdddc19a102c4f31c990bcdf5eb3233408b73dd267a3e3e5a4ea19999ede77f3b99ab9e0689051a26d7daa29c7ae97c47b99a6a32d0f756270778b242346539408e12351c13950fcf56b202ff4145a8e18e5c9e9c72ea2547cd147e9a124cd4d68f17010ce79714240da70f99ca15ebcddec894953380a401d5bc0f4fd9e90a57bdf89f1f1678f6306825ca1c6e3161d2736bf3e69efb821cf786061092468ff9419d477d5ac2f8b87be35f71fd7ca77f05e14b15876230d6ae02e344fe075987f34d76bd1ee5b51be370d7aa4b61a4a1cd6e5fdbf03097770c5b03b83af981e25b161533ef4d0cc4d49f3a04aa8cf226727d2c43f96d96f8742f13e3165149491d63eaa7ba301268c2010e2ab804373d546d8e2b74f4c563759922b5a0afd55b8de29afa1d72c04b55e6c665e86d36d1eb647db6b2928957fea192428908ad4ce5a47503a061b8a1b66ae70c02cbd85c97a8ba464f348b05f66489e4111c57c7e6fad1ded7a3499080951535c10389c1a3346282c1bb50e04cc636af09acea77efb0e9ee23c25d0d13d0ada90c6c258b8520c9ad4b689d9e86689de9435418e759971813aebfc4f56e16a3718a1002647a737d5f230095b58fa4e9e5e4485e03fc6c965a659a31cef392caaaebec1769777d2569e33b2adb4ce9b473af0881b1fd697c5584c0b0e8973fb0eda6ec793f29b656ba279e992e16d1d1d0217cf3de6da1434c809c7b5a3f304bf29e82dd4d1b2447415147df7647528023408943b8e1d960fd6b6265725265f6f02512fa1ad8cf0e01cbf5b06cdea7f1126a0507eaf27b2b1da4d3325ae3a1f71cdc537362cfff98d7ddac0f0ef001c309df45363759034632cdd43e895ae20819293e6ffd9abca5ea7492e59d792ff791bf8d63bd27d828cadb13eb1fcd688419b7981fb86683232c7dda84c1690fea5dbdb47bc58f975efb15c13b8c74bbbee2420214c3e61a8ebe8f8185604dac1b915b9b9359500f0442a74e39e67c9944746702b2f0415006e1684fabf7073855cb9eeb4515cd39e8cccc8ed559c5600bba447c44c34380d3ec80738f3941aea05bc871e4e2e88435bbeac66b1e84e81d1916e1afc6df7cb8fe5ed54875434d92c05e70240867c33210e6daa2ab012cf3820598cb34ff8dbd568740da6fa77bebc276609c2170763aa425994d4e5951602e8fede8ceef815bf8008ec5f3364074724418a948fb05d3a93945443d7f9b56a1ccf4d77799e33ec6d0e9e326a21648aebdbcf7e4918b86f4bfec62a4f5690808fb05a8a1b185a446dc7261d07345db22d097798b90927bc7597637ba681e59c14dceb9ca286312dae9cc53e5fcca52a02ed06aac527bbbc66949da0f503d81dd7f68dda3d3c6de0356842d28e23c60c09750f66e16eebefe22e09ac2e5df7f77370f6ebffe07b946b8b479835174c70012f10538fff5783dbec819e39332bd0bf9b439aefefe27e09ab8046f1d751fea8916a2c4bc10e725949bcdc392bd5baae303316ef6df6640ed6bff5c270b48d76b8cbcba5db2e4add8ba5326e0aa8922938de414571b5037ec807ce94dec34bf24b75e1c0a6c610b1b396180a8ebbc635c8295263eacf9fec0cdb2e62da81ad4426fefd5637c4ce4b3afb4e6bdc1791a6afa70ae991d5a8d7d6c2c683e4f33bb9fe867282a48b630c48a2ad330123b2d4861869cdcf9e0d9b9501ce86b4be5147814d4b8db5dbd584e96ed5dd4c2e56e262b0b332b7f01bf9bfa589ad8cca29196f141c54ba95e00dc7d3f67273a88e98062a8bcb13e35c4f1231d2ff46ea78087ffb425b44dbb247b3115a0626f532479eb2b60dac73a35e9fb8a78a7c6dff76ca69bdcd0a3d88089347ffd3df79887bab261e321c737a8f4ab98a9b413cd820c7aea22324ff5df314c2c4e032a6c3a7b5c40d6554d0711fcc346bdc4713b161159e7a6b8a5fa598445e4161dc45fd859e20ca8ca8ad371b66a0bcfc5c6b51924f3e17697cc9f70157f5db9f4210a9c53d880a8a843710286fcf55e4e8d6e7258c69290b6b7dc5bee2d654a52067207d007b4075877365885099279f508a3758d6daed678db34dece169e05c33a252e36e36df6bdadde5bcddf6e2b6fad8cb75c8cb79dcadb51ea2d09f5f69ede964e60bc758112448f1aeaf78b14176f619cbc3595de9eee5b546af456a57b1b837b2bc3be5dd9debaf6d6ab6fbfeced0cec2d0b7ddbc27c4ba3dfd6e0aa176b5a5a6099f179be2223864a0d101cf56743a70f55c9ca4d8c71e566ce39e70c829571852b7dd66a32757777c71863ec4e39806bd8f9c30bbc12fc79b7cc1bb0bdac621df2bbdf0274581f38a289ce8b08ff70030806ae9a16b453e7b29a850d0cb2cc998f5d227040859a4cce8407df2e4c10bc6204df9f8d7e79397d8c820912df38664c1c1a42c4ce6b32010a4358d56800013430c8ae0db9dd0e721a3162675543c3611c92acaa451142457a7472806680e4a3049ac19197472411381f20a2d32ae295f33c4318943b95996bd739eb17f698a6265cff6f16818956756b316f8f99bf90ef63618fd7811dab1ef6782cbc35ecefc7fccca7fc640f77859baac1ceefb2f7318a2b52907d383794ad50eaf8b0ca4812a9a4474baff55e93c941f0e545b6a4a78690291fa3586203c92231f7fe1c07d9c32dd28d5ce1fa5721f42e0cc94a0d980116554cf1a174d9787989628915cf383a26e625cac7288c3085e72f94ae97927396703df6e6198a49c5f44d1424ae8991e0f666ed417635501fba8d60f667c9d2d4dd3cb99b99b9ab2042a25a9dde3d23297609d022e68c912eab66ac613af7121b59421742323c8647ff42706378ee32bc159f5ef700240788102038dfb2f53c2211ed6ea29e40247a1dda8986106aaebd144ff9c248e4a249eda38eb3dc578e3b17edd33ddd8a2e2f220c829c7222528f93f02bce60ce2d9711067da20b53027c04d2217a49f4c40d99e739860fd9e387fc22629c17cfc9899ff889334ff7aa61b886dd0681e12607f229aee25c8aab788a7bf31b274cf10ee2d520dde436c56b1c0639c553bc570ce2b02ae50beb370e0c0e867398d2213fc5e5a7c0e09e711a26d6a4beb0a2be9ad3279958286e8adb140f25e5260fdc5ec599f9ec1dc4c52ace1400c584eb9ec23cd6acf8f555c3489cfac28479eea1a2e0172aa69e9bf26c6471613eac41e238e6a82faca7cfc430523e9c17de4aba422039cf2312bde6d3f3314f3c9c50d2e0d9af471ffb62b825cb9cf4852f59f7caec16a7f6c99cea491c21435c9c15ab82ec60551494104514353f744f68420819022d764417f630be64b87b31ba2a64102723e84a5ec9a8e24a0f7b85b1ac783e44209f1918a9f9a173021357d4acb87b3e6448a8f901258b9a95af053d08f03a3e1d280258d55f0ab4d1ab9797985e35f38a4176195ec7f0d8011085eb4228c52f8050244816ecf26ca2533a27952e0633a2181dc3b2f8ac0786cb2ba66e515e8a682f3e37e416ab324beb6c346db3611d06b31c065f64a079d5e6f74bcff590b58fbb0c623bc830437c0ec260e6d24b7d8cc399cf28724399b3da22ce48cf5c6241dea0ee99534f5a41be90b9123e9439b20a34a0b899470f658e4a9c89d46d56b3ea9ad65266f6c57a9a7d59966519ed1cef5a69dce04e6f2256853c4c8f79165d4489bc332f1b6d9efc1c588539b5d686f262d66d12aeb5425885b9f858080e83b4f3182d87f285a6b71ea365211ca457d94a931bca9d971c5c2bb4b3135da874f185f5e42be23e1a4fdce9493e73e93df02a88c46155a9e4617e3dcc2d4dc6651fe7596fde6d372ffce1edc88bdf1a91916799c7ceba676d3bf745f902e699d73c94c5605683b602f942e69cb59e659846c45aa761cb4d197bb47110ae82d49b7ec30e56c9288a143d7500ece0869eb13a2330f11de2a7cd98418c31c658a34bfbe0f2cb0e1b5610e4c398444a89850c923ce923508f928f273108224929a369123ddfc04c240dd285e3c5063fbb4945d2e7af072e7e2976e9b4bf503a05410c8a7a703e36f908f4022ba4902785393ce6a5d8f9a87e9d86bfeac99f0c92dc9ebcb4ee0c2732500f49fe83ffe43bf94292f330fd896324acb339f9522492934895fbc2ea9541ac7e5c0c225792bcd4f513275da75f9d8b453fdf665e09a569a552e9d3ee17190c7330015d21f411a80843fff211a8083eafb99d5fbf30870fabf330a5ee97baceddf5ea695eaa7aaa7ac94bd52f32f82f349ab82f6e3d32684b3d30687d32683d16bd759217ceb7d5eb4783096e187f5cb7d6174cbee6d2eb749257e56bb1e3b8b7118383559d634e9383062bf496b9b9ebc5cf2407e68f0f35e7c1a5342ae5119075b157631e7b7c3ce668581549384fe29ee493ed1279433e8924f4b253b9288eb98a631867b9ebdc77de9d4de7a552c967885ffaaa9f7c36b4b8a91abeead681bcf624bf5e28fffa8917ced7b491c78e743d204ff278703ffaec12f902c7394d77dcb7c9fa95a070548cdd125dd4ed9ea0e4274f98036f9ddae96595cd4dada49dea248d380eb453769ce19eda26a5e458b8acc3469c09857226eaa1b6d5e832bd2e9d8026eea5f9b96aa59327dd2424f2641466668e99b9995544c0cccccc551f418cf6a5bbb7a9628f71ce3999f9d36a77fb0a337b77f7e77577c6dd188d7132778c51d62cbbcccccdcc5c1ac928f2812b8213968fb264aac2954ebd62d987359d31a28c90e6a8c338cbd5ba7d32210f94bf4e7208581a1a8607c3c41c5230b8e1a55efda5d4c4bd4e7289c3aa50065d8f0647901b4a1cc9d36255c55c4758b5fdb00ae3161d1bb8853a2b88bff36292a28f493e022519f20a9045e40b9b635ef50d8b5d44be80611886d5ce26f32dbe0e4f87ea9bdb51167e64bf940eeb9dcf60475f589d07f9d6a5770e84dd7adc59dfba4e8e6aa5e91db2efc21f6cba8fe37cb39d5beb9bd7b9ddbcd66edbdcbaa675da56adc76d5836855018c612a5d7774c28571c3df5e046a0243a2f7b080999dc8e6eefa161366f97cdfa6e4abd7ea1c9b483622877f34cde2ded5e1237f55aad6a52f4db617df39622c914475635612492ab1eacfaf1236e1e4d0a888b7d2ed0eeee116726519cb9b179b146dbd1303b00f1edb4bba687152978fad9a02f5c7a691d88e1fcf881221a57dcfae1ec06c16e11d2125d220c00644b7b83a6996f027c8343de4e8404d2c8535df48bb2f97ccc7abbc0303b76340c4deda8b83207ca0ab354ba248f66e4e5405981d33a148df330be388b6d2915682c58a5d1e2155f2f365e3a26234189c615b73dbaceaa55856e883342d2b9a728d83604faa9d01cac9a3a70f8fae9c7ee0606d925145f2ceef2451017ba1de4cf9982eb1f46a22c12e1108a8971654b08893136edd9b47b369ddddd4d9b677737266b90e5ce04916f40ea396786d1994d6cd239e79c73f69c33c6922d8a2bbf18527c4bae9c734e4c04a42c93d2099e0c8b4ef01c4199c680d26e1e224d69370f91124c2ce46cb57e10358661584f9517d11461b2ecebf163c62a3e7c217b1824c1954ec5cb5056e4760b8f2ef4ac53807df991a8b85be95e6feb6a9122b548d7917cf3a694914fae5b9745e48dd2471deb746c7f3f5944be706511dad94867f140cc4954dc39fae1e737a7e92e7c09e74b288df8c8223d457c863c70a58f8fcf0bd6b291a509939ef78f4e5c61e89dd862c8bf7c74a249162f8d3c928db3dc8f3cfa6455e7dbb6792994bfd9dfbe6edbb65169e4d11ab8af7aa1fcb0faf5b98d4a23d7bc76de756e4b5f28bf73eab5d3b17df7490ec8173acfdc8e46596733ba0cb6293aa9f3d1486e4ec3cd79f169ced6d9480ec817d8abc4913738206f4837f2992785b85288ec223684f8b2d1352312134bb651b947b7b0e76c31df8e36e9304cac59f1c0cdfc32282529942693119434a7924d4c564d2b9d4e326bb922c4cd7c32485d3b4ca68f7a74c6c99bac2fd395f75693c9f42263e48b8c91720aa13abf39e79cd3f473ce39e754c5cf8114afa75318f1d43d938c424c94e28c14483c75af4c363082152a5e489eba8c49e7f43b953cf1d3883813430aa0be4ae0031d3c7595eb7c450a4e90c231a9571f74200a183c75d49572c9145e0021e174e9ece9ea3999f073ce39e7ec8914888900983a1bfa49d13780808898d3617436f38a110891022259c2537fd1d9d0976682446783c51b0440b2c416d434e79c32a0f859020ea2a8a20a426a26b5c2cf2ba8f8797fce39678ca0bb9201985843690f58f0748aa794523fe96ce817c59c53658b9f5e2f0f6af0d34d3d28e2a797bc8717fcf05c71032c24f143ab5085d007eef47a4d0eee20063f9df44294b1bc980429819120b0d084162db1410828a57484a2c2e2828d1d9ce0a75f93832f3f3f3fbd8b11c226b04411254fd8200b45f081cc001186885c153f08c0872e0c20074fd0010f7e48f0d3630e9af8e9f61221a9001036504412176cf18226a8b0c48a4b29a55b504aa58ad086154f34399204e8c813a0e801500e52f084097ec6cc698413fc1c41cdc4829e7a76e534c21474648c132c1b7aa76a461c10f1810f84200525f458a166522d7ece58e777b11e7cc8a1a86c3cf5faf3d4af13453cf5a8f3d44469ac4203251593162a32547eb2fcbc01133fa75717113ffd9a5cad9fee38d8c0cf792fc5c29863479c91341448e20849d00008269658ed20c80b120caec9231113220e8cb87e12e1a70d4cd0c4104d58e1c55f48a20822f618a32bcedc78f61a09e39146dee00c988670a7909507cf8e2161303e090637bc4ba6f091ddc42adb3ca3bc3181a81e8dabb2b0506a57e2a241c8b337c00676083410133a39e898a06666dd25406291412c01210f1c3f9d7a3af4e44102f9f9f25383e64fa4383c2df78e4d6f5abfd40cd2eb87e3e9174ee74102791aa63e3b26070270a486b851881b4244051b59427ef5949f8c52c628a394198d599665998cd1492ab8d23329a5945fc6838772c821071c72e0d1e3870f72655d7e42da06ee8c4292c91397913078844166c23f5dd095af206c84e18e742efb94524ad9dd7252395b474a29a5f464a473d2d943e8eeb6f173cec80db149113f6f2fcc7c6642318b3176e5c145983b85eb5efc11084266f2e10ffe41350d88b6452d522c3a0d5baa39e651d7bcb0731ef38835114e69eb304831ed33af6258584db531297f3e0f969f1f05c16c4a69467d524a9d524a290a8db3490d4923c6163e8c31469619f4c3bccceb8a26638b6155b854878a913b7131c678638c296177b70b1414172f4e524a30ae89741aa1ba14a762636c32b495ea99877d74c664e91698868cb106575915638c2f96486e539d14cbaab659ae1b912e2d9d742e505ea4c0309d502995183256dcfb66b0b44083d6a0b10638a2489a6259d54c179c9d12def2cd72dd88744b272e505ea4c03055179d12f1844aa9c490b1e2de3783a5051a3540fab60500017001a4e1028ee68779e6933ec59a5f329daacdad0516df96d09640a95e103d686ca1dd530a646e5fb4d06f69d4e03459a35946101c519701603de96c64580cd61f214989ab394d6f1e0feb9219c26075199ee802853bd936d7344dd334ffcde301be16831006abbbe8620882728bc2a5743631e09c3ca5b38921088315060c0943d3be18560cd6172f525260c8ec30581d466723a3637a613299505c9c984ca56b329948a6516732994632a7201f66d0e9d5309967df09480d119608b2aaa1a5540c1547e5a462c4480180e58120527a40480c9594cdca2608ab9ac857140e2aa75b2a6a06106ee7216a353d19db52762c8dc20afc96e3c52a205f3b1d39861a26c7ab5baa57ed3b322403f8339a7063121f22df82ab851f974d9c915f4f420c5697974c4e9cd9bcbafcb8fc30585f7efb6274d1dcc58875bdf6b15717d711171f979f9310ab402ed954db54cab6202404923985c0e2aba386304c7cb58c544fb53df1892ed55f787c9a851d56c13882c5aad210191e5699bcba0c1156d12b2480069417c680f3d54f5e188390af6ef2c2e882e1859ee2851851d89d5e3e642793139a3e94c9f1314f30161192382384b9fad4c3aac8028bf80836f4b5263cf9c47c88b14a5ff8e26267a8faa955dd858b55b1614cab868935b59a563e2d1ed64e4e673899ebabbb502b4ef2d58550e862e863c3d458d731edf4b9401a207afaa14b0f0a6708b5c3aa3042b142ad5041305604ca000f0a8bfb10b542ad5047c8d037bad48f32368f34ed73f1d9b46d4bad705e9ba68531e8ab46ddbaa961a6dbd900fa758bec18206b6ab758b732ca185dec170a60353fd7e76a18abe8b2853056bfc9b88d421edebaa2cbe69b735dad34f03fece69dd83b2561b00a0179c06539674fb317c03cf30b509f8e7d323bdd523dfb6a0eeac9e4748bddaca5443a7322d9a247081c702b245bd8cdc5e7f46a613e2f2160618b0f31235fb71567cfc527c76b3bbd4e49647264765c7a826c5639867260e1f26a9815afee12d43032bc721de79ad6302ba189e5a32380c1ea9ab783370d63f1b6f1690544444f3750075a466c1ac7209eea3506ab1891af58cf571a10e03b58a579f59886c1bc7a7553ada6e92b1e3b075097e1415a6037efbcc8a08d754ddb5c5342068d8f1e3bee9bb5ba0cacf5d56578950d80b1baa5fa0c205c8d89cb8b4b9a87a8d5574fd9cd5132399ab59aa3761a6612a156384274502c22289f8a1af215d5fa8a32f2d5eda6c9e4b02a7479c9e854cd637779b18aa57e3a4428725258c7ca4696d0ba1ef31514dc58e3ccea5aef8d96e5552e494f55cb92f273614e83581585769860c1aa7ecc6911ab2290105a8f3916845539b4c9632e6c7b0109c1071f623a8f79cd24cbb6588539e67608ab1a8bc7dcf2d0261f5a228f5de143db93807efa9a4571c63e26ab750d5936715f6c6258b3c4aad4b1558bcc1c134622c923c43bfc6a1c2cab59181389248f10eff06b522cc332c923c43b14a31891e4110a632291e4991d89983423882b993932616704b9d1a3478f1e7d46902b3d0eb9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b999455861754fac6edc68c57bae66af66af66af66af66af66af66af4e961bdd1314f756b357b357b357b357b357b357a5139414937b3d57b357b357b357b357b35729304e29f756b357b357b357b357a8540cf77aae66af66af66af62c8f0d5ecd5ecba5af19eabd975e5deca7b80925e2941b34b4959469ebd7d26e579764a87d09e67c7682b9b59d62cb33aa2acee3944833c3ba52bac6862319b3c7b9d45dad4b466a9d51186cd169267af3f9a6b9b3dd6c54d8e6b965c1dcdd6b6b17333a79bab671f3599429e9d3471eebcb759de3aea221247f25c4a9ebd14c427affe7976174718945ed8499e1de5155fcc172f9ae58b3a6a5717b5e6102b891bd2a09413c52ab771a3eba15cfce22016e221a2a21d168bc562b158ad56abd56ab5bac8d5382e97cbe5ea9cde6956f3742b6bb55aad56cbe572b95c2e17d68db81394941997c5d3629f17fe119aad2ee25751511107b1d0105111465bad56abd56a75511775511775910b142ec5842209c921a2225e310f8bc562b158ac39d4e29d56abd56ab598c53cdc621f76318bc562b158ad56abd56ab54c33463c2de9e3923ff2258beeca84e3392c168bc562b1e650b7f0d0d0d01051d18a71b8592c164bcea139343487e6d01c5259e1be195d8f9c1d1a1414c4ea167e7901417793e95ed9923e2fc92c168bc562ad7031ae9f57500e0deaf1f20282ee26d3bdb5de950907cbc1723c07fbc17e82328c06d19e346825896b430ab7719e736298ebcf86e5340cf6f385f53af8f2f25b2783f6d84f74619f01c495df666aede30c2066689ab6b24148374e9c2975af1cd15430e208ab9ada0a4a92c4b5ac587b5e3a7751a83012c3aac82c2fa910920f5f5e76395458e19b3f242e459d3cc66cb186e0d75a49a44bba298c56ea3182ee4a90faa1dc1ca8171902849e6ff90804041c182ebf10c7e3b4576b6a0ef4c50908378c4c9e0ed9cd4b194953361a8d3ccb3239e2348d1b657284034da778a13ce2a9c3f0421e389e5da23c53b7503f714d6eaafafdfaede8d12d94e4a51410a88f512879e24d9f0b201d2af90bef272eab36afb5f60e2e78e29b57bd131577b4d5140c6f4f71cc4bc170cce5dbcd73e1560a41f1eac99742341d6b25a7715e08e4b9ce6df7a56ae8dcfa0cdd1799c9776e3f205fbd70be6f9ec963374305f2f507bfe96b22e40b1ae7d9c0e00eab464e2180f22387e1f9b0c15c477bcaf782e498c4092511ace4055ec8dfae6d317cec74d01962f8faed10c357b7a43b99f69c230af0f3807ded1440bd75d0af2ed166881f37cd6a289e0b71dd59c096e81f15368b73d9679c71466666ae19465f5e90a1464bae74ed069a86a93d42904ed93e7c04400f6e185d327e93c1cceb57b35a6bf6cd38fb8729e769e04ef7a8c519cca975ce721c67bf19b5e842a5163ded547167015be20c8d53faf6d5c852f08e9f0b71db2f8ae91467a6b7f48804762fd292471a6abe7d9be6ccdcd96cccf2b50f036d9bb3d3650de2c8ceb76fded35d886b4da738437d7636b20b35cf3cdcdc7eb586cdbbb3b1d2862eedd48b2c5328892450a0c4e676f350db3c2cc5399934f2a87df1934a18dcae17c4f5d29cd7e35f67bedff5e961745ef7eb450f07831b8e6df31c70e06de33cc6d64d2c18dc987d73698455da27b1602b5370abbbc9d4981c8a2e9bc7e0b9da90f5f67c4892e76352cfe7070f83db907539c4aa9077e6b639290b77733944bdd66ba29bd3dcb065db6603ab56ac0aaf0cf2b279cce63836c7619320f84d0af19bc4c06f4b8a3e8c4a7edbc298c56fb1c96fb7b75aef8ddfb67964d93e1d98350222892450a044751bfb0bef77e6567ae6913e1e65252f88929f747f251a3ef17c0052532a7d256f4ffbc26a81d54fade4691c8395cbbe8a753634c6060d394858dccd630cb9d5889ccca32ccbbc7ed208833205d75deb64989f7d3206cf959ef9dcaf5aa79e0f49f2707cc81f7921cb871227fcd10383d54b30b8a1f491edc995e7c316378c455f5dfa745f9377f5fb422bcb4bafaf2186beba8d18a315bc83083f1fba8063ab6eb529893a2374d0f398a77474754d0bf9a57718b5af5300e6d2695a92b0af9224904cca1a7848a9531a63acb5b29452bae0959bb0914552aa03f5385907f66fbee1e307a5b19b61fef0213d1525a9e886fed2317721ce54c7dc06ab76f8c159c09638b339e62e7146e3c7dc6a9efcd11d3dfbe8d957de4a9af83ad494f6e980e1d97d0059c1f0ec303c3b901b2ce179ec629815461e731556a9a480e4314c36f7c81584658f2c45b8f2b321853b833ffb8c65b7ecc920b9e711ed721f58c5517189ace57f79c84c7eeb15e7d968b76ddb6c6224195af18b8318949c6432f86a23ddc3afa1f621eac1d5158103ab78f4f8c1fc6952323333cb1a63cc01476baf9e2c62b0331965267d62d8c42636b139e7f40cfb64163ab81dc622250c7613e1047541b8612c22eac10716aa43eccc91c71a1c24b8f386ee4ad3305ff8126bb8da1ede516c6f723e399f219037e473ed3bef0d3372ea37b6b05f1c31b161a673ce713ef236c7dcee605dd7f90cf1bbcf8616d7813cb779e1fdadf342ceb1cab9c601f9cde3d76bde0ffaf59b21902f604ed319b6faa994528af90f264cae10b2e10a269c0429520f63a4cf4c29cfa904d2343118e3fdf6d9320b31c638638c31c619638c33c6202cf3170f50270eb6882b25bfabfbce87147a1eb8d17c8ee877c89e87938e1bf180bd7476529cf12157bf430c5fc35dbde462f74b8f919958b122d1903ac75cbb999be3ecf8c5193d4e6f6fb207e40bb38799d3ec1debb7126420a1f312070543598c59943aabef26df1e89621673884990927e217179f374f423186c97395287c18ef1631c3b51e6979a9e654dbf30353f1df419a72807e5bb9916395015449e6b045ea4d84193e6e7d82f063e44f91799015b800db4c8e04af78fdd123fba2871f14697241faa005571e443930aab4a71463e73973f60a35771e3143c3d8f7d9c8287c8c729788e78ede3143c391fa7e059d18f53285185ef3e4ea104162f2510e38d08937d3cf89312866a1ed698760d0ab57ea69e9612984f0f837d03046c6857c9be986ee91af361cdc08f86560b73f5c8a0cb1f8186309282074bd0e9ed9252a64c9952dbdea418c528a594524a29a518a54e2da5946e4fed10a088b1051a18ac8e801d3578bed618f235580c0e610de171d971d1d9f91aba1cf1d58891777d95916955999c5ac38741191d5669ceae7d355a3243be7a28c3f3b546cfc73e05819aa6bd200489f60ad2444e2fd48ac1edd36e45e130586d34b7d3bb770437f472c2940c27bf79d6faeea73ecbe4ec6c1ecae4b47519d6d6bdb90c4fc3b808c9e4b02ae6fcfcea3243645a3246647e647a645c3248be7adbcd258855a19723c4aab0864b125661417cbdf9d045890b931aadf7e18015d48ac71aeaa51c2003c5d32dd56578aca9e1734399d7cf5797f16f089720978f03377479b9c49ae9d4e994e1a1d6c6153794c9a9d152c00ff92e393e5f6b1cf9ea05d8892ed5674071a7d3eaa816fd520ec02e9039e6b12643f9f830bf9403a6bbfb77811aea80ec43b5baa53af6a158dd828a6985a855113d383118104508a18d45932b681bffa41a907d98ab81d44c0f1bea964a3d4ce8d4a379dec987c17a32d227576fdec97584c1eaa71f065352dcf2e6d9530fc3c8d5c908abe2a9c73bf91861994ca3939313172ed83b15b95e2eece714d42dd5e987fdc860555c44243162d3f884ad4654dcd0c5e7abdb225659af7e7ac596a7578ea16ea9cc4a74ab3b1b192271d22328866559ad9ab66d49b06c648eb0323b1cd775a3910c8fcc1019567421ed903a1b199d9d23602082018b38145d2a0c433630dc6e86f93008953819485c142f7536323c334d668a389f63a8144d782541ea10cd8c000000a315002028100a87842281301246a226fa0e14000c78984a6c529f0a64491224290c33881844082180000000901291191900a02ab68c34f051546ae191d4ac74ca3ecd8520a365f6a62b7d137999282253049c7ee8ddfba6396d7e7016a4a76075b875cde1bcb431e4156674ae0c44c7455f25e1c284cc5b2545f96d2cf9894f0eedb95cefe7cb8c945b3181d0de9f1c9e2f1dd52ec8a1c2a39b4664ae2eb5a21255f37ee59daab9a9436a74a00a1bfbe9a6bde8abf62a6dbd6d8f88ff81cb00fe60dfb2a5502dac0d349c2d5238b82ed47dd775c55897ab15a78b0dadac3d2d11eb30832a497f31b55d6ff4896dca4b6958a28d92e732153d1b367471ad0e91c40a6987e0264acf61fe2fc56caff0b53b5141f7d3ecbd0696639696e22046a00f2b700017833a8e62a819600165ef05b961cd37c1aa1ac9394d44a3813b43a2461783871c990517e0a1ded749bc40810b736814cf37c1c0c05afa5e7174398304554ac5f20a51345afe8793018ec98ec1f13e9b6b276a56113c23ccd463117694805e52a0b36f6a47bc1e74494ed2135bb61d58bce607094a9c63b5ed9439c4c2a9120162cf554d5c3019aa27686a6f0fee4ec6bd897903e0b788a30b1555669cf49a23189f1ed987cb717bd429dd30d019112d9b80228b5f12b9eebd66e9ffaeeb36b40e2b29b5acc6d8cbc8c4adef042b9264955c9673720eba326ed54d57c137b0e846177733b2be5d2591cba7456fec94ddc24b71f31d6138e35d00f50fdfb9509ebac78053a769ae4891bdcfac21cfb300810b117b7fba34d1cac924e0794f3e345af73d3eb9140ad7306642330c8867f8041757d010b78c0bcfda81c622e7732cb223e2ad0d25783d307a9b1406494396795c7eaee29529374998ab76f40d44636730f3c4d67d68147460bfc053f835554ac209e006479593f82b6b5a0b5eb03ad2ef58c6975adfd5b2492c3f4db33d5d53802363e49a78425019840f93902ede148be7b2a495ebf02c88dbc3d0d061ca2ca5c8db11f0ff33d7a447d64ce9c43b63ff40c8320deb37e90114a5862087d79505c12f1d3ec21c81907f371f9daa0764081f1a5c572f20c86fcf033f4578b6e0a4ffa36885fe208842b741acc0c8a419217019742ee656fe9dbbbe619d1441560b4fd7ca78766ae8737ce446508118da11cca7dc23b5ab50f82e1232d9674fecd24f426d02ab01b2a50035ecdd0eeba70caf884e82dce01983a1eaa777aa3d743d93110cb46b4d0d71359b43d1dd5e12e7fbc4864b3b5ba86d7e7fb3cb2d05d9b3e0b36fd9b0de36c0302a8a79f6677974718de505ebedd49449a2cf4a1c31b0c14a023accc0867d96c507c5d4a153aacfd6c4300cf7562a9e95811f5917055029d3281f52b8b7af1677876885d83d5ada05e5a8d620858bf96ca1a328a6bada37fa39012a050bc1593212fdd94d5e89f8c9a875824534ce3aac620cde669307ba8ff023354c87909a702c4696a8c1c8b6252483ae586162208d90a8f693c498233576c46a5e82afb3459759661f6ba7b4268c27afda0be3fa5b46516d39dc7552755fb306012ae07e4af8c6ea4ca13ac68bc635e2231a7d458dcaeb03f0b35811d01499b9bddc4020686f88eb512c7646260788a95f66466d1111db53081a200008b1ef30f5836fe48b0d1cfa73c240d4e2af6b085bc4c5c1d3f59a0d098843b1baf70ea60309d2009436e088e224a088fbf38c7793c01558162216372b9e5dacb7db97a8a3875b75351c97808afbe3af78bae042ae1c9bc5f83707b6bdea65786fcd39e5838d128851ebfec72297c58e1c215716264ff29fa0879fdf2c78e4aa45e7955d67075882df348544534742410df717ce299d1bff487885d219fe8d3d3ecd63f1a059552c6479cd6f9b01760745514e03a4ae4e58044586355334a69164a71fc29c4fca5a4dcec168d2b56a3b2c48a0abc29d044b11c8702bde87ded7e722dbb09bdb344413f61635e0fbc8c7964ca67cf00396398383973123f3d2198c10ec12071488cb8b06a00cb62a20f07db3886ac3b2f81981c03d2705485ed3f17859b6bb22f305d56e72a3c22afdadc5105fa516b17fd0f749db599d10541628841cc6ce126b2b14d42ea19c9cd3007f0eae54e1c52669b1300c77041d52a7f15479a5f106cf21b132bbf424e7b86d8294d318beb44ad6f9008bbf1592df9ee6fd6c6da292b8a1bc2ed7d4a325b7eeb446313fc88e9abd42c81b093c27e73edc52d45caf0d758f578962544550bd7088e9a92e85ad43a02053822e8f19b392bb46305318c890100a78e996fbb89c0740302c4b40825bac576339f393f2ea2b85a070c89dd1f37c4804a4fd8b3641ddd59c3cc8abeaf7b39c832ac7b86185654bb848d2e875cd636a0149df08dd33091463df823db92e1eb5160e0455db54f0ca0dacdc182cbd79d2cd5e589ee609bf470b65c46a9ed0820d45d51773efc1bbb76bec52d8c4639ac5f96ed22f878833ad1ce8154c6d6fc234b4bd05a0bace7412ce27c511bd90439474963520a94b291149a89a5ea78099f8a7978b0126562ad5b14aea2362e05ae9c57eb480b7cc6cd3bda482efd45b4d52141008a1b2d0bbcf46a1ca36a8f4b0ec78191cc2ea5d10b85b99bbe91bb33d1025b5cf741f40ea1bc6fcbad87aa3e2954f61c8ac0c3acb56193443d37d0bc4a32688f42ea6d95fba2b040f2a902c7fc3919eec351d53c6ea4a7f55a3c61f50d4a59bc34433b8ab705eed2cbd8eabc68a52645ea88f23720506341c8416f76f59d83e67279b246b44c5562326569922d183da3f8d145639c34101b264ac5c475232037845a301a34ec14e128518f2926a29a741c1930f5adc6e3b7bd5c40b6b1ac92d69d3f2cc4ba57676c2976ec140f6bf45057272a771911fcf132f7aa7dadf67282a37820b592b76bb6f2f2bf42b58d296a908b0c5808665521a78a952b0dbfd9a6aedd162a0cf18a8d613353f944b8f4d8fe7f5cd70798d7d8a026b00bd559bf3c0b71681dc40b66bb499a4fc3e298cc673cc24dec01bf929985dae750d7feb9ae5cb733d69a6804dcf20b2cc79884201e9b4cff033d2db0173748df05e0b507724dd2a2029c4af7a349339a83bf4c62e5a546c995f888676f0f1fff5c223f666403e6fc60c5d5d43b3ca37ee0ba0888af01b3315542c890198a2d0194903fb98b95c37aa9e93001ce0f902f24466bbc494f285fcd52813b704a47b812665cf52964f422387146627c8ba3bf0aa26f630edeaf54065bb428f730277556f969c3b71795478d298e363c64e72066ed26f093e4f43dc967d2d1c38d075e975b71e5b1cdacf52c19e226cbe0448801ed1f5eac69cc5948ed482c4ecd48c8a045e66d640af3d967bdbd9762830b276321f510aa1842f507d09a6a1fbd81c4b5e1186e285c3f0b3c6bee7ff00c56667ce04a3eb545eef0054f481de27c475eed38ad1112f041cd08d658af266b19a04ceada1207813c81549c8c0d6dd32de2cf732a859ee55fee41fabc4418144686e054e5d3f1f805c1be1064a9f29bf42ad911e140b039539f9ba61bcd7c1980a6f528055a0717ef89b7b89edcd36566f5a44ca051ca02e7ee85a01ef9eebaf4ea8932ff5dbfe1342296820144278a2f660ab9072b89e535e0dab4a048f61a339c48bfd71bab9f462872fc48e8d3ce07557e52950f5e9c800ccf174ba06010bfc2b3e00fec0c7c7816c72725f1278e61d7adc09b11a64c300595383ba28574a5c548d77cfa5857c0c7ad9799966035e19c56fafc6960845000f24da4f43842a30957ae0902de087707a2ad0c6597944ae1f63f727386c6f58781bee83da8e3abb798f06863d0695f1ab8bd5235dbbe2b26eeb0952b35c5d53f4d3acee4fb3e8f67aa52d0b52e7091f192ef3266b5c7ed9e2ae689b1ba89d76ad29a7604151ccd0d15d3d5004c898d24dbcfa306de73b41c511cf1a9f142d58d68da0ea983819a8584d7d9d0d2f1904a50a4fc049bb289974717bb45056960f9b4bfcaeb4ba86017f382e9bbbbedaa6b26b256e66141533cdfedcedb4b6fbeb582b959595868524aebf593d9d24b7446cc327976ee720809e91321ca4244cd809c60a9962153cbd729f248e00999324c03007ee9b25332281636935e366fbe743cdadfa14c672ed7d6ce5a93ff131e30d20b9ea1c98b7478ba553c0e6d8c12bf308516cf98fd16415fcf80accd3b775467cffe0a413c1902ecaff809a97e509b48751fb8cf8835f8f58f1ecc5e0b212a81ff62f5f0eeb6b7b700ab3ec412879c75e890c1fe9517f7f8715b345c0dd57ed988ce3f99597890dab9ca188788246a1e2c846aaa7a99543a0270bf10ea391d82217a720af000e4daee166e077a2a9519e94fe96c0891d09b51388b782ed378a4227a1c1edcefc7666517e06513ccafc0c7d5bc1a8d12218d397fb31c420c968da717fec86e42490f4c7e346249ea3eb2276d33c293713fd361af50ff784ec4e271bc83899834db37056da7e193fb29da4a2c9810198966645a1470e4a2f8340cc12a8965514dea0b149730df3b61950a8d722220dc431b8af150e0d5270387b82c4121521e1d57523c62bf692e7849a2f0b70e438df0ec981bb5fdf7f249b0245fb80e4db5ddf2c0b816fadbca72b293cca5b03498e91046039e7c00e6bcd91baa27b17cf2e15d7ba247fbfec7f069d1184d223133f11b86d9e1258f0d38167fcf87a29ec46803c1b843f51276052c05ecb0cf8ec1253c31e8a662b3202b1f5a9fd74d237214f96294bedf7f0ffadc51349e3a1c58a0e1f7f3c90fa94f2262037ae329a00670a0bb5fd3cecb12c5ae37b96c9061697ae469c51329ae29d02a7e352c2198e009c6c8c0dbd4a891ffc17c4fd38bcd84072fcf684b3ee41582535e1e60c1ace5879dd47339844f98a300fe389f38c7912976c464f62f12757ee1036432e7c9b94a450b3cc0ff467cc6c7b49fccdc0584d2836564ec0ca5ee636c40f08b3e57e333ab319fa30732a8bce0366a8d6376b5761955b16b29b697803d976e32d9048b3d16abb52de7d694e2af773f3ea4d0db4db3afdd503ffe1cead5250be5229ae68f98f8d44fff31268bc72eace5cc970f0eb2d7312f6ad8817dd68f477334a35f104016f7b64550ecc77a05ea5bdf8de39177544c5265c8a5660b0f03182c7bb2285d7de42d5f240faf6a4b4b8ae95f23897030348d45d0c6eec3dc5a9f2dea2b0a7112a8a0991b0048f890b91d807c0a4cc0d6bf478904f55852534f98c7e5e46ce19e5490e89f033045f36b2b7efa4796ba624da3b3d0d9ea7748487c5e8aa19ef1ece077b616b54e8b023ac52c380c04284fdcc1a179abd5624371e43625dc229c9edb135f68e6a28ceab31e5ea334971b96502cf54c3eddfca26af813906e81aa65174979f45454d7552a11fe599984bb6c2db77d40d34c401e70969f264db7eda4a27e4cd276b1d6c65f0a4e09e653d34bea0dfe743c729690a4a4a02dcc94d18d9401951b9e78aface00aa3922c77b092e0e8c3476c07652120c5160a315410c4391686273afcd755421db70edb9392aa303eed8bcb25b1b64184ec8cc54c37a54b0f4b1a6bae109722b148fe54697b1acc47ca528fa7a84878824c1ce8e92a9b22e286b83ec670c117a662627369e424cabf484b009bc7087bc3b12ffb1d3c007130f3b99c0dc10fa04b94af287b2e92fec7cb63aca49e90baae29436c6d2462852ff47f2d0fc1445538e2d5c2970c9c5c34ed179508f5054460895752eec34ce3be86beaa3fe471b41018ad7204605f24b2e6a68d2e4d2cbc48ef4ed3bed4d39386ae45aaa79bdc019b458ff472ee4e0090e82840c5e90d44f00114667e6e8c21d405638c9ae88dbccc138465f0de479a12a47bc893ca1def00bf22300e52f28aaf1c271d35831090a0b2a07383316675856cdd5c9a1ac7460982d3f05a88a6fceb2cb5c671335e62c30eec0fedfa03a856c9564f1551c216a18cfaf0162af7425cd606b1931b61c540642bec39ab89de170434523d8bfbe914db2a212a95a9c515dc99a357c69f208e22a5583cfab1dd6a1f731369342f1f1ce0f35b59f6a2ba96bfeca4fba53e8b2723569f2f0a31810f4fb7ce071ec410281a7bb462fa3ec036a54e2580f39709cb7cd3c98d1f9adbcb5c2b3f7e31f4b37c34eb19fcfa17e3141c0355abbb6b2ea03f70721c4f9c8a41f7a46c8fccae640d7e598eb622f31d3affcaf40690949f684483187dca59c786c77b63105c6001eb2f5b6c6598c8e7af4a1b253b377c3dc2af29313aecb80d8eb9efeb8900f1331440c42259fef29991c177c2bd5b498915940e399df8555c7826368867c600b1c071ea1c8aedc9fa164c0f22ab404d66be7ebedcf8c9b1734dbb555e3400748afd29d2691788bca989f66b711b3e28a834e60dfdc3a60ff921a8fd7dae8516f19c1a09cced1438524a537a138843d5a5a9b536d9b05084c13693b55a0d9def9e62750b49645607fc2ce3c6333a881029dd65c68d423ce9d291411e03442aef2db6517c059a71efc452383e21e1c27d0b8ede31e50895dc927ffa9cfedafef1db758014250befc62c009b7cb26100e14777f45500673f73b27e09ad7d0ed4defba1c36e02b7a6cc29b527eea12f436cf7e7ad6b92d8374dec5e1c737e81e320f5dc429550695f5d37a0155fff2604fe0851abd0bea2a80bbe88e482e5576977c42653f588cd45b147fb40a8c6d8ee36419d1c868ab6137856e76c77b533cb2c2c26181872a2d206f5bef4ed9b94e582d91ba7789ec1265483e2d4ec9606e3027b085fea5984e159bf8977b965e026cfd1dc5a80b89854e6769c1680a568e1812c3df4eceb6a337d1b82a2dd30ab2c1f490280cde47fa0980d7fd211ecdd20fc894eb27224325e6f6068390163a21b7fce587e85d2624eb3c97495a9d12d465444dc1e2f8091f1bc682b6463529befe197750740142ac01f2e0ffc87d26578887ff9e7ca0608e43b7bf00b65285c7a72b842446f58214545aa94d5ab9e8554ee35e9bd449ed8980301fbcc2b608e02bd8063f2626242adda5582a06b7fc72944af3df4f50cd70c48919eb7417d32d228d02b50a5d8a44f65efa03a13337aab7eaf8879a7f57e3735718bdaf86b66edbbf73859cec474e0778f63202b0d5fe2b29da7077b1c90421d82713cbc1b76fa12562005fbd361669532cb57bbd5a4aea1963d99cdf92228ab71c496d2905d87be9a5eaa0de2c405b6acf982915c6fe2aa6f1c77247ec8c5f1bbc60ddfd45d00a2f6dd6d0eea939fdcdceeba9d1fdbf2a9171ab1a61366fe08599343ac2799c66442ceb4ac1161f3892e6f7f6af3d5020c07f896fbcae068acaafee5732b80db325160a2868b5bfa6822b9450d17353612bca3480197874b657da52565ec06852975a7edb43e63fa90ade386eb1cd154d416cc5b74a1ed62ef5d7cb198e7d7af79cad2828e30c15ac8c09cb6c08430b2e7110ff95e620d211ca7051dca54e1561984dd98998eee98a5e4bf79aee8d228d10ec2d6f6fba93662943c92729342121d60e8b1d00175837a9408292bf17c2c0519ae3f288d606880598277653655f1b666a5035654ca7b03a5e5c98ec5ab3b8fdf2b61ca7b0b2c114d51368d15b0179bed4b2196e60fcb67efe80c174d963bcdfbbbf08d1b0d8385b6d93a0000466925fe60f55b50fcb51b13410362326fceb163cced4c3792ba59ead0f436a4faf24acff650a3d3edefb391718253a7caf98ece0f3c659c4ad897264b69148b848cc34fefab6bd6a238c8ce1d623f018b911422e88477155457b8a629c111e07bba3019d0b10e507034d118a7d09d68d1796340a49afbc61569be1d5a889c1bae696b56b3092dda91121b5d126c4488518b344e3c4c83a63a77406de7869b3aa5326e71ffbdabea3722c389a449f7c045fa128f01c94ccefc8f9b4607351fdcc0506d623ec3dda6f2c9752a5767b6437c0af1b16ead053647964cabbfa5cfdc052125485bfeb9139c3e749f477fbd42b81ecea1261f13e6fa5bcd8be0a9e5e0a99726bb82c9463f38b080a7d1fecd3d157faedcda800f26f405a7277ad2a324a321d8209df5f58657c2945153a05e9f8535f610f14bbfe2723f9b8592c86809c46ec32b112f993e01b3f0e4c24f7e08ee2990dba846b5f9407b33ed4553d119860c042f2d9f947040bb9bf2164c88d901fc71358643eefddafa0d4aa4aceedfa2711f3b2fdf4efea48cd2dba0836b740bd6ff448812cfa4a79a5e952432049e84b7ab6e74fa0faa07861b6a94f2f31ae165010605b42f1b1036a2bdc8b145feb8d0075e8504d76c5763629f5e49e77f6aa301770bdcbbb15549dfc25a8d68face7e8094f928b5fe56682a1418ed3eef083ec0dbf809a35f009d4d02994a020d947fb379f9104017cfc781c109bc0243b45254bc19f2ea43e6959c38b35acc718458e3c509f84e004f618c329ae542029bb6085b143548e68bd6ea3583a1180a3bbdd47a2006785b95ce528bec3cae19f40b1380891e33b109395e3f719b425863ac0f68127a8fedd8c6080799e9de3e834db296e972788e0552e791a4a4c1fe0bfe1b9d0fe1d05b7a671bebea9e86eae05b849e36dccab19248534aa48fca4a06d1d7abc303b423d9c725d6f6c8b581f371b1e46e9cf83eaeca82eb311afaa9749e1d5e914f6c94b9cc51b6054b4d67efa007789ca93abadf387a5cdc99d20ab2ed02dce3f1279d351464b5a65229b10222c7bf47dedde058599efce02b5e7d9ee9c52a704ba8a7ebb3e52eca39efc45f4cc819d3f5637f7ec208c3798cb963724004b40b5577e81c20ceebb538b7cfd8c095c6875ab8bda08cb713aa79b6db61833161e1283954beef93b5e4a0ce08f1b3cc8da8a254bdc2e3ef7335428565b3630ca10b2c2adf99b46a7b95bcb48a798582b51d08d906964d3eaa3dca448627fe6e3c8e4282e55e5f2d0fe8ec7c2371deb168a54598b99f0e808b9749e9c08c7223f410e54777e56342594c96457d6988819792ba658d8efb751526e61df36acd9a805398eb1ac67a82290d7719c31738479d44dd825e91de36ba7dddc2cb5d53f74bd843ab6cf86b0a8cbc0e6a96114891963deb156514c5c14ee177e3e698702f5c6f4046e74f508f4d6bd13d057eec510d811d2d77127d4d7d183f08644ef4fdf1ba127bcd1a147d8dbc88d0edf13a0a37b23a09fb8d712dc1bd3217013d67b36f888f43a6ee6f58cf49ebc1bd918bf7bc158826fe5869fbe521ad34c79a454852a85674c6238f41f1cd520f5f03006df1c83ec227880e2919e46e237b73b036e3c7e8f60d28edba018f4cc1907bc894c0f32eb1f630aa8873a1fccac3f8a09502f75664639003e260e412fb7befe3e833613e380b791f981ccea63cc01f552c70798f5c73103ea499d2d8057de87dba764d38d7bb82b48a2ef0ea81c0de6e301dc962907f89ac79b01e8b90e6cc0d42fe7c5ddc3de67d066621cf036323f90597d8c39a05eeaa8623fcfefe24ee9261af80b9268bb019aa387f958003765467e16f470afe0e7e861f25e003765e1cf6446106848fae992862433f589727b4310fb18147581acecc7cb41adf279a733d9a38d4c0dcff38d643993487ddeca96a75e13fe3dcd3bf38a0394ef17b6269c40f0cb5b0f4e6c438f47caf2b24ad9ff059c6ed0ee868fe438679dd5748e0e20f16e103adcc992ba47732f4df339ae78ddf7a4551beec64dd9d1c147b6cc5d49a03cfbb2fc3121949b50ea75ab3a41e9969abcfe4602057f4324a8f43552de5bd4a743d86cf7c2190ddf7eebf00fa6205efd22f625c674629f5705f08412d3cd6dd66718c306b98341b7e2abad6b744600f1c76c8bc3cb4e2bcaf03149262027599c73588c59151c795143580e444e857caf278e2dcb5d78ba3f4012bd46254675aba42bf24667a352e8381226d411acad468f10e1eaabc2a540b59c444789a308cafa2bce07559cd159712e4e662be5808521a1b2143e4cf3ce5fd74f02089f193ff7071aa5fca48a290d04408a19714a0cd79e024519dcbe99712005eae5befaa33e19d425834b12866931cd4775fcac8b21f6078a88f51e78feacf0591e8391dd87ffd797c9ba32503d0beaa77f22a3cd606401cd8e6ac897a4efe5c9aeb4e176545712f99a49d0175dc2c984dc2c5248713dcc813d6af12a2a4445380de1a1ca465b739b9e4aa496a82918e7eefc1cafb5faa3c0b644dbda57c5ebc8082614122df3ce0e01f5f815f0d5a74f439c26b3a0331a0c805c3bc00a23bcb71e5abb643919d7c08d6c1b5e6c0dfe4427ce108c3b5b6408ce9463c6e51e6e4fb1dfdb1375fe6d3897382f85b68c4bd873eb660cd03ca19b6ad1d3bea43292892400c6d372ac5d721c33360a567ee83bf8beef8dde749509cb22aeaac9e800a7df2aa1fc0b7ea3d4aed5120b497ce3a06479dcbca1a22ebe0a7e356be924563d7a4a1da34031456aa3354be818f4357a151bb1acf1505165969ba7864088effeab407ef4dba4c20ac3b40e9ba2cdea650cd89b1f2134a97bc066e86d9ffc575706eaca809794a11a4bb322b67f0ae322feb323a642e3ec77fc63c277a4f6cdfff5cbb4ae0ca8d69ad2166c353599fa2968da65386253d948f2445725ce891878c70c19f80410fac046f939e1c809d61f8d42394c15aaad7bdd834682a0a4288c1d73ecf30cc355a45c431596283a119d28d9498ec6c59f97920811bb59bb5a4d06d1a8d1004cc52e585eb5e3d06109491828ece1b76874a5315edcc2bd7cbee72567919c40eb3d7f8b968019931e46d0a849a16e6b176e685fe9d5d888b8f5e46905b0cfd0e2d6f5c63241ed49921ed9ace540185945b1ea36034b54a62c09e43338808b7581366ba9e948c401274e14394cb5ffcb890b97b4fcd82ce6c49d5cc8585d24b35252422069f7a790e76b015ac794f0958f2c3e49eaeabed0fa6d16b68b553236111805e9a94c14f1e303c1fe3cef18298a73b8e89bf00f7df127f91cfd14513db5aed4f1c6f1d30de60c25c8b3c880784c41448306b4b7e987f072841e5b77724b0cbf30605eeb501d8d2e73d6fb569c2f0dff974cb5461dffaf2bc3ba4f86cc45fa6885f37488ad6175aa143f7eab8c2efc98c4e7c83e21db1618c67ba4b6076b367d525d9fceb351afc55ec565956f8090a9090d4d152c41ec3b6ed8321bd788e3b6ab075003740a043f124ff118527430cf0539aa229255e51cb8fb2fb68cc88f7ee7d99f7d92674c60d7219f39a87cc0c9b50612e0294a940c7b5054f78c70cfb4e426db918176010a006556d70d75677adc0b74cf0b9451285f5d66d7cd50c060e7073f66176e079e898380ab50782d6f6fa720bbf19137b01eb06ce4532985a06e20f9dc294bceaafe0c4fd9cb6e9553359444944b0926297d0c61c5ee9a28ec03ca6fd6f9240cfd82aa1f48004d0e583c4d6ea12c379124a01ef5d564d6743fe5c9e664379f2dbbc64a50aef67ba19f38ffa93d2b52153927ded45970fef7295c1c448e817645e60000c1c2084b11ff636d83e8fae08bd40da260448bb9ca6cbe5cb62d9045254330d4a082a811489220034b04a41545064e7182d31abfe2364da80e31c606304683b4853c3c5b860686e26e6df509e00e313092e78090efa12c73a0f95772bebb6099c4afe1aa4dbcce82c933a5c584a457b4bc5dd10cb98c4007a535b17e504ad6d724a68f171407bdc55848d0e8a1fe8e8a14a527e0b22ab091bee35d64e9e86931e70c585a5d238418f7a085387f2792d4c1519ac7192acf8f474b063d1e0d004835ac3f0f7e806dfc6855d263399246f52686100e93d00f155f851890a6d6b5e5a85a0ae11ba27e83f197717fed9a09d7073bdcfe006d1625d0b331125eac04d3a1cf9e1a250594e382834ede15cbb0ca3fb98e3f0bbe4ffce48ba10130437920e964054535b5d0c3d6230dc8b944854b887278b3e529a0510dd17842c65fde963060dd22095eb77d3f20c90098f7fe4340d5977fa11accd78105b9aea5945ba4549d135bd82b7b0c41d4909b465d0986697f05510a8428941bc1eb5795d78182d76e1f31d30eebeaba92e3c971830b055596a832713a84522bc2ae886ab62e2a3eed5a575876fdac3e7078d3115282037f35fd878202fbe6add0e2532eccb61a033e8b3db914524565cab5705ff1eda7ac2e23c4c3aa224ea1f9c4a47944d954c072011e41b41d58c823c5d6f0efb07cc0436444cd08da6865e5e1ce63a0bd22cfac8d81c603f5f8d22fc6b2a5411d6bd161f2c04410e42d7a3852f3f0bdd21fa63102cd3cd1ce22268a081a629fdc4cfab9392d675f4c19eb1b4e32b0e51035ed471548454374a06747c962dfa66a26d0fdad6708c72db3e500933b15f1d85badda750a3cbfb9f7e4d6b2ba8f6b2b6dd037de1e6cf1b23f00d3ff15c9d18c457684dec6a22dcbb18e91d278ba17723632779d87b68a98faf4d7adc5ab6abd6aa52960b8810dce184c6428c9fa7e6d5efac402c958c517c5466b33e8558b6f7978e5ec1bbc2040e19a3a61f25ba16d4e618293a8d8ea48cb3046fddec41cffbadf148614db1c96f50f24a62c5e1079c6848297d7c6dca1cb234475e40ed4627e6765004fdeca9f0ad487d792a03a15a6972f03eaedfbe7570cacda5d24a60749668a081efd65e4dc38157e2b5f3dc2c93b3d2727ea0c0df722fde204950451abe3d0c6df508d87fc62a124436f55590412d4754339015a95444001a9f6965fd2f51f43dd6308057abac751ae261e0b0d1ea60b7557b47bbab10f0e85d2a27485cc5bc19c93048268272c96d82ac177fb72be601ab5b67a914d9020e13d5e55f84c87c77a59ffe47c28324e155e319fdefa9e402c3de15a044468299e541564b70160332813212faf6778aa3afb7e96ab632272224991f70ea62f4106fd815052420a891967987a022e368c70f4bc478eb92ccf46f518489f5dfd5297cf62417048e9e42b99f85b4308cd7df446207ddd22501494d83c4590f6c422dd41a114dab53a382fcaf02a03a69893217e48ae612f9984a511b4bcf0987c6d36a54002c17e49782362ea71a731705b4d7054a1cfc89a21c2abb98eb26512a89d702a6c391a0f6700da08c9ef2b885b84b4d331c544f00654c86305284e2474a07e69b9dc5514aa98d66672cf9e3f9fed19c479cacd0e3c4516ec68a90ba496a5d612b09a0a1b3134e15654df49528cbb2030454949159a26ab6070b1eec627e7daf469ee262c072d0efa0128312890d2dad16b264b4a715c1485db2fcf41b676b072bf961a0b99f761050efcef75b9b8d4038ee31b411875b7ffe7e3c91b0a8d5453eff15f4e8a6daefb45ecaa1c2c645cd324c621827a343000c1709e0afe1d41036e5039421270994f18167bf2faae6c8bfbb4b2cf35fdb6a880451e467a4327f8858253c700dd69273b7b87948ba3d38838377e676db9248fb541535c3c8c892fa5746af716a4f1d294740b6078c25b26849e593a62c8de4ed32221096e566be0a77a1b8d0d679b458b3d0e2d1ad0d648794d5202c1f2480072f562910dc0aa81a25a0ac75c222172a941275826d0aa534a02e38efd52104572202f6cf760e1bafc102d13d53b2da1f2d98730381d955255d3fe1e33c9ea9d9d54d78c37d56703d5317beb1ba8010f4859e010d5395137eab349fe2ad63362ac8d3be3c4379d65c4960ef9662f6b499bfa38ef9db88c7a6e659c522ab41a28b814a4c32f4076929bde014090a93b7088237315add7567052c69a83372a24b19855431ed214a2f6e588b9e74b9610e8a2668e9781db29ca5a866d886b8f07a4ff9f84eac2730e67e5ca22b35ef237d37c891ca25d133c8d3ab828efdf921505e8eaad03dcda97e5003443135d960c4a8897eaaba8960d6036aa1393ba040b51a66ffbc0e915ce2c67117bd5c8589145345be93efc3a172c15c78e5b0cea83e76f61e246e29b2541b4a8d7a904f57e10f63d6a43f74e363540f3b45d8d1ddbd46307e3fdc4b2b1736c8d9468e34083d8156458a1578351279d472c25d834bc1e99a7b9a27de19a9e36e6a929e6e46d69faf147c23232f5ed47362fa6e12c54c3720b5fbfa32337a8575736fa04874d03a8fe28ddaf76bf85f9c5821fd19ac663beae487eeded11cea8a5887547c06b2abb5ffc79abd9c4e3e7cf80aee549bff034940f9c7ea6096954f24700bfffed9d1364befa7485a085c6c5c2cd3486dfc6fa652c2c7fa14d61aa0cfad64c73a32c9dbda72e8d775361f3b4682cc566c7e3d77187c3d11990530c3edcac28dd82d19b4ee3b765ece5c17c5422612ab8cc40c8e6c272e21488da3c9a497abf15b1e6eb26fa1df191b7dcd1dc9a2a92b8dfbd2ec935bedbcbaa2245ef5a09394842863cb3402c64540a856c0dcf444baf52850504884e02be5b075bf72dd586a017cf62196b1b6e40bcf037782e14f3327a6b5414ab48c61b54977169fa54ef82b244d3ca02ff93fe3818f9730234f6edcab36d8c87cbda2e3f620fd658bb8f8941067df6077ad9696c0b203300184f011be2388b5699f0ee8983011c95488b3af7ed85a521211713d2520b04acdc9c24e27a14109f16491831bf24b537f0dbafc56f90b220e725317dd4bed2046b3605025619a7152d524b082475877a002150cb88d73a7194ee7ccd8fd70ee3205e6ed9d5b12db943f2573a3ebde88d0f5f68a1c6b08a8f9941f3dd810f30bcffcd6c7b87911d4cacfc2907221e258e1cac079df6408f38e7f1f4c70d640dbba159d685bbed319a007581a74080f342e262629bc4d407c9b07f1ad9039f250e3b3a7caff7420d1ca365410d4474b53f282bcc957fe17df2c889dcc15f97e1877ccbcfc30a8ec656fd182b6a5bdab34dc2f6ad033a7e40894541c2ef1cb901f4498be03598b6dc4506660eb7a3468e5d1915c1c6051180bc1bbb988722b6c28364bc15a416eb0d7f315e4ff1482826dc5383d47cac656ad366c40b773ed5a7d1add6f2310830926c2ad6489927a8ac35085942eca4b5369d7400d72b6e8eab7652872643423a68ad466cad66566acf65a1da7052240d49577bc1c83501b8af838d26f43e01bd5be3876139fc030a4c7270715c6a89fe9e51b56a106c7e50a3b5a4faa2a707225399aab3b6638beef0921ca68232a80fe551e124eda5c394fbcfaf41050c2908d9b604dae9a5ffd24de1e9a6137ba9b07d7eae76df041ca0c01cbed2bf8e8331a540cca03d9e1d51bc9eb715be3f7de584b68c78549e16bd398420a68e20e1e7e3157eb7cc8a7a53952ba69d6dab9a28429709b272d77d1ff2b3a283f04d79456c8c554256daaf1d86c8d7dba4f791aedc4a521cdfd1461d7cef51a56d2c0ded5aa19f14c77ca248405fae9fdb4ea5e0e48b58df1997affdfd4ce0a14fdf055302bc60fdf9e6e93357174a90a4f9d5eb2a29743296d022381e597edff5b66f21ae117602f0a53a943212c89d7104d3a8bd97a8743fe7a6e76b259a18459b3f1cbcc7a1c71848c774a9a87c4c8e54af68435d84ba94465d021a61fd305b988a8e3c0091d7cf15f0bd1bdd0b6a91d2777ce22213f404de3183b0135121baa8265998d383762c54c57e4d24d2da96e9aa778bc41b648cf79e5ee8005569b7820e05362ca8976d531b8342f048e4a29913a94e8c711b9fcc8165b049c0627f2cc4945c0125f4ceb75693e3fdce0d56d1b4389851110978bd15edcf3611e6a09a9ef40d2579def28513f19cdbed1464d26b819c66b517404cc1956114be8989f19fb461b40bcf8034ab21bf6a743550f3d06b976dc177d42701178cf0fd49144150b747c297da677dddc3a34446280da3d904839f82cba2c7b014f25a63421afdcd86ac812932c10d36479f74bb3a01c3ad121d629d16386909e2c051279f0c14482ce7aaab6ae46683df64b547fecc026bd2643628aacfb5b758c479e39eba1d9a409ae8166100b4f53d13710296865b5bc6b11e34cc5c71daeff22bd9c07ecca20a744f6574103b7bb92392b2938f14299e330a16c3a1910bb52321160f96e1534954ab018b51807cd860c6b81d6f5303ecdd152550715e18788a40effe4e6e76610c96a1cfcb5c924ace0e8b1ce8ed7a3a06b0fa7ba07081c489758489b078e317cf1e7b95517fab6cfe5262bd3aca7636ae3b205739b7ee808ea71398f663dbda3e72b5262e93b6373e268984db9de77bfacac9700e31e093c0dc522c3b284f028004ff4f45ff63c5cbfb4cd4b2803a2f635f7fbd7fa5c0375c589683503a435eb3c585b4e4d69014e545a7876dbf900297e177f4a573b895c614fc02d7fd21fec683075fa7026a5fad333130351e077e59ab06a3b5553402babc1dacf00de5f0bf31161ec06fb496f5a5ccaf1496e62f3b06ea879a822285d16851cc8044306af9e21deeb663d3203d395329927e151d11d7b168e5d940f7a247b49c833810a57052ea5e81144f0e7d42b36092bf50ef154169e314ef5072fe4d33ae4da98dad290bd1e924596e4aa215e563663473c4240b70c209f776ed4348fe050f0f2b32f92213ab02fefc600401c0d881d8f1a4dd58a8ba79cc4a8395a8fb0a0cee3c2862e10f4398a18c577a79386d83cbfa221922f3d6c94d71f60ea6e1bf14ddc8a3b07d6b3a4cea92054ffa2c98b6dada072d020e3d0991f00a64437bbdd05a3f56b049a88da1f31bef1a4a1de18d8690efb053e247381c5cb50b76c5ddbe0f506ab5c0ac6910c61d222db47c60d63569e3a77bbdbef4ddebd4afd916ada6f85bf082400254a78454c97b05939a0db68ef0cdc2320552df468b4db3a98c1b805747b4405592f1212b0818ddd82d584c9566edf6b46dba602680bc18d07c14644c2bb274a00410f773635d16ca7e308042901a944aa9b3438fff707720d3f84b34cb2a239930c695368989b827814070b1c83e0a0d012e36fe0aea734f672719d7307866eb3cde91525aa8b89088a721b586d93a10baeeca2b503b4aa9afa753976e7c4a2d4a63c92c67d867eb25b461505b0c5e34ba7b9ce08646c0e3de5845e3207b8fb8f10b2253eb7687166afb8e24dc7725003d6518493f695d5ae553903e847ac8b3ed84cf09f5f764089a740e86db5b2c45dc1638c9022bb1d70e79a1b15486cc97d127a9a05af2c9cf9b938daa25237d56dd10945d9f8809dfca6f898ae086c34d130654c6e2fe4a2786dc79abc9c5cf2365cf6a3101b8cc07c2facee84af121af0842cafdbab316b35ee169a1641b39d79d084eb2989116741e0fc7c49607c76453be314de7f594cc38de136ccd293325098debc61c3ecdec93b6181267feff5eff1b22e150c3f2b75dcf8ed99099ac011ede4e135934c45ae73253a98aa4a109ec092e0e84574c92217f2d9e6d7593065f1a32b643f4df7310fb11e70006e008d4a16727701e718b99d82d3925e82997ac7bea4986d7f82718615818b0a171862ffb488d1e4cf3018c05ae7979eefd9031c3f23422958c0f902ffda4271010039978a5962f554b2807e0902b409af9ba7c0d1a997a19dd7a4176d8deec6f73f066253a87121c902f5d7dcb9177e42b3ea66fed0dc073a649eddc83cc6fe7aa9a83766ccdbaa35d79329f208d4455c80c4a74bf5be85f216386c5ec56c8c468c32708d45332a8e8d5124dbf73105c628b84b8403b07b994f49b682d0fea9005ad91147f9622aaf5f929ab2eb88028056aa1dd6f91f4f5547998b33cad5ea8d2c829d19b94c5edcd78cd76cdf2a733e833360be1d6d4d7f95824a492e49255d1209e679126633142048927287e3ede7f4d934df54f492a5cff0251e0cf9a8d0d3fb864c4b7d8e60149980adc89dc5ff2c86d6fec4c43435b8addefdd0824637b81c9e5693c9bbc95bf7aa160361fdbd9ed0166756e25d5d133cb0f629d11eb74b91d1d0362dfc8c74bd6d9962551032ed6c03e014d7a7e78a25cd048371b6950ec64ea94b966b63a0775c3fdecf04c5f199a5704b8cf855008e442818eafc2bb25698bd6f39a6bd34f74c195a5a2f7e9fcb712860d250a279393564de2be7e7ab48af5daed2d2ba7c4b27319994ced4be98a23101fabd10aaa0109cc0b85b2849ed09dac4c023856b4ea3f3dffed681dadd6898d3150fd6486f5f40726686c96309102d3d10b7c11b0120805960ecd72c71384c852c1b83b7f2c8f2763c4afc30fc6a741c070ec0ccc8ac4fcb29d09cd3389b005e7c2c4422d27f06876942c34beef9c71d78ee388ebac411d0d19ae27a47ea12b1901017ed9001bfa2c8c6d03658b10b22426f357f275386a7559911630f2c4324966987d3886da7c9873acfc6b7583d07749f387ccf23f579bd9259fd9b7626ea6975b6dc7d83271366b1e8d747c41af227384d62355f1b6ef19788a7b5ccf82a22db6314f148dedd43bf614030259d211221d34d619e4fe9283743a52cd0a1a3b34fd04aa01108ea3aff5c0da0757a7e293334163b32a212d0a68e4c85b132924f9a96794288d53a1aabe90ae44725b8061686f5c09e1aa2f1e5ede616db5fb614c900dcc701b4cca2a0e4456878c16f68831a465f01a676f08690806d1dc468ae446811d9a19ce802d307ff33fed9a91a88eb65c995497e56e783c8b4ea7f753348a8e06de4fec885861c4ef990a3b854ce07d2cb51fbcda373fac1363d56cee50f216627539adcae54b28b79f40d5bcf9d36518181ae135c3e0585e48e0737e7a2a20332414b85a31251231b972a127e827d7324033be7bdffd409246540f3609b9e34930cec9e3aa0a149a15554ce0fb0d012e882b14ebad95dffce7eaf6ab1762b5b5898c31b02dc701999eb4d35b69d5e7796517334baebbaf009426c929765f890def634799b5d82cf7b5c9e4995b84c1bbd396ad7b345c1054f355343d2629e1ddaa2790ac0a08bcbc40f24b449625f53e5855c1aa4b0ef0ce44a7ef7db02a19061e0f9014052d588536981f2e8c202e4798084225b0e0d35e2036776f90232cc446bde23f192e55b9fe469a8a989e854b22aace8f386dbf0938d533c681edd300a95cb17d21b966a43d539dac267a22bac4bfa536b2954791803c9335ce93a2eb84c1997ac1db82821aa2137911674b1f54c5ecaa94485160c4c9c4b9ec5aa78d6cdd6b19b812cd73538caece124c7d6e8ae4b8acbe81efa25982ce8c1ce8b20494c56b302befd2d27ba0e441bca040c3abfa6d5c7e1b6843bd094a9d5011960068b4c072e8d2fb357d6bd66def86e0c555ad0d6da4b9cd34db90469bd36c330d6da4b9cd34db9046dd03cafaa546a03262048ad59252a0adff77a08d7cc410a525f4beadc73220221a6cc5ce309c4dd49f9f946399d6e13b02432c55791f88474a1b8d4e3f7e1b9b28991b782d14af42e05508bc0bc14ba1782b14af42e05d28bc0bc14ba1782b14af42e05d28bc0bc14ba1782b14af42e05d28bc0ba13cc2c6b843e117e0389b12720808280f66a8cda021befdfdf2d5aed4926b27d89b2a54c22d055f434b02eede1b33bd564c323ae8ad43919af508a186575085843316a1d5d0d7d1d5d0ead1ebd06ae8ebe85ead0b8ddbfc6ebb7a01b9c06fcf17d97dd5676c30ddcead0a13270ecb8ece7dcc8accfeacf8e229f0400bd2ab07f0406e6a9983cc7713b7886720cd09445e4e879a63560846b097f35cce061d5fe9efc4831d54d4f5e02cb003ca9818733cc89f4bf58982212cb479b2b156d50dff8e6788b1327f5aa8371a48938f2bccabe0f45d756dd99a19faff0c9ce1317786941f81dd8243e0bc64e69b4fc6bc1ef44ac02ba12e9ae23e3b41b9bc3e88675864216b318d89685cc98ad39278d9aa9c28428b2fb70b57e29dadf580dbba22f48431f6283cb16708679cf09d024822cb309e38e51b8d1144d46cd5f834c2dbed0d3579142695fd9814553a8fa31aa2510bd003ee4f74a43f69f0ebc933c8049444c108c718ece809fbfb1fdee443a95f06fece22a5a07293e1930231410744b2d62315471eb1a6903ed440200424741f9deeded4abb5d7eea9a2f068ecfa5f211ac5e991001adfb9ce59e615a18feef4eb36b93974844b059447068a7de85c41ab7c4be0a7821d3a548a9a70efee927504951fc9cb163f1239f1b9eb8028f1a1f4b5eb3bd2d4c2faeaa8d7e5c5cd0bed8804cb13bd7acdea44aa9c6fbdfc33c0925da78260e88a1b7f529d2d2af9239b95e0e6432bdf556ae2e1391deb2c0aada7f85f49740a7b3c3b3f47b8a69f778c2c503a58e8e8326bd779b91bd2dcba0b44cb4844e5ce7941d6658ff6318456283881ed53c77abed86dcf7229b38dcbf373d804d351a5561a693bfc3b7b7aeb405eb5e1abe3049ddb12017bd4849d9c4fea297cb2e746470e1340ca0f2531179f15eb0b638679303acb65aca912b42e5cba87cf255650100b7a42adf92ad268c5fb2bd06e06d9b87919bc5466d9cae21b4278f4ddd667442357dbc7a9fe648ab222ee0f1dc05dd8d5670d5ba4d223880305ffa2e4322e29b4463c2f00c485f82ec06f50718866b3b27c36e1a56fdc4d902497bc5038993e2d6948e794109541f3664a43042af643abe100ce2d236b454a131beeba492c8f77052c26e71ca94b0e820c986ec5af9612548ab91b8fa488ae50e2e565c22c28967bd7d71d034990888d391fb139850989cf6c0812eaee9ede1f28a9ee655784ae7cab4a95e7f7bf86a3d778e96f649e25be68fed9a96cd299457b5e56664c8c6560a1651957282befcd751a307ca53a9b034d0aa65ccfcd56d08e6d040258e03ea25ca7cc95f0d84b203cbc6aac8d3508f94b9c3cf905cf602f75627efeb66c11c61e795a1f3c40116a71ba18d328188b1525f816af930100115942820dbe14e44d45114e6808518eeb9459cef6932b1adc3917ca85b533532668ee0073ce4a5e954012880053fd77bf062c6fb66c49cf30bbe76c1bcca4be23e967199a898db7fffd8f0667c3b73337d825a9993a42d9274d7df083f60a778d63e9d5996b2f4b429b6c6049af189b29dc1998bc515c9d968a57d7445d5e0fc32a9fa50561dcf8163cdfe839aceb3a85e185b2d69de7712f82e58b8c9472c2d92a932b7226096888bc6587d0df41f93986645f981d1f981c307c0cf3eaaeb2d738d6536a2abf5d2fef582a77ac1a60e635fc162031672c9942be78ea7ce519d3f4a46e3374d2773f809b582786ecf6fcf06cc12bd0ce048030c959b2f9830e4c8673f76a9f969feb9c649c9e83852ff064ffe63f6589baa6a28ff150b92727b3f387f3a96b3be7cce9beec68b05645598dc3451ea06201bd7ff9ef84286eacebcc577cff0a2c152f4375e7bc51d253bcab90f045435c9da3e80cc3b996fe82822ac959c623453e71ca3352f303485f2d1d3aa5f0574f3e882be8072eced7b3160662399555ab5f10544ee31c6a7fe3b30f5a0aeb8ddb7938bb5bdbc93776e1d8dfbe41a96008ea2268d6564f338484f19113b15d26810f21ffef21f305f75e61b36470089fd4c75a551668849245548de0cdc93f88af28e8b7e060b816dc5d44688dabf98ebd46fce2bc413b4b0564a0b846e8fadb635038e81f97255795830fbd643b1b18af927b96e6382117bbfdbb653d01b93d32e74586cefaffe9fbe635f35dd70e03e4558205115ec93ae1033724dfe2d7e5393c43c800cdc18e20ce0946aa3676d8a4a8938802cefca41bd4c2d5f5f4c9a3bc52c93419ef254b8c5cd09dbda75ac6dda2b28c9850d57f52a81efbcd111c95361a7304563e9ae92a0366cc2555818e6c9a3ed003f5eee3cec341f050e3923dd90739311df92a6f929585b4c7b3c43c2f57ae516aaf6e2ea09bfd20bce08a42487780274d6f37bb22aed59c77f9c91c59ea6829a0cac126c42c358a102137aae97ad09982d9b456476a20550131821e9345c784f03c9fa8b34d8fa1b60f6bae096279c9f3a308c26768b4ccf5cf6fb7e7876a37f7b90143283fcc869abe12fc8aecfeb3b414b699aca45947947b0c32330b661b0767e1d941bc82a421af08563c64e0412d65a9bd1c347a305ffb5244ea305f64e67af2e6cab886a4c09a5e8dccc769f47889cf3171a3a71eeff546f541e6296e468e14ea5214e1ada44092c046d1cb7b17c1ffbe1058147aba8239a6537c28dcb07b193d0b3ecca35be6886dda6d0222d4abfcb610e2b984aa9e6910897b49d33a23dfcc56fcdac3030ffa7f3635051ad7868f835c8814ab1517a7286425e15f6ba8e552b5d12528149333019cb0d4f4d08608b105eded8c0839597c77b6a7af4cb052d970ca4aefd02812340126f336e04fd5db44d233e47a79c5ca4528ad21649d6fa73066e0544c5ba14d79467cd5d24434c6fbfc706639ac847dc54413e6ac955f6284fd04494302f12cbbccb41cec829f220568847cb27b62fe342d7200e336a4c0edbfa9ea0e9738aefce37d6a26b923657433321a8b21fe84f9792a26111fa7424ae713ea5ad106e87a46441f58fec640abfbd18b7df0c1f5f10032a0531f89e1032a53052ea98455d0ea901bac35b23b0957d5bf1cf814af6cc3c076fb2430a4391d35223961784bf71d0f523c54452f3083eb29baaff8b89dbaa803e29abbe668e4643dd8fcb8c8f5d7bf00a2a7dad7a2814b3df0087b7d129e8da5bd07c19e4a6265e257785806604b1b3d67f1940d4b62108cb25e92eb16cd1455c636626a23942215d6d394d512a21fb34477a054111993f4bbe050f85db058077a0dea8b956d330e5968004b75ce527770a9cb0a137afd56bae7d9031a05e55616a41b08ff5fde29c2a5aa5d415fe6749964b8d5db7e9ee363730ebb3bd7d56d9ec5669043e1a9a0b80ee079320c8e69b2403cb152f808196281aff8179eaa9daed92910d82f826f5e629853af0b2075203aa8f0c1920cdad08c526716957a4c5c4805ee733b7c725138835bd28c6614e1187302ad7664f9811b3abd3d385b81159d692d695c7e439616dec04a39bf3809e9864718e3be0b650aae57c04cc113eea2757d4c910beae2c65ce4a6757fb731a568c6a0dc067287435acc5733ab5628e5b9da83731b7f35c814b2877f699a05fe51e1fcc2989979cb0a1364b3a491512a2d82f28918e035f80c7f2647259e247df5cc252e333570fd920559a3980743419cb841b91c6a815e01c5f82c91b8075b53267e39f273946894a38b468406ba2011821e4a2c41ee42e88b89e792d1970c3cacc9e5206885a3762ee3d0a643aaac675426464b08f2376f06d45cd07cf09e121115a9dd92176254cd1ba46042bf5b131630460a930774e292e5bd5cac7d2ca83590291fae32700f0367a5076ca3214ce328fa99059cf7d281a0ea86a124281092210eae7ddd416059c8770455fad9d70759ca7559cbbb9dcc21b84395075e06367ca3d0bccc9a5003297018eb394bf16aeb96f910ed68c1c2ec314f529c3916c67f0f7c2026595d327db4acc01bdc1fa63042353de807c0ba533f9bc67f72a92cf867a196085c97c17304d2a8f010d3243cc0c3e7dcefffb7bdba959015efb6ac52ff1383f907b5d4857170b6bc82a415b7460df1180c1c7ac21c224d8a84ab3f60cf1dd9ee982c1be0dbbb2f352c0c71e99879056550bd882409696d2430c45eb48a566d32d2dd80e25becbef5ed1e712f76e477371830cc4365174c7d9f1ce5d1894c9da3446cfaa3e34b3598e362fe845d91d17150a6004c21c1ccd136626097006d4f8c58632ee2707b8370df420f404136898b8bf3829d3a15590609a7a02dcc0312a06343eefc6cedca6df040b07c9d1ff14f59e27c84c3d6584ab2626cb40b86a0d8f573619beec9b9ecf926b39d830793b69b4aa2841e0c02ae75b7d6a9d77a36d9c6f7f1c2e35926839d46f6e7a58fd85058dc760d7faafbb541197146e1f8f44a8b42ef1fc6a5a96545f1d387c72776c5e53d1df7fc24829c543ab8070982c2cfb55201c2838dcf2a843031a2000a5629ba884da7289897037a77b20c0a60ad95a2e0562fc2916710da767411b1688ed2bd97fd40fa07fdf9dd7252228f316ab54ee606de5bcc0996932e8d540b58f7c8e824173032c853681e45b506ce6bdd47bdf46bbc303d5734895698f487ffd90faee54eb870ca551a7fbb665165350216c387eb8be38a1931c94de3bfb3a3af9d462dab19270ddf4f293f49ae239afaeccdfaf66bb392d454f74282965a63e027f2f09c006a50abdba135041a1478758a77bf93fab3b1d1715b2c88da512e7993de07e5ae5c8f52925906fc9c2a66a7527669951ec08728e914862a00acecb4a74462c1e26b46c3f7cc89b5d75dd287b91906fa9bab83615bf2967c67bc3326e0625253f0fad52672586b7460e0614f1969bafb875b02fc488b1a9a4af06821d0a7c4836569f7ebba8c84068f8545fd366bfaed659fc0c0618ebd73df14eb0256c86b9a07d615bbd4b0c3053a8e6adbdf3da4bd1115ed9d54fcbedfe179f82c3a9e49df11587c3cba4c4de8c5dea644b4de0b81573887ba626805e5f53a3bffe6989dc26b60403f3cae4aaa8eb89bb63358c5b492d8fe4cc4b802cc96338c3bd5d7b25a01498ff023743737a7e0aed96ff6454e05c71de29f632cc9b1e3bedc4522932ec3f065139d007dd303e3dc4f6b57ea1543da812f0d4fc70353e552268fd1f5d8168b30b2098580c54d0ecbd316cc192837918ecbcf8d5ca74ffbeeffb8002312d74b31d5510326d86ebf05613b67e196f66571616dac28701bc5a6595d34bc2988e0692d9f0302606c5250a0dbe41d81716f59fe15f1ef9c7e508f8827d2b78a48c02192983801123df532694bd15280a24bad11f125f916cf72c250f9de6b8a72180eab7d175d3b992e4c766df65c1d692067a199c1bad94e80b0cc047881ad250025477065016c2a029c4012b29418a61d2266c4db64a926c0cbfb7beb997f771d28c44a460c422356cc1be1caee45c75149c2de7e37afa9af75cad523f09eda555eefe306ba1bba3b5afd8639e13cb5785ca24c095300cb8d44ce86edf70a9d25654b5acd4862552cbb357f4b657b47d11fc1a69713220fe4e33f7922796d1da9035630198fff0f8b481f8c559d26c6b41dc05abaedd268dbfb48eecfcd5be8108d997c348ac0cee5ea01b3681853270987ffce9d63a22e8dc32a46e91a51a761215512dcfc2a66f6e61b1ca3a2b93152c42fc9420b1fb43bd3e22e17ce30caa2ef16e980cf586111332e9451db943c4e0b4ca6be331511d4bbed4424b849605ea40f6bd277d2fc2abe738c85a670956c8e06dd3260f7e6ac1a2104759c7406b4f7a005f7828279a531f691f1750fbb3439d5ae05cc3d685c3acd1e77483c5ac433d4c604624a126f8d14bbcd8c623458da7a8d06b44a3805e4ad8a4767318630f33b5fc8b7b1278e7457f3aac0e15a98de384a7bcac29ec080aa82f54b56c14f292995c1787c39571fe6f8136a1e3baf2cb95254eec0819d50e1f26aae24f18d7e56327a5767f32450e69f55e84c4918a47e829c2b4c241d5591d9e164ed94c5ad5df4a8cbd8e638c5db635adc489b63996e57d52a546eff848b60c4f2f209a5e00c004083cd63aee427fdaac72907e4adbbcd5553646e33a08a3981aecda72cca8ab09c242f84c0b0ac5407411b753352f592561d8fa66f3e90033ea05acbf9c96fd22126ccaa326da1a46260fef69846b12c3fb269f0bb1275f67ad95b9a4ee49ce560691225126f10b81e7c37cb10dcbdb38418480701370028cbf9d3f3539da51462d599641c197583a80a66492dbfd90a7ce88b0da4d99fc775a5d4aba304ec2a4506928ca2832822c8b66da4c7733746ee71be829746d040e7c447da48f5202ba8fe1e13f3c1525b6501d281fc1ff349dfc238f4a3b05557ce5a44fad74bfb508af5f8f621ca8694811227ef7ccfa96a041e88a509e41be55d224878f986daae1fcea8816c1236c3db2516e12c998a1a8a049717300390804b2664e554b3ff828fce8c0a55749065f7c7a9915952feb6679a8e43606fa45986b1bbe70f4b9594d0a7c94d16b04dda83cfeb352864653ed657f3039a63814fa2e34f2d61abd62f1a79708937baa453a5016831aaeb9106de1eeff673bb3ecf7988be82235d7a60ff7d40c3e32f5e16b7a806174a4bbd11b914520fbd71681838c9ddc0634171de08939306d6ce3e2dbef7e8aafc7db286d225d0f0d23ed830222125d981c7feb01706423729d06cc75bd839c0f1504c8fb20cf87d5854b260c45fb174222ca056d5782216a8a0f02b22b7aa32ab1195dcb353738d86415b46e6ed349c71cf27f1f9306c3d69cbe41b94e76bde659a55e6d1d18a980b5a10264b286cf1dfd02f0fa8ee7c4f72d89e761c7402c848f2f9b8bcbfe7835a97caa24ad5e53ef2c80f5499540f9e8abe89ed7b872ed53de87bd18fcbd86da992c11795fa74df6cf0e9a30648bb9b2f4b6dd5a12ca7aa9ca7c6a7f60e4373b6c8400376948508f2d11ca210f833bf912c7f2af027338e33c27f70e2dda11648d2aa1ca147d3598d57dd44cbc89ca2ae329e5176d356bd94970357cdd0ea5e9f88d3133d888e8921244a7b666a7fdd4327f23d0d1938a572fb52238e821b0a21bb5159ed28151218766888727094290af00ea8b509155e80639186776c58fce36da8c27354bdd2324bfaea27274c70890de349de0bcfdde41f795488cbe24e07c61eace6a434f72b002249469aa43a60affe18b522819fa2c5e3ce0782bbcf7b72af6035a7870c1b3fde2a9e27e2e26567454e1aa7b00a47cf4729dc8004e14a01749fb8de1b8a50edb2305bd3189f6ce5ac0a51142b7cbf7f657dba11a1cc607bcad9e67e256750a755f345d289124c78b1d2addccad99d24568a414c880d4dcbd5e1a495120f11b449803d6d9ed6ca1b9d077b331c4ec8be734a4359f53a98e82add50b5710854838083427614c74ac4d5674080b6f709a3e0d4b6082885fb991cf74212ec2d5d736d4aeffb365b59fec1a1e779cba30b6348afa3e7ec40933ec7ac20865494795e8d1a3386b38f0a2d94651a820aad2159b0c419cff855c9f4e2c906f2979e232c6495acba4d7a103295661dd8200743ece1e34323f9974b50911f0cdf48550e3699e03b175087107584e2adfd0f157d2462f7fffd69fb2d3f0f6e1e6a378d1e12fc903da48cf902149403dab65e915e3050b2a20e5664526d51adccade55c2b1319e7f0893857117715777d46c307fb75de6d7166cff9ba6119f7d3f0b8c51a62c8905ccceb0028481582d2625da9c35642ce826692bf041fed3cd62d619486fc4ab68590b45d42f696524a29654d099409b108ae1abb971a3b971abb568d1dabc66e5563471324fb579936d8e0273bf1945e76794b477120d83ff711427d0b6f3eec6542a5f711aa2aacb0bbe9af8d211b24b04164230c1bbf9c9f9c175c2f2e39b71347a7b3a10a52e56c69554c608e5262fe1d099ce061e26683e8d2788ed05918bf9c17a064c9f9b13939b0d052d7edbbc893d64f1b442f36886c10b56c109d368836f82ad206d1ceb9759c125ecbdd19175bbc61c34f3ca9af55cc248fed867daa72ad9e2a0713443b58a283240e2dda756bbaba12af29b165ce964c782d68c806eed26a3e40c13db181b3f9a5c46cf31b6557f6d9ea3dc7369b79b9aaa338d07fe62aa43ee66f54e121d19a9aedcea874fb96270a90f3e0680dcf0f8f8dd6f024a1b7cee3029df9741e22b4dc652009227e42ec76aee7b5a01a8ad792500eec8bbca6a496e33526e4d853640d5744ab478d20d2ed9f37aae8faf91f10f5da500d57b35d5a4d499193cda4dbafa44dd535c963bbb9c125e3fbceeae59538124c76ca240f27648c322ef64993d8d99c32d9b232338aa278c79103bdc4d3eb82a0aea268429d7878ecc4a21c320f241e4e9c52478043e0e8c148e90d1b5c2b9902388a9c76d4aa8f1c08f69903c5fe04fb17f779cd7c8c2779e86c99a3d01c1e5168997797a6cacd4069384bd0997d1cdd9de56c8e0d9c2d750e34c7060d349b77b9767997c46e86b7b2a01dd88fc15b31a037f649deb282dab0cfb9cb0f6ff9862ead75c5ceecdb566f08345f64ab473bb07ff3038da77767f62d2d7ad2ed9fb408876b34d8af25a11f84e05339a037f2db7fce23e33cbb3bb32fc379743c3ca218018587cee6f010414b9bc3974395bb341e2274b6c40f47776939b4a03668a050ac746b737258414b2388683d74fb28f2b469b2a47f12d7916d840bddaa72dd82248fed06887db67a1d17e3f77767f561d896394f8f4a083e97895edf5e715d2bbda648237add36d1eb112d7490123d48dda46c8c284e478e6db67eb9ee9903bd5ed46513fbdc45dd1675dea5a96e7786d4cf5d4444afdbdf452f994c260435eb433046cb4a8c134fab479e7854b9564f95d37a830d66d0a031ab1979d49c7878ec6c699fe6c4d3ba49da272dc225b1ebf66b4136e76cf53aaea6c4e6e46cce09e4fa3aae3169f5e88d172c26237b4e7e50c61ce5c41e977d40119b6d9ea10289ca5f1d57b8e3d2f4d71d5624fa89cd8152b5940e7e8e1d36c735b3c53d8267ec9067acd04eec93f2887af078bc1f3f7edc6c8bf649793f2ead96782d3ccba3a1e6b96ca83a0c41ddc2dbb9d329603001c40e3b9d02461260cee28a9e9f4e0183046004a9885832ac4050b5d657f0c00219a4a0c483ce54830115921d16b564d8e20fac2d78086221d9a84e8f78f840cb0b78e8c00f0f2db4422084c5e547e80a212856332d4e6c9611ab853d06c11a7212c366e15ab010da8017424458495bf4a22505bb14831094a26b95a9f5ab6badb516819328436cdce9911324946c53a7474e4eb04312985ea07012f47264a73a3d7292c48991136bb584139bca04fbc4450cf6a6a2c5c57e91d2aa32c46a053b385165b1c74e8f76c8fd6cb1d3a31d62580db1c3cf3ed2a10633e57f79a40316473a201de93084ea881d767aa44310ab2c9bd5e9910e39f02083900e2e5cd92e9d1e35e14285c56e757ad4a4055aec53a7474d9828f1142f847ea63c6a324493235252700a7238515314e9e9ed941e13d3ab64532af5b8341fcbebf12a11dbf776dd3e0d4e0efb052881019636befd52ce39698f5797e4f184fc34c3680d1d4b2f8a2f9ac8d267ccc1a08833ec63af390be9c3bd27345072ad08238a0f2630628a5503453e9f79f9cdcf14ad144a8088a03c618a378928769706a1dd8f7979ddc104a69855c0015d75a1071c7ecccb6f80302b100f5408e1c4bc9c42e1421366453274c39997df2062622233f0e0635edee2c1608aa611f35b832b547ce6e52c1e0c4c511c31a7489c00cacebcdc3502531c47cc2d0d4f7420c8bc9c364103533c8d985315cc00c6cfbc9ceb4cebc2079af099975323b030c5d48879cdc0932a38f372d28916949882c8bc3c0610a6d8d2d2c2020b54a698434c23e8d13832875017d0b89425e6220c3e493ee0cccb73ce033098e26ab5aa1e0022d28539a41482256e64c01c526198f967b158170344507c30879c6e22a833109943701043b0800a98cbd56ab584e80106e6908c448710b0f6767139f1d42398b001c91c92bf78c2c47f7910b088d9423631f9aa248ea9636dd87f29d95fe586fe72e2a9a517f6cb88793d32849420f3721823537441316241cebc1c048129c23041451343e6e52924a618f343aee835624e8d28e261c8bc1cac628ab011737a451630d899976f254c312663841760e8cccb4322a688812930f099f2021d5410040bbcf8e2c9163bd4f48b0779b2ba25bb26779039caa14d5fb4b27bbe96c52afbdc2f9b8e0344cd9d1e557141cf242dbdb0cf906e7b6ba034db1b58c4fed89feb638d6820ae316974c198246e80de7bf1d5faefbd1483af391057bf14882defeb1f3a0977fd3e76dcae05608363cb19178cde9308b96b92daf2a76b0c669167d242c604b115e01ef6f59cc35670cfe79301f6f9f00de33ad66124f88665c0388c8b3d4129fdb969239dbefe1981b83870cb48d82bfd32b149f68a7d81bd62af5cae2fd63cccdd8b43ad31c6d98728f2fd3bdce28b3aac467a7dd26907c1f075883558d2213e3de694625c2a954a295dd2214ad4e187a1118db5d618c573224371e42790b1638c492a7bc13ec3aeb54b7fc84b8fb9c62188bf34c2dea4c326f204824326fd86a10e35c518c40f6270c418c43804b5c6d85acc73c6d7d61513d9570a07604ba5c230fcbc7314b68439655323df39dad1ed1ced1c95f889b3ae8f799b9e5e9aea53a9d487a9177fe7a87eb0531fa6c89da3ca4198dad1ed1ca5322f27d9f2e61e15550ba777fcda3dd521cccf1d2776763d7cf0c4b37354e23bdcd1ed48d9d1d99ca3f077766138005b07793852d93bbb9dddce2efb50562b713f98328222f37e8f49f1bdb7c5757998b4b1c581881fb0d613026a4fa470c308a3d76a6b36ac3d218a63f67513aa0424fbfae951baf644aeed6a4f9cc43a9eb8b6e99f15689bcf3e155a84547b625773a2b6ab3d514322e26705bb86544f19890b6a73e8d39568eff70c71317fe557a1d6eff7f4572645d839e15e9997e9f6650680a9ac8b54a463ceb786d12d3f8710e9f6a9e5b1f4a2f2d3146a18affbbab4a8531287d1453cd257fe5b3fb044ea0755cc633d62876fb22999ac87640ab21ec2fe9e42e5f5f0da93cea8911a20bfe92f2fbd29f50b406fd06c6d5cfdde1d0670e896c4b43e3e25a0c7dc7756974801443f7710fb74f518b494b2c882427de0b735f0724af24b2d618d34a07e6889b8dc498916930d44a34f0a98d2078a44df15563803cff4c30271bc921295aea128e421dbf00312f1c2364d65df106ab0e5295ac46c084a76d805976a69212409ba331a6e71c3e8facde0e7a50444aa0c494e84d8ab205a599450c1b077a75392a4a862b7743a25098b1a2465e940151c6021b14b9d4ea962c90c6c36a7f6e09af20219bc102212e94cb5b54b3b7dc9243e7d1f2530e04703e88d9a7a940eb5b4920638ea77045d7fb5640168cd9d4282ce77d0528acea1232af11a2da1f43c69e1365b07d3a25a8a3fa774466f8faebfbe0defa51ff2f8ae5922ecdacbb949887148770d6200b7c051b492502ca2246a40839bde32c824977cf8e1449fb047748009ed433fdf099d2afda4452745a2d273165ae4fc6116473de79e077080620520302ada99f96d76fca0bfb1d3295914d14d9d4ec98228050ef5fb62135b67f54daa983a23066b332682e8d6b7997510803b53032e012fcb6842cf5401ce0841c8a434930422b139679de1870975067aa3d29ad09402f0e6baa20c3046a3f034530525a388d48c942d038a1619455451ac6680800584092d242e4db8c0858399f9c5d020be4a29f65971158783e966c0bc180e57713d70976653711557d6b0cf8a2b713b2c65c0a105f4b208dabde0eba1e22ca8bee551d0985352c5b556fd7d1505c57c023f06d582ad5ff53f4ee38b6f2d4f7502b101ffcf1994f8a9d7bf5f4c71d4264f143aea00b4a7481aa4a3481b5bda04509346946dbf64378bafb8fe919734b6d4333d544f59507d901e53f520a852bde65485f1d7aee62adef2f43402355b9e96427eca65d5d1e9b5da1251288ea2b85930cac2ef1e359d2f69707214a004066c16cdccef0ddbe51f199ab139d60194278e7467124593b525d89cb306f5f0c1001a53586fb60afc3001880f05e2801f26fca03c072411e90c8e4d17c73789a2c9dad1447f333425bb430fec9c33fcd163878d8e0072f8608009404aa61c8e024091212bd4b1b1c1a9361d86322ffb76375bfe655eee652bf98a8f119c12a4a09d135476bdcccbbc9c9479bc6c25f3f490de8dda76669f1ed2398dd336fda38d68249a8976a289f493fcb339a7d6655fbe12fab44da7fc70b3603438b51cdaa67d5863a86dda88b6edf034ea1d0e220e8be29813c5caa35ee46c361b912119243d81529da854ac58da68aabf9a63b24406437d04c1134fce75ebb2e59f60faf14f536ec95f876c9e5e19b2d96c44cc5cb73fb26668682753e60970929479e33dc1684dfd717cf04b7c2481506f32e54ce23a048e234986f464b168c39b941b197b5ef6e0d395a3b8611863b9a24591103d345992c4151363b6180bb19fd812315d0c2926458cf7c2440ed6d3b1d3a1134f327e3268b0faed20f6908e221d46e2c9030ff8ce6d478913263b38df9b7c3edfcf0b3728763c2a44b1675b6529217941fc116f2214e24ea442e4dd6eb7db11f1762322dec45ebf81ab9f083fab9f89b36c4b5e411b8a0d8a6de440a489fc1c2554dd40f23d54fa580a89bd93cbe6ef86a1958fd6209104b7d3953e68c807284f56bf17dd1e32994693698cb110b3c57e624bc474312962482fb11f97d84f2bf6c38afdac623faad84fec27c6db297a61c29583f5603d584f04dccdcdcd88f260e7e6c1ceedd26e70c464d040c6efb7faed2054ab9fb8faddd5efb4faad7eabdfea87514aac0d32e8e6860c1ac5d96ed6c40c89c60d3470348268ec00e37a7169b1563486664dcc7633241a37d0c0d1d8814610cc0c698634439a21cd90f66a86446348f6af1267d866d8c69103c93ce73e422397f9176f6e6e6e6e7070703cb8b9b9d9447b68fbb0734b9970e86e8ca4dc0cd91c1c49e0f8317d37449786c3851b23dbcdefd24c2c38455cc1f9b139a6164645374437443744374437443744dd8573eb37243bb83bdb59fd4c269369347f7b68f58bf11722230f70dc68cd8eae5bf116ed20b125ce964a76724137b84bdb71f2a4dbbfc1f970b37303c0cf0480d58f8f1c888f5c278a000c21e1d881f03372020713386e386ce60fc711a31f1c448ac8626231d21911612484510f463bb91d18768e500eec9bf80e921ddc8e125b06ddd9cead8822b04401eedc86ba7d92ef04ed80f0bbb41d243b4c40f8ed2801e167fe7ef5d3210509dd2acb7832a14ca0d6a249870e48c74e8794532a0913a77d3c712019d9a36464fcccc7d051a4c3680f79e0c1cdcd78da43434348e864f05518337c5583187c8585e42c1ccbc6f259fd56bfd58f86837767f6777cd8b9eddc3ade81c1f4f6778ed00f7064a91c708ee3390e1d8e221c443a8c8c9ee828b2393884307b266f0f5d9a89c583227c1ee82ecdb4e2a688a41b9ccd31ab180ded21a326ddfeb8878c9674fba73d6494e488cb856e7fc888c850d8776e7766fe563b37990c0e1e8e2c463c6a0486c9349a6264486e994cb8cab2faeda1d56f0fe93da437d860060d1a331d5274e856bf9d1b12ba6e7f276773ced56fe7b6836407b7a364f5937273590754218a2919173687231f3a7db1079a63a2e5504b149dbe09896a9f4eb3d0f2d09d45a1a5a59f93a0395967cb9f4be34f3fef284de6a9ece967dea5fdd3cfbd4b7b3dc52d4f75068a84f8f5f04d3aed2dd949fa914e3fbb40697947d333d099fd3363a1347dcbd139d0d2fe89af74fa580b9aa3739f297deca337f6531df4a88f795ac5f124d6310b161d1142f4d0c4d5317669e9d71847e12192058b8e08217ae82d49b253a10cb795c7ce9697a35e56929191f9c9869011c964349bf25db21d0c11313719241e48a329d5c718dec29baf0d7c1b70d1eaa98054391e3a1e5154a63b729c1d8eee7453bad4127255bffcf617afe1ee8c890f4e38e898ace53ce0edd3f433fd2ecd9a7ea6df29f572e249bd8b7813611071e2142292d813b1a0d9946fdaed441f8ab77cad5ecb4a49e38741bc8938710a1149c42227871544224ec489381127e28e88381127fa0c70a31dd84ff156afe5b325c86baf30624771a0d76b97041142f4d0e4b58ff839e2c7358e280ec4f9e74cd4439325207ff17d67b483d728e362c38c979f64dc47a824934921b185f06518ce96b1d88927f5b1ddeaa9722d1ead4162490e6957920d113d9102a5d583d96518aca48239da325cc8f8647eb2216444b22832dd8bcccf45e6d792f9b1647e2b999f4ae627f393edea2984221822626e3248324832484606c885e376c56cc44e3c36703670a74ce248f9a9e1c2cedb808b0d7c375baba70252b57aa656efb67a63ab576af55abd560f9f98581b33391b386b63cfe4646464546faaa1ab11a5c68e66069a1b4d8e260718d78b4b8bb5a209aa11a586aec68e66069a1b4d0e3439981abb4f3e96fda979fdfe6e167aa3f6cd93917b479242e1f397fd5991bae0a48cb48f9385ec637f569e6b8d68fbb3fa53f374ee16754b94806a6adc9dd97fedb071ab8e1fc6a9f9859420748b17a61c79813924e65f3c0875c28a2dcc2043681256cc185228f543f41353686b2b3627f5b47e0c16be7ecdbbb4578f9a31af44e76c89ffd4387be316676f5398a05f27da0619c1f361a8f4827d5cb3fcaaec13e3facb8beff22cee12f3addfbdd56addfa8c4e8f8ef4562ad5d7138feac51b7e4fd7fca2f45fcd5717cb5039583d6a8571fcfcce22b112dc0dc3bd47f27475d6e358bcf42dbe42eeb917ae9528b14f88c4f0ea174a13a1505a473484cb31b11a8ade692750a0c64598083fc150c0d7fcc5639f7e7f4f8fbdbec784c13ee67dc062c82042f7d5638a5a6cf0c7e69cdefabc7e5146e66bbdc6a16443ec987ffda97938bc32851c740206e5ac0dd0d6f2324eef8c359224c8c496fa66cb3f413ff8a77efd436a9b3662cbd2b684da77c3f047af803ea0111ca8c4d2ec87faaa3e276b24b4c66a2be0af3e048a797d498d5b6926e08f911f1f5bda0ba23e2435cf96200efce9b69cc5962bcdd33edd7e875d52276923dd3e50cc2f0c7f47fbcc484a6c8c53338b9447e1462aa8e8a7f651b14896f5ab8721e8c803523a4da7471ec8650901459edf516f7a590bb6ed380cc3184cf4d2dfd3f56ffe027ff53d3dc65ff7c9ef3163df633eb1a52502398ffdabbffcd57a97675db1f55f8dbb3d186c4ca4ffd264debe0ebab4d77d5d921422fbd7c34821423e64ff7a21fc74c54c20eaff3da12dbfdf85b7f808b08f91418458a48fd8936490d7c73e48ccc3f48b9aaf584f4fabaf591e3a06520051a5b35e3ceda07d4502d11141c7400a20789db5fa9a9551e207019bce224fb8bf7addc32095f2867d62a22ea3629f190923dd997dce91c2bee236e988adffea2b3647a549fdd3be2bb6cc3f235bdb72961ab6fe33ff30efca48d9a7e6d99c7305ddc1bf3a495bb139f755affb212fe96583aac9d62426b2258804deeeac8ce1f63482355f2f00ed4b54bb304ecd18a7e6e69477b3681c6f858c6d48f67991a06cccc3a7b661eb3fb1cde6bc6caf5b2bb6bcb498b7afed6bdfa5c15efff6f7132351e9a19fb867af6024ebb33e0cec33ffba7d187fdd187f5df235c2eb634fc2eb63a410d8c77c8c1422e403f6312f24a65d78f40608dde278f4064cb7b0b7f6d262248cc44831fe7a188ff95d650711f201fbd8c7c8202cd4304ca1f3be081e9010a049f72f090448c8fdf9fb30d2fe344612baa4267ad89ff6d7edf3dd3e35ae0b187971f7872699af8ff9f3e2b8e66531f2c51d633868e3daf7d2bf6e635ee67dbc3e3642ccc39e84d7cb9edc629f184933d14e90b8ee813fbc1b88cbe5609c9a324e4d194e4d8dbb346aeadcd5bc18a7e62b081c6f0a1cfa25714e7037e806757bc518fe0a3fe6a9f9e2afb08e1746dea117bf4477663f86d33b8b39da2179ed974fb67eded5d9528aee913ac996e5d0d6e439a4e3efe92f0e2e61c6f0141348884c7a4240265ff7595d9ff0ba5fbbced952e3ba56624bab79b86eb5efbe9c7a5c76041e4404bcecb3fafd135ef62b77c1f6eff1093dd9fd9bdeb045505a9f84540b8aec0b7afe800da5ed3b4b002df7133aa0b84967f94f5c099c021efaf580cef28b377ace3d28e8f7c90ec20e1050e2d1d27304364d0f71ce0274fc3374163e1533a5e3d39f6140e79bcec21739f8b4c4c110d434867dc12f300e41cdea6d53a753c0e02289ad3b9d02460c68d197345a886054115e01ea29301331b439344a29cdb69ef8335581d29a15c0ae83da4eeb0d828188b10b3f78f553d509816ebfd219be0ff6fb345f3da4db6aad68ddd35dd79236027c47a0637a63026007ffc60becb8da08a8fa7daabf6332051a4607c90cea53083df6569bb18db5a20d184f5491d2c2c32e5359f659761a9b5e576044bf23d8a2c33ecb977dfab21f76fbe55b0e1b62ff254f1d2ce88cfd241b18b9295538a1bbd253898463d9e7bf6051c5665a53079f5f31ae2ec8d500a9000eb0256ca05b9fcc43d24f7aa5dbc7b7becd3961274eb7337a493f69ba8eeef4a84a129d9baba8353371b5c82a858191e0619c652041e9490cbab1944a9b6e238cb802046bd01458174485cb15e02e0b17fda4483d9f948a142db224f5b0d32959a88056723ec3fa38ccb5aaa05e4b4ff544804a9e3dc013884dd7e409294ce07620d3d39e59287d901e0b94ce90ecd14235c71f4b2c041912c610536431031ad4200cb3f4457acc125944fc912453a8b57bf6e820072abdc887545b51eef6e3830122260b340cf307c62c912e94be88e94d25d2059759a4f426d285d02c52a448890c62ba61c90f496218224ace347d91d217319145481a5e6bffb3d5f7d63074b972cef973ce600eee37df4b69ced55eab6d98f1b5610529a873381ab9a62cf2199de5121806a0e7af3afafdaa7fcf14d06f2d55162d7535a1e3afa6f08a9b96fab106229a3089af0c3b801a0c7358c225d315f158c3d0c8a46393ef8b90ea15bb5c76e312a718ffb8592e5db5016e0f5dd66a5083610e4bb864ba6158813ad631bc4920587ad165a3ff815c369982af1f93b38e592b30b439f5f3d3310cf36b1dbaa84e2244a0e707b9cb89c7ee36b6b42c9717ddb25f5c79a75c3038858289b9a853684fe349040a94c217ac8e22ac249a443090b06cf162c46b1559a2c8a7040b5850c002272e25c0f281d56fa33a9d82c54816586cbfdfef278910e30a7eccbd976e7d33c6f9deabf5bdb7569c73c618638c31c694524a2b0a075fb72fba6250d9985a7a44dff7edd72394566d746675b74435033deecc3ac05a79a1dbafb88aa48462f7b8b32d65d7760a34a702b6b4f7eda3406fe49c0bd0242d2e5055a85d85dacf7da335f66bb6daaa8fd6e43626eb0f597d6cb94195edaba1d65b4317494688e878a1a49fb4880a85e2eb39ef5ed405931bac7a5d08bb4062c5d7b59e55b9d2442cd4fa9601ac499b1b05098ae8fac992e1e2ca0b5ce45c41642bbb0d6cfaf5bf30f2c5db529f46a077764dfdf6f3d70f648874fa7885dbefb714b12f5992332cecd3c6e67366fd9acdb9dfebefb0393ad703e46b04d6c33c09ac97777921ad673d092e1ff331a490176a9eb8a9d3d3ab8710211ff7615e08fe1821f761fec4c54e4faf11f0c77c09f863c812eec39028f4133775f104e62e7ecdba4e277eea1f5fc459417713a963cbfc3f6caecdd5c926f06ad09de51fb426ff0baf2ef95b9cd5003acb9f73cef96b3b72f08a5a8a461a8874e654d55a7945d55ac54ae61051dd5ab12d3c7a03845a7aebd36de9dbf8636fbf56a1b46d6f44be165994785bbe66c5bf5c3c81d074d48b2f3e15519f0a41fcfb280e019a7e82f8973c5d5ef5a2c84f9a7e45f2f4d1697845a8b7a7cbbb3c5e8abf2def8c2dafea45eef2f4c4a32273d480d0f44beeb0a5e98dbaa022fefd62bfaa7691771fb52f109a2e7e8e1ef3c55f5714f90e196cd1ee18e1f5b0f7f17a98ccbffe923e602f2383c81ef695776928b4b49c7829d2260cffefbfcebde45991d852f5a20bafb8a4edf23aea0da616ddd273d957db23c6cc53f345924f02ec674821ffb027ff612f84575c4f77797bff847ad4734eefec511f83d33b8341bd88fa9ab32ef20c519f3abd783ff5b57b5d3ec52bae1615dd6a2fc99627f5f162bc6d9167c8bfc7a5c520c93024557f67aa97e1b50b8edfc2cf31459e3d3d6563d3514f2f8d7c99ffd3c5ff2cc913f5e3939c3e97e1a83f7ded02b1e9a6f18e5c7c7aba624b0715ecb083f64b02b1e9a6afa6cf271e130914fb127ff108613d245fb5a8a757deee23af667f478c5bcd6d8ec7ed8ddb2a7466ed0fcd12792cddfe5956265757a432f5c0d89724f1626a41d863ea7da84e281218b5d5c707fe7dd5df4f81d2b6fe17a6e1ea11225830f91a617fcbfbd8dff218933e5a5a3e08ebf7f7983d7e5743a1964417def7b459e9d89cd4e315740f77e448dae1eba8bd788c0feb23f9c25ffbf83d1d85eb43912eeaf3d1c396d72792f8f165ad380abddae4dc4b5fb325d58b96935d14dff4a51cba87e40e5bde47c196b5dda555db0e1f2f1b760556b0a5af120da1406bee4d01473b523e961e25f8f0895ab4a4f002426f5522a7dbb2534f2fad75bff4ed9366d6df1f81d2e8ed416fe817ea619e9a97522dfaa556f44ba3e81745be4650bdeb49583d0c29c4e557ef228508f970f9d50b699d3abd644bddbe8fa3138621892befe77f0cce957dd6fa09f8c37fe1f4ce5cc2af27fce3d7ae89f6d3c85ff8c38e5f3485266ec35f98c4e93ab6bc3e6c79bf6cb22beece6e0f5a736d77767ffa0874767f1b68f156b8aaf41b45d6b1e5fdfb3643291b7a21863687525aebb539579a3319d2b7328894525abf874dc98a54beab524a29ad94524a2ba594528a84989f925be3a73595f2c4d0e6d02320b07409d89e5a0f40774a6beaca61eedd9b750f8aa90629c6a4c6187ffd5bd21f525af89a3cf5d35c0a7b7c6e531b5ceb0fc618e31aadc902b0657d94129bead812638c31c6c118638c6d6c5969ce5f59bdd28c290d290dc4efb57c97b53ab6a46fafb5381893a10d168c41f0c41dd30feb83245de176fa18ef5a2b10561316cb035bc6c8ae7f66a49a7777469f030a809a4ee9a5a93568684a29bd3add2cdc8194527a75ba32dc85ba0d4ca531440dedfa7906a622901103df7b0a22083a33ba5898bc176bdbdb18df7cefbdb6c374206cc17f6f4c0c324a2fa594ae5431d60ac67afdf0c3bdf7de7dfa01e605bb5c2f94c6c0b8bc80624e3cf469abdffb62512a632406d3ad1e8684d52038e45385329996976d929f78e8d37befc53bd446ba3024f2bd79f743d67a469441e98c52df20a4b41c4f4164308bb3f3de4bffefcdbac65317a6b426d7b83a3cde10d360fa36d7d77ffaa1e61e006a2c07f4e978439ba7e30da90d0700a8a1a94163566e30c306752303183fd0bef4c463efa5a40af86903684da5f407ade951005b564a29a53b6c699329a59f29a5f45e4a3b7dcd6b7756750bd7855936a494d2ebc22c3bda3253f22ce1a1283bfd8aefbdf7de9818767d9be9d39bc12ca6fe76c6648737dca32dede69d61587bfd4d6baa290691d20f6888f1dfcac107a4ed39db4ae2ca01c538ecc9d556c92bf44aff71d8f8e90319f18f58e7acf3ae4f6cbcfbb5bbf273df5ba235a64e33b63f854867ce3a9364d7ce33fe7ae41d975671396c4ef8465d85da55a83d684dae486aeec5c3ae41646562cb526857199dbe78e2e9f1a7f0ac75b0764de42bfc7005dd4baff92bccaf1ebe6cd825903eca26dbdebb6388de00a1dfbf36f486abdf4bd23babe4ae62bfe5568d443a73be14c5fceb5f6410d8c77c8ff98a91416274e89636d427bb23187cb483b7077d4025dd7ea82f0f1be3d48471b10b5f9129d29dcdc3a2268bd70ee341ea16474d6ea6ec633c48fd91c184b99e9e60be66657c083d22890c6830633c8c0f7975c08cf1e2dbb04fcd8b7d18de9167adea33af2ee9168fae17592fced05c45a41147448f8e48e9b548f3bace41e7d0350ef4e8880fdd3e4ea2b9e8d2725078ea3988d6d8ff1b76ec614839e8e5e9a59954d9e5138ba15e0745a11e4502792021ae3bb3e1fe191ffb339c5196b187bdcad5ad4af5337dced970f927dbaaecd8c3304886e42533d2b75ac814198b910ffb18a903837d4ce7529012c1f8e63a1d884e87f9b7617f6cc6c3c820423e663ef6f4ce2e8c0c0223492125c47e86dc41bef05cc81829e47ab2655ef6327ee6f9ff9f54f7a4097796053549f23ff624bc901924ece99d3d09b1279f2485c06030d8c3c82142c8873d272bf0339eff497f33487a3959717796c5937f565c87218162fcf320ff3366f0e742e0c7e0e08fb561dfc5c119ae793238bdb39f0c4ecd194e4d1917e8634b1b721fb28f91b618394448257ad8cbc81a1465631e89a99cf8c4483052e87e50d555dd9d759c2cfe6562706a8a9703d98420701fbf1004eeebaf2711f05f1202af3bc65ddac5b8f0225d1aa662ad74fba88b84af74fb20c63f181bc11823612a78c74a31126f8c7f7c4c7cb3d812fb74fb9c0711f2c19f2483089d40b18fbdec39596de41021b517a046a814cdd5273d29119140000000288315000020100a06c482c16824cd0261ef1d14800b7298406e4a369648433916042908828c31c818020c3004194368686aa84100c4d8acf42f9546577efab151c12dba7a9595314b88a3456add2127ddd3fedc814158a428819b68e59b39e4d8d283d9fa6dec0a6f5795e6b08bfa55fc3e524b1ba48e21aa5a6b0c501f13a3ec451054096ff22201ac994f17dcf854a5a0ff1543978d88f6315f8ce51edf1c4d045b251e351c36c0269d61c4e036e81f9bce6e16d961a373c91027ab34c6af1d6172631645d2f4632d72518298958cdbb345061a5060eb506bc8014dcbcfa17a1b95a1cf7774ed259a90ad4a62251daf4db6048993d29a92b46781300782a8e74cf3340ad33ab255d9350222174d20563cb74628c524abc9ea8051b4deaf67b1d3e2a2e4f10b1b8cf3d5b8bdf225243b2eb6e2989d218aa50b5ed2c40676198001d0229c2fe3ab99fac6238381a13863ae5a63115171bc85d6d93f6ae12c5dc360151fbcb318417d8e4b193ce4043097ca12c066bbe6156ea0404e580ee0b1ecb463b8c9e092a098612aec14b57b294aa9d3f63275ced834c8373e498e390f9052e8f99af19f98c533b93600d244d0ecfe9804bca3e08ad4ae0110b151c9cb06a8a3f9bcade3d4ed3e1c192e404a0745d785f3d2af90e6e860b88e08d485651c53229299e66ca8464c0029ed94e97d3831f95c481e6ad477f76a389c1563a5702aa6a6a79b32108ed127e3605a7921bdffe179afae7d93566656ddf04391f35f2e766e411258548dd90e98d38af942d1ec5902057bf4649aa9a6f7a8f74454a49791bd75adec28994477ae1079766a208f8362dcc41b972ab76b6745b42aa152872c00e3b923c630c4b51207d44a2933bf754c4a6c8c394a7b2d15ed54fe434e2c81dce39881baf28de5f01fd89bf394c12823d99baf8d770cf903b7c13fb061b7d61f52d2428913ddcde1b55b56a0f0d960c002ef0f34f924931f14f041c4147d454cfa4d73f465e9410cf928c3a0d0009ff3d326d7841792e17ee91ce5c5b51fb079888636f33f6e0e89a19e812e2f7045f00d7072c5c4e3edad87e94b65a61b2366718547c94f4a9e9175fea9287e6ad1ff31e7b197eb0419636c3e32798fec457298538a40e01f1b3fa245c5b2014cab0ffc86a8589722aa308f543d899bc80411ce57cfabf8b97b4013da89cbd2cfaa47208c7d1eea334542be9085f24fce97a70d8d400f4f72928fb97a15102cb5448444f9b8597a96b37feb76389b4c405422c879b7310c97de13b125f1c63db13c55b1f6d7c7c64013ad31687cf14be3787aab896a97293fc24d23c65e63382b721da13329d0a463ee483e4a5580524ff2a1f4b169b1f53f967479cc2f4583de8f8d5c420b2d9d481bade0066522dd11cdd907df6d33301a9b5ea5da521e2f5737ee64275baceef3ff997ddbfc021fd9a7f7e14528e029dbf1ea8a34bd6d3c1b590162255cf5ca312f13a4c4fbf7f1694bf9ef8c13c4228835e5064ac358197a0f492cfc03d02693391cc9bedc64c8ae5e8a5cf7bd2bc5ff21dcca1f0f1c2e3816c4aa479420569987d81095c32be595299a31bb47b2214eeb4de71ee569198e6324ee1d0a61263290a11ec09d53e95a01ba88e19b77c87ca4af9c7ab79b34129754a73f21d95bf27b49f9092f68e4006652a51bd17a70eb2e782616a4c7b68b21ca51b733cc83aba8c008e2e3e76a0a5930d3b06737f198f7e35c649cd3a808f6b5c46c221a972ff8bae73dff587e2e4c7bffd294fce7c7c7a1d393dbfac1208a2da43bac3b693929209adacb052130ed0571031a2daa7c49320c15cb2369a109b188b002bfbc685b28f23252f0c7c8e78772c64ae4f431a1744f260fe77143566a3b28b19d58229b88207e1cd6807c4b532e3afddc871604f4634d1aa8a34c56987facef8f0542b009e809d1cc3b1183ab7901901a2d243ad1a2e99ae3510be3e1f9e18ee350a71efd5d0188a27981935568dab49d8052bd9897f1250b3c00a06e5297e6110a8fa4ee4f5125cd984f89e88cbfc56596d4356f0dde74b53b47821867ffb2eb40f5c08ed3ea13a93c67c38a353593ad266b62fa30459e2af4bee5110a9c52513688429ba0311130d54146b896755768765f298d12527243b56e7b93f2892a14c3b7acefa08dcae079337bb942716afa804418bcecbd7da80e7df72827c21f80f5e12d1e7889ab58dce64a619b2b2fcc43316c28080766a9077a1c06679dba388d0f40bf6178a51383857884c345be6b76cae76d96695cec6271c4ba2eefb70cbb087b630776e1e6377bb7fce16eb74a725ce4dce4d345fcf6819a4d95877ba831e551e22655f06893c9cf3e6c70c4dd8503b752bf4b0d382c48957e012cdc4d4842b1b60e87f849bd343b81376506d09a428792b505e16a143f735dc1c41525d65d815517507021d177dab3d071dd606983ff5279a6c535d097bd5c9a04f3387341e1acface2eb8e14ec5e09484ccfc619114ef13aa34782a869868f26910a5409a8e4599c8d51eb95a9829c749cb0acbeffec372d96efb3fe6e9bcc4a3d1fe934c5bb1cc9ba29412b4c7d74212aadc7d22293ebd9d9a29bf436b29f4a0f909e827ab27dea4c2e96ac84c693d124c7ad2a06e7f1574146e03b25ebbcd16977414a0d6c9f776bffe9ef55388332c68ec52501cd8cbe43168681adab60271d8ff5dbb2143a9e9c3151310922cbadec2763a2d4ba728e036443d3e09d34ec25559c8222d62ad4c7953bcea2714541db0282dbe7a517f5b4448c819281cb349fa247f626d06325b1ba4968c6ef7e25cf728a7f0e8ccf3d7ce85a0b3905ca652b2d47d7bcb88a4708dc9e999861bb9105f2a21b7db7ae45be48a5d6a5ce2d529002f86ad4acf60b005a1edf19bdf3810344898bb0d2f778d0d28e1ad83c2a567482f8d34f449c3b1f7f77f9c7cece69d9d2835feb328c76b653e2dbf3591b7a345fe20d08939c44e35814c315c126589f4d9af5799224508f398b948471a28964759b21711304383250caa0599f6eded1306c9f796bbde4d2762113acfb509f6309d9a993066937aa40d454d3b50aa554fcf7d5f06eea7f3f38f3604e57a5af50391d70efc77d7820ed9a2daa32f784f5f1a042aef8ca3eec91978c71564527c0c476c7800eb37015e54c86e250c6067010ab900a807b4e53082120444c921a303f88fff58d81799d06806f70b8d701ad48f540a1a15b355114e445a9016eaa5f5f44aa4e72350976bacf73d9bad4dff0bb7eaee1d10f6048900dc170cafd3a6aa86113388dce252692de91e2ceb87f0bbcd89745faf8a841bafbc4bcf4d7e4fe6a9584b6e38c61eff3cdc7960bab9c76bf7f80c99ee7a01c4ed4a7a281b94ea4cd15de36bb5537c0bd95939e8312723ae140345afffb9a0010e3947724eed2b73de3d7387bbb9aa6c0242757b05c86a28907db40f6f248896caa189cdc9f8fca08b4838f62eccd42985b6f756d6a07036588ea2ac5f74351b62217d271a56b13223aa9dee9dd0aab65c13671581bfed8be412c01967ef430614f48d2bce5ab556e1fea9b791d4a00f181a883340c8d69c345bbfe2e5df739361d3ed19a5644b41192a26edc2bf414e39e3efef9f2f667cef7041c23b65c05be1036c65d540ed750afa63ef0e743edf1d8e5432f4cb3678dcf5e2ddf02031d740b11bb8d1ef840280c281966f73a07384f6bb040a214ce517063502f450cda6d23577a0579b36857521c6929c07adda577ee5b419cfbba5bd916b6fdcd9ab8cdc5bc20606570cd621ee0ddd5b6149e8e059b5d0122ef65af24b960059e6e33ef1e5a449748769f37520d35182526e7b27773839ee796db9ef18516a2f2f73fb83bced84a4eeadb8dec82644229c70ffcc36c8430f7aa03c0af5e7d15ebcc5f117f72c2475f5c7633f0a35e1c62d0001443a71d988569b736deb9586a651bdf5524c0a723ce82b390dd117d569605891e97a69fbb7adb411c39ef0587bdb4c7141f428a8c5b0903610fae8426d5519df1805d676882ecd6dc7d8f70108b52025d141085962cd2e3064535dbdb92021d0c806702e94d473bbb9f25f9a14ba68a8bfe0b29c58ed572905072274d4b4ac0b3cd6073ea655c23920d87aab321697cbc238996c14830262318050bf7cebbfe369ddc4ac5febe9c5b30103df4dda35e182469ced96ea05a8599f4bef21c82eefbc21e9038f9b31c45aff77b8a1c6df1d0a7c990e2b8f99fe4c626c8e87b98163a5df172ae906d6fa07547a07244fc837b0f5be861050cc0421bb7e24c192218621e8464d622a02aca5d3ca307524f938210c0e1d990408b100f4e7207c076db46f00c3f05c555f857d1c80be2d100d9152a7898cb0c60d171250312ce10c91fc6af1dea34b4f474d71157169470bcc13034d1e1905244acea926ae5c96df2cc97bb3712be994597480ba8715507afa9a595b7d4547f274bbae436ca13336cd99709fea3bfcb148e8e935cf7db6e2c9a2d4728c7c69b14fe8355ba57ba5a5b9122a59d7d6c248ddae10fab1a580ac75c94c23ec41cb17a2711d1ac328de7cebe6322664dc5b3e20355a4026aff461c0c93ca162bb42c16f85ca8d753fae9fb3f610e686f3b2ef9369a421bbd586ecdc1c6584a11b5ba026ef3d56a0b5442ba3e438b924e78c7b6c16fbad3143c4a036922d139ce5f7ea899f352049d7aaf61c290ddc6e922dbd6eeff8df24337a6c5cc25ea0f86d9615512e80aa2e9eeb0401bf5a6d5acdfb828b1ae6039315a829a3f255114ffa9f66445086b67cefca5171a90be4d5a36cbfac7d16a7cc77483064ab7a71e1b6c28505514561164e583c9ebb61e4fa9c34126269871f39216bf2a600296c57b4314a3eda5c5f73ce37c25a7aa78cad16ee1841b24a88bf7611358d41f72943e14ce4b27126cf0cd4fefdda530546b13da96066dfefe6b76d83555f586100abb7509a724813eb854283247f6e8be3b24636f201f22d54bf31697212f2ddefc36f598da8734840195158f455c7fdddc7b0a61d87373a61f9d919eb6ce4592a7fcc1e5221b5b0240eba510fb601a8c4447300564000a3589ae18118a4f4c8084e480df4b7bf036ef43c30ba88d3ca2806b332f480144fba241516d4c8bfbdd243d1c22e01f567815754041bd587037c5b3a307574836b751de4f43b030184bf340ba3a1099763b1a8e35721f5ba9756c12b1f4df66a1b275ee934b6729567ad008bd93123f05d9cc55c69779cc934e1e313831ba78b4ce01c4dbb8fd82f710900a1c98b66b1d8fa85c9d10a87f7d892ba683d2e6c94672c1eeed84d20143c2cd9fe434311c6774eb49869c75b93024989fbfb10148c5afb8cf24cdeca25e21ec33118e347608a38158a104e3261a32e4b70f724222f981dd38fd9f231f99537618d08d4fccaf8d0ad03f4b01fa9aa31c09446aced4b5d1be9ebae2171998d83accb3700ba8c3698c19966698087917abe5628a33e3357b5e07b9ea97f58980261b1efa36f62df257778bdde035fdbe8eee37f0fd4aa0e3c9d81037de8e56b49ddaee1427868a8bb5c849ed0bcf394839f0423508c60c5e0b0d0b2362be3bd06b65bfd3900d99bef5bdc6496d78d2ff072098ec66a664629420172669558ef4b075a680ba805a08e52ede9dda4d50d260e870eb1ac3433a26af158bc511f0a648f0e8e571180213d87266a5b70d820cec0267c937a7a3b3af515a575c4a1533b2985894a5e091e4b68024735dc820629cb40b4dba834bac47990de18b7a03d5a5d548e9abab3eadbfb0908802c0ce366b5b4a7b2a9a770192d29f64ba5f5a9cad9d8104b3353afa2144d17a2cc00ac6d657c866a675c3ec0e34d12a67fd9e360ce9ac91d75a73a90b79775271150c9717489664b1e5306943b311f2038bde865a7934c229f0d301b382d11e143f71141cb98b9ac71938a1a5bbd6c98e073d1f613674a41206bf49017a76183d980364140acd3e81d770b971782762707a0039c13ef8f2a5d8ac8e1c187db596c7981eab0a60c23ca4fcee9212c984ec09324d046eb9f5719749f48910887fc2a1c502afc38ad23a924d206f3f93067704d58349b67dca85ea3a0645b4b5c8644ddce37717fabb1b84af9b02e852b12326e92f4a668118248d874f04b373c648c577dd9c8176fb19c7ed7d32e26bc698d6ae04b488831ca01eeaddb880daa75d831d2f0925781bece3692a6cfa01d2ce3656364a131a3f17578a4c06ab5092e489162cc7c9d0c2b0e7affc917a5823a77b9250818367c21944a484269e90024143950dacb993dee27a12ad43c4ddc06824bbe56e833755743c7c0ce7a92139a1c16b02f38ca8c808f00bb10f8bc687ca465e3c6978176353d9c1dea2ff74b5e938a42c57055c8d8530c5fa25c865b505a215e7033c6267b86fc9d025254f94d88cecee6f8f242d15eb43a89f3325bf3ba5d20ee21b7bc1c14940bc6c5e82db2e526b98156ad5e05a6555d73b209fd80840add2a140a706927957818c23d2871d6e7ddb8067a8d2fdd8d01cd051f797a11343575a6e5932ac61a31a27698d94a46b45c435561f419e73f9d26786c880044493bb8f222b17fb2ad6780a910d6a1129ae5beb150491c55b6adab285d042155e1375263478ff0cec03812cf74833f873b9729a093bef595fff81a785166aa92656abee23ce186aba8452d1375651632d543d2e6e054a757a91d20ccd569e82c054a995ec1f9d309ece72e59a4187ca6ac3dcb6c47d989f28c659842215bc30018f261e6ed14716d18b94b74aebe507feed8f14c9a7994e9af02a2eca36420a5eecdfa4848bdbe36c06709182c9ff146ff326f645d33d196a5fa0fbfe2a140ad8e9c620337b66db9cb2e9b1abc1a59f5ec68b46e666a4a5745b831daccfafa7d9c4e5c0f7277e545b412c0e6915012c9ccada4da157cbc4e8826884f18a2b2eb3ff86652d32ab34ac399c11e365903d1dc734772fb6b18dc91d670b70c49fb3651f3e7eaab36280d4f086d9bb3ff2b717df002f2408d56c4d21291515ad3e314b255d536121c35a928af171273cbd5ff0a12d27b78b539a2f60c69321e19c2bb6d8bf7089be4bb39b758bfbf4702951337a2af1d45c929771996b1b9dcc621541fffbdf90033bfc86eceed4b46cd9a78d31a9c088303172008937aa8471021827081053f8656fff33b4a377c863a721b57c303b491e666288705532f7cbaed8dd644ba487bf45447f9a535f150ff78d5ccb0a45bbc2386ce70391d91cfb7f3e97288adf0ac404caf1ae627d880a9c624774326486457ab897b9ad6927c2c2961408ae9a30100687442f2d4cfaf09fe430599e556cb44aa3c0c771f5e968cba1ac4d553079941ebc11502d843bc06ac43e063718884dd8a9ce6f9d40ee087a822851227e0b857693497c433ae9d409b18a93e96808b8d6a8191e5293d1952cc37e0a9ecc40a093618a277c0a378b527f2762bb352a71504191bdea4f44bfa3b897d4956610c8bb59c83284400e851223ce60b68569b8ad1e83718f6d2cde0465282fcf346324ebe80796320f14b101c5cba79d8d73fb94f95d36b3da6a4a3cfa85e917422bb6c3173cf0ac9543f104ddc67d3a0326f1a90076de8102e4e658af89c550c4b8add691ecb04a732c0412908dec7cbfd848a101d1d229f0191831fa6d3edb330b4540e8ec60825ed43dabfd9c3bc2c9babb057b664e3a00ae306cc50aa4075995833331bfcc0d9160756345e1c62460dc59ec97c6544b4170c900e868c76186ba870048b360463a922f59e49cc3a7fd190732d365c8762076fc223c2892063720c14e0c6cdbec265ed05e08644a5e85a43c509a368aae733afb8aae68752b91985a291dd248557f1464146390215dab77ad21ee8089d117a6ba5e9e81922d9d2f81c65db12297373f43ebd7559b30e2d734363afc1442e301a87c838fa1d084ceeb471ba357c2b2e38346c3e9420ea18f68ff6470f66cb9c8345a02ac58ba34fc0aa09d548de014e4633bf77a9d9fcd81b1e2f0965a6882782aa35a2c5cc30bf85e3b6b39856b753071479ab86c5daaa60e6ea5aaa775ea35824deb9b6cd76c027b2ed89df9545b219cd79531577f77fe98007b2a2161419d805b431d7aa69e97807c495a9ffdf486e00082f1a99246c87255ee3149f26c4e2c81f1090646e328ee7035e211f34715814649a7405214ba5d92278aeda159cca751e228bea3d2c7759fffe19868c8ebdfb2a9ce91bf32ac19d1040cab1d8dbbfbad12deab7d87015919a31399f29516642b2de76a85450fc1d91242b0f6564bd691ca57988aa787ea7b8c78b7bebc561b40f91a129179979516315d9f8d6bcc1b4d5c709a9d37893ced037978e7bd39a663056ebdf941d78b63f040f57c3deaa2077bc15706abc967f9412af350607db02ea7dfc2262d5cb7f6a002dfff4b3a79180c81006238d5b391414e11621096884dd291e24d597f82b44f095a699dcf1825348278da04529516df60f9dbe1c1903d9b2f18cf764c57646419825302c17978fec68ab50c422302b870a7a411804b2bfb40109095ef0e78b6fd6a866004e3322f6d9a8807991284040ef1ed3104732bdd86ee34d45cabce1baca9622887c30d13f3e5894d9101e13cfca10bc2540997f1567c518e004987e64152dbcd3961ffd06690d22715b11358053dce38e32e622b5b913dcf3f6534388a19d18747f8ef006ec9aca2b754c59bbfec5042abdb607b6666f83793372df67d53ea2fb57898e41296c2eb4d4dc9de0a72bb1c06552e886a4fa5fba480bbe17cae6e9707ed52b6151ccbbc310bc5e8e80925121e32c4e13755b6260ba44c227b8a88cd8a207e3649f210533ba17357db55fbaa8c3b88e99c6c0098b77b6a52f0fe9558971969a50279e44bec34c7ad28ce6b81682243a62316796a10842a3ec054853b72ee04b1e9b0effcf960563e7539d0e29fb7ac5392abf51c4eaac56c46d5bdb70bf7151a93769dcb812434c686f4a5d4b6a64081ab5a6aa61094a08716f3dd6d4001766567415828b41b6afc46cb94c231136b263e03751ea43937881e56aa28d143cf8e0dc65a0dfc915843d0fa42b2eb6daabae8363bd70d7747deb008bcd56de107c105675dbf28ddb530499442c84ceac40826818bce3882463c39979b5bf01e65d31b0439b0d31be3d5b9d112b54ce30f880ad5fd288598e460e7b8fc62df876af8de7f06eac9defcaecbe681dd52369313267eb6ee1a987bbd2aaba9bbf9f399bb353d49deaf292c4df297616aa342ad78a26cee176b296ba4c5bab4c92edeac26cbf745b9586004c248f55e9999c0965146ae7fcae8ee3fc0163321d46212bc2b1bfd2c6f32731c86a4b3f5e34f762e4c0a37f00622e9b81af524be4dc672a0eaa0792386e50ab66e5a1aed4ef211c17bb0ebdd3988ea8ec8b05031df5d4c65d21206a0507b42165a0e147658ae17f5a7722525c020c621a6fb877de5579379e656b452284764530c446e032f35b905fb9f6fcd1856e2de2630ab8d968a1ec28be7492a2dae28cb724de4d0dac37228543deb570c78f4ac1aaec143ecee27ef85c74b394c97ac605ef8b24344853d4caa133b8e79c1214b3df5fa0361b654e74cfee4691ef4e74117c01448fed076ffaa1d5e45e269dc90853bb24724dce02b080b263187212126210c616844cd6ec5d794b435f43237c6049caaf915f5e6e8fe23c767aca733c2d78c79ed123601435c0b8c5f68fc78f85d24e78f483f111bcdca874523a096a03e88c1042389ce6d5b02351bd73781776393a708017533355b9447b5072e8154df16ad1266e2fa83a046c49f62af85c41a9fdc90b89e5bc89b71fdacd654ae46cf9cae8d58000b0edbbc723f7a8222bbce33478e3ef0da2e414e7baec45564df80921e2a85a9e045422df10aaa0c31d1950040bf6d85f15562559a1dee440e01e1f69c42142c966a44863ea0836a554a3daa541ee357e365ab0c29372cdc3041b0299edf003ba6ffb8118dd608ff157d36d7bb4634267cd7af6a74a677eded4cbaf26e8fc1ff80c54b20d39dd4c1faca6454c83e49d438a872062972892cf02f20b5f4512bd67a1d44b1a8895bc60442a0e61312a722c5790e4f89b4216390055091d1d116d7963869162c1d852c14338e3e19adf855107180eac2dbf24360f4747431c8116c0623f17f8790871ec71aa911e5e7f8352c8e193a76b34b686e7f3d5ea7aa6582040947b6fb84d7959a1fedc4590a60f0500d61581a4132e87e0837147d0d9187e39da23057730d9a41a1650116d7310e64b73d0ee878a210aafffed2bb9585b669a56cc5a44ece08caa4443f826e822d2c7dea38aef89ca8ee2b00b50b76f7917936e0b09fc908eab406a4fd69e4e93005e98de52f266f3d3a5329931aa3abb87035c4293e3dc4752b4c0b100c95f4654837a5d7debc2f2e42e96c85a3da9c77c8f6cc6f1b68a7afddd550b7c6cd80da8cabefe04dbc270d7eb6f6aa0a8d768e79fb1d73c04e33fb2d016881dbdc2fe60c1738e36cf28556e6741dd30de5fd474ea5753c80ace298467691e9de6f43dc4186d1cc3827fd90248aaa79804fa5c444c2869cc03ea7d4fa75fe5f774027ddbe0f0f8bbab5e29d24bf734c66ea6981609ec86fd467359176a021c0c65800152d3a092c77eb9a83c8ac2fc30ec3fc20ded9a1a9e6d2479a5f6efb1243bf7af510d702c0322cf7a59080fb85f4a7c015530963aa099275464a8728c295ad25252f9db9366334b4217114d67341ec9857b3cb4a73397ad4cc6ea3182c617b00071d439259bfd5d1788035aa645b8d13ae0d4be81dd42941b70a4c91d89660249342b4934e1749324214e38857d509ec2a232949cd853a87418094c6fe858d670e2b6230387f3c1feb8b51dcf06a47ec23256e8e6fda49c77096df3bfc85a8bfb0b6789ca65b85c04a7364eeb2d0fbb56bf02605e0c7562414890188bbfc3232e64253359b204a36d142d15af7a4a6cc3bb895620285a820afd11e0ac471012aba16ea670903e55510bd37b4c2909803909120f5b35bc53dbac584b0c7bec6ddcd21727f8f04a5583fc392f23c4a99d9ced8a746d5e0a6d5b98f91b74748eb1d59d0c3b978ffa7ac9a69e5a2677c4325c22fff18dd170ad5d84e6a70395d29d889c44c98d5a218cc303ac4b024f1ed659ddfb81b1d8a3d283a5a878bb45d82a5a5bd885345d961644147db21bbddd00d789d36aa6fafbf20609dbec76dbca458cd246a616bd4b12063f5422bcd10a0bf3bb4a4fbc0e0f6dc47846740a2c627b52afab408f2d5beeff3822ff793caf5110234d5561c47667e52936dc2bcec3400d33fb7cd20b3f6cb9ac37d17ff5cc8de5c94d17acbef488fa3752c24dcd778c05c9cc0310ad7c9157b741cc7e56038c5096c9e117e26b40a33b6849f8feb5a4daba80813c1507acacae7e264893fdd00bf6982f961adc38f700b47ad6564b8fe8ea8b633322dfffcbf6efd1f0608d8d588f079a9439a284a44aec1b36ff9346c95b7cab9a52da9a329fa19ad04cbb7b40387d9915317ba0d4613ac664d82f3917e369da0852505d70bab36c24771908fe6f0bdf9753fff5ba75852499558cc7448b0fa5073396553bca5a815654fb9086462676a40d886f642e3b2802f760e6e2c824d71941b6efa8c9c4df4ae8268c8996d04a84c8c8b4a5ab9ced081647f4e932c4bb80391ec0724b9ff74c5001213de7aa064bdca90700c5b4f1c149044f76aa95da84b36ff83a2988515e3409ebdb511c1e3380a72b23abb92b62176525ebf2b8878a63a18f332a4b7614cb87be4f188c2c3c6d3cf1ef793c7ed61508f759750a61c6c17e020f6f64705cadb71a7d00f1b6ccbaa5f6c7e8e3109d5ebbd04c0538734632d3ecace32a949d293b8fe36692428f3e837029292ec67c9917a6e0e6cbbcc9c9aa55361295797bdcc51c3b37d5bda704a51b4d300085d8ff3942777ca9c88d5776293eef4911acf5db72e084cdbcfdfc527026f37e635b2f9f7f222c8c8b24df4100c9f06ee103a2b67784f3dcc3abfc9d89e6abc7843c735002b668537907a298115c0f24bee9e056b89a1c295a823d8cd478cb625c78ffcbdc30a12889c8ac64b3712005b777260695d49536aef739576cca2a15ee577ad245c1d2ca1ac79e245bc37c17f537d4ff8162100915e53a3dd6cee8d92e016e362960dfc03fc7cd1366a3b53e51b10b568af5be3a4c4ed5096a1d56ada53d2b432aa93d0862faa4bfb61db3e14718cbffc21737530bb0c9fa3b3e699915ac92c42f0a3c1bcab0751b843bd2b4d5b8a93c24882860dfbcb05182d2d41b5191032096ac10b9b06fea905ef74f6a5238433a09e11749c34fdbe7055b47acf25d4bc0632f7ef6bdb454201ba17e900d8164d6bb41780eddf6e62c8459a3bd4184b93c6aa163996194398807cb9901a7666d483c8305d0e81056f1c0633a0a9c670373e33a800b4705bc0bf0d67f0e26bb29b188c020ce1e6555b58bc1e5e7ac9e4f9211607009df6f9c5d5f1e437c709c54e49321527c349edeaa6dcb782adf8e5dd92f9d6636bacef11e3c9cafa431e2bb6d694fd78e3e93dd7a1e0851b8539514510384bbee2da79986d2a1e22e28203120f65c11aee85fa8d1873bb19b593fcd0a027b7a457dde59092347550e44e2330b4b7ae5bd7c2d5b538ed7786df9dc0b32a982b55d2dd3c3a384af83b51025914c8c4233964ed4bbb8dd06333e5cb7530824e9571999223d6b13b8bd0811b5022d61c5cace6cf953a2cbc43115d61868b0c67d55698415796a016376e6347c596c7ef8e9bcca89b766c0229bedc5e1fd6821cdd78784902f3adbfbf82589bcda7bf2b5f7ace5685b1944c41a4d494a09a64fe469db2850175cc24aa01f326ccb8fe1ff19dac27df20838c95b1940c5baa309f0cc498b5f8580fb0273249e4af015897d38d591698e41888d3d6b2209060fb1979b2de3e05a9790f274e3c1f778fb7bf06a73875e86c9f1900d97795ef2b39a10ef1ea3c71aeb006885973b6d06acb83785494184bd04255707d7bf80b2bafe33e59d2139324f15cacec9994457b0d19da85f620c122da72fc4b692a7bb9a791f3a4522d4d8539be865d38985c5853d0bf8206b782b5401b72835ec13358ae5a7dc7c8ef76c08400662ac4f48bcdcc0fd3c50fb866b58754cd31d5c609cf49f0e9deee2887413f5882230ec11aced0e3b90a5ee99438f78222a53c7b5e548acd3183f25d6f16ce2470e13d3a78f8532645cfa5b09f4ff196d36e9fa0cadac0d4ba8e8447a023784cbbee9a09c395a70b66ac57a0938765509938e76c6f809ba607feb96592c1a686efb4837597f355c6d5af55997246a06976e013e6207b8ebf5dd8b49ccb1cbd6eee7df6606c524876a200538a0fe7e8a1c011e1f545f2f96ecb8e214fe57efdd46ee70c0ec1797d4428aa17fd0ec6a338a7e45115f49d9f4cb82a186e721bf0caa4cfadc53782400b6ef89062989aa9b1fbeba070af7e4f336b4c28ea209840d86d9ecf2e48f814d5f0262256abeacc626882c6111c6ac2d2736a224908a976c010b6ec11b895be086e216b8211dd73c42cc1510bbf863db08b2c6a7c7dea3efe9bb6bf78b42c9c25c421f4b2b2d530903a903f6badaee7cc7c8625e0b15332590067c7fb219b862af4f715e92b01f5157b096e2666c68078ef94090fdefe2e858cb7bd22674a8a408f1d63044513852057f17c66fe722fafc2c2055e5854b49b5b8120b1b15f38e175a6ec80063ad5a955df6560e8e92603d085bf4e795a57304ae993a367e1b6507d675d42b64233d6e1a7298ece807f1668bf32e6cd64e5ff396fd30232422ee16b36582119d6650d38db611042afbf8103f1f02e9ae04b503369ce16c578aa21513734359952005d5a483ef5353b6c0400d66443de006ea0eb3083212b3c29e49256a247dabc8b0d2189fbcdb8813ebec13ccfcbed01056541746537a55d42c5531342be268f549f760900f67338615d6f54a9e2e4debf32fa5e4bda44c6f2ef5feccf99d88abb2473fe8363732837f420b560f798f4279966fb4d7117f37f50e778a1185b6ca3dcc83e93cbcbfef400c6660005a1e7d370fa522256aaa88c9a23b3a6ffa95c674431c87027f97799d91b4f51d485ce18e343c1e7fb85a7ded7d129f5792b4489774ef8c59b8135cd1dc28c4a49687956bf8b5e628846e15a0d7f95c735c6c304cec2a5810c812f6cb42bf31413e4c7c030077f25da5614908969584e9737f4ea25d2a3db0fecb14531c76c06dab8f21b4c14d14ecaa3586432f1bf1ff167436c635883481a2ddfa1561640d37a3754808ae7fb3acec8508a05f4eeb7b79ceb6d1da4118913403abc1951b415ed8b66b6b2674649adf8441bc0aa4ecfab21f42480d0b26fa064187cad6758aba1172461dc7d374bf0f854d3fb6996a8a8166404304f47be74700cc9d116a330c5eec2295e9841121a26f8584a52912880674103122b6b833a453dcaecfb7696966069c2415a22d68ff4308393fe041b6f2ec31a50bb70c5b886234f810e19c803e70b68099046106b6a96332264027f832cbc23dca1afb104dd722cf07ffdb0f77535473aff06bbc35d7d84710511c16cdf5d118e4751186353cefa84328168d43fc42207c8eb2c63cda73e17ac2ba426b9ba854fc52293d6e3f76bdd5186b50824fa5dd4d8b081a66608f82277e7d4fecb0a2bed0f67b50b3d8ac99c6d45260660cf6dd20a2ef64c127f8f7434a30ad4fd3280a2b2f811676d28ed71bf0d6ce901cc3b1d18caf39b5ce74fa42266583df5e40af01d218b4f13db45cb993196ce8ace465c44258f41d2e9e7d6bfd5b03bf8412e7d32a06b9bfdf7bfa568b9ce6ed8fd0eff44fab30da2b457d7faf186979f4d5bcc33d32d5b5c0ae1ca61b787cb62d64223e981037c852f3c9c369e3421e81e00ac787fb3bd61aafbc660b4734c56dfccb02787779ee6d885abc639a5c206db8a3c11ae5764ba5a16d7476a91d5154898b9e88e8fd193a111493e0f7bee3518dd4e29b6ea48e6513570c1bba0ae92da1179bf8d7e5c4e90345701f315eb9a5f8c5cd21cf63ece55849b74c2b7857925d374ddf931e07abd2b45ed335e8da30b4213d2ef33074b3d3a663e0234cd7a2403e03a129fd79dec686595b8046ecfccdb6d8f09d83a5e7ae29bdd1c880a32019744a4137608e97c61aa2440a322b10edf02b49ee84812dddb9a4db20ea386b466d51fae5ef489a6a5eb85b052ae746d2d8b2f9191213d5646c59e153a3ca7a9112eeaa85b8907a1c560b957d66e1ac5a982ba9c765bd10b27d29386b16f65a6ac1b5beb0b2be18ae9585b8907a1cd60b23db7929a0ecfc17f843ee08d20e2a833223f108042ade7601eacbf9b22f7959165f3cc41051bd0e4b9beaa0d9f0362ebac29e9d35d3f574155cd08a490f85eac1bcd40a39a1d66c5eeafe088a6d11a553c4a2b2362f8020f4a08c791b65764f74f86d03e3db4d3adc9ed35fe4e146f9b1b4d4885f2a1d8733a8a3fa520931c66e0b9714628914a8365bf710a831b42d5817c4030b909ec31e6a8d6fc1cdce6d6f0fa441ddf9b12a95cc1d1635cc637b12e4f598205a9f7c17380203009622d2aaabf6c2e355182b6798b35033a78ba4dc313895e71da8684353eebb36b8fbb39d438f0d0544f4977a74bafcd6fad2cb5e119f62163cfa0b0ff68a1d2c009c87560fc9fc89d4dca8f6a8e032cbeb0e54dd06254a00eb0967ec1eba8cc5210095485e37d233f7af504d9124b09b5102e71ac42832a4def21e1e3c46fb57e986b222842c293aa085adad5048ba071c33c5edd6abb3ee4a0f69d18cf0d94eaa84979b326ee699c97f09f0a0b51a27e4a982d36dc6336149be246c7490e4fb8fff1339c34cb1efc42995b3e03f189c2c595017899358f45073cb6cfa02a65d25a7d7b470ca78afc11f564046d647f3ad940ad5cdbf21195091576cb33f2a57396d72b990aff4df2423ad688c1f12dc7c62a3940c985a81230b337d06294b1526c60f59892f8dab86cd0b85451e0936f548705d291eef230310071e70bd391444f037b0021be8491bb577da60d128f7205ce2f9e2928dc7a92c8c45518c59a80d68be9bffab20afb88908ee57a1de08d3d5bd6037d77d7431fd32a8e8747fac6526f30a13ea745158aa1e954805b24efed1a9270e3e73948a44f4643449387cd94429ba17a5ac3b83b39884d5d375ec74ff9a1a32274819b5c4b50e23c7ccfe7361e068c2f74e418962d5284a6e40e7f39430ffd41309604f64cffe9d10024a60af05b1ed59c23d2b09afcd234fc7802fc1f57b3aeb6ace0b9af421aa87438efd45a2f86357b75ca79de0ea1552fa8642e9e9634b937a7523692ab60c54a4588d2f811e496c5f86decf442961e9d71c3032dff9e6ee8e9a36dc6f0626251dc0849e3535aa09486f0d5ced83768b9d5bd812749975c4afe70874e376e42fb03acfd7cda47ed0a0aca1ff53c9c4886e6bc2137dcc714057745193b583cb194258e08cf745f48ed7e5e31e00442f03afc22604641de23e7e3f2bd3808f04feeed115da40854d6371a87444088efdc5ea853db9f582bda0034afff27b50718dfbe5f480d02ee85ea097673a54f01ee9881bab7a8d7483124c029fb0a5ad86ded146431fcb3e2d9ca4f6b00ec3f5fc7911ff6a5ad9d8211449c54985d723f9194b01f52c1a9f96b7030e436f3f0873af3bf84bef554dd63fa9e007b37b5f0eea43ad53aed56fa39fa44f0e4f7f099b0d7684ae3d0863ff33d6b78eced436707f0e38666ce8ca1559a6facd49110c385ffd70fa9600ba013a3a5ad9546a170bfacd087406112e33f00b218e7d456da6c9381948eb7ea14cdc2f3c555d8ff4a95969e6b53085b3e3aaa18a0a259e4155c0fdeb1814b17f0312964668bee5b52bf3049b8a073a8209d2414f5bd869500837ecb572fa98c9e731243b84269d491696ecb4ee8195e5a84453f6cccd494000b79c364e706962baecfe18c550c1297fbd0e8485679bb2a889c5fda3b39bcef2fe9102a731e6ac48203f59f3cfc71b24a75f5a3f7d6e2b40e809313f3c1dfe012d31de556b5014532e3e4f0d8a8e050bf618cf08d73fb2335d8af203530938538369f90b40d6e82d6868566dbc70098b0f5a4a219bb9a607813f71011842d1260a09a3585fc8666b84ace44d8c1f2528e8a84621924e0d4d90a7301eb942eb285326835b5d57a8f794c4b75cf705759bbd49fc8331c31a3f3c55494eb547a50264b6f0d1b9412d629a6283fe6f1bf3cb043f1e5c541f0b6d18f425d59c3bdb7edae877c99306c21971512a5331b9419951b2358cde25af6117b1647bc15525566b9e4941352ac9b8349d56821e01b77d506f94a8245ee70a21e805f6a0d03acbebf8fb4485e1f55b1fb08a411c67153e0a36ae43b5c780b0f280e77a5ce5b5b353c52bb5de85a70db836c85365deb155610cb917e7dbae97446ac59e04cfa396841771edeff76082b0d9d762b99687c0b6acaf2f0780ab7666d20d30a003c0c78a1d524dbaf1d8595cd84b34956f94588a3a9c72b048d18443164ae0096c45ff3083b148d2d76c2f0fdf4f67b8410f3000c11a6637da338d7cd0244fa6ddedf9c3112ae93c80d265666f993f118dbceebdfed4877952e520ca51d0334b263b4e5825a481f4647d40e83c2bc357ed564cbbcbf3b6017a0072ea601ba322f4daf41ba26a6d9541c2064e0deaf2eaf5e1a854d0a787374ad4c6cde2ffd7911a62a6ee5e2938c473dfb51e2cabd40e76ff60f8fb3bc693514e658cd562a9fe6ae01fe6ba94b80c5b87686df9f828fd0a670cad106894b533627a7c305c5b7394ba658f6b84655c892db0726419334c5708ccd718e3b10b5f2fa2eb7e1aa5185e6377f16c9f84c611e2cbab9e01b1fd22994c19ac0fee771df7c4fba8853d9491a2cbcab09c7e44cf4ccf217b066b20205aba01c53cc5e0103668e2228c3942166ca38f54e5e7532f038d96c880f28c8889701291f26de9e32a9d3eb56685e3051a3548a1e73a050e0c341a0cc23871bc949869b5afc16b887e5da59bbc1c781e0aeb20a6972b0016e4c0f8be96cb93df0d72ab5b4a8ec9b29d5d1d8b98f4911c0330df20144e7ad23c0523b11bfc51ba23f23a9cf92607425d01a1baca13ee7df606a229bb23ead2403c542d10890b6725176d7d5c582fdeb349aaab5b85046d51714d1fbfc1d12a2150303a78fa7a309ef0420fd767209a7fbd3402213c4dab3d767142ba465b5ce07ab98b7287b30664833e8585c3413af436fdfdc2bd41000333b1864205784a7d0668c49afd2da160c3cf733cfa1123dfafe21ba6fdf334fb25719e070865bb1e7733cee739dd9e474e91272e82c5b255454b08a571e98bc495d43cefe6e1533acf4f9c376e403878fd29e2b3180d546278cca8960867b5a1fdee0dce086107def211447479200da5de78f9faac793b41b94f1d0a746d4c68fb30451e952abf2a9ef041afa0b033385032e5391cdd3c71c24b1ba9317e8e4af3622b6a8e2400245eb5cfd1252b74840926c28ceeba386939e65fa99f8ad53e17301f25ef737efc9fbc1bbd64e03aeffaf01c2829b5e567d5b5450b204a5661448423e1187d26c6b077ece24cb91dd6e120544909d87d5d387dd930354bfd6f51b606e44670d425bbf75cce1cd1cb0b6886c6a309bfc7cbcb7b32e77c5c148eb10a116d509dba6bc369a3bd29019fa07ca867fc7c72de0951d1251fd16fba8df66c9a5696a2ebe4c11cda610ec46315e6f98d2fe850cd064100e30554eda64e76d64162c4d9439d2c457c4e60c4dc0eafc0008f2ebd58c05d8c2baa2ff7189881e23175025cfdcac0ea82463bb1d2cef9629a13ac9b011637bffa039996744e42221a46d282cac5c628578d6f2f005dd7f8c2739c13df3fc831efa2cb1e1eb140ba08789370c713c585b7cc8344b2067d3489caf9b8632ef4374979d75a256082e6088c8ead392c2bee3658a973da906bf3666c298b03e764a4968680b7e56a9fc56706e8e44d5e446ead3bd70fbbd18809178fbecbc31b90fb5ff13eb18e4ed842f095d1002b8030da6fb1c70b5f5550b54b01c095d484a5327097bf125622044da3cee761ac96c9e6da0582ee4c562ae984b85fb3219d6c607aa3d2891f39bb964e30d60bd0017b3bcd2f9d2cffcb03481b493c3acbc79eb5c23c42ad64320a9bd41433e5e34fe840a18be4c40a3c922cf26c5985be25d4fbcb8831399b7baa967ff7e77d760e140d2c9e24b6c227f8b9aec3c9964f37bab4469bbe773b9ca4dc3288be9f26ed13f1f795111e194ef86da4c7fae8b6d93093fb65155edee7a6191920dba9d3dfd9853fd06ca7cf2858d75c7c6ab99c002cbe4df00407663c39331338230cb24ecf292310ae5836d60c9d9c60ea34267774d8391f02481d76e12d366f13ff03360df59d3bebb5fdfce643242ad3be4ef637b049dfa8dccfd33586a3931649b14951969f847fb800926c22f3cb932cad2bbcb3925f2ae0569928699502784486246d922a94efa50a77a729b49055cbda24b0bf6a45c69b5a23456f6d12dac21837b053ab92f373117da7c21d717bc50f4fa12c4af78c026b09ab949681f891af89118b79d2b000001d288ac33437c015bfb4414cbf9b4dfcc53771387bc5144f86fe5aee7e30101a7ab6aa37419b20cedd566a130941770bdd77ce35440dbc6fb2c66a0ff1e5b66623be579f393b7d8c4a40a159e8ba88cfa6d9b43d6d84ad22b6c16cc61e1f089b349ae19ba0a2da0d2d0b293ddf4222f4500dbc484025c6efca20af84dd88e67704dc1b256c22ecf553e324f8de6b1bcdb50e72d3a7f2f140f33a752d2f685476825ba4b6dd2fdbb74e4efb7236eaf84d7bcc0e1b5d339f10f07a52ba34635479e8ba52fe071d6602aa3c8f78446e0b852ae1bd3721d73857d8f86b93e305dfa5996a6bdd4d8b31d8f47d4d98fd95ae8a0ab48de85c08c0d730b8aa8e7e889787e762411e1c3661557f69c22644197f4eb0c90e098d84b00979c44f006cb2cbf76d20854d7c8df6e688e8a4232c35ff73fd4f8990dd13bc65c854acd9b2d4441615b7f34ee691d23e81b36d2876493521836a75853f69758b5ee65a5d92e94fa418ad2664102827cdec579327c4440e09646c1e41fb2ce1d59af4dbea5d153448fed226849e1b46aa1dce3b53cf512b4b7d4bbf7ba9ca499954b7a9c2d175275c131cb25a93f3a84624d6ea9b6393e73690e7c0a2ade4262ba686269f249a80c88afad0e5beb8c726bfa6653d72e25201a3cb3da26513d2553c53a2b991ff29b57c568223668166c90e26c2cd486593f8496d9bd18bfed14d6d926eae82f5eb07b671854289e88a78f01525eb61f205ae9d7d7cb60935a3f056f573e29954a650b7a6db246e42c13fd5c80738b139300237510b5a3c743ffa2e9adfeb9b3d27584a6d79bf8df1b650fe1702bee978489f46b0879c3588bffcbf3c868c3f1a6c3fa3824f53cb4d30b50182d088958ff13cd86ec2b8e64de2af9dda0c81de043f5ed8db9b04427152c3630e2fbf893bb2fb42411ba935e92dd0e6bbccdb64fb73f9bcde84b725e76baec7dea6fcfb94c2cb00c92504ec1b20769d7b30a7d9844b643ff55a0b894021a0144a9bfc2d148ab0336e1f84e2de5ca96489b3b27dda84236e12daa5a8cd26293214b788145b3091bfcda3c63ef28fe527b0d61f0a5fd3d89db5131b30018449937e621adaea3132eedbe3be5022ebc91e5b9ad54798d7c3d045a7571d88f54eb596e94f94833aa0f95d9dd875e99f545ba61ef060b5f7cf2c5b6a73cf921e4d88537e3748cac49da708f419e087879bfbb73d8ef4426ca37ecfc11cbe984c8aff03fbe09ad6feb47b7dc07427b791a0bf28a6496c1e171c88829b4eea2a5c3c883c1f6529ecba9dbe8822b6b98d1f9abe982dc7650fd324649792b998dec355fffe356d4997ea898be81be3080150c263ce1ecf9a64f9473a1545d178de451bc572f60ecf400dce58e879b9972cedf8e852855e29f360ed73a5488349f4ab048f47ef8b3a61467c3d8ea66a4ecff06987134b9bd3328f923fd2068eb1856397a32f3e60385786bc59dbda42e13c12a3ed43a80e216d445924b4825f5cd4983ddee18c6225c9831fc004eb095a7000479608ff1e1d5cb62692015677c1ea21b7d05e5bda411d79e2026914677b00d0dbe5897d267fb17d3818a8c2052ee38e12bba64110f3310c45e0882362b872e522fb8646aee8038d381ad609486e8f916e497738d1096a54096926aeab72b315e97b8f170266733cadba871a01bd6de19f48f7412d79d175fbac70787b44bcc0cd8d43268f018ddabbd9caf017c10d43e8689e4772fb8a7e61326d97098629fe97c9f62f6979b0ed7f5305ff5a51af01444a2e800896416818a045710b64643df4ae37a27ddd1077f0d163bc914d36d52f4c2d2e5c33ebc490441cda9b4e18415a8d79738e656b3d64589d3c7f18f06c1674f9728e514bc510c14350940852ea65ba9600ba957e88cfee6512c7b09d6a3addfa2ef5a22c7b4481e4ac5c8edaaf9d67a0fa58ed7643f9a667454df6148f8c3a7f2e11314f3c19617934bd84fa5669a2a6bd8c29c5b94511e08829ea8152fa37b03a420974d4e272e67691aae7913dc05d1c6a364f1253cf937a49e749388884f03c6520688cefdd2497cd271afaae01b20c276412aca9ebdc02ba308d6d819c59625b837bc0eb67d6f035facc57b7ad668f6b19db6da294ddbc7a8bbf005da635d03db064061b39a74b6dff59dd91fd6cc056b940fb401f63292c3aad3b141521e4c9f6bf709a13a5210ed3f5323fa3abbfe853ad66368a0585c53b8b65d5d17510a83f7ed46aee4ea4629a0fff6b7b345dbe22cb633bbfec960e4ce1fcec2864615b98066f9035eed1520daab8032600b2e1afd821d15c0bbf323a31fc9840bc618e601274ba063b0aa49c4f78d031956a1c26747420395b73d99bca00a8f3a7fb4a55a712821f11c24e228b792a12fc74319d95a08b48c0d69c21aea155fa23f2680a302d55c1a1b835a4be9e93b7044b67a4c74313143eb38b779640fcf81b5c32ff271f1f47774f0ee607d7fc3e15cd741e5f2b70588d35ae721bafc4ef1f8757e47c2329331f2319cb1a6d8bcb99ab687742ec0cb5d57e1f0a24a23d6d11500b3072c89af622bf44daa902b024272abd5c3cf891cb5f2c7d293b7e85b379ad3320d4ec63481ccc449de642e961c40ba5f6fb72f6993202708f2889e9494740cb139bf00e4e9ba3b64f4bd7070e61cde0c2d03326aae0a100e3100a8d2bb82ce58195190c3af3957f1f7e9c6080da58aa750bb435fab3b230b77b9a05f53fff31761f01e3f76562539a4c41529350d59652f9dc50a91230271051ec449b2129fafc9f6527539f9424aad5f6db5959046b3d17f6db4a23a75606eff5576c3cd75e929a6b161a915d708629ab6575b7a08df687bf5059b20ec9a6618485068aeb16cc7b5562e06e1d9960116dbd1319559f1ef68c3670a4dedaa5c8c9da0d65339154b86e6d948e83503ed6349c689330561dcea7082de56d1a859660675ecdcfed0f65b0393db6a46943cccbc963104ce6cb001822014100909411a90a4f3f6ec6b2d80285cc1729334488ee740c7b5739494d312a40627a4e1c16825b42e0762e8f9ae1992e2d8382cbe5f5fa5b1ce988ebf0201f3cde0df8ecc5edc76606c6ad7a7b4b272ed4673be3f64f6a8a1948fbfd7d84a1be4ffb15068356dbad7aa8ea69cf8e3dd5598def36795749db05391d7ed4fd13efaa27c70054c201e3d332066e32f75861ab18da27ff4d8f7032b7fc80c6ca0af4c2320d0595e2d87255cd953f82453afb6326965fa20f7e0c5168d18d51d31f1590f98219509e33244db27cc32546b5796e6edae0292894acc39f8f44eff0c90965df14304668559b37e8f5859a994d04673e63611af205ce0d2534ce4bf7b64682865ce9bb271b87c54ed2f7f54dfb525e3292ba8677c0521ccc79e014f40b722b096b5f05204f045c2bdde5b7825cc0901bc60223b316c28d12cf179978d6650bb5486b9345a7ca6b38f28bae9847ecf45a310cbc414f845a8e0fbb338fb518da3afcc3d1d68e4376649c16aa64547b7d8927496560eb7cab24c40e1117114958732252b06ea106f4c2674988b0455e7d82b922b9f868227cd2a4602ac2cca332799ba1439d7fc7b4e12b310d3aef9bd9b11d25ef7adeb726dfc9db1855f70c1ccf0ba65734868019c05a5f357e412f87f006979e96eb6d54a77d607f73395b79857a21aae424c2a90ec200528fe75e278efbd0c1da0abcb04a45ef4e508a975d4025e32d8fe0070407d86539b694e052e3f98ff950506a5e6462dad15c633af7062443a720ec69f6812981ba486a2f457be5d594ad9b0e41f0f83d47c0ed9b8bf8737da07e4179f35e669624fb5be049834ed3e7deffa30a13b68e7962d79beeb87cf7dad27c2c4ef1e92d64ce8be418013587dada99d03c3fcb4590e9e65d790894fbad2bb9ab410071211ca2545c6658e87fdd6981b696acb1b0903186953af1f1e4c01f74988e53114ce4e27ae5e3949bd3d9d6ed63c25d90e3ae88cb3b504d77207aaba517c73be69e1af572eb6f3400eaeedc83a9dd24f62e90c24de3e4ea5b063d0923524b4f393cd71eb0c75e0b88ed49443e7d7f866cbb68afb2393fe90fc7b4b00a7bc5d48f8b26a68c864dd5a1b3f3936ded29078add292af578716200a294c59d98fc174b9860e9e7e283b9067aa6c9eac319ad211c80b3500efc4d4e3b83531160c7cb2a8e36cf10f594314d46e0153b8332f7442f76966c76502ba2c3c29bd02658ceddd418939774a51a8d173b83abe106d317abb3fe933e8f95b8915ff6072cf519892a2d4a82654b4bed432019a263f486f52f134f46f613b6a2ac14704d9c489144f86817ca8a0fe3aa5779f1458e09de5acd3139fd8baaffd077eb389ce9ea306f2e0a4465f54b3207ba21abe6661d3caa61b6b1b518ebb571af980fb447ba339065bbef73f82bc6853edc47580fb1e916dec4224ab64b866f26a787a0b157632b120a043d25e18348ad53810324ac47fdb554f42f9d6d947c15b4be95012f67d6006406d6fd2184adf581b90bb881469e8f6e52a3bec3a46ec7c791641cf44e43c3773f93269612263c4ab3a69576ba7580bbc3bf821921196f85770939a95e62dd84c61cb52be9f59b841e22a871ba5b95006328b49583f763442b984ef5611089486adc4bfd7206653e995e65f4f925949e2371fee12b105cf27d4c69a7825e5762653d53ba4a7a4c692aeb9d2931cb2a371fbbb553041f0c3226426a0d64ac884c78866c5011d907de8bed9c5d7e1eae9e5b3442599233c3a06a7f80b7cc85ca404654744a2fcb40365ad5622a634b0d544a61cb40d23fb8f21e8fd41f042405418eb8f5b63194cacfc0102d6afae8ffca838e9076624c541a53902468f58162db8247672fe151ac030955ea2ca8a39163131f384d0bf6d683d89536fb83d7ff2875bc8da15df6cafb0d620223a07bcdde3e3efa41c281a965f7272d95c8271aada0fb1f887e75dd218c948790c06c10927e72dd322ce4603f6884b76a3423f9701c22c2243fd1cd70a0107976efbf44a24439fd37315f945aaf48fe7f527fee6d1a0a4ab237bb7f1612e7085f95cbbb23217acebdd3dfb90ade699c9e5cb7509c7f709362569dada839f94c07c81bf21166cccdcc67ea219f6d1d89e70a405a4dd148831b2ee3b93b53e12ab965b8ce3ca6fbaa714dc2b19a7dddcef08506545cba8848210ea2f85b4cce486ebfa340b8bcbb2b3fbf746a3b614c6132b6f1f0cde3738bd3320631c1cd241bedae3f955499c39fbf234ea69695acc544a0135ef0290e2023e105e27a20dc7d21bc907c14b7095defbb087cf0e39d863b261ddb8220f804e18099906779d6a991c00bfa955dedada414a40dc82a7b30f8a44b1233ce338505a1a51edeb9e820ae2ffcb3cdb1a6a95ec408010f072722412684d6bb041047901e896576a30cd0a046538a7ceca1813fe1e0d911acc519a45d70ddd196a708bb059faeb757726faadca98b4dec44d73794f7b29b8bf8150f4ffafa0a83ed26566ab60906d45e226c9d210e82aa130dc2e668f4b16060a015d799a4b612246d4dacc568b51bc6734e1bffd176584b3c7b0f9c60168ac3b6e8b5aa80a386fecdaa2f3e10922eec657e3818b4dc0f6738f080f43aaaf642597fddea63bd0004f2fdcc05cf6176f4e28a7119fb93cf0f42bba540ceadf730166e6b62718e4626b9dd7b73aa731886e04c569f65bb811b479b85458d0462f460f24ef8266970340fec3b63fcbe0a71e50eb6e97b912dd903339ce77fa470eafe3dc1a5fd7768823bd31f21b842f75da531c67ff9fdb89516ba88a252edeadbc12257472c9e20248c754d85b07829bda15a10e8ea6d9e6f9c552273287d2952b6eb94ee6f7c8adc3593d479024b1ddc37a9a0ef645e71dfa695581084cc25a3cfe8c034a495bf449e69293e185c2ca5fc3d7ea1f65f7ba1dfb409b351aad879df61e678a2f5592546c59cbdbfca05bd3e6491b51166c7abf9b4f1e164b6dffdc30a63d3ed37053d0b19597bf3c3de32caaefca583cef4205ba96848626b444f54372b11d85b7d3722264a5f7e4b756a82134aa003035c055663e2479091be677f333db48103ed850e007be221c4dfdc2773f0b7818c7ecfacf76d12bd1e8a7fd68a069676f4ac413f4b334e2b0c5f806aeb44f1e6e61e7b01593dd9c35d3a30a2be53b795e88481c91d2db28f55a3a0be2f678df83e3a8f6a063430fa4a3bc6db11be7a3d7665ca6114225fbe6d327f94f4837460ef2e5e5f01226e94945054eb3ac2325c35606fe78797c40a96715d8b50b07792f55a2ee202ed6e67c8e1b4ba38532cdd03ac972e5c6344263bf22ade864cac784a910fef5f1be6ea3827be8146948b70474862db3b513704c0d5582d32815dddbcb6b47880b370e0d46ece38da3a8369f8c58d57265ae86165c9b75e36825b1760bb4488240792252ba56e5e97dd85503df374b0344f3b43dcefd84946c068dfae84ffd81493c77e94df775573cfc340d329bb430bd4423b2cba0f94047727e78d80d4721e048f14fa72366ba96219a886c7f34ab7bfc12e0397db83d6792f5888ff0217a634d137215a3a334af13425b631293027646cb81e8a16c5510302048a57bc3ba3186fc4e9743a8ca6647b40d1db8dcd43cd9d017e2ad8c1a61f2728feb516b160ee8cde3d3227884c6d6638fd31dfc29b88483ee74d3b13b59cfe4d5df445f1125be8323e145ce51588140a1a4dbaabcb3be492718c3d0bfeca0cd135e396d90a91c43dd96447216bd2ead4aaeb24de2c8ab7e84e5f67d9bb7232c18d63508777dc0a68aa9bfb0cabd03dfebf3c522c988b9cc13ebe6a1a6f73bf9734905d5a66ad2d4c81bdcf6346e69266b200219b15c654548210441b1d805a0277117226c2b820deaabca2f995cc17adb96a4b91a5f2136dbcf57810a74371cb224af2426293a54221df5bd5acc8a19d89dbc15b54854fe702fa19d7415ddf211368d4ac38ffc17463f5e789b301ac389cefbab170f1b5a3d74fb54723aecbd1508f65323f7a6ebe73ade7b3bcf486bb0c426aa60e15ee79c5591cc37b1732d0f256edd0fd7e66ea0c3ac154b28a2f2b1cefba7d0ba0dbc1b42f7d76aec00ba09a1bb67129b81208394433334444c5be959c55adc352d9429f5fc252f6514fc16082ccb94fac6a0bb335f52709c4c760d44bb54a0b96f4fe98eb40cc957e8b8fb1c7bf6481cfa85283c14329ebcba8dcd71929286fd8d6b09ddc19440d699ff4ad0aae0f6641ed81407a51da5877369896deb40d79c187223ec675325df222d3c7b775c9dbb658ccf41d9c02614e2bb770e4967c867d81ccb173ef1e7727443b126f73ec15ff7da3680aa853307aac76fd6b369021e4ac3351e1acad70652c80f37df61c765ea96b3c4b0c535cc18e57a7643627116f7ccf203db3fb5b04c24fa4fc22807c9342816c2efc476506a278578551d9298695e0002bf29a0a5d6f8de2be77f8e648fc1290c487d74f82fbc5643100cc481cbcd6fa04ab646087e449586551c64cac953ea75dc4070a08045bd3492e6f52f8e3bd517fa9f87fcb932c60aaf72c930171242a8c7779d8fcfd525aba4d7767872f922c4a0626dc8af7bb5d42e672cd22148468b8acf18f9a7fedaf2b798e0b6fa5ec6af6c6f2a56438c39b60c611694152b713e3989dbca233ed0f9d8ba484e23c06f9abca1070c670d774574a6a092bdd40fd6dec194e3b65858580208b9015823a0b670c3c64da483b5d8ef3e59941e19740291caa24ec8b8cd76e5a95e6038c9e94624396e6262e5cbe8446d4808745cc40a50f8a4c797066facbdef5891e5c8669a82b3882d0d778c7cb0fd78d9446169ffbcd967c4fc3792ac95ac448cb340dc415a5e662b82cf5be24be628fcc9b07519b504cb066a563ece2490d61bfbf7a9a262e95137bb365f0242e89300ea5a2e66831d7940c05a66a1840201dd06bed5fab80a8b4e4809c43934ab1441a5807a4ebcad2139aa7e118482f738e298c3c3ca8e042aa40cb613fc568543092b3b9e93b0ea39d5d2f1daa4590536c08302f427b408f600cbcec7031aaf2629d33963df4ab5c058ae6a119c5962d01ea32752a1dde3bb3ce1ce58da8d1c0dfd0dc2c7fcc4543dbe10449a160dc4996d5f32f7327f7a9b36298f4b50137c94884c550867fb71de61298e9a9cc9ec50cc5a3ff6f16736f87e2f3f8bd49f95f56358cd7a8940dc11aae327f8462baf8b61e848b71072e3648d998c5707ed86e18352b6204486e1a330ddd05367db3861933d6b5c6b756817a801d1b63b61cf487b8878f75c8a7ce87cb9146206fba0731c90d944140803a68df2d132b5aa5c11e2e7945ce6d99854d78e79158353671593a03add9e1a95d6e343a0936ecee4947661e8632eeef0445d81744bd0ca1a25937741c1e071fc1265916d3ac5195f81f099a4b9fdce199acd1c1b3c66bd1136b1df854ecb29dbb303b7708a559bf29c29ef4ff4f665343304931922a96fed792be1e1418c3b27e708065c0d4974d54201a051317c433de4306033577920e0a117e28931542ef7e7409b4efd3935c832617f116bb86948cfd53032fe8c00337c8c7652bd0e014254656a8276cde7452e9f077305ca2db601569e9d6bd499ff5709385e75db001922083eca87faf375c7c024ac745615f304ebaa20e85de0bc83478002d8e49d7269f3a0e12bc913e269f913ed7e1717bcdc7565ddc9a799ca2756a130d6010ea7c9fc690700890e1751eefc757faff1687a1bc5a6a00140e1ee307f27b793c09d8aa64ab58b27793bdb79452262903b105e705ef0541b07460082c493881658a1821b05021244bd08f2c457ac84244cc922c4ac092d0aa4da1647182892c52c44065f98284162018d0e2434c07f46c53285a9850d1428216b45411fb41c73685b2a5855706b6048961419b9b42d992a4b5023db329942d4e314f34b929942dbd146ce9f29b42a10203df9616dc5104373f519541db0f4b194cbb80a23e08ca64a20c05db3e23d0434542d9000f55e679c1c7dfdfef9fdaa736bfb5a60106e03522ae796a90f2ae20f2afd749f0cf8fcb06b87ff44f15f0feccd35f02f85de6b50a9b11a986785d3282346c151c1f40aee896a6174cf558a02e2144c5123d32154a0b3d16a05b96f48076c76477fefe607977f8b60c436b2d498e96fc91b44f5af3d40fdad0d45f63d2bb915ced76abd19a67f8e0f3589356a21063db12e422b573dbda07f0ecfc20251f409776689e3c3b9b0d003f04433287a64eb9a5d7d3983cb8d21f6bac3bf0695e7bf04f992c84eff54a85bccf08f4d8e087b0879900c2626060313130fce4941312da1172cae57cf0f1bdc005db3f060696abb948266242ad39ab73020cec049ae3b4a7f95af50aa6e660660856c7e65d349954a6b53a36a69bd4525ba3b16c230dd74b85bcedcb5a96f4115f7f2eb5f69ab3ec67c3b3b6676f4f1adbeeacc09d7d50979f59e62e34f5a6a4092a0b82e569c90755c87b7cf27579dac66795acbd2a67961473336b561f35a82aa1605395e0119907ebeeab3b7f3c559f62515c72413e76b9b0078cc79ad40550d87632547d4d4adf0550fc95b280a2805ea943e238aa5e2c55a24a14c5185bf56118be18aac227c557813fbe18c6d8aa07e9122f47087efb1e0445f3548d260dafa110638baa92eeefbfd187f5aadff19b93a0f885bf637c1a63b933baff68eeecd00e9aa44967a41896678f99fd98d7c75ec7eb63b08fc19e47ec5f4f7b666870d1630ccf1ecd93678f6fcbefc5936787cfe3e4839bfc1d4b33f20406f0a854da415f3dab3c57e21833c646d928338ee629feea676464b11895f8a1aafc5efc5e5c8de3f7f47bd600c6b72faa90b76d87a438b68c6831b596cc1c2d1ad2f03e581e817998b2ee58c08bedeefe14ac3c21e4a03ee8a796b7a644f0b32e22dffcb6d58baf63f5e2f82b5307c9437cd2b4f5781e4b117c31ccb3fce02ceb6ccae883eeb8a9e906076d235721680e7e976a7bd5a943541cb2b65c87631b7fed599d29ab133ec678f7343e515acaf884aa34cbf8eb12f8aad711be68f2b01fbeca0ccdd9f3b80faa9ef6804f7bde82d6299b2f83d64d38e3e7f1918bbe95e7d8318f2797b5c7b3381957265e3ba2e45a026f10e9cdf912daf645afd57d5810876dffc4b17f96ad4db2348e0bdadf7e8d4983d96f678fbd0a45976b5fbcc46399da0c9fea24d68a0c475c85684c462b2c0b2658750ff03261e60c1fa065ceb0396bb5fe65ea68fdeb5f2d738778c7a736c83fcbed44e80781615edfb976deb3cee440501badb7d6a59ce7511bfbee699f7409d4d785b675211088dff26759a3885765153d5bc13c0cf63c5e3a603c5a0f3365b26d9d069a63bffa4805ec61c2ebbbd7b73ec677424a34c7c698beb338f6791840cc1780f6c47cec797c87d58999b33bbb2e57ec5d3173470cbef869cf7df11b61a68ed6c3a81e97a1a92a0780bbd54790d76c0ab1a73db08f791d301f3379b41ee6634c1e3efcb41ec6e4f17a98d973cd934e6dfba95ecc63ab7cb95ab32aa4fa1992f6a56ded8b570c792c2ee9fe32e8bbd91daa42aee44b84a62cce7d6b1f02754773b854a13a04c4772fa195c75e58daf81b31c9a3363c98c0b87afc27bcad85c9af49e9a2efb1ecf12f80eea036c0c7313fcb22c62d13e663ff03f3b1cfd9fc89fd8c5901fbb1afc07d98efd1c3d3fa1a93b6b5ccfa3247316bf1b4edfc35ab633ff7ac3eab9055e54e49a38a56fd69db18bf6a2642fed5afcc93b6b339cb5ff7ea7bec1d2df304b5796aca324f590e1e24fda4993fe3fc6375884a869a7021e28a9e99d721f33393870f332f63ceb20e159eac804086a55ccf8c39cb013332b2180cccca284efd97aba5592b167d9105da10fc32be22a5a911778f5ac71088e28de9d65ad3dd4b9a1deddb50f79a8de6765de635987ba5d9a84e73aa0d9f596bed4cb5d6cac8ac75777777678ad57c84e26de8d3a6d67eaed9189bb596b604c30473f7d7bdd7f5656b6dcbda18f75e3db2dc57d6da7b2f2903f5bd7794895fb636747777771fab053f77cfee8eadbdd67ed9cabefce56bedb5eeeefee52fd79ab5eeee5e9dbab75821e087531293ad826f4ab3a949734491c65a4bbbf7de599db1b6ca582b73f7588cbbc3506aad09b316eb5b6b7dc9ac7551dad2ee2cf71549e948a9e86e434569d5eed8417777cf14bbfbb5eeee260a2aa56e4ae9fafe35373158a7f6d27cf9e26450e377601b3ccb74e6fb4099dff1a02c54c56c085e9d8209aaf0e62fdcc9f777fc6bc572fd8e67e9964e41eb96d6296898f177bc8dc942990cf83b5e66e6b37646c3b6c6aac05015821804adadc28260a812c751548560160c66b7f1526b54ae1d17814ab1ab59d21c9c00a7f6d2d05a9b9638f7de2644e04f15821f9ee53cf37da00c280b5531558c08b3a44f9b2a039aa34100b6fd9a33108009a408a36d672c4b9c9d8100c8fb3b3b3b3b2f96ab54c0b3744bb774cbf582c18ca21893853219907e33b36a565ac3f8d27035696a2cd93a6572ce20d51145130026169b0fc5f714aa79da6abf9df5a766697025d3e4cbc69a94b9e571e8349e15a2a1aa86ce6cfade44548daa52e04843bf862d994d7132d8b40c7bd994225733a6a5a525d58a3553775557752a9696d1b2962b1603d3ab30d4a0155985789a82ba4e402a05291d6a0f09107bca615e3131574cd6da116500999087459fa419b5ad667178361f7e0996e677f3268b3cc301194c5790e610e96ac3b0e543328e03cd593db9b00252a13a61683a91933ee4352d2f46aa436d359c2f6532ba5579b45aa13ae1fb87d53c7d86ed2f5cb1822a869e9e7ce869c8f4217ff221930a6d5353bed0e7341e7680dae2c856d035f3b6cbb59dd8f5611548901a72406ab8620393251b9ef65981d4a91b98ecd37344370c65d180921a70b02287204f8a8282dca5f33271606a9ff7e9bc5266febc5378b33f6fef6ad9f5afd479a7884640a4e7c43e687fe21fbb9e5888e64f3c8489767d5c84753920b14fbcc34b76c54c3053138e1212649f9887abfc9f580b962affc453f84b2e4888957d66a1330331ffcc44452cfeccbafa7997958628ed339fd9889b3f73536ef167ce538470d92d5afc0d896b7173f32c48dccd8dcd420892cde24d12c7c234bf2471669943d42eff495cf9ff3624ee6dba6c9baf2171363a35354f43e26a688868d8344f237134b49f9138da8c0344f6ec6748dc8cee99972171333253e8380599a13239e160c4d7d360bf5c50b7eb5b24ceb5e33414846ffd2c12a79370ba018a46da5065933f9238727c91c48da2ea4312a75ac2a90620e07f240e84da1f1025fba369c12c4055e6ed1675e608ed2434450ab4368a212a5a8c19b6bc7012227177943203122c76fe191c16cc70819dffe69721715926ebf1c3112710ecd0eb028407f20e50106195517a8606159a326dba3340c5be7f7198c8c0518d9d3f865325033be89d3f667c92626718129775ad16830a589061890f508030c2e9092a240c0345fb3ac15dadf152ced90590ba748c1f5cf6fd96d639e75ccbb59c3315172edf1f1fd0c129c18f0decfcf957242ec37841cb8b9d9f247159d76a16a7be25a226c02148882553485094816ce469051a6e33358b4afbde5791b86b850c2d70b0f387384bc4d0030823286a672576fe6f043014ed64138918362a1eec4c9b4ae0040317469010e0528f205057d8908314278c88c12e01b1f3d3a8a306417dbb578052325d1c0c69e1134c8b9c14b19bdc970d582829cd159e5a942b7499bdd0dbec6e6a589032a361a18a1934211bcc5a8072cdb4404503991a5a321e58b12399181f4e0fe3838a18cc8517d1cba5a4f524a65d7882e50297d7ea851829841c8d56a2102e55123021ef053a5d20053041ac1b583c90418c44cce82636083ecceb05a9767a2105e3d4d79c1cf554f0c39517aa287add7065882a579868b1e0ca151db0b4d084254832a35cda766854f3867c6b7118a9dffce36b7d0751a4aa3fc53fbf3fc13fc33feb9ff94fab12c7f1c5d154e17d9de27d7d7f785fcf75f3585c9e28e0eabd42556d13a2f8edbc4596fff84ce8fa15dbaca0df45eceed85a96bb4dba3a2bab57f7e5cf22e5d65b3fa0efd55155f8ddefdae46a3b046fbdf5d65b9174f9cbaab0085b6db5c1c8565b6d30b24f8750c2565b6f30c214c8f90a5b6db5d556208c84a8ae61dada2ad3309db69a3e1133646cfa4ccee3b5275ddde684c0938385255b63eceb3c16a7049fb2bd56f3825bdb3076b53b361ad57ea53d91e51997570259adb423acac20c48620a4200181acd61e4345400085032c6409a2973f60c3119d989efc377ebb5b66377542c2c472c05c9a819faa0ca61817c3c8385fed097c21155299ac058d1a59f068aecd9237bed6ff36ad1a26f1459bc964333232a6d892b8c42a429f3131c86f270ab9988f1f7eabd85e07884bbb01f757456807884b6f9fa608ed2fe6a7196371e99bf4cc19cb6dfb4efad836a539d6acefd89a30fd920279352d1bad75a9b579e3e2c5e83527f619cbc9a23c09b687b6ac3030665261dea6258e022996dab86773648ce38cd15c82096aba41465c2046d03675daa1862dd2ea4453271e7a9b3aedb0649fe1a64e3c48d9508660026588255da6ecdfb4898b121ee435c71663d1628b71b55f6fcd35d75c83b6f51f58e8de9bffeb8f1db49d4743d3a3d172b31992d0cc4c4f46e6484a26432a8ac58e783131433030484130d8d1d4eb95d473b9845a2d9dd64a3916eb69b54ae291a4d2d0384689a2944a953415864544fb472968ff28f9d8946ad127c8c4b4db2d5162224a9db42d9bd959fe7157134b3da0621b28a4e427156ca0d0f7792cc6d8cc4d56be1f9fd07e02f7bc86795ec3554411e7dcf62f2a6c4be3a4c72fdbad35f194d7fc53fa983e1dfe927cba6fe7b7acc464bb9b99c9f765fb94d76cbee6cb16fe4bf34ce19f96ff39bff98fcb6a7e69bf46cac6ec6dc7d5981f17af65d12728e54869834a7e3bc1a5ed4bfc767ad1ce1f5cf2db0932193165fb834c7e3b715193dfce9b039ffc767eba2da576c02588da28a236a86ea764979ea0e49eebd01e4abf2d8eabd0dc0473d99e996cff1fa4297ecb4c36dd34e50fd2141e0fa98adfb20736ddb4f207a94aaf77f4c56f79884df7acfc39fa92cb21fdf05b1662d33d53fe20fd40124212aae2b7acb4e996297f8eaaf47a475afc969336ddb2f2e748cb91d491141291df32d2a63b56fe2011211521151d4df15b3eda74c7943f47538e78473c207ecb4b36dd30e50f1290a121241f7ecb416cba61e50f920fa420a4a0232e7ecb1dd874bfca9f232e475347534955fc9681d874bbca9fa42a49bda4de0fbfe5dda6bb55fe28fd10125232e2b7ac64d3adcb1f25233add17bfe51f36ddacf227e98b524e29d7e4b7ecc3a67b55fe24353d3d254df15b4eb2e926cb9fa42949bc249e1210bf65a34df758fe2801511a521a92e2b7dcc3a65b2c7f92a4444569f15b46b2e956953f495aa4a492b8f82deb36dd61f993c425692a698ac86ff9c8a61b2c7f94888a8a7e947cf82df3b0e9feca1f251f4a41d406f9a3144473e8e6f19bb33a18bc39bbb43dca90d43e6fd135cdcf9ae3cdf9c10f084f0b94be6691433d2ea216cc391d01b2a9ce6fd4e9c80fbc647b1227244976cea64eba2f5b14790c6d7b499fba7f539a63edbb1df2e13592097dd6a2dcd4e9455e8138112e2a22a33edda7a3b745bd06fb74413b07dd1f3767f382ce591ddbcecd6dafbd7bf854e3db09b1babd58ccbf03804d6db8560b02414b1e2195685ac3d85e5ac3b9de25ecb6033125a71d64f080667dd9c108952c469a5e4118f100cc041a665327233c4c3162c3124658700d29a2020de89a4d9d8a44c1bae8d8a64e459abc94288204064b111d5050448658124556102262450a112897155a6fea44a489ab49b7367522b283c140a408ec039a66532722178029818e917a8140bf9e30810e48c472d0b34d9d74501283a203912974086a41a163ace8e99829311c215574340222311ed0af4d9d8a66800dd1339b3a0de912c4102b303a2ddbd4694894100c1101c802b166535aff746d3ae314a73e0dad460d997216385381cad849c6385fa88caf132c5260236c6454ab1136c24646419c4e70028b83e7daa76b8bc0c1d3e3d953ec9e6b6f24b93792d81b493ec8616d921cd7ee247727b13b09cec70eb1e25a7bedb5d75e9b8da6c65e7b614fd4d87c942f4d6c817856c7a45ea4bc4cb160711345a6e9a6451499a6162ea2c834b9781145a6e945add62864acc88c6a383de0c0c0e00a06f67e20a8d3815decd7654617230689b339f65eb13324ee76c9912145c60c15ccb0cdb860cba056241968502b120d36a011c3ef6870b0e3800312676ff646b1486e3a3bb546b153a3d6286ad8e8a0de5b9f68d1d401009ab4b84283831b38ec8d228abd4d6e3a3878ec8d228abd4d7676481c588367839f4b88bd3e600f89bd4d6c74b07bec8d228abd4d7878eeeeb1378abd4d76cf959e0fac7c90c35e1e9de5d15d1e1dcea7569dcf07d37df00108b61046f7d203d222c416c38cbb830f082390606f14512c121144207176047b71148b6d24ee2211618412665cb8315cdb0866ec48280149092620312100480260b31b68d004ce38e38c33c65ff014a81c1bdbc7804a022a09a824a092a8958a144020c192f1c5d7c8e8a200a3201b619cb1e08c0225a6bed46a54ab519012251867289c8d6aad526b956a6d9d9284132c385381cad809ce54a03276722fced7286327385381cad849c6385fa88caf132c5260236c6454ab1136c24646390411813315289c8d30ce50381b7d09825252ab51ad46b51ad98ca837ecb4687befc518e79cbf8f04010c55e248ae58bae57ac1606262329999196d060dbd4ac3ad34f42a0d4f2048034900eff129728236d6c8f811f423a8c6c6f63148712447f16d2506d584512fb054734ff5024bb92793054e97a12a745d86aad0b1b8a95de15191bac2a32275d3e28509744c26d06d978a52dafeb1162e9e1c018b501d03e88cb61740c9e2f8833225985e5633804eb781aa9c60892ad1dbfeb1022881a0d632d97f8d2aa1b4fd63481fd506e2f9e0e20585028a1705c5dbfe3eab243738569a72b91e8eeaca6a36dc09bd14ac00652a30812918303028793c19cb0a0c462f55e8d04748cc223e11f229f29a2db44275c6779c94d5194d9f229bed6505d44b1524083a712285b37d74325e72642ce996742404d11c192b64cca0199a61e3095a0ab2cd32b0693003d483a1dc51431e31006875c4720021306db0010d99e5e016333575d3c1d1c3d1834227f429f2394275703c99f449d213253578389e703c6d07e2599c7742aa06efdb35a78352c306095c348464ec6845f064ecb63f0b5e4f830d00e0414b46512d4a46918ca25a140d0e3cb8a16b4c2c7235a61a138b5c8d89db782371dfebd86ed870b06abd7df97411b568072ea23ac39d1480ef6a529a038220500f079a403cafb98bc82564a7c658c4454471fc5d43a88e2965a9ebc8761a9d1155a6f09870a2892853caea00f14ca9ed40556874376e90b8fb373e4a8cf7e4888b68caa68fa5e0a78fa1680735297586dbb01fe3512f5a88b694da01501474860351a139fed93c6b4c00f16845e0e0b1322af146a5a2eff1ec118b204f5959ca2ea6d8634f48c3b67383e4f9c09623462dc7a8354db69388389fb1f26440bcd18bd4f69701434e0edc28c2d844ab3b2f55f6139d4f918f9113b83609414f3e453e3a1f232090385b08f7da1e4c4f403ca09ed770d604aa42d3030821d84610c9f17d74524f945ebb0793156409d41b2d10af06069f22ab03c4d3da0b4f06e9336303f5a278c9114518c18b8a4620a10a099130526ac35602f56205884781a8501d918aa64f91a55eb26c1f233de8126c19d86e087df79428512285aee51017df0fcc16dc7dbb6c776ead381691de9c620ee7a2a80d9bc10a9bdea75fae599c573581a5aea9723185b8b3ee4458b1e2c214537c0c5835aea6606910ca1a195c5cf156758de56b74184c0c88849689aab1baccdddd1d464873e8db1f66344a478bcf7bef95619a255f3b4f5b738dcd78f58ff7daace053db2eaee87ada7dbf82704d93050b72890b1c1c18f4a580512383468276848eea798e59b2b81965b4b87151abbdc0a11d5131186d313070ede20aed084d2ed175c915ed1b8c2eaed0e0e0a6c3018ddb4d6a67ac5163c7868b2b16db185d5cc11d8c2eae5000b8a6365db224ea97d0f5c9251e7870e3060e1a1238706020c312d50f46a10b92a87a08bbe88a8e74bdf75a37227fdf2eef32ae545c8c955c25f503193bfd10e874ba10e842a0d325499244974497244986c1c0317cf052afb9dfe8a6385075c39defeb67d2bc1698dda8d46b9c4b52c76406000400f316000020100c084422599224692027f71400105a8a3e666c2c1dc802410e232906622886812084186388410619649462ec0c00e80a9d4c5d7393b66f9d243fa9ed11979069361d90e289cf09e1c16ddcd98491ba0a9b22aa9b2ef640aecb510853fc7dae7b909ed2e70b6c7aea3e38ee872b6f1c0025bf87c8cbc05770fa8eefba052a69eecd44d2d5c178d7e876ae59bbec2766926fc6d1cdab51cf60ba9b79286f14fd2a6f107d19153c383d7289dc5824e851c8734f48338241976be37a049eaa560664a91507225916b608c0660d1661f0a35b4a4a76f6b3c7499f68379d57cb621c4c27c226f4d5884432079a2c794c654dcd951b050642567eb1360c1fe615ec7140727304f191a21aab93a52bca493d59baaa1cd490a52bea492db332bd41a362b9b2a5ad1d201f477e6831bdd34d9545692d02eb11bc302688145de835b1fa38b77814063c3bb5a215b04cb0bc18a4f0057c2559f609e639e31fe49f17a76a956f5c119f9ee86c221df0bd2ad722c8ea42b0d5e06fca9303ca83679dc3fce2c529acf8cb4724e26a9b34674217d2d3430edca21cdae07685587567d651220a0e6917748b428c7707ab28881ae799150b23ed4286553329f483ac6e2970d2eea9b9099c29648430ba020243167cbb0faae409362a8b42ac770b5651108574de0a3c354262fdb628bd96709b3f3a0e40e9df21e64a702bb8aee077ed824992f94f8972f56854346abbb9b702bbe3e79244375bb6135c2068209ac35f871d29d4e5f5790a3d98034b4a4117d795a13b465f554bccbb969b6f5a39d16155d9f9d82b9339ca40df986b158ae3cf70d368230f0935c6290aa1860755d5d0be90e80b086e0d450502b7d3808f4c98960f86f95edc0669f99ec91a31281bc0e12d3e45dfe14da5003d7ec5b3a8165e081539728c2a197483e532aa05b64b01fd734bf0dfef75082cb844da701c08445830c1324d2d1d0e9b724c95290f22bd1499443f45f5b3d874f515ddadad48575b998e1e68424f3c4043e86674c82d150a69ff86e20e26db6ef1267bc8bb94128f3179f59a33e875f067b86208d0040247f4fc031da66181653189c49ea8a70b0282e0c0270a1f5be39977674a07a0979b281f9fa2077ad79ca5e7fd035579b89d1285e3a4b9cffba1905049878d90929bf47d7fa42a2eb750527798f417493f2d6d888b98a99b1b906284cf0934e06db9dbcc445525364555b4af14d746f5bdb0f5111a3ea1416f50eac15caa4ef328fdd19e8f094cdbed4f98ef012fc2416001d7a1eff06ba8f0945308e293f72d709f5dc2ad8e23716187d367b68e8a9d6e8a840539133f2d900052dc7d33432b2747f6143dc5ed397c2e5d69ab02df972c4ea5d15d44aecde9289a6e34ccd627bbb04f183760e2bb58b7c82a4a0ec1a1fee942b61deb4910c233be42d7c264eace640598f5ae65f06b0751e40c444f06e565f0fce868406e61df3c842fcb0775016f9cc6db8eff602d0dac70e68fdde09a3b758a777880c365d46457e8bf818a55bf4dfbe0cff3e23092ab07a8cad94076aca4daa03bd3a59601c26c43157218a92e1f6a3360ac76714879cd1036ab31843b2ec73c1a0dea497aba8c595eb0bb32c2c102318226a8e67c19a7804d43e3e30a347083816e4271850a1fcd628c0d44b12277071714a3b87927a19ab28dfece4e530050b1892603fab9f567dc1b60312ae12bc0d251bcd3e2bc441d0b7b68d68f1132420a7e618c95276cff57cfab6fc94424948948c89f9350d4b2a1245775005ae32ec7eee64d4a76b8536c7928649838e41209a2f7ce2d5669e830425790cc4523fa8d8e85b0e677b985599a44bd1d074fe4ccfd33320024d810e62761b170682c7b7b09b482ea7528db3cb0b2b0e164686e778cbeba80ffb3e658efe5329527b818daf2092fef595bf7c2063e3ee29fd0f3e62ae56275e77fb60a168cdb40427ca033fc0204e0376edd31974ea67484298764894243eb2f6ef0b2a520af5f4735f03211d6347b3a4b0f63fe0f56e7a422add7f398885c993061ed2cbe820983d2116bfbf9c5aeef2bf62c60e7603c038083ea8dd0dd62be34c44609456253e275ead6fc3cccf4ffe724b776de4b8d084e932c222062ff52d425107ec5cc90701f74c283c72c88e21b236c2cfc380962c3252e488938ac951d4a9e7986ef83a7e1ee381c5c7d6c823de980d5d9943a48f28e00380d4a7a96e834e7b0950be15b3a77d1b0a947aedc2b09657a600d25dbe4d79d951008a19d12593151a0eda43d463660eeb75f47a3ec5525c08b5d29475fcc013a601547878b26c1558711f9c2a6f18573594305b9b4deceb9367e6331abb7b615bfe8c80e105978e4c319235261ce42b133007b479df3760b4d56995980cb170a658745164ffc1203d2e2c37be579747cc77a53758a78cc05c1e8bc8060cf6deb489085ae85600108b6321051016406bab21163bee8a6ad0c910290a7f605b28155d1c096dea99979ca426d68958542cd0003da7d3df43a8196bcac305326dcbf990570eadce6f427bd3dace8d8ecd453fe3ced03a436f90eaa95a6651a64ad46f1b4f501711bbd839fad6c5e1e37c01c2b0038662301caedb322cb3e2180462a24b2df737bee0813a3db9ac2d03dd0231c949f149bbc3ed7dafc16f24eef32035a68e066b963bba979a670e6099d63f02a7b58fb11ad5705e03811a7c154730d55a4090e28502c73b88825ea71ce14e27e7d75846e6814a793254b08451ae96faf7a21b9aaa953dd39d626b7380d397c15749991a6c98e1fdb654e23c42a6d2f6cf66883bb8c258c0c6515ba009e652e9e6cc8fa04095be71749ef2040138611dbf7b4b3141d2867742e75856dca5f0634183b9c18a12c17e3bcde5214228e2746ff1808ed3f2615f2c29d9e30e3c70768c13c0366d3c4cdffd872eb01d742cd2c35b8c496b824626d5ccb4aec62c1c3275cc089ad7f0dc166874a1312af172578d5d60e720c46e098436c6e4263eaa619f339a2dbed55a0c741bb7d8c3920b868a9be798f426e70115d56e1fa61fba6a49117ab5dcee656fe7048d4f45781abc1e8dc3d9c3cb1895a9a997dea9d9d05ced46d55a52764ee41e8ca5ea06d759d8b2ed02b5f652466e7c24cab85d3bf0c0ffcbe6c632c94b6a885c8b9d3d5b3f7b9be0c34f0dac7d3253576301cbd2bbb10e73fea36a212f10c6c3ac3261613e375d483b615c7e4b21712b9059fd9494fe1520a6bad1b7c2b9cc9458b36a4a75f2ca3dfc4bb0309e4f8347dd435c1d3490ebd2a885e4eaf82fa8c03324379e235345673d81fdc92e31a3d2a0f891131735dfaa446a7c7099841d701f00444239fb0c99262a6ab946ac7344ee34f97dff388c7aa4b7d6f1ecd824e1843dcda6411ad051b651a6fda8448edac28c4e66dcf0c607edb6a433007ad4843e6e67b5666f8de4c838feb45e4cfa4ef4b90f1e27909659075c3496fdd375cf9310e5550aa67b0294410bcd5342bd3bd2f4076358d318fc94d2ffaad1e04314f146a1f8b23d322c37cf653adddcca16a576f3897b786912fe6b976601dd4f634f19939ab59e977fb0d084fe1007d776b20c0aa60225c9856af05157e5838a1b767c215a0b9d6c9e7305e1c0c364c1c233867f31423c233bba271aa890c44fcc9a1dd1a9835daa3d41501ab7d0c543b36bc06d821e7fe17d0ec2ebe2d171c0be4e2f593b499669d4ac5986c0a20aef2e24574a933d30d88e43950b32b8b88876bbf8505701de84a34f75f27c47a826a8f68bcb61cb3aed97afee882afdbb9d723129965ef35536940b06bf317143db7614b29fcd424d4d1ee0be682212f58af56ca2afa565f82796fd462b894fb7b4f7c044e4f8d8ff5fa430d64ed287914a965ce0f9820d5ad7e160081908e65b9a06e48e4dbcd381ccf8ed6a0dc2c1c7be701275eb2a4611f5403ea9a48db5828a35d786cf07ca958e099b0c9403e8d5259e924985056e2188e4d0b758b0975ed396a87a6086b5c8f276eb2ce6d25134d69ee8d6a6337ed3366941412b3b11c3dca4061707b413225523d4e5cd6c2b0a9626209d0356e2e341886214a0dbe36cb60cddf9164f1e338f662c367cf50684105c5159cbaca7ed8e80efd03cf29a49ade1438095bc3408ec0d5a6fc07f1a7d25935282ef4a7785236112d8c95ad288490b3c1f76b6eaa8acdccbc902eea5f041e94ae8a96f6e389770a00631c4bb09f410e7f2e6b8abda6532e93bae66cbc690aecbd1c2c70131fc4549eccaa3a021cfebc5adf4efb3e2d9dc6f73673c44e11f50e3a0dc336cd4b3fdacae6efe64c2d715a42b21d8b50051ca0360e2cf861bd4507d08bf060ced288d2ce70bdc838c228ab049c0597b51e790a5a610cc637d6e8cc6b9e6dc37761eae4ed45c6e7740d845135483aa1ab68dcf37fcb032a9bb7e46967db97030466531dd543110d82b1789e1f4ec474965b84375c6926f585bb8f69ef498ddad83d5153689d36e843d81986f4cdd502600e0a5341cbab30d1f2343ef61cbe1253c001a13868c265199fb1f361c0fcbdcc9a8cacd62b1b01ba158f7664eef600647c0bc711f2e9e18c972c33243e85f211652abba016da861aafc22ee1eb9169dca8326d33f6e3ee76733de0ceeeef27dbaf6527545841304cbc22aa2ce5e87a81935ba51e409dd222ec3365eec4c93387557dea9b45dabc2ab9d5b52062b91a9017a2c91753de685f23c17cb897069c455f2f15411807f7aae60597a6118d0f5d70d04504b438f71f072ceb619d8a79264d909dc4c301f41930b539b314ccdfeaec4d837baf7d0fa5fdb046d2e39af8c534aaeca6c18538bf8f72f0211d4f0e9aec0cce4c6badd07cc548306ed9dac3937ea83a2eb972654c8f493e195a4f9e2ce5e1570e3d8f9db03a2ed2c02b4042927ece35ddc861a5f1d09d7f1c6ae34e34c98e9eda26b8e411528d05018ef8b821e9d86ae19727d79935ba611ab49e704e20e5dd7e9e8288ca8c1a5798d56ef951050a079937acada4aeaf3b353a6369d7b5b3cdd92c64364373f35c77992d67664fa699f51cf93778e2c4037ce1bb78eb2d8d547469e62b0badb807399df26e6e646e0991d587489ca8299a0bdbf8ae009e34634b54b6709712a7cc766a84ad02e551e907ac9541adcd74d37fa41e95e6622205bc22d538293e53df7eb5282c37107c231058f6ae35095923ece458be1427079fa8b4fd3e28ae4dda78c9e8e88ce11432937d200bfe26a7688babc42c4d1b079cfe7b36338641525da40987dd0f232d69665e4e093d2e1aab418ede0ed2743054859f265d92a9d4eb44f0a9fb6ad4aa1d141c8fc528ae21449d4998a3bc74a04241b224dfa55463ebc0565287c0e59ceffa84fa2e3ab425b837055971249e86b82854d1c3518dce420b1f79676b40369005d15e47967401d131e09051019c9f5ab04307464848baa309422b3467b0f14cbaee2d69c6f141a79b434b412909c05f0969a747e7f35bad5342b51b15d89d1cef4bc006569364c81cf0dc9414ec61abe4868f76c3a830e3edde400a76fc3a4a74ebc8d83e2c7a27cf7423b10947c8deefc717bb00f45be567897c0574a39f201f2355b97bdabc19a697572b233cb7c45c596f8c65f06018c40d1d63228f8a6cbf6636d3dcbe3ca227d77e2a5316211566aeb4e1b1068331ea7395d647509763587b19503be1cd31a766e0a52e002a306bd6f970aa9c3165243f1f93941d0530d1d969052ba5ce8150c3c6d839125499dd2a124362087c63d75176d40f26b0569a632f1cd7e456d487180d1744c680f6473f3997c2d9926a33c3468373131af1f398dd6c71fa468683cdfa6b1bc209fb57da903c980d516eb8d48ab4f56ac149426225f485869be5fc5f91ed4d848110096869ae9a75c1c5fb5f430ef46b41aae28cdefac4cd78dd30143687f36e076f78b221c43071dccb8060dd34b266720e68bb51789bbc2c965818da85735e24a7af759322a35291c8bd0a109b9c6dbcb27a2692bf6fd5e9592110e7770f34fd3469c431b139ff98977b6bbe130991cad5c9ec463172aa1ee329d692f4d6d1763ecc912d4bcef5c840c75ca5c1eeccf20fd64008c70240b81a1d76b0a29826466ede93148b2793fae035903082bdbc3aa7d7b34e70a11d49f79e14b1f225ce36b9c12e9eb1a2a7b0e1ef9f9e348dfbd4412318dde7a32e941a06540e9564738d752a0f82e841fa851d3972234dd45ec86617b2194b4674a404735c3b857d00a4a36a013a87b156d97416ad9e1acef6cd0d3473c218ecf3451e0e3052e4b58b2e90bd0dae83a90690452987257a6421481965f147cfa1ba2b93908dfc0ef643d0bc9e059c207b2650af013db97cbc077b0d485bfe378dd91fd76c684fc3bb1ad01bb89aca5458cd443f2acf31989ad59602e890f45808822e0ef214228dd3a593cea34d1837097b43b091005c780f8ba66af182b18851d923e96f93f8f2f9cae31b0ae392c062b6ac2aaee6c429f678728676b1998f2ce62a18fca4e8dba39ee1abfb8da0d1a5729507cbe1edff76a7fd9bec06b1cdf85486728405c3208c211b06c12bddb0985c9f8e32e7303b1a28797a128fecc81cd60241279c8085968ed3845f9785aa7f8172ad545139c412f0e3a0f24709717d430014f705be38a27c8a0caad117a48a196d8e60ad4d66f62778c5afd6417945285869e600e0334c014505e4fc6f3500d363526d29310960751f1e3feade778ba7b771dbfa05051458ac993abb3a8d3bcef8f84cd7c528567c3fbc06a32fe168d86f4bcf4ecfe366313639118b1dd4c133908487c9a354da435d47b35e78ec3c0283a43c454c6ab390a9c2d6b335362db04a1df758ffbaa69bd22887753effb0282e008c3fd789ea2a8039c69e780409cba6f651c3bae383697d3b90663555248637ac1141a5235687a2dbb2a85ed49e36f2a5259221240a5f0d1c1b54d696600093a1fc84ee203e6e8b1fede3d73975ee4463f58d1f275f257a934f7c310dd97b405d21356da0021a9fd1e29cd00eceaf597c2312adc969fda52b9cd69419d801521ddd4155338d2367bca06fc89d24a23daa597236ab939e502c8d61ad6ebdc768b0cf7665a1ea9d0c1f13ca34b2bf798087a7560316b0a838a01adca626b7d7d442f091d2773b3cbc1ad17bdc356f96c600771f318f5f0a9525add56b321bf9c7b73f620e15409d7eafaaa762b811a554f3c6cd018f58d0e4a5cb03f29938634f49f85423efa4d0833fa70f74df29e466f5239a2d974fec261e1fd6f62e22cfed859b3380d27f2b0e686c7906a3d1342596a944983c3fe25e836c9c7e062845fc8a75308ce4187f30765641e6f77e05c4810e929790f1ffe4f011afb6cbfad7522cdda15196b2adcb006531e8ef19448ee0af5c665d95c879dded8d6b6b592f6894b43042aa571da6d345b8f3a1480160891cf1f340f1c47db0e41d37c5a6cd284ac205453ac2578f5d4239828fc26b17a9c75ff62e21dd3159e6d189e66a9de1fa39fa44ae9dad81e3020c053b18f948e6c059a244824ce8dd0f5d1a3aa5d16c81a5ecc13f65175edcfde882a4eabd63758c5f84cd39032ffc9512855bf634226680d4af30063efc5c1f7f9b840e930b65b45aa689a71f6806a8f8949d13a24c27a107144a24cd65f802b50d434d71f5d0960eaf0b7350af6c1ae7fe8c6d92fc8cb92f58a8e99dd1c4ba0227e11b0c9bccf94f3581a3e3d69f2be126d637aba8c2f107a287e1e78e75fe6346e9eb7de799fafe35445c3b7bd61e260a71bba6ae1394ec066cfcd6420a2ac1eb7daf585d1c67133373e5f9273ff4c906af0797b1ef69034ed8999a388562926ab3da12c250f0b859ab20c06d5eae74f563835fde0686cd47c22f0d12eb411285b1616d1d26022147d5a3544a84c868e5acffd40d9f0189d18f5eaac2de35012d053ffdb48df0421dd6123b7694a38465a55b02e34c98571da61800206b80213df7011000184e28794bcdab0b226d4aed6791babcc48ea3c4192ab2034da4634ad71c0727be91c9b4ebc2e67ada27eb697d8924ed78306aa21256cff99700559e1f5e6872a1d41f1a0689c82cf2cdaddab29fca015982bb982b1b77618bb1ebad5d75606a90ebd2a3cf75770a73475e5cbfcceb7758cc701625fb98f5b1e4fd38f9c2f9a4f7839a9618440b0a490fdd9aebfdb99f3b1890e0b895c9289a0dca1ff299449ebd41d7a46b4b40c6ec28f477ba82039078d5c573a07f805701e60751f51a59150b55a6afb601febb952f9add88c134099f04c49518a5e09c2458f91b754061cd6f1d9c0beea17f530a58edb9f57dfe2f4ea690c30a9934e5dfc2826062f2a701e74e0fc707cca27dfad07d11c17b44645beada2956f73d8da206aab868f5fea250a3a7286b3512754cd4a22eea2ceab5bd44417faf28e7022e9a2dd65d0699de13262fa2bdaea2d8d550d493f14503445cb47a7f518cedd02235dbe31d392915ca1f3e3c3361827f5582faa0232e563dadd926a87e0a8bc0773ef428a5c9a2d1949f3fe688baa0afac6763a71e311a237f25bfbef0135625f04e5dc9234fcb653cbfd0cc6b432d4e397a7205c614407068b077680b112d84ed390ffe01abcb8b8d56a92ba56f0f3850ff852702955e674b26f7a9fafe79189b84e26ae611785a82b60d6c588de54950191902c5ab94edc31cdfd1ca4683504ea7f1c06ad855460ed00c0049d0775102cb8da76a23cad14a3da899d1069e420af8f93337a0886cb69a9e93b7e0fe1b8c25c2809984b48ac4af8e138be917ea7f1b88330cb0ebb22d93165fb444f042b8ba666d86f7c850c68ccb23e9d02c54b6653762938795e8384519590513dc2775ae0f8817b64f0d5f846411cbd58e415ad054e75abd13d30b9d230fbc037da77332e19c2f893cfc374bbbabde73af1099cab26066dcdb3373d12d0134d4fba5a10b2656166ef9a93c9d5d1c72a039d1320276c3af85edf1ce14517b78327d97575d236409cacbbc259143bc5e6977934a801b96199a72f91fbf323d170f29b89a48c970252bc909a0b9d8a1945b1c346f38e6754a425efa0162cde7940f93e43251e9d4138e899aff6833abed2992fb5afcb30daba8892721c3920ba5bfac3dd7be430e5cf390435b77f6ff393a7cf531999c3da6867a364ec072719b5f5b0b3020db6cb1f0b2fb2471deb7cdc53e396e41f6ac55ecf9f4221ab04a668e50318c1fe3ac7bac53176c9d3228e63899162cfd12bdc7115e0dd946ba7c4422cd6858650ae30bdbb4c8c4e82bc06eddc47a28bf98a2e69dd5e49e3b9fe197760f6beafaa3cf446e4ce9650e25e3ebb6397113aafb2641a23abd7d01a42c8c3f6644a7ff1f4546941c60b9cc5f9d0e49e8e95bc61b7076d819cd7447a0ee3d761adc628bb44f9c001269339052a78fea3d8e59f24ce1cae6c46f6ca4b54f5c7d420cb889179534062024dbcfb2e6c32b392061641e92ab033199026162cfd152c469d6b3d3221156b6baff91da4080974398ccb49283fd642e19ac0174a47d47e6558b46ebf3dfc5f588b6aaaf34d773b8f998502802b452114cd4517ca1a1b58af7f96c879e8bbad42afca2d45320e7fa862702cb255c0b035c157959820388e4c3180dd4309d0bc8925a6616900841708d96ab958ddf7089eb1490ec11c84559e9916bcb75860809b8765eb20c33936c5f13711d6baa37d89096852559fa036f309d38ab659beec32472eb052d696d27928dcc852153002bb7d02c2dc561e303a2228dcba888a89a3fa3faaa3fa759484a0d7bf4291c11f697a54b22edfba63d63a240fb56c914617f14bf76f97c6c0d241ddef82c6f4609218fc3d2cf780b5af2b0fabda7c88bcab43250fac8fd68f8cce78b3e6973057137f9de2bf3cbdb7fe686bcdd309dc77d237fa74d95311ce23c206d3745fb271f17c32c8796669c1c30e1f4351de3502756bb0ee5eb7663fc27c77212b349d44b90f13d7b81ee088e093aef84c41f283c9a432ba5f38bf20fadfd72140a831fd098811c71c18de419e9a22d35e38a73aae52a9fd21ff7e052e9edb2e5985951ecb5a45de73b0a5fd3532a00254ac7e9e3798162b64b84837ef0e6be28cecc2825716158a1210c3f3675e81649e2fb7b9f6af39b42d3255bf0a14a17d4d439c6abaf662883da564c945cc5e058489fda17fa54d928f70907150b2e0cc31c542fe846042126185c81403acf4d938d83bdf955dcef179d157d4ccfe604e467e353f8ff3d49bc5ef4e350a7e8f41a166459336c117b9b896482a1f9074c64c936c21f1b16a226f6ad77f31ad40f1fce5920937b863571accf0b0331c70cc6f9c3edc9ea09e73a0bdb39ea7b17bbff6082dec17041c474fba2a82797d778090aeb4cdad8c12d45f3ef21f3f4a6b8d853c9fc50fc3c1c3540f22b877f19cf2b07e36fe4ce0e11b3d13c24180979a352653767b8f557ad6f8c753ccd4cfc392b87bb03385fddf169c1bf2c751d7f0bf8f9be1a1a1f896a10738238a8c1cde8f5893a28f1c71417b6eef0537cfa636b9980d71bbba7321bda6656fb57de4dd0f12415ef9826c50ad716eebfdbae49228fd00b5fe63fdbc80e1ffb2e0669763a0a31c5370975f926abf6580dc9ffe619a0a771ea8d3b7b5f64188b44fa7c1caceff8aa1349ea5f922a8df4eb43b8c5fc262cb04818f349ac212f50b4d3f2ce617e3854ce6af7135bf5c81cc623f80022f9a07a4228f433919d9616e1ef02012516d48496b33106dc0cb009e3e23f541ba9457a3352aacb81dddcf0aa8cad7d313ab6858f5d2fc5812e42781e5280fa07bb0664446bc3c76feb8d31e06680f5a9a0562852e2ab610bb40efc9edcc4ab017d3df7d2d66e164452d4141835f46811c576eb00eef90b053d1a213f64572d541e965751c314e9ab36d55425fea7327ca16c5241ee25e87d90fa625ef5c897b729cc2cc5c7ab480d747591c6d946e72661d1f67516afb816bb9bf351e27e7d070b62eeb0bed9454216864101a1b12e53277c17359be8c63dac04fc07301b657d9bba88bcc0be8d5f2adabbc45c2b41559ddd4b3502d9105114113a9930117ba890900afccca891dda1e7368799b95ac3dc77012394356d25ca2662ecc78f01c5a9d7911c70f10f4ce4bf77e752761b4d87f9ae7d60e0dca69c5d864b354e88fe3f6d9844f70b8d8d29dd766a703db69b09ec01c904b83e2c88b18ad238f51b66cd3ab29a5267016d49418562ad865c718ed0fbb9db3085613ec23451f250eb28db7674d78ab660527ab822ce092772048bc68ca5a86e3353fa0f74ccee949eb297c1cb2c7649eae827fe3470bec168130120fef1b84d82676160b411084ebab2fd6b1fe96a51f68c950ebe20fb421c5150ec24a0fd26b6798af33dc305f06c4b31a168224051f44fe1443397faf79cd44d1b6a1ac5e5d2c6e1bcd6297263fc0ba3ba07073d877cf2280f76b0d618fa41b287e80e83c94ff406bdfd8df3bcbbe157447004553018b6dc7d3c2ea02adcf2586961993d77d5304fb2844143fcfc8d010893a530008b672c3852a14399dd1e7c798e84cccda61a1147a062e50d3af918db1ac203160654646658741c07f3a175c42fae12bdf85ce3b95ba6bbd88ab12cdcb8da4ac0ca59745883235fb477d5b78c5c2672309aa8ac3274a54bdd75e15cb4b9d2e84fc78f2484ebf9e9a25e4b99dd459dee6b5171f45558c9ad5ba9c361c24f6b31ecfad04c6e9644e5340612f1a9f3fd6772ecbed23e97e4f0de99aba83fe8fbb75102a07dea708f36c49e67a5dc07e7543538cd5fce7da5d6d6fce95059c67216810df34bf06d9bb046afc581f171ecc83d7c5acd63d054c6f0c2020aeef8e0cd857042e03f1675480a126984244d8a8ad22997ae1d2181332fdb6f50044afea67add110c2865c004c56b4cf51d0c9938eebcfa2647c986846301f3f67595d41c574bb4e614daefcfca03cef4c729aad64f921d074a76b8c12002af65df1f4f1ca52f57da33f619558b0316e5ffd22d7247368537e4c05217150e0a5fcfc4596f3dd5ee879ce9e080cd5061819a04bc929e508658db256cff99c7a88032404b45d9cc849952830467c6d5a0f78e375ded639ace1847c61599bec927fa58aa0a953d00c7e817417f8323a7f7fa81b0f1a9e425b05960b1730fc0d85bcbbee80c8fa8e1f658ca63af4a46f8d066e62b73bcb8c036582af50805f6deb658816718ea22b0fa614daa0d59c2b3df01289b5d2af46c0a64345dc7d9fe9822a28ade4291c485b0804e660f0eea275282cfbb476de71b182fec6f85cda6c5a3aaca3b808cb3afd36d01cee5b540ad80f4f4088d3130d7582262cf352136df591669d5faadc1fd0208fc554bd553b4e9ea295b080a8121d8be18595c2aa13abd805cbb8a904d1af6a00dc717bc45a0a72f88980250895f70b4d9e60c5542c372b65376cecdd66b8301606edbd29bc959e606b13d860f760ada227b82f0b9c5ad7261c860c5f564e0559cd6c3229a88c1b96290165dec6e245c706c4cd216d7e62e0777f2b462b52f647a3beeee4980049f12d979003e85d6c4664df73feac5e89de8e7a4122ccff6e58d6d5fd0b2f62e73f8d3833db9914154ec16880431f06506121e126e7472f7f61521a356cd88b765c9b652cfe1e5910c1460cd62c48a12e889e2398021863383b081340d901d49159f77c6acd6a6cc05ba4516ba2f2a18544d5bbae4bc2e7e623350195199b594b185d8a43355e7df216fa48205d088f94125b65c2149be3df9978b1e768ebd6f80d1229893d5726e1f874423fb9ed184184d7c74615bdbb19a786454767a5d860fffb28278742578fce0de6a6f47085e5f8bd5986e81cd94c9ce9298bb58ff50cd22467b6575397fb042a25c5238acc4a4f883c0549152deda938c295de3957db3ae4abe44c778aa82d7aaf933b47417592badd298b9d4f0bdc971385ff08a44c597e85812c816d32c7ab00d44aa57b86eb307f2f2d8824aafcabf303ec62dbc922a146b61170a9bc0ae7263d7f21a6bb3d8f46b8f96d1710378840ae0fdc4450d3a1862834bdc32822ec51047d4e31108bc7aceecb758e151320454df8d044ea56dd8157cecea47e0bc797eec781079d1927ed2f2843c08fc2eab27fdbf23fd101952c4505fdde57067e4a94d0a58ffa02245841996313f8a1219c683b5b4f839c28805cd1443a00081052176788d182e3a581d865df3717e04c2eea583bedb28e29ba1bb617a59c27ee2f9de884f73ff6dfffd865ffb5ee04b3cb08da790e0840029fabbefb43f1acbe06001600b3a115263a4497fc20972f78a6f62c58e96fa8c8eae44b017d69a8761141ed63d91c45c93e37cf94c20c210efbb2ef467b7dec62d529c12141273ef746c8177c6fc00d4ddeb60eeb25a744d01b8070436413726bf266ffc34b1256a99ba1f6ad514adcf69ab3068e0f6c659a36b42d17562e55f9190dcfc01e88f06aea6e30785fbc0555bfa08268d94a545550bf49438cb6535f20c13023c9b324aa28ef7d50480b63a8771becc59abf7bf4c7d6b36914caf49192d245d9d5ed11b9283be7d0ec981be08496bf32151bddc2bdba9d1c40e410e5d42c47eb1d51a45dadb739d09b9c36fcd36a25e13b85d80a236a04d56a043147b3872efef9a4de468af49591a0938e6e1ea66cf20e6e3721e2f4493ba05b0b28a7885fbe1053cb3d24c5aa72c6528ba51e3a033382b702254637d83e02ca12d49d31f55e352adc409e666065135cbb7153b4f6ff5dadf29d75bdff36965c11618c10db1087801a0ce1cb452774c0f39d9c841357d9b868c4f28c7ac48d1a6885780faf98b37dc3499026141984707de8190935b4b4b05868174969c70a35a225d8edba5f4d66f2c68a696e07d7f0211ae42d97e9b5d6c393f6054a8e69e9d16bc20276f2818300f5764d9cf29724bcef62c7c0e88536e899c9ded6b4cc7616ec999dd7a37307289075ee8f0a472c0924b3e1b7687ff25263c482e2133d18bd3d6d11df9e5ccb1e818370508f7be35da2e2f9117c9cf1afa9590ff95932c4f8e12a144869ce34000acfb737abe7acf59adddb242ecc4800ebabb505231e52cfa255555750d14dbdfb62081b3392281871f5c14d572c47e696470033708887de8333e85b03df929e5a0484851cbc4bb84fc20202333a1d084f2599c6478d67073eabed0d4e21413c5aec246fd683218f403855ba5494f1d2305a4a9f508d0eea8a4277ffb590c64ec9b192976133478abccb3fd119e6a76d5b9e36e8b87434a973e14b705bcb3b4c13d267f51199f00b9309d23a410501f705c5c1b049a2521e8907d91f2d161a99b90a36f96863c0c9cb51165a862046031620f782cab6152559168d0d84092534e3e6e856a3d249a7c40584abed906e3a415381bb94e41962bc9451558ceae7ea20fe1af12399b17f180d430875151b85f37cc9b9d1b9670f02ce2fe18d3580a4c89a6581c26df960926166c9018559f9034082121beb07922c1fe5f08c90c7e44aa504535407b9cdb6257165262e748b4251c8ca2b222c6c020313a3716a4e8287d4f06ef9dd7f8f207748293c2b4d56546dac8897c1a894dfd4c1da31e025351f8a652590b873846502b88fd014e2ef4d73e8e066349d7d68f6e5ca981bc79c120b62ee2da1fe993d340afc0f53552a26e952c25f511019724af90ebcdcfc16383677309104609fd37b4f38188ef10058f24f9d6ef28781e7b7f700179c32f16ad141717a7502b12bbc25572d5008853c7bebe2aa7cca447ef9bfd28093906f5b86bd32c13388ae1fcc21457452ae2f112d39e08527dd88173d891f4e0d7008fd2200818d62f71378a548fc1cf8e2d5168f1df0fc9e147c9c703896d4f4c4a1d485c7b4248a90346aced107a40aa54d76e35cd702948971a9e13e9b00abe593d057a4dc1f3033ab9a6685657815c23c5a51eca25d56c7bafd468549e991434f20e9a5951bb0fd14d47ae8c68ada8d761174bd7eeb14d546ce93eaf84b24307de5ed29735101e9fe8e5965f3fbd17cc7f895f2114966ec4a9434da8ba93701050f483851ee05bf57423ebe9eb3e7790b2b87d9c73d9605b3f22c12033e1f7106de9c60dd3b406f04c31b25de06f54ed42fc8dca4632bf9e8a0bb0a19fcfee3b2e10c38b0c61c898a68f6eee892dceb94aca32df9dfc49011f99574e45780497d3c441fbcf612fcedd5dd2d36368c1339818088fab8089c3acef97bafc45c802b868e0cddc4d0280fe0ce216a77e81ea18803dcbf2b9e45fb23d9c8f7ac61c13b2faca5fb8c7dcc44080bd3f11f256988ea72e646fb187acaa835a96e9426ce3defa3ceff8e18bb0722a81c3d0bacaaff4d5f87eaf2c9a370ff5b70f626d49d7e69b6600fa4cbd0841a95ace8cbb734bb1e996245e9da4fad2cf2029e359a24147c8227b47e869cbe7ec0cf43ee2a8de8f3842751707f5c3679a100ba866e0aa5396a3ffc293d82bebc2c2826df7d88582b23503a62d4361f483f7d1abec09fe5031aa75e857b79c31a61cf27d3eec387cfa336c9dbc75498cc9ddab6797300110b89c7a31b8d0012fc0ee14d813491f0089bb9aefe96c816bc241f4e7dfb1138e8947c96e88d420bb984c273d1c9a559fef39fb36d96d93c81df867e0dc126f8a4318c9a293f1280c422fb18936a9e4ee0402226405ec895cffce8d2d0409b73e1edca7fd8e118128cf72318a2927de71a477f5343cec1834fe91f6920e3d45c8d238026b78c242c382e0d7ad914041d69d2d9c5eabe978eea3472a549d568b7ad994aafcef1b88d4cfdb31e78162ae2e123b817f60aeafd39b694a355865e0825d6d4b50c6cab954f24209dde3691d2ecba45988aadc1e3dce338fce751e968f2597f95f35e4daed6d8f98f601fe037cf14e618f153107165901afab610bfd76c6339779ab2c5eceb8618d192f18c62e60ddb73649cb181a46bd0eecd5cecfb5a5804bef79c311eda4f7e0bcbb5a5701d8299c40be21988f317f988858d42019716274d7a475a7700251c7da6c989ad63dac284d5f2543066a8daab87ae4e24196a2d8a63ecc4cb1d6cac748c7f9602bb88a867c862196c524999fa120b2b826b5884cba251dc4d1c4249d24647322c4a55d8a0154005db032b15d24d63005a1c45ae1f3ab226deff795fbcb7a10d91edbd7b5cc24bb138ab9951f8c1ab66845fa58e2ccef253a54ea8a748e188fac90417d6e66a248d38a5650ae6c1cd3c4d08184401c831190ce3f78f2127ad4babb86f747a17beb3619eb985c90898e32a802e35efbe9ebdc05710ba74be152d8da5689f2ee7274cfa4ab8c5aae812e4124706814b44820f6580ac825c1c3baebaa928753ae8802523e8820158e125a09b9932aa1521d98738f0f617d9d6c7d401cc619ca852cbfe8fb6d7fcf5824e6e67f2047289f139220898e282905b5ed263223979de7b3e46883674f4877318cc922b1e49a92c7e9cb09e05b4ba9fa38c4051c5b0f668224f982882f1bba102ffd4ba15cf0b6b8bc94b73d96724f0ae4a7cd9dd8e669a31462b1cde9a252390cd7a139c7f54599bd9fc36869d20449b9786f2ae4f34ed5bcdb012b0179f7974c7975141013ffc9312a6abc4cfadb777b43a9b365ffd130cd5884df16beef330e44b6d7f877b6ccf92c0989b8ca42521cc2e1aa8b82910706d99d1cb22f1b95e955cd73462ebc2a708d87b208786ba8469f179bd8be6a63536cf773220411138c32dd48e7f9e49e824db974459bdc909e688fe6a3044ca2462145394fc681e7417f1a9fa55bc1b0627b858969b54f21af424f8574b48142f28f7d1a08fe58da523854ce182c358215126dcbfa3ad639465ff66568c3a42c2393b07f74902949d5a392f82d965968acb5109660d04b8101846de7d2ec2666f1e6b259a59580d9d56a05de2af525f77303fc6079306a1a4e8c04869e0c47a3758ee9f2559c61ee703aa095f7862615303df19ac822303b64db11601b32933c47b16071b2bf2dc45e0d2de1f61c128ab7320fc462def4acc6283426f50cc8c6e4f6de145703d500289c625c206943714143f42bd8b0cf3494a478b9dec60f31e2ce937df8d06b363d4f8964ccb93fd959bc472c6a11786acd2e820c8f5610d66bced6668cc1aea3244871e277e564120e80a3530fc459d3dd4b57196c48ad0d4ab65e23bd99c27bbc410fbeb3f5fe3018543ba1be6ab138ac25ce38a81fa98392fa1340350ff6113f513221011ccd9587009d952b91c433f2d3ceeb5f03ddf30b879847c91231fa1b6d36df6eb224a641cf826c17419a355ed72bdfd658ed43151e7eb5137a72ad970f8f5eb6dd7b9c4bee6fa2c3b59098a047929eff3fb4ca3ecf3199eb1724ea501d870c6e6c70d3c61a1bdcf46d646960370ba142fea5abe2b94021ef48c4e47474d7d641647e5bbe6201d3a2dea692971d5c55224fc85f6ebfe7389e9fff21c13213fcd984f660ae113f6eeafc62396e33c45262e27c04873ed8fe1a814930573490ebe359ea9670752cbc0a7e0bcabad382286fcea65be03cede99665c8b203c34d854f20226ea5ffabc1a383b6ea90bc2bf7c0f18dd8dfcc7c0495521e4f6819c95c2aa70c26f5ce42192b10d70099c958230f2fc9a406f860ac3b823f629f360c506a112846597746adf8a4af269784d9807e08ab4046a34bbb71fa973c01ea0daf5c70b22a16412e56ee07f2231da6dd118150be21be1a7309bc00b2c97418e9389f5aa1ea0443350a0e32838308e1b0bf5855244c76401a2e8ddc8ac47fa7676dee5e2d0105e0e271c7b69b8ea406e4b21ec2971e0d8398d78e98579616c57cc6590a17243e67543e1cc4e61562c9440a0819b80382c8886351369551ba5b2833634c4c0ecf42ca3e02102f6be79ff4d12200c55028f4b447a085af6bc4cab5f1987f43fa150ea7a3516f05b95a4e718f048bea7c13668e88552fb9775e4237227cc55325e5cb9e4c5f6f1afd72f4e0e976008fad620907999501b4f4448f0a26efe19bbaf1781489019b0e89619622fd2b3aa543c687836f0a8f471315a38b4df176e99276e125672ee6df35933e08d49f8c2b55f61d14657d53a54f262467cf6cf7e7178cb71877b3f40be96464599575e8a8cf6d4d8e7adb038a7174c2f8eb25be115922f4bed3886fd349b2f0f9946f6dfd1f81607d7129972477001878e8d6c0fe5a469a6894f00607e344fea53aca44e61da7da27f24d58f89c40aa8487c80e5607d7323e5ba6b409f8074b7c86c1358707f08bd1413f3495519444eed89bdbafce794a43448cd8ae97f1acb6454dd4749b3ec1612058c86802271681af9169f73d01f7ae001efce11b99ac550db45d7b34ece0a17399ca2723833001b8950707511b0a7492c237b94544142588c1b2b2abcd5334bac46ebc2e70693ec7f8d58dd5fcdfcee96acb1348033f2698fb90eb71692d71c127d8bd788bc9417cf17aa7b1732f1a04a7c47829e36baaf79c907b2f90270d22074688fc271e8feac17c0fd00556ed4dcf683eb427845232e6470d9057852cc26cbaf32671785547d93961394dc5ab3d6c55eaa5301a061454d1d3fd40eeda9a2aea6765c44bdbc2f410b255e08887cd6ff519662906a6f5c382116bf989964364868c2498bae45718ca5f655fe8cf8f08990f8468b3b389cd4af28926f52dd8747e06f5f12a4e1a73eea871d634f0c607ca9e677ca7257dee1b5ecb46bf3af6cde1231f64abc223c6e6d5d86263fd280e1b79cd256e6c764ea686fb8f1dca2eaa2907726c7e71beb31b0482659af68e32b365c94ec022087ead0035e1eba95d50ac003637c7859da5ef370d9c3fc2c2a2a1fb7cfe372218047e0c7dc5fdf26d7e9accd2cfd334c44d2def8981e34e7536f927c6ec7e380e532d5f10a6cf5109348403064ddd00773946c805e6e87161fa0faa5d368942a165527321cc4f69c7ddb0ebb2b6a578147cb6896ff3b34ba94da6fe0e72110c0c7d56b6d9f84ba1e45623c37ed0782c3bec056daf7f67ae8146c6bb61156d5b9415f485abc676cb79540ea3a87360e1743a7d4dee7241da04376af536fb3164ce3e1dce9202b56cc3552d555ce9dcc9d5bc287a954f1ccc65b3d9e8dc951280ce95afb8bff120ffccc7e13f9f7ceb3f6e88a52f523ebc8cb13016156270f3361c3bbe8df617e9d8bf83b262c54d42770ff032cb3c55d6641207dd95a5e7c21539131f0a8a1fe89c890115794b561f56abca77f8de7b0fa751a742fa8afa9156c42c240f1e80884071497feac6e564281688e7437f6d9fd0094dce765529128a0b01dfc415fc757426b7bcb88328285ba689344ed9c22d9fbce078b21aff4182e3bef5dc2c58a0dc0cc37254b2f4fe8b18859ef4f2f1d5c4948a07c0e9fc0991cb242795233e5e907b57571d61521d2402d5034e8f2cf25cf51148e3c19991650673f0c69c797c8a1e022406c4a78a181caa3547338195553c9869a8a44fa612014aaf9a41e78042574f6aae96e675d433ee9962e767214e0ea89564218def113df2256f3c01e3682643b7a1af75ef176ef9b7a41f8ce8766a0820cf4dbbb76295e85bfaac3495c64cd905cb5a23c28ad9d2945605372a1f347a4b789ac80b5c0c9393ce21bbec3522c5f24cb470fb6c4f199db0af83fe4cd2bd028833320c49e116c6bf743492e48f7635b6ff3d84505eb5002c85b4f872de22d3d4c519a862fb3fedcd4d61d32f2fb26de423e914af0b922b45cd0d6fc72d1aa6169274cf4fb90b954268b7637987cc2e728ff425886969bd7918a00a1b0a5fc6754a0ff99ab6e1e411e85da2ea362ae5a571f45d5868e3b03b951dd8eb38e109db608d176841d4e9a72534dd824e1149ec5b766727d2af12f70f5b718b3676a4b2b16f997ddc32ea671bc5e1c8f709910a5e01bc356cf224a64b1670f9dea094e41db9cfc58b495b3af2a5d40c0a96cce12ddb6f8d17ef00f07196c24c3efd4955ac1ab750400afb07bce102fa262a705d30145a4d079a8c88e83637831b07f3672b657b24443126477cb9b98fa65ae68766230b77e56851166564904700066a7cba6e8ec1afefeeeb104dd3d87140481344141606b8a05665701eed55d061d370586b216d6fb9a5dc524a29650a4707b4061407f22b3884fd39e90b32152d38c35dc18c97a90732af5cd9f68911937c99e892dcac02444cb476ec21be5c41d148eb67871168472660f1e9d4896e65c78f3ec30b5267c61df952b64f418320f89114d2210ecf0a423acc113d22a81870220e8d2e70ae4e882a42491d0900218aecb4351f7346abc994ab2e2a0072a2dc8689c661226513f944429192f28833294e121d7a114e90102c262ba23c77e33813dc45551c22706a777ff6fc5cebab404a54fc62c66309344c308180aecfae312b56a3def6e092a91ab8567aeb62bba2cfc6245f492b007ceeebd22e11f9da7eda8fc155f167b4da666998ab1cae8a2f43c56834ec84c78eecc7420e148e4ec538947e49116556a44e28891c499d30d6680aa796a376dda3cc1dc86de64759dc916044e2377303e32b26b9c19e60ad8baf1045434887f2b5d5a40e9d39f6ec66ac4087f2e54929254e84c9d7fc1985647cc940bbba902b4de5ed41ae5257c89edbd19e97c88cd45e9739d2bd7124a728d20042eadc1973b0c08ac99f11a4432f7a4d532f85b64c1a4634ceab394d695021c56ef5ef96830596c7ea0c22dcc8315716fc1cfb4874e8452fd3f5a750110b3e2ab524188d469b006151542445105c94a2942c45d0733bd11239c0b440cbed444bb480b0c377726587291b414ce00a811059e420ca0e7688591039f9e267a4ec762227473bfc0dcb32552e1247385e502309aed55062050717354db4bf6f33cae4818ca848153edcc0071d8cc462b119ad5612435c232e889c0811c176e0e3840870078830c490104d781091506d47f08679237f3be8f839bf0f5abefc98fd09e0bbbeade00f20fe61c4eff7dbc07d1c82ecb88f3f0bdc70cbf7ded721c88e0beac07d064120fef1ad05b90bee705f078cef7dec3a5cbfaf834f950ed7b77b13f54d7a844c3fdf48bfcaa694d2b7d17a068816f45183063a74188fb6d47acd0aeaf7fafd5c9a0518f5ebe7fa180cef9db57e4f095c68fa52f666639d4bd8d294749b89bb6443e29420dd26477a71902ec89d71d8e1fd04041941900684008226888062073bdc55f9ef1776e17dce74b7e9aba1db1a4c7124d01d2967efa39eca9f27b507c815d8d73d95b9f78cc3cf00c1bf2956211819c59f331e08b22de90c3923e3ce80f606672d6e4d763157a353792952975ff92703dd2c948372916c647b95e5a05c241bd9f16505a78f9ef967cb4aa7e94473b7197abf7e66f17aea9728b5f6292d792f04d91beba327afa7ea39e9bd2468a1c5aded180335130343660645450c125e7420c34507ffb16a2e1f4965e72155e68d5f6547d7d1deb4d26295e94aab32dd6194cd3e994867c15b8b26576b34a6199a46e94db2c5f4a8ad45d38aa64c31b3bfc92953d066534ca1cdee1417ac45f6abe9aa945a94e58c7147d971fb4eaaaa940d5699dbc46dbff0ee57d508800d1b01588aa255a99f03e0f5d4af11e3d472957d7c505f65463ad412277623fb1461fba579519ded2ce387d92773df97830e2b6d479971c89c489dee1bd926716cd1c5cf89d277fa9c999d6ef9d8bd10246edbd295a0d05d890a1d76fbd2296b0fa9474d9f73fa9c734ea794bafff6f22375af9452bacd3969add3b70d0cfdedf449a9bbd30a527ac5cc7741e937a508f4e88476266a506acd0109b63fc92e35a6b1882a370241e3b8a216e50bdaf6285f146dff6e7b942fa06c8ff245936db747f90289cd6d8ff245921d2110e58b56142fac6c8fe285d1de1ec58b29b647f102ca0e511ec50b27dbb43d8a174148c8664ff27311e5a4d56e1773b9f348a5f8e16048369d4c272640272fc6786219ca376059416cbb4cb57894d21617292e2f2e2f46198917981718fbf280605030a8129044808a41c5582156f08118183130962059d2721918325296c41cb64468c912dd755d2733b3e4e846961c944aa51824b82e1324c82041861151fc641bdc99edcf8406fdd497cb78196f0413239e50972916161696ff8c38a22d2f171717970f3cc2957d8081818101696e4c4c4c0ccd8ccc858c8c8ccc0c1a2f8a81a0448a5d03801a1698a6810c193264d4a4822e9d414a9552fd405b231a3f25476c97291a1a1a9a1a01c8403468d0a011001bd369b454242ae9c146003602087a05f57097b88ea8bd006e0470e332828a27841b9c1b1c2a445b817303e7c6e682d94e9404ca0d1c377004d5b610d021947e40e3c8c19173684e093939d78acc65eae6e6e6a684128c70dd1b376edc18c0009648b9317bb1cab15a61216b04921316394cc891036f276222c4526402cb0413ba289d76728df86162448c12ac1d9607245e899fbf7bd56d3eea36481041e207294c8e98d0a8d8f2bbb86f0b764ed8d959429bdb8996185121df22c409289c70c2931d764ec4a4042f6b4441b03dc2a6c420caedb0294a6c8f28a0e0f554b7911f3fea36414cb6ed444834718414ae4911124a9038120bbadb898e00bad559aea3dbd9d9d9d1a1e3450828a080020a29a49082d7e39e827f0a29783d3ecac3f35db7213ae2b5b1b3aebf3094715881a38ad98cee1d93da6052277b07951d2af840851526e5019a3c409407a887478c403c26550168aa00445500eac1021d810dacb08226b155061feef5f0e0b6bd11e436d4468e115de381a739cb7efe9916a592675a7b3eb511b311dbb3c6037bfa6087b18a3dbfe6cd7d54d6c75fdc97dbb97a94247adab97ed25af9f4ab301c332e88ec33429bd9b65baec255f63dd3b65aaaa844d2315aa9a2b9ca339b96b37c896d4b60989ad1f86cfb34adb932c9402aae63828134b036572e6f695c4c5439a2cde62a1f1dc15814ca249bab18ada982d9b76f62c275b2159eb224b0757708f38ad1cab36c450cd706e699dbd8e7915f90f65dedc768996047dbda9f6939eb641f8cc1fc6cfb2148dbf63555b3271ccab63299b21f8243db82b26dc1a26d33cc4b2687f61350f6fc10e6c7656e608d95a8d0d2be9e9984a206b6fd7cc4d148a1e37629342e49a1596e8914a345fae2de481fb7b76dfb68db3ff54a60eb4ba24397f5207df44aa0f7e1663f6f5f85edec7d71e7166d7b1fb7ad11fb7296fbceade94dafa7f4a4e79173db487303c3d875b5966777da5f121dc690711fbd1e20cc6dec038957f6294710e6acc86dfbb9144388d4b11fa3f5d19c95b77d10567ae9f59474696b1e27ef3b6dfde58935f73e60903a2c6fbf0a398b7bfbd6c759a7b79f4f9fe9b92f6f968fca94feaca5d7a39ffbb87dfae2ced3a32499befb1c232679de3695bcd269e7e9f500e171da1308b2f5f715a14b3f3f7a3e8494beca76e963cc6d4aa08c0ba4dfbe44fa6d9e3697c1d3766f1372d669dbc781c5e22e49fad2cbd325fd491f4797a40b56d806dacc5a64506b3b9790689b8be03a15e85aceb27bfb7ce4aceeb7ef5c73359f52c93fdbe6e32cefb7df80d8f26f6f5f9084ef582656c859f7b7cf69a2ad90fccd752ee0a9edab93ede5d7589d94e62cfc1107516cefe533a01a4530b42cd8dbd3e83b9649bcd9ee4b59db3fef7dd213234db64fd9ce44173cd850acd4aed268a4d0272018d861901d83bb4d9512a434ebf188bb3ea9c2dca6fa10427f7e05a40e4aa6b6dffe02ae23e3a9ad03bab2b730e2606fbfb7ed2d93b87343bcd97edb2c93bd7d9e5f61fe64cbdf36a0bd3dfe288dfeb0438f62cbedbd2f0606b88dfc1be46f5cecaddb9d0ce684e8edf1b69d5a7bdb3609a44f3e780a3a032120f8f8ecf084ed2f3329c542da7f66bb4730b4b13d469739d8cf381e9b725bf9b67bfb11887b8bdf5e6db3f7e18f9ff7751fbffc99c61c8790ef7dbe237ebea3fb689812981588ade89a2a1e33360d32652538c354d9e7913252e033a7f681b3d9ff7ca03c1b8fa56ead76957debdb22d1416cab8b6cfb38b12444ad21c36aa041a6ec4bafa774024d3fd2cf063a83d4a96fdf0172158984dd0e7db6ad8f4cd9af21840e71b68d2d5a93123fc9d274fa7cb3f47a6cbf43dadbe1e73ecc71bf398ec31cae25ee2f7703ddd9c8ce8f89aee861cb8e87851572ec13a4ce0ffcf939fc3da5207d697d1efdf787fefcf5b395c0f6fef4e170fa8b83e9f554d5c73e3e1468ee6516204d320b88183855aa9f67f2481ec97df7f527e302f7a304badbd0e7f6be2173ef32dcd3cddd2ac361289a850baea084b6cf33bbda495b0383367dbfdbd75b6bddfe6e1b4a0b1a29cf54d1fc42ccd3bf34c618b7fa77bbf7e3ad5b84c9146d2253d4242b424730a5848e015f90292a34573e8436cd31c61823aedbf615a7e0aceee9f3e41fdbdb87a2a54367da6d5b8e2186fb2d1f76bbbabc7c8df9fa39e68b81cb42c7f4880163e87a5efefb70883bbe2fa29bbe6946f33ccf3315c914952253f467288940d3003a40a6e8a792d0a6d854a54043e1d34dafa7660b1d9a6273f599a4489d97a76f2a923af7e99b4c3253d15c91f0f44db3b99af134c6d33751148a448a9e0d2d6cb4dd73658ad2fcccc7007e8ce79130fcd3781a200eff4176c0b8628ad02eb57cedc87626d92773963bd954b7f68cfe7dd3fb903a3028fa303ee6eb17836a89e981f18537d00de363debd1e9998f7981818312f03038c41a6680c08038a05df15bc816e98ef3c1752b872e5ca8679f70ac0f2f87924eaa31be69371017ffd0afe60c95bf71f0eff5186838c8f455315631f0a74cbdfa73f039459c094410228b30089810f630c6f20f1caeefe6532a143930b38c4ccd337c968c9e7906e013b99aa7f5fd7bf3205b27cbe4ddf2c3459094d479bbef785349f4cfd99a7add60e90848ff1347e8a88b576548fc79782dbd0ff3e1ecf3e6088811e60bb72e58a16fbbe8541eaf80b32453f19f8a300dd17fcb181fb3ba2c314367d12be18dfcc178ba68abe8c064c331b3c731565be83c6d8545114e8df94075c6c93ac04936c531cfa2938cbc120948c0902870c1021dfbeece6bd990bdda2e56621eec41914a61945221728715a951d826843e87ea8bc64d1ac76d5eb9109903b5898aaf80e90a9e821a16f90d1b0c37e6cb57648503e0cf32b41083a3a68cf4705610626d8a63d67b42dd75cdde46089d68ef8f16dab892f5c0040d3616ac768e38498cae1ac1f67e51dbf1e599f697fea151f6a1b7bd9d61a2e6aacc41bfc55bc9e9a591242fbf3ccf9d55491295c53a39142571a29b44bf1218a612251d064a822573a5e53069ad491df85d4f1c27acb4f865aaed1eb24c9404381d66f7a3dd4026ca26a68cd550dad8626436daab00cb4a9c26f818c36fe3c3f1d2f699a2f876073d29a3369626352ec0c5b281bdba28dadd1c672b600933a3a7488166a0d4dea843534f929d0f4654d15a9231f7f4d4dead4d0b2fcea4bdad570d1914cbe430bf433de0697c2b9eb704ac039e1e6c841ea3e2637575b63e5c7518d15b9f511142f3505b45e687b3e1424500009a8b48145d103197a700511f40144507a29256dfa8413d1c9219329fca10988dd997c76d7799d09411b7728c08a76f7c48e0e22f1067f98a3ec2ecc5276d7752623bbc36208dd8f2174b2bbdcbd57c7eb96ee8fa33bef8f2eeebda4bfb7e648c7ebc751098ba38d9ff4a38b0962d1c5c6764617d0064657112d882e25b6f599e38a45367e3b6d9138735c76f2ac007be1c6369ea669b2c222434dea303d7e198ea40efdf865e842eac028940532e42a6cd28fdfc2e6ea743f4a91294154b1b116b48d6d8876a886168f6d5b10880dacc769c70ec4aab4a20f3eceb21f7d00c2bd38e2a20f3e36b01c663107a3d88198b3b20ffbc12081e812da3885dfc68077e8efcead52af470ae1400a3bf4bbe28ed38755e63cecb6ef3b5c7187feb016f90e20311deb5a80b5f044ae74e8435036e63a4eaef4fb53ff166073a541cfd9bd3002068ee20db6b9731dfa2618b1b19b50f30612aff4c8ab9adbe0c71668e7b468ce8a1b7332f984094128c0367e1d44e28e3d125d07918d3fc7508e580e282b2d36fe552d6796e3b3f1e7f490c343ce0e39ae9922fdb9bcf13b429b10f48020138298d8c1c091df3cd933c7f6fc3043d953ef09c2b2ec48681ac18e027efc067f682ab2a5e9b5e503826a8e6c62354799669ae3ca6196e3d22d3357069a0c55e4ca024d15f628ac6c6c03db605a36b0dc753534f9713fbd1edeb479225336b0cf2a69b2f1538e49d0c68f81a3b8337f8296082f8e360669a4d0dbb68166afaba1e59a2e640a1fc9148632b3658e2bc7e52a2c7de8c9c69fc383af6c60dac39c211b87261f13111b6427bb7abb6ebb7ef51d05fccc5d3fface0382e20d7efdf5b5af4c5f9f3a8bfb5ac19ff9a55a32851f684fd3a73f1b3455f815f013772c114558287eac12b7c1ff8020bf22685b24be02527315febb64639bc4c6130c5144d81ebf057216cbd5e2cac6e0161dd8f83b20e647293225882a906cdc056dfc7668e3e73258338e26e6abd0721bfc3864a075bcf253a008036530742ea6e430db1868e38f1428721bfc394574e85574bc367e1df87518913a2ab43e631857e8c7be456b63fcf4814a54e8d006b671866d204e5c67060904e62caad3004fe10f5d8b271b53a94980e62cef23ac4ad1c6555a1b7f0d42a44e7efc356cc0a6606bc077ec911a5aceaa8fbfe6a8e66863207377ff63c3b643f067638bdf1e893b406af1c8c66f4a62e30f615c1bc7c299a24c731bfc0df02a1b3ffe19a44ea6e219082ad9c76df0d6da180fc136ee409390dbe007721bfc1903a9c51d8bc46ff0639bc46df073ae456de3ff54a06b6881367e181e056de38f3ed414d0448c3b9778b8a77a9410e0ae1aea31f5ac4375a847ada430880f21453aa8e6db4de3f3c8d1bf24d29bc00483cc287b907f36bedf551830609c5a736585a60a3fc618bfcf55c52d2c27530e4c773bb84e053afc9dcb59a4c7dfbde66a3ec6ef02865d0f1bb78061f7833fcc4b36fe1ce4acfcf84f5fb89d60e3d2e3df827056f7f84d9f85626920877cc732b1316771d8466f5abb6ddbb66ddbe6f91062bfbe15fa0bb88e7d81a7f0638c31c68f31ce0f7b8115c2f833fe5287dfe3307ebaf3d76dd217b7f7c9e477a3ceeb11a23f2d5318639f8df1e3e7306a630cfe38fd64135cadb50b7ea284e5a4511e90b5dc274ac498622c4f8a340f47a727462e259789e849115e482efd01ce6b958a6c9d46c148f2476e9c37c618bfdf1d663bd03c0d038ee341c64351a76780c22b68519477d05d578cc3a2b4f1f021868964600492fdc1088f1f8e4895eff06ad7a98797084e2b583952f275e0a806d9033e452e3dc0889a0082c9f583728268e8270692eb02832484a22401c5e5073db39d28092317225cd082f941b36c27720110302970c111030cbd889428c1a6db89624f7ca0194233846608cd109a2134436886d00ca1194233846608cd101c19886dbfc6b8a0ece32cf9d686ed436ec9b61fd6a16dc1d8c94fe64ba243d48e3bcf48e5ce3ece9a9bf4de877793402e066a1a29f49cd6f706e270ddf837829c55bf036dc49c155690d2665a16ac45dffc192152e769932baaad2136e732654f2ee62273d6fc962367c99f1f94962270310e8acb59f8a70d3c4e5bfe4c0b8a8d98107b7e969f0d675a5b7ea79d73b05def174c3c2672529111848e4f63a561d7f7696d0da82fb494d63829a594d64a6d37e3c4e6d7091d7f36b1fdd7c446e7479a6f139f52ecbd28148db4ba748f7368fbbcab39e4aabb4d3a873ce51fa3f4928853e6f341f7396339ce39698d324ea0e34f986f915f07355aa0e37ba76b1ca12ba5d469d8f3e9a76242c739a7a6b37633b118a7533a736c4f299533b1f9bd7dafb5de946a09cd554c5b6dad758b8d368f361aa5c9a7b51e3141b96d5b653cc2759526a573d66081f68f4e7dce39e79cd3e99cee74565aaba74f94d2165b27a594548a95e230b99ae9123de77cd9269df7e79ca6d3ddb66dabf408a7e484a2937a3db52675e8dbed524a7be85dc186b91cf3bd945297171899198f72b8cb9e472279b3cb54da225e28a53030c598e370ac5c6581576aeba535a078092e72db44514a6360ccc4c894c339775d96c2898ecf614a29788ba001cb3e7548fe0481a27f7c9f13e4d68cfe138c2fe3760739201f68ffb0c2a6efdc727941db4629a594524ab78d524a29a5944e3a299d75d64a29a5342aa1b5ae45e665ce19834e1718940d994a7154e8bab95c73158d6c3e3228460ca554069d2d77935e01b6bfef7263fc89af888da065baafc947e90f507cd5eab22d266fe736ef9c26af67ce2e8b8f71674480a7181ff63c12c9d3dda7a70a45027832e93927e8460ec5a1bc205572855f4a0ac617feb0c58b627c51115f9dacba3caf67c657278b2f17f855d0b698544a6957b425ed64f369279b2b1ba593c557378baf8ed66531413b2342c7a019aff8922a295779a318dc5ae291ce85935d9beb5342470c825287e4b91001dde3755e1028257b8c52c618638c3f48a178b7c79c258b606c6f4f780aca5c6db039e43a9b4ca6e496c546842db7a2a97ac275362c3c25933a1bad286f59f8bcb2d1e66a1eb9ca67ae8ee0ab0bded654499f2d6f6b035b0a914573453fe034045b1679a944bd169eb66d1badd2ea4b8e0234a923ad05e89685afeecb8d08d71b0285371295bbde19e7d1d6721bb00847f7e707b6e3c7bac48e763ad9e6922da59c451c882d7feee0ab0d3661d3057afcb02ee1538aecf81b6caee4bd3ad428d46330fc3075b381e1cd0e3798148a9f4b54484ea690602e54c41220243f41bc8af84c205cb14403400fec69cbd65c917670557c205c457c827821f9994b80762409edf8a12c62c787214192cb6de2cb2881f60f73cb410ddba4649f119e3ccecf73ce18e79c9f0bf49c36ba47f7e81edde3a45fb72040e6a6dbf4362e56d39d9f6c4569ceeba99d476dadb5d2d7546dafb9ea6437c3358c98f376fa5125bda56647eb8c5aa06f92af89ce94bed04ddbb2ab7533cab22d688ad540a2bead5bbd15d708346b2c0285dd2c0285dd2c0aed2dcec0e57437054f40459c6a8b4b0c0cd2cc9c47c35c9148a5128d44a7f769575115f7c916282594508254c9152739b09b7957e957b076b317964d4153ac0612d7d65ed7d6e62adeab2d18a264ec30b66af284c455f297b8923839897533a06d57f054bf6d7733201e77ebef05b3e9931406d9d105bac2a0dc8fba99ec66dd4c7646b2825bf761cb0ebb99ecaaa05edbeb4b42c75a6badaa201dff24a3f941091abc360d7c889cf8994862b15a2d66bba7343be79d776eb3f37a2c8d3bc707bae6d074fd1035bd1e3abf2b6ddd365b67690a1d5e19bbcddebbf5d86ed62ae6324ee5e06098ca791fc71034a51fad291c4a0351790712c79cb5d10fad6bc2dc866e5e4fa5291893568b35925c65db69ad35d4daaea06f51f2214adfa2bb59ed26c41542f7f4a05ceebc1e3a49240ee8f8a5e82db3646269617169697171892e2c2d2517af87beb8bcbcbcb4b09c5e5e4cfae5e5a5f442f25e5e5e6a66ec8a907d46886a6deb20cebd28773c3f9c69a4d0fa43a2ebc3cc4e545cc7d264cac2788b859baac4d82cb6bdbf3591c376297d9692c459fe1d12dfe98ec41bdb01c1cce62a4778edbd30be3b55dcc77c3e559666b1b0596c4dd8ef8e7447e6bd2ea5161cda2bb6edb6cb4777cb27734bf6ea1698dab6335ba25097a67a3cf25f3007a63b2067a1de7ea744eab8bc7dafe52c98b7ef69c0592d6fdfe4e32cfcf64d3467bdbc7d53cd5976db371dcd55cc9fde5dc7054fd96731751ea9f4ee3b5648bcb15f7af77c08890179240b52c7baa40bae6379f0947d7b7ad960db0e2805db5a1f2671c71ac51bfbd6484885f1605df3ede90533bf654efb2e1ffdac51b4d808f5f98679f9e276319af1a9cf864c5918abe7cfe8e1d8be3f59be70ee13189aa884a756a8ed973e1ba24761515c784117e66f9f434a4aefa59352548b459e44f0f667c797383b6c7f227d0a144a0e71996d4d557c1d57eacc0f9a4fa8d4ec17363d0a11d160000048007315002028100c08c562a14816e560b4eb1d14000d7290466e503897489320076218c5186288510419020031041965680a9b00b0e30920ead46a4fc1bd10d979d7a81b6111e9aa6f9276cb4de45906adcee10aea430bfe8e784f774cdc9bb1c3b33019c09bea1f0f9f72f6ae6aeea51bba295f0e84ba10fb0c8bfb61d01d94a79369ce89b0a60637c7e911827e9086884670cbdc13ff1c808e5de11fd6bc8eacf2e303fe70559e7063526cc3194b300f58f238327a06919bf4732d6c4ce80e4e0fa0f24a216b65ece2b6eb01b34a56f8899f8ed4eb4902d29a63a065b99b4ecd7b2bb22c26ec11c95e18500157a418b4d0b9d112739041d55c0f7b5053440bd2d562e2664be9aeb3a899e9e26e1591c6fc8d1fadd43d65627d8e6d81b44566aac8d1ae620a48d1071ee4eba5964c4c6b57ec3dc3f027be5a70c79c720abf7b1c2c74582dcdf088c95d10d96358500b2ee4f5c06400794a9760a36577b266fe5d9aa9bfa59f2d287c9d6eda5f46f80c25d294dc49ba7044db838d5c28480f9505841126695df130b40b36fd363f7313b048261081946aa4e7852887fcbac057fbe319ff9bbf8ae038214cb1e0e235a46f3876e95b101a554c59f2b8d603cd30148f17a4ebe9166be0a804a8e61b0879ea1c0330f9be94df0cdae58a01dd24a4fe2e4dfeb80e8c03bdf640271a51f46d9ff2157e241a0fc663d1249d896db0b909b8c6175d9bf616d6cb0b47cb341134f85f0aa72054bb9511d07fcbb111f682b80bf16e67a840134889a5d63ac2f1de9349e20634eb710edf1fc8dc28079bed2254f42bb037ee8cde755d2bbb5c912752c7ae4fe30d151c555f1bd0492fbab901e2358691c1451c902a65f728bc48624169e4bc9a0a85aea5afedeecd0bbf44d0af24a5de460441e1880a494e38911aa762ee81873b7fd5a0ec980d2df235035192cbb04bcc7bd880b6f82cca22f466edc78eea42b287e0a93339f0998d6b17933965e32e2208189feda0d61b0f1d46c40a49a528cc9521ae57f7dea2925e45f4a440cf298a082ab966a18d602d432b2dbd4e95b9830947fb458d2b2d0ac09b7ef222a1c28dd24883050541036db1c6dd0e351413b1825c4d1e14ac50f4d91cc916929e43e74f09c34c79e1a7635cbe07e5da3124c0e6c8a19802f119c6c2d0db40d0a36dc2b90a3ebb9b2fffdf0c649e57670c3ddf6edbf9baa8139a67a96efde84c77c22ec3bc6fef8fa85ffd994ed65287cccac5c3f2eb37cef97ed99bd8582831619d262a9dfaaa9fbe956a8a767f6b7ef56a95ca708ab61d9b5c77aa4ed18d4ddfc1125ee60a2cee4bdceac4d1e4c2b60929294108cc5d2ba48eeaf0e4238c47342b740df21d8df7351772836369dbfdc28a4e44132cb56929edc79446147eed57819c1e0897d92cdadf70c0b17c42f60338d64a8f085f4538656918f1539be89a20a2126a94a4f4c4dca48dfc44183278e63e4b00860184edefa7b983c1efcc11c20c526b831524814cf9d90d4260b288ec9bbcf311b9a3725502bf0fa54a51a1118a91fe5880d71532c748b97823b35333004b3d226851ab6caf3f56f41e1f5803f7d6105b882341a58c8180eff351cdfc76f36f686e91bb94071f26e8087e62fde841eabb85a53492efd8c38b1b080ea9c6881f8904fcedf328348435c12c26ec6640026651e07435494ad7d106b6913d36000bf25c83a16a526a8afd622b0ef5a4946579bc122065bd936b7b69e708a76fded6b661ae9a490407b67493cb9afb921be40648d57f1f4c151ee9e8e26a8cd20ef80cbeccb549dea5919cb51d1b6aa02d1ba0345398241205d3abc2f079eddf5f4f416f70169c350693083c252d443ba9fa80fb62a3338531fbf3092f8020fe263460af7add2f94352381c855771205668f195736ba844aeaae95b7419e3043c65c44a7cbd56a0e90df059b0c8c834b0fc20db8f00e8b8f75f698379b5df1ec65a813a1ddd18788b4e88db55c8cab2b76c15ad828ec983d862470171eef6d2af999da5bbd210b721379b7014d78a8172ef806bee95a999233eb6baf021b7bce3ce5ac50880ea0e59054595f48eb56fe28f604eb0b1e4de8bea42e9ff9c64dae4b7a00024d79d1d6970ac550877117367330a1df1e6203affdbd87f077f7aa6a4c38fa309b77ec23d2010cabc17350165a3dd45c7b8d00f86184474e37d8397d015f390f8a15a161598fa9c79e5b6f902e60d57c0d8f60819cae1abef6b304aad6737285205755cfabc63ef3eab44e4829495d66b792e1dafc4438526a91819012198a8c08c18b1ca6b66f03c69d82084932c8082c13862740bbb1a0348b5f0ffab93b2355638944cd6297433fee66b08408fb9a342aad83a70b77520648c18d688b8a6482013fb00fb95a060c60abca70190106874b0801255d74ba84139834f5c03ae8a543d9905cfa2fd6f00980feeffd8a41809f28e7ad26ba0ff28bd8513ae7cf457b8f57d0a8331024e5e1f82f6ffa2f53fa75eb187a062ff3224a6dc294dcab181040e6ad62271e009e2f8180590ee113b184cc5b9097cedafb16a0d04e3bb8aba4b8268d7be7d2be6a4c2aedf9fb1191c56f4b7c558238f4bdf97f1511ad5f076bf908653c84a646b3dfb7dd55bf65f35100d64f3cd5535ab4c6b7e1e78501e94f33cc24c0bed2f521bc593fb48cc3a08a479521a2b9f594c71e1875ad02aec5d04ee0bedbf3da233a3fbddff0fc1ef78e51fa6fa6b81c1c5348e20fe8d83d26c3109f43211619ba836b3da19fe1753e27279e74734e5bf022de85f77fbf948a0833f195dbd73ed902814d4eef8b2dac1b9d727d969cf6d6688d96a58f171baf51de716569101645bff42e707f4886728b2b42103bf97668e1b9361246e4b2b48bf4044bdadab58eae408ef756cc9ae76140df054b0c0774aa457c4eb4397e3fb907775689eea47c8d875edf337ce6759005d77a05addf1b24334dcb619dee6c451dc0d513b39491e2fa66369e3ff9fa3cc0f034c5568d39fbe24b87253d696093a553dda91fa6559abbfc866319ebea494a23d60d90e3a4c3f04f8adf555adeffa486aed2757e9246cd3249850069f9dcb980c895f6e9e71f2d68b9f775a7e02dd42acea0577bd4537687d58e10d7b6ff1aad61caecea8369afcf908922d6ca9c28793db8983600c2b2258845cf9cc9c10dc80617ea328a530ff61ebabb03f236a0f30804c379b46c2a28aeda70a9973e24a87d327add42ca5215eb7998d41f74af81d646d01f28f88948ea9df08320a267155ae2058d4a0394e4436643ca8e96ceb7a03d1d20fd0ddc461f169fca26b05ea6ea158e042c7a479cc03e1ca83f0daaab74d86908465ee4492aad09e3a7329d6726c54083621862fba0903bc97ac5cd4d899d82727c7d3a0cc845f36fe9c06e182d94582b7956127c86c49ea4d3468da05feff6541921fbe033003324a4dedcb024876e2040dfcb0d6b23c71d743d4761a35c48762f158ceec283a8756a4e7a3799870c0a1127380067ea4521b14497469e58d5aecca547ad9163b29e12b18b33248b0113c03c31083040ae9104325f928574e976822b3928de659b46c640bb638596e9c24ef0d7a445a636231682c77711e745a64e8b3b1568d12f5b51c2e231774d9a683c07f702fdfe381ca7eb3cf3089d0861032b4e3920cade81fa8ea3884864ff93c24aa57a58df384296deafa96ad4d6cd0644d94d6df0d6343a97133cfe63b8600b231523534b659baa5ad8092f24b8d3793f1b5b4909c6ad54ab2c73f807e9ce6965cd9d5271328c8e7c0e3c89eb6a0ef3fcd23f5b699e8c07f250b522a501aebe0fb2ccaeaf416b5165f057fda5c7f0e87679e0e9318e4e750142f42ee7acfbafae5e645f00437246013effde999bcb9271819083a2635c9a64dc542246d85b69f73567c991275debd7a888bd5f005040f28a928947aad8aedbe6b523c9a8a4e4f957c5d1f734bf857795e9ab3a66a98d11d54d470578b0af8be296c9ce49e77d1491cc24c720950d34df3cda03444c4e9b7fcfd362722610198a2fbb882af3798ce5f02873d6355b6f126eb1460d19da781d22f0aee0a72a627466e46781c1b1b2dfa76d4900e8df54eb859e799f1508c88bd427860fc6e94a68071a9dea94b15367cf7908674ea02803470f3d112cf409beb9ad5097a488f1a2a3e8ca83745048401e81c3832f4c5d467c8911f1f60155ea83ccb3bb8932195f182be668c93c1ada6f6b6687bde739de7528e80b1efa5ecf5a6d44b63ce5358119384a003172d21aea235299696971526376ea9ea97941b320dc56a26e240b8aa2a138c228fc2c5ddbbbe174f05a589854681e9dc57f51cfef743b6ffae078df97c69c1fa9e7ffc2bcdda5acd009efc65afc63057a6a7d3e47966166880a8c78ff9dffb490100bdd5ae8402001868708db3d16990f7f350a349a5ac3a5e16ceedfbe5df11a24ca65d04803fe33a3f4078f93ba4ba7952c5b67577268ec626e6c44d6efac38881343d6f78050f7e5c8ec4cc90f87fc9eb72083e04fcc8f75bf826df32c36cdec618a4e47e70ff781907e880437681132793e92cf3a56b45039e8f7ef15fff0f80a9591f7bd44b12e4dd6571a6f401b85576ad2ddfa492b8939b42d7051e61299a6bf0060210f44937a958d24644c6ebb4fc648abbca85b1d124a6f103a557597733b202fb2e4798e02ab97603d74540756bf2c820b0354afdba3a45a6a2b751c292bf7efd94972de934d5683778aeddd632313458ba9fdbdafe155fe8a7748c2b49052411fc95f2c8ce7919c48bcd2b2ae1d7538047bb912b45cbef2fc3988b976da5f20406ea912e1ed4c04ad5b470bae4c15707ed067dea1da92a18cb5c3511f4fd8b1c8ab434165e0cf102cc60fe696dcea4f7ce188328608fab61c23b71a7a3adcd62f75e89b3c0ae38abc469ba03310e71de8c5a96d4bf7fca5eca177b620d25089fce433c54460086b1e00985f95570976868ac44122524dde8212a29274d766a7cc78c2b82b1957dc15b4978c68865dfba9ee9c421ed8599046b06568ce25a25129cbcfe3cb4657793d4ec9f847778c3359011939e43dc8c44abf973f77aa0c9fb61abd2652246a2b5927873bb81dc96750bb526886080c6fe8799bb7c0d59a6db92fe5eb01066bd041b5dff1fcbb7a8e95c56f1cf3c6274091a30dac9d18e18fbf1bb619df6a6e886d4ae923875dd9206d39904cc82b8454380f2f372173b1c8b53ea3847a0712505fb46de88500d639aac0dea8150ad3839e351ee36f49678890fb41f0002f642e4d3262304699fc52710000211f09686b40397a620aa12f53938a4e27041eeec5f60ade6416c1d64bd1c1cf70e3a1e9541c680386fb30fd735e874e03d7e4615a9feeca5eed20f0f419c8c242e78b4eecb3cd872ffa4313a9e48878ef473604ec30a930ee4403cf4495100b573d3b115efe3dcbaf5d07e54cdbe5417547931969348c606a3a4811f8a59a3d1e44b938752af022f0910ce4e4347db32223f9d06675eb723a07b73dba905e3b16dee9f0acd83622a0d0539bae5e012413c2b08788302d58b6c0aaa32f1b1681da1cf629c38ed23346feca9e7e706dd50068792822777edb094c19c131ed621bf54620e16e4836e1cd7e7507228edc5e66c28ced37d186edcef1ea2657fcdc51f0761a21e053f8c9d37a9645ad2563d9bc3a62e47285f3de95dd6930827574d35e70f2e086668e212ad5b7f85b63387ea62cb79c73d47d2e8558ff7affdebc2831fd8d6cc69f30f214f7c662776401ba46f32379ca16ec550287998ae490e57f262618363b6479237f6d67462d361e87c04d17f66182fa8a09cc5d2661f4dda2c8b2c18e2c4f3315c5edc1ad029cd1dc46fe4a9d5c34ef3ef9a472ed27bd8e6f9d753c73a8e426cd6bbc71184ae12ff9f2150249ad443572dfc85031c35fa49794fbbc9ef518eb5f4ced129254bd051f605bc0e72c5cb204d5d2f3c55b88dac16c36fb588fb070585dd233b463de36ddf8265c0034aac653412b53fbca4aef8d433e106bb211e2ae19dd599a976f968c3681cd3fd58a00ce345d5a3c1a7fdb8b332eb816a44ff99ea47da0910c4cd10ac471fca6f2c2ff0f04db013e15815738d193142f6353b60489ef1c08711c4d4f46926337cca5bd27942d68159efc55eed31e93819ada225e8bc0bdcb28b6f9325359a0becfa1d56bdbf7bc48db9cc054a0efd03ed40945b8134a2af0eb6319c12bb39a83da3e666363e6e6ff3f5434c39bfa7087d3328881d7673ee988bc558289fe36392351d0ff702c94f3d705798f5a1ff5317691a5269a4c6ab40b3eec7d519da6e725f74f6c4d7d2d9ff189d6c7b21660c932f66e14a543de11b3830423583af10ec5ed0b1e0b79cb7df5b59df9a3b0571b5df49926a7603d86cafcf26b8834c86c513d1a369b6ce32ecc47046636c5507cef0f99789ebd36bdad72bda98a4508b91bd5f709558d57c0669a2e43082f4b2435f4b99de7f473a31678adfd1e813ff14cf80b07707ef5db97579b388cd60f9a58dec891d8a33bf1532938b3cb54d1fc24a35d33cb17725db11fa78de04020a379e76bbaf110b8e58c3032738a5c057ef71883b8ddb0ec82a1dda826ea762e61574ce53d8e157fa90d7ade4a60de1cce188309cedd550cfa84fa74aab3ee10c14b6f3eb7d50a6315e16ce339b0f2aa5644801c48a9564758438ad67942ab1a255685f4f340799d42690abc5451b8c387a6ca833b89844157c15e85d508a5c80a8cb200d7a9d830f48fb3dded7edb33400736eb9107e7e1cc690b3ee1e871d1b32e4cc69e6de71103ad6738855ff07b69c0145407f628186a51c3ccf9938afe6bd56efc93a9d3e0c7445f6ac1a78143cc7ed58c9a33f716e34bbe803918be3b5c0c952408cb0db7e5eb7e1f9c0ea80cf4eafa22139cd7cc3a1c260bec2b4a72ff3c165cca7847944abc5b0965eb903b41ebfc687f9003ff792965a18e02d79e07e040b30ff402bd24bf7286af767600c57e928bb379cc14a09d4f6624e5b96efa093996e2b6038b09bf3c8d7ee6c10d32c75226ea014049dd53da8bceacc8f30e94d28ca6035de9d2c1cacd36bd423e58f95c27cf3bead7e3d1ab1129ff2ccc2dc7f67e9620ee6251172b6f76cfb86e8dab5656fc1169b0a19c9bda8a3c53aada03d05cda8f4b0546bad1607de6e85ffe07bbfc11086c515f0e36e724d15fc6f4ed6b9c222458c538e3f9996009e4bbd9dfeb299067d846c2f62f66020c91595e06e180b6da1990e30721fc7de1c227ca9e649cb7a17b3fb2a49e7f4228a118623a71c9cde7342fad7e689604cbb83138903ac0a4e65454de2d9d41151cc08144f71ee96da3c4338f14c74765070057cb5672559ecc6c24033365d6a899b531ba22140952a97b74dfc35fdd0022bc8094277898d502cf56264d7088bea391afd293c587b14c4e356bfcf78485ce3494ad246355e07995549aff49b47d6ce9cd5a3986f60429550e927a79a31d15bf1b52512cbd477eca7621bc9a71e9246710a9557ca8c4fd79a545d79819fbdcf9ef6a66633eae02c0b2af4aa0653de00064ecee1883cc27aaaa0286ddc2e780870216ccc6c8ff40d1f28f9d3700d94966e779fe88722971811ce4ade4da2edfac898a486b0874ef7347d7e331e89a4123988cff4d323ade34455aa905803cb0ee6b2df05cda4dfcce2be317422e80dd819d6e9b0a8e94ac434d639f7b49078216945f6df8b28f2540e008e4ee68b12ea15f26f54f5a60e32c93b6c1a237991670f1b4fcd492760727642ca8daae0b31db1ece190140296b2c0c2cfa77cbdae9a1ac3a02f4503b1999b799ae9a1707877049d6291fda3f848593c67ad10b53863940d2aaaffcd4a1bba450c1e7eaae5b0ab0d94a5514dc11681758dadba9c10ef5a1f3c62ade31707d01606e7ff64bde50b80499db1fcdff5f29ab81e5361e6262f6c098d93335987d3727fc4b307347499392395214e9cfd4a0d27b568a17db43dba67a59c952942ef66386af0d10a8430f7823c84f8c34008d3669cb8540071e85711ae4b21a735719d1654b9788b0b7c81d353340c4bf06bd550a3c85d3951b01642afe4482554fb1b8d11264df68277a1fa5e26dfec175858c0839188f2b0d7458a866b6d2f4b4a2012e458d9570772d75d5085695c9ca76a2fc1bd5d84dd1ab600fd7f531c8412b5581504064a8292c6f2dbb95934faee0228395d203d1beb4e6186da4346228a2b3afaa8ffd5cbf106a714569c80f74ee2e442f7cb09bf643ea285be6cc36b7c798f376dbc4c96fa188e5fb3ed19870cd067729200efe8ab622a681e0a4957c02d24e10d82055dd6d0899bf958bd436f601e5dc3ec86f53e906b3e6271e1122a28b374761f0a25a661b3db35ae6b7786f32fb59972989b5f247faae71709127d2ce781083c7a0947a3f78d0068d7ff24a704fb94b9204ec0845d007c95c4558be372f1793f99ce43e47aa71e3bb14208313056a5d1295ff0be680283347b42e307f45ab0e4538a97e1e12de536d04649b7c1ed8b02edf95109b0c787efef2678b478a16d734a1d9aee5d75c43e25b811084ac187ed26b5c9a02f845bb308f84ff16b0d4805065f8afc9425be446ece71515be4de6b682442210efa9677ff8973720fc5e717aec5c06981e1e58840172d5743692b545f6cd61d75390b8e719baf501b1b5b6daef62225ca3ac5e2520060ee435bdf743f67607a2345d51af85b69a527ad4446a49e931a6de11d47cbe1a66816eb0e665a44299e7eb4a9823d73b66d43fbe0880f81f2e3af9b680cbc2cd6f300c6e40d36ca623b854ee669e3e91a5f5c6236b6efcce4345dc2cf347c7e34af2580bc21b3b3b5035bc743f9af531330546d71a5bea9eb1c924c1c45bc469fb69b2f1ba00d1eae2a800c76bbf7c87d9e86b9c749c6354b2e68db5e2d0cdae3d111b39103809decc245ce4d3b0b4420efb095564fde87965901b919f97bf2b76285cbe748809d68206f07222ff38cc99b41079c0d7e87076b1d15298900a5bc79a6cac73a611306cce42360ad35b8a6bcc06b3b694c6608ce1cea28d53e84a713887a535e3b9247c7dc7eb5d3e4b016183ede005a60fc62fa4d51cd51e13f2cb0ef1864e4a21e63afad17b5e5a136795b00580f79d1a5fefd5308e188f6ebe23851b51ac15ec687a7aeacf65f265e43fd58cfc6fd312804dca03e84541d3511e22686961a60be3688643f7d17ca40bf8dcb5285580c791426403d9f08a38ee2efde5470695a85791ae16a91de66ebf2eca448a71410bf3bf8c4624a2ee72cf8526991a6c4ddfa5973fad5b7a797fc95ba95a7a4b592b052ff2cd465445c3899003476e6449a40564dda34d756959960ccd7c7fb6932133548d76c6fb9878790cda9fb49230d03868b48b798eeb17fe1ef58f8dffa84aa1e0fcefe8fbebed4316781bc06493d812f6096d6c7f02c3621f62ecf87d7ffa4eb03a976aca2249bce0387226a04d722dd137dd8c4408c0e3d81c6898e44573d5b4a86dd43b1a59aa4d3e285147e88802fe7105d89fc9a0a486b1507b58d722cd98f23635a78cc14b4d035bd4e2414e9d785e6dc751b9b43a45fca4c238c122b7b80b08fc24459cd47b9e0d052b939e20d741e1853f02810f987d0a8d047ac76b0df89629f15b1f4d6b7cfe60778c90074579a3b6ee0555a2848176b00be406c8710ce8902148e26b9aeb168a7fc91a11ffe100e2cd9ad0a1ed18a843ea037f008ad7423df3e116843dee2f75d85d3ea29f935cf0bcac71bd1cb52c383c04b541ec4c1c96721cc4e4ee079e14a001e95c59ebb3d9c195354bedb3d46215e87afb367d5c6b8330aac29bb5281bfb59d3583a04cc205709795ecea1f80dafe66ed94e2c49c236efc12fa483f61c6c9c0be3fff2197c6c6e6b4f303c16802b53dfe3821e8e68e9dbe4b06274cf7a53a4abc0884e027196fb6e8a2a6c0b18034c3d314269967a028753388e2c3986c67e88c9a4841932b2d48885e20fe25f6c69fc42e5df7f73a67196842544efe7d6d97d228e8468fdbc37f36b17cc08a58ffe3497db05ac24b7f889228af085ca6092d244dcfca71d2b1bfb546e0a66b74188a974cd4d40f5068e3009c8297196ed83366e6b08ed336c6def9578b9b39aa6d91d73228af59df3f20697a9d9225451fba68de11df49a92ca219269efd29cabc9b5eb8a82f42fec14b9719dcaf4cbb07b0d156c15eecf8c81f158c0cbc31afb9b16d392ffff47b8dda06e598ea30add74b8b3d2fc78ab829534241307115cf663e11cb1950708ef9544177ff8d4ef12f3eadf81e3add26a15439499cc507e2bdc3ad99e97b84b36c16e6715c0ecef1b22c11a92936d393083857767826d6ca26b4b1fd8826b6c9a06f646df5047160939e6cfdcbe661e06992fb2344de9d2cc2d145e10208de99ea15bb115bffb0db62f058863cb281c5401ef1661b135eb828cbbed9a9c300e7fed62a5cafd7570a43e2018f9e9360e97c207c4eced3168c6063f33f8082c56fb939b3934ad5956eca0178d5c99fa429a0300b2fd0384eca2a001fb85b2395382960485f34d838f6a30a60b41444b18adefb903565a45674f811cf82eeee3ecb39e8f199b0b2c2c3bec1f570bc61d74298091aed58a8a7965f6435f31fb322d5808adedd50813481d84900364fedd7036dba89f1748cb846c888346e9790cffd8b61a915f6fd0a5e2b21f0e393f8d1ce125d29769943da9331b6c509d32a4a6f78667a83fe3da963e16c904209772acfb3152749f90438bd38a19b43c1103ce7631d41546d4f422df0c22e8ed6fc55df695c2a2065d9f4f84a9004d94e20df304f448208d0319402b0b75f56179a2004a5636055859edb22b7d463ea82fef683fc4a07233b451d5ac85bfcad80987a82f752a9542ad648317ec01dfb7029cd5049417b86212503f27e8f7adb82477f3e4afeea294e84526550743e8597aa17578e215bd59a7885d76ac1897438e4185681f999a8c891bad805a46590f509f90ca4e9410e0e21c2a3f6ff2e960f42d0470dcd502059183e13128905615b14156addaaee57d4472818a68bd8b11616565ea1228cd9a39d12687fbc3c0d5e655368b00f27b91231b069ce1c0442d8b711de355254ec22e19091201d6b899d220e890fb75e402fe8d49e3fb09c5a5ec648c80378bb893ed198d16a201fec3b2b5473941657521ef65f3ab8bfb44d99c0ec15a6eb9f6a92fb3020afb8b8b2b5da32afb8a68fc0b87109e8f5399d96dce8642e2d2bf2084f8e9c890430a79dc628a8fbe340a31f44ece59a4b47063fd3603eabc6b1efdb5000c6e2d442c0f3477823fe2a11d7da89eb510757dbf4d012d24bae88d5b3b4f1e09f070c13ffed90c35d61641d403a2b2d4b90cdcf4a3fe76758ecca3377fac8d9982909c5b3f1da031c0d39632bcdc6f912c035b963cd1fb417ffdd6303448a4ee8ad2e750b87a75110eb9044f710149fe0a945e2395b9594850e8be6b84e390044a2ab2205804c46c060aa6c6af01b514df56e12895f64345b30514c92104c80b723f9d4fd7e924abb6ede401ce4340e0f2ba276f94bb24af13d6416431124c0f6fd282503609f804d2a5f268c33483d5d39690158496837d55f1a9f35b056dd75919dadc03e65486f1d7ebe3443e9a2882d63f8752485b02ab4f8b6c31b9368a403400e8d903ab2b0702c37225b6fb7d229089100cd0ba89017524c7359733b0e5fbb7892e2a5e53b5e43bae145db23cf7dd7979a3a1b8bfcb7834e674bed50e2b45561497c4b1043f230299e52f032a1c698447ca2b025074fa99fa55c256324d456810f82c88c6e71ac58b6de14107c51f6c4c91aa0dd7a4af15b753f258ab634718b1d620942ac3af7a8ad4bcba46b99b5eacd0a8ce0ff873c239dc12b17c38c889bf3e6c8096e9ec6e57095464f6aa0cac8bfa55c6112f84b70d42411bbc8457dd9ff7c129b66efe44854fe99e012017807201fc5f9eddd3d51b2546410b3a00c93450566d1b64445b10646b1acbe4080d24a69906e823d210c6e383cad9988cb16908b7bdc455e071a74e10aa45fe935e4a7c8385061fce51c638f43bc1f4fbfb0a1142ad29ed25174e8a1a459c7cceaba5dd2ef5633b5bb903809a8411ccd08368ab7968057c23d2feed64c7deddb410dabb103eaea116a7a21b02da2b8f93a323d4bc2204a04a50d46f1b16d583cfad11948a51d0013b88af21d232cbcf91af6af62d76b4531f378621bce1c4b1e12eeacfe4c258ed509216b56849985815ae590652da61b6241592db43ca56191e468e84c4adc2d1c0825d3e3543f7e132ce4b71590ff14010d785304f1173084457f5e0edf7424d715eebe9b7b688caa1591d196ed3c8ba511142d44363a3e3b1ae663374c59964ab6c335763116f6ad0b2304eeef01c1ef76817e323491bb3f240b92f39d9a3b94099f11029a8b067d6b31a350b4c6a24880bac21c711013099b39cb74c9dbe330dc2d1854cce2d57436cee3fb1b595ef4d94402a5075ca09822129f753bd2023fe2cd7e9f22e22bc93328bc36fcd1962d7c4817c37fd8ed34869ad281c4e198c4a28ffef41a4be41d7172729fa81f229c709abb0415af2a81ae71048e934fc58b12e10c7fc97d1f48f46486f1ff473826272368325405e4211864cc570ceef80f3cc865a690e6ccc16ae6fd1598222b6f5f99bdacfbaa1a8b900078ebbd74bf47910dcccac56a934a9c6a70bc90ae606709f4dd027571fa7a9b79007aeaf8fcce97c9b7cb91bf898ef7ab054efd25f88792652ecd2e668fa61127ece4861a3e4fc8ba36c95056a1fa334e8f56da65bd959825b9daeede984cf7559263b8d0f5470c211f09644ea10e38b6c332f950cf0e2bcdcf9fd43c8624adbb9a8e330af58a668c8e3644d8a6db3e633394d411a1390ffa48cab53027ffe178f20d1a11f4652cae2ae72ce7d9f9ae26d209cab88a360e33ed691e80949b28da20663b79f5fdf8c505b37b1c539ae3983da62d8fcad5c84cf0c23c2c384cf507b26f937103ae17b641af08b458ddb65832deb037b421232a2a4510ca1c08e6c280ee78386019d51928c1d325ceef327b3001d5a0f9fa9518c404a9454de3aad08dd39507b09ec85c630ecc180a371e0ffc6607fdb80b15e33674e18c6f4603445e3ee7dd9209f90ee630b4a31681fb8f448b47092a5d3893936557e68034b65c82a3d6b4b7f5aacad3e556e6c21b2c0e3280ce1e18a22dd6012a96bbe7e6ea27bcf5bd60f11031ee4353729466825e5202a18144db71e5e12cdbde5728fd86a861a5627ab73bb70ac11b90cb5e0f713ef7e65529d7318f91900fe0d1ca23a9ea47616f3262d14aa9856cf92c9ea06ca98fffdc6ec90716e975c6d38b9eb07070cfbdf98698b4aa97862665c2e9eeffda40c85b1c0936a68e85d77be707c0c21ac51cd3a3d2dc1ba88a908423014a73d41929f25cd0a441ca8aa00ec404eb3eb95621135cb1b45989de1bc16a87517a49fb7077e80c2ac2823baca99994f6701ac086a65445164c840e0d17d17aa47b69141010152e6b1485931160619ce146e11d76cc731a37778e03d8d7c3772b4ab964348dcc3832f895e79560de06dce2c5a6e7ba81c463952fffcdb491a76f7ced2330779f4273a9390aa34d0b73b336e3a752b518c834be26d3f94d13d0e585a81ef094034e1e08fdb739fdc94fe322abacc0daf2cc10cbdcc5999d3ab0fbe8ef149ba719ea876cf8e05ed5e9f682dc1247854643a711f7fa6866b94a49dd38b49804a79f4043e251b06e39193c92eba5b6306962844bfcf63f68dc4aeea0b3e754d11f7fec77757c3fab3ee495b6eee12864e05a1465e86275cea94d00de3fd725066d06568714516d7613b07321495af3bb793ddf9ca0d589d4c1a9c874b8c22da3516672ca7b1f089658df37855bada1ad1ed74a3628dab16af5899b371a8146a10abe079f7e18062fa142196100abbc69ccf51b633e576e78cba8144ed23823def94b9975549c74288afb74ac68cff05d5f84c047bb71b970d9e87b868504d34a223dc03509fa64153cacaa1073269730fc534cff8f8ee0362134b3955c82169624fa7c94f16b1d89996b4c4b1fb14300ddba720a57cc19718ff89a388dbf3457283dc5edacdf30adcc9aafb73b48ba597a5de3ba27c815678dd8db4baccd4ea480a70e651d864c03466f0bac1d65275406c44848d0d855904cd4b808b873565c28571c3b7bfccdd13a87919f75722f2384b8dae6f52366286a52ff33831e5ee282d4d72a1230e8475a61eb4035609e78de4dd58c8d337b3a840a0de95bdd7c58d5a45c43fd018512a702bb991ca0a24641af3c88951eae006871ed721c9ef57d77b5ca360a379e5419bae1354a14755dc9d665aed99b11e74ecd5aa793cce7934d9312c36581e86634289925bf5e245f9a68b04990b2b0738be99d79ae51cc08d0d48251990b82405fede79e7672b918145570514e8a3b928404496791e81c13566c6c49e9eb7a131d04dd1018ece041efa09205eac0f8f5e0394524d9784d033a82cc1da4f16162274571935ac18ecd70316290a418f311d6e022ca39fb720b943751faadff411e6875ba93d2c903a880573a10289c4d9b2538ab37485e4f0a4ff059624fad0aaf8acfde43c43c150ea27faa8d8d54729b46a61f387d10d62cfbfe0877e7f61d59b3f5c9a6fadd8e08e197ba9fdbb2c4709aa1266af152aedaa9c7e093f547b57f613ccb417118b1647fd67300910ba3d33cdc82b80e7aca6497dd7ac91be9bad6de4a23ddb7279832561998439a6aea6ed5836be440e0dd05e8b94adae50929c895ec224e2bec20692077d654ef3f4d0d4c0c09645ba12328348ee486321869cb2b887ea2135a710662b3e7679a7cebf9116f963603bb14c06c558c889479e0259f2c71ef8def1bd17cb869881b1f490d67da45cff2bb4402b0d1b378c21641a85888c5642a36558efe145e92e01fc9946cfc37f61fbcccc297b6c43694f5a71013bf0d1f83278fea0ccdad2722ac6955beb0fe0bfc80b227c08b0b571ed8cc7acbb83e15870d531a7be33437f63049b7a8001326959b046b7b89520d710b62dea0bdb27bc667b974d2e6c0387a6aff8ef06727ac1a56124f5c0b3f44e6ff789dfe5a39f8712964379a41e1f72a723112d6dc69faf32860d3fc8b5a051fbf98509a2f2c417dbc8b03e9c17502a37a2bd4bd04a6ff9e8ce85fb48271e381d0059dfb4a15e25bb2e594c8ba562769a2acdf98b5f28a0f2cf947dacd7e1d2c2c93a6f431abf6ad4fd5f4f29aeb0a857eb23b9fe0ece64199fee34766a902c329a5c38d8c2bb1645f73bc93d864aa044823d59a7baca97d6d81dbe394b03962ee19f28f70bd2ca451d1f35aaa06d9ccd52f37fe1f6802d639cd3564bb4bb362cd517a95d644ff2a067c49ae3dd35030dcada3edf98c169f8f0cd4a45e2c030ce9dd4a41326381f0fb3264993efc3ca19f5fbf924b8715b86ed5183c3dfec841ee34c381bc39e6be587c74015fa1f91ad0373285c774766c5cc92f921263b3678a8c00c865153b3b60705a4bcb49ed53786e0ac0a6e7a377a3750b7e3dfb75fbf85d45196296db26948b5921617596e58edb6737173f75933f1a2a11177b7e0c7a27716c96c7ea6d38ad5829844b45897defafca5a6cc59c513fa9752d284754111b06cba4cfdeff60d5495b5dae0ac79618db8e5e8754c4844145b07cb5be4d536124f76799b19589909e722196e1966e1dc11d74ff3e60894d0ea5bd25989e7edaa555004ea93aac79b47cbef3ae0a868f29b6af2cc801ca9b91147dc2ee4eebffbdca48dbb552c109074d3517493d7cb43c3b48033f0d899515959977cec58b3be70c28c3138672fda06a25609b106f5c1aebc0dd404976f51a0b5ff690e341855aa39ce9c3c9fcba748636a8a6edc8fc6a6f546a26449ba02f573b72bd0343236c64971a867e30b502b6ebaa1770c6cc515436503a4d87b4c81fd098925ed8bfbd3f29160066c247c7e0d88d4aea5fbe7e74f7fed5f7225980b9344bd3b834bda8edbb7d99c93ab8cadd7283c79954da69cb44c0345b10ba9770dd8cad740593233a40b1a869c3de4dc454a5aa2919696e9cf94ee35a08e5eccc79c1496f3f11d16873cd822c2b3de198427e9555008fa8d1ed36dd640f3fadd963f3c190c4fb7581e31734533ee88143d8069faf930fe5470e3b60cd1488a56968c3da12ce18999123bf7831eea4af817d60a91b83b109b6a6529c0d6a80e66b7c5ccea55e78d89d9196fec77c7d3fd73e6c3856e8a24cb69c74342f169f51816d571e0945b36b9eae4663a74b175e73e0c14ce336995c52cdd406286d7d00fab8c191a29183090d54d21f9635b41b11a759d67eacf442a00f9a4faf943cdc33493a588849962abca4feaedf9d1a5083a94b854392a7c5df53a767b854583a21058d0157186750490feffd49b1606e6d4cc5c13349d0ddcd0c5ae9cf2f46d54a3ce41f6ee33e2cb0e774e86d37933ab1258430d46398993fbfe94e537d0098d4d4728960958aa3386a112b632f4830279b82b60264b9bbaadd6bfe5dc3a2b0eea4d914c124ecee8d5d5b9664c6ec9ddee077db9bb5a59d1760ec900a6de3f4889136426e3affcb838dd503fac072a026a5362ea5b7d8177aeec41bc5b9e226e6e3e10c4790c5deb80458be8468ec5734ce1da09cc72dc830d950954ddbf2d17d44b2fcca514c3a781e73cfe401c823f985d2a007a5c267389b2d070747107b20cf8f4f0632f212eb4f16336d507f62c201237f1b7962e3090f818b356dfdd01717e528badfe642b67a907edd47e80c888a63cc14624d0851298b03b199148900d00ec583e34dad105fb4094f3aab7f17f5c4c7b2fa401e6be153499e2b39bc8d8ce0feeab413b4bafcf39df0197b7b1c1447e320ba5c07d7de66297febb52dd63168cf9d853379c8e9ed8fe09def45f59c6f45bbb748b043c15eff14c4d6f48ae0b95a3e6ea8654ce7e2d13efccb741f6a28fd7fb36a9a61d8cffe6fd2a6acccec1c886e62625d4b75e7acc64238225eeeb8c3ce1271450782a59da15b9dc7ef244273a5a25e08077ff8fcbbafdfcab3697900148857de84997a9b846827d3d05b700c6bc7c7724055f4695a037dcfeb8e0e1b25afa920022357df1d399e2fa7ee605d28feadd3ecc66676eb56751ffab642c5eeaa40eef00ea760c7ead962be9f152afa6964785ed5e53a3945505297cdd7266d73f23cb18cc1dcc7cecedee1a1331d7b2c9dfeaab2cabed47fa4569f92001ea89d289d3f71b25aab9b793e4feed6251a72bd623495899e567f6ba10936d1b58a09cc7b8ea8f2801a620a4fa4b626d86b2d7e3c74bb07dca418aa7ffbd68b6e6582760c00112441bec497757e4b13413cb3869b0492a2673ba1deb29db4c2d59463ed32f65120006ddfbf02dbe545e5867abc8a60a11699991a0745ce815175ae124e90f326656991106f2f4718284066e2497778a3263a411e0e3df6c927fdfcd4b70d1fd6f2dfd312f87660fd52ae5bd0a03dcdfba281dc86db44dfef46949d55f7fe278f1cbfc59666eafe6431e4197d238a1e6f93fb3a7bbb04a52f99d062dffc4140c479960409cc9f6755b1ce9d443882719d84f12a60c1bbe3c57249b1655a2f504d0df618c6b45404e8cf17a80a1e7341727d8ac134719ea76c4261e03870bb49e1e7b22a4a91921982861839726628a9c2419cf9478f05a5766eb9d0a29bbf97242941787f69e5e91baf89c63a8e8cb071ab8104b8c4ed29b0f19c3fb07439819d1a515244240cfb07e269a1d1b84328b791727c68682a18ae7f5c44df990fabbbac151dc7d82db4ac545425913a5e911106356306d03358bc71e5553d252d8b0b0e17eba7ac90111ce11f28e532bdf203db2c5333594a47471ad669dd73da526931cd3ed118b2e1f2019060dcb05b1b444c0757f7eace65e266d56d06367f602fa2dc5c6b072c52ea24fb3101b9ff259cb6889f7e12aa3144911d14417e086724ea7a46f7acebb6a40f862bd7645ae0a15b34342c78fe93f8f3441c9570764ea1faaca90984d972f034737888b98cdf56ecfd0be5a4b0b5b3c45a160248865d85980a1ccdd8fa230a41121db1f8d25416b13dbbc23668898e5a12a66550494e431daa173362962366d276b397298bd366799852e9c91d9f89b4216d3caeef0783a3bb9f5d05435757e4cf6e0308b90ac3bf4f230bcf9f71b1326bc68383733c3921d4e49f5460e2c477699b9afa4c90d40110e4c872fa78dea557a55860b53c0114c243333a88b6f186e461253727a7cb37f5a7066009b99983d071f757b03d3b12625571f8a0339fb5af030ca3a5c9a31931ee393f2590ae66741e93a952e703a0656997563c539ab0a89336ddad951f7ef2a2ec29b978218ae19d7673dc37042fb4cf1cc573b91f99daa6ef141daf9245c4c8ee8871736a87044d75d61a6097740cb8c98c317323b73529756d427f797f2f4e9897a43f35b7c442ad0c31cda368939b6de27e7b4557c6a3132cb30d66ee345356549800c0dacb305828e4f8edfaeff32a960760fe8d03bcec2d14bf511229284954b71a3202c9db8628a4afb819ef6c977a399e82c6cd32a9e7cf1c8017f48527c24162582b79bac004837e668eb9884853ec819c4c0c04cf26675de0df3f334ac794ee1524e664b72236eecf290832c5138acc953da0903a8aabf9f24ad01eef9e33da5e2b080e2e5f6325baed5e050e6854beee1201a1572a90c3834055c8e805755ddbe98770b91791f6e5b5ae26f2ab74fe8c4b1a269123216a7b4f3625e794f523884e04162e91709e674809ed95225288f8e18c27521c5093d3be4f06a3deae70a1061dd02f0f14cb7ea816473158a282bfe895e7e65eed8c12542fbff3fa890ac8be3eeac4b416d84e0460ee144695270afc16675d61eed887203b300a250353a7994a885b2209e62502739c67e5931523a2fbeb506f9f95cdc0dbf691a23d7d63703bd93e47244c7c8cad2ef4684a19660036b27ad2e6d66edd21a77db1e10dea5714f19dbc1cf8438e90b8aa1788923daafe5f55d3916cede3bd1185d792b02aac28fcf78909544728e99a37c544667c51b7c191e5b6aa6d128e5527154bcc97221baa8071089b4d1d5ea4a8cce75c668b8cb3b5fb1b62622d6e4a38e0caa211447ee07d9852c6a165fd1523289fd734968e7dc49789c8a5e84538555ea38a6a3abb41b0d9433318c387d386c3be0d04ddc66bdb1499c722e8b01187a40ec0a615e4fd692f985a590c7fda0537f905fae6a33bae253f41f2ce11e7c02fe7df85c025bfc742ed0a48b9e9e4b2119c8131828ee419bb31a67cecf3e9f3f2a91160a16caa6fea02740bea39e286db039b12ef17780f7509b5268db4fc8b5bea3c3c0777eda3ad3bacd153dcfaf59f7fa120a4e0ff6afc7fa94f84630113d05b3d5d30d148cba3fa2b92a70820879611cbde02ae9fd5f326d30b007796774a67b04f15cfb9508148bc02ae404155eb4b15561f0e85bf8cf5ff262fa90f8c8c2b083d516f341c422fb9325f0df6cf0b31946473fd91acf252e7088dc2563ea08d5fca42e6718b89c59fbf6000c3dc9f82ff824a2acf3b56a72ffb6b345bec4af58c54b1603da4450bcd0b4f747d942bf9b5ac3024517c53658f72282cd93dc7dc79fa036610ecd5a2bc2cc2d141ba356006f72b7e2ee8f71b08131511ee85edaa201f3357a9db380a11a62522303866af0ce9043d8b00f933ade00f3e8e9a61176a3906551199bcd40bd3379c7f606e2691838f909a6041d6ece9bfe575a0f14433dce468482d25b993e800aed9e0ac14cc80c6c0b9a64d3e0c9cd2427356980d32bc9471053a4f4a8483e53166e82ceca018243df62be19d0a81e9cbc132615b9eb75dc3478c7264f4ade5004a356a11c62603978a2fca16a1b5622333ed1a716c26b5239da7885f645ddcf1f365c0094cef297564893004729437233a7f781f3ca31053f369cf8460af6538f07b89bf5a52d1c8d2532e330525b8f0fceff7fe6ec85b5fce72757ec9202d6db36af3528f72a45361eda4d2e15502a6c3708c5713e95e6cba7794d2857d035c01967808932839c67213f514914762a592dd247f1c66699b9dbfa1d89cb8707e0bbd9c430799f3c1ecda4738de9831885dd9e6b95ae68787c7f84528a1b6dd32914c1bc1da210bf246758f9e392a7504752a90ddeac1310279ddfc8426e44eb6afb1e574e684d4aae02dd45dfc77de894e043e7720ae910046c7e3f7d91717b44fb3396440d2f88682b3d05682b4aece5e10951127da7c1382fdbba2fd8a2914b5f58a80fcc1ad882c4d608862847502eadb0de3ef2233d6111c91ccd4cb8bb22dd8fd491ff6d33c7cd7892f771cd832507c076572ee3921da4bcbf30a022a13a861d7171d0eb42fab8d5fcd6595d4f8682d43c566ad8ec439c3b1a5e7a048848d3b3f75c411d2564326bb42b90ae981f6de448410ac74a2334ad6708ef1bae0bf545ba8200952c90b7c80159a0c05fb4f2d6934fe89e959d8d828f61d83539b2d22d381022e937cb1dea19e39e36ab8384dd81eba988f939dc44e7a8ef15313902573c63c8caf14103b88b97549af438728863f41ad01cc54460f3ff97232805b4c7fe67adc74969dcae26ea9e729dcef11a16ec04dc27a0f4a4a069fe2d3de12d2efa4a393fad6ec63f2c4b4a6988a4f650231c1ea87493ad7ee5a490286b800f2a2d414f35b3362fbdb2f461db8492a207beb1ba90ce494590f48710dfe5c0950bc606b6d60867702bbe2c0fef75655cb4b6dda5a3ca9d73544d2b0644d0fb7af24c0b9a12a8cbe36fbf8e3cac99431333770233d29ac169258aa76efa3c980eebb0652c60db126471af888c4a3068c3bad874c95833de74601e7be830c004ad09a2664d9cfa98fa5ae74736530f2707e1f2b217cf1a6434c2b471095d1c753a4800390857e165a4dd16b2d9790d0b82d7d8d8534595229ceb435955a41ac38b3ef77397ed7a09b0cdb38ced310d448583257720d1965009d6a775ab92e25b6cffe5671a249e358b81c4cf46a7c8e472fe368eb2c06dd0d8a7badab3788980f5adcdf6a3710d7791f4a79a6f9755cf0f0c47c898f381e8471371e6030c341467122c69f7f568f2219ab33534949ad1d14f16affd985dd9f5421f3bf4195116be594f2f0db08b929e0e9db7b472443e5e48d59c3f31e130d17f3ef02b48c4d95356eaad838c6bc858ba0412a974fd4df71b578a6af360f26cac7020d1992a08d5f1f78b3f9e2b51ff22575b555d1507d5a6d34fc1157c7d3316ba2477ab707631c5e188b6b5824b561405e21c4bf51cc6383bf1f3f361dd628c035e6130da504e1961f311b7fcde18a81673e3d98dd654023dd127b25db7dcf3e8159559ae427e8b8e4bdf4cdcba0d976b367ccfb721bd2aac85a196d966e6b5b2d535bcf7b7450ade6d5dd6e8b048a0eb45c469c4f7de46289d8aacc31c99dbcb699d079301143ee59945b501eae8e2244ddaabbccacf57557ca80258da77f3e6bc1ec1e4d035661f36b2afd510c1b3d681df5a490c7a5259c8181b0d3e5725f43d234f0913b39b55da897f5a92e30c7d0f4a3127e68fdb0460deb0b7d06f705626ee4fc89659fd89ecc50d1674458a39ca81e38e3ae658c78e71e4b8030e39921d3e82b3ca9c2ee85f64b4962303975fc44223449b2b0dc67a3e170b9ca56ca45498f45ec1290f1601819b5edf62aaaf2d26a4ab97e8460901d6d6b432e9b3175febf9059b967bd2408cfbe6b9f4522beed109b6847f4705519777cdcbe801383d1126b70e75dd74d8440e71ae12be2db718120d11d555d0c9abbad2e2f3e8243b4f266c091698a4f4dc14bc656c895b82512b7f01af1fc799e2c6f17cbe855b14a00d154172f6bae19d0e3817d5a138e5ff663dbfc40590e7a1ea2165addb434a241da9ad961904ac881905f4b73ff745a4aa2b61c3a5bf8f1ff0439e227698b1d6c4002782a3fee77a815460bc7e6d5a9a96b925582182a7e41292ab0303af66e26009f12633aa72b90993ef0e2d07f64c8b198d48395518f4c34186c03c044a2314f5f84d4a235110bc53b281ad948e9859ffdf4a934aa7f0d6e23bda342a472badd20e3b0dca283ff33d0f2f3f970ba414250fc1b642c72e60828e0ab654b558aff761e2d084c7e42f0258b184f51ba957d6e98775e8bfb8f8b0c401d22da712a53fd5e38fd1807496cad6f0b2572809d840c7e2cd00e78cc798d71fb7f2a487729940ff8e918705784452454520d11c79c312de29d146e03812620279694540c2d81ca2b025adea7d8dd41089a2b84e2b8cb8e70f8721e167e4292069d7f2a60d0ee1fad91ae458841490561c95700fe8ff68950204507fb5162ddc966dc68ef37732216bdd01539338528eec511ddb62d9e0c2efc34f948cd41d459446a0aaea70150006e0eb96948f28229c8bab6fd5ff400c65f41cf193c55f9f9bb4ce4003050fd3a0407ef64bfe4849a4e8cc4e9a1c091086d5960df734d1fe9a12f9a7efdfaf918b9f2a2aacab6080f9c91ce8d0297a589cef64d6c4d486028196ed022001b6371631b3febf2898c04dc116280d405d40dc9551fd7f2f88009c035631bc7e0801f851b821db0ba2270e44316188a37e301be0b6730e8295246f89719432c4a80cad0eefb6d1929047218a1059e96de56d17dbe297f440dff43c8f05b88bd277f0448bb3ec724bfca3755c3521dadc0c3a76a8f3c75cb1895d7a7bb0f242fc1a1a202d119e94231e9f1e13e5b33757a241ac7f5ae9e34159c24d1f6c6c71eeaa396fa3fcbd257f4c1f90ba0b91d964b5f04673037e32ceeca694c1a8295bd7557b94f7496798a2b6e82a8d1c49a2593eba043619a74423ea34e08fed0cbc1f69ac5be79e0203cde9981cdbfec4e200e89e6a799d6645aa3692e2b9652d3fae0a3e81d2c4a7d17fd5ac9a172a57582751b080fe31a75b91af4b1d2abecf5a19e768472d73240a331ede7d6f15d2d7311f607a0016eaf5f92cd23371e22a552ef33f5bef05de767938067b1e2ebceca711943c1ec8a52154a199cac7c654382af4e80b1cbd2cc7146dfadfd973a502e7fbcb8400f9e7d4f3d1c9fbda7419860f7e9200313d52fe1b5ff7d53c40f46284ec23a09db8121200a612ad18967bbfc3d73fe0f5f64c54643f970df682c335a54ae10784d3035a6fa27d5c30145a4d07eabf72d4a3f6e798c0cb5e8ee442992e861768ca2eee8d950c28abb7e5e89833d77d5a94191d5a1d2079b3fefe1cdeb71f780cc487d7ba8cfb453ec78c5128b15129ce8b612fe14af47adf7e0be94d9b6cb9b79429c914a5070f08a7072eb8dc1a32df8463837cbf336a5176442dca7a3b28403a22ede7ca154dfa807e40031acc66b0ef3f23fd9dec765a8ca145e9210dbe91ae0ac23a7b0da82db66845047714613e6b1d053e5c51edcc9f218f5710257aa04323fee7606144594d96d6431a34dbd190835eedb5378d3ceef6d91314993e506d931aa53e297d8d52b9a3925faf0c79a4ac4044444040ec31501d868a10410945942821fa21d3b5ef725c1af86f98ea0bb77e35c1d8af0e0321f367a8ea81be7cf9c8d77cf1d67a3f6789b6431af185588146b7da12db4ad528576a810375ea56e298a02f9979bcaaf637f0784d1e2f9503b4df9e47fb4de399aeead07edddefd446ae6f31b9c5fc11a5cda68517e8fa9f32f079b82b10c1572242061b247e7789f04225208913fca1f3acef2eea9a3ce953a2c744d843582eddef4a384d9973694d23b07a5d142275950f1603d943fa1ecd1a2144f602b4b1e4a2e0dfe2343699003c10a963a2857cea8555653899bafcdd0d3a20d0984036ba566a5ee800879bc512a95baf4dea8243653b27c1b9e6185143dd1c434838978eedc83dd73dc3783dc570dc4b1c2f237c7ce1d779d77210da5ef3801b6f93b0673f48581a9f840a50634f896c2cdabc1547ca0e2c39ddf75a6efbef4f561287df7a81f4696c1b44a2fffdbc57935baa7dfd13047794b5daac1bbee1b04553598060d54c1d01caa6a30b7a83d18aaeaf3e54055fd796b4803be112b59b622324b597cf818c2c794294c5f7b19dc7976f06b09a0bf343cc747be3c54f530c506fa9cc53f33c4ca8f2bc35187e68d483065ca122500aa2155fe434325105262c4f27b3c47c5a1ca01f4fd79de439f231cf2b8872af6f1a7a18ac31e361eaf5aaffd7ca688431b51899d6d058868f643ec4f986d0bad9599640c345f5ca12563c81f4b97ab5c415e0e4791e60adb922bb4206380628b3edf31389a98afd63d6de188c0feadc2c31dd9c8b9e7155be534da3e7927061a75a3b75f15ecd7c6cc3c04a41f3cdfdf24432e073bddf403ab5249d159fc6c6589db21f3ebe460471da49ecd78dc91c7eda7c1124390566ac1734706f15072dbb9411a6edc6028ef3468ef3f27cbefb417ba6bb841196ff05072b577a971f272203f572ba7f1c673a6e016418723d006fbd70a234d202bdf86b595c888a7dc1868b49c259de52ce9605f8e9fc8880004d3c07e5150c321e8c5de8c1d1e98fd6b65cbcc3403924e8401dcfec4d7284f8b5d7fab3ba25cc146c6e86f118765a890427d097ab79361e9ce392703660d55f4fbebfd61e9108f0f0f1e1f1e2ce350064747474fa5147bdad552ba74dfb12161a67db1458322c28eada9f3618fa4279802c611d3a75dfdeaa7e1d5dacf65f3fd3ddce13fc3ca346cf9d67ef66d8bb7ad4e0bceea249630374b2a94054b8b2c65b73ba55a0ba9a6ca71a552673261612727d4a8a37d6a93bd253d718bd3d4dded799ee749cfe3928d77e2d3e9748aa1459a7b02b1b0a3086261c739bdadb5d64a6bd974ea1086bd16cdb744f13ca94204f00d7f77a79853a954ca84d2c1f9558a65aaccf881db9d4c286f86e4e64e49eed3f8813b1b1c57f765f4b9dd918bcbcbcbcb0b0c4c4c8c8c8c378312bd30814bec0b282dce9868c02bacaabbdbb9ba6974b59aab95f74aaeba5f4adc4b7d7979e917f9f2d2fd6456c527c41156181d51c5cfed50b266f9609b784283211d127cc8d96ed2624c615d0b904d6301e1332331c201274ee4079901d3a2b42a80e239888569f9a3a9881afc80b1fd7f5998e28e361dc4f22fa148112040ba9fcee128af3797c43695a7b452cdb9b06ee1f851cdfdf9e953f6196487262614597e13a2c8c76997f738ef0dd31277fc9f20a6b8a6cdb0bb755a13d4c94171bacb7203174225887b046064f9c7561146d0030f741072049059111749749c5872c7dfe108894ca725be35d2455afdddb64a202731605e7d30d65485b4dae483fe16e0a0d6ef6b39ebeeeeee39a1a89cd8b1f5467ee8b11fa745d9dd7af9958891181021621bcb07905cfa0db28cb0fd2d7883d4e8cee94b1871c70fd244a0e28a377f5fad63ebb6747058e0f07bbb5c944410b7e1ffe572911339dcf19b5ad8f3c14a1b3a9e73ba7426cf172d6aba951d826e4a44e7bb06755a7cc3afd3aefebe5a67a8d3d26917db3ed241255b8243ec73119470a77c2d320de3d0a472ff5475a872007df9ef98efe10e9f233284f4c52f2682ac9a1341765691ec815c167484d85167c6c50c160b8429b200e12886831aba7e7eb9aac35791fd1594af5bca40dace91b02682c916214eb1d8abaa1baa28315160479024899124a80ef6f355616129a0a2648a8fdb39d1c847a614cc56abbddb42b29e6cb10cef4a5186624b476cbdf8bdfd68883457fee5fa8b4fd3d2a17101cfe11711114cc469b1ff47152344ab14d8f15b92a4595114451851ec5f28b5bf934623cf68115b25135ba56d1982df2214d6ffbfb636aa1324610453c48e16090845bba75d3d9207ecf9d90019e6e89e16fb775a6c1b3b261eec583f96e1ddf6b934c8cfd1dc5f3fcd90a577fcc0edbbeab9e02f9fbeccd17fd9a3e18a0bfb37b6831c8e768003100f4c14e121882b3e9ae8626803f2f6334d277e2ce0549bf934f25b94a3d832a20118f8cafd82b02f59f605613f4b1227ec81c0245768188d107bd4a3e3a195367cca3b1ba459c9daa2bbfb7f34262061477b5b7dfdc28c34d7674f8f6fda67539adae77b43edf4474a3994d6ad73013c69edbf242b7f6b554aa99f6cd8b3db943addde73d1043fa909e512f30285100027ef84429dbc95caab5101f0690230bdbb5b85a2c2ca6432198c6f060514bec60233ccbe1b9f7781181601d47480ab210058d3b5241c3978768c5ed8e8115938f825611ef8bd60b106301d09c7025a908941c6a3310002bc902180cdf4da4141624f90e7d4c059dcfe1a4a1bc2519a20c6f3c49918141414e4927a6199bc19388cec29aca08e01a450b5af0f5371509abf49a1fe58fb750d8ce3fb6030180c75b229c4e0e1c18b849442c2c11be01cef5b1a4917a8e0b61cc16d2d6c90511b054923272d0ae00729a490428c02a8305dcb819423690b8e991120e1d091c308a4180e1d2d760e236cac00a794ed1a859fa9cdf9a14e9e9c93563129163299acd326d5aad0624293c68c5e603a6d52ad0a2d2634694cc8a24e3228541d754e1a139a3426548da4ecf1a4c56167b3d9cc9b952d4ad326d77a674c489e7e06c92e838282824c53fa9cd26342b3d9a4dacc8f16139a34267402149d536d337137853a796005de5d3cce9195c6e8f687736d24c18442b252266560773aa1604c31d020ea7876eacb942acf566d83051dd3024c5e0bf6bf6b5127af9399cf5d2ac32f6a8f8d27e2b4beaac33d9c937a9e716a264187ebf57b4c61171e71141e360059779e6050dabdcb5b9dd3b30b980251ad9d93d7cdcc7833e88333683d9ec7a8671d1478023d8d074c60080a32a978a584db473d4c01cef15c36eca27f74d43f92ade02338e1cdbac76c369bdd96d12a9537c3f42a6f46f77c029b4cbc7a1ad186391b6d039cf1956b85ed99c146cf0c2db60d169c4e38a14e0cd4f5ca08520894524a29a594524ae74f26246fa1441276f0018840092c27f87358a6e339b2072888420a293dd8010e5a127d9c76c94801185d801f0683258de2287f664752ea5cd9ba96524a29a53e5ed464c72aa7d8b1baa59256b9c1d135c9920e6aad2e46419dcd629632aa8187ca02c81a0da54e4b204219fd00c6db4276325ae2332d01886774054cb70424531413a04a5b7eba2840525c1740a528462a1758b82844a0b06d91d5284cc8685c0c6d328a9694a45cb9007216d7d768e21b388ee3c2508a10006861248588992d6052aa7c49529260d22256f4832018527c3663ab72dbb69538192a96bb5cc4e4478c0dace97211931760616244ca923bfe0b22a46861bfcb454c8c70830862d0848718217bba5cd444080689264b6290b000b85cd4a4072e576c77b9a889145fd4448bcbb66d2f8c0c418218205ada90090332ad877a41a1508cfa176f86bdde285ecff3426b81dcd942eef4d1e2fc6971f6b043494237d0f6d12ed44f47421a3be6b98fd80c621ef3596c168b79cc6731a11667436924313f8ab951cc89621e8b792c16f359cc6744311bed8a0dc5260ca6061dd3747cc3746bed944cd3a39e86a5e738ee51262b7722c176505a9c4e5a9c3624b047c42e171d01643e012e171565b91d6d2b22ea8e6f53021b011eafefa59376a13caf93c63ed2d169b5c2d6d2e27c1b22166807a7d5ae173f79805aad56ab35da5c9904f2241015efebe0c2b81464a32998b8edb9f0726b905ddc023cb17c611b96d7177e3fca98e9e565a32c59ae9402cbeda3767194235cb9f32594cee99f96b9246a1795b176bd789e32321476c2028ecd0bb065c0b6f2e339bda567ce9a3f25261c7b084a23b9d3c94b38768c8553387e38b67f3e5438da5428eba4024cb17c6d38eab8f63d7094d7ab264781630ea7a005733805479e4bc3715eefeda3c0f12fca07f516b8f4471d17d573e98f382eca8235b8f728907b6f343580a606bf5e38ca8b01d32d99a4d70041175d74a1e5764f410c986e178e396e29bc61e2409a16e71709516d6860b9c52d043f708e8da58feefccf6530ed42452793406400c5e49054f27dbeaa52269f744ce3ad0d54f5509f5f9bffcc102b35fa2a2d74a1af46c3733808339da1bb807631b7abc559aebf36a704a1d6e834d3ecc13afdfafe12c4a1a00efdbabdea7b15e035338465dd92b0288ee5029ea3fd940c658ad8ad497383355d198ecfc51d6914a0fd235d99359149252d893c76fb639e435d8807cd6cd78a058bace9611d08fdb1b15c200b76f90e3fe40fa943c3c0863910dfb40311b21ee4e2107b83dd1eb0853ef2a5c970477b1f1e2f2d5455ebac7e04bc60b771b86303adbe1eac54ed684fb5779638c3fe09bb2609e6dd23c94b4abcc0ae14a59c4d91755010aefc0e6aa73abcee48f33c8d8acc0848cbeb34a9cd40455df690464190715110863c879180c160301b828eb8a2048797fc8672d4563a875f3ef5366a97d4947079868ce4d76aedf7fd8b6258c48e9c74e5ebf09c1e2a3d1ced4deaf19cd0daf0481ec9172a57ea13ca24436371d64e8bb2915a0bd85b1c06fa8ffb00fd8703f12027e2428e64821ba8013984851d5b3bedf2c642f3cf25eda0af839865047be2b6ef5059294020c15e9fb37e664811a2b81d76d0ff0b0cb582243976a689182cc96939ab69c4d0623f138d3a34765a3a1bc82ce0b46810b578c6e7188f1053e434bd0f76ac62cb73b69f95c0b902614ccca2efdf95d21da7c57e1af6b0793328b5c131c347cbd7021d923042072222202011d0964effd6ddf45bdb5ae2c04af66654b1fcf43b9955f5cbdb214e184e159237d98a83d9c4e7088ff6f37dfc55ac119124c9d050922494abb4886b4af8b563c7911ddb6b13d8c209d0f07504546586d26247d419753f7347c420f7bd7544ad8ea8336a17c304a95b07e1421ab15d74bc418db090020bbae2e5285df0703b6fb95c0a846ff8795c0e476a0297583ae31bf99d6c7a0c60272eedb46f7094d70053c8ee660024bb21f18d7ce69b4e33712fb804e1b8859a9daf466a754f31791615c010920c45f6220429248bfd107b010f41a40d4047ba27404395a13cf1c412a413f7c5e5a227a028b1eeee2aa72ffa3bb4a7e10e9f235aa87295873d6ccf2f3a41a63345bdfa5c49e72eab57087104c9c70a60f00217bc5c8bebde5d7777f749352f081b83168e624be7ca3c480d414b8888404bd10c765842152552b3caa7147ca1fb9b611fd1b1e7b06d754741626e1ddd7c102589ca7d15a0ad4a095a454749b75e2e3a42da7658a2a6a8382a82722493f1c110b3736653492423c0c8da31f14c7316929bc02318e61c66ead8b058f8089042df9992b6817c4b1447c78675e5b7f8044841748699d765386f86bbc3d07895e9cee499c08ee8fbfad378db7aba7f5f7f933bf9d3e79ceef3e3efebc994eb172bbabbe59c736a74dadbdddd6ea5eb6abbb87a77f74c13679e462744f19e692251cc3349d73177658c6466c6016f8b73ce49d3dddded32d3bd6f879d46690fa9b96bb4dbb4dfdaa5e5b37c57ac740c9d536330a57e6e9c66e16a5a58bc420e19a35f8747c6ec6737ad27471f3d08b4d450c7120fb40f1d9ef25cc97b6cebdb39c108d6a37be40a1ee018fd3fac0f10f04d1fe9589254ea244cfe48203a742c49ea4cb46ed2c84b019dd27d52267432a1d3e53b9d2ee964c284d6283a0a82d92ff4ddde4d1a8e7649ef7906c68263d37a16bbee58bcc202648cfeed2b9d349659dd9fc02e75a5da598be3d697359c9ab800d109bd5b4823073b3c30708c7e2dac61a5d1fa3a656f9d6f1934daf52cd6671a1b97be3cd1767b923378a69d185ae46956c3c0628dca46262c3468681bd52852a93b1dd82a76002a3a1ac1cbea4e1943186adb44f886106b599f73ce404410369041d20a7630f49a5a16779e60be696190607251b8f42b12495cfacdea02891baeebb343ec58a70fbb21899949621a151e7c7e44a92cc13510b149058a1889e4c70b4884348bef582d152d4a2f24393a1a92f10177a5480e64781882c86888257418228b133b2f170d81858c0f40ee96acdb6fb1d8bfade3ada07eaeb92f7d73618ffaa54d5eaec4f597b66799c08a4bd8d15a6f7fab792e789bbf149f05b0a5734321ecc65bfdea5b78c3370b23d640816d36744c0343e7210eec3ff74d58b1431a7c336158d500c1ce1fc07c1544f9f30910438a5294cd2dfd34ebfc17ce317145b14bfb39ef58efa43eb52d2c7d7aa53763a747890b776ac8be85cc3493f6803b05d89f2750d51ee8fde979bc3f7dff29f439d221cf497b55bfcbe97d545aed50e500998f791960de25dce1f2302ea16519ef79bc5243ed927979ed9f66fad013763e9216fd67d8432d3a187ad882b35e5eb5f3f2aa0768ef12f32a19644ca8d242d5045e3ee66548bd4bb8e308cfcbc77c2a5469ffe261bc0c319ffa54b8e3253cb285aa1adbcfdf7e86aa1aa5ef2f7d7f4ca872c08b87f12a2d94e1c5c350c9f0bdeabf573dbf644295f632cfe30513cafc11d3fba8eaabe87c95f631a690e7a5861ae07179fb0fe0978c8c0d7d5acef2770977a4c21d98975035430df0bc7cea1fc02fd1592a07b87ccccbe0f231e18e233c316f7f472af4790965b031e10cfce2178fd7c8b2981f5babedfb5f9e5fa99f797ebd8447545a4843e9fbf98e3ce37e034b1f03a6fe0544bd0ce8f230a07d69c27fcf9e4a7b2ddc81f13d268447fa55ff7dcfe3f5c284ff429fef4d088f6cdff730bf43e6777c210e987ff1263cce1526c060ce8af9d52861321ff31380f955a8da898979f97a7979f97a610d06a8d242d5ce8be7abfa55e803e355e1915283aafe17a0cf119e170f03c65b97df01239440f65fbcea71ae5085ecac17210e1536f533eff2f6550e70f9d44fc0fe4ca8da49bdea01fc4aa964902fd4cb174abe3e50be34502a6911498b489ad0a1a113d8481a16248404c9ab85644e64348432898882a67732319b09e6c553df227dfaf686d8b54d27b06120ca0776d656da35368cc8352750da281cfbe7fa7750bb54d2484e69a3ebd24a8b7e34033bcaa3d9cfdeacdacfa9fdac77761c17802decbc63f560d07e0b6b8b9ee2516a18a3ed3beebe54006e6ddbf6168bcdc18eadbbd168b10cbedbb75adb66a5b0daed96e7fc0d9408020f3308c2132c5e5b873d5826b0e212a5df9e46ac0560d6e6dd1ab6f866fbb175b79209b7bed401393086169cb57d83dc071899c814802d6c5ffe7e7fee03803acedab45008fbdf0cf7da56d24a30a040a1e4d677aff5abf5ade7dc106ffd597ac2d6df6aa86edd7edc4c01d8c25209db68b66fa1552cb03a214e8b3550ac94a92955a6dc000b2141a6dc90b2625faa90c2c2a88285109ec4000981082242c0410f21c44420c48a10806280d0e2b265cb86f9de66762e524af62ad7939b1dea74bd17cab980303ccd416bb4cd8a4d2e5a4ab35a57a2b9a9fad1eea695fbaaf566cc9294eda892a9647489ae28d28fc5d03be5cda05ef7d4d169b54466c9709c342245f56c1bd1f3aa53aa75aa9bc6eb0b33d49aa53965e5eac7d56bdd98d99968a3946a5a9d93526d727577e7f8519073cef8b0f29db92b71b53bc95031670e7aa7006e3fc87933e6871500d79bc60667a57548a78ff3697f93ce979f69be9c55243645b9336c75bf7477d1f31f03706942f04e13cccac4cc1faabb4d5ee99d3de79c34a82928a5d269e5c2fa7d755f5153fbec64c94bee3e39f75ab2a54a29a5eeee5ba9a4f151a7532de5dc1f85aef4ec73d378d35834b97ba51bd50292b953cbd30e5cb7cf3ac30c078c1c741d05b97bbbd62ebd1ded3453355116747470e8e872984adb0e4320870b71382b213b410849e3362afe3b0b88ad760838ae9492dfea69606ec0c0a03430c37f7bcfe1a84f0efc6c47e926af4f36f25af2bcd59b316136f68eca86e1afdf2ea5853c2708496f9ed33bcec430931575abdbb65d717451a8f93f770235b5ced2aa276d9e96849d77f4244fdab624b4eb96644e975b6c490c4a776f1464209de8f2b3a42f7d1c74e2cc39302236856c1a0a78dc294b52b6a073ce3989ec514c48e6eeeeee32a888296768215dba74e99c16427388910b0bfc879a80737c871d82041932c4889124d20e5d4b43070203ff61192e6ee436e898663c2941b22119e29ba65f74372450c82424b74f611656d250f24d7bbb8495924a5b4660479f25ddbe82a55d5fbbd4c1fd249b27e8eee611c2cafd3bad7684a0dbdbd4269d25e89866046777aab6a8925a9ceeaedac2302097a7f4dd77a57005e4acb9fae9401eaf127834c1292d4e155d0139f255d0fcd50fd00f21f3573125ed92cd95d09daba13b574eee940e1eb56b04a78056a683e094768daaa43b7faa92545b56b0e9ee3248ee746d9a3a9041792757661f773e9529ed3bd975cf5f9389813ed8d5cfcac7233d48d435d8ddfa5dfd9e0d96ae06caabb96bcf5ae85fc3c31a66f4bf202f7dba03bb6a8b1d8bf3c7eec79d632907777e6988dbe1cee7805806f70db47d273d2980e87ce7dcda30abdcf9234b713ae29b395f8518586a547d0b8a62f5b3026a71ae7cf4cac7eaa7c569a4c2153bae7eeefc2ed601e94e48bbb427c29d10b00bea80b4385f052c169c792ae89866bc4172fda9e98737e306a845977206f6e6c70d90169dab31bafeb546e62cbf09ba11526df26b8cda35de04d51cf9d7c888fc6ba0b44b4e9965a949f29a2bd76b90aed76cb9fe9beca0768d37429788cbbf01bafe3742ed1a6f806e806e7edc00e159d9e592b77bec9cdceea0804672fa0bd8eda4c9dee6e1fa0d10b39cd638b9393741412eb8fe2a34b909bafe35b21a27eddadebf86a85d9b6c9992dfc92e7b20e883b589b10c1779036319d2bb6b9cb46b768dcc26e67f03f36ead93ed936a9ba9c1aecaedb1f08d0b2e5b1576d199cc91132cfa8f3256aecbc06455ae3fcc8625f5e33609ecb22dba7c7f9be4395c0ae885b5a46030bad8f5adf322643da9135e04d321f8cc90eb417cd32aa2185899019060fb674b70bb2b24aba1956c65845742d7df57aa1f5cff0d5411f18dcc6cbbb3bf01dad56ee7a611460a88ff0d50bfb0eb46052c7a0d91b3bce6c85940edaa29e24605ecaa71c2a2bfd7c8b0b8a05d19d6a8a0c48e9a4cdb8176e439ec5a11e138634473a2c95886f6fe3ecec86a905d4850bb1cc87d057bfc7c7d1b23d77fdcb05caf91d9a46a64d76566d77f4c01b9fe1a3843c119a11924334a9ae55f679a5c7f19ae627c630099d90d92172994935dca0df10dd5d222456a91d624bea1dd10dfd03779e11576529b1691a816fada0f4a472c034937c43234570c16e98f9d12aac2a574d49e5c4a9f5a41a7a049e015681619837e497e0c1bf954854b7f9327b8f43bf955d2e76816b982641ee914f36ea0e99654dbf3159816e94f6abf558b94fea8817cdd9b21c398ed66f4084546406204d95d828683c580bf0c2a2276c7ef569e6db577125d2dfd7ceeeb3f7dede79c6108b69fdfb5d61056bf8621a83f9f0bdfe7c8d883f632c8298278128219ee6839f734b45c586d0d671f40fe16721f40fee35c41c796cf119ef9dafbb0b354ae853e139c62c10bcc14f9e868b19f93361edd1388aeb68b591a75b55dcca1457da3800761065a960a2566adb26ab5d2b0bebf576df2d86417752edeeaf45fa46da76b1e82d972d66d5a6591e6d8c2c922ad3a44aa492903e21288ca154809c42510dd56e0359092ca8c50050977754a8a28a6f8b1b2e4499128981089a20648a208e283287c0ce93477b96dd6ddfbcbd9b639e7f48298734e71ce13b4a0eeae31bbb3c9864ed6e9bc84d28d6af487ed078f4a2973a2b68ddbea86844352a7cb57bfc035661b6cf22565506c69d313286630aef69365e588abfd7403c82087121411458721b2d8604d60c15d214522e8d74385141a5831410e58d0e0a575a13d4eed2376fce4b73c674221f4528792d4c9ad9ed8f203501114376c4786fa7b2263c40db768074193522ab7a94d9f735229a5fc396da46ccc256be3d5f06b69c972d6528d6a9aa6c58498744c33b6844a9bd0dbb8ea587aeeb30ca0affdd4c051bc5ae9fb5a7bee3bad01a5a74f4be0487335fa96b369176dc0560304579b2eebfcc19a6a77cb9e75b27dc2b08ce6baa2611186bb5a981b2616298eeb5b3b955f8b6d695f772933fa8b74ed86ce222800e8c0320a004b24b3ff0c5d38f5957704548692996557fe9785cd9764f9432a965f8ef365f74b7672fb4fd031f3ced1a66928b8f6351cb7af736edbcf163864668d8673bad61e45c73c27759fde3016e645fe95f27bcec9f4085426c39062c5545bde0e12b1638caee53934ac4eab059a198365b084390f8c0418d4e0090e8ac0e087255c34414d44d68617cced974878118e4ad6060e33b8d2050f702022096d8b2230812945b1fd7ce7ed31df07217448820492143d18d1b65c0b85f58860b95be8af5dfd493e298990037aa23f2ed09e14fd9cd808160011450f4014c10225d38a2686684f9ae8e1448dc0c4116ca054d89958a2c8896b0da46aa8346e3725b16468060200004102d314000020100a8784229158309c478234fc14000b708c3c805e341588635190e3388c428618630021841030666486863803b48943e97f7b2c8a3a158d34ba0fcb0fc8b6c62eb0733e0274565b359e90d8c22ec8de0f5afac0defe6518e667a2afbb601c38909c021bcfcb05f934230b9886f370490746e4d242d323fb5f62ce503940829f4f82397098b651590d3c2a5324ac5238d14aecbf2c1289625c3069251fc1a77a42ff6fb7f3ca95d4ad75f06cf4b188513796d26687849fe28cfadfcf1e63a00c50c38c3f4695cc8d35d6eff3282d6b50337eb3aa9ae2b8a3e28a6c4736760b2df4b8aacad4f0a96aef5d456d7a31709d25823b513239a60e629b3a36364fb8c235faa060202e9ba69b19cb4206dbda2f0284714eb72bf37a8d7537d35790d33d276924ae7b2c2dc5e9e20b5f85300b652061e9472d3d384317749c0264e40e741d6c3fb5388ae39d6c2c2f0576e2cde4d6f098a48803f138ce6e9b86160c7ea24c456466c6dbcd39686466fce1b4cc0ad1d07021573c071c2b6125dbc55fc91f97d81ee1f8cee886f6e298ae5f08a7261d4852dd429a62612d975805ff5d19fc65d8ede745e1071be8b789cefcde807ade3c7e1069d6877888d1144f586539bc858992968f218466ec701fbc4f76a7062c598f38362ef41095e31af463395eaade87142d8b5664cf78bb113fd83344f6990174e356722be958239d933e11e41bd9053da9bf79a598838060939ad11dd6cdb4d8906722dd609a10c876ff3a3d65f448246d6a86ae7aec47c090cdf0a5ff187ddc75af21b555c89e410e7d8ae2ec701129809e71b0152d2201512f35f7e89e1365dc89a4af160acfe8d4468d4277c6e0ea02c56e36cfffea1b07013c643e4e35a0c17e4f428cbd6822b6ed091c04143f91bc8402bbacbf27c27c7fcf0a5d5571404e742fc1af3290772995c0fd21bc11eff4d02ecb697e4d5d4160853826324f98e621f5e6415951fb118b77c0ff52ae45ed01032c7e7995ae5e29930a17659d7fa552a840a928ea6a2bfe1732ad4cfd10b4f2705f94e7b074016ff515d04dbb305681aed96738d37c19043dbbbc9c765d4e36ae9558ec3e59b0e4c1e18a8b19d8b7d51b29e33b8725afb9d32df72a98a36eda3459703bb8726f9acb09517dc266475d6f478e2ae8ca78058ee72fbef6ecb2995605e0d98c97fad1c64f9c05a1540afe7016913e9a4f650b15fde963bba190704acdedb4ccf4e1ed89b7b900e6a67da3dd040d220583c9e7f4a35c9752adc91ac8323c431476f465d22bc84ed9f3c1cc3ea5ed8bb6a7aefbc417629e0510723802e5c3b78bf8250c005eba58ab8a8bd7a53a0edcfb24b3cdbc88c296b07e53313be5963d68c5cb608e5d26749edb3230ea01f5bdaabcb988822a32c86f3e1881d4fdae94923a01e771b33fbd3046e80cb3339df7081e374da3a6a79e8b4eb97b1048d23ce9e7a6c9dfb099cf593582817874be6b6e85f65a381cb6ad7b217d4839b0deef73228cc12c604d1ba3b02a745abef0617520e99c5def392ba0e7d8313b1558ad3fa844c5f29b7ea5c268144e030cd96f778b871ae36095d6013d2e861253c1501e9ec1fd476c25ea29ed8a6444db939e9e7c5ac2c46a254f16983c34a24cb86bb229706592f7d5e40984cfcc29a943e479c5396f86a9bf08a6d7a9dfb64239ddf9b94bec84ac25dfc808f42091e1ad9831955ff96d2c72896bd394d257670bfe2282180b478476face32945d48194d0ca0db1a07214676c688ab6bd7e0263dcb8b48d14ca1548867981276d3852c292233f2e01add41bb2dc2e4de7f94a760cc0481a41255958e5a936c9be74f4ccc6a1568394d4056db74068a5e9d9644f8c4d54fd3ce3a6f08feb2eb5995e39321e82c4e844e67138a75715634078ed0b336a9b4c371109915e22336ef054d59cebcf22b8f0014552f2579d63c0705f2b429bc2963ecb32aa4ab5ef7acf38c62ee41e6f5481349fc88076007b336090934bc0901b692563b37d902dc23f8453199246f978af0ac2a8c56ae4c8471c747c2e7727b0caf1115c9aa5fb26dadae59a5c99898278f506906840c874aad3bcc3f42e106c1b931e54ffbe749ad6f2a1e962a6a1b139b6c10f5a6a5b18e54ae9858e97cdb668e434b93081e328b276fa4841361d4046e57f8a6d26ba487af8c79908e5595c1016c9394f97080c9d42a8501f47d85b080862103616631b0e5d139804c3e747c3c763642b773a72c0dedbedf97af548947a60b3e40c93e74fd7c3960863891b0d341f076327c48031abb4322a81928709f08d68bf12868a98dd2ca8e818c8a14c08dbd8c8002ab4097840f8d89d26fa525725a15565f8dda569764020c9988aa969218253fa2d45715a0ad3d5452b1402bba7baac99d80e49a71d942c85f4d407aa7aeb978987036bdf8a21ec60524b69c9f39c400341cd442b8033cb9cc46f094cdf76fdbe243515236e9b569bf4ffab90019b5979dce3ee2881c19002a6042f6e820e7bc83f0e35bec10503b8194bec8ab347fe30c28abd7364e86749c79246bf8ec6cf309bb53ec315745aced21c764f4406340e83ceebe0488e2852f99e27b00c3a9cbb4524871e30bc78dd7aa480c25b174230b73dc48220e51e4b861d255bc86bea62620dbf68c5c99396e902273663f0b4a787a0ae5b8f94f77a5082f3c20448e9b716cee0c08312d48fbb1ec827ec687ca2f1445dd056a173e35dbeefafbdab0366a7f1dbd7c87841f1fa7e71a1000676e698e8dbaeeacbdeb0fa9cc14324e81fcfa714015ff350a6c8ccd39126a5e0d6d2c1be9d7cb2993f0cacd0e0985d7e4119caac9ce029d3f27d26a48a075c6a114cdcb15b46b8460587e64a1808ea4ac075aa4e2cfdd90216b76b809100286f373154a4fc653ddb90e3f10e22cf916c2e36082faa6c7ac3bd7abaa26ca01162bb47bb33da3e8c12ad6bc053716f9a7970a3f3a95fa56c725087c1fa9cd082bbcb25800b67607c7c7c15321c91c17498d794ab02b0e17ae64e31563232d4a3b29c88dd5b3db5ba60fbd884d9f07748e93120a89c3413413f38d340f96c515beb7d06c955f1e0378e6664c4b188409bbe05254389640cce502f0e5a6cd7c3588df478023c6663e59fc45379dadfb9d05c37d389dd147ec45c6491c87e933ca684d7f636a712e48ac5bc6bf12634215b54a784990661d5f91496a0513f83555a361f29a734404ad100ad13b1815ab5ab331713060eb9638c4dfe72c0466af621193dca3fa2ceea0fd176446b2cbcc4633a8e1327a0ad6533e7d85a1e33d97363ce2e3eabf91b590ab6fbc653a12b20a5ddd9eb82699018e0f46fd761db87f53a7ecb8a8d235305b8d12bb90b06b85c0909f4669236f12b72101150b3d1c9cdbe9c3bd928aee94baa4a6f90fc45cbd162a52b0a156918432d013c1d76c56efdc7d372e512b400bc9c9dff7e6053a46cb1fe8b6f915c254b67b547b0458c54c10f9147ab029aa8e697e96ed9a21304db4eb4f33655f245332b60ed843fe7523e6c292f30c6cf69b9bb42446ca57a4793611065ea9ffda028c404ed4d8ba7bf17a0752efe9b3b0b2b680016b8509537df7626dd14e4c1e640d6776089dc2b2048a382674e0d6e7b6a43384079e2771ecd125a616d8d6d2081143df0886156df2438bc4192a958ae372662a2194351ae40b91449df8bc4d9a2280373718823175dcb23dcb38a7967a5672badaa925c4d9445df09992d02679a8518146ae7c64984a032b81d7f4117027f1b7eef8060833938d8b68ce45826e04714227bda15ee94d85f7d6c0273f7830fce43691982194f4aaa5487f4d0196baabc976f94d2e238d0b2c00a26686a6ca1fddd3b05afffffd901594d34996e7d45373af44cf950d916ef87b84b757bab770b3e73f4c9dc6f14366cb4bd5559a0abb4d391e9b3a62146cff6039c08a1aad927fa4a295466d69d009d6a7574ba0b5714a77fc50d0f4cad66ad68ae235b98837827066665f73e119a0f50df25d43d06850ec5abe33e644cd0808640062b71e97bab7d663def78c2a183c19e936f8a8e516e16866d1a278a923061235af158c162753ec0e1c4a40a445e52ec78ba99cb0d8c533ba12a28e3d9e44558a2f99d3b2ba4d1160bd059151d9e87b7feb86732e60429c52358c1a4c68019d3a29b806ea44b325fe27617fd5c22299509e6a235581bc48c8891bd84e017b03bbf38f26113cfda67982c40eb8e9b5c222d95b8652da2bdc845ec7a4ad35cebaa8eb34be44da5d7eb6d7667a2070de2df31ce3ed602512d69b8451ef6a38d3e1a94c0043c2c25d0531231bdc4e67b25a945553aac59bcf1a40a0262d8d42c2c997c632b31c3e3f29211407a7e4598065b529810ac35fcbcb348bbe77f9d8308ff80620067bb13444bab38f68169cc775417a77158f980c7e70cda8f10189d908dbf9875236f2d04ed2403c496d19a7e9ab648224b8e6dfb4e454ea009680a6ed0c920a2d60f6786d9ee493c74caa38a806b0ab303e86b51d7251ea56f339e27eb3a80761b952a4ac19e6540298a71de9c5a61cc325f1f920837c87bb446308b8c40f1ac90f9292b68558b1f1e33fdeeb35f1c6171c3f805b4a6db1477cc36be9fa0bc47aba7612278fde1514ca02e0dcb3a417ab7c886d0c0f0e63933133ff1bcc9e621cace03a083299671509e22be4fe1c86bfae58d1f03b68de714681deff29bad3abb067ed18053639cb493117af1e26b8ff4a475916007fb7a1dd1ebcf408d0cd5b64dee7d88c1eeb607cf11fbe70e4a00f4306096d8a24db201fd79b8279414129778fc38b966f18b114539ac162b260137e3ccb86f7cd45047de6921466d63fcbb5ea94481c584be3d5c29eabedbfe51bdff24c40e0a11e696621d8f7b01df50bb97bb810bb1242006d286e1664fafa0647ba34b76ea4cfc18936d44b6aa01e6891ab005f08e8f0f79e0c3c3e7d37190500f1ee96c90c4d1b81f1aa4c7246f8132a9a04cc6862b0c99caf2745b6202d437ffdfcccb7d7eb8fd1e33e73876214f4ee7f97acaf760e68997d958493c031b2921f29ba3c3b23c8d0f4f2c402746c7f408544ba5c96fef8edbdba1c1be4b62faa401a9424d70582f53e50f1161753a1b68587a5448713d398205df98f12d3ac40568381816f50eda9b32d8ceff643a16db3397685fb2f18ae88420989e1e0d7ee2c8212bcbc3dae8e000958058a310084361182ad0e4e2ab98d359c6ea565804a5613a1386d45116409f8fd1b4a48a84634fb29cdbe59855fd5e16055d95f4d0580e6d8ae976e4f5952894a547886566ab83314c1b015974276020e656217f091ad1db7f09dc8ea23d1fedbdbf2f8939b56d1e8ab026b2adbe3446c0c649e57a8b964641c086741f66aaca672638dc004b2fee97394d5ff1aba8040895b590bfcf6ca52d33e56d9b26f0902c86c9c2d3ec8f45c232a2843792548964486a00c1591edd7538af48b25cc4cb3aaaeca27cebe2a597d6be839505089f452e3812dbfbbb20148160d51f525a58231131db51197562fba38c641b87eb2d958c7b7a43ad7fbfa5fd6aac4f68b1c5f6eb180dced4323261c6fbcb5c9fcaf368fd374656630f2933d4409cb84fc64179e37874a03d4710ffd23c760b1caa50affca946b6cd3e05bf522c7518e9f304b35aa4c822a07b45a8f7dbbcb016d51c4836a31f02818049c2b47a61d0f0146006754ca1c930bd4043ea938c662033fa58fd741a20f6d0485c0f9deb401c4c57f8490c04dc720f0117c2390b064aca047a45f78ef60a348b8380ad39023fd1945c7be66043fac6e22c13dcf92326041b19772d66bb1a726882b520aa36a099cc556f21f5b595c5ac4d8664af1862521c46ce30c0ff1fad35dc2aaba59bb525b3682e87651f19dabd04e490aedfb548118a97123d39c569209ec548bca8d23a0aa2187ca0b7dc6caa9fa714c4389b2ef3b4013b4fa2608d195a0ae442ea28280d8590e3f5baa2207aadaaed5f1b79d04b6876829fb9ea44445315fa01274beffd0e6dac0fed1cf7f04c86106902f5bb9557311eef5432539b111c13e827e23436d5c28594e620a3a638de2b17b46e229342751cfe75ae6bc7cffe2fe10f43bcbdaec76d97f34e3f80f715a689fc9adcc46fe2e3403f8b17e5c402aa114f034497dd288446a633abbad7ecdc607ce7c12d05388445c0bced704b3c423eaebfefcf11a22af6e42d32598a72162f868aab559e7ba6e563807f1dca36d7166224927bbb0e82ee006f6824534e4fe283203bb8e9319cd214fd44993990c7040bce00ea08d928902cf7230338520012869207b22b94f20b4ad40c967c179d8db85121f313199a3dd40232a413593e0a884baa6d7920c1ced84447fcd28f6c5fdca071d02be3b0c5cda8108a1b067bab9816535fd99d9add384af7f20408bf58a4076fc2a8fe8bf3050ef08fc5c9a342e7e8d446322aeab7a7be009f1e86787223684afcec4b40e3a2f0606c6b275444de91aa4e6f092ae499b686f17dc4a4139cdf2e2b0b2b80fcca0dd179497a7375bce21c66bf2361d7fec218f3e64e0d9cef5d9e0b2dc88315b8abbfae1ca6c3420b0027279b82085aae7df188176f8e3a58615203cb8af615faa059b9a9d4bf5c0673f7efa07f3bf53573889ee29a37185a99218c577e4eade37d065603714eed5a8ab4a6e70e4e151e4509c6c36ce1bff6bdcef5ba502d44aee91e8f254f3c345c4dcc9fae3ef159d21e1ac56a1446301dc78f84eabcfe7483f42f37db9b9bbf7b8673f1b08a75069444edbae22e8802795305073b07ca196d1dd71821d8147a9bf7ddddc22a7451df183e37a2b1ed0d867da9ee4953664bc5407d248b8484c5f810d692a2d7c9d6890109d8ada334a6e3feb16abf2c727e04af42a695680087105cc74d6ccb70e95b7a0b1689ee9142ade9419da716718f0ffc6a0d1483210e16a8930a058e085cb20342ef953687560158297b27e4f9546caaa307ce0d1c0c304e42ac1e0c3d9768d77e69cc2c0c5911076b4cbce28567984a61a7041b47009c191e9d6d8845caf61c8a55448a0807197b71d3e53027a7037abc49ebc371326253091bffcab0db5764e3c77ab015305b1b88fc0449681a6104ed5670182213fc151c7a6720703a0887959a798b4898a496056087e7aebd9503161a06aa0e0931aeec8d575a002630fdac2bb00e7d37067c66aa1c7f3e30fb47d5f4f0a2d08b2c199090e43e7537258aab840018bc3fc87a7f812f38c4f0452aefbf3f01f855b8a742eb43b20d9294128c5cee41aa048450d1e52ff7aa4c7abfa662c0167aa01a6793d32585de5571f7e2e7b46b22beb912703b19cb91a28d9698ce34c49d729fe0d85c03f27559e9256cdaa1117bcadf0900ea738428c0c418845636ef141131fdd52776fd76887c4dfa67d3a9932638f8f7a40465ef4bd9bb54a7928165f330734770dd2f11f457bda7a82cb703a3d850501da5ac5717c775d57ecd67ecf4045e8185ec561d1f972c2d65c604a6aa4e4be63d0ec767b68d38614b96a0b3f92386265e604150bb1e7c8a481adfcc4aa0081bf0bf84e3b209a5e795737cbb30411bfa76b67ee8a47b31c027a4c0fcd3dc2592e772902718bc345dbe915307cc47c7f5b59d6a84bc7e043a0c04d7b288dd62a190ecfe343cde8a6d45873061c222f0c1035d5666357226dfe1d8cf961bf7b932994b062bc9a2a9d9338d29551ba6b2c06797849a0ffa5537f25f79537637e1e67255e185a984b218b03a2783d4cd494ad413147bad06a40a1613932f628d8ab3e79d003c417e0ae0373dd66e197256946259dc4b35cd1e44b9edc979b1fe90b906ffa7567d7da9a4c6504dde5b1c6cb09981a46b4fc7d42a8a7f7e1819d04df2e4f0940749d00364c791643673fce561e410e7504698ddea0741fd51204da3fe8909c02f54445a065da6b9257ff1ee927b50a95d1f5dd11d716a532ca6b48a45e4a4734bf171053a2478cc54601ae2416c95d41b54505d4632c777d7d028c51d702122f88bcb4fe28393eba6653fcea3df8551d085fa9b21a9e3cd766dedbb1f5a56969cf8aa4e7b34516635ff1dc710259013c0f36a1948dbd9d471396559b1e35a84627da45c4344c0ed83637e0f5b75b9932973bffd432e10a248e64bf7b7d68a96c7b21020899b66afb245de2d4d097a9214905418c33afc421f43203c080d6a33c465fe47d304a0826369649ef0b816c6a0fe3e868de6105dd96d6e59c21863846be420c6605dd9f1de6b2414b904a99aa90ed141a7f86f7e5dac9e34475694462faa3340cb2af8809a5aff450ab70a0de761e018e363c65ae8fc2c380f49e6615b0f9ab8f82591711b790802d1fb56a16eec7f54453ae8fc030721cc257c82e49bd3e9ac53fe620c2d18f57c09df22d4599e23c8f78ad5302c656acf3d27d599935485b7f70e8ba681236751a5dcb466ad0d4ddd6eb6ea427bd29379384bc87cb285208d3c9a94fae0923d27d500022305c5710484b0ebf9d8300d985ba194245ff606afd9429580846cb84b00f0b8dabe8e4199a8abf1bd1509b1d6cb94150c73572b6732b8e90a0f197587aa858d7554b449d79b4788d5998aafad373dd06755681a243ac1b345bc0a621b2f80f92045829201ab2954ecdcc35a9c297f4051c962ffb08074f2246902ba8064c991fc61561a427762a52d2bc78bba73381f39b72f1ae6342860074533367b35741650ccd5bc6e5369b731af8f0d716d5ee93d7d523225646dd52dc630eb601347107a212073ed5831696dbb180624e863530fb37dce0e605a8d0c116b7becdd5645726fa522e509dea16ad9c58f3b1c5865100dd481d797d29475ac10f182f6fc673a747e889eb08723c44c92516ec4f167f7e2cc471b0db84ab5813a3114cf4859bfdf318e2ea8ac5d10e7b5bd66ffb9f0b026830d23086b8cd5bce5c3483dae9576f91220c492627416c7673e3472f10d214f90e20dfe695b7a269233036ff533f8c84dd9630e7e3e3de5d95ddaf40d07b3cc085653a64ccd887820a59dc2dbdfa2270e2385888877436724a38db3e770d43ef316bfbac4c457825e66c9f45d14c5d4f028750c73e3fae890162dfd0b2c696fd2451673f7132eccc22e75a2cfe3640b3a802bda6fb400494110d6277d872cc00467494c201fb0ec2236e876a3326f2b1f97022446e24d01ccbcf8064a77fc557c8863aef3f425302bf4144746ba4107eb42b454b1cff6cc310573fe688e1f08b6ef4f6fb75e2495722c16e4b30c6cd5ed44d836c620722182a0bdbc0f683b8eca12c6b35fd5e09c95655597d0508ca5eeab455eb8cccdbf061c1a339d08f5c86d4af9b015db243303a9511055b7f668713b841f2e1f11255589f3a8bcea9aca3b8729d18fb8d5fb0a493364ff7d5ec7c9d2044c754cff6f65457db7439bd75a44aca1be874a34ecd128e6ea1bb3cfbcf1a6847f051b581563305835e8c5f75ed4eaed1102b3d60e8415d60187c7e0bbf161810747fcd19dca08655c5af8f4d9201ff8573674530221132abdb103d3e46a48711c9fd32e5244f49cc111849a33e424ff25cbd0bbcc81eb445e0855c23c21d8597fe24707533f89ea5cacd67bf175502fa78ca634bc9e9344db4d0bc9c0cea89f1418f1c66d9aa45bea6145cccc70b75694fd645db42b74e1fd4ef359c73aea34f01f2e0a66b83341ed0aa7197ac126b3575ad3da296f3275331730eb9adc31781e1eafb55cb219ae51e4c8ee381cb77bbf1f28ca6f60d32a9e0eb0568b6b91dddaeff66f9c387d87d3d8905fc53ebb33b023ccee8490b961c98cdb4a97a7019fd61496e0c69ad9cfafd8f745e91c43ad01c3ab4b8c6b88968da97bc0534a93a11c774482aff9d389c8a0dc16386c051dae59855b05555a1eb00c6c98b80380c55fe6f8cea3133195bedaffe15a88e96d11e9e07f160cfeac99bb014d190a7589cd75809071115c817e72975a4132cff0e0b1d44b3eec429a432d99ab194942fc80ad13243a4046e13e54b355d80613931057a1cbb76f00845d63fad951ee34ebba7fae91558873bc426b0a509e0b8e0e57d314272061454b714780423e30a2c464eb13a4d1d02b71bbf401146db3385a37e5ca222d6dff0452268be95299ee3e18855e9b4e146f801abb703f165032bab4ba522dfa0528832c3769c8c607f02dcc8259dc08799e688081928feab8f8bd21d927e4d134cbc27b019781041d10c85e4649f17ea67a81d518d7b143dcce862fb8728c5e6318e9f7272131e82f25135ae806b7bd79af33205db395110ec9b9449ea0789b5ce64d83b8edf7d53acadae6bd24181ab29b9582cf4f29f6c9e054b2c567c60ff7c67a4f30167219ebc7daf0a40752396be643124d9744f3574f19947995ee73c0caaee0be2bafa1bf1947d86e8978d45ea0b87d894fa51cbd24f5f138d4665078fca272fbc44b09d124d57c8f73ccb8fbb310174d0a063d44c79f2dd92841feee02d4a455ac4b12a72a2f95c70660cd14ce555797c7a186253d04f777906f733c1a74ce37fd6b5c8695c51b4620018512bfbd426b955ccf40c150ea69a2ee812cb58a2eb8267bc98b9ec3b5a47608ab298b8654516a293be1d9e21d59e15ea488b717cad68df3c2158f95ec1c9e50b9b1c34c47f2251f0500a948710a552bdf2da674d2884a681a48e34ed6dc29307c119eac05e83fe0a45a2435360dfa5e7e28140054fe44ffd164585e1394812724445b1788bd10e3c5ace038467ee8cff069457ad868c0ae3ec52406ca0dd6a9a6e4091ff6a7d4168777c2403e31bba15d877af17b1483f62932221e7c26184cb248983bbd4232c6f3fe23575c40861872b1afedab7110a1899e58e42ee23572657ffa7a4a90a7b6200b0588339676141b1dd06c08544ea0614e775d062c29b9d0a65d841d03dfbffb83d614278aacbc3c70dde66a417a1652606cc4362b61d18c728810efdd495158d807790e02867907d7abb3ecc69cf71cd26f483925685c03e014b8927c31a969543d7a5d9b218deccf07025ddf6241bd92191992e19882a6debb3f91495ce6080c66965c106132ab35ac182613133d0b34db71d3611c30d9ca1f6064b56943ac8eb9b6fbdd6e3556700522c03da6ae20c23ebcdc3a500e0a064beed0cea2ff8100d26ed2b686340132a00199de25d51061bcc7c42478d2799d0dc145221556372c99b9e784646bdb2026c012c837613c981cf8df35aa6b0bc3397bebf61b2f47839600f6fb416afbe1952429c72869660ebf5750e43071a568f70144b48d5190ca95466a6e944340b133ff862471799e0af6833e2286dcec2ece919e19b5a842d2ef164e2d4a0d32c7c21878aa7e4c9ebfd05cc28080ad6c2b38ae3c21b392af87cb0fe88a0f3072389c8cbc53b3ca93c04acd81fc1a62db60fd822317c74782061baf83bd24b2fbf53b6f4abf8d719f333006f102c591a447241e005cb69e7718f73f4c6ce037fd3890d04a24c9de4f2309de8251d225d80780d7285b550636dccecf3cc0cd78f7d6b16e467208802742eaa71d76d16cd8b6b97046f6caa8d9fa9b7c9665085b6d1d0cee6c0e7a0edca0687b1217641d8b9aca329c9e5ad3852a36a54a22564c492404705566dfbdc23aa9b90fb09d37ac45b7ad94fff5c2efb660e35b7508b2f8d70119372cf9e0547acdc4f9d220d6ce7d60ba0bea70eec6f5b4d67d928358d68d7741b172b491f72ee5997619aa7537c55b48ba00d455a55b4a22ab8d6842718ca37036e5f4218e2d1897dd1ee53c8fb28f5885c41afd6b2d2a5e6d5364621a837924b999bf23779d2ff1070960d262578623f8c4cf87720041ea0203b9ede56b2034e5431158cf10410adaa0b59cc660ec2455a794cb346d4d1e2865bfa264c13ff308bdadf8067e21ba1b692001c365b0ca9f4399ad4c8c32b4588f2ab781ec72fec8f27e8c5f70fef4b7c7e5c5c8ff8708bb0daa3af142a86b3faeb7582498da09cf11bbdd74e9e0eb76e2eb43671d79f7265f0486a9be226c9ed0945eef34314e044c1d135ac1d398e34b3f5aba3b0876259a3d984b5fb2349c0439bb2de7783881029885cbc219551381ab326a5b4676ce98f063a450110074574592590ba2f57dfceafdbe0ee69d4894cfad645ebbf1b2ab294559bd9269362606967a495732eb5fa8b37ebb146427c33488aa19acd7d4d66872ad68be2a8aa97b25dd2687b827caaf15e97bcb5eb10187942b9c5d36682e4e441137f0b29414a60947ec91d4e1ae04f7ebd7a4186887d560028c43f22044b920c5d5890657cfa2c9aa08ac722454236e78623250459ddf78b493ebfa54dab0ea90a6b42a4e929443635548d93c803a89e05f5cc3aee24b22fc520544303ab7f8cbc19603d4487a1088f089341c69aca5443d010625831a912a5543da24174cca9290cd786f63fd74dba5a6618a7243a6fbe86b20f30f885b8bd7e35bcb04db546c175e5625099830a184354d0d1b73b638f64603ebaf97d8768b5ebf12da2b4a4b98cac957b8315af0dd9289fd2d9c8fea7b738192520e2eb66e3c48c697d277db360be5ebb36ec3fdae436dddab2e73af5728c3aec64f334119341e39a83a468f5135c5bd95e8a91673f39c31ddc99c869dbf3a787be5710a61b86aeda52e50b4adfa930897e68d0c6ad63233c886d093a724219f2908b889461043b0b58bcd8ca28b8ff79d9ffcc4ea41d0539adea024c7f99150892a3d73a68fe1f4af5dca6a1f2e185686d9a9f66e1ccc5560da9643b4a2874774b801e914d6b166d99914362f59ec99a8cb521a7d06958a24fff58ac0523c18bd09603ad9970e4d530e841af0ed6aead9be70c647197271db50718663fad7cb91202030e54971b73d8d406b2ed8c541bd2a079a96a037bfc7bfae0768f283a113164851959f8a0004e6f9a00a20cbb0ea837a33f0a053c7e2e4ce2b57a26fb8e74f489c81891a6e2429476441a599e0007e4f82adb459b31043d74d06cd3f93e1746f0006beaaa8db0b2df18e36f8ad079d4bff8bf7ef7f861a48b5c32826e11a0a675b68ea79c381ff370df4435d8deb5afd78d95f7a2ac7d19ac88d4e4e1f7be543c1aa0f08589fa82f5d2139c9bd54c81f6b3cf6b2c0e9a6dc1e140f68e89b89ac9b64298861bb5b949fff9098c996194714f621cb8460afdd5461b9f1154f9c19a99454aa27a675e8d39ebf5c49677cc52116009c2b54569209eb4983ca7fa20b13df81976a01de6a8c8b020e33b6349ea4062eef8ceb8c30cec1a68983a2f1c61be33d43fb2c1c08822c0735de4e88429533135dc2044646327ad43ce9030d733b5687106cbe7e9fd24490c8694fbf04b935ffcaf8ea4d72bba3d74e0c0fd3d625e8c08e3d80095355dc1138142afe4fd31057ec6d75b23544a35511900186e4e0938300429d8960a61e1a0315e27e067e689c2612bf49956459b4853953d75e154ab5168e820876c005f5203e2009c3b5db7f2721c30ed94319a67c262204b79140908fd9aee51f467b85d3ca25782063c29a21a15471ff8014a594ff70d9ad7c80e27f72ffc9e23c693bd1d18875a4eb62062ca4593882cc8c56ed4479b6e4808dae88855660065ffd2c5e80d7191eb178e1a1f7663358e216819b33c86afc9fc21c7886dadf1109b8e6c7ce8b27d0d05c3333cdb824c5a94396b03b4afdf2825a0c4fbe060078100bc5a5273a800a1b894cc022a43b21ef78c0cab2cfc16f709e998113d84460b9b38b119c550a773131de70f49181bb3306deab97527e3e9d573b5250efc41ea70e5f4681f4b117fdbf81fa8a00f925ae8adad8237a87424ed5f644c9432d632848e8cc17c3058789fcb3e4a23d75f204c3faaa1beb1bbc2af0cae36aecaa4f452d9cb7e221165eb148f4411caa21b124d6e29e35c435f551418c7b2ad62620cabca80987a191d43259101c331bb9813cd6f8fc3bb58189de246e35e902dff9f634ce207abf41afebddba88442e0e894abcc7c2bbc2a12dba5c56435db7199ab99909cb511f867af9c27d8fdc18918267b9aac80fb8284f4bb46805e101e745a3ddbaa0fbc49347403ac64e534c1499dd051462ac1fcb9410da01556061ae5dc306257acf10f31d24db734f6935c657945fff63d4435ae9d20c8edabb38d8102af46e6ca4113158925167b9ac009396d17338d0722e8579f8611232b86256877f9382807da8b9ee827b9bc92f9ce4194c07c5023e260081792b704f15368a153e47d241e50a8895ccf5b0909c34ec27b589577123b249b2da53532eae293bc9b4201217493c13b7e3f31df70a272e58b76ccaae530b9127ff790601a4158649e8d3fa2129674178ba383da657daa3f3a48683bd382bf3a1ba145adbb083de39a3c9db13efd0e0b293d6cb878cdc953e1f61302ffe6f4819d7c52f676f84814f94f7cafbd1c4f00e91725ad4e67456fe06afa3e403440e80161371ad52c57f4c234f24e6db5ba8c33f32d95fa0bf929d816bb6059c62f5a4b8d25cb2392bbcc2572ff4b924403fb08305999c07d234f344d967d92ad34b66300bcc884b56e08b2795a0d3cf44a29d5d2af983a8a05de70a1e7f878414ddecaa24e1f5058d58cb85c644b00e8b015a8b6c1a451a032af074389abc0668348e5d5339125e6fc3dc0b1ffaad7e40e11dac6757cbead47b857d825388a5be086e312ce17c0dabd80f0e678d839906c6349f33d561e047c2bd0443e23c26a60efc98b26e1d7361c86dc227923980466405c8debb3d72eb1d5421cd1e762d76a8344a4a03d545c0bfccb06bfc9fad3189e8c6fdc3cd567b718b1b07bb82b034def18c7517dd53b357631edc134fa908b9997d146895c9fd770625f8b477254d0f5bf01e709369a416baf99a5c8008a0401eb9ad41faf83a56f51c23352019dce45ab3e0b504c8608c80734040b4721a2454a941cf83c244f90bbac71680816c383b47bf36adcfe6481b63111b0e0412d3c77ced5727102193666a979aedc80cb9784d4b57b38fe1ed031cfd3703600a8f4a02be2a44d33a485928f2dfab4ab28864ab3696dba517dccccd6a411bf6571f4e439b4d45c3ac902a1cfb9f87eca236c6d46bf111cd99aef6cbd739fa7dcfd23c1feef8727b9aee1e81f0cf2d299a7b6e0585ba316a43fe11419615d2610ef269d0ac48ef9a42fec6f7a4566d02aca771584852a4874aa5dbc3443e24546024c844c879e05417a16113a7903e0e798e24827033b9d1fa65568c29c00d1ac01f0f43a7a060e96d6b9a0d00a8f84463502a9ab64ef986d5e672fba51e12fd97a931bf33548d69e6d78621e58dd261d3ef41a7f25da2aedf0a99ebcd64e7f371ca4a8a22309393b6b2c37c20f1c2c4c257e023c73bec1df641a284a714ee8883f1b496fe0e6109312a78be20430026e5e0568ee8a8005d28cc598d161ba27b7550c4ea3768288dd082730109e8fe0afce1960be8d4106a87a4a18028450b51854472c02ea8090eb6cadb82246dce424273e737911355452ba131283ed433e4db22d26e961e192bb5254c432712bcd71208a6f957c8b22e3f66030f9c136811806742c0fb0828a36cb0cfdc0c4b5e434f01b3a01fe5d52c85bb9c0c49df4fdd072741b4451b24ebf918302874285d5a7cc508e31ecc1a9144c429f1033901f0dda9a9a6d8e8c29465049b201b53be90df39dba9eb056abab0654570dd246d314dd76fd5327842ae253e021a9f570053ad81e148531f76598ba239393269d517f6dc4e58a1640ea2cc240fc07a627039a239a41630204d51c5a45aca1f0e5b972961e55d4eb8a79c246adb3c1688f60fc043070b6b783bb146ea9c1c56e384cf8f18d037b69c639e9fd60355b686ece993a9f6a1a30d50890db4d78c11f41b240a1a1f66eaa3ffa94e97702252945f6357a8bbcecfa5dab40d3821a8882491226add84ad560b6e024cd57d31d4ceff376d52b7fce696b231a318397126a3bf915711262b2d3600257f4c9d8c4309273f662ccaada63a25df89003ab5fcc7c0e5ecb814ff17be073edd1860f962fc07ff791293064a89bda58f061519dfc56812d41a01692bc26ad53af4d8c58691c7921ed291843342038a4b5e978d82633afc54f84c590f82ac045a96759d890e5c22a401d3d357322f12675a915ec83fbec00be57134d1f7a4657bc0e08026347490df7b6393da4e5a6482e26fd7af6ad8a08037b894c92a5387decb5482d048397bfa2da05e19fe4535a674e970de3d1244ef7f3944e577eded179276673661c6ae9b5cc07feea3999d84c6612084e77eeef5908938242b8dfdfba21653f21ddcd10bea15b8bba422fd38dcd48e02f310a6f2db3de0a2f69f85a0783650d36313063bdff6ce266d445957df90f61c0e41e91ac92cb79469a80ff48bec1175c0bc6b652dab82deb2fd561b294dcf314479bcb110c56bc40dece7470fa28dc0ec9a77590c842e061427e02f6b570a515859ef701852a5f8c734fb6bde82d445d059830e27edf735a9388e799ec1320174ac3d7309ad442b5bae8dee5b0689eb15588167557722765f75870d51969547342801c33d1b79931aa92dac0d67d6f5847725483bf1c07e1b23a9239dfddea06913ec13aa59204042309d58d90683e22048ce95948f7d880283cdfdcb8e40c1ba3d3347df8c3bb8e242a1f3d83c965d1825abf139c31ac1e81ad46da129ceb9bf718e0514600bfeba3b3ab4df0a40651ec1ba88688107a489ec349697fa586f9f3b2181b9a25525b2c20ef95b5770717ae9044000eaadea174a8edf7ea3f3071a2d3269a6fe548d67fce5189de05f7918536fda1556567e5f903a7ae95814c1bdad1379a333742b7a67c8686355191319b770f82722d7a761745c0f79a6f0b66e340cb261ac683d93eb4e9a521dcaf43bd30f4f50728c78abb4460739e0fcc5021e58ee1c3e2ebbfaaefc57b4c34667082d3145123d0c45cb0a51808a588aa6e4ff57b198207f822b57e5df91159809cde795aa167587d51cb564454beef65b5eddc5acd787ed561a69d1f592ae33ff0b471cff5e6e1485409ebce8aa6b234f6ef176a8a4ea0c76eb7aea2abbda177c096b93100446c90afdda984edda4d35a4f871d93306f25aacdb505a69518a9017bf2cfac9fab6b7952504b01006af9a4f92969a655ddb5611b86f0c22e89be942afce431d2b5135466b4709eb5dee9fb3fe334a640467d0fa75f29a1f84a546242bb42820cd0e9ce5833fb0b15a91dfab91e640ef85494a80ca52556fc701b88128790979f2b482472328d7e689835b27059ab35ca6f910fdec0259b76fc0257f73381f1445fe48c5b3b36e898572470f3a9fcc5ed0288fc7b533c08fae74f9b1366b200d85635b4210ca9d74bdea67df68e1ccb50617a44496cbef69e1fc357ff7bc37d71af81ab73444345118eeb3f1a6149bfb29eb7c1140394fc83c921e91de6cf6e48fd43158d24333cc23dc35d5f00dd42d4ba574f2e89fa0a0c8aaa180ed50510d4ef2b8527756172eef93f34689ea297e430ff5a44032678b000199dd0954b457d15d1784af6a6520f0dc4053dd451b3d1a4f9a1ed33c1554cf01713c4e057653c2a9f256728e6740b46a2021dd8c1d15881e59f0859d74d0fc48c448666eb061ded3d99bb7a14a644dde6ac1e6c464a7976be0db2a8f210c021b307189cd30c34686aa4214c01cf924cbf776c802de761442491051536ccc56c7e5bbf11c8ae05276092ee3e775066a60dcd612f0a9f66124ca6db7ada5a0502cd826a2e417bc65f156e43ecd0f55e3668cad2b80836343b3db8f01147fac342bb9f293c8984b01f0332a02404eeeb36802b8d718e44cc0a4e2757a54a860c5f4254ce786d01cc8ba1de28cf472b909bfc7e048a07579e43cfd6e391e8828825654aa4881790db89d207bbfa3acad8fec3daac23da5cc09a957c7217e6be3f5a0da7cb7365e47e5597f5fb39a25c43fd8f569c06c868381a3afdc1202941a8621b139f563c71d75d108bbd791da53f25332a0373936181e0a109612c8d0be159f2fe47594d718c202c29c3f9ba5160004d8d87856c71013d0acaa6ba879fe1bd21c03236f39dfce92b9598a49ba56a3ba6397aa8bd6b200413c07f2919d8af0f8e9358519736be8e13ecb5fcfc29ce72cf166a4d3469ec175f1ecc4070a49f10df3d70a7506ef257b19fe7c8f290379fa9c6cfcf2a032a74623dc84c04aa60d36d231b54d8805e5035a91ede627e6ab617c142d7a05566a4cb8de41ae57d73a55393ee8e1c90d18560cdcda15f4c939f74a5ddf5dc6f67d6f915aac5a636eac27a598e015f20a5e1f2d8b6d665a832771b6e840d68f4a4f85c398cb01b0eb888ed964e8000fc5abb731248b96813ca62d4df5ec1ad2f5d0310377c14f348c0be0f8b1d8a52d34888802f32853cd4fdd839fd3d079a5df76337bdf2dd334951409c969327767ee2f31728e371cff10c1b69072a0c33125bae882f0d00e7bc180c50a7444a1350a248455331245b85b4eb2f592cf8261da42d9ea6261402635f6b0c9a1e79f7525cba38b4eb5aa0be61db1c23752dff494521cc4036f122bd6771e6a02374320b6e03c7a7c09bba4cb1b1fb022c85c9b32568de294c392755040ee9b11f452cd4c5d0f638b8158f4044ddc912ee2d9626bb4334a7498485f5a4ef70506c3323909a81db2f79bd070d932abba3dfc24ea7a89a98bbb78ce2a166d2394b56d237549dc5989ba8d96225f7236b0263adaa8057e3db13de179e4d7608d0582046348c93516ac2372dc1f59ac4140cca7591e45d1ad766a314ea3ec670e6291643ed4a058f5be3c12d7d59f74aea76551b3b25b6d89f57d5c0c3d182f052e195fad17277125cc9eb1f15cba0932f034cbd327a57ed9ab08f13bc1d66b1857bfb3cdb711b0180c19af7de412b44851a41f4e33d47736b03c8026db19413b2aadb595302523995c0cc69395cee07509e7cb4e27fc4128a74d1315d2656e4c67ef55d1c18f16aac5648725f439764ceebb89aa48c0145370d6ab3b13a5439a9892624f0c6f120b80792d24fb6d20a888107ffd5d309ce125e15e004f1379357924b8a93de3b2c784f35c4c4cb609605142b3c577f07ec61bd2793530e6f3da891204dc9f82362f049739bc355d4221f3e42f8d8f4c3e64186de32999d6896cf06c2297b688c9e7f9409a27d21ad5af323cd87b5b9ee207cdcc7f95ee332fbd36dde91827d47cbe50663ac118f228fa83539976bb8f06a53b11d14ae684fd98aa09f0acf32ab6feaa1bda6a89f21708436a271ccee40f33cf52046124ffe8aa5daa989d557839a5a135228245b6aaea02640886ea1467e1a5e07d73c152f80c9e7aa1e12ae041fe57829db63a1212df056d4ca05ccab095bbfbe4be2b469d22ead342549443d819dc34c364454bf45d7c3d2b05d85aa16457844eafaf5cb3e45842bf29c1f58043d8a7180137e78a13ddb304601f6ce24386be61f4fbb082568b0c8f6124a612f63fe342335b36ea9daa84b70493ad4feceaa7fefabe19826970beb913b1881ecb56cbad832344d5bbae83a800cf08529c33ba8ec30d0d0ccd4d43fcb8646c0778a040a54aa163983cd4a63f41ab354c0dc3899b4867cebba1f916f388c96a9c7df28a88d3df47d56576e04c429299c6dcf89f6c8d4763d6d8c008464c3d9cc50fd97ea330a078da74bdedfc937a5530e25df46b0cf450b8973eb5cfdd8bab51e366af05d09a654c41c1af2fa8541327076c7f8ad6725404c938bd5905db8beb9be6bb59895da37c9429a0cbe3bc9f5cec944f9a0ab7c1c1869695adfe71d904d01186d21821b8ec2b71d9b05b38f9294afc64584a8f49549f0d73d8badcb9398712c56c1466364c82c84cb02102eb2d434eecc16ac0caea9f8c5ab39cb7640614b74c5ed3b5122a2c692f0017aeb2ea653b0c42cbb91cd98c0f14cf71cb5acbaf3500f6f27582f6edfff3e802ed6a34fffa13f757f290c1078e21950019b64ed451b8ab05bd1ec3fe2e530e19617247bf45153a04f8ce08735243c191b2932667409ecf2949add4ce8b23a229cc590aba89ac8c39237e5b0322ff8efbeeea1052872ae2acd865e56e88a4a99f48b4a4677bd16e920bc28faedd6a112573e0b668aa63f41ff1e2cf5c6bd9e6001ff08c9fc867805a8decd5f8c61aa5ac71a43550c530eac1dbdcad3be4bb4f0d11d9809dab99d147cf2fd3a63c21c137ec0a4b26e0a6bac6a1e3480b724fb4cbc754d0b9a759190102020b4708add8d74de71c21fccb2128c23a2b2a3b1882c5b0068398184beda1f77436a6df1205f47c4b3250ca8b101dfd712b65da1f92512810231886d48e0016ee131023613345c2a91a97e3dba5a5b4b7eadd499f06a4a058e05f77d59ef31e41481bfb43498293206d446ff7c1746ef15db769128a82b859010e640d4715bfd2d9eec5bc7dc150bd84ab1868a8bf0d7f645ca938a4a10b28def7f25926b8b179f51e237696014206cf2f014e308fa821ef582cd22e267175622961a3960b05729bd734bb36670a337fd088243f0bcdef3e2edca73ad992c202383516b876f9a345463c0719dd03bcbaa1fb737df02a75e2de45889669dbfe8c045d99f1545df04c4ad8801aaa12088faffd4b184b93503909d97c5f2c3152918eea23caa3775a736b70fb36a8c902ec2fb2f18f28d71d4a5fba57db128dbd270491e27c3427d0d6bd89e60acaab3cd0d23f9970ed67f385b91966601f4b09936836b5080403aa657d71cf6804b5a547a22aa50da397cf3d2ab5ec65790bf07201c57c67cf8ccd88a66e101c64efc48d265177da4ff041c43e1590020e6a303007d2e588e4e4ee39a6997cc48f2fc22a9557e697481a3ff9284ba1efae2ea2c59446141f73262e2f4d0243e0f2bbe69b227b4784bbbc0074edd43dc819e166dbb4c180bdc7bd1739e945a5f6a258def9f71809e6bafd9d7a25c8a59b8d0dcde4a352fbd6693426bab8be1646fee6bbf1b92bec97da802e853e37763ebcbab1d97a66664e716330c16c871cb2557b5beee55c6a747e8f43de39d2f9d57ef7b97ecfb7cae50a64733473ba3381fbaee69e6866440d4152b5a00ee5870f51b7884c918a8b2618bc3040285bd780f4ac41e059bc1524971ff8e20917e3c5152744900190bbe27f869124cabb908c4cee7e7f56279f0b56ba8ad8c25a6324a30810a8210816391ea92f9665cf2482e3d5cc62d1c1e227367db2cd8910324425027062b17448400a200a1a0d62a89a8433263648444d1e0f622719f581baccac9722d08913c973a27069695708cadbc021eb30de2a1452d105bd4dea9169971b8445d1b6339cef73d6c6abe3d51c89dc33bc944a4296e2a23825d6a363ae6c768a37404736db15f8c3abcc3759a5639f5daea2a7073b0f3648eaa40f46d4b3c5e36697709fa3e094a22fce8da1467c5be5273c6ad7b01289a17f41b587463bb703123e135a99811f617cdc92c962800434b5c043e0dea0d40cbf8824225205c13a51430f2ecf5f480b1f708f290024f36d4d28f653d95a0f6bd09e04573fd1584b345f0057e9806f58d6457e997c035321e97f0831300f89bfa209a4dac1fd238c3212e203c549e86e3f0f05c587661fd65b9ea6a95b1964a0bc27713644e0d79897d63bf4ef9437e89e8030a859edd6c1ef5037fb2823f899cad9cff57038fca6a4b423a3e140341e0345e74a2f7d984179dc9f9c75b3cd09ee01aba7469e354677f35cb0a90cc10197ecef886f33a6e50efd606576e3ae16e1d590fdfde4b944091b83735913fe1ad1c476edad99301b595abfa355e28de13b6e12229e4b4a7e1376834bc89f832ea7ba79590ee3228d68152c5d232defbca89ebab83f87623ac139e22068888813c56eb686a0713397ba1b5708fefc5441cfb7ab898a73c9ada08505bb202ea082a7085a2a8bf20c584b4874dc6a23418326127aa42a4cf821b8e12dcb6b124ce8e81e68990f42b66589d57a98adee14f576c0540b06795822f679428d643423454024641da9b430fdf89b1c28be8042203ed47806fcc761430c4cf5427daa9539afc3d3f2c4dd2449d42aa9a13973a23e76995110d9605b293d91ed7f254771f65ac365dd6f92c80e5af3ea2014e2fa099b0cad333c70f982ceffcbdc8db52bc10d07e41240d205d0b748006fae5943821eab802db9ed396339bf8d01e709adecdddbcfc2b6f19f142fe2c3c82e49b915f54072828e150d86d26d9c8bc5aabfc34e669873d1a939f0d6c74313c4d455006384bfefd4fe5a4288c7169dad54b4df08c11c1b5200a5621f21f145dd214cd56025e85f63eb675e6a8ec25077181f5a5d6aec3e7cdbcab2083b765e69bdb301781fabb563e37702effbf28d0fd6bc45f588f50396f7ead1cf21337ded70022d835f690b830848bfbdb4cc9e4d150b30925a57912d04e4f103ae4108cc1ce96cad9dbcc1a4d426a109aaa3181cff1245f9334e98547a3f1b66a8420a490313cbc4df5d43ab2fbb811d95c1769cb4b3117eaee02d6622c265d5fc6c579d827f5c8fc61c651b1223e62f81c0fe586fdcac906ee784087f444f1350d71cd327e2d46798f370b63d4efc86bbbd3c0640ef5bf0a3cd14cca9eeab4714b30c2e97f67e5148a8c9c04ab51320a03854465d280ee3e168fe9d657989a4d481583706f6e49a275c700b0ba3cd7a5dea7e66f2e7b340322a5d17091c8752920e6794a26c46498a01203fae00c80d0d0e0f192a004b51130911732e9d8e802d9e54a8ac3c3da5e5ef01831a3bf0626de16d6ed8e63ad7ce0cc9c6fe0e7e748a72b6ff18597e060684f7952b1317c2f59903d2dfc0301c628a6e5540212b1c4855c3ad77a222f780bb9fa21c7aee4bef1a2427030501c1cc9f5e751b40e5fe8ae37fc5a10ecb71815282112c50f15e01354e1b8feb025a3815f74f0be65486bb7e903f52bbfb444f5e4fe1dfee2b7cb4a3af5f00de0299c856329b01e888238e292d5497a13950d1a5b7a5676c01ffd0ebcdf6d82e202d9508a3f1b10cd39dda3938790247caa0429aaaf7a62dcf34d4db8153720fba38197ad9a17e936b55ecc48d0f2e54c264bacc67a489346e2606cbca5937c5abc14d042ff670626d3602d7ffd163b12abcd296ef8faba71f5471cb1e39e76c261459844cfdb8a41ab3154b760ac86151d612c84d037708bbc3989c8fc239253254c813404e75b147c7e0b0b2be5cc1cbd6f27560ceeb0417e7311259e14b4df74c1ddfcbec237ab6f4287cf60108dd666a193dde9a7bb6441eba5aa47c03be76ca6097a38eb0af46882800495194d5d380e2275963ea5c6e6aabf5a304104b04968183d1d88988e9b31557be19d5dac0283a3467f41834b1e32dc6e3808d593ddb7db3a5f1437b53cbd8a7aa78eca0dd527f3b3640dd8f24bdcce7b6a4bd3df4316fd423c1214310c06aca7dc6e7047d9031ba25daf62f07e23b25ba31403d5bc1262c90d5b1db772dba3adf73496cb5e07a746328605dbb8816f5f3b62ed85ba02bd9d9eac0ca6311a70557817da1e8d92b1fa986de0d352c7b29473a8b7404bff011ab4a706655fe29cce655d92ec4b6a3f1ee2119b771d9a1c4428e660f64fcfb4bdf7b9f3613fdb239827477bec69df75295b0984c661c393a8dd71115edc47310310c430a48b4907f1d41a4ecc7a0b1604b9b21dfca9612793a9189177b9f226776792789760a1f7042937e7fb18634b29124da5c7e4abd5ecc8040637364193fc92836b6c8f6d9d546ce77b3897d6c18bd72142d375a2208e783cd0182f2831c6fe90f10395388ca61c7b7b2e84200e204a21f26c166c2e37dfc1058ce2f39b1e25e6c0791ba879150fbf2ff6fe27f8707538815bc5a453ee9918f057e41412fb6823c5c1e1b9adbca62edbee735bfa9a3e600e81513786559d1086e33a5a358ac834ff7f2f8d4d3d25cd56ac02febd4a43196adeea8a9bb53678c6ad38078f47e1217090ab8ba42b0a084a654c7fe7139d2fc66b3aa98deeeed0c3f798fd39daeff78f16952e50e8b68f65ea84594da1fce734089146dd62f0cfa0613fb07053abbffecf0a8bff76cec4208094feed04704be96da8796e60e9b0d4fb28ad6d6f09c434769f4ed30fcc4a2c80dd39d69d603023e00b93c7fc6abaa0d5bbed4d84307d332c6a9dc217f61995c66c6d4597339b1d07178828608d2518d4170d9ac098cddaebb7b0d72da7661fe0821b62f545eab9cf91e3add1b0c382f0003e950c012e4283b735698b46d5c95c09d6f55446bffaec00b93a15c205ceab8df8b5aa6f4306d16a5ad3ea0a0659f809e1f45fa519f1703f665c8f1b8fdfb221bf546313709460663ccf21da287bc68abb78324902ce57bbb5e40a97d70f2cf4391a5ddf8ba9c652b30646a25a05289c5b242ad63063468d36ddf82415fbc2396240e61aaae249711f3310fae08958181fd73bf6600d1c5a3e4266f10d89e1ad948b755f0d2e695f770e343316e0f85364f0fb250b29977313707cfe0c01e7fa6cac61378df044d8750a6b9d5339fd4118fb16e21a404eee90f0c1236e8193fed2af6f5c65d8bea6d79ecbeb602fd0bc8ad59147dfac2d4ecd0c3e73a4cce62c589f3f7d682a871932ccdca631ad0e9334f87ee491bb2ec09d60a78c4d4036c30c97d4dda79be5de426750c4dc17e37be64f7ef15db45d2a93106f97c598b77ab321eeda01a5978e8b47d868b197e860a5a7d49eedec9b2f78680e1c93cfd6af3e08f4360d90d2d0b946dc955a8e8801094ff737ab6776d9206a1d070a684d3d6e5471db15a47c6c9b989c79a378077bb96d9e2e16ab32a714263d36f57f0822b131a565fcbbd92689795485bbfeb92301f7dbd71bfcc337bacb8faa5103084c2344bbd42ff9a7a9d7752ee08eac01b07124616312fc49072f3cbbf0d98ccbe694bf42a07323e16fb4782c0653824852cd2cabc93b8908744e111e93f73a166ae2d79241041f3d9d724d4b6c57a5c1f55a01793a76f1bf3a2612f1bb66f5777cbce26580b9025307d049d56a4ca45b40a148a863565670174fc2e46f01865a1bb1c1efc5c4ee6f3636f0f04d310beca36861267017b5815fa185cdc0210b34dd6dcb38036112cc341bda1e4c82e707715c594f4a3593cc520fc30a0b691c144d5cd4868e31ea834957ffa58cc3b9289f24097ef3db99a4074ccd8de0c5cfe8105abc5c83fd3f529f54ba82110c85d6fe4f7c0cf396d0c0505eae54fae778dc1e0255ff768face03c5c5439152b79509f416961d30ef5390da12ba2a2b9f406f3f5171b2354592036291007768eb1d1e617a611ff79c42b9e9a73ef103b0df66c84fd3643c90732dd21932d04ab29e1af0bee56bfe8ceb993771ccbb1c040fafba882dfb30a0db4e0128e9fee852cd3578833d77a923b21bea87c7ed9cccfc34a7caac753dbd89e7dbdf26fa5795263b227187301e21ff4cb7eb52c8cb7fa0428538f4cc838d116b0293e1665ba76990e3d58f0aa600e25e8f6200dddadf1c1a886a7dc75fd62bb48ad4e4bb47aef57b950a6d391ae7990fcd250b76229293ae8a9233842dc059a837710bcc93592f7bc95429a033324ac7c8e2e087a2f3633390ae6af12536a73df3eeb30027bedd95384d10920b404371c214802acb7eb84b94488c18698756e43902f0d4738357d8d8b0f32bc744a0a4a234b044fc036821e495fc17027bd6680d454f68de1d191dcca80b4290f0a2fa6ea0bd8dffb88646ecd4b8d39c47072694c6b05d05327d995a62ed5a61f5755a6051273b69c20cd4fae447be0f5337c2927db694427b28fbfaa5f4a3155e742d728a26071abc4030d864a1b522b0d0e463b40e5cf404d86041d342af6beb57dc38d4e7ccb3662c5f52d03ef24769e0aea68632b68d335ebaadf362db4d1bc01189147bceffc9237848f1b1e3543eca0269e9adb3ec7e8042ae540995722d946968e3715d67ba93c0d4e0bba8961e547af914c338fbedc3320e185b8aa5b401236260cd7b32ba7ad2b88fd437ac2c712fb5ae3d956b8b3950175b6a3098b97dfe75312cdd723fa30055579717aad30a05fd6556901cfad59d37cb093363f8c69705c8870ec79339c8f82525bcdcb75b383b54c3fa1f3ca504100a3c3b15e9a0f014b93e4a87db4b510c84a3dbbaec04449f117e6bb37200d0056376e89b3797d81bd3337485a5d59c4c8457d9ca7d366866e44911ac5dbe7ec5ac58c776244ab9071c88e5fb6f390bc644987b61c537631085f10039635d0be8d4a1f88e826d555b64ae1c7c23691aa338f96070e5fe297d7117eb8a44b44e7cc3da060f810828126ff3f6e0a4643f80eae3c97278414268241ca2b42c95a57acc68884b21eb72d15db3943770e500974ee787874bba52dfa8ef7695756d5a89dcc3b2e11bb974601078d8cf731cc0e08364ac3d2196037c1dd817704b5fc9590b23210b07ba259ea0b40c7326d69c90f8c93e698c7ccf0fae5e40acdf0fdb3b30918e250a230343ae04960fed7b8feee06f69add5f28ab2f123d7800ed3f89677e13d4fd067c82469f9f1c9c0e3044dc6ed58a0c121f20e82ec0c1805554091f63df2f34242bf031809f2be308b04d15cf1512068a3ecd108c6b489ea6a93528c7df765cea5cca263a02fa57a9e5fc6080476d7f21591e123f8d4edfd1ca52f1ffd91c51a10e3135cdbc3f72d27a87500301629c2dea22edaca1da655fe56d5418593519b32084f53c090b503fe87438ed4710dad429d55805cd6ed8fd3ecc9e3423bdbf4df06d8ab6fc5c4547df9f0050f3c52111f48bebf3a3835ae962f5395a3814eec10f9d0d1a2841ff83e9b7e5ccc28a9944e40773deed13564e434b12f5ece04c7e7416327f82f0c984707d8371b2f6a84e8aa95e2c33e75f41103167d93a03d07c94ab37c7bf92911e406166b7cd0756caa1b8f59a0a32ad956468b8535ee0fdd594b91d0e8f97a898fe4e98a3ea5f6835be92b49b51a93cb9861d24f0367d66f7f72ad418122621f0e6e59995827b74a2dddd563db1e2e3a15ea94304baf832d5e8c2f78f364abca40395a4f02b9c9067d3eadd950f4e574256a679d7df62b1a5dd3a10fccfdf76937dae903ca255cced5814d0e57a64b5455890aad1faa255a537c427b3a379b3f75b1ed11866514148c4754ba3e3ce6f3f25cd37cfe49e1c513023fa490812b691885b457e5a9f19039e0d9cbd4252760ad7ec42ee88e3cc11202f1302e0f689b403448ea8942328b888738b13d5869158abf07e07a2782ce5be339582558611653d8c367a54b531d4ef8416171d02502fec0a1016d5d7c28c5babd4f383a2f30f5ed8d54cad9877aba420a6f1e85ceea3b9456970f0012ca2ed4d26dfaffc33967b15f4ec9e242f071f4e832c4955dda1e259129f1fc86c5375280e0c990521a0f0d3fdc4ad20c5f93528d9dbd602777e8060ec0833754334e5553803bfd400fe1df8b87fa99f4b6bc9d64e6945792a7445ef0b95ba813d78d5b6000bda1d44c79b9a788012880aa1f50debd13ee99a133861e02cbc3a0e23f3b3d3199f7f01b2c9802a31b4e702e357d834fcaa34f3cd5be0e461d8d4c4c77f5e0288f272ffd0aaf91bb9049d0b4dbae5c9c9f16fd68125d52d073023a4a5225d14724c091abf101a02c748fcfbbb0045ec32c1aa07528b6e92e5dfb474f8f8471a5f9afae34e44b4650853d56df1660bbaf478c97d9753e27d730f0b34c1ee651ad0f3e75f6a88a92595a4a4ac8887248f1222b228f49d89a56335a649fdba38dff095e676af22bb01c2507ad9400c80c9bb901d4df1a1cf3cb58f71985252988f0ed6ccf54e135dd00653a2092c34d43b08029222c8979561f0aacd873505716635969db7744be8ece43dd34345dc432c628310f8edaa62bc98fb666e3d789ea461d53009b94ab2a1c0caa8f3991b25cf66a8a0b673d80dfcc2aca2efb6eef3456e2b37b0dccf0242b0842d5fc8f150a937ae2385f1f6916f984c250100368f4ab0496effeb587458ff8a72e942e689c917f3d65366846fa4effa4f0b207680fb4f596faa511c6414eeca1303f92d873fca2168dc7da8d0bc5dcd4741b5afce451c907e3b9f2ea13e53e85b9c0e994bbddb7de06a912fe19432463804d520b967b86e516a19c155b5118664c793a85e1a6916dd31ed93b2c99a55f281a88b35f26ab613252b73806d882ac68b7b1d12dd6e644111e4da590a59e64a86f54d0f92fd06065de8c3104c3874d135cfb2c2358c82ed28eecdaaa2b4a903bffc95e7c90cb4cde8def3ac4f4d638ae2e635da49bb25ce660c50f980090ab854c7823971f60714fd5c8e8f2b5ce11e09507083e3110fa513f0a3aca5d1453fc22331a09cb09a17d57011fcd6269f58e53a5046c30977e21e99d031ae10700d01c301d93aca405f98356823b19ab9301c2ad0a7845ff95ecae6d944dabbf200f3fa7fc61f46384ccc620449bd2d48fcc740a4dd6546b668818a2ef05939d6f7bc2770559df9a33e9c3f4971888992309639115f638b2bc5c421dcfd03fbe07553a39fdf48690c6d15ce19fe0978ac1ad1012db8d1330ecb10931b7dadcad963afee1c74f64cbc6b639f763ac357af0c53565affa8f3518b4f2e340cd1a886af4f49a2dd6da50b0923a3ba83d89e3cf7f8c458b9d79ac2f034c712bc9583afdc760d54586dbf17520f1fe55e8017385d9dc3e9b888248a048b01bc4049b8fbeb239e6186b5086134f7f9e1ffce755ee5aa2909a632c3af63eb8246aeb996065ed3cd313de9f46a4cdf1d0e29b95be827a3027ee0431ddfbb787f34197348af03d365cfe356cdace8136de85efbba0e322f6d2171f33b07f1a1abb7a9fa4678bdb7df2dff50050d85c13da2749671f51c97ecfd6bdddb0d7f5c816bf531126071c1e16481961e8e312bc31f1f43fe170919008939ac269722f3a5be4bb083cd772eaa2706ebbf6448ad55d4701aa64169a1f14ad8024b9d0dcc63906e4c08e7df444f93077ff4b3cfcdfd685e193dfbd22c40c4dfa9ea58d6e3eaf8cf6c76b9d197d8a8b16d5ba638b642f52bfa620b6d2bb07363bb04ac3f61afad99a04631b8c9240e49e5b5f267642bd3ea3306521b48d988ffa4924da59f443cc1a033cd735756daf8b2b6e3d765b91405f17a25ca1ca0f61a3a5afce53c4b04884766a0df4ae36febec89e60886ef3ba2ceb89fad233376934840fab44d2d1b7a46b17db056ed6da7932a8058da838936bf462cfd8fe430eb890e58b2e3028d74ea68639f22bbc9541ffc7a2f1d0c5106e2f6666f8c9a78134085dbdeace37a799447f2c5068d785d6c1640fc2628b0697bdb3f9a75bf8fd66deab9538bb6e222423db7db4a941e8cc4ea8c22e09e213f9b8bf6a32e504a6c84d9fde3c8e86b6aa3e7d80a8bfa60e6ec80064bb669d78111010ab5e55a5f3a6803c03229f3f5059a636e8572ba2dbb0d3b3454ea035dd4393b6dcde8ae5ab3ad189e311bce02d8c36ccd71c4c1313adb16b62bce5a5ca0677da411d2e0c92ba3215294db15b8f9231f636f38e134e517b0a75ee4d48b5b693f606266db2998e28c4b86f8a2bb0831d3493a846ee31944adfc2b80af2a69bce00126b4f48aa2976eb55f4038087461730b685be3904af2e42c1004470974b914020e0450e30ef298cfb3dff44bf4f7633ac2370966cfc626a8c400aa28bb24536ffbb51c028ab2b9bde92267b3a285c8fe712f3e292bc8cb8505fdaa674b43924c3921105b8949a642cf9ddd2a3b3a4c7cbd1a87fa47f87dc297a710d55269153bd6622810027c95e5fe8d4c9194e44f10c305b8bbfd4ad8ff02cf890228937cc9705ba528c4b229154021c88892f769f2a6f3202d1eabf32c12eaf6f413e63fcd557dcf699b5466dad9eac334d0d11008ab24de9d31bbd7f247a70e21018cdc081e697ae603875928df7cc01bb73892897e7cbfc32bd4c2ed3cff475364c086eb747566eef6d7964b8500954d3634007e9fdc9e3490add81c56738ae9ffa4605fd1e2bcb7b018d2860c84dcda005435fa3411a6aa1ffe4331223d835e6b83883a844f50fb29ddf989a47ce30b4c9b56a056e038eb27701db65c9daa9babd86233e7b88e817d9334f419bdd8439ecba71861049c4dd769a56349e883e0810cebf6236361faae9682f528bff3494a9cb47c78fab112db0ba207e9004838a57191a75457ef66630678fa39256567b35cad3e608b07a86208ded8a21de342f1cd31b17c4f5b9a7f659cb8feb7eddca5a64d66312bcc9b60b30a3e0e8679c10b2d3f380285c9b63610be3cd26924f1a159cd5852269962126ffd56aa3d6e01c08dbc5dbee58ee467e672639e053397dd9778881dad5ce756bcb8ed905e703fe6f6b4c5d186bf7f3772c584499b7ea4716c82b1a8a413a30d0306927eda90d350cc7beac0cd49baf1c2dcba141d19d198a322a7d5b50d0920848fcae76c4589745723d7f01f9bb18d752462eff3025d38367a30c06c63ba81aabde36d5d261523b67d4824c80faa5ac92a32c0eb421f6782d60e8330dee0d60a19740ebe046ca21a8fe589ebd0de67a20f3e678744154b62e21b011c6876010efd3e956fe3d769c2aa17d76c73c240ca1d74344bbcc0351836b16dd08f69c9ba05e828140613dd5f89011b60c10ff0f42941cdf1820cacd508229954fc9f1a51520846edfca91acf41aba84d24d88285cd8c2b771a8326e61ce534d1a1d8f3e75431141b421407f730eddc63699f44cc3e6c507673e6ed6df0f2553e81784b136dd4de3977e4250889e5d87992e81983f2fb359e743c32891771109a7871184f3051c01f9cf1b6ff8355311fb30f03efa5ed4a0f8caa058e030cc7d45c83b63b2e865e81082de18f0ff39e0e3070dbfd9c8e1e26b68d51fe739e6af96564e7d8810ad81de21e53c27f2d5d4a766cafc3bf6af114a20c138b6bc1cf6d6b11d0d0ca80bd152e04f9534bd2075b5a95789af8be1e70ea3dfadd91f5b1527e399f3fd798691454fc3a85a6190391d041dc7c1d6a08d1c569854dd447596d753fe904162237ecffc7d9db997d8829da6c56ab8054f198c921c5744c586a0d4fd45a53f46c5ccba1262fcadaed8c07d22183b9809a205005f54f285f281c251e500c08169e8cea853546243994e9168745905465f939466073a3eb27f78f2cd7ba2dd5a1f8817c094991d7fa419aee7dd1a386fe09ede6824d0befe0dda93350c44eaba78d904bd7ef4d200bc393c4cf470073f47f15cf2a7a05c38d4238e8535131a25e1bd9ee5b4941113943c1b28c45d38dd3e7d04103d9c01584479deb58475b5b879cf69e3bc305c97c8c4584b83b6bc517fbc1be0d6d7f68b9edb3f0c9bca0237146d49b90caa8205d09a09e4891e2a66a5186781750c3b5e001fc751103c57485cc6788b51459d06a454b8e15a447701ea19c7604da250fc4175a321ad3c74cacd7bfc22ab1410106edc73ccba0fec2b626bd4f76a375c86beb81a4df4d2bc35c5b24481c1e8249b7723912985602b9a7c26d927dc1f5233e3ef52712126dcbdcd088d0c5e24e70c26c9fb36aca0dac8a3cb2359701a9888b143b587d70671ed17f643cfc60c763f48171d6b4074afbf2c97cd74ad8e2f7729dd1091c76133b00aaae3b95fc9054ad89d7f7c11e6683bba9fac4869970a90717f7f1d59d0eaf487b1a91758c6d128ab93b69a1f202d57251a40482e7dfc00f3670b5004f5f152d2ba0844595b8fe289c9c156371c0ac1e53b24bdbdd3aea767d57faf5e4d9d9e3eeebd9ddeb659fecf397cdf63fa6f051da07edb9af96ee6042b9e3f49fee26fe9089f226ca872a85df374a4f96ab077dbb862b587d3830cb84913483ce1b275c0eab718141cf516ed5a5d8af9d19f30ed3b8ebd2d236a292b0f7bfa50cd02816089a4210bbeaa58e9216d56feda74b63d9c11f1c3bf1b42f34fcbd774e393615170c0c0d72fcfc2e99302f706287e9d769ddc78fdfa5e7e8d8c242274bc64094352d1082ff7d2d428f4ade5e39f870f5e6a2a3bf1f88722f8ce6bd867707a0b6a1ba6f653f6ed0a324f2aaf1d45e8e0ea76773a4db6624fdf310d7cf0a4c8b1757e0133e390d87fcf0a034db6a1aa943ae0ef4c16d201fabdc5991c8c0451ab1745a605088f48369892c6e892e092ffc38f4d7c00a5e53456e5bf2ce5697efd6557fd2689b7c622703db4c17f724fecdf504c603ed83a125687216c00c91cf7b975ee24d2124f87a8a7a4401ddf8a114b2358dbdfdca32e9bdc56bbfd9c836116a4ee567b83eb3a91e88dd224e11180ed4a3b95e3912a13112c2c2c979ae8f3299ec0cfeff74a6c0ff82972624f22832aab47ad6e12c69d0cf019e788cdaa659f4f71560f10eb8739ad99d0271496841d8625d548f14195e23d6571a11eff5233a4d0ecc2f2840a7c493e11f73e6d974dcf0c1c170eb62c85f6b529e1f3321ab53cc29891c194e7ab7140b8a5eabe40ef9195e72819490fe0834013f14b59f87ca29d5040a21bddb2b36797538335255a635f2bd70c75357300ba137b1d358f06de8b4546dcb1a6d13f758ca802872a02c9ac4106f492ffd5056e96810c48335350948ea4ff7ccd724810038b992d2674e62c779526f2b8e9924dc8fffa7501bb0113cfea5f626f62d79cef4826464ac75ee310130cd32ca0e6c76f06940d7fcf9073ffd9d3b4e9405abdb8bcc8539d05434aa16c7401b1dde881e17b9a4a6b9490d05984a480444289397ca95fe65762a4ca71711065477d4b38ae885df7090a234d87c838cdedafd1eefe38c8ad13fd7b5471d0b47dbc2513fcd55d0a6d19de69f0421282e46d91830423d843258602221a7bc990dc21c0afc7e9c7d5b156714399d583e843611bed90cd9388fa1592e4b06e503d437b185b9eb91ba07b0da972887999191cc515625357b969f5ef6c11451d8c9f0191c63604a74408c8635b9b6fa6a095332ec9c307b3aee8d84002819fbcffbd8ec0f3a3b1a585c7abaf8329132bc43c031a1dce6b5c40d619a7031ae9ba9584b180e3d43e9aa9daa9259fb1537f7500a1c50eb45647d4227e41093298248b4d7937b7a8e6c8ea26402ade1dc8322e5c83a42b1e6e18e70f42ec2cab31a608849b9487075cd1ff908ff66e6b5b4a29534a29a5068406d2062bd404294ed4156a0207ead37810074a6afc21a12035763fba5ac054bf1c542a55fd61aafc99c3672e9e34feacfbe24fd8d500bfabfce2cfae144fa620a8fc1ae07bad213e6daf69c9948328b502b4de10232b923273c47abbc315f84a801df4b1385db9c301d67e1f8bc3fb4209b4476a2b0dda4ef5d30bfefd3735dd51a9bc20d0d845610b7733775d36026d5bf6068fe6dbf0fcf907cc20853226baf9d836f8a3bd6ea13f4606627c805251a4c48de7b96abf0d8ffd7bd783300a5a53430593dacf428e18806a5ad5b11ac3233e64e678dae1281befa86f814c9ff72a1b315f92c14caf170f514665b2d7e643bf8e998ca05ff692e2ea171ea03136c5dee887bd3ae6369bd3450e54fbb397abbd8ef54008842655b551fdef02fac2088daff6fabbe88b302a82d4fe48859a686884794ef471d48a13bd49d515b428cbb205e2673ff59001fdb22ccb3a3ee24de1b26162a06ea15fc60594b6d71fbbcd03f225de67324df1a4f6cb6c5632b18a08ff20bc7258908463648101fd4c3afbf0ebd77e198f3d66f6161e3278d2ded6cf3b6d7d08618d4ce4b6b3213fd352fe7e53a9159b5fa23f968403fa799f6782416c740bed7538c4fa0356e7ca2799af3b0424469084a552a97872f69740d73d473d909b18f0f5df649a8248ed973141eff36a4a330b29b1d8d73197e4591f1f2515090f333333474752fba1bbf7b4d0afcd28519b67d01c1304fa491e67b9cde83be3894531b71975f18a45f5673c643ed4ee18ac7ececa62108d0c502881606728e86156ffc498f4780a3ca0419deae9e9496dc73ccda4dba75b88a104fdba87c787bb87081f9f2449f6467ff7b40f13e8c72c1e47e24978078b9e1aa309fdb2ca5bdc81168488f8423e47fed2f432b30cdcedc378b2d15e0bfd2b4828771f83c7746b362304fd32ce90d4fecdab79c18bf1a29fc946a33af3528842bd854b54a61c62abdaaf434665b222e8eeeeee2e6594534e39a5ec5ac8628040bf8c157fd0e42fcd1763158b46f5772efad11817fdba483e6c1a3f4a63b5bfd445aeea22aa04fa511811733a5beca0a0fad16eb5b6fbdaf5d17efd54193c4776d187b3827ef1058bd91663b188a5261aea2d53b7d0ef271b51d02aa323d08fb6dac5ccbb0b574ad57fe1c2850b77f9b41dbb584a0fe87ed659900d3904d918840fa5b417859f6347689cb3343ca01faf6a671faf3af5f5aa573eb5bf89f0918f573180687fabea97092d4952f79b6709acdabfc325b48455ed578014b1ad4e1eeab7d56f838210017f693c9aa70a68c1ff5b40bd5b274138529bc74fab5342555af547d70ac025b5f038e2090c5c79fcf0f881d2a97e3da95b1b9084a853fbc4aaa76ee5f103c53b1e3f50eacb552043e774e4a9fd3a58b86fe02daa14acba42523ca95d5d212978eafb7ba5271b48183c2cf2b4c34ef5d130ff8d5de02a85b6345ffb50f195a9a84d398a0a2380b07360f77599a6a25b778875148c13b40f4b880945425578a828aa3fbc328e10139650b9a38b732363b5698b166baf990a5369afbb8fe4deb12059d5151f1eabfc2bdc0c6b39ed9801d603604261651569716b396d6605d52187f67a4256b53c3fffb04d36c52b6679e69567e67fc088190ae3e867af5545d4b567e11f56cd918a3609a5bbe7ba72da2a7782758bf15f2209d01084f6f0d19ecc7e3ca7c39d0d3dd4a51035f9e7c0a792d396538e18b6f6805a4e353735b1e5f45e1534fbbee5444f5d159ab50775e04629635a16413d40b480851e9e26c9373314c700f30b1da752ef4909522a7d5132d1f6bc06331de8b6b79b3bd315b44d45b4db01eddfcfc73ea9fccb1d034dfb34d8c1f660d0b653be4c9490a402a0ae10159eaaf906659ceeeefe0169edd5f0f3b15848c838e0bb26c5b52938f4bb0e1d3a40267b0ca6f061717777a2042cbeb0a9c31a4570125eeabc356040f7d979b8f5f1139597872a0efcc4880ae7013200a21acc08a1fbe2b4e464c5c98a931533436686cc0c392d991932336466c8cc90992133436ecc0ca133436686cc0ce92428733b8cb061f66d3b22c10d723f1b4b34264e0984f0bf08a10c84bd10b6172becf666162f96bc904136777ba13b12f1dd612cd1d28bbb9bb818bce8e9fe820997dcb923361bad1cc218e5fc9973ce39e74c325b30a39c997b0bb29111941767495a8bccd3947c94342d6e72ba60dbe27331c6281d4ea6a166194b295bb022bf8eb815e75ac0390bb5410e03ac8d06a36bdd0e275a3b8ea0271e33c06ec78b18b0d016172dc7015800e4f2a26ddbe94f26d38817a704d3c2324fe3a2c425a0865229beb8c07038e8c0cf5c6899a37c482f9c8e9760005261882e5c7038e8907e3e0acc4cca46bf52d251c682c82c2b2898023b8c3eeb76188d3efb11933efbacdb217b5297e570769a3c8e82117ecc94d127cab0204481940a218410420863f4e9d43777a212c8e336f3c55dc1aaf1556002212c45c89179e23ef7b096f1f39c3c0a621672420b0820216c424e78c294bc62229df22435f6f0d1369ea3e2c7d9718fe74cea4d8d7c2a558db712236954641e940be8b7b0ba5a5521b2f2866d91e4eebbf95f2039b2fbbc5a74c5e2641b6fedc58cca5349a6877e266f5681c994325312d90728cbba793ac5489887c7477bf149a76d4f87c60df6ca03c9994fdda6df9fc7f62b5bb77d777467d8a6813bf968542c6d5b0f168a40791607d6f86c02b95a1cad46988c391c15238da320743bcd00e315e3100a902451c240ae18b3d903283ea1c61efa67709b1c918791fc80cadfda833ea2142a232b264a1644434686927c4046ab56bff8db24b30e3a116d693e173fadd60f27f133c5c54776b8d545ba2b9fb8ca31be8291c04f88626f14316c72943d7bd5457ed885d9fcd3737677d74f2c8993e09f2ed22b153cb1389c0079cce1ca8bb6d93f4a10b994601b9e42c46dac04a37e1ce53f3f4727e9d9f6240cb2e00760517e88ac16a77bb5518e20aaf3c4d53ef58e0f7bc31f7e80551bc62e740816476b400824b870331428cd2e7477302fcd1b02fde20e10bb8a6875d077c353d9f4038daaf62ec09595fd50e61fa2267a66c2ddfcd35de4796714d8c55362d72fc6e1bde2296cd345fac5adf6691314611b968251de2f7681b9f3ee05eb805b3c855d2c45f710b10b5d449fa07988aab34f9b6aa8b71304e3b8df7e8795df9e3bed40048f95df7e07fadc5328e856dae940040ffa2bbf8efa1220806a838f58e553a0d40db1c30c4fbb183e8fc51cfa2b14d6c351eedc6f6f8591bd718ae997bf3dcb6b311b0c02b24e9504d5d726ee2cca27cfd9b399f908c8c3e2581fd8826eb3c5a008f40361ad56dc813147451a5ad00fb24e35f1f7bbb58991048cf2872c762176ec621c91046cd32ae85ca338522d0f12ff98a453fba4055bf1a753db648958f514e47c9149750ea8099007ea17a354ff519408e04de3ed5e91444e50b537a6d81bfead02b6e91a308a875d58f1b0386ae04ec54bad5068cb73425944a9bdadb39c623229f468931e236dda12344302e57e2371326cceece9ff53760e83a169d080c29a44a553f23dc6186565e35f2052ca51dd4e496e5b696d651bb5701d8cb90cf4577e3bc574c783762ca718aee395a3fcb72ee318d0fd5b10746609fac59d2b903822c51026980115b11cff1616d02fee4c98a2ea0fd41e93f63ceeb038a140b33dd6a9c8d35e77f1487b4271e5282134eef84ec3fc9b012ee507da27d9ef2e97d2b086cd6e8f79915781ac956e968d85858bb15bbe703a7860b676d95892a05fdcf9b8b3f259dcf104700dfcac83b7a8f2e7ebd83abbd87d3c79886db575fc03c42409fd1af6936594966225181898394dd4fd575c7030707576bdda1b5dbf96e11452a8d3b4ad3a65849da572c15775872c93c95b79eeba8e7b2a7392179cce660465985701a4c56c0cfb8a7c18f3c4352626663482397bb672ba711450cf2e9078ea267bc35f9ebab537fc6370307095bb9e6182fa6f140ae57e2332c304855ab731a7b32308d71541d410540ff4832cd8aace5a9c9644160b0eb284b8034f2b573127a0fc6229ed793c5018338c61fc72ce39b713d7cddc276ece395b1e832ab8aa35b6e560e851d598999909cab0c589626d620816e571071677fc351a9ba90885d9591c0284606d1ae6a89dc55953117405d56126a8fede10e807599d5af947238b13739bed1d16557fd8729be601822cb771138cbe5ca6200a047fb28c527fcf087187e280729d0e44f0e07efb4e8714369002233caa1caef31cc5792a3ae3847ed163f548a7686441f9487bfe9fc7aaf3b41327963bff66625336d8147ce7b66b1d78b07d45f27877dfc83483d3c1d968341a693468ccb9ed7147831b7d8e962d3c7e90b4b735ee74cab5a8a30e1aa12f759b28515473f476da798a2dba379877dcc5a86f20e20a25452e1a10315a2d8535353554ca1af8bf6eddfa71178380affe5ffdbdcf9fe6dff3241138304509d5ff3b03dae26c7b91d3d10fbd9b49a3ac6a71837b636794559b9b8cb06aa32df3aa91b611247130f46bdba679154f5bad376776667698c321cc6090f6f537a94d6a3f437767ee06aa5946a9c95473e3bb40b50b506be85eef8e3b5d3fae1910259c6c139e92f7a626001e0ac812dba442af4022c3226d71b65dd69ab40b4731eb960da1cf6e2967dccde06e6a73e84f6b37a977b7dd34d2881bf50085f20f0b7585ac084251500d31fe4f5d3733434323a39b399dba6e868646868c193368946864c498799a938c6ec60c0d1a4f06aa86ac116bd480356ad4f0e61a356ad0749b8c193c63c68c0c9a193268783266cca041c3f350a81a3500408386e7a150356a000000366cdcb8515383b251c3c60a33b6c0d13858e7469be8e6834f2c45a0dce108800006c09365c616f4dbea6e775a6185154a2bacf0b2824b0b6559e15660147f573f1c2a770b28c22c3a68208c4900fcf8b0abfca3b35ee65f1d8054f8637db979797777797bb9096d6978593008c469d4e51640aa0e0408cbb5cc41f8a3a8ee10eba7f6d3ba43ac9eda8115730bd84e2f7f9c863800ab3b04844fed37d51d0262a76abe3990dd16537b0c0357d8de165b6ca14585694bf3693b840cb73b84314a39797797b30c05231249d3b68d721cefee32333333cb364304ba6ba29477b7e4dab6b8b8703a2c49a0dbbdbc30f3be944accbba50cc60c142acc807e5f1f88a651df25efbebd9921d0d75e9c15434576726024074aa648d162033e8b031f3050377630f0bb5453c5a28956eb89234fb89e2092056188c891a12a2d2028988c860cfd70e489941756423ca7879943440554402a95cac8aa871c26409165d9cb4e072ffb6872547fd49976aa06ff258a0bd85a3caee99bfe03ece8176a43e76f2b75a7f85f1e1b52a2a2a9455092453104130b9d82b1ff866136f3d1546f6668e876bcd07d1b94104228b76d93f0470521847ee3363d6e76fcec684f3819628f133b3b3b719b1b0ba451cb51fef05bda7273cf7519d78d62ab8753cdb647f7b0a908ac47437bcc517ab8cddc591a3a357d3a029b9a464c097394119b9a4eac4fa76650cf1fb7c968fc19c5d1488e648c31caade31d194992441a914692a5cb27ed0575ca6bcf894d01603dff1b00701218ac60e76327d597546771117bc3b78e5923093392c6d9c62cb7e91c7fde619fda7504b3f79faf4934619d224999653de3a2bd83bb981250cd03a2df6c91be7fbebe49c49c650d5f3b494ea749249e93946ddb0aa733fa154e076e23e95d41bfa982d5a922d2a52aab4898fb943059549d7758a250fe245168771fa744f571fca46f1d52b71ca0464e2d1bb376717ac645bbb2a8d08c456216d7d3a9d1fb73499af3e9d454c2fd744a32e954a6699a16a3b427e164173e01708dbadfb1ffba0db31645331436fe6f466aaaba29c47a2f363585d85412aaaa53cc9a3b9d92305879aaaac3d98210ced66c2dce1328b416676d50fd4f46a0f0358f5d276cb6a66b4a99af4934b1c856fe3336ab6444aa7f84cc21a07b8310460e7e9f461576afb0d3b801fd260f87a45badeab3355b527234f9e408c91fc9f9a39306c483c463f4b2d38136377334961372c36de918788c3f473f3b1d4c2308903eeb7420653ca0fdf1e443038a35e4f433159bc150f1c1032f47d0adee900f3e9480404775877c50b9f0404975875656b81882caba43abd80a36a4480592a38f7e14b398c52c8b5954e1e56b26d3322595f9169a17faa202dc381d27458f32a6cc24c3351e7d5181c6d73229b7e55e007d94754308bb4db0b965842d235c39121942243cdbcd3a2abbf528258491635cba05316a79c1efd598a0c6ed304618638c1146eeae8c468a114a0dc038e79c114a23249c31c2491a65d188481a6549c0d99364ce39238449c09e241d0989495a919bb6c242a96c29024ab8694b29f5961728bd085f229d8c7a5c5e9a347a29415fe29d8f6cc421601b81b634de0e258cd2e3588eea9b216a58b1636924f9d16dd429437acd0509300eee64a2271b7cb4f74302ecc204281638357444b283d106e7c9d188c4fd2daca6470f580f79b2c18724c922285313c7624441e520de46cd4d910eb0824c4184779a601e8410e15765863195a10cf4a06aa4511638918782f0a46e78d6d5830c6a74294930e25d154d54c7404ff558d5785681819d2868edca5b85bf525408877800823584c0a1df42055b6c51a14ac84404fdb2f52caae840d112ca228b519190511010241ea84508361da2222d52b0e910413564484811c907f25f561003a2ca154950af2b54050684a822033e5441440374a5ae50152060809098a3b64a99021b434290d4f825212b9aa8b0ee90101e3fe249164f78b63f73709e34fe09bbcfbb1afcbb6e9d1f7cd94106c4ae067fca9ec9642a519d8c0844602145284a952150a915c8ea0d2c503c8148090e4451451b6d8c0cfea37780ff68877eefb6d57599a3a0c951f0d751922787dbec301a7539dea30f134cac179be0fd191ed01313778b9ac5e280bf36daf36a58680fce1df842c31bd2fa937c9465dde6c44e3557b3a753df644d9fb9337926929964fecc27b33599cca01965b254d3480260e5f1d3ea9f2ab7711b7fe8362a47c5262a6c62bd6fb22adcc95a0ffeeeee439811eab74d1c204710ba63b4a3cfde01a4d7ba1d48d101592dacc55928db633210f31988c5e16ae4c43b36a0dfc27ac060310405052dd9dd65d7b28cb3cc336766661bbc3397b2bbbb2c087b98b5738407099320201f25307377c8ccec5b87c3a87eeac4f76f4e04d066a6d0aef26f16a7250d356b276abb0d8e624d9fafb16294a05a11582c2b4f1a3d64e916b5283fe1c0f563d68f285f9df22c8a4b0a8b592d2e4bc060e6c5bdb51c6e2db7913d8eea973ddae439e184bcccfda27ad97951bdac5e88748a5f8cbc2479f979f1a9fd2f3c2f485e98d47e3921f4d82bccbc0265cfb62d6154ff7ff419f56d4e6a3fcfb73551fb373f6d48ec8dfe1815a2f6fae5e2d22208c7c9558c314e4163fdb61f96db6cad4d7befa7b68fc9a3bf9d5c5e2fac172416c76bff8cca5d5eaef65c622e456eb3f2106eff723110d07e7b1e1c3fd731e7689dcbcb25c6ffa27a59b98d0bcce5e552c5a58809f465f512c4850afab9bc629dfab6567db90d7581b94d0c0ca19717941715cd0b41bfdf7e16a767a6a05cbfeda7f68baabd9755f712a43def3e17d78bea45556b5c5a2e515c5c52ea2781aad4feedd5441b6ca3b2c55a9bcb6db427914a32df9e04721b9aecaba99f0745aa64909554d517d58b6ae6f5a2c788d8942a4208a2aae2eeebcbd9ba5a31781683db984c93e7cc66c63964cfcaa153cb417845a479a7f948ed9e5967a700ec0d0138a9fd9f8753fb67aaa0b07e3592a8464e277b679408e83aac878f4ecdcfc13d670a92456d0c3dfaedbbbdeb23313268cf3d0fed398d87f65ba783d6658eca6a16a76b3f89442291d89d3392f62412493b6d165bf1451461d195833b90cb94a521a4882841a03245a542020a2d67f7f5656687ac2a2269b037e827aedae036391cd540312d7a8c30425f761923e55c154d559023b3a727ab8dd49e496abf8c904340d7de19f6463f606f408051fdbd4858287eb477030c5c6d7055a782acc0456c71f606b5b5ef9374b1f7cc1414560ec618a59472ce39b32ccbb26c34ca36180cb220cb7ffce7f5ea55afd8a7ca192ae8486619a723eb3a08e3747015d0919d8e8e675b4f26399df87277ff18fd34aacc5b7b406802fda4eb93546a53696f0a17275f19902ed96aaf67a6a07ce23ac3e2349fba36474d4f3f49e5870eaeded9d02f87d0de239d6a4637b24856a1b10595b1202836fcc8325794025f32266328b46060b1015f7776defd5e277d5fbb9d9dbacd8c19b5770674e6fa517a1377636c8d76b8cd683b428770e35e61777767e760406b40bf9b1a638b6f5cb24200593b332fca0a8af02bd5ec456110cb526508a11654825a104786abb5641640adba4340403ba83bb4048c248df0b0b058c255774889201cd41d9a4151e557102f168f91b6570292b5c71e93edd37ebd8d4e919e5d8d5a79ef56ba18ad6b2aedb9b46d65723a2b5d96555ad23a19cd64aa0c60150f7592b64a09c30a493367f76fc33382cb8e055953c3258055aa0a6bcfbba9ac80612ff40d0c27d0af4018525d07c8416309dda00d0aaa0e397207218410c629e3438eecec7294bb8a6b96209ab0ea0e3551d5187587805eb5656df867513280d5159281aa7e10261483188cd57ef7d22ee9d403e9357792467a2949a4f75324ed109ff4fca4784476ccaa3ca01fffb08b7ff8879930506bc889ea27a849516b29f0801a09e2279d6ab70932632e172a954ac5c390132652886255da86f4fd113aac535348edf7550e279d82b0b494705579fa688dd2de11fa39cc8be04a83a9604ec5635d1739163b5ba833935e2267a8094fed8fd07596f3207ba355d445d9e4e6234390be8bdc8679b8a7e8874a1df6afba4d444054bfaf30b7e9a02cc86141b015acba7c04f4f323f4a342d02eeb52972a2b6e313640f983fcbb3da8363442d3ac542a2181c2ff361d3cec07614b7777773f08a37417c6b286b1c15011980e9aeed39622841d3301fb04a14a9b32c2d94124dad2e4684bc3b4531984409ac602b91d46199a22745f6319692d25e8cda5126973612ec95882252f95bab425e6563e202b2ceb748616b4443338438b12ccc2c0c0c0b860172e5cb898a105dd2a848171181898868181e16da57361c186f772532a31c330bbbc04bd7043f3a52d43c1c4e964fc82c60a1a1333e2b8c4e9304c43938e8e59b3bd4161381d5cb3bd616a183060c080c1304627146a4a6b8a81199581dddd2d633784f206afdeddc0255ada5e369a2294d9c5763452baa180e504f4839496a8c321342098436bfaa460fa6ced906ecccc5c629a2294b91be9c0f062389d1844f4e3d1dccc0c21f5ee8592a590ad0acd504626851454508165fb3f755250eebe8dc6091d0d8d0c192e43860c19289862708c172ac0881123d375de75ddc5089f0ba834ceb6253185684600102020001315000020100a074442d148288e64514f1f14000b74904078523a9408a324c781180521638c3106014080310464a486843a00040afdac54170534a324e7c89d94092059114f62642105869278d1e0b6f7cfcbc2a60dbc09f51c2e0a51668d5cdbdd6cd686d16ec10b97dee541dae91929160c9f1210ba93343d5002b5b9c63a1c88e7d6e54016d153585dd539f4ec6f729345d565384827f6aba6947622e03e96e8cc669e08f4e0337ae3ad5a9b47c3fb1ef024d3315ebfc68d9266162b8f328ed9e0085db81f4f23da9cccae34627d485b1bcec836cca6d6b52b14e3a731ce16c5e690ff2c58ca1bbde99afd3eeb2672e86c5db342e479c88e8a38b8640401ca3ecc055e44c575af83eddcae1147fe59901e0c8aee9c10e34b5f7bd90d8637fbc140d4c87bec85daafa7634fbcc1b1931d87d1a94bfd317fed474d2db2d1ebc72d5ac0d776b44c420e4bf744a8e374310ea73ddd83b7f03c4dd7679fec024bf89ada9deb0e84b9835332f24b243c4da99196ec1f63c08f6089fbf862cc8faa2a3804521684657c40b1a48ab1c9fbe6a1b107808bbcf43ca3dcade7796bbe80fd632f30919cfe4acb020516cc3ece3a10a5768e06f8e107e8074f39ccd8a54121bcc1811cfd793843249d0d9735a24c7806c320d343c701493e724d7bb6ef2148336140ac78f5d929a1b0dab8fe97d145b7ba8a271a871b299f48de5d7b4614ae087d43c48ab1d05b239c2218c9291288d2340df259a2b81b81f55de187f98a06a79f5ef034b283304acbde6160ed634b98919973248d70cf78d75519409e04cbef42158a39854f84ca8c01afd92e38dcfad415eb91a99fb702e8eb995a9303ee601042a531a6169ca7f80e96409014f477c25f38c4117166db0c624bbaafe07e8e77531396f7059cb00623848c71e24ab76af775e0cc49e31f518c3ada3297780c5bb67b1b48cd1e2aa332f6ee1003b60db61369a457f93a2bc4b21187cc473500c81d4f0b3547f157acbf047adc800993d6e6eb6e838d9d8485c27b51d5bc9aa093b46ea923134a981c67f79167b3d195a8620841c30051591973d75a498a3f72b555eb38f88f20f0060d51aaf7264ebbed4c151eb3a03f2ac51a2eec5be1766a1c28505da13d6612a05f5b9a21324ef3c38ccf9021f84c570c1abfcde0bdf63cc6e0fe2a406afb8102f27295c7a5a2480c659849697632162f5006d9f566bd35fcc3eed820044ec2691a4709b687c966e5121f25a37a92129fb96273877477fff51118aec4519f85ab01ee665c17790ec6320b32ad460f761496b91dbba07903cdfc08b4c313c4eb433d519270228e36e4565336c0ae74db81da7159b69265e5b378334a327872b97a9f31047455de1a34e4637db3dd61c5183002eac693d81882a63b577360a83e336298a02c680656df1aaf62c598e591c96ac98fe53546a92f4fa1a9ee9837b08add7280bc8ee6c02eb40c6673ca96e79dfc992142c04d45a09c5f041884c22460225aeca934b2616715926f95f374c0d86207aab0da1cab067e41ff2cfd941f2dc06934ed6a10d043e22af3f8a10c9ade322b810491a14950933d98af908e8a3bff87fd92e193cb223e0bed8d6fb22fbe0c42942a3a5e40228f6070369399e4c23a3d088621ec59f192f51f4284f4c9c9886f5c7e8a177bcb990a2ce86a36393aeb6acc81d67b953b6d4f63b705a38fa7c94d35f67e6fabcdecd64e0663cb064f395abe8c7c53f692f0ae26f151a7b5a9bea64b07a1aef89d89165b2e619c8ef4dc0fba25098be6a1309af3b5d6cc9f47c1a184e923a35a4d2c238f2ecedd5fac1bb97563076fdd6b8dd460e14a6f3475b4b366f926afbfd6e6e66cbcd434552529993e441aababe8dbc125dde62abb474cf09bddeee732356e1d5df883f3310e86b04c8d800b35ce86d7bf732b7035dd10d0c95bc6fae1dd53f240ac40b48a9a1a11a091280414e33765fb7699498352543e6fbc1eeebd9066af74c39088b175c96b337a1ddb87c4ae92acdc62365e493ea92b44b05a569a3687b9f064462518e48fcb91fd38f8c0d97d44787a2f1a7626e2583d0883d0f2c502770859660c4fa1eaf62d1dede1edab2f51b4a0dbe356d13d5a903d354b0d808b2abf09c3b6b86e5e308ba37cf45986323c864c994562c7391ae87b0227b75538bf7139aa7e7fa0a1eafe3ae5bea51a42f48708f304ed1b25d789bde75620285fede2e2fa510497633253104bd85465080ad82084176ef2edc0a78d551ce85fd9795350f25279c3fd6846872cfb403c1f2137ee9b12e50b3ed691605a51b1e195437f98594726de5123cf8a20c4e788a694d0de22a78c5ec72a50f8fa81c383163c8b40a0f1a484e2be22e2c754924901e28ec07d8b69d90ecf0bcad41d6f3efb106299eb361e3af21a03b259397b6706d9373b869cd282c2a756f20d138ec6c4c9f59608a770a0c9f300194c707e41265419f7b8787f741b54de3cd60faeadcc29b3239330f04a357129792e746240e47d3f50b50bc810f3b63ed358b83e8a872191706ddd75f205b32c6283dc4a8a1cb987ce1a6a2053565085121cfa287b7096af437e3c802648162b274e82728d6216bc27bce48766344b73f77540362d586962b60235a51f39f17f612e5f814a202a0ac22992ba6c15b5b49f07f68463994e87306132980343d91922a2c3670b4964eb9223804bf8f4b21ac507fd3965e334232ea31b8a7200bbccc7942ce55177c3b6c744adfaead494a78571c7cb17b5278465dfbbc1edcdf2590ae56c6235850d58781125920b0035c733dfa83ea47571723473680547bc66d2cb7d6303a7f4453c439a3bae1c03f0413ac6eb252b5871f05bfd15173f57ae67c720d3fb47bde4dc687fbc07a64690c7c6e66f715496316e42cf1d0a0aa63aabedc5a6b95898d1bae844439c63304ce50a9ba171870d8a2f325cb1591860aa846083d80e238e0cd38d4f74b6e0d04bb22a7b798b9be4a4a69811fbfa99f984c3fa046a47afdd62c9714226d6c3180c8166e2192b51cc0026c0579ad2f871d656770453b87ca02683027bbccb38e306cc040410b096d36bfb2717703e1983bd42d668788f20f6172bee11a4b30ca4ea6cb63820e417e1290af6fe1eff5de8bf78fe26bc65989b5673f5b18d067235cec5d1ae479357603b360261912ce11ee6b0edc15a51d546d982df22ded11990e0f6f27e9a377dda413d55417b97d9d76a31a0ace1ee9cda8cfed7d3cc5bd530eb7c40d14d84e0fe2b03700e04a46a05ae02c445856a9c7e78036631588c926da0633e180c7d80e142ab9a1dc952e42d9068697df384e0da365a933ea35872cf2fe726a8801a4e8a2186caf9a0f83fb78e0ac75aab439ca72bad0e041a0192d543640f01efc7f5ff110d2d7dc61bf4c3b2d4ab71f1783e36dcfde380bf50f0f6caeb2f942d1b4069a396097e32d4301f955eb7d83e78fe26c8782a73a8186ece21f4a25e035ef5f8097dc151d82a7dd18508a2b7493840d8367a2b77f63ba27ffa0a021221957b92249f0813c2f5cddf42b7cc5b2b660a734b829d9a2b47388589a3464cd58a9f1df6c628f4bc034c875e6017aedc514f730d2d504ec974a7dc7de278802c625ffb38c9f6591896ab42f50e3a55d0663b714a4d8a8707e07b8c7e2964ee3eb81ff4fa555bc37ff2cd9fdf461714e032fc84021c49f0c1829442f07683b53888c56c27596e8c500ab767d33e19082242a73e0adc741129c0bfc79556c7c88f85b6ca9384ff73f1174ca9503ab33ad963d132641454240cf54d0c01d01bada072269711059f701a1cf1ace92160992bf112b1612c7b9752417b1e3c3e9dfb3a4fe580d0470632097bde3f5bb663938817f55a475668fc817af40d59c2ccb702d306ac9f97c87f4925878185e519d4ececc2f5a1b53e0dd973c0bf5b2ac555423f72657d656a82801fd05db00b3274f038b83a5d578b98247609e243ef7146148475722197b5f9e5ff666239ab6b899be0aafa82eba59eba7efc2cd8c7f15662b073adeb9102b8b87bc8b31ea60c8983dad9e5237c0998ac97b5078a472cb8cf93dae98449ba686f58e68db72eaabbd599045d53d76a6e1b73a12aace26448cf459914d2614b3395dae6a31ff0be80300adb7814be3c5beb2d0a452f67cab49eac3bb516ec7beacc869d9675a61070650237aa6b202bc475e59848b95bf84665ba68c36aa52c600a5b025234cc91316e80b7d91682db288558ebb282bb8c4ff3e9c1f39e163e3c1228717b0fa486344443fb32c511b91a028a1cf366c340a152ee63455503583e52e13684d7886a2917ccf2438b83bbd2683af19c541fb8338c56ffcb44bd29a0908f7e2c05bf4da5cdea4a647cc338278a3aad7e695edb1d195d8d24d220bc8507f7c86f27b921ba71951900833a369317cf2ba9dee474d07268bbc5580930f9d285bf30089c05289c8c5e8d089a0f32692ddcbcad447c194d5993ef23da8d19f463930eb98da9610ab940dcd642b4173a934108a3286d4c9b2c0017438a75d912cd9ace38f86914ad55cae34edc2d5867d6bfb74bbcba5919638c6eb719507099c220b47cf8177b62172e7e64c5c0405fefb897d051f206102e41c487f43d98b251e7ab122611561f5d00264dab8f8b67755e6edcdaabe6c2c6f62abdc9017cc7223dc68378074a2c764c89b07d817cb9ec15191af536e46cf95f1b934fbc11ac9d308ae1a949e5f6b74ad598b27c48c253dabd43421d3702869702aa2f0afcb9295ce8cd47a9ef5cc2dbd2b4204558336e6962bc80658871c559116083738adc2184c1968e6b7b5eb228273a239ee22cc905f27fa1132557293c8c993935107f5cdd189f4979b9a8e550b0fb58549f72d84a94b630381a62ab926003bffbff46fdeff9e9808fe2433cd50b0f7ba541af943b906adcce9691831c7223d61d861b4180183ba1183d157e4af15bb209ea37b38d8d6ea8b240ef12810bd1bdaf36b75fa9d9f69216c6916f8ecd941acaf40ff7a95a02340740f21b4f4170870d84d27f5b7438465b42926751b6c924af442223d929a46ad757c22d447101a7cd6879411ce9972c5bc5a23444c426df080011b8e1b230ba119b91be1331872cb9084be0a32d948c217d47767849f3419d051b9b5d64a4c2bdadde775275afef044faa6ff3369347e37967c3f14b5d8b8c1706873aecfb2a073926a72dea25566bff213d03a2046e8e2b234d9943d2990206210b0d0bc141be07c52f53c933cf76ced65cc94fda0e7676f220948b90855094a3694f90a969d6c0cc9929c8e7d316d7f0cf0f14a433d7b150c85286d4667554eda151f76909889dbfd10a1407a9b635724160d864284ba2424ab2d47f6a05639dedf50b0a59654b7fbb02bfddce02aac5b8c26c39f944412fc69cd67468c33d0d05f577e07075b9c26abde9850e4e81f68dc835b734e120005f50b4ccae28945c3b640b81d27fd327532050122163c60243feb5c62ab822a3dbdb5e4aef2b2fa00cbc99b5d044fb5cc93dabf50a6df9c9429ef0f71ea04479c3975762354512c634ce314e16cd96883304bfa19f94fe387844b4fec6a5511cf889fb60937c476a15b6f7ba0e5d54a91bae05121f678f0e46dd3113392de8a66cf13f5fc91d1e150c52be389f8c6892f1cb546f0e72ccab6490be1a9c2dfa1fd18422dd7ffd75ff8e057716e71fe9b6ab2ddd3232cbe49bed402c0e58c23f95c0fe783c9a5310815f4a84e23f4b4a45acfddadbcd1f735a9c83e1f1cf059ac81c7c76603270871f9141eee5902a6bc26b3e7e7fa4278e1968fd1f434b70bd6fd0919c95e498d9006d8404f02dfe4f2cc92a993afe2b2c0249b6b07b89a5dbe54be2ee399ecc8c55540124dd60d1daa182c8e5338ad3fb58a7219899f93511d4865a6204d9c068669f91ad1513fb3145a499e52d609073b627486bf5bf67897b4b16c58499fcc98c21da317e93e1f048d9fb830e8a5956fc9abcbf6f74cd305a70b2dfaf42c0a34b7da2b288e26bf6e3268872ae470cae9d9be1b83d2d74cfcccefedc4c2311c17cbbf501e6b8b7b4cc8e262f6d8a915f88b056614d00675e3529c3ad008a1ac9a6f865a57bf8d49f99a0d064429122328daf280b743a3997bfd9c8b168ce7ec1e6b91199309d2850269062d5f32a328b52a7cd3960de3be88086eddb975fd1b8cf7a6c7d02120c2b3257e22e90b8b4c73789e2a7ccdc00a09bf0a5d044a05492f6eafa8aa3cc147b44ea87f84ad3f607187c056c8818745b3074ee4f09c47a22783910d57effec7e5528ad54326bcb8ed322602c33cc9a3099bcaab6e9d363cef81765a767e9834888c9a0479974b145664802da68a667df84d324c8236b3edee161b7c8b44a87eef2370eef8f0276320bee18027dc1ec6a28a49111f85473074bbc635448bb505a463429d08e93c31bd461933afc54a5218da048a0bde0e862837f914c5768a661c5759ee27361b78c8a83e450e0cce66e72d210ead649ab5d22d3b0b41585a41f9568bf3a57118094bf827c02c4ad1ba594b02f09a757d81f326e738f0d765deb10ba676890e16cdf12ccc4460ecbd31993b9a95e8809436492171c873df7a01cb49b30be301a6dd38388df2f17ee42d17b111edbe73d590c7ffacac4660ed341a60024d55b072c6058f98564ab279550faf862f8e594abe42f7649c0fc0515f76d00842ab9171ab9635ed3c76bcd34b01ad0b60064ed9879897b0fcfc86bc34214de0201badfb30283c2d06bc792605fca7478d881e11f33b7de9f4193ad656ddddf22f442df1d56ffbbc52537cf3e1141b9b2ac0a8ce8886cfc080bc0baefc82be6fc077650090b9c0dc4e755dd89891fd65413e75c655f5116be35cef216fab32ba6a18b2055c42db431b30c28715df4e165f4b40d06d43575871f8918fa7e345815fd58f50cf4d820df6bdfbb56bf9dcc785dae20f0051f11aa89d7e31a97e52f14ae5d2fa280303b053fd6b346e5b77172ff2870ce4ff20a36456649e1a7d3f417fcfe744cd2b6b0c3066f8d9b85988bd98fcd3413bcfcb94ffb090a831c564ee300b13673a51cc9c3dc585219ba42ccb46df0bfbebcf90062648224338462c13100c96afeb8b5372f04c9e3007f0be09960eaaac9c814b8ad6ad27092649a529691f94b72acf33f96e92373fe7478635356014c3ed81d0a4ca3083d1e0831ec2e2884478e09b9c508458ceb72bfbae645e5a8253c5233a67ce3abe34de9caf2021ad25ad63099788435e2c275900813a777e5f90198a848d66ab834ccc574d52939faa181b29893731e3de7763c1069ae700a0510cff70e21236301522549feea4552adeb6fd3e7ff9140530b5b33079efdef16e6149b238008988afee4e827474726ba12418d768bdc86d5c819a0cf345d07bf637bb29944e913a721d0ed072c18622a7d84e392705b0d9773e0a74b940e39c402c1720ecbc4780a8de27e6b4cafb21c23c007632a1ed892689ee138c2b7812fdb537e3d7b2a2eac3cc240ac8fa2a52b01c1008d6fca0f385cc4a96c3cb73736eb7e7f29e764aa4eb6f73a996e03f92e968c5c04f574a84c1ea552eda5c4b17c2c10a42a53e4cb0df46cf57b9ba0af98e2c7ac0a3c568737bcb30bb2e5b1db0a9dbe29259de841586019c4b475172eeba792ea7122529aa6b3770050c15ffbe7dd74d06b8443e72be3b0ca6c49c335b46ddf49cb1b173513085e56c9a9d8f2db5b125053795b1b0d4fe298e543e6aa54f58a49e5ced4ac3236be4bbce1692c932496c3bb4b7060a64c5d825a7a41a7f5ffe28a0a5c3b95d30373f7dc48a9629068bd3ad89f424e79a665c0983ba6582057cb9035239e1a8e31748d0eb7d0afa9486424d7390ce773196e83a772ab75c6bbedcf3129b5a85f6319aa5942885467b7db36770044925b2aded5a3f3ec6086680e201f7f97509fb606b7f3b8d0b44b6f448095731373d9db6255ca9f26cb444311f9678acd641d89c85e05095bccaf7dff19b0613303cf6f7394700f995f46ac416ea3a1a5543bba8a41d7339789250a279764fd9135c87fc7eb5a3c14206d0641ba6b0fbb506298751f2f909552cb2f8b51e91d4d664e65d017eba69d5c786758db2c0410b596822637507b3c40e7dc75cf7aeb670e94fc5a002a70a49f61768d11465fa13932c7f02e4c44e15ab24dc1b2bb9cddb0ccb32e5dcddb44defdca44c13f47ee362a05f75a820b77afce4dd13a0c9c75cf2e2c7edba935c4ed2ffd8a194927fd5f5f7557786077a7c49096c0130ab36c2532969c8b6a79e11e3f01c5f62742aeb431af8f1e2c1c1f17ab22ef2a5bf615d8b6553d23dccb5297c0cb3a58559000f220ed2442104e2c0e94f2b71a4504ef8505bd02f84589ced263d945ff13778aaad4d07c1d7efaaf7ac3b2d364fc4d4451e31bb1647c8bcf99718c32696b25ac55eacff52f4eca46ee6361185e479eea64c375367945cb2a8a630c64bff3f8e249cd3e0af3f8b4c2db4a26c3e381936fcd35d7ee7e63ee69e367084f93d1c6cc770963e81b2311923788d9f02911f754dd501831667d97d9782578d8b0e77e6142c60cf8ca5a17ba209425e0ee2741d569b10b72a3bc3a86f94de3490e74c5b701449b2e7f565ddc73baabf3b156a9bf855da03b9aec964cdcaedd056cd488f011bb77cf810932218a3027297e73498ec8a8bad769f9515aafffec009680b6202a2c410bd7f88594abc09d34831aaebd8d40c3f428335f8e14479d5aebee570d77aa3f4ead68bcb6948d446c22dc63161a84c0bdcd29370d2f40e4bbd5b17e1a9193262aaf1d4e1de2058c7c15a11f42da2b11c71161adb1d35f12c006c433c7039495531d2c02c8938a19c8fe147b4def99b5702353de61b5378fd0cbdc86d544cf38250f760713b2a22610020b294ef1cbe4daad9067318f36215d48ee9bd7ee59a5cfd7855c7d3ffddb51ebb0204bebf8e60e0be6ae80fa4d5ca7f3d70e397132e0ef848ebfbf9e1ffade25ff8109a291600c37ff2c44d7572c7fda71c76586fe502304c53cd9d6d931d6a2271326c55b1b31a44976d8dac42c4c1266f0bed1ac4b3259189fdec3d30a739300a1651d6d3d470a121d5347cfd88e8507e26e82307e333b384f12dace0017854bab2927896e573f9727af0cf816e928127f986ac9b98d84b051259ef212003998f15e76a5c205de1352b4ee369ac69eefd228480b824a16b74d1850dc83fe662b6dd502b0d297c93cd9e97fc59f8a14d0f17359abd34cf5d3e0cd329dd4da25497e7bae126fad8912864bc384e8bddd3dc7f2bfabd68bd61539f59100343033ce6d72f863796240fb281435220423e56428905743fa53a58ccbdc6798a22118007a4a12b55d942f00c0f12f6e4b4ab0893d09a20a7a309541154c3a74785fe5e68b610cc72487ac0f2f4809080bb976c18046b8712384183419bbbff6f28fe5cad134f31c9065cc1371261a8e276a26f356122a93d263b0946006776da6e912455d02a5289fa5a242f2980c489b825b6bebc832cbeb2eb09620870e8df464ae5a68c99f590e8d3ace01f5950ffe1e9c8dba1edd32881b63b0129e557df3feb93f1a288ef2dcb8a7f0243a7edb99757a64edd3ba5695f4e49ca31f36178721722b2760654200bba3b0a31d6ca9c24e735eff21ae89d6dc32e7494ee4f1ec1e74d705266423d87c3ff0ea97cf90658648b042abc5d8bca686ed4589f455b7fa355781413f1ef641e28afe01db0bf32cfb8d7fa72f8525e29ea84192a75b7bd25652e9652eff0d9ab878460911da73a0b5821f763bbd73f08d5de78d0a3fecbdcb692310981b52a24ca51c67833e98fd771ccff9fc603d402b6c04bfeaeb3626e6fdd978f9860ed5a1e4ce06a5069eddc868102bfe472340f4d284e56fe56de1bf12c42ed0670c5e3432dd3b758f9897e70fed9d799eb016d4c0c353e7c1ac79bc923a60b71b948038cd11e824a48df9bae43a150d0a06a6e80573f0a2f08b3b8cd4bd16fdb052087c8709289c716a83a9e6b43bc9912b6da1b1784c689c4a3c7398bc4826b1cf3168ae7cc742b329bea3b55d6a7350ed4306cc8e0cd8b31d674552ec852f99eed00d559e8879e73f58ae6c9d4c2e25b54ed417ec279e2c3f745433354d62b346883158c4cb2f270b561a923ae97d588b2c2590dbd81ad77abbcec803bea9a7144da5132934734db713ac8ccd2e378da5ccfb1110d05233b46df2b5d3a1253b0e91a7e80d340172213365d91b8eb93dbdbdd723da82daca5bb402d68943823e7edd2e1d618ef1dbf3afe2c38b74166b6f4cee880f1d5bcf5100dfaa25331afa33a35cc68cba5e202bcfcee4a8e46510190a28783a6efb9807dabd8d6476e278385ef5af83ad4a13374da341c2c1e221d50c584e0a805b80c232137f37b510484d946047d87cd250c0147aaee9f0d050e1e00551ffdda55f59ea36128a5f22af4a739b936de6f85873e15e9804482ede298cf528359af930899e3be98a3433bec60997a329e3c6a49a8931765d657b37af856a62ae838da0fabc7b4164a9997cae3b2f00d89a205341fc0ab48eafa6619c9666feaf688c2738d68759f61e5f967ce563aa121beab8c2d2d99b3417ebbe202796e1e61569450a5e1b207ef2c8ed2c274c7e3e195d03e802118a9bb944c605350e57f13568719de9016caf9d4a42a0ddc826b6dec0e54918c5caad89b57e9482e38dd8c3d1cee3b2b5f494ffb45a48a7a7bbd773168929295a078337d2588d18e6874cf7fea3b5671f807ae20a441191ed69e571566721692eedf4d06a8ee69769c58856d394bbfd77d9456137a32e1765f5c26a69e2cdf1470ab193de8c9fded0e8aeef5cf9dba12d5f83628930eacec627230712e1ae7cc527b3c102ea608a86d8a1a7fa3f9b46ad9cb1bc6dee60993b32cdc59bbcfb453f94c1d56a19fe781fbefb138a7ab111c2e1a85e9433650f1cc53f58dc26cc6992cafdf69125dc48370f98f3927f04ab64f8109ed61cc5c22b288dfd4b0ea9e286191eaee0d8f0617a403dfd8c990ec1fe70e51b9b95f47034732c46661dd8a49a75c2e62beb1357f3f4f14d9b866a4764046ee9a6f2f8e9d094c12b6768bad500288b2f73ed49de11ec3d1b5a75b2ef8dcc5a9c8f796b3352ff13b9b881ec1dd8c58ba49dfd758c9e0308cefbf10e6ac7cd134e677bd1fb6f13ec7c0e582c7ba0ae4c199fe11849648d96b056aec7129bbcba0739aea501e5b40837daa2b829d86630e8af45f49ae8c85b43b9429d5ef12b4d89231be19b59176ec59fd0c48aa3c44e7a58c278aee344aba9fa6ceef47aada7ebcd40fbe743ac6b996adf9cf2d5199957adde8ecc129140223f06147b1942f6a6000ff944e3bf023c00ba7e546e714557ae2f2f2a9fac71349df63494aefc9a7fd771117f21c1cfc672b065efb0e1672e7d476f02492d4477643ee102c48c44ecee97652a68d451c2d2ad98624f5d455ad862615b88b624d1034016384c0f615bed8a7aea39fdd36da9717d716af82511a8871245ec64a13281834d364c5412331ef497d5eb2f54027262029518e82a2e10c00dd26461528f3c84f593846fc3fe54d777e3f4b0335b0cd7221ad20ffcc5535c934849f5ad2ebb5c746e42c02ac88ba202b5e6c915f23baa2b4aa04ea7b42cc803a53a5c5e6a8af8828d02a91bb2a7cdcc049c57495013a87f3ea0d29ba0f696dcb939294319f5841bd0caa9e9ae5432e7f91cf7785ca685010f046204e2d80c98f12b59100255c7b981b7b32af4167806276f647f3f013847622206a5b35a6c3916468a7d41571ea1bf6acfdbc859f3f358fb23ab1841c6f661647bf7a6741ed110e63347b8427f3fb0b28920c77c0cd97b25ce2073ead6c0cdbdc9b91804a742f357f385d0db3ceeee9bc032e6ab2c00d7b7eb3d16c804ca82830177e68602f621daabc419741a81a853ea5b297c5e4f010920653a713821c4259515423220ac5a16b6a044092feb23a793fd8b59f202203cc179586408629935a5877e2f24c0a12363a49685fc3e46fbc61194f0fb39dafc85fb0014737f8b6c2adc9e199acf1a1c6224b87ad7d855d73f46dda07d3985358a768f0398517b8096c705090ce7e51a140622baf3ba23514873019c93a4c17fe990b3e4651faebea640c391978fe378ba296c8432b1e4058388a618010d07c87464274109f3a02f52f1d87afc8c8465282c9811c1b0c0458c1ae55f7c39bd0c8c5af6a30efdf576d72c8e463bdfbd93237d3de0f53b7015a7e6f65997b67bc5dce25ab2cea57ed42b2452332a33c4f637981e8f1be056dd5e0451b3b3bd428622e5fd0a75a88652d00d54653f83e1bd43287e0f97c853e263a2a20e2320dc9da918ba1e7ced39f1af51db5637b32a812fb4adab7dd122205066770d61e845fa18594ce64e3840c950eaaa46f83ec510e5446cb34557e578787c6197e13ff3010294b240a984df33a167381e156609492be8602a26b0ec683ed8d5d8ef9cb4b5df25a83540df2e8bfcfe29bd882bcafdf0c709e836b34e7ea3b7391cf7c49696b234517f9f6b0729f9d6310bce32c850b575ca7b51d09738051f16862af47eb0ca4a60039d843ee02a31b2b081ba764725607a7b23360caa1fcc58029e86936e075214b19f41540a118e7d32379b8a69a3108b7b928341fd8efb88333cd1aaaa179a0067c151375df007582582f36715c1535fed59aac9e4518200072817fdab7004703e7ff32b50dd7c276e94c680500ba9435eb2b8902cf0f6ca93a4c95a91d3213f00cba7c4e3c126f771179b711a3a67fe800d99c3341c1e27c2f07bb4d14b7a3b04c3b06331db5a95862f19feb95836bef556aa9849de698d9e03d2f8b4d0cb98ccdab819ef69553552b2daf3c52c4564a8c644dfe816ebcf7f0279fe166bf2ba98a8034e97889ce9522635ab91a906108016ef8ce492f14b7992f92a2766f7a45aea42f2c464a1a641966cf8691a75c11a727be30e091db8efc3054ff01675f494061bed2fea539845175a4d45c12aeb1690d0b7744f1dd0cb122e062a2afb754734b24475916479b47c588afd43006ae5db95c1b45b1141c2689fa160db00fca2145850e3d75f846e692e3056862ba5cbe8ce5c20c1d85bd05c1c23f9942a61a3ee73440b762411d11da68e5c4877b92606a462c2edc8d9e70e369e59ba55e7028120c8f5d422b44fa975f058aa23d5697c9a2b20e3dab32df3fea1aa2693d1c860a5ad4fa922d5b1546f333ff469f740b5a338c79574e956c778500f8ccbf6c6ad966a54d571ddf0b2e85bbef39d78e356472aba5044cf46d92e6e95ca594669dc610cd499fb2656465d3ed633ae4488043d10a764a7e65d7cc66b87146b7dea97d3a498ce65f96f841e3bb67aa2a88af5305addfbf0a4d26ae8c4d01ce910fffdf52590f705541afcc0782e2d6a74328866ebd6ca2bbaebdeddc3697f476690ceb4e248c7fd01af622c91932c00bc6a2611678f41b65185f9691044e5df659023750ebd1271ed3cb7a99074a929c81d4104a4e03f91042cb426c2ea8fc6a0923e774dfba15e3fc50103b12aa2272bd3124b15fca036866c24543ea25f030ae52ca5ace476a478ff91b7d2ae007e6010d10274e2467131f32a5a29caf14506f0f9c809fac5ab9ef7829084552531ce8f088d129d2f73425ea01cc35bab08f2687a16078128c24411995c7829880fe15f4087b3d1298ebcb128a7c21b30dccf59fe0668eac79ff6b15e8522c41c1edebac8b6561592fd74619a4d56c8a9d8f1fc8221c9cc1edcfdc6eb40023971336497b56d9555adf285a03cb7ceff2ed290899417cda0dabbe5c329c3aacb5969f2a2f8b879e88b4ae654cddc044724faeaed8647379c6e65e1ce0f0aba0074a02c408a9134e53386a8823156006d1cd4d38e77b92687bb9ff868e004f1fc1010552fc09c19cd789b2f696521159cfd6bd630381be4070eb1dd01af99580a30fdee3492f187ad02348fadc397dbdb28e63a09e7eed4323b3b3377238ae44b4486880040ee151d356aa8d77c82a6dcbb1d2167170d2c4a8e0e5dae5ca0fe5fe25ebc61281200823a429fecb1c2b9ba3e3f6f94569f25bdee2fb27b3fd23c722ecb269561d065984f142ad15e4dccebaa72fed07ebf29ce66509ef9ccf0ba606e6a9bd0c8cdbfc1459b5a8dd374725c48d3e8fff300ddb45095b808194200fe88d52ae372d7984d549c647b32d8dd528a33ce9dbac8a7b4b06e704a7b39dc03e03ce152f5403e510c6420d629af6a4f33eb8a96ae5c90ec2c75527763f8ed0cf93e3f6de048908d2bab4bf1af871355cd478e44e2770df2985502721fcc5f3a485a12f4ac64ab46284caf8e642309da8849b43637ed8c94462b838b3970865400683b23cabd47f69b15a0c308140785851b4d69fb55fdc51cd7553cbf1045d9bd1249df10d14ebabed65667ffd9519aea62ef76090644fad4d9b2b508b7ce6f86615d8d36d03c0a2b758ddf3980c9e3ba1b86beddc2da67817b0b4b5ea8649ceb65162ab32e92a3f57174dbb21ebcd085be156f2cec0a9ae4060a9449c8ae231c29c7c318ca79c6076f3e53827ea75a9be6e0d2c5ee5b6e59f0341096b9638d1205ea95bb8d184780e3ed933adf4c074ea2f4a4027a9003fee876c9d67399d2e329025a97aa751acf7ee11d03f0b99724fa7ab9fa00fa4d81ec87f67b2695e54c6193845f7b5e9cf1378331a33fb44f287e93497eac4db7def34b6603c95ecfe435c5685491d74274cdbcf51d7f989817e839a7981683e4737a68680d78a5dcedd054c181a691806db9b808f4431f96c3474c864a8627944daecc114805b51a49e0774894854324f413b9dd04909238a394b471e8ad2bfb262580551e7848ae0ee7ae2d50ed5fa2665aa5c92d7a5f869a892a2109437ca49f14c7165209cd9edaaac9c40e6d0d47ee1503403fa2f3c12116522ad0c4abf1be45224ddcbcc02a8bc5737b7eb425bae8c726de42c1672ec39e2b3533e8009f2a0ee27101dd72a7c17f5d9a04d5c556ca800d42044551609ea01f462e57fde253ab4171e6e8bdb68ea3dcfd125f0559e8862594810e0e171ccd8597e61410fca5f98af15389cd29baf328bc24c89e5209c9c6435220019936340c67330f4d1fed3576d301de141983e2d7cc456c7887a762f201ff26f7542e10a9d269663a1e8398ed0aebb123f7c41b7f39e17095d0b3a14c1510b3937e5a3902520e78630bd5ab280e3c54d04236b0f8cafacc5614192c5c990e4e032058209355296e9e7e0299faeeba3060186b95c0893b0d9ab0e16ee364a9ad9a1e3ad6261184435cf738da069fa3508d48722ecf611977c45c8aa0f9c398c02403f71c871c1a54bb3f62e275635e49438087d8b8f843fb7f36761b877bd741fe4f2ce4689ca7782ac581874bf810b91d888a301fa26f8608c5960279dcfc0b08f3445c3b24eaf9377440737eb687b9b0709005681355e7822f8c4a7c64eb17014a39b862a1136a190c2e71bbb30685a96898b733b04db3b083e18746e2d09331877831049ddd65a5abec4eeb07fa02eda5553cdf108feccd33aa00460fd04f71f83767ecc77c250a511d6ef75fcef601a0af25d934277ef5f725f6147d5b80fe049aa2fdf4510f3a20bc2b3607e2a97ca656e22756dcd241333b91e14260af8a499f84d27abbed0967d4035149075f15a30c005c165406fe5382c12d18622c936ba609390ad403bb63a84480248d1a23402a81a41362bb9820168bf7e648cb0deec66f245644e932a8413a263bddc35a7ba1b1f94ae6f3e9495a6736f5e89616fa96628d5cd94064b0c1c810beaa405db00d7ecf2507a89cb49ee4c424a09f3e4db83295b034a8ede3ec96bc7fcb71ca0e2e16ccbbc1fa513ea80cf5403efc588f4fdb3d8518ed9e8189401ab3fdfd45f698075b95fab91fe0feda25951fd872edd8dd073d974b99302089278f28e59180e9a61bbd0ad61cdaaacb46d0ca34204fcb716a7cad284fe18c4f36f2b923aaa5fdf6c811d27bfce19511949be4cfdb86ce8e75c04ccfe93fc8b9bf05facb01c605a51a41bd90cead5cc77935b69f9b8cac92fe2297ebf702d43d768fce4c66e0fbae931a8382fce08feb32216a37409fbce426422a0cc311f67b5aff203f0281a7811d00931f14dcdccc064025c1e0733c381da58a78fa5e6d66986dd2f98b9fa29d7ef5d6cd59225784b4aab796c40498c30ac81c6037cf5bb5aa3ebe9ba257a4f175bebe30295eba640795cd714f9c5b60c2277d195f2dd9904d3d305cf8932684c9f643caaef4706c86216ae629a4b4c973706f167a988c7db68d8d58e3626349348533c3aee9223743099c1ae16d97bfad6aaebd8bd9df371caf9ef8b1864682c1f748b9a7c589aa7e26bbf30eaa0238f7c889f49cc5c7e73d4fac65a1ee2bf8cb5919e8cbe67bfa1c3aa1189b38f778c3b13f50d92bb11a721ecbb9899a5cf1d5cf44491bf9e0c99189a506947df5d1b08f44e3f7ca2f13e25b0800227284036e6cdeb5fa84a09f309a9314d3a52649bc20945c7f10f847da09de20338f03040f1f2e064ca55c66f43bec2d066eed696d720cc1eeeae983709a3c6439932e34fcd8fd7e0c3192bb9bd05fed0c10f107b14f6b1bcce71f60882fc45e01b09c6a75cd1cab89817ce83c49c9b9f04cf1acbde57ec47ee30c29986f86ed3c469b749f454eb036bf043f70bf5d0f73e467dc598ffa0b6189b7b517e02c1185bed29c727715810457835cc175799846feae085e549aa67342f1f4de7a578226fd18e7d039000b046216a3e2205c39b4f5c67916c53e3e7351e651b5d44ca0434b8ccc8fc981bd0e5565ca35919dcbb60ed6ed420904f35b854001658ace4981ed38e7485728e7640e727dd39ec14232e2678f08d57d52fba621ab28b82962d45fe107cf58a85a80aa5f539b5f9533acc2e5aa2a0c8c04cf017be17c92b50a9d4b82d6fb6f37cbbaa7abef1fe8828c20b9320f3b5146127838979f04b719f448fce30fea2d9f5c6f419715b29c9b327b1b04462163c093b17088df87580eeb3aebeceedacb8b2d813def8022b94431421e74a7958eed59a3e1946d6aaa7e24738cc167c4dc5331c016bb1f50de7501ca1dd029047614eb8af55226ac2043308df3d52e5d1f587973a72e6b1f5b86096152509f2832a7a91e0d58b094a188a200039ab558243f929df45a7e1474b2858b433bc14fcb9a021cf0c7b23620ab4f8ac559184335554c175334cb12e6dde29cd02769a6ca8f5b6736a33dec472575e88e1cfe325b98d60a2cd3e2cee813807635acb04185031f501ca32de963de285f6beaec564ebc969ac1f9bb43ee4f844a2e61054c550f09e268761922f19b951dbadb12dc0d9229a95733bcc51126e512bd2737264f3f4e7b484cf5b08422902d5146aca8e49d6dcabf20808bf1049a751d0146c54484ba1e1a6a6af852f6e1d4e91c6094101241e62149048cb435221057fe8811b7547193afc44124bd66659a30ccbb12816116e2e07eea10e92c70091f91e041ed2c484a9b2f6cda02f3124448c5d9d426173c7b5a2ef424cbf0c1385732be804096cd1bfcbcdcdd0f1963851a9bf1e1e3c8b7393904fc7271a1225284f039488527c95de9470fd8818ed1f153bcf7e2810ff9f43ef66753a694b3db7aba2557dd75f5a2451c9a893811b9c850f2aacdd5c9e4a49e26240788e44dd429a88541260da7eb1c41ad37b44f4af75c356092c8599f7162d152e00f8dc9743f4aa89cfe18eb82fba7c24901e29b9d2c55161bed464c40bbce8414c57b21bbd126a4e58abceab16bb1bcf93add517126a7eadb9b917453d83988c13f225d48c07d658a98aa1f74bf8b05b705502b455a3bd58ba392756fc3fd9900d1a29417f5c2e78c83130628c05a00a7c3306bd4a06b8cc5c037a8766a0134b970d409aa459ac8e285af3cdee06b2f6aeadc5a0c092e57bae7a3217cf3e90ae599e0b421b4d62a149ed21548b34ed9188ede9c5519d961ab8812ea4af9b5cdc9115b3e2d3e748d0abd9cc32b54e8f2b06e7ac4fdff76f0856fcb1666e2b29461cb59bd70d9ae7f9e5bc3578e1aadf87895fa4399fdd3968c50a35878660d488fef37a635d5c2cfa536520b513ae7202d01c086c476ba41079705a92dedebb872919f1165a917b219ef4303776185dc8e9ef83036dd856fbc79785284aec71954cfc8b3423c1579d73bb3b45fba608bc72443adc0392f8c8f1faabe7bd845144ba81521c9b929a6036420941944468c21c5bd6f57f0105b2b9d8d0fbcbcce721915018b0d034f04a4ba57c5d01a3d3127b3fe90e6935c99a050801b3e06b332e89f2160cb5fa0217a853fe0c0b22aa6ba01845c5d9a06d8900f4275264228b23917c6b2f028504d2444e37e883d98500ce6b110ff46e052695aa45cbbfb1e20944d2f29e0a60748a7d00c77b5d587a2cb7b0c53891a9287d8fd27a00b97218bdbe337ee586edbedf678a9f534ffc020a56b1cc0ca0f525df73a8b4a04622c8ab97c0867db474d2057adbf32692ea7b499d420155fc4026cd258617a3f13ce6175758c1e228bb31588250d38215b72ab37cddb6a04e889af95ce394db2f03c003d0fd3af1129cdba71972e4a6b5a6e1e8eb2b331a5947de44378c59fb68345835c6704fbe25abfc892a549cc681e879f8d1fb054f405c6cb9744c785052d74045f4831b439d68079e14584a1544d50f49658c22cc2e0f92363b94b9f73d167439c0cdc0b4ad0b273d0c6c323db7b7be3c16697d80ef67497ebb86b7062e745d7963bb6e49fe51c9acac2b103e324d3b3227da01b1abebef2c0ba698aeac767e75119bece5d5690182a7b9872ce6775829a6158d826d6f4b58d16d10c2f515f7e5a2e73f74ff52219d2031741e243ce918210b58dd17b7d50603600e15664dbefd305043f43db4485f0065b5961a6d3a627295e584c8d23a6e7d4d2a06b24e573009dbb588310d2c23877913ead4e0714a46c47fb179733a0d6ac87f8101209e93963eeac846230bd25100970222055301d680be6701cc4a082209e2dff4f270d62964ed198b6b839c3d23a70cbc8ceb802b7e0a0c3ff1e1080ef4d5bd6eb276b2aa3aefa42fb0cc83eab4b212af163d563225ed17dd10934564abef77bbac8548399f0c95acb747e40255250662b2248d402facde02586f88dbf2f381454ab76dc73c50c8c8504ac9b03f33b1e14d508c247d473360bc2fc678a827fbecade1d16213e021ba779a03762b3b634c927a70f59e0c5cf636aa069a3cecf9de7515437969ed2d264465eca3372dc9fac7738013cb22fed2c25a6b9de009a29819fc0ce8d35a8c6fbf4d92706d23b4e7aca9417c6d67eab728f5065cdf863e5e0ffb7cb68ecc368dbb771b3a52baf63449921d905e830981edd6c370556a20925b76f372d9192579404cb3308af8ac1559bb5c434700f8a26b22cc6325a72421b58f4814015787e8c1dba6f617d7de66e478097f84230b8fbff1a1a6e071834d5547ee77e4afb60f3002a04284d6b2323a88e56e92f0ae6e45ba17df71c3502b6536a09ca90b9ae5a6a9e5c99c0dfc4e4f9d17d85443423f8f42a59256a9d30045212d4182e30c66e2a1f617dab7e2a1a441edb55d5eff71443514cb1f63440d823ea6fb229cd4d56d424533a7fd19a9778fd5363a010b8eba56f38fd451387267eb4b8614ae7781032207e6d5c3d4d59649c3d10dad1ed83c6ed1606a14f8b800f248d904c626c8abe384c4a89803df0f3a775ee2e839b2ea2af01bed5ae4603958cd33803645a72ef6a3a63e636006aa6915ba82c727ce11f787e8150145c405f029bfad3848522d8d3c39948942043ce35adc49004c37efaec13e0e464b4ae888b8147bd7853cbe3a4a47e084c3d45f373a7a1fca923b2224a606c33916fc90c3f358de2a86070caf7701c64087bec5ccae9fef7d9f89f5a404e53d52478a4c8a59b27c6da9f0dbd8951bafe4ae53f5bed89cd0d7d2c0c849afe75f4ae35a3cd02469b4d63b094e475d0b5d14a2284761485571e2856600b36922929a7013514272811f9010b97f7d337aeacb136ae0e1762d08d9cb896d79e168b2195f397d7bdfe8512b1b82da24de5870843cc4315b060220f24fcc8eef763f5f26e0f5d5795da85c47377d50da26006ef56f8c043c2ce43177816fc2a3748f91028fc046d482dd3642382b954208d4d0a6dbc6c7b3cb7a60e394a3fa013b868b0b7d2c48af22f55923af1b85c6f760767bea30493c9d1a35051d951d8233d870d43537d6855da588cb800abd6c5e1418c9f379d4ba58cc01d3b21bdc4644571e26c4e1bd8c0c7da41e72ea7132f0142e884ac9c7ed20704430e087166448e3736992300a2eff75ca2d53623289f5a1de94a7d15ec232cbb9c39141e367302e1ff01b25beb5e6008e52a76d682394cc2a040683f2fb6f5d39eb9b5361f25868967dc8fb01c7e1f0adfe1fbc67971286bbf53678875ecbbe59845420019b3416d100d34b6ee9534a483077faf32973876a73126d5cb45d1adc39ccb8793f3d3fc08f0ffc679b76e781e21b087f490601507de9b56c7bc2c0e5ea702ac9ef97a87ed5004965a0758c1d132c5bb669a614e794b35003ac60ace978bcc1f6bcb02571c28e6b0a69e86c7a905584de421ca73a61a9588f957de0f4be3c75b75f616060c7b2406d237a74e1991515e28f12fb17f1f02e01d68b47d516cdab9ee146a01988323c87e0b1e1285cf0543c9cedf6803243bf939e6bda5bce01b083b69ad34811ff41b49db5ecb4796459eecf3089525e35abcafbbd6ab74f81a74fc64083c05521c5318a279acd85aace2c0a31043dbed638b55956f56bd18fabf1da0c64ba51fc68aca6893188b9c8eb1b6db2a928dc6c4bdbdc7a0e0930a32b9d02c15df48138d3f50d4f7ef32c42d4b625d4bee629f21205dc5424311e42e93b5295672e43c8c74dea2622f0cae280344fed0bb6275a04cec7649f5f3fa00749134e5abc899a2136ccab350a7353e0f65dfcd563a5ff9fb9a207e6be70146e39ccf84b4d11f68cb68517928a6fdb4bb171c26894c5fe45feab5fb974abc7c899dddfb2431abc522fd34a7043ed281b84d1942cb024f19f052e41cc889b794a6b66cfd1265a85d9f298ec598cad171671f46d1cf68be47e5425febb1a625089f0552290dcfde4b0123730168fed4abadae510f0c92ce825160e0c85459d2b6b60ec8e8d751f63a3b890eb5e319839c211947ebb9463c85b0038d08b0fcf30802e95c5da796512879012dadb493e7c0a33bdd14bb3f2bdd0dc33bff4265c7b820904e6d4fc2f7c2c2913ec1bc62c0e1666d579c8e60908c1683cedf62c37c68ec965806ca346393b820bcc547828142852ba2fd926146b5d9ab78a6bc2e156bcebcb24d9ca9ac5bbaf2e2ac6d39f5bd2e93861b4df3abc0157246dc0ac7058ba9d6eeef78e468045ce257ac3bcc7e9d292f4cb843f47e284817dc4423bb22a5d04921ab62a935063cc6aac86009c6d28053141ab539a292fb20f50b8e710bfeccbcace41ea1c332fc799504e93387f482f805e8b200ba387bc70feb18ff0c8674463c141d4c46d0985c6e4f472023c948926984bb877d46f4628c9bde865a4b25c9324f48f4a70c46e9d56ed4f947a03e937f5398b2b883c42d9f0e0a4262e74ed04a29a2b9be5310e93a5306a5d274a46bb64295f21cfe94d822a3aa367b5982f6f45a10113ff3ab82414a09a6ebf3cb357d248ecd1de29508c76cc30a9d3b8d3e1ebdb5daecfba51ebcb0c04c6d43ece8ceab792a6fa6c8d0e440af16681c21e858556fae70880580b95657a00c016c559ef7a194ea83e78c2dabc448fb053f6691df403894ceadd1bb8505ee373968b23f720dd81c338b818eab7764e08a4b10a9315f8d33aa01764e6f181bd0b83aa627e3c995bcf7ba6cecb669fe1289ddb1d32d221b2e22b7e8af251261069d0347543ad08c54d5d044163337d93b03b8046888f88074e36f9b31ec1f8946d52335bb5de2b66f18160ef5831ebb645c311f228cb3a1d91949e2e8e99fa3c92272667bab87a0f47ee90641367c491a7a5a72b90214d9047d9ae66dc5345ede4ea29387c270c8b14cfb7a97d902cff328f480714b8451d0030798c180b3f786ddd6016afed99992a13a99428e10f9ffe94fef4e129b7ad077abca286ed5ed75799378106526f749d2aa693c0f0bb7693de3bf106b5263f95b3c82685f7e88e8b5011765ee3807fc5a19cc032168487cac351d53a4c8a4011a5222737f1df00491152dd1c4682f145fbf13b201738d9319a0f9b9d915d6ec21848d11a01b058a9b67f7a2ddba64c9f91103c98c29ef32a8fb1a18fb170d1e44bfcc29fc2f4ea2477508251901b4545a8c4848b3841d060b20574363400283246c0edceafde0e72e976b32eba1844f02148d9187ba3840a206b568521a7928e88f7c3c6a0177e644345ce964cd2a663110c97ea38095dded8cd04e01e858d403062be55a5c18ed38533b547531665994e24ac76774a248f4a34c97aac73b8ee7fc8d3e5cefa9ed10cdece8819876a718a6324bc5f919843cf76f162d02d390e3d75bb64c811a9834ae9a42c3e102afc4924f9759011081f1e8cbcee34293c3e1fac522a134fc418429d47e15dbc96cf6afe7119ae313713695d5775171764509717558cdaa77138bb5c7917c37374e8e650960e1d9c4812564fba0f083b60667756233a45099e4d30a1b85764cc27e07b9b1b4c7875fffd9385e603256af0c153c8707e7ef3e9e8120bc9d73b30a870c9b8fe3dd9e3456d1d272616d6dcc1eae4be736c0e26b71ec4f5377b0679b2675575cdb08d902e75a69381cf086dbc7ca2fa51e5a5a08bc65c919bb642ed2bd3fae77adfdf34947647f1cb834c6f2684d7bd3c2ac37e3ebb43f54df2900261ce8a0ddfb4afc58ed4e2ee65ab50ba5150a9e95d17157cea6570d41b1310afc1c1edad0b35ceeb5962e2e3ec662d7d0bbd64f51991787651adcd458b481d9864f656c1f5eb06775402617141d807706ea94c84fb8bc0eda73c0d583f1cca2d5fc2b6d1829d4fd58d9720c2c6af1faa8b468d08085c9726fad1e4c8357aa37fe02126162dba5b503061c453369608408b550c892cc14ad1e72578e4b5b71b25fc959dd420169d55ac9fc7ab110489b17dabdfdaff0c3de57417f1d86bef39781cf7e2b609326dd6b5cb3dc2f388a9a63e5155f6c7a23f7bc8efaea60b791ce2888aebf2e1d18925b1b252b53c08c32166cc0eb3dab09e8590faa9fa67cfcf988c6a02d7a8a8796199a631f916f5b5b694a0f4dcdc44ded7096b6cf7bf82ad132365e73821509d2010b4a1220dc0f592eadb1d2432e3015c357b9b3d149283e643f948ed76b192501b687c09652026a82c8de409dc8bcb72de254339b3c4e9e56ca314c726609d1c58a1ab79211aa48c04350ff10d153a2b89361cea6c77820481d5fd5aee81485fa50ec2a2f7ae7e34ddae46f5fa4c6ce76a60a9f60836c4285f2877d3bb8d80c68f33067536771a3982968489b077f3110fd5e3ac7f42c15c9d89ada3cbeb3bea71d72ec1e1ab27084b8a765188956f1ff2532caaf3c36d4eef3f7f20c2ffb43521e20363e59a5af9a0dd2dcdd7e094e23fd3ae07fcac2e4f7d7cc6c96d843f9c3bbffa2cc0127852ef21073b4898d2406457165a5756003eeea3c98c619a5e7d54c001134819d48ac2c63a93c0c5b870011af3c14bbdfac539561e710b74d9bffe5482450637ffceca2e093203ba201cd426d1c736828767da4fda558138a7d163327e5ac3e50159dac28c72129a29a827a76709f96af4218f11cbf833ecaca12e4866b59ece11e8f39a46f12f7f00ecce40054df6d2bef92b9bd56ebe26d82b5aad7bfcb1912c4de542cf65c12199e6f91839b3c1dabef16d9c106e5bfb86069cbb575703e7183f92e9928beac1c1a8c1c926b671b1ededc1e9a487e4577fe951f1e567686944e175be0748ec2564c8bfa73eb212a7d1ab9728c57f014d3e8d6c91401ea3d97c93fe09cf4fc0d113c34d82a1ee74dbdf6380a5b491cf7a887c61d73a648b28017cedda2e23ddd70de04ad5e2870bf48105b3cea2b41e9cd6236d1c9b529f5cc0bb0b05ac68025eb415542a4f648b7aef9258070b3b24dd19e13daaa56e10d444227440ef16547b87b7fd4e2ea849c4d11c17d83415ab7180f1c81b02ee0598c369bcdbfbbf04b5ea7eb75a20293963d5865ec5587fa355f6a285bc764b47080a5c66aa616b2329ae26ab020ccaaa59163ca5fe5b8ec8d5e14dc928db0cb2d32b3a41522710eb655ac222f4d494b0942ccb980a78147a803402312897c19ac35d7468a1638c5d9bb801a3495dd4808a8f297af55cf6fdab2596a979d74a164780b140b827d87fc645a6fc632f0494c5487ba7c457bfa6978c92ba29e0d1c5e3cefd757b7279d11afd70d87ba11e21d92909c299885a0dbadee58a8f91f312c9a7f691bcdf2346c0e8687eeb433ad18c6bb8ca44b51209034da45e09e75c4d902a3cad098aa5be2eb5b18588fc47f0caeb35a6b2da3f3886eafe007eef120423ea4de1b5f9d65199ab66b7d53d87e7855b2d77cefa3335de6f8c40e73632c20c05813e01ac5df7d420384daa1e3d122095f36287b31fab2bb079132123c629d530f867d8a907bd87a14f06fedb600f1c1928e9dc7a2ebb37877a30a0d552626bdf7719e53809926e26c6e2a674781ff1b70c412948bd8661317a91b88cf798699172d81434ba2cf2444ec6e43cf7ba998fea31909a9721993dbf3c9dd08c311eee5acc276499adb323bc9227bf3e2180ce87e7a8a22605e0a7a50ef579a7a57e08570b545a6ea1dee311800e3394208ac2a9f3b448e1dcfedc61d31fe0e3629cf3863bd280802a95b24f538ecfe597df6b05bc4789fdfd01ec9f38f68e714ebb547382b5e30b72382547500d61281c22d059cf009bef3010b48f227a1aceb397210b09a10eea7486a128ab00c0d4b1e5e1821d384ee6719efba264c3fd41e9218d1e31b12f106d54b88abaf02146a375349d9a31f4e9b0f4b55f2feb7a4d70ed64374a8a57289a0f7ac422e063d0603ef15fd180a4dffc7050dd36cabd7572f4ab54d9b55a8fbb3ea254a0e6bbbc02e8ad7e81f807a693347188badab0f39ab6a13bfd6a79b367fc8c0aafda6da36c76304e300ac4d1f39aa798f5d903210266a077f29c6fe9125921943c8eaae81d74dbde24c16724cd70195946ddfa2bec38bbb3fb72f8646048d62c7ee8fcc440f045e426ccd257edddf53a1343a5cf7374835e62d94a79974288f9f33ca2c181feaefabcffab6c030136dd15da07cc595af2afe4d17b31d580321390f012fbf2d55b99f88e88947c56c8e0c2c155fcdd011a6b965dafc24b86f6cd95dc7bd1d09386f4e75873102ba709f698c831e83d45b9f5994c15b96e6d72cb009511b76a01d0a4f729798f0afc92c03a2a1b83394987a01b090650dae54eefefc94a4b8d453cfd3c2861df2d63578271c2753c9019ac31f82e9d7c24b6b381dd143875610c88bf9707ee580d8c612572546bc2cb165ee3946617ef2f20aa61285fc13a3b446926b3c824fca0bf6bb9776159566074544fd1785398805d8f0dbd7b4a1d6b202c4cf1200fbbfb9e604f73ef0b5b88ca794056ec7920441d054066f3c50eec85b15b72943b4c55fbaf2e6fe639238e2fb51b6a06864ec5cba18c31b9ccefea50173813e872a10104df600ea3ad2470751c3ee0e42967d3bf0765a724c4a494b85c28f33f9062493f8bdda52e93c4e65ec94f5e13e027974004002a45e428b60a59623e5681340f440de83ce8281d2bb5080dc3f27f44f7d7504fdf3cd2b6ab7ad52b7901dd4857918e64192ed19f4195a42ad55608ad09d76e09400d39b4b6754dc68bdcbb62af2eea5201a0c50695fc264008e048c39866eb79d0e379620a6620a26df4171faa2e34eb65b5c0e94168aebd14d259b719acb470ad9e14fa8c1f354b3d940c6e5a1abe2421b2adec70dd40307c11d61a078807e590aed3283880d984a893b95b7ac009737eba9ad8111511f320fcd70d56f4bd4f0f608c928021ed5211217cbb251571453cc0ad0ccd7a9475a94ad2760f77c3216f50bcf86d9a64a5ca3362f4e592a7fa64bdf7bd397b0ddc4a8a1539c96d84af9ab98c21fa482915bc9ce48cf7dc200e4e66a729cdb42c1445778b4035a66c4fe9dea4b07615e3b935de33224b085daa7e98b77fc4c3d39806b38ed7974adaaa1e7786632dd5d377eec86173acd6e28ac779e86804f2f6c95cb8d3cc9632b167927a7b1a716fdda9427383961268f0f4f07a53391fba0f0fb2324b13c7e834bc966f9a3f15718c636c5973229121d95f2eb5d6378d608dc982eac3c1289475158a57f33c6f045af37de76fb28e0d73c53c73599a6b8d8f8c179717b7a47e90d6acf1447c4372e7a2961bce4657d6b576ee064b067ec5118ae40d92ae0eae1e2a4df95dbf412003c331d26c81e469a53a56775bc661cf17bc786935d12183cb4f64423333de82e02156f5129bfe3e8c6f3c408e6206fe5b6109d80f46fc509fa32438bb7c23d499d299de4c40dd94036216f54e13aa90be4631ed56de54c51554ee447e64efca8f5e1a7c4233cdc2cd8e471aa24085c772a528be3e12f00a091e35c254b4537f8cec1096d6ae6350b4716dc417b24664eb198cefd9fdd12a823be4d479f6a1a88aff6bc1e4c75897b43e9c43ca4b39129e53ca1090770362eacb121fd903e357bfbf5abde60f7232f2f7fc50165b6201bdd6d59a4eb5c8183b15febf2e3293fb09374a3acad4ce93e19252b28e9a8cb6bfef2e541bd3422e30925f5d80d060c516b9d4c9477f4de7d89c22eabbb3c9dea6b18a2579b11a999834b88e4129419067ef52078170a29294661e28814cd6f7daf9322c37e10da81e7631bc8abfc7e14fbdf38b971abbe0b2eae59827861413eee78b630e70508b90124a49e5353a0153cc663a3be2c71d4c3fd44c430e7360b0f3c10a92ad3887059bb31a0865c54d7d71ef314e522a2655e66c57f2ef0354022edca9e7fb81b63c805ad43ed823448042c0f8f7e8549f47ddfbdd96be81defddbd6873871dce7bf3c6a95c9950d91eeb02f11eca04b5d3a75bf5bf3b993db9cc0ee1bf9dc2b75c1a6a964824c71515e35453a11ed9131c22191bf0283b431e5c56680a0b6289bcd177ce83892f89aa07a3bdede52d09a9e7fe9f4121155271932c6764fd6900d76ad617e462e519ad9e83b2984970a98df3653d85e3d1c5464b86fc2d4fc85e62000f74bef6d0981b05800aaa72790d019e62f097072c577621931fe412c92e8e8e046a9dcda99619afa2392101bee5b21d94d79a968873a3720b6100a5dec140025417f75cfff4382bc1cfe205b06f0b5471eaf1c7cbbbc5fd3406176cd8e5f24fc925425ff966288bc7baee0c8107a2a1e7b8486ad2784e1b31d58d91a9f05fdc08f714261022f45de89df6d5ce580d760b92a5eab5e99794d1776b98c34bc423207410838758d2861612318cbdc80bc22425d0f39130bad243c50815f10030572d82ed8d5339615ed82bc0e0616bf96cc3be48b2ced61090cf054bfa52a5413c023125108194e54625db61897cb18c688d743407f4cbd2e848d46fd6744e1faf7541a008a5e37c56ecc5e21bb930ac5d276785a23b14e72b7d801a7a435d9ed630722177d317ed028c3d136213ac4d5d7a3b343473034a2f926f59309cd5360799c21191915a8ed40367a9600f18f21a71e5f415bc511dfedca2940e508ed675aac19da260c85ca71f0b92362858188c8111df35800bdc5d89c64d7681d23bebb075f0a62f3ee2a75164f2b6bb7be59652ca9464d206e506ec0658e2af7e3eb96aebe753eb64912d6309a3427393931c24734b8f71d18db88c5dc118f39b92c3bfb97b6e3f43ed059873b05fdb3401874e0aa5dbd010618414ed90f90618b03ba1010ff4d862031d08a5166895b571f817623ee7cc2ec949194b0dab894685d8dddddddddd2db36e92ac558767599665b5134529f28ec90f0773d8a47b3f62f6d5cf63e11629c7c93ed20c75d35ef75eae072e67dfb2932d97c5fe5281439965f7d6cc9bdff325c74316c241767e0dd39eafebeb20a446b568b10536c0a30c17c080044d0b43ea700ef9b908902109b2198c1c7e3a7bdd9683b2c75c14352da359c9961a5614d962493648e61d2574162381124702248ded1d9e672e5b0ddbe059bbbbb117d873f6d87bf68160c061d7796ab01f7e2c01aab5f75a7b2ff65d77ef952de35d194949818814a26811a58b5a5db000b4f09ee052cc0427961d053ec1fd791ed007c8fd433c2484a45fa9d68a6158fdd1c2308c31962d3660b06329f25a6fb70d187c7388b13c2363d87769de5baa4fabb59452b0b117c241f77acc5a1b30d873883b0b71bf9602f3908083e01378be046e804188aabdbdbd3f27b27799a1eceb52e996542531e0cfa154e1074816ff2ca820802b70d8a54aada559b53a5bc0048972e3838b7c26b468686e688634116db4d0820c3500206157c4b01b739138a8e59295a7fcce6497e3c8a379ccd0bd18775d18ca31c641ab04c9c9411b48e0ee5bd302cfb728977e5357d5895224b1c82eb34572b515aa7431be6c8ac6602c8a38c93387778883ae14146972914c20029c05e4c8a61cdc3ab1bb9d28625f2bfbe524fb37c9ac158d8332a94b49f65f925dd57d8278333bb7a35fcbc4f0f387e970cdfb513dd7b776499eebbb412e8c83d9f3027cef905b063cbfd02ab9a5ab74ca37267bfd6caf4b0c12844571453b44e7d0da5c51b63886baa56bba4344eee568fbe6b6bee215efc5d8316b635ba155d9a866d56463583786bde5ba43d589b3607f4f486a0c14282c3c32a07df6dd1329bdd0be10a3c215afa86975a4010089062da8906aadb556e9241792fdd6d1efa67948654b71f96ea9cec1e91c7c4b71d725b55ac30a786e9ba721d1543c4d89b368473428648b6b2a1ec97fbbc9fe9cced1cde97befbdad9d9080c3cd146aacbe39cdd0469a2c359039b2f7df9a90229314a16668cb917dda17640e4c4890a6b5b26fa6ec9f1606d9e29a93feb49646050eb794e6573c593968871d8d33e461470c5a129ed4141df9d7951469bfa99ce42d3559327729647f2d26e675f8a5b166c82b4b4b4d2da5890eba3239dc524db20f21823e43e6fc8f9bb9f70cfb6dd338af5f83e13927250ccf8e38de0ec9f1b487e125b1801ca97cb83d21fb6f29c9e5704bd10d8637475beade3ade91a3554ee7535121c1cd9a13197f214beeaf31dbfb177279fb90f3edab31b56e9b86c7554da5538584bd9bc16206311b12a0e0848d37e594d8ec24169023abaa3464834601b4181bc6e1c6c072cba051007aadfde46439d1406936dab8d26a1cc4d29a38288a83607c6bb90625fb2624bbf57b31eeba7f10a4a7c9e2f4444fffb547ee0b1cdabf21e822f0bf703e141831ea7cca77de1f04800cd124a436e989f3299ff2bda43ce77b916148311901994ea410865192f9468c421a93fdf339719ca29a725fe07e1db3072f043d6610f463afc9ef1c587d7bc47a413c76afc7decbcb0b8c2431c2df9e366aa367942202644f79e97cca1b31ba9c057ca4f21ccfe303e7637c8cef2586d6f1e66ab2268b7b607c73e5e1bef3303e86e7f141c6c77819df8cf179b887f1510a33597ae4e1be0af848e555bed9fae6cdbda2235a7ac2b080436d5c390c29fb5f1a1cd213f7ae8da78be381e3388759e51db2ffc6bdff0e99fba46cb95f0804c5f1e4087b25a0811550a3144f8e360782ea31d3cfbc2176480bf888333b1b47e0f053e873ee17448f793e7dc2137976d3ebdecb7923389e367ada4ab6d8b741c2f6436dccdaa88ddd34509bc78099c1f248694d3384912ca9be15e2a0d2102431d58ad51ac9d5cae44a6bd59c522e774a1cf13348522455b9bead9609bb44ae36897af355285f60d2bdf3ebd141df0d2f604bb2a45a6b0bca0c561758f0a054e91634996ec1cab47e7394565a29ad1f082aa5b47a75f7fa75b4e794d94917a49444cc10460a42881f424c25521079f57fffbdae43f6d7a6c02e0efa3ba07dccd0553d1676b918779d8fd768b80105ffa8b201d8f6d06fc31f35f73bc005f7d62803eebfaeebbaaeeb9ab4ce99ad7d1f33f4cf7ad6d57fbdd694863f6ad79fd6b3d77e3d1c741e0ece10f6c183c29f65ab3f2780fccaa486bc4182e65f1fe290b5c99db41e01e5a04b8983ac64f9a8238d6c49714e5c2ce10a57d8620915e3381a41deb4800062da7477840e8819923636b23b80a83b42f731c016b873f8f9e494522adff35c445ed94a61a3b047e86cdd52ad5d6b0d2aa07301d77badb556e778b0b7d65a6badb5deaeebbe76bc2c19608cde9a302d07c9280eea19641299a729ce21b4a594628b2db6886188f886c11a2a2ca5942dce106e8cc18f67fa794ba52545fe05e92d29822245349c41bc0cd9afcf5bde4ac9309d846471d319b2033159fc5e81436f410187d2060829929c8be174c44ca2255bbc0a54e4cd845d54aae676504a5b2d7b8d2b1823e7468c114f48504a96acc6d44c8d28a6523a744c7c350ed8041cb678bfc5f97d5d0e6f8b0e7644294240284b514acfb44cde78a87df9748e13db37f7438fb6ef05c8a84f93454712ece2001ce0b049a4f9f7da80c15d44b6f8d3245c7687985dbfca4ed35f8fb2650b8cc93b39fd3a640b1170d8a20d18cc65e2146d1b393988c459fc9bc8168db459f2a9dae214f0f53f330cc3340d9b5e2747fb4ab33e225bb4720a29454791fd4f4630956cc1be4e8972c4de3ff34203e4c66480419336c2a692d2260581116204753211514284bcb84dcbb0cbdd8bff41f0fe0f708f6699735e57dbb06ca35a7683dddc9beb86dedc544ba9b5b49bb397bd21c25a50b6b46e52d5351b60a48fa571623f1f97443e1f299ad943d9ff1311204524c0069039fa23c1cd9fec946ab4bd99a513f73f3593e56252b22a6b7dcc10462201e1c4aa5cec9d728a2bb41047403a2bb4469977c375b0d65a2b37046cd61b030ea52a880b40c0250292021234df09981f612a29542b2dc6184b95cbbd5dca908045cc8c9dc2904c594832b5d5869dadfdaaccd7e5c5b0eef46906017d4cbb4f82c39416fe20d191dd52ca29e56782e07fee618309ecc9eea3cf07046b3029606b4a29a5df430819e7ba7ed2c3f140a574efde8c506bad986e56f835e24894843e3ca4a807077c7c0088203eb339ce5f1e77bb65578e87fb1a67e79433e38446e494cb041ccab1d29107c692decd411ca3a361e3268a838dcab28611298d5d21411de0e7068e04f8e0b2a4c965adadb5624b2291bc2f30c8e206d8e1eacb929ad2a95d5b4f8dd65a06ecd9088e6b02bb026949279029b0a6cbbe204f5ac17a53a38c20786f0d0b3504dadedd9a95c07b7fb45a4a29a594526abfbbed7811a1d4dd2a548c9db13536a3a59173ce19ca462ea5943e9234642ffb7b12448304fd3d09a26c14528a3e9fc24a919d63aaa8cdd2bf7fad0782f97d068a61d88f7afdb42707dd8a0e1ab1289b9a1fcde69c73ce3967f6b94c161f1b85a2b4b552d26e300355abd629d12619a314d590bb8e933ac83e0d1670ffcc611db9aeb5d65aeb65ebaa66066a6cf15a5d97102d831021a3740a674bb997f3bfdaa2c072947dadf5baaeeb7ac971385fb77d3e1a0862d7b7eab81eaeebeab6ab1edde3e559172b2419569421c3c6482428254b569ef2195b93fd65c897a2152bce10a5488ef312e20be8deff8271d0ac6faf530a4a76e996c6419c83b6f7b7350ed2ec85a1d279c7ef6fad2d2243f546b2b8146196c898cf909d0c32de6f96f1d913c8902143860c192f63e5ca7478733cd49b1919efaf4171500cb165ed8ad779860cafb6bc0ac5c17e189e55f13a9e15823d5b4af1ec108e674df664915894556263ac8c8376891d2d8d83ab9aceb3acc9b2429ae1da139a94f22f8ed1d1b8a103c441d2eeee6f09ecd65eddf6765ffb841def782f103c7a70c0c7071c24859050aeec5eebc8cd7b91a9130c61b4fd7db961aa220565c4fde60149400f46db6bde17389423105a032b8a8cb62f097881e3a770ffedec002ba1eaa96553d3a6015a3b30bd964d94b43116e5d76bab84c86851d7375e2f0a7befbdd7f3e6c991777c3828b4a80f38c83f630f182c928859be60544ae283110a28e4eb5a24d9dfed4cf66fa2c4ca7013904d6eb857b5e9b62e471e18766da5938b82c3f4dc78581e1b8f134fcb038586101a261aa5ec4f834423080d22d97fd2eef6f4c0659595ca8a524ab3189c554be7b43694b0a104a63f3595558d83d86a295c835931d8d3dfd9a3529355aecacac3b2a13543786583170460d96023451aeb26dbe024fbd360c312d9df86920d2fb2bf0d1b1a8e647f4ff44eb7dfc39222abb2c24770d8417945246806114f94220d34a30909fa879e911938789514613f630cd9bf06f1f2567855e398e520ec2096821469b28589cc816d5709cf35838883f0e8339a9020ec04b9c24138630ca1caaa48f6d74607d529700715aaac623096bf8a55f61a8ae41a44d9e25965fb7d1346c8d70fd12af5ef45e382ec160a2fb29fa0cf30dabeaa9a2c4f68b25f28326d714d70e86179284f8983aa8795ddc3f2b0aeab8e34ab1a1c5299ab3ce9b4a7bf1e3cf70c61241d3c244d4f29a5fc70b27cd0736c9d83bf740a94cc400823200b78e28a2546f6e5773201d7cb1cd6b000983bba1400038eaeef455221ca08480c28236b0019fa48d1cd5ebd5d072aa5fcce81c98d33bf4f16708803e10f0238823832810282a446402612e348fb10c4c84e685f11e0675fc79a488ca3edb30fbb2f6c1f0e50014152a310878b0f168b75af96b19aa47441a51de6a6a39082384812f1218ea4d5fa7ca4fc7c66e85e8cff3598ad9f24459fcf07c49f564b8a14d02d29eabccd10f6a8ab5533cf6b82b5cf435f7b2346d8e7a1df4b12fa2f9f0fdfcbf59a80a980c36e81335d051c76ab4739539a80ed78bf1aeca473f8118d85ce5a8daa548ec91e762bf493765d970d396c980ecb36f6b3ec2792c59dc465fa35be02639f7debf321fba64891c133f4f9b45a9f96c719c1b6cb58163cb20f5abd72d83040d8de41926e853d041cb658adb558ab85a9cc8042b6b7cd1e7df92104101fc8fd1cc8dd238738cca697278259af9417b59eb416ebc66e173dc28830a52126904c9d1ac98ced39b91d5886653158ce093361661892ee013abf7e941da49459645c0536e97777c334cca8448886c30cbd9752ef0a38ac37550c5d49f5c669e4b0de5821ee94524aa96a098d1c5e27f9de7b1365b44f9053ca963de79c73ce8997e0ebed7be370992c3ee5a8d26cfb938087095c3f1d3730c1dd9c5dbb7badb5ef123458564359aba9503132a91907491b6dd468ecb5620aad983acb564cd55ed7755dd775594c5e97b517263d113466ad7d6dc534439a632d9b568438e82b301d530f300d2612549d388d95146d5fb940a326bb178eb08249f6a7612545f79b742f883da80658bdd0246b859f9e64eddfbe7f0d6b49a19f2c13d99f90382d94671f36ad98f25d3149917dcd9b292fec1deecefd9da1ba72967e775c6d349486cab2282d2837acce18348c1dd1b574f9dca0fdb97241490edfdd5b60d35c976865d511d3d9aa574c3967cf9652823c12ecfecaef2cfe3924e8efd393f28a2364c8c7f45e02f31a66b294268b49b6489983481109fa33f1b17c7c2916edeeb6d6d660611103278ec50b887c9aa2b070c1921c766d9f216cacd7c899b22c935266db0d1c09a874be1cb74f866d5e678dd5c4411b270eda15146089aea0285d51ada05662566456522b332b352b362be30acd8a93ec4f653f953fc34ba5a4df1cb52ba81b505224adb85096446f20453d041f406171b08262e2834c3f547d02afa0b207801480209f15d40a6a05552b789f90a4846271ccd0d75abb7348fcb2c5bf4f0a2c6a000e0648d07c77c781e3de550d8b7484a3503132a9991a1bd6484329a56d62c2d42813110785397c7ef0017dce6951286bc59b0512255b04d902091743f2086e16410a1660b46aadf3344bd3241a9151a5504a6477a752a9542a954aa552a9542a4529ad61054cbbbb692a954aa52edc7d7f9aa73ef7f8b7ef6586152eb819bd00e9846194a41f48e7c0c888f3468cb6dfbe174ffdf0d726f17ba932182151479c2f49f61b0f3cb8af4bb3244537260ce42ccd215254bf4bfdac01f7cdd22cdd590a53ea9198d9441415f0d1f65d2649f412b299901f7691691ae2a0142607c9b771046b3f4b59be5660c9dd00c3a563fea839fb3cf5656e95a77e3dd329227846e1d7755dd72ccdd236c268552332576982985aa5544b461a2737ad6ed251ac0c19365253c84e4a8a1090b38e49f6ce02cb37505e130017d87eddaeedbab87edaa27813e5e6eb966cb1e9616620b6c0f60b716ca9d40ea9190fdd6f92aba8998226a59a29428f34d52a07adc5816388ce9d4a7d4960fa49755bc0ad9aa11f76ea934aa5ae4b45e41cb1f6e686084cc4cd0dd58270100807afcbfba2f378248a29e01b60c0df441491de51784ec0a1bcc9420421de6b735d17c773b1aa1d13cb3ec4f2e7574f6bafb6d6aa6999e7190807613f5ffb9ae4a0e739bf17793321c0a409064170ebde7018b4948a555575ec2c6daa131b24fbcb7afd5e8cbb4ed69e73085b6b0ff103cf1cfe942d32077ffbf980a0ead61a83331654154b15787e1738acaa6c22525555652dcd4a4b6930f8481db113d3113ba70e920eaaa3a4b3a4b3eaa43a331dd9ddf7fe8bdefe077c51fcacfd6ffb9a94a0f9d6ca1cea1756265382e8dbd7eebdd65a6badd73967b78e583ba227de4b841475eec81bfb756274287e1c52b4ea14e91ce944d191a2237644dbc3e22fc4aab5b62376c47bc71ad647ea1861914c24d931621b6999963c00c9e22f3d94019912c2fe89112f1cf31e415624921471242942003502ce02befe76866dabd58fead2d669fb37f5fa354c101c5e143cbfada8944422594bd361652556bd61d938694111622ad92044288b45b529606d955d1bbbbb291d6f0aa594d2ebba2e4d0ac9e2af4d917d458b912d2ee894fc3ba5393ba552a794afe6703ce72e8e674797b783c3f1e8c5f126beb81e500ea232154a1ee2c7cdd80ca5bc7f75d2baafa578ceba977559d995e2d951e6ed90f9ba3e85f3a17685ecaf8d1f5e6d4cf128eb5ed66565578a3747da384395c5d2469636b2ee655d5676711cceb579aaab5d557655acca929365db3cc60f023a5c8753bf6eb5f3eba1ff47cdf62da97ed6b6c5beb0b644d18a1100b313d937d7ddf56686b2b75fa94693712fa89dbbd7140048167fb05b823b1b27d6b69ed083121746b858821d516bad18638c31feb07aefe2038a5a73286df00cb9a8546088391e6456d9c002dbdc9f8bd8a8d911efcb7967db4c2a9eda8883d7e8e0ca9304ee1cf41ba280c58e9400909eacd6da7b5d775543c23226128c90d210243128d188eceea6739bdacfd3fc7c48fff98060d78d4b6652314a8894864c9214f59ca7498a7992f64dd264710293425826fb28f499eca12fa1c9ee2e47dafbfbea03761dc6ac21f39174e7229324769db366a84f2767b1ba50abf5d74ea0f31c6130e44e2d87b215812c4fc0497946e006987b4f3bbabb3112864dd30cdd28cd298339e79c734eecc298266992eed532182c1a3a50a15c26355363c31a69e6ecbaaeebba4e08324cef5e8c5fe331433e6aa1545051c850c35c6215a69214d95f747053027e37e2e0c9c11923850cb50d97f2e02a262605d9bfcb025791dda7709b6ba3027fedefcecf515d7f7efd1a06316405b84b327228736d7f391e64fe7cc091e6f985af69da1d713994ad1eeadf77d40c8517fb3a3b884ffd0bfb897533d43029301f783df6bd45cd100c47c150b9b7f3c1a474606e92a33ac6ac724e4e9f4e319e73bc8e9921771358fa09d3f94d4c0ab2e8aac053647feef3d93d0d07611a06069b73d63a6796655cad2854ad1c8c92e6346064649a00c5093353e8eb72919a8d2560bb044cb32f016399c506787e8f015fcac202ec722f0b1358ca16ebc28345091c76ddddb4d69a612ed91d3bb3c2c7ebbae425ad94224f14dcdaded6ed5d472ae5ee1a462261586d92b1d4b09c6844a4838610011052be6f839b5b6f9ac712011052ce3e1fa683649e12f489657f1e39647f2226f71bfe784568dc73fede7b2fa7719ed37ed37efb1fb4ff817bcea7e33341113c52be16e0508e4524f121e5f117b13de78b4879fcdeef03fe946fced35f0538bffd0f9cef07fcf5e0bcc5e30bec9ae6c9d16734d27ae33c6d7b1e2f2f9c4f4ecb7d2fdba77d52057b60c815099a0fd6b899cc7a726aa67159a67d9635b1c19cb3eaaeebbaaeebbaaeebba6a5d11a58800b9ffcac15fa2838c4891e52e71b2f4775f5f98d22d7186fa090f728ba1c7c9d25cae9798fbfb6f1538ecf1fa1abbc44bc456351d095b410cd4d44e736aa9134a498c0c0d6ba55aa235c9fe73da50825edc9a21ecdef93e3d58e672783930f7875b0eea0087dac90b09aa4db08d145d5d60275a58223b3d42f6e7d848517ded2445ddc50d5a906638420ebcc8fefe35dc0cd7d47278ed12377861068ecd2907da49b6e0126e9d2a6e65a772a6b43e75df550cd4f6841a65d0c10633947a5fe08edee6fcae6aeebd3455758955ca5a2baeb5ce690306cf1cdeecbb977559978b7a3591fd9b90a2be86789709e3aeab4faf52bd5080c3b7cf9af7e5a41685aa7d6b7713d623ce623161924516554c11d69b1d76c861bdb99f65b19c47576a2dcd25a6246195e0a41933e718939a512d69d2b2e9965a86dd6bb3dff6f968f7661877ddff030137785b44dccc39e7b457a6dd3b53a9eeaef762dc75ff20f8f9e08889d1e1225b6cc949d4b72c5aa792898883da1a9151a5504a2ced6ed949d72417b8d65a2a49915d4286bac922457d44da22d9df3291bdbba96d6bb243ba52f655a9b3255b9ad1ba2d908fe3411c3e3724d063a6e6fef7bd6fa5bc32dbda3694b8f884ed875f6f5837f7afffc7301a3a6f5f5365d55a6b12f0bcf4c75565d12e82e8aa177631404abb1b0b29e595b7a90c692f676e7707388bb3847249ce3207ff06d0c84e8313300fdb474313b0067a50e186d5c9ff63f7de7bddb32cab61c996263662b68227352c6b2dca241a3921991955313256b6c9d47d279eddfd09eac8ff9a739bdd3ab4398b7f0c67714c5ae193f39d211f71f41b691cce73388f9ffb5e6a7061062f462f40649860d4263f028911d7f9cef7e2e9c75f07e918d7c11f891fb58ceb00802c8c90b0468c7878fae39ef3f569b2705f8b5b0ec236e998213992f9a5cf200499278c5e8070aa3832e2fc0b9001b860b4b90efe40fa8b168cb84ff15e3e47462f405ac988f349d70148e760c47d49b4a74f606a32b5c973dbedeba1ffba3f6ad63e8f7d99b1e7b1df764d264b0486dd6037d8a4947226d3e55635ac6669de6141a7d3e94818a2105775462e22d7900c254aeeae201dec5eb6d2140eb76959a7d3e1a4743a9d4ea7f31d189ab67158e8607f185e6795daaa5082c008290d319d90a094c4c8a432223768a2058d16363065289d4a2622a21119550aa564892d95582c97169c7ccc900b769122ae740f90fd491f14f009f0fdb04956fbac8390db07434042d83db0c97cb048ed7f79f5eb92e57670d925067749bec7072524d1050c48a491f679eac723cbbe97249e1f5efb5e9204a21ba8f40d7805b5642302000800000243150000200c0886c301b16840ae4b82ac0f14000d637c4a7a5640190944498cc3408aa214520611028c014300406086862600dca65ffa758d088e0c9fbb2a80223a854921dc88db41a38e19c71f2e639496a7f744dc657595975523788ce5a50ba1cf153e2dd552211c9e8bc5d80139f76d0c3e85146879db58f205aced32b4176d5bec0f53ca2e22834ca258ecdaafd24c10685e9ec86e1ed6d9e0008126fe3cf031cfce5c5d169523feabebd20e83ac87a92f953b9624ff57b6ca76a2438acb79f3b1fe96b20f5f6f1852f058bb1d9bc9fdaa614087ad612060e5c03733a5b038b1285a4564be626c8be87a89d94226cf7d2044cf5f9d66456fd05671fbc39b42e5f5e63eabc96d6f790938bff207bf97cf44a2aea26140a70d9f5d5e835580a85454019daa30a0cae0e2933ad423f897084c82f159f28844e09bf65ec415650a9d66c9c427207227f18de17dd4c24ac5a363f0c10c9e1d11845cf8618ff211116793ee4a6e27c7eec081c303b885b9459f5b7739aac05252485fb85f7bb52df86713599cabd5380d6eaa498a483c1ec3ace36930c8477924e2db8c9af4f6435178c5e0ee2d748feb9bec0a0ba42344cfd679604f3df7302488bc4b5552151b37fc8a50d5e9134145e439e6a472766e232c34e08b7a665d3171b582a4003dc6b3a69aa6b568f0430dda12d426c0119563a6846bf4da3c8d07495df82068dc87bf01029b123f8f5b1759afda636fea2f9f779e383506588cf88a2cb373b213e39ab97b7cd54c73700d11db4735437e444b95bc8228c4d7cee30d8e86c93c33f0a0a83d1d422463be6bc7458b48cac2cb1d1816054cf77c899fe3d25e251e7e7065a238bb477cbf1e2a1beadb856a4f542c7882bca20d9994d6dfce419c8164fe5b1a015a4ec39652e722804927eaa3742f24d7d9e159e4e102e263baf083ea3b7114d86197e454b7925e2e7e6ef1f9a159ab1d82f60f09f415b7f8cab9a3b0a7b2c53a95307116028b74b9a0603806115df12ab9f38e8be0a963080b5d848692c00e1a2f6cdabf922224b40bef60245afa354b958637c12cabd2205935d436b14ffcd727fb6762e3966993dc7747879ad4f8530cab94e7f0c2bdd86ce70f18be2f372da00fc5f4f314501cf851e897740e258dab24e1342231feb32478435ac707ae266404e513434b04a869a56922cca61eaa75c509f9a31e74345d81454d8a1cbb081ada731cdbb7d44f713f2391e3954aa386fd0048799eee1f41e403eb24546b78d05ba47656a447916807c372e413c2cd7800f878812309501c19597344adf556ceb5013eb46db954f5ed95429f072f5612ae78424b8c8fb08abce15e71c53ac698e65048d165bf16c097b30d47137b4bfa3caf8ed3f27021764caa4134c153c5533fc2df91043078433b635fc57f15301ec9693f567132862509e779b5399affa58001cb2ff4c371a259ac8aa3501a6b6a4db9c685fbbb337d8e8d014fb366d263b7ac644e50f73dcca9950b976f306a9a471ea58f9c66bc88d314150c3e55435794f18482e7cb367ea1001f220e7f96d591a2f71ebf00445fb9226f80a124c2f88f6419059732a1efb886fbfeb809b4936f70a98bb6e0aecc8ad095c093efdefbb857998afb56a3f4db20759d032ed6f3593d3c148d7663156ad9361f6411285d269b383cb258b5f4d23408f240347c6ef8422f4a60ee6759efcfaf45ecdbce013636b3d3924f66be24ed4efcb47ad48a5608ebd5cfcdac2fe9e8cd536261e21ebba14a827a4b49ce5412fbc77a63f661ad31950404e623c6f065ade08683c42a8b9b22a0d61dea5e2d945ca20a97f46b762e48ddd274c325bde1372ef9dd2cdc6277a892d79c9d0be94c44ac4adfc55cc8c600a1cba04f217989472d92c29364d7ffd6489be6d3f7629138a77a2d2431e931c4ac1f04ad56846f8f2639ddaf2b93746fbd3c4ef401222e153527cfca03b211a01a2a66759b6f929aec638d95011d567318ea67ec855e6784a80813c378295c48f8978aeb1e59d0472629d1c680d188f66f0a1a0ef320bc080546746602fd037a4dbfb87603dc0ec9c4e219a25e0e7d2708ce5adbdc12f912543eaad33921bc2ff158d7cbe9ca3d5894d5205442a6942b6996fca768dc09ae7856c02014577938adde74164ad09012d22ddce24110453ef2624a02df1e9a3e3b2abe3e99d6440556456c4eeb4364e4588a6a22e67f29f2c75c2522fe02a3f5e31fe5557f4700509afc185566af0cd0e26a2c86b7b745c5294c922949156fa354802f8d5a5732530344c3ea36af71e1decfcac3274ce67b1a92b8e2a609e93c83ca5cb208d27b90c0be62138e6466130223993f29436551884f9a2e3488d79b6e2ed1e90eda73d2899587094409d8b7d85f4338d286723395811cb14b0212ee4753d6b85beb09eae7625681bc50eae7e459784af48844cd13d661f588a0b3a5b8cbab5f9c9933b367a3d2fc14f57418b81a8926017dc693d3526d1cd8b1a01c6693138a98d8a78572c152eeb0455bcafb5136a3f6e5ee8730371b612533ff28fe3b62387b48f7d6b5d728ca4077bc44086bb052d6418fc1c17a3d042b71f54b1017b6e25e61940cde3992ec4273a808556e072c2cff02eabe4d01aa7b9bd683fafdf963d447c5c266b5efa0f6764e7627db79d2bd64bffa67069fefbee62c754c0d3921492457b3856cda2ed310777b26d8190a3eaf569ba6ac559d08b9956f9c898e0d4961ecc3ba5793f36caa0ba866c0b773d05f35d87fe5470ea4cbe25121e649b035dd0377a545fc775b507e1a2fe8fea1eee09c20b45d227cc0e2d735f239bab14a719dc93c3c1578024f2b469223e1171a5e2534202fed853be3c860cf879e7c6fce99e184899c61753b2ae5a4e8c9e2f7f634d17b3d5dbd6697210c5653d3d59bc59f9678ca31c04c0c558832a6c68d842d5d6439667625dced9c32e719c4021d6147f6797646bab974e70846b07041fc99f052b1081dcf8cc66c34729d0bee04a8c08b15007aaffacfd237f4c6edd418fe038be65b6f238ad40abdd791ab5a218494184291e2afddf65530f8fb4e341b104263177892bfd5703fc4ad75b595e0698bc161787549da67a43e360bd12f5a85b5c27989954dc45df959980ec6820c85a94fac897ee793bcd8ac0af34187ed1321f9fa308297e8b6f3a3680aeda7cef416bd52a5bba20c8be3ed0cd2b2a818e95e6fc74577ae1e64d27129f866d813cfa6e50b4c26469b7acc61b32e6d95d854660f124f0fc410a36737927221ddb78c6d7fe67ee789655a3f047b8fc565b246d8b22d66d80b70e8c0d00ff61013c8784ba7af8dec22e8b6aec01e5896410c2636db3d2e6059bf1c9f03eedc48f920a5bc9b3110bc1011ae47b90a853eaf3233d832948cbd12b38a2492ff088b8c0608b2671dcae971724df25607de094727c9a8fa01d0c1e9924f4c7c947e284161aa94ee1e510f4661cd7870fc271f7490d8f531cb87d22d075a481755b4d3836433b0814083a00e8da726ef12483ef84578debc5b52667da3af290666687731de0a5a01d8ca3376e290e485dc899514a6ba077709fd23e1bccc66a55e6d95ad0295076942ff35af2efca1bc4e2e1bd188cced66041a5b75a1448e40322c89ee08efdc8a47f252618c41aef71398f4ac34e83f182eb139b1b14dc62014412f429d30a4602c19da5dd8104800a4733bcbcc0e90e42826c0021e7099287b348da5b05765f1b12460daab3472bfa4cf145a17ad339bfc8a868d630cd7b898b3d512bc5fd1f418b5160502e98e37ce8d98489c87e58040dc82eea49133f5ffa317f88bb8e74683d25ead83604dd46d9c5122b831768908cf0881ec369ca3041196b53324e7bd917e39731ab62af37ab3e7d83a43ab6fd48cdc2cef381944fde011b05e667423f353067c3739f64f5be0bcd320045a16919e7186e418085350c66b8411d4f7289b8beeec6b2b37e55b0e855d7bdec2629576ffa6453ab8a6a933cd195bb78a5202cc5abc26e0285aa55097392afe92009d8c9b2283d7d913ec740c9bbb98906469bbf3ac36d051bef3582b0422633790c6888117da680900db163eed28462c0c0a57763174b560560e3f9bee6e8b6df1d572a6fba1bbb673a0cc55d27eb9ac002c67d0179359dbc5cf102d0d8e411287b6aaaf2345c028e11efba3aabce7722f4f91191248f472df8370e7fdd8b122369ca2a39ae310e2c020b967c9a9aded74ab0ea4d032da1408f987a3364bb6a99590c60fb615093a29f342741b935e82b0325bed9582a1f9bf1f9e02b47060707b03ab3fa7833f60e878560871ac603fca29143b443dd88af70b661e7d46bb35aa6af3d45efb3816150237b5f6404954950ba686712464374c505aa4490a6edd3c022f46d6876ee1c36ac98663000730e3b4d412cc1f4e4a3461ff5fc88f2d46267a643e60a00aabe54eac9eec1e3c41656de54d4cb8a528eff3cfb14c64018a3d89a39252af20b55cd335db7290da8ace21ebcab9ba2599542a5f7e4ef3d7a2805cf728e612b1f2f2ca7d335402a29d4f476d1898d5d1e4e420c343d6d880a7fdcab37410d4f7d1eb7df8cab26e60ab802a3414a5b4fdd11b9da55c13b4292d1e9f00749c73e27a3041663263e4f2409acb2bb7cfb0acf359c23b06d6f5e9fe5c0f54e9fa8fb4d058bddebaa05d6e226aec3affb3fbb7e65434477b5b335373fd4446bdbded304b764c5648c2c21ecf93b707c6dd96b3bc7bffdc6d057e53707e52186ac9e6d3db40ce74720e7c0d5579edd483f531c4e2c73863c63c152bc6c72bb54b9efd72a47db6d758c34bc053b1e254d56cd97ae2c54daf01c599c58de1522dcced93e78c9b7835e9b3404cd2c60941bd990ffc9e4e4ecd5234ac2e942faf11e3fec7187fdff51ba722e0465d3715ed34311f90b50b348aba084f1c367dfe2f24158c8e3a6b2ef382acace0d62f936ab42b1caf4381eff65d2b3048088af15adeeda97c06fa7cabdce6802ebd07794e23052f4bf6049f5d86fa5b10f58b5c4093c0ec420222890acb675ba14c9c647db86006f28ff875bc0c263257eabc4538b1498f909dc8223cd90bfcef706069ffef871119b96c6f5d5021290aba2f81da860a507527525baf514389fd3e1a6d8a0634c7da9d5139428e0bfa6bd07a0bdd2ed4c7fa34dc9a72c08100ea06bc5392e744965c66bf8e8d87a9e2f3f65073d7ed77305b7d402a6040a361b23fe1fa3779c425ebd2f947bd4c32576c983ff5a069756907df4246d66ba5858d32905b47e0d320a81c5d92e08d5c26c7d806cb11f4fe7781545a715187649ae8e6f7dbb8c0d4ed0b2a4831425f3183de507f8f339e97ba7e2dc6a9ee732fc2bc6c095d9ebb9219e48acf28ad323176cd0036bc7566133e3e181eced13f7e08c17c07939c2ee903017be4d6e65eb81d8a664cde9e1359738734ca22f4beed2d42d512ce3854f6c2916405b329d7635b33017edfebe362cb28bd47f6015d37059fc02a873833b6974deff97e7d97fd4233e1aae5a5a43ed1fa7024a8dbeaf1b972b9df3dac6eac548106026bc49e2607877460f03a89d0f486a3986ac406478737e30006fac5a03065c9ce86c1d6fb40fb15d679f15aa4e1afac2363f3916a7c57fb2ba997caaf5b4d74db016b0c3499a25957dcf2234225681320972b08fffee2e07caa71fefcd81679ab92f13d752833e412229bbd61696bcd7b2e124f0c025098129744000111f2fd7534be547cd1c466de31fd9c019b3992cab9ea1d5eb8c58767a65ec83ad64d29a76978d77ae013ec4576e8655341483eb70527d078c36577985422be8e3204052bf33424cb9816ea134beff251b89d04fa7b5c019b2dea3c7177bbf82bcb983eb1ab8e9ad8c26b53e0dcf38960c53a8f244cf162ab14162eac46987606c1269e477c80af59f1f387e9bc5fb8326a3f8a77425e2d0c41c7c13438d14a36550c4b6e9e99e0296790a4886fdea0605710290d93584ae999490fbabbe9d18ac2b7f4bcbca066dbf6f1cec0ea70b229036f8d0fd968765f3412c347fd23b83de97d08e7532004ca689eed308314864c334d16de4258c7006e7361c5683f7b6b4c01b48e923365140e90b195f21ca00c40608c5898aa06045b05c70d329c931068c1db40f70947efa93c8bf41ad16c85e5342f44643eb916072e6ba84b545c88ae80d9f7b2861a15a85cf8952e338ed2b772632b8a7e13b253c187b0484ff90d91027871936d1b99393489130e5379f220917b7a2e1917a315d42379454212e7b01a877725040f88a3d685485ab138609ec0c74318071dc15ea384e427e2ff4a1c273789b1977568a6c8662d121c2c622aceb5306a2ad509b760f57f98de39ae1f851b5fb3ce84f60f82751536a8a341950e2ddad0c5bdbcd1634b72614b66cff13aca80b4d70399c28828b34ed69acde3386e1a56d87ee30c354c7a4c53658d6f457d44e1d2b2fb2dc90a1c102a274afbf0d32363bd702fb722fa3a3ff2c5a011e04a0e65b17415086b0302b6824c70fab9f7007442813ffe630be123f19fd7041d1f9885f9df266157acda0ce1b00208171c0bf2ff16c374465707aef66c9a8cbe63d0701cc618264c0e279b154d3d0fa8b2645c8bc9f8bcc7f16b4b18acc1312c0e540bfa37610d2d8bdde03e41994bd98db3835de83b47388652039b940d45c0d775812e16461c2c9dc74fb249a5932851dd4b29420faf416f77937f297970f5067ed0eaf02dc15982f9ad12c2559fe597bc05cfec648e25ef4dd4b39a0b7ca8088c2c93d2994d364e2a3afd9a9e1a4e18d08425191484e6ac2fe4b400b844795fb01a6c88eb122acefa225b1a771f1babe0bd6e31dd8ea8a4eb365ab8d60c4b136e02eec49b21a324421b76c2940ad32e177f5a7c6c8671ce3a7483c268e1497d002483503316f2b12516a44df873098384413d6d41369ab7af21a886808235217a5adad1e0e6e5fc08c851b695870c3f76676a978b981610b072f691c5e1c4e58659deb01d3452e72ea29620831e152bc805db5c94c184e785cc05d9d31435e1f11d86c629d126c440e05c10ca69924043e1a0a3b18620ce7ffe6e6bb10a8da1c64733c1ffa13504b52bc7c4562b5a2306f22c9dcde131d04546c15c4e0ebd90bbfc03dc7d07e49240974e6d09ee023208ae7d272f57f29389770e5f10e29f044b583588b7e43c364aabf9e81d013f5cb82c38a08574a3963c404a8975c57c8ff992211f919ebf61e7938a2e43c306e2e71ef378967954a4cc1109ac28fe20bb1ca1accb194d44511c5313ef3950953c3b2360cf49514b144cb413ac4966338761b4d8455f696108ceeedcd911a8841bfbcb47dadb18f08249c2149270f2ecb4cd9e4b6933b80cc7039c670453423922a79ae7efddbffb5da7e158a206c259ca883c4a5576934f4b8d21ccafe2235b1e3d11b1f57d31339a204dd57cee0df44a86cf26522d50ea5bf440838265fece68130c7a551fc6131d404aca620403887df098915b6cd8a0aef16f93ae0383adc37eb640fa8b6b2f9cb9ce64b2ec43664a49c114ddab5807e67f4dc4bcfcaec131b2d04ce73996e0e4a16e396581e4dfe561b82278817c46366623a16e85f0d3ea00fdeb12fc22528e330978888a84bf6c9841a26ae1bec9bad7352d1ce392d6d348f307896e2d01cf7a4744b97729972ea6d6f0a23fe51b0fbccfaf7df5e2ea803e4b067f12030b502e368ee0e306c022c35b315677961858a42009bcdd172a222d8a1166f066dbbc45e0b2909a4ee9140162ea67c3847873b15c78b9f32bf32acbec64488ffe6a4ae4dbbc20e7319b345902ec07f0dc23ba292cc9c4190550abc3419b00f2210232ae6a7856583f8cfe692cb5049a8495c91a109f4394f0d557685d6e0a58a3965f21253bf954277576697410d2f12d5594731d30e9dfd5ea14d8063cef82149ff2a3f1cafba9cd26e869a7212c8031fc5d0114c0448146ffea766c61eec9684e3b3f887a27e58668cc45d71ff4a80a5ad0239a126abdb3dbe73623c95b374d2837c18386418046b507cd24a641d44f9b6a733deca929687c1c03af9da869fb0f3dfe75861951bb9b48894693c482c743c1a3e8eaafcb527208229ffc0562e8c7b3d4588f0d799fbb440af6388d289101d756516d7abd923fe82b2f8ad290bdc29dc9b5f5fe377e2b9312a5c0972abea62589b651b02f5a3a6af577e97e0917f8372c81eda93868926a9f94faabbeec74a902e901942873ccdd12305ccd8cd36cdb6381e450e84073840a6056e8cb3866ab64137e63e8d9ab2e155185b84daab0cb0a7b124900f31d2cc40724d9176d6eb0eca4c7680716d32e6d25745c235b1097df9822d50317c5bb866927415cb6f93fb7b244fde84cfb312e3d7147cd202d7d320e023c51e4c54131209b718884231ab409349e4fd6b84b00d3f71251438e458d3a7b441397a2f54230387d6e0cc1d49f980313d93fb03e18969451684d0375bf026426813ec447da0da7db103653494ae00bbdbcdd2d39c70d35040a23c7ed2eebb4e07872619789d032bf9bcc9a57e0e585852e07b7c464bff65e5a517475f8b5a1719f83769868bbbf8a3c9996aebb049a7a5bae9b5c32d21b4df4ee45afa3b178b863c4f568705b2a4698045c824bb5086725a09555e17f6886a46144ba6585bec824d05a30b0e9e428a35ff3632e6e2ac3c8f6fc85d1d1482ac83930f0e05e31971d965079e971ed5429b149e34d0341250a564d24349c0e157a45fdd71ea7ee0608647d75fc7e563a71279db41127fd7d77d52e86df4ea9f721fe9576b6b38c93a90c489960b8f0ae6b30ec7a2afd60ca3d5264cb5d300c1eea8f8e098b4a0a6f4c7eecab23708b92db7cd928120c49d5aadb47bb7113e3e30fc89971c4d48482bf38e73ab635724bb3828c815ecb9130da3384adc3b03207123a8e4fce8ed5c58abbba9bb1373adcddb19e16b6af963d83a8277c7d84e6657459cf21acb8a6807341113d56918f310209e990185f2944a238735c80c0ccf6d52d1a94137a06b8e4c342f7e7405f104cdf8d14477b9f0c82bd03a5056edc4702a5a11d41b50ad7cbe5426eae6311b9b993514d918e445568966191e26b3f561f821b8c408d8938454eb267d9d42112260cde13ce2f9899b65dbdede9b8539f971275f47cadf3501604e12c08b353ad1e3ab52a58147102eb55ea495031d7ebea8bbc4807409748c67a69545a40d4d4c14572986d7321034cfd14065d2590cfcbb701aac6fc28caeee8ccbcd1ead3af68d6ef0ff97b7f85e9a28a96b71653b947631d8bcce23bb020a1d574e6449c24ccd1d719907c034ded12d921ad74167c2a032497daaae46518ec17129ad7096fc50c91c667a3d30a97f6a856b8d390c3a280908f5575754ed4399b4bf8e33d9e764e9dab3bf0c95a5fdd319ea045ef1718c692d3009686f43222f41dad53c48d33f5ff5230b41ac9e75fe51f91fc5e85bccbb9676cf96a93150935f75e7eef55a7d1e0e3a613380ffcb1ca2b614821eebdaa6f832b842ebb91567741a8f9e5d84f189e41d4cb65eeb94100c8f7093d295b0daa1283608027f55f7474041c9a5a1cfb6805b65d50f019ce5a99232e2e3140e31d5d57edcd03432f70ff48d77759ef8bea1cb01d099be4a889bca5939c33fd3e0daa6ed8dc4768b0668ae6f3db3e6fd1cbb6a85da56340094e7d1ffc325670f84a5ee4939923ee192ca40e79971bb8625f57fd5aca4dd229b6e6d454c6f974cbda939aa74bb681ea6d293f1f5df2a254303b7f2e2f40924121125e431605dbbbda93bc410073b86d62bac7d3f53c8ae615b2c2363c02475b861d192b2eb4b442d11cca5bf2bb6c940842f3b0394cc3d411cc44eb5c3de4a16ef4aa0e707d0f3a0b4859f992eef6a36c8154428de03e35c08353d4998a327117fa90faf926508d3ef5507421d22c49c9430e382d266116ad88426a0a5b8da7f4550da15793560a5d3c9bfa1fdc808b778d396d5da9467b8d523dec1b638a206bd7d8884c1b535bbeaec642ea6ead7d3b9f167c13711999166d5f05ed61e8ee4cd4f033705008988638f212ee2b3e8ed5ff4396f7fc8ca4dabaa1ac050c08131520b613c31dd9834052860b3dd4669230546b030b3eab684e9a68a10f8363acba1a7f965d89761272efd0cb4f0f5a1f6efecb2d8a80ea985f1d18f39ed333b420e9cfcf37eca297b804abd844194bae15dd07d802835f43c714674bb15d2fd8dea5cb80a974a0b5ba029483595970d8e1f29022b5686930b1532c97514ee8c1e3d750ac8830b55403f39dfb40ddc8de23d000cb311bd73d5ffeb4a43efc2681296d11279832c626bd84de5a0c515040aa587c46db2cd3d38f66bd1e9752ae2e2d6a4f0d1f0068a03675e5c0474d87f3a40631bc348cce9ec056349d83dff1fb7912792ab4bd8970216b593a0e3753a1f07dd457b84315836334c43f725aaa2d97398d9922d55981c1100686b8812a529d5e56a4d11b3ebaa48d820f5dd509a195f645cab2ab92061fa3601a4494aeb442010f76d41da9155b55d9b714aa089f00cf7247fa4a542a76374f888f1c6c8b69a010dbee65c865bf30229eab1008d4a522a2fe399648728359af7813874ea790e8af9638dd98b8bd6f33104db55108820cb9f9ac6f6e6c3de30a66fe8c7efce94ead3ca9382172e4b8a5b96fd349e0f3099908f32ea59d821c6ac463dc6a6744da57d97baf7892d5e29873b7d9528f94a76ffa8e8c1d930045f45c426af0cc6d4bb7057da85a4fcadb2d38b2b42e49b25eae4ccb394a3d8eec78e1c2ad068240fbd1208dc8ed7ac96d19d4667ff89ca46f97688558c8ca9f1194e098d436c8569962633eeb0154cc38cbd13479305c2e64be2483022cb54e7c79e96a6e32369a02ccdd69007ae7f647f0b7cda410315302bb08f774e6a2f63b07c6739f936d32bf41aa50e4096f925e9d3b7622268747a585c5e268147084729f879863b597b32cb26dc712570f64b1f19ce649b6c82f9f260e19dbba5a636fc4503cd68bc54b1574c304cc56537e65d9feda0a9e93ef4d385d9907d54e0794a261a999b4bea271e8ed3568c2f7b0c097d5e3487f374b1421d6fcb5f1089cc4003fab1de4b035776545b50c8d7c2bd3f4b10990a629c028e0d5596adaec7b5e2d8d9d2e3c9530c20081337a559fec08b1185d515287e3397c72bf4a94f852b7e1b6c64b6b3d0d598246ae273045373fd8c0825e91a67592c92c5b8065c49cab3e0703e430fae4b3c14d55bfaae26516f6bc715057071a972a01f43198d4d37a64981bbd19c77662b502ea6d30694368d41dcc72e6ba3af115a127d947a2b8bd2f3d8de49af3ae85262dd7d26ff4f1703d50649301e7850add0a194c12c6781fbba0eaccb698a2d4aa766c588f183f0b4b0ba52b008210e5165579f47bc064acbf9208a6907bfd4d4d411792e888620e6fdf2a708ac9d1730e1bef4ee2290935b94a3c097b6756b5b70c4e6d1734abf96005b90cc641f07904f2b994d718b0ed473eba06098110694b552e9b99696771333f65586e70adf7e20c5a45316ce6179ed2e77cbc6f28a78ac01fe0ef14e2d18260b50309745686da85871dc194f00c116e9402b320acbf6070db0cdd54f9c4c7798f355b2a4dadbff1193bd803ccd0e64d06461737987e0a393b2d302808c01971565fd2fcca723a5e5fc3710c068cefda4df6a2497c4a059bf031102c58f64b519c71c0e17d1ce432d523892541d4f0ff2dba07383dfe11b300a785b0481797d595ae2f396b20303299a82a47e6f670b0e7771d69ed98283d498284b1d540b42f0dfe1223b54e632a24a643a40a886070f45f660cde023fff568554a05b823c7717a34a4aa9a91669cfa92860905ecc1c5443b3a354269933f2701e95f284ae026366dcf192a5802b9840d7148f72e3f344279418333f250a339155b1faaa8ac140ad732c0c6244c19f028d6a75cd919f1f2e8969da6f28556b751c0496f89da49529ddf6c9e8397ed50587198a494007ccf6f69187c5699d47d40e3f18b4bfe4328aa65d48ff086bf90d0b538b0624e248c39d6a28ddb4466b35358e33337bf83d841c12de1fd4973f8914023d4d72c4230fdbc1704be31c781f5670ecd7793736d598568e9627f7f84d6414bd04ebf7cf2e7b686bcadb4f4d7f90f958e2184f56dddbf225536d11026865137c20bd6ceaeb6718084205f53e75611c1a56eca30a2b4129d8a1ed0722712ec85e9d33533d79c0b9cde5abbf42e831c33a6bef3c08fa8b524766ac0b40050405a000b89550f26bb141f5a319646109a94df15967c4200d7bdd714d3b871dbca281af1ce3ed4b3a78739979a5ffbe786235409f2507f22d80aeb4432c3fc8ae704f93f449303f58ccde1cf5f2cb0fdf7cf0461f86e89993cb91e729b4146c5ab15cd3d99b4bb101d3c6cff0944e22a0fb452e3e146051e1f0d8544730eb96e959a1489f88966da159dcb9c498a2bb6d879a203be30e35e385365bf848ec3932984c9f7f90a8abd8d160bb362dfdaf3d48ffc2ca7c3df8017f1f42db336a32c9322ea909b3aa7d69951e51034a3163e5f8838e368b5b061da8f42a29cc29fad8c94412ff64d815bb6260c47cd0b9cd41ca1a8970211a6557980f1a1454a63d3581a4ada8994646b056d38a834dcae9767b2dcf37320944992b1bc3696eeb22d280b81fe4cbd2135ee3047b3a18724c8d32670d652615857030e35f5acff416c2eddd42b1e3c0edafcb03078f8c16619787d446f8cecc7472b6471b6edc7b8ba37be28e91ea429b8761fd8a7256cee8c42590f70032c781162b62c86c65e5be9f2de9e9d49be23ded5a1b1901bdc5913e0ca4a138a38403d9dc0ed1ebbf155910ee3a8a3b9659232568896bdad6154e6b328d4bf42bf97b69e0fa24f2f2ecb993618a1cab429b6d2eb5c68ae6776baaf9a3803dbbaab0a79eb021c3469715ed315fdae932cf5c8e95e0238aad7114b4d3b6d1daab75bbbf2e579cb26bb53f5bb35377cded2a960b5b0e5b54f4ece9235172f67eb6e9a7a473cc8e3e45ff7904529b370f7302368cf72da7071379b5c00692407945acca2ce40f5f81e4c0e1712b1471923e8ffc1067eb70c5a54a7b50402f2e7522a333e6a428ac8be5263cb2bf88d2dd8aeccbbf3cead12f2d1ed4f56bff673d3940ee2fbe9e2cb955bb1027abc00c4153f8774b531ab953c70c964ae668af1dfcf70f3875d80f72deed8e0fad791a9ca641eaa2aa428483c1d1324ef87f13a9e73da80a4e47ddf00dfcd6ff5430592f1807b89f8373eb65760f0e85c0ad6db55396c5e2f6ac6878108f26813a225d205695f843e046bd5bd9bb1a9c21e2c5d28dd65f8141e9b9b358990e212fe144ab700656078a66c2a8a347b0d3684fd2348e7e775b9250414d3442e6dde27bbeb99de7fdcd8a9ad6517f5c6b3861997d4a30a5b6b4fc3aa2a4ef6b9195c2aee81fd42bc696a929dc805644c01348118e171e747723440144d41da33b8871f872ace86689bf8fdf3b670238d00d03f2243565f80a720a8d2b741a7a00eae1b5401963a1715d1a982bdd4482bd0a6afad0c47a837c4f1c94f8c0009357ca3f498438627fae4b9823e79d8cab766db3380c0dc8455e31421852c744a91e9be55d3dc4ec45704d0b5523f3689fb2cf7992231f9eeb0a7d7ce6809f08971bd367b3747f3e830a4ebb4a747b2001fcaa2621d8f37bd31fec0f2c7c2dfec095c67c360d4c055e7a1695533573a7aaa252e4ed0a46f2eef2ca01eb339f169db4ed3ae2b02557705fe60710bcce6ebbfa631b08d254d5daa6b037bcbeafb03bad064c36e5fb1210d0c4240d7e69b47977d6dd5d6e83bdb6e9d848da16512fadb481a0193af8835ffa5df26b6427aaeb0ab4f79aa7b569b038c2a59784c3ef6b7cae86e70c7e098745ac71d72b549965c312fc7242ce7627da43ca919a6b925fda1c8b623be8588f95d00c64797d9e52d660b5a98959f84343dfe8cd3dfa58389d4289710199f61b7d59c9e384ecc69805cc6310210472914dd9ca19d59f950da534c0e246832444b6004de9cc58e13453448ec0bb27f90e3201cfeb26f801959d62134034edd83c397858bdecf4bbc99728535c31a863b78c48363e06647506d9f9483fc3e52f03c8d4e787f9d8945eecf3416caba8f441c7d7c7864f39b40d472bde37fb1ab68c5648bb30f08f8f239b93852e398c9870644962fc9cf019211aa00517787057e588d3c5978116292213503a0644e28cd0b14f5386c85acab1210b6655689484f4f1c9c4d8a39900f59aea30395a4e24c57b546015cda90c2d565e00a095212de82efbb23c26d316310de31f96bf6d5d9c3c2806c9245e532c52b668eb4ee762304b0068dd1399417951439697d3534d6c5c842b9ba11612471b5475790a7a9737a4415ca3c502c0434827b7bca3d64ee153f204b79724c12bc629b415830977018f59e7e0b403148879b7ae35a7164460cb529ab990305c46814e85b2d16b5401a2824b2ab036541d6f8f003ba46ec400374a84891fc4855f4b34b10d2762cfda505e6cb3bcaad2a75d479e1cdfdbe32e9465a577475006079bc9b9a786bef35be808342bd0723596bc49d30a301085c91bb74e80064999741b8419fe606c23c9f16706f670b37564754e8b1b03446544eff1a400e9758f8a9f1d8de9d3627042207f053bb3e402469f43c40b22ec73915f09c64195b81b3da2f1f9782606b290a5323e89549fbee62eb7171bd4500019c633cbdae56139867d1b6b85351bdbc31a6b90c58bd609cc1d6d273e30ebf2fe5606ef3f2df7dcc340f3a6064e9003cb30db3f9e9cd9eab2a104f342ff4386fe3737b86ddd48fbb0b060f80e69d7122b80ed4392b5667e33a51f96e377bde5ca9665ffaf472cf8d657c0692b360941f542f6a35caaefd452f314345ba69ba0a867055c9238b097b5a61aa6a3329ebbaba8799e276c9e184a73a7cf76e18cac99019269df4e78daba679dd6a757b8021c818c9467ad034d78583173aaf87b9b8e5ee8827168626cda2d3c60bf6c7ba7c8624c1a05c6e58c47d28d095b9b7771ad1456ab5dadb768404a0fdf049c4a1c19125cc3f145d6197d96a7c8a332245c2b3d428d21392ae007c398419ea5a6686dcd1cb8670422899d2abe00243fbaa84e5fc60ac9d30c743de3b6eb023e913227cde93e2693fe8351191b448180ec062a301ace4f38cc8bd3944844b10f213bddf6155d19ad9088f711e7a8876e35fd2e29a2990a6690dadc6a1e3ac1149cb78507cd851662ffbfb1c08403b588a2d6f44d99c8566621ad6b2bdad5564f0313d4e2eebd90f592e7860f1335b59a23eb5dbd9d8baa581decb225b1cc63f08593f50a10f600d3bcb50eb2e9504e1edabe1d08d0687082ab9900ae5c8a1e160398610526f363df5dab7189950a39e92d2531f45b0ed37dd828db740a4f14d22cbf4dda5cb7cafd87e18000ced6e5562ed9e1532ec6a1a8670891a9021e95fac2446b42e8da22ec492d7182182081837e4c57122c5c1d388f003ee3793027538212ce4a2b82013759a0b337ee2a54c8c0c7afa19efe9e155884aa8bd898609ce41f080be3b3dfbf36b369bb5b2b2c51f320050590d626efb7e3312621f92bd59f0c6e9912550b4d128d071690b0e4ea6eb8f11bbc1803ef6aa48724261666f45e89d7431eeed48b73a07231d6847383742263bcaccc8a6ed6c6ba037712b43fed3b1d05072b046b8e16f32148cbc465f6b2d588dd0d8dbd48aa07c0d19feaab7447ece5b47d9a7ed55f96709a7e33eeca4b3aed342659a62fb13d535f0b666440f04406e5acb3fb311c90662b2741e0d0f4e2897a71d45131f1677a40a6ddb1583b4d3f919b894225927c3a14892c4f4e5093100d42c4f4a0ab5ca55d3245a7a3470ce655cada6e929a55be9933312b4f2fb62af4c4c8c37b4288f11dec9a4de80c9cd5021e70f187414ebd179dc06571c240d04340a7bfc3a781ca49130925ed2902b39e89fc6705b6ebb04b6fef51903cb3c62207988b286086ad4e6187a31da1469dba416f0b8fdc4d4f36695ba52fc60e24aa8bd7856b500667b46c1a69d61511b0c4048e82b5a18370376a7c573045e7e816866784c3353a9019e3f291cd5f249c8847d884777ff01c96faea944c2263438d1215146044abd21b14e40d0dfd812d327c97f0eed4ab931a463fcfe9585416bd406251b9edafe8c8be67d64580d431391f1d44d5a086620f931412b72b1bf53f843cea451f77728a757bf134e2a45e4b1f2391bdbdc23f84392c42cefe190e2d5713d26e096e4bc9dd347e84340f9cc49c7a2b625ec8e9b1bc2680bcb33233eb61d123964981d4c368ecec267f5e071fdfff94d6470857c5ca3b8cbc66144fcf50a50272cc80ee5e64dcef06f66693987a8d8e0d0295952e0f0f5b28e7a4e7a0b6a0a967193b4fad4ba2cf99d8fc95f7b4c3b312afea2208dbc9e16dc09bd12399c826c4ae6edc740b0ef41139a4e84f418c323d7a7a8ad6b60dc2fd72583e0a6e8cb01dce1b4efc106344e35c31cf26303057461ac79c7020dea46608fae1ac7c983fc38cb2a578efceb4d2b0d15030328a5ac7b38e160aec93214be35d118792cd334cc8393268268db4e105868bb08dfb1b0011eeaebc73e265346f927933fb875944d927d33d529af404aca0c059d8e63a60b303f8048500dfd2553cd34f66601b3472378769ef5b1ebb21f5a38e76ec1f50ede56f900dada0cca4801cddf592b219ea283c29b3af4fea1a079d55597316f32a86a25cbc4635d011a5d64b6f23ea8f5409eca7b58ef0707def68a00d20758c91d1b94fc55cd5d71acad08ca7dec4353179b3e8b11764ce486735fd7651345e3320fd18e92397bcbc31bdffd19e61a328187962b930075a354d3b158d1766ebe1b43bed533830405376a9b22b1efcfa018a9277005c8749e17bb721ca564dc04c6eec9f0be40d42d860d3933baeea0b61831b22ade9e0d1446aabd7510b1ab97def9074996a14cc9ea791034c7918e3a481df819ecc9ed63d0e07e8eaa7af495ef3e1625694d2e5be05a8fc56e80f6b01ae17643e5d0fb3126eacf284ab2670e42a34ed7fe2418362b5b5038e968a45232fa2c1ad1e6843bd9a7e5bfa3955428e39ea4a4c5723d49862ef5f1744e0beda5c3b80a1f6852ca1d7d3a8ee504a88d382e6ea73905ec86ef2e982dbb6007f776a47b5a2cb85270686e541dd961212fa92d2bcef44033583cea38e8e29e696925a22ebe632a74baa25ce11013667de65fe1e7103f8b12c3e91a7cad7a5db7ca482567d44efd5fc78d60c5f9b0bdd07f34192a77c615e67934642d9b4e38310b0c8e1d093e4f326b40e294279721d62e04a84a04c7ff53ac998230c4218982282a97c75206d906612534ac0fd131ec70451cf8b783d67096979efe8e44902f708781c99516e0bbbd11a30bb22f4d11f227c4c0df30af8bc00276f7f9549d1604e98c96831b17849507618ce77051efe4c24e786f0a81e5e7d4f0898eb7601eb0a14ff70f55f05f36955bfdf43ccbd7fc89fae38bfff9461422439750fc3b7234209146d3d3987b38c7d10e3b7daa0c784740b7e2278796f4c601484c371cce4c80f68c2256bf023fd8e6ed1989b7f1d73d2a57fb068147af27e734a6a88125aa443dc72fac578bf5260508632d98573cf59c5b3ed8f4dbe92813b74b8bc9325c1b482a91d476d19b10dab7e3b6465888d0430312d29d835ab00e27fa04d0208f115be953a34866a3f240bd66cc2c1c81e50a40c92968cff5717eb60794263ea168ea22e389b9e5389d536316138eb9c02531367d7600df68f1632e19a11f227a58e57bf2d84e8b569c61c59990396506b90084f9c06260af5eecb49c63acb5f7dc0e4444528c2d9bddd3d1e33f124158e1cd2c58b96777f8c1168f0b85b9febc1702605e3a499baf2cf351cbd07e1c5f94275c051cfab8a3e3232ad7ecd86646de5754a5a3a5f2e86e058abd5162662d3b4bee23e2c2004c041a1eb39cd8806efdc9201a43f548c992576aa54f5d90f9e66a9aa17e247e2679882ea0798ff380572c00fec21af7c0a0e8daf2c7d4839091076a371d8d24c5ffd582407e6df38beab4ece57fdbfb31500a962a00f01ef5cc0ca27994d9c923e900dc3dc2b5c9f3188d97dc3a9926bbdaf85d2c24a3bdf7e5d695289c97fd8168dae9e3166e93bfb02a004832e278276d4b26df81c1016208dbd8ad55d733b687099f1f347ad743f6aff180955d2bb299456b7dd43d5593f22fb4919a7274079fcff230dcca1e4e015e5867e7f5f1bb9c6bfc918141d9d02e45f58c227e8774e42a7a6d96a60e71e733529c8c1e58f17b738e44877cc5c6437ee471cd2c181c9c238723cce548e4b849275c0253553cc914ed761dcbe39ce211d62664240a267a239a349e6c99facd774d7fdf3ac5a0016574d6976f3b5323de118bdb174df7437b3586195438afd67fa865f434aec9e4563dbec25104933d9655c48e342a95445fed0ecb538bc727bb24bb927eb003a8075d99dc18125235620ee2519023522e492dc5af265b4ce5ffe5acbbb1e0ee76cee130714cf61456150749460efd59746f91b2e543c61e9ec6120680f5895f35b57fb7adceddec3545e83123c3018fa694c7d26bb9e13cbae2bf43a2e3b999f19d91ca9204c19c6c3025d56233ec6624696a3836789cd0759d578c4544e08f2a77248d589b22f4ae62f3843d53f4af18179b0bd56161e2f98e77874c9814b45f2ee30971e973ad0ba71275b50ab2538ee64db95b397bc4bf705ab860d1e6138468c6ae9c67be20f4c6db1d33916ab0a56a4136f8366cd1a0f169cf867fb5ee96c4ec2d658d71c100e827aa1d09c0f6adb8ac9c3ba80808a484e6c8581c1b2d4a271edd1bffc49d5c874aec714b2ae82f1a3e6d681c48f6eb7c7597d79b4f19bbedc13d2c6ddaa5465ae3743005091769e29e249418b7dfcd0058495cd9befbcd32376efc456aed878179bc5d8d912aa05ba8d300bc8fd24a968948d3b63cbd2f46046b21ca5d36298c91b066596e46520a492cca33f7acb0d6407dbdf9cd4dc24e00efa41d6432a92edd42b2be6ef9e7a34a80909098c16b410a62c486107257604c8759edff6b84c55d66b4d03048127aae9c76791223be2ad699ea4acfa4a7cc951badea33bde8afcce650d5270c2ccd99042fb9084faf246a5fe899363d122b194cfa1282ee5a49ec6df333bf245eb0609cf3ae7f90a23b79b6a67f4df342d276a980a8b377afd33464b1281e52868a96369994b0c8e09bdd1681e2f31d68b4a2aa460dbc3bc085f7dc48df5da360816b183a4d0646ee439701a1131303cd8c773ea5ce1ccd04d36bdbde8e1788d1e9685244b72582d026b39618095126c19080be15fce0f094b4c08e8f0aa0e0576836a2bfee4a312a124885a9c69f4e393f07a1f3d37f78e3a1922bd9c33dca5d11297349e2e9f471d681a1518339e47a35015d1da764e504229224ce19debdcee14c4afa5044e4a702329e760cc849294e1d50ff8d1cd80d68d49b5285545ac57a5aa75022c07e5512a4b680faf4e1ee34f78ab7cd6cae8eaee3037ae42402b1551a8abab14c2a82eb5377a3077ba9cf2ecb0ac3ac124ca53ffa53d66a719107eb71b681e179ba95d7796b85c6d2576d744eccbe48f809453a1fe64d496f2119cec4ed8e306bf8ebc2025a1f84f909b9f280a52eff136b01706119446017fa495cf8645f08c20a380d8a8c3515714bf3afd08d883d2ca2a67c4bc373a2ea0377b0451f7a0c019e1b84deb4f1f226836899b0ee6a87d5bede116df6b6fc9dc3f99ca8e8a5475f1f8dec1d4b148030c6100954dd6927f3d8c5730c2e10e240a82733efe0ac2639afe2e2d254f777c61941504fd6b79e26663bbb440ab499e7215215353af2c37eda74e7b46b407f4ad0fa4fc4e590d77887a0a4c04b5f71ec01bed301201df72da641231537f840bca6115fffb40d6336c58583889f7b4ccae6b524fb04c40a17d49d8218a7ea3fb903efddb36ea5eecaa5f66da07b1f63b702100fa830f89be71b031caac7e2a4b473b740d8ee5d27dbeb984dfce78b9969aeedf1782ff530aff7646c8c49d72223ed325a15a5e51363c9e8977c5f10ae14c199e04c7ba799a6d23041ddfae3d51b2de48495511ef6af825a887650b5f95b3bf684a91902171ffaf77a1049ca794a9c800c3779bc775a130605902f233606e5b0b47dbb324f5fc25402dd1d61588ceb27aecd9e63edfb48bfde2ef6d4a85d832d307999115c90c5e644864420374dbd52718fad91418d8fa87d64b8a22732c1d9c64dc21a659b07cfc971d4b193185c5b8c7f638f3d0d2d1f672ad63f9f83d776731b7dd2c8cb5cccbe1d38d236d423c4de166f895d6531f698f5eeaeface4efda182b7e78d23ba768fdac92f75a41420ca774c40631b5908663bb8d579415111933f9305bc4b4a52a0de507de82aecaeb1b950498b3285172fcf5890bab185636227dfacd9d7f154ea7d633d7d91f40a799e73f9975a8ecca980c0cc3631438f3e0cdc7931b1923a418aa984636026f7d7c4f97d1de6dee28ef98fcbbdc0241816d5f58cb053ca1116c71724c9f923b69e6a7ab97db61300b4ee15cc1090e4836357993f23ea6ef6cd280bc7f44ca05f8d2b566672aac59097bf0e3f56b519a6eca4269b0e8992383c9b585f308d0cb45248f96644abfdc141d7888d4cd6b1a3a26565034213e2214a28d8e5bcbb14ffffb4cae91fd1877a74eb9e9140ea6a35fc7669111c3cdd7a136d4f57d56d3696bdd8253f8d3c1e72139fc2823af8ec19b658056ad977d69dc3f6bf754ef30ac06bb8d5d32ca5f13e3206b147cf93639b19cbef251e962cef17df524ceb13945665dfe25f8f6a88c461041c7775ed2a01c2b61a07665607c031e628a1227d83b7a051d9de66bca220ee85ff587e201b72b92e4d5c47f9d1a8dcb84d276b7f4deb7475301cb2304c7d053753773d411e5702757c30a8939520723b46eebc140017b3544240498fa884f108baccda198c470d0f650d1bb0908b283bf90342dc4ae9fc4dbde918e80547fae1be892f85d27887e3bcb368ee9a94acc076183bfa405c1f156391a984d9c10183f9f83183c7cd024f00aa9ef079ed1a163705cff162ea1e7f937f610b058c405df6e7d5ee4a6fa3390d14840986e134b1553b4af2f9dd166fd319858d06927ffa7a9588d59a1aea55a312dff8186fbf448c14b380917bf14fb03adbb5038d8cf71dc13c12221ed5b430013d57598a0317e4f22793f88ead921b842ba3e60c72e18ace1ece83d4dd7c7ae953ae93ee77e75b53d530e8ac30f0d9e051a463ff12a2fdd5aa4daf8647fea2c09962af57751ac2f90f8cd774dd392c6b21924f7876b6e2975bbad1075e145ede5667ee9c75d0e1008a7840fe87a1deb2e1be3eaa64d6eb0198ab9faba9abe275efb4e278289b222edd1475cdee46beefa5ab5a2b1d710fbeed944e9a5d0a11db7672b48b09b5612e9e603c0b3baf0ec9e8568046e2cfa15c73b0e2de271c82340abbb6ae3cba3e01e63cfe6b56a589d027dd669cb0be8c4750627378cf8220a4968383e368cbb12574a51860668eeb6384a4de77a8f97771475f6acedd6cb1086e51efda7227f5a80fe04d286fd7ff76d1ccf050aecae032c92409124423f060c74fc0692bd35d52da49b57550f0b7c7e64baff873e1a8caa4f81268fafbf9d8df631bee48d6c51954735b9da8fada40807550a4b43ac0b6e1c3c0edb25fd5571407da8203b39848d2d4a2c265bd977234843dc161219ed9ffaf7a863668e42679c3de73e7c670475a350a658a03873d009ecd94bd784de56295c5ac56820eab984a7b19011ec0ab2d92c4d0f42476ca542e0a591e9b678b8a9396894c0acf4a063be5e30dba0678f5839e481c21d4e627e17731eaf1e6efa4cddaa39bb751928ee183df79c1f70f4d890067ca68f2751411b0c08cf782c4170efbdbae06438e86589ed728c7801ad9161f488101d4d9ed0c89adf43f9fb1ac2f53c330880f253a7474d410275db58195b2550f73904a3cb6147c40b6a8c4919fe24efa5ed65ec972079c16885b781488ee4a6c60b059e6632d669b6fe37bb466ef6736a2a21c3b988254140f76f2451f1ef4cbdf4c8162b822d6ea902dd90309c504b061d1c288be83740cc0ca3be395ef500fe64bb743917840b0c3c25af12ebf138ff1ceaf07127ce54bc7cfce7bbb412ffdffe07e9aa7ae74ecb259dead979a31c584fd85c630d3ee18af68d0666e7be58601edfded8567e913b67c2d6c636ac56101d20811198c72498f3b30b0d7201da1cb1bd74e12e3ada36d2b295dc3f95a31fffacb3485eb16b6b19570254a027d65652144471e17329503a65c54394925c8d13c3759c89299d5cb2ebd05e4a0d89de502a2d7283a7f5e66ba07d36d895f5829176dda34c0a50b0c608fd009b9a05a99abb7423a73afb1240b9c18ecfecccd43b7cda689b8773e6ba175af5870d3d681d77a391894cc3990708c667c12e5e05b79fa8397f787400545422725682a5bc596633e512e629a48a59c262cc8a650e0695bc415e100bfdd153028c9ae5028346563703008d480963c4bebe6f33d2b41d6a91ff196f631a3aa63f39e583f2b9b92f6c681b7d690563f05c9a16500ea1e420d130a2553fa188cae5a9d8799f7f7ccc2d124215f6904feb436f4f982dfa026da733a0a5e82be81c0e2af7af66ead25d607fb7e62c2508f9a1476689b2707879e3ad950ab54a15afd2a81026f5fc5f8cbf3106203938775f3ada0fbb37d30094a38f1523ef583da1a00d70533e1dd5c466d9ebe15435befd67aafe1eba8cf83d673822c0bb62e9a050b717d6495103c62175a7740bcdcc48347a2d26bce2a81aa97144f154fbe944409236c4311ba0893f360758cdc7fcc131aa7a9b1b9df86ef067035101e06ad31083583709cd2c058fe1248dd11b90830c1e63c7aac12e52b35ce27e9af98115b46acdec3058b668e08466038a873e27a4ef2108b73ecaa82393d9f75c49dc32f62a87c75c43de943209045abb8f856e1db1847840c651ddbbe6e0388421990f12a84ab19642bf50f1356dd507ed4bde651c94b855b8cde74c83e643cacc64ecf650de1a2eaffc4da1f21cea508cb58ceed0dc0a3e907f2aaa42677120655f0b341827b13820c29358344a883718f308733380a66a80ae5a6b341ec72ed5f40fcb557081163cf9b13c1b315aa55880836f4b99812f0e68fea9367b46bd07b999f3b912daf7a339801c305775562829ab9dfe75405d3a6797416603ee871c06084c37c2e00ad23ee22bf88c2b18e8e84fb6de444ef126b52e9d1bf31ad0c9448d1202d12bdabb3037e7898a831134b69e71521ab6f4661648228f7487cf590e7c382a8050088c956a6459532f25ffcac2ed4e95c8d25ab29745d31e07c7ed658bd7730de92baee1a60a096e2348c025f7600495f912418f63a07da8fb70beb82cada9dbd349c5984ac7625faed4838895e2d41808e28caff7ef4cf86345e62e1bc603dfdbdec209db8368fcaef1d40b16a6465c7e899d7af5a82195eb6e75c827c8ad82ef025d60d42ac0750a292548c60a5d20b5bfaeb242c77cd51f627dd97386672a244814cc740b63fc2b0e3a8b09f552c361765b667f29ce57cd6871298271bc963a7316b5c9c61cbc42675349b1dd4e41361b787136841985877972a07a114578defe7f7308c400c98d9f168afe00248f994ce0de1a69ea1dc796c65207999ed5e5e2f46809b5cce14eb8225e0392fc9ac56c59df046e9ee43bb48475eda52b1d4d50457d42e74a21f1cb5e44f00b79f302bae404e4511c8645af627de7a4e1657276dbda0c08ee8a5b271e2c8915dca7928da052b8adb91601a447f62498778ac0d8a4d2e340dae7b3d42af6621b522c254b5168fc40d70d18ac7842588cfa4e154a14d4161156289c42e07908fd27a41947ebecdd699cce403124a217f2889b617ee13e5f1f322e75dd5ce9c8844ea54c62a7fcf2085dc036a86e022fdfd99f737f9e189397defda94f314321778b57702514a0c311059a2e562d4603bafee8b175ed2e8b0505a63c8057e15aa88178901b8f1108ba14d616092f216e16a3981d860ae2cadb02c059149d28f6606ab7260aedeef4ba6f22277f5d15732072230494da8128e9391452aac2a1532fe1a8a50d0c6f507b8d4c738dd21417f031e4834dd90d4e70cbc7bc5339ddebbec1896521b93e61b13f761ba6c5a4209907259a53cc6a52cd9427030e0b5392d1a3fb8e51fa417d1d36c81a441923d2d75319fa4688976d9be2c2da5152928718889851c69044b39023cdc24af22747886f3cdd53a18c4bff3f93a41400bc1124224147ccbdc5536f91ed871d01e441f37a804bd5db8c8343acd0dd13619f35852e85c3c164b53065c4c0295f184919596a29420e1ed1bc1c70d60cd219a17a10f790d60c12c011caf98fd19d414cd03689461244fd708004119235f1b00f55f82e15aaf9f54d6bfa3e94640b8aa6048224498cd44115edd09f843956ec687e9fedd40961defc72e69bac3fd721a00f25278466e33e0a6f8d81b18989e1691c1cbc9ff94fc1297cdfdbddc3235a1e778f82e16835f897f92468149e0c279e95366879c905ccee73832077975d2ed4bce8fc78f929d24c2b3264a83ba6608e9b43aa767988bd9766eace2532bd6953b50d03b304a81fd37ea46e61b2b946acf425dca5df0a5e0de216c1faac1a111af6c415879c2e9117b2b2bbaab9834efc2e91221ab8bec3b6661cdd80da292586f6bf96fccd737388b5e9f22d07fe93a0d3ba39db2ccc3fddc32eccf25841fbf7c72d9f98147321aa88d756841ebb94a10f9dada772733dd9e6d1d83c95cc938f48fb6d4e24a786c242a362a86b328bf38336f6874a88dc3080ccd709d35f73c424d1e93e45ef1da936aab38fabc24378995d5020073aa15c7aa8d8ad9eb809a9c86e75b6e8de6c404bed909bad8f02644b600c6191730d8c66736c8d33a5f7b212794595645add1d6cfd097d5f24ac338523d82c63bd8f4075836729c30a56d2435d9be74e1148d3173e5d8365fbd78ee860b9654b139868f3b1ff46e82c36f64268b7c262a82657ab9bb7d1c94098ee4a594620d78e1bb8d5442465ccb084c6267b029ddd622603fb384207cb2fc9ebe4c9bf50240958b18d66a74ea7cca8655db2b42d278c487a5f6057d188c7acd288bea742ebc5d8803eb55a291b82057045fa8ab0dcd069076eb8299a13c183828e41e11f802c0f4dd30d8ee05f4051bf9d7ef18572714ecba122779b5dc9f31d3f453150fffe19d75873457346bb74c2b5ca9e582ba15b772c540c40cd6c706dd27823c65471115dc4263710876ac54e33b98adca2b041d405604c12bc5332d6be844ae18b283aec4e566dbd78be5c6ab3b141027dcb50ecd0af2c8c8817ed0eb78a828f09893000b36b1d93f68104d65ccbcb53a7c6d668d3081d23f43ebea64cba4c21b3cd358f5ee611a5bfd9a6ead29794c610abbb3ab7f289b679697b04524042e7532bb5bb95a8371a0ba5e620380eb14b44840234cd23f53c244e347e67e82c9ef54da146f45ce0b66d70871f004868a2c8b37a4a1385b7bd00aecc13f80f6e6943efa7f70c42e0473730ea9451ba4aaaed6abd77606d23978cdb29dfd0e21a40fa225144a281c617eb944fddffd253b6b9d0379f30d00c022e79dea43b80b9347874fdcec6dd52dad7238488fa2e69cc800018439f2e982a96d94333a50f0d6be0400aa67364b7153d7ef075c1920076aab8add39cf984327e6690168a093c1db58905bea15250c4ace254c4b1e98c0f827b79a02f165230354238f8d8d8aa0d2e12526134fc7281344052a3d6a2add14b0fdcf01d74233f6806bf74631a0623a4a0327265099232816a48b384d824660a8e5938c17296744eba66dc3d0a1dc5c8e9a4ab84226b0d77547548b5ca2ae8ccb53d045f36b1685b5a3bcaa3511a4586f5121469ec3ee4ef0965eb5f4ce2dfd41e4d37ae40ac33c22589ea7e978d99bf7d9d65497bb6b3a94778086e948e698ca3ca26cfe8f55f5e5ef5f5aac00e246b7674af354d909300a949e2ac19811a08c4f30ebb689fa45572691b07ed980c7ab8a94d541f1dc831aecc4df51c7ff2878efc291eabd08c3ab23783f584a822bd00dd46936b19029784cb23ed372cab7869fc3970b0a74700fde5b01523f0838bdcf5618f25f5d1e1caba30226d8dd16b885a19000db54287da200487eff2d0a6fe3582cc692d3834d75fecd02837dbfae21bd426d1398508f0b253e3f4b00564de73099edb942595b6284915dfe3748ace0c95dc050937ece727b55f94320897e98237eb859452c3f4180b63f4c96b3f02a3db082f63d34c9800a11226924f4eabe04f08fd4c8c856502f2f15d2a71a22e31ae3d91e29242d525d9043f34d1dac6b4e1baa136876751bcb9cf7c316aeb1c6d0fe3a12db82af6fe0f8679b62eae890f57c60e90f8a0974ecb5c7bc3788aa680bdcab6d41a1806feb1665b5ed556a993c2568952991e51049d017b841a85e655b83ecec949f78aaa30a693dece33fe390df3d8a7625b691a3f5d702d08ccd2ff54250487f32fe60a812c5aed811b70c891b1bb1b555d590eac65ce4f89ef2a26c525a35189b298c0e5c23b294ad6c7ec0c7b959abd2389c5d4674af11400f3a5c25aad03a7944430cf2f6e82b8b53eed795cdffb353491e8be71d4c26458b2a1080b461ab2ac0615b00a65faaeea89c46e20e24a1db39bc0b2ef10e88d79db614b2c9654d67a9b43a8d16b774075b85b96e52b564173a99c4e408db680c7c133065be5e3c3be3482cd16bd0210beccc2d5eeb53f2cf0d6dc4114d833345d8405ed33a874ad01cb90d100842f7766bec799dc69d85e56b32a448bbc7161d227a54fe7fa512ebab42aeb8a73d4ac7c2af94092043e2d10cacdf870c122873465e34582f1bd72e62fa5a5434464cef01d9e9b8116c09e7cf4642e1640793fc6da6f5ad3c2fb4baf8abed5c41d6976c9938e0d15c8e0d0a65eb6f596c6c7f2d0d95cfe18af3cda40e2c10c062fa969ed71a6b7e801edc0ff7fce7a8d184ec19765c6811aa94819c12ca9d4a678b891c028d1317e21384b613694c301bbcfa1d2a013aea974ecda0c0ae5956a9deb3e48aa17483aabf32032a476a3442f3476846bfc33a91780abcb703b2309a49fe88f8797437b622bdad31c2ed303444020533c081cbdd5545f113c3b4ae519e7946658afd4873c877593ae323b27cb0618514a53ffcb10ad92045c6269eb287303176b09759cbb9aa4f98668c5195f1e49d5ebe7b7c4eb9915518c3fa8bdb162f9b16d66dc86af472e821656c14f01bbb2961d2e4431cb8cb2c779ac78830bfa41f433468216a5433a93eb056eb8bdffbc27fb583b998ca2f6329405057cfd8c1edbf8891ec7a46d13fb960b69a4b59b29badd2d16061d398b61b4771fca8e4faf18c47b75f9be986a8061868823ea913b6caed3ec07f43e40a644139fa6b83a6a26e61319857eaa9a00517b94c3d2a6236b7137bbeab012d0242ee2f45f24b43a9aa3368b43116925e48e54ef996c06447ad5ba0ae5543d4d8cdac86465d05f10ef55e3e99072c8b0f78c6429b742fdfab56dcb6c1ba5000b0a605a562674d988bdd04e90d708afb8845a594a68405850a1e27303a58d91e74b19494a8f6694f1429587a9e0d61c643126aa9b4d825d1e5a1ec9c6f164a95950cc0a6e53a7206647c8aeb721d742971a220fdd91260d4777f607a572ea2a16f9f3c597892dddf8695fcbb846605d297171640e4ac1e19177c27746f8902a76dd83d3d1eccb7715bb3a2846dde6ae88c03e6643c743e0bf890dfade86e829f4d9c04922ffd911c2aa8f971de5ada151a3def8253668752ce44868367c3520102bf03c6a1a941500e50f351ce754efbba0905c8f42801dc01a2283db4158658ca9a2fd2108cf0c945854a434e024830967aba3afc5d29770413a0650b2e32889522d4b6ab679c5e83b5b47dcfd9a3f7d7ea856102616352e1ac18be68c92ee91a19c7118aa7f9c026ba883f863585ad7be4deb5c6487812aafd39f041cd70135ea81174ea7ece05848b63098923cfe8a7b0992c3421cfa36b81828a9626e935c390ae242d2ff9ddb82ce8febee8d19ac5807d9315a6a978c72c803b76cc2dad40165c838a3a5b03e4367f2607d3919f9b4c27e1d2be02d55e2cfdd778012cc155ff48110d2bd37e79d57df4e761a526d90ac8b5e413c3db1d8b97d9a1681e77f31087d7417d447b2aa67a79081892556495b60cf1acafd31ac4a774451053140847893a03dd11d9991a09372753b9dcb9f6e17783385d24ef4f76bb9591fcf6a67b80c760cec09eecf1297486d1ca741379f6f2ea7ff53780636a25c93b786587f33e9f956bd150c7b84fbdf3f67ba8bb320e63c79de91f659528e1215a82b88ff32e0558dd169800acdb42b5bc93b7f228e59239617bff0cfe8f58344e258b5da193fe01057c2409ff20c16bb976a3cf61176fa9726b1d3b18bf9ad13f49c493ab1d9bd6c7be9690b9b181afa2c9efa0980b2b9516ec5043a345743d421c50e30e120c29699ee1bbf3b7184714affe8c587a9c9bfee8ce071b2e4ec810701d87a0e2ce8128ed8b7b887db9526c2462afd01ed304b2bf9c8139b99352fe083f875a6551f8bb12c4f3f4fa27ab49a08f3117eac2b59b5233a0c09761fb9bd76b73701ac3d7bdbf074b403d720f647f1e3db74f7834fea207a7a504415c1c71af272ecd3997203c389a61d79e21908a48b2d863eb9befb7e69efeb87433e2400a58bee8bebb8e58ca233fb0148143b6677d2999e5929ef14051d1e1e7c443b67ac67baadd824caecab060c1b4e3c8b1db00c0228442d54fe0d10254503a8af353311d3db2618d95fe80b49f6497094830823c2971829adef49ac5899b9c4eec0c078fc1042b69122d9c41fd2641a846c6db07fca5adfcc831b22e3321ef23481f7ca719abc4007f864c7e456e349f9f2aa6cd6bf526e6c81a8315e19285e138133ce95c6f9be713cb1de9954ea7d1d1ed17dad741a3ab1e4ce4c494a5a46bc49b338eb8cd515f647e8b45b38d2f4b21857aa49c3422ac9f9fd45daaed04a5a643da5ff6daa089415088127c103ecbe2d2500645b659f00f67faf3915a284b790ba1fc9e94ca8b871a9d908dc4b064401b3989abed899a923af85f7f1b6996b16069c7cdf6323cd423652536fbf1fc3276ed536fdee007efa68b2f1f993888289f456f019fc4ec0e212f646db5a0a30dc333144081729a84014ebab2487b59b39ae2d0db916f52289a9a2830a9d7fe7e3390b549f4569242347047457ca11d962b843ab8b667d8fa1687c3d6076e3d55bb4688ea4e08631eab2b8811de89537a2a1c8ad20b4e0db06002ad69405dd9f841a2e988c2a845f55861d818751bfd29a6be6c781f28b448944e0883934a7cc94f21dd66be0a32228e06e426578d155c28f05b86b99f07abfb1e27203152eddee0111a8831bd4d48b036e67efb6825ce574711abf852a79779d2a0ee66c184c71dc8d47cc97947bdcc6e171fb39ddc6d1edd595d0006e14d819d52056015d6297d01e72974a99d745931e2f77c97c074d6fecd37a86c90840d09f2f689f6466a239df97f0e91f0fdf0ca5d280fbd467946e0e53eb022894123778d445b1573cdd8347f79f84ba589ab572ec48ce8813fcc96b642ced14c5dec72478bb5ada5ea0c0146e5ab6dcb8c52f25a7f3a4f2815e92c30ae261cde4ce5bb8c2e34447a201d4fe02eae2ed605c409ad5a80391287bc7c5798d7714e401deb06797db6e7925d587080e40d663e75ca95fa03fb112be10fa0015383c701e80ca6a561b4c0f18276af321426904bd10b9e1eb051c0bd8d8884dd7b21ab7ad1b0afa7befbda59452ca9452e305ea051006a51864fe731c7ac6f41c95bd44038c85e5015aeeffb47f77d75a25fb1d1f489794eeeefeffff2fdd9dcaf8546e0fe4bf972ff50aec1555def43db40c39ae050c0aa4bc892e1c874be154c000c9c6a08d2c5d86c749f4f7ce928ec3857b129ffede657e97ab4731cb8a45ae6b72b9b35a49fe5be17757af48e1c66843f67277f74829a574d24a5d2e4a2baba3426175f7f49769c39c37ce39e7b451058ce161261f89c8708cd1d6ea6ad9d82550faa1b91f1b204b97bbe7cb7394329e4cac514a29a59452fbb1d6c6750383ed943a854350324958ab192435dae9f47af9f0f17af978bd4e70a799fc04725493a6cc1da849dd699ec7f8a7699aa6d96072291317f453c560bfeca95d068a1d04011baad0349c1be5fc34edd33e2d6e1cd79d949aa66958761cd7b5eacdab575cdb7346acb56ddb79bc906580dadd3d5bdbb96240d3344d8705798e316a37af50436559966daf18ea8675ac962a9d124722381b5db19565ff5b94b46656a3275b29fd416128901896099918b5d65a6badb5d65abdbb31fe27b1b07c48598531d231606a101da230981eb990bf2e5191c8f588fc55487e901317ac3035c56d35aa28cc5a0af328ac1ae9950f22085889e4c715a657de460ffcc4af307e7a3589c1c0cc891303d69594006126bb90dc4a8e009244c5e292b8deaa69ff884da50a503c737f8288b05185abc2750cb2b0587294910583d85d473e2aaf2ef7d8b7746d526115e7c7a9645f1ebb6c1ae2e4344cca584819066b70336eada2e197420c866022ede032c0990214a42dcb32cfc551499076682594206d5e55bd011e8334726b9ec8eb4a84525d315cd654b518da70784fd956575dd5550c6dde0cc75d65c550e6a1e00d5f20c39da78386b3ccc33a702758f36644970e67de8ca8e11d19d670c49ae752247a3b34bc792e1cd6b0cb8633cc038907128a37c33d22527093507093e49c33a6802ce882a407de7ca3baa487f10f68d00322b2baeaebe63f5786357cd2552c8aa1ba44ab7c6d227f6b06c77f10b1adab276e75497aefe703e22cab0b042c59d60a035557758962a84ad12a8f63855920f55561b6d43d0358b7c85fb5901f572dda00df43ab7cfe01b4252547bce41ab995ccd4e4ef26ae38eed180be28acbaae0056d72bf3aa6b555934d5555759c6f8d9638cd1bb1a220e1dcb32e2665557757d54f5655411058d124ab477d7dafd9193d65a6badda56419f5e61722d0cad1be6c032418d62cafaa2e223274bad2c95a5feff9665b6daf48aeba657b61c2931b6165b6cb1f50c5a1b8638b6c8089190d2211485c31035048761ab421820380c8f28f19ac2860a940f374a9e9327750a87a0643c493c2ccf8c0789a766c6621fb3d6da4a842049721321c8910cda88e5c703c84d84172df24639dba30f7986bcca873c31aff223cf0c7aa5717882b40c8f97dc5a73f4cad3bcfe7b1ae6033e77af484cbf75f3ef69f21e9cfc9e9c5674d17ce84f9e9afcd8e341f22daae841c5d0a3b60c7a5627d5c92373be257bd77f364e5ce93decbf26fffd0b497b4ce096c13fc26342797e6471c14e2c3f36e5ff1e4aaa10c6f47202d235bd9c203bee1021484d7ecf4b7e3caf70c18fc970c17e3dcdab3cf6965cf02fcb649a3039a6794ef13da799e839615392fbb1ee870b624f901eb95083ab0265f796f64f08c7417a947be5f1c7885b44a87d881b4b2ecda05d72a58c1f4f72c462b177f7e1810e79847095c7465e45e47fcf1057642f7dce7f9f1117c4d373724fe88961e2e6c83df204e995c71e213ccf05173a1dcf096c95c704cf29c733c413e339b9e0829e537ecfc913c4c6a9479e538fa2c788878847088ed55aab794e9e53ad1aab6573a334438ab0a55ad1b06a9ac460ae9b1ca9524999a954488098206a62091aa9e4688852dd56a9181071740aa243c218c965a0263920e28a23c8e20f48ec33c6383d50464c2d06d95cad4767a28aaa5c45a9ca3d15953193524a29a59459a4319f4a35274e8cf0a4071307f7e8df5e8c414cdcd851344522514d0b268b3044c9a48ed0b458aa2436340c3ffe89a1fe7fb9d26fbcdc9a29a594de78b99fff692afa904c49a4a14c0e0c55dc99f31f8abaa4332d28e7a2a78473896ea1418366a301040dd3b7747fd9a3f2636b61f6644ff64461fe63d22df405f4b45141611c10eca99322c31486be809e605a06e387fc2ea480b970d9130c130d130d53b455740846134d609cb11229214e0961bc8070412c6154d1a1b9048cd74b0a29329812b66c22bb30bd186385e178ac172bd8b6fff7d831c6f8b2a59432ce3925e794d2a6542aa1d1755dd7755dd7755dd779776771412ec6487e5a840a1da2e911fd69595c777794d89568c86e725c57a251ea5177c487f010d7f21e3ac4a56257ead1d6d17c97a443928dc8ad2238969055105510f9b919eef4046a0a7ac2c5c48e6b3dee4aa518e9293b0dd77b4a111ca4b4aa9b870d12b8a173e89b969ee3c6b0aa1cdf42f3e3067c8baa472277c7d9bf195c10638cb12a07a529e1cab562d5b46cbea54b316f22bdbb29f0a4cac2f27d9f8fc55f8cd1629b95af56d25733477ce856ab54d68e44b77ceabfe741df3c91fe7870c36435235c7554891e635cad8ca8950a57fe67040d624ed4b4629c38f243ad75d6e926937b2a954aa552296befc5988525155958be0fe3d4939ce4e22b204750915c7658c0165168cd10840d664845b6090312624effcd4f93d9c15f7a618a16d7dad343d049f2071c73b806addc4a6ab09221b7921a9cb2b5f7726ebad6e4a6525391a7e76f8a214fb9d46341ee973c4789c77a74d25590bdd703efc77aa03df170afc0bcc3388cc37420b046ae1ef844f28314c7a961cf167c21bbbb3b92ec33d9793892dd9b38c75710183b498ed55f872213ebd0c12eb00e2fb007e31c3dea0fc201261d5ce00ed6c1835fe0f9c1ee4937c3f1053a9e0e76817774b00bece10149ececcb3caa5768c82235c387fb30ffe33f1894dc09ea8040d04972054012a2482249c57ffa10f4a68e2ad905a8495af664dec7e4c080849baa6f7a98ffe1c3ed615b079d73ce393bd8dacedfaf910333e79c73ce39e79c73ce39e79ccf596b571f25a66bfb2d8a1e5edfa2a3b3a524570c3dfd21db25b407568eb4ae578f2c7d9939f959516536a1ae2ab6d56a654fe6a65d8a330f6f17f45df70551d206d2d1d95e942edbb89b65d9666f96659b3d4141b15dc6655ba6651967d3bd0b2ba09cca289d3aa595b39cecba1a6426a3a454461965cc3ccf6f79bc69b6d26cca8c3b792fa3f9b06285c56882c572bec5ed4b0c2ab440dabf0fe38f606461f9be0f0c6691fc27a1f9c4fce748b63243f2e37a4587aaaa613ddaa0901f8683f8fcd08316f9714da27bcb60555d257ca0a854e4ff64e79ccbe1cab8ef362c419ad6d530abd5b68d8b31c69883d66a6d9669dac6495047931fab5a824eba1a8ee4c71b4aa52853a2c4f7d6522742dccbfd17da568f3a57d573a8c8faacae2acf54550ca56c9e8af3a4a7bab6c634b9d103fd64edbd61a636c200c5052b0cd65e8555588555558515d1be448c31aeac75555401f2211af32a4ee9902119ec9a0c76120e6f58c399c58f5d66a6c22aacebf08d185868102120073db1b897c10407ee2d1bd74d8d8216396fc3e67a7e520e1fae8fc8c272ef09c340c53d593f9de20fee639b75a7b48c06e9a0a753d7823967d3b4fb9453e2195544356070c1cfe7856f89f88bb9d323a27e1fcc56515da6ee0f1fc829e6886ae6653e8994524a6982910136b1c820b7c96472af2e2ca6097e9c604a2f31db83a0031cd040cbf0787ac516474c452c5fd5e6cc660575c0c77f593b915939eb55077ab4ab35d0a31e5adc2bacbb7b945276d5d5a3d6964d97046ff7e8b1c583526ed3325b7970adada5b5b2966dd5d61137c618638c91566db3d9e65acc6ca51edd7d4e96f683e973f66cb7271db769993de9b84dcb5e10c29552246adf5858b48ea974304d7386261820a79823aa19948cc964ed49c76d5a66bd136b3b8c33185ed7d4d1bf552aaa39e79c36e33a6deb26b7b1b068df77a7e7eed564aa157fb11cfa9b8d1530345840028bfdffffff7bc360dddac6757d3d1c442d82c144b0cd419d7190917110c5fe9e845940162a8289449d5807e7bf4eeb820eeb74581d5686c17a1471e87fc07a94809b57f1265c1160b01ae68c891130b1e72ab13bcc6f608e82cdf8018ac91019f419972e4460060cfca1ad154d510b8d23c38fb72db6184c0c6d2f1ac79179db924d8b6ed99a606d31cddb9ca06185ac502b820a331b86a1a665295a66b5101566360c434d0b516166c330d4ee546d35ecb84e66f1862bde3aa0efba29a903e9e8ac69b893828241ae07e1d6852761270cc36ddb3aad1386e1b66d9d7653523494ed64eb366edbb8cbba41f9185488a172c54af5a5c6b85cf7ef73a30ed14cca596b5dc9d4d52329e8148ec3f127e602a93f3e39f25b7241ea72ff78d405801ca04008352512484bf9fd6689bfa547153beeafa019eb15ea955425e97add30814e5829c6e93e61f2c18f0a68e203cc405dd445a965d5c4506db149f77e0755e1e65602848c77fe2f254b4896854b9c90a01e4dd3b26ccecfb2ac9b73ce8802a594524a29a594521a05c6185b6be3ba098388e67491c867d3b40086b4c3c78a16c95a908b39e79c2e3e29d77385b8e027dddd638cb5bab5020bcbf76994524a7b74448f34e040f0d0063a0a44f0530cb567d067eac94fd64f613f9103948ab4c3dbe044e2020023f950499c5764d340af9c52d75a95975c72b0d6665996596b391b970b8548775b20b6c5c35a6bade536cb6d2ec28015d65afbff9af6da7c983da74fef6e97cbe572b97a587befe783b1ab86ce5f9adf5dbb8eb2ab41e32ae5a6e468ed342eeb38ae7324aebc759d0f59e55b12d29dd2f7f0f38d3c914f9020fa7ff09aff58f6f6e851e89384fee6c6c6a6a6a62412cd30838a0a082443464aca975c1cece57a0ce7bf6745168ba5815679ac458fafac5a43a98b276cb03a308fa903ebc43a39a5971854e801e291c98f3bf354eabf868ab7cd41d535adab61c32c317cbd9272bd6ecd5a5ff3ae7ffdcd7fefbad9b7707ff5dfe79f0a1553f16752d69220e425c7a023f1a9f4ca13493082c46087c37466d05313df5872bf5eb19e735ffe7f58714187a93d6a87a9c25eb00303bde4840f10b7478f3a57f4ca77aae02a8f3d3f74fa8a0e16f26398910bdad99e1f9e92e7e5bf0eecbf87fd0783fb319cff9e89fb4d020e7751037560a04eac7345cb7073078b5e89118b335a8a2bf6eca91e7927d4e1418f3a58f48a91c6d1b962c9fd5c33e8a90e163dba42874ef0e3ce16ed514177608e23039d2af4e87107c77164512346ab38ae6165cbe018058452419166c719701cd6b55e69427edf263701991fd60f6b0922c167951e695ee70a0178a85305cf051732f875605f07f675703a3938fffdd781753ab02b3c588f9ad5a3f7fce0e901ec68018316d3d66ddbb6cd5adb6db8b70eac03dbb613d6ab890dd60b37e8b8344dd3acbdf7f3c13c5a9aa6699a76afe6c385b52d59dc7998ff297ff5a8bc799ee6756682992bc863ffe1641874771696efc3b154d2bcd27c964c8824fbccc7f103da0929b9decc43aba2a57911c6435d50a4f1f01e5e06442277918bdc9fc3cd719cebe6d564c23a2e4cd435670f5b39d305e94c3ea243076895c75996922223837426c690ad57c7733fdc1e94d650971059b8456ccd49fff80aa89fc36c4d5e37ab24aa5ee9d063bc9493c30e110d2c9c6cefffc67b1da17051aa61ad33214a267584a6a58a496abbfb0b181ddcb859429dcfd3dd5340b1d3700714676080e20b5044c55044a26758a1b8842a03750ae8492f404eea809a0403e4f230d8820e4ca417f1061660e01778071416c9c71be0f10b90cb0e36a6802115e9f00e3300e9418b54648b61d8a3517e1c676228c78b16ebae60e0d0c2c02f6a9d318ca838e4bf0a03e4526447a7835d606097223b5270c773e9e014ece2c12ea6cfca034c76ec020324ddd213dc241797223b60e0179ecb0b0cc39b7171935c60e019f784e2813c9c7814701e6672581e33a44781268da0b7d8628b9c3c6199e3f004e208629e2358c08bce8b2517205c45876610410409224408112246de4806db15c176bd0a620ceb5727aa578470a7c712438f988a333104793ad18b47aae7a30331549122904347b7c46e5525e4971d509362982dceb7d018c6d0b3ad69d9b85c9450beb384ad855b33b2c0a3631400d3b194d6daca2376d227e062860f6030a5b473809c4ff87caa9452bae7b0e69c724a1a261aa6a8d252ba4b29a59c5c8c938689862985868986292716c3042272414ac3e42dd12d9b8db7ea91b631f168321557e4c7200e08f931eea4c89f120ef9b7d3c459d74e2ce349c76d5ad6d1987befbdf75e7c3fcb59adb675271725a74cd8c47931fdf88fc25020f44451ff51199a3af2df0c45922d41e288443870173541dd18f19df4fa4116eb1e17763b5c08002365d87aa817911222c224fd5d059a94ddc830ee550ca2f082df4b494596d52afd0bb66ddb1a65c39d941c409f6fa6857474cee1757408a55572f8985c102be0e2c7fec31360c2a48b1fe3ad2fc861502aed260bf4f71600dd84d2211d3de330203f59e086ff3ee5445ae506cf4d442ed831892356000a7eec030438f0c8c0b764d8dd4f4e4eba462225c3e232279dbb735bcb663689331655bc7b141f32d8d6dd9b7718c69045d117db2b61586d507133bc75ffb66ddb3663ffd50cda8a84d87f3144adbdb6f4aaa62aedacad18dc16485f435235a96fd93e40dcc7dd920bce588fbc16691cb6082d432b864e3cbb7920e64cd4143d1832584dad4fff978426a12bd046018b17156205155e322f4ac321282328a1e98a896e51d22ddb7cbada3ca7445f38bc75772eb76ddbb0ec1c531997436232883305921d8314480671c6417b3069a171cc285a86b0d26dc6be25eb91c51405b11888f364c22483f884c32010eeba136eaba1a55ea12fd494c15c1c910a430ee5e4661b877272b3edc3facc0f2b440d298561a994dd6fea045f578b463ec11b8e2ab1c329c01b08056ba00da3e01468f804770a6e0a4e41e635de40dbf599a8820262798b74414d82c9e1e7e39a8677b4867768de8eccc39d03522853605971efbdf7de1b4336aceeeaaeeeeaaeeeeadad0c3c7bdabbbba723543c8103d9a09c81f3dc42fa5b4a1c74bf34ad7f34a429cc8c78ff8ee677e1e4e0fc4dc05028643690d07436348622d9daf7923b10a0b6c11a8026cadd67533a3055a433f1f9a59aa02e4469cd9ccba7b65cdc0355973be589efb4bb0f7ee4641f16e607d3edddd0dfb4c69eb87f5911fd6677e58ac564d8ac54aa5f2636b62f5e885d08643194e61f528013e035a650f0bd30cf696c9c4fa7c33d670088b4569e704236ad5b60fe682dd02bbc6a53606e2dc413f2f29fe18638b07922ccb32f9f1474e6e245cc1e685dc4a7eb8b267d1ce2ccbb2ec435958be2fdb3c1512bd822b076a5f82f5ff2a9528d689258edcb7840bba4a1543495cb05b436a7338e293d4910f10f78487b59be7abce5528b899e7aa7b842b8a11b839e20c1463c40fe8ff5e3ace6bba6d5bddb66de35e4dc2ce135c7bb76ce92259a9ac2a33b82045a150288aaab4ea406d392d8a12cd89c3952a0aa6d0821b3137625a50239c73da5aabb5d65a6beda4b57a5e9e57ac717a5e73ce686df79cb456d096b7c95598e7e57979683cabaef5d508d22d2f53e3d4a3ee775023e679000024f26300a87ab4c515d008921fd338f588934664b43e19a91e51567e58545941d14764b464189131234395bfc25a32a04039a1de043faddcdda52e798d2be8e697f9164feb77d02d1e289e07710532a4110ea4614fcb03c5e72519ada9a24cd88c6ca95e91d1aab0b8795ccbf3f2bc7a6437ab7d7ae479f5ca7b1ed87f238f15321a87a70acfcbf32a40102e58619e572775fc9fd8b7f68405c389d5085f4c215b4a97dedd52c6d55dddd55dddd55d5d1b6e7885423a726e84c80d5d4af9dd5ad792e26d7254c272aadfd0ffacfdf74a334a69c66aa504210a1a259488eed1c6d8c518630cbd23f214f4597673ce3931adc074af2c568e8c9465d99765d996b5b12e7b6373325306b338d90f8b42a15028c70e62b1a80d2a6eb5d65acbc4b23e2cbba4439bade991638b417b93bb9918da3ccbf26672c88a212ba4553ec5241361d95254f92e674fb8a54759255ccbb22cdb4487442226485145b530f72c8e116ec4e08765a39852f462a2e5a49f39a7665996a569325227476af55a6bad3146ef6131cdacc536c70d3cecacd4c6b549213485d7129278e2464c0b605274959a41a24a62f372b16a9ac4544a561b57dc984a7de8cc07a6e80c3d4265a84c4ccdd023ff518f86ff792a445f5ec531fd91c14f8e8125cb5c7c364dd5af120e39b898fc870fa12f1f4eddde18425c1e380360097c7c4004af2cbc7c4c91e2aab5869b9612d630ace1c97759966d5a4a4daf3c4e01b56896384153840b6e31705335578a019e7ea8f4ac5aab59a84600000000a00013160000200c080583c160403020cbb3181f14800c5e704a745c3a1c0a64490ee4408ca228a48802042002083040880cd99005710c50afadba54a793bff4224110d47c37802b676114503015164c626d49ef0b8a42f8a0ae940875055936c6554393d739505d6f4fd4200c644e253a9c591aa496d4a668f60fa6be33e32a443895ea699d53192d362cb68133b928ae3209fd469fd8fcbd2430ad4c28696c1ed7e808b30bec7486d1e559474badd87df8889c2c600adabf8163f258e3331f8b07999bfcde2dd12b15c20df6e02393c227b048a02ed75f2b0b29b285502005b6be878f7c09eb4ac552a2b4bddf1ff5d3e3c12f6e61602da9959c661f11f4bf21672920605af1708b2952b380e7adf7b7b4b1fabdb0c88bcdab748422a683e3d46a4d0a7330c2b17906eecf425368ce52bd930895fcafb78028a59fe10f93ba21a907ca26de99bb62661d0223408f912b1333eda2a27cbe7eb2a19e5315f02f3b8bdc30fbb95dd9d0178756dd7f0ad4a24faf119f143e8c381135175d8079d6453ba9f38a722462180bb15954e0c254be729c47842dd74eafad183ce02bec0a9f61148735b91b64dda2a00f94efa12953750b72af0bff5264a9d1f0f03158d080bdb26fda469797fd842035f7fde94bcf379f83ac5fc1a98eca56fe523daae79aa851c58974b034c0988d45b1de4f4281f60841b49a8906c77805737d11c7b74424b084c87c8e8dcf80ab75a63a083d703dd5d5c0dee1e2284e1f3e29fd6f93293107e7c76ce862f56d400d7a8c56a9920f22f72e1d1e9f788cce8e09d5666c14b3acc7531d439942ceb814ba5f62f9d70309ab10d26fb4e2001eacb7aadf921f69c442a5cfc4d2cce1684cf0039f395206f665d38221a535b5159c96e07ac21d58a15bc2ecfc150cc150ba286841361088c2b3b40d4f3a22a251a00d2a986880d341369e662db504e9b856ac1c2df858bdcf0071e6181b9b323cd97a5082f438761f3af5e4049ac5895d1404774495c223968a1f025a7cd4f79d7f7fa91e1e5d246d5715ecfc9d4897803043c06240ded8d06a1a764b3af3fb6de5745072782f858010efc2e382f3415ba9da2b2420afa4dd03970a637a1a3b175042d8c26c08e2488d1bf8891d4b5a5570a4deec2e7cdad62c6d72c1d2db05af1d49183646ab28151b3ceda8872aed1a3dc6565ca6c8dbfe8ff948c4b8a8440e54aa800948a3c0f95bcc07805366447a19a1b4c8d5687931a95972913c8843e7a606f8ce902bab01e898034b759f588807484c8b8ec45de6de8417e86a520dc433b5935af717d28476d077444ca96ff7d75da52925e68b4c99da2d0c457f8f909f4127c12dcd255bcfb1f730baf4741be051001beadccdbdfecf58e92ade37379f95cb31f34ddbe5efdfac8557db78242cebb9472a74894679ac6703f6b2973ad95f88486a7838aeab5658007e4211da81022edd814b018ee634460020113d1f91f8e88f6f9b29c9880f6d0216671b0ab297e2fa3de003d408401fdb5b820958d0f99d2a24921a5f8d3e3a654b5ec50c6f4973c8a36f299d2f4db31056746b7c1dc2859b53512a8424c2b13100d01fbce04ba3f21ae31f7d509e0c90469881f00767be316019682df79cac4c4d289ed7f7198c35775f2ce9d1c09067286cab93651927c2f5c81a93af3e6a7c8fe49925910d18745ee23931a1dbd5dc50d2d21df5d31907ce3207882a3f80043f1e78e867167b07aa9b0c044915a3f142c7767f465c315fd33fc76bd6921f8e0092360b824d892e94f5080c45345ded4a335144ec31a38da359c5438a47dcac6d39c5467647dd3c6283fe0357b43d39119f204f63c4c93c2d6f00f53f6bd01d3dd615cd17434123f25139ef844807daa67c156b7e64bbb1862822f3ee1105c78ebf6f41530fda9eab8ec645b2f395333e3d3e92c6efc4e75b069e57c380deabcb5c14799e6770c7da63a76f48e38c2e5c8fa23a6e85a540642fc7da361a4ba43574dd519595e39d9c5f51114cbd5e785222c98a36c4e01529b9d9466f269b803cb8487fd4766147bcfe5463576bc61639449858b66ca794168205036d09a0654434f6ccbe47aa3f6b74800e24ab683ec57b110ba18f4d3e1e70e5a84de5bd1119c450f23071401e810cc8610362159a6ee6eb98e49baf063f910c20c13f60f48af623b20f0c88b361280d9365a8ea192b4c5f8792e824d68f8a4dee9e097bc6f445b2e5f164b10c83d1a7e14a87fc99959085b0003659ed59614806bd4adfca316a8bcb09a9df388868ed80163160e223f5dcf7fdaf9fa2a7570d7d005ddbf5c4a8076498960082e3262e7315a30977b9c4bfe3ce2c20a4a77221995b4e4e72958af2664b291f6a848d1fe98e7081f6719718d137230540d894f0b54743fe1e338f04080e7d25517c79be125b1c8be401c6044332c916991d23d229c0e90924b2024258b6b973be3d8e4b446252ad878fe844d5b53ea3bc45ac1caaee894c6c13fe47c4f425977a5a725ee1f8c9cf7c093d3b152b8277174624d196c223a615eefde46046d5e226899d4e775c5621e5ab5a8c75d997d9b2c1a0e241d0761470b2794f6acc0e93d662bb3e0e16783f1de41475a10df3fc235a0047856ce698abadfaacd0734f9fd692f9a433e020ee71198725503a5d4a09c2d79c18718f18c255801250603465802c54a7a96b71d46ce597a453ae38f1a9bd354f775c77d191294d1f5ba647b43eb47a21042cf4ee89143181a7c8afa71671fea7a4b56a412acc29df6aaf6efe48bd52dc0d73db5243370ccf996e3c9cbbb5b15fbed2f761601d2350e5d01d2b725ba2cf1b7cdc581e7c702920285c0cc9eda56509c1fb871d29bf30bb071449352cfc242f3dac8cd9207b8771ac34cda3abc490f3abd71bdbc9d74af31ebeb6ecc69c07972d74da455396e25e2ada1dc388cc66727be204eed5740c798a812ea210ce6a5e7957d39f57c97c0ecb639b6e41f933be6c91aab1952c461acd1047c26083955bc68654704dcb7396b898e155fb195e73c4940154d30f4495316992763c3a4e1c1939ebe26fe67565e761eda2cc7b45f95a64cd4fa82396a07fe0009f316241179fcbbb3c11666fcb639587fd74e39e90af7d17ee0ce2d7900c28a0178825f1c122e8f0e12e41ec4d0462dfb55dd6cfdf323f7cc3e31e642bf96546dcf6b2ef3249f96294645cf1bc9f53432ee78c77b9fdae14dede4e5e17bd822006c07163d3234b296d4d26da0d3f4a82ac41a08786be13bafaf2c4ede2ade1ad56d3af25c499a7adf1a855884f0c5864ba5afbe96b4e36fad6b64108a0c285d94b5a8e17d83a272f7f5c5b6977820da3d3c16838fc94bbb36619b4fb1247c8099c7e95c77086bdf7d110450af8dc6d3c49a9b6ef36f4643d0b3e10c196132021fd7c065d5c9f6a138c01b04de01e7d6add1c23f89faf4e0a95ee2495f6b6a77b109047586549f924a602085b8f37cf693f9b9c8ad0ee454a297aa1aafcc2f49a46f5b9d826d8246654b468bfa539785973a950d81509db711d95e24e0595574a2efb4bae34dcaba2ad6569fc2bcd3629178a2e9967c2104f01a4a49967adeb563869ab4412935ff9203a125ba95e1ad9d02b2aed40d1dd7e2318685b2dc2279a34169b9ec40d9153f5ed1c44048e508cf5aa12eef596e1fd0abaf9dffa33a9001edf60b16ea8086548da8e4d72792059b8df80205bd112b2b3c68feb350d081b32fe584edd45f8bcb9975baf24803079560a1cb1642db584a191f0c3200c2ce444a8d84d5c545c871fd708b3914c509a8b010d10171ca17ce7da28de3ae7f0202788860b36d217cc5f9a03391c0fbe4a6e5c5165dde212a3624a4199630956f7b1d733c045c0daf5c4a43bbabfa55d012496be59e94aee42593e35eac53944a851db0ec531c8868a0bce17cb6a1ca5a4e1e483912ae7fec0ad2394fa81c0fa6112dcc1d6ea065f29bb87221326d9381389f549d656185885d00067b306ee71149ab12179267c46d69279d915f945e8342ccd6842acc71a616ea8b30d75ca8f4daeabd7c8b6ce2b5877d66b05eb5129d9d0b53475879899abf41c43bdedfdeab800384e118ef71f5c1bb8fd6fc3bd5a31a35bd948669925eeb0afa5d7a60706c3525cfba17c347309cfce7df3a060e68146de48b5a9a93c5490414966e0900dc7d5f34b9fee90791104c72be8bb3083706e3da38c6846b2c111d472a101c0ae8ba4382183232cb29a16a8049984c3252c223c2fd66323e92df0d1a806481708df022e6119dc24b3c3abdfbb6a4c41275e94980c9c680715b5444644273d2d4198675b56acea6c100ec6bb19052b5a68690fb5f5dbd16b8850e886ff43a223763455066e03bc6d030a6ee807ba4fea4ad7203a37caef401b1e94ad31535956eae6dc8cea13c86326399589d2bc24253c52c39fc81a6cce2dcca3513a35a518889190c5a202723475e752041ed2921a5bce62418246d9e6224e4841ca9a3806e7a92974e4606923afdfd12d4890f615c7ad9a4377e52dba7eb0dd4434b7059c5eb7f524f4ccea1e98312d509d253081e7632e48527de0cb06c2ece9573359736606d069ac8c16e7cf40306e36d950569e74988dcdfd6ac59a9147a0ef0456fbbdd793773b4a7732ca1ed6ab051b2c507613ddd82b41ca3e22366354a60047364dd4d74db1b8fca38127dbc593236839e3556b59842c0215e3aa79a08faf63501345cae2d1278783396d5079607c772cc79e1dbac35789e08f169c7e11832daa9a80b3c0d9a9c9cc039a87d8239e133851ed24fc314a56434a4f4dfd58320183cf1326e033408b6f5052caa6fdea3ffd5cdab824a74628b924cf546e60112125bb41596310ed93c318300ba287a7d5b46cd61d893c7fc1c7b46c216900c3ac553519420176aee60d1a705e2ec9da4e890d8ad16c2e6e49469d7cdcb04d329ba1eb3b4dcab8b6c37355c039a9c39f80462b02a2d569b262cc768fe70250be6491333ea8dc1b02261046bcf61e056b97ef7e3e12d7aa343703b4c6d3baf63b8ab7840748b2ec8317811f8551541fb2526be7191703ff08b926757636469299158c981aafa972e8068a75d5091aaa02849b82dd525771b7d4da7f616dfb13a3fd85c6070f3d6c10ad2a2588b47d79a55ef7c36dc8795d853721674525356253061ab33fdc775c814a3946700c1c722f21530b339fa129617d13eccc654f25639361a6bad595b34f6fd54bac6bd5d50424c13e78f1a13227512cad9dd9d0138dbc4422da5d25a59f91a0968f2f851e2d9fcf542170dd99d63fc70afa89661f5db105a56e822ebe6bec665cb679c4a2b14bedae03d30ba8fa9c6da4dd2c3e66aa079c91c37398f8385b7fc704e75020d63ff7e38c175fb563367741a15d6998de5738e9402333de19f2fc824e9976fd12f7ef9fd8de11733f207d1af48dee90cc5c701b78d309d073b73660d63652137fe35fcd47492fe6349ee6497436377a7f50705b9b05d42e5e79914e2e14d67af488b188da123fc8a63f4155c96a426307278c1badab1ed9b84462541979919c35aaa973093ad46bb13cb8d8c9de0f712c0b4ab222284669e6c10b4558d2bb9d70406e91b8a56a0be63b498fa4e2a812a429c6f2c572a3c9f3dba16578f28a455d1b6bad6b8a1be7228324840d5b5f5ee6175daaf7f81657c678ed00b9fd350231b43485de3a04589aa300361a5ce919a74ad38f67fae81a32e4d715fc74854a85fd21bf4dc8c2a34d2d20fe9b9932d8cbbd30a7192afeed60021fdd8f3d0f4bd3c20610d5be43d6c44257de6c1af328d44c09cab2d96586c1f7084b812dbd4dce3389802f4185e6842907f886a068450a408c34f7390435781319cd5bf350e4e2c4b57f440ab68c70aebce6bde8a66c119642cbbd093757f88cea8f62417d1c78cded8c7e362dafe92a733453bc6f2b9472c2d0013a655d1325c00549410281e1503a100d9edc7ceee3dd2c7df426bb8ac9d027355a015a14a91d11497aecdab18ffd47c9cc92b155353bc6873101fffc5f7b6547deb7c0aabc17a1ee7ffa6afdab99488997136f2f81ba3b1c8919222a987b614b76da680ba1d27c0da49cc4a586a1ba8ab58df561e88dd4030f7adb7e8654b394153de34e8d15ed6c6352c1da00bbf0b5f83c00d401ac33cbb35b2103b0677dcd1004a2399a65fd619c6cd1c30768539390cdffaa585f34a5b8340324660039ebe6dda7ebbe2d327a3d2c8ec5d3560f9878c07a986ec42e4c15831498227f1ff6b510097012e84a3c8c772116705ef4ba98106bfb2ef05697de2242af0bb1ee16e13e698e9f960212257bf01bb65d000ea281a0f92fbf8929bb73acff43e7adaeef7654ac8af42eb15ebb1085a9e58e1a2f86c449b1f2568c2f4f14e8217f8f57d25dae78736d87afbe0c93c9db462c6f7e4f9dc60eb9ccbf5214bc2b66429db8aad7861906ac943563d2931ef54551a7ad9a223e3dd66cc4921ae5f13200cc05b930e7c099ac0064049b0ad75fb91e512230fa5a9e0aa359428cedb57eeaca885b2e5b5ceefe51761e78df1bcba06f8d53fc83cc42035a312b50407fb2ff93537cfe605ac72ec65dd94cd1fcd78bf6ab15ca4883d4d42f4ecd69ee4095235026ea66f83ad2a2eb61221c36352e963183931ab1aef7e0dc1fb76b442be80ccc2ac320e52062340a246f84e2c686319852304e4f28d6fd3adfb2c4d215bc224012e77a04d4308ff30ee9401be4d33148b60d8b32f9be86c231cbd13075c5ebadb8366546ab630215c8053bc3541d0eaaf2bae9c7fddf5e8bc49aac0b3925a9a2225a7ce2fa512bca0589e8b91ed7a7c9758b7c337ae22e0961da9ee61dda6e37e2872a8779d83f30402e4f97822f6ced7ecdf433829bfce82fa466bf5cb2f3c96b07edad4530bfa553e256fb90afc248322431a3860b92e59eabb74fb504824d8c12162ed1af9a06a598ce9b2ce793293bd0f0d000273aaf5c808904a9c2d3325b462f914de793a53e5622f744f119525dfdb71b43e3eb4a5dc1d7d40f5c37e3625c6feed1ea7475d1acb96c77086a2961ef7bf98738f28cf6c5c7ace506fa7968223f2156fa81e65eeada629bec55ace80fca090e06529fbc814d4a5af65c5f090225c7e528c3f8c26f880d94924d0a35af82066d46f7152d877bca44433610f35f8bc3e04963bba3955a18d753ca4097896db6f6072ba6e55e4172f8f90d09d1ba5771e9ff1097f42b795b17bc7903656f8ebce9bee3e2022ad8931d2a7854c424842df996cabbb80a48f3a17a623fc038475aa312ab242ebe153b3731db78848b2e6fa05a1463fe928762a3d84ab3e1c5c97a10ba2a24a503311a262daf3a75e90dfed96838bbbf76173877d0e7a7fa35b192735b0b1b859d783ff488075b6cdb305c2743f25b2e079da35f6c4222ed38246dd1f63d81ae87142f75ea7a1a29b5de95e48d8d766d60db29b82bf3bc85f018ad7412f7b3325efb6feb89e166819c78183a0965ef24a4df11a73aec03389803e36ac07c33585e77933f8ff713e6fd6233ded2f0b0da6966c3d7eb72f2002ac091764445eec84cf79973e9a5a447db4e5f1e295a2d73b48b87bac00b8e808fdf2ab989ce19e949c5c0d617e6bae4881fe83035eae69660dc3514757a613d1e90b5f84d92cef51b3ea18ce3241506b8f860adb6e24c665123e86d8e0d3763a2c9a4609725f66ec9351d9a67f56fae7362910af8da1d43ea4be0b1524e69231b4aace1e23b29f7533a60c69852a250268cbf2ef40e51a2a20bc5b8748365a97372117b0b96247ce50b01962f302cff1e83acc4c842dbbe28230f7f61dc1733b840ceec507396dc89b02604b3a96996a1a36a845fcd27252a6183549e4a9782c566645265b08f0a6890a51ce4e563b6f27826d469fbe7aec3d795db28e1aea1145ff44020f7cacd49bca6391f2c20331b48b79a839efd0522b3e7bc41100ead9853524bba47cf1788aae25b5493cd87e3b9263d8092ef8d677f9622f4cd62d3bf35fb36f8465f6f62dd11e212566fc23374db95b07d842911e9b5571ed137dfce1cea316a0e69a1e95bf51d9877dbcf632d95db265ae964aa6ba2293750449f4c28fec58a888991513408f5cda1291da9483740ac595f701d6c1b6d1ca480c5b79e4f3c5342a9a549b3070b5ed3181231a1b6c5f3f541a113608adf9dc25e11ce7277e824f705f631e19b47635a8e6a4f533874cb6ec0c81a0132d01a17f512c756b5880fc7646eaa319bee7480948c139c52688c14ee53bcf93b6dcbd186e008b266e1fccc5fa3117758de641aa064ac56eda9c606e092368eae072f5e133abd5ce198cac8badcf42c76d1e2a78c453b096017922aa22fa08766d347215dc7148b3c09e64063d9f490cafc8851b0050cb8491cc3b0b1da7eb889050b0efd38385ab122033c9b84a5db0216ed9d384b831185d93efa68d0b889b764817b005af73c560d8ed9774cd1a6d801d10b08c239e12e19922847009ec43473451e06f0f5630996afadcbf32c087efaa0e69d549ea7f21ca960518012946381965057beae3d0fcdae339af29e3649f96814226c1a1c661e22ddf1a53d912993efc2a9a41c372bf6cd160f074efb0037511c9e8f656bf457ec60834a1179b2e6248997b4f62767bb65368685fe9c6d85ceee2c4e677f63abe96ce20f081898ce36e7747625d4d959b01f4732e3b367d0638efce3855420d3d19bdadf67e129fa5f71c217b2708ac45885bd44e385eaec8c2a6164b3930d8708dd13832e2c4164f553c995cd5c130a62bd0e893f411804b1e8320e9145c139e4a33ff93e87c449e3df5a735a21a902f626c3b6670b22e652047c76f3124d2bbf930af38db5f6e6c186e6ccc299c53bdade7794c13190226d77cce2ecc56f0eba60384f9d11f913a2b10d706ce2afefc8f5273804cedbf15f95ec0c83734be13df371f84485cbae813783ea67109c29e14ac1dba88d74274a42770586552383591426368de78bdebabeb8d0460811908e7056fd50391fb4e3afdd30c221c0674ea34e6ebe345ec5a0f7123b7bef4915d8088ebaa498500c6a524bc659564bd2f1a1256aa12eadd55cf5df9bff5c6a7814fb34c898ebe5efbd2bd06fd40a84c01d73dc054963da1d8e2e1db757e5da7e0f25c6598b3b4658d8801f990c20a9d222ac4f86bc3a6c2f117ca9f482e31a95501785ac0fe97edf0d1e2330136cc9589139b82a669810101f8044d8c2628b8835c7f416118b716f15c5cb33afbb87c40a726a161054898912152ee98594b9b017331b6a073236382d7f8d79785dc3d63bde95c5260dea8737036a0b5e7b994bb4f451b11f0592f8ef83610a4127e1d4938e6058d448918c367c73e11a37d65074e2ba6adee416554f717a6502314294e03d818e601096b870921e98335f6c866af4baa738410f266a45cf785e64d6360fe60a54e819b9de8052cf8e04d7d69dd76350a5d9777731dd872a3fcd97dc1e31ec687707f984b3db5a4832ac042cd41a3607e493426248652b7f77b2b0bf192349802db94f7f5744974e9957083d26ea73c3a21ea6897d4ac6a48de572f1d987e316db756f751892a1201a94eb40f50eb23e5e11fb098083ee01b79207830998b4b05675e2a1b9856ffb309331f164d6ed181060dfac3addef8a8e6b72c91dcb4daf165cfdf8a9da9e7459ec9cc72cfcaa88272ec0101299813016ab60925e33040aebe6697d96839689df6f2fc52f0a80f82594bb006ac3f522070e0789d71b3d8e7543aa6b79cacce1001ce524de81306c1c1afdfd361ce933b15b18361c79f8bc4d67038892b8ca2f90e8a44a3d9d199e8d3069f612c6dc5e5ac49ed50575b03403ee12cb20d1a0bbe931f094c8ea1242f0c4b3a25d0286dd68f9a568a50dd985a3f4ca34090c15c223d86edc29f82d8cf3d483f6cf7b09d720198b146f0046417306723e05c3087bb5cc05c0abf6afd59b76eddbbb8e50859f3a84d440c174477916664208e300e2ab031a1a0acb2847575767a669a7f7cbc72868a6f79fb416e4c7fd35b1ea049d3a59e5b71834b082cb5c77f8db021f87cd1334e3be8f93c5a22791d841ba3d50a0d1fd6b65280dc34bf6ade47d6206d6c5b5ae60682829f1e132b37548861984adb21a620affd04fd64f3110871a9f0558402cdfd1eab5899a8d37ee6ff92bba501ce6cf5c5113e350ec2e00f46042cf3ac7e79bde55fc8daccacb8d3cfbb9841df3d31dd3b38f2ad85b073e88ad16fb40125a3da9d760904e4f0cd846440fcd844ac5168316beaf7ca98426486928fbe57f68d050c99c0d3823adfd235ceb3e670b884e583256eb293c6d0b89e3e9271753c59de8f39a0b320b93ff0a868f6e09e03cf5573af4c0baf719ef60ccec223c06a340fe21fb678d808f965d1b5a7fb12231ddfd02903a37faf159a34ef11a510ad07e7e885caa547c715db98d8f92d492f9cc1fefdc32f75143076210e986f0cbfa312acb2b77864b72a61c59ab5e1a62aaab5e34b5679f47b87efe53ca000ab52fbe90ff9eca21588e4eee6cfb7cdd998c21c6ea54c9c28d177b27a44466522034ade6157b959490f300eedc3f7e51091ff98a65fe6b84bf02fa2e2706d90232b4caa49062c01015831197608d7edbea2c57f91a836bfbb0a7738acf282bcb37ace928954932074d8908e0fc8ed9de6632cd4ad56fce4399ccef0a631a4bb4c56494588e43cad0eb88d4528de5cc1e0336ecfacb4467ee5a70eee2b6a4846f3460a0cc04fcdc0ddb934edf40552799a5dd22ee7e839241522322444a898c927854a3a168c374382dfbb08ef656b658621788b8d4e1eb16a47ac47c547a9cb20111a384d81e0b935c89378e42de9d195ca2b48f4f7da632e1b692bab94b3a0e7d9cdafde1050f908f263f727a3815c969d6d1e4bff411f1638c5ee01007b98d54069f9f0d43d3cdb2248b01ecaa869c40322211c6d2519d9929c1f9c80f71d99f26a9aad238325bbd929692151ee46e24f00f348f1189231f333fbc9b0ae2846b8a63c6b6df962400dda72681c887794246a1d0e04c4453baa720d9ed4f5e1df8c1741a782f80ba96560caa3f6965ff7764e4e16acf6c12851e9644be66af16a2a5ca5dcddca2f602f7afdba5658129fecec4a5171a0a3a330922d2e96c3edd7442a2264a9088dabcb4bc8126be74172f2b3c682864c664eb235b280f958f236967695e852c4cc9af59318c6692023af89a84513011290788d06fc2a9242c0d3cf32ea9f49a794f6fec1dacaaf91f0e357eb6cc65f05e58885401d0a35aeaebb5f866cf4c9bc757314f7bec63295da4b4abccb72c3e64f86fad2c2286d9b186c73d80c84953febed36c6f5583f0132d941a9fab58287d4bc7fe29a6cf5be69eaa81e486f1078d7811a8757eb6a4ca2389696cf9e30c3bb045892f32c87ae95b30024bc51315f0248f34fd0a57a44e8658492f3f3ce8511f498defcca377009c169cb44dac59ed7583c2d3cf6b86a2395730ad77666d18a382d83e63405da61605cccb14e5c80ef2a2f96f893a13da6e47a556aff9d5ab87941aaaa8be3bdd7aee9bb07fb14faefe8c85b1f84ee66ab16e9ba6c7efe03b573c9e12b8bc0f235f98cfbc4c372e482d26a3506578cb2f0062a02aa3390dc38c195aa06293590afa4e294124224fbd0b4d31dabf649b6fceaf19b3b0d92b972b4f9660a0b8e6fb9d460461e6168e4f2f6c78bcf67a671c770f50b9e93247bec4b190ee70e638cf10f172d8a7e5d56c4b06d1ad18fc94861e5438001cfcb60b994a59701f1ff9e8d0bf9de4a401d7fa422d4dc9bfdd36a0c4c4fb038674510944d54967323eb93e2a534b5f183c6bd586273fa3b05980b6eb4336a3ea3a0ffdfdee600ea588c7d66a564bbf996de762758562a3ce6f790ec5eeabf20191fd5b1ee73ed7ee0d454a68cfbafaa642718b2a1a3c77f085aaee63a105732a50ec4efb454e44bb04b9cc793e30b1003dead1639a85b56264ad40d0535f02aaf14bc8c1de4db1ad16feb018a5d17699069fa2c8e691072f7ca34c3cf379c0ccf119263a9abb863f29ce89c8fbbfe76cd9ccb9f1ad7345f6b00a173476364ca690ab09e69b2aebe1130bdd54504515f0dac7fae4dfba47c2772030e2e636d9be40c8a1172ed111d55c3e029b7e7f0a440922a2f040a4068a6e1d470a6644bb8e8080564fb4c0a6247ea46a15e48a120d5c36e23486128772128699a791bce121a45713a19144ab0051bb0ceaa70811c9e607651bab6b3ef011cc67f74c9b2abfee58a24ba6df6f980d1178eaf0405df75db163d306b2a6f4772f7f5e803a243fc58f49cd0f561fabc94c214ecce76ae4680266336355340f89d5f93e37b9e53ea9f96c2f827d029624a25a58ad65aa255a8204ad455f09c1bf7e11ac5731f479651005adec48fa2095a4b27dacab03e99c39416603092fcae89c0baf23b4a2ba4616a71500dc8503a54f7d559d598cfa70437ddd202f31f04cebe9ce259333124afd6a11bc8d251866b9b6428f9fb2301499a360c0ddb0420e1665c77ee82fbb48ef966d51649d1df559a4ae7ab21372f334f65663b1771705d97832566603c89f81b06448f971f292e761a524ae1b8b931655ea0e74ff5351d53f1117dc2edc4eb344c7afdf3d4a8de9f843b6db2ecdd0d8661e24bb856f87850b8fb08dfbd5d0c0b1e38613acb121730a60fa282cc644cdd89a6fe62bf43c1e5888138663170c3da4d50b89d4b9c3033fb324f09477037b23c8b673a469f5efdf1acd1fc9837245b52bb92c0a34a113bdeebd90c34f14668843e912aa459908e331ed2464b35a711342952f0107f3337473df6ac510fb1b00bc586848a15554c02e32810ae919160d4b4ce622f5b5d3d176afe3fa9fbca210d6a52f5358a2fe8fa1ac14e651c7bf1c5d988b6fa8686915127d4217ed8884f93bb65547168e9e90d8ccd0f27277c2c210f93419e8ac621ac4db9d918552282dc759ee78c0be86fe4594324d14f744aec2ebb8334c50c261bc11711af63a9f36d8e88975be559d0e69894acb0c7d95abe604dd0f1adedd66059595f1258b3433a515566828a061913d696149755da52fbbeea6508cdd764e52ad3d3774c63e38cf190387034112750b33424106ddb202cad7e8cb54042112aad2e9d63f3c85c6a8d004d25e58bd7bb7eb41e56d60f0b880f3e094d60a3e8c33b134309a0be3179e6821c1a19c561133ada2bc74a3dea2e36cc2570df3119fe4dd6c565aa9a4ed2d73ccb9b77e6b6797d3bdc95b0667c1bbd1ade4927db884f20e05b25832ff037c2553b9f69da766e35909e48e4bb6959451d5b9221e20e12a2990afa529d26ac7f05b460d57c12c61a53b712f72f00c612bf95980d9c35a697e25c2ea2722dcc188e041a7413235e35125051e41a027e219c9b9deafbda94866ab291f2f8bc9d18caf500d087448301deda4a3078b4ea23bbd28ae4d2fa2aec9d64c139de7173002a6970e83f42498f52254b74055944b0b0d3b64f9e6a7d29b6fbdf1b70075021dd25fa2d3aff23d39894e89c95541d7667a001a244c1969a97141441960ebf9198ea9c5f2f723bbf21d5dfb606c7e18f7d7e04c5192329c43857d5ff5cb87b97c59472b7cf381ea13d06645e7aaca6b4b6e56b178df8a67fac0114535ac9d79f42ce4a4469097fe956745af2257511865418db9121382b070811ce37023706fe10b439352d0b5b8ac4251c8b23a87a13bafaca4c95bcbbb1931e518a80c7638c8e85c517dd18bcc5a019f3ab68faa03f6be324a56c1c9d62e1c4243498c1ac2ae81c917ea5c0f58605f9f8a541f501b9f6e8acf84342d8e293627ef8d58e1402a50d71a7592f10dba73740d9f616af5b3e38bbf46dd966f842b47d53b4eb8658dd27bc5ef8eb9e4ed2bb070d300ceb4b449cfc8e6071c5f1b79641276b653588bdd3af4add8b148116a04277c2d0fe6f2e2997529adb780711030d5d50db3feb83cd6052ad16edaf3c7a7d72b0a3793d0e82f176800def0fcd6b2011483ebaba8ed2294a30c4bdecf03c07d1f377ce4b58f37c60fd0c9f1bb68992fa70faf9ad9898095e3f4a0bbace604e1ed510423bbc61b2d1b9abb126cfff89b887d012aaa4a62d4269e4eb6f19ae6f487f29eb0c23b6cafd9312b09fcd70e14205f81c279cc765a513b5258846a3be5c9523ffe4ede858c143a0db805aaccc85d975b35798b0ade787d3c0c906486ec5b4558b34d9d211ab9e33b74846251103b854e3d9d42370a7863aba077382e4cb7709ce2976f9b9f1b2e76c1554b0b87940990ecb51932c87b1d88bdc11cfa2f8560963c72154a06529b5f1ad16d2b452c5aae34024db9bb27cd27814430ed764192f9a056948bfc22519425835b480cd4b3cabad1782dad6cdaa40b7ca8a3163b4a1aaf2dbae6badc61c58bf445ef47b218b025264a4a5ff2fa8f449fcd9a4cc7f07debf7de3064cb5cce0b12e2318d50cbda16376ab94a8b93c6bf242558d262f3011761e12669c543b025699377eaf2bade680f2ebb9c1a0508dcbd1c254938b11b980c71c610d92a098c149aa43a6141c44a0a8332719fb23d7afc4978840b05b2e8db0714b92d53d8a8d4c854c30d39a5c4e11f72c69bb749c99f2b41d289d5919dfd108934e37987794a84e2fafafc8b7740ae771cdbed52a207db1c13874060d2e1b2572f7418c83581ffc7730e48400c7ea8d41f1e98ec2700feb471bff116346b3cc4caadb8425e5965d581c6ee86e49d67a81573a37760d19a7f07e30943e0a7f19b6c5d651a12c6818b77e8404c9cc0b8b8447b17e0a535f13b9bf2a97264599d7343121371feb6b57364f1170300a2df919a8b2d8b9e17820b3b19373f9b5b61ee19e5203b64b0cb6cc86f9a861201a4f613510835ea15b1c6a3c49ce1db1de5d27923fb9e8c594186db008de34befaabed880c7d760ad0cefa2962fc30a69c96fdff858ccf1c714241ff728c93aa2f088368163d47b06f4c425966b166a0a2342982eb67e27d623c65419dc8e76b5050eb770e782d4fa33151c622b371071a2289e11b08133fab2d6a6b6cea8b23f89832af8fea866319794816af1963e1221f698dbc78066a991ad68c0e44aff8a41f14bd75eca4f63d876c7bb227a15cb34a812237a04b6f8b51ed6a4509bf907f150f32afac028e83d2280e3d46a4bc1c7c98f275a58970ae08f01710e683ef1837377ede3245a661e2f0b08c5054b57a36492acdd2281a0ffa8f411ea7915286685be37bb3549863185f9189fa5035f4264970cb18f7e7bf0acaea32fc757c2e5919d525e1991997f6e9c361d4e344e9058edc640267b22eb6604f395d098b49e46e4296c45eb921cfe39249cfe20a867d302d82c4451d9240be9f8b6857a62a0487d4128a5db31f6b05ee7d994331ca923f68b1a08c17da59d8107eae9551bea61eaeb13f121af4aebdf015e09ac46ee36b0a5e93641582c057d0f90b4dc439b99d94ea8667692130f6c544d0cd5ed388707a7a410f2af8c6dc11dd7ddb6751f833812080e9accc0c4893a9518013ab2f25992faf467eafd1c66cfc654488aaababe94ed6dba3bcd0753a242cbdc2bc0b2c366f4ec00a07874a8d344c93f22d85b358851d1ac8c62ed30a154689dd5ce25241e3d8e45d98e67f3c1afb9d0e8ef8cda44f19d91b38c06fd9aa430cbab24972554ce1cc6f42f03a71d2fc1ab1bb48e1390bf3c2c5a6c7c7c6d9344b91380fbb9627b29b4c5c0a2646bf4fb9ddea4ddd746e9fce20fe1f61b39139a6c93a8aa1fa2c3a4486e820ff1b185c2796c2910c0daa091ae816db6f02ae12f829e693343f8c6fa8754cb88e8c5db37dbe5be8a05875afde147695c9557d5228a5a26524055398531946d94336eec05391cef1dcd9e0d25280fdc3a6b311defda3ceca67e5e6bacb027e6b3c449915b39d1fbfd48dc9f71b157d620936010a92631b6fea4aa891497b4f2794c1e01ba4a030f8c05cedfee1e392a507a23df5a2a5da0d1334c8fc59e0672566160309a191c0569f1eac485feb3c88be53c454b9d70161196da33e5152cb0c498a90c1a0de1ee0d0907bb59bc5e4e238ca6b951e2d85fe9e8b659a9007ea38114043c22cd266baccfab959540f6f3cf1f1dca41714b225305cac9a36ab036f9317ae2243f1540c499fb1a7426f460922a38814bd98350c48cdbb9a980fed1a210503e707b6f8e91237de99979eb917343d6e86497105695d2b1818a5fa4cc2c050746017900d413b0a8f6066eb3503ee617ed8c22e156339255cf4682f382db89c214320f06ff6461a4888ff8778c2b1fc3358507884d835091c16be8362723e34ae132719059a82306080404fbdc5885e410d574b44e9ad0f09691ebd416e5a100b2576523717ebccde4027326f8a61cad7315698f8f29cc92ded25fb7adab4693fd3a92655b8afbabada203d508433ec14953451b200948b5af90cca6a5b4c67c9cd15f11bb4374545a2f6cc0d7106bd48b249d7c8bc5442781eb7dcb0a3af49ba92d55dbe67f627bd9567b7ceaab6ce8ec9d7c15821462454f61d1923dbd9a22c6a42302372e7da567a521d024cc9341e986314992ce8c77dbec7e26127fb88c1a252497dedc381dbd0370c606da9477350a57cc6593e553434d144731792ca85fc6f3847d69398a5c863dc6277e5dd0e3d806c42937eab831b6d52161a50d8c6cc28757eb130d8183c5ac82abd7b0d6a2f2e4d4aaa6b764b9ff3aefb756546cdd7bd36917b3151c537ffea4362b364a4aa51f5b8b979d535244392071ebf0436edafeda4d83d2da78dc66917627be390b055ebada94b4b3230ea86f8c1336a2b1942a725fe329809d99fc082eb9a3d52c76b069d1bb00d69d3b5db92c719992a1e19da15a50c4951125cad77a35ed82111102d3c8219b6bd25c3c7f34a687fc28db9db134b6494541122227c5378db0ff9bcda20876a2b42299eadb419b23913cc03ec634662e903f7eef181833f254e0a4169cfd315364b4eb0f4c0c20df3e024ac1b05577913fb59a4954ce599dae58f43e6869c12f89ffdc2548cca0e33ebbcb955933067000922e957a008a2711d7d26c0bf629636410a088152a6cef1320e43f55fc98b61080dab58540062bfc66046aeeae4397b8f02cb2d84bd2d56328398cc2e7c8099e77f426ceff975c2163089077e477b618690ca7802fb51474474c4cb839ea60f7a260b7bc991223afb9180195617c28c8fd40ff5eb8cc2671da47e22b61934d1e3b140a82d92bb0019b86612339d9acab061d56f534975a6d7634b276b16975706bae27f9cd6076a7a09d4fe5251d169cd65e6fdaccab1fd20e8a93837cf57a7c4416937d04a185c0795473b0c96fd88a353be1cf29f6ba32a9e4638b09db19a2e68299ab482f0cde05453b30724b5c40c11af5b59386ffad499d29ab7082f684cdfaa6cc5886120cc18f0ae325d0bd81d787373f5fa3ddd60369292bb25b03df03fff58882e960e3e268d049ebf3cb296fdf16042be79a56424f9f570320512bb72e3ccf8613b3861f30d5a0e3a2ebb4a08b36f37024f342c7ed4badae2e063c38193e18eba7f26b0af73ca393aee46bfb08de37373cdb2eead92284b904d72aa50515bcc2b47eeda30f04375767782ad5921e771f54d9a77922f767ca549e3f0e8d3862241ed0153473ba94c59168990599777b8f1fb8012171131ba3aa77aa7f074e7c308104394874c54801fd5274f2136ea4cd9130a68702ec43710da214753fdff7d6c14f8fdb17f5001e81d7c08d741579f093123c3370d87c6b534c7c2f725ae159380a80f4663867c2ebd0849f3a06cd9160aed228120b4c3e75e6ff07b89375bbcd6bca5d89bd0872337c5a1062f7332afbc388d5dfb32a8a2f4810324346179b3bfd3691c9b56bb1cb26ba530456c9f3ca892189c4d42eab61c80fbb4decf5008bf574e7de674fa5cef6bf696c3e9ecda2846839b2485b27ddc58a1601ad94e313c272e1f873aa6b9d9a7096ac58b99f4db12cde69a92597584e0195264422674391cf3bfd009ebc466610925ce897e254e233fbd8c316f75b4bd34df45d3c84898857c74a3b079a1bf2db64c435a98e2621265e90865f57865f35ae87292a033ee0658c43e490020efcde1ef711d645c59cf944b629c47d44bcd03bde8e837afa1e40f3d2ca28d92440fe7f4efec2948f143e31dc40974889986a87c26e81486dad2d310b4c706ce4a25d701a93899c6bc217a9f1336608485ddd9a0cbe3d4fae30c5c3b95d673cfdaea07a12dfdbadd893bef9d788eed5fdfd91271af267939dcc4195b6cd0c9ae67ccd4a0df4961dfb765472a3f52b02a97276633902990d22741b4dd48bfbf7fde272b61143d4ed1d9bc2bd6304ec88fea4532f4c3245f1e9161a2700828c0cb8697ae65bf8d89663d2668e49e6e5b4598804a2964b6d9ab2a6a50bf3f8d7d01503218c22b950859b65415bdc94ce3b9cfdee87a9241c6f7285489ec7bd8d8b378ea25dc75db5dcbaffecce2a6f7cb2c945d9a4b26c4a4ecc6674789a9d67ebfb23a0e9a9e2ca5a25ff1e814fea26968ca46a67f91fdbc38c31124edc0cde1506faa4c0deba542eeb6b6e795faf4b6e86b3d54da127be3049153e8f3537637a2b7b318bf62ede46c0427661b66abb84b39cc32e64f762301e8ac592ef36bac96d7c509b50f8ce69d798f3bcf90821ba0ee3418f96609b624c71ae460563e9009ac5dadbd2f6c5b3d0f17ddec161bc51d1c7afc0132e9b68ff3e05b819cc96eda1fdb792959340a14941dc9cbffa74c2ee4678183e082df67122b04bce625c4401599c435345942e1052c619683783656aaf65b5287d0ed6c86814a4219db492502f89b05d071505ec53a83c75e47fb6149322f653622eadb376dd79ed1524c51c9c17f92533b6db0a99f252da56c3c0bc21b699db1a4124e8c50f549eba0c4c0f97b9b039ba532e356081bf6bd0f70b771f9c79a5144d6af7f70c4efaa60dcd9ffa89be605aaaca702f13d1cd5f858246b40f9a5c59c8d8f35e7e13ebe302de6d3278bd912c13b6dda42990c8e699ad7f659332a62551643571bf741b37d8e8f5b329eca71efea4248eb024d3d97cc52eb738e65c50b488a0b9ea4af0ceb9959ab3d2a2f9a29509873c546d74655f93b25c2e8dc63a1593b1252709b6e0a131fc7c254e0f99995872d1c31b0a83f882f783a1e00867ddcc11ca1ac30136b5a796826988316b3fb45b3ba28e34e5105c089845079309e094700dcf849b115649da0e60c5b0f395b0454314d54b6efb6cf4566120cfd791316cbb846e839218a850afdef466599d777e6ea80d6b8cd78912e47a2c4c3c581808af7d3d76b61a3d89ad9c3f93b9b3a615b4ea3c114d52a52ea6795ced145191328283979d6fa614a70d052e2eaeb03c045850bf78239aa118ebd592705e951bb00ed87c70decd44cc03930651960306f6fb0f649f038171ec4487e044a554bea14473249320e3e3128105ce560cf94bffb0b5f8e8aa8c366b24507f0783d690e60742cde0808735df28c1259e6db0353b92847f777435a44bcee4e5601499896b2216aa903745ba590258dcdd6768948d768429493754dfbad1e0868b9505f1b880a7263ecd5827b55093343472ea935f89fcaef8d8791c4abde7fa1248c845cb4f1689d298a583a568e08d75c92a1260be803180332d6ae529a529fbf650c9c79573ec48231cf89b87ceace64e5308d26733076ea1b2b1711f71a7ff8f5662fa5992d06a26de29022153e216bdf70fee8e943ed3d04b24e24086a13d9861bdc99dfc3f30140584bea798f76dd09b99f88178d500edbdd048332eaa8b682a68c30e5a1a5f7093af6dc20e2848d494097fa1545fcbb047eab9f5b88e3cbf86de390f47db23d727b890e358ce249dba81eb9bce387d27e22fe68d59c31c23adb98c996b17204ae7e61e6e39895a407ce9509eb64988c51de1dba6f5ff1c41ef5b2f4c3830a6110922a4d85085688d2492137b5e426c170bdf6ebaf775017f5f1ed3d0c6fbdf7ba1868b85c08012a573866aebc9721107e30cd8903c61658e6574db4cbc7cd2d187c1cc778e4693e2535521d7d48a1a5a3f8b654d27a3b6c706e081a3f7a2ea292fa7afd5f8295292eca5d91f1df692245597b2c864c327dc809fb717ca9092246bcd094d2eb4b4d18024f29488c25fa00a1bb7c79c756a53f8b3e511af1ec51f0460a3f9f79854dc5d302ed6c1b5e1e85c25ff9110c087bc1e9461071a13e4f9cac5e4ad01ab08edc0ee963022d37dd8acf8a3a4076675579fb87d8d80ace0ac73dcd5b04ef206eeb647101f15709adf0c5e4ed6ac39ef489382dc50910e7376470f2f73d7cb33f880fc2faeb730d6b215af73be5bd727b5cef78f3a184812327e27e447bbcc9db5871646f5096f02761accf32a68abb5c5b5f952a43626ed345b7d059846dd90aa1de2ca1b249cd01e2aa2aefe2388bfd632d47858058950eb9876e4151313ba62956f6a61f846b6f4b7e3cba1f61fb492cc519607d72537f362b53a3530933d36c6c1248b1dfe9c759c01075330a80c0cee24e0427cdbdf2d0f9137d2c28c3421c9fe396c15d7c83137e81cf63a487acbb1223f4348c28b860bddf185b7c3f1794f52c807e5bb91c93325176c13c596df8a806bbf7ea5488d3d4aefbceafece0c363dca0d40087c465aa0f693729ddf685a9b467052f47b57a38a368e3d5e038b6bcab2b0a68289a5347cc4c1b058bedf9179bda137024fdcadd11cc8fe606c1bb71569577e90acd1b249a28306e1e752888092a04a5bbd808ca72565f5da57ff94c061bb3a4d6549f532142f990d8a010b99e11b8fda01ad8892efcde4e549cf70a4571260499747a3405d7152e2764d4c60f4f017825b478d02079c55c3a0673329412dcc8c34f6d6d3bce9d06a95f1930c73ed39dc7a7e47d11b090208194712103d78e5aa56f4f3965c12f3eccd7deb06e6ef83a588d2a7ec2e5bdd70f002d70c93700dd835cb01562e7d6abb3162398345a17081ea6bf3e17bef103158e28b8e9236daba441e22255544b95f6c7999c029520cf9ea6edff56ecc83688e1eb57b7b82aa6775d3e8075039be72e9bb7b3b648c790c9862197dc349d9a89d405a4c9e8f9623a76164c61116b0e9628b94c9e186d4b8a9add3d599ec5c31cd7f594fa26e5a67865a2b469dd8966832744f0b51e857f5283925aa3dfd19cd271948b45ce7d109193593339aa3cd278684918bf8fd65b4afc7d55d2a6b8d5d24fee7cd75f8df7ee7464e9df2a8e3003f161124b566eb32cf3f49e4f22ffe1bb7b96147d163b691ad92ac46173931cf377f8f9efcc2c3ea02108bf1fcc76b6266722e5ff7f14c4a00df3719aca5c7f35c15a71d5fa342f3c8bbdf745680d22ae68b976b4461d858a135d4c62552ade874993d967661b4d8f51a70e149fc20d3c69cee3a7346600b85a82fd8061c4d7936137e9a47a05d2f12c1ccb7451d0176c2cb4de135a6d8eef2fb06cdb22c9ee75d31da50338c7353762b420714fa5c2aa2d4883c0b2d08fdf07cc7a9af17747440465bf156520f6e09ce8ab23e30bc595144f37c8526518666c4a4131a18eb21d7f0ca1b2bc75a1f5aa139c5d444f46e2f8df03a6d4b38bbb54180424059c09c2cb429c7263fe7460027687ef0b3fe41d0e5bcd19f24eb636b4ab8fbe7eb223e239f06fc990484f63d4e9304b6a2f017657eaf24c94ad67d461ec1ced3bbc130f00c1c3682552850f27a4fedc016f3d87a95e75aa8e5456036db2a4206f2cde7af62bc3cbd1c62c4eadfc0da8949e50e5cde7ea62ba8a8263616476e5b27441b294a54090c08f394216720a75de59fd29262ba053256c8f1792f7320c4727c928bcaf935c606608aebe7ebaedaac2175725e51d281a1d660f29b55c0507d5e889d89624706242ef6b1be66d98e61ccc1d6f27cd1765b65c807ed8850433871c5c2c9e18be78f5a82f728139b3781b4604c6de01af5884c2256815fe9958e355ffb70fca1be02c95e2b459aa14d471476e399b9e7ccfdadc4f040b219cf14c7253323098b3b8db38c8e3d40c4bb1b80e8b3d64fea22d2cdc0dfb4238145895945b5f78c9d6e38766043f37d04493986a58210ff3eaefae807244a10d23f0429e82f883e3f42fa34156ddb40f239a6996a6f9033bb68129444a5d622e05d79b2761a11181c5d05da813ff2295c6bd510fb9742f530f2a1ab6a4169c4733b04080194224762d9ae3d4a352c8a369aaa79800acc6acee7c5d6f9c960ab0824f6a8973427928132a0bae6c612b2f3589aab16cbee2e2cd5cd1d612de59ba281a15f11383b506dc50c7e272215349d7b746fd6553ab07e4b6c6b5a4d5e0cc4722e90f9a319ded7bc9e3d740b699c3aaf13478e43dd081c5ed47681fa208ee97bf236be4a0790c67f4b72c88eec604045964741322116c0508436356293d7a557f4a99e8d772982a6f8ed0ce1dc3952889fa82691d371c4c6822812b6d3228dd33f18d794b9afd1c2e4e6acd8b6eb199e2de2ce8c8d8c7cfe985078aed16517ef12dc395f0287484724a50faea3a7f4d871fa4e299fbe724bdf5314a6149fc7f555d5689536a977453ec7842c82de8ce7f2b78d3ef9513ff55b50c65cce2f3f63ed4a937c24b88d52b18e744eecaabb611dad36ea883ffc0150dfb950080371f1e51ab683e188f11d499578b9c0a899f92b678a78ba8f0d571d0a3fe19c99709ae0dd4045ceb6f972fecbab4f9941febbd4b15aff29489fe91d2b0db30dd346e0f0727abdf210644c4c949b16187130ed3867d23601cf8b4eaa2a05c08b893be3e4c4ec583c9916740bb9b2fc99a293d477f2540b488003c53474d5981a8af6eae08327d9d0dfaf212a37a1592281bf2794ba4b69232442ca0770fdb1dc6e93f706d026c3b43fd976aa235ff1705b92dbf6df484cd541fcaeb6815ea2df5405b40a559041f31c387495a6cd54ef5871858afdea7a81f329a2a528c88687ba40b1ca3de39a445bc91eac690b06ebd5efcebf31c2519ced40df28efa291646814495254a0079ab656c82bc7a05fdc0dd72612671bb40f0b818b5bfc2874f9f278c6c555da1c62dd09b660f93c589915bc1eed0e238e800202e4aaf0802d9567e3398508956bb8de09418585bbd14f3b76ece5cd85f325ff8b36797b46671a105e5b81053392bd4bddf282ef09184a9647f71953ddeb1a4a638d57883e58dce6edc62dafb0d7d923e97dce51b81acd30b48c3814b2ec3cc3e1a43c29ba2e3eb92645dde22a2ffa6bd7407b36ba0d523d8b4a9bd75e25688de3b016011704b46e1deba98a5720c9a22c947313eada43f2dd73a80119f1cf621fb1ded41ef89e5ed64630ac86ac10007e0b23a121445b666194c0a2681192ab97fbd6dc943b78e19aa6e0006b74104a251a8a90341f8b9cc9b91eb4f656c0092d1160f7c7c23fcbc8d3045ee79cb95a18347a66579d9030ccf0b553b237bc1955b2a8f1bae320dbb8d7e3f2f6be12e282937a78b7340060a5d1d544744b2260612221b83c8d5be6ad1ead1079de949d02209abbab4182cd9cbae6a3e570599a78babad662d5964091c52429c39d1f2100f352003d7907179d57798b9f997c66b12c106c3877c5c5592fe24b896f379fe8bd00f314cf62fe7c21adc1a5df867ae3e11045dac60f99b7f28fdd330501f61a2f5c734f0e1af49eddd39032f4f56b4b1a62625566b0152456723d001c53646f49b5e4c98702e35e90e3a2fe6aa348c11ece3e0310e4d68a30ddac5237b13c91e37119297ecace329395063f859ae1463a07de15e85b30bb330c98569102b6db3afe991cd8a72c6da839529d3828e942d7445d079589d4403fcb3beb1831e15fb2163a9bdde62126633beb7e087f8fefa69043b64045680ac64cf55952f565ee41ddf0d87fa4f0fdc7f68712ac382b0a51125c37c0d2f7f86621879eeed0e63f0adce3433538d5902d36084a3c0ac048696a47f009ba9044c488e9d952fa1e86db9212066428e25bcd8b760c09bfda529a4984bbf66bc6a5a6770c07befca249231d2e7f69233ee6a4e5778c438d55e91496b3858ae72f1f8c2d4601e342b22d29ec775496f2ea955d88ac0f4825ae2fcc936c72d097a8c85545209e39a9a094e612e1bd505937237e4c3a2a863b377a1e9082ed3c0f7a0357f28f44db626067cb4a4827c4c34a242a78e5ec1eb60371f10246605456d99aec16acf75ca374728691d8fcc874e042273b3d53f6ee3003843efecb9a6f01e524042d7e4f25839c8ef3fcc3f3cb21fb2de22df010734f613fbf463a62d79fe6d107d077426203313f1f12a072bee71a9184747316903631c6f7f299c80eac75caaaf0da2875e4076d5983b62b197d4dfe7ebea0116fd2f144fa08299610e0352bc0c3ed78a20ec021faa4a0549420f56246202bcf341a42ddadc00c1a286750301146c0b5f7e708aec91ec402106eeb205d3cf114fdab314ccdfbd9c6560c8cfe6b4374f637fe68c33bb183c2d3593866ed6c29bdbdad8cc409bb194f6843e6e35caae360e2a80073f8d8f881b2a7c316e8f39f6db93bda6aae3a41a2e34c4692b6334ed40130803894b82a685f18ccb90e1fd5f7ccbf4d6209103bff8a1067fabc442352428717b16ae7a8c28bef54dd787b3ba60c8eda01883f5351d681f60fbb50c029562347b7e3aeb8113c6fcbf560c2e419c022c30fa49714351c37a0712ac8204dae60349934346efcccd831712f9384b2022bdb5dc7a8df559f7d15aa783578b6e9bccb4be41a231a84303e3a6dcea1b4b19f0abcdaaa43f01e64d5c9f00cd90bf486880a175eaa6c8022bad765bd2a5cdd7ec5ac678a68f7a3823d1aee529202456edd399d1f8a7aee96c4a49e1a0d50a781a9a3c2dad4cde8ffd6581ef79a18e7f651e42e3817d2b171fb53b8e949b85af34ea85522f1bd1bb640712d928ebc26861bd9dc8daaba0b3fb106ba0f7a8a31893ae7792579b4cb30bbba6e1dcbeb19ce06da08db1f13239de11bc14786d1800e58a87f1cee28330b745995d163783cf5c661c631d1b62faa5d7987577adaa93430b58565a04f3c0fc6956d256a3a3223b11ef5d4485eb1f00e5d8a86b096d87d17bec505d99a5ab92180b2ad76bf5be8942fea82b625f72727f7aca6a0531b95396279b91e99fa53c1bc478a87fe3769ab558c5bd40baca8da13f538f07a07172545f861f4f0969785188db3c629a2ea58c27b78109eababac31e20e55d8c0ad887b695b0a19ec0e7f17c4ef7ddca7aaa09c82374a1396ccadccc75f124e379b1029876e29890576b20a10bccf4e56ccb0762befe35673a3c062cd82b4eee3d882bb76f54ba27f728c451b64e62c0385e7311c5d6d0dbdea74f0a0662e08b89cb648cd2230d1a7f6c1c2b30c801343d8280e95bd992b00d302f5c99a9b2db1dcc899cb3f28aefec201b416acf7afa397246cdb0a0c2130b94f36320264c75e03099d5ace837e96992c6d338f270aa98e2504762ceea9d2b64e7abb1fdcc93f284bc997db4d5c2f1656e717853e7ae383741d8cb9280fc71a10f898c3779c3e3fcb631bf9dd4c3a22bf974a8ac177ac678e5dfafa6cccf9a027a999b6a3c50025e31274831ad3fcb04b0755c3c71a950d33b6a84857a3bae540ed14623ecc50914c0f17bfe6c0ac555224ad1a945fd6cc94786e35b2424c322a4bc8c1bd2475d881ae982d6bfe3237b91ef2c478fe9e57b21bec347d3e3b304dcc77309d4be104493d4e4a4100bce1e2a02ef45c5953b3c9c55b88137913d1ea2249627a33e6ad171cd56e4df72475c4bc614ceda39ad241e397be51114f740153439ec3e56171d0717e0c62c6b5f65687c236bec48cdd09bd0a286aa57289fef2438f2626ba431e21e0d2d361de8d398a330d4a686342019d48cd9c9b0eefae4308f36f0b0cb7027aacd5565b3056d2380779eec5907c1818a100aca65161108ddaf14a3a4e933049304d51af8b7743b1f20a91eef72d5f74c67bac04c171267d2ddaaf85205c38be3539585cb9cd2f08cb073f043d766222a00e556b95efa5252bb79452ca940265053b054a051a88337999a13a7dc3108b70132d3fb9688a8c644f28f6d4214d16712b36433a6691450ca32f9f8a41a43c38ed691270dad33c70da7bcd10073fbdc64d40e1b8e5694e9380c3e93432f536ca0e1cfd691e3ccd69da7acd1951d7987a218d2ce216279b2b4068407cacdf514b2db516040204b6c7f6bf1c318806e2f5edb491f81edde846b70d084dbffe7f7006a922de317b3e27db53cf91e328daa22f0efcf102107480042c85fab4a745eaa22ead4d61b8855245c6f8c8d87a34ca68b95228477560148f6bad1cc65c4c86e74916738b4e5373866558866558ce5aaefe1fadd65a2235a6be48f48f33ebbfc7ff31990d6a9db4e28ac39b6c6fb60d639cf2846c9bbb97f9deba6ddbb6959973838bcc32e223bbbcf6ca111d394497b98f4ffd98a169a3c2068a57b56ddb4629f5ba9999e37558260ec36b9b22ecd7e2ca9e331f5de7c53e95f78412d65aac59ac19c11ad6b0b8f7061d7d9ba72613a554bbf4b54b29a594524a29d52693b529965bab0877fc432399eaff7f4a45ffae19b9113b295527e5c9ece42625c488abb52ac2e2617c32002e6e795557e5273f2955a10ae67c52da185fe60a159d9427e58d984bfbbc8edbb0f6791db7612ab23b8e24ba5a6b2591c2506b91a8d62397441289364fbd714676aaf24fac30bad789219f265eca2c57f1d36b2668f6d448a8569db192b57e663573e53f364358ab9410fa2fa29a8e26ea47bf475ed290b8cad4538c3f3acdd1dce887a73fce913d5b339c1d9b4e796de7dfda1f55507c8e1b756a66383b35b643d3c9b10c93337641e6b03831d89efa65bbc0ca8f84fce00b4758454043f604a9b448192015869bd3628e1c9d26f935d5dc28d3111959e4bcf6d4a39a5cc13a57c46f96b348a475b84116e9438a99652486ed5d6031b333eb53d3c22206dcde0516af2cc8046cef220b969d59b4f43982963e35d8267f6e4420211ed9875ce59111320b5258b4b060bdddce45cc11bbcbacd60cefb39046d63350938901358c24097e4a083c0e2f493ee48b91fd216929dff933bbc071cc9d91c822a753d25256519554cd5cb91c52cc1c3f3dc1e9aca22550297e48d59e427c66eda93f1c92aaccff90a0162d795c2e0ee971713e1c727f62f6fc8c1fd2f52197c81e182d811f0f6c4fddc515375d5c612386cc67662a3172648b1fb27c01cd5c71254bb8ec8fcdfed4ec596464d93c6ee4a8b93f96eff5d57ce5d7fa663e72b70046163fe47fc8cffecccc157a673e369766d3bcadd55c7bea1680a0b1f6e4bc484ecb0c392e1e97c7f5b9581e339c31241071814f0f1c724fcd79d007db2302283fcc70ea0f2946135bfc9420a041943b464b1fcbe48cb4f4d54c95a95b62db66dc53735eb4f4b5e88c963e17e7f5cd60b195ec9b7d64ebe3142108e40fd287cfe8473faabb4929a54f5f1aec437ec857ca8867019250ba028c23aad7faf53d0cdddddd6ba5ee37aac8625869185eab821622b3eab57e7def6047b2885fbb0b958e66701c0b7095a961b0102971614f4d4da1c9052532d5d5da2b5bbddc41b01728c9f8224343c6626a6e599665f9f7de7bef3883c9baf54a1f663831583bfc3221a9ba7d2c9016a11a2442a62e7a6445554266087e91459ac22f303129afd2db13f4a22ab3445fafd764ed57895972cbce25123fd597498607e100899f64701c538bb4d81964051526c731f50b63fa32c93a2de68a98a305e45202e16089154ba85082f1b6d51245b2955ec395a057e7bf028f633bd753964049402d67f8099403335c070849cb0c5ff44524635d47e4dcd80721ec16c28e402d180411a0080b6a79815e16bfac0ba8658626930c7e0221397d098403d0cb047a99402f13e8b52d087a31c9227dd116500bc865833e243208049a01e88524e5f5f25375d012072901e5ef867b2f06bd402f4e867ca10558ce398a4f29a5f6f1638cb1c74cae6666bbcc9e4206490c147bea7ffd5e5985a8efb4566befd534bc8d4452e7353c1f89a21a58bf8e4d745dbaaeeb30de368eeb3acffb3e9a3b23238bed0bf323f1c66cb8301967ed665b339def94b65a7ef29b3939422667c8e414d9537f35737e367beacf8a2f79e5c7d2628b5f4b234104c62e83506ab1a7fe9af0920733ebd7f2d316e3d5289999420689077b02d51a899d828f880b4bc3f3ca0b9ba157def29b5feb6b519ae38c7c5df6eba903e031a25271f0c675dec7019f440a439128fb10b2e823adaf7a17a6a1d9d8dd0ba7b556ac52398ec964be2466b9668f8eade299e067a75ff33f8ec972b9a4069aa6b9bbe6afa5e8d77cacc14f754360d2c0699aa669f4ab7a0cfdbf7efd7adbb62e4643553cdbcb0a183c70c2beb56efdffadb5d65a6bffc5691adafcdfb2a31aa2bf3fadb59ac6c96438315fa34547d9088389c5d4cc15a7249da1377beafaee5e966559063183c05854ead04b92e88534b226d261b48890a81f409c49122a54a781789ab8a0a278111d473f9de8cb5ca97aac4bc8221dbdf43163f0f4a7354dbb24bc777854a7a96bae3c286a4e7fa33c72aabbea91e828a263572bdd72b0faea1bb84dd3176b3bc2fa699d49c14b76c9588ee5588ee55896231d9fbad096d80e3ad2f1de2e26abd1027db1c2e8791e9e792251ce9e96ba11bd91f636cea6c22489243516914861a8f58b65864766385945667833439cdaa5cdc643b08a24fde45ce0265e1239e9a727b54ae4028a165338202635970a1741b1da511cd55c71f1d363167c646323b8050fd9538b78e5a2253c52951b572812e52ce492b31c67644f6bfaea6d24de1bfad9702110647bbfb59196b2a4a7454c7a9ee77977c438671762a5debcb79eb55bd9822276f3a83712ed3bd284e410de807736c3a922a2a944dbddbffb2bc7f1f7fefd91786b465736c3a92f79afa7f18c9658689145ca1a9b6d086b4b6d4236237beaeb6dd57a9ee779f7de52d52b149398fcbe185b335566372b8c2e348131c698a34698000d14a080d080031f2c81f2baae037ffccb0505a4860dca037358fbdc28870901241c002264fab1d9e6944a29e5386e75466c1cb5ab6d625405f64d64987838f52cc7c850b85c238bebc5525de3cbf53243d80ce7e86242c6781c43d8057f74bda816dff56fffdfde1fc7ffdbd99076ddc2431c7e0b87759fa267a8886cad25020eaef25b3ccc3b9de44918580c061683cd994913a65c7ca41e06854c3ef9240e3b60b0184c1886a1adf7de1be394f1d6be497bc959b0b4c613d9c978023b8139899dc4d49c9027332737e3388e528ca39f3c0b8c731689b4beaaabbae3c9a83a194729c65591718b4ec693f1466babf58e1cd5cd397f743d40017112759d2863c68202e295b104ea8251bc76cd92454c29a5ff943efe4f62679a87891bb3d6da0f634f6b1caec0dd5dc39db7715e3792c6d0ea1a8b91611a26b23b8ecae2f26a1d49b243b4227a41954f6aadb5d21d33b4a51f42b4ab27127d2b8b98b0e3a6f2a7aaee81c70d3b6a0553b474e7a7489f721cff2a7afa12ece03a7e0887616a7c83d0c167b6ad3ba8d9818d9228d060d75137bd8abd25b2e8b11d287657d06ad50c49249216f3e430aad7fa43623b16f0c38e1dfc6439adff154d694e73465e6badd54f56019be4e1d49f005fc171b040800c4e50a499c84e553e30b6e3631c08927625ed06dcfb3ff7dd71af0ecbd9cb9b1c93566bce17bec86fadfd9525238b44ad2b32a986886650ddddebffcf696d0d3dca94d24ebf005e5db0a0c8bfbf787dd5a6ffff5aa7b624402358b002c618638c31c618e377f7711c473ad29196c2700c473ad2d182323051c0503f808c60258a0b7e51d3eb6097875d1b76d1d295c95c57e692c9b82bdb4621c07bb3aff98113bb5c1cd799a117c3f9262806ee4e6f8a50590f947e9b524a2945e1228f9a527aad2d5bb8e55e93c9f42693d6a2feae78974fc09efa79e02b3ef015daf93ee6bfc59edac5a7a2a38d6050fc97873188316e61930f2f9d0c4e6bd1c1f0303aa3939f3ad072a011ec688bac812823ba5b59fc978d9648a4699a0d283b78baa379f07407e4e172b4e6e97befbd9ac67932dcdd037bb924195f6468c83db527e6f1682feb6432ade7d64c720064f2d5280e78aded4e466fea6f4a36bf93690f76f30ad1b4a847daee3a994ca652ddeddf25e24b9e9f86f0d3a659e86e8abba9cdc471a454b4e4811d18df85549d6c453b59a893859cd85377b24e766d5cd10baa1cdaa2ee3cafadb970fabb581733c369928086e2c0ab52832c76b2ebe9c2edf1bc1ec7845e1d1fb87b859909c306842d08ad9eaffdff058212597499761a31acb5d6432c3f4d6abb970df2a73bdad31c7dbbadc3197d2a9cb5568b2a9653d9366b3537a278db364e4b7957f7ba562c23b3e5f77db5baa115e075d8445526e09d8947bea98a385ff5de7be76b3459d3e53866cb2e4d17aa329178484db434278b024826d18d4d0913dbe38b6cb592d1c9f2d3dd534f5cd219a31346e73857ab0d4ef0894c4b288e5ae53c8bf015dac46ab69a5d90b656abfba3f5375e5db06074afd0398eb3655ed99dc15cb14e14b1c51f42edad9fa669ddf5b4bb6ddbc68d1cc56d73b5c25886fc94789eac205563dcc86d9b913435b4a436754865d55415528deca9374fa552a9542a15629cc29fc29faab672dbeaeb6ddb46494a929892b8db0f761103d5e63e5b0c331288684183fa01c4c15032437d1a08fee285f230c624e9a7530fd24f7666ae3c4992248d4692aa1943f7b445078eba9035d9f7a33055f16cfc5118912c52f2653f4d6775cb816aab6fa89db6230ee4607cbf984c52951d83fc14f929f253e4a7c84f7d4a084ad2380e19d4bae9fae9f0aadf36942259531289fc280df44802101b700ef80a753f635f0d634c494a3a8e1ae30a49b8fbf52b7251242275c04f762fd9b4e6601ec264aaeb259972542d9262643255fc3f7d4a67b3d96ce683e6599ee5599ee5991036bcd6741691527e5a000e5746feccc6fa4ce9b797f3c66739f4b648baf973ce34e794bbebbfde0ff05c55dae9ee526bb10eb2b9113753a9d4ec1fbf4d49531e9d3fd0d2b4992a37e512b6388bd853e538668aaa505a6a565bebc54050e081abc94b13996a23c2335ebff7ef7b0f77158ebe28cee8afeea0bceee820288eee803f16206444713807d8537768d8537790c8fed970238d5cfaa02aa9206c8f2a68213047fac9b3c14bdf8ae8ed21dc4fbe57301421802164baffdefbb523832c44b56186b9f23e38e96d11ef1e76d82066b8810c380c53e3510f1f7e98e1d4af821cbae47aada6c964c68fa67aad5fdf29c65a93682591c250fbbc8edbb0f6791db7612764954aa552a9fea5b857fb8f8b913aa54f7fe6c3c76c369bcd6639c83215fab3d96c46673f9bf9d061397645689fd7711bd63eafe336bc5b4cbcc86c98b0c866f607f0f640079338a5b9346d74bd5a5c92c4903218cc9cd9536b8ee273ce395d296264aa6971ba346be7fb00334b17984b5c07052cb0c55c22c3757aea09f313b65a9cb23da71754574d5b5937f28fe3445752030f172d4d146abe6889d3d365a2be110a0f4e7ffa037f789a037f6c52d8a04401d14e808ac2ea20a8eeda2d4e9700bcc875739fae9f04268a645242d674cac0e29aaf29b2385dd3453946b2385d0ec305117beae99a2c2dafe32fe03317650a5a079fe1a66bba389cc2b3f345098c2bc0382200a9964bcbc5f26a8191c5c6179956eb26f94330ce596bd38e1ea689884fffd5eade7baf89961c35bf1b68e4ad744d93c96c47d8846c301cf2dc3e6ffbbc2fb268fab4e8b03dc3ace1f31e80c30472d0e123b11781cea969fdbae9755da76138745dcd8a8bf177efbcfb9f94765dd7755d87d3753822c8f1251841101faf23c7c33057fb7da65734ad15b08063c0576ec944793c0c539b409ebe1af6baf905f9be2fc7ebb8267018ded6f0781db6c97751b07fb462c4c864c8191a2177c8878212af3076ad58465a4792c0c6578b538c473ce2118f18e39568b5b5e896a298a47473d1edb5b9a8b6d5c6c234b9c433a7988897341a3f512d13c5607885823d57222872132ed058da0b195ed25c67bca6b03597126c23f6d4d30b43ad7df3f1ce3dbfacb75187f3be4eae9e9898b2668694c5c862621a6ce627d768a8cac4a30e4b11cd9eb2130dd21a288a6ae80a8a1900347bfefc9391bbca06da3008c338670de6d1354c8f3743bc5ad1155e518d35c3a9f7a7842ce2d5c8d2e23257aaa620ac459f797287f3791db751bcc2abcf47bf101a8094638633cc90d5e292647c81511551e999a29b6ea5fd749f848097ddbec495c880bbc82269e49b644615342f6314db5fc62af2f6e02868210027c09a80c6f5ebf1d3fb4bb51bd66564771c55e77d6dd63499cc85010b4d021b5f2d3074c78eb9030d6b77f8e9b5552c365a42c69a049ade56ab1f383d6697d72567a8ad8e909d46037d1493dd8a2314d93c5c10a5f280524574440d80afc69ac7d518fc1185d53fb4a3785cad813f2e8952f70a022df9d25c91456da5ad4ad666aabc686f68b495b6aab54e4f894ecbbeb56ebdfeb6d5c3a2254d7b73be2c883189961c6502c00f882821128331954265141398fd20c9561211582f56eb888be5250673491273592c99ac872c73f73277f6bdf7deab4244169fe5c282519649459545930c3f05bcc4e9aa652cf05de0affa23be871e1798e16d1911dd7befa494e24d2643929c19acfcddb72964fbc722b6d6e62d621bd664f161586b1ac8d818c3fe5dc70c0f43e74ff330d809c84d0340f4d80007c833b66ddbf40ca490863dbb8e93c6d37f6aa294524a2955d140c8a2c964da4c26c7b194463ac10a27dec791ecf1771cc7f18e75bcfa628c2dc6f85aaced16c3aeebee486faa040134f6ba0955283172341016b270417d35503b8e305bc7f11b51d68ce1f33c9db5fb2e188dcd91680361f5d59ded3a1025874d537d83d7511d646f5c11bb819c5148e3273b06499224b991d4da160f0e88f507f2708a7fe831c3897d2047ab7d3299d0104232140a8542a15028140a854236252525e5ff3f8555adfdb7d6da14560a8b9642ad2dd44a61a5b0c01b70d682ca2b5921135ff184e132db23c6107bea30e527ac3f5430d9530f40e6274d839aec9992a252c098c5500b4c61b5905ac26bb6c79a636c1ba58d99a7f296e8ccd8de2a85d5bd1eb36852eda9535429ab92e292c3e2b038ac14d50c3d61f84a8a112c313e546c0ea8c9a653542946785ec9298285262c705818b4a14a61a5b0f6d4292c0c5e6d24babab233c30b2f9830b9aed0755d17adb56aac6d5cb57eba251b63b8ca0c9976c8b443a69029640ad9b8ae900d1b347e7a1bb2211b6a89d60bab61dc855aa116c69dcd2c245948a3734eeabaee1d5daf16972431a40c06e3b9d3e968d185edd83555a676fc92e282f467e4d1777447c791478fea5c1fdc2af6d4770a8f616a5c037c03bf3726c59e8fba4f807467a4c228d0447946283c320d672051a2b20e82eaf2e8aaee8aaa8c50787874d61ef0aa501ef4d120f07e4094d71ed0a5a33be06d81d7b529218bd825fb669f675f2e5aea60ec3257b09e1ab7cd70381c0ee79bcd70622fb00bbf66d8825df092d70b7631f192c54df2187b9ab1a7fec6c816a6c5a7a7b6b0d7f175e156085eba4580dd8e6eb95a5cd719cdcf039b57a8a48dd9d516aa190000414001e3150000200c0a05830171382c1ec9faec1d14800c5b72467460421ac963911c889114864190320a10600c30841062c0504dd906bc98d5cb5b609f949b011c6076ad5833f00bc586f9ea663073bc5bfbff7f24d12abfb30f28ac60a73d00822f7ca4fbf64bee1769fec658e61f3a04a5118dc61f0ea5ada1c76ec0d0d99366329662a2adc109573b89dbd66948b4a09b6af9c87abdea60ca317baa810187f15a91c2f0cbc8d9370d7abe0906a2d5b3e5bf8872e3fba166c00eb4490147b69304c1129626d7a61701283320ad915e065f1f6855151f8a150463125261c4b6f5211f96427b848bbeee7b19ed2766cf175f4781f587a6d38e8cb910f863e1c1c1df23c45d7ddb640141f6142f43cca1dc8e2fc2e10a2226571f4927c6a7be8a63fc7b6133697211cdc309fdd2824c4ff73826e97745c78cb9e95fd5a33f2e481356a310168d6c665ca9c45d2a20b6322074aad3da9c25187d552cb97a24602cdd8b499d91c709c40050620e96a10a1bcc9565fa6a549714a6bdfee1e72fa9f98836a084aaad9a720028a41bef533b6e109d209b346e5468d8c7118ad1df1d0c516a6dbaa62709e06b79d359dee8260050d6980e6f425fe0de04fe6d5307d99a5acc15561a93d44363313ab90a94c3ab0fd7083fe4fcc20b24cdcd70e36f38caa4161d3b5b785549edc8c1604c4cf18381c1d6b3a192c65d2e6648e4bf382c6afde827ed0c2c06b858f84959057c6c23289ff0790e3ceb47fa14a84170eb234abb1b16270a2b6312c27925c214ede8b51ec4ddc8e2103be3ee90f578eb605051f48638937bed0ec92f9da956797539ae9cbf400358ec657f730545e42223c0f96fc03638cd71b4513948755e125aa10cba39e480ece9cb707335f55b20754fe7f9a337d8e0d18e3ac8f9fc71ad44ea597dbcc02e4a9b423cff7e8e8ca9ee2e95561029d859944169d66b41077880fc816001c03b6d3ca9662f9cdf477eca7f943cc306ec54c920143b19acee627507da4c416cd7676df1482a1e599e807a2b9dd027e7acd5f0e3419940cfd78839041ae6f881d2b6da0d966b123145dbd54cd744c9af6b5c7dbee9bc6044e99d9fd3080e311d4bcc868123460cef12c91d2542887234bb723e32bb4b4e9189c3e3244d9ce19088c85b43336bf2e098601c2bc75fad08500bb60857a5e22bc62c1b049113590a9472f3ba8ebc744b83c839916014254b839d38f64dc8539d15b7ea34c119261bc449ec440b2891b037644d9fac44dcea2282a871710ecd4add277e0401577f43af10a6c1d79a9a9cbd763025e14f69b6557448c42b32d54e3454338a88b79146656ab90e7a16dbb376fa6ce6f5aeef8e56c2d3157477597e24b34e47f04386adc55cef967b9827cc057dc31f194375b8e7b5b32be48511ff47788d02888332642704d0ed71daccbb0a1859a214a103b72b734e7f022c1eb14e5bfcbb009e7f821c60ffa4166267b3fdc66613beeedf6431c316423e56f150c9a11d0958b6110404b1923217e37e8ac92eccb141e23ab75a951d434133ee528b1c33c6a127393523c9cc8c9cdea971ad57218fe8ee74656e0d07d889ce49fe7cae08f124aa1d011818d39f4dee0b0632ddd72b6a4f408c2c7226dc02dfd82272e806eae7ff9a682a3241ab86f8086dbaed533703db5f5ad4b032a5ec0938301884e5582aa600337cea89bcf570ec7800ee941efe175460abacff7e5f2227fe3d450d754c39d60734876134ce5bda63606dec0c5acb69ff10486e359694ec6183bb81a2d115589c58092b82a015d8c57587e98d8495d6e8a89298a8fc489d974ef2cf721991e82d7ccdbcca2003b3158a18c1de1344b198f184494a728bea5540b4632851025151218afd8177ecffec40f56004f3252acf20b0dc89eb0573f305e03f3925ad60bee111239593b0d4c8619ef3f0ae68b48e70f4beef14031584eea19b07a1dbceee46fcecbd5fc672671064ceb0965c01946942d2e20df939a96d9774f0c9ff5eefc689f1d3ed70648c826d00f7453a43cc2a90bb8c5a47db03aba2c7bade9d8a80c53f41c1397abc1da4797a67cc56eb6f0397cd3c2f6914a86a26c02ea90c0020223fee46ebd63e1b3f0d25d9605c885a8c7e47a014f01274ed6f810747c906f5fa73ede6a04704388b9e2e6c43d970b6a6a5dfed83020ed93e80954b696921cee3ac2db731591c56cd1de81e7b00852e55e460320e8b6fe86892e0092fefb0545f0e0f31732a8ed60c1e4a2af8cf1ad865c3dce69190a3657b257202432a39d4f6099d63bf3ad538743a9967745d8cf58a8a216785942cfece01413407a79bb2aef8cb225ddbfc8cacf2d35fba6e994889a2bacd48a1fa27e52f0eba3329787b237568709fce80bb18862cd6a04e19a3e11fd11c24818d0b0eabf45e3cd6fd38007781f9a3c1339046cb00407b8bb023cde55390826fb411f9dd65600166d58d821415719d44f2553377cb47a96a3e4a996f943594763cd48ab0bc69167d5c892298104c9aec1a5637a8da874033b8b4c2fae73203ef03358ea4c262ffa77696ebf4156fc0f4bce9751a4057a6566663f6fe8436df60dcfb2818bd1dea04681a11d9049c7998c752eea521fdc75118e8908c65269174d5d465411b054c33256ff572d04f8798b10a10095ded0917bebaae80cc178f8ef2a77afc101d4563cf5ee1b20b2f5ebc5b1396845e9c6aad6953e06033ded1bb01a27b713901cb762e2872a08920166eb7a0c168d6ed2e4e2d05cb5b99b97de329ac2f6cf89b7fa7ca5b2c37a46e59b7943c4c6c32c1865dfec57ad392d76d42658491b7694bdb504e704b2feab5d70cb0409a45b0c3eabbf68c4a17b5a59335d586922914766a183f53fdc0dc1ead793e1652ea593cacba02d90a01478a53fd1e54be7b0b686a14d54a3316fb43f92329bb92f4e79190ac0b8357d5dc3580ee58928f66c41f07343717f6c6a4342e08cd0706d6993710208aa8f00bdf9b8fb9cfb377a3f6eab86dbe58c1dc61c4f068341b2b69f81fb73dadb566f6088ea9d12c27bb75338b8395827d3c206b8b3f52a19aac382dc5ca52c04a8ab6a5c61025211e36d4ada797051488f254c7b8315c2e5a26156d127ebbba649abde3409b21a4a94aa806e30023b027c1aa7eb6f17671180fa26be4250d4e37a9307071093944cbde5562adf020107b801685c55f7eaa81c7b8549420d574448897b1a7612718c950e529d458ac42976c134c1a0ac8162ccdefe5eb17b8dd35a9382bc35aebbd8e74ae1318b7228027bb192e7a2f4778f42f79c263a765f37c18e60f0c76541d985d5a5ff0fdcb4dd54051601236826bd121ae94eddc317b8b712006a8bac5297e1017fbf0a02b2bed198d6c87b1b3967d9e4fea004d520ebfbe682c87549a8d8573e67c31f5731cc63d365f2c01d40d9c5336e4c0d72df595f23c05871776b6533a18d1a95f5a652314e1601ca0d92a27e596a5dd0cdfd380de60ad4eb8a5f1af29563e99afa4bc406e6ddff03a5e5088d09332a8ea6ccd44a754c257a19b64e212b5681ae0f74c12697bd94317a407103902b09afc04273dc4ce4d9f00babc02775bb7778a052a270d50326cafbda2081527a6bb7d73d93921e7222e6218c77aac6fb790c989da0bf341dde0d59e9b9e4d543cda0bbadc57ec05801eb2f52bdf0933091a139e149b44723a3f9539aafcd73129e4c14a41fb6b257775b9131925a775babb762d464360a4cecc8a9c931ae92ab0c45603a331b894fce96e7440b3ccddd64ac3e95815076b175324576ce42c10a2189e4db0ef1527adeb6fa38fb972933b009034a31ee36ea176c43873f0a438289e893c41d4a79d33882b9a2acdb6f1248f9044fb9361c2c307514b22814a8e240a251aca632711a6bb890455686275fa37eb86399df2372898515b84cd14a138e1a5306d5c9bd713fff9b78eb4be1ac27a7b0d6a8b40dec98b4a286bc9a947f323de256e58719d539233cacf08f0aa525a78949d68ef7d10afb0aa5f99c38dab2e11fd97f993554a773957cde3662a6deee531c577a008d012e3a07e4da56be60ffc52b9bd28a568991aa58ac2adec26dc569630f6ade58877454d512cec8592abcc02c2ca359027d4758751aa66006637e59229f18b2a82f3f730f846d38779bb0802065d05e02d568fc8d98e01b2d34482b0d3f808fa392c8f57f3d0100d67e01d07259803b2bb4b6ab240ae66e07457bf8ffcb029e45f11c7705ba6f04af3ace89f9f9d0c0b38eb3e864177fcf9e38fff021863e0941cb36bcffc6a303e33b6972c39d66346169f5d5fadf3e0e76245093ae9b3a9bdee8d1f9b3b41ee92a6ecb579d10ba6a969e16bb1d35dc89c9e0eb79a0abe87dc58fe33e3efa7e486b38088183815564953991c150af7bb7918abaa54d23c40a854d62c8782d77da0391b04988703118cd839f497bba55daef8e3726630cce79a7825b2e00751e9897c7f0fce17a46df15c77ea5f027ca2353a90250ec07e39268e543584d18818ca09ac1955c4f0c1c0e9ade9169f09bf28bc1ca5d33a565edbb5444014095c5d4845b04f97aed6fe0a9637db967d54e7cebe3688fb8bff4e276a30aa6a81abd9ec8f9b5f301f0fd50f4f6e055e152a124f1621b78819d52480422b0bd90aa0f7a06249d802b43d4e11deb6ec472a40711120983f128dd61485901351dc84612225106feffb2777f63bf4b099d78e4f104a91cf489789a664d15fd1ea0dc247d9a5684bcb5f0d63b8862bdd400720203247dbc5f09de416e2b97ab08ccbeea19e58299d141cc3c7f16a1d949232e8fe431aad494484bb76c17fec7f607bc73146b326f25497f7544df9f42ba9aee7c94ac14959427f0a784297b1e53a9c4b6b0c3e267cb65c82cc7d78127239c3369cd28fd1e9e3332a487671f48f0fe99473921540a6701f088fc00f1700700954850ba0c2e5a2c0c70064796eca273dd53e7656a98b568c0293acd43548ef4e51c15bdf0be323b0c002314862bc54ea82219aea1ed142d79202a646e2f6a2a480cde576b75a46d96a198fcda52e9b3acb339b8c0a356928241d7d930381c068d46e93baa4308d3c4c063a8384a28f97ba7efad485ea54578432653c197516dab2de81b24994b5be1f393854e59af5a458fd53b9f8a584d61195cbec04b51596a75c1c9afbd4f778a59059627eca353f77cae57306abef19922d5a9563304835e53a9e870b8cb6a0bb68fcb3a433b88a9daaa2275ac7d6d7bdc340cb1fad6d77814e6dda74f77dd7786474da3dde021277b9b386e1cc8cc18f2d408b36d466dacdbd83e4b5f1b30515eb3b01058c027720b5c5c3eafc61a084ee7af874d02c012d73ab3814be79066370f294642091405342130d41ab0740f71d5b3d4ea9d5c471dac37e9b5c01d21e3235110fca3266bc8239ae1b1fd36bef2af6491b7b0c3affcf826c79ae710b8aff28b85fc58b49ebb3f7953a24909a1e551cdd5c4141e0215b216baf0690ad6c02a03f3be67f1e1c6be119d11b831504f51751ed7df9c3175b79fb7fa2c8ef6a2b3c93f8a9b0e92f4aea5742b7fef268ac40af079e5aa6a499a46aef74d920b5593add4a0ec605b91b3d87205f6fce04849ab556f0c5463a9f3199ececaa75fa5126c18931e9af20c4853ccf719361cf1b90e948cf855c6868ab118e333268944a8ac56c0e852c2eed4edba8a97e0ba7a8d86b39d9438b33df070f9913da612af4e591a08642e9aaf12f8faaa13946a8103b255c72a5a9e3419acf7362eccd123bf7c0b33d81100ce994448a3b0c19124284889cd3f87de511540010e23825a64605448b851d483d2d72c95b26b731302d9eeb57db1ea0971b135de6e63155284ef595198d97c642f7c6e0876cf120976f4557d6691cf16a6b1c402390f514f3462b62cbe46eb58450f438614549a2e75c09f172269e7198774d5278429c5f870a583997dbd3c293b3aae73178f1bfa51a31956cf784cf7ecaee8a507f7ec7085ebf5aba5a95a149a9739b45caf632cea3a9bdcd375d39c19d0fe2c0ef12eefd605c0e51dfa572e7bb213fa062daec725de339d11722317690066f9ab13fdea08f93df06886530150def14c666dc6ff33927076c03f097fb80cce60ce7df23e5c800313871aab2318df4631bd2901cfa22d7ae21b4e3aa71f47ad95a9f1aad067dd1f7cc764b227d944f94af679219d70fbb8b88c6c5dafa2848002c27f1e8881a9357dc7c8c95e1a7202708a2781ca9a9c66832bc5fe35abea4992d51a200bb2d27addf761519ae231d3a92db32987c24d275b98c27b0400e8a4a097fb7126477c1b87b739956c369fcc03859637a3b0b74b75438bd9e95304e5e9d11a3f55ef5865842de4696b98ad636894fdd0adacabf451889d042eeead190c3d71ac58022d8b87424a060e81426090c8575b210f1222b63c75405c13f41a8b4541540b35425582c6f0348960512554ef4d7e411cc04aa77d9dd9a41db95620dab0465623bc3b690016c1915745ab4a3e2400560c4b874447ff237bd069da9b9823cef992bb288f5b44a879ea00bda01383734654004e6852e1ece9be038beaae7e1b5829421dbd356b00b2600afedaa42351240d7c50328c235d3f88bfbed409f134ff0a095cb32742d5bae169a72a6b32bdbf0d4d1d66600a27de038b3b111e809d20c10f5ef0db7e946daf2a04b8b7887ff3552ae93bb9c145c217c5a8c3fb821591ebc0508d21c0ebf1ee5a308479adac428fba10ca9f225108a123f5da6f05324146644ca7cf0f05df3f702b751cfd2bd8cd9cc1d0c3a84ec2b0143c52f064a5caafeca851d7cb235202bda63f09c8a1093e45708aede1072f6cdbe574c2ec2f6b4f254918380778c9aceea6405d654c195a315ccef28dc8d372aaa6701e51816de77ad28970351137395d8930d20161512738959ea03ded4753de3dfe873a280402048ace6514740b15461922e338f8c261c4a35f13da2b532fbd2a23d4b23bcd809a4ee5ea78e5b4b52090c1359fa1bb3884795ca262f24c94f2eeccfb2c7fc458b0e8552b9be888dfc0ecb7ec6352b182fa13066012c773f6a4ec9422751694ea48b6f6a364e9ac062b25ae2b8364a068d284ea8b2ec770f3657a1dae0892e80985969d07166327e06b1b96f9f5c5b8f30d548ce0ca62855a97142c53cfed36f329d85b9acc9c18cf91c25053b190f4b70b7ec4467d9bdb1671f0ef84c98b04bb9bf116442b502c8c5978867c4234d5407ab04519c63e947331c9944bbc000200a90ed2766f8982cf84b6bb79b825c194e137ccfd55e47f1dd9249629a1bb6ab118ba97a292ff734324a1cf5cb05d73e335c9fc72bc7edf7294fa2dadfd00f30737a17284354065cc06bc32860bd366f36afbc9b453fde52905e583df7d262752873c3a033d6875a0ce558f1348c7ad646bb52ffa2d41ad513584396ef3d5285d8182f33002eed807fabe94b9e6b05a50bd14cc6639bdd3c5c1d0846c362d9750a04410f22bbdabe1345dbe610defb399df51a92f8029b39b1a972a06d4b8591cac1f8ab1cacac132a077a0c0286c36c851871c265f2d3c7751bafdb16e4e71de012effb0fb854cff6c9801bad112d92028f95e81eb40278c828347d7a5007ee6b6b14e2d05554cf73db9e2f625ed4e65d9e51203e6cb99e6955894e24b62cb2640282088d95af6c1117f1c138f9c13fa35f320a97f2fa88d1da05b2cec389d1079351e8b53d5a77d1b3cdd8243910bd91864d555009e7f6ec32408a46508a4f14e01442c3826946f0f5a70db88c26a2728d02f7aeca769d89bfbf0c68e031ab2b8334470d66d065ac6ae61e7a26eea7e72156c5c5c0afff987363cb45ecc99adae6d4010108c02661bf65bf9eda27af9eb4ae2f53316072329791a963136e621c52238b09cd0c43eb772e140a4bf43cb123b6b1493cdae136e456e83a3d724469a9b286a817cb2b6a1d893c447657a2fa02d3c964c1cf78fc098a0eb6497d4b5686f5315c4fb2799a5368ff521caee1f09229a720aac07130a122a78d1a470edf0506b1230e4f96e84d95c7b1b9de934f43c49ca35892bd6ccb976cd055755ac823acaab6a61fc617f6cec10d1cd4c4b1efe05cf4dc974e85490b3c5d4880100b71a7856b69a4e68efb6091ae43d28b3cb96f410a5a29f114bfb92d815707bd3bd891158d6581015bd149864bd29999afac00c7a62ccdf16a75e448702e7db7c2ac2935294659022260f83623bb31132e787128530b1e4060d64fb62b5a468592cfa4a705174570faad5a8386b05ed1043382995f947753c8c9c44b0bb2437ebb81840c85e0e14053f021149204deb03c8551c935878169b253a136a28eb5d04557516ea56c87f5712c35035c283342be91de122e837b7bf204e39bd0682623a23d6adff2e123c0d0768bb58a0d03e3af5f1055c1fd14662d3883c05c96507bd7e70e21175b368ef5ad5bec9d283dcedacd63f76bd6a2014d6c0131a3b0aaebec9014dc02b4ccc10680320f8fc07791a2c2ccb209963c70d2549013e39b37aa75f3f4e5cfbde4d0687e0020b2fc958d48bbb3a9fccb759bf73fe6fee2370841009c46cea4aedba42dd31a9d013a21a5b862ed125c7d4670b1c032ae654af4dc62fdab3da01c1f7c6e47cf8df7da4d69846d6adbd6703b7238967fc9021d1eaffa5655ea0e22c285bce9690d86b1ac368813254a0ce1bf26fcd9b03b9740c5edf31de8abbcc8b7044cca9d07130c2825c4217d58d7240d93fc35e61b4826fa6c248fe03d474a1df307162f52cef34b3318629706aa8428079796534f8f45d898b7bbd93eddd2390932548e46f58b9a0a076278bd7a168bdd7f544522fedb9d3194537c2bb135413189fa32b20ee7a147b44e7472534ebb06eb0985509b627c6297ab519ef49291e8eddd167c5ecf7f86b753b0eccda23804c4a50ccc47b68b42359a78f0360f9829c6abd0e6c7156db7beffdc4692444aad3ca8db46927da9ce58329b6e710211d9e6ca34dcb55c466ad8ae9d0bf24b734a608aad3de0b0dd2b21c4dbdac1b6cd3c0b17d72dfe24055ce9eeee1ffa5c0ba017bbe114b6a9e3b8b19fe3ce4ac94b9df094e8e6fd5535df01203dd2774fb8decb9ba18e34e734dc610c15b3250492370513a7ccedaa2a68a9d5cdb634730f44d94f6e4b991141b06430c2ede4edf18e6af0a78c15703f8038cc04770fe6ba28442efdf81a556bd895bde355936b7e33c1fba917740657881c18df196f2b67bec3a51b5b78557f4703c42e9e5f0740423623b42b3e879f0f2158d6c6091a152bc5c6326beb3d1eaa4c2068fd6aca76eedc8db9ff1b68020a483902585e838907ee7241249778fd0bb91fd649294c4568e6c249fe68ec92c9abfaca3ff6db1aea59bd152b00e50116ce3e5072b16256081a51ae1e49699ecd5d6033414c21f13260d7b02b7178106ce2768705b19fc82e086461370106f6512f2d155e3cbd649a2c78391ed9425bb157c5ba743456e76bb88ddc761f40909efe08eab1428c36e1c60b38afbf528189e475f7a72155c416e3c8b262b369f0cea21da73bc24f604cf8860692180185a5d909e8ad6de1458727e1610403a6eaa1721501727e58c0d1adc0527aef901f1639f5096f64700703b7d0cd091c91749f983851900ce9da082a235fba2d6690b855dd95b4fd89314a85a7068fde46250a7bb57b2a0ce98c1cbc12d9c1b0695b694c0d94aaf23f4cd857001f81cc2b26f85c6b80c432c1cd1eb2dd9609f86635cc6c59cbdd48727d5377e1f10f33ddf7e3ad493ab7b51303aa67e601350f84f30a4ae581176f6861ed93635098a31b4b036966142e885157a365469ed8b2c4dc1934dcf94e36afb56075b72ee4a5bf452654806bd6a4b069d5340c6035dc38791a4384f4f368e5f1fcdbf3d6c5b69e081cd50abc407e02a6ce07e82facb172607222e6b2410fc6e39156c12a879a0d07291f240bc50eb355d6f2eb55f1f6ba396ac12b1bcee7308de1edf56200975310fae9fa60b8223cd8f2105b400cfe7f5f7806e05254356fb082f6419615be40c77199d6658e73490e7c85d1c9f3d40fbfa65c679c220f2d7b8ac0166a556091ac10d78d2ce2a7809b25212ee3ae1a3448bb2398910ecad76c5532a4038dfd7b2e1c020730cd9653981d836284816620a7a64774ba214841529cf0444b0bb5b5c27a393b430220e6e57dd553b5a3da9fe9a30ea22e8376c00cf4d0010cb037593023c41e35d90501bd5770fe1e0b45797b9eb83f59b52bb10900af2ea0b5f41c9b633a8893a35488b3f5ba19d285257dae252f5b587c15006a9c19f4d04f41e7126e2db64eb09ebe897ba820fe896fab0da8f4366df2c7939662a515b47d2b3f0937f3bda81ee43b6768349720ce869c205616e156fa3d8adf8bb3d7578e263a61ffc37428d5fd80967486f905bc7289c4a6ed5e812d30969cad55d87709f51cea6eaf0c9d94a6a3925b0ba85d5490b7c796aec1679bd68f392b6515604edf9180f8b621d44327785f0b4f5277c03e6248186b3d67fdaa1bd0b19b1c1d60342759eda57336ea6248d97007868a1a14ad23d6147cf22b47f05d198dcad357ca3e6b8422e52d5fc811bbf62e7a4b2394be3f3c84526c1043e58747fba55bb242900bbde58b04375a87b4cf549e64a07d136bf09d2412aee9cd58157ed921c44f7efd0b3c4316b9b2fd281e1406db3cce3f0c58b69cb33f2129bb9fec633a03ae6c25567fa16c6045657c389e3c4ed08651c8c97af2c81a76e764b51418a97b1071d0eed5aef5a018a8787eb351e3b4bc47a8d73b1684da85f4de7f3a66bca3ea6653966c3459281f013852f7f5b47b1ef95cec9852a3681fac0a30b16052fc581b85ccbb3430465f831112c9278329c095d915108702d19b5998a844e276b4b7f767244f3a13d53e06abac8cc9a500326578d4351888f86b474b413a7eb6b954734a888afaf0e5dbfede083d6b4d1d9cb7aada06d92901deb463adc2d3b081aca68fe9542320666569c8363b9eacc555b2df717796cdf028854f40c6a489d25b563d87ccaa2973d4226851fa9007867558f50bb16282f864656e7c896ea6325133189dd0e038675d8fd16a0bd900a21d06b59d68201c0991e00b1c6e3dff7ae3090c4e615eea17ce5d56e2cda9f0481f50fc6c88621353b9bdc2e03c2ec1e04c3216d415b9294d35152bcfa6ab4e8dd4c98fe7c8041a6fd5892190a94505e19203e314d29ddef5df8b49c077086054c0f3f5a1017fd635a63802d3a6ccde1f5630be2e269810b5c6e7c08d33dc960f4503ff0c9cd1c22b1ccd6ea8a3c18f2b00c49f6c97d250b35a6935845004a8222ef8d5b7a5ad2bf4d4627175745212c01b15166c4dcdb0ba00c39e328d4326fc6866f0d72c58afb61cf755a22c09a1d7ca17022e214d47a9c3daa1c584fd5ffe79d349e9a69f83e1ece8467aacf49503806536313160b80b41a4aeff4a575b4119c55272eb9d478f956326f2c31ac2a433062e822291594f4a30c7e78e9aec2c5eaf79e4c24cb347864157fe908ff4c735feaaeb2a5d1555753ec5249696627dee01cbc58f02655a0af4b29b5cd5014976b3210207c3147d9e7bff70b54103b5220df527b6a6fdceba1838cced1947c613c9668638108a8fed9e2be357f786855b628e9d1300728957dd848505bf119389e0bcaf340d73e5151483c1e9917d41a677bc8a731efb27b3db8b12a9d12790853ab329629f4cfa3f1485840be61d9900de46a14fca0faf65a43e52536d62f54a35e521fe1450eae60f5c5bb6a84a6ab4fda47f8a2f14fc6180dfc4d0711ad7463c3c8e1e227bb19b372bec3513a8d9017679f38d45c1ee51cdf8b52a5d1475647032fc9714a91c909ae7214e52579f533491eb9e4bf97560486415b0e43840a2a2e07d599519b04e276d47e5370148eef929ae4dfcfef5d03b2c8cb68c1d8d55766f78c7dfb1cafa15e9d6bc483149e25bae7befa822b2a86059465f44909c277876921ea9606b07eaa74b3723d501ab5e7da2e9e293f8a35350622d02d00651acdab35fc05d2ea661af6445f98865473e9ae0ef0fc4fdc5c092a9a21aca678f9510c96f38c400b4b245d230b4a397baaee9011055359d12e462d0e313caba8b8effa3cf33a724db5b1798f49a71fbca004fe3afef67a526064fdb7cd64ba283af7ff888f7e67aa0ca78b1c2125c535b0ceb6100cf6e48e0147f5ea45a11c138160078e843fbf717b7b4b63964dfbc677674116f990d9a01ae8132827b811f3782e45067261e5ad16d3a0201438cd36a6cc26a230ba01e531d383ae57d6df64d41fc17d6f3b8da51c573486200297c775bcfd34330c00cd0c1e50b08a66771799253f682f7cbb691067ae2cc7dffa7397385a46b3ab61aa21ce09561b289be5f50689ea12d3291060350ee53a5685789bd0c0db292f57c7e92cacc28a84bb4074e33999f58be52c0bad47cd25a69f4340ad8532ae647900d8b077f2f6e73bf186283970b6638c364b180b8f80a942763bf766585afa19c4748208b7ff81bd593c9ca13a9909feec9014fcf1ed909b17545686cb8c3f4a9cf0968ce14e4fb0a9d60dbe6d2b1c80df24519a986e0b21f75b6308008bdbc0df0cdd3d7bc2e0acaa5cc26e095e3191f3a979fd85a07584650d011c5f5cc8ad97afb375f3353433de46d602500d75398cd26699155da8477d7024140afc3fd9766bb36a720c6e678859dbe1ff4e868c8048a52de29abffd48ebec4f523508e9364f85517210f7084ff6d7ca333196eceea55b38fb92231febe1359a0c591137a32b23c40598da61773879e674f137eceb24ed68facec2860ad0165ddccc57e172bc940aeb5089ef568738ea173dc11b4b3a9e28765ff76703d340d25e971e0a27ba4100b5657f87abc5b5caa4196aa090832e63beccf6d38b1772177f8d18897cc0a472e13f898bd7261e780ce3fb6bdb931c9ede6750ae8f3a018a05670b80bfb8acb2245a2c741d17cf0bcf32bd6e0a73ac34728f2cc2b08d1b574a6d8ba650872a8ac752c49f1116d177805d4960003661eb5cae022ca53d2e887e42db5ec7814bd8cc1c3107309fd32fa515b931979965cb701c34730b80879be72bf8af4d9aedba3a7dd909e0e0454fd62113eea8071272a1adccf1cb5e51738eb1fe322198dcadaf396c2d3ceaf5886909d443eb4732931452ea8d607cda2e69cce1ad7fc1a0777bfb651bbee0dc467cbe07dd819b4ea9740741848ce5dde99b1377c09129cf72f9648816179595e72c71d978fe11b60e8503c2e9db5c3cc72c0c2345e587624dc1b2f529eea70b7f192d7f7598ea5b85a3ea51ac280d73db0412ea15587dc295da6ba4994686b12bab0db27130684d6a67a8130819b23171927c18123e346ab7f182d132cf446e609956e953031963f74c56527fd8a3b96df94b104d2fc8f12ce6e094b7710eba0380eb8454d7e07d41ca29e09471c9c617ad2b0807c1c16489d289cb8157ded63a3b84c12452a1d2050a6201c7c847b60a1c38544eebaec1073b4c03d325e0799b553a1ef894933d33f6b087ef0a506c8dd3914b61fc0b58e1c936e3fb8752ed0202d0be04818d00590033f330513a19048f629561927419b53702acc132e407c4a55e0c68cf0ed50f47b8dde244c3cc673989b19f500f0f358cf3bd27253dbec84933268535fb064090f34cddf29ec249c99477a89324567c3c49b9d0a153bf12e6fcc1b290c44d0334c84b26c280f147c1ae5351644d08b522188b468f3c31e2bec5bb790e114f2448d785ee0866540e0a7bdd8dbcdc5a27a9527e84aa83d36411a295813f9f1f6537e92e396a8f2b0a970cb77e02b0471f296fff99a401fc1f789cd7f2196f7362c4e9ac234fcca094880471bb6d06dac45fa77e7200432013d6f98e839fe02bb7d1f203a6bf6a09ba1e4d50e0cb84b742fa5d89346d0544c4e1b21ded624ad69f9ef0821494986179161186fa5aa3696658f4a661c87dd691c3c1114df879b6c54eba0cd629e3831dadd823162fdc906dca71139535ca9cd9df709171106cdaeaf2d3518b6dcdd30e755621f12c2078a11bb9694cc1fd616e85a6b59023dfb1474a1928750a0498d36408ca3bf909d54ae219f00245101eaa90e55faa2671ecf9a2090f35a41ab042b56f01c4d5b44520163274d3205f879e533e8f0e78b27753518aedaf1e0853c625d6b82a9d7fb5398248bde30ceb21ae4520cd5e73bc2ebeba11ef1691619eadb1b6f7b86f71f8110700f1078908cbe2bff9cadd6185f9150be12b05199e44424f3a540145aecd243d0f12aaa6b543d3eb32ef05a25b6e6346f66ad1c49de04f6856046a0baef60115b9178e876851966128e12e7b0b37db5d75b2e883fc51873204071d4ca7e30927119768dc460b7380176c3c3df66067a2eb26379825801c5eb43a415bf1e769fdcd60a0107207c0847fcc428cb0ad811927ed211c8ecdc10702cd2d992c903e9af1b436714f2f6e6421ec14e080478c32061ed9d4f9caab3476027df4b189526fa960f4dd360002303ab51e36e7e9da7fc6d896f4ff351fb4240220450ff402d80ad0851d669fcba1fb59cb06af7cf97a6b6fdee91f67edea7954fb673a600711c96bf211d3e2bfc7372789add31234261cd4eac39183205bfa527a5074e42d1630ac7391b44cd795efe6d68cf0ccdafa36f21f402a50bc2e6529eda5aecc8fe9cc506bd5bd2676706a1baf500d84f6909613234fad0e1ee6c5fb2b4299cae7743de96d5b7af1f05e4b358e3658e4f1b9821b77876722a56364a2e535ffec1782fedad54c5f51f551aeba91a3ec28a330255cb31a7a5a8380b089b2f1e776114cfa6a91be6d8f2f3e4fc9b87bc56e8b3408ba07031cbb7832ed8dedb1558482002764cb2623deff30a84f575498f0b72866ed1012ca1c3385dc7a913b1a2938e9a2db6de22ede0d565efd0858b56d970d286578ee7e3ea7db8c12062591a7c9e727f8ce06b28bb04f53458dacbeaa3c4d97b8af4a334094849ab235b629d031582d204ec08fcf611dd40dc73d4876dd80a4e414080d2fe4614085ae70b8797bcba3153551e59e6e0d68b21b243de186581c86f02e15e81d5f4700021089319a169579345952a9f078e5147fd98e36b10c4a9336b9ba0678d1231134642112c44b4f60d3f0372082461e2797dfeb50a3c0aea6888c00da9de93fc542bc744bad6a46bda1cd35f955f8ab985e906a18e62e24c5da219e618ea9b394b7461377b3affab718b9c26a5f1542ca518d6bbdf4a8022e5b66bb85b1ce3792b7533b859d0378311456b00855d9e12f991e8b6d46599e4c66b641cc4e60d14e6c879bfc7e704503d5d0e7b57d8add59b5a888bd3070f1aa1dd08a6be9fc939c5c1ea745ac6afbf125c55bc000e3326a921f5ee7700b42a30994750de3f4c27c3f35f10cc322437017dd8f80b3e472cd224342a7ed7d9507ec41257823434bde31339189bdb64b9bd5d37ea3df6075e14376ce80e8cbe6ed71764c48c056055b24d1749cdf49ea44254cc5ef17f50483f920806448709f2ca8d864d763123619b1bbb1196124fce72ddf103743e9cf9c07ec402f2aec3f66fe868813fc84067f7a0e9352f7f1ddfc96638d068b4c0080c3130ea6a9abc02d036301c9805a4f3a8749bcbdbc0c497da262b6d6d05afedd0a8a7ab150caabd4880d702202e3bf498190c69c20e5614ecac663895e75a133c8f263a3b734e64b8959b07a162001a4f89a6fbcf0aadbb3eac050a9377e699d128026405280b046730f6790574356e49678688275ba67bd81e5e4f473f138c663b77d384c9b683a0742b2ac0e0f2705610c680033c01e2110a8026570e3f7ba7bfa7aad18d0bb5c1d458c956a70d85417f80857e42510e43d6a451ab266511461aa367ca32aecfc0d21df73d4d77fbc10ba559e8cb1713611a63eda01ba9bb1d768722ced8aa2d7bdb85cf7d804087cae9e7d5bf79ffb90b0a598f39f10879be2bb9f9503f0abe5ff686bc7159e82ee081eb4f1efd6825cd2aeea97de41a364cc31e63b798ff44638904b73b379755e87cfd244548aaee71d96e948c4166a0c692bd35098b4c1fc47ec3d226c2a86767f8d0587192271ad0bee2f5d642db4534f62348ca7d8813de2981c284a7f4a6f2ff0e3d800ebc8268bdb73508e3c674dac89e1b36691387809185adfe82b7d966664d32e178505dd87f0b08ccf59b688681a55722ff65afba1b8002d1914f07ccb4ff7680c35c7425aa5f4e442fee18abedd72c3e3f67d35b77a01d615d0ddc2ddc67835377ca24aa100f885f6384780e0658e1e8ab2093d2d5dc7198ce99ce26de9de3580fc5f286d76180890cdae7e0719e59b0ad274b12eb91dd39e0ec1df031800eece9060f18da8f1898354d4ab12f319e25353c425c6491f0dbcfc401424d1a69ee7b82803b94d313b63ae9dbe370dcb07576edd9d5d2e2ac02ef2d69dc26b5c3a837f01c5a38439ed182fdada8009c2313f4b64b42c329f7322dcbaab40afe7c20151476d06299969bc7af04698b5991666dbc33fe713e637c8393aa90d63ad1cd2a19eaa002159114538186b317636e1dde637184e78e603eab984dba85041649ec4f9d8d89ded29230f2b63d09af9eb53b8be4f579e7fda1aa3c34a4d21a1cc3146d8b4701e14e7fa49fbdcad1dbf1c39eaa858fdd05ace6fc21568cb9cfb6cb2675692a1a43b370d8c47da97d050c48bdee9ae72ef7a532e2dec92ce54cf7214d38e2a6223f82813102387fa4afaa884c28a26e64f58c4d00f04730592b46bc3512059a1d8d4ff1bbd18c449e92b5e5b4ecdfadbdeb22098497d43c386756f7dd1fba56149d8bc1194673d9d1797111b00758ee3d453987a37132992abac6c3cbed352c2e0f0af9364098d8fb24b327a4f402ace06473aa1b38305f51d8de756e163fc13bc6465e0339fe516adbb570a819e86dae42f95701aa406b772b869647b2775158ea0187f3f15d7c3449dd8daa9821a0f6c7713cf399d1c5c331eb088ca34d0a64eabf141ee0fc34f893ec3519dd961e1c764d78e6b4eb5e1ba9092c72aed28f266425f376705306e22390b5a7654494af0a4bd28b076805856de9c9592ec2aca3056e07c628ab0b9339466941dfe10356fd7efbff052bbafcf0d8778f918e2572b0846d3e59c0eb8157748cb188015580b39094ac9d692d1a3643c570d6eed5fe2cfa530137caabffc6baf51c5f9b699901ed0be32e83cb27b030a40baedbc3f225986371c82bff8dc94eee6c4e357e1e9339530e6c24844d0e7b216e192ca94047b22c9aa866ec7d1e249b96ff6b3dbd142fb604dc2f94029620eca50ec791d6e496c1d4ad2d9e86a20f11b170a9883032e82547b22b97fdbb0057454c61d4fb39b1d031dc0379fb9865366b276696ece1d389d8100c4b7b45231008ff2576ec47a4480ce0989a81f9111ef30caf15c8f4550772c9101750fb820ab923b033eb9e61821261bf7c3928bedd80413ebc0439d1610f5f8bf686940ee96e90e91bbc23f34106f487b922df2ed1dc8616f23e490a5a010e416d734fb4ba52f0b3dbb173eeb86cc841fae741c7144bb433612d44cece2e8d2d30bc84f94733eccb7b59213889f424e21f1b5c865a56d1c2a9cf6b79f799613850f7d6b626825a450f29556f801972169832988045929269c86966da19a13ead6bee2d2a67ba7af81e3fb86acae8ebbe2dfe963bda9139950e375129209be2d8f591947c803ce1df7be4d12eddf690f0ab63c59bd24f626f54495dce730d0850eacefec4f6477c8b6fcbb2a07681bb15a85a6ace074f8646ca3d8772a84ad35e1872147a733483ce05ec431ba3f272fa8fa89dc99ab2505e91f3b0cd2054af0ad0b9db298970b20f377b976046d996647c1e608da886a5d7fb9d21d5fba59979af1bb822a950929c5fdafa0d37fc9d1b73a817432f5b762a938c36de844fe9433d373cb0b841a14728f350f6b0f223ea7c024613e09a4af5bb8d78d567b1b206d998bc40c554146f75b51b33b17fc252aff12225cfc8f6f7b366c72f392710a2700661acd883e1cf8b9b0f19eba0e34bb6ef8045191ad2f4decdc1efa489fdc419a86102fd64066b180bbaedea2c717c554cbdb9a0daba6a34327e81a9790a92b12f87253c4ab74f9ccc9c7a2504a904a113a82ed6921774fa0a39019fe706b27244f4412cec89d88d2b00b5252e65a961809405de3fda2c05614cb85d8c054aa8a96a4971fcf1529256c083a64162730d67116a9bb1c8a638e3c674cd30cfa6cc62116a98a1ceded358a65474df10c1d3d8563a661f85d0b6ef84132c943dace18b9011ded24f68693250cde3a0719673e278ec4923251c844e6a032d303339ffc1a9cd06abfd16f95ec22b7378147f880d4ada6807b36e73179e6c951289ac77d70fdc8a2c162fd35cc9e1db60c954ffd70880fc1ee929457356d0880547503ca30b6940f31495990fbf1e19ef53991acc38885ea4162063640f81e03c85570b9c0ce9770f30c221630279c8bcb56b6be63bb460e2b65cf1f289a49a72af320e842971effb29c0f32399c3b78e56269c6ecd0e11ce76885648b6de8d11f8d131227ed45845ff7af33a209b231aa2001736122f74c8bb28e220b74aa12851bbe8050554145f6632ff6cdb21cc30ab9c93b9fe27e0aa24315b6e59f43dec198e83c9d837e66d310b3ae0edb1cd15801b0ec6099a6646ccbc84fa13a182b091618125a368eab6c6e5f2a18886d4829941dd936482ccd538d87fc38596a3694880025afcb7885f11d87275abce3ff33a670068d2c1a710f090a5ea41d954f040d603d62afbd0d2a4290c035a6221f91e3339bb5fe10da5d6f18c24893c1137dcbe957c12cc8f6f3efa8d2d7c99900e04e968f0181204150c033d14d600c2b310e92b5514691eb166ba4a9b7401d888ccf73780c698797e8eb9964884821995cd4d34f1f8d9ba7457fd9d4e6c414199093b961acf76c4492be47752ea61aff341e4eac1ba46a87d3a87bbc96cd1a4c46c0268dcf0160d91a8167f0a91d7153ab243f52e9db3efb7202ba2f8aca91e3fbf2de22421aeba5d63aaa844c34fceb8d12e512c5eb55cc0948cf0cbb5efe18d4a6e4b283e1b89cb6b7bd2d87803ef8a3ea5e3975831480aded3a447a8c09e52727b3fe508560f6ab067c6042ec4df6664130bdc765303408e269c95d8a9f11be9ae23db579f64593f3599a732f4a0ab7aa3e887083a7c81930685fc374a61e63f1f02c49017c1df540fc73607590963a74c16777290fcab4aa32dcd68941cabad0edc919ce625d1226afa414d380d4a3d8401a4d902a91cfbe7d6d5b2f929704999769f16f151703287e6087e6d1199e19fe428e6086d21a11df1681fd6971de30c611ef07a4ee7d7a9a29cf83fe668ede46b4ac149608416577aecb0bb5f30087deb6011af6afa651c29aee212ddaa4a0905e9fad8f5cf36db2f971f8192ee4bc778a813db7712270bcb598600dd9eb2660a77420bfd95947cf64c46f494d7d06c12607abf3864fe74844c96e99084921467b581108fd5419033049838502538e832d9a096cb7fe70de1efbf58e3ce6e2f761d8a5ceade56f3e67450ebb70045214d1f5912e02af256c841286f8a5de18aebbfea3a9105f7b9000cf044fffabf4fa042042428289ac053e60c5c1a1594feb3347bf413b8709298cc648f83b22c8b457befd294070f98e462cbccbc3c33cd2013f52327d5a76b6b81062aaadea6bc8cac96e433ac28bd4229fc2d9ada29d891664e3bb33bd81dc764bcd31433ecd97c3d5e5fff6ff803ebf5e33f81bd630dc2c75e8cc88c9efb29b37201d3dc7276225f7194eeec3e6343da9b10190d9ef23c01cefbaa3971ce939bd2c25d912b73947b1966ff2ebc693703eb7cdcc46037e8696a1f33d1d197970609cae5352498aa46ad81a5159951afab1b91b648c3bc95365ce0f7d4be88c8facd55b97b93a12e574e919e676accd9239fdcb0579028cf1aa85813d1b3e4abcfe24aa87a8d3608fae0b027f32da46be89a43744c6aad2ea18443bb3c91b7921c46113183068983116b913bb0a5ff594210a08ad17e338a93d8c93e5b7a206363123cac129b53f4629bd1ee5fa37bc6607474a2d207f7e000893785dc57b380bd6e9a35b0b21eb219c398182ad16c940ea371f5235c249c86b978bdc068d7b14f915d8a16077433f860931dfac83ea22a5f3abbe38a348ed2741cad6733ebe846d262942d733c793d6775c65e9cfcaf3b0f51084d9e70dc63181618f209f279e291c5b61dafe46206bc0a888a5acfa723e4da47e5402c0bf732e2b9c27515eec7737464306acffad663b5be9bf8346932e3aa865534834dcf3857a02c9530c6182f6f967f27a92548e9a8a64b5ae7c894f83af21490e7c2101602798d14e78a22b0defe7d0086182385e998eb44c437c3244e96196bcdae007bd250372d209acd339dee6475e89ef69ea6b1815cf92a428d5407998a5ffd977d7e5aa42ab06c9e2059be2d35978d9277887edad7c5f83c4791449e291c52de803e4d727fbc1d7dc12a7f27e6f4e45f40af7578830c6e923c42f8890b629a7c33e47e68440833c98ee6428d354e6fce4134465e58869b4489915186238366d8716128d8ac3f7fbc0490c7e8408d522018fd08d702333fb53a5952518531f2207ca7a858a0d03ec97c3d919be401061bc1c7efe8d55325fa1216df0d8d873da29c05388220eab62e6803e205dd8d183632bbd90eed51dc959a1b2c26d870f98a89b944cd488b2c902e7e906987adc900129c4ffd65c3b48498be2000242707fa36cc2d1098407f99d9bd166b4564a85ec04fe7b8c4989e1769967f351db5bb42fcf7a4ce29f7424879899a8cf759337b9bf9e123d5a5b9c81de68d03417bb3f66cc66dc25dce4d41ac1a404bd621b4245b71e886f0c2127d722d5bdf08114aa16ca15fe38ee47b9246903893dd433247ce513e893da22a9f9a81ce5ab2d42735b9b4f07786c7d4245b87f7365ce51ed517619a4e0a09d395a6db103cff67eb14c342729506296e71482e9d21a719dedf8b4e59a0590ff88896fac76868c929dee4565c0bc524b3d186864ef674a062ad8bcb41ae9f3aed936afa46ca7fe460ee54bb0567929f8da9b95375c2b0cbac8a245077325e178b6088fd6bf1d5dc9b22a3406e8a2c3da6bba87575356c2681605c7e58a96b9ac6037e08ebed0d034c2069f4e084dda3932a5fa73664830d6c5231d0149b47d1fabd7eea694e4a24a33a2d625c758f7c4f2cddf14586c91dd5b5b51dc11358ca62ce807a5ba9172adc1641776a8a3cf251fb690dc7431e812c2a1ac0d304f28848dea19f620fd5298c0179247b40c6208fe4bcf6d159fd67271a2b9f0837cd0f17e49188b3011037a0649d05c823a84c4a68c503b21216ea13239a019ebcd5880a428b1b98d0731543a603e8c4a5db71dc0fe2032d15549b95e5320823bc174a87ec4b7c20ed99bfb4a0eee4a248f6cf38489b718a06f120b45599cb6326e5843231fc3169b5110a3677f2403133d15d9605801809113551eaa74f8ba1a156297062d02785b0b315c9052d3544444df2a2fe029cbdf9681a366c5c75f2976f301e34954825d155681199fb7e7aa422e2716e5393a1c022482e51e4fe8c922962960a678415bfb0932546cfc2262fc48634427523f09116f59c6062b5a2f05370765b7b65831f5d032689d756a4dd9010d554c7986e565d0f2dd004dda1d3193d47a70c51e37a562be89e4d654facc25f2e0ce99ac2f16fab9a2b80868156e43cc744b6910fbeb6bd2a808108817d84d5a225a7c0f3400071d37068b8d9bdb7f02ecae0aa8a4ddf4e001ed991d4112682d14b78bad2149dd20513a6d80187fd50d2e55b6caebe6a64a8ac72067da9064bf02e07b1a1d49b60aad548f1fde8758ffd256dc7d4aedfc560ae00f78198993e0b1cfa6f80e909236c02713aa473930ba100e484231615589f48ea8a394300a6d3fb2949e76e003ca69774ade9f9a934b8ed973c315aee66d8c7cff94382a923804bfa6d1a92b3d0233f45e56ae142113baac40413a19ea714664c6fa1a600e22c9a05162af81e6dc4f2a3ba7b2a36b3584e814dbe0a302da29ad8b5fa7427d0e1165cbfa44021f703e7a7e93193a76f3dc187620134cd7a35a4f9774a81cdfb67e546a2c3e134e9ac100ded2c010509ecdd211ca7ce8e739e17b29837ae20e288d1b3917bfe1d536cd326cc2eb1e7c9fd2ab58b0e17dc06c3cc72482b497d018b8a8b40d5736dcb578e7c6109a4238999e7cad5524385dc8cff864da9e293ff38d86e9c03575d1794a8c638c2252becc1c787040ae2761d7c2af560a6385b2114048e9642cec5354be8ce441288a5e66a4c1dceec91547204e6153a77ffd50e4e10fc755cc92ce0d91101ba377ffbaa29a1ae9f73518579d8e1d62e57e9c249e07e2861f5f3651425cc181ac86640a18bab383471b314a4d9798617ee4eac0a95b4d76559ccedfaf42bbd17c7b9e7ce7472059fdaaa43f26e9264130f5ae8f121cd6eea29b5d0b9a4353e4162fa13730e87d1adaa9f0001fcee55a0bd99cc0ff4cdab34c02b1c9913af7fcd4f51b9e2b47e6de8577de4571382d3ee943b471e1a5f7a199cca411d206e4359fbdc6d7fba86defda78ee0195d8f5552d402840cf10101816801c31b804b947ff40fb83b8814b02dba42546078ba36dd9be12761ef46c45162f006efaba328c635d5f788edaf28d6b9d899b80087cf94b5d23d93bf65fab4e00387dcd779a14ceeae1300cd828bb163d3879dc6cb1b88f633f5b5c04961ccd4f31267c58bce14f8aa45bc50922ba74493513a7889255424fa4a84e526f1191108ebcefddfd42d9f30538fa55effea1434bc47e6cd9591d9b1c6055e4c0279699daddbdfd027f0ca2e576a4ace6f36585f65f02bcd9bbab3652ec63e43109c9945f6cb49e1b1a66d008be0be3b41b66180c8a966454358e8a762b40b20ec1c7e5e3e1611cc37c3465ca82356d77ffa3014bbd51d977a01a9e8738cb15bc6f44c0e85ac34316afa44cae26999a2f42ae3d7fa7faf409fb7a081163a2b9952e57282d6c910e1004c22d003bd3fc80c32828449583dfc41b0a87fa341e85b8d0ace1aed36e6d08c6c2785428f4b0618ceb9d63ec02914cd09560c0e6e5dedd5ccf49df9cf8234133a5629c96a5f0900eb79e922205d394cc70204a0f1868199f8b9c6ae28c089e1035478be68388a4ab3ae64387d3229ca9c9bbb06ea7e4d36ee6289947495a4fbb0d895a26c11f0671bb0f42ed78c293dcabb47f9a55081f4df62c27f0c3e4dc1bfc4e4663efd9cfa3c536cec50820983d527c00ff4c804da01af8b411e9aeb708d309018b0ed2251a3a6c028c44743a70e8f0c8e3eebc78562628b39c6321da49129ed143bf054d3c896b01b69efda754378745f164081ecaf5102de75a94b868036d533a73baa871607ce309c67b72315d9b3ab4beb9d49c677696ba9c0b71a17053d7841cb216425234eb648cd9f24992b3065ea80b6cdd81eb8de9efabd6847a443705322fed1f5ee3096521acfdd89d8c30384ea029ab579575a30dad9a603cd126ed211f06f6af6db6806e6fa5c9a65c4af566ca09702775c035dfae43ade01a790b259f1e8dcaebe94530ff2f3950f380962e8205117712d2cd24d6e462a784eebf7719c3e4fd3ffd2dadeddb69452ca94920c4b056f0542056c42b1a9c44118580394734e29594c5b163f880299688102ecd22a98269c9327c3fa19f64dfa64537e3efb88b029e287c4bec378416cd6dab43256524aaccbe130371c065e97e1c676dc66c8821de9107539b00c10d5e8afd945c77e2f22243d25a5ac28b3b12c6848126b6d02c330ec8a4c87c374f75700aceb1e204c3f47cce36135441150480ba08cb30d340ce11bf7086305e8cc816934df6d7b1a58940307a161d459b67d306db5a100ba1197cb5a6badb5368628d84929a5945a91a453d24fd36f57bb3e9212e1a23ac00ff08db45be84908ba8361d8a725e50451d334a8c19d1ca21c3b3b3b56278532d25f33db30738e666d3d596ba9c41acb423d6dff3577aafabbbd45e0bb33e7f63601983b8f4ccd5a6badd5a27faaa6450f02fca06e04f26a4f3b97cff3fd1829cd14c8bbfa61d0f44af374d2b4d04392d534106807b4b3699aa669ee9bf3c42972422267ce39439f8302db83405f8258804020bb694eb82f9cd9dd7d341af96834f2d16814fa1887e338b90038f3a9a5118c39f557a4bf8f578297c3178457b8fdfcee310e8ce1170fc7b994125ba96e9069b3e6abc958cbdebfc4fde5a691052f14e90710ce63a40b5213d997ec83b2d5ac79bd5e2f51263596353e536b4dada9ffc54cd3361c4af886c36fa05fbde9deb9ecc836fbd6fb615f7b20a46cebbcd5b9cb8ed06f1ff2bce582790ff6212fde98300fe4f96bf3624d458245f90b2bcd1a0cc3b07e53eb83c129788565f1e7dc7cb8017e3a2d7b67c632e66cce3967ad3563b9aab173560d853557844cc5cccebe5ac998b56c9bfe5c2d8761f914de82005f702e7777b73ec353ec10809d53d9ad07b8fd1ffcfa4b36c482d249a97438a54ab11ce92fd51f74c23d256ecd8eb71bb6015c6dc5f2998f671384c32035213f0595d4b382363ed3b3b919913e9f824aa22bfb9ad8d7a73f85f504c2c01a4a2c0598801267715b0125c6e2ae522a9dfe4457e42e72918bdc5d4af728a5bb9472155acd0089254859c2ab0b287465b8c50f9f1789466e6f5a4f93651feca3d3caeea31d40a1ea7382a58fc17a21d0922c537a9a1f3cbc7cf4d72a1d3bf827c62f25571d4c43e3a4947e56aa101553e798708a9c90d0d951a18e546bedb576421b9bc9e5331a8b09daa08151ecf21c955fde35ceccf57061e397e0ce8dc364056862ebb5d0069d619d86693ed2306d837b0063a00dfc04a4d1dff34bf616b93ea08df92618d3a61e538fa9c7d46382a1d4a695eaf51228d812a3765093513b45ea1fa11ea3604cbb6cc196da6432b9641a357b66e618e3f6f97c3e1a56b396949e29f69503293a442e4ca2238403dccfe2e18bea0995cb353333bcb21f80fffd7da10a8acde56d021cc4859fc99fdd4d25cd97dd8eae21cd875d09880b4910af7c171cf874a5e75e09084b2ac977c1e173a5d743e8462f89f9b00957046beeeca0c340e6c2f2f3857fed758ff396bb3a559392e15ed2525c3d1da5a9dc7e0a9d2dec228dd343faa3f2ba3d537c46cbaaf63e035b652dcdb28d9f24127d299ad252b3476058be3a704287b8fddd3ad824e09836c2b49714d8cf76c13269b5a57cf5be7a2656fb52a7529de2b1bde6f556011748da1e6688542ab5558049dab78e2ac364955e29f35aa5f3a9ea71ab2855512ae34a3ac5a74eb9af9cf5d9c06a5ff295e79204a8bf5396739856e9f4474f3948c0d7fd01be384c4a29e5e7f3d9a4263729652a85615a4b8abbb8d04e51a8c4d128954afd7fbc726a446eacd43e8b544a23f22e559338c0f94eb63435530ace149c29f8806677107dee666aeeccd48dfe25edf297341b6c604883e98f6aad72cab490c9d765f97a0219c23959074f9ea3dbcfc32f8f704657da550d8013aa7780950e234061c23979328430fce18fcfd4a0fa78a218d8af58cc96159ff67d6badb5d28b5521387467222c1e3c582c168b050467e7ff5c3c50b55ead9e292e2a43704c3536455aad2cca90afd88fa2b3765e318d28a278611a5f56bbb64db778d552a59cf4b76afbb04928aafa4bd2308c04721430660b60f89086b11dbb5e0ef0c9303f1d5faa7d34073d48e36cdb5a2d6715a7689679acc3ad14b75a8474ac23596e89534eeab34ecfb8d473e1d56527fdf5ca354348b31c86b70879dc6a61f5f369b5a40cad58545bb7d8780d86611083281464ac66a3d369d6671650c8aeb0ac84fe411c4739cb50a896949e292eaff9b406855d39aa1cd491948e93168b97400cd3364cdb72dc733cc7dd861a4c43d6b45c0cf468fa178036ec601a5d9fd5a26d9af7fd386a8c76d450e5e46499bb9ce84009259e88c2891b181663d47070b49012eeeeee57c0acb0c46879d84192735c8b8cc1c2cdf02b5d93583a26b5703046f7835f8005234ca41507a3e5573e03a20623b4483c56ba1f407c4552a426b014f4909aa0225ef951cba9e557be24923246a72d2bde8f9695175e00809471e55da2acf968d9b6f22dde8f965f59d9b4ec1363ad2f23d6d24152dbc87e0267a41227b83ac1d509ae4e707582ab135cf9c94f30c624a3e73844fa13b18e2efea43f70924d2d6e38f85b3a48925ddf18bc45cf8f54b1d2e3b1c33929a5c42157fe46afac625831038508505e8ab48089107a4840704e4e45a07650291d5592283dad95932928d49c73ce15146301f2137066244407c15f705b582cf8094f0191e0acac8c45a146422c0f9f097923ea9544ee8d3c8ee10aebb7bf4635aa5152fe0d9f3b7c49dc3c69c585df851f5ec983511e1f49c520a29e3881abb5b2d0b9b21285155a402985144e1a6e7715495061450262860d70fe6d344c7fe45899d8d21fe0d9db229c4fa79328ad2a059caa39279cd0c79cb007be3edcc03ac0981d600c08204c7f152e7cc0981deb03df883f2f84e9c7b0158ebb01633421ac8cfcd5548591c197d20b73ce39e79c73ce39638c1c03164c27252f98e4cb9b35269fc1e12fcd1a13f7166318767aa669c29944bc3065ad96992fa7a49415e505d787084f4829250e0edcb4ec8381e0e83382624b8c339a2e87c1c171f9cc7431f45899b3a3d1880726564627d404050fbc3a4c7c4a29a5d86703651a886eac651f8c320f664cca478bc8cc31debf30b7b905d450bb7bce1a2131468e2c1e3c6acb3a8bc562b1582c1e735296ec0f159c625ea5544976964871f5d028ec10420fad7cc99f22996207fb8470b89ce7727cc605fe60f6b813576b475332521409a4296843724dac92af8985a4124dad7834b5d35fa7acdd70c0cb344553ce94a6344d2372228283835f96cf6611aceb250ee7e2345704ae68125b2b0b4bc7c2e170278f2b42c5a66692fefa59ba0e5e08636744ad8127415bd0d59afe6c86982a4e7f452a12fde5a0ea91aaa3eaaf698a77e62af5ecf2d5ca67688aa65634b5a2a9cfc634e5332ddfdcd2f50063e7da9991545236d3666727958a71a55d5420a594524a598428887cd658eb4dd337b9350c9b1e3f1faf9a0be8571e08c9762edbb33c1052e67cb38ce3be6f47e857b66e87b59fadf4b4e8afb8ec6879966ff1bc877e0cba4229a5b304a7b8e1b683242d7ea883a4acf3215e49435fd26c0749a10e85680d8528dd1a861a30c7c8cc912311b63462661e8de6ac02123d3e7e064ece39e76ccda003478f08e01516c2982a128944af39e784ac5231ffcf69844aadff1b0ec86197310ce341c352b9562a79b19b9a17145be29fe1842df12932e5affc212756764fece779d3f1f17e3009f39a5593c7e796e04b02b6e64620d65cd549759a939979524a55aa5a57ac28ad15209898d9d95128f7effbeffeba119be81dbc3c5eeeeeee2c11eb73160adad86a651484e97720976f5d0569b1963f3d7c589691af8c42dee6714a470623649ed6b10ea738c5aa98691a9bda1b14a9c73ad80d08231fb64c8a7138ceda1254dda83fbc5e4ff32c8d81f131bec73ae9639ec861fa716855346a118d6ab415382d512702be336ce167e8c2da16aee3b02c782c7481800b0d2c730b215ba369190ebd0e9e0fae72dd79caf5872de5ba6ba08fdd0c18d3cf2aff50e7c367f8b4713d22e9f5e8c027f6d8090c3d16862996bd53dcb9f09f5a86fb48d75cff1e52e35dd332d57d8799542dbb531da08d7ed9a1eae1836de00be1f736fff3777fcc9d5906296c7cf692bdaddaf8f3c5122538f821b320e4ae72ed9538f7dcdddd5dfe8be35e2f9fe127208c7c2452a2608c3c4215f2a1fcf9fdaa96e31bc992df5de9f3812ffd493d7ec118f93f54b9d2004aec9c06e0b15d9f30213934a912871c201e1b5f96aa49fe0f3c7c52d2ff7c868390df9531e0b806c6c8ef2b8314164544065f82607b3ac3fe18e7ce27d2df8400df0d0adb9f49f718234b1941b00996bf07dfc9dfe353c5f2df40d3dff4214b3e7a1461815cc8249a1e74cba14bdf9c6b3334b165b8dd636cade39dfee6d3cc2f72670f12771ab9f331afc4bddcf9d42b7df33bd19d3c5a66fe7cd10d1d251177dfbfb4cc7c9e256ceeb78ccff9bcc2a074cb74777777b7b7a33fd92c39763bfa93abee563afa9352d2f80c24f1c05d6e0d919fe386942fa57cf9523ee6554fcaa79e0c4f8f06c64819a4b0fd1ca5b4be566b77e82f3e2a4694cfe80061e2c71863dcf10163e2ef58cb990860041b1f55abb51cf71f9f238c10f66899f8f1695a264619637c0328b1d0c68de616852a666666f6e2064a82bda251cbb40c44c215af5baa01554c050b7994a072e33f840e1dbecf889c45567a37fc8d1e1c5d7f910c41b01fbf880a2b84d33967fd7cd605c95e73cf1edeec3f9f2c7bef3a93b2b3af9f75fd4c6c563a5b6a64e94e64f04594b2efeeee0e6d4ba10350430b5126b20379af45505878a794d86b5d10edb718b5879bf618a6691fbb8f97657fc363f7d1b28ebe562fdd814b42377a4d8610e4c20d87d07557065f644062fd678ce4c0489428515c3561e4c0bf1f068f80069cb909d409f544ca932a44c018428321519a70c3842541ec4b47e1297262c2102618c16c7cc301fd088c0fa30ff1c2cb176e35baf81dc46089ce0a0bcd245d36a4ec962e9f81d2957d01615a0be2f687b297105b9aa75286037efed6e8fc89641eb992c59a37aced4a29a564f98c74b1b61b8a70a9c070a467a4ca61fae433da14538733e9f299793a4917e6d5cfb6ae3708efa6fd3c61f1b5754e9a279fc95ea71796bdaaa6c1a0c2324cf5c15418a669a690165fa57df3f5d7b7f4dd0fdad864f81150e3d59bb7c437d0d1ae28acbb8cd3094b08ee04a90a42c8d4099d425fb445a5d02ade6ad1700e9803423f996aa89643dbe04e0eaaa22a6803ca50411b09f809b74e49cf94e6aa8dad42454555717a543593a85034458ff4e7de0aaa5a86777cf6f8ec998e831ed172401b4ea750eda8a88a1ee98ffe8c0fc751144d5115add57237373737ade8c34b7f257ae32faf7bf0d2235e838a18301a39c188eb5e11bfa1c4e6a203de1d2f3ce189205bd4d490848030fd44d880d6d09b1d7b5385d5810d50155551ca9d5a56549090a194550ea0654b9005dd59ce6245ac624f695d614bc418679c52c618a58c31ce39e78c5dcecb4fa553cb60224ca41146868c8ad55ab3958bca0b6444ab325696b9582d293d536c4c375c258b3c3b31dce05006aff526c51b1f43c88620a9dbc29698f5b133cf2cd1c96178e5331b54aa9d125495988b84ba1c3a78d0f805e0d72a6f89543b20949af2838140a0ad0126179402ab50ec93655996b158cc76d556885a7304af152c04dabe0bc99de88a382b272d761ec855ddd326bbf3c1aa17c3c6a990abb66278d16a68c1c69eabfa5c8eebc958b70402a94005f8c41e6883050615007aa8e4fe7ca66df4257bf06e58c5a2470dc3b28ee1575fde6a821b9ffdd3d53d96fe6e05b312b208ed102502c059f81373317902154970c26689677104215b7881080e5a6f4b81909a2350aa2304598111448e308408975a12a57472355cc84345183774210f15ae990422a0b0a8028522f50a52caf7cbff99bcb070bba15b2c1186eca0a08a0bbd9041163524febf3ccc60934b3bf1f04391cbffa365986d580709991c1e82546222d726aa76c01a4a2709b7c4463e1bf0f79771e15291465ce09881e438c133852b58c03385174e056f00055d64d1c51298d859024906615b38015cc8f3832eeee7429e1f4cb925fb7c67a98aeedc41746327e446a7fcd42bc58efe4026f71319446c8c0b79a09822041456c86062e9853c5038e9d931badc703473b3cb18239785600c3f44811059732fcb1f5f42c97ceb0d1e8764ae3907bdb588f1815c201973bbdfd06a6d19658d860c108946114689d576ef9594c225953e290ca4c380099754faa43060e2c6291d064c307b0c63e447204b74bb7b4b9cc1cdc6a08cc3f8f0236429e5d75fcb979c778f61cc7c384b40dcf83ea11c02074592ae22b52b6fa9e54a1fa8e0ca2757faa005051229a5940808c2054a8af0c1102c8860c8164e7edff7f2418921820bc24d3158389081611813381108006062c57384275b024705140a1704a7be48d2056b2f64f2051300f0035b2f64f285162d1c012e64f285155a0bc9ceeeeee2ee5177772641bdd4dd5dbadb3954a365e3852e1f4a794466f6cc27aec4aac4ea64f54c2a4889559fd19f0cadb87b3eba4ba2feecbc1d1a462ac1c2d287e5c0e1775a0718235f3e0c9c29318c7ce9f570ee952018d02bc12c5cf9256c65f30163e40fc009fbfd651f4c7e9552d69bb9f40fed41a06f4bd7953f821f7c23a194b279860ef99205a35c39e35fa62be341fc2cd36e13dd7299bb8b71993b17eece856977f67277322ed3e72e77d3cfa676b93b96cb2e527e772eb2bbcfe5eee667a01a4aeccc89311ab931c6085b9ae6fd68a2ff87f69b57778040dee4819d46be2495dc1823ebc6a7e9817de42d2eb75c78e1477b600c8d5b00ca60007e3caa97d128c140dcb4f69460b5da32d78640366ea01e1ed82e4f8f1f802516f4a1d77e7b6863031d9ce1c0280b3433fa0b81340bb9160f2fa81e10b9f17768996e1e82c3366c2ac2cd78a1819b31579a0bc2ab839bb106841bc3082efdb04911d3854d6600e572e7d21e8d2b638b9081839b4d86d3413df8addc951d5c7901844b450a91db03bd2288b77e36a1dc291dcb951d6c72c4eb6698263b17fab0490d86b8f45beeec3ef7889b659afc687003f5e30063e2d32e87fe62931ae4dc8c03d0e4fbc48ff14b5005e5c6520d57c62d551d182e7661124ab810c6c41f8013f6a53f8d651d263ed625517b0624967be030eacdb8fb48e3339e2947e4e1c1941b7f47e441dddcf82368191861105b4744134a27be6c7ea1a1659c75e3935baa2181d8ff62a253d4be95a133338d11f0652eb50df29b7b308d18e51320464a3d87704794d1d11f47af5d0e876187371a1e9141772f557f1f84da371cd1637f5dbd3d463abdc6dbd793641e4262bdcec82fd5fedcfd2574cb4e4a1c8450c6fecbdd0fb8118f1cc1440a29455481b38517435880a321c172830d164e2f6025710330a89015bc6a8de605175808480d1856c851b2049221568843a0e2b5828d0a2fba10a2f5a11221f810266402141e0223a45085138410851148bcd545930f155d5881799121b30a16b860ba9220388f13a9c9e34412975ec8e3c4e996ec574121744a3dea0e7bc94b7cf9eec3da6a2d46770f82796c37edd03321b8c121173e43bbc212c36fece0918196d11e7e8ece4147cb54aff4239a9c89a8748ed87c14aa990180000053150000200c080583e170402c1a525565f80114800c697c4a7a583a9ac843490ec3388aa290410611020c0180008091291aea0460d9467983b2904febfc26e0196c04c2d3bde7a334ee9b17a1e4fad0c985fbdbb80112816a1c8284e5c272ce3ec588f2b1b647c89dba1c176c444d079b79a97b67683c7c65d55da40e883675fa027517956b48b588b0c5523285b770eb13c832cc4e22593fa600061b5dfa219c5d3f0cbb997db857de44ef595df6ac32edf67aad28c3113bf25b89463f8ffe221890fb8ea3cf2c5008fd0cf2d80ead7e786c71ef48248cab6915c6216ab8706af5cc6dbf53dbc6382f3026dd8e3a62a883c459d65b5682c6e708411cf893958a1e82b4dfd72b84a0201ed998e76cf724ad8b154056c8bbbe7ea3706351cae3e31777f11af98a6accd858c9c40e879afa0ec586733b89267a06513f724e5b1d1d39184d54794b009e2d46c1c5f9580edf329a6ae247e8ec25496e2a16a384ac83413e514a0e47419224b39650d9dc5c552e30954ace86418b52f3b68b1d58761acee079276ffc6425be9ce10cbe0d4870306e9d4929fe90ca3def4463d23fbaed1317c59c340d2e858d633f135752aa24e10ca33caf0dba93d9efbdeb35f4ba42847146ac504aad116e9d4ad2780fc48b56b378a1a35661f9dcd09aa632ccddedd554dce4ca8672b09098fc10db86d3a1a10845f03dde5a59a0cb68c3f1681fd128c0d27ceb0939262e54fc4f48c9e53099b909dc04527c8440983afe68d93b847d3cb9417cbb37dd812d69a59590aaf9f1e450bad37a5fd0321b12079339eebca96e19312b21fcb552a606caaa3d59c72e36b6404b44dd8997457118c41e3fa3f01e4f0cd425926f0676170f421884b74db60cb37b08ccdf0cc0d0f25230bbe37d0bffacd668c140745fd905861a66562af4dc8d643f264aedb184dff11ff084c35351625bd5b383d472c3b990b4f01894980171558df57ce9049999fd84d08bfb54f183fb43a5d77e9226e0d5932d59fc2dca3aaf24ee4264bbf486a9b91a69de9a772ba1d63ce554af9a8174b0f558bc508ec38af46d6442cc85de4bc26c93284ad84fd539cd6996c573b373a7100506d4f15a68a742289f0591e83234b228a585f66c72916dde5d8a6636fc430c240c0517e170494a3c1cca873e605fdd423d53b17a0a6fe43959b8d51ae2595d182e5cdb171c08b03681b7d0f6057f49557c5ff028860309a6289b26ff324eabd4c886f42680234c56828a7657af4a078d8cbf64c8f525fd4398ef7286527136cef7e12c5da58faede6a534c48eb9978515090019f61fdd0248969dd82177885085dcdd91a55058309cfddf61a57513b581bfc0e09b8da92a3be153495f4f47151c1f5ebfea5daaeddc173b31150ae4d4f5598056382f77d8d9ac3ff4bd27c62e2b457f8b235371db07a8565615d174406323b62e46d051d107f200f6859c770e545ca73d8067e946531d0c928fb9398da1758e6ca8c5a8f0dea581b3aa3b44701f36e46abc08df8a430437e0912428406586dbaf7fc09f5929a9883f31c6128912a3410987cf9624fc9cb9b9f5002f1bfd71acadf44b4a687fe781df0d0aca6aaf8bf688ddf0f96563c03f97aa7f0270b1ed53d044f1aa6e012897f9f888fc7db6f64eeaaa4d2874b38069ab503c31d5175069004d748c8fa487e0c658a3d81537207e69d56a7a53cfdaece35b43c9f92bcb7e3eab7f60db0a61ab7a03d4399ca259eda45bcaa3d0a10373609843f5fade1f4d590266381070551a36efd141a487cf0589e2be1c70a815b74ed22436f338bcd025b6498f1eb3599e4f194cf5f9fd569c385b7e483b3de783837d060e358bdd58c5673a41fee383ffde288b9a50da285eded9b00383337920aa7bf29908ae6733e1f781bca788c845aff3767de3242cc5dd06a5e6d1cecf5c19843b7352c2c9ee9eeb6d629d88f424ca858025fc72a41a9d0ccd21168b54c383ea1c8e112fc10da9d8a9faea2e0eae41a76efc015aa5cc3fc422ab1da0b1cf7cb30fc362dbb8997a63080258e9c8146a1533e9ba3285fcce0b8c298c1da11eea464bda9dace4b18cb895e00fdec1246bbc7e6669b64a938d85e420c764029bb8de2123070c3bcb5daf8aef89ef8236019751be14f1cc5555873ce60ae9186e051a8b876d3bc4a5f98704e665526b68508d3573d72073dafcae94325930199060390030aad844dec86353fd5b6c03658463e5cc42eb18dbaab39afbb317ba2b2cb833e618c06e402a1622f731eb93d5189b56289974a532801cafde3c25067102e5d96bb01b4cd505453d8c5862baeee4deb438b9a26babb443910e0660325eed7a588e6e2d372a22859e45aea1ec88d28c90af9d8eeeb29e88659489737fa42372d378a18cf538482c03fb7cc0de9eefef62ed2dc60a3020ca3dde611cf320903000d2c890c8c3293d18d39e90ae19fd60ac8a1f9fecf1a64a29f97ef0b1823ae871557f1cdef3a894d025194c038608a185f41df53a240f82b71c8197644b5e3515612f9ebc9a83bf64796934aca3f3aad4b70d38ab9c9c729d87ce1ca163276768d8e7fca9dd094e40114d74a61a2120e9f704198d03daed37e4590318844ae1e71820212b8b9cdc1ead1ba3375470ac2708e8d06908e201cf81c5fab0a12abf96845160b4714d74c4b9f6fb7dca39f9b06400b5b115c1fddd1067de2b5ca15c232b6436b794eaf1453a86cb4a24b00cfcbe238103ad504ec78175e6fbc33c2a68e31b1883b0094905e7a364eff0f2de780ab7f2823e540614039025d7ec44760d745528e0924cbda89730016c751c01a2c68c8e226ffab9124d74e7a236b593e3371c078e41dbf0736b7c8b592259c69ccc5ead354cc5dd21da664546012e8a25d58c6fdf98af91f489cf147d01eb410f3b25452e1ca5693008f251317a6466244179466f1c835335e17746bec73c2b532c41f0862b6a2e5f6cd6538fd82ea08d835603a8cb93ce1bd757cd3c74795e6545dcad2d0dab93c67c709218b02fcd05330e1608838f69485764f20e56feed82c960c3d8ce7da3be4f825622f2b3c53e136e5c717d96ab87847eecf087fc0530d32f411b55a4405b5bb593a4350bb31dce52072fb930f4411fdb3495051c23d731c7b0a4dc83a380e7f89bd7426c6d0c3dbff3668736d2772c07adc07123f153999127bbc9ef61c55e390a14b3f818ccaea2afc7d2dd2cf309fe360fe6611ee91a503a01dfd1346bdf963af21b26928e1dc6bac37a2c13404c860c5a2254bc77892c318db091071a7e0ff182987d12c8a45a2e882ea2c32315011c881d1053d2371ddbeac650a27a93275a814e17a2b748ce36a41e2455be5ce520f2d528e61346a85b711fa1d9efddf2060f8537c7e8bcdb2b82b1a5f00a756bff4dac59197f378ba4c355e28529fbd519dabaff78bca9da0518a1cabcd5453b63cd872d6619b1cec14f8b5c78b6d0ca68f9255319c3651068ddf0e355f6942ff94b58b7cb341f24fc193b52e1e9b3eeb004d536af679e06f4364c7b0e3a2bb71cdb6e62824f05a50575055911445d60759c60b2e772ac43a3ba359018eeedd89a1e813f4115a06ebd7e271250a3a42486cfd5a38f787aee887e2836108cb386ffec969d18b05dd54b313c498366d842baa95ef14fcff7f9a7078b06fb04d046b4b097e2a9cdec85eaf90f18888ea54c190755b368af87dde37511d196e26de56778d8f14478f25edf4c90743885846cdc681a21611882684803ded7d6de906e5a7114ed4f3a99b3c7f77fbbc3156ab8f47435dc811200b09bda1ee4031558f42d86f77e0f74d60679191cbd709f52385e4b737d4def9c8c6daa858efdc93582171b1353ead3c7555237c56bd116afa105d63023ad6e217eb27f1640b24d966d82d4f38594c0dd54ad10ccc44e7511cb8947cbc84dcd74a7de36b88b964f6aab5110730d919f1b2d3ebc7f7d15c14e47414ec691a8140061d106fe85c73014bfe2768b4920ad03d70eea09101c2a1ee66d474185868fd0c556334c9ea8076ee5f089955367aa38d45a4a7ef3969a4e1aa4c14b1831eb58920508752b231daa40a0ce4dacecabbcfbf1812bfffe4966d0ec8489b675b36f20df56198d4f49299676e814d5a63e0a8b6d40284a9c721d587dacc9131d8e69cd8fc04f05eec7784f3589a56ba740f196d6ae47d546c3449e400338a69f97d2b3e1eccdd2fc900aca6d4ab2da52f40410ebfdd10d843effb23bdc6aafe2b24aa02d7de4c910d49469e333a039cac2e4f10baea7951003d3e49368614b004ab626b269175e4923d420d8450f8ff7c4beda89c34e6d613b27a096e899e0a0ccdb31e96e18b30882f4ce572b7de49ee2a7ca467dd80ea4962b284e250cc3ae8d30d1e9ecb81ad203dad7b5d42ac025a6a740d518c0eec89e0b4677a7de84e0aaa0c961f09f01ad3d971cc3d6f12176f02feb1987890230762709043befc0398122e92002e25c042b893b24ae81b38db9fa8fec5b7b499af1e7682142d691254176d859a4cc11c392cb03f347c70969a8952c063e70d5532d7ce648eb9f6143bf44fcef943408f8be3582baa47a49884db00ecd430c5608cf5f49dbd04c78aba6296f95aff815f1a9cd8278ea6db9b30c339c78e24a38887dfa1dfa441bb5551fd1a98a1300006a31c1c6ace295a0b1a23541f985e72b1e17b069f2d12b98ea137018704025fc0fa2b049c083c16801088553322c6f5d5daef10a95ea205b3601d093b8bb36843eb34dbbcaa962ae1435801bf7d2e6f758740cc83a1416f7ebe0efd73dd1050d09cea76c4a32b662328f0df4d3f983ead9a91c64651e6301dab8d5eb4ad0125641ef182493727887c7bbaaa0ad1e61b0fec6ef191093e0d25fcb76b25e463e7231103bce89d4def3b9639e3184a0704f7c8b5041ec52a226cd9e0ca3d6c18ff791e6814bf145d8119e50e2e93de4011c7505eb5ee6c00ae008827ad3f039a79da37e881e6cd36b33a3188ae4621fd2b9370cccbc8585d1a415b096c58f220360ff1eafc3d2967e0fc67ddeb1a41f97b1c5aca11017a46a5bebac4a664e1872cc7e012214c0f639b417116a2032d4d3b1765b5677bfbcd01be2d07f0f35b55d430875181d7fd86642c31a14d641026e6bdc69791b456196622e483b9375d6120c7497b77924a643644d01c361b6f4d6a8ea4cfdf80f470e6aa542e2a8b3b7aa09468cc6b913feba8061f6fc046ce0fff5b49ed1bd76018a57a425ac080a671a94af5d3609d1712b5c5bdacd309ba649577c13b2fce06c5ecc99ab1be85eeb99dadb576cc2f111da3dc50fd0fa3a2698bd5146c7212f28990d48a0d09793c74cf4617c2a38d97c1316cc14e1fc1797e810f3e6469f02100364619ae7ccf9ff572133139cc7d0185ae931777736d7885ba3727f2d20a40ee70a913a146b624641803a09e04d0ab22f6094ee646ea79429dc3a979ddda6e37d3da1e5b3bd924a38b143c4de5b031dcb96f4c20610cf2238de50b0fb193bfe711350e3b7f3bf9db20364f40dd5e9a84fd1d701663a27643b479bfefaf8d8125a81a223bcbbc4a5bb0d28e33a220b933022be68ab607202faaf4a92cb2016030fd9bd7edab42a96b65b69abf1bfb094385deada4e3e9cf1d95b888020fd431c8697a0554d06383400862d6c1dfe131de36b023b100f4276550d0435c6b8a79715c2988e52bb044f610a38bbb523ef45814f6532aafb8c6476142e022d2f624aaca5dd1f894e0803a0b917ca3a0a2f0417a4546cdcd4db3e7e85c4250c8ae19628e477778c3d8a69ac1e364b344e4d9601ed11479be4660b9d7410f63269b098dcdc6ce8bab58b885b53a73f5416abb1a3a939395f5e509374c5aa48c8a8b082b37b010098ed5014145e9ac20a45c8b054a312247885b688bbcbd1e0d719fc5dcfeb0cb70f83604b93679b30020536de60349883b00e603a68c111962b0fe2ea7e5cc304c46917f1d429b56f9fa56963d4ccd32787d5b7501070c30d55a6c45aa86ce00deef09027fcac06dbee815ce3a9e99b1ae1a7694bab888a4e387cb8cf690c5bf9e68107aa463df89140d3c52621d65d287bcd8f165d248bbc7cf88979944e18055006d1c5a6032642ad21bb2082669231f402cefedaa2fa482dde545b9d05c9ab574fe0b91ef175a1debcf91e505750be46b73cb1cf092d76cd4a348d32d862f4b7957c8500fb62f9c0e3788b701e72783e385726fe64ecfef89bec4e9ce6da343adae7494f5b64e3ba7686d3f19405a386aab2f532eba69f2cb64a7d4e4eb2efee7701a2628ca6afdf731b57c34433c4c245ea00e35f94a6e2a45c64d4f19095a28549003c23c93cc9dd07826622220b3861122869fd2d35ce509f173f531b4b2951d0762e835acf3b29b65026cd262a2e6ff7504c36877f8b6328c21e7db205b7be32845b1bdae3b5608662b30f2f460e2f592fa65e2cd1de704a620541e5b6f07752f56c926b1acda80324dbf4a49bc00e586a825b611637f84a02ca54b1050a0850ea1844bbba2a91b5a2fd38f41acc096a0a01f1a2d23431d3672a5f081089d85bda617b88edaf19f1917463f70a17609c411b984695a6a8fa0bb40ec7a4066a53a244a97f7601a67878532529bee72f4303722b93abbd9668c166b74301dfc0f6ac7a34ec9fb0d91189d443ead0a1e70a3cd4a3be90f3e5fee90fa44aa8a428d9b54ded9c3aeeeed32529fc10257a7e3c07d8124406c84c7b4ecd35b17f059c71dd043bc51b52f709ab62cb8be78809b9b9160f288de673a6852c45ede1cb4f8c486e69a03ed8ed78b351da63fb6198cdbd2e4e8d7bd2c16d7273787407f3de3c7b0de6bc79c076727040b0de739ce0ceda12c606fc6acffc3c99150cf1f638c997fd814551cd1bd9ae88e36df04a41f84624d84bd585032e0c1653c12e40826e98914246c35fce0c3c5a39208dd6a3ff2154bf6f50f0b1461045fb9ddb2ffd0e2d6d71cbae17a66b6f9cbf13ef8e4fda225b397ec00fdb1d5a7f9a020b612f909c956bf0af0c00a843fefe71b3b7c991c1c8249ff3be931aaa0e23064dc9aac8581ed2c4283b120c19930ea06af212b0bbd976c8b342f376cba5183c646168f30f3652ecead575f240a9843e50e65e8d9adb255c559dbda1e1f823e2d2de9af96517b45183e2b365caab7ee19a3e3b89c650c0a9f09cd036347e162972238681a199f09c70684f85850ac46811e2957c68eab3cfdcb52ec261457f4c9a0215bb389484df6d4996691109c39e97095f405a446cd904501c9f9bcdb2fcbea1d5d9b33e8c6d370a29fc88da9f5081ba23817f0b5063d0da6ebe85e3b87918de4509489d5cc744014dd46c69cbb2eafc26f38168bb9514ea01b41917e7c159cbbf8821d66a43c8eb2829e81ec129c78eafb29c9e1e26396f5ddc7a1b90519d3398687483d3c8d927fa743ca46021ff8148e49432564be8982559ada0a6a09465d78f38adec0151ada6b940cf559a1c8616239c0ee25e504e01800b867da3cda9ec44b48def3cf690b0f12a4a73e5b30b775ecde047c8c45c7227e623c127747af099827ac0727aef7cafbfa09c4f98522e4cd77308032bda3fa7924e78adb19c84a050a2f5f81b6cbaf7f2110d02de469878bc7e338d7209f517eab3c640484b02ca687de4c26f59e106ae10cee5df03ba63f7902026a85fb0989ebc6afd2603a038b9eef6d4e74ba328f436bdfc863091dd96f31840f6011b66f818b8e557c4b59f4148d2199465aadb2f27c8462b4de7f18425e0236edbdb5d47bf0c9a05db33c3e446082d79cb099643ff25424a281fe68b80c0e5dd92f8b092598ff64dcb11278e8695fd00bc87c0b92191c8af78ed456161f5eded78e3cb7cdd5aaa44c430da9609054e6b440758ee049272b7546eead3cbd3f0395d65f308374091b04b93f33540f618ca38daacdf28c2ddfc4670d67176d401187329472ed1086095813e1877c2b7ad884172ff861362f543a5fdc9a3c9b1bacd0b3e1646d9c7085d3262a23ba5735e7937816b09348e541412d96140b6260963935d208ec44d9cb9797cfff370a8110665adffafe2ca8db374d05807384988edec03ca7fa46dbb35ad224e807e23290a45b66e89d5b591ed85612c930cc20ab2ad91f8b59503a171e1d7a39124d3b416e879eb678c67ed30a3e36bdd361f69773d4e266a97e37bea83515f01382b3c7c706f57f1e55ca86fe0e02b644baeaaad8231f3c01c388962faa70075827b803120d740b8840cad7c912a716bf7bac0f8c24ca910e983350db503ef9513cf54722bf7ddd14a9370a3db626b526745cbd043cf584f202b23c74f429eaa6787c87a5a0a159a00f42d7e860e3262e9c7738c3f50161f71fd9ea217b1ca22179642c4449794d1455cd63d7265625572d9fe9038df1800c508a7f905714745a8873e62d486eb734c483ad87bcb260b31ef910cdf8ea8011514932bc88f557140bd192b90200f73a009f2c61a79c8e38cb2c4f9ebbb3d46177a2eac525c6ad938d2a520155e58b70070fb48ed59c0261dbe0ca20201015098c381d927eac439b89024207b1a51e19608b25949b26053ed935d310b11d498a9b5226ad64ebb25f501ba9112df865d2b1d85d730c7fa29a859420ba89241a190f75078470f97c301f89f3149c758665b1643fd63661261b32af24c881ee3b207d4e622ffdcbe3cd92069aa498c5d03c4ed63e3e08c2e173a4a7237da87006a0944af621682aaecd4aac43586341cf7f32d7620b84675da492ab221bbb4daeaa15c9360ea120f4dd62a56c455cd936f7486c6b5c6d26ddbc28207e9f968db6fc5300ac6aaaa9409f225b8883eee31a5abc8ca56f76ec105be873dea6d63c107dc9d49deac8acef754bd34e1261d638a1969a00bee0b12ae61927c3abfb835b40cc1a0a1ea313d9e5f222714d4dfa590996635e474175982f9a8951e16068e169b631d39f4b8b32834daa313e98b5b8d8c9fd02bdf94f18bffa1fc4bc805e3e497f4fe9948458ec8f6928d301a6420ef2df53526a9953531cac72724d9bbf58309577fa00232cc3a0bb32c68247d90aedf36433ee1dbfbfc6cb07d8bdb13fd73c6a83798822c749b703870913cde7c52215a386ee54c3bc7d8463328fd85e228e806fb9fe444b2e07ed22de1668c2e2ca15e45a3bd292d00e1162906286551f614c4a0aa3fafffe2b8cf6c7c52db5edd91d1f44aa84c90047abf6073f752086321f076af54d1e74254437abcf6ba29a353bf5899cda58217132239603f25e386a8458b8351901f5a235d4089a33f49feaa1624508c70832831bc7a1136c85a7b920d71ca2e83c37a2aff6ee3c2043f5069c55deddbd7a05d663f6931f96bdeab204f2dc7a03b2ec7045a991455a0fe9763165225e9fc9457783d76de1fc8370451124ed1cf17dcc1536e3d024bc8e00a8d28ff4b86bb9e091c7becff6461e852427d70fcae017d04c477befe635d211760b7f3f15c696ce5f3b62f35771e447f4bfff255564e3d188e2067c78d58a9e59ebcea6b88d120809e1632ad65879aac6a32bb4ab82183055df7a705923650f74b9733d7add2e83ab3f49878c95d16d9d399d8d4a1171c3c347441b5f87fbeff8e30fa8f661545ebde4d3bdf26e74977293ca7a40839b70d218ab6b186c807eabccc9adca2b2f32a92cc4f874add26b1590a651653fc11b7b2b2356d9a6dd605890329f9fdb53dd10e8705a898897c111e854959776fa40f5e4404d23317a0c3cc98aa5da7085f0b98e429c8bc71cc27e3d65a19d02b45d1c02a0dd75dd41519b3ee9e7c2786782cc5aca08f7bcad5fdeb3d5358b0937a009a8968bb8f356a3099d617a297345051d2862bdd2e3e50ea49b35c704826e6df66b01340bc7daa063d9419f2678f76aefc49fb5a9acfe726a9033fe4ab60387ca6ccafef057e2424a9369fda8f37bf39ef2f4bc052a210dde98984854ac537aa1af90dcae8901cd5b449b5c512643cb5b5b571abaa0faf1f0b0a0d33221e6848250501cc76f781487c53792a1b5fd842ad1854a20ad23f6d14a3e4350d90ead50549a6f810951436e8e0e2ee768469d6fc755028981a8028515f4e04fd151113c374dc0fc05ee8c0c069d718823d697b217b6c43d3049827073cfaacaa0e4e4ea352417387898b67f119d39d55f112d2a0099d27177dde4a4a82a2f1104f21f876ba5fa4ec3e7134121949f20ccd275e2073ba9716fcba86e730413add6521056ca1d75ab591c2ccdd8544dcf935775f0c543fafb9d743eb9fec265198308947bc086e26322ee1da8a326ef1fdc06e473ea474b99dd0c4fe6acc1f6e0bdd7f6c3a4c995274bdf14042ea6cf183b2868196941b5604df3ce0074683a028bc2f5cb10f82c2ffede9199e19ef0a5a859268c669d382b56957b5a7bcd4166e5a33fd23838d5bdf69e92381917907a086dee012d7b33a438acc5d028e20a9ea7dfac8330f8d583542f1d71becdc435f1e09cc368de6627c9e15de108f62a7a935433f4fd3f48275bd76da9ccd9b038ce0c92c9bedbc5d045e0fb68d1adead55528d5945d67cc055408e6e655cc5eda2a90ffd9bdf8a274b2926783a144b35a11c79b550f7fd764c871bb9758fb952d432ad3cb303475ee23b639d414326d7e52c23191413137211af8f2aba06a6d23624673c588d0b1efab1a57551e25084331467a9b597bcbd3144317da2f494c65be74ed8a421514a14533bcd064373fc98e32176112681e56d3c4339f1ca2d6dff41741369fea9f3295048331589acc73c0d80e1519d5f762c9bcae6636d83ca6b73a11312e9bc39a79e5e13b012fe098d23f158429e6ca075bde090999b17426bfc7dd3877f6caa5749987a51b34c3ff158c4cba94d40c023daa43a85993da192e3462197ba98d031a69d6a4d25e7cc17c2864aae8f8d6b70d932bd17e473a1db8d2ff69d061b9e414bbbd948916d6b99446416095818d777251e9278fc2f2b945c53137353451ae228fbaee994cf9a765788fff8372c4e31051017fc9f2e242627ad151f5a3b7bd6fdd9ef92eed2803e29e07c9a48ad750b665a13105948040af283e02ec2008685a4d6dd1c31fa6b192c0e60d9c94ed0dfda036e8b51210ecd3430fb9ee4c7dafd43bea7d9c9166e2eacd3fa9ad208b3d07443428104e8fde038b6eb73feca40591344d680482c394f13d2866c26869e5ad0d3c35e2678bb0b0d3d179de48e467d1a5f8a447616368f578ec173855f66c0602d809ead27679e92da1bd551d34a8f6e244515aa9ff75283ddfe548a523d1d2d152fd8eb8edc5282985f79d83597e90691c181fa9da520b45d1b60e828d27afb825346e782f7a505ddd8ae1edf9cf01dd6cb00248bb017cf925d5077ed09aa2d3629bd036f8e59f6265a9388f9c7b2922719704aaf7a7c1e0514e9616cc1726131d2b35bb169bf1faebbc2beaeaf00332ef87a6be7df61badd220a6eb57d0b83d2e1055d6b427715fa9fc2bb010e46555ac0621ee40a68812744bb55491f8ab83761ea262ef95c11351c21804f985b9378ce47362165c08252485f0c6760e53a93d7e1d25a8e47cf980a740529c2ea2db5085b47a76480a0a3561975b09cd4d880543956d8b858a8fd94542e8987c2c2910e95cd5934cef28c45afaf77c4c72cc279156c843ba7e04b39867e5d2879fb6e5cde933049bf8e816c59199869ee9095eeae0d16e5bd1cab271f5e36b0ca68ecf8a537562176496431ea679919177d0567d572ce24956d1ecd46494e103098793931a04230c1e7369bd59e6188d8ce885d228f6f34b1ed0cf89a18170f10410ebc23204ba2f85a5a95b88afb43bcf97906a590fd90fbb563433fee7e00091a4739c3ae9f62a582da1f13569367403f1b4ba9c8221f062d82258b16cf17f220f5f7a1285a2efe10e66dc95d63dc1078937fbcf6f3027b8248a8d6b285d3bf8ea73abd7346b304a8448c7ad630b53f74a64b18157d25e11e2f568012d7c971681f0a6f00fd3c0389f58087e435cb306775dea67c76e1c1c15563194a058c9bf3973dd61eec114d6052da2af87dc51dfef0c133e031800feead75736aba37e5c903c56580ede5819d3c209ee3cd9258d603aeff445840edef7d39e1c6ca8dc3c0408369b033e628fb2c98d8da70f0642190c76dd52ea0c6eefcea4f13010ec565bdb9a9e326175da610711f020d0e1dc8c02c51f815349de92dfcc6d6dec147a3093a5276ace1c202532dcf2d4f660e36cd0dd330c02b0f21457870fc21a8ecd1f809dcfa9b5003f80e2d46b3fcefdfaf33e0013b6c645ce3d0923511fc474349143c63948d9d034d0d2770493899c0c8b047efa770da5da65201e8f0a79c774fc8cb16c86e6363b7da1d8c2a71639b6a217ac1839c9c1200e620d0cd098de15310778fd4a464c3d337772b90fd0aa01d3d607489bde3eed4a985309e800d9d667d04e5fa131f0860ec83fbe06fa690c58f47027e50ff93331f8e1ace764cb251367d8749f7e1a44716cc00a814456812a4636055ff5dd314dec09877241e3b18051f3d3df6782239b425a67c794d692d2455bfda9806906d1294d235b780325381181a62a800e8a2e0587dc045c01ea55d7ffa5fc0250cfc98c022d4b683c403d71c831185e6b3dd1b5af5e3ff5d39ffaa409b741554791a29fbe9c8fd177b1563fbd6dc054e250ffeeec40dd4a9cc6030518fe233298de21830be25c3a06f500f55ccda2bb8d6d6a02a81f2b656f7260302f1a855e7ef9497f784b93960415b6bfb5a4fef2cb621777fdc98667d11aaf8501019b0f286ac2445f68d269d1194568b04739803b95748ccd12a419fe8f758619c37245191c7dbe3fda95898e95db0f48ba9b2ab5610c06fc93ca9f3113f9a7bba82b66135fa68583dd4612c2b9f5b940846026f960708bce2ffdd8379b9f8807166190dbb7660b190dcc40f48c88d692232a34587c7b3628d96d11b76a28206a5e5bb83bf92e78132e6c0638e75634037c68fbb6efd401c1e9640f59fa3b0b2a20dd9b5bd49d7a49183301e5398b5054097fb77e76bccd90500194e115fdab6149d042ff9e39f226a07f915dcb102ad71302708b9d7c93d004278ae7710dc8edb87553449417f91177fc87e2ddbc8dc0c57b621ac8ca090346dc1b82b55b2651f8f9c58403b8ac565cfddb251bbec03ca29219656a4fd71cf69cd3566b1849132f3ef3bee694e35afe1954865d7668d32d6156cff02f1b27bd66f6808e62c047f46644967163146b2f1f0bcf37d4150588cdccdeb1812ff84616c66fd8ad959205c5355588aa9ca9f858260dc97486ba49c5d3a7f037b26fcbeacaea0da50e3df6ec22343927a59b33244f742674216b6d07b3a566e89f464e0bdb09969a61d57d45d4360c19a3584d1af9966e9ec998795869fa3d8b3649c6eef73a993a427c3e73d6781408eb9b0350f8ae5f7b128b65aba9f919ce663febee32662c54ac456ad39f33f490253b7d51fc07fb12db9175845672e28b56000019a2d5073f788a81199d2a14568209bbf7cae0acd453424d8e9ccb86d6c48c7d9367da22ab919af568d9652b86f6364aa5b17afdd25e89f8148b4b0317a43d9bac8eac7ac692d92c106414edb54e2849a94ee84699847447501e9bc85f1929f713bed11b631a371ab617856eaaf956bfb7849e813a387356ab04c8337d9e308cc1028c61f247ca4978e765a4d45f1a9d5bbd1ef4732078896181eef3aa228e75158a16458f94fa01e898ca2acf863fea4b04d72dc1bf7307dd44a45f15448e88dd4d345de3faa407a4aa345ad4f03029a100fc57449e79563f251f31ae8d6eea9e8288079355cab8be1636ac18100a0a481f131a7a6a3b604e7e1d5f9771a79fc74d51307b9fa54917b73d5ab51b3f341fdbde7fde3c84ddb91a01d984770e2535b088035726ff9f343b9bee032611b71fe37aa49f93d72d8aacfb2033ae4f7ade07e123fbd5936bcd7a334242cf367ea0856db0da45cb10cc9f45a58921365f9984a8f40dba3eab6bfc6088c4fac9633956103db49b4460a41a74f4931dd49e11427a748dc19124008d1fa8e5c11942a8fae3d8c60f9e4f49cee087da91ac0493866fd9dcb831512d67341228e2c3c05c3f4b7474bbc5b8a23868e093842b0957dd9bc76f725ff3c404f62192c836d2ba399114b03066c2c4f1836555a026aa036ab52668878d4c098970725bf9cd6b426be688f8413a4179040e8acd55adc6f7315b68620bfd5d71517f708a1b008813371fc071fe8a602a2a4fe742052f408aee6040fc447e1d88782560b3e519e61c654ff3134f421b2d4d010eb3ba80bd3dd09b738441c0c8058e8334f821934c493ea082e93465191d4a3364441866d3caccf7e18e8d646f1a74bbfdca0d73bcf3bdea4055f348741eb99f0e2034e256a17341c532cc9f8e57118e9c699c8cb7986f2e87c643aa77476a1dc42ee11ccc4161974e92abf8ce43a0b93712db546c8abc6905cbda8a5cb3e457e554dec432862da0b93de90147cbb081d16cae4587dee40fed602107cea1fbae76ec068ac5392a639cb483a4741f34a1f65386d48f21f3d5287e6bdd614c02a336c779209104919915ecb905cf97932baf98d70581ac956a56dd12cea85176235d398b3766cabe42528d04e12c46a58fb6523a4c9154873b79269264e8e4d0ab1609d0129a793fd5bc0c77963ddc7bf452dee269b4c7cf1885cc344c83810e4e27e4175212f440e34aedf45432d2d7798d6d18b653dd2418182c927be309ac794a0133d0476530379531421ebc846cbac95a8bd4422ccc0f903cd71331e9e09b1e1c0889a759c6e39ee57940e66f855acc3b1a3bb9520045bb166bb235756035e2f978865daf5e97ce98838d3b9a4d9e266eefd1c3c92705a2f71d17f51a9ae210f4d4297fae7914ec2b02cd7b8b77daac58f612d8745fc33a4ea88525e282c7972018bbf846ddc0e85a76ccf2ca35395010cfeee605aeffdf5ae6ce013aa29fddca07e5a956ba36eed58fa09291f8e824b932c6bd63460ebc6d3dfbb015f3468707bfdf93592d486ed8410b67259e4411ffe64122441cc2453c587ecdbbb2a45ec938d94373f4944cbb10e2f83682ceab71587fbfc9e028825eb254fa9c4eb91359e2eb440144d095475038dbc5179d788a773b9090f88f73cae85a72d4b95e251f13d001d6beefaebe1afa8030902ae32164d076382b61da7b45bd58b1b559281105ee8fee296af109d03e0fb2af88b2883cb32c65803c61fe421fc3953c663db0004f06db246502ebb20787325e96fefa153573a10d71e418b291ed60ed2da2e210f1e0926ad31613db1327b29d119d800b8b3e0e1b167d893b4ad0d1b61576e94d1dfd82a76c9313dfe6a5754db4150c96786b6f68a245d03e2f2ff010805eb34069c33fa440a31255f1349036950491f2cc9405f66545a61e550c53589fc95d821bc7ec0ef18bc0b19e2a89937dab7fd485d9d5e2459bba856f55adc1a95d2e2501f64dacd735886c9f49b3d162a9e54a69edc9a830c90978a7bcaa4fc4bd64acdf2ec27bf609e477a436c3860e8864dcde6b4083561a34c4c3a1c15e84150e47296c759a98c22c9bb3afbd4959e8a0971cda3a292b0978e27708f4fabf39474f1f23ff8334db61b75615e7d064f5330bf116af75aeb36400ad9c90258f35b19bd3886b758995bbd13bda2adca09a2cb49c0c94039bb78c6c9e80098dcfcb51cfb9637abfaf8264ab1e6084c478b078516a1cd206d4624d635dd7681011dffdd56ad744c681524b4fca122f67e3c6a835b96c45b68ed2b1829dea19f06516c53a656eab42fefb519e678c3914ae1d2a182dfb64a6d68849885e7a868c1767fccab746ac59984944df2a2bf150a11813faab51449353c40aab8dfbbb60fd9c2b25f0151f30e8126c457af624231237e6eadef4899be0c83d607344565071ae07f10ad71fb7522a913010b5326e8c63a916ee56f33615752e28e0d838b5348a7f783be6fe5d0aae1934f5ee9d350c20ccfe01186218ad32c79fdb104816c3bc316b6207747dc0509c7a125f79e3ab0340a8f39fab610eb4b7dc8eb1e5c8e590c05b45977d60be6e16e6b02e44b08a08caadadbee1ad5f5bfb756b8e414a3b226c1443c4a5182e81cf70be106db15fdd7cc31d382aa6373159500b1c6725634016fe64aedd105f28d0fcce808f2d85d3cb2e55a057adc8a2f472617aabfb2ed0cb04c4431c6a5d6eb5d1aeec0614225c98d2fcefe2a6f9570cde76038d9c603aad60caf84fbc8d09e1d8cb865b386acd5f782832389cf0c3a58049862a6ad064397425affac3df2dfd563d49a20e62632b9cbbbdd03265870a308e1eb18c3ba8492e96ea7918352191dad6756034232496fc7b983a51d3d66cc194c5670eca390650db172792a93fd38358d805218fc25331baec9cefccd05dcf0497e73bb35fc88c5946f2681de1147901b68ddbb9ea47002e91dd07a4519ec80b4dc9ef4d1a5108c7167367d16ca4e85f5ad60ce91190f2f1151c83a594b5a05cc61ff6654e3bddc64b61ba04f4883096085a879b26fb029017675c85c5fd73e193976c2f767fd8bec2666843ed8285f7f2fe1878f70fcc58c679d49629ba64046d2e8276b79cc604ddd66acb5b93787a3bc7deb86eeb753d208e46cc477c8e1f3ce9122926c1e512c8a36f2f6360014219296c4d4abf14856990aa6445ca46cdd64a167647657af9c1ca16d7d07fb8697f91f601bd58c66e2a89d188d10937b3995b30a405a29a9d3e26723fd2008241cc977d2f623025207fa72bc1cadcb6ada1c27494677a4501c1806e42f5ee1a4406f262aeff523200e9545b57832a4528c3f1d6ce71b1ea938e3312eca0b8a7e081ed627e51151ef63fb15b3f53b23cb4084e195c208a6d3dc574c09676539c88bbb1a260d8878c8e27cb07d6720dc00f278d28573b06c0c87a2c7091b20e846404416b7b5754fc9040d6d2ecc5f86a2342d0d0e9d2c41350dae9ae1279835c0a144f44cba8131ee08b781e93b7853a39c1b37bf1ef51ffee02f19a21dac073c13380cbce2cfe66c6d674af93f09ccf9380c467dcd51802b81b1a216e4900326ffe626315e033f84f53c1dba5a9ee7c04cf520f9c1d2b16585ff6dddfdf20eaed901446c9beaea917abca42667e466f0ed856c2e34bd29e29bb46fda244d2fca896b93854be58932e511581549bcfbe21df3f477b6c4ef0519d826a19193c2fb5b19b77788f6ec429ef255417ba1914847d9caab1be74d3d641c8b4a5395772aff88dd03baa7a5166c1ee50597ac9a26a82303d91fe36084241f189db20cc8476887118a4366f0b048a8aba5d90b4fbcc2c4b2b187d9cbbe2e0df5f38d7c6b8714541d629f054cb16b3a14e6de8583fe0f1bc3ad61e669c1ca70612c1fd22a9644c3bee1d8520cc75b92637f248438ecb50c175dd2ff2c9eda23d3d5fcb8790bd16521aea907e6c5a3d76f152585da2d0c175f7fa34bf0bd9a73b3277fb6fd852dae2c91468d494799275d8f950d2913ff5c387b3937d2bb31e4efd357cf8793fa9c7aa36b83d58381935ff83d84089fef8e9cd1e0cb1f129576b22e35b8c61f10bb5b08fd57066d05e654d1a6e97a8e17a2aaa1bea3a175540d904060a5ad7f5561c23a061fdd1ecf6e864a7e170ca91fc2f23a7a8213969deb8457193be875e399bd2196ad57ca158172d1714f2de6857280e5e2ea8ab3a46cab7300817ff0803602a5fce825ce88b2f7c51a64a8b0e1c87d504c741f8f599d51d474baed1bfc994501504a2548655dd652aceb38858bdffff7499d53cc08d3baad9061010cb8375fb52d411d9e9f36f26bfaca262a5bc99e8a2cf59c6e49931b028afb33d9af0c6983f69b176d92f0f7fe650ea7713177828808caa4ced5ad7f0f023c893e5251ec56005b6bc720380ac92fb795a1da4e267e66129dc9a6ad8ef2a856f6ff3d3591fc0b60efa6963992dae56edf98934211af1d28f10914841f7fce410655a971b2e014cb032a1ef7f9122c596c80d3d8ad41f727484fcafb2c37ad0c8d0eb41cbe25255b735b031b801a2a70949f507630debf3b2688b5a5207fdbc9296130966d79a1a8642fac4aebf62521b108b0c2fbe8288d4de7cca371003ac5e7772a63a46321fb51606bafdd985ca10eea50cc2434f943fd3223ccd3fe24c915d498258d354961c40f3d85c6c5a7d1c6a826d4d3e7c6e140d730a573fc43ff662bd68c6a24a35756db863b73716ac0b5a519d66e84c0ff0691132d6e6d8adf84c5006f23ab9bbf9e1f433fc44f7139a0e7c57dbe014d364b7f6995fbf7a1a377f7318f46ccc64bcc5939e306e81a9ae4bf1a73d70b7ec231e8983b41471928940f9fec354d548d027473678d64fd51dece837dd49a6a62685169d85282381e4ff41fa425bdc5cb88783971de8f24b0dcc6fbc7b0cf5ae3e8d112622db115d96bc9b314be339c7a9011c662b8ed8400957cb59165598505b8e3d4b55fd3bb1976a10d69f2f004fda5932ca6cfef43004a86299892f752a0c40484729f3ffdaf8b84cae8550ec956a3149de8eb3f674d6f1f472639b328e75144cddfe886a3a96fd5a7ab1f1189831c88ec39540a68955a46cb99ec9823c9b2b141b3589767c39029ffc4823cfd6ffd500ccb4b4721a9cb06727aea65b34f345815b784939fe23b2ccc6c36e2cb17cc111294b68318eb8650376b9363159157e3d73e2a46de325d0d13b985fea4e5674ec02efdaf7428dee194a9a703a8ed163358aaeb17d9788c21622c93144d6667d4388a5a6ff288c9cc483214dd802c4a315ceb73a27a690d81afd7db60fe70aefa8f96653ae1ffe0cc73031e6327a07bd8e09e473abc09dc388b70222e7af4e06cf440aa8cfb262ff90c745a271beea641b2cd7a57f1b543c4ac766d308dc0b3e2d174e47ff42db46fcacc2499a8d21806e77a9cfb7b90345f241a54c2f898cf1d3a188f55665d3e089d8c0fa1f3b962ee994cd241bdf235ac7daa614f2dc6cb428f17c97702610e2af59ded60bc089278b4720b3bd8d4847e553fa2de9b124469d0eaa88e49e923f9f4d8262475977ecb2054dd35eedb552060aebe19ee4be12a809cd55ce280e5f43dc8533e2fb0dcd16d7a54ef7780d58840bc220dd737f27e6a213627edf4b499e824ee85d0bc52ef56b3cd309e14e5384d5b49da8e652ebd33784d9722c5097a0b653fde25120e99ca815a873e2e4b41d92905deb2d6a6211bf6b629f0dd390dcf7f91ca2e43f2f238a98e509a799ac1e2e0f55d5d4baac134ac1db24a22e714ab123695124cf9806dc292a3d9f0b288e2bbf6e8940d0b064ad9a1c0127a072392603a2d3f054f7c1e1208a4c582dc3fcd456638764258772c8b3462f503b72609ac42aa57617e3c7d895f8ab7645aad245d94953b25f2095cb15d19ac79b551b2c0a978a2a4bf7074333812b805f5403a811af77263550d972329d35ab9faed86e4843507256d0a8c49b73a0dc41dccfa5a3f6c3274da498f9ad00a5b143f5ac0810a581a9f2286e47ec270e7d95fcfb3b2e4d9ef83a98c872e90df2dcce17b8c5f71da7e6ed3a2983d497593181bb4ae1c3fbd837246cd2e84ac10ad7e5e369b0c11832c9829f9d1f2b21794cfecf594455a991e18586f9f812ce0486187954aa5838d3ce184ce20ce3c5b1cb9455eb4d3ce02927ed9e2dfde6c87c05956866c8d8662c945350ba49f6bb60042a2737e9fa02ba6e2944f575b69be2a22e6b513ce7cdfa83832b5e1a7ca6dc7378a02c1ec9cad8bdb7378c07facaeb1949fde3bfc7f2ec1266d689a9c8d85caacc07803b59e8ca26a16a24b6d0316914d7df6c47357c9849ad85b273349edaef7eeb213694ff4fc7699aa44c0e2fe92878de6091edc231c38ab63b2ff0862e878645a94204995d67613f3314a7e46c39eac9bef846ac851f6015fe2ad39afa0da0c133a300b90978f24cf3ae8f8366f229a8b1dbe088685bb4d402520e2efc6d683c0fee9f0eebbc55e3ca65665b862b621457e45b7e87745c91940a0fc78c1d4605ee17dcb0888e1e17505b3b0f522cf2927e16ec1b291a707764b44d99c7b7761b0d75e222369c84e15f26a31eb27a94125364147ced76feb598c363f50cc92ebecd2d770e732da33dec36315d3734748c6848f4db5ff3edfe7ab869298209197930179e48f0fde8c7b7317b4894ac8eeea3e98885b0a4091c15093c4842471833a6d65fcdf3a1d823305b48b6ae6c30558d86e36c4d547f81027ea45a3ebc4fb0517fd1471756fb56f716b96d811dd8166da3b64cc52b81ddf467e7e17136043f4a895d5248bd0a3d8d7213a143846905d276477611b0d4813340514b3bde63ab5f0199af4df84c16744b7de46cdd1575ab4cfcca92d95763834270c98a25a46381f1aa5f1fcb4ec2b798d1bd1e9d2284a80a427894ed67c59d988ac013300f80c4ebbbda1974b880ef09a383bb9bf46bca6e844f73a17bab1500244f48df1fb745d6bcbb240932ae7577ee4125ab9d0aa1096ff42e98538d12b94739586793c2adee9788c4ff88a0e0865074c13d50b4f32b8c98cc4fdec480d31106c1e900e2d79fb1692877b48dcdef850885d4c2ffc510b7779c73721f1c3b6b01c6a698304a468bb9905d1e127bb4118895bc6bfede14535243eee4f1e09b86fd9e9d2f7ee17dde9525f6b61716e88162312f40f1028bb6282c826480aa0af810c24094070ed62f5199f91082d3e0760d77113d2b7ee5d7fea62b22958e4e5c8858d3156ed397b8e4a326d20f3db61bf9c73f4d7e29aa741322ee16df10dfc355309ad0e812c3ae4d0ba489dc8da1cd4ec861105fdc7ab91e99745c01126944b6395158649d602db5df585b2f2b3b33718b42f4a926400a0b60da1685e72824cca089dd9f17852cb884b7f4589887d02afb0dfbb8e7e75f82f11bcafb2f3c2f610583f8717b485971c90cff2bef34bb7e216b26fde0701ea2a69c21bb0f102abd20421a1eb8d6039e46cedc9258c3e90409ab40edf8cdb987ec2257a26f4c99f31d298e691eb47f41c9e3512f7e4de772da6e77b90f0bf28e0dc7485e676bb846333f71baefe7524d20976db311d80ef54f11500b45a5d09973da35ee0c7d9ebd960fdc7ebd64c3015c14eba28a09d6798a9cc62464aa1b7037f4956f27e5b38c5782ed0e75d839c82d05dc5b4b9e7383e661d3932318a08be41b84a657b3943b926dab5c8f3ad87407399f534a06452f1e83c11038573cb0540737e22c4ca090e29945457ab4ebbd23e8bd2b2cb15941c6c10208413713119bdf624c0152f519875892c32e481c68fec37c0804c46f82aada58d9531a3462ae971b9570be6b6b4c030453472e7d2e6a68f744653f820936748982835a74c19f9e2683047f28bd07e6ddc1582650c6b7ec4e1ce249aa530bf6504ac793e849cf93547b28a6133495c78a27f6e4769dc8204faed5485bb9c3a7577ebfa2be6301d506b14c42f3ebc253b6a025f0cad15f31369ab4ee36c0750bc4af811793b240bc94986a8e16ff83c62809a9bfe6b829f702349d95c8c8973787f95772ba51c464457dd6430e31a4dec9198d9f0aac8cc24b38114c3af86736a9d830b9524cf0fb45a402266f9ce405394cb47453e1861131ccc3f4f44ad4ca1cb27f7249f67286d5b361ed23c27dc96e354e94142e7af52140b1dab6f8ac3c9c28e981293702f24729299f8912d860fcfb87998802d7b22546a22b4f0de11b12b3e5f48e1b72302aef06931af187e8f4d43f51ea8d825a9ac3dcd79104eab58636dc2d24efd16620f45fb1a85eb94ffa94c6853b1647f111f90eb9e17a824a61207db7e0117f5de945a5f594dbef322a4f84b5a6fb13a0725ecc248a39040d4230bfe2d8582f65891d5a121be5f5206b1312b71b5705e8a71cfa3b036a3f67401a5de1ac388128f2ade146a853d28033cdab43bdc56d6bd80d65bb3adc222c872f991f97ea70b008fee43b9bedd0324c871ce370f27359af6d15878961877c27a8555b10ccab6187741c49d30a0ef1218b0d2bf90aab71fddc85c160302b7658ad16ceac86ea4adde756fa2210e9dd1a1ec70ea3505ec8dc77e6a89381751ea1f06285ca29649d346d9514975eb3cf68baf8a1f451544b123f99073b439011dd217875045ecf6e495b3d4c3ced355982765b8902cf8fa945c67cf8ba09c82688d476cc0b44e5a871c005cc6905dbc0aac9877e85050100ddc0de6c92ffb452d4086c6cbf06bc34a5e0b54d79901cc3a4cdc05e8a712600716f0fc03de244eb08887514e8a7034cd52182cce839a766d8eb4f6fb6f567c8f54b569b3957ceb594304229107f055979bd314a80bf1de0d9f519397d0e3a53a44149d5bf94c01362c4b8c487c3d70599f786717b1d8c38aa3d17a18e053227f1d82c6dd285f4eb610249aca841a2e27c9ae7004fa9977bfeda234e4191c17a76e2a8dabe93e8137de6b15a7b60057cbb32287ff5584be0c9a76a5b5b17aef573a88074cc933d06c2fd64cbef4968e2f31758dd14dc4e80d8bcb38d0376bfef41f8628cdfd6719268a4c655b55deaf417f1e54cf0a1d816d7591249dbe60172f79f8275a1a7719d69f16b4e3252747d3e00873ad5fdf422b41dbcf50464b1e13f07bf9245673017a2a25cd543de7d83d7796e1f9ea0b76eb87a6c0936ae3fe160c29771e23b47678382d87f22e786f74683348e04cdb6293c224bee4a5f9d1bb4e31f41e9ad33b71d6f8870e1cb112e340fc80123070c3c127edb89d630f3722a7ee277bad4b7c29de6bdb11a434cfa7b6e3e058b0bb357dfecd6db174d71701052935a8a8352acced9fd5e8ef5951046f182dd9dc69018adfa7b020a413cd529cf106b58d98ec318521ce197fb2b7239320ccb288e0e45513fc8ab83c6112e0edf7165aafce28b4fa701732e32ff359764b23fbf688e731d2f1ad29942da75a4260120f96b2fe6048a3a1972b74e818656e6d9acefec258f279cf0a82c57f5514ff784f790ab074b83d960701731c1628618552cf1fb7d2a756dd4fe00e9400ddef9a75820c7f1be8587830da18fbfbf50bca17d251a30374193953c0a98a4162979e995bbc3e13b6fa297f3dff977a5766e84a08fb04b4a1c0358dbc8e13353a191204f1e06d0040f8cd9d07cd61d58cd96255a723874e074e9691ac9e70c036be578940baf1714b00f37466ee73e8743df1cb9e014eac3391c3507a17fe82147d7929b2d97a6dc4d03717b5160ed0ffa9182d492ad9c45ff1b608cc4c080b4bb918661da9b05080a7422b5b493e23bcd158c61831756980b20a936b7f4879d80c6bb48d8c82b4e076edc5ee197612f750b3db9cf4d3cfa6c96de127a0cd1c88167f182a1d2e9fc29eb0ba13416c4785332b250778555e7eb9364fc6e383b5298d5121d3a5e167f68d38e3dc98060f5a176d1169034c1c61f44e8ec782a24bf0626c1175451768d89d9a4402e5c83b1bbb44f9eec017ba9d99a60f10a9da0f4f905462da7dea16cf36da53b0082bc56c173bf2baa55c43f85fa9c2e9ce72865ab6968d5244058c5b69fa17e0127218b642017a61a2049d27093799d09653d69541e824ac485d626d4788df1837de1cca46f38058f5c95ea6044e894acc58cf8bdfbdc0c88653fa426a80610181831da732575ee12d1eaba4de1d244b90bd5c00ace45007571fae7209c589b6c360cc2eb0e308d157a263fb0e8a15aa8f84fc7903b74768fadfa6e1044cd512806c83c41616985e859a2369c40a8168e83f6be7d4020480e699ad6422d8ff1b8f456fc12e3fb34635d62d14470eadca1a7b086a7b907bb3bc9dc0232ac3e6ba5e30e8d7060698f6f163e521239be0408d7f247305695200b99c0cbb12dd0d7ff77a78046a55065fc27607c018fbbb33f585688c012de733622cb5364478e29ba3b3e3094fce05a2c1458a3e281d65cb3ae055c408b404f261e751e4d994823d02d3639388e0afdeffe10f0de77f00653732ff6b2c1babfb2e164a088d161e8d78e757f74d7385fee1a96a15891e36494b73e21c0fb4317ab1c348e1558adefe3c3fcb3ac5148941f6e8a20c4fe5b105e7e298dec87eaf5df29166cf50bd2df0a2011d62ddca0405faed3e79055ee576f0cd2166e3a6b5c3f374717092277c10aa77e4a0a5f94f96e74cd270bdbf0111a2f0259e34308948862c1ee3a9f4518297aed3c1a3bb2e9d60582c928982bcb2060c0414dc1a99ba0a399b04339c5c8c5c8bc6123ac4a89a917bb44b94b56fc16a2da2085dffeeee694abab49d63c61fb33149c58a855d2e7ed9a5b37c41a177e6d158b64698a7cd1daf5fd165855e2bcab641a5b1dd03baa42f19546eb5838083106fd76762fa4125b1662e14a2d571b3232469b2996761bfa84e306adef592188f088134c5d4d8a429d69a068aad1204334df458bbf4a8b71d13d3496d000f2ced5c2c039bf29e2c575e42a998a8b13c0a61a8bc5957f486ea7ab380f9af19fe01ed769418a95e0833852a7358ca908d08c1c6c11f926796da0600cab55286f0e20b099ffbcc167056b3907216b7cf062d9b2441da372024a0e272a6572e25a2ec1e9068a7819362340d30736d756b68679f0dd2cf4c2d935fcd0e2d68651397f68340a62bb5926a9dcd93af953f1b3daaa14546a9f02310097b42e0805027b68d1f2e8a6385c025910d4785d3369ac271bd608d28c2c00e0e61a87de56019cf0ff2c80f2b4b05406f99c65d2afe0752fb7fc460532e0681e506802198fc7801bbf823222995d6708b71f7d52884e4e7eb92c069f11a740c500b03ff809f1e7aad8be2a64ba74d86d8296423d639698e447e396fcf5f24f07e8c3810918c660d4a298735a58f4a04fa4bd6664295594b275cd905c2e29b4fb560f8e60c13db37500ec789c073696aa606506bda6c696af75c6fd907c522ace95059b3d0c7c996e8ede17bab0684a02cbf8d57f93c57fe553e6f45e619743d0fca17172ec54b89d4a95fc900a29f37f5dc7c6cd53f84afdda0cfe7557dbdef9e29242975d374b1a4d6321c07dc1d23bb76bdd042afcdda0689117bab106df4a2be0afd0ef6b6cddc1c0fd8805df6e265ee1245a716677bb6b8060ff9971d9cc1456dbc794d64e67b5e23ef20114a6e101b004afbd8c245493ef3230fbe34971b261279a0c867b4b58d0e20850a651e93db758ac880490e424780718341742db64e433d6e12de79a67b57f032bfa1a53ecff1b7f50acbb18a045b9b72a2526dd68c5a43700defc0495b554c8b785a59903ea2c0ae9aca8bcd1a815ee2779090e19a600be9a225c37a44232640511eec98a0bd5e34033d7fa8e3849c61a37bfd71df046bd579ad93b06ebb8ff5b6760d00ffc5d9269560de3a01ae08814e23f02f9aa05d7ea0d230047a5cbf9924912e00371903249ae8e46a046cf6884f5c747ddc2b8a97850a305c294c2c28524bcfac2ab9d594a103e782f1ea42670cda30d0d910f151e997d220f892474cb51cb357a1e3d5f94318a8849f0d04465ce891a18d88bed119e1ca1f49e6d04b19d72ab6329ea11e88da85ab4619ff6dc121922c5c051dbeccb45e01d9b60e350182508e3be1fcafae77236708d26e7989027075414ea41bb856fb161ac0b08f678593bea548411100b73fb8b34b0700d6672f3d690f8b43ec33c9a303ead9fb2cb8da22c66f3416b7c56da0493f50ad1f938499a7a3c2c69ca0b149ed422763a21b90e59c9b2832de220cbfe5b32b6929a3120a78c62d5eccdb28442b3290ff4ca01ee2cabceb41503437caeadec9f7d4e2e72321f1cb67b850985776f7b7b3439e3516124c0c0ccab4f7034171bee1f2669215c8850a478040cf810b48c94ef031564bbf6aba85fd5369c017cdb03cac97a8e5d3052b29a0e696555f0305b32b4f52691980897b46141d51a45760e3e10cae6154bd9b5a2cac821475f2cfb57ae1b80aabbfaa13f7cec79c000e400122a3943edcf04b210c7fae1fae50fe571ba7595b76e24a4edbdf7967b6f995292018c084f099208394db3fd8fa639fdf63860be69b6edc1704689da7fe228a5de77dfe964fabe5308da919ac06a0abbafdd473dda7174eb81438df551a697a993298469917b54f82d7229eeb913c7a1b8130ac5711cc7711cc771dc09c571a71c0ef51cc771dc736fa369b4e7fe14ce2c51274a8542a950a84fa150a9140c678930900604154de69cb3893addddbd2710e6cf4ccace3219b20da5275b641873b67d3327a82307c5e48dc562b18cd2582cc6414149b1296cf988a3b43863da1f713f39cb4751184637af47b01929339f8428b5fbb13279cd67a2a62962a32ec4f1c0afa9e65b5359250705057d4ee96c710631b9a143a5b32fa78d728ad2fc2ca8c5f93332d4ecc5d9560a91322aa8bd94450a26d4cbdcb58d8e54478b994d9665df94716c32cf085f36808cc9fe892a45674f0214a25432c29322e0a0ce16d99b9c1449bce73c0ddcc139e7d1908570488b243c2119e42c6b7b6eb39b7da145ee678bdc7b34e4c1da9e089f304665f9e0d79e5ffbee69d8214e8bf3b9108516e777dbb659f9fdfcd0a6c5e9e1f65e486dbca6d6ba5a6d36f365082ba89c4dd370cfdc093ba8dcfb3373ec04e42fb2badb0e7ed0d6fbb538c9c0e2a11d4149e5bed69cde64e335272b647a2cccb8ea68c36fa30307050e75b43861733e51b3d186d3c29bc1a1b6f7b469668b9e15a18bfab5d83d4ecf2451bb5f358de73df5dedf39f6b8309bd4f33a4abd90b323edc291c7e776dcd785da7f9c46e736b56c4e275f380f79e5b30860d47ee6e1fddce375e6593777cf649ee2b95059dd8cb3af63401260f37dce6c06893a3fcb6466cee69c261ce6dca4c652667ad291942b918ea0cc5c8974c481fbc32dcd6971ca186b3522ebfc6071cef92e7a0d9d1e23654ae055db49bf1da29499fe3d7f0879c1edd7223da2faffe0d1ff9a863bd597efd57bc7d3a35e526a454b59a6fa3f9a66b4e1b81abce8e3f4885dfa2ae40849baf48bbc304a2b54ee171b0ae38e928c4bdf5439978ea6d96eb874ee1061897a5da1405b72280e986f794abf86334475b6485f7e733f9344e5acf4baaefbcff4a83006d4a7382e857a930965b9f0840a6b7d1ce7c213aa86dfa34e9fa903bbcfebfa8431aa9f60a825c2401b0d9e14e18bca3a40a5d2fd6b6f2f2f33114db32c1f623f4c28f6f713d1acd8a2bbd83430aeb9e6da08d3a2cf145c7befff6e5bcf6cf096aebf37e78c4999d9465ee32da09ede4f072af298f913c99ddbec3066eccea43bada549455e43953cc65fe993c655ece88e1a75ed9574fd1dc8bd025d9f5a542c2d6e626c4b49d719f037b35a10e77a5df76063b8cfd7809a6649d3f052a685d434ed5d93493276b10f19622c699a268fb4af41fd89649aa5a1154dd37ec3f1188d66210de5bb94526696dae958a078b2a70d92d820e888db4856946edb237d2bb0ba4756d702f4bab0650982cfe5ef6136e24918f9ec65eea00ff96d89c8efe7e73259e4bbf2a5fcf1412b462df2ddcfc3f1b32c1ffcdff37f7c3beeb5af208f6e7eff5e64ecefacf735941dc3d9b1c887e35dc877a35efdecf83df738fd8563fff7331c3b06c8ff5e7acd7bf5e7bfa95ae9e1f8f71de9c2229f7b362cf25dce12912f6ff611e997cf3d87a3e9f90bc779bf09fe7c3094ee85da7fe1f83d5b2e2cf211f15e5e3bee5cefbb9fe158bfef7ef333fde904ce3b43fa9371bc0b89b8b0c38523b5458edcb6459c48bfbc0dce67d0877759d3fecae5f3cb8be2d56fcacf9146d0e2c703000ffed932dbdd76e882e4246f7e4b8bcb7eb411b76db56423058725773e0a2ae830b9f3596861d2d01a2595a21feef792924313f3372d93527258725facecbbef69d1909569596824873e6777a86dc0cb560f8f99385d80f952ea98b1385a143b50c51667bb1849e416820df5b3ff11731e595bcd4879caf1439ad0dba665a9fc6ce9008a6a8ffc7e4c05f0cf9f96e7375bd6f37bae420f6bda14acd822a5b5665996655996653fb3969c79a6993e1c8093ca3f6157bb47e6f395a00b31f7c8b4ac14aeb4e3062edbb97471da1b4093eac4455bdcc7c0595a1c8ea6c1d1a2f81ffba2ce1f59dbcbdf6c0ae2952217ca8e61249e4905523afb161ed20a7e70258d2b6158145d7eefc44935bb1286c51197bf45d1bd464a97270bcc3bad942ebfa58c1c8015e1c8502e8ef312356e65688199c2e9caf92c1fdd9dd685d39532cb1dfdbdee02bcc0e31a547e89f208a003ff1175b419eb6d86224a93db5d337dfa4f903a8809d5d4f09c3394ef196dd1e77e0b5db4b5cd60d3da32fb637b79736a8b6a5a669d50f9388ed314693681b4022c3063b774fde5f3ebc81bd5f37351bbe44deaf95b49dea09edf8de44d7dfe09246f4e9f492baebfc6435cff4d86eb4f9bcaf5e7dc88ebdf79556e87dd35bafe4aa415da163cc4c6840c4b9a0af5c28df03cf63ad336ca75de37ff149abe86a74785150c53cf57662fade0215a86a6e246cc0ecc2ad708b5fda829c92afd51db423b713f6e4c6caab0e54ddd8f74c9f681a1ea4d21e8fd48bda09f29fcbe1f39286e4bd86f0abd0bfed885e0fa7bd4d412aa5221a3c2ae612b4919fe53e846520648ca30cb2b98080e8c5dc595b8fc5a384e1c2e7fc6ddd217833781ae7cf9f2e59b86300d2184a76d94a3492317b54bc98d26d09599691bdd6851bb94dc6802cd4cdbb44d0b472e6a97921b69d995492317b54b299b59bb7c94495ce4dfdd2d934c26a8fc2ca5eca14ca110010043210cd918620dd518a231b41a9a3104800f87564333865e865c865a865443a11d520da586504375e8346447183a0d9986c0a16f680498a16fc81bea86600230d40d7143012061881b22e1c6d00dd13fef25888888888888888888888888888888888888888888888888888888888888888888888888a823a1a39bc6511730b87d66da46b9cefb40d3a9a252aa169797192b1a35583642f86040338c5e945e5c2e452d492d3e2aa115d0ed67cd30bafd355e946e3f8d17d7ed5fb914ddfe192d49b7ffa5c5e7f6bb84200200502008f419794a9eab2b02816ebf4b8bca73ddfe140a005fb924ce870a6d310d4bdb6b76e7494e4c10eea9dd0941881ac080f9b4b4274c5168b163346cfd47594446117190d8b4e86c9d68df822f2dad64942509eb21c123ae9ad9991b55c4b6284b6200410e3f3191524a6770c0449b1e2a34b189f90471e70cfc9bc6943a9fa5d2ea13dd6da48cc348191b794323c0f206bc1ee435fe2f788d6cf71fe235eebeb10b7ccadafc6736cb7a80e94fdba3ecce93cd9aecca63fcabed696d566e1b0eb14517025aeb3eae9383541b464894978ea0136eb76f7f0aa91542453b31fdcadf4dab51bcfee09116fda765ff1b2da8ec750784283df9b3710b27534a659531ad49b9692af45cb7367382a08e3296f33f94c664c8e256f1e60a89ba6aa1f5252083298319130329a5ec7e0923a594b2816c49d9154961dca00a3fcd342c32987f84b128da5c2e97cbe5126f5b230c050d8eb85ba6e4359b92d256a4c45b726666f6ca2b3781a41e338f8eb2f67939d1640c674a734e6627aa441a8305575bd27ee049a5cb73ced95736cdf48a8a9a467e933be2b8382ed32beae8195d7e0f055e1425640e294d683ba22e94a681358d91bce9f7a6d8b8fca3270564c011bef38bb89452caefb04a19edc32c09256bb8ff30a4410375476a823bc65cafc1066c2dcd528fe14c29aba294f9e072160677a5949af594944a8095002b015602ac045809b0126025c04a8095002b015602ace872f794379037f3399437bb226f62f3c6133583654af28659f3a5150cc89bece5cdbcfc994bde7cdc4f0d6bd8d2227386e58a5744028f364d330c03414a29e9caddc5517eb7c4c16366e613e55876b7381d6c40b79666ba11c0850bb8d062298b152c4d2c5c6860689b155458ca0c443e448981061aa2643c60917151830c05294afc64b1e1099629b61fc0c8015b8161a67b0241348bbc10d0ca51281728e0b86842b9a022e3c205d904620a2f03238bacc7072a84e8c187255b94e0078c1780714402a21b5e89e4450c5f40b9235c89d485d2f5a427bf0ec7225286805d035c296588d805c375c766728f9a6674fe024b37cc873b7efc05933b764c227961749979b26250c786b11b79a89c13486951def148f60b909f3d4ef791ed28fd92d6c9f6fc30189ec8df6c1fb5d8a48e0d9332c607bd5d83145d54b93b6e9196a5e6247e6666666666a0531395a54f663908a845661f76f11266a6462c5df963bd31a08944c611920e5c62f0e0eba4fbda71d45d3dce097b22f3717a34a59e1743c18790cb8f8342d3643fada6254952471413cd159381883b7a4ab11d2a8a49d3d49846b29f5eccab42de645eac1ac91b0fe66778e3893a6a2ecd47deb0955b45de682ffed4513c6166f49474d8bc6ac09a90bbcb9226540474998db68841390abaacc431b4464dcae5514bba9cf9209561c41d352b97df5bce1a55d4ece7084e540d887ffc64365b727243893a7ee69232ccd90c7dae7c8f3127d1361de8f6ca61cab04de182d88c0b4658024aa24d42d0fce901481a35d84de21b6b19ac83cd0074a0ced8cc88ab246f6a9908e31c6d63a409310140d163d4714614ff57ee3e33c3592ecb47dff963cf9d7dbb3d32ba4872b44317afc3360108b2f93a2b77004eb069dd577f98293e8b1894a6991ff39a2ce44df699f7a836d3e60b9ba7f2666a3f330f649a09d8b92b4bda0665a32428610eb5695a4c73c157ee45f03e59c6f08b9b56df6b366b6359a0d96e881abbfedefe028355cd6c9d3c50b905c60aeeca842b91c008baa92b916e40a573609feb9dbea6b1c0bc449ede163c861f8b902b3f92268ec7c026900a8401c30e57109145113ab4a69df118e6e1e274875b0f777bdd51e4e18e3624b8a33c1ac11d651213de81936210424a1639b8a2a4498b45706f96aa6831e4f304890c5a38308ac20758f8200822b0d2f3430310c6e8418a222a825a3540e8e0c312448ca00620b4d80544f460081c7491430c2d1e1e48c28a2cb2c8e1035fb4f85bf09a1e117c11461838c4e0c39418b4d8e7e23416660eaaf54aa432c240c5a07a572295b185d8930407604e74d0440992e8c295946103a432762863063bcc74e300b5655d1481bad9af6698f0608c20cd23011745145b4c27537ea1c4fc8292583167d18a789144166464b1810729e60f3c7cd151bc4860daba48c15d39b9de95485dc0600539405899428626578881d4e2312e7ff58107083e8872858907964832b7608018485481c48b1b7c00c591bb945284c868e04d989322274ba8c0490a7838e226ec8e6fc512139933b919d252d1cdb2226c4005131a6a40c611adec67b2a3ef4aa4a5264b474820dd400ad2952529406471747158ba6c8f385f0348199628782d75500450085a1dae646851a1c40743ad0e3b0aa5961541908249ab4376c152cbf3af435ea2c9cf1358aeb436508b5ab20b40020d5a1eee300c576479b5367012620c201808b53a9c01446b03cd6c82132ca1e2c7a88a131c5d015c59051244978a250deed06f024b13455538e1bab20a2682aecd955540c1e232b86366a8ca9bcd7c331ca8db8fd46bb865a30b3d8ea8c5ce858d294b37d519ee7774cfdd739ded01ce04519d1a99775621c6954852acc828ae4492c2e44a49c20232ae4462c1177dc395485250b99e0477cc40a9fdde69a689da75ced9699ad6bd2ad5696057b3fa2daaaf55a57a0f532a29532a55980a478a7a9cf66056363efaa9906ede076a4534efc1702c02a6de6b2a1c850c69e108fb7012a0251cc4fc532487ca979542a6d36251aabb2872f8ab73ce79026f064a33b3c7c07d4729f7b2e33ecb38cb3d0d353bd28d861ab785fe9ce6194ff6e624bc19266650506597235b6c4e8bf26788a8fd3c54b03dcccc842745d0810861d4e92981dffc0354291e7f2a306c26fd6ab149d841dbd3efd7b0784dc3aaf7ab617efa694fdda7fa0edcb1316f73e3d197b31d2465b2ffe9ccce1ff2e02c8b7b22db77cf6ddffdf6ddd3f7a647b790a1340db749b6e95d9e939a86ba84e1d7623682925adfcb3edab0d477100ad6341ceab90c76b3902f43a9b54deed1902f2fc9763014207f5508a2c21a8e4ce56656282739a8c5cc04f2e0db826753eb66d362466d084cd4b183725acc42a634a34e5ee817595d16f7dc73f68516a975e2fad6cb27e298f1605acc1e4a07bd843fb20b30c3b79ed868315bb560d36266bfc5ec475052b95932d0f6933385a2cc32abe306794cf6dd423763207eef9b3d6f3c2343c561cb4b8042067a42136b076d38743ddcecb9ece9c60348ca64afd909a4846a5519de8c13352505914d85a2fafbbbbc19a3cac476327d05bf10a6c5ed4fe1b7b8d5ed37703b3deb034fdf77dab66ddbb66ddbb6ed3ff0b4fdb66d2730ab90c52f2f7ffa20ab47adfeb47a9e4a43b666cc9841e34f34683c8fe967bc6c799d52a95429d4a764ab2564b14bc862cb02e2f2ac0fd2f2362ccfe95b4edff23ca677f9907d50c0d1d182108fe917e93d4ee01c0648ca71822a94ad2d94ab1b2fa8f251f764fabafbc2e88858d2142c4c84bc49bd3f17c139c0778f364dd734b1a671124208efcf5ee39dc63beb475eea0ebaaa1f394a507dffba45ed6be35bde6f7051abcbcb4bc832bde812b24cd6488ecbff8e8b8be862a9a35a6ccc085f9e46e8f2441d442394ad55285b3542d9ea58a16cc1b82a0d9db43c2bfc99e15b362c8bbe8d57bdeb5b2d9605c4c6ab9e456d101baf62056979976f7997ef42560addd3ef2ccb47f7a63719dd9e4b717ea8356e6c51472eba2fcff25821abfb212f21abb346725e9e25846577745e965e5eb65a7e94575e4c2f5b367e9449acb0c74bccc6b7fc288f4cdf461ed3451ee3cf7a7779f76f096d84a0f7acb0a3b490aaa51439717d4bf50d3a51bd296ca316ed5b888bba49d37cefdfaf3692d234dc8ff4c3958ca99f0aa59441594a654c7d11c2da12c2015a7eec28d755ef36ac9431d5ce163b6422a48c3f1872116c82bf6774c4523816f29490b1b884dc3e2dbb002fe18c306807b3528b8b5eebb3d3a2c4724d3db54f3b70f6d9fbece7f83dbbf3036f77774f73017592768e51a9dd1d79495e11a244856349ce31777f77f9ab973ad6492d1eb54b08c6519aa69fd25add4fd841eda7e08e6cb372bd8f8c64cbbbe70fe2fd6779b8f79e2d0fd746a0e5e96cae5552c75e6a2cdec3ee3cc9e19e5f3a933181cc3ae159794babe341674551c8636ed02e9fefdf3790342c8514fddd5d43a40c1fb15e55c80a37aa74ec7ba2f1a6c50f3c1f9238cd39e79cf3e8ce282db67d20ead84b37f673ce296fb23bbfb178cdf6f35570e5256f64d08e5ed2e2fc49a5fabc0c760fefe00e233c7be9cecf93a86310b285669dabf9dd83a4612412266fac3ce8236fd847cacc67bdea9c7fa30accf4a63a14f143b34d6ebe369f88e69d77de69d4bfb3237dee3dcacddf348e736d9b76a66956f8a1538f70bca6dfc1243e7dba0e758927618e60362c2f78bd86e7c97c1ecec66b78bc0f61441610eefba7cdf1de0b59f3bb972d1610ef3b4bedcc7c66ce2ccbb22ccbb22ccb7cce647f42c6d3bdf75cc803fe479ffb0774effd939ceebdff421ed6fc69698b598d16549816b31b59d47ee7e8953099f71c438beaaf428fccdd516851d2d954a06c3ff841a98a0a68baabefc842ddee736699b685a30325b9feee3ed71fa7b15c31c6c6d2626c5a97cdeea86645de4b2b170315751552ca5491ec1b7415d957b8bf94adc47f714786b5507604ee68262dfa1220d74fffb4e89b06661374f14b289cd72fc3e48d56b342c73c216b68c8187f2b56a8aca4701c0a45834618c6c40c6000e34aa7693ed64fcde60d3af943c1d0366508e726b1169a869bbc421c3a2e73fdf98885fcd583bce99e3e4c863d5aded01aee814be2170f8f8569b1beea7950aae767d11cd5d7e71086456d9094951e1344657ba0705afea007b125ce3c5cfff1c6a51f00998347204df0a7e34af5f461e44df7f45545445ac45362f3d79b9fd6c2d28b72a6f79c9a787c3ebea7f65b3cbd04a965f9009f3e0ff0a9859132a7efb912b445c46beab938eddab3f660f8d96f7189bce9ac28657c690b75844eeaab5a2d04fcba22e563b221238a2266afd1f7329a651cc759aa6594665ae6a2ce4f0c062486aa0d9e8499daa5b4d6d5ea5ff41feecc92a28efc4d69f5157d4eecaafca1e3306d8a07f21898166db21f436090aa18eac069d13950ad402da960d77778567ca989d0cb7b93e7fd0e683d0b5af93dde33f83dad530d3949c9037fdbde6b3ee2d52fced1aeeb5d17f27b5e6beaab8ab658548bdda996c110279c091964010141d483bfd99c54eaf9c1fa29bb63faca33a8b1ded4839f937ab07ecaeea41eb4f5bf4d9f837a93dd79b2fd4e0eea4f76c7f428fb1ec3dab655157faba95052ef5bca781eb70311e07abb907b90a854abf5ff5a3b284d9345c4122e757de576cfc99a56f218ff4ede7cdf7545dea3211bd550998f78710e4a656b1857f52c5f5cfeee3ded634679a067e977b2c5629b7a9efae00749f198648b819c1ef5414e8fb23c4f78507ffa93e551594edade3385dbb631802d43798ffaa99e4e15e5a9dec4d3f2a8f74c9fa37a53cba36cf518efa9d7981ef55efdd37b3f519f637a54fd93951ee3f13cd99e07f5e0bfd7a42ccfe953ef7d05423df839a8072dcf137e1ef0eb6f36e7f429cb63fa14f8f573c0917d4e21ea73c04759b639a64fd9f718efbd1ab2f85bf018676b2407f5a76f806c9dec0ecabae947b984b231709616f83f4ec84195adae05bee94719e3e6d3d0e5665fd84ade67f1de73cffbcefb2c2b85efb7e7f159d962b779e1f30ecebae9d2bb85b2452e4a6a291da5a1f8b7d27ca9605db35c40af777677679feeee3c9e70426c86a00edca014f36122880e06c311d2a2afa0e3ff4410584148d36cefcfc20b3b0e903974b0bce14f7dcbfb0bc3aefbeabacc780f47caf8f70eed99f954bfbea70a55cf852c660159bdea59281b64f52a56101a9f7a1a9f7a062dcb073fea5176cc21e8a29ebfe7ae6aa85266a49799023b5ce0cb5246cab40f19f20225713d514769d447b4e87f638af3d1e52b5ec3029ff5b2e5bf62a12c0b088d5f7d101abfb23c4f725a7ef53c2e4f23c8ea693c0dcbd3f22bb98341cb02b2fa1a1f64f52f4fe36b589e27392f4fc3f295190fbe6c8176646e29c376a4389cc3c60e75e4239d99f1a59429d2627ffbc8733f6b3373615cf573f04f21117ef03d3ea14e28cb4a01f5dff3409d4ebffd47ff14d2e721fc853352a677c217581012aed0a2ff4bc82d433cc67f86bf4b58398703a409feb09922ff71866cb97054e7867ae305b54f6dd24288263118bc18c2c51d3a3a4d6384190552c69f9558c1dfbf5126b90ef30b6af6938d96660b5e63a32365fc47c8a2b28ebc01e9bbf3704fbfc3c072117025d292d26590e973cfa3fdf63cf4b9ef6701d17efb20f4b92001a384dd7e1d262220a020825857c67e902855b941889b99dd9dbd9999dbfd496841f59f25eef8fe2e0591183538199a82450888b06202c14f9325ae2412883354fe04c06e58622951295dad64d822882bbc0cd78fae2d65d4199d76eff6f6aad3fd23479bfe43c7997f056666f618121c6a600195296a0002193310d5d0edaef9412f17d0962d61116349de689b65659ce536cbf324a70bb2bdf79ee5e9a86565cfb1329bc35956f6decb1665e55856667f80ff8daca4d44b617b3e4a4da5452a494b49fd5aeaeb53823eb25fb58fff118d2529293119e231a1062eb7b88dc14b536249536249928695827c66cbe230a6d4e26aa9086777e728eeeebc5df95ef6bd206ffce2c001245c0efd9c0d86c120ff424d42637ee4622cb1184bbce49237d9f5d5aa56d91b57f1b92357f14972446b6cc112c67603b19bc69dabd95f016db16d99c675db779a16ea6cdd6f1fa4fbadfb20db77dd735d8ba34fd449b7c93eec3ae60f691a239dbdf6b4e648c7789126e2b9834fb4f7b7e95ea7ed90ee79e87bcfc37df71e6d30fb2dd4e9bca7cfd379f48374cfb18050cf769d97fdb86a75d96756c80956477965c80a4220206f7248193780b491328e139bcc3ed916937a30467582e11435fb17e44d90ed6d3c66be2065e6bbad5f92981520ce01720ef0ca222264cc9d4adcc0a24eff1584d8101ac97e7b4fd3b42903d01290367907bb8ab8082907b1eb2faf44c201952baea435f294eb0c03c281137766cd6dd23eca0a2d8485169d7b2eac1163202b5a0b90f01c9d12aeff4a0595f2ed10fc3c87ca98f71dca98cd0f9da661e6246a77dff7fbbef73ccff3bccffba2d4e64cba49c6187cede50f863f3e9d2ee70747639bb5d144f4002de26000ebd1afee41c6f8cf24a1d7bccede9f8a94ee782a52ba1c0da9ff40bf5fac201f820d402c79f9b4a865fdd879eae2167d11a078b5a8e5119f7b8900c56b65397776b9f6ce9b1d5989eb594bc9d334f25ccdb251df1f2ab4a809e9f0699ad1a67718d9a48f8f90ebc448e40df85e6146e3ca6a6fd3344636f797f72aa5aa5e5ef5d27d5e54fff2309c93a3fa97974152dfb2f324a7e55dfe5f5a2e2d76e5314eedd88de2453dea74f2b28a329950a8aaf929ecba37852e70960b5ad1498b9123552729298701a4092d214e0f1d4da3230457a0944e4d8100e595ba32f653451295cff5a69f0d42af6e4fc6c0c8582d168fe0446d2eec578d586d20a75247bec27c83862aa5c0c8e8a67179554a8d995248316ae980a235aeae94af57d3b8cdbb13d5356f2151499d9ef68daff99ccd9a8915a05db6fcca6cc3663618781e9dd763c1dd9d5d73ce8c0577f7eefe4b57dd3dca2a57feecec93215f498548991f493833994d77264a29a50784840c3948c256ae58e1176df0d89b241851fb4a0de4a1dd2b4930a24ecf8604232a07a6c064b4e8bf428bce821029557a8cc38e623e499e1375082bdd982b1ec73ee897ee5a665150a765a0249ca4ba0d7c7c5c41948088263e33c84149cc87491042fcc466f8b9f42a55b9e14a0c53402005cb0f42323683d01311c911ccd9a7bb3bf38f33bf83a3e006a30dc9932ae98d2054f92a48d85512848249f9d36b3679074b37a2044e83d9b325c559cd397facf3ebcc7efefcf7328d7fa56573f66987098392a3eeee963ddbbbe5d10a5f8194661340421f9062e5bb32f613851b2d39fb747767a551c68ac086e0f44072b4431de95dfa6187236275d582b35ab5d4bafaff5fd5dab2aaab1e28bca0ca8fc9e0630369a565cb997ed952f64b29abacb2ca2a5b66724a9b99aab3423777650c86cf0475a4374b42c58f0c3d7032650909194e6000f4ac2376b7fceeffa1c3cdddf3b39c1659fe8f9ca6c97eb6c5d122c7729891d0d0dc6c547190446b0112f4d332ee02dba4fe993481be30411f9ed14ccb00e538eb825f6a67b665feb20c4fc27c5fe62d3ad40c5c0048810bfaf0cfc7bcdc7bd3c7f4f1f1ae3f274df88e6499c6fd117a3929e34fe7530bf4e5beeb2c9532dc77811b4e39a7f7907af5cc8c28babb3b9617b214502a28b5848b52400d545ff0712354910b469886896024c6b82db0119246caa671418afed3d259b5d44b0732901071c715123478384d5b9ad34da31f13c240d2888129973110bb7cc4e7f2e7b3f5b66dbf6d46476020e9aebca8a356d461114de8d22fc9f5ff9268459a116d752d7e1eeec8755c96db356e50f9f329a209f977e1d692600a1c63f1cbbb62f1d396ba04181f100562b2eb675a16a3c57ea2e9e235a7ffe1927d8c9895947e64185f39c9eb12cad610d49bc2963fa97e86ac14e67f3f2dcbc77cee3996ecb91f98d3352c202e9f7ad3b3829cdef4a737d99f27dbcff02d132bb3415c3ef52e9fb23fae6f997e866f81ed436f6c51976eeaf4f3673ca6bfa7950a59f3c5eebced699d52dccb964bcae54f3f4f3aef65c677eafb888b58889bb4f8422271fcb4e86225a32493e5241dd3ff635ab75e8fd75c19454c61aefde5cf77cbec2f2d0eb1456efcabe110c5995fad347b24b3d1e2af7cc55d7f5173eb813a6d5d8dae69dad3a6d934d99aa8d54f6bbad5761b30a8f6678c3bda8844318ec9eb7a1f4569299cf5e6e01d1936bfbb7dfc328271afd7ebf57abd8296bc5c89a90051f92565fc25f48225358c5ee77ae98af6ede2f73e08fdcf7aeddaac6c6d766c2013f8e3071603d62fb86701e1a79fc3d47a51e9f3e750b6d2637260c0145aa8411fdd84b5b2d1a2376cf5633fe5fa7a776498a68494f16fb19590341c0229ca9b2d49fba057cdec0c0e9b2ae22b6b2f0cd4a99a5a5c5ebe47f53d206873523ba84ff57cafb23b4f3e9ba3fa17bb737ad58ee9533d52f5a770c6d730f5a8f0e55b5a5a567fb23b3bf55bbea7c7f4392e6f7a7e138d3fd99d1dd3bb7c8f29b4f1a790f5350ce151e18c9f31e37b5a21a45ef53929d5cbcfb03ba87ff99dca0a59acfa19cf6c73663ccbee9c7ec6ef985ef53d335e5c5a54a9daf3b53783275fc2e0589b9ba3bf87015a587dd3f8962f32dcbc915950d4b16d1a9593c494798748994ef21ad6f722bc6cf5cb8ef5416cb09ea7be8d20acb7c1b2b2d5b9c0b7fb2c0b08eb43f820365e041b1f820dcb491ed3cf637a96952dee25e8a3f90a12d5b35c242d13dd98d2df492965b6a298a8c42d56653d0b15918c04000000b314002020100c860322a1603020d8f3bcfb14800b82a64a744a9909a420c86118a59031c410600000000066604884a615c25470e8f010f189065bf4445d10b8b739b138fbc05893b52c3095e2ea83e6a8647a56bd77012bd51e3b6c9f3887742f8b20ee74a76fde9910832823f95d470083776d74d1bd868c54c9c931ef85466134463bcc318b876871ccd7dea7e1ac032819e1b65b597bafe9c1d59d278e2a2d962bdb15a405530967cf3788e411c9780ff2ea057afad6521295ea5e62226e38d4634c9ce8f25a5cf3ade504689716624e9772a0676b9c0254a8c6db688f243670f17d23c5e006bd59cb78d6aa32c1d8d7f28308082f6cb598bd412f270ae9ee8d8e7a3601dd6d39c61ce0fc640777b7fc3d6096209dc61c80a4bd13f86b5a6ebd987cd1b3bd8d7d18bb2fe8f56fa2f100f29f911bb2b7890035a7540654e02615f73010fe565000ea747ba3c6bdabd1af619f341064f88b497dac6a530156e1cc6534bc108dd2eda7788727cbeca858af8d6c3966aacbff080424d8e00850b128dd61a37933eb3d51f210b08ce82b80ae80016c850721253bf628f95803f48933683fa1fc9900b81b074db6b0b176751690b1b8ba6cafa7193b6dc39c86468d4cce4186041cb5cdf928754d5eb216483e0ac7b95c90b0a5d00095bea17056a4b211666bd557d56ff501fc7be05d8a62b6a69697a595c08b65bd3cb0044f378c103c4bc3ffe38cc5dc561796bd6db78d860d958192d0fd0af14302ec503b9dece29b40976330544f683355a455240d850342bd0457e6e538534ed3847c015ba04279aae07215ea38b21ef56cbedc145432396aec996804b067a49ebd213d5caff4593cb47fcb90821aa03f70d640e49b49ffffb09c40bc4f98844882bfc1218b12652d06f314d962aace0abc10cdf4c7b2ab6fdda9d783b6c0f87cc4ff7b8c111d7c3ab0c749ace3439196178b4f6c53cac2c01e1a6e08892a2acc6e9f025e0b4e21c29037f9ca3f186af1ae9ad928e321dd99bd7e725207062995f4c83a19ee95b2b5f9e8f16e4199700408646e3261482c4a73632330f60e79a39b2701c9c7c0cb7a0a70fa2e7ab31f7bd461083991336428b388419f1b4c21afa2775a3e3699a79ff8c2d4a305b83222d77af91e09de4c2a8b3886985c8be4d4a9a17146c2396f1a930cdd24324e9f5ea19d5f83f3f480f32888066e592d3ac26a40ff2852b61b29b6643f15ef007388c703abc909389937e78a1743539db5848079b8f91554d6918454d911687b50dc4a3e0fc4f1800e285d35fdf80dfa0c8b5c59aac688b3d22b4545c028dcd4ccae874f91b1e3ae2e053f2250520dd8860ae4bbefa2f6506b56bf913ed293b6df6be9ae642c66c9525c1ea8679fe54f421e88e0d5cfcead63c83b00d615d2f7403e42c04a268a8f7f54d138422c6864506ea23f235601e69060eb09ef68314ec109fc0f55db122f023902932e23eb01f67c357ea380ee81ecbbd5bdfaf0a0b21f582108050c5456e894a862d53c380f6f74b5309e6da2f4f455783c9134a77acbb79b078fa1bd2165cb7165bf579996a8d86179330ded40142e9c57f20acf645a16e44dd5714284cf8967b55f2cd974b3c70dfce67b0581b1f79c89c204977da5fd025fa51a360be647336b9aaf3be20bc45712fd5feeb3f629358e96c78feaee0decb519b509ad53c8aae236751276eda5292d4b2a8a2b4960a790c6af067158226678789f94ebb00eb96963cb4404f1c94f640406d8db0b13bd75bcd4e6707adbd44a3da9fb430218747d045266a214a4f460687fff26e435273598468b5dc1a2afdc829622bae443ee2a39c8b6c65fb3b8b866317299f487e2dd9a7114f0c74ea2c70e1252751b4ca6478dabf167427e6a6f69cd66cd34bba19680604b962633aeb6791506c6749be6216ba7cfc690dc180383d8c00358b17bd542a40fc33529b08c21c15a0304af3641f487e339b397bf7ba8d9251c870fb4c8670d29d2e6ba05916fbb6dee3eb468794245767e9c2da92ba0288c2b4ddd7d91d0b466fadde43b577a06a937a8ae88ae55ffd5b74cba41aa30ca83a1b4f6d35cbb4cc3b0cf146a36d8a5dea9ef6a3805c23e51e8ba8ec03fdab878df20e5e9327b1321f805b5483468c997f19f2ec7906a553ef16b877710d588f982c4e2bb6d6b45737b37e2bb74fa75ac7bcf78a2ce301be4de6ca4c3c346549f8d8d4e3b49b3dccd8d57879cc57dc52d26d5739785fda8baef19064bb06f6404050183f64d425a1d8ad4e1a1f6433fdcdb669b754ff2325f23e3a8930abbef90c004680037b5004304fa85f9fe87e92374fd246b70ca76365024553f9f050ff9d4cef2b1f40e664cd1f84174cafcd457e5303a543005559a1866690800c8b00dff3f9935652ef9c607b059d2bbc07a108df83fb911a19c2c7648c3b27a048b9c468154789f8fa8fbf55fbaa1e2ac3f7c608eb7b5de4e9e555308f1e1a28f5c0225c6ee0e536df9fb1d59df865e8b0b951dc36880503444fb56b709776e493b8bee32c9159ba0f02f7a9f5c9c5b5907b0fdaf6afa911b44c1244b07a85f6f645fa4abf00fb2e3941f01ff19e82de520ca473ff215d694167fcb3f8d816bde32ae356b487526310db9f820e04bd94c380de06fd19d9bca85eb55dae48751d34ab6491bbd19d67feee46210248036cb1360627fcf03f541f345f0cbf8165646eb9862255751985c43241598853450860ffb255a4d55dbd519bcd9921bb8a434e7e6dabe30d75ba1c2c09dd1820bf64d18dd7c68d09fb0867df2d50514c671b030829848c1b00fe7a04592c8a36124868f28f0cfb05a9f25c2efb0001ada901e309e14ac063d209a7c51759cd01fa0c0e370b39652a3318f2482a2ab7c9b17b098fb453557c69a94e9605cd1e3727f4609aba3305e4e40504ea09e94d048f4cc81bd69a26673212a190463e659a28b5ee763be46fc1a9b8ccdeaaac917766460bdac12570734aba76d591c386523ffc21085c1bc4330265cd1829ca3c9a999cf1c382c4500c6e57ea54bd0d490aea47e4f5390cb753eda2564fef53368229c82bc12027fb95a190d8bb50a1d1ef897b587a3486f46322019fd719c0237cccc8c669d8b911df5e6552e0f2f6bea79cf9a4d4165af648c4cc7ff453126f2256721f612dcb4c74e9b96b089d98559673259f94319193bdc21599affc7085ce20bab22d473ccdda37e1671910dbcf228be46605aa8f3f9895c29a39554db93d17f0d0a9ee816100d156ba574c92378f146d21a18c2dd2fb2081c183c02c62047b8c446b10243d67366270445bd5c313c7713e9cf279d25fa53b39d5492c435c36d9c9d7e1900b245f222a4275b14e8c89b026c34ea479e33ac0a675f70c26737f21b540d72acc898a1c73f742cce5974322315693f09691986be793ac27b5237de69e678bcc4df255c1e9f4fadac31dad044dc1e6e8ed364874bf25d437e4293ab291a6d8c312692c8e464468d5b7574c8a3f4cf01811db3521b080bbb510b0666ab16bee1880ebae585366287f9751be12bb0828fdf88c2a3cef2564131f5c6cebc51a03d79cdf6dec8bad1370a71e16de23ccd4d79a040cb86bc2c98eecf84b3ed3f109eaf9a9bf73dba4e5a6e9be3ba2f3ee86eef5ce021a4a32258b7ccc55137ec469fa43c8ef77f914c04aeb9271b1a9b58479406d42225ba015b1b0a5bf6569345431a3a570ee6df5a602a2883f378bd8c7bf163c6f45d4ab0be84f08101634db614b01117f7de32f562b7cb49e0dfcece4c20cdb69f1e434784a911812cc32d51945f903b9e1cf9f4032cec695ebadcf52550ee3f17a8592a4fd80091faaa187631b5abe3e4b2a95377d6010613bb571f2a9603b9f4a28885b109b30a9034ea36f62c0b0d19b92440513f8a8c2c6f630c7d8a2b5c59dcfcd4693a9ad8cd6d0855ad055da9df449f0bb2079d3663bd7f987562481203c1cbf4242bf745ab03a6c7026abb975f5d9778352188c012a85124b79c825993be82ee3af20b45482bc58a53bf8865c4c2901dc24083d8b0771818b57ff9d79214303aa8b1b86276229f97430ef1503b23725f5747aa0f24328d429c462385e8ba9424030dd05e4b51fba5dfd9450d3f5ea1d1b4098cf5ddea2f9e4cf00112f16aad5a1088f47a7fc95a39c65601ee623f68c6b28e19b9ffcbd6224abbf1cd42c33e5fc0211213f24317ade6b58cb40aee648d2b6d78f99cffd4f2d0a64383937f9bc8e08e3acdd11f6b420d16aafc215df4c1efda4489b0d3451b4e8669685cc633902525950c41b17404966c7174d7f9b34bff0bdd0182e591549c54fb70c98a154dadfcbeb24cb9cde24a34b9f1dae3df224cdb52b497ec4183e4f6725b6a4c43eb30d7580f6df2aea44eb14b951f4179cc7d2a992fa0cfabcd43a5ef1698d09afccf8b3fd19a2173f974463ca2e5f301e6cdd43dc6166931b319a17881f2a1e2fc27aefab11a3e0e5822d4f6168d5c9c89b52ce5b20dc43aa05b9a82a932a2c14c6680d8b760c61e4038fca1eef5887c5fa87b839b59617a343b60965850a43f67b53d79d378fcb4504162b0fd21f49f2b704776c9992227cf2f4cb0263d746c8a9117b63cf3abed6f6853f2e476a6cdabbccf24b0aee4361faba33b0dea4825527b251880a86c56699337145af63b88e70be9b19a3593b077215b8b99f0954f5e952017bfb42b12167a9712ec1123a768cf5489ffb39056610b75e2e9bd691a985c0b64d21b3fdef558a6cc1b83092058b623151629f0bcb995a18617aa56cf565ce9c421251e74e33bc28d27e85f1202144f0b7704eb545bbd7798a67d55031cbcc69afc90c194c80a8476265dc4568c709a9e326506f1d331f47b3e68c256e58a8433d5c11bbe32ff94e6a435334bbd876b6e33db9f3535276881f33196a272d854cd6b39439cab92ad04f01e6c747ef05cc7c515525a046254a84f332a9ed9f5539879c973680d86716a591c318d488ba38999b37c4637a81f757a5791687013911ecdc6b6f903b7bbe91df051484c34db22d9623d12aab375a95408fbdf459bdbcb17f028eaffb195c94d01de648a20be754124c0a5aa9f0fbc6996a3ff8a13ede018504d314337d7ce2a6497d2006b8c5920e10a52350824b6b8ec0e8c9e45346bd92060778f3574eb374554ea6ed79f6ef0df1099a36b3943e2d06941fb37e6d003b7c1e0d18ee9a5369906128f5760f17ce937015378fe15a6d547eb7998afdf79e8ce150285829c3ad010b3070f2fd4a01b8068397bd7956bef95943d99fcdd409b4fd0443c70b72e519e9959a43590b460c78a91f37f73e646f4120138cd01cc3d469291f667a906b6f1f64858814d71b3f14792db19e889db92a897083d9e9d577ba47fede66627cf2de46e01090b4af61ad6a55e7a0a2048e0a4a3a8341373205d02b90562eb6c1bd4184d67c4a5aa49cffcfaf4586fb4db9c16489d52f690362cc8af6877e3deaed0c590d0b9e76b281eef705c0f72c919be21ba755865f0ed47f5aaceef36eb8d000b405d410442654f1d2437d6e9081f5e54d6c125d39aa9e10803533c77ee1770dc38901defbc22a06420bd8f4515957f48205afe87e27033d3005b3b14e5f8081fde43c65ecc8514d30db1ebf0901898a1f886fbc8d58e17c831f2193a04e04e19019c7d2318ca41655b9b0ea7df2f4e5db2af3215bcabdc6c9409de5d26826eb6d73ac7207a70ef32182a41c0e3cb7f4133ad1c4d3aa7749a862a8988771218afac0621d1e3058ef1d271b4a9fc6f753dad9a2850c24863eacdae7c4e394b58d1b04fae0df7e144cd5d62419e2f0fe5529e38c9a65387d5aa5fcac01fd106298c82929dc0fd4c2a61bb055b3d1a453131d319388a462e51dd0d3800e0d96cb02dbaa21757fc39eea523d0df6438ce452a857c24be0d80b0df3647d6a38e76590a443e499c0e5ad11c469cca746da94f565d3c70c360eacc2afaa6e3c22670e34d5ad0c952e6d39d0fec0b2d2e7e28759cb71e85a6f050c50f91a70047afbf29a90c79cbca52c91f4c59d9edd687d0e374097c8cba579e07cb0a36b0290a763b08ca44e691909fa51138b1b5f0bff86c41c5d779ba824fd3a84e0f83b398f3ec2d87485373e9ec1570c3bb514a26631e27af642911dc80b1683658904d9b3a4090996ac30ddc42d316969c136b6f9160e150c4fe318b2ecdd353e0f02cebab7773407838ddcf8afd6f0f15c5fccae512d52963f5abba67e37a4affc98c10288ed26531ca2cf2df5d89df502bac042864cf0901e40e6bed50c85737dbc6edc592715d49535d0e09723eb8e330a5d1c7021f5407cd1c80a9f3fe059fc5792ae356660b74c0d9353d2d19f656605090634bcd49ea6354313f139f78721d8c2c31eec67d4a6a74be565cbf4c6df453c007d9fd9031f235b8709bcf72c7fd0f8a96734b4eb158115c9c4921540f982c41b6eece4522b7a5d07321b4f27ad65723274ee683ac495e6417adfcd05c9eb1668c0a6ed8f161eb70dd72c5b31cdf5773098554625b9af73fd36b6d7626595b373845cca7230aa441750fad3aa32ea9544d594bbf065e88f99dee28ea47f94eb0478e859e51357a2ae856e67f94cd99fdecfa0b34005002d0c6d25034e8a6db1e2536cca82d702469f5d88615fa4c598afd6aee5e1c0c51c9403191d989d8d819977f80c07633cc3015190800d23fb7961730c3662f9587da0e0e31b3d12e9907dfe7142b3e018fd160d9b64f067ef3a9e8a6b1ed10c4e2dec3f718d12898c7522c2de7695d2e44a0e55553cdc7df246b3aaef804440ca2c051cf287a85ee215a0f40a5860f8cf59fd24a46415038ce27deae40b8bb456cfe13b023213d4906a13fef4ea1d1519a0d90f3937cc820922a99a4932101c2de1dbc0901ecf2d5390fc4010fdfbf1ccc00fd0caddceba8175c43483167a3614b195a49a1c33a379ddf541f190177d5627cf426346f4558a5a7281b8fe2c7b88ac8fe793f7137c5f77ea1839f3d26a29888bc0fa7aa306c830f4d50f0816a00031284344f661b57341da69844abc798341203c20b73ec748a3dc83960c180a9117dff5559914ed85398104bc999a159b15ea734d09c2f2520cebb86fc6442cea436faf7e3a1348cc4ffe4484e1a139883071a1a8928a7bf0b27798600b7bc443e91ba3dff4d29919d93827224d2b5d617f1828b8eb1f284350fa583af5843a45db5195c7f9dec48323c2069a56c055ff66381de4d11acd0ae1cb83d66ed76b380912ae67c6041f730f8adf3479e4bea81d2963e0299b9b4b8aa022605b3ec3d5d1e0fb94109c4bc8273b06fd3c1cc9a830e202f2d3421fd5d975a59d74211d8ab3cdca04e3b99b381b9d0a1c46f71e581353db7e08c74fbb9727e7060555f9a485b3032160cb4d20fc7362cd825455bb2b0bc5852c829fa181e1781340981c562b060c16ae2836def6d2f6f2b1efe0a4e696aef6da160cfac75b77b627f5762d4fab23a3474f45dbac2b83c59df83e38f10eecf33728d4b6dbcfeb255a24861be3c941de4527fabb6e92a1945268287c55e643bff0213f660da8e1a616b0865a38998b6fb5ffc804d5b2e4cdeaf2ebc7528037cee50b4eef9aecdef707ae338b14f37cd2b523e9ddd6aef2bb098757340cb44d982a9376fd9cf6a72432835d9ea32fcd2bdb71e3ad7dc68918d1f5b77c13abfe04bf0b05747dbc0c3baed999fec41fcf4f12379566d364d902469515f4a7bcc063cfe92286d33d326acc081582168a3133cbebda683e852c5631d1affcda2752f862cc8bea5130b109163b4b54e06b2881b12c776b86525316b08ceb2fbc5b9b55ac3f709653484c521a053a1bb61bc1c43ff14b18b06c1ff2968e45df8e9618c732a238dc8b088fdebafe3aaa54c1b84ba17e87dc9911268352778efad21813662b7020810b7233dec45adc329e20b6a8cdb37aef6811932c5587f66888e81035c4ba6e4f38cac75f85015deeba9263b1d6d57c7ff0e04d688c74424e3701e030d2b798cae91ea5ad6a0d090d489e815da06f3563bb06f9e184c24b1c9c7a55e5da386953b28675ff97a6e80b390474f5eee72d4dd7d48752033b1a86ae561d08ba82622e4f47ee14e0e1951055d7055e8d777691e86a50f0b92dc7bc36de40c144564913b7a299b2e40cc2f3ffcf483bd208fc41729cc247d46fe08527e1a5e57ca736eb57a82a41707b652ff3425db8273ac8e8645a17e344cf552375b6f552fd8c5c1d16fd26fa9549c0ba39d4a4f1edd872d1f92c41f086aad8ecc54f2831eefde8c6590e013b87c96e52e1705ddbb9b590607eee4646cb6e1726e0b612e660dafe21b7ddd62811362e3d4c258f5c069b69964daabb3fe3fb725cef93303367b14aa48b5eec1d2a51fdd273964630929ad69374fc19d354123fb14665b4c15fd9abee282b77903662b8be7072864816170549aa0546ecb4e9a1e5344fe80561b9634af4c36406e0e06aa0b727a8067b488f47d6e4e72e5ea2e9c34b4ed01ab366d0dd2fa2695a4665bd14bb3b54d1831fc96cd7e6b8a0e4895372851f1ad13382dfdd27f8c8ae6d4821382b1ed103780bf30d6b08ff5a1b2201d5096f6a667e394305bccefd66170b051aab657d89d944ad6d3626eed17148d0e24dcd47c79e6af54d09b2281d6d74e16867eb0d76d81236f0c08d5dd0775ebc032e79dc458bd2f2bf5d6267dc53d37b539876a97216b5529732bc2f5e158614b367d04edb22aee61e7fbaf61043171587ea5adb32e9d88b332ae53097b4f408f14d32d84c0da94a9b30fb167d40d05eb54305c24d227453ba685b16288279e3fe180a61b900842a934f9d6705de6a4633181d02f12d057071678ffef94564a69042a54924b007c182b7c6b83292d74de3a32900ea89205c0b649ba2754dc82b1834fd51c99e1ae0ee3dde76707a856dc420957e283e4a900c12b49b83d230bde5b45e9f86d4ff55500fde9325b1c99b21b863c02a40a7ed64c8a0a42969bc6fdbeb43924ecec841b81b22d55b7518d27b6c3580f9ce6d33e186ccad382bf0fcd085d2f5514e8af0df92ef77c294728724f480cf0c19019c98e5455366af8aaa57989344d80c38b68303374265aa89df26efb895c1b3987a534c47caa935180e9ed207e65e0f9a30b685dba085c4d416a1878e33f735a5ad818ea6a2b630f9854f2bced44f6d1eb53530ebaadc0dd4b930a956a0010cf303b7caf12236f4833430f838b5a946d9d891bb09aca41c50d38148000d370ee83c00ba11d1b638d9e0b38941f1bcc4889bbc3f41de2c06bf4f98a4307e47edb04b48b5764477eae845e732b4beda13e7bbf154c7a2eb17a37a03140361824cbf39760deadc17244c92b8cef03566889026f3c05173d0393c830eadb1dd41ea4942164c02f26851998c1f208ef49280a60fc55b1cdef521a0d4b9b946aab05d01ef262fd9411a1558d87ca8e0d630581462ce587fce772743825745d3511c58d441dee81426519bc7937a4d148741032b478c3a720462545fd2816d035de52d7344a255d035816d612bd8ddcbdb3cf670a8931212a0d541a11c802167d1b902f0ead26de517a54962915c7057d57605ffa50fa599c9835d38f48499b53788e22aa59520fe5f44e45de3fa91f4111516177327b005da94f0bc8ae7aa6a6489dceb47887e83e4596a89080b476747b0d65150d03cf3265ea0a356b3fe34833a8dd8f7470c9d04da10ef83663a1b9f5903ed85219ac39b802a4d8b0627a75f2225e531f2679eefbd7293d973ab80787f9172353389a71aff7bf96b356b755a19c52f14878c094db47ce835f128046dd6af6033b504eb579fd53e68be769974032a2221e2bda37570aad997d6cc8c972a2080b7ae73620302b6982243aa2d445f72fdb765769a8ba263eab59acb940d348ca906c2301115ed53b79397d6411958328be5ed434cb3b40843a2831401406104c39b32c018ed3000b783185a67db5fc20b8983f0c2f7d6ac591685479364bfdc13dadd87eed6729d738bd5be4c5b35b642e8b6a24d767777015adaa73c9032a5514b11fc980e4e3515ef3019cd4b92bd71c1e273e0f73c5494c032c21a974471e14dc05e44053f590bf0e3e6442918030ffc081cba6aac622221eb6fab267b65673fc0d6b211b9a0d78987d940855ba492a199ca17200001de358474767946063e85a64d3f8578b64c5fac7331288abd6b164ff274412b00ec6f4a041598f3358014dd0b410f127620641d9d2c4146b93e6cfec8c4cf75c573425cfc7ccb41710d8af2cc469b181480a48ae977663762977049722d114650d41804640d27221fea5c7884eacfd5a42b190b3eda3c9410a791808a21767ea2382b82afd803a0b8b249b4c5640c26dc162d2eacf7f600eb839fff66e9a0d593dc2700de487f9c6545dec3765867b76f53833b83d40f06d9d9c2f42e8a88b6d1da413e59230318511c183d65216f3e2fd9cb0f23a410051bf16f693d766a46b630507d41fdab98014903627d8c1c49008280f0856064e12032446d90b554e6339d4f30bdfb3b8e6669d67cfbcc3886c0b2aa08674ca1bef0a14e8c80e0e6a3145a261d171ea23b1d3e134b4d0ed3ada74708b991c664caa82d0b12c4260eb4a12ea9772cc7914983b89606abca426761498ab76269925ff80c04558470b78787a245575025c691378cc99fcfd9eb70a3ba147681cdebb25148e80001cf910c937d0200e464a068a9e7d27bc7727806326e4be673f523e551266e0fc5d89c43aee38127722b72538f09413d03e0e3237c6188846adf51361df9b179a53e513c2b2b0bc518cb702b932915a71bd0ab077aa574b593d1b246293e362723308722701b99152056c3658b3ae704e2721ee30d95d42598c7859e7680a428aa5933fbfc7cd14c25e686a9fc558413ecc33cbfd9469dc78463e961499b4ae9ada0fb7b514020f1e07199aa13ec8b5b8682e2f7115daf72a032581def76a10987affebcf3ff96dfe5ac7a3d9f933727fbbbdf6e6e449e26f75716f6c1e9ba1cf90b507758e482320e4ebe1036c1def09e2837a9cb6a3c8a69ff729e9adf9721978953175d513c8ef9016d00ba0bb37c34e4cbb8e4b0b6ff8f33d6c554054a193a743c807048fc9c3c6195af627d0e8742eed4198117a7f1a0b1d480bc89816cd074772c38a5d09127c4d40d59f9e4cd7bd6469030244863eaec99cf25c1a6eadfe660618d6467ba6607545b6b4290a21874f6e4a3c9eb051e7c2a6478b66125d4d2bd7955534bd0ca6127d2c45accfad5e0e902090f3d7edd767dd43e99173605e6fc99920d1d8cc9200bb9972ac61c458403bb7190a5d1ccdf0c0e1e2f265ce946dab7c56ab746ab9512d6c275bb74ae58f0aac695600ac19e531c070192d675318116c378d3c8aa14b1aff8c0805125f86ba48256977b3ea8684f22dbab88355b9ec53b949ab7663fe5afc1d2ee6db1bad66b7b3c20d8eda8c88189a812386415042ed0d6b0270a5868c301cb952ba414b645c47eb714a08e8b776e7fe74fe0889839ffb13cd4442e2ca7509d2bc41e2a49bd678339dc0b33f0ea45ce7046896e684dded8799ab7c1a3c574102a501d0c5914b813eb06cb448c5300299a666181ef31713aea4f9875c50181975335958c378662ba73fa64618beb9b48a9843742010f8d10527ee64717f1c7e8de0c844ace8c854444978d84f54c46b24d472603f234927226e799d81861125109d97056bb303326239844d7b2f6742c96ecfdc7b362cd8eff33c7e787093489443c4ebd36b9add4a2b1feec5fea93b56a4cd9b453d8ecc91215ab3d41eecc276f4ff0e2798b10b79e054cf8d024e2a7583d5bb5791c5a053a9b38f7f539bc835e81eccc5281748e783720f9e866867cf2c005462ff22f9a5f8b20f5df0754437024a262ead2123af76cf986f947deb80d5fd15550bc0c9d7e716c8e6a68dc303226aec08a5d67390648f21b7c3be82c3bc75a796f08b310196d2edf093d80346822135cbe061e4b36fbe4dfbdcd128603af261f78d0895905e3a5e0a757ae22b87e77b6acd1378507e90ef814d151dc846d0a328d6c1769ef95c30e318584ae82910ae9d7ff728ee630ff9042c32b16216e04b2080c5e8648413016b87d638113658bb2fa81829d2a3c420184791c93707a5d463b295a77d8e396b49f1d884633c3c6d070130a6a9d569fb6ece01143f08308e7ec89391806533b001f900b2687c0550e19c0effc753ee2b09bbfcd7f222b658e069ac5a6a70b82c5767168ba6285f6dbc3bc4c9b3fb8639687d5ce457e0883153d810f087ae32dc35d5cc21e0237092f64b64254422d44242fa00e398d7f8a7f76c634da595025aeeb88203ac16b3135f13bd28151f9cbc7ae32263b3cb13ea3ab9d40d8918d47022d75946a06e379cc182ef068c5696ef4e93e5d369a1b788c075460f8acbbf08f40aaf56341e0d94b0d7117f2378f24df90ab100eabf811bcbae470fd4e4270531c3a36d67d7b4480314ed10e0fd401edc8fecdeddf04f854d0bf9abcc37a96a86291cd1c5924ad228676895c139674aaacbb3aa842bea158b6da699d176572515f8d754de50580dda02cbc520867c73d0021e4584f5eedaea7b917ba6f810be67204bafa080a9f7aa04401811f761e9554ee506a183cb013d426b22d6190250855073457b5a5f02875c8927e92cf3e1011f22961089887f0f1d4ee31f77e5da9fb34590a4dcd57890b2bf6f40aaafbe7ce7cebb515fc54ce9c3b2717a650bf33359d39a3b3912b53eb417835a96bce2eca281ef9103b7d694df7598d21188c56f5d84354ad5460b0e32bd797af662ce91fe8369cc3fdd9a4578408a440dedf00413552b7e6e615398a8ef7d247186d02d24faf6476f4ada3fb6a1bba98731876a632ead0ce13b2c5d0dba38d54a2746a3e8064dd2c41cba47509bf32b737ea98bdb81386ebfb780a004b16215e7c8697cc023b4971ebd910d437f1719e4f46f26ae27cfa9bd96f0721fada7666dbb022acbe4d710a80f2d67d5e4062927c10bc712559f2d7cc77e5c65849313ff06cb31241b7f8812d44a4436468933473da363d2f6e6bb4a087728bf5c3490aff63000f92ab7abac2ee10942f21b5ad961e4a17518f1b8c204e0005bbba5d925e0ec0161f5d5b0bfb67d3888fe00f3c22eaecf7d78b3d588a9210db8a38f1dcdd2748254beb9434315c726e454b3e7b46d1fbef875dab146b7c59a611491001ce6180a3f4f537764c57d75a55b90f6f97cdbf6b53345fbd87df2a29dc845a47b38cdfb26ef8d0d150520d6e5e51dca56666af7a0a358e4c0cd22e43a3fd294d50beae511180d09c18a916e701dd985ac9979747658153c16f5f7d0af46a919796892bb74c28bcdd8531266a62b0affd6a59d8979d26b9ef4f178974156a59717f6096064884e57718f65356012e43afde88069a837fcdd57a4637012c7abd9e2388df7d0d9e1066364d64f2c20ea277a0a064fed89978c56089e901f08b3af108d80a73ea8ddb0a52aa3ff2a927888628487b3f4f7652ee1c9df615a0ebeed3eaf9104c315f61fea1d763843c9a36288acaf424467e8c476049ed9f25e278d6b4fead7a567299d433494e8323aed8057a55d6bd7d3ab46267af9de5a9dbc1451bb35d3f9aeff1deb4c4058595de659796a012444bc9ed3c8a773c1be573bd8800528d799aa142f1e02b53371115a223b40d7d7efacf65ddf8b6e640157eec123390d9cfa35026d631445b77f0df22a0bb6da74c787bfa8bf2c0fb8174891e58a86274cb4dd5e3003e9b9e5925ccdda27fd98e5cde02c713d12adc04a1de07d6bc261bd90797c74e15ff4c66504f9d8f5f29bdb967b12edcd3f2a29fcbef64686b881fd275c6cc554789b5dd353824c24353516254602b346667b92fd3e59e1400482bac299fa36100ae167fa46ee6c955cdef16ff0ce4d4e39e13970ff7b298dab6ad6d84313309eb2b3fc4e75a8d8962825aec8b5c928453cec18ad603c8b1d0c39d5fd972453d6987e8f3522c23f6bad8006409cc5897b5e39da544138163d1b658e99ebfad2a12299711722d95c0bec67cdf2888bb00606d6068f7e940d3353d3ddfb9905ce18563d3dacae1661ab817135545c2511bfaf12260d0c72eab671c5b32a0e340e14c9554b071a149e3505d5520252b834b116d18692ae0b186948f37a3733827f86c366401322974012c33ff1687b856886ea1f744512673cf5c71c53292f0c1607fccfad329bad53c4e9059201db48a8197077bc44bfdadda7b20d129bb17080771466100e4f249b7b4f45d60199ed03ab5328a85baac9ad6b8101f1cc0cc7370b8390a87a01fbabfb660ffaf3b915b4963195e48f0d41c1258d2b82768b349025fd6b93545d647b744cc81d9f6b6b257651cdbc54655e61c8ac8ca8f3f9bdb3c1ea4ffb564aaf646485f33fc39096b861f668ff57e9a552f63380aeb147626c06f4a0decb1bb394b0080658764e69195ea6e7626debfb2d23433b933b5456754a0e2867972f9da15dfcc307187f7e645cb1b40008ad74dc70c47802af57f06550651e8d63fab6731c8a09301ff3ae68c8346ba1686819658d8c1cb3aa24aef9a5a01bb8d3c863f24797273e3af16736bdc0ce5ede1362c9570b5e87b89ac00ca69e48715af1c096db8a6ed2d61aa5e2a48bde01a5f170da0878b2bf161071b52ae2a289c3ea91621cd08b9c4458a0f1d96d7e4a5c4a2b9f05c4317828f329be12fc23cf4ff766b6720586170588d54d1ef560d6d3f2987ba38e840b6e7e75d521149d7f7ece6b7f1e169a0569246d12cc88c1183c94ea3bedf849c73aa110a76845944e7de13bc8ab62a429071a65349db979e67af230a9010b9cc4a7d7df04217ab609278fa983673978d0c242d883c913cb634df08327ae728e9120f4e06f6054a49a806df159d4487d8cd0a3c0d8ae5ad2294d3390724960e95637aeb45dcc8b6014f4fb1e44fc522c5c8348c591905d7a6d925830a3152a5f105071db42d4e350f1ab9f0f7f009b62b0eff2468a7ad03e5d5cdb7a17a58d8cc61b72f461080359391d6ddc1614ab4a879466f2bda90aeaeeb83a3ae2a396633e9e5176446563eae6445260ca33815b72be8120653d9bfb7aa84a6d5436b15818041aa67ad8533488d24772ff8ca3890f824922176af9a2e16e2236e6e75be085a62417812d870d5961b9e8137a285695b589aab5710678b0fa8ba6c51ab9952029308c8c3eb9fe1a31ca68cd7bf4874b382a9953c23adced8bbd06a6e4ee81f1dd0881edcba498c357da4bed07d6c50104c1325c8c5b46513598ae8c1900fc1aeefaa32c81bd9088cc44154c023608089aad020ffd404fd7757adfe7dae61fa81626a72252864e83f853cf052b4748c72a6aa13a8ea75971c69d997ef15f9ad48b0cbefca29a6ab28162c5aed25ae36b88ca56c00458cc49860a3b1ccc32e9d7dc7d4c88212e0784d108d172e1a5c19dc4db3e2953840481068044b5ddc9467492c4f000e9e27396421de5ea8cc2ceb5f7982c793dfc889765e016f1c233f5470bcab715f6aad7493f031a09898501403af3161c24efc8d1206c52c1b05cd62c2f24da06c329019d77e870a753f1b8855f5701b8d207f946e14c8d5b1316454323d4144ff4b32caf40e93477365168bee40c8288b11558f880620cb47c66f2bb1ca3efd8794bfd4eaecf67bd335592128acfdf9a9908a96b465485735e31e3841e0740780e9b0f718822b623f660e7a1492755738590781213664098e89630b5ef8e6fb8eee9bada6c433b64f95c27324534de524b0140bf6da4aeea7230cb6eb0c4c04000d1e79f8c733e67f83f366cb2ff2598b284310b3cd2531192f5e49b59a714e49b008eac3c15c29caaf0fd0304f59c69d34c477b6b19953dc84c0076bf9386f0a632c2bb7766fab10fd19f89ba8632542d20e1b355d9df0b9e0093b25fc674b8ac45a0192c15d579352bd0618913b0d0f03673b8471b6a2dd92adf6858c4bd212c24d2929cb91dbc5b098034bd5ea54af54a70ce72b1316a835d2881310f442db3440ec3d3b35050e142e0f597c4a5ba095a05b073014b6f628e4911fd94a45e19a2b70746136ed9c5e2889482d9fcc79bb1f0d5975902680b3169c94bec1b5081f75d7de1a4d9614d0407c108c9a7c5eb264e365e7efafaaf0fd4c3e57dbf2ac1a100f755b7a40b53a7a5cc309e5335fedb34836cd4a1106510d92ce3aeee3898f0af0cacc37e58a7501dea84fb07a40bc27c0963152cb67287a45e6908a518fa428d46b2c8ee7a9e653910a21814d56bbcb871ea7aa46af808829adb801947353eb27c0e9cacbc97033dc29da0be2d10f02df1eb1db68cc8253fbae8692d1c0fcb72eb879be86127ca35c771fdd7bcd97f193111b41bfba00515d15d4e3399684628d191e268b92a9acfda878610c62996f9e2ad9c934f04783c75642eb3691d4abf907fa9ac179d32e008008198485e24689581b30dcf53f8ab955fd1be885ddcbe9aa5b22b567ed7f0188b7640152fbb03c1d0444204d5513494cbf0f18769af0496155672c74ca48affe63c2190e86ac952118d1a7ad152dab61c485320c2d8754f479b413e815a99729d33413d91bf3f380bfe9507ec3b1f48bc0df5a3886553234413e4b49900a9c2f83dae330f6a26338b53f0e44fc3ad2a1e77c7a55d3d95cbca02e9a207716ffa419c0acaf92e08e2c40ae93e25b05c30ebfa8080028816959cbf2755abcd65dbf68099ef77ce40e0312d44d5091ab326e13c500cb22d1f213067343c2ead63852da80eaa53f5aea3b385adf31149e6eb9e7d49f342b8951206a830fee4917bf4fad94a9bfa809104dea15871b1cc480571cef8b50d86226212448ff9444e6e500230675ae47861f1d0705356756b53cd7a81b055b92d88953c060cb359c439c1f539e3859be135ba95f51f34ef5beff7707e1dc648e6207cc6ccb32f9d450bcf77fa29391a7dfc9e6bc4a80b63f7168ab86283a8c888314a6d55742fe5618a3a731013df5f4bacc15a3834967b7824f74c407b9addee292fbf0953c423239608e7cc11e039cef68018079c6294c745026e33acc0cf40325a9611af285ee51df75b265300ec547b12a4fec47772996faf1c6121dffecc12678fb5f2ff65f451591cf4526bd87fe340de1ed063c8569baac935663ad2cb50ed95d7077d31fd4c21864cd04bcbb6e955c1b12198669fc19ffb729508f019b3036489abd4f54f6da3d7d98dd669c148040064696bbee654e86cbe7eb4741f63ac28c0d4c154d6c3459cf088adaafafa629f05141a00423401014c26f6374d5d52de1a4403a305880693be4fb27f49865c3da4d23adb94685346468629003a38496454907396ddbecc4c35984898d61484800ae11b6e3ed3cd2d4031f82fee88c4905889ecb93ed9a0ebb786934c878f4114417e824dfc30a0211d0d9f4d6566380ebee97f62480d51b73b977e7dbb5f6f733804061c262a50920a167d61306ed0f937ae71c85538e4230a4bba36e486324a8903eb7160b6492ab39b26f052fe47ac213a2e4183a6b52f8884a1eb0d8e870d96d33168ce392736711cdac9e9fef8bd40cc9d613d858be4d8a141af61bd8e603f84dc401a8d5e94888a04dd2c53999073b48fce56b1822030565430153ef6a045abef5f099c34127383c2a98c725ef4a6068106fced04c80269926c22962c8a52aa22c6cc1611ca867f16458b7556ea40147a804a81203d0cc186110004aaf88a393e5dc8b3bf42477d4d5c001b2560be0e3772109bcc3955d0b8bc87dfd0413e1afab71963c70e58ca37cc48e0a33870e7f60d3d1d61584c73a1c9874673ae5806a6e30449fc62d985603a3624ab5eb05549216a2ebc21fbe978caf7a0a90406c71545c649a640b8e3a8202897d2f87802c5b48bb3a25315022b0a0239e2b3bbfa5e9ecb2aa8ca05af415329a9115573c3502ee470a0d251df3ee47a887825ab4a20c6a7a6707cd447904f20f6cbc9f78150fd3bd9b89ec1ad8fc8ba7b94f7ac2c04f7ea132880a5305a54b158bee46cf947febe08f6db01f5a7511d725b9c43ce63735d02a8d51499f67ad0cc2dc1b585a7b12736eb65050023189a9ed6f4a219aecf2118038255d2a0903312238a296929245369eb38844434e73e631b80dd10ec71746791e4a551b7174b82e5b3ea1ed4f22d97a2e68609537051b17c2068c65e114c4cab7953ab6a32e30748a8fc1b9d036dbe52b620ba0a8e7aacad8aaafdd5515a4ee04ab8aad94648638ac30488819c74ce4a6cad15dbba3aa919cc923d5f28b62cc3add3fd9e128d410d7da17784b972e5c73be36a2bc084ec346350b9a8e8d2f1d26fa54f6fc1a407091a55448f0628cd37f1c8d810134be218db72c43853386577b783016089481503e8b63958574da675bcc18c6b5c62513c9e47ad4174e606e7f663757564221c5e0a6ee1fd8128fc325452cfbffe691c0503441b97a28ca4aba56437e29683e3e6a94e0763a1d1a25e88717625781cd3f006fd7926e99da7f767f2deb12fd70f7cc855111d8c798e56060f745ed1b23a642e9cbd558ec77dcca789079da96a59ea75928f67cd8de5c3f1a996678f15d583ef608b2f2325f4c0d32c41ef7b113e8a510c682489125b35167d986c3357576dbad7e4d9d03c4afb2732ff6071069e5ef182b2664db68899e644231301f0460fb6fc54b4032ba0f5d2972f9e55de0c1b98c834845960839f1dfaef5d59233b9ec2a26c02022237de1a249d26488a1200830925dcc9dac3a49e9da1e7ca001c589396e4025d4089d56a1c30606f05178a9b1b5ecf90abb4906020996fce009eff1d0a3a6744ffff8c6caf73205498914e554b976fc3f6626c741de68085f5fd364610578a002067a034e1f8bfafe8df7f5e949690dced44b511c90d1959a607859f9500f7960621ecdd5de66e1e062096f71eafc025dda5196076426b3bb70953ddf4daf49d950e4a01ebcea44b3a1fcca397364716cf6b686648ca5f65b1dc1431a8b6c82fb8c8a41d730f1ad43be872ac064159ccef49a09ce04af7c07b589f80b0cb97b1c587cf44959137db774b826724522bca81d3e178d6421fcf6f222310ee2fcfebd382b8451a71bd5f848fa6365dd84035739d2a134a3fee466b19d36d1292f867fa93dd1b04dec7ddc79086fbbcc4e1e86b11e37570e65c010985727a5c3527453a07842d6c786eb8b333c7eb9239f2bbd731fd9c3ef9db9b98a627d3ba42577e7998522ed8188ac5fdaf7ef3b8d3b90662a008c464d1b3192bed24d57f1fe59339ed129c0531cd6c685002a903d7c107eeefba6a9524770441376b94c5b40fecbf932f86695d8bf0a4dc7350e24b0b5950c3fb71909f8e77938e4893827aeefa4384b4f98e236b22c17dfbf5e948e225ebd342b202b660d1d5138455c3658fd6d1f942b556fddd23b44f1264d84c0505efad12f961d9d876864baac1235c05eb022eb0033060048522217cdc5a1237bd0c2741cb7549109752232b783c7cf6a9f9a7eb3cd71eababf761f97b48428adf476ed82c29168430bc92528400fbecab03580aec4756e730922864f2cb34b0918990ca7c3fac94802c63736a0fc8bf0f3452810695581bd5245e5c08dd794c34016f217627c170f68130eb5dc142c9d6266847e058473fd74d7c84624b377ed153d1a0bd7e827b365ea9045fb00d6160b1c7e3a8a71606b1730e55665ef3046ca61c651d5189715a0d2a2202ed75ae2ae7d44d04f45e8e215ca021d4c0d5783f636e9659a1dbf5424d0f6b7f1abc999714e79b39040bd89f251ec3c9449d78659d408f1a5fc339f8e80b777e872626d018803697089c1659c832c90b7b10423c87be402882cc4068e57ef566e2b9e2deeb448769b31c8a4ca9ba9b708d2f69c7e581add40926bed6885800ac145853f8a3d275d480e69c246f0178d1d4333e60ce259c601e9131c4d86c3413c5a76aa5b9afb36694d5ac5c22bef1f9320fe2ca89075ee7ab605f26d499e13bd10a2dc863ee453245b14f91ee87183bd159667293dcf9a66f23bb82a0cb253300cabe507c445f5dc7b3822c551c36f9e8e3a539998c4005487082d4f57bfc4348242e9e1d576d7dc280cab50176314fa1d3d707a896d864050a185241645690499826c965bb62fc7323fd12af1f6202e34ccd44d836be93cf1656021c57b71db3f9987be3b0294284eadfa29755c02064de85c272063b720ac267fdc72143df5960098884634ef5f991aa674bbc092262fd850371727416539533e8e8c24138f1104c7d89d1aa5d2f114ad391daf342eed5d3f3e6e59d49038e78572421bfe1f77291b1e3f5043c1e9cd154522c1da14ce424a9ac20ee2b0d964d16bbeeab0c85c6f7415aac39e869800eafd19901431ebb8651947881e8b64270a9cee6f2558c4b1cea437cbc9703d7dc090f7f298c84107ddf3ece85740bf91360767da4a321a3553a3e3e062988f344d50d28980458f402833e9d73d1971bf879ecedb6dc7da85432bfbb30813944e594a9d8b1070935ba5860342dfd3974d9edf1e9e2c3974ad642972283fedfc430854fc49865fe62efb1eb02d9efcbf2c9a30c698a3ffc7dcd1e24c01a0168d719d6038a19909a2e38d60968e0dbfe8c8882bed66b59cda6b9376696e9a64c96ec13dc5fc10eecb45d6c1b38f3afd3b64f26e22d60eff0f57b09c1b593618a7a4be0ff30107256548a77061d556d271a0b8ba58ed055c76eed12f5a21e151bf4cae9a3cfaa84b39d76562e053aba7617548165d044158ba0b423f035523451f8266c4fa8f6f0715337b11af7f57ca253e7f6b163f9e287652e2b0cd8896dcecf4b69964f50d4295e13fc181b19423c89cbdf8d0bd36385624f0ea4098b039c1ced49282ddf20fb5cd6a85f0d1005030a340691c73dd2100c4d9a8700aae98d16b9388a64c6eedc926e013ffd40e2be52c652b38c526903954902f3d004d45d31cd27bc9e4a33d2abb5bf83f2459e43c596a0e4c365dbb35b6e7974c9b7017a1a9f48e192a706e8ad5d12a6d422a3e7b7c053a60946eae3c31d59ff3c0dc51a0606a7b99c04869f0fe9c1eec341238ba605e244678054226a1972a27f6222d9505c6ea3fa9b8578e06b71819c6f5ced8d06d91ce026cbfb4944cd02a83321951494857bdc1db7f3e4d4378ef98927c1a40ad95d411264534b7d3c78350a8a3e6905f834980ac5124d2d080b4fd5ecea30ec52ca65e177bb7283f89c929ba2a91f1c73121ce72460ec422e28102a99173b7caca3a2269453b7f7eb482a7750799459720d30004c40b33a394b6767ec35a83495da8604e4a2d2acaa81a1c27bd3f4eea7821d5a452443fa93064d10a3fe10ecc222bc50e6318a616495d7214da6df3b9b74bfb194c396ab5a48b6b4895eb52ce0e851ffae63e14932cbd7e1d1e6c8a61ce6db2181a19961d3158030b7fd0eb55e97bd85c441fc6fe8badb23d60f08a0ef9534e46409a59a19a859acd8bb395ea9578d6fb3ce97a536e0023e9a25168611fba276034c57bf81fb64639b0ce6c5febaea97f0c6bfbd202f457e9ab5af9f59125dee72805e38587b8eb67a3df5c3a5e8e3d75d5ea62beca0222d796509b9fe812dcd54457c68402cf3a255358ea9e23573c8edf7b4616ee146e42ba717299d504ca0ccf26ac56d7e239db9c00dd96e295735a6b0919b4ce6a9d3b89fe25d1e7e56e6b89bd5d55642ce11a85e9b8debf7bb782e969d604259bc1cbe3e24182a34432807660f951ea023c8f2925fd6ae9b00b80f1b4ad49a180e33e641e4100b3b2ff75b5643894225886362a91fcd3a9d5cbc1332991ccbd73f76847625da3f848f1c9d90cbe78248a83cd1bf280e63ae0811aec9b3dcf2aee6bc3b35ce0186d222807bb811ba6438f1ce3268e640dd540c9962b545da443cd48e8ac887e5ae4dbb9ae7ac22636ab7a94076f1a5cd145a310e6ef5a4df8788cfa4206954714517ae9554615cf840b229fb0167dcfb4ee877906b5f57dea253275c71d9d6d7a59bb182fd492fc3814c50b0b5addce4604e2c18d1968de82d5bca7c366aa66f050ef760c7f8ed8285890bdf9ca9c6de78fb26c41f74713e9a01ec9debf1bb42faac41b6dac762b66dd3c0c028e8e4009d7e91f16ca77e2452b045ba8fcd30328ddb2a38848b0fe3065be2271291967990621898fda99f73db0d59cc3da722a91330b841405fa827558d4cde5912a892b6d0170cfc50a4b917fff10d9f769dfaae263e144936f1fec1d6ffb88e72df1cdd019d4ef2cfba49c688541a9c67839d682e9241981e46602a23dce25b16ba883f4686a2fc8418b3021e047514e0e3d21b00408e34c327054187ffd5abcdaa350fd2ac3c7e31e8c059a84215a190f1b22c7a26f5de481f084ad3f8777ce016632d8827865d2c888646e5585e349f10cacc706d654540b90548e1a04bfcaa4b5318684ea0f5ea5c8f55789a42a2cb4f64654e460cdd91dd5ec9615912dde9fde34bb50df329c390305b8557e2d7f4ed311450f6d226c1dbbf2e16e38c3fb0bc9d4c517eaeb1c889522d798963c038df42fc6f77cddf8887721332b4a7735ed2fd2f2d1a9a50be27bba4af8898e16f4248a5892a43acbd16ea85f456faba1b7e659fde74d55c5592e7666f162ec2e8b6fd26b4348312348f446ba28f4b94060d303d403bdd31e371f55dbace651f4f7c34e7224efd263b50eee80cacf73cb5d69c13a0e0d9a98593167b9c9ac68a1bc48e3cc5c02d780c3ce0c9978dbe8f55904796b019cb958062717379ba3c5a761fdc3f27ee86d9dfdff0e8c3d24d84eac00af6dd2f74d048e9af496b0aa4a6876b29a7493069df08558e9414bb2231126d2aa73289b39a4579aeac0362b983a38ae2e98081161f6cfa0d1eab44419a6a878f11fe5e3ca5d4f3693f8411638c6aa66520f6e2e441a66070302dc6c39c653d0f391e72518f07f5798955ccb322daaa433742548c28c297ab40bfa28fe2b66ff74a79e6c98cd29cfd65744a0c57d7a32ba4d44df7ab60d8cdba3772d56d7ccab5d2b8da0f228c2e6d6e5a2171879e2e5001bfedc1dbdaae2e057f4e1a0ae3024dc5309958f58178914299baadf7e06fa7fbeffbca11de7718a53b8c3bde4da4f6dd4fb8f9315e94e4dea31471e4cc5f312323a44a0e9bd375b7657ae4683dd936925003c8130aa65a0e5747e06f7cb1a82f92634166d873e692c5dde791b91d603e961dc3f80164234d27de92ad1576f05a971b1bbd51224c492f240ac882b34101a468179827c2023881457334d191883886cc6e44caaa7a5e9ae014a09bd45a40ede122442588939d7555e1c4e32c6b5b7ac4a386440ea4d2395b1a4144f88bd7d59fe9d18ccb44a97eaa81a8bb2df7230cd888d97147cbaadaceed15bc04be58fa84361e3e9b807b6597c700ff99a58f5ddc6a016f3df8f8caf0b551d64ec993cb86d515970a511b22520e4be725d16cb747416abfe4a0675b368569db78a3f412ba3aaf098a5367543ccd335a71e0618636f6d910dc7d0e14c72e9178fb93c63b24a88e978c086baca9e80002b0191b5fe763e6c597b9526a4904ed80bc658d5b7dec2f28a777be78f1fef8d6f291c287cf78c18702776ffa6f8ad52a389fd085f6d3d591e8c1a5464e6982cd881c1d002956a9f8dcf655515d9f80021f16eba98432cf0d57fae331b42f006e07b4680bfc572778f26c02e76813df5c8a0e9f6e6d5948d7a0525b6d63f7e99a964c46195fc0480bc5415d6d0bb8f4930b69269c4070261d97b8cfa74775dac36d006211e134b44c5e8eb464830922315339593e31b2f6a8dcbdf92c3ec6999ee91fc2169324667ca72a7ef1fc59ed238a89945a0c35a6ef0077787c783fa2839c71e698bdff419cd05a65388138fdb6afe3b9ce39a9748957053b2c93ee487976ba3c84813a08a4f227d01db6400198599c5ef6a20080b255a0dfa993f852a1791029aa6aee9a6ddf36e4de87920ca457bc6e1433a2f0c8fca39d12fa16d690a2c0785158ffe80ac9321764e621a3f3f91f658119818110b8e3d08d45d75793da55c721d4e603d5ba3be6d2a3f2853f0d9ba282bf1c9ea9dc9c2c46350d4c9145c43a6fe1d75184b15a782bfab590c155f34f87a4b4968da0386f9d3e132d28f9411bf087764b9e858d79e11a0d582ba1763d88d93aa6e08dfd6d1bf43aee2a8f7026a1bd47a6f192bb09616a4cdad9224eb2d0b95459618fc8880369299522ea84c93b9ce9d782ffca24a609a34b6ac368926d0198dd820367eef3c266a07123c08764ba1060cbb016e7264fe1579331909247630480eb8b6e8e1753e6e72c73d6646dc6fde6dcbb1063a588c9e45356f338a00aa12513829095c833ad5eac9a30ead198537e20cfdcff4a8d854a95540d8f358aca745da52c7d7a5307d8a5c385b08e5729fe966e08c5f185b03dfa36074c69b01f909381b24047f818893aa3f2c7726e04e93ba15de26c222fcf4d7e36f428200373697ec12e60808da3c34c5e800c140011aca0140605832a805e4fc750f3cfd0006e801403e885aac7c43a8035ad0074e74158523a56b5c79b352579b30af2454034aa5a4565accb9c6846277dbe79e10387b04183e9334df460a544737c6afd51e6f69d55f947243cbfbe54cd6a1992aa4cb2019e9de65ad0d6d6a3aca30e9d17b64e789c728e333e5ea9af06c1bec91928f1e2b55c93f2ca0be70226cc96499109afd10e0016e75555b21b55564f05dc9b6f3b7f70078ca5f60eab8f81093abe01c561ba724387e13f2e6633b0e130b89604ab996f951dacbb62c32cbd39c745546122517a17517d0bc9db71ca5276b664acf62eeb7e9b8c6e579dff9794572ef335a32268c3835ca5d733bb4d039d0257d259c91a45b8fdbd30a4f05d3242c792ac5c540ac9dc1e55a6e1b16caf5839ca246bd8c7ec6a36cc597d2b07382b22feabbcb729d47fb3000c2ec9eebaccc86f00694a28d4674f94ee8fea2387a442ec5f133872ad05f58aa058ab47473a23ba14152ddca028a9d7d422f509135c11e1e1f024c9e309f0b879168e7e3e4566b404939631775c12053402afe5dae5ef9c145c7888d9589a5b832d191f9c903f1e788935a78b3db231b61b9cc77163950f18de70ce430c1d52b2265ff304b9c344b00f1e1e4a6971be8a7147b0353b3d906795921a7b96dc6a6bdb7ecb248f341a7e8383a00ea9e55fd129bae9c043553abc7804fd37f11da45359762a0227cf5f6d5a472b46aaafe299c1bd7b4316f0138b99b46a5d6627dcec9559db5c863ef0dd122f2f161c20c343ec42ab761d96e08eee462d082576acb4a629b1e34c2ee250eb9059dc3e5952ff9825f53a737cacb65eb3ff7a6c4af2289ebe4327679ed145bbdc1fc0bcd2bc50a459839ba1c0f846182684ef355ff2a231fac6d58ff3b7d1954e5e76c5d95281480c1d698f8de5119b120f05dd592b32c47640dcf8dc16ae587c6d7066f1acd830f12b9f053789e23d9e35bca066be77cab46ab767818d33fb5ccdf0da4fcb0aa6323f6778f2d2c130cc14228d38988fd1d2622749d6391c92c5e635ccc026882fee75f7e8029495e7bee9d702895f8d01d78ed41735d7a26ffcc1341f2f8252218099370f7fe5008b647757477a332560c26444f3d8e86a7651446d10f80a73d8b3ecbb8c59e09d9ded62c3fd2cebd45f2465d3026002e7b3f0da52527b8d701837b63f44499f4bde2184c0b50349d9f969454f122a8f1faa82ac8e9463c4c388b853952efe74422dc2322a4123b04f7c1981eecea0e7634a3049255c819f8c37c67843b1c5aa621e12c9a795b8d2772702c6861c8867f62b3bd7943f1616b654ad8c755ee2a6fe71bb9c0958016392f9dabffc41a57ba9063c0cb2287470c4807aa751ac2efcb4f73287a6a435a6f5c2962eeb75634265ed64960f4d55364950fdc4c896179c1feb7588236f51684b4e45ec26ea84285a2cbe3682fa2acfb13995d5c457442c2cb629fd35137d6fe25585a65e475a63dbe9e49e102ca38fbf066d921f55afccf8c017965a0afe59061103601411cb249e7316d0fa126400e6c8201b81b4c9275f96b27a580dc3b28e5cfca28f81986118d57d0c054734158361dac72425612831a6072a9e604cb5bdc051c520d294740679bbd15a6930a3d9efa02049663c8338ad743a4b1b16d051283ef9ab22d5c9456a0bf608491a4a37d35d461309406cc324ef5b9e9816a4cf0d0722dfb44a5d965996447c59f275d943672145a9ed5a224a2369b0b19a555cc8f9546b50f0bc54cd7edff8aff96caf052705526142d7c168b64e385cc9639021d6863c9c344957c33304062c5a6b3944ead6096ba019a4533c96d9cda99a0992c49797ef373d97a09b43b0f5af0aa165a1ade6dbb4254b0f986eb606db5828dc6fbbed0670f0947466bd3373c0406d58cd8030cb29950743b0603084e3dc82de3c6275befea09063b4379cc68497174f0b4d63bc8de6b0a650389761ee28a1ac6658ccebbcbe33695100203cef747afdb5636fecbf09c6465c3efc0bf71b7b64d8e214220ad21b1630d7c4884870a25b4adb24434a50aedc8e012feacc4d2b122ab3163ddf44a4f6d8e742db741aa879fe36d2f302dae923d600cb82917fbbf1e42b4ee1803688d72cbec56628366b7b8d548c2e42800f956696824af215a287be10260a82b605ee241e164c760e5c713266d05a3b3a145d38c9c7eb7466a2e191dc003669509bd110ef1a586a6aa26c4b40a0f3602f84ecb360d215a74483eec44f3cb43936d13298b6f00fa9df7809e94350a525ef1d1018bfa094737ae13cf474a0dad5ca903903391bdcc2539376d5351f46bbaf4def712ddda897a60a201293329c35ed95227e7d0ed758d1ce4675d091a1fa9b10b2fc7f9bcf11830733bc11f159109a0ab42a687959f05fbdd403d277f14de9c3c1bdb862a1e10555dc3a44718451fba468aac3fffd5a0532d0d13431848d46bd97aa184abd5f5cb5aa51ede27b0cad16110cae6c3a5272da2d73628c84845de06c11a8b4d696f94aa90f7c17bc7b1c65e1e1c9660d2ab05fdd2b87ddc2574ee1cf0a50935d1824fc06f689d53293a532e4e06d94ec2ba034e6f4a4065f693c19650b4d0f53a8ee303b634d4acc1610e86fd6b644b54593a1fd8befbc682d6b41eadc017885b199cfa447240b1c9b2c4080f1a8040d31d002b5c54d41e3dcb50c397a070f495dddfc3e11d17d292e85c5d24341a209a8cefe4f5073a045c7e6bee62e50ee57de597cc15ee59c1f15700ca672de03c88b6fbc680f6baaaf5aa10c51675913b48b4e5e6f963bb39fa7af33f7b1383cae97467077d6e7c6bc52e1eab0ee43316615ac4c7c87a0ae61cebac20c9c8af501f169c712a00edd16e5d628574bee056c394e77d43c89819195789b4c050a890148c5acc4ccff6f942a7fadb3bfb0c6751883173cb8e6113ae4448085ad6e4151f38d294a00d857b59d0fdb1ca06c420226d85a489eae68f9c90362d29bfcda784725a0c31e0b032fd77cfef4f1c2df19f5e89b90cd86ada1c7c94ef66ec97bb0a8343dce6bbed489738cf0b103300d57533392f52d609d48e2dfb24dc74feab723082415528e9eb9e689f860039b8c0beb068561c20f0fdf886303f77e1f7606952531ba8b2a34a9cdf29a21b040ce0d53d565ae69e5515fbf95a684a3e229a3db52ef1232dafd0301679790ee07b2953b8fe300015a470016e374b9278242f39cb18c495f6572bd3b73924e1f60ff03a3c3217f68bad4a15e17400032f03843736baa8d3f09fe19cf1c3ff5cd77cc739abb1d608a63fef7145411e6624c894d829a4dad2d10c58c5ae1ccc3fadaf376943682700cccb576120a4b85fe049b04a719d6dfae9fba036db4f82a7d8b62dc63a5a639d84a643d0394cd5b08bac01a5e0c1f4300b2ce2bb8548dcdce7b6daa4aeb37cfec8c09389293e386d91eb2668a54e16c1d7dc7c1b1809e2c0cc8b2e9d0f1f7bea4fc49b4082a426e8ad75a7c9bfa8434455556cdb5a71b3c0773cf89cefb5910aa73003888bf258437d9d4b38279499d7e066395a37f7526c4cd8bc5b78201228fbc40b854ec52f9e26791016363639e1ca8188e3685657ce6da55e0de70b2303bffbaa313230b4193b98bd8943d56f887a58e6bb59bca997783ac30f39035f0108c7e8eac66c6853d06b5f506b5a17490cc091060bbbe7f2225083a0b4ff0fc88be91bcc8bb1413339b4aa46a1596455fbc5888c98fa982e22b581c564269824555258f95d12806ad1ec1d69558ba2e58c4f552afe4440f1cee1313f3e1bbcc602f6d0501699294a7a97f5e4842091cf5343d0be14dc609a61f357ce4f656751057301ae899f10ca301d38f32a2143280269874e9e837f3d51a3646d83570774b7001ce8e1a714bec50ed97b2a887399ac4f8507170a2acbad935b56cc5104d507810e905bcf99fc08270d29bc8ac7f9b7201d77efc21c6945525ad9a5de007f5972c9afa83a12a013e4040550eaea15c628e94f8124c8f75004ca411845b42d60e40e968fd044d96bb658aa2d2d5905d75669d244f78787d5b14a2ed93f8ca9c60038c0ed6d7e230c0905e5afba66b0057dfa2f1bd72a0b1f5a7a72bd8c53a9acc6494fecf02c5c29193d9dff781a54fa174cf4a13afe8a99141e9a74061b019e2554375717dbd39125ffba1921c939cfceb7d8f13cc78d92ed84f32469da601f15ea83f5ef1f965816386948f266bc67cd73f1f818a3bf504c17596be81819b494e85d8a56cef71069cbd4a2c5f03d404a5df76253b20948a11c014093fc30bf3d6c7e243ba048ba8d1e692e595517109a5fc365be5666aec36f09faa915187558d784b56147bdb0e2d1c9f12f49eb57878d064de61304b4a7184da8d0fc13fc53f1ecb205c1acd35e9faa57026602fe8195c4ff991b62002b06f9110218d0b98abdd14a58d0183eeea041902066fbd2ff1aeaae7ad2b98301f05e4daed2a32e3387054983a669a3c98e39c802647227634f5f949451b7da599925efa851b5555cd09f083daaf5eed4fcc2431b78de1d6955e1bde2622d24766feb788f3478e82996dd851160beb214b4b24a1051b3dfe817588da275121286662fee488cd3e27d20273411d097983333c64af05fee29e61ac59750c0a9cd50e2be0be8e5eab966ec67a4d279e8b522d541e2ef4918801fc6279fe1d00a573e7a985624829486f383ef16dcf7ff191119f8c7b5b0adc42b832890df019fc5e180e24b386290e94bc5633a49d9611bf2960ea0245ff64eb6bc5c24cf2557dbfb7b7bad3689d8b1ee236a59a0b4b9a867f263d1226b6210cfacf662695aa2f2cc5b4130acd0efc1399295ae1c52b2684d107695baeeb94463fdb442aeb4f67d2fa533bca4a957e25d0cc8fc4884471ddfdd4dfa4fa1691b21aab68e67c825d0a044a51207da8a329c95f77146bd5d7e010148c1a509c158851bc73b1bda92696e6a032c766ea33e0f0c6b9825ab3012fa54395b2cebb684cee2d305eb7bae1c9865d1e6d50257adf1986d074479e9653ca99b3beca8e4e13eba39a12bebce2642e96d97d6b42636de2a4bf006c627f3fde03e4fc4e9a8cbfeeac14ecdd4eb7dadbdfc6404f031b2f247f75667eecaac9da189aba8c3ab2929908c5635e88593a8bf01d0ebaa5bb2676d4c1e359f0b6a2a119327c39ae2e7030499625c446e312787f3808075c254b18b0c79ce95925e7ee8c395db2093975d1c985c48cab7bf43871736bba6783d52e12c8b2fb3739347fed73d74709ee11d18cf909a29eb7fb7fa33a81b64fb2f0201fe4c73215c262cca24e05d6026bae3a3ddb74536e6316d78d080baea1d0dc6c6578d8bdc9a477058f5258380bcad25163aea436917a5c6aa25d6563ed87b2b2246b20a7a83b484e43cadcadc037f69fc555b1571588fbe9799a43d2ff3a27be6621dd9a9c36be28ba4535ae26033f01caaf699a669541047f5b800d3f466c6894dc0a30ff214cb269079b795db8e10d9f6400ed09f58baca8e133e9597596f2b63358b03517c5731fcdf4847fac2b81bae52702dc0cd528cb952cd27543366979a728708f221e9aa29890815018ed7c54ca30c86b58ceafd4271f90c55960c64860ca6f32ea8b54e3c668955228f0d351eb899a388e69100a9c594e4c3b304a38f2c915e16d8a69c1b8820bb8d010df28c6b454288da0e00951354ab75d896769a41c6df4390918472c8f15207a8b8f34419d03161a7f339ec7d8ad5620806eae40d17ece0deee4ae0560de0ad9af58f222999a3c2394434c6bbf8437fabae965434589b04d9f4fa303533c7218b2c5906f79b63957f5334f635157472d5a3c5e866966e52ea0e7461a034693589a1121e486354c59d06d435e2bf3cca32a3054ee435f800a7bc5f91680e5099f911be875224b9b4d3ac5b580bc7b5a00e2d5c77c6a3d256e9905702d7b7535b0d0f50af053f478ab6e062262ad80221cfa8c976eebdc80fb0174c4be0efc22fffe2a8002e47592f854404d50f389bcbf4a275b34269fb1a7ae191b68d59fa2d6b0067b48e27653af8cd51140a93af60d2dbc6f7c681e02a183677246b338b4dd16e31e12f431e34ddb229debbb28401336ea4ba56576c0def1143375f9d8ac2c81d4da3f157c2c67c1cbf5b59c15b17c5971a381e0cf2bcbdd72d0211a55d7ffe4e5b5cfde58330f97099b7851c897c3d241a2f0b4a7d68335d0b960cc8659592ff36404ec68e8a335aa1f648baacdfc799a81291713e025b7ad708b40e192ab6d12fdc7da4bfd17c5b707f8f67e352280995a332a6a8a1908d952f2c6e5394f2c48c9d4139f8b43d174f1ea331055ef6e8abfce304d26a0849ddd782cc69bbae8fb75c8a677cf06bc33ab4881ab7e101bbd198525112f796e070d9e453c645509bf2e5f44b385a471f79150fdb08007801c7e71ef66573694173b4f8e6a8a4f1670059a868566f2a2aca415e4b5d47751d89434593a5ca6e46ca02ba80934ff63d1ebc089dd4c5c235861b95ff3266622e986621d92f86211ad1bcac0b492757037db44c7eddb4fd2dbc56e98cd2c8263b042b7c7f89d73166912ced14431dde74333e03a716bbdc77b7dcc19ea1165ebed23e609f52548589176cab224ed31853a73a6b59299259612735f4c2593a9e02b2e32edbd5da1997ff10fd5b853d2f0c8dea91e91dd45c46af85358273e064ab53ad1ec761d42388fb2f86f0d22ec3530b70993839af385c1d253303ddd55cf4f6ee8b32b923b91b11129bbc8db88dc9d486c047146dc4b418f2d91ed8bc4dec86c4462532477223862b440dfbe48ec8dcc4624364572278223c601f4ed8bc4dec86c44625390274610376c896c5f24f6463862342b84e9141c0e21ee49ec7d94c594048c277381af9d3347db3d0b9d3bdfe2dcb54f9e0e12dc5cc283febfb1833960077a400773400e28a1af833b30077a600e74000bfa76b007763007e74006fa3ab88372b00719a0d7813978074edde8400e7a568258d64c5e9ba778dca03313a4dfb379eb616c8325a50edb0583846c2f87d25747cc023643889c08cf0e8c57c0750fc621e4b9c3e088f1f621f80630ce7ee77c93c5f160c08a9e796ec9a910a37ec9e1ddba9037c3dbdc91d9083f41c4aa9fcf5576633efbfd1098363215e12340530e4b61e0ad9cf543047d37a95e3ee4e9b640f6100752e34c669d43a5b0fb5552f99ef040882e980df59bac7809ff17f3f2b20a4000c2018a640dfa958e845cded8c5484ea4f3021aee11655c304ec29a0ebb16ff5f56e63fc39299a387f274685b5747e9998670c5a3e276d1fab7b438334b7c4703219e76fa2972d77d0210200ee70f898e8ccda6e20660d00fe4e1921dac7c74bddad86b88785eaa4f5eba91ddd6e49652262953210632061d06fdb5824e443a4be0fe19c5d3b7ef6f41f9f641e8e7427026d64c5915aa75babbbbbbbb573add4be0bedf8225acfeda97ef00eefbadf4e0be4359fd0ec225d897a1acd250c7095c448f6ab05539c1734fdf8932eb57e1f8df77efeab93036c56fc4ab9a1b08f797d5ad56ab5508649432b34020abefdcc9955c8140ee4b5b1d774199989ab1c0c8d9144539251debd397b4863fa7fc7ffa1e5271aab1d17777f76a694c61cfe397a9f59772eff7b9cffc09e23e3502edf341134df588b56654da6c4b3a09030ca6ef9f05729f2ba1f55d09567ab4e6dbb00f07129a370cfe04b542d990f7b21edd8f9fc8854084d451def139e867fd0565b3c0b148e67c3c608a9e573be8fbbe1e677486c0fde3e598743df33b438771963b1d7d45f002350359bdf70d5ea06618abd56370c5fa156bf5304020dd7b1f7b0cc6bef3c0f1b3f7de77fbc87ec4d9c30165600cb09b56d37ff32f4020fd37df7dffe23be9dd84ae1f85d4fc8563ff05647ef117dc802f2e009bc78bf1337e11874d387e1f2c1c31ae09c79bb1ab7b0c76ff02473a0aa9b9c18ed5a2114cbd876f5e14a664528434abe0227f162893c910842e6ca7c958a1e8404e9b5ebde16adff5458256a1acb6be7b1908adefbe15cab81f321bca3e60b158cf0a89b4c22061b5d5864fb97a4340402ec1b1a546cb932b8f8c7bef57cf02651f78bf7a191782e085b2fbdebf0c84ee59df85b21bbee779def845825814e0c9351c87b20d45e0c9228e2c6528abe1883f4976b32002e8c071939249344cc2b7055644206bb596569f866ec36e77177ba7c7eaf5ed7b7d20d6ddbd3a457266caab4ba8211f3f3f4046902ebd0dc3a55488236817da210a09edb00109dd7bafd00e1bd00e2120a1eeee242f58c1e433fac83cfaf8740d8a2874ce393fdb6f5b5c619e206990d2d5246a02b289071ca8a5943ba6d8dddddd504050edfdeeeebedd7d47af88fb5a6badd5defbddffa5e0829ce5ac550117128b7c09b0c0e8a0f9a0061410e13142aa68020cf8cc10170ca25104a40481254788eeeef6ee6e77f791b63de2414a51d7334969eca3b4f114297d9cd94341b109cb6fe2ab8f4c04b68d93674875825841fbd0fd72c9a5c2fe4da5ec6944e2f81eb07c9d234ab822822daa18d992e52badb51e81fb2796fecce04b7590c04d44e50ac745342cd16cafce83bd78f1e2851be39aae69ef1d9c9828048e6151083c5251082c25acc5a3c39faec50ee7474a29a5a45f8cf360bc3e0ca326064c668363e3c168b61737341b0d990c47c60c0c30902163c68c7f100c431a22558d1a39399e939393e3c178f93166c81e07031a349c068d49e31f04431a3466b31a357268ccc290068d598d1a39393a3a62ac06cf94ed60071383355a1dcc3047cea494f646bd7183deb871c3a7bc71e346e3d0abd1ea60e6e8481d9d594e0d9d1c9196a3a3238a349a0d1b376ee010451acd868d1b3770e0d8d901000032c8c0c6ce8d9d1c36b4ec0020c7cb86961c4a3aa9a79c12f468bf77777bd7eec7eedf7d710b4dd1774cd11fc70a1e3f1ee9ffe1cc1ed95234894e7217934b9dc0020c467460a2a10586063d1c85fb21e04df40d86c0f3e3470837a21f1863fc23841b51083f887ecc9e66ba7ed16c9266833de549e94b29a5940f8a2066e9af19ad5556f938536caf75d2ee0027cb6e3a83e8054b3af1121eb10d2db80d8b378c2de30177e8ddddddddedfdb53bc9086c69cea8702b8e4dd1bbbdc391e6af1d6856b025164f54ae3ced18b66fc3d8873f8c71ff4432d583ce2f46ef14bf89c7eaa4a1d71d5ef095058fd78176080109f51fe0bbbbdb027173ad82fdbf3c5ffe6cf759ff87aac5b556efea6e6b777717b100a54badb6fa6be97d3a1660a48c94785a2109c7ca51982954981005942da630924221469a16858476d88084eebd5768870d6887107520a109f401fa839254ea26f5bb25d0cc81c2f5c11607388f3064d22a26e48a850b172cb9b61034e2c8d589cb0f4ea79c5f99fc487daad5567f0050d299a59a2c49b43815b8a5726729a2514a89347be495575e79a54492455622cd1f21c8a22237534d801483852468985c292b30c6104043dc0619e22e648a1e44c810ea4186b8d00e3342660f111ff22165c449c6f18f05588468e244c9e8367ddec250d5b085094f5c61540413275390202751c28aaf50e555070f0108103bdcd356c01308eac77d866a4b280c76fdaba97e701f1e09711f36c141dca73ef7370c57c04342ee3384b6824c917bcc0496794afbf87bd8c7a1ec3efe0eea515f76c32198100c0ce234ee6bc0214ee38aa4f0d001a6c87d60dff53d2a0f29f82f781d3b8d8b31041e22648a9c58059ebfe2be1f7c47f17efb0469eeeffde9da7c8ebb1cc75323c54dd6130732cf8ff104cbef07f70659f68c2c7bf68c6f83194d93dfed44963f6e90e543a06bfd72ce88cdc05e249554d22cc0fc955d8c4081a4592bc953840450595258e2141585094e06314078d4bedd71cbf6df4f2055d443cfca4a1d5c8b07f582c06d63e20825d6978a3411c116685c50c10e25d8d2c59115a44a290dc2be8ea420fbd65a2bf327f34783a0ec7ff6dfbe689f67f6d8d79124c8fed0d753467145e9b88c6b92ba86c30f60afc918f88d20bb502186d35698b1691c6545172e58129b1a28a46c9ce03045302baf9798a41a2d2f179924ece5a635e6e97585181b6f0c92eb0a2c621d99aa169217ac32549e139b555177a575a1686295c0c53de1b40a22669f90ba58dc62950c1487c5153774cccd6691e4290b279803314f59cce0e220cc2d0b2d2f0eca30cd4f8baadb6a6200c40565badc682153042c0c8ee58e2223c6460c66e58e2293854d127615d92029b3248fdf4213581afcca1d55e626aa4c962db8953baa0c0f0aa89a8a82428a4c73475521ddba9e8db1a24cdf8adb99a63af3823d562b77ad0e5ab9d2f7419f3235149906711afd5995499054363953d75699be3fe9da741ff9f46d98b04c9152ea32b468ebc73623ab4cd1690c6ef916d7d377a6d9e33d7d9761f6744fdf9bbc8a3bcd9efb3d4fb3474bd7ea535738360a32fdd17768f16875156cbd7eb4effa1638b36c79e0fa5728bb9e025ca1ccf532bf7e456bec6b8b070e31c699227dd710497cc93574261a055faf08ecdffa7e31e378e56e555776a74ce9b77e74261ead90678a94e7a70309000a4db17b504aa933fda44f6301fb8f35565455574c30fd4b4bc29d3fcf7fa069c6044dbbbd776ae5b8afbbbb1ba445b84fee971183ec496a31c1239d03c8fd382dd474e47efdead6802f9c3bf157980aa006c07a8f712d83dc995ad0677e2981c75bd37a8141b599f362d430c197e72747eeb7a184c7bb0280545203058f94e5e53081479abd761f2bf2478c6d1d4ffd70bc8bb10de74e7a5caea037c58f61d3e261a5add4e7bd1c05a5b042efc3f9cea0eb1b3c53a2d4437d7865bb562b48ddab38e63e533c52e2aa77e44d2c765b3cb42617cd4c0c255e228803f67478300d3fcbdd49fc55eb2b4903eaa1ee9dc9301ca915305a3cec8d95b1b7bfc40c2578a417cca07219b99141a3083cde4f26050c9c1832590ca88f0c1e29ce0e18d8644421c3a7d837b9df7332e382aab0cbbfb85d2df078410f074beb1ea59e5d795ac71ec655826ebf135b3cac8ecc553695df8fc1340e0ab3160ba60f8e4d1c1f1df933028f14e7e878f7a382c72bff66670e285dcf3ab1084cbf1371665e3027a5a6f8611fc0fdd03a806bb464fb355a66cfd8531c4880295aeac376dc6cc602671df8ac27016709fee1535deb2ed9be8faedd6cbf015df3b74fe43ef3edca5acb711c178eadef3c90678af65b208e29da6795c1dedb560b87177ae0f8dc772f5b1c70bffa9ee20a01afdcadc04f47efd8e74057feecdf6bad7d4772c3b1afa36b9ded2c5b1cd9e61ebd23e3de87ec09afc2074cd1fe184c6301d35f5dd0b3f6db3e6641d9a8776df5db3b14e72a0a961a884695024d10b7f80929e92a2ad0b515641acefe7d483ab26285d3f7c3f37d3c53098f7b0f1ebe20e87a36f23c0e79a9f07c3aa77fbb3f9593674ad7b38a9bd601ce230ca154babbbbbbbbbbb432a89dba9e59e1218562a77d614c8c925302f3bb0c756de9b2a5ec9772ce3927889faca0136554a5db8a9c53b44148760bd97010217f0b22640811a21fb5217fbb8eff830c2142747bbf17e31ebfdfe50bf962379c4f804965180c6a8a22ccd6b6de99b2e9367b7e721709eaefef88b0586191a0fa78093ce4b409b3c16cf3615d60505316c9a4ee596fbb26fba07bd63ba09b72c02abc321d30172ac1fd30295a465e1d2646a925cb975a88aa508ff68d52e98214d18fa62c5f8420529a90252d925d56c93286ec923b8f5f500c293c431cea2cd144670199e5fb038846e9344a2c988a87bd45a069d95b8bae24049dc25286919b88148a322bf77ff77f5283190ac5e89a0c0bd03bf6795a5ec7fab1c922a7d9ffdcc7150a39cd7eeb5e0fe3eefb58ff571ce2b41b6834493fa3f0060dae4fbfefd6d0ea8126d4945c41c1ce4202cf4f9ab3ae309f070df6cfc0044c9f47eda7997e064630a5e32a8ff4b9ef2194722b37114260c9ddf2401ee5910b6f08444a06e4b72d5b2ab03cca9d474b8403948a0b087808b2325fe60107448a0656dfb68c6465840df23c6265840d3210db6abe4c9a513aa2ae5dcae3fa86c2fff2f6f68f492a257db2085751f1c4f4118a406b2082cc31bf9fb4585f14e244318a12f289132f8da04ee06286d0627d21f7f92102c7fd388d389026fbff943724ea304562c0e050d3eabfc01b135075f2409ece9b567f05e2cc3394bdd65b6fbd40da0c375758b80f51011257a2a882c31332222042cc8c0823b81fbe1c79713e1cc9c2b91fbc24558ec3b1af05ab1bcf8d2ca7e538cebeb801b77247a191420ab6b9a3d08c3902051f65e74cfc8163715c149a1d32c771ff22cc72fff3fc4c44e38446863c3e8f1da29b5f6df26787328ae3be738e9364acb5b68694caa4200fffae88a554a1ff8c42e6596daca632583ea534744a8be6135dcf469c1a27a755fa3defed6468a1f4468937457a832443870b769e7386183c7a33c8e011c674a364d2ea0d52538e655bd435efe5df7a1b6bb8186529eb012332e5ea6b56f87bd83cfed5e390889555d823167b22f86dbe48100f1d2e78ac5531d0a65275d03df54e7d1ca8dea9d289dcfab1860b158c0916cae6c32c58e3c424d7af374bb93ae92083bd06a96b36d79aa45c6b9472fdee032b8df550cd77ee811f68d68432fb355f240887321b92d0037f4d4864011d84a798eb4fd60dd2cdd20d53aedd7c85ed3457f8f3b312aa2ba895b52d1eb1bc92b4fa2216fc7afc63dfa4b81e68d6204eabb5e82629bc5132c5fa3a69f008638255aec563b5d2b161a8fa2d50c715ab1f6914ac5bd7bcaf6766fdcafa3ac6bceae9b80253a9ec81b4de604ceef37a18d38fb8b6c2f1aeeabf403a69ab67812eb0274d226120068f37483748374a4db3e78657c94dd2ecb15fff6669f67061ff0dd3ecc191aa0983041212f6fda8bba2fa5f13665d628a53764f9d0e5deadb6807667641284004cf9cdf7b57359403637605c2a8f1dbbf9a61eaad6bf567d5a2aea07ba7bb7148d729a5f3522a52496938524a29edd0ddddddb1b73bab899fe2ccd205319c5972b3846bbab0e9016315c09e60ccc4054bb8fbf28209e6bcbca0c285963c3e0e5c4c31c245930b60e022c9962dd26cd9a28a980ff82677d416535b486971ab3648d60adf4c692ce00a5e51fc3e2e3bcdd64cb3e79959d445f9129499b614d762f7fddd06f27d7929d735ef1bc759ef85dd3b9dfc428a2d9d5f087027e71173d23bc5216c135de31f5d9896701043a5c2132e4a0f5b64b036179896cb67f852c52a8ceb451caddc512de0825bc1a50c296d496f76eece72f066775f557cafbbc597e33e6a67ef5e79c4eade94bae72e4a9cdd59eee0e790bae751ea20cd4e5f18507173c0d29dfa4ade1c28bd96bbfd37b42f7bc7d25aa975f73b7bbabb67c8a6f329a554babbd329d650e93a968d0b04966f03064e70e6e41a34da0beb74e22c6ec9e596d80021fbcb6a5917a81503f93b8f95ebac74d2d0655dafda985a6b2d95d2056bad4d656a09e1b4721cf7cda8322a0843c6f7618023e39190d4e0601c03862cf4f9cc20914c303eac21536b0499ef977bc18b9b9607b7cebceeda4dcb0015c8dc537084e10b5e70356ccca6468e0d2ca77560353aa2012000be284532350d0a1559ab5757ebd5d1a8ab65a3d5f2c06d301dba9ec9e994e59c73ab6e258b6451d1e5ecb5dff7896828c1f281265157dae2519dfa886f77ada5cbba5ef5258b50dc2dfd65ac7d2e6e9fffafbf1bba6d4793edbd893cf8dc361e6575abcb627acd303fe89a9414cd9163d6dc2f5f4a4a2a478e1c510ac831cbf1350af949f9573e6e22cb8f19790b52801dc9ad674f1731c3480b8cc8624c76e95d6bcfc314f7347382db95b2a86bb3264f100da6b42fbdad402a734f47e81d3f40ffe859ce0b86e5f682f1cc0bb6c16648903e4292c6d1f79ef53d44e6be00dcfd92b907adc0417e84e76a58c2ebeb73df289d32c75dc994b9fa25b8defe0d5f60307d1268c6a06d05e9af63f6c88290376401c58b0a98e0808511b7650b4a299d35c44fe029c28f189d21e4516a8291b28bd8d00416252598a0c1862a9c16333539299522a529f4d0201a50e6c9920782f8018a114e0730486ac794e757af95d65a6bad7506db122c52bc888952c491400814bce0f1dedb359f1d1310beb425a97aadb4d65a71fdaffebb8f90322cf0d242540c532a04d516eaeba8b5d62a1681e737a5b3870d8e4441454c982853a0802a19f9000af385a6468c388cb32dc1379329689c48d1720407ea42ecdb028f78ce12d0a3aa15edd50d27a5ee5c3e50624c135694f8210b961c8e52d04a236563b13226092d43144181807b3a9a42626138c20914942788d8a18923598e8058d23f9c50454a133f68591286c301012778628317148e2091e486231eb83be77416b220426904383c8581da01cbd10d4be45c6240942566682285872e5f381b8e4290445671d40489cb3997649723252a6028466792a6df8c6e2cc870b4830a421871c28dab41894ca5f0fcce34538a5120657b80871b94a8f041062348dc09a32290ea527f99e1a6e4d445143180e04a16166a027ae8e19682d118295c7032bad277ce2065b75aa8baf2a54b1157aaa081ca11cbd30dc4962aca18d18509a6aa66304281f55f1778fc66de0563498d8ce6910ec250e4921d8eb8f03486872c56ae50a11ac10023b468e14a125888c181ab4266059f2f340c283b48e164459507ce70549448a1bec0802f634830834d8b12482300c37d56d8c42224a5b4851e8c985088b203143e4cd972730cf7ce5799e1407907ee7dfa22a54beec4194b87a98085165a349159418ba629763ebd8d93a04b2a5818fc87521b2ab08862429417262704514e4aa6f43b8347ecbee5de49291da294524aa3d29cc994524abf6978e752f7edeed99d64b9bbea56dd2ead948dcd64297f1505ee977fbb1b8fc9dd8d6f539449a4d0f0ed0fa4c672dc722481479adb7d46bbbb1be3e9454a0fa9aaaf80664a29a519cf26f8b6cc54724ed91fc6153cdf45e09ce4b79cc0d3bbd46ddd9d3dddeeb676944a5a43e7503954d56784c0e51c533b68469908985f9fcaa0f92fd95200ce40a8ffcf105f196f07b0ec3650b3a7f5f2ab4d4ae9c94eb2e44a5ec949a9775bd790ba465f862d90e61691dc7ad9aa505db3f58bff142e2cb40f27e5b4d9b5a4ae512ecd13577585333385a37ac2dd9ae4f1662e1cb9a9f15e2dbe1061a748812ea5de99df5069b854e560e60ad5949b3f99921ebeaa2c08cb9c94fbb89e4c4db95da1cac14c767181caf3bb241aba6aad5277a995c3f8ad2cdd1ad029491042b39422190832a8be0caa219217541f3bad5fe62fb3cc8357f67f65291aa8e1759acb2eec77924ed124683ae04a253598a14d9239a4e89a0c6d077ac77ad666ed5b9b8dc2daaccddafef59eccf274ca69f6efc55315a93a55a47aa55255a48a54912a5245c2e12bac5495aaab542bfbce7d46707d5a5555659f4a75cd7bfbd43e25824eb94f4d6893feb6c211076d8add6b8331ecfba6b2f542fbd43bf66fd2b892d3ec5ffcbd9243b952b6b2869aba5199e91a0c2ce108f6364fc42694815053820d11d8dbccd610ac4850b7780c5991ddce5e7daa4ff69f9ee613959ca23253e5458ee44953724a4e0d994833692a4da6d9349d6cc75a5d4e3675956c924ef249da2494ccf7fb3ee71002dba46c85867c10fd08122bcafd9385041eeb53b64f3649c97d8a04d9b74c6e9b6890b5666c741073f7b2d65a7befbd2ff070420d5f96cfdda52a5d966f6190557821220b2bcbaf4e82784286c690e5e329a8307579010657963fef9d0ff6958f7990d292e5bd5c18764db0cc958655640f6738fe6cf6c0b2fcbed27544f7381fc3b4bd98bbb54e4da1ac937db2360b65a5ec1485a250148a42d1af555da35fb5e81e3ba5c526f76185b6e8f32e8e2c656b53a63a60ced626a90117da229963045db3a13dea1d1adaa6dea16f04fb4f5ae53e4ee534faf762fc7dff5499fe2852aa44e548548932d126ea449fa8cd91901cc9911cc99f1c6936398dbacda15ccaa76e4db3694ecd260935a76e5466aabc484249a82a09f5a36a164da4994483ee0adf8fab52a9a994649374924fd2d635792fa54fc59c43083cfe645a846493ac9265b24d3f40ee532488da867cb80f0da2376f05b61fa082c6ee5275e0b8312972e1c863c3706c9b9017d2905a4f03ffcefd3fa4061e4a249d63d6642893f4ce7c28b0ec10ca7fb88f7ce2fd709f0e325385249fb0c823dc999b95a92361049926f70f719f19c4c1cf108fd3ec73d0e180fb0853a0597992bcd3209cada8a46d98052d54686800000000c3150000200c0a0704e29040280c346d0b3f14000d6590467c5636974723498ce3300c8218638c318600408031842084c8a8380a20a93ae0ef8e5b8d2b012d9687ec3d9145f2a3d70cdf0904b3335447e2ef2dd46f5ea2d6a06ab4793a636febde89617ff85ee29ca08cf731d4731a85b16434c5381f629f34d68985826ede26e7f649f4abc3f6c357c947c3f51bcd6630ebc3fe927b066cc6b9f1840e3e007099c2bea75ec762734322a7e01e20d3129ba6199e5431b84298cf6972ad21d2ed5a88aad7b5370d489dcb5777b6bc48fba388d74821dce2f905107e6b672cd9615052ee230f3d0e3a63152427e0fa0a346c0a688207f25c6ac528c153a5fc6c01ed2ac4d88f71d4b1c0d5cb46446938589de7fa15f64c21cda0164eac2c0281609d96ca199199071cc7ce20173b22ade8a627b5c8fcf5c023a37508b197198856ee74b8c60da8d37592dd4f01cefd105f468c8beea50d7d117033795c6f4d708c94b91fa723aa22fb8b314cb904ae006ba2665e440f21953b84baea1e47635659b6a394788c033364cb8614569dd18ec72f0a930bc686a0b05b8f9047da7442c89aef91a96c91797536906f38267c6239fd1eb8329579ecee0e924897aaa05a2bb4e0e225101c0cff461244ab9af151dc1c25a1326137859be91e9625e08e7178b2168b72526d7bc41868fc05176947f392646278829122274c66f95104c3098e1981226fbabce17a2631783ff2995d6e2f522e47e448efba56555c909b79413566399b7549c3d7c5a8d419c58a1d627e0c09bef9ebfb627ca92f844a61ee65218d24028fded8676118f3458af1b86bb1d4e2f49b7eff5fa310a095324a24c0dc309092a5a8178e190102cd148478c1f843382547f95889397492f1f1fd4ae8b59a7a1acb004b0ecf790aa8a8ce4b3f678544a194c383c260ba140bc417f7e580a62d1bd822de074b8d9723f23925518ccba28671c3f14780311335d1e7443d4dc9c2c988fe997cd82a5fe9a9db6e329a8598001e0b58cbd64e00575c62abcbe8b6bd4d87f666505d7fc2aadd810610771914f0d228e9cf4d8cc2924aa54a96493d3932a41112ca7219af7046e4117f50468749ba233047b04d8a15ab1b8d4bdd1949a3574703701d08147172461659501cfcf4593c2250229acfc300f93507632f88e5eb4d2bf90f84870538107b5054770f466b3fa9642c59ed90a41d251cd1f0c97294842d752deb416da859dd690c801031cca2f6e97f9a4ce49c443ccd05a07c56187d4c54ce9bf2d81378be6c8f50e7a863093b03e47d428f1086a24eea20cb8b146c795a7adff31822bbcceb8fd65806f147481f63e24fe82a120085331a78ba01342a37831226e64e48be0a22211bb57b293ac3f2d278efe05be6713565699b9a2f057797799446c0ee4012b8c00c604b2ea31082997549d582e8c40ae7f5916e0e9b4aa1cb4dabf9f9970001e6e1b715d5d8ed35bde3ed0d0f7c578235cfe407ede500c88e609b7e239b825544d0a098331b36fae22591b06f27350d75e8e6b11280403d621814f2e1c1db4bac01f2760511f9af68fbee09db11634efcf046c214c589626df939930a96e89b39d1084c56e8388a3136095390dcb46842a3791a2f9c464efab6e291234fd97b4e7b0a3b35b717a4ea47f5ec9178f80b0ed285ba2f5aa9875e0c8093561eb1cea0039de6873b268572ea3a49cbf9d4e00588156578e3a8b065567ae06876020e2686398d4461cb1dfff025a06bcec7cbb5bbc9ec36847408d85374be9e20c8168f9222176acafd830107e0e56be01e8e0a061c965ecf987de7df5b59c309f1a471fb3bca4fc09caf03a67bfd397edbee2c70999875af04b6d0e5a0b445fd7467560a56ed6b87af8a4ecddde252b1026463050a226e17916bc202c4073cfd2a1adc14b849a87cdff2c86132a7ae1a398e3e1a0670f187dd577a11581afc642c743cd669fbd0c20e2349047da8d7dd9b94cefe2f2aecc3b44b6972a496179862cab939217999213a999b4465782d86fb6d678103d1b114d38d8029696038564f4777b25b71f308e574a881bbbcf90742cc37830545c884ddd209d88b344fd339e5a4d43bf149d78661d5a3fc135eecbcbf98d69fd280676426cebc26e1cd85e00bbc15b9661aa4d25f3e45c1ca049bbce8292884a66473e629fb3c6dbb330c3ea06122a67c11457d8a160cae7882e7b620653e9a92024ccc299304025b152f8c277efae720b1275bd7e7a092bbf901444ce3bf6404b4cc5251ab3fa4d3fbdae1aa87e42286997ead6da8cafa5188835f626e6a065d1c3a320c66846afec000cda43768813e617cf6fe0e98c6bf9e58504ff6547c1f96936c88aca48c3a6f03028dada5dc633b21fe8a4696dbd958170acadf12e95dc1863913b150f5a1dadc27a42be6aae1fc2397654430ac9703edaf0e023a6a32b2f7fd5393e219731b40f2a04905b709804accd02f044b1bbe7ebff2f3879709c2980a54b7448042fe68d732ebdca18c0c3a12a20641bca3327a4c99920f98678974be4acdc3f193416acca76a536a2c66d41fe7de3c25788442062fe0b9b83f6b7419162ce584d082a73a584e3ecd25e4b3a4b02f67fceea2a1fd0ed959a6528e4d08d52c90c25ee502a919561b393f63882009039b48c24907ffc55faec6c388f25f49590094fdd680b436105464fc4d216decf73fbc44de9258ec94e2bf6da862b74008bdcde10024ad4dd125123f9375ba7026de6da164aa2498cca06535fb10108b88506b2a054072d75a352713e7688cf021ed8ab26059e2b70f47835f5748a23e9c079d963e960060ad0e2341094073e345746b2b04f2b7c896054f7fa34c38959a98bc2e5ae4318bf67318ca4ad0bc980e545bf39778b2fe9a35c6fee0951cbd7a1e36b0ab2870ea7cba53fd2d99845934720c1309e9197049e6789aa305176006009abc2754470cef553e848881e0a017f3a8ede61de60d174d6783a6801403c6a2de6956284eb4146b51dd430c45e5ed25ee4ac73ae563873ebece0421d39aad9701c0c9182fe5d1f67fadb4b08c5820b2b8f2fcb6eafb82c014dac21cc5449bf3e5df428794cfb8772e3888bf74668dd000e9f35d63b85adcf6bfe0ba2eb094c003653d70a1170b253c7a0ae0b25af56f83bc8434f42f63455daa498080076c762ccdd1a9f08cacf4344b7b26e85ae6460b5d346d9561a7d8c3a94c535293643cabd64bf19221a15cdf9baa3653b8479938536bae7501645de9c91a432497ccb464774d7030e944cd158a0e006e256b593ba209ca772445ff506a3f2e898d1b6bb867262a33f7a88240f360e32722ea3011a411d72cc4b0663526eed1a428436794d9f4fa410689adc9ff81eb71df79b313eacac0cf4d3c1e86b090060a07d52efc18e37ba172ef0794c05feb585bc74fe8e1bd969eb60a07b9c81de99d7cfe8e0800552ff58120b2094590b01a025fb584262e9f6630d25731f1fd43054e089dfc7a2bfa8f49e4cf280c219fdc3239fbf97891590b0a6ad961c1d222e3fadaf78c567929c6643daf4223878a14d1736f6a98487bc510d328ad894272beb7b4495811fb33f04761d6c944194359e7c3f3e4e67408493ec4984c64119b1e6b0f1055022371bf4849dad9cf82b1b292823d4eca58d0474b3ae1d08e1abd63e04c71241abf27b85767441240ec12c83ac2c79c203fd0c8a2ce05974a878a84ff1bd9e113f256f16f699804e64135baacf61499491b015606a40f9e48f21efd58a6ceb860f9898ee4a0cb2f8ddc2cd841cba83b39235ff8c7f5ea5fcb1dcd7bcb056d848d8d1d126505e2eeb799746b0a0e6aea530ed594a62ee5d0153b1b4d317368b5ae9508681142a95359c02d3892421000b09296c3334b033e343451b54b2a4c41b9005abcc996ff1607a71171e00902302fa5c16a78ce8ad8b61ed696d8856a22180379c7580eaa395c654074b2b8081db12cd3729f1afbeafa89dc74083317949794c8405faf2c68447424fb7741b0a14609793a21a992c022cfe4fcb871977437bc248a3e71561c3811c514b2f41b17c2047075fcf233c5fbc78d76fdc33e05a239714abdf9335499ff86af6ac02c3c3962a105f69ba903fb94840904cef8b4243f5f7e701bebd59095254ab81b2c70c4b844f71dc8f1a468f5093fadc570a30e4e814c8af48e391db00e46a9ca5dfcbf7d82e2ea58965daefef9594b9cd2ea73118fbe0406f7c5972fdcca3156b8f5d2763bd532ca76d2f2b85d1fbc13a5aa2d374af2789e5ba992712a9632c0f00d8d0f0bd091b91310139e4873f4a699c7d1374ca2f57cc77ee7e5e3490ccf7ae62f6796afd86def0d38de992f69603d28bafe4fb997d7e79240bcf1aeac6c3b59e9a28fff7d33d4221b5550556e565fee29d524ac2a6e298026b84eba309644995db12f7128b1017683900bfd6e44ca924c72145d53e83168fbdcb2c4490e859fb4478687c8bdef4612d41406912b5b972cbd5671db17678de8a278edf6825814e3e973ad6ef03a34b76633666a3112d4e46e8e6a46f10977dee66361f12ad4a25697f8cdf0bc056bad29757439d0838cbde43b30f7526564d00a90503a08caef1271c7efbd03dede3da83193bf996a39ebc5a5b35ce6996d994a236d519d8d3b5c61b590aa723da532b8951dd6e4b6a19de192eb76134d13fde0cbd63d5c289a2381a0a957b942fc08ba99e249441f0cdcc031072312f08f3b29170cc12bd1c2ce514120adef3c8c668343c0d090121845e9a928857b78b89adbd50b57d94a2ade94f5a34d4d5de12cb252980dfd948e343b6eedfbf727369224bc52ce66f1d392c2fe4ddc9ee9b84fcb0a73189f1bcf1af1d7de8cc70bf252042d846350eb3c2c6d00fd8dedb3a83da8f161ea67eda9a43a023cfdcadc6166efa5b2dcdaf57c123f501673d70904311ecc72765c529de80e135e0a1f816a25ad1f4a81573a90be298e78c72e5f7c71d67ea8c954545529096635fcc2ca063968dc292b9ef63ac29d8794ae2b3fcfbc68160edee7b37fb30cd84afc6d3ad4a45d6c090e7070194f052c37072086404c02b7179dd1091ad5ba6d104134591f6ad1f3faf918647f4b9c6e9f7102a13ecca24b69c4b366f18a0d683397f41c0524b20a22ae3c26ad17d0594848f7402c429759bc8988a7613a6b84cf55efd333ba2e222595a391c8a7fd2c53980e2adec814f695e1f97909ede7ba1dbb26e55ef4816e4a5e4f1cf043cd442450462cd6de269d7e3d4e899f9f9dabeb418e5693fcb0a22ae300112265470ba6df440af6c0e91eec389d6d0dcd170dcd1a22c496e80c31c18754134114be7ca856eaf6f62fa55e1c08cfe915765458c922d30a4cce248f575dbf349abd72be75492fdb387ecd35bf6af1bf6d5ced3ab46b08a9d75e084b3efb75c0f4b1d92f114111d6771d3b58573d490e7c7bfe05c4b4d2956c08a08848223ff92ccee8a2099b29c889f46c8f317d9c870637748719a2b430c0e09a1832fe668c1cb3668b634ead7e6f83e0ab6badb3b24de76a07ab1c8e7009c09c14eda3caaeb87b42cf78e40903326a4ac8c4e2f729044ee294bd5886bf21c39ca4a8e8b7b6cdca24faf9775f1d693f54c1c7816474c01402202c995865f40f80ea8f45c9526e84b0dac772d7d8e653e0c3c6222e3fb896bf76fcd32dd6e270a883d601d81d5e13df9532edb7924c004a98d924d31cc52bb4b495e92823c9d8957a3f9d65acaa50bd5d4c9b6e6c813858ee67b1dd181611ce61511368d3ff7497ca0fe9e9393559ef298b323d2509d36b785b5191c048754e7086f5143a9357fa5e0e4a0e43d16a837a7d97d3b87207d9033f05ce051bb6cc2ad010784b9aaa4879ef8ebaab26cfb3ea38f1a952c9c51fb6c54acfd2debc133de04d6ac5152945ac95ef458b948735d041e4c6b2643c6987c834d74336a2db4bea697c48889108842c2bc9e286bb8acc9b1f70bae800055655c392a7b35f893d4081250934c436454387cc242d293679cf1e4d5881346fa668f93e874630a5581344144963ae46b4801810e159f85a37417d193d2c2532371297e965efbfefa5b474ae24c75f359470e5ea98fe51c5e6e3a4aedbcb4d90472ca60210fd4bc2ab3940114526dc824f0b859898e28bd91d8622d9bd4dd23d94c4948bd3a41c1fa48a3ba05c2f5828dce05ac6fc00a39bcad625f6014d48a46f5e16bcfe383141b890a081278f1fe3350b4a64586b816599c78e2653fb61c3f9768e1586587c30634312e3ebfa659a78aa16a96b72c8970f551b8991b73f9927982e5466b0ded608b57dd578383357ce5307c84350b101e4ef9ec7e2a5a9052ab435bbea04e9345a8f4df1a1913237b1270e5f73c810da4cd037cabf50588022268ad294a621982b8b4ed8bd8870c2e8786904c0ff942b6a4aef9668438683d5ea27f15d9c3a274c7f1758c50ca3e9a350431bdc2c8d23893922576b43f0fa4b13953c7259fa4e2e70c0122966d436b5ea0f92c8f3f55b8d217a6b6771398d6f7eb5c95edfdbb23bd0c54c5abcafd07ef48523cfc8883897101839ca1c488365ae4be3ea4c98d8526aaa14f225e53819f145d20d7a53c70fa67e4b4fc31cc9ab12d269889524ff740f21a04dc6f1bf507256f0f1697e594fc36520a7c385c1528fed2bca1885d11a32c8e5e3ab7889c1e70d9d337289a75f2201ef3a0785551f3c6dfa984c438b841b9420d2a7a5c0fae5f7b8af278ca764e3ba02b80fe10db8a3aecd3f158817c829b47f2e2a8184ba5ea54b8210c1dee3ed86c70f14ec785e6d8a2f0ef934e2c0bd1a96d7d54ac209325aadecf1d41718e39f15c637d8cced5b0e8b2b44ce1f70042f6c8726d876e01aafbe4440c19fc18d8148cb1ec1ff1b4080ed87ecb5089cefecff4aa6cb4bf81ca3405578ef696af4400cb0024ce33b17dafaf701543d7614e22ee753f8371266c6c0c0be770457745addd675673c182ad8da4abae242b9a55a3032963420152cfd867c025fe449e0149c971eb94a55ac29003a5c33f839af7e5e969720738701cd7fac31c2bb7c355333575214f13139a011dc56aa2103ade595bf0d4c67a7a886acfe563226cd82ff85171801293e940a280d38b61070155b286876ac6632dc0cd92af6924f3f7d50d47309ee59697a0b09de8bd4f542a35999cd950cbf646008984d4069cea52c4df1ca6c6b93c99b0e5223ec90046eb4caafbf9f9c89a14ead1059260ea474c9e653774f4d506c0a157e00acce56561cd030db015e38eecb57cff97fc25eb5c1f6ff9d235be93e477b43b2524642cbc05b83fd3b53fc21c0102cec2ea8baf2f739edf825e2671a3653d40b7059ae8ce86174bc04b9f30119e71d5211ad7240dc3f65ef110ab2a4c625880937b9d1c75709b922bbd5c4fc673414f7b708e446f9a8aed8c412f91752d51932a24953448bdd864372b81b9e4dda8b44a98d97a50295f3926d671778a420a7d8a8f0b9e52a227d7d357ae924157de236a2e6d1b68c6f6cd9eb1c002bc0a30e01a837954eec654501023c49d8862c91a91f7d74b3825848a952785aab21ff1e9caf04b61f77e5656f18c0210ea7d673b2f9b39fd478cdd619aa09c9fb0f5f31532562d26fa96e8385144da4d3bb00093c0744adf907f2fe8b7500baafccdf3f38b527685e359e6ee2b0d7843dc211438b21294e1b3efac816baff6d2625dfe13011420eb40f4751e34bddbf6c91e2c0a42f6f22972597bec6873a0dd0b6bf98a7ee9feb587f2785f0d054d0bb525ae0f4e969c44bd5237e4e714b76b85b576ec6db78824129e884c070a6c5e4f2358181a0c17f38c9fa616db7201a63ff0c85cbf108644b542314453536211b24b9f521c064deb8d842eaad568cbc6c81bbe340c927b36ddf269fdb807f92f8ecdb7cdf932c0954b56525dd7a1916e69f8403991ab92c5e18853aee004a874bd9bab2e08cff14932feb4d26caaa7fa451caa4a85677a9163905e39a0d03b2db3345429278500df0ca909455dc53bc76566f4d0aad091626a2756dce78db39aef127592c79c4a50d3c50e33cf4832b97c793863ef21006d29847c61be5ef6137d68e622ef92ef7491216cd21956f2080e3fc7aab2208df42495865c582cae268d38065123a34a56f09fa41ba66d18ac8018fcc8d0e1c40f2371ba19bd22f86ead13de1d9d9cf29ec8ba681ccd02491f05b36351b37fa988fb1dbed74b9f13cc26dc861f69e7a3dd46f04cbc6b985247335918b4f063f660fb40d3d7cce79b2350076b810543a3aa1318553b62a956ae11c2d9ec34d3d4d9434e1bdf939d49d7c83679efce02b95de7ed1fe5376257573c76d3b1c58f45835769875b61dba5e50110ae0884908d58f1260defa5f05443c03ec865ebcd9ae52606c6580bd7a835a92f875c3cfaabe9ff1c166ecab77847e76f2598847c46d78e4687e4a62847e62cefb2aedfee29bfc956b09ec502bef2b12708c98f4cc20a8b31e955659276b3eb156c6153bf95be1ff43efc842fd1c195c70c3293a50d9cd29ca59aa2d781d52e9a266dfcb9b05e2a62833781e2b5646054c0f99362ae32e8ee1125c0d39b8cc51d600d14373ef6e6ceaf9238125af38417fc4f9975a891661919373705c1ad0850cc35a60526b5b3d598b45bd8b0bfdedb548e667d2a2e127f680468a0d238f418fedb5d27f86f654961084677279e6ff139359dbb6988c35b79efa66751a91d0225004c81828ccf283f2d4f8230a77141951f41b6d9bbde162ddc8cc38ed43369e235d8514b99d58552bf71f788132cb58bef9bb64476cf0f59952d41c4110bc6e7616bcc2a787369e590156b5365dcae5e43004bf2c440b60ab6309c58f688dc47d57eb7e586c6b23377a6f949a61ea9b98da90c55543608ceb5cc5ec5db3869d4162820b8cf6879e3c9503fdceb99af4de4a83b9385fa317f5eb0cd99e7290c5f5271c89665ea398c9d7d4a3a19bf235ac79507c6a26ad2e8d8cf388dfbcf304450a4712a9ab98378fe4a650f847f69853dd517aa1c0bd71df3fdc8ab48f3749690f7fbfb9dd0f134e6c7dff1d5e304e03542101788ad506b7ded09358e70e6b478c6e23b89b088c5d16c9ed9d4201c2460cdd0677ee1d672357334343b81b56e1fdd697199ffd8c245b5b370155760015c6098f434021a887855533c5a2a4a9f4230c38e738836089c240182ba0477c791b32e21f9942a10eb8b39225322ba82ab38cced79cc8fed51db71ac2cd67e0a4e256f97028099794ee772e18d8031c0705710dff2005ca711a00b52579667dacc4c98ad6683c998e9c09376571de6ffee5018d37e49a3db4b87606a09bb7842a81f2bc8b2d4ce841c60bce77c26809ccee9080fba76907e8f7948d96f52d30e98c4975a173f471382c5ab7860b774a5c7321a37305dd6fa3dc9b0ef41a2becd189dd99fe3c7e8843d8c03bebb1b2b69855565e2a4fd9c82fba57b9dfe8c938d8c742319f8ed24dc95ab1f560a2f6196ad254a2821dec00b6a623c8e61d56504ecbc7066c2f452a4115ea0ac31ef7436eff5e84ed7dfd1db40e3bbdfe1e9d1396efd295d512605a805138c60a063086d6d16d428bbe37fa7ab72d9760bfc79492d786751fa794859a4abce9b1080721dd681416d5b3ec819b0ad4d80866387aa28241387594073793dcc03f4a86af7c295ec1aa2eb0594f95efa2a386c77b8edc7cfd5f00d58ea838e7043404a84a5206a17c706a2887a04de17246d6dfd076927b29ae8a45c3999c533174b709e5d35a7760a664bcff6314de60561370ee79e9f655a7c6c32a044850f13229a0a596c1b05e23e2a6e80348b76d90812a2a0b6654490027aa845780158a02438ca84fc46413aaa4ffe6faa35280f75b1ca0da972491a5c530164168bd024319d792c561b23102ec96abb47847ddfc74df996c6243f8996351dc0f6c77ea977db026e28502f2e2b4b4d9f10382fb5f6457189976d52657029ddcb012127112f7ca8193ec5910b62326bb691d4053cd1c1722d22e1bd6a6c210a4667a1758ea42c2601aedff68e01fe8c4d254ad808eee1e6b423bace8b20a05b1863a171cec882d07cfe08fc872573748c66330b765979a2489f40ee28002a6b7957cbbdad79075eda207d0f77e0db3616525f16fde91d0700d51c25916c6342d5a24df00487e3065beb6201407a5581467b4ff341466ff22c8abaf51b642d5c032962586ea44c5a397a89c544dcae96fa03804a8f913065f67c406048622d0b7ac66449a02259c592e4114a1334aa9837e0a29b1542efdb6bb1e4f30f614c5ffe657f79ab2dc32149831aeed1a00e0d05ee23f7700c96b53e997fd1a2b85f711e361196ace96e6186047cdf3883a5bcf95a29c00fadfe252085f998e910cbb5d61dd989877c44a98b9ac22aa26a8165e2144804baf040e7084ae7ec46d2636f0367622256d8f73d81e53c820b259b1396e30138ff10740c55b08a129db116548036e9e1473b5515c1bdbc74aa70bc105f71385afbf1f70ada200ac3099084b60affc1116631f0cb145e17a0f8dec078d27fcd2a0fef12a289ec254e923458f32c4f12835334d5e1f89d728aa4b53f657bacf93730cd649d20b1a087e5730f8231722d29d0777d2dcf25a477c6d21da0230fd4e4a173a05c5c15acc19b28f5639ca2f8c2a20401e45f374c7be06a9abec57021d20eeb481ca66a7122c18c2ec23ecfad52ed1ea2dfd7b47ca78391ed89ed73ad002399332e9a064d4620982294da7e2de1bd47e078408992c62299b1f60dd5f076b302caf451da1fd844614fec971854664f030273d5b65f1390722e8502d65a1109b392dbfd442fcfdb7ef3cf7c2c92fa8258da6592faa7d0627f5c3d77aab735d57588c27384fabc9792d31a28b04ee5bd510528baf1ba116e2b51ea59275bbab111379aaf6686918e2f879571ff57a106c5c50d730dd7d4809317e0a6ddf062cabeff6ab0ceb2ad41659e020b1709f9fb83641a93ef5dc8335628a53915880d13e868e68ec0e8d28e0df01476a0870d155d8f12fbd86471606c74c2472479229733f3bbf85a2271770aca9789cf5778b91dbb87c44f56ceb952543140ae58fd7f8a79cce8bf229ef81730722149e7dece1224c407cc0d0ad83c3db19d33996b6ed3ab876a5aa1c2e6e2b4eaf9ae5d64b7ead3076cc6e7457f7fcd5a671177077d7963e3eac8bcb46c02e1ed3d8b784049a9a9351a384926c87973a477841c3de49401717a24575e3cc755a3380c62d95b07945f3f845ed7076ddc4a11a7b94db242ad04f178352ed1b2a44854e5a798b5e47f90b435195fb2f5b35e8436f05d517c6b37ace30ea1d59050dc4c001af7787bbbedf11a3b320a46b4c8f44f15354ddd92967dc23a6fdafb7f63b32ed3f42385eb64ec9af8da248caadc81848e7a4f42d1db5d49a7232fd7bab957fe6febb4832624383a3047877a028c43807a1b26489f5ef2d3f665feb636cb0542532bb84753d8068c60677b73791e3a908c9b062d6776f392de7dcc071e5a183b20443faa15c57799c14eac6e9450eeb2f7ea12d16ba3a0d0e982cb427bb3d18cc81eea3965704f05362651bc7c98021b5aa05c776d2e4d44c868c3f39f622d8ad54c49f818ba6f86cbad2c3f6e0626db89420f5457d75b5eb14560862879da0bd4eb9a612d66e7377fd27dabc62253c9a6d9bca19857450524a8d1ecf6eef3c5e4f20dd0149b51fd6b349e489e7a4913cafd9b6b04ccb640eaacd8a96e3d64986a389b3f53fa334be4e08c51f2a52662d141ba57df268b9f15b30ef7536bc69dc5e544e4bf09c62f3fc7836750a6f86d31f0a1b726bcb877f90b5cefe61cdc60fd3727dffce0b0b0285b0b054e69f0001c3b4e20ee04d207cc5ae9302b322d9a8dd93a219f1b48b626fb3050dce014a8de755f7907e1188ff58fb514776324c81cafa19f00d76888ade31efda917bf4c8e1564f2d30935886a755250342901fe07b5285d3391ece0f7acb94bde2283c9d1230127f8b97a3429955a3622219ab4553f321323fdb951bfea1d82485ccf17a5ec9c739af20d5b9b3a351eacfde7a141ae1fecca970b5c07740f8ede3a4ec50a4120fb48d9c0f33dfd50d4a037e069b99a1d1a39f3f7a12d3cc7990a41751118379967131e800e23ee9dc224a7d7ab6a4bfe5f639c6e7daccdd2300842854bba580ab7a21b695301c26ac90371452bb11d56cb0549f1c88cad19c4a345d9b32b3b92826bbf40a3ba9f02ab4d9ae0e4af5425a00ae91260d1c902c2930c56d4f69a0233189869eb8d6ee1e4f035467a407abed5003553d0adead603d27692145440d56f1176068cc59090b0e3176a94dea55a61769703fb347aa35d3564a4513cf1b72525fa72959e7edda665a8585a7af3db44e8e7fb69bbe0c8a8d8dad3da53dba70d43687dedfd0cad61f2b17e50a88ac1b8911af7abf47d2ae54f56aed18c2fe35e155ac5e519aca9468b0029fbeb6561d60e2f3953b97b493a9577d39c9238b5bc9694105b1b4d025b95578553769e16c2f615873e5a21ad65da76cd13757f14b3b08b70525722f87d3efe5c4a386c919d859cb845057110dc00a0df142607872e428371d7adb3584ce3b69a723a2064dcaafaea67a84bc90d140bb526410047d6ce09bce6550290df654b238146132719c3e06f5c2b6ce6d88d9b3184b83d676db57cec33c92f31287b82043111496699f50cd1d765eee12c772dfd82c21efe9fd0b4935967ddcab85615822dc632c97d3e31417c079413708cb2e2cbe311e9557b246f92f8aeae209cd515864dd3964812d43b489c2bf608971aaa421a432c4af2faf8ab92b8ca09ba78e71cee6e295a1fc3a8b1a06ddc0c6186c16b2c470030302c42995b9a70ca9a1f3fb08ed9ae8f412eb3e313750b93cb7f2694f943925b55ff9c326df8b72c9b123da875bccf4bbad51644e1a19c488746016538c7a52082c7bbe07a45f9ee4a82ff94bcb34b477e7e1678f1cd7f971e59ec8f20664aceafe9bf8a4463fdb580d4b5a7289b3d7340d06c7c609588da46d2c51b25558d1c3b271561b2e2ff6c4962896f4341c190e64183c9e3a1a4498a93c6238c8c078f454e04204b3a6460e5f24580c9c1a5f6430903c72b40861423242dc88b05661a4f89061bc68aae011875553230213114c474e052c8830d77104d5651a4a2540b9d75109e439f4121f92941afc904d704c6a36fe9394374afb165d76d21867a97eef11a1164fcf70d8832857c872306ba92edd27204387f8c2045ac48a5706a6a00b3520f210743bd5a191ef6a5688cdaf98c13ea1a3230c382dcde37e6524f7b5b42254b8944ebd0117691dca91b7b5d79bdb69a574a1de5bb529e74a8652038aaa89f669da02b83ab0475025d07fb0ec77a0518a7bda4547bacd1c535c9fe211054fd80b847ce525bcb56b3b4c8ba437f12c163827f1828290eb51970656058e5986f2ea1167cfe916404d1543bdd58e810933f4a3a66fa46e67d743ecd56877b77fbbd881bdf79b60f4eb8690daa24ebdaa4e0a4ddb858801fcfe25ae2de80f153c06d8ce6ada437ecfc08a4c86438f31e040facf8a54ea6bf9d355f4932376193de526be487eda0a175f4e14f8a4d0303141f6823669bde509ab64c511200569f08c88baddce0dc51610b7c2da8a512c3b329b2812aef74158fc989860d3170b4804f11d777843f563284c18942d13a6b703727e944e4fb6e7887a2b4d88fc5f0967ef76a2afd4f3b49a2fbaeec843a83220adb7da6c2bc373edd695fc3a7b1e07f9821a525c7aa82d24747e6ead965ffb851db305b3d2fdf92eb5f06d4175df2e634c6c50e41b602363ecf21b9c424aea3b4a9112a800a4f4b6acdf345e2515de8f1c4aaa97b38f45e6b97fcf07c67e1972f616c5bf879a8f2efe14fba4d54b08e49fdd60db344fc7068f94b86bb71ab4b531d72c30de2c0b2d48d97eb3005b44b34d80a4cdc8cc44fb887145a7e603710fce60aaa8d58f57dc6b58d1879ce34ee3a2fa39e561dbddd61a619ee78a1ae8e7b12c362346c2331157a844456850cfed8ea7b6a78ad5b8528799ad7a8bb621d28f5ddf6ebc57b0eb377b1f8feb6d9727c43ce14921afcf9634ebfeec0e2f926e378029a96bc88685c91e514a7e4200af9fb1a0303a0e5ab86e8a66f65c6b4649f7f50719e3944cd1c00ceb51152b5ca2472b192807914d5161786d7a67e48ab4327b0a15610fd86d04cb0fd8946356c7bd92614603ed431a00c629349b207612263023af88655ea445bdcfbc5d6a19802bcecd4350d163063e8b832aadc8499898fb99915be0bb210b2e312b72ba60700e30e04d6e6895d4a958cb4ef08ac1a497ed90b8e18f3ccc7b040c52c711936e70683184c6b9c3c15b46d6ea98a817ad491e50bd218089adedc60866598f981b28afe57adb8aacfc031bd533eaa2804ca3678198ac40c8d9ed5400eada248fb85046002fa362579103fb5ab54369ca31dec7f8dfe396133f6aa9d7feee051fc2a3b2406e8d64fe47785274a2d7f688b316c7614878961762835cba94ad675b4fade5be3ae03d575b037433f12139a73c58edf68994c3e4e7116a43fd3628c6c853a4ddccc89f084d15725fa3e096c1cc040b3f80078420ab9934c55bc3c332958f3ff5464412206206e3637932c8a2e0f94157e01c651851eb67361fade91e1674730b51d33dad704dfc1bf0dc45dd2ad6ca3f59bb2cc1209d795ada04050d62dbc122939b351b3b8de0b3d7765a9ce8c6eed940df135684ee58673e4e4ed28159ab46d0bbaee8d66139ddc155f5f9cf4bc3dd72d51e9318c7fdfda86c584132cb5ea2b866fe21a847534b9bc8f82f352ebb2876c9ae3d151501d61affbbf3fe0997ced4435d21d774f8248540770b679aad2335595925c964aadf51af48a59d5b1cfae4acd69744541e9a131274c2d1940f6b4a7491513ef51613550456ff41fff7d05d0566c48b1941ad76e40c7430fcc60ac1058f4cc6aed7a2c84a7df0729a38ff3961801eb6ecac5f2b6e73042e64cfc62b69c217b997a293d4e0234b604528b174431b08208b21653a96c682e7ee5a3aabde63eb8c40ddce7b9ad2788f7c06f2c62e091fada48ed64e495edf60471a5b68bc52cd48f0b1943de790448b65e96109590397acc36a3995fdb7ede3720b696d75b9cd48c56bec9329b7264d371acbaa80c556278a564609cd16b8c451519ce383ee28a2c9ea8cede91919f57c5d39f520e0b4aca382f1cf0d2ba3adf4d486b543ba96a60666feb8022e3612ea174193d6785e6e768604ca5e7344c0a8c42996aa540545b37c0b1d83181e38744682e072beaa6a2d1684c5930cb843e261b9c6003acc9f169320917ff4b81924c90ce91ac85acf64246d2d59203ec9537e333d882ba0c43821faced582293575e9d2e46ee8acab793c0141b824b1aaeb85a783f9bf173cf4c8a246ad01d81ad86f8ba1c3ca877001958fb99b2c3681588271ba036e28cf408f4d28a679abed1aee08dad5a99136ac067af6c389cae391b369f82e0527e01d6107d91b3318c192e1e7f012bbbd0a89dde4ba407071dcc99ac936614d660b0f4048c0ab2c17067319133c205a0d3e4c408a05b8ccf24cb489b68a0c14f0cccde967bdd6bf2c6045d5e94e472797669e3e2f35e1aab3bb9311d9fccacbc2e32b7a195b2828dd0a751b4ebf7602db2715a8a498db98adf685d11d20e049103dfc791560f21acacbae869b4dcf0d20555bf53fc0ca67b0a0bd7762c3775b79a9d51dcf8d3bcba984d8a6bf6a6b839ae181d262b1ff5457a081303f509e8eda3d784cb08d282177cdd4d5770f5fb8f69756d68a5ea0b15267f65aa22f3dbd961b3caf9f019d09436d0c924edbdff40141b2f6011a6e3d10739209b3c44a807e785ecdd3ba5c6026982e57fdb4703a46803cc30fa7bf11041828b37cf391d812a9c49e036bc192e68b3453ff067331f0df02ced36b34125d6fbb31ec0961c2eeba9e0ef96976d53130e600a213b15b36f2b7073e18bebdfe69055a95546f5a2f7a33c1281984eefcbcb4e9831fc1a44f6d68c65958608ee2142bc373de9d4715b50cc174ae3c68f86bff958bf511f3765c8f5528a67b9c93945813f2f97e1bcf76cf757d81e2603f9d200266572539c09db84050a5bff088568f396e2677713d3d802ed439b8b033c19bbdbe1357e1dcac7127ebb6e7a18d1ca757fab8aa717aabb2d6132d71b5504ddcb4ea8b758080ea9e39e52e7a979bb819f13d97a0a617329bf3d49956b551fe1980872550b748dfe1d97ec9c78fb4e75ff2b8c5ae171b411efb74c0e6801381da941be1ec15859d88e0b655abe4292ea80805291d77d493ed6defa7434fe02369bc42f362f9b42d8e8b344ce1460c94fd16266dcc8ab4804d7a8a1d1ef1b1beb50fb3a74c3f73ae899c4fb0da3913aba43eb14be3f9879ef3d64b691f839b0cafac3a3ac76c5e0ae58b0503c77b1be9d461cd815dfe1dad0eb6684e95ab79dc28cf59a0b01211b970f29b508d79756be6ccb6d7fccbd4693b6bbded06ce5127a8dce11552d855391b9228a1ed44bf0dfbd12c8a7ded358ce59151f97445ed18be79602dc65c9b2487529e4589db98100c1d9b110643cc0ad35e9513e31eec541be12162516856c6549998701669a2a2395910b038f55090d05795ab4bcbe171eefa6aeb0121c1e9450e36bc7f3b03919c8d8d8013ac498ce3e07ff44b245c6f444f2fc66298a31e8c338c79d05e3641d4b79fe2d8293fa25a9611f8df630f1bd2ca2206659070e0f33ce7f1d31cf4e88429e6938a95fa911488dce92622967e3b4ca2bb49b94964d6fc0dce90100b54f544d0a5b317844a7393b8ea5939e3b18e9c7da6aa5ce96c96830bf11ea2a94f40a1e0f3babdc15c637cf8685b656c3b20155b4aa74b746fc9b15d10325760b641703be9861a504da88dd4353e9afc9b2418cdc0523111eef268e5706c0e2fc3b4ea323171654e09d7c19c8bd9cb33aee24c0a50ac0dae10f291b66ef541dc0a4461a0b61bcfdda2d067f9e0082222f906dc10f5b82cbc8e19910a875855dd926ac101fc9673b5c6777d4e4cd402598003bb51504ab5fa15d0fa141283be5732a5c1743549a3dc206692b88295dadc4a24f81f1638abb21a66f66d909cee1cc4828ea80f048449b75e35f5641a93589e1bf20b9f02b20bfdbe59c5e745dcc27ae8fdab378bcb8e8f043b79c055f5a52d591b625a6e432de19dfb7e61c5dd5f55387c4b70cc6d2dbc349f979abd7a541c23163a1a5222e4d9a0c0a99445bac3d62b66b496be9a70453396a11e58158840acf8387eca6d8a053e90c7466ab87c13dd91b0f1665dedf7f1b18b768b87f8b29e45e6ad6e4f573672dd321232effbf48d5c105429099937787356d022114522384ea9f2440acdd551f979fcff753a2064dac216f78066d4731ac9381a64cbcb79ae14d59cda7ce9da6f61fd2daeab5bd20f2d0d3c2f43bcdf88442ff76139598eb0c7f63e5459e5e211445b2bf6c88ce5c58c36d10c86ef71a3db7260bc716614c44b19060850fa40bd53f234163d27647d20853bf85ff89750800b369e0153982d15645b694a34382753014d9a017b447db1fdbb4645f038d7e4ca7b733024a4a920be09e0a308de9697e425c1a9a04671007bf736f57dadda6122447bc6ed3722706c289c2270bc6171cbad4b776ceba6aa16e742771d7c8add1aa704ff01434f22a2a41fdebe71acec4cd381111c5abaccd74d98c3da13e355dfbf067e28a7339d6120680a5146d9ac2cc79e21a4e8470c0ab091312c2b03e08b99f3dcd5d549c4658d230520c0fdc1d4546df63334a8132481eaf4bdf3bd114fcecbaf2e398703b09682c146e3b050b605954a03678943b300e19c104c381d04722553ce7883a400d830264628b1d165c638f712403d4ec4d09f255ead0d9408650f0d23ba6981d2a365b184bbc367081d4779583c5323d4f133bb0fbed6585e314229947c4973cf1d85402c16a721cb6d26a9518e20e4f9d38af1c34464c560bfe9c884c1c2496fcf73f53fcd86b8cd8c8320887f1beb559454c5963343196b6e12e8a98aa5c9c0e16a64d4c4aeee78c0fdacc7a7f9d0a922c0ea7dfe1cc4c31a60d175def842cc05ff3a5d331e2725a3af1d61d7368643c39b1a078d5f995efed49a05bf45901cd56506b80206b5ad99886a3d2bf0afd5a9bcf08de5d87c5fba188509ddb8419e252a3768d53924753a70d5206a18cb2e7c64c8733dead2856b428c09e0710cd7d80cbcf473ec2e0775c1c84d06e03e3b9b7bfa3477e65662cef0aa8063f37c85588f2c97ae4d11b5dfa68852115403df78e0cf1cc881992c440c32af2fc859b4090e02156c744ff571c542e1accb1ef34f3e73d734c6ee01d5fb2289fa8fd7041b100ba4cbe48720df3bdfb8e87f0999cf1fd30f77372611dbe0f9eba4f71a0b37dea2c094b89045cb0825b03c0927f08c3c261bdddb0749d527f63eaf9a51f246dd98878be8e759ebb4e8954ae1e668eceacce388bcbf80f4f0832ed11ba447f2a308d2d9f471dfc37c46d71e29b3af7c0e7178e82218f89d8da3d4142280f858e65e12343e058d4fd6b45290b5db94c3491cc5ef35dcc733923f34dfcab5f3f6c9e0e9a9ffdd280c6ba25a07f55ac1107cd511b900bf8aed60cc86d364b9f8d916253add859740862eabb29c05f5b0c6733e3ff8dfab118a544dd1efd0b673ffd4ff4d61781443fe3efa3b8e06dffd43d831abfe8293837c2bf2529e87aea1a4b06410caf7076db752f8e39efd699bdf86ef5ea608472c7a677a232b64af55b7e1f0bc0fd5de926d3d4466cea71f8f40d05f94b8927ba4c134b298c485c3f73cba19f48a43c610bada53ffaf6ef61d0c4f9420e9b7dc5758d4e93b9d99591783c50a13cb039375488bff8387a8c6198b638994eb18b6031cd779cb0e88131da011b40f9f49a0d80741f08aab3455aad927dd4f6def4a40c53c9d3dac7e69b1fba826b604f95979f224cb0ad96dda7726f2582004c88f93d467993e16b179af09981c8c39faab34257a513047288745b32da046fd38f121b507ba3d03ad9493491b9e497850a75db96590c3602772f92a06ee363221e15b934fef2e1757b2ac4bd380e6f1d3a3d53f41d179b2430cd044e8fbacf31ceef7defc8657034370257290693d497ee3641e52c941d510c19dc80c65fd00eac2154c02ff2046ed9d8868a1bbd4a4110307546570d63422b055c92f41ec4adec2c484ef51fa91fdc1625295ebef2fd4a4bb3e4d5bbfb995398775405d717abc24808806cbe5f57bb8bf6617db6a10b4cff0c01b1431a672ad28a9e67d8c4f441c5c11d1d17775b7a5455297cf20a97524d5c34964b9d05e45d492afb8f38b96752852d0b6a4db0ea57c0d47bcebc415e8fbb261625f169bd9ba720525cca672e99876eec5607f46cd765c098f879c75abaa2307e582adfd21ab1a891459dbdca18bd5f1411e9fb5a9061747108503798b9ed5a2cd37e9bcb5fb3cf77282c9737bc2dd2d31f502deab54415e9c86643932a875dad0a86e61b4e8313d283bdd07dcc3f6560040546430943ede923f5931b0d1a3e91e04f538bc854618e6a12b9a566764e348ecb9b07ff1adbd7f937d9d48929046dd54b098a0614e9611cd4518cc41c360fce1a68420c047df0793f53250e887c43f7da4e2088797cc1021997f3a7970bac911022157a441510e65d78a241256cfe4581f6be94982b2e43f4cd2d827861c5ac54aa81b9069f53162aaedcd78240f17e14e518ee5cba01a5720b9529212456f260d6e71b60179fd0bf1c392bcf385a34054d1506decbb5814a519416fdb74e2e95d884bb85bd0ffb61ffa38e83283d633b4b701b047bb541d8a53106cb802f61c1d79db5777ec8deb7457b57b4d99de083333a2b13d3276db6efb7f44603891492bd69da790e2f29e142c243915c362e7c8e5f8f1ad932cb993444873adfe1649e9c62e32ea2fbee660baf29434caf33b1293675e0a48314ae5bab8a23c128ff21002abec96f804a2c797d33730e9ca23473d5e4752cab6829ae95fe04de1190b219b476c09b27ca1a7ce0e4edeadd3c8928c9b48f4813276a05d06f977dbc31ecb7fd38632e0490834759bf75373d962e9ba21ec1c8b1ab0ad4e73f5ba3e6181f46a17345759857b7e85b35c7941fcb1596bb019bb7b1196784b62040c12542a8ec4a85a896068d86faca8536d200543e2011905bd471950d31608266578786039d8878a2662c03bd0f7db563fcb90bd909d38249c4e04d29d3a22f3cb3ec6be2c5854ae1208e47a6e4d201e4c82db1c84afda7f1cab2b0c2e0b62289cda5dd16b365d168a23564534d9090d688acac5cc458e8c9eb95c61924c332e38154b3a78968bbe6c90a8d5eb5f7ebac63fa8beb393b5a847e9dfe8fc1dd30a60afbc8817c9968f3707b15e9309081173d724fbbd189dc839b91539071dba6b2c3b3e80a770c002f3d4f40eb8c71d442cca089dbdbe6582b79e2889f2a03ff080f39fb002c3d7831110b83775aae725310407a57ca36b584e62f4da09d11bc2bb5acb017c234b721333aa0230c1a863bdc2ef719b27e5435f9c30f3adacb0209f2ce55e4e9d00bcdb214ea4ae68a07181fb4d55fec23859de387d851c27c1d4849d5bbc4140a8047741303d3c927e03ba411544b8ac9702466f61d51d905bdb0fb0b7e3f0f1040e30be6ba756a1dbdfbe38fa008eeccd9e39471feab5051b1ff24d27c70baa4179e2537e6204a8617b645eb0a71846481a8cd9a9fedaf323ca5450a5c2481b247f61cb1da17c5010899289ae7aff01d9093c578db06ac6de76150e348a688b4c14a85b2e36b2d30d95efa453b343fbcf06c3a93dfe57fd848bf685161e5fe775324b6b01d852ac6c0e9a1a75f9c491614e6e8ab0f7912f879ee38af4818fc2cff77697ad0e12572b388a52b56acd14ccd8eb5272262a5e2f1bf1aa98949caf0cb53555fe9fff0e4c54c598483477910cfa76334b7b10af22f7a2559009d43dd07d8a4ffeb64e3e2fd0b3f5a34fcdac0ffee322b002baf916fe0daba2983fa126f28de1085c9c12048a616ca5016001c8542eb0028a9f98156061fa449bbbb938fcfbe1455b812caf2588060b740935e5edbcf601197a18502c421b9c48dcfbb014e65c2555dfc02b4b98bb3f8fa578cf9844ec7b3c49cace7cda0997919352e896ce828c05e20452446ad86634de98a027375d27ac967e5e5fba3163d350fef956cd70afcc5e0016aaba2a24ad1b6b4256d18b0a7ae1ff9d17dbc36ea0bfdc00bc26eddb238a5da9bc5782fc55ad9fd1b39e0f29f6a720dbde71cfd4b25cd0f38bbb154b66956ad86fe8aa7c75f4f475236c60fa48abbac8df366d94299b9ca0f165914c2eafe6ce60c443ff56e1cbd5f7fc5191a46140a315f0c2c1099d16c35fa2f592be61fb76e609acaf835dedeffc5cff8f5036d9b30a4f4a51783e6dfb2a936924492fd6142efda6dd0a5fa4963518d373cceaa8def3bb983eb81c00469d558f0604cc981e218adde2b2da63f4d73cb531293565c660a02b9ab86e25fd66680792b00669beef5684d0c65a13b70c4970dfa537722d9b7f248135a643bef2df14d0313d8a96882396a0c2743de67e3e2ec9ddcad7aa1239bee6d6682f37e10ac7b3b20654189811ec08c58b602c7b9fb1c5cf7cfc210e866bf247aab78fe94d360439e54dbfe96e119ff515814c6fdbe832befc25081ba69483d3f7fb4c844b647abf1a466e782567ee97b24c4baabdd59894d40bf40f217f008d0deea5a3a077b728fa1a4fde4af155a986e0faf288b2bb62d77fa501b3b2060b0f8d998384efe010407593b4ece6760b43a3514def765321539e6e94ea79df3271ffd89f9634db06f6638f2f0571a5e1afd8ef4dbe6fe4fff6efef8503379ce3a3dd2df7c4efb2e0e10ce2c20d179a4adf3a66cb0a65b141aef7430739ccc4e726e748d0cbb1964196c98fb95b8c0552b55b5b060227f91375712781c9832cb930f9225c1af92153a80e61d912c191ee1947802a4354f1c4963613a93fad655c838d11091a59ec2fe8f098ba8358e9d6211236d779f6a78bc87f9ed572d3c5defc7190b81feadb4e2e3b7b7aa3e79bf8bcb1ff9b7d3d14d9aa5d65aa1e1fba9774de01de244a0e2ff99eec2c85eed6775a3b543e716ee51a54bba5dcfc001266f6e2540c04a5847ac31915944c1449b854bb25e197dc2de5fac2922e8042bd4d58dc26f5f5bdcda64c18e276d76791ec063e15eae5ae856ab7380198e7f551bb0509548b4a26813cfa0a3939985829515f70991b6acea419016a4d580eeaa6369e5666f6096349f66db83d879cf96293918e9f79058f9182033b143ce21cef8e7bd0b0b6c67937e944b07e5d5eafb129774832c49a64c5699c2b501f7e9aa9c81c00dfe5ba4d43a8cfb356801fbcdfd21a171476517ae10d8443bd9db785bc39f1a1bac62217093012582cf1b256c18398bb405617967a56f181a8c0d148cfbaf274601ab45f6547a1e8b1c68318cd0f4b4c036c4f0b6b9041971562a5ff151acf301b89a664f851b47ea896661be994076b6371258e015202c8717c74ffec2c86af482a8b861ee64bb9fe5ba5ed4d98267e021397bd95ff0b418e199e98695a1143d1726cb786f3c1682490f0b839a6af519da86d0126be97c0397cfb712123353ae6406adbb57c9cd39576c66ef30766fe4b4c381902c89a8fe631a0193247b98d22470878ebdbd867cba4cbc8e1b41f11089c40c2079ca8bace9abd176d04ef0e766f18c2adc5f47aae1ed4a8ebbe6ba90eb2eea8cfbf71e6f438ba1bc7d8b8cf8338c2e83862906e28585538f3eb2bb087ed883e20f02f20d0dd1c0c72cfd5ba23215c7faee64a22d02369a03ff9b714ce538a515859f52ac0bfc67093a20e3cb6ad879220a5ae942e6260ce52c83bd7822681c84a016c0f319af889b6452df83024e6a1dd7c842c2f837410c0c7f54892694dcbd3c1509d08a859d131be3b7aef4ac54e59030ac57f0ce7bb267bf0d184cd7cb61ebb742e6cdb292a7f24d6bcfe5faf9d3a0df6ced2106678b868fa9670d208d9d4733d2e6dd9bbc45ea4ac7f164a5e106d893d17f86342d6eafe875e3014f84ab03c2e6f00e75992a0fbc1bd171895cabb112bdf31e2a132edc138123f1bba1ecad2f9717a16fd18cbd457dd2d20d3bb7b66ba892891097dcd90f2883b6e089738db10a2467418ad9a9704da1b71ce957eb416c70746542c9655d026dd0080619ec447ea59f4254d5ad035b8f759ce9b8ccd1d6ce383b15fc8030199e823490d7b6a21e4bb122a6b06cd395467b32c6074adfc904df5ac38c75d56cbcf76eb1eeb9d4190af7b9479480561a525ad30a60493afa1865d609218fa665da3be00e58d08e59d3e4819e7f627b2fe6ef832776b1a91cb2475a0d2350aa36f35d42c8d36f5b9149894041f10e769cec50bcd070dd7e76640b280749ecde97645b736cd2b3849d4aae72a05d261296963cc16e6a81b00328891d77b8bf5cb63abf20a5de34e81c59388d27deb94f2818ef22524d324f5f0b947b7bfee0fcf17e3124a58e2794d83277ff2c5cbc0a49595837133c2ebea9e4b92474ace5785b4c2326114b27f4712244a44674cb8e932a0b7cf7b11a10fac62eaa1a636e13a3f9876c76fce9704d320731b5908e6e8237102268d9f93a66b52af022c181340407f15a065fffcc20288bbc82a9ba71cc099bd9cad45643b494e5a01562805122f116486b9648b611ed644edf66c97e88117739c95217119d20b4b7f14def1715010aa3d941d85fdeec9860940513b6c38b7c70f449155dc8469c59444647a288156b8b53e369358ba1d71a743d86c77e6ea9dc415a97ecfa0a0c57b03314054bb97d193c9876edd318de8574a30e0f16638d318a28a1e555c22071fe9964b8f15a9936d3044437dc2cbf672d1a2e988bdb2ed6a512cb62a47a96d2a3b96c6e28d43f284b0c842d110ab3c43e0199be6a6a2e1b23a0d3009c1566f59e8e59bc52dd01923c76d485df09b148997349e54f4096cfe3654b021401d447c96b2e5762a74d1c6dcb28534b22e719667e3a2028b3e5bd7b1a567691c4643550eb9f66d6912099b1a6b74981303b3492f8a4d6d89a0d12e7714f7bc2d8d61da7d3549dc90ecc093665a8faafa56e41b63ba69f8937709810ac10199297e49d2523b42204523cf0e51283f1727a530c2a2c68ece0f7f39481f3a6fcd376daa1e4f2347522687997af12bcc90f846c4583882d6f0bdc3669031858028403859012443139f0cf3747d776dff0100dc2daa110ed4c9a21e0196eb20a0c75416d238c35beb42ece6a24fd2e6d5eb809dc8eac25e694d874d642a67347057669a19c12875420574ec3e00260444a70445741cbff49a6d8a7e107c73b1335cce83241388953773f8d69a30f075d312f6a994c97ce5da9bcc8b88982244af500344cb0b726925258109d78da0744d0b43d2344030c7e8b18959e305eaa893ff1bdae1042d4857adab6d2021e95260ca3769981c4f464ac3619e594139e095a9e088f73cc4229f950f668492a83c3279e59a381008811244c4b66b9833aa4b17b73ead8ffd36aa7bfa1b74e195576fa69f413cb70ae00b3913f1d29104f54873ecfa41ed0a41c1d3443abd3f02af50c4978b5bf0992e9755dcc59b0b7c46a39d1db6a7d617d5efd7f76533aa28a0a87e6d1450c3963bae1bae4d64c408af0b432dab0c62ee204447959d294504556ad14258fb82f0ebeeae0e1111ac2d8b3642e6c05c31c6258b0747f13b390aa23f4418c5c27c711679c8711c00577de477ddf460c343557bceecf56a4869e2d8014460b8245a43c7b7bf702125fb95c6f38ff38b7562e8f4f3172d1247f6604ea6242eaed3258afdf6bb8d581eb1324179b2fa00eef5f453665f6a40209496fb845eebfa2b18b38879ffdfa8f104d1a906625b8eb2a91072e6ef5d07d4187e2272b3b2b6dddd4e3755a038b489cf2960e5158af1c394caab9118d7da5ccbf4a1049aa12fb4582056229ae6c9f36df7c86fe447f6a4cca632d38e1e3e83c5c589c0775dbc3a8fe3c07f4060af27f94de839041c186f6266a000003ad84f8778810a42649c13fde33caa10e76884dea90dace60d5ffbdbd6cd7a696969c90e000c1a0bcb0a489020419db1d5679dd8a9eca2cb54866f648bf0915c18c9cb175b7e486418db7bf68145ecb9f8e2a8e2d7e343d5d987c52a0d44194b6d9449f946d2aa480317ce2a12c6f91867f8e58bfe900340d5f554ceebeb7f39bb4e4c5dc7b092926c7b8b95a4bc969e4c1f2f666cd9d2b4c0c01c02b67af92cbed2cf4baf92f4b5d6cf9b4d5debe5cd8939e00540d50b1236f847b10fdca388bc5a400f99400f17200263b0c7fba0236a812e513569105973485d7374639b3108d915d37951318629c460f6c2aedb4b8453974ad57285cdf5df02635ad22b2b9c873a378fbe197630b3801c86c896d8cac0c51699605406ef29ef79d2291f1d83198c2ec6511dddc978a44b4d7a843abbd6ea81d22987c7aecc69415c50933036ba299f81085bfc0c7de92f48d8a6ca3311538feede43ccb41bcd46e529388fae824fef5abb3508ecfa19b4d84c580e0d8031ef777ddbd1ad563352099a892e34bba01918039b5eaf42e6afbbecda4738bf5679bdb99e44537026d2295779d5849448a3bba75c9c112662eafa8d74f35eaf72479f89db25624a550c24bbe628a5946237c363375d1c78ed51bbd1b255171b3d0e7cca337ce92aa7a3d367a5a7a8dcd7231a47a347ad6644ba279d0a99278dee28b330667439baab236ca3c711bce9065ec3eab59bb2d19e72ed29c752ee76cc69254cbaf935e9a33fad46e52937ab6020cec190ee1ccce829d84b7723e1940de9d94978f46c84eb67a276332cbad775a67ed9f43e94ed00a37fad631485cd3de565f4666cde955eaad9e1844d70ba103b34a763323bb571e9ec18cca2c83b98d153a494f6f80d3ba84a91f08daa4927610755239c3dc339b41a27e77a9e21385ddbe1844fe7d9a139611ed34b18e75cae4550e57ebad2ab24e5a6145cc4e93c43f0ece0d3b1979d4aa7d3e9743a9d4ea7d38906a7884fa552a9542a954aa652a9542a954a25d3e9743a9d4ea793cae9743a9d4ea753e9743a9d4ea7d3e9439c524ea7d3e9743a9d4a2a7773a5eb48616045d4f3a0bce8772a6badb5d65a6bad143b6743765df47b65e1c2068bc841c3234495524a29a5a43c223802105ce0032964e1053f34f4f33c43f07ce049164cb6f082224ce1051af9212ae689afb5d65aeb89f04153318fbc115220821327b238430b1a787a5d9e0b8010c61152e001138aa0717952184182911874e1c6145a340f6648b00d357f5d171bda65d44a4f0de3e460cff641c45622ef5a648278217668e2e50d41bc39c3b6bec618cfb34313310f7dc53807bbcd76e80bf350324f272e229e475ec678c913cf13cfc383a2434c3865707951ee09607c7144002c80470c31a187465e478512d060309b34891870c1a299f2a6789842382a948066e2ed32630b832544834d12ac61c60f8dbc3a9ad060f4b1ceb0e2098dbc2b3aa0c1aa1bc21631f8a191d7c4031accda9006144d68b02b23001334584283890c60840a1f1a791d0ddc82ec22e479a69c8f2e56b51551cf432b9d527edae8f268002fb1fb93ce3cfaa56c5015dd4bc9a02a62c90390108c49bdc3bb96413088043dfd1e0f404250f5042c1e730f0b52226ba873f04eda885d34b3076f82f4daec49306c706ef3da7dac2b6ad71d26332c4889761dc3e84932d8b2bc754def6c32c998734e693fab06852d4f343a7ea20163aa13a8ea3c9d900155b1c924633a994da02ade075b9e42b30b28a361231a340084da75da67a084314e4203c027f291d12ecab776257429cda11d606e2a31cd39599cabd45a9cb1b14197771607b1a18e77b6cb42066d34065f74508e6c64cbebb298c248c3cf808227bff0220c2fa2f086105250450e80f04518c260038c2fc048e30649e841167e5cc08413be70b2050c844cf084282678c2462b88179c7801cf099cc0841cb4be908420196401544517d81002184940c20e0d9e388941194e40c1144690e08514710b2bc878d7eb556945a2247281848a57bd38e381e11305266c6109475690059ad905155c1598644109464c7146133d4d5ab66bad400cc9c64b10e083f70809b1ccf1c6069dcd724e9d202c480916448bb44832d8dce1100d1f25110fbb0dee401506c4dc63428af20f477eca48b9a45fbf6e2101d49462ec5a482069d7aeb1071d1cb140d613c308d81101732e92983823c618638c313e9fedcd26d80c725498f1c8733070ee603e392f65159040820926a080620633b002e27844523a8384e2d08b2fbef8e2b3a28af8e2ab22a6007bf1c5175f2c0293175f7cf14526b1e7bd161d7c497452cab7087d49c4820b1f787185b04565e902da76b56de328db3b8ccf0ba716989a606be9078b2336d8aeb5821ea8b0d60a6cf016556c97843790741645e7af50805d8ab8f2b3c198f7de7b5328b14115a2fc8a15f5076e37a8411357178c20c9271f154be493cf074b62845e5c6f04e1c5177d98bcf822132650bcf7ae80d1c5878a0f49840e660726c15c4b8e41ed2409d2166173e2848b2dc71ff85862f43b8c5a7849d04c64d5f6945a4ab948024ad3da94524a5b2be869ad5d6b052690620913212c59a28a254c7ef8707f2d28b3659603d9125514d1b39ddab58e4883045b8e76ad2396d0e4073896d874b46b1d1105185baa5deb8823113e075ddb57f36c946951f5aac70dedeae78cb1c72ac649a92a5e96a47a38a85e0d10ce581b804f2144cd9583f0d52660ae25c618a57cd2242995946e34c618a194324a09e393524a29a59492ca29a99c524a29a5943225b1237181b560f162242c42699d4a43f8f79b21743d72c4d12795a82a75fc848fb55aa1213c6ca904172bb677e79c7356cea037238c8931c61823bc7373cee943ce1e7435e79c2adaf26c9d73cee9361b6354cd29e79c536253ca299dfc4abaf8bcd311dfe06052c210b52b7b290e063bc6014d8475a0ed1f268a4b54b144154cc428a3fc99f1e7bd1fbbcd14777ae754e2c706306aa26cd456bb6ff3a1b1db67d73998eb58cc5c30d1455d584295c5760993254c2250768394f19a9eb66d13bbe95834d40d5b8db514b55189d5cd876a2bfdaa6e3e587bfdff6fc3021b5cb355d770b930f7986817895c2ccbe8ee2eeec773f39be8d9a576637f3d6a40765afb7573098876fb4cbbd374b0d73e03a1e8daf529bafd965d8772713fa0b58f5a8d48037259212c11827c01566bad22ad54b234d351842da96c24155366a3cc35fbf4b4f91cac13c8564faa4ef624dd91a6dd742065b357ad5ca6e8c2726439b49af81bdc8fbabac1fd806d37d40d2f3d1be61e1cd90a0495dd804d3a6d151dc184dadeb8641a0e094e2c115ec006dca2e709ad5648187c515b2af81992dbb6bd960a7c7e1843135c00e30c217cf10415345556a16b75f39a563363e4ad11553082082d9f6db5a7d60d094eba4802d36ae646ea9c5923749082282d2fdae8ec31c1135deb46b34081155270050a5095c78a5a6ba5938b1f8e8b22a228228d234d5a5ea266ad07e8baaab1d65aff2acfe85a6b6d3abd9051b5bc15a365eb08142d99a09c183161b109aa1e4aa8adb5d65a6b9d1089e002e4696badb5d65adb3a4113a6bac311acd72bec68a30a513aff0484273d496c3ada25e1c913377802a422b4a5da25e1c9182ced92608534d8c8dc0f99b91fb233f74374f76cddb197455f170210b280316636faf0b15aa9fa7297bbdc9545dbbf20f47441d2ba8c0af836d9441c0cfc0b0ab0e8eadd37d37bb8408e36b61e340055176863ab372e48b606a00ae20bdcb8a08e58d2e7e0e83998181ffc99497af0410994e00530a620c24fe582881e1ce104114c8e98492494324a29a54443005f202149122d48411657d862899137937ce00751a421060ba8006485231081a4e74d27464fa842108cc0a0091c2c518588286205701bc1125098c119342043091618810823b3065f5081856000821378c0840e946c81452d028f168ecc241b28028a9227a038410fbca844e039e332a1da403d210c28a8420a4f40030634528b963c5068f9e3411e2ca2a089d142f88239e79c730708356002c5185ff0f014413387d09307899eef39e79cd64208b769e77c63670927a070a18c263040a227073cac1f6fc623a80183286800041f0cc1092c08c10b221b7461056d2a91f27561640a4424e90207375090861016b67044145718f98205d3da678408cf08fb8023509c51022aa8f1c4106a1602184211a070a10443692c215e61a4072970e2ef46490cca98021a502c6982094c4637d89424e08d318a30e3491b2ae8420d3222c591f916242638438d1514810a2d8260248925e900d50359c344115588a28329442106409001ef4874a18f98aa8ed05b0bf14f6bebcb012ebac078a156032f92905231a08a4aaa7aaa1f85da286c423f039f5cb767628d76ed9a30a244bf7e4d34b9b2a11ea8a2a72d3a0455cf09ebc991a1079964e80397ac11259e3d43434e24946d43a14eff03216c88e5151248064927d327bad05bbb6d28d4904f9489d66e1b0ad58450135734e6a066335b447d4ca5676b2f8490fa2861b993165329a08a9e056f258ce7f4994b68ebc696e8c621a862b1a08f9125d6a6e4b30c822a9a836b275a401595912449775a8eb3c730092f82385b29a594524a295feba528a776bde8a6e2aae565c4d892563d3a69bb3cbcf22fad2dbe2ee5803dc4d8739d83998139b273b2a409297a748995a42e5938ee4e26151d3af00a46e11c38e29a61a20b4aabe92794d68f426d5bd6c25aa2d6d592cf92d503aa608eaf568eba34b2826f64a3b0f491ac887a8c28887afd02c696e3e6150eccfa09b34c38a354b0c592257d90b0499c4987cf32e8a5c32e7d26e6217af4ed6622af4706614c745589519f28e362a5b1561fea83895e2f718562c0d64bcbda6dc324928a049b34267d6a3abc1ebd456f6d8b0ec921ea438386a24ceaf4d327caa4306da952cf8fe1799b01cb6063c01bf691412f5f6c118a839141138a0c92413228684299502694d9c646c2f20a09045530ca0b8b4350f5c59602f2ba8c6d28650f1f218e43500555f4beb45af1ca2ba04a0241158d33f0c50a5ba6ad4c44d5ad1fdae407c65c0163808ec5cb868e41c51848747cb6baa1635031464f474c44d5b6e17d0163cb92155959d52da8a2b165b70df5d3167d4903129b427130f42f626c99fac8256c9245cf84194c88d14e2e617b4fc14331a08a3e4339a4e1168c710dc58031f414368932911597f8449947434f4f2914da01fac4255186465694a1ade8428fe4f408abe9af257643a15aaca67f4163a3495a3e43589e7abcb948c7223cb0e0db2c5f6be2a1b5f17abd3adacdf50ba76caac2242f616cf2b1fa9ca00d0bb007b51af9dc838752c611d56aacb32f7334f444411c5a5e0d538926f6beb6946acfecb5147bb63938efaef3d46ab26b74d26de0d98298a171eaafc666e6688abb4826054dcf71b7b91fa33b2e7a0e618543cf02f47c7130f33947bba39ccc8e93c341f56a98771912245dfabb34ddf4f284cdb5c438efa5e7cde6e1acf2f8d2a756737ac1c2e65a2eb8a2b36b29f1a4b36a088cc979511ac7df952bd781840c244048d0e8edda95704309347064272d086cfac3c8069e2de7f58d178828bfe1edf586b5bf679a0eda71de731cd3b2a705c9f177a769aee5b581677bade3dac0b38d9ee3e6edefe58c6e03cf06248eee6bd2d51e2feaf2be769a96326aabdd6cd7fe92856dfbfb4b16b60de368d72eb536fdd26cb4c715d35117c50255a6cb438e3b64bb1320dd1e46f7021870468cf04616902c29ddc1942d25e538704af4d3e5a174310cc36e10b1b1d1e8969422d9a8e00c5be7b5f6d2312d887c0907115f4a4bcacd46236d34c2b41aedd87d3dd248c7aed6a41129c33692a6d56c3845229d848d320cbb4e6ced3ab4b553ad06c3292c858d3ebaa88c85de0b40150f43265055f32d0f070bb81ae6a7aa636b661a7afeba39d7f34ebd40d4e7e0e4bc286d03cf966303cf763d621b78b69a06a29edede6cff4e6fd274b0c739654f0b22faa3da49d7069e2d88da398fd53979a7817030da2576edf6c2a61a906703cf56e31c8c869bf0a0d560f8e11d5e9cb005f19266e39a34c5f587da15a7e8ed757bb5beae046648fda4f1006952c77064f66a2dfa0c4c5d175d52d7553c1a3b65f662ed341d74ecdfdd25c52b36c048f0bd47e4f5386adf7b1778475633f6a031461a532f42083175f91d81ef214fe56c94f3bdf7de7b6fbe98531bae20c4a9d703aadced1ba900732dac0821c4f3bd236f0894f7c284ed5de57ec0c776d9a5df9dbb734310bb2c98a04517a05d164db068cc61328310ce534a2785134a6b53f3f06e62f99958af74d3cd4478b34e6c688bf8a081eea1694e918eb5f34ee7d48e973705dd9f13a84a417c2359edfecc802ad77365e14374496208663046f6e0c162b38d3e1da882458460f5e0b1b5d868e419a1848e6c07e483047a60b15824a011a207168b958a485211bb24384a40302f676afa703ce1bb83aa09af73db0ffb90c4e6831baab3d550cb8e91feb4e867af503d26a3f7a4d89edf92244f8967041a9b19f619c9618dd27befd8bb1b5a22d4eff9bc306193b88883799f81f5a75fbf255a74dbd3aedd12aca1967f2d310d88caaf3f20e9849abc5cf5fc8c340df243dc8f00dce5d999d102b83c0f3b3c3578b45cf57071316fe88731bf7c0b8c99e1f5f2165ede4b9cdf152f656c3248916cf3965150ea4d551a6e4afe35eceb66555f12aaaeeba91da7e142eca02100371580d354fd7f2f6f4afee52ec79c445600ee8d6c0a03f096a0a6e1005003220191702a085bae034301c057b00c180addc8fee5bd91fd724c74a516413e5208094133220baa5a4bb47a47bb56125a3446ca524ed26c66975ee4bd9f9ec792965aa0cf4001bc5c24ba530ba1b86da2190455528844d3a78aea14593ca5b84a603e7d2efa0e931cea210905c6c03e06a383f633103ac9366f3a9e512bcf1646218a5774dc45998de52eca581906804ac144e91387a6cf713cdbc6f1bc358e6754e3787ee358e15903b603d881f3b6c1e0098587c89e5e8894a5d3e7e5caa02ba5b8528b2b7d7e4ea701e7ad4f0f00feb1f4f24248fc9040950a064596b760190e009c6de37b6517573e9168e4384b8ecb70c578ba0c38db18300b4e611d18f5158cba628e9b65eb744d37dbbee8556e26a26a792a7a15dd69d6a61e9184072d3f5fcf4d3d220950cb6358f62c0dfc10ed64191b0f7b7b0955f22911fe0c9a3e4bf868998331b51d1c69015de0a714d005be8b86c762e80a2653c8c134d9b2f49152c0181f18e32292282387a20bfc634f1c8a32d267480e5df5a25fb25550256dc471238d542a95ea511d5e4b3c671054f9c820290455f0191a5d40177869068a8c86528c866f63cbd3a7a1cff4b19812d6c61e9115c51e2530775a8de5f18aa312b127cac060e963371f1eb1a707c62801654ea58f74d2ef6e064223bad4ae054e9ef6e60b84f9a2d5d8cbde180ddd8b640983ce8e8dcef1c9186bf40fb2d7884f94b039694da1283324ba40685b5b13949309855e28c8c76adb50a80bbdb762c5ff2d61b57a06be84b164be36b92e7a185b840255f092258d4823640f92244ba0aa88fb2149a30c13b5a878b8919661a267d4338bad53ba63eea56e62bfc30885641aa13496ec588b4875f1b0393ea15234bc5cd29249c735b6e806cac88d904a409988067481af88fb21e393880654c1b8460a360755f10954c1172774f4b21d6050155d74d1bd7cb1cd2ae5759dd835259f574ae8295622fac43da00ab25847e44dcf6402c3de734e4ae1002beb32103bfd9e1d273bceef22d838f3aeb13c444fd8f63575d88a5e2f4e4e10af87682cc341bc862dea1e6acc79382fa454427b9d78bd6225da9f5643e7219650751b5fccb0120651359cac56abd56a05b4eae1b15643dc0f1c7b98821fd00a08a8070828e2d4c3ceabd58156404db00002c20268b5025a01ad8080562d69e06a02ad7a4581269004025aedd063b55af5c01e7bac5651a6842f57d646fb4c04b36bb18b44bfc16e8fc95b9c12457964b55a39e7a4879593d50e3d56ab1eab1e9cac9cac9cac56d689939f56125758fb9ae2d47b3d266f754ef48b5e27f6fc4c14dd7cd9f0a9977a18ebc47e4fd1679b9297f8e5c9e6681e4e517c8355666cd9b692b8e2a9d48dfcab02e65af26ba9e261bf23ef012579489044991246a1e20b9234d007b4633c62980fd9842d5e3e657ffd00162b49b992d27dd821a5a75c74739116fdbab9080f25d1b1a75ccf445727e6229dcaae019dd82251ea7ac4a9eb50f544d72ffc88401544f202d7e59f2604bbc479a405a937fdbaa6a75c1cec2927dd8c9df452e92a37b572d7396e6fafeb948ea76eea06bb8e63d781532b18c84eab3c7583fa76d4b76f34479cb321c7d3b37c065e475d95e7b82ad74f1727e5aeaf9b70c67ea9dc126e81aa79968b737dcbd7e945d7078e86310c48fd6bc2e65a3538d2f556ca2945a2d30c88384540bc1c665a2463bd3fe8649da3807e0ee6ba0eed1c0534bc3933b40f1d7b9039381e1393188837af9be12a0b2ee82a428a43c046bab5f51785b28e24891463e48161d9ad177d0e06bbf43aeaaf63f6c2ee348bd1b6b2eb8dd9858d5d59b3ec3e25b2ed9ae88e24b62c9d8389dfd1822db3d8d3d76aafa8136f1c6c9119a43eb84cafafeb05ca7699a08a458d2d9b7e3dfe5d9fc0101618f31270e434f0f11626003e7e93f733ef34c5521280d7fe00780defdcfcaecf5b775a745d93f9299160cedd19634bc76a614b4f0955ce478f39a7291fe628209350cae7e668b620c618638c2f4241674e18638cd147bf97a43842123ee2832fc618231154bfc7f85ec6a0420944500d71eae6e15746ec42ebc4fe7cac27c06350c6bb86f24f9367b158fdee7466478912055ed94e7b4bdef4b11f9a29f459bdfba13dc618638c38575c9fa605c15ea34c6328d36a2a8d2fd60baf63250580fb96006096524a2f47a2d45afc474918c7fe223d1a849070867d9d86cba10e00ce35385131010038ff8b23ac601db8d68ae5faaacdb4668fb0fe4558ffa274a333bc0ec4217d88ae37c78e9732beca353ac347fa283bbcfe8eb31c5ab9dc9594e7deaa29c6a9bfb00eece844c5847e6fbe996fe86743bfe7d5bbd36eea49d9aee870bd479c5f63ac03db3e1803ce56069cb7197046cdf05f7ccf82730df115679bf82a24bebe4b341e0ef50fd35a33cb6146c2a9277da409c96286fac5c733e018700ad7c3f7940e161c23ce926acf5e44c5e788c7718a37d5a7b0caab10d36a329faed5d65aedadf522c12ad1b8f13351c7d5be72b78f6e86ba392e8e7bba2a178bd8c7dce6243dd7d0b3a6682935a8fe87df7befbd12f6529ec538a2d352f6b10509c52c8b19ae447af4f54ca44776e1ec5746717e19b63425ebc84ec1a9cb87386ba841a5fa51a86d1ba2af447ab47d26d2a349cf2ce9ef5976f68d5e0b628f43c2f9554d48d676092e71aecc8933911e4deb651a0f074baf65d9a95693615734a94824ba4874592cd3783554d9ae65e2f713503183245408f5aa5d8b0a17f40c8c58c2fae9aceb27943640bb5615415d454fabdab5a87852854f633dbd517a49c744daab37a29edaacaeebe57b7f2f5e5733b1c3e6bd8e611367af2fd6381fab0ecc2ecee843f4ac77b2dfb3fa0cbb385562f670b08f4a42329c618f5ec219365652c15907c688d517310ef6d1f3c35b4fba23c410457ad63c829ef925e94c1f2f4ef621fadd1c1b1b32ff94c8899d9fd48464d8d95368c49e45af15e7d75331ac037b8475608b70adaf669fa3f78a53a3c7531de61fce367f1f0263e2ebdf9187c37bc5d9cb269216a4fe619cf7eca31a4fb309ebc0ae5789b33e159c1d1941e671aa90ac27ce4060418d61b347d735ed77df6fbf6741e6afd3fb0eefe925fa942b722492ebeb3dd29b7fa43ba87a3ab031fb397176644c9dd8189e3ad4673af1914838553f3a9da787fdb263239c75a2e8279da85d981d1941de71329ca72624ebf7307691e80f050f871a11eee96bd2ae19046dedadc516aade6ba7d9605df143c1ab81e221dece064b5460ae25cf1fb8240b23f4b4cb82085a3827edb228c24fc31ff723b21ccc7bfc892e536cef130c507c583bacf1c617ba60d10c2d1915018c2d7a58342f88c5a2798fe6c52fc400430b16cdfb6b459ae7b26d92234b0351263e89ef1de941f27c222b2ef969f22e9db060cc9128f372b004254d68de63cf9328f3583322914f2290174e1e2b47d6eb7150dc50e7c89a3f53805acdcbd09417b5c3f2898309723013c6480cebe96610845a8d7330eec151c4d81107031f8c72d22964b108eb99420b2d25432d25463ab6a670a3638cd3612640ecb40766d076781a0376ee2e10eff03c843647e368f2a60dd140093df0410f0b471e08893d3043bfbfc72022a608eafaf9b4d71c043bceaf539e3de74501723de745d12e0e9b67a3f2d1a12644e5a3e3b859fb083bf70308e760f2e8a59b701efd3445ab553e3265edeea61bc4b5caa126e45dc3a56ba18afe90fd071ddb29d87fd0d97ac33aa826e11e6a6cf3f2a35ab59a511dddd71903766e44b0491260f76dab234bacdd36157c67a2e966d40a4edd6432994c2693c964fa4c5cb9ae53a99b50a9cfc0959cdaa9a36eca944ae194093f23864419c8b276dba4bc9442b08d26b28ced88ca4d972af85015b35bc2b61d715a38a13b24ba4819a28c0f29ef1502aaa056633a16c37d7d2f9462884549f89e3af6948aca8acaca0c2a4fcd709500e018b28ebc1e18039bc57590d82cb80854c9cb70874095bc0f89da3619fe341d64c7d03ffc0024b21c889d269b8497af69387d0b0068c0ef2700b8050300a36ec2d9b669055fe5d8caad57b9efc749f9644a65e5a6547ebdaa5cf6ca532a2b40503867d771d353d78ec52c7551299c5fa78ebae9aadc5ca455be72b3ed95151d57b9d957ee53a6ff4095e9eab8d36c503865ba6c95a7307e2da8d2896dba8e9b4a7d45a54645c58453297c95e7f7b372b796ca3599ae12fc2dca40564a0e1cd7b2bfa38ea95ce80355f228b972d38ac9f4b74cada470ca741d5809009e32e11bd92a57c14af051181a81ac2803004c9f03e70d077ec31f168c912669ba092bc15779259ced26af6d0555f2450af93fc8f7233f9bc01f19208e3330f7b2d8e4b3cd328a261d37803918859054bd9e10c279da36cad8973336788853f04d349cdf36caac6e871421694fc56738557b40558438452aa2dd2150d58a42a8e3b33facde1da276a8414822e114fc082b49814ab66731050f892ef1a8269bddb44a9fc562a7ab3cb594d214ad975a5aa356f32e9cdfa534452d4ed1533a2f29a50fe3541d1ec4a094d74919af79bdcaebd6779892d784549c92975d7f4959e994b71207111b07fec51e88de82c248bb96cf1a2d1fa1262d28802eb2b69f26fa9c50d8e8aa13bbf65063bb2610fb9c17254a94283ab1a7b42f880ce41dd73350084dd14f2a7bb6c42730983ded61ecd2dbebec9d84297bf7947d3db5972d3a661d144ba050220640bdd2ae15835fd86fb66d2f6fd681ef98944113070d206f139fbd39a509697b516aba5f3735ebbc9d38832a2a6c3307718d43244ecc81f8e7ec240206663ce146bb5292264eb49e40a371bbd61342d87b59f639b3939e32afbd482265d9356d5e9d78937dda64d933fc94360327e998bc339523d2ae133f9a4a4a774a52b0783d01d4f3f40542499e34163317658ea4e4e3274ec9f80285edc967f2da67d7dec9cb2b95a47c5e2525550a4aa5baee3aabaee866a913dbe21cc475c5390a0882b6c4f23ab4656ab6aa110763648e76323b38b1c4b9b89f0cc814c4d2ce09a116fa8384aa203247439c39982780762d2766d039a55f6f0ee65d07986bcce97899c2a61303e61e9fc3f03949066a8331442691d7231f4edd3c89755e47ec5e19d8df17c03052d46a20561d849ac4f27d0756e72e937ace64589e4d3231c8c8f0dc320396e18f018562d9280fda049496144f1aa65d2b8a274e2069ecd189235395831850354b10e6f86a2b9d32c29a53a465adb4d504d329237cee87abaee6d486540c15cd01aae87940d5d4d1438d0d0b823de2a8ddcc4b9cbac1c1ae99558ea3f2d2e361c4593ec3f210f5ddd6ec22d7b1a48283788d7d06ca8cd9c0c6306d0255f4d9a5622861b3d9cb32dc3ca08a069a86af8b4404c86cd0b64b18534f22d926dda4d590ecfdafeb1d34c8a91a10f7468b8e1120c3a08c3146e75a46335cd732dc6671ade3ba5e719de3bac6e14e373f349ade7473ecfc847e78a39bb34eb9ae371863ff9e89ee6b94fb8172302a59767770b1b9d612dce80cdb689173428b4eba26d915bbf93a96baeac334d1b13f2dab4f89ea7befb2b1bfaed798bdd783d85f43ed264763387fa045b8fec212b654b0adc05c4b86ce88255b7e46748cf1a93484f22f2573228422794c74494a61a539d1065ede07aa648e64b192689df7d751bbc9d10fe70f48d681b0c1c7c8ca1bf4e1c1dc5554e802e6acf377f460cb3646333ac630dca865848184be92ae4b0963b22b5e87bfb0186874fcc888784c8e91a4b5e708e308184d9a8c8e87a4d7560c3762bc133b66b3a96936f394be6234d36cea29ce152b41d18c3ed36eaeae38ef002325d59348a40b938e592dc8f669bb6a3538727c069e6c725c3b1151cb67b7c9b012d4e5536e96b21175f69467c7b420ae951c390d0a0f21a2a1813ca6dd5c9de1bcc3619436d7754c94892ea601797d3d47a0530e7f69589672a7d9c4a788349b948f7e6937da239cb738a2c7bfeda6432dc876a3c51c18c7cd298d1371e63bd026524a93bedddc81c6b99ef280efc0891a28610a3f28820f8d732e2aa79acdcd22959ad29d76337af61948f3759c78fbd1cdd7ed556e26e17c1dd60b873e06e1ea7c1dcb325f27dd6937f96ad16386457726a65cde0de3cc7fa02f9cf8149caf6943af1d4bb9d7b1a7a95cbbf3d7c5849a3e75739dfec299f4790ad4d8b7250d0692c67e1d9b5453c9d771483bd767f61a955fb774ede294aed97cbd9e6a42e8f305e4faf64c03723dcb6eea86f4eba45f38a5f294203b7d3def744ae5291bd24915e7f9d15140761a754b77231d3b09af7c08a178621c0dcfd742f408aedb3b445f3b2d3520d7a308cbaa106d4fca46903343037682d0d8106d45f8ea3ae7e7ea6561bbdab5c258d211978880b9df1d5f00b56bb5c8689976ad9614edb49a77c5d66a19d1d9a222744f89886d4decf7841d38882fbe2f76e060cb5b103334103b30093ff38f5229a994d339e9e8740e4239674e7ccf4518618592dea5e884ce1d42483521f2b46259e6dce7a4873745a79b333ed7d465639dce51eaea81d8e94921bd91b821760e067e62f7b015e4f76a9e76130fa9009d73ce39e79c736e0bcc3d1c5e3b8845c8057c38e447af233b8beea2f40312fb3aa4f633ba78ed9d68de622a6f6f314e4b1833678e9f120ad1f13455aff9949d534609e7d38220c108f8092ff1cef56a0bb6186a92764dbb2e91e8128944a24b04b52014622dfabb44221186c9ec61f83932600dd1ca6c8aa896b5e38cb22cbb62cb0e8f338212c6d4bc4929bd70c38b96ec6da8736a07514a5121a574bc0853c94437b399765f6fee8776edc20eea20688a7663a7044137271d4456723f7e78acce51406b8fda81d809c20fabb563a751c763550be2b460b176b0e86175dec182c56a20e20fab359c258bd53f08b15a5e47d462b56613a1b03a274689d2d905b15ae2d2abe1d560832eb408b64c713026a88a5664a2bbe367cb1b8c3175bc2eed46f42ba54597f9a59e7689b49b656b97bf6e0a1b6529aa5dd793971469371ab6412210abe50fab25ce895058ad61d1ab21da5743bc0db6cdc99e9e2cf3f13961d9493b9d4ea313e9743a6da7d229e5a4c2c32853a03b63f4242e58741b91b2ec66521bb5e899949a26354d8eb4ec224cab79686c5ad67e65d7afebb2234c864cb6e8754aaad9d04b9cb58f5e47c74677470e36eda36bc7196937f4a447cd066731f2a4163d6a35a2eb5a4eec0b6721fa3a76b3cca3e7ec993ebf8f328a3df53029af9b23444bd273624b1210dc23327af4ac5d8e8e8d70d6b01459edb2c545ab479a3c863bdbd6def5011663c4291bfbe0912d9ccccf3713affa4c7f6120a28e0e068828553110513f07f3449d1bf66bd733314788867f1d637ced3488611298abceb423073bae80ad230cfdaee5eec002de20e30e1785783b2fee3896957b64401cac118d903e930905aa42d6c975c51aa223f75df192b8160ebab892c9830274d182723484aa9193326eb07107a93e61cb811b94a7e16d4a06a10e90b4ad6434bc75972d61676073e6bcd39c0a88769038bf08c43d7e2606a1a78e1d18b483424fec1c4c970007422d1d8f79f778238e774224ce423410f79e8151cb2962a4042460b5732fe21c213ac7cf1793b87e54e943c6478d5c8f5447f67b662fac0365539900d1ef8e27a08d2e6f070bb61eec7b37c196ddd0456402f026541bdb565fb17df1c2e6fad12877304fc6045ddedd0e24b64ddab9fd3161e3b157ab68065acc5a6b31767314509fdd4c04c94b0255f4bd676f76d6fdc1f719089934b43b9cd8ec77f86ceef92141122dee61f1f5bac97e74c7130f66f8c9b650de2880078ca1421cde42551686cd459787418cc75139d4d3c79dba9737b634e4d04462480f9327b3a75fd0903969cf9c53f648d9337bdc4e4329a3504a295db9a6d9a41367694538675b86b3a9294b531e4d6feed1f42f6b6c19d57905af5f9f5826baeb1e2debd3a8a59158f28009943c6f0f3924d39068508903a01b30692887a41c822fefc0fa640e7ade6c227befbd1546c1b0ed105731fae8f99a58ad56ab15b518cedb8631bd832b1a7d94a438020b3ae294ec88239e40948da5a609ec4d343667a10aca0763e8f5ec03221f3f2f8ec4d004b75625c4bc2d2f6e3c7a4d79e59a05aa5050b56389ed3d23dc2049cb6330ef36eec7c8fd78fde8c5a93174e6cddb632a979658608cede9f9d40dfdbce9b3747b337db61fd53b072333cdf4a73f4d88b5c2a7b1d3d2e9ddce42bafd83c954c83c4d390b76fb472f2f6faa74156afae9669bbac14c38557aea4645bb8623e5a50b64a753308ebb5d1c12c6fae10cbfdbccfd7899fb01c46b9ab291b2633bcd66634156c35a6f14e243577b1ddad70fd775680f5d3f03ebaf9ba3807e1a0376ba62e778441caae530d3cef1c8f45103426fa8cc1cccbb7c97aec6c6c1bc0ff1764cd02221af0cc3eeebecf55d594a99331de982a402b007bd184d39927213498947a367972da311698ee61ccd3b98f7ccb6686a35d76b3c91968e8f05680b6674fc23d272c302b48529741cbda8851b18d1f0703b69a765479116634c8c98747ba8b1917e3d4ed2ef34d296836b129624b95dadc652f8aa331bce2967b03ba417cfc038d0d238f5d8e9c528cd2d4def30118661d7755d38c7451189e867a2c44e27075a1a877eca3baca55ba0dd01838d3eabae430c7bde5a24822ed8457f5acd756d3fc80b677762fbd8d86102ab82f0999d4d5b3de9e5c0aa27c6915862fbc8b8623d07508de3e24587d7b2b1d9bb862221417000d515e3c8536cdb694024ce6013d8bb6bc1de9fe66060e660e0dd7d429b6bd50049d776ad9f1974b6ed7eb89476ad1f24eda0aaf5f3d30eaa8c60d2416094892ce569ece130677e8e7446758c4d3a1e6503289114a585f8cdbfac13b31d48b68c42b1dc492436106ad7aa01194d8076ad1a50d13b4c0b7740b1dd770963eae5cb173679b9c305db53e2217938bc9edf215abb6d28d4af52f97859a8c19296d702e6e056ddddf1c4066fab0e543df97ee0a2488a309c571c5875dea17144c7a9b717e1bcea7c817e4474fa15e9775474817777c7131bfc75120f4c33b5c3201ce12c7a466d8de19c75d6992de54a0e01732d311ede69c162f50f396e88c56a9d528bfe438e023a3a98ebf5f1420c75e373dcf8d295d7d4a4eb5337f255c76bcdcae3757d3ae6346b45a42c7f41e9fa4883278d5d1eab19d56a562e2f8ec73b3ae63420cec10809a8fea8946d3595412d95aa19090000007314002028100c088542b1603ca04892e87a14000b96a04c705a97685112c328858c21c418020c0100801190119969053fce25c4d67c696e42f064afcddd82630290084fe9f6fe1907fdc6de405449b7eac93309e0efcd5409c3bb086052a7502ef3c405de888854c4f1402053bd0fdd73983f31f8510895ed3b74d4d62063f0af7fb92638804a34ed455e8adad1f42e62f2509f83fb4ebafa34a145e534e6f630ccd0d50c454f561bf3c6be0272d22eb3a3a56719a2de225601ba184418326490ff2c5cd48412b5b81731314dbf194a0ff39ce45b89f6dffb3ac68b383dd040189937fb22709881d88bcd925561a040415286c72beac136199884c6169178fcc140bfe4f55e2e6eb08d222ca2bcd04de3ccd0a565075b107dbfa62f3be99fdd841bebd3ee37da03bd6e4af0d751ed09f24910b23c69dfb89983c4e122571fa0a4ff5cf74bc114ea1c2b1f5e41fb3020d515410495994b8da542796ce3cf26e0f4cc9d89d0e30326d2d5907a3d5e447a2ff8729a5c728d8c932666e214acb20871b250f3c85f6a603dc8ec8e1142de291f17f2d73db1d63de59c39b38761716a22092fec8f58d38e0874a73f5e6611923450320187eb4d2937181aaa7b68452410737ad446a8a498a865c1b39e6e549f63a33cc282ecd447a65afae6d229ae687493e77245983b936ea9279a1b6767cb7b0e09ca2001d153722a53ed17f7e80c967e66d377a219c6ccd3234967d2d9831cb6f06ff2d42c59699fef3faf8c60a1f954cc0cb8437f475c5db8a59c2f89e00f9e0fda5f646b3dd7ece111ce13741df070994bba98703b1deae87aa97e8a99abc4181a718dcb8dfa7a3e1af02b2d632d0707304b1b899682c67cfa37e8c27a249a3ec5a862be4aa9f8251efcfda6e03532f8385b9d570f673f596ab2e769213a6b334ce3ccae710603637a20003613ce4735657139049cf61e69fedf067d387341534b72dc3414cb3392182b4859667a213297277a293856423eae55378512a1417ff6d79206272bb9153ba96c2d6c9078cd9157653025f1a86941cf08707d9743067a2484a4ddb7fb526940311eea8e90dcb74828264e016ec2c173ae4af42930768e113b5fbe0ebec912194a3b6bc0d6b18195aad101895050ea84d9670a73a49da37d53e33fe7222024617505f196a36618e341937546f7cd227b2910a8edbbdbdec3d705e9c3dd0245dcf8886b03330109e856d262bbf018eed402ac06ca58c1786bbc1f0f888487b5ef21baeb59914acec5e5ba80d0932a7c21403e2b2f3044e7cd01a81560eb1fc729d98faca61fcc68fe6b6f1b10389da2ec8d3f5e2de38d1b02dae8be3ed4d03c525f535656308a5465b7dd88ec9228380b1dad21161aecf31c8492d388d03e158d05accc23aba4167e55adeae94a6df3f672f40fef9b175be2eb6dce4863bd1e1e83044164cad974237140ffa5d85bfb31983742516481b8d0958b323fd80ba83a8e998b67462581870b3b557bb25a2576e557719499fef73a4ad4d773632ca0e71434d5c3790c5c4fc3b290b3ae9bf1889bd688b6e4c9660bf8d816872c61c59c0e5b13019f3a94618565bbb24ea691dee98b730ac085887ea57ab0a5096c476f0cb13b47c4b6895d355c810fde0212f065c024e640fd06f5c6aa160b8f8a8bcdcfa5692fd2a5315298bd474c37d612364f52bc936f1eac7273bf64aa414629f537456d1f5615e22d0e4889def116675da811f3a66e3535655ca8967f4b11aceb8a9d81ce9c7bdde2fb84974eb0a46ae0e68ec209814738b22e6211da0482abb8eeba221063ed1f589ddb57656956f2d59c53ebd690816b36732f412967f3796121fe19167da8560d92515cc4c6003343520dcf3fd88b53959c1d96b9740e88375e9ef38cad4eb1a600033d5d5474afad541d12d105b6602e7698c20ae443d57a995149c390c62aecae237ce5d448ed0028f0b5e53496f5376b8499840aef25eabe40fe99825c7156c706735d277500f7eb1daecb9aa34808a9b1c5cb206c805cdd311826b5a736d93f4a667015a898b662ebd149749e4f7b28dab34b33fcbca9d28cc8596d93db951633f6ea7505776699d7d88210c153a3a46859cd0dd795e00aa2905626c52fced99d797594772a9c4baefa10247e83073597f906357979c7283a4f2b777d98368f8bb9c8b3758c8810d75b3d31f49aaa683a4c6c7ac333c246513353b54a9965aba88a47f39daacc190b14e946fadfd18f1c0e2d722a805a8569c811e48dc2fc4b53a69fe7102eec9d74a409655f613c36438d4cde2a6d08c09d3c43d35bb1bdc81c065b6ad0cc657aada0d2887ea55964ae5a8786bf4eaa6d4400fd820fda2d4bf864f230de2f0e34ddbd49a57919f9e8b8a5e6c6084aff16c0768c1cde968705ac2cb8255a4ff9a2a6970c28917f11c6f22fb10e476a59cb2b5c349722c349f9d57320f797819bc53a5d68f3199df8627c02a46a59afeee0687ed27645e54a6de83719a6b18505b4ed16f2459f82f9e81958a6ba10f95ffe1c87103844892c2f0b403cdbf5772410a38426ecac0be8daf541e316d037dc5ab6a540bd61d0300e2e8741c10bc42db43c21b2546a3ab049e9ba81c246a29257759b12d1f284dbdf7dac4eec1a5ce267574b32ffebc05493ed0aa3532810751a5d730f8f50ac4d33b94c65268d1d5734ab5fc7f73999f20fcf6ab1978151e175766ed1c6de99f5a2a7afe00df11798b587a761748a9502f7b93ab5500563e9ba068df5c0c32f53cc32bd163da377b0829d1838fd53c5cf945058302d845ea0ce29550a9bdc66d24264c06a51ba47456c50e85d02403d2267a17ec9010a7ccbd68ff0793b91c715198d1f9114120066487c779945bd7377c7324aeb50e77362218034d55a087146d4f31369b24269e10e26531497dde49e6a97a0d3a767502d451a8822d4c867d4e2e31a6ba4b98e116de9e65e9448d2ff0904a4c3f8f6b5e6ebff5df3fd7df2fefdf2fefe79fa3d7996dfda07066683d505131491222195936b3127f5f830b3effb21cdeca2659cf9e69d1e8daae8fd74b66d8732cd4f84d6185860cd17b5156343617e5509ebebe98b039f9b7a15dc618d745c16e3621a31a0405c7e3c467095badf50f181f310270ba645622dc574afb7e6eed4f6771c5476624e4103809b304e2f4e636879f11a42d742c0e9dd4e6ad849a368ed8b9d3f2d5b5306a20605df450b1a479cb0d38e8893d75629b924afc343758fad90300ad8fa7bef7274522715f9c027e26811506ac5e751a5313e7a781c8ee692a8cba5af471b6e967ceeb72df7ce5527f99201255d5af246febdf7b505b16d2091cdb306162b7a2f897c5f17c389edda6308a2fc0e6f1830a95c11a5eae680a1c46a0d539b4cff584fff10b597789951cb5b86002317c0c53831d9d8f96f410b4ba8ea150319c37bcae5bf3c314bf6c25740b45ca697fd99295b6ab3e08190fc8c2a9362a1d28de336985a3e42a6b65102c4377321597b13ab33e214c47a8e0f83ff52f1e602d5aee8b3cf1219802d8c323aa6a45ec7f514f84a92f5c84d5b2a742bc90e8bef63b1d80786572ee6ea54fdb56f34b3efad13cc1d9f258f95ab13afb59e8e159926d191b455b6fcad4b1145f9e562f53527bcd3ca08597754fa5c6a0bfaaa08c868ec0dd24d04f2428b35797080a39724f31413b58891be3dd63a1ad0ef31181d2dac1818df623b56625258f7fd7fb2bebfaa4e6b1bd663f39965eaf523444fe8050323289080312dc5ce32b3f9a3a94f54a980e66037d573fe7e6fae42372ac0ee4a8970b75d96c2b0bfd2a2595ca8be35c64154150855e8350309ec537c474266687772066233bff7f151b5ac55d8f2f212b7c335a02923feb09c7fb5c4dc7416ceda71347dc9427fba27af6a5e0c7a18315e6b0985557a033ec061c6c747af6e0ba9055db9a678f22c5516ba919e6308416dfb95a466fd5701057f0894c1737a5ba772935d66082cb603db10138c6dcc2afd63d4cc04a27d087fba2a30ab94a2d9e040cc2ced5511378e76ba88a3f902a3d0b34dd792ce0f5405cc5df3207e4d4ffe85e7082d0a4dd690fb5ce55e57716e73b48486b0a024367a55bab9637daebd4aa291497decf0f4465eebc01ff94ce8ce0df4dc8295c98bce9e9f3e57f9e735003f295a1635eeeecc0694b65c5680068b6503920fb431c0e43b900605bf82edcc5fd9713dbd46b1df79852b1dd10ec2758a0af4095935627762f3ff272eb0273e709b40603fb1c03ee1c06c2202ff44efbf279c02bb4c34207829aff4ddf8aab72b14d6c773ed7b4f6839ef85e0d039d2a521d8c77c6a9cd09c09bbbc860d35d06b141dca20974c68822e3a67457a139ba2739829f1c6b1cdfcf63da596915ec19262a6617d982d234e70419ef5c26a0a5b04813fd4d31594e6b27c1244fa0cfa1ee2b06031610b8a8e672f7a7f5600d7a0a92119fdaa9f650bcb6e55652d34a5aadb8d55f4753ce84f5e442c231f37f06c729fadad008e7e705f10b0ed4d6ba1db935e498b80e4b42b3d1e48871b37a193645754227d777ac0f05a506cd18ba9b2b16cd4bbf9cdd5ecca9a45221e9d243832650a48362f4721afeefaaed2fdf9f28da151820bbf54847ec27637ac257c1e087d1d366eb28264bc707498548e6def1bd650a751ee7e7cae25f285563e853f6b87859503d228b6e20315dd254eb840a33bbb483b9d7b33b2b33af60ccc26715100e9aa005f754a41870aac9ba6d2d6d6c3ffe010dfc554d1c6a0cf706124a58156a52f27baed0ade1c7c4c18cf3a3a7d136f5b37fce544405a41451bfa248bce823330f7a2fdb5c4419b242a8053b96a1205719333034f6f92a3cb081a5a88a71d6f475474205257f124a3b4f89a3958e6e279b464eb23f80b3f4ce9f1da13bdc81661bf2ee3bffaea23162b9306c6900e917e07222011421cf8fdcfdfd5043caf7e1c98b4b29caebdbbce45b3a9140d9d24ab2348dc0b1870085b6f96a39d58379c454e00e54cc6676c0b053e7f4e9f00327185fe9064b26a23df490760e624aabe16a82668893b3db2c7e310c8921f02fd3226c55decc336b60b5379f83aca5a9145dccec3c1eb7189def9762dcea975f40adf9a21c79ba9a382e0acc9180b407ae07877a03cdb5d48715a1faba5a15cc16db4aab83c503eb238bd84514f89647824b79dcff8fb04e793784c8508f54f31ca4fb0f6bd2e3f12f75a97828a5c453a9f375078818e7c8261a4ccaab5a93cc9bc81cf9f84e3d554b30e60c4d73627446482f0a7f5ffda821b1d08c9d95a2397cf990aa3b6d739711583c09cad8a1c0ecc8f1fd2b555322d41ed80a716bab5a6410a07f2f84e5146075993b0d469bd69b7d6141574583d0b21d903613c2fff59bba33ea476bab62f1cc99edf477da5875559f5b0f4e86bf65e49638ee1c69a0c419362060fc3c72b89acce8e9c6b7cc5f80ca034c0de6b71efffb105dbeaf2bcd1cde4592dcafbe7787a3bbc8e8450dad94f7fc3f0854c535f967aa5974bbbf0f786c8a720aa2615c784fa21b9cd7585fe0097d71392859d99bd8c8f38de0d20d8b597cf95c9b91abed27d7653764323346d8bd4c71cb1c52f507c84de450a46bb30b1f474566d83c7e6224f74673f8b3d8aae64f019e2ec768e6ff6ec726db607ccc0f553c30d2ce58f1aa860b112e059d1a8846caa9184c21f3c0e4491aaac79c6f40a86c4392b11618a5f365b781229edd98c4a4e5bec22a401224ce70558da296ffb4f8420d67ee0fb327cbb84cc10630edc1f5c5e153fe888a47f471f2476c0fd030905c66e74a76de30f19d0bbedefff00bbaffc383de0f3ae9680919e31ae40e7ed172fefc2489c7ce07bf67fd9007bfbf45254ebed67992f71f3070925d84dc0e4a05287f3cb2c48b30899b9ea1b7d8c861370440a893cc00600b964514c8830a9bc7a4abcc29dd1149c1f4f78bbc2e0e5efeafefc4cdc5d6c2784377636cd93e8e73bdc537ada8bffe2679a1f6b552c8d9afc8273e307a631ff6a9356c8a24cbef119082c0c12accd42d760fe392fb42cd1782eaeacd042c803b95956c532a2ba6025838520a4d4dff7d235a406668a5e05d0ab548ad4835d6e9774e8709e9a8cd0c24f71f1f4c5a33e1a97b5c44c9abcd82a95093a4006b0105ffe430e47e28229cc2452e981b598c6426c6d7bbbda7c5fd1476b0f5ae602e2265835d2123c2b5449ac3bfb78c039a4b918a83ba808000ba1c3971644105c7f9113071b183a24aac97eb8189424b95d4f8359812b25d6dfff2b1a30bfe95a9ebc5ca3ed3fd4196759fc601a1f94f5d61b5bf4a4a36e6e0ebf64d12d8202de4946e8e7b968793c533fb7ffc0987fd3f5c4562e9595162ba0309d77c6f0617d14ac61a79d23bcdcaac193853050bef87a329ac9fc3976f8bc12836e51a3709b968dff14478ee6b7520d91612c56fe162755dd99cb699e7cad65a0504cba23164128bd93b32ad9881d7632a10ae0875166dcbd9ab2f6963432582d5f8abb3282c53dd71aa38874b8978e31b1fd2bfc7fb6aa00adf0118c62d573380e37ff9a07028d0352276e3899e70cf12567581230bd20c474ec6b062c33758a60f278d772ddf1608e17992b863d48fec2f3b6341dd553d43bdf2fa0481876f4a87f55f0ad03535c52a87906551b2a4f515ed39065ea031ce5f17ff820480dacc2e810c998a2a0363fec47979ee624544b17c29f398335aed828c122e5bec01e7c3968f6729810b3216305569d9c00679a144f11e522a25ce06792ef6c74b726662ad8e900dfd4e077224f44c4207a099cb6fcfb1622fefe750a4537d13a2ba335b1ee13c67bd0eb9c519a0c2a36edf84f7770bc4da6ade954cd498d4d755868ccaf5bb0bd60f39b686076b015077b57646c6be5cbc24cfb7bbc94eb9a0281c7ea4118df0e9a40c6a2cb35eee7bb783bc0e7e43be24046fa79ea027dc21b51d80acaa93e78a7c292eba828177641d7538cd91791f8c1102707514f059bd9522120d8cad12f2d50dd474dd4e435ebc5e8a6b265504f16c16054ca13b3d701bbd5293f11ee1dc02adf67ab6a6e93f5c55cc2f3a78abecae6e6c7b86e696cd0163378e814ba348a5b577537097fc84ffef286563073b578e0a49477f529c4236ba729c18a792db7e490654ffb96969cd357ddbe1f03ea69f7e2c353f7fc18d7a4879264113907b035b9f4b5a5b28fb578c68637d45fc3e83a6e17f9a86db694d5a40d4dd31a306db0264e622e2988f6e41ca7a59fb9703c2a6a7b6cce753b7d0104c12934dcdebf570d5ec96a055b91f9a7c3bb6c5950351eb8564e8301a87725fdcf3e7ae03c0572983531063fb2d23b8f718669d026e5b75acf96cc9b8867c7fcd5e51c3fbec5b634e94ced42b2244fd0fad1d5cfd319df189724fa6c0c3df052cb3c528af3d945f845d228aee948a5917ff41c29a21cb903de3b71aaab49a40fba115b72d023995273572e1b18698ed532f4317e099632712992a550149df06b060095d534049bbd73b378b1bcce360ab11cc72f67e0f0becd51ed3c5a076cf08da15122c4fb52efe25db73ab6e3e09474c5eee11357b601b54c31ee9ba7a7204d37edccaba760388ba304f6dc36d4f7cb681b8e0a830dfe4533cd334fef2ae234cea2d4a3df7e72682c50249f851755475de3699d69d719b5ba934711d0c3fbc0593211014cae81ecafead337a0132f2ebdd1d02683bc259bbf052e4cfae16371061a48262e65342a2c18e575d9a08a0cb3a6743bc9a33b663ee40cb4ed6537051540144d85fc7503611b584330ad171c1829980c21ab10126a798d89897a94e181a008b9d6a8cd64071430e20db00e5b18d7974489cb021b86845bd401f808121fc4c9436b1e8dd3895c1b699cef7bf3d68ef5256149b5f26c86199c535e4105c0b5b1c611878da3d7546a76f31f74733c2f32c72dedc99d018392b5fd65b5fa0acd5cb2bc556196152ec4e90e8ba06020ccc4ea915c1e326d0c7c4aa748171b1e2b4c84f98fbecf91f2301cdb29f6d4c66a5ff92ab5e4bde2cdc71ba414a6f4ae9a6424b3e3fef1451c9538f349a5f51d587864ebb2a5d002c854ecb522076d01d588ac3e7db1dd3cddb61b16a56e89ac801a1978a7ce6b9861f4b180452625108a0a64a15486974aad85d6932b374512748088f54204e5d34ef3ded8a39a9af74e1f93e44a1f88c32daa527b512cbba53baa50a7bba02c35ba192c749f63bd4b0d62a9c01f552c7fac7f75bd2b74f74d7bbd97d053d0706461794425aab0f677908531c975161e07dc969bf43762035c538584cc97226787aa192d80c0393cbc4dbdc2c39091c226ecf4b11fddbe076a0cb3eb94d2655e9297dd74bd05d299a44d1c9a6a57a15a9907c62d7c843f170480c860a198c5a6c195386343f4d52afd38a65a8a4c4284320cbd67c5a7af762c7b705cb4a64844cac87f0518ecbbba4797482762d9ff767d6fd172ac3238e0af2e2b27b289991898b5bb7d7f07e7887e1d6b5bee4260ec9b9e3cc9eb95b23c7744020d156b37c53d726c074d800aec303bf2976702d1f479e06b6e381f0ef99bb382b59801b0ac16f442fe89469cf3a717b35f0126f97c3d0bf90aab4ecfe3f74d1c6be0373bd4dbd5f272b744f5c3aaf7aaa48b77b67df489db601f280c6e53b498c052ca8a6dc478113317562c04049ecef91cbf287e245aca0e30b4d4238dd4de60b7c6d8d325f63ae1639f31ea9e6df7751c200d9230b9712a179cd2c9c0693fd003f1501db3a9dd1d4835f0c3d29331747fa463e6f0943bbd09a57038d909901d472a2d6315dfa68373561fd31db4681eeb062dce3980994b5babeb26132ebd10282225fa2c165f90b297c090d95237db8cf017853ce7226aab7d6e8fbc6e47b3ecc0c22a98603143ec1623aba5cc5dc253076bd80589721d6d189645e0e577180994b71d4046283a011b4308a9ac07773c98a01ad018dd3cd920cbb8d511439c794e216ea236b01455ea7c045f93743e2b4bb775900a8a8733bb0e5030391b08c538f433b80203a57472ca01752387520de00d11f237a5bf9540ad2b0e69ecd69d6910be90cfda09bd6780660ced560856e6990ba265a3bd4f89601c24f30130d5152b9e04ee366c0be52083ab7854112f65527ac679da3598e59e2e4f45d7e1e34b5e0cc6ee688abb0dc2ff79e897ee2e9422f4aad02da1456787b288a3914f1e0028ba6cc22ed2c3bdbb7abaa2392216d1cafc77206484ebb8d98e52dfc901fa7fa651989e695e5e110f508eb740288414804ba262e8aa192a6894d7c6d70ac6bb94c109b02772eba61080d065275715d7fab4629c6d47d71fc4e4ac714ca0099e60754346c0bd5113c4302e2e03d38ea97375cc22e2fe7ae8e06c84b1a24817244123101390cbbab48884d6f84d95acdb8348d33bf76cf464b0de1b37668e51897e6840e9320d698e71a282d4bcc9ca102ee7b8516f3e5b749363e18eaee040af43b4ad31f4434d0223aaa1b845ec1bc388361a1e26906b5bb06173ca6102f329c6a61b246ebfcf4e90f801afa09c7057842a02b90fe415f4b6681e7ff79a68c5fe9c1602e2bf4b488cf5ad9547d12a4a5c9388a0f67f724f95db4a11e1c9b40446fdee12b52350df6a91238834626aced96f5bf8ecbb740fd0c497029e14ce163677ce3a135415c91ba9ff9f91f3b466af99259ccb3eb8c090672bbb4017c4acd0b3370f0fc4b4ad34460829a349061f40005d6b69de758ba46dfa81dfea33840ccd8708f0500c635587f165912ea8840fab7f0900ef4c8c38aaf238572dbcc7650db07bf8b8df38273147565204910b3c9d4311f1c6d943083907359370736370e18768835a00b2360346bd777769b06115b41ba6354f2581d579d35f1a7aa0be7db747da11d5dd9a3283dc5d7265bdd3b91a2c08df78dbd3b0d67b9cb8c27a4f2c3209382cfd5fd2b81114cb489d41c82809d9df2d6222d9b26cb7b963dcec1c77f413a69955b61412744029cb5d8429488b9029cf84755fd47cc53185b26f4b8a514eb8ddaa893167d3671e34c5844dd9c79c00d410837cd80ee18f2b8f96a03f485db70f0cb075ce9ce010f2bdf82121094d523fbbd725ed16179423945c14434fd228c3f3bd9d21ec0cb39ecbfd112120248ad1a60953d6588a6716473cbe8e9bb535e546b9c511498e71b20edb2116b5ed4bb234b7d3cf615d031368a2bacfb425323562b62a8a65b3d3c1235cda8767e6e788afa40fb317f3e914fc5935d7b078cf0d8c8910d0a0309765afa07e96c36596d314b68ecb9451b07864da61c20418fef35a2048a340b0cd646dfc44d981a09444e5b0571942a05921a580fddb951736a39c334552ffe6b67737f6e2b58c1d974e49f02e01a6200c61c521ace76778a24f538bccc3767e9a3db4084f80f554b6e1eb445446c90cefcf5206187bc114c5c8566fdd57d3af1ff383494029edf0d00f6fe2153c4e1e820297a93f18cb72742a7fbc942368175376a093f80b2b07a63c9fc428bfe812949db85346b3f4008eeb296db959d21b7ec7d20c5073740a4e40cfc5e5370d554e9cce070806123e4c69d93b11f8c370d2812cd99315e1ca71d6e7d467ef48e4ea8465d1d943b4c5f345b8733fea9f80c2c0d2d07b547cfda7d7442332f0f6f36b2ab2b89bcb484e1a886dccc1ebff08d519db20f7704f1a86bb1d2c235ab2bff8dcada97d52d8e78e58a41daddb4af0f752db14c163687d6b945ec7d4782d7821b89285c532348ced412e8ec51051db18d75fd544b58332c7abbe4dbdfe780f89767038f696ae4dce6ac2919a4d331512028e3f5533c628fd805c21fd4060ce9050dbfdc1f913d764e94389add33a2049bca12c393d9d1d8b63d8df84e256f443dfc640a72c54a276e7032fc6f4b66fd2a50f0b0917022c16ce8d01056811ce4b86d041ccc68918e6976321d0d8a8ec7b212b48cc924eb08dbd755a343cd1242b82d99b13d2186a8fcd6690a7cd621fbbef99685dbef0c65a6d3611b88573e1ce4537032ca59a3e7d48465f31d020b08834a2c0b8083f3b673a69e891c161f5686f7ef9bcf982f10bf69270a2971e449b5af9d825d7acd99eada65bdba34c9dda18913c5a5b5a6980d08131f0e5a12fd40b955227ed274af03db2d8be35f0c1f3a52e93fc8a92f0116683d818a64c5c1551664e35b5caf25e1d6416a4a228a74c3f7e6a88074e43913bee8a5f1280bf69320b76690e48c8d20bccf5503f2346dd8faf6875bb44b9fedca1ee7a30e0d26797d79a8b28922b4e4328d1cbe58ef96c09aa9d0b559d10749e6d5b99f8fdcc8183d0ce4fd431ff77260f894c5a8ef9d512224222cbcca1cdc2cf0f588cbd6b99c054b80100848c03bfac4b6c24e51db09c98853c916051d6d7a20ecdfc4e16a71805163889bd69de704eb06813bcbfcb4b2c8ac58987991bf06c531f57bb10cc8fceb4e10f2d93feb69b72976638ecae5376014f6ad545266b11f3357751a0cb826167591b2eebb81c049c1a34ac7402ca0e91f3fe49efabf5fdf6ef4bec84f1d7d3f184654816b54a58f7d24986a0d5c9d64b1862177b81e84fd445864a108b545b4d63bf715a7d49bf1712580d2aa77bebafcdee8d5f00ae25b3b486ab5e7b0a3a42d9826f7b592e626e786bb6837c53e80bd6358530adaab1fab3f26c34315b8282a03de141b067ae6c9672cfae24f0b7264730516964f063554e67d739989523cff198d14c945083a1bd37a32a529e3d3df5e27fc08221aa0a98adf3257789d28e88cc149a5308af02bc7596f2b629cf82dc430503978ccee887b96d3c2fddec5695620897c7cfcee43bbe4a8e564c94f10b3b4e4848e461a0c742301a0d66e3642d9cbfb22883a3a2f03ee7382e6ba5750e066121b9cab395c378cc628dba2f921cdd64a90745f2acc9313127ee0f2886df2c9de205f4da2e48e83f43b49b009df288b38acfd7ad23dabc600d481defe1998744df12a8311c6e20f5cdb3e26db78aa4e9ff7249571a7be01b8939e559f2dce25bf84e0e7154d61cc7bfaad8271328db2f1a2b35c2c9d06adb88e51dcfabec66e36d4fb790e2bf3c964c695f0da0dc0486c6473451326567775a3a774418790ebd9a9630e32c601716c90376aa4260549492c89068ca50336ff6734cb354a491c895f3a3d8926309d43797ef3d7305b37b9383efd1ca9048b6705dc878797d8f974a6f38d67743eec121212d61931a4f1ebca494b8bbcde4d519fcf9c1a4eeb9cee61b4b1c638a53d2cbd3d879de805bb90ef1265742e6adbe3470cce0d884bdf9527e53828ac7e8e2d5a4a43ed82550da3d259154a817630be14f00bca2814f355bd8a801ac32b82de68342c17fb9dde9e90845c06f5a3f99575135249dc239f9b306b868d761b7f8c59add675be57f4256759a7d810d84f838844d97e2a24d4e37a0758b0225126fd6c5e51261f37b178ff60b033193d13df9c2b92fdf733edfc98f6f3e7400ca4fbda5f4fa24cf1f75ff9e9cb778d5c0295dd3e5bb93e31732aeef9e9d76e29aa4de23e3585e436eb7d4e2673aa18e639972a6edbd6d0edef651fc3cf014d1a5cb8af89004e9954f8948cabbd045a6cb4d5a0fd189e082697f240476ae4e8bee37f9b7026ce3923367be67c518fb1b2686af2f174290f9b213408bc3fe5abd4f99fef473228864ddd130e978409a824476c95741f644f84ffae3cea6bf22b06c7ea4e724609fb8c6d70fcbf49ffddf4413307813183e23c2d2a9faef3edb655628e8c6c20cc11aa98d092f25f1937cb244956a55a1413e72315d20dc0e3da98bfb9631906bb6bbe8d1e561466bcfc154fdb7b30f4b632646a469bea7bff3003a6659626afaa48043942e4b885c7df4024074ab9775556fc367ba7573bf4a409287354c4e14bc1d6e3f4dde9b32731a9f7410822bb4ac403af540603b52daa7569b027e74412086ce82d36dac96819b090b5fb58fc179ab2795f7a033d97dc9a0616ceb3e77e512242643df0b8f74b44bb6a5515784675289de1b488901df9997d98fe7b70ba7ffffe488f3b06c26ff8abc6d03dc6232f02ce2b88be0381f29da92c6f28641d13d654d975055195302089ecdb4d2920518c2cb3bf4db918f97b2ddc735f4a6779c691a84f12f34fbbc486137ca2726f150538ff0d5bf9e6ffd455b16c23848c9a783c1c373d824b143f334b74102de839668df23b3d605c054bd32636133d3764f654f65e65f4d42d12138fd03695d3fe6eeff837d70a7b1b3e772f8ba5a7933c918e26f2d11500bdb08d95ed5b908b125171eb8d25571a676512826b54a046978a9b08b4694d7c23ce090bc08f1654313d009e84177f0cb1f110206d1f20bbb7128d362d007754991dff5599dfcc29d46ebe49f08264cdb8d799a226d2356996a53649c24b65b9cb066a02babc68e80e3b244bceaf7ffd79a6252be984977c378553c8be33a6a328bedfde55d4d6a5b253e8e02070f3257eda6ebf84042dba49e1a7b3f2c48995b53317e7418c5b59d63c56a838f07e0b74e4292c476b7260a5608e6848853ce434d9f460823a5fd8c7c716267cc284abac8469d39d3d24fe2d7e7f5005b8ae15b29e53997d2c9fa619be632c15c3cf60b5e0a82587ddda7f54da029a5a1d350a2943e77db1a9209abf95940d28e407ab02b5842dcb5c560435289874df76bd7a800a8f7759d17b6c06b130d8a025edba97fb4dfcc50d9de2da7ba201a091ff1e10fcea83688cd2089f0001a1bf00d1a9bf32ca310c3a7d35c60a4fe2637ceb23a67e417d04af65f655406c82024132aa9cd431ccd5f03d3f312fe17216985d9288d3c41940f2bdcefac0c4b56ed38a92f357fac24b47ac6b8813d157854c080877208f12ace980211651a2cc1c6eda7e7937067c6c5f37b4ea85bb991de013c156b531293092b2e15f2d386ad3170b6a9ee53b82f9dad1a70204ec2ad488311dd1a3a563d8e0208f61afcaa9ac893ce4d477496da042ee27dec38ffe64cf83191cddefc511dc9d2abb6a693464025fa20a644f08de16e55d0e7839cac3e6811d6ec4746087719c58c4790d5f9f9be72f43c3966c4ebd0effacbac5feac369c025406b426b41517e6d0185cc94247242f31024a2ff04942adb5a20407118982e0549805584012ec41f6584d38b1aafccb2f0f9e559a7397928f1ed0fa0a2f3af0d29b261943dfa3eb707231b26e7c8b9ed2cf0ba71e33243ce6145792359156f5d8acbb9585845936dc60ea00908074d5127f8f360ba9a0648e4173c943363294604cdb9eddbaa9a457f13daca518615d7f91ffda400100f85682de2ef98e0255a155f57331a98e67e6f6cd008fdee8e25f7bcd37965374f2b151bdb3b3f5f1b08363734845a34f2d326109359315c2cd303166e8906647e01ae9163e8990fe84185cb41e9ef72b05ba596c4ddcb5ee0f09d7cecb6677df5a06128e75547ba170cea2252da7723560167a8407f717c549a5d5a27953e68f06704887e1c6f5eca11c02d7278410c29ec4fd9b910b908e816736203968803c8c19c9b00dcc5aa6d3425711db0369647771fd148b06283be19bd3acffdff32e384c56832610f5d913edd745254d3afaebd102acbba7270880c7ef6b109ded8474c4ac9fa583164a93785c06370b33b9b20cd1f32f20a2a08f80001c0cb96d68b549fe9f60de95f153506539244b000e15178bd35c4709dbf9a0d880a1a3138bc94a85150bd3d068e84c9d937bac3e1bd4b17b45b3f83b29bd91c8c8f9163193b7f86e01dc14863a32ea00ae0542d1795e9cc9319350d1288e493aba318bcf346c2f58ae39e6cd8dcb5de2032e0e1d1bb5eb891545640df9e4d3b86e104fd7f483fecc6be8460393f02000008d8a3887f84806d2676b0f4e96175b3d0321120195af59bde2893f278e2469eafdaf065168004bf3cbd2b993c27e1472ae8368a42ecb724e8da65c1d6e493d7a116516f1076b6b8249e94bbc0ac40624293635329faace5aa7874e627f586b2b1414dde529e20c624cc49a9e9de4c275d4ff558acc9367a47e2330453c1a0f18dfff2eef9c923ab607aa312e2ac3243e0d073398eb9982281e4284e12d80d29023de142a1c3106c36aaad7d18f6a78c9d8b0c38937075533f02fe8aab97ff157f7c56d1b0e495558e6c5daf121e2cc9bf55e206b5499b7937a8c432f64f251bdbd0f5df16ef151cc067ee5207497abd5615fc0c59b308e4859a274783e20b06865a3490055d8f208be262661f0aa518e27a6195d883a1d3026b9588281853dd5343266e544e006365ca67052e38a7af85ff8f7d7b9fdca392c7c5c5bb5aad22a1b8d7ca83468ea2916753f0d00a0f9d7755fd392cba3c5fe216a5276fb07b3faeb90387b1bd2d600dd151979f03bc06f4d8354b581b869765f0361b0460a2a9510ab1f6c6efa264265157d20aef3fd5f74d4b68bf9f2f705c90a9b42343d7c833db74adbeb00ecc90deebcebb1d42e34af786971f06c305a684cf0c16aa6a24ada4917253945a227d5faaf7bbe902c9f4e489babf8e84dc8ac626468072b771f815e0e3255b532909964082a94b46aa4cdcdd9b50bc783503a3021c1c8a83bbee317bfdcfe20ee684c573a82ce0712b1cb889b5a949b30a350b5895c778c022f990003fc480d5620191b0fa6b367b959b1820409955020423f0a8a22036e7eddd3ecb2df520f7a97924dd17b88c0024059ef0accb9f90d04460eed262066aac843eb85e733c3d5864db386c89453c974938e23fae5ce21efba117f1dfb2812b0114c054fb7c477c654734a8fd9f3d11663f75ad3efb1e3b286cea85b90ff8dc8b494c358373e5c5862d3c565d6f79d7d5e20da49f0d343025103e3acbed3761d2b65bb102d48e78aeb001ca9b120658f997a2e59ba9507dc4e6b0764090410857883f2a2cbfa3ba87365b5469740e504835773d9819e3f6d0dba35aa77688e89c81aebd4ffe427bbf1cb603fc76b59f6dd4ef6c053634bdd6ba945097ace2ce36eb8a3ad239e55e7938e2f7bd3852450bb24fb24366cf4f9900826c9f38d944d94c4b05827007b2215848a6de0f14608faf597befd6a261fd5f426c1b7762dc37b5bb5222de0315573765703dcdcba31e2a1119c92717302e970cf1df23318619bc2adcf19552381b5bf4d4a3e436d8cc060bff03b31240f0e5ac99e3bf74fa393c504c05c1cb0814cdb3310fcd12ef4c40b1f9770896022f4e260cccefa55e14f61daa7dbdfc335ff3a9c7549ff7b930bde298893346476a76f0b6b8c9c2d8a29f8efc73d67bf299c96eec0edd2f498c65865d267c75e868bf0ec48e7de46f2849350318fa94a2a68e54027df45b76d1f4a59226e1474e005b0da668d481f3bc164c1c68fa96b963419772267a313ea1b108d53c39d157fd713a683299436894dd006014af337d8fc4096cca75a82f56ef89a12dddeaddec68c736bd9e6a3f9b585b68d071a33c0f44fac018dbbae3de226d92db0741dbfb1c73687a80a1428c4491a4ffbc19c18894e5d8c3a7c3e4545c3225b0f1656cd6677ea791fdac5e93e95f9431298c1d60d4eb7a1ae92e03a36ddf8f02d46c7442d2556f2b29255002fb1716442b2f6be3da44c05d367d7f1c6ec927208955df1738e9a665fbe8a46868a51e87f82abd4c8072d8bf2e975cd0f8c1a1be00831312950b005522a2b182297dc2c6045c2dd2d794795d32cd4b01b62d5146247d33ecfc31afa722151fc1d6365b806eb55ca9df8781ce1fbd32f3b57a45fd04c70e864f344738efc1376424d7c15fc0a8372460bb9fd3b9505ba8d568100a7368bb14f3827f832fae400d68685a7be90b6ff25b413b19944c68e3d4744eaeb674ceecd7c411f321d8c784785009b578cbf4c84861307075086d9a7f47b39a9fcf58239b54fbbd8072756c6f7ef20287ed3fa6183cb3756d1d4867041a99d65bdf59c54fa5774b0d79ecb01776755db9ca23c31c7156bd7ac1666de5afc620622cdeb2f6cf65ff35d657c24132bbbd208df562d495c6fb4e8190ea2252cb7f9459436dc016d92e632c505e27f89952063a41099503e0a045d6f842b1406e04cf473a3efd76eafc96a0b88bdc16ff23cd01e659d26166936e36ca626ebfe34a2caa7c484534bdf299053b5947f11e96619e8c247fa9dcd4e2950a8203a836cfdeb3cfd7712900014108d7a58bde2ec00feacd4b4095df07a27f45b5e771c355234309f4e6a6468d7b46f21ef38ef542a9449d3ac14da82556148395c0a2d3f0f4ab1da7335f98025bc4b5f16b7c728f1682772938551e005c89a85d54450a367e249d0c6dd86e3527b02ec8b22791f5783082c2f13d421b3501ee7531d2bb478a7bcd0e004dfb0a79a1fce908bf05d844e25559de1f314c9be482fdc88609b00f2b886f06ed9459b88dd672da0a18eb395bf47175548556a9729e32e2213ca6a6120be6eb293908d0f030908eeab2e2b988893b8689c7ca933d55369e984ad865a5d12fc12911aeecfd28c4476a7ecf0108472b459f7c8f6de11155c11e71c528090047310c2a7a9db9c88eb90e550b2db1cf0b9805a8aaefe3d524e00cec18adb50c09cfa63b48413caee25ca38f692f3086c1e2b68eda3048a5379dcfc9b9c54a0bdbcb9697c10683652f07882dabcf11376430b793ed5e6c611ac789faee2fa93c4434e15d8abb482cf3d587849af51a27c8ed49ae4452e0b9c2688b54a766058510ba49012ba901c62f14730808fb41c418869208a2c1fc78faac7d8d7d96c17ead828c023c08100dc0a37a764a123c941fa378af926f422208368dd02058e7fd4b271b61ad26a74fe11c352ef199ddc615cc44ef00b574db5c792da084b764c446e5d6759755b34b5eecf60cf410628e2b90884b748985c5ada2184fb1dd03fad8255a28b366489fcb4937bfb8d8f7152ff0f755f2505c4c943e24b6f6afc90f16959b03139ca3b5078134dbb26563112ceba5d7a2c6e62323ca0b77313af3942e77cc1ec4767dec711cfe777829ec6a59508b463178e9110f5b00f98af42eb584cdcd9f32b15a8df6d0478232ccd70ce2979e47dedfe3349a9b55c36d431a9fff42a1fd03164a8e4641bb9d17bb8651069aef1619cffb4ffc5d584d4c994d4eb533d0a1e3b80a487e7191f7d576959fd67c1ae002975532df9104ef88e7f7cba4b41126f85a9fcf9b336006a4465efe1bd4709445f509247acdf812f8fda7bec7374ce2f1a9437ff150a921048ebab9ce6ecff01888fe3f8a14b0e54eb973f6fa2f00f2f4c10ecc475004723c1eb7fbfd072c01ce64ba0646fcd825c590cd309d94c3d8e0ee791c00d3d7b291b458ee6a374eebd78f659407d27f3bb765c5d956afb62edb1ccfec52717dc0d9043edfb315078051cdddbfcbe1577443fb99c53c93460083dc58285113e8001b16dd1ff50b421495a2ed6ee97902449f62642c736c5c310c6cbca86a532c36853892d353e60f1ac5ceceadd87410c6d3aebb66ec2be500df7e88d0b9a1fb3058848c1e0ddf5de77e7fc108f137ba6a85600cadc38ff3f80107120e0f217524a1d4dde1d7fea1ea9112f8fdddaa780ab15801e40d2e6339dbbe90153c2d0fe0da84256b123616fc9889bfbe06130166fd34755338111a77d06dcfaf27b070fe2f25773f14f008a41301e1a1c71c390107189082135e0c53cfdb3b833350bfd4fcf80740e4b57bf7ce1374078aae2781b4c6bc6cd9196a030a59816baed8d5bdb73f0b70d1a9c3de24d8b84da673b89d861967ed019baecd57816dfa9756b100ea1da9c0bb7d27888b6334f4a1d871dbc55e55b0f958a9f63420b78144f2a637c573be23c03a7dd2b9ae3297767f295d989734ebea6a3145a7b798761fc5b8110169e2a2b032a38f7053e80ccad07cad3788e9d31f18013fbdf153bf313ce9a75275a84d25c01a780aaac99a93679127c5b5cc6cc9f2932d3fac8a770d6b655f7d03db3b50649181488747a5c199008a57547a4bbf25eb06b8fa0c1ae602f651a5025b5ddc1ea9521a7a36bc19156ca143f18a9f80673ae1b28a95e5fc29b4beefaf563255b08d5af78e898f522db22aa1de41b89505fe71e7ff04a20c9d5d390163fef92001145517e607bb0175b7ae0095b27a96ea1f15bb88f0d91f255b08531da1ee47a495719e74e9944f1a4b2d473d8670ced96f70e0055d25ad17d1e793c7f512089b704b46b2b5e3a967e1fae488fc2a5fdafa5404410b8ceec9d69f06af0e74678c7030a78583e9c99e69fde34c2e515c5f88c744fd1fe93ea2cb0db1803b63735b31631b2c1016f5a52804511b8696db5cac0ddd7b6f24e0d305ce170f396b3d6c05721260e7ee8e120b8e4aad2124bc66303a6e18776f132e0003015b0a4065c0b603fe1381bdff6cb694427b4551c83281c5664b43fc8970e21f51014879b43662b740f4d47f123f0cbd4538c0c26aa97ba6bf5765ff6a84a9a675be388d7522c174af1b6872c47fd6942823d29ea300ed02a4e93d2778ed5aa2ee2d48d05eb7a72565c4318571b5f83f3b0c7285c81c3ab17c3e888a18655040469bc091b9b3c147689f145c2b0948967d2477816c86645d0972c96b412b9cc1acac769985d744af35b93a030d4bace6b41cfb502a517bbff04c252e744c3de7eac4101f337431e0fe30cb158dfe531c5792e8ca3f9d415109c73e1daa6fdf8330d88859673ee53f96d3de8c7c4a1956f4d845e2bb5e63abf1fd9eb87f0f31c757099a23158e1e30e733934c8d8ed5659bda6f9caa92b0da8e92995ccd6701bc4e5aa08741286e4606a6b3aff8142ddaa7beb8da3aded62cfa675455d4e79799ac873219355b03d0ba5d093191d86624c06d52a2dea2e94c2a15ec1c60f3a10632707dca9ba3af4dce69f56297e7fcca43b481f96d041ecca7e5f914cfc168cd6f721ac7ee8001452fb26296fd50f8063fc8a6f4643a80eb11109eda7334d9be47c64eefb633cffb784f3b14a7517560f1c05c64d77789017a1151f9149a8ccd7db68dcc718dbbe276acf66de8dc215200272b5b750afe63e1d1e091946d14662c61aae4eda2c1e66f7f0c2e127f59f95156ba08c08451e03a603dba14a5f271e0adcbb5887ddf22bc8aad71a94fc21ec081c7d27a5134827da6bc7ec3402e4c0bcd2d59709cb24b46948376a21ebf167fcabc1d88279858d51a46391025b86a6815eb5e27c2833279585b65abcae63554d185df06ea3fda2fbd97ee271ab2a800983ef9c94d06d1eeb11ec0ccf4d20d5ed1ca894aaf4f5d64cd45e9d77cf0498cc5f54993bad293df8ecd6edb8e2b0c2d2ab3c60c7becde67bca6eaa6792a4e9cba207e17833e55c614d1ec31625dddb5fd4fc7212a11aac37195d7827bf1b51fd9075690a83eb34f6c1b850cb75a7bd2fe22202647228f757fc5bface0dd8531a35497f6a8aef5a998419e855458d30b97dc8e3b93f68e27fcddd2cd3fa6059238ef12abb970eeab7eef00397631729d0917d5d411055c8448442e67763821bd853acd8f8b503de14057f1c18fc52a2720bba84445483a3d16cc71d72f44b104b68213790332a1645a8caadb6b8e849cc3026660c269ab2e0c35d150317345a64b5dc3f9d6f8923c4dd2260e59564078fed18abca7725adbe754e0ef9142e2e8e94a18beaaa49b0c4ce2becd86900ce481a4ef74e7222b42589c1957b297ce8e4c27f26578fa213261fda2871b093e5b2911f990a1421c47b04870fd0129067cacf2e0b8fb02648477f9ea85042a16575c913caae6f9d15be21d45a781af7db62c845fba2f62fd15245152f9c9aa4f5b5c38b90a65861ea810efa78fbe2f1ece40476a84947e437789cf3e11e733c42da3114cfdcaa4689eace9a64693b16d55f055a223f486d5c332a529e93736d4f7e1f3470e9e40d50b3cdd5c1174e30e2e2ac7fdb9a2f352e1e51a4e408c6b4d63c9985d88a5c9b538c18aae3363ea77b5b41015fa5fed8cdfe8297cf606947f56b32ad66fabd5a43d824255175b3c694e71ad616f7a49b21bcd6e718d8c7795c584be29d0ff05e796b182095ded9dd0f2dced84e0794505677fb2d5feead15964acd0cf5a085646c4a2f5c4d31f58d250002efe649d301a9e9d5d0ca0d30aa06daddba46e5005f5a2cbda94bd4da91a8a7262db713492c0ab3f6cea3c77881889a5d6b614e45684d4d7b9228b9b1d18d54692bd0f1230e1d00268383c0b19461b070327c48c2da1c3af4e51bc42a05e9903b41173543b4cc9ce41a243104d7cfc841018adde1c9d036866284ade62d9054070fca43887a9b001e7360c2d763b73512d6163e8bc67ca09829c5275524ac0983c6abf8e577cbc13f3fcb40d0f3287b9c14bd3f939d45b61c46f46e9a07465b2f82753440a47c6a94a53b6029999be5146abafdf05c774c929d30190ffe1469862eb1d0adba4c657eb5976ac1893533c206a58a462fb41b50f17b826c36f299a45aff759c3f34da79fa9748a8dc0e6d69be2128b1f2e574c6bde83f3dc4cdd7586b4441ab06c58f550055725c1edb96a973529d3f6f1d8d3ff770e3b305cf5c385460c7a200b2e0d51902a9705191d142dacbe12ea7389cdab39ee26d3bc27e34ec8008dc7abfc0efd8292ec8c34a174641939cccddf470567b97bc7627091fcf3f37a9a3da1d17efa6d2dd4b9ff590dd613ba36063ca8ba9ad575414c539715f5b0ac082e1f323d08363a2932869c2b00fc0f20893430f7de0b21d0f8a41cef409afa6db2b79adeec71dc594afd077ec2473d73327de5d6317e8c509e5fa6e4fefc4b3a3c8cfe05be4dd44838607b21e4f4fc7d3d0601e738a325ee266dc58dedf64feeaf23391bfcb0947c9921c34e539e689f410c33a4737bccfaeb10250013e82f2d2fb2c4580f1bbe6d455238ae9964140e63a5df7900a243fa0f718f9134cc381d835340507d2e8216450fbfe760dd468d0b7abdee149b41dc1691305df8bc0579303146c0141eea908ce4e736e9f98b855467cc9212077ce30d42142e3bfc2981c5cc6601091d3d412fed617d7e4a1623a2b75f2e89d5403e60e00661eba1ca24cec59270ed8da12475918bcee6491331927f06f0225031dcdb345360725f25fe843249f46c07a61dbcf6445542b5e99cc6302ec70c692ee9e2f538dfe02e0af02704d273f2807e73d13926b15a30d26a003ab663d8f0f9e6e69e1e33406e0ab87e81e654d24d9a876d31467f7ec9f0acbc567da4b99f5e56917ba4601228b0ea54df07d7598ec552131f33c0a24789fb9a5465933221e4aebada5d4900c02c811b5b289b0dac02f00be12f2c1559e48ee4d6787a75f67f4a50fa293584eb205a1ae2eb2de80b9ab4420831baccb04cc8dbcacef237116780b505f23c8d9e91e0c40709643d1b5a5fbde88b7b2d40e373092c9d7010053971db0babb569c7484fd00e0f99588b6d42d965d657eb9f512cacee16c253f8b782b8605956aca56c5d6a35625c0d07bb1a291ca10fd178e641150b481b6c9185daec36339b204f69f664862dea0f17844082f51bc59dfe458b971bb40c836aeab6f64e70ab622c92d3b4f391653dc704fa078bcf20db5ae18c837678ab17f4d6c5cc8474c23617c53f339348ce2f133d3007878dac8308cca11e350d6f1dd4ca693e24fa738ccc0cca7379c42abebb6e86c48b4b2953305c550e7f13c22d3d7d4f7966aa40282a56a89fecb5a3cbef8bcc9ac72c42a354fa56d7ab152c7bfe122b8320cccb1363246350c88e9941b668aae649f66b3e686e4b02215b3cd4ff7109cd91850d617e5b2209414252e3bc1638bc4c92f15551ac101d63026b77ce4b7a0c8ea3234500b3aa8a5770dedf61d93752e45cbdbdcd2c71adcc6452b668c257f790396be557f86df57d57f718c0db22fc4a2511b2a1adee4126033c449f6598b52122f888290de4a6b1c8379d22d1b2c68420b5ef43b82d5cfe4d0566080010a7856235697939b3a27ccf139594705107be6f7697919b9a99621e7680e990cbb65590f4ac8ad683739c8ed27c0fe4ca011e8a981a84ee8f2160e4e0781ad8c6130fe6e978777a5808fd8b1dc5c3e4632ff2bb4ee600375a0afa29cdaebaf74de2737a12738103e6f731444a91e0a83508e0d5fe7127d41c82259ab723fe8d68c1af39cec96a57917d3c84760051813d0f7003a36b4a39471d1a958957f7e4b5a4162fc2592b0cf699de98050327f87f2b54d078307ffd26137bc67fca320cf464e4c87ed24f353f87bf81ba0d3fd3236b8551e5fb730a0849d7b55451c1e751875b5c5c96c6a17fe55203a4158f75d4b6dd1a11b350109d1040ee60f486ee8eac7f9dd81b4d672e7f2621a5c636e70f98f1883c0e213c325329a590584c04255768c3f34959b186b9183fc1f8ebf9cc34219068c23886d5d70bea3370ae75375002505f3f7b737e31712a10c03f82dc81c8e512ba952056319f9de70d9fb03164fdfca37174d0b0006345810c9986270b2ab29859f2cd7a785c9a6de4101948cbd55215c5e2e21cef59e5457b2dbc4635091448184fc8b7b71bc0eae6ce67d7e793a58d1e041197b624fd1141dc347b2e2a03498ee76b7eb789aeefad5e01103ce5791793540b7f5479c6d5b99a13803380bebf8824fe9bd774f1ee90623981a2239067809a919bdcfae4137a6149803194774ecadf164fbfbd8d977f87e8393c043ff6474a989b63c6355624b692b9f4cb3ad132e02b052acca4a95af452e2f8309720455683cd283e4b028f608e944f52d47be01cb693b943b42f67d3b1c302845538b811067e7c3585c38c035516c5270050b7a2d81e0a7c6243585012ed4c159626f1c608e51611d0d8bab06a2f1c7062455246ce48fa554f26bde2af8648b68fb48741e87f60018eaee12a28b0d5b5aa9e4319243a3e600e5c34500652b8b05d133fa6716c681c79aedf8c93b4dd775375d65c63b0ec16361eeffca9fb93cc0a15dd20aa33add6a0e6044993b5e5c29127193cf3b0354547f53fb30a172b601808f3fbddae17d718abf258702a2c3044f3b103cebe427878d990c687a78056eaf8b2c50c4369359b62a4487286421ca244b6700f9296ce61135ace648d82164b7ca105b43773120e8fd75005935d797b5d4a08a354a5f543453a55383153a35fc57afaa859c4c3d1eb7db5ea14a47af26b25e75413238b6701e838c5e25db4ca5e4e099a549637af72061c843412e6be1da1b2c3c801d2a13e7bf6a2d2e412072a4f801095f7bce61d6238f9f80a6a946aa4a665b5f33e21d92341e1020bfc05c63cef1848c4f484c9d5473a54852315f029c2ccd91db5d7e6a275719c8da05f3ae430607fa50c7bbe66806e36ab0f6287c88aa3dcbae4a25524baf893c64e054e50816704c112d940a4ddf5d00d62fc76288455289e559c44ad55b32aa76c905fcae24ddf503801198ac3386243069ceb3cc6c745766bf14445a3018669a005f73f56d4a49feaad21db69f93abd9974d6dafcd4db389f2ee21a62624184d27a7e937208001480c4d5deac75e304697c1a150b8088d427b5ad6c367923b21c5a3d8a1c2c017e331edd9d92226d5f12a9c828bb9fa760298b9bd8d2320d8fbb5633699aaa186044c19de1f138e0c79d8a100a7eb76fd56c1d01891c561cc7597bbcff16f28ed27a7d9c585f43542c9f08ace66ba3ec8d688b962b2a1aa505fb2a3f2ffa0d9a97338ba46a35627be1431551929eab500ec3b260ea8e2cbb628d2e8b7688a0f9dc0822b9a232d99f03cbb411f9665edd895afc1b4bad1e34eee13d9abb9c8a7d58b9d48c863afa0d813586025cb42a21631f39d92ec402222dc64cdd84d4af47d6000b326b6a9517dab8b39f0c1feb063466a8a422e1fafa2b22b770e618c63ed30877c16604a9a63a326285b4238f7d89567d923039e27d9ac2f1c33162aee3a59e327e9533589425317c42cb55e3a073d7b1d6f3fc6aedbe7075679b2065a46ec910987d631d3c811df880caa814747c5bd46f2470dba8edca1db4ca44a7b064914f09f0f8147ae114bfc88d533e7549e0f7991ff03413c4fae9c94e304e26037170d36414ee73496819e8c6f49adf59c9726d0e65a25b3b31aa8a6e63e228d5c1b83e34bf99cf6f4e0652a9584d028515a2e7d3fd9fe4cb12781f1e2b5a14c6bcd43b190c851ad05fab27ea18a4eb0308cff4cdb6a1acdf413901f9d936f7658bdb2c9321583696577913562c54f2d3e775cba7177b22f802aeb4523033c6f97b45a55e58f5072f996235121aa0e2791f5f40c5e6c15dd192492c8b54a160c0672e739307b837cb8ab73121a3bf5921f6df57927707baadde4084f170d89a85f85357c56381b7b8a8a25730a75a3bf56cb618c341a41076d42503dc0fb07a568da281b060945e27685912cbc72bba989f0ea4f4b84e3099cca41945d25b4d5661abb46267c8ba36373d4122a6837ac37e1053183701fc46715876863af610ce00fc16d2307389df6a8ce411445ce1d3fe7c7f93b5ba20683516fa2af2ec3fcb9d90762353e423f450e060684d230b84c3efa1e1ea7f87ec67b08f2a342a455a5c5bd1e1971901f84c96fcae809669c958c6d09c413358fecd679f28a700071af0fd00d83265714843f87890f79c7e42a1377928ed3ca1577a190bd4da45e1b5d9c00903e49619f801800a508986c651b6f0d42dd4cbfb75c4d33d0122e715f64f6a832688f07b430cbcd000a42dfd2425800a6966b79ba4b1f130b788221fd7dbc71db4df7304426f47e3d4b831cb1cc5a1779aec1114ee8a53fee04031a62d8a895655cc394d87e21704c092ee4325b488c24a6d60cf927c608f161fc13e6a1c796bdd148e6de1fff16ce1974e6c1028224a4fdcf63f30ef0b1651380948c96b836490570f765cf80d614a41389359035d44ec0acca9971d0ef00d60bccbfd5443e1bb9064bd500f715d46f7e8323cd633c5b763a5ee867127a017c9c4398de46d4938c0025c0b602232882e7d6f483ae65d6dd5240030a5824cbb50ce2a1541ec1d33546d16b4af597377d052f40b6f4e6c582fef7e8e53226467755594451ff58805c26929788634f484502024a5abd6476ef46104dcc8d1bc82b5e3225331c420471c377839e1086400e1839e388f476617ad110e830ebacab5d21e56b5be2e0b3e9dbafe49ed283bbeb30fb00dc31b0296200436e9359621d015c5152cf1fcad5b9bcb9706095a559bdb83df2db5a52a6e65be0b416d8ad02bb1b09093fb57eed467850a61a7822fcb0b078644e8aee64f9ab3e79ecbfc19eff71fa83d2acd94d423224c3a132a876c134aa710ab52e8b4209d509efa1f3250b75757f533074431e594a3b0e23c651d75d9b13d5465d0b9d00ca95445b1f34a5322e48828e40e1854552a50fe69bc92bf3c2eea4d07997e1417bcf12d0ede228ab494b46ad960bd600ebf75fee6efa74abcd1af58ffd53f977d70d832a1a73a433fa5fc3f1940276baebeadc51c8728cd4f68b56356e3c5a7b7977888b42f092dc76864f901ec0cfd0980ed77050fbc5229d1ee16b534f9d0821cc7fdb82b69c8685512bd1bc6217ff42546781de01ac44d43ff4f0c92e842d92f694f17b97a886495962ec89b0ce4a4cba436b69a5b15c5e492bb9cc2f20423578a61ec4e400d1830c2879aa8eecac86b7fb1c03a4915b679226afe7aa3fd4d5ef90124438ed34e2bda671a2fb871d5f52f560a8a5614f4ad3e88ae0c2118ba0a7ef094a8f4d11d0ceb4664770691b7a4578134ff7ce047b2b081a5ebf39c3a6d90e8cf59c0d1f8710af0723e6356e559609e1e6f82adbaddb7d991c146823ffbcd48f062c98d29772b8c8892fbad70fa3f5c484948b9d288ef9904e4d353421ceb2cf7eadf1dca232bf19270b25a60a8dbf568552c7eaa778d91a473444e6ede05a3f33b30472785927368225c5e50850ceba9de58af73521de51c8a8bf36a2ab99e24a09f9be7e140727bad82e50b965326361e49909a1f73b5875019d73db6f579a2e1c818233e186540c573a220889188c79b17573279ddd4311df743e591af648934dd5825c65b43d1be2298b93de6488ddd0c0ba9b4b67ca8259db49742aeac19405041f8491724dfb955a6cf9dafb388eb172ea0db142f6db423cd01ae5932005eeb4ef06eb6a918e31d06db4acfb91f8cf32d78cbcc33982761778e074264f323d30bfb2cf4f87fc816544426bf1fa8e601a559243910e5958981d906b79416a0b204202c8f639890e50c54ecf06089a825eac2ad7095b97a052aef32380952126368def568d0f42a7b7b727c33c46c039ef219a796f26cc845a8f7884639a2023dcde7825b326a23fc21dc8b11b9a411dadefcdd82c3ac1c89775c0f79a6d0cf9436afc1b2e34a764484536c59474092f35827a186d17f66cd27c136919689b53437321bd1991412155b493eae55817a8b0dfb63798160e322db82db9e1a71b44812ad2d3c828f9d8419f9a105ca7f83ff653366103c8d2b9ec56c08f18ee64aa5099d9840b677bd9aa402fe39a2e1648e5fca9a0116708ebc15bb018b6e4fb98f56c89c499d3da4a19fcdf747184d21dbc86534834779f02d2af386d6d469f25010c50e943dd8d225da9d5001f58415e9299984e533820c734a3c28d6c42036685acd689f0120acc457625864e5b5bd0c10eb02568bf477febe3e40251159701d404062786262aba2b7c8ecd0a5b46c40800c10779d887ec64aa488bdf02e429fd3aebc425fbc2276d42adc331fdaa02b211f8667d60db09916c6331764fcce652fa4fbd160c829d4007cf8ee1cabcaa0659d8124db0f5134db87267c5bf6cdd1a12355f6aca267dea715ac34077675bf4ffa1cef2d2cd1bac4dce410dd30bdb3cde935e1baa2f1c4630422cd82cd5180151598a03249ab7a16c805b2214cc050d801adb50d0abd9ace9417522b0da70da15de037cde06791a04bc70b6a58817aadabc100752ee738d37ace0c4766d470deb80eba6643a2beec2a73a53df7cf52e921f73befe00dcc1914021bcb4194193c37d60803633a4745f3affd70e35d84a05d76836dd18b827c3ae30884f5c1c432d744aec0eb13b9fa4ecdc8e42d1cfa3fb75258d7d10680425b098a52107a40bbe2a208a99edca09f8380eb5548e09534800c66a6c85b8ba724a6618fdab1f4b322b5412e3b59bedd09253abf2c278e5bf1f93b832e7db2056983a1e95b3f11ae9f25c45b6fd5c15e97b4f76aad15f06f39595aecd139f4160371cd5c19e12538e17e6b403bb44f4c4af689ebdb544c133c68f04c00426e8e7e3628a6d8f06760fb4b4d7057ca99c62194e7fb338ca0d21b6669aaef6ce86567ed7b31a9cd98fbafeb2ab1d29c21a96d3ee94fb7bb7ec9af9bae59844bc022b3d750b0448134d88b91a50858eabd136ffe9127f5a16bad38d8fe52222970fb8981be0f49c57e30ac889bda119a0f62195c6e2fef82f3e804f7897572d8c0ff09455bc6bb4b70f2db7f10427e17c67ea3bf960b412cbb41d81ddabde94f0caf7aa70f2f4e642008c3dfd9eda8443a692fb160d30860aa817aa27be288b88309ac0fa6203b3c580ae4dc12645adc32a5c0dc0301909a9734542e42c94ddf8ddd34d1d200273be31d2b99150a0a990d66bc1b4b125bf7803cfbcb77ab7b4c91464b4483ba009644d9bad063453021e5805e8ef30bb6be50cf3e1ae18127c96a56cebf8f176f8342a06e1ae2ffaa235bac5289354c9d7b3750c3ce5a353d7e20997e04d9180b0be50a921e12beb6e04b3f71489f323d99ccf3de4f621ff4a4d30e8c20e174ff65df7d97a1a456548c92e9b94eb937ac759b9bbe7aa3e2077f948ebb9ec129c24325add580fabf983d559746d28e92da88365e9bb1670fad1621affabb5f9743ea790a62837b2edae8f5ed3c1104d9a72f7b363ea66509a2f68dfc9dfbb56dae06dbdfba92d121402003986fed280774700ff1e4ed8bccdcf2276c401b8ea36b0c572a8cfabd5702e3c7e21d62f1f0ae3a7753d8fe668576a6bacd06b0d17e755f06c1bbe94938fbf1b22f20a4690ea8a38b31c0c4109596d6db6875c0c5aea62150c28aeb3e791e4f7a09478b733832e80a452821502e5ef64ce06a45fa76e7c3b2439b282710baa91917fdd6079e6863eb4ac04cad80e04277aab247e9535756cf0ddbd8578fd1dc7f6dd1db5e26503bdfc92046afa4cf0a0522bef3558b7a08196ff27c982c8f56bc0ca63b2a8b833776096ae46ef622d08da6137ca3aa4f15d0e3c809363dbbb2410788701d93627ec13b02035eaa81cc54871850e8b78b9e66de5838c16482482d4521220646af7341fea11afc9984cc8ce8c33654496f0c0bb25483b0ab4381ef12a4de6add7e4b03721326e8d206c37455e0ec3012842bb99c7fa81c4344910136041e4e3b27420adabe875641e4c6e1d1ac97b0cba8e90209776ef61eea2693d9aef4f20cea412124701652d9c2c58d6dd687d05f62f0f1b25f3878a9066097fa24c2053affb9346904a04bc70939775c008b2a789e83b7dfc09529d9629d35db51d4297a7be7e0dd5dcb97175feb42b73091a6dcd54dd7b839f8e1842d830294c483c99b83177c1ae874d259f363fa6e9c925dabc8705a2e54ce54b3235e7cc214924f19de2df278722c20e9a655d59cdbb5fb2afc63ef727654580a8d234c1c190f1241d2bc3627f5de463d951818501031778a3c22fd414539a500fcaaabcc142dff0fa4e915a1ebe47d067655f31e41fe4fad752299d1a47376b9bb4736619d715f44fe4f45bd24d23c32e226122f7800b3aeca40a49f510f9fffa774d99583d217b54c8b43e6ed414b1660df3219fae2219ea13506550a2808b4748dd77340cdab1af972199c8636a909df6cd779c8b2ba13bb9be5570f2a9181745abb7860489de64b52cbd47ab9ee751ef4c2935553664c3bdb790948ef5bd8175c742df56eec52c2a3f6bf14d46561d1beafba55bea040b9c18b124e8daa7024e711cc2e98e34a41a8d3a37089b2c55ec7654ae5a92b725beb408c477b7e51f2ee0e20bb6031827752d3967c02ecbfc12bb636b9d58aceb6db88ed1f329448594b6b51d8fe9e3849fdf3feedad93a43753486b700b0f3068580a13cf69a1df1b82416c9b97b98679d2c98f7fdff3cbb868cf6628413da24231799bc78025799be6419c2cac6683017324c8541474dc61515f131f35740b7b944feea4005a2b3acac92d229de764cc39fa9e0e0b3feecac61c2700dfa6f579040b64708d321678d8679aff79ac95b23919213119dcf6e440825a17199419052faeb46b2bdfc4f0d4f427e3301367e5ead6077780f2309f61fbf3afc7a53c670cf257f5cc177d9a6b1e0a6145ab34d8c253d467014060c887e3974ac0789b46f45cc3f3858379b4547641073cd96674e50675174d7f68e16313aa8bc4dc8c54f6096a1ea604273d42574b48eed31c076fccf4a2f05b831678e0dc69aee6e9d7424f9ad1442bd6e67c9ab8f2b3a88bbb682950a5ba056ae724eaaa19ae8240144f2c27253d079848d562322504483b6930d97e5c97998921a41dedecce411b5c704b367683237e4b7aa0ca1ef3b1794e79f593c6f07d5646dab9a98dd1c06d22ebc4de5fd7426d3bd271e9038f27abf7c1872fdd1f3816f01eb7e68eef7da2c4ac591b2402e885e4e1b91c32d3fe7db1502583b0220f7aa73ebb566b56d37b116b3feb6c51450b046d52e4ad540d34b3e7816b96047f55c163bc4a928c17543ba3f68f0a29c8bb01bd5491b949a580810558aed5247994ae55723ac3c584e892c4bdb75cdb50a4c9ff2ac32d0684dbd6c5b8ae29d15203e8bf497eabfef4076565821ec525c4278ccc9b30e483d09eec06229f55481787d0a01815f76805eed433cf8eda44f68245f99434cbcb3c59954344eecb1dde8e753340a29ce9797bc00d3a398432f33671ab68f4e83cae38881f00ce8bfdf0737f063456125fa734478c250ef40bc099b1cbf7b43d832fc3377a517c64dc8f7bf30a449fd4f343e81985f1a07d3ca513f97070ae38e382f63d29207eed3cfa9158c9a91b769fc28ea831237276204bddcce5f59f4617e85abc82f8ef31c9bb26bb22298c93cc42bfcb316daee3ce719cd77b8f5c95c06f002a20a22e3bc4549d9d631b005d4edb7deaf93e2654ce442eaae8fb39f991af9f6c72a9240c0977f2e963246c25749bfbe0acbe1c20786befbff15eff5e306b53a220ae2e533e93941b082446549fd1707ca27f611924b67c0bc7967a0fcb8519a3c19f874bb36691178121614700940ac9a74e45d6230e89149faae63cb4e1dcd8721b809b758e8d0f25efc536529f7c323df535989fe9a2b491c83773b5ad7b834b8d35ecfbd7266283742f9ba0422612f04e2c981d1b4c6523a5350487eab8c06b85e82d11d5840630aa0c82cac2dd4a991c0bc1a895ecd825424673efddb0be1648cd66aaf5fd9c5e40c8773306fc2ef9619e5025c9b5df41bc8f7d4af546292db9c9f291c346ccafcb17c3cfe5b4ca3481584901e881103f28956a9a1ab6b08d13100c4b52080b1cb1d655959c34109674a847932e83eeee195e756f669055ba47e3d877fcb35ffcfcb3df3f67267d6d01a94b0969db6f00d8f3b06f1a250ac2c808e1a3717135673027aa080177764a95f1d9820779e58cb4da31e92eb958cc81e710198a9e9f1528f3ad096115cd8f55d0423caca09f7c27a63b488c199894fccc013b4a3f0b4771238fc44e68590e3667dc39eaec7b38671c9c7208d68b68d81edd2986c8a7b4d0233de3968ada2ff61edd49d374d73a17fd20412fda0e0dfb0e75ab1a6fa3095ee38116979c5025d7c8e42cf73eecec2546c14af509273adb530511f6cc1003c48808a1139b076ae9fb222bdbb9af4e2f0abad94985dfa725f08d45b3253fb11fdc174887921a646e9560256975f7168901111d9de5988adeeb3ad0846f1a15eecb2d383103cdb72c28bd7c065b9ed2a8ba9be83882fe48436bdadc9b8a86e51417c71301de1ed99e30761bf9c4ceaac432d17600925bf580fd72105db10be880a38d7f2b29a5297f1fc4bdee2c3f1a4ff4ce1fa861cb5c60b1ec856b93c68c3d7f0c560e26c88875d2d3bff0fe18a5c50b3453b7fec6790f3748374b66ea7c162d2219772bb242e5a904b14b6255e81e224ffb42e871cbd827b88489a847d70b6fbd78ec1e7c08e70388b8ea01787bf28108411f743c0c8d12b73686a7b167d2365e22c24f40e2d65c6e964a521649b3c77efaebf9a14c6c3fd2e6316ab718bcba6c1ee197928fdc229f8a31dd729cd54adb95d8daf317b0390474efc74d7afb03ed65288aa522c1b8fc19c30efd7917b2fef4384642a648d2ce5cdc8376c8e002f775f4f3c639d8d9b889f0ee29de35d15282a96bb002fa62636f05161bad1b65d107a44e6f2dd1837372cd6754f25e4f01d7b47ce2f75890250c7aee2c091bf1987eedfb6dcb8fa1a8c00aa5d81f8ee641e702fd9e26c16774bee67b1392e9a3f2fbc7d4007d14da9f2bd6088c5c35568f0be16c314519472b59d070d196bc12620706f9dc46d9b47061ae627da3bd3ec9e1ea6de95e57a503f2ee9eda5b354c2fe4795c66aa4c7e5d3d775b69eb6a03227004d29f1f3058c665fd3bcc8d8687e95083230cef45e2c1e5ceffcbfa8c8ad38b5cc35104086f3a411eb052beeaa613e109a74ecb366223592a58ea02368b67648877cd09e83f62a56dbcac11a00a90f6a101a36a36b873219aabbc3706b6ebb1d4f4dad594de6c3290ddb30d01018d910e692ac3bfb03b0d90ee472b63895580cc05edca70d6ec1707875daadce8fb1dd759dd9cfbb7a98b9762df815b609facc6f5e41f4068dbc4cb80a4b3809d0cdf14bc4d94087575990069190e79c03ffc387eca7976e81d5877bf635da5d5be6874c6776fbffef1d255ce0490c419ca47694b534230c401380c5e3a1384920893f57882c2dcaa89673bd2335ecd86f9e74e67f00cb2017f0b450b7918acbdd0eaf3af69b16042736bae8ecf591e76de9a784cda7997c00a0bf61f93c4d037a488839c70902f110e4aba4701bda173eaa8844cb2084c3b6e1a1e0a94c29d26d20e66d233ace66e58042aa69b2754f617caa2401639659adf65d61b1fbed5c6d0c7dbf2a2261e4a715be1e7b64ee3775022e8b2d226118d4b3bee40e0e0fd33896ce0d9384ab0e22f7a1ba6ae467343ae281ad9bd384ba7e7c97c9586128e22691243445ffbe9fa0851a5a3e6f731795e5373afad96ef94d9ea565bb48fce853f40e82da66ba4d253dccbe9965cf05ff86f002e8120cb461993fca9bbfb43d6e8789b614502623d9caf371947f4307a7060101fa71fe4bc80c893ed7b194cf499b92979de0066fdbbe5123419b290795c74a3ed6603c156a8b02e9af0ab3769aec6958b9d7d84533a28c2915010470218ac679e874d19e690ab05d16bee3316dfee717d35eaccc92cd09907499d9b340bfa6d4557ef5a71766320778b9cc982dd080c9f9a5be1c496813a3ab7f468d80aec23933cbe6d4282c89c9b8e6df208d78a0a1400e0664291783c5a32f6b662d967744c68b6580d4993d1a9ab470037d2dc9aff5dff15ceb572e797629a0306a6cc7f8c08cbcddd8d2f4c81c192170dffa0d189e97541b20bc7c5a5a0cd070a8aa1a218d0def3fda61d382652af6d1bae174147b7e8861ddb2369a0840c0c67a9087820dbeefafe96fa188a2080acf2c70c4eb6bf99e853fa93adae45c8bdac3554507b9b5a9ac023c057376602b8be405b27a7962bf163c7971f40b4da961273803ff4a115d2b888a5ce7b11871e98bf00ded33f9d2004a62cad884069627cb9ab8d3bc9548286aba93fd38efe483585f412c280d2953e9ad0ba29e6ec2822f14209ff97cc43c2e2e2e6f6c609ee08716f6f89a2e627f1bd09599eb6fd464378f7af8934683c4a78ce14a1a5b963811d81f6928778f047cdfabfbb0a81497e00f682c85887132a0fb4e9b253a30adb69d7682f2795f50c496120a12e1fb15456a3f5a9da6c38bc8529dd7d9b2be23ad1f6069313006e6d4c998a0397d7fa9392e9a2fb92814ca62bf13c28706946b325788bdf4d34665813031cde4a2f20b36d0d5c5e2974f3bf363c00be7750edf9ca18faee7a84b82de02cfa47db24ed5be0c1146857470df00172e82ba16461622faf5b0382ef295915f94868a0cbf8310c5d2400f825f813776b719b9f3ca27655c14e56b0e9cbc7bddd929e56b880a7e66c443dc5c1f4a42b564806e7c8f5cd947a127069a862cfbfd9e84185d670341c7dcf78763e860a8b46a95d36d18655f6d13f1cc0fbc71b434afda4cdc4ad87a7134a65b5d1316e74feb1d9ba701d73e1c0d1046e0db71c1036f121d82875cde2b1eee4f51dc3b2ba1c1d8f63a27018b8b5e71755b8c396ef615096aa0067392f20f25806f40c321ca94a41ffb60f4dadacfd1d4dba8916d756327c3207bc870bb181d21bbc0f1bbf47df0bf2f34e6836981431eac3b82a16d76bbdb22122325805d32f44a8fb810a75493b582f8ff4ef27d047e0a23fd0cc6b915f7a50f0cab0b14e0c3039769abfef8a7e0645122152b2cebecf5f0807e603c39c3c26a5e18ab1702f1a8cb8576c9a8b6c2c58d555baf9f04f36e225adaeac15ab78837456ea44b785f0210a684331b118df62b17e804b6430b30399f216b0d29864c6062a82d84dc496d3ad2d871187c6dd0348c5c5da89f290b4dc736882535fda7e200bff079878632b78a9899755f53e1ef263a88e809a1e0b825624f8f4c7bc31071f25aaaa1093b3f55f1759d3f767a7b8d59bcef24b14025034c09088296be31d91e546b0c54a79e328fa9229a2a12ebde204f977141dbc20b2d9ed4f1c92acb808d1ea93bcd3c5e9ec8516e5b633f6bbfdf31ffeeec4809779a87b1ee19f392f82cc37979850331bfb90494222b364430813912d5cc6a0380b5e3d8dbc8eac08f8a95aa218bb28f7f5a0ffb10bfb34ee5add1bf5d59a45ec4a03abd181f68905f95bbabbb4c2b2ccc344af5632354145b96d6b9681e1dd3fb77cd4278e448e3697ba130b784f66f3a18bc24d6f922a31f67f145606f5af772097e13e9b7111de0695041840de24bc275699f0347b4982e08b04d026f23d02c165473a49d623f902e11378040e5b3d6ea2528b0d3d5949315216c1cd976a9a91548cf5cfbc94e752e72b7795fe7edb1d49734695c8144c2996b8cbcaaded87c78203f9ff3a7d6c6b4b784aecb708c54d1dbc3fed63bddd7631325514bbb36110973150290ea7ce40b7761fbcd47666ecf09aaa5d7e5d4ec86f93c28804d5c3c5a506eef7c701b44874ea73017387d70f867d8a270d5ed911e91837c4a52804435cbae8509d1ff08bd9db29b63e3ba9d435d8e2baceafdf28202e57bd5cb07f4c897f6f612bc6deb059c4d1ce42870a26ff5a0f775453aa940847552bfea0323a94ac5e2e5d4f30c26a452560c55f2ca9fbb02d5fe0b16f0c5fc2d0b19648106a7decfb7f60cfef0699c5447f9c337a90698c189b8c4338ec464c00b93c488f0e76f4ed1168ad32b4432d8afee85f0e97e30fa6e36b0b3784743c6f65503747362a49379c871ec2dcb80a4b2f68dfb8fdb193ec142f8dd634ff9efffdab1ede72552b63bddb5606dca66fc07d38e4051a05ed481dab9ab13b7d96c3cc872df0215e2daf6907200f8ae94b242a5d98c4bf92a50ae01e682d273a743b10735d5308f2b2da7402d4120a0baf21ee3dd5e658fec7fda21b1a559c0e625e56682e421bbb2fb6d5ad5fb9a764228fa88bec93be5f5c58aa8bd6609a84dfc221d654682226cf4853d3a05653697c591e3e2ea71e60edee124a0bc3c9f81dc09e7a90d8a3b479589764aee2592a9798c912ad9b1b7318a42b69ab6543d520c5a30aeba36723eb2bd7e77296e85d759d8652c88e9d5118f268addad9e6686118add0a64d3293aab04caa540ed0a98baf7a8e4dff0fee755169cca885de9288b1499d1dc7a45d5cfbef21f9251b0ebb9e16504919dd192ab4707c95452cc6b59ac447ddeaabc2e7801aa879badeff7177893d813e9dfddca53ef4cad8db703e28d1dda30c2ffb1b419a71b1d2022d2a502f8a6774ab191cdaf804221544620246952a15be0aa200b3728a9acfa0ac9827eaba05d1ef170072d7951044f0488d54aea2ca40dc0d5bec8e57d2b58745267362457236e3e23638159933c770359b120556dc25239d9eba82f375265f67e85278e816a93aeaed2e61696f74b1325a4f5067369d1dc086b005d114a2748b3cdfb3161568ebc33f17e8093f0affb3f7fd08815af0a6b0426b908d64607fc81d0c4ae5fcb56584ad535bce8d0da7e1fd9f54e5db24c17af186624312989c555c6846823f3e5959c143414a99ac584fe96d63b38b5903a0ab875ed4f019951148661348beb24e35d5863b266b2821be78ace129a658ce73e1a38920df9c087d080a551d3a7c5b637ce5d3b877f7022a05dd6b46a3941b15e5f5fab98e59538f7bc48f255400f095de18911147b26f512242b82ebb7940cc834576eea198e0f65ab2c4922b1c1d6d73693bc86c273bb71ffa21763c79683f2b5b6f691e60d8ba344bb63ffd2c8c651b0000f30dee6f5a74d8fee928812204c815427ea5376fc3c4b22f1c11a85514bf75fd9ba208a9af20ad1abbd3180f7c9207c924a6df203b8c6b2bda29fe8a7efd55c4a1fabefcd9d16bdaccec0f6f602540efa1b9e9c5fc77aec0801d81288473c43cb08989fddbf32501e759dcb72ed622207eb8deaa063e198cf4755db5cd59f70fd6b7950294f2ccd0dc3d2baed7e39f63a561423208195e87d31a4b061dd9f6841ecd8a277fb6fcd64615495e6af0f1efb1526048f2581f7b0a267ef352665ede5670fcdd98d6f331a2887818bdb5bb695c2eb31adc3bbc49bb2a64480588b5d6390da0a480ffb8b6d0fbbbb05119b39832006b4689666bab9f9c662ce021877ca019a3f4f85b5f1154fe172d2f2069a79038eea284729bc68f90fe6847608d6c3aa7d95c05894b81e1584b7919f4df0e14783a5e55e01b55a558b945aeb448d533a55a1b8b3b319970e2a2d6a741c66094fbb755e306c001d5256929e718999a08e1ab7dcc231e2e2927caa8416339b386d3556746095529c776a2bfbaa4de90684dd3f719be1e94aecd17f7326819629f44edc76e0457ad40fe97c350204fa884fa813e4a6f649def117c2be4c06f9df26d9d69f4672c825e473f6a84e26c204ff652e6d74ead69428a1c1790d1452f639d3e8b01f15a00981c863f4beadf8e03b9249ecc020f9c80e34285b9ab15113167c739b7563ad57372650f8e7e1c4cda384607d7dcd1ddf520c18c9485910550665362ebc928f647a94c1cad6a71965a4c017cd303aa794bb254205e19a7319ddc8c95f9419b561f7ed2fbde615c06819b50bb15d13f2c378d74e91df559ad81ae1e0d60c00cc03f108359664f7f859d5a30d0e566f01fe8de8d6d49ffbb898888086d5ad2b6947b6f29f79652cac80a850bf40a964a6b6dfd9d41d0aed56ee3b8099381519b00683e7cc8ee44cb94d3f2a769acf22b4d8ee3388ea31ca59e8abbd4d1eb41a5b970f3e8c9d1a8e7a7d5f1e238298d91a3dd765abb8d528e7b766ea3996e418a169ce818de2d77dc2bc78538fa06af479c4247e26dc31977ca498e93f24537ee357f1316b92d8c32c2eed19b61cc10be2a4bdff3df84c5982d3e1a6486388463a27c5184b5dca55adcefa763078f68c510ac8610e02a180c775443838915a23588b2086999d371c668be1c128211083e9a334d6f1a6eb9d18dce8cfb8db676555659ab942ea1cda3b90fe82db4394896d119c658e15311e1c639b528352d462dc68cda5ac3cda3ada4b4a374b335a31cd7719cf6bbe12df372e696ac6c6a91c56db64e4dcb322d6351aad19c9d522d9c915bc194a732cb68cd42fb53b5bc94544e6ece4d6dd5e8dc366edb4422f998493927a5d9a434d36496519ad14c4fe7a400086d9ec849cd49f6f8ba3266f32b6599cc7f3d46ca171f01eddacc59b680adb347de885bd4536e0867b90013462b306ddce7024c180a0410be528c0e74542f088a1313854feae869bf9a5665ad73ebb8cd4ed60fedf59a96639c5c980c4cf3885668a01ac02a201846a6548b47b4a2b4865216a419e37476cfcbc9e464f245ecf188556c958c9e3c1ef5db1a328f47bdb5dc8643fb01993635a9516dcba13d4407da66d7a2156f0a3d31bbbca419cd92db2c3dabd22c9b19cd364a9fd07ee39c74679bad99d428ed414e0f08bd9459949282e1d6c757cc14bef83be58673a29eda6518731766e76c8f193de7e30c6398f8e2e5e9d5661d421f6766d159298dda66b58cce99f5c07940e8b3df897fd8b419d61edc18274061c6e4136885427ddcf5da0f35dcf4104d4a272e8e37a313247e0994f0049e38d133c0101c2f04017ec4d131af97f171221466eecd290a43af9dd6bc61263ccd09a19cef095fd41127e25c5169c228d6214563a8142ec6d2eb51af1d6721d943d0e1e0f7a00911bd02600f48a7b7b072386f20aa7dd5b2d7ac76dcdcb20a8489f92138777a3ca2153c221478ccfcb0a1ad357a0b6de8e1486d509c98695ab555cbe6f7c6eab8b9d95b9969cfb6d95911e3467560cdcf001f3f6699649265cf3c9d659565332944db67b9b26ec765676d9994b6ca5aabdcbc0974f1e8496088951007de31ce39f08e1f69cc8e3e4688a28ba0ba0271220743c020450d25384394243bf0a727802112883bf08f59045dc320f53b35dc80eb097a2173889d191637e609df766dd3340f5c1ffb8a62a0f0b1767c64af1740dc3162032e5dc320578ba1c2a71d88edb6fb109dbebf9ad783ed310622282145df0e48a7b9b07b05b2bd7b3dad5d67e50d432e66e7cebab59317a2ef6be6b84cdb58b67695b3db76ab01d9aad443acb4466e7cdd18287c1cc519d6b4c73081b9d0b95b59920bdd9ab7c8b244d49b38e4d1b12c17aa211dcafa11f27e74dfc22df516ddfe561c03e50b127a86639ef0713da2159a88ebef5c0e22baa8fbc2500e79bf1be67290ee99c7a3fbf6e8edd0b11012c82e7098b80f418743bf43b7ef41d33ab20b215452c6400425a48c81084ae0d0d0d438efa98374b71a08cd59f969515c51f7db65cec397bbcf3c20d9b9700fd137dca2d31865847eb328dc5edef752d31d0e390f7b1e1743e766a4214fe370d6a1de0fbaef2b8ffb799cb96f61cc41b490d5c3104f6a52e8694923d2883cd2fab1a9c1f8b3008599a37f52e40d5944d254d6aa152299b124d4466e61f9c87537ee1c9051e87a79857823f4ba8228030a283e1b3d22b8eb6cef11a5d4d6723da2fe5aee7939517fd6da9097f31d67fbfb7d4f647af06d974f74fd26ba21fcd55b60d6399fd870ab2111411649fd5eb9d73b7a6dbdba5e2fa3cc18cbabe8f5120a190bbd5e6241de6879bd9442def8ebe516e28df77af9835f2f93983672bdfd72d41fceb9bfad2c939041413d73d3d1738553dcb6649984a4912e9034d588a4a1e28ea8a6a3d7914194650b224d7d28cb174423f22b7d224925de445d6da6ef966db8fef56e74a9bb9128d4594f52a14245d6cbc1a9d6fd4e755714baddbda19c32613588e66deacb619cb7e065e9824d1a99f2659944fde580a0e8a8bf4f4e915324956dc341d4863462a4ca200db471a36769c495e5112af16bc9d2084b9647be502661ad0fec48fb01492229aab9a4437db894a56e65d9822823060ca8cebb1f36ddd612f8d2cbc12ca6d605f1491ea130222eb44ebf2ca4908c4194512f939034f572ca141d65c88ce07a3a2d52be16cbe2944c6295a50ba4118b33b65b96262f475671799605db166c5dd80245a12f0cbe59064d989515c49b30e822f2869c1269bc9c2d9455a29793ba8ee9c2f6d8666c5f602ba3c4a7e9e803f4a3334f476a14f5724e9f5e8ee9b2c485a48b4d00389b17d866297b4ef0c923b287c7d4d2884c023c0cfbaf860657f382631623b31f65980c078bf1a22e815f8e7a24ab7c9fe8c789de928bafc59a33a05c20268c5ea607df0e9b96055a51f48e41f2490c924dd8c0e61829df9e4966126a639b30188f1e6f23e688c11ebf63daa079bc0f330663c6e5f5f808cc58cce32d3063311e3fc48cc978bc068a983199c70b9137f2e33d206fbc3cde087903e6f133622c7ca4a9df252c74fd66f188326a8dbcb1e330a20d1f2f62ecc513406170165d3369fb6bad7c27b7ac43b74d863c2c7584e86db46d579371bb611b6ac38823882011b940020a298ade65723efd1c3baeeb421ebe9dd4f7fb7dcebcefcf3da21e1d636ba5e4381d2aa5873b4e8674a80e6d21ee232c121d94a1efe5f0b5d65abb75dfb0deb687b20ebd307d7f6ff8dd93f9f7e8e5c85cc7bc243c12fd96b00d8f7823a31065ec9037241449e383da904d906812e5c88f28e60adf967794a31ec55cc7bcf4f6dc2b92260a51284c9541d20a85a9bf2243268a4b26627b11dfbd3158c81d9e3b9030752ee1881174ad130848705f08a304f998416f5ec85307b10a41898c83b65e78e5890309abbf73de20260c79de20d2d4cf11c81b369c398834f53224f8b6ef99045fdbf0dee21784c49ba86d78b31a7fc6ec72f859aa95e2f0a5e6bc1e501a87b77a42a28ceae5088932ea1774f78b313ed6f82b31f16dbf56cb6296c5f80a712f792a0172c36cc2b6db709bb02d3462c2ea331833144633f5959c8c34b405983c95489afacbe1c0c6501193a79238719699c1177d7eace8e83345121df568142e21c3678490a7e2cdc6e1735786cc1766818e62728c4c2586ac0f3e2ce28430c25ff7c48884c16e891226855460c2a5d01a50f8b4a1218551def7292e38450b6691592267f4bb22e153961a66758f5db0df124665a969602dd46fc6f7be8b328c5f134e65a967f25c426160708125f5cb72c2aa2cf50bcb6d41e1950b095aae2b855959ead93363af70de20c2ea49e0ba57e15696fa4513284da0fce61566c95287792ab1728574d245906fc8c22d596aaeb4956ca9749c655014d3eba3e96720bdc6298348af290d82df2a781b4619323de17d816f0bbb70206992489a7a1d33a68aef85270a63acf0c98b3c1e52c396788e9ae5e57cbf2cd8c5138531517cf2f74545df9999a4d68daa97475cd151ac4822f286bcd2243a9137249448531f65949964a79258e1a289ae975096a0ebc8f529d0d032c51a583a12b4423afa00a940b35050f47de99ed0170627047d61d607532cd1370616651925be4e471fa025b4e6e948bd7aa63af572529f5e0eead2cb393d7a3923bb71ddc51e0e4d11c8c032a0161daca1518345b527caa89741e29b4b609288f28c01c217ba9cf9b4e82498cc9dddaf0cce66ec6add37c9eaec153db344210fdbdb75ac1f5c97b70c26540ab562ad5a2c2dd75d39cc2f97d70b8c179818303132313334333539d4cca031a346ac868d0e3637376e70ec8023878e1c383ce0ecc8d9d1c3478f1f02f8e1c30f3e0c00c800821022081f7c08912d5010d94e01225b2a40b20d1a40b6552890ad952cfb461f48024d27132a855ab1562d9696ebae1ce697cbeb05c60b4c0c98189998199a999a1c6a66d098512356c346079b9b1b373876c09143470e1c1e7076e4ece8e1a3c70f01fcf0e1071f42fa6a9af68d3e90049a4e26540ab562ad5a2c2dd75d39cc2f97d70b8c179818303132313334333539d4cca031a346ac868d0e3637376e70ec8023878e1c383ce0ecc8d9d1c3478f1f02f8e1c30f3e00fdfc90ad131fb27dd243b65004906d941fd94a9940b63e59f68d3e90049a4e26540ab562ad5a2c2dd75d39cc2f97d70b8c179818303132313334333539d4cca031a346ac868d0e3637376e70ec8023878e1c383ce0ecc8d9d1c3470f4f5f4dd3bed1079240d3c9844aa156ac558ba5e5ba2b87f9e5f27a81f1021303264626668666a626879a193466d488d5b0d1c1e6e6c60d8e1d70e4d0910387079c1d393b7af8e8e1a449134def235b243db24dc2235b2539d92ed9912d130964db44967da30f2481a693099542ad58ab164bcb75570ef3cbe5f502e30526064c8c4ccc0ccd4c4d0e353368cca811ab61a383cdcd8d1b1c3be0c8a123070e0f38585f4dd3bed1079240d3c9844aa156ac558ba5e5ba2b87f9e5f27a81f1021303264626668666a626879a193466d488d5b0d1c1e6e6c60d8e1d70e4d0910387071c8bc41ee121d72b38d9f6e86c89e8c816891cd9168940b646b2ec1b7d2009349d4ca8146ac55ab5585aaebb72985f2eaf17182f3031606264626668666a72a8994163468d580d1b1d6c6e6edce0d801c7d557d334d3c9844aa156ac558ba5e5ba2b87f9e5f27a81f1021303264626668666a626879a193466d488d5b0d1c1e6e6c60d8e1d705ce162875c8170e43a2500b952b9916bd04dae552090ab952c339d4ca8146ac55ab5585aaebb72985f2eaf17182f3031606264626668666a72a8994163468d580d1b1d6c3a7d354dfb461f48024d27132a855ab1562d9696ebae1ce697cbeb05c60b4c0c98189998199a999a1c6a66d098512356c346071ba09f1f4daf43ae4e6c727d62235728b15ca3d4c855ca0372f5f9461f48024d27132a855ab1562d9696ebae1ce697cbeb05c60b4c0c98189998199a999a1c6a66d098c1994e26540ab562ad5a2c2dd75d39cc2f97d70b8c179818303132313334333539d4cca031a33aa94d68e48a6446ae490090ab921c725d52932b1307e4da44967da30f2481a693099542ad58ab164bcb75570ef3cbe5f502e30526064c8c4ccc0ccdcca6afa669a693099542ad58ab164bcb75570ef3cbe5f502e30526064c8c4ccc0ccd4c45528fd4239a9e266b576672ed91912b11995c9188c9b548037235623a995029d48ab56ab1b45c77e530bf5c5e2f305e6062c0587d354dfb461f48024d27132a855ab1562d9696ebae1ce697cbeb05c60b4c0c982b5c70a1e963640d08266b536059a302236b412f59abc280ac59c9b26ff48124d07432a152a8156bd56269b9eeca617eb9bcea37fa4012683a995029d48ab56ab1b45c77e530bf5c5e403f2e5973f2cada9317598312662d4ace9a940564cd27cb4c27132a855ab1562d9696ebaeab69da37fa4012683a995029d48ab56ab1b45c773969d24443e2ca5a9296ac2961c9da9256d69828206b4d64d937fa4012683a995029d48ab5cabed1079240d3c9844aa156ac9586443ba21dd1f4ac9c5d5965ad47953522a9ac2181ca5a910464cd48965d4dd3bed1079240d3c974850b2e322053cea69472468594b3203067551090332b59f68d3e4dd3bed107f49333275fce9e8872062594b3285ecea41c20673e5976cdcbe19c34419225c994644b322613466f809c354161a81698e86db9d0f4356748268c6647268c4af1edcc88912c89782335a749a96d59966d9bbcdbc5d76e526e55ab75b3df3ace4eb9f5e8f82204a221424c494408a7940b52288e5a906ebd664c432dabd16620e2eccf0ff73a859393a354f6abfbf53ccff33c11ea3a26927993b05e7f99ac5e8fba3e507ca54d8787d42391089cdfc7dd2be5fdee95525ef9c9cecb893aeaebe588cee54e74974c58b5d65abb5dcede7bae095d3b8cbd9ca845dd4507d2f6fbe5c48c79afbf4dae93cefecc588efafa783f57c9b4215a42618e4c1ba2239789ae76c99249f5a53f16c852118553ec8fe808b56181a8e82a3a42edcf84dd28ba729b2622b340574abc895eb0a2398edbb6bb7ddb76ef76efbddb76efa3cc98fccd6ece19fd3ed15bb85b7afba51854fd7462ad7e3a9d4eabd36a754a653bc502752254b63f76ca84d5d00281f85aa0d249cf7a66acca177a6909933d81f85eef5ed053e286be16ec6516f8a84ffbfae89f093341d1a4548bd4ca768a677f2c906994a5deec14fb6381be1fd6c7f27d2d9b7595ec4ff6e58dc1aca7bbf2637d210f4b294512e39f227a8b9adc29a2273362d868501928151d2fad5079edd8fa6152669865a5fae9f1972513618204b48b2e7098107139060a9c39a93271971db6215128c47dbbf7de13c7711b66c9445a67b9f438ee75ba8ed9ca11c77e955f9baa958f384eeb5762aa5f3fa7fc92d15347cf1ee325295d732867b4e24dd42c39e2e0253a6a8d71d789ce12853cdc755db75d4ff3385546dd94bbaf72eaa395b5d6da4d75d36632dd9a54269589cb47bdbaf0cde33aef940f7b9deb94b898cb324a34c1e517c6a42cb52887973468f82c57825198fa0b62ae0b44d097b40457c2dc7d821e853ccff3501e17f2686cf15d1d7da88883e67e4d98ebc2135a85b7cc1a3da774eec354ac4173bf28ee9d97c36d96cb7fa53c4ef378485d3ae8d1d8828a2b34f7abc25c139a749247630b2acea0b9df9256a225592a952217da20d0298fc7d47f8157e5709b65719ce7b570c671c75e0eb7598f850452b9711ee642215128d455d1dba66b596b4df385055b0bf6da5e63905252b971872fdbb6eaf9107e5b666bad5ae6e530f178c8a825a192cd18cee49e34de9820dec87803347f26d0fc018a3730f8e20d8e322618e48d7803839b4a7dca1bd58b13cb296f78515f8aebe4017ef5a2969141d6d383836f3da9698e71f2451f2b51280faec4b44a95372895b24a9a5197d0dc94e8244dac42de983179e3eb622093f5f5643c588c021303eb81dd2364a954b6852bf393663945df7acd180d63ae90da98a099966d4400aa893a72b1d684dd30dc6ea822cdfc9eac5494315372cada59b1d656cd22b15a566badb53ef3ecb32cbba6691aa55956b56ab50c084a6e7ccdf975e1a35a46a595536652a35ca6ddbbc9aadd16d02c35a53636ea446f189d3234f84651c6fc94f8aa566badb6d63a7b4c1d81f0c9cff750a978a02317b63967cce60e2c0c466d6c35f5db75a32569ea378b8e3b20719c158a0bb57eab4a45b36b2a954af54ea5e2ee8c1e6ec91b8f34d7aa77f85b56e10e8a1e18257fb7cedb260c1f775dd71de71ceba1a697c38978b0da72a7ca30ee2e3bd5b76c8f6f459907abdb51acca5147adca52a3b24c0d0d224cd81f719cd5af9c1a2606bbca34660da9af8489e1db8f1165745df0606051c6946f5fd7bfe8ad9e0d63a2f84c79bb2aa7fe9954a66f4b95b8eda87cfa284b4d824dd8a9cad830a60956b8d0f29bbd22d3d1c70b26d0233ae7fc80a822071116f5bd4b601006f665de1d61db852ef497a5e6381c76d7416db8090144154b4498a86384b1bf20b65946892e5cd1f78457990813acef519b7ec47198289dcbacd1a32681d731319e54479f2a4a38c7de4d86682814ea640d6d10e89a09ab37793ca6169df0b66d291c338c55b8e255d4a028dbb61f247adb8c6a69de0835b3f4953e2d7cd93fe79cb3d3c2a5db763fef754cefb788be9ea7e19bbd67debdb6594a29a534ab1dadd5669bb576089877ef621926d55229a946e595d2daad6a34bbf55e8df531a3746b54669de5364aa564fdc8ae5dd3b2ccda70f36413d62d81b32c7cf4fbcb3e65a64996a5d67634cb6c4ec6c447bf37a9359ae3b6859b476aeb6551c6f472b228439e521b465ab23c229f4041c31528d0a277afb1c9a29006e17eaff480e07fcb98bbf7cdbdfbe532ecb5af6990fbf920dcbfed791c0af5e8429752f8e443efb40c83602fdcf71b6569aceb6f9e35bd29b795b55a314a8b091bce628ce223db708c3e566e7ce10953af6083a7e78c5ae893d6344d0bb709abf531168bcf3e3a410a3e241da3380312ad9dc313567378f2c53a61b4d78a832ffb84c30d8310a3e83ba3d0516a31587c5167b01e64c29a49c326081f4c66da885170e86134e24da471859e73c6b66b6a3a69d431afb7ec52aa88e8e883061ae878e98541b267fdd9c315a38c4c079541c26725abe28909a6e8193a3e39e2476b3e6440320625b741e4880d87c8111fce7d7722f8f138b7660dfa9b593107fa2eabe20c1cb6262c4cc51ca84c922f7bfdf6cc6ebdaddde2cb1eed10556f2107a8b6a756d79a0d417546a17043795b1d32031257946dd143f33e4e1d002498d0b1d1f5dfb7c7831edb68cbd24e8f6062fe7badd1307d9ddaf48d3236f86eac5a164517bde678510e82bfbd8b9c68abadbeb532d155577d370d89d6b4a66f970515d199cef4c5f48aa69aea1bc2a1bca70ebd8a9e7a6a32009141ca957a45dba02a7a3e5486227a3ef45ac2d2ebe1dd72797ee3b887a27ad1d4389c77dabc6f70e819468f87f719ee7afc2f13f97e04133a3b687b1db4dee8216eb4a8070e2db3eed10d3d3acafb905106becd33eaadfb766897ea9dd166fa50ae791bb183b541701f793fd8207ea8ba3ee405c1ddf37ed0743df682f821d3f59d17c40f34881fa6ae37a1423c42d74e43eaf5d0815090d01ea2a9a6e70ee43e742e874e73d5d63ed32c90b63a984152b411fa8aeab94c84d5dc3594c6af9ad33497698e6a6e6a8e08a9b92d3f1c6eeef21bcfc6c308cdfd867abb13ad834906263aea0868af87eedc2fcd60102e574b0b0b4babc562ad562a550aa350a793c9542a914820381a7ddfaedafb1565334604fe9557243a854226cf2b614cba17ecba11a7f1f77ddf475f48e4880f07f55148e48813cee9448e68e1984ee48810a7742247c070482772c40c0e782247dce07c3f8206cee85eb8ed33bcadbe97285d5f4f5533695929a3243ac1b9a510b20b9c510d427681f38531e620f7aa87792edcd711d03b027ab33d9e98c00a0f1cba86e11e423177bd85ac1e66106d33be9ddef4d5620dd51f51062eca604547118c01c92b2a201b61fbf9c871f1b1e372e4388ef3b82d9cdff6ecb7fbd6ae8771eee1715e163204c7e3c15dbb1089c384f6aa3d71e33d20ff9287c54c47bca19f88063149f8a68d9e3f9f7cbc8e496d643d2c98328f6d6a68a46e85931a194d53f1f4df13175c29736483e919db44a07dac40b25f7ace0c88618a8e3e62201206296170a2461fa17265eb21b2c5166ac86294182546f141a6061fbd94a9c1b7c52d6e78c26c67f8e8131b44644f768c4ba556ebf592913ee8888834d2d3d3d383c344fdbdb908a9ef0ff9fbee4478bf2177435c880b7161aeb7b9c7d4971b72435bd68c6f90fb2a7bc2cdfab1f5f4f4e8ed556b40b21e20b38703227bb670d773c06a7cb9a50ec249afc73d7743ee756aced69e4d0c576a99881791551f2db5ccb44c84d4dab32aa98c326ca5f264ffd0d91ed2034dbd1f9d06426735abd96d909a5d0b2fb369658b2dd410062434747cc2c4c8a6397de59d150c4434ab0b2daf694e679a1b585473fa56cdd2adda6ae9b4d266d6227165bc614da1e5afc61aeb6e3efa4cc182142266318429a46c51052b5041034e8c30f2379b4f68b9690a45cb571d3d1a5b4c61a2e1404b1a5b4c498196544b8fc6165344a0258d2daae042579106ceeb21a39441c748d375b5eb3a3a37d9755d174fa11d023def83332df28832e2e38f8e13568626516ac81320661326335a436eaa699a166ec0d399cc0f3efa3d040dd236344ec3700d201fe7a4f177e63db3d32ccb14201fa9f62054a335a2569fd5ec52fb90a38ba8b370039ea6a1119226a2200a1d234d0cd2b302f28610492313306d6c14081f3aa80d9b800923659435d346cd84913fe243c5121d7dbee00343479f2f2cd1d233c0109ccda7863c0286e0d850880e1650408108477a70843081011c449e84c006e108098196831e1c267b031b987a885b049d3d88165e2052d1d9b70eed427320ab2187a04686e041c1153406a8111d840a2047173a0b37e0692d01df8cd1ef22a4a6c7f9cbe2a3dfac78e2c89228b8218a295ac0891166d355d7304a9aeca587804df94f9bda1aae9454522c73d41246332aa5a6d51c69ea0c36fbfda5365bf38da228433e95fa9cb854ca20f9401d7dbe10037de7e3378d71df59cc7ee31b1b8d36c2e46dbe11268a30f2d325af85d9a63f24df75f401da42cbf0e0dba94f0da567047aa65a6688c38df3a4a3cf142a604841a7a30f156008f99078203a814f1554cc2b62ae00b53cb5498318a280346816a94214681015576459a6e91f99f4026c081850cc1543600d7f21ca1bf76e758b1ab61cf8f4f67b9b6f0c14b7d3aee8de6dbb9cc5f7de9b53bd9c7bab2365ec4f908e3a7a31244d85d9eb2d813b9167bbd0a341bce5223e1df2eee6c5f06092a63ea60ad0fba4628a1e15c424fc95be7b7f4d5ed7dd7befe96eddfd45e12d7357c7ec3a0f08f7ed33240a85ec0f136b8a22d41d39d26d9ba4eb64667f31c638077bb7db11eae5589b61cf7bc8e362a088ba0b12711cf77d36e788ee6dbf39dcf1b76dbbf6dabb1d91f7c3a0177597eda7f7ddd2f5becb1c07246f74afefa6cc18678f58241cdebe2990452269aad497fbb62ddcf7576aea85ec916d93e87a6b91849cb04cacb536d40410bc714b88ae284b0d7e346167f8ece53f3cc2f8b333903447244d1547ac47638b2aa8a0b9df0d57f184e63e8fc791aaa38f17a0e8fb7d3afa78c1097d51f866e9e408053a3261f51b5d44b2775e7a43db03da263982c4bb694371dcf7619ce262562bf72ee8eb8236d6a2efc31d1780f40e81a1272fb6b8648b8fdb930e5fce860146af9067e300a7ef0098bee39358e9510a02be8a3b4e497d472a31e83b56f977d4c2f57845f6c45adf92880d057c4b245cbea591d7b74ce245dc12c90222131d9d80f12d9bbc7c4b270cf8964f68be6594996f2945c6b7f491f99652c47ccb9f067c4b201adf92ca8c6f1904806f5925876f69a5e65b6ae1806fc9850edfb3c7e67b12b1f13d91887dcf2235bea791077ccf2476f89e48707ccf2401f89e4a6e7ccf2537df930904be67133c7ccf2638dfd389fe9e4f747c4f2839be6794ef29c5c7f794a2c7f7fce1f13d8172bee794d88eef49c58604be67d00fdfd38a0fdf538b1ebe271702f89e57623fbe698f8d097c5322427cd322417c5323407cd324800ce09b22a1408ce243cb2940017ea5b76545b2d49ab6655bb66d314a4e561feb0b6003f651025ccbd90b40ad644528a53e7c00a20033866f04a80d038c4e3f8406664c74fa226e849069e300a7a79b4e2f8bcc58e994747a99c48c81a70838659d5e2a519da64e8f3a4dc0e94f5da72da75266ac75059cbafc75fa17a7e1e965d08ce5d3059cc638853985c188bd30e094e674e6f4f3c88cc94032633231a70d38fd643263343ee3f4d3891903c0e9679319cbe1b4e609b5e1001d6c4e6d9c7e4a3163b1d31aa70f98b11d4e3f83660cc7e96795190bc08ddf40e0f4b467c678383d25326338a7a748cc983e3d2d32633a4e739c46e0f414c98cf938ed717aaa64c6789c9e2e99b19cd3532633b6e3f4b4096a4302a7a74d66ec87d3532733e6c3e9e99319ebe1f414ca8c09e0f434ca8cfd383d95326d4ce0f4548a191322881810a7a753660cc8e92995191bc029054e9f1120476d63aa20b313204b18b5e1c3a4339665198e1d70e4d0910387079c1d393b7af8e8f143003f7cf8c1870100194010420471354dfb461f48024da753043e5948a177e6930514594441c709f3c9828a16c18df3c63450d1231d7dacbc41476f099a7d7d8d55b220c128892c9bb07a1e6c5bf8c52df8a4410bec93450fd5649c686129e593c510b238a2f7f737d4d4c828a146260935324918a2a6660bceca109cc7f523d453e7722784085048431618639797be45c7dfa1d0795a74e8bbfb0e29f5101bc6200536cc799e959edf3b22f033dcf3afefd2bfef0b7da75027853aa8a73c80832674e10c2008eed449dfe0572d1629dc1d28c11bba70834824129dbe51ff482492eaf57ab1421d978fc680840812a8ebeca0be8fbe4ba7d3e93ba78cd8c10dac24813a08b27c9356ab55eaac5027f569a1008328608142a14a47859bf5f9edf2531886df2ddfa893e8a9542a15ee196eab640925b8c2057ca9d46ab5be3f0a75beda042454a10610045d0e86fb74d177eaa1cbe57a3112855b035243125770717129dd25dca256ce190441d65f843aac8fb22dc22005155a0fbf4b77b1b0b0bc98673d8a15258850fa9e0a37ebe1b7ebb9a5a5e5f417a1cee993eac0096d28c2f77da57fe1ce0fbfbfb3fc7f21fae9f3891b5c21a5543acf4a97be773ed74be13e3dfc66798b482412b5fe22d4695d14ee30dc320d5ff841e27bf1edfa4ba5d28b704b2bbc600431f8beef9324f0c1132b269369489226672801089ca05244c90b0445aae0a384eaf57a09498210729083954aa512e20220c4c00a69b55a79e04919a454014522913c00832ea2b8818742a13af082285d2801e8799e112b48810b80e00282a025c212182461e4e2e25297f4600c52bcbe53a3d1485362ca0c5ef0b3be5954df33954a7def8459198430c515d6f3b76bf55d9a738639dcd34a152734c93f7db748dfa252a9f4a904201894e0f4966f16d4b7492412b158ffc350e7ffc2dd126e790325582003cb7576585afe7dbb8e5faff388f4cb64327def84d7d9c967fdff633d8842142c6f85fbfbbf5b0fa954aa6ff03b843166b158dfd1393d3f0c75f259e17eb841c0440d70605ab55c670937cb59dfff6bb55a7dbb7cbf0885422d3f3de77c9e169d851ca147066af87576fed65de17e9df5edba8a44227d8fbec3172f5e9c4ea7efe87c6f69f929dcac707fc00d5674218556abf5bde3bace8eeb7f2bdcad9fbe595fa1503b8761c8f2ef2d2d2de769d12de13e85fb033d45ae10e5bfeb0ff7eaaaefd3499e771e91f69ef379543a7f7fdfd1799de52fa10ecbbf70abc2ed812648a1081a5c2e972bdcaa7fdfa4a34010fc2e7dbbfeb7fe3acb5958ced3a2593ad08436e0000ba87fdfac7b2e2e2edfa2ef1697eb3c2aedfa7ebdced3a25fdfd159bdf5975027dcf6086368811758d7d9617da783a3d1e8dbf4cdd2d2721e956ef9def1aeb3a3faeaadb75ae769d12dcd0a276401e8749d9dd35927851bfceb9b7497542af58dbf5b2c2ce7516996efd5ea3c2d7af51d1dd455f7421dd557e17e853bcb02164ee003d68a749d1dd24f6785dbfbeafbf5d19ce711e9c96ab55adf5157a954aa8985921f2c4ea713e9a7708feef2bd7aaa542a7dbff856b158e75169d637ea3b3ae0bfbf843a2ee19e4986f00429241289146e97a3be539f2291e83bfc3ea954aaef9d97ebec7807ff7ddf795af417e3704412a2b05f2f994ca6effc4d3a9dcea3d2271004bfa333baf79750c73b088736b0e088176a7511c6f8fbdf1f89741e95267def94aeb3e3f2d1bd7bde795ab417022568c84213567f3d156ed1c1efd44de1dee140e7e9d079443af4edfaf6bec301aabfeff084f4771e95febe47a3f3b4e891cb4ba18ecb47e106c32da4081661d0e2f53acf4abf525ffd35c20178b9b8b89ca745bb74800a2b2ae0c16ab54a7d650a8542a15068f33285db8823dc208638a452a9d40b1a16066d78428bb0668910def004d453592412894299d5bc411452b004d16111265732091518410425d70b05822735d0c211fcd4f7e82d2693e945a4b4297c844991e8a8af421dd4a7077ed2a0042d989efa0e9da5b5bace4ee9a2a350a8f3b46854075a10871228994f7d836fa544dfd1c12f7d15eaa4c2ad5d014a0ed2008abefa1e9d8542a1506129759d1dd3f14b2f95ced3a24be15e859b52610a22538caeb3333af850b8bba3be55a552e945a47429fc220ce3f3b468fc1d9d79d353a18ee938dca87087c00a5c38820e4010fcde095d6727f4d1c170732f7d9ff0f70e789d1dd1e74d3799ced3a24d1f000209d87085d168f4bd53431f7d9330c62f22a571b8ed39cfd3a2a7e860a823fa0cf728dcf6045f38410b7428749e950e7def68d7d9a9d71eda5f1661125f67a71389ced3a245e10e853b4b62491547f4f6422288e0404acfebd038f4680e6c7a9e081d7ba5a74717b1e9f922527a8631c2e4f74e769dfa1dedd9f70e8fd5f53b3c265dcfb3dae1e974f71d1e92eeced3a2bbefe874dfbe83439deedc75b86f2fa2ce2ce3af946abd640e1ddca8e3bcf2e71b5d1f2b49d027cc26f8186189119ab0d2038a854fea2dd2540473e34b6a1d20f3f23f58d9a3c613f64d6a29655cf0451f2b4e68798fce2237be76cd9c73ce99852529c99065d88a0c196aee174425a4a1868f0e4183b44022cc8864419911265f9d73ce39ed36f3376b6b8cb0929c97303d674c1abe999ab01d1386fe567c74c3f1c6472b818dcf0cf790cfe24dbce93e4f332dde789faff1262442dd7c9fefe2cde80624953e1f8a37a71b796308506b37fd9a72b6860f752a91c0d1270a79f876dc66ab954c5ad133b6994c9ff166bbe934de70373d8b37dd4dd7e2cdbde935dee09b6ee38d77d3b77853bae95cbc09ddf42ede886ebe9b8ee3cde8a67bf106bce9a17843bae9a278a3ddf44fdec86e329532099445f4fc284ba1e745bb027a1efbd0f397899eefb2167a7efbd1f3d649123db78ca2850002e80b1126ea3b473afa0059a1b35fd985b611da24371788620576b0b78c0d12ebe8e5ecaf24b3032851bb94524aa96956d3e88f8ea752e8781aa4239da223d08435e184922534097a8422418b7071a58a15a0293e523c81e24413254b8e4c18922257aa589942c5470a28519c68c2048991a80595024cd84f149b00c8b4e2e317691046c1bd77c618638cf2f69b599685f3dbc4818ee4b4709369c1a763c2c01014869e982deed4318a32e6e39c35dfb7e957cc2eedf14d985445199f8ea4322df83eda8515229a1e3c818f8f3fe56bca13bc919a07780df53bc14fa9994ca720a683d761ca3a5060b8c19f4ca69f4e5253d32fc5a8a34c2695eaa9d42965ca44a454a6eb982ad353275318a3cc29dce0b36bca41c0fb61e9f1000f9e9a320551a79f72d4d20b4519f2a75fea81f1069fe973f41bca1a0ff0bb8e796fcde04719fc6bc6c0832129343dab07f0f4604855f7504f1dccaa704b0dfe9453394619a7a3b229ef21da0462a185a637614145db83a483997b6bc642a1343dfbefa70784de14eed041d44f596a530e02c6509707b38ed788745428c19fc08c7fca2cd2eba38e9e29f4d24f79977e6fcaac1eecc183619052c822853c8668117e3d3ebe059f7d454560b8bb8744a2fccaadbc05c137c8165a1f3b6a68cc21a9d9bde10c47f4f6499f59ad93f1c1177b241124e40da9553afa5ca183efa359035ef420df65786ff47e5979fe4a6fe6a8bd20bd9d35c36db656f84a2ccf8084de2eb9d0a44f2fa7f5994ff7f236dde38efaf4806c97e1f680903ec33d445fefc7c17b199ca9cc6a3d6ad604f326ddbb9c390878d54399d53aebdb3bcb4379b31c7599593d905842562be43144a7ceea8175d559575d75d2a36e61e55410552893a4b274e294839842b96486bb14ee219a144a29a164326114cc5fb8e5bfd3af68a801e79d2fdb8c320df9efbb260c027e3ec8b7c177bf7487a47c2824a5943754e57d0d11515f440512012d6b482614ee794e0699b76239bfcdeba0a32ca7c62d9701a372a8944a1ca89a9e546a666400080002b316002018100a874422a1280a14496fd90114002481883a523a9688a260855118a49831c61862080001180119188cc6019d72fb003b9febfea45f2f352d55753a194ff4888c3c3e8397d9c9b0471493ad88bab2f77996c51f1100dc067d6d89f8373bf7f9960cfb507b068dd9b4ad0d083b27a1776eee21dfcf3384a8be9be67496a7bfdddd6404270d761e3e946f4e27480e2334fbbab4f8c0dbfebc245362cc4e6be2522d0fa8b1b9abb3cc99072046bc15f2c8a415b5014aae993ee7024d43bbce7ee4442ad1341b218acc74ac24f4f2f235f0bd23977af7838a3e1111b8396530f3ad8b85f2f1368edbe266a4f76affa7071789c60cd0a658eb6620f9b3f8c1b48e3e1bfea545ecf1324886308125255132ab81c5b450eb5c1da3fc0118dbc0c16f7c239c0b09fd5ee1014187c438674897e61aa826a47081df4865d92350d4c5aa5c7f408ef004de0342907b616686cf5f5086d0a3a11ad39e1db8159d62652f4b0fb4da74bdbb787feac7d8c577622afa1797d880f1b7e8269319735e64f87652051806861604adbbe78a001376736fbd2b55c3b7a2c467c999f397638339bcaabbe7a31c54993ab6a7b79a18b025783002d764af337c35c971983c832636174e53c5e61668f9b93d1aac7bfd94d10c059b496da11cfe326da8f133a414950a9747ebdddcd78dd387f7bcdc0c43c845fd625da32ebdf70465cd0fffe07e78979ac03263ccd115f3a2ec16f977789f9b6abebdd8fa1af06eb9886cfd1787d9c134fc34a116aa9ebff73c2acf54b44df2520c1fba6b5ab78bf185c11bbec928b312e54cf582c21d8249e5aa5d38e8870fea4335f16b727df10e650156dc279aa11d7ee6689491bfa141e6f1c40bed3bef5ffe7a68c2b3b8ccad9b78004df8c3f55c267f2934e16daee6d626fe4113fef03297c1df074df82bd5b9b589cfd0847fb9ce65e46f8126fcc5f5dcfac45368c25bae389759177f1f34101e7e8e355175756237470f5c78f35d3f0c4b1dfe1feddd0329fc783f06ce37d5611e9fdc3d38e1b7f70bed0bdb5267eaa47afd400a7ffe8ff135a16e261f68928bbe528d5469ddec551c4b7053c7d64aef5d9c69d65d48cc2411e3fb1e4776a2252b32b0cf29eb5e4863a1fb223210a1a8f7d809defe9487ad8b33d2787aa6bd55a034f30c84eb59d56dcf6af89c7c6fab8239e5d94fd4fdec910cbb697b41c887ea209f230e18cbd3db37af052d70ae0ebed0e59e19efaf637df0aedaf127d03862aefb50414473f7ea15b63f84bbe3e80ab6dc8365497baf7df00d3cdb4f0366d88d2e441c44057bdd1d566becc5e2eebe116f4f9b3e45eb5fb235677b256a1c33c3fbb2c0e47116e3c53a2be184d6f339273c62c2770633837da1ce7c52bfd7f549b393c72a543828a1ff02553cfeac02ac8ee326cecc61c816c0d9f77f5fc1903d2884eefa5cc2d9c43f073f8e808156f113541b5e55af42d904a14c2974cadf563899ce0e723c30cfdc41c905531c50becc649def40da082074ee47e93f33b9c919397c69d1df4e910f5ed7ab896e2a7df49b1ef7eeda4075dd30d57d017ea3c110ae754e0540547a82fdef37e695e7e2a0367d906ecba080fc5d48025f4db40e3820b052177ba0c3b0dbf8c6dac7ef98b26922236d23f34bc006e26148e1e05a6fff7599d0fbc73be29850a72f63c11a8939e068ad8d3c1dcad5aa5ee0c2ea16d058149300422d1c7dc37467ea0fa907529422a3b8e2a3924d3715bd10bb6da909c482e220bcd9afe32963a530fbf4ca0563d74e3da5832155e1a88d4ce9594b4945983aa21dfaa9195214c1af800235d1bcb74279aef9154d350ae3321054b14917defe7ed678e3bfaa191dbe936c5610f7e9e84313fa6debc4f5e6e9fb0c9fa28c395027233283c878b1b7e731081ee2d0b80e28f058c5830bc305ca9f9b7d39b3482c122208f93f88ad9ba58e50b2bc74a414843546aaf56d6d2c266504a30c80029b2f39ad227ac3db304f80f4ac4a97591838c1b39217daf4b55a026cff47633eec66f41932b9c09cc49a6731441b17cbe885963de6230504c24a81de3d82768e44fa1ef4d5329a0e657b6ed2cfdf3c9cb99ce67200967d4320db32e580117413a11b06426f75e3e3a030aa756595496c985eba0ebc456338b0e89f00e4ba0802f1a25438b410644216bc67193c5357cdcf26e664e87fda243244523224b657afa0f894132d8a7b347122aa637e33a8a1fb297441f749767968a0ccc66707b386b110982c05467723921d288d258833c9c1e8b99c91b01ac193c4cd2ef559b1d32c0bfd056cf32118c2bfd9b58f42d147a960da499d826bb579cb8762258efebdc550952c7e3228d9a4792beaa2b23798fb5ed2057113a4ae1fe6976038475644b073e012d39ced3a194b716e5751dfbad7724438d9d472399350a82bd5944ceeb3e1b3212e0722dffbe43e6dec9a58807202d463b5af1821d7f076f114710a34b17d3148ecb77b68f5588bc2bce8cc4a3defb0d8110aa780202bc8503f06c693aba5e3d825366595f22266bfd657bc47a4ba193b50d1afcecdd445d9e0da6d4c6dd4a0764f4cfdaf5e3871e5488496cd08c5c459bec1174e80e4f6893ba29f51af8206f65c01631373b43f4b18f08803f9d442186319620878d92712404940f6f91760e2f4547a3543f377e9bbf70fdd23184e98608d0adb714ab6b7a10fdbd782851d92cd3d6fef3b26241d14d17311ac65b08ed62ff6992c03c696e388204dd9d544073983659f0c1798b7cd81e5049080921a2f3afb932c2237eed6a73987a79933fcc857e3b18d3d6a8fc72469d146072e6facb901839057900f5989b0480099ed00141fcb3b3e573582d7ad1c9a335093d8ac318ac3c8289edd0f658b862d5d70d4e4e1a241591475c282d24016929fd972910b00d5142c5c2a3c2eacc05c78f8b963c60fa763d1cf733c262c45000dd5406e02df50a6e9adf989d0058f3d0258d73b2a7f9d2029fa6849acf1ae0f9e805653153fc2460104d9e2eec6d4afd6b2227d14bbc1a09af77834735c9a3dd0521fecb6563d79bad8a526e86bb120794e6a5a06fc1e687c7981683338eb47e8818aff25b0226b47e802f1f1923d74f8e4ed2139f54e58a39ba3e4f2a9c88b803bcfd4ac76043d50715f025464ad182d25e33670e81564523d98fcc617d3f1e3e56ccbda459f45922253d4832f1c62ddd7fd7f74af0ba6015fb60ebe90504bcb14a450fe8f77204249e9bef3da0cccc9a06eb5235d6ec95b99231920ebc0f5321227ea3ee1392d89fa546861a14f2b2515bfee546b140d9b4e541775aa9aeac48ad91d7dcc682261a973c6f0cc87bb8f2aacec39abcb71f77b73abe1c6ab9b5206026bc37efb7b4e14c47d58f9e29e364d2421b245762dd497209cfb51ce5212b0e6a2abe7fb0f9222c72a4186a0d203c3b72a501b8c134c0e3a72ae087779dcca1a992ee828b401fcc9737b3ace8265222495fed264b5b3c127cd0ab51ac9ff081f2d62da108a60b18185cc11865e58b2f255659616691de138303d1f3cb01cd09fae7f3853128a611edd7ae15db417aacd9783120c04668219991c6e70f9a5dc01a049f460b730c90f44aeb08694d21486777f999c9fc632575e59f4295ab0b1b9687c68f36a30034642700b643c760cde0f3f63e4d41b2fbdf503f8cbd973098ae70ab44fb96f73c973c2e2f4ceb61c6cde6b3231736ef073d2ace04fc15c320b368b4beac7291ec7f2f891df4bd1feee065fc9a7ad85a9a0f059ff359decd159ea6700008343234a26297f3821573fc2289b9ad5fd3154bf01bf4aa255941ae5ed5ed679076c48b8b77b7b478662dde4cd4199849a1adbbc0f12bea8ce9538720fd0d361ab9aad4a49d46b3e1ba9378dedeca7d61178be49b6e3ba489f08df60840f151c7b07a6f83bc6b0647b72c74ae108fa2f60578de3d58db44ef233b9d1af4e3ec972974e98afff82975c67a98d3308be68fe606094816ed8a1191a5a49302ad2e47b432833a10be9cff3719778a1d2a9446b51503ed02b13f6858199557afa62fb77b890c43821f6891ef42be1b84e66c6f959082bdb3af2868527a4c994773d660b5b618453ade7ff5b602f1eefbdb07dea9b024b9420fb4d4eb6df813963e520605c62f4fc049c584a853889d082a6afbf70c341fe24e1e0d92ee650ec2fa07dd5757cd53eaee4431bc150d5e950627952d3880dff88320499b2263d3cf5550cf912c6ed0501682fbb306e2e767f02a8c2ebfcafe3d12b07617bab449f57e0aea549ea7205fedccb0d5fdcd429ebf3998b54df319595ccf1f544625efc4df389476c74d3a8a50804e532505b622c96d2a1344e810b2d90e44be01421cbe81a849dfb35486c04daea00c47b1dbf435a00289107924c1dd8bddd2703c651880de6a50215dd5dc02d688dff7491dbe9d8fa3e1303dac5737d624e1f8a7f9e208bb8b0cdc56b25da04355c7a408194f4378c6922dc486e356d3c1dc94d2bbfa9527e399d64c9095cfad3d5e5c1a4db7367d5fea46bc7332ffd0f9288ed707dc13402130d4822d6e32769cd1a7a949b3151eafbecded222c479c00db33599c11c34871b0ff1e42e884282c066da8a0a197934c935af5b864bc0645a538a9bc37ff11085755cb3c03a9611522e8e5d2aec9152e2511ca290d275b74af46d18ff57731fe6d48804f164d6509d7d995b7d7f28a49381fea8f8d5627e18b103fc1a97ab90710ee154bee6884af64c400e72e908eaf4eade9996aadb268153e07cea0ac48a17b1fd515f5d7e7de702ad4ff76c0852d82813a2c0098fb9c4fcff9d88ec9b24e9a3a282f2c1abf5c9d894005b04fb9e9d72d818eb80c64b80d1cf49d39322a57b104eb499a49c0a5acc2ee29a93eb8ee934a8f3bcfac1711fad08cac008a776203ec521608ae9d827e082a371786ac290ffbd5c3267f0e20c07998c731ae6b233ae1f258bb4c8ae42da7f5e0cf8a9626ad3e86e561a67f187d53eef036f1e001daf56835c64c27e26d63936f71065393794e0017f0719bacbf76e458e9be9ad5da4bf865469417a7942d233f4b72b36331ef62130143e65a51885a7bee8a61b4930a4021dcd49f7ce99598a2f57c442e9a7e0f015985e817479f94c499ec897d205c90fec4631d398b72208b001957cf6c7e759c5ef91279ff85fe5be4422cac318ea7cf5c22b7d650e888fe5f15a0cf18e089bcc60817d0eb8601e83d688053b86b41fcc4f2b28c2cc76e2c62d4dd35412bb983483c37b1ef08d75473a251b29c531a3d6657e1d513dc0cbb4d8806754c81ac924e4db85db1e81ab77c0c488e00c2015449ee42f1710722360482e14e06eed6b987eef988d23be5a924b68a98c27460fba0988c06638c34588229444544f11dbb8f68db0a1562172709ee07906fdee47fe9d44930b430cfc2e625f12a400d641a3d5dde21f1b986c915f7f09403acc33c5080ad801df2396c5f5b0e3ebc94e9a7dbd13899cd4a98781155220487892349ac1d768936c83307eee4fba1d6770df62b25c3b243f62a100677a2af34cf4d4768f0c407b2725c7c048e214a581270581b6a4ff6629b8c73f0585e1f2437c8fcc0668b93ed8f1289a6b1065e62f2b78692bf9dc37953314651587866ab8c7ab2c44d19582b4873e61973146c1350b2323647ef47713608695a6c9995f2337a35aded9f8feb42ff03b04726a6f312f7a187511b57e6b50ba2da20f0c020f4604d42348c6860a3117c06c0da1137cacd76dec2625043ef334e4c08a2d63a0208273a9c01380267a3b743c33c48a1f900be6adb25368b581888ef1edb939bea4dff75e7695288ed789ee844f93c4bab7f8952bea88d619a78e63cae836943e1e7c61c12df3f331765bed2e879d8d070661d9029e95ce2811276121965dd21be8a08e2ad9ec40263640380c1208b12b8808025457ec6de3f69bcf677429aadde16c62b84c09b82840f316f00ddfe9cec8d88b6cd6202af60dd6c5b2081c52487e577d8b41020930371a7d49dbbcfc5b1c73e4db660dacb904242dd4622f06e7c6ec11cf42e27f728b951df3f05eab3b8578fdc52073e87e02a6057bffc40407dfdc7ea2653b56ccf26d446dd124e454261a09a0996c5d0e0829088af592cb9cb1bb1cc3a7e25cd1dfaaa52b5b695c05a9d01859348c9d53d8589a12ac3b64577c504d01f11a37087b924ada58b0ad019dfd8da5dc5829300602fdadbe8f776ab34ddf6604ad99529b330cc8b33a144223727ad7840b106d0d6f5ab38a0a41b0bcbb407ef7ba2b74de97ae2bc111156fa575981fd8c564605ba0a93c2aa53f7f288c2dc5f2df81b193fba2ac7782ee91bf4dae9728f467c259564f2d491ea0864e5b2e4065f13c19827ae4cbf200eac4c8ead10195e415fc0aef95348f4c5604dc70a9ca963c417f2827102bb9d703ca80e6fa4d5dfa494f3c9d08085350cea656e333019a38d97b40f80b22cad9efc0149948615dd11a2d9a22176b57e50171f894416bdaef7fce142c11b7c630aaf78f5c0baea06b816bec2ad2ae253be1439cee9a15666ee43d88ae62d7a22be89ae81abb922bf50270e6f0bfcc968cd2a69fcdb541b055ca5727bb3b2b6683138db28150edc056b470122e8f8bab8ed6945715d35db54e3187b6e4cb341e4f233ef3349ee951f934cac42bb5f54f11a0b36146237454d7bd7c9111e22bca256f627ad5f100cb266944a0095abe9751ad303db4d676a21bf1ae8c03c3516137f29445acf34dc6aea405e43f78e00f6561488f5db844cda305b08a5832ec74445541c11d9378f871185db83ebea313d004f183478c41d08dc0e7c95be702f372e27c16be2c57c2ecce98bea2ad3297a5caec7be0192180c5ee90735387f60c9f32fdc90c48ad68bdf59295179ab941225e7a8a06708c92ab246e8367397064d749a30045bc313bcba6e008b23852cd5f50dc1b35a05615b6242942450b3ab149e81121128c7f8d0d5ee921b1f696d5935555b71ce1760e5fce97dca40534e62e00102b24f0f116fc6b65c1cd949e01f0d5d2c3016915042003319b971dae7dfa082477cd9671f99c9ee5753cccda043f14461d7e253e9faae12268408c7b03b15efe0b7b767ec452cee89e8f6ad1b6003b1b0cdfde0e7ec8566f56552812889e430ef3d511338000322eeef4047588b26b5b664d82e966dcb3a32c0951f0e6a4ab6881bba5196a9642e4e9bb19ad35f8120d2b58b04623593f52ba2b75c1bc57c91f1c00a3527d705ea6061849a4aaa2ea838d1bae82ad0d9497d40a6f37ea65e03e03154b13b706da7d4aca91b6fefce50f35a818f44c8095333780ff26e6c5fab7022c43acba4df20aa353ca82d34956d7f04fe2464fa9a8b90f6211dc3926d1ebbb4514c4ac3355312813e2e2d06487c33d4b90f03db7d1694fc2a5f3d963edd01bb179bd715ca40ebe54cf2c33a246c6f2a9ebd85b14ea20fe16861c2c14ad40266e06b90f938acb83bf919a40836bd216d32975da167e1c94d45d28a7e561b8495a2a3a993c742440944dd8edbf3a0e5af93a051bf315fe13a7f09b21efa34ba30f96143b21ae3348b908e4d667c4931556a4c109dbba3ffced658f4e79d4ae67953777d4b9c66717000b8eb31382a72a9bdef6f1d6dcfee4e5ed07d066123e5f5b8f123103a63e816bb4981f80c684f68d825b3e00370e7738d9f03558f2b86ca20d1f93b4a023e807340b9d19f2ea73d6b1c54e4589ee149d247544bce747394175dfdfd829bc35de13e7d46e6ce447f78eef1864f17fb2bb803941396d6c82594495c9644ae900f2edc956709a05689d707e638cf5a17d354d160e17a7107b08122711d918612f0024ce3b20820c05f45571a84e1a20cce4e9379622140d88662ff4d5d3a53022728e9959a6c770de403101bd442c59c2d93baa055bd05e7c2ff0873a04cafb2f7901d5c63a0f5638687a0826946220b70736083032885591c7edf103774378f1872b81a71178eb979e91e4a196f0b283e813c7fc40993bf795489b2f8a8fbffd20690762c6d784ff16270eaf2bb3f308a200bd53ce5299ab0b52ed2ad4f8aadf2bf2c77dfd5d027b0769a86b7884eb301ecfccc573705262e89aa39f8514eb9463b68f66e03da3a0db948a9e66bb32ef66da4b3954e22dc110e6ded43245f2bcdce01e0d374ab9a6a6062224a36988a6f16595e856cac428d35699e8ea7f934ca913b53c57fad4bd9cf8f289c6c12a1afcc6181411e1ab1fe7c32265f6a524edfcadaeec45ade63df2cf30532520d7af140b180c8d6928ff82b4d8b640a341916c53aafe986c53277aa9a04b0849c3bc6dc3601f5805e884c0f085a0e9b08a02e015817b2a633fff6350617de93af83394a2f7ed5a42c252438e0a845ba69e01f4fcb8a9ee8a7abf1feb23ac69b52f69602eb0eaf6811c14995a830817cd20def4986e8cdf95c9539561af1034e48761dab2afbc33d9557da5eaff164020a82529fbda17616b33b2becd7bea187f596a365281d05e2e625f97b6e37f53584cd9311940de6f99cccdcd94f2cde2bfb023c2d9d9f958bf9853566d64124b63a23a6e0f61747a73693c2ef8deca29696b1c9b18b7fd377178f93a3fd8a6a89867963d1f7ac53bcd71958129a047aaa4a0af20f17930459b6a3d5884ebc35768ff0ef514cbf52fb432452deb87c5e8ef6f56e4dadaa51d6c3aceaef30e19b71ca78c96b4e1fd23258aa8f9883c81e7fff9967e62d988e3626a8295eac126d19e122284249d533f35ca9961bcc38d9f857d493802e57ce45a0baf3364c980cc438398db47ca1c5d131bb471d06bcfec8e1fa065c2fee2f9e48f4547d5db83874f8df00a0889fc31cf5b6b4690b778589064d2a5abc4b83e531b72bdf9618f0a4d7ec4329f1982a47f9da9742a764e4db1cbcf21bd0cc5d367724ecba91dacee70c114f4118343528e021e337f63f3f3c1d8da6bc0bc46617d2bd1221beca7789384a5d2d28acefb87beeee32367377dbd932385e2774b79a376b2585cd2bc770f7474ab511cf7b8133b986e802ae5487ed59548473d6e112b69e37cde37ec9ddae070bf5d92c8a72c0fac7ed26f07db1f597f0d6c3e1cd939a2469f8bbd17cc4acfb9a37c9a08c003ef841fb5f1feaf38d036d1d660e8c81ce76c0b1e363c37807f3974d8c4b8b861e6e929885409d3cb013e2adc7f7c43fcff201e27255031934893e8687e4fc0b3c12615d4d0dd3470562328a71bd277cad54de438a2f36f2ae8c5e2855e8405df4f5cb438bfb5da99d3845b45db036fbc4b676e7634b90e78ded75c57b68093eb70b0dc019ec9a776e59534f368ffc8d91b0b1062cb19f65e8f961ccb2a1f992a218f2a51247c6390c13e034c163d9e53d429045c475d74edaeb09e056130b4affc7622db2fe601692970a4c811b8645ae27ae4b11f19f891d21fe4eea113d939f99d1612a05e6c7f946c7478b78e52116a6079f67932a57a6189f30b789f731ddf7c7c62bd94632bde2df6c4cebf11b52c12b856131fdfe58377ebca1fc9235e1b5b7df2f110cb2519ad98b5bf12e8a5cc5acfb7072f528f6d4fc6744ba84596dd4042fe34182717274857307016f2ef1216e159358d2189f8085ee47f17db3d67ba5fd7d83118b2bf1e5c13ca4f00f6981575cac43dcd3400ff5ac58b8246e60ead28c9fc04543220df1d66a7a7cb908d88343f63c5c62bfad1f8188db64a3ca32404d43a28e3a7d4503992e5e7b95df159281494f622cf1c7d19457cdbf95b81ae49ddefd15da5e0e4d66caac3fbbcac3964f3accd38ff7acbeb19607839b293df5771caf3cbea6b1874d32a63ae642294d3b131ab97b657801e2e7599f14387d0b9b3e35bf8fc70977c7e086fbdd973fa17f69e7b06b718eb42b1251729632413f6cb864e3824d05260f8d582835d67e3b10304b96b9acb3f9fe4ec3f7a1e4183022894e77c5a47a7eb7d0a1378578d3a09f76e95c918882183c629241adede01839f571e2543ab6579cd48b3bf08cdf824199260d164d58f8b9e384990fa556138542ca5a9ff2dedbd2a214ef226bce141388123179e75eaf35255a5a45e05b8f0862b469bf08a7074fde261a6b23c59461a956b2a5d4b33375330c35c7bae16dd18d649b7b38fa3d3c0a3d29d82fca5066a67b52203187f8fc893551d6a638fceac8a1032749e2308bcbb234154eacfc66bcd556ce0787f28e84b4b74e5d511e600f82ad86700c67650e51044506e40291f2a3447f818cc15e6883ede1e9cd073f0cdb895e2c0403c57b9b10801534e83b589465b90baee44e16da9e4f0405f6387f9ebfdde4d27105ba0f03bb0212617f360b7944f4d62b7cce79105a88296ad4a74b58a5f9953033271e38a4a9cb72e63f194d21ac2e4a284dc16b4d927c84ef68e50d942c30be4d9158b621e594a6e0b47def803058ddfc9632ec9ec5550642406ff8e7edf2534a165126bbc90907414f8455920cbe50a8eb19b3c4cf596134e2fe2d2c39b2586070422733b5e206006358f89ac7a616600c6af1626bd45260ff71721843367a9051c2ffe7ed47064bd849db6d059e4411a81726173942ba88c75b69ddca9b38c4949e0edd8c6739b1362f465fc16edcca2ccb02bc67070209b4f3e03be1f0f916ecdf0057548b070676bb9d180f93247e47c9eb540d1665f2bbbd1c662e4587b3696f61c0cb1202ecd986fd6b52b711d15e8fe1e0fce50b7b398196b120bb706eb21ed0de41f898b056e0805528e9d1bd0836e1378b79e28989a34f60e15dbcba0dd7217e172709b1f06ffe76d78fafdda12e1dca2d3b511a2749d7b17e53bb7491097ec75bddf454634377e80bd356f29114dc82d4f4926759cefb0e89d32415a566d86700088b44fc610aeb9bf398a81a7159813cefa856b07b57b0e27e16e601be13f0e7c80dfa02cb495b2f0a91f2e8ff3e3ae7d0a8883d07b9f6fa939a85ee28c388ca6375dc6472031ada6430c0aca050ccd3f88cdcc612b6b7bd2343ceb2254f9c2ae711c9e22bf515d18ccf048238872b67f2d297adf789c5cc2ede4b7bc6262ada16bed652ea0b4ad18e5cc2eb4435195610b8a03fe6359d4a8953dcf5bbc05a7d781a7444a75754bce2402d1f3ca32a4acf3dbd97712806b3b108a4fdbce590ef0e892c18a4180b7e1a0f3d948a83bfc18b50632e01d6a662e1f43ce364d6540322f19a3334ac26b26691d9ae07dd3a90c4af7162314e3527cd5288dd193bf4c4c019556914f4518e980c8d111a77461d0472aa080ddf7ecb30f8c6c9cbbda54be3507a81614f3226e137a470d601f00d338c01ea1b45674163efe3c04467b5fd08e383aae74de0f41d0702c06b74a8076593fdcd409f8016625089e0eeba47bd8af5f7d94cce27998f59dbd595f8c79b0607b961d563c5664c80e444c9176c962b71fc90e85b0c9fd3d98b1c321e8649d6ba62c9a1e3b4e29c6402e520f2a60655abf316074bcb8e9d4326f50431b58012098d439642248c18842329a63150940ec29f1605ecea8a7145ceb33f1fe9e5c9b568ad71d4b45b54acdb4ce4a57fe29dc52603302254967a3f5805eb842864f404978c74680fd63c1d28c7820ac3f7a9cb82cc04fd1ed16f411470ceacb05cc58171461a11e592820513361b4bbb99db1510800611bcdad59cf94875babffab615a34a591509f72c0cc92c01775a6975486e2543b3ad9c71887f748a490241102dbc6a94543415fb774a5cce8a95a8a55f203ced63023bd8fb8272a3712950cc16a472f7270692dee2096de6863671957f82afd504c09172fca3ecff087432406e8cf05304fb22e18a14a02c1eabe09ea8a29dd41f99a681f664f6aa2f688b84f00ceb6b624afc4de011220aafa8175a347484e0eb5c32a97d2b120295a4591202d41d24d5a842f8e7e122cea4bfa27c47cad54859529d05188b9ed6fea6b6f5d184b66a50b5499910ac2d82f5fd33e7beb92b1b42355c2da085424c47ef935fd5b4b37cd851969055697a71af3a82d78b98229c18e9ff34b28e70dca98d0a2e1baf08fbae977705959382caf4ca9919e821a04f1d33f7dfb9bbd8769796b5a09d424d430883dfd6bcbbbed1d0c4b47d34a444db102a1c7676de4830969197f1935dd955e6165045a1063b7f9bafeac559fc6c22ca90a2b53a0a310b9dbdfd4675bfa3496cc4a17503523158498dbfea67df6d68571bb47ea6b8b6a3d1f4a826beb3ed8c75021dfa71fa1e677a40b509b818ee2d82f5fd33e7beb92b1b42355c2da085424c47ef935fd5b4b37cd85196905d64644453172abbfabfa3551f97fa0ba9df3717d7625ad06da106b1089befeb4d5dfd60e268bab2955a0a7508151fcd4a75bfeb6f6605a1e49552335841a88e2a77dfa76636ab2cf185e6afe4df547d8559a5e9bb907fda1625d06d42a1665652b2812a2244c4100ee95c7d6be527548285b4123212d402a8450a77cacf6955687d294a176f03f607eb5702efe06ddfa1db0ad0c0ecb47d36a444fa186b1f8ab4fdbbc6defd0b0bc9252467a081514c45ffdb4ed2f6b17a7791faaa80992eb370f8c946d2de677815c00980bf6cff5f3d19422d243ace1287eead32d7f5b7b302d8fa4aa911a420d44f1d33e7d7bcbbac76171544a8d542112918b05f2326877124009681080bdea73ddaf950e4a0aab5114a04b408121ec548f537eadf4a0541a894a21c55150ea7dce3f285e7c289b543a82bc91a02a2b721e04942115c2b053be5cf9baaa435969053a01b4085310863ded67dad76b3d8854a6c6876793d945c979801796e67181bd31c958da912a616d042a1262bffc9afeada59be6c28cb4026b23a2a2186c2e32933b800cae5d98e7f944f56f3bdfc80c1fead52cb52a23d380a3d5b85db854461aca6bd248540f501523bc9ae30e7ed19cec6580d23158285b45a32125613a8450a77cacf695568752d148a436f102f54b5024946f2e55760abb350f5cd4f78f6f519355d014a409480700ea6b8fa93ea5f54152d88a22605a022a8024debbce07005269cf394858426a0c61b7fab8fa6b5597a5d28e6802ea02a66288b46fdeac0a0b9d4b7aebc4fd7b5d510f27acc4087ef6cf36dfd63b6928af4991d12bfe62e4e18474bb3e424c774917581b1115c5c85bfeae3eebd687b1b046ba822667e148ff47e492e0b58f250b01ea05fd486db68356416a861414c25efd32edaf4a176765349f594901de726285b09dde7af5969572e12e7a152b115403d4e5db9210ca2c9db7e52f6d8c1bb890ee07806af666f3b1af3d7cf5b4da0f9295df2f170cdb69a98371573f454d9641d39012611d00e8ab7cbe7a624cfb1faf79e93c29c1cedf3393ffd9a535b92ede851ed8189f035b85c55119cd67362fe097948d1475b7578edac2a1e511aad209347fe65c37bb0706a95f1537435b3f9e2cf34968f3dc07fcd16abd2e0453ed8dae46d0fdb22165f3de5adf6455d9f4729c07ddf382885582db641de6824bf4815fdcb6474db7373514bb04ac02569dfe2cb1b6afb5a91f29617c5844a48ddd04e346e7483f24006b8899c8cc4eb985f5d08d58cfe53c0e08e19b6273698f4b8ea0c994012a5d0e482bb62e5a1c5a9b0d8438c59f4c41ddf7126e889c7be6f8922f5f913e54ef9dca1fdc83778087d0005f543a26d026d474c7db0e6672a7c2ccace018c8f7472dbf7558c5962c5c4c5d2195003351e6616950108e0d95c6b349941db7e2036510aef718183ae30bcacde0adb783587c1107b842bf4def4dd806776e26843ae3c821459c78d042d51b66d2de036a7a3d9a92080d935b4d5a025bb52c8ba5452f5b1205645ea5f2a1719b90441f973e1dbf63179e5df50694c78ee932e9d26e2b01ed5d2356c4a2364a5c9fa7ad53d531674931dc6b0a4dfc67fa53e15224f3a693d8e68448d542cb29d89a3082075d786f29f4737bc6c0daf6cfd1e0b85fc6878bdc99dacfb56ffede8df354b0e25939b0a5e72a12a007261d62278f5049d720ba6b800f90aaa92d3cb26bb4d52a2a895b687995ea8eda06b0122c00aced692f4bd12f16498b5eaed334756f0d97731690dce80de89bc0bba1e8d9855d9f39fca6ea5ac85b1596a1731937b548c025979b5de92958b4176c9238cbe39998ba6303c9b76af7c39246f42e5863a1e190c629d4717228ff931fc480a516b1a177a92b2fbb967e33d9b25cde7f3871130202889a6d8a32b110a36a4f72c0a249e33e1c906b518024b19564947968522ded686771d8f981dc40bbccc8b2a94dc0ec8f0eae1c7a5bee5f94fdc8433a7ce4f59a2e21b184d1794682626201a8349cc76e074905d8bea094a4cb3b610a4ad0dfa3987a4e7a65690f7f170915997f60581a9fbf821db52b760f88b7a3be7bfe6f15de365ed5d3b3912c9e768ef633d80dd1c83e2e213cb1b71ec9ebfe3cb6194e020991369079502520b9282d950e2dca63a7c30e9a6b3662ec9ec18769e4661010af47a30e564697e8ba6253f67cc83694d1bceea31527b349e304fa58ec26a68ee169c9a1c647550afdc9afa8c05e5e91dd343dcac74e81b1f707ec2501b781cb4ffc3db6d5fac009868fe47acc438f3a55d635b3acc4e345e9c80b16f71025f4dcd1cbb727b96e75b2401b73fb8407015f427fe6c45740493cb6e1e87a83064c543a0dfb62ec2ce28e9c2d6bf13b437399a130048e31da791119a93e6643d33b91329a5a79aa351699accd9143f0353d38560f1e62398c274869c9eb100f4394a622c63d67addf3cd174d741f6742f8b860fd308a18da29d81000c2d71c1de9f0ae88db8e7ec31638a47b80a8281fb410cda77466a90cb3a11453c89211e09aa3154037190f926fc061ed0519e5bcc0ae22d009f458fffabebf1e08ed64467ccfc1206373f5386a83d4105e37d316cda3b491d5522fac18c78b453debdf87afbf1010c9c4d8a34a49d6e8853440d64450d6b5df534c3895c054797b7ad76eb6fdf61c80d093d4fde7a709961747d9b0cefcc71122112bd6f06f3bd1191db62f1d783eeb2615becda17ace71c8e93dc672ae41d401d1ffb0d2c963d2f50b680de883db2750f47def3b74d5dcf4feeb685bf8c302b6f1b7f77db7875dbaea48e06e012648adb8961b3226d27508a395c9e1bf5f87a95336e85c90ddbca7102073ed62e1f110dc6bb02c48dd4764efe94dddd8adc2a3b540030ff94cfa73c5c7333af1cc6cdbabdea61f7f180abc32beeb38a0330e7d2bed8887c6bf05f8fb5513314a815008fac23f14abb42d68f217eb06e3b63d56f123f445a43acc0f73f95298a6d344b0b0674effdd4eff902ab15c71ed4db4f8f5de79c77bd75c105d17dab516ea68d9f9dda3d79d94707bc14697ed5e92782b921350d5b3bfc603fcdba3c344625cdd1d4e84b29d30dce3167f1957be3b7fa774b67ef183dd57a88549d9e32a56d374ca1e9c7be6e9047bc0d581af85ebc8a3c06b8641fb750559cab815b0a2ecc76945fa47a87a5907a014213d09cf563f7ffa00162330a69d6fea86ff3f7b6beff04cbc50b246215184b58bb77dd67c8ebbb6e0ba8207e3329fd109d51aeea87a30930fb69dbb341e47bb06cc912acbcfc49d1742a2eb0d4365cc9269afd4ed6e3130d49f1a076a20e9e22b563656492997c2424a8a5012eee067c2d7ccd3a81bfba8c1e8e9f41c0ed456fb4e58cc05474f950d6527f7e832e2578e70f4b289e59be276731f690782f74d7571b49d7f18a5591da3b5604cf26ff5cdedec78f2b6dd20b1e954645927f8a8336a22e8ab744885ab4add013da939d3e17a1c6c2d6112d332eca09d58e381ee121a137332e4aebf0c09bd780f10e27a3f4a109bd7666524aeb11fe56daa71bd071ac3bad003e22a6152e82aef5a887869152d254d73dd41c525da477ea8e274e99f16cee57683b9cf6700ac4aef1a7f031ee07b47b8554730705dfddeef16e6de5fe24664db93f95b6ed2b3a2cb056ee1fe230b03d840d9bc3c9e8d65095eea05ecd29cd613be566b3500ebaca751dc716e72fa0d98f54786fd2773a3db50e36a62ed78beb41958fdd51dab3c38c9602fa585130bd64272a0175574d092f5877025fb4ec184e58adad193a87d3de6b26e6f59d1cfc042cb0ad33f2334be8e74add8a345da6c09d7dc4aef2b05d4992a9254d0027b901e7b454e2068bd03c552c9e571ed84bacf1bb98206a181cf89e9be3fef1e8adc876484684c6ca9c760abeb0a18bb9b973d22493b7a69e9089c8b9c2375f793c3757a55a81aff30c9f73d0dc42460d289631d7725eee8ae69e4d48ae9a5b0ea30c4ba2bf227d706175268e2fb1b19130fc736ce8c541f45acecc9abc300c1a10730b835f5a3029b398acec6edc737b9b63c18a4ea5076ea3be96ec4643e40203b305042d1f98b6410dae842785d3617d98b1eee96d91547bb2877b649c58aaac6ff3fa60e3ec47a9cc9bd50cb53fbfdb8fdbd6fd99b679c2f29ef9f84a7db2bdbdb8fd47d97f69138db1dc14fdf3885de807d13950fa7ec54a94fa6ea5a8914f8bbfcbff07bc1b18c9093af1c7448e21b3914eaa63a2b0f2dc005ac15eaeb8edbcefb1654c14e46162947ca6429b40adf3b57fe77d407ec660f14300a358868caf52f97463c9fd48963d1e45fbd2147cf145c4224677062ea450accb2c3265232e479aa21c481d24e21ba944096d75dd909bef8ff9d3118d39e3ea20a7f2469911965893decc24f5764cf28f7f5d9df1a25782584c32565ddfbfc6c9c4819c0623b10a71a399f82fef097888fd6932708d08f49081356b420f3510a27c72522330503dd2c4d258ebacc70dac88e689e20e5fa36e72faa8aa4db4094ea88dfc481d24206c32a020ea9a878d405361883ad287f8ede212bc60d8425bcd961710ab0af01f53007d953d27666721d85ff9f0d83df2d8e152a72abaa2dcc82551fb334f9207c9a6284f3f5206fd9042a1a6cba9b7cdc68278cfec8acb4e18272b63e2e9efb8cd5c29f7a36eea5b618d7ebb9503cb854434b5381d3ae7acf4998284a3d4914f0173016849666c8916610b0a74c9bd937bad76604f04d15e049bc9a8e4dac54b77a857fc474e33f3c4afe027ede2f68e8a23b3fd9585239d0828675b0448c84620e0d62270c009455818af31e1605444c4aeab2aafe069982b412ae14c7b486f66b76331fae69e8f7392b4579620a1d9affa0c5467a9212fe2b852340e7980becd2a00de709728ca8b3345de2a0c1f919d6b77707eb6352628ddb626168f275bb82d378c16b154e68036d2a23c3460239643c814487b15c95b75447546bb1dd9155c7c46f8ad90018ba826ad98217f8764dcab4f7f74af48f94922aba743a21d2dbb1430d29f7bea1aa0e1b805e4a70329668d744ad3de70c19e44bea61e5e194ea7f442eb7a3db8bc6e75ec3f66ac71dd0a93aa364289de1fc4be0dd77d9bd6950cc5fef7e5090126f368e162220d1d22466a5e696b7936901edb4b380dbcd8cecaa68ec42c3a7e8538bb06c512b1f6b0411e63e4bd00916c6ea46c52f309b6f7145e36cf0709f64f2cff840dc921b65920f3c840094f0f03928b858f3e74cedc7c11a442f9cb7f2be761451be409e9ffbcad50a7305055d82be53c6187806edd83a624cd3e8ba8b82e92dc9cfca52097f461b882734110fb8acc368118768cbee84228806b0af42b68caeea38f1f0d193ae0051d11a6327f9db131ee899b0b6f3bf40d0133130941dec0cc575da708e334b7410e507d100a54444acdaf7cd6478fd0339d03c1967443438e13b3882a5f58e5219ad47e7261c36e57c965a4bf300f0066c67a3020668f3c1343384f02e94daf87945aec81cc3690f88e031412b4a295f7b4fbb374c45e001ae40f784641a798b55c7492f07652d5a8dbd199f798cbc47282eb936772bb1a8bd4ef1df1f45e63e197333f7f12cee4d5fc079b2b3efe95595d85537893b78fab9b31f9997e212090a0bd373254d8cf7256b18036f8c51c772380c325fa4e10d492a6e82a43daf8306afe78da7bfc0b7c07ac86913dd1b289a8cef772500275e243df9fd67b4425c64efcc7b77a6e40d7df05f054ea8808081411cf5f8fa10f93914bac002df9398c2772a8472fd7d890ec4c7f8f6622596e00fd53c19f04f1175f3f75c3ccf45bf7fe6c1feff78230962273a529e1b7649fcf1fdc53575ff4f97a85fc2321b60d2ddafd4d262cf29678eef141abdb516d215b7dd6cf35127ac536f902890aed620e5107d454bcc5d291fd3e5bced9bd01f4b2ebb87030e12bb0aa5830da4dfcc1d25e600640fe4b1bdc815ff5ad579566bea1f3ef18f23e2c7779b8789f13d872b3b0ab771fa53e0f41e4adf80ec99f0954d1a809015096e76aecc1a3d01f771b3f36144864a50bee6022b6c8d0b67401531924ff488248dbe708a9db730d40dd2beec1b9d5cbe5d65b764e08bd07ea61360144ab1778b56e8992091c37a22e768bbd809a5d7e76f42ee8e97034c6a19830ad4161f011a47c3d2ce2cba2a0c15d8509b2a0710684636706736ec908a338f107f3c1ed8f12ddf2595cb9d85395f41efc60a54e0efca333852d47f59fef7bb461a785f82faf4842d9e4d9119b9a394d5d92b886b79e1abc66435274e5fffc45224d2d1143f8856bd7e3f2f70708ed9e5a728fab7a50b3d2a89a80f95351441abad3bf8555a317340ca97d376cac51ab56f123a65d018347a479eae83d98bb71e743b94aaa1170152fecdfbd6341a8b404a497021ae99e53553bbfdf5342a1bb95993daaf38deb3c3c11ded2127cd12307b6c72928d9f556751b98027d160fe59b911f797b530589b3dc0a93a2ed50cedf7b73550ca65819e4ea69595402053eaccb4d91fafd49894b3cf82fd805b2bd38f0b613f5c4f38acfaab9bedfc12583425c85a1311598badf8385a708a1cb983b75e1248448d91072047c0c517579d82111e16ab1a576825ea82233670ae99c24132bc2b7f7054b0e4b5450d8cbae7faa69e418e31c13fa1ce14f29f627c2002a76dbf1150bf3da1a9d2d9091cec4450bfaa0762541a77a010dd116744f030e15a234e8110d4db23c80f47f27776f0e37ac1ea056914c3f8685b2ebbf3cf59365f47663d5ed24616cc57604e009ff724f3ddc772b3d527a577f9a51a2b3ad8d88baa6d4a2826694009b0f34457122f8ba9f603c9137c9a9c42fdcf2ec3f54a80a52d438fb6c87524859f8ddbbb81717bf5c72c21ff743978d843fa7b1219fd446469ce4bfd3884e39b925dd4ec8d83ea773b9659733495553592060060b17c05c68a75dcc4a7e584647b79294ad30b87dfe56fe77286b71cc68fc9d6dbc46231c3b7c37807363456dc3660445b50de697e8f97ed2bc13524f03037c6e39f9601e6be75d52ae4161b20278eb2d491104a0159360e3581acc9cb9340e554ce4b75d90033ce72717535f688c75add24f947b521272f4eef19de4404dbb90faeadcf097c426e783016028dbda537f6a20398390e00b9a207ae23704fc3c420a4b1a09c8a9220c72e864766673fa3cb9972a5859f19d805c2d63b027055a6771b37e7f4e4c1058bc5558c30fd012d5f1206c12182fd1bb6a733ebac5f789f4a69f352c2aa15f683af65237195facf67ee6e8bb0364455fadfffeb1ed4f9a2852fa50242eefa35302a7b681a181c5972b3aa1418c0d739a275dbb6da95049569a4b9a13577965c788a226b54bf21b704f3e22c77bc71d8da9b5101e04ea955d77eecb9d12779877af9511e9ee93def6b48d0a3f2e6aab5bffb40a94f84e0980e9bbaddd4653b1b1ced6205a1e01118fd27c1caac8f2a808f6c6d6030e4131cb27f4d95db37153e3848b6132feb919fe711304d1aafd661470acb61ee4dae9beb74a0fb9f89167166dd47065647bcc4f3ad6989bdbbae46d6644682d5cf7209163c6051fc089f349656f535f30e69fe5edb8d15b7634b41c325e407a5fe99f2073ec0f8e15a9d17fd47a8e704c241fbafafeeb045fac40eff909efae779886e71976a66cab3a1729f86212afd3ce7ae62806eb257af9199d3b22fb86603112cd10040c0240c7ef7b6013fa11c3007cbdf0ffa7ef4f9e4a4b4a0e7d2155306ea8e64025b7ab2e7bf4b3ce66246157441e82f0777ef649494c26eaa21fe4bae19a25be9d93cc1232eb471078450d89f7bba342a409af112fcf1b06df8fe45be350175cfc00c18ca6dcfcaf0f114d3421770dc4276b144dae442effd77e7970e3f310ae1cfda4ae17c92cb53a55036fe6e3ac79bd3615f43e1269f85627f3b37203ccddf97c68ea301e77188f1475718cb8c44dbc821ba53cec691f4a782e0925b7fd31ae3f5377ee873b6a97f3e68f16d6591618bb0d7899cafba2e5f92321ecfe8924d579d37c139628ef38c0e2b3b973ab935c67e28b0ccbd83231aa1441ebb33d4af4f2bd72b60cd4b785742a7cd1c05bcf9ca7d6d03de1eb00b6849d35e1594864a4c3819cb72f94b48855e52d7ad145f44c72745397cd200d171ec06a8fed26991badd3da363df402b17cf44f419e8b0f5b8c352c9dd4a865451a5100b0328e0ac5eabaeb77079aeae1e654f37ccc1565729da2c532e64ad6989312d4aed31f8701eca911ee4667f91bc8302649eedb07089d8926d89f7fd742ae3502a0cfa071bf7d5cb5844bd01f6223d41e5b01b2a817eb32d3a59288d960b7dadc6594070e806e9e15641217772229db4bf73991e8c44a0017da303264f49b0bb04236503686b4f7a4597856243c930b5587de80bf9348b6bb917759694da61448ae58473c76015cbe144a75397e47e97c6a7f658e7fcff2422585dfce2b99e4aaa0eb680c6e08354170e27233bf80f86adeb9a7ea79390e12764b16499c2689cabfb00f30911581c893b4a34e31ed05c1d31311fd7c316be76e34cbdfcae1707446f603f1685fadafe3fd93ed88aa2f7fd71d42aa94b32a64a50873d222870cfff86da7c5bc5ba89061b69fc2ee413e4f698f303d0600cfbcbdfe596e098e8bc8c5e370e65041436206c49cd13ebd38914dd95945ffdb1bddbf120b64f857d5736d43f5f1e10f1964618da4f79874668d4f58881f129791d8bb0808396f6905137df5648dc5702d86e890625b633352f632f543fa6595548f0b42d44d97795b328f2d332630dd7d35414826ebcdc42992ed233bdd5775deeef948ebf255bcddef4314bfb2918640753d120e414efbbaad8f5e676a84eb7f177b624e627c14a13dc736c219022c2b581a9e1f0ad0c0753d11f737d1d9ceef90c6bd963ab4aba407a4c48586ef38c089b4331b33cd1d1a62a2a51d5926e3046634834cf5cbcc5551ff8390effa38cbb7f1adde344a9e358c964290e4a07dc238eed504e960dadea3ce55862d4fe64e405091b6f0a7a6fd1d27d081f5baffd6982c77c612fa05b2631c78a44b7223608392aa6de8e3d33d9f1701a951689a22d542ef1ccd540e0c022e9d6d1ab01a72c6a1a1ed5f06bb05d4b76f2b681ce05412ee104b9c39db2b1b831fa148bf9acd966109adac5d6c448019ee7c6b4d651a36de8136b389811cd179538efad2846b035a2cddc84e0444f9e267ae904aa5cf2b30125aee6745e7f969ea77d887162e5cb19fa4f20f9dabe1c3393c1a2c67524960f9e7961d1f346102ee3719337feed1b29f00a5c68f0ccabf320a476ca5cc0296ca31fb5dd1d86230d5a82157eacf7e234e8cbd4d674aa6483ca07ed8dea5b70c2c66bf2aed9e18a39a78c1b8f0cf1137dc994194fc361f7fd494a4908cc59792c0866993c56fe8a2ecb00efaea4a637ebe4a690bc351ee74bc33ed34fa1204c837e0b8bf3b14a33d1e35a3be077c276615ed24c5848640204f76e56634d5063ca704a90a45900d8649e00690a349cb819bc21ecd8a0a123efea47458965f3391921004b09aab5e21d600b75e08db15aaa9e5873c40920aea3b0004daa0b1bd5ebdfdaf59efd65db01504e84b97c622b5a7bd414ffacd0bc54ba14f2120d56808d7ddfa8337b64da75ef44044feac99d6d647bcdd29377fc5a82dc95a15de65610862609c661a7b1cac331ca6e5fce9e4a47dd5cadab961fc5d4061141e0836dec1d9f9e221aa797f64e751c2f443a2ad41efd12a2bf08ae8d39e21c0e1ad155e8225b0bd2fd05edae544c05bc2251852cb65d6b4116133a7990f8983c193200c330e52d151f4cb64e280f2e59986e8e29e2aca9960d0a4a175b50a34f85b22f888d7e67b237ea3145c08c6b0fa6e436bd0b39bb4a05964deb512ba39ec81d5706272e81ea7e614f20ba2576702a952f026325f325c5c19b5b658e5526426ee258c5c42ad8771ff2b3b53330d4abfb77001f3cde68cf9e9d0d1ab91241756bf305b8914e4feb75cbc0c77811d3f1ced2a425301eefaa9821451d7b3a8dfee5fafc2006efb9a8afa00302869c92d8ba03c1072424f0e3f7ece8e2555749eb510ba8907bc57f6ac103f06ead1c8d293044bcbc616ddf1118c1dfcce3a646e1f3a8b1a328304c0db970e57c379811cc1d3120c6d5af82bb2e7585967b81c11330e3098a16aabca271adb4031280f36bb381670045cf4176230561ca85ea3389f6945f131e17619d1b23d3173462cbed3605776b321129d2ff0b159a938a9bf379a90aef04a60509041b9a68107e422079d824d56ebe6092bcd0ccb10e60c6abe8dde00d01da8bd1565bcd348f896f37514469457db80e284d249b7c08d0a2e1dbe8181dfbd67a11941f4c05dd5ede5d9b67a5033792bc1000eaad9432595722497c32ea07477779a4896aa242033daf3791bffaae28e2112e45a49b87904e774b7d444f112958bc02baa4fbcd0a0eb3a73b09bfbf477af542171c8c0859fe30232cbbff358dfc7123edc7fe63c4176460d3b7f1b5d1603a990c1366b6da42093c36faf154261b41b9abdadf5c7caf1a0c086e770318184953d377528ffa75e63dbc66e8761d611bf65741cc62244491852eb90ea56454166a9a629192243da8f2e18e958b2f74445f8b163b1955eb2b2109a4e13c3027e9c7b7756cd2e5007e43989e2eccf7f5ac2545277e494b01531a1265fe0ffbd8498fb09eecebd704ce64f6e343941be247030668461bf23dd9c9d6653dc9952257101b028211642eeff438933ed7711b8e17c480fd2b33821168a7679b0403ff24204898305c6cfdfbb16e932c67cd398e0460e508cedb7fbf66277b67fbf28e52124cfe8b4811eaead268c3a452fa234d78bc6ee7798f02e0b17673e9ed0ec584bc69e373bfc29bc5013e708f6a4a3cdc9b67d7d8e29700973a8b11d2bcdd35223d024c1c14142f7d6326cfbe28a8d6aa7e09b5324ce8aff5f66549803f51e3f7902da9390550a0a95cbb7238b90489b8c6ea2bf38f217d5c407dcfddb90f1c80c516dc89b9695724df8f05b060e6c6bcdf710d26bddeab57283abcde270b8e49a576f4d3551f13de77c5e5dc9af6d4ba93c1353580898fd4b783439d6b0f0b5d095b897c153b05c45fd7d6d44a18a28ed7488d69adfad3ef03eebdf796d33ed1869beb215fb88df4438e314dc22f66bed8cc97d0e3327c0c828d100a2535696810c2d81ea08e3e214ade9dbeb82b612e2be0a5026a477c805c43731638c1f4934616ba170360be81d558c22e40b0ddc51b1ccd283d6cb7acd59b116b3cc810236d63a96ff132e6334eb2b11577afe41bfcbe77c6206912cd0c16d9e64000c86613cb43064ab94030fed5b36cfaa7d39acd196c0cd99052086132a5e0e8a959d6ec6b3ba20b442f79f904d148da7275d562b467c04d1f3b0dff5a09e593dda03862fe29e5a299f4d4917bd81473953bec0bc1018d645c84a9fcfb59e97e60d24385f00bd94446d39aa001aa41678fd591a752a5a5f913fd55ccc7e7f11f0820fbd8df11473311fd6a63016d1f02d8f75e136065012b394e7c3dda7439a5119eec65ce821b13745604429dece2ddf1c25398ca85562b773d6dbf523bfbdaff1f255f6436fb83448d9557ae290cb51f113b860c6af8ed4a1f93a4038e85afeab32be5ae1c7530fc0f905741f8757efd5e5d478e4f06a13bcf44d1c7e21ae11b5c056d90a37bf07af9145856da072cbe5e39ed19fe12be7e7a5de79bae2d7df4937d887d8d4382aa3afa55e4ca746a904946a7d072b0ac5bc0cec9b028f37f7044b96022f699cc35314847f4c416238759182fb0a90367411df06c5bdd82267a7cfbc89289e872efc324e8fe9baf0e16e7b5b2977286a0e2c45112209b2c7f3f1a955b04a7a98890e88ae841e40067a66a88fd998d1954d153f2b38cc150d7f167da0fb98b3a70c55fe4eb10a596bbbf7bf8441f363992f8e424a5cfb6657b126b0fde67a452f76ff2a23447d5fa0e67f30a8805156e35eedb1462a67f7419d8c9dbeb3a6dd077c512865b1020ba88966359a4f805cabda24812fe6f27040c9dd680659f89339225dbf7e8007d298477d1007a91701214871b48a3513c8123a37a75dad5bbe77222069aa17a18920b99e636d69179bb47a729752d45ad829f4b58c359a191303418e17f06d3ad27e00fea8952352b1e3dd3c78ead0ca479372beed7ca8e32b412063a68d4d1f2265ef73c35e7fb649db674a31c44775a8955d290f0d7326e5ca31c491fbfbf4ff6cf0ef402aeb8ee5ef475c165b0afccc03e5dd4fd407cc460d4f3af07c15dbb806a9e31bbe3c6572676e64ec75caf3ee31e1ddd3dde0378d27e86090e95646de971c8d539455ab74f5cf70a617a800bb4a644a3436d302f91af434be1e76d1ab47bec02776e994f69b907f23b5c9d297c0c56faa6b63f01be9350014e52c87b74b3a1b5c171a505bf2a22cbc9d3e3d410359230b63ce8d71c6ee628d4717b01813491f112a78208d872b20d327fd69e572d5640a11483b5d2492d5b8ae3d9314835d67f39491457ae56df8546d9e75ef2b54d8c4282a32a9153a13cbb5650030ae001f7a9139c0723454ceb066d29067c91233669865b37cc2a3674d691bcaee626827f6dadeea36486cff0b926f98a8916423598614160d05c5c8fc425f0c8502bffc8173ca957ee4f47612fc0842966c9665718bf54ca80ce1e9fd9910e51871729608138a2bff5a7ea11d3caf453909c204f260adb1b3214c8c4e482a00082449ba842f85cf71047430cca97109f8178431e84cd9d0b2095e69f5cf51290d4cd56218710210e29799825cb514f6e50b282bb0c70de38668906623849103c014841e9d653249ab4ac8d03c34385a7a0f6b8ebd722e3818ea0a13435a35b4bbf8e49e23918653f1f5215120cc109492d250f0c90b85f7dee80b86fbc7e73e88a4c014847f6f8f61e39f1de5398261b40c08c1e1c4244bc0f2aeb77b2a7ccb80bafea163db415391250cadc211c9dc703777c38f07cf28b0ddfdc7414375f2255cb88595dfe2c72f2154df0e1db743e605334b4cc22af08f31186345add94a26e51a5e92d593f386e96084eed89ebfdf41d79392589ed642a23019514e4237fb6e443a39690fc36fbe8d03074f5ab7cdbb5c033150d5a5e928d11e2d2587cfc39678957191ce0d61177fd84a619e073129ebc76a0e84daea9da1e45582d59826e2f7b6b62ab1339c38819b01f3baa7259eb12732c0f77264bd3f09c0e853ebd9555d1e7de96d8be9720041fa5eb0a91ead424900646d8377511d7148fcd128252f9f12a3b3265538a5068f53cbf03ecfc62992bc36d781060e9a811bbca1256b6d1784d497921e0b9e7ffd19b5be079d7f3e8320351521af25a471cce18f0d28a277c0a5a64bc82fa50fd846f2c4da31e14d6709344b8810cb5ddf2a215e8b6a09e997d7f3471544941015c4c5f5e2dc5525c4e7803d41fdf903b3f34a08031026f8f8f5fd6bcc28f912124ae90c91a59a08c9aa08db24523f013bb0c8bba7ce44b962ec498c91bfd670205b04bb0c3958740d72436ac50ec887a28ef648814565e17595890311574f23e8fdd590f31d3dba7ed79759adaf06421f07ace63889553578afd9142c2e82faca0f6982fda4110a0da63312836768320956bcba0158fcac671b3f7f9d91acf58c8db68031ca7e68f0f452a9d38e142df1d98fb06bc6e60aa1e00aafc9d88a64966f03b93450eec0e8bc74b58411cb9f6ecba8a4c422c64003b830091eb99a268f7e9920678a69ac8798c4beb108fd6c2c7b872c437b0f2a1b4d83a75235d8eb4d08ebfd952fd551d4d81cca15fba87e014cef4ad5d05bbd8eed0c2cfda4a4b87c897726d00bf99439480cf8efe8feec8c08d86f19b566cf5314fb7497017888090bc5696649cbd13353acbbeb819aabfa8f9eb64f90c5056b0fc39fa8730cbc5fd7515d32f4ac70244181075bab8b0aaac42bf094e7c8726f69604eb1ae784aa6962bc9aa06562bfbaaece53a406700b268fe64843949cd66642b55cb0e6cda29552df35361751e6411084b6ec05fbb212034c8a19c885cf2f4a01d5cad02ccbeca2cf04f48cc0ed2f2f01b528526d711de82a2e59d4e92b976fc02775ae5b4a09377ef2d45a862521876851c19f845e4aba3161075527052e063f44552e555d3bf67c5c4ab604590950923f4b28cdf5d6aee7836026641894f7619b2ebcfd13319e937ef347b4901e2c11fb35cc2a9b03b232338c073304edd30acaeeb3db5a39f43255d6f73ba7e32097456fcb42958590177206a17376405c742536c2d3e67bf0c7d388c021c3dfad68415f3db4305b2837ddd32e253e1f8a571a385207aeac822454be6813ec69bc4dbcf1b94dfc6172eec201082d3e5dfcb53facef34067f8e22f5e463ec113e5e4a56f95739d04422899937f9a694acf8b60076bf504dd8bb612480734826e6306bb48994bf106e6ad98ae0f2d8df13e5a87c5d8414bc21d3944404800218871dcd5357b741349dc74715ed0c26995c7a2fc1af14a38d5deaeea19ad4666962c3b484468da771b3f0dc15230dd185c94f7841702cf3d1aa95764ac544d1141dd9ac9e8e30a021f75e749cb9bb6c8d71ca23812f72d5cb8705a3a9e85e8d6b1f6db567ae2e765e176138354c832ba0002367525ecd359d9ae294f72d8cef0d7e99652ccde4956ac69cf79f213b380998800a5f1a05bf79b43b8f1b68dc4f6671e5225ad9615681d327beb59a3974908d8020dcabc9aabd42a22708d1323003b40918cc0c0b60eadd2f93e27ce89d853f49c33276d4b3726050a7dc1d5ceb9ba69f269cede4110d216daedc7f13bd7422e17fe128f3e13d9f3dd4ed958249de5e75068faa4b62345e160e3f7c708f009ff1aa70ed9dfa2ca6a0abd8e0008ff020bf1fd8d24d47924f96333e5418f0a285044f00c69eb5156d3ede4643a5a1ce6947ef1252464515989ee1611c2c13e66f61feb5465ea62d696db8858746ef52b971d1b84f1aa2d446e7e5c91178185a8d7148a498c672ad5131e224447b4dc3e69cdc44408ef1520da77486427880c3ae456004da59a3c7fbe81753d658924f0bda449d4c8005a290a6ceeb048cff2315f4845daf7ca3ec41edae14403e9bc0f49ae1d916e7621ed9f2adf82a0c3b040b7bc29efd53bf652ab11e8c0c8503c236dd942d6bac59e9e40c00b52cb98de178c31b8270bf67611555654ff50697a51115d6c910a00f0e0df56b277f9e27565ad917600a2b2bf7cb9f024e0ad4d0bba68da5226ffb88316f075b32615c917c262edc12b16c43a9af03af3e96c8d4e5383825ef3582faf2eb90f7f94c5d73e550bd918b83bab8d860c757cf018504b820f6e3b23f43723b3240c04dd60f5527d9e137223b28ee1725dcfb1c812fd7c8bfea284dd72041e3dca6ee421dc360be9c3a90fbe02f15c39d6aca0dbd70c86d5874fb4e414eb8a3894253377eff1b25b6d287504d8f0dae6021e417affe671358beb29940e35b8a565ef0e19dd58e99246f82f84f306ace64f94c64d32d52819663d36bba18de728457ebd73d9973de25475f72241ace99df84a7e617936265b05f2b416e4db1f7c08d03d9490231d05c4a4ff083e65daf295b5e88827a53fc8a80e97cb186384babea888395b4a1ab4dc93950f2818c55f56963e4536fa04205b1fa703ced792d08f1101e9e07d759ba64ca00112cfaa0faf7ebad65b48f2b105d63e492f76a7799b6590775d1658c5ffd8230431a1a452735bb0d5f326e3456c1eeb1e6ace4158d49a565ab066617dad28cf58c8d5afebc55fd63b6008827f0b56d82166bb62cb1b8ffc64bb010a5241485b404a71950e110035b5253ac87902ebcf10b9247621e9946b425e361d2102fc5794dbf53b616b30066c57acf37b5ba0f609ef14b54fe07e9bc3dd6da680b8b7f3452985ec37f6a0d8b064aa1a81a8175f12b2f7267b6f29b794490652048204d504533146d85a887f47f336485dbdc05a1ad6678a8dbfbb7f3f14e2ae4f02c9f6456b6d77bb4a150280824c9fe2ae8afb7e47bbaf42bcd90c28d707c2fad333d247bedacd8494cca1c25ddc148787cda6000869932a44bd4d7178bab66ff24647dac8e77f05718ab50c2a884fc8763c5b1fb6fbe7a8b65a12f9fcf6e82784b482b00fb4b5e9ee682aac67800a9df77188470edcbfd2e8be230722a8255460af3dc20932ed4209325254b53fd3a03efd2f44f6aa67d97d0161874c3b5c8530b99439e4fb4851b6fb201c810d3f5948a69df85327dcc0b78f864c3b59a5891ee2e4f3f7b543f7effd0faa1a7c7ffffe540a6e4f468af29ee2bc770aa27befb3f774b41ac8beefbb49d44b25cc61468a72dde28968bd208358d0f78d1fc4ef9bcdc21361ad1a963f8496822af4c183d0907c0a6d2ba282a09f90909087da27fe80796a86f0f3325214fd161f0b71a6dda5f4fba8ee3252d4d52d9e68562fb04f06c9ef51a87d110dc8404409f914f10011222cae4a8513403972da34778463979be2f270ed7c01f247defafc20503520b2b83c5fb61592f4449dae68c20d7c7ba65eb8816f9aed2345f9062ccbf5ec4613dd637dd6ef3e9ce0c39a5c9fd65bd469a75285c4ef485b6bd177b91163d330190fd4a6784ad8ae1d50ad3f1d290e4f131c1eab6aad876f67eb8bb8afafaea3f449482bd1f9f92819f9723383c7982f2c5a65038d2d842b3482382919f9bea27c69e9fa52e18b052ee7d96de5ce03994cfd8b808ad1dd8530595034f019a0531ab0f433fd1909219b811ddace5a10b682a0ee1474445741e03b459e5d53d45dad8b1c245ccd8062024244090d26cf4bc7996fc6178f0f0be7e562875f9b765f495e3fcea8d8612689150b1b42b864f9647c09f9b0ee98b903074c101830779ad8b9fa72ef58d92e77ae76b729ee4e96305eb870a953a78e8c2b7a268c549124584e40a3c217c34e922c6707ca4e905acd83969ab5d65a5bc3d55a6bedf37007823eefc11345d08679a892bddb6db003411beba2ee2f15cf7231c6574bacfecc3e4df1ddfa7c0749f134c674ba36068930c61ea4d439dab24d7174dc745facde583a31c258bdb174420873db5409d26ccf8c169cac37715cb8f77a70d2ec7bef9d32c1a6b839521bdc1437276b9fb986f142fb34a4b2af34a3a8136218d60f1fd427626fb591ff7f909375ce76c87b99ee1ea6ed87b476780e1bbec36f78a3a8133f5fdf7bf7effe847fa74fcc665a02da28ea0487776d54b343de73f80e9a8336226187bc77edfd066d54337a3be4fd8fa676c87b58ec479aa88da2c037121ff6a03e21fb983672253be45ad4461eb043551bf991527dd746d40ed5378a3ae15fb591f7fe465127c477efc5371a3fd77a703e1a4e3ff2f1276165344af4d1bcf7a119459d085fc3cbbe87f681f8b0cfb40fa24e88da99ec90f7b0ff40fc0f7232feab5dca0e79ff818da83f308aa20025ba8f9f12c1f407f729d1e952db43aa71b8e41cd59890fb246a082401fc14a044f82be04497c8a76cf58858ff202a2a0f544142015e98ecf34b2ad9db34f702c93e6bdbadb5b91750db3ea5bbe60758626ab952ab4da9cd6626d5cc94720484879b17759ae01942f4c2a64fc202388b2aa4bbea0ebe6ed7fdd5a8448a81ff68f728c6b6388a31997f3dca34498e058e2d21adef349794862b97d4b514d2201fd185b0e993388052f6afa8d62f4fe048f229944bb2aab92429bbfe870c6a965dffcb5842dfb40b69f5c4f0c5574104347e6c466b16a2a410253d08e67d104c9f411474dd5bba67f85d44216efc332a9d2ec25ac87fce4cb3adf2ea1c089d366521b5a94e5137a5a405a193c9fe5482ce5a6badbf75514ccd7c320b329ffc58d0c3eacc8f275d1bbdb2f4790c4538a8b9ddb805b9c57c0c01bad81972e1c746bb846c2c6e386e0bb06dc1d160b372f32d5d6e329b14bf5561193e366ca3573c6e7ebe50472a4aeae88c0c2b271a8070dd467cdd445b190bc2732b14e0002fdcb428b0f99c01381da53817d828941f373c37096c5868dc8b0c32d80c60a35a406e42a26e5d026c48fe854ef130736eb48c97b4e60b4a8b8d16553254d980053173bb549848e5b9a9b949296d3a6e706a369a25cf08b72d606e15d8a200d136de25c226c26d861bed841ba51a6d5ed3cd83cdcf04c096bb65604b61cad1548f17395c8c0d1a83c738c385857eb9e2201cf17273a26e60b6bff7fed47befbdf7dedbe1ef4050771d967d2f6edf1ea27bafee3a6b44031b036c4e82921bc5c2c65ff81d30dce87661854e97394b92cc5c595104080b540c981b026c44fc0e7de34be80cb82a5637676a710bcd0db63a6b6e74ebea465b1873cbb6109ccb0436109c9224a94b19178c10b1c26285062028dca81600d8dc0b89b7a154fc4d8ddb9ddbcc36a4aa40e10593386cd27041caaab2224b0e42bfa8a8153cb7a91b94db071ba502c2180f086883f1044a4b09ee8486f1245e0ab07996aa1b999b9120afd3012b848d665991a92adce8381b4bb182c0a85ae1e3898b98292ec2201447d76060a371b3c0f6e44d565018ccb88b1b4de371aadc4cd0369f71fb2c6093a34b0e6bc6095e4458cac6b91523c88008c0e65951281a1f6e5ae8b1b91d2e1dc5d8fbd3b11c134ac6efdd97677ff56746c97477dfd55be89fc7caa6b819acec0f9be266a8b1e96802ef0f182093a8348892088772ee488907a102e4fb3ad4a07bfa3202645be2c3dfd3ff9e5cca03c8de23455dfc415f0582eebdf756a1fb42f75e21210f5503fcf7137f3aac2bc849e106be3590f779def53ccf0b21901ff6de7dcff33c4dcb4851e08bb05004f183e2857560ad6e09bf93d95915f220876cca6e7305c14177fa0657e7ae0a055535b5fab6b5d65db5938992e4689fb5ed210295464132c2ae75cd3ec53d7380896cdf73f73abfc561b267c7772bcc041fe0e4eea9bf3b0924d33fc57dba08b6d37abdee9e776375dd87b733414fdb8d1c8c26ec77fab45a771403da5192be0aeb554778558c1d789eedba1b49a75c7d4408aa1649a7dc759762f05d9a898e52a113bfc701feebe10fbcb6731bbb1fb0fbeb3c13f848288000f80a42dd5b2bc556daa66f39a86fbbfad6fe1d39c0c0a94dd0765167ab876d8d90d6dc11128a29209f72a5800602dff895f05f1a0de2e34f8f28e8d7c61fd3678db1bdfbaee45e7d7af745323ec245329d48c64d4fdfa34d2b50f1e312f0c84e60d882c689d49029a24b66dfcf5dfbcaf0a639a71556745eddad57aad7e45abdce4ed1458b474bf3af846f5b4151a442fe4ec712755bbb37cd3905d921ac84ddf64318f56abb98fd129da61ac3083fda7df5da4e3c2db5feee7e3ac555f8de8bef7592b1ef7735840444f7de7bef65e394549ac9dda63927a4224d5ee68eec66036d9a6bea62652525c459f0bd7111bbdb18d1d2ac46b4f33581e0c136cd35257d6b98cc8462a64c6d6061f7d1cc7407849548792a9767f2968c33e50a8968b6b1e9e9c8abec7f7980506e36f116757f24099784feb5c33026339b32b5d994e9ceb6d6daee2a8d2c30c6de0705638289200882630998a5b94d8aad49ce5b7a422e0da2e76c6c6432992ce7247cf403f270dacdb66915faa1be7fe8fffc6c7080d53724b95bd4180b77389037e5e598aa8850346c9a63ba9a137af6a12128ed6c0532c5e0152bc4546046f1daf8a8ba7bfe99e17484d45e8c493ab96b9b37c6ec3333fd00b3cf876d9afb81dbb14d733fa2ecf3eb9890d6d0ad773926a9ed3df1d8e7c7a4b4698ee9689f22cdfda8b3c34d733fd4e80d34e16802a22f6e6f03514d2949734c5536cd314d6d5ad6a80534c4d711dbdfd4ca676dd744e44098da3d9be68ebe6c334cfe20c2dada878e2622b71465db4d734b4ff6591b93c23e6ba50b51241c275369a93cb251fa5812815461e988d4a2b3628d89646684ac29576613962f282b28216730426e0952ca20610b0769c5078cd28e8f1574ec984bdae4ce94296895362ee4152bd306d58c15c82f2798524f4e3f56d061b6e82143861e48240b5e77de7489c169ab4ce3a58e0b3fa6b46862321d29899032d84c183c4a1d3c6894e1d1c254010fb9240e0e34e6129912c90b28e04c5b951c2c702529a7a0c434c9f1b165470d373b9258c893664ef9430dd9c5455a6195d162beb852f29812a24217134a284048184620f3dc90d2f182850e104aab3a251936e4160c5461ca365b483455a69228e5d212b28e14d982103725984772bcc911a33421070b2b774c14dcc8e92a6f88c1d3858ca185f2494af98589c9040592c910274c268e1d371c33ca30384228997c9941e098486428978cf1fa62829085cc428584e2c4ec81448522a49a5bd9a44799e58692c9c20d176495196cc8295da429d59431858021ab72275879f2e48514ca1d51e597d39c25d28c0df28e0d11cc281b4733e8705953c660864c92651e5d6561e901254a49a9c5480924488e0433090e924a0d1864931a2bca397694b431879c99a24562e1024306adca2419a4ea62428e39224291184d65094b259c1b6794609060945890595e24c0315da0298f94298d6099265c91295479a132733029571ca12a426a6902b36416b951bea001837c81060b928c5719048ed9044d9c32a4142c12cf159a2a9304aa2a264e8ecc50847cd3540e59227203c80c186594192ccc1a5e70b8d094b83224169619e3aacc5345b6a1225d60b2e6c80845b09a4aa525ad1b3a64c0a8228345925719070e149aa73222609946ae4c165562a8b83021a31c295b28d2d4443e592aa5dc0021060c33068b2baf320438a5171ad24e19320c560a57258d2a2254260f26e6ed08a9a44899d46496b074c20d2c3060983460b0286be4cfab84ca1f1c724d9a15cc9457b44c1f5e9ce04a36574c1951c8d569de6bf0a48abd712cd123f6783e6389dbf3b0d10371dfaf751a6a3d4d7618866158efcb1ed332cd7b12f6c23eec447fe1234e1fc71fc31fc30dd986d8a03f36fef9b98745e0df0fff1477a83d16d340df7b5fab8d2ffea8c35a376a5177de532fc686811f088220f86920831dcc02a1f2c7043ef959b3f6f50ab54122dec704551d7bae38f0eb6310e75eb25561f43d7da0124b2b92c5f0c7043e59292f268b11f9916d88e9b36ed96fd067dd9ebb7fb2ef44e1b9ecaba0e739e8814af6097e0acfbf27506a9fe007ec2ef2bafaa0f7b1cf8b55106a834f69088f0edd9fc086c7daa7a2006b8e79cdb2c7dafee76920d9d707df713644752f3444757fcf0326f0097f1ff82708b52b8e7d0c579c86afb8cbc17bf0ebf76007fce3e84195298c93f2be03f81cb4f71504b3766865d65a59ccfb276137d0c6ffa17d3eb41b7e4cd5a321eafb58a28e76cad62a3484ad4287ee5fdf6ad343933dfe936e6dc97a6894881561ef81188b187cf14317f5f93981443ea79d65a76c151aa2031dbab87d1d57f1d6d6e7b4c51f6912a0e4f79fccebf04d800e7d7fbf2d9cd3068980443e26900806a140226e7a1627010b3f58540ab7ba29ffb690ecfb6475c5a3d3502bdcaa20aac5153f61daf86118b3d6da18ad48a4326b63b16a7d422d1f61bfca3cf6a2fbe81f12d56967b8b56b78ba571fb7ba28f6e2fb9f28541855cfbf8669f6e9fd472bb23f82ed5dacd630cb47e090ab8e1ee0702bd4fac2adaa1d57ff9827553f633e1d883ad4d234209109639c55ad0db7be1fc71ffbb1d10331d4eaa4367807fc45f50777d0a727ddd727077d76f8e7fb44a51dc6449146772c1613ad0eb5f08faf747d74a5792fa37d4fc2e2f0ac4c4fe3579932ce18c7c41d6a79d228ee31e6622cf6e1fcb0ac81ec1393689fac7d4ada9fc77e8c899ec414fb1bd363a825ea50cb4d4fcb47d0fd855a9d8f7728f2b47fd31c902c213c766dd35c90ae0dc4ca690ac7163787af238e2159fb824d73439c9ee26ca14d734f563b370469875be4daf7a826895b22d7fd3ec735574c4124896444ae0a0263dd55215b69f9c3b0eaf1b17ff8abff61b1cd5869a7ddf87d3aff11ff583f1f3d7afdd13ffaacdb8a5c5557abc2c82b3e3fd8629b7d7a9fa2168dd5fef6fe7b71cb47d4ffc42e3c7ef5bef7f429726def53d4ed58636c71d5e0b52ab15ffdb91dc4fcb1fddeabb5be7f4d12b7fc47914be4dadf679a6c2cc1e15dc7387c3d5d5707afb30fc54cac5f84bf3aebc21d74d785f58962c6a1ebaafafc461c3bb4d6dd833bd6cf1afedba07feebbb8e58f69defbd0c427616bf81a68e76cf7d0724daa429fecfbb3c648da559f286418e3d9768f4c0c2ff6f5c5afb31dd345f8bf1f451f47519f28661beb8a612f7b1f4db84c03813fbef7301c756671f773a65f7fd6187bd41fe064142866019055a8146b9a705ae7100d0d004000f316000028100a85c4226192c240a0caea0114800970aa3a5a4224a24a22811043411083200c80300882000001800000c2300c4619d1fbdff62ae26eba29ee702381748989b6e8528981b1b920e4fdc20cfcc94279561952ed9a8b581f2526ddd88277cba8518421c1260eb455e3c559a51523db249f352906f7c5b422b74e87c930919632c0162367ef7e7145a0f7ae1a3cc91840dc9489239750a51130de45d9250d23e5da6b781ef1668649095d7b377e1b215e46ed86ec550aaf72a94b4dee326d83aea7fbfa14676077cf42e99281b261a08224df6aa74235512d979242f504b158675df0547f09315a3843ab988fce8cbf5db3de06bfa1628541675ee73e68e0edd81f63a24950d11cc4799479dc4dc79cd30c4b18569849871e2665a0a6d09cbc8c8b13fa19695f912ca19a21dce5b38ad6d7f5abe5ad84c8f95cb5c7b32fdaee1ba2ef7c47cd0b0dde3a750d62c254c58cedfe61a2f7d96b5a4278617a0a6037e1a96b3b863bc2a18cf27f30628d708dde6b1346a0d4150ab1dc930aeb109bcd084342d26533c2800f6485014745255291008f2d9a049749b4d44af1fe00fee2ca940ff4e9d7516f981fda1feb767f14f8de20e1bbc89b4df3dab6110c3d8babc157e97cc083e6598914fbfec8645f677f08a7e44d28debc96e7bc3235c61f5c97776c749ea840cbc98519aef79d46d7196f1cf6cd4e5b894b96a997c63b20066b9a06506bdda7b8d5661eda011295184c2f7071dfc62b218a63308a65f58cb57bc8a1ca70e187e0dabed83f46b5721db96fe62270cc8a3d2d4dc11aa5788a59c39d5cfb5840f66126b4e85d9c5ee739ed2f05a10810243605e5771f577733d978b56cd60e9682f9369a4301513539077443307dff7bea88ff48a27ab8a3ab59aadcda09380a333b107e40051fe0f789ffd13423d210e11a4a4b7423507f2321600f1feec02be989db3bd2ff1f048036f785ae61f1f98d0c5f3c2349c1ad3710a2620cc4415922f7e2f16d8b216b297b1ff8ce424ee312f8ac276604888e2329dad0b74ed5c29342871e06ec4aac9108660dfb04468acf7c89fbc3e793f407a75581b130ca4d08c8f5d996edf352ece51c08c0f80e02c2927b31ed1ed9030908c8de44e69baed2a202a888d5ab7fb3567f9ac28fd244388c049e1d4e2accabef86d9aa5f6195663850debde5867ce4771d0db47a84c2ce22831944cafd633fc9ce843ecc3579905c2f46af28176f9bd540a68b47ae960a0f1a101058b7088fc9bf44700a89ef75f6b44e7f2a027d894c346e1b6d27189bca1732ae83ce46dffc6af3771cb44238982f308184cb6e0701dc7cc49a66b3d339a38418725f42ecbae343023b5dcd73488655d11b0afb32127bf157f5629eebc31cc71647a05bd39b17346a5d789ca9d46c096202d43d47af9b6ee2d3d105cda34704019504d10561a8d264e8ce3082d01825f8c26dbc08d9fbb1eb161609e2cdb4cd47cb36756b2fdd86a8b967cc2b30b674f1e44ae65cd7e9cd1b291837c1cd77ac4b65ab0a136870a1096e3c784f7a67524282c44c3415de80021a14ea7044ed3333e8bbc44b594679d42e406864996b389ae01e8b0405e50812db9747b7ae3c3638a164923bcfda630188688df39bf0d4f50d5cf3050fb0f37adbb5be3d39f4f7d293b3fc645c19f81217bb7e1f18855213eb523af511e1ee8a9121983342d122df4233fe229fd12e410c8e4faa3924e41c29a453612dc1bf0d7f4244c6a3aef5e3d1f7da4c477cbca1e266930cb6e9350b9f8d1262236265cb49669281da9ab2457b30b0c6ecbeddcb9e3187d28a6ecca6133b155b356f0960e05e11d11c1a2f9aba1052814515b45acfc01032635dc14b76d93cbac96408f1d6ba722b67650af6403564ae3dd77799449f5dabe840f0b6b5e6ee8deca026a306ae4cd91f04b406a28a9bfa8607c148d5cccfe63a23bf17c7c939ea9a24950e58b29e34176de9f7a25850b30e42421d94ef5d6e7bbbd16f403d6f779f2a243276fee9887d485bebe850516f2c6993f901a2250ca9ea9a5eef88334652ccc86df76e93090949589d715449e0dbec9d23121776964c4220ad43788826125666eba6405d0c21d7294581d54f1182fe04cd24cb65a5d376aca18b2e910b5cdba857554d5d5a5b604b56d6ca8cd452b44466cf1f70983abad5d907160a66723106c7125ea55c674b8567ddd20c3fc24153b7463c5338d26493c168526006f03525e37363a3420dca529cf3c1843c8b3f5736a96f3155e145530382464110713186dcb3214e65d74b897c10f13a8683a7a12b5f2926cb8e9f1c8b987b89a4c7599634ac87ce3e4f6de26b24741771b0db7126ea55bc2d3d2eba3c76329e2aba37630c44843a857a65a43ba02cf392ade3c23ad1a58e5d6084747ca98bc5f491236d383a5c037b299fa36f026404241c7aa4b4f0aad32dd9878c49a8cee330090f1fe5ffcbe646cef5d8f462f65b244792ac7c3ab1c19116093e5ea4e950335776ab1c49962e1bd99c0c8044531bcda81d19c551a34c4dd12dc5c93a19aee6da8fb43d8110d3f6c25d4500c96427cb126b50248de696eaa25c58fe58e1686f1b0e8e309e373ba9ad5462a57cdfee5837406ed592779754184c8ebb1a08cdce8bf2f2151eaf45b3195bdae9c07da04c50cd70f2fc8e85003167a82e190bb987d1741e5d81b069876adf895e2e8de6c2c555acf52aaeacff569567da41e4228e3865533d349a59feb027f6876701660fbd06ff5c5d98a42f01a7c362425756147612b2394fd2652eba445416283d628ee3b5a4551136f195476d700502edd824079af50c4034b01c73626c64dcc57a820cd33f0067fc3c575a6f6805dd58ca48149f5ccbf2c850e8e90cb04b4efa2a624ee7e98079b3c968664f21b284d30d12ee0e013e331abf305aa7b8ef480dfaf209a68c5413da982f85fa3daea17024e9d30a16a5ecd00597442d66453b9cb31de220e9ce5d0bcb80f67c0e8537c62d69042eb61ed8993bd9219578e4f572db5ee309d0c33bda6487c0cc18321cc9762ed8f30c6a08a239c6ccb55aa4fd0a9f0ea25933eb32d096e01655450d11a80822bdc3b0fadec7978f3f688a7265b2bce8a31638a2a1b696476adf504d5b0926cd943dc7f22f15444f03819c02c68cde10911566c1a7eb068c560db4e731e1303b81b060203f15dc93804e0ce110d98c158f0586aa243a8d5c07a02081de0ad3fcb7b68e793cabb71eecf8ce181a0b4516902f41b4a5278d3d1f0b652e6ebb99b944c8e66b3716553c14c2cd9ad30838bacb80d88e4007de2984bc4a8e147ac854186787c2b775b22731b9426357d6a15563c2e3fd6f148f873810e54122897d83619c27807839c3e3737b8062d32835f8b3126c1fb1f474ea08891f198d74ca86eb8cfdf0e32675e6d50e334810cd90c45334ed3f2b95a89f79102b0fb43935a5b8c148b276a4a03dd17687909d49f8198047074e2cfed12ecaff662e91562c6937b9c89308b605d75cb7c355b1be2ba1fa893ae1156f0d23f78069e2dcde014745802b8d460c949a034f0366c57fcd25b7bd30a0a6e77363769cc68bc126bdf56fbaab6740be2d4d9f4fb32ff2d4392a52cc9bc7f2620cb8e8677250d453e98643db7c4ea0eadd13ad0f4889948d01a76f0293e7268c36ac6e127b30abecbfb99a1889858e0bef4f1bc4d22dd1b3e5fa6f85cf44e530682b642f0448582edfe70ed51199fdd348445b43448541a58ac1ecf650206192116686cfdf798ec42fc1c948e75b0ea23756baf5e6d271fa04c071a5a53bac3ed848981c27f95123bcff1f2bd70888805a30efd16d3cf8a85151d87bf2775fac6921f33f1eec9bcada6e57e1367bfcbd3d1af2df6571008440315ea834742f5b591f6e97a9b519e668a45dc1a2504b5492b08a223bb24eb6d914d09f5d31246695e8298341e53a04412e7049ec24d1ad411d67701a14e2b80de8a918b3ee9860b74cea09cac2deffad266ca87c68c134312e6097444d454da20f13464f2cba1c504d15433e14b822665a5d7dfcecf79693c40c8313637283f07ef83b4fe013c9a0ed039a883ed84e66ba6bcaa14d7d77e77e78b6a5fb15908e2553722ad17a8fbe3a31c5500f00133e381b5b8de3ecb9a09148751fb57d5323c2270418b42a0e6d0b2eb8d208b272030dfd29bf32b9b2bf31a23041a4005d80c2e28649bbd221ef390951190a1972ae58c827df44829640025b056f1f6ef19f3b16d34b976fb6e55fd9c8f89f284616d00fdd00218eaf619affc54db2038df7dd851789a057d1065697e18b6116f4e420a09e01ec6c98ae191a4c275cde6b6af56941bf3dbf2a0294db8a06fb66532cf0e41eb21d31846d365ff9d92a7fc43a4b859b43a9a0d1d116166f20eb8d83b9d1e8710f40576f658e661d65af0ae9f35ff2c72f7ca901c318af9ae133e445fbfc8097eafe7e14f6078414f82bdc903aa6c28afb06c395abb50c92e466929c46d426d63e848b8ea5b48d7d15a037519d6d74cc9b9984b89006a8156307001433fa0bafa03756bc6a560469b38f0cbd0b3ec385c7cfb85e1ed0dd91d9c266d2886cb80ac04b7b64d9e1964ad40b818e3ec891692bcc852a8c0492a6416a796693b815ac4388efc39d12217109c856f0208de63c8cdd963cc27571130891a17f6134f0dd6365bd8b64346239dcbdbb96787913f2770a8f35e5a1bca175a502bb48e5247085ff7bf597351f97d9752cd423dee3451a50e5ffe2a9b834fedebc03e12bf9874a9d4a8ac5aa142b94d6e4e721ffbb3020758fc89c31acd6e4e291bfbf180c9b776a297f63dbb2f603e9f0771cfe467d7ad9fdf90489b8d085f60468f79baefc8d4f6560e93916f3274591258c0569a84c24aad2ece174cb736f0ce0778921e8c12cc48d2637e7880461e21723d90680896b014647f5b509b6e5db84de4185a1ab2331ec1452b8b1d3519f3f7c3cd669fc8c0f713c7bb47b685afd8aace4f1573fcdc426f0605ddfb79daf603274ebc0900c6004fa8d7904079efbaa5672ad3f85cd28df862f6372edb1a8f300e3fcf9be74e70934a952ef93537d867c568cefb699fab94380241e7b637fcb539c378c725d6177e55b357636e84587f6b629bb9e38afdd2520d679625b8173b1e3118dee08b0f9b02eae6f9ca96df2901db47cc835ea0469585f51984bbf6ab3cb625a2b7d46fc42accb63c5cf33f3a064d163b63610eb9ac582f403f05cbc832981f95eb7d52a4d6eeeaaaad8c9827a2999b58ff1a3c6a10aaac370cd0582f21ab3dbd03655d74a32f66ff4f92b3185f4778e5953c17bb26648170e87cc75bfe68b190f6ca2899e19adec5becbaabf1e96a074072f986ce9bc4c7f4d7edbdbf85abcb055f58c2a1a02c31e1938ca815d5c94897971aeeeb4bf1dc562cfee18c5d33b2b5e1111270ba37da4392cb7c5339c22bef5f598d947a7777319827120e65e37d7c1be4406a6e95ce6a0d66f65d989292c311b40cb8241bb9bd65278a878ea1d91f54e859b497a79ac812ea1861867d93c69879ecd4146d0013d3b30c610f78817301e14f28d8f86150d3a44987e9ac2bd120ff3544612137c4e4f0b78723dc930610c35d848bd804ecec936fa29ec2feb019023c055e286ea9ac139c2c21f327ca86e23d18e5ed93b0b3a7fc9c62a98d300619ccc020867d08224a7fb2b202700db2a4e323251a6c455452ce55e5c1ec1e88c1310e3b785273aa8b814641e46a2b8c179bcf2084229b8c0490cba7b5def40c8121e1d9c928ce6eb5d070031cdf85d77256b0bcf99010fa5033917f6592891d106c077811d248bb59cd31f334323dfc00bdcc6112442e79ca0a82583f47a6b34dc0863e0b3e2434134d822fc53d1af0a1e71350f57e300b253d6f799dfee9e100e69ae822e029e8a5624bc959ce0f1107c20ea29a264d5a4c615da92602ce1cadc59f0487022e8f9180cd76883974322c38c0b55370e0829205dbe0e14eb4c3498c33afeb366e0bd8916777515e5b4035d2f421091e36548a0ef0510b82b7c2f1a8fd050395d409d571face8eacf52e10c64e3fc571ea66aa7a2002bce16b109e69408cfa686280308daf9cc3a99bc07e405dab5b937bee7438ad72975e822d81104b247ae38c1cfc8ff69edfeb51924c73b4812d3ba775110b4749d6c654497b1f489e64acf7c811c28d45177726f87900f5bedc33093cc48d5dfde98c857b70255b111adc8ac7e04adee0c481ae88e6e81fa2439d9c0474c0a5e20ad9497e7142f985003858ec6cccd644d77b871555d8e993ee8613c24ed32cee3d6fa54ff5495f30e3e24a26f444ae7120e1235cedfdaf8191ff568f8fe7b8d67e89dd39d7a3ba82a1c3a0619dc2f886d0f8bf920843fe9bd905b09acf91424c687ab16c5341e0b562e1f5158105260bfe4aa4a10e09b8f555ebce7fdf3afa11e8c4f4a6ce709effc6c75fd00121327cd8fb6ecb6f485ae46fdbd16fd67e67f865f237b07e73f88ed1001f6a8fd80672134d3baf34b40317e7093062c3654360339ab117b0658e3a0ffe4acd816330fd818b7b35b162695a8d9c166f1ec435e291a4cce24173b4742a35980c5dd66fc81765daa655b6e851154547526f0b97a65de898ba3f78b98fdec317c50a2e461082118d93bcb5bc327ad2f19ba3eaa891a7bc100a7b8b340bdcbc34ff8beec2ac7dfdf483a5c58435c8f8149ed02c5d428f6cea6756b5105c9250f05784e9d108fee4c7694a4fa5fadc25949969b68394b9faccbf8445451cb9206e3a4b3f2ca412754684e7e720dee50ae53f9efe4fb1073c25fde01fb48882601716e146187b15543c22b60faff4a049beba848afbd5be11427db143772c4385b604e3056dbffa94366846af32a61832784a6c21821449307508a7ee378e3d5889d45aa8fce97a8d8e46a1b05b9a3f914538dc0c0e59ac41c74dfec298d45ad7c429b344be6e1bbd510438e0708ae4304654c879001dfb9bcfe74cd45c928a802cc0cc464f06009805750be47c5de5b05ff5249d5c2f19797bb5cb63856f94dd2ac78a1fd784fed274944f065b5ea2a3f0fa186632745297f9cf663de9c9f1ef18c38a873300521c0ce4d97185bfc96b90117650004f6ce2c671edab8a04123fd2bd31403bc6843c890c1c5b6719f98712e8947e720f42f8d0ffe183ac065182c007792d8a14c1a6247fa9e6dc56a9e6ec03aa8aa7d43e88207754b449f4c18348f843092a254572e8875b5d8ae8f3a42055c359dbc85432a489466a3d3310897e487296b2c13c11996a24a30771ab904aaf34614ff5cefd4344d1904041bc40ba7acce8470264dd0f6ad5a4bcb2b27122334d826a402770a962d7528ca46c20270aa90f19ec2e042115fb38e05691a96dcfa90432c594bb03a18d90f2af34694fe59dfb07f143ef870fb2e243a9a2954000211237659c42f911093b25ab3c7d787c50409440d8ff90849a1a53ee0f886df9d4c8b3486429029626fea9e6dc40087ddc01f1a9f4a70a22f30fb7881439c9f3204b3429fb4c0a4824da521989e60f13dfd47e2a7e90f391e8035e90935aeaa364fd6013903e036232969c1924f42de9f4316b8a2c5f0e6755b09d689037c85f414e34a9b24fb755a9c9a4f08712dd94d7a76d8494e10cf48792444a793907392dc812e7947eee02529b8c9437a4c9265535731f903fcc7d48f7916d68cafaf3c4666aeaa924977220b70a12493b954186e9a3ed7f6a2f690151223be54d99649b5a490c08453f949c523d9c8e8c94328634d9a656e71c04027206e1838040a641183e4420383e9c3fd681e8407c108a1f0a207f3e282050822ce94bed4ce58f9c55952d0c794d33a50693a906df315e1b4b0e627ac922704f123d0941b268046c4ba29993ac55d75a2d5d91cdd7f9eec326345158061cfd09c83c785537f111a037ab7318e616c148c687e17c130ce84ae8ee788600028922488342212cb02b3540935b478697d334a0b7519445a434d1494d27952ca464942790f3a7f8ef58e9720184ff23fc8102d1064800c4524ca945e27c274315fe2f2b3d893ea603224948ca7ad2c4766a35733f207d98fbe0f9080249811c03e4442cd558d20e425b21e553b63ee0fc61d2913a4cad802c69a7b24b3a03b12d7fc92d4725482e020140276546695291aaf57af27dc4f547259a945d56350bb1bd4a35d9c9762d35f574db4ded96b47e2891d9b46dc49291ac206f1f743ff47f78419892dc36aa86392d51a43c2b4d4e5269b3f001bb754ea4afd38bc5929458f5b20361e5e35a10483ea57c37dd2a539899e669d999a957b2c9ced44e86c5e8ace7b10a0e240d9a09f15fb2b4264d3aa9dd5482c84022411e907a1ffaaa7a12a18f1ee4401f92a4a4244a0e819cec53559ffc2f4c0d67e403c187eb630dc4ba0f25ff947d72013203417ebc204c93f00fb862cd958f9c7fccdd8907fa88d5f4c0fe6bf92c8c52498545e6f943c92f55bae97629555869724ed5e7421f7c90f01f2810f663c64296e452c9ca591fd08290e491ba33b77c28399b6a28a39968bbd5974a952e1091c4ce183aba60e55d7fbae1ca9b915a18a9442985513681f87c544194407e2abab9d5879233a9e6720751e2d589fe9451ec4f1422eb518553ec28e5e96044a37cfa0c6b6ebb4adb2132f840bc9770efb35b05d60e749409b116595551707811a6a5b3ff6ccd00ec0dabd46de5e7332bbae7e4a1359aa6582c9e1953cc6b5e53209bc30cdeb0ce12b5077a9c086ffe402f144effff06feb2c734a1fffc66995375eb2f93bb59412b03f3eb552d8b9db4a3745a4fa1b18e738528d632dc241d31f687c79b92fd63769928addf4328454130d3bdd4943316c4d5ef6d30b6dca0a160d0e29539fe28e2e10bbb38348af741eb782af73ef057afc4c1501a1a0cf2e24fa6e6d8dc83e74d45fb747c78cf8f19bcb6b3bb796a8fb163967a6f9dc22ba4fa66d2c722f6b3b2b649528a824f26b1926c953e24959d92cd6db11c761748b3cb8cacc5c2f0745f060874e9f36489a2480c69cd324c5e0d207d8c1bf3b16caa36dcc31eece5adcb9317b479437c329017315d5b68b516950373e6136303c5ec88eebf3046fed5addb6e0d20dd941c77816fbcde72ef7749e098330a075da48373d8cebbd399f63592f55961b231c28a8e5906664347c413b8f50ce1256b02bbad5107116a3cefe7d0496351b7464c0a81dac64d9340fe1ecf960b09a89d0eb9cdb119f41c3bde1afc191ddb482598309ddbc607b2b1493dc0e167d064a4cd487e05d9f368eeff592199a938c1b2172f4c47ae2ba6b9e0c15721ae893318253518d7e453407e8b5b711bfb92d5546410b41cf3ac36b935ed1df55204925feba0a182baa0c66611e53cf08aed907be83c228aba8d3985c746210c339ab26585c10e2aac8045c4ecb349b4160dfb984eb37276c36bc845400c2a2be5a2760f133d106f49d5c093c22a4850e7589b7e1fc74e2a0e71ec330faf108d294aa0e0b488775f2b62678b8bdb80b13ede9ac4a211d5c77cc9a3767f924dbb9ae3b01417480b72ce41e54dfbf844ad7d1c2f32e7c51db70b7482b2862cae3521434f13805dd2c38e3d37ab05948d736faac815d24de8ad37b376eea788a1ea610413737f8ce012ae62c871ce58dfd4cd1d029f8c2199a1c0f2f6017b8a284d5c006ff92b452325750727a7fd2de402226f0f88a67e7802e784fc68875c22862677c5d0a720f6d6e21d7056abf27f184a03fac2b7e1f107c8b01ff4ba66523b7000705aa6c20112d75789445775a7709392b3571d22d2d761191db00201b5205fab57f65aa0cf68475e968eacbc58b431e999628f327244b6f2e6fe7a7947d1e8645975c11f8ea562419ad2403c5eb015cc3c13657b44f4a56184bb93265780d28ff787b2971affdfec1bb36677317bb9cbfe4cc5e345aa0a59657b914cffbc07c16686174995a9f64abe27197544a94b7a874a45249da1c5e59f4314b349362146c09bff1a2fc48a6921e1d0a369b4138476fc7d2dd3f11be4e56bc4bd591acb7e4e2bf3066ea3801e0f40bef97211dc5958cbcd3ec150d639cbd30dc0b4c8891668ee8cf4da232eaa65409e41fb8467d43a0d20483576ef198fae60489b12f4a9ded2b9120429470f18bac2d300234403f45bb409d26b11bc394140f97e35fc044dfcc871e0dcf939d248af949b2576966f0b02d6804b02f3bcbe706a84a775d6426d9711de303d8016011325b026cf9d6ce7c0122c42b256eb55ce800a0115e4ca1d33158d7fcb3544fba901c99bcb5a2279dbc902a7b8e5bf62012c69c0073f8540088f8d29e8ceb16f83ced84b744f1124f62eb3ee09022d5d1d3a7e84403633acad0f49b16555b8681983d67d58a07fc081fe23f3eeea945b72cc99f26ecd747ed123d3837058be6c2690321458a1250a0f5f73f9a34b4926a459fc400ea4f7548d76fbbb7d70fe16f2deeb3e6abb35d408c73900313cc60e303cb442d48c4d7b5ba50658766d794db0cb16b08488a4361015f16b09131f0088973ea1a4169b4ce9946553e54056db4ac2794b482240fcb466d7bb58bcb391d8089c2737f5400ac98ce05a12ad6be593d6798b2ed6f4cd6a87a2d1930405f83358f98bf5ac6cd5775f545ec02b7d312fb3cae37c82f56c69f1885b47c619a97abfe660dce49801be970159831f12c143e72c85c46653a667a3f6a085574ac66fbd9d3265e7ff6123099cd43faab91ba80ca5b78652254fef78f01d3c322f5d350efbcb4e39b43b65809b66480baa7a2b34c46a9bc0bc8f481d2018914023a95adec65af4d6091e92d96d276d9a02fe0711a7691a032c5f5c434b529305a3ac7b3f98bd3b330a6cedfe3beefc6fa18281c1fbcd314df905bd8023eac39e001c6314c499a89729ed2f149bc2746f10ad4f14dee12f92c331886cec8aed2b50a2818a288e49a5e1743571ec19574358e227d67fefac9157ce8b26a59ddb341ace6645e7268235b9fe0528fcfb65d9f2c15073069037051e65023beaf3de991f7ca012219ba83f9e37d6173cefffb1752d490a1f144cb4d3af1565d6abd4f6ee2d70f85fcb26bb4da4e1c677238b8ab1cfeb6f2ea0c0ca3502c4680dc5b7048d87c6da1f4831c08cfa48c298091a164858ceeec15f16fe44c230a8faf7370225086dc92c365c8a80d123fc13399053c1a642b7e17305179dcda8e3e51105094fc2ba18629fea54518d33052d097868c245890dcc35084a58d64d584d1daca380ea53a08b26417091dcb069f856081a301bc7c308c5f3d3d954383612d213328e370fd99c2461add455057f407c101bcc4285cbb48d44622ed80e6950f2b88362068f4686df927839d53daa8f7b1325aa4c5e19569f130cbdc372eaa6648a1528b78124d292c85e17771cc2c8c81dacfabb86ae5e69f81583fb482a23f7b7d205ccaa243fc0699df099bc17d4cd68ad86e339276f060c8b41be1884804d349561ac5f76dd20d2a9bf1de6bb987df3dbf27602969f00aedc1b859bbf73bafef0e8a71f30239775290cdc50c7f8eeb44bb1d0ea95256b6bc2253a89005dd3f18c30dc5f0c0653d1b5f338adf83d3474392c390e6dbf386f4eb90d5335bbb208418393adf9c6a653d1d62a4041526a0e0398b304ab9b3a33e28eb77130582348fa7a902dabd9d82a05fbb95a6161f552b9f8ae08faf159316344691818729cde7aa5c48d2177d28a0ec756c12e33f77bd2a4ad09f10230a4957b4049292b1fa07425af440b5158eea239d83aab14e26774bf5390f093607f46a75720ccbbb3d2f740bd1f34b3550a2c346956b6aa4e27eeab0b1c8f0b9d1a0b9322c4a0880f5bf606465af65aeb435a35d9a3c4563c11bf3fd684e773e7cbf8ede342f1430449ab2ad1700be0cb43f58199351a7d67174fc72338c0149873ac4c4a3aa979c48e64914c729cc1455ef2ea64313dbb1edd4b7f22c15c74a75222b0389ae68e439c8c03eb0fe87bbc5774f642c5bc22a9a5f34e0ad702cc05f8c93ebb6cb263b3dc7ed4ac750d6ee31257a409c082951283a21611e13a906e44c1d6945bf48a925d1862d5486192e5f848174beb60cb3002a531b2c70cc4b43494614ed307b726f8322876c0b47089a0c4e69a8084d0c6a405e69bbec501a663744ef70d79c47ef98ac0354fd5c3aa26fc7645c7a1724936b3c2465c8430a77fa60c39ea3c006e7cc7e8f529c05db57be86597f66281bf9894c34369d3d8a3d31e75dc98937868f10aaecc3fb1200545873fc4756663ea6d91079963f7bcfb2112cde4ac13130e88b192cb7c24cd52de49549a11cb8f61a46f6cd463394e60250bbf12a92de13277a944c8a7d9d30cb41a86ad6e2f3493fcce771ca4035ccea9c51e8db9cb578f1246cc7d0106c1465d01ab3187e2ae2b901d0908c4f5a9220ba709e3039a9f8f2167b8b512bd429f0b217ec536cea26317949ec1f4df6b4d28227900b45f4d308637671aaa206280ebdd66a9e760761dc185df31022526ec3c31df6c3e564379d65ae824343c50404722d76f73cb0f544ca85aee12545ab4471f074df58bebab687f2b103ae1d602ee54150d7616aa6e3730c634da4ab771f72e9026d6c9324095d906697b195f5d7c9efe0f34f86ffa7a13ad108d5c9504d6b42799a4793c30c5ef2ad6fa5fc80580dce4c775f6521552b01954932b3833703b0f396a758c77ef90040a62abadc751d4bddd90a7e3d8bd49072cc1650436f9881fce405d4a314e83bc38191981a2cca1f91c627341b6b19025d831d9ed71c36995c9dd0548b0a34ee4fb33060623afc08ea30a00349748222849002e8f1f22514f4d13021f106bf7d4a8e0e404f508100d8dd17867e667f4d32ba5fffd719b8146acc83898600468fddd052fc84f8a71a7ea51486ff99bdd96075b86508ed53ba741249d19bc0a5870f1ef09191fc5620f1c3b64a7e6ce13162561000f105b5e307288cb8d4db34fd47e07bb331ef260da8af14d51a895122891af145649cf1dda55b1b73bdcdc6e990015a6b2415013be9132a02c159a8230c4ccdf5ac31b8d420f15817c17b4f28984a72cab308c03db405348c59dbe924923186d9e4ed4c9470577d879607cf8770a7d2f161444a00acf0f188672ef670b01c4129d70897c0ebdf74eb54fd735c2d13157eb9e8d35e52141613fcbce95df44519f11f8c6701967185156072f72ddfb1a05d9ae1eb34ae926ce0db58afd88eac2522907ae4231bb8f00cd309a013c64f423fcf189d9b323f1169372d3edf97e59fe5f621de5452cc183f5c633b605db9227482634f1558258c2ba149634ee756060c729c172c8cf1d11a01c30a62961b976bc61050874a010bf887322f8e41275da43c6ce7aac0aef27e447106e65426e37de5ee8bf4bfd77ae4fc68f54ecd14b6680f8056611e29d8060ebce0bff989069c8128ab3af50c2ce522941da09b80834317c937a6e3e43517cf525ea4cdf975b4902f3390f244d04fa0aab975a30145ea503898cd4157575661ff874046611eebba07dc17a584c619312e8f24f8fedb2c74ba844ff50c24c36770f919500000603c0c72a01da49b62670632ced47531668811208c8bb06e028025a9384ec2dc996524a29934c01f7087d095609b32a3d76c43e1f7b6fa0b7c0ef18df77520e568d5ed5a4687adc77135de68c87e79c73b24738233f671e86571520ac180185e5439212248a84746d893280f2cffb493b41aac11ab5cd7bee64502426b10089d79e5b4d92f017eead8b6057a55e411c86fac8a88b88ece670f8e7d77bee7098d775c518bbbb9b5ef83007972cb1bb359a60b71adf7ee3637856b6d50d676a37395a0d343fafed529b89c106ebbc6768dbb88e46ec42ca0bbb4187f0badb7555a74aba56f7d565d12fac731cba9d7194d22ccbb2ab3a3950e6d24f994bcfaec45ee8eb49cbead057ed8aaee351ade116d3b986d29bfac2ba3333bc1ccec7fd5eadd57df8eb55610e541d3a86cd4bdb84fec234ece97453a012f50755e2e4a10658a7af2614e972f2d7158aa445e55417f5913e825dd4477d94a532b6e569d25b83fd6a2c7afdb27608e723228d7278f188d743cea8570da1430db61067e46342edb9e377ea7e39852af9c14a513e6fed39ec7739a957ba2a1901bd9bd8e9fc5ee34de7f4193a27bd349ecb71cd9c0114242d28465efa55c417d267c47898d2ecbaac858e41cd426a7bf52e7a39d62b79812ea0c9e392d74ef7d9f1532a00e9a6294925c5aa747d37a0ab85831e7060f44de70a8ee353d67939d45fe700e91f630cd66127f4542a59c943ae4b21743880b87f3e97cfabe2e168bf29e331fb4d990f4e9a1e1fbe734f2f642cf6c1312a115e5cb78a30f2dd2d22bc7c67007befbdf7f85df8584796d66021b1acb7797c37faee7dd2eba7843a81dae79066b1c03233d7105d2ae566caa584bce0871f6a2a8ddc184b89dd688adf41c8d687fcc0e2b7eb2d60b36b1181e4b9458491d60dac706911116542383d4ef9450cd792c75b6362ba44b09d2164478f1f2f03c1431fa6e3d15a6bbdcfaaea5cb11add550d23e20822947c7d18cb539df3d06fbe8d5802074ede653c2eeb61f9e0532fa535ba4ca9a49b1c624fbdcafb139f5e7702eb2ed62bca9c34c4932f1e014f95fc16b71facc45f855c4e64cb6e37433adcae260f2912bcb0594da336c37f5d95b26eb0d406a6e35d5abb09b6bbcc3d75da194fbdb45716c3c13e9f52a39a499b81ac1bece65eb3faa2cb0ed7420460d6027f741adda5e33ec5d77d9a3668f21b7d6803a487b0061896bacf6cd7a7e77e32d52836c3fa525fbfe76f1b72daf69edba2bbe74585cddecfde10f59e1d52d5e517f5e8f446e9432ff4d7d4a04f6de8b187b757cf0897932b08aa440c7a5dd7e590fdf9d0476dc35ee8e7065d470ffdd47e60da1b92cef5511fa98cb5465dc3e6acc7fdc9abb6a5be6eeea4f9f09f30e74c02109e1c566f8fa7abdac17f721ffed355991ce8e4d24d27977eba43afa35dd6534d589bb4a1bfb41fa9a76d87aaeb2f4a481c6c78b12d4a494a6df42e9fab4b9bf16e36a58429615aac5ca9d2ab37630cdb8e69a149615ac6b0ed1b7d8a592509840a1dbc2ca42af56a56a55ecd76adad9004e14bfae76f6a7783f07aad52db1c7ccffbb92633a2d0c46957c7db861e12d98662142cd7bce8e40ed529a4935e4b2f469d60a25b22bdb05761d8ed11d147bd349173469d63ba62920893c024b007eaa88ee76a1775e1b2c4a5cbd297265c969cd0843ac1b644242a8c5fa270f11205d342ab605cde39299d036b547a47afa84b1375d1237a444dd44498962e7a44f611d147f4cf44bdd6fb4cd41d9777535c302d483f54ae714547ef6e0d2faceb7a67ac05c3e4b04bdd0c28d0e774734bfc4ee39ff1040b243dfee6964cbed439d14593912e3269f479795ede176a7211be745b9c92e3e2943aa73ae4d239b3dd0c3090943ce78fd64ab11962d8e94a339e601d0fec37f79b9352b7bcf31c576175da6a52e4943ae76d4ea9971e5113a2cff9c7cf2d265bb87ba9627133dc853e622c724acfe6f512bddc64baee06af7b69cb1bb58ac5cd80174222ec686808d72bb5cbac5be2d010ae576a175b6d557245d615b511fbd0c238778e9d3198751818eb10ab582a968aa562a921e95d0a9e5b4e9c40e78d63bd31ee9cad6e9dad6e9dad6e9dad6e6334a04d3a75225fb5cdda59312ed45e26192f9fa7ead5c6c4d8ea313176c396ac85f16aad75547541ef635c47db18944555192fb7d0ce18eb28945d81c2d81973b7d45bb74ea1751868aba3a0c9a79f34db2bacbaeca7bee96ed22f553b8d4828477f387aefbd57b10c0de17a25ab57aa922bb2ae28dbe0cbd3093b393f1bde1856b1542c15cb51d2105ffe00cfad21a67c57dfa6c31951d15174831f7b384c8b5f99b6d91f1a82ca50a80c5bca1cd3804cce59965d8e658e05bd47399107726e42619e619751f4ccbf3354b6c2cc4e1996a132cf9c76e6a7ec5ecf2ba6c5cdb82ebddb94373ea4a9e5cde7f3ca9c13daa1215cafcc39e774589526ca51d651be65339bd89c73ca786731307ef218cd5e94cc29c326160c52283cf9de7bb41f103f0f3d85f0baaeeb3964d375410883de76f92585a8f20a786e0981e4a487cb8991105baeabc617f6429d321fa036a15e8682586318867577a350a84679ff48bda933acdebb0d615a3078fde0c94f6ccbd5187d9709dbb2657e22b2afa73ba2e997a3ee66ef96ea9ed6678c5f77b397c7d8bf629cba8cc66d735d1ea3c168b2a7464f1976b7367a5a0d2f6cbd3f523f7dfb917aa6ab8d1ca605d382697972eaa94443de9edb7e8c3aa714887f832fe49b7b1e04d22fa08b10474170f9fa321a849537c0732b882719cf66837627395d0d09c3eee68a1e528c37a56eb1b7308e5dcc63346e1b98bb9deeac7eba9b2bba346c1b02aad7e445d815fd5d20e7d365c5022192ed5902007427f5915236638c40588d5176ec3e2263ac11f31afdc8f6c9e78c7da48f3a27cef8a4b776720680d78acd69341bd3802ca3e6c4bc8d1c8e76ac91c0789c306e8f381cd323aa1bb3307df4115a6b61dcc278b4f1aa6cdf6dc7b3c18a9189de4611c3fa6866d353f019e735f3d99c69472ae6ce5a3be92b8c8581b12e3acd7cda4f2e3587d2eaa98f6018969218bf53c13de5a9674385cb7a36d037c7cf46eaf2b381329c4db9cbb33ee2666457e5d7d426b6633a8c7da48f1ca9b58ffa88924ccf1401131d12ce089a9aceeeeea90165b57b4ed995a3518dcee8a50b75edda0d5fcef639dbc18f77fb8135ea94f9a03ce36e6e7759d450ce305e6bcdb22c4b126d8cb5baa39ad90b546366a3118c7337fb8161edf1ea083818de77a73c8bf18cd3b097e855920f76734fbe8f784283599c037abb067012cc4cfc14d54f01fe39d073033cdf9c096a7e23c1b3b4703f06f8779940da6d80b75183592c166b26c8e9d9237476091d8ef7fcf4f1b946dbf8b0ba5827b7b0343737ac1bea5c8d5512b6d53b521f168f398599d79252f4efb925a5c8db991458c7cb9392e4b5a434814a9e5b528e78ee95eac73d91863b54eee7ee2beabd0a1fab098e19a705fa76a894759e7befdab987f5db01067d36763c9b764aad4da5dc5755d816289714541c503e3b0b0e07ec611d77736300033c5319947d3ae757238467c72ec6c1020b0e07bb661cc6a1c3cdd831b5c082c331019ed1befa2e5bae44f1fdeebac0384eb03bac03ef89711c8e773137a37d5e15d68771da29d63737353534345a0ce1db7d5887bf6511165487a2ac43db7da87ee283ce419fc82fa8b1a1a5716aae4eece686860683fe9c5e48efabd141372534411ecedd499f3b87b20e75de3c543f973b771768fae518d59e3feaef6ecf29ed95733c2ea7ee8371d683e5f3f971a946bf71dfff34f7f5619075e24daffa86d539daa6bd8a94d412df4f28f9e8ee5dd6e33eb2be57af8a754fa1e4111dde4df5231dbad4d8b9a4d3475e967cbf06427ececf356e6e6868ea66bfb8d1e585b6690c9b99a1a1a9a9a9f135301616964974e7309ca3681513bd7d2605c67141df56786865f45c30de16daa65d473317166699f929e9673242e8da6736cc32c97ca3364ebe6118b551678e5e69346df35ceac5e9be031d6fb0c2b858ca73152b4f87b6a385b6791eb517a0b6810ef4a672ce9f03eb951776a3dfeea2770f98f168973d3eedf002fdd0cf9e2668c52720eab9e58416614427b2fcf5dc72620a2d4a4a92a770a249133e605da184e9597164892278b0420826ba5042b970e208ea44d26359d16ac2054d20f942590aed2281309e8041161cc0f0c1922450b185143468000695294e3041110db660c208efb9c5849518b4a21cd11ac2132c2ae48c503071838c091f34db18c8c4345154831622e0400834a05a30b102c8c40e93091c30cb50c0bc3c9692058c9767194bc60241d21721c2e0a18b264b6e8828515e5e2f5b515a2fb31d29254b0a26a06022c9f4046b8a2a0f4b15568bab38426b0a15620821094528972c5a439082c310969001b30e2fe1e555786e05e1e82baa3e8cd584554d1ffa7937f73ca6d34aa97f80fae5ecdce5aa9ea63efc575ff37e603a7598f14c37aff5898cc38c23bda544510b085e9e7f7a049ab7330c639cb80d4d37c673e76c446af88c47134dca38f2aa644fbfea753467f1f6bcdf66524397933160b1584eb0b2b8801090048106ac160f598ea0c28b227470240c2c84a0e4634b08358061456929a1e4b1e7961249b8c896deb225ab89272c3ba462a1b3c46620847eb10e75e819eba4ea9402f876202e806f17c0b74300487acbabe2f1806f7fc0b7f318ef3360b186d044600de10ac38ede5f5028b00dff869a40125dd8861f5218042be339e3e18177cef0b595f18e88255c77bb873977d9152c203ca2a4f7d8a912a42b082d25d439eccf3d7eddcc19df1f33b08e0b8ce3fcddb8c162ad56eea99465e560553fd706eff09667a5c884aeac1658d84aea81d5c446e7dfbc267ef419176b780121843df08a73269024035ebd26fad2e5b9f5832b9fc473eb0749da9dfbc23aeecbb3fb892eb08e73e1a56a562cc8ee188cdace353f28f27223e3c1584158fecd39b10eddf9cc53411dbdb2c2495b2055543b2b08eb9eb5c46e944b2f412fbdfac221b92a3a58d60c2cf59ec238ce5930b05b2f29bd235272de4abda5b9b81ace88719ceb68d60bec466974b7fa25586c05d6c0318ced9c3879f7e4194a94efdd19158cdf77834fc34a62b7d5dfb0a08ecee91cd593d25b7af7c41b83956725bee2e21534b29c58e933c61863355d945e946237b0584258e8b1abbbc93337b594f2bdf7e6c44cfe4cf5eacc744d3925c558b0086531b1b19d95643b27730ce5186ae8b38c526a4fa813e653ab0e9b7113add8c649d7a374c6388d312f866504ebd4f105dddd43356abc722f1900a1c3f75846ac7d6e2519fdebe78818c73e265c91c262629d6584657fd199c5d7a5c418a31c621db3dd3dfb3a0c73acdb08e6d1e4f2849a8e9a7eba54e8313799b40d6658a60d3d56a5b774a963c6da3108483841220927cfad247a7898440e53648c43a6f3b371f4d918e276e8a4fd3c7af4785946ecf386168acafdde7befb9ee14cb06421d85fa08f6092490683b6357bde2b8810ec8d0edeaeb7ec1b133b9ac2fbd4cd774e1e63429bf61d570edd15be81c55eb884b5d743065073bfd390b0893c9e43b5ae89c1adda4b1cae4d4647293c9ab5f15c61c2689ed60e705027fb33645b3ba91fc91956aa870e907db5e6b8e1bd7f5c21249508e383969d2031286e4c848513644c88f193ab081176290c242b2db0d5575b657f0aa60df9ff8427af5ee7b3046879452eef2ed593bd650a65c268c9a62a41efd01c518238dafbaf0ea2643ea3355bc2e44aacd44d3b52a558c4f48af5e0cecc653a2bbf06cdc804078e255457a553106178a58940bd0dbb15c60a748b690cea47c8a6d52a12f21e1e49d14cfad1d5491e145872f6a2cf4ca29714dac3b2377044324242446031b98261676fc48fd7348e89081dd5827d8ab273dc27e8fafc0588d87d5abf6877a44bc9550bff3cede953a9e8d0c29d5bb271b9c694b2e435dab1a2a7ae90c878c5ef670af9243462fe361f5609feb00c2339d075ed5bba798f16051857220f64b88bfbc74ef04871e3e23f4b2d02b365dde7787e9ba2cb078b01ba5efa99eeaf9bcf227fe73eed142afd8553f38ec2f09068a7ab9a5eb727e39a1bedbe53fdc74d7c5d5b9eb72ce5dbe5155cd9c5d7557df4d06f64bc5f727bef38bdd694234560cec7683fd07f6f38673376e08e9d5e599b6b197bfbc52beaa5aab576c8a941a4cb05b8de8ce8abbb2c571715d3aa73ae611bb5b1b7db6a35731fa8dac3e0cc31c5e8e616e673aa7cece4e284ae3301efda6734e6e3d7a7487d26ee4c83416b41d5a0b33bd8a34ac2236bb3cd3aa9fb2edc64716b0a9e3ba91a373a8c74b734b58120b88d66870cfeac10afd46dd9f8d5751dfe0537704945e459fd185f524f72449896ba223ca3c9d1cf6c9273d9d4ed529c738dde4d167dc344d1505331ecc5a6baf0ac681ac4f87b13eddae60dce4432ff4274b515ae69c9d86fe9455a7af5e93269d26cd79a7c8287640a484a250633db1d219ca4de8e5ddd87fa4dea9a47314529bf62aca88823835d5ece9f1a553d5bc3f5e1e3e4b7937197e93fe23d5ce3ee34b17d2ab087df0fa51a4c7d2c13e67cdc05225979399849d9192f4d68aba28c360eac9e77ca1fce2d831330f8c3117be868e5f3ca97efaa3c3df489084a2014d3282e439c9062e493620f14a509e5b59ac6471f2b136811f0b929d1a75a83133c768f267baefe6d97dafa4ab22fb5312a5e6064dcfd088511f9347f50df35eb5a5339b4379b949d6ae41ee65673eb00ba566aa9555303ae6f1084a1ef32d424d08fecb7a609a0a8b4097531fd7954ea7bc3b1e3afecd23e4cc87cd9584ca5db9a61ae1c973ab0a295520cd2b50ae20a1516294da48a0a76dbe7f561f3bfaeae35df9a0d249275d318e36bd4ee72a4a7df5d3e15dad9a4c9728e734944ca7d1c43ad6b91cbac92f2d08f5d7ed21dd69419c5be7a58a5705d1d2f3d32b465e00cf2d236c7121f7986f73464de526c5347855c7a24f6cce395d4e6cd23ae1bb5e10f83eafca5d7737eaaa9fd54fa7dadb543fabffc2026decf4fecce77cb35ad150628350fca41394a624d5e78e679cb3ab9c5707dd95d593a33e087cea42f063ca4493aaaec7f9dc867eea00619cecaa82dc6fb087e906b9aa78f9457d3349870c21ecb9facb4df0a23ade948e7db6a008448faa379dc32ae8cf55eead6ee6049aee683b5fa04dd270553fab0be4bcefcd3b41f5e17f4b7fa504811f848a0e7d73734eaa92ae921e574ffd5a3dbdcef99cda5df5ca65cf0a12aff3dcb24264842a4648fa9b1b20ce99719e6fcfa3bfb85af58a37e9d16b843484b0eed9fd455fbd2aded5c31b2318e53369e5ac553cd89dfbf0952677f70d3dcc7c44e71b24f3d1ee6e90c7f4f2980e7d5e2009f4ba8b607e4de9346060653ba5cc99ab4cd59dc66ebab0497fa473865593cfcd6d2c542c7d75da23e4de390d18d8e61ed5a50a029f3d08f57c1d73a76d41ae07bd41dccb7c40e7db9aeaf2e9f05dd5757f567fe9bde99563b6ddaaec80589a820aa26f7f416ec9f973ae21c63aad138b487ae840cfe10b42bdbc9bd3dcf773d841dcf7a5d1391bd497c5932c90b6e84078d8a7a1fdf27979cf2dba90bbfcf975812e22cfda76f9d6d267952b1fdd1dfd733805118a3e9f4b1504bef320d4bb5b1d663ea4cf1bc4fd75b7e7d0df0de29eeff63c62a8a7c19f8f772f8f19cff30883b820eedf4d7f2ee45e02b50bb9a0200787a85c037d2a2d814ea5587aeab252ec3dd63fd6d3df582bd028c20639960d7e39febd25febd57e5e6af8c47469eee033af5b8412c36bc4e331e68f3ee0249a7d0695010f7f36e1dc47d74a07885dcc30b63132a774e90a31184dda09362cab7c320eedfa5d7020e2d952e4c134bbc02359f5b518441e54b154b10438927b66802094a0883ca0fa83091429778439c337ec9c10536c021090facc0c1135cf06060a292826bc9f342a5064ac5045da02065db04a52d680bd5c47442521338a844bc5613538b887782a2b2c064744d305a44b155821963442c81a371248a214c3844d124d2c0013bc1952850294252d1817ca1091cb8d0011268608316ec48da84975498bca43284b4cfad28aeb4a62cb5a614d140419c1206508890a0a50ad1168e424b06b2b5e4d1286a45b1c3c7e756144c6441c3043472c8a1832e96ae2822092642d02f2ec04b0401081dd8c0881e6831861731d0b881c60d3528c649614e8f1418dab1c0d08ea351c43e2ff21d080309f86530b0d3ab0a7243e20ebd444c3b942774aabe3d45430441e01dd7f882ccce906b872f730ef668dae19dd4438f9abf38e30c2a0d2226ecef3d9fee8eceaeef074c8e093bc347a4e711f1961de9ecd2731431edf02532ed48cf410222a61d79894c3bd2397fcff9134208111111116b4ca6576d73346ab040ecfcc3eeef3c887321444497c9f49d197dca77de5caa739f26226afb42f6c7608d6183d067f913f4c69037e88d21ed3b0fd24444cfcf43e6e2fbc341197368e29a3caa56ce7cd8317c3cecad6d447ae0079e3b9fc1f4ef7ec0f47c5d03fd30bdbb1f306dd7733ce83b7f4054764b8e9052c5eee3887ee8ebf640ececaeaaef0fd3b7cadd1eface8304cd77ee889e61673c64f8fe30b94be7a652a9e67ec997eccb677ef94c9794f2f0dd86743411a23cb7887004118c3ef5a2e678c2bca41947188cd22e16e88a99c38cc7e5993fc7dc72848117a38f067a79aba9dcf47ad280e80de2de3d7e0e9e9c71a0a3247dea0f4518cf2d289ef0530b9240cee596f9561de87220ec98cbab6a4c0bc2dcf954fae95572795784375755b657f06a5a080610e47a5809f88bf5cc381bb788f0c54fa1003cbd29e9d5b1ce36fd4d7ff33ac6a1ce7aa96dfdf4b974fa5c4a8df5d24d6fabd5a77651c66116bbc92f4dd5f3a8781bcf198f947703f24bb7eb37f609441427cfe911ef8afc83c2cabb224f5ddbdcbb9e04fcf377753493a21b9e3bf282c4911a76a8b6cddfd8a94f0d883ab5e1a9f497fdb822ff1cf607b9ff7135fcbb5b739fe665e9eeb7fe5a44e586d2b7985edddcd4d4d0cc0839e860d63dd7670b0b2f13797fb44dcbd0850b928c32c545d85f5fc7148dd7ac6454331a1315bc35979201801434198d4991efa870abec1818548655135d4105df6eccd38533291aaf59ddb06adcc8d1368f8719277aabe0af7d827d1ea3cd70afec45655a8df67c058d9f8d0a5a149b8585ae1db69417004d6d601d19bfd9678c5a4abf58e72789b9195158daabd7a39f16e81e7410fa36a7f4aa610a6a356ee4c01e17167a8d5e65f7a1d7681b3fdaa61dc3666668686a6a64807e4306be5b06ef8f5ef5095e26468670ce2e39c71041ecbb1b34c78802d1439d20f4211c220e01dd183b43d894af66601cbaf22fac67d34ea9b53fd88b6f0fe219edac87648ad1b7912f98bb33b40ccfa1b3c0f800e3f48c2e2c16847e52c3ded01aac668cc3b719cf06781e80a35006dba02896e11ce9ec881eea845df4ce9add953b8d8933d239d0953ce9a10912236df37c73475fe3630d66da303b933dd394f578377b6d8e9528cfe669f6ab56bd5ea0be2fc41077f4ea59495123179143e2a2bce749513ef5ac1267a457cf0888907d14ccd89c0effa0609958dfd899807314ad72b0ac733abdd381715cff5c05ffdc1d391cedcf3757f40e05ff1cb6bdee4bdbbc775e94609ff7601c41a80fd609926ac177740e7f773fc1ba90ef758c0fc618639c8cd353d2e0a02bc1c310a45e8871dae7f7641d8e3570d4394727947452eadbfcb7848594524a21f5e997976a37ab24ac20dfbc4789c074ca39676668686a6a6e6ea4bb7f34328e203448ea7b76774369e49dc683bd101111fd26641b4264c3bb8cc78b02d16f448a107dd010d1d7f0cf9971a6f4ab8a753a1e0d43440f31ca3ad70f95e0215116811bcc08d525f10009e672f876ac7344e0a6d01aaf734c36cc67671bd13714f44dc6db6a16c036ed323c1bf1062af062082fb2f000073e1801c798f30a82d238da01084964f9a2872876e0620a2eaaf8186394589ed8e006258200e34a0b7a8a844cc3c9154bbae841912ca25425f10757781104175d40e1810b1804262439bd2e7c403929e495514a29a594f1524a29a7904ba28c31c618638c312a200bd2143824f9a1c8869dc8828f31c698821d24490104125e7085879d88e4638c53a6d0c373920221972065289670905c04d14a381c283881d28322508650836b055c3cf125f052c1125d3229a46ce7a4434829a59452be270cfb09c94209484a40c5073fa002b96042146c490d4be9a46059e2030a7ec842c90ae00d98500506886631b208c1845296294d205d2958020c66249c730e197971421282580297221cd999473fc5f8e9347342e841380714a1d583122c960811c68502a47e02051d4ec0e58b134a5ec4600212d8bb8e70a1c5bc7410860e3ca082094ce8200ce8840b889631c618638c3e43a90f766084092b56e0c4071dba8831b9307201c19d20a907151479d1654b13700829158878197181022aa7fbb1431849b088228b0c50709d400946d95bc20d4eeecac1cb5ce2be18239b37ce39e79c73ce39e79c473fe79c7342cb54889448a2c484872c4a98400948af077372204a148460e2e4053960e22ad2a15b4ae0614ed8c48b49e2c3020b0f5ebca088154780c14ebc70f8b804167cec12c65b82004abe780214528c5064041cc2124e10598a2594402e60060fd832258b1bccc0052f58c27503174ee0027c983405c5ca1caad52df591239471da82be75a478e619fd6a6413aa8addb9ea67e69f6b36bb0d4980dff10d3f86c282ca3595461276e3281bab039db341cc7994e8402664557951ad07cf803e351f8c03634dafa0af9e0d179e0df4db02e3d058c2f2cbf00352cc5757097849f6435261787ef9e5a8633914db11515f0482dde89692f9e83431d6f42afa665987756aa2437a3d9895d150efe31e225919cfbd86728ff6545025ccab780626d52bea97cc5fcf1c4ff79d648c33bd8ad137cbce631ed32c8ca98961d42c9c60a1e4ab6b86723a3b16dfe560f603e36de87a50b9878ce00844434c3ee4f9f421d28710c9b1139d08cdd10e11ae9174131e15f088e8789050d8a95e2766d286f0959daa0d79c04ef57a61266dc80076aa571366d206b0c5171467d88989f3a90d79410721ec0cb99c04d48710692e76a617350c75c7dd21433bedb56a2a0af04e11deb2e35ce5429177b4e39c6627008ee6d288ded1ce743aef90a11de79cbd802262c73991e87a4b8d2ef32a1e5dd965bed9a32683dcfbb8375d6ed2a46f431118faeb3abe618fd765bee107e98534c8d1e03e48c697e969a8a9e28d13f6933cde1495552f38972e03838bcf190c923ef3b3611c1a4ad84dc6a7bee10ade9a5ea99ea75ec6cb609d777b64a45e06ebf0f7fba1665ebd66dac92bd38862b9a58595dfb0afce2d2e5f3cff49e64f5ab55a1376c62f9fd647e6ed8549c5d0ab6c870a523873a15799cfaaba8cb6811a60d818156e0c315703bdaa6eb5d3af9e8d15bcba651debd5bd7360bc7a4de7a4bc3aab7332af9eaa0ea339a25e555f417345cfa6b258abd5e68fa1300c25c3b7f900c06438009c8fac3603e354a751c55287bd3a798d984f0a2e5d8606c4d53c0034200ed3b69a4f41db84de8cb699dc871fcb7a367f94fb43b98ce642af4e1ea36d5ce54f3231f48a3a4fb1306e7b95b973594ff7c052d8ab8cf62af31bbd3addcc73f48a3a74186d73184d3de2e9b31fd8cd52f8a45893f3603374cec9ab0b750e0a8579a6c9e7ec745fe623d4abea427a554fd7097bf9c64b5f6778367408c9d86b0cbda28e754e4dbd406e0aaaaf2795bb95051251509723a17ba70571969fcd0d61b76b08e1eb796daa5791610a2f7da3691bf6d93c775a0dc6790c609b4763807f29fcc672168b06115838d06763731f6160ec13f6e49e550135a71185c5b1390c32764c37ba78a55348af9b1830f990a19d5a4d9a6a06d50cefe8dd6592790076687602b0c38f6647003b4930d2c2cbd776335376aabf8c02bc63030282d8a9f706ccdd8e491bc04ea60da94e84b7ec989c3318b01dcc5f10da1942c30e36d9198854c14063a7dee77c664766c73de71cfcaa0db99c8869c7a40d994ec4b4f3aefb1887deee213f82d113ddb1ece9a54e9999d5c538cf694c616f40937b93f35fd5b9faf55ed5dc158b715efbabd1abe7a9f784cb7c4a9528525239dec60dbd3618e8367a667a05fdd910f284b5da3c25c32d097936dc176d03ddb5d8063a84f56e2bd3dddc684b1979f885bd3c4cc9b0e2a1c7223cf4e79e3c945ead2015fbac67416f2847c022acfbe1616ae9d950eaa2ce39ea248d44a10962ad84a708cbfacd9a2e3aa58f150d7353180742b7ec60b85734bd7aae65f91d5227998830c73a0c59a7bd572fbe08b5d47bbed9c785cb96e756165e7eb35948f98a8a1e35550f3b7bbbccbbec879df966b0f92de3c019281646d50426a0c592cc671f359987466a7b6b33af0afa19db2bd3d7e740a4f3043920337bee32d7effe5d3d09b0771b7adb3e9b7f769971aa8ac70a26375dc7a3e3c168db73eb98565b53fd54a0ccabff5caffdb2ce998f2bbbfc00ef9867aea39fc60f0b2448e8f240975787994f021e663eaaf7dd68e849408fcb6ba31c080f75eb5705621d26e3c96c90731608ca79a8c7ec07029fddcd61ed7e520c529b796730bba8be8044a0ffe49cfd3831fe721807c2d39ed5ed6514460302e33ced10801d258c590f083c0c6d1f0e8ba1251fe4b23c7548b354e0e9c5fc3db7aa1f7f529068cc2701ef18077bfe7c0ef0df627ce5a90186bddce458f5761e331fd79aab48b8f2dc4282932d4dbe5e58f304654e0366f2b7b55f5e5dc7ebcd8388fce5970bb9affe7afc2f9e93b3d363413d416e8c9ef75f1fa4347e538c03bd351518073a8d2058fed54de7dce815643d8b71e04625e3b09d396d519fcb7cad3e323c93ba1cba9bf9777d60a94fec3e1d8eac057196da5562efb937e77b734ef80870da888cf1d8b58cc3506cf7b0db51af66418ed1071f2885bd6a1fd6cf40b11d539642b18cf490f20e2a77dd1283ecb9b525055b8c54c0ad2bbeb4ae4042c38854eb8a1bbe9f5b5718c9010e206ba992a587e72c46cf4b3cbbfce2a13b2d5bea0555f06a9cb13b6d3a6b3fee1d730fe7ec33eb31ef830f9d7b9cd139a772d2b932eb4c9fd37b6c43aa79f31c8fbc2a77552ae7b247e5a6bb79f69ba731cb7c387f310ad7e5eebadc4514d74c715538894e79cdcb65c6c349bf544e4aaf9c77f6f3e87549bd724e9d508f494b7c941f931e464881a2531ef142534f77bf944da4d3244d963c8f513e3a75129dfa7369c5112f7dca3837232313735d7509299df30090f5b8a48f77c3e85348ddc5c04887b1190faaaa4e9dbfde9efee8ae5fc78b58aa869563c70b1be89c1662e4a887269d034546ccb026d845531803518fec12cb7e5ccb0a2bff3c46cf7850ca6306a7d3a854ce548541eb14d100000008004314000028100a07c462b168402468b1ba3d14800b9ba84c724e1767518cc328648c31060042080102004400046668aa00c6255612461a7dc0acbe7c44027a4be74a4819804f658b63dc64f4376e5d1ddd68e405d132a732980a0493a6d2affc3f714254a5a4d0d34476d94b1ffbc7145345ea428f282b2e293d76f7530c545636e54f596ede60e6d45fd2cef45a8ff12a0f15a8e158996ee29c251a0986dffa313bad8a7cceffeb06d87ffde495ddaa5354b02a0d00d7058b228b6f81c90329c55945383111af13fadde1f81d7df60db2ff7c62e79292937560fde55c6ccc1a3be65d5992b24c5ca57c60590db3926222931e974fdec8dc065a59c66826ac7737bd7041161209c5eea261f547b9dbba66f4723741788a0103385bab344e3f10d1e58e35e7a7c835ac6b521527d54ad49466d3230109184b537e4a71fbad60270654dadef8a1b2f407288e40f7925deab1c172fd1efcf450d4218272d7cc436818c6d8c5fba384c058812cfde2dd2217719549df7b4395130c1edc64d1d3f4d1d99313cba15d4e600047924a8c742592ed7fa888319b69f3addee99b6157965036c94afd2977ab22aa949cb3b6831b319aafd8b734afdc0756da3da2728faf850f0f0d3c1d512c80200bb44d2ae0373db76f220158cca760e05c43495791c4d30e8d78739af1c614554652c4e25872888c0da5b37f5bcbdc30aabdb825403b7be3ad2de6a0c95fd50a93f2872c6e18b5602f51136ee95125265dc995f640f4275e1b199305e256d91b219019eed0be770633bad2dff19ae5b6c68086135a40614975a5445018ba34b175f7ff5fc15b1dd291c1c48a4b4a1f6f95534723a36cf54ede74bcf51276f462332fc5a2ac76dec45a350c67d21aa05deb0d95b592b367926537010f54e9407d2be803394cefc86685a19600557aedd201ea4d7d5c215c09fd2ed79739deb663b3246cb19a7561aa8eac2171f7dc7b02736ef127d43e89ef014f8c865dcc16489e55b7151ed7de5c878c29a1f2d781f407aa27b219ab243ffe4ba530b292e6d3c535023119574a16573b31df3eae897429d4aeabef95460d9d68c8fcef1e5e6a78899c26b497389c2e3d5a20d694efb073ab18ee7198a4410485a37cb2b46ae78dc48e57734f38f18d0be53a6c6a95b21ff236b093853809d55507d247090a4c5432a3d904445744ea391730f56d0ca18158243c2106f4ec0638adca0b528cba0b96ba82270cbd350bb96972225c2eaa381d3510c544a1f1e54a5fd77c38e460bfc05508b2c035223cb9493df8558e3a6841c968c075d44961780093c72f5e791964edcfebfba6ce6ca768b5850947688d966ecd8dfa96554d401f7a269c6763f4c2e1c774b4dfaf197d48a4bcf1bae6404b4924e76298e5e7e8aab5c3ae8945d9ed82392e4e7ea7f39d1593ea2733b99e32eb999a1e293a45072c7ae2f710aca922a0021f82a50ee577f94d0834698ebdfed13a9180fca2a8f8a618f52c7171d9722f5e33eacaef83303c087e675b066fe00aeba36785ea913e42a40c203e80bae6261aa58f6e7468ec36b126c0ed0f8907afa7f596f40db5b2aa1db5b83ba53a1084914e3ed2d1803d6747c6193495e2afcf4b439bfd0752d492bc821638b96f7cc529adbd4186da01511a2ec18d6a3fbd47ceb56fdeca7721eda59c805da5a617fe29ca682046da2999545ad961192f7ff6710214a5218a7ea7c6a61b4a09e0fd0c5f4fb8061f5d67ca91995820250101afff82703304991246b19488e650437c317fdc360c25d2fca4c6015dcacc827d8c2700146314f7a39b9617c9b68159f057fc61bb89fb601780fa0dbc283050068c48e2d1c89d6f0bc4d8b56c905f85e8480fccc651c300e9990d648997eca7fd6113913c57be224855845728ae9b0ad10e4eaae3af52e2c074f5e4d22043200e902d6900738363564797b5531497771a81e71725e7f1082bb2be0bcb9a461026d06862c8e0e26e32132fb570f08d0b49c04ef7760ae1297034837bdc9f6984e65983b1eba4ca61f6b4a2c656359b92d838de20d2a0cd1feffcba50157903bbcd3d967b3c0393100b21fcfb6288c2385146078724962be732d2a115d7491bc487fb76497c6fc4992b240bba2469cf0cee004a29db1504afb655024a1f25f8dda9e582d5ac16e17deb01f3239d4dd595482cc3e0bcaba37cb2438c2bf5f688fea76aa6a810a5c6819794c16d4501fc3cc6aa58915f3624919ea0ef63be1fb8baf1c8ac1aaa57d27efc1de80fbe5f90b5681aa84235590bc8e87db57d99ef358f528c56e213ef556d91de37edf1b05cfdb5411422f7f8d45751f8746d0bc23ebee6c5fd63d7d1c04749676bb68619c177d8420d8e07dc57b033ecd27df91b90bec47efc7d1c0cf8cf8430eeed5c71990a857b538199111eea2d4fbce7c99df447fd82cb3cc9b79c26fec4b1ac67c30a39fb3c0fa1e0d39cefda08c2bede4a6e91bd2fabd6190f90224f677c71d955f173d3ef7e0921757b20475ffae529c8ae18c395fc8d3aba92654fa00e7d3c23ecd012de398243cf4228d8b2b0e182377741d4b45ec71760e4d7bde61fe02525ac380e83ba07b6be54aa94fc76e466d19c425b9eddaf94a50d0ba185ad2a42822ef0c3ca84cd9e34b8396cee8928479b32380ade642b99b508049c79cc7e4e957a8e5013995700fcbe83bd99d2113790b427cbff1b002d253dad7c073ef6764efd1eb4a9e9a09bdb776928aaaee170dfa2e507ed083d011868b720739cccc52eedd91dc11ca80144f364a775fcba7ba966b9d047da2be7a788a38225ff190356f7aa16d8d1f24b1c8c3b74c89db4da0af1e8e34eb5819808fdc8579169ec420cfdb610c62dd8246d0c7be8336a7321b35e9df85e18c6bdfd302b2505a7fd9b003a235773cb50f73c716f88e32920bc7c1569cfa5c564dbfe9a2e6451a0ae7aacdf7b218150995602d20c84537d381c38be2a0783d1727ee57f1e6e6897e3bc2a717f1966961a826827eac675c80a218cc2ca412b08cdedcbfd6e55f86bf06d738a0c7d84c47e0d3af1e9a1fcb7f1cf2bedffd05df6d02a578af5959a5ebdb9f9b66ef218992ea3a66e6eda59cd62e2ae490714122ff15c440528923ff1f3768fa769e8d2cc963414bd7420091cd5324427f95e579d1208f52e8fb30e7a5b6e8a0c4161b9f157851e465670d933af57a1630b296656293bd40997edf7e55fca53044ac5c360977fe797aa110ff254af609d24dd62b98babc8932aff151820e384aa389485aa13a25828a1c21fe87cfdd1c1a58a838a0d3831e61bda606b232d668baebf66e3371080a19901f38642dbf18a5809fc8cd50406ea7eedb17832b99e5df0b92a370d0d3ee4b7a9f6e465e171fc632d34f3fac3c7bc5e034e64301471f8230f7dc8fed1d02739a12cb86991cc9d912a77ecf8dba50dbf90cd2d783ca06775da4cbf0b09160198580aefa677df4908a7ac084f5629945f219f596d0f9e9731001a9eb59ea65428d19b69e4a51d57991ba0e28831bdc6914d9066b0d2b2008c0ed26b52977369b77ccc3610a3888996c5559d415703b9ef3ba718cc920535f56ca336759dda5a911f3788cf702d6d0dfd6bc0ca283e51ecfb7e276cf1397823f6df4a4b3352c7e2060104a7efa28bbe2c1ed1ff6fa48b26b3d7743bec4860213d5812dfefab456226508c91d9a82599f39c693e897f8e5f1c03134b9717ce4f61d9b907657eb2011121b6fa2dc92ea404e47d623d7cc2bf0677a5c10799bae48757b41983799bb8321582d16dfd2741d0e4466e0d8b7d26fa9c8bdefb481405c84cb1bac3ecba886e78e5abb183db7f616adf3300086adf0cb3be1ca314c9e19e43f9557063111fe42cfad2e195457109cd0a380fd954824728445b8a108dcc5349ffa1bf6753698303ee90cf8dc735ee8bf6e6f0eaa9ae40c4a7da8b1b1031f2b2421bd934603749a705765a16878b4201d355ec9ff854af51fedb0f2b574b0f3b624c13adfbcd62bc43637b9de45ae49c2a460f32902d4005b476260dc7b66d8980e5c5279a9cc67cb174915f663fe1ebde50fc3ec47ee4de87a600a6637edaafc7a541db05716d7d9a11cfa8a09251b0309686785e4bd42887070ba13d6f907c324f1f3344f656cd8bed3bf57f16e9548acb6e05a7c5c7ad09d51295ab27d9dd983286466db9ccd0d0f3b0325329c4feebf85523ace3b30a8a21508a866656cba77d1ae32221ba703fcea524b978fd06054add51900b3d663a7606a0575f00743d3ffae14614f1593b9ec0273f43ca0448aaa60251f00b76abfc54268d77fe4cf72924bbe62d5285039c8e572a4b2b4b344e94064afaf179a64caca2eead42c57d711c742a33e00309c9f67cdfabe81e71d37b7782e2bfe89c064409d04567a447dc033e0e0726528cd9ca51aeb65ca9d80ac1d08d2ac0b8ad35eaaa8e2c14191682c9c950c1f1ef4e04e6a0ea5c8641f998f8da6f1babfbf9e3da6e90f9a548c86b038a7a8c3f3f6387d8d1a82baa3fe11c2c1f10c14d66e260f27640cd50448e109983d5f96817345670f097cb60f3721049a83841d6fe491750e6ea7d70f69642b3389b0f662eefbb41f83edca3926033eccb0c18a8376e3aaa13b13cd5979013e73e7f7ec4c2e977247d562656ce3c1d2c2aa4cd7d00c43486b2a27389a7c12908d2da672044cac4b73c610840bb2487dddc56d10446df0e8fd7b80f08ec798bd1d9a90daa9e1fafa396b24ffa47b7dc7418f3084c14eafda0bd9e4016be5a7966da536f97649aacc7c1e9273e019111a70da1637449522b6c4de9c4122a3251d397f36f12adce14deab02f484aa1668449316c9b4f681835ebc64381834d18515a449de3d39a199c965092b8293c30c38dfc36c33a2e193f8088e1f900f00ce797ceda90824c195241573d6e61704d384261de05c1a53144ee017d70f00315e31d774c08018009a9b4012cca0078c8a6bc0ca0f904a9ba6187bf52926b5adafd459ac9acf91b99e6500ce5b89f4f2a19f844ac8219523f196c0aaf3dda2fef62bdd6a89147676081507655c48398dcb476b5e43bd87116a627df0eec2e746b81507b7e5beae7e6888f3558dc55ebb5e217c2226edaf518d1be19d9b71bc90896a363a4285ca551524b649d07091e2b1a6d3cdb0c80db80519d3165bf7b10debb1307d3fc4e14213036c92d24b2b4bf0e3b9de5d388c811b2169d81943edbdea2a5d0b64d907e50d9c9aeda5ef52065b5a94c322589a3703a0c99b45811e5bb796a48c195e7dccadf401d18bb4c3d08f8058d0e891ae3ba06be86274ec62e9f4a9fdfaf0890b8f261efdb529a1116383501f1a64867c8fa28909c0730ab19b2c5f2a4dc55685d6cd9c17261baefe4200a58f2a2021c8a9101fcf024a57680570980b098c4b75e437cbb0c4abec3c804d0ed1004809bcca761cb684478dba8c69e0fa36f5fd703e7783531b61ba4b3ecc8315b752271e8bcd7dea93cb2c8d2c718d1646b4795012859b19a0507d81a57f9d43f77f3f24cf7944cba12a7eeb34f067373b39ed5b73b8cdfb6907d44bf64c1c0c022a243fba8313eff32d5e05c8c14d5c87233fb9bf079ef774849a129ca2d9e0a0bea94f206cfcd8ce5280187231d6a0bc3e4ff42a0e614f76e204e96d7a826c08536b68aa6555d7f24eb51a40bdc093536a97830e6be4cf765a52d8cbde874310de75789cb900974ddf8dd6f4b88f356d69b1787bbc5f6a9870383c2979ff1af8099a243d6942e38fbea5e86bbf03441ed7c57d5fab5bfd1819e46a1089b5d66214dc68f18e4c63301088bc1755deb593d5eb8cf0155c2b7f114455ec175874d5be0f7c2ea616b6e2f37babb5b17be2daf00518669fcb880550d74741b0d49d733c3d7ad5c3ba3f35a0ed4fb8db2b560b1b38d274c6cecfbae6115c913207d0e8ec121c54b81ab2e8fe78d7b275eedd6894dd1f9f7f9d86fa5c5d3ed5fdcadfdd3f509dbae16748a105ca239cab180424df742e8b1f9f3f4cd7486286d7c1dfef7a1fce814c9540e8ca7c4f0ab8a727f31b3f715fd5b80161f8624e264e33ecdad74c17665b1e10a28a3e3a40e1e443c61b5086a307949c7648996f3046d6174b305537b48d45626dd474606da3b65d2420fca99cc7f06af17e1bb58e11456f1e11ace0ffb9683ab4a79ece997a4d65436fd396ea141ad9124bc5a754aac90f90b346d75285691e908f85368a631f8cc800571372314d17ed71ded552e8b9d82b5c80766fb5656232aca07e29ef13d3e68d4a2f69a1858a9f88d650bf83288c8c916cb5f044b5ea1c18e11775e06d50a59d2df9f090412c92638197d0a84f036b7aec4dcb6c7d97b0475494666c78daa292d6f1396e46810c5fd296d02c5213602818efff2e5194cc8d933c03eed3517079073ad1039f872a892b712ce063f5270f2153fc734c47551988ac829e8a57a3330909d709e1166e772034e37b5f347ebef1b1fed867de0ad42a908af3dc6753abff8d1f025dd39f4341189a12668909b1d39872b0f662911954d07a4aa553655bc3953204718fdb07d54abc0c4491e78041a1e7355217066db5ca029766406ac24d0b516ee71925860da5cadc529df17901959944a35067752907432f0089c80da1498391b8a579cbb5990e885bf209a020c8ff2e38f86c71339dcf342a0e0f415ce5a9438495f33cb169a865d919e415cbb0851cf7562845b9ed553d013c12b7a19d36a85f51358da8c0b2cd86b2c0ccce3561a26453a4808877e4c520a5f684e2f41a13492ed5694f3c999ee1f1a1316470fb81103d990e3c439fabc3ddf5dfd996449b6a39734ac47363cad8f8239fba30831a0504493b5348e9e4b47feb9d0df312adc2b71dc3f32a54c1c98e97afe0bcf83a4f5a529524ca8bcfbb7bd13e12eef2faa3b493e0b2a8116652e68a2bd74ed03e824509dfd991ddd2adf3f7e8a4c471b256dd59b6e4f3a2c0f8ea410a3670d14730dabf6557bafd8689839c2417227822c0ac21ed2412149c0976c703014a04e958383806714b9f222d67baa3282d0002148dc6ee7a0a860252397ada61c098f345748877613a7d1be255ea03b6502b4d0b68a8a5cffe3ceb485fd251c37800eecf65ee298e4bf0a5f4e165d5a0949910fca60e37f582ebe28753afb85884c0a0e514a645dcf2e45f07407525f5102c9e2d49170a25f20b28a626369fcd8a106fb317f8927f797f36b5bb21e1ef0e8676ca93608b3bb240a117244f7f8c0e691a740c5d2b5f9672afb3167adb02efe2ac5c48014d4f9bac241d474694949c86dd2d223cd95f3fd3287c3ddb82d0f988ebb9c544c7437398221a014234add0ffca736612a120134d511981fad484afd69a6d6579c34c30c585de8cd6f86da32db820127a0cfb03f714d026e72dff1a1c806165eabca8378763afe4f7c9edb023a7cb890bf82d442f41c40be3534413d43a843b357f5a5609e93fd90cd15800762c5c6c44da83d180a5f361cee80f4b3dab723647fd2836d75d22b080503091e889287e63197ff3ae890c94186d824c8cbbb3ba12e52eedf230a8a45f74bee22c4ad7c0afa00099a6c007cba5cfb1be0ac0ec0680913dc016bac69624c8838c1ee45f9970b9fd64369f6f54e891bd46418ba0ac4a5fc3519c9dcba492edb01133990f18435af02717a00cacb35da33285be942df6810072d441be04ed40a13f1c96212884e50324015e80fe4f5d7252ecff0a1f17fcdc93e65c447bf997a30cb8b7b082ff17e66a7e106878f6106bcd83484db1d24f47f27fd0f557a29d9ef51125902bc5e97931821985a84b9c8e6b4a51abef13d96df888a649010635e7ff3b1bcceff4c0e806457ae008d1551737d0ed1b7d18a9810080b62366dbeecbbb207d8062fd68a60ac290378a2e38ee578df2fec56a41b3890e563ad941b918390cd340bef8570f90e47a82766b113b097d3f5d32e7ad92485886c5654f70fd6de3e164a740ea8acef9d2bdafcb8e74c19aae11ac5e788829090c45b08b879e9840381b362a54d1f19f73ad6ebcca6538c1155aba8915c96f6651b3a15c6437f241292ed2035946896c72d68d9bea45c1583b2f6af7ac244b0241254730f349fe32cf4df2b11b0b9feab8495febfad6c003737671ecc64fd4f701dbecc6b3b02d402bda6453995a43cd1559d9448d3fc19cc0bc8de12c9d931a31f82bcc480f5281ab8ffad0b7ee9b62b092d5177dce5fdafb27d40445ea79baa12d4e3adccf2d40ccfdf3860bdba866ff554c2b4afbe1755b755932ad3804dfeca885c92bdbb37bb176565dbeaacee4550ebe3f425e0af62c83b220ff5f87371065678372530f0ebdb627433e958b67da81b7fb12b770d75eb292dcf62a0bfe0260bf97bc2c2fdcf61d34172e0c9a673a6ec7641f0e359ca9123cf227b4bbb5ef975bd33e15f6bcbf528167417e0ac3b3ba0640562cd4f1e2cc0b34aac83c5807f487b2bc3310dbd9888f8aee2fbf98737291872fe3dbc086575ae42a1bc16c9647ef7fc9f9decdf53f53565e2599f9c9a375b239ebf467eaa73e9c7f652eb08cfa9c746c247f1c817c917a676495c04399094101fcf84c50c415cf2772a38cfc0d79c8ae89d822ae924acc4692cc0535e5cdf55b444c0a38e394fcc2d313c9983637c0071bb4793659f18394e39329f83df0d6755aeae844b899e8b04631030b84b4c980a7848165f4232335f08400d558f1ae032f718afa9334e4ad8310de388878f89e9eaaca8c71ce093887e06e24021ce27eb7343e327e5f232ee7747ff28b9bf5a85829517491ef2663201b5ca4f6d0393e5c33db19f099c7f95ddd008f86777178282ad7b29cdcf71efd8d5d0ae61715ed2e5efe40df2baf8918ee5127c062673d9baf82228f08241d6c87fd9f791b4cd565846e744f02cb64070898f0f5b1b7f09a8a36ff9eefd52b8290d9cfe375e422858a68944650784f663e0c60357f795190eaaf771beaf774144533b0d380eb3a089f39a38e7bf9692187602ac20f3f24b9de1eabdb3c1833b8a5ac53935c6d701b9e198261314a62739a421c671eabd3c6c2f85205cfd9dabd3faad926ba32d1ca2b0d462ada9ff40eedf4f5090405be35b70a0a6b7d05977206e20f0a4be7a953ffef688b922744aa6f94606e3651afd5ec2b9062220a6b4e54c97e2572383b7f1de0b178dc28e586c0ce60df73aa3926447daded09404189bc9b1e05fde9b2c63f1aa7f24d98dd74b9574ae49a3193e859ba2e764450cc774c470f1dee3c2bc514adc3d2f936e4ea748d2dc2d854a4ddbd14bd508dc04eeacb38cccf9c360854c193acef029163b13305f1a8bb43c5ca37a088a0192896ac0f157a54d66115794d2328e966065098ec2ff631b65ebc1959e7dd8b008968c6fa632f9c9dc05b40dd587867160da337b814b920454edd39cc214effcf3b664202542f9c81458c7adfba2ac6707da1ed449118f4adf34b6a6deba8c6007db77abaf966454dd2785e16002a3bbf27301f926156c60afab3747edafc1e8d4c7dc9355d95a503dc0e4e20018cf1bce2b9abd5cd25ea45b9365afa6dee217a74725b8486859f275740b8dc51b22d8f6fd1fd15f51fb30cab3fe70423ac50c398b8d90617eab94981e9d83b55412837651ad56fdb88ea0d6404cdc3e195545a790fbae7f1f197be26a0dd246f3b51934eecf9092da790247525d7822e15038d1bb63d215f31034b5745128a54ecdd79870b2e9a7542a9128107f40eba2074e601e48a95b3cdfcb8be94e326aca768e960858ab7a532c146bb264922219a9e2388adf560ed0c7ba4640d473802dd9eb43ee1352cc4051b8eb3b481ec80daaa9514426c3e69641707de8af1c2567354571e28d990a68c1a8522d5ee162313b411dd31596fc528db38282d4dc57e5549890fc92a672bbb468498453c16b09adfc1c9f099b33d1d3870269a3ead0ff9dfa3c71a1b0337201ab7be1a53bf2a65f3dedccaca59555790af303d2283eab8e907d8da1d8c1b8f04e0d8ac080f3579c129150de299f3af8972b88e9dbb73ea5e461503047ef26f8c38e523d29eb6d559bca1afd62fc580eda39160ddb51a47c1e677fee6103e3bfebd58aff6e1ff9d3dfd1a4372498098b721a362a6aeb1bfa36c37a6383ef146be2a2bc463ac4011597faabfd94f140f7abacd37b14de497db592e4e4a91c4cd0173621158ce45b5f23f9885a3a4954afe45611851dab5fdaadcf5a484fa60c1604c32da42c257b50b6e9a6fb54090f1cebc92e6a2b1292ce3123749684b8c3066524cdc582c72006f219d09595ab34e7537c9d0afc40cd835bbcee3aa4d0bd061f4e7256e35d4af61b1a2d819f26a398e1e8015b5bd913110455b8bf52195a831929ecac2c6fc2db553b8cd86bea934c9445a2a3341f7ed33e23a575e19ed4140c5366116ee0fa349e6c11a942e08360839bbb8e65e16bdcba9079a9e86571eae22d0098796e7a7976eda55d08d938baa6c64d80982b7bcaa0562d9e221dd5034a0574c291e16eb59266778d32e23c2f46c0d03dd4425950a306fea5809ec11aff58ef80d780039e89c694bb8e5a55961492cdaff847ea4955cd5b4ce20121ee9986b1523a58e063e3383850954f78d125b4818630e7ab9e7a3c8d8eb4513aae00bc85d6e57c7cebae51d96a7fedfa21422c64664559ef6f5417778e2ddf9f8633862fb3e9098c172a3571ff1468b4751c28a2a397769ebe26d3f90ed23f45439bc799c969f6f3fb3759bf0bd524459c2058e2039f4883a940ac4fd1a5d626c7bab6a10044af4e15ea86bcfa81bfbcd365dcab19d8e4758aba2505a0ff71ba776d01ea5da9f2099da7bb8dfa1a0d826631c8da7b77a64620e3ccadf05c796b5c4008774b9de889d741dfad6f5fb28fd720d83ca55ec644b24a55b012e7de263638855770f3b9b050578f8851f11e3f6c9e5097de9630dc9764d7637512dff9eb0e3b119382d39e0300406ffa10bf5db564367a71192bfdbcdf5475fbc0f153ddfe5c10e523f2005b7418b0a3eda560ba5fcb8022576efb3a7c81cefe721d745a87b2695299d43d01b063dfb02242913aa965bfa35988fcf8a35c5d259889be6f363b9b25def62dc7c782e654f2f3691867857a0c683647a8fcdcaeda6762b9a35dbd491eead5b7121a7ef4e1a7d2a678fd8e1fc5e7c4565c932be7e622eb761085385433fc840b81683d4c7c5627a3fa2d990ec7f1a6398711583ef69e5a319cfe719f73f40e5e5298d266e5c40fe21ee2805910daaeba6f03f37d6a4577046e588478beedaf086a93258f1b4eaa157342e46400fefee950be723e82186639e86e1731359b0969ccc70e04d338c6a78332a14985d452dbba682ef7851a1c9df8d40450fd6a3cbe7482511e8358523fc6a9ae71611d865f293ccb1ac46f6ae44df549bfb976dcc61bff4bc8122e9d7d8fe9dcac45a5a7979feb6dcb60cf821b381809ade5db236e1c32194ed6e816fa81e337715b2a5af146e7370cb94af138efce231444c78ceff36e3d7b8f1619c90dd4cf9a0680a7838296fda3cc89224ca29366a7d32faf93f9e6a09ab342f21708399d0f57e18b9df805910413a36f4c09ae4ad5cdbb265a4ed99b4a6f96ce5a3a36d7c2d7362f0a362cc5b89e44c23de15c47a8260ab66e0523702d9e2892aea7376f9501cd30a1d5c41ddcafba7674b164063680c449843a167939038d42b02b3df94e5b758503c1eed8e8fc045c8094ed9a594bf5ce40e1caf40a796d46196903f4ded04cd1715b9d876a110fd674677d1b1eb1ea8d2355b1386b73cd758fc993c680d491b22133c91d0e38df86d3828cb01f09b5fb967399a1be408289780f38d2f9ab76f81989dcf7400cefb2184da73eb2b1a57e897cee4c8c039d5a6c513ef35f02c10a1a2458c74584270cf9d7c85df51fbda4dc4982bc6129614d3d5c9b88ecdc4f4d38d9526518fa116145c9940ae99a5eee69e921aca0b58d1af9ad68ca1911570a3e1fb1b979a85db518e9f414c69c0cb5756eb9e2415390637ab791ff2318ae33548d1bcc229d35a209287ce5b39e45ef5ca4bf446fbd18c5e915541922d5acad3c23c48f3e0ef41ab0922d78aa569020992879137087509cfd509be7cb7d59e706cbcd99c0e61cd3addec0b4ffebdeed5855a6b893e4db3206057e4fe07ede42cfa5cff6ab636155572120920b09c5e7222186100536f9d9a0c1f5bda1600965c892e5fabb719b38550ae38403c254869d7bb60023c12d2350adfc709f05f1d2680086db3cb30af03e6ed126bd444c4fe595cf4acb87b1cf5b91b0ebb4b463c2482ae2e1bfb9f5d82410eb61ff0fbcf74a53007ea0f24bc83b3a0b3b567b2a77aab909d81d19ccab4b61fb1fcb797b943985bce52c10598ba1c828478d6d68e554d15d479e9c23bbf5e32dcfe0fbd752f7d1ac0630b7e9772aa95d8825eee7f1afbdd831b1fe5c366bd82aca9974f7e6208c1ef572b6cfa281ba1fe02a1b6e65eac02be5a72539c6ea11cbae442c997029c09e18002f7d5a29aa9acfc3d131e216be661ad7526d7d6b974d982c82315740c54d53a0288f66fa4a64a1e3acbaaaca6732281b3564783f43ec955a2d93d7a028ad3a33936b65c53d4e3e6251f6bfa2699132f772ac02234cbcd62c6760fb18c87f9162f73db1d11bed96c6953c9fa6fd20aea39aaf21877a2bc8eb8d50f3c5379a8f9fbfe52dcf7e264e6f35334708dcb875cefc8c66058db842dd669c54f7523ce68c2c77c400de1d1b653605280d1578a5d5d2682b5f6ae6881f4ae4b391c0c88b05aa99787aa47d724305a267198a475cb3f7b719d6c38eaa8bc2b327abc1cd9c7bb18205c7f81cc2a25eb063751f249fce2a1b1d076c65878e8b7fed4db00da7a1755926b5ee55640145a1200ce013c7feeb24f00ed7966ef7f347b1f6759796febca3b70cd9287cdee9af9973ab196ce3a237b9e129c3053fd7f5204177536d9100cb221f05fc5caa72f34bdc364c8ac2660da2cc3b774afd1553183a89e3e77266b3014a70efb924899e64e7ce7ed7545ae8117c89c989ce4351efafcca381fba08b1d865331ce76d571ebf85f56bcae702bee5782fcdf1ea633190cb62a0328b810b2245cfc464981bf7378547e46c783347c53b59efda35e6f1b2156b5d8a59e04df271ff944ba7b3faa45704c6fc66e572d87284ad97b79807024b4b64e3afdd7ade6523de351b490b14591f97f61d55d9a63419786477521f857a10d680e771b4cc559669ca74d431d1e3e2ff51d6e347f570d92191647209573983beaa659be343940dd8eac76e1da216da62e97fd0632eb07c85da757984145fb96ca8c7be1aabf20ae645bc3b703a69e8f45c3b5d9e17bd521fda82974d978a943faf5c2bdade15bb535ea1c05611a303cd61a13f9bd5cb9ec3da8e7f2b04de1b4c94588923062536cc091a20ff27281f97a518c353614df2c647711d6dac9bcd38c0e02b1f1f48788052206e6cd40dcc980b7c985857ae9bf0a7a4a97ef600a5de9c5b5f0e46d643ad0f37c907e706c59545b4c9b7879d9b64bbea58b59ac5dc7d6d01171d02512984cf5f6dd33518917f255a1a835c7188531957a1751823fc1f14a75178395b7e03b7c6010a8f42e20a464b5e251c534769f45c5577921253b484ba73731daf71592f622ecf95c9b993266234968779b26aaf74b4fa53628e7f54a880a2402b9aa05b41a47fbec05e3e6a1c1494aa33f9baa5f32f38039a930e9b84f557962281118ed0393ebcb6d6b1468cedc603aef686c40c66262632b3eef26b1c1a578524542c978fc5189fef443e91098a0027be2d7341a1f8cc4e1ec0b9c19d1d9119fa5dcc6c84a17a6ec7c3cccc1fd6aa96d5aaf257bb34bf4c619b1ae686d5a15f1dd232b9dfc112c24a0e67c7c192b207bcb1c5282353a3b1084b2763ca3ba095e03280b7b78b7f2cb1c7789e12571d2edab78f1c3eba8c1f271defb4fdc3feb546a561b5ce4ba32fbe541d32a749223329962f7217cae724ac3c4f595d6200aa889cb50ac10eb76b7d82a685cc580b1be5a76051af513388dd7dd3314b450f37ae0918d3d616b18a0a1da48672943b5e27e392568f8a27bfbe738df59dccd89e1e50878e0033efe1d4930293d4b01049864e170d4f95053c5dd1bdd2b709111817259d07df8073d54b3a413ed5bfe39decd9033460bfaf4a221b3a4997f626b195d0c399d4a4c829cfa81b37ba6ede833a439d01bc9ae62f6838eafe8e86335fd5a18ec803a64de6cd145e787f1f66d5e837caaa2f8b59528ae7d81f95d57252a752a383b33b888bf4f76d2fad626049902133989610391e92b04d86a72800e16886a955a0b1e759de581422ed79fdcf1d701244ed72a9a85db4d75b4e199d1cd0c96276fb9c7b27f5fa38b7a8e167ad9059ac076fbc84420623c3abb95f85d230b9c44f8b5eb9faac295eb25cb7421c4a3fcb19914f1f063575fd663b7bc7620ba9929afcc0e266261925a3a1ad0d6bc1b1e5f84665ab7ebce689b321a72cb785bbd60ab79ad40ed0f8b143dd8a29080dd38f5f7a57c8ba35f4a961d13ab25583eee7c4d8f3ab3ed236d1dc311d2c636651d351a2e7933cb324fe8c2b924e9b1fe913e3b41cd74c692fa35d168b9ec719a176337cd0a560dfac6ada83aaa1b96a60675593f8556bf923a05fb7bba4d85064ef31b62acc34350e64e26fd61d90a39f291fe514b9a9d68f64e148c3f94af2950f632ac508d2b76be85c9855a2ac5f7dfd3e88ab07ae1c26dd5aff5971ac991336144080737382d191898d98b18e42ef992896a37423a60a53123ea90ee3b21422c1cf4ac965f2299238de9d00c8ba205a2ad9b963815b7cc860cbbfc33b58d5dbc3711f3aea10fae2ceba35fe0b075b03a93123e333ca60499a94b8534d575bdf734ef3d457529c2550d4edc46c0942f1bb6d0c6268c9cce82a70fc9815593e5ef9b019df4e61f1f46418c771ed134bc956370656f632cc690fae42c197dc22c7abcaad034b7df3d7072607102ab63f4a92e4b57a6754c04387e86b1ab77fdeaf7356b3c60c803b28fd42a8607bc7a688188844115f2d5987d54cf4f7e2e2f47c30c3b39a1b1c171fe330ccc0de19306be822654b88ed52657eed4bbe3b4bc2246f4a5e9b9014af5394b0949ac8734bebe7006e239896a1be3aa08dc9550e6303c2baccccd898c3c82582382a544a1064f162a0bb61ad97d393029d63baf3bed4b12e9afb390cbf874a327108f3bf13c38660d0893992c5bd3b2b7582e2dafe1e75f9224d077158ac3285df917393ad6fba725be4a1f1373e49a7983b619d0f8588830187c3c7e25ad5358a2c7195e2e83210ed0be0a942f0f900728c0f55bba9140b83b4f8184cf196daea4c00ea8781dde2cfe5cef13af746c86a9a3d56924c133b02c9f03e4d07cfcf4d2e856b841deefdc8af9e7f0d0458c80c86f7ca0dea27410a3719e5c627526ea3f2e8074ed4077d1f7722eab8f87614b8db90abf7ac11ea675b9bf0f04e16ab6132841951c95bd3ffc743e31942e1675dbce1e7786aa8fff2ab9b2e2eaf781175740c4edc0aeb205eb8439fb06ab6d5471d6be661a87d0f213573263a1e62c82293f82e0c3b48a2933865fc253d2f2acbe031d7c510ebdc4f3f292ef1873ab305e28eda18e1106ea911bc00dfb0490af9ee0d50b31607a996d19aa0798ed66052185be345ad73bb3d6b8a34045c1234990e5397026006e394e1490c8807feb7754e644e490971103d08a69cd6f646aa7975484e40da129058c5c3d5b45258e70ef9f19a5291c0c7e3b216182c748b537b28f8237f9fa588a1e7da816eb735e81c5ba20596b8dcc253857010fce01a640d49ec2bdacb426418433571bf5478ffb9899097e3e8a9a25f01a9849017c2db2f7c70ca2569334ba8b004380f17dbaf4f7b8e7c3a88aac9033420709d1440219cc3e200e745070755b1433f55e9e2293d851e2d96a8968f6abc497fdd5296774658b5f39a1be767050635a437cc5f3490fee24709d48ec64ca7915d35d8ddb9e60ada2bb5447c841ded5eb1415f8cf6f58e561c2dde6f512b7bfd44769967ddeba3c808c88e0a4c8c50c4bb187947d31687adb849861ddb7038c69bf69e591aa7e07567edcf2d8e9a3f95c4d5abd25ace0d3467e2396a90a28754dfcb1260c904f2d01fe48ff1208af8616506a3591d5e24fe1e700b92999f3393d21e5ee5437217557cc911020ecb68283ef9abc43972b393d55adc0617b7e6f1b539f11170815accd437d761db05eee1985fd869d288e4d0cdf82a0ea7ce3e784c5f5c8b422c9d16d126e803cacb37e54b9ad245dfba9820f12b6aeece035c827622b21411847dbe9886a4d1e20118f0d1375a6034e1c225f0316f825d5b4369d64658f988336464100544e9a0ddc5b9f0bf467dee609fba1a938903c6cfaac593c1666d6c91e0c2e3d84c256307db2a97f805b8931f7ecb8ef9e7e4cffe1c8b0247fff0620592c66b1317b221016955b254921837fc55c402e12e11a385b99977ccf7978de95d1042a62d29140e41a8a0a689523b95c8a12ce3ea69735420ea85699d95ed40eaae0a4b4a73be9ddc80ee857d2aadb6eb4ac98680dd566ac4f6cd5ca080bd8b8c32121744bfc8e000176585deb4ebb5e6f9751ce45c8df2f3d28c49f851918e213926a94b7fe39b27d229216059b8f73b107a83fb68c81d47e082b31e44b130f4b46e90802eb3c2bf44da28b3ba942c4a94db215173fa85a63820269b11b9dee8b9c507e8f0324a00f023e70eefb78084aaa0cee69ba2a921063378b517d280eae69786891da11db91182d07c81be5e6828efc33e625668dfda9ab52c84f5baf446966f2016080f293bd2c04f5a90b1974ba3bca10514ee843d68bd0b9a2680702b365af90a00a1244672b1911448057a32d3d5c816fa92bc02e420077617d32bced81d69d42a15ee03c4a5289b581996605e0825b3121ea0586b683c795b587fe6d279e8ddac3f96659584eb709a984be94d9a45c17df5b6dd745670dd09b8541bf8d0259566c014e96b92acf2bc2e9ff915e0ceb70c8b36dda7507bad43a9b762f05533f721282792b3bd44c3112d4139c3d67bb634cc4732bcf2c67728388cef1924d81a571eb8dc42e4c470f4df7db5cd49b34c0b8f1bbadc86719db1a23bd80e1dce52ee9da247b85e5398906f4c8d8d95c152861a7a42b55580c9dd2178f5d61c67f5397522a862d335533300ad05ffe9cdaa260329c29391e4457e1290480ce91fc8fd5778adc69fa5d7d967c0308ca56c9915df7add5dc3fa1d5462bf506bbe904985a34305ae41862507372bd6f6d09a22d6f537e4b22ec41422d0ed528582fb5caba0a7480c38a66339cb126d266784b94f682acf13cb17f14a516299a8ed024f3171add24159d40fb67465ee57f531fa8364ab69c7125f9511330b5799f20d96fe3e87eb4a775743e753203ae6911331f4b5a12200a0140dd635d160045eece9bc2e934d7c4993b30f9b433820eb6af5df7438f5b5770fe0102f244ae1cf6054194b407c8986db699863b5f3509e32e1fe1ab6cafe5c45c6eafecda43b72f6903c3401c32e6d49cc84ad91d0f498ffec5672481c578713b4b78bea0462b3194facea771ebabbacbfce29616b8c0a59397cbbb9b11f0b9fdcc32436bade5735876e1ad76f75299cd67ebcb8afef88786c9aad447554a363e1214961ae99d5ff33959f929c33bbfbc8777560104cbacef0cab1c7c4e2c075ab6c5da162fc1c729c2d6312846af9da556fc4a6f15e31c1edf45a35a7c57170b761c02a1a731bee45cd8de3e82b18c24a47d690ee059a63ff267e0dc3e7447fbd316215e9c2d10fcdfcfc4e34803de1769109fd493fe9dbb4ac0cad6e605a2f8bdfefc808f71fbcd7a788df2f734c849ec4b7b9aad740486b1f6e6881b1ed842beeb28158d7ba29ef8775c44a4b595a6be544774b6aba2cb4895813e43652afbb26a094af8f0e4aa04c25dac4f6e1cc8fc05ddebf99418ee8516683898f5666871d5875d034d5f99242b4655e8bda64c284feebb171965470200204dfb792c482b2b0306df0c44293afb15b62f78b8526a1d7f85bfbc4e0b52219c8db36ee83c8ac404d83d286a31be243c513f689707694aeb0e0c026edc2524165e2ec895dd3c7761deb190441085bb21cb6e465f150d7fa0395aaff83a1d4bda17b5bbfe49a416360d8c046ac9d2100254b1e27ed77088e23d14e986fb4c4ec27a6c9310bd10e0970cac008fbf2b21cc5d85ec756f8bc00da39605fc688e970060974a1716be228e1e6c8cdde4d56130995c2641f3653335d7e1828f86f6f6f82b75dc15a42cb5cd9c38d7a904b15d1b91127250e0ca9d7543e2908d061fccf779650fb6fc6cd9cb13bdf4197d0908fb875edb9da576546063f926b4722c02371a962194c60eab80fb8ad25d92b7eaa710e8e7d3a6941b47c5364d5922a7cf1d5f114cc2b0a22537a6c0599c8813cc6255ea25f46b324e476315ef4c6dba2313d4d4ee24424dfc8eb58d8573e291241e4f168c8cd548a4e256005e133775a91c60bee038695511e84b970b472207edc206d65f4f43bb0502f473c4c69368a1509066984e7194a10940a08406ead2c71dac23eb3c4741edc05aa8c3cfb28df1202708a12cd206882b0c86a6a1b4d49d8a5458eff072ed1b683bffef9fddcc01c22743f2853051a66a341d01b6bbc1e3640842f1e66ef286370e51dfdff2f3fc172c55a5fc8708f54b88478cf32f46ba24f82ad441100380c685ac802f4a499eeab2a3ae80ae8eb807c604057d039a894d083851c2854f710ae1400e9a886de43e79366c159fa3a0d15a6f119aed810ee593542dd109798bee64adcaaf3764bad343aecc0d5c07002170c37857ec05d5258d465080048107c1d96339f681ae1f195d753165009ee6f443466c74892176aae7a7a857a06560c18e44a331c5fcf8d7522d57f31f5e99a8d14ad48a1af5fb9f184c719a82a92510d6324365274d743352c13ae57209d639eba16f411b5fe367acde723a4bdf8a2c16d7040ce7fdc3e0b7b291e3288d984d55b6e0fe89f0a62088f1d66da94b512cbf44b7d9cb1aa1707ec19e882d3b9a94606851953ad6a381d6205e27d96931dc6ec096a084e05d791b307e5082d21928b0cb8459495d6d84dbe2b0e74cc0544082047eb338850705a6f8e6470b1eba4d3d0269c1ee9d0dd30ca7c09470a0daece49ed54dceffa485654ca3e524fbf03cddbf21786c9921c0b3633c33e67a57bedc91727074787808f4d11861eb692accc7142b8a72752195bab98715c590fa78802e7760aabedd4900bd566262556ac44a63f9ac54483981fbefa2be1b5b1cd4d598f4cf60498265199de42fb0928a8f5c95a49ebaeef399190b95cb5f3305a79501a26d3356aba3af79890f7eaf595fa14e1927bbfeeb2187af7963da045a022f008aec1bef797aa43319ce791664af3864512c5b7984ba4ba0d7bec6af87612cb44fe320c09a904792fcb501cbb2886250c54198796d21f8c5e79c5f4e618885465ff3bcc65ce837e63d6b9ecba684e8a4d2bac2fcabaf3ae5f2aee34cfd9dec97d987df33d309564eb8750165938354614b1b038bafe64d3edb9bc9793a6dd60825e160fa44e048244d3cf549c0aed862572d8f7e9b11a5dc097df44ed4eb36b60a5369642c23744fdb3803714692cfa85e109823a7c13c8a53ae7462ca9b2aebb9ddeedc82541515a1eb96a7cf5284044b7d3308779df8d8350a88e8f2f46f487fae05f9ae0f4f71abd4b9753667828fb905bc1e1758bd1d9e981ebc6a831816a26e9cd99cbb0dcb5c6a7dc4cdb8bc4fb1a63ea18a9609746c8591f4f107e1de1cda511bc9aad465bae66a51b67a101d7e8db9ca5610c3cd92d716b00d2484f8f08e0d94fe12328f1376e21ebdfa63e712562aaeccfeaf90b3145c69424017b82999a76555b3eb38c6b87ed17582451ef49230ecb9f4eb31623677db9dd9fa415e60f123a8e6af3c716749a006756c4adb79965e69e54499812064284100998e6aa97b9127f4ef4fef23764db8b8e46f62a2e8c6178ae38a0602d9433f5ca84b6db458d0a9258f953d17816be5964f0e940824215b2ae7e78d6ea06d22d956949f1572c19c0c92b3737dce47cd098b3e3d243f8954c7f55db8a1f328fdf9f33812ffcd390f32299ec26fd4a05e6cc1a10cc9ef01b1c64b7317ed38741e893319e434f81e9f794297bef855b236f311f69845206c875e13e9d67715d0ee9db308c0a436715747d0cf284449c2ef0484e3ad709dfa2104f18096194e13123bad521084eec82bdddebac3cbab594b28c491e6a657d2b3bc87fee9565d3d912ba03a0f5231861393caebac5ece20d6790b7c060f4d07e140a6402e6c08e54b1e7f8494ab99828c90f212d94510d40758cfe1e76659448f3ce9b2f3b6ea5c8e468216457e3bd7a90fb6c21e585a08b08329e20b7612f2ebfb3bb2d62b050e9dd288da9e43d4814eb362fb8be3012e724abdd0fa4a40528345b5412fb664c1f24f856d3fe418281cd4cc2d173250066e463436fcc11123fb02ebf002fd148ed7fc70919deafe7d9365bcafe617caac4d447656df3e6b68a83680ac01617e6421a8a22b90be3ebe022605cc3636d5465daff4cec9d76b3fc6a85fe0472e2ea0fc0c90ad0e102f405b52090c754c033b83e3c81c9d80711c4d67a227b91c7ffe8a1f2cb0f240d88428cd3ee3c4a47aeb149e901c5421d32f90651b6e5b9b53da25556dc8357636a58da87c7be7bc8e64854724bcdf6ae620f70f95d92118d55fa3fb117ff3a106695673dd1352111e8be3f275cc4226aa09b587a40b214e5fa1c01c2b47e9bac47ac2451eb11b6f84fad5d9c518c70ea72b6816e0910ca3d72a050036472b4f31adfccd3d9d3811017110f880ee00cb4e7b3b3918728d9c6b9f8ba6e1c8839ea0657d6a7506d75530407c615af46ea4c3eda1a5b8eab68f2a07448226045e63a5219a43c623dcfe0883462415be4567bb6524bbc2a536e4594b7c228e325a901d5f206c31fbb6a663c04c658dec2f13483aa6f291b0ab6f0f3b6cdc22c249b24b819a6f84119b68f1dd3f17326fda1aac9474675edcd5587c16904cac1e710ca2d9756c229493933fc06cdda6c91aa26fc45ba91715d59d762a76239f3b7522a02086b122898b04db36469bb7badecffc34c31d3cd5651fca187ed61a761fdf9b44da3a2269fbb441e0a14b5980062663365b6bb8257f468b743ac3218ca2b75229bd6dbe30008656da54e2bea45adb4ef4d68820dc8de90737170179297c3221f520166b5094190c74e7859269753fcb36d9b4dcb01263c45c3138411e8ba7e06eda0b2b7ea1cabba616d4c2fde639475b285cf46c0c7006f22560b564b15f4a79644ddf52c5504fac81e76c3f52a5eb6dae0fdf2246458e7ba4f1dcab2e9e9c0d56bcab7828f1f616d66cf903807791408af94303ea6e2b1467fcc76bfa0e1152471009dbea6b09f76c6f7d42a232fdacdfc514d1dd5e4b2c7b11bcb9ab2fe9160d5350e3bf9a04c81b5890a9d07f618e46b64298522f7f31ad12f13502032bbe65fc98047f89d98848aeadb0d133d2d6fe629c14e45419ef51703d140b949a0281a5598f3ebc59fd04b4accc7207b89048043a3e5557f75b071199b0657e4f1132a642f18479f26f936741670fc183d71dd706d39243447fe1f496958466c56c1dca0ff10bfe62eb4d7844e1895b3a3584f25413b4558c180ca633107514c09da5d745ae64c45449698ce079d240ec7afea7c6a3ea1f39d4a32e6560b330ac656bf5ebf216cf479088e7a93fbfa96fd242bd873f0f8a4372698562b098958b3bfad58f5822e0fca379c1cf645e3578bf02ce7ebdee8e03c4729fea2f70a17b6bdd139237b8549d46abaca188772337f387f78338bcbf81386304605e4ea0a7af5a845acb4c8fb2bd52632635d775fa14f0e642d40cfe983882b05b08c0297bf05cb35c3fbdacc87136778ea561fd42c48fbe197671dc26fe8169a299398e053a68e37ed4c631bcadc3ec2eb46c9cb8af04e3398323ca6c28b3b4121aa06aa4a4b4352a1b965b4642c07a162782d546e2845f13520507bb69fdc11e645ef486bc011da011a03ff553c84046d0cf42b608f96420d3665bc4d7cdffd09648e7ac18b30aa723962efec4fdcb051b88c358da653baaaf5eb4029e48e1620adb086a58c43fdd852609fa434957065bc2a9f147076fe93c4e2e81cd6382e3803c5f085b054a8cbbd9c233495eb8d0308b9d5489297f3f271ef5e386134539f00e6aa9e5a5123864290759eafb6e49018950f7ccfee78ff5b56e4da0e46211e1675a6883135bee3107b742f93af106eeb022f2cb3b2c92804ddbe32e2dbcc061b90641774e7b351454330b1cb120639caf18fd8cd577356b8ef0fd84c09971dd727a0d55f21867f8c2895b023d71a3f15761a311e8a37abb3eb93b2fcd1793306f9e151805dce92fbcf63133411f3b33cbcca0f20bf2b8a68de82e42df33833b9acc2b80caa2203f6a95754280c2a1e05f69319301aba76835d4ad01668d9dd94e9eb648b25829e3dff78b6c13ec70510281caecaaf007ff266a7fb451819276972d4290ef93aa7ab11934d8c63be0e604d1e8e3aef101ce1041c07c1d139f7f753687c9ddad4d8ae5997db77fdc41fa8167b682ec3821bf33d405c779332fe814c864a621d247b90e347bf48fb87eb872c7c191146410429f018d694afae731c8c58c7ece6cb73bea1a94933720d2d3bc3ab7d4b754520ce4cd46ab0865c66d57275f8c61a3a3ab5a36529c40430e779c337015aa10d8273703431439a7c3bea12b5e78138f4b83fed35b4b25f6859c6eaa2c03c1d9a3decf053228b876cba541751b504e765512a3d874be6ccfa2f1ba807d4e15e8483ab3041110f99213fcce3a0ff919a99d2e86815333c9d61d2925984a2e89021a3b29e0e7c110082f2ae29666efcd8ea5ca11e27105889fed945fb6e65a18694efa8d6e98027998c66cfcb82dff84dfb000598e3c7724bcbdb0494c8d8b097d19b44d5556a892a6d7ad89c8ee0e7de702ffe9befe6a948357aac14aceb0aae4dc8d8eb87a47731b2f5c765244c54a981f62b9f892e09145dc57d0307bd307a600ae79744fdf3dd092e51564304398ede22f7c20817ee2c4071eb0884917db27a8d2a9f9cc6f0f2e82e6900e1b6c74ecd26140d765841fa34d6d87cf44a7ec40baf48bf31f44c99c9904470ceb05474e23ae4bc0a28ad060fafdcfa504b3373d992cf6f00e78dab93558ed75db20e2ae1f5e26e8c46692be5db179f021eb21335e77c95ee5f66d763a0aa385b96d675f612563837e5211f2a7ae36851396850967f368d64051eeeac5f24fba2c31b3588d31d2da37f73b1ba7470048ba366e0b86c09dcbd108d95cefd4749e5c99250ac8d85ab8a99641ac59c23bbe85cc4cf53c963d73588e78460ff8f217ed133a7d5bba49cf88c4e1529e38231e4173f13065f5863ae0069daeb3378160c7ca2bb47d4d0fcc9c1a1e199af0ff56dfe6843f7f1ead4758096c51af8df81c013655e3e30312f671aacdfb2068538a90164271188000f65fe0211a0677aef458a89aa578632ed925aede5f4f56447bcfc5ad69f0f8e676d0f9fa31c0d7844ca41086b9ddfeba44bf77ae8c7da0fd8df7cc80b7ac69fe3f495a2c454c429a5689491fb473c4e519ad20b8bab134ce819f3130b948087fa68b1764b4875546fa0fbbeda101497eb4f92d0a70e034102e62a9cceb1562474ac3e0c66b7111d797f48299a5112da4208d64c4c7d115b536a73cbf76f3cfce21c623db2ecef2084fb4d1bfd637a302dea994d6a2326a28ebb433f1ad34cbb9c4bebc1a31498ef6929ddd6a4a3d4af14fadd93c4207b77b273fc7d423bff3785153eaafb94666cbb0b24f9aeb50bfaed7bbb4f34ab0b56707e650d231ad8b9c47ce91e600e2403f11aef3a89a9f0a42cef3d0075b95ccd6e8d5b5bac367a57b59e7c09ceff359435105935065f30a63e8f00c448a92d01df58e5051aefaa50f192890eae4792d71c6211d603e44ba67380a57feb424810879a25250f2b81f62c028a124db1a9437776cc941e152f80f39f315f0c22b9ce998e73807e68eccd8c1258d3818586f0c0c223dd82934355208aadd96804102f9c261630ea5fc8666a23fda2618fd85f182c0b35d230309c48f01c6ab63874263c6b4764b7c38cdacf7672ad6e892f08fc118292a785418abf6e5c339b12e83624bafb424e4708720bcfb29d96dcd82a5fd4f21bbd070abf014df914ca09ae00b299c1bb552e0440a6f0cbb64852ca014b1a340e1f2372b817e5372509fc645b7e3d9caf8ae5c2e3b8a4520b78ca313c8f6e4a5c6c8e98a14fc7a844a051fd5185385dd8ef86288a496c2a07703519b1349d967507818a78c72cf37edb9678086a8becbdb9ec5e4c9920950e9433c6a03ce643ef9fde93df34d5dac45c61860454a8a2a44d3a5b81364d21495dea93cc9c382d3cb2a5011a48e27bb7742efa12628f2ef70a8daf58ae8fd5f67fe40982c9df97aca360f6380b8264582daaa222bc7d12c24dba7528546a656eba88d72c641bfe5dc0ddd225aecbf2b51a138bf5eba703ff28ec19f23ab4cefa7429cb29ec91ec52403a0f3cf76a0cbb0bf0d5cfe5a8abcd79e3f421c21f1f0d2f8a8f2bd5303833788256b45cfb6e75f473cc12a61a9e6cd919900e056b3dc09bade3c6bc18ff8be58870e030b8e8da66bd7fbb5d27723041df6f1c4086aef30b4fe4191f270ccd6dd2a95e3dc4081f807364ad1c153d542cf8ce3d479113b8a1d07de93fb60e81411ee11043680b0781dbbc85859cf2ed8dffbae908e803b57fc63af13b2b511a7874e5445f8f5e5af844147393a2063de4f1b4704aadbdc94fef5192b0a877c8e124f397f75f16995d63ea8f0896b439321b5e3684c60bc2dc0f33f139ef2f680294c0e19ea2826296c46b1829e4ae423e072b66af08dbe029d5f2ee7b1a70fd2585d68f528638078d03517dd6084899ab9e4cb7ae6800da54f21ba9ed3e842fbc090508c7d623469e2aeb418869b406fa7270d1d66336f793b83024f0e46b9c557b97107a0da1b071714d3953689a3e0dccdc9ac2a52baaccbdf634c697019b635ec0c48614f141c12ff89324f3310b284c94788a20ec31ba371d01cb68dc8324accd779659c0b7526e453386155e2ef31d10b3f25fad0c20e692eb71096f1ff0dc5f34c649110bfd8de66b3833329b359d92320181b4db5915800a92e9dd6f3ef7dc4de363af968c9bfb2e22662ab84d42ef34b0672ed0caeee45ceae6462ce2ba206ecb46073242c10b5d896b1f83d81734e8401183ee06e0ab668fccd96802611a330525868d5c4cac6b1b9c84de135ccfbac0ffbbd3b9d4768f588b7b1061e098c7846fc1980eec9624fcd8cd6a8ebaac1d8b6f2fc1ee32c8f5359f920fd9dd628083f2c99040823b5945e9606fa6611ff40fb95b32f7584c808fd055f40cc42a47c2c4c05af8b34eb2b61166577ee7ec3d5c5964118c95824b5b51b1340945874d5496501453a7dd9272b7b54ed6d2afe60458c5b019efba0856b1a907bf640b6cace27ae7a25bb56d1e7f32189527b08a3dd38b371a793bcf9e86a40787bd96c19e2f0d4693f48f5c654f85aafa5d9a3fc8da48fa07392e684688477a8bf28e068a3d58276dd767b731189b43fd59f9f60b39eb97619ef3918146fa8492640ece28f1f1dd3bdd117abe7f17179f2f5ec6e8ff6087b3991ba1ff828631bcfb5513c6db209107a20228d0136a8aaa97410a1b6f5e0da323a41e7562b6863ceb73b1e749889933a2abfbc9e05cdd1b647d07271789d36ec29021d7afa523214920ab20b0b3f62947fe113dfcc6c838ec3659218b211945033a24170c55d1f138c075f4fa791f0cfd5233d12240f707b5d2473543d3eb5f2e00a1e2a1ac44404e882101f47e901fc3313ac0ac9cb70358e5cde3fcee4abf6ecf9ae64a1c1357d29f7bc34fd5c0ca71f0636064c344199825468d0f5cf1528a27c0462003428c94736111a192fbfcb3e888c5d6eb8aeb035494c946e739e8fe4b8fee0619dd5ef1b8c605605c90eedf181bee0978361a233e13208391d829d57cea0e22b97c341a18285444a796facb826f4048a82dd28b2f6a5f0aeee0c05d62e580902d51449ad9f7409aeef849b16be8ec204b811e09a2a8b6fe17912620834729d08d9e1121050e0549104dd4310a873b885344f17be77d261e85613a005170158ff206065ac6143d8889634b3794da690955d5e97ad0cec49f7efd8f53ac2ebe45fc9341477b5c0c4c60d21aaf5668a141c6de44f68071c7b768bce754d102de797aab42664bc742d4cdd788ef7a655be947fa7168b473bea5d3a0592523b039b66c014922a266c7f051087047fb68c62bed676de7e4e3e88b6aea96dfd97c31d46da3004604499cd528ae57e4207b2f1df70680d85a2f510f8580bf85c4f713271febdc96d1aa27091b2d3948b42041af4437f42e8bc0b72b2219fc26d1abddbb7207c7ac18caaf16aa8bf854ddf05837d8521850f97113a9661556bf59ad46309e13e4eeaff4302a868475f209218bb03c121646150bfc4016670333b14d98e1cd02ce9d80d25d0db37201a5cbe5c8caeeb1c53b592ae4a5bbfd3a485541e1bcf93c220f4ff335376766dca91673245346e07a2d6a5c27c85cab9c05dfd43489e2123365d11c1b27222c538084e8e7c1d1c923667b3c4c48b2b4b732595f4ca13e23194f5ea9e0fed6d6a0222b25065bb906e6c5c9cf233e5191ed15ebe80a1df4efaae9211fb612ba59d328e71ea38fa81a703c5cc34412c2ebb4aa100b4e0408e6ff13f6e1b68c233800d1781a03dabc694fcd3a9b1dc64cb086d438b17e59b50fba17608ece1af052488d85ce062057815abd1713fb9b8eeb7b75cea8483f861174fff66d24467ac34c5967afb4ec9a8ff6ca09c9fe22bb058d7366d02a4ea98ccf44883b7a684d6ea9de5caa267001f9f2864606dd61fafd2a1cefda5c32e5fab9c7619d9441361f1f84aaec038aabdb8294707ff66af245a698e8f92ebd56461ef04a2f304956add347cce0588014e4084640f70edf3fd673da188700043461339433ab9c22d8e24e300969f1042f9974a97040a3aa35da1a6cec2a42d6ab8ec51d51b9a5645aa654ae43f9fff634752885e05fa2b905dfa5924a5c7e5fec08000db5107862b1dbb89260cb793221b4b1c43e902f2ac434f2f2436e1f578aa21a683bf07e7bb6f06ff42282b4ee5f14eaf24cc7998c24d1a34636a2a8c162340356ff849798a0c1c0c942645fd98d00d5fb6044cfa91611d43b84b83e295d57b3e6fa05fb9c357a31c1adfb1f15f985ad3df73be056ef6a6f2abee02ec541ac160b63cf52fc0bda7f034412cbf731f7c0a86a4b45a83c5f45f5f4aef407c2573b686e3ab4287c9e7e48c147b73c670e3c42a0e2170dbcf33d957f3e137e8f31e1954fee7221b0258fb8278f3bfa8bca69726fad3a10539eaa9982b36ee32b66affad9053abcecb25488fc294c26c01a69a6277830cbf12a5fa88a3804ae6c726d1f0ce4ba8b557631a72f0de95401b2eec8d95b155ee8f45b0e2ccfc19ebab673802b0f75069cbed0e55139f1ff4ff6ccb1028b359c462f608a0ccb25bfd22d287944e8fd9dcfbaaf7097ca9406eacf81751148d1c8b8d4fa16d2f706a0122785fe0a40d5aacde1015cd4b10828738f0711708c8e537c8eb2c981ec090a8d98e53dba5946d2404688da265474ffa0d6a76fccc540ad51a81b67bcf338e737630d12e0d11b081e5ff886823bc26180bbfc1822390149b988ab7166e5de0c31f6d903d98d2833ceb294b64e0eb63795b18a1a9aa330e7ac5cd61db778948bd64378c7da8d76ba5034ee0ec73aca6583ef0a015acd4a637828488f52a48e0928d49d85b07739be114193388334385aa3f5653a16e39ab31d97a7898024023aab3a450e7edaf37119389e5a4c1fb88c00c5199430260ab8df08ec1989b5bb2546d46cdc5ab7c0448ec24b3a5115b45bcfc930e34008c3b5a719f010e2cab2d17a0baa66aefe49cc2478da17567a17483c816027198849b3478720faa3c5377156e11113d0fa1af23cdbd04317033cb653a756ac848780eed288592c9cc96a100fc24d560ca71940ecdf4ed17aa2640f63915b4a80ab3f46177a04cdfda01748f171af39502770eed2ec78f1efe9eb598f15e5f2a57382c52cac5348d454e1bce11e5f760b54e7fd3d3221a2fbe5d13f01c1be7a40a88788a09294376fd402d1ed83214a603813369a00591e45886e0431425f9633567b2572df86ae4050c3ebdb7fdb4cf88076c234ca487f95970e04b18b2824525142815a3b1408d0a22c7c80c6ee840486dfbe79eb9acb3976cb853bce845a1991b20a50f83b8bf0165b462c2237343080fb3ed19ef55001cb1877de2fe4c44f39f45d9df42c7d08d54ab7a5cece833cc60735db33425303cb500a6a2c5e9b398aaa84af8f19bebccaa4abc3f5139c00e222e3c0e9a26b2c085f5b7fdae1c5359b0b68cad053e1c29380bdbfde4c48cd12a8351ee482bd3b871ac88387c5575a9039497584cdee070a702c925fb531fa0f7ad90c0c76fc12e9e476f4751dec502d68c5d01acf61c24aef693413659148a1841636429d91194466cc8dad0990d0214348eb8e1999ceeb3189222f793aa0c1efe8403ca2f58226a03c92b5ea95e37b8325c00925015c86389e8ccfeb24844887206319c35c379093d0915fa6146ba153f4a6d13bc986f5a0a864b8f42c87ab3355c462e8568e40aae7b7e485e5a33f43e9d703982673f65ea9694e65a4d9295796f80d19e8db4a8f452a9504918b3ce718be16e88050e2ecd43108e8899c6574347d5b886e8a3c00c0ffeec3da05a130e330072e2615b117710c87c9174a6e7762901dc7fc5292003f6df4984b5f618dc51b4146fe1d058109a4be390647ff8c806c23be839263687cde5590c509d00bbe8ff3fab43759137c8e636dfe91fb2c99b25bdb269bdaccfffefcd1a46557291ac86bc95449cca94110d3fd9e76fbf4b6111ed28307abbd4b4dc8cae4d286f2bda4023b4795a8d24dc29e5cb572e51040406fde2ce31355b32040f216057ae93e6fa9769ffc959b7ff78931de61bf911a585a4fd4fb24c05f631162f0ecd78b91c4c298c592df46fdf34bec623ed009803cc3ad40b91369f0306e514a82cc93ab0473c184e1ad2e60b1d52b3938b0332340b62e778d4920926ecfe74057659103721eee3ad6963c480c15d7f3ffb6447dec2cdfe8992c1913744bd4f1fb89232bd7f777943661bd113eb744abc7aa009fb1ba256af33e73ad3c76d81265d096601151cf2ed5129d7757f33dd90fb40e93a99a04be9827c081ec144a3e2aec0303d93a6d1fe4c78d5c3613b86ebad3d4eb0b8d70dfb0af0acbb2c629b359c01745d9f1592c13b27b472e774311df0108130ca3df81249eb36dd2b83ee21cbe7c7183409e5f6e0211a667172558b8d373525cd502e398fb433db5be15543fe043ee3c0dd045c62d9563aa7241ae770a09e0e4f7453cf1fb175af06030c0b36029cbf140ad0effa78fdf58c6b2db34e4e394331b9b0acf5fb8e36b6681abeec871ebf9653ab23775f41b4125357b43613a29654607a95c5696176e2e256a16265dfadbfbb50a5ea7b8f4c044f7520dddc6613f03fa16bb448848ae7f87a4664dbc19488b29ca5807223218d3dcfe84129491bcedb626542de7602e90e891a1d69c70c0885b0802660df92efabdd8061452a98127743882dafe46e65ec0c0a4eb75df314104f878316846f89f7f4003ca36d1a518b22ad590d12def2667819a7d3c984572afcccd7d4f284b394c15f895da30da3d86028d3e32732a28867eb2fef2540eae45090f1c34e0d1423fdce7fb3b6efd8487ed01502ed0c159a23294168a1da0b7040aee044da21fddec8a236a51bd1be83aa7df6a44c48d9c34be4df13bd9d737e97a30102ef913afa684d52274fc46dd1b78fd2357c4306fbae047f98f090eb7d2995788cc80f05b3168bc89b1ace9fc9c409d131b0b1562583cea177cfa3c8b7b475a6e8e7b377e0c82b08c9bab3d0c981ec6289671b31f41e375fd758d2fc983d9731194aaf1b034e93c0406bb2aee29c22a5c0ea849acd34a468787c56d3380100ced2f427dc4cc403864593044fab9e6468366c3a05f8e6f4219a23e95ae2f30c156325117e8d4c04526a5222a5cb11a78513bedf21cf35f275d8407142c81d7e1922eca26786723e00274f074f318312807e2e81845f4ac88ee3b00ef4233389e2d3f419a87ef6cd2b2d150e9df85fb2e248078927d76bcc31f612fad331519451daa8070d261163fefd4f0f375e4e4f24cf703e9d9531fd90e0b5e385af6ff6931533761b8b4d6828878c15f4b33ac21724e32b2002486b2cd988f6cac379507911ebad42404bc466322e64b9d92ada2380e779a63570b84d9ab4a804037e6ff3001a83d112be89eb401fefd530965343eec29fe29c03dadd27e68dc49c59d13efe0494942c16891f5a3d93913652a9f16b60bb81590a49e1f59f43f4ed8df0e194d680d4f34abdbedbd431cd2ceb68d3c2309cb2da15d5ba36147e46c91544258f6a99b8a4ea436b11e0a4f9da8620a0b9139e0104425c7d9530a5cd0e6ede9f633e2b3f727c081fea75214f7058ab32c11f8364681215eb076b42893945e6857b78767ca1da076e414125408c097a29d70c37bd1d47572fdc6c2114a4a0a155c39c3c532312038ec2e539c8295eb83390f2e607d02987e657269a311f2d49f9b576c73f93214b9e0149e1cbb368c368699a53cc2f836c20e85b4d151ce27f37b5faab87e88205e2dc112eb719b95ff22d751c364bda029170e39ca46d51f67150dedd1f4c00205eabde3621e407df05d75ff2ac545ca23a6ad6abe0aa367412fe48aead62ebd33345f8117a6289139ab1454887873be900dc5deba28443d02e0909747635e83298ddd05e6dcdf0c1a7b0c60028cb897c8b3109214a420491ed37bbcd735bad9e36d81b21081971109cec4b19a3b7b6387e2d789afc586c9c1cbf4e9b7eb7c0a121096bf00f8a98463e8ab90229edfab7da07e4cf1c946aaea0765d7b4cd4210f6c630f2da21f91673efc33aaffc4b2b2224f779e0deccaa6f15abd942039829e73dae5dd86c6990ff05fe5fbc81d220522445a4d01f577e680325c40e216959ef24a1550209717e1e8207aff841233b70de39d1de173e074f502b1f0d3c5683d3ef28cdb1d0ad1a72649bdc63be4b163ced72c7725aed4dc2edadf4af29876d1b5459ecbd07e4804399f2375db78f010ca6b33e82f82435000c98eb60c64fd3bd643d8753d420a32d605651e6bf0d349d15884553ccd9c603acab12b1ebf8519b61ebc43478680e73fbb7e32f13c574b9f25917630ca8d0a6c0c6709e32ef6d3214e521d7bcd2ef9180e2a615bbd453c190d965d33e93422908c9fcb9e14133c203f71503ffd3c18a62cec69570f68330a9fbb5bee229f15948c508d9e26055448a61fcab54b0b1e2736fa90135dc5544451ca7c58c080620018c15e705ec7620295e12ba10d11b7146ae2d74aebc149fc6a8438e49bc12583f2ee5bb5b90e89790a2bef639ff72c4140ccd1823f0768aa1e3ca457a83415bb848889626ba17e710dc3b8ad214cec7d786b936119790e29909312fb8610f28deb7f69edb54a504378a5598e4b7f1327348a452d6b76da4b7a8e90dfd9dea445ae25f110223b64a97174a17da47179e01861bdb6ed9177e18a5c73f82802bd3faef09fbc6b94adcc00189617f3a068a7d25e7c6ac066402585066d67e693712e049cf96e82dc30f0f9971c78d872700de4410594790dab8399c03d7407b63f94cb3026844038f46c06fd10e54286cf5b4110acbf83f7f8ef033b2f2547faf2d331ea9e77702e71d3e8cecddf148a5a492f5584b5c2889666fa29f815c90045fa57de148ceb8bebd54e994eac00364c881a20b1854be4eaaac7d819df6b0ab117ad054da003f3a637ac790c6bf17cae08e363c580da52ff7580eb4c0462d8d36b2f8f2636fdd23b5457b37f2aab393e7d72ea5f1d881e6867304c1e1ac06dfbf5f002427ebf9df3639f8744b72ece811e3c869f07567051bc8041c5380e94f30300ace17cec8bd3034c270fa9ff54f0f1707fd607897b09f159c685d97427123327b8da5cd25b2f0773985b3121e14babdc2d1364ce1f960c00119d6de2c2e05ac6313289b000472b2fadd898f4b59dc1bc46abea4b994f0963d55b4a60db298688271df115533a3a80021e2de8e1108700794d23ca610fc0459760d4ac359f626f2a9d6b7f64d59366815907016d5353eb67ed3a07c1762066bcb8751f26177af193910e9ae85156f2f024dc8025a588e0d3b631a36f0d9804a76ac7880283cbed449740247f958df590120a62706d9e3be3a32ee5600c8f205c25ddc8b8102e2abbf1f8703aa99207aff03702bdf5ba3eda738bf16ab60049a9b750cd1b98400d318619451d28b5ebcd212cfda41e7b740cbf277448ebf7a52fa440062a54bc8a356b002e49574fbd928d470f5b1b7d9cb3aec6d8326fe64d085576c32103828a5d223a8407172b67bf3f0176bf126f767fb4140b95ca15e78401c4ddbf1406db3083367bd6fdb060bfb268ab91338ef4ad842c2e797549e4e95105fa62ef2230a6229baafc64c135801743b1fdf64c2703bca467bacdae03d805d93b2d607dbd3d3ffe06396a32bed86e84bb06cca07afbbf434093853aa50e88419f93cf0ab5dc7ee7f6254f0d80d439666b16d0afeb3f40a19f8a9ab7563ae842ee7d1e1ed0ff13dc9912332f4373435219dffa6e0c378ee54b9584bb16b70a16a753c6e05ff24d01177a2a84777ff5bcd346e1a3a289395fef86369e662832b9980c9804cc5f1719fef52ff6eff5c446025efc3693d127eb1ee6f055d111a20cab233f43a0fa87cb88bb5971708e27d3a7176d2cc44a07ad2f9d86a94f9b0a20de214ef4d1f630c9e1b731ee7639f6d9f53e3ef714880bbb50d59be00779baea1530d48dd8a3f161a5b0461caa3cc02f9585bd0b69009aca34bf8f9f8c349a2e8d74803fc7d139254023839fb1deead10822a95a919f4c1a41de18f29208d0768bc39678a96bc89871c64a262546283b79032c84ea0d879389960ad13dbe138c86897c0364623b356bed52fecf23df6823177dd5b000b57f53aff88e00b4b261a8ef8425621a9e489340e80a23255f9681d4f6284ff24d1cf962c9236203a72adb0745e825448bb7ebf7d62699ce3861b43866aaff90d058a1bd14f3750130426c89152c74aae0ba2e49b8a3d79c7d318beb234321b2b5008068b191596a24b32ec7385fe9147e8434b7009c0826b2231e2a1404e1c8590bf8254f47fe1f506a18b6e5ca912808759883de3bc51cc8211b982d5f78e0c117fa4d80f08664404fd1df6b840d41298ab0fdb807c3d8d592a3a111a74ea9a96e4ef5a9956c82bc95c94604c332db1d810f7b2f36a278bc89f052d705513c9ba052d6d07393a782274776a79df730ce804b4069bf174341529b25820dac0a27e919055bb6b7af74363964508d3769f9e49dbd90d42184a679d7899c2f9c7eb0a1bb62df46f995f1cdccdbb5f8beaed780d5a03e9ba618b0554d2c9f06f3b400367400bd46f1dd951b90d64b7ad970a76cef21c8381eec4abaa2557c64eccfe012e68944044109443eae364c720652281170157103e4aad40fa62ee0d8d83fa03299b915a3cc47c12f30a9e0ea27c992234773b0e77381bbaef066c31c789f3cedda8bcb85d1ccfae635aa8536ae0c41081c8708e848d6560b577e8190d7be124c43be19a70a39a15f5012b35cefd1d786acee1ba6fbb15a83a05649d2b66470455377f3e24d10bfea9def41492352f8de019f070f5013e9dbd561cee1c1f672b1778825922d50425003c9b73d078416f7e9dc53afd820d93aca071d16dbe47b410134636352faa14ab5251752afba521af55348fe48cd4f0c7670704ced4175e001cf1256c7c348038a0a54bed0103ea16fb0a1010547185d66f53a2a760b8faddee8245e9896dca9080dc949dea0c9f578f43c508e22e557cba25968178a4fa89a020b989d0b8c1e8988e08e8334858e03f7c768c875a008dc1e159e18649d2a9acf83d73b6375e86a53109dfdb6ad023847cad77f6d594f8b306c2e5891dd506c958d23e57a7b3d36b4cd38fb1d4a69c6d205b79cc4d9093a0c1d9cea3a0cf20c87b0647b020debd423cd4fcad782d3afeaf36999adf8865be45514fe4af006bee5ccd26f6470afbaeb977bc37976716855d5e317e96c4e9a42e0ea292f45fb141d00308c708bc7b1e7a8da9ed6e58e9f799c1391b70ba140c23fd59eb0be547ff998343598167a5d7a984569a6ca25a455f71a27b7149fc539ee791060b37a61539a0aead5ec5a284c2540b6cf173eadefbea40202959b804e3d725a21273052105608fe772fb5d1c4d40e865db1d72eb0fe578ff5325011b66a67048eb3e4627724bb961afe010dc39bbf1a89eb41f286e1d7bbb6a418129cc41b560a01478468303fd0123849b5935c591c1648632a098dd1818e880439c9e54dbad95c127913a375749691fc49454b3d9aa34992c9f8899c816c1cee114abc0d22c846b2562e4e03f6fd347109219520f1349786838855fcc592dc57be637c815e0486f806ce67dc5d5c70d314f577549e4de8bb9771328ad9f8a8e614de4849c0242519315b4de94295d83958da82ead4f39ee063bcd6c4acb828e11de7408e71c0c03f4d68870ff3d9f85032065158e3a8408811417e3ae20f6ca933559dd3b9ab1bcb5bbb83b81df00263ebfb0b5d29241bd32ce9b74f72985f032e59841b25b8a7a1c0e0faecd08711dc0033de1d28814426c3968f36cfb4a11c5f2a656e7f7c79f2015308eeb916035bc5a33de0909e301140390470643ce275c40c0d2c226bbad08b8c0df8a8937fcf03089a95aecc0279a2f5d2aaf3e5c7ca21e3bab20b413e0a74552973212083708423b79a90b02a697b44b5d96c0a92a4ff2c423d032c74c4fe2314e46786b03d520ceb4227fb1910d9510864383c2cc5169a0db874de643985dc9be30cd9fe6756c21ba351994abd152e802ac55e07d9a15c64884528a6a57e5688e2ec1918926c8670af155e611f199d26d85030cd1c530dc6c057cf855fd3acb429f943c9bf4ea458dba5792442b666f3196bd38c3e2110c72b19d16e4c22671dcbdd9bd98cd5b54aa560cc14a8e4e8b915c5edefdae963a546275268fd50a19628ca8f4102cf60e79cb4183d6001d083e976ecd3348955c358346b06fd9b571b210f757bc89374e8b20fb37dfeb00955063bdb1c7b205fe560b67a67f135fc849644ee8bc767f0d9c88058239641ebc26c2050a793a6ba7dab40370b428ceff451d0895e6533c791882f9362957ce83191483b4df352e2519e9afbf3231213d8b68c341d11263cf73e417db176237e6a46bb0d8946c6d7b4c0309d4a2432796e1e43e7813e00d6fb2952b9713dd91028a821794733ca01f04b5d459fee94204b2c3f7f972708ea2244b9bd211ac1488275a2d370c780e3e41c0299db388d07476ca9f6ac308ba6f9d7a369ea5c02f3e28d46125085364e1862a20e82da591831c9343f1fcdc2e7685f21602691fffe6e3b0491258e5f931b212128fdc346d3c1d461293c3980e31e4b0c9d8c62675490d1d956a78dd05718580c3424267d5420ca63f62441cfcc69379a79066c53053747b936e1d19cb3ada8f1a47cc453a63d28e670b1d507532d497d968b441d19766641af17fb5d82fe437774bc2ff62e551f2917e307961306f89ba5bd2a474c3cf7eb04f1e4b6e8fa073af4e7e5e7d90a666687bbbd8b61d26eb46b7e1f10e3b4c58527081c162e3346d5c56b16d9232742ca116763e75a249f2505b72fe99bea1e3d5c39c247b6a4b0801ab24f4ce6652abeb7b0ed2e06526d2d2272c70e388766a89ba8c83e3b65859b551bfbcd8af177c62020ab8195b5f400e3aa2363acc36a04fd57e7d32e4cda7fe012ca22243e04e95bd63d3cdc99e9176ad479150d869db800e34949386d508288bcd44d863c01e513bdbfc51750e32351cb91cd483866e111b220bf65914a08aaaa2fadd9f484a22422bbbbb77bd059105a905a48f41c4bf87bfc1077c1adc1a74000779ba4e7abde5de7bef4973487a9e9c79bc4e7afdc9f9e52f243d0f09858261033f61845fa72e1f1447af8f73a45da6ba4c975148b8339923dfa67fb4fa50922e9954a9f5175d32a9b2435761da7c747e0d8306de23462677f2d36edef2d3a0bac7557295e0d725f9f4fa3a46a6a4df99dca111e18a9e5123fe4947a05f27bd644ec77f640e0c171f49396d83b1c91dfb9546f548b91ea5676b51e345d2a9a4ab3ee2a719217ff71d2af77184ee848cea50772673286ab4516e32c7766ae1a251086efd797625bb616153433323738a817931f151086efd79b9c3d7762d9712898f4270ebcfcb1dbe964602980db2b21acaf521c964080a58511c044a61d870eea40ce1af22a48fa97e2f932a3a984e1d58d1435c1ff957251c6061768217e0480243419095d6501313b7f967ae735a40d3f0c83e5134543e953f512bf415e654807ed942ea7029658c0380e2620fb22a06265e4c91c18b5b133938e92327410dfaf8bc88137de42a1da0e8e3bfb04205caa562032b7164ffaa2367ff38292a38e8e383900229415414c1b0d92c714430b4d0a30a0f1b58f65685945cc590e753c50d3aa6307ca8b0a0835d32a112c319bbda43eae41f1cc8c9e787c86a845696dca94ca40e8d999319945994d99419160a4481489d0d5417f4b30f1fd2658e1a1902c94f974f5d138acc51f894d3c44271c85a8ac92b3921e143f49b912e7f0e913af33581cc1f3d5e13c70b4b977f01a9435fb014e40efe5b0aca3b3931e14a6a1284948b174f3205903ab45667c89d6fd69f1ad4e9d76ab3f46f5d529f74fab8d63adb7595ce6e15c415438bd51343abeb56413a7dcff5b57a62e8f4770bb42058e9046747f696d9269d7e688146b34e9f1fb14b3a7d92fd29d952a9d259ea59716e713e3afd12cb6565739dbec90ef574fa2f2b180b0353e98451c5f8786d2eb0249dfecb0c461643e448a77f92c95819994aa74c0eafa4d506d9fbfdcb7e51a1a8d38fc9d1d4e9de3a7d991b347371349686a6d24993c30b13f3f5daaaae5a61355665466017109dbe4cacce489126480d4cda581b9b4aa74d0eefcbc8ec2aea5eabb5d65b2d4d15777f7d3628369bcd66b35d9c6d6c7615c17ebbd55a2bb641b1d96c36dbcd9e066f6e7615c33e6b6ec5621dfe705370381c0e87dbe088e4228abb8aa3be815cadb5e60ee3a6e070381c0e271b422693c9643252c904739a69d162d3a8124aa7514e58e8cab341b1d96c365b8771381c0e271b422693c96477b55aad62b62ac64f33352c1e55597287d4e5eb0e368dd96c369b61d72c2773326793b9582c168bc52eec769bb7799b37dcbcbd5eafd7eb25a34f6bb4466bb4466bb4263fa0b50f3e48a5fe254a8559b19391d7fa41aff3c78e85816017c7ae117685d80562d7c6ae4e1f6bdbb25d28369bcd66f3aaecc2ee900b0b2f0cbcb07d61f5c2f48575fa36531b4eee745370381c0ed751964ceee021ac6c5b59b5326d659e956559a74ff18cd995c5615776655776655776d5e9cfefa72a913a9889e791483232a28851a30b17285c7f3afd8bc24032873e0c183806aaf5c942b2b0d6eb6c2ab0408bf4442bc716e34a27cea19c31d07befbdf75e8131c638a6780207951a3cf91162120afaf2bd42b57e6f9d3dee65752eb82c10b1ad198e4801c50e285a86783af854fc830cb3258c9032031aac0aa8f4ece0c305fac2840531248722846a0092831c42d2c5f7de7befbdf7de7befbdf7de7b73ec761d2ee2e3853c7d64de01638cb15015b288b613988d80065a90c82141c207470500f7defba4881e6428024bcc85a2d78d0e433730810e970e4670a040b845654d31a2c1d9cd129a7001c105b9820a223754d144b883bd3eb84a708354cf05530bb9f75e57096e902a23b7849327a09019b1c5864e888824eba402c348c8f4865e40b0008a233a58b04fa6108522e08a807030a2090d4282803b4c9c690d51f04089a8fd48020742d050c4051a703168c143dd928b541b5346b0f2210724c5112038d41cba1ecf624a6b9d7203d2f45c35f3525c2103a94bda6a04300840875d57acfae3a4ae80e18a1c54052a2195904a4825a4125209a98454422a2195904a4825445f535145850d0b835dd00676b9462a2a5454d86c2a2af87c19ce8ac23768d5147cacac3e7fac2c0e0434a22e576803270cb6254ecf504dc1274bee7894dada0d7034edcdb99d0e389d6a6ae8b4ff54d23f9dea8dc963677571295649e1a305631071fa7482372447719a659e5e485f8237e8ad41df9d6b703cb8f69a4cdd62caa5951cb4614938289c71e733ef8aeab9779dda4ead2a083e9a28efeefc401bd79b99f39b394dcd1bb34b2f13dd3a05b2702b4ae059f12a62c5ca8a96152558a263ec1232220b21d14de101dc12bbac221e84eb1f2778140e856f1162b7c0b66459910f7bce49695193a29f6dadbd570b5017d4755dce454545547cdfa7f59618b6f8d822e45a1cd28c908a80a7021657d1c08b64e8fe715245418ad0a1a900ba89e6f941975ba7ebd1a123058d25a593c284f5a7b85d7890ea13e499e9f5ad0d1841f44f35230a15473737652ac3459733bd3eed6305684d81ae3fbffa9842fa7cfaa2d5555b7dbef55183ccca0aaaf6f2e04ecc9ef5bc4aa787bb9c79bcaf27059933dffbf44c6f906783e1c88e4695ced106c35c380279469c34e2a418a904f2945c4c2ff6e5a5d2f9526ae222a5cf37dd4c2fb6975a9f0fd38406f54989d02014081aa32f1a84ba86828a72b85b10ce7664124932935d22b2099b3d7268fa98ac3b345729c82521f43979e60c399a68a5f6d26a2fce36e74a67c65deebc6fdbbd2b9dfbd35b8321b79c573a7938e22352c9c5644da64aa7a9e46272016fbcc0bcec98933d9d2a9da793cc8c89a6a6664bd1868565c1a2d2c92266cfd3498a3433bb66da11faa8815eff06a9575465c99c89925f717dbe5c814e0fa0d0923ee1b8e3d50c2184bc291f6b3e95677dac89f5d1834eb500f531460cb44c9973525ab42a6a15c9c0aae96060b3e44780e0839b55107c82d00f828f057e9810c283680a1035e07e589c0c8fb54ca1c596811800ce8b20fee20a8e85e5752f86f8f76388ac870be12ff0f179e023e05998b051bcb0823be155701ccff97073974c7227a85274351d1114d9c3419889ac741d5151c65c5ee9c118638cf17d01e32b2e8c2556e9e02357cdc0af00d91cae34210114443820b2425474a5e7852baeaeebba9caff858bdbfeffbf49e576657ae00f56b833b79d09ee44674be813fc1380a7c6dceb7bb75d674ba42b8da9cffe6db59b1925526e03870a225fa111e11baeed701a100f28574517564a5d300f82477f28b3de0b3f035f416a524407e1bdf5711772bf6003e1669d49829fa5984c08de98282a80840d1cd4ee8febbece3f762ef3ed1eeba45164e358a74afbbdc6d2b765de76d45dcb5a73fad77b73f4b00bd7b15af157bd0bf4556aea151b68ab56fb1487dfdb5d35e6df8f54e9c32677e30d23eff5a992382b56b2dd2aef79474dbf0eb355717f3b5076ab1d6a035343825056d286bd8af461567bf913a269d8e83afeeb0f75db765215aa95dd9985ca1c124eda0b256b9ea57707db45d5e91526b9ea1827b07d9dd60af0fdc07ad5222f79313a2cbe572433d17d447de65ae868535ab944cb000d1b108e9e3ec124b0d587e506bedbd32c996755dd7656f6209c2d264ef3a5f649620d575b25886c02c447026598884b44b26392859585956598cd82008826128afc0b2c0a04b2e845625c143b562c4ca9224f9087e82a3e8acdca6152b5d7e44af4ed422d2617ab1b2854f264444888e7821ed7c6895923985d635c308c2354bfe885f16bfbe17775d061f80739639f6011ae36ee77c0abd53d855bb311883889bfaf8ba0379be4e92e996f49aa668dd1ff880f7be5a90c7e5492ea50f4be209e4b9bb2381361ad0b3c3c58eeae276d2eb6e7b600ca26eff9339f63b900777d2a77936c8f35912ca6a9047cad4484aed973c29495aa17f200fe732477e06797227bdf6b6f6ba7df03d30dfa36472e7da005d1896dcb9fffa5ea30d500d965e3fc3b0be16ad16b4c4214d4e47365b0d6d16bcbb8f59a26e5f97163784a4534aa004292550820941144065d0904996dce69c94de4063068e07314268592b015bc2bd26cc0021f5b92be172e6c4ab8fbc011228c184d7732ac344fff435381ec408a1cb3c9bcc81da3e288450967e42f23063529c4e481a8fa22e615debbac139c50bcc2b6c54148de5a38f1288c6f2a101228bc8a8080d630629b30d788d07142a38155d97341e55aae83d5535e078c269386e70efbd57f8d7250d47154370e47a7a6839608081875dd272dc1005d75dd2721ce1b41c48f413379d6a3d7749cb01d4475e93f81a45ba2f927fe435553fefe5d5013f8c51855bfb9a76b9e46a2cd2fe89d6f614d9fab5deb6c87ebd2b9d72dfd0a22c4d59097e9f20d675ca5a4710f48c02517c4e1a11bedbff6ea97431ff2bcd54f176620f59ec0144698dc5daf5873f794f32a77f9ecda0cc5ce6cc0f3fbde5e7856308ec0c9a4ad2abc5a36b3b4db19525a56e6f40884899e3d292cac88ae3482edbe0c216a155b3190557b98096238a576badd6ea806d17191826113a19e9b0810e21928e5c09e7700959263dc38bc987d61344cccfa9a7894c4f142693e9e52507119a1ea29a30870d6f4d91b6e375b3e307da0e22586c1b1b1b1b162c762441ed29d276d85aec20c22989a083910d1d6c1c1c1c9c0e3ad8014424002092d46809179e1302d853a4b120c500f614692c6891e992c683c7a94b1a8fd707341e47a442ce43c3e85167954285444a49dab2721f7dacc992247766a75ceec82e69aba21f5ad502559239e79c271a8e20b921eaa382e9d0c20b1c782ff49831f954bd56b9a1de755d772f6d55a59fb8e9445be5a005dddd1c514801a0e5c8e2020017ba0e5e48e1a52e693a62e8329716498428279f6883fd3a84bc1b1db98f450e6cc30a6b66d0343ea699205c647ef6a9491863b3e43d5160988e31c69888db2e693d547a88b629874b8b975e21e987cd89f84649bc70870d06dc41f4b24550d2368c3e222f1e12302f31ea9cf0300b52bc5c16b46ccb43062787d7a9064e0e32b0064e0e341e47641a0f5abf5dd278281180135824a49c53be9c524e29a79453ca26526e41b65a93201b50b5f65e8c3bf917e3aecbd9933fa3cbd9f33e229f96ef7d9fd65b3e18da30ac7486f2e5ef0dca1fc9971fde42f95ccaa744a82ba8283784bb050de16cf2ad9413269fd22adfce9e1442e8523ea556ce9093d25aadbd93d66aedbdd85e8cbb2e67efb3df57e9fcbaec79dfa7f5fef4de20188623301c8d3827914a2ed6c5a5d2e9523a995e5e60668c9ce268a7cb34cd1798181b1353e98c914fe5d79f9c202fb4b8607232318e91e34a897fba7c185170f9ac2e512345a1e2ec103da87cb8bce0abddeb47561d5056dbbdb5ab44ca58004dd127e12e99e00248d0e0cffd913b4055677fad559b4c80ac430c911d02c03f00eb95e8f225f8638d419714eb2df61451e1fbfa53752aad35dcc12f4615febd7dcfbbf35617b4deac7c516b076b0c608c55893e5f94a0282501c6aaa4cf261708c1aa7ba85c6badcf8caa3f57acdf89a48e71c5355b6c9339148a74413f013f66f4e8527fc2457dac414b6cf549add114b52855ad5596cce1b9dd4b04aa1c53c0c44c2d29a3ca54ad534a1954071229834e2a842c660436e4e5e3ea75d256af94555d5454a95f262e1086c15c489709208a6118f643ab94719398f1304c8794d1252153dd0f4dd59c393f9d3acaea28eb8af6ef0f957191c8541235499f1589ec806ace9c8f1d507d3976401d506601f510c37a0884c5d01536e9211359651f18490d799f131050af75949339f561e0c05d9cb8d4e40e7e0ec4813810079a363f86551d1727d245fd1f1908f7390df96a248561bd568c72a9c99cfa2a237cd42da05edf4619e5e44ef7f55f7dac3f3f21132943ca10480814bae40efefa61930f6429233fe4e4a35bac3eab2c99339339b5c758917455143e1fbfa47356538bd4e4450e023435a68081dabf1f2d4ded1f7da8d25f5004da4bff8925d438fbf7a3b7dfe847e2e8b38881d9bf9fa08d11066cfffe7b184338cd087df41b75827eefc19fa008fa3d148d08f3f77f9d268255f7a04b5a0b4dfa095a848f84f97aee30dfa3f59075ef75288e9bf43b7c92187e057f8b5e06661f3d0545004798ef8dde438dfa4792061d8d5e8fc47084da22fffd1c355e5cdf4fa38ec6fddd4ff006a51d86101cfcf1e23a18a24ed0bf51285c5cd7288ff74e1c2fae03ad0bdab0285c5c7fb9b8d3dc2fa893da60bc9ea8b5d65aeba42c9f1d9de2aa0d858bfb81871f17f7040e8c17081feb3416b324e627c94c064492596cc6dca894524ecf9b95fee852ff0fdf0e8f3ea13505b80513a0746b636e3d9d3e5e75313152628a62723445636e174f29a5b416e3d3eb48a79f65d4ab30211688184dd1d3ab3bd5783efc9aee16119890972b0621af969452ce9c4fb5d350a7ff9d70fa643b3d394939dd4e350fbc21f10431add6f35a443a7d0da3bbba62b0425e3445655a92567b35c8f3e1d7323f419d3e68a3b50a24b384a6a8cc13991a4d5912a9d249caa1cc8fccf9fa7d708378886d81ef2aea2b93c96432994cd669cd73ce392d1e72711777711777711777719dce2e7b9fb65a573af528ffe888c0ddcc3f3a7dd9dde4704744a7dfadba55b7ea56ddaa5b75ab6ed5e9fcf406491f132f8ba743ccece69c73a23e26d8cbe2e9906ff9966ff9966ff9966ff9d64b60141086bd021d749ade37c1289d7ef7d90e05c2d0e9e35df5d7ed15e8200dd3300dd3300dd3300dd330301c8d3827914aa65d45de494e4839b809464dc22dbada7cfd9b73ce69494e48393a7dca4d70474dc22de1ac16cec259380b67e12c9c85b39915692688abe432c39ae931cdc430d39a59c91cfa2fbb8aa40ee3d329a594ba1cd1292d55d129a5d95e0de3a3ad1ebdc0a0d3af5f08ee6ab5a94aa78f3d53904edf5a6badb5d6e5086badb5d696aa28c54ab152ac142bc54ab152ac1463a144364165fe6053c38e9a24f4490e6a0a686668a1cf1923fa9c28987d9233fba686e0efc94249a77f351f8537a8cd7fb4a1d2e9dbeffbbe7e3f0abf07f787aadca686d9b1e77dde8f42707ba871dafafc5a93a4d3af5dd7fd07ee0ef5853439e8f4b1875fa330ea43d1ccd0e9cf7c7393cb37b97c93cb37b97c93cb37b98cbac9757a336344a7a81b158d8f30b957a73fb3b276666567567666656756766665516365cdac7eaa0e05aad96eb85c5dfd74fa32adaa435d2f0a8bc9b46432ad994ceb47a6d5e99f6af36f19c767eeb47a56aa55ebf44faff9b12ce3b3d3cf03a5e6e9d5e9c7dce6cb5c5e71d6a9f5aed4add38f89cd9fe51f0e74aafd0d265773e6fc74fa4fa560722693b5a8fa7d6c85209c57bc75c38ed7097200228407ff818fdf02a7b188d05843d05831599b3116b555d9685b34227cddde467be3000b7c6d0dc70edce8cfcb361c3df0243e0ac11b8e1f1071388200f5372c6c6a6866ba700c410b540ec71550f68613ceef2bcc697f48b7c9e9a886360b8e38230c2f29d1923b2c847499dcb171d1426d72a7067cca923b342f4362726766f426c81d1912511f4b4af4fa2c44db6221dab7114d2525ba8b905edf46b45f23be08e1fb45fb3422ccc7b868197513add7a711edc7986861f832e2e9675e8680a397d9b18d5db9c32edd4fa20932677e8c386332673e8c485f446a12ab4ce6cc27d5b7a26e42af648e6bc75cbada8eb9945ca37632da284646da63a450c63a449fff891eedb06bc7482eb993776cc77a65c09db4da6d456b77eeb2175a94b5303573dba025ce7867b32aebdba6ee33245dbeb53ffa7c91743715f9a8db540ca908ee6ea3d432a577e6b568f5fafa3e38e78320ea36350976a3c6d9b792db8d702cfe237368e0230cab57f0eb6b50bcf93e0d0a8a5db701aa99993640bd7e99dca8240ed7f6247443230040100033180000100a45722048821ccae298e20314000d54823c4e62442a974643a1389ea4208601210622c800028031c0188310537a5200e05270fa7e8e80c2677fbec62ebe59675e27d2c9060ee777b6f49d655d5a2cd80a322181af14d483c53151183a276e9abe88cea310c155a6098925c60217b470c0b446f60be0af1c1ead5aa7a972c434bceacb302993df01b483b915f4048f032d9db42cbf18280b2ff8d2cb460ee7f7835fbd1c6454f2c58ee1755bf328fa91f5746a5abb5d740121c18926fdd4a006d28d17c0eec4a02244658db1eaf73e2fd2b5d80a018c39f990957e649db5b676c5c981f5d1b00f756356cbfaf2d2ff3f42ba53c57a9a29889876e58a3b7a9a06fb08e077e8664fdba7f648ec12ac309a6d23207ee548f9b74ab93620c987df5e66fd637eb6819e69db20be571ed92b15baa62e511f627b37a314076cca2db00185901192a8faa347c7186ec8fe8f15be1b189c8c6b766abd79f95c4ad81a9c721e0a6aff6739c1f2d55ff5aee404a470d6f6f3212079655ae6afa39bc574e14abfedbce12fba7d0726f4c68ee6ed3c2ab55a91d1050f567cf21f3042dc4fd30c612d3a49330fbc61b5fd3ebb6a7d7c4ec0f70e247c646fe81ddafd3a463d6194b178bd417a2fefe359000ffd1f8c054cb4205a7919995c2a897e569836746d2979738915be6c4835cffe63761b3618dc3893e199703c96027e80ae0dd0eeb30683ab199cd124d358feaee0f19cd238b5c4b4b18e8958aef1db0f5fc3600fbee39e046e983a8daa5996661428aeb5c2b9b162c9a30d8bc8200f03f0e25abf6799ebb4707ff77ee2ca3af143e944daf4ef5783a2d35a7ed3f1355a1ed0a34fd5723d7c0e2dd8503506b64894b25903c25a9ac0604351c400108aaf40343307e594b8c5153527146dd9c09fb8331fe91f1ffa3b37519f75f330f5a4716bffa9eb6b571c55d06cf94151d8e73424c26796659caffbe0c97182e6742bf874c4a21fcab206063f42547ad86d6c48c2d986c741680555185fa94a3d068095ef5a221f116905a006b58d37686e066a8105c03e6b349594aff96e00cd2947b9fe9d26aa0993e4c902d0ce40a7361d5f88ef3e8c572ad89877326db663cdecb52e5b2dbf5b8bc7af895cf1309a8b66360068cc8c7220bb12271090106c1896a084ea5784703a2bfba4be658b8d4ec637354c8cb40a0a3b352cea3da2087884eed210783ed16a391161c5f11d3bbbd12ebf3b0ae0034b05376391dd4082a1711792f9099fac7131e1d449d21645a5caa5483fd45b06c827cee0009096f7767cab145a6777f2b59d426ff76e038df045eb7a0407372e8e5108aa5e7215fe4d6979ff67bdd8877e010a85b366b69e8d61254708f6d39bbd189f5f1c4c85836ac57384131c43f4e54e2e198490d85567ebd022cb1622a70312e059f7285aa2bd33132e3ac1b9e431286813893f6819721c9e4810966f0f00b57180ad39e673c0422f1d44f5a35cce9da9a439e43155a0916c5225047eb971897d4df1d5e97f1f668fdc6f8aafcef6db6f608c6998413202f24724852b9772816a17e90c44762083861f120a71e9095490c03da1d9199b428d79c7a5e939c4822248eea4896bb229dcca0700d030173b1aff33f6b779d903ef820d1aa07eba872dd1d5726e00a420bafdf7bfe21c8c7d7a326054b1bff78af3508c69ca0240cdffdf0be4e3301324ca7fd91f7549b06d4262e299e039c23712581b1be92596ff91addd88cecd6c932e5798ef6c54b320aef2e381e695360631857c121bbd0c6fb616857e1417a5e09822c8029d23f3f78273d53c325f13a7abad283287f5e8fef739c39351c564515de9e5f12c1d94a12ceb1b1117808f09c4c7c75a50e3b26c329a3ffbc343cda84f5ce0cc90f65197046f12c947dacdb4ff1eaf3d5980ea0a68d6376062b13394f870e1489c58b3039016dbfc75739a285cecf71d682c1f82b220649d88b743d4a9ce6ed46ea41fe441a6b23199f4ddbbea2fb76fe45164880a05f42e380fc49de82191255b58a8cdfa5892959db13c9db5a020ab8bd2fba5dcec90ce7bc7fae3a1b6837abc6134e73b16178ff41dc9149d4de385f5835bca2f1052476706796893ae3ce0e3a5624974e51e60db91c0a4ed97f596beb188b15732310bb709e0842eaa3c468ad93cf328fc5e8f162cd124e7e736a88867384461b11adcf72a3ec76e09a385c0100fa80f02395d6dc6dbe9c2b7cdc0a5cec0efcfc0afd0c02f47239a465f5b2f8d172e2d1cc5afc358b8fde10a14ffc4cc994b33eb6d9782a8b770693586150b60c715b32be1a8cea4d6ba05c418fd50af16cbd50b38beabd6c66bb85f061a9739237bb6affea971b7f99f46ab714a233597343a331e0dd85cd1e8cd101aa06aa0d975f13c930bcdeb339ae27c221792ef2e33fdb924f9eef21ff73e98693efe5e6f1aef6f1f2cf0fdef7bdb781efbc73fe43428f32cd1ed2457108ce02a7eaa01da9c2de3c573138637b74dc622588a95e2355b649df0e06dc2d6463cd8206c43957d2063b09c910884818f3b30b9d48369dbc018153f1f8b849a1a2360d8398fa0c5f2293a2a9c49a4ff5e0356811828af26f1a1c2288575aa71aa973a9e688255a6928cb6c3fe2b86c1af6e1b5d8a00a39b02c0cd1700b0c6a28c9aacd7fb7a1bfe7fbb58067de9051835ed9bf310e90662cd118893f2526baddcaf942a9e967884b8b71d53460afcb871bd7baecf14ed30f3191d1724cdc840edf0851c09853c1383b57eb6c00981aa6f62f5c99ba00fec83dc0fbaa97c2c932a870b385f819e09b00ad3c1d25d78c006abf70b16861c0decaf73f01c04bb06fdcdfb33a280de04a506a038ee1853a66c89b3e990c088ff53ec58461455edbc96380e29ce74f1ec1b2e9869e36c5d56141af1bfa407e1aa3f14270a90ee6c9ca27eff252c292171faaf302930c031ffdbeed2b49633be8a41701ee1314770c089b5fa2374d8c9692e083b27eff208018edfc3854051c3d6998b12462376203fe8edd121b767f561681ad7f4c4090de88c24c25994dc0edd9e53edc48de104efce122dcecc00907a32cccddfc2edcfeba92157e42a44fa304eaeea01261747fcacb40d0e42b328ece91e1a760048adfff5160db0449a3ebaa1554c39a63ef03d1355b25b786c91f5c2446ff556a033a3793ff64c03cf0fc870cc1ab3d00ac8acb836693206e110b71e2566fe53e8aad1646a1b411aafd7ec9db776c13e54e0225c2f2018f504e658bdf609cb4c83473269825efe402b62f2690c7effbdc8ddccc7c48dc825892c7e5b3a51bdce92a3780986455fdecd0df37a3fef9c301875de23c9db84b908bf51503477c20f394332fdd01048160c57bf7029cd22c40e6c1f01cef3fbff41f9c2cf9b973a5d085c5213990c4be4b3c2755ed9a076f11061929cdcdcac8f50908302d20ffd45c85961aabbe4673f82bd87e517de8f3035a017218f8d2179f6084ef12fa52121e7859364d4a98df8c1361fe8383558f61034861177b7977993115b7c04f883554d411972fcbde89eae165158203c7ea201cbe8ae2453f250ad30eded01e01902111f58ace513a875f4e1cd0eb6f91d2c30cb0deb1e374e9687eb0b0f83a3490949039744f3f60390c76ac26acb190b1b295cf220f7099eae56200ea95b72eae3085ae27e8c8d7f0b05bca2da86550d7fc8807afea00aaad9d7d1f6964a146dabb0fe7d1eacc90d7d6bcb7001e7628346d39677343b485e29c84f10098fc7937df8a27a4c013d00d7e0b033de1458f197fe3cdafc214a066d84d0e2c6c7cd2fd4cbd81c7e818159b17ff4d484c4171ce7aacb5a33a08cac5f4d674b6bf1b8a081825c4af590e24aa2df4917a3cb5634288fa904fd6a2c0b40b1556a75749457fdebbf90a0b25899a2538af0d76486f09b812b89972d077c4d263230418f40d9628962bad6c83c237f65fe330fb3f6352bea3e9189d638891c2391add22a78a5378bb9a0ed084bbe3ad91a402969a5e29ec5aa173054c37ab01d945a9d30c5a6506bf47623977f003d22e2b7a3c64ec0c6e3353366107659c962c0be82ca998500374c5eb4f48fa226585810a7373738cc6ba8528142af23c702d77029018ca2010ebbcf4be8db0886705c40d06ee340b02cc8d4cf9eaef81890202dc0d263952c3d467242ffc758931870ec74285bae146ca585dc74b08c2b907c20811f6c94dc6bf8b8d831f34d7a07ac2a82c90ac5ac44502d43a2b26bc4b79272b63fbc6a89a04b24abb6227a1bfbd8253e734f234920107449b7df7bc7b28c548dd886ce503998c892e0ee61adff93bbc8fb830774d93dd5eebd19e44c0f729991b4c0a3ee171e912a036895fdd572d6f68957d1c17b7f17ad56e52774fe257fd7f19379643294340454685abc14d7e77e00a3529cf248132be184fbf5ab1dcd7e12519e0ed5d460bc370af095b62e802a4854ebb3ddd3884b87f9eb96f2a1f31cfa15d682ccdf54f5f79aaef81402f28344ec8b1f5cfa6de6456be8c4afa28afa64949e0e329af46173a52eb3cf6f54c48c867a99e4041ee10866b6f543b6e90c5c914d694c3547a72ab13552c6d76878954dedaf147d2efb0022e7e9b32de3d57d1725594a2562d69a18c74c52f0fbb701ce03b400f111061faf0584929db9fb1e2f4f56822d050d6d591f94e799b79128a4637c925d8b0821950a23124ea4bf876304a9b602d09fb50735928bbe769ec1b33418d3cea80dbacd3a7b9438ba3b5895de09cd3fb97a8cc8ba3f7b3647e38d7f8f45bca24017ea9a46a4b5b3dcc423c42f961cb607034544a935a98f801f2a712b400ea3cb8f0d0e0fccd4e874e4688931225962dfd637d7c8cb6cbacc2476ae4ba73d3a13e00bb283795778d7b35da275f1cf4d6a73a21ac452d9ba48e2da8cbfc7379f4283bb02e5224bd4cd71284c43a9eddaf894a96aac4ad0a29dac503c1f70994e2594fa2e1853873938486332370c88291607e0c3422d1b95d117ce933ae32c7133c42e753f77e4e77ea5396a172dbaa4ec840bb2a042e62ea4e99925f9462c23515acddb481b5a8b9194cdfba16d8db2455992e7b694e60ce1a52b277c6402b3a15746ade52ef0c3a31a1ab6b0ed1a9600deb8973dd3901f59c1a259e20031f0f49cc00c8230f127a4e3857cdb9b5583b4d5aa56e0ce96b1ad81f479ece93ebcd0b2015d57ef7007b49daa55119433e03ba1b77cb9580c2bf3c0d32adef9430d4062ba5c18ba60f6440633c11059682369d1a18c0d7a8760f2979975ee44f00fd92642d5e6f8e94ce0f6ef7abc97decf2a4e207046bf815770a725fd204caf1ec6741f71a2782eb8029aff7ffabf1020fac1726b778622c9ebb7cbdb3fbf2a9ca3cbf2d06d8fd5e314cc9d9e4500276dcb1009645830d4cc494235bdbed99ab5e959ead620924f3ebf02ba95e0a6cf9f77885e7fcb42b926ef91f9769c9245f98317f0770033408ad75d8c3ebb14a83a33546774fd0c511cfc352837287931d68f6b510d8cbc5c37ddcce7445c66e2b3fe09f3c85de509eb811728051ea5cbfe7a46e64808db20d24f0ee070e511fb660c41d4df95914f5c5086b1a4beead8cf682caee7245f30725b08f133a29b7ce89e9427812499e9e676b00cad5359a6266522d29807df87817237a722f309b4f478841ce682245ea6b36243512e47380fc437c59b6bac530b7b215e4190f63a434629637b0ca57ce0fd14899d10a7aa196c13d2aa7a9dcabbde602454669cf1086723d7841a3195cc4e35c261c0f619cd032fbe5c0f61c3eb37c677fadf7e46db61d3f4d87b20441d5d6db8db82ac432f7430a212c4bf1a8e3563173d867f389cba63a41530033f6b8046fe8b15236c63bada713b83d3b6a7e75e0fdd7a6afb217caf66ff39acb4f30087690bcfb676af68506bc7c0060f32ef43e849980bc8721b4238cc80e947374b4f49c6d703798d3f38cc46b0a53a45cc7da06706155c53c64b5fd0e08e3e85ca3838ef984adbeaeefb83907dfcd004e214650d44bf02726dcc69c5439e3ae2063e86ca8f4d1c88539035e8c58e2f73f20d4eae11b0d914b4f9eb53a5d59f872d08a240ce5ce39a51d2b677de4e1dff1f34563426d6378f550b5d3f4363978fbaf042b17107c531e4149d21777722405d4fe3763328b6c1c6d661099913312bd4817da3ffa58b0db09553df6021cdc385c1149104d8db5735664930e227960f47c42ab91e624c90ab94361a7aa6ab876a902cb3c7fbca79f835f0b9b0d0839ab33a76c386d5f2b3d596d01e145bdbe5c2814c0ef7bdf3f2fbb95787ccf8de2cdb3fad189ee16b7b94a4f42218ea3c4cc50f843df01a37e664de5573044199afcf6e51113c70f5224604e9118e5e553845fa08222ee793fd90610b978cb66621bcc266339252d426abc753ee185dba7494d2140d2c9ba3dea184d991ff3f4b99e02cc0201d71022a0c0bf613d260980400e7963e92c7955de9fe09fcc3c47a7fa9d5e5e9386ae776ce90a19ff5d13074ed40361f36af3f1eee88fcdee02d393fd73a17c6b7340ba6219e341d7522080e767bd09c26ac3ca67d37b6d05b40264dc047932d710072691e778d55538e0d0f0438348da347649edcb6093c8ac6581867680d958d564a11ffd05343edc85fe72ad62e92f5e9c38de1f939705ad8dbfc43b22eb259d0e423d5325a761635e8e058a7673c9129da4d40b971609fbcc09028f322b6461ec925a8a19f385f221b5d37479b394eaa4d6814745f156e6737fd32373338a263d9ccea1b2dc9916601a0a36ab2c1bc4dca45fb3420b53418066326ae879a822815fddeb5bc8b6961a58f4ae7b98fe30476497903390d4dd8743efa440227653b61a5187d23c8cfb849e1f9d1fc9bf710f86c04f89b6f293e339a2ff326851323f9356f53787eb457b5db7b6502fea06650eb269b0feab7809745278d30ac8188ce3067d87296b0da48788f1812457ebc6d0fb64ae54be18d7bcd0c9b30fbc6bd016747f86dde9bf8e408eb366d97f0e9687e0d36133b1fcddf6c37e1f911fe1ab7263a3fea3acd9b257e3a42a63260ec1a02367ef5b394b6b98559308c93877ebf7b196c48f6422d1f70eddf426ee7f73217c1788a53fe890231f4e247698589711c8b09aaf1dbf411bd2bd84063c6c41367a4d0a8e1404e4cf1a27115344d6f8bea47668d50cf7634cdad52bbeadc950a59bb79a67f0974281f393a880f8a5ab2bf29cb0195864512bf619b05946c86059a405de35a8eab4cf13f5bfc689e5bdcdfbec5ed447a8d859b178fbbad5e57c17cb13b343a68e28d787fc3b181bcee88e3de501c46be487f32db0d4ddf01af8206d23ae9b6d590251ad8426ee4ca0689c5a174ece3e01af22a483d4251bd4a1596e932643ac510553aefab9e6d261a58f50e8f8388576589b9dad35e887c5e55ef8bdad5675778adbbc35d3c43481d3eeaf4a3f0bbabee9b4622a6708bf23d3f9d12e4be2d39327e7e6c44cd9a174ed154db57357a5b07a1e1252628dc5c8a9ced72827b645f8e9f5d1da4c5254c0794003fd81d0057052aac5a5a8d0a70be94d15c379fc30ba02abc9592e60b371f96395cab82de984194ea79e646f5d955eb1854de2f52690e219ca1dea377298058de9969b6e0d276c9c095c3599571cd0a5fa7e666b3b62e10c7832d04d93df8d03663ef241376c1a1543298298773cc63e5056de40ac1710c22b9a41674266817e19a344872857af6eac0398e197bd4db3e315c5619c4076ae5237c9f190c4349fe1e97c57a45bf34b9139c6a366236d2f8e7b1108b4c2ddc82b83a5197bd48828eb7d36edc96a9e22d2b8c1262c6e635224254412a82694a47c8e310942a8c913dc6ed421eac6e34336c3a18c111d51437123345ac7a9f2a6b70d2e896226a2b2268703335579eed396935d7c8ad5cc969ab4a540aa0182216d41a0b9171640e4173db8c87b191d8758ac1a91eb2663b9a643ec6bc9411f33d083415f03b3ed9967e55b94edde2319f580e831b5825449c42dbc3991882b20f79b81ffe545116338b064cd1ffeadc022a26354f75d0687ae530cb86ee49dfc3fb54898d55bb20e007d44be5f9cae43ab7db2f65ba3b8fc7334c3314c4801a7a02ca75592c602207d4387c8f7e8c6e98bc8ab0a17fde046c0022fe21baf48744e074b2a749e8dc27538270cc79a9a098050a56284399e597e735dc11e02bda1c2800242edfe5bcae1552636885ca095af48c1c617d9c4772398c0afd2f315d690cb6d21288a48c0407ca485ec284ccf69dad973098fc72f314b3738637cf0770972c0bd53fa88c178c229c0a805c32f8c95cf0f11405c11ab6d1f4f83e711878dceb5c9af4f9f5a41c88e74d162ce3e98d0bfac5884d1941d72cc1f0e8dfe247a93679f0939e6a11a1f6b2cdbe7660032407137397209a0ad2147171dab3fa012a7107f9de6b07c031cc663c0caabfa337ced3498580b4603afdf71bf3f70c048236f981d85ee9d383a449e6887f76c52f92ab7c30ffe9aa8727129b9b1e2ea46b8b410b6d0dc8b2e9870c98f1c860ed5cb753ad71f9a81d546effc9c3a549670f0e95b63280273db04c6c1a00f39251126479c90385462374c70e32aeb3a0d6e021d444208379c21c0c1ca5af16cad828c0b1afe6e3fac92991b7da667689df78687ee1fe869cb2566bb4d36f5498f5f98921ea76b1c6e232e87a6ba5c761e8b658e47095fa3e82e1ae4a764b4eb99b3bf2b6b326c3e9c946dd8e75d129f231baf965a8c6988784c54f647a1744b409c174dfdd13203df429173636143ec96d60e4d582611734da1de7472368709516357c9bb3c8c0d77f405458ed6ce5c5ff2f91e1810a55f6534853f1973d00bba6bc70d5759959dcdd40686a84e38bb2d7a85b3f316b8e96e1a92ca76d5815abbe2bb7c92ce65356830ac69383358e3fc9f4278a326416bdf0c7d623085e8361d29f3df274bb1e694e05b0b58547f78cc573010fcd0fe14732280cf525138c3bafc1327b081c5abae6c61059c2761aaba30ccbe355cc8cce21937998056ed2221ee3dea395ee1d297c692338cc1b8185af9b8647205b46665524ee2ce0e04ca32997e1e7b45c1663a0cf4bb716cb39d6d162466f045b6450272de81136a29e653d5d92e597d0884ebb18a48402a4427fa7c82d09767ef42e1ab893db00015340af28ca17c40ad047ef549a6087053811cb5f709426545277efaee8e3c1e5c750f00b12ea9e0b8e953633511523965e98ec4390839fe0f4d856f95028223948e68e1d0a0988f3391e451b6e72dffc698692c6b86b219333d615d985e58cb3c99b1d438fad26b81f0967942bf466efd37a30150fb908bb0aa8ab6b9fc63dfc0bc66e4b9b3fd88bb5c9224efb6aabfcf63ba9a10356559e5c0c82c595404e475a767ae7df76a5b39ae0f6d8a721b3f0bde1ddbdf11867034ba415cbd7c140d61e54c04b11c1ed8abb4930b092c228900ecc43dad0c984e385a708fd316af6b193c797ab9200b88fd3ed906857282ee16768707f49ad68b698b37afa56e8533c399771a97f8a3d9f28b580317586d5ed1a991d29dd56506b69f0e8554950236a2989e97596149d5d833468b364bad02b47fcdce20d73d82cc93bf1441e0e7ef008980f2cf051421e082773584735650714ce910e8dcedfdea858438d2c4003b323362bd360a8371a889dde3996c2ff233d1c1838dc7f6fa8fda393f1cf15498bcd74409371df30bb2e706bd860d755391c44a98ceff2ee21e690553006d810b2bfec68e80c52e09b6dfc46bb9f80cbf09bad22434d7c6fbdedbabcfce95d91036374218301b2c07f9cf3150a97f364c1a2ba3ef7daabb0338181ee681d2c0f3055c15ee45d8a25fc2051daf65fb00037eeb2b9be4ab68803c7716e9a47c3601d02bb67661e0c3961454f8d37e9650c6b2b4c68c0e3a8b9a2ffd8e6b40a04c3d0a3661121f0e6c438867ea834089b6ef49d70d45aa984203c35c84f48020077ba56ca048a829150d3bf160915cad7fe67c79356481393313bc82b88bcc11552bc3446689927f6a6801710112a6bd91cee9af927bc89863b7ec7b619457d65db2e3a31b0a63b3b4be715cce72a8b4b18bc34869f5dac1ecb2e04942bbf48db9d3387c71adbfb08b243dde20b524618850d0a9d17a32eb3af16b76215ffb72d92b10fc2630346a6eba116071f6e9ce600347f1d045f7e7a8b0c1073f6a346868823bf0e491d3c07dfdbe68c380fd9026119850e3f876d04495ba325c586e3d359fbf5aa34ee71b2ba2fde80cd3498270c7a1dac554638931f332778d240ddda4ad16c9b1f96671f22fbc8235e1313d056eaec5368ea1ec0783e867a17705cdaf35b79515a92a693a28976f0191f17d709e0ecfc761e439ceafc57658ddaf7ce58539d479f48a6d23a5e77a671d9cb0d2dc2b05599d152b2f72d1c7ac35e678b07afc2127aa35c512cb840e7ae127eadaeac5ad4a9554816390ac37d40ed790d61765c12a80ae1ee3f62291174d251a10232f566beb6fc7dcb481f8862c5b080384a31948fbeda400ff8583dfa54b887d1706d871ee552b29029c30e09efb42bfd2871104e545be2362693e64a4e8f8a07f5166d566b688ef12db3a81559ad12122d6b1ccdf7338ae056a9a90956edcd017d73048a8a1b64b069c7e1bf3f3695119e0e8d0623a7007bc539909d70b2edb4fc93e4ad5776bce1e74bfc5dbdd90dd599ff7d76bdb5a7b728e049da33f4191e88f6a3604cf9c2b0c83a6e5e4a026d97983e8b285d0d44c999a5d22a81d2de5b0fbda4c19d934e0c9925efba567bf4a2d461f08e9efb89140a9d9e38e0578b817b82f6c772d1cb0faec87d7248d2f7fad843e4ca194115ba8aea328a46ccab7270385793f3a63475e82aa1ebea6c966b127173e4d0e0566f8059cd78ada63247d9e1f5c2a8d806405c24f21b505504a5e89f4f8b2529c16eb31acc68a944e92e4366353d14643bf300b191537445183000d2050c7db6d9ba9be181c4311813d03c236af9f890e7bd41f2a7280e1ae5e1b2f025798bd66e86d26ab91a375a5cbf17ed8adc575db3fbb15bf728ca10f451652d6628dde5681c7dc54530efa3264d0b028ec0a34ec5c0bf54fec73c3fae13d8a0b93964b4a5bb6bdbef548d2a2f4f1e41ae9bd1282baf829e60889b83aca3213b2f0a61b6fe86be87f53f788219be715e7905c014ebc1ee4067519101b01848770991bda4527ea5d2c989af00b55b292578565aa6ba2be08a4fc827adbec51e5b78597ab6605fd64efecbce6299aa19d7177b5e07122e666cb5d2fc07a04189a1ec8093dcd5436be325904d534b3e1b5ef31b636b347864aeb6edeb124712d90af504e2e3dc3ce2fa1d085e0ad4b3c82cad8ab2164b2ce8d3bb2a853ae880eeae13bd62e54f43b6de67e7feef030ff6fe21c9c4568927ef53127e8e632002a42a99b3fecde1ac2272e9bfe0daba8d6fb98dd3ee4481cb386aa5ade7b84a93138ad2cb666b55a7a6c93daeea4b086aef010edf41153ef5fd6f167d4f7b437a0c075811f82ec02df3b77cb3b3e7ddb9d055cd4b898153090893bf578a4c89a71d9d31e61ebb3a2c54489793d4330da9e4f87645be036026990485961a4b23c388e491b9747094527812719ec47eb5c1a04a5445d94e2be444ac5b1d932de78287924f990a6d17257550032dd21062c63d3dade0ab97f4e12fb34958178c827aac2e9ba41826760ca2624f38b19f5c035de48559f3c73b1aa13ef27c3c050046c5f6a76917649d3e47372b93d3ca9bb03daac4696da74b0bfa62070e888b6aaf3917cc47a7fb01e522136f79d7bab484f5e1023408460c2c924acf78f64e6ae06150648042cc146d3864189ce7523882c0083111ef5f317aa56e0fbacfbbd6d212dce3a13e45945e482f57528d5d8a1934bce78019e33845f0c0b98f98ea06b8ad7ec4915376cca0cd08a8281c365e41a8fe293cd32cdcc126d13eac2734a567df0faa8479b74030779fb77719ce3b6730c488213ab4adb059a807c8c076ba56be0b29f6d55186135e8773e3d6161d0542ee1ef3734f9a7160a4c3292e58f80bc6a507154e1daa4b62487b18029177acb388e2a5c74970e721c8650e2b6760211ec0c8be602b6ccb01cab26fe00ce6413c44b87186cf3158c50c0ff2472a3421919d67c3329dd92e1ffbed465209df0200c0311126e18790070ed44e35f8d7f79f069e1fbf585f5cd7ffbb4a0ac75261074eaf2d4ca0e03132b511da69ecd049af5c9ebb1b22783afe1d2b4761d753f4f2d701574bf4ede3e3db0e6ffe493c135b4186637e27497ecf119fd4701047162f3e33142d64bc27e40d004b4296baa84c57650f18c833c49ec3d8b56b21064fc1251b369c27143dc174ff1cc94b4e109bd01aba1610a2280f321c931e7b6cc281fc34145df213015d5defc20a51d292a712410d5b6fd75d44f862a17fe3d0eed57b7d3967dd58fb248d800342efc150e38995f7c07416fe43ef41ba485a860b91d2753e1b6cbd671a76cbd6eb42804eec9f1212ac780803ce7cd1ec03d1374772e77e559c2d1efd4640b61fff68eb1a360af8bc8d028a070bfa080feb5b9205f0f4b85260a832a560ae181ff26cd50b584c1923720b5c425d8d21a1e0e04e43916a917f25048d2b140d6da0e74e74bcd3530c7acf707d75f4ee7cdbbb45ac8b40646779d16c8aa4b32fad7d1f748c5f623ec9641f7d835554845c011340cbdc16d6a99a683e2b4f93e828fbe0b92d7bc661b5b5512bce099afe29410582a8ea692c5aa45bb2ea6ff51cad86f4d8382be85c8442c028ca5b2285d47dc9f06f91aee102a4866b49899a234607c49f4386627d708ecfda3cde32762ab3caecc72d81791634d26c53e8fb2e025813ed3af28135207ba5b67da9f15f4a04f02133ccb3dd2143c5f10cdf44f61248551f2416b79ec5a0a79d03dbd268d6ac9dceab75e8f3c9aa550f726756eeff692dc16df757130493dbc5787b0f448315cff5fb3dac75b211e7967d6f94d596ba9f4d2842be55899019949cce4091c9153e4f451fd7be2bc41a35b3b5da9bd01da9ab17dbd081fea7329c5fb06127a507dcf790d06eb1ba9bd80f32e995238287cb353c957fe34e5e293880373d8565655d18e8ef47fa197211915cd43ca402ab115a4332723f3c00e55ec8196662c438b3618b3116ea674fce011cafd3187f64536c152675132ba954c0427a5cb20e6d382f088b13c8c39aa1da0d43aad8f100ae1b1efb9d11b5c72cc1357501c035c65a8c6ad62fa9cdb03e4816c2d80a0e39b07ef558d133215ec8d701787d54a8e9818eb8896541600859b6bebb7ee0925e6038f124546e848e1b3c1c706bad63c1299161f59958f2193ccc8334f239598db629a3476a3f34cf20e373380e6d871e12f29d76c746a0a56269da715b835b6c903a44812703a739633541e2b809c327283ef1944e2e91be6cff93138277a3a8eb5a4a272296b08e2c538eb7f6223fc85f261162ef20673dcf0e66f439b37cc77300b7c77a0f85acd3a96621137c0bd8888a823aab48327a0dd2612742732e4c7644987a048405a779c3e45c9c1fc93f4c4a84d4c926164b07e7d087ad9e6417e13961998c9d2ef612a54f620b28e96b4bc79088f4b4244426d330f0ce4c66e0f3758f517913de4820e671f7648d7730909d774276ff2250ba3db6b74150ed51b393ae91d1799adb04c9c98098d3f003e46b71d1e7c1dd21045c8351f564db58802cf40969b6c2528b7b38c4ecadf8aaa43e2cb05193f1a47c76855a8f544c42389c54df6cd9be3575a0654c27c727846adf29969a7e18da2d4503af279fda66eac08ff4a8293b42f740cda53fd063bc061706ca2e23a9432ec500fffa326d24aebdf1624d0b58ccbe6155dad4de6bdd505cfcb8a8abe3337216572b615bda9131da239540c80f047e88b20c7c3289c804a0d70b9846048b0a583af7e6349e846b95316b5c8b6e93811b803fcc5c0935593a162d39006924516cf4251a5ceb7815d19b90db71ec7ad00e1e1af711f3b458829d5e340dfaf218d9c9934cf764f7517ef53c556b2e83ed005724ce29ec43b05a055ef1e3767ce1c4c6316d84e0047d3b0abd489817e606c124b777fe7036ce1f0877e1c24e5bcae70def3a028a140615988749dfd71b0522eaae66e1a17660e362d196aba3cd2e38e589483528e0e35ee83295637752a79a2888e614c21fb4d33224d169880cf34e4d8f8c5d9da900d823d81505acfb5e7274310487b1b04f2b668c7782d5bce4ac8cb6073a21850ad74efc9f4403cd903f738bce5cf53a4c538410f332cd20141bad2c7a63a436ed00ffeadb56baebe926d9c7755e4fd0e6118ad09dbc7ef86d89ee6a525009534e09234ea184e30ed97664b828b4a11248be0b7b4e17fc1a1d116a280a0f98780b2205b9d057d8e61a7a40e484607058e8beb2be6bd59eb3843df39e22982450d0aab1d9a6b3ae619a55a1ad181ef625ef7fc6d17f1dc5c032a028a4e81be9416fb3d0e97891a75b36d7fcf16700a6bd0b516b3f47aa59d1e98c916fd66e3ba234dc8870da67cdf09f5a69cb97061dfbbdee276a6bd3686721f63c1ef3840dbee8fb742a5f1297b9e84d95b1a40a3bb87d45c6e467f226a373a1750a6e1de029f7263b67750b46e6e480dc4d75502ca053b142c8987ee4fb4dce60276606cad44973626904686a320e60c1dbb0a90a9c65ff7b565b73e37d0a9572f7399b236633e56422be904998be621058a17b54372c9f456b525b142a3ec1e64e6376f5ba2f0dec28c6eaee2d37a62741cc279402a3def544acf836feb9bf3c9656a22fd31a943b336e4a94978688224595343a64e4835ce596a6d4702609cedc5eef877aa2d85ca49790c4cc017713c2c8cda9a46d52283964ea55083066a0401dec7844bc710faa1d6494f68ff5560fff51918013bda585a2c37850f29455e4aae86d677f56fab13248eecb81f3f34d1ef08b1a18fc0ae623387f4b78afb9080a418806f0aa180cce1967a0869471d9bc08289469220586b558c533c765d15566a463e051e0c13bc72978aa8027414d40eebf458afa280e7263282f39784b79dbbfacc889d0e663117364f131138e123d024b1873dc33a65b48ea8c94a3119313f61eec03d43bdda613cab46a96e850b8bc0777c7cccd7b94f2c04779286321a8885668f61d12c50127a6a4302fec3bf2ec2972967d0f424fed0504720a56d791e6e44a0a1fb3533ce6997b627b90b0a4e848390d871e5fba07cbae02d38e19a922705a6aa99ab36d9788601126a3e0e104507aaae74ba747fbb7101c1a88e9ef00de3438f65459da316355188648257e588d79701187b030cd608b2a980d484d7ea89af9405ae2f37c7a9e9e63f2700f51c2406ee955a61ac66725795ccd302435f9e16a868312131eacaa62ff5b32c7dd69390a708c1333da325d1a8620551d38d0b9fe0debd22a0ce2bcc4d22f491cbf6a6b3bfbdad2062db60049b39dd9c62f91dd757c77d00651a90e38ed1ab2aefad080ec87b431be9b5b6d09a40680fba4133423498eb2fc530a24a1e828e7f8c3856d0c1cb07e2737fdd2ab5550a293ab374ca41d64826bc0c8b4b477276658636dadafd9a73b4cd095b3b5214706ad97e1c4d8350cdbf7acfc8dfc2d4c70906828de26d85bbd32e4a99040bfc4aea816f6222c6cd0dea0007b5a91820061d3d44bf051a3bc03c56e3ebb50846e29adcb3114b2fdb5f6a3a4000316508719091dea7b366de2e1a2cf543906da74e51c7c8c14c9a81ae1e556a24a656e2b6a3d0d4374ff511d4ec436b5a9b1251b8e53e9ac39646c3aac5fc27f6e3dbc99bcd413d556c5ba54e406565d7512679e3aebe4870431c8e6ca09653580c587afd96956e7acbcfac96778e7f183a507426ab5fc5f35f42a638a012b15ef76940410662a4fbdfa6ed81247a15849ca2d1741362db8ad900617cf63581fa173160a0c78f1ff10a856dd4affeb9b99eb66e3bd378dfe0825ab38093886d274965dcb4d94399ee899ec23e206f8723f68c38d264a069a5cc4ab6d3a8d47daa636289f03cda5477a98651bf6bf689df5c11b958350ff3761257c447b789e5c9e5dc29503890d6e7ec895ed1e6101b7c1d2043012e4c209ef01547dd4751ac3293fa75d2be32b7670c90c42e822184a4b91f2cb74564b8a86ffdb91455fe27eecf9c6ee20b435354824336072324b15e9ba6220133cd98f7bb81e09175bc3adb702908f7c3f43727e8f5e96e8eb891d292787efb0b92150d91b65453badd30d4916da3c5478d7487de8a549fee67fbd8f4f7c68fa38e2b378b7af023b4c93bc704da9901ff5eb6b9f087f7e9bba9d6e9defd5b7d72097eeb19fe6555cdbc21b4348de6b46d535ef429450555fa596f8ac4e162da4067a5e46fb071519ad062eae111cacf8840c3241fa2d218dd0e0b6432399aa35bebc815a427523b19f8be802463b9e3c1b5c0b12ce88ca8198774b3915d917c1c35f1e1cebb69d574bc928733e5efa15d77904d028923d2ce30eb3e98ed9d847e550b5189c2e26f47da8775e55bd478d5e1d919389a2a3c4458e1682971cfaf88167cb45b98d8ae0bb1d8fdabebbabade5b08705f1c20932c997497239270fd927068852c9a5541f9cea7ece8d5beb66f907f24c815a70ce90fa17e61dde439b148a4b0beb7299bc5ad31e5d2c89c526261b6285a765c97979484a421229eb423ac6d9c8406f121e0806332241aaa1490591b9d86210095d1d3058ec098985b4ded20d909f025f59187dc22a96b15d230cb950b9486e013ba82f086b8f2841c21d1540af33b05817b8f7342f089622430f20abd5052726e482f73207e9c935c4018318890a50bb86992b03a87b11bcbdafe0f74603d3f71ef46146b7df1b79bfa1f769e39d79158fc779e014d5fce8a71fc495751180d6fc3c1a315376c603301b08cd7a25305368c0c08baccf039fc322b1140b7970e944439ba4a3bd2cab682cb6e18c85c14413020860f2c8f663e87b6909703605ff65014d9e5d4c031815819285a43026bd22a219dc5fa9947e10abac4495b6bef289c32c5a03c71d657f337e079a7c381b171cdbf7f5ded08aadd9b5f11df14b7dc9fb9a532b3e7152e058df74fde3a7265ab8a04e6a5ff702af2871098220f5e4236dce5e30960cc58642a4cee43f6ed32dedaec4b698d0c8a50f6e976c017b43c041d35f37f37e736f7b8bbabcb57415127ba8c94b34b513a55c5015377ac5155d7347010c17f648414c8751d0647c5ed76862af0bd27316db494be875ff84ca881a16d0279fe82736d158342a1b0b9562d2cbde0a795d43055088d293cce97376d1a068249bb13908c391260490c1e717128e338dbfdfe89d8dde8aad40e6410d401fa6734f12c4107dae874457dc502b6ea88a1bbae28a1a10df21074533074735ee7d3d14ed7dac7d72cf9088bcc0dd16325f981d9def8bea198576324c8ee50e2bbd9403864804603846c35d8e8e00431a50624063250399be19df34a4610c992e40d8e8cdcd9e6d88a5e90006704ddea5170c30bd9889c9842e9953c4cddcd26c89444701df9b36f076536f68feb2706418b982f591d45fd200a070d38cbd7c0c9a718f611980636826730c1e06e405aead04ff6db104552e59727880bf0e59da419e0d9a9b36d9c3011b4f18e46027255c9340d857b00b18310f0f8e9ead89325f55d0b13664d28c7d26919d11fb63d8f1056e5754aea4dc9e112c9bea1f3981a4c1c2b3e5241c4d3bfb2e711228868f269f99f4e7addfb645b308a73e9e527702c423acb6aa7b2742e821bacf9262dedb7bf29e3b2f2210c52d16939c196d4261f295cabba915330dbc2ad6f1f0b2810e11e7848fc26c61e76492ca99a7cf89304543f87ea347866e6290d4f8609c7beb8abc1df2622934e3222df96b5f5ce3c4440f01b259ad798b86406d171337ac85d8da428c6f11735c89856eb2c50e363d791359782f6e60125f547a0e50ab529473c974019ccbfcc0b858ce85ca9a2582502361f9e4a08f340c5012b6e9012a037906ba2fdbd69b3f91514bf2b2338da2b4945ccc0f291557eb4f7010986a075a939aaf599800de2159c58a14e70889d7dc36e083db0bef1e132f24fbd84503e24d618507026b55e443e0438dc77ff8f23f70446f0099b5e6ef45674acd309a3b513d0ee2db26bd8ba1915e26d7f504e7884eaf2f1d04e383a473693b2823ad03e66d3e3b5e8bc7378710dfd933a7aa60caed7264aedf60b6e506658feb8977a9a78423bc5ef2091496f2fb86d2343f8cf448be53f9e1475e539e331a129e32f68ad30e1b2d76c3508f5a1e9056d064a307bc1d64a22ab8806559a63272c7b3df9aead48dbbbe1c7200e67750ce550b392d1dab0f40c2246417e6ebc2ea115db3f17cab9899d96e100478e073adb98442c1bc5ff4ac59645e6d1fa508ef836fd1d31bb51c52df427a8eacb4270feb3b697dc5b165e697396ba1b1f12422bb6c9261298e004c12bf11f35b575b41abd59b049954ec55fc35c318ce519a570729a30306faabc0efb848e8735b0e581a85e82316d70e014e39727322f3188f5992f70049a4b037e78018c6eb97a201e515818d41cdf0218abc7b9399d8744c01e791b954b374adeee97f14c0a61203d2ef22ef0bf11691bee9c278361dba8073a4a6d2414052076dbbdb55b72b840f1766a775cbe4e36e9192bdc46a844b1710b008d1adb0104100bfa90dbeee87c91e3e51a2a57b1bb5127c2c341cc62cd2a2b976578c882b09d64ed311e155b8198a03a6042fa423a1814d92a57a9b813c64fa918cc7a4508ff9854e3613d55b57021213bc933b01e036c2840c98c02b7fa712d7415a5b1cbe5778f3329d4b2ce2effb04d35da7f74d98d3214b949ed4e8c0546fd925439dd5d76ae89101f1aa8843fa5f29a0b7f82aeead46d24a9a8f579e1d67f1c7d833792b5ec7f661a3476739c924b664930c566d3a7ca31ad0426e0df52859010744b2180e51b2e8190c6fa5c302b7fe3519a3b5b2cffb51d691cb3cc5b26abee13648e2a2c07a43aa82894a1be5b27e6ff0a3d18e1b945e9cc9762dd56b094a62efc45b233585f6b71f282788dc8a238b3de37474aa632ed9017aa8155eb7b7b006d8dc9715ba47cb735649a8b3787243ff8b03990831572de84d199505a0d82059635952065fbfb21b23b32be4f935252d7313502788c57080588749ab69a2d103de449fc0167facd6462c4d7ca3da2fe4574a4361d9042516e3f3be1ed3ef675ee34b0add99b35de0ce9dd28bff02431391667211626cee007a0f2b319d3fb06e000c11be064cf413f37123b90b543603ba4552735f50d415a70d9ebe079c5a6104e73715ea0d31fbf21551660ec8548f2dfb44f41f523b7b03af2bdfb224581f3674b75f23b48c01cdfc9c028843c8c477fbb8bf6c40cdb82d858a7bf11ddf8b8e2999310f44ff81ff641342441289eceeeede01ee0667070b072e2e534697faf5009ca0d4afeb14adaadc87f03d0e37a5904e0e425e618c73c618e3ced426f23823a516c298fbf53be159c494d29bc54a29a594520c537c2ddc74c7bae74a8a665a7884c0e1d317b419b3a84c8abc8adf09cfab49a37b248c7c95fdae7934cfec21858b77cc798648918c5b05a5f2e2f24352044117f54fd01101f91e5cb6e868b141c70c2a5370f92a2f95262a417266ce93a63fa88231f6cbabdcca489048515e4991443da1bbe659758be21922677a94903393c2161e0b45a63fbfd687f1619cd364b549a6878e7352200ef50c32b44be2803f7735c0090345f1050861b4ac79fefc39fbfcdbd378f182f1c780ed5e5b1c7bbceb6ac2c4d7368d1778f7bae2d8cb2c633a1a7318e72723849e3228d0113f7d22b45d8df0a8334977c8f47307655aa03be5b10ab8a5ac16c36e2874afd6186b5dabbdb86e31aeb5d7f3f03fc6a110868542d65efc22299d1836b3ff39af054aa58c4f9eb1b86709befcbbdd40fae2c27dfbd8de34f8dd4a554aec665abcd9502c66114a4e4729e54ba96d2fdcb7da568a2fdcb7bf6d31aeddb61207398ee3387a334b557018588b50e3667c19280d2a3757ce9d84905adec3a5cc709452ce394b94524a4bb5d65aabb5d65a6b3f191f7d1fcb4cf484e9e0952b48e6dc1f91a75b958c8fb62f318d01909f6419b12ccb1e7f19bfb695b6d77edb6e361e2e5f4bc138c176cf5a5cbe95bb572d623a9029225344a6884c1199223245648ac8149129225344a6884c919a4ac1988774cb4d81ec1289658c753569583e8ae200e257d78e31c68bb30f9a30d2de807514b2b5b96a4a6584d094051594636f08e2f259744091486e2fb0e88030f23d2010c67ed76d360da851430db9463929ac5ca7315fe521cbd378b1f2adac3ccbcfb963b07c68c7c082fa18569ec60b7be23228d05585e58482031effc453793f3c9d49c3824deda17e08579e6bb1fce82d425d73bb3b8b8ec9bbdd3d201e0f97bff5dce2b2874587cbcf64d41b9a34d505616e350245f7e50fe07e05e2f7b17d9fe5ab2b08737fe58310a6d7168c7b1f7f5047db1520a73104424e7c7e8d6449a5c8bc9a31c616a4985432431a8d06fb6a0492321864df2b131cbe4e4d1a6d5bd49c3474796a58964d93064e178f8f73adf57294eea9c2708cf023cc529e708b9a34d9944d16954d16954d16d5b389fe73fa4bb8fc0e08c508a499364018157471bfa70709248a28a2eb542aa6ba4e69225a2567e8cbbb4dd807972a8516643d30867e0a2cd0ab8e69930b50d733708ad2323db4fc5e338a53d70df2c4b0b885423286dc4af196e863ac936181fcc86970b8d9507843f7960f63b094732bddd01d238cb1f6ebd7805f5cb05f3f460c7ce956aa75bb897b471853b7128d17b7b4602211f69f65ffb686423066beb0c5b0bb6e3c61f02619e0ed2c140a85e69c76cead358c912a601b86c52df7508680dc5e8861f796f2b7127df9545aa094712c3f5cba899b6e5a05c736f913df4c56c165a034a6d48a9021c5ba7b590172f825c8e1ae468cc07039fd1b18e20bf0458d1ab6d7be861717b6d73ec6b6e1848991e184e975c5e19732ada2b4e3cbf4e858cafc4a8ddf39377e9f52caf1fbb5d65aeaf8b5d65aabf9bdf7de7b3d7e81805cae2dd3837dce60d40dc3bfb080e52e727c83ed9ab2008c137e1fc609afbb7462afb5158695755795e532501a586a1c48c92264a4df097fc0a4a9a99aaa295861abceff9a7a3e67cc262e65ca347364cdf3f79abf5f0e3ff4a4fcf0b394f4636093c9f48206a9e7cb0f7f0d0efc0bec7fd284a6c4f7cbde7e34c42a5ee4fc8b0cdb0fbeb8df1130c42a78c5f329ceb0141e1fdfcae3476880f839c66b69504981c81187e68de5144f9a126442007cce8fd25048ce4428b2f67274c31e32943882e0407120c039610eefb4aa388430de00e48a31ca69ba2188524aab459de6bdf762cc068f0d1d1b296ca305618c4f96e18112b6c478c3468c514a1baa1b3eec9e3f9867d60d1f1cb26ecc1069b0e2908523a571c8c23184c6142971b470004108278430cec8c21184a306eb46931b4260e18882952387432992540659c68812ae048060da811033f8c00635bcb047b89d411022431dadb5f6b26c0481671cb26ef4e08615270087ac1b7ee4fb4197d2b554beb0fdbb7b064edcbedd5d467dcdf8515f5c9a86743245e81844dedc70627980081f00872c1b82588891cf98289fadb4bccaa7847038fd99fbe8c7d780b8f62c2d960d2a3e35109f8f5d7cce9bedbbdd6574cf9562153c3ff7ba7bad8096df9ee63d1a82e65e6bfaeb587079edb7ede3b600fddd7ef1fec5fbfcda6fdf8bb7b9cf9f5f7baef3deaee0b24743d0df3d7db8b5d0f279e3b4fcb67158bc4f467d712d1b4ad1caebcdf2f0435fa73ce5a7dc6e583695a295fd2a4fa528a55b40b77be4280b40f9ecbf2e03cfbad73e6b5fdc58e8dc6b11e07972b8808eb2bb0678f6f59359f7dc86013c4c5aad7d7c6f96dd7bef66331fcb54a1e709ba38e132091affbd89319372ce99514a69dd4ab56ad9576badd55a6bad1dd5d0d8e7d4cae648d4ca555151c7c5ef77f8ea5487dfef2af03baaa1f1df2cd33ed33e7b6d0735e1f77a99a66dd96b5ba97beeb7eda6e3360c8f6f5bcbf76d10bf8fefeef6e9d74cdb6338719a5790b1943182eebaa7c44d6500fa365313890bb10d537aaee03c4d1a6f6b2ded07331a9014c9eff28030305a6bd268af0923ef49fb019cb93940917c4d7bd1e40067e4dfd3cb47c0499ee4cc54f1b8a7fc826df7c9356d4f26204df7dcf78ef7a9babce37464bbe31e404245dcdd1f8999040abc4f55a7e172aaa05d2141f77b52a94e1ba5a1c98b63c481b98a14e0596a3247d01d6fb5d6aaa363adb5dafceebdd706638cf1c8a4b1cdc8a4b1444994cb1575a24e87afae823647260dbf632cf2189fee9f70ce6fa6541680e1c1f038fd1374413f4747ef18638cf9c95c651d507e8c292912631a714a8adc5218a27b09a110c55ff1df8f81afad290c913fea400a437496278c15893ce79c73ce39e79c2ff37120fc39e7bcf7debb827d1cf9f5adfcfa726f2a322af2a442a3948aadd65aa9b0d65a4b4577efbd970a3d7366ce6ae5725181f105f005e6f43ccfdb3367e6dc3b2f11329cff0245332129c22d4018bcc20b9c61018a54e02ac0ae8b2bd8155690332f45b205482380de596efe77bc3f97d7e240170db4540e7471d2d363a6a08b499f48f4b8dd38f3fff9dcdd86febc336c80861a3570a003393678e0061b3770e438e9c021871d3aecc003d583871e7cf800636ce2be3fc018f936f1020163e44308a1350261e43711fa2522146010cd11223345442545560563e435a54c5aaa8a5511298cf0b1e2410f1e3ac82800893bace5c1035408a640a1652343f8b4aa7e42aeb52ba303149490f2638c31c64f8608471ce28c3088a616862e68a4946e284530063e8d1b88774dcb216887069038a2891c3a3491c28919602ddd35399233d7629c0311ac1c8450fc1cb272e0000e53f0008f44223b72505a76f4704352ec0862c710941d4576bc60870c76ec60071174a041871c3af4d021081d7a7480810e37d021083bd0b0c369072076d071d94105de0e31d881098f1a3c74f0f881470f171b30275420011048f40882144c1081bfa4acfa74b57c9faaeffb5cfdf7d922eddfbfbf4f26bcef9348ac0145a117970964c5f7d983c5f7a9434f1b54becfd4cac79487bac5ee98c58a90cb7ed95db4fb09bc6b970fbd3c354d1a7aa2a696cff2fdf2a1970fbdcb67bcd9b8bcfc7d71790955fef221974d4f2d1f85059893bb7c10c66cc977e42f1f72093dff3ea462f9527ee5f3b8cb68083ae5571e05c56e0aa0c15352565698186b1d0afd4f91a84315f63dd6a0281fdd91f251d4ca77514b34ca67af572b5f27512b1fe75af9b6a8b3f269f0339759a2f55f6de5cbb8cc128da5a4fcca8771952fc563ca86300665f7996373329ff6d801153a7ce96c5f8f2e14eff42457bcd3539f39bc53dae949c67cc538bfcef96241e600a971572ca40702160e4c1ec3e0044299a92a2ec63d6aa2ab0a05ce0c008ab82f241a0087bdbdf5f1379fce18eb172705f2001e1f7e5dd4934a7159ff670a37d013982f8e133f052974b72696947d4c055d602e3b4c631996310cab95889933dd653bce0a5471506bc515b2a4788dac912f0dcbb00c85b117f6c25ed80b7b71d9b9b2a8a2a3d2b9b87c95159798eac5ab5429a2258521a1f328b751ba944e775ed7755b101d39f7f2a1a73f653f0aa677b7639c38e0713ac1e36c82473da70abad03eee2dc5f5a4a1799b3486776fc1bba63aa6ddebab8471eda90963ad672814bfb7982adeef0b736de79ed10cba6797a7035d68f5b3fa58c5d9a5edda8287bc2272125c6e305619096254c54f46266484c80c4163082a8a2682e4e081c707b0d60328091c4095c09f1466c8c112968e286a60022374b7440a3ab45d25a13360b14e4860e9b8f1034b8710968e23583a44f00111a48a3031031d301102eb44847bbac1ce930e2c8770ea71fa619e80dc1cad3973d00093399ed0399a58d6a906cf3864e538421426160e271464cab4d1a428521ac38c0ac0cd26de3bb92e83099cb64891a44ba94042157c06fdc8b58451b1765acad5b71b0c13b0895970ecb38f371826ccb4e0d08597c0ed87a548ca1821e59660ed8c9052ae6e4af74b0dc110da07ad3964e550b920c78f1c3d72f4e4e0a11c1a9981a6417f40b372e44c8e43568e1b261338682dd1c10e392a1b8ef0a2c280d710d41ad22921201aa780062db8ae185300a55ce11614b931438e377c58cbbab1538401e46b6fd050e8cc21eb46101858ecd251e81d687b4ac106dd5f069a65a3b571c8b2818323c810f912bc81a528a59c136e8ce9949352aed38849c48f30c64eca52fc8da58852190472096da5776671ce1dd1081ebfea483b513b309d2718a3a4c98b09904b0913a0d614176758a659ad668ddb38dde9134dc9f6e24eead72513201944891ea92383c81f4c640dd314412f26530401c521475444523d4388a422cf1471473c45d314b412e131be6cc949895018a39473d21aa59c93d25a69add6de8bf1c518c3b22ce72c674ddb368edb38aeebb4f6bc9315149694904a8bcb4a271fb717450f171d7e208e492aad8828604e3a56b8483f2b9e5811b4820969b5a2c90ad70aa0152fd2cf4a6b3f5a69d89d3481c2133fd4a388a01c1813dc60a0f0887d0ff5e0f4e327fa917d0f0571fa281f0074260cfd939326b60ac650faa313e8bec2c56987e900e0e1f46b6c2300e81101a96163878f20750a9389b422fdb45c15a8be6a10cc496322778f217e8c34a26d816e3677771a31c6d6c9dfef0463286722feacbdc97ce6ab71b18b3a7af144d56433c99a7ca29a345a6bc2d0c75aefaeb91315a76f020fa742e4f0160e594130e1396a231c340efeca592061012467e8b3502267ec7e82c54aceb4e44cdccfc215474c68ed474ce8ae6d27af88e545419dbcd69a34211dd25a9cb66c264e4fb609393086ba30ecbd904b875c2117a790dbbd2587d3dfbe00a0600cfd93176d915fee17def0c5290b1414a73f428166b1e2541bc2e9e7af07a007a79fb15875136ce0f4b52158094e35229c868e708a84d3df215fd07d532d1a304c1ef78bd2386d4eb7274edf049e8e08dec7fc1d0648473aafebae4742d2455b493fdaf3482ad2118fa44477279ef75d975f3f497b9ea75f67fb15fb9bf2dafb3e83ea4fd18fa2b3edddf5b4eeb5de3df2a8483fa41fd20fe98776f242f183471d1cb280d8d04f73cef952fb5d773bec7367abf71da51da5ddd7cc4ee2680fbb93efbafc5ef75ea6271df5aebe54ebbe7bdd7d4d929a249b5e0f6c73cec955595f6a3549382931c9699da669cf4929b93b37f9aa495293a42689351284126e0287ac207878eee69c73ab5c7d4e23adb0a9719cc6715beb24253899d671dc6bdaa6fd06c3d4b8dfb6cbddbcdf38cd7bfdcde832348db3415cfbed493fa41fbbf90d0176edbd1789fc9a296526e52bb9f7ca7baf9d32ebf5759368d406616ef7dabbdd4be9b66ddbc5b68d8464dbec8f213c050e593f4c3fa6d87e30f94164fbb87d18e3ed5decf61de76298d2fbdabbf92fb7656c9a96afcef60ccab1dd69907e348a6778b56eadb3de7b31986addfbb3be789751b32d275693a426494d120a056925354c52918e9056d97c1397e190f5c311fe6201a16201d1c302e274f2f8f17679ec317efc27dccba8aaaee3f6e6e9bb67d01954dbd93ed667d0c7b0f9a41fd20fe967564eda1ae8675dbf2e917333a7d5ace40c126d638c366a5bc95afb35ad6cb3b1f7839b4d8ddad7570bcbab3c8aa7dd5aabf633244e0b2d6bd5fe5e0d86c971f48a9a243f43f2165f35a0100eaa13b5d61670bb7b01f65b7c8db4f565777d79417663695fd6ac6a90d89730f504ef336c8b8f8f6b666db171f06f2d3e493c80fcbaf51992b378fb706ba1c5ee926fcf6277c97145b12a96c5b658cbe2ed96b8b2b0d5fdb2fd7dede377debf7c0939dbdd6fecd7d4af41120f60bf5a6bfb0cd961aac56f6df1796b418bd725f470ae94f029f6b5ba95eae3c8dd23b6b5a0f1fadeaa8487b3d3aafdabd9e4257278c610726c9f72fcd916da23e1f6b3d7587cdcaff8eed797cffb3a2f6a59cd2abb260c7d97dd716877bdb255760fa5ec16f473974feac652a4396f3561e8631dca2e2dc7a2f4c9f782837d7cef7b7d69bd5fbcd5a41161911685bc15a75dcba91bf755d57d9c8d6e2d68fcbe68d2221da69ca04dd09a24f100b72649cdaa26496ed52489275896944b0e3e72f873c8e241891e82380a1cb27a50711e8ce8f1a309d7b8ca27c0c4fa4109d60f35583e2061f96004cb8727482bbef283dbcf9af69d446d0faeb177c136de3616321e33a62415e987b49233d70663797becef971f7fdb5723d39b1d8d18a47cd21192922ea2dc4b52ddefb04449f9fb3592f48372bbe7ae4a27ad62dc9cec33628c72e374cf7dc7bbd38d460c77c37f494a78df7e86c4912f03c75f9f9c77db634f3a120f101feb2424bc6f5d72f911fb8db4e2db7fd767d07bbb8d837bf7f8debdb1805fc62e4b2d729bf2d17277f74fee17eeb1dfbe1ac1f8f1c7ad85f8f8492ad2910da548e23e4376d28a637a2ba93cde38f8e5772a18feedb14d65e5c3f06fcf61bfddefda897bf1ab6cf8b14ddb6cb4049838c61dc61fe55d0b95634e6e4f3ac2f1dbedd33fbf95a71fcba37c1d496575f849e6e917b7d68b7fefee914f9af9f7efce946fdfb1cba8c9b671b2c7fe6e9fe302228fccfe7a1f79b8b75fb0ef1e77ed04b75ecfeb3e9e4129ef1ebfd63da73dcbb7f2cda0b8ebeceb4fc52f0fc7dfbd1e6e9ff4f3da9354a41fd2eade8b6dd24f3c016fca577eb040a1030aa8004940314135b13808c5e22819a088409d503da05640634708261822d5840a7c967802d553001ba04e3da0842880121e52a4c0033503ca068b0b70031e47701e48f0e8e1e1c383053c5ec063c5c3091c2a282c21fa90d671c2c13b5401c7505c7e570484918f449744b74467a46ba27342fb81da9d0e8c91df01fde0f227105e4d3855788610e79c14e3aa6975e25882904268eb163195a28961b7d2c7d9d956923f6f64b6dd4488d9aff8da9a6da50a21c459d3b8ad2423c4302207273737cc029fcdf9c9be6b9ec9c76a92c413e4e318e3d31f5dd38a5bb5a721e41e7a3ea004d3ae9ba19d7cdeb7b590e5531e58236fb68d7d3d72fa699a57ad3febbaac3b1632de6dd24f3ca1fbfbb9b389d5b5eb13dc578160cddc78286945fa8927c8c73eade1fb610d069ab48a9b758135f1c91d5142c999765f5d4d98fbbaf5ca085f585326a70d5d0906a05b1e2ee1ae694911fd1630463eac2320746ff143ca6f015dc011101da18de59627eea0663bef90a29a77f0fab907a5c36beee1f5332acb76c7f55f6458956559ce913336f229d7548540f56baa1251d5232fe0a4a19ffdccb2f9d96f99f6d90db2afeb8a7dd4da682df44c23383171f999c949f7cce4f432bdf2ab498c3ce6246076c99927e4cc04923395899c81f1656e82cb6c84cbbc047d595df3d3ea073f7e9657d7a4d176f800d24e5204444711942a1925eee15df34f7351eb33137b6018064421fc081379d031da6b5fec438ae3cb97f8109bb2063ef8c721cb4710cf5183d92659a0d10fdd3b3e516a9e73b31905a17b5565f831bba19422962045128e7ce85e0297df5f464c198140f77ff92324babf94524a694f39b9aae6969147252749455af10498482ad2aa074204c999ebaaa605532d0e97c8a959b500f5221d21a95845b8583a44482b999f14c826a0b576c9c7af2be8e2aa302ab9d98a917b8d949664c593e769758972fa54b318579eb1ede6d29112dae69e9d5f2f6c05ba5b1faf15e85e83b0ba031f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f9f97b59f5fe4a8d91719c629392574968231928e88689749c41d11d1ddd6e051fe8888be1f794c60813827a554ca9c9c5a6bb5d65a9b7befbd49e8245c2e9c848e0fb1943674ca7bc4fee6027ca1652d6f302014895c8010a61a813036d95218e5dc463d5adb2f302ccf90524a6b0bbaa81f5ffbb4cf9af69ca669f961a75139635fcb2db87c5f1326efab7d1486e5f9219c587dcd9c737e2c3fcebfe5a77246cb30ece74da528ef16747179fcaa11dcaa4ed89833282f619443f78ae467554918f8b3ea28bfbecbac91d7a75f452245308910b85a18ebb46c0bb75c745aab56cececececececececececececececececececececececececececececececececececece8eeb08ef44e555429ff2282bcfa25786847409293da44409292ddf6dad96af6fa16f09b5b46c5bcbf7b26d182d3a3a2d5fdf42ad50e85b36cecaf6c9a8ad904a8baf3c7c16941595931414949393939313cff33efbbc23b8d7b4e75aad566b6444abd5d269b99a20c2082153dc919018c569f401931026427e0c81c4233204912143040942248812593ff79ede23219ed69ee7e5fc3aef9110adb3d63a7b5a8f3e204489109e2176d88c848c848c84f8182131e2a9f6c505edf3c7c81e7b0d6ea52869494eac637c0a83da0827ad95abb50443ace2c6ce682995b6d63aadb5d6527befbdb75a8c31c678a4a3e54f6ca483b18d91ee4b6f34fb1f8431727e969b9c93525a6badd65a6befbdf7de6d4484963f5d2e22b42d5120a02e03a571a57e8d8204c9e9604dab669544a6a32126a5b5566badbdf7de3b3f77a321b0d110754e696da5745259ba479039674dcbb2f679cf7d0dfac505efb98fe1ed187a6794ca8aa7d554d3bed2b602e45a66bfbeb66db88d82d0d9fc6aa48cc97d32501a999c3ac8ab56a49262bf492bd26a3e7d6cf7a8a2a423a49f4c24ab724a8a6eeee1b302fd50817ec854562168a717dd35ad9a168529243549b41a5a8e663a8d7cb0a714bd473e68a71a2453a29a349cc63a72d47e35d24177ab9a34564747a5a3d251e5a071b04e7e1d395333a644ea5a200b6499dc8c9154bb2a69d9daaa4ab2acae918e0a5499acea1426a0576d528350da3ecc2187f4b38384c44766e3246732d48fda9233ddaaf884765595e8f0a8543a3aa49f8ca4cafa0c7a9fdb28b6553c2a4dfab1af9ccab5098f71635ae39063bea31db40dc236c8b66cd06887c6c1bef26e5bd9b65c3963f29233f05ed4455d1fab9a24dbdab039f6942367f049ce58694f231c503ee48c11728647ce5c949cc13f82c81939737fc443db87a31d3af26e7b2c113913b792237206df950f20de6d2b0848ce749b6383e44c8e9c997f4d366c8be583880db241b6655b17bbf4504d92fb838a39e7bd72bb97a4e27ece8aabed70d755aed639e7dc9efbb63be7bcb3d65aeba4f776f5f34887c6891bd75a2baeb76e926abba94b90f05a13c6be46e1f875108cc99bcda847c7bfc1126ebd25dc662878a74b9644140eb712a63a08bac04d348482c7c74eb413b7f1cb19fe88f1f79316b72db9c1c0b494c2f1074117293408c65829b8fdecc34e73dffad5af6fadb5b66e96d42635cfc5ede73cc241e3dcafdcde544ee5ccdf1d53ec579a638c5f8c3546daa23f77af9446fd9ad5adad8afdfcb078ef7d4c29fd9a16bd15c74823addb9a60568d4aa0ad35f1f833065e9f7edb523a6ba546d096b4f23c2f9254a41fd24afffd344905492b928af4336950029f3fa8b4ed95663dead4d000104000a315000028140e074442814814a6a2a0d80714000c858c466a4c9ac8e349962331884296006388218400800101919119221000605dee90e8f9ad514d2c92ec833f110d71489e850eca1a59de7780c6b88d73e870adac8d3a44c1a5bd43534ce9f228628adbd1f6f88f8121969d5b119b0af3be70166f8b0c3ede2413a1a49b40919060c4f102387f87bfa5916d89c6506b07566d24ec043d0d08c8537cc0952b7909d2166444067b51e2da4f0a1b310097efafba828ff8ef4d85b7af8385915dc4b1b775ce74a2b745350183ea658966a1e3e63555388c0d6ebab7b74f34ae8919b52c40b5fe07ad0f637393a1a5f48bd9c5e0ad5da52c9abcf50ad87dac33a7578de6ff838d3db5659b713fd14a509ab733541d2b59052505555eb3fc327e21549c426c151a3e30015919c609369db17d2102c4a776d681b856a9f0a5dc6c107ba6ef817aab0ecc10a4ac0a2b1829a9a13b1864ad944bae777b3800a481aae4a974620f73d8d835972adb3581a74e7cd9c91e488d457b026beef6adaf7ddf2148a8fb7e17e1d36f9049ae44b13a08296990de84ccfa37262b5411cba5d44dce2f8832e11325f6ed6b475f020c26abc9989dd027885e69626bb4637ef6770d867fa7efdd7ee61afb73d75776a238594ce6959264ab533439fb8825263211e1a026d9ec9501c625673712e331f1735b877c57379354f62c2703827be314d3262d263fd2ca8f74a1a6a59124659e408d936c9b750655e388fac3456bd65dd5ebde28b3474ab37f89903a06489b2ec7332c2d6ca70469cf35735c5a0f87814d423b84e82fddf52277d2f8728bafa5cc29b93c6eda15381d32cd6fb950921a3a0f8092ae5ca242dc5f77d2072cace5760aa4e8a4b391441bd46b48a6dea8bff5f2127b49146f78eb374e1f7c3a75409f0c89a811e7b709213d8017bb85df47a9db3e09ff8064831de2c810f1b56a495e8f0cf9b90432408298e8183c32d2209efd0b9d80c21f10fa2712fbe629400bdddb57c3fc6159ff27fa8f362a8a94bb40c1ad648acf6c4c861d244d2a19c9106b9ec40cfc05ba6934b8f86b918be8b5577080b9fbb0465634d9b2e20cf66e4708ebd16b536f07d5b38e0e6ef4e20b75c448967650745302b6d119a3b27e9e1f4283a05e31a8247c3abc1770249231af666a912693fe3a7a3a1b6e7c50f3ad50535ba9bd97f6aed5341f5c77ee7d7357af13a0cf79ddc8f417e86e0fdfc036ecf1b1447a9fc5cfaa440874f30de9fd5838588fd585811750be3b6d5d134ebd3981ab052139c84ee6b81eaa02b8c10e78da99491e16308e065e0f2091021a1cd1251e706b697f26dccc399a2d045955b20e7359578bb9e330a191b7b8c5c5261a23d30b6fb32b907cd1de2230041eedea0e6b2cb9ec49e1ef38f3be73e2c98c2c8acce4e455590285fe46ba090ca6d97dff53b493f6a1b562c39b72d358a1d800f41e1040668282c3ad57785b2b414fd34675e92cf486eff9bdf4a465ccba4a31c26506a929244bec7ffdc6a8829ffcddd6a755a46cda019ffcb0657d4b5af670e692842e005ef87c1220c146cbecd03bf41a501fca6403e556150b218e45ed6fdb2c7a8ff7bb6e100f0e25248c3b35d9b3184357d8ece078c633609a948f67e2eaead27ef605f241dbeb59a2cf44ecee31ec70dde518ac903e58980561191aaea3967a92e452282f3d1a88c913c0f39a056f6ea1cab0ee0c848f260bf529eccd4a22c06f866668dadbaaf2cc435d85928b52f0fcb180e3a7f4c11d48d33c78453e7719180ad204567ac1f67bd932e72dd1fec29a5c04b480780df968c584b42ce5981ef3f5229cc2429524e9d7e7e052230fd9f6956ef33587c112a6cbb3f7b19bffabd3199905e3fcd3ac08dced0497b936e915c91e26c81633f95458ab62905595e0a89c355285294ec36e833f266f81fc9d9920350429bdf1b2d8b0ced0abb7846e1b93a84734a5269aee05bd9d11950e26af929632aa6506941c0a20f5132f5a371cd45fc56f8ae2efe41d501e455a115e4c512e135f3c77a1fff44e48916faa1a0dad93e861a25d6e83a484936ea8d212e3a883f18dd0528fd2e5af9bfe1fe0dea984e079a8b27a35bc97f1b52a21eac7669e03de4b56d7bfdff544181e5b38c07a9fdfbd4ac8cd78edb44e007e59f5b461a2b207b47a012bd415047bfa20bdd93f7bf2a6d610425d58f932c5fcb63afe4d14fb38c2bc6a6e41ed8ee4750068f346ed13359af1d2eded57864f02e0b3fe26c2bce04796bfb26c66180ce8ddcb9c5ba317f4d66dd6d708f33a32beadb2f4be615461f0e7ea251f1361b6dcb73e17837cb9e573b47baca07dce84161755a384524a978b48639fe8f299aae5648456faacf8b272f1c00d934329d56c295cf3882bbd7cb6c98d6736e269d2a9cf747817bf440cdf6cf7eea2e1c2883f9a2411f9165a3ba786f3a8ce0b8719ab2ff742533b05a25368307f5eb3dc3409e092538d28ac12677b448828b63575f80d3ac9442fa2766d6904a300d2d4df1032a34a70747a06897b44455e0adecf92965ae866dbc0578163f2ad6b9b799d000b6ad704bd9ae2665b42288951b17c690b5a223c34fea3c103f7a153a204d4cbebe8e82037b746335fcd95d7c74e835d6afa91a2b3c64e0b97cd4653ea44b30feeb2c9a8543935f6825d6a1a9929c5e5de6a93704ede887e6f15499bab77de406ca1610d8dd3de4282ecc53b0ad72075507556bf7b5501a3e915c42d9ace84eb7ab5d5c2e6246ef021aabe24faf37553d520efcc514c625084591bc521dcb70561022ccce7cf4ad1355590ad0b3b8c7d41d80ffc26ab172970dbdd4092c9e7e33def8252468670bcd441036ff034f0679a8a289ec4e2f27b6e3adb8641c3d7643852f364abf817cdaa6e9cfbd6dffe0e3e590cef9a2c1ea6e8d98e66a73974c1c9846299bcb37363051b784caab43525e02cb3464d6435eb993df99d1dd13757353fd27b4892ef54d3b32eabac514c103f5dc7cc4e379ca6680b76f4bb5cbac7360563185c22a2b275515ba614f5b574f6b4a3e0df34ebbc1f99366ea60a32ca344934534d2ca37653d60c3052062fa09af61e1aa1968d8a794631e778fde0e557b2784c6326540170e2a444639bcb24ccbe56330530ae1999c43c37b7212c125c5489189d52c74ac4e7bb9586cfa586435c11d306d9cc83be5db837b1ecb72c12508e9608eb31fb98ee8435ac98bf44eb156d8def8f03d7ea65f27b49f97b224fac074b6f22270c66e1d6a1b2c9c501b0d1ee1e569aa9199699851fba3e8b9700a262420d83308c49c6ccf87b7a329577031c47e324bdfd345a985b8d1202af9f0b6df998e03470fddf0f2e1027efa4d252a0f4b885728b9aaab40e71e878e1c737f79fd0b52c21083c0ab5d5cdc5d9e4d4306dd2f8458300c68b029c80bab53ee2cfe955c0ebd030237c8c267900d2a64a92e3e729134ce42dbc1a25742ad8418db507cec53b4a1ad1813a204f7efea447fce8011556bddcc8d83506641a3aa2ed47cbe59bd353143cf2f913997a97888c4438d6dcf247a40c12d27d551dcdb323e4a8118a2c95febecb531eb559a74a39dc232b59180a86e61293a559ea640e87bf87694e3c9dc0617585920d93e0a5c5051599e4be5e84a721f5ca6217c961dc60e806b5b6b1ab0e19eb8ce07243f3f001bbcaeb7541a7b231bc210ba3971e6e1835c089bb5c53955c17aced173312f299955259dbd5c29697fe784fbe5a4f198cd14a22d84201fccd65daf9f7955eed2552c217936d26d21c4ff3e585e353412f35fd4ad15173afc5458d51523a44ef0abaa03952cabc5a7b412e348e3a0a611947af4a52a44bac788d12f84250ba9be6e06f71c6487b746fc4343a84683de86caaf403bcd83ae6c4055eec6e1d06feadabfb2d3a7a9125f062bb08cd17235e6ca5cce568494969ee02bc8ce11b3c05e8bd1aa41ef54774e065e06a742777fd0f10fbfdb3ba9410c07313c4cc1ebc5876e74be79bc083087c5dec9bc7be3426168b6259f6a1884a025c3b175b27da0289acb2a73beeb19a596c00f1f8817c7f85574cbd6d1d28453510ff066b1c271855eebf7ecff3be5125837ad24c29a6bf78c23873b70c286fae56794d792af5e15ec41c84b3e7cc8d6a5d7c81aa91ca8b24f53342aab9d54394b02f96ab5f00a46f514a21d5afff54c9c5bcdd328372200de6cd45699d7a21f22045118fba0d399454f72615a54aa55a96aaed7784a9be91ea7faf6a69de97d3e2cf948d4339b7a2080c787b71c1ec0be94f7047da450ff1b7ade1c1b36c852d0bdf8215fe54dc72c49f967c13b54bc7379be1e4a0830703adb1118adbd250cb59c4250d5808121e500c5ebfbec4f63141dfacdf9c57685e4640c3a311e7be0ecb36de558dfefdbe242e1b5f29766ba5397ca2287154e3632b2d9f1f380731365cf8fa7c3ab258cbdf675902e7e9e3c8fa2b1ab527c912fec03e472c5f1ad5e68613feaf58bc9fe7ac9732443c1452506ada72f043e979704b8ca3140585b7597f5cfc806593e57049a7a99924aec49e1f41f85d37a8181f432b1bd228fd9f270558ffd381f1565a30f19b28f647982123603edf7ebffd7cf2d1bd14098775b0178a29f4e2e043e3f6a616f48bb67da79c452a615a563c01fede9a37e167b2dd096d1559363b3ce17cd8519a5db560040715fdd50c88afd13e5981e658bd92aaf417a21d9fd69e3c79006f37af668710dae40993263b598858494a53b0c13b21335ee46511177a3ecbd5dbb572b46f10b045fe0482dc7cc9c5ae7cf226a90e0c8c2af0f262924cb33f8d0d97ccae30893f06b7d23d540a8025531a0404759470b7ee15ab35840ce7a9775f41707edf88ca8adcc51ccf78c327c0fbf0a4ab2fe269d3fc8eb6b92feb0325732713453c953542b8fead89783a95a2cbcc748f51f0d8219e340072cf3668d2abf7b6f3519a13e01b10b54d4a66f04d32300e4d03367cab850d2fca4d614f7cabf59efd45954b55cbf8166c60f20515678d5de1f1edc9ce2f5962411d052c63267ea5b7111c86d5f927f98641485aa88600c98cbcde3a549257be21300a1154f936ceb67dc162415906882837a6ba46bf9bf2c7a122315049622c669e4dbea9cc5f2d183aac924082268f9cf9861796240520157d2306f6729c349cf92ecd8f5a1125ed3b894af94e87856c60b4beca5fc4509b1c650ca53939f38e77839c2c0e41dbb47c7e6ba6f53984b929b629667226fb31a81a8eccb2555352e0fb5c88603680723a5a76209776901a1362bcb420ab6decb071012ec6404867747f3da1b008c5777d23539a1d9485b77f7639e1aeaf146631e6a112e78daa3c168506bb5657d36db89f58fa21999fb43dfe9bf9c812770d2bed492dfdce7433ba8608176152d8d5e80df9ef631d86475b2f7751e540ad11f22f52905bc29c6d9d3fc36efd5e94fd459530e65de6dc5683c9699353065bb060a2dce21c0762bab88bf0a438ae79249bb1d6119c28bebbda6a08ac2fb4ea54b8504af40351d2b2e7898df88bc9a5244b20aab8a561c98e34abf691a343df6ca19dd4e14814916a89c1c09e63b33a8e9e0d6ffa7d4d9de3fe2447a8b5ee0b05bcbd2fab1e006ca071ea3b8f3194e2f035c2024ab75740edf61bc6d2f15a4ce1ff6a16ca41dd50e624cd3f4259794a395740852db43af43c5b104d139f6a969af514c00d55c633c1d1d6f236945cd7feef28f63821456b26c5a554129b91958ae31eab65e830be3e05260530b1bc92a8119040bd0595606a0d92f4dec46c670626057726de0c051cc95d2691e19f6b7eee2a10f7b6a5be24b0c49e87a4d2d6621aebeed8fc1706a35d6d037c7d935585e22a25e0d5137c69d54b9d65c52d113701d492e45ddeaa861a7016c068e4bbaf9fea9884c830e1f15cc10b27f016f1e6f67de7abf072d5cddbf55b55f7ad75eb9da02ebc21df37f5d90094a1ddb8de8053893e2ccd0bc0d25bb61546bf9d3e045cc293d59a59118ff6c60610da252db76f7a8fc22f170dd2b74452c8770fc2113101872f3ea51fc383780e34da8b4d82cbca2b2f70a1ed6ed3152d286cec603b5a19674c807bbc2e52e9a1d89091704d23e2bc7317480fb598f73b8e4066585101a779eddc455f9f6407dde799145f76d1439967fa3fbcc0ccadef006227ecca4392071dd6a3518f1eaaf687ed3a8455f08637543814def186c2d5f22f2958f4a2b3c3b232cf210c334dc4fee0c286688046d062d7be4736755f9c0d6026df00212365b126380e4e467c9d87da1f5510341b63b69d0cb17906d784527a6c87b0627c63a925c0f4a51eb4ed479b31ce0817c73152189acace97f91fed0164c7f882f1d8dec4519b53cf5e6ed5a277d0c0b9930214dc499f37f1a9873a1fda503fba6a49b64f8bb3978a8e6d190d8af5b567291a9fcdc4ac5cd4ccd80933fdee6622c2bd774cdc48048306be0485d63c6766f46b12a23807d1fbed144a545af392399af5b10b79ee48c0f1479d7efb32f8884cd8a8f3774f5384aaabf03ff3c186d3c399584703fdb5c3c7bfa0d755c10af0f089a89f6392f249d93374fb5201af5243a0105fc4eb252bd8736a7be87884038b815f91ba48c34f3e9a5c674f4deb1fefc1cb856d305ba8d394fc94456da1521245fa1d242135cdbdf5b886cd117c69b88690e71fed273273810ea289046070f14430c0decd96d6599a7f6c43d93eaecb882af82e77aa47e911039e8ee75a1a00a41628ed3016de530213e6e532faf8d74936724722ca7f2e5017d5a54096536575578aae90d05ef675fa50cdff8fae22b0d02d3a467a19a8d0249908af1101954b970541401da3fbd0f8abb8d33ad136341ce745c2740f6cc57ab461a50e26ad7a03aa1660507b9cb5f934ea1716d98cce73868e773e2c008807b8155d3a4d0370e74e355376031737cab58e1abab2011290763d0d87e1ab5cce13d9265f1de153137e238be68f39d6f527143f8752e28114bd6eb0e40ed2adbed96918f1f10b545e50231b3760bc6b0fc38cbcf825c488f8d6560e6bbbbb7f1a51a42e36232d81339e5254cbd3711bb3d69109f9d3fd4836ea7ad449df1db4da3f0fa19dd78caf934be4d3c2c95e83500cce27e3e4032e8bf1d308926413da51f521d251a59a78375123c256b0aa55947d9a3c4b172dbfa92d4f1db0ba3a51f8b6c96d157acb4b942fe19724e4c366e4312038574d4e2daa4ff0069a7998fa9407e4b407545e8a014acf90c20607453a3d52c49faa0d7840ea23af9ce4d4acb1856483fd2960813a1117c2264b5d7f98d654874fa370fc6427e198d1239c48be0ba1c8d3d310fafc811e06a9cb0a872e9556739bd871521d0d217228e8f63909435747da0c8376e74631acba1308affd7f345bab7f917a234225f833cc3420f2e86ab2f22a7ea33cea073b06f16312da04a470eb67eb51e4a24fd15da92ffcd1d7f43e8b026a7f3765e001e8e5d7398eeb4db32a349ccb7a9126b479c43966027309e6383154525367daf211a9a7f355ff76a67527c108f3dade88ab0130ad229ad66ce5d48ef5caaf4450f3694537a046eda25d30a0bed3d9a9f5c868b79a804e4fdda8192d2bb2d49ed2bff74d8df61c962b9f025a0a8e6a37ffebc50610307fda1fabc88181938f31a2e3afbd981a46906fc10988cbaa915594fff41a3d1aabbeabe66bba34c83e602dd10770b0467b233557751470eac4c773fb8d364a61421048288ce41c174f8a0cdd5f606fc7dee5348604e8b8f7ce19940fe53f06cd45dceaf6f2bcf800a60bec12e874f5544defce212a7c73bcb04d58a5ddfc82c0e43a00452f5c12d733f7ebea608ef41f41065ed2fe5cd6c13f6d96d572fc2be089381fc9d9c4e185fe9bc83418e4e9c8eda78c5fd4251d085ee377bdf98cd9baca41be4f6e8a6453a321da594dd94ca78124da55a837e93d69d6c8d6019f4ad1419c995ecfc9d1132ce849222a251c7671044598a74ea18309b537bcb7d02e05c15f6c7a1e56c0176c0d89e89bf344ec061c7dd68da24089d994a8ace9434652756392176716b931a82b5f7c90c9496bca6301034d6d1df89e72409e1c8a9e7f7d1553db9b7ffb41cea1b2d407fb6dfdc08d3ae540bc711ca21a7de9ed486baa076ad4cfb4eeab7f8a09bd9ea41e4cab053660acadb1e970f93ba97d644c347fc850e1119608a2f0bceafc32be4a366b3ab82d4bc152b1fe12e9b05cf93245a6fed95ca05f4e2f05717d322bcee810f1d36d9dc36fa6cede552d1c755e1c1316d152d215cd050cad38106e4dff8ebb64c30ffd92a22732c5d0485406f282d0a6b862a5aa1a9b0cbe7d248513fd8c9115240929bc280adacf727ff656560d29b5a9ecb3a8acea1589fc1853f27c7a8db9323b21066ffab42e984ce0cb6c76530d192a0a6c5c63e2a6ce3a259cc5932bf5b59924abb109771c0daeddc42f336999e763a84169cd2e787d593e36a214ca06839077bac950dc924b2a41a19d3ff41e649a251dcc90dd83f3601e11c1661f8b61c5c5dfa6a9810fe794144b16dc5ea4a952a6979d32440a7c19deea5d223da2bae62d16c12d0a3831d0e58f7bcdb7356c1270e5dea02ca84eea3b8e07f8afd41bf6880bec20875b25809b23d12f58ae964d42e225afd4b08ef9c84ca5cb7a9cda5b3e8d1601caa97669a56e899774fc84bde6073e8c122f61df14b3f1a7b0d205643ef65f641393aca8d81cfca50bbec23f01550b38a38defbd232f5148c1cc43f13342f26f4ace03a856af2ecf022769df79550d7da1f55f532f2ac9a9ce6b4011e0f625bd696f166c5725748bbdfbbcf5a746549ba100437a854c8def2276a1d6518944843d519d70445918ed456ad003426c8c7c639c2b4a12693b10902c337d37f19095aa61cacbad55300f130bc52e76d9a8b35b3c297853707ad2543f27405692333c06ea519d2f29ad696612411387630e5ffc63a543acb9cebcd955c74a59479c01489a0c93dff82474c255add26a77ed58928c600e26018c8212578b7f8da27c1bef3c72ceca35d65fb5d7002373dc58e9f6d96ab6383b37422bf799a5f21a63a107a945c4e1f8b4e9c30a779cb3fa6677c2cd540e15fb498c522b4f0aa3b9901fcdcc9ad5f443677c89d52647492ba4ece4993bbd0e02fc8d8c2ad38df4f6aae916cf9037f0679af1d096de4b92dcdeddc31712e5d55f1dc90961f29d97844e2c6e9e2ac8102b45bb54bf4945772f255e153e075242b95401d35fca734e6d9182672ddc9358110b1424d20a53cec918f1ad2662c61d27dbed524b46295df79d3812772dc8b1cbe2ab915ab15136b22e394362c8f6bd3362aff3d1c07e25b3ca3c63b403c7b0c4f94991f587ae5840d74c5e23d03029a940f9ada1ccab865210cb876e76c85ce3fa4873e1024fc49c6654204800a7c7af892790fa3889ffd58624d5f9365ca45bb2ea7615474cd8e3ad4be5451ee4f2eb450c4b80f881dc5ef87413a31896aad14c996373919ed9118257dbe26e4085837b1256f61fb86aab1f7e818c38d354a00da8a52a90512be2b29bbad1101eadc2c9ff0f160de43b26f58eaa0778124bc0a458f149a2a027bcf13129041797b853e5ce58226aa4ada45e805ccecdfe2c753c653ff6c5ee324e49932c0e5321eca6f9bebae6fa1e3bd85cf4fd1013e1e323a11f9789c033fb47437e7c0d8769c4a94c6fa4dee6d09f22c8caca7b11fc3ea03f516dfb125754174004bde5a201da174c245cf7579ac294af8a951e9e865bcf66dcba220afcb60d1ea61447a4f0157949d7b6d17ea8dede633414b1a5da021265a08d9ad4114bdc7a0101835193b5d33ed61f5a7add9c7a94ecef8308fc7ea70b2bca55eb781b57ae49b354edc88eba5bb1eb0b21e45a3a82e234dc2f98797241593f6f51009f1c7ec122b99c7705acc4cd57b786cab35c4b454801ebaa0a312c1d49eb33c39ea81862c411aa17d8d3647e61d92833700c3b99da1fb2e55782648e19d0d9233d9d00e18f37ea8446d9c16f564cd372b70c50918a3154c00356c37bc4cb06ac77c575dc4a1abb62aed76fd9aa981a2e15024504c964ea842123739232d4c7121bff44b3d902dc505233ece4de5292e4966f434a0f7bf19d6a36a0a5a4c3b10c9027a844ea0003b5371a08a8c8deb9c3e3ee8fc05fbd2927c85e13f2e252713120d4743910ebc08d53c290a652ea8e1bb34302f3b9c72eab388ce85e005ef424aa2bdfd3c05aa2c1571a6f5a505f19a9d832f2bd2b3f4ecf2801873dc85ba73c16211d9008d13e18ad982a6d041a33c9a6f95d89a5276c812e0fbe444f05b99df6a7a2255ddaf6272a003076bc76aac1df4b49ae504e1dde0b5f8a3d131da4f3fc96bf97b6245f5ffd098a2a6044fdc8093f30c8510835fda741f180f88012288bdbe2f66a3d447596027fe9fda290b9bb33ac3ee8c631b94415081668ed54e201c643c0c7d00cf87993a5a90560d2e38d9308703a8f09fa587a9dfa767a4976220d3f4297a17b413c65c8cc7118b6ccc9248089c5a743fc600869678a514769104155d4ce3e349f01cba5b1069fac9045bbe6bee7d3f9f0653d6a74bb750b411d930faf7908ef37a6a1699a315de9979e82c5911092c5e7e509816208cbd58a05733825f157980ee8248d2c2d80efb92685314d71fd8d34abb93669945eae8c58adfad31dc094b853114ed3a860cb94ea7fb424ce844e4ba738b8cfb228fc7ba16504ec97f44e78605122346a625db8a805c3057b17f3ae2acaae29b33411a0b0dadad5c9dfac332ecde2f71bc2ee076965c2bd3c1a51510319dc1dc444a2d6d8b24283067d510f9a327ba6775494d66ee223fe4ce9435a7a94ddd7c3b40308b7d218404bc52c19005a32c8f34b108aca047d318a705221ec616f63ef2295bbfd23d30cdca705c845566b2900d39649f4430f242efd60a3091790a6a87d831f54047a450a4af442e7e1394e5a4df7a508af04ba4a7c862992b69e9c9528c68327b260ab54bc79480d19b5a8f4776b1b6f3c9a3d77f628a758c7b7a173b85007efb671a07671f5f3f81cbf4a74351038f9d1f9150aa542c4e0269e72c1dfa91ffbc4975b8665c5ded9df88744b00486416051a93f23a8d27f12870858192c99e4f69bd279abfc548ff91c20462a1d800564267e50bbcb42e39c8caf173e447f80394b516e61e9ffb40591b20f641c15bd343209faabb5398cf79674f1eddccd18d716839c874f3c68c3c99c49ff3cbb8f0d0bab82b819daeb1ff92bf7a6b81da1826e5187fc104e0075ce6323565d6f38d17fd8f5f966fd90c9b95356b0a62f95f73d7e5713302598977ffb4829bba4b8199810c44d9954af1ff7cc0931f018441d6f518cf94ae0d198c7a137b2eb31c822cecb0a2299f8a1e5e98a018e350a950f1945ddcc490f5437f27ab89eb8a51f4f06d68d5df247487310ca75790192c149f936b8e51d92d68d705d928537877880019976ba3df5144e23454a236a9a0d5ce7e82793acc6b6df18ad3afa3a0daf8eddbb09021098555f54c23d736a6825cbef50337021d4d70f0c05cce0bf50c73fcaf76d7cc1fe6f37814a2d10f222b0ca0ba30636eac8527383c5473f1b325659079f4d66ea4086fb383302bf3d0664c4121ba01b54b4f0e486bd8e879f4eee53394bf191c6d5718a82a4455025add591a6809aad2b713cf7b77fd66a3c3867074c4ef323d7ba92600992208638ae280dfd076126c219eaefda6aab417056a4abde7be17d634a425f4234d9b4afd1e1eebec684500d2525af0f3a6dd7b8541a9453c76044ef1c83b5a7753df5f10295e6a79654b64c6d1cc44eb0d3f8993543a1767deaa70e49366b85ba3499c3154fe0ae1e789b20032b0f3873c75204e0a8dfca23388a38437a183b6c2a62aa25df6f6055b9c886873aaf6cd59e8f603b33ca164cad24aac3961b529c6666d2afc7e40526c63c7b72b901247a87063a8b4490f26ea4568799e5fb569617953c4eb08c032f53a261155507468e99f1ecb071e46221a10b839c62b6d6633e95d774c4d713a39012728d2449b1e5c5fa2047bbd63f11bc4d9a10c3549dd305c39e103e52c8e6dee126d52cd846d915e28a4a04d58a2c3bdffd29d27c773faa57bd48ce3c3c36e4cd0fd3d351558f3d2ca76cea88c5a1978836b87ad4838d441d3927de4d64fbb7deddd186bfd24928e3893bb40b91b2ae15570e2116f26f547511fa059d6e79b4be85440b2273fe3138a13c5153adcb34e20e28f8036800c2ae03d0080b224ed081b5a38b896fe81e2341542261e5700177a667ef45efeb4de6c9313c541c7eb4bb5d3e3df42004ce5174e98270660f2bac400f5687a745a7074af294de4092e382437cb562cc549fb3f198970226dd26eeccc3b59b4ed991fa0cb958cfc37434d5ab8c14a9b320d588c322866d24c3f47b5ea62b39536e136ef990530f5d1a917e7d28b3982ba5d29ea7b2c3349ab61cfbcb43f2a15358a57ebfe2a51d84a2049c00cd4caea96be7fcd4f224caa6b67799b2626b2c952d4c0ea52b453f56eb7b7c4d7cd829581c10408683706d9ca517fb51b1971ff6b109810cdcd9164bc957fdfb5640c95b649ddd7ae6d384f23952506e2060330d74d6aa533d298c1e2ea72c7649defc1629b1d60b342967f49561b1ad76e3fd2fd5392c05d7ab671533479043a0a364bbb6f36d4c77e63f3041a3768ea4625d5c1094a762c6d5cde8e5a1245a4ecd1c4935c00756245ae17fa456aa8280827ea733d8507146e8c5f1ae8e0365e92d6670b3aad70538d6739fba09308fe70b655139aa79b5ded8290b0a080f20eb316ce0cd27130907aba67b83254eff9d89a877041fb01289acbce1d376833271352325b2fca2eab83563e1ef0239e59c32e252719888b54148936d6ea5737288a325c8340c2efdc70215825e65f1eb02a314465cb9df05b390018593fb959a20477d66d5b1dbbfa67af75744ba5d3913237d439bb1171b48df027937fb4fc581796c4f138f3e1d6432516d562952d6df1b035cef651f329802b95e9236157b36d0975abd396803f5273fa2bdbe8facaf7963875081285db04ef5f2eb3c6dc9cbde724b57e97d0da1723156763af6701845828360f19416927abaff0f6703691e01b798bc16339735235956b4284f8305a126e7059aca6ffee332ab50a9f8660de482fcbb28656a21cef937b32292f011960e9013c88157f70d7616afd26a15900f2d3fbc06117a24b944fe0db94c338c29e56d25bbd2a7dba3357324e6d8074e409a276a2d33159b633ed094c69250e3a34b6712185eb29b10c394d2f21fb8a1489e87f491a1f21fa085a02cfeccbbee4b2212fb54e54cdc209e1d6b5ded13c8464279a9d89c22e26c90f8996cffc63481bd22ca662a9514102d048d690683dc3d99d62a36347c91cf3d0083769a5febc877a68af7fc26c7df6f76a8af134f770ab61943ab56316548646cf10a4dedee7a41ffe8a7e0f23a4ef69bc3582c0d50d7d8c9e0dc106f22b39a297a6ce83d8b2ef372c7c3f3802eee1f3b1d84a254edb74db390eb3bb6b6eff569305011a4d4f74448ad8c151be2771f6be83340a4848675b82580309646482718a2721d2c4ed28826c838639959630f09d7352bf9fd51ba8b49517b15a82b21f1dd497aebc7b766383c1be8cd16df6bbd7982ac3edb49bb62f4c0ab096e6dc23bba51e9f029c46f014d1d41ca8e46fd82bc17b72fbee3fb5a9c0d0f6ab64a21c7efdcd337a9054a514e21318bfae7ffe42e3757bd9467bf9d6038d9e2adb9ec4125739ca5b5cb5674c5a5da9b2255050389880882658d0f397ba16bc624e0fdf176cd82caaec496a2dbc8ca04d904ddcc017eccb3fd769feafda6f280fbb80da0b9a4d6e9815791fa86de1494c15e29d19a628b1feaea01682cd2289ea9b52c742710032a2cc661c9d29471e555d124964ad04ad7ca3ea80aca611283aa2a253d4299f6ada3ff1dd61a130bfdf67a40ef0919c7b2cb825abd67286dacc280b136342b6ad38d493aa2552e7100a3c70c512a795d6e0e22cef1132cc14283e3c7c2745f6f6024ef51019c4d27b7e7772b6fcbb0d640f907e826d0c4dad45dbb0c2105f57cbcf989a15b1d2e6f11cefbe898c1c45ff9ecb651edaefb77b25fc1fc0862907cfc37bb96cc123420978c71d802acc43dab425771300ad54684c4b4520b3395453209532d256744fe33ada93c1f13061c34da0caf82bba0601d80862e5af73f197db5503c720c53cd946a88ef5d63aa93c1b247cac5932a0dcfe2a926f1c8c6a3c561445d43bf74e58bf298fdec8cf573ebb3ea8bb66b27696c846fbc477e311aadf56d6f3ac083a122e5d51eebed6e81b894aaefd58a6d88c8c885d3cab491bc5ccb7efc680f74ba38c684121861275463348aa5f2b4603761bd0d0da2f82e68ab35d801436bff812c8d34964585021a29e08e42a1996c063b8fcdac660ae7ffb905866c536ad2389991c2bfd5e110fe2f1117fb727a27591c2f4faa8c75270144fd4706329548a719c4abfa8eb8184f4fbcbcffccb41fb536a3e933492c53de5505b472ea30ae81d7725a51bdefcea3034f2175fc5c9d9c8e824ebe26277ce999fbb984427b564519b3dfb4a105070462b7ad18076bf21f9ebc1cc20847f866ade603e437ca33744a41d512e17e18d09e0b6810e56fd13bbf92851e4d9a9849baa3640e1a9ff8f3e036a1a16b4ddc5962b0a25c81be070fbdabab3a126147164c85ac859803cb9ce61f8988f54d791a5d856cef2acb73f7f3f5bff3edc227cf6699fb37aad4ae6b227f4d826201eaf892574b4c8e2bb938901ac30608104bda126ffc1f147f720a2ef9420a36905d0ddd17f98d452dd1ad4ce75b9d87b39d90f63792a037573b7b3e6dd474b3ffeb5e36f5951a338da5bf980cec22e3e55bd2969012d6991b4335b8e1b2c84b868cee82068970030de789a1fb89be23276a641247f627d8568c0424d02a10b035c994dc0add902ae671a7073f680bb0920770418372ce3df8b28594576fd526171126e895a59536acf630084554775447a0939852758ff9d564bbc472e4219ffdb036fc27ec40f4f3e69a5178684bf22caa8ac20defc06d4a6a27fdee6417f63f4a2d5211c6e6dddecedcd728624d09cad89da2b50c877e7bd64e469735d2c3a83bb14037e8396464ab06b28dd272c0796f6d59868d1c987cf3bd2bfd356b39985fba507854ab1138a2e0733ae9c16a3e375580e5c34e4e89bc39fce2a9aa7adb7dbe139f53ebec16ef24d61e173026771dc1e9d347d326d70c404e9828713370d46acfb9abf8d72a5dcc91d847a60d4d904b02556f6a8691b1fc1924a6aeedd90c23bec865337824deb38e1dcd5160c577c8dd045b3c92f8c007d64abda620d546460bc16bea30c6660e4fdc9c4e2df68e0b44d179cfed32e56038361f98a8c1d1c6ad8ff45f87ff4980abe40993d6a29c70d0c1e511c5a771f54e3c1628603870f5f0b6789a1df9cbf4b9dfed1bc3f1a909a9996e30e28f36327d44332f2484f00d09100840f18782fc04de004c881f7a585fa8cf632ca9bc081b7a62da3004e430c1e4b5ec5b6227e1f2eb25a32885d1b761e3461bc2b082eb0ed9a207fed83017c184ab09ba891e7439ff5ca51ecce239e39accd4d25ba1ec4a02090dc0df364121c8aba2a5d08922a04c29822e16defd1b4f0687a1d9c644ac3caf6c28cf29cbbd8f034cf1c18c5205d1cd6efe2a038d522ed5351cffd39552fdbd86bc8f1417e1022151060488bf3b459ffe32e121412af8940ebc5b4b6b5121e5b969f2cf793f2c6d1bb1a6108867a8bb047b5b569263bc20e053a6e434358e282abc20a182031734530bcbb840b5b6bd3a0064bb7411ae8588cbb48ac9a95236f62cb17305ccf0ac0aa31c58c152b568c183163c588292696981862638a892136a698b162c58a9161588e6d688008b108e87fbc2c3ff96cb9bb805bcdfe8948c0e33800bfb061132e4092cf59eeab1d52427fecb17846831aecaf6fef089af527801aec8baa91b458a9dcee3156146e8e8104398b3b9ffc3c58b675e0bf8b322706692d2693152fd8dfdca442db73ed8882fa9327064fa8905a510428a0b2c7d1d586ce4b568eca629a5015eebfc62cec4c88fd545bbd6f70f0a9d608a05bcb9563882dcdc95c9ca2043dac48dc479f9d96d63c86ba2e7947e1f875b031ca36eb3b724d26a219fe1f287d6ee314a66f3180d83c2cb6721528d23e917cf00b569eafe7367db7c512055470ccbe1fa2c7a96fb0084247a18cc95ea625c92d4dba92fe705ce1386418e008d95bff4583e81b747911d510d4ab37fff052412b1003aef4a053bd7a8aa48c18b8027bd6eaba80989891f21a4c16fe923533a889ae94588c1cf3c8c4212392016d9a4e3affdc2106ed380cfcb2eea1c48cfd861c906570598270a94a2d9a655f04286797d529f712219d68fdf8a19c50d1478b878ff4fefa6b2fcce8a5207a3409818a2aae8f49fc99e80875e4e31485c8acb393f6ce16afb9d37343162173c39d39bf3a6af0ca9e4115c1e362512b75afda4d6dd571736aff156593149e3bca5cc61c8dcaa016eea0d02713c3eac8783bed1756463a47397e7434a86d71c9192aeffea81c8142c78db59a8ab60c0c16dae9c361c140743b12f5d47d238a81c45ec9f47aa3658461c1eb8d02a4e410588c82586af77de8ac63f3715f4de72736b5fb3e74d6b1f9b8afa6f3139bda7d1f3aeb04197007082e7d9f58d46ebad095761cf45750ba88a32febb5fcd5a1cdc97d65c22296a37221275055d6e1f2096c042401688a480dbb09ca6a51f8d63625718ffd077ed8c898a593537526b6a278bad299080e92845226b580030cb372cb878de44072f8a503d73679fbed198ced53b49ab94e7ec0fb1e5f699f26397ee6cd60b370fcdf94a497de463a1184bee4d2e0fa76327558bb54bee1b0f146bd525b742afa458bf295e33aec0625638907dd1b1db5401633deaac52f920fa8732019b1c0c482265b9984d58c3402604d5f8f2bacc9173cf99d6ed959cbb5c16b5fac0db8bae785be0dcdd40957a798d836730fc5355f3a8cca7d8133b200f61df80c50f71ca5b6d56189fd3e0f4b6dbd65d75829ad4ee8b3dee82a47a5bff336de9d9ca316d75b807658361e25906ff0ceb7bdaa0f9d0ba2003f6062d56092c619ced13c18c8a11bcf36b7ba5120a3441859ec021b3fb9a4e67de984a65559d080c5376adb6939668a7a39270f01a4cb820c8281a417032191e22c46a80504af76f47f2eb41212a3e595669d57a14f95b1bb4e9dcf2d53f51d64f9fc37f0b955519fc03a3678ebe0eb67fd476f0c76dcdb3c36241629b4b89c995c5e12c74d195894029a302c3084dc8c272bb05f53e79a821574a563426515b665e67277a081d9536b832bd81bf3adc43688464c352f872a6c1f8934fc376f0bba829bfadc94af27b6e90773862374ee902975039d74997887ac8a8a0f085de40f9a44078b1afb130f68f05e0bda1467c99813668ae84e7e8a99658c36111d51aa4f367761fa895cc84dd291dee442a383f360ee1313168e9c4c562afb08f747c85b416f2265f3e4d71aa7fde0b2e64eb6c4cabfd2865706102b22f1ffc421acefc441bcc9fc8af9c2656bab812d5ae6d58cf0c389a4a03df7f50186de7e2d9d1afb9afee3bb335813865cfc859010cb30462156eb35889c6151a9ed2173e0bec55818b98c89a559723a6dac20624ed500e52cd09e74d8a2f4ad31197a8ad3471fec0f793320ab4adb3373fe2ef70afcb78d07f5c67a02673d8c196a2dff9cbc3829f19ad53f3b639e5b9ae35fa0a9364ec9e418503066360154bd5b5ba909330ec96cd0f3a86cd0da1b40e7d324bcc0edfe00fb46fc2510f33fb8c707e4d5773cfda14301f12802391da996055909e92249bec10cf86b15fd7f516ee59f7dfa50b3c79c29f803d546cece5ca93cc6a82c15f75b2109be02865ad1ef18272264a3034fc068ef9c03307fbb531a6889ffd350d2e6d2337a33409f2bea025ef69022626ca4188ec1036907a66408ea506c3e7102d4e92f25f8819d55246d8ab1c80d3772e9dbc94c143a9f8ba1734bcc48d6a0767292b35df342226a51f78708da20fa7a708c889dc5dc80a72903a1a4552d65c261110b156d1cc42d9adaa36e2f00cb2fd48104fe04cabe231129f1e87d72128d700815cdb95baf28d1342e48409f73054928a613721ca0d3010558fed8d07e02fba96cdcf8c41cf4c49c5578e2fe72064e40a7a4adacd6ceed5f6ca5989036655a15d6a8c919e2053eba13d14fad9ff2e0932a0088676e0c5635c0c431cb41608d96e3c63f2e1b9cecdfffd5296ce1f037e377b087dc7a396ba5717e94cf7a0b145b65e7f45e287043f5295ff626abb6df7e0749191d04807b65f29399a8c9c7b243b780226dee2b80a843035cf9ac71dbf67b97d45d664e4856a3cbaf3484b2b4a36e67e5f1d4db7c424900c283682c4ffc967ed7d96d330b5853cb5fcee478f071034a4292e64070ab538120cd9f16f26b44342deb9e2ef3d503761d8c4188c9940cbd7df10db32d32c11971d534c35e2f03d21e3449f7f9901bf620a54157f6075d885ebcb0fc11e593649ea21d1e8fefd6ff92ab9e1ad9f766501d1286e4baef840edab928475e841c7466a2e7f72cd046979efc2bc01ac46f2ed027bffbc5a11fc19003de67ab6111cd821031d0c4b3329961109b82afd6157d11856bdb6e91508d4e26c6c5fbe069b568051b9cb55e03f78289bd179705e81dc4558c9b7b712d40cf415cc5b8d98bfb02f448e23ea3381d5c16b1b4c03ff02e2911610d6eb3c6c16fd1658031988f94dd4aa60bf7218563f26eac08e9792541ac0895ea71e7033d88dc9b724b4ef500a4034523817367db63ba4bf4cbfd360f99d004bcb7503a580c246c1f55be45be15d782cc23a01cb737d3aae65d6287b7bfc3f0e848e216de472e6721849361fa826b6d2c38cfe750802170d7db926b963705031a2226e7a4f796ecd6daeb796d2c8c96fadc5fe4cb334978716ea948d9dcbf5b4e2c0e967db2be42b45e32890b8cc60ee6cc0aa4445f6c92712595089bb1927e8221fc26669ca9b72a976f2b02363a4caf1c9123114869c0dbab1e90142d17b59845006b787ff35254cf664acb83513ab777cda397a0fc5852e56747fb442261f7108c8a680db2bae66ccb6af1e54cf48a757c82ea101cdbc67ccc4eddae547ddead67468f520d5a1f7ec4ce2d95e3c401cd9dd1aa16c4cb42e8aaeaf23227100f6e0f04f5302697b120285025985a1a91abd5598b65db31185583517450bfe2493ead06ac1eae64d6379132c6fe3ab7051871076c3b0509ce4b683ffa2c86acc843573da5594b4f01e79a5ef9f5a1efde93f594fd37ee1eba18d32c21b6ba318921e1307ecaf3cb7e6f6185ea4cb7e56e875e9bd0bf2aff4aef39535008a81a89735c65c1bef113ebb4f3698e4e37e79cb094feb4fda73b13b021e9f8af229fff4c1bd049c99785efbf97e0f8fb5b77cbef7aa2febfcf0a433836c99ea4cf1eb210d3603775038ac90f00601d3635b67fbeece9df095d49e02bc663e642758c28d62639cbdf7e5c122812353dffddcc90c4ded4acfd9115cd6033b6569d74da7109941aa6b9d841ec11b8d7ffe40f98462dfa66a35e029da5d09951ef11f5ab016f7a5ae8d9e623bf0fe802412c274cd4695babee9e489bcaeddc7caec9aeb1f0acc2c055a7863f93cacda70848ac5f8ef62095f56d54211451b55beada8722d745752152b205b848141cc8230f3097cc769e84c88c66a3a120a0f924cf2dc0701ed0d4a060b9c839fcedb5e765c129dca04e747f0ff33e44842113ad8731d4136a53a887dcce0accb0a95815693c2a06947aa913202922eab237458320df8b84b1690daae4b05883e20de3c3ed8d7e000b9a8f46bdbb413bfa429d8d8d41dca95b626a58cf0e899699c605f7d926c3def34e4baa373f9c64697dd6d2334248057f62a280ff82cad72bae9046aa3c2cfda29daeeeaba0c66e866c0669f4b5a6d811442abc579719ff865b2bb11591b6a6547b3e932f4e8e094051cd91059f95bf9c043130e722c40e45f46758dafd38e845751b0420730b36ccce914f17b43320031f2ee2255af205215b8a01f8fdc306d829e0b7951fbc52ce79772ba147b2f0596831b21e5231f8fa67dfe224a05093f266ffecd7a503f844371648527364df30fdb0892e5da63e635ad11e3607518de18fa9ee317ac0fa62f2705cd7250c36742c51acaa5fe08a6e933957265f038df98fe8f66c001dd8594351c6e87ccbd7565030321a18035186260f10c8d436925ddd76656faa0def02e946fbe93ab516eb0f9ec8d7a262cb3c5384ab2ca22f55f9cda59510707223e3e318550ce1c208727861272eaa05628582712450aa85b471edf9059c7251bef6be235ae509de9868f2c92b43cde188b61de82a585826f6b2d23543fe2bd496da6efd9163875c49aee285d18729de1e0ecef36160291f2756bbfedf5d87d59f00daacea958c499c6698a7afd83b76403701942321eab9197ebe8fdcd17768f53ef0b28f9fbfc7d43e0cd06a496dde5fc5adb8357d021d2f77e60f93ab6df796f2eb32367fe0693d3c7ebff9560eb5e59bdc3fee7929da804291b1c326ce758611c7b9023f6511c3c6e95686ab62affea3e867ec8b42bb689d45d50d94146ad8ff9fcf1db6499b867e2549aa903e8c5122ed2255db027b84a18255a9eca07f09d03cb6344af1a511c0b9d4517265ef60f20140950ef150e659840b6eba4642f25c9640308706169081530bc2343f62ae5a0f86745be1cf8a536324c7a562873a587605537bfb2949e740955f832b8faf3ebead64734891bab3d47f9a075377bb248658c17a0102fc81fe7de86371a5c64dd01f066dfc0a7d0c9114dbdada67cb137062f5d088ebc3ddb90c794afaae316c51bd4ac3421a4841097575405867861a55ed0e443fe98acbe9327158c8d289c69a0c0ac8ec4795fb3cd09ec3511292729c6e624905a51776ef01a563a49a99e13c1631cddd7872502f2cb796d8d694e93c4543d63a0936713a246172380ce4b606295d5c7e19e4839096bc33bcb9c33dd1a159014c74b6a13cef532a7c06eebca6735ef5d46c43748a68fae31adb52485f33fe14a4b2958910b4a273079966426971051464c81ff43d3ab8b670abc75bc5d181fff626dd733914b515a2db416ffbf06d954c2a2e782c629efe81389c49a5bf029786347565d625783dc95833ac13302435612eba71c04fde9067553fd53af2722e5a619d2867e578da745e6a330789ff8aefa2521ab0324bfb6f3d2b8d841bc7e68e26bcfef7b8b5be68a15f857d0c6c09f4eefcc568fa68560746c1cc6cb445f22f6f4711a89dc0e229ed3fd189575c479f04539be8cc8ce4d4b9bce199e1a4f0aceec2954691cb816773453a722a9aac75081a1b64417049591c9a20bade1b51dac12557bc487081117840de0294b484379140276ba191a6c00af0d5abd127be7a79379bb80f2382eb83c705f1ac02124a75da8226961e3555f581e94c9dc9cf07dd720e7d4cecc60b158c91a3d8e1e0271caed73bf3560d07b2e4aec564e7eab7dfee158a77ef23f2ef7bac3e8a521acd65c2db712cf55c579157ca0d50ee51d822f0baa99bdbbd6359c0ffc49da000728dd49eebd2905c345bdb05d5a2d8903068e457308bbd95383d357195d98e712909c48086043f63df24e79128d8d631a6f5ada891cf125e6e402d41577a081d7641db81454f8268b03c70d2a38b6bd895c4c92a5cf45db6db25619880f387a52115148ba1c88722a76d11fa80665d65a905ad519bf69a749022ddcaa61a62ad623aac1548cee8f9e2daf5df3a3021ea52bfd234d50394738c521966d69e2e9da408aeeccfac7b00427b28ce2179b899cba7165e69156a0f801e89b2534ca5c95340b183c4f32176b77673b6a177b638d7e08f229c030a7f31817fd7414b34a093a4d7bd6c4cf8b05a9ed091e6129e66ea217a007378876faafb2adb2f78cbcdb144ff661aef32eb1400eb13f1499fd88b2ec35ee0f2ae5db114e9789056de4dd663af489cb6d921e5293a26f289f665d4609f3020bc2a040d034f495a12139d4e035bd33e051a7b544640528e5a548f2f52ca58e8801cbb1ddd510505677c524747f4d65c50a05250e3d69b49979a08a1b35f8a765247dbfdadd84339440f2178ae6dc46b9a089431d4013b9298c270a00c9a78599e97ad8ab1a39ba3e3c45d9631bc629cefba0df5d5f926113d30cb07b8112c854796a274b94d616e091acafb38f1ace20a9a3272d2019b1652f8e2065faca35bf88972f022e6d4cf04840f41ad7f80cd10a4b5365211edd61d5cbd34ce4079133c75c397ede2d43330454ecd6e526e301775792e8d6deffc59662352757f1d454d7223eec5fcb617cf523ea301f35c3694cd6d49e374e9eede901841ef8d87f118b57e2595d0a31fc40ac5c946a7314611fd65e3686450d8995a9bf32d6d2ad1bd18a15eeb89217d5ab0e81b484d15bd85d397f0b8b39474f54fb49deef37b389acfd984f60443100e4a1491135b1a7e5edd6f5845412d752535333d1536f42c0d578cfa53976bac433ae28c51d2a0f04c4b153ed763fd3e88fd1f1f27d2abe24a682d3b46151aace5cab83d996142d219fa5ff0699a5a137ca0088dd4c5f325c2eac80038590748924cd1d68befd8609625a8ca19a0ceb5680a37bc7f186b8a20f6d48088d1b53fd40554db9196db1aaf75b1530a1f90a1e0e3d2a15bfc11fd2c844d62b849e2c0da5cb09f4c970c98f4458cb57911c8c9268865a0da290e1cb075b8420e73a88a46a9157ef79a5957826c513ddaf57210ad0ccd0bac65dcc0ad39322cb62b065804d9c5f0c7426166d6c9a483f67f746162eb96f3c0ead5d492c51954a33ec5ab00699fd6260c2124f8276814555efe7e223849320e862862fa54776c2f8e7a130992f3943429964229aae23312b01a13aa90cbc30f621681ba912800d731b182ed234fba29897b8a4bdba2b2ac431c8a9d47b69c74c11b126d12abfec145e02d9cedb1559e87b0108010062fb7ad9a81ab92344d5d7defc194304d80680e19ea087cb29fa16ed7c94eb32cfb34d3661c025e5e3612c46f0458523a99ddc8ff888120b30624bb2f6811602ef08b759404e4139fe8ec5f11fae95e8eff74119a56b2e2580e23fc21ff980d3969356b22173806c462759194a11b3c3d874f50ad6c48d9ccd6cbea5290a48101b5227ad4701d50cb691e16a463fccf22561c87bde20ec2636b352f0883756a22087c18ce34fecceda57c4739e9ce9e9b3f822e8519e56a4d815aa05cef58e7ca018794e6fd5d187b9016c5f49b609b104e5bc8a403449cd6e81411f86dea49b2cb0f931dcd387097f96608ddb25e590884663195adfd2b896eabcfa24b44d34fd14c660a13dd9baad3f8975c8c0a3c60c0b9e2b2a0db570b1e8c3152e9710d268d2bd6012ce55ecc0bd5dc6b1247ac82043ef3dd9819f9ef6febd8f2a03d87b1640e977c27666e8bfd7246a7874c86d67328e4eae887ea474d7834f9605e257995760f382f21c3ed009550d088a67c2c55e6a64a76d2d9a06f5d7d0d6d0b7cefced1ef61bf1bd7b16366a32eee4609bbb1caaf9239ac971b227b49b698c9a16265d9137081ca13e93bf5cbed9570ace297e130a2d543f8fb5bace9000fd96a2fd1f2f67958fbf6fc1a47eb1430c034751f370dd3e90bea4825489572c64525dedcd8a3fb7fe2077e11bae4fb60b7f9c7a0a8bf4b22b59594c77932de0bac2381ec5666b6a6bcce582dd35361374a5effa88e43094f62eabea099315d248e02a6c30ecb454cf130f2e8e6080e6dc06bd396e3566742434f367a82e600a10e8200bc9f07d1f037ed09bea8ee76d912b3befc39c49898b53c4deed90f4c006b5aabc82781c39071bb6c9ec0dfc5754a41bed593e38b91c62a26f7d8ac17e8c0471f038ff6cd1e996c2ad78d58ee8b896f3133c1a4c778e56b7a28ea176856b739238f5ced894fa1339f6dba1a74375026ce78d8c5c83a8dfdd1399a7e35a0b9ade2b9b4e8fc080498f622ff26c98935bc5ead67681b7b0929c2977062b81ae00409db6dd4c3d0c71f15033fab7278a6935f25c67c42accc65c30dba1239fe012473b98b3f170509ccd8182ed4ee993085d91c1ebdd87b8d3674da7f016a40806bc2edafbfa7e248c4ed617f797a8cf88e2973cee970293e170974a5ebb18efd7c29d531c9fbd53d52635d5b50d18419cc82a9f6e3e1230bfbcc455128af81daba1f79b15f8d7e160d276670869b7d6f40f89f065d7a42d59b83e68432e220815647c68b86045ffc47fe4bbc48e97730a58fa1f02c8a5228a46c5d320bc2a5ea17db7fd8a8a8ead05c42ef5388d6aea5269521500d4ad5e143bb649ece3810c16d8bec78c903f4ca5f49475b0983acce0cd1fe762114973b038781a73257ab20ec10b3df8377711536d425244fc121f4fb0a5cefe4f907b8ba459a88521d2e4bc09e5d44a7c73c0a4e431f8ac230798f03a4e540f4f16320ce640ff5d8d9e31801514aaab50fc48081df01c71b89d72eddee7fcc3fcd61d6b3f7c3be1bab9f1e5d9ed45537601f57edc7088de8ed029eae5d89115b981d7ed5015a812e6e4db3e89289611beae7b91fb77b93b15429ab5eb6eb9fc1ce9f0ef28581a197ceac0b97b532b8a48f1814caef3603334d7ebc31da293d760c7c2438468274e8585bb86c42b1f64c7f829b6eb337cff3a016994f04145867261071ce89a2bbb4e88350cb0d8d7271e296e65e4c8adaf5764a580aaf36136f2e9fabffa39bf74cf568a083107b85a544ea04fb94c8106cd075bb0c5aca62fdacff9cf1b9d40b40817b3cf77ed157d6f66b866e3f471ccbaa141cc8bc2a0ea79167aca9180e8d0beabec4a6137ae78e3f622f088f451fd316aeb074234eb567a560ddce56083fb4365993de2174062394438edd07e1d5b4fe879b6bcda250ec90201930f567ab0a33957c6c4d50c169eb1e3ba75c4d2bd2ab37f698f97410a8440135025490c3ee7826ac269bd9dc1cb75d23ddf3b7ee967b6dc5a0d9c68c9340e74c1507c8a906a7008da0548d1fb8a48652e4df4084aa392b9038ae23a724741f2459c2f6e51c2eca3eb5fe07aef355666d37bdc8d752158ad4096a7c20c3fce9e2f8eaa0a4d4385adfa1e950014a75681144eca53202608baab1952070c378c6eb275c9a43d2232e3d58ed353391608df785f1c4c15492b24b57fecd362d03c749e3244d6721b4968c82fc32606d06b65a816192e13d49609973fb4fc9e21f2ec7e393438e8366108fa2842250d795f99b5e46bd1f28258b7b639cf95710fdbebe8cbedc15477d07090489fa3e3df4e1a21e1a3c6485b240c262023004d7a359bcb248eee241707a20cd6b7f29ce9cc2202b917a027e955dc3317857d53b54713943ec9936530a95087ab055f9f02c38a751acb3a26fd4c17ea0ec9be458bff5a5ef551673b154560e2c209d3fbdec25767a979a0b696dde502fa6ebaf87c91602664931ce87b2bd61d755b3226bcae888dcc92429837293ace95f6606295a1e713972fbe7c787e901cb4cd9e3f68114c4cb9fbb2ac16e729af48a8b00c508c7c8972b07832faba543c2f77b6299224106e12058a942128a39e8d395f990e1341ebe193fb11b014dc6d459090ad5bcd3320b3bd5def9abadef8430607f6bcb231d22ee6165026e4b6b9b2bd0574f06634c5c61e6d5662dcf3860c0bc5fa307a4916cc26ce8843aa423c386dd1a3f0087e385c9c46198f00dea0f1b18e8a85f5f75dadfe03e8aca7e13b094c2045e9ae3ec95297c072bc383d8c7e48d07485a5d07023a3d76b00960866f50fb80f1710b4fb6b02da84bca9811137cba5daf270a7b1ab528ff05212314ae6309bd3b090607cc3a99dd4e15cb096d9870304ee060b93bbe3f5d91c3de21a29515a64aa6309d5632d8fab9553b2d1e004b2abb134b53b4d166ac5bd32c321efaadf20c7edd2c2410a99cf6e36395731df9dab371386f460708f307544be5e996989d92aedff4d3f0b1fa81d25146ec45fcbe9fbfab023aade4c1bbcf39654a08d5720191c62c898b78ff54d64cda898f0d076c1cbdbc9b6f74ce178b8430cc0bfa00ee9b1a8af07e2174073016746b2b8407b193ec736b1eb36f5acacd74d10f2b45032a0dc2d59a75133c2b309868faa2fe41fa5f19b3a45f261044619a061830e579e3da649d913d64bf4ede981abab5e0abe7b8a11cfed72a69e9ee6b55e3ff8953c129270bebd46c13b32ade1e0319c9d88a37a3b3d613011241cd00e12878135a12decc8904c8d3c8a46371ad96c99d03773e9b228d569c144abcfc398708431a9242a0143be96d720e85980045ac85915a916040908a9be54b241359760420911a8e19df39e6285e4d0687cef18df1095c7d58b6e16a545b421ed1ffe8fd2041b33a9316d4a49c43b9e542bf392316e4f469c02ccf3325190da3a4975f22a452af7258c63361a05fecd94830f277897cb950c4cac5c4112f279f77234eb569e7977c0b7cce4ad498b53c1577e06059e7217ea200953a74d618bd2c51ff06fa1137cb1002cc36c72c0a9584d7e2f934df610c8a3986ca1cbb3caf4702d43c59c2bb1a3ce4feab069e9767ef8db50acab5b3d371f2e6af8637e37e48e25e4cd9ea044ce0052ea7064717591038c98e7355d63b46e97cbbbbddf79f5b82527ba9d50d898194ebffb16334fcd5524e99fe3f668d94ce6f7f22dad1420383b52fbb2bb722238ffe500d7a50d17ee4d95c7e505135f21951fa9a02eb297ca0dbdc5b371192029d18fc16a01758a72a514df1eeb80f59dd7b26650eb8e101c2444498c7061361fc03f73637a0071e7660800e1f9c3daabc5c75834d2e99151c60881608288a2438325f30c38d2656f0410a1c3a10992558c2c7860c31a0f003ce664a294030061d0f8eb2a48063de3004cd14434c1e9ec8e1619eb075c4e48192052851420e73c8830d26c10fc81637e40c93aa8a13062f688801460612c60402c3690315295081e7867499988a06579c385831c1f9c2849db04301ae98c0901832d8a25c8105884d4d0bb0e1e8b4a2a4e2050c4b4860c3028e88a628279003093eb0432245040ee0440943145024e0840b1aecdc9628457852c3881600a0d9419144094c027b0086102031de5044d9d4e40b2d6c6451838b22889287024e0cb1e3a540080ff850d6e0e288175c053c4900121ec82f6e8081085b1343287060037902132425e0020880d2932206520306f0e1039218b80025022d903c4cf9d91931e5004982a040ae60c2111356380283114430218b3ef0a6a0e1f8c44c710989240b1723007cf820ab844e1ad5c02a00012f5c5124b2923d4a40b620cd6048001590312b38a93838e0a8e2ea3f8b2292e8808191968e17feab90a081960264b0836ee2ff02514ca0855098d2430e35b000ad81678922bce8f1df2401503d3ab0f1420dd23fcb0408846cd821055bc2ff5d0a20f47800002a4032fc1792e282070080448c4604fe77882064c888a5051726ff660fc61518c1c30f26cb952b4bdc7066f1400c43bec812810f50c0c655143734ce16591080421049b8eca826495891058420a86849579865b08229b2a0600440025990f01b88e144161d0378f04080ca5708c112596a5320398bf0f103082c892c331020248a23a80020041e8a0803061d88c20195e6e199401061403180162cb00c214323cb87308aa8c0d1908514728041091ec2d0018b0e3d259af40f5ab0218c147080e20209f8818a1f5288218c0e050c8a3c8637205ca085308c1ce0a904145400a2032a14c210824410597c360cecec9884c141007ebdd7ba84c8094bc2904940023de4c0802c5b3f7430aed0220a2c2c9820032ee86005830905ec00bc8ae995808b140c209ae061e5f1c1ef062648c080c24256023c60010e9038000f8c128c78e2a8470621e800104d60643199b2041420384208902330a2202014f4c2c70f040a4004c6cf1536d9131b3e78a0c206060e282628d101178670610b1c307e04bd040e7030c24d033cbe40819303a8600b2d3d81caf1050788385b0072f2a22a68c6170f188148c631c9043c6005f3c5011a90844e8521c61d9bc5f505005410434d7183169cc0812fbec83268f190420f382021812dbe480a8114121b1eaa1748b0e20b1c0250d1a00727217c4134c5173321aa54b8029725021827bce0224bf1015f08118413504b78e1c40ec3306c68e0852336f0ff1f46125e60000519286106c900090a124822a9a535f8279962c4c0bc9032928cc13f0c30a0e4773c9d92eea8a7a4dbf5ae704e384811beb169f55a03bebfa3cdefe5c61b1bcd6edee36aedc6264d732fd559a816a2b5dee6e23a243e7f5bf53db1ef777011ee6d666b29776bed0e6637d5390675cf6ee73aa84e7bfa0a7cedbae839718507b66859e0c496131ae8822a089b2c340caeb0910415978266e1c520320a81111d348078f47004304a0710d06a99304015192588b4c32c13441d2183864e00aa902001ae102108c09127b525e4530a34e8c0c4c127841b4174204a098a7039d11202255e60c5067d0a1d191a4260985020c0a5668022f267a0cc140881c1162558800b2c688558900e81a501e96a410e8b3b52020f5788b08109596c0cfc177f5af8971510f1244fceff0e265b6fc60cf97fc2ff312aff3fa504f2c206fef58213c8180de971a6e453d38e17f4bc5760efa8a63aecdb6a77a33b384811ce7dd5a9fe67775a52cdba1bb866161fe9a08274fe9f68c7cea36eae3dea66d4c50555a3bd79a88b13eae1d13c1bfd7fce9adf8ee65477540f8d485d0daa47c73e649138eae290fa7eeafb88c7c3ffd74e1d3974a8a063ebffc6c6d52a50edad4e751dd5583fa81cfe9ff7a78e1c43b59d9c54dd399db77b8669cece1c2dc428ff6ffecbcf61138395ff4f9e38a8fc3f99f3ffb84ff2270e340325de84bdfedf84fdb801c6e7f4b4dda8e63eff557fde18f2acdad3751f737abb3db992463be40d1fffe40ed2858b2b78fc3f05aa66a466986694668efe9853f73de694bbc79c72dd1e73ca8f396d400f41fcf0ffb03f6d40e0b4e18006fc3f69a3871d6ce870dab0c10694ff77f56fbbfac7eadef97bd4ab2a448f6e7edda39bdf187efc3f0cff020194902ffbf4cd2bdfe3244d80f23246439e1c60ebfffff58213e0f87fa81abe219b3b37aaed8b3376374e350d2cfe5f07dbecdafc49438a10ed99c5eb4fe57c0ff16eff1fe74f1a286856ed386d33d63da758bf5a2a100b83ef6e35eebbc55ca5f99386efffb9ab3b9add2c44b18a79abe6de6e14d31802dc386d35d6eedb33abdde149c3e63fd8aaab9add7ed2f860abba9de35477770f872f6f5fec6e9ce61fe9c2ff17f973460bffafd38d62bdfeb210cdda6d5f9c6a57f756e3db3f80b6c4a75a732070bf84f8fffc27edf79a55bbee394db5cf7d8ff00fac5995a77b3ed244353ff97ee9cf03241e203152c5abe2453d453d352d190d1932e43ffd6d6d6d6df93cd8daf200c9d6d6d69691abadadabaaadad2aded6d6d6d654d4d656d4d3d6d6d6561391ad2d224b46465b5b4386fc3f07ff0f847940ce39fb7c3e5fafd7eb79e081071e2449922409122448901c3972e4881123468c142952a4c8d5d5d5959595955555555515151515158fc7e3ed76bbddd4d4d4949494945454545414141414d4d3d3d39393939353535353131313131311224488e8969696969494949492929292909090908e8e8e8e72b95ccec8c8c80887c3e1860c193244881021428a8a8a8a820409128488888808081020406eb7db6d686868484848482828282808080808e8e7e7e7c766b3d97c7c7c7c7a7a7a7a7876767676747474746ab55a2d27272707070707e7c78f1f3f7cf8f0e1e3e6e6e6c6c6c6c6a6478f1e3d3ae8a0830e38e080030e78f0e0c163830d36d8a0a6a6a68686868606638c35d040030d76ecd8b143870e1d3a72e4c89103070e1c3832c820830c6eccccccccd8b061c3468d1a356ad0a04183c68c193366d068349a0c193264cc66b3d9799ea78c8c8c4c8c183162c4c4c4c4c0c0c0c0bcbcbcbcc86432998b8b8b4b4b4b4b4b2c168bc1603098cbe5324d932449f263b8bcbcc85c6031302fa44c8c1853e622f3827991c562c4c0942e32315c4fe274bb9f7cb8dd6a1f422df97f0ffe6c9962cdadab7babea68a29a5b9c8eff0feaa0e5aacd283766160721c2adcae25baa573503797ef286ed9fe411fd93523bfe491194fe491386fe5f043dfe9f03ecff1ff933e67beed63477f350c7d653b3d5767276bb5d21cafbff9aa5ff96e8dfddf9ef3cfe776233ff4624270c4bccf5ff484e1896ff9adff3ff8ffa136680ffa9abbb7ff9ff69fcd3fe5ff7ef9ab94c19ffe7ffcbfc7f8c872163f8ffa93f5fc6f3d55f55f08da7edbb7f18b8717f37debdf7ff2f2effdcadd96a3c395cf7230e428483ad9a6eb44512f660ab9b5275cdc4564726fdbfd59f2e2c9ebb3577a70654e3c9e9a84f0fcf8e4e2d07e7878f1b9b1e1d70c063831a9a2c59821001b90d090501fdd88464c992a528cbffcbfe3f03ff4ff5a77902776b3adda84e796a3e3cb59d1c1b90e6f9d7df61abe256c53cab7d8528066eecea1ffbb6797dd135a3b8d5c222866a29a2f1a739f35d5d871cfb7af69da515ff6bdec1c69f250fff3fe3cff2c90771356fd5ff8ff97f8d50aedcada59bd54135f12ca36eff5fe3cf92c7ffb77ac54458ff70babe5bc8f10de6aa0d36eab455b1266e56bbbd612ef2ffeff927abfeffc69fa417dcada1ed915f60882bfe3fa6df3513b1cf75917cb8cdb8ddd8776333cc6eb17d6fb7bebe5c4ff970aad735a3ff8fffcbf4ff67fe245be06eadb340d8ff935fbebb5d57e336e354b7eedbb58bdbadf6dc5bcdea75c8aab8abd9ed38d8aabd67d6dd409ccb98ffcff127d9f2ffb33f491eff5fdc2bd0d5c48d8f5d45f330a3dcada19ad82b6617ddeb9a758e6e767feafaff2fd3f5ffa510264c4840024a2e36a4f07043012496443d21040fa8842d05302b920d0d0a84a8d08a2db5183cb1e26915800d2e42353709e0a142bb31809a39e4473c8460352941012d21f8ff2032fe5d02f8dfe0ffa9f87f237ffeebb95bebf1d1a8ba72a30ad4e9c0cd7b6a42b59d1caeb38e4f8f10aa877ee9d0500f108a6a9e1c76a3b5df2bec6a500775b9ced1ed5c0d6a576707dd8ceaac19ddab8ecb5f1d77bb2e920c1eacbcbc94ad2ddc9b332a204293264c9ab86a4cb0b160c912104a6c621048ecc24b7ae0c5300c2f61d2041bbd782ef8854168c132fc354b4cc046af9a2520c0b0aca6093632b14b0d082d354db011895f38560302ace65563e2c720c0c8f6c5b6029b0a5acca0d2a8a4318134814913135c353d6809c19958eba5e04c3ca4b49a39ff348362684afc429246256ea9d98281c12e3199eb63a44a170d131786c124cd072e982c5d625c2e3426083132b18c864913d2c8c426083172e18fc121e5c23198676218fc42f346313158f6829b9046241318fcf2462e4c60700c26693e9061d2a5e6831e78324ce20f5e30cc49f3660c8ea4188665c56a16702c31ac660147130bc1b970cb1bbdd03079c12d344c62c8989a2d139398ac61f201082696d56c994c9a60a3186cc61839b7b6b6b68c4c1c43f30108267ea9d932712965f2cc5246d384349261179a26a4910b7ea16162821023196e421abde097922606933430d88c41c221e5c231d8888961700b0d1317ab108c626a3ec01fc8683e78799199d08396101c0c9346b62c4eadcf8283d5642de058e2051ca34a172e31cfc4246e898972e18f8932318655c086c5e9c2e68b24895e6c5664bdb0aba6e63fe7433049a697d7325f6b0b9311442871e9ba911270b95c2e92245f5a704c6b09966b09b1ec267ee121e6cb7c2d211202ae2556e3a2c97ad5682dc1b9c060b8c6c42a3199d5f22e97699a2ed3e572b94cf33119c324832bf1932f1d5ebec426560b76c1258dd6bfce74bb0bc36a882586e1978b466b069755d6e064355a4b96485c0b960b93355a4b70257e99353093c426cd63578d964932c9e0caf2023c6038b178e04aec2263b8c7b5e87062c172206b604e264e2c1b1b1b1b9b7205371e3f0f54781802838087281bd8e0cc5859b3e34969033523868b0db21c42a3460d583983062b04a419356acc40a2316306b64143cea03133f39a79cdbc6a6668606862332bf8f5382458b32d70666963ceac5899612163cbf4c009c3002b48980e9c484833353b9ecca0e1a280b3059bf84507163388347ad998a979bd666ab462382c1d5f94363b7460296dcc2733d87489d1c53056d406ce9995b79283e6cd1b1a1e3566d0a8c1b29103cec42d3ab0cc60590c52895faff255beca1a1297361838b174e8c0b2a3490954c2502881604e9e9440387024568967de15a306eb0507ce81b17460b141c5061532070e1c8bc562317c63268a8e26240e5e0e8c0307ceb124c74c1417b691c3df4a33280736cf9336a3e1190e1b3ea470c2e01c58aee0cc1a2b2f5807161d5fecd8dac144c71553c795a5a62813470e1c2b5f346660367c2881b0de460e3678b011854a8d297f2bf1cccc4a0944039b7836c5eccacc0a1d4da8e04c1d58a2e0cc17cef1aac1218503e7c03c1c3847cd921e5e78490e8c4307c68171d42cc18175ccc4c0583a9ad498f239a60cc6d28165666536c5eccacc8ad2c62473d0e028310d0ea91c5807c651d6f072601d38783b300ebc03e3c0b01c76e868c1396870b4c468d0b841f3c21f43636666668606abc435ca174d08b8b246cd526963c66a9a4a1b73f6c3e7983807cd4c8a7963621c342f58ccaeccacd07096786605b703e3c0582d3930d6a90363cde0669833f00e1a1d34396870d0bcd9426306d3c0356ad480c16aa6d86a385fb865666686c6c68c0d1af84503c768e021660d1a2df83505eb85840c1aac971513cf66566656b0ce1831332b3813c3d060d9f00167e2171d58b09466d0927963c66a5c5f96ae182f3ca484bd6a1e06d764e20da4cc941b50215bb00c2e2106c7f8a00717fc010c96c1b0181c830783656a78b0d265e3c08905f3ba62d660b95e6ab46234582f34b61ace182eb18b0b86c94270e118cd10e7c2434c2a2ee58b07274ad0f47558dab866aecc6099a91244831f9f343430e50d9ed1e2fb293ba01c7162c988e22a3dae658a8c28244c4a0636b0c4b01e8b8c2836b0d8c062038b0d2c36b064009302a302b302930293022ba29402ab02a3c16a99a902f359318305aeac495d32d8158374e216fc5283f5c29886a4c1e60e12d3608c793418634c5343836bca16dccc0cfe18c63271376ee017cc0e5b0ae70ccc01e621036f1065c7941208268912c8dcb1044c0a0decc234a4c0f852210fec825bcc200eb04be9027381c12e2676c1641045535c300f2510d65bd90165870d3b72288134c0ae1a588794bf95d8069ea9e2c48c95294a20197661d9c08273c98802438343302a31a6c13c8c6938c09887310da6a959c203e39a108c4a171ab306c67a2c3630d68e293355669ca099b132334569e3d2c02461585c31514c38c03cf08b2d89130b86c57585f106f88a06d7e00f7ae0f1c035f8030e6a70c0f00618e30d70c9a306875923b501c61807ac86b741cd9b37650dc6c1c334b8a6c6c50586062b841d3430fc3130a669c2b00b4c29045ce92b6d5c349f4aa991414323c3898c03e70ccb706206d1601796d104e881c0b08e1c38f0bfe00c645830524c25702faf0ee05ecc580c528c4c5633c556c30953f2c3897389813fa6346942c09531602e5fdad4d8b6c022fb320696125c896964b00e9a1c3458305cac464a0b2e831a18293817be4183f542628606eb65c5060dd6598306cb95021a34583368b076f08075e264d0ec9802b3cac0b03869646860569436569436ae2ba58dabdc41c3dfb8f0073e4002123cd64c959a97e2c4825dc1b93079c50c72b9b0b02971623d96d74c951d505e62481951cc1b172ea7c88862069533333833ca0e1a607024ac660794cf71613d16b209f9c465741997905b2550894db3c646e505ebad6ce0c49866a6ca145c0bc61296362edc69687640316f5c18efc801e7d221e5735c184b46149c0b63cd54c1b9b00c2932a8e064447161988c28327c28817e905184d982b1ca9a9a9286fc9cb2243fa72c67905fbe2b06cc45fe93a42b060c0f2971b0b4c48287931c6507365879bdfc60de98980a15f385c5bfb09041f3a2f2f22f8f1413a324c330d7cb5e686fdef064d4bca89837af181aac107031250dd66b8aad86b3c452f203d6898b815d3432f86348d7fa9a021365888379c130342ea5cdeb3525c9e5c2e285e585050d678961388667e016192d334a9a970bd66b0acce9b22213616605c3c0349989802bf10c26075c8961606060609ac45eac3489c562313246fe808b61324a2cf6b8980cbf6cfd8dc42f58cb0f2f380423f205c3c060989a253038c63469609894d560bd88802b3199c313d813580c250c0acc86d206f6e4c5cacb95172c5eb0b85c2d2d2de40b16028e5c5f53702f9897590c062f36254ee112d64f2149db94130b0647da3470c29464165c0c8ad385cd58cdcbc5c56682d3854d18928d8813eba7cce420e2bc122e352d45bd13b8d28ab9530299402e6cda702517a50c8bf30733a8458a39055752c195af2955708f5bbe0687948bcbe3252dd8a5a685275be282652f2549e29224adf8500291b802641519125440a68a0c0b1916b22d6458645f9436322c70257e4d71b990284dd3e572bd5eaf170c0683c162b1582c167ba2e58996275a9e6879a2e58996275a9e284b927c79894992810be145b634b135616bc2d684ad095b13b6266c4dd89ab035616bc2d684ad09276c25287348c19919d89038b15eca17152a55aa60b560c161450a961a58660f38737072ea307bc08b14194ece1c6a60799152038bcc0ad992438a6d03270cc732784d318362b8acb169713ef91cf3c44283c60970a679629942c3cae7982f51b0e0cc269f43bea6d048295150749a3ef3a6c4c3d2a604e238570d960a33c4097124ce004b09141c240e01a723b19084808b2599b8184652da947846d66253e274f10dcb12f31617a2d266a6c49ac19ac19533a54d69be50c1b9d06011e14a01e060a04c8121ad98266ae217120fe43363b097f97ad1e0d099588f5f18c703a68993d284a18179a17982838162fa4a2018013c0a8302ae84c9410a9624982630293c4c1b959324732031162c8a8c0419254e13bfbc7c10322d3197982cf6125b8ac55e2038499c8cccd294b3e5a7c082804991029342e2c80c04018bb5b8c8605260b0874931cb9794571945091363c99861a97262c98072a5451614038633b822030a0cc7d4cc9c35301950aee06440c17a2161e34a066440c1cd389165a961e5c546e534c99a580c6b8509c149e2c812f6301bb67860cdcccccccccc5ca9f901576297ab86159c6b0b57621b95d35432f1abe61cc18935abb29543953305b812bfd88a38cd1a2c181cf99aa28a0f257ca08aed03568aa698b0a2294554801842c5344df2cd182e5c62184d0c9b34a5091b42e5158364e2b22c61b017ecf57a0da9f230292f2d4f92640cd410116899598688e0a5a605c360170d964b16c3e57cc578c5d40c11c1cb0bb605630213533e3cc53cd5c353cc533c3cc53c3554cc5342c53c1554cc5340c53cf553cc53b6629ef229e6a99e629ee229e6a99d629ed2ed7e320202fea0880f781f0ca5ab56938c70755f55602fdd1a98779d6bde921b485c0f8fddd56d11cae9d633eef179313602fdd87c7a8c3ca9ce4277936ef7134a6c778e7d533d35dc6e94920fe8beab769db76a0e1ebbea6eacece4eae1bbeb9b78bb1dd1eca29bd53cdd7715969e4a3571bbc3212ed57dcd7c0fb71b85aaed7e723590b7d9e5e9f5a706db37a9b79a8824d4c3f3a63a3da5d1763fe9a3d4e8f8b3ae3894ab4e5a27f15f8fb79b673d6594c138f31d741555d7a151ffcee37623880080210d510a776b443595d2c3e7f0e2c14182601edc014eb27324f79cd6466862faa74fcabd5111aee7cdf116df1c98bbb5636f379b41cf81bbb555c8ab8c200408781280a30dfe9f0655d7a1dad797c5c15675df14bb6e46717014b85b4333cb3520e2c9280de841071cc8ff305a48184c0c087caa87f4c1f25c6829a1c1210f2f0e9c439caeea984c8af030a3bda26e5f19f8219376cbff4b196287a7c693836ae2ba6696b72c7c6be31ca76dfe7f212ec0db31fd608f4e36879aab836e5e3157f10f6bfe6654ff70c6ec7681f8873b9addfe7b39eebb6d738ebbb5dfcbd72540b0eeff202cf0b9e7b4b72a555035fa7f99f5cf2c1bb85b6bd55eba59da99b5c3dd5a9a661dce0abbeaeee09a5d6e700962617917a816991809b1251f337e9982b28a124879d300114e55b6454a6d69d5d7760aad3c0dcc8e70c8298ce1ff69d4deea760f8d1a6ba2da7350b7dcad05d534a7b76a50fde5b06fcb12917a7edacad5bdab455eea6e94ab474ab99dd14637baa78e5dd5bd97856eb6e7a7ad767a702c11a9c727b7497a7c92ecf400090013fa7fa83f7511ffaf81d8c7dbb747ccf2a57afde55663fdc39dbf2c4edbacba39479d15c413dc6b9453c7d000bde3a3618ca450fe7d6a3d3cb59d9cb6b8dd9f906761ba6429ddafd9bdd3ed7ee26d60abeb3c37a95d6d754ad435b76a12d2711d0e719a676114ef762b4af570a8ba9ddf589e8b44a40fbf253aaa5d77a3b8ef96630c5431e62ad6421e06e2ebb9bbe74bb590abad0ac4ae4e57f535e26e8d7795555706fc3fa9c3546fc8359bdbdced46ecf1798b5c1dc57bae1e1a33ebaeb9efde3366b688c8985954e5adea76a3ee699e7f453d3e2f91aba38c99dd2cafcdc0ff541775830ded56b9ababb85b13f22c14f22cf431f9efee1672bc77c737cd5d4dd42efeb377c2d76ab51e5daf383d6b3788fb7ec2358ed72cec6a11075b356ddfdbaa897aeddd524d7435d7535abb492857d9db8db73a6bf7db8dbf3df79d46dbbc66a317a0d3032b403c18fe77aedb9eabbbda63b70beca17ac875504df4c04a4747c8b3b07d6fb7db4db36a6f5f37090e496c48e2e47df8e6c3bd0f5fd7ddb9ff8792800d6c754777f79cb6d969cd7a08b566d6dd4fb75b4fb3d905f2638ee2bac87e4a22c48f394a6b608f1ff390f3bce69018200618fe5bbdf65abdf674d7eb90dde89a51b5dd6d46dbfde494aa50ffff42aaee54850ab6aaab5317aaeb1d02581d35a5d3ed14ccbc60e6fdff13d305dd8fbfb6b8dbac962a9879ffaf822bd8aaad9a59edfeff013ecd3d1b4008d8a9fedf4930f7ff6f618576ab5de57bf8ff2cacf0ff69ab66b9db75917e3052442b51b7ab952851f98b79c65738a7db4d2b51b98a831061ad44e545bef87f15dafdf4ff4e52d0ecde9d45a228e204be19776c8299f784436a8282329a425242e23de974464a4fba29de0ef3907839a8a626aaa729245d928ef78474a4c3bc1c121123a8261cd2d411120f3785e444f5842494c4a342ca3d21514921ed78483b547535fb3e45e955f38cba6f9dfee0749a5579391e9566555eaa95a86b7675ab95d4aa2827a32b2bfeff087e82d2f170395d535414ee4acaff119cd325e9724851bb1cde1512ef092a7775430c57fd0a84f3eafd7f1b9ce40846e2e1aa74535137527f5e9dff59bb2458e5f0dcadb9baab6e11a8b693e3ead47cc209570cb11c9da7384810d4e55062a842f23c3807b86a8e8304c1c29a0ea6ba084f97c345e17298081b53951c9e541bd01966b655b330a70a5b9d90e7a5a222778968cdbddd4a54770b8b7ae9ee76d3aadb7b434e7473b12deea320676f3754136fb7b4f3de90e79ed39e7bbdd5bcc7e725ee61cf98d9dcf3519af5529ab570a9f7d4db5d55310db393d62ecfe869a98738cc3d1fb55bad4a75e72a7775d4babe49fbc9d5c20ec553bdcb82ea0d59d478cca84e7b6eafa7d9976b77b8ddb4c7e74dd72de4da15f65edaea217f795e8178de5ee7ae0a5cf7fe75ae1a758fafb90daabcbf50bdd5ea5045b55b7493a4872c7a85719eb2d0bd1ae59ed3dc35db635b9dabd3567329d7075b75fda9bbdb8d458dad1a545da828a9f2a5741ae20027d4063d0755626e75b476fb4f4787c7860a05f1fca43fb6ec030464f3b1a168f6497f867c8078843430037b343003519b4d07edfc75758ac4b7eba29b540753d5fde1304eaacb6e8eb221427515477bea4d8fcf8b717a7c5e1bad44e5bd54c8f3d2ed96662deca15a6c4f8b4d95384979a73f9d84ff4fc3a370ab75b1eb1fd63c6b176d557c5cf13781713661e0cf260dfcd9d401e0de6ed4516f35ffe18468cf2c579dfade69d65347b7b7486b4eb39eea7bdda5590b715b49753bd7533acd6e70489566bd846a3969b1475a89bad3af7ea58468cf43ae1eb5ba21eb2e71aedd20d2907597fe5feb87fb7fd56d9b85b921eb2ef1ce7bc7bc6af776ebf52de4c3dcee74cf7d176c5516dc407382092135a4800a24722001d1111ab0105a24209b49400915b0059494ad1613c85182143f702053421302689cc4f41840061f7cc8c962e600331e604287231040c01b401013c0c82a2711277f6cb390670d04aa29ce5813b310ef8e79aa758b85a82a2c629ca3bb59cde2dd5b35cd7b4d75475fb7f376a35cad650cdc42fee2e30fa71aeb5f9bd1ffcffa7facff77057544ce5f7f3fb5657bcf38f79c627eccc535e354735528e4598875ba5b9c7dfa5d73d5ed96eab608a5d38c62ed66acadfe5ffd7f6170886e66f866edbd9e4b53fce3dbccffdb6cf04fb3c43beaacde9ad1fd3bfc3f121c4ea5206c359e1a4f8ed6e9facbfd65956aa9ee5d65d98d9246b52c34ffb68bf50fb719c53d0b3bcebccd6ec7ad1e6ebc9fc0e8f16792137d6f17eb607677e730488232c4c7bcea292c49ebfff71e2dd73bc569d642dc5bede6289c13c9099d2c74afbf9eee99d59d1f794eab66219add3cd4e13a361ea0d640a90e0f104f06f204d97c6cc09e9e9fa19f9c5ad75cedaa51e3b5ddae4a6c917eff9f7dffdffb7f0fa07c16ff5a209da956dd8ee448aaf131cdda5df3bf12ad97e1ffd13f8f903c776b3d9d1f9bd0cf860ae9f0f4e49d201fd496faf8d8847674500ed7e1fddd3a5d0d06e04a097255da9f9eda4ece2fcdd1aadbaf72cf473a98a3f4ab5fadde904d7b5daf6b1ef6f8bbbeec8be69e8f822ab0dd390fcedc90e7c1563daeedc669d62bde1df3aefe706fb58aaa40e0cb71aa3b5015722ba3221ecd4376a33f359f1c54a71bcd3976d5cd43a03f8d98fca75a0b03d9f1f1a007b759d831e77a3db61973cdba99bf5b256a370aaab7ef467b45571377dabe3ba783392ad5fced756150d5566b7e5bb567cc2cae03ffcfdbfd8e876ae2edc6dbad7654afeb467ba9467555aad3f6dd463cb81e9c0fce86fbc101e1827042b821dc0e0f8e8707e7c3833bbabdd55d6de22fdbd5a6bed39cb5eea8cc66fe32e5350b99d857bb5ad8917255b9aa28275dd7bb9eb59b9b22e6156d9152bdbea916a2bb54ab4095f3bca22d95bb87acced56d50dda59aabee16f637b754ee765d2460df75ae5f2d3d05ecbbb4d53b9dee9c0eb67a5db33097b6ef4eeafbe9a89982ad9aae5a15ee5ccda934777394ab5395b8dda8dcf3d1b1eb356df352cf4f5be960e65c3be9964a0f8fbc60bb5dabdfbb6b83ea8e25221d81aaabab9238d76e9ae2582292ab891bd553479d75bb714d54bbcaaaeeb0a7dd8df677d85f28ae71499e909a7a48b927a8a75e1124a69ed293eea80949b7b4eb4d21eda83010a1f78846dbdda6391709bd44a8aeea46dd5d2d1caa286e5573d7473d3e2fcf7277b1eba760ab1a75daea76aeee9acd6e8e2ae211a1baea3150b493aeea1b54ebc9d9c056cdd16b46791b5439cfb8ef160b7b8e5a0aaa6bd64a54dedf7dcc4d4f3ef965599a8fc596ef636cec5def0a427e0c0a99be6664ec65c8570d3206d9d2e30a7ab305d78e58d9d2f2729924f936cada974f4ab984901f03f27a1918f92449be5eb6f2a57c9224cd72892463644992264c49499664acac3145923032f6251953334b18cc4686495759ba9c902e922c65e40b4ad2e55fa4ab34354962952459b65095384ab284b94a72052549c24a528ab4419266d9928197198b41d62049922c874a1a2d2629035992b092244bb347962e998bad84218b5ac8170f3308192b4b178f292b4bf246068804808be725c98c2adff5e50da4cbb471bd4ad2fc9701c828c82fff3ccf279f749125d902b2a5f44192e5ab2c4d92f42175c81612a62ccb1296237b94a5ab2477ccdacb8d960ec8181919942e90aff206e9224df2cba812cb48d7972aecb18f2480f93419207fc811a549c248578c8c21cd2765d972334921932c4b172e615c6cc81749c6765c3c1aec90a50cd652c648f246966513268c74295d2691ac295f923473489797926c8991e5907247b9a3d461bef9a40c092b4b32c6759aaef2465996248c740149171ee4cbab245f648c201ca52ef6e25292bf169858494a912d246996a48b5c22759443658ef2a58c952fd2244bb2246fa48ef2a58cb948f365926410699236a40b09439224107247293363648b8b2c49f24692b01ae50cd255922f173954be9066b98334cdb274952e72a82cc90c4a19e942b6b84a92246fa48e52163349578b8b24c9204ccef86821327fbc489374215d33a48bcbede5088e1de4062eb22c61654c93278d9d9725922cc99224c99c8ffdf08d84406251a4058f8bd070854a119a29acb0ca513480ea04569e475382dfd1f001bdcb4afc140d41c83045b3a1a468c0c2222adb8634d1b0438ba59c63678886199da11c6387a013c4f008a29191402720c100f26599010887f1359f173b727c3994385670d83878c7c70f28385768e0c3301a601b321e0a2dc9fb5e2a3c7691f06ffe23f98f128afa7fa5deeebde1db130276aaff4f1ad2ed7ed2ecdefd3f52ab63b79b7474793bb808bb5ad8f78e0d1361ded5a09afe764bf2700a19ff83ea4f877d5beed672cf690fc644c60107b6622e3117578c16a0988ba9038627c2534d8b0d4cd622e48291215d30b09eab68c698335715125ed882c528632e2e9c181d420dd89255c1a42e9c182294361c2f2a17092322622b48d3d18c84d2257335dd74b18582117c5011817cdd661a0c11a1746560e60c499aa2d70f26b660300da298d090312ac1541221005b375cac6066c0cc50924463d8c2655c6c11751725df0a67ccc58c7965b9048082082dae18301da48c8f0855d4e0e243044c5ab0b810a37c790d99c5865c4cfa489411682f1b5992ac031812978fd8d00815a041690406a450e3dc611283360da088ad9717080309313d3264550e1348ec069008b2d8104cd612c30cd229993a64495c6cc962ba985dcb120ea42499cb12cc2e25aac0960d170e900c76a3c5c0642f48351ee0b0256b59daf13b61eb659e2f12260593bd80c07c1419bdbc2e3d5a9660b2d88d9823171eae27171b33727490410d1f2d3f98a63a1041d6528bf9c4f804e9a26230ae5806af9b09e402e3ea8187c45c623297ce8bc92534847ce130695e35765c5a50730809c84c8bac864b9008b9a7175b8b8d8b878b078c015b3e62b88e5e3f5e3b5c78b872c8f0983a46d8aae99026d0cc71992f00d478c08210bb754003a84889e20006d83080103e30f22ca0467d1e2031725545b58b6a22b27413f2b1e980830db0eb0823b25c614515428a38a8c9e0460c4a079258000e5248426e3a68c0bcc08a0802881e7258800d0aa0018a0c54363d746a30bcc0db65f9628b9f912b5a30446fcf56484105160191c57250c149900b09aae890c36d08c8c68c17594b2c070e0a4c00894ea19e88e89290662880000220c3cacf051cd0000684605515043433c3f5012a5a541505017289c11e60021010a8e1002c9c008004cc30430a504f45407ac0905e983128b1b8b0c02d28afb8aa284ff0b24296a01c41290217266096283f502a5106513ec0744099e5c22279901ab4e878e588b951da2869903366b4978c72469eae18e5cb4b56bab4b494b1d8ab74912649c23e46c6c4610ab1e522c096ab09e60393b1178870e2b0f57ac1c43a2881082e1a785c36606cd80136802d57934b0d598e1912b65c8ab88c983b5e3a0ca625a94508263381ccc85e1cd86af9616b0612115c824c19b2808162eb9565eb05938108b6607a8d105b2e3331ca189097ceab8526345e6eb830820b852d191a191a3367a6d33233cb32c2eb478b508cc89431895a6cd8329d78746032180f5be60b2c0317ce0b8f49ba605c38b2a4d78f5811580630190c0b6606cc0c9719971ab122261823c03230a38cf09ab2c501968bc53d0e74b86e3032b0998b3473cc9c2b0ac606ac8c59c570c49e5e346290b017f3e582314db33463668be922cbe132635431193251b2590c1623cd98cbe6d271912d374c1c22b868f0da612b060f22cc602e1c2d4723b8a018a1a6873f4b185590a0ca07a82031e50dee54708264242448d06eaf68814e8f53a62576c408d453160f2c01020d52d879e2c29017a01881121a1882020e58804ead8303bc3d05eebec0055838c1c41246482004103c4062e40aea492949831d366a046181101cd000103ee8e9c8b15b0d624061022d762b4b185d7081e4888d1ab31168e0080c0c4181093c20850208a0e741112ba8279d121e38a2031778b2d4440994d08011430881830d279440820bb01801134308110404986ce95487348bc1828a29a040c2080b00e1830e2a1040a73d0f901cb1d22101b9ddcc6294f0001232c0a002139d7a4044978404e41623c6f5544ca184073670840580f0e1013aac5000264988e88200b905d9786e64cc5c9f858a294cb001248e30c202403c408715542800019844d14b8244884eed0b2f9a9864623c91c3090a275c59edd44080138ec61e5cb298619460bcbc2863d0b2838b01a50ee502c89e92c7dc216b2f39e60f13d7d0a0cc41e2886560de20675c36c81a2e341932ccd3250313e38cc900e6c64b0ed9cc25d6028b9530d8eb6592ff9fc1bfecec41e1d75feeadea0ed5f68df5f87a7ace1e1f678feb06db60ac5395b72ae62aee2a06727473e601018f0778a4fcfb70b0555175e537380811b6c169ab83fac7e3e4e429e1df87811babb81f7f496ab5da911485fdbcaa6a5e46278fab7bcf431db7bfcb2c9f3c752ea09343ab76ae53ac778a6f6c34eabe9b9f3a03d0e99fb6ba67cc4f1d5edaea53a746e7c42b066e8cdd0d7473e758ab668217a2ac66756e6c5c10e0b2025f3350bf3f405a9260bc1d9891da8cffdcff1b51f13fc4838bb0ebbea88fa121a1a1a021a0a19f21db90cf50cf10cfd04e901090d08f904dc847a847884768476828482828280828e827c816e413d413c413b4133404240414040404f4036403f201ea01e201da011afa11fa09fa01faf9f9b1fdf8fcf4fcf0fcecfc0cd9846c413620db8fcd66f3b1f5d8786c3bb6211f219f201f209f1f1f9b8f8f4f8f0f8fcf8ecf508f504f500f50cf4f8fadc7a7a7a787a767a76788478827880788e787c7c6e3c3d3c3c3c3b3c333b423b413b403b4f3b363dbf1d9e9d9e1d9d9d931eee8138704dcadb16fabd3596092ffc7c9706c64361fe00f300030c691e1c8008031c6f8ff87fcbf90ff2ffa7fd73f101c26f886e3f3e1dc4e9c1a4e0e1cf3fff1cde7c337198ecf876f1da8578c65be9b2e0caa2ec61da8571b5f90204168b8feff20401ec80f238ac0383d7ae09e71aab350b5cdc3188edc7eb83fa63057fbfbc398ab5ad8f1edfbebe46ed7452a6ed7dd463f70fe6bb51fb4f307cc8e2a376508ad5e8d997dca4fc056ddf99153abed0861e3ddcdc30bfcf740b2d303c90f17e1243b4780ef2f88d3070e144cff6508ca2090941d941b98e009d2891b9b631fb2bac7d51af0fdd1280d31fa276bc46c572bc1c75f5cadd5dc0cc49b631e6cd51de4baef2a5cebd1550c54310e0ed738effb29d8aa9cab4e7d57e9f7e8b8ba19696f1e574255028812365fbbb1c1a851e31b1b8c8bba095d4ae56ae25ed73dd55bf735c2055b35d51e80e18dd9a906545b8cb5b06fbe66cc553cbc21f1a09e708de7225b3bd20555ed6ae2e6987715075b95a8ba3ac5fcc538a98a7bb4ee6b541b62f7fd61ede69b639b3150b5d16e1ef260eead465d1d95b67ab8abf1541355ce358abe3cddaaaba38cd9894d71066ef665d55675c269bb8d6a188735ef3a88358a66dce2704da3791de2ddb1ab5377e356c5a94edbfde33ddc24dc24757331e3ae8fb8ef56a36adaaaf888e2dd716e55cc33d62ecf42ac853c5cd3696671abf50f0337c6dcd5469db67ccd6d76da9ba7b2ae8e6a757d3fe1e390639eb62f9055b15e7bd71cf71e6dbabe1bc53958b7ed465b5daaddacd71c12469c4820fdbbb8b8c8f811cceecedf9c8b8b8bec9cc282ffdaf1a7d75f76f510f736ab6e2662ddee1c6f5d73aec6f131cdda5d71a956a2f6fd94eaa754f7fd74822449563811f416fc8c41b295110298145fe58a0d9815481d05f8aff9687c699a7391b759b3f8b8aef9e52fe6aa9087695a15b7ef06e2fd44a488a5973262e8d3346b9df6bae6bddeb16bde3beaac1eefb99abf29ea6a62afa7d9cd73862c6a3c8be0a2882a67112e14e1e47d3e8c624ec43e1fce69a170ed46631f3e6c30c75cbb7945758a710a3f5c01e585281e8081f6ff73720a20c5f734aa125ddd6eb5c7a2c6e1fbff32529a3cf7f9b0167617eba09b8539dd5f541bb5ee9bd8e36aaaf95ed19e76873dbde6dcd33918b0f2ffd8dd40cce2dde2126288a248b3b9c52f9126e6e8e8bedbf675db778829a47e6d000729c2ad5e7b1da8d75eba6e2831a42044cb5fa0e84243c339791a4e004fc3f5a7e186348c38c09f4640f9d38807fc690406fe34e2037f1a51c59f4670f1e711af3f8f98f1e7113efe3ce2f6b31a670c023c2d060d4f8bd180a7c5c8000d195a789a0c323c4d061d9e2603104f9301044f9341054f9ba17cda0c369e3603ced366b83d6d86a4a7cda03e6d86feb4194c8099a1f142237af234a2284f23d2c0d3884ef034a22d9e16e4c5c67966e97f66f9e0cf2c05f833cb0d7f66a9c09f598ef8330b137f6691e2cf2c2cf8330b177f66c9f233998d18194d8b2c4fdbe27cda163b9eb685d0d3b6a0d29105eb67319a7e1623c9cf62b03f8bf1c1cf62b0f0b31835fc2cc6037e1643cacf6288e0673150f0b4208a785a1022785a1028385d9ca7b9414f73893ccd3df23437eb69ee92a7b905789a4b036d46a39d5a3c6dd6429bf168a16551c4d3b208c1d3b288e2695970f1342dfe695ac4789a16379ea685cdd3b4087a9a16b9ff9791c9d0e8e1693412f0341a283c8dc6d6d36639606831fcf0b41892785a0c533c4d867f9a0cb3a7c9c0e3ff67cc64517e2653e16732ad9fc9763f93dd7e26bbf999ace667b2979fb97cf133172cff6f03df88c96480e9cf0cac7f66a0c99f1948c09f1998c09f19e0c09f1990e2cf0c74f1671130ff4f6383060d5a273ead07e0691dcad33a104feb22785a4fc1d35ef3696f064f7b7b9ef6323ded053eed6d9ff63679daab80a7bd433ced05c1d3de269ef652f1b4f78ba711673c8d98f33422d1d388564f23eea71149781af185a71179781ad1034f235e791af105ff3fcbc0460b0e9a0d99191b3531b421304f1bc2e369437e9e36c4e9ff6766ce289ef8330a2afe8ce28a3fa380c19f52fc9f52c4fc29c58e3fa5b0f9530aa03fa508f2a7144c7f4a81e44f29b0fe94c2fd530a10febf25368b1123060c10c3a701717c1a10233c0d88129e06040a4f03c285ff7789d16456789a0c0e4f9381c0d36484789acc069e2653e56932579e26d3c5d3ced7ffcf808921e34ad4cbb872e4655c495fc695f6655c01c0cbb862c2cbb8c2824b4c0c11ec9f44b87f1251c29f4418e04f2210f0271150fe24620134578c0604cad380f0f0342016781a90229e068489a701b1f2342028781a902f9e46047b1a518ca71169f034229ca711013d8de8e96944c1a711159f461480a7119df0342215fe1fc6a586ecc6092322e74f2288fe2402f72711527f12e17bd166a8c0d36698f2b4199e78da0c563c6d062e68c8c248e2cf309af8338c2afe0c23067f6669f9330bedff65682f5296fe9462e44f29587f4a39febf0c2fd43fbd30fee9c5097f7a31c39f5ee0f0a7170ef8d30b20fef462ca9f5e2cf1a7174ffcbf0ccd25062da68ba7c5803d2d468da7c5d8f1ff322d510cf9330aa63fa3a8fa338afc6714587f46517c899151458f97510590975185ee655441f532aa405f46156a0c1954f4f032a810e2655081c4cba0828997418595974145172f838a2c2f0305312f2f2db075cdb954dd55ae5ed79cdbe99b856eb6b7a279f86e37d835e73ec3ed02bbb1b7b2ee4e8dba97dd37bb79035b75a8769678346e37a872de7b3b7d7759e876d22b9a5dad793a557746ed6acdcb5575aeaebaab42ab28275daecaed9a739c93abfbea33dcee11efea8845af722c7a3564d1ab5d54372265f7c565f7c50d5521c76d60abe34395c7b5dadb771be99c10d5bca12ae4aefeb1da6955d1f6ddb9a3a3fbf2ac5d5e14af8aeb606655ed4605554ee5f27630fc932f20e09f7cf24fbae0e400ffa4015668e19f64815ce1831ad81baa6052f582141d682ae1aa049c114490001d5c17c86046ce110230686880859f25489f170448424a141e42399c2000a310512c00d5689b5b5713b7d2aad136e7dafd746c717a78e41d8ddb8dfaffdf60caff937f36804703683f35c87681142ca8e0ffcd1c6ad0218a66812941ff4f0a59e108941301c810c412bf1e23901040eb89ff274f58810637ee8ba80949081527f438a18bff3769a440a5052e3c090204ff0f7bc10058c8256e610cf97f728a17a8b0e40b17a408fa7f170cbe1802841cc082e273f85133bd7e7a08e25d1d5205d2c9106b61ef7be518a759bbb8ef3627e39f9cfd93e7d943fa670ffd4f1eace46ebc765757320060927e82983194a0019f6a15a8a27ac85b2ae009725ad0820455d8060902433fb2ac28928480630cba0b6c1c48e0d16400218e2da8e19f943977b042c2b438b103d3e10ac0928d8a1af00e5dd0d444c00316af0e1899ec38a9120314bf9b222e00852b4b962c5f7c102888410f0bb03103132d2ab00122be403990ca92058c30b264c9e205b9433f42cb988510305040173644ad2b7022449618a0a081b285c914e30ce30de8ee298cd002616d6b7124860d403d55610091a11c3011b0450942023d968a4d3b82d6961b3660a0c6890565b8458c9f12448000188f0fac70e5860656290510c174c230870c804c350c718586155c9c12442002891a114b66012de3bc9d500ccd0987fda2868346a807056425ccb8b909c236804a31b10015948eb0f2a2c5104542d04014b3c7941c788e609f14494d1161d0a40448a2e8e00491ce84013929c0d93c606848420603321c7185104a264c01838a010cc0e0820e8313b0056c99e00161b84224e954301f6480074f0942c820e1085925c680273be4f0808915848c60431117cc5c305445c4911448a0891b2420c50b25b4828e54301a80ce937f201c5b43f0d9e22a4915e850a500640ed923cb4db6d9ae56d2e36aaddd2cbb511a122b01333c59e1831ca290323236e4b1950edcd0800412ec00c34b8882548ca9106e807ac18da8d5c2b540b0012560b9a02038810548f11ab000051ea003508895e5db45b5503adc101c60481e0338b98109776b3f3b3e3cc7750f753a7fdd1ef4b421893f6d90e24f1b60f0e70d327fdec0c19f37107d0d1b403f3d36211fa09f1ea09f1e9ea71dee08e94a97d4b309f9e096763d9b904f540fb73bca2199806403927f565dc910a2146065b8ffcc61c89f3dd810391530f34fbefe7fcd28ef659ea25ae7d7d35d7db94e104f0fd55ed4f0c373574df30e10b0e727a493837ec01d9dfef0e45f9a6621600e7fb73127d8aa6e0d3aa09f9ea51ace1aceffef19777aabb976a33adf3dc4db8d862b2857a074a048c8f352901ff3aad3b6b8d3756fb4f7ba42dd73aaa66d46d9b74ddd5c1cbec6cc1a33dbe3f326c0c7cfe0c2ef7e7b354a358b5ea53ab7ba67b4ddb95477bb65396a261ec65133a1eafa7ba364e07df1cfd372dceec3a39bfb7f2f6220c4d1ac2828314080bb3537b36c76833b4240b62014e8e6b07a0803a35c376a5eaa8b7b5d739af5d2fa63b376a38e6e6e371f66b445ea7c47fd3f962e7ed5bfdca487475e936fc2071550414601c39331bce0a4e4e0059c176c20808bc7418830bef97cb8f33cc41a6d3331cd28cadb3de47a8a45afd2152a6d71bceba09b79bdd5aefbe68e7c3fe99dd6c1dc7741d5c9dda89eea9bd5530320e14994273b2e5cf145443d3e2f77357f5d9d85ee7537a97d7d9d52edeab6b8857a78dedebe9bcd2e08fd7f162ec0e20029f054ed54058906f0a21b756f37da4389b75b0f2572f5c87591b2a090781c976a56771dabbb8e77d52aa77bab6b018a635757b5bf536e0b4e782ee275cd28c69d2dd44e16b0f82cc419b72aabbb8af51a6c558e71270b3ffc7f8df7f777b200c26ed31e9f9705dc0a567c6d5d7591678cd36aa21ac4c156c5c08d71edb8ae2f10a76a8ab9c6c06db4c208df53d5e278aa7aabd9631ef634cf7ad5532a34f15fd44fa70a4dffff2e2e2eb2daf1e7e2e222d3f84687e7701d1d8eee34a75533abb3d55a8da33bd5d1b9f161ef429b24bdd5447d046b21cfc5c545b6736202274efebfd6b90e065fce623d3cf6ac851d470b7babd937e866ac855da7bad5c36116629e855c735cd44f4e6e74292cc1ddda0a6cd51c2b56f0ff47485218c80ed63ca3b8737717f1e65cc56ed6c79eab873d176bb455790a2e373641bdeaa335abddcd51ebfa26e582ad4accba49afa94e13d58ee282ad9a953b7777cfeb9ab6ef9e2ac0acdd2a57896a7a450001f474aa169940210b776b6beead4e5b8d02d97e405bd6f1e90142857852a0a120609ada6cc09c541b5520920aaa104a81d0ff57f15950465a4c3f908c4b43584844a9223f4c44b2ae78d6aab418a50bda53bb8df21c199528098fdc23a31f2bde4f6755d223b3d22901852cea540564dca91af28252db8e13d5d28e4e109291d4484aeb047b743d58494c573b553b2325a5fc16c93c524742bca1560ba4ea191ad9c994907e4029498a7a83d07e253476f63db6bfaaa070f714a58b56513e5942d74a6d5aca45907e4c4a448044f62a8bd712b172fa51c448096b3b4efd901d89447a0f5aa4ab3b523b3d2ac77bd61e216d652372a5cb393d4ded1c7bac8238eb1a751e7a3abad255bba4aba3224b5a529deae887f087c8c42423ae423b1f1ff5c897b4d42723349813d2d9c919a37eb4711b91f229f2acfd54e4695d8beca2deaba9fd3444c488a7248575e56454e2647565a48749f64348a4eac78ac47942b8292555543a142aa8a7e5b1525521d610e849ab0d1a026a0d3db13e4061d40afc1d8b14d14db9565a4d436f9f2a0aedb7cbd49ff787ba4592389bb4faf02441e914291a9d7e98d4629ba97688c629261e5311dc7675ec0a04552449ab468454a774d5e259f540e5d2ac454426154ca78e767e96747e96a88c9a7e5352c59c1316d08ebac45605e923251dabd7ae7263d692d1548e67972544e4d8db6c340c1221b2a4cbfa2142441624452c1e556556a808906df7c386ea8cd0600ec9a709e98aad1a0abac575c986d4b259bc98a5c4883485453584823a02527982aeb29478a2a6a0baec87ab0a0a8f52ba9c1591a8223e197595807e1c57a56d44b5832464b58276c6dcb6fdc8c88ed13becf9c152b3785a4b2749872d2a193a5969f1a264c08e84748494797c7e8ed857e3928cf010ad7692947c9e7e448c601159f2211a891a72d2e964057fa68853c7148a35ca2a42c5b38363935a5c904e9d643a256c94ca63446b27379432baa94fdf198ae22959d72ab4c8102b1b6d514f483c234f39adbd23d2ea17686b81bf9facac937a2b5c3206814519554f96928e67aa2745117d8652a11414c7b136233498437512161450c9e875728d74aa8e574742ac2576955262cc4241449e7814d4502e454277643def1af5436724ca585cda5338ada19d2350cf91fbd3be4d3c6355c6ad451da4ada4c7e8c71194cf8fa5d60811eb48698869ef928286aaa47247563a59ab534478340a3955e98a7427292cb4db6c553c51ed9370aa871361736bd655be2a5aedac785a4621ca51232da9dd9beaa6948845234d3acd4599eed773aa54d0a9bf55382c95677584b6e8544fd413cee84705649a0a6afa813e65b93aaba21257a26a6a93a87634d165a2fab56ef1675562d42403ae6faeb823324c87a0766c15b10967b47315454544b88fa48a464c5246494223ddb3634caf947c7c8ce89e9d948e4fb1f3fcc0021af10cb948412d437faa88fc78817c86949472accbe3e394fab8563a9d2d298989889048de39ed7eaa88b83ccd7b126a22fea836bbfe289205ccb53f9c907ad36e2b914d5919c9ea91c20983b8a39fd028d483d5d55d55302b67156584e2452548c32724d6c9e604e411995272a453f5f1380c362d5541112952259565b4c4cab68ecd48fb8338d583733a168d57e8ae89c7d46231011d7154c024a2ce6a0a2a0a0a28e743c4eaca69a7aab843a4c996d361ed8c10714a3e4223a1212232ab5d56cac2f1743f74502851ca28a54aa9de2723e39071981e1987a270464b1bf7133416092a1e7f3a385c51abff18b9497ca82e116d4a433b2619ef4a2989fd2109157f928a14892fab7f8a5943a25567754343232d95ef824f3ea9d5d59092a8a8b42a2894a3c2d21111a64721d3ea93ba4ee92a2b4e158dc56ec4a3e345acde1e5d10d5f147ee68a4a4b36621ca85784eac2dabdd499b74d938b48bb44e47233e59b9482ea22a4d097d6c4aaa94644cee0fa7271ed3f1eab793aa1a62caa911526ea7dd1972b372db496987f8fe569695b2d9b81148a3ad8fae090b281bc18a3ad2495965a9ea902b53e223454c733f76da44a58f4cbfa19d1355aeb8ad863214911c7464d4a387591dcae51faf53136ec7ea68aacd0af658b1468ac5e0540e6b4aa795054969f178d4865aca1de18cfb4a873454fc2185a6e914b0cdfd7e4b47bbf8fe80aa1a2a411195e86c6544682777a435d4fe7250c39d54d14562656813baa4147564522ab2525a39be814632cb63b53475bcd22831352a5115854a6fd493522ab483f2c1fa6137d0b0458fde291f97c8b37f5345d91511223b41536a10f08d528dee6bac2a5e41fdd83c1da1223ac72c9f9cce7b6595d3e930719e9f8ea8a3a5b2cb85844dac100f8d92d21579bad292f9405d017545dc308aa8a35a26961f81a08ae8b44e424582908a4c69e5b27eec56289c52309893c96432994c26c3e18c74419bd2079d23f8f059e8667382b9cd6cceaae3aa021f3d21e5e4e4e4e4c88220e170464e41dc51ce253a51f9acee2bd47970edbb629250af7aab7cd89fdfd19030b52a32f58334a4dae1a1595b36f443eb25ae4256eece110d2a1275a56495443422621c32126c62a582d29c965b843d6222a205a41eb13f4c57534651413c362b61c602ea182d2d097964c6a6a5a66ca4247764c3a52a14562f423c068d1c73b89fcd0e51f9e8041d91b4dabbadf519420559573542fda82a92637a9aaacad94749cb4694e97029ad21d18829f83b6a796c43e2d295139392904a875b7aca4045a2b076dc258df534b4f9f0a7a528a59d1535c473d5e4e35e395171212875e3643e3ec657b87ff07646542af7a854cc42ee0e8772514a4a3d0b1d091df1764a4a56506abb59e00f26f44a2935eaa4a07eaa88e82ba7bcd3f947c6f38bda6d2256574a526cd39015d38fb5fab98a5a4a7a85522c1e2a54c4e85211c182e2057fa02894913b0554aa425ad249e2094aca2e931125463821599a66269d2238f587dd5985de8cc35d69e5dc6e29c80a086978e406d32c2b9ea353102a6a4bb545749e8c6d51f7eea45044a79faa9d0da8772419502898a58c4a3827ee021d611977766aba33ea449ca68ce4a88240afd390a929533511895cfde879b9111e21a1541e1ef91c89685db1534163d29651fd30028ba42fd027c875d71fbc5587090784432a82cb310555239dce092754049783c2e19290a470463bc52125354ba89515347ac2616d239dce49869f744a4846393939391fd385094b26f870f97f57084a7c1124268cdb4d154162c2395d931155940e6656e7ea60ab260d8351ab9a59a4bed5a99fb65a7350371df33ae4ea118f07f39ad45523a09f1ead5d212f2ba3a86602fae9c9c21dd04fcf2ef274565e8d7a7c8a798ac7a798a7767c8a796aa8a798a7847a8a792aa8a798a7807a8a79eaa7a798a76c3dc53ce5d353cc533d3dc53cb5d353cc53433cc53c25c453cc53413cc53c05c453cc533f3cc53c65e329e645ed622dec28d87cb05531113ee1cabfcff7ba2744a0e876e3dd6ebc138efeff87bbef0680c500aafcebac5ed7bcb6d337af2aabe606f0e43baab1067085850074f8ffd7cf046083004410c09210f76582d2e19acf8735578b9aa876df8d8def067504c573357f05f04d7cf8ffbda3a670c64d8c34b1118011bc6b266aa0fee1aee2dd8568c69aa8067d3edcaa6c8ab3ee3dd4ad7b47f5dc4b5bdd732fd59acd6dcfbd540720cae70600e79f022033818a094edc8ebb0e9ab084b909f8c9842fc1cabfd6492564a084265fc2550933ffdfaab9bb1ac5ee06ea2416bd6a5527264c9460e2e4bfc68f5a03d1146b2b265198ab4c6c1e33f9ff1a099c6f17256187e72ae615dd601cad814523a43042d3ff8ba082084e22bc6c51d972524b55cd665dc4c524eeaa7d7df1d6cc7fcd17420c5f0b2107082400e1c9bb1b88bbcaeaae6a0d5cb3ab899a7fd0c407367ce0fba0668909967460490b4ba496e896bc00600a008600000800205202c64cc9164a2af05f3baec254bb9aa8ae3ac5472dd4fcddee5ef170e7f01e5dc7374aecbbc5405d8dbb9a15f66d846badda7f5a037d35fd3b66b66b8e813a57ffd4ce75daabe99edd3cd49c670de45a45db0d6c754a9694d828b172f31d5ba8e393c71d858c5682ee3be41983301a3b9b8d422fe4159b2842799f0f639f7677ceb7665445aa197591977b4eb166379a9385c55445d577575474d44c479d75d44c3adda80de825de4044e1ff7d5df35e9b511ff6ed7cb91dfe8173c4a8ffdacdcf8839febfd6555667b999d5eed659d9e965c08be49df9ff9a765f14cd78f722e6fcc5bdef3de5626d857df8f0e1a3665481da1de635772cfe7f5d55a63ee5ff8399b3baab7ad7970cb31662de031fd71fab39c7fdb45530f721ae452e6703a5d38dfa803ac5fddd3a2deab68887aac3dd35cfc22112cf3da7403dc5b38b74bbf1843c0b7b43b5f3288eff7fe35c2c5c29eed67f8de374d2f65dddffdad0c55b2b8723d66a521baa5deb2aa8fef0de5dd532bfa6936a21cfc2221dc6e11101a186eb5067f8da56fe7fd7390eaee6bbdd78beecb465da30da08b448da196c95f74d61e0be618c71ed86f10fbc5b0ca4a85b5589c5ecccfa82358a75855548d0cfd6962c1ffe6bb71bef08e760ecbbe149756fa8f6be5b1bacab055b95738dfa34bbb92ee69e2f67a8663d9525839502ac24b056c05a82c581ca855a83daf43d7a60e0c6c1564ddb9c83710db87d5353439557d44f6aaa5d740b9ba6a87284729278543863ac7f40866a17f29c5313a259fb743f1ab93a2af77ce4bbd5b40662de23bfac9ad79c4e098310028532c11f66411e44f2ffb55b6da8f621700b78a353a059d040665a271d415a430a951aa12b4059408be806bf2e6a3e9e1c5d53f1babec03563a0aa62dc559f0fb7ea50edc52d5431cfc715d76e379e0f03f750a7b8e6f3e1e34f0f872f8a83a66dc618c7c7f3d57c3c5f11fb6e783650ddcdc2b6b8d3debe3b6bd7176c55bed71cef2a8b54f36d61d187811b673525aac1a1da7b43b5fb704eed48877dbe35bf1d6b61c7a96655ae0a7916621f77f570a8f27c19eb5f1b54f950edb8d5c39d93876ac7191f09795e728b6e706c88505dc5876a2fe26e512f5d776fa72fff09fdff1124b896b55f2c5cb6f25f4b812a4eb5ba0e872f4fd70d442df224485cbdd3971f4182b590977dc05ad877c66db4ae192de21101a18aba9de5209f95af1d75d64edf1b5f51b73e1b9cea703efeff4c3ff3d9d48e6efaee32af97c3ffd7fade533db587a477d3bbe1c111b51b1e1b289c93c31b6c50ebb1db4ddbbcd397f7560bfb8bd3da05b2db05f652b59785ee75f754c7f360e9bfc63dc09fea24556e764fc242922bbe57dcdf8ddb8cb7ab95e4b4bbc52599a9dda433245864e5e20c89947f7e84e4c9deaee6b819921789d07fed66771b5c44f2ff5df3361fb1f27fe4c96b0dc4797864f85f4b792e1ed9f9ff9b1caeb2fcc8f9ff46acbc1e1e3b0ef7ffbffdccc893df1d1bb9fa2c349d19b1d1991969f9dfbd0895ff2f02c548bbc05e51b75b58bcdd786951b71a75dfbdbbdd8a50b5e7de74bbf188525dee76e35dbd573e59583dc0eab5d259bd5425513580595553d5e944ed46bf6eb7c19aa8ae59f736bbf90dea15f7dc8fae266a7c638371b0557916a6adc6be75cd3d9cf5ba774ead5643bbc6383c988fedf137d4ee1027d5f9e5adc69b876bee0fe3dcf8f4eb769f0fb75aebf597b1ab819cd7a8865444ff9ffb190f06bc23663c26339eafe6eb7bbb3ecc55df06b6c3ac5d7717757be3d3adb96f9ecf867715d7dadcf56eb693c0ff0bf9d98eb82b9a026336356598b58b83535ddc6b0f9fd6493efd72aea65d57737dda4df261edab4de929a93066524f48c55073b7f6f9b2eebc557b9b8b9a5539181acac27db739c991bcfbff1f3f93b29172893a4114105102881a46e17ccdd5c036bb37dff0d86fbe54edf9704eadd7f79eea71b536c4f1f9d68e66235216ba9bb47681ad56b25da06ad4693b83aafa7fa8db8d45af845ea09f9ea554377b4a32f4b3271e4e4b38e1e0f4f5e59dab409c727c1224384d5234351de970cf6c51b7ee76ddbd719a8538262cf870f719d30e4c554cffb5feeededd8d71529c5515e37035d8662229911cff355d0c66ba2959e7ab71fe626e35d3bd2c4d60c9f74990e4d9128ed217ff4990e01c41d2a23ba5294a4b3e099260ab1ef3101f4152bbf1b1e855daea9d4f77e0b6695d5dc45ceb24a59e24276ac7956761520266494d707aab71b3a45d920f9213bbede9acdc7df89866a419905a248c7304490d557b66777f737b14c491048e7a4730b920726a6e063eaebffcdbeeb0d5c4205ef3db66275c4bf5510b3b7fb7ab836d4e6ffabb8fbbe3346bb7b5a91dd7a1d10766460330b2fd3fef2acb551557ebed16ea22e659a8c1cf702c98e124805b9320c1e9eb7bd3bafb6783bbca778b9be1cad9902a86b03084e89320c138ed1ebe79771c8ce372218feb9dce86d41062c527418235bbd1fe6e7c04490d49879910ded7d635a3792624561445110a4542ff9f04098e51a7ed112435df0dc649753e216e5f2006e25bd79cf3a59ac85d1ce4cabf42dcd5999f0591126458bb49dd5f576bc19ab60932b3ae6a4ff5cbf333a22affcf79332227ff1a4724accd8876bef6ebf133a2ff1a9029359ec5f22c0402a5b79a0359d2acbb53350f9b7e76c3a255815ac8bb8d6076dbfaff5cc4bbc5e1b9785bd23fde633684a596eaaedda16f3614c34eb95ac4c79b75cd36b82d6e57b7c521df50cbbf0602df1f9a85e615673ecc425747b52abe09b66a7fb7bb53bc7b4dd7541bcc336f2664e5bfc667424f5ec5fa87519f8f371312c277cfd16e5151d1bae65c113fee30cf9847544464dc4645453736442c7a55c4a2579debb4c7e7356ea319d00a404133a09a9f24663f5ab39fe0cf8c99cdcaffd77c6876731f40daadee7e5c792e1e7df4ddfae0aa707d81b8dda25ad8b9ba559f31b3fba9765cd7ec6e7c5cd320677d42a2c62dae86936a9c8c7daaf001c227fdff5ab0559d72573e9c53bb71ca5d09bb0d30ebdd8c0706339e15cc7806c033020fcece12b31d07ec20f9af699e85a8fb0e9d7257383939ef8ab66a16575ddcf3aa66a0daf361770385c1770f5f8c93bab9e86ea04e15339d07cc746ad089fa1af7e19c1a5781bb7d5976afebee46a459ed05b31a0eb3da0166b5a3af6957e53a4ddbecea60bb57bcdbbcbea89a673946cc72dc59ced4ff67fd0ca70a1c0fcc70569c1ab31f11f8a1ce7e10cd7c4c31f3e164e6a3a9868f7978cba91db313ae690ddcfd88f636a7d6e38371f8e63bba594555a06ef5cf87736a363c573eec6e8ce3bbb9a9d56c706c7aae7c58fff0baaa56b876f375ae531fcea939e5aed23663e01e669467dceaacdcd735e3a4ba7d5f614fb9d640ad813e1ff7a958eba41abbd7ccbb09c1ec06881b206ed6ffafdd7c7b4f39e19bcf87736a5d3d6657d5c2eee6a076317ff171779c7116c7f5776b56ed45ae565d96bb4537422fc6117a6d88d6bd7fbac7d52684d8b14381298656432c40e56a02606071e2c410253a4e5cc0a149495472d5a2009921300b58d99231e0877f591459b274810393058d2e5cb05226e603ff37ca1f70e0046459890580881a2f7c167083c219f2b922550496a52852644f1fe0534887b08598c592aaa389cc8785a5d4599ecca892fd701980bef2e20111cb3c70011b9a921eaa381585ffff30330770f25fe2fc973f4a9b190758cc386052faf82f6f4a9b07cc782c99f1c03074d001dec14168f06b03af0a74d0010e5284d356ffcbe95f425e375c4cb8967085c075c4ac8601dcad7577736376e2ae2eee771f8959c8b56acc4e3daceeba63ae4a97abcae56c3c57f5ee7255b9aa566553dc144f75cfc5bd26b99a08b5e3a0d6f12ed54455a37aaa1bf5d38c9fe930c14c47947f2cd469d6421c9cba59c85917a79a2969dd59e99e55b7e7a19e4aa9baee7c3775fe1ea5bab8d31649d7f2ec5463d5ce35eea9ae0db5ce62a7e05a8a136cd5de35dfb57b3daeede63e1fce986bfef2a39b75aaf260bbd78c7d3ecc713810b8759add8eb966378ab96a847dbe7717c13e9f0f7f8071511151eef908e3609c22a2db0db8ea1d11ab0a77c3ec848360edeebec318030063ac5775c86e74b73e9f0f096f806beeee7c1db2d9a831d6421e4e759a3bef9877b57df19afb3e7635555f2d75d54475c50db5660ab66a9ed9d0350dcc9cbb78b7b8eb23c6d128fba26fbadddeaa354ef0ffb5ce5fac34e3360bdd2bcf421cbe055bb575b315c659e86eca3d1f4d51dd10861112cb8d8326dc0c4c6924e554b694354ae40bcab214028354bb6f941090cfe713f2bce4c3386dc6d56e8078507578ec3638d53e9cf116b6c6ec94ea1ccdaa3d0ab86f6c6a3f708f8f2f2b779fbb3b9a9370f82be4e15a8e7687386b77b3d8e7c3c0dd79abe236a7ae1e6a9e8d6a479de5bbb1016ead44e53eccd556783462e0e6f9f7fbdacd5167d9e08ea387d909d7fa7ed2eb2fb754fb296da980abdeeda75eafd733f6e04c46e87543b3180a000943280b1803f0993dce8f2c47ff7cc855ae5bad94856a236f98dde22c26c830bb45bc7be6398a6b9d749c7273df49ad6a4e724ab5ca6b7556e6699ef656ef70b317a7992c89efa5fad8635123e7abee69abf9cca586ff3fa6594fb95a8d72f52b550037c5dacd1cd78e6dd6c1dc39e63d705653bd7bc63dbbbafb7a7c5e1fc6b5e26eb3b2d4b5887511d7801bb35ac5ba8879ab51ede2ce378b79c638dcc51c6b377776bbc0765d71b8c657ccdf5ddc6d16d6c5a68d72e076ca5d618d77c747b876b3ae19f7cc6383533d1c66b7bb6a15aeb96a1ee21b9f0fa71ac706e8a707f7e8acbcfeb01676265ce3c3ec76dc35e75a3dbafad5e2bae7d57d573527a5addeadaa5e7fb9a5d2eb4fd5f5fd74e46e21da663dd535e76936da4fa9fea92eca7fefaeb89bfa7eea5ceb244ed5ea58d4d84b5bcd67e6046666149cd90bc0dd40cc55a7dc154a448928b155d934f7bae6699af64c543b9a33eba6696675577b2831d52fe76a0f250acf2f663cd74475cdbda1d659eb9a7bed9b0030761e25f658b5c7bac60d06152c6accf9a28a2f90f8a2c9172218b71751e01bd7aa71bb43edbe43edbec356b3a946db77b76deec5c08a1838791162c0c3a873b2fb66187cc1f19a35c779eac5614dce663c41f9194f15f8194f5840cd7ec693909ff104fc194f5b41347ae024b2b072022c41905790607ce1450c608085ece29fe4e29f7cc13fb90599c53fe9827f128be13f79c5ffe772555df0f05d30f9ff2e7c5de02e82e07061c567b5eae4e2877f2e4ae8f1e16eadf78220fe89e368e70b6c5c075d4ddc6c8e0ef7f932d6bf76abdcadb55be5bd2d725bd83cbea5ad9a8571f0edd65d4d6c378a666da29efabdbb54a7292ed554abca94c50bb240e2ff6bdc989d6a1c075b15e3d482358d718046f6d8bb76876dc6d955a174745beb275c3bd2a9c7cc726376aaa938e7f772dce38335df42d5ed6dc6fae76a252a5f57b5c8eaaeea767734037f2fafe135a769c6ae1ea22acf46a976756e374e572d446ba9eaf3f18c62fdc36dbbd5be59bcaa68c6c75567a9ab0d6ed5a0ea6ebc669c5d21cf38a8fef0d1d544dc7510ef8e35e7eeeea8266295ab2a0ed77e2fc7bc876637ef19877735b87bcdefe5356bc66d8eab872f775f54fb7cd8005b516026a89c2b282b1eaeb5aa50fff011d8ea56edad0effde1d9b9ffa7e3af6218b94e6b44eda4fc72c54edbb561355253e956a56cd6beef7eefa7eeafbe9c8dd494456d081150c7bbd1a4ff5c65d65d595af6bcee5dfcb8fc335dd6c76bb11695d73eebddd82a96ea98ca0a1070af06e377ebbb9b55eabb3768febdb8d45532003c872b7a6fb30a7b977446f3757f758d428e47929d50dd5ce6bf510ea05295040f57097856a61ee890a9b2ab6aa305661a4631c7c63b70bec5521830217e09bfe1d335bcc6e2aec6ab177a2a001270a54d8c2627f77afd766f476e3dd6e2c6aa4220a0a9ca84040e73ac538dcadf1aeba3b87458d3a2c6ad4b1a13cbaa707d511d23d59dbf44f1ad40314f2d9b1e5f4dc8d6fdda871f0ad676111dfa6786200244c11c51229aa70732f4db3e66ed62bda43b54ed55eba6a15d8eb9aa72f566fb8ae6ad6efe5bd34cdbfb7fdbdbcd7d557abc7f3bae61ecf3d21daf3afa7517703855d2d9a22c43cf91dac16ffff2d3cb476b6ae644d7648faffbf31841119173af0ad709ea0857749c17902a1e76e0d26823fae2fc6f99f64e29f5ce29f0c01bb7769abe635aaebdd110a350bdd2ce6ea71d53daba96659550b3bd7425e11d7fd684464cc4eb21b4ef18d23e130bb417ca4c3f8078a5d7dd4591803d776e31b9fefb8daf02d2c628e8f5ccda98e2eaa8f8e707d3fb1aa1ba59bf6e665fddea8dd76b5912c74af46dacd75ae1a91fa7ed2c323af4a3982b30929fe3f67d722e95d9a8db2d1aae6a41d538d3be5aedaae8556b8a6b50b5cd54cccb81b75cbf5fa7b778a375f71b8e6f3b9d8d5448db9cf87b34ac4a96e337bd4c28e57b5b72f1e6616eb603ee6ce31d7c1f4fd616ccc2c4e35aab2b93da6586fb7b71b45798bc335b5b79a63fdc3bbabb9a85d15ebd7ed98ab78ddfb878f5d75b7ea0e7384415588334e5bcdf1ee580b3be62a06a2db8d7bc698637703718dfff8fcfc00ed1617b71bc45cb32fe7196b2b5cf3f9b02f0bca97b5cbd3b6d5c28e71d6fc6a69acddbc8b5d876b1b779e579cb61927d8aaec46f50f03774ebb5b9d54e7eaa1fee970cd0d0633c635ac622dec3b9cb67ab7792c7a9566a3341b7595559b5c4d8412410cff1867e8007368269948222072ca5de54439e5ae70b0dd6e5469a43cf93bd4b8a85b9c3190631fb23dda8c9a5330b1e4bf166cd5e170bb293eaebdd5abca5b1563ad64e39bd293ee690a6735640ad3654ac09c408f5288465b9d0153a3add668abcb154a1cbecce1c440d17faa31cff8d887ac6e7fc3cdc3454130c71bd8ea86ecceedfee2da6dc4b96e4adb6dc4dbadb6db2897ead2761b6d6054aa8f79d547ecee7d1b0d376ff37a3a4d719cbb5ac5f12c7437f11ec539170eb3ab74a4e33a2bf75dc771ddf3940eb659881a9172cf477a45f391ebbe87994d75aa7b5eb59b4b91d2a35d9543c6c0ddaad8dd2a310bf50febbe89b8af6f06eab4555957779d623e5cb550b3aa3bbc3962d9709ceaa540a8c46e0b07baa2059d5210cdc00c30000000331240304828180e870422c974dbbc320f14000476be7496441689a2284a29658c31c4100008000180011081a11901a50a9345c4818107274cda9a0c0c85ed8f84ec20174e38f0762d35f3e17834812a5a3de2b367b2c82645fb14053d27c3338bae65f2b3c1ba5ee8881cc5cbbcaa73fc18a1b49548bbcc7686a570487eec2d5d23152434d397e3063ff50ed5a5bbf9c2a6065d22e874146aeeaacc42dbe5e9ca27b8a8c8ad9d194ed8c500aa775c96b657f4b60b8751c68c7adc1745844210aa6474894cc82fc857689b6fee7f8b19cdfd42f9801cbc034be1ad1622208d6472e5c74c065a92df835781ed4a0355d081c692ce7b60af1105962d042f8beb3017bbd86fbdf08db41f9ce1de5b772c33634dcee83a179997defd9640b749d857a245d796aeaa51a0da09570f4b07fd75cd196e51f8bc7e29e65b3ee7f0556d2c8292af2ee9bdf6c50a5da03bcab507a1470a12db70b69b430351d03aa852d822a95e245bd92cb901d18b34fa7135df008d66f8e9e6041b886f0c80ece29ec93dbf0b524d4c02cf83fe975946d725059c46513a620f777d55f082f1ed460e70021823225241513942daf19a6b71dc21f0c82d92520b91df055d71b905d0d51c2bc18b0803849f54ceab59f68b5a8124baf729e2d055c10d63c1c8b17f7d7f92924b20627241d9111b2b36e78cc8ebfffcd95e7c574ee4784651098f40fbefa13800442748e042d32222f59642d14bfc88e1d3f3672a67d1b848592af3029dcec8b4b20d01f1d7c685451bec5f8ca459221500e94528ab762d659b22d38ab88a8ea616331031d0c822a86d8f9eadaa92102e8676b6cd899bfc3ea983182e881f5f35d61355b12512924a13f7aaab162df374a8f3a954710c9ad845a08bd20ed1c5c225003e844d88eb7ddde024967da3dce8192eda4c7377a59ef3473477d81f17f2c7a3b4db8fe38d3438047f7deffb21ddf7df614d857576841c57847705a82b2f05cb97b391e1d17a093255e34b7ef451414d3b3de4fa87b28f08cc364a591afe8184755f587556bff62ea5073738df1d29dd2aea4ff6bdf02e7396b1cd715179001b5fdac83cf28bdf71370fe27427b7a02ef9b386f4cd14d8b9e30a25f549afadb5ae16f949996283215d18128deaf10935295c8acfb210838c44db43f2a07134eb5a934e7057ba1fe97abc4c722f4dee10345e7d0f4a40e3c93067d27690ecf8c6d874c7bd346efa91f084e2d3ab2307a362ea8c3a4fa74a528b26d50f096b9d2d8ddd6a5efa4c01481818289d3748b52e02ab71b2049d6ce8eed078045b0e90b3c7d69f10bdf7b2ef28ea5920c94317475f1ce55e982d6146076f7cc12bb49e3168d9603b9375d2edf566eecf778e0cd4aa72efa214d066d4cc2878e5b1c0b5bffafdd0aa4923e8a0d47fb87e3739d2e3cfaac840f426af282f5d2d5daa3b099ce1e2d87ac5745fb993122909c9b8acfde6ea2a62c2dbe7dbd27c0ecb09f868c4c51cd6baa8538406d8c36851af7104786ed1781899252d191788380e5b5e723019cdc0c0bc9b7ee94a45da59da4fb0c7eda51e03339c336803401d9f7eee7af548a821c5e28b4a4fdcc3c840b1efd9f067ab31997a8e6fe16dd90c8bf05d0d7b765ca22c2b63fc5cd54dfdff1e933dcc74fc12c525bd3760c6d0604ed28fe5607cbb190b94d49ff29764c701337fca5f3faa63679f5120b6c45d313a8065af4aebca133da35c03b26de31dc507f4ad6dbd065efc681b2481004cb81c1f61a5436a2fbe1075260e1d605af0f078770cfb889e0477938e2b60655f2badfb2a7a9970834199be0f0f746bd8962ae4cb70805b2a527f0580cb751b7deb5654158aa12f79eed704ebd721bd3187fc5ce4a5203299a08390fd6bef056e317539be6f385b3a8a5a7b7432ccdc3bcdafd9539605031e6d4c56d24fed4d0c7f4ca4e4b7e14dfbbe9b0591a9ae4d5411c15573ce091e1129a3c4659cfc7c7f11831350e69027005e95d4e5362587be15a3e4c5d7b5e1caf7d8724606b1c61523b0aa084e4e4319b2c40aba75268b2b376a4582b2b068a0c310ea4c6b43defdc25b5586399423805154f9af4284f4af25b101af37af329be81a31a28feb8b1de7923701db3a28164c8819bc050bc4ec7e83a3a7f47abf16d952e1455a962392687b5fa648331839f6f8f3d3ebcbcdc27c04e5239198860b299965547b30ba4e41e9d3edf2507e8ace1d65f9104a896ea57ed04a0990e8d94d338d846bb6e1c2793f07ad7b68fae5b6eab5233197d0cf030f64cc480fedd8ab5d513ec54ef4bdb40f364237bbf0b39f99f7cfb6759777623ca6e701a4d761ec50d32631f5dab9c48ee187598ce16eb2d606486e0e4347a7e15dac9c9221970e3dbdb23da05cfc8c050c15b6e81e2599c79b2f3eda245fc79855aebc75495cf420a9470a155cc313bfde7ed99cd1e7664893394a47632728caec2c7e7dc2524884a87b45613ec3ada006a06e243b0734c7106cbd9309f583d70ac90d91f4ee04c7c4f05ef2d9b5d42b44897f522574157b13af9fa7017adf2e95061f0d26ac51a859b415d7d536a68e59a56bc9a2d008891cd69b74073432abe55c3006b84ab5e31cdcd8b5286586668291833e35deab95f3021077c40e38e29710ded4fa8f9130138350207f015e387cc7bfcf0a46713a42c01ce8dda4ca5e2c7f39c7072639eefe0712d024b0f1014d8441084be4ed32bf1238765da6c7c21bca8fe167dcbee4994549c18a9ce7b067dc8ed713c117f3d80887431a58988cf26bd77ee8a37e86f533cc1f58857e0faf920b5f31b4e8c0406419362703e3c49f6a82c5e7d0db0284988321be0276986315b1f1fd63a9907805d979839c281f39c4c9f1dab4e4f03e22b4bd7d11989116ec85fe49804544a7ead81078fe839407b2a679121ef8db4ed8507f1d38076f36fb33fb307d5683afc28e1d38d57f87f5246938256a0358f70b38d2e0e56b8a70f9227e3de11dbab463d3dba2d6421f127e3fd432065b3593d348584f1b08a40e39b15bb8db82f72a12a849a5556b68205bbb912d3a23219f482da9682625586ed6f969af45eebf82322a4f40d265919869f84ba9fb6938f3568762375658a6bbd1e82e552f78ea7deafde8287e21b36df2cfdb1adffd7bd669600991037efd0515fd02ddd7df16245fa0b41ddae6cf5d115dfa065eb811810a9998fc61d7745dfc1d67230f7756c33589f8666eb25aa1477e11409dd8bb8c0052e37c2760ef43bf00723996670a8db530368a1bf3935af1ff98bead1be563cebcad9907ffe3d2e9f2f263a87a769383e97d38c057e35ced9c1d3c847b9dffc0d8190772864f391adccb5ba522c376cce3d118befcad0b4c334c922b18755038ed43a278b048d1157ad029da898231b23e578fa387110d4c22d272834f926c046ecff4f6093f9a766688e89a0946a12f69172919d66b16618a2f46b4231bb8a8067e776ffb7c75f872d7d4f8393f675411af6a221787a03f5e36fb6b29ec067e8edecd1ad568594812164599b98527ed1ad6f4699e33864150995545d53b088d4f85d1e1b84dfc1b9b63f7239d11a2e80ba9d5093f9933e4b933282d4702d979d4da9471b6982c2b34190a83ac6fb78615bd9cba652ca4606c711936cdf9b3665202a32de1ba0547e0be854fc9cd7a83f61106c747bafe750e7d41c8b36d4e631afddce66874905ccc43528529e242fa4e473e28add15887b9731dd004055465b160e1f7cab382cadb3178d1a010e0a65237119232865c61de796caae51343d977c73c6a6379f5f911da408ea371fd0dbd694321e5adb2cdea5c7c22afbc27424b4b107db4ee344d542235458fa01b2037c94f7b2ea49bf03106ace60f577cf085888527a482255a8a40d67b4775b9b33df0fde2565532034c00ae0ad61811c9e87335d1ac81ec5183187aa9f7e11e2531eec967c51b05e7bf82a20edecc92b676383d7e082d5bd85411cebee9d6a5eb14e629f88c67692e3fb3148036af519b5957fb04c90b55d37b9e27cb11d220a9f22488d687efd681f884359b992e8bd3b3a3ca3e5499880504eb0a3a583111e6450ec5962e010f7795510ab0943fceffea964d8f00bb97d628fe598b86cec8101e47ebc3ed1729343ebf1d3efa3ccdcc13e7fc1b17680c494cc06cdde802162c2ff8d74933879f387f1969300b0012c7db0ce55989d9cf08e59aa0d06cec69f0f9969d46877efa4a08c463ee6afa4dae82de3fe4dcd329c2e83918234d4742c39b0bd976f59b1563b5fb217914faad3bc32f272aee26a3e79e98424b58995085da116daf46aaa6c35d6021a045b8df70b35786dd4a403a29e22ef03147f47140ce1d3294706b61dba7ead0f70a8f4981533a15e74a6d92e7d014e696c58282328793dbb7befd011e711800eb78e50a1174216d13c5ef616ebf334f054d1967799cb83d492c8d2db115a63c711f42b1db7b70a3aa914621a7a576f68c28a3bf29520e9c2ff41d41a024ee1db148e6c3936660221973e13b451200fafe4f20700c7ef2c6019d2376165feffeb3f38ccfad1b3548b3e927b9194c7c25f15d2e573c5efbe67a60f5a269a88ff9d717b861e8b9a38ed12f6593a905eed9fda18afb5740f1ec0aaf7a6ef91e3af158aebc65bf53b603b758e75f293d917d2f58fabefe8419dbfcf422d284bf5670aa27bee1b702c4408a92bd357a597fef0922e51f35297a6429a30b025a63f909269a5cf47c9780070e201053181c00b0700e336ede9402409f4a81ce4fe1a440ae58f9c26ab318fe09627460d01f09cfdf31d6c443d54e95028d69dffbf7e35380a35742e0494217090ca64ae4ecca20809c883e87da03e02bc14d3c89646c345541b5b45ea8f0483036038ce62f81e96c3db3d4886a8cf26181b1fe188628c90b03d58e2523b9d0b471d721f2adb319b143a7c1db0bc7f0dd73aa92d06cf0473bdfff6efce9c38f7f63ff84ef8b2d41ca5140cfe71fbb4b227e76088b25a42ba7b2046f8fff0fc9b6f5c0bd2293ab2adadb7824c78208e89c06015e80fa26c76973a97e0816884d2b936314c2443b0684abc6f5af3d6d1cb4a96ce1dd2d5cff3eec6fd66e6dff10cdfcc1acf65eda30f9b03a93ca470ea66b6dba71b93744d499478f13e55f59c2b9326b32a100b870b2fb2dea3b059c9af5af7f3d9cd72d591b4c5adb2887c7382091db9a51a45332017be229da0b93c384d491b51775697e13b4c20bd2b2f2da51711eec7f30ffd07319b3cf8768ccc23ea8f05db68dd96aa696b50ca13a7c7786d656258a3497080654ae54d8a4948090e2fd9c231e006a914466d67d2b277b832f5e617c1ad27d55f1e78b2f6aed97e52ef17932ca4d4ba4322326ce54fbbc041188883ec78fe40a7d962cbe97bf7bdc67c5940a232c1998c2fe41be4e9e58c8670ce3a084c5db52e40ea1201bcd593d4c5ef5d85a8d4d9095b7041d0fbcda42dbf1db73776794483eb1782403e3f2d33da557065c747a2ad72bcaa86d47639e076a583488954cb40060967179b740b4727f675ec85d6c9e6f0d0ea6403f8741983b234de2943c59d170330c8e356baf414e619e893effb768fe49f425386c765c5326f4d1fbaaa8a70a3994f558d2466e43c9ca823059240174cb2c3cae6c4b3551ec757140d63c51674ac5e423be53f31305aa9b6276065bf2791c7d3f443194308c3f20cd2c9b9d518d51a26d65e8e609939e981d300f1f07a0dfdc338f9140968e6772bb13d2b7e37d8a4fc15b3dfa9861b1f5e10092a6aa1162544016e02c80e7e676a3b7e5ffb99af8791b1732b5f82b96e0a61db42d7457deb41a9d3b92b3c96c121d6b372c2c799252630d0aba5fb4b3deb654238a01ae81dd15ae4b53886ec287ea4e98e865bcee711c6519872d821091ee85c1871e50649693987fc71bf12deea59eed94a6d4ccf907cc96c66d31bc83009aca743222d8e60d9e2da5dd2f48f5e6e36c62ee49d31897caad5c9edf3c8cc819c1a4771e0af9b8cb8013e414118a20f9a49b2bef4fe41a20fe56419f1a6240e87702d87fa6e6c3cee45f13b01ef285eba766de752421876eec4c7565dcee09ca450a0812bc0b3745239b4df3061fd1362208a77c00f2a019345c03fa4b565c988ca096d6c20062b814d8f8839893bbfbd0e4fa85fb89a036c2c00777887cc9092f4b90f7902e6ee5eafeabf0fcc90b0c161261b4620e5caca78a068787f8cc0bd7d9114ee2799d75f3a727ae4575045f33402bc86cd4ef8b9f730b348b52d429f48fc6a20619c4d5cfeb39245fc13be9481ebb244d463e70f758e3b4aae46bfee2d9e5315bb7755bd5cfc18e63566ad2875273a7ab9893a56619b6179e2df4830a17311bbb6a5ecdee0fa95f1bafbd4513672621c8ddcf57b56fe3c1947dfcab88d06e9923e38bf7c83ddedea50b5815caa954f413443ef45d789b52a3f5776614969930fcd117254796a3895482ab349577ba06110c69ea1ddef78035945cd283042000be00be9f8e41383ee9a4ed553a4abea022dee6dea1f6d2874d22c36bd1cf7d6d9d9de9e44b89b06f9213fdbe530865abcab96362b542572b6bd001b1127c8befb54a2a0eeeb4fc33e6aee6f1ced4072ea1c79dc4d6b3c1da8a1420f53de82a9b76245a0011f55018edf1c52e54482c3f8c9b9d74411486125cc455fbc0f4b60267dc9c43f01a0132672672120b27ab3cb45a7126df38e4831332a40e5fbe971bd877e984f6c75ee050697363a4c67ed0a0240fbd5a4dd2346d05cb946e09c109690818fa4bb2948de21d731cf6149a3884f6eb02be4408300ae04da8f593127aa20f5d25140bfe4defe3fd3671aef1e0c2e6af12eef23dd4318f4e5278bb1dbc8ac7c150a2f41f7f5f0dbe749d5efcc0f897f95f33c8459e9e59318f9de2a4d395c8f7ab4b8e1c9fdb84a265cacdce72f1048cdd90bcb18c43f63a5489a27b9e7cdeb1539dacdbc2e9737172fe2b5c6dd4c8425efbf6310f3da55380e6df4a8449d5cfd42c57cb1a10723aecb5dd8956e9822bd8732e9723e2b05edfe91eacb74cb1286d22569d8b134ded819a4315843c3510a1007863a6ab24ae418b4ec416b1bbf8c6ebd64ca15c0332c895fa3d49b1cabf91148a36574975aea5d0e0f0f25c83e1acee30f1582e67edbfec1436361609ccfd1f480a42b5c17443b26e18fd39000643797c79494dd3038e02e9574eeaf1233f718d3953ed7f570789bf29b1076b61dd4fbabf64b6c2cf3ea7da0530b4fa1aff887a7d8d709fad821af82c6e2ecb47b97eec7417585bf6908772b31299e2900a72734730b53121671b0554113f395c2e1d785f1362d5a55a735b8f1acfbdd747ec0248a8777da05c2a87937b718b1fbe49ec6d2e27a111163284f0c7c2a45e6c900d939e56000f91dff5f53b52cd664f9a54288e81660fc853209bbce147abbc28185456573ddca391919cba74dc4067a3c8b34dfb70708d55ca11a2b15809df80f8b54bc3607b5002a99e006fa1803e89750fed54b3350900d0cee9f8af7a6b21c28cef8e5667b2a31e4f8d05a507427358347d20125be02f77af3a40c9566e6503cd1d55d69609cea445c485aa5d44df7bb65ef75a0f0416b1e69858a758ed5b7c45e868582267505617f30c1da477234ff6821518e79759c9e50ab3ad1fa8048896aa6b44d8a3470adc50e0c135121e966e5a7f0f6b91335fc1dd59102a7f1d1652d1edc6566c39111d7b84f07e2e4cce417a7b587bb8ead28669b02bab063c4eec7be09e489fb58384b3d68281761602fb1f33088b875a53ae2ecd5b8e91ced7bd46ed1d822ad5c8ced8a611fe47eb88da88acdf9d82a21685cdccb4c9807dca51b1c77ae397dd24315e6ce3b5e4c5e0baa9d837e61c22cd97bcee03651c1aad360cda4f9a9d93605fd0046fc455b1e2279dd94fd3454ec8f82b413b793ec37d3a409955ae45070e4fd3e17ec15f24e613fcd9f376b81434d031ab13857a502dfccde407b0bf9f66a917627ed1f44679f14a283d56001279d58252b32a5dee877d6c1a62777dc5504ce02472258dd5caba27b917fa8c94d3efce5f5a013590a877958a8eab39deee216f87b17467399765d557b75c7a23d95f8b5931d1f38fbe4593885ee8be1ed1148627116f7118c3d3af01febcc8e9882fab7367df80117c26754cc15272d463d06aa3f751171137a4b79c395985060b3b5d38ee2e9c00609998f220d22724a698713876b2f30da071f7c41fabb44c7f60b4c8dfe5cee4efcaa3a24f5fbc356e4510b5bfafdaa9c8c37c6d2658adba20414cfea29beb70497e50936b0b1e5364c1df1fb99a50c831235dd4f2859900cffb59c10fad41e2e2d934e2225b3ca4f21a2245692960a1f8f27df35860030dc043ba742a06b0fed01569ee1cb07989e9c6b52b2a014025e220ddf52da63344fa7f5aff60b99245e1480a4441b72fdaeb372b9d374bba199083043965f6a43579eb01b94f310917d90001a47e5f01640f6a05572ea298a679bebfe3d785830e8fe866c733b5d4f497237ce66d9a4cbc31ff81a6e9b27b62855d3e3afaa923b74297086a2a39de74e1b4ecf6de2f7e379e6cb334b22e55d60e1603bea21105ddac7524330ff3b3837f7171c25c1512087df83f0f686a0654456b53adadc04aff9e30e1cfbdd2bfa681ca532f42fdfb76d7b86cc047e258331083b75b15ed3bfaef0b888dfa9805079e80abb4324870270e3773e5e5c527c7328c62e74809e5109a1db8a737643dce02063d4fe64a15060ea89f43e8e1c9c27ac825612a9e3795539f118400a3160af58dd9940989050ed32cc5b1460cfaae1671ed2023721f11c26d0bf116e8b825237d7a064d5300a3205ad82f7a1cccf05694edc140b8beaa50529752b222b77498445099024117ab7708a1531730161ffa972c76ed200e68d05a7c48b9b6ee03bc25af908c8ef839179cb5f318991c20ffe83d8710e8333cb013173116f376b088326980089816667317f13bae751e6cc2eaf3c747e8c96dcab912bb9e507a27e881e0248887321574f090dc6d8a7460a8e29e0956cdb160ea94e694997e0dd4c5113e5defea52e8256860f80bc513f53721400f07cb12c917250d7f09265930a542af4c00f90f2d32bf65f692084719cc97e36dd92032b8123e102d8700a7b8b0534b126695e0d83675e04eb04df6785877db1d3f88da9d35343e4b35fad9f61533bc8e36458c6082c9d4c06a3ea9e601f9ce020cd727b45b531e553f34f507f7862e61981dfc617f3c7b56a1e13c0cfdb366c8607bea03c4819889b2ae5bf721403d9dcab0fd327643c766b3713ab64fc3ef395a0a3ebdb20cb134599e60de0587f3fb131ae5f091104126100658223842b52357d90cc40157db2077318ace63bcb58202c3a8e31287a018d7dac4bbc979c34e10e2804a87112a81fef977d8f38797872c66761b3325dba4f2c9f843cfdeb977ec92d495941b0f11ee3e00a7b83229f17d5d80655813579c21d63d7f5f79e6de2dbfd726b66f51579ff6cc5bdff95f18a2dd2b0c10a85c1fa240ae262ee7cd4bac431434224f817ce00332db00526f7416e039632c0724913629f16c4dc74a023e5689755b5c9e4ddc750852a9c153f8a1c2de24ac10176b5a44af523e64c9a23511349fb1f445c897cf3e99fd32e80e0a715b3e87f813e52aec755a53d243b68fe2370d80f955eaaa7323cc120232f3c04e17cbfdeb5e65279f41f4a742d12e93d323a9559ca3ff82debb92f00175f5b0424b696fcaf415c4f6a77d4c604cfc831b168a30d888226829f05c89aa467fe242f234026060b4601247f9a598c974068c42079963a74c19aae4ea6bf04ccaa2a49f63fc1ab0e95a006a9ace602dee689bb43fa01df09bed338188960d65fca6431871ad27d0015c19f43dd72a4a8208ea9f3ff07e5ded5824f846d416d0025d9c5caa8a8a8d0b6ad7de671666d466f09d94d755e13031553c84efbf726eb0c7717b00ec0c5d3cc3a830e023ac09cb25a3ec41038081b12092eb514073975a4d0c69d9dda96bab5149ce164b8be9c8a44f0f98d0c026d005b9bc91ded31300b0c721a049bf250ad259ee5d42d30234cf034013e1e443991f18428f49d691290a5949b1d0f626156c51e6442788b1e89d8cf39d84cfed36554dc0624b06179283791cb3da12cb34f0c6c44a30dd54cad3eea39b0f3ab986650ccd914d5e92dab9c66abcccef4ef7da6bc6be08f53102a57ce938d004bea72822ad547cbd74f1bd44270c264f2b3145bb4926dda7ac3368eedf0238df3306010a390f41cabf27a5d9978b40897c2ba58313f0e5ec606690605bd18475462e03ac8ef02b695c0c15de972c26e8e3cae20433975f8c725eee89e4376ec1312aa7731147cc4ca5a9f0fee788fcd1b1fb408a3863fef273d09df63eef8f91a5c0831beef11cb6378bb12274da545c9a64ec6c8a43f304e821f65db1d46f0debf69ca302a44daf02e1241bd5ee94d2af0682266d14233ef269680a442e9b5e6219554bb90ae80d98a26c5f87538bdda6d5d8292644f3aaedda10114e8d5cdf0566389fd24b042ab714886875bc73af8368107969cd6baca4223cc9cd471dc69e2974983c043f17486829988ce5435252cf3aad808180b1bf17154792a085db2f032f961306c6b648dc6e8de430151e5d2da7882ac27c1d696b1b4a2d3afe431468870f88d48760b9142f06fa85cbb0420de48e2bcf292d9094cf08b90a1c4f0d94c6e0acaf8a76cb7db298b5104cb3ada1e5c00206daeac2996f17b6042eb20a432365db6a40fb57aa96034a0704c85acc929f84a30fa1752a00cb3322f9aadcd17e695a770f163822fc79c41068bc84c3f31aea6d3efb939ec513d9fa871fbb97682b81cf1b453b9b04a7b7520b3ccef76faff77c8041d6c313ccda95c3a01a8b76c447684c38665c1903bd803d32c399ddeb8e144a4f6847c73f0ee9314412cb16e03e14cf63d78e57c862ed687d9a56f0f431addbebce8e5d311fe48e28bc8373d1ba88a3ce2bc29bc1c76bee779a02a82bb43476ef410f3af49c9b9dab96bdeab629b7777a20fee300b6f2b6f39be881c88adf6c86e33187bf0090b33299a458ebf6b617c1bd217fbf5301dd1f04db19d226c502bb72ea297aa86b60f4cd4829a9a33d7d8e46beeba9f650c3b78cbf73a9f6c0aadce8cae864fbae84cb93820dd14ce6b094874022ec31b575fcdbf9ff380e8e519dda70b6cf4019c15932f2d8c8c06c86da35167f3c5989de819740133a7231889eaa18d4fb6727eccc8a5c294fe38ca150a79531c864532dc564163f460e6d945c9994d96cff704494a8aa335c4710eed5a7c82e006bb57101ac24af765a7d991fb827ab7ee5eef05181dff97cde6bfe3f20b7b1f80bf0878727c5bcfa2ddc3c7a4c402fea3bb71c7c54376eb1bf71f248e07f9fbaaa7f20941d0d7be3931d13a0de925bd018dfcdd74875d69e1f92bdd641065be5a0dbc380e9ebf27cb3f0eb3b15602e3b82ccb74ab7d73c3e1555f6f8ad23ebe08d7e146b173ffd6e0346b75433b994f2b6b1066c3fba74db88030360c6c0e15e33efa224ce3c0fbff5868c857fe414da5a1cf575cf4bcea4964a4fa24f05a068bfeeaa8750d7613a643bcc1f3610ba803875fa3a4576b4e5715e7c976de710e7917bc4210654dca2bc98ad05ec2b6761dff0c8ba5ec6db218afd249a60c62644b18f5567799fd0f05aa2e95e37aee81bef1c17e131eeb5d6cbeb625abb4c836112e2990a9655a1ca6885738283305fe0f84ee32098a5b750891c04951c1f1281374cda96806f96dd5aa641ed36621c567325430bec1957af926d68b32b99ea0ea09122dd0aa4a373d37589f274301b42bcaaa6fe59c7b9148189ba0a124ed65bd89f9714e2ae5cb2391113ab6c3eaa3cfafa7a05984df7a8d954b31be97fe6c86c682ccf5e484777b8221e76a782526365e1195b6fe434c6e4601604f3f29bd6005ca5b2083e45d4d9a910f54dd0e56def733b2efe59c676c0a998844dbfce1e2cf06d34e361ca35fd1a2af61a13de89c95bedf717aef2fb12f3b69fe3bbf31acc4fc85aef9fb478649256346021ed9929f339fe4849af9ec01a2d1732127c9e743d7e9f3d261240e0ddeb15c3048f973cb3b2036d43de1613a65f9cf83d4cfb3c5ecec22e3f6cb9245bcc5f258e62cd417fd1ebf4b6b7dd403fee68625c99ce97b4fc97a90f79e79d3b8ecea138b0c842167e5d85276263b1403d49c51ed2ce66d4b0acdfd4f49c87c525280e22d3b0a281bb95e3f81f61798b00608eaed0a0c891f7bfd3e73869e04e6dd117a872edc2c7d2d51a4f20ef05de36478fcff3c8857f763442e82db80514e7f8c1b35e33e36b511ade682702f8eeb0209f9cd6f47b368710fc1f3befe8c04bfd3a7b70a7af30d19091f52838ebc13dd84131ff7341f6da67fff9e461bfb19a3031f0d4c3a789b8942e209306833882893f8d239e5e53e6af7d11665544a3a076e0f60ae616842f4fdd7f204243cd28126e1f7271c144961e7c866e2f46dc0833c9c867ea27fa9433d822e17deff8ccfabefe78ec12e6e0cf71939e8abe47984ad771a95e5b5d939bebd4188042d7d816328773c78f28977dc9ff6226ccbf20a44920c2af09cbb5107892534051a165004fdf5786ceaca8b3099f3d409a18f5489efa23a2508490a4ac6884da222a02afd6438b55bd6d34643d9d2b5cb21b3c3993f09305784c2ce31c134a876a336b2426469cedfc4c743e1ebc9c2d4915f8a0712586005d1ad38769361f2922b7a1e46a46829b04255453cc8c484c87db4c12ed2907f0779044ce11106b3fd79c991280b08735b823a38b43ef3c7966c35889c589749a670d98b95364e5e97010e7c57275407b28b781bd4549f8ff07a4d256792c0a3929e76b17fe739f69eb15e0ef36c8e5208e9f046db97e85d2d226ab8fb651c2540055424f4aa52a53a7a07f18bf10dd3eb1c3022a285bb9a3489a1c85b4c04df62912d199d0010764060f26ddf985fb2406d5c25031239a6e93b75b19988e446b9f4ce0a024b9cb18db707edfbeb851680b181fc60c1278f6e673dce254a82322eaf56031f53f1f67c59b0cde78847f7de4c9429a83ae4e24e90011ba716b9b81dcf04bb2c487223148b4f2701cdc0fd7e50dc2d189d9280d9234a6ec15653237e511246cf10e582fa50dc35f39709e7151a0ea2fca0f7f543b07510d5c6c91ba06ca27c9e3c3721609e8649d096d846ce224a46f7c0ad5d62ab1731e607e6250ed427ba5cf44f7c172768c5aa0f679b7159bd91442658cc1daf3d018dab98bf8c6d120c3533885fae652525e00766bcfbfb4af3a6394f79faa3c996ec3646a33149fb85a4198d2b464f28f6cbca1dd8f57851ae99b7b24b2d4c5f2a1fc031daed6e383edf71e8a707f244bcead225c9f732d12715e96ec84f44f89947552bc5d9b8842f47b51b01d9f454321947d382880ee88f936903a05b373b442d114b25cdbd76283bee246f90d06ed46b094e2c586b3ec8298002dfb8326e211e61a1a599c3bf2b0fe0defff1deaa29be9ddaa3e6e87990a59741bbcc92b3945472a57196578a3dbea6830b8392f2ec642e86efe45b7cd0e4b10b633b62c4c95102a5aaa1d050bc5c5b35bf9bfab69cd57e5e25537c1ef410370320cafe1c1c4773063282df9d6090af018d7c156c4db86a4338258b70f753a2eecdcc636c759dae0ff3cadbfb23986a2bc89786b20b5869a4e7c77acb79aba3fad321b1b07b8959bff7944ca0941a41225d51c99bf3bdbc03f83323f8ed1f101145cb4a1d0dfedd89241e3f2dc44a8b6ba11449f63200e3a0341fe0d6f457da2c3c3fe046fc6e88e6fbbe521e18943d3407d8ae5e3bfe295b84c104eeb5f18fa0046d33643092fabd726653e9ecabb6820141e02bdbfd8a7f2a210c1c23a59f105ff8bf88222808dbb1e8e018c3168e845753fc52289be7b39988a26505e2d2490e0a31112213ff790a2351b0cbe7cb8fb096ddf0f356a0a412b4e91676a9d5b0a2915685669f82a79f58124fbcce3dd50d6305f95115b250b6fba57c58cf9fdb540e209f0103ed2e7a9211bc28e3167b6cc05bb781a7b8518920c34bff3f49d668a0b435ee6f381fe3e1f240d834d29b1e5a59ef71888a327a9e1f3a5c02eaf3781a0de303dfbe8fa12c3e953ad520789b079a59c207d3e4c9dd53b3ea4caa7f7a8dca6a99ceff796bcdd875c01790a0047487f041ac4eaa4ed0a598d8c4cf226659d327cfabfdd787e233e0044637ab027483755d98794f78c44c01b4c65b995806f009ab1d4a4cd862b4d8eaa4d010d1cb676787ac996b40d21ad75a2d50ff6d3ed4fd75902f2934b7a27c88b9d2a3558570646b0ba1109c998b9d0f17d314f635c060f1c0639c8c116dabea8dac1a216dbdd08700f236265506a91007170473e0fb89a93e2f0bc8a211270722eb7c33498a19c09d74e4d1c1275b82bebc34f9535ad53b33a74b80640d6cf966d56dd7190cac669fa365021dbefb5735e2b56b65a1b0126e286ecc53df0a9298770d5339967fc783276222f00844a386e3e1f2b8c9471062cb82602367a685dd5e6e444147dc422d48f1d640513b45316d109f6189079d12e3e6c1fa88798bbbfe1581ff682930b564de32d943a289eb17ff6a812d4c70e88494d6d56c2a342c8c59806f8d2356a1ffa4af14b3ab4808685f3074ff99380dd1c9085a5b566a165e3b71fcd2ffc5a1d6852855682f9c6f8c672b864121c08568b51e8df9a0b49ea81afa7f9edba3c10402987b0c91609e5dd2592b6daa52afdde2c8e8ccfbfb6e37c16bc9e1954127b5715b762f13a198158c475f619bf7c6840dec3d1f83938e16bdfe7421035d42fc57f4126efd2d08c8216068d2cd1942e3f4bac737ee910039b235d4798fb674e95e9f34e430c74a2919d9dfe939b572000b28234240a8ab88029d26c11e6bf84510bca9291a35ce73611c927911c3a0ae3c3996f0da6600ccd9e582c9854dddbca7a2dbe051df341e135e2c5267127d1fd6a3fe1f822575fa8ace147e38fe495f3d388763ed4d8c158ad80e482e302273508d9b7ada52fc160c7fe296ca61e3bbd9f5dde7b7a249ca0e054b0467391f16cb53f3e4c97cc47ea4f62f94ff7bfea0d02e40ad49d359d3d228464ccc79f875196d9f555f4a33f7257eba847a70ef321048483c0e2df27b07c77348cb12ba9025745febc4720c1c61159b77efd63aa4aa935cf81c5ed9baa13542dbc8ef9e0c247beddb86af5fefc5f426e089914523abc6d57821bd317a79cea958895045cc634d95091db6a3a1acd4dbeaef2225370f980f520e29951bbe713a21ffd87c3e38918cdd09c506ff27f34671cc8074902c8844be89007ff6666d8034114390744d2276a8ef26fd238eda115d363437e1efb03cfcee42aebba1e3cce7f3cf7645f4bbed35409460e5a83a557a7648d91f5a01051d409ca5afd23ff4e0a0773bbf5176c98e8edd0307f4a26435432f5752e6c795312cce0b970d87956b40e27993b3c03d596069eccfb051e77c670ab573ed614c1e7fb07163f97d4f4819f80013a10df26631bd5b8ea506ee6b9ce34a2a7af8969cbcd599a8a4100a95bbef1d26e9dc3ba9d6a40774b9d66df4697b3ac4beacda869bf4008b9b1070b21213ed94480b431024f5d0d78b7d43a55c6538ade3275f4fb7513713725f2397208ff9fca0027affc3feb4c8ed075eafd688b20385091c18fc6f4ffcc14617d944ac425b2589239661f70152c4b394814f746522ac99272a25b93b2fa55b02bc9c53e2e4e5582af547a001d53d8263434614a14a7a073f097788d67974d539a5a2c10fd0f876e885e02d719dea5f8cf97e41ad0eea5efdb21d1d0df1168de391fb2393039cc8478a50f177d82d993765c32b247370adc51f96a266cd24d5c7244c0963b480828219430eea197fe0dd11c6ea37cc94482cfeeb2a3bd84c071f569f97fd4d17901480510d2e7147e886fd7610cff636f380d2da48ddee70cacad885a9efa218a055812412243e8a2781d244d836a369d499c96d09903a1102cadb050ebe0cee4b9e5820eac53a2223e4c8da9fe6effc704e07626eb2b9080b171efa9f9b2a6d42bee4d102394db638ffdd6737bcd0307cb6e34fb579cf9a2d010b5de27fc8fc894590e7eb85264aee19e1c8f04fe81ab6e3af2a7f380d7d554de036a891b53f4f187867d9981276ad1d965ffa3794c1d069356c2796087fe670f9bf6dddfe8914fb10758e2f8e3704023b64df5befd3c08453ef6e4d2e267d9a006a0b4fdfa22b121f8cd49686f3c26c758b05d883d809bcb413ec90e909b644ffc08b5dcd60469abcbeaaaa1291788678f5af9d5084e78001aeb0b61220a090f12648329f446fc967417482c8c43fff0b2d4c477a315ceb10a58cbe31cb03101b8738cfa73be8b3fabd1c4bbff3d579c597f849a21593cc9138ece1c8b31448238b04a1644f3c9f5bd56f038a2e0bf3dc7cffb6801edbc10f53e1f6f10adb281b18d12879889f2039ccc74ac0c4ea11aa7b35d189b52bc497025f1f700c0120f45be717b78d7448f0cd19c4074eb7071d77681f830317c91d4a0b7a6c188244180e956a9c224f75504bc605da20c301c8624968418f0ffa9eb10ff2c153cb544b75e5bdf813e2d8819c87460904530b803e72240ca443f003914660694baca4e42a9ab3efd34212102ffd0830b6c0367b20bc8edb5a395636b7a612a5f17c3bad187ca1b6e8ca0bcba0e4626802172c3e085cf0b7a1f7efb901ef82b02120d0a4e4e2707a08b399bb20067132a5c1b5a547c1d247cdeb9f9f6b04bca6a2849b2a20f117e5673682d3be9f5a13c968b9d997ecf813309301c05d843c0063e976fa106381fe58e0f3226c3ebb93aec89bbaedc5642a726dceec16f73228dcee2301bd46b56f40101663a0ae8647d84275b75d77f8c9da97a64e7ddef3c6c566718b29d3f875f9d23db3ff29e460e343abe5883965fb123d1c9d3f9481848d1733d3f80f31b57e3d9c0b7e0ffc059ed9341cb1066e6f6cf990ad9a65dbaa4120efa62efec12ed5d66f3070f9dcb0ee1f7f0d9267bd81fd17e33594fbddbb92b624bc1f690681324de60a68ebdfc0dee1eadb5e7896156550adbc70e2c1156ee3762cbab739201de37914d6f657cf1c77088ebc760ae63c43334451e89b36ee19fb435b2796a31e7e165fb0227c31b032671b5f46f36f2232dfcc68d5b168fb3ba8b49451e146a7c70b3b0db70f6111bdd5bf5864cde54e619a9f8404d3f3feadb9ca3b55a585f5866beaf0de2c3789e33b670fa3371e32e261739ffc97517b8a7971e6e665ab703e9e5d73ea371aea9159fe4a35e9d56165f7cff09f3d62b6c747c95a76ee674c7f60f9af554a588fb411dfa66f0de1463abd20f16ef5801649c6c7610dda9809e822e7368bd58e7e84792782e075a7380c08a8a9707e7a20527c5a850707dedf0e6c3d5c591b7eb08595d1760f5c30f48e87ce2e5d69d7e6b6581e6f0f61b5d8b263819cf0450175d6e2b6a31d603284b5af06009a448026a9615a6bc82a0bd090bd67428aae8d7099d567a197ebc19a749d1ef026ecadf4f58a07012e374872f161677455518c18eea032dcb232f5a3afe9afff08177b5cb831f646a0fee56e117780a6783cbee1402e088760d58e7a9f8d8a07fc02902d052980d2f8faf00058d761d97efefe7251b189220cc913b3646dcf2cc42075092b2b97cd6a531ac1fa3f503b070e5e56adf5204b7fd68f0f072dc562471b0e02254259792fd88a9d86c1b3e3624aab631aaab0d28f8d04a1dbd95966937638dca04ccc8f30a4d10a6d0dff57f7522bdbf0433faf74b2debe6dfec2bae0718b0613f4b8cb15d5d730b484c18a55ed2206040207f7b538af4c8e0a41d897d93b0fbca83689014b7d9cfd2cb430b32171b30e9488d8512b882610d6b7a667210d6d0abd56304b114e45858227a1180e936a853f9ff127364b10ad2543eab60d7c5a80f258919c6691799b1e041ed3b45dffaa0cba5c2b9321b8d85ae7a05f0f99ae2bd2439a618495431302e3bd26e489142e7f3c429b725fc644a5bab7f9b4e0c97c7676431863c9610db991bc598235e18457f8d440dc12af331127d35777f539a0ec0ebe4fab50b94ef62ed35162093a607f5358f49f9a0bb36f8779ace6f1daf1f42c2d81bb17b0ce493f1eeb37c76dfc546010f877c9028d6d8078d01ff2e1d5ccd67e3644fb0f24fdab63efb899a692783303375e9737cee985cb20f8d6cdc435aaa2fcf0c528630d01647e7e1fb107f14f9bfd4b0d4013461e778dd72ad87dc5f6da15ac4877febb5f8a7173794a7218669b38366b5221f1667f0bae76521eaee06c9062a104ac0756d32958f16aef91e282ba01ea3fce2b1746ff198e9ff9b8efa43ec6d9867c863ab3f3310e0d2f1e5c3322bb649fcd0e593fdf227bb7baede71bce661089e5803ee3cf5406f0d4363b973a379c627bf3820aa950a06fabcd5de09e262772df9d809b588f4fc235c7b25d2eb66c6201e9907ab37fc8a54ae8a749c91de59a19e094893a984b9ed9ad043ecc66fe4e5388f4732a1f463189f9fb9d910abc781191f6479e0e1c828fad94e6f88decaa87ac25fbf54050532cf61e066b23ae8b6ea6ce664847200c8a49f563724641116a5662d1e6c179ae632d7730d40b904a963b96ed54875ce92329c3e36263462ff28959998d0785bcf526b4a454926ce637ba7c87179915b88c25ed6f4e95c068eb9f7c47c0347cec7762ac0ef097870d66a50661c15e35f0b7ca4aee092caef744bde889f6e0e0b8f4a3c94a5a21011aff573cd631b9747bc629c7a28f4ae71491f3345d6d02df98fda1061aa8d76cb1248bb81b5d07e74f3eea2f9ac761f6226f6cde18a02e6012cae0e0bdc6b6a9ae2d8a9411dc78d570f50c5797131556144262ca03fae4e1b03e1b0823a6375accf571aead0df86c72d858b9a932ec27b3cab269bd841af7d6b62f571f6dfa298634fa0b8d0193d63d74812aa1716da2e28c49c8919bc6f031b90027a04768f4d1c6f3daec28988120a7ea4f4807ddcd55346fb4feeef4e627f5eae173157c96dd8e779aba277dd2ac4d13269f8cdc2e7ac48472933d116f43f79c3b0aecbae44aa67bc6bd41de00dce85836ada79846adacbdee2872fdd238c3b7bde94780af09eb3dd64ea74e193c8414c5090ace00b25a3cfd2946f8d49d164ce3aae6ecba68d10d2c27749cebe587cd2ec29deba9f00bbe9e51b795f9b522d6bfba3f00d109e82d2c5fb9be2c3c6a36e2b688ede8d73e1cc803032f89ca3daa068953c30c622bef315fc4ba116b59dcff4693869f23200f5939c8b5327585eefc5b2e55418eee0dd9c62d08c58f1238e6af7803c7396c7ec10e522da288055a9b54f884a66103b93b0aa0fc33f14622ed1d2949ee071c595e5b98ab2eb4056213bbeac9820dee4e339d87a5052eb2318a55b0eaabd1f6dc37055fbeb4153ba22f25414c184fc3e87b1cf54a5eaad6cd0fb4040361a6e912f8ca3a09a90920c47fa359189b45efad82f36619e708bdba1c976fc6870626fccfd4783faffe10181de8447f168a2074fcb343b47b3347245cdc8cd1394a788911ae5ae9375740c50fcb53a5763a0c776593163784e8659b06f6fba991da2bbc4e822ee05caee027909feebe0f8e38a8da3caa807b8dd4cff9d0374bd60658733866207c1d547576270802e950c2ee85569c109ba553ab842a7940cced09b12833b74a8747081ee9b28a580dbc76f864a19bec7631e5bdb1185d7d1ae8ae319e0028b6a714a7975c780d7377ff8ddfd1ec8c0800b9dc563ea23b60c614828b01ac44284ebb20b29686c930252e743caa7bc67a0616bef84ff602afdaf2f4db861d0b48a50f8e5d3e70726ce8f2578306835e11fb58a3c026f809033203e0f82eb1e7d1dbbe3dceb2650762ea9bd7c0e5c5e723ed563fb6af0983fb811a22ce10596e9a6fc8a9db00cb0423c0ad5287ec3bb825b22f0f42de37391971a18abbf36ceae4aed90232311b01785d797cf5c3e552f7674320650d1ea3cd95d03a8f4073d53adbb3f509536d5cdf8b2d6e4aa76d1cc01e9da36e0439a1d08ba491a95d164a149bb0164ed7d2b62dfb0baf02a68d9ac66e7a1b6412f22666542d8133837f84712fc76344d0d7a4025b93315c1f858356ba8dd69f24f920c8a3c82f07df0d2a6accdf29369b62934516365865e3931f85d4651bec6c64f1d6c13cdeb3b1af541d38997a78c8d5b41ce59ce9d8e98b3b3bac7a3f690cf2b0aa5fe43c4a6f8efc4566a80c452ad6e96be5d71cb074163ec71eb7021a050b8191f341ef3cdf6438c0820a6cf1592807d975c64b42545b171b6e4cedf4b00f3ceaca55bcab965b60fd33b5f1003c9ee807143f3857edba16df311f707a4fb4fd0cc73fa244e4f6091ccfe9a93b792d1d611d203d6edfcf4287afae8d16f654975136a42c3d83fa9a520cf8e73a55d83f38e7432f6d6117885c8ed13d13b3c93ccb8bd69c05ad3337c989d59d9c3f73c15bd8f032d7e40ecff01e364084affc3719e1015fc798f4e5cf24b3b180476529399bf93b524cbbfabaaf6b5826b1dd8d1a32de079b64c49487d1805569d2d99f5544a5ecc67f8f4782b972c94f1a3327cd1a7afc5699e22ec83752ddf598303333cb70db629c006212f0272299a74c11384391ab979aed7c2ccc9603684b92743b0a3c40b3f6ba4aafe17e7feb4d2fae66ded3d7a8dc9715b0c5914d82cbc9fee59b9216759969c0db466f188f01c6cd43e334775d2cea49d38ba31032e3072ed324aa97878c183ed301ae9dc02664d03006381a1ca346ca1e3008918ba8492bda389708f83f3a2c06a03fe9051ac2faff447c34858d7df16e54300f09f417428bcfc309f6b2746597b1ea94746dc664942135fa3d1fa2d74494b53e1c3fb3aeb8a5c6bbbe7d046b6874d7da384a8341b9a79e65266fcef41c44fb2774ae8d632e5040c698ac50378e556533f74e3a7868602a39ed460a11fbcec75c62fd4934efa0445255be5da49301c8adc0d4b9972f5cf81ae632ee5c883c402aead6879326f2cfcc4e457647ca1875f67d8270878366960208a858e9b93ca878b9407885d4e9407608f78cd139b35f7f513a28ccc465cffe96c281daa47e49d7f5c9eef08687e25c12b338a290fc03b3f84b11442caf2fe483fba68f5b0bf48ccd3624283a5dfb5344e7e8852a8d578f603039a4a23b982fc12019b4df76099d03c95ee1ecf378ce7c80abd28cdbcfc94494e7d1eb2786c8ba4283ebff7b67b8c5876a18f947088e819bafe552d4066d7743e6cadce2eefe964442fc36c423f75ce3f06c43683b8fe0dba3cb8cf33606f9a08eb4d1b9e318542cd966999f469286a473b16e037c75dcb5f3fdb95a851db368d3d208962c4b7053ae25dafa7ae42a3b36e09228cfe84f3af1b4f06717ba34642fd1a42092c6cf877426659383843e986e5563139a099358d1de3f4609d4051e526fd0200af013a6dd32d391141295722804b28e3320d3d0383650c7fcb2467a7b05d8faa548e87b1734d247820f12a6d151e5a6e35a04084511f5a8a370dd806116e633c40176a519e75b030fd15a1654d5d8fdeb7972499c2d9a63b7b7c096d13b79539bdbe099c5ea80d9d4ccc6bc3323f62a0e862eb5c83adac0030a51114c16fef39f5f4179a2fcdbeb706e034e0d7360e61d63912d5a5edb3fec5f0f6044ba850d00a7e6a94bef265c3f1db0e25bec81d70adfa11a5402ea8f4996e4f3a0892c74bff4b7fcc47340be75c9e30bda20cf9cbfbbb7894b09921e06d06ee694f0574700d66acd2be58169daf04925374c664da7ad0fc7886ebd6600059fc31066e58f5f932f33d2453754c09ad56347fb8ae533d75212148e7195cdf6cdc91dc9756638cd99dc10aacdbd66fcd50a94ced6b32dc4725bc54c75432b5f9ceacfeb3227c14bea4a09a1736cc2f388adcb10ba84d071cfd76dabd367ab804d72fabc4a709e60e62af6fdaaa211843ca8b3cbcde59d59324fa2360a6de5816f3f3f117f789c7ed8830b0ec868d8e4b585dcb6be7ba2a42a6dea9ef46f1681b5f7aa7e467b9691d3161b01af98e5157fa4a989300a31211b8a940c5eed4b84d73e24266f13ed3e94b328f7421176086900c732537028dad17a49880b3d67cace1d09d2e1af58fa184911e16e0fdf821e92aa0bac0810fbdcf4d9f894dfb2789056366e074ebb87e5601f4635f0b8d4f3c04e640640d14eb3480a57fc2b8ca31d282ea19c36e589b3066b1c750ba0ec97f92ccf9f43cf2cea974b1b91e49c3bc72f92bffee82a632367337cba121db8c01a6feb8d32506fe616c281966f396faf9e036d46ccb752b7472e013f0b690cd6931d834824ec9c7f386342fb2e5036c45a73029156c4230941c10ae9a4600f4d26ecb310173fec1f8bc8ad21696cbce6ff4590e4bbb8a3b1d6ee0e02e1bc0c4488356b9e66dd20b5df687ba29cdcc5a7e24acfda7eecc236f6986138d069f0b65e9ca2e9d1b61dadac7d63f3d005cda2c6ea256d7fc0c2c59f8371cb6b94b4650b8011ee8c54d635fb7cc1da9564a0058c104cd0830a3ea54781e34251319ee6015835779b57da6962536576caeacd1075277f3fe609edd2bbd1d5d43d5b62fdd27fb8e39a91b0a0b172644eff13b6108cd3d02b0c845cdaf016296c3f3e63a63ff2446fb47c5ef400cb5697f2cce0d64c6c81e6dd8b688ad013f5f0c70bae312970463d303492166b5a772784db30965178f27ce1ecdd5118bf3a9f2544132458ad121af264eb1f34366768ff4ff613be9e65f3f96efefffb209fc82c52c81d2807c09177c1eadfa5f7d86f1ad371ce23e9781259586645f7353cd36e5c59b98a2e8c487d0178ea2fcc63afc5c08941d84f2848de98a1daa4510a1e160d9fdbaa577fb1f27c4071bd3083f4f3da4b649e8b3c77bc78e5752cf7f18e838f77bedd474a71cdcab23c856131dd4bc38bd44730f8d8b7b318de3a0043bcb9d95be5fd3fa04105aa28025511795edc27545055e5ac81dd2189ec9736f6c3f5235d894113be55a754e312fa31c28410ee44f92da13a452aa4bd27a9c4fbbcba9224b1f8a733701a4946732f0dd18771160562cbbe9e748b8021ad7755c030708dc5268dce751b5ed1044738c6806d190ae37f41f8c7f7e495c0adb476aca5d39410fa060821732172330b064c119c2a011a3283f58791592fc3e1ffa2221bb8977c240476c43a6e1f58d51b11be0d0a330d8a3def97dc78886ea62d582dbe13b2d6d660dbfb62973031081de82f4a11cb23353c0588356265a5d43931fc277c4272243ea9665417c6c8cfdd806a3696e08b6519975e0c5668b113264fc87958abf56113f61a767dd70dd8741077fc7c0bf5d0b7a6152b0fdea651ed91c0a3639fe7aa8c03948e2b70db40e1813deef26b138bc5a422646b2b05b6fa2c1d1ec170e0bd7dd02c1e5d6789d822e9d578af7e63166c789c5b9c9fc69d42f04e955bf13d69927460693a806fc2cdd9a47da9e7025fbc477f3f9c027888cb1ef2ff8795a9a1c65db077ee255856500b6e872634193c5760c78087cd2c5625ed15ed997ec9cb577a9fe539f1b58747b6d88dd678c27c0739ba9e009092a16853d189cd5261133eaed0a87ccb20406119a01c9a0fdf9ae148ec935b4de9b89f11c8fcb3f867c99be7519e5d9c1697fa9135d3fa4b083afdda0b52587808795317b2971b9f987b6f4ff30e8df9de189ec91fea6142510f3af772251be39878fa70abaa9dee9823a4d8b5153fd2fa03c38e477acc57461fd95dda27e68fc0ad1e45e8c18b831e23c248eff823ed31b8558dfe47ac70c69ef46eeceee866c36d0fe592879c0da29b064a4cb636f2fe18d41c3dafd27cbd133f682d991e1f0fb942f2aaa14240c2f534488dd7a5fbd7f1e5e77287335708c07048dc98307ee1983798f684067569ef189e17416c11f1a39b3285451b564de7e4def162be025353a57193ae85c5f8bbf83fc258fc65da55480fb4be2818bb1b77855b9a7d9b79eba8f067abaa29b718e4ea01382601b6c9dc6867601109569b2fc02a400cd6ec2fa871afec0f0cb7c13084b3f6bc03adf85b4591d34978f51a1149e3f06869ff51afcfd46f825ed16fe3894bc2d6e562e866ac713a179c4dcde18c51c32c150f0488b3695b8132aa50f9f935c9229c7b77073899bb8d2a0d781acd70cc1a7aa3c283ebe356ca625ca8966df5fbc5b2bbcbc51abb2ecc3914ea2643df5f5acd4118496131d9582b6022ca5c4f942ad78f8f2b56073899d98d2a1ef80acdf0cc1a7ae5c283abe750d45b4e92900fa8fb558d3893f81ca85ee41addabc25e2cc39a6459a425c269eb84629dfcc49fc40b52586ba5f094abe6dddff13f24d699e41342e1902c2a6492c0c3ad30c4cbd14abb3f002c6168d670a4357ee05f11c1e75d30f0830e0d08faa92d209c7b4a378224028f6cf29db6ff88aba33728309be3cfbd000a7289c4bbd9f9d6258978f2bb2ecbff1e9235879c15f17387d1842caf1765d252e33c6ce6c2a3e73999159f312627e192e4ca86f70233f5815d889bfaa3f322a8ee2824def4b495c53e13016ef0db4cdc23451d3215c8601de49049043e1fe5164ec8b9dfd5fc61d4d15813440c3dda3961c9588a609d84693f49cc9b5a8c01d201cf96b5e806cde32d1b244caafa498a3842764113d8c4f7196608d02802841e9427929376118724c2d275183926c7a2f2c5f857fd063be738f6c33a3be0446ee4fa73955b7a7fa86478da92154ecf921385e91e9de31ccf3959e5543217a82b24bec6c54f2a15080236d43fae3d63200d2fcb32b65cd70ee95876858b12d5cef697407384ab942c089cc4bffa92d781d1c17ad26720d3c727917e1ea89bff03948e882795e7f9ec5320745958471fe677cceca10c222fe22b96a06a82160260ad29124cd2047c72036a5a42eb80b549c9fe859f4bd6162d3db02b001b5c013cd5033dc82c47e3fe8faff99680d20bb03c69494441b6cbc2bfb700dd57d1b880fe2e93783048926e5379dfc0a7338e9cba79c995aef0b277996f656688769147b14f0ac6b71310ad42786b54f2b561781b0478f8e29baa5c4103a8c4b8910faa84ac43aa233aa14f18c7e5869ca77742a720380a1175129bd1f3f169b835b62f42c2aa5f7f143b17929b792c1336596f9587ac53a63a4f1b726bbe86490a13bf0771efe4e63f81b4cf0220ce9884c4bc9ead9a591f0761c87d2d3107914b1a3aa0f9bd3e9aec39f04385ae66590f6657bdf3b36c9c7d7c41820cdf13a05b4578834748e6806fb234f5b75bb4f51ba55d812600313d03594c1b35d5dde31ed6bd5d7f166929ea5a1c3204f6ac64c80a477cacb6f64a1d64ff2300408b73869689ac39f8d0853804f87e99c7ba3a2d7e1e9f0a80fdafe63baf808cffba266e750537b218e380d8bda0fd84bbdc9f84febda459b2c9d197ac6f1b4af2b66156d5468bd9f9c0c93711a61638da9078c12c57a625f0f834d2eae3e6ff1229f2ce1eef5c2459aef7830b6c2ba62db36f7cf8e25d56ddb8c575a9acc740bbecc2f96cccd530a793b191d04670de28f3a81052da25b7e5882fcd037337664eae6e07e0eecbb82774d60bfd5f4c0bd1c772cc82a2e01c24a8e2c65dadb8af3f84aeaaea0270589de088e405f4956bccf8a10317febf3570e4e9fd41b9a54f967aa030445a1b15458b7cd681a6ad7467f8d041c1a2573806a62f9a7f702f709fac236c5f281912850a7b78e40c866a2997029f0d42ef60444386c92a7a192d45c7600047eef4764a865d60e89cd6801723b0cde5ddadeb6db038d74647fa9312bc4065a47909592cb1e092eb8e3a08f3c63d4620a62489d908c0e362f10e3e8df844522089b51546bf32e6543074bb5f247e21ba631b15213acc13c086db654150bc0f90b89f3e9ed844cc74ba9faed215fbce5d714c411dc9fe12cb4c886de894b03d76b9dc3928c0c2cdc7f0f34a1c1cd12183be98395480244346169a0c32113d0421a39f7236d69733533ac5ede44f5f4530a0738680970c4d248f9de362ccd203c70290dc7f3cb37fa19062f7372de01ce955d575c85e5183d4935e483af2617574aac16baff1a247db14fd53c11241d6d745e10862b2db6a13b6fa6a57bea37ab39fb16f436467fb05044cb1aaaa5af75fe19c27857ca8c6387221bce1ce07c4f399fccfc925a18d851686feff548638906c351ae82b4f2355f2eb125b73fd162fecc0d0dca0daceb8a01e39c2237f4638043fcc63e02ea79281258fd6df3b38949afd011976a40a9430de8253b903ce4f8b3c1b456d749bc15ca1d0be60c480af91a1d039e2127b0fb10817241516cc8df6e5cf6e1a103ae4953ce38edd100bc475471ec85329b42e90fa0203e13deccfe1d0446cfaef0b2993f1cc2cd843adc879602ae4d5a4ccf8665abf02358df1d0fda6fbf5993008c44b577b5e94d8d34a60500a1b4213c7bd0ec486e86491df0a0deb5419c967b89626d31c4c926ba92f6b89fd1fcdd1367e25cf3d75e00098bd1c9fede14c4384377c5ea685f00343aeff115c4fbd6422e7df131d032bc03cb8f498016e6c13aa81cda7a88d4d714ca2bbd4988d4bf63bae4b2357cfaa7cba8aeb714916d8c7caa87826d05d24650df491cd48f30a546e4f886db2b9e26175435c92d269ae6d271ace372cb701d747f27faff394bacf9cd987267ad7dba2d557e72d4552914d750410cc775fc8f376f7d7039818f5ddced90d87b3ac61973b42d1b4f79baa6041c7b034227fcc6a18c7c1cca22b6ef286344d15bf40e522fa7d89d59a88891db1f24cede3f3621dde8ef30c3577f72c1492458353a0832e3bb60e968d11dc8436f6ba42f230504dd65aab41c2d43ca804f2b9967e57403b602f443805f4132f8cd9c1bc1fbc7b081230694c1b92cfbcf47d458d2d04d8c2bfd447a506522d02e26fdf7a8fff36ce33b574a7c60f818a03f36fa4caa082c812b6dbfe769310ad4a935bcd3da6e16cbcb0062d3020ec0f3332a77b5f26bc8d299c54d41b829f12e0c2c662e159dd23d79063046b8b7851451ecdaa16a5bd1ce96f068581066d0840ecc687c1b09745add195f5e7d9635a920051a521431825591fbab733182bbc192013617d75cd08ce06065146fe6e7708fa0697d1323b92b7a7ae452e56de8ffccb464d1931b258c4bb55c45736097c0620e5cb89f293d6627224cc75f57c3e23a4b0a43982ff0f274efab6936409fde7d9694dc12c6f3a129c042ac90506eb9b7f377101e8ddf1689c7231a39bd2098af991c9e924b8be14c8b2d1276adcabaa058382d775f814e480a43fdd847465e5a03ffe0663a143689585917baff47fe3cb8e691ba67d7d357e6ad91bfaf7a4d27426a7287f3d2433106c44712f7bc7e59cc7bf9bc9c4073e0b72708e55a512503485493e8bd2d8bc459564fd5adde647515d752d961441c2d804b451ecd96cf87a5703bb55f05be42539f2c778c08ca2090ea1a05f72c78310b4df46d07c823d510981dc2955889a21bbe3c0000e4ff76f08da6d84cce12403be75c3430868b042de31de66ae65e3a22e56a54060194918bbcbfb9a6b7f637c2b982f1221ab1ba2439acf6bbec9d5872cc5d80bdb412fb71333abac2acfa086d9792b28fa0d4e4570135160aff11f0b24681ecd710a5c24b20e76e1c55256c6b89ee5f298c43af027c890533b132855bc6317e647b30a3a8f803b74a21e890f1c4cc332443aadb0f2507f0593c3e243c7feeeb55e802aedbcdadda8d4da8105e7f848d042ea6182a162373adb886974218e0b54681aa2d4b1f148d79933dc31e68e43c26ecda1eaf01ab9626db462e84c847446615b437c7ac5b05c8a82c6949244d2a965550bc16ed726460a4348c6c92e212696ba86e0c2ee95543bc521f0ce2c4f931540ce74d4e5ce7593c504f6db9cd84cb98adb1a21a0ced2c6b029f13bc1826278cf321f7500bcdc5baca189654c0646e29cca24c91c8e60af9333de031518e7426276ed6e3c72baefa2523a66d232641f8692086425131449974607f085b91b98d7a710e355aa832cbe1a186e82182108d5983d7a9c301c844e6f9b658a6602cfc115418471ee37543ee8aa498897748c5c32e02b07f3da6dbe274863d12871a77d664910148dfb3b57f8338884822def359030e671f70e159d1e91349871a69d1cf408f399fdcce2afa277c8e96b907e0e79f80aeb7dd12bab8d8d168dacd21d4600feacee0ce48522682d8eea18b59e8f2e75af4ec7736831a5b30564cf5564fafd8d832e335d68167328e47d98be851c2798c7c43fb773b2785cee4ec1208b95d9ec6508eeb5db4ff79e8ef34009e43a707ccd2bded277154d02339fff87d0df17704feb8b54fad160504e3d1e7e7a406283ff2785f109bf4cf244a397c883813f37968915d6f4a42fac0925149eb3260893bc59b8f6029d3729a681a2c992045abbce6dbd4c1e8ed47656bba849dd4f104c30134277175b470a2d8da1c06a3006b464bb266183b14a8ba73adab39bed0e9881d6a72302209ed1320944dc241e429a98683e50c437485ad27515d2a9c2063417b9fb1b18a9a4838f1cb1becb112e2f79eef63eb53782908549c0b3f809af000d045040439e09ca60a8fbc83c30488f90bfeddd47369a299c6c3c034d3eafb64c36f2ae29120a976308c8069bca308bbb95164d513c0fd1230569e252a162bc0988c53cf84b536c9e7fb358dcfd1ec15b51aadee80e7c2aaf9f3584ecc873bf76921c43f82ee88aad635d3b9ff5269b45b32a714e31ab8354e917eb2687c3cabb90796a6fc950a19219749a0aeb739067d764badcaa43c6d69999cddc3686de35e4d445adc3a4c63c7b0cca56c01426c86a4f4d6649a580652f26fc02465e0a2f48a53a2bd25a4ad58b5eb4d0b8ae49cc9c20981b3be81763f09e9a56e325b7eda3f74f2c03d7914a06747310313855c90ed5dd243bcb2d75a34c496a04035ea96e0365acb8408aac5b6bf219b274c8f1a8802240c46e89d4eb3ddcfc502444bf4412701524fc2b854d97db9192360f0126c5233c194d0861bccccb33aa999d7179e3113b2b2f3a0a1f2125d1f533234e0c1da6e23705e7d433c408e43b482412f83e21bca79bb48dcd87e88b00ab89ed949e9a8cc2764f3f117d1ae42ccedf3593c3574a80ebe9b20a6c52da37290a3c46d1ba6ac7a1a568b126527ff33ff1cc4d7001a0ffdadb7a1351f401bf2f703f172002831cb82d00986a0fab290da8b60ea7efcfe2c0c474ea0e7d918c671350870ac77e063026bd27d8e23a6feb3d85df653e366d9ef9bb9a232eea7a134d7c1ec8add1b42bd5ea3fa4c0eb013cb384cd292ecdc7e1f80af0548273691cba8142f6e07b07ca1a8e8a15c75a36f292c65452cb0abaf11c914da57eefe65b8710289ae406946944b5955381d466fb18a5df2c85862e3d7d232c0c726caf10be6fc999358a441a585a53f4cbbe891910018f2af08b3c2497720aaf4e77ff2c90276e48f00a6a068da66385d835b3a2dd3e17cb61f67d477ceee308d2c64b8bdc1926f24d56186d4e54ca374fdb2ae83c8d617f264700535ff7b9c7c87bd8682392c553382af56914c3b109cf87e6f402a7e18c6535d798300f0eeb4381d99c3cd39b42e70216f538a44cea3c54c074e0a4c16758ac7c29048b5877ca94a51b16a201b566a3598a23e471c02d41090eab5c5dfe136e7965a79cc94cb541e0991c5cd6ec5dc5348277acf0b550f22a15e5bbe89bd5a23ac822a59ecc6b58fc875664ecc6799e7f30a2bf7dca98496d06a6949465dfd61593ae69902d749f2f2eaf61cf7ec940d4ec9f5f7d109ec11e198e66e5af669b1a8545f9b713ae3b192bbe7d265aa23c6201e000ad6f611a55c85549e70a655a092ec4cc096ec25752305439af53c46a500f59c3af79fe6085ac0425d9c50468e593013f4f66c321cf2b44d0647c38faca0372bf2f9623bc23db74e0fd5389be476c3aca2273a241930b85a13d9193336e306d0ce107d5ca9efe577acda71cf579a82bea38cdec4245cb2d1eec227092285bf5294f0ac5d8f9da2ca50c2374e98e28ba8129879520b074375dbef9076bff63f808e406cd1ae3aca0c35e5423f540e7e1e273dd37a8a97fd13b140a75bc0d944771e3d59210920b1ff2c0e6e455944e37f169388c60ce2c8c504fd8a96e5878bda594ff79f953bf0382cf833aeb584695300b1467e58a9230cfee72ec05529f4e5a9e7c1ed3c98498f6c5851dbc2934c1378b6e4cdb311f47d55e0a8bdf2f1b0d34aa911302d6217e25932eaa37f47fbcabbd8845642ba622e7ff40a78c8da56562a26ebd859989fb8b9754cf62d7413b76743fe9e1091a94584e9eb13abd788e024853621a2e6bc3621b14d7c3fa85d7f9262d9ebf4b5b760b140a75e8375485855b6aa44ce28f21b19528851329773f6729264e0baad321c1f8a4aa31a5a0ee19786323a3010bc6b671c8b62a826950c9bb1bbaf20c95d6ac1be01c54feafb0cb75a0e24e6a5f8deb2f4a7ec92e8e9a0d5ce65019fa46c5223405ec4923c4824f7105daa145229fa500a1adec9fdf3a49e684b413968d052e290ec8c7ada892cc57510cef535aa9c1ccf08cb8f11ee749ed3c502a17371cb9a08f73eba2d13f47cd494c807984a362d7a1f79b73985cf8cea3a5b8219bdb30f709279aea1ddbbaa6c1d01c691f9b2936a81bdbe135da2bd49187a38d40424f182f28fc3584ad705b4c3ebf0326ab2b2b36c39c4cb65067a59b6bd41ba541261d02e044fae35bef8fa55e09eac6ee4751472a3bcf82c36349d9fbb49b95805fa3f440f25e530f4e3dccd2e5694de53fe40255143ca537e38dd88c84c6c02d53c4801c7108f6eddd06a03ab1d42920fc8030c76a892e58023727c74bcb157d42cb95acd8a2d5687798abc149a88822fea899d4fe16ce48857433442a49efa5934eb304565490b0f743fb892ca7582db987fb2d7617bfd8683be6ffd69561ebb46444073c3110e320395025c9fc5fd096852883c02ae466067c53bac458976758b91cb69fc4095dd2f5d419df9387e0880fad33d7685ebfcd659244f438900432498d34ec4cd209d2551582422f9ba7f353fb7a542742b127a084ebc0a1cbf93f9f9d60cd8c474fe82948b6dbb5ac20b80964b8b8aa81ce72223849e796aa5904f71a1e8002a326c441921608f318970570373992edfb55064c36e979c1dc9e8704403f7e8628a8eb28a80efcd43c315ccaefcc42708d33f81076673da7f75a7e958daf68687fb823ecd34603af688daebffc8950ec55acb42e71e2cd71f9700444e5ef4f8fdcd4bd1fcd5cec2f8880ad7b28e380fba61ba052654dac8f24c00e854dc5984e1b5a849d7dac8fbb16e76afa8a1a315b9432feaac4583b4ca3a1adc03bd8a936f6254f3be40b96a8a67615da88880c1dcc6fa224a2f79f05bc84d1ecb323925f534cc342e54c18c83204f37a732c71aa1b300e82fc10f82d9e72dfce5ade38f0f305bef25dd74c6e1c5209a1ade1f5bac06cba047437abb087237691f956a6e3ba78cc7801e79c980ec06623a939a74f5a56ea6fd9b2b9729a8bfae9f9f588418b49cf288063e78a2f0a9efa120989f86380d16c0783954b677e4c975e26a79859de1d5906612e4d1db42afb1fa0cdb7bd256fc014ea6caf9b4eccd1c226c517ef9db55905a5901df8aca47316396ef26f2c9badd4d00ba545c54ef54e12833798681ee1c2a89b097d13c0323cf42d935b46787e7f04d0b54531dd899f7c243cb0debc97cdfdcf5042de9cf051a59dc4ff7fbc65f68e1af182093a17db41ff75356c80fd1f27c550d021259ed8bfb761fdf1ff7ad0d305314788678dcff3c18eed8a04e857b209032f160180c2d4f2c70bae09e94878c12a96834f06ce20d2dbdc6d227f5a8874a42a6b32715cfc8442ab6d71a243127033042a67bc162218f502f5ab21093f6d9930cd4e707f21b566647a10ac0828efe627400dd942dba0e3eb32645ddc13ec58380885a95c604c49e4bf3a7e0783c840a953e7f62c342d0a93e8566cf1fa0fc36a2ca9ad23a4db6faac5fe9df6b87c0e5c3c9fa407f4eb5f997bee159faeee40aacde00e857f29a09ef8ad49fb6932d0ab00e3a24b9e17a8b85b40bcf3782c420bb4c19fbf21112b47d614dcdcb1ef7cc5dcd04041daf93fc8235a39e71eae80514bd98ce86a77a9f05615ba80c9bc7ca3c91410f15532070a0f9f30a80bec8b494b5d5306c52d3f7f1e2d6bebcbdfbbfeb5fb48b041b4e0370183ccb749b65092e84cbdc38e16ba46102642d39614302693d0033ea4acf83f5a4ac09ce01eb87fb316bad172529dcdd6ab7e8ae6be2a29a4db292c22b5ffda2dc8bea4005af1693913a6533f734dd5e3dd1c83f5c0366c77284ee4592ad8140694970faab341116239023fc7c6b0524ef83563d893d8bdf926538dc742386d3d64279522c4c904a247204edbb297c961a9d2a79b5932cdc69617bbdba5a341897bb4d6818145834f4327441ff75d2ed6a85a7d1a269cfc3e2e3099fb24c4a0f7ffc7827a63352c435eee9daa2387d9c45389874968ab84429daea1ec982d2bdabe91e3bbdad42b6160ad51aa54c1c8a1d604b63771b36b42679c6d89440254aef07054da6e165a25a2c065c476f5d4e9fd1452ddc45cae3663812dc55e0843c461d086b40e8caa3155dabcadf5da9ad02d4d8935cd8a6df5f858d1c96f3018efdd6aa00a4b9fbe0b644bac9e72d888087ba78f5518d709fd36522ec1f6922480143e3fcd68c2d3b69e462eeaed11044fc58a8ee69f9bdadcf273fe8a61f5da19c0b76620f4e4d54cbd11a5b485d6f332a4fae0b49ea732b7b3cfb86e545ac0b7d5c348ad8fe3d218f5e9d3b4601a5aab55872e11d61b926f92fb93e3a74d1c2e734465263437e0abab23177e6c47c3243a22190233ef538e19589114135409d2aab662315d24818367a05c9ec10aa98c615fe3145fd0a58733a9e4d52c8e47214529e2d40af7605ac47175b6ead48cb4b57e5c2d6c9e60bb7659c0c61984f656f3250b07600c666e1aebcc6fcbea44eb69efb27949901263bb5e35e2b8f62925427084dd919e2e19a04b052d9c9945804e08163ea975867779a3232fd632bfd9fd388dd145a2d026a02739c0d7a3386e233accca2c55ac0b2e0d70a8fb2df273b6e9521c8cc40961bfd51ee16ab9688f5ca7d09dd5fc9331e1b1e5b4cf419d3fd99eb0f9a848d78e1585c5f7e8ccfb10442fe1899475b6ec36574d00ce63c1d56c4db545d55d717be8392853245a3667096555a515e08ff7ed4482c7bed76f57b7c9b0cb1a377d87d75d9deabcd2f163ee02e0f3f2ccefa1b1b18ded8feba61ee95d8207a470dd0602d73c875aba9e25d3d4810a01a458cf4231749f710fbf4cd31f61d118431a7c2df347b2b852fce13fae3777cd326334188905da6ecd93d3786af515e2ce1c7aceb95601d0fc598472fc23cb1bd1e8a28d2c52708ed680cd6c4570786780a82ad7053f70dcf9ce27aaf636a43a2b50b827cb592d4ca4d1a2f52f795cbb14fd55d628c7364b909a5d2bf762fc159b47e99a9ba5ecd7a8a60439918f4007c15fffc2f4276604ef96ce7e9ba331a61de275e672cd15bb8c886443afec7622cd88fa1762f1d16c9201d926391ad7d66073f058074b3cccf756afe83a085b85e624c7754c3716434d4e3c95f702870f49aa3bf79f961a57fea6a12c7c891d7e155887b79908b67e88189aceb4b270e7610b54aaaafe0fc8c245eebc77b03072f13142d1e80518acc2f4363ea9d6b64c5f24add9e5b4ed2806e17c5aac3dd81f53ff57aa4e5941943298deafff489d2e0411fef34f432a526cc0ca27fe146beb4cd9943d4ce2bd661f7555a6ddfd80d651b35ff33289a6a5e72c9ff2d4773da196721f893111a73c9854c6ee17d8c87e3dd4b44894858512005b6f95769c3df0e4f56d3ec1b119796ae48649ab2666eadca4b46f22c4a700e08b33639c963d28802b64370e429452a2c8e902321705bca24098cab9e34922f2c73e45168989867004017e31c2562c03e90bcdbd2b6ff6c256729c151c397921261bf51aa91dbffa7655ebed99b58b3fa0202a70b587cf6b1194859b587ceccb987782936fa674db0185951bef812ae6f897b823f56ab71bdef2cf07b52c83530091f10d01d29e195fc40230f900a6023122b1c950868a0e47461dcbe4440b148d43de31d364e4600c20d36b0cbd748bc538bb6a468bce4e5e5c2aa718f92296846e803a27d13754ba6a549a2aeb05252df0c142005da0b17a1521038ee361d7cc73e20f87928b4edcab0e339eac54473d522b2fbdf4684f5526bcaf5f26f22644bc9b1a5dc7d24bbed24bbc9274250da0dffbb102afeff1f6126aa41dc3142a15d5a557beca8856b53a91d29230a100acb090ea4065cd4e06b70430d36a0010d68e0001a20a0e2eec469d142c471dcbf376789b4ff354b248dd27414b178c27ba4f25da8976737953378330316cc808b19e01948f9c3057fac718e0806be0e3f7f7771c7692ed3fc47113feef0830e3fcef891851f5cfce840062670971c8b2bd53f9d6aa61fe9a45da33e55104b6ee824874842259de31a27a75da30e7f5b6e76533258f531833ef4e8a3893e6c1fda1b16b8cfc0d7232d654caaae51cbf7384eee68190d92a6d28e34ed60ef5a0dd2d7837b67dbd05aa9e05b20692ad14d7da4896eea437ab2b1cdd5f5b11d6f135fa0a5e1f3d166c58799994bf221ba4fcac790fbe443db3dea70277ba4d97b64bc0737f788e27b0c197ffc1203371e8335ee33969363606616032eee3e2b68bf4ec600003070e33ecaadc7d64cc140097787814cbdc08c17e81734719fa1a5a00b36e0822d5cd07247a15a9cb75372b600052d08a30540b48089fbec66e0159a9e9de551d3cf29d9e3a6036e8a70f3f9bca3979a0fe2dca558c0011614b15940030b06a0471c7aa071fff74c9c3ccd7fef3d4e9e662c63d3a892f3f56061057fac408f158c9921fe6274054756e0810ad850c118f789e28a320d7d54929327f95bf6930235526053a0e5e1461e378f13f090001e3ce0c1040f137cbaf455c959d459f163f5d297056d953f56bcb50b2e486deda51783a5595f8f2efa594872cabc308b9ce22867a0ad974a17f622b3a59ba6e4ec2c48436d4d7326ef956d479aee8879f28f956e196f77f8b8fb1d2314b42151c0030a48eefe76dcf1efd951666a1bca8c3d3b4477fbd9d1e4046ffc046cdce70998f0d4098698600226f8c1044d7c96b8ef2e69ce40b104af12e8d041822148b084044146f082115431822a75bca08e28eae0a2b3d539f975de7aa42225cb94731d2411fce1b3252444d433bf7389000d154116116ce033efd1254f33042bb801820b022774b4800e29e820820e1fe8d0a1438439c298439cc3670ea731cd9dee08d2ad479ffcaf97cdb1bfdbc528d4ea0ae93ddb5646b74bbb0d45344729bb33b0d2cff5a94a9ee6b3e4cb7af9edb6b9d37766fc81283e60c4079cb8fb7c010cf73751ff6dfdefdddf41dbd003683cc091ee782d951e48e2b33e973675b439b3f16dc3b581616a1a4b75008d0e8ceeb347cebfb86acd494d3f9d1dec5d150782e000b88121362024471f727821c7147210c941003958d0c01b1a8069a089064e19e04006c064e00806e4d0f9577e259cce1159da749b284ea7a4534d5309494b2a889328eef7e8b2796f2aeba5f8feebda57fe90d351e2a9de7ab6e2f08ea6bbb5a50fa39d936f6397aef7f3d6ffbdec27ffdbb1bf26fdca1f840119d8bc719f27ca46c546e7027b5ce08b0bc82ec04dcd65fd5dc6401718c1027358600d0b1459000a575f58380d93b34bafbcaecd2f568006eebe8342b59ec8308fa6dd63a900100a5060e75c1c93f552cddd2b145061027568d052cf0582f841fcc5c2106f3a012812c843024c4840058338d4c411441c49d69060cd02d66c6ba2709c04c1ff4a4313c7d94ba5e9eb7637b6e4d4e1d865db5d2b7be4e79e12a7290f0a45c5994b6a6dbf1649ce3de24cb5f554735fbab525498e93a8fd1bdfd9a51a0e06c0d1031ca437d8bcf1c31b436ef8e1461b93738d7ad3d4a4a3c4c810a764d3f4964051a412ef2a473cebe8ee463ca3aed3a59e4afea540339c5d5cc7d7a2ac7fdd783798f8d4b30d366d9c996d84a9df8696ba6a43aed8a0809e7d910c2dd6d2f4300bcebe0a1ba835d678638dbc860a6abca1c6125f74addcda47253fdb52a69b946ba552e7f8a8a47dd6ecaa915293023563a811424d14351d7889a4abdd665f05856a715c4949dddd7bde67bc6991de12efc9e9d91771126fa949b32fca6fbd4c3f561a63bad04deb16d9e5d3f4dbd265cb4da521e651b6d288e29e061134d6486d6aa1116682b2a231c54aca4263883ca3cdd5bf774afefeaf75c619972922f9baf60c999f3184576748efcca896f69861c694b9393304e0ba68db11b70ef669faedfcc6e3974da45dadbfc559eeee6f78dbfbc2ddb51d98c74de7a02d876051fe70eb31efcfdfb9babdd14c0617646821a383084020025cdc27486176fcf2a76d5d525bd4bf429cbd19f830ab47b0bbf837856daf6bb5fdb6c65f2c023a69a8c45b62af28e398fcdf36bf7ea7f9d2ac30061d630431c6149f5bf6b0575d919e4a940fda2ff6ed8c6392a39b566e6a4e6fdcc45b5edad11c7299e66d5d9952314c0076927277e4e4a5339082dd7d56131d25a31866ec46923a86af1861c4e0de93623471c722c69030c208e3ee9562704645d9d930be30a68441c4dd1d4d1da5d85f92fcb0a049e3180b1a3268acb897d090c068533299aaddee17e12d4dd56e261349ea16d04abeae05e30c18a2fbc679080c1218f87ec106afbe18e3eea1f551c92f3ecf5af9228a7bfd2f8638965eb4716df2228dbb1761bc88e2ae517b41c4dd939cb9c3ddffbd8b71eb8c1af77a26ccde673a1f155e89388bd56eb9daadcbbada1d087c01012c20c04120c904411b33a568d8059a2e74e8c2e43edf255573c430eac96b1f00c703c8b883d4545f6681e469560b9334c43d0f78e2ee332a72d107176cdc41ca05145c7071ff6d732ccb8abffda1a4526fe1664a2a7d54f25aaa7fb640620ba22d4c55ef67699187166db8cb921292507e2d645a28d122470b0038800c07c81cc913f982874da79d27f28539035fd7d60c669c9a3f197b3b4fbac74237fd1bc366d98ab2ff6bc1abaaffe0e751dce0b899299332f3f93433c5cd98662a0b3eb250e373cf5ca92cc6a4b298a273b2c082cdcc58d6c7428cfbc5789b40108b28ff7ad0c26c167f539076fa7bb0203293c9dad30010cc9d4fd32920487b50a85603805c71c615e0c59d150eb0a2b3a2481520a862892aa050f187fbe440fa594b7cf36bcac3490fcf8b37e53e7db1b51dd69a4ae9fa3ceed1937ffa3415312a60d0a47d3f9cdd1dca8465e4141a986288295653b8145f485184140490c248146da238f379e45adb963d274fb3a39c3c692a664e9ef0bbae9dc9f228cafa3746451d450e99109041e3f362cde527736200081870c68d18995b633065c4889cdffe8babf46c0c7fd2c47d1e454ece5c78de2b0c5802c51c505c008a1e7c7754f62c4e6acb3df1c513443cd1e4892563ea18c366cc6c4c0c633a70028d139c133f4e0869228d6b5348b5e962fb014d9c7f879a8493afc7ad472f239ec9cf39239edd2b7296380edb909ae8f8b2ae906a5b39799a2e2b7bd0c3fb61a28b9e26259af939e961fba0cb5efbe9cf9ea4f2c6a8c86a820626fa60e20c135a989895388ea4b32d55d2124e2cd15a42b504142550a0c40426274fb38886204a0fcd9bc73dfb4f86672e907e0641da29212a31c5dd5d0922f95d92d2a12460b0f3bb5e0a0c96841acf2be3b5a69210899288e24948ef11479ca5b09068816b29637f2b889130e38ec4870411316a4c263161b670911e1633e4de650b1789b7ecc2650b1731ee2f4df58f50e39e3a42d685cb162e52d38eca23a21c31c4ddb736624d12a92dc71911c69d869f8dd0e2d2080fae0dab1a2f020ce7d122a67454ee4b3b0a93f816e14ec4993b4a0f13f1799e720d8c999dff67242929284d128c0866c80f6c7e00e3e48715c62af5b6da0719f86086e34a3317a944f2819ba61998e9fb10c57d20e26db40288f1a939ed615a00d10b90441680480f6e8ad4a01e2af0ca5241a11dd5131ef0d078a88207301a0f3bf0008396d17068882434eb1b99d364daa1c90e426678ccc8cc7cb4999312c8889031d162297822e625464483b1a1c14477e794e4916322a140912514cac31b57e9678e5a98ecf9f6ebc81cf53df2207deda21a83336cb2b0198c738c0d188d18eb48726e8893279410b7a3d0f613fb47bf40b3d1f4121e4412a2f25f19b4b1f1cb6bd7c892131c3d6d6797d2d8a4337a6754dedc052cef642379852f2ff822c517f18b00be0cd1c435e2166d108fe00de028b0156ce286627f25b7e28256f2881c0a427ddbc6ba3bd3fb5ffbb50447ef33ed663735bbf4705b64b59d7d61903b71f2bf967c615bbd394efae8675dd92a583fb4db5ef93cf28af6cbb93cdc9d2b42c881508a50873a8401d0c0158010702f713a12ca8e8e92a1eefe087a976657d07b2792d4d7f2703a252543f2880c9247248f38ca3d8a32e312a7239f4892e4f4bcf7431c8442b562f816dd51d4a6084c11931a7861819729bc78f1628297c9edd75b7ef02fe6e417d18d4255ebc3490f93e4ecbee28e244325f254da7f853892e4662e89df75df74ad4de3d222f1f9abddbc6dcbfe7e7c6c7647d35691a6fb137d8220adf75fb1bf9fa5f60d7d49fcbb6f887b7da571ac5fa2bb6baf29547b79a0478f8d53d17a66dc7a9ad7c543bbadf7443b215d1569daf23c709fdfc6a9bc7fe6c1bc16f759bf05774db7a4329c5d7c533b3b9d9d6d8b42cdb60de9ca82b4a786f11a561edf609fc5fcb504efd6b8ef4e4cec6fa7c589b8d63599f5377eb9dc60d7197797a85d51e81b77ca66d232f4a353521ae5eee8bad79c199ff4e3c4c971ee3eb5dd5b1dbe95b91bd78df566bcb74fd36f1ba265bfd2c18dbb7fa5a10e6a7430e3ee3ac85c071d485a97377ea5f6c0d77fbbb4f12e69ba88e9c2b97731818b1a9f568fe122725971f1c067cdd8f3686b8b085a3b3b20487b3e4dbf1c9ac841082d8727390869e3ee9cce8ff7c3382c252c242c783f1698c45be2fd58b8fa5870b83844c1c1c344f2dbf66a0943d3f2458b122d1bf8ac1777a6cf2f2a3598a5882c4258fe70ff906a1bdb72d6dd97178bc5a2baf2c6a7a4d6753a497a450aed0a0e5760b8b281bbffb8d64a23c7b5d6189f5c890b494552dfd7924586454563915006d113d170430d6e68e30628b41b1070c3e98610863a303466680f5d195a3294047bbf31ccba42dc91a411230f03312cc4353f0c675a2fdd277c42d54b4d9e0a871cbdf4c16e4efa1b5b11cb9394bb1b75c62794a7127a2344874fad337db04713a222b4c26acd8a87150e2b9c2037829008ca62a50656b8702adf45ada65fc6401f7c80b7acaaa28e0281967a7fb19c81a7193803abd451058c2a3b548152050054c8a052a9b4a870a0ca808accfc4b5119739c947272f96f4c8863a9522a10a6d031458cfbdc188f5f2475725fe212e96774d376cacf942152ea904201f7c99594c817a47452a000a500e88c06f404908fbb732525f5f459a4f2f3eb5a13f79b7e9b33ed4ee1cf057ec8f8b13f3a3e73f898f1c9d5d77d97fcd05ef189e22344ebf1a3e7fbe14017dd5a8ff36cc1737970e011929223d5450a875400a294a14521e33e7f6398a61f952f0cca23b285614eb2a9fe6fcc5d53fefb0ac74aa318d16cd843b3410c1bae706e43a1765a4f50282de23cd4492852b4e1849d3740706dc78c9da29d1314174069830028493428459eb0717fd045a5bcf4db24c995c477dd94c7c399acf799eb2a95177baa59d451f0d230ff90131a388180e6240acd89e884086a0d4aa35ca8610635645183026a58a2d500a409174d7498725e2cabd43913a12657df25f5f8baf634631804b1a773f61d7f63d826d20a627d718764b62d4ce27b923263e9c49513dafdf9927e46f58cde3de29bc73d0341da9a8120dd6fda56a4ddce6f8cc5442aeab0951d5d67f462d8cfa8ded695b72d3f0c8320ce349cf1a9d100c669f8dc7dae341ae40c6e7c863433702088b3dcf4733243139f4cde684cd84c26665c6312041326f35aabaa765bc266c90fda921f9d18e854a1b3e76b2c8aff45270cc4711c57afa6a34447889233ee3ea5122f9a121692c8e13edf4a3a0e6949a448c221a10249d08c8136f5986c689301b3684a9f20955883d4347f5a532cc5a54842f2e49aa7119cc6fc08d22dd2d4658b3475d9c265ca2c5f3b79719fb1d85f4b875ad95271cb7ba7f5c485411e17065d5becef8774855974531f531af7a999c45cd36722d1d6113747cce0bbad76444bea08be462ae0ee931af162c44829052534d3c39f5fa4ff75f2ebc650d2fb60e9c70da98c4f5d2ffd70de5f442d156938bbb4520c8aa37c17c948ce0672d2b8e9756d7d994394f3022757a9292dc9fa0952b5e40f9150d04a48fafc58b192ea913f3cb225b4922dd69520c9524da93ba41d9d25e3ebdad7b5328899325cfc21d632c8f115831b8fc10cbea918448fa149ec3d1852308871c99d73ad74225b1a0cdcdc74d3aac1d0c467c652a70583b6a0f6c299fc3563ed05cee78cc6b41792f8d4a5cf3824c9d9a52eb4b156bb30c6852c34b6f3a44850911634290229463ef1d99303496882144645db9da854b520d35ac8d2420d1a0b24d058f082052e73069e2e96a6017cb0821a2b3c405b01b642111526a042102aeca8b08200dc4c0e0469779af7c1936ce790c3150241da238fc81ce411c99940cb150909c9c929e1e40cb47283e3cf2d53550a69b4143e051c525081881d44c620e21141c1041a0a7368281881c2160d0519b41332a09dd0c309f404124c30433301029a099cbb5771cd04134ae8400940b84b234740a9b7fd31fcad51a7df4a9448941012953ccd12eb8541921047925f97e96b39cbf45a0fcb2041cab166ae6e4a1649883399ae903c4d79baf80a8b1bf16cbbd882b45e9264a1b84dbb1249ead32963a9edc5527326905a8ed36f33de3825f2c4490f7bb6b35b5b9af1937d2d163fb4adfb5f6ce75a1a8edab83b086e28ae8dc4582ae6b836e28a341d25a159d3d86848d53436ae69466853b6a5a146c49dc73512522484e9522490d0c4672ecc2281c86caf48f049451ba2461b22f3d9a5720ee1b4214d7c081137ee3ec21a6d046d04313e82d81a61c87d6a2324f139c2100f001cee3e33f6aece63d761d7a87750a8d6932732cc2382024468a285604708526821642d049f2e5bf6885d5472f2b4b98935005c400340199f3e2a799a20e0a1814001106220d4a08190c2076bb40f44ed03e92ee5b7370ad5e26672729fa6208bab97d31e1440f3c0a475a087d6811c5a074db40e469a103c34215a68428870774e9eb69ed59ad238a043e380097727c1350e10a071e0d238b041e3e088b60107b40d88e08888e41199a3451e915af6b3b017fbbb3315adb60100b42027d08268a105e1e25c498b3c2259d8bb56e764fa608f86a387866326f6f9351c1f9fba560d881b9f290d48151a90253420d535ea39b56bd49a6fcd5f9aef68ee447311b48eb306ce1a6ae0a871c749c3090ed47002529b3796ae70d41bded76e350afd3acc54f515af787cdee696ac97a250ae70cc3d408478d0d23d86dbd614e41e96f331f7d968831574fe6df1181394830b599cb92410277082746014c4e4dbdbbc71d038c30c9c328078808c08a4c11903470c202a18e19ce03acf6fdb4c916811df2b2c5126a6f6b3f2b3f4088a796411cd469e2944eff3604fcf8b423db15f89619e92dfd3f75f078642859d2cecb4544b87fe81a6c6db8262483fc671550c47973461969c1a6b10b4ff498ee3a493d211e9e481a4117944fe7b1b1429dabded079d9444bb491d48bb0d8a9ced07251224d2b4f598398e93538ea04b9a4aa2dd24a94f33f08ef9b42fed389086595ffcfa2cafdd14a4db25da0d8e2137db36e36cb10ba2ddd9c25c909c05c56dc1314cc917aa8a350bc3d167f655364bd79ad2fc884275646a53f758a4e95e0141da5365604693814efea1700ced832eda00cb0426ffa4233fdf91094cee9ce4125891a654ce8b4d2ab9351dbaaa7f79103da81da18bc4da1813c528116adb56422a16d10f96ed474a8a48a8c5da36d4c7020f3a9e0b7ce4614316d3a886211fa2fa15f7ec61420c56c0ca38e79f18880141da33d46406166b5a6f2492335370bb00d46406ad5d6832038b255f724a912e9fa9e9a6f5d3748aac7fc72ccaadbf829834354acb20df98f262fb71d8bb1b5fe9595922c9d720059293d35932729cdcb4c571736b0cc6a467b99289c47defe1f7b4a579bfc0d3fe5e16944fc4cf241a24b99dd6132efc6f7bf8b4478caf0cf3c839cb38fcf074adc48287eaa8a768f291738a9cdc4cce528e1669489272ee512c917c54215d752fb2704bd7ff95eca7e7e215ce9fc3c5abfab1fa5fa4934fd36ff5471e7678a00379b491c718799cc9238b3c92d0f210a3e57184bb886529069244224b3148698a41a2442c4d314892942893965db6e441f39022512512450129e1e1c2c935147c71f72eec2861552bfd3cfba20d07d7ecb0c056353b82f04da4ff6dfbb59659eff35647565187835a3e61a7057b57b42d1bf4c292774c4f32060241dad3834281502dc68242a150db76bf56fb6d56a49ba851280f7cad83eba8b78e1271c4f9b58976eb28f94e65ca3b28140ab5238aa9160a0583d454d23ca69ed03cd22b2e1dba3458af2dcad80b05e94e499a74968c54c68ac38caf0db7df3477dbb561c6deb5fbd258c53dae190ddad64bded95fd4d95dafada2ff3d190d1f665f19dbad71b86d2aa3a14fd8c92eb63ab0b0d382422d81d59dd24eebc996499318f738be10f6713471c7370e0fdc676bdab8fb6f0a4a8cc7fff279cd185f2386b05f73ed1a2270bc81830d1c625c5b38b4c021251caf8b73ec0d36eefec6129ef3f3d37a5dfbb5bba3f7c6e7daeec0be84d50d99e64694531b6fa6ab8d34b30d31a936b44879536cbca19ab261c6ddd9d0f8638388cf35eec0ab35e25843e677d4d6854197b84613f789576ab4d9f60387d410c2ddef98c52b9f9a17a8d9424d4ccd15351ca47182f9a1694ad1ee34ba48438bbbcf99cbf4ef71a2dd9c894ba591c4ddd1d823bf1eef48f3eb71f64574d3fa8934bfb66ad190cd740e1a44dc5de733e438038933729802ca13fe62120a14694389244d1ef83aff8724b4dfe95d72b6cf2099f187bbbb7e33c6980184bb7bae99b1823b87b7ac20deb58c31cae8412b8308673f0c71a7c16eac9f4732b0e0e21a1954c89035c42f895d3468ce6838bb34024958d722504ad366062a49c979b1c559a45eeabf8e8a3a0d95343d5a1a20639c1903076d8c13c41881185e8821c6dd85248a9b331a72f23467607e116791861c87e28ae80eead1a2c538218c3bdc7dc4482651180a08a389735ac45b4b347ba0b9a247e7e41f421305cd0b683ed0c0b8c2b9c9f9e86fe7d11371ae5f673f0fe78709859d1630a23818448ab0d4be78f34519908efb8b6f6a4989a6c89bfa6248fb6288bb7bf1c60b365eef6b2fce54cae3c5e7ee45157777ca53553b321a5e1bea4d46c3edda4df61bdef5af756d2bfbadca2888b76d65af6534dcb6fc22fd6ab71dd2edda707b11efed5abcddf1da7093d170eba24ceee2eb22c97c180e1ff0e662d2ea016ca47c0018779ff582f87b40137fe3938b351f17613417a425a4d212494a6dc1c6dde7de628cfb1632ef6c91dc428b3a5c973e93e4901661a696fd48906e4f8b290e789372801af7300ed0e20069c68d9934939a0183bd9899e253e798f1c0bd278b2cb52cc2b867f169599ca06989542d1ddaf98cc39d7f5d4aa4f58358940141aa612152c182a4f5830df84003c8b8bb8cd66f80ea8a3793e2d7126351db2bc8f8d4207805d115292b5460451bd72779313559797a9615643ce3d00a2b7e8f1e27afcdd893f58be8d6559ca9e2ab8244051dee77d436d38cc5f05b549ca99552d1a2c27d72175b8e5a58993365a894c929e320c6e2cc75d22fe229ce80f89b4236a720798e499d31f6eac52e292620c5769fa58c39d2b6b297c214c51c5198e151acc8fc41c68ccf22acab7565ecc9a96ba631bc6517146a8ba4925a98e9c970d895b147260d03bedf746b060c71bf16f43cbce11757e9e759ece589a0b8e34128a4703fc16cf62ead77dcf01e69f758c49d9d27f5629e27fb59f5b57e508ff545bc772ebed86e5b7f0c839b8877cd34f6790341ebca3f74b1d57acb9807a41befa41e74d1fb5f0c57951de34e63523a61c6dd89cf0922bad5849a591347b8eceb6d62c8fd2b6e42ba37e1fbbe66828932eeee4dbc3d4abc352638cc8474778977de4b94bee2af385ce20525ec50c2082582945841536248c5a11289d23c3e5247be8b2b91f4b6f1ddc2ad9f35e57e97d4346fd9a366202c3f8c66597376f79e47efe8e18ac324aa48224c124490a800125e787d0e04e94a6a2b3d3b034ba68d74c70a6224c02001b4af0d75aadaac3731637c26a6062fd527a9f6b561052ddd5f8fa8e308315e0a65d68ef8dc9d86471c417277bd8ff0e0db4694c0dd6b0f16f95fab836dd96f5f3ef760d9f7f7c59d11548c40e1e7a7a5ff6b9df417d183af8aa0a10822ee2e2b116edc3d8d080710a125f657da4dc67e45c40cee6e2f0d65fe9d2dbfb855150ab5001a16408202ca50c057ede7adc730f6576251d64b53ef9a89f22fc62ce924534fe2fc5914e044012d0c51877b9214067a71882886b8610852ad29240fcbf9c3cc112601ee9e63b5528912f2116a6971d6daafb5a9aae8b56127d316a420768533ad411adaeeeb4a083c8448e342c87c0af1e3ee60be02e651f662cdbe8a6b1cfa8a83642fd6571c6eb2974e409a99806f2740babb6b41bc3185785ffa0d05b12647288830eed3d3d2490cc75a5f1043ee3e574124a914012140801a77af35c5e12f860016028e1ce00fbc451a63a150ad5a2907c3171f008c033c71009ffdfa894e9cd49a8af5d2fd40fce14044008825420b84101026770f5f1ba00c03fc60002aee735fba658ff7109ddcec8b441a63e1ab7f06060ff70fbe96175bd867d0454f1ef89a5a2b0b3795f9877490102886e0e2ee4576765a4f9e487cffe26d4645219bbec185106990ba0bb12326c2288c1761ca8cc280129593939323477cb1052d1e521551fd2ce9cad6a4b16c89b845f56bf0e5eb7a19ef975200e9e1cdc483f4bc1e99f14ac4ad6b7f563e193febaa7e563e3f2b9f9f95cfc536e37a318fd0c6abfd38782a212c4546838262187c9efd2ccdbaf46b6529cfb3f51854ef88893ea4ac9c05afae9da21f46df0a6ec57e563efa67e5b31f871f7d7efc72ffcbc26305b7b2d0a55f4bebb772319038dc7dc7473f14c0dd7f68f34319eefe1204e9f3d31aba424424d423759e3edb82228bf51bc3463edce145f66b59412c1b90c4146e7cc0800f66dc5f909b4a1d7b8a0304032737c907381d903e864a0823c36275159c0dba18130512fb3eab0711782862c4830e5dd14f5390c5f9ce1329d22079ed8b86569a34f54a2d2121a2d917cb425f37b2f02a7ca27b85350333fdb994a83e0c07dd2b9776140b5ee1915e6214dbee2e379d7d512c8abb974cb32f2205e962dbbfe95ae9b5d80bdef0eb330eb72de370ab17bbb47e70cb785bd94c360832d26bbc3217b9043b53116f8db1b86d1cb83b079e3384853ebe88220d5f70c719417ce3b26f218810bc47a20c328015dc83a8104400ee4152700f42c43d080aee414e700f62828f309a113653827b905110214148700f32c43dc808ee4102e0235c47584b1011dc838410240536ba6de83caf98b7797721fdec39ee5ec6e88e10040038110802827b900fdc8378104448284389a4f35c22dbc0301215a483204246452570f7d179eac05028144af4919736746012ef1767590c3b2d958243408122f3d805a152221561d9436993264d92a10769da66ae6d9b48f67dd650a7ad4f0a688889423d4b826327533ed2e6fc307cedeb5d31fac5f6cfaee44cc3fc9bc626d5535b18d5b25e17a6788718a43bbb689e9f0dff136d2829f715f7c47ec56dd156d78c6352df940dfbb3c8f936effd5d386e8b7c2ff6f7c16d250a9571b7895aa2a4feaecb8f8f500f0ab581e0c7b674d90256fa1f3de197c6babe05aba4aa2b339efa43a98d577895c2abfa3b8cba5871f72236ec4b610f3e11b7ed7f7d1971a18117a9344c2533c9495fccc572f9f1595fbe883b2daf65750fdb8ae8c78ae867b9a2c61603b87b9159def4cc072ed688e1051b3914e93cebfda28eba463910993afb78518618568c70c0e1f507205c7cb190bca9fab987055256fd8db15cd9783524a341b3becffcd7189497869e87418c7be6499eb6a6de7faf6be56b1d8e582668e9be2f75b65fb2c69ff1f72e0f4f1dfbfb5666a5202d91640c4fe9e8fdc93be777a9303075892439ce7b5157a62f4941ebb18086543263298663952b211615a9f5144e4ebc414a24e7b6a4fab32d86cfc993134ed32cadada9154e9e246a7f18c3f63f3b036734367b571639f5136048b0f70f506ae75c6db99f22eabaa3bc92165169faec519dfff5fbbe47595fac2527eeee1539b52c9162d8cae3d5a7e914bcaa8f7796bf7895f14ad73b02bdeec6abfdab2a8a5762d80a12578f4a8a9095036c514186229c04d11f0e0a1bf8e1c08f047ecef8a0e23225aa088fca8a44a94821fc08d1ea6806da98d0702589868370e8137633c0ce41ca0e5d94d9c6f0d2002046482132043eecad88b805f4c6258a488888e84716295204080ca02f46406380c478fe0d8944d547a237d33602022514285080a400e96c5b7f0b3bd94f113f3b8c7ef00f15f7d206d2dcaa5febc8e7012a8c7c4c40d552f15e8961104c01197284fbf551cf0f3d5b7aa6f8f774e0eea88de78c11cf14231e1d463c59dc2ff6d1295c4756b575648d526ea4c8b8c475d474c375c475d4a914dc61ee3e2bae236bdbafeb7dbde13aea6d739c276c984237add71260c9122556cff4088a1c3abb463b0770f700f86867dcf9d18e158992a64da274cf17d1160995a40639dcb15411c2894ad74aafe7e10dc4f9b76ddb9a7a783f6cd4dbb68114572740fc88112aeb3c434d631df52eaea18cbb447161c302e29e867b00dcb87584a00798291ea43103306548225c1b0124a044248a114479e195060c0e97115a308327c4c4c5cb05e2164d8e60f140140e7a464dd6709f1cb70110006cc0e9182dc161d46488cfd1920ddc5d0d8e1eee382b70c751813b4e0a2e752158e4084122244b8eb1e4d4ae51f3081163c5b1ebb0f4eaeb91056d83171499018ce7e18e8387071d0855ec8e73873b0e0a70ec70c7390108b8183121832f8dfdcaa760c25d0b5e8b858c11130fe68f5c522814ca1dc704ee382570770f70483002e1d321e30ea4482789ce13c91218922eeb116861ad8454ad9ed5152a9b9654d0e643b4219958f01048f3c7f6b639d36b254b0655d5fd22dc6a818621ee9981a238b2be8e3e510f167793bb91d154010dffeb344e1d227097784b3db5bc98e63125330692a0ab28e3584c8ffabe7e8d3f1ce4d4a348e5587116a5112334f63ca2cc860fe90a4868647ae2ddc5de6d26200eb2429c8170abfa6c4a74df8a8c0669190df2b08c069172a42e9124122c758efe11da78559faee8a63eafebc2204fec57f38e32bf38759694aa8a44f2f011c924ffa620d55ac41aa478157b208c5b5795f14a4683ea67bcdd9d0ad723fe8b73fe196f88612bbfa98f27b447a18ab1941bc3b70ad1c5767baa0f69f73caa07ad9747d38f4599865f779f474ed045a768f7ec5a199ddcb5321a14c320c57a4641918cf97594e74a0fd1030d81f6eba153aa8b0f2018185109248e510b2a1842c1a805342c2ea31664b35a97cd398c5a68b23166e1b4dc3dc7472cbcf1b1c20211231686dc670180a0613400f0b9aafa2aff4594db2ee6f6bfac1713bfa8ebacced1f5c7fd7536c417770fe6af77ead71ec6794885258557b3af52f5ecabe0d54800307077ba717e323424e7078829050d8c52c0c0288533661d7dbd15fd87153adc00848f0ed058137004f0434c0a03b887364c6ebb98db18d42314ea98fbbe54348f348f7484c216231486f03be62d4dfaedc6f977c0dac26ccd5f3da86fa5a8c356f6b38a3a6c8575f7b3f6d7d1095046269c3132e14817fac1b182de6713cebf83b6316c05e316de57a5adfcd440421cee3e82c947230ca12102199dd190d4161f0540c73790ee8c836294a8aa645f2f4d8d20f8e1fe6047b72d7ed7d1d936dbf5846524c20f1ca3102ae0786fb5d26da379a434236132839219089084229981490d4d7428131d1afe992ca161493d9d4ea7fb2246a146218c42d8608fa11d27f8c38f11089f3b4e087040e09e44a45b8776ebd917cdbe681b81c0c47d480942b4f1128498c34b10c2045e82103070ff40c81611a02204eeee44343f30e0ee5e421f4508d0640f0fb8bb8bc04116da86483a6dc39107643492affb2d3715a513efabe34051010ab86a8b22d7810ea30eea965fececfe7002da6abba9a63a7734944e38190d61904f6449464318907c2181f63b89447ee1d658030e09b8e3a889c31dc78d35ee3870a859c38d37dc71d4a4e1863b0e1b6d78af1fa8f433a7eba285884600000000831000203020160c872332a9743ea8f61400015bb4ac5cc9902e50f328879041c61803020000000000002006946aa5257dd9110cb67cd7cce717e4a4eb884d14df0b7ba26e8eeeceebe6315f23b0e6d6a4a11d168e2bab9a29134cd092cbcf1e3002f291f3dc9852d04fc3beb8a46812e698a658921bc6e23be2e9bf2591f75017a3c745e77a0ef09b3d22d8678c287b4d6265db6630223073c7975dc16b94f018a2950fe3767beacdea2c5726c0f155030d54683eebe2465d45ab8358f000fc148814c897c9d364915d22f371bdbe3add365062d8ea9021186f08c3b2a2da920d5c50e72746fcd6305bb4aaa8bfde33e162c4c5ed391242f81b7709c55627052af96e07a65cd7cf30d35f7fe6deef91bf770d980b6f04d52036b6ae2677fffbaf69eb9b91eecb2f36c2e43dc7e10ccd4100d428e2800af2a867b747c29feb684e29603ce257dd5a727fa8dd422cdaffa31f1a76c0bee7a7c3cec08568b58e3c0cf835c823656ebe02ead5b674eb61e8b30670f50f7be9948b7cad969688522c7fd03a49645a188cf0904f6648879fc144b0c029600745137fe58718ce449e66e680b35f187bba4ef0a13131aafc7a8230752256fcca748ace63493e87076b0d1ea040720457561fb633156a71c7a37a0045337b11a0db11af894c93dbcb5b1aeac4f440430432645f9c70075e5abe02aec3a1b2ad415d3cb989a006974ba63b044bc1ae40f5bdfccc9366165ca29699eb0d00803a7984dcda32051bc8339858efc7ca0d164fbf92bcb60a6b5d4f43815d554a596b90e14eb25ca061b98cd36ed33253d1ab4662b9726eb0c7ebca6ff0fab14d666969ce530ba4e34a17f3aa3434be6007426f5e9753c38be7597280c4eb90aca5ac16605c1aa2ba1db8f3f84539a32c153fef0b3e65d7c2ab3b8c6801deeabed348ecddf86f52be9233c89cb683aca155ec686bc00f795ef249d9c78548363100d9155c36e8d7a05ba7bf8383888b1983b4857ba1ce595f4577646cd66dca0f34dcb125b1d0a38f9633b4b1b5a679473f93350f656d12464b7c30547185ff37664bae833f474614d60132c6854af0c3c33242cc8bcfc1835841437c9966494a5b52ca3597669dd3c6a1e67a81f2eb0c8c61e70b3f05fc4d4b15a9c529d6f991b4c888416790e1f6f4cad6c3ce823686064404ec26add0f743fc6aa14fff83883dc0b465df2271f0899aa648149445a25904ada12bc9aa8ff00a4e8ba76c60a6bb127a44eefcd8e6de5bdd3927bd7dc4d50b6d604bad5f25db8476d68c412d235631b45e6570eac406051403925bb4ff943e2fec48d418485861fe4b6f47d9c1e448036f9fe9b0b0251205d0f9b91962a6964401747e8e061cd3525a00a8bf988ebf4c7a48ff41e7a7d9cb552ab3a65c315de23598cc4b80267cc4a670ce057b919eb6726c2c576d93963fc7732938da684e97b1f169cb2b70b5d1024a020e7d6d5f8f8f338bb3171eb1954fa8f002b3eb6009466a19a0912d8dc055574ee5eb79db310c29ff14cb97405e8f3704d1891519494100bd22bf2b235d2eed268140f3949712dd97c64dc26e1fd8df744b0ae10430740a1c7143550f7b88f56dc7586a910cddeee4134a096de403ab68f7e0fee76ef90ee329367dc693a10228e145a92e62ee2c543071a014120244ca7cd221d9489bd4b6ae91ff12db8e0786f1e6ae4a67c7c4a49c2321220889ba94f43a1c92f1436e644d546ce80c5852d35c8cc7061e6eb67b5467c8aa22f0430078d72b0140fcdd553ba1d9c771b8f5e62697c8268a5f252000dd91ba1387fcc53b7a31d9ef18395c23490ee2c7439ebdba070e0bd2b224878a261c899ca175c4999e39164ab5985684ddecbb494d3c51dfd0ea01acdde945b6768d5d6d0019d59f23e96a776e9f2ff9e18920b960c4bb0558cc11fb90b1c5aba7f68272b5a3f601c182cd6678fc49d31f110fc405d6c461e216d7a1a35dfcb83de41fae864cc4fe3d1bec6bf660cdfdc56c86d5521d8be56ffab9b8bd4974144d236fa62c9be4eebcbb80cac1ecf2e1a488818ad43116b6e6ecc065bdb54404d73f125f93404bcfbe60fe876fdea728c6ea8a064ed0f8708065b31b5fda9a6ca4ce92b56d435be111a97f3f416a3706cb6e02658a8a20e397f62dbf993137081285dcf060decad16e56d7141d2da6a73e834181822139a11fe4573422af1181824296e24489f22e63a4ca386e5babae677ab97424709101642d7b9cdf6183b7a0abb1de894f9b566507520f5f98a18a17a2e86d62be61d9c9a422b69ecf6a7f58648b1881794c8f6aaeea55a2ebe1bb3872073500435c2fb154170dda18d59b08e4ec795f93bf99d579e91bd839950e76f7cc86fafa461fb0f1e82ad39fd77bcc05282f9c2941c27f9dbf6753e8e283ed3d6afa6656d4d092aed7b9ed4cbc1292244c2ac76fd1f7e8d4d37ede2bc0dd8228b631c27df5dfe3b2d6c1fe33ef1e5705ba09ebad3bbb736757d417435f129b580dbf51a758be997222aa37516d0d4f255a1f8b82e6f869195b775a72c74af260f4792ec487bc86e0e2ccd8819ee85cc12015a2ed437e7c8f08bdb68756c3fcfd9508573036858e764b1d14a271da69c2ed56612b1080e15e6da8abf203b36c11ea35ac9f71e50bed4786973eeef86daa9915e93611cc7f9b73605c4c3c3de052b1f5cabb2a9a720688cd914a98553438a41aa929230d7a166f6cdd0e55c29fc4a85b9063e03f0f5197b3ef43b7080c1074c7d7ebb82100e63adc4e308884c2c692697b1b7aeca32d90d4da61fc3374a084c065dd2da1fa6badceb7ea9ac998992357dd2991bc4b2ba84c84996d4114b3ec87e2d3330d39a31dfcc214a1af1b3f11fc21cd84ed97989ce9ebbebe059b873c20880eb7ded5de4e83eb3151f52503eb246b577718525e3815eee25c1ad35329a0d81f66962d05cf3cd6e2fb461a118aaf9f3d44c75d917c85380a0cdc5c8314a3025061237eacad6912f462cd11bcdf30872f53f5d2322e4d1491a9da1425c39d639289fe661720c7a42f64b1422b614235ce3af0a4cab114e618a009040bc8e0240c13d1e482e693402d119bf243883b15518baf6f285b3dc73f27a93f3a4785447d0779ca5718bade7c60fbc8f9ac89dd8fd951909b279cdba5e4899912886d0ee4aeec0b3ad22b7d2b16ebba0da0b02b8406df25cccc315ce569c3320ec5eaab55a1090ad8dd895c1d45271a1010601b29bb272c832d4c54424ad870729a1009f200c232923543a26cf8ed998a8802d35996c499263beacd3872045e4fadd88950fb50e5243fc5591067d4ca355f2ecd52f9d5e0f51021ffa343f14f1038e191535e4a1c0fb86040ebdd0a055695cdfd72ec8708ee3273454f5cd0740302891a91c5f5ad1a269e7f79e0793259be4e4ee26c2fd680ae06f2963e34c184ab889f9fa965cb1dda87e56718430cb1cc4bab8c7d48307fe95fbb54b5796815e29a380a6d1161cb818d525a2f787341b24ac8921cf8bc3ff99885d82dee108659dd12a15dbd024a28461e4206e4085b449ea2c7a603cd0ae9cd793264d43fe5397d30ae621fa6d3922d5402e5068d6ff004f68af37e98e530bcbb0af5a25a635305a52337936b4f9634077a3c2cc7dcbb8efd1111ced26811f236c210c3bc959c98b7bda09280c3b257e29c173eb3bd13dc6f457f2296d6fa720da0d4738e6bac697e319f039bae3398626334d30763311dc83eeb4e741354e51a3320cf269e5acffc1b07a1c3e6e1eb803d4c03d14568ea501a7cfcc561c85cba6cb553d76cabe87fd5ddb18600cb6aea0141ed0f5ddcd789f2a219388f8ef2ee74eb84548fc4496738827f1c4d97965c94c274c18c7320677eeb033dc75545de45b7b1e2690719722603e2e9f0d44a85eb5cb85dc150a1980f803986a34a5095ef58cc1556a38e942e065e5bca5f06de368600ac01893dcef2ad92c4bf491ce9d01197116a3c464e250feef0d6561e8cb97b4c18e91aee65dc6e3f66cec844da0b39a89ba4d48d0207029abeeb186754a1c8aed7fcff288be3794fa20c8f5d7330ee0f3159b6a5bfd549fcc01857976f8ea2889676c7f1d74ba496a1ba4047e4e79ec9d88cfa239c0bcc86ceefbabfde74658a7fc77fbc908bd03d108dcb7d89180861df6c05102e01ac4e5c55d71b885646501a317f1c7ddb04f31d4406985076518a126cc874a1063898880bd729a366e767d0673a9d0eeba3ec55e5a12c2696620026cc0c5f2a3dbdedea58d5ffd7ace0551978b9d7f135ad834bd851c884fdafee3e0f08a7848194a21be2f9ec00d13d64a0d755ec23b3641213167315cd11a3901462fa1e0a6a4c59815927a94e3be31dcd5b9a6445bc73c05b928a8c8cef7d3f423fe6d2ccd1931aa91c7b1e491acaa63803b284a81304c4721423325d183ba8e874b847c30dcf066b395682a18b0c950ceead2f649b244a9e257252f93b5a02f704dbef00684a10b89be20399c814e99639799be0b62a12e540533101d159c7610ca63adbe5960036f93332d210548121a0ef19914b4391dfe38453f502439dd516c1ebe5b37fca9b0c63a6a820dde9e62e11639bbc670d3bf2c81a19dec173eb44a0f34468bc28ad7fbd9b5998887d3678792173bcaeb7b1a2207c6589e4d786ebabab804dcd22e4e47fc769e6c95efa5aa5ed943f1d424774d1a9159d4a38e75175b126848b9d72e662d41f3bfc49fd99fb3d811ac3b2db31423063050bd0f2c86a132409d28c7ad9f632603a1fed18dbe4c322bd6c174a6d1cdbb7e9652ec6993841b36e97f2193b41f22436edad46399bcab336487fd77c8e8029d2554028ee7571828a24c63d6f159936e532b55428d89dc0ee029a98d096e3c81d99152808e6d799fde3fac1029834f12e6ccad405fd49787d22782ca27733eaaf4189674112b3a640b678d84c004e553f068d5543b485a68420aad2f1f69a457636f11088051d1136614faf08a5b8113951a58e56bb1529d241bf0591e726bb1c10370e675b98a78128e7596d3cda66966c1e5d12bfa4418f09ebdf44e4af08f9b4594f8684120a9c2a6d2dbd4b000e2fdb64ca0aa1d529ce77f4bc443c82a1e92e45331b3d3d158e314409c619ef167ca3f823b60958ae29cb2cd5696ef0c5fe8cfb3388cf681e2bf8db4c5fcef3028fa0849df1e020b5f5a8cf3e5e82744d5bef054b5114c8a7d52055fb9332f03ac6eea6e9163fe4e2b844d8a2408ca616adce62912fd7013771df56c7f32e722c8b1149fdd03ad7d4dfa7d83acc4ac08e355baa3f420a8cd83bb76781f23d556c2a74e7baef3f9f796eea4fcc09bfaf8b33a22f793ca472f41c0ff7229e1f4d08823f577bb6d0c0c740370cee9aa372184845709d559163c69c799f97844a0f6e8c72ff089bc9b0b696a62e14080f8fa5baeef33a23614a11e6119e76612dd2799c71f54902a621601cea91737ac40f43cf15849b2a1a40ad949bae85c4456d484ed97e8802ec53bb4843b940146e312b026c06168abb9f7439d52539e77bcb1cd50e273378989d017ef2d3090c2f37c823d0cd62f45463260bf87ef02c7d0ea9e780492c56bcd2d5004353cd8e6a87cfdbf179a41bd0c870cb6d656164b5c74940c5178bcd4d33951111a35444478293b58a5348f085ab4b7ea9bc658385605e37f4815472bb542629088deb6271afa70c0c8dcbd59e856953986f4ab7265062808155c3e11da392f09e3b542f4248f7e77cc9fd51e7ee212b3698394904f8d06808cdf2eb692edb88d34d82e85076418e26bf099b0fabd194eed6a984182fe38a65920fc6a89189a4326af30e0cf700ce9b1510ebbe67b35cdbec5d0264a0c3fefe1e516dc58d3437492578671c230aade06cbf8c400cdcd7686f9bd1902d91a84387524bc183aecfd1705f35783eeec2d361ff71ceb8c003a63622fc26c7cf69e81811e056d98bf43c66ae9d6faa7a2f825c40dc4ddd6dc7f81ae4ec04b8c77463a34854a0f0abf5b0b0aab76a0d365bccd86554a58ec043fec41cef11c0537eec42142502c532c54ed5c7a0823890969c9c500210b83c9c27c2d2258c971115a8277bfa01346e0c04493b98213e771326735ecd08a2b747f584de21fc8be0f136c0bdee29472c6f0ef3c5a7e4d545b45b8279e46bb498e5a7a6d43fa213a56133d2c1a42158e108b85b6ed160d8ea4fc01ee399cd19ff1e861a1d012ee67faa68a2335acaf5724804d1e99917577d6aa6a1324be54680964c69a4d6887e7f445cf84b81ba13a88b41d1c5807521ae9595cf1613710a7ba3355bdb63dd89c46d4ca3874a88bde0c4174321cf81d8c5b372f647413f02d17b909fe3c551307eebaa5c3fa7914ea0d05c5b9cdefd82cfcbe872855bbc47bbb89559a9f974b0fab9227b7792ba5541990a6d738924dc901a86357b0d8a811dfe865ab5c621f6b02961728fb81c2f97cd20ca47afd844b28bb8aa230f18e8fe45a4939736ed845527e3d4892f65651770a6ad5b8bd2482b422144ebf9c5233e7ec5737da09fac05803ff42b91305c0f3fb8accd3ae6a398817d11321865172817edb5f09d36efc5ed75a37bb4f99c28861f6465ff117ed8a725e2d23591ad1d35c3d3b0f7d98f247337b2e8db6ff9ac42b013e52ecfaf4377626871ff9c20e0010cf9c58ab03a955c7fc12fc4f5c6486892e11fa1fc5d11fc8120dfdebb2a9ff3c12fc175fa4efb7bf1431bbab101b28e58293a9dc6cfcbf5b2ffd0d8bb78c3a6d141300a64457060665ed6c5698d9d09469727980237baa8e18787ed5a41466e0f6618f156ec6186e498bb44564c0c10f25bb2f7060b37b3b8f3824a2ef6426c6864950e676de88347b8298c0dca9df7b472e583c518a62ba65cb7d64019d5c9ca45ac96a2c1ac1ca37be831df6b3ee5d59d177225d8a355cf91e072b29e8e079d7b662e194dc57a6c1abbb6715bb2481b6f2a4146fd1da467431bfb0d26b445ba4eb2b120ebdf9ff3eab7996af8e0a45ca7acac428e6ea46d8e2b476d2b424b53e60c219344e0c744dc68382fb76dd4bc5e475f1cf17f1b8b62f4be3dbde1c5c18d36ae4a0303243808db00280c4091742942d9a60c950a00465c1dfb7d80b042c7016338d0f3107a949529d19cfedf9618ef53e6b44b58c4a4d901f0c95bb6038d37fbfb29f91f8269f1a3be987e8e41ecd3249314e8dfbcae277111ed6345c1a3a790ea8e69d099e630bc6d1c30213d3f264e4b795206d305328a5f82c3db9ac2c0eea173663ddabfcc2203f38096c578797f65b4c19fc621eb7b82e340921eaf4ddbab11fac8340443cf320a9d2db2a71f9b4e1f95bcc52cc516d4d5afb12b1577610e6af4843bf184f3d0cae0e98b037731c5f6f64746bfe79819de5fed38b6c82466bf2dd44fed69f86d127e1a72cc4340ef4fcddf53a96d06235f8766ff113c1e6630dc216722f1b476a0daa128ae2cd06da29bcf58a308e65e10af2485c7c0b9aa191516a7abe879011c40b606df72a1328785c154fd0126a5708298cb7f609dd4669b97beefccb496aadbe6f9e4b66df8283149355b1067a458954773328821f1868382cd7850bc8e498ec563e42608039cb0ba91cfbc0719e53180772ef84ba8074d619f25f2fb1f16f418389cdfa3a6cdd316614f13a18af57ea7c9a90fa3f613297fec408da8ff67c527133aba8f03349b55302a66515bb4c2c27c72b07ec27bac4c4760fd9857f68591e23e88c2521d98e17a2c8cb9e2479bb7e57b41649cfd81a8194b9bd1aab05d6ab884ed97bd27158765243d7a75386687eb2c8d7195b1517cd24832316fb7ae56ad34c4f8be589dab92354e48ca7896eebb254084161a89d282a575c4a233666f5d3f205cda45e06de5de10d9101342d5aaa0d5401083ce563217a9aa7124528fc6ccf56dc4c51cac76d43b97cd59f2a0951fca2cd207578ea32e3bf4bf02d5ab8eb885282e05d82ea2119b094325490871cd65d3ba01c5e2d3fdc203ada16cdaa4fec931f02bf4d02d8c38ecf33d6dcd7c4650d2fa99ed852406ea2a691aec109dfbb8d53c1444a04b4144480d3afd7a06ffc1982a1d7e879ad95da859d9de5065c2ac3ab6b7135880b0daaadde06542bfdaa3ecfd0f84a2a4b50d13a2f942bac55c772ec5bf884c10238c755fd2825e6bbca22d5b85024392190774c270e2b2dc53d0863e6914c06436a6c1451d076decee8ff4f1161ed0053b8e1889000a32df25fcbb5e5591442c7de51fd5730682edd1fa21b2f09215340bb13df46d56db7e8fda9e476acaffdc2654f630aa6c430d850696007f7c04b742709804d7583aabf71ec9d86f4d7627e568063e1ec4a4760fea98ca035e507ebe93f0657f8f6ec0d45265554935105ca67e51c82460dbbb532c9a1c63f77cc2f57bd93f01aa42397e9b2503788b68c48da96e47a7e28d09d76f9c0d303373b11e9ab3070d1816126898fe72cf4a2ca30d18210d02553d47f4b3dca6c151e378cfc59c0ae33692544629e4216710c4fca4a26cd8813ebd1d81e88ed14ff5c79ee4331f917e04303279f3a72774d7e3a82189c1172de5faaf51b4c112d103cdf72087a41628ff01636dc0819258be8749c8e3027e6de84e9f0b47f9a0f4871798da04a23f1cc60f410c60615a557524b0404f6e6808c3605cc8cbcf15845ab85ab398a34f40991439b3a56fe10476273f94a019ce1fb4154d835024ece284206ded36391318722ff58da58bf4d802328e04ec8e194cf19c110a1cffe179c6bb102667a09d3f21f0a4cc8760bdd6347e00ba0d99e4519f384ed6a0717f63472e5e9f83ede16265a5d3d1e9886677bd888a28697c24f0532f1240d8dd9ce5a29d88ebf017967fb2dc76547a2796733e677fbfd446a48cf853e144bf17848c4f4bb2c34d3625d25e90495b125d4cb9af4fb60bcc593c77c03993133bfa32200d00290f5ef15fc0092dcc9e907fb047ea39725ae04aab35afd048c9a36c3b7fde4e2dc1e9f5efeb0d46e568f8b2b51caca956c32184d4d47148e7565185d148f8a30827e298bdd69da203e34ae2828245b4f6bdc59a30274faa6d5639eca972db182da5a4b7e2c273302b757d5f5ee639e8f35d4177ebae775a2b3f18b2e5b41dbcd027890c314cd286df4f14269ae94e9f24e4d42c305fc231a43a123e95ebccc4abbd34b0863f79d2c59862f8cbd5dcf0012eaae3e8b393629a62cfe504a23b2350c28fcc1027ed30d4245f8b2a238d25b4fafd43e5ae2e09eb9efb47d8f6f31dfdc126bdfbe236cd3b01f333281f857ffa22b8083c14fe4506ad7c76f416cace6a6fc696a133c34a78edfc1d5c0b88b150c5ce3acb061f6ca8a1b72e8bff385638582033794a490e8ab17272e04d83c56c2f89de01d608659af490c2a29710a1e70ce12e621e6a9bdcc86940486c211afc60992bf148290bfb3d5d617a82f744bd993f2bb7307da7f38ee7621036780bb820310531b5db405283783c3a9cbed725da540de568336adfb0dac671f5cc813331bf586f13597807f883922b049f39bd6606c1ce46d35f7750198edfd6bf2143acbc9df53ec34f3e07fe314858a1669ecd1d87910fd225626198b179e8f9c664f13f75b86dd777d1f640d18a07752191a06de03821c021a3dc03193af567d113adf77f554e2cdc7c3920bb1f6ef955f7980e9794883fc01f449015891933bbcbdb279dd69c8e9353a5ce2fae71b69e8d5e57bb88b0d88fc58a0a633b1a4033d53ff7bda4a83257402dfe3d1c828769dbaa63958dbf43411092cf6d2a5c40ce11f9f81efa4d2d0c3980f2e74cbe887ccda3a270a32adbd4803251a50671b9f93f2be5fc1778fe3a02268665628a4d5b875c5e9917c3c1935b0e9ded8eca62f756d356653c2532e3a32080369c998409726ab8ec47130ea537a545053631dda4c17482e32c8fb6fe54dbda998fdd892650c83caecc61daa744bbe1ce830c4cb1ac37e743ba503a57cdd78017c83a41c37d425f65eb20bc1610aca8ef01f334a6517cf1036834ed65748a7ea8403772b0ec8530a9172fbcbc4fc873cb25327f8ef2eac42e82e125b0031367251c2dcb58fe1a64d9f416020d85903203f0d1f4860730b4c043bee9cfec7d8d5fdc21928f38c4578331cf500e23ec417ffc9a2dfd1ee0b8cb10b107fba3dbc46f8ab87aca8a4d6f6de0f648037f1fe42675fec7fa90fd6498617ea4a85e1529a2b230a719f6a0dc8db654b446ce8dbfff690131f15fb5fb9e6eced7a33543f111a953a1ffa2e9b64b5d1f2ec18410914d62b21ba21b3591be193f4df3bcbc87f103c0213e52b04da3d2c236273400f82f68078b7997642a22dcb4ba7c07776316d63661990a217ff9789941d0a87edbe462de4aaf40f402aa058f2fcab6f594fc8c0b7a63fb7ba7c1ba3008968d4381a385f19b142683b81b149ff3c309829228fd486352d35877d1f02dfb0e369c023f3ccb8efc624e513b248dbc6d5bb862df525f080f9155ffff8763159d5986f1fbdc9cd8d42aac8433e222ffdabe37dc902f63e28567e87f07697184f7ed349d02a79c70cc81e74e3fb92f8c0580e0be431be8faac079ae997b2a49f0ad6f44d1d14dd21b8af6fa868502ff07b9aa38839532f75f003eb37c513809d1598dd888be841a7f6621df05abd96bc4280be5d15de3954dffa2f71d5b68f7d00d0263215f33fb9636cd18e418a82a9627b2a555136d53d6bf688ebdd88514f7289861448a9b3b33fd59133b1059ec0357fbb54cff39c8ed36b46a5274043cb868f8737b0f21d7a2b8b6cb109ed53440a9431c689bddfb6df2cfeab850d3398bd1fa4b69433c3c9f2de2cddc7ab0494a39353b943792581f29bcb19a0e453e6c2a6a5e36a29ecadf62ea7c189cefb49b9eba0fe572c2bc6b4865d71011486fb60d520e8d59ff5ac955406b7aed1f364d5a0fc4db944684e9e2fcd55e5339f835e34697e259a23d2d1b52d32b709f117695036382828413f62b3d44aaf360e7690249b1a133946a08b4b96dbd9f7df0123b22c45cd1d62b849a0ee8db10d31b2df33404bea0c66c892d60e0392b4dee39ca462ef3d31eda790dffa62a7a404f8356e9cf3a7d6953be23ba91a0220a494314b4105ce1088016ff14f56c69178c69c0ba793d79fe0f50d902953af1cdc8d550e883b811ff3afddc16f13aed83e4708c897262ebc9b293d5680cfffc5743520fa35a089fa6c25b7911ea356153176433caf708a6dc491c2a0b13f181f3e17f27578b431123b313241e68c10dd1efee05baf00dce5452cf5474cce2ab4a53088c2f800d5d65e19793a48e2c9bddbc5d8191cbfce31293761f9430f54be9bb35e06c83bdf160d319ef986e154a260b535d526c8395961774642ed706a41c66f07e5012cf60d2f635a0dd26110d60d5982d6449a44a4cf00b8ce1aba2e70f20aecbc5ecf878b20f9815867fc24705a4d2e91300fdeb79bb83ac106294fdcdd182ecfeb4aac84117cc3277c76572c475845bbab430eea798784e072c6c506e3c417c27326aec3209605d99beed3eec5bde6dd452d889d4d874280dfdc7862461b86b6f9cf7509165d618298655be59b9d2c3b9f85bc96b9ef5b69b1b7ec7dd3b46430f5a43a8afd2a58c321ce79555831100bd55a5797ba08a31237520114678e22a60a9702a0ae263a7173a0c43954fefef0afbe8f97112ed1e81d944f47a2d9239af7bc847ae957c6366f2e76052b81100cf2c7710f4b068fff9058196a37b368a26110e6cb0c17e62a8b947e026defba89cc0c0e42c7145089d96ce830e671eb0e969ccaf719a0e50086defcee01562a97698db2e285a2349afcc53a183e8fc069142a0a7508ab9606c56d60cf1d2ff5a4db4e68d7a3276b7cc31c727b3a9431440fd49e161a171fc809b0c50e071422419f5387933e679d636aff641da810c1d9642127e7081ebf549a6e66c950b5a45143534039b557e0fcf896b5b7cd20773d9aa8af48dccf3f9ffb8b9344b20a74a6218364310e14b34cdac30023f823d4dfb4341364022b1d283ef7e99b61a08a3c362d471c08908aa8888a67dec8d3296eb90e4d325412f7dc0da0ca9d973b3b61e3bfa8fb4ce9eff99377cdb25a3bbb19a1fef0e2fa991785d721f186e672addf01e2666f5994566dc1eb168f4f2eb650c3eb8a2ef34eb230fdaeb8b8de90855ff5973b000c6453281a9c9c57303ccd3437c92c60d9086bbde6694e06892614b92e7b399b68d3d7f92ae06a38efcb41ab739923805d73fdef84be005b3acf6f3310ba5da149a0c4103ed474a357338a874d38a7a44f52dde7714984353a1142c100b75081e55dc3c5b9dd0e86a4d985c79e977deac877da904a027d4c6e014efb10f2304265819bc8a3b19c3a338604a15bad0bd95989650cf3ce44cb2d3951a68ddca08a1e32b7c84e857eb4c4ed01683b46d6f85064353b7be7d0454179c39f52bd8edeaeaef0717a5bf5cd84e92adefc2adab4b7c3df55586c05395930a45ce1c48a0fc3896eff381b5429143765d6ac2b51a2b47c995066c43bc319c8d8bb81bde06170f57d4610504420b6472562a63b55108951524c42b39ed936793b1450c3dc60713ae240ae0725a3afdadb23744c7c9254085a0262c230e0159f9e989f40b7a6c73c8826b25dfc98f34badd0a34c0cb600f4dbffbc0be750d7d17e1cbe1d7cf04cb6fe02d4efd60633fb5fcf36ea72da87bba2b51575df46c300c9e2dfe7ca0caeeae6b40ea6ed876991b4410e9daa4feec8ae035e2a257d47543cdca4afc2a5061cb21efd85dfbd31a7079cbf9d72a177c051b42a47dd885e528b4286dac2b49cee6b2a5dc6f7cf48e80130ee0cb4a4117680dd612981d9f19003b52fb219c52895ba03870e96c1aba3bbc773d32a016ed0f26f5d78940c7b3d159d9e283078d0eba81418dea8663d467799bec449a854376b903d33e17609b5ea04d321e94a7e4d3065c1c7b6a133489ad6c5d18f946fe033170d6a510c9a4ca765beda977f8c326ab9b8038d2e34e8c86ab050c3a1af288a09fec3828ac0d38b6e2036a01e78a12dd1f38d25c116f70f4018fe7c8a6585d744d7a1917c7ccbc0a5466348463583a8748df7458a013ad1975c5b7430f772408343ceafa75c75243c7032c2d37a8a9162b5ef54ac39051b536af5a6ae1501cfbd315e6e5ba85424bb9b8f704f5d61c83eaacb381bd7e1588252ffbb6297fe4423a8b0ed9c949efdf425653675d6c28c8033b6da0258f1d495b8739956fc764eb7c959594c5d608a5573534e6269ec1db3c7e24c195005c325ef7f95a35106f484c3307f76bf435ed8059722b447b294281fe92cd1c0498200770fb6a753badfcaca423b8d84512de993a280088841aac6eef0236c02fe3629c738b64c5edfc188d457f852722548fb0f02b8704f2968fc1888fc639e4b1dc1a9d60cc2d07ebf420fa2c0acf30ed26168a22ba8f1b7639a30f22dc3d0d7363fa485c7d540848af24e7a2459fabf8157dcba0df3903bfc45f34e37821115981fa6d510dd8364e67f519b08abcbb776cb3321e842708fef7d7f2ea60e7cb56f748d4f9bc4f315e07fbd1ab3a1f1c203dc74dfe4961d9c18a09ded1ca356c9658baa02e211f5cae2066f129b2e6647032842b3197f26f8e923fe429205c4437195b1fad0a240759a268166187118ca947022db983910fd7c272788a1bf3f2bf9cc943e4c4cfe73040c4be8f613c25e039dd7e19f05866984d0916deef5955949e618daba0698a68ef6bfcc03472ee2414ee8fdc421b517ca98ce0522e060dc4ad6604bc83f52d6130f719054e6b29072034ab2b26a64e0b4b4ea0ee0d70aeaa7de8d6515bcf960683f283ddd5a15ae0e689343002f730075dfaec0e7ea3666ecff35522026b9da06dcedff900631e03f4892cca6074cb4e50273ea8f6bed889c7384467234402c2a649d4a2b46108f4692f8b11eb482a41cef8ea882e1385f38ea3c8a9a113ef090ae830744709f2be8cdea7b53609071ad145449799be071118884e4b887cb5ff93d80480f123da86a38103125bd9b5ffc4f08e658a060e5debd73d226b44b36a78e0dd266248b8cebd21fc146efa8f28f48521b52ae2cc9a76a25de5a8baca24e8f22c125d31cd1f633e1fa571a260d485857e7612be12d23c64f4b988d575582066b6364bbc44c9618f799eff7017dd0443941a39bdaa9d6bc1f6752afe5a338bc5351b5be2cf996dded6dcf70cce2f75ee9cdeedaef0dc203d31e90e925145f4dd77e6effc74cb2e63a92623dde1d2146b9806f6d802823d02b4d5ae61c09cc258631d22e065b5f082c95c4cf04bd7093ce06659058dc291092eabda597533d2ac904a4c0f22ff82502e8d61ec3cc775d04fe31f25c7e5285c309b46eda8149dc2d35f7fd23c2c79f92287f541229e640dda27a79883bb1b1ab111d3c1e344ef294c41b83ed3f2463921609acb9ed649fc54a55509736d48d3b019e2eed28dd2aa16bb45c13fef52e0325023ebdb72068116f47a50c41e18a9ec9e87e842fbd02327a69d14befce18fd43b01e27c6651734efd3303cbbd069b7825125ca3c8b5ba26d66297b62295a2ea42f1c4fd8b4ff79512184b76a6d17450e72a5b7e06243b84009989eb857c8bc1bb5f0bc173375bc7a86228ef2b11082057e55f02ec23a46bc5cf7ae70d01669d36dc983bab75e2bdfdac419d9f598ca60bdf749e4175b3ed3d85ef5509debc9a3f08322a702464b507a5f675068d022feaa3ea10505fc0a77424b42bc4feade695a58aa7b8324017f4081eea4254975693c01b3896587a178b1494ae48488f88d492e4311a36dd12558e8c678d3554c75b9feb836677d5d53317bacaf4c96360c381414cc747da1962687b10832ed0e262431325c39688e1d6b9b04362a28461a58a5789b442f0d6f579d07dc12f8e702116287340a5859811069876e6d8b819d06bad99b0b65a57cc3597ee4120ebaecbd40f0fe9a9c76619ff2c9f59d03add0fa970a7b6173860a33bd41573281fcf7fc727041d357895e400094267e63e276eb1e1886d3fdfde07f1c7a7c197ae3807a29a9dfe08f0dee7596286b6a9782835ebe024f477ea77e1a9cd6047c64f22a8504f794ab8dd2d24df92c4a9c593229c3235f8c07ca4876af037e81534df944fe61d262bf0ef17dd7cc2b734ab8773c28bf336599a0ade5d932ad22a48683c5d5a178a2299c80b08314a01fb5bc8f0d5ca8075918b6acf6274d3b013f5bc4268ede47bcdfbec089beb97ad6401f4a1534fe618e5cbbdea12f9384992ea4763a76aec09e108cc7195f90820f236ac9e55b0e9557458d0d2ec594e34a8630919333531564939a91cf80a98acaf17e9fda36edbaf5846159d6d6e151deb731beb5d0f20598aaada1bbb408f0ea467069187b4ad7b16c71406b595115e91a3a300409fca9e03b66da819f32db23816a0e4577441a7e13e702b946085bcf8b2f804e68fb230408fa59e5293497f93cb121f615a7dcbdc734018c0b5fdddadb93a69176c828548743ebcb0b92025a1aac796979f9be68ef12e711b8b5100132d2c6d896b705380b39deb13a397bbd74a5c6378d7f86e52544bf63285c25fc3d41686f36fb7e15545788c632c3f2e8854f705f9a58508de26e18a61cefcd5540a56603033875f9122f6dd48a99a9690c5e0d771f18fe6a63e3bcc229451d0bbaf8606b982f4e64668e2cf209b201cb10dc85b6f71200dfd0f4be9d46150623d2df07961d45bc6e07e41915a8ddcce1f9b3b7e1689d590f60c3f2faf1de4669f037e70a9c249e7caf13f675f7884c35ad875f9d02280737a66b5c1536bfb6c2003d64b94e91788ecce029c6d43d11e8996e3ad27f65c7db055a7869550535511ba33c9ebb89ecd8f9aa834e2561efc5cefc2dd6f00401ae8c55bb5e55beec2c5ef44398ff25d9d861f1b947cc6f1d9063eb1d0add8d080574143dc113497118bda339d8d1fc34715637d192c1f8b6100e99f030af409be598ac6ed5d1bc17bc3c1bb25054a33b48de23e195a3ddd5d95a78d836292a291cb7924f9befc266eab3c38956716974314dd487ff96ab306253ffe2cd6786f1fa204490e7916862efb011b8c53afea81662143fefa724e35c71354df4c150f789096c9223e0926a2ed0790e74a38651eecd3f5277ce2afef9fee0f4c8a073238f7981d35a1e5caaa427a1af3d4ec5bdc14a882418982d054d2f919280846b6f32161908822f8dcfc2169b0642e888d4f7dd5c1afe236148834bc962ab6fa266f9b5949c79c4313ce49a202f8e456b748d56ebfc309f82e85d82f6dd877205d976bee7a07ed81794311cc00b2331488e271fc7456f2388a6a42b46c92205f40fd4bc1024b32a9e42af4f9b3eea2f36ca4100fc5100fd18b2a91442060a3f378efe871cd4210861cbf8b93f00c719478a25d3bdfcf75561ad8985203600fb8f7ae65a0cf99badd67b95243cc8dddf81b6ff787807bcf70331cac335ccbdda8516209e02068f51f67a302976e7a5f602fb3e51250d7b822204c561e161e5de3dec1c0c9e98a860dc5895854ac2a2df1668f62f0b55951b3765838230708c48b6aa0672411ce418c97cbd5932a34e119ce810fcf051d3d5077c9b49daf2e07cd25b0c070c7b81f0a6be875a9d215569e323c1ceff7705f037fc78b5f3b3f64a4bf6afacb35c19ae40c4bb1d27e3a3f93c81ff50949c5d38b783a9754ab48cd1960a7f8a77baa1711106db2209ddf505db45438dd9c091157e023df93069e5ea1b950fb5d6cb8932ae7f9b21ce068341638629dfdbebaa0d117a718c1d9c597c239f8ce7ef05c7cbb29e51a81f54d0f82db264230feccc1b80833df125a07a2f02a59bda437c1f2ceaa437a63ea0fd1c0d80bd21dc06c23726a4a0bc03971f7bc28af60fedb4ed0e35ac0baa1fc2737741f10a732ba56d7b3635c7f820f3cdd4bffb25104b9b6e232af6431117148c8392b343d3f470716253218e8695646df1b9f5e0526091f8b9752407def073a8f35e5181b90b706f6dd7dbfded8a5d6b7b1b781a5fad44bb8d3839d937b7ba00c8433efd604b85582530074fb2f1a82e159857d558d1a6c83d24e608258cdf12a8c773423d61ee91b84a34d79280ec1d288147f40278124787bc23af2cab7fe360e164beb9c6a03184d21eae897ec89dd65b99a7893810c1a04c6ef771395662b3fadafb11c06b746244f30eae5a6738cc84db55b377ab562a261650ea1de83f847021b93aa26070a133a4c50833924c395c08214b291a748b95c4f072fef9c08b3b074ef5ac00a578b702a9a1ab0da4f0aa4031ab99028aca517ddbbf1e70c5835280f722b3be2cf51905018b06ca44a01f02da5bd9ef7eb0db64cb552e04e67b4938ac7381d95cfb8793829326a89d7aedb6a2c467b4ccf118f2f7ee3a82d9ae04355df05b77ac6d1a6d30975078c428c8668666e2c37ca2f6ecd51faf7cc9712d335f39953a205a48f94beb944f56c8fbbc24278eec32cc6c2557a06078ca4beb12db3bd003317afeb9bd2bc7ca28cca4cb6c425f16be79f885993d45b961b423d3a144bca8e309234718c0bd73c73f1dcda74a465a3a505fb579bdaafffbeef30702114eaaba2b999fce119ffc68baca077bc952ec6567655d61f5b8b09ace068039ccf2a92f676c44bbf29a6f7dcba64bfd73206c23170e66c5e6f52b5f3f5bd6567ac0f3060ae0ecedcb0b37a0494e74c3c6ecf72255b29a7513d7f51d4a0c4cda4dc3d08f10716d23db0ce7840060463f00b3e3b8956f336b57a286c0763885cbb5cadc753a75d877f737a5756f62ad7dcf65dd17051857e883eca7a027075014dedabbe1bc401512f138e3ff1ce049ee5b6c2af1a9ef8da24b674182659420f502aa866118b36080f914511589339ff4f1a1ec40eff1aa64caead041d0420424a29a3839193cbf0d1025abf40fa9b5e83407affa6b785f9f3ac4e1e5933446087c739ea99073b2dd470167c5514ea999e2ffda2b8bd64001d6402b29869c28a464240a08a3812f043777516c98e60932c1ca2725250055bc12fb8aed07fc0a50b7cb2c28306bbf3abe840edf9866810cd4915b539cd4908684a6e351f561bed7235aec687a655aeaeb731553491ca307d1c90ac5daad7721bb5e1632d088df021fa0edb0b70bf9bdb25d403589038d84b6ac193a341d19a5b468f8a469a3e32fb7a2ff996c9ceddca2d241285590206d5e08d4e16ea5d484db222b05f45365172b23db323fa01a49ee9aeafa719bad6cbf0de16eddc3bded5891bc9bcb61bfb38038cef509a53119e8b1ddba87ceb5f3e0497d210260c852f67a32d09f5eeb3464ec18b303a5db0be3553ff1f3ce01e23f0b34edb1620297b687ecbce2cbea922578a079d83d90564ee82556f8032cb8f7cd5921726d8f2e607de92a393e59cf72870eaef49e11fcc76ccae47292ef09b3989fd86089e9467041dadeb24c2fc3b436e14407927f08d17146379b33c025a6f958d9c3788309ede2dcc270bced6b9809075fc0708469bee27761a1c0441a2a1a00e4be5259662e91398a802dbe68fb659e4c10e58d3d15544fd80f604a614c8456239410167677bb90abc8d0fa19ad65338359abcc9b1e583c0257f8b4fa1c521c5ce562c48f36f48591380dd15bc360f480f2e0e4298d7401fe1302c98d699f6b6c8a69ba856266038a90ab855c7d9a6db320f18288cd1d972b26803a786b3316d2d9633fcd694ce67754d07921edbcdee733759ce9aad051118518509ccba04abd9c0c5507d46ccd97f929b04567649142047f712ffa4446621948f1cc46d57a9e50acf501d08918d62c0a97cb9df0a79e21772f23174f06e9f92ccd122a412b3f307342c3f81b7b2b4200f14457692c39fa20ec87f869d313f9fd8a8a0cdf8fd1935bc83ad350f0a8e2d6afa3eda6c2d84c2c10fdfa87290c8d0ea13ddadd4fd8bf940f903a0a827cad945f9244e7a930e4d4b73bb5cf6aeb4b7d342d63be4aa7c66fc23333e705bc39eee9fe7487011352d4b9141b1fd5914673a5da1f904455d2911bec0573006b6cb1589e8a4288a56d63a0c264ceb194458eb38e4c0b3ae99edb52aa3aecc7d8be4fb22bf429a7668659a04476b9aa1be53b0d53c0cae13c25ad59dc114b4e74d9f1c8e9a64656b8aea66c45ff4d5d775bc748c0e42d10faf25bc476023cc55103eb1c8755ca7961d93209f051867ecb406365402e9d28f74a52a4e2648703f4229edde7eb83d7a46fe72262fc3f65073f5ff255027493a1966592ea3eed7caa9a690fc618c2982c26a30b3c136e0458294628eb060fc0dd6240d61f0a78aa5acecec4be43f1fb9e33312320ed7a04e3e0a2e4c1e80a5309c4aac3631003f662f972e2825234eb1f6555cce342b2801a90af4d7616613ed55e201b97df66abdc8351604c697337d854c9fd78732201242ba25ddd2007aeb6aba7068e2c078ba9e08511b006fa5ae0272d3a2f4d877a7cee46139be4ec5cba11aab5132dc5ff332e7329fe8dd826448fb8f337265d3abeecc362717e9559982ff8e68adaca3c07aa94e94b317ee281e76eaa57ca26b60661264ff1ec0b281c3e24e79f6b8c51db8a431351073bfe736554bb1e9c7e551a80543a59703037dd2787fc4b5ec5c851a3fb89024975bec5312e4f467073ff37f2662addb6adeced1c5467ecb5f004eaa96a186ef581efc405344706e444737fa50ec7ae88286805121c48c03ee57f00ff52b2111ddaed715c14eb81cbb09aae47b42201c4f5ccdaec883a39f8f50095a6c59f5cafef0a09c2078ae06d3b4313fe509e146b38670a2ef3b1d27e699ef680a23240920f83e306cadc1773209f253b39682778e49d5a2fc2afd78d06970f946b91f57ce9319475e0e3da4866c3909b4ccdb2891590f445ac6a0f721f98b6f798683aa250bdfb0b6627ec1a0ab04c32608593ab310766e854d16a1679aa7e54d11fb98f558c48c3543e7063453099ef138638b34a584b6642131f1798fc06478a62f3b808dfdba336447031d6671d0a5f33809b18af6d21c5d815b617a2aeb64813b2d948384883bd9844a13a09c6b4a73a584cbc9e821c2ac377b8c9d0c0a6e25bc40183072f4164747903d138fbb82889cd6ec1d01e3f244dc893058b75ca1185658d82bdd452bbda2c13938cfb87a40e46af7c0282f8e915c8853dfe2ac2d467ddee3aa9f78469e0d3182b05c3761281433a159010d67afc7f1d0f6c31605c2fa0bf15b889c79b01628c1ef0e80e0f3e8817de609528cd5810ccb40470fec8ee331f95cfd26e06d044c1778b339e5b19cc1f6c919a8c8943a5c346b17417c472bc0d829c9a95c03dee96850a8f38fd10b22888803457869cf7477a829758ebad3bd334f468e7aae3b59b5d1bb829a9dbbacc0a3cda9fe81a6089fccb892300ef826fc95e465bf4e381a17f2be20cf465ccb0d5163097aa0bf67f0cf27e326a861893f1038f82e6fc27341bd2d20fc2c352cf81011df95047fbf3baec61efb5581888a3273967e008f8c9065cb5c4b83e29dce98b5ce3522d0b688db31b99e64ced1362ff54fbea01e0349aca8e66c31c57bda596c60d6d9075cf65d87a7775616990261a85fe8e369254a654a9473c06a71254307a79668a7e47a6d79ecbab3e896a3da919200aefd4e6d9d5d407099393b9fbe6ecc5ee11949813f11eb42a98cd42f050f6cb79c33f931ad250473efa19319eded05bbc8041e72304c65cba45c7651b7203dbb4e37b1abc38b90173b86be784fe0550ce88bd67c9914bd87317ecb1e6c85a17554cb3f8b03c4621e022df685871cad3e492395085db32dd632cba53d28b2c6c71581e1683c0b522cff23a959cdc4662f9457a6def7fc2057e2c1854b0ca7cca0b7c454655d8740f222cc1e00ded8e114fa343dd841d47551529506cb01fd9eace13d3c2183a4572b4540289e09b0720966cffd9ff96b0d82d8aa1be38f099c646dcebde00727dd18f861bcc579a2bf16816831e42f286c286a0ec609adc39a9fe844005386df749bd65d11c3058d870e56dd9ce15e0e5df05c4614171a92eee524da5b8dbcb622e195ae05621bc3d97fd0b90e553ebce459a832f4030fe1ca84c24f375057246cda67fb971e77c5e26dff166b9535538fca4649332ba4c53b25412ba4d42e7bd7a40fe567d285f3f481787e41cc878491792ea8c92c06e81af73a35d9f4b51c7fcb0c9848ea2e6f4b31b233e894108fcdbd7a7fd000cd29c2ef29757ef4cd5982a82eaef3801e24a70f667c8a5d2284f85b813eaac30e20962054db9b3982f099231412290cd8c599b071afe33fbfc4df6555503265f5ab5925a03a6c2686772a55ab93d271b36d51350f9c6d92dfde07b04130e023ebe65e63e7f2c81530cbfd0e7d918608142039865c0c481edf45ec7d7208b9b0bdc18c8e2b97a48d331821144cd4d7b3b269ec2c26136c7634721e0b5139a88cf2b8c66f7f1207ae9f640f1014ce713d3631d9a8e4591e905d446d77206d1d3931de418ba440dc771929572e23d1c3b079b362ae44db5071fb84aaef0f287386a88996d022e92901e2498c71083e76a71e0119fe2d5519bebf2c435212dad6e3aa06ee0844a12c5f218e7440e783f25e6af97b75aaceb4027769e400404e4efe339110a1f274654e220d1b27aef06037f6079ce6e31a204bbeb4d582ed6c85c4b3a2b82874df21e9c01e3cfb970c66147f81386520c8dc28f6cc37aa1b481f1e605f0a224a07969ff2749cdf3e15bba94a2a64dbd36f6a5c0646fc47955533ae854648092cb15851959d970d800662b1a66093910cc0a15a241c1c631b54365d7b1a4f611f79813b17ce4d3e5be150ef1babb0d773a11783a89c6fff4812887a358ebabc76c6d89a1b4eaf0bf5d7f14068a520662670fc4e2a3b559c6a4587407cc8be59e40eb11fc7f1fecacc82df38d36c0c51c7b2ff3264b65044e8764690fe6aa14f3ed96ce6abd32fc2cdf4503a10f8fc45e425e304fc1e677511d202fd4eee4fec6e326fbcd1b5ca9dc8b813ffdda63603c0231071013565d486b8e747012cef37ef00d456d6d429ebb6a941a61e7ad37325dcbad6d20af2496e8b6fb597b068dc33e55b5b44a929448b6275075e3333afe51746125e9b7a62e3530f2659f0995cb2bce7c53ec4dd794a057f92bd6e8c874b0e6962c7aa66b55b743c924a1f88c794b66d20fa32a7bc497be07d0fe41f9dae7ca9584795684abe9c099510a631f4114a73786c5ed8ca2c8e77c0feec068727a046c0f0b77c623b34d53372beb390ac85728a3546702085db150f4d8f39720da72a79fdf8b8c8a43c3ad0394ac3ca6271faf35bfef42c2be0315af8a1d45614f0275d7f98514b1075f2574c20697cb298f2b9c1d7465a5107ccfdc5d2483de1386133b41612e024eab176a5f9dffdcdd5a3747612a53254c1b617a4321985b6bea6824a5d0a59a30bf484f59e407a200e0745ff30154ee9345a8467e065b1f457880dee4178cb9f242e7f0f9543e4f5a1cd82fe74eaf9fbdcfb13cff6902eabfad5aca0c820391614278ca6b835d38a77c11f22333812237aa98d52ed451d3c818b405ee20c212ab66332601d5a1fe133c6a6d5db7361fd23905d7de045afacab1205e6afc8b349a6668454b2283e410b886365772236660ed71532f3e5759855cf7a636ebd5127ec9ed1b57e8477ce56ca733c954e0319721e3b543ea8a5bda2225d626980f8c3be595f1ce4a6834aeb512807da2a71c0e8f3169b0d0687985819e2b0ad8c21a1d2a41a40d6350c3a7dcb6be152b8b5b033b84228fccf42cec640b1c8505da0e12eff2f88f2402ccfdd886d0b37173d98a8305cb61dbda57c93866d602ab4ef16825f6ef9a8be3aca3366563c7146d8ec90ca487c7cafc9c0a2ee5b4fb6f71c727c82dedb39b46d1eecec81900b44e1a9bbc6a3fbd3e1921c01b85b490a941231a1bea1ec9f7baf7f851835b7ccf135f62442de74fda417e59ae62a60f84106004c95406c3573e0bc31c20c24f1f3a7f81ac5ab57ef385e0468b8efaad8974632cb98933371bb3ef127c8759e75c7e6c3bac199b9a33bdf90efbce2371820fc1af81f1ac28346258d4e48177d7880d2faedc558c734c16608028b325730b6de3856bc91abc61c45347b7b8080c045de93ba3946db573bdbc2d40b7438709054ff59cf0498d506b81b4609fbc2d7443ca2508beeffd0b2e7b596117f866eaeb13b97bc7d329e5a04d40384fb22fc8890c91d2914f081272b5cc4403059c9a1b265764f8e1dc206e499159a7f05df4ec70cd45a475a65a64814705a6e24f53e52cfbd97dc93083f5befb4a8290943a6a6f015bc1d081a0975bad49b50405facff089c20bfa9475dab4d10432c6563450d45a3076bec6130872943eed85882fd57d3d435f02ab7e7ddb389bd55280e03dc5e3baebd6403c3db7672e1e8332698d9df66e186c047ae6b5272ae2e9291ad1e599d3fd3bf38e0994b7257de609f25b842e5d99c3129d7cc15fa931aa9062fe83ac7d47eca6016e294340043ea01e388261d204e7abe9659418ca558c262e4c3103cf82c4640967e8bccda78309fdc45a9e276e7c27300a687dea56f09b055061aa4dc838613851a88a44c9b4ad1412b9569e7e84554741b1dd7624f06544858ab27c0989174de78bf6fdf3e3ac1b50f3c84c7ae3ab58f4fb85dd9b5833f74cb18517d387d776e314b644b715dd291dd813b95a4c06ec251f9de62bb26a4e1ef9982f9ac4343be1b86b2a606e349f6b3f79aa18ddfa46eb0d0cb1d5aef0cf82815b00d9d8aab7c51f1b8ba76c6de82813e63bcbb004cd81c1dee0a6822fae485a6afe095948892b65620cf909811e4b19691ebc729f08ececbb6c20c68117ad3afffd8a23f5414041e8b580c8e6a223f6166272ae635dedfe036a99103c1ca7ba390b253d9ecb0b2454278d41dadfde0d6abec613c4bc603f5f30d4a64511b84210884ede27a5f12f2ed253d445929c8ed5dbb1bd26ee04b182fe50256553b2fea2da9868a0621bb5d15f9093fd267b1b24b728ec7e871ef1e85042605d4c3306d821deb07a70f4ac0e66c6759b14a527aba057acf6894f73109350daf380a2e8b47f95f5858063e88e114d9ee22fedf02da8a73ae79f0b7a31a34154cccffa0f81ac63265c22f32b0e9577c72df3603d151cadc1fb0050769077e35d3693cc40e7febb9cedc78889300a7273129ae5d61e898b86a45093ce4141515bb93f4b5dd6dc6997609747dbf2d3c0b53a806f2a6efe9d19c30b0aab76c96cebac48f0264372ec9130c2326687a87191db5c66dad774165aeb8b065d11f9ffdaec28e4e35ac8ab5526940a421b6dc717da4eadb7c590fe1d81b2a989c934c80f1fd471267f43db36f0d1e02f46f4a0f3f735cb8fecfc967de55bc1336e3c7b30116eb12019591292f2fadaa143132ce74ee9ba721825558e9a268a396439a88e84dcf5e32106b31618e153a1c83ea2f4badf60dd1c8fd3dd01cafa28a8a0cda5760485f3ad0d0b92f3348aa3179f44cb4e87dd302ba4c745eeae885490c22ed1e6986714c5299dbb916b548ac0a31e179a989319dc82eb270195447348fbd7d630beaf644a8d121c489adaef07ec3fc67ec77627e57d29014f69e0bc88bc37a6fddcb075d663d976c896a7f2c19e1b499deb1a3470ab786e66ce8aba5493fbbada2733f24c94e58dea2ba02b492a391c8652c0e46972156dea40fb2d25b0a20113904c6d6da8b63526dc9801ecf2c67cdfd35ac837a8c41d1a0972ba2e1603156007783c2143d64c9ba8fd3162e2d5a62a013cbbc98c453e1a902c08b568cfb4f6a22d4cef162db5609f88c512ee70753fc9ba7d33646126981c06565db69829802562ca706afd9b83203b5de6bb2be15a96223dfdec2abbeed839c779aa929f61fe2b0adbf158e793efe37a7fe6a305cbc10fbef01a72b27dd7e5be049a10618672760639b1dc52ad01b89ba403348e1a294e9f60a8c51bed08c48ef3b1d09f664727a4d25de8b38387e261439d3848743a9fe1994351fa1a0ec4e3ab691e9a31a398667d6630d03e55e7dfa0e5facfa7b4a16edfbab296a687ad840c1f36ed97c9bb4daa4a1db789562180f21657ab5698438b26a98dd91fdea6ef8e6ff37006e0822da0b14a95f7522e7102464f030e2772152d47d5d80b90bf3143f69807fa157dd508686cf412daf0b7e5ea69c1fdc3549446559e44f37170c43f2932f13e01a971d40b549d7b3dc261373b650888727e5c88fbb4a9493005dddb1c7cc2f9e2c9c606fbe4564d006e9cab9cce9c6ab570006981634d6ccf16cc2380b724f548bddbd992eeefeddac2472dee2e979a052add6e1b6975bd4127d6890d5a51e820c946b761295ae24e861b185f5a018c94e69c8b64905eba3dfe14a9305fa215ae94dbfdf8b5c18f3d9a82ce880db01a44c86e25c4a010a6eb7059f7ae90b2c207142a6b1f78649635bb080eb9efd1739fa67d089ce37f4f4e774db8f929f4dcee8c2eb5d6b5465e7759ada2ebfc5f3a1a5ef10999bd984c555182c81b45d75f5f817df0f94d339f853a00905310f75ce50ff0eea548e161d8173353c023907546448ab19de9099b8e08277177a8245a98322a3d5aa6d9b739263b1d19732ee81c4265a8de860a8b125ac8c66e508f8e9db854d2e148785c443b409786109a0902020f9e1ab6a9fa74a51e537e1f65ad18598e0ff4c98469a74ffaeb293c1afba12f617ce6bf9451acc07a651fe4bfb876e47239112e48b3a1efeb4a0f543a981c7282d63a76a755805ff7ce80befc9a838205577d3b50b6a2cfd7251959e0230d107a72eb0c9e8d3e9ddd771df70cb8434c5364ccc68c206a988895099ed2ce0a465d7f1854bfad4232cc9832236d9edc2a3fdc34121bf0d320904fcbf0793bfb45532ae312beed2fa8260899a7ee9f91c6ce6fae99981f01d03e556027f97b5a593c0e81a07db1c6777fd266e1af6bec63dc59eb3b4a4bef6345f0cbf26a07404998af0e3860030b1c1ef92c8ea08bfb99836d4ca6623ac2c89c6c20f6d988dee45db7c02367baf2b85cef672e7a7b73ebf3cc6e249cbd052fdfdf4e07be1f47f773f3aa3b0a3bd62c9f3e4ceb8ac63a727a740153451692b5ad548a1785ef69dd6bfe19dff096d49692289b79fe41f17b03ad5c4cd7d37159176218dd54ded3ef7ea409c94bb7c4717f730caf871143cfe9017c969f4d535e4b1c1f03a5e30862db47e2fccdca068798c7b8c44b565787dc379eed7d933a28db4505b1d6ee3675412d67b0ba1e30fb75953765341c11855e6aecb3332ddff7b7eb2d620ef1fae469f134abbe86b99e12d88516dc9f88cbb951779cf7e3b3f4c024a600706500ae3482f0c363433438e9ea966c9318f076e0cf9a79c737f52e3aef39a379484845fcbcb72ffcc2f9799f107077a5d6de7a8a5a6b5d2bdf6cb7e3cea66c2a5fbd99f027dd1cbaf24b4433938c3851ea27b8ac3c3c125827e67a2e8732517b30d65d6c58b80b508afdd43940b43264641d63c01b7f7d2b42f0109e6183e5d657e1762216c03a4c8e5339f596c0d967c156b9f2bf1fdf648f9e8aeb4f1adc7518323e0dec0ee66fba3ab8b017d0c02d13681522faa1e655a2438b90632a96e9e0a99dca88130c8d3ace4335b43ec302949803b8cd7cad1ce4c5fa28662327421e27f7d733bfbc9726a091cef6bcf55b240b25c2bbed5b91eb821dca08587feb514f29840f1ed0e8e5786b7866ffa1064c6c167e607e9a83cfaa03ed31bafedbd63b318bc1b586cdbb1ba3bceb8e902172769a679780decf1ce84cb4562975744cf7f23ff33e6006e1884685d4419658fba7df1c596294195c3ba08714e911321c16730c343a26725ab22e003fb47b30400be2e104276c3bee62fd44d8de6a28b2400062ced3d17a988a203347e83520fed7a3c42d0782e38385dfbbc5aa28bf37ab0239519236018efba7302c1384f66ad0a6d9de032ecc6ec3de07e28d0970bd14bb2486980080f804996d4baeb810d0475d74981d46c3ea4038bc6f9afa9665cf22e0f7807ac9544664bfb444c1cc8b9d74fd759b7d646349fbea78bab483991972e1347ae7adf6e5207ff7b3a3ea7aed9b16085a3150cebe188daf54725e2f712c496d280fa1ab12d5781c2e498c7dec0a2c11ec38e0fe00d0ecc01f534703785e8a4ae4aec12d6202a01bc12294f95fa7d2cdcfd86d6c3c186e4a6e1c5a4f3172bdb83609eb3bed076e66e970500ec6134edc071343dba60a360a2e461a7f1cc03ff7dab0f68b2a05ecd950718410afbae68c6335f0ffcb0798fa8fac5e687e63cdfa59e8ffb54ffbdf23665c33993ff936a1fe23ae36d7fea4007649429484943e9226bf4127e844fc170f274b82c70a89c1e2422477adc9b769d2d800022a743c9cc5c3b9c6c58bb719327f89f2abbf22eec2cb6acd1d2343aa3799f3215350243e5a10cf02948e67d05a2120abe3778c7b56b742e74ba7003144b7178ff5e0254e3b1ccf47a1becf78b92e3a87d4468efb209a319e7e19b6d6de2265bf2340e346fe06c8ee3502ce0222216fde923ae2d2dde3ada1aca54c935df5e01b9aac477d1f0e3532e0155c65f26eb31e4b642c7dd41f51b928f54310ce356603dbe41f7deca846d341a20ba05a90e10372174dbefab59a2c24c78cb7f626d00bf7927e85f01a96a6ba612608c679804a92ce4a6d1ed4b865ddb56dba0b9899fabe90ef57a8dee68b0742fe196c05e3c52e008f725dd22ed42e3a6073db4ef5ba8074ad282d3a02fd3da32534648618fe795a58390975602da2035ded81982f4db5802ec6d7ca005232aba2de8a4098df060caf44b58feee5d72f79719761be24048f0bf34f2a7acef6c5cf9c690386594cba1ab9a71dd8a14808873db1f4be2087c4a1a68e0b8a4e8b6277427bba5dabd71cbf3c40d40f27f7d07ae0bc431bb4e08ebb55a2709173091ecaab2243840cdd3ff1536955d6f9d84e4cb884c1371e77b0d2f2237f2c16bcfe087f97b490084d101288f1d698c17336555c8d5990e299efedbc45e6f71a12398ccf9638acb8ae08fd9a30dee2fb9624ab3d6a800e641ff9ca9c7df8109a56d8680df9fa35d5759d73b39a035c092e315c1ba7bc2f4fd5977d85322b384107d30f083730ff9583363ae84a11a2040a68f2eb9957e9e4b63f80a1dceb43f441b0f41bc8ee71388f14b46e088faf5e9b198575746503fb908f8ba5f351a233bd990a528409a344e2cf00d6a0454abf9d54dac045b1400ff60c8ef43cb8d9e64018c4e2a2a2e28970ec00444af9001d32f909839f079dcff06d4f2c7abcde2fbed1f4af9f8be1153bd8bfadc1d3facd30cca7fd1f81522ec27b16c4aba52d71a9042e8d99f425183b2441c93188adc8bdeaf8c1b60fa2f0a53a3932ed0eef6c647bb5ddc795b8b74f1c78b8c757cd8870137327eb91c960ad6ac10043f230d1fbcb3298c3f003961172d8352e1d81caa5f166db00d94968ca105259d2791472f44316b20624ef32403c4f60b84e2f20db905e79fcbfeca66c6661fc61e9f26fcfa4ad00c973e4ae95005882f8ba82d086d6330cfaeb3d2af3590660a59fe6e3ebf391425a4200b68606b72882d1d4e6bf90fd38f7dba3d3499d1b82eef21352f60f72dfc7442fd3e52d97d05c851eb64e69443adf2cbeb8b3201b160b078a1f569c7def7ed7adc3b22982f009c44be9ebc7cc9a23f145a2c607a34396e1919c84d5791b17f8c0283cad2b22ac60639de6361efd4a03447d05195f2d247f26b027453fcec13394f1bc0182a4c3c281efd1a7e8924f1f6162364657310951160d2ead9f12214f4c5c2102aa94b163acda3a49aeea6740bcd17df9273ce3810c404318011ae9fa1c3f595238f28cbbfa557f11ada54791a86ef030259133d8670dbf6319e29c99c25b79c6aaf76bd2482ba4c608a93e9caca020a7751f0322ef75dab1520f3523c837833c9e89cbcf6dd5acd8e9db24746b894d71d259fe84d378de2139b1f27a3817f6cee95275e6a7ae00ccd4f9149f9fb01cd37716e5e87749c935a89aef060af434927144511e5bbb2c94a44e8dab648f92974e1ad05bb56a873d8aecf8514c386044aa80b7b3fc0ad001517143d156bcd75cc076933ba7e135842d8356c0b7cf25cbafeeb8427ab6e6f1d0b37d4f8ed288e7538fdf5db59739fdfd02ebba65ad79c97190bbcb781bb31b478a14881a923198bfcd0be11ac0284b2906da70b14a026f8ac677dd547d41007446e9d023d4d10deb3c8e8607e713f37e6b2dec04a12584c75a68eec204a3e9dead37a979de6d74bb8966a00d82d79825470779939b62028cf3101dc5bf9ee3437a47e91c83dafcf73b3c5ae4e344d3a6be767d7246bdda2b4250434134454211e29a1eeb30bc833f3d914fedd1c83b6a4cec81804e14ab75da4fdbe9b8b11e10fd3a00b76e22772c936603989419a337ad72452bcf5309ab085737544b410eece6efe96563654d209cc9b34c0b02f1be3bede39803bb43d5fe8cfb83ff886116372272dec659ca6f1be2a758d1237a41daba0bbaf5b9421b5a4e24412ab547980343268f91cde747e755b6e83dc1b1315d8f96a05f90ea6f14e4923fec3fd5c59ff1591189946cbc3a1d6fc858cc30d43a0f23e73aeb62c50b5029aa0285694ec58be9a710ca910ef7fc94e1b1187a4287bfc025024e34ab6a111e0d0fe3c3645da55780344ffe8092e7c0b4252ced63a3a20ee8b2a119a8f4860755b674e8fefa0909fe7c74396fdb25df7bf8d60b87e6b7ebf2424cf3a6b201e315310106ac3f67726f9da8fa15c3b3b5f0921daeb7a5d1a4e38979b2a7d751b61070d281c6f3427f07d4c2eab3d8215984290eb8e5b7b06118c2978f17fe988b99120b2def3ee9c8df4faff46989774008da64cd81537f9b28680e5fc56b0f66b99476280cfd7a6aa5d0f140800f4347f3c8d3b87b30e67e1f5872fd5d050774c05d3111ac5d894c6297f710d29cfef458a3558491a057bc9df18bf820c0671edd50e7827dd5d2933d063ed9920773da1764232b36e38342364d566602a4d7c2431010be8d8c5ae70d538d522181c1c8d1069a02ee9adea580e3e753cca083e939e0c8f67395d68789f4bbc16f633b3938576246783fd6d812220f4648e0b077dc37341e1946d82e4d01e0262c458165908f1f1a704191e98378f9046cf9d11c3bf6130c017a440822113cdf8a1b8e85cfae02e9868cfcf6fd40da5a7065417fe4660bdde3885f88d1889ba9379dfcb53c61ca3ce7b5026e75acb1dda486b6696c837f8a86687b2ac383ba4fa833d596f42f2c1f1f5eaf9c9a1fa7d24161db590e8b409d426489171f16db7e4f8e8178e5ed399133ef4a6fe404f0fa1894cd1b37f1e510f1a48466270d8c01f08b66658ec4abd1cfcd7a48c80113f3fb01fed4a49f21858c53061a8d6ae41cb348f4c39e37253489873d7df1ac77753301b232e700142b0e441032644ef0a887d78382b0a87987eaff18f3c520e1b714e8e83d114345728a805eba4a6f5eaa8f0138789af8f5024da5a611ba93e014d202a031e9e02ee79dad46310d85224b2a1669553c8bd87869842da33e59e2eac16cc0a11be1fdb7b6a4a19a4ca3bbe5034f085f1192243f4c32f3c65ebc3d93c3ef81b5ec63857c631b27f97e348ff11db89b210cccefa6a1998882628145e13f14a65c09ff70fb2baddc52c0795398ddf6f5ef3d83df978e8d303b3a0dea0c27660be6cd4e1cb42bd97660dbfdbccd2234ff1ace5f8647816149e1f2640ec8193f83c0caeda29b60b0cd3dbbf0c4d05ffb8fc26a8d40333592d89a3512dad991fd0d931935579e4eaf013f356685f75815629726b02e1178c509c057a19d3113cac2d240fdf078dec407b2782d3fdef0ff412bfcc42e101cf70c934193d2cc68d692d189df601be2a1bda14888a1045818ccf81c4895de81502259911584cb0386e03d6ed075ce6002f6d2911d3a16d00d37f3f0950848553c7534e9cc6185f17a08089a313b93d087f529ad2bf9657ca788daada538c7db2733c1eb91f085614aadf2e172118ed26850685b93953555d261c9f6d09ae63b92cbe22e622d677a54534bfc8efccbb46aefb4812eb24b71580cebd3b4d7580c7d20f660782aae15f019bf176c344032666626cc6be66c322da7b18a83797eee77ba661b0445317715375b116149f64af360fd200ebad036897fde89234346b130cff79cf6f625d5e306267233803d23a198349a50562029d0da08159ed2a479374a43cb005c39640e34e34b9838508c2fe31570f37be984aadd20ccdba5e4078bbbdff1394f7e79431e9ed77963ef74987ed1018613c84a43f242534e42cd57456380023ef1e36d64fd728dd5ceface26b70fc1809756ac11541554bf341633f894f7c5dc1a16932c34817ef824c5da877df5e102697c82a42f5ac1a7fcfb5f39072cfa2dc6b750fbe1d4ddde250d334e3eeef2fcaa76166a43c4acf0639adada0b8e167094ee33ef519ced633c3ef9bb97b15c7b33cb8a2b902995d3b28ecd86db69c2e8d4c23170cef549b64b9fb307360c702e9a8b6f41f6bdd139cbf40864ea30cf1dff55227fb08aecc4de1dc4c32a6be8966bea3ce4be096eef4e45f949ab62dcb6949063a9e7d316d5684a307b67c18187afc1c0315f8d414022e19abc15d5a2b2e2a34449ac025b6580b4e5cdbb4ba1dde450fe97c1cf91baa625354b8df8449f364024d38aa48b0804d7d6c723dee8e7d0e002240a5fe8f04b84d7c413957b3dcf13e237982e75fb829c890ac53c9be006d75506eb336eab4805da660b1053964f4a0ec437c48ed87a1a0c30d98e8675e4205f57a0d2bda6b8a66c9609df64af3de7c95335d1bf096de6371fe819c7aa2b1cb067f4ee910dfc90bd734076867ec1b2adffb9fc6fb99236ae46f8cb06ec11e2ee3fee0928812e08511953f9d8a90fe7d3f04105edddbea229cbeb1110460e8bcd7b14ec1da70700e6c8a8a594c88a6719ac4900d0e83312fae5266e00adf35e9a4edd5870fb424138e2f7a387d85bc914a84523851c04962658d068af9d0f9809f8e696b4fcc03373d987e63463d52f42659153bcce50b010c2fb69d9b044f8596805440789015a215273af98808803a39a1df49b4f3105aebbf3d4dd0850067969a677c387a72bd0cb60a321cecb434fc03c1c0bb01e7aa66fda711075079428708d95ef1d5901ebc4bd630828a1da34997eac90eecfdb278c2299b23be2cdebb7a180ecd1c78fcbdb5d9d030ede7d89c2ce3b830d699e8c94ac9c58a9fa38e737396363f00a97a3f98425ace22e53c1a45d409bea352bda0eb45f671c33b531191071b069fb3ccbcfdd1bf10902d3774c72cdf9ff8d46c92257c225542f88d4f4cc1a286310a25fb2dff87d0cd8b6faed5077ea3423347c56d162423d443095f1911804680c24a1ce929396bb3eedfb4eb2bcd99277a45eb9ec7f40515090c26e313f2e8298b6dc8f0be8f068316d6a558e7affc3675cdc3457b7253695dfe70975f6050d254e690babaeb0141ca36d3ce9782f91a2c273de9753d3a91e795c9e3c6439af8569c3eec33d56b1e95c6517d4dcc4c0d15e6f7c3da57561315f4ae61809e2efcf058519b9d9047780119815af680c11e62337f9cdbf9c15038c3abe667d1883ab08b1bd42fd18ac2d1606ff9f0d2d1f48d6d020fb032997e54a378e34f93c8a2cf89eead1c03810163d5b146d6e87dad9d78735b70990dd51fe480e733006a9034377ca702e9a5e5fd5df9cb9bc79d199fa3378840a93b99e8f1e82605fb8b1c2cbe65aa36883211d3de58350393a7dde159489d6e37a194d5495018fa9c187b201adf658e1d7f1c3b96fbd10f1e0a94d764c4a9805ce9f0b4c161e16889e05397b23b5500b7a2edebacf64915e54727bb892742849973b487ee4f15dc5a149096713c95f517ab95d35fff8961b2c668854d40fa0e337ec26f960d1ec084dcf9184e91f809694e6c67936927e2f9f60587094a3dd03490ab000d9cbdb53665d0c4083d7715e902ff61398b696f53465f2192c4cc2bfbae35d6e29c24fb2ce69a69775b25a3100af7c36c2b11c65e425ae829f9684932eb9cae8dfd31c8ecdec6c08404881b6207c12a27aac91effb08f2505b55e5df40f796df51fc4edbf69e93cbff99c945b2a33a677799d545b8916d393a6e404e4c4995b536f7b31e1682fc75bc729c0d2e271f730dabf4c3d6707f7e356a8c0e8e8002741de7da93a0793a7fe4606156e5ca2646a093668f5535837f1990ae2faefcbb3d519f3f0a5f2a8145fb7d98a87f89b46fb10a3a16cc5869eab80b5914dbe5c339ef9d577b1a650f3dd626f109c4ca4e23fb79fd9df55395c850366ce20d73e454e7ce4cb3fd6e8272139eb4be346ea5c5ca8e1439a0ffb4140ff0b8becf83210bfda35791832a4233c019ea29e45c71674724c6e4f3d3decb02c91cb78bc39db0b15e68e20be075195c5608f96dd440df8e1c97fa3beb0fac0d3a5847476172be5c4d7001e64762cc39cb73fb9b10aadb9c4653ed671d9029e32f769291edf4c368059d507c6d7aba3ce2c8a27f574e1e357cbefbdbb17a6eac2adbc163263adabe86bf5be946b61a919299d49a146723be85a4e5a1859bd6b887e382f009ca7890e0b88f85afd1b54852d5f3f592fa5be7213afbcbae11b7ae1b0a6ffc2fc1cff7b9fec424bda1807dd3fb75a2b6b9c2da85c81e7e7e96846f3ebe610221695fd6b0f1cde6832f60f34190bad790eb68c76016579f8b59584e9eb36480f356141427c8c26cfddb5809c10cab71855c400080b205810980aa6d17bc8a092602ad44dd2bd17e4f8dec7954adc8cfe653dffe7eb100a20f0c6283e6b3cfe469671b08b53ddb39bf08d40a6b0566933bb0ed3a9488e7cd8309ea63c3c44f21f682e46ecfe30cb63636662f5e42face9cba6126d06d9aa755c7ee5295b5065edfe412246c239a70891013857731b85c09e385bdee428b6fd46ea180a964ce8284667c56eac5407c8a6eb8acdecdaa8c8957d19ef6b73ebd7c9a740a1f43e2e40ebbdf2dc6f3c2f3cacbfac1147401eb6ff93ce38657227ff0e0ece3a8dadf6d48ba6d548f50eba393c952bb56c6770734186d081bc937c95937ff01f148db231930404d9ac849076894cea120bb113c43f6512383d3c8913fb19564acee07372f4d640b12b5cdb466fe23fcafb6cba26596e4783333ab6d147e0e34610f0629dc0304956f127eadc55fefaaff31698c02cdc6e595f43a8ce3a32d4ea8768f384243f5266c8496db54053d64e0ae8aff016fa0d42ae9cd2505c7f7f127e917db5c50daa115525782035f18f605d2ce16ed7bf398198ffb412f367ba31d2bd7eef3644d1b202873650a57bbcb5b1b913079c8a363b4024f39105e0f940d5652499b2efe6ca168412daa2b0a78d12ca2fa96b42fe77d86ca8e79f4bc83395792c5e914115c76acc41aaabef63a661079794f3398e7006ce2cbdb86becdec37615657acff6b7ef738ef6d96cf16c9a7812d1d13750c0685c9a9bebcf1ca42a750ecc16d9e5b7099b71e90ab5350d3d7fa329b116d0852dc2793a898849680597df330a2d66f84a92fe217754207b1eebbd1d522480e9f6c43a06203544e598bf58812df48a78c0361c2b0454831c77cbd8745efefca93cdbee22d3d9d0e3a866b1dcce495d128d86c9f7f626148d157889903c6ee4d46b68a5045a3d625b6bcc878a8fcc10c6b2cc3e73080104ca3b9cbda6467316289352b4fad01dd3114cc3939908f40569eccafdd7d78008d80a92b168e91a953ae1b3ee934e7b9c7c26e5c039a5a54a3baf1389c6ac1cf40414b01b0887c0843853b17f00b5ce8cb20b0155c8bc269209775d94e159c41a4e8d17790152990f1a94481467301d9a04215fc14a4e151ad2edc3b27844ff25bcad691279f79777f31868aa8c14e28e4b363b1cb4505b69e73814727690f7f6c16d4b666c3dc30403817a04d928c55ded11e3e38a84d502d6f081a4d6bc619f504c242d832601782bb183bf2f8212a32753f3a7941d67ce20ef1d169d4addde193219d442bc3572eae83214ee956035936808efe6e869ea620c4fac710e1a20a847aa8c0b2a8d3125f8f6be3b5125f769840baf4e28c3b339399dc97e7ef43975e0f670ee0a75c9782adb8227cf38e21605a7a2807f609a028aa4f85f560501ec2838fe9b20e1d1d80bd54ba2d33e3fc80853817c31ce89fab8241087ba692b2645a8073da448d10b91fca3d936ad18929671849620269bf762bd7ac7da155fdf519249d42d5bbd8f4cc28145e40346bd0838dbb1a2afe1ceae2f84695833cda626a74a2b80add071ce8c040c35364984d3b690197f247683480822faad7617849938bf8e8a7767aa53c745270f5006f242917ff3fdde85363509aae78e7ff4f22875faa7ba441b17f9b5c86dd4b59c5f1b7b384a9dca6b93a1924814178c47b499d0495a3994d814cf1ba783776d594caf44e486d32b9a4aadec48eef161d8c38084d8ec71812f42b32a22da9e9c0d46a80b09d36d8c286b46121a77ae93581794eb2e7707acfad235d194143e1387935638dd75326e3d78f2bcdb7c1dc2e6b5e13de4ce5e459c8f96940b7e55b01d63867b97c6b6b84d6fafa2b9fccd26213517c96c185d89ec5133e0e082cf537d64b578c4a3011bc38898826504060766afdc3b1b3fdb9424855e1fa7ae1fe7433aa1aac0911bad07fe73ee5ad7f20fe3185d5c5c9990f37dd7d53afe4d3376c46627c7b130d006f477673696b83bb9c08617b2fedb51c3181afd6b2f76933bf45185d4d5c8f09bc6f334602ef124476423ea8c9b2613420c22eff0bc33bfd7fdbcd7bfae058157a757071c280f401510d4f8f430ec0e1775a13c9fefc3b49d6681585478e936111257b4cb4b61b131ba453ac615bb401393b95534aee4058e8827b120178b59464d11eeeb9220dfcbfc34e52d3c830883e862c078f0c0c2596dfb14f7a97c3a63895fd6b3676ca2a9550310da152f236e307e23ae52192a036ddf51fd44374ff5d85f631f1cff1ee039de2e58f4869a9154b31f6297e06091383b9eccde80ab05ed12cccd238d39cf441a44dd89c9b3826f282808deb15516d799445d26743096bb90050d9b9595794a218c7f072c49d3e7b1dd4a83cbbdeca942a27c2eaeb150bfa035988881cdc864d533a89a27b68078a53840c034eca9b4ed88394c9b8588e08c4aa1b78a0bd1615a7b0d8b3a4d75c507f14f767c086f0e7409c93c0f327b0cc42c1f6789daa1cfce4bf9629e9486cf488fd3c2472519b54411ad5f525afea4c19acd3341fd6a16dfc7330a2b53cca6b21ed0e4c09815363eb655c29183f3152f6fb45bd0a0359990982c1ad75aac826bcc481f22f96af8c0287a4be007cc22908dd37c5f39a3f5b147f32efb3c26801a9cc87dcadb9c7ef31593af0848464cd4a8ac42136ba754e41f368afc87cda3bd0c607aac2444c25968d610e018b5ad43534fede8e89bff0cb08d4469cb9ee5b988f9c91a845c11d332510dc99e7ce261059c693b55cc0b987365674dbce30ae0f9d49dec275e8a464d6d246209b05d58a49badcd02d93732e30635ec75d136643451c6b1a91031e83946563614fd68fb054f1114191a927e0bf7d3cc528c979b78e5fad1fc89e7eb70ffa289c9b39b793b5c7690c0849ad48b065f44327fc403e758ce34b36ac7db4af4f84a1b83c16bc5a6f1fc1e5dcc0b229c4753e3b841e91c6e56f45826111c8e31c01697c197a07c34d883e76838f3f49debe684b971a49971dcf3e40a718c1e8bcb6e0530041ab0458c13e5861b1fbd08115e7c7d68fff2558e13a9f97613116caaa81154ef6a41e09567864c6e0a5f0c1062888bab966a913a96039e3b3482a764a4a938e221581084067a4e271b995615cefbd6db2480548bda113520123a211e62443bbabce8354ec2e2854a5011a545f96dbaaefefed784aea02f3e23d648a4fa7a75cadff7fc4db9a11e85dd9e1c868e551e0535458c2a89ced8d4d2d7f59231db8dc51672e61ca8db4ae0e40d7235c08248919997b9905be327d467eb48c771646b0e04a2e039579e273de0c14db0eed93e3865a9589f8f480a3f1cec90156b3d5265a2bf57decc0bc4b80871dbb09f9d73269b096dc85b8f76fd7fc52b03568f1f34d891b91f8f70d153571190732bc995818b71adcb858ca8be3e6c3670467520cc0f7ddb6bca9d0d2f4ca45ead42e6493ae89abecbc176084cdff4a1fd0e54bfe79e629f886d3a9f7082037b8e5b1210c30950e74bb816e06962a16ed5ad0017c92e5a42888f29896f234875727556313d5b1ab83fd5dc3989d9833bab4b9a34dc2a614b9186adb8d88155007ef22ce979742abeef5f18504fcbce094f45a25209f1890531be7f21b04a9557494c2763564486a3c2a33461e68f3222d40e96dd2040d04370f4be97f9a3e4bb78bf87d8e12ac77a5ff5017bd356165bfc3bc901a1522c134c04af23b6460f1da8c218ef75485d53f45614225991a2d26b80cd52a11115f43352a925702a0390ffcc4a208fecf84d1a9519fe62dad6ccf64ccec5ed539f00b5f833ea0e5688adc95dcfa43940d99929e8316cb7f72d6b713e48e5028391cda0b70d0df1ed5bae60111d3186ba02a4d20d9990eeea0e43152a2c6c8c6dc80cd7ec791c4e9123e10e538cca7efcbbcce121f43296786f4bdeef1d29d213f0c3b729ddaf974f444db2eeaa2fdb8da0ca93244c6743696a70324eeb186c1d76c2487202c79d256a0a60210ab1d1d3472a6e7a644ad0186a207f5611949c05e055107be09f0728fe7932c830421260c8e89a65afec21ac2ff5f34a591534828ae08a2a723570b4bd6c9478d79d5f5d6e25b7e97dd0c475df1bc438a9f12ce97b414eb1d8e11ac58fd1162d66eb3c4808edd66ad5858695f0ed8e7967aa246f868e9b586a1ff06b90ac24f29d98e06cbdc455374d5818e7c6abbf95fbf593ae409df6f64a64687ad103a408b0785353fb89a538ee67b2a59f5b16ba7d561d0b146b4c6258eeefedeb7c17e05e36b326d769706a013992e792970346654dfb0a9855d5294be0700923b8866089082370a032cb610b345c8393e8dad181594e50f2eed3c325704f569503cc2dc3b5a7232362c3114db03c8e737981a90a9b02de86eb385adb5817e60a1bc2d36c3739347ee3dd727367b0eaaca247d83d5c6acc28037bf573bbf553e57d2d1c19bb8ab9a801719132b03805f58a0305859c428b2364df7b391cce6e0bf63c3c61bba9f56288f0026b474bb3ce581da17c8cccb2e7079be5775c7438f61bd8baf92319e21845daba227e56476819b95d76f2c07974df2b63f59643c96aff5df44c5b9bdd2f9095073a18dd1281d0d7461bf357d107fc3a334e987b68147a8eacc2169652b91294c28028fbe3186dc5433f9f39b4aa7e29d62d88a185ecc9cbe84a6bef80fcc201d42e6ce762565b6e5ab72931e44d938cc083970ca89518f874aacff837df968401c720fba0be4ec44bed963b5197f7fb014ef76d65b067562f2547ccc40c185272d27dd7a4221cd16394f57378b93128a9cfdb103af8caa746f3d2b750679297a852ba4bea3443d4fbd660f02c1daac3f064904491c92f094f9b7529c09e0635a2601cff99b12792ba141f3c86712fbb2b9a335acd7b7116d24c744cd39d25e7fcc81b14a3ad8244b8e4cdd64540fbc5c9b197ca6c8f8bebb5e5c6285d41ffc9a9bf77d13ae836e7a4780851fb0c7152d5deca049653001b10417d7db71d5f2df7c28db9fd11dcf93933e62b7e19279c1cebcd61ffdfde988b9e982d1227d0ae9b47e9fedd329d919677357eb708d149a39463b398b204b49363eb59ab151453e414dd63f30174c85e293d24f0ba64c04ca4a354faa714a6fec73a9990f036be4dfe169caa0262054f536554d653205b3fee32d71f7cd74fd2c0a3840554e36707c54aec2e9e91dc3897aee249a64ac81c0816a7f396b842a7e334e84ec7330fe0caf04e819c255bef892b90c6cbb6b1527b77e9ea4a5159eccf54916a30161d49ea4ae3a8c8c3b245a03eda2725be4e9d10b59b8278393bbc5bdbaa26d3c4cb7bc3812b4e847d274036db5838c72bf04b35803a99b622b4c42f1405e35afe4dbfe87b62e36b97791ec561f6110f14fb57402677dc9115ca387704e5bebd1db4547220e0e9e2233e6fcde37f03ffae46ad1a135b4eae688c73827d28115531bdda9e37637afea63abbf41160bc3ec35dba95ec5244a3bb0c421233eea3d57eca514ff3e4949d6788ff689d0ca7ca93a10586da02342feb7d6213dc1b8b4e3cf3a9580735f2dee0d2b73eb2451756b28a8f6fcf49fc44ab90b326f811be76b86e4c951e75c6e1c67e00e1a1af3563b59d4471fe9c2ddda9e75d00dce73e34319a2bb972db290219d14d7bd5c05d8c93cfc93838b8104ea94bf1baa809c7f985d563307631e96a173eb8a72fee6bc3a53ede915a2c41ce890ea5eceaf03ca2b6e165cfdc1fc4737c33ff86284204fda78fa80bcfd0071ecdc281b6b2199db5ae8edecaaab63ecc45e94ce51a424234f9593ca0a4b1482b150183c41faafb9f3586a11333b0fccffa47e61e66151b5122c3e1bf02fda762c383946c0b4121dedaa5ea8d41cd9979a6e30fd354064e5cc838e31da395d032634530ec795fc45fc28b283dfce1c5909ac3a846e6f32b98816588ee2d29e687645de4a3c0d9600acfb05beeb6134d8d968169648c88efc168dac9e27b6f09bb3fc88b1982b1710ce9d16c9444bac7c934a6704e04c9e9e861d0e45dc53aabeba1682cf48bd914d7ffe6a3fa54ff55300dd1f383210fe631896b06d6813be92a4f8d262d26a020678b9c3f488d68bd0bb65dc3d6456d89a7e3b8f329c22c74301ce68cd22decd89658ef9523eb2f2c2ce071972140797820b65944933207f2f4eee4d156dde39f452e36b0e8c207f6f2e94d05ca17b244508972cfb6f4188f8396e720cff8591f8ef31299ad95c913991e2bc89021b9b22344bbd657f5a7746afe51a6d935d016be54ad1feab372d05a2016f6b0b31b38af532d1eb6290b975331206401c9a01d65c90bfb94b98b7459a683132eff46ab8ef32e9e8413e3862b8851a5c8d0a2bca628e82b09ee04bf43bc2f88c8cbb7c2c5d0f38206fa4b4e5c3e5514508cc8f1f6d399bf52f01b9aefa029658b99e00b7d805889541f2f99dcbd9d938ee4ca25033b1e9a53f360498921e3857175feeba8adbf190ab89371f17f6660102c056fbd17ec65b6fbf82b41056df3ca448b0d0e7cdf6a87c4a00ab2a9a46d4cead80d4402ca705d9473b3f253c08f016685a001f6d17f3392015d199a3e0bad10debe94a8a102fc3a1af51f31da7038b53c56e0f2cb990b7f00515c2591e0784aa5cc77315481e04df81b3f546041093b333b97a3f5384674b419d0e01befe69dca8044e0e38f84b8e70e52c1c19dfcda9d0f0487aff43cc81a233d0d03442c64fa56b0ee9ba840df2f7a171c33f77e2720c0c0f9efbb37f1dd4f8d5785b5584b939e0d733898a7362b9720618567297bfe900544ad65657937d9c10fdddf6bb712b46d2c8efbfb40967ba01d5c18d3fd552d8e6d419e33ea96017c1528f3d953e0ef1260e3e00bccc25d439116c9b9b668b7011eec9fd774a24e8e0b48436efcb40b32f82be6bb1f546018f8376f302a3ed1d03f60f1c91130c217be0226a93d973accb576cfcad34526fccd8aefa1ab05a9503f8e5806638fe32e2ca67868b761156207188d6a673f562642f5f8c0bd729abd329f39660c3a113f610242a3880e3d4e7da64505f47f609de7ecf4d02c8238a8906f6af0ad7a7f2fb332872a795723b72d53d57543ccaa6e3530e7bda40e384b96be293c6163507566bce5381fb514fdef96ed16b741c02588c0399448cf877959441444523318e1f9bdd5f36013d9116197d781bceb1ccc8537a21dfdde85b2deee86cdfa79e0b89d130a32c1c523df4c085c2c33f1a6c501f8afd7664fdb9e1212cd6d438527ccec224037cd57ffdd16454636f258ef1de170b45bcfb30b6986b6b65c4b906e111aca090b581d6cba86c764fb54c2b7be5ef1af3f13de4cfcaf2d7e8cd83562aabfd1d90944b3bae6cb2fbe2198dd898ea93d93a4a3f95026a80324a780ed70de2ada5c86a1d19382ee6e1728895ffc138b421a1894817e50d02d3b23f4f5d9602092add60a749e283b36be262d17d8456e1ff169d6d9db3a47012009a8fd629d75bccdca033015da985e92fab278bc9f31a13e4e9caa260dc0525a69643cfed3b1f7deb09d9069757d3cb63b1061c419e6077476b48562f83858300443506d0e313d868d84079def8816bed8440b9f1cef62d7a8cc62cd83f4a6aef79c62eaa693fccc29973ac65d31eee2892b5f266408842976c884f601cad25fd65acbbb99fb7d0f67e6ab821659f8f3c4c8d5955b57e53b23ae65796f96a1b5df54564780288ea6ac3ddd0141b5fa19be7be364c27eb1624995cb0d9b0739e7140b04edecaba4f8855bc9f3af8318d90367a26451b7f5fb389b5ed48e7d0b2bbe9e3537db146d23e278cbd137cac1626e8cffc730abbb8f89cb5b3d88fc6452b9c4074956fb889cdba8bf1c565c12b4763da7ac3f152db8fc306373ee9df499ad4f0eac3e34c3bd3b725f3727c59b8d48afc95555478e32f1a0f62abc491084850acb6e0bc98b326dc97f7b2069130f00593ce49af941de7127f0453064b91a907bdf415a91ddcedce19a69bb30b177a6829151e548c56d341e7905d7bcf38d52bbc76a67868102a52124acc598fcf041dfceebdf6ade68fe46afdfa29a142198918247ca93cd710bc4f05178f03e8d104cf7a1d89dbe54bd7f224cb0c826d255cbf2c77e5da041382faba031ba8b2346586f858b77be356afd3b6d449d8da8da69fa8a98ea3ea48cbd3503ee57d7e21edb41e6011aefced544ba85575a02b666cdba370e99e82f61f25aacc6810f5c8efa1a34685960b80388fd0bf068b5110972b9eef5858317049eb38adfa539712cef31d4eb0d935d3df00799ee7570e574757258e4af79669abfa208d4aa4370bd65441a979b021e618c0c8fb388dbe1002445db8e2e5aff851810df66fb205d2e010a72cf84ddd2a3b8a1249b5c4a1be7a63e8dd83d9e2467c61736495d2822b701779407bdd4ab01fd8d9bd97d32845985db09aca618875ab0bac233a4c1a2225a9f9e0af9eb2f6e53d6c628b2d791d3c0df1db6fbe21786beeb8cdb491ec7a548904e41d96530d31616fe3fc7868a722f22c5d3c6a961c895aa9bde0f5b679f9c0894bcad7183bc887d54b31bb1b542031d31b8259f8edfa1e3f23df965cd856abd199bee9d8cdedd7fa4eca3b0fd8035a9e6bf68a16d195341df2b34eda95a89a1d8afd392e83560690f7be05cdacb1cb064c90dabd02b28a7154dd4f840256ffa9ebb1e5b0b351772a195e72ee9145601678b4acab5e78c2c91e46c0dbb18a59413d156e0a4932cc735ccbce4acfb4a033f712caec899da3b584c11b87309820ba17b40fa7a0da8835bb632130fa541c8ef7976fcb5993324466ac629a41a87867ed75b313fe96e669842c6f39c4c09b4c19f3ad476abc1cd915bb2f9ad659f1df9fc1b7cc4f32c632895eca2f874d81b0c3ac2d2e90937bb117d943cfdf63fef524d8c8da7ef3cd6334465c883f16d0e980759e262a2a3328d5b6abf006236955fd4b8910dda46ae9d376eac86ef4e210adc883c82c0e17a6354a85a83b367db71aaadf51cbd0c297dc2da8e9efa1fbd67aa226cd4a69c150d8c843ddadfa129bcc7ebbc9f9c6d81dcc9d8ed54c22d513f527f23ca4f215402136c9385f74f1361dc3086c4d8c0da537b63217c4660de9ea3e2230f2279d65c6653e02eaff23c8931cfeb8e5fffb1ce55ad6b410abe6f925089a103c04b867a5cf3fb1ad56dfbe9338829f8f9098fe6eeab88c2b87ac7ed654543fbc31280994026fb697c0134043a873ba073f097f835e1ffe5b3f4132c8c0eb3a4a885c2379489331bdd63fd063165dbb600e31770860d378f1d0bf1ff8b4c65cda6492a9bc407d3f7c91a916232672fa070e5a9cdc514d3a74866af2f11f83b898165c2d712681b7617d646d444030ceec0aa90dc695bb88ec1c9c8f1de8757d08c9ca63409b3a3dba33cd768463660c2e2c5855bd4d490eb334a2db335ee93f1912b1ec1a8fd41fb6858197f89996b9f9780065e4b94318c27d296e33f25a63e7b96bb412378656347c98199995732ae7cbc86f489bbfa780e4a8490a208098c0024330b032a23e606c78df1f837107dae01c7c28adf50d0e16e63a6aee804358f8aae14adc63d46a01c09c50f8df4a5a6812db936c11ff1a494785449319d191a45fa3ac90d7430d1117401ab94a569a4effcf519f74331d7da54a1720f7a2729c24062bc5246e8fb9f4eb4348a747120ec0410efe27ff7bf2f476accb111a75cf471296e4f89f44cdafb6a780f53fc1b7a52da161e79999b871fc00c7acd6ff2cf74ea5e34d3a306fa7ecc6af5afe93369e1cf03703e01b88d2cec4513040f08a31e9d6cc4202c3d75bc6967413357feed846cd65da97551c6b931f72e21a69fc50e0300a866934814ba06c3657d221141a1bf2e40761a73a0114bdc77a6260340ec96ca7f15525473bd0564a28e8003dfd10f1055861a01656f766bf5ec069fef58511e6fe799b8432980767088a532ce56e4c9714ad0c5d019a24ca60c2cad218bf7dcb54b15ecf23c8a2ec42f99da6c106baf600ee4d3c9da8345bdd8218e2baf28c5c3df99c1d0a953123c0f16ab8151a146c4a4f20e39c30f50e8ba34ac5d7e6a7b4ed84975ab5c02aa4cd9b0ac73178a92439b303f527f3a912512662708e8c73cc715fadb8fd92c81e3bde1de5348e9475fb02c3f70842d486df3a259fa6f423b16e375492fab6f816c4890815c7d6b84ec57885c271284437fc6aa6322aec978d0b9c476c7f3cadee1c0ab4010e81a7d37d045b1d4bf872174c8740962c798461fcecb63b41dae15406e36df46eca41132eca42393dc782b03bc63573e171cbf5fe9f126cb409fd8f05ce68322a52334e5f65144581d1f397fff56c4c901437fdd77f0a5417bc61038d3e30e37f29fea030f888958f12df1f51dd57f711631c50d140ba45e0673a002244c00d8287c8a0137ab94062197698d66daff4b1b96c71cc181c39c64e88c3033d2f167c39a60160271fe64c0a5241dff2b85810d8add7e5e9a46a34e501165243169781b1ae68370fcfe6117471fa0125010340f3c4ba6362e35512f4e126c6707e7af3a74d00ee7acea5e3e8bbbb0eeb25037a4afe9bfe9155af870bcca4534eb12b261d4db15c97bb3bf5b6adc087b1dea056a757d61be83a5163db3aee9a7b28160b1b108f8c50227c4e97c3f83964bf52cb512ddf268b99cc5bee096262435c9aeb84f4c8e44ce0da6ffb7387b8e8805b93543e290a23e18b4b4384cd2439753536ee62d5a2abe62ec0d0c0a4e23698c9002d6d6f2d7a89964a2f3b128f61463a61207f9c24e9efe87d58db84976d236e094a4e4e7332ee668f5248d7fe322d66dce600bec454aa7b8af972b453cea8dda0f1c640ca686be207db72a8bd49d9461d30e2e226ea09bfa55c941e8c800e8c6abc87f5caa1273d61c1dd7f67327f9800fa3181b67327543604711968e4874d6a7980e97c3fd6ba6afad9ebc1a399eb5e25c9265d71f34022a4f647651e3d1081fada8d2c5798d4c29eb3cda7525d223b19d405d8fb1fba22bef8f2ffaa32d104a796404c8d03b4c8079e9d4c832e3310a83a1d68369169c468d36666e68e6804b1594718a0c741765b73a106bf37f810702ab6ddd69bec9be2a0fa13d31afb52a6143230d22eb92e8bbcc704c5b7648192c711e0fa9c26c4ed1676f0a6db3f0b2fe67769b27b3143d2de4412324eaaeaf5715258e83b0e7efe841947dab58b7ad388820d41d424d7e20bf56de23e3f4fd1a385d879e4a44424dd0e5ae58849a6da0da3f4e34880e3d248147c6d0e71258265cfb7c76f0b6a3bf44e578cde4dde223f902aaa43c5d170459a7ee8be400f3e2b53a2cf124ac337f8a37dcd9191ef91c56bdbddb172b4ae14af5549fe410541be8f9cfb6e1f2aec4c70775e65c46f670adbf15956771e40243b3e07cf8ce6e4817534ab148d0094973ffb43474b41d472522a5c0f81c7c50b0dd2fa2d24543c1ab750bec4353da50c2836223382fce9c2ee439c1d723265d8f4042b86fe454bb2393479e04bcc3abd9c463a0fa8cb9fbe2aa0c3cf6468f52320759e8e8ad91d4f91f21883d39f42a4cb2f8138d448af460ecf387614c6d7c92898d276a10e7900035b760432ba27f402c63e740df99709271a40033e6e455485e793e4e086e650f8318b06fecd169ab6f2b17cc613355c519990bf24a3f115a394f2ad4b73da4bd1066fe1e5935e3410c3610ca1bc888ad5114dfd13c94c71c135820f4812eba7906dbe83053cce60c3d88fa161e84a14fdf1ea28b7d753618b6e2588792ea9167d78038fc3f1593e06b32da71363fb3f7004fda0269abccbede9882e98bf5179e36790aabc164e8c4eb60286260186c33bc449d3a5e6d0f02e0306b6de6ae60b2881a32f8d60e18f0cb1de1569c16321c62e461de2cdb9348f27ede112ca8838250c497f2f4f768c1f553ec4114d7bc58639bf204c018dc805f4135896faaf49095f03d0fc4e01ccbe831a453b010bf60f6c29a5919059e3814adc78328640be0d96306f027b7999a126fd0111d1459dd2fb0c360cbd0e43a95d26c68cbb81b1a70983a470398a8e4de773dee304f3979b3cb95e184b1e82518d5b5990dc5b512f57f482ca87ca11e66f1ad3b4d28c8243181bf551e8ce1c4c96762b6b929faada6e51cab9afe3b7e635c1d8e11ad28cbc1a4249aee1d024171b71e6a91c59f8e0e3953f6943e576d2eebc885d0c6731e9864789a33a1e47b0ef4126c0ae6bdf7c8db31acfc7f5e6a38ca5790bc1ce239552d36a4f50fd80c3f99d2036782a9b2c3916ab032e28fad4a7aea03c5f9425ba9306269ee490984fa728ba27446def8cfbf1588ebeecb6b3235fa5cf9cf75388876f8954c205ebda7c5b5c052f0504eb4730b6f14d6e70bf49ccc086dbb2e8211c31f82940f3573a017f9a2c081e42120a9fe5e1fef75abf5a81f53ad2da675117e9ff32205d13e2fa70fc1837f252dc4757fcf67366f51e56b08ec80a8c5f6a64fc4d0266f8960d969e898bbcffc25842439b1cfa1636c89d65a6d047df2d9aec4b953ba956ffe4cf9cf7d961e93283b2bcbc726d471d80be8e1ebaedd6727ac6005ac7e81a74327e66e83363d29ef9f8b5c958107a36cd4c9a072745374300eddd48d2f468277e3893bd280f612ee14506157a5a8d78bbd8c2f748506e1e83901ade062ad1e574b92ea4c7d5063509868bf5b0da8cd4297ec04c70832052e611caaa9b8a8711df56c5d157696bddc3999c5dd686c6df447a3f076cd47f756af33f3a18fa90067b5f07f0f19b40623788c216fee9cfea9b869c0bda7aa0332872f157245de9319d4ef89702875e75eac13b9afb348db6276f8a10166f5340cf957d335c089793cf8107903e1025ccaf1920a31f9d797c3e2c92ec28646bbeb052127f38187cc580033b41b48d079ae2d82708a904f90a44874f6f8d1fba39045e1f0e2c62441f9d0a950608e25f8d2d3bb6127c7a820691a11c74bc8c2ae315ca44de293b515d473ab06144c5874c13c430205f501536762170636410053619148710f9d368250ec107bed3204878723c51674a17c2460d0388a46199a0e1d222472f48c3065a82101c9d720c98d1498bdc0940877c5e2e5965caa420d2328805d984cb86493a54e828b2a549580995811f6d168870c2b5183df4280a10a2317916088638537442478090332a68e7091ebe390f8ec2535781a5e191113fd8390f600346ec4042b402f5a0119ab9eb43110f3e280cd2f972058a91811b2b127d4992c41031070fac214c13024e1e4c8afb4a380aace4e242a5c0163f80507020e140826492352489a6c86e880a24297502e4661f5e298380264b1326823d000ee5c6ceea1a68880b6441f561880a2a7b21aaca9c117b66fc9736254d7658460e12158b4b309d00948426dd5b30970021182a45833f0813b4c2fecc38318404ca6f5026342836020b07e0e032c6479e0e4193ac08b05cb63c411924f188e4510089aa32037e8299053d74f87190c1015009bc3f424a1b8c121da619b121e78a041b81964ea25d34cd3984c387271d0250419ac10894c972a00aeec1cc24c4eb14343820cdedab0702ad06666c06ec01f20ae1e2cdd9e3216205c63d803e520e3c7098bc59f606a1393558e1d3c9cb912862369865b535da530a4d089a0a615db6f34f1a01d8ef860538582099bd80a3f44346043371d21c12d1803236532aa85c458da8705277c7065b0e397204d60815c01c4e0a78baa354240f9a252d8ab4e1a323106f709af8d7a1f0b08a0b8fa348121c556131c7e96f47a9917e68f93485f850d1685194d628d29d0d531e55502b789a5a701305e5c1162d4677a23c09c1a0b9a511c29643114dca0cad1c896a105bb20981da13504b58bab5442a24b1a00b63000a9b3f8c5346f216ae1a649bc4845d98a3a34d6c48ba8941d3ab45dd224872e23439b2f685ae4db300e48ad4171b531d31fcf844e85b53e9c3609f2043162e0ea90b9a1aac645419a15c9a8e62e4c68da00fe58bb989c0118b136b36d438f0480e0c8c247a7d285075e19375aa8c34d92775dc03552128290901a092ad8f81b9d12050724c310eb87b6b54f561ace41568852606e08c986c2fce8084518522922e469cb092e4f6b48083d099e8c0aad2154f849e245e0031818b9a28703af038a40453983e404de7006e7148330056959a9c80c22b118f4ab049da12186a71be513766c8c3d42180416b2c1cb6a43b8892992aa0f4aca65a24c17181a3a4890d33c9839ba36c8d8c2763767c9a6128c69a8049ee33b5e4f44b9b8467819367bc88ea04022186e6ea1840c4c8ce92164158f26031c474c74f12085d2a909972f2ba90439b8520b1e4395817816b537283960b4242438d141e6e646c32839287c31125290abf44b8241f309c782097d7d51647adc995990c1635431d5b030b6b56cec418b88354e28cc85a6532c78513d304c1118e3e0d405c80486392468007248a3019c291e3e7881f3b2326ace25c95d243a4d1923f31c3b1193d0575c110d3a1d1e193f3cd0007532347ee9072d3047977a794a80b83b85499739cd364d31b2ac0116ffc6021149448ce445a3cb0420899178670dc70a22343888b2d01a1acbef0b1d2e7542135017f55084f16d9acfc1920d4028e0109ca812e1c0362dc1194b365518c574b16b237b0ae4943e09ce982818b151947402cd610c9788249c2e62e000711787ba2f864ad9053e7c6469ea311a968344a483529521d125b4ef4cc96b87802060d4e9b01502332102c28b9788a24fa08524de4004a511d0f25374c7d29c022edd3963f4b12ad9963186aca091b81a74ef199a9c38499a08806ace7fa546c73ccb25c31d1cb46a2e6b30d20b81eec19f3d620a22849866259dc9a32860a87485881b675556182537a4a4a49c98d13ee27c54c451e810d7273a781829f4eeb23410e7c004056e15a2334fd7003c6a39a511cf6d8289923c8e209b689eb04a61d44242fc2eac5a69d58f0c7b7298b26003e703c20f507c78a129ee6147271e120a61d88dbf31247f16a4af51284c18c2c1d0c477550e3276fc6beca5285a8b18fc68732bde78c4b830120fc820036d11107f1ab2b159d17155694206576001429312bbe4494005446e90009108751a32ebf844593232838285a73672ccfa11aeb823aa0ce09020b63be607adc4300e95b9af274644a44336b60a3b4b46bb30276d425698b03bd32f1123cae344cea74b9f18709d9290b8c51c004f23356a541c9d23a7646ee049935809348cc1903156342068c25589ec829b2d7a18728296d14c1719a68e10232931cc4605017018139e0b22c086184534251ce11420d2a645c0c323d2aa0549464a152a62bbb6898b608483789cef8f1b0d7a34e9e20af06080a318764141cf088a061831828a81cb35269a949d8e4068b541a042b3565ca28e0a02160ae858fb9d220750a1516d44bf1201d822404fa127587c4ca204570e093489c25738424aab4e7cc2227edcda32b30236ba628fca4d41b058a2470124e5a7c4d7880d28656a14080c32d3760c0698bc1e5ce8d36447af00462e858880b09b9cf48aeda931e2b08343e29c117c2d1bb868da8b5520a804811bca822d2e00d1001919c07af326e7a47e2c31409ec10081565012207157d31aa06d01987667dc64c286ef06120459cbf2ea7ab04ee90431d055100ad3164f1c805b603cf1050a51c3b1449f804e643446224350c22a550e469ce049f232506700dc91bb4f2d629169aa26a68e104c3125473e4545ea6335f486915c4a5658bccb1e62c8e05ddd23e2a1170405d1a99b97b15264bee7ee487fb54b01a5367bfb89af71753a762d44b9cdcab2c1df12946a45e49c0a43c4a93dc0fd30e2f806d114912a42d081790143ff4506528dcdf1f55666b5d78c93aa57f676dd52347ee558d182d72af5294c8bdaa0c221e5e8551b3766e3a5305019f766a6f6642f25f9f893f4679eee25e35888f3cc3fdfd09aa0a141584ba570192fbeeccd579fa8c4ad1aa4fb3aa3f32192fae1f87fb2cd9c278ca858fb7c9c2783a35c3c1e78e63843b95ea5ea5274f1ef7b9eeef4f504d4af7e78ca56ff29f746d76d13797e65e85c7ab5369a0741aad03ec8bffd0a474b39af42d309ed2cd80f1db5f3ca749dffc9ed2b6588ee3f78cab73542d77ec80e1a2e85ed5a50e35d48c37de96007c12ca6cc912ee5574dcbd6a4e151a390ef8b48bd6c9b37b559c2a38680454a19142a36c7f7f826fee556fa8556eaadab0a95a43278576a95293c6bd2a114d61551a438ea1b7150385fb0c1847dde599c2d9cfbdcaccbab685f388aa325564e26a2cbb578df1c279c45f5c25a63a8d03ddabc2b87b95dc3715fc8b73af02f3173ba57f21e6679fb4443cb755cddcabbe004aa3d1988f6e9e4add64b63e2f7e7d51a99b7b5597e660dfdedcf617f73dfb64411173da8a108567ee555caab6546571bfa954adf04c8ece55ea6c424efa943ea526dcabb46499a7523e6a8c16ed933eab7d67c9c1b1b12a2c56dcabae58d92aac5465658e8e3a4fe735d6aeb54bdab9764abba5dd6b07db8d8db583546adb2607c7c6dae4e0d8583b981c2c9342a36cd4b2e8dc4618bb4da5d13ec0b2b81acb6532dbd8d8740e14a4024aa3b30c288d52a954f9afcf4419d9732e210895f1e0ea348e2b94a567d5691c28b315cad2b3b2b23649d626ebaad4a814cd6b032c4806b7ac202c5838fbb94b41b9af4d0a960d1ad812bce35854fc0882116ce25e85634b908d7bd50d1beeae77159d98a097cbd1f92f7e5efd0e6065f50034886d5509bfad0a9051a38ac68cff4085f3576c0d13d44a9ec507be743ae75e2523461c6ddf9795db9683ecb6b3122a27671976c64a770e93ab546934accf2fd039b7a80a0646d9a07072b4598df609024e67ba3a1656d8b36a9f733d73f733193b660456caa4e9d0b369b3159866f5b725ade4ee01607cd0215dddaf04a46d45cba48aa601a680f302ca93263a0c0da765d53e3f9bced1689f5787d60bcc2b3affad5e1e24784567a5df10ea54ec390f82f2200a8c97b30beeae6488900f5b2bfe567c5fcc8716858b2b7c96399f74ce67350e2d3a48150c7c96399f743309b2e75cc20d6fc902e6e1b9ee527016a452fb96ace05e1f356fae4a0559f031d8da7351c8b7f60ef9d65e2277e72a5d09577757ab9a56b1d01056032abb85f0008de3d4a9d81654ceccbd2ad0bd4a1a75399ea7ee527086f376302f2f902a9b9233c98930788d6df0ca83ced30b742da45458f03997a0f6059a51fb02e3bd682060df73115d88b2ade707afce73b234ea54e3eece0bfe67b6623579d4ad58cb9d060c087a14dc3dc5ec4e9d8a51ab7195ea24c956528dce3e7250e7e9176ea9c822dd49e4c0b93a7bc974d2b79fa5d00d5757575703a2f1596175da27cbb3d9161e963fd94a11e75b28eca66cdd11027b71f7f5a52472e247ded609423b1c554f30bb2ac22d1389ee7d521305b64cf43921752af606b56212239a62c3060f651ebc3010ca7d17aad0b96d6caccab7d7273785ab42e7362f2f2faf688a175e84725f1e00e615000156a5f07820d3e0159dfff29c65565c5b738d3ecbd2e85ce296dc2997f3fb8be7b9791a27fb168934df2201e75b24ea7c8b44ec5b24007d8b44a16f91a851c0a56f735f9afc2728455373afb283ab8ae5a8557aee5ee55e95e7ee55ee552aeea2d2bec2b84dfae65e85e7ee555529aadcab5054ddb97b4c959fcd261a955a0d1414f49aa7f1dc27c77cd4e8ec423af5aa0a3abdbcfee2783ac7d3a652a9542a954ad7755dd7755dd74d2693c96432994c26dbb66ddbb66ddb9665599665599665d7766dd7766dd7766dd7766dd7766d994c2693c96432994c188661188661187edff77ddff77ddfe572b95c2e97cbe552a9542a954aa552a9745dd7755dd775dd6432994c2693c964b26ddbb66ddbb66d599665599665d7755dd7755dd7956532994c2693c9643261188661188661f87ddff77ddff77d97cbe572b95c2e974ba552a9542a954aa5d2755dd7755dd77593c96432994c2693c9b66ddbb66ddbb665599665599665579609bf4ba59b6c59374036f7135b693da63387675f955d9509f7aa12ee5575746969ee5573ee5572ee55247c2bcd018616d2c238f0afcf1947d9fabcf8aad33ea757ec15a58a39e33f2f6a2c2677288c6e7500e45b1d1af956874aee7ea0eacd0daf79c7369797d64c83437b6be6c9c1c9c935f39a6d7569706e7976666e63636eb06019e05c65237bce25a87bd2bf39dab7176da281e8c370e15dca5ff539cdf2c1ce9947a5c681551a3254a371b40fb06ffaa4f6c95e8082d119901ab7f092aeed45fbaa53a9d3cc4b6cf69a963572af4ab0e5ee52b6f6a27df21c576335f72aa496fb549ef15ce5d2f510a64dc654f93953abd23f6a55fa37e72e0523e50506c663634c56a96ca7543208ee3e954ea74d33e974be2b3c8b9a99a5fdfdc12d5b27c0c95813f7b1b1bc406a0700eebec50cd5e7845b368f3a9d03ff66bea4202c08b7ac20d3d0bd8a40d580bfe9b4f994fefd992d95ed9abb149ccece2df8870bba6f4f99b626afba72af125015c0bde2251b8d5f1cf354b0ea84c6f39f57da5bb3496786c6a6c64627d7444b936ba679490b673fafa7f4cf5d16ce2396585bca2c46a85faaaefc3e347fd4cd69b192b4034f97158fde16fd10c86120d28e44624188ec8502ab0b8c52890d9b65d6825412b4260bd6a10d611829a4a25224be03070e1d2d04aafc6c07f82d00c06b0f7cd67264080000b9ced569240033345d404055dd0670b51260e369fc87e61557a3f11c810201aaea3600ad758b9d4a03a752b40166687621e6bcfee66a942dabc0aeaadb00b8d62e56c955c56d42dde01715e47b46fbbc602f69d56b1d0364cb1bb1e6b2090ae8edd8a9122343047ec0742b1962abe5b4d52ebabb5e17f76a99fcd0d3db8bb6d256cfcc0ecb2929e68aad6184331f6ba40a4095957b55957bd5aed2eefbd17da772dfa84eb9fbaa440a8db2fdfd09aafdfd09aad985987bb4a84a942db14661847b374c39752a954609673ec299cf6b2fda179de569bc66626eaec66952bac0ba672015103a0716d2399f55996d7e068a8131fda8b1db93da07e8478ddda67374af8f3a05a456e53e593a0d1ca4f6399f55c2393a57e334b96f2b8c900d13e1cecf40aaf35925dca36e851170776aebc55715542b9c4af7e66afc8c0e52b736f04952d3d80ae5d8f9acced538cdf9ac3a9fd50d39dc9dea4607a983d43ef7ed8869439bbbb3ce27dd2e456a94a850a03e79eac46993a69dc9abe2aa324472f7dd8809cc7399f24c10335451a7625b1812f9df9f60b40fc31b0c62dcbdc6a27d3e1e3144e7bfc2699c37ed033020d5853b5b17fef6a56d69539a84e08081020406ec2b65ba421f3d835e510a9800a74ec5e4789e32c55631362c2aa5aec89be5e90cc97d3f72df8e1acd70b4f8c910d39d2833babc8840d6a59a9c317847521e0a4330cc381b2311b56148cd0c03191a44aa12324406213a480efc4c0fc998b2054e0408148f64002a1ab1c58b6c196f24b21d2ed844c676808aa9645498187c8cdc4981e1475b8c242d008491e061408ce7a11f9c2ec40e3138c475810ee9440a4629032512a9a0ecc0a033808be29c0c308c210278f8f5a173847102cb991a970e5130be0cf93c710120ef05273f02624cc16d2fd4a431baa10188da0bc33d49f3f000e8455c1f4cca3144442fba2635258c3aa1c28524c3aa061b42cc7021e65dc2a0f037bb78e1a3871eec45ea42edaa038d30133f5c64453905edd125b030916b67400b176e85578640e97078c743e1d4401b872c51d842b247c819d5a48dc2000dce1653887a42403094812254ec113a29b283cc96d50ae19e96310c749f1209db4c772460c929d522151b0897d293142dee8c2999d328234c8b22cead266dda0a2df2365d98a91b812d2ef419f4c1a1c9dde027431aa6fc889160f04d1c462b5cc46b0679ec49d274e78dc320dd1c04bb28653f06bdce6478694bc5287869a37ab495a79360993ea9de5a08802348e3ca4ce010704f500415418ce2c010104c00a76806f50d59d5510131ad9cc767954b881a97fe2479521504c4a7aeea82ab6a26c84291a83b5b55029b1c203171250795d10e129742b92aaa94466726d8a9faa1fe2d695335a5ab415d3e5afda018dc43d56f40a830f2084cf7887881c991e532458ce2e16104148fa9f34d002968a2044dc3524d9940e934c762542a390a180049c4624eeb2e724108128b2067d4a02272c28f85dd2107d66cab040b0a59c0e2b8a2acc04d542913d788a5c034182a2498b3852e108717a6b4263bd002e3baeafcc460f6096430870d8d3b296648233541b20750c1a2344c8c268b2f2d0f52174e016600dc30226d3618cbe0d764508af5c888263631364411554988e118338a5a694209d1548d19511622e4189dda5312edb00542326c2a240a408218393de41400fc532688b2039f40c026740ad61c429a0408d5c205cf191c26c05642893126540a7fa8348521a412df3f3bae25108aa514ff0e87a85902dbf9f38757066f4e900236fe2ccc91b124e041e9f749eb51219533f8b959b108d50c005bfc76d4cd605bbf357e732d9280ba5062f58b001627c7154e6d2b2a617ad810c880156436b9616f15012b6648f25a3b0180ba820d0783402efc84acd8eafb7276a74481be46743ef8a2a431f66df1626460cf58a7be2a252e42849c777d20121b75e9f206a66f409c3d175e4488e11bb46768aacda03b3e29119e3853a5adf1f9cd795e546306f89498046cc8638410dfef086442400be4dee201089db07c61da43c262cc5de3031e7b31a82060288810b4b7414d0c49566939a0a24c215e5ae10486ca7132d2b4264c292890158f91f26a08819ad8b178660c9f02d0098118490921e061d614c1cd9d0cc2784e643027604b559a6f84c08105ca4ee3fc5628d01b1d756be6f22258084d089a0ca9cda846cb024b190c921350368d4ac885cd2db9950ca19cf6a2553fc95194b136284761e083f234d4a884dd198854cf2a6408d598ecb5cf402145fcc018c6798ecd906b4a9ad2e769d566792f55b2e2f4136a2e6f93170e279309101e25b920e75c4692121caec5c9e2e170e1c775c4a6ee041a45c62d102b8ea16d10928231267247d9e6430cc238059587981e53a0e20a720481494fcdd1f3a41cc4710251ebb5996062ec4227277a3adeba88e9b020464fce4a091f50e440e93d1864a2a9074da63c4a8a18f95de575ca23b340d2a6313216f264fca0c28587699ab71668a330121b147948204ab138246054305aab5340c9c40c155a006c214ca3b5a8a26a5517a68788510588aab42f37185c545c450104f028d6c21344c3046ca64b102f0ab44b18de173a782b3c345a3b120910cf4de50660519c093cd713086804b530451e465d97b4e450a43832636d8d19221829e236ca113e6c6b0a0d38dc0990b2b8a110b553543ae3e501459c2622e6e84c4244f1230c2d4d87013f5094e8d1c408870e54504c101003cdee948dbbc6434e2b14614feed02468f0c2f8927167a3c55aa9f5417be7e6034b280677e0ee164c69a4b315e5c3894795daf46d61b078e28b1907048342bc9c2074c1ac909a763a8196e212a6b00b714e1c309545b866a6b42b74e790621437831d95408f601a18d6d849d3f481f2b33a76cc58c21057fad955b995a126f13699f0538621941c313f4c2841856093934a8d4cc879a8151664a2616295122a11f9052de1d884cd89c4310f25ea885843281a49558288800a500061e92981028487c51b6f5a25c016537950163a50d729508fac01f9a16e0d252e810821e4a9cb21b729911ed7943a12883e2920a356a9ce4111083d125950e92625e28a960295826e0c9b1730237362d0c538fb3245071431ba107c3ecc30dbc0e8b4d45ce1d9098430a74894a0b31099e71c16527895bca4809ba3862dc864224681b9b22f3fbe788abdcc09f041e5cd85b72fb940532ccd690a3d9083e287e00ac3093b727d13a6806abe30e5dc0641dc8875b223e70a08e8b18bf347020f1b465ce2285d903082c8a0849be10e093d42475973ea00496440f167acea5ec4854ab08454559a330ecea20445964e1ac4f538b5890427537971758860882c70981127a189e691e11b257098a6bcf133e81105dc99068b58b2942880ab0186b1f1c056165c5b9b0f245b1a33700aa84c11146484c8db230735462942c3f2d6c5b1efc7116681b716626a1815d9f1f076e6e30e974850e437035daa6a6349a0b8116a83e1cf1d170cdca6a05a7380692a845bd449079210525337186e5bfa502a94db9ef6e980c644dafa3c10b412d8a4415b92482a1e7c0a93a0ed69295224399fd2da38249212ad4a041c512a865b27277a2646d091cbee22b921470c89631fc579d032e28e81a5150c4e998df872a463c7161e3e223895f9fb53142d13a1464d88ad233156118631a04145a8e08820d1e0401d19f212110d54c81090674c089b24496c142977a86c6270582053264a0a361800ac34d22515d9d4fcd070424bf1842d6b93e2893e31dad6128d6114c575a770ed4a28423a8da2c46b533cc7b78b01a5350f4f0c9de6a4b0b216c0860e504d443c0801884fa00c0aa48809f16441229a89be0621f8b44af529f482142279164fedb8d9504be587c9a54230b6da9d2b9026a8e169a25624d18c19abe802b53c209b760808f3a586a1ec0433ca8f4b4d506c23c749842434e158a0c42182568d260f432c22a5c2a19b745e40a169b7b2e9e523c3860b9a48da2542b442434ecaa49909d3761c8910954683531f006e830ed3467445627cb4292e2d01980784667e8040736417498f880747e3120770efed8ea30dba260c0b457134b339856f231a8e56c02f5405d7fd8c910d1d0843dccfa6ac4d2481703f03ec3232e55d1bace4ee673a4a9ed79e3d3322211c4f2d3f66623868905a316787101027407c7de0b4640561c1386e5060ab8b93bbeb6d75f9d1f3aa46fba45d6fb6bacae8e979b97b00dfeaaae243dc0be3e934deea82e13e0bfef175c5b96f7581989f5b5dae07c6805b5d170005a3f356d7d61683532de6e8168348bec560d1dd9dc2748b81892d2e385c1f9c3a67b8b5a0d0d6022e3fb0b5e0cf3d0ee450584564885aa50aca7901eecd542fbe6a1ce8f5f7673636e6be0f19aa7a255891b1c2a260c79602015b5b6c7c6b4b438e2d248ebc2da49ebb4739b2a555893a15d322f42dad049b106356df1656a52d04a0dc1dce2005087c785e2120a0a0ec253048800f1472f778ea8e68eb00e1542947e329746c6c2f2a67c0548234aed2d75434d3cc5a76ab000ca74ec5a83c70c8357c8b009d3996ca04f4583157ac0d9bffaa4e41eacf1064a7d207fcf3db1af0e393a71a5b035cc90eb2d4a75f600bcbcc97c9a18dd7f5956556969fcbd79de7b0983a0d9356030ba75b5774edf6f23b0bba12342a10001016a82234b07268050ae01289948037889c5205c07277081b9884d87a13aff615b6027062c5d6b0aa543af5b3ba6ab44f706c0c50303a071656ab51b63a41eec9126d09c0a75dde1c58286f016823f6ecdb0b147c3ea56665651ec6c4bca8339fd786685f5c05fcfbe3f3021c9cfba6d5d80b2af592f3129b6e288456a95240af3e366a601f1d144e0a8db251a97f7f31da195b5c8dd3a86953a95ae13c826a36cf6a54ea5f0c859bce71d46a5fb40e0a4785c24da56a542934cae67cd2cd54eabc17c7510be71155c2f98dcd8c3a0785a342e14c40e1a84ec1392a353a0f36a1706e4540c0630c775753dc03fd9be7e46436ea54ec2f10706c2c2a057c12a6e273f75930909b330766357996aacd55698953924ecda70a885165681a5bdfdfdfec84ee3939144eab7d7fd579ae2a7de3a39e55a9d2aaf4add9944ae9e6594d9a36cff3ac46352b8ce3146cb0025bb5beeb979082e233a0746eaf6fc53cf5eab07a95797590a352e89c5648289d028e2ae43e2a898c14c5c95d506e6b42a374d3793abbc88b4aa348444191a2dabcc6bc7a0859820c052754157797202186fb441df2296e0e2cace6513f9da9ced4199ab727cf7f2da451bf67fc170fce090e6139f21005471e1a73e421b82db442df424be25b6886dc7d8147a8b080b965c6c6b7cc02f9d61903df3aa3f3ad33aa6f9d39f1adb339be75f6c8b7ce4af9169a976fa19dd0eadac2c2c24af4c3b1123971ac44651c2b511dc74a44c8b112353a96a22ac752e4c0b114c18ea5a88463290a742c453e1c4b1114c752c4c6b1142d3a9622488ea5e893632d42e0588b226461212800008947c691786f1c89e7c79178891c8957ca912a908e54c1c0912ac20200c06214e4588c901c8b319463319a722c420192605e0d922d02af0509ba68c43992c6ec481a301c49038c2369f871248d508eac81e5c81aab236bd039b286d491359e38b2c61f47d680e4481b091c69a382236d281d69e3cd9136a28eb461e8481b4d1c69038c236d283ad2862247da00e5c81b011c790382236f6870e40da6236f8c70e40d3d47dea03af2c60e47de4074e48d2b8ebc41c69137de38f2862147def894c541cf911cf81cc9c1852339f870f70309b220247a160445cf8260c8b3206cf2ac08559e1501e9591122785684d5b32298795684110bb6f69a6f6d14beb5a7beb50d7d6b2fb93265c0b54c35702d535fee8ec4c2ea93c5b1faac71ac3e7c1cabcf22c7ea33c9b1fa90722c3f568ee5078163f981e0587eb448b9ae458ae95aa446b81629a76b915a11608080ac2c11531c29628c2345c471a4883b8e1441c891222639528426478a7874e4882b478e50e0c811161c390276e48834478e6873e408138e1cc1e7c811828e1c31c391237a38720413478ef8e2c8116f1c39828f2347284262e509e25879a438561e348e95c78e63e509e4ee580d30383a32c300476690e0c80c648ecc40e7c80c808ecc60c391199608c0bab3c3b1ee1871ac3b4e1ceb8e15c7ba33c6b1eeb87177035ac80a551c59418c232bc0716485408eace0c891151a1d590194232b9c72a405048eb4f0e5480b4b475a08e1480b768eb4b0f7008081018d6b8ed5a8c2b11a5f3856e30dc76ae4e1588d888ed598c5b11acd3856a31cc76ae4e3588d8b1cabb1d1b11a4d3956262bc7ca84c0b1323170ac4c121c2b1386ac2b2f198ef4f2e1482f288ef4cae2482f338ef47ae3482f3d8ef432e4482f4b8ef40ae548098fe02ac182449eb50093672d10e5595c063c8b4b8167715df02cae0e9ec515c795604b18476ea9e3c82d8a1cb9659223b79802b0a08198231bbc8e6ce0c1910d4438b2c1d65656842c0ace95c5cb91593438320becc82c6c8ecc82e7c82c828eccc2c391599238328b16476679e3c82c8b1c99059423b55839528b03476af9e0482d271ca98505022472eb40294e8e2c85ca918e041ce908c1918e618e742473a4e308473a3a1de928e848471e8e747ce24847318e7474e348c745473a3672a42328479a22e04853101c694acc91a6c81ca9958585800001015908cc781602389e85408f672120e459081a7916024c9e85e093672130b580c116968216a71c4938c09184121c49a87424a1084712a2702461d591843b1c49d8c491845f1c4918c79184891c49d8e8c8c22b4716467077a41750f0a08096840453363872ca07474eb173e414d991530a1d39458823a72871e494288e9cd2c69153ec38720a23474ec1e4c8298e8ea422c09154ba1c49658323a9b839928a9d23a90022bb3095712d4c705c0b531fd7c2f4c8b5303dba56230377bf42ba01e54837a81cf9068023df2070e41b068e7c03e6ee08b4b01668a539526bce915a2a1ca915e848ad42476af170a4d613476a997177ab01050618b0d272028e3c6b4ff2acedc9b3f629cfaa42e059550d3cab0accb3aa58cfaa3af3acaa38cfaa42e15955b367554d3dab6a86675501f1acaa43cfaadae259556c3cab8a8e6755f5713771d868334aa4ce303a4b1231c2aae09e57a0546ae26a8018d56b80d8d41985ba7066f827f1635b1edc1d53a1412cdc770477c770610b125218c279c9610ce4a3369767af0dd237175ed2bda7a27004c7d32a396a73947484e57a7a5e651a319267b4b1b1b1b13c2d4fa480cbd98848dacce8dee0eed8e4e0d6bcdcdd30b2db478cb8bb9e5bdd5b5e3384d9dc5cd32d2f4dcecc445c5d9e9d999b1e75ce2233ae28d1fd5b456beebe33b1f012a3fe5d5d5d79d580aa2c79b693081946354434a38e4a8df9aa7a7a18cac8a2d2370b80523ab4aa342a7d438c0dd131540586bb1b920dc51952220d79799842918e14b2e2d5994a8dc3009f8448429408312214c7ffa675de84d0eaee48425b8402302e5207bd305c40321a22194d2019d3de9083189d701e1b9ba37d7ccfb96f5a1db4c1bd7611f77d3884c83b410a82119da9d439af0f52d0963bf52fee2b232b74ee0994657564a0c01581c800818aeeed400262e3fe5980e2a67f22fd29e37f92fca9bad7d806af316add87fd11d0b858298923fd10bad7989797971f10ee1ea38f20377df81ecca6e035364647750a5277813d4d9c149fd521eda1a3e3b7f6acb92772d7161659388dc57731831e52ee5e483d651cc305a49e2a860b7a5c77c770212f0f290c17f2349140ed1b9c652fb1b8cf6fae8a220fa1fb0522f39039860bd26ab4cffd7380921f069e2678c4fcfdc54d3c34dcbdcd0de1e172e49d1d82419076e0ec2176e8ecac0d1932048d73c1a008e7c30ff7177bd1be3c8a1bdc8dd4391318cf7c638e74a0b87b8d55e539b08faec5fc3607d59c18732aa0b189cd6ad4c03e3ab1c0beea2cdd0becabf6d1890dce7d7ccf981a9d03a5d30d5e627c16aa8082d139daa78741cad7c26b43544a8de7e9053d393076a306f6d17905f6d1a955a56f7f7dd5393d2fc0cd9fb701e5ee7c6d0cb8a0b3cd58d80b3559dcfd81ac69746f24c4451fb5b966cba9817d550e817dd5bc1673342fb0af1ad857cdfbaab55105330e7a703deff2bcfa9c50646056860b892054dcddb1e8b4e9e0efff32eabc110128837074223c70981f2aac3903ddddeff628589169f4dca5d0beb434177c9ea179d26a5e9a02ee4ecd9b676822a946d1c4454f2ce3ee7955e91f35d1883ba1530b67bf59cfa15069625ae206a7266239150d2977348d1c8d1f348968a2b87b150b3434dcc921120d9d570503a37d7ad15535867acd51e9d6823cd3b80d370e1b22dcd9ce8c98439ab11308f8248c01a3d4b84d06dbd8585e3496b350c6712a862ca33de6236c56f9f6fa4acc7bee9314bdad8d1a63e7f333da3737cb52b4ea3c27f7a5cd6dd4bf3fb32a356ea352e3366ae13cc24420e0748d4dfa37c7478dd70201a76bd4421269546a9bac332b8cffa885f30899fa479d65295a0be9db741eac52e3b6191acf5509e7b7ba5996baed093ae734bb107371354e9bce84b11b753a5709e737eafc17d837a593d2c95268946d2a559b06023eeda45d14b89788f6b989e9e979fdfd2111f36a31bf7989ed95a84adfaa662da46dd44218d428565a04f251e3b538bab5ea3c1717ad6b769856a56fd43f38a994ce8d706f4e9eabd2372a9bf44c9ec67c7353a99adbdfb42e966b4ee7b81aa7616974af31af3a2c77bcc6bcbcbcaad2a86f03db6bdfb041f5f24293a37585d3aad7181e8a2b46bc4ec7990d41c5182ba8532b6055ac20abac5551e0627a18f6a880152e8c8d4de7b1313a2f0d4f3bba36e8df3c2706fd9be7a46fd4782ded19ed934ed1aad237c0a75de1b44a9d9fd1795693a36653a9da336d9ed5a4d3f94e3a9defaad1796e2fda379f45a56f84b33ccf6a7dd01452e0c8526248c9930241ca82bc0748289f063d717c82840ed2c90668b42f8c0a8df641e973a7ce83f24c15ccab0a4ae3d9599d0ab2c8f31a239c67a8d704335d8b062e31401ab07b40e6091457aa2b616824c944aa8b871d0103f2fa2e84c09a2081cd9220411b80f0c04183ae2713ac2d68a16f57a97bfa1757a3718b79b06f8f05759ea1d5d9efd957a56a54ee1a8dfb0e9446dddf71068ca3ee3acd06fcd35e804f42f7edb7574cddbf9057779ffbdb79437cee7bcf7d43f7fc010c5148dcf767f6ec0b943efb026759ea469d67b5ea3c3757a56f7e4f695b752a0d246c52a7f3605fb46e1acf7f68d5673c153c4b6ad2cbdbb2fb7eba6fa7fb8eddb79efbce73df2adc379efb4ee1be51b8ef3bf77dc27ddbb96f13eebb84fbae73df74ee7bce7dcbb96f12ee3bce7dc3b9ef37f7ede6bedbdcf788cde6bed7dc7708f7ade6be9bee3bcd7da3b9ef33f76de6be4160b9ef0f5364912a2e261721743e6a9ce79e027948c7a57c70af52fbdae6e6b9ea3cd79ce7aaf3dc3ca726079ddb4644a57d53a95c1f61732a8d4afba0735bb58fb029fb3de3b36a7436314fa56ef22c4715d1627ef3abce4da55169dfec579d8bf6b9e9e945a56751bd0d182e38a7731c61ec568df6b91506bb09f37d151e8eec035d2ef8a3423f8e470b1f1fc3736477c002f338ba60e8047e401e90529696b006f74d209070265dccbecfa7ea8f6001c7c90ea17d15a6207e890c9433d182c7cb2076f9394190fe139244fd0586c3e310e8a04b2438f2932c3df8286ea5dc0feeca267242d1659888dd39c981274121c69d1090be070e4c4f3489cdf7109bf4555040784dc6d3f381f11e54232e1a91abc8affe68aff2c8e86a8258dfa388fd2b066f34e1190d6f94a5e5a30cc1782a778d368b54465ed111b798bc239fc1ae8e4f283bf08a2fc2e8b33168cee389270db7e2ea6ffc18e0a9d292786315bffd6618f1182c91e9980791bf838464571150f2ee9443b732e7e2cb008a6d2b5cfa7c6013cdb73133c8977d70da549cc1ffc0c6b9f51048e5cc491a3e0fc7d3b160fdd97b0cbdf93b191a6da7a308479e98dbed83c447fa18b3e167a84f9340822ee406e87f3a04ba9f287ebe06aae1a0b0f8b804011d7ed02946ee60caa19d2a4ae451fc1f5d768accd750a047ef351bb8559e385ff4b7c54785216a2bebfa27da70ed1e09da3703e6dc8424b5f93093c2b89b0e460e048a1e3f9340d087b090f10f1a71f91e04eb165440c7abe0516e0a4c347d1a0c1bba8b02d86fa93bdb895f777e644ee3c3b040c4e73462d110b8ec3c600af619bfea7c10260cafc840ff813e6d8e294ce14ba9b01f7888c5315c60a2c524d870409e16f998aa161e9d53b8094594f987b63bef9469cf6b9065d108c24cdfcd9947ff0a52f7f264625701b3c42f41c7b41fa586872286d2333a3ddd4c2c94ae0182ede19e6bbb0d0fccffe9141acd13bcef528acbcf7152391b37835fc2d19a6379d57422013d1c1004a55b5d92e403f83cd0cf00ef6a2428f8b317703f00cf89cd37a888c709d0b5c3bc017aaaac469bf932c415b87276942ea67e931a353bcd21430fea05a1ad084e93cfc007c363c9e97946217aec317564df318a933b5a32f861d0244fb646f9675c167772ce8f9b6541f072ee90382146739e76657b9b8da79dd0a0f11ca074d810b0b06f635b6d1e889c3896b9a8e71950e001953776916a1327d41adc7baa59fe1083553b205be37f1cabf69923207e869f7857b0d0c04169f0f3723dba7c520603ba0f2fd5578a22b6e3ec45fe088c800db7e67d23b1a47f9852c58d78e2a3057c59f22e073b9e8406322f0104fa982a283a8e4402769e46041e8f07235fd4a8cb63a069d22194765e841c88ffd0e4f533300e3b6f0f96f3bd685f472b922b7884a3db1e59ce62c5d35720defe46198f8f64aacf53f12ab427b548f4066830e85260969c07db49174a19e44ec422ded749911b26d9e42d8c4e3ea25485fdb7e8ed1780c8a31f5331f8b3be00de9e887b1579c2e820a94afd09ea9eed69ed84c791f2b7edf875792c3d56ec078192b862bb7ce6d89247e082eccf2c7dbc12290d7f54557408a0acf814eff47a16079da966c19944e49fc06094cf7bf8f81429557e42011fda530c2e4e2188dfb62106d4474855603bb56278254b5b2e6a6cf5346e607c1d4e7d9e12228c0f95cce2424c1c70185c725f8a8676357796788b3a221e020c959b5944e20fe2c347d30189df1144cac1d000f22f361878b7468834234759cfa451fb63cf170e07d2076fd00eba993467362738005ef681c76ebc10c2114920d1641ca5f9b3511276a22028beca11c957cd12b9a4b979bf00837c6722a01760c7e4cb0e21be0f3f341c4ecb802e6c62f7238a1c78421abcde1d24e1708ea878451d1c6948723a7e89a005ee044fe32fe93ba29b813c7cacc99a2f10c4c3ebe461d1625509bf0247154ffa40e069550c5e8e073bef43a1495fd110c52bb408f341585479a426af9de21225df44c6e773281e782f848c3ed40fb93741e6623b07c5b913530c1d6805cd73f17d780b871ebc906dc8a3d042e52c1d2c1a8006b8b75168937e13d7c325bdd1fb3ec449aea64b8adfb10a72ed49d1bd4a08710e2178b8933f886e274a2657f04080c363830ef815c8fd16cd7d96bfe4b78d513e11a893ff8288d32fdef9f1b81a513622ba48bf24c5d5863ed8f4163aa8704f889b3ccf004d3e0ad9e5fba4a2b393f0c9bf07cb127dc836e6ef2737da4ad2cbe302133f4285093ee90c875ef454c0bb242ebf8b908d23c16365b7a930e3c982b8e75933f839a250fa0866065d4c240b1b0ac3cb491878bd4e9e4c7f25c753cb9df8f8a33820f6a7429cfe0688f5031bd1b89954736e09cdf0b3d85a6c2f0326bc8aa34dfee7cdcc573a50c203959a5f529e44ce7cc1e702cad4d07a7c6dcff6624fd39154cacd9070d17185161d8d4cc463450d8e818f9ae75468eae3e0b9fb01382c7c032da79ead06886fe08175ea214eceac84f82c40e87851746cec3e875cbcc932d32f3135e82662119a8bcbc5ebf8c446cf42ea7c7bd6e6ed7402f0335e1cf84e63089dbdcba23738387436891bf698295f2fe5ca95e329c53cc099baa75313f267aeac1b48ddd4535589f2633e24fd8e1cbf4985faf18d2d006d2a0f049ec30f090f92f4e0463881d25604d4700a46f65c86060f4f46c6db1370e4c7c718aab3e1440d700e8b9a5bfaa7c787b2bb734670293ed018a01d23abfa6a3e85d806c4ecf01d7406f42e24c7c970f8f01b0edd27b4ffb6e08552be666ef9254e58f92b3962dc431f9d3f61e2e54b2405721b5c45b40bb6336e038dc7e6c232f075ce9e4b5160c4eba679fc0f104f8ea786090f3566f0094b58e9052c56f77029a96dbc307b4d04fe68326cb8b89d1c4e5f53b8c6070432a1bf3080f80d3448e81dd14b0f77644fcf95a2f3abe76921cf0c0ee744e44790f21fc0a3e3f17c29f304d8aed78a73e0af9430768d055cde8615f96f3c71f20f0e3f6c417c2278f5508a2ef49af91319670fc070e0efeaa6ce270115e7228867670150c7bf10a27a6512931fc852e7033892e497841479397730bd14b814cea5e5cbafc972e0842638dd45b4be930e63fe4e141b0d2302f1477e2c7227813e3d1e76c5be23d1c9179170a347e401f8071614ec2947a23c821d71db541c450f47d28f9fc4327e8e45129bcccedf8b1a93df4c0925af23d4fb6a8834791e9a8b6302337d371b7cf608463f5ea314eb5d746c7cb6c4f73aca72b88f13875e0a441aff429aedc41685af879185535824f820b4f4fe86630647f16cfcb9b6103ec8c981eff983e4192b65da888e74dc4dcdd16181617e0d2a1c7a0ecfd5df80cf5c0b5827af1335fbc02946dc87dd845fe2b4fb2182c8e82f8078b8de1e487bc6d60a6701e5f52409071c49c1856ffa43e68e1fa26c3f2d27af100a94a7faf2e8cd2aa069b3028dae0086d4750802782a7082f893bc3b1f6491896dc74beb5ca804d937fe7c7c965202b4a7b01cef08c3822e9cc3fe128b3d17a3b3c821e4a07bb94647fecf1d07bda046173f15494f23aac1f81cffe57b5c90712e4a555a51982abf488f906d15c8d22b4d3d7226ce078e22c9072d63859b731a42e9f3ca243c5817469bd11a316f8bd96c084c0c9e66c1025da6880f7f1a22d24c28a8fe6e02de46b2e2920f00c4676fb191f0115740788e48a85c87963e3f5525cfd7515834fbc6c3610c21d3606c05277129cf7653a5ee9b5984ef966890ab48d2d42a342ddfaf82bfd5263e4fa607d19eb6489e47218caf8b43e6d18459a0dd2e505e0f001897a275e1132f9d6953400af8272c0e5e452fcaaf61327047e9e33ba1b29fba20f14fe830b299114afd9c4d6bda4f9194072cd4a52915aa793274ab0ff253c22104a6f036383a388bbde2339101f02c2d54fb1382458ee146a407125b71b02b230ead64c08d6881a12b4128f49a18b83bc155d2af14b8f89953259fc2057631274e3ecb1a423fc585a307d363e095c8eafecf12f911308c1c858e0a5e057ce2a3d2405d8c27f73f7192eee4821d2f81006fdf88a3e739c591db031ce03d89479bb41e3803df45a7f0ed145af076e6333fd332c23d829d23b61973343faaecaf0663de0ec2aad1b450f003eea98de2c827e74a7ae9133780fc20d3f979b0f407aa32e6b96050bf854a999f93e1e6fb10a0e42d63a83794d4c51171fae45c3a7c788c64d523c0e1e5583ea9f1b646183687ede01ddd6dd27cc2c09e4d1c984f90d4e581a0126c413f9a7c8f27ae47e48b6e0312122fa5e3c05fc589e3c9c02e3762098ceba122c21d095af71940adcf73e6509b8123f076ae86b88cc61f5ee9c8a48dc090196fe047c0fbc139e181826c68e525f907e36cd98709327d84386ff405065d9e1082419f0b7cf4549438b800455a1e41058b5dc90d1797700650739d79fd2c916cdf998ab9221bf0d6fb73e5efee70da81d6310f81d1e3832902e13d4eecf1c53c344e00429dd73385e68599d6b82047541f49539c0d0671ceb7a47f088d40781669dfaf9864c945ec68d445fcf0f89a942b8fe64b1e8f334863231a25e95754d5b81cea98f74121a309cdfd6f5aa4751d1059fc44d7854ff405e8d72880ed1a02e07c469a53eea900b88e0043f4273d46dc4f5adc5fd2b2f4d694065fe4a91c1590eace3068e1f510c9f3eb2705ff4605a65734898c1b1ab1fc40692a9772e1c62e1386f79ab0ec71b91c3b9e84f24b1f9ecae1160655712b1ca87e6cc1182f93a3fb1aa9d6f80d008e6f2495a2c6c222f1283f43ffdd00f1364c19713a672abc243592f4f2ac912f151c3a1c1c77ce05559b23fae5f0196c8aec2ccec963e7f2f1e6f0f0050bae7635c99aff3a2cdf27848a37b1673c273c24bfa10313975047882761c4c4dbc8a21ec70b0f1b8ba02c1f65aac6a612f2b9221a22ffa14093370ca1df11d9f5792c217136879678231628fa0fbae9396919a40f3d85fa0378497c12df25dfea80e12b9088f47622d479398d5c9c0562a857f4e4f4421a5a6d3780fde809475d6aacce23986bf25a7b3e7c1447f32ab868f151c9453ec7708e6fe549db68d42471afaf099a8b431fa771a17043b211eaf71e723f2892e0790090b30fb0597b0f189cec0e04b23c98ac335da5d03fde8b2aba4a94377f894f89972e69732c094a7e9631f8d6991c9f18a7e511d320d90a5e34fa0196d1e65b5be0ad132c9a1089e12b651a1a4bd290679049c42ee160ea078c0cead500287f11a8f14f8400e0802e326946b6d20ba013f40c36c27ece1b313b838a427fa177410f3a6b3a04e9a50fa3e3d06d0c76d289558d1eae0c54c71183f33a3a553d154c39bcc5e127cff049f129a801dc398a44712b8386bb8c1ffa1f81568da7668f670951e119781ae4589cdc5e53043afe43868a672365e22bc630f8272f04df6489fd438078dccb2049df6805c9b55e588ff8a2e8851ca1afc8d08ba7024194969a9d7009f1aa8b58cdffe724ed05f1fab8973495fe4218169f17228be3b0e1e239ec68e1a502135f48b383233a43f43a5098bbc233954f92127fa340c04f54dc9ee44c881fa335e9d7d421f23000475c716fc0938113630f00f2e2d78049e238f688b70147753618e27e514801d7310fafa348c7334a88f44966006e402f9438a945403e2dedc61379c6700816c69ed12939e1a5062ec76d9487b102e6ef5e98d12dc45c701f8cd0ecc4c053dfa8d542e701b0c1536109f4568c5a9fe280a13f91a5c3bfb00ae2c925989c0bf4cc7f8ae3c3e51c3fdeef4b1bf73efe1a8ee17b6134e457f492fc150101bf74c3f3a368345f140a497e4a2ccc261208e9344c54b5a84d6c9ea2052afdc304de17421a744d18127dd598496ea094923f6b83491712e1e85814da575332f7775678d9a020f9f9a7091fb48e54c319932cbc4515267f2182a14baa74c44bfd88a295b4357e5c54d3ded085cda9202173008b5d5e91238b6d688e7f8590336fe88e82a7f8e3e90fd500f41ec844dfaf4725c75ba1e60a8894bd9933886b487ef268aa3cfe9664d46b397462d7ad611e4d140aef42cc1d5f1209f0bfa47df20c6e001d4d89771c9360cfa340e933ba38fa3c8a77dee6ac71f729b0f59e50f5e90e0aec7c08b7b9c71055fc1b964c6924a808bf921d994331533ea05bda7a3055f21a6c80b81b370e7c8906aec7d261c0c769d3da42c25cb9dc14b6dfb0a691dff9f3b8ff68f9e07ed6423f6ba13c193060de8d9086c72325a75d37951c314a533ba832f58b98003d6bc08a7759d468678979f83466a4ad8455f93f62c4e83f85be9e91a5485e4ad28da7ca51b793c8c173f21261abd940f57ee0390e8469c3c12e91da3feed8f9466f143c5f0633ae440dcfcbde347a2ec308fa4598326730a8cb37a824e1ebca4c9dcd40c85f2d28741c24f9fdd470e80d844c79951f5c1a0605217fa729d07661a2c543911be373d02cf9e067d03f2c51f93d070eedb03e0c9e054cd4b348e19e82920cfbf91a5f8bcc7b6b56c7cf1cfd5cebca9b571b42a6b53ca9fc952a60bbc15f78b94454b404c1097f4aaf8c47613cfab9b70e3bb0cfa9bfa2c4d4b62b60e0199b297ec31892aff0278716f5c28d0b22647b3625927c9c181eb6184913de43dd89dd272286cb5e4b362b8ac7379788f8437db6781b20a1361b84a80b804b7c03a72e79271a129e908ff90e191ce92ed2109ec0108866e2b6c79b36d61c4982352750c182fbf013f46100cd6d4d1cdc7e94992dbf02f0ca430d4ed99dce34ff4010e4e36984e2cf5838b439ec10e513da1ab810a5da2790e4e6d24a679e0a94c38793618113d892db63a6b47052cb2d6e044dcd1b50f2d97a77a478db1350eea0c2de87a509d2655c4f6f48059f7e6223ccb522479d47b8c507492f69168a14fca41d7a1b9e7178517ad434de9226cf2549ba137931e48a2b2cbd0d2519170268d16e31a1f99da1762bcabaf17e5e8ec682c6e892d8047d7446900fe439748446088fd7b7a7093548f421ba6c6cf6cfc45bc891479b49ff389616a24701a3e64955a5e80764419c4d5e151791a98797e8fae34fba1ebc159bbb91a879f84e2538bc03a9fc071c357c0d668c1f034888beb384f045252a7302636ccf0254831da12ac4f76df9db0358b71f4268e72d5dadf81475b8b8f28cd967e214e9abe025be975cd7b7e96bfa9a8585b33974e1d32cd136611d2c7e879475143302df9c90f68449145d05a83bdeb6457abd36595a30ec8acba0b4c897a9e02f03e3e0b1ea483d872cf16f3294e4c33a65d2d1082e3cc501ac0f0206fa987054da841cc95c8bc1ec730a2df1221558fd66dae7138c8570b9267a7e038902dbe9a7d0e344c1e10a5290f0256a36682d6c2efc064d530714b7713031c0761b85389e2691a10f11c0d145316afd23d5c55f5903b02f3d62f42dacd079870960fcc709acfdba579ec0400e8fa2c05dc473ebc72c29f2470ffef40ea5ec6a6880f132336d4e23714ee395aae1671c987a4a60007f0e13808793ca8b0fe280f178d5510f66e39176676c7212eb02b78408ca09b56578266e277cfdb26033f3e4714e55e07c8727749ed25edec6fbe95780f0f5b19ff983029b34199922ff221185dda1c08ebf54a8b80981647abf4f31361c3f26be8d04a0d7c21ae06b5c7c365b883e9fc74908dd2602a9bfc2428f236bd0b91b2c8eafc5503ddc9a307b531d17fe864f0b6db896e78c3434f938305478853959360530442ee6ccf0783ae8df13e99486d649e1132c59d166d841ff0240a0afdd20f56109a84f618d1b6fa1c5b959c337ce78294ccbc193e59f2f62ad45d8e8659b12f5802a991c4d1e2bbf2368fa5f0483ec1204a28e182b7eab4484fbedf8a4d3e8f571355d531c920ee97f1da2ade52f899bb5643bcc7bc991e8e8b1f1dc69e30d0ca59a2e4ea20f3046f8558e22b9e00a476d27eaf5647a14691705003c9a41773ed0db8aff537db3b5c0e9e3640866ec1c768b9c5586d10c14117dbca3d00ec4b2e0624a42380a0d21de499724ee034898cb78558772038737e3b0deb044ec9678383e10b9549f566776468098bc000e7c7461190a0f64cee0462334f6191a41d14d6615b74bc2c9f1e010731573d0f69c6011374360c70bb824c81b61d0f493e0d8f8932fa73cc221d6cfd2f469196761decd0fbbcdf794f35e448e36e3c4cf29316a7d2097a24701e849378092e4b95ca0b2d35021f0567c386a315aeebc1248a13411019f9c5408c26dca99c503d9309b86244dbe6054dade34c7c09b28b2e5a9b490388736d3ce90218777591dfd5ba2921b2181b55d306cfc479b9c0f4038bf1b901a9e600184d7f0e6c346d3b53dcf56e11eac4cfd1f1584b4a1415acf2547e0265cb3c39534eaedb72f13efc1498c5f2928795362a1362456c26b7d717cb7506c1fe20398edf858e837ed0adb4a6ae81a5220fe98277adc4408b477e16390c7a892c00f7972f2200e81bc9536d0a779b34f31498ae320206a4f1963c0f3fc977b3890f82e3aa5f9b7bc150ea7018ca6b26282431203e9458549f54724407235888bff6170b99d2c0274360024bd5c0535278b31e71098565c49a1a2fd27540277e33ad32bd01cf2462bbc0f7224e9d1c605be41ca2387f4667833a64a791615b5af0023d1cb20bed0634a88714a69379cd0a2ed727c287231117ef81c1d3436093d32dc8408e66b505ce109321c6ed60ffe3553bc9cb0cb977f21f5ed2822e55908c1f9294173df88ecce0b8c1979487c1f3c0ef0c163046e7a537d89beca220b9f2391cc9310b4434fe943f067e6003c87494b7c8d5487ed57447b6cc1d08e23f3e16490bc0f842bcb9b9140c47dbca1f8445cf43c93071efc440737175323f6463ad15b6049fc59a82a3d97c3c5af38a563133aa1c1598851e13ace78f13b322a9e8521fb5feeb82ed026d02f8953c4e534eaf103f0201d8cb2834fc9d2a69d1018f987444767e168877f16212d45ca1aefc306907b0214e772d6438f4181d23b3417f5a2bd39ee56b96c28a9ccfc943f17f4a6c53fafa68197979207e95c1602b9a71d1f7c0f68f3a452f8f961e38fcfa044ce8924091d11a7230f13a1b0ab685c71b2384bb495194ece04cb875e4e00f5700c68e9758f92ff92c8c2736220e80118d23a4f9306ef2a6bb2836405713066a89d285306b7732981767e39f0383a9dbe0702431f284804cda88de38fe9a249236a72e47f6812e17e7bdcb78a64f8769455bed207e4bef4e7c72321127b2e5e961e46a3e38710903daa4d697c6d8fec896562f492b9e68763267da35264be1193143b318de38b58b69aedd2c5e72d0b5bda78c0f338c2b16bac98fb266c6a789c40006f0826bf1f236cbcc49a32ce416eecc1a0443d930ce4cf2130e858bc28f044a54239cbb5c2c9d84a6c397f98ffa90d15df8a22f75e06912ec3408b2f93017c8d8d82e754e4d149a562e47d89437c501b46c7318ad55e965cbe12821efa8f8039770244d05361c8bd883c593b8c4f8097a2117c8bbe502e8a40d87e1120f1e90000db9dbaa0792083a43f8628751bd20b9a8727d88318fed1f195178f858f8d932520e16ab4fa68395150f899a7c0adb578eaeb623c9a80dd8a5f2f84d271beacbd1bb8259a8a99df3ba507362a1f9d7e0e1e1e07c204ea99da8cd25e1af09c4d6ccc231f5cf14356befe48d4ca3b0542f12f80597f636b8df6b4668937d13148e34daae27043f636a6066a5ee54348ff8084f6122ec5d96f68823c040b245c8a6781abc9e9f99ea5071fe729ec2b941ae5b05f90d762498fafcd917a2348badc0bf27dd9e2d0abada9f18f83863c1f21110de985e0df40dba4534842e0f108130f638c809f61d8c3d7f8487847aaa9dd672ae47f1026fa144becbecb4ec1568236f54140f297d140e625f979f99f37f2ef5081c31dc5417c48f09a1e20987c7d25e98252fa78914d6d9e1765cdc751e0f7680e89724c4e9e9a4d2436eec08ea4ef33a3c25f8170e01b9780dfa4c11b87e1e270161efaec0d2b4cbc2314499eeaa6ca2b1957788be9a66772c88cef188adf4998f5a92176ef040d8abde805c7ab98a4dc9070f43984057cb4140747fc048b3a6fc48021a7a204d1ad34b1f34f2f463b4a252caf64bad189422c71678b2e7a0e40e923b828e020e43c791d26a6de8658e429ac40f2d2345f5f97d7c4a357bc1ec13eb033f5b9f16e7ee27cae22fb0e241f9de069e63b66c839b58f00f763bba41f1b00721d24e2362a4d8e3c4682710a4ab05f94c17689b62c2fa6894127d3be3cc698113fc5a6e515b5b1d34b4ecd79983586fec48df380a236e81e21fe781d453c7a4ba74c7f6806e6ab7ce17d081851fe8e17bec7446aea3606d5382750f146ec41eab338a0ea0589009e463ce5035105b804081eb48d0e4e0730f76803b852f186822cd05ec2b6fc2d4296471054e56e384cfc23863c3f741148c711e2c24551c8e43e3e71f12620e2f6ea62860bc861e0b1dc0079c466dc6e9473f46bec7a389921316f7c8cf3352260f8b7916f5bb5f9f384145579112c0f57740749ef6062e7597489e343ee1adf0e1d281ae965edc3440769017598dcedcd8a07b541c53bb512f811b320df07ef877e1384e33a8cbcbb9322465ff302a82351507c2d6e263d264e823e2c0396bb88e3e0bfb406b58f4285ee47b187d7b9e1e863e00cec4a3aa42e3f0eea2d32427c1a8a171b3e62ea351560f46208317edbe3b78d1b10de5492d3c78880c71fa5a2f3013b4a7d280385362267872732e8e6bf3477bc07c33e9acaece8df19155c0883c067956cb62b23c1bf02441f6866e6d52480a453d9adaf256ad02990e5705651cb4971d170014d02ba8906074e224ea507934ad08f3a916a33d0a3f475310638d9629a673289cfcf9d79e0bf2863db11c2e3d18a04ea3c30861c8f9b231f424a252f65c8ce3f7ab0e6420264ed0e718cafe866c933c94ae06ca2573e408f381f69c604ff7136f72ea9bc3dc52b3cde8b13fbcbac4f2a91e97166165fc28205ee08cda11f5346c9bfa44074161bf1d71892f36d00757ab22dd6819412b51b59357f2a63c063e940f1510e7ded176d08dc0a1bc61d039dfa1e7e026db503a65ce6e3b2ab16647c57dda9bd20938137d1888b061501d157d899f0f28041ae34e5f87196615c9903d2261cc3c5492529b4a98a087c458a377b81dba24fa327cd8e71177b9050867bcc0fc3b7ea42f75c0257f8d894f8078071e48fb090da64d82e3d85188cfe70fef0ceb8df50d37f85736a0778f43f6b8ce3101ec73c21a9043f2664e95954c0afd10791ef71cfb41a221dbe4b15146e95f6c3e14e99fe509181674012419f612a7154f32187b258e3a012d76da08021ff70e5c1b6d1b6c883c071d027445c7da83dadaf0982e9a1877d76580645af00b0ca3b3492e23e662c5f4faa271ee8c811ad2760cabb2d62fb3fa0ba3c092a918ba1b1f85e9a963609311d7eb03085f66278e6cbd63a781d33b49f0a11dba1d800b98db9baa756a2e433224cd08d2064f9a330d0979128cf3939b9f340702afc44ecf406778c6f21c8144f31e9ca27841af45808c071366c7d76022421fc45dd97eb2108e0d22e81cfa502f00118eaf0852cccbc7dcba0e314307c4404c478209508aea7a3cc3bea43e44f1d3ad0aea08c233a64c54904b872ea140b9b80a3053e078f91bda74f8ca74055e82d2c42fb0f51e4b857569f8bb990eeeaa1156f487763bf793e3f552cf73facd87cdc5653a771d539dea102bbc80c2a0ee5c1a06744b8e93bfd11d47b5637bea4840a8da9d21c5f81466f0f0242f687f6261d0a6dcf7f11c55772c1ce2189c0da2a04213f45182b4e7738c767bc19e58d8726bd8543a3bc8c47fc4b2e95f60b42aea315d13af06bce9b4c2ae040a6303a8c3f3d3e43989647b5e6cf9e7b16782e6276f6893167ae580151e35882e5077040d38c0f0afc2541d8f50ec5783ec5163a912e94e3a8a2e50bd618f9213e1a7c85aa05ce0897c1d71814f8b523595a141aeb040e71ed65e2bf9f414a7e2e19bf04000cb6de6291fff49e399a8e28ae370ac4bf95f8e20e0a2cd868667aefc5e863b73d5bb722aa4bbf78f97e1e64e02bbd107c126efefc8245067e0f6ed00cc450f10a5c9aec478cf0dece121edaefc27f1955ed01f700f1259cb8f6012612dcc16ab72100e8e17fe876b8994230bf80058c2d6788910f95ea934ee515c0d506ddd76305cc8fc90cf616a200376128edf3b0ba5cbebae08422ec71343c045dcdb3f930c22e6dbe2da05c488a315a0b990e577315e9fd00f6f14c59fab4979ce0b798d83692271797a0354a3ff2f2e94165c2b01d95793e9840481e43cd815f82e9f02fd50fbfb50ce09600987a102e3639833b270efd1d1a9a8191a3702ef597972c0fa7e942e79053c017d930fe1629537f4890cced3c9ae45f1e8df82c8bfc5e861b0ffa8a9ef3b58be89fd319be0940845d45469a6fe06083c6eb80e84d1408d2f025107fc6a0aae56089f448116b342139083f38a3f9a416cdf0431b695a6dadf0b354cc3e98a8c3496dbaa48de419e26931aaec2f7183ee43d20dade8a8c62d15bed8480a0a7c0b8c11ada4afc9e7d98149934a02c29734e13463aa116e499153b7e5d0e33b2291d26bc6ce38dfd89dbe03628a37293df90d59e6b86154d0f6b319ff4cc0dcb6526de3ca192e7e4d13e31f9a3bf41fdfe663d0b0dafe98f68c19ae3c9eb61f0ee707853386707aa22d5874153b0efff525640bc1e29f20830d7fd48580039922443f21a5e7ad148b3c7ce2c797f851638f4931e7db7c7aa1238d18fb4e5632fc850b201ccb28a88ea2eae23e26b97361c0c7df40dc70414f10dd41aa1bef82d8e366aec47c010c49fe484652b34514798a1129b6da0e1e2f60eb8103b1c3f6275e68d1a2e2143d89148d34d548a877b312b5293d6af2885c38f4744e9e632854e8384428f02b9d1e3511349ffec0a36fb0b1a403daf2a8af5e38724a788a6c4109fc9f892679d58f245e56c643735540fe9aa140fb4b49a30f4289bc1933a87c461d467e49c1a277f106da34b0f8b85d243c5de1c2de4340d5c7b368e1f3217a26e8401c8efe608e056de3adeb3fac09f48c4663bf2541aa9ff101cb53b1668f06cb0c5f539cd96932f4b8a5ab42baedc2c0116d19f58adadcfc172f2eaf06ce9957f621e0899a0c7c90dde6501cf9700e08ce9c8108f6cd3847fc952b57ae8943133fe2603f28a5ce0bfd7ef86751712750eefc20d6728b5214e59d064fe86f20e53f199aa10518a8f19fb638edac248907510646b760d5b88ee923adb7c2c6ebb8139b50d385733173b22d817971015f34e8502ec2bc310948abc5ad1e05d2047fb681f3175eb42fe0aafa513df72d5959fe08a0df807160789f0251aec3f18c9b8263648f19b3e7235cc2e44dde94bc1e98a1272097f13893deb88beae253edbabc0ba84fffe94ec09eeb93caff16d57930ac131f292bd01d04067a3e5944e90d070ef90dc0264f3846ce6900c1b0099fecf9270820391424579c0f82189e47a3cd9bc053b17b3456782983b468035df2dc520c33df6000a9efa3e3cda96e0e7c1201937f13e789abf0d1dc25e21a1f10cba26d58a7920f5ac99a9140a0e74244c8a51424fc1f363174941d7bdec08cd8d7d5b87a4044fca90840f5035c3b3d7680e977341e3f0ba5274f4088a007a462f66808597ca4066cfea0cf8a2e40e0879fc81143233231c5d7bc8cfa29965c3c1e4189be004e043733c5f546da54f8e15aeb47b688fc825273769c1c9a1b70c2c019dc41e46a9e2c6cbf52da07a5f1fb29563a9c80a0431fb3c1df00cc8e7d2569d35742604123495672044cd8b80547c4f7a157b67134487edca0a3fdc60388d3f5c8b2170019e23bc0747a5289487c14bf2aba858c445eb508cabe22e6cf01112aeab00af0e31852689b71f2e1133e14d18972c0dc1977e7cda2f87946554abfa66a3a1f2c237a04169fd339d149277660f474c85ce8aa813b6e66c8810e4414c13f1de8a0d3a49af24cfec8d8703414dd0f2cc73fca4bbc4fa013dda8d18ea7318ace4b39f4703410633f47278f8fe083e505a7607c91cff91294b0f8b74306de1110213ee50699cd3806f085f0b9f28e1a20f8c1d8c88f71c0e23f28b5e900d049af23adb92fada5f1072a2aec1d9772b8d46dc41653e4f2b6af537ee2c4984f04e87a0304072e222f94378271f659500dd24a72c43d804d57af8283fec0549e1c5948c92579bae439bef43caaa6ca07b05be06055b2b8b30ed87b1168e4ce688b433924c7b713126ff6648c3eb3a8c8dd9c5afe2316323ee941d409953971190db4f6d608056ee789c8197171390d188bcf8c64c20d74ade922201638820a88b6102c336e3508c5696c593d961d0c8fa4cfd617d668d4247aa0b9272e43bb87171bef238acfd1ccd2bc230037a7e168edd7baa0d970ec80f0432768fc520f4c1e038efbe1981afe68538b0d050e08978924fa52881b5e4fc19ad381c1c3d98c30b5dbea7079355bb27c0b1f08fcc1a42ecd408b0aff04a6eb378488e133c4b0714a9cda1eb0060b0fc1a9fdaa41019a0e212aee80cd7a388a1efd174903761900991e07a0489a8496a3675b83ea104c2be7a4e6b9536502f3106466fc2736556e85422abd681495d7f3a78d1f4ad5c7ab9062e193e84c793b737dae95ec72549738bc04b3229e2503803ec1a4c1c3152ed15a18acfd1512533622437e2f38a975086b0e5fc6a8521ea7b1ea73103a37020e125ef1818a3d572586db5511b796e7256711e58b1b1e49f17427e86c048f983ec9abf70a4c59f20e3504e93e7cde3c0c0241ba849c30fea283e0b78562f1860240d18b0435f1c1ad014eadf2742a09e238211ad9fb7931e841420e78175ad587e9b0e70785087464181d2e46401e8d176783c760d049e771baf1364b8d3c003ce0c3fc117214af8587335d3e2a2f245e03832fbe72f8e46566113ea64dce4719fd3d050033deaa08950d415089a742c8b919b4153e81a6e84eb326fed79de6532accf052bc04beded89fe649a9a7128380c351f2d90b9850701871287ca734185c8f5bbcdfeac839168232ef27cfded771e1bcb1edf30090a2789e049c7ece20636bb1b5afe8f14d872dcef845b1f911049e70357f35be0281f5f580ba7b3b1d81de81f1c4bf78c4e7af1e39fa4b8d626ec3e9c2a13caaba5da0460f642de05d44fd793c714d0fa00bfc0a3112ee0a46f005f299b7d392a213bdb9f27c0eed5e845e128761c0c166b105df1012051d03aaf515704cd028fa08f2ae1c4cfc420e41d730755fcb8b0b6fe289496358763d74c399579ac0e5eda804d04d4c88bd05429a7615247a9e102530bbeed38f27b0e56b1bf2787b2a02cc7c813135c741e04d633adce4a368ac711859467d61f4cd193482734e0e76fc2ca9e9a1f2a03c1135b09f13c2cbcb3071fc098065beb7df49fc4970014c6287629569134144fc41059e7e23d0eb7f89f6369719bc9701a13c93c7c65f6040c3debab1c1b76810e223de4cf20a7d2f761208ad637870c171ac18f4315c8d3ec03194bb18a3e4c3d040e57267a05a47a5f59bd461b04b7c29e16008e1f8afb30c6e858451af6dcae3433654d17a47bcfc4986509e630a225fdb9bf093c27cf95c8d33fb6a45e25721abcec192a26fda9be05ad686bc133239bde6907347bff64514287d4900485e8409f5473d951ed32044efc287115f9420ed632ebaef826afda8325e5f7741d07fb579f22e270bfee4c3172de8d2257f938e6e78a6cceb6501f2280c28f84421963c8253225e0f23a88e2408e87249460fd4f6e4871cf0a05901485f53c384ae9602e4827a50794a3a4c79192b0a9a1363237fb3c2d3180ab53c314411d7f3058f3bd29147bb5dd32109b0db839ea8f1238516740c46341f2a4297de608a8703d8f2da1c0e747a2b0dd58dd8c092eb71f0c80f559ae14f48387208825c6eaa091cf7c487e73f6a7471c64e057ed013b2dff325f1012dbae46d00f41c102ab7d9f0e0f16389a8b8d62944ef66938e6f1688fb0e7a652e48c1cfa378d1f27296071e81c403475373e3191c107aa0a2969fb326c5cb39ba5e8423a53f9c50fd4a74615a09023a1e48419e43897c79009a246c3415fef8998c46b78321c40118626ed883a57744616b2f6b2870c04114bc8b14a7ff02a065739191f032bd357d202ccd67f801e43a3ca9f92c207e7ad3228f2f81e1f0534148fb2a45911e9a8acb4b7053e2fb83991790e0e65d0430bfca13c51d27841b2f8385857fbd35fd43872ae704417b860d4aafba1be58f7ed05d0524c40319f5b58b50c0e46d6a68d057be583d2040441b89292d37e0084233d7c7bf50277e3e93ae1e4fba64c7f852e6659dc0f429486edfc611cebd0aff7c54d60e7f6b34f60e7cccf930266ddf6523c54f7821c9195d71792984431741889476ac0af26706dded4aa7fc38114f86dec245041f5425cd26100088afe999b50744aea7a281615342da78276a7ef89cb4bc7714808f2e7b3af146321c775b1e0b6fca861ced44ce883b1edc4381c1f84978e47956051adec586884de591dcff0843b64f304e7806771e6c0e2a22f9d51a75ef5d51be883d97de2f919b4f5912e70cf48c6fc3f8c037c501f58bc808df0c450f97e17844cf1123e49317fe9e148601cfa9c317bde8e0c4c340734557491ae55766c4f91f2f861e0ca3d953af023c9122767c0080b4c762a3b7d9ac91703f33405fe9c2e760d33afe350ff1448a9cb8a022896f94a3aa5598097a37358e38a0ba20bed76748338884e81b3019d1601629f2377b0e5ed2283b17a3c6d162619eb861251f2fe60ffc867282bacc63255fe4086c2b3a22c71770dae44d25a197126ad45cf6f6fc011c8dad04c5e0e7b1a2a04fc9642f474f20cda231d1e5065075960f75dccd90d49742e1e1632484974bb9f00a6f207c00a525ffc770c91b8e12e47cf0a46ea5718eafa180448b0af0e6670042a43d3151b897b412a4527cc6f34e2fd9c4e6352a84d2926cc8bd1907246ea642ecd41539b6e06ac19938aaa3b5848df1c738cece42b0f796da9cd23c1eacf027871a351f10721eb1c7ea45d61ab9a33c3476d88ce4e522186e3f7e539ccca3229ac703150f804dbb7b70a1e248da286a2c698daf00cb8d0780e8e5899a3dfe052820cfc7cdd16b8ce0e33e144d680092503c5caa51bb122414ee46cddb05a2d47766e96d0e7c107d0c2e2ce701c4d2cfc17bf2881a35f04532944d26c814d701e09136d0627e1aadda77d031c3e1aabc67c1c2e78b39acfc1533196fe68efc0412c0ef37056b4f79b0c5e534373c401adc9370fad18f2475bd25383ebbca1f236fb1e58f2e134d73473295dc05a7c08f1077e51720f37ccb11c2ed07cbc62b9850e803c5ecf8649da1dea2f1f7a8a26f1c121f012e6817a689cc796f03f5b4eff885f02d1b7ffbd0c99d9be882e1072d3278248a0a1dc61909cff661895b25c1e05f8f78b4a41c217c0906335f88c9cb352435ef9253fb1d738fdf0eae79022df27893cb861f9522d5eb4df11ccc01043feb93e6675e95b42919529cc799295a0fa51dfe0611cc2ea227c8cf687ed04782a8f13622d2fcd8904adee46d82bf21de78fc078b0e3528f9615a3bdbc0f7793aad2cfa10fef066d430b90608943ef9b5f83fc612bc2181405b46358e5bf2e344571b0d701189589c0b4a904f3659f8220657dc51013b1ac41e483ee982241d6b92ba1b680eef4267e743685874c5538ade4b12036d89029c2f634950afa570fe973db1cdc18dde6bb9c3f86a9d16ff4d0ee02bea44e49d2e746c45c2b827b007b52842110e7b21f52320f170502716f7a3b54f7f653a7ddfb0fa17490136929ae69f7482bd11d0967f1c5b3f08073e232e575382e4c9272de1748ab6f82fedfd88a0a870c6bb4e1acb92e027c30675132f36be1d9b7d075dddc37194faa54e80de52a8315bc122c53f8502d14f7a5af21755509d4654a80f74b1e59f3c1c3edc25407de28da8cff3b347e7b57e5ccf981a8e2213227fe452f673c024cf906ac57e40763e131abbc7d422c367a892428f61177f0d8d05ba94133bae620ad803c659e0ab8cbc7d5884c9c37e82348b400d5c8bc0019d4959ea116c85d88a41163d9c36598fa34d29672b82f5441cbc871d6a7c328e325c4784b88ff2e67826d332ade88b952f51036a0759c4e1814cbf5e0a989cc7b8f2b23f51fe4a44be6c4a72897ff268f5215cdf9785fd361485d15d9c716d6413329f685090ad8825c54fa296ed176a2cb855112f2f6480c2ffbce9a1ab3018e209e6087f2249fb7d3e95f92096a2381998470f0a8a8c1ff4c8e969e880714d3b407c1b0b0d5e83bbcfc8081797a103c4178a25fd9429979e888c24ef094e277f4357e7ac9f54fe810d820f34458d5ec0278acb4c74bc2208081ec1b1875643a2f56a2043fe43419f87b4a68d8711bbf12588e8d07ea518bd12da0c8ddc00e9876071f3114c01723505426cbc4732b7d023ee05ade17b29727efb0334ea4ae464e8b7541ebec89bb8eba8607425704b7c0102185f274bd0e50008f1fbb1f0079f7e9c82a0a6132e01f2820a0ebd11a701be5806f815f6d0b828394b9bcf32ecb9dac45b5211531ec769c63d70c14e6701153feca0e8583195dd28a8cbdb3ccc3bc424bd2f9b936403704ef83484741c0cc993ff9322e6800679f14d6cd5db5810e10beac85ecb274bef4600f45929290773c2ea3444047ce0e304ad648dd607c0b063e7f1f1fa2128bce83c35c0874b4bf32cd1057e20cc51af2987388b4f695ea646d3fb1c72d2553860f95084b81e91cb764298aefc9310fd2834850d6191d7bfc9e8a217a786dcc69430bf6410f1c9a47dd15c1b40f9190b632e67410c07b529f06546575cc6a5353bcdccec4be565d18eeabc7e42318ff7496c9f14128223eee5f12ae80f3f33f53a12add823ed30f443bd204f3dc3e3e13b28b40a3a346e67c522efb3258907faa4c843d0d5f84618493da30f0dcf5b326b226fff60fa96ff04051337c3c1c837b864c7371401e43864087a11a39e5a4f0d348e618f80eb0989e0a81e61d15da908f9a13a510fa0e78a5bea1bb52f8091bf9ad6ba9a022edf4747e10b89a3e802fe4cbd05138e7c46da8b2f8003cbff38c3f7568a787220ac002d83d9c82fbdb1d895dc123cdca2a46d688897676b24e605114138a440af1d78b7c7236089d382a6f4702d6466bc7ae7e1470478b50f2a11fc9193124f6140119ff3674b67d213e7ffda8c7da018485f4654bb0d4518f177ea0f7042c0b7b084e2274917f9a4007c5e520fb857d11cf429eac03ea9a9661b72e987ae19fc464d587e27638e9f29a17b33c43aff030a20bf90a9866eb73c7d1a1736365b90038fa6a6e6d318833e28ae4bb315e478344ad3f5bae879004abc68bcabe5a7e50dd034f2f878c518855e1f4a7a9e1f150d855afd4993346d2786f01e510d3eeec3c6803f60a3cf9e42067d138c9f5e0c993bff42674ad3907bf16a4ecc781c2e32792a2f905e09880c5fd6b8b782505f7c041c14bfe02ffa5a6e33362230e8ff0509a319d1d8fb173fa4fb10601397a3e56aabc9b2e299de1afaf14819cfc3a1cc4f5119bd0eb31f1b0f9b282fe5c2a07d47ad8893fd20d448c8887a115d6cfa31038b9f31f5f3692b82fca3bb022f02ce0807d48bf30d8994933a92c00f046af91431a18ec9a0f89d8aaa55d419f12fa0346e2037bcfc0d9996ef5480eba3301d68af4b979e4da1b29d5626e8472af1d848d66cf928e9008d7460f55c68a070cf02e58f18e1b32d3dd07b2352dcdf3ef17db8e5d02d7bc8d3ec45fa2548007f019f212ff3a578bf36160fb663d0571ad4a41dc9dabc0aba249f408387cf9116ea039478fe2702759a0623245fa588f8194a17aea18f00373206c4e731c0659fb9507d1f63f2ec2d2db4788dad33baaff0f538500239a23a40dc4525e61e62d87143a1a2ae412ec3cb955975a449097e0ba7e71f61f2701d4a121b81fce89fc820f0453eca3e0e86285befb1ca5f34959ec0579ab77840634f9aef5c0b8a802f1430c6934018fc235a86f85308ab07259d791205135e0b2ae11bfc71f9900f3ddec417475d2ac5bfa70a6e7a6f09ea37813ad36f4014bfef0518a7b10c7b14806e7c83d6cc4f0853fcd5c7a5575048e879c2d6f758cf9b843438aa3e1db49541807f0180eb6f413479550d3cbf271de0472e35f2e999384f8447722f8ac9f40bb88cf2086e819f84abd456d008cd275852653bc964e0037582a1f1bc9ab81d80429774f7c5b1eef83d282782ffe829e3f99254f92753aabb2aecbc183d63db7503e4d7f440b5f10c157a7976d46c789e3e56b4c82134b9e28122486d345cbe381394a06fc1f0b8bc414f27011be4a206fdd0a9a88cf82288487c8a116d6fea13557b9023f913366b731d91f41ec650d28610007c1f95146f6586833f3234e029420d7235c23b3731ca91ab919ab1479102f584e4fe68c43a69bf47c006cdf868e0cb163cd24847fb351c7e78422c447c9fc0badd2a487c3f4810e8c1039fde0b0250eee00f2a3fa374e88b7080e47b42e8f0690fad0f03b1f427d43a27c1e535a8440d2ec3cf28a7d3e4cbeb394ae006eef8710c1116bd919e9d677348697782d1f86dce023a8c1a42e7f41b9c0e182a0e1dc1674379f1eea66585263cf5e5762a1ceac03a763e050142da83a103bee489412bb55678201b0474042b5f6f2a012aff26ae4b0b38d802a8ff807c0cda8f8e16fd0c261e3ea20c8de33571a13dcf8f97d191c72f5169e048b85cd25e2df178e82c5fedeb8863e0e37ac6d389eb706cb2e9b6087da52d81f45bc690bb82ae74813f4dfccb69e7e34446be8fb1d5cc02792e1728421baa44f89b941cd88a578f9e1258a1f729c1e851c470a39f54f7bc1dac4e1e4a0d997bf28467bb251c5fe5c5fba5ab4dbf6786ebb7e4de7c15a813df04e7c5d75052a05150c1e14e66c3db3e051ee1ed8ea3e2d13e92315f7b019a1bdf86439d764149cb330045e3df0001f40524b56e0a551b977448cce556b667e3a3906e6306936fcab166933882c5c7f270d87b0c2e7c131d09f4802c86be9695e88fde174e08c9f1a3c449e152489cf9124a6cbcc59b2e1d48839d83aae3fa1b4c7afe6d0fb5073d5d9e049dd3956d90de910d1c076561fa8dde26ba11888707f4d8eeb21a327e9291272f4153de1bc01aa4e5c8f8bc4ac88c473366c917c66d6d3e2a4e8fbee9d881101472148040bc421f297e4596f58d44f5f9b533709a4f9b4aef46c38bffa352f83a16e578029998cfc493fe2821389c0a058abeb3b55c8e1c383d82c10587fd4c72505164bf5afbb13bc801e3711d42fc1209025e902010bb149a3c9ed6a9eeb336b43d233a664ee7c5db9b2862f60d14393a0308495724c9895772af6c0d2b30f9e3ff79161266bc9c3b093e051af797a1a0ec278c42fe491b541f3db1f762a74a69137dc6381f331f5b0099b22fbc63e8edb869f41610c97d170a479fa9cde93a24cdf1447bb468010d34791e9aab1f4328d4077618a547d8a1f32e74516d0311f045e460b7842f835e8ed3db663dd87ec01e00f75304d0e7f2f43dcc9ce0004045d930880d3f8a81872d234da30b19b5a6797c09f3825741f4281764ef836ec7a329a5e69d2085f929240b5f446fcbde5368cc2f318ed017e69ef89468745b41d3e472fc582d1865832f5218e2a69a307f0db080e7e80bf02e16de9fb570be2ed06daf557178adc43dde46a7fcc1d9c96f1222e8874cc01d86c0a6679054f039065c70af0537b6e097073ee606ce2e3c20e71c9434be8aa5f5a544139f0dd21c8724804d9f2a83c8e55030a1491512f8180ca5ddc520ef2f65eaf08b9ea8b90d34751fd3c9f2710088e82a4fe03794f3c3d526e5fd56a23f0ffc4a0f5514a119f009781b61dabc9d3609de01079fd6f350c945a429d14ac640fa2c3a98b820542afe25c4e5ff6034f00a70d6378123e83ff65c6dbb00085e489c357fc551d20d5d31f4d1b43f9ee349a31f7149ced95491b13f8c35722d59a29a8b48a3c3a12672291eb21e01104c3a0ccf813b2ab2e26ba4b9f1b0c6561342b0c1032c1978812280be875bc9afa0a5f93e191276a565f5fbf40af58af5e8634ee8f9a09836af44c08f1dc8458d57a4e3b4cd1c1a7caee68a2d45502257d0b7c4436511734659f4681e8730ded044a6f7f060930bb03ae02504afbc281b90361f214a6fa68aea4b3112f18808296a1535a81fc5501dcf2168cf532d4cbd40179c6f2249caa6d4b2780a832df6567ce7f1141d6d3a4304af494e8c9e0b3b74e5a1960ed1c2f9291f906c214ea92302b346ffd0b0e82ab4847c0f44657c479b38ed2eee71b705785f7883f14795616d3e20f2fc008e409e890723bfb3e4932644a9ce1b70e040d3a148f01544cc4e268df40abcdee8042ee363e1436a6b80c3e0061ac93da842b4c7db9bdb2c28e823b9a8ed4aa3189d8a234fae200f7e237948b958214c5eca081a0d412ac4f99c7f3cc9b2c5e72119a2013813b80c39ec5b58b9f26bf070f89889c62db510b277c878e369041bf787bcea2b09cdfc9bd50d47115bf2ac295a7e8668a59551e63c0e1ee9466c91e323c18cef8d825e8b1b49cbfd39f1882ae89e0c15320f80c3a3e3982ef2528b4ee901615afe1114271dc12bbc8130a6f428489d1e4206413a909308ae820910ad20079a37c124692d23ac4b68d3b41189a1723a511ebe518d1a5e4869c3b6e312e298b6c06db7beab5f244dea375cd2fcc3994afa5dd4e6230835d8c83f188ecaca86fed100870ba24368ef801bf4260e08d1523423bed205a55e1482cb3f310a7e843b707cca26403e22fee072f00cf84fa9b8fe468f207e29a9e913e12ddb5413317750c951e7e8e3e40dd93df123074afd06d569bf80987c99d9e32f796a3d98671c07038a75c4463fb6201e4e8f63006b97c942f40cdee26c2c5aeafe071cf74878b0f14c437aec4785723c1d9fa79d2551d3c77c3c760c1d387c4d9610fbc67cc327046ad0a74cd5f944362d1bd24ac0e5c8557a4648ee2f2698b0955f763ca651a3de9da4c8bfe441703e01ee8a298a3c1736751eca858adfb04386a33171f553fcd49f08e47ab412f3f530b9a1af84bd8fe2eacec62be3bafba494177a91c661a921ade845cdfb8823b8e59e5587c00583937d89e08e01bef85d9948fe66c34fb77ff4b8a9015e7ec8851f0e0ac5927793137de718d9474812f00a8f58fea688983b1173f81a0a7878151d0437dc338ebf1021c5175595380035300d82470f2f85a7c7076b00fe91e6e37335f8e208be18f5a911db237481a17db809fc2245c8bc981637ef014ca71dc78ac0d37df1bd9f436f9f22079806b416e93fec701f50e1813ffad8e35c5d391c4d3ef41658893734c5c81695698593993143efc1b372a753f50150407d154f625e911e0c8f488ade0b6841f6155ab4d1156688fa0b47e2ec293b8dab6520fbbf87f0843ccc712c4ba973b162bc4c032dbea7d4b13fe500e17640003c7219e85ff099f9de87ea23d946b61db51cbe26ab70ab898af378d244782e93583c74cfa69d82c3fc014809700a65103da60d699a6b9027477109d22e3124c64b456870515fd09ec219996d05c6224f1b44be882e139e40a9349da2039c63e26bdcf160f4c344617d921ac56fccaaf3a5b4e65efee3e5130c60f9102d845cae8ba08f81404e63b883e349fe4878212f187c89f9d0c55c22f24b343ab49e3042bcd49b1b7a81609817d143e3651863b859d552435341f90230c46c51332957b0e54fdb29a2c423e139fa3c7bc1335a00c7b7ac9972bc28df0e3450e97fb868e315d450722f0c10683e4078aee6304d3f3a9c731480c4e8528a966f052b929711611dc5f6a3978001be1151755a465fe15b5152730d8e867c0d25a6f4a42d34fe9085988f0441817f3260c2f50cc1e2647433360aeaa08bb22275075e3c7ca1b0ab7d8285956f5dd627f4a9e47d30d1e931cb1e5e8091deae21394e4903915d05ccd8dbd16447fbd1d35e4310a6dd86829863d25abe824a4a3fc521c5d76982f4206e2c5e7007f16934ccab0961ea2b3540b015ff0abc10e8041d76818457c9517af4cd1e2f847ad90050e1f147728e6c2f67795ec7a13b3d004b0af72a4017c1be79367a60ee4ffeb7043d5e258f8cffbb60480f5970f65516e1b49f1a89dc95b1dc7d82647a19690a5dda88e5d5c82e68659343eee9cd6b0068bcbccbd2950685947af592d956d478f7d12b14f682a8946fec33a167207af47f5aa4f94058826fc12fc59e22a3fc290e5e6f8084db272a62625bc01bfb4a49f0b612c9a73794dbf080b3094755e4d003f51878258918be902935b764d6e17d6872e296dcd636a8422d5e0aa4abad4c3c7c0607c4f80f432a6fc866f7f0173ecfa805eb577cf8f01cb8f0d874e6bc0741206937df08fa376844fd88c052cefcd463176004c005b90cfe2eab93438a46fa2f2b575ee5079c9e2b30c2db39669b8760a817c06866eb2d40fb0c80a8e82913c0dc910640bb6a24cdafed9636d087cb81f4e9fa147d06fc8029163a7e93e24d98bdff38c9ce87d940a4052431e4583c38f03c06ccfca191be0dc391c96732b5be932b06fc50811f1b060c3cae8b84edf955f82719ae26a0a9ef9f2ae8b89e5d17373483d22aacc0710b67226d1e800a7e14182d2ee092869fb22973400f9e2eb682d136d1547454605268bc2a971c8cccde4cbe387edc99fb9824d078b6bcc7dd4850895f26863d09074b1c441b3e1b552334bf0182ce47a9a1fa6b81732f6102fc7039c90f4fe0fd13361adbee899507e184c3c7b0947d744628fd68ef824f3759e9798585278467c2eef303e659b0ec74e004039ed5a7cd7b0c32bf5012d996ff4c39252b32bcababc12b32af78862c58ee864ad8db41d4e2bf7c75fd98012ebeccd200efdab4e0e54292dccf02b387c422cef60c761e2ae2caa35813f3c5c707da4e95036e250b1c4d652dcf930824661bc27172b61a56b6211b264ec80496bd45203e9b26497e854fa09f9b917b2b6b8723b209a4273cd6f8367152e8418e7a3c059c985dbd81c7d7f8e26c1603a63892410134d9ded4a96ce0e111788c3d2435d21d6a9082af4159351014673e8968c7e70821e7db7059bff08f80af0333b801fdc0f0d58da12b5a39f39118a1daa52cc8f1042d0efc994552feecef81af5541bee70a1777e3c2d33bff78d93a0a277d0e3270eea070ccb36030b2b73635f9b72d58f45484d693654df0307fa27cc71e890d08cecc6f29fa690a6c87ae2863fac349f0e32162e88b8430bc29438a3b5e23fb45b7e8aed1d8f6803625d83556319f09ac71115b223e0d96d67ff521fc2a46a09a83dbe89318e5fd9d26a66721707b3925a9de83e3f44f587dfa1784fb918c39f1cb2a57aff013e1569c2b3ed43bbe0601af0b21ccfa498c8dfe4a0a2a0f629df481a4e0df1264a255c478e46a3204ec471420f83214a6ff3388eb673d599cc489012e2746111da8410f0713f145fb01d3c5fb8068f3c4b2c4af0306d6623752b91bdd95edec70c007a1d0f4633b3cb8a8b9efe34095bd1a9ce3677003f436da1c77ec698cf36df9c5bcf8f470fc185d508f15471ad07b5114b6bc9528871c41d61a47b5c5c84ea322f683a4acd892d4a07d972182baeb6b85830d19b13fd5e0fab91f627485c5099e47a482a33075e3352491b78e425c1c4d8ce03bb47cfcc6978eaf31a1ca456939ee25d49103d854a60b110971c345a9f432111ab7f044f397ac10f92c6e706c1e79763c52754ff74870c10780597cb819523e305083fe2461c277b0e4e758da18fa034aec741737224e860cd62b3208f4732d9c7a06a448afe08f9847c67afecd04a75de4418d37d183c2c612f8e26330387d27430fdf14cac83e1464cfd110dff660a73c8fe748a1bd2845ec7c8e39b6191f721c480176afe1a09d131f18af72c183776011e3319d017a0102739ccc0a327721aa9223739c7a37304c2f37a3f90699d278a34e2d1cce9911ef3577e88062de4b70d1bda899ec41085af394ee0ce805111279a6b5245ec790a00feae068fb9101ea3321f91c4a232bffd6458f26a2a1854f414242df4935c7f3b07ce8248553be831e91ed44418cf7d465cd5dab390faf65f1c1b98f339922dd801009722a9d34681269aacec6a984bf4120e5092dbadb326854bda6540efecf0b42e7c320c89b0a73f4bb53db8e7e3979b3b40f8e0517f7ad60007fd307f60542543d89e9a3bfa2b0c12d493d39daa708da109854ae01cec7bb7b32bd0e35607484a6a4277569d4038a24c421a8d9da6e0d14ce688f89bde0ce97a70a10c4134719f0035ab49b1285e389f829f3229df2fc953a795e61cefa1cf3e23e73e5740d3ffa6c3eb916bee248232fd323c49f9569f144f4b0781793686c342406fc20141dbc8021238e4253d7c3f110c82ddcc97a2824f8fc941db56d67499a8fd2804d1f8211c1e39a763421d884f72288c8dd54f17b2814331712a4f1218832e439f87878608c3dcee601d44f90d2e2251958f260700e2db6e5e24bb1317a0a8910bc8e03285b2b062517b0376a5359541dc426b0cd14ebf8802f777ea052a0977754d15c3c50f81c4db65e128279d5aef39f124972c1b14adb509a9b6fb3a26fb719f2389536195cd01f1fbf20092147fbd37b0c2e7468699454ae748ce39530e479984decde8a62f9404a2e69aed7f34d833ebc2a49150f43a5c1f10ee9bd9e15635c871a02dfe9caa73db840d0d5505568310686b893bca52b91d039ecd6435f60d67803743bdc511aeb0efa40791747371c48900c7b4f029daf821742cb0974e8016811e46a56183d5496123b0606068e0136b03d10bafdd246091d85898b1fd226b9ab0ccd712b77507d032ce8bb41f8b61964a53fbc12c7593880be2800e25edce2e633a1a0e45e4fc03c50e7f8067be63ec0a92e0fa543cda39a2460979a63e0ad187af3094b4ef8af617b158b895cd3f4e97e70875764577c004886de54221abf67b7e860a412b9daa916ef5528667f95fd7d0f3c1b7a459831af4489d363189ae359658dbe5058dccf4990b4ffb490a710a3cc0721eaf23c9e5aec313ee07c00e2d52e43e4fa5158a4369e9c2d0e83a949e310f4fa234d7f361f3724de6741c7ef4805c9bf60c9d15e8eded90042da5cb6c67e2bd1aa1bb9549f068b1e9a121b1faf28cf86bb3b145f52202a6ef725d1abb81bf004c844712d3710341248547c9022b00d17e9d1ffa575d184b4143d98548b8e8203095f2348851baab1f1042a10d288aad25fc0a4cb3671e8c129418ab3cdcedc9e2d928a4fd034e1a0e23a7d96051aff9652f29faacd8f12f86a477898dc1916c3171068f23ca041f657e2144794e6e517c1d9fb528f22f9e1591007a02474b596cbf1bc539d4a451ae7b263f37d0af37c4091307f08a1cb3bc4a3b69670f5691dba74003b316fcac09707dcebf26f1024eaabe7f311b508da9dbe852f802d791691498ee3d1c79332d3e8ddf94efb81b87a033fe02fb506e6cb3e50b4eb31f55c6641ede24af63e89717e24959e7fb466f55806bc788a3f287cc99b27dfa6cce06261087f0eb6a0950091e32d029fbacb134e7ea0d490176217e8970440bf8bdac6292c42fa02869afcc8911a9fa7657e5286426c212620f993d3994d4542f83f3816fb528be593871e6d02815cffc7494d936023e45fbca8b2cbec18f32f3e64fd55d48d6b5129e3876429ff05c8e41b0df4f94c2674bc2527957e865b9f5613e2f0dd1c3eee362adcfe8796425b5397d8079813e217a200baa54038af9af1fa0f30659a505694dfd20842434043e55f1e663c8038005e4e9ea2a95fa03c4e410a6fa5ad1f924adb2312dd390b31695e908e525fa96ec67f2260e845e0af6d7596c77f4cbf7ccd942adfa92befb9e2b479d8d7defb99f19e840d369f07907e3b3b2ffe50a54e5e48663b843f578fc04cd3f73c6d7851336eec360110bc801481e339abfcd8143f0e33bae06bcca6ce0306ccbb80f268cbf174e001dbaf0ffb04c42978c1a217ff8d6fbae06a1e29acfc9b387bb4238d31afa46d7c130ed29c2f4cd71f2174e8000235e83451c43cc35a9cd67094bc01031bf48f0f769e6d490b974a9f788b1cce27d38480cfa9b1d15a4932cf4527c1950eecbe830622fa5311a63b9a69d036eaeefc8824d82efc84e4c1ec2cf1c410713e076d94062409e908f630d06cb836fc86275397a052e75fd610bc9b45a79ec80113ef81060197f334b65c5481b7d939f56006c4b99b8020ae06e7f124a850bc893b5dae810ec55620a5f0a72c65f92c85de77c6429c0395118ef6c6826bca22e635c8f87c4725498ea910168d62cf79031dee776070c99f648a74113bf2ef8d35fae709bd1f9472677381adf06de78db69b32fa410a9bf6112a88dc9388d8afdc129fd19496fde41126dfb0fcfa205a85bf229e0df8783cc5a6bd4fb4e4c14b6202633f782b6e372580fe2e787c89a2b30d1887cf29ec35d97467b05e0318d6e964387996899bc6c1c6e2bb383bf8a4b04eafe544dbe70865e04bd531d886f2d4f84026167f063bfd6a933fcde194a9ff43771b11a081bf1317c391fcb8e29f06b89b90110d0f5c2ca4b59c637e6f4a8f2d24548467e364b8f1529970423337dc4d1a2b0f38d8e547a908f576ecc0bca0d9d103a193fb416e723e068cba4b448173530fc06c46896ebf264c6ff725950ee89114af1ac4c7878cb9e113da7039963e2b5eccc2d7cfdde1a20fd491f32774a6f6a80263fe4b92264f632c869bea71b61b3cd73cfc25d0537581f05154bcd8657ea4de448d0d2d615ee457c492bac726b04fc542ca3e806ad3bb652c39dfd894ef02e4ce37b681f1335256fc1018776e890b0f774263c5e7259b0fc58ae32b82bdff9b01e8dbaa815afec3c71028ec113c29e183ef433f19dcf22d4a086d3a39d4fc0dbc46fb1329536ff8a6429baa23c7b910416e3a2cef6e9e891eccf0f0d54cd0f32a75947c1f162bb744063f1e9de83739c3c8f118527d00940a2ef76787263344d0ff10b97e5003280ea90dcacf75cdf922a4127bc881128f00859833114cf57f82eda6e3067abb44be8fa44194e309f4e35ed6283f8863e296c026eeaf045ad93c36697d3253a84da62ad137093a77282d526f2a0b8fde9435e461eade453809720f951838610b463e800bebf11cf20f84caf015a224de5198eaa69087cbf38124c98104f9e406ca2ce84288d8fc1619526e456f834b3992c603eb98703b27105d8c73ea8d81873fa8a882bf6084e1678b3c5c00cba6b1d87d7243312ae93420327c114c1e7cca012f9f27559b57094cbe9902983c6c089f07014ca1b1746adf0c9d073f8da0e7f3562fa7d4e192f3908ef8b0107abeca82f3135c91f8198a21be44a9292e6350e0d7795df845a044b991187d9cc800c72750a1f0170b60f02450310e58b6b6b7944b5cafddf0ad0c2c3eefd39127ecd4742b22835c881ed73b9954ddef09388fb3a1e311f9d0bc86a90f2f62d4016fb1a8a89580a6eee8a5db4748117a872f4336a645243e42aee3186818f0cd7c6a2c6dfe4de181f41096f8790a5f75ba141ed9f1fc54d27f5b14fd0388db86105171036ba27be9b2f0775f7bafb69b9f458190e6101f713dca3aad63939837cbe064ef212edd0298c6470b4bf29b28fc78ba16cb479cfbe1815bc8f88d56856730b0c8990c32a52b114af9312645f612c33e7fe8678b572931f84c9ab4ed35646ef8873220ba0a08d253718978e89d407f2d29769655f015e980d972d25a7ca000289e03cba3af706443d3a19671b60450bc114c920794f7b81bbd91398d48737ea822d45f50f0f8ee25a6bf6d98f06f38ccf021cd39aee10bd94b2980e35f5c19e3656469bc8d11077f93b1789e0e397ec29fed39ea18f80f4fe92d6049baa2aa0b0ff3b5e51aee3eed337b9a1ee50f9e23512ce12c1bfc6e826c78a6ba3bfd9661f1813ab4d82d4a957da93db1d641b6e2f5c41e9f5321972f20e36ab72ca8bc4ea23e37dc5bfdd4b2d0bfe558e204109df89d38293f650045173e797c0c0420e841358eb882c5305a8c1e05ee69bfb6e2619363e023f4711033bd963841b60d3c597c53971e1acb69e8673c0369275a811e0f5da33621e19367fa13f6290445f14defb81dc3be1cd5870a7a1204c467a037c26d0cca7109f09dfda9b0826ba226b71e1c4067c6b8e39fa2a8fc141e057b4ca101df65cb99ee7445bf873585fec8abef352da9f15e566cefd828b6a91df8b8d24398b6a242cf2bf380381a9b41ff12616a37b194e824fc98fa5000184e870c9776d024f324a9bd3f42e8e047f9b0f2b196519ee61092374329c183c2c2c5fd44a8e34e0283b661591a6764f8e5f90808f3f7b2e683641e1fc009777b1941cadf1cadf496ade9a77094a7b11e99f925907efc9a133ea7e4edc75926793283567a1021b637f3a7d1eef086c253e281b73920c6f99a2e7abe09dbdd4322437c18ecc7e3199243939973f177181dea4f5ec987d6c0e11d94685e93a72bdfd08cf30538ac681c4e167f41cd8b1d29470dbf504480eb5104f43f140c34d655d50760b1491748c3f402512aed492e083d04a7867f1006c6975132612349c5c8978825701498c0feca8be83a8630fe1d2ba3740c12403c4d4f915d2307d2fbdee2bc10034abef89566cf10bcf3576d5ce81d6b2ab8b6c4e71f7d613a9b428fdb93d876536053fe018500be434f8a3fa0b3e46d14a0a28f586df9193128b8dd02075f38c6e82bcda5f9412c25fa4b93394f53c8c43d9d69e28cfc6ced00578a388cc3315e2785cecf40b073a68a24cfc1099d6da7e0cae55cfc784d54c83ca0a618de01039ebf63e8419329a6704191523c222410fc8a4fd8a01e8d381f3543b481af54ce45f1f787b2156ec311a437f142d059386d9f27a987cfd994c0478081e4182628d991ec70711280e6cd75a189e78d08f14f38097a3313c4b6193e4c7f2471669b795af37e7f8f76e3172c1e2745a29b9ca27ef668aaf5ce78f8602431dbad8ec25f4801e233588d712b3896ec278032f81bd7c2be1403c667c96969b6e9260f559db39548983890aa241f238e85ef90f3e633ed40fc2e4366fff246e77fd4e6f764e4edfd8435fa9e26a5fe8c021e3b4982456fa291915d43abce2f4300ee0e67e0277353c6b3fc86781799da43c932f5276c1cf9ad15bc981d63f692c5ad5fa1d51dc0bde414385972c516837e920e2d1b500dc1b7e446c7d1ccb0f0568e6cf0210a8e3c2257c16e1233f118594a3d5c8521ce48a782c6f22875423a98e8e7a6f5ebf6a6f8d68ba9c353e4bec581446f22eaa41f319ae46c9c7cd1678dba388d1d37362ce0cc29fd85f9a5c2c4975f48d13518173fd2a34cfb11989c0341eb7b293586fc290038be3a28e84e25f30ffe743825110fb4280bee8016f7ec026832b98a4560fbfdb0e0976f9ab61528a71c4b1a005d278989e7a14486f311a2e9ad9e14d17e758af89a12445fa0c3752ca6107d0d1f9397d445c3eba0da792b3fa7349b1f5bbe230da9c97c7aff45d7104fa285fd3ab4375b0d80e6ab6074e7a5306ae16628a4da26bcbcb82133584fa44fa06772e78813320ae27c84886c0b07ba7ce21c913d8a6df357429978316e34f95874bac5a46df20740266802a5bef884226a5bcad8865fa395e067d4d0f172e2266d1a774a4f6c456c5389407c8c0d647f0381a3b3b1d26427fa245eed14f60812057d4319d3e3bf4c7f0783d4adaeea3ecf17047d8848097fd363d459171a7cd24aaa0da30815f77026cfddc4413e1c184876983c8dce7282f10db29e78f202943d8246215f72d7e5c5a069ee44ce5e31cb8ec72b84c545cc307bc54557eec59091ff091b1f67c08d975af3490b6a23e1c95c82f494caf89e4ba444daccb0cc1f0070e72b6958f104c6d2e83a49232f878ddf020f3f57c221d11fd823ea9f7956ec54859efe485602ade840ed4fd821d09118acf9368a02f7e3967b76caabbb4cc8f364989df4639911df655ad588fc26f80c254eefc38485c71034f9a51eafef3161c519a83df94125577b521bc257dc1df1a473ccbb68b1c171a409fa1e5d95def08aea675837fe0cc0e7b3a8106a42721439a017669bd3e3a92f541ce09ca25dfed49dda962245ba95bb44276a92cadb9058340b670cdfe2c5f59e924cf9ea54c96694c5915711cce40b08507227766ab6073721dc0b090bda0e5387176c3ef78607381cab0e22bfa208c183a892f7583e02781c0e98dc4b90297f864b229d5492e6d554c1a04350fae349a0247a1b99e0afd13bf053a87e4fa58f9d9f2266c71539c9a2ab1832e493b232e939761c3d28071334256923575e20e22ab69070d90fa07719d1de9484067b69c7e7799451a4993b06bd19238a7e5341f51de417defedd792f311eec47868c6f47838eeb6913e49b6c92e15e22209d3019c00564f2f273a0505b8cebc22f9035f5912118fc50c6b217ef143e9f215bbe869be767fee8700233dafc1e19bd3dea080baf4b14a695893c7d8e4a69bb0bc40a8fb4ad6812090a5d54744c7b20c5e8721ec76c0f4312fd8a52c1335735c7343841a780c1e83f069cda93dc48fd11c1584703a4702c15695e02d1c16b51ba4f8963f29cdc8e6cb844a44311d5a7612451971369876725593c9727a42768e9f27b6d5fdec91d407f5141cafeb29bf32ca71e5a141e02dfa40cf552e8d4f13e4d8eb897884f9e064ecdc50896793f6a499c8e5415df0b15a6db408a1e24d38bad284b863772c5dc8028f579f28e0a4d68a380d77024c5e9dc2073c61c5c768bc3121e02d3809d64539ed72952e0dd60ddf919272474211dae17b324cc05bdb9f95457d169eeecbd230e65be088116de82b06b67457ae1747cb83c53e379078daee8ea283cbf226a8ade9bf4e57930ce5b0291add728ce769540b66f81b7c4175df2e3fa51d44dc87ddfe149e3eb01a1f07314fd762305755e539b737bc973e1b72425fa46b835a7e04843f301b2f65c4e5dd95b04277857a546ef838cd61b3ad9dacb08301e91aef53868988f70c0c1af1123e88937b4166424d28bf963c8c9ccd8784a52e2e8b92d22ae8005803de3aff5b268d59660a3d19d666eff28470cb7a264b89188427a9ff981e6aa427a25c9075e640da817f4e4b283586578317b1a6d37794efecb87b07d06c401cff444f0cf5c3d1f14c3a87f50b97c50948ee65261812f3aa2c8f3186db81d4613af78638ffba912e01faf12dd4723e7564b41ea5dd308bd3210f4a2789cd207d464f9991901b6163c329f812775b34186deab84826772c7e74867dfed81a3035220b229a19a7d929b45da8699ffe31b301b4097c6efe44af14133a47788f1b5efa0d073ba448e6f2236e153540cbccb1734af52754313e1e0e2cfe0489f2565ce0789c2ed2de18e873933f5a28e7470382919f6030a487f429e9c5215f5adf0d0de6480d89b3ad4fbb9cb18af2a7bdb7552edfdbce8649392d3e4e701fac990cafa4514fa6c42403cbe0503612f9ab3baa4114a379103957b2ab2e4c91e993c9798437a1d1bf265fea61a5523450fb1e0507749f1e70dfc60db6255189fe649725b9991f02b310a2fa0cd98eb50b46e40a3a8eec1c81ddff44c7ecc550287c3c4c4f3b0e873681e4e1f498ed6774294f8cb5056be4604833732c98613a1c3e198581bfa05d282f7f86be1a624157ab240d6e30094be8616364f6851f40d49b86d23f315ef24e0cc932a13e38a70038d07a2c047e852d309ba40f90d988078a2347c1c110b51e3399f0ea8c01b1da1cecf332192691ff9dbf044c87e04919a6dd2a97cdf56f9949dedfa8c213597cfa4d2652adf67bc74954cb9e57b59e6d7d08f9af1ba7432d9f585e524ecb66beb423b6a3295ebcb84c23374f932932cec521b359fedd2c932df240c2b9fafeb54326acef1bc269fd0f8b9cacab5852e1735a1cfa7527ea1719229cf4a992dd48a9a4bf975996fdc3e994ab99d9f2ddfcb265fe844cd560985ceabfbb6abdbc2f29a44d49c9f6fab9c676552b9be70f2b9e47b59b695ea43cda553e9b233b48561385ebeb1cbf7b2869acf6452764295acb2755bf6752ef95e56d95275a1269c84b66d0c9d656772e98459a99450534eae70eb945f1766199b4ce8ba542a99f0d309f3bd2c13500f6ab6c95719b7f3ca4cb250a87209ef096a325de80cb7cb79665d280bc33375b942076aaecc784ec24e7796e5767699cf794a95f2bdac9f06d46c6367fb649fac0c4d269db0f3e57bd9d6655d964dbedde54df59fe6525e425726dcb2cef995e1d74f53c9c6701bcb4b767db239749fa6fc945b16fa64e5e73cbbac1286dbc01830c9e4d36c63d84daeac9c845fe61376957c2f3bf785f655e20bed6b041f355edbd36c5f585ecaadccaecf57096526d7967ab1b97ca1cee7938d974a3829c34a177d2ccb32b51ef87d95abcb64e164f25dba2ebb4c2a99f0f39d974ae7d3855f36562e09759e26db2ad9d9f926e7651b2f9df2cbf7b24af67dbbcb5b359e66127e9f70d285e1e72ac32fec7c5d12fa4e937d5765d219bf6ff2c926675639f33d63b4b96f4a272686f64517a2701ec1478dd7d0beced2dcc4c49c72de175ddb57538ece7d533a313131a532b973683b4d66ab7c59d85d934e5776bec9f72b4eeeebec0bedcbec2baeed6b29a815934fa7dccaf3bb7cce4fbe975dcad237e6ca92e93acd24f3959dee0cb7c924bb2ea1cf3f2af339356bd967b490ea8e693a4da5ebc6affc5c9d2e7485ceac734e73998ca150a5bbbe33ab8c97cb98ef9d65e99b0b6b39eb13eabe4fd9a97465d6292f4de8384d66f2098599ced9e964a1ee7285f95e76c98a8945a57d55a752704d5ad1709af0facaf2cac2cc64abdf34e165f27d425b2754f98cdb55a974e3bacb956de195d9ae2bdb325dd65dd96792292b5f67bb26dd7895d758b7692ad7d8c96c97efd3096567d6f964d374bacba453c9c64ee6cbcab1dbb27d7a4d13ea3a93cea7eb4cb2f072a99c63be975d2adb353131ea1c5858cd13139b6569544ccc7dffb49ae60b5db270cc2661799964c6719eb4aa646759fab6b2b32c8dba4ef97dbbcb33d3699a49f75d9f72729e5fe733995c977c2f2bff49a1685f81b19b9858600cd8f2d089cd16eacaecd39597701286e5e8fcc7649530fb7697a7a6d134db569974cacf1686a1cb3699649e692adba5727e97ec3c2f9fee0bc7669a72bc742a956fbc842a59b695cb349f4927fc643ae3e5ebb6ac33d992693e6739a94cca6b32996c5ba8eb8e692a57d98d5fa5bc949ff07209af7c2f1bedcb63c528e176994cbeaf9b6cdfb76d97ca242bcbaf92855d79853add9985571da609859932ec4c2a6765725e26d795a94483693e65766526a1cf64927561263c8b89c1796c9edbbedde53d12fda5f94c3267270c5d269f71ac9ca5d5a698d87396a1b39c9858f529269697b77a693299710bb7c9768e5ddda5a99c5959669d2bfc8499cf2574e57bd9a4527369aecfd519c749966de11596e1765a0d2cd35b9acca4537eb2b1729697cf64bb84b534df767db6eefa6ce3b59d59e79ca539b370fcc22d94955d386e934a2ccc65cbb2c93509b34a97659930ab7ce5f9a94cae2ff45d67f9b926d72b4da50b2b972f9c4cb2f19365dbd94a937d425b671256c24ba5fc42d9e59c989898d8cae6b12ccb2ca3ab3493af3cb349160a7d59e833c97493682acd986599ae9b5c42dd997ddf77161363e6b1cc277a4a338e59b74dcaf2ab9c67367697ee102da529bf4c28dbcac97676c2d0d8094d0ad1519a7192855ba813fa5c59a533f92697701eab3cd4509aedeabacae4f2e95c99f10a65a14f9af2bc64caf2eb7c2695ca3529ad46d9d0bee89e73e05cd28b76d294d71976be5066dc32633629cf4d9a4c68f29ddfe43b27573676e5964e49433369c2b0d385df64ec4cb6b3724ece626285b39f98189cc716ce7e49e50fbda4e984592774a984b6c955a97c9931dfcb4aa151e6dbd7da0a7e7d39937360219d9858f2a5fe0502f6c9d04a9ab0ab74ba73ab6c57e5aa642ee124cde553c97c3aa16bdc2e97f0daba27346213ca642a9def537edda4d219275b24cd16ba64a1ed0b653a65d975dfb54d1f61994b787d95701266beed32c9aeacfc3a67660b7559f965b24911da48135e5ba6cc74ceeccaaeb2fc428b345717ea3a9770bb84aece27bb420f9bcc182a3b6339865b662b2bdd9948f385ae4f1786db785d93cba72c0f69b6ac72c974b64ae79c5cdfd7290b6942e57809c74978755bf7996ce320cde7131acbb3cbb66ccb42974f2690e6bb7c32dda53cc331bc946119fed14cb24be533096de7f709cfcbf7f5d164df182a2bd9e5520985e5d67d7b3495cab69d95ebfbb249b65d42953c9a4c18ba6497f1d2957734636772f98426952b0cbb6efbd4d17c26e3e792a974c6cf363927e33647f319aff3ba8c9dd0394ebeb3cb1a365d38393b97cb7556ceecdcbe389ac9769d9771ec54b2af0b6dd995ef65d5bee8dabea8255e5699b7b26f777969fa46d339b772ebcab0bc7cddd63933994e6da3c9cacc978d97cae50c75c2f332aed16cd9a7fb74d975e93e994ef7a974976f7779789a4613daaeb0d28593f30c85be6d3b3fcece5609659fcf187eb2739caa19c7ef7385d9350965b22bd38d512967679c9457e61bbff1bc264f35e7f66d59a5eb94e3e4927565534d560985e125d3095d4293301bb38ecd95f99499cf997dce4ee7ec3e5d65a9a6ecb64fa532099593f2eaaeab529054b395a1ae735e673686dba7fb7cf68d6ac24f6592553eddb75d2aa1b0fbb2f18b829d4f9819cb32fbb67c2feb2edf50cd395e5717ca2e6736e9329db30bf711ce6bd3b985b40d54d3e98c9dcaf6e9ba4aa60b752e577709fdd4949f731cb749e63cbfab92853eebc4a2d2a8a766ec743ed997b964dff5753a9f5040c1a898d83c7ddfeef29edfa9b97459a5f38d99f2ec6c99f1d355726a3e5979e95c3a9d6bbc6499f0d3c9dcd46c675766caccd7856726bc849954bfa8201f1a0cadd68989bdb0a6e69b4c42994af9955f3839b3ca27dfcbb698a3c9e93c37adceb1ec3bad5293999a4ae5735de37576954ce62c33a9f0b44a4d7e6353192b93b0cc26e5a5f27d3a63372f3affbd6c61a0f4a96465a5e017dd975329f8c56ce7c7d47ca15078969dcb27bc9c65b67d95687fa91933e7a712fac2f2bcb62bcc425f4bcde7cb2edbe40c852e6168dbae6c1eb352d36d632833c96497d067ab84bec965da536ac6cf25bc746118aa5c9db052e94e6acace799dd7f9e9b6712caf4f36e693d49c9fca566695ee3b2b9f3133f9f2bd79d4be9665466a2e5737f9bab3ec32974a586605fda4d2bfa59c174ff7946ccbbe221e5293b98432673996ddd6e92695ec92faa89964b26bf2c9c66bcc84db58d9fe49a54e47cd765d95d0e4ea3e59e752d9326165a36652194393ca79752e619751f3852a974957c9ba2e93c96c996f7251f365c6d0e52b3359f87d2aa14bb8552a6aba4af68565259c5cceed73f9aed444cde713eac6eecabeccd59d95c9bcbc658ba8b9b6f2fb54c24ee6dace70cc2a97ecb44a3dd49459e52c27d927dcba71eb4259b635d464dba4ec3ea1b2ac7c9d33bcc68149b12fd484a14919eaca6beb74329fec1af3bd6c4ba829bfee724d3adbd8b964b2ef3a8df99e7952d96595d32a35c9d864593876c62f1caf4b66525e95839af1bc5ce7a7bb426567ec642adb17d47497b2eb74c6f3baba5068bc5c3f50b375ddf71927e136092bd978b9026a2e65770975ae2e2b2799adf2e934f1a7d9c2ccd8c96461e713bac26b52396dfdc0f11296e7f9294399ab1ccb7c2f0bb7ac605e15100af7619f337365ba32bc8465e8ccf7b2ee52d0edf934e3d829b72bd319cbd07856bac936b9f67b9aacd2552a9f4ae63bc7333c53abd338302f4f8e0399ec74aeaa31ab80d0e4875f6c26e77655269fabbb42994999f91703f6315ba59e969d59d68de1558693b0b2705a8dcecae91c8db2d17d9e662b4399309b4cb24ce6eac6ee3bff050ad2ad789ace9595e7e73bc330ec746358c9f7b2ca399685ab80d03bcd245486994c68ccae4c28eb7cc67f719ecb2eed349dd0f8097d996ff25dc2cf64bb9ac9719c9818155e2a365ba6d2d93a5f99e9745de59b84936c9de62b2f67787ea14ae6fc84dde56ac654bedde5d93d1d577e2e59385e3e59190a5de1394be8fb2ee324946561568663be974dbebd9c26547ecaaf92a994677765ce32cb64e33465f829cbec72e9329f50e5d35dfec169b6f3cc7ce5f8192b57277486d9374de5325e5726d37d5728bcc6efeba6c9742a6516dac65067f25d2a616620605fb24d93b942d9d8e94293f2fc5c2e67289b26749e5f78095db64e68eb7c99f29aa653293357a7bc3a9d4ae8121ac7526894edd9178de3c4c492d057d37c9df3fb8c59d7c98495cff975ae9fa6093fd7766e95cfe50bb7ce64db26d965fb894dd96d9dc9967d26e179e954b66f72f9769797e2d134ddf8d9c22eac54c64b263baf62627f7ff3534c6c9e9e69c2c976b994d935b97c3a936e32e67b59b92f6f0e2cacbe513331b1c2689c7cbbcb6bbe99e6caceefd385c2ebcaae6deb32f95e5659058456f832f1ec84a16e129a9c57a52b43dbe42af8649a2d136e9f4fe5f25dce4c1886420965b62fbab62ff61dd3846558866198959fadbcc672fcb21d5e4c13facead52293fe1797eb6cb42374c13ca8c5bf7f926e1d9192b95ca2598e60cb3f1fb2a57e713ba64b2c9a5f2bf349531949599acab6493c997653e99ac20b56fbaa76c7b2fcdb9752a5718babef3ac8c9d4eb6edc2949ff3db2ad9a7f37dc68473abf65c9aed12ea542ea1d076c94cc64c2893ef65951b7e4b737661f95d9f50d729afabcc4cf2bd6c0b6b69cef17275c6ca27cb7ce1565e59be97ad52e73c25cbf6599ab39c84df59b9c2f0ea6c9532ccf7b2797f82802bbbba846d72afef0c5e020a46c91e4b938563a6bc64c26c0c3be5950ab37bd13e3fe695e6fc3257369edf95f9ca2ff409e57bd9e8fc97179df3dc5669a509af4f27ccb2ae7209afccb85df2bdeccab69006e6fd09023297f00b2818add2b6ca37e9aeef0bbbcbd76d5f7655b64fe8f3b96c6328335e426798ef65a773950d4d4c4c9efb02a54f31316abbb2d36a2095e6ea7c93ef726e5726fb8c954b25dfcbcecfa894393fa37051b5254c0d948efd94a63b339f2dbcbe2bec64c272e1b40a539f533397fa9cc62e530a93d92a93eb9b5432972dbb6c93c965fbbacce7ca26e3a5bc74ba73cbbaeede4769c66ef27d995025d375ca4ba8bcaecbb7bbbc130fa5098d9fcf76293be1f775dba792e57b5935b9afb79d56033dfc93e63c33db647255c6f2ea3e63e5b2fa9c5738e1c4c4e43e695e951a952e976f777979efe45dbef09275956e9b74954c26734dbe33d49563a8528e63e7fab2abd3757e9326cc26d9e4ca649d32d48d57e5cca4f96c99f39c8432a1cfd639b7d0245debbedde585f8254d26ccc2cff55d26936f2c3f994c184e4c4c4c6c5a9d03944ed5aa5465fb655e4953094d425739b97c5b96e98442f7a27d6d3b03f6d125693ed75556b2b3cc4299f3bc7c57be97cd935659c4e632292fdb671c2bdd250c3be33ca7207565c5c4a27b7d54aa9953704e4c6c06eca31313131363d9b7bbbc77f2489a4e67d26559e7ccc64c37b954aecaa8f40f4d4c4c960399cbb7bb3cf98f34db67fb4c26a130f37dca30935df2bd2c605f95d9cabd91e6ca32e5797d63769de1f565ae807dd522cdd765ba4b987d6566fcbaed0a3d6ccaaccc745b97c97c6365dcc2f00b856ba94c224d661bcbee0c9d97ac93d9b6d0794833e95ce517fac2ccb985b2cc25cbf7b25ff22bf8354f05a9d2bfe797f315fcca835bbd17d284972cec7c329feeaa5c2e5f19caf7b27979d2ea07692ae116fa7c5ba812665b376e997c2f4b2d8c65177edd07d25c2a9df3ca544259b765ca3354fed1642e677985aeb0f3e9ba4b27fcfa683e93b2ccb632f465d9d975cece1e4d9929af331386ce4a66eb7c3a997c2f2bf827e88287121abbb23ccbc995094dcece67759ea3521733605fd56d55fc8ee6ec5c61e5caca2e13ca429dadcbe175345916ea6c67d9659973bc7cc62bdb9a7d8e66fc5cca4c175e57b985ae4e96dd8b4afff2a6d2b57dc3e63ccbcb270cbf4a28ac8c954ea6df3cb748321f475366db577ee5b86d6736669f4fbe975da7523e6a0bff8de60c33992db47532a1c9977542932bbb7c1baceb26d7b75d5d25ac5c57e5eac22c3be26bb062539e46d3e974e3a532c9645f39f9ca30131e31a3f984dbe4cc6457d79567b655ca6842dff94d4263189a8ce7249375c668c6d016ba6c994c59562a5f2654c9f7b2d499ef598d0101fba6d597655b11309aabab9ce5a592f92e6196856599ef650363c0e4175d88bff8296d5bdb17cda51bc7f152862e93c957998c5931313131a50a17cd2553765dd6c9c230f4652ed9d9afef6faec6693088c2a6b35d67a7cbae712bbbf333093b05a9844db865e7b97db2ecea3edfa42cc7301ba245738d9fcb6792193f6165d27dbeccf9d9be0d369f4aa8f36d675696a1cb77f9b2728b4229b54f9926d894974b99f95c9d4ee70abf2d33509a272d6459e65c8d6557596d2a67270c43932c737657a6d385f95ed6ed406dc6f3dccecf996595ce767e42937c2fcb4c2693cab1699365c62b3309753a99f1fc2697d1f849755916cdd6b92ea1acbccef3fbce4f2593ef65db166e9fd0695e921d0b6ccaaf5276e3a732764299cc765d4a01a7b3539954c6499b6d7219cbd0e71acf315329bb4c6a612ce7010a460b6339a9948b36a1b12c2b5f788edb16ba7ca14ebe974d9e01365d2534e9749db07366932beccad2681c75d1fe9aef2b3f93cfd875d956e942db27dfcb7ef06bbecc5519bb6e1b3f95ed33997cf2bdec5c158cf601ae68c2ed92c954325d56b9742ed979c9f7f635d7e70b75b2ef0b3fdff95d6199ef655b762a285dd90f7c4d6532f964aeb1127e26e1e77385f2bd6c612c77d86bb22dfbc2b2ac64b2b21cbb7f31b4f95cc6ce7966d977c92ad73829f3bdacdf3cb79052fb58107313eabeb0527e2a5717969570db4a01e526bc7cc62e936565260c65325b99ef65a382d43cbf796ef1aa3d9b4bf75d5de7727d3e9dabdcb24cbe97e5ab46a5801e9ccd5929bbc9f5994cb64a78e92a5de65adc5c57262bbfeb53c9543a97b15266aee9355fa7f29d9d2d74666527bc845b29b58f0591d764aed055a964cab15286952e1ccb524015cd18dac24fa7b375c6cad7f964a17c2fbbae6cde609f9cb7649b78cdf9995cdb778597c9e733f9649f7c2f5b9da9b0bc9467ea340e5c2edfeef2c8523461a7cccab09b64c2f1ea84955f7421bee6bd682c272636ef5103a555ea175ddbd7b3ef6f4e4c4c4c8c6597d32af5a3a0682ae17676dda4929de525cb5c97d059ee5dee093a67595905842603ef9a71ecbeb13319aff2fa64beca98ef65ab53a933a670825dd7e70a5d325b66bc7cb233ccb2d9395ba7bc6cdd670c6d6766bcba7c2f9b1c61c25d974a7639439fcf25749d6168cb8a28113b6767eb5c5d985d59e5f36d992c893a26db269fccb68559a8d2753a63be9795a57374c924d45d3e93afbbc24c99293365592a37f74cbaecd24dc231b47dd757b9aead9c5cb36de5776699b133998c933053e67bd9bf40c0bebce73c384f633e2accbe2496d0249465ceefbabeccd8c954b62d5d5c53f93a99afdcc26e0b9d5de8f2c9477dd925dcf21943d7a5d36597edeb6ce1e83c38e74901a5d340b69d7b6b325d964dae6bf219c74c794e3af95e36503a95d9d5adc99cd7956d9fca769d9fc9a5cce47ba5d524dbd654265966bcb6492553e92add6554faf7cca3b62c3ba2d9be4b16fab2adcb6c61a8337ef2bd2ca0740a75bfe8427cc1826262cf7910503a5513133b131363bedde5b910d14ccaeebc9ce33986cab1b28d61be9785cfd8e0e4ba3297f3f2c984b6ed9399141313134b2aa7d5c0c0b5b67dd71666b2ebba6499ca95655f1776cab133292ba1f19b9467e52c857eadade05f20605fab04148c26ab80d016219af2cc74c2cee5fc8495f39a7cce7c2f5b259ca3ee175d882f3131b1e95ced9ba37d6d62625331b1ea137ebbcb1b546bba30fb8c5fa8ec32d9e713863aa15029b3add99c593839c3ccd785d9e4fa6ca1694d26bc3e9d4be7fab2b1dc26d7e4920dada95c9fcc9639b74b670c7d2e9f32dfcbe62559965db2cf79300977d69c93c924bb9463e8f3d92695f1caf7b28554aaf44fc92e9764664d9865b6f1ea9cd764cb26e7d6c9f7b2792dbbac0242934b10cd3639b32d3386d7799e9f32abe47bd9ea1406f1a1999ce1a5bc54b6cf24ab5c63e6cbf7b2792f5cd67457786d5937392f9f4a25bcca7c2f0bb3af8726ebb62edb266578863ee7b78d5bb2e61b27619829b350a5129a7c3a67be979d56a91d9af00cc3cf96c9b64f1676aeedcbac1b6bb62ebcbaf20aaff3cbce4e65cb6cba3133f99c95ced6653a63a6d2c9f7b27c54c1c0bca914ba64dbe0e6dc2ad765f27d9d4a799ee118caf7b2d3be698b959dab7d531326aa82805f6a725fabf2ed2ecfc40b335f97b9b2ebab5cd75509afcc65fc26d9a4cbbeadebce4fa553c9f7bae8a30a7ed1b57de545fbf202a55379d94eab81c9792a7dba6f777974cb26bbb2ca179a9c97d0e5d319bf2edfcb4ee7a8b2e92a5d59a9846326738dd7559e676a9e3c13ee29d92fba10737262628052391a4fc55ce6db5ddeef709bcff6392fddd6e954c6b2fc7442f95ed665269b505719c350269c9c93cb151ab77c2f4b8d020afe11b2ecb26dbe32fc84baeeab7cd77976be4fbe97750a529949b6c9caf2d2f93a6196193f992f14e67bd96a9f136ed7a63bbb2fb49597b1ecba4bd89df95e76a96ca973cc64e12a544a75a9739c7cbbcb2be3d07c5bb785caeb2acbf0ba26932edfcb5601a1c92fba105f62625540a898d83c953ea9f1d7da0a7e8989c9f11c7fd1b57de5c1eedb5d9e9b5893e94ccaeebb4293ceb54dc22ccbf7b279723496ca6c3b7dbfe8427c997da17dd17dd1b57d3de7122d6239e6893026cb8457985ddbd785936e927593ad18e0e0df9f5833b974ce4ce8fa94a16c9229cf50be976d219deeb1ecab7cbbcb1bb121ef49159ce6e5756960e7c1f92f8f30068c90614ffa97b717eddbdb93fe6148e6fdf5f9ad172c58b850e102d8862f0b14324c582560f0f28a30010220840753693576e0206fa96c3334b0d04565c0a0957671655820c7f3b44b018404d5798fc1164b7580ec0304945ed06a276435c07280c05d3e3840286d518560c02f7e3e67bf024b65eb45a065f3dad92ff61ac04eb862616855f08100366fe6cb7310c002809682551e5815a0f4f9ee362f2f8ff55b2adbc0e74cdda0f32e3c9b9737e7e55509670aee921253f4e867c0385a25b6288903022e28f18e24b43333101fca3c90751863c2ef52e9265b76e52016b601c3850a605f142648f08a00e18183065d0cb8162870afca8a0ad2e3514948a2a69da75b0d506f1823bb2e0363d6c26071d1d8c860c5621d0cd4c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7548f8f8f8feeeeeeee8f8f8f8f8b7fee6cd1e46ce01e1f1f1fdddd1f1f1f1f1f1f1f1fd94c72f48265c1131c9fd2e49173ea0946e2d3047168ac2f6e2f00c4bcc3caf03ce359e0eef2a88db348936a2bd243c69df898b4fd9851f00708295d0210ab27a4498a7355ed1f43e270bf0d88be04ba3bba0b4e0dff1413d13ffa10781d3baafc0cad452e6946a317a1d27b4f82fc68061406381f1d470ea48a9227e374769f4b511e4c13937d280ae217a8e26d26a7d61e8ba4a40f61b4e4839c8aea2019fefc825a8a2d4685d0af9e78e93f83da8d7c9af22718d4b997137c7acd2730de8668cd675a61e0e1bc2dea196f8a38900f79bad195fa5a06e907b88af101d404d0a9b4f8f8202f9336a2169a7cdd9145fb0c401fc5804e6b9863f41804e4f01d8b87be2bc7042fe002d05f1959e0469858b91e76c6ebf1b0e44e2f50d74e52f82650551fdb62c681603ae2a6eef6fc88c4340faca0e119e11c7e0eb4b5bf62e4a2c1d40a7da84716f619ece3035d001b71cf941366d5783a92463d1c0913348d0c8bbe8fd51d971046c0eb0d52b1656ce179417d146c1fa9107c13e6a39b8d187f8c43246f8269f016beef2378c1f3443c197a4855d45711264d3b6d0078345ab4276500fe8e3c1adb090dd901512ab3612f841e1393f1194482e4394299d14ace0adf0590475e88ccfc4a1e69d1599e1cfe928cc6578c52cab72ecd692f451bbc85974f7ad1400c873184f2222a564734f54f2433c505d7083e2049ac9f42b6f90e16f038830d633f8686a12b51f4c7aba3dc5e4f852dba9520e6b9a45af4e10d3c0ec767f918ccb69c4e8cedffc011f4839a68f22eb7a723ba60fe46e5cd1fd8524a2321b3c60395b8f1640c817c1b2c61de04f6f232434dfa0322a28b3aa5f7196c187a1d8652bb4c8c197703634f130649e172141d9bcee7bcc709e62f377972bd30963c04a31ab7b220b9b7a25eaee805950f9523ccdf34a669a51905873036eaa3d09d39982ced56d6249f2fca12dd4903134f7248cca75314dd13a2b677c6fd782c475f76dbd991afd267cefb29c4c3b7442ae182756dbe2dae82970282f52318dbf82637b8df246660c36d59f4108e18fc14a0f92b9d803f4d16040f210985cff270ff7bad5fadc07a1d69edb3a88bf47f1990ae09717d36cd4c9a072745374300eddd48d2f468277e3893bd280f612ee14506157a5a8d78bbd8c2f748506e1e83901ade062ad1e574b92ea4c7d5063509868bf5b0da8cd4297ec04c70832052e611caaa9b8a8711df56c5d157696bddc3999c5dd686c6df447a3f076cd47f756af33f3a18fa073769cfe001a9d71029cafba9d47e15c434fb8b16ec2f48e5d14212097a24174bdf3983ba1a2118df236dd1b3c400f56a6aa9fcce1b86fdb846cf8120befb6e9bf82f2e80be8e2be6f39e193fc34f8e6f2582db90d04cfa4c5bd6341b28e9d9980174bf059b5cd3a3215a8be0296fb283c86bd959f4644ce6680c4156775447c3cf792be0560ca23ca42a6a5eabc0937d64109a87851af42b73b69e00830bfacbd28fcfdb509d88c5e69d4ce8e26495e4789c463d3a5359f2916390e80cd5364f264669c75833c8c718233c2439405e07a1d7ae0373732e9710355698343f070e16dda7400d7f468af4a5d0c0703d61c6f61a18ab67a9229e11aad8119f5c1b55863c2f47c6909d490f06575ae8f48dd2dcfc102376bacccebd09f6612bf90373292933dff244f4317a167ca866826ffeb9d16c2cdcbc4b05a52bb0597431af1eba31cbcac7383ebeb630e467bc1b8f224e888fa154c0ff48217a4a65145c0e33f1c1daf0790d1d0abe4563b309c9c17232779ff4073188bc51b07d091c38fd5888151b4587464f65d4a3c76f1dfe00a9329fb426ca4f21a3461b6a63e614380dea403e0afd509ac2afc097c45960a1a4351cf9431121db143e40f80b9274e80d447a78861b835e0adce86dd390a68446882b88d3635f69f6d5beb01e52181207d065c4db0580e4678ed2f69e18187fc1d5b82016b75766d0b28518d1e48d166dd898e806f99ae3129da46be352fe04f01041c278fca8833fd0cae185b83aec4de5e15f85a8342028aa2f2168e6a344a17918879074991e261e879ea06f075df03a557af61d9b3b9732a8e2a998ef47f09a7ea6c594afc8b0ee3fbb2eef8bc0c56dc02c1cc918acf724a5e91542d9d155a206ffcba5014e0a49924f1622fe824d15be81a40adbc0853bcf94f1e7a7a61a9e112a3cdd49021647347cd46e715ddcc2b382f3e950e84bc270f14408563e81ce87d77810df6d0c4ef75082f14889987ccb8f1c7fe70d8a3e5376c635a8781d00d1140fa488d595942b1e4cab4cdf0955f662dcaaff61d4a08fa509e090ea163fd1a24e3a4ad5d407aa0dfa4d9628fd913808f48b37e2322ae5f02fa0063c5fde95f7b9f3e3e91c83b80d0b00be1010d81f6eb93f8289be1b1b9cbc0c88a17f1293c70961aaa2b194d2f33472106a4fad299f5688a1ab5d765f236c8736e2e1f13d01801703888d5f30206a43c9e0744667dc4da9028dff7280d1cfec91e003e650f1222be23cb08cc65379b2f01c8c087ddc27e2c71cad7e9a26cb3bba51f18d1f903c03a7a0ee90c7740f3e2879528315efb18384f3b8b2c20b298969344be08f92c6e8092d8e7a3c84b2b8a03104effa08dc93da80f80109b1de4be5e595b479e29ab400f93f21486d405d403cd3853719098e9e0460fc7fdc0eb9d693049bcc15169f42c8e86a454afd27486c7b93a1880fb026d013909af310b238d1aee7c54f0203f285d6517fc7658f77095ae030ec323d29292bbc500e9c777ab1f0c345411f2472e7e12cdad987e4a2eec0cdfe44680b77ea61d8600a167d8e89a5f94c88f05e185e6c36c8479fa64911dda9098c9714688ff7f834c00bd5bcfe8639e4652c5ffc29322b3ccf22c19df594e27fc470f8282a32ef9a81d2d52d440fc446890fd601f0155a4cf00860c91f2224c0dfa14ae26e73bab411bf189e94f93c8743500e640bd69dd8bc791d77ca2f6e18e113da006e1c91d47c9b0057768b4d8e9cd25f04972b30c41dc3acbc91c5fba0c212fa8e130c4e630400e77418f656da08d141387c7d1c33547e754ce1a388043adc9317afc7d0014d08678c03a004613b2729f218c6de53ffacf05471caddc08214cf9084aaa5671fbeebc48967e129d07701e4f86c925ddc4d1371170941c5f32c8af91475671ececfcdb17658f81c869f7e528d1e3e6708a7d7c8017a451f4e36832b433f0010f5dfe0367c52aaf85554980e7de4e165305af2073634f99f235e3e8c8038ae46129b77257e3a6bc59a33b872f24ce6c5a74b42a88b5c52fcaa1e25b4061d8eefa7c4c706934482a7b821f80d40351d451501ff12b8c5f5d07d6d974c99b722d1e51d78cdf1218956fc881f7ace2853759f1c361ec09b612f72a0f20fecc4b4a3d59cdf03e6ed875458f18a4544b9162a87dcecca8c86ab91c01dd869b17728c0e129c8083e92366d1caa03c8930d78f48ab8cc741228505eb0419c464207e0190fd1e90010383d8507859fe08c0867032395cbc044e563778486b078c107f070a35d4880ce5649852e92e3c8d3e9c31d67f5f22d9d4d4f15c2e2cbb490d84b90cc1ffc4ba3c744cae1d10c78fc8524497f2678a6133d95720a4bc6fed1c9c7f14660b93709f004229979261cc2f8842d4f5e8aa13fe77248ca866a72e162e414ea387106b89a4075cf63c2df43f1aad378063f7d1549a27eaf47996b6541da7055f0477501f16c762ab8045379ba439dbc1784dcfa30364affa882ee9b4d967faa4a214ddd377cc99fe413f1a4c3377d71b11d9d017c298c2ce8c61972cea99193f6e2c6d3e710aca40169f4f0c5301cef56087344a9989e4468cebb48b1f308817b5f05e267031105e125143973233d347d800e558e486990a3913ada18eababc0b10175a8d233a9eaa6c7053b9c29cc357aad77336754e8aa49e4c5fa77f9228e6f5b0307a3c73a6b6242b2e5c8cdd98472a62f56f1c553d83ea9b2f72c0b081504273236c9d740d1f609fa790eb2d05c2ff5204b4452d177fede2f3224efd399a0d4a171213f5837a34da9e1cb8137844612f86b0f13a6e04715161d1cf90248b8e1280924f0131ea3d213a7e8ab2d4bba013f66110f2e833321b5ee1ad4ecff061c5ad324cd207e44cb80a4450361f17c2ffd0baba7ae9cffff852b01f04ee475ad0a3cf2ca0e1405c08700e1f2cbcdb9e258f47419607b123f338480c1cc129177b4b8914fff232f11ba8e4f932751cdd0b0bc64b3190c5a1299ab8922f033e4318c1ef4166c9c70892e0d7a82dd25132559ea5534a17b1f0e6a6b4f8f0186008ff920738ed6b82efc788e72ab0ae3c8d19c327da10e543ad01fcbe3eaf4faaf2453b6221e47dda1e780c17d2bb9d80ecc5050d9e9002af77806ce148b25268ba176c0f84eec276a7d0bd1b24921ed056f42c8334db4f93312e620789fd4c18bd8bb7197f224720ffdde78dd099fd87228174054351fe07a23b9fc161d45f7d2df12b8c72cf15484fe30905c30d49987a2b5919dccf1f4c3ea748c83301e2c4376d18f3900c08be2119ef6f297bf48ad678f1449ecaec297a44de041e473ea92cd1f714c5a6a445f872cd2ddec94802dfa204ed1d5350fa249f0b9f4a0e228f43c6884b7924e62f34137c120a91be4ea7169e1548c22f32936597d179f48dc0dc38a5401abec905cf8b2df8f36e4910fd4fdb06577ca4ff9de1f822fc7cd8807d5b3cb0818b97a4e2d00fb9d2b38314187ba710e53f8461f1b7c43d1b14080c4e28cc9d9704278fc3415bec40056d5ea5ab8156d158e6e500657f6835c609cc7f3e58a3ce6fd138b417d838f537fe9ced1d1b02fd0ebda4b6506a8fbb31a2c31d20ea390c4c18fe8d20b04775e66d1f22f3e09dc47835125ee045b64cda8f8b777ea5e6e9796860e3cd0731b6a31c823e9158904d6d23e0bb7c22b3974c09fc1fcd27cec3d0eac92471b4f71e4677c1c3f9479254f0497aa89ed181355ff782d23b90c7fc9448371cc016f44bf880f80dab009e64d281efcbd0e77e64825eecd2265790167d519b60b81a2d389b4ad48e2b4e9dfb0aa8020f44c899f6a0e6eee9d212f5a115775e4c140b8e056fc790f7f4ba4c5fbcd985c136d381f4001794ff2522eb072141743b2d9c57fad3664f3b21f9005589b6110667de46c175167f261e8e1d0deee5aef18fb0993e9f2f699c6a0095bb66633e54a0223a4597afe704488b9b82b39fb003a0fb3863c8c9b4d06c2715389e640f9957b9dae46c1c08d07308f4de4f8d146fc54285d3184bf31374716f4a8bd013e0b0e6bb74e5e90f98d01ec40a43db4d9e16eea30da5973aa1f25b529a8dc44c936f03f3e7814c1d3901071cb6dc9920cfc370d01fca2b7b418586f40bb4508ed5a3d246400a84ff20c3a7cb48a2f3b52f40b61407dc83e1cbf4d1091e3e019f020fd608cd8dc039d9360cc3bc0c4219368e30b2570decf05402f6b8544a2a8de7efca0322054a675161e68b1c176c1d97723c803cefcf4d51f499be5278804c509783fab999e67f1a17e0ee41c1cb1bc9d3403f295972367f56ec3f4407cf05058b6fc2c4f8238ed8f90069966f8a44c5497118f17f9b0cb71b38c2474185c5c75812eaffb4f9b1ff2af8c7183bf3837ea6dc009237cffb217b8821507c0600106fc9019ead639191ebf1a44547e273e1803502bcdd9f0ccf0608d34bd04be53116b079a13989bf71c8a39356a1dcdda148eb3003ead79ce9ed4d775aded3568f9daac0daff707366a7a944e7f954856c228553fc52111ede650ad02d5881bd1b221f3c540949ce61480957c167ccd738faf08b4d289ac4571eaf9bbbec2f26b0bc6c02dcd6120a38140a72bbba22f1275101a5b5ee3cfc20111b7492423dff66c1fa61ce7078b22e41fea9d4e20b7181e23b392678023cd2f4010d8d1f8909751f2145f65860e76e5104c43f17d9b4a18c447f269392bde9ece97152535fd7f6e2cbb4aef5901af234d4b8fc29ba137e430b0e3f84e4c23f792368cf4963e33d19cab4039888a87450cc859116c4942106004090754303802401e312004048281c8c05e3e168481636ed031400044e8080c042994ba4d134c67110841031c4000380210418409099235b056700f75368ca0f3fa9c6e5252b0d2a9572127ebf967022fe21c25aa118d097ba678afdc727aa2fc0c4eac6e8ebe8d19247f1e0eef2fd252aab891c2881f9444857e5d4427c6f83b32115f43bb99071208d40063e4ce0dc323fe6d054b4bd558af163fc3364507347bc705a5a3c25a322013d7927a0556c9cbebf1b910ce3a2a5423b28604706916eeb14109b87892bb05d60804144fe24de39b96d5f63e5e158031fcd7a4f87671a650208cde085df6bf42fda5156bcab84db86a047558fa75a5dc6bc7654be54abaf8267e4ba731cf030f7cd8c74e313bfb71f2a1c99205c61a853f71778f210a2523db1a10e35735cf89d23ea40760466baf2ce54fcee8f14ca14a1875569b53a36b7bc78b9e4bde653b590d7e9c68a9cc0660cfa1acd5b29c6bbe18590317afe76ab96c10de01c34ebd509244120c0c0d6f779971e6a045fcfdd2f66d9e6e6526d4eb1f1276f9277949057d40c78d8af40a57d4b5723a704d1fdd626f8a2b3ffe9b6bc3f2671d40fb8f4f57c86e63f39a49e1a8979eeaa9675eac00fa58ab56303505eeb6a6613cca02b34f0f1ad149d6fd1c58f5ae2fac7b9ee4783a7b662f9b9da755c29fdd550601aebdf39bdbe024eb6f4a78a745b28d62cce693f8fc268ce93dbe0f927427b1f9bd947bed243b8db8fdc93ab6d084574f5734b13438960354650215d6c4508d2838c27c4966ca39779027f5b50b65209bfae9baf19201454c4984537deca969b238b7f024d7dc2f88a8bf817556bebaf740e1e56e5cea08d8644bdd3fbaf91e16dca9f61c871bddef9061cdb2737e6d7702b6e0cfdf07c009ddf80d6b77e3385c1f5cd6b20903cd0828d9bd8a5d58758948b9b7499042d23968f361b0c40d1e40d83434f9c1131b5c415e30427e707123e4b8cbea6e83bbb3a20708a3c2955a40bc06181d684fca3235efb968bffeba93cbf934ce4b2e7900d1bb9e1dd2e4db60dec7b8ca16b3a6c598bfb8cf02fdac987d790b646f1365bccd691cc1e7d171cd03a724c048598c520386c6c8e9b753443f0b03b9bb8c4c9307ac78de37d3a86709e1e6ea5a48b2ec78c41c5008f9e868cbcbfc3ad677f834ddbb31706fdaa10d7490172c0e64c0fbceb47a79eb82a43c75984a2a88e4b8d215a975a41b2a2bc792ae23d2acccc2f7c27c19598b07224fd1676f2ab3c21bc5286adaa646082621b1d0c7dbcea9f1f7c184ef797d0f653243cce07870605467e98cebc6bd118b65da1e3384d6770be1160f7547a12d699eeb89fb2068e26046b9f68f65c0181d684beb717f87b853e185d3d2559f2f70e362bce793b6e686b524b267183d8e41451f04831f35a06aba2964b41d4a3c2b3be855d756b45cdaf610139e03e9d131990cd8b596445dd37e88b19f337fd7d9424b4ce110c17fc2800fa5f4d2f7101810208f9687a3125053dc4662294963a021a4150504f6a1452d9908f43404cc82c0096b67c529c076e250812ea2b6e64a7a0da27d92261c0c02a4a135f0732dff58a980863c8ed23a00b0140d6bf0becb1d19914a5c6c602ca9b14f399a70b94c38de06a33b35ee5b83011331ffff775876c2a374f092918f3c19a278f9ec0c54ac86bdf8b18522368f8e4355223e660bf5912cd5610c00977553466e9765fa85f8a6d207708921a865c868b89ec097d9cd1de9f8255f01cce1a70474f6b15d271751e25f654277edfca1869943776eb94e20ff066d3f06b83966dffe6d94b9282849283d86351a80cfddbbb8e68ada57174eb0e662151f502161fc5bea76ffcd5fcf6c75117d9e3e76620c43ae42934a5950e1ef129191a930d5e36f9f18b12de5fa48b54980d9dd351abc9386b45b16a705a1ac427844b1cded3471e1703ec99195a357797479ed0d4525552d69209fd6197341ba8abdc74f4f03f5d87dcaa93403793686ccd60722cd2a9a5378be4fcb0142e2769844c516117ca66c61dbcba59c3dadf8d1e21d1644548ada602a1ad728695f9d54a83a53c04ef37c5ad552b268ae43a8020104c7802fb179ba886b170b562616549a9e6efc9044ec4e3062f40901253bbf4c3a55a3a901c0c9b64ea336c7160471caa4c11ed470abf9d6013b1c56cf31ae91837634d5ae32b37a498a963a9853b04484af70550c6b60e47bcffe1c5dc3c429ef8abcba494c5d1d55a5b7bd27786f625a97d85d4eec2c03a486957e13ede5978a5b41553f9930773004443caf03a35a514e22eb9954ac93478ab4c43eeeaf512733287bd56e01999ca23ab0d3984ab91f17f7f1fbe9d9eee6329e23cb4c388d11ad598f268a1b62638a71bcce7d075d9c0d1f3c5defd4ba6f9fb8147b88534683558c43e96e49210b74e928684a25433b66b0cf4a69479919a5ff0bc350e3606444ed449cabaeb784a895e5eea3fde504df1208d32e169d357e71447d456d1ce53563d0c22c815a66e236e6aac6cd08c38e90b518b29321f7a411ee2a3408969009210940f38f5756c10a7c87144b3005da7c20ba8173bfc87bc200650279da1c73fe1b4d2584c636a527090a3e0f11fe2e333b29a28043fc21f7c085f86507116a9051bd34819f728b556f34d8e778208b0e3639b0f7b04e12dc987df85ff665b2a789d53855f2644d07c25ee904ef9982fe695a43a4b0118122126a5dbee010a11c20150f0d05e7a4b363a8f98485af380d7d78f0d88101620324075a5f3c758c2a0e15e7746992c85e940b34056d38c5025d1f09518ed448f266699fae4c7c38b8fdb34371cffd39c4ca815aaa78abcd96a6e0dedb7a5812aaa7c29859a934537f5100451e1c0bcf41fb8b210c4ab6f769b8da8e2c9c9f643fc93a5536e29a366e0c60148c16fd334b21d2d56c146d102fcc1d144e2d44342c4e0efd50def43179efb43bb0b47eb5d9818446a8f4f48eb4fef06650ed7157a6e409f1138836122cb36e979c008837e4e0aa8b9c71f299506152acd32c7f4f636ae39440ac20cb94fbfa6b4a50052283e651fbd687cc5b76888fe226c65362d3dac6fd173b4bf85f4e00bead9eb84a2929003739529be5173a5df65c184086c3e5375292138fd12cb00a5b926c9be1bfd1c0831d493f66db0c4d6c050e1aecc4d3937cdf285713d88e62308e0b429a2525076943577a3eef511ce89b4aa4f745c929d085a10bac4a35bdc6835b973682577728f5c6974a4c5cd383b21e70b8c8c2c076ea3ab2ac996b29150b3d5b30a1b3909fe906cc37237dee20f85c98138426246833954cb6236033df533012b558b75b06e0f615f1a63d46854525dba7aaa292dfb8a96705fcbcd9d24ad7a01d137708f1d226702b98bdbb08c19ade0fda4e52529d54a711017fdda315698144304a0189fe36975f3c5aefb87fec83614dc53cfbaeecd18e54a127f532446b1a44e7f9754b128e921570711ecd06527ed6af9569d2f8de0230fe58e153557b3fd9668a3fb16808d4cbc424987d5e931c5b381ba9e92e59220c3fad18065a2bcd1bec104c529c423c551e1852a7e05045417c8a30afc5dd2d41494d3cc609e5d39f1bc893b9aa56aed6c2794a981e40ef8b3e861c7511249b5516392443c1954b818ea7927059105278433f2e4c715145910fbd1773e3b321ef4b98a3568fc6fca1f8504391ff033635b9c2b78599233105866858a0cdc6c0fa7cefb2fe95d9d6c5cc1b32c3f5449e2377fc12cc43321d2d53186a8c23d4997ef7174e25f735d0df7bbf74f81945a5eac76bd66ec815b5acb12ce328cb275a2bc82d85595d11741ce3f6938e8fe1cb35b33d3dc5ac3fdcc18078262953e39558145ccd1008423707085111cc6f789d3d82bc83b5cd33ed9f53de2529b3c02a45ab8cbd40b4868c732458a3c7558bc2f00d0819800e2b4acd753a5fd00a06e0ac0ecff5d2d5bd464f227450658ff0a4ec7b068f069661f40694c981d09855021116e0b8c562c51ae6d30e4390a472ae18f4a1d31007d36684bf337a792a1c94978b0f2f845c2d66f9f6b09d73afe34ad911d7181ca146098064188f52f18deaf4ef302a22f78ede3ff5c02ef9b87f033bfe1024a0ca548d80a67ae0644251126c7b8c34384b1f8af717948d4cbe29a8ddff1ba7b2a89b994f3f71aa2343780f0d43aed246271c9f1fabf0f6d6b5696364261fc0bf5202dee7c6133b44f294802a6e6a104c9a3cbbda52ad4b86aaf688559d1b7ca698670d86affd38cc6c9a2aab4e9f1139e359dfe2b6778908823eb1029d647362499318bcef73886154e0132296df54424746f49ff07d70b33e9d4443ad8760e76931ba68ea509eac3b1d7533eb0e426a5d3c5cd041b30a0be55bee760466cfc8aa0ba4b84d0d4168f411b3891e3153345f8ce68d7e35d8472f741f6b8b41e1d7226498be3e8489c9817d78191d3b3061163e6a32c55564acc82b9b0ce4e6ac8eef11dee20e4dae6406a08b49c984ad28168d476bacbbbc46b0a8bf2eda942bc1e2c63ed7316fdd69d9d99ede895cf3415f00b4ca6fb3ae27b3bdf71c027937e6405a0510b44fe0502d0bb2cd0509f8cfbaf837787ed1d7fd7b3b53036fbdc766faa5bd57470e41647b379e0ebe22e8c4836dc47f1d5ad079571bceac9e9dd8d10d4848bdaa589317066bd53f8c5e55265d091618fb255b5e1cdaa5f061272dbc17b9317bb08472ee0c03a94c400e3ddce296b4ec76440c817b1f78df0827697398e54f7117464a5251731e8a3cd653230ae9355fc72fa61f3ca81ee8aa3b89943ba41718b6270d71a047336693e9553aa820a990d39d4f65ea86e158451b009974391ee1a2ddbc2a424c9938fe4682476606656de266d11b45f399be487105ffb7d03ee08285dd7361042344cbb1d6692fb1006d7ae27b18d01dce7495d741212b94c4677fa8c10d3c864a7916e9cdc713130399c49162801e1c55bd26bab05802002a50118a328568cfdd02f030fe037db507e9c2a2812eb311edbe1ac407dde3bebc4edfd824784e6b49bc87a35b1a131f1884e510eac208297e6f23eecffe28ea61480ba8940891ef9cb0b53d440ec6675f26e76eed8ff61aaa4ac592fc1547fdacce8d1f3a7ec29601febc0cf2ca1b582deb00c5271a06b86278dd0a16f8cc9bfc92cab8ef4924d49e53782dd94bf64586a4d655cc827ddfb0e234784c2e617e15ea9ab38357f6e714e9906a0f91be0b8544b1b15a76ff7569f64ee324d06fe5e09e65ba202a7be0064b1e36fa534419375f0067e068ec2ceacb08e01569b9f16a53fbb2646e3025163498aaf196952937ba294f834a706d52cec77f15c451a3ce45cd08730303f64a7c8f9d2aa7244c22dfb6e5a3431d44e4af6f4ec591990cc2dc4e96944c440a0111e25a5c00ad0a14b773c83521e86fe316bd9dc29b3f86aa602bc8826bf7b923ebe2987b4b2ef4ce2ba997777e3a4024e7f3747c2da7b80362f17df49b38b5b507e88db754048359894674ba0a15652f03449587cbed4b194f3d337db4ec3142b3621d89f158df71e30eafb395dd40dcc416e8363be4bddc0ab384165833f102e0c4fa9e9e83901b2ff83d84ec125733a77f94e91300fb633ea3c9f9dada10f00647af963d32e696eb19d40a1af0196388e9271716461fca6614f9856ea00fd98a6ce606c436cfe5271d87813940c316d1faf70fe717ee1b5278dc27399b83d36238e209886924cc093b57c62fcceef85e4e0562094e0ba17eac8afa83c8f622d9af27695575d30287e7b21f6e2f607087924687a4fbe020329cdd07d835df373cfc4f3315d0048a8c7349b8620883158804fe718d7d6bbbbc08481bf49ab3af8d5a84e5b32c852cd1b521758e4812265bd2267eaba6fde12ce21e2a3d433e994e7c5bb911898cd83ce4c197f64e037128c1066f93adecd1effa7b1226f79fe0d409b0afa9cdf0dec5ad67f13dd9b1696accedbc43f94ebe4c75cc12831e2a21df5ed70b8826abfeae1de660fc0982e5dd959163041a9bb0ab39fe294347eeee56d844e6069fc38a481751d25e402a34c7f6d8b6aeeeed0b9a79d4466d39bb65517d9fddee606910d3266f9a32da5af3d79e0ff1fc9d874e1c00d87659621710a0e3bc690d8a34e247afae7976eb1d9244c4f994cceb71a2a29668d7732b3ea44ac5f9a938189e4d4a86d9c51fe2152d21eba198b5d84f92e650914313ca0f70fd48f0038c4ff7e82d58917927434553801a8c4a4173613ed77d04acb02c2f1410f36eedaf881bb6f42892743c8feaa714dde50ef5057280a89c08f8e137813fad640e03e1791aec93f0157f2adb43fb2f9c2240458d58c90179c256d0bec94879b02e89c6b1b3eab7b86324864217385bd8d8180acc16d03aa1bf6a54cf07b02cc9fbf21175732e958a6ef5172e097f99da8d4f4486067f07a08fca2ade20defdcd3e0844fed241987e32e529294bfc75fa7c10d1e10a9007c37416b4766f58988ff3e049466b06088f204867a0f719b157cc4bf841d0bac7891e94ba168e3dfe3bead167aed1057bb0f5b50fe83c82833f58137ddb0290d4fb30792a87202b510c4efa7cce450dc98cf09189118f63e793738bb583ebbe66e2052fda7d3f442fe33b343d4f7bb76c84fb99e0db2906f7c3c5ef0b8170f9743955dd9f13b38d801beef4078d516c99ca2f12e8517ee65f79ba0ebe8f6469850cc95cc61dae743fb9da3ae3be957ece1fcc88c4b3e1ddac3851ec5db23332317bba94d71bbf96189d8d7489ec6b6818f4cc2a4fb7d692eff9df12f86a970b7c4b7aebe97d35739e0a99a196adbe7fee83e33e66aff01e178f6845c9b547957f0521fe6bc44c2f659f7b7b0339bc524a6e9fcaf48cff52ae48dde04b8ed11b092620202c243a8602b6c0a2ce323670db8ff49ef8c411289d0adf6f3e051edde847be34c59bf092b5ff2980b17a7e5ee3a5108c6da72ce8ca8116f9d6ceca3d0c123120ade55bdb6ef506db44651f6063a2fc20a3e3920515d6e73ab34812e51d1c9b32b5b7cc5cd33106e44b222e16b78d663d3f221956b2afa3fb44e4251233baa77ec1edd41ace337b79145ea88a8616946fb2a0448d7c1c600a5b0628094494029b49aa8f12d31cab987bbab2ea8552cedd9056a6515bbe5b44a41172439f5336997d14baf68b5e7dd1e8e35ec7c0a27f21dfee8bd90863e85f4ee67f427d649ff3c06afb0fa77800bc1136882c6249827ff2e95ce3dc9557477ae0e52c34b585db35ee4d5a9cdd3a9807418f6e09d96310dbb4f57cff88a1e20b2f3931086dfa2feb071e259a511a851f47285f01b24fd9b2d92df52a5ec9c69824d9f63318ecc17d5f8b78e7e38a4804a0f33d5285563a4ba5e088e62735f649c6d9ff03c9468f61fbaa2b0d86d2fffa535fe139e33f5ca9733f67d8d3ffa04ee157ef0039fc2b5f2a424783c4560686f58ee5c14d04691ca10b6cf767f80bc597996aa9d064790542b85dd96ab6794e416f902f45d92d49f202b5c528a27acdfd5fd0ce7bfca6febb3d1c70f9bb8b38846bc02bc1be17eb7fc87bfb9560d1759900cb765784d4ba81c1eb979826f1642d83b1088c7484a14e5be9e62d3d2a297a4077cd7f03e024ffeaf2f765d49fc89fe55bc632b0bf6742cc423aa09227accfcff82f9f3090039be185ad1f91394d1753380b8a170b2e757949f83b523a0c640f176d87c2487332562afe3e7e813555d5f6b389fa1da1d67c5be484e26d31d519060e25b0c4111ad9c0ed9f0bf9ca403bffca56b7e6bdd45ad9d89395cba4ce876bf98b167cf6f9bfa56f3141fe30ce9999f2b24e40ce7cba432d11b79aeccd23b4436a9f3b84e90cfeec323d86fe3cf3d6a451a7936c9f725a3c21884cceaf4f4099d5d134f4c06c98b22a8a879c67aaf922acfc4aeeca6d801cc07d8167d0632f9d5ae7053cf39fd56cf943359fac5914dc369779bebd50e7f1c21d70325dc8c21b28c6f9a66e83fd042e4796d5d361309a4e5b3a2ae2391c82907bc4a5e4b71abfac3baecce17e723d31cc53904c56258aaf0c42d959ea1da619d7c67b0e115187ad4821338c6df5ef97d6f7bd490b33dc54dad8b1d9544442c163e3420b85e3724713b445de262212d78a2f84511100c7d22bc83134b0d9e709d747a6cd0654c8e3443ae911e4fd6632b25e97fc84748477b74793c55d6d92f68a4338da946b0f9ea692408099027fcec70396982fee43eed6e02ae9dec6d3254a848c226aa373d6ed17fe45b1890fc148baeca58a4f570faf7a3f93e84a9d443554483bcd0f64a85f513680c8c1bcb0211a9a5263d79fd404faf85b1d8db250f2ac66cf5ba89384da099d0c65dd8f19d4849ef6c7efa90cc21e2f1358a4680eb24a976404cf85c25c2a4f1c4609288084eef11d1123ca978a62bfb01d413b2bbfb6f49daa22f504211e65f3e520318084e159d9c32c25ce0ed2371283ef50eb243cd3da07e07f17160cb71502d07a8fede695d891a2d54e2f4c9643c1f24e30b740b3e8bf30e499d294eb3aa7df377931b4903d7fbd0c052fc8e1a922bc0f126d0283979de969ba072bb5212f0d1d2fa6596170636f77bac1c3e6dc25c79909363c5cd0247c5da8f36a2582453539713ad8cb1ebe60503cc9b04387a7029143032e9386d9db16c2cbc2bf4e590b2f2f62bb1adb6c2df9ac06ad6bbb470bb0116d30c7d51e8517e4d51534f7451d4d33b823143de48e55a4c73018289dc4cf53213a9d51c8f52ac8643dfe3626a646e9de3dc972d62d3147d799adf1501c877f46892d1c79dcea0e9610e1c0952a33128858b692ca4ad2661f79becc9c2b649f003830fc815fd83b191b22784250fac7748e0ecd85938a099b98f447014b501ed0b6471646120ce6c0b5c1f58cfad67dce74535bcbec0933b4d125f4feb1a71f5ecac7a3ec465f68b83ea19b70bdc6e0a3e8df15309da4c1fe6a6fad6d8996d2a97f458a6e4fc2da3014ddccdfe5fb0c2f93691948358ddf9bcc90d0e90d7fc942a93c87c1e745aa67f3ae663bec9b3ff03dc61fdab8c3dbdec29bc8b25a4c6130751387358029d25ccb3a823a789efef815af50ca04f453c4748b96af9fecac914aa3273a17054f3a1a483143b7bbfebbc693477296b32c2ab3d142da774715caf07ba1bceccb68e290028241b6d782060f125cdf1b01dd6b6abdf27b4bce47ecdb573fb6fb669e6ffd790ff9d4d0a6e9656e7494fff423d57ee6f5d56566bd92463ffc0f3c777ee82ed8fd48b2a6d8090a3d636985fac4c3e6c577ebbe81c8cea336949c5c36a489ebf75ac436f9f59d718a7869ca396c10060aaf8900740f794b39206ea437e3acd3873d74cf5aaeb41676e453828675b5f676657866eb689859dc5a0d7e212f943527e15bd5f903801614f7cc3b07bcda909d3ac6328c079a00c53275fbd32c3705ee1123dbbea5e152fe73862fb03acba563c884c6c1837549b7ce88e33ed7fcf2ce8d3adcfad9e02b9024b33fb7fd227a51744bbf53a2ec2c4e1677f44004e2d49c849dadaf09458e52e1d7bfa79bacf3cb4953a550fbd7ecb8d6365a4e744376849804343d24d5f2f984946761162d31952442525d7f43955daf35e0c381c36a3c7775eb894bc428151b06d46d35ae66fba93fac0fd229ef7ea36be9a80b7821041147fded05fdc70c2e4cecdf8a12262d720234b3b8c76f0cf3ac61480a47f7fd92f7bfe28884f981fd478e48704ecb63cf95bd6fa1ae67e42cd7bbce67657a28e200476f44aa29524b916599dd10187faf039ab38c209d50e963591445e153019f7b116877b34a245dd0171e8cea00083b0b0858e5cef62f589949ee81c62de51505c82fd121e54355f52f07d53251a0241b29915cb18b86a362da2bcdd53116c000d4a2e5edcfd595548b541b58456167876142ff6b013350c866c7f25902539a33a7a6258642751d03812fb3e0c506e5c24f16d4365041e4431b354f6f8904a7a2242be9d139118382a465aba64d3a5926d01cd6a1e4e3012d0513af12d065e870f979f2d77e0f674db6edade65db8bedfbb5ddadbdaab61d6ad7417a83d8761f1aaade7c9588de35ac8e8504a332b2a92ee34155e6a604694f8e69517810543832a2431614e9997d11fab589700233153a9aaefc3a1652b8faa88cbab41d4d898305a47135ae45e1a9c996543a220a4ae331edd4dc506292b2d838645df868503af32ba59eb43e59a422934577d0e4575d468134c34153f6e7b5eef5b62bbc520ce09d6ce6a32638b201619bfb5ebc41e78ca5058eb8abc038ab485d733d7ccbd81751c08bd977f20090120ac932b5c9b38143e22b178f044f0dd4a28c9e70072e9e9ebdf4f1524299e0437165ce6e23842d07b13611d4f1fda577c5a9c061a2b9f3b862e83cc74d764aa02ec1a93d07b5711719924409a233bfbb6e5aaf0742a161eb1d09c5cc9f155c821d93583a705b26583b7a6ac114efd955b311b7b7ff324b2bd49c73ec141ebb41ddb6c74b522338433cd99db225d49a1851219a78132eb0e942807f850b343560aaf93c83d3945324a66b160252e58c83e60718afb34deaeea2f368d84c55610995cdd8927fb326051210e5248c3bb54b16ad3e1621561120d0c2c6b146d1d3ad58fa3e395013f1422a4b58eb25265ee1757d2fe853fadd7f7ed0cf8759b96f709fd8a7d23152ec148c88c0f49852d0fa090f3a3c2efe0c77960f5da753984e103ff32f6ded63d89ffe149c8af706353c7c0f7938062196400b33f3f96e3b53ac8b75c38cf81a1b667b51e9f4d5e5609933c5bf36043b950107cbc960e93ad42b21e56201ed6d13b797184782cb586fb6be1a95b389794a571ba76eb90358b66b9e499c3f6ce0a61617954f054e2adc4125d45da37b55bf3e4e5193fc32500afa029cc43cd5e179a33b1f2b82627b45070e8de853e0154e4cff2f2605c58f53a0f3ce3ee1d06d3b5c624ba7c24c6cc955431ea6adfd1848e9e40487ec3b23849f2bfa2c1ab06dc34229f775be49b9efb203920e5a3bfc88903f95c0be71ae19a9caba3d84c7fa8d8f9cb5ab9fc7c91cfcd83b23ee4cd988ce9c5bd7cf09c1db43bec8a81b624ac20703cab3430fd61688e4bec890d299e9278cd096831a3e33cdccf1666fd271911caddfa7a372669931d266078db64813e9de120977fc50d81c0fa8a9c332c12dbbea37aef753a82ef3152b22f2a0f68f28982f9cbc8ce418f607600914dc86d2a35a4957764da0bc0318a32688cc4000209698e4393cc1d71de489cd2f817f21536714c08ef47a896a2575e7317a9219b51c06e06d8eea7d8301bf6146affa89858aae4b2796213d0ce4a4779a7a8a7de482085fdaa7e307fa16c206a9370e5e77ee1a0be7a7df810149a5f58eda55d9efe32cd69823eae5b68fb3c6a61f3ef0ecd316b95f98e45e942a9025403ff310aeb567f01571418c44d0e03ef786be70a2e9ef0b1f25cc3ab81908d618c028241518c4a5dd23592b1ceaa7f24d3e0e7a8a83933d8fe89c8b38878b0a243300e1ef1dc940a0c833f50239398bea9b43c6cce296935fef10f79a1cce94d0195ba0a020cd798d3fdeecd79c8a3dfb4ad4266e21b1fa81329a33fbd9a3fd7ed44bfa96fb4283fd62a35689007ec77ef5c0ccc052077157bb822d0b54f2816d3bbc177d2202b97dee9ff6187c29fada2cf6363dda39132e3d9a04df8a4bfd1a1cbe7bf18e538ae60455e8e17d599eac63a1dedf74e5043769c480f0165bb79b386328da5482f8a134e12f8d35834baf3918c53ce498d0251872c0c51b74efb00428faaba396b4eeba412494d2e11c59a2b1594ae3b426c09d1ead760f4b11a2c0bc03025392ab5a6c9bbde1f0cc59f03597b6dad1a860a1ffbb2638b6608ee072ef8bb15d75168d40d29b99000137da004c7c27b077fca916dbc27c8b215345db23dafaf0afd2699360ce95df92d5d7bd2838e61d79c6ac46442b1cbc41a9d6dff1109b653341976c012ca7017ae21436e3db2989367c3e05df9c8b5bb94ed0b63df6e77fc4a9640a220f358a6630c21fe090bfc2d440fbf01de4e1e62e4793fc27666fca791fb45519c2252cd9e79c4cd924b93845be5da954d125b4149d361d37be4a96934233ae4c1a44d0699d877490f94319f9da737334c188280f03c1db314a609e99382381721b12f5182890d4884a7571e7374761b9731df0f1189a9d9e681c1f979b99347118e142f67d9e3ca3a79effa7a3e2b4c075f2f6ee4ebc7f2b1c7126a0eb135e3e7d330c2b6c871dc4da2e3e72cf41b04dea0b1547b231f574f1572b13109bf3f77db666d046d9aec7fd311f45418d133dc0152333a5f529d044a012d40aca1367856cf5911e71d8ce9f81dbc34fce910101594901d190afe2fd24bf8314a3b36ce859a00288313f91ceeb00d83f822b42b30abc3ef234c2373e7bc8fd685c3bf2bba47cc14bee2273f462adee91704c5289c7c92170ed12cdd580fffb4c05bb3e7d7a20cd0d95cc830261673552294bab70425fc29fc3f8c2d73bd0ea5f6ecabf2b3b25105a102f8d6571b7cb6edd8b83a58b7922ff243a32b94f1b3bbe43c56df3d0b3163b9f4eaecb434f6b0b065256f1b77eb2f3ac4bc0fb2e3077d7b5c6b558910c2edbf180af3d4c741eeaed09b7a6b192d6a735d49ceb33980025bbe567f4a395dba63553386415f90290b51ecf2096a34c864bdc6b79cc9388ed07ce6806a031dfb8a0a955ef1707984061ddad55e98cc73b5c6f6dde5e04de78497131a60b96078f304c69e1cd1a240e263f9f7fa45c6fc4bbf94b8899f79554accb38674f3be2270371169b40cd10137a9225ea8e9caedfdb72f36c4d6d431c7d01014cd2911fa547391e28356880bfa2fe0bba964cb2c977330b371c858cd9c666fc9248ccdfa8698f689be762e7ff80db47e14f0ef2d87239c6bb65a41660973fbb2cba489914e01db35835f64a46a360c04661a4779bd944ba9b978e5a22bc26c6b47f01e83b7e396812ddece61a39922a61ca93c8c8b25fd3ece57db0dcd8fbdfa1099e99d0c9d997e54943ece5f68ec8c83a372ec80018de7d4c84b0ba9da611c6805b67f614999e86f15e6bda5de426a1fe239cef35a896c68cf94be65f1e558bd54d0d85b4667f35c9910ed1507f9120b16055c38f41fc9bb8a13fda6a3bf70adf5cf6beaf567846aacb33035788fdb0481cdf0457cf27f30bb3c3667cd5fd617ee2038ab17350b18a048fc68666fee08c6f5b6ac2598d77c9584b11f950b2693970afdbc2e7c52f30b30ef8045aeeffecb6b3e353ec3fbeecb9d80ef8513a983501d752cc56ba39d607c3603074120ee55227d4f34a9e31cae7d5a2c6fe582e49f8bee1fb3714e953ed9380e2fa4f8cb100e8e670f2def4c582ad7d96b5838d9972c83811fe93bc87ba6fd341c02aaeebb5174291acece273fcd263d6593881b9d9ff7fe452338ff874beb68526ce3c651d501cfc5a006fe8db66d1824a28848bb8477fcb62d3482e8bd3d72110af08bdb3120f9f4f62afe797f31587dd7b05c292310e34fe819106b4d5c7de32c772a2830db548e5c9270a5ac7ca2ca724cfe46af0f35f43d296c3fb9fc7dafe35141f1c2179594648b601736880b4f5639b6300ff0e2f718ac6ac3fd51564a1a404111875459b9c6fb77d19a6f8d6c597e3ed70bf7104f8bc87a08431c12db0e57296d5290afbb2f19013f4fd68632c4cc4739947e07c87c221ac4d607b515129da8d08de052e31cd1c9ff035290cced357691011408fbef22abbaf9dcdae94a428a0e0110b82260c22659dabaa36e637e4eb851259a06ab126c33732be33f27b6c2421a677f036d0ed284218136ece13ae8fc870461ec6d594506c0fe4c3c5d01e0bf5ec214e815f81788d896c0aff62b245d6b1db1b611d734e1bc057d8f5f9263f697808049495d22aa54c60543c58e4360be2850ef8abf64b602ba3a23cc048f7f5c2e3bcb08264479c73719c51d4c18b6a48546a993d1f362372c880250f8ec4f4e4b063a0a7a40b60a218626756bd0ec7228370afff0f80e98064bdf71c7a98e727777188911a363bdeba4511b586a82f81d1ac06446615226aa2700493b009572ed5a1fa38926656441885f229b08fdcfb8f6c1d8b92b04b85d15c37103ce771919e57b7d471730f09cc846229daffff18ae1c3043d901549c46c82e7b3fb7a3b61fa81d054db3c368158703c0c00b214e3e2a638a1a00ad512c09ca6b5d3a9d47365906e2a773fe36403ec9d9ee490541c722bed8ab13e28b03aa24dbe8be83c154a0c512d80843eab4bd559105ba6425fb659fdf0cc9b6decd6f7ba84928ec24aaea291f1296a1111d8f03032ed3a3db5fe8b2edef0b57caac00560ec9b5425f93bb05a260235ef31285c59b4c6092e28153f03ef5bf070d4028d0384f6805fd57994cfae01809bd410210a893940c95e496728db67dea0303bdff453bf78818347a48806ae256136eb6d731d1f7f18529824c4dc231497dd9d8f92e21bca4706258f32ed7e01615ce2772c0a265c984f4d03b9e5b5156d9317c79ee498047862bf949f5e35bbef45dfa9ae73dfe366f4204d4389b2e09873fcc09e0f3098dd31286429fc3d7cdee36cb5247ad2471da69d96c603dbe549c8601402f7be5c5d3a65cf8ecf98e5915802bd41882b379e99a90628ad07a24852a282571ee46022b63cf923bfb85b4bde48ffd18a6527b13417a9b4812cb82330f831dfd2d69506ae7ba2e0985515822477e99e394f45191d93a127b0ff8c7e5e4cfb294642ed7978646fb9462f94d40ba50e493f86b0470e472cd9ebb5172f50690afe782b7c5ba8621d913367d40c8b235270583835222437c3e509231af2a024edc5d66eaeb08ae0c1ad7a36e7b18286f66831415a11cd1e333ef72597b1f61e0ae07712a56c614c0b6ac7eac14f58d4811b1156d6a70c4ebf09532cf83ff99273ac49668178749b12b4220fb6556558f73627a50fa08f29ec700fadcb74e6b0f7bf55f25ee24f624d74e8849244a59e3a4b66f4e4a91ec7390ca7de9cd381cdadea6377767bde45c1f07de2b7b8104fc7b4e5997059204d01a65d8f5e44418cd0dcc626257148c58715531a7b38e22320842c308ce480c56d674fc8b36d90790cf626f51a3584c0245f9fe68cd14a3583b6a07a3373c5206547acbdef0ae459bbf815365b5dced7e0a574485accbad83485498a3c29c4821cc12b9d678dfb6c54fae760e02f3576fc8fd21f2fdef3de157fca0ab2183df6babb6f70666aff01137369f7beb3c0559bde970fd265aade350ecd64cb9b8d6065deee1fa066fcfedae7ec901bdb5a5f04fde543f5b6b63f14c78eefec95995a3e45aa44e1c0f64c35e84d71945c3ddef8d8bf397e773cee975287c00978d0af69bce6a3188b475d9f215289e30cedefdeffb3e9f72965e0cadfa901e1093d6b9a8fdc723f1a7324ddd0e247573d029e691e582e8331ec579a62cf099b39f78e0a17affa66641f8e7cd76c2e6a1320f05cf6f0f6acfd5291fa0a5e9dd52da157e84bed9eded5c236661563c1d1ff4c1714513de8d478073f768f9cf3023f7aa370fb824d3d3aaa0939f5f2814eddddcdcb644d6bed32edfff48861f9adc19f30f99f384ff77dd01c9f8c58a49496e14dc2de614cd0a0334c5c7630fea365439a3a761579fd9048b259895137ff945fb00c7e85cf3d199cfe56e23f837da77a4e4a3cc688fbc796bdb263a616ecea4fbd16528298cea8cc392743b216768363cfbf26da625f52f6ac05a991c435662a6f354a627a8b3d6a1e37fa66387c0939b7976a65cf243f5ec51daeeb2f772d70b9a84ff3be092db5d03d47fee8dbe38fc99445f91e7ef3046d400511dc124f01c5378cf300bad72fdfdbd292aab2ec94a4c1ff746e9cc3d9be80f96c51d689e663cfff0ba3f97ed22d51470dc6799f5ced2d5a8e0eeca28aefab60d83eefbf9072ba91f7d98d3e6669c37670470c2e40b8eaf2c17e5ee6ae35f41df6915f30a878b2ae9b529113ccc2102a89cd2b97dc9ff2b83317858dbe3b641f538db065e33bd936eb83ed4b5c5ba934338af5c5b08e3d1f85484a061801ca60ddbcb3eb42c438228a3b0597b68ed5d98d238e99ab7e4972c8709b67470a2c33392a2f5c63fdcf559861703cf6ca87e25b8ff97a894bd51703dfe77c7f230877a56ac674e5bfc8b1f910eca15737d51d665bb0d2e2361a7d8a0bcfee78bd07cc0c524d5cca0b30271e656c640f9b8695dbff631883f9c05a3023ae7c1291e85ef8f4179498a334394c9e4e858aa482048a5917d4ef4e27c9d0507747ae095fbb5c5af23dd75dfbdbbce2a731aeea21d17aae4b9f1d71a2d0753a43dc55c4aa864f6ca262dcd1d5c2567fb030a31c83afb32776a97b3410270cbffb5e23e7de7d1d1a0ee3394c3732adb3dc4b23d945f5107eb0af2fbce0272efdd8debd66bf72b73ef71d3e292781fd018185a4294ff17e7a9e67ff69abf33f479e934d772d1b3de8571755b8e761b6e3d103b29e14c9dede78be70d39f7d7960a4d7f5194abad33ad775cbb7dd76aca6efb8fe7be4115b378fdf9f3fe9634562f99f579cad29d28952801bfac21eed4d10e0fde8646f0e05da2c35890850a550d8e6654e11a46a32e2dcf6b6760fe2a3ee1c2b1120d31057ad377fc2cdea4d4d4018d7713affadc8451496ea83d098c13be985fbcccc6bb59c98aca6265f0954397ef62b8cc277c40f9e6330951f878629650b78cbb463d293846c6d206ab97c48423d71709283e4d7054eee566e67a7deb54cf87efbf1df9ec5a56f106214bdc3567d9b9a9de62e761d830f609ef2d360dfae92060dfcd3fb75568c1ab9bfba1926e1da357279272aa4cb077d41b84dfa171ff1155514508ee3652ec064224ec82995a23c51588780829d0759bf4ba88fcf805889e2a3723a186a8b53f72198d91bc8d76887355a7c39ee0b0b903cd32d256d9cf1163d6531a3f6b1b28da5060074ce3fc5678f817203f73dc81c1f34623c80f443de8d1ed13f118df73b34edb9d529d8781df887b0f78a8268e6451f74570b59795f8b5dec3e317e13e571e778808bdc90bca941f96d3707b50090df9f8f89417f7d1cf38fab4b14827683fe5d82b96bda4091c6144456eff702ae4ba0e9ba573778d6f9358b74e1ad20d0800cb4477efe48e8ee83d9af81c38453702cc18b367abc123a48977946e70181feb86c4334b85625f4cbedb6f24e3ccb2425fc709bf69af1c7bf34a4f3cd3cffb9a16945646ff75e28944948a5fe87d9ae2f9dceaf065bc4c53b1e2279026ceb9d78d8c1d84ebf58f830bb5346de66657d47f433b9d6eee37f22f66b3c477d9f9c8cc25befc25a06769a912060e85442e468666dd42766d1dd2d0e27e0e7eb8b9f9fb2738fa0fa5c46f6a3776b904f68b795a19ba89b894e0e148f26ee9997f411e5989c712743a9be0b1fb2b85d52d96042d99186652d49567a44ea3bfc9b71e475d4267f00896a3eae7398d92b1607e73da1994b9ee0e3e52952cc5009d576b907199516768c4b1af812b2fca8cda010dbc91e07c45b71d4a5e70fa29ebad0850e65ceeace2ee8b3be4fca2de9c8f5cd6271f0af88d947236eb4f5a3f15bd8cc7a148065954e560ce35ce8d951ade3c66d6eaf6fb6668e49f16a8827a59a2facd5c7b00b8a0267c3eaa53bfb046fab2f2a641740b7eca1b6ce38064ff3b51ec2bda99b80795b224ff86119e17c6db02c04d5e79bd6629ecd79f2b7f061667306952ed907e8a993713a317e3c72f9a3beb0c68b71cd732f9992510e731a3e3a7ed162348b1b7fdd577b897405db157d98b019864e764fb01b659ffd8c22b4aa8b69d4194d3436d5ea06d7a1fbd3db79b5cf99a874e5de1dd010ea10e794160425b9319d0c0367feef9072dfd717272dc1ae5fdee42895aa1fce46ab308e07ce3c9c915a3dfe712bcb233347e135c1b386a7690c6071a542c1bcb209d30788f1d772b7be2f0f4ff9dfaacdd5cab763f4583dcd307077fd3461bc11f5a68afc3a2405755fbe84fe73205e2d60cbc208e2490c7d6183a3ab95d66f494a03a360a7dc6dc99a2ea2b83fa33a003b6b3ebfe5d5c01a7b1ecb796c1d16a8c35ffe225c7a746d07df735eed54a7b0f3d8f00cd9dc221c96585cddd1ced69afca46f41d6c8578528d25f63acbeaebd05c9512ab68d7d66ee8351a25a0603937188f06be93996341f706306ab9d7745943e4fcb6f055cb2b9d384cf4278093ee0870ebd02301574f81845dd043eb1f43bd53e000fd0160843f881c7eeb2d974c18bda47ca8014f58e519697676f1fa55595c778591d120fdc6e3b67e31623d529cac84c6434f3f9a33d9d4e6cb487fce51b2acc1a95144e21ed959295623faad8a1725f9b4b27ef5dd76bde9007a01102cbe4050edda2d628b7a30342cab5daf881b9db9b1ef360c4fad250a3eac33b51648ac77c3122ad909f28a7b3dea36bf24a1afa6de7a6fbbe89cd9dcac962cb63f3c682c4baa308f8240c88fa802b99278f59258d0d3f15f4dcd062218c6809d43c5e56dfd90e355b9c8bad84049ac2eecb19e00b72e5e373421da57c71a2c14033d6176b0d7ff3025f7b2b051a79d4a43cc53e0c4919c516f7335bb210bad2ea80dc6ed4708295418e19a47dd5ec4543604764b5201aeec661e9c2851d2250cba4c1f5901f5964b635717baafb4aa59c2d3cf64c786798d7381dc27922bdfa3730d20c930ac3783734d9a377eadbfc1edddbe8fdb0c9b767401d1e947428bae67c30e9f4f329f34e0d4586998bd7e3c60634c54e2b76b03b53011259e1d7c89dbd4cf3894dd21eea25b1991df4527e46232e89c8e870cbaca95bd1af867d6a30c2251cb401fd8ef6d630a8bec3a46d6a79a24f2790d155192f699a36a5fd2a7902498376d2e784d42e53dce0098bac71107b18f484336580c134f632393e866d64aa22444ad47b9c501c496c2491370ef84ce6777d5c6f877d7e61e18f77e7d05dc10d6ba57029461afdfdf0cd0ee31587546e66e1b65dee57acead2c5ab7a7873286ecce902705100f34313c3354e3807b8fd97b937c3753229107811ac8be569b86e7d7656ad075531b8de7369b8889e727790fdd848975489eabfe9ab50a53f637c760b52e3dbb48fd79a3d5690fb5fa73542402df4f0267af768265e0acd787dc8a30cc138806fcf5a79c138dc97b311e38de1312feff040dbe21c1790023620fcfca12ca1b7cce848d7136b5e3f9cc197478488ab9b3e0ae5fdddded53bf8957f702bdbead66c10e235d12a11e2e54ea8825445e123621b2388c29af5366368ac9e4f72e1affa4ce0b46eed0a32df18932290a7ade17e00310349b446fab1b2781ed2c5f1cfcd786e35efaf9373de2148507efd0528cc74f3fb290f6e45f9f10dad7a7a0b5d81bc2dcfbc15796e91978a1a00760ad0cf0f26809ab97d4434b78bb20c8eeb894d5c5de8d1dce3b5634bf63c807850e168d77984928b187885a6166bdcfd98f84b21a8683d9da3f2cf083acde7aed7fd1ad6ba37dc69838def1335340215fdb68199ff9628cb73778a0786f9fc585e568453720762ba57be73b04984b7eff587f36a8842c800bb00eeb4d3260742a04d1e6db32e31a2262495260b36249f0034be8ce0672b53289f0bd8d352248015b7fcb1cd690d544c1b6f1c2cfcb115f09d57a218ebb728de6bf950db6a49b5c80fe439702cec25b398fce3e7657e8eed95a8e75367f9e3ef555fc3063fc4bb86d918978c52f88531ac9da3126eb2384322e9732ba4497d8045ff7a5b677d1080358655a1282031334585f8871f293693701314db9e163d88cea90f77738fbf6ba47da22834ea0209098cac206bf6d417549559fbd714fb6f8883d1ebc5903f2e1d89506c85450f0b08a64de922a2e6bcbf712f5b57a7b7d23cce4ebada128576ff869855884e1f92185ff6b329ba1582f319ecb576586657923dc3eba6c03b48d75908988aa864486d88b27e1a0e550c0471362cfe56623c6c36d4cf05b98ae78fbc98214f8d5b51dc5dbda5ec7a3d80314edbad40400fe7628f8da53c388b9075be1561f2c70f748ff3688cf93d6302a002527cf11e6219c125805c2b1e7a7e6591858582acb16c82e6e795e2c0db227e00471af508e7cdda8c865a989836bfcc6728dc4360dda167af5858edddeb737237a181665e7c3427101e134e0eb64163970ca001cb8adf155373d2b5331c10521b51669cfe4fd00aa7b0b66ffee382a9ecd577f89dfb375ebd13cc04f37ae6cc1e1185c8e68b780117b1db4dd0b28c433896596686cae2a7d5c412907271882728ddcf07ef20f8ec3af3f371c3303393ab6d7719e69b6d4c7175fa1c95484d3d087178f076c70526932e10bd80d7f7aba7e70da9eb7088dc16d3351f77fed30618383dd218ef4e3a169a6dea45241cda26fa06309f5e2450e171ef76bef450cee59f3b4ec4e5b477b00e3cda9d786f71aebedf1b1f302c7c89889152bf0739c2a7ba1c78e54a6ae95f7559b99ea0925cdbd94578e3497744d9b0b614e0c666977504f9dd029e9e75968b5117d4c15703fcd84980f42e4f04dbd014a27a8d5b0e83ef54cd171dd2484f01bba8c96cb7456f7409ebffb637883e73f499eee04ff6cea7d9e97e5ca402205f46c6eb74a8ca0a317a028630ab7cf321aa628de20da72a64e8295b13475d6196df7081323816693a67e7d9a47577b56d6edbd74e38e09cef3d1aabcf8232009ca70bbe902e5c624cd497c437600cfd0c4d00133bd1c2033442e681903b39f802bc0b5a6f439f8c066acbc61cc4da342323a44863dc822df0bf8ec6eea449fab497be76903efd25ef8f1fb8017717b459c29847133f1aa5b5c3931da73f2a4ff5f97c54c15a3385ef1a41cda6e3b258536c15e2082626684280b4ce917f2c28b2f8a620f83e99f3a27bd416e514ba323f184e78522469c19b73d137abf08346177f67d169f7884c23cd6ba3d38a8c32cff161e305309f4959591864c687744d8c494e6bc26dfc08d4402a48b5dd787dc1032b93eafdfc2ac9b90f7e1ac0b3a3c69ab5eed74484c8fa1da31a3ed66c236c3c74b2854b28d3da433a7d1cf12f0225c7a9cc8adf11f0331abb534afea231ac8154f7a5e518a34ebb9e7d47dbe7074354528245c5d9ef374ea0b8acea084e5e61cfbfd3c45f74a857ce065078e27dfe09ae84099a7138e7c5b155a8da3acbbcb9e2a40c153d8a5d34db70729fa2661452c159aee3803bfb5e62f17f44507e11e9979ff5cc0446e61c9673d0f1a69d33375678e625ded71da17a519f68698502bedcc8e975a48a4a26d9a2d8fcefc3be35ad168e327e6dff1a5f33178369cb6ce0993271bbb0373566685c9544c90ad2490943ac826a8f42d17f1dee1ec55fa0d6b6a157d367e5864423191b1c703d3b7d89b2cda139f32ae52f9409f8686959da054df34a3f77333a2fbd26768ce90e3d50970df387ea01e0b982618974827388c3de5976fb04e0c28d6fa89e52c0522e7b1408d5d6503673f398f0613d73c4f5e441bf685b465fa98f522de5dc7d460ebca35157f60732930e11e6eafc2760c22208941fc684b22f021d869ccb1659ae3cf757a5ce39dc795a1fcedbc92709643caf28c3b87fd4f815bd48e070f2ac6315d32110fd56090b5168cd5a9c4393f2d1a9bf41e9ef63b39aa4fa94f654f7b1ae32663f01f0a416b7a4de8be60ca78a020976cb367bbec5f862f6672b9f41fe1f77f2ab021e5effb572a2d9957be5d47ec13c4c365122ad7877810ec46a4f6804af39fe9e6f77934f18ba49941233f95c09390862d1add6b8e9c0ae63c81c1fc24e28f7096fd742b3de7cfbe8593bac19e6974f513e9dce1e7d9a03ff2969567e454009ef96422de8a833191e89798924edda61451d397ab6c4ea57d57dd94be2d88508f9531a7da70a03f7ff952e52e8b0832738d68da797985b4d771406156fe17932f37697044341fe6967c17f9c8efc6d4f29b8c850b49ce870e290d0f2f195d53b7dd71889bc3bbcca66abd0b99c04b4dab97e9c57337596b7a340293154a8ead17e84fbcc299efb8ff8a8c00b896f6f1e24cf6420c4a4eff14819f10718a4342a22263732af3dc35604b6e22eaa80a4cdd968977b766084b885a14965b0a0e57258bc28a8c028e354863751ef2f86e6887007291e3c9db868bf9da209edf9309c71f9268deb139de4d9c9033160bd1dc0d1266703622a6c5bde0f26ec899c67bd86383529de13c5388843d5c9eb2b6ffd6991a26a7edce9a4eb8f1877d0e6fa2c9992cdb8818227fa6189801d0757f42bccf0a54cd3f970605662ed5ec876e0780c598de0839aa1b92ec7c2c616b7bff1aa98aeafcc07f3409072f55f609bc20e76bf11bbb1394bc2c504a5f478e20dd39dc2eca9083343b49a6ddcbe526d44160324f50e69cedee128635792d53dac775da664ca2d798fd2fd5005f89b2f9988afffc9fac270e24088c70c7238d2bc79bc409e5440d49b87bda533d9ee0abf78fce74dbb15f023e9cb5826bf8b230a67406b3cc4344a53d03d3a4d7499cc1ec839622941b073205fa02109f318e7722e5b2678a0ae68cb9e289986ea582067ca44053448d5426d1b68e0905eadf4beebaff5fc54d897a56b0dd5b757f05717847de4a2863803ca0d6c340d5c6e12584407e740bb0c31fd99c604c2b445e67363c88b68b6d88c668324827917dca1d276f061c9bf8e981dcf9be6861fdcaf0bc46ad4cba815c7230c81a14b6b4173f41b918cb868403865df91a774a914ddcd4701b2b903a0822e2cadb77ea32fe5082049c0e03eeaa45ce0e3440b0a6d0013c0c3c0c3c0c3c0cac31c6ee1ddd7d446b5f262925d3f865f690e1524a29a59422f3f685672f04faa1fadcadbf1c17f90e5f0e690f48e31449234a7dd01d7ef35dc81409625b79293cc525cb9422295d87ba2d99dcfd5c5224768a9f27dea4082f47b1b8c555304bcbddaa6de1c1a3868553ebe5952d46c10c465e40431449b2f1f4c7a0f2782123e8000e1798a0c70be141222272344291f86b1bf784aa39b66ec7101eb863080f5024f6a5b510352556d4fa09aeabc4bc3bd4cb3b49d98c767944659886279283979cd158353a670ed0e844724ea69bb24766519b0f09823e24c87122d9752cc6f5ce78327ef005385c60021c3e84f418810a686c22b35b8bdd19235e515e743c879dee14b2b9cdb155a5120d4d24a7cb222c8366b6dcfc6a80462612ef35cf4b8b6778768c7c101d3b10a2c3477c0829b69848341df54ae6fbb73dfb12497ba9d388e928da47cfb1e54382f890165822e1ba376678f37ca223730875c00a342a915479a47ebe8da6b839901c414a01c9d1021111213a5cc70bc8d0e1011111114a9448426312092ab3eb928e9bdb095187ecd86178b440442497070d4924e8787562738b7bc63212c92e6aabaacaab3a487f201f3c1403342091603f1657495bb28e95901e232043870754c9d0814646780ce9818f4890da9bb3723c088ba61d68382249c70e95643ea665f8ca17683422b994f20e3bf16176e613030d462489db78953cce274d690b038d45249e76bf6c6f9eafa4f319682822f12e2b2df5ba6610974424d98613131fc57cf347444444849b062212a458f4bedbecd6f1a47188a4a42bf74ee796ecd968182279476db8afd411df8e46219292a8cad1dfed795f27446214abcb289edebbb48348ccd9f0243453da9b2f88c4241764ae5f0b163e1c880419eb2ae5d8af2473102012cc52085d5b26836dfa87abae52a9bb6ba560e1332d97c966b760daaaf143b2785026fcc6e4c8c9fb909445e6d59c92a89c47e643529cee73abb81a43767b48b41cbb7ca284d06fdb888e1f6664640405331811a1a18724edf98398331b1d5367c88e9424a091872471ee219f76f45f327cec3811d0c043d2a81c911634a6bce3e5d85a1f26b8439556572a9a9af195cdb6835cd30e5b41e74e4f06193e4cf0c3070a4444c8f0b1e38008f941c30e49aa72ca72315bba745487a42dcf5e493e6bb64e3381061d924c3f7c5df4fd6899ff2026870a76a4caa03187a4b418668425d3f4abe7d8da3184c7b9c00438760c39c10e1d2e228282198c00a12187a4ee98ee41d54f4d9d39b6340649230ec9b331c3dbdc2b2cfc7048b6944c26714973b6bcca40e30d49496905a59456d8ef300d3724ce88b1dfddf0e942a50d8925edadb2d46be3538b020d36247516b99d9459c75b125381c61a12345f52bf27b7d3fc8582198ce4a0a186c4f0cc5c4fd5a6aa2c0080028d34246cea8cbf13d59bf9e700f26d68483eb1bf29e49e8c135f8e2d0e62a5562898c1c81834ced0ab586caf581e7665af164229d5a6a4e70d8988041111e9dc010d3324aa6852e152a65a8ebb0c89feaa7d9d74e8ef60224382278da5e5937c4bcaa53186c4b8b172ce869dcf4dc5903cb2a4ed8d9bcef1320c09d6fd192fc64a88d9c190682232a8dee85371491a5fa8cc44e3a258b5cd9999b76d5a52c2aec335d8fda5a8507b2a6647aa0bc97f2b2a9bb89cb9a3070314cc60240734b890e8f963c2d2dca56e488bc61612f437f7766ef3ec99161111a9203a78a06a21c9b63a4b46d19cb4fb39b67a04dc5948d2ee2364a5b96e4f3616927c2f7d2625fcd2638ec615124529175d95fd29b511203e46843c1958bc56483c159e617ddfebbcdd0cd0a842720a995ee943ddbbe5a990d4252f9a45139f92b753483c69f6a5fa3ec9680b0d292476888ed9d4c73c7f8b461492544e71a77b979abbf3917a8c80af40030ac921dfd379967c7bcb8d051a4f488a1d6c5773bb4ead25481946052222222223c5f7041a4e4852da31c714849dad959a90e83eea3b5d8bfc64314c48109993fab6c50fd24a1a4b482a35b59fd954462d692821c9c74a6e288bcbfd291a49e0528513f3b4acedf8103fd11eddd8d727f2d14042f2e9dd14f465bfb83e3a42829df0105f7de56b6a232456b0b4e7ee614e7fb408895ed6d9d29cb9876f78041a44480ca5ecf2dda577519da3318404d163294c67eb65b53640430889bdb1724b53aa986af43fca70c11466042349ec72fd6cced5e8710130124c4578f8d8f973aa57c88c5f245ec86e7dda51f19f5435e28bc418d772b69bb5306b63c1e0ca1866f42249657b5dffb42df3c701f1a1021191e645c26eba9fec642a6e5f5830d845a2f99a0a52d4aeba485229bf629bf23ddf77c38c5c2478f78da7b64a44044b07583a385c245b07194b09d7b83933286f9158b174ca529d21e794e85520226235826b98618bc4beccf9197f39f38ab548b4533777954f78f857961609426c08f5964a47575d44a4d44819420c0d3366917ca3db43f6cd29954b2222226508111119a9205a861023c30c592406599674c43277fd8c45a2559eead11836acbd3360911843f3d6697a2fff393d0b84e8f080d588101d5eaa7a043108e4d1bd2231e35ce6e06b3afdc675862b922a775b25db90a7d25624cb07216f2d5dd85c2a2222ac48927be973b8ad20f38e5691e0ad41c8d26937f9aa480ae9ed95a4dfda5d8a67a422b18490a54eeb9c0e7ac119a868c5b2553a2b4ba92ccdaa44fbdfaecc885fac878e12189e22d144462f29f78c9b5211111acc304592aaf7b1eb2e9196772912b39667fb7d0ea5bfcd2045a2766f12db332ae69c3d20427ecc18455289874d2563d97b87449198838ecea7e44e650d4592aa12b13f6a74f4108122f9dc73a3fae5d0e1e74f24075defab4dae2792cdb2d385d20f569aec4472a5a5b8254b06cde044529ce6895d54b12c6f8ead1c3c19c605478611721698b189e4946bc359b6e799c85030839115ccd04462fc184d69bcd86d326604333291d49e4df5c9a5a8797c328c0b4444c8304252300313c941e56d52cfa33d69ea12c79893b66759d08e1996a8d35232b3d866c13fa50ad594e6c3401ac823f32ca852208f6230a312c9a7667aaaa9ba35f4385c100220690625924f47fed5e9532bae7d404444488fe731e4f8402222401e4d224997f8f079bba39cb792488ca2d3a714e764c9f7726c194084f448d87e181e3732231249f194fef13e13d2c393638b8c1e3a820019721ec84891819264de0463e01032e4042222401e07400c8f52f6c8470fccaa007861062492d493968f733a2c5c633ccc7844f225b33842d3bf7b9e71b8c00438aa460acd11c936f69931af967778b6e861462312adbb53d47fccee75568dd4c89e1f6630222979a86bb98771d77211c9a1325eeb4b66771e0c331491d4e5294737adb61e4f44e2a51c2fa81215c773c882cc404492999d389d944ac947818888e9404464a44c0716a40b0e330e9178fdbdf9533ef9a9374472b9a5e47f3a9a26aa1c5be8918f2043aeb410494163664bba5712a5448884b5ac2f197fac6af483488a36a74a79ef8248fa54396813b5fbda32100936576f35da49df2667002251637aa8179583896633fe807a65885d50b92f198dd3a7afe279395a6fe587a4127fffa0c2bc2fcce843a2e9e92c79d153ea0c33f890147aae924a4968cf2fea32ccd843f2698e95c6a285adce541766e821c1834c2a977598cba88a88a47161461e1274cebe7c316ddcbf82872451ee6afa544c7cd433ee9070a677fdfe64d29c2e33ec909cbff3281d4d77c63fcda84362ea561925c3fb4727d12131482f75d76a6b39e67348ee7fbbb0f9313253921c926ef4aa6b7be498d0c7018fb1cf16b7a0e5a9a2a9a0948d9fbc1b0ec99953ff735eac95b04544444478a494c7e1015f61c61b92532b8e5a36a59dd29ae186a48b5ef9528c15bd533fa30d49a2316e3a390bd6b133830d099f720e7aabdf838a7d0dc91a3a66a828e12d9a9ea106456bee525cb82dafd53a19b2d2b55c2cdd399d918664d3663a6d3ebd1936ef9e8186c4f1523fab7da3dd72ce90a4ae6f6ece74e6ce316648ceadaf0d6f6aba5237a30cc9d1c3c5fc1a2da7ad1a984186440d33a6932a357df7c7909ca4c6cd24c37fb4852222ff4084b88888884812668821e137cd29b1316fca2d991c0f044d30230cc921333dcdaf43e9395995edc00021c10c3024e50ef24d5d257ff800f20111911f3e80a00eecc0b04044640706487f216963e8494b97bb722cc7c0018212f06086179232ebd929ef9373391281084444b80e00ac98d185440bad3ba7ef45e952e5d80ac27c98c185c42f5925b3591bdee71d666c21b92c65edce6e1cad3187c3052110418e2046043982981c63e018038708708c81a3057498a105abb2797d6cc565c885370b49e2c40675df11a29d0d998185a470bb1075d971aaf546c10c468230e30a0923dd3b6dfe985bb722222222529f98618524195f4588ed9e907193636ba4ee0833aa902497530eba49bee78c8cf8206364081b11665021c97c4c48f1204406717704e931630a09a7632a058dff3136828c1d1266482129ea7e060f9bb2a818cfb13532d256ac0833a290f09536a39bd2c1f4fa50484ae27f748dca31bbf652c59e906096554fe697f7f41a34021111111120427ecc704292798e503d1121f2f78c262407b5a945e6f8d5df1833989058db35d6ab9982e713f398b184a495b994655de38eb271304309c973326a619a5d9dc91c5b5a2798918464fbd49e772c6ee594ef1948484a6255d332c8f8d8212222e4c91011c1e18210e800e2021c63140c444474bb30e3080932eeb69f2e966e6f7f0c19c1810f1f3bd670b82004385680630c1c26c0e10213e0681dcc304292d2171af2ec63f2d01521e9ec533d7b569341663c74e4082244070567308308c9f95e263c88bdb16ce5d8dab1c31cec0102ab1943e0ab44d542662db77b65ddeaf841efc26b73660821295310c2625da99cf9c1482af51015a5730ea647c048b2ac5bf174325749f48b24994a5fb456f1a047c717c9e3a54ab3284f2f92e23f46df50efae205e2466dd3b31162cbb537c1749c2c3e5e7f02f2f265d242995b19379ae3a139a5c24b50825d7ba9be16c5c2465a7a4bfd3eb292df62d922e86ac6856693fa22d922f06a56cf3e920e3712d12644c2d5dca90ff1b5a246dcccc56dbf6b2b7ce2241e5e6f3dd5aae994f16c91d6e66d37698103516c99a628c0813a7f636834562ee2515e683f6aaab5e91a477cc6b645bc870293a76805b653c0e467a87791104c01509ae99311eede3cb74ad48d2166b6f8be14e098d15097eefefa321634af9ad2241f9c58fc8cdfc29a6aa484c1e6246c5b01a936c39b6ce7210805424c73bff9341fc66f2ff801000542407f9295f305177bbd32992d4ffc5d9309e9b37c441a54d254f09cf241b4248fc2cdda036b574de2618c91a444f640a1634091b1889677266741ecb187355e317c58ed03166109d4d695fb0d51e9aed19a665ecc56d56f7f1969e5d6f41ade43dc7f896bbfc11adb21d35789178329bbca895b200870b4c8003052222436aec2229891aa5e9f2263dd8ba48cc96f46aae07af462e92d255e6fc6cfa2c730717c99af36234844a7ae3961ab7484a2927f12dd1a24b266d9134722b5c565026f4a7e4d8ea51aa356a91e8b13df55950426553cab1555d831689e1720c3bd22ec6a465e6408d5924cd47131afe6dfef23e7ee4778cfcd0014448902d2086070f03840c59247a4e5b52635229cc9262d17bca6a7a5cfaba8afb12a6b2f946bb9c83f6b0483a99cdf478ec945b751e82c373c82b926ebb66646c0815aee18a44ebfcd15a640a1d672b122c9a505f2d32ac48382b697136469dcad7088fffb10ab3abba44cbe6daac662c8a066da5efb9b33940c0021c3554919cae1d662c4f68d532158939a8143b7b42ec9c122a9245a93ea9e16b9d1a3d458210a3aa2dccad091525a3870e111116d43045b26e90f918d38a36cfe9408d52247d3695c554f3cf07f5f8111f42466e043548816e19bd71e341233bf81e552e30010e205a409647913c26d47e46535ff85a145b0a1e66b9c64d3dede4737d96b767518622f1f2a668bac2a679908322d9d2a65825c485533253e313491dd6f5e6dc7ed7429e48f8382a8e8a5ecade722792bdb2758d0961f355102043c608f242caf040193538c16d6625536f59cd6a6c22e9929aa8ccfb5c56a21a9a483a61faa27b1232d53b35329118c6f2ec3dc48547cb2e269274cc9ad39c184d25335d6a5c22415f28f5edd4d329dded2dd4b044520c5bd3d5dc9a5234c80b312648428d4a245b8d9b8c9b157d270b420d4a248b4a31b5f96306fb4e0810a3826220d498446255f6d0b2b433e7a21df1410d49247cdec7e456a188c8958da1462412f69214f11a199d636953a801894413beb7b66976c442778f48acfca0b3444524d4704472e834f11e3693dbcf6cf5841a8d483e9dc544c6848a27d38c4850d1341be2397b726f1149216c43c7542e94ac50a08b82198cf8a8a18824954ace8aba5cafe99388e4399d3695661a934f1191746a4e4dc3cc7de7f01049312c898d3187bc0b174324f56610963c55f08d4f2192d25d86e9a770bf362112d5a4abbe87f960e7d5184492ee932a6339de09a523d41044d2cb8fa5d29a749a0faa11880433cdb58ae9a6640901917c1bbdb269c77fb6afc61f92663c87f0d49e22770d3f24e8f8ff592bd3e6f193b149a1461f92931c917a29b3efdb1744444444e4742022c2e3f0a04f07c6e3f0e06af0217194d9f5670a32c3a80c1fc91164c88ff6408d3d24d67fb41bb34d9552af87a4b260322771a544ac55230f0942d375696a9de85b030fc92717f377e796e771ef9028b24c2c878a7648ee1511cd94e5d39375483cade822dbaf3e85990e49755aff4343bf2c65e7a0cd69bb655cd4107e328552613d7248fcd0b54bebaff97489439266bdd3d9425b3f3570287a69c9cacd5aca5d9da2ba622bdcc8cda2466f48b2b22de525fb7d2aa30321d47043720e23ba34cf3b7cbada9098deef4d8a8c619d5736249c164b1157de49462822222252aa588d3524e8b029aa2645cbbfa786e48fa7f4a84c25ae2ea7c1724fad9131b97472298cf48af5b92b2ec6d2a22129877c4fb2c67b3bc5121c6388882ca1c619924c27e53daba9e6a36386243523ee53d45252a32f43f2b9a6ee7ce72b322a1912d3688a5549cf31242513b519d46fce5e0c89e69783eed0701d5318064d978c7a2a770a189284af8c48133a8a5fe80b4952ecba82cc6691b57921292e6bde75b4508b295dc06c46b552956d5c5cda0a9bd6697e2e24e5ff2857417bdc335f44c4b450630b89a3cec497ca7b46931692fc3c844a71c487b59785ed529277734ba772293393ac14b34633866a602129fd7855fc147b17d715927cbde259b67c9ba62de5841a5648fcd5b6ec31ee874e9f2a24b96965df9dec924fa990a0e1d3831e377d4273a690e03d9a4c96d80bf79314123fc4cf48ab78e94f8f429d6919177651aed6b2c58df3a29f52030ac9b1e26590d14f3fa7477c0819f951d735a8f184445d59fd646e1ab4cf6d4084fca8e184a473cde9ca773f7ceae050810970648d2624c7889ddfe4a764f6bed5508309099a722fd8459f33fd2c216993fca7f7e7e0d1ab4e430d2524a89461c1824e3953af35929098f4061dba4cbb87e66b20213167315df9d27589f174a003990e747c60c79013b0958e54e308c9517c2ea3ab9f7cd68795358c90a044c6fa8c72b99bd232a8518424295629a4ce49b3351121e14a960cf9395363085d57d9bbc5a568b9ae52c72ca12c931c712a564308c9a57273ef86d82a29e2704108520e1798608c91213982181184800c1c2e30018e3c84118c444f151ba3af1a09f2a657100630b6b3142e9c79c55bb214366811daa9a272bad4a34118bf487e4f7d8bf312bda91ba932be04af021384055d410e0e7ea42f9246a74b616954bcf4ffa352070e7e040923d17aa6f963398407da9013242222403ec70f1d474718bc480a2a9ba754f35237bf8beeee3653bd5553b5829655ce962d6a09a54214862e12f4fa7c0a553b73f5837c075a408671018e31707c0f74b400c71838827c074e08781c3246827c18b9480a6fdd68a7e4981c9535df811d1d40c10c46c6080317c9697abdaf4f8c855cebb2d7932d923499f7e7b829338aa91649f971bd9ef9a745f2e914b7f421a3aae86791b49b84aa0bd62d6abd2c1eb5131f4369281689fe992b3fc7b878d2814562890e972e5f4ebf9cf38a24214d9dcb5c975ee6b82231c9aebde8fb3dba3aad489ef5b4a0f3686ecf382b922b09a921ac55246eca2144689295155645721233964fa8bb4f4a4f45825cbd509f39a3761c1549a9f3667f86916f9f394582582b59daa66b2a6d8a4413a2dcdccf42c5b64b91e8f12f2e7f483b4f991489a235fbb9fb9bbeb5a3480a21f36cbf3c28755251245e9bac67eba0bb852259e47cbd99c94dcb8222e17267af8d09b527cb4f248adabfcf31d8b7e79e4874f5f841d676143d3b91f89784e9175139eb889c485051bfd4f9274ff13791202c6637f1f194dd729a488c23e6360993977fb34c247c69d11edfdc554998484eaba67933dce84a954b249f4c498fc8d4711896483093ef74919a2c3d692b91a04e63e8a8bdccc59f124929d3bf987f8599ea2c8c4924dc788ee154fa20d7c123842189e4dce93b4c74eba91989a4ce4925cb29548e8d068904dda41d2fa78bea9d7e44d29d8ae13c871c717e3922297c50326b0a6acb3b258c4624e7204a05d5fc418a503322e146a587dc14b37f9645747776495cc4bbbc62ba56afffbe9ae6eae7159118fab974797fb2155322122db42b87579c0fe9232239bb7c73e85cd694fc4324a67a4b8b3de23e636f88042d25735ee7c6b0132a44624ab2e3a64d13de39881009b656aa32e820f2c2671009e667712b4a08a5dc238884cd31564e59d1ce2e271089dedba33f6868d2dd0191183d68874c414fa6ceff9058a3e353b21cc32f64fc90ec6526d4aaa60f0956a5eb848eaa29e9f9909c74b3528ec13e5b4aef21b9dfacc298cc5339ad8764eb7cf9dd2b699d340f492364e3bfb2665492f1901462ea76972f95e57b87e44e6da3c289af4f427648d88ed329a5bf707a538764adb1cdab9f79939a0e49f1d3ebc8cb5bed56e79074aff323c46af4c9520e8971ca7e2d55ae7e280e8959b3e7aa7d99f81c0e491a1ac455c9549b62de90b831c6aec678aba9d10dc95935b6cf892c3d7f1b92c3665d98b7a4d4d6c786e49c47ea08a1ae4e3f6b48f4cf31b899fc57911a927f366e2caa864b95a52131b3b56e66cf29531c0d8949eb6651b71cafda3a4362e91cb3738a1b9f233324962cf919229732245bfc91dd349d7261214382dda98b9e2958aa928e21f1fdf3c584e5d0973631dc96ccb3b4ae72cc5c4a5696ee295b2e6611c390f47d55232b7ea820ff09446c0b618021f9db5ba3a7c73d9d453d76900211111191c442185f48d06c2633e6bfb3c5b080210c2f2469556baac5a79a0a87d18524253373b7dbfb267a2e24e8be9f72cf1736a8b88584addc51fe2ee78796b4909ce5a393541196e5ff2c247b7956c6b2b6be94c44252a9d49326ab498a50e508e30ac95f99d2e3d70575a217111111e991c2d23584618584919117531065ca445548ceb0763265935fd1e25448be20b48585b1f558e92924e5f8b6b678b8afea5248961937212e2f9effa951c0e44ec62aab3d3e66be3a9cd83bf5f3c9e2e55048522a66ed31f7a52b7115c27842b2aef89928cdded0f3938174009142184e48ce31fe680a9f720cae87d1842419767cf367d5861b851006131236b3be8f0e26a67207218c2524aee9b09f64103e1f232524c6d8771d259f242478e9257d41550c1b72184848da94940c9bf36f784e3942d2c69c45854a2a23245a5de8a049b533da45287a6599a58b3bb1b738e5a9b29edd5e3a6724088308c917df57f335a9ed7a6410c610127d7cabc2ebc5ae5bc210425e55e75dc1c6b54d2cdbeaa9cdd93efe2e58c148d2251bd7f4c54ff7554464472a8740031889a7ef5a37465339fef78b64bbabeb937edbedc91164a46a478e20be481aa14de59455497a56073a502f12f3de664c1599d9f17820233ad081ae74a003f12231c7e0b3a753d6deeb2365d82e1233edef96f70735e53f0f1f6408e9e18347a93284f4f0c1233b40431749aaef2cb6bd3c68c78fe181c7e4223148cd7057eac458a777ecc04562ff6e58eba017b2191fd2825b24281583da185fdebf6d1ab6484afa3c9b8bf86ece5f2d12ee672bec2dc78a13ea802d0a6630f2346871b34830cf3163fa1c3d28e9c922c9d5c488bb68f2739d6291a4ca2aca27a1aba38ab0382ca69c865c785d0e0d8f39af2c7efb8a44ad1d8d337bfd3190e791e377b82251e6542dfc6dec76ff030d19413a769c217fad488a9e3998957f0a163a648ca07f2b74c86045526be6e6ecd1e5d8f2e182213ca0b10a135145724aea2d9a7e1b6e93468066a0918ac4a4396ebe12a533593cc7160868a0a220b2fab9b3280f171e4344a4036eced551572344628e7aab719e5e4a5f83c0423f4434c80822a95faee2a6ac15941b88245d3b5faff61d3f1620123bc558729a625364f587c4649f435faceea9ee87e4cdaa9e67f13e24a531addf514445950fc9b31d6fb4da5cdb0401ec21d963bab8f92de2c399f490209684b5f9fd7f4aa53c246c983851cbe961468f87c42b3d1ddb725a9acfdf21c933ea99bceb20d4fd7648dcddf64bae7d1d926355fc20749790b1f2744830ad993497c80d9e417348d054e163d66cb1ab457248b4e0622a059d9aa774e290a4f327cfa3e45b79cc81438296dc91794ec38ffabc21730ba7bd9b3d6e4870efcfa752a7294d9e36248ed0e17a4d2fccc6c386e49cef7e399b7fd690af214166bad31745df67d9d590242664bcdc77f2f93169484a37be5aaa3f2cc50d1a92665e6356ab8f8dae3943c29a14b5d1693324fd7898c586fbdce92e43621c25326b1b422dda6448ea78224e89daa88af518927ec4bbf75db4bad18a21596ccf2b6d3c0cc9a547e9fc71db15130c4931ecaba6aca5b7d47e21a9b22e5b506dab157b21596455c896cb57ecb40b89b1f3ac9bac4b9a772e24098d7b153fd8f585b790b81f9664a66ecabca61612d3678cf29a75b262b29074615cab72feca12221612d3ef8bfebd8d7e3a7785c46c3a936e5f8818911512d4b9fcd6c7947294568504b16dbb95b992e9890a09971ef329cfa1820c4d21297fb654d193a89b2e85e48f5174c692f1186b1492c48cae86bc2bf5b2a09078c13f67b2a896f72724090fda293de384240d6db2a366313db00802684252c878cd5c1532a99e04c08464ebf8ef95f289599b04b08424172b654296ec36350940094915f6d26a4478d6ec0b2009778e4fb174701f09499ee3c9a44b7385b6fc1192fa62449c8a0b4b216484a4fc51fbb94ee4670e1240119254e54ccbb431666c91008890d8415c3a0beba9aa21010c21492999319a772f79cc11801012734cc92ce718cf729f602497662c574baa279380916c6a33e7f511f3a6f38b048d374fdfa02733c67d91b82232bc36a8e6cede8bc4edcbd828ca548ae9bc48ca6d32c7ceeb074df92e927bb4888c3632ba48bacfb2b9415d25cde72239786ec91f7fb96d71911c437f341735791fbd4592d7fa6cbe678b643b5963614d2eb6d2b548baa42d3cbe85699120bb42d393bab348de74eea7bd2b2eca954582f61cf3b4f54947716391203f841471728545724a1a47bce6faba5b5f917c9deacbba652fcdea8a844f1f64e893235a5a6d4562127272c6e44e7854599194f76fb44b6a7e96721509aa4d33c9c85ac754aa22b93fe628e282053d11539198a6569b9e42459268bd1cba7f31de82a74836b7683107b99e397da648faa432b7eb7d3ca552247d276d6e719acc2745928c5529ba5cc7a97d1449517e4eea8569b05614491b979744a8d5ad9c8522413d695a901aa7770545d268ac67b0e4a132558c70e313c97139d765f498447353373c916429cc2dfbf8a87c3a52b8d189e4d7e8bba1662a9ffa8d13092a05f9994fc71495e91211791da4ddd8c4e93daba5604965cdb863479a256e682269e499f2e0d96f1ab61d3bca002224ab702313c971357f5a7fa344cc66beb88189f7d4fdadd39aa92e91dcb6e9c7e4a9e8ef694b24aeebc6a8f8dc6e41e14625125773f06a0b2693f65eaf7083128977aa4a58981ad78e27919c3bbea956fbac93a50216fcf9d3c3478f138888041903c7182f1011e1f14282948183f339c610630b372491f4eaa92fbe7f6d8c0682473e4e90871b9148d66cdfe775f7414c9a632b085619a90210871b90485242de6a58d2b3a911b31e2fa81b8f48b2ada0925a8ddffb508e15d9c10d47247a7cde8bbfa32fd721c38d462447d3e194507b3f73298c4850fe5ba7eb5d5f737c6311896a25f4e68af6712b223146919d82d22f6d3b0f7c784044043d0f7c380311f2e346222cd9f17363d0a2c9cab1a5831b88489675b3fc975757d6e0c6210c957bf9b2f9402222222222754084fc08c02d6e18a2df1ad9192a864b938848bde246210c0f2a89a9da80bc901d3ad20842a6940e03c40588b841085e944a6de27c63ed83d05ac6cd33defac55810ae6ac9673fb5a4321a886754d8aeb7ab874ec80d40204e8a7bf2bf53e16221434ed00304c9821b7f483239d35daad2e592b188e20d3f686539ebecd2da698975650a9bd161a512dce84392de0abb1f2a773ae8dce043929a92ef9bdbc1ed94ce70630f897a1d5eaba45fcedfe65821407cb4e06af5906072a3bd9eae144ab37948103a69de8e96f235d31b7848f298dc3ccd5876ec306360166edc214907256a2e8ed6bcbcda21513c581af9d93f7719328c1034208fce870ebb5187a451bbe5fad9ac43966ed02149e6d1b13a5bb8649acab1e543c899e0c61c92f3d6d584494b39c38337e4903c6bda9e231df4d05116e4870f12dc8843f2094dc983e7cecff4303adcc73137d85bc10d38a0e9b116e52cb9aab9bccb28d535ff2474463e8790911e3e4682e828037bdc7843e2fc9d77fbd7cbbf8f80f81819f9018210ac99c91104931b6e08620744c899c08d362ce0061bca781c8cfc0041082070630d09d2d33f96509dc6ef37fbc00d352486d2a284e6ec66fadb810f202342928e1c41d2909484e626ed5faf3a31a321c9e6cf93d01a37d653ba0337ce90b01dee74a9986b2cda39b67c0819d9da410e1f3d4682bce10ddc3043d2789a2d0f610a6e942169be7d35dce4e8f3518eadae203f52d082ac203f7cdc0d3224a5dd1cc477dce4e1fb3124cd7dc834ba46ff4c460c891e6329d7e875526fcd46c10c46ca8d30246e0c16b36dd8598fe606181254ad9c579041275b4b8e2d10b00047b9f185911102dcf0c2ef102244c70e09dce842b2081d44c9cf29c6f9f68790213e468214bbc185a45fcd7ba1ffdcffe2ba76630b493247772c0b4b85f0e08558f10d2d2498549d1317fa8d2cf470316e60a187dfb8c28e213cac90f896937fd9e9ca500ffa07f2630421c337aa809049c00d2af44048101d3d1870630a097fb6233fdca79ecce5d8ca1d14f3c00d292ce04614c8f81e507842026e38c187041941be1f04b8d184c458ef6dd9d639cb3652868fe860c75f21df8f911f890cbec104844c901f200841036e2c01488e2023376e28a1c71008dc48c248eec007eaa163070fb88104bb11ab54c08d23ecc861041f12a408c8078f11840c016e10e14f0f1f2308b8318405dc10420f93c3003682d1e375f0c0701e3a768c1cc0c62f7698213978316cf8c20790efc1c80331232df0c00946608202d8e8c58f07e2001bbc58808d5dfce071783c8f01d8d0458e203a7ae848b950800d5c08d1d143c72d122ef7eea34513672567b12d50878c0019413e788ce8a0878e1132bec7480faf4572c91b8db6af967ab44ab0418b4435a1c583ac7a04312341805c59e5e6096ccc223947d3eea6a4534e3a1fa2c0862c123e6876571f711b2f4371b820042222303041d0494444444444444444b6468e8d5824c61331730d7a33a54f08323fd0c8480f1d233d8c0ed4e347c8d0e1a5ce43c70e24a3870e2036b0018bc4bcf15d7e3486d6a44c818d572064760ce1317282b82248026cb4e2c9a8800d56ac828cef81001baa102244c8c88e213c186023150eb0818a740a0bd830057a1c04313c46cc0e43011ba5f833043d909113640136482164c7101e11b0318ac4db1c3ae54f428a4491f0175b2bda7357a7031f3c0ea28d5024788732e925ad3fe9f74a03364091a43e774a37e7fcdf208f9127633f91a0673febe52434e6dcd188fd187932ba6d7822297cabea96c539b6900f2123f7c3ac13cf438710204607233c18f901821048c0062792443e54a74dbd1e33f2417cf8d831f229379170b59ad152e4868d72c7101e239f0a03363491bc27c682d2ce535f990f21239fca056c6422f1d35450c9b2b3ec25f148569848dce077a7d2c71c7a71194080983362af0320434e2911d8b804036c580201362a8190a104901c049904016c48e283181f51808d48ec18c2a3003620919c3a5b715be5f731f6d0919211727ec7b73d22416efda655f7a9605b57066c3822492b27ad6974dcddec437ca011330ed86844e2e676f936a1ef6b4346246bb78a8a9914cdac16b16cca9c5b1ab18ab231bde2e696f48198213908a22eb0a188e4587b5932e36944d37e8c08313ad295a6fd4044c10c46d64622847c104494f1e3f0781e232668410b5090a8200505b07188c490b13958cc71934a691b8648b6eeb4e2154aa5b88d4224c52b0f659b4e4224698fcbaac74b4179741049f7e9d1caef4e93ce1184aa5e6975a3ae9a395c4b3385aeec4d0a44c2a8961e6122442619dd914a1b804890fdb39b33349ed0b88d3f24ef8fd0d6d77d97ea5214cc60640636fc908975ac6d8a7d855e99aaf85d7fde6df42131a90ab12abd67326982bce07ba0a3053d7cf0f851061015f010030448078e0e119191a30307085880831636f8909c1bf3e7c558d931e404c803bb17888808493e82888888ec303a28630fc99d622f74b9a668483d80630c1c2236f490a0b67de9222a25d9c11c23df031d417a043d7cec284eb09187442f31b7a93e89dd1363176ce021a9f4cc5aa6aa8da9d75eb07187a4ca9e6ecd53c7861d92920aa2f2eb6752bd241b75480c4d1b43675a8d35251d1237fba46ac720a65c9c43724c1dcacf44e698b26dc821296466dbdcfafff9b38d3824fcaad98b688c7f52da80436207952a2b4388f618f38684d30e96bd3398e651ba21f9d2a787905927de726d48d29cce77548f6cb02139851dcf3af22dc89cdb58c3729e95d2b865d79758eae69bc5a6b0dfda504362d4920b4a6da8a6936da421318ed21419910d36d090f867d7fffd1d9ed5e3828d3324658a7de947ce6be93b021b66484cb9bdadf6b07f3a2ac536ca90f8156eab840ef256836c9021f1455dcc3826c4f55e1b6348dedbf2106a357773fb061b6248982dcf1cdc72d9c68a106c8421296b4e72e675c5e2731b60488a1bfaa9c43dcfeff7d163c4c617927436d19b25e4e5abd70bc91f458fa9105a54bccd46179242b769eed2412b89d3061792f3996bfe94a3aa6ad9d882a2256a5e516336c64d3df4bf6b2f254ba64e5a48d6b720afd6aed94dc959c82c5e33e62a5af63a7b2fdd26a3c664caf6dbc04272574c9b35d26fdd4a574858bd6c1796376e38a91592624fddb27cac19d18d60a30a09f366c94a74caa94ca242528f5e903974f8123d4f2131e65f10ade8f53a5229247f29996536df96fd1285a4ca9779ebed2590df1cd880427290f58a96729a99474f480e3d5af36cca86131235a8dd34322631f3add8684272a8cbb853f79b3e8e3698909ccc374599d9b01da18d2524fdfd9a89f2741fe22a2131bcec69ac0f17543047602309c99e5a834c799be9d8d182175286f7b081842499ef7c3cecc77b9e6c1c21d14be87c3246891e211b464814b9d821c493c689080216e0b05184241d63a525959dc35ae8870fb64184a48d5796bff1e44b25d9184282ac39c0488e113f954b29ee34f30b937f2e7996625029f71dc4f04552ffb99cacf4c22ccdb0e93ffe749a17c9f6a6b3b6a9adec5aef224198e63e9db7d1ed2d2162e822f93253f0caeaa7f2fe5c2447b554a1fd3a89ba9a997f181e23e82b0e62e062dbda17f91d3cb7488e95161f84af39d6167f1e3df5b85597f1538b045156a61b639b7ec8d022c937e6decbaad1baf52c92942add9b82e7f40fb12c124caf5a944c32e9edc40216897b67524de7a0572465a5b060497fae488e96bd7279a76eac582b9252e6ca8b418f32cdb118ac484e49d89adc19b3e0965524587cc55819af54e5716084a02a1c2d31d1b0d139e72f15d5e5cd9a2e23544543457269109dd57ec783c59c02b9fd6141a65395b14da1481519b44a598ac4284a460979513fd34a8aa44ed25be542651ff9e620c628925f2c97768a17e476174592d4be34f22fbe3a2814492383e6f5384a2ecfa0481e3d5161c52b3ba5f0271ecd8dc15f73aaaca42712fb72e4b52f09559e5a4188d189e48f35225e7f2fcfe39c38d8a629b526feda623691f4faeb51c332ad7fecfc2086264ef329b7076526924ef766fc18e244671026d0a04644c3ecf644778904ede7ffd79d27a6b51e3a0c10647d10c312868bcaad499570377b2ca59e478f22258c294b3ffbe76a10a54924997ff2984aa733ed31922899501e4a854fc96f8118914856f3b68df59c7fdef2410c481ceb634c2756a7ddceb1f5427454084ad09110e3115869f8ab178fb11c5128d3963ea52e79ba1c86071b101f424c8bd188a44e7d3129d9b86ca22264088f9132800829c50373021c63e0e891021c63e01803c70fd9616080630c1c6655c69700c718380ac8e708624480630c1c2dd0523f0c8f91c288d2ea43456335b3aed243c458445268b615eb1c56c3b3e587188a48cafed713ad4139b6321109273ab542dfaa2d543f50055741ba04622022c9aaef327dbaca10e3100976c94e5f3279593393632b7d20862192f42b7baa2c7666252a44521695e4f456c7ca4988046d5dba1b653ee52a9d321c2e0841d7160a6630120022883188c40e1793888f1e3ce634c7d648c5400c4124eee6b0dbf962779a9e635fd0230556573c74f40dc4084472cc9a29c96953e65f95b383188048be8e9e57e2e78365bd6110e30f89c1c3d7765059bf54dce020861f9292b2df70153a37c7a5b620461f124eee65cbe2bef241870fc9699a7a74f6ce5aa5d741313688b187448b314737a93ed7b6a8410c3d24bced9c9a8fda733ad282187948d4b87bdd78a32107f1f1e3c020061e92c39ad241dba70efd81fcb0c2e18210f4f8821877484af23f7f89dd764850f24466fa87374d6131ea901caf2e9a974ca1bd0fee910211912038f0e123c796410c3a24884b5149547c2bfdb70a62cc2131357789eef2b2c420861c12b36fb9c83d3bfd8f6299418c3824c95342563c1dabed8339b682f8481d8882187048bc685a194df3e9eb5ae920c61b922ba5f26c23ec634da76e1162b8214944e489ca4e99392d1ec031860f0fd81ec4684392a95dff121f65cddfe682186c48ded29b4dbb8873cf3eb120c61a12d5f2e6b5cbddf466043b7698b382186a480e15691ddad4e61f3d1021235558412e21461a125b4ddb32295577f1d190181f4764a71679ad416748ea943b65c5dd69a8dd0c89ea7ece9da45d86a4f09af74e67cca429244382a5e0a3626bccfc5ac1c18e21de3b4c0f20295000e0428c3124451b95ec536adb74713124da7832a1c5aee2066518927e4478144f17f3590443528bf80b7b3ae97afc0b89e29ba556940a993313c30b4942b72a4751719414bb906cdd25e449713fa5f25c48d02e2a7dfebe680ea32d2466ca8a6332a74bb11f2d24288bd9fac4b2a84d791612d3e8d44aa64c4ef4060b49a9f9fa1696ede5bf429287ba1c9bcf0e8fdb0a09da9ff2dca7b60ac929eb695e7f950ac9f1f3c94f9b72888f4f21f164900da1f5eda54c2924fe6bbac698b011a7282466ff6911aa74fe190ac919cf3b469919dd2d3e21e9c44919d5b9a7434e481acd67991d2a987bac09c973d5d5c9b49e7b8809093acefa58cab92a6c969014bf6a7564cc59bf2503319490b0ed96bcebcdf7ee4f4262bcc80fa12bf8e6899090789b29efcfa7083d3a42d26c0eef2d3a5aa63142c2bf9b98cf94234611123df359fcff70994e2e06119242ff2817991b2ec72ec6101253aeb8e229fba3e82d86109264f7f7c8a48475d51d8ce4da8bbdcd5dbd3b36301277ef3ce34659cc60ff22312eb509958f9edf675f247e87eed318b634dadc8b04cd3f6a36c7a02da9971709c29210537bfa62a87717c96567de69e4a68b04e951ae541075fbd6e622c94bb48b1a1d73454971916cda2d332abf4a577a8ba4abb4c9fe54e80f16b545d2be9d8d0931f122cc5a248b570cd12958524b495a2487e5cdee2b1973fecd2231ec2993b1335d27f5c9222987ddff347e29f3bc58245dc8d6a9df53bb18834552864c515d8bb12b6aaf483aa17454fe7c39cc325724a90b59f3af5b91702737c6b28c5b32ca8a8419155a974f83ccdd2a125474cfa53fe60953952a12e4a89c623a2925a455a9483cf94977f20f2a1253b8cab1f941eca64f9194d475a5285e31c1004c91e81eb3c4c27a7bb65230f31d1e166cd3fa4787ddec1117d71a0029122ca3666639fbcf0d60144965ab214afdc5bcdb0c1145e276dda53b15e3190c201489393bc2a4961096a2840a8a84313932aaf666e711ed3e91bc5b194545dd2a6da11ab23218802792539d6cbaa991f5387522b16390953e9792aca07322313b74d0f3a6b647a32303d84472cc9362d9fb23bd633491149ed2655bbfd63a9a89a438efd85227fb016022f9c3536751b9bf446266d2144b578fdb6f89c42c514aa8a0947c9a330403a84462d0b33c8f1dea0e0c801209b729423f75dcb2701249294aa88c9b3de5724712491b1a3288d2f3654113890479d995a6c4d58edc20916cdaed3ff65a8f48f60bf27333ef4dc9932312ad934ab5cda446247529ddfdf61be3a81323923c688b99d5639d635a4452a8a496e4a7140d31292249c45cc6de936ce7522292e467f516cd3969d01222923d2c542e1326c468e9108996645b4caaed4c8cde10c9e9d455c959126ff2172249fb8611eaad1bd27c4224eea824caa45fce58428348da7953e17f533d0921412458bcb8d79be4790e428148f0537ae75e71791f048884bd9c994ba7c4881afd21713346ad098b273c8cfc90e029beaa9eb00fc9bba2c9459afa942ec487a4b4965257e9f39e7b0f49bed6165cdc738ef49098493d5b6851d57a1e9294bdb5274f6a33a878484c5b3265c87f3e3d9e3b24894bba19ff946750b243e2cf65a8d2acd33975483a6d51f11eadc9733a24650a7ef79ff428939943c207a56247df2e0747be5d683da1c72139c47a6ebdcbc6cf391c126654be1c73478beef91b12cb4ecce475f8dc147743c2290d3ab7685679501b12e3e94af2b35c8ede860d0997ab63847ff78c69d69058ea76c3684a692c346a48caa35e42bfb6aca9983424e8fa669dcce8a88b414362f49421442f36c7a43e4362888be551b3535b509b2169bb325b63b40b23ed3224080da18316f5712a6d3224c95b5fe6e5b4592c3d86a4ac1ff79bb3ac898e18922bcfc94a4a7e0a9be630248f3aed8b616e2737832149affa6d694eea77e62f2425cfa333d908cd95642f2426531e4f6f3b5564dc85e414ac4fb7ff29394a5c484a5145f34e796a50da42625ffc94b36ff8361d2d2449d3e1f22909d941be5948d41d8b3173a65bf3c54282ce24b4a9789a84b6f70a492363b0ab1c7334ab6b852425a3a89495a7b4745b85a418e7c9fc47641027a542d2f98de610a293fe904e21b164fa182666368d46a590f4bad9f306bd277bd4282426e527cc2d99298b291492cbdca2cce7511d9b7c8235627484be3a9d903c621e6aa3e6bcb9661392eeaa3e8c67ff9f9a4c4892ebb97df409cb71e6121254cc435facb22cb71212734eca44bd9c84a457eb95953b25d34542b25f1cb9a3515f737884e4a09eb4258fa3952b3442e251bf3f335f84c424846856ec4784844bb62b7fc13aa6f1063084046da6ab4cbb299139031042e226d11d35c5e3874f309244f57996da75571c189f3a75da1f63ec1789a5b92957e5ec1c35f345c2589cccdf65bd4852b567a5b456f430f12239a8ec9341a80fdd9c5d24d8684dbe17dadf575d2459306dcfd94467bf2a1749d2de828e39ee5c12c245f2c7671137b2faa3e616c9a641c36e97d0a3ac22005b24b676caa1dc92cdcca830f0c08ef3484404c8ff08420601d422b133656c051bddbf595a2498bace0f4bb3ef412c3208601649aa937bca183b64582e8be4f2d09ce326bd9d358a4572a6e7d0e9c48ca9dc60915c41b8c75892ad7b571904f08ae4a463ac28a21bbf42ae48d269b38ad21a6266f3562458c86556dc303b716a4100ac48f4100df2bb2cadb2050d02584572ff8790d5499cf0f0601504a08a04cb9b2bd672b8dc5ae7d8e2f580005291781932855cc5184bed07024045f2bdee6e327dd9b2a673ec8e1d660c211f822c08e014c9bbf7a2bca328f76ce6d81a11f2646c09f910404100a648daf97c398acce09d934a91b85d6954ab655c4c132912378ea94bba962e9ad02892536ff4a539759e4c4814c95e2ae6b13756ee1f852231c60e325baa145024992ca97e7d327d9ad1279294787b909993f0f8224f2426f1142a5f9d1dfcd38984d1ca31c68f339e520e2712646a7b8a29db94936f22493e77be7bb71f65724d24a69af8fc95e65b3fcf44622561ee39f6c5ec6430912cea4ea78faf9a52d25c021915a3c37ecc5822b97a7b43c7d9145a752592743c4fda6208254d892991d8a5c3bfe8e4195fa49348b23079b3a2766b719544720e391ff4c6e7181b8d44d28a4c979d534ef5b3422259c3c22f59c55476e72312e7c46f4c44e664c9744492a8779dd7c8e674c946246f4ca9a3af9268ed9111c997f629e69613b242b888c4d49a3b4787934b1b1591e47ff75731e518b38989484c396cca9cdf64574444247de51c5c3bd8e8ecdf2192aa55836ae639251f3344b209d77495bb4c9c6b8548fc6eef038dfaa8048e6543b250200a06436150200c06192ed701b31300001818201146a3e170200babed14800350543c522e2c18281a120b84e36030140c8501a1601008068381c140301006c2203594909b000a2a11594e3fe036c405c5883ddc3e418700be1e083f5d1086ab8e07a88343ee8e8cb125bf45db1c8fcd9d385ea24a4d2038e40dd60903577005fb8d4be833db3ec7cdc27136f2d800a94ce3c5c8b0edcdaa86fb63334610139862584ac57612dc106b7900db133bafd8dd2dba7435503fda81d8b1ff575e2e47420daf6e4bdfc66eea1921dee85d1ded846ea499ddd269c0bacb0172c7562d6fb846bb08503e277d1aede6cfa80314995da14124bc830706efbe5862cfe08610155a92f7fb130f4b22a7076663968ad9183690b6686b3283b2ae4f4f31331574f08c91473310ceb70cdbcd5cd4c1170ec3402fd8f11c5f58eb3d31c6e8182056479876c4cf2cad7f45d5e7ed97e1374dfddc1197a04863f6dbe22494cc990d50cbd4605eaa30bca97c1b4ea7df3937103de6c4734c19546bd28aca30f3a03cd9823f1eab1966190e9b02bd0b7f2a5ea4384ca0d0664c7223f884c7d4ced56b49d48ab39e15ead14b1c6562b48fc5798a16ce2f9f9287b7c00f4d981990628faf42b60026cfbc43cfba7bd01cf5f480ffba0bfb0fca8f441540dd341f077744d0d115940d8bfd6292b50cff538957f1b34940917bb2323a44b2c6c085b52d69a874c3551fa7c34cf7ba023b291801020d57795a30c82a78bcbb0c4753450b2cbac8357da429b530a3f0c7be9027d4652fb9c97f3f9491a3d5cbd0c2807f9af1be9de387e04ba5c0b39b56f744b652544f67e328aca0d792d6981bb2cfa2d6a16103de335eb23718350f17295411ca3d5c46d68ec06ce6dc2022cb0dda990b8ebc8be83468efccdfd065038c317ebce09a9da0dea441e329eec84c3821d96c33149579da5a4f167062bb35aa7d26d3178e60f17157445e96fdd2d4cdb5a0553d90415b93ba6b1b425f068c6733c38508ef5bd7d041d58a0f468371f8d82b06c4c28c6ac16317e86adf20d6734a414c93ac77ad604c6ce4b8121e405e70383c4a00e460c4c2e718bb34cfa9342153ec165b389e6537d62c7205de576e1281d24c0f151c823c23c3496cdef1cfe0ddd8a3e8fbb690b226e8a4dedb701a8a36e8821d14234b9052b5c3ae69d0fe9e914b794d3b69cac675319a6415bd4062cfb3a09224480fea802eea55c6529537bf92e60466fd931cf454b58189c059d40fe1283a524c7dabd613ca246f353f0c69078ebaefabe402ea8c3479eb055e1e009291b8cad2020035736f4f1f45c58d215f33eb936a9438a23663136cb37fbffbdaa902fd02017cad5f095f94ac0b6625944f31c614c72c2b7dc8da0a04c5d68c4f4a3736766ed07dc59bd0a77888facd160b14f6e535d5585caad2092b6492366736554e445840aa0351f09af80f3f8a8c339838a3aa6f12740a87fd322110cf034a32295ee3a5a598a97a09febf264aea274a1ebb5d27c8867666b5e805e7ba4c8989ca31a6ee8cce4b6b1b5cb3043af403df61a9d29e6d715583305d9b8586740ebda77152302b6070c4de573563d7f7dfb4c9eaacff1828d0e161e62b0bce99bdedb6a4260c71314153ecbbf32255f90434bf22a393723195a140f97ec0987bc845d9ef95fb73570415f6474d0e1c0743ceb8407950887a571228bb603a791c38945d0b2387a23ec016fe7f0bffa70bf7bf318ce4bf14c61cfb87625de4dd2447d5f1f6910661cfeea378df0c67a9b739333013e17f95e14fdfc9e07b328dee334998c79c1119f8b5376301eee5491a80f5cabbcf98b4333b095899b566f7597c76a7c569778a38ed8a1011b81732c0f5ec4231899ea6d0a8693a05354d45a012f4042a414fa011a44d390285a7acb7ae77bf57c62782c554e8b723d9e6bf726f939e0c7aba282f2535e71299e0a535b2126fac0d22f35472b7d24f17edc687cd76c34b32a0a348f37a381868fd75fbe8030c0488a175a0b6afff2be2a8a53d152bab15615533bbc67d5dbadde7872c64ade91a588d40f61a852ddf2d333e112603178abdc2a53b20f9359b9b8708970f8140d680517f068bd98aef3c36167d3b9206c1d3885ac605dfca6bce960e11690b3163ca49ea967dfb1474c169dc8b39713c92417684427ac48ec0481c35233c624b34792ea55a06a989f9529dc9b7e94d1d6026449f6fb8161a9561374feb0840ff486e097ead78879b71883caa0f3881f7183b0e4230248f6c08813427560542c59101a191d6c4c402bf20950cd9a238a4452c0888449009e191069119e100c09456bfd886d14847a2f57ab29843a824af5c7ba3674f46016ab9e1d956e05e49260e6ab2214cdc9d512be01316d214fdb5282000ea87c3b2fda6d6ee28f8928445ad32cd15f764ad894e8b613a9bcc2030b0fd86fafd6c44c6820d8db0aa9f81d53e41dffee07a49deb231b711902488fc7705025b24f515f3c74b055437522aacc418c438af9e7a0a3dc2247245a2e215063c35025fc0892687b0b470652e1b2a3a9c04c185fdc587ee44e0dde905addd38a03a5033da12f142e344674b7c005734e44f558a31a25fa1a6599a9ce2f8e10471d1684c34464cf391a31824aa3819cb1291991b72c0a6fee4e58c4d4f4b6f7db10944bcb303f0f3a6409ee7e9a91ee80bd494d0a06558f05707cd0db35c5229028ae1dd8298a8eecccd7f4ea4c8a5f153d785b44a187311fff04892a0894e49a3d0a86950b5ceac154bcd272d879ea367d3e1e8b9340c0d9f86450357bfecf9517ec9d25af4667a8bde4a6fd0b1d39b682c1a3b8d4563a03168cc34362d338d89c6a4b1d368d2ceda46b6283bddc6f6387accfae3d82100950cc183dd6ccd25c0e93e136657c285033f2cd442be23b3225f49dc1749fb96fe995648dbe426b999dc426e90df5cee30b749873264ffe438099e0427c193a048c48a3435685725cfc926e423f9844c934b04718236f0dc3393198372938941898913dbc47689616225312d653331293176627062e5c432314d82495005c9402d164f451cb325d50610844016ca2be57579893cd9464696f9cbe430819700258012b804de298014280a6c0235014d80df1cc190c60f3b4f67a50fa67f7a7402fbe8531e68d73c1790e1bb1216cc4d9c426ff1ccaa4d98790e817325ffcb5c655234a99bd4261593dac46acf65a899198289f69a5f3203a20ebf022b0540817702802e18f67564f7faf6c818bd0b40ab2d20715da9ec11036e7c18f7f8cc07f6689846dca8c3adf0df6f07afed2e56f7801dc39c0600a99524fb487e49607625dfed0814486b4066ad2579a38c2abe3db1111d6dc6ca067a688bd160a03119f71f8e00068ec9ec0fd64440205504d0b658a346a52b8ba68b1db0c884c6c7bb23fcc8b26bcd3cb746204128225bd5692548b4116f434f4b6d0f236d903e55c416da6145a6b9351a5c66d09405c14116565bcb2420e25c111bb6618c5dae575886e2385d34d89639cddfff71830d39ed6f7fa1adcd2c79c83437986057a06e0a80ec6f015546926fb7a23e8757b7c90a47692b7bbec3cfaeb552af6091e8dc97821fae271081a2256873f6779de8fe33d19969b5e6dc29c96ab0761addcd50919da96916a7755f47ce505a00b37d6c1472b6efe01dc20f00d9c6643f266de30d7a0efce25b81c0b4e8555c9792eadfa168b165cbe22b986616790ed31fccdbc0bb7c3af1cd946aaf9b8442cb53ddef873603cb9b10588a42e8a78893db3c5c40ad57479741a3b0cc2c742f643af31e40170e4184bf9b6e3ff7e1bacf6e5f11409eebf13dfe8fa24d4abf0bfbcf96463bf6214ce7222f55164fd7847083dedd6423d96e33af160f6bb4672029861ee89fbe520e82006eb63139ecbc424c2e15986e83257c69ad89b5342a738ebc74148b3e088c38bd5ec82c6965fa45e28d52e9137b3947ea3af34655b8191ab113baefe291b85c0e8d984c680cfe491bed936837dc01b63228af7215fff5ec15e6a6643849f1d4e9d883a02baecd0cb63c777c63407229a7bb8857c931cbc4904f6bae8fbac1632ba0872a2b5991884216ea6ee766ffdb37ae9b7672d6b5c8239d42d201ba8a64d757ed27a61f168b15e0a516015b5c5194c4599a26357c567120cd5773b00ecd633b4400c26f9f77d8a4b05701f8a0c4c6bc71313b5b6e4524bc052902a9b6c997e363820289d1dc632696254aeb4e1e129f3a3bb46611d133cf83f715cfae7136fd87c3e3aec49094c791fb7762b64add5b0bde87d0c50adc3f982a04fd381e3cd837954c008f9f9d850d44391bb1d9b68e09bd4f76ba9417a59d11b4d1cdb763e3bf9a06bca4e621b01f0c03460eea1782e8b553387ecdcd839b138673224e63fb957ec84aee833bccafb037a150497600e17833528ca57f224eecd31ffcb7220f8519e6d14f4cb26da223ae2b82d4290040b753c9322f35f8e0b0e6991d01b2329b41f46ad3654b3283e87a6cc3083639a9489daed40066d9c23986a66a63c6ae6c23a80394a0034a9f6dbd8e4c2da42327786308c7bfe7d3bba67426c1690901eca0d21483cfb9b384ffb99bd27576f153a1af255ee8b539a671b058933c4e5cdce83ac114d173d5431d933bcb8a691fe63905bb553dc8d30ca9f7b04c246f3ab337fd4cb70440064bc6b568373299e05de3086b1b59ecf853a8e9fe95a0272af00a5cc024b26993eff0c1eccfdc14862c01e865caa4ed167024072dad6da7fc69dc793b498720d90d2f8c91903492dd15a642675c85eec3a7a628184de929566a87f0905198fa648ac2c6d03c0b5cf03ffb970eaa2b1d344cb1c5c379ff215e53bdf33c8d58d5ebb25d499d4af834b2d36be1642308aa5b5646fdb470816d3a9ee04b5ad1aaeb5d3b2ba61496dfefdb3aed0bf60773ac7087e2a43d2e0b1bb755626c574f0650d7a3c8b3894b4f3d88b76159f840c25a2ffa2878da46d447fa6fe8752e52b3a8467886e3a83770c3872fe18480729e105a7f144c10ab9194731eedcd25aa637c4f9a0b24b83f742e5551c449cc18b5a3437e6c233b93a50c7b893dc5bf10cb028a5937bd43c890f2da379f0e146a42504407fc90dae3663aa2775adea1687411d75f371143191f53ee1b4e6b9218fc078150b8c94151f1ab18e4cc449716004eb81287c980e43b115287bd8f9199013f3b51fe8ee440ae4ca7eb5ba52295f7b76c2ff0f52ad5e1ae476e1577e93ee631cd16ad0f2e14e6a1e2c61a254319735ffb51d9454223cd598bd22b29c9faf02ef139cf40dc9f9f421c980bbe2721df6badbdc74a0bb46aa3e42445e5bedcf3fba6019caea8ccca4f7e69175ec2d773ba07fd16471dd4e6e4976b5272d5b026245eb010c89843b4daea962a078fce7c1b5ff7521bb2be81b6f787bfa990c178a5fdbcb8876a5f75d857baad96b1f5c370a0383e6b6ad7a6ebbc339f31f785976de16780e1b053f71a0a579b9ab75c6ba8ea481f528203f436360e9ccaf11f9028af7b01ee2e86155df3d028bda2bfe4039f20287e374879be25db565ffa9aa6f31862af687134526f105b598b387ceff6da5994994146def61ea236852e5f4bfa09bc9c243a91a1a37a15891cfdd99ea0cc0656ea058d6d94a656bfa32d7e3e4f5279add10d1c1f83c4e30e27e892d40f866ee0b05201285a70513bb062e2cf5accc068bae1210f36901365cbf91d27e2aa1468a120f71c30995b56aed38c06dac40847da3714e8745d3137a63a415452244ff87645c8410dcee89aca694ca3a19e3dc993bc4020d6a31b265dcce2374047aaef9afa1dd704764d2f222cbc4829e4ec8812413dad923b4119153e19d02b535c986cd51a80fd79b48421196b801845a12f17c1980ba34153a77ec0717de3cc001dc7d5225b7135e95e3e03680aa677d53ca6fb6d57d4d9575763ec408f27f6f5576aae9a02d5ab3c5157f14a37c3f821f302021cf66310da96756c11bcba01a3964365bb6d95f7ba2a0abca08f7fcbe497553d0931478172f6f5c04eb4d6525497767407a2f0cd0b48b8b85c2e6b147fa0fd97a16c8277f90d76afc7b0873c86ad127a4ed118c4b433b96c0a84469a0f5198278a2de8385b4bb80b491d616a76f0cab05af30de50e22f2929bff037aa886622724f3fbbddf749624a03c0e6f1be5cc3ae366b5d4ef3fe05622d1cf1aa2468dcf72f3ad29bbcc11df8ec45753b8de94d3cc73aee7f048f89edac655533449ad1cf13bf6da694ac7b3e2c1beadd159dcb59f5ccf670f6a7068af8b933078e637b992e827ca02c455b6fb6eaacf656086d4dc99c49e381fe7d5afae063308c51fe7c4ac451e6353b9dae289bab548922d8bceb2938638610d123b4eb838f567d30149be89fba7969a89a820b18ac4b48451eaca152e9cad013c3cda940380909b0d0b7bcef97a1ffdf8184ea7a73b3925e4e25a85e6e9c452f34daf2a280f7cacf2cdfaa0ec47a141b1031285526a032a3340867e4043d706acdd7d9334ea436c431f193b6f3ca28b3a25d4feb42d12b1ea4a0c6d4850000accd266c5ba145d9a2e07129d3ae5c4b7f575ef93c6c6364942c6860cd982213690f1a3f86593a302497739650003360682720a224dcbe9100dc42fd85294771ec0ab31a3cd5f7381811d67af991328e36b6784f47389311a3724d28b30f1008e5b536829177fe430b80a7673b596d7a9d2620c6cfe208a26f2ab56e115e16b03538e1904768c9abac739c3f096fd75417888f00d392e189e711647bbaa46adca19ac3d7f59c73d76328aea048cc5678f91b7aa361650d85ac9250e4abd283d982adaf48647ee227adc5e025e8648cf0c0c945eb1564d8842c1c62f5509b2a73e2780b49d4714dc7471699fee79fc3c9700c4de3228645584b62bd66cc5b5475961204610ecfa58a58d42ffd4b766a879c32536731f6b5a8139084deeac287870d92efe22cd91f68fd37b024cc2144d39aeb44a61bc698502a74821f967d3ca80b1765f16014379f1462ab228160235615da91cc9841cfff2f9cc03d74b6a235ba63b12c30f497dc9354c12c25a13728e90b81f86c930cca7b9f90b6d16d4efccf151980af01475cb0e8c1b2a1f8587d2947005ad58bf71c754fe727e0863650a6382060a05fae1c2b2f4f777497bbb859263ef4bd4f0756c05f69475cc38172e835a0489e441997af0891ff1f6515b99dffdc1109eb349514a4b0d55c2841bc562625b779dd64dbf8e72217a3ff4f2ad3bdb7e9cf4bad24952a24cdf0afb9477b54174da07fd5e7583ae0c8eff0041e79ec64bb22226b71db2f0a173a0afed4485e0fbc835c936f131d65abcecf0f876b2758811b8f059fe113443a4bb6958b238b650ee6ba02fc2aa7eb4d40f3ff88ebe8fe69c9bdc3a15b3884f64361782526f01120636545fe57167d28a48e66de7e422485d23d7f6ebf18fe838e2300ddcc9c6ac10d3b18bb54e51228ca909e772db8e19d2b8dfce1101fa9e014ab322656c7beed8dfc270229f221308cb81d72d3c300d743c27ce9217936ccd626a4f03d3f37904f5a580d2a6ad990af57166b52306cd21c5ef487958fc586044d7d37200d56de69a55172827388656baba6b6912da9b2c35024bc629adf5cc4241eabda30e0c35f2d79a09f572ac7de219619231d02aa719b7f1d91f6b990069d79794dcfd58469082832daaa07c92e22932b2686d333250073b3425103864ff6832d778c384094a414138dbdd04045d3e38dffe26965cd20b2c30d151c9404e99b081c4932b38c0b542b328243187c16addaf1717d5bc051e6c8144a1d3537140b21dc26352566871412ea8130ecdd8365a7cb38633ea7d44a8e37be7fb2c0c2ef99ca475ed5ac551f1550c4e2362e6219f40f6ea10c392848c4880db00549c707fd451f61f5276095e0349e1d0782d0edc7903babfff1d5bae7e75535b5450551afa3cc14a424225702d8c05b91392848fd255d9ab035078f936a2edabfef5869ea56e0a2dd48f598b6f2e9e2df57e9fd2a9c6c2c0a1479512f62e95e7fd536b7c4a8ad4578ecf2e5d26632ea36e26e9a1419846dca9af858d2a0a609a2e096fdc6f2c3ca86db59d9420f9ec66d68188df4460b4e3a661856923d5cc9d40253344ddb175e02c7a077c464d11e60c4d519329e1dd21b2774b1a3c7606a75ff44b1c990cadd34e4724f14d5aac76aab08b2a07fc8988b271e1fdc3b19cf52d326f9dd960029d91e2ab7a80967ab34c0b82ea5aa5dd4f0cf27dfd33ff7f1bea3add79e5fc15a5453ee0ed8d83a5c3eb3cdd07ecb294070ad762d3d63b900a4832a0b04034d31e0522bfe031f5004668322d7743beaed9048b100e93d916af872dfffe2f2b0a077a518ad37152e288e5862d5819434c6d0dc03b2971312d95ae65e093143765ed682adce71cd30f2985761f81e9bb4a8ae5d87ba466665efccc9811314e891daf1266543ad374ab14aa259ab7540add745f9dcfe42230ca1d60606f8905a148d2a1226075a6101d504ecbe9ba2ce9ec20b905008f45eaec91207fbfa02465bbd3f0133c789878f5f7d32a8917ab895e19a8f60f57afbcde818b2407046cfcf63a9148670fd6ecbee73181264a3d87e1831eac0d336770195fbbe8203702029eb97bdfb053ebb8c3faad48bc6cb0d9b82673816df9514ef6716dd2c04f734417297787d72f2cb15638ea70b2673c87f3aa5d223cbe4391ccecf00fae4286ece387ffefece85e9b08ac716d772e616b38332c2b202b83f1c212c7121e5607d1aa5812961843c290622018bbd08b1c041ad559d4aec82e6a576417b52b2c55ec1a4639465d474da324a3d846a1512d53a551c728cea0aa11aef20c99257b4060f867923d8f224e0096c24b1b4df2fb522cbfddb3bf17007ff24b14600b683e77061d8b4aacc3386a0b2e36c079ddb31ca8e00a0fb63fcc7dc4e18266ec0c96f15b08cbed417f857351cee43e75682e1e079f391dd5dde7ab49224a18394656a8e36834de8b1f9b4574d3649c98099e681116094e60448258089aa0675ab5335430a1126bc9deaf2b6eb78dd2f92a9a37ffe8193601b6c03f921e9bc15fea0ccb05a41147c60b6e742fb0cf94c41932f81216b1c181ca2cbbba75161028e64abcf7bddb6e81804626f85d34987e5921a1aab09857cca2cdbd76ac711de0541a62a161ff9212810ac74238f5f0b2214a860e48bc9a789b042a0904bdb0b69f9f227ef292669f3c1bb012bcad269c1d8d70b3bb9432921129f3a02b9cc019d43834ddc7c147d16ea15adb268296a2d7fa31e51a2ca4158f74d97c1362666625469aeb5c62b76577adf08ca7842c149611690a01a4816fc20765dc7dd43881fa34492fec9f93af1705a101e6725e9204a40668469d8c3b6118d3b36290e67c48e752c4b1bacbc4a57f9a9863c5c0df4d2c50a22fa9f38d91ec52d455c74880361eec583d70b577814d30ea4f08fe70b252c41aa6f00a297c99c2cf783a25582b3657714914757b7a186b7ffc91c7df07fa2bc8f3bd336629c0c2a011e224589f6ba04945d5a233528d931608ba4510d10390c37e2084268d6a4a1d121792e4c0965ad0776ca16de7eb5cf814a75b791738789b85e5a8bd0a11eef0f05d5906379666c4ff2b414a48da2520aa551f52f85288708e7fa675a02e0bf4422f2e00a298dcfe5dc8d25472028f66ae568bacf02e79a63efd72f023a8ad713d5a5860007c34de4e325ca51e8fca339d8b6209340929c7535306b18d26896910bb60449ea139c96970809734ec7f60e430653ecc16ca4950748beb6b2878ff62ee7df9c18bead36a55751bbf87219c753e1e5715c885a5be19222abe475bf5288899a92ded7238228a54d6675d8df3219eb87a1248c57eacd50d041bc520db0ea240851bef8ae533b759a6f117bd874416b043e235e862b9588b409953bca74143ade54b2a8aff95e019652ca18ad2b2d2fa3f580a22b7a21b1b59a8d9963881474a2ac3bb806600876782e843070e08f2342243f68d4ab1c1de52d9da0f7571be9eb414387d571dad99ab7b8159657906854af847a5404095eb08053fb2294e726d9481cbb4f0effad24410b226b0748d104f4d0005febca76a0ace6a490ca0901c7e703ea2b3c4755b269d18844f05579b008d796ab8fc4d83c055bbec7ef4b696ddd31828bcf997289034a15d8abcb13c334704c51f6d62a1fac8d3e5f273716f8ca2881660981b109f7bdeaacc1a19638e6aed96683f88a034b2d396fc7416ec8cd9b7444d463c6b14dffd107cb069af158c5e20d3ef0b6edfff7e08b93bf99dd6d97563e404212337a250f362be252997f3ceb239ba588a0db57d096e740b3512eb765f375e7c59071f9acbb14f637f31f7c9cc94088d017f8d054bd61a6994863156f67639c6a9970d8ba5c9924010307d1da26225ec95f10caca9986984c851a4ec756e00c43b957a47978eb184e687fc7680268b97be310014241403fb1226622d587cd7df10ffe6ebce22303e46281ef3a5d81d590e2f421fe1003480b7d52786490971f2544f0a20535f30b453dd09ad428427391621f839613d293d67dd7095fce2e1dae15812fd908142062d6a519cf9417537a4a6db435518fd14d79563e1e6cafec79c2ae0aa41ad112d361e02317fc461916b431b8001d0961f1a8995d9a0fec5d5c0752c1f9b5ac82950d799603dd1248a852a47fa078e36dad5794156a90d9ac3b3eedb10f20e931dd12e597b660bad43a2fa72b5af9819d5c2c8dd56e1f6dff64953e0decff826edeb067a8aed5d1ce62fe8d27b379361647cd362ad8693a900a2025e80c6feda517e5303c08257cd5e8a96a89822d37ff4e22ead35566ce86e6e9df4b4887d6066d586931daf7b400ed6d40d4b7e6eea701993abef7dafd355b5a1510c62a3348c2dff790ce229e86f5c00039f10f5df15f8b0cc8fa93fdad7d115ea3d0bbaba4db6df89a79989bf3599d7612240a030a59cca28221d29d49d9eaa9de353771266216940a63100415c955a0eac742a93499571c210042902bec94ece3b17a57ed3543d291423130f3a7e61e5a18324cf73934f2ca90383b88df7222557092232eaf467457380b127619ad64cd6caeeb50be11342766b50732ca8ca2a9a658e53de133a7af1ba8f6ac11d27bdac07af3e5a903d638b4fd329c15ac1633834d4b5b9837689038c6e0fef755df4b517d46c74251186522ea3b2b3ced5990eaecd6512c946bf305cb02de745dd2043b8e316a32e45b0acf396e550f2c9183287ecbee6e4cc79bbd8624bb58c649656a6735b69dd88b191e7b35ce2dcb72c83507c77b40e482ec10cd1fd28ba28c37e9d99f6129a912ada3486056d7485d6877dc1d8b150e503f31a6d3756cf638c8983ae3713fb121a52791ee6c12cfb7732a9542d6c3b6e6d7d7a23a1590986a04afecf98dd1a116c73fe915a09a784885f5aa8165ffaae04914b5d5221e92851a3c12d02923b995d20f07f6272995fcba1ffcc1e95f102760d1d144b6bdd8a4e5747453b90c80f9738145800bc4a428d9d0dfaa4ad08cd30372eab494df05c7bb381a324b71adab5e4de20d2801a85cd0a9ffff519c10631168f31ade23483930b51e6f08fd4e2f881f924fed330e8cc00a48b4a8c6d615c9c89dc237e6483785fa9f2261f545c35f2b48bacbdc6680825f99d14fac69cc9be54977e2720f65b94b2989fbbb1329e2a670d3243ae022608ab1ead04b705d842d4e0f725b7c11ad072aaba04c9ee75b117021f2bd5fa0b72b9a4b5bce954d597458f6455a5e11a01ddd093856a722ef1a438369799e624198888cfe1f6a110d603603cff985b5a0c4d56d74bcf540ab1e128d8504cd0fc92cd115851fcc57336e08c3912ac9d157d999f479046dcec8f436531762fad23dbe1081c28f4b3054e024adb3322a32627994981a729cc1da93e5bd8b9592ddd99b464c712c0baee7e931422a05d055e106f25be22b94185b55b3dba8f6f3c5d579d17e0688f4697c0162086865c8506632e7c6619e86504a435df71967c2f40aa87658fff813006ff97c455fe600094e4af0ca55aa7f2d50134c2c92d940f4d2874ea20ea52d6c31c6fdfd779566e7ca9e6de360fb247acd186e7b55ecec973dc258c984b613b908292292ff040d011f87ed293c3825e82c840a67c0981e5cb8894c21f83074eae0f333cf2378d701337ea5d9be0d598141fa336bf7a229d1c2300d1db81f5a491d2c3ba5c283bf6424b764b7f06c80a7ac6e5be6d116495d6b808c509c9e53ab36a729e85deb82201c9817aea9dd1209894db637a865fdcb98479743692af50e4ea6be499d1c32782ec57a80d768fcaa37e471f2995878334d9bc9b13d35b9afa7dac22e5135db374f72392f09789051ee923f75d8cc64fe295930b854b68da983312dea3927b5b40d3904981dee1d0a336feb1a15c0ca303c9b8e9f00a7bfac699a0827ee3c895db033d3fe3c2b28a27409f8128e753d75e1d9b9f329f6e704308d2eebfb828294b916b8ecfa1d889c1feed95eb894bce82e17a3b921ce5f74dbedb85adb4c086c25194404e70621c36a382da0e9ecd03bb6c23f4650522e87670725e78a828f92c1be1c6b5399efd6e815bc3ce0c8eda2d1d29d3b529940dc1e4c772c7a6bc55b908882b683773c7b7ae1e9200a80b0022de1f2ca80bc573ae1760e254062bd7eabad15dbf80616c75e0de7ebaf3f6ad5738e076d03b288eae066bde21ca5ea28936b70e2781110d78d66ca907403c0037c3791ee2fff2d98921bff62fa601161725f72a46a5bfd0979908c7ed02965d3399ea724479c5947bb62e122d0e1cc3d4ff06cf132f2410a368414d5e51fcacded1bc518f4ee193428668d25072ff3714dd3cdeb50476efbec93b6e3c4fd27337354a193b4a01a46ab8451fb43cb0195908a8f61f1e407f9fe160ad5a945415c990280e8a008a1bbaded12dfd57084943cde043f170fe2a32a02d2ea72953ff08aaaae6f50678d432dd37046532bb0c756730017102129edc687a81532a385ada71c2830c106966a7ca27ca3f8808acbe27bfb8c618fdf97d14702a4102a5ad347c53bf73a9b069276c0358070c9dd1c24391fb73f27904e8388d3c0e6a480f9aacaaee86f442c51f9bc73178abd9d32845d8916eb40203c65737b68b80bfd8584a0d4ba90c28b422ee37874c24b61b85ba16184a1bc0346f20d2a3075b70c51d3e25087c1a07118b095d2f5a4f1bff8c570e81330e4ef78281e4ca57350caec822486f18ca4a323c296d5ea6350ca1c19937278698ebe1e3aa93ac5879aaf4c24972202ded45a0198e1b3ee14d01f2545f84b56c97b7c79f35a1a13a85e27947d1f05221c3f8354d08a2a85f863eb0d8fa4c927907c4c904c377e8a6782fcfe63ce19da395787718efa6e1e8a1ff61ee378af10491806ccdf07035bb9fa9870efb4fd44cede7b8dd1593e140c1e4c9e7dce7a93941b4cb2b43ebacf499e0710a314050f6c0e489e315bc4203d5b5e00f55d018ce06b6b00410dd4da6a624c1800608a36963350421b934814a10a6e18d4256cdfbf5c59928c626f1046cec697e92b1c225cbc8ed098136e28b9b2996a28b3e47e2efc20dfc8d4e16941d8b2b1d5e616a6a924d242d12f75c293049c981632a7fcaa7f33aecc678370ef933f984534a5fca8085c3594011d1be1f4b3cddbe914643d0e3a2f7f26fb3f59709ee7bfd756662679e61d32978e0dd8cd6aef72192defc15827f3ae8969b1c26888cdcea230ea02403a86baffad22efd2c463ab0ece68ff4482c2d264013f8834130e66a34e8117961dd5b0ee826740636d469d8321d800dea2498ad86767a063b3b8945ce413d07cf4e112574c10b178be1d0c7474df663646a2cdf0ea786ebe0010eb7b8a6b8939c7afb06b65ad2fd2a805c8fc654e617ff187604e1df8fa3235bf548002ac4ae025e0045be0c3860b02485033033333333333333333353c7c6fc367e7f11db5811216d64e672e50bf28a94b429b3f10be1c2efe01dbc7dc8366bed226d6e952f0dc10ca50c85ac304306fa38e6abd4232d63a1f755eacdfac17d460a2cb4349639a58a9d4424640a325ea1f470d2e985f90fe9235728293f56f081491ecdb61890d10a7dd0553969fa4197048b0c566819b46f2f64f0914b4cc62ab47e3bc96e29920f3e55e86d757a79903fa950264d84cbf3f171eaeff10619a8d072f24b57ef8f33dde6586da0025968e1022d6cd8e8838c53e8317b6baeb9107387bb0419a6d07c503f1c39dd3ceee30d24166c8e2d707881a30b162c067084e1c5035c4629d46b9d8e7829628f224f0a7d18b1f7b56be207f38c428f1a1f260bf7215b0ca92043144a760d3d321f8f735bda43a17c556c4f9d3fe13d16149f50bc2d8f24ab07f57e6114647842cde3ecdae3fc117e106d27f44e5e3f1e7c90797c761a410627b4649122068f929c5c2e7e055cbc6f4239e948f1e463b8c6be39c2688166a1451619c8420b2dac9441862694b96c110b4f26d40f7eb25b553ff0ef31f1c844540e9a318f46199750ff479a2db6465a96d072b649fe711c49595f1995d022264f4a791cd2fd207203c9a880400625f89be463d1d39f0e479b1c38b4a84005b2d0228b7f01171908430515e8c0e7c0a1c577c1616861c3c677c15b84d1366c70f1c5dab03109edcd7e9c62b9581ec5a440bb582ec2f802050d862494cab311132185fefe9011095db4ca7af8c9b692446566900109fd22f23067a4c89e5388165f70a1012ffe8b0ad8b001c6175cd023741fc4ab3c7bdda992c60a321ca1a76c92bc62fa18b96aab828c46283f9290ec819fffa6b00c462836ee2e29ffb6851e464090b108e52ff3f92066cf25622a428b21e385ca963ea44c8e6714a8800259684002d75ca08c442821b6b22a4e90e02502642042efcecc1edd0d0d5339849291e5ba7d506d19a31b480707de00631d50508621b4dd10ae736c8d7199ba8154868ec82884d2b5b934c48dd557531201840c426831cf0f34c5d6cc91470f429b394b97f993cdc70b42df3c9e09352e6f1734815032a47ef371f0c9ca1320f47cf2a32c7a39197fd025478a5b967e7c9f7bf841cd1197902963e5818468918516e902197dd03c5c5ddb5d1ad9f2f0414ff136e5e1d77bd083fd7ba50e97936cad07e58386bbcd1693073dc6c922f1e2bd75e581074dc34dcc703ee80e7abc6e6f7fffa81c123b689563b3276232e6e9d4414df2396bd47ee486880e6a4dfecb911f83860d0c1973d0479d8791b9f9ad73b864c841dbf3c15d798794b25864c441939c30f152da3c1efe78dc4032439001074d4effeac72953b9fd37904a1264bc41f36c9363de3c3eccdda0e96954fc81fec49082c36c844164b441cbd9dcdbbb87b9818482a309d8b0415a6441bad0efe20b1568200b0b48208b2b2ec86083be1d31c6cbcc1aeb3e6bd0ee2a5f8a7b09963179fc40861a743dc99b91736e69dc345c6691ab2c6765aaecb269929f468c8a045cf0112828c840833e1ecce67f1fbffa1ccfa07c9c85c69452ce1b4232cca0e4cb0dcd83749741bd093f1f44329b694706cdcf2f495e90540e648c41cbfd830b327133d864c4a066d268db587947231706c5f32fe4e426491a2a3028e359b24b6ca64cfd17d4f892871f3d8a9962fabda0fcd8e45a26eef57bb80bbaebe7999d98e1a31ec8053d66df7c7e8ad40d646c414f96246b0f224cf2208603195a50cbde6ac235eceb77419091053d3c565bcc21e221648c08646041ad7dcbb6fbdc1632aea0e733cf71d2b96bdcc80232ac40365820a30a32a85076790852f3f2af2132a6a064f6b1779ef188bd611b366448414d16d161696abbdada838c28e816397fb039cf50d0c7a3cdb12e64b878a1c71314b120f1bae57a946952329ca08c578c7d67be61af347790d1045d2287d57469dcc40f13f4990cee3efc1fa58cb51d642c410f9fbc98e95fcb7e5782d2f16d7c2c27b1c693042d2ece4cee6affcd27cd410612f47c3dd39072ba1cb1c711f4ce1dbfc67bb3c64bd9186418a1aa34553799b5ad0523878c22a8239daf69fc0f83d8173288a0a4183eebfb604c624ad9a20b1943d0ec53907a9d8a1cdb628b13340e1418173284a047aab83d1ec882cdb1857f18fd5f94a098404610b4cca925f8a02d50b082df82d49101042dc5a4ecf0a127336b58c60f94142c484c97e2edf438e703fde3fa2f871c9a87d36e9106193da0b7c791c7977c5496b70c1ee8953fb3ad539ac7d1179ba3045b94a0462063076a88ab704f9a75af91ad41860e94bafe1cfa2c976872a0b9a75c9b3b0ff27f1cab41060e34b99c72b3c3e606bae75b5f875c39e3391b289f429c89c9c91964d44097eb9f4cd912ab2f84069a8cc5ce5e29ab87d9c723043266a064efe492d2a630fdba1764c840cd91597ebf13a4de63b81023169a8eb4f638fb76a61f7f030916ea76f8f1492ccd213edd20c62b341f6d381f7778060b3601440c5728f923e9a49d537ee9ce0d24301e4781d10abd2627edef1f76ddc55988c10ac5c73e797fe419b20fcb1b48888518abd0e386fe983a05cda926a38aecd3dd8ff24eb582fd2eb6f0623b154d72ad1c99321d03156a8eca9a9d7276f871f814faf99686c6310f175b984291cadaa9e3c464c9bf175c94427fbb2c7e2df679d41f14e4303a52682969fcea9c43f3f8e606120a72101c3146a18fae53ba3449728cce96238628f4d17c668f7a98b952418c5028c12d799c331f7decfc0da42d8c173140a126bb3ecfc17f36bf952888f109af3b42481ec411cbdd4002630b32307209313ca15f1e7645bf945d92080a7274421fa7ef38153ec58fb5c70d24244e383e4c791e429a60e50d24aa1dc4d8c4a3db8e8e3dc81a3df20652e2208626140d5f72492385cab88891093d5f87dddc162c888881092d227c7eef9014df3d333dc4b8843ed818a74795b323fe64871896507bb07938995e925d48da1c625442f781574e7937a6e41083125ae4d69690888dbff024d4b4d755912758c85993849af1f672e965339faae0102312daf8cc98bf56a4f8e371c4861890503a53ccff38f24072f0f1083d7dcb6da4ba378955a1218623f44148e6bce9bc8d916f84f6bd1922cc77880c3118a146c665b9a9aa388d8c21c622d48858d2bf297ec67314a14bd6545cbb591ed78950e7ab777a70f9528e3922f44f3a41c2fcb84478087ddcd2fc2054ec4148b521f4ccf5c33c9c94ff5cb2855092e651fc78d01e21d41fdd5d25999823675c00b8438c41e831d84d87b20fedc913843ec13c65931f4be8e71108357b9c2e5b687df2c10510fa2032fd583b6374d6ed0f4afaf2d1867699fa38f9419720163ecce4ffd3830021461f348d24e19f2fe68f762c366cbc22061f944c2962c69c9e1f46308b3660c3c66ae1812cb4c862021900430526f004fc33c09fd42862ec41cdf92a989f8de68b0d0262e841ff91e8461ebbfd8977f2a0d4f558b2c6d4ff16731a90401e62e041b18989e1fa7e44637907dd652422a7930f8b941df498196d3dcc672f218951075db287ec5cf5e0bda78392246e966bf9c66f660e4a8fa4c9dc30ef239313c490839e25697c8b9f86560a2a21461cd408d93e5f54580cf7992fc480833ec8ba11b90ff2e3d9bc41af4821e985186e505ea3269f66eb5097471b947cc97edcb961e13f6283a6171aa63375dbe708ae41b1c96333912e5d0e793528139a66cc47163bf8891e889106c57c94e35e54e87166120d4adc69984efa0ccac8871ff878b4298b853ca010c30c8a6518b758397f315b80c18559418c326871feee73c6fd43eec9a07ef6f2d26e8920361838bae82d7078aa20c618344b999268e4c4e570c5a025cdf12171241c2bd80287631831c2a09ea418bb0efb31138241afeb14e72e849e96ed9183185fd07d3c968999533c1fc4c70bfaa71f4cd2b91d11d9c0e0c2b7c0e1553688d105c54ffc83e464fef01f5cd0076192e323ed10337f0b7a8a61f2b82e0f2ce414d482f63d1e65964c31759ad40da42d70b899418c2ca89fbe72c32b8fa2472516d4c862397606bbcda3b77830707c11e30a5a75cedb79ae25a6fd1b488c8861053d52680b9e7978b14bca42030d381fc4a8822efaf9c3ebd3458f525888185450b274df72f6c3e49c008c0da308c7f1e2015e7051821c3b81a41c11883105ed2e844e15e23ab38739175b788103bf0b7fc0510c29e82159bad3f781f6b8fb28a8713925bb3cdc103a9831a0a079d7860b3dfe710b319ea0668fb9e9f0d9c16c27e89f2c24c40a3d9caeb016623441cf17f1b11452d688cf042dd287e42933d34fe74bd0c2c614721e9ee5f9854f097a4bff789ca7af2a840818b5458c24a851d741528574f0cb6e201d190ec44082baf7793cd25cb1528fa73421c61194f8b1414b2657878f11f44829f90f243b3b5efa0bbb2e6214412bcdc85bf1597aa01f2268a33942a5d0a1ac72d8ae1063087ad61e794ec9f4c52d148216bf4e5ac24b730e1f5688110425f5c68b88c803411bd91fcb213766e8f80fb46039879cbb53bbcb3b85183e5067b26465bdb85791471262f440cb973ce5571e44d95b4888c10335cdeba710cf711bd30da41c38b6281583183bd0bb5f2bc6255b1043077a083fdc9c3e649d9b25077a72af1c298feb2c4884035de4e3fc7b4f9009316e401ebd1f667b6a886103c5fe927e768e21d86c6aa0f7a7984173fe40cba228c4a0813eb6ede1e7f9a4ac31e8853a21c60cfc98abf3d74ec54ea91832d033885d5f0f6463ce4f2c3289d18f39176288888df820d6e461de44a143094acf6fddb994c4d810f1414712d43ecf1e5f5336312479d081043de68de0d7fa398ccb47e83882be397df820a7173b5ecc088a458e5f4cca7714414fa9b27ca0793642071134bff025ef31e9aa3d4350a3b2a64715513feebc10d4f0b379264f3779f54150347288e05afd62930782b61f435cb038ff401ff7d8c2ca3c23ea661fa8793a8f2f4592e881857ba08d75eccce9dbc47378a0fd30f6499787fdcfef0e74eb0dcdde89ae8eaf0e947eb964c1fe6737b83950734e8fbff284d2b1340e948b3dda0f7ac91b68f93f4946ca497994a30db45032957bdc23d7ecf1a881622f39a323a6d0404d4176425796b83c66a05ec5d61cb2259a721d32d047321e9b528f3ff5308b85fa831c7c94b23a471e5eb0d0079272de60b1cb5c7e855653f9c34b559ece07b9424d136cbae3e24fcbadd0a653f564e8eb414e2179031aac5072a76bf71c5a2f52bc0a4542e69453549eb9e56168a842d31bfdb1a649a6ad39a950638c8b8dff5115f6a3424fe1c52dc42c9ba6e25368412cd264ca79f0c937a65062c4cef84c86ceaf147ace7c6946b7463e8f24851e6452ba9c3c272fc651e8c1c6aa8366a2d02d4548f767a6173442a147fe7ebb0d69eef70685a61df9a7bef2678ce727f451acfc9959dd9262d2135accca9a655c36368c9d503b5865ecbd14762fe48492f3677c05d9cf395f36a1ce7796c8839469d26c9a5067f3280449666542998d3cf2617bae882126d43d6d2bf3c825d43d2ff91e77b21e6c8e25b41462777856de85985309ed7c9c2f459f58b6f729a17bddf78b973909ed7edc3655ade732be24f41c32cfe5e1c76dfb3d127a4857eac1d499865c4342c941cb53f88eef5cf523d4c9e3c8b4f19dcf3eec087d30967db451164be2dc087d3cb16df27f646d7f3262f790675c8412df72654c7938197214a1a68f3ff67185a9dc934722f4f281841e8f22ef048f2142bded9d0d1f94790f3f84b6417ae487b529fd86d0bf22cf849fb56f4c1542cde371feacec8d105a56fcfb20fc20fb4fa4310835238c68a8bf88b31eb73404a1947867580e793016fa4068f12abaf238afee930610fac8bf2245087277dde30f6a48fcf18b69f6383f289ebcf378547152c4f8f4411fbf5c8f355c7894e6f9a00ff4b285dbddff38cb7b50624fb2bdafb01e74bdcf17e6e3acf51e9307a5d355bf9b84aced241e949ddfbcb114bc83e23986e4588da70f1f76504b82eb54747d45675ea05107a5cbefc343ae3c7978d241bdf0c97db79dc1626e0eda56ceb0e972725072c44bffbeb6051f8f38e89647912522561cc90b1c344b294edece11b6c27603c90b2e368ccff106ad7d142e4836d31cba71839e5dc2a78c3ccc3156d1688396657b7b9c7abc210368b041b9eaf3af921412faaf410be1278fc23b63fc570d9a7ee4a41424e41e6498067d906773f861a4d809153428d9e3eff8c3fe4862db16e60334cea0ff387bf6718ff7324e63067dd09d47b2e13a65fa6419b4102f5b07689041ab081b394bae9f5d8f41e998232fc93b56884131a82952578717adf7ec230cea8f477fe5fdad61d942030c6a1e94bddf07ebf1588ec114d0f882f26e17f942c30bda55cc63f7ca1267aba304834617cc060b687081c616680434b4604cb9fd64378cc7d1b2a0c854fe4f112f0f73e419021a58c8f38e76ce61bc37872aa071851c34ac4075461e56d04896c7d9e38a182dff9124ad5812814615b4143e7cc4f6e834a8a0b6c590e2c38237d003848240630afaa54f1e3e8e1c29a829f78ff9402fb2e3864614b4f68164939648d6e36940410de2b13ed4f4349ea0c4866496439c9d6090a8f07f212b4dd03b488e886091bff24483097a8408f351ee85584e4281c612146dcd696a425dc7b02b41fb8e8afb4afa3dae32094abb8560c12705096abf679cf813f963b626d038823ec8229fcf46ec538fa3610435fbff848be70f5f3a1a45502724ab18f7fac75dbc8144a4010f030d22a0f72e614a27b68130b60b06d8b0e1c58711c6767168083486a0dfc720f775529e3b47087a08323e921f5cce6f27086a1e56c8659a9ff9f40341b15026eff3399befff40ddacdbe3b88e210fdbf7811a2e5c30f972dbcfbe076a8c6d1fb521cb62f23cd0b3041f94ccef7cce7b07fa786c417253dee41fa675a06d581a1fb4c439d0c5b6c763dbfc81069371a027f9d0f457c97e38e11b68123a3b7feed1c4f9ac0dd43c1fa2fcac7674363550ebe3a7fc935431e40c0d1ae831dfbfc514797b7f181a33d0c71a379b7772ec9669c84099f8123b316759b5b1d0a52e2ab4690c9a5312166afcd1c62dd1f2159a0fbfece2886fcc55ba420dd171ee2d851c41be56687e9af24d96c70a7d18ee9764e2ed3d77ab50c2f74f1e8f69cfc31faad0ec2634c5df8f47e28f54e8831edfeae003156a78079dd049ec73e8710a4d346c7c1c63f3a5ac29b4f8b8eeb0b53dec61a550772ca79eb694f7831452a8394bfcf0f73a93fca35063d8cdf9e0272bc2a2d07cac3dcaa88a8542bdb6fd4e778342edca1bfe9a7d90c72794caf5294cf9284f28e1c783cb54d5093d9c6b5bb6af0ff21327940ecd1b42f77a1cf326b42c1673861fc44a59d484561f3a68d09463168b6442cb573932228f88850826f4f0af49766b7b649bbe841a2178984b116162c91996507e5851de3111539e9b51093d7c58e52dfd565dc12dbe01366c80e1050e14740b680625f461b57fdcacff1bc9d662c624f4543a3f9ad47b9f932d09ade3e476b47eae4cd78c48a83d179f6a4ae2f95b3320714c7dfecb1f4c98f108fd2b468c58b74198e108f53426ef903ec5c8cb69841e151da3d3788cd0b38fc731f3c9e4ec135a84b6295992baca8e1b2286021fd80206331451e9ccc7dc1a555a1f185ce4d000116624420b9537e5b318fd4bef0c44a89f95e72fc6f338628f1c6ac3c6e67830661c42d3b9c9839a14bc62792cd81c86d052bce651accf2198c7881e740b985108cd3462ceb3c50fff529e8b1c1a280698410835dd85c9fe9f3c48b6c01146d1c1812387050a023306a1cd79c59eba9472de6ec3068e4341681aba5c3ad55624696f2085f12548090442fbdb79b7105923a51b47185e7051cc01424fbbf3972dcb0f32947fd03bff76235f78c9399360861f148db1bff0611ee5cae3dc40eaa28bdda2b84001171208830b1470d1c51637c2f8e2bd0533faa05588848e7049328f3e1f98c1072d548cbd0af7bdfe617b507b7b906a66a2c70c3da827f93d759765936d81197950427eeed9d1ac88183c28ada1ca2efbc71ffe30e782cd91631f77306b620f4f2383e7109a26ccb043e39fe72c244deaad0ea4d208937d1a3e4cb981c40f155c8e30beb018cca043b160c61c66c841b3cc61c3849194385c413c72da9a080e5908f1924edd48b66e207961c61b34dfca830b3d177bcf9a0a640f66b841c955f107d21b2c82f7b061c3860d30cc8c36a849646308c9df16e0608381196c503f4cc6ff287526332f30630d7a1093891fc61cb3c4980acc50832e3d3ff00e1f864b571e1498910645cc7c2cef038bcd111b11cc408352d2de3f65b93df782f1398a07669c4193ca489bea2ebe2497bc70c10216801b9861063d668cd3ce2ce9eafd32281e3a8f36b28f3fe5126790410d15a2c725620ae1f531e8a1a46cce5e9386266b0833c4a0f67027e76185454987c3a0668e1b3ede9aeef4a7d90c3068133d1d34cff5052dd2e4c6672a33c9bd17b409a9b3e7f3c02bff594098d105a526496fd2f8f1f82dcd05a54333e441ed54f984cd7e30630b7a5c0f626eceae4c3b5be0055ca0808b30bea8c0091a47174c9698a105b53527ab728678679b35ccc802d972d00fd25609c30c2ce81d4473e397fb8fbeba81b45bb8175c1cba11860ab2d0220b0da000470e0670c100e3891957d0b2cb266493a415740bf29f429c3c8cffe08c2a6895233bdbc557c00c2a2892c2f8302e7d06ffce14b4bc11f76372e1979782e2e9f257fa9f543747419fea41caf78310312d14f41e4dc5ff782cf1f9c713742f1f6c6be514622b77821e7e8365b8bbb77a4d13d4bd1ce4b276eef130974cd02a9c4b0fdf479143794b5034d9c57c9553f0cb4a095ab8bb8ed93fdcad1c3223094a903421ff0f740b2041ad1f669bd89f7f1d1f4750f6af36e369ce9fc3463090851659900b6461010964d1c5172a08638ba31946d0d2f496079374e139338aa066f908318dfc7022fcb8810406d5cf2082be913dfc58eedc47dd168171841482194350c256cba7935e086aa874a2ad99313bdd4150f7ddfcf32885e6d165670041bdb8943a6b82c54fa92c3420013cccf881da9db12ae96dbc0f24777a98e1037532cecd6cd870dfe3f440bf4b6375793dfa8db7113378f03066c654165ad8b06183123376a04bc576798f21ea3be60652a10a14abf3c30c1d6816225c8790c53687a01866e4401f45789bfc088b93076f415f988103cdb5355e7e66b4c52406aa30e3067a4839b69c04ebf15967860d94f7b3b8f9bf23879a8530a306ba8fad2d720ed38fe9b22bcca081667fdb216149aa5d9c81ded9272a8f44348504fdc51e8f07954c8f8396266cd267180d6138689f2ee50edf7983d64132b47c4ee3d97183363ef6f0ebb4410bf615f240538ffe6cd02aee578cfbf14c7a0d4a87e9f8a4cfbe1b35a8e183f91813c44afa34e88310e2c1248f063d0f2c57c798dd24ed67d0c347ce18f37033a83189a4905e736ce565d0c7930736b1fc3fe664d0cf87e361bc3ec4ec3168d7ee1ffc36652a5b0ccaa6f8f979d82771ea30e8c34e2739ec62cc518341afbad0a29f2f28a9e327f91c61fff5825a39464b8fc7ee61b80bfac0663c521ebec691b9a0bc0fd2e414e32d687ad31746c4bcc7d182e61572bffb2ffcdf2c28496c82051d0bda877896452e84d0afa00ff22083e6fa41dcdc5a411f4eb8d118d3b2b9ad825ac92e7a73b48f65a9a0e6d59f5b5b874f3b052d65074de537777923057d6f27cb879b2868f9d3e393e2f6e50c14943c100d975d63b8974fd0ecfcf370235c644a2768313e5c9c1c9ba04ed60c99feefd00399a0f97c6692509917e112b4b03dcefa39f48fea53827ad9f143f8478c994b827edeed3dbe499fbb232428527926ef23c2558e8ea0e96f920abe11f4f6698b1c36edc41f45d0b43b4c8eb97f1c2f11f4f1e9f6b8f5669543d03305137febf99c870941cb383521ec76849c20e879976e3af6f0521208fa7feef43086f012d60ff45c62fec17ef2667da0ee6bfce9e91cbfec81ba13e37dccb8550f1e28337d6d61210fc2b2033552cef7a135ae13d2819a3fae981ce249fa73a0c48a7161ffd6318d03a524db9bc6bddb84d00df48dfe916f898f3236503f7808b1af935ca7067af4e8addc23001a6897c1b2bc3dde538e0066a0e55d56cc1c6afb3a0290811af9f312fbcd25feb1d0428555a4c9c342ebfbbe37fdeb41fd0ae5ce2f73489687dcaed0340599df906132d8ad506254f08fef7421a459a14588655c79ccd8ab50daeb47591df3f7605528fdf5b9ff72da64722af44c792fc264bdf0312af48b207bd1231f5bc4a7d0737e8b8b9b2e466f0a45825e2c3b3fdf7429f4ef54973c8796c8a4d03da6fc1d7fb3a4dd5128dfa1b36dff6ec715851e44bb6d6724846a43a1e4758f485e9eec8342f1fafa3cd69dbafa139af60f376c84ad0c1b4f682f9963054956c9359d505cac62528613ba6fb5fe68737f546e4213ed10df2e777234a1c978698c1e990935878f6d5e89097ddc92326946dccdb9841e6d7316c763fb872514cf92c8cca3dd1855426ff7f16e4c3d1c0d414a2865e1f3f52287ac27a1a6fc51d262d3e39d24b4b43399b143ebdd9150b43b426e0a3e92cb03127a4a5921e225cdcd3e4289cfe9bd10f383ce116a8cc4748eec9e49698476db316bd68f9b61462867b1f2280f62ef666d115adea7720b15a9a1082d25cf15661efb1b223512a10ffb5332ed41b088215203114a8a31ef66cfb83789d43884565e1253feddea9b901a8650dc24c404b9389621350aa1c49c2e624576d38f2ca00621941eb3ca983af428e6773ea83108753ff368eede33f4481ed41084ee417762cab0feb8788d4068e59f07d93d427ba4bb0620f44961daf3c73c93d35de30fea757cd38d19362c796af8410b3ffe51a65cf129ce55f4801a7d201b7c201b5c630f9a688f539cf0d6016ae8418deca2e1471626b47979d06724876c9f25474e1d3c68a5e3419285f00eda8f83e46b986f073dc6a818cffcab83d2b9c7ae6f3958e573e9a0e761da9c3cd0cb93833c0725ee7e433cf1102e64e5a00ffa2ad84cfe1e680a89833e0edd1293bd2a5e4a38e896d971e276f60d7c0a91b879143b37a8fb771bdadb629f65dba0e4817b5f0a1179b2756cd06d828b66be9462fa6c0dba7ce5734d3e976358d4a00f46e2a68f982434b469d0a64ae6fada2eb667685023d86d48b23f2ad9cc1994983f089e3d4e55d59919b49f8d97245d65d035b8c5e95455313b4506358d6bd6a0ed1ae7426350732e75c60ab631f96250bbf7c7df35e6227d1894702dfaf17b8041edb4298f0766172973e70b7a86f251ae14f6827a19937774ab657359a30bfa678ab1fee26be4be04391e05846a70a1d860418d2dd4d0829622bae5f6d32ba89185836d929cf9971a58507fe38fa432c3c6cd588d2b94ade2c5fd286554c30a6af24ae6913ca7c6f7aa829eb1ec743ee49436d4a082fac142ae7dd44e41d789ada193e41197181b6a48411fe6ad60298ff228e8f3567d9b5384e6a4a1a0d5e87608e2d5a9f1046d42a69dacd72d3b91d250c3096a654f7e0f69ef083741c9b947974cf43a3598a0e72c3235e93fb987ac086a2c4109315e76378b8d9c3e3a25a86d9fa56a53d23cf6ab461294205f298f3c66774d3e12f49057af799c5bf4ba53e308baa4eb76bf92893fa88b09358cf04024f347cb5c320c358aa056bdf9e8e3445eea89a07ffe51fc0f3608861a43d062fc9007617bd0697e084111eb88f9529093209920a85db5f177dd7b91d91b48c503358050357e709cf02c79e4ffa30b357ca0a698644e6a3b43ce833a50a3074a6565c91fa590c7146af040b94a176fad7b929354a45063079a7b995c4b8512f9c11aa8a10325cec5d815d344f0705e161a68c0e1a891037d3c081f54a4e483eafe70a0c41f75e5c6902bf67853b250e3067ab8698685c862a1860d949bfe90392ca5147ad740ff9020fa319b32884b033564ce6977dccb9ac72ad4988162a13b744e22d3126ac840fbcf69b2ddb4a62c9928a123165a8c31bb7e9cc20fefbf1bf8385868a9fe7f18f1a973f29cefe25161e4c001465140c72bb48850b9e51564a6ee1b44872b944ce2a3904796b743472bb4ca21e798e70d2b94b6ef743dc8b955e821e665b1921ea80a5d3b6e67d32f3b52a15caec9eeb4e1273ba50315ea5c548ab8890f3eba3b4ea1849f1a0f731e4ee3ee308596f1bb7cbc314a2e871da550378fca475ff19b531edc410a65c2cccf235ce8b72c023a46a1e46b884a7972041fde1da2d034c4e73493efc7e1a33b42a1560c3ee73f39bce5680f50e83122cc649a88933fbf81543a3ea184f798f5badb462b97c320a1c3137aeaf8aa1052859f5e8ad0d1096dc25745df5cf851ee0618db051885ca103a38a1e987b81143720a4123613c1889838e4d283fd210ed79e29a382a86983b2246ac0c414a43ee8efec831b657f4c8843ef2d603f1b17444496e209da07174b185d30970744115001e7460424d33d172c925a472cb2574d730e1c3c95a4249e1b5fd83ae646eefa312da448a94428610c3f5a81b4894507296db0e6fd390a3a241c724943c1e8d6e84e01295bc1b4846061d9250277a946383ece5e1f73790b840f2e201140935a710392f611286175ca0208c25a5e880841e3d90785a9b3e69d2141d8fd027566756cca9bc1cbc81f439fa881eafe87084523f9a9db210b37af43542bdcf336797794ee67a420723f4303116957ff499ecb308bd47bfe42eccf648378a502cf445b6f0e341989c89d08761cab4ce7f3479e022826c9c8e43a8b1fbe3acaf89d03f1e86d0f3e59aebdca09d462a841ac1bbcc3b85a78d140f741042b1edc9d569b4c37c5b818e41e816f3575bda14c7444341683d3edade93ea2a107a488cef9d7212b9b2041d80d03ca7d91e6727c969071d7fd03f5f849c932a931f7803290b0d340003366cd8681b362a003de8f0837a392f4fe581f87c3954f04575d1d107250789d5d9228d75276f203d5cd0021c26e8e08352d9d19727681eb78961e8d883e6f9e3389907c1d283ba673eae37b31b7288f2a0c46e4c16828f6adceac083a217cf7bf85d27f9c37407a524fdf5380fe747113aeca0661eecc4d24c21631e4643e8a8839241db36327faa343dc0202174d0411f750af379bce962a86c0a1d735022c62c6bb00db6956a42871c94df0babe8291909ed16641d7150efc79d3af338747ae88083fee3d16bbc0c3a75953d41e39040c71bd4bd3c301fe5fff1e83fbe821cdfe106b56d83985e8c5073d1179ba3036dd0c642c5ceb27924133ad8a07de7e032e5af41f341e4eee4717752de2ca1430dda56181fbe8ff328bada34687a1a43c7e6cfc40f4403d91842c719c8c6093acca048e75108d96d5b337920123acaa0bc65281bcd2519f41d0f73e521b6624e080942c718941ff4b0b7e3c73ea14531e83926eb8e9d0cbe57150e3ac2a0dd8f4230b99af848b5163ac0a0e510248f877997efbc8342c717d4901ce2e39778bfc19a4087179492ce4817e3d658cb8f1f7474c1387a75ee317b7041f9cda9071f4b3cdc47b720bb630bbae7f614ae37954f8f5b418716f48a97eea73c7bbc704b181d5950539ee95d6c6c4faa727460410d2dd92c2a3f75f8e145c71594cebcb3d4f997a9a20d038c07163aaca04b8e9c9f77d930c0285968a00175858e2a28592643cc4e5b9f2a2b745041ddbc793febe6ce2796dd764c419bcb59b4ecb3698ec81cd021858e28a8613cfb279388f520ec400714c846c7133493a8fee8881eb6885de87002d9e86882b677d27ef9310f275ee06082d6e3f9810f2797976de8061251161a6800185fe0c8b1001b36c0a042c712f471458a6705e0815eadf9d2a60abd21260bb0037d38fba34d5eea401f5bc873a0d4778554de89bce3401fa41fcbe4fecd0ddf40ffce3fccd97879cfb381de916b7cb8d5401fd60fda47fd8306cabceb77a4b80033d025579fc885f68fb202c840cb7339042f4d1d278b859a43eb66333cef64b0502657766984fab1eb15faa6f71f07b7d215fa55c591f0996b5fca56a8133347ec902db927c90a4d52de4f15925c85723d1e75986acff1915485563166b2ac7b36fb49853eacc949521e9475f6a042d3a0b12363731eff29f4f0631f8f071ae452cc9b42f9711ebf50a19278bc145a6bc6acd7f323b39c144af8493e7c4f8f42998e217bfc711e4ba745a1e699f80c9d50a897623ebaa343ac41a1e509ee1a1a5fb2fb09f5073db6799447d9233da1a4b808a17b987edca3138a75eccbd8294b6ccd09358ed66484e730576f423df3f145d649974ad6841a62edf7b034fd28cf849eb47b2439c4841e3bf7488b463e6f2fd1556eb7981f6b0965f27878bb31fc307c6c2594f6c15fae987decbba1841e3d1b730f3393d0cf83f7fd8b65cc2909cd437c7fa226680a4642cd95bd7d344148a8c132b633f5bffc8f5073bf684890b017738ed0aefb2f4365c6fb1ba1767f94a6deff81cb08a5a2077539f558843e1eef0f2e841e8a50734d5d8c6521f73025116ae8ced88f82082505eff1864eb6fd39841adec93dcc86d0071a3fd23c8e71268f4268e9752348c808a17b0e1f3be583d0c76e1d3371b36dba04a1eda4f423d71f6da52b10ca65b9f648e44f520a106a9e0b1d7a78f5da11fe41bfcf7918c922f483e6637bfbff0ca779827dd034f5c596583d1e4b0e1f9498dfc79de23d28992cd2d9868d95eb41a9f1f3095e5561e741e998df94af197a1c1ef44f113aec7f3ce6e3efa069ea7108e94b963e7650f238e5d01676f97a1d120db7d341ef589eefc939e895da420ff3c5203d5c0efa650de11a5fbf07791c34dfe42da791b747371cb41c7e38392cf4e6d46f507325b3d461768362d29b4376aef196dbd0691ef460831ae1c24854464c7ed7a0568a98796e9f76b36a50df3737e46d678e360d5a7db09c7e3468504ac3650ee7dd2d9933281be1d6e35196f64e9a41ef7bb9f249731b9365d0bfcf871dcc0717be2483de3e12bb107423b36350b3e661a61fc5a4a34131e89962c494938f4c331806c52f5e4e2ec5cde3100c5abbdf8f73d426fd815fd0ec731ecf9985dbf6bca047740c693ae7ebeebaa07eae18333f0a49b4e382e6d3f723d93cb6a05e8c8d716942b9f9d0825ad2d9c7199b77793cb2a0bdd578a7ce0a3e4ec182161612c647ef15f4091b63f23c12db70b182924779e49a7bafb1875541afcaa92ffbe471fa1415b410e6c45baf296891b4ee473e8c8e2149414bd2315645c7b11e745150f38f25e6660c4f1a1d14942eb3dc13d48e375e5631864f91738252fde391be8ec9e5719aa0cfd486fb1c3a85fa99a04d55d878f32f41b1d8f6df1e75871eaf045d7334fc38c79caf7b1294ced2ff23b13c78db91a0ef848eba0fdb95f123a8793c4a21fff8248f6904ad53ec91857b9d5ce92268622edd2da137c79a085ae6d81edc59a7be7a087af9cde5c1a6ec2fb510b44bff9b6ac6dda50e821eb93b7c14b963923410a8cd171f2549ff400971e3e77777cfb90fb4f3815d1c1ff4c8b23dd042857ce2db1dda8379a05430dbc91a9a67e31de8fb12b73734a225ac033dbcb7c78a21e26673a066687ea70c0ed40aeea3fbfdf23ef6064aea1eebf48b5cf5581be8e394ecc7926d0df43457e692079a629a06ea87fd96cde447ceccc0daba3fc93902c8409f90ca58681de3deee7f6f7610165a8c65ea7821e8bdf80a2dd8c49be8923c7ad1157adc8933127998b7b956e8c3f26e0df97ff8dbb14277cf63fbd5ad420d9b2c8fe57f10c77da8428d290fcb42fb4c071fa9d03a3294f4383c6e7ba0429f5c5afbd3e314caa5cae8711e052d8f29f461f87d5bae8dc12c859ac7b653490ea963a45047a793e77ddfb946a1bd8f355bf6bc175211855ee6dec3cfad1ec7a1d07262d3a76059e3c340a1fd6e8a083fd926f909250f42e51e87e9d0a3ca13ca7bb5e4f1707a74420d27e1666e7a7042cb831c12733f4867d363139a5645984d76212cf4d084ba176f3ed39b77963c32a1c76dfe9764917b431e98d053cc30734f33e1248f4b28d6e25765d695fbb184b6fd3d901f7e2aa17926ed9ff194f3e35042cf9f760de1b12fe54c42b76d696d1309e9774968b33b79ac1b325f8c47429b91cae595afa77448289e53f6fed11fa175c6ffff95e7f4704768fe49520e1f6493c51ba15ede9c2d5132537146e852e383998bdde3b917a1dce5f154da72ef582b42719f6c31060bfa552742c9d5bd9d268d0835c585b07910f5ed398476729633eef948b286d07d7bd431eafb5ce442e8231fa52c2182f4fe845037c47e3439febd66109a5ebe2c713fe57e46104a99c4d31429a53a104a7e2a0f7173aef700a18c5624f794fc83f6b7d1a1537e50e36b53ca1b39cb04fba085a4df31c49af41d1f94f91019ff911b3d6e0f4ab8c983a45b39ebd7833ecaab6dc9e6413dcb56293378d07ee096f95c27dcbc83229bc71ede83af54da414d7f93b2a41e26b1eaa0071fd4e7d1d20bd5830e6a98c56e0e3de6a0c9e41472940f27c772509264b1dd588983de967b64c70207edb2ff62e2c79a83f6067d5cb941491f4683c4aab441d11caa62b8ebe43d296cd0c74c26244eb77452d6a0fcb8c332568a3f0a93a206dd45f340be6a9306bd2eefe798b3588f23050d6a4e2f5596edc77f9f33689e2bf7f2cbe5781f3328b93df56e84f8703a65d037267fc5d6c80f3f19b41f8714f3380f22bce6c7a08cf5d9b95df778941783f2959f4268cb113e1e06b52b861e8ff72588e660d0535b7cd9f7a8d2e55fd0a4c716639d5267b6bda06d9a8fa92cdd056db6f2f82da610233317f4c15efa1ea54e212d6f41cfd394f3a75f538eb5a07cae9c92257df9146741cb3dbce4e33c2ab38bb1a07fda917fcf15b4acfd9942fdf0a25f2be8ff2d29fc707f5cb355507a782e97b4226ecc52410dad31d975f2cfd34e41eff1a077279f4e8c8d14341f698a2f2974e81f260a7a87b1ed1ff5a02333507878aef9e4c7c927a81d37530ca907799c4e27a81fe65ede1dbacc6c825e21a7c903dfdd503341771f8fce47215c82a6717e1162c8332154821a9e62056bcb95259804bf3e77def80f097a654af5e3903a4fbf23a8c993d526edfbcdcf087a947744de5c11f4d4e3cddc3ed2902a47046d5cb28ff27cdfa76e08cac6d99cbb6cef1e216892bb7922b3976382a0c7aee49fd9966102410f293784faf14efc1e3fd0b524f73b4ecfa63c7ca0551e4e76ff288fb2f4e881f6c39c7fa4f340ab7495a2f220fec43b50b34d12911fe4895307fa8fbf07b5971ca81b34f2605270a07ee6c848ce3e2ae9068a6b8c7d3e48b101d840b71993b01cd3cc311b400d74d33cb6e0791a28b1b27eb8a30d60065acbc7d8f1791c266403908122631accb7bd53bc62a1a6fcb10ffa8265b00b165fbab35ea1be9c6e964abbd5952b9419bd303afea53faa158acee468ddb8f399154ae4417c1e8f3dda33abd0a72fe55715fa28dc2c9467f1414c2ab4cf1b72eaf1599012154af6f13868b6303e1e4fa164f0bf904b830f3685961347c73e4f70af145adacee3ce791029b44b9dac4ba351281f7763ceb9a7f3118516398b97dded9c855828dcdc19bd141f5028fa1fd27e8ff309a57a7072697bfa53c7135a9ec7d0bb2179889c4e681142c831316977fb9cd05b5247ba1fe6618cf94da89debd7843eeaca4172ec8e097d26f4cb66e1ca2bb9e71c137aaca8931ec6f9b0fc125acca37958137a5c99b684da1a76317f7c7e6057421f44c6fbbd85f0e39912da87e0f6a36c3fa89627a1fbf5a78bfdb399074b42710ddbbc1e1c097d1863c71fb75fc5144342d31c79ac377bd6107e841e3f7ca59c7b58d3631da1d8858ff81f5aa6cf46686e1fae337de5dbc9083db65d59ca7a26722e42fb0e7fb1b267d31c54843e4839d5a63111ba8f25237ef23ccaea10a186093216ee738cf271082579646ccce6fe9e21b4ea10bc83fdf0258f520865ff7c626dbe4f0e21b43dc91fdea7b6c80f42ddfc6521434c107ad21c3fcc960f84a2b9a5ba4273e8b18050f347b1872f9d64b63f28f251794ee2a498941f94081f0f47d2fccba60f8abfeb45ee880ffa75fc38b784f6a089f530e4d4e7aeb71e94bce8c9a942fa619907b562a6709bc3f606f1a0b659f076ef0e4a4c9cdc8b60d2e36107bd7fe43945dc6099d5410da9c28f34c41024468a0e5a327fbf0edb992c5273309d7cfe4b9192832e078cc3a8b46e65e2915020100684c150180804b8dc2e00a31408001030228fc482d1444e5469071400044528223e2e2c14201e161014140a160e044281303818060282a13018200c070382c0359ce67e49a47b5c54f1dd07e8aa76b4850e1d3b6145facf2950e060aa2d9544829c2cd02945ca743ad2a082db9d9f0fcbc84f361047fce44b6847e42966649281cadde704a7bd3597c48b64bc6667f54cff046dfbefdbc53d3ea10d7d7270ddb33143ccc8aa05b24366a686d5c4df934b8e3a151856832aec2e4f75cd676673442b41db20bb6d8de94965f867e9a998450245facaeabdb3453ebab314d70e9e912141de5c22a44385150b0a8cd699457d90ba69b69fe9f64ceb84751d5c3b9554aa0369f4aeb0904b9cc10a7a72de4aa8362a845d9d99f0b650606736b5ef34d7e670a2a773e3b8480146e62ebe04f6d1e141499bc06bf137bf4ee75de6397f0b829c679fa833dd81e5f37b32a7b995cbcfd099cb3e642ec484bbdf8bbb0bb30fef532cbd2bdfe70bd43e6ea002cdec0bd886528d3e1e379986db9e9a4cda2539fd685721927be9ec061dee3a3737cb5cbeaecb4f7daaeed0e83d9fd99865dbb156ba6ee266d399c4ea63b88243c29c920cab7e7a88774d9c2a4d19251d9a7d6f8abf22d6d3d3878a9eff84a7f94e97f8fb7851e09dcd1b1dfdce66c91c71d08dc26c58816e84959339a4df76331ba6ccd638096847d14fb0ee0d66eaa200f549916475796bb517b058e5577fdf8b73cbf922a39f23a977ca1738f169dfcd85dc303541eba7cc5c8d84802434643ce57c9ab20e1d2ad761d71acb67f6e91f3a2d52cc22d9371f47c5881807fb609aa5afd47911d348c699d4258b381584bda66572a95c6efe4d9916715ca2ecbc9b99f2e30602f5897f25e9fbf8d5238a1c4da89bbcfdba9ac762c449de1572cb999fd2fd5d728a53bd998bc46b3d5e40899ab8bb736ee2ac3736dc0efcf8943dc891835cff8e4eec3617c67d1c2412a7d9cbaec45444ac59a070c18cfb9d12c326f4cf40dbbb3da17e8206db8ae9ff6dce6152d4c1954ccefc4c7ad8b5415146aa172387541cc00d263a19b1428fa2750c622adcab13f41d1a84a72226353c1598a9f477a41cf5dc3433c38bcff24d2e058fabbb8eedd1e5d83a0bf3fb3d7bc8838b8cdf1a3954e2d156f97e3dec6dedc31a5b1fa92285ad785b2eb2996135ed6befe8f77da953962688e38e5d26cf329edc79946f75f717beab339d2eda0efc6e5d66bdef959cd7cdaf7d566790462fbad08eec5de56aec4f4966c35ba092dfbfa1aceecc8464e5fe6c8dce29322d40e564e43d627c1fff1145dd6ef71845ff906655d08b384670833a74bcb05703af1e0429503530a2fdb96fd4f70333234f123baf1a1d85e8382f6191f791d7ac7bab67f4bea168e0797d1bcd9a9b9ce6f4ff3e1af449a290cecaa7a8c3da2ea5ada4c5d92fa31fa5e48f423192aad3b8e155be51d8073ecd6cf14f779ab3a2d10b16d07bf6abbfe8fd9cc6bce8a45640fc21117dcb9539509c6a5b83499a981a1f610c2ba9374b1cc62b22b39a3aa9d891021c1a27354a33f798c12d56061649f35bba364b5021b577e71273413b5ecade65ce60aef8939dd3a261b576aa909b397cd46c9551e03ba72ebaf21398383cb46bb4d14e6a6c2463d394361bc17c364d38758529d7a99b3c9cd6d49f72ec44465ff9e83a1ec18ea3a64e1893380008c5a3a212aff9f15a39ba733aca162a0d32cb338af3008d3d3a1c56e38acb7d31d8554def87cc6f26d33c8cff47093a9317bc20438f03fd5f84045efe04649607b7dad305b44c38cbfb7997e4b21aefa6adbe5a76b974fe343ba10ac1701062d30f3c3857e3d661adb005d889277fee2ef260709e40ee55c2ec4c8399b4ef684eb7c81238d734ce51e2197f3073c1c44051f863268c4b2cc2cfe4ba6df2c02437ce97f00698f5c167f5ced4af050e8149edf6f3336df323ac1849846a736e61863b3436b5e0a9d49e52bf39b7f2bf7e1712e6dc9292b12ce4c00534b210582e3d58b56484282c2ef8ec4a6cc2f6cecdcd9f51cf52042748721c63c5d23d394f70e5c5a0366fa9606a4069f944e8c4e5b3f8c64d68455331f91e4569c2f87f42827d5b076147aa3978b0fe60ea8c2e6330717e2196ee0491246188e7bff9f61f0ed1f8fb19557168fd7b9f988c2e4a8a7fd615247b4042320ac502726943ff15788751be2089104ecccfaf184fcbb6293da0f9022df1ac05caa105bd03c8e690eed2fa43c087d4b37dd5741b0192e7a2e985413aeb01b84e05ab36cf4aaaca8a973bcc2d0104c5ec87e2148eb1a6a7a6dbb6b1272fb3b9d526743cf9abdbc8fc528d9b5bfc654381058a864407973ab031eedfb72b0665c46809de4b30a725b35aa65ad37b6d6885a9fe01ce43434e10329a6a17f287b752c8e935b0a1f4a9edc336925885544d969075fecf36720af5c40b75951d49b5df0c012724d71aa54f9a423681f76729a091562aceb6850371e2b6e0b191529f27064ea0bef41c90785b22b582f4fdbfc88b2768999e70308ed929e11e49e6834485c7cf57ae924dd47367b9d17ad68c2726319902cfd0177d692c2af0992dfd702a4317ac295c728ece8627601ab52f0ab6709565b477d7e2b277f1ebc24450a164967ff24d60011bdbfe2dc65eae5d92526658adf08318cd0c1e7e82f28a0117e3ab7534940fd2139811212195e4f55952ffc36e96fc5f586a8b78a5e811be7777cb7d4879b5a0a21e29945fdec4481eecf1a7ec45c06efdf6403706fff8f8952763225dc552609889ac4f6e93b485589e8ca53fd35466d123577c19eafe4b4c9d73dc7474b46866191377e98ea3b932e10aa0b9a25fbb66572b2425a526ad8b01a962628d2534ee240cb3b99864ab9e1bad585a24ac55f536bf1ccd0ccd20b627709963acba796b4e2ba576033554cc018f8114659af930205db0959f06d9adb4c04a52d644533c38d97c93bd17451a1a1ca1b9c65f1fb6c6abd7b7400c0599c9aa207430dad3aa5804e8f9f127d5be40c7d1b15d22027641433ca8e708dc76819c611fc85acbac49210fc84bf0f553b879a0013ba4a2662f3192c756584a6d0bc45f1a03dfbadefcfeae31379d14f4bbf25ae3553a8192eb73b85266774699821a77e3cc1676d72c7ca1d8d4126752dff500631ad547a8ae0a9b519382aac1d61f74475b13d1b437a19752909d9c29780bb61f40124b0e1669ad282937818f6eeaeac1a147f9ddaaa5a00106e97f314346367e01c11d7b0224517e90843b69af7e7a83e04610cfdbd2e06678e40de60917f761552f11d3686481069b4a15aeb10723589ba448e9773f026b65af1fb77ef48383729d2563a0ccbdfc197c0ee454a3311cac8a41d2173101b79505169e995902dc244250ec56429ef79081527478317627268fcad1ee12476ded224cec369793f8b645c21a2cc0f892e4eb591d0b1e76b325b7e48b9b6689d3001a82572598f5015dd85a2cfd51e19b0dc584536300629ed4985424fb8ca9d0b70eaf810d9fc053d7f85c8cfc82f4778618516e85666b91b3597c29e49104ac5a5207cef523d22bfd6aced08a493c325711d89573e40c04da0a2eb155e6b5845aa4129e21f32cb540e1da87c2718613eb28c128575de6c6c7a881e22bc3637b7c907050d50af0ef5d3be2e19f4a57dc53661e95855301f8820a596f30a69f9228321324e8c2a360868822ec30d242440eab23af002035e779af8e7065e2c114e16abe04a83cd675d6f01c36e8a518241376be7f4ef80ca8698b107a3c36c3b4ee160141f3e323a771a51621c8e898d064dca05066ab7e0c959cc4cc8d292093d4f7433386897e12a3cebf97ee1a3ce06ab8972f59884a2c6e79620186850e181aa614afebdb686680797e0bdf55007a8cd5037d9a84a18984aa593a08d6ead11e3303061af95ce603d3e4411e309f50880633ff18cb2ac98ca141c72f2a73a0a366f63c873caa8c301b0a0e66f89667a71345ff23dda58acde8fdda2e6f0107e4727ca005241cb2637bbbae324edaa007e145d0924383a30b906c2a40de20807732fc286741ad68d158909ad2d66d3343dc448a7c2d08bed592f7d5594f2185179751687a1f830f0aa361e2b078b03e89992147865d27fe29b3df53a2981137b961bc00d45e16239736ac51b930c03fcbf5518067995353280fda3afaf3591002e47f9c8d1ad88a043ec20c2b74c6e70550bd01c0c4dbde7820f468be866236d65683420c668ba7e4aabbb22cadb8b39e63ded237b679d8fdfcde522eebc33d18b767295f04b7a9f7b87338affc2271d40394ef33327d0c314a86b034936cacd6f7857c24a62ee0443130fa4ac46ea7fa9305085507ac300e9602b29b3194dca64dfcb282b370686f9e84d94e3a1be8e0e0c428f5f83c1ded00801cb2f6d14431c78c688b5f1dd4099bde41456b34ba8774f84a4657240c9d17d3987612c235396aeda2bbb8af9fe40469b714770c9dd4a76c8809a84ef818b2100fa07a06e77dc5a2581ffcce650a16192aa44d5134b8368260a3d5aa1dac8c6e6ffa45996d72c6ebcf7bb7d205f93b547d30261232f75ee83269d8ee950b66cc41af95f614eeec1f26bb10ef12b04bb009ba8e4481a955b77b83441dea33e23cb8147f6f6fd12d2608a61cb40fd72109420c6df46a596a1c9d4c1160779054a1c96a51c7af1cb40eb4030a5248a256665673ee8ddbeeb080f309512225673a618d8d25ed6e35ee8c17355685402d15ffffb530ca476c244fd19d625803d9db0952bd36cdc438f4f9f74eff9c019065433dea639b002da58acde7a5d8fc07f7c34aabdeaeb3588ae809409de31ae9125c808e4660da224c8e0d858d05a72f4598874c84b7947ba89c20ef8fde9c0928a3c45519f3e04d98db320bf3beb229a7c9704f6e547a1ccff8ec409aa228ea73356dddcbb39bc9bb5649ab141a192a2c7ac84097e0e97e9f159d1c7d9fbaa56a4397437750959ad4337aca914151025022847a023a1abed4a312daa0d3e4a867a93beaa634e555463a00618b6914d2286659181f1163dc15b37722020e4acd5847d640d74f7fc48f1e2f9fd92d471e0002f3f9d7acf10110cb8375c7cecf23d295e11146a6d9fb4cb336cf34bb6d32c98ae513d0d15858e7fae19d2c8cec469a229127161215a644cd022e5d6d4bf06554e6bb78c76412011e11500e98600f2bb90da43502bc4179816ee0391009b0343042c198fb66cf52075716eec2258220a04b70b21811c94206e8c86fa05704d43fca4a85ad07d951d939c449a8421e622c31a5d83578434fd05eb9d738880bcab0e20d614d804c3e9e3fa56231d11371dc4f41aa3b7f36686cfa743713dc749efbdee6a4cdaff8666c740b305dd984a0cdac9945aa2c2b6660df88d7c29961ec41e3514d6e005d51cc1e51318b43af90b24b2c26637c8fb8f7ca23d118d01588708538ba684cc7e77b023077ae3c1b1375b2a360a9b3620c4f93a8145e2c8a1eebcd97fa8a012275f24d38422078bfcb04794654a1ad3c36ab73204177a73f9b5294b79a2a0ed442f5f2a64b22ca267e979644609a51e662fb834603ba05baf2eea1a49b181f06b63d049fb2dcce479122c2c0bbd66a376e2169b1169eff26c64b451b3c031134f96b66980be37c0da1724ab1261ddabb7cb4d4c9f181e7c346053f4d4aec1f9c792a3889967598c1b771381b22920cda1f4f8bcec439e7e0799e9898b7e51cd79d6e8cea5a303ade6c14cfdfb1715afd16681a2def2a5d31e22d761ff244a5d81415742677af783f8436f8955590a128ebdbdf07949b188c4b25a36030d31fc03323e828997274b8144f9b3acaed8b37174d7caad890087e0a6abcd8696ab0b68112272655559bbc69bd836300203ebc44d1d4ab10f16c4127bf1422a6ff84c77918c58471d8754935c407bd01abb6d1af57b7538aba46d0c6353ce3415c07bf6d049905c5de6be26cb7b681056521934a24e94225c5483e558294dd066d2d8a65d6768ff747dabdec6f95f4aa083d253c1bbc53bd9a5e1c4f86e705ef90cadb98675a01bb87dcf3cc73d3dbe425d36d73a6f66c828745efd1f9cc73fe7941bcce9637c15fc47354eb4df1c07b9efcf7d9471e7699c1493e8fc35ba0c64f8970ba87878a0858a4557b0b50eb2604aec050963cb4ab578c8c9437c1f3c71aeae0e5fd5408a70c1e0ac01b94cffb4be3ef449d83f79c8ab0c76e1e7348515c38590c112825f76764ae4a890f78fb062c4d521705f6fe72d61668265eb064ddb74592fd9ba2ce120ba6d7272e449a71cf0e165ebcc40541f85c47e1e6a405cdf1b95c572dfea0ead126a0cff9b0810eeffe9cd032e0fcbb87b5127600ca480eb834e40fb01f8ee1708edfb2a33518db637111e33b582b6a8b0114c500f4acea6bb334da8f7582639e945e1a50c6eadb6664712adade155c4feab69f64919622ba8c17216dc0132404f9b736beed8a637803849440d540bc66d211be82614431f21629212b70880c49c2ef96685209a1a925ea68b011d683c4516ee9046c3d4eb5b65e4f9b37021aabf95c2f695dc8b4ea9019d2238792c602f59a487e4613d73d7cbbef0d7f8d2982b14870dbf70a614a5038cb1a50d7baf5ecde8e480d299e6fe8542e8039430c5d6c5caff185d2f842af9e825a60a9af57696a20abd1544a30ddc23c1105f9ad5c25044b3fdd03ecbc9ff5bbb5c4bd7c19a9548cc923c51bb596d50f9e61e1b9987b11878ad6535abf9634325d5acec033b45b642c793f338cf0ccd1e15adc46c4333b55e1717fe50bb968a0b10e1a8a3002f1e458312624a71c9219f1c752defc60402028588cd260023d48fa7f530ce423422efd2834f3cb6804de8e3671cd4348af5c8b46ba6a082b0ba74d1247004c6d5db81068a4e039f288e3a0c1aa370f3dbec6b550d0e2057f8af0e546927bbe1617c6ba61057ef5405cc90dbf925ec31aaca7d2831aed7ff307b9762db5a7d069e8d169f5ba4dabadd021ad33b258648956dd5229a2aab0a73c336901066781888d26264938311290ba18de49810ad26d51e8105f5ca0e54710905291b1ed85dad6c023949efdffa3485b3dec034b974f663f34526398eb97abe045297a9150d172c4125410984e94cb289a26abce2ab1a72588d4c708d70901724f9edde02821a9e0089536304cc34a5d2575447129f689fc7372df751b7a3895840f27ffdadccd9cd4bfd3eb7e650c6d0eae287e1320cabfe0bb4364303cfafae27af953afb78b8731b99918dc793853350da770c2a7a5010a96949cfe88f485b3ccb3169871f46741992e9c0b0e9edad5af6a3121a1c8f9c44fdcccd9d77bbf734136a8a1dc4182d6520e0e0be46e5fbec72404c5a373f69dea950415b0c59d106e3d76d2bd441349c0fb12af4456e317df5003bcdbdf9cc7bdc55def59e41c3e60331482ef737bee46bdab8bde38651dc8b9b70a14c797c188c3a7d154828cae223f215723bd2234cb612afb5bd8c7bcf6e0d6100a5f6f4a5bec54c018c6bf4244dde57ed02c9150de8856f7ab8ed852357aa1013aaf362c9984817f9c06fd2193ca2e798012eb73b31160dcb26a94c0dea7446cd7da39849024d324b19458d49ca71c7b19431e2d5acadc8eed25bab38faecf356b43cd165f215c5c4c91c6a999213d15fbd8f49452eb275096eabc17470a991378165fc810c0bc230cec8b2f0d2aecd02da835b310763738ade3cf4b7bd701a2af1b62da28b570e110e683331d0f3d81e18c21a7fd6d7de1093ee4b3706172c5388e433330d8178f402e04395fe0cf0e53152c960bc7ed3e4fcf057738eba53403d86441055297b891e62d2ba57be174437411b6dcad79dd3734546a7e3f47a023253df46b49931778ddacc12c797304c4aee2e5909116d143551d59ce2e36d3592fe644c4aeeba4f80b0af45e545b8e13e7bc30e0bb5013c0422f44bc4501f3c8eef5f001f501ad6506b0f7b7784e8436fc77076164b4e7f441e547a5d9c202dd5ccfdd64ca3628e78842cc3d13d5cac27ccd974fbe8d48fef5edaa99dcbd001a741e583ae81339df2e5151e8f93412b13892a3d279911e526d87d071bc4538e34593d54dd099a7255c5c19b8a64b4269574c90a00082c24921c5fe4590a40bf0003686555dee4574461465a088696c4877a26fdb34a925f530b328f64aaa701809a61b3348416de04733629860352caa3d1007599d1889de0cdf06438420fc91e4984554b1564be001c7f1f230956ff6322c4f0174d48ef893d30c3fa42de391f66a72e2ed2427f2ed33dcf2983c11a8cfa039a908f95c9fddc4cfec08d1d910cd93db06b45396a3882c8ea0255466676648255be11ffe2122d9cb6920dd4e658fc9fe9bf07f7da23b61e892532efc7ed5e8760be0413bd03b2a9bafb1c81020ff8d439154c73fd50229b05584ccfa98c394e8383cf077819eece1bc0321403ae4977792be719a09d9a2a2249d35a406e854112df13804f93cb7b83508eb74c8c5497b897148d0bdbb1768b51cb09458a8f79c42d3222d3d35dcbef9965b98dd3505f5b8b060a2d255f2eed39c732ceafe26a516b85167b8176871a411c15ab086e6dd2c3ee27036080e410ebc2889ef60224f3fc95a32c2f760bd607a1559d9819130020209826b86bef9c1e25ffc8003bc2cea31e486fa1641c0ed41132611ec016743f83e718dcae4dc6bac33b81f7421b1af4eba52b469fde083003fbbcae3ef0893a3335ebe42663d7b1149727bb68db117c4b33eee376b7baab15e1bd46eb84cda0e6e6f56fbd5585420ca64b43f99444551105fe70e247ba1ab7e0bd8355588a6b2eb560749b5612e571619d335803bcfe68655ac47ddb8b814793bf0e96da53f9728a77da01b4d65712681467d32185f22966da90e39f7ef6a4774d692f8a02710dd70ee1b598e47bec09fc89cf0b06bece904f50d110c35c6fe6cdd7f4d945bba9b7e3a9c4271f52020c38c9af592d8728b394d9b84faaba36c4d9c1624b35eb8d788f56b1833d2399645fb72a9c7d6f05e75ff682920930c5661f2440fff5abab6a4f7c87873c069fdae269d476b57a102e9e24ac1218e4b36c6b52c73e92d631b0c9d81e1bb4f779b1a53e3ed913a77cf62385668a244208c88f6e6777bcd94654fcde97265d61bc4f0a3d029253147302dcb04cf5abf8d2ec33c0952f88da4c151aae0ce9bd67a10265a3b98206a05ff23cc2520706a499032e1e05a059b9c5fd33008239c2b09569931df0da4a3111a89c731d75c31c29f0600c458438255fa6d046630cfd8f3e6880f425c67d9f0c7dd3af45532d1b9e86bc3429843548839fc5e15ee58479c37d2920ac848d31b6f75a7b0bc6ba71b5d9dadc9b9145aba5b94c4b614d8e81859bdd9847fdd222aa69517070b65c9e51cde55ce908bf1c5c86773ef71ccb7ee39d139461c5f18d3f59ca921f1fe2d877ffce2cf1278df99dd70ca4211e3b04ecf1f51ef0814515d2ab95ee3f3e064761c25657a3c52eb848500cb2042057991966be457d4886bb5663e973253623f8f604ff800b5449e809aedff2ea23e7c3b9ada5c0893fdcb5947ef9b00df41d4da7f0a15e9ee905f754cdd9c9c2886dc731056aaba3db5a1b1a05a28fe98e5fd4948ca121a40fb387f3af5249cb4e6859a8ad79c3bfbaf55a6321ccfa94b02c12805c22f1c13332cb397818e5dee670cb6a402af6cd852553037eaab86eed74cccac9a14bc880b6c3e72a759d452b851e1a0bd66fe929a7781ec3dde77d250a1544307ff04743d96a087f505b3b80035ef7364e5bcc6a9b7b31801cf222e281e7a2fb6205c7f59dca7cb05d86297b49cc9f25435ef506b760e963d3f3fa609006d25671479ea70a34258eea7b12cfadad0a35fce8d1bc73649824e3d5b2d08440548b325b54a060ed38f15c4652b4cff8821f3042ce407c98edaa5f7e51d464ace071d1ac300f5c27324ce508a04bbbd06a5a2ab08b7e3971ba7b92b5b604e361a38a0d64d2f757a6694703e9bd7da750f807ffa26a2a8e466f9a48180dcf5fa4876e52b4d6e8d8e940908804b3732b7aa9a89aac8825e6ecfadb2652354151e290b0404e7515af470738e08d90136bdd58a05a108b5437c35bda033d511284c450410f4f4c4ccb7c710179507a34127569133dfb87357789bcc651935fd760206d42cd98ee86eefa42761d5749fa4888df0f20385fc02658c0247235ee99eb87055502589539cf2186cbacb7ab9c28c4ca91f45cf86a47aa783b71a75a75c5ba7b5484151241eee77277cbec0bd7e96de33be74009f51bf3ba60328c4ecdca486b307e812a821a4207c877bdc2833cfac604d924b6435f0613085a6595745abec450862f21b580f5855716ab08cdfa99c2079c7cde35e9ba44d3132f5468e14bd034fd643c0da10006eb5ae164df21732ef7015454b01489c686d33ad9a090130ebb84e7d853a1f7c5dbed209ed20a1ca7a9e9efe43f2342b203db0a59754675bf86f194b09181af8287ebd83b4ab99bb5b2d5bbaaaa80e410f65d4c433dfa2a6af447290e3340d7cf661f38816d384dd65fce66f482a2de8579b548b381a5b7b61dfd0e70eaa8385bae60b7853dcea92b205b521e4c53c3468984a3e54a51609c5c70280096a18ca880301dbe7b6832fdb951cfd63904f5aac79dce7303c73013d96a58f1a8dd736940ef8a71c9909f774490d3e1fdcec821de3ec9979373f6d78bf41fae6a890719a2cffeefd96847863277f2edf9ec363c0efd18f55dec83849fbaf48ba040d8cc67e28d85be8b5b2b23fa996cd8ee9001df832f40ec3f84d565cdd8b5071ee85e91f8a41eaf0b08903ed35ea944e607803fae0c995e30946c4403575a371cc2938890926d2b568cc3b6e0ead5483bd9e94e1dea710922e7081bb3b2e7d91d076b5a08ceb6fc798654fc77db9050ca9cf65f9c56a99a501d02431d700a76536889cc16d7c9428d1c5ec28755d3d7e8d4f3895aeeb3432264a87c313912fec9ef68ab8d477558a3d5460aa46129a0c3d97fad915897db7cb52c00e5cd4816155229825a85dee61e562399bec571c232556f647a1cb71e2cb0302d43ddcbc5850f5e950ac0ebdbf47d4706809826e4888254f03e2847b4663453e4dd0dbaf928fd0d7e49dc1a76a2cd289ab55432efadce37b7ca00ec4216a252a4eb351c315471474a1f6edcd9d8fd1c0f2bb9e12117187d841099cedb3cd9ff3be25fcf3e82745f52b3e530f487ce9e31cb4de4c5ec68ce83b0158c77f3821501f60ce807050fdaef4b7d0ef4edf427de3b442fe49bf413635e61b01e049a779023a38f10c2de5b9cfa8b8f30d73eac844ae139c4200616fd88970f24d1f2b47aa42a5d36f9cb772e5506e61e6171ec938d5823561e282c72a65ed78fb081616d0891c5ff9e269fc5e824d4be72ff142274c9873dd29c23584cb824a7953645cbcf38b3bfac19a271b1b1e63c6aeca627fab1800e32aac7061c78838abededec12083b2b5a775c8794a67a9af66f5a4a0974b946d5a14d1a3044c0b11e2661c56bb88df9498198d4aa82157e977d87dcc57f1617d965d4cb455c61fc5b5d04030a385469d8de857dba5f812e0d240e5d77bf997101df8fc4074a920f4344f9f79624c90fc2185a18d87d6a813bb4a9f86a70bc1a2902031f37a463d80eeb7bef661e27ba165bd243f0e4a07131a36a195621f5914cdf6f20c65954010504870ff73567b24170054729d2b470c47f158ebd45d8a9630d85c05ab059985dc853802ae6afbaec633f3cad834b54d6b5d059720af48ce0340e2b0cf8661d8edd3297f580c3ce20b8e382672e1088d030141b3b6260ba1ba4d629d4f123a6e150f43687882b10a2e8818ccd0026ce0abfc3457e71c72d3c4398a5a7291bb13f6676c091c6eb2a3fa0b827bc8d16650a415e788e9bafd03d4ad97ae149e4fbd731653cf494c415d16c15ec4d33d42e97b99ad8e30beb64412a42323a7f339aa45d3f3ccf4507121d1b4acc0680a21fbda6118746f07ae12dc5adfaeb3d127ed044b78c18975f22c654f33945616d707be8fcc69c11fd4d658b3e206b2ef9bd0f466def3c6d43f5ad377999f9f31197b4f12c5612165e6a6b1acb542bd2be01d2c71a5739e1097f039d61b8def26aae03a6a764df8a8279996cbf855e7bbee0e9aa45d12b14569a784fa4366d39349fb62b1e2a602feb15738d5514246e714bb75e6f55c3795cb45b0bca11decdf431b656e89b45a97571a7f55821f75e868a42d29bc42a3e7dc773d39051e55264a17ae242413c31e89af4bebe22655f5ca13f9e628fb407a16b8ea56194f1018f36870c49ca808e54454d69bd184708bf33114d56251380c254e07f86e16bb2c3b5754b41300f04957105d7dc68ffbe9073c3098ce21485852d073a7c5795b3daa7dcdfc4aaeab03520a7c61acc9ba081c7d00b71c90fd07fcfcc85a5dcbfb059b8b0df4693bc5773966a7ffe5882687bab6127940aa2a16af55399e363a8f3128d1613a11cf71d3b2f95e48a88f98d6f496a8303cf785d1f4d30d88651e82c8ce43315854746abb9e2924cda064827513161a28a051133261349689a2faca602eecbc4180c3150b25472c33f3f3f3f3f3f3f3fbfe221b0b1496e694e7fa5e7e6a64cca4caa61140000a300b4669b66fa7501a380550004870b110c370ce57fae9040b0229553dc8ec3f4bb0282f70aadce39595e56ffc0eda658517e261e513fb0b1eaf207216d07a1f681ddcc95b2931c8da67ce0aff3fbc78d35d1937b60b2f26a0e7aea81cbf4b20e53764f95cc03af39bc7a4952df76e281cb4b76eb91e70f39f20e4c8c9863e4cb717f1cb4031ffd6ac7f9939ee6b10eeca578313aba8875413a3016244dbcf28d94e01c18eb33358f7278e18172e0d2e74e7949731c24471cf838354a5fcad0981d1cf83fefc8f17b10bab637f0f9d1bb7906b33c3137301eaff3e7460a765a1bf86a4f5e41670353f12c932697a89635f01e723fd5a6af704b0ddcb77ef87135f2a695063687e68f837839cd2c34305dab51aaab19b43a03b79ff22dc40c7c64c924bf4a07a153063eb8747bf4edbb0c19b8a01dfd6f5b4e53c7c08e57fd8748470cbce7f657bf4e18f8d03ab8f9e5c8b1c5c1c0aa5b4893693ce2e75fe0729e9018dd036d8d1738d70fd9dfa99e6cd305de2248b41cf4fd480c17d8209e2db0692be57fef285f5a0b4c0ccf397db2c0ee8da84715d2e5c002ab9ea3f5d29c9d2357e0ed3aceccd50a5c8a93c3f1b85205ae6a722d5854e063aefc217720554f81adfb403549c8e144450afc25afb4dcb9b23651e0d28e7545d574ff4281d74b1d5ed55f80271c9252b3c6bc13d8e49719a52d9a8bdb042687623176d65ecb96097cceae9de38a96324f96c0e41c3b4839addf07122570492cf353c8549d93043eecb073ee8d047ebb3e238ec71edd23f01e2dc6a9eb7c018cc06bb54885fabc48e617a0086c0c92d972d0a1abcb2f0011b81c574f5ac60a3905bf004360a2e8b7e6d4bcbdf00b2004a6d737d5e4588cfcf80204818f69f2dae7f4f7f5f105000263db714894f8e1bec783f10b2ec4ec1b57a525851c0f862fb8288b99237bc1f724cd71ee783078c1457f7b6bfb24a13b1e8c5d30713d96ba20ee993a1e0c5df0b179578c6945d27f0f462eb810a3c7ee7e5f1e730f062eb84f75c9e1e51e8c5b7029784473cfff34efc1b045153db66ac1fa76c5cad92942be68c1aba46e89ce91264bfd2cd8b34bf65e1e2e0b5622c69cd268aba5ea63c1e86fc59dbc29f5430f0b3ea44715f31a8f3afd2b9898ef3907295bb546ef0a4e6205d30fcf46c7d30abec3b4cc61072bb85831540e234e04efac828d99bba35533bd56154cc7c80ded51fc187e2a1849e9fd71fc8f56e3a8603c7b58c1263fce9d53303abe95462a3fd0144cda6e4bfcb28fa34ac1558ef3e85d44dbc99082eba82b0ec62838a95092e949cd238b83210ad6d7a38dbe4a28b8de389d9e7a2c4715507039dac4cfac7c821d9de4baee5bf9b627b81829bee59eae90dc4eb099e9953935521297136cfc1c9a6a79bc39c76e82d55069a2eea561219ae02f4f8c93ef3fe42824134c6930d760124cb011436e9334c95207b904e7b795fdb2dc2a554bf01d5ec86d51af93d24ab0a95fd2754f4af0a5591d468f3ee70d4e82df941cc458a32498b86dbad375d99318096e3cc4689e19b4420809467bd3e758ec3a73c72358f776914cb772ed70041f6a478ceb1dd6564723f8aa9c3cfdc756793c6704379d537b83bba676be082e9deedd4aa7085623a649a407121e4b041f66c93f5521376f86083ed27d5ad4891d323b046b1a436a9884689b32041b720731fc26e4cc94178293ac5c1bc9f2e620e584602f743f088d97b7960f82af088d4c527983562e082ea4ce548b29f661c803c14ed75d4a355fcf1c40301eb44b7a5ea4d3ff0f4c743535d70f5cdf7a7b1e7f0fb2ef03ef9f2269fd368e7ce0c3eff71c5d9687760f4cf6782fa5765696e881df2429761ce57830f2c0e546e7b4a3b9961b03061e58dd1452c8a71335c61830eec076e4f0959668b31803861d3893a841347d47951803461d78b1d8c14b54ffd36dc0a00367d771472239568dda8031072674f9a6e572e05cd4f27be8f4381907be63997ab9e4d8d3c281d31ce8d54793b2f70d6ce43834689aa46811377059338fa65abefcdbc0f4a88bc4081edac20636e6c85e73ee7a6a750d7c89c4983b4d35709e5252462dd3c0e730259a77341d4744039372e758d3079e81b7158da9927e5d6d0676a7ec7c2a5ee4f72803df9145ce31a7205e1d64603b448bb1726e0c4c9ba647be2a4db6183833c3c06a442e8fa6966b3330701de87720591930bec0e6fbc721f2a31c64c880e10547cd83df7f64bac0248b1ec5f3471362b8c076e891f4942db09aba933c2db01acda4b39fc5089d2c3031ad520e2c8f05de543cee94e30aac69dc8db53b55d55b810b91a15386e47184be0a7c1c7d464766bd4b9d0a7cee1b6bfbd6cd69330526bd9ed596460a6cc80e92e3a84a251d05ae4e3f887b7601030aa6249221eab1058c27f0f9f33beb72e0516e0b184ea02e67dcc80b6902a795249ae689dcb184098cc794c3dfbff315c912984e9dc30d7d1f3b234a60f28edde4ad987591243099affe3b12f87e37b10ea29fbe47604a62ea9de6ca4baa11f8ec717f24ea71d6a745e04d42f0379190c253223099339e27cf1078cb4c7e17cac35c1102fbb1cc42c490930e4610f8f43a6e1df4e6980303087ce8c9cd6297bfe0926d724b66fa8249bf1b3287f88d057bc1654ca92cbb684e09f2820f1ec4d770098f225876c1a6a6c77429ea82ef14cfa7d443ace0e582bbcbadd54952868ec3056fbb1ec4ccb92aa7d82df810c37bd342936bca16fcdb654bfbad5fb16ac179748e2bc5aa6ca92ab4e03f7dcaf34823f54b6516bce78821dd279745a9c88289fbb59597738d1d0b6ecca2640e3e2cf858a235047daf16ff15ec74ea90c30f1943ee5dc1b64a59b6ed5bc149a4dca16394a68eb382936029e4a6b1feacaf82ff9861e21faef5c455c147a51d55c4d5cb104f056b221d4f8d4775a8a3820f3b8ea8ed71902a99537062922e193505931453b01f49faf3f0b3eed3a538f00f935a789a147c985d66de9a35df1e05532e796e137cc26b517015db73fff32ec77528f8b4cc4b3935a3a516148cb4a68916ca3eeac927b898246bda649da3219ee0b322a8e7ef78f8924eb0b7fe972d4ffe0809279aa8f136c975139cea04c914b27774ab26b8b7bdcc0e72844a9a09fe4359c8d77b1e3b89092ec71b730e32ed7b262fc19a97de665868ef484bb02ff9c5f3a48eb38f953063d050828f434a21f7794a8993e04e4287b4fd0b0d51125c5eefe439a41425c248f081a414735ed51cd60724b8a47a1699ea973e8fe053a48e39a576ed1447b0fb9a721cea7523b8906f633abd146bbb66049bdbe793e38b6043a6ccd39ca3b46a8ae043fe8ead56c2f4b444f0579136638e33e61b117c947c36297d5ebeeb10ac7d98b6c31c444bd93204dff6fd710c9dda3fa80bc17e8eb4347fd657474d08b6c3904a3b6b3d729c41b091420e9dd925be1d41b076f126ab470fd71208eea3cc659923e4407d40b01b27f2bce3ffc04964bb9565a4d8713fa82f91225ea50ffc6db0cd15fc553be7031377637b8787a5ccf7c0474992fc48228f6ff4c0574e871e6b3c3d79e06274f4beca140f163c701d0689e6793cce63b903bf7942d2c78fdebcedc0e54bed4fb2ebc08712a3a7a91c6af4d0814b15d2fd2773d8d3c7398e3a9ac8814f3ee2ea917d040989032fd213e38514a472040e4c5964e6939473eabc81cf410a71427eca6671033f69ec3dc2fea36d0393438d51192a1bd8501fc4944c3b32d435303932a428a9c93e48d5c087695b39d0320d6ceaa02255336f4e251ab8cc2f2148fdf9fe7806deb23ea6110f9376680626dff5954f49577594810be6d1f6eae5d77664e0272b5f2ecf1fe8c6c6c069a8ba9c2f764a0f13039b4cd73dd20d693d0b03af9582a6e40103e7996d3a75ee9b7e8189b4d7e821b162585e304fa36f4a9d2e30216aaf049f960b7c875c29e6abca4ed26e81b129fbec23ad16784bb5f7141e45325960b249c8cacf16291e2cf0ae7ebe2931b37be70a6cc839d6b6ee7d0f6305c6eb3e9ba4ad55481558d1a9e920f754e0a3de2dcff3a3cb7e0aecf5a878865e0a7c47e7385e8ff6b18e022769937adc39a46a0305ee2e93a7ec255d59f3042ee79a69ef6a5fde09ace7c03ddc9bc0a4dac951d948ba0d13b8bbf7a9fc2884004be0e284903ade60e195253408e666ae09b6a30f2169ea4e345d32c155e4ddfdf8ca36f203137cd0789351f7368f2f7817fcba805ff02ef80ee312dc5abc4c16b3277ef05882abf6509ad3a132b4ce30e3282b6154e2c8d6cd398cc8a10467316e79dc297e962c93e034f7f4e4eb13497725c1a70eea7133c736cd31124c0ec4d433d8a4298b850109c6c34a96234453f11432c27804e3e165b64a88a4972304613882fbfc49bb11f38d60f2bd784e31d4479d1546f03f5929f249d48e83e8221857c9b1675fbfa70986a1082e3cfeb4e992a40cad31c618638c336630c6187d460d7a13816120e2c608e310e77febe83e118461083e5649aecec93ae4e938d405611482536df5d78e4a4f631d062138fb89691d769626db83e0cd927be65a7da4d58521082e7a70ef7c1ea6e6646104828f5247ff1ecf1353bec200045be92c447ae8c0438cfc81cf51fac7f58c1fd82cad1e558a173fd6541f3811edec38ba90f73b990fc2e003a71d058f651d6dcee9ec818fccb2ae50413db09d7cd22509ab4a9592073ed3c31c1e638807cee33079cc97a33b70953f8e33c7fa495dd31d84610736b3e3f82a0ec2a803971f216cef63746042e6499bccbc4c6a3307aecd6c54525447969d1cd811afcefc1e19076efab4f7e376f4f79c8203dbb1ae6fece8f42e585408e30d6ccccccc164277031fdd81640fcf17cfba0d7ce665db89e961c690610317f953d9e4c8629ee71a183f5d916afbb0534a35b097ed753b35a1391cd3c04e0a51cc3386a0be3a1ad8f3bd1c57ce9a9ce37b06ee2aecf762beccc07af630ac36a40e831554e0781046191857adb6902cde8f7964e07f327898f36e7706cf18b8a0d1636df51031b09b42aa9d6a93e813828430c2c086e68e3b19543070d1a3b8512958482be117b8cb110f5a25ffae2747185e603da5ff68bf1f0ba30b6caabf57919c33a70675100617f8d052693246e6eb9d2c0a616c81f3df4d5df993730cde0b1604616881a9c8513a422dcd52c8b18730b2c0fe86eb479b76cece868105c6e279a6981ef7dfd4615c81bfed1493a758953a088661056df2c30f31c6dfcef93f184615d84df13a47317efe45461b210c2ab0993fb3e3601e87eee31458cfd11d713f0c2970965a57ebf3630e3d5160423249ed972757db79868230a0c0daa66433f3ec61bc5020c278025395f726a494fb93629cc087541f2569fe46b54c13b824d593367d6dca95c2045e2a6378eb4bc7b1a42c818fbcf227b13af720ba4ae0623c9d8e3e9a654c3709ecd7eac6106d15c9572430ea9ab37354d123b07e2be1e1213c9e381a81dfa0632958945c1f824560b326692eff4cb6f98308ac85a764e61173a5ff86c07e643eb51b9a10989038be51b24f0c761058f328bbc4b1f4e84c0a03084c468fa3b6e3c81052e717dcf7e42eafdcc841f70593317be0a187781cd2ef05e329b9e75ef5974c0d2fd8efb0a2b3e577174c3ecdbe395c4d17dcee8defb7e77cda6172c1e41432c7cb1fa47e1c8a0b263553ee3cadd398730b2e74107434055d31136dc1664989d439a3ae7bae167c744b31435c69c17e9c3cbc2bfbb03cd066c1871c442778d0d19005ef932f4d07ae1ea47962c1e55bcacb71944e3233b0e05fa3a3caddb13a1e5fc1ded9ea4a9e1c0721e70a56522488f5c7984deb56b029b13bdd8ae59074c20a3e1eadbc922d27ea9dab6042ac98f552fe79eaa8829b68d937b58906b14e033452c1ffe694555443c727390d54f07184ea20670f314bfe2998142cc42d4d932938f730abc518371aa5e0537b5817828648c19705c9ba9b937f1e63038d5130153c6d697845148c59d6cada2e6e559150b0ebe671e441e6a4ec0b14dc779c727ce92289e5141a9f60f2b5a4183afc3873f8d1f004d7718cf4e59fca256b343ac17767eb6c3675d617a4c109aee3f4e44d6a96e44fe3d83a7baa7b99ad092e4acecd58a1427256052b60c19f519a092eca73103d3e9acf474c188d4b701f885844c4f08c2195020d4b7013514fed63d5a5eac719685482b1891c9d95a34dad86126c797a4b884e92630ba13109aea4af3d327aec972e92e0d3fa8f44e89e48f18f0417e1d1879843d4ce9d8704932ce66cb5d9375f873e82b1f21c55df4fb0481e0d47f0715dfced5472b22b1a8d60bdf3ba8398e34fcf7198800623d8cf899dd2ae9e79762c82cb9e3429519e151da7033414c1a40e37c690f06824828f6255f43416ccf4541a8860b3c58e7332e40d0cda9061866f0c681c828b9e735c62ae1d06af82262306358041bf0b0c0d43b0ab7d1d121d2bc4c8518656cd804621d8a9ccd1b3be55ec143a83062178b5b434ed7639f4f87e41c3800c335c734063107c9462f964766b91e84943d01004ebae617d79c7ddb70f041b4983f407f350ed3733d000049793aad245a598daf13ff0913b5d5abcf36bb4fdc07a0afe5daa91a309da621ff830bba7f638c7e10217aca0f0813dcfdabf6929dad38334f6c0eec7fd9ba2376ea0a107beb355eee7dfaa1c3134f2c08ada7798b4223cf0591382db5404a974dea0394077e022c4e390b32abee76b0c34ecc045e57b101937fa77a80c1aa800066f810e6c5d1db83e3fabbcdc568faa0d34e8300736c4a8ae4e297b1c960317c420068de4e4c0de7f744ff97d299a180726ddfe77353830eae1c6ca1e0493e8c03770f13248ed7adcc0d64a57941cc4b440a30d4cb6d3dc72f7d02fe6d9c0455bd14a151e84c61a188f83e5c778e91768a881b33dabd34e9963e83864c081461a98bccfdf4e3f8a80061ab838a9be12c173540759008d33b0136d7a3d0a4fbb92230334ccc08558914b542a8d028d32b0772e392978060a34c8c04f4766f57d68f3c839838c179871c6b9d280524d80c618386bad983e6ef6aa9a2aa021063642fe389aa55886d681031a612030b09e6a2fed85495a2b70410c62d06636a0f1853d6d56f7756c12b532b4687881b39446eb3fcacbd09ac1cfe0cfe802dbd271f41df484f230598696b9800617b8d5f7a8b3da3c684b51b180c616782f9b48bfb6d14a73b4c0ada7a8358f60f6d19e05563b74f83183876588c1021fa6daad85042feff70a9ead87b8d6162b70291e6de7ec359152abc0a68f4c969eb6ac46a5021f88646fe53aa7c0e6c80e6bc76387ff91021fbb6290f44481fb207d57dccb39a63c42817fc9ed8cc83f818b122ca25ec766d5111cbcbf0d1e6b40c3095cd0adcebc3839c0ff3000c0814613b88da57bd13f5267db683081ad98313d5aad2df74a06591a682ca1c6a0a104451a49b031905063d03802df92da399e8e989e23970c348cc0fdc741b2fd483554fb45e042a55bc8e81e3c874e0d3488a077142b87efa18ded6568bd195e3ca03104362bb68ed4c5888eb72ed01002651a21e251ab749c528e4986078b0c083482c0e520d2bfd5bf4d4aee8c32d00002e775c1a53d4504c9f6a842017ec1e5b0eb3cbde3290b9a02f882c9b154b4728f3e767c51a100bd60fb53c7183f4ae6196d180ac00b5e2f423f8e17f2acfb5d7021a4549a42c6b451d3ba603b4796a85ac9b1643517fc67c5241a24db34555028002eb8d839693dff6a76eac8a8d243016ec1068b999133dbf27a98591f0a600b4673707b9758d92a786ac1a4e8f4ced7a8f5711c5af0d99bf971aa743ad59905a71df425dd74ca82c921e64d1f46340daac782ffcf99628ebf2d5ea5c0828d995a3fe593e892d95770e9f2e68fa47a49a17505fbf6693d993ff4de482bb8ececdc1eba3fe45659c1df8634318dff7f0773155c96f65054a55405db31a3c7619ba32783a9e03c7eec74713bec2b880af63f8ef43e07d62938cfa99ac723680a3e7d55f75f785c31739482ff8e3ea5f59c7314f59182af1d3d4bafa951b0e691ee4a55e8258f28f874e9bd59735cf1180a05ffd13925677440c164e57af455b57969fd04571ea5a91fa2ec33ef09ae34879690dad2f2b0136cea28513df23838c144dfe4c7ec6f829364e913553445f2a3430134c14f768e9ef3bd4c5a2a4026989032c43d4ff78f2588092697998478779b110a70093e585bce91e86e097652d2db8ea3f0cd71490d05a80423a2b1c31c63bfea9445280025584b13aae34c933c5e8d150a30093e42e9664969fe5c8942010cc13ecc890497b45379dc595f3d1e124c674d67a51021e28fe02fb27ade8c69ed74049f2ca2cf737823b88e3fb68e9133820fa1935ac6dc1d42b3083eb4accee3ab15c2a2087e2a9366b590434e9544f0571d64ce6b23828f7235df985e8e601f820fd2ea61aab491c23604d3d6b1c73aba109cdba6ad1bd31ca72c21d8247dfd418a5b21b58360472a3d1669ad4e218260a2da727003c5689383d0492c9a0aa62ba76a376b6be81c15ac07219d295687a825418318a7e02b7608db1c32e4f8465330b9a742c73947bae3af14fc4847698f34523039b1236995ca76c1830c3146c14d7f1c96a1f57662c410051f74d3d4bd66cfd13e628482ad0fb33ecae91e3aa618a060b3566ec95a955a1196a19583bb81189f20627882ff20af43b5d2328f2c0d6274828ffd2be5aa0bed29fd32b46ec009763a3b7aff6212c9d318c4d80497dda7c47c2b867d258626d6b839922b6568b5c00c33ca7081afc08c10626482b5b70bb93d2964c8970c6260828dbdef2187a81fc6fbb20c625c824dc145434c663a6a1de7803268b08210ccc0052e6880011c6000187cc0000e3080030cd0010378c0020638a8d0127c5cbfcfb1d92706312ae19a25318b1e25257809be931df5425664124cada5ddb8112354f0be208624d860793c861cb8013122c185464f8cda9dbbc3be0a6240821109bd94c371cb28154205311ec1bfc4fcb03b4f56248f32b4c830c34f066238825b75f3d8d1be1d6688d108264a68a7c7d70cea3146f0116f63a5a07dfb6e21408c45f01d2c337dd02882554d957224ea413ef513c1774c17a2a79083cc410c44b0fb92d29a24ef3c558a71082e7fb277734f1b828b122bdae47883871029047bf6a3b97d1783106c5f4cd59fef171d688360f3e78e38a6d2271ee41a21862098a07a57e521f9fa3f6a108340f0be217ba5a73c4e758a01084e7ccc3b76fcfc810bfdfee167e37174397ee02e5fa67c5f925197d307bee3cf9159a60d2992870f8c8f78a6ffd0436878f6c0be7b8e39c89efef08e1e788f27a60f569d3cf0e96fde51aced20d5c10327a172dce1568c0e357f07c682464d9e35e7d8296f0736c71d86280921cc255f07aee387c9337a74bcb1a703571fb77e8c9aa36d8bcf814d1e72fd36678fb6b81cf83072ea0fc48c037ba67eb14a7298417438b09d83bed08f722269dec0e714430eed4082dbc50d4cca90c38cdf7ea9b781c9a1bb77a463fb940dec7a7d1047d3a389ba06267eca148d21a306be2a79d2d0d7754bc7c14603b769222a5db6474acfc0856458c75f3f0f4933f01e39470e9dab43fd2865e02bb5dd7dd8498366850c6c5772115db7c7c07ff4f4fe1e5e06535b0c4cea1b5f5fafc3c04ff0389adbd633ea05033751f5e97168b19df60bdce4108f29678eb12ae2052e455bcfa039860555bbc07887672dab758da1728193f08858f6133a5eba05de3c8cd1263ed5b9a3053eb61ca95ad4b3c094a7901d4fe88ad4b1c0aea8e4fe770eaa972bf0a97d5744b25660b3a4ecd19fa1f9c22af05932669df42515b85cf7f0b23805de2aaa75a899573993023f297edc1c1de2272ba3c047f96eb126595bb85060ff35d4777b76f7109fc0bf5688f2b2943c4e6024598e976d3ad00fd30436fa834756840cf983099c46ab31d5f158021fc7fe11ed26e7c7a14309dc76c42429720e72a827818921f884ec3d4f5e3924709a9234d6f9448c23f0217a98b204ed183931025fc9afbfa2a257885d113875a99c69d783c8be541552684d2162620c818b789b472bc6d110991842e0230f939e545d86ca1123084cc6918cba95796f2f3180c07ab766cc39bbdc27f50baeaaa31c6a86d96947f9820f2fbde03b487ad371a48de1f182fd74e74187dbafe62ed80f8f394d688cdd51a50b3e9cf4716ef7a8be7372c1e98b5eaeb8ffe53c2e78491e5a9e6a8ecbbd051bc43f9aa8d8828bde29e12984b2e041b5602f96a55b2d55d70ea9410bee3e773c2944f5a90f990597d3c64b39fdc3581ec3430d59b09f35a8ea763c39b78e05abe98107591b3ae20e0b7e2a3f429cd47911cc3ad47805d7ae9d394ac518d4c0ff0131a881bfbbe2742b6d6f8d928839f4ec9362eae0a697a1958333685035821aad48a37a88d6600593695983c59c2dda51656839da2aac862af83edfa4fd41c472fe656891118317987b810372e00218644003638c6166b082179ca5825157f70f6d7b4163880a36f859cc8abae9196a9c82f37c39d4ec51e6d85d620a3607193c65d5a895828d31a7b465d0dcc9bf04dc052b28c301a46062dacf514cb5ce0ecc5170e3a3a982dbea87270a3e59d68e12dcb263e7970b3542c15487b97368d4982a4441c1ff7f9a5a8fea137c559764b6e49ee0dff27d9047caa5634c27f898636b6d7b6d0ba182851a9c60b395a5dad6ca21a1c626f81442a7471ac3634b61086a6882dff548d27d9461a0462638c9f99aa38cf1c404976bd2a8b5a5f1127caed6a4faa86550c3127cbc1ee548fdd145d55f89f7438a21a5ff1812831a94e0d3644df0df0c2f5c418d49b02949f630a7cb51ae7d4b163524c178efd97bba3819736424d8cb815e0e2a25724702095672f44996c852a98e47b0bf295b4c15b523d8d2d431f1d1da18f44670d1f662a9790f83336a50d6001e88c00c0a153518c184d46ae9cddcb3d4bb082659c8b5f31c49ac5043117c8cb64134a737e9e0151a6a24828fcac36ec96d3e79ac29d440041f454c925ef27b085ea73447a79343685b13c0aa61082e9d6e8e466f2c04938267c67c1d722031ad41082e5db41c6e111a438d41b0fa614b88942977483504c1fe96869c9ee37695816034ea4e2d3a0e104cf5470e93878c16439c50e30f4c554e4ddb37c150c30fbcf4fb6bc8719a8c7a01f681fb20e45ed75787317b0c51830f95f8deaae4b0df03bf315bde762531c98c1ef8c991722c670eba6a1d0da2461e388f3d474fc910cad298554fa881073e0ed2c6dadc21c79cda1d989e94cfbd46e200ab3cd4b003ef3e29d953c69c1cf2191c89a85107b64afb24480c7d31230338a00051830eeca5aa6ae5d8b3253cd46a5cd925778e2987ee1863f40bbe0c330332d4900393238f42dea4afada5a9a1e4eac0374713ff2dd48003af59d4473fea2ce7d019aca1c61b388fe3ce1a1d85c8ebde0dac07ef30e7e946777129f0851a6de0c5a3a3fce51d9115abc1066ea7b433e62056630dbc2717dfee8cb5fe9135d4c09945ce10995fa11e3dc618e7881a69606332c97d9dbb3b9a748c31c618e328326a065b35d0c0c48f8e25f4da3ee30cac7e88bd7ac9a3d8f42ea8819bf18231c6d025d43003fb297bd49c420ef516af51063efd56f6447944851a6460f53ad845f4764eed7e0d5cd02018a3c618f8201d22a5261fa350430c4cc89759b5a35229240f50230cec6fcceff7bf8a937140a10618d88fbcf3320b314be935bec0d8c78aa839c7a8fffda08617f8a4ba1a2775f80e6a7481ef94bec56350cf10cf375ce09275e451c2939fc40c0e6a6c81ff30c5af8a7b61a9d125420d2d30a9742ff525b73c5fcbd0aa828022c10009c81000106a64810fd6517fbab43a16f71635b0c069c51fb1ca183c8ee3388ebbbbbbbb3b33333333b3aaaaaaaa5aa2892400b0a871053693478a0e3dd4da56e0835d1c77b3748fd553052edd72ac29f888754f053e10b1b7d09f3fc79629b0413bc4ebbe5e0a4cac8e8e5cd54481cb953d85ec4e9b3e2f50604a43f5a3142442db2730957153964ccd524d9dc0e7680ab973879dcc2a4d603ff2cf1ef91292e3304ce0f772b664b0f53cd15c02fb410e34a473749ce6daa08612d80c0b9a4c3d87d6a1930446aa4b7cd47390ff03096ce4641b35b142faf647e043d5e7f4c8367ab611388ddc9da364397e48bc08ece59426c7e179f76b86086c66d7e8b1db328457725efe4f8e2204c63cca8a5bbea1e3201b04f6a3209dfa345896b2061038cdfce25d23f9055b51dd3ca3bd87fc515f3099565deabce3cd1ded053f316dec38eaa41a5779c1459b9c681ae92ef8f841477e92bdd57e5d301a443a83483ebb3d73c1c49892c41c232d5706174c4a5172f4f96b4269780bfee31c6a9ab672253d6cc1e6e8bb651fdb65ef6bc1f5c4f2d3b6dd4fd3d182fbccb4c1528e27d5cc825df730e77f60ef139a2cd8ec398adef45b8e33b1e093e4dc269d6a82b9050baecef275f54561bc820ba9fdb3a7ef7b7a2c06c2700553fb21059b4c2149dae40261b482f51cd56ac4bbcfa1a39001832f830664182bf84939f44db6d8a66fd0679041031c3c0cae3a10c62ab894776e7ef5515eccc25005d3de5a3d7152736a3d155c670d71f70fdedf51ab1b3407d20807c24005b7c1ab2e5de83805f7296934297d0f1a5953f0b1b4f67f4e398a1c16462998be209e831c99240fd3803048c159cc1d75889eb653f3a360f42ca65051c283a89919d42006872858e98a49772c4de5a0854179208c50f0ed1d87f03833846e7f1d60823040c127f71cd328a119e35d86160d4cc7e005661832fc302a0ee313bca649b93ffe4aec48f404173fac94ecfd3d1525124627d84f77a99fa4aa200c4ed4182c30616c825f4b56d9dfeb6718335ef09e72b8c18b45b37b4ca91e75666ee2c62e8e4f32073156d6cefc92b5e1862eeab0a24af2b1b27351e4783c2a3569087181b6b6260d95b6f75b5ce3e1b67aba60793bdcb085ea9d4376dd89d6e767246ed422d3bcc16aaf5f35c36b300334dca0c51be287d319791b281f6eccc2c99ea6d2a493de4990b8210b7cd5e278a7e9e46bc5c2f6c8733cf94232f3c838dc8045313a781419ff328ba6cf38f20367b8f18a7a73ced25157106119ac2e6cc5931f957b3a567c63fa973a6a15da7b7f440a375461db86a416f9f5fb3da920e28e87f60415254bf9a3df5e8ae4f40a374e51ceec0f7f5ae27ecc8e31c6186398256e9802e98d9924dcef738a136e94c27c716fe9d41d4a0cd51d6e90828d19c73764c8669083afc02838c9943c268b17bcd21405fb29337290d483acfe915030398a5da219a347b0cf0cafc10d50303956bb140fef4ff069d3c42a2fb7fc3c3dc1597d106e67a7593c9d603b6d22fba8e4c714c209d6773b0e422bd524f49be0ed5366de0a538b91d504fb615acc21e44c26aa1833c20427d2ef6e1aab83681d976037e4d856166ff30b5a82dfbb8f9434588a94d31ab08001d870a3127c58a45c3907cdb93d8a093728c1a794a2b98ba9c7366d4e827155fb9e8e6107c6180325517ad8a94663928e7d32dc88049fb3dc37a56a0efd6321c1e746eff8c3c80c6dfe2398aa31dd5ca75529621b841b8ee0b635a3f63e5b307569d05cb8d1083683f8bb5a7d18d1bf69f5648be05c47d36d525c116c7e2076ed714248d6248289273d96b33388e0fd42a5c3b00f5c73f42138edb5b212b7d74fad217857b5cbce192d049383dcae41244a882a477977eaf2442f438fdc1804eb418d24cf7f51d59d2048d6b19a8444b5ab58d20d692c43eb2837dc08448d71031035c61ff8dfd841feb758aa1c3ff0952beb731c7b644b53126ef4810fd242cccd8af94e573e30f651ac586ee11e58ddd8711c224ac8517858116ee881f7df94c662c8f6f8571ef88d94e3d6c4ec20528a410dcc3843057860525ab7ec2987973ce4b8718701dcb003f9c3acdc9976d68195501539fe9e141ea4fb841b74e0747cb5acb3e87dacdd98039f95cc722c851c776b3930d59ae38a9d961f72240e7c94f9248d4f76030e4c0a4f29879403ab88a80b4a59010cc85824dc780317a246a849bf471ee4c7106eb881f57d49f5af49b9d1062696464c13f59e254d5a0cb8c106462779fea7c9b8db71d6c07adc143be588d6b4bb1a38b3f5e8eaf431737269831b69602ab895e6c9eecd81050ddc98ad7d2c1dc719b8cbbec8962ebd36aa66e0c4b365e8f69dec77fd0acef0c041831b65e0d28598a92ce7981ffd64e03cc799ad2ee580c18d31703976d8c69cd2a8fb1103935dc162e79413067e246f9e1033c64fd5fd0d30303908b97e53fb05dea5473d457f78610f37b447195a3930e30c1acce05ddd0c0c907581cdfb414bfa40731cf4c3c10ace72818f83103b5f908a999adf021f7828527d1f254d28cbd04207dcd082295ebab694e794ed230b8c07967bd37b182cb0c1929a77a8bbfb1ad32e88c19fa1a5811b57783bce761fb46b5cadc9d814dcb0029b266e0cf59f61c60a3270a30a5cbefd70f3b77f90a2a5029b21756a9fcc17ec2c221820011966dc9802ab292f34e25e49ca4fbf0bb60737a4c0ebd947678a68195a15b811053ec68ea635872155c7571937a0c044ba946ddca32367c913181bcd6127959863bb5051e08613b80b394e9b45cfecc16e0253fe1f19e57719f25486d60c5650862127b8c104b653547d373c9f8f65094cf80735294755570f2a434b09dce7b846f7d2eb793609ac9e75cad1615aade00612f82ad1606f951f810b7996ffb992b6236204f643f3a45c3fd260052f78811926cb8d2230aa919a9dc364172b15811b44e026a7fef0d18d509b6308ec7d102af3841821307a1221e64912b21b41e03afb658f2d24867b08831b988113b801043efced4a69ba831c45f70bd62a4a2be565f0cecb179caefda6b0f37a61487972a0316d9e17dcc51c7bda2553e91cdbc51d7b5e8817fa751bbae0b33cfeccf0d0e378aa33cc20e3b0910b3ef3d795c7fda8c9df0bcc38e31c2ed8d1fcb18fe52886506917c4800c09b8190eb0710bdea23fce93a1463d646568d5016cd8825dcb51ec3fcb51a6b4d4c26cd0c26ccc82ffb8d2c4dce194a587872cf854152f77a56dd6dc1d32f807980741d9d74006ef6498b7c1c682cbef8f4eb73cb060530cd19384b41942da365ec147684badd21d071a541baea8311260a3157ce598bb777b2bdfa6b0820939b46d7a8bf9e2a4ac8289fae22143323c2c6d872a38b37c316fce59b7a2853652c175ce1ade416f2457720ab0810a562f072a2189e7d1ec29432b07858a9cf13458c161e3148c4bb4ea9bb496a1a6481ba5384871902c1e7d2cd18e60195ab9021ba3d8638aad915fc4ea19c4600687b1210a36a43443f0d0f1b3bf8ab0110a3663544e31cddb600314dc47371d2b48fa94834954363ec165a998412f5de694e3cb202a4fb09639115183c7a06327788b35b91dbb7ed2f6e004ab9ab94c37ef4966ba09fe2f7656cd8eeba7febb1930a0b200a955810d4d70b7c942c75188508f533632c1e7e8b97a1ee2a2e9c104eb9fa727526f5d4cfd12ace5b01834e58c2558dbe89a737ba7c72f2bc17f14de217a7a94e0d4532f1b93e02be3471f6fe5f85fb32109b64f72ed4d6f871d3791e0934bd48e5d87f67d9060cbaff2e75c39f40f738fe024ad9be57825ef7354575ddd046c38826fcb955ae9be11bcc594c63e88d1f265c908beef4cbaffffe3386f3616c1b8d4880689a12218f7ca3ff9ba1e668f9308ced37f2d3bb6caca1122820ff2c5ff360f2297d85eb071083ef75a75b21cc6af960cc14678641582cd1d67ab6c21435dd408c1c6a49edef923d7241d04bfe57e96114c7d43c7046c088297e09ec5228f54b7051b81e0e399a6310f9dfe342964850d40701352baec515cf1cea9fb848d3ff0be1d897d65e6947f990d3f701ee4f66c53ffd598d71fd8e803fbb1c6de9d1c57af2b6528190e6cf081cf48cb9e38c9b423e30c33ce1ed8fb38aef41c45cbd02abba30636f4c07d6ba8fcd549cbe390330c90800c12d8c8039bfb036bc949d71c6ce081cd9ed37a621cf594931c6cdc81fbf178db7335e4183b76e0238e7ff6689a759bd6814dd11d3c536d2cdfde60830e6c576e4cc94b2b36e6c0e7f8b1a714592abd9a11d89003ef71d462889dcca3c56dc481f1e8be52f628f9593b0907ce4a2377e0915b8a39d40ad87803abed1f73b68d3951b20bb0e106c6f3caa2d62a5e78dc461b58cba943ac4df7d07e7a850d36309d453dc7bdc91ad8ab4f21397e6e430d67230d4cbd5f90c89a6dffe331f02a68189053d840031fbe6bc58f5e3fa6ae6d9c81ed4e11cf315f92d09ad9304319b88ed25b8a1f6a3a17572b61830c4c9e942784a4e4b7a68f81b5ec9aa27a14547a5731301da6ddc93adf8f62d046181895d61825b7254d60030cec6dc81cd946c721f71db0f105364323d57fa867397358b0e1053e2552c5ac1ca4e4215b645436c24617b8fc418e92a23f48da6133308003fe058d010378c00206b822a30661830b8c667bd039e5d44f1d320bd8d80213afcf3de84b41b2c5e4c0052e680f64176c688195e02a127352faf4f50461230bdc84c73936314fd37a06890546a2aed4c7b1a47a71f30a6c0c29e952949856e0e368e211db73bef9252561a30abc64444bfd16950d2ab01b2ca668c7e153e03a54071d1df95385500a5cfe7ffba0ec62c8fb4781d78d1339d01b0a5cce6059a2ae471ef64fe0434a39fecf1c71021f574e565db69c3f6d02d3296610a9f7b78e3713589b6ce695337a75331cb0b104cef3f7abf84797f797bd820d25f0f1c5ca1e5b2784b48656b09184b3810426daa3078f3e0881828d237099f7e26bcee96ab5bd32d8300297d42f6ba5b52b43bc085cfe581d8a74920c3688c0487ece9543ffcb443304ee52feedac087d39bb42e0738ce8e9eba3fe350a838d20f093a137879639a3ef12d80002e755e11d6d5b84e78b18bf60347f78b41ba94af3440c5f70dd1ef8498e693cfabb0cad858118bd60e3486ebecf8fa246f58c8f8119ea023178c166f8d5a729570f520e8741fb408c5db02993c4892998c7dddbfa20862e58f38c3a71824dea58920b3e8bbb69be86ea8d1a3170c175f00eeb3f486ec18975464828ff778f2db8f48d3e0f3a993bcad68249ab1f7b72ca19a4775a1435babb45640962cc82f3fcec1c5db39de6a02c2ed78992d45187c4826d2dcfdcb9217f3d7e068a018b927466ed1ec95fc175876a4892aa324275059382f6e486a8a9b342a606315ac179ec6bc1bce3d00c62b082d59432a5eff8eb3bf6090c62ac82d73a7fc939ba6839b4570c55f0297d048d1fa824af85f2582c0e0644c26030140684958e0f93140800003c248ec422c1288e436d7e14800350241c4a3628181c2014181c1287c5a15030100606c340503010088381a1709018800783b0372a7e5b7e368392c0aaa8e3660d8e539adbbb9f10a02802acdefa6f8578299b62eb01781bd474998152d4df3f450a4ffb4ff6be034b3b4d37ab979915c3ad2c3145443446d0ccdcd49523bf5810a23ee867872aa4172aa1d79110f977345a18eced9937cfe33bac996b7e0d89fe053cbde0f83470d5e2784478337415daa126a706a138e5b2e393ae3d23549aaa21b352b57e61a74b2b125265ce208559188bc6535ec1f71db11340133dadd39949c2578cec84470de49ba58e2ed0e2e10821128717418243bc55d07f00074097ded3d1cad20b56ad6525a130db7444193f86d78c8d4ec90044eb70ad405be3722b492d230740de7b1a8cf07dc575dad355f323a9a1ccad6f104b80c544211653fee537b8be507483a59d3393ae4fc876f3502fcc6164f6ef5a553454d73c46699bc354f50e6edab0b846887b1d78c51f9e51b4d1b575732d0d64430f90ca7032c672a05d0d96910f4da2e0ef8515ad1f9ad0c43964128771639ae1eedf701311a9b8968f9298f56ed6b8a7f1f1ff0f3ba28bcc9e640da1037d981f155c121bfb9366b52b6a2ec2e5f44816b8b38f1413079f2e8b0d7c582e2f67fa3967d71f074af8a1e24450f81e30371f567209a325c572fba46bddd50d433ff9f4b900a926775c2f0d4944ec255eb02c9ad58dfb6641cb020ddb3a6ddbf2cfc8f39655433f7da788530fa5cdbce8b5423878ded950872d03504c9b4f98340f411c9b856f707a09a1bf0ed076701d72e69273c1d8e0506fe8e0c08aa90b65d699e5e5bc6734424c5fba3f91ebd011586cedb7f093cd80982e8b2192068d69c2e1aa013775923a9cf4f08a5d2846a0989fe313e22cb1c2c11c4fd44702df72baef45c3854f48141f43eaec980ac950d68e121583ad189e0fb418d98c054acbfc3032f7250e69e46cabc221db11ccce78ff734234758cc15f68bcb3e612c3a079d3a80855737bd4021daa2a6c21a16d930c06c2dc1847f8df40fb0497bd1c4976d6a343d13aaa160d2ba91cc4d8c1d6060c3d6055cfe3580217d825dcb43164524f5c2fe43816aead19d7058b4112607eeba3377c4b176232c77983742a8d1a2dfc321a8988433c735ad251786fba3c3f25ef53eaad403ee63d677b6ad6e1178e195c5678952d83cbc9624db8ee8835ac2affdd55e79cd5ac45a606468658c4953dc552a34b19f189811efb50463c12378ac189f56867e28690d049f8716d57000cdeea9301cea4c1a798df309f8029d1a3a424564fd719e629876c36fbfd82813296f6e5bf42a696af3c91968b8f826971a0c11d78e329168ceced42863e5a7154c6539e0c51001edb1da1021e4a108d2adea6239cba3eeb4db3f00b1af8ee3c15550d1a3f6787bce5c08a98828842a4e82104adb3dca6f90833263741fb83ceb181bd0664161c17dcd337e25a080f9d65f1c88c0ceeec873f2e72e1ca0fe4eb48011917e01181f5edff8a905b2674a81da48319c7adfdb8325ffcf3041ac6c7f4bcb08285f08e09e3433032ae2f6c5e4bc1a9bfcbd4480d781a1cd19acb538b5ba1f917f141d177464b51248f4ecd6b597916f845804de3bcd4b072068d40eed956485326cd71a5e7ca9afe8bae819d7519be9f75120c1a4425104f0bd505e03a344f3bd885c3a2a2511aaba45a034cbee73c48c7daa06654213f2fbc0abb9cafb546fa22a8cc07acdd651413a30d5086489a9b44795266aa781fef4c0b6bddb64c8bccdd74c4e297b22e3b1b2ecb0971e83b05551e330a464f004ad30e9613006139dcd8d146fbe42f9ac9ab38b29df8074a0be226dd656b825af0ed78bf56b6f2383c459da562a8f322b5a50c32dc37ed0454e9cad014b2a769adda1cfd8049751e37150c73a3213882b0563c21150f72ef74089e4f097921ae6ce35e6ebe40cc27fee5682960c713d5ec5cfc10161ce88acbbab179e7c6c1326250d4bbe733f605d0b808f70573bdf4c1259e58a041192e32345b8232cbab1ac2d0d28a6a6e983b8f981a7c82b326ef748836c169da6e2a62932c979f49c3d244f8c1c691f7e596e14a6fcecc252576445d10203a734377f894ffce91d97b4972ed908f91b5aa47f516f2e772ecaf2e1ca1b933cb502f148a6c93d39150de86a9b27b49a10ebcc1137868dbf8a54ced7529654623d648a119ecb8a92a0cca046629adba254d96d2852f45ce4404db1b4dd2d11a0d44f9d8c7ac8fb42682130fd2353d7831b8cba36821a054830505f2447293d0f711c670f8fe22304248c1fc78c2c5fb7049143039b680390e83280b529f4afb912e8253483d05065c741935fcb41da54bd97fa83450220fda011b8bdae0836f05af750af66d11f270ad522d2b5407a5841df893364c70d20ad4f78a6aa73a7a722a2af02502837b41695918ee8e083b9cbfa5c6763bd048303c2eccee294ba2f9b4e5d323e91a0a7b3e8d6457538f9e1a18a6f8830c7c6d3a718272e1bb6987f3a005cd027008d53c8d98effbfcaf1bbfa76d8a3761803e34174cb56807d91c2539a012ad4649902b63dab4b46d68f4deedcc0642c1ceb9dc3af7918cb78c03527ba4972506904d514baecdcdef5aa8dbc4156dead9ce8f1f20cc3bc94c4c838b6e94d63484c3dd8c8f465423f930561d8d4023df23aa7199859c7b1d38e8b494cb2612204d7aae726ed9d924da802d0d36574808604fd62befdb4db2469d5853b57e2b4311d200126afa1a6d9b740fd950346edad9c908c20cfa3dc8047f50ea9528c501d6445e781a000de3da14b9001c7e1437ba962bfae64078281d24429175374974b984682d981d2b6a166cdcc52544f616e6c101082eab102b1261daa146413a6574a5c92c665b65ce75f66d3ec301f88ac6167c68e60879046213bf39012484d722557540f7a7bb194abb035263545da353ca65f9ef6226fe0d58c67b7a1d08acbd6057b515fce7b7caaf7e5eb91bf69788332c40489d95c7ee01ce579a0c5ed4501b66bf350833e556b81b7cfb4416df9fddb0e6d5ee045e261532caeab3e313f2b4042548f48afa6955d5c40b517ae5c8a55c694bc4ef249390a3706ac26dd66cdff5eccb85c0e3687b6cee9e6c32dedea53e5e8d3bd29f59d41279a8ad50ff01637bac091d3e8e83372a855fdeb3d8d52d3aff60d03aa1bf8651e18f03eb5aee17d57c019b94d75697a776459c1b610d08f0181b4f97b7a5e85f4e006c81f67c2b6bcdc1f5bb9591bfd641dc54036cbc68e6e633d03522cad0e3b4f56bf785767c83c73db2c3085cc694daa52a7a3e25cc3c1e03ffa4daf0c05a45f8fd668835d2ea6cfe7b286f17c1516310a96a792c7f70d947030a4770a490df3fc8d1f7bb717eb35d2a882f75aab61805843eb51312a24198f9b555ce80bf6b5a8dc160c1e45394c513e2226f147dba6db2ce958dc4d193a8be63e3e635a6a6220e9d8e4ef10b4ff0eb6fc6cf38a517556d38a37b8c6569ce3c7a5baaf78bc8c0b0e8b20ccbb3f4067f46b6a2e285924e5edfa3b6eb2a3abfdfde3419966dd76dd1ea1304db6dfcb1f54b4b10b03ee05b55cff61e6d86ac93e7ad66380f3e871aca1f7686f263241e845a30b84b985dafd99a1c87f644af856c8cb8dafac034a02f90135fc966d03fe52d630430c6de3e9971ba6d3679c39d1261dfae24a568740b26713b94a1931128779cf7a3e88b946716004891e0d93366304a20a1de4b17f79743ea802352eda8e1e995dbff230502df6680f9c076c04bb075c2dd4bc0d12d482d84f5b8c5659f03ac41cb5cfa9bcb1561549b46fca9616270e48c926dc944c3f9252250053ee757656af1f02ee10cb48216567cb23277571d97cfd316fc62e44347d1559f5a7aee8ac287d70d61f3e30946a7a68eaba25f3f26a2d68d66130364ddacd42b095ae6922da01b7546058b1a5f43d66b443d6e0c0c157778536d693c97c1a6da50cbb8b947dc39ea904f8512ba707dc68d55e4872bbeef137511fba1e8147d2d29e2b6a836f12da042c52af508532950fcd393d5eaf5da1c7925a441a0908fc3723583881308aa39b6e99513d05c0a961ac6da8a27151ec7220ab47cfcc356c5d49160c902a3bb6b523bcf1513139a87afd30ce027b742ea338e76c8dc13f5569a7559f3383b2809b4ed0c2e185bc8fb15b1a24b47af13ed41f62c89c1d9234e904718c07d0bcd5c59b962a12ba7441adc4e60240b9f36dc3c04c1920562c1d1008be5cd1fa34ce7fd5243838599bc3a034c9d02838b1b223408f2073a124a477e6e4333ed3df649f022696822531c8f67ea66026d5882c2206b3319a3bf5933349137df7204700d96dc669b2dd43029d09c1b32652513a30518145091a28255d1bb5f50c7911c856df9f4500d917cdde5b84a0ac4c90ea3c13609d8ea783bf9548af96bf10c28fc71533a9f74d62303d99f4ca8336c1dd8fccdef52bc6b239c378fd8f0193b4ac485af57455827b5127fa5b67675d7e505d643ddc5b00a8aa1b9ed28ee480a6bb9eae56d743b4ce174a4db7142ac9794965f4789d489325a03490d135cbb76c6855c4be8ac4b6ea59f729f6c1b32260882e25736d4b02f872e0a5c0084387bbd3b499b7e53b95af81d018e6aaf50265d3f5cf68979a12fa0b597172b180c0e2b99c54d11c1de59b0f3f95b1117f7be7b460d5240023e135bace39646f795db5e7a3f18320b1e0848a91273816ad40760851ead9fcaef6701cf41437bb9e9142a1cc5561368cda7282db0757d3015b09ce8375be759b316fa962c5039c566fad402d638e718fe87460812c01ef08c86416a41009bb7d1b12c5a49a2dc0adf7013130ed5896d3ffa209ddafcef146a632b87f57d7781a55c1fc67d37ab82d9af8e5df1305decd8886b19e044e8dcc55649e9e127c4207adb585b2e8ddf50d0a6ac9080a2a855ac7e36c0867473d17a415f92685c2c24659873a299f429562ca598e9ed4bc3ef6fea4eaf59d7bdccc594bea849d521f22579213a2f09f31b0d700da92b86b25298a763eb181dc1d98f12965f194068968a9530ae483a1c792f882cc9a3f5e6a3314061e83b8ea75d78c44f05d6cb97506b5cce8abe9fb7fff7ceda9a9eff831300582e9c81d9d5f6a248e76b885a8153a1612036d02a0dc6d80ff5ab5cad39e13cec8d5d7360c02e4ae55ef227ab9603d6e610af7420ae59109f184ce4a22828fc5546005a59e91f855c6005626d3e4d8543c02089a448b508e7d256ff4ad1b25a20700c04e8c4c9b30a497be776b79501dfcafe73cb2ab8d8e8de81a249f8e62c4c9e082f2e4b4597920a4bedcfed34e2317561b3f94439b96309c9a78a7493b828ebe277868ebde283c12fa44c2c72e9bfce1ce50b5d409b016618364dd49bbf2d3ac6e46dc9b7814e214ac789d16d33626066f084f2d5fbc042a1c33019ab5e438204b621dbb18bb2e0c5a74e3903ebbf1a0e7c42c0f02b564e4a05af16d631a9f54b344e24bdbc60282c925a98d190037206112cc460b07f35f9d02cd2e7e49c7e839394378213d4b47831253924259ebb3b92ba42a5cfe0b2518d54abf4d5145aef4995f3789ed8fb9a82efbcb988536a194b20a0414969afb23eb4ab29d10287a3061b156d3d770602a96da0ed357368c2c0f590acf834e8dc3673326f0f98c3a73046d358afc920efc865fcf6815fcfb6d8f0dd9140e995bdd8bffac904cc80b0d3211079c80a3dcf49facb54975a0958954f14d7b99f60dff75763b1036c8e6809c34c370e250b38da3763f8c88b4e53ece7a3b66bda64cc1694f4368c2f9b1f82ce5d9f0805bb0abbb6d6850e00e80b5c70924cb6c657fbc144f7ce9e805200638a255437f41f8ec660d084d93acaedd208ce845710895e89bc7ec6abd36885071c6429dabd7ca6332ebf18e02e1b5159f439a0d2347d09cbe831a454496fb3460b95cfe4cf02ebf26ac0df2944eb714aef3170a7661d71cf8c5edb07bf16fe50dceff618fef0689247831db6d4e1b3a61c62cb1e00c7570dd1652107252d40b219374f54590831d932dac205ab63907a67e3b9112a889272c6b435904b29fbceff59d1de1313a7920c4ef0fa3d10220b9f4ef03d612d045f8921426769db62221beb83af4eb9a17553fef0351c69af034e9c43ad57f545cfbebddbcdfcef552619fe5ea39ac377b6b7a7ace400938ddf38d24aa09ef07b7f0b5f1eede90032b548abfa7455384daa71f7ccf8fd5acac14fcf5ccbf3dd6b205d402d238a24a1e0f8a4c052bfe34d0516474057a7c0f4acf2073929e3408f2539dfb43b4f25f985f9527504d00870060b3dcf47555cb14e6c9737ed6608f7e0fc390de8de5f95ded7566d93cf29f2a82ddd82525763c4b06a8edf1a9aa850bcadcf4ef0931fc66c897498e9544b7eb0a4d0084b412200aed3b38b35936f15d2e09ecc240397a2c4f15582d7fb19905cf364827de568f219788f6c4112ad5380fb75a3475640f2f84256280c38c940788c99c2a64a30e499bd65afe2eddb87f3de420540b0588cb4f9ea2878a00e32b6249d5106a3cec2728e74a970c7fd35d26b334cbc80c90ae3e88ae173789ede170c2f4dd2b6e65c42b38422ba4780170bfe2240fce119789982fa8015661f904e00b4a6444ecd04ee3bd0f4d458dfcfeeea1ac2ff5b0254ec9ccb709249d15c6121da7fda48288327b274bcecb834c8ec8b4c79e60da1f8eeef813b742448b9d4dd338a3273be98bdbf0554e12113c1030b79c7c9373c50dfd8909ea464207bde15b896493d183b08a3c32480e3f4171f3c401a59097ef5fe1ce86b48ee59518db1e02c531c24049a3cfbf7277e9f0a370ecaf536fb5db7375d4fe3a14e07f9a1e600e9d6b68933ade898d9274ee3556a68930aa12200ad53513f28aef70918d957a5f616039ad6510b3629908fdea438b39549e528ac492c86517530756f204dca6908e8c48670cae77174096d39e807f4bb37a9b525d914bf3a27d89424ab4813481c43cb9319baf57a3f82e43c348da5eda8be7b0b79bb704243aa53001a7ae0f9bf0581be2a0d76de708f74c505f1072fbe1297ea38b621de7682e25b468970067c606c43d39428a363bd4b2d517734aa99059674c15fae44626b54e59ea5ead7aaefe6615c3f890a41f5af08a0023b371bfd2b78bfb8effcf9ad01a89ae599098c818188f8e659661f4f679a99780924be5f1e09df000fd43dd196d7543c51f3a4753dcb5e6010c063b327defd312b1a86691c72274ff48b5b45de49aea08867c4b2e1a0e4260803725cb869732105a5c33a41c04ae0a9d49c5cf08a0e9f3eb99d0d565e8373de16492e2dc5c9bda9f7a7a4f2bb5068016c7da8b98a7f55df32f3acfcd22732b467683a85bc71a8dfd50b6c97bedf9812c511578d4af0973b5b205618d00496865961213bf375abcaca94256b84c94057d99b7d2f7416cf0073d1a25e9dfc87da8fcd6bace4353714626371a93ef521a383b542b65cb0510886ff5240552bbc082c2d39c8cf5aaaf07a0870b1f9a2de1f9b808e827b76a5aed4751c16dde25976cad1d7e21efe1f0d92fd33e624a0e86789adeafe5e012e23e5224d18bad1f05b17304defdb12e14ebf950d9a6e8a9fff309afd9e669f1683830a420128e0e56ffad5bfd63de8d70b1587119644acb49f92ff5ee166d0ddefdf5073a389cec7d27906affd6700d0d3c49111789e204566607a1019e900e5b924490704af91542e00bc413c2981879716106745ca4a4055ea654ac8502ca1bdccc0d8cbc8b35ec6c8a98c844bfde6719189652c3b26d3f3cab0314a2a1379522d3463b493922dd872e153389cae7b8248fd342ff530c857097c0500c476e45444bb096f197a81756d96a289c396fe609cf07e804478271c0143bec10c3d8416ca7a0924535d048d430d3ee4eabd16db2ae811dc10fe95c362a475cf4139b089f83a44265c08500f0510d60f45117c06b688be1af220bc955e2900faa4f680f1138a5632197a198fde2a0604ee701679b752b968332657299004f43f9aaaa4f13158f16d0e6a6ce9d6d9ff2b2c0b638c4f2e7d40edddc1e2c43d92e6ed3635150aa94639c592f889651554477ecd5d27835d504362f91fe3c30cc438ce675b42250a730cb4b01d96cb7614482b56cfbf2a49782568199588917a93d2124eab9af96be01a3ee79e099df0fc5cd12980fa7233a8895a617fd36dd8566ebfe9c0bf5da6897aa855fbb3c2e9352b2dcbba985244608653baef57d763c6bb9bddccaac17652caacc8a783eef8d953dfa0b6cf8232840f994bb4a8dd495d7a236031f484856de63b0478c459bc4c1a24975f61993178ef5608d6f8a88fa381164a77df004bad145ac3b5035d66006d58fe10fc0c5168297c82569e361737bab900295b971dbf257ffba63e6bad79d14fd3ec5997cde17aca4e54a4533bef2ca613fd424b735f0f3ad7ec6a01858f1ea6583552391b2809c670f7d5a7785d64cd92729d326126f1acc30065366e3273f86e7d6bd454b84cb09f8143647f0a42c5a29edf38d7c115ec7b0936e55633d651c92178e96e575b494069319589573274de136764c348d1968f3b91a4876ada08a759de9554f829050ab5fb392fc7e61841b67bd1c115f6b6a2e9427ee31043c8495b45800953f1a06b10ffc5a071eb2261c8e2600e019cf0ebef4d8a001d087985ba2fae844f72ab5d123504288d6e5ff4af2bae460a39c32c676d69c080bddb442a2676f946d42e1b43060c6dcd165d079b5279edee8e0d36821fa4a749443913db2626b7c0dd6feea125a32d3f63e0586748730360f41d00cbe2705740b9a597baeb768410490040b9391a0a38c1d2c0f29f78a8d8a279f9aa2983029062988c6e28ee1c69556a54057d0c40821724e232f471eb68cb0038c7287b993a067790bafe3c2087827a7210dac1d40089623869633757689b1c05b214958dbb236339168af33eff0b7b4c0c39c13ce86a3d37cbb87f5578ace44463e71fa3b918a4d71b6bcc3936b922b168050186633363dd017668c3317f2cf7aa2a94b0641e1665ebd05a50f92a89dcb880d8b641991604ecb17309d37e28d10818a139dbd0adbe46d4ad94a2f446888d8148c62887990b6ca73036caf6a502e5dc35fa3dcec7fea762b28e2fb562d1c2ec069c9b0c263bf086428743342cf56d05029da19fac862564ee0807020f84f07142199c5a550e53049d3cbae44834de7a37380e5235a9c7f2f4cf4575c80613b2c3515f44aefeea2687bba65c05d1e1d07ebad8b1cf279818b190be371bbd6dac364acc6834466e8c726704af915ddd683312adb38bbf916246a0317a63d4dc693487f4768e9247558cb4830945711c99ae9ef90601405f716c7d68a4a2a980d3bfbb87b47b47849f7892f437c600d302c40ae3048e24ca58a8e19f479aeebac1177674ec51a41fbbd574d95f6dd85563699b9470571cc04108ab4aab19f0b940c8d89f4e95d40e31331cb0803b4334dff8aa32c6c79171d7bcc5e530e9fdb585ff77d581bf19fb0ad2e2e96c97c104c09128c8827970949277a3e9dbccd20714e482ef4ee8940cb70496614b1898294ca6cf780665272b526b01070758aba9512d43ec6fb10e23f3eff261690a8fd78db9bda6db6fc1ef445bcadaef1d85e0c0e4e1b5b2bd69386783dfdc9a4e60470f52e7038cf7fcd423679e543be7884b2e6914582591d1d70ce02378a3ab7e84c42af6f37614a20161cd93fc4122c68a886d2577c8aae786a2c2087175a63d712495d6f60293228398f42251222125122084a54d0202bfff8795f2b9192e39395baf9f6a4296b783a4b79032bc69ba21f723d99a0fcaf50492edd6ef60a61bdf24f16e9638b26ae37c5cde38f01a68249e8c1055382e9734b37123f18db1ba263f320af2348643c9a101ca48bf2325c55c7e83182a9d430a78729146db591242f807bfcbb993fbc05c0bbd743423161045fec6e333e0e10e63e93fdbb408865c6a02e695bdd06a17082da395611065b5b09a4a0bfdeeca501f7ef5a0aa91b83889cad104aea3c578d9d9ceebf38530949c5ef7bad02b5f1773552f6843e4acbdb5a01e782957c9a7267938799a8e51f06d993af6cad5f19deb748144713b6a99c4571642ac9a0e0a3f6e5baf2041ad9f86d54fbc21fa9c3e1d895c73c52604b55165f9cc2c66aaa08e8ccdfc4b7541ce6710e06aa87b7abd72ca6ac0bcdf39900cecef687bff19e2d6016e78d9b7abb12718de85a2e6342a8ac788d994d3f22c30844188ac850e95c41f1d74ae96b70439616150c79446fe68034e87bfa3a9939ba92ab822e80b0d08130418214a5c62b69485468b05d6ac71b9294853af9271c1236f9f013348cdb415fc4e44821cb872a7a0ebca6aff479589906fe3d997d7a1088501e190f337043b253509b017ab9014649899b33079303e0337f168166ce13ecb0f47ad0d7380fb13b63187b601aa9870a4326531906f504e2f5b93b7dbb819769988a6eb34913e76fc65dafaaa3082a58b5ee4c0cfc4d69d658a7ae20054bbd09323e1dfc3d6636ae0e92fc252e1bdcca9dce587861297e0234fd605a54e2feaf6e48e031f3a34bb84bf869a84c77da85a445c6262942cd5a76287d339a6b4969615a0e03d3f82fc0b05e5b26e24fcfe1e0668e9a69796d8dcf93418cd5375c5e96a8ab60e4e4fd8630cd3f552e302fabe7c8e5da98f9cbc335afa5ea666218b40ccb15b02a34df07226de58b214fb048256545987cc94c75e55b66329b8615130f933b50bcd91731c3871730935e4fc2d66bdf8018d2b07932b89a2ed0b6156d7a2e3e471eb36843ed08b8ff4eee3e8f5c7e7105a3f487580d84b1fb277fe582315a4259aa11fb55783ac51811aca99a110ebbd1f37d5db03139e1120af568e7e82174d19c9922fbb779bd676baeeaeb1d3dce211df39bb7acca4b2092115e0d37c567ec50bc108823eaaf53cc44143ed99df5e4c9820174f32373cb5382841519c4c30f141405de72ce88ad31a34e8b02acba0e5ec090501b1fa6f28e301a6aaff2a8392ab219dec6d301198ae0d4d76a7aa340bfb103fc4ff1b6d059ce88b3dff32a269c6a2598d8e2debf1bc3dfe59b373465d7fbe987129c81ca270b1aba90aa3cfa7b6e421ffc53a18e85a19ec51b414dbab02da4b853862522626bed30c7013bb2830aa5b6bf8eb4ac5442040026089f34854f6c676475452fa0f5d213f5112625406a489407dcc1b93f35f0f1ee15bf9b6c60843d406e02577f7a2d071ed421258a9d9f12ede94d8eec9f05e010ce2cebda9ef5a09792a14f4b298fe52a8217bf8bcac6b176cfdf17ef4d86504409763f913b31518b341887207b059807499780a81b972b09ba1ed42626f4f31307b285651a50b02800e043d4649526f5bb6ca59710f8e0da88f96af4421d057d27fa37ff171dc52b3d16cb14b8c352a7d2f82d8fca00c491c22322e08cb68a34938f4dc9cf3488ca13da33a417846ad94d77493132d6d0869a42dda6c0b9f9cc4752d1912e24baba8c5c115cad1280a98fb124723a22748e9125cd7083f1a3b1a287a179586d64091869a39f524c31b441e5506dd86512b5b85262fe7378a909dde2a4e803284d2095a918cd79c409fee269531aea5200239b61b54c62a3b867346d22a6326d48bd2fe3b9a69bc262ab8406640e5e8ec11c6fa8a75c9724b6d2cfe5c29d0dbdcada68ca79508468493c4ce590ae45504de4c9da62b9c8c42757a89d18b188a77d450bf292e666fd4ad8f2eb011075838894cedbb4160e06a4bb9174acfb6d1e713973d287ddef3650ac1ccb400d58075f703bc2e5d7f75a2dfa444b12e97088b6cd493af06c070987850bde59178c547ca2c11885079a46cadbaf43e34d80841e6f93cccdbfe61f1590266d3498e9d1f448e95e5cacfb13fda993aa4137b078e74e18ee4ec792b8f6963bc7cee78e88fec54ddd0c95e82033a73f773c2d7f190a5b50acb48202f22ce715774740790ef23e21822297a24c35d45c59a6e11cfadd664341051dcf57b589ea478f6bd484d17cfedf31820789b89156eb3abb3f79aca08158cf9cb89ca545cb26c54f135334ad796593a54d9d3d6822796ca823cdcc159604856813aa18eda39b9c8c61d4642e208081fd3242aaa0ee53dc76f8ce66b0ac67b702770b43a81f056110e95b8774420460670982336188a85ad1139668d0c6aefdfa9c5507f5556acab35bdceec4f3dd447a0945b1e658262a5233aae3c3355093ab2d6c134ac3d934ca61c8559e73e434c394ed3833e52face85ffa36fbdb1f2dcf733b83f57f07c075ced23d002e2f65ff20b1c64be088b525672beb4da76b03fbe21ab8eead0cfea589d8cdd214d5857778a5f26ce711ddbd15c67d3a44a421116552819179666e6fd32d8b9fb39c19a1309042a772936fc319974333ad2e91c92c4ae35468a71298d5b524219494c4cc79fc21006ddb96011d66f13f7faaac23e7c980db2b431a9f1a27b119c23ba343706c9c8ca48d864048bde25ec99821d3f6bd9c914ef51aad4211b47e5d71675487946568aae229e336d3bc5822627b26466d86d0ee2927db7eb82eebc5de40a1a1be1b49544c56f4a3c1f11088cc440ccd4599bacca5b0edf810edf35f2ccd6c124048c45aee43a918726c3b2e55808dd6846ec9617b1c81faa9d2c01f7417195c7d93d75096b43958ced01c0ef3ede5d3baed3751586206c544dce0bb13612e5ffd7799db4374182467a8ef3291df8151daa137987e77f2d9b207e2487ae7e6bcee0ad5da8131dda0b701e8dde0e6b9080fcca9ce1a15da953bac08ede492cc83537acd298953a8ff2e45b1eb25546227ff4aa250caeabc8539c209323376ceb12e8ccad3b498bfb6599a80ca78b001e148ddb55713b84bc132680747e842bf36359d577723a761174c0ba9f6a48988aed63dea85deb266671babf31a7d1c63017b20ab1f1a733aab10b1ecb3f41fcd1b5c1e909dfbb433a739772c047f05ad2b046188a3d8805899b29137d8fcf0731c2551815bf29bdaa158d6d15b050c15cbae07684fa82110c9952f97f387f66d0f4589663a8647628666419a9b14932d1f8ac5189ff1011bdd1ff414f630534a1028e6104420b3a50f0a4d001f3ffffffffffff0cf363c1c7cbb1db6ee2fb55882899922c95759595620428a594a44c29a96d20d6460821b0d71df94e0f033150026c0b320b7f0bc9e1e35488b7ae384a5c71aa2ed78ff225ad68748c7f7926332bdf6445fbea95c40e4afb89fd2a7a4b26f2c47cf22f5413022f3c4083024ef050455b92921f73cd732a69928a434539451793ec9ec49524fb25b9313c4cd19fe6e51d39ba94dc68297a939d3948133a677890a2132d7b9097d43498368ac63f4f4fcc96f4f9e739f010459f7f26cb62f50845a3a59b623a5e09efb31634780d30283a31514c09ebd54f16fd4497ddaee1f43fa6b8a58727fafe9c7c2de692190d2d3680048f4e74c2a36aa5acfd557ae544973f9b3ce25e4a9229e3b1893e8cce1fc34aead3d369a20b171ad2c4b24c51cb44d7964c4c765a26b9429868c6672b85f9be26b944e7b9165f4dd8cf8d134b741d1e735af839c1532ad14972f7c6c5a8bfa95b4af423c753923da732f593e8b44d8cccf2a91b36e92189b6e36514b7202b948a7b4422935429593a9a7f370889de826cb6244a7c66703d1ef1c767505eb94de3884e4a9364d1990da30b1b65d7a3119d1c4a98923327d961a211351b384e190e09e0c083117d4e52ecf4e0a5f74b4b038f45341bc3bfe9939358b19228a21393ae98989c92887675e5e49127694c551951ebe215118dccbc26564de5108d6acb92e4249774ebc8c3107d07352909cd511db25388634ce2a42852820721ba92a48a499a9efca8271e83687c4c8cce2f49383c048169ddd560aa9967e213a8c07a04a2598d667246cc46531120daa0b35c09cd258a7e923fb46bdab21fdacf5135a5f0a224f19b5d7af4a14d428bce312d175394f9d0ce960ea67e262a364e0e2ede3df6d067530b3225a16e1ec41e7a68332c6b5ef37c181e79e8e4080fcf1e4b449424236a36bcd8a2059fe4b871030c1174711e8c1c383a80021b30f0c0430c3cee80353cecd0574acd1d27fa040b7d1dfaea68a14ad2984fd679d0a1cd521e46996a791264ee8bd3051a1278cca12f51367f29512b579ee5d065fe595c0fa6ab44d7230e5dc9e6af96a1e46cd203873628495241e7da4399e6f186367edecea0621025f575436b62d8981178b4a14bcdeecd2d931294c0830dfd575f8e31b9383cd6a01e6a3069c8b7e2fbc9fdd1f9420434780da8954878a0e1cf2b9fe4d8a152ef65789cc13cccd06b92717ee5c3861c9947193a792d79c9ebb90719ba6aebfe1379160c788ca10bddf137c5f3c692b1181a6562286db96a26094d18da24f69fa7e43975bc0686becaa4c61c375fa6b4bed0fca768f1da3dc6ef510d0f2f587974c16a6cc0830b5daee72405f19cc716da0b3d97fd6476dc24ac852ea6e7bc65398ff60a8da8dd053cb2d08c8a39934105956451cdba4026f8828007163aeff3144d2fa749932960e4f8c2e30a8d7cbb9920dd3ba31721c377c720e4f47f333af76496b4b4eef5c95a6179312977681397d1892e9b5454d169ba4b556836ba89ba952862b924a3cd597373d99c330be3a042db49296db993a07398dc9dc714fa37e1e4d19a377e0ad28bc171c0430a8de5886cacdc8da8d930d8058f28b42aeb593ee7756c6d2042a1ef4e29a388af66783ca1eba463f288eb2f3de211b5a382c7188d12bb174f4768e784564c58cfeee221e27cce8347133ac147f4a98e267b541213da2a1545e47b9e704287078f25342a5912a49ebc182be54ae872279343b6346cd893d0888ffa392541a8aa92b43a7820a1312558125fae64f7b88ee071847399144b9239cfa3240c1c36d0c3085dca26651a36a60c1e45e894a0796da2852af9d9c483088d58117f264c7d83a2e0318446dc557474cb06c2438c3e671f21379a89a295d3850de4070f2174e2e2e7d8162a8610a507e306b2e11184b664135febbe5c923481d02651a252660e7afca0f13ffddafefba037a1c92fea95543925f1e801c2a29a242571f314c383079da4c43f7174aa78de6f153c76d0e73c913175a96e8bb9870e1a555238514f70f18ee32cbaf43dade699b35e672050597441eba725315e62d1088db9839652b27a34b0e85bc64ba89e3dab245ddc4039c2f000113a5ed169af78522c39ca2f74b8a2135dde273ac95bd1c91f9b49924950131dace873b55bfecbb544c72afaa0f2968eee191d84d8a18a4645483129496b5319f542472a1a5d517366b7aca2a24dcf9071f33b5d44c7299a519298d7142f3e8717e7b4e830452f23336617b10a0dd203414729facc69b2579538a2f65c6c3122031b3772d86052f4a76ea595c41877970b2ec2483a8ad647ac4b98d6d8d0408728ba9c428bdc34412dee35a286a360a023149daabc71f65f1f512b9b80260a8a4edc392d8d9f95b24c8a04782fe8f8441f3d7f8c166e31982a1951a341dac26f74b1617c71386065dce189def477ced75e7574a251b26f9245dce420ceece0449fc23dfab828a5326b36d1b79a78f72d7d9564d34287261a259aa4aa723651fa4d99e8a2be75994c3a4cb4dec9acd772eaca6a1d97685c338810954f87257ab7b832a77c4bce98eaa844eb23724d9b850660e4a044a7a1e3526a67c72ba9bdd13189d64d73ba25191f1afe11351b79c6830e49742558c93163124609471891e8620c7ab39592a36cd0bbc08b2d20d1570c8da9e2eb5cd6c8a0e3117d89ba9e4a271dbd53d7e1884e09a2b38e46b422774f8eda08f2228cc30586d1c188366ccc6f623ed6e47147d472742ca22bf9615a7e154de87ce6a14311fd680a3ea279d4943a12d1899c0d56e2d3e44b1f116d4a19b7b7ca6396890fd1e7142fc5fee467c28b637418a293ca2de3894cf2434721fa7d57cf7fbd26a849da4307215ad9dcda39f274cb890ea2cf8f6ddab1748cdd8c20fa11d791259f2f36c83a02d1c96e9d1d641e01a2ad8e62f98396db7e41b56e1d7ee863f6605236ef30afaaa30f8d34d94c72bf468db22074f0a1cf5dca4a5fb88ca855eea193b4a44cd144e79ab8e5c0e1c507364083023428400113802874e8e1ec13d394f4c47e72440d470e2e3af2801d78b88e3bf4154449a967c451bac30ecd55129412a5256b30dd881a1860bc8d2f4e17ca961d75c00e3a349734f4292d2b9b1f46ff8508e6d08652727d727e6eeec58d2f703c187a810e3934ba2ab6484bd12ea664448d47d011874689c9772b07fd39530b87368b9698ee9794cc71c71bba0a3afa06a995ef220c1c491838ca073adcd06bef6eb9776d683fd3a46462348da8ddc8c1863efb644949b8b96bae8ca83d186534b4d8008d2e9009cc6da0630d6daaf294459e92e5e48a1a1a15b149105b722ee93169e82b74640ead513ee6110d6da7768952b27686d635346cf20bf3fa9aa18d25a81ca23f6e0e298aa0a30c9d101a4a46d3ca2b297a460719fa929929c518e2885a7a306ce4b83134bae5ef7339e3f957cae81043d731285132c90cf2dcd2858e30a8f12499b7f47d30b4a5f955a949fe85ce4d66d09f9e1e26222f7499a992a42ee26418255de89390d6a3ff2d51e8e0427b49d23e72d72ab676980a1d5be8674ed69ecd7517f768a18dfd1c2d93258931aa53e8c842a3e2e68ec94344088fc142a3bfbfe3e71872415bae601d67f4d9cb941c4376c9316a46e9b0429f4cc65a864f49f42d14424719ed6893c49fef862cf1ea404715dad15229c9174ab4a04125a3d78b7f11bf202a74496e53c25f2d9720ee143abd99fd9468540aadc97773ef9c4a3f260a9d18b447bfc8d22782421f3ec6968d6d312b884fe8543c8ece616745ac6342c7188d0e4ab4f2e8c8096d0acda4d33247dddd9ad06b4e82a51cee2255e50e26f49f63788cbf9425742ca137934c7c7658097d10939be3726ce53993d0cc09ff9311e741e63b90d0ce9eecb9d283eb5736c7e33824741ca1133f9b8476152d2abd0e23f42756e1a79e17a1b54e9d530a7a2b25b941a1830889a6f034d3103a252d798bc82c792e2f465f69659e354d6ad4601c4ee81042df67325cb849e24518d811843e6c472f939d748fd27e66a003089ae68e5929a724173a7ee0858c2509baff5964860e1f3c5726997096d42c6727868e1e9892ef85d26ca18307e8fecc1cb5d584edc2d0b1834d44f95b084da2092aa8860e1d5c927e49bdde494c8ca959acdd41e9e934b93424401656a656b97c8d8985ae41ae09e559799f521d65457362c8b23e219bf9aa55a42a0a482a1abd5c9e61c387decba3a21f4d4ae7ac64e9a534a7684f89793fa1e4cd99d114cd09a3712e9e20964fcc162f28340029456b4afea0a2244f626225456f3d4a84a7f828dad112cb57c33f596844d1c94d65e1d3c47cfcf17184a2eb4a7252bc8310f90a28da13cd9324439b3ed1981093f132c913ed9fbc965d9212436bbe135d1ecff0d91373a9ca39d175e8a4f7b326a1f1b489dedd92cc0e2a48136d4aee95c1b308b10e65a22bfd9a2f7fb43e9de460a2ddb76062ac835ce57c89ae5409baa2a728e24916c4126de5283a6a4969253a957392a0e2c40b723194e8c40c9f64ff3e51959f446ba1a34512adb799f6bf0af19f5222d15cf00c1e673d48b4df1a1ac3735c3c8fe8a4558f9279b464b58e6834261d62a93b758ab14658153436ca5b944b995d637a78ed94699d936c294634a36562aa08f5afac16d1695653294d64d6d2bf22fadce12465d595b1825022daf5ec71a35a0e22d0efcf497037e143344287131dab32cc255943f4a1e5377b5b7af84b0bd1fe2555153e2ab2354888e632975052104a06d1c8202afc55c9f433b120fa8e9526bc7d93f43e105d2999ba52549cf79801a20dbf1676a4870b65f93f7472ca96a0e2224b85d70f5d14d792241dd511351b340023471f7a4dd5dbea1504e143b35e5ac34f1234a2662347185f300e903df496fb42365af4d05cc59c5a3ae979683bc92b9d7d3b683ef70920786894b0f032b72063bb2403e40ead26490ccf97a3d173cc0e5d25d1619464d6a1cdbfda2a2aa7d57a3ab4e59da4961c5b4ce69c437ff154f82f5193496ee5d0752c49d41283360e6dcbbaa99854289dd21110383472924a2d39a6e46df21dc81b0c881bda12455b66753ed37440dad075f651ba73d20c4b4f0a206ce8f20825e61b2a3a9c6c0d7d9c855af56908206ae84bf82c7282eeb7b0be0c9034580da3021034809ca1b3348d7947c63e4bcdd04942559c2fa1a3ec094d005286c66298b78b2ae91e945bbce0200b40c8d0a61cc5749f9ab605253786ce7a4b0e9bb192ccd11c51ab13018818baf899041da49ccfa516240c5df8519a2f28693a4511040c05e40b6d869025c93ee293c937a2e65e6cf102dc0088179a0b6261fec4e4781c784702902e7426635e0b11ad26e86c0640b8d097145a52c94a3a597f205bb88392657e52c5a4a185058cd142bf49de189e6ed1008c1cbc02902c344aa87789673145b73e0c18d878b4450cc000c142631df76413747d2edc8b335201d302c8151a5941cc456bd1373929cfe82f49414c695a884a31bc0288196d8aedcdf224548919ef0c102b744226133589b0be94513340ca6874e65f93534e662a3dc98b2daad0294ff952c69caa4915848cb6b77ac4c9a525e3e7c500a142335a92b57cfc5c82a6d08949346c6b924ba111d9da1d554ca2d09a981d279e2458a7f440a1dd784286cbe4d1045d9fd0c6d7911fcf534e9dcf31daf4984a4f75e384cee289f13cf2b284199bd0afac888bce24766f8c09fd7b75ce1aae4d7c76e501c8123a499216379378f9dd644094d08eb99b143ee47427d50c1e8cff1c6c049024f441c73c84181d9baf81d841054b263ba223f4be79dcf2f3aee630ff00c40807176162490c174811fa8af9fb9db2648f0ed90084087da910d1417aae9f94e521342e2643be9f3461f20411a3139a4dbbe73b1021b469e209b265524082d0677459ad985227d59b2f4080d078decaa09745e88cce0fbad89a52985259a693d48882f8a02b399ea02907417a60354e00c2834e2893234ed041901db415260932378ca90c23880edaa056e5c14d5031ac8225f89845bf6162e9d11fd4c27459b495edf9a6ea25ac24b588c5b9657c3b9e4a3d72c1072cba4afa290795c41093a657746292a4c2cb7a989f912bba0a73625a4c92b4a295cb51f6041384ace461454afe549e5574baa6e384aaeaea12251faae8b7848999c306293acb5434964a3f47d912319d828ace9345973941778a4e3c539a260669c2c7ebc314cd95f0f8399812374ebe147dd2fe23297a37afa4f28b7cee98338a3eb86693379f0aa3492c8ac655a38ee70a8d216628f00314adc7d0f94f4c4a313e3ed1964922663f5e7695c713bdcfe554613a37059dec441b93ea4791eb161332d5830f4e741eb6e2280fd3a5a5db44fb9bfb844d37b1c99434d1bb8c2579dec5a4dc4d26fad1e549664f98e8040d7a52e44a9e927489ae4a5ff3c414f4f8ea96e8447453a79833b8fea812bd8993ac936928d15692a4ac8f4e77071f93e8c4a43fe6959ca04ccc1749344a7bdcfcb1e413d54f22d1560c13438930d13d2d24fad19bcd63e7f021a32a8fe844b383d09c23dafd3c5abb63a5117d6bfc8ed818c41b7c30a29d1d99c42fe1543e631f8be84783289d1573c9fbf9a18846a95fb854a2a79224d944342755c77e50aa41468c88469729a14d38a1d4337a0835450b1aeae11ba2ddcbd3ff54425caf5888b6b5728bb7589f94a21a351075f04188bea4245f5eeacd26840cf163109dc798b72bc90cb2922a88e6f4b25f9ed964f011884e0cbda4cf3da88a4a32a286f7800f4034d77bf22649ca233af2f187763f9824f75c5586f3fdd02695262799cdd25a611f3a9d849a4a92e09df1c1874ee8e5c5509d726bf8d84397e4ecee5973c42ac9f5d0753e0fbd1e457a8a9d8f3cf4a64c8f503a1eda783267e8c8d83c49ca1d3ad17c4eac8e129f42b4431fc424f18491a5546be3b9480e7af15187e682f0544929a99248d33fe8f0091e3d63fe9ca7e0630e5daa70c2648e693a5352173ee4d08fbccee817a744b5fa11875e47cc2429a778768e0d38b4155f2969b638b6e0c2c6e7d802ed0d7d4982548b61e639abbc67f87043e72763784d82dec1b8a16de8c4b9ced319dd19cc1c5163439bca622e69b5236a38c0b8f11f6be8e272897272dc8b1b5b24aaa14bc165459ee8172ce4236a8869e883923557ce9abcd5e4e28071833fd0d0688bf30de1d11b317830f2e30c9d92539e5bca49fc9c6c44cd1c0d2d2ca0850f33f4a9425c8887f498f3fc87c105f202036568d7938ca23d3787ca213274c27308253fe531b49e67bada5475534cc5d046b79cc2b53b09269861e874deb0267e3160e82f0799528be53ebed026517e296ba29b9c222ff4ae1fb484cdebad99b9327c74a12d1f2d252fe5123dcc5ce894a8a2e424092f154d088bf0b185f62491f1b94989b29ed4c261b406d9924fb2bc66a18f1363522d5996498bf881854ec9c1e74c1099a32dbb42a35292e1173a676f8f3ecee8d42a8c1c8bd1d4ccfa30a393fb9292e273f261853eb6c9a274fa69442d8c1b05bbf828a3377d9e24bf2c3b4ae32e4a073eaad009cfb9f4cb523849eced838cdebf455e8e12b939f7e26de058f7a29d8b306e8c38e2830a5d4c794ddc249814407c4ca16fefca194c506981175be4b84102c40f29f45962fe3515df4c62670c1f51e8af4cf70972949c7c4250e84d471dbf9265530ce227747193ae86feab80e1638cbec32bdb7212447f6cc5f0e184fc42c931dbb2633ba27657838f2698625b4a5a95629b1f4c68e7ac62b0304d32c667446d101f4be8f4c7e435cf520f1f4ae8722649c94108a131e9c58cf19184de43533ce75b6b8a93166c81e30d1f48e842c482ac94c2f6c5af458d1a1f47e837557fd411990f2334dff93f9418faa424c4647c544974bf8756c889d0484d6e490ea321f465490c716269eeaaf910a30b954f9a79d2635e3a21b4293263922f5b4a266a3e82d027a571e3534e46d40c57f001843e56f582a9a5244e92093e7ed0e60de6af9a171f74a9e414452525562cc87071e3c61736727c0b3e7ad0a72083fccef379d4b21f3ce833c7ae4eba9392f0b18356cd72902e32e70f1f3ae8b77fc3259d39579e860605683806b8b871a3468d597482e62e31fc9229152617060f5974260927e6b2c4e478ca8382472c1a75cf5164ca2529bf81c38b1b14a82bc303167da9f8ea26479fce1bf48a0e4912a3d524a8184344969518cc33d01042ef2146680e1dcdc30c42eb5926867a08195bbe116d5b020d20f4255a85c99c64c340e307583e1d973dc9711968f8a0d7a445496d312755a6079d9f6a4afedfa2949405010d1ef47b264a3f6e8c6c1234061a3b68e3c3a4f6d861ddaaa40328b0c1050d1df4399f9cf262ca9d679945e7a384c9794159744a867393ad319524feb6412416cd9ff61853624583c3c582400416f6afe8b2998955e5dccfeabba2cb3ed31da3855ad158c7145359473f5ccc8a32fce75d453f5e5e7252a5e9da5145bb3aa742778c9983482afab7ec16525b4445fb7332fee47f0a638a562c6a4ab9f272b6eadf20528a46851ed541af34aa0929fa0ebf31e9c88ca28f1f4d345d5ad1bd342e0e8a88a2577d4f9252923c14cd9e14bf1e11e7794940d17f4949fc92db6a92bc9fe883f40a5a7356e50c229ee8bdc49379b4096fd5398a9de847a6c5c939262f8870a22fcfc1fc7cf54d34deb99458269a96203f4d7471e1a3552e693974cc05914c34ab7e5a83ec94fd4a4e0434b4d8008d369805114c885ca2d112c683e8945ba257ed8fd1a27c2295e862b7888e4cca94aa54930825dadf58d9938cef243a39b8ac550eb22f06a9243a9d9abde19d740e3237069148f4fd5f2589215320d1a7b86f2abaa9b0577a4423279a142eb89520a7f217441cd1f86f8c9f6314688499e5f25793f02be1b8f181911c5b94600c1146343a4f12839594b466b0c020b2087ca4f5596a75cae2e7c0e1c516783b1051c49992909f17f5e5000d0ad0a040c1349144b49fcd4d8f5b8a884efc70d2452dcae8123b44ff2a6a42696fd10c1143b4ab23db256c5feffc85e82adea3c50a1d5df9b1820821dabc5e417ff0ccd2cc1944eb299a74d398c2ec8882e852d21e6547a6491d55205acb4f6295e8d513acb2148800a291eddb96e2490844fed0cce73831c79f9820e2874e92f9a49254764de9db1244fad08812ffa45e388fbd213eb41d450653c9f426eb581744f6d065d15672b45a44f4d06735b790333a9b8fd0881a92219287d6e4be98143e5f886124657ec0d0e24c0a227868736e5052ccd5c1f1c51639c2207287563e84c5142c45c40e9dde30f12dc91e5639c482481dda6cf95b35c9e1acca17a14363717536891a2e98c944e6d0258d9b4754127c4c2a816107113974a9c433b5e78467f14a8e3a3788c4a12d4989b7e97e9ede92082270b0aa2fac34991c2188bca11332c50aaa4c4ca1e48a1bda9c525072b64bca797e9136344ab9c8df59d34a840d57c68bba5890f714cf7820b2864e18d18d26c51c6a9db9000e1abc068620a286464e7bc37c09be255a9134f425f698b4ca150ad0c040182322b0408a2182864ef2ec39ab323d3c2339b690c1191a119654fe0a273f97de861b5e41c40c6dc9faf265c24a4e164a6cd4e510018edf22c7165fe098408e19748d1a3966f0501029c38de40506284081068890610c2262608048186c6cf185043ee0c58d2d3e44c0b0c5171278e4c58d0e2440e40b0510f1828d2dbe90c001230c091840a40b0f22c2051b1e225bb0b1c517122881074044b4f02d68c1d1a2012259c081051c1f22573880c81936102062c60044ac6000913208205205018890d12541e9a07fb4a6072254e832432f9e4ccf31e7e4380f06ca3183f681c814fa9435caa5ae9c238848a1ff1295d7fb6114dad3ec9d929656509e1481421f2ba4faa8694fe4097d25419488f94ea6637a2263b43e1a221e643c294f4ac409ed497e217e614974ca8934a130a1882ca1f9cf4c4166970e2e1f1125b41bf39ee5ae8966728048123add29bf58f21711dea7f31b30b8394490d09ad0d59139968ed08972829632f1b22a4146e8541273789333ad3c39456846acf36651e2091f12a131dffcfd299426492a87d09634a1836f381d272a2262a4a3eea9282b958810b6c6ca402408270204911f34fa24a14b7f88f9aaee832e731267339f244910e9417b7951546bf8cc9a771ef4f1e7e4ee89b1cdb5dd41a7152a7f29132b68f8ee4474d06fcc9f5435ac24d16dc82c3a29e9294dd3b34264d15f05d511d17b2c1a5d694aa624273193feb0684dbe14a2b15f41dbe4155d8a41941493c6eaa027ae38db83924b8c65ae155d50b29a6c913935851056344a4ce91a624a2ecb9d85aca2cf7b4946099f3457bc901321aae82cc9cb73629fa426be4d84a4a2cbb578a6164ab35370442ddd58eb02cdc004d80114d80000244250e10831291d51b391e371d8ed293a4d3209a255d1a3867344adee4cd185922abe26774a61558b10d9a599388490a2959f739394c5cb194fa3e8a4aac70e11ba28da8cb9f9bc94a0503442a610fd215f50f4792be7cac57b44c34fb49b3756fc688e1b423cd1ee280b426c6cae923a9d68e3f6494a7cd37d0bbc3821204308275ad124c7f8cb55398bbe893e5aea8a1dfc4b6ef0d610a289ae4cd0372413ed0919a724f19e31a10fc1443b4a860f99a57955cb904b34a741459f6f50c25c7586104b74e2c9bd5bc32bb7fb2195784574f73fc72c84127d98f135cd73dd0f79127d895aa23be5d013af378448a2f9d8cefb2b1ab2d547a2cbf9213c7acc861048f4bee69ad43a8917421ed1660bbe59a39eaaf87c0b218ee8e4b0f3dd31880b218de84576094d72fc669fc788462993a6f1645229cc08842ca22de51ecd628a5f522a4411cdbffa6c5099b19ceb174212d1b6650ba3722c1937280d118288e65c3f7c49a26593e8107d450da1b22d28a54f43f47e294c887a5cbb9814a28dd5e2214e1226f77708d19c79cea96e31be85a8468d1a35e4103288aea44972085dd5cba011449be49bc7301fcb2ca9184202d16555ce5f4bfd93a207884ed6b8b9cc6384a8a03ff4a34a6e18e287ce3a6710f2c48f1e42fad0e9292fdd79acb49b9e8b83e3c6175508e143bbd59d576290d94323f4a74a29e7247ae8ad4ae736714cf2d0774ca9abdd7d3942081e7a0b3adb535ab6b23eef0a2177e8949cd9d17df2465d133b343b42f5e6d268481dfa787d39e82d9d0e7dc79474d22ca339f4a259a2a7a5c5364f0991436bfa635945a49a9cc32808894373924ee1458e3061650f814333b2b54de739d1947c0979436ffe26446ba942dcd05912fe9977651784b4a193429fa8a9f134a276c33706216c48eeb829c5cbac4ac81a9a912b72953763e9dc4f1d0d2d2c70002842881ada14da9238f5135cf34a436b959ab584af1067a62284a0a10fd57d49b6b0ec29e5cd8c903374a282688cc94a66684fffab4b5d4b5b8bac105286367f34757bf9b66c0e199a4b3ae94d82321943676143e9782ae69854434688183a133ec920740efa4048183a29c99d63948f7f82102f1342c0d0495245c69379857ca16d1d558b7baebdc91a51bb198478a1cd1f55b9728b9491814148171a1983c5a0e3ee9f786e8e6f0117da6ca9939ff20f1521763fd645c8167a6bad8abf164e670d7281bcf862be08d1421f4bdc0e3dd945c46934b4b04017373850a38608b0142159e8844c8265c7de4e41a74a08c1425f7d2573eaf58cc6fe0a7debe9b9c876cfe865d375cd4ca9a7960b0e31a30faf986485de4d36d3b322f38295cae83f787ed42b95a43cbf0abd56868bf2f029967732da510db2aa43c64cee54e8f3c40ea20431a2933f85e6521254501349a13f31c971347b124b72a3d08af63c172e080a6d7e8e8ed379748c167a423baf16d563768cded2ff3acfbc096e3aa1df3171b9f583fc6e9ad0891e9494a78209135a7d51929ce269095d8c8a239fe28fcfe628a10db9dc92b4e52c3b27a179f7d4dfe12bcdc24542eb561e7ec3ac47683f29c1244189aa11da60263b4719313a4c2d429f04d3184af8dfee7c12a1775fcbae1d7e2d647208cdc5943349933787d888d1c74c5925d6a7ab624608fd493987f038a953cc4c10da92bb72cae80508ed86c9f120ad645c4cf9419749cc151fb4a33149f13ffe727e7bd0c71293d049bc985be6416bb2c757ad4c9ec4dc417f4ae4ad744ed29a61880e7a2db93c8ee885e9d22c1acb2ff1972668de912cfab378c2f2799fa0af8b455f9e3525293b335b82459bfcf2cc3794eecaf68aae72d025fa5b92c433b9a28f673ac64b35d1ddb6a29f8f1eae5de8bd82aa3ca77410d7e1429f9e644f6e1ccb116fa153e2e74d93da3fd4d542a7992785e9491ab5cd42dfdd1e232ec6faa358e894a678498fa76c62f40abdfcc9a0f2c84e7e9633bad4a434c8b8161dd18c4ee7e4b12b6a857e4f30294cc5c57f57466b2977eaf3cf415fab42bf41e538199d56678e39c5a9d026c1620c32ee28e5f914fa4cb2c9f9662d964752e8a4249796b9478a9644a17993c398a472a0d0c73ca9c4247fbf49394f68b7434bd8ea3451dd317af7531ed38290f9d5095d580f6b2536a1379d4d2f912e6662421f347b4ac14b52a26896d0271dbb34f6f24a68b4b6ecc714fc24f4a1b2fcd3e40a21b44742279c5842cae6943f4b476863e5942a3fee780c324297493cddac996735a808ad88cd732d9fb46f88085d5fe60fefd21da1b38021349e4d3b778ce133092f408c3e34aea74aa1a74549de610142e89450be417b38086dc8139bf2e7706d1508ad896d51a234eac9f983d6d37ab7947042631ff4fbe1a76292a4ad353de8376ccefb67a682090f9ad3d952c9b7eca0534d8257ccb1f2ccf90274d06b926557c02c5ab520b6bb3fe5a3e60a90459fc5e5425914efa80262d15c5f09b2c41613b47205c0a231e916374952cc19f44801afe8672e94c9a932860ad31914e08a3e4d8e7e39a5a44ba55c4905b4a2b3307256b45dc2b5843949d5b48a54b4892a1a15b52fe56ad14f722afa331ffd37f1991da3a2cb983f4f96ec29ba1e13d5c2ea96ca5953b42d72435a92a252f4e69ee409d19ec43c1129fa1fcbf193ec23474e348a4e0cfa56495f26f9bdc43d284014fd8a1213c3c2a518cb50f4999396a689f961048af3f39fb0fb9a4f349b829c12224f74622729bc5f578e0b5127fad81f45c9a34cf2f510271296addb44af9ad2941679a2938634d1864fd12a29738b194399e8d5e35b0c9ae413368530d17f3e3d5da2648ea54bb4f33ab34998f6584996687393494d3ff1a4972ad12625c36490254faf44897eadf7b4849e3ef19f44a3f2a6443f13f3775e12ad26b9ec31dd631c3f12a700487461b1e27a831ed16a0e2346e5fcb962c811bd895f3963e71cca4a1ad19fb09b5b42cb88664dc972f2675209ed221a1125a724279fa90952548022ba98538f4ecb54e2a889e87489a133cf72644a22a2175dae169f4baf6c0ed1c8d112f54f1eef7c490cd1694b515c4736098f4d01856865f62b73e55813bb84106d2c99b1262337c5960ca2d31fa76d5de2adad44106d92c47b7613cf93ac9240b4c9ac933ecb8d9b55028836fc778a71f592b0f81fdaf13c173406f9a151a164a8d29774dc247d68f48caa08cb263ee6f0a1ad9872890f1f4f92b487f63c35a89a9c4c88f4d09b8e53e9a17b7ee6a15739492cbf180f9dffebca5e7b8736ee75a69c6487f6c49cb9bb3545d16a1d1add723a68d179f743874e66c8b06c829705e7d026ffb27025979f54caa10f4ae5dd0bf72053260e9d205ac793ae9282bcc0a115eb395941e834cb9d37744aa49af896dcd049d3e559e5d4863e2c636bc59e0d6dccf134d7c486f3f81a3af1e4901afa303105dd2e4943f326bac5acb12cae040ded289ddad99fa113c74498e55b9273ccd028b12b640ac293249f32f4c1ca24fd8f27d4c264e8754ccf4548c7d06affc7119e189a4d82c73e31260c9d6ecd6ea26486150d83a1abf8972f7449cc29426974f7307aa11f59eda7fb2f97b875a13f95c37f25b933c81473a18f9a4d65af4a29a62df4a32a5ceacc5a68b7afba4d6cb3d08552f91fa239163a9da62b537f1413d5bd42a3e926f794a87a3add339a750f9a336fcc684cf620433e970a2163854effa66cd61a746ccd96d1a90c72bd29b35c53b60a7d38372522ab264eb864742ac5f211a215a2dd52a19165622aabfe673e4da1d3ec592eab5676c94ba1994de24984c80fa247a1cfcf9b356f97968ba0d058cc7934c87b56113da1ef9354b789ec50fd8ed1aa6879a92464a9983aa18bbabb6a427be3cf2634fe3b97ad2613fa4c37d35f39df622ea1bdf29cd1c398ccb2514223830cb2f3750a699924f429ab93984a4442273ba9e63349124e353d4217835730d360962da6466864922aa64c4945684b87bef24a194ff449843ef352c7d21f425b42740ebdc15cc48918bd75329317235bc442e837a5783ac5937b150d42ab3d9b43e6dc5e420984564788cc97ce399e7ed09cef6578ca9cd0d107cda8e91ffdb0e0b9eb412bb27e62522a6bc6f0a04b25832ebdf3d9a3e50e7aebe819b753c59c490ad041a3e449118f7f165d92171b4ecc4eaa9745d739224cc409e115148bc6fc7d3bf72541438645a3278926ce29eda4dd577422ab5274ecd117e43ba266a3ca6868b1011a872348020d57f4eeaff1f262f2724d2b3a357d4246cf537d29ace863d672c68bf8909555580da4a18a46e6c25452522e4d4a457b9a848b1d25272142459b4fd0293fde293a4198247f53c8f8ec4cd19e75cee5ee3c974bd1c88b25ebff491321165234d264ff7892a3e84b26f953cad933c999281acfffe14c190aabd1050d5074e656c104ef139da5ff991253fc4e95f144bba193fc13349d68f762c48976b4e86ef2249b68d6b3cb458592734d34d165c668927332d129dd3116ccf2b506136d924faac5932ddfe34bb4b1257c282586ffbc251a9d7193bb62ee936f25baeedf3c49126745a744bfeedfb12be6205e33894e994951e3e2e452424982ab565c5089445f959488d0f0150f241aa5d55acb643c87b13ca27d3579f7cc56b2f23138cf45b9326838a2f72032f75a68cb229646742642c51815f117bac288d604a19412ed93dfaab2887e4764d04b327f2c254534ba74d27ac14f32e5398d44f426f9ff8f85d0317244b4497643ace410ada96cc68b31490bad213a8bc947e72ddf70b285e834e813b1f9cb3b5742749278b953eb6f3406d18593a953d85210adebae67509e3af440342a88d02c1f2db86c00d12699329bcc9b529573fed00915c3c6963c41957ee854e69225584a1fbaa4524ed5257ce8f79238f9c3a7c70bf7d0c7e855e7b81eda91b1b393874664ee4b69727868e46249df570fbd31bcc3aadda9fd5aa11d7a5161c6a3c6ca494a75e82b73896e3dbfdf263a349b82d05319333af773e84c8eaa7f49b67eece5d0e9e87859ba2d2fc82f0e5d8edef89444eb3f250787de4b92e1c9476e12a77d4327c224c14c36ae37c9d170432707d3240997a6a4181f8d36f4b9e43839570ea6a69369a0c186be54ee85d031bfa5b986764d87cf144d7f37a9864e708d39cfc7ef1463d2d0c54e3121fe6de284686846cf2ffe3f439ffe4997a43b08a5638636f9e666ea12e76f19ba7057d3253c6f7f47867653e5accadd185aef203f26214f148f89a19372f2c94cb130f449d8a0e5cc357c5b07864e935242769abed0c5544b96bbf74227e51f994b0e324ba6d585f684ce73315c4ebe880bbd5ccef3de4c4afc2d343a76c6ef33d1421bbb93263f9d2c347f4a8f4e3163a14f935a623011a2e5bd42efbb9d2dba67f465c2e7dd93191fae19fdbb88578c664bd30a8d36152ff9a5657471af476436abd0f7c87219999a5a1a32baa4f753902f0d2ab47f26322694a6d0ee69ccab3b9e458452e8fabc2bba49d13bd088421faae44aac5cf24484422b5ae4e6cbf84d3a674fe863c79262f54b4f7736462fa3c2b5c5ce9cd0f89ecc5c4ad2b88e59137ad390d5127a96242526746e4185b55fca654a4be8dcc49c314f5892544909ed898d1bc65244b99f844edc6c6a6ad262f81d0985c6113ab1b2478c3ccd396b84be72e3626cf2922c5e84ae4d58521d6345cb88089d24a7afa7e121742ab3e50f8d8ba844b0a4226130128ac4c180388841f8cc7b00b31308001838240d8582a178449235e9031480035528203a302a1c222018161418100e83a15030100a07c2c0701810060443e180303422b1b33d290560029d4034e01950060e02f2803fa721f8567e981a9e0a2e5cac220f451a1087fa547c7dff723a14334008a803a6ccb45ac05ba825ac0c6710fc99858cd7081c1d7f045c79906f9635f993ebd5ccf68113e74b06300383002ed0dda17ec943821e5827420046369641d510106485f2cc363605fc423bc0f9600530d8b80ece29fed9e0020d30dc9aed28e32ce420cc4e4fa783c4d8c4780b38f213b6cdda9135601d2cf882f5804c4e84fb70e59283a33f38aa65a020d3cea0edbc802fc6df12fff788b5db4165436e38d060c521345fb904cb06e4a0e4a53389dc617fc8efec1936678a44768c2116cd3074181d92aae4f1e721e5c0527109cf6512d91bfcd10bde156ee0aef959fa51bec25b9bf65f1e0c039f9cfbce270731f0080c031c86fffb405840a40006494833b10dc20b4332e33236f4171e0f103b6d2a281d082ae096e580a28b520d8f84dbe2f70807b64879284b600468e7b054974810bf10554aebb94e02640fc803e8803a401f70817d7526641420cc342c02fb8837e2878acb442e4ecd1c5c5f43445a74c1a8ba9fa2a678c800038a8b23f9b05b4004e0d45f6acd659c10e1c277d6f907ac8a6f036ce343031c2e8e43e6c902bc9a761856b030c49a0cfd2c4cc1292bc6570bc7e3df90a6c145e80c0b08035c84a4a23f58ac14fbe8e4c4c001f7d398f9be9ae403f1d682dbf00bbcc5ff5e5cce808c0a0100d70532465f1983ff831048653d314f81fb26e3f364301d9a40219c6be7cf01f8c0024e03a3a09bc026101a6006540653ecd3ee418fe1a6b0269c4df813237daba940f27e9aff8a32b8883e0332cf7f5ed7007f9b0e2fc101635e580eb61a110b1b0338019cc77ff925c8e37f19813363d670ca709de7b0de9113047966c32ef982c3ac26f5537cc0a838c1cc96e31797351d0bc2a78eb0701956ea83ac0f0717e574425a28e29abcbba595cbaad95d08133c2e7389d926618562409b00fb6fac8062aaf7051f602f4a488f80fd70287065225ec570101bcc0d3e51ee8937afb929b097e1388007abe86bfd65a7b12d15c275f33e0f35cd9dc31656548abbefb5e00d02a382026413080da01d9402a4034fc0721000e040f18e1a28094e12972ce91f8fd70725ed211044113b2fd180e8549fa28d2d3e19ce22247c5be7eaf303d0960fa622dd7d413269a52da22a81be14576994e2e0c822a30559ae96c14ab4da070cecf0f961253fe42c50639b3f69caf5d3c07ec19c25b9f767243d56089d02431a976a00216609ca8f3d828731b732c1282718572b1f75d30c5661c710cfffd13b82cb3e1f052434719d164a507b73c9fa58db9006d70d3dd8c2cbf21301ef209c0bf785e560638f9b8f45a85c99d995309633b816898b6e2652aa5f6b72c70c6f4fab80365c1a92143065abcf7a78d1171179c06f5899477b6ad982f58515ab8e4c20193c46fc1abc1caead01de09f3f49a0c9cffe870da2000c4fa9461390586b171f96eb365e903aef3db69ffe6b0eaeb752d4f875e16fe711da13a3ff202d2785c14feaa3fdd85d5ef67f9a63e3888002d145c6af1bd2884e2a517cf19bc35301ed26d9aae8a86d7722ef082e729874514c09e766cc18e8e1d7e8aab1b7adc8b1be30f86c70fff6701d0209b3077538196d26cdfccd0196466c14367dc23c67581688bbcea0307f5e1c802972de8180e55b8fadb29adb20f87b41e637c18b0f609d650bac0e67c5ecda2fc1f0753fcd1931c0e4ccd6c4b225e73e3a0dd2efd5e3e1850abfe446d8469e33ffdbc7f82b65c63841e12bce0485149dae47d9ec857e502cc6e4ae1ad0a79a0a4847930efe162386f752d4f84a14c000c03e1993426e429c553edda9c2ad7d82cc56e32d3513c6955d250dc01cc5cdb92497166ea1bc68a1da187cb43f8e9ae06501ca3801b2132ed1fec5576cbccd1fb76e2967fbab5533ac109c22c0589238d07f634d550028e81f4a99e11b9d3424c4f045c1da5b0f2e0563c85f63efa9e00dcb632cd19e30d93e33bc3ac3e0d421fcccd8c5cb9c7b3b2e1bc82b20273615ba59950ff10d379310bb0309a6b0d57192a233c9ed3ffb870e520862c4cc9bdf9836dd71576e7965a3de14abac5225ccf73b615889efe658b4650af3cbea55d001b1e3bb7bdcc582bb607ab6daff090990adb2b25e06ec5b8094b2f910a244f790d21e2d8a89bd9466a786bf45fc09a7f4df3c24ab8731a08280509f1e8c2e28f385e5daedd1c2eb0b42e6acdb43948fd7000322b1a3f45d95092c5903c03032f08785e4a623d5cc44db8b87e1720ef3cce79aa01ff7cf0bceb891d2c734a33786dcddf6da3c0db8ff05870a552e9572c92f8a4dbee9da5692def041ca36c3708943f702ce6ebb1a108f5e5d5c7fa54c696975812cc543a99655602f0d56c25b76861f2e672361f11094baafe4ae6b6c8f2cbd0282def8864011b3c935ced809315cd27db394b001e0eeeaa4588cf3b4e9c9698185a5849f5f8654c03c35e813f69d92e823dd653172c2847d014576a833603ad01069af40cbfa1b09408b11db0b0c536a0d6cb96519bf717f8259e2a4d8887bcbcb9d0190a6fd22f1daf248aa8f5a3f80a61cb226b9cf029184f466d8113eb3589d3ea7ab7f365c9a1491b2f001f0c28a13631e9b620e5987873fe7e77c330fda21b24f63ac72c79fd42406bedd59ab8406c869bd62cb1b04830a69f85d3514eb57449b155f193214255aba35d14042d3d4ea4afd707150dd8e9cc9c18661d83b425dbfbd0f89ba552cbaa164ac61f0cd10a87a289f8432a0f4ec26b800346addb1c711488bf3c78c9072de46265dc16b616468da84889fc1915c648c9035432257dc66c620d0b054e194de77c82fccda41d7dd3db1db099cc25e0de2384f6c34200f66114c941e4209e3192edc8c04813a723e937493c2ca6b9033476002a2160c9884b61133b3dc269c9edaec1d63be177d84ea135b858fd63721639f40b6ce8ad9a6ea9283772c36fcd09c46d28b242dda517dbd12e813217e0247f9cfda779af822f8aba8373140d4ee25745fa9065a8909c5101458dd2e6ed2b1ae12253180472ef5b3358b8c36e53bdda4ccd95802f1593295db81974590c090195579b19d97de92c786923c952b3bac998f427e0239ae83ab8239e3b3e2d9a78427d92b7e8b2730b5c239ff76a5c41311169b93278637c1c9787abc632c99ea62eaae6c6c1b94a32cd0cfd81cfb9d8dc3d0ea481281e1d930e866431bc3230458f7c94047cdb05fa426d54f0d8225bb708900b2b042f37023c1c91b738f038381c6e3a1cd0b1085170623457399f7bbe9129181165902f04958328961e62d515aacd803e71b71a9a9da5a9671010231dc431779cfa9b56d15908819a8531b80de5012a17dfa3a92696b238d7e31c7eb997a98607a819e4f898c6b4308440478ae113a0f415f2f550341f4caf8cda47ee401965226b632a7357d39543a4d6933980a1f0ce2863a1e20163cfaa4693b152dd3df6f932cb15c75d2e1f9de808e6a6e130fec52c4ed92924ac3ba649ce0b3a02afcc158bcde0eb6f83cff2bbf349d9a899c32c0bf30911b21b17e9b913d11cdbcf5db07c426ce85239821634a0e4930b5398b07bc813c402ffd57a983e07853f7b6816546eec8ab62605c7d1f57562c42764642889ad5ade40ae98b228918d0e9ff485193f1a1e2e03584f1800715b18407363006265cadb03ca8e73c567114e6c2960ddd126dd00672c6e0a8768ae06806726e2759a01303872bc265e19dae5da7df9d40142a0e8789bbcc169e726179f0f7d7d23ffa1c696150d056678f5855650f14aaea82fa21911579271832c44c31aedb3b0ca359e17549da46d2c325d64f87b3796a3312a176ec642aba4f0b5be2ec40d574ade1204e3e8760789c4bef9c3a7ad3a001ec02570c392b8a78afa6529c96404a16fda030402c76b20287031ad94b176aeeb460991b360dc811d3ac927154a7f9e531a9b1cf3f73c462c4c7710fc4345a32f5b27fe0d45937380c78de4d1d946c8e1cc1c863a4e394d8c302d5d58446ecc6c7d2dab432969818c79bd7d49dba980e776702aaaaae9e2cf60a63ac0209a386bc5477db4091d424410202d51ce1fbb322dcee700b4cab620dff3918df0a9598dfc71c175eda14343d8de53ef065c7d48746da39cfd973b47abee899b4facb35b565c2f8e963865565663b057041581b460e18d06f4a1c3a04425e27809d4f8fc99b39ae4771cf2f2db02f89f624b80574471136a2e9904473d2863fb14edc67c28bb13c3628775346353c65213c994d4bf29687353af455463908cf25ac9ba7d9d8bac48f21f41c60df799cca2c3b052e51db2ca8cc1d76fb42db2e4199d3df84f0b14018049d89ec55eed5ef85aeb690289e8c772ce62dc21a0f35039e4ec9502d80ea8f79788be5ce4c8d08223dbbddc0fbd02901d6530b97c6c66c88c532b0938835c8ef7c0409c75bdc19f48ad34c905ff4a45ea6b03774943a1ffc9315bd54cc2ce2a5ad8028b886baf8e85f324cdb1302a3f6623bca5826e440e8362910a9640d388a527f5a71fb2e4c52ff599462ae875320c5e3478c3d3cb8ce077e798bb7c55c1a11bf8dc92af26f0c3b493998da2f1c7ab5adff0e00b9d34f41c3c2fb046a5990cc04c5afa097afcbdaf512cee75846b356215091a1d6ad504c652255913601a24d158576069f667050a8e4e194e11853988676112860c4a59c7d58a3c862687ae041e61761a6f32402b053eb7d206a94426f50f10a4a395325b0a519cfef5f47bb4eae811a02dde1d11412c7159dc2485cb64a98605348e9a444fb4217219aed4553cb2e6d9c0afec940d32a1e71f1401003eaff31db956094635897387a1eecc5d5080a4cff66fcb385a310c1813c4df86dbfcaf755089f3bf3903986d5324de433d9c8d62fff0bda72027b2ade96fdfcfe9d81acecd38d4b45d209a1ee1728425466f67ce08cadf0031a0ee48d7b3929f9c1f8d47ea1a1e9793e4946be2d5835a369402ecc6cf8c6ba4d47cc3907ea191ed90a6fd98959c35e657476f184d16202514c1727ff7b4448b74c103fb02ed30dfb004a1f9c6ea0f8629e27b90d9d9cd71e39695c088af2a26b23a3c24470350261990301f3a310660c0c50fb884322648896512eb3f331d4529a952b3e6e53049f472f1bd54ded6f9ede53f187d520301bb5fa038313ba9429d69762b82e6ded0fe702de62f3b2add381ea990fd52a90122a8dc7d68d7e0da48ca1d67c87450e975a90abb73b2605e6f177db3270e939960335ae0394cb8866a8b2139d41c2bb47271c517b2485cf6ea08c328a0c2134e9b2d33b9cb899845d67969e8ecd47d623e7397aff75e4e3b9d0618894339cf9d2c4cc241f632e91164e468b866ce63e31e7fe4d0a2f1049f279fb5af3ed5caa983a3e612b0404eebe7578e3bf4942c9b143ba22c3f70a69d4a242e899cf521f69682add8f1bbb25e1764433a53e85c10976848e887417ea47a2b0e69aacc4d09ac466249d586ce22cd62ea79918f10a4138a8f0570cbef0e68974f59b7af05ec7b9cd8fead04a4906e4d5c2ddb60907e9c83f68941daa836c3f29100117b262b6f4f0fda36e36c54211c9a455ebc704a233162778033d9a38c2f0a203b8b8ca9932ea7339cb83fa88ffd076a4e670428770a862e8c085df13f58307965c3ca9593f238d4c2953152d2d1afa74f8cf972a67669429a46fd9d697587ff07b1ec6ce72e71ae9ae09f48902e0c9a7c0c949d14c5a47a4b9a158f0fd09c73e83b1254247b212ab40f15b8f8c34607fd5e4a3fa0d2c0aa4a776435c08f20e18b259ce43eff097bbd7ccac823a54cd6e2732a5755761649f9ce62fc50b347982b19e41ba422c16c459dda2523805e7870279911468f4414d0717973f7759adb2ab39c451d32db9521005285122f126512b313c248fdbf7189e551d14af54f56ecda852cf9fda90fb2d66abb54ca56f83415a09082c9115adae7cc8dc07150da4e3352af3c05e6a0aed8b6a5ba07f927d8260964299304e3846a8a9b04ada5ffaa2ea92db8f436a73c0a967f75fe2aeddd09722aa98bc7144588910a68144d9346144826ec2bf82b544605fe7c4ea503ad1e8cf8e30b8d37f3f7d200c1497b9502d70ec72b0127a4bdf39cc6a0b7a6c97f64f953a24ee731beca9c40007cc5f612ad13ba961d69de4f552c2c41e73966e8cdb73e7e6efcc18d21860e32b334061c19df6f245f346abffa513fa36c730f3d79ea3b674ab73ac777a60263e86eb3cbcd24d36194fd8ecfbaf6150368654a14b4d70875aa43cf3fd1014a987faff1b1ee4f1f106ee8d072bbe49c198dcce730be55e8823854110a93b0835d7cd5ec81fe7b73980b98c7742f623a53080fcd7987d318a1ea6a4919e143c4c11aa6e809f9344bd4ab0648e49f2bc42f87b4c60346feb9139dc37c5b39543e13643c746976258f8e9a1aa6aee1a7f5f5bda6048c9ff93d2a114fb4c04f44ad76917d269280c15cc64c5e011d40157d09d0be5e0e8ba22a235457e7ea8b3ec12b78b837f1e5687f625c1a6d44824e58deafdda469ced512fc295fdb8320c29f7d7566e256d0de3c5d50de9209b71e9a323390efbb7bbf9a818693a550302ab26372dd53f021a995369c08e50640a45a903acac1852a289432cc772ba5e971f0b37359baacfa423e028a5690d4c9496cfe307aa94e7d6e0a92c6e073f5af1aa8557dc409793fe8ac426fbbe79330dcbd4683e903e5f473bc1282a5e9988ae4967398b3e02bc1ea30fa718d33e43a8482ae25808b52eba28050ca770204e9a4ff371d28066b24e28041b44969e5986e56e34bf7895cbd5505d2696423904de5d545a5b6b200ba2993ae8e04181d7a756d633760f2a87fd7e58abfa95e8746044a898f89c5929c4108aa097ec078de2a4cf7648240256167b18d271042f6483b00618023df2843f83990d960078a5c7aa03696908e693110f92a23b72db7eae665cd7a8ff00f55e3c083930b330cc9b0446373616785cd31172a6842a960026b43c869759f17940a499ef31a0d22b294c3eb423281da48f88c984b7aae85779cda5bfb2e653357d142c7a741d7335259c7b8d0e331b94918502f30d51f7294850be564a9db7b026920f6acc69efca4d100f60b631639e844c859a0258c38170471614b19d15481f42a92b5f0f6bc5982d8bd430d9ed7922721f56126ce370d61d72c6ea0c26be6e4c46a6246e2c01e2b3fe0b14e7e710311b862a2158ab40c52c81d37a8b710672cb8e2bf2fa4aaae0fbc7436597a9b43e9cb7e3528e00f5f69f8065cdee4164b26c4ba57a039cb5aeb8332a62f0e033e6a3205c1e0e80fbfac5ea1589cd19b14d6873e8abe0a9f212a180618a04b7424dd7eb5e91670c8b4b4fbc0979cd3c317d93700fd81dbf2f58b2756947d134738ddfb66673d78b69495de39388fa819f040ebf3769fa15f784a94cf33a9ddf5518c269e8714ce857786bcd65417bdfcf3813d81ea026287cfd76734c2c00a66df183e6df5e5ea568f7593437970a59ba08cbe2783b77ee0a4ba7a871e5989e65a7214679d5775ede268b307e242805a7981cf4dcffa2d9366c3313e6bfa9552b46ed3cafc690b1117417cf6a07acaf4c2cf488cfd84cac936352d66257b3bde5c577e798082cbd911fc6921dca83903398f11b70a1568536924add15c1b529858671f895509086d4193e34158120ce230c45fe1eefb3d0790fcb137b0bf27d039df2b592af020b1492a6e4c4498f2a7b76334f387d1649771cb721b77f6e4c0bdcf40f94623dd4388b71811c41443c475d5b98b90889887c062d02a0e501e4e0e8139313f3af09230ee092980c1db2873f42e284b205a837667b76f26407312be361db2ac58809eb904f7e47d349512ec8fe0cd72c3688205ade88caa3c223e6c540ecc12bac4768cd9199dfa564fba50f7c7c109c684166182e3717f44003cff846ebc218108487ed56d0b12f6d23727a9e14d74a8e367e68889c59374e9e7a927cafc3eab48c1d6ea042cfa78ab6f2a6ce184845891bff0d97787426461a5c21c1cf1dbbc95719a01d16e3de222fecc067791c23e6ad2aa29a9a26549ba29fe2707c0892947dc1adc16096787f140520722c2c38234607670d52dbccfb2ba9f27ecd2e092d78c5ba99763ddb783c056bf41a158b54aada305add80515c9dc8874abab9d000dd401b6905c2dd0846da2f591366749e1a2fc971932f271585389ec7691f7eaa147903751456aab64012f57a1ba219f93cda64070a12eb7c4ab95d9c11e5e1d62a9384f336a6d27713148069d60babf2707cbb30113b22ec2d3423f029fd9ff67ee20b094fa80faf68c4ca4bcd0223e67e6ac3c37f240f311b02c83d3a9d663824d05dbdce2aaf5192df7c49c7935b7495b6875ac36e97b6bffd450d93cf7ea7ef75984332c01c4ca02d5199357320bb90b146b2af456f8a1bdb05bff584f5034bbc941cacce693bc92bc9d6c38f654d9600e2630b004a4a5e90a179b548f315fe2717304644ec3d642d4f0022c186d3cfebf350c8dae2ba6eddd0d0d84f36462eae397ec7db5859b59c8eed3a6656e784165bfb1d5670611f45f01a7e3ec6fdeab7424a6073679a03740cf51fe546a85d51594889eeedf9841e548db4c78e99d178cba0c1baed456596102414549387e7514d19a41bd01aec03bcedd95f85508dfcf63d9965060a6e9f403ca4e12d0ab20f72bf0eb49c6da2509472bd91dab4d497b955c9de18693be87ea2fca985d2948757aec9b6a042aacc92ec371f386437577c1e8f68eaf8e74dcfa2a6b103ae0bcde62aff3892fd53c967558ea5bd757f602e8e784472e6feeac04a3bc7c67c1c209a39d523de107e11eba092917f3cb8c2990e8dc9ea540d463d252da664d4ce711155d238a30460f84fa85570a394a02e6ed4fb2cc539bd172fbc6ce9f9cc86691d872a57fdb98ac35f4b525c0a6b55c32c2daff5991c904cacdfa788be72325134918df218931701b6244b023b0851d20920a5a5a9155b8a30f47f7c30d4bec0d72d83cb1d9a56d66e989e08a3a652fff651827f040fd32c9c376dde489a955f7aec436f907476b8aec0de9c0a4f330d0c212b09806f65aa34c87cbe93046a98f2f1f733574fd0849b12f745b620d92dd1bf1553e9f7d172dd0c518bedd515d2fcb4c48a004ac838644af95c141394dcc5696bb3645b19e075e0470c29b0664db1cb834be70da5e45b43423dcf4c81e0149aa053a3aab711648c5f8af4110f8231d0c9ca063bfcde1526038a8cae48eb03308631bf733cdeef1dcf5a6267810864c6aed59749e4e0e822a31f49c2816e71d190af08c92f0c07d9b4e7fe40d90692fba7f295e5570c23099107687ce624d72288cb7a3e57a453eff899012d166a14328429434a4634ad67722a7e2d2cba2c0ac0e6293140335e09e7a9be6423678fb6f9321cc9d0f86a78a8e995396e9a1ca1b7952825e2d98555fc5ae8a1ddfae4c8d9569b50a40224c70a6973af4af8f5e029c171d7251680a9609f012df821ffadb3e09679fa6cb2a05ea5d5391973319a98a5d1b72c823cee7e80690b0053659b94cfc50d4a443ec431f0f464a0b603d62e5ff02cd214f34c6c57ba0d34f0a76b9c144e2df123b389fb020a9c89d55b6b931f7a255db8a1860313fd83bb5570d5f9007c41a63ce6985d144cb17035ff77a1472d7b22223ac05b1231e5bf94b110ed61e4eb68ca822a7f91d542110d62102664e70e661328ee241309384fba6c061cb45a689b3d0ece75a984d22bf590d13039a6ad3509909f897426e85af70a5c5e00e03d24629b88554295b1432537252de072c6494330a0992363d1ab10a7d6d09f1cb3262343de4f626c64631245ebdbc2c4f16bdc2ff34c88cca04732354ec340cfd67a661c7edcc7096c46b57fd0f111957c8025718e4d255d46419adb33ccdf8d236d29610ef5af62e9bfc3f83ff06f152cc96a52365d800e8561b250821071af80a7629aa3fcdc616ad341846aa0a3d963c159f667b95ed1899844c38821426881a216cb0ee57c8170012e42af4b48b221f6f6e06f00887ce5ae405961b24fa11a3e60042a9b1dbd136cdee8e20c8f6b74610b1da17706ab2b6e91e1114206624cc214aa22e42f48b88680d3a82e5d108f443a4d238bad37b869a1b1c3edaa88d745779f9ef8565a1a354e4808bc5064f116e73b0b3c70979487e3a657c5fad945385c919e77aa816e42b482c7cf813108fc7519c703c37a04573e32cba917ed851f5294fafe7265aac1e556f0bc0c1626cfed07d127b65c91d5a5e674ec3ad3c6912e1c0b1a303c3a055c796d92c9cf02ef9e112693408b094c541fbcc2f5cf99ffc49421515b32acb086c6e310deed5f3f467397f2e496b902f398197b0ee4bcbba486b3b59664b266bbbf6b0e89ef025ee74ec5bce82088d7d8c79858d16e7041b8a706fce5eda4eed25882bdc655ea2fb5c514346383d1b4c75702c0342072c81cf42fbd8337fb22b730d361de709d6a7f68b0414ac573131bc6fea3e9c838e133b9d432266dcb14c71fc5f3963963cface354241e3fd34465b3b3f475756838071753c5e0837ea91c70d1366fcb12911ab8741b4bb985838e557439dac18f372b0178204f0b0ec52ad4b46f921709ddc5219f4ddf25b5f44548199baf0d9306aecccb9668ee4a302a078f90b8f415f975d04736c88b33c66b62f52fa9e8a0d926e1c6d70accaca964caba73a0b8dcbb09a151211ae9103ad289e76eb3266e1d6000a10835a62f2f8a65f9907542e683abd0e6b99e6fa964c2190fa3f3a3421b539f75f6dee546525abeb9e65fe2ec08b95fe319f63ce4e3dfbb5bf6729c069c4cb326a1bfd1639b5a963a9be3539410da64e2bda94aabad0a18a132ee2130508eabba9fa0e630d3778c5a560d6b620cfa4d9aa3d03f23c83a3272b31abb6bcc93a98b5651ef6294045d0834621eacddb247fddf0c179a473878ed833aab87c7e9b90cff917215d91baa79e62473143b05234b2185ddc17f6914632da0c8fc62aad56a7184420d2a263c0650d3921de8a865066cf407936ac352711a96e8c1b4c42396511c3aa52b47a32e2d2bca256fa1fe441cb8272d4a51abd889a785139f82f01aaae7161ff701ea07b7329c8a54cb1314b92eb57fc8240093d409d5f9a312f103c6a4345320377196ea18deed0df0856b60b0b68b79e88b059b02d97438000d45ece27d26e7e9e9b752a6fb0e9a8eeb74918c00253b3656b7929f1817f87e070940afb1231e4a4b6fae009a0df1663d6618baf7e240ee4455ed747016387d98005a229dd4a4324745e5f0b6af17e9481cf3911536945647cdc4e6db4f186101abb31efe3e42a109761dd5a0377f30bd4f14e6bab8034b04fa3db2a7b791bf9619f9ca6e2199dd0c1348ec65f0d2f48180619d1b2fff8ac68af32ec717583f4c2f52bfdd19b9c358fca51e1867def482e6d7dac2add48cc9517d7c157b06da0ec713d99c1423d7fc40cfdca5a679f1d1841a8d306793c98307eadcff9075c4e8dae975e4fb7d4805f3bc7c5a22d9fcfd69f01b438b08aa1bb977d6c214233d7d33a85fb736335882f9397f3b5ee0cb1fc68414dcdd222476211d6cf3c820a65a092f540054c802e85c85124e33242ffb18c17d7d80f500393b730fa7a09c3a0be903f8a1edb3abc0e210f647a9a6a0c675f085db6b90ce8b3cdc7edc4aeadc6365d23ce5e137c48bed5bd35c9c19eecf88d186a030cd3be33060659022bf9d3b598cae34ffae9707dbf97a550cd628fe9facb3cfe69d0a59b9146498ec3701ddceadd77151ea9a27aba072bdc242fb87a65d122e7225f0363ebe1a0265ccce4b585987d75583837972ee0bc6174233d6886dd06976e6c4ba3880bc0a2b68476f1103781f0720f6972b55d667e48c6c570d8587206dbc6b9268819e50cb1e7724748bccbea1db014095eb5f73be8060b400a4373654df6157800a9c22fbeb9db3c32dad1c8281269d7c7f54b05883d919a269a8a91312dc776c8ac323776beecf2a7e5c2ffed0f9f02857057b2cef87190ed893f79cd10646fe1e40dcc5cc8209d927f9b6b3651e4fb6ba34d72346554a3417c01f73ea006ec35a3a1620dddbfb47a77f4d96e8b12a1bf55714cf33c6d90aa9550f898dc48c8651eabb39d235a5c563431ba7096bdb0f3bec06028ab1db7a20707133cd64932e0545abf82e306768c0d67a19e8444f5752eb694974455a66cb63f816aac420a9d104e1a88f5bc34e3e9f0a2cb16d7814485e2e004d695eb066e07a927da878313868f0876572382ecaa105a9ede6d3d8b2261067cbab5ba23c8e8e13839f35773eaf6c2c00d27aff7d660e4809de427fb45076d090eae8064bc8d41d0f0c2445f8d2b2c39b838caf26612a84a4a209dc939f24c709860b1348daa75f200d32c22a07d5bc5505d7274fa382752bde48012b2835080210efb0dd52f8567b3e62b3938fd7aff113ea0ea9dce981bf2f0c78d86915f6960e6b9c23258a4e0fd143a45af295069ca5aa875b205189e5df6016f235844b63f644868bf858eebc12a432d62d0ceb675e526def90a2efe44516d71e33db4302905b475f9d20fc7a6b8e893c85027b81c0d5719c2f5ffcd0cc5104b8b90a76d7537b366892bf300d7ac085f8cae4d163e063a858faf986219295fb9164091770cda519327ece620de38531ce25d59dff76ed89bd45ac22b0df7ac4530b7ffdc81eef0a3072fe63711f031bac76d2e53af69edf9424283990b749258a868c95009e715aebd1069708c859ab1348c0cba470ec414a5e5610972b46bcbc6ce23dda4aacda241de41de2ac039c904677e8aa446a316d7b61119b7471ecccd50e6b45a2990e00eba506cca50bcc7a4ee9388ff5965f1845112a14879d1fe764ecebd683564a9f38c93c17dd24db1adb7e38c632a308bebe29dfdfaf04eb7cd5ca56183062363417788a05e673c51a533797252d10b19c2c0fcf12f0a2e174a5ef07dd10065bd642cf1a94d85dc6a3933c073a38d41afd0027787a2bc4dba0f185dae6a89a77a7018c73c743999a5785195878f8aa84db27c3ac6310b77983826aeab406bf243e407f2f1407949f11f8513c994dd00d0a2be343b9db87625acf429f004d705f04631951c52c43bc1486297665fa935cb3747cfa20da853060e75abaf5628c29e678d1096d9f3ae53389d39df4b0814ea03db20222000a6ebc62ec74b8011dad677d2d61108ec20a2dc0890b9ab0c2bc85e64606b09f57cc786aae5a6e486df6a20abbd083b2e7a714158284477099c74a2ecb714a23f1d3e1ff301c03e202ecb12c3058dc9ff6a84a49b799013768a9a6bc25c640303ccd785681e94ea4eeae385abdda919a7460ad57762404537afcc01b7b128f9e4a444d24d236b3fccfdfc8b64f9a7c7bedd350ed2e1efa8c92f7af72fc56badae58a86905e236ff2568bd045f8edf311a072db08cc154082f804053d03a87b81d9e938398718c285e9af0a0526893a0b0e04abc9cabc84d2cd16845c1921a1a80e5cf645fa5e43964261aca91cc845b606268ab296e3ac9f80b1c3fec454bf32f7669f33378594526c9c3b02cf12aff7d42fe04258cbc988da908a995a72118cd94db8cdbbc79350d019cf708024961b09709cd908c1e96d3ab61bc88f3d15a0e1f75d0c17028fd04025aaaf1efd524f96805fb1581f3860acf5212b139e3864c8ed1bd74927176510f9165d3b4ce902f2f83f8b26da41c864296161902f7e8699ccc0e6d21cbd1eadb635164d3749cbd6be884e4f3a38768f0416bd18d31165db2e53b80337d49666a78ac5dee9ca2a62d8eb461a1946a701fea984f6b1ad18de912160c7b67aa670559ffd80041d1cd172cd684c1951547b3d52a5d51af394d5ca88ea0cead32de2e7dade8b4aaceec62147863e6da3ea30fb872ecdcc52670136e7ffc81dc118bba02d97b4987fc2659a5e903158578d1404a2e35ba20208669dcfddfe954ea7b4f92d52d2f0ac1b5e0332cab02feb8440f0c3560b46439330f0f0f0f0f0f0f8f39c684d4d6da21909a924c124af9319c37644a29c99464df8d90644158f81769c31fede08d5f90810d0cf80b430c793809955490492f040f875331f8a664b102dc8174da95d62aaa49c80e5acc1353c2448c741d342932fda611ed75311d5e4d33ea5dd133640ea4da38a771715eb3298783105679455d88110a1007f3592e5196a6255f0038902de9492aa52eb92ebf81b0a167956e5b2337d429315dea36394a8a6d3877f96611ee351b1615e49fc80caae5351c539ecd05a196e32535d4751b4cee34142f315de38e864392d1e3e3f7caee3467c8c7c46286c2479fa95419ae5221eae575a34a2703f2548685a4b6f55dc7f05852a3421383b17c6448d2730f110b433298756be8c0b024a1c167b4c517fd17921572f3870e4b79572f586e31cce6126ad38563dc1a2d1b2552eba870e15a97f3dd1a992ab285362e6db75be6d40a6aa15029645a76692a0ba7b5ac22336d8605634b645d4cda35ba7a85c34e9c7ca2d2b2c229051366ea42a40805a8422ac5fa152926b13c2a904c2d898ed1bd0cd3148e794662d8440b31a85f0ac7487aff4d371f859388b98ab0e81692daa170dc594d2721e909a7d331f78d1ad37d899c709013d205fb0b42c5899b70aaa0cae24beaaaa0629870ce1161a5cd334b38e9dc6cb2e4c5574f56099a901b6385f3dd24e19827c8163dd944c229e697debdd0c917c7231ced64c82db55fa77e239c6561f64f45386daec969d2fee2c44284f3f96e89a07a86411b0a30044baa9cbb8a423808cb3e2193fcaaff8f0a0508821bad2aed4764d1970284d45ff43d794129fff50705cd72dbf88098e566439e9cd0bef7206171c22e4b0c4958001ea04189d2b72c3146b983ff46bbe868d917e9c0603392b499834e74e9a9ce2493e41d07c6c971016e704c4b79dbb28d0a600345cd8a88b597e6ddab05222ac4ed8a1647d92c257f24ceb6cdb348c47c9dadff75aa65591c54b8e6145d9b581c73c55f103329674a5a589cf436a48a9e2276a4fb0a654b99cae70a4497b294717525cedd8aa375caacd6f62529272b0e419d48924bb88add94960c25830c125520fc94491b35f1236fa9386bb434fa7726f60419a8e86254f4fe267fef14bf6be4f0bcceca8c9bc29a0de1235808517e538ad3a490c9e42d4f8a424cb2efb423bce44771b61efd96b19176f22e8a43f4b5ba3fe115b7e4a13867300b41a5b89aa937284e2a8a65b5d1e6965b7fe22ceb1567e6675450224f1c456dfaf27f3b719c6842933ea55a9a623871aa56f13849d1acd4bc89734cfaf7d195cd829a385804bd7651eccf429389f35fb0fa4b5d31a4a4c54495172a2b5cc6185fe2385a52b288ff88955e4b989410e2be249538cba8481a944495ac494a1c244dea9e24e7492062d6cbe9e5886fad24ce9821a810f248b0aa6a13446320611499a26a953eab1e718a1a9624e6d511a69090e4eede88d3659f5841882fd527230e2aed0499a153375bb8887ff6cfc28c7e47122922f91022250b9361ee4dc4a9c4dfacd7cfafbf82886365887b325888c8213ac431474c5a3d9145a9ce10f748b4a094e94ca3ce42144c8abce815429ca297dcf8c610376277436b1084133b972cbdee8b6fa4c2602f4e86208e5bc1cb4547ae0d83bd386c828c409c3393dacd8d18ec4d409cc4c45c46e9ea492dfde1107ca4c7fbe473dbda0fe730a5e316832635651fce7b7731a83c134658880f47f1526f17ebe667ea1e4e6ad4db64963535b51ecea5646c6cc49987d3fae5f291e9938107c3b45582a49060b9c349f3d465ba89bfc1e47638e88be239d7194d556da3e420a30ee74b41b72296a94977d520830e275dca4583daae2c0132e670101517564aa47c6ff10f5c608b94038cf60ec8e124d2694a21c6b628496b519011876334dfaeecfae37d9d7f7cb40e1c5ec0e17032ee2a5f628e742a5db000038902fa86834a7b9b8949d84c2c3bb0c3254081076cb1c5072ea023078e084841861bce7edee7a6f3b7e118d64de628f9a7b2335f90c186835ea650a5b69f5a6d2a41c61a4e3275c86c72535f2e4d861a4e71a5a934c609f12d54c608196938a6e6d2b96d4b025b3ce06cf1802d20808693d490d338bab933a66738c8ef0b7a92e5e593918f0f2ffa05fb05185e780146172ac830c3f12b3676978798a8de156494e194f27f57dc8ca334a8c9701e7da57e4958cc71221f1f5d903186a346d4d3be8911c3494eff6e583f35273361389d925916d6f54f7426030ce750ab23f4fcd2ac7d36aa0c32be704a17646dce74aea2688620c30bc7c93dd173ad429031eac26135263112f34406174e3d614355b4c4939174432b6520630bc7a87979a22ade454b18385c506020430bc798986c17f8175eb704195938e5d97fcd0ed98c195e24c8c0c2d9b448b8f8a5da3f4969041957386fdc97521f7135e26a85936baf88242e3aa4955538e9ef4a936f5e116450e1945c523e131f1b4338a770ca9b26d4e80d1d2ba9094186144eab954ec498771d5138cc9be504fbd2ea19430c0832a070d29ded946862f303194f38c5dcb45851f2ad76eaa20b1c2a70c249ef45fe6bda0619e3261ccd65f449de4cbacd9a810c261c4292ab143a5a9964b484c38e52bda04a1fc850c229769c0857d55dc97249389bf4be9ca59b45cc031948388b6a665cbe984a56e408e74c4988a0d515c7f6d2810c231cc49514a5f72e1d4538954cfa32cd62eab8fa86968d1d8e02229cb4ef6ba553dda1973184734c8a8dd1c2e5ca6c84708c72db173ce48b340867cbcf9882f69b989064389001849388d147d9049527b493f18383aee8253f72694657860f4e7a262e59055f13056820a307288307a7982b8910265d193b388813763257969873843274705013f4dd7f97fe55282307a724bcaccc43290307e7d96ce30db591634718048c1c89326e704c933a162ed7ae6f1205326c6062d4424d1083162c481c316691c8d0933974793164112316674d32a638a1b1fcd23a581c5e6373c4143b2b950721c62b4e5f6622379e737e22571c4f5cd6dfd36a396432462b8ee619b4964c4177aecf8a93522656a7adfac429ade2dc7fc2d25e6f551ce4c968d15915724f3815c79cbc29a717838a830c4b39a2e8f414a716714ba364e9b6a598e2f035f2275ff929b34a294e16d765448a61d7f4a438880b62c4bb3d8a73dfbd659e18665264a238a99cc82da9be7e55c508c5295494f81b82be0023c7dbb8a18305619032314071146d97f9a4297de298ec644e3a19267bac4988e189e359c87c62233fa4de9d3889695c1022069162843871f84b9bec3f7fa5a38cb1097d530411f484fd2e1143132751aa7d4c6edcf102dff105189d758d88918963369f0dd3287327dd189838ff28df924d99474cee86960d3076d8d0408c4b9c6ae38d66b81853347f5f8861897367b4d269d2ea57f42646250e6e722eab2fba536b4a9c6b4ceacaf96685189338551052c7437f74e6599238a6aab244e268fe5a3bb252dd5f4a0d312071d04d2f1b1233abe4f211c7204dab8c90bc3aa3230e735e71ae26d48853574c3e096230e2a4e75531699ab60b3116519c6b3b6328e2e03a59cbd4c46439520290831889388b8e6779ed8c08a52eb08881886385944bb48f90df78c0160fd8e20139fc46ef600c6cf1802d1a10818f0f1bc80679458c431ce3fb24cb35b1c3521ac31007b554a6e49f59888310c23486f813e2ebef181fd95c8c419cfa92e6923795076c4134b0c503b62019d8e2015b100c6cf1802dc805b678c016640e3104718c669623a2f54a5e550371f84b32d68db290100310a7a49d235436863ded1721c61f4e15724d8af04fd397ce01822d1eb0851662f8e134b96448f9fa364f3a3f3eb462f4e17425b49ce9cf75931846c4e0c321f904493222e94a97f522c61e0ee2ae232717fd2b67f470c8975a4972996b6e350f0769552a24cb0c69b1df7a410c3cbc6e996222ed2a487b428c3b9ccd4af66d84f3102b9f8e187638c436ad9bed4d83d24b8e1875388d3ad72d71e297217a43bb88410753e3fbe4d6236ea4f0ec0731e670301d21d5e90932e59637b40c0662c8e1bc1b34cded65c6caa85a20461c8eb5f1e7cccf4d26c9e660efa24f0231e070d60c22e3d76196fcbca175b5c50724b04599186f38b84c6c7d91ddda93729527861bce124c29d53f2af79f260d31da7096a4d634d6a898f8a33fc460c3d95e4be586af247ecb186b38ee86952072f9c6968a9d21861a0e32ec2a776388a966a7e1f0bd12377da4d37b1e1a4ed972373ec7186738954a12b73463105e6a0c339c5c84f20dd73446198eb12fe526f764386ecc3a39b44596901fc3b9f267093be5179b8be1a031ff224f0ab12a0fc3412e84cc23abbaad048683129a7495e6568c610de77d450ab96059250f4a1c93d2b3dcdb2023c8cd631295e6ee746f4b561e1e514d36698a3c62feb3ba808724ce1f3265baaca5b4da7001e7281bf8f87001e7988147240e7abd2d7596c893a607244e1d2321995026e22c1674c121481b608f471c5d2da8fd8ef817a7b9a145828f8f127838e2203f4d67be6b50972b0b3cf0f181430561e8c88247230e236452a7436b4544ea0ee0c188f38698f7910da337bbd5d12a0803055fe058d691e316718ed199f462cc56df53d58e2f2840030f4528a9ba2aa59458b1d4346b93f29a5341366c7824e22076b762b7f2049f11513e8ac7218e41581227c14ca901c43c0c71d69130ae236251737e430b8c2f6c98198f429c26450a25224fd4823872ecb0414c064860830b0f421c64ca23629fcb8c856c031e603b5c47043c0671c8a2d296fa8d9e15b50ad80b63000f419c35774491667f2737a78047208ec1eb5de45367b7321e80387897ebba6cb5aec53cfe704ab167399af42e5bca0e0f3f1c83945d9112e2e9cf5a87471f4e263bc44dc636ae890fa7d4244484a8cc1e4eb337c15ecc5bd328f5700c29fff2a68c7938bd99ea7f08a5364a160f073922aa79053115420c041400c1c7c78e2f70b0e0e363c717385850bc60c1171e060c76a8c71dd20ee95187f3c95cdb0230767c41010f3a9c465f69d8a442ed9ece630ea4ce430e1e7138a85e9e141f390f389c4704752a7a376f38c41b51926a52653a150f371c62e7aa89d174e0d18663bf5c309db549589c0da7b518522a593ab424cb1ace972d8a85470e19e3656ee0a1866392694b58676d6baa74181c78a4e1bcd133e59c2f95cb180dc7d4ffaec94a4aead89ce1dc1d2a3494bce83066380697dcfae29145a8b70c07dd1b93714925c3f1c4de579a74aa47c4630ce75bef8d30ee2b4a4b311c5289532a8cac5829a14718ce3a56997b54ed0186c376c952a72567636fbe70982c4aa531592a0465218287174ea754eabe92312b29df85f3052933174e49668cb1905479ac650ba75379ab52e1d2c2c13ddb3229112ff6ccc2692e64eaff8875b190207860e178f1925c05bcc3ca8c031e5738fe848fa4747c6d68d6040f2b9c4ccedb8f0551154e2652f0921063527a642a9c3e725969b578db78a7707a7bdd88666a829aa8144e6341d4775ca248ac44e1205bb76ff795f2a25f7058f080c2692f7f5ae94a330b79c221fae8882ce9f742dca0e0e18493127963b94d08cb5eda327834e19826f9d4dc59565b9b09673b8f13215746068f251c77ecafb3d2624934957032993453742d5a1099249ce4a44c377dba73f38884735a5e0c7e1fe19854fded8aeae1e0618453ba18df2c25d1202eb311068f221cb24fd0763f5a73bb66060f221c26688e65ab90c710cee7a73b43beb31483ed2184b3cc89b4d974db2ba58d0d2b7804e168256b824cb2bf00c30b2fc078c0211781f5c10308873cf6e9f396d6523cbf000347cdc1e307e7d55041a890b32e761f9cf3f2088ba09dab750f0eaa257f58bacfbfc98373984c7631ee8a06eb77708e206c4fe6af8353e5f64c16d61c1c4ee97c956caea6e88183f3a4f07b6162f84da2c70d0ea276d729522e95e81e3638a6c6526bf12cbd3cafc541d85889fcf9ffa5725a1c3624f99c9c98894c9ac539aeebe26b4c92c5c96a4566b0d2904432c5e2947f64fd7246539b048b53299b4b4ffb15078b103437eb5864d1156753b115532bdd8af3d9c8d4991483a9a45971fa9a2822687e15c70c4a66c92575c48954718e53751a265608254ec5e9f462aa78763a9245c569dbe2c2c8c913d4884e7112257c2be34389ef90294e6983be1407b519228df0f57b1393a2d469eb1bba8fe278a12bab88c888e298e226496651138ac3a6bc1125084b212f288ec9e59246523f71b66cb78b116b4984eb8983ee9ef4db60274e52349be995b79193e5c4c152881049d398108b6ee27c79f37efe9a4056ca1ecac4216dfbff5c8989a3261d624b26291293f012e70a12b78248d012c758328ed4f82e1b7e254ea7924bda9e85595d28714cb71d723f4bf9b6651287b4b15dfc92c461631c5569cd32424a89c4c12fd826bd9acdb687c4e1bb63d4fe6e9e8e1f41bc8941b9f6788e5853372f766dc461f4c24e6f54d2da33e2e0b3b1cad2a6dc978b38e8cd939151d16c838a38c973bb4b1a34a5c42c11073d277e2fd924661811a7b4d21bf95e1de27c2f31fdc6cb10c7daca1bb137970a932ac459675e37a6b4dfeb13e2389ab5b11b1dc4492ddec90a7dc1a4480c296608e2b451635698fc06e2a42f057fb5d11121df026600e2a8f1a7f9d45de9c8ff0fa6de9c4dac20e387a3a5dcdbc417a91bda3e9cbbfd54fb2557b537f9705241ae65936d49d78f7b388876d1e722a209abc80c3d1c7ee24af23f5922a5f370ce8ba55c6210bba74b33f07088b1b49ae55ed55bfe0e33ec608aa694868b4937a30e27218488ee0fa5197438e957cdf7e61c4ea952c7dbec9f8eee6b891972388e88b54b29d4586a461c0e675d36e1445288dcc3e1b0513d4e86e47a492550cc78c341095dfbb6ae6132253931c30da7e05d628487ecfab436d081ddc48c369ce62a288ffc619a4286d987196c38eebe464ca65294b13ad9d8e1c52b0cb88b3074e048ba33d670cc1242e56dd2666ba91a66a8e1f85f622666cdce48c3c94fb78c2c15e4c20c349c644c9bf37e62dbd32486196738a6d5d1911de373299ae120528acb7b56665a2633ca70eabc4a418acec9708897336867108b71d9180e4959583e115b5c2b258693787ee8fb593b150de4f01d2cd0c0161fb8c0161d701d39bc053820f01ac8e13776e0386e98118663cc64a32fe85df74bc1704896c1f44ab85849fa5f38484d71b9bf47a49d7b4298e18583fc3a15472e2c871944cd1fea4c65e8008086195c389f4ad64d69bcbac28c2d9c7fd3cb28b51e32de450b47d1dc4b8d16cc546d569891854318716215434ce8cd60e1a492c914e2894849857a8553ba52b94e8dccb0c241b8c85acb4c6046154e2567358f45b77c397f2530830a87d1f2e1d91763dffb3682195340b9fa6b04130433a4700a4177dba4f770db90edc08c289c424c5349527231a5cd1b5a5038c9fd4b96736366dbeb8c271c948a50b7ca9722ed84538a4185fecb3e2ab67b8e194d386bde33adddd3d15c99c184434a51e267d57533ef4b384bce97a8512da78ed286b7a00b3074780efbc00c251cf69288506a22a8d4ec0d2db6e13bc068815d604612ce19f2cbadbd3a36db1948d0194730798a6db6c828bfd20c239c46756b04e5f2fda2bfa11586172c40a402338a402c5113be244295ad4bbc86cb99326308267da1477ca5a9d809660881949a2e2d2897d8cd08c25143cce737a41083484ac20c209c4653d2db2072b566ff83532c154ceb68f6e9cb7d7050b2b1432ba67b70aacbbea4d9e7c15194943f93fd5a51e11d9c625ed15539e1f793d3c131fc05adec0ca5379e83630c62bfc7fa9f8183b3f76d9214a2267d313fe3062715163bd636db429f66d8e02c5a76c2478ecead512d0ef71ef9947b4c8b2a83d7c83917f76671ea493e7749999ecbcbe2906b7c6383f60f538ac549e9141122bcf42fc4c0e22062ace4f6842cf3f615c73832d8e5ec72ff93b9e278294a2cf7ad38cee6e62b3d71b3865971ca11f9974bfaabe0ec27d7e4d39b72559c4ee869d1d7669a574cc5168469d20b9bb6a48b8ad35ceaf8db540bbbfb294e39418e246b0b15456e8ac47e54eb529c259c92d9fb9d93325254227a97a75a1ac549b30851c99aa56d2551281a4a6b6da45d3d14079942c4d0ed3528ce5d17a336f36624ab3ff1850278e2904e36e553a9eec4712fba75aacb0921d29ce0f5beea36dec4594b5c268b3651e46c122880268e415b298dab36733967e258697d9547a5feea061347531a345c8875eea3bd441973222d86a690b3c441c51e8d717d5e19abc479e5bead4283669b2c258e992d8b95d9a69a3a8963d4f8953ca48ca44e257134d525d4c8142012a794b535d9bfa97c0d09620c9f6c7ab2c4d8235057a63b33a538bddd11c846505ad9928dd2ba11874d9578963a94b8301971fa92615a73b388a3eda8bad8674aab878a48c4f92aed9b85b820e2702e49b9a97039c429c43625ac4d63a8ae18e2769315e259c87b3252888352d99d21281d210a3088633e99bbb377bfff9d20625080401cc46fca7866f9debc03c4f1436f9cbaa46dceb53f9cde4663332bf383b2a7b4c910b4ebc341a5537216eb166a2c3e58daaadfc341c8107454ba779bac1ece2234891c375a6a93017578556b344b65e9953b35259154a95dda10873c226925fbb5146a17e294a2994e8d572a4fe90871cac9fb15c3a81b3088535216cff28b97ae7c21411cff828bd808a24452321c5e04e2d8bf313ee6c44d96500310a7ca197beb42bac61f4eca34529a5b9e081b6f68b500473a0898b532250335fc70ce9c793409b54b6993fa7090a464d0ab9274ff6df8704ad2472995fda76ec13d1c248cd5c6541b6a6ad3c3a6319967c54a2924e513dd67656741c835f2706c11b578263a7f7fae8187639aaa48e262bd93cc1a77388a68266fb9cc16410c410d3b1cbfff35b62c4ccae5e34307312ad4a8c3219b6db7b946da94b9061d0e79bf940aa75a8d391c56d77ed3424ef59fe4408c16596316af9452cbc33dc3ede8fb76c66ac4e1bc19fc72436b773c183a4aa2f10d1d990112d8a81a7038080b7639bfcb4218708e1c8c35de8068b6884948b9adba26c6755960956347186fa8e1865334c916d465380921a40da76425cac4a6ab94ffd970d64a3d49bce5dabebc865308572a412368be6f6a38a99d89e97aa9fee46938f6cfa69044645ecb161a8e753aa6845d9f461139c3292cb3c93d75a9e26630406986f39f79aaeb843fef3087083287a71a6538bfe8c81a6593e1e4b1252cdae90bf1f6188e3332b8a69ef9eae5da43f0f1a159430ca73022b88736350ce73bb1a7dcd7cf4e48c0608830553b9fa32f1c6324fb8962a3b9feabe18544527196458a5fa30b855a659746f22a4ba92c7986d91b95215a0a430d2e1c434a13638ddc242466bd6dec68ceb1e3055b385e5a1212fc334f5b98981a5a386cc94c97a80d6b408d2c1c73569420a38db31a5838a8784be61a8237b4468d2b6c9ee2195d5d5dac4d2be34515b56955d3cc00096c60a08615cee6216a312c59868820a046158e56396a2ac624e35f860ae791999522d5c5cccf53389c6d66c89a3b556c2b410d291c3353748aaf06b54614ceb7ab1125fb9ee6586e68951a5038e4082b415b48b7908d27903d44d2e48d3f71c231ae694f8bb496a5ae2bc41a4d68410d26bca0c61218af8ef9fdd6f0bd8612486d6159cd2c658aa6de95e96cff26895d2309674b3b8b284ae7e459359070b24f8b143b3428bbbdc6114ee92eb21a4638890c2126f3b26412be1a4538aea5a9fd65d3a43daa4184b3e691155fc91a4378ec6e3462476a08c139a5b2a45021572308a7916c27f4bdaf018473a5532aab6aa5357e70123936996bcc76e0d081a305668b0f48c0868d8f0f1b1f1f94a8e183f3caba8949ca36839c30a0460f0e429a8aca79175dfd83b1434b2b508307e71895ef54907317a9bba1a503c752a0c60ece696a726eb22fb1d5b7b03e504307a692ddbe5699269b03adec5e3626a1ab5d1c7813a289a5e4dc54b9012716d14792be5fe0378891b2f10214e820356c609694b52297564b8a2fd1a5d25e57e46a718ab467b1aeb12742b438c64cd297662342539ec541c8243a31dec610ebcae26c314cdce5a496f24a2cce6642d3c6cb8c31291d2c4e7ab2d58dfd89aed32b8ea6d982a9ca7f1f1a291aae389dfa8d24f625c3d78a5388983744f4bc285967c5315f0a8fef33b1d9cc559c26559beee95e15278b5732f682affe4553713a71c95f63bd43f7888a63ca9af4470a276ed4e514872d95f073299dec8b86294e27d2d55964d791b8a1510ad4bb2484606d2d1ad52ebf7a5e4f8314e7d3af495978a996138fe2245f72b282981bfd4b14e7b690dd92d0cba24f0b83053bf4073442719221bc62e3824071926caea5f6d54f9cd7fa26cd47bc94157be2ec63af7a21749526b71307d3f43b2727cd024e1c368a9ed2772287b8b78953e51871c2648ae476ddd042c1173834b1dabfdbaeea9f89634831d4456e996b1163e2bc394e3493f0cad1f512e7dbccac4d1b4b1cb206bf90979d912a770f7cc00b30767cc13736f0f191c3559083bda8adc469e654c819922eb588a1c441bbce6abd5fbe723b89a37ecabb67a6bebc99240ee797dc57c7d2eeac9138e50c89f577eeafd984c4292cc9941717af4ee88f38289154e345539ad4d11105d5cd104a4e4c23ce159bd6c47cc988d3281d254f5e2f9d8817710aa74e7e5fe5679229459c740661a262ec2bbc823da09188536e68751795d3bb705b817b001167512a04a1d2ebc80d1a291d3abcbb016130068c27c30c90c046d23844c122a66c070e1d3842906387073e3e7474118617388c17340c71d018f4b5c95cded05a81eb28c4b9424a420419930c5b18ec858df485eba1418873a879b884bfedf7bb00a36d803188e3558ad739a607d010041a5d53d335b659d4f24a25c35dd3c23ee594341047ddd4ff3bf6858006208ec1676573944b1ced610668fce120aca2299926a40bfdfbe16022cfc5d68afeeb210abe00a38be5008d3e1cf2888818692399df8902dff1050e3e1cdbb737d64f68933173434bc78e1c1c46c9dfc32188c4ba4a9d95c22a667a38abc4d0a1ddad46e8d0176084f1022f18a09187c3e9c999b29769c35b37b4ba602f70b4a7c3c34973ecaa6e58a83c2a37b4aaca2a40e30e0765f98216959632c4b6c3f1368b18a1275782fe7538e9caae915b1b5b5b7c40021f1f9eced0a0c3394785b465ba177f39529ecc68cce1f47da696c35936572ec4358a6996c4a13e3fe999299adba183059d5b7c4002957060018d37b880861b90f62252266b56128d36ac6a9242709978293935b541830d4755bf1411cda3b186d3c9ec2b1a26613368d58068abc6d46e6ec92ab4ab2e090d6bd2cca24d230d07ada3d4e476c6447c1ca4d0700c52928e5fafc91454b791038c2aa47186d395fadeb0d1a7b25a98d873940f1a6638e8a493e1d6e1365ee0376c680e1a65385c9b308d6e6eaf5eebc8c161181c34c8709a3fdd2db15e2d47674305ee615c7d41630ca794efd344a869d93bf0f171838618ceb3572fa682fe5dbb6884e1682993349c6ac07094705f59e2e234be708a496edc0daa4e726f34bc70bab9905456ba3e15742f6874e1144b77b55aca9f4908d1e0c249840ddaa382ce328d7d81c32a87ebc8c11bb0f2640ea0b18583482c8b7147fb5c3e2d1cc36f297941b756c37b011a5938ad6e664b0aa51dea7d7c78faf8f094c375d0c0c2c95be6f2a8d39d13b2385e070b72d8d081634b47173876b0400134ae70ce947fe2853319aef2ef1068010d2b1c379dfab30c6a32e9530d6854212b1da28249c280c18e2b182c0c722c0d2a1c4e88916bea972f6aa7318593866fc9192cfd6629d190c2d9e6b2724cbe670c9146148eb71be2afef9daa590b04810614ce6a565a66d4c6d59d9f70ec2f292644ca90acab133091e3424c13ac581ac52464d1482132f5cd2c0909b20de0e802093498709c74c144e8502f3ba5259cb4759d091584947096242bd5b6ef77ed422309c7242126a9ffe5656642c2f1dc54e65fc4348e705c7ff90e899531d030c2e94feab76b5816b9df8d1d3b388c8f0f18ec601a4538087535712b6113060d221c3f833e25359b90d57b08872c979da794dd9ff44b0ae1f499b2e5a5bc662a668370cc5693b61694aaa88006100e7fd73a3e277429a0f183ea455cb60b30c0405dd0f0c14162cbd796a424634a6241179e230c5d1a3d38f9ebb8f68d8c23253776f006ca8d1d1c4681c10e2b3478709cdb782a4296b4201b06d0d8c12108896129a618579aa48393b0d7a0512b7a966a3ba09183c3b598ecffa4b4a34c9663870e0bd0c0c1219574fb8f18a5718363c910534e4c4b25b349c306e7f2f88cc984b616e7f4486b7feacb01868d2bad1cbe19f0a0852ad9d2244630d71892294b51d21bea450e3022f0f1e1450e301e081eb33826533163b8b69452aab238c8beb0cb5dcded7f2c4e3a5fe6c2050f162739952788b8cce315a70ca95c548a76c24bec8af3a974b31ded5ec137ad38f5b668bbac9854dec80a33b2ab5b4c29b666caa426a2c6a99a0db2598f551ce5829d9a9cfda1524615c79141baff5cd60c42f548c5418ceae94abd952435c5000f541c64d658aba4580e1cbc016f018e1d9c819a84c729d4145a8ac3ac2953f54f598d16dbf1050e3036905575a438aec69052d2979a29e85f60a31e055eac0e52c4631407b11a91822c959492bfc0d1054243c3431407db9e0b4b7af408c541f5c9b0c1c4dc28a1f500c571728614798da5ede24f9c523ad994254e902022cf919e38d98f8c7023255aa83c3a71d012b994ae7ab58b1b4e2cc982044ded6d667d32e515559267d34d1c3f35a414db0b516ee2a18993e653e962269d6bf22f28f8a2031f1fe90bced1011878e1050b20f071c3231307d53011d921fc5b528e6c7ce128d04a5f70073ae01e9838d964d92032861e973086528bd958e220b2a72c8bc935a1151c11f8f840c117393e3ed2179cc3a31227a638ffec768848a2521c2f8ed58b687a991191e220839e399529a6d1221ac5c9ce43e5b52aa16e43a238689fec8bbd4271783d15940c22ab061502c5e992a9e0ff2a1acd429f382519245fe8effc8b204f9c841a916b4b7af3fd9d388c7b984c4c7f2b13270ebf212f24c4483ea64d1c47c4eb5811d1ca90268e294d4e535127518232715a1151e269d83d8f3171ca783966263f43762f71b4a041738fd212a738bbccf09bafb4acc45983c7a595d7a04c8512c7be932677bb499cae74879e595efd4c128719affb4c5ac4c4227108faae6f26c59038ecbca68d1be1471c444cfdf33176de3be21c3ba336a36fc471537ec3a4e683117f9fecee52321f8b384accee1337882032c97c28e22c79722693af52a9643e1271cebfbc9479bfa406990f442c97f7f3bb3ec4414d8d6e8590528a6c88b368935af24a55a47021ceb2a9c93784c977318438a4131fb941e8b04c0771d0193c4b8a0471ee125935ce84ac950cc4c9ac92e53d1503888359ed9e58e50f8789a74c248d67c9981f8e299fcd6ac55651a93e9c44b959a48a8bc127860fa711e31354d9997c0fe711d277feeb7e33ae87735f8c14c4fcaa44290f279f57b7602ac2337838c56b3edb0ba9eaea3b1ccc4ca64ac93d34dd3b4e67aec3a92b85fc934b2ea94c87534d783f51f964baca1c4ec2cfef3a8395576ee57034b1294729a12926b5713826b5790dd96f2a4f160ea776dd2483ca1b4e7db14ae34fbe85bc1b4e316d53a269c819f2369c548e8e8dd9fac1862fc9728bd7a1351c6d54cf5e92ee2252c36184e86c49a7f1729c86738c86ea0b115e4c8386f35e9e60a71ec2fa331ccd2dc5fccbeb49d40cc75cdaf1af1a278365389d86889d5a922121ec4cf5fb3886637897deeebbeb2e86d37aa8ef058d1063c270f41ea16fd2453b81e1f0f2b7357e7d9afdc2b172c4f56beebce285536e95bc992ea414b12e9ccc747d9f0c17535e5c385f2e132a279a6549740bc73b5d3135befda185a3ea68a524ae7ab5e747160e1e79d4f2820ad1effcc0c251736d729999a435e7c7150e922cc5d45e7a59c4f96105e747b2a8cb9b2a9c6499d49e31a970dca4f67467ec8c993285d3459f9f778b14cee37aae1333cf4f5138a995da90b56aab86c25956528c29e59e701a37bf52573ae118cefa35954836e1bc2b97296b8895f567c2c94f285d565735ee124ee9db7446d7a86174251cbd7c5316bda64a474938add6ad4a6cbd8d1b241c57f3aec9cba632b24738a697dd58fada4b4c239c529e2431e9ae8b122cc261be34e46c50f91b221c466fd88eb3bedbca100ef25e1b6258fe8e0f8570debf10ee2c262b99201c2404e93a13f3bd0884d38824ddae3784bcfc0f0e37776af684852e7d709468595e2a6308a9ee811db1849c89310f0e9232c6edcb0ece996a463684ffb55407c72034a24b7035b9e7e0f02656367b4bfa18717052e9e2c814732aed3738dac59021638a21371f3638e9643311d4f74eadc5294fcc6d123b5a9c4544927e512bb338ce057929ae85e9cf8a2c8efa95c25c4a95581ce7674cf765cd2d72581c3c94ed099d41dde2af38a99084722fc97f93e48a7345be92db34aa82502b4ef6176398acba0f21569c3389127d4af8d9885671f65149ac6543ac95557138db4b39292615e7b3f81f16648c598a8a93245326612d4d9d75a738ebfe5aec57678a83786b9fdcde6dda97e2f0ba9a14a7546a3efaba228dd0284e39feb29cc688e298f49b9c3d14c710d933fc6a38e92e288e2aaa9b84a845487fe2dc22ab765f7be2f8225faf7a77e2e823a7c2aac8947e4e1cc4e4fd4d1f2392ac6ce2903cd5a2a44b9f234d9cd3fa946748be5a33719ed154693497f8ee30718c16a369a74b9c25690b27532c713653964579055dcd2a71ded95832a88c122711d29252f19fc4f94ff26bd2af501397c431a26592ba47e268deea323985c4b127c5b079841e71f8ae7422a5902a32471c93f2758b575282841b714a0df13de355bf31e2a0627b76e6f089d52ee2605be1f4ae24459022ce75b2af1944b0ec6a22ceea9373ed7c4f45c46135cf282d11a4890e71b8901846c889b6350d719e5c92e6e4a51c3d16e2e0e33b262369cb23210e3935cbb7ac9f58ca20ce25926564aafbee4e10c7d03c27c4b40ac421b8a6ae790588d3bc5b5f89d46726a93f9ceb828e6f08eaf6bf1f0e9b32c577d39a3a4a7d38769a4a9a7b21e5cc879357e4d98f91b8dd1e0eaa4685c83075976a3d1c75b265be8ff270d8cab00e0be3e154ca46bc88255709dfe11846dd89688c1d0ed9943453624252a9a9c331b8bc98480821c5950ea7d0a4c5375b3726e91c4ee2828cb490c4dc53399c648e2f7d79c3ae8e713856beb5f7060ec7f3f4ebd04b4aaee50d874db929e8664a22379c527bd8ad083fb7db704897dd4ddbdc5accd8708825f32b8c848b351c66bd725e4a951b5ac6025ca8e13c793b35894c3b354269385d5bce1422b7617915a0e1b0af22e3c814a2e30c27b998bb52fb8caa6a66386eca5db6e984f6d1ca70d05411c63c432e9dc908b820c3e93662d451758de1a011ebf4655913b113c329467e454a9541c77d26e0220c2711efd4c830290b01176038a4107afec2d1c28855e6897053ab178e71d6d9126335cdb43ab8e8c2314dcdd86c4674158b0b2e9c77337a9814918b2d1ce7c4d9ab67584dd9b8d0c2b1aeab7ebe7b349b908b2c1ccd4a59c85fac6b0862e1386a34a8c9fa0e29e152573856485a7f2ff4c40b3e1756389b8964af419890a2e92a9c4a7a69f650bd32497301175428c932614a5d9223635c4cc1982c68ccda1a31d85c48e1244a69ccd03e4931d91586171480810374e8e00d7ce0e3e3830a5c44e1687fa6f3e4e88d19291cff85021188800e76c1c787377f0ee3020a27b91ad3b9e9453ee114a6d9c4af85cf4afb8b71c2f1cd92886397ded03ac39163476982176080610305396cd81000174c38585cd7917e11308e005c2c217d81c3055fb060005c28c132212f7e2e8b48471761b017555c24a11089266a19c943c48694541ae9e0343bbc782e9070343bb55e2a92fb8fb68b2e7058e0e3c3065c1ce1a06e7721a410612dcd5a1fe0c208a7ed2da979dbbaad4c1d4538a630d5a99753229c376dc2f56cc85de22326022e8670780ba1c557cb4226025c08e1206a74df2a5782708822f4895429cc84481d2e80704cb5144b33f76b227c43eb07795e0a32bd6c7ab9f0c1c1c2b85f87c6dd51312e7a7032a5e36dded75ee560efa251025cf0e01462747dc92664cdec5cece064ee726d4145d120ee0dad924a012e74700abae9325287c6968b1c9c44c492efd6f80d2d1c1c93a6dfb4b10bba5774432bd16e709cec7f5fca2b9c88140abec081012e6c708ee46df12c555003356a71dad9202ec9976f68edef6001295a9c6b2e8993a675ebc0e14599408d591c5b4dc62eb2b23865a850262aff992e128b43cc90cf2f9c960465c1e2147287b6049949956c947bc541b89cf286d6590d571c27a8d93ca63795deb4e2203e2d47fd66cef8ff8696b26163870e306ed8f82287819160e0b011868e1aac38e82d19b4c69890a2e53556710ceaa569929d570402355471d0da3bff8abb462a8e6ef25fb4de5664773b500315a70c192f29f447cbf2539c6df5d24cb5925caaa6288b440b2e6e1adb5542f0d1963566cabe612ac52988ae2c49547643cb465ea0010ed0c0052490c949e19d78939ee1ed28ce1ac247a828db063544714ab2b277a68a23d48a6a23b986e25cf99298d11dc20146086c941735407192b9620c4a84bb58658d4f1cb3b24878bb78e25c21fa2cdb52cb54df89a3e435cd9c675ebf1f270ed7a55c6496c6487a13a7f44c72e4421e5d42d3c4b9f24991a849d34ab66a64e2287af49bd037223ef23ca88189939bd6496fdd2193f57a831a9738c6661251ba82b486250e21e4cd15e4d9e86b5e89c35529517a3c4d5aac3d87da28d691dab1063528715c3791446d526908f3240e51cf4dc6914b374a2a8963856ecc7e0dd1312d12a8755599ba68994c21fdb24c522f41e26c2ac7e4ebb29032ff6074e1818f0f1d3bc0e842c7eaa0c6230e1e9af596a2dd9a501d7136954f29d5a4469cda9258516357a73ac30892a90617cf20c94c4bd42a9a12f9f32de2344135e2c90d49546baa65a8a188634dc8593b264cc4b1d4cbc934f3352b39220ec2260599fcd721b6048c6ba864ed2563f2602c140945e270301c0a914e57b3130800183c200d854291503051855d1f1480025f341e38322c1a201c141016148787a160200c060943613018100803426170301c5a8866241f7c84c2eff0ec64423b28a11144a2eb0721c0ecd1103d5a23dec3cbe72d3d5e8ee861ed616b3a03c074a7fa908f61bd21fd0cfb6f941d155434ab9d65b1ac88b1c16ab6429e82cae3bc5a35c694ae55334c495a5eb22194f1398793924c676b3b4853f5cd7dc9ddf05b04b0f96ea8a4a79802a305b522ec12e47b464611e8e6bf719bbf1a7c33bc1d0d65822b53a6fd5d80ef250332b0ef1dbfcbb69e66d2871476122162a5f3d243ac29cac672b0c070399b9859cce10cd63d33fe12bde17f2ada58f363f52748ec7223009fc688349b889476249ef41fb8e17ca520308e14678c19e6c377500547ee4967928bdc05f2d1b1db8787f54ec0aadfd063f29f4af3e6c405635350154b923b56bae8a7a75d635647a339111f5d47ece83bf0c7bc81985a8c4f42807c2449a334519a92e81e9ef879adeb8166cb51a9a70dcd69ed0cbebbf43395fe86a77cd522f83130d68dcc64edf053de009415af9776fc1ca1a3ea501db2396c54a9fa62cf0959a2a1c8cb3467e3aa0616111bf1c181f568f51c834f294116d88c7ad8d7ba38446ae5b1cf42e5c9e4734358abb7e62012833ecb1b8bc867e2744afa03beb16a467c2f2b4b94f5a929a6517f26ea1499cb78847077bf7d0abcea7f2a12957da99b74852b8f3b1356d75a3f72a5f19a448cd244bbc1a14412d61366f0244007aba70ccf0291af8ade0edc47c0acfb5a75bb5972b87f22fe9472d47b557817915e7c4285cf39af758249c5b0f704de8477f25ebdf4f2cc3cba0ed17576aafda148125e4d99c27beeebe04a9fabb4ed62c279a6d9518e42b4905ca376152e8b5fdb52865702d6a8c75955ad1921f8657da01d5a3c44393ece33039a5dddc9857237da40e71a6cc85b847b9c4d0f82d130f2769105cabc4677fb2b2f27eef20e7a6a0e685c2441919fc3c9987b0729b0f6f1d936aede758c887128fb08029c8df7de2bac2821ef1ce0c2d6c60b23df0941e60989e5f74e794d4d7798a58697c57db37d7da48a0090057969a6ab6af63e601faf5e9db67e1546902de75cc885f87e4f2f3c1b25880b522edcd7c539a6941e7eaeaf85a9a025ed8f9343f17223e137703b4f30fb7fc713a3e6d46ee894cdc6767ff5e3dd393c06e6e2413db0c3b5f4a3ffe3f0b597891f8b9bf2a9480940e55a1d369ec42d0a7b6032b0646021f9052aaecfeb24f02fd4016b8d39a659181f91ea209251f363468fc81745c9d4abfd88c3e526cbdae55279c722f569cd29d68bd0493ec4775e9ef617ab58df8d2c6a0492833ccd4c84b9d0649106561bfda2d0d6f93bf88c4e6e5a5388f49d872ae609f71941b73974d339bef57a8d5a81cdb901b480522b235299af35574ff966fb535ab2d817ba7e88b8bc2dc189148675b5782c0dbb1b0181f15e3798665dd0a7c398f707670818463cc2ed4000364eba0daca5c93774d6a78024637d797f2d5262d684801a8b3762202933ac28b2376e3c30c97589b40e9f3360466f429ebe6fbd9ff4f8685d05b0ef32122ba2098f2112cf79b79ef48be3d83f0d66e125d398112c32706584d8a90b9d7d0b871f2902445714ede5f9aca26460d3efe60944d1d86c346a9d306910965a82227a149c79b1cda576d540bf47b64800d80abeaef003928baffdd2ddc3c41c35257e63264ea125a6098b0998e17a169176734f6bc2936c215512f863b71391f9561e4d8d4013e9c662b338196bd17a90736dc2704ec2fad42b72551dc08cddde5c4897807c981b164874c42aca5643deecb62622205bd02b895e9a6c4413552a4b93f089621a42f5cc7b106b333e097e094a741463a18353e536b496132b281de908013f8d100fc39941498353dd822a849eeb2bb617d1c2a68e3ad0b05ccf453c9056af1746d7f0405f00e7581b3ed76687f5aae263dfc2f99125baba8e5ba99037c77ad695e47d9b41f83f3e9db3adb6eca8e691636374bc8d7ee34018e6a06ac97ded5e939a62ccdc06d32e8a12ca87dda4e8c6a626292c5aacca35687aa62a021bd81db77a8d2767ae38b1366656b014b0362a2b2d82ee39d17c049a10d6b93b3bd536b89e98fb4ddc027468bfc371781b32d6b59c86114c517abcafd9a879fe2a48ffc2302fa9d3c393e2eef6672c252e73c380e7143b97972b22977819f9911d7e414fffa8570c458161ed7ab40210ddd4211da9ba46740fed8057f01d6032617e759566b44dd27a6ccc7946a15f8ff041fba785cda49e8badee9dfddc1800d5f0facb0e2a700614c67b268132147619c224a6b0049ef17c2d2b70821366b98feea22767e9e5cfad52420bb28e28cbd0c69acedf324cc2ce08af0926c1301bf15e07e0d8f25ad802e811f47f1d666018a71918b8448b61e6cdb3705ab5d703cda0b6efd894137f3296771469ca92ff8643e3657280ce3dd26d07a1884995b56b8b37054fb8ef3001e5d98f9349347d781c8062a711a896c7b9021a753b29341d24fd87aa7a2ad4c75c8aad36c870e4fb2f28152d5f912b4907e73cd9fd9ea76aca441a597d305a14a8c60e35d825627abfb8837d4b94e6c5f4004bd99efb3acc89965e93d4c4d454b9335b0b88039869f79c490f45f0098e208a08b83efd87182b54acfee3c01f0cf81e0f3a916bc2ab7dfdd840e1f111e3852a2ab5d8765225a04690d565e5357dc581856b0aabe5bee10f0af4e709d5f6ce9af4192059e6d0ddefd724962f4a411cddfe9ccb2faf16d3825aa92a115fad1805d9c9bd06bd3e71c1eb1ff2cd2b638385a741850ba8e19c4ca98607ba1c83a0c7640158a1179e6138fdb1243f81a8a8fb3f00f00cf4391058c7a170a604a6b021335c38ed50d762eb07952ed60649b836ebf808c04973051e5a45cce2aca534cb437040a8fc81d4ce8e74d266a4970cccea2415db8a35a026ae283dff46171e5ce889509a68975211e75990782bbdd0c53bc20108eac21a288374a9960ae502289112554315deaa357b50fbac9bae2a3aeecba95730ccd6e075e3ec9c9e33cb9f6a41321c8d3fe69c8d726fbc0cbd66594265842bd650f521ef71c8051a8c104e418860812b1f0b0e97ab21b092153b7c4e8b7711e6aaf0786e50cab891a34983b006be967e4ef8338e1e35671baa1a8f4bcd52b75816062c7a05b12dee3de11ccb9f126e9a90125c030b125bb4ab207476e4c9f284617ff3d66d33b7fe555d701e6bf564416825312c9fd8beebc76534bdb1f313325d0fef0eda90a4df39c967457b39200e45cba8544db54836db93c0a3a9d20602f398ad9ea0ce64c87f90f304dc3bff433457e8e4db88e3004fecd86361e9768402cdfe4e40c9addd2cdca954e6273b4d51446ec36a3a85491c96b86c5a2933ca8b9bf08afa312f28da2ebce200f96bd52e255e6102616c9774538e602f7dc1dc7e3825737cf5a5bbb623af0694c3fe16e13725ff1852fba2f5239f0184e86d3e8af2296765b679559228322d08420c4839ac303d854183bbd5a9f0a9fe33b21accf67fba45d1626718d37ac71353a151b867e094e4629955b8143290b353554ae83fdf77ebbaaf5a0b8d785beec8a80b32d6d01864ae93e576afe3c0f89764220c047903a89ca496f94ce87d9780c6aa571219ef2ec9b8683c3a7eede506242e8a9e1e68816d2d409368d353c42f6d4c2bb22f338b7e4174c41b4925ba944920b43fa5d74f5042ae51ff2b3f48f6d27def27e77c854199b2df4948f421fa669674e235f3acce67d64e3d240663112113dea6c911fb91531719b0a74706f6c1516e8e8446588bb801afecca5cb990ca94fe16485978592ac7fdfecd02cb5c603b453cca1418f208a452164ff58af46af3dfd7d930b74cb1100840674efa723e12d5990f016c77c18510cc300837492ea0b1fa0a56694e771f2f839ecbdf4a1037c3a3ca31924c8116952d7a9a3440b3500c821b42064b58c59d29297b75ffd6ba51356394dc1ccd169d03735acac6c32c92b472521928058079ff2bbc8fdab9284df7fdcea340d0506ed29cf3b91bc2893d51a767056bee882d75af6b54c65a365ccc270f291cc198b3eb1c20245a4a9694cfc8a20b771bced88d760a5610f81d65126e7f67a727c194b7c9af2f36a42d289df9e7ea8f1a0a8b30b45a139ecd46678e90bdfb39890869ba4dec0d73dab56417446355253a519dab3e42c994ae3d9620572652f045da2cf63779d154459e2771cd92fde01eba89bfe480537d652d4ec9ae16b9c962b6e246694806ca8fb144776d3a768995f4e8342c4130cb727bd14a1fcc9567040fe935acc27171103e9f14eaa163a1e29d573244c2139623fa50f29d1e41119cd90a83703aa0b3493b9400b652eb57a056bd3885470e0841a151e823ef03d471ece7a8c9bdfde07f3fa3dc01af93d96f70321b230eb8dda158c00055b1b3855dcf38fd9dbf2a17f0ae34a7285baa2231af779f06ea938428fe67bf638fa81db58ce998b563523920d86dd6cf6e1a0d85d2fe89755eb1444dc7265f410ec6ed49c5c8b290147e95801f912da0c80988263237119f1261466332193b4923e9d39c65ce259b0dd3ca0cf5d0dd2fbf04e7ee2eb70413e568151bdde4286758da7c215a8d5b8854c5fec9139ca609933c9a14770c74254aa1c82c0713680b562cfaee8bf47453240a2b452d5427df7df7f8dcef90263d9015d931539b0906508a7f00c152fb0e101c3c88054d505f0bd1569a638959704051bd3ecf8140909ca8ed2eef3430b62044c09239308c007844a1a051577546c517bfa7554d35faaaa0ed57214a1aee3952fc95338e8dd3aa3be78b393340b97560424448666cf3786100099ea97ab12601cc424bc21d23a0e1c880f2e618073acb7ea90a1a1e17897bded3caea5db8f341f7fd040fdbb9d31dd138e63a89f422b6fbbebf5b56c993cd3887a00b71b4dee328665246151e4eaf0ddd0067c28b565dad781bbb4c032013d6252e397110e56ba275e045de26c5d6e1e30a0606461cd61255ce45cecd9b589c056288cf181a4b3e015a295252241aa00efc845562a5cd6cdccb84651f9a1d2f91e7fc2007d4b3170901e01842274036b28d6f61095572664e9521310ab1e1045c0d60d47316442802efbd7864c5cf5dfd2c5f51c4b7027443763e1530b4b54a71d94263f020f6dacba42ce63e1c043baeb4e005fd6d81a95261b35deb0ddb572912a3e8504e085769eabbd8e4078738152a73a83643911d208afac0474a23b5611dff452e839180c6480e7e844330d604510617dcba121c325c6c1a923238bafa22027c3101bf7640e49c462c92b99e65c14eba75509795fd5ec03da3251c3df0a644a1de923fbe4ec060448abb300eda72dddd5f922f334aaf2e96a133e71d9116773d187608c45407bea714937913e3593ee116bf71bc7031edf7c97546834d5a24e5872379ac69b414f55955ad01acf1ebf1d20b4bb330e2ffa6f0c3b8fdde2ed058b947011ee120c93f7a833e9f2e55899f685b8d13fd8fb5de4a4cab1cf315408f14365f301340633450f073a50888008ab5d5e99a0f840bcc4a10dabe17ac187eef4b28966644032589716fad1f9a8b45c479d15a01ed9f2fa4c6485b5a6c310e846cbf7d38c4871845655b437dfa8b48e918326e7710c8d2b37b104391383d5b33c890a54b81e42f78c961afb3b6d2d2e2c88cc73937bd1e7250736c480ca1dd9162e426659e01d4405861aeb14adf80d1f08021e5c446299fbe01903cb653e490e32dc830be8a0229f6f620ffcc38c5b9f5cd827c2956065cae0b1bfd6705d254be5e92be8ef6eca8b7a40a14cb74914c23e825afe02ab2aa6633941d2a6a0c34f3f4a68c054648eaebf959dac7a1a0837540b26d663920c7ed7bc6dba65e2c9460554fd8455ffc3d20c3094dc31f7614280c84785de08deb070c3b4412e3ad69e5c5e655e73607d4b20639e327622a7e81f5c7db1c7d79fdb689ea88a2683b3f7f202bfab6da84c251f23aa4a057228a306e1abb45baf331b1da5d4daf933addaa341dfc21c178867835b150809e356ebdc12feeda19143864b114dc118647ce2f9239d5d0c48716d60f7a8a7c25089671315dc6f3a30aeff73fb7377c452f84936583a6b596db4e83987c89118e4e00c7bc950bc3b2f9906111a1782961dc7c602c05f35bf0ca9600b872c4057b42ff345d14e7949441acc8331e1545901c3f9eb1e804f4f4c25f826cd0822ade2a181a7446a5c014a7129ba5804a20fb5a9bb284f1608d6e97c868fbe1fd1704f71932bc184772ed370a281b76736a32ac18c7a10d4ff9451506194e610b2fab4d98999b65545d18d77020d92119a70c396342ba3f6dbdf191e9a7fba52b37c3af5c1fc4089a7b25949b76b64614a590f2a4f4baf2a01a97333018129e756e9b94420f229d1d5af1ce66719b82fdca9692a4b418374637a3fcc9dceec6b0f16e710134974013f3588046499226592318b7d0aa1548eb8fba12651ad20aab08ffdc71546c5006fa1170e6049e278654e92701150de35b126c9461187bb5e61b0ea478ea4187a23e20c2718db5befb746d11ca770fe0e895ed0c59c90b0cec53ae63f298f602b0cf212ef4bc999928d962df608e02406986f67b75bcd9bd5d5c8d7c4204d99f01e96eb0bdb4e11a9d1bf2ba17142bc5d79ffb31b4ab4144e30011b02781004b9a3a3b04dcc7ac9514ac054dda53fa2516a4e16af3ed415eaac51dad501841208f1f61e44128b681077143c408406dc806f8cc0830d75f0b8b88d8370e99889fc63183e7ae7f028a53e61d0eb71f0c66d34c8a900459c35e6359743c81ac04bd5bbcc25da641d29231be02d6caedf7a56c72b853af9c4d6d553a8e71ab86c5163ac4ccadc3a824f99e59da835987001c9d3c546b9f2df19a391a6c2621e13f3ec45577325b97ab40faa372ef90de2e0a8ceb5fe0065a48fcf4feac80751c5409d9c9bdd675eb16d4f33ffc0c277247e43291db22476b24c58c1ec79ba946657d0a588e379c3a3b7d0cdf010267969a94451164b309f128e5e06a16785373f34cb6a24993696c8fcaa54e20fa4ecd86c4bf0edb175592b9fe2292c12c4b6da3c1cb5270ed3db81185d8c011b7dfb787245071ee3335017e58aab235074ad6c478265ee887d138cb90dd6b5676a5cc6123e142dbbf5dd3469665ac8e7678eaf14168e5e935bc1b84d432fd95db8d9aad5fe6d94a02281a6a846459a2096de316a352b87268e11447b1431d543959d5cc68fe3ae8a478e79f403a88ce0290b61523754c48ec655bd1c4c9baded2c4068560379945db3412db93f88bab8a7630a3ecddae773f6f2aab5b7f740834c87eef7906a8a79eeabda44b7397522e1d5dc874219bdd6c77b4fec87addaf76ef89f79c6de4adb9afd7697bcf67ba0b3954eafabf775f7b93518cfa7ba7e87db3f715ef3bddb4a07bee210a192de1055e789bd7adcf3ee850b9b2b7fe55ff18057cf7fc30807689eeee81de73b651c8de79c998dd6cce573933c5eb57f7adbb751af3c973f95baf2dd36012bbde8c72f73ce30efdc7a1eb60ed2e3f475ea1a8bbf77921d7455c7597e3f99dbae0fbc92e7fbd68e8adf3d3135655de7d6d26507783f9cdb837c7bd7b7971db05a7bc757383c940e3d2cd9689932d237969b73be778ebb0078b2619bcf5997c72d52202f1984289cd0793db96fb363ba551dc29e517dfaa9b377e5246b9bf253ea6f92ad6952540c0e315bb12eedb28816b82e6af93d2861611d0b690a38456462f0f285168c4dea8c08ce48972e9d06516780514c6440532cc071a88c409e748ba05110118ab48a4a46bf353a9592a028919e2f9cb69662c12ecb2056484bfcfcce2229b51cd526a583692890642a69646ab723aa10d6276abbd627cf4905d4e696b4abea0c70606fd73c69c671283fe6d673033dc172cf7cb88642ffa2f96eeb117a43157600f8fac1c6c28b4541e391e9903ff0ac0c45d51e9476b46e84f898b676d98b048e4b7b771c0f6e3b2e9769b1fae9c2077d75d2bcc68d3e99a4285593548235e66d1d3fd5a780d2a6295e1b126436128fa1a7722a1bf7d818b97ce5010351622ec97d624d2d4683640db7cf1c26f6677568102612a55dcf29402d4220cac59b484293a887e6f330eb678b7be1ea3b020187074d9d2f4153fba25cbe31f4de7340ff42d7002df94f554804aa76972fa211917c1ea83c5209603540a3e958c9f0421c93ea38c9acd75d0404984a1a08c4237837ee55414ad32beb0a23231fb747a9750c23322cae934ebed9054e1397524baf52e08579a97b7c2d49c1ad4f79604eec474cabb9a9a40590f46222e286b91f1ab83e78c435bed7092330a8fa59f2f005b35b290d1e3ca063faefb0fe963d6880a62ba3c3c7c20be098adc832c2905241be0e171c2e2d3f16322556d1ea338c380fa3248c8d1d5bb749e729af61d9edfdc5b28a0b72b42aba9c642dfa7c2d81026f6ab5c165e4c3284157badb29a79216160259975e95429f6419835bce42f94a09172dec802a762cb6aa16074cd65c2ecd76de1f7ece4ebcf1156ee45877e2b202d98a205d5da8e0f55e80c00d8914fd8fc6cddfb9b175a23ac86ac455c3e40342364a989d4f00f3187d807625b7b7e0f24412037c1f03722d898a84e122f51d232036c99818a2262cd06286a1c032b53522133dd18e2dc02fc8412a3f7e9f93c29f409e15e6b283fba375101f787cf7e6822ae0d0720b74fb65f9a2a2fe24dec10d6705a66176ded08f5568e9de4486bbf63f1136382712130e6363dfcba42d5dd9e8241d8f17111d36e810af64c8591ab900492af40c6b9fecbc2a6816240245780d5047305ae7720d324c40b841867ac7f00bb2b2046c05301896840805e5ac051294cacc6d2a0609f59157ebbb466c12402790219080cb9810c36410440e320c34f18151bc499d7488311db07e2330e79126aceba0637c246a8995034bd0bdb04c41029a0e712c273e593cd1bbad3be1509eef50e3963eed6145a048a431d81b2126d3d388d542d3f28501b03635862a9c0073033b3df82fd2e35a27c568015017a2832fd4ea16b45451f001eeda468d2345302a01780c70090f079957dfcee24cc227078008804195b0eecbd2343275c322e9c27ab4001a6480b2fd6e22eade6d43e524974ad879815c81c1669ec8120c8845e20e292f4b5c0adc5482020593d4c70d883d3332b266a5605ee311e02ee93023c0331df5ce83058f172ded501fb7e18491459862d39c35c24db84f403155bc83297435dbbb73e484d60d217889a11e44aaa04fa98f655b6756a0a6d03d207de456beef207bfbd21a514623337bf60e6176402bb99f88c43a615c799070938c188815efe7deda0850a78fdf64a351e0554ab87ab759e7f8e6f27b90ded574efa3d1ea90cdcbec399d4532bf76ab64780b90fd0c1af93b0069a9110dbb66f15f2a2518be7af7ed5f10ef4469477ebaacfd87b37b21d28aaafa2c801ad0530a329a33e1381013d6b9e2cdef12d39b3e64b51ba83693a9af53ec5e1ed3eb53a71546c3e80124dc738a2b8a715db3c423f012ff4f5e8a2951b86007bf4d4b73bff7a087ab32d54eb03fc3a68ca44397473f11541ec37a9af109eda4f9a01fa2b80eb7544231b9cd4ee71541c69d26533e6b1466f31e49ce02856994c8c5a37ddac731e62697fc040690d2330d8381a3570646c6a58828d45df82d446168d3e6a7cd3c08f1b47ad6873a796c6933684d916abd69a931f9a07615bbdfcbe46596f94a0cf01e26a7224684ecf43fa3c0256fdaf36b62776421222a2a8887e686bf14e45a0884f82756636690c08d5c0425ccb1951ff14d111e5b08f8580a9807ab3b376ee976536035c1c449d1100a7f376908ae770031e97d63a17a07d2c1115d3367065b3219dbda507fa01acfd636fffe7e95488db97baca644da68d57fb34014821a1eb950c8fc06eedafe4c79fc103c46e6e1e2cc99a046e7fac59241294eee21ef90657c4c1b10174957afc84b67608684820d44480e08c970dc8d35446ecd070fbeb06d6356efe870021f5ea5f2706c2b25b845698574bed4ba3bca0375aab165434129df7ebbe81145bec1499dabc5d8c172636645670405f14a3dea19875fbda6ab6d81e2901b9e486271e86a148af662ac35e06eb64c8d9c3901c1acac0d3fc3c5e2c9049ca3b10602c559fa41c06224178c28e41598b8637c36423268fda68c3dda708d14e24d0555dec1818ae61ec4847c73d0dc438ed3340451719ca4d6704a8d1aa5e0e0e8f87ce6be0a4cee000fe55667ac860df5e9efd76d16761240db06961e7e0799e81b2de4c59fbf84448690b9bc900b0c1243449a476a0e4158bd5fe1b325c11e8821cd5cf2bec8c23044256d89c01fa0fdc4e52e27606357cbd7a437178dd7882a1449dc449a1d2e4bc6dd0eb6a498914090d486c931a81204a1f3813f6058806d490ecd43f55584a23db77a1696b0eb08d3080baa8e66d1969ca2bc931231d963f45a7678804041a3776c8311128c765dc78592bcfba6556d4343c99adda27c82229fd111a674b2d3f6574a826a7e756ae69ef9e6c40dcab09e6ef25edf419fc8fe486e5429237c262c042636c595af90d461c10525793afcc4542c2d5aaff30b4b333abc17af9c54adb719a69d86d23d0ca6ea278495d8bd22f3bccaaea92c6e21306eac0f5f6c597d446686aeec1d81e9d585517647b6260a88a9cf9554ac5e50f831076a5080ab9b9b08c68b8641409e6e31fc6ab585270472e783f1bc3ad21c84f226ab62da11656c81431fa2301d1a8c39f58fa605d7a5a04fe4f5885b3c373de38f89cf28759843af086598354bb7511e797ecd2b0d01a429dc002fc7d8af8c1b24d6148b0b4798009556f737cc1eba953686f00f03018a158e165579b56d91804430414363c03d482dcc07931c86fe69dddd6e853df32f92691116be82f4821b651467e055776125acbc5310938aa5865ad934686d853c3996dfdc5823c9dec492d3b8dbac1ae070b3d9d9d284fba663877d9184033007280e350ea99b937b4b318e223b044887c9b6225c68ce1966bb6131b928f8b226e452931233ca245e7c676a4a6ca1936fa4fa16bbfb7b4d3c307603f2691eb5930e06b5236be99466fc070c4d240738cbd450a48aa111c5fca79391d4d8b31e98f1f2083851d3bf0036d78fec70b38a2882a81d30336055ba852aff49431b634f0d210e7002a3d702e5f849eb44000c58705371c66a95b0a70ae0b5b31a5c0fd7ebe83ac199bf67858b4b1ac381c9dfd27dafce7f929bd694ae78c8efbd2892075906f0addd62b78e62114889016b089bfa0155f0828986df616b4bd86803228cbe51960bf4f7760543db92a2c0627ae27e1167e2e7e3fd116f1fbf722267499e7927a1d3d33c25d38ffb1166140e70a998c0e2a4d5c2ec2eafef651f277c8c1ab45f88df32b52e4be8d7f58b229c4ae110fdc864aa1ed056e99e1b0f2a62e085c6b64480de3077c00dcd7876463ae8d1fdf3099679fe5138d4f93b533832c45865f14f9fa7236cb40211607fdb0d791f615590406a0db0da74201c2f73a07a008058c9eef73be86e08585d84681518902379b7dde757889c453772f034ba67f51dee09527b4ac26dd8410f95632f2345eb36999ecbb5cad61e2bfa4457f9794da2112eeecf5fe92ea3d5ea380db83f7fc78ea7867dae0620dc29848c0a3bc0ab0abc0f055091b2ba85c016c5be5bf35018848becf2573184dee8e6556e23e348de62237ad8e9193ff5aa0d71a11c07f7dba8479162db8bb014da86cba9c20f3b292f848304ca368a74f527bcd3eb8f3be0702128d4bef893c4bdad60590a253c1ba08f27ad484b2a118d2d0781ecfbd1e0f60d05a6c30f6c79675cca56f85f71f11af3f347e9a749ea223d8558609010e492ddfeaa5751ece6d13960b438af626bc65800a5cbd80df180b48bdcd36c0939faa7bba32a5f78debaa804eb51fde9719535696c536d146f1b0fd0a83849c3211819828294a8c6de87a2ac1482cccb9e33525d0cca03f58c5cccf49ab7bee93e9837e114b4c45c825d15a52e713c7b61467339b412b5a5f8c5086068e04c99d07142f2983a2560fc9062557a5557f3aaf6c625d13b266bd2fa3fbfec0ae91535fb3ef314ef3ae94b5879dba62ca75e3df25c1c54a7e64e55d7f5d86885e660700fe300b26139af2506f811186971a409b8f23155e11db1689a07311c9b9622a03fbf4746b028958eaa07511259ece4c5af0604db9f3c1c4e948cf815b67d0d5a3335d66aecc9354c0f7d93ba5b6999b3c7d7d076d910890a4e45aedc1ecb0526bc7338d6922c7733abc188b1e0fae993354a161c9ef3deb291b16faa6c34011cf68470657d4b0b85b5172dee220345808c707398c72228819d6f089d6e344bae2dacb58b259350b387bc340346d2f25d40c68f3ccd6c9959685b8233ca830bb5a748156cdc223204e893aa501cfdac12cdb9d7b7ca024f090c17b7aa9e8def893f5353e29a8951b8656fb9a4691327277eacf72d962432a3153baa902188bdb781bd77916790f9f95f56730360d49bb4827f7f1f0d372828faca220680c7477a51f6637f5a1d527303120ff059ac69c50df845284b800c175b44bc2267394236d12c71d15d9b99824f16db21d0abf4adcd059aa56f695fc9d2b3d0cc16898a1ec995b7bc95744de899bea46a530ef74fe2be8f8a2ef4b676ebc142320635f0054e83388facb718bc6400081b2555ea21ea4a3b4840850ec87d6990d3f25b9c65252f1578bc32a8d275de2f8411dd9aadaaed8917777f619c70c1ce020514b0790e0261965627dd5cfd91443fbc1f9b5086bec0d1b55c36c92852e242eb94b91c35e0d52e352acfea2cc1c963d971a28f13cb74707ebc67de170348dae1c8c6fb684bb8ff71daf9d3cd8f67bb943aaaf619a77122e36a92170d9c69db6894e77d0d1a75ae3ea97eba8b13432f7f490b2e4585b0f5ade525ca1503f884941f0a64e2e535c3199ecf55ff3e98f4f1bc040d3ad83d3849c26fc99745b768d9601f0bdbdb2e14024fa9d48883c5f622982d786ae529b153bc6dbddc71ace20f32802a5d50432e0e930d9fbafecdd32814c8990b7490a0669030f1b15dae5d8ee48378d4293d1885d6b53c2454df62f9271ee5d7269cc03ea6de473cb1f8285624708b31db90d3b158b746d07c8287bcaf83608ebbf02aa69fbd580b40684530f728d60bb29b1439784a689a8984926dd83f7b6f2c732213141290009e1ecab0b561be77e5380bd91d1def5689a8f07c2a77e6304ce7e0d0c0cd01fee5e7573c4fc44873ac9d713a0f9b4a622bc1f401f280c68061eba246ae36df501da68bb893740b2a4d001ffffffffffe7aab42bad5b3c631b21b3b5d6ec27c4ca9424195354d8c814c894524a29a5d4cfca0adb7700e77c3c1e9b2a1b8808950850089757527199d2fc6083c3665e2b73a2969a6e16be528e1d15fae2e3847a43f268dbb10b8dcf61350cca0d493948bfdff1b90d7a6a43b278c9f7759d6ce0e53e3dc6bee5347ee3d246a99bb8d69078a566f4e9394b0d49299e129929b4f4c44683060d1a34685c11a1ce4892add00c9d73a74c7d1a92339df020730e7d4113f21f3830bb43a121b931e7929933982ba4d7a63795e7ce0f36365d429921f954fbe5d0a05286e4d3a4bbd5bfd62f32b026b22a5b593ededa826a955efb48139df3a0c690983e8a96a711d13a16312446cfd5e616aa30247e92bdda5e391892f6673e9ad0b9fa426d5a66d6621662f369b6a7832aef6859657921c17f56dfe3b8bca9be0b899bad74dad0dba5c6e44292fc91f1123235c718de426298bbd09e23d542924a9f2bcd2aff779b9585a4cebba2c173d5721e61214989cbe952e62b24c8278b314ab6e27bb566941512e7af3e83d2a43eeeaf42c2e9eb70424ec8a85385fc480868d0d0616e14159253aad77d2da12ff27a0ac9592c960eb1c9a35dffc19663c4a314a1a4808c29ede6514763e03038781c1e213950061585e4a03166325da5d27eae06654692ba67aa91e5390585a46c2163a6b0750368233978fca29e90dcf1a367fbd79c53d4cb09c91ff437c7f77aff7a4d48f88d9a57d34427614a4c4852399a6ecc714ff65a4a2d812f7bab96d96e8bbf9576d49dbe141ba584648da2755dd4e2728c062a098927c3f78b4a5b27db2f242495c5919db74d8990fd83cd6324078f236d98161c24ea084949e63159aaa7367fe346080c176584041fa55d2b07f931a8a543c78e321264742d254aa83de47190c38687e3004977af638c153ce08b6256454850f94574f21c2223d1e2898f9bfe144f4d84242d2ab47243654ed1219c74d4ee4a0849498dd9c959523af91f84a451b12aa29dfd6013238cde4b1410129478f58b50a283db671749213ed37c7ccc079b18617824f3364600824b440d5de829a4c7ca59f65c24c9a4aa425b4c3fd81e0c7f3d5c24e9ceb6b143556e91e82b9bf3c4ba83d42ac4e8f0b0b345f27b5ed3ea26675632b548eedaa02b8976eb18f1834336d5a045e225cd3f71f97ea5994592baf34b39d1d8303f22e211868d1d470d0c1c20e5de73e0485924f7fd5bfe36a5582468cf98637edcee9843b0484cd2c3e68effbc226194bcab7429d4c94ed7704572c9f9aa5125e3a3bd1aad48d81ad1b33dcf146ab02249481bd9f6d99edaf31b7c18e83fbea855247e786dd97519cb36055b5491e82977c9950bed8b5fa4462a92ce3f2f6cbe995ebe5091a4f4bdedeaf767daa807354e919c27df9dcfe2354c61969395332dc58611428d5224ac89dd1483debbfb9cdc410d522475b25c7bd828923278ee4b7add57acc72b354491f81d73174bb58572b7f3c60e1c28831aa148128b215484878e392aa048d864aff51f4f5f7bfa13499e4f37e975fedd6d9e48ca233b66f24aba64b9353a9158ea3f07e9314877b33891a02fabe7206338dd2b6ee2a419454666659a48fc644ab6883fb9e9af1a99487c4bffb992c144925fccbc3aa66b44cf352e911c63ea78bf7c19f2a4352cd1ff67d754c21c449f5e9b72d4a044255b2909ef3a4fba418d4924af8f6c7f93ad86245a714b8d19ad0a5a565a9e1fbff3c1e61c468d4824cc86ce1746a58f592d4824a936f92a5e2148c8880a6a3c22c98407d36de9972caf1a8e488edf95af13a9e399ff1a8d4852a952d8cd69d13ab31a8c48d256593a3a78c0a8b1885d65b64b6ed63bc52a687d8eeb4abfd4cf853c1860e4b071351491d06b16ea44693ed870809cb5441447e6821a8848121eb6263a76dab45ce3108971bd5293990c919c72e7a01d57a355f9350a917c99dd6a9dacd598d586d4dd9041d5474d0d422449b54d5ab6b4c524ea1a8348ca1bf45fba70b24d481049d1e237b86771b994148804d55963e8cbdfb09b06c41d4efba9b01e18d4f8439257251395efeade5d3f246ae79329b4bb3e24a750a9e1e1a4a6acac061f1a519953b19bbbaf9c5ea649bc8cf2576d1b6bec2129c76388e5ee9cd543828bfeb354ed1fdc948744b1e071a13b7848f473bb0aaab943d267afbc98f46366da0e8995b23988d1f97490eb90703eba925241f54a7448d2a6527c7d4ee9f639245d09df146795c3a283655afbe390306a7a43eb4c8434e1909c4ba84d398dd21bbc0eb1dc905c613ac520e53dc76d48fcd6b05c514cf98bd8603a8f9e35242921be66c4a386446f2fb5d63971727346f26832d9bd61c9559386c498fb3b9f4c4143c2e820832cd3154dcd674858abca49b63543927fc8e76bb40c897179a9a74cdbe9c9909452126f99935c7fc690a0f42775aa366a53c490a46263fae4fc3024a8ff941edc7c30246d4a32698f135e22fc2f24bc08657994aad1bbbd171264a6f8bbec8e2fb2ef4292ae9c36e5545294d0732149fcf3eef674d5ee5b484aa1d36e89d80d15a4850475fbd9bf948504fb28f2fdd288d8b19024835c5163c24575e60a8999274bace573b08b159252b8fbc8982d74521592465f734cdc54485eb5b7d3f0149245ae7df88ae249c55248d2b90a1fb58d42b27b86c69c9975773423419cb6641d3f6ddca19074a7929acb5898d29e90a035683315436cef8424d52ea1196783ee9bf06e8fa7cd6039139262b61cc7b4f22524da897ef6110fb24d4a48562fdd3df24f4282bec77bcc9e4bc60c1292ee3f4e990c62ed1d21a93dffc28cd8d4ab182159e3c77dccdcc59c32925df3c90d69fb49ec2224c6a46be386b8ba94c9484cf93a2b9ce5fe6d899068ab41dfd2e9f53c84a4ce223ebca864a7a910122f06f1b2561a9f1384e4131a46798eb9863580901c7e94ca9c611756ee22495e5e189d945817c58aa29a3536948b04a1333aebc68a300de122d973d8d7eb18540e1aba4592d27572af84094f932d9263dedd8aa1d7a74cb5484e21cd7e63b8a9c86991b4977266919cf47283979aeb949145e2077131d7f7c9fd639178ff7ad143eb8f07164951c3f3890eeaa7bd22f9b554cccd5c9178c194d5b5d589c5ad603f58568c5554b74eda4a248caa9e15e1a144728a2b1b7486d2b8b1b24924e55f783d95a2c357e9d924d1c78ca6cd7d76570af37ef72bffe121dff6f811834012c848c823bd9190479148d6a449fa7d8a118dd107978d843cca1a068904755ee9435632070e46d41e81e4f840f688a4cc0fcb9c17b3c9b594c3032de41dcdaa1162b4a7e9530e909b8035226994ee925e56ea0c1823924368cfa6b3addf69e583cd30608bd0c5b2d95bca58d44ae61a7beab5edfe5cb3a68a144c1189a3630e1f434bbb52ff6043233b3c446e7cf88d316e808cd4f5052c1149276d64da864044ccc1eb802122c1e48d9e5bae774f9f0fce432498e8e841f687aabeef83cdccaccd10c9cb69ccbb5a95ac10c9a3f9c3e91c84a56d468864d3a4737d6b21c4bb360863c865f7122f4d1049a32afe7fecd398e214880419ea29e34459d2ed0144b27cc8cb6e9a37269d3f24ec78e770d19d3f63e387440dbfa1927bd21997ed436210fd77297b6e64ca3ed85acbf890ac292b77e60c7eb0d9b061d8d2f690a4af3fe6794a38403c728c7888dc08c99170e4401e0ee2811a03d34372c83bcb8df579ad291c202664e46f181c1e772061e8388f8335410c3c2a60794890a22b489ddf13a5e9e121d9b5d2812304e9048e23a1416387dd21c1f32ec77efb57603c86d92131e8b5787af2d277d6ad0e49a1569b9a2a75e61f8b0334688c801c0f1b01394887c4b86fab964935872411972c956b85cf7a914329a69895ad9de53adbd0eccdd07827bb3f290e09325ae6705163eaf179640c0eb4c121d1ff6c2d9938fd99a42309e443a488e8c8e15116b037248ad0585253c8fcf7a70f1cc6060ade7398a0df73e048343724a7f7facff78ffc3f7d1cc2318648b621a937698bd524636c285cba94f5b214b4c3ec353deb7aa6a6b9fa8db686842bf33b2fedc3286b6a48fa8fd91965948eb56ae5170f781d8088ec78000a48f0000fa4e0018eb033b018e66931d4c5c5d65d63ee34635852a6eb3424c85d5242a5a78a973e434352a6981a44692e25e3636748d44c26baf2b998faef83cdc386c191036444478e4333246e34ed2c2693caf9c461654874d3dbf1d4d54686a4d935915a2a271be18a181b6536b03124681a25934e49c55195f2c1a623870e343124fc6dd8ffe76facb33ed812bd18250c495faf753a943a61eab881d0434ec042303024a9136a5e3b7c0e7d0bec0bc97a75f63eea4d750cc5f010f142d2d6c9697dbd9c34e6695d50bff47abebea92c877121c92bb9a8eccfbeea9b82d8169243a9c9184f29dd9117869816927446b532b50b164ca3366259c8f7e23de72b739880068d7c046243c4ccb090584aa7ca72e35f2149a6f23d6979d7439f5648dc141de7ed3d754c31ab42829631bfcdeca172fc6c54483e8d4965fabd447ec70d3e053685e48df5a91b73560a495b156fb4cf3485cea4122c0ac9734a5f868ad1c276b29d60662445b7be1d593a24181412b573c905651e26d813122baf43484f5b3db9d2a0c143302724af5ad038f1152394da84c452e26387269962f0ef0c634282a75d877ad5a613a725248ba59b875cb0d4ef2821e1f7d6366afc1854105a1292e62b34ae9db07a192121316f285babd2b163c68e90244706b1dade8d90d8f983be7cca5a199b7575d685bc055f1da541fb360621b72224783459a6634a7eb0e520c7f10803191ba7bc8d91b343644d10038f08181989d9d3da35664c84a44b49c5ab279ddc21246caa3955d70921b92df9880dcfd22d088929425fc56905910121f182d8e0715e32a5fe900949693a6c3c08466e788046efd05d245f549351b3ab7b6a25e453dac181c540e9224984103a9c9e56b820ff60eb349282ca45628fde7cadf76a32a38643c4d0a0709128724bde3b6fb74832d72ead1a6725ab52b648101ece62ccb35d5be95a24a5b5a03926ac74774a46d122c1d2338e50d1a9592428315ef263c891e12b5924d685aacda3725490672c12935cda2f59bf41981a16092a6613aac6bf10d42b1233cf6c4dec8fb9855950ae48d2902998d293a95624862b4d2f4fb9a9cc5a41b1a2b1f1d0da8e91cdb414fe7259ed7e18d16dad2261ec3f8d6813f9e2ab226184da534ad5681cf15391e85aba1564dda92046053aa9cf8e0ae24aa7e0ce4ce6ee55de62eb9fd025f364f61b46992231e82f5529126d3387cc986fc49d5ba448ae985cb62ca520e37dd528923d4dfceef3b7258a64cfc12bdfda5f8790ffa04291f0d5256490d3d1db3bb77182310a1489315e6b105e631bd6f38964bdcaec121e3fa994792231c63ef78be1c3a712eac45d5f2d165a75b51e1bd47590165ae3c45e5a9baa6a152e55b00e19b7ac346e6aa84d24679a88baf158fdd5349134f699f734ecca44d2e8d091b17d726d1d2612fe3dc930272f9e56579748cc17bdeef9254b2498576788e9894cadc1418e2f68d0c0418e9090aa44c228655d32da6becee3e284a2497e820b4e88e26911cd4d3452bafa494c94822c9c3376c29cb99bece4864e556da6e75d166e33bf3546ced784122a9e5729af7d45ea11e9194152dfba99359c636a58c7244c2a86cb132bab444de675423924674a6cd22da352f568c483419add447cb4f4d9e5a44f2ac5ada7f5529478e1014a02212fdde4cc8bc5de27c4d4472c570dfb36e9d3cc9bc0d1d858804955d515d435b8ef10f916c177f955e754324ff09cd93ad9d6d4f5e88c40a3ae9f9cf232d5f4788abc45c2cccaa861a537dc9e54a7d3588e4bc399e224eb67c0cfaba1c094708dc06c8c717a50491a46f9b9fa46c52dae281c06664dce4b2c86646eb39a1f76392318048d05cfbe9474c7fe0c6de729d67a5987684d84fca2e3f14d3aeb5b5324cecea53a3db87cf22ca53cead3e2426ad7c4a9dfcdc9c151f926b9498986c6c0f899531a8b5a810a5db7fb085fc088ed74372e81feb9ca307fb1df390a4ae675aae25af74ca0b0f49e762a193f06a11967487a4cf4166bb1a8db1f5db2131e5248307551d73ccb93a2408d9612f1b3ec2ce628a0e09ef6dff97a4c7a918d5a3e69098efd2c38652e1d7a27248cca4aa6ed64a0629f70c2a0e89b3b189595dba2b5d76cc24c63454756d75ed76a3562263f2644a8b32b7068886a4f564ea9b9aab640ccf90602f36a2191235a96e6bcde1937b07cb9094f298afbbcfc99475321c7e2ebac856c6905c49b8dffb5ba5a7114d22ca5230c03024081d74eed39f73c6fc8021b94bebba63f7733c15fc42b2ce5b8ed3eb8b29d4e885d53e5c6eabebb3d64e4ec53f99da98fac32e246d45d3a27739c8852475233f9375e6b94ac12d24eea985d57027b62de714b590f45e95f3ca878d1ba32120c932805948eacf953bfa7c72d800c30307c8c708080eb0a0cac959cbbd8c66e6d2d9323ba8598b3f5f21f9e78377c74c8819f100c10da0151244b6c75ca7f1cb6611ac42c2b9e9f86fd3225221397c0ad66661f7a14b5348fe504d35af16292465ffe84f19738e090f4621b1652c73dc4e3aea6cd737043423f9b2c36f7c7eb3b82a1e4221e1baa4dba9e5cbd1729e909ca172baf0184507b9a2131274a85395a9c6262485eafcf857252624ec9ea8bc4ffb12123f7b3ced1a4a66e92025248b9bb0b2bda0a6f69384e4faf48b8d51838424bbb0b3683a7690397b011e21d9920e4a69ff731376d1bb73a45d46e255dc0f6d322fa8c84570577fd35d3c2623596da3a9d279a3c96d899098ad6383c7d21092f4a3c6d8fe4248984debcbb56710127ef4ff8770d5d4ff7d008190ec9e545c396b1709a6f565af16f3283be922f983aa93ee41a3acf6b9480e964153aa67cd593c641c2e923a76d0a9ad4a255f0b8ab1778b24e14185139d53501654670bbc42c3ea9258aa2e8f6bdb371d7e56d722498378909a555dccb2b448eed3eb758bcfb1467fb348b63f99e36515952c7e74e4649118b25eb3a6d31b8be4fb52de992fd3b048aee095ba4c7ce52899ffbe22f9d386ce75864a5e9f2b92dbd7747888052163b5e258d1786e15b9a594425732a5d9bc1f9c2aee5291e821c47ad6517321e46b5424f6d9cdc6ced775a9ea3bc5d140678adedd2b54eedbd28add6be9dca43c2279a548fcd9edb52432cb9776a4484efbac4987c74de44691a459d75a7dd511901b202468c18922e993c5f7702a85c7b8502406b1b8f1736e911d7b1d078ae44ef2a637d56f8659f7898457b9cba5ffe289c4922d197316f3b019af1389a38275cc7b9b62799cf0b498690eeefeeae03691aa3c1d733b67ee033c908207a0e134815faa18f13d09fe863141ef28bb4c60222f912cbacb839a8fa774e996a882c67aa757c8675df25422e93ba58a4bd3d93051c8512229ddea2ea5ad6a6fcb24126fbe2a378db05c2ade278944932adad5722529ead98b4492322163a9bbb1b49d1f2492e3e5a7c58bccd47b22c23d22e9e2cb730765f69f29758e688418c708a4b108451c2de6cd26026fb10e39b1b7f6b26c6e275b4363a86b329d394424cea96c26315a9b4a2767708748921d211afdee3f6e6988e4b306212de57a85481c79b71664a999dd4888848f1ebbb3b483785283718348eaa8a661feb4df20229d9c2092a39ed0b911421fa2e350041788c431795f651ea6f4080122f97f3bce5ede7f484c8dd6211bd5ac5ef643a2b87ed0d9d5e572a80f89716341c5c6cd946ebd717c309f5cfd375beff6b0e3f4902064e9ffacab109a5e1efc1ba963e2f090d8fd163c2c373cc914e21d189d84ef25df0e265549261d6465ba701dbe1835551d4f09f9513a2467c784454f675dc139944333cb564cb52dcad786f59492b8b00a2787c498e682a90b998b436207cdfba27398abbc1e1c123f66d690f9cd6cc2bd21b13c465ebffd67fe0d9a716e48162d167337065dba49916b4361743ded28951e1b92fd2eb976524a6bd83f5cc831d9931a1234fc68f17ead4a95744672284da7b3589a912da52149de2c6d0adf9be24a6848bcf2787a29e65a1e9d21418de5ea2a5bf5dc793324771252654e8e1215f36548127f9d329f869d7a9f0c49ae9e1f43925e693ad13a55a72486e4a4b63f89fa20fae26148106ad3a7f01b967a0443e28e29ff6cba2f245b756e39d1dc0b89a7be6443e35d4850751117923b2f7fbcb36c2169d4d27297cc9fb3ac8524613279dd958dc5370bc9a7f5ac928e8b3229c2427206bdcdaa397485c4fcae954689f9c9d2b142c2fbbfc6a7bd0ac9492e5490a62f5e10a242e2efe6d9d07123fb3953481cbd7d6a63d82dcf395248ee6b110df515c5435148d0bd4bd7d920733a6624a80d2a27995a53160f149242e75dedcfa51cd53c21c9ba928cba0d66b51927248afe8ef6addc1c2e4d48381d2f850989d1ab52ca399350da2d4b489a191d47645b9490a46c674b28df4d3a589290e82ae234dfc585fe0a1292c2789eebfc34b75d3942c256902132251d44fd4648ae4b7ea772c3c78ebb8ce40df1f094463488b58b9014eb4e4c6a928ce4bf4bfe575dfa4b44486cef1c56b27965be212455acb6e56752325b0b2149cf73b824ed35b9414870d1f9221a2b4c45078404bbb495acb76285cc2e92f4c6376ec5f6cbe92279844e91a744b85b251749a1d1b73e6b5c1c63b677ab0def2d92b369dc6c56aa2dc76b8b449fcbad4efbd62229b3e84bd5a5f152891649b2c4a94ca76c4d27679194ff1533445a8e1f592427cfd941caa7168b04bd95ced4c81416b72759e92b922c94f03f4da9b77b572488c54ef318c34793b722e15352b29e3d3c87ce8ac4990f7fe2d62d07a15524cc7c98568ed92633aa48ea3ea57252d99fde662a92fec744860d4a5424a7d57de77c8630f929925256fef9f8a02992f6d388fc0a9622318d6636ad99713b8da44890296789aeabf814711449c2bea2efa8549937144572bc9882de3b8f39c7301449b5dbf715c3d53d73a048fa14f4dd3daa8e8df789844d1e965d9e3ae7ec3c913476f7669f43257d5d2712642c15742afd5c5a324eb83ba3edc9de44a2adccc918a33f8dab89c47c7aa69ef2c5fd6426125b73ad785fb0cb9461227984a73aa54248192f913cf6415ab2ad72cb2d91a454fc0312c0a2001605f0ea1a90ee93492833020d18c3c6c807182038285034cad23008a011911d210400c018870209f0624464470a06008007c3ec000387b171800088843cdae13942742080055e00c00b0710c20b2fbc00800614d00111101d26010020c30b1191436300013023c66910000066c4788e101a06f042000c4040c87b8481cc8888878702bc00c0020c802023211f22363c3c1090b3d0714316e871808c8501121663e840af4090111b1f22c6c30301e90a0419f11c375a91ac97ffb2e76728edf083fb6cc7d98d0ff11003c43d1c04bd0e9162233840588120236280f80e0f0f04e42a465ee4c6df2840aa22297fc384870fe525d65424a86c277490db8ba02231838d884de68dbfc19ba748509676bec76312dee94037fec6def81b28820364c406c88ee311f2364074dcc881344d911c5ad34feddd6ad4bf4a912c72736f365d262992832c8db172181942bff7c851245b16adbdcc7e3138406e20a72812bd54af84997e0db37cb09d1d22a6810cc595292ae377efc5cbc386b9182428922b69d70cb539f4c8f6c1a6d681fc44f257d6c5ac90f960e30aa42792beb4a8d84b0f2b2ab6225f642792a4a7e41616caecfa45427202a5ce2d9b6813a989cbc461e2ec126689be82c85dc5705b39d9fa5bff9ff7ecb10fc3aca012491aafe4754e192512fc2e2ea978f31ebc3a424e224125153796285dbb17492241e8ee58a3e49685cc91485a758d39cc8fda746b422241af52a807bde42392462c7ffca78b264d85413a82f554b550bb70f7165de5a277859d873defb31189b1fe35c660c26444b258aedb08a1a529e54b107211c92c9b316daf08eb2cace22bd6cef3a8b83211097aae7e73929a21358388a498b459e80e974324ab751edf9cefe3de6788c498ad141fb35588048d9f4bb7b4a920b24488c4dc382657193fe6bb3f5a908348522f266f2998c915d5992012c4853eede49e6ad38c69c84024291de17ab9358c3e6d0222393c6cc5b449445ece3523ff90d43bf72294cc793c99f44392daa4af336692f529da87e43a59badecaf496a6241f700fa887e475cbce516e21a395f250acbbef3acd0a2fd74ac14372ca551df7b2cec1e3503042be0478213f022283bc43991dac0e7ba8a8a568655bb73197a5c2a71c4774b6b0705cd17103e107920e496339770c624a5453ca907348b04df2b4c28496136a39f0b1d43357256444870a320e9a70c0c262b9574a96434d54e482bc52b146e5827df986041d725b8352b547bac168b4a1201b6e0da786448dbd9d96738ef7ef9f9169484e31edefb4e8a93c8e86a48cfe9d4a83ac6f4ae61992355d06bd25376e9d4c33180dbc916528434986637c5752163b6e111d393ac76034c4603470061986e4201f74ae895cb45bf1110986deddc25a57ce0c19a7b14beafe2e467e216947bf3328db30bf5e7a2141c82933654acf2e247cce1843bfa7242dbbbd4472e198f5b1e416125b33659e65ba0a99a516125c83e6145a2a877a511692e6936ce99f909997935848aece9369d943c7d4f415124f8758102145e766437e04e48a905648da60ebe1fef22a24260f6a8ea44262e79bb68c9a163a88a790a4f36fd40e1fa61412532cb318de354b955014927fee947edda98b6b37a5194917e6ce3ffe9f5db8402149561af5fb61e51392722cb1a051ef626a921392cecd2da58f9c4d48d4a4645305d160a6f35d3221f194102ae7b7bccb252458b6c5aefff89a904a68f45a521d94b69290b8792bf3b2e249399dbb4442e2cfed768b6cd1417957461e21713683583efb7852d7bb3442b289cf6ea2d4ca629eb20ca3f1822c42929c6b2759dfb8fa24cc4292911cf62acc768ab59d532e899060174a89505b61fc4b974348b0764b1b572be9bb8f97424818b96fff98d64e742c8360342e819024b2f942d3c6888068ed22e1377fce9bcbae57ec74916816f34ae89874b948ca95b46254ecf17091f8b9f4f98a92f16e9188aad6c89a6ab7d98b6a0af27e2ee5830d6d91a4fb7137e6b1bb5a24c8a03af4cba593e2222d125554057db1f3056d8c9011b30ef91190649c45e2b8b55f57d8ee1c51164922bb25b7ddfb316c8c05ee952a8959b7b6969c70cb5c52b4086171f88a646d13bfe1b2ce3ee85d91a4937dd8d10bb62239c58c231f67b4ad9e901549db97ddde63b61159c2015791e8b32a325ffa9ce1525491942be8edaae9914189539114de446392335f3abc52405424e93cd3f8d151b31cbcc353247696eeb58b0c4d818bb01449e2bbc3662e96f29c435224e5da86174d1bd47785a348544d3696f132c3a75e1449a964b8914bd9cc7628123de9af9f4ba2b6620741917c9d3955cd9998860b3f5114f9fa74d113499db2eacc98a61349e2ef3bdca6c9bea890134939bdf3fd52b6f9accec04d247c90df0ec2eecd72ae89043119d55625672251467f34adb9622249b58c58cf9d72c58ae12592572e8a9231bf434b2458a8bcbb6ce94a60fa1f3359ee9012c9f957ef3b763b7916dd7ad8e1c04924970ead7439e9ee59eb3cecc438944482b6d1714c9ab6cc631e4622a93b958a9ac6cd2e730889e430eabae542c59cb5c1c347247ec79349c66a1b1d9198c7bc4643534ef741d888a4d05439e9b8d6222392774f9c36b7b332155f4462c778a793f80a5b296d838a4894f9df6b191543c6ac0c4c446254d310f3332122123777a630da544fd3623c44f2c50dbaf72e6bd8110d1a888648cefdb2f239d907512516224973bad267d5ea129010651009ae692b7ce747f5130b82cd30e51efbe440741f0444e22915622a3e966687be38c43f74fdd9d74174ee02fa21e1e292da7610f17c6b00fb906c1f6354efd0413e246ebc53b1826bacd21e0f1d1672021484817b4812ca74f8c8a49e47bf912324f589180b500fa8ea74bbd152bc59079887a43c97a97aae43a7f81103e221e137e70d15327d873288c676b6abec90283a9f7a260b551d122efd96bd58e55d1ce990642ae8a435c3e951da3287e4d5d0edd4a21fea3713500e89a396468c4362b4aeefea9c4396d8af80705876f3a6b631d3e31b9257dc437c9296bf53de0d89233a53dc65a80d494a8f1ccd95fdb2aa886c48ca2f159fcc646b2fbd06b442c9933aab2131a97bd3c1e4f6ba85cf088976a8c4ad2643f26030128944e280288e0273cb1413130000000c209146839160349c299a2c0f140003652c183c3c2c2024161416168fc5c380201c0884826140201c080402c1401044516b1c6a7ccdb854e8878d85ff8dfa7777780097f8b9a9ad0816a40dec0076a9fbf669ffd74f05013a005d385edf7f1b0913d0cfe88e8de8912e171121bfadfdf0b4d49a8ab7977b5ab52ec81f201f7241e88e2e569a9293f6dfe93d8d37caa56d1478068c553f92dadb82eb7c35b10b6337f61657f29502306283326d9a20261b9cd617b63b308d44f296afcd14635d596f1a9a5e1464bd6ca79254d66c22967b2f8d68ccca3eaa193960a1836b387c6854e82e21cec2b092f8f119923156a74f30e044bee7be117215499ce7ac48c044906c616da9bd49ad057325222174d3e8af6f8f24955ca740357c2222d217708729b6799c000f3bc850e5b29fc43087b234ea99624c9aace84761737ea94c58b268d3251f42fb646693f96755ea1958b3144632ce162993f068f0658a2627a4da4660379f4317e69d0bfa1baae4f8d7f2c10c30274a084de11b8ea5dc010c111bb4ed8177b9cb6c182644a305659862cf29d0507ccee1bb31e2deeab775e2dc79a95a5fd6571d517e2167d32c8478ee6ad7fae82014ff1962adafbbbac94caad15dc2989f28f84fbf0043e575e6833842f06aa5761a761b8739b02633b744acc67d2d96ba9b71891c4dda12b7bb429b13307af3f4d8c2be6980ba7fc1e0a7fa9ee818c8f3b851d7e7b994f2a00e0637b396ac550ac62f6baaaa8cc2404a335a96cc4fab0fe73566b669a2b2db23e8cc4e5724042d4969afcc77c86cedc29d8429e736f1a4deab704a8517379c60c3eafca007c0fe1183a600d7596575dcbbfe9a0f376696480aec0e8292313ed0952fb83c5eb514a4aaa6e3edc19bd41e3276ede6cb85384620300ac832db1c55ebfc87a047be2a94b3d95caccfb977463c4c959387e1339482173d6f140b1b074355f956ea161a5e915459cc925ec7be7504a099d9e9d164a40c525cd14ee692f99c8187fa8041eef4adb45233e5e72e1947e8a6a8aa48f8ca1accb5a6d548ff86bbf25677030153d9f216e0738c46b36e5737a52822c023083a28fa6af881617e40e96684376b93b96be4fca806360f6c889b0dfdc9c20a18aad9efda2f5d8521b94887053c11062d901ec5e3f05e0c90f75c308d5560ed7653d2234e0afb184e88296059d08031a9bbf4247364a8ab4a47525aa8ae2d0fb698083fb1f50ede74c7936c3d94fb2e2b99c329b19cf506eb208829d8b3355841006375a8407f0d2c48866804d718bd0210100c17724d9c117569d72d1bb146faa5c5f06b09de70f6e3e2bf8c032397dddb6809ce37117bd6382203a1e182a759e39e84fc22a058824d86b5303217ea44aaae60f7551fc04f58c9d89c11081c3b46aa092d54a8e133e2690a6497e6139f974082ca479396288cd9c52bab7587da734970f3d052c5ec3b26f34042e69ba7e5840494684438efd4a197a7e54e990f7f475a4ba464f6036728e00326338401632888a3547b16d2fb90b77895bbb6d9a5c417de47e0101d421c3901b5410b39504f8ea5ed9fb1fdc6cf6bcdc0a32be5f957ed84fd371e5829853b4b8b2f21d30657e342749cd14b044b21b0795d1ca6c9109016045ca13d7e408135edfd57543a8652ae2e85e15671a8ad8104a5f394aa442173ef29299eded50342be15eecc75b1e054d943fb50ce0566557cbab96821ffa438d4da5963fcfce65490c0d6b924303bc93532b1bab7e2052ac71de389424ec1e58467dd94c7702a0dc454901ebd3f9a3e75df45886ab41d1421ac395464d6f29122c4aaeae780d119dbc6d6f2c9031938010df768432567afc2a94570df45401f2e371668a8007525c008460f81c31483c1087d6027cbaef56acf5d158aeae228f69c27d4d46d62a090e225e8470eb3efdce82015a05fa9feada7e99bc9fcf5e7a87de0e660bbf38c53a415d6c0f5680d403869a227fc7349dfbb1d8ec23849fa5c78a01ee5b453ba01ba0200000c14e53bb00c50d4dcd13b18375624e1c43b603b383abf9b3cae3f8d2ed77b973b23c95013055f13dd2d9bd6822cb54139615eae8e1fbdfd35b6428d0b0a3444693f8f58ebebc6dbfe559e8737238452e85863459a348f3d865d87e1f5eb8539c1c7b201d84d80d4da616ecc55a6d990bd7804ba691864e6667fa7d0acbd30d68a2bac8b20a1a2486d2c35d4bbd4497914e11888a6534e3207f796f5cc8b47200f73550d609a13f6f2a88a0d90ca263de88fbb6c8136f4bfd73f620d5579998225d2b0ae50232ad85f292195dbe2a94fbcb78f669818e8b5aa1c502439f4a4acfc52bbed68c3c3f105960fd074b87fc9a884475e37fa23aa901af5fa315ad2f2c4c2f8a81712a21917810e996b86162edd77b2d84635d7035446d0c7cf3a563437c9bfab41c246317e4c6b0600c354aee48956f5bd7f010c9f4db4a13bc0c151b46ec11d16a76ec24578b3f0a8b75b114c87c675923718ace7c621db01baa5aa385a8c0358e56937cce97baff1ebd215568400d0cf508f41971725a1e63914460d89812fdc976b45d3a1f2776bd263f46515c4c54a23ac71e4b544cd86354f274dd8c3ced6960c6ea092a39287183ccf66a9faff4c09c4843f8a4ea323a1a47b0e38a38fc8fa1f9a426ce8e1cb08d8609d4aaf25c28ccf8649a73e4869ab08b31e9378908d1434779916278699790d18cb43d021eda4da59a6c5ce8c49c192304a92d940a22f7930c0e3252623339e51a212de2cda6259d7b38ade15c416e5ef2d29d0f233b3c22551428302c07cb26c193b7e8dc6ede4a3e64f8835e258c676df8bd23bed2632d9674e0b349b4d1d427bf6c80ea4d4e85efcc9ff07753b5af2eda6011e6e34295be272f99f3d4cbbd60bccc20bd21f9ef95415cb4fc87bc342dbb87ea1fd126aa4f357586dd9cfe700b1093e051f1e28ac758c3dc1368818cec2d15f31c453955016a32c1e75a4c358f9c4909607ee4d53779656ee0ccef992dfdf8afcfdf59c0b903611c03e7d79c91e93178f352f133e60694785767fd9a9b36c7fc8c1fb3c5bf25a016ed75695c0e480d3ddc787a59cdbc6b3cfb29a8d768fa992492ae46843237da6b2da41f990923ea3bddb784ac4288dc0293bd66b1adf70f888ae03884ea71e74663cea4eac30637b455223e2f4592dfb21c81429bf94a64bdee6e1cfa769a5fd8335158c1468fec501a12de7c67be8d7543069457b255bd282a778b1b49f525eb8e1e94e70106833bba789e7883636dbe887cc63042d740f2e0de1a39ea95895980a2b540b06ae1fb7d8198fc44904313447d581a2207b30fdc80fd5f183534318c61d65b96f8af9cdb2bc46f3b89b5070784977d83d706aa9ded3e5dd87d1940999535852b1cf3306be204e988f80b21c777c0009363a30ab3108d130c6bce72574a492f4360c0f1c145654a7962046020bf983cc32a4210352f6d256e3a1d2b43b3fad94f5920fd52613aa12bb8d43c9d42a826ee2ec16a0c1b9f9da201e026e835052c2af9f4247833aa801bb0f8ca62a0c4d8a76863a91b1a3624eb188c52847d59e7db212656054ac4571c4ddcc049935210457899f2f8ce097ab5855af22678f306b83e10019d7aca9eddbd2bd3d6f91130f289f99bd03bf6ac190d6b8fe40293ebc67c7292b63b477a688447fab9823431c4a9c8633de0a297d73d2bba7fc08ceda6c51dba930b17e193c030e3d96c8bde7fe85e4a80b2e293af1a8e5ac38082430a04bd219677a69b11663ca48ac83440cf0e2605cdc44f814c129b46dc309a71b42d9ce68e46f8d244ac248b92938d566976812eff054f851dc13d8169350909a92be8994343f0df293f40d035853d1face91efb6fb232c184db7605fc1845cd10a807878460246b5ab5e7871c91889bce05e4d3b739ad342ce77fa1a60470888678f87201f07e0bed2d969c71f38451dd680887b7a74aebcca99a920cbfe9ccfdebabefe36280155596df7cb205a1e06ea59929e2fa2220232d3c8dfc9d70771172a6155a033f370617030b08bfd394802ed7d9f1061294cb2d9c99df7bf80fe6a8a86ce94c0905c15f0cd218e6949e9bd1bc51295af05a38828f1ccf1b2841c66edd896d3e845d0501641a9661029f30476e8d364e3aff26e35a4eb09762b094703563979254e48470b0d99bd116d32297fc7d150bc7267f374fd850f80abca562f1946e53b1a909187e998c123cc32659aba79a595c9828502b8938643e18120e4fdac883d6c475f672182d15dbd8ed878ee896d74cb060c5e454571bfd66f0bc3090e8bc6157f7572745f95ffaaf9bf969b7c1cdb5395a93201687b97c055a71fc0b213fdb0472642a97229defef859124e81d510d072deb87d96f3feeecd10f1a0f74f95667e3544f3df3c4c74df2debe4f2174b75e83af62346ea0423452134034280e4221baeaf27f8f43633662364a68952d160e0db3766132a882ff0b919a3ff79c949ada5836536d01bd9b0bd9b142025306e9d4dc8d0b29ae46408d31db6eac06d381e8e9e07bded4714edc1fa9eb331694d88818106f790d953505659ea2234baabcb75f581bfcea1a4ab718bf9551a82a24a1c1df08cbe9db9512f39022f27c800ce7fe7aaed9189ea19e51061bada6ef05308fc7d0a3da7b8451787ae76eaa52cfb3963c1bdaba6f02cb53ab9c82a04b8f15c6a5aa1927cfc214bd3c367ab39000f1d3dde43ae79538a12902d848da45363f92e64aedcd1b2d84f4c828d99349e53d4a92957e6083d64178a39d809215b8dbefcdb030c554da87175b44fc2252afca894ffe7aa3ad282df146d333b2039695bd17260d267ece3ed54a6d42d1620212b4f16cabccdf815594a57972410edf6e686cacda6a21273f2a67568d49ba33cdab90cb4ba7f3d915b8407bdaf13750fbca1957e25462662b508611603f64c30b23008e4fa72108b9aa7911de513c07466f4c578cc39475deddd2f8586bd48053841ee411b046adcde50b704356a4764ad23284513a90b1554b9ec14912345e963cbcfbea2c1d96f4bbd7bca1e78306568036dc63aa586dd792797a458153817d1372560221076fa6e451fb98fd933f76f77799cd5663245128dd73eb5925c57b2b7ac33e7bd7bb7aa28988ae7ae3d7eb38fce2e12be88d1de988ce2fcf5917f16fd8705d7290e71910b944a85d80b4433aab5c4f5bef9b7b6b9c54024db663ca5633f4da23f85dd4fe9a277910413741108afe9239621d9df62f05964a5804a80fc7d1390a46715540ad1c72d0ab103ec87e8e2e4487346cf3847cd54ac228c0ba66d5ebd688af5710788654a26a323a30b4a0ce9238191263a8d90c9239d20ca89bed000ac9778bc3b7a2699ae9aa09acea722d3eb0d0a34a59bf3e8d3f23775425aba7a3cdcae2b9da67ee3449c7b302c106685ec39f12ca055f8fdcb457c464374e1dc58812cd7826ad281b02be40b47d9c244b6a73259103b1a9c5af296da29025f29b301d1845e7c6dffcbdcd2708fbf22d95fa31e250aa2d6004c20ca0eeff473ce5c5d545c08eab4ce25a7fc300d96e0e518fa8dfee7168f71a803a714ef1eec9f556600f75fd9e2dcaa15e42bec11dd78877e0f1f0b371d9c3908dfa010a116ea50efee21d16212465864cab371208aef322945ad403bf47fb26f88b6da0de308a30f70759c70823841371973f69429c62dc4f343490ea1703de7ddb865e349b1cf8595466639bf9008c5c7b990b503767c659e47a5523d56167064ae0c2f380653380dc3c386a0583b3b07918c597c0bb5af9121908da667b8f9db58e5fb85598ff118a44e3ec5b772be98df31e00017cf4729488b9bc5c925c23e202596b2d46a035dd4bfeeb852443981be5231fe84b7af35d49aca3d854d6c443cc5c992e4d0b32496e6853f56c061509d3ccdfba1e508d9bfc01a3e85cf1e51af8f84fcfa3f4f9dea6e1cb1d8f6fb368f580d3d68ed0e2317734556d3d178f15eeaa4ab8b725028d6bfa268497b7ab35635dbec936c7a2fdb2516ecf86a51a20cb45e19a3d5971f779d8a1a51d84a5eea0a82ffe6033533408a78d2b4bba9a19a172ed0322910430966145fb41fc6d066fdf90f185472c46d4ea1073fa1fa0e3acc67284b05b968e6743c1fb853e3819a7aaa509744e94f234184bf7ce935c7c0f209b173a02d3d9ab1207f56e534432e52c83b61046ea156e37342d8b476ce92baa681e9872703618d1faa2d00e15b385e1236c8c1e8c70255e61f20049da460d3099b745f6c38bff49107af8d27db07ba61d23e205e79c44472ce535ccbfa1a7f67600b0c381fba1ccd8ac7103326cfab35f53c5cfff2fd65e1880047a6170875153d6a3cca2e026676a098aa18ed034628f00bf4d34e858171f2b88bace20407e1a1f50eab2d79913e25f3ec35890f2c17f1588557889af603d98a4e15b322dc9ca9bd581ce70fae9211732f9215548bb9f3ea3ef32c7276fe27361331829d5dbe6413accd6851babe707a1bc35af1abf6cb077a7333fea54b3be85dd96ee528bdbff5dc9b6265279f86bce2d29f77c8d485ab2dc07e86842345061ae5079e8fd898752efb1c36aa52ab1c5bbc5c099f44e7885682370fcf67bd67e6ce82db1f38c7de232d949ea52b6fc63ff68c43dba28a2da26e664f38bd03ced0b99e58f3be3ca2bb31c2c4dbb3a44952735a237158d2527e79993cb00e8ee0a65e757700675f072127014b97ba503a157fd0b044967743e3d22fc080d89f27430da48a509fce067d8c05a5d3e63c8566a7572a70d1461caa126b978c485dd42bfe7e2794154cf9f330fc5e739763066b0f02fe0a044bd8d9831583b3d0c652589a1f3bcdaf8da2842f2dac19e9aa2943bc77984b6647926d8518f2d853d8c3ca97b34db24b10cc27cd2111fc0edea7bd98a3731e48208e8c8b03753194d15cb1e4f19a9215eab642916d442a5f0fef993d44eb62ca0e5bdcb9f1f4c4b4327d3c12e5a9ed1d2550b9c6c04f9872a1e7e8c484c9ac6f454bcb8700c2e9885404cb8d2b0440b43a94c54ffd48313dc76cf66f87f13d23f68e445f8019e6df6f33223ee41eb4d80c317dad5f6584200e62d728403c81754362746ee4cd89d81a7c3685513724068c857d1539543c25f30e6928310a7c5ed9528f6fc320a1037b4d0c912884d6f4d0a2f7f8b806755bedcd5603583ece3d1f39fb174e163d0608b5d8c4cb3498168738ce95afe312dca7af9badf39e1c81e382a1b78a16fe3592f2b3bf9aa13a0e0be7e2f808b6f933ead246c1abc49ec4d863b529f031ae16b889a124fa882c26cb4551c44ab40a6d3886fc0fc770d531f1970bd0146c1e6044cb567bf922a822a8c3798e6fc03f42622d5b0b749b597b9a1d8a8ed51c338f84abbe84e697b2aa0a89c871ea934a85fca03f6a07f97f80580e797570ac55674756084b0f768758fa98351298c5d88322e0b01f5f78bbf4e9c75fd2d7798f091f1c9960e6ce11892ed05a1a9a7cda052c80705eed2da348504c6092677d4b2f334b063db3623dc0b151d25977250ef027bf410531c0073ecc1d247cd06764229c0f03be1643b90e484fcd6c5f9adcd5673d9fd52b541f7420bc351c0174687b71f3da99c2db0365b7336a33d8f12441283e07c5cc393ed8da916ad77a94771f9975127c2b03919cba5348c8ec2eaccf2557f0133d56236bd76c5cb2b150e213deace351b1848de2c6b1043fee0d6435ab25a37a5655cc6109e46d2d64e4433882de4078bf17b73fb89d37137c6a392fcc2bcef04c4fa694f89994fba57a5146721c381c4c23f9700ae0522f49f4449451439093ef12550d4aa21c288c88cd08110261672e9870c10b598abd80298158d07d50419e23cafa02af03eb204baa5d681f6fd24f03e384f89c49723b960164703f74baeda59550491ef7c44e0d2a4ed139127eb84b03f19f942ffd90fdeee0c346f48c423d64ffd60b8bb66662e81dc6ad4f933ed402dc3cb2d5613b7fa332aae4f7ec88173b038f1d61e52a614df61850d23750769184807fa0b8bc9e39c5a12120a06fe33d28ddb8646b28d6913039108e9e0a0175a6eced11d1e13c8448d6be19ce7ad6428f979293b05b172c81632381203bed414ec1ce27095a037f411c583ea0f3832d88003da86a6e0122821dd7c6754c45732d7e229cfa1dc779bea7f47d82c516f5c9f3efdbe45066ab25617f64b5e59911d790e50a4402e21ce4013a4f0109346a93b106e00c34b4721bc5433519f2ce484122be5ef161cb59fd3562d9fb6c177c751a5946772ed51c4dea9aeba3908b7eef2bee1b4a6705ce80ffa4052cc652dbf47706b881d6f96772c57322c03f908376287f56c783cf3c2b8097db4f0a064b65041c288b6045b2260fc748a59b9860a999d3966021bc469161c29d38b29055f778280666b93c7931efb257f6bd1c291a28789356dc4121d3bcf37040ebfae154f2c399c14080a1978e1a6be841058684ffb630d07826d2357bb6cc029921070b46942385a0aa243831798ac0e09dd9d2edffa3c1d1572a5e09a5f52ef12acdaec8128461ec836820a70cd3e55eab21c9c0bbcc64638381c6283426b589a2b2809f7b9de10b1d74fe73cf42dfc46446b0c3175d686eeb387a24c9ea30330a74b96026e2477a436a7f522e5acab45d42230fb51cab8dc7c7c8e9670e533aa0e3af29cdb29483d3fb102c79245aeea29729723757d604cb4bce8fdde1f61bfa7329894db2baae42bbabc58cf1557d4d5fab32aa03ce930745e6409ea3e81d90141e2a6a47a514f27f62e2498a4020069140e380ebd9163b0a2c288e0c1c55655b2635b2ca7aaaa9381d16effb6da9d2e398a4b4287037786dbb7e91c8f59830cebe62fcee944032e3b093b0aa93382763c17b55e88ac1995e63ef56f0a28009c100118b1953b84fda551359b4329f34dc8b999e1544006a48413d902825bbf0b3fd70b9e2d0a9cbbdad762d941bb830f24dbf73096f6184db2a786f54c8e15e2e344347db8004c974491ce1a81b32bdbe9bbd3cd2a6471c8a7161ccd6d794ab04ffa082568bd259a1b7bcd96e20827092b4eb607da3f34288dfdd6d878f6dd3c2088cebb538e9b475b880f776ecfcef53e1b05d3f0616b203a7e16a02e48322c746960430482f9ed97c41f5ef3385c8cc78caf0f0c53db31941826d4004e263d471449fa1ac2fdad8422a964294121fc357fd6410f9f7ac3ade81ab5484379c44d68930d6729194c65688984a087490d3b4449a5ced925f479d458bdce660c212812f518d639706db13b7f9a1558e944dff261376b2a181320451f6b80cbbae3a4640361e24724a9b9c4a6a534c455859db3d5f2f19e875313aa003d556f4d70a1e50547871048756b42dc681110e8d51be7124182a3d2a06f00e72d6a5f52c0452b29b0867d95a02063181af1e5c2965d36ceaeef1d24c8d15da594ffb2179ebe39976a924e9b27a886b64be04676f0600d860f73a4a665595d1f9e8636e4f5b5772de85604f1bf4fad34e1c4ea00d48d35f89a90698a5edd2a4bf352e67ae2621421592e0e31bfc6df1de7c3c21d3e9cd9b07da377c089cf668114ad3ed4f66f296ed85de3fa2e91bfdd6ac8fbafa519d6f98b89f15f7f2f30dd1c059ab6068258acc7d35f29a018e11186d6cfb5a7af3a757909bcc41b88844256b7919f136c7628fe195a941d22e16fb40c78d949dd28a7fcdb75abcea847c7b73e5a6cf84523a11dde54e6df24f45c888a58fd21252dbd1bffb186084e9a6b7014ad0bab5b7d962bc24a0d7426d0d92ded6a8a75402728ed37d8fa61006e7d0089e195ff13f10bc275718cb14930715915e1853cc454ea2af53ea328e30851830a12d2cf8fbad6397cc45b5d97b2a35a5268d22f0f8009d1bb664283124fda276b36811ec240e40c617a3531824a66aefb40f7ff900296c44876d3177a9080534e52586e8790fec0305b94912944cede2643f1379668f17a321c2aea588cbba2dca548aaf7dcbe1222657776c105f345a6bf90d20e3e9dade9a6638eefd95482a341088607b8a003f18371b2a1d77d5305fa027717d1a5553a91d3e293d77ee5a036ecab52b90936933a7087eadae5aadfcaf4ef8c74491c2a9e6efd3231c332b0ba382ff7b10617b9a5af43829da1d8c30174b6a95e19a3250eeb28c9732b882436a35" + }, + "balances": { + "balances": [ + [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + 1000000000000000 + ], + [ + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", + 1000000000000000 + ], + [ + "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y", + 1000000000000000 + ], + [ + "5HGjWAeFDfFCWPsjFQdVV2Msvz2XtMktvgocEZcCj68kUMaw", + 1000000000000000 + ] + ] + }, + "vesting": { + "vesting": [] + }, + "transactionPayment": { + "multiplier": "1000000000000000000" + }, + "treasury": null, + "democracy": { + "phantom": null + }, + "council": { + "phantom": null, + "members": [] + }, + "councilMembership": { + "members": [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty" + ], + "phantom": null + }, + "technicalCommittee": { + "phantom": null, + "members": [] + }, + "technicalCommitteeMembership": { + "members": [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" + ], + "phantom": null + }, + "parachainSystem": null, + "parachainInfo": { + "parachainId": 2106 + }, + "session": { + "keys": [ + [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + { + "aura": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" + } + ] + ] + }, + "aura": { + "authorities": [] + }, + "auraExt": null, + "parachainStaking": { + "candidates": [ + [ + "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + 50000000000000 + ] + ], + "delegations": [], + "inflationConfig": { + "expect": { + "min": 0, + "ideal": 0, + "max": 0 + }, + "annual": { + "min": 0, + "ideal": 0, + "max": 0 + }, + "round": { + "min": 0, + "ideal": 0, + "max": 0 + } + } + }, + "polkadotXcm": { + "safeXcmVersion": 3 + }, + "tokens": { + "balances": [] + }, + "identityManagement": { + "maxIdGraphLength": 64 + }, + "vcManagement": { + "admin": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" + }, + "teerex": { + "allowSgxDebugMode": true, + "admin": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" + }, + "sudo": { + "key": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" + } + } + } +} \ No newline at end of file diff --git a/bitacross-worker/docker/litentry/rococo-local.json b/bitacross-worker/docker/litentry/rococo-local.json new file mode 100644 index 0000000000..ecb70c118c --- /dev/null +++ b/bitacross-worker/docker/litentry/rococo-local.json @@ -0,0 +1,170 @@ +{ + "name": "Rococo Local Testnet", + "id": "rococo_local_testnet", + "chainType": "Local", + "bootNodes": [], + "telemetryEndpoints": null, + "protocolId": "dot", + "properties": null, + "forkBlocks": null, + "badBlocks": null, + "lightSyncState": null, + "codeSubstitutes": {}, + "genesis": { + "raw": { + "top": { + "0x0595267586b57744927884f519eb81014e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x06de3d8a54d27e44a9d5ce189618f22d4e7b9012096b41c4eb3aaf947f6ea429": "0x0500", + "0x06de3d8a54d27e44a9d5ce189618f22db4b49d95320d9021994c850f25b8e385": "0x0000300000800000080000000000100000c8000005000000050000000200000002000000000000000000000000005000000010000700e87648170200400104000000040000000000000000000000000000000000000000000000000000000000000000000000080000000020000004000000040000000000100000b0040000000000000000000014000000040000000400000000000000010100000000060000006400000002000000190000000000000002000000020000000700c817a80402004001000200000005000000", + "0x084e7f70a295a190e2e33fd3f8cdfcc24e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x08c41974a97dbf15cfbec28365bea2da4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x08c41974a97dbf15cfbec28365bea2da5e0621c4869aa60c02be9adcc98a0d1d": "0x08020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a10390084fdbf27d2b79d26a4f13f0ccd982cb755a661969143c37cbc49ef5b91f27", + "0x08c41974a97dbf15cfbec28365bea2da8f05bccc2f70ec66a32999c5761156be": "0x0000000000000000", + "0x08c41974a97dbf15cfbec28365bea2daaacf00b9b41fda7a9268821c2a2b3e4c": "0x08020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a10390084fdbf27d2b79d26a4f13f0ccd982cb755a661969143c37cbc49ef5b91f27", + "0x08c41974a97dbf15cfbec28365bea2dac713b7f8b14e2815d297585d3581e774": "0x0101000000", + "0x08c41974a97dbf15cfbec28365bea2dad47cb8f5328af743ddfb361e7180e7fcbb1bdbcacd6ac9340000000000000000": "0x00000000", + "0x1405f2411d0af5a7ff397e7c9dc68d194e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x1405f2411d0af5a7ff397e7c9dc68d196323ae84c43568be0d1394d5d0d522c4": "0x03000000", + "0x1809d78346727a0ef58c0fa03bafa3234e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x196e027349017067f9eb56e2c4d9ded54e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x1a736d37504c2e3fb73dad160c55b2914e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x1cb6f36e027abb2091cfb5110ab5087f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x1cb6f36e027abb2091cfb5110ab5087f5e0621c4869aa60c02be9adcc98a0d1d": "0x08d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01000000000000008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a480100000000000000", + "0x1cb6f36e027abb2091cfb5110ab5087f66e8f035c8adbe7f1547b43c51e6f8a4": "0x00000000", + "0x1cb6f36e027abb2091cfb5110ab5087faacf00b9b41fda7a9268821c2a2b3e4c": "0x08d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01000000000000008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a480100000000000000", + "0x1cb6f36e027abb2091cfb5110ab5087fdc6b171b77304263c292cc3ea5ed31ef": "0x0100000000000000040000000000000002", + "0x2099d7f109d6e535fb000bba623fd4404c014e6bf8b8c2c011e7290b85696bb3": "0x08d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48", + "0x2099d7f109d6e535fb000bba623fd4404e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x2099d7f109d6e535fb000bba623fd4409f99a2ce711f3a31b2fc05604c93f179": "0x08d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48", + "0x26aa394eea5630e07c48ae0c9558cef74e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x26aa394eea5630e07c48ae0c9558cef75684a022a34dd8bfa2baaf44f172b710": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef78a42f33323cb5ced3b44dd825fda9fcc": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a44704b568d21667356a5a050c118746b4def25cfda6ef3a00000000": "0x4545454545454545454545454545454545454545454545454545454545454545", + "0x26aa394eea5630e07c48ae0c9558cef7a7fd6c28836b9a28522dc924110cf439": "0x01", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9007cbc1270b5b091758f9c42f5915b3e8ac59e11963af19174d0b94d5d78041c233f55d2e19324665bafdfb62925af2d": "0x00000000000000000100000000000000000064a7b3b6e00d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da923a05cabf6d3bde7ca3ef0d11596b5611cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c": "0x00000000000000000100000000000000000064a7b3b6e00d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da932a5935f6edc617ae178fef9eb1e211fbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f": "0x00000000010000000100000000000000000064a7b3b6e00d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da94f9aea1afa791265fae359272badc1cf8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0x00000000000000000100000000000000000064a7b3b6e00d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da95ecffd7b6c0f78751baa9d281e0bfa3a6d6f646c70792f74727372790000000000000000000000000000000000000000": "0x0000000000000000010000000000000055a0fc01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da96f2e33376834a63c86a195bcf685aebbfe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e": "0x00000000010000000100000000000000000064a7b3b6e00d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da98578796c363c105114787203e4d93ca6101191192fc877c24d725b337120fa3edc63d227bbc92705db1e2cb65f56981a": "0x00000000000000000100000000000000000064a7b3b6e00d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9b0edae20838083f2cde1c4080db8cf8090b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe22": "0x00000000000000000100000000000000000064a7b3b6e00d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9b321d16960ce1d9190b61e2421cc60131e07379407fecc4b89eb7dbd287c2c781cfb1907a96947a3eb18e4f8e7198625": "0x00000000000000000100000000000000000064a7b3b6e00d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d": "0x00000000000000000100000000000000000064a7b3b6e00d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9e5e802737cce3a54b0bc9e3d3e6be26e306721211d5404bd9da88e0204360a1a9ab8b87c66c1bc2fcdd37f3c2222cc20": "0x00000000000000000100000000000000000064a7b3b6e00d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9edeaa42c2163f68084a988529a0e2ec5e659a7a1628cdd93febc04a4e0646ea20e9f5f0ce097d9a05290d4a9e054df4e": "0x00000000000000000100000000000000000064a7b3b6e00d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9f3f619a1c2956443880db9cc9a13d058e860f1b1c7227f7c22602f53f15af80747814dffd839719731ee3bba6edc126c": "0x00000000000000000100000000000000000064a7b3b6e00d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "0x26aa394eea5630e07c48ae0c9558cef7f9cce9c888469bb1a0dceaa129672ef8": "0x319318726f636f636f", + "0x2762c81376aaa894b6f64c67e58cc6504e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x2aeddc77fe58c98d50bd37f1b90840f94e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x2b06af9719ac64d755623cda8ddd9b944e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x2b06af9719ac64d755623cda8ddd9b949f99a2ce711f3a31b2fc05604c93f179": "0x08d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48", + "0x2c5de123c468aef7f3ac2ab3a76f87ce4e7b9012096b41c4eb3aaf947f6ea429": "0x0400", + "0x2f85f1e1378cb2d7b83adbaf0b5869c24e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x2f85f1e1378cb2d7b83adbaf0b5869c2ff3ae12770bea2e48d9bde7385e7a25f": "0x0000000002000000", + "0x3195e99b3353c0f2dd3f53c10740793a4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x3195e99b3353c0f2dd3f53c10740793a57c875e4cff74148e4628f264b974c80": "0x00000000000000000000000000000000", + "0x31a3a2ce3603138b8b352e8f192ca55a4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x39e295d143ed41353167609a3d816584": "0x0a000000", + "0x3a2d6c9353500637d8f8e3e0fa0bb1c54e7b9012096b41c4eb3aaf947f6ea429": "0x0400", + "0x3a2d6c9353500637d8f8e3e0fa0bb1c5ba7fb8745735dc3be2a2c61a72c39e78": "0x00", + "0x3a636f6465": "0x52bc537646db8e0528b52ffd00583c91042e09c64a13531040cd381d55d2e3297c94b9248bbdba8a12604680b519cfdb6f3293a94a689a413bf2eadbe912ec487efc84861bce61587e4ac9dc15a7c6223d1b1760e702e42fb961e0d1bef86fbda4eddd6defbda54c29a5c914ed112c1275e4aafc01d41d838495eaed4fa8921a8ed3fe86fc5314257fde86fc1b4e10024a55822770d5942a7f8b3864ccf7244864520efd1d84805205b50f6a1d8d16e1aabd46b976f16fe59f22d7e5919428c4814784a899451c27e6a0de22de3a20dcd9129a3aebce96b2d45ded4ff8554fb5a1f6db2f399d410828d5fea6fe726a27f4fb07f5f64ba748bfbf6bcf2ffb35ce27f297afbdf65cbfba6abf20118d72f8d5a0e626bc6540e8a956fece9676a8fb0b42408929d72ef9f23d152cf4ef2f4aaab288abe410500143e5883453ded803843b9bb2a57ebf5cfd1a654ebffa79b85909ece0ef3aee781dbffaa3e4f12fcff82372ddea31cbe7dad52804b5e72d788246c7afda730dae5cc976d111c92bea73fae5bdff4ebf366feb3a3a768ffa6e93ed0afbb5bdbf4dbfbaf7f73691abd7b5eb7b064fe8fad1f17bd46fe2d64f44f2dabdc7bdeca96dd7f86de2564fdc8aeac46dd7db54fe5d9f71a7321db59fefef39b3266e35819f139166656fbdfe16d4e8f842d57ec5917370e5ca7fdbc5ef49707bf5b5eb6b97fff6ebf25f90c846833ac857e4dad52fc5916b113f3d3d3dd551a9ee6b2292d77d16c376f53348c466d26d5773fdfac29c7e6df597e2d677202993c9643298bf6b8578f55f1e47f1f4c1e3675101e1286b3fc7cf71eb40db3d6358f9c7b0ca7ef52f4f2bf1b62e8fc7fce3579b9945afdf0a31e23235ca97f2f86351d6ef4c2dda6a1416f8a5545226abbc2b84ebd7c8d51f01e1091f8b5dbf598da2812bc48865b5c884cadfcf200aa1cc08cbc46dd7867543506a6e36bcad5cbfc67effede71ffd65bbf63d17b976ed0385fd8e423f9153ae5d1bd44acc46dafbbdfe64bffcf777c16d577f50bb28dbd51f241cc3daddeddfedcccf55a63a47f8e5b3d36dd7fe8244988e5cf9833aa897a79d190c6ffbc5fbfdfb5e8b1cf27baaed17d77d4ff68b7fdff36f437f1f386484f2fdfb1d24d294437e171b2422a96c97bfd71f57f73df931c8b5cb7f41076ce50feaa05e90487f5007755007b5123353e12dcff8d5df5b0384defe84fc0b12d967b0c80b32994c4a5dbaed1ab9eaeff527fbc5bfbffdeadf5f70dbe51fd42cca76f90709c7b0facbeff7f772fab5d5dfe32fecd7b66b9475dfdfa65ffcfe39fde2eabf2011fea00e6a2946f9518f946a539d235cede76a539d23fdf2fb2545e2ca74dbb5bfa011fea6633f7f5007f5f23825faba212835b318de220947aef273fdda316190a9fc5fbf7646c31c953fecd73ebf4dbf7626e54ae5cf59215bf9978729d1d70d4189b97a4a427eaedb25ec2fe8ed4fb8bf9bb748c291abfd5cbf764c18646affd7af9dd13047ed0ffbb5df6fd32ffefe9c15c2b57f799a127ddd10949817e0ed01c29dc9a0a82357f7b97e6dddcfe9d78e09834cdd0ffbb5b32eccd4fdedd7d671a7ee6fbfb88e36757f7996127ddd1094988d78fb0ac7b0f2bbfa357e959ffffb35ba2a3f3f907e71e50ffbb5953fa75f3b9322abfcdb2fae234fe5df7e751d772affd7af7dfee5614af453370425660f783b42b8bf9be7ef6db8ef03878c50c7483f7f3fffbeff82449c72d81fd44acc2e6fc3fdd570b4740972e0050a070b1c2a70aac08902e7091c2f384ee074c1610267099c2d3848e0188173044e103825683de1c870a4e0e0807343cb0e1c15e0a4a095039c2c3852e024810345ab8e161dad395a3268bdd18a41cb052d355a6eb468d02ad362a3f5020a4c00859653cb8c1615ad2f2d2f3ec69d5c4c6a062919a49c52667c367c35a4cae854d0a5a055854d0f6c786073039b33a82b281c5037a06ab831e3a60c140f5077d484512346cd18328c6c9259c899b4428221a79052c8282418a90279857c425621bf9054a48e905fbe3cba276417b9050e979b3424144647928b1a306e64d28b6ca2d57453c58d143751a04e80f2c1e6a9cba3b3c3260f14161b3c6cd2743ac0b1223587b6464a8854102920523fd8dcd0c241ab8cd6a2b76019f01bec0687d19a828f6023b8081e82896024380bf3c03bf094a418626c02128c00861078a0252603471e6044729140042050a401957b601d86244048129c2f384de00c8103044e0f38587076c0b98263034e0d3834b4ceb46cd04aa3b5c60b22689dd15a418b8c16182d2e5a57dc90b9f1723343877123c6cd165dc60d930d18365ed4e46123834d151b303654d84c6123854d148c43236103c405588c9aa69a2f6abca8b1a235464b8c9a276abcd474e92f9c4b0d971a266a96a829a28688bea366881a216a82a8d9a186871a1d6a72c0c901c704383ce0cc80e3038e0e3538d4dc5063434d0d354c3532e0a000e704383fd42ce10881a3440d959a293532d6132b0f561a560f583c60eda0b505cb0e160e5837609d61d9805503561c2c395874b0e660d5c1ca41d780a5839699161cad2e5a5fe068a9b15253029c296aa4b0eea83141cd959a2a3844d4cc50d3438d0f3558bc879a13d4a0a0e9a801a2e687d6414d969a236a8ce817301d3549d42051b3a5464b8d123529a871a2a6891a286a54c037a891a2268a9a296abed4545143050b4d2b8b16162d3035606aae60d18065863503960c586fb0dc60c580d506ab0c8b0d161a2c32ac34586ab05cc05a83f502160c582d609dc162416b0c6b05ac312c31ed84d7c0fa82d5052b0c8b0bd6162c2d58339619ac325864b0c66089c19a8205052b051e8403c1628215064b081616560f0e064b87eec2a2a18560cdc0b2c2a2c29ac22a014b8ae3c03b58e5b1c26395a68d58d5c102637566d50306634583151cab19ac60c05eb09a585eb0b26061c1ba8205866505ab0a1615ac2f2c295851b054c07a82e585e58413c16a82d5c5956071612dc15282b585a5859544cf8085845be9245841b08060fdc04201eb042c1f583cb07660e5c03201eb0a0b07d60d2c1b5835b0985832301eac2aac25966cf5c43a603b563c58ddb142b3dac14a07ab1cacec58e1607583950d563558d1b19a837504cb88951cab385845b0886065610dc13558c960f5c6ca8d550c5a5ab466ab3656655a3558bda0d5c66a8d950b566aacd2589159a1b16ac18a05ab33562b588d59895939adcc5895b1226335c64a8c55182b30564dab2f565eacba58855971b1da62a5c56ab6ca6285c5ea8a15989515ab2a56547809facceacb6a8a9514ab285650ac54b04ac1ea8995979513ab26565d565c564cac965829b1dab2d2b24a6275c4ca8855112b2256595643ac845805b10262f5c30a05ab13acb0ac7c58f5b0e261b5c34a87550e2b13acaeac7058ddb0b26155c38a86d50c2b2b2ba6950cab2aaba51595d5945509565256b2f029cc23c4c39708d3843d0879e04784778468c21d843a087310da11d611e220bc417826b4415883908e708e508e308e9006a199108e7006a10cc2374237c218846d846542364218842f08d7085d10aa11a6119209d1085b10b2203c235c41382614133a8566846584648463b40a4231c2304230c2a6f08bd08bb08b304cc845b845a845380bb308b108af08c1845684558454845fc2294229c228f88e108a5005cd449882de123e117a099d089b08bb845c4226c2254225c22da19630891089f6121e111a11161112110e110a1106d15c74172110e10f210ac2138458421fc21e421ec21d1c4ba84398436882f08acbd03c0871086ff021421b9a052e4458434843384368c58de81b340e42a65086b08a17115209a7842508a584324f42f5a4c2439546d503150f5477a8d0a876a0d2812a072a3b5475a870a0ba81ea8cca06aa1aa8e850cda1924315878a062a332a3854336829543250bda172431583e6a26aa39b509551b1a18281ea05aa35542e701b546af80d3c862a0d1519550b542c509da15a816a8c4a8cca496586aa0cce818a0cd5182a315461a8c05035a9be5079a1ea42c5856a0bd5ccc3a8b25061a1ba4205466585aa8a4e818a0ad517d5142a295451a8a050a9409502d5132a2f2a27544da8baa8b8a898502da15242b545a585cb6016301a4c467584ca085511aa215442a8826875993cb80ea91ec03ac02a3c1ba413a8223a283a2f1d974e897e0295050a0cca0a9414282fd20b9413a826c032bc4bea4bd784d6b4f130e5e0b470754c313a99ec426ad1f9d0f5e039c8a77ea365d0316837641e128756989617ded444b817de857ff126584ece846f691a3812aea5c5b49367f11f1c05ee839fc0af781567f225a7d2637c8a4be938ba0dffc2650d837eea3c1a8f4ed33de832cd46d7d1687a072d46e7a0ed681bf41c2d47afd1661a8e3ea3cde8315878b417dd5443438751e5d192a365456b06ad383a8a86a2956885d14774119da587e8201a88fea151d027682ced43f7d03cf40ead43e7d026e82b8d43dfd036740d4d43cfd0569aa965e82abdd4547a4a97a0a5b48c9f380f161ccc033ec339b01a9c068be116f0193c869dd80cc60193b132c35b7017dcd4f1c058f0156c036f612d7c859300019422a6816768a1d582264442257037785c97557874ed43474942200425c9100b42421efb1227434242a8e5a126214882e4c405158490940c193159e2848990508f16179e161330fc682dc102d20a4e869a28b143432a083d411242626105264322e8696dd9a3264c92200d2d111a1a5241845612fb4405274c8460301a72a1488809948ae464a80518924ce05b48ec9012274f9c082551e2e4890494d40f5a472c79a26468c98f56117b04c310d1110c43472c20b6883862d20018aad8ca52426b089e9610fbc29013560b88fd615950f204ca904e0b056be4c20a4c5470f24489101328752804496037ad1388d0c212a4e5c32e612284347464e482921ead1ef66888091213a3272c20b9904468052126506a12284b9c24313a0184160f7b7434d4e48396096a5a576e5824272e3c5122a44292a126372d1bd6a885165a8880aa55c3aaf0e489111421284c5838321a7222822742300c1101a045c32e1972b2029323272e28791285890b45434248464fa00c19bd5a332cd2d09190953d627204830a434321106202a5c2a0c2d010acd562daa3212417a01c3159d26ac9405b5576c8088ad0901113178a868c848e5488022584d6d22239196a418528505c2d2a7b3484f4248a909093273bad29cbc25112168460088110005a25386a32e4c449121694084169c18805a1254e5c781202274c80b4a4203979a244a8a885252dd91eb590c428091421a3274ffb44051f3779ec119293a1284378ec0a4e982c71320445680829c912264b9c245102e4a6073bb4e489920f6e78b0464f54381a3aba638f905c8082e4c29300dca069c185a3a32400b8d9c126414ab244c8c913264b86908e981c190d4111e126077b3464f44489100c484c9a30094192264c924449f23776ec1114179e18ad2004c24d1d7b64f404e9e889039218259940ce0d0e56044ca054a32122a327194041bcb1c10e0db930e40205525832b4c20a495218c04d0df68848c9100b4ae4d82324274346432c3861f237713861a282ce8d9955c2e4c8091323a4178684606802054a1221a3a1254d98b8002352f2a4096ce7068e3df271e3c62611dab969635b104a8294644912a4a108ac6ed8180193264998203171c2e4e6668d2552f2a449088486905c5032f4c210eb468d357ac2802f6895c3e634268f307595bc9dc9649595bc87c6e1c0e1c0507893dddd6d63d336cdc3d3363b5dcac9524a9692577acb953c576a9257fa6ac5712038c7ed72dc72dc4ae6965b6e97437121486f3611df656d77355fb94967e99ddab9a91b77b9ce3ee5aed7685ea32dcf9a9d1a6bbe7277354e2dafb6abada6c99d2ce5ae94db80f5e4ee7a08bc8bdaddad5bd7d825fb328af786019279d9a54bdeb695bbdbb6b194d217b61eb3e4ae57d77dfaca77e74ab9dbb12f6fd0f4dd393bd53af3ca6d79e7e6bed2d7d704dfd42eef6eca97e5aecbf5399d19b53b17e5ecbeccdae429d9775dca759feb72999d771bdcbd61d697d3bbcb7201bb9bec78db6ddb7cdb6df3d6b256ee825272b7b3a574dfad9bbecbd2d77d756e5cb52c15e07b3399d97da7f304bddd5df15b29d999a5af644f4a3957b5d27b4ee621a90448e739d759d376aed7cebbadaf8daf6f27254bb9f293db2699bbe3eddd95ee423cdfdd9a9add4580b6bb3e3d88468178db36dfdd367f6dbbee285ef675ffb6ddddb5ddb004afc6d977a7c62b35100477c1cfd7793bd5f2ceaa7458b5dd92b0f932afd39daef9ae4fdef53d80fbd6ecb6a0fbced5d6dd00ceaedaf575b929f61176b79dbbbcf9aec6ccbecb73b59d6d7997a7b6ebabed6cbebcddba6a3fde9da26a7f7c77e3ddddcd779ddddd572e2f4fdf2dc7bbce73b6f35ddfcde5f27abcadd471b9eeeb726adaee4ef7dd0238efb24be7dd9879a7bbfbf4e9534ab9727deba4f49572d3344d5bdf7566d9bbbe2b77997d35cde73aafe4f5f565144bc9bcccabb13651523aab53ed73d7bdf3e9cbeb2bd7dd775b39e54af99ab66d2f09e0d352ae749752a6dadd7f0019c08a59ca1fd27d5bb65c2f9cd3359f13959abbdb52f64e67b98b626ec6d182a384df6cc360f4a485a116349c10b870d47374346404a585274a80049064c8c8074e0f7085274d7ab09a0c45196a22f4429216845670c26485159e34f911820fa32127432b006005c310919012264d42e00214176b68e848a885274b9c2431a2ac254c9a24892274c42404d0d5b3029323318a932177ed084849908e84888c9e880069c8098f084b9c24111a324a02a585274a4800a14912254f9ab8be178696386122e48449121694a8a0c2935ff908c01ae0c91323a1254346434b8c9e0c3131421200b706188af244c990915092a3254a868446207a956000284a8646583214020044f8963819820225c9ced7d36305117a2c69c284052542484d5c48a2c2d012201fac01546062341445080123945082018c8e849e204181c148490838300c110921bd30e4048423a423262f0ce1ec082d304172e2821125529224044d9a3c31120093165a78a2c48572f28409929224474e92182109211925413a1281288912254344ff2ac1003fc4c24e4b05241784909c30318222a4e488490b1f80d0630df0021315868ca0d00f69e8a847cb28c950132542432b24317a8274a413c4002308196088284993107c48c24be885272240125ac10913168e92b0f0c1d60208519c0c39ef08424e9e3039321a8a22343464a48290d113a4244a9e3469924408e985a19d284e86bc002a1b7a959492942492554a5252fa906c52d296a4a42159250fc9765a5292528764939256496943b25c92d24c4a4a9a4894384922516225159255624d2929c9951c49122f12254e4a4a5292ed1c92554aea2d899392943849a991247152522725311225dea4a4a424a50d89122f92245662244a9c9494a439924d4a6a249ba4d44876916c525292122bb1d254928d6495584949c991ac12235925a54652446ee1dde5168e926cded331e9e43dfd1c8d492fded39fa2ee65341e74b18332a210438a136371030938d0d0b28134a9bf80e2891cc4a4f90126a93ce33dfda32ce38567ec14caf8654c6117fe3cd453a99203eb9e8af655421e6285302ddaa73a87589ffefe4f9c42ec08fd9e3883d811fa51e2046247e8efc4f9c38ed0bf8913053bc2fc71cb5cf127d9a45498767cd811fa7be0618a4a26abf2b0b9030f4ca353fce909a6fdc859b81f3bcb158e0a930b46a54a10d8c46147e8bfc1059b36e4c0e68fed657b7aca230d34c2f8218ba45498488051e1be4a10d8946147e8aff2c16a903ffa19ae410bd4706388263560eb651be3430ba890810c3a600bdbe894ede9e989a354aafcc06615176c2eed08fd54aa3c6c52d911faa7a89e9e9e9e60db8ff24ced9f3207dae86e0fbf969333e9ee9449ba3b65b57f2c10fea09e54a97b278b123bfce4d47e7e2fb59fa75f5efb81f4abdba6360bfb28dad5f1631f3c767777ff0599b20d478821eb8e79e28bbaebe3749b086f0c981ed85086b988abff1515f74ee1275897f50163d7a554a723cbea1e5155f92e469155d25ddfef0ff2e51fbc6724a1ee90babf5c03a57ef76dfac5fd7e4ebfe6efef3c4fbfbedf77f5abfbfd9f205e272a554edcfe1395ea1447ae3a47e66f3f7feb2ae928df53e2c224d1f65dbd75227feac054bb4a1db0d49e9f14532295d457d91d413ec3825c8afe9a383f252ecc9bdf82f39be2d694b8b005e707f5a773c47bd47b8ffaae1d0aee81de110ff4e673ef75df6fed5e8a3a1f2dc27df71dad5f6ad74f872db56bf79e7f44a8f7de931f4ad49e13b9ca91abf60e16f93eeadfd1227eeade51e8fa51a211f5f2b92e78846b47c7ae44f28f94aa7684ab4747f91aed505c25d70671c408b71fe7fb91aef26b508f1bd539a2c99f5447be8ea447e66bd4c89449ac846acd0c6f47d85f903b07a7ef975e8bfcde1475e66f5d71ec9ecf9aa8339febb672255f84eabffd8af37bb5d556db7e357e5fa3b2d8bf89b37a93ab84fc52fc9bf29bf36b917f12e1a590df9b9b2bc5748a3fb59786ed12a13a55d2d8cdf0dc655d6ece3046ed9ab333c59521bbc8f505af886bf7c054bf9fdfe211d7fe061db0b5a344fc5d1bf1facc47e26ad13e55ef1b2cf23d0af50b3a60ab4799a2827c7e2716f9d35307e47f68eaf7dd7bdefc163c01f55d8347a491eebf0f6acfbd67d1fb4efcdec123dcf32f58847ba6bb3efc41fd8963535e1ffeee3d71ec285c2df23c4624aefdfc4a1ab71cbc62a0a9f2b5dfca4ec29df130abda6f13d0800e67a4bbab5f3ada3f8f0b090b69b86d8e3981f46bd4de25ab875d1734e2cde5de1df0690049bfe63641a05f5c67ddd90e5b7e64539d75dc37ea148d90beca3d94ba7def23699bc895a75fd3a8193c2254b7e7b6efdde82e90a55e074240290b0da505b5a789236fdf224737511347a5aa118ddbef6ba0918dea1ce9df7ea3630cb57fab4647ae286822fff7ab457f8ffb887f3f7884357a84bf73fab5fdfc1d6f1377a64353d5c4a9fd7c965247ee3771c7844126b4f11c3c81fb31862723fc1a1d5b4ae5dedb96d5ed3591dbc420d7a4b49fe968f0969942f9ecb195b09da83b3b810cb587a83bebc14c75d59df580c508d29433755402a3d5d105137851f7556c25f42deace821853394cdd191037542075673f48c1e0092832cad4222475ff04d419216a5152aa8e48318041ce107544da328417833a22d1108359c7a334a21840154e753cfae2543999acee3ed5fdbadf3c78455ce57b529c92ce226aa8138dca11f06f3ace8cc8526511f9fc3d2ad5f92cea1c996854f9138d3a1e2955f9736bd13e3d3d8d4ab57f66a94c449a2a56b17afdb5f8a34e71a251193ce1a3dc3045ea235ffb835abe8b44fdf2bd6ef0047eaa45fc34a98e117eff13f89d22793b2b02888ad475d2b13f0a577fa52a5617c5dae2561691bcbaaf34b9d36cef6d7b0c760e91f5d95a94edda7ec16e51e4d667fb163180816dfb6e856cddb6e756c8f6cbb3ade11585276c55bdb7bbbbbbcdbf22575ed507f58adffaa8bec50f84a907ff7bef3f10a2be7beeb7ff40a8fd1cb9aada57f5ab54dfa28a05df5226559f6e54498bfce969e497a3f64dfc89337552ed47a7fcdabb287fc1235b060e49758ae3d49c2880caacce17aa3bd932aed4f9e308aad42675da824501d0d842e2d05477eace7058a27afd4d51f5f4f40404acd3226bc1e089293490c9672db20f6c4062872c4600c8e244930c09cabe51b5c80a80c616b2280aa0327b610475be9612ec1bb08ecaf60dd8d2d87e9213218848434a1a990cc6511915998cdfe908a5fa37b82006566dc020893ca63c3dc1d6a67a6b575ca9fdfb22d471690d432bc4e8a76a91afb43378ff9574522a557c60f3996a6035f4cba6f6fbc0690ac726956b6857ffcfcaa052992c251cb986da3fae0c6a7f4c7ee83d3900628a21a6f052462683cdf71fc3da30176592891b8c28411157c0a854f181f933d5c024952a0dd39e690130ff29322d0016eb92827f6c02ded3ef4eb12e29683f692cc87bfa351aeb92c27ca7b1f79efe497fc69dcdfcc7755a1dc80086163ad458a28d313866840b53e2618c2b8410420b3229a803b630f9e34f0a2f93c1bc867e89e03dfb2eaef75ca91b429254e2964fb894e27ce68dd3e67c7f6efe36677f0c12694d74701a51b75df3a5c8b5889f8ae4a452baec6e81123c3c18421cc83c128f615821a07ffd5a1d78da7b9ac815f51ff8bb42bc9fef79a2521dbfed89b6dfff7a557766c3952ad7677eafd80f8adb7722d77e8e7b50e4783fa8393a7ecf5d31e34bbdf7fcfbc47d54ef7b47bcdf0880bf74e4ea3e83452f50298114592d1a45d0033c9eead13a6dd96186fafd9152fde8b6aba3e33eca5b712b288e2fd4068f80747ca16e1f279ee0bde6711f2736e7edd760918f8e5cbfdfdfc022a8baeb13e41f91a53a46bcdff77e2911eaf71b2c82faa545b1baa011ae7a9488fb468923f77ca49f63b06e5ba72845517a2ce7f09884f2c6639652056f0f10f277cebeacfdcb714709fbb92f0480b72ea4d898e18cba3d58567b5508776683536559ddf71a485c1212f96f1d595647ffee411c952b3f8328b094ba746459f55f318aacfb523ca2aafb2e8e2b64949216a9ea8efb545754127609fb0c167d51589db2ac9bc2a2700039c89eac3fef2ee762128f4928436fe5da4ca05fe37fd1eab867c897afc9af9afc0384f37382fa35720ee8d718b2d02fed87f44b56ed35d0afadda232123974cd58254cda68e41823a274dd57e47d334ed9747a3447bc66bf05446b6dfb636d869dfdc7bf7dc6bee1ad7791bf72e46e1ca71bff1b6a7dcdfe957aea30ff0e716ecbaa2d828b74d7b6f3df9792bd5eef7e4c76d748cd5ed5d9c73ce39e7ec8ab6a7a7a734757bafbf4ea323d78e16f9f7ee44be8f12d6265226a58edd176d69a43cd5281cf8f2a74884fab9bdd3311cc771ec641147a50452aa5114ad6a1f65ab474a55a3feec2ecaefe4a76811a7a9285044ea22af28aa63846775fe0489cc979c38728ffa2946e93adfebee1ef528b1a36351acce7717c7edb9a3a352edd8f362557be7446f5e20fedc077951ac6e1c0a5ec723a5cadf89455e376eab148bbcce77d088ac93126d4764ddde41231b25e2775a14aba387b5454fb5a28e3fbf7f8b3a9e8bfc1e6fcf45fab77723fddbf69c11d207a572bfa01124957b6e4fd8fcb7f738b143bd3bea39ce39ff4e34e250dd0735aa7bcebbee3971ece82e106fb0c87c7f0689687454aadabbc871226be2c8757f8a633f8327701176316cd7bed2c72a2e639880cb0abdfadee77bd3df63f067910d03f3ea4ebfb4dfff15f2fdd46eff1377faf553f73523dc73cf3d8b5b19dc225dd82ed477bf34c83b71a75dfb28d1e3c4285c47aeddb318856bd73d276e95cdee29a6455e758a78e567faebb3cf95133b4ac4bdf7122ce222c0bdf7792a54f70c0e6d8fea28d7aee5c491fb0e4547a58a7a163b717c007ff741ce627ff72d88a2455efd3b715b5c6d137daa26e6b46be77cfe4dd4615ac455a339edda1f405d9008ffc6738b27a5b82424922f421d2baf90f1a74ea5690455a737b91f251d93aafcb18bc0ba5127d5c2cd5f91fb2962a0006868f104db97e218ab72d4be89121075fb31a9cedf4dc06e01c69dd910838ac475a34ef68d5ae44f4f55a35a62fb49fec41918476563ac4e3aca1fbbea1fe45214a1ee2fc8812de30a6ca528424d62a5d93dc6db31c4b03183190bf2788112b005bd6d6f5b2c0873dac56f23e68e2f956b8b4c0b8d70fc20412a3fff907ecdcafffc2e122aff08c406cfdb4fc97d30415177c674441dbf36d30ef51d0848f7f4f77baa0d2432b3324690ad4b847f410584a34bfb20ae9f7eed181ca8d47e1286ececf4cb9f6b57ffb4ab37da5294a29184791e7f2b8e5c81b4cbd5aefe0d84a3abf607790dfbe5544963f761be5c21daefcf15a2cde90bc29083fae9fdec7e723fb99fef718fe2e64feee77c9c1592d32ece861d6a504beefdeb8a59a3a9f6f333c8bdcef65ebbea6cb4889fea7602eeb58ee6b48bdbc429b5e95b3cfef6eaceacbc61837e62e6b3ae1667c6487fcf5de45f50861ab4ba332f6380756756d2b08b5cbdb9d59dcd6045ebeace66b8b231107583a8ec0eae8ff87d20dc999533ea287736831175e4f6fbc11567384195bb71a95d9ffd967578cb42d8ef7dbf46e9ea97bfbb3b0d59aabb4b1e8f792b831d5042994c26851b0557eda0e6bada7bdb81706756b05411aabf27bfe557af90e540b833262a75eb564ffbe40ae9baf347aeeeca1532ab7ca7a3bf0b391529a7f6bb381a15ed53ed47daea7c6f038fa8aaeffa8cfedc1ad15efe8227f86f1fd41324d27d9007b58b5b3bd1886bf497bf893a47b4ef6f4a245fa3bb3efe5ba7b8758244246527ae118aabfa3771d5fe910362181fd91d54ff169db8aa535e1f7f07517055a7bb3e4d398ea5185f2aab6875d69e7fd47eb3909ac8cb32843bbbb2a576dfeff5c7e009db73df8923f744a8efde5371f51624b27235ff7b7f4f13fbbd94a893faada3524dfd278edaeb1841558d16a96a14a4aea9ef5f34ea4f05459dd47355bd27bf16c7eefbbd1675bc97d5eb82464259f5bec5aede07758b5c3d074f483d28aafe1353af89fe81a2ac9efc3e71735041d1ab37bf4fdc1e5450eceac9e69d9bd45245fc5455af89fd9fb8785450dceaf54e6e99d239b25c3c3dd57eafa3121899acf67fe2ae5135b145ed5522f8410e8ab37a2aaefb44fddd8ffdfb0e01a52c3a074fe0ef3a221de880a0ba0b646c068bf07f3f8346fabbf752a2f71e7f29716b50bdfe52e2e2a07afea5c4b5a37af2d344ef53e2d156a3745dc188abf7534af5ded340ae127abf202f85de07b527c122bb46f5dedb1942ca4b893f5513378cac04d5e3c45156d4375864c3c864d5a346457b4626ab48ac0afeae0f6784f455f0bf5f1005289c387a45fd271a1599507b7d508fa4d5ef3dfe3871ec8a7a4f34fa1e49abe02f7844a86a1fd4a058f4a36aff894526545e1fd47bfc79da73e2c815a5d15d2028ca795077e2564edc6f71eb268efc20832b579386ed92b4ba12c8db174f0321ffb64bc7db5f9e7167d57f7f9f5b21f3f7bd29eeb4cb7da839ccbd0371efef72a0eefd7f48e8d74ebbb4f70fb2427e7db4f7efc4b07bb4e744ae7bb40ffb35e2547fd77656039aea8fb342b8facf9c7669e256225d7766c310f2837ae5ea6b5733a8bd8e7cae5d75e4efcc0622ea3a6d01a3fddc684ebbb46f8a5ca5c8adcf7e522bcd6e383cfe13b67a2c8e390289ba0cee6e502f93e71c215b605263e62741227567361451b75d122c9235677df62548a4889f6a8ff5e997e009a24dbbfa27b8cf22d7158bf8a9f24f9088d31573d6a77f450c843babb2430d7bac90ada8bab32a65eab6ab5fdeb68a83fb584cd59d55e941ddd5a6f4e6dd52120f9662be44f2ea5ced29227935c2329914915c567e2999c9789eb7a0dc9fafbd37454d9429847362a09f8e4a557e12cc7d410df8378d29c162f21bd62ec69460f2837a1da87fd4e8a423d72d8570ebbee72d9802bfd35812ac4a972213aaf60c9ba2a423d7f99a387a9ddfa27f904f71ab8b0d5b300587f107b514b7b6b8b06d20569249dd8d83776442ed0727c0dfa216122c29c1f85dd40243ac4a0c30f09b005382f107b9c760112d30f037d5428218f693600b4e80698c61fb9dc4ad016f9bebd74a8f43ffa4c87541ff0a7862eee052f757f498c10760618010cb0f6a4939ea4b3c22db2e95c70c08177c805295922a3137c0db0384fd9e53fdfd737636f4b7a94e016f0f10caffe9d7c805e9d718d6f9f3a7ce9f4f42bf649df3bf5fab05953275fe00a9b373eafcb05ffd73fe9cbf3c9312edec062a76f886dbdfe17e831050aa633fd1d2a2a4babf3c4e894236c2db13c29d2d51a93ba3f246b3bed692086f79c675b269978ef3eb382d32260c32fbef409d6ca3e052775feb3406a91b468e1a5e269321011b8180b330723c51c70d53f7bdb9ce90caceb87e6d48a5bd78bcdd29a66c45b84e6d8527a1a85394ebe37454f2a114ee2c0554eaf8ad5c212b848b18e01156e1a996677bd4ef7f73c59db0f58c1aeab853fd7d8ceaef1018d2af9aeaff2bd84242d7d4f10322babe5d4eb4fdd7aeb9da76cdf1ab4dbb1c056e1e831198bf7d4ebf7a52a27efe7ea639edf2ef96fddac4281d017eaa4d837001fc1d6c30c2ae6390ea1f8413f03c005821fe6d600468af4bab5e37100559f9b75fb088d78d1679e59edc9de2727a7b239dad5d8b27df97f3293d2a80d089ebcd068ff73d20edda77ed4aeeeb683f7f026bd32e1d8d1699613146959f42bfbaee6bc0817cb61fe48031ea3e04fae5751f49bf56cc1a6fd4fd262ff46bafc0c67d838a94ad61471df70c95ef4c9715f3821deab8692a900798766d9d6205f048d3aead52ac401eb2766d65a6babf4f42bf58ab3a0ea915c863d6aead2ceeba284f8b5a361a2eef3098b0a68e61dd1914606af8cad68e84a7fd095c1b0a20aafc09450d3584bab314ecd0af77b0c8c82f7fa3926a4c2991f6fda3f673259d220e19a1f6e39c2f5fa34493723fb99110b3033075c5ec804b25405d313a485355daafafb8ef59b56791d7259f4123b28e4acb9dcae91757ede587fdd2e937e22a5ffea43a4d8b6654c6a83ba342a66aaf857bed3fee6ba085696fd32feda798e3540bd3c3383a80703e952e2effbbfb0a28b8782aee39518edb6b2f3991ab0ad254ee3715cc51b5e744aea25f9bb875fb35ca473db74546f9ddf74ba3e3f628aa6344fbeeb5efe8d72ee72a5311cad728d1f6dc3768647b0ec729dc28d1f6da8fdb77df19d95e7b0723a0d18d72346752e945ecfb521f3d1639640ee7e7fc0c4a2a89347313cf3c6e9f7f94b2ae7bf7c875a3236adda5bb7bf7c815b574dc5e7b141db7f73ec8477eafdb524dd47e2cf24aa4fd9c7fa454643e1745887a144a24da9f707b6f0d109671546b8f9a20117f4ff447c98e72b86ddb136dcfef85531db93aebc24ced1dfe8d8e44fef29912693f37517b16e77bfe3183fe9be8cf1afddac5af5dc150846358ddc3712794cf39d06e8fa41d52e9c25fe5615dfafd3fd0edededed40edbf2934755127059f745d473e7f0518b6de4329d0b00fea2456e20641b86974e4aab6dff513f1bf768a12255fccdb0960de65e69f5432ef6a9ab6ae7ddd8fd3344d7ba9695acf95e2883439e67ef052bf628a84ddf79a9999dd7bedbbaef3bcaef3bc6e13919a88e7714b4f49707e9ab7a1b8aee3509ba77d1394295775c8ab6ffbb5aad4acdcadcdeff39eeb576fd55b6ffb3e3af2d42fc5de73dd2f536e3b544e373b14eaf951cf14896b87a2a3f7ec3d117f47bbcef33c8ff37e7bcf63caedb5ec9abbf26b9de779df7f2ffefa7cff7dcff5d6755de7799ed7317f410e7eaf8144f845e66ef612f66ebb9cdac6cba5e4faec7b740cdb65e5ed046cdffcbdfdb66d5ff55e6366d0fb6ddbb66ddbb66ddbb66ddbb66ddbb66ddbb66ddbb66ddbb66ddb36cfe3761697f35eebdefbfe7e8f3d3aca177702dafbe691abf7a3f75e7bef511473c7f16fdccefedb72b23fec17ffc70388c3db7dc9eedf9794be9d80ee6dd5ae90fdb15f7abdddce2ec5f336f4f85be4ffe98f6b57f36b226b1af76dfc1a1d918c5f9999c531a7b6275b4ade9992ebd3cf43646c12a4f237c7dec6729f12c7afdc73db8e94524aa48afaed3d29bbaeebbaee03b9542af5f2535beaeb3ab0c957bb50f521cd14f852caeeb7edbdadfbed79fbc475b0fbae03824a89a394b2fba9fb1f7eea3931859a9fcb97f4669772dbb66dda442ec541607db8f7642765c749d9afa2234f55a566f7a8d98144a4f4a474299fab523ef892038dc807e9d8a1d03d91fc8f3e77524a293b2965d7492965d76ddbf65dd7759d945dd7a93ea855af5271ed5249d9759d6aba4af541aec957a9b8b79192eba4ec382939d54647ae6eddd7755dd7c9aeeba4ecbaae9372dbb6efbaaeebbaae93b2dba677b2ebbaaeebbaae935dd749d9755d27e5e649d94929a5ec3a2965d7759d94b2ebbaf91e286eef75a0f464955276db554ad97552765db729d54ef4a494524a2965b749b93d4bd952ca6d6b29b76d93524a293b49a5ec7e942fd9755de7c997f44f765df749d9755dd749b9a9a4941cf75cbf7838666fca5f29e5771bd76d3f769ed71175dc37f7411ecae7a4dce1723ea9aaa3f4e949297f3e1fbb1fe5cbf9f2594ef71149d6f973529e0d48dddfa93bbb4e0c5a1feda7b653359aa37a8f8e3cd54b6df351dfa34022feeb946b573b1182ff895cc37e6dbbbeef8bf2a31e2955a73a47c0f707df2912d7ef3bd0883f48c7f91ff5effb3e77ffe644a5a6cfe9df37fdfbe6f4ef9b7352cff3cef33cae5ddee6791fe42a716cc253fdbd215e90aabd4d389f631ebfaa3de77dea5162eabd6e7e200882e0836290a3c411c9e7f7cd39bf39e7f7cd39bfef4375a8df2d258e412acae79c737e73cec983dade9b734e50ecde9b73ce39e79c7382e357512ce4046d56cfbf76efea69dfd4c022fe2b6e75af7322ddf7b77d3fbabb1fe9eabfb9bb77ddce6a875a70d291ab28bacd27617d50cfef397b952fe9ed4f4ffd111971dda7a7f9fd38e744ea6a4245a13ec83d716c12a4faa37e50220fc7bcdbfeece8b74b7ba7dd8f53fbfe2ec8397f6db989903f55474947591bf44f7a13a575def671fd1add070e1973fa4f7fa2c95d427e17570857477f71899072e30b55c59c8393b373bb74828efd3c86c736a19310a6ca0f639323bbdda59c5303896c2011b98124b48b9ffb77ffea1bd72edfa88f5f459a755fb2b721f354b6d9976298a076c9719d48689707469eba1f438232ee54b1094fdd21558e41aa74b54bbe47c720b59f7b20ae6dd7f87577365c3af66fdf2448ddd77eaa1c8110f976c9298e4dd6a9ee4bbaedda1de6128e5ce52d35be509908f7f9397ff58f4e970865ddbadf621050b8712703e6421bde0feb90c7841d1c86cb1cb59f07c856fb55b577c6254b4dd56d85f0fa78de5191a00d5385026d5362ebbc70292a3af00b94ea0c0f74e3f333205747aa4b0f54a8a550e2b05ae9c09247d47c5b6c5050dc60d16aa28bd68d4d0dce0f5784c7ae39664d9ce12dc2f889b0422458bf3aee8846fe4651b8ba5ff4358a5767558a1f544f7e52f451bdf9493147c4a9def649b1553dee93e24df5ba4f8a36d5437d52aca99ef7499155bdef93e208d5033f29fe542ff549f1ab9eea9322aa7ae127c5ae7aab4f8a5af5589f1467f56a3e29caead97c52f4eadd7c52eceab56c6a6e5a38296a8414d602ecefac7efdda993761054cf3e75517c89c8053777d8898a34cfd40b83322c0d4268205448c5177b90eaf6d6e664e94f998b9db8939664ec8407ac1c10b931369e6172f4560d1bc80f1a2022f49d4e5b6700c978a41f7b19733a88fbde8c01b03a68eba9f27039fc0f2040f281a521f6fa9abfaa0086f6c982053c7cf092e3637df2aac3b7bc20b5650dfac4fdbb8f8812762503ff0d59d3dd1826a7333e60a1e38aee663710e1baeeecc092758ab5095023f0fd571359445e5093b788edddd5d5e269a36082e53fbd97fd18ceb54db97e98a453be31f398b6b56fb47cef2f2c74533ae93a4a3cbe9e8ea323578a9cd597e08bf47f3dd8febd4fdf7a3ab8edd04d271d1d4d1cdc82bb28c03c5786350d1cee23d8d87182cab2ce128b7d4fe55bd096752d24bed2d6642da5ecea8687f415353b46753c6c4953a329a75aa2c533d6f59fdf8ac1f736a7e0c6d7ebd6732d5ee7e4613ca6260191313d3952b57bca7a7bfbfb160f19e9e8e81b3784f4fbf993a7613d74da19f71a09819b0f0fb1dcdcd4d4bd66a7dbf6472209c8fe57cbfabfffb73687fe8404bfef4f4049b92faa57ece81e68085f4875574e9a6f54935b0f0c3bfa1bc6545976e7056ab5f75dfb1e8d24d0e0e8d81c57ad6dfd0f51ef0c15f07aaa14b383f45f54b4b53527ff33934869a9aaf791cba3674c9e6a7a8bc5fd8d2d294d42f6c29c67400588bc610d21854aa57bd0d5def998c25dca8f735229711d9cc4ae4332a91c988ec241e29d51384fced67261db9c95b3e3abaeae866bedf13dd0c10e1c8b27132b16c055410810219dcc088a62a73ec60063a9e60ee657c994110b23bacd07ef61d7624219341a6046cbd87fb6de3589a4286052fb802b6de83faee279303a9ae34e101068b5316323670a02e638336c3850d60ad44963a7653ed2566fd6ec681620cfbf19e7e141db98e8e1c47c770a363a8d13167d231878eef74fc3aba99fa539bfbbd784a5fafd06c67c231e7ebe812c25842a3f9daefa352ef5df7b11840ef1b85f2680cddf7f2d7813e1a034af5fceb404b1f63098bb86e4debe3bfb0a5ef634c0780a9680cde148fe1db34341a1a0702e91208520d8df7f87f74e9fbe87acff6dc34be47973c8ff28ce99883a24ba89fc2fd128aaef7c8e72f1a9a8e2e753f85a331c85fd852c75ebc679361098db4efe71fc29d2971a6b734693b53e24cd53427deb2c58162dd27d5c038d43f8acbc94185611d794b8ce900b00e0f27d8148f61bdc78433b0293296507ed3918be43396b048ffb835f109423e41386e5934344b86b7741db7a6adc981788bf7f8735c18e6e4d4716bfafa55b665d99ab62cfb461d35270d8de664803a6a326d8b269b5e2699e9c5a68eb2cc64926550759457e4177965d6d1676ec6b1f8accf3896a6339d45d6b4a5b3a0d932329a1f968ddcb8647e46ae3aff10320a42aefd236fa9be714ab35566bce519fd4af5e71c2836c5d781244abecf647326db7ff9b9377b8ec65c366b998ac6b8ed63fef263dc7f34d667da0b949f291e7629f74294831ff32f6ca7585f89b1536cf94a166fd565b449637ce657f5b3cf65335938fb7eba339b17199e01bd74bfb5dc32e12fccfbb1b380bf30ee37f5e3862afa29ca39811fcdf168d8573a1a7234c729a906b6510ea6d18549baded3ae52e24691e12dcfc867aaff3a10f7fb9c036dbf1f3a90f6fb390e347fff1d4882ad89537c8a2fc529be89b129fe9c18c3f9a41ad8f672fb98f65518a67dcca5fcf972c671b39b5f8561d31f75530685baa1312a5540188e3f15261360375f85613738fe2d5a856137fe369465dee35fe3cfa2551886a2b11feff15f5128dee3dfd11812eff10f6915864d7f157deff19734c633eff14fd12a0ce3682cf41e7f907216eff1dffc3f5a85611a8d4dc07bfc3dca57bc673ac92b9a16ce2cdd8f3c934edaff96f9ed17c639ea472e83a24935b0f91d4daa81c9e77e61dc266a62e83dfc53ccf11e7e29f299ba2124d928716f4e5e8f02c0a67819f9638aa791f9d8214c526787307d3b3b84c9e6831dc2b403c20e61e2d96a7608152603c0a6b44cc6363b8469bbd9214caad60e61c2c1d9214c3f7276089308dab743a830f9c0a6381a59150ddc214c9dda214c28d50e6162853b84c9c76a873051d60e611200ac696c3ed301b41d4265ca0663fa81f557f16d87304d980f6102bb1dc27483da214c1fc07c081300604e63fe4c07e0fefdde214d63f2936a60dbd69ab6d19c39351a3a8d311d00e63fc539efe1673a002c56e34ed77bf8936a604c0780357defe1df4fd258ce3cd5faf0ab56c83edf78cb33ce59ddba42fcb73275576affb8025aef192796da3ff6a8fbbc4073b1845f9db258c4cf58c222596a3fd76905982bb0c8a28a2d4d1520b02efcf3fba70aa088428a29be50817a26129a70c2cb1329e8b24336f93afcfd33891d42658a0ac6f403dbbe8a961dc224b7ec10a64f891dc264b3c40e61da61628730f1c0e4f74f2e3b8409084cd2181309b0ed75f8fb27113b84892b6287308546ec10a69c237608d323b143985cb0edfb67961db2d1980e530b48d904e1531ff352fb996c60da3391009b2f830930148dc960028ca331252e5bc8008bbd0c26c0248dc1b05405d3136140f1048bc960026ca3b12438805003cb16323ac026377fccb89c97379ff3b11bee5becf45fd3a10ce1b7ee635d52a01fc2c7c2a77b66e743f9657abccec7c4eff131595fd93a9e0700ce07368fc332f06d66e0c7986c60dbafb64c67f1f12859edbf521b0034d63d0f9d4008341644690c4a18fe0e8db10e8d7d0f1a7391760e8dfddcdc7c8bc6d849e63dfd4f43568f57d1d87a4fff0fca3306815601024bfd8ff6e3cece68a90f747ea7e6bb9c77aaad7a96cf541fcb52fb53eda5f5da952c7c6587c6bad7a17c0587c6586643630dd258152030f05734c66790741629f341fdca3ab16cf6e872703ee7fb69e3fd877a6f56bb7b549f597d97c54be7d0d87c1cda3534d62c1af3998ac6384bacbdc82b2dea59f80a77336d68ac4b0aacaff998f6312612602aeea753ed8f754921f59c97daef5e4363dab3a87f34e61e8dc999f7f4a368accf743426b3aca87b69a7ba822bee98c950a3312a553e988aca8fc932b59f890498a4318dca3201acba5ac8904445d5d5c28aa5172a7360ecf0851839c841c4121838247144186270604337870f422a60820651a881a68834602cb1ba3976e8420a19b8b0ae983a6469ccc034a5290f2988806d4d7513420b5500eace9ae6a85a5d313770e2e1dbbcb9296520f33691e3c270b4cc9c715cd398d4379131c3263024d7e485cb262d663755b1699bd6d4e58e0db68610696409a2cb1c3fc0b82f2ad7d485dbb411b4208917487146197430c1a6175cdd5913128dc6ff00e1831d1d1f3d72705a373635ac55a84a819f87ea366d4a6f5eef29b57740bbb86903bec8618d2aeb7ea18314147800e06997ab5def52ca69824ee321c1d585a4410ba18b29c7a0c0088f7a1752365511437fe2031c61bc200b2b7ca0a20b39b52629dbfba63990355ee002293b70e28c1dc2f84cf383840ff2f3a3421b607a90220799a722b8d801083c3faeff1f3c98c2066840914511646c81830f48f8203f3f4138018103992b409cf106acb5a06387e7c7f5ff43163360b9c2450d24b280b5191d123ec8cf4f12482cf580060b38dc40c304377c28600533f87283322aa8638b05c0a0c70a6e30c68c2d8ca081065128208d1c9e1fd77f9559104e94910571832167e090f0417e7e0000268d0c65da90820a1524c08cd64e1a56b431c3d21d5e7a20248c1b2237b841b6041aa719804140173626e410879938cea08206460499d56c1c561811471e61acb8c2069015acd90c881b803101973353bca458a510860c74502103259ce8a1842742106aa045107658d183531c24705191f0417e7e72f8018d251ddc08b3d482036849ad992b69cea0210e1de4a185018a00935841c51b64a8000e2e3d8c10c4fc787e5cff29f0a188365240658932d0fc7c81657a18e80286145986b8030b200ab0038ae7c7f52fe60e2a8c8841193660700601beb8d2e5d4c0957007d30f49b821c31059f8306dfbc512172d30f802cc0d6d00f9828ae624063ea8b1e58d22e080c100be904d1e8258c10d5cb220b1c41701a491292da898228b1631697ce941e3247c909f9f076cc186193988a288349a70bd1dcdf3e3fa07da2245134ec0d146136444f8f96ab717676abf64125e06e91f7f7ee86a599c00cb972372b0c50d596001f0491de0b4010ef871a0a541dddddfdf00a7414dd7490260786b80d0a9ccd63de7de3d32d3918edd6b1fe41dc7cc7123c772a5f4389179d953fc4c8b9c43a1e40735279e801289e4f3048974923d078d70cf74e488e44f91e3c413b66a759355a3b25b9fa9a57a2a4e137618dcd45e48112bc709d83052264e53e8ef54c7c857ffaf4e77852cb781449872c8d55b2de5c50564361421871373cc21070c0018e01e350ed930dbc49d66c506eb502814e5ca78cf12d1980b9303312ac736cf715c175e3a74a01f73384c172e288c19d4334804d58961bb2090031b5996c8e28c162c80713c950b73a67278803bab6c9cf19ead736a5144993aeb6ac186ac8e61e528c610c38b599823baed5d0b0384b669daf7da47c348e28cf6cfe3ea51770c1ef0505175c9a8628b3abed7f38859981b525cf0c0868b19d87cdd19175e54b06e531d75fc68ec753f865c80a973b6b71aaadbce30a3da3857f8d5396793193fd4c9d5c939336c1773130c1668a9b383cdc951d74679c6afcc32fe314034d5c943ff9fc7d584033a2a58178c1648a9e3ff78ac6de1020a4ee3328f905c00c1f90e5c7323c7715103a7699aa669daa6edd49d6d71a6a2ea36dda00b4ee62d8f14b7765c4845cab670aa5e8347fca95b97cf2adff3227f4a535b568bbaf599efc90623e046f291b86a8414566f7dfcbb2d80d7f9ded4c4285de57bcbbd9ed2689157493949758ccc2a7f56492591652ac2adb38e5c751e6f30b4ad47ddb66d6b3a33abb26e1776f8d0438abb5fcdccccfdccedee2f6b331728d81185182972dc70c716b02d84ba490184b44188aaebc5135de830e68b19df2207b9050edeaaebc51340d4adae174fd8c08b157528218325a1d0c002eba273e48dcabffbebf2e6fb154ddbf5696238c2cde3264e00d77d14b889c15821dcdfcd4a683fee2ebb6e4ea87114aecacd52c76ddc72fa35e9c861d4fdf93cfda2753e57a6ceff664eb5eba47504c2ac697e02ab7218ebb3cfaaf33d4dd3b45d1fd65ea3a3a669dcb43ecd33b73c3c8eb07f0ba68a4346c84ef5870023e8e06b4083397070820c3ce98413ebe32fe488c534317fa77ca715229d968c2ae9285f52765a1f38425675e2048c69aa23938941383203056531667bb6fa2fbf82b250635d5b479e417596a26e1d59756432fd0aca628c75adc10908cac2690bb035cb3785b763e83f72a3c4c24ce5b83194f29747aae0f9b396335994a9cc66c29d6981a672c1c4461db7da5a9ca94dd58ff0174e792a33959a442de2276ee2046c5d8e41b8cf5a56c81153a9ac657df6851c253193556e6a3002fc4595cf60114e62851cc9dff5f1fda232182b64e4ea949bd687c1086385f0efaf10a745d28ad73a4d8b740d1d88cb71a0b07bf6b99210c453bdfb3148f50ec3754f7ff7f46b53f2186fbbaef4de81767edf01501c088b03e9fc3ecf1c885140ddefb3e0403dbfcf571ce8c7eff31707a2aadf0fe2481ce883df672607fa7e9fbd38108fcdefff3850cdb37e9f650e1480dff037e7f727e04020fcfe0b0ed4fa7dcee240a200e8c855017c8b3aae17e101f053dca14b3e7ee7359186f0f4ff37f1075dd200f0aee7c41ebad4f37bde1379e812cf7f080f8a1ff4cfe77e25068006e0b517e16f44912e51f1791e4704812ef5dc3cf73dc4a525105e676758ccaa00de0608ec03107ee755e21208d4ca00604be083f03e1ee5835a49c17ebc0e5deaf1aadfa1566e604bdbfbf81c9db779d4d7884b3a36bff3a99d4fbd0db5e2b0259c577d272eed502b3eb0a5d5efa41ef5525c4a512b0a802d790f028a5ad9812da1dec7eb502b36b0259d97cf12976e386aa5076c897b1fdfd40a075bd2f97eefa15618b664f33b7f43ad68b0a5d483f0935a61c1966e5ef52d71c945ade8c096747e8a40adf0c096725ef501f84f5c0a00b582822db1346a45055bfa1e840700b51204b6a4bd8f0fc5a5294b4a301e6a4504d892cefb3fb5d2035b6abdaafb7ff1595c0a815a69d852f83b3f65490946a99505c096540f025dea6cbee659bffaf0ad300c8417a91520d852f73ee8928fb7c2b09fff435cd2a14b5618a6f32068cfff81b854f33b7449a7c7e73cceb7fee6ad30cc075d5a5a5a52bd00c4a459ed9123babc671f479c80f7ecb7c417bc67ff46e42cdeb36f2372deb35f23fe78cf3e4bd480f7ecaf449679cf7e28f216efd95789a1f7eca7c420deb30f8a48bc67ff1399c97bf63d91bd78cf3e4accf19efd4e0cf29e7d4e64c17bae78cfbe26f217efd99fe27bcfbe141d00c57bf65b642cde33f39e7d254eea694e8bb7f28d90e6a6711caa4bfdfee77da947ea55f82a9bdfb7495596cd2369b5e6915ab54895f3d59b378a222bce23c9da7a249b5a6442d5314262d59c374242d51e6f1445ab3a8fd4d5879111525841782324ae7ef03b8f94538bbcfe30424ad55d1f7ea3285d7fd05e1f7e24adee8320fafaf07f20caf5e1df11e7faf0eb88ddfaf0fb10b9f5e1ef216eebc39f236aebc38f237eebc3df12bdf5e1bf1151ebc36f23d688a9f5e16789e0faf0afc4707df84371b53efc2a91b53efc29b1b53efca078b33efc9f68b33efc9e58b33efc28b18798b33efc9c88b33efc9bb8b33efc9aa8b33efc53f4b13efc52fc607df85d04e1c7faf02b81c08ec65b9e7196a9fe2914a9de7b1c32c23d63cb0ad9eacf2f0865355f0366fb6dc67161baae09851ae3fb9c40f08c54eafd27997e1d494989c2dff0fde71afd3a926ff35b8f2425623deb9f7364a87136e2517f0d9d572614d3cbe432b7cc2366960944bbfc39716211e70e2f00e7489d41d911a2916958328e4acdfb4fa67e156db3867e156dde6f9d35d488934abbfc59a24cd32eff509476b4cb3f25ca394469a65dfe9cd8a1c423106432595d7dd14dcd17b1be2804bfe86873aade1fedac7e4fc468b5fa22a31ba249660236354f34cf80400deb89a6930358e113cd311a1012cda6a014f84433cc10f0e97ba2396b977f9076f91b79ffe191c6a3e33e3db501a68e4d3631d57ba2f9a55dfe3fedf26894565dd1717b7a4a5389e61aedf24fa15dfe44134cbbfc49689734e3e3a726f9a51907f24135e03dd28c3423cd4833d24c7569469ab972e5ca952b57ae5c71200d68f24af52bf38a9ca13ace7f93b3495161c7d1f1251654c7492cd57fcb22b1003973e6cc99334d67b62523970c178e6153f5979ea5c9a3a352f58054cf92c5b37816cf82a6fa7b163468d0a041d3689c9c9c9c9cda69cb962d5bb66cd996b5ac652d6332d5bf654b860c193264c8b0176e865a8ef4c23008d98b065e7b66239c5f1ce8a785f31a1dc39c9774cc79a6e3d71f0db4cb3dd02eff1ef485cfa1507270280b402dca3008476e7ea9fe1a70200e4547aea363183e47c79cdf964cf507d22f7f1eeffd77baf7b7d9de9f4bbdff07fef71c1d1ff5211d739e2b624a448f8c9b989aa21a68973f483df0d1173c8a84a2503ada84a32cb4cbbf68a34bbec81fd168acca97229276f9731be12c533784a4a954d3b02c1db6a7daf18081ddeeb23b0fd8f755502f9d368d2569afbdcabfe5fca4eeb9dfdf69c9f927753f73fa93b497db73fe49a897df9fc4bde6fddc3e09f5fefb214b25b9ef4f9aafadbadf3ec9638549de7ba92a281848c1f0635550b0149df203f5499db7f2d7e427cdef67f201537decbb4ff27efb2a285848a7fc48d158b77d739fe42f9fc9070cfca84717d5bd9c9fb43df7b1a4f91345e5c7923aca7d2cc99ffba4ed2597b469b1a42993bce977092bdeaaf0a42f20a6f34c3e604e63f2635534987fd18fa44d0ccc69ac4a0ae6cfd482c92296738aea89c907cc3f36e54715d49474ca8f5812f82d261f301f36ac30e5ad3ed58e4d8b15a6bc1ad52aa7c5b20953decdaac6a66513b238a9d5dc841fb46c58a94ffa3edcf1ba6d7e92f6aee5039d1e9f333f27874ef9f1b1240ea5d3834ef9f1b12494e4960757603b3e5a36acd0fba4ef531fab8282e9d0293f9e9e9e603e7ae4d018930f180e8d25a1be65c30a535eb7cd4ffade3f96c47dcb8615a6bcce3fe9fbed6349f25b361f4bba79d6c7926a3efc58d2ea531f4b52bdf7b124f0b74ffabefb5892f62dca7343431bfa35d4864577563427a4392aba93a23620e5f9a48fbe474314dd8e7e1c955d02ff46b92ea13f96a4d1af4be09f74bb84fe2449b92e81dfa9ec12faf79374b86bf2b65f479ab61dcdf945475d77f47de01fa954e11fb158357fe47ef347ad16ceef7fbf8e72be88ff28e78b7a5022a3a2a222458af88b6cbec8bf68f545a92f92effd11ee8ff0d3d3d3517f11371d6b730f3ae6301d1f878ead1d9b1b3aba0d1d7356357464ade868930ae9a84ad131f4403a7e1e1d3f141dbb283eaafc283e2a47c79dd5318a8fbad171135335ff498b7e3c3d3d5549c7283eaad3b1c926a6fa8f485a751aa5559bf2bc520e4b2abcfd09f777b302882abe1891fbdc6240e39eccd230c18313e060cb19d1081dba5915b21e3f9ce41083a90674e4008c1f6304171f1574d4b4eacea840230914c7510146e552308ee3260a45b578e115401f8ce338d47c0f25e6a07e0cabf7288ee37e9da85caa721cc771a8308a1e788e4dce57970b36e8a8b32e1765c040f5dce1c78f1f3e7ac0851668a660e146163f426e670c358c61e28c2f56c0940608542c7554dc206a69341ca0b8a8a307f38bf8b522a1227e9a409defa2a42333c834c8b330e263541aa2c8b25127ff91d24ada8076f5a85483dad5df7222ec0ff236c690e22e90f92e8ef2d7887ca75abb46ff454152a73a4556d6a096bbaefd296e959468df779dfc8b9836c0d431873fa8fc3962dae04280b0b9eaa9b89b5cdbc76f0ca5ac524a899add5c4eca77d7efe44829a59474a75d4c698ecc71770689f00a42ad6eff72f58959e52fdce6c51329e068d0a5a8b8811cb2164821c28d3276be94017e71416a96d25475675f9e985f3801a8a5eaed4f9853c32e082fa7d19147b76d3590a16e1b775ac7691d3fc77161e8405339ee73388671dca6cd5f074a5a410db418b3c61b68b4003675ea7cc28b3a7f73c2ba61e810d3b1b401111d74d832858e1ce0c0c547177470a9dbec0b10373675675f98aa57970b3b9e18c21b7f5c898a1414e1250d3475d4c105b61ff6ab475d9703ed6c4110ee6c22c7146ca479b261083072a660c106d69d490144e5ea6a51851a1b370f52d410860f4f347007dae502ec7bcd0738c30b3694e0c4a0618c0ca1ee2c0a382aa846a82df702d4a6691cf7fc9cc609d1440dcab80110686cd901ce112ce0a4f09bba638e9855adee982b70d8d2e4e766091630f113723b2160051c62aa980106153b688de1a09039d43044172fa884b9c205b09baa7d2844d5de4603619ac6fd7c8f13b92f36e6a8120a68e0461271c8f103152c60db73fdb2a99b1a3cd4ad6e63aec0a16ebfd3401a6c7b9e0692b08d8e4d7eaa46b9767d116ee55fe795dc57374c952d553ac14d30bae67e529b76e584dc8f610d1da8a39a16051655f3609aa66d2fdbc574d3d2fda669517ca99a06d3344ddb34b02e19608c9058fa256b1bd9f97283379af0a051264d5d3165ae5403d415c3860b2a3bf119de86dc77963a5fd291ab925710725bf79cecdca94dbbe67b5213c31984f2c7b0ceaf2e368db1da7d47477f0d3c21742014e599f7cc9f334cbbe6773208e58fe0093c7b4e94ed9aec14e45f65270ec34dfde2d7d7a096e2f7853ebfd54438ee993a9deaf875ce473297b0c009b8215c316ca451e57716f9c68e40eafecfc2c62112366e18ce4582b748e18e79c24b5dd51de3a5a936356bcf51a93d6be95e03ea28071bbf3ae72c0561d4f9b2811c36dfa68176f62ba4094f9d6fc393c3845b4307e25f1ada88395c843875c3d0d146957567514051c7b0be13e185fdbbdcf634dd420cc8295b0b6d34cd5beb3ea8432dde6fefaf3d37795dda7b128c80d75149e3edd4fca0e6a436df88ab66d3af8dea4c4d7ea7512da897ff49f99a94bf4914d5422a5d347fd4d73df2670dfbb52fa5aa7dd55efed72fede5a43afe5c03a095a87429172a84a1180421438846684693040d331640303824188d8603415273fa1480118ab458489f4a854994e2280a628c31c6104288218600638c199821ab01403b01a607039a9f2c7eeceee22ac5df0f3a1165807551f1a8b8927df6aa110c1674b1d6a280d2cda534258b42a5c90031f617388c818c37858666dfa00b787d413846b57e048ddb69a24035d43225f49899cd886faecac04c4629171e35786e808d7d18713686dfd3f7f000b586d09f3eb871d9559cf8800e3a1d6f635b304a50c13c06109aff46d0efcda1bc32640d2814c7834bc02484e8e4cd1a87aa62c93455bf647a952e3d3d55a80da40f820edb5276d92db1fe309c0e6b2cc9b2e33790b37d1d988e723ca2af6b7541f35d2ac2a5daeda7f8567d5bc8f5eec201c27e49fee09adbfa923ad4734792634f3969ad4d5825dbd712dba5b7cf12b74bb7cf25be4b6f9f2566976cdf4b6c97d8be4bdc2eddbe4bcc2eddbe96f82ebd7d97985dba7d2d8a25bece0dad9706eddf81d92f71c85acb52b563cf54d1e07211753a37ccb20f82c3d1f419fc553958a2d3260523dbb109123e85ae5fd08d321350bb6151d87e4655e4ecb3e7feced4990259e88b45298805ae594414600b8bf8d7269039844c66dbfb692528c00bd70811b579ace564d99c94ae86b9c928ff509a14a99d0c3e8dda80aef4c441c73842a366d40e35c8c0807f8d8204f00f3e106cae629bc9518a92bf9caa768738c23af7910587b02174db15a8b53112a27760a70da3b23ec68e829e7f338152f6c10c676ce5748b5a38cefd0a9f0f2c7d9fec70c67648b7b985834bbb581a0f7cac0f6cb8b63d9cdbf862b845e706f30a449afe469ab5a498fed4b1417409fcd01820d73436f164f077a7063f8c8f165c9a15921b9f40e6e8de28b30e0e6ad2ba9b9b5228a7f56e685e514ed3df60d62391b6bf9926453a48a7e96e24b38d0784b419fdd1a32f0df10760a278a97d66c3b5db33dde217e32d3ab7312541a4ebdc609651a5d44080ee51e0f76efe3c0b141dcc308b72b5ce88279c18181b407f818d1b85721ac69c16e316e4dca809abc1310932e683f309aebe9be61285e9fdcd6db201013d8ddc1bcd2a8ad3c5df6073120e28d3ba1b32f92899320fa2ffc0461a011945636782d62e0261a97dd2c6795bf3b86909d41c56f476018578f91eec4129a3eaeb0e1639a65f037e7888a7c3df798572b195f0d0de83df1576ab01754670a98381bef29df529bb35b1f4f41b91d8ad3e0f0e79a41f40ef3f3373f361f32c7ac9211aa1cd096e4a4a2075101d2f0bf86d8ad6e9c572102e364dd313829fbf9aef25005ec8c6a109932d071cfc2a523938117185cc0bdffd1a29a8f54faeb6016ec78b862bd6edd5daae08b7f0957722b0ef91812f55bb806a448daf31f8d22c0178a7c4f36de248e997285badc6243092c82972ac31d562fee39db8b4db63721c3dd4449bcbe8e83f30af97d25d2e939386f8ac4b2ea3de570580a4b05ce3b83658214f585e6c9c1faadaf6a40322c1ad58cc456fb5242bde9e9cc9aa09d2f1147d5818f17c69382a72454b553a28f982c27249bb15553e577dc0a038e658941d9bf28c557ebb06132830aca3ffa7d343b7c8445d1552cc25ac8706cf4e1ed92f30de48d5ca78bdc0a339a099f44eee6b9ad9e35b79d94f7f7bcb7bab71ec8c74b4dc2cb423f5b5c77cd0578c8f0b33510664b7c583302e4f5bc8d5b04c3d8b6cbe20d22869abda78f91d434d905555e13bc2135d15c7c59c0c9efa687870314dacd00b916313955581788e68dc1833930808404134ac6f30d2aa9985d19b704a05bcc24e00c710d8c579e51a07446484fa3eccb8c365dedd74d8df48df6b39767bd22f64aced068679562db0316bb7a09038d823eb566ee83814c21903cf997abae5c6fd770713734b0dc4ff5497c0778ef5627283c8e13124e3a54049a1c7b3ae6e5e6a20b3a4cfcd19466e63a12ed4b8f8517d9ad94773d91be6432220f5aec3c506c26b912c6a46db008583d5d8615a2229be045f1f1c15aa4a2a992d44952eb8dc543b1c98abb5dd13475defea9b10496a6aaf6f5270299325f0c7da525c0d73473c933f454aade5c1dfdc68db835a70bd44eb79160f05b890a4cb8781631eb9a93df909197eed0b01fbf003deb0ab9f103086a3d6354f87cd7d2889b319b343d1492323c8a4bfd67b433e236f844583e9da788e75af55f1bb2b3db8b1e489107d25d1b1852e8291ec85c621165767c2d99156e19051d5bb0a1de42a8c3c6e75f6669816e274c032ee232ef33b7f6da653de79d8f7c241d4eee3fac2410c87b80ca209ef5bb10ccc630fe52e183d80b4367867192861b04615e5d9d64da1d5e9915a9f761c93485b771c8e86aa62b70ade658733463cf8cb0f4c51d62ad6e05e8b3393500c76d6840a1f96928bd633955cf22ca935efedbc0a205b4e80b3fde655a3cb40e8c805ea07750444fe4778ae181d9e7c6e6ed99150989a703570a9b960c8fb5da4f9452cad4ef7287c29575483271a7b94d0703e64ac7ceffda8e70c8bf8ed051ff02032d2b6588bd970afab922254b14d1b67c7a4b29733821d1ab9ae3693c5dec62f1f5b95455a00667ac3f4be7a47a3485ca850f98bac91aff96586c133bf34c6ed58e8573d4d40799aed8b9bda480d9648834b13698ae14b4f98a74ce14aa888a18ddbdb554ff700d1bb0fa960eb7b9e42f0ff7fa2b701d1123708482edc3aca6cb544cb6a7b01b71a7bd87ef139b6896f73817b967d89f439d941a7d71e43496da2076309b88c9a58e0751791d15b45e4bd1fe0ef4d9ecff58c0768792283153304a57f9d24d1a79d9cc47d67fb94e54a8e41102ffd3fdf138e6b240e2e57088bae0656d30b411f9bc11339417d68bb0fe556847ded25190a4d114245e13202bf9dbce358983fda0cb91e35e0f36a80d53137b98a0c7d0cc573dd5addaab81ebf62b1d80e9794e6a1471b448813b04f4b26918ed480334a80b75b240480f61ffd34241cb91359dd4f5c47b0ea976f9dc158809628b2dcf1f8050634925056a696b4d85149948fbd9fd0c1b7f9d616e4cd098c620876d92d4087efc6dfe46fb8bc79a0af4c3243eb9482a395b43f04d2002e1718aced6100db0226ac805902d56cf1802a51b59d474f44629840bbfb80d7fa25730490bfecee75cada6a75f6e7280c009f322f222954bdfe81c58954a0bb702b39c46e69089925f111bd89a1eb81665ec7916c52bea636998b399f7004ac3d73ca759730a303dbe41a72b5ad93f6bca29a05b5e0ab0be5c9e491c0bd6273c06102ba4c16ec205af151832551d561098da092fbd7e096467e9c5d518c97f4f6e2589c187b4bb5bfd205eb44f66f015ee7fb45d7baa5f7dee61cd963fb44a43ab971312870b46443ac4ae53cff43772dcc4174a7494775ec9de372c7da7e52724001ad803a9453456220a1586e2858ef6421b20491b34e07e55e3ed4a96df541285189f03a534bdb382721d8e9362bd7830be12bbd8d85260d7ca41b0f34dd51da68a7bab30a6385aa747817f481b5c73e8288609949fa2c92961456ecd3585b6f0c70787bfe74430960d2e17c048d50c6563f000776121d49bc42d0ddd4ce71587bd60d54fdc38a499e941dd1869cc630577ae0f21ef457967f7dfa2b69c1b03090933aeb5904042bbb30bc6146404ce4784c1237f8889d2760e9bdb146b6f40d62fdf881e91b564acdf502a2ddb871f7da98d10b1454afda5a63d2d7dbf85d54684a4d8efa149af22bcd91d1215f68febf89dcbbed5db47d856fee7c8fe1412affe732f8c824645da592402455839ce3c30590ee47fe45712cd937a42bce913fdd785ba56ea4adc4cd1be3ec4669f9a0f12f64135cfc13d343b7d78347bb128a7318d715b9efbadbca25e45b36b537d2c83f9ccbd237f1a4ab7fe8b5ff7ded53a7fdeaa9ffb4d746535c644b0ed7e9da137d686ba37a39139ac23dc52ceb36bd73796a9f7bd3607b19f9ddf0952ed06a55bf9d560ddf1cec7ae2e131ee90b9429f95dde146f2f16ef7bd46c25ce7944cd4a46702ee632595adfbbf410668a23802ec85a1dbdf08d985c88ba6ee86e5ddc339857b723d3ece092021790dcff2fc80c9502f6c29bc1355cd68c7aaf5b4fcbad3025fda1785b9820fc45656e8edf87c154f2436b1f52256cbad0ab2d4ffc69f6226ac019dd58bac0348619d1566648d5a9721d2284c057f627e3113192053bb0b60e98a5bfd70d8ce342aa1ec99baed92ac9c6d6e1d57cdad61db5593d40c27a2146ec944ad028b8d07855e6dc100fba6e5944aad0e76bd06651babd45692a60b943e38a1faa9f7239aef97c10e15fb3a9f7fed15fc31e932675e8bf807b7e293b2dfbfe2b2dbafc4640c0b5c2e9342e89d6bea1dd56e9123b4cde15a58aa38a63f8120dde52e63d933fffe259799bed836dff57e4d5875ccfffcbd4ce68af5ef87bedcc7d30ddecc35f1be080237606a3ee85ab2fb51997ff8b060965e680ef56234da17bad10e278986de8ec9451bb7c8c939683a8ac2286ee4147e69f50aa26d7f9719dadfb6fc7a38f45e533e6b38c8108cc4cb85f47fc2a12dd32f219aba3c2ecc62ce1fac84fa13a5adf72edbc72e87843b792a03ba04e83aaf37d650fbaa6d2f025a47aab143b94b367ee01d862b4b3d355f2781a6504e2e94c3eaa18c2cb685a701593c2c8c3e477f7d820c9525df1d1f1fd3db3b4fd14bc19d8d9d049fcf07e2c9d9f85675a3689571ed3468d0704dcd284894b74ea5ce0ec13b5dee32a376f90e8bae0002cff56ba57396568fb7b32881b3a8be63c47ce50955396ee8fad7458e70aa0195e931f2b04a40964c5f7950a461b9e0fea2f4dbcc232091a60d8386595760578f71d1d8003376f484e8da4b2d25651b8c0dfbfeb67acb1caf68a4557558923a0d0aed18496a89e0cd7d4f9a7efb75c18c39bab58bec3b0a37e38eb3dd321e7a9f56058a1d0052d16c0505e5d13466b51697f15f13b38c16672141c58d9f1c2b6edc1ff0ff0d30de95f651aab7586be6bf565717e237aa2dca02c7547850987a1dc94f7b2473cf0886d0f527313492acf12329d68a869aa431b973fc592ecffb9faa548b467531b58473222bd04a0b3f7d8043ddc4754a9d8aaa01391e09540cfa72cc8f38a7a42577c3454e08c7900b1ffa1311bb725fc46d99ce6dd4634969ca8cdb5564705ad0764ca97f694f42994f953ebc947128846a015256b122a2cbe9cdfb03ec6c18e5816e4ed2b929d59da9ebede6d150563a05504fb98d429fb745d8d5f39eb9cdf1f2388e89edc7e72cd21c1255b651272cba023fe058fe71a976c43ac9762591abac2715984beed92976f257e4b0c5d43d064ceb2e09aaefcb3c1ce08df6b54070fac29b7c6079183b69fb25841899fb3da41bfe70d2215ea78753bb4da703a31a1a9d3fda8ba713f97187ab2bff4fe4c84b5130289f74a06fedbacb074335ea8774536dfdaaf2d130f5e8384d8f7473cd9e05b25a505a6b9a4a75fb6b807d6ceebf501641e73938e78c4386e690ba175e58eda7cff29cac42dd2347dd83eba11c5a29877e9952e7a653524210ff8cf8675342c8732ed16906cdea2e7c973cc02ad339514bbc4754eaf1cce24706294e9d34ca93b52e6a94c4f7b0322a2fa000844e820a4dc71d4a222e95ee82729546f0e6f1fe9f8b2910974b5c6d88180d75b63a1156f780d97e8718456db045fb7255ec801f17eba0efc8d8c2a905c8c0936302851554d425f8d54129f615ea55de7211c40f1af5eb4f2721cfb5cd3fec4c6f0e10d050e1ea92060c08d3e10af805b6784154816fac80d051f4453f778da88c6f70c3dfad46e0add126cce4f8923e2d5c99556a9a8f56aded805ad444c6087ae63ff1a72b1fd133dd67024d42e88f3322494986e8d0c8eee72bd74e3a74bff6d703add006e7d2412359587d40c74b270be035dcc8c1a47f38660913bc9507bdc4cffef15a1192f371dee83405884fc2c9839e8b125de9f2c1494c79a728146c8aac2781381b96a29c2e63f78a2aa83a7d5861a424fb73352574919c21073d01f1d98884bdde77713fd80dd8c2ffd62566d487fcaaa9655f8e01eb72e67d11702d2e8a8ffe263fe57972338ef749bf851fcf6894433c611fc84cd5276f498710ae6778ce02c7b731df6e800b98025ad4e7d5097ab561d7412aa518fa1c313a33551a6c4de8f04928abf4ec7ce3706191081d320266aaad508e64b49f682bac3e4557861c621a4d5a8ea6c2eb3bbea1c3401d691a1b16113e58441ee20afc2917677315127f0b183fca756dc50c3bdd068cfb4e4c3f74cb20e927f6e8f618c9b5f13b9c614664b6036aa0d39477853d9403715f55cfe86bfc40b82ce7512a23cf80ad0374fb504a1a10d64a637d9506e957b7c8585cd4e1535ee6c147efaece05391b7af8edf2667257b97e4bbead88739d369635112ffa43d5eaf5de021c6395cba8fd402857d858f2b650db5b9bef9e859f8c1529781167fa385813c9e6da4c8c04ad71bf537b3182a15e08b20db8d67a6a8e62ca35e2d5dea6edd00925f299974bdbc1338d623c4c6dededc823e0639eed4de0ca8c5f0190b2d0606cdb6381299fb659cf6bf8f1c84eb9f35bf887238d95a03e168709be3f070d696f8d49b185c734c51770ebdd8c1c263d6a726cca91d5f9222f86856ac60616d04bbf8c2b694bf4e2caeed56385852af2b1a147ad5ce5077d9269a0ac5fab52063c6a4cabb4c95fa55e1a645f067095bacacbb8df523bea69baa3c8d04156b31983c2d765a4b39bc6b08caed0e3161afd0c0ec9a3b6c8bfdb10e0df7b98777feebf56769aa45da60d87599fea448cdeec7790faa54e62ec6d883e5cabc63903fd427399caa976a9793870b13582224846d93b5341fed2a4ae268c98958f646bd63e21ea4573f04ad500431c278a8640ae736190977d2d723f222be2520bb6135f59ce83ce8a20a9f81bd48f7d52a481be40cc7d3b67a6c86625d0c755c476d16240febd7737e0862b38657505cbd9d78616170d352afe10cc29ada6055e0626a1239f71e805eb47d48ca9a24eb262b46239b015b31354dc0bf90963e8c29b995b441fb953635119173fde8055106cd14eb8c9f67e053f78e8e0e24e0eda9f4e132143795f1e8877600a8f0bb47894abfc194cf243808036150be6d44be1561ce119e9a56b41c4440f3f556c492e2e9fff5f748d1e4adc4ae9db2500c8a381b890f14fa890f47f8c47a577600e65804fc9b0bed1ae0479a9d74408b040aa049a2f45e17e26a9f757d3098a25d03f42c54a948cf2441981477b13cc6d6209c870524b6847275b2d3d34148ab4c7dcd8477a595d5e8a77081d0a72696c277b62c6360e685d11ea9a38a54063c291dd1f10714886411dd07a322a78f59ba2ef1aa13c0b61bebf0495990249a383c0d8f95b4aae9178960e178d281034e1ead4125a8fd957130901e739778c08cfafab0a2357b35337cc6be801274a66a8e30ea5ea176e7f03dbdcbadac28afdcd8a0698a4a3f56ccb96a7836decc392b9f121b1f08743dc939e9f1f30fb809fb3a3355dbe45e54461c7eb7e289eb24a050c20c636c1522de730391c23819809d12f0c256cf839b89f82b94499b2447bd4f79c56d22d2cc4c2c246dd48c5d7fc3d07f7fc57ed2fab19b070743d073fc3031e91a97829581d82e07e22c4c81ba33f19a512dff067fd0ae43af4f38e779a1eb143477fc80fcb443fa87187dad5f64ad80aa0479e11ea4c757509dc61ba200b1295aff45fe2f6c02c2b683088c435219a62d09e4bde1ef21d04d40cb1e4f79ff7d92017fa638e90d05c0acf8cfc217e28ed0e8e2186aa78dcf0a2d971b1c3ccc3a5afd300e761471b6f9ca0e97b15301ad2fed7c8ff43505f42413896096fb0eba5c759bc82eaa96057fe7ab17ebcee29ec4a7b778b00dfaa7a141077aea239703553b34bc2ef0602fca24ca1bce4423cffc537d25a7eeac56e080f2cc7a8ba0e38af008a08e2535489f726d027e7c0ece2240de1f603cb54ab2e40bffc90f530407772720310d4f206018a85f4844c52dcba37efe0249bcd9b97d7e598843dad0e6319709695e215beeabdaf04a87530f35981bf454529fb75fac72c1d0bb4a60cea003f23bc41ab3e854629e89290ed4bfa3238ddfd99c4c13060a6828e59c9b98924eed5ad37d26564715738ee89811e2c8440fb9560c081e1939ef7e487733b065a1d07ab9b1bb94e868a37d5b263c08b78006e11113344507dc2fef39357450e737e465fea32356cc0999cfad0ba8e4e9ba953725ea8b3ea81f5da66574bec5b86cf50e6a6d0f56ffa0f09c80ce13ef2ff2023742eeace02f05affa5d6bd6be5b089ea5f1a601f087c6eff713833a101594040117c994b6f0ad5b8ff86ee8c20580c077be46c6548043bbd259403e266c3cd54474b10ab597ea8ccf646afbfdd749272b05e5a4e5cfd3b9bbcda2a9eaaaeb460a11fa0389470bad376b30c064ba635725ce997f36c0ef28b947d79c717d78613030bfbf89032593c02501af8aefdc7df17792f0dcd5c1a354a24a5a067b9a1384583ed2cd9a38d2e88a8e10fdfa2407eb1552f522923c35780bd58684566110792007a69f9c556fb92eec970ac992fa6deb2c3408dde733af6857dbd63d7c97a7fb569437396e8fd3b144f00270462fea69e311c4bff39d4562000a1fcb7280b855e3c93d6164e87c3196bd158e3bb6f75d6b97bb61c746070bfaaf6e052df4d3a7bcefab54ccc1cef4d516afba32db33f1ed6079a50af155c6b22f5b9e39edbf4ef0227fa5db71c44a7ca181ccc167866a53ce802f43832a755e1ee57880303246e6a62577841faa1cb688a4f6304f638a1b862a5cad512deb424fc52a7b22bc2b8c25ac8c14b518729574d3dcf434db16eb9f6b7a139f5ed4667cc4cf1e19114fd929d985bda1da00b5fc722878c1f66649b8b049089e33a99496bcde92c9ef354cd60c09ef6d2dfd0c27666e9643f1a44c17dccf39aa576c631b86a5cbb21a0b6b738de03c29a9a574824d88da6378fc7df9318a71b73bc34f3695947151306c1beee834a75236d88e7ffbe20354feee2cffc6170cd70f08b15897b665fbe33af321ca024d396a0cbf83a470acc12dcb8c104da068ae52204d117aa9188aa8707f84e57f38a46b2015339e0f872664628b8dab44385cba787e51a3c5828647d652a7fd83db805bc5b738dd0e5694a61bab7b64948535adc87a50a33f2a7ddf32fd6b36886e0ffd1ffd4f3684663a1dd33979ace3e5295d09fbb20ec1db0955b1efdaacea3f79ecbad57e5af537e5f39a85ae160bdd0148b86d3e460b4ea8e634a8ce4765ce062119c513492f2aa9938de4c5c6e28f5e24f42a3a6baf100b7ed6dd765cce890802761bc5797e1ed0a18370ee5d9bdc8f667b595cbab64b65560cc6dc98de886104177ce7de83380ca1a64e652b828f7c437f4f1e5a1faf7fef5e49564d9a0689d01bc9bd2ceead084547afe837f3423a57b2e9a1300262ed6027c606567a7ad7bdec1628c432fb5eff2c743481c6f24f98e5771fa6438a35c208001b7195b48b661355d8ee030c52cc7af90e2f27aff504c8715a26c09ebfafe1269dfc814d212dc527fca7249b9bf6512c7ea41a880fee91a014b9c353020898e339921457c018b15b2fbf3aa933e19eab9f9f0d0aceeb7d4f9407bad20ceea38852f862774df90681e5b6ece99177a97a7f5930ca90d0c4eca071b60a79411c6427310966391807a19c6f1805e0f142ca0d367726852075075943647db74bf3227e336ce97f388ae3ceb4fd5daaa9a65d1b43d42654b0c29ae8a6f3daab81610222ae1d082ce0866898e1bede75b90b0ca08271f9e2e58d31cc5aec5ee24c14906019f54a0cd4583e9e1fa3204d4a99b27f26912618eb6972c04c35c8074ab95b972d0011981c3d2831184b2d6b0471d953477b582a25c7b822f5b2ae92e12b4f642dd5cbcbf88fb85f1bf02dfd7e07f21bcafc4f535b85f0bef6bf17d05fe57e1f82a5efaca74181af89eb6f709da71598d692bc6562e97329b0ba185733fb69f53061288c04b57d802b8b816aa05b2b981a5a05808d04178d4462f9973144723d7198536c1fd8fbc2cbccaba5f7ba4b41e3e55ae7ccb48c2f4a8562b63f70324a62ce2d5ae71ad5f456a671f1359b7847230de7b7296673888f5b46a5ae52e40caa48e1b4b078c05ade044ff5a9eb3db5d80a4a99057210051102687c500da762d43b9b856d7097e20b7a9542f9c0a5465ca22c1297355bef2f4ebacf0469321897627c50406045eddef4aa6fe27b6d089df93e6861bc1b6663edddde02a629ec5c55ece4b353b12abfa46906b5b558f272e6be7016ff285cc941d2b6c714bf90fad4b18eee3f787a5607815243682431d4a5008c338b7ac124ba5aa35262514bb9446bf3bf42ab2ea27374d61d9bb61d93445af22a71f904d8111775ea042fe6b5d7ca95e190af9e9bcac234130c0acd86e2463d9efe8ea339ece41765598c218e0790019ecb0c07fb85c026d81f73cf9bd24dcd3e0bb758c67feb40d468f755666e0885183776a0341114fe0885bd9a6d0ca23c6dde011619f72f454dcfee99a72777837dabc565ceab8aaff46f2e596c0cf5e6581d1e4ec49a419ef24392956397f95cb1ec75b79a8b4ca36aefc33f3b7ce5d75694f3687acf38f2be500041c114cb5cd3ce9ac32ba8131c81c536780533b478c9c08c44f83932245ddf3650e7618b725063be232d4920c359928ac427f977255b34a4656d589eebe7ab4fc4d92b38fb0753dd7889bfb488c9f32bb9ccac996bf8c135f8ca8881352368c9a315e4b12ffff2ec63b540796d8548ac331b0d913e80484ea1be365c590d564883130de7b4e08768c15d6c1e2119b4e818e36a7b9f61300987c825e440b0d25c7df96eb2732dd2d2555396ed8f8c3c6d3d6c3ecb31265c083e962612e12173e52305e0c4c6e10b68c118f65b2987a8e041cd4864baf484715a6fc2b2990820a8fb7e58f20bebe933bebe10e8f63811bb04d976c2db69edc1eda41d7203418cf409cded15872bfdc075f5480869935eb502085bb849dbafd8a0b5595c7c0bb421229f102d9501766be6d9428f4c3a41240a0d683aadfee4bb29ccc1040ff802360d87b46ec8c4c22a9d778e680d3513021b714376a26371405bcf411c4c510de733c1a7eddbf49a78259d4abb412ebfe402607e67b025bda28ddd131b6a44a9eb4f35c21be74e2f677a023199612a5eb3bcfa8652456dc741fbbe50263b8a8f41dfe8ca71668f43b88e44ad5fba75554d1f40f2cf477ebfd83f625bdde7ac543f63ea0376543c4fcd7ca61513cb197142253ac2c334205820bdd911a3b8b22c7fcb2586b4fcb81fa903f4cf714391c2ded93b94a25b95e0bb7055eaef90dea8f49a65b2eb747861d6a91b22cb8c36d1aeac52556a7e15137456bbd5173c9416baf8153d4a3250c3d7976b0d6dcd93d3a36dd446f2c5546d35273442b45d363908d0b7b9b209578627f1b1bc9ab69fc1eaa39ed85a945d8ed4c70647094a0b1b0097af455f690d5152583369ebc0c3eb13396632b93e4939327823f82165cb2d5f9a947cf58655b4bbeb93387a82daf853fcbec04c9595d9d56248dea38e92c160703cd4fba1f25d4c258687a43f585a26e7bbc474f3622d7a72bba95274a9387018033932450052e6d7fff1e8593e0a3c1fec09d0312d5b5ef3d4a8cd209934687aeab14a103e4e18f6c5be6e3d6e2695803d0468f2adb969361606194ead1cd11dc90047a42aebbdb3d13b3692599aaabf43a94c5720b5b8d1cefd150ad934b8afdca92b3dae1fbb8cea816979e657560d1f0a4f650823a08c174896b736874488fe8510f07912156c3cae4d5b484721e25444e09009bf27f073d902ea73c9716a828f459676d4388bb9b0073a17a9521f2f832abdacec4031f75c939baaf9f6a8e3fa474f1955927ce9b29bb35cbf1acf4a81a6291a3b8cd8b1cf315bb3c127c6f1a3ca8a811c0a254b45208f34f50c185f90c27ec7ce640412b17463d0af43837856f1a62dc81f52086ff84246b9f182f56349314121be1b820aa602642c7715983360e35ff041f5ced65979579d6a87c52f1af683855fb1ed5de5be282aa283bd7adc25787ee46a7ff2e1a50a447cf7d28d77d2e35b93d7e5baa18582a25f768878c4adcf0ba19cc7def9406a8b229043da1fc73e4d285027f8ec918941006966efe17e2af4c21d8503955ec512ebd43457e65a116ec1e3d9c0b00359f1c7de9bf6fe7d28bb190075b41fb9969c92120b8cf9854c18be76b2c963749cdae49412165067ac38d98b89b64a969642a599f666e5047dc9cadf9abe0d67cfe0e3d4a9c58db6ba9ad0f2ff708aeaa900273d2dc10dd3b181798727995b947556c7879e91cc670479e61ca88c0d25472f7faa44a7f7ec5b51f68343fd6415de6c1f11773f0fea36a008d4d3cce81cda0c53da8944fcab592faa4568690419fc24dc206d8d4d9e51366c86d85ffc2e788c82e81a30ae540676412fe0bbc77e0713247831681e2a3f0e94c73b5e9b5496fe38b99dd1d530959faf88b879e1b795f3e05cd907331ef76bc7793f14877304f1869ac2eda4d4c1ebcb6f1506ba0a8d12cd72ccc59ec7e8a1b874153ac78b56e03f45bd66ed0e879ec9256250e45c913af04c1297f8eccb5647ed9205e51cb095e73418809acfbaeb65c595e4125b419b42124a21190d9c0790f03a7f33db6a16cdcd624ceb56c3d3a1e21ba845ce5ed04bb16da5db391784ae3a1acf9950543892f686858ec10939ada34c98d8bcd568a2c403e9e6384f8ae14c17a5b7a77d9b65bb94e7cf6b4c5c20b2a05563d8344f81fb9ce2c62c6b34dedc6302b2d03433c8a263a60954c1ad5aa58e423d5d996954ab6dbb9377c0256b23c99484bf5ac8fde8bfb49aaf22cb37edea1794095e45e0fc2aa0b17dd51a79719bcc288c7a540cc7250cfc032fb4ad145c9372bde51aac8727f9aabb607de1d9882c6fc4f5af35b7fd16a49578037cdbb470ab6b8a7f05ec6772bdf0fbc4bc9d56d6d1c1abad55cf1641633f2ae8eaf227ca9e0eead869a6d1ab171f152e6767c4e38998a9c1bfd4b7be47ff08503a1816775c2446acf1bce925eacd0f3766983eb02f822850d3b40630afd96f7945bdb74a23bf21942836016f251a339c609b5cc9c07de0c2f63d22ee0ca99ac48350543d7759b9221804150c7a7b29da6d1654f0fbc21b094cb5c12755ca8bced4cf68cd0efd0c06d5b71d1516e13281f1a5d6aabd51ad79cc415ae0a60a05d8f8f5102077360d8999f6787202fee81f62cb576c810da9c02310d0d73f49ea166dc027b7acf7802286e782e2bdd232cfcedfbfc1635cf9a2a8c45fc94b3816facf8d3fd116ffd056aefd0d710d71dc1e12742cd931be079947a4e69e77956474f7a30d7dad12435a58ba1578b4accd752bd192f236d38ced872cb6a4ccaa0b91cc408d65f4b54bfb7dcfb58b8865f5be1822f53b7b511288c8551d48a812f994cc2404746fddbe449baf599d9bb0071561e3ffbb5ad6baae89e8e0a3c32f48ecb2d1f6e9428bc0bd0975103325bfa3dd8bed983e6e63a6c6eec614b3784a6030b373a82352dcb840a71a112a53efad1cbfa3148bc4b14038ffc5b0df4a5a9403589e0f0097340a1bee2a3042ac905ec01e9db6c9fcd0a3864acb9c805887267527fe7a082596e46c0e5672ef435834268315018f47b61f04cb337d71d1c96016d66cf96b4f72c9ec3d1c04f7135b748d083d6790e7963edf8c2f2e382e3395788d1424a15ba1a747900f69a5e849463ee8a4d7e5fb54af00ce0cb714d1de3e818cf063bb6c78b1151cd2075cf4ea939fcc6dce031b77d77689c28fc315ac328e34ea066850e7f1ab9bf0c923367290b34f7ee28773c55ae0f715e5cff5b8af0baa3c2133ee6ee304506789c68301b6881cc1771578eea83f00898c55e2affe82616f8840957504a321f2163e0f5288031e49b0fee54b75afef9ada07ecf8f10382b2260dd5eb9600be47066dac2f1aef36040347c2309badb9e196189e8b65998f847d01c05a2616fe1cf3a43d0581992bfc979ad9c994e1d87dff8af126f792529d30c1a83d70e342dd117778407040f6e571676c37f7b13faf2cb3f90c01c96d4c3efae9f0fe301d3a32fa93481c1203da942bb6f0283417a729572df040607f4c96afa5e130e42b879562657f4bcff08d4ac4cbeec78ff2288fa7bf009533f7af6bda5c61688a3166e2fc4ec202d74aaa0b9b2ac350844a70278422e60abd1fef46e34bc580895925d8133d6b74ee9d2cbecb530a23202b8876d10c992fceedbf4218ea907efb9bb74538300cfce1da121e4b3ba9e7a9a2b994d6dd0e7ea23ac1a7efa28afefe5236b8da412e59d324d324e997b0a8cd6b48c47360bec7a462d932e8ec6c2c07a645cf62e179877ff4461a9b6ac8560d0b7eef32ea3809247fd926460a598eec53f871735e771d1259849f0b220e23d1be22f1323cf746986fea377608ec3e5ea1a3267f788c3783a9db107ec20b9f7c518d1d4452b9e1c1c86b6da3b00d11efa359c8dbf50c235ee169e5138daa12739295150437acee048c95b57f9a9245588421824439faab278bad445b10e93ed4ea6e87fff05da77ca9321b84dd8e441c2aed86fcea4857eb79b0ff14669d1d703bf20a4041ab6967e0c7b9e0d28a6a785317fad8af6e29f964399f3542f353d370df654a6f0a959e9735a87286e565164a682a40e7f1a78c7065e763f90739437f57a02e141513e27465b7ad6d7e9e1d26d1e01e41c56ec54def07e3b7be869093bbeb938484692635405bd192cac4896762804fc4eb548ed1d054516f4d2a379bec5b3dc5568eb88350bc29e52e4f2062d875a89b51d3f2522b2e880320bb3f65f82453f19187b6af04ef413827f3797664e085a209687d7bad68ab7ffeb78e4dbaaa3a5c15c3779452ca81609d027cf826d9e6bb21dec3d3916c178780df1c88613d686a6e08297c0df0000e635500c83261c35dd3ec4d01e636a2070f54b0b14e19bf4c8796dde9f6eb7399558cea8e1621f26c9cb92c283cd3ef41ce4f57a7c95001367d418a72a5a2d0430f5bf6aef7105f7bd8bdfc77b2872d587d3aca0508a1d37865fca4e1c50c006fe890d59ce0da72d555e07322724c7e821184ae584597ba98be1b2ce288449d579b1fdee5fe308a4c3a004ee0020d5354a426690b03a0d53da07c8a030513587dffe3c070674f61b1b42feda3c14c68d2530ba05d88d98a000a0158f66d3abc1aa3827a80fab586338edcf791aded69b5f8a2d043491198da0df20907d66bdf53a664dd2b9dffe14e9f8e44f52e5c60f8b51f83b454fd5d31f6680dd1eb1c955cd47e0c9cff7ef916ef32d80341b23d954f71343ae6ef33ee5aa1bf28bff19f0d995cc84a261c237c9d4f01112ee891920d165b4648b1a284ed6e3f7b8b39ff87e858bd5b02f1583caf30bbccfb762e1760203b67f6b9d6128e93d5516f18e017953163b8a11813dcf473417d6de454e51d06c2e3e6b52bd50d185400a29afdd85c8585bf73d0ac79f00a6445b8a682d01c665daab0a3178079343fb40bdb67ba5cbb0ab6612b0ead14efa46907e13d830197088c0cdcec3041b45cb6b9ad86804012d49198ef7a7bde9ddb0ef900ab43486b128ba09b303b7afcca7c2882b7c09075fb2cbd1d38a696ee0dcaf798ed0a63ab38afb03cdbd8afbcad8e295c1938b1719aadeca0338ce1d528d5fd5bcd83618b7bbe7a5d4c361e5da29805a2f96b4daa7c794d25cac9ad0c8db8e538982e0bdf80c2144d8f4841a39c1aeea09e076a19c99787be50c7607872df5d74e0a358453812e780fdf7994791aa2898263cb7c647f7d7c8cce50be231d252efea5ed4ec1d3c0410c2537c76d4fab9d2ad7c3f7e501fb5096bbfa3696601be2e58124ab2bf21a6441959b42aecb06214c94511c02f8dad7ac421416838ab89516c236f9287b3f1caecdd468a88fc9fd1991d30d797beb40dc516122b62d66d975c28134841451c4a8a4273ebb9ae45fd450f510e34ce3d4e5ed46101acd9124e69c531c41f6d0492e8dc8ff2d113d927b2dc855cc95d1993271c1be560aa0a72bccbe2d695f92370106d224a6f509d88212f728658f99320bfacf54fff0fec33f89dd0dc8b11302ff50a32454ee7c781915fb1448a88982be04fedae03dee1fa80eb1ba1b51b4d94d82155a2cf9e3acc081d096de93f9f60681083213dfad7b5f5e6c561fc14b0960ed0d409f3a61a23d50a58bdf579b700a237e32ff83b72fd84d82a7aa8aeeef70ce37dc81a1ca0d0cf190a4529b1626c8bd9361ff56a3542af58b1d6b4c71a188818066a65f98bc67132a3b8a47a653dee89a3c689f28269672f4882e0750fce4a5ec84f648a342ebf6a553b81149d5013234123be08a628c1ad20c460d5aa274c5000e351279741754824260217dbc39b734c5223f788f828e0afcc0ceed7f42d834b21ed20d8e64b464bf944e16be4a2f280a640e74ba8d0b8db4404adef66caf509305ca63075388db59e3fe4621967cc3e30e3b4d0f0739a606f4df25d77890301cb16959d5992da783f37e0cd021e4128ab157643960e26431498492c08a0f624fc18c9ff05fc222ca1023bbff6916485f30c9bce5ce898bd30bbd8f4de9f4a22a746fa4484f15c0098d8883a693594d1e6a6907925d60150bf3b9fb2d092c212d0b6a641439c5320e8a18facfe5b26254d0bba436bef0a4d69ddfdd345db71df15222f4d8781289abd7da94e6d7a9a49d6241884643686ee19397587bd54c1be518c5292c9a25bab8cb1064ec4367b496b280c4ec81559cca3ad6274b2cd4741ab47bcb522d9d5cb62535c99700ecb63998432e3c2526bd73084abeb52a9564460e1d021568a7ddd624db8f6cd58144ec5d00733b1329256d44deb29244b60686617a220b01081726af1dc91829f15b8a88789625498d58df7fefcda81fd335532c9b966aa773614028eeff978e1f659e4647df9a5932151ceb333b3b6681aa2281f2bd9c73d5ca9133a4aad0bf4f0b574c0d5d9b15df32d6c6c9eee0fefb9d85dd5284156ebda7a2eb28ce4f32c0c9974bd5e6af636e5f1731a857026086a6326b686528b41da8e8be3c5f117912074fe0a0caf5ce5343d2f69a98ac0aea42003152296507f4e483bbeb7f31295a47c6b31ce64682b1bcae9a19ed1faae2372ee5725a1c1f9aa51de790999de2200baed28c653c535077dd8103945455bd7cef3ed334b97dcb2124aa75d457235bbc04124fc360d6c45b9c72750c859ab8f64f88ed7d3d2231c1c7c5817f5ba6947a1717187e29625cb0468575ec071b1e17353c253c9de9091774d2bc1377d38c14db5114af5f0a01140c733867574b6c03c5070c88114b47c54c9c17c0ff5c6d4deff21ffc591d504cd6b9c69050177b8d7b2e773c38ff5687423ae9d66548f82ce0fc22a45044846761fcc6ac83f9cdf40ca9cd70ea10f1510cde4020bc0d80b7a5f3975d91017939b6874574535765c06f91366e1595230bea78732107d91c7427c9332c0e251499a473ba9c8b56b6364ee905d5473e4a9326146188c75356872bf51cda59d1fb54d0abc02eb6e3d1b3288fa4c36133ae241d5c0b1a29a56c3015f96d22f964c1c19eba9e127545778a988eec0ecc8ce3a0232729dccd306d3163b976ab1391235c03a582b68cdddd42aca483c08b8c4916cd034f1140806cf9ee2d2c1c77cbbcbee48720357b389d6b68d425ecbfcd05ab4f0816d53ab601731397abe4ab46e38a4867d71053bd1094e189da5ba873c1c738ded2fbd6e5027f4d772e43f762af47aef5c544013d13185cb489fa6402b2f7ad6c306cc7b2be383b8218b7e5f6490cb4cc80753e0815beaa1130612461b14032f956e22f80ef208de89c0427d7c486f4ebc10afda0a0fdbf683ec2aa9826b6fd4149768ab47a1f0cb414ac6bf7a1ac830b15fe0900fc3de360c158fcc8f69767f3b570327f8740977f2ec2cad7a9854a7262382718d85db9349072a9bc60c6bf988a95bd00d29dfa6afe8d5a57724a85176f9ff84f0c2f3467e4736f00446c2e26c0b87494b89e51016534f269cd7c5e7dfc569411208f71bd54fd10690d80125026a18ac5678e9aef7e6f605eaea506c1a64fcb1f9b7325d885544cc790155611d8a73685b26be5cfd201d633d375c59179e3e227b56798eb1ed8db1871ef09d64b62b7ea5c86cc4b4c346dbef93017e6e52ff2ccc422b88c4a6cef28472ddb4834cd04c6062955f861af42d7cf17545e89facc02589e8af8a557faa0d9888ab69ae20c58f38cf4a5cf23a7d8da05a82a0f6d4bfc457922fdbdd48bb8ed4f2c900b1fab59c5c6d92dc7a672e1436330a95eb9d22bc1fc27aa65dd4dcd4491b62f0caad2e312005f27cb42808063e6354357fa7e52942e62eb003ca0975343e18426a89b0c580ca84a719cf3532f5516e0e9a5da2028bb53d844844c6b07589f5be882152b0689b41f59ac7e7b93df2486a4849cbc82c3b9af27c830bd2d01cc17ca7344df4e3f24abbdefe48f4fe4eb078b24d881d69a40a37e171a50029935c047a4a9e6a512e39a4f5464e898d97979857edaa6994aa19d52fcc91a14bb1a4dfe4683a3897936841358c34478e497187891b4b8d2b6ace292fd9ad0c6dc074369737ca5015c55883a0fc7e9eb2eb6c528b7fe8f94de1073636d1edd9377e2c58df5b912341b5cd9ff5dde19403bceb6e661f1864d5d1831dea85d3c3abfc85b80789273d4aff5a1269344aed1d107730febccbd1beb8f1e007466c8d82ac477e968ce609286248d1ed60b69998f74db2511b5a289030944a3e9b957ac70de85e1699c93859508ddc9d9271881313ee4f7733b021c0b45937f060d1d7470e60ff4574c1d0e8e54db93b28a610960309c4896ac3d1276f29effd1abdac90d1da11846c5ef5d8e6b9484dd76ece0fb27a48976de9b5a93d9590f2f94a443afa79de4bc24f5227b392c8b44297d7cf86c5f0cbbf26705255d8ae431e59dc958b2713b4a46c395ddb62eac3db67bf6da1cd4f7f562f74b10b14af59e72af0950f524f2fe3d04f86241e5040426138e196299a8bff05eeeca61cf27276f3526e82a8c33e3a72a1091f0f695ca56272dc6b1cfb90fe9da11553790e63ad2cf531a4665ad582f5b7170790afec3d231a0e55800fe8fa99bcf1968b4bc13ff127164de17922cba0b1332f72de269c126316ffd1e1109ba9044eda45dd780a11eb1bb39e7d61506682c7a4e26ab1dcb203f66e45903a9d131ff70850e8957c18d0539eb5acb01966cea723cf242bfda5fae1ff86837c5d1c602a60cb24ab9621e471badc41dc7e3e34ad2c161abfcfdf081242b662fbc450caf36ea0b552a36d5b57f4dee9ee3d7f49e4a5609ab822bde396b1c5435cbcd7ae7090545b13032b915fea4aa7e0007d786917d7daa3e1e21203ba7a4feb9867c796500eaa9a24632d0072e7ef65efffd089c468514654f5d4f4db6f30be40c1b5729fc856afffc4b07042e698f51902fd5fd76f2b2f02e99e63a16951a2bcab35832e777632ebddcdab2228763c0fe3de45ceeaf930a74e7da2fa25930adcfe855079a3ba9f0be4367f2c5ca5505e8146993b222632b1e80d93a61b759c40b8c222f24159a1e5997c52561a10225e65765dd1693383185c229596305e76e30e28a8bf421ab2a2495223fec22d696ac4e37bf06a50a0583a3e3b2b45fe8d4669437deb8707e3bc5f5ef197565fe5f1cf4425083ac0912ac825ed8505a76c94fae49753a7bffdd77ae39fc6ca5416a3e28dda02411407252addab5e4e797ce08b099bc90d2596e283c0178782b07428ef1dded675d17142e7dd5e30e73895de524536eee533aa926e72ffc27cf5bb8750099435c33bcb34ba62425d52ad331228701183801d001cbf37bad4d4c4e1f568addb615c4883b81f74aa9348bebebae44c38ee7f6eefbc3f4696ab1c925585ea7e74a22bff14e6d15457e0a362c66e63e7d3e8f6581b2a1ac902809f02727793e693247da0093f45f7dbd39638bd3998975600849451177fe3673a781e1ca3c6d1c2dedd3754d2465cf02e10f6f6e0be269647331eff8d055f4330b791dfd8658ff023d20889a65a2104d8807af21ee255d0f75ff2a10833abcbff3a2629859821a0407247823d100f4718760b9ae74b469567c29f1b2124a58c24c416721e08f91bdef8f779b61a51f27ca5dc2e522b5fdc756458d329de803c4ace07200d5342cc346ab00c2a0e9aec586d5f8bb813c189a2155e67c23a90709cfe67b0d891473e4c70d72d22b13a3263fda133e641ef8e41ee21007faa8b1c52c915ed16b9bc40a2bc9248710cf35514951eef361fabfac5e20cb934831db9258046e2197bad02119c3e6c1a121bb57afd6876033df88c676898e2724d964344fe0d6003ba39bd84f913d01466e3cf5533f4d3c03c21b4e05cd9cb9ae0359e8dc3f9287036be28afbe56c7be8564010627ddf949e022cd3cf76f15cd0834ec39a06fe68e68500736d1329b6286e82501bc62d69985584c6294b4d266d0ac41849a9b16045881cf448c4da6d3bea297d81c0afd42d968979e0fb5024913919cc015b27668bca3bd65303c7a2ebddf2d5aea88531e5d541429662660f99490aa04ae2d857967c4931a15015c4e2f2758cea6446d1ff9e28a1035f20c3832cd585506d89211f4367c7874c172a3ac75e4e87dad4ed5c7546768df7af82fe086196019172720defcd026d7e6b113d8e7729d9b3f75c521167ee129c59098cc9b23302e48a7f2a50484de9ccb89222a6f7c81794c060b79a2963ba3a639ceb2bc1b17f76394c3271c5b3d21a0822b7401b9000380f0ee2f813aa5b5ba4fbe5d0c8013df1331da1c5e1ac9fa434f13332b6956029fcf86071fa83ccd2fcbc0840018bda09aebce1d231c9d90817cf4b4b66ae2bf35bcee58195c1a85747c138937080b06ac82a0e3cef1e62da0894f36117928062fc989406a0ac9c4cff41140cd73497a125e8a3cc808483ea679d3d2f39630bb92e768650641b4c4c48b32df011c28b06a22c2e00b79b4f390d13683e2cd6838a487e854747aa12a4c275be5a9e932f95db94bda2efe36a32624d73081cb7778fb2774e3ea932868de12f1667bac3ece38826c1a44c7881951ec22d40e828d1297707eec4ae03be32791675045bcaff8d222624ce8e7b75de6c2774943cd62207e62ad082c1b52b3b66c5faa45b63a2f2bb89e5ad703a9b15528df9d27bf827098f309ccb962887f7a6749177bca8d7029f44574a5fe13cf01b5566170060d35842517d73baf860bf1e48e0a9939e3546aced9e40be1b85d3468aab48d73a2647f654e1aa9119e2c12f8d5cd1343308b1f2a8ea658c1704dc1e39f496ac1a8a883b2baace6d1321a395b1704699dfd337030c3a27bbcdf86663a9a9162121d5fe777e98846975d2bc2fe0111b6d909ae9af44440581ca4618bd9bfd5509a86cdf3e73de804a685c24d1fb5454eacc57867db3f721a440a33cd5026fbc63900cf320ffc9effa32321d32e37bf6a2e0a04926d460e921018f77969994db01038b4dc022305a899cb823286cba9ce9f97fe4b37d406bcefbf6254f6a9f7aee2dfd0bb0f21861d52726578b86f23241fa1c3e944a8545aaf4fedd08abc45e95fad966511a90e37bbbebbbdecf1b3b77de257544b90e333a2974ac1d0342792e14d331f6eec8491d59f3c794705209e14a90973f0a22dd3e430301985d6d974bed283723ce4d1ef0f37dd4d02417e6d5fd420145c74e91ee935618e5cb8b86c7d10642d44f26813208499b02fdc891d0c7b4bca1bdc1020a03e03d64c8e2fbe231f1f85b3d84abfb6084827243a2c8a66f006db332325cda04d98c85a3671f95c1f4508eb959704b7d79a10b31933e97711ba5e941d35f2d0d621225fa7ac3c4dafda0fbdb0bf5721ff2c909d4107ba7ca19cc8244554f8b0cc7ca4f7d264d5a4225203940ea5df2ffd4bf38fd2f434d5bf284f6a788d0c31a1c3c50f1163e92abfca25b4e30ad450eeac851baefc139df64bf1a6d7d307ae7f05cbfc27f6052e203ef1657da0eb6df6653438e5703f3c46d652f26ab02fe72f9522dbdf27d483b309a510104da1228606f67ff4573b236c4cc1cac68b8a3fbb8540cc03342e081c158d700b143e17fb22ca10363c4c2a09484c0e0dbc522083e549edfe08da5d04468f463c2c2a4d8bcac7cfe671849daa98852931445c67b203dc8eddfc01d8b9810e546664e7fcf22a9cde1647c5796d7d24160dfc314b9a450c49259050f0fd36c10f972c79037d0d846469215db12a178cd49de0881a28a3290b9f59640885998a50e31f0ba04064aee28682ca87c4d84fd11d6588157ae93d20e695386cf120b0cf916d241042d96f0b2e75e0ff6dc379a0f51967e93b911386d2dd380a60079056dd6e913c5972e21678fc66465575c6399f644904a75afb194de1ecda7b4bf8d6ec6993dd16d9d718f6d59fcbb782ba18895a428ff09b889dfdc7e61d4bb9a4dc2a1033d687830997c472725a6396b1e4d17be7ca0dd6f084b1b7682d5d9d67b910d8f16ec81f90a44b9bb2896d37024e5ab3ac8af3c785af48340384c9c80cbfdfebea18c93bdd320e9101a639c47e66cf97e1cd644abeed885e08de1506c5031053af08a02d9b8f911eff5df5acc16ef45ff1061e0f5dc860b6be030c61167067bef05a1a80f8704c635fa4b2f0fd01cafb7084e0c3da50ec2017fdaa4a7328940f29bb4cec2c86e668a3566485574b53e92db0a443cca924c11e0a377bf9e6ffe5e737522d814587f81fd1eaa2d03b0f4a0edda342b4ec49a68646622af34c200fc903c9f81e6feaf6d5e75aa5a205bc0d5a7761312fc94611c0fc959f41201d4fdd0a9bc75c25a813b0e75c9e7037b3b55a3c567b1c1cfed684e5bdaf911b466f37ee9b6fbeafbab6b580bb460987b24103d9884fa9c25bba843a60671d2e80409a5aac0ecfa0603d46a75047f1c8dfcf006c7c6db1d3461b32371a9ef2af470a910f89b45ac3a69292c84422ae63a9b889daae9460d9d77a8e7f29438457bab88908bc0212db23e2aa8075234014a59a30b2ca81ad2aaba0762ac0a3277016fb4078d430b2086a5fa64a98ba9a576e003b76fc280f38ea8e4e31f28ce0a3db75c6c935b088a38c808200789cc012c2c4882ddad74dd19d7b9914422da39931a6b46b945e68c02c4428ee68e3e4cc8f506b8071215dcf61b05bbef533eb1d4b0552673c42e46226f61b4fc45d629e0ffc62608355a719092550728ddc9b69175a08974e8c43736d6916d112684c6d4a5e22a1d1449f6e7d89ef19a1ab6fbf4dc48d249ca443ccfe76709d87f038af685eebcd98c20df83e299509f02b46f0b9fa41123507917e83045e14c4ef0634515e92fce85cd0db2d0fad5c772777b4f7f9447a1576619184f871ca72b8b865d5ddce075a90bde01fae5d025f8c4cea9389f0db00af0c6f39779a6143100d97eaa8f76c48859c962307dcb2d70bdfeff937bfd12bd2c44457670dd66af8b3e1a204a98670c2f280fbe014dc9101dd0e701bbcbb6a0fd44b0a3a52ad572c494ed42e079ca990a219ca6ff7e8e92732677391cfb4b860e524b0793581e6bf46d01d9a88998d8fb852b94a4f7163e375d5c151975115b2ae055d16151606b775871ca3468218e3ba66bd8ab31657e9ec1a0367b5ed098de8ae44945ff12482557cdd6c743e509bc9735574ea58a723e49d64da8c515857b9c8aa0c2c3d0bcaf25d27ca5191104498ced4f04f0e333be86f2da946959b302aa68242ab193c242e466c46ce8d856f61d107e6b11ab40632f1b4a108a62fc90488da4e277702590033c237bce657a0465c7d3db8f065491f1cda78034da4e6795677e9102b1094063a5563e3b3417e04cec57ef3bbbff42f1e8039e6acfe6efea7a58133341d15ccf3752415601a9ee3ea9fcccad5fba01abe64af93058301a9807f603ec07c2eba179be6cafe0a3efd3f4acf356e064753e875caae20c255bf70ed652bc661b68ffbf1adf659764952c93375a4922006cdba87b15127a2568a311115b939c46b0dc590332e3a2ab8e4daaa4d03538745b54777a48c572f9ccbe23d3c39d0402a6ce5ccb5bc54f784a073e2204aaee8c1fa247657bdcb5e6d0c0c6c003fa45756ea53bb56842e18d402a444d4864ae0f2ec300220bed4e98d9980d9478178397d698a0d99575c88fb7d09a75ff18f819cf1d7e116f7a9b921ce17d2fbd71291b8a1496f0e9cd60a841f47e00928fd17e74a311baa3ff563293a1046df6d9f2c471eaa9fc1bb399668f9e84bf41e9f9b36c617f7fda29a6cb679e86311967a38d76be34b080e5af0253b4929ec5aaa533acc6e2949afa5f1b0035cd8ffea39c1f41391d69c7086378e34dd27b772700c9cf08d0915d5c7150566771cb05fe32e2bf2f25ea48d6a36d746bc257d63022ce56fbe391d9ff631373d3bc2d285152c2d06cc75443c844caee4dd7f0465296adceed64c3ba8d718ed70614f0700f2d423c1c62585febbf5301800ddda329af153823786bca6a352a0875ea90517339d50822888c628114eea8d38921727234c298b647fc4cd0fa6446a13c752dd51d655491f87f46148a488454f55808bf011994c50cacbf0ce7cd5c7112f0f924c3b950aeee20b35a500394edb1ea87e5a098c703550024f5164ec4484674309eb89599338568bec99a0c099ddf35d30d3a09ac898eecb59d570161e20e9752d464152443148b8881304e7202b8e6f1ae9e806faf95ada18d8c2784ba5a960b9438517f8eadffc5db89bfaeae90f887c37eda9b235a043bb0f401d01509ea41e142af61a039d018e59bf251783462c67bea4a31b600ce7d42baa850c28d9c7c03fa65dfb65a300cda4a071632ceb4840f5f2dee72f743a96c3025a7c12153007b1dd513fc737963c035eb0d4fcac0cc12ee66eb1a3e7478adc76db5d8531e1c8fb4b328a850d7dc574777018d6e1fe39b4063d0df84ecbdf7965b4a29659201400b230b5d0be2963d5a625c686b2ca81963324c1ae39a47501a02081fbc08f1450938362c35229d0de6181b9d1a4fd8e8d9a1ce9baa17960f902c20370cb125ea7673330608d7182715d4b80a020395125b36aada68414288313834e062c3e602b115ab3c1ae37c534222861d6766e0f165736cacbcc4acd898f8f4ac595d712d417fac80c87e84d48e1e3f56407c985947683b6dd0307da1e189539ded4620fdf0848724556cba3c81d9fe5f8b666d8d172e38bee8292366fbcfd09324c9328488283ba474b961cf3528648e48a9c7cd86253eb0c0c3c65862c27862ae0a6047630c72997bfd08a244654d1a33418aa8e1c42555434b6842cf9a182a39888e9823a2e771590c11b09b4baf7beffd1437d7c42855a0672d8c19ce872037703e9463aacc12323d5ea0a2864e50ad8d923b8570922509186cc8d083903461cc5420ea590b3365850b736b6184708e0bf383e656fce8590b53c3920d1343768c6710a331874558ac0acf99334da6ec00e405263892c0d4aeb064363f4a70522f84f9eb98cb39c9189db98b89787fbd373dbc7b5fa05187cb0fad2b1c7646539b03c40bd4b35675a6a33687aa30f502526ee2308982656507223427e5860c9aab556179cd0182089e97aaca872a43f25009a1e18cd48d2ede4bcf5a1596193c3da56c3c757a4ac180c469d5d90b9a296df5f482262a055c11129493cd096a4e0e6c92ec69e6036a6a371aa0b43451b2647365a45af0a202a6679722b0c4cc81fa5323e9191ab967e09bf6ce172feccf17c7156670bae247d2338491490ed4b7be4723518fc037698b4c8b6a3895f46ffc0e82915798c169fb481aa9844940033d6b554c558ac6113b6cfc08c09d17983b1adbb2d8f8e9debc32d0b306a64a63dbfd72ce16ddc596b3d9da2e5feee27befb53648b91146df7b654f652bb2d35b06feee4eda4929a54e66f0627842d7f2025dffcea20cf4fcafba7b1326334cd135839a81a66e85e8b47fce7356279dccf9eaf9598c4db7f3457d4b638b0c55b4b57afeecf48ae79c3ef9ac422daa0b41e060d3d3ddbad7d27237dfee5eb2cb1c482dbd5fc6f09722f74e52c7972c6345790ab991cdf6edb57656ead3ba2647e65e6be79c73d239e997734e9fd6291dd15a6bbf8c59d2136b3855d252199da2b6d6d20a5eb28ed31f7badb5524a29a594ba256bfd5b2b6869ad158fe50a955c41dfaf967278ac9f6580da5231227d5bc74ab9a7291a1d457292078b1e2b3578f4b8b139329aab7283a6b92d555cf6dcf0a1523da76ca9a27a5a17ad26b23f7a4ae118c2a5a7149d22a6d4122c0e033d6b6096e8aca7540eaa8ac3679faac0204f930c8925a763740dcf1b221fdf8cc53c0670f8bef4ace1d1d256cf2952503e6646d0bc60a9c2846a8bd5942e361bd373ca4e151dea2955430b9822b302f72d9820fa65ffbbb684abe7bb0d1770df7e0d4ba8a29d2fa6ae4e6f30ddc4e0c04a419932513627264866d2a4ab808ac90f4e890d948a4a930ef5a482a2d143941cb2874f80a8b30a8a4688f5ace1c16ab295087ba62263c1bef762d9fff798ea0a8c5a99f62bc7c94e932b5b1a2275f60da3f5a2320284f1bd35aa257d81f6bdf7e27b42cf1a550b78c8c55e158aa7d7bdf96295c38f372fb85095a58ccfe5a893d1ba81a30825254864ed0c46e79c458bce3e3b670e8b554faa2871340a3da9b220b93d887972417aaacd0616308a49870a0e4010a1c58717a636fe22054b1555162428a45c71a339ab034b5ba067cd0b5651d19c784cca1b636b82b8f1bd962482e7e37156214b6b9a398b8aa69d2c9448da2808ffd4f7f358a37b6e4cca9bfb39963313286de7efc61b2e362702abef0795e04cfa92952c6741987c00f7539b20eef98256a7e9cc8d4e13fd6715e952eb34c4a6cb0c66486d96e294c244d3652ead972a4f5ac6372902888614d131d9b3585113cb58ec57c858043d20ffd49f47d6b8e162e74fcafbebf2477a9fc76e82e0033dc305cc23b4258904d53f9ab55d82fc25e85a585be97ffd204b3ea0fb4a46606e4c6fd33757481112c4078d224a11b2a5e9c362294ca0b4dd39999477f7f4b3b5d612e9c8fc24d45c9240df2feb73a4e58008aab66f42cf70011a684b1229410b6b2bad7bf614a07e69adb52ab89e24d0d2bab57605aa89e49fe10907d899a4b6afcbd40987af9d3fe79cf3e79cf455dc2feb8b7a4e9b399292945292f47156ca6df9e810ddeb355f58ea72cd17b586e17c412d08ce17fe69fa625e97e7cd179d77dd7c9169cef30557396ebec016e3f9e2de7be70b6bed7c51ab9e2f2885225a5dfa503a5f388522ba2eddc539347fce39e7ab703d2da64feefb1c9ab6c59c3218cc84d75a3fcea913d1ff7ead93bc5f0663319fef647e5d55680e543a84e86f3d86a7bd03ebcff976dace73893d28b998cb9d97bd8e26d1ab0a65956c16e672c7754f34e9aa4259a58ca7953fd1a4ab0a753177b9279a7475b1c54f34e9d66955fa137d5a4e2bdac45fb9e6d975a50834256dd61c9a4fc95b9b43f3913acf45d8b4fd923211dd490e540a259639d72f34aa4bf5a916a94d35485daa3e6a53dda13255a52b335b5ab7db9b2cac2b33b73759565548a060add6da7b2fc698e3b2cdb952cfe0bd18638ee372ce5d478239441f831c9773eebacef3bcef032d0856ea6006bbcef3bceffb40100cc32d9775b92a7557767996077e1f08826118ba5caed70bbc32136e69fa2e2d4ddf7573bd5e592f2c4d5fbcd2f44533e24dd3f779a3e9fb64fdd89f9f4afd27bba8d51ca24f9b6a4ea06811da447780a248e81365babda149b406a5e5d9d2babda1b43c5b590ee5369ff22877e2508ec4a3bc8ad7fcc969797cc97db892d7705a9e7b3d4993604655f12b4d9f9ad1f4a915add556a734a55f4e2b2330315cdb34097388be3ba5b4d66aadbd175b8c2b75ec94d65aadb5f75e8c39cb71953a67efc518731c9773ee3acf7a5ea5ee7139775de779def77d2018da30acd443effb40100cc3d0e572bd5ea215c54a5d0c5daed7eb258aa28f8fcfcf8f096bc244a56ee2c78409180c76e2c4091428500001c56c2c56a9c77e422313f44dbc0918ecc4893086ebf28d2695a10863b88e015920a04a1d28bbf21ca27f22bb66f81e5d8e405bb22acd21faf646a1c439dddee60b4fd3bf51b61767bcc089c14b8ddabcbc409453c8884400e176f65d14d15d8a80b30dcd55cd711c478a36864f659c6b518c2bc51634a7b9cf1cc77199fb4cc442a6fd65736a4ed9424397325d67dceed860830f2526767685c588a64ce5a1742a8ff6da1c22ce85f000ae90bd9027ef13d2a4f36e27844ae8755ec77d4035c4c9469c1bb614f122a4c70f77e79297ad9867d6a0b6b5650b8b9e23a06c6cd318dbe043638c2fc721b95c99f3cb6047872378289d89ea62830b1e3725e24c8a66be9cf3113e7605c765bca531d6b8c21e63cc45d550f5831b29475cd9988bc64b7134fe14f8662a272c742b38f231a6aa418fc618632e731cc73d550e5930c61cf6f4a4ba218c0e75f9443f3888449d7361f540ac476750e7db7dd8751d999faad00c8df03782116e58009307098eda962a534af0959babf33c6fd1d2395b2f4a55687a63285b7144586d35db2546617faba151f79d14d70ad9bfac0b54f73434ea9ef3e89620449ab96871c9a3811e7254e961071ad204995a52e2522d45851c37ca56fcff0ada02734f35868eceb2153231767f858c071d4f147a38862a2a0d6dea2cd0734b93292f505b62d8b2b425ce531c11cf2d315c7c31de12e749735c7de9b905cb081d56089e9285a5453db7c09153db82036b59824e76e9599b5242eb767a5a8133660a0e3775049ebae25c7a5af1c18ec879548a8b47a76cde94951894c58d1515931e70871b75cf97a5a95663cf2c160cdbc9ff82ac3d7f6250c10462e0e771350b115ccee017840acd1da8102755840a71efa111376b51087458a303131686304293d2f945289da56a2eeff17fde18835159d7c142b0f2830b560a895580169b94a6212e9015ce7159c014799b7b187d4a44f41ab5c8f5fe25bee9cfff738dbebd115b5521d7bb4814844923bed2fe1e197482f7df7f240ac0f74825ed3ff7dc9f832ea307d034047096f63eef7b1c558bbef707c723a7133c4e122db44d03fca39955e344db34dac4da2049a39a71e383b6bdc7b65ae4bd7f86aa42b95685f25315bacafac6a013be07ffe392b4bf37069de03df8de83d34a97188ae2a82ae4ef8d59735657b5e8850d5a9a6a59b50869fb73376eab16b9ae506951a26dff4cab45b3b6548b52e0926ad1065fec10f144dbfe9c522d428ab267cf9e3dee4ba52795736bde3c4a9718aac45056baac35ed9f827fb67e1d5fe1a4a5ec4c1c59baf08fd265f6a74b969615556baa4fb568ee015c51b26dfbf3dab4db74e94b55c906558bbc4695fce99267f9ad1679152d4e4cdbdfb76ad1dc95c9952e597a520b1ab46edb92a5d32a59ce2d4a96f3a64bcfa2512e7e676511a9a3b11cc4189d42f4694996a2a625d167015094787f6a4c041425062d400507ea7f59c9a012b493449a96959451197d4baea0326aadc645c440041c5dada654beb9c2fbf05dff5900641357b8dac3ff7db9c2d5e17fa4a532d7cf104648662a73916236f24870dafb354d6fe9a3679d5488fe04bdf17b1fcf7b79d3f33c6fc6e62c4aa15ce16ad74fda22244b77d52862f8ae31fc29ba3e85ea43ba2af9893fff22cbd8c4ddcf997f4e32e8452a60ea27c1f5398ba2ebcbd8d7d7fbbc4796b3a63d6f0cdf4318f36f6814bef73ede8b6429eafa22cbac2b59be8730bed2ce9fb146a675649933598a1c59c63059be8ca874a1ef9e355d5f34a7b92b5cbf4fa3d1c05470ceb2c35ffa589f9c289622f989359c3817452a3341f4af302aa322955130c6715c29860ba8ba3ead95487e91b6a8ef9bfe0caba82ba6d58ed575056b455ff67b078a1269a54f29adaee39bb485fddc7d0ed199a64fa3698ef429bb27b0a40af857c8da2707f41da4617d8bffc3600d1b5035fecf2b754c9248d869149ffed7dd73a6b328971cf77aea1252d0f52faeee240b2aa3ef48c472d61240820b9282a0e9d38fd9c1a2c5fab93ead2427d6985aee20a1cbacad0bdf5c51fa93a635015446e7f4c141f9b907016d119449185467fc2b90807e0a1ba02f83b6a0d1f725ea3534a51e44d3cf1c7e006d41bdc9045ac7a953d80095510c3811cb19a500dae29244baa7cf00dac29222b8a8451d299369ea02042aa3f5e9c39a88fe64996c38986922e2f7d048a4b22a14a32b501d0b8b8895deffee945d5b1e71a4756d2956c0d4955c812ec0ea195e32a804fbf52d098a806a4b6543fc9a6faef8565099a52d66adb5d65a6badb55622cfa16b4d405d223a4984fb4aa6a0b2ca91b90ad5af15466595bebb52988f9648dace219fe15a04fe538bbea10338eda4750efc39354492fcfd46a3113a600377173488a0e91c3a204893ee2f18b0d70c189d8611d3f3c31c7595333406bfeff3ee0776dff76d307f86f552b733e45c48ee340f0ca796caec745a6da51e0486a5b2d085f62f5dd4aeb3b198285e51ccf9abd6c7e7fec762381613c53006d5305d7a18c303bd02ed5f561096b23086d71002b30a553b2dcdae4ac2f034256373687e2c8c5161d807d4b061238731aea63486cf211cc6c0158958da30c694953e3384e1b030069d2e8a73288c9135fd1cd22e8c40180302425e1883d3dff77d9fd3ecca398731bae99de7a24f02ad9d9023a21b1919d9a4a41b1ae1d0e8558f8847c4d22a69ffd22ae90a55a1aeae68124df2277f2aa795f67944f49fd3b38bc22071916f5621c432c7f08f8ac0dcf82f8c2ca31486d730867d19014989afcb4efb979d9e6fc31831f2739724c291930c3ac137fe4a12c1749c7a46912cad4b1fd663003dcec1fcbaa74e00b4c7d13674c09c3fc939875e335c8b60be0a3d41f40d1d60424f32cfa1c985313a9de7908725642aa361095997483a53590d61509d3fd086255c5dce40f086200e4ba8ba3cd2d90572210caa020c4ecf8741f524b3985fa2c8896287455114bbce155eb0fb6ca55ed779d775dd644135fdab4b14684a2251a777be598beedb9ffe73d3b7639093768ff52b7914e41e1a75b3f2bbf2c82013eed347dad78edef7e459914126d8c78fc9a3a04caa1063ea9af68fe1d92ced5f1f69db47da489b84ca8d33b83d7bf4e87d1d706f335c7be0de3447962650d232ee4d73ef745b6e52afae72a629929d555a69a01ebedb4a6bd470f72414bce75de77564387c9db6819e54c2c8a9413aefd24eeceebd1cfee1b9edc82b503ec0c9e18a16235b8ec3099e8b85e49931959526cde949f584482ef244a2288a60e63ecfd2e1f1832dee4090146378aa7878a63c3c603c3c663e2e34f29ee0aec3b8c31de98ff3cbe33cdc610e63bc84c6185f7c87890acda6fc2442c56a5488bb134585384c8471d761e14265c35ece397b18638cb1e7e59c3dec1d81c28ab1ada6ada75babd56b663b353b51167b9ee7719907eeb4b4b8a818ab6355e7aaeee052a5e454c5052b2dec7ce309114e26729e16d44fe736d749aaa3d469fd10d2a9d189ca353ab64e26c6a8d888a4902109ad2152369e3c04ec48c207ad2e4ad876d8d86d686c6b5a375c1863213648f570c6852c355ea30b969395820149e7e3390f7f9e1774392e672e73f9de7b3fc58dc53a1116d3baa14bf1892c61244b0e4196f03c09e32dd411f15c55a9c201a7c71c193647c34c9a13303d6b73a2e63cf9d0bce62425b125660eec42a3fac109c1acdb97cf88588c25e1238920dc4f47b9bc3c7a9168ea9078ea7068d4e1fcc3613853c0db13e8799ee72901ebcc5976088cc2c4989ca78ae128fdf8cc716e3473d8dfe8f9f1384f9fb3d41735f80a404f2a6080c474d082d3bda993ed0d3cb67a50c2cdd41a67810e183a45b8b8cc3d1962e68d131410223a384d8a4e142895832ad35012e2cd18d89b2a27de3cc1dedc60a2f3baf4b8e1917a769181f6e64608ca794313398e737313dd44b9396265e566880f97b91462c011c3530b2da400cac67b76643733805bd624f16a6e6c7462e532c771f5bbd379c9dc1b971070ca191c150044b8ca705c1b321a3f0c6ce3a5b3d3e651cf2e3a9cda0c51850b6b6d9e3095db654b947ee9d9c5cc9618a5ee2ffedb60270d97257370d0a045458f4ca65988b1152926b6571cc26465e949911caa245942766dacd65a6bad44594f2f738cc846e48a25ce7c7da0e1870f41bca1fdc09347736c9a4c2411428f9c2b5b4ca6a46c0e0f153d506d5ed05c069be3f09d36b359e29dcbf7b2b1a26fe6ba7b6b6c86d077c244588c4d53c8711c0772a06717354fd82cd115294a18586401904dc45fb2983793a548c932a6ebeb5b65d5048dc9d2849285be973341cb6041e48405d0102d232c8ae8f9669ce849ae4035d14745a6885e81ea1907d0214021c50293863549a65a9081485597cddda1e3d273ea071f0820437ee939f5438e2b25574654e859bb52ba6a01cd1e8b664b0a4b0d1aa6a7d4172e4eb53cf7f527c7fd9cf1d298e47079bfd61744fbf38ac06aa7d6be870c40ba4e93c499ef18cd3dac9ab96282c3f7023d6b667ad03f7a72b151430a8292684ce9a18716ac7e2831002847ca24b194021f4f5b353334fc7069610911f6cb91a9a373ce99bb97eb70e46cb16ce60e630e572961050d1e1e7af8b05385b91fae7bebbdf7de2f67a6968eec8084921175c820f9d10166ed54c26389abc2c42b13b19517ad3269c7550ed7ca8071bd4ccf5a193965d4883cc4c947705564b890b992c920b9246449e9a8f3da61c3158238b050ed509374041142e89071e2a4700cf18354430aeee899014e14284fb4c810b9a18a8f9e5da0bc69c20685381db05165eb0907992e67e6b089d1c6a9c8edc98e254e924812ea925523c796247792ec8944942d4664b92df9c8c1c28d885215f00051830e2f4495e121c7aa86e3e8f2e7c8b9f7e7fd7cbdcc89a3efc362b568ceb961ca0d776e482293b52f51cd4a498e2c31cffb43a4ed097f087e956e521eb1debb8447ace4ad423e2bd26bb457d6ea8e95bdd92c2544fcf5d2b6b6444c563be26fe5cfe451471e4d29515035ec59bdb39dbcd66a47f4af5f44278316e0ba7e253128c016216a1ed39257472ced92ce2a6a5074c4d2bedb1823440e1653c6a7262db1b424161b903659682c15c1a264644d96166a57a2e20c2164851a0da6adc7963d2bd4641560ae4554cac9f681e68cd690255ef0f850e22987fd555a9dce8ac33429a2cefefe4b9a2824f60d593ad0ec6e2442e2695c6093647f75881eaeeccf5aff8125e9cc28650b90aba031e4b4da01e4060078529620e4acb8a241052403aa207efc108030578c3061b04303ab64937c106136290419e80881b316f9e480b2b483c6ecee9176b6ce0109d639629e43007019f6fca06377230854f6e74daade7469620397197a0cd1b2bb7156e1d99fddb2b424b6b26e4864ede084c6fe38265b5cf6ac1189c1b6bfceeaea66e545d9e4447d50b538e2c4d2504b4ddd5899ea5315a270e8d737b549d3af4955c98d38b1344c973549d3ff5c2f5789a4a7a0f1126477236ccafe5680e0f865908db4c1b14396a600583555a0440d3c061a964052078f281b41090eb49d1f69e3a63944bfcb58c8978c300cf7b18c9562b9018debfd3a561b8660d947f9677487a91f7bd210ec23edaf1b43236eecc82013ba2e8f538faef763dab91ad34e961bd032621affdc1d26311395d1c741da88f54b197ed330d139c860eafa3549ca0b4fbb1b3190b23f4a86ca07ab6ae50620b66c47b068bb1b431e2a151c6e6abb0b40ee0ec00406dacea30bcdfe28289ab171a15264ccce63109ded2ea0d1a1b6f3bd730b0b1ba89dc7cfcbfe3400672d02729386b6e7945da17d39b26795cb90b08938b5d03863db798c3939a24a66d8b30ba07879a658d8f1b48f66cee3c615dace7f62cf139d9d49b01b83fc69140e20b49dc72027b53f59b5e7fe6ca5b6ba680d2517a0761ec9aafd5db01b8fe6158f20597b669780b3bbcfe3d14bd933a2a30c82ecd9b48d49de00f1b1679916c4cedf75ef91473e440aa6b2f62cebb161e7efc8231f628992357bc6bd11b333397d094a9ca49dc70c82ec4f88da68141b1a45ade85516b51240d6119e4569be65846f51259ab48626312d61d1a50d2a8d6e1541b758d01bcd5243b386688d42a5a15003a04fb4e98a3609c0834ce8f21864427eef3df208f6e44d68c42687aa11213a94a6e2e7175d9fdff539bff7f9419fefdee7f5ddbfbe23834ef8befbefbb0f7dc60788a302cebcc6145c230a6640b08132e5ccd29446023258dac3f16331afacc6580dd9a2acc489a54d38f23e3fd236a13bfabe7ba4fd8d78c44bf50b5ed29469498c38b134c80138b3bb9227851193ae4fbc0198a7971d4134ed4925aae92905d50ec2da41dd1cf29f618ac0216a4f2b29334f361c4bb63dbdd0a8dc76370639d941d94158fb9bd4c71c33b7fdd55a8688246efbab5a43ac6c0b6a8c44b954524692602a584b1c9847172454b9c8a36c240223cbe3a4b34467c903f3e8539698d03e308f54c8949edaeec62028fb03bf50c90ac13c7680458ecd05e67172b952436d77230846f627e6d1a3a4d101b5bb3105d5fe44308f6e85c31da6dd8d2ecceccf07cce3dc020415a8dd8d2010d99f0913ae12495f99c177bcb03f16e0ac4547be03cd0d55fb68060427889d7fe648b4ecee8f6c116f82d84737eeee66009843b1df12631dece852b30300672d0a824175fd0ac5481da84de5c4e101b53948f24369ef3c4e2b513ba2e6944d476d6ba102c56a7f17cca3bb90e686dafef0c5b87395483a77a991e5b6bb71ee8f08cc1ff52507d3febc461b3b14cce3c4a2c5cdd5f6241668a0ed6e4c91637ff6ba4a242c409240ed6e0ce2b2bf0ec0598be694d517a6fd556daae77f1e80a21760eaec6f62549dbf0fc099459ffd75e3511db3e7b667de9ea3a967cfe695237a6348d3d1eca373abedfcb31182a0da99740af247aa3330c9c2c0c71edba3c7d63e9a7120660a6de79fc9e630d999ec46dc343e69fa758a58e2a56e3c721f6298f68cdef6a4219430ddcdec2a91e01c71b2da79fcb6ec994100349009096003164303e080a8030f3e580100b2820ac65781c1053120142760263ca8357b489852c007d7d3beae8db6f0b79a5299fb4c2a9be4b55159b6493d697721d67eb54f65d49bf524df5795f484613e9d4f9968add54755aa3054263d83e83993acc0dcde6465615d99b10293857565b3d645b5f65e8c396e5e8c39aeeb403087e6bbe066ce5df77db39b9ef77d6178e581f39b2038c3f0f502ade67cd77c5796ebf5ba7a99d1f3452b3d5ffcc42c3db1f4b4cda1f99449cf207407ca4483d0275a8436d1a52c2c4acbb3b5a5757b9385b5a575bb2a1255a5567302e548fcc98bd4f474a2e76cdaf224bba5e75b2d4ff21a3410d4e6535aabb522e8e963396d7a4ee9e9620ecd77a794d65aadb5f7525a6bb5d6de7b31be1763cc715cceb9eb72eebacef3bceffb40f0fb40100cc3d0e572bd5eeef2d7eb05ba288aa0fbf8f880fef333c732db39963fc7323651a040014e20201f7f7e401fcb373231cb98114c84813e9eb027401f51885436ddc99a3487e6bb08f37db010fc50d0f57d2c65dadd412043a61d46c75204c58aa90ba4b289b1e3eba2599f7f0a2a13eb744c5df6e64e046358fd81e1145da191f5ef4acf9f66f4fc693587442b23307afe2b8c61f5b5cd2131348af9ac505629510685fd94374bc34e844628804e000101c14cfc0001f9884040402f205708040484af06aeeec86769adf64edd282adb229659ffdcf4fc6bbb53b2155e68b482ca66bd69fa754abbbe33b8b2466d117df49c4332ea4dfbd7f1a96c4afc42a34f89cae6af50d3b4a6fdab94e84f42a5a38cafe643cf6f09ec32872fe8e044e12a917cb4bf8f2e3da0fdc1ac2795511b9d725c5230d496a9cfeaba1fe7dcfc5ca509f4b46118231bd9705299287ad9e3b0773deb799ee755cff3a87b33035779a465da3f00ba9c807652a4cda1f9d773962b6e0cc7e57be273c4b36969fa9e68d54ecf2a33b050c545083451353c81c179d8422f37251f5282f4f492554663db8f9717384b60040115a606394e96b4f11d3808f1f2727392b9da10431ce1258f103c8ab80eaa2bc6835b169a892c3d0e9091884af2b245551161c691e35ac205b7e2898d2f52eca061aa7218b004d12484ef0c0c7ad860d950a4c6c62f68e1018c0d0bad831f71d4d0da544952041e77c10a67bb3205c2b4f9ca12fde969bb52048cdf3c8d763da727555a7a2d2b755ffa92ee36508be654402d9d56ceadd895685512e9d2057d8faab1d89cf7b362576e9546c8a8bf9fb5598d697645a3cd2b24264b42035468fb29e8faba92b736df5c51e62bed76493656d069ef71d2922d3d6b4a8a5ce9595bb2a43d6bbeb06104e8f6fca64b1776892e25d522673385fcbdc34a3e78a9824a9698a98eb849bb4c979e858d10f17e86f8f547aef73eace0efd39f3ff13f3f37cce74ffc091205aef7e14e3c7eb9fe048571358ac2acb4ff090e760228f6f4f53faef7f9799f1f1f32e87e055915aa0af9bf6af74afb8b373c9636c9d2b6b47f55017f085ccfb8afc20c14824e70c1def573fb3c0a4127bcfee77f48149cf8d7cf8de24dfccf73b037f13344e1c4ffbc09131198dbe72330370a145bbad2b4ffcf18641f69a37813e3ac422ad43fe1c8e7bf07ff84a39ff73e7c1566a000bef828846fe2ef7f1ea2007bd77b88c2897f3da086eb5feffa1719443f56954e8cf4aa0af9c3b26a92f607aa4b9c3f8adae4e1e85b55a80aa9302304ae67cc88c0dcaf8fc0dc277e060a4127801f7e48a2c0e7c19ffbf528049d10fe7cf8739ff818533bec5dff797882cfc37e8628fcbceb97b49f60d2feaf26ed0f1b83e823ed174dfb7f16059f07df43147e2230770df0c30f4ff850f048cfaa42fe211974df0494dfaa90ffcf4893aa90bfcfa8a4fdc131e8920f983484923e697f91acbd46ab204a46697f246aa34dbac450594bb5087cffcc949b3e302791208982f0bdac5485c0fc5485f25215e26a5c1467e39eae38a82c2bae86b302368ce85f668c864506e0ccae72a6ef5fd23a07f3838638200ae3753905873f00c186505bcca830342682168021111111d8410778c41224fa3cf03eb0c9ac11e2ac421d08991353746d2d0038c3182b3437a79d15639203d7e2113912e317a111a66b6c1803bc2ae000b455b11c97c86289d58315e01ca13807a5678b2081c68840af8311f47c1780eda07ae014844baa8169f72945979d163ddf4330d181f5e003ea4e6ec3b4f8f100f400fc009cf3856d11426884410e97485a97471c59deffe041003deab3ba13485de4ffbe2c124004218416452e08f0e21502e84ea2ad12a7ac0568d3744569c4ea42a404f0176844ff090276b5002d11e21971fe14f9b8a0040041180b910061c4d70b57087e239811fd3d77eaa6d86591ca2e85227ec06d8c806b6180b68c3bc500ddc915e0824c0926c222b07441a4ef97447edddd674de3770e603253998f11fd398cdf3a477e2e2801ec176b457d568f00e08b51972e25076721cd9432eaf95e7a017ce617a015930bc0192085d7f75ed2fe87e7e4c09cdc28426884b1183efdec221ce71d815a319504ab301ab462447fb76226026c01ac085e0d4047c8368ce85f824ee75711405b25facf8b0d005a307404b75f44bf6fa9447f4aab148af8fa924e02f5597d04105fee00f471ecc2b05e41738fefbdf7620ceafb248446f7e2bbc10d68eeef58c6f4fd69ef5b90f13b00ffbd254ce3e74619317d5f8746f762bb2b021200522862d6f4a91148a150d81da79df573220e0630c462030108c9340840062b00408eaf624e0c2e48116441051edc4bfa477d2a06282b17f6ec0661c414374df6ecdec9e10059a85ced991503bb404a9c2635f6ec820102364504db0d36fb68e671926801401a2fd78604cbd5c29ec19a44b08383d43e9a511c74c040dbf48f66f30b931eb58e86161d3e5cf64c03228eee112c04b9b2d56d6062d8f567aea50820b67da4bd631fcd603cb6259dfc67301ebb7ee04b9eab25fa03e48e1e18f6cc9fb86e9a7e35fd4d22118876a3c4d0e5724dea1364cef0258aa2cbe7e7e727abc996dfa4f9a27e4da3fdebd57c5145a7b857eb6269ff9b66b68851d9d57c816f0eedfffa6a2644fb1f0cec6030ef0d11a59b75b32ed6cdba59b44579b1ee9b8bf53dd11648a0680baa69d0f411f389a1365ff868ff597e55b47f53da5f84d23e4bda7d944626dacb570cfaa5a49d32d9b4bf0844d37914bdb1031f8644ed0f05a53e22d0bde22b067a232da28c11dbeec651cdfe2a00bdb1e2c0c38e6d77a30aaafd59007a63073f1859da5f10d8d9ad1e6adb2391f69702044dfc5cf00aed7b23d2fe5e60fe62b4c5953265f6e907dd2ada3e7dcb46db2a455bfbd8fee88f826274e98dd1f4cb91882ebb20baa73954dedb8faea4677f8835edf73d8cf1a3abfd6b49a02de28dc2366bbef8d19504da22d6285ddedbd37c619fbef521d6afb539543f6b0ed5ffb9515fee2196f7766ff6ad0ca2fd6a471103708a188446df1395d15a6d0ca2cbf86a1f920feaeb461554bb23699a3ed2f6c620133c2d247a586defdeaa10d2ee46a46d9544ca4482e9e31f641fd3688b9232b9b5f636f5a46285a5e9df3c35b0126d31a9545509829b688ba9a98d46a3bdb04bec4353fad4459798c9daa854a9d1252ea2e9dbea9489b628e716dd81b670254d7525016d61777c356dbf1b91b63722ed197e76fca0bea70f890eb1fc9e34fdbb455b9432b8276dbfde688bab75f3f8a445f756856e1606d1feb52f88f6af0a5697f7e633ad8a4a7d7eae12498360d50394ebc6d1213a2340b941db473327ff9907b52b9715d3eaa9ef5bbf411cbe1c77ddddddddddfd397cdddfdd53183740658e012a73eb82bf02fc19405b5452042aab459994b9537fed2f522d55d1777a6593bad2658e928a72dbb16796c68d2a54745ed8b34aa5cd9e1f7bf6ccd2e88281e7e88104162d96cc172b4ed2e0d0e34a8ad49c1e43eca359b7a48a611302903d7f06e09b6f9473882e2a606906375a36a8922062065a949c499f681ecddbf5ed18735aedc55ceebc3ae6cfeb32e82a91347d724a8fd03285b6c50215373b56348d407543961cfb683694c60626b294a50a4d11a93d958c10616a3cb8eca3590a2d2b188813c7c1a34615fb4733978e19b653ed21e4881efb002fecc1b203160f5578f60c76431e257666934acd854dc9212d52a6d0eca359d09524b613fdd90572653b55e1f15b0cd72f227d122a7619316dc7f9fe1ef09be600fd334ba65ef881881f41746e3e30e1e1638e18650b9724323a8428c1128413347994ec9983e387245e47da48c1b1c3101ae80099d344f5250c1bad2e4e524d2ac2dcc05ced192e2344d5171c6e597b46a7b409037ba107551c271c989c6142a3b467f44c151daeecd9cbc806630c75d6eca3991fb19293a49001cf0f503cf6d18cfb41cfbe4bdbfecc47c7056df6d16cc5a8b75ce902a60619a4e030060d104f51fb6896eb84d9f88f66ce74c4be3ff3e1e17160d2a95ed289bead1ee3ab5511e2691fcd381f77b6fd99cf9066570caceca3d98a6d4927fab32965d7175fae45472ae6eca3996cdf3f9acd29db89526989a2b58f66dd929c7d49a7fab3978e6da91bc075a8d181da33ec454e98c4be7127d51a1b41ec199d8a1ae7ec1b5866224e0d7bf6d16cda91b3ebcfba384760987de3ee4a3a510ebe598b8e3ca84ac22369cf28191b7a828e14b1672f231b388d25583ab25a8832d99051eae2058ad29f399b9b944ddd06ab3d83696d4a3af9cf9fb91ba0e63474e4b8da994c81b533e95db651aab079da471ee5480e33ec594da23262d9b35794ad465485aab5406f53157aa221de26ba14c316a8dd8d63154a444d8fabdd8da4152e5b259238a3b4bb1184a4fd5170d6a20e829859aa16041eb49a9cea5f6095634d126dcf5e53b693cba0e3c6d3ce63501b0b439c3a493b8f2000a14a5258503b8f1838d94137b480a569e711033bfbb3d427175464cc0ea69d47155a761ddd8a8d166de7f189583165945ced3cba0f4af6477d521b57b0b2761e2f68da1fae61cd4d117892761e392b69766c2a51a46e3b8f1c14d95ff5596b37ce25da5831ed3c7e55fbe36a8e384692761e5500d99f8799b2da794cb10546298c6de711031e66953c6bb6761e53686dc7824508ab6b655910b92c5d5e2b2af565ced5ce231192fd711d9581099ba59d470ca2ec2f83962692b588cbaa45970d8e254cb40aa68c3852c39411476ad8b508eb600aa580d49a7647e2a4249464495ab07ac8da1d79b772d09d9d494cab7b526c75d9d3e5b66fd6910934fbdef68837ab56ec2a91e8ac45eee04bf401c199f378748028533fbea8dd61b29451c2dc289ca19d43f77d2cb30c98f69957c3ecdb9974721233e91105f0030e18ac761e1d093c23c49181697f9407293a41acf6ecd5031894b5bbf168266184cad39e613438b8b0bbf1c8daa85243d69ebd9e7c00a785a43dc363b638cbe8d9dd7844cfe8a003d39ebd90885ca53ec3f7e4f946bb17363634ed3c8ab0c3fe7c4cf85cff3dd29eefbdf748993cea1e699ad1feb804225dff3beaa4fc38f3b467405082b0d0f67db4ef1ae7ee887425cb07684cca20d2de2c8391e2e169bb6ed616e9554bcfed6349b5822695754b5aacd0b6c740430ad5d6ae6f34c3b507a896be648902183447caa05afafe7749dfae318ffbd0fedf8f1be1c29da6fd390762a6547bd75045b9caddd514d4febccf0b99a5fd816aa0d8f617829f17765dc6f85a5b29a521759f611886436bc60051dadde822b33f13518a5072c411b167b01fbc1e7444eda3d94463a4066d777f34eb7ec601a10311bb239d85385d9ac4e8d813b53f4b47cfd2feae152247ae6812953a4dbb1b897cec0f5681d0d2dc2a0e1d3034d134457a28edae5259a2ad6123670b89225939ec0f7372daf078dadd586510b2bf1333093b44d0d979bc60866da3b163c7cee30070b0f16801899dc701d032e8c142133552f6ccdbb19dea07e9d8dd78e43696887061cf3ea5ed54ff9b77e239b4a5e50a6ae7318511bb8e1f0a304f3d3f20308b0f304abb1b5334d99f83793c72ac3c74bcecd99db23faf747a57c312700a30d7a220ac5d27881a9ef6e74ac0b9da1fbd33c454d49ebd966c917a9d7689324b40edef025bd412a20b0c64da89749ef0a804ab580b9b98748c219a01060000f316403028181089c4b22c4bd240f60314800c70b04a5e4e9dc8834992c3288a8228c820620c018400408c31cad0545901ac78b2ade49bbdf08f48aaa9882c2d65da42ae413c4f991891ebcfc13ca6f1c4895f45896a039f90c3a9964389943b28644bc3a965ee03f6e9d69966caaaf136b17a08590b74e344bee49dbca0cd71f1466a6209e3a1a70d2144c5832c311861553dd82cf2a19100e4d0284c86271e8451966892a53047047fb1a4e96132ab1ca8af41849b9d981a0c9521171dbced304fe60d980083c06c435bc83299186ffd9407fbff7af742d2cae8a055e26d1a299942366c605586cbe959e4426e417687011bc7fbc790054f4aa9271305878f8065ea65f910d3576ae1fee50e3a34f21db8164ce41c8f7ac73cbe47c42a2c69de26596f69081b271bc51a9df81d887f5daedd8034e96fb35c63e1c618dad114bf9b1963629faf27369ec42f11c384311e0844703a6edc50bc56cec2274bd691777468693ee9eb6c8727056ffaff08996acd4bd7604d7203d2660265b1407137dc8dc8cab90bf690d95c409370dbc64d77f4144d9c9fed2bcd7e7cd261ea29dae18af2edc9cbd805527e8ea5103dc509235df7d85d3ae9ec177bcc3e843b0983eec88f470931ec79eb028a55aab51542ba18bd176b2d2ec78f9dd1e125da74b91f346b06487b22f89b207d5d980ab03d21d8d3736214423ced268e140227755bae3380dd06f67d0bc7b9157b069b626c1891a5307c32ea7dc5c016e5f731fb21abf4f66a89dd6913581bddddcf6daf70817498133f5c36d4968c5d05a66b64fbd9df2baaea390ac247b4f7012856ea20c51502256cf89434d51c5094ace58957684d0f8ab084e09f7ffe244170036d7e6688c4437ccf775831f99ef726d1a9541371de981a1f6ca61132eba82b544bbdfafed2a2a7759c3236a2eacad480fe6c5aa3efebf67c80dd5b41ce203dec70b986392ab5adcb5ec033366b1ed26cc598bcdd99849b808c9a40b02a9bab39987fa7569dd918328525324366cb52030c641a92d94ac73e61efd3fef4028d0c83a1dfdd6f7b8e2480ca641faf7a8c01d2acd2bbda1750795878ad89b2fcaf551ccd2ef0dbfba16c7e8bd15bf41260301330d270b569cb76c10c858f43821bdac4039fdd3b22dc6f5c2d1a50a5a134a923604833b2f43865c7e40227409910808b3fbbcb1ef0c78c4abb5a5a5850dac06afe0fa2dc1490669b118698ca68868ea1ef177bacf40503872a73ee1491ae582f8e5aae5e0acca08b69dcf745e7af63d4c95073e55dad84f193965bf73e9cbd926908ee691aca69b43b489e2af9bd7405911c5cdad99ae480291bf0d8280d2c1682129b97ba2eac5406501639e9d65438e088290edf794e040abe363c7966d773f70fbae4d0b324c0c197cdfa786ae0273ddd312f976f3e2ed62a0efa429551058c8b4b40c0b3a259119a6599e99a4e777116927330e5fb95ca8e350356942b03901c5806e8e83043c9b12e46bac4573c18a795fc3484ceda06f59c79f5429ef6f7871e64c7209994dfcba5468eb7af3e47b7dcced1963bfcb785d7f21359108962d51b4e4bcc310bfb81fa8056c1d29fb7fa30b635758918cd2c3e1cf4d66c6aa2d22a32fb0b2f2b290c6ac8285f68adc9bb2722e19908ec9ae44652720236f97bc9a0955539c6b2dc8879c1b42de31f89e75a181fe1dc840694f5b33d2a981faefe2dbafa6c10c72a00dd9aaebf56b3b819e20ef6b652e11885a2da6dbfe66ecf2bdaed713632c6759822bef47136c6092cbe2af906a36471122a43e72934e6644b4ea47e8dacf0e20a33e3fcc468053fb4f8f0872529c2c12e18190841aafc409905cba2fb94ee43add2ceb597a8c2fe40bc1b71396396e9eb86252aae1a1f46c881116cc74ae6cbd0ae1c8772c352a861684d2685cc5f640b9d9b60e9a2c762b023d0ea06680cd5db66e10eff98f13e32b02c4f59e5971eb40bf7664a470396d531af383a59751bdee1ec662dc7ba9f70231e239e2e84606e92cbb3b21ee3b6430d4286bc1ab4981939c01f2700c2f94f83e38811c1185ecbb2a9a934391aec548dd3e8a24723aca3b98652b1b7482e0b838b4a24875cb098ae8a9864c139fcd2f540020aa4313141dc3089c34832af45feba2abaca25996b4633ceab46c1acbb815b77b9fa5d2e2e33654506e5662cc4aec1ddd294ded02b7715490b031e712b8d849d4426c38efcd2e5d3e54eaa33ba2dd0b09cc70ca859ca7e02d18b4af60779bccb4bfd206bba1c3ed45365c01457c78b00d7a23be1aa5d855470d70b10dc6882d0caf14b256f02c7f0d2191d95f49f595a7666248e75338c35a436615a37597ff97b6fbf00d4ea21b0417fd0123f797adb6323363ed908f8712c85f5c15aadd030178d044918ecaae2944af4e2bda26ad4c9e777360f88544b57f0874bcd3c0144202f4420ce3da05385e0aad8b0188f9580da4d8740326dcee0412bc875d4ea2de37e18c4ff95e340cf974a8d8ca3aedb906ce71585600eca86fecf5781e2237ac1bab0cbd70ec950db99f651e197c9fd7faebb40a2f61566465f6b1fbfe9b680563c488237add46f8897b90d86b99f05960bb493ddd3db0d6ac9ef72c3f16039633b60b51e12a67c51be8002abd684b6cc7d335291e7a6f5cc432cd29da0aaccfe657438ca039a87a42d8610ad7cb05ae94f45d65af0c7b72ad326994fe9a70f8fbbe7391cfb69ad7cf15ae987cc00c03ccf0972bf949625e274fc616fb3bcbcb73b5172435107117b1782f539435ac0e20f7dc08f6d1b3f4e8074073cad961612f9186f756ef9e05dc85ff76e80917679f32810506785e059d931b1de2bf7dce9a017541712239e80339ef4cb51a28600e55ce155353e829461e6b6904abbd474be23ee0d6cec3aa0b67cd91b60dca267d1fc230ae1c197a14ea15c7ba5882a45205df9c5686cc7e116b26b1db2fb86e2be765af17ee41809c93a675ea9d3f2c6f9b140df5ad862babaa9332df04bc05ba2d11d2190b22365a8a639bba932ac6e0561ac5ff3fa810a94f05e72b0bdd3865a8f387d94bbb933169ee9c938a8911ce0938a9bd4759790dacc0d46a6559cfff396b23bb64b8b270016d9af1c2e21b59d9ab92762fbd1fad8179d58c7e1f1b554e8276592045d0cfd3231976691b5566bee3aeda2bf9c4d72e420b30a7ce0fca6c5f8c2d8a7275789350d40d14d3c4d27208fd6cb1ad89036fc4cebe34e9d4ffd7360f69a791d70ba2f53937b18d987e4429dcd977390e7153c668a37650788bb3afcfcd2e8401ffd0907ce7e0dc204a2b1ac72989fbe77dbad99ebce2474e420cfecd300325d3b3ebb4a8409470ca334303f639bd711408c260acb9eb22d9c23910bd52471bde84e4be7d1ef44132db3ace1878145f98d69ea6c826e5086cd99735013a116ae0413d38c7a5ac8f4f19f0c294171e23e07c2aab2847026214ee119a32ad4badc4e44a1244e32ca851c50aede856e5a460e28ecdfccf30775424b636bfba86df7cc247fc7bab134d22f84e11be1891a101324922051e21325900026a0c57cfce1e20577e8f27ef83627970853b59894b73d031e1b18c046e3d6c37a8fd1009fc821c0cf5af94a8f7ddb223863bf8df103cfec0fa354d355af764f8ceb9a12b902bd3d5115b9b1ae3b4174c85c30230503cd00deb1f153ba4018ceec375eaa2ee1403980108e734fd9287a43b4a9c72316d0a2e71bcf41576e2cc39374a0a91304eecff4c23072c0ecd3dd1797e94cd6b00fa39902fc175a1a73b5c31885a798526aa32e9f5552705595df3e34d1ac21ec533983a8281a22a287ac3d34633b9822129d630455364f1fb05beae0ff0185e6f4cd667378b88c6300b6ac62737e16383641c12d23783a34188fc6a27b111210c890dd46a8f9fa62ac2f6dfd3be3f40aa077d48a8b8b144fd08b2c99cfc472d981ed2c8b9b5b3de7d8b0d2abdd8b182b4070177340a512fc5a94f5622db532122dc31f43c5d9d585630b3be6a1085fb02657c1dff420bedc1fa7bf440a76a702574269db3ba09648e64f0b2eaaafeb59a10646ee44f2746b765b9fb4f515974bb19db84b580ac1355138c9c4fb0107ef2abe560b8e6ee5df904a7a544d5d8ca15593f60cbce8a90ddd93c9e6d5532e47cc115eda0975518b7a8af8ab33134e69ad19045e86c8a7297be19e3444aa06a0b773fa3bf2def1719aeaf5a1ffd7dfd6dcedded0ba4161fccfe8f41fd5039999b3d3727b6895b2f761e116b82528d2a731fd80ab969fec8fa5945d38dc72af8d9726a3bebf066205f1121a96595b1943e99e5945089594d7d2655f41054f895b4bc18404286c12a9d346cd87e4527225d320ed0af738ddb303d0afa0e583153026d2f1c8a500dffda57b80d818f5dfa584314b9e6147305deb817abde47c364d5c1eaad9b256912cb7190142001ee0a65944c0baeecda9eedf0800198578618325e0b489aa838584b489a79e468399385a72e17a33f5c05b98052655681802719732d6018c887f634970f0b7ccfeac955d280b9cc0be50a15757876768da2dfcc8b222ec4ab14c514845dd6c3ea7a33ba742ce0b00200c6ef60a857caec2b4a3a77829a3a02d98b443fb4ee0e58daa15401cd534970320f58462c19762c318eac66278f0e05818191fd133c8af2649ff9f9c7425a083d3a64b2975a51e374ea0dff60142990dbd6167cd775b6b2eba30f3fc59505a0ad8dd21c7cb3328432953840be31d7d69b082df08af55c38020bdddb1aa85b591ea2d2a281ecea2ef314b8903bb8c09aaa67937287f01bb70ae66cdc6388dd3aed2fc64ea2ce0344e5a7a165154a8e86b1bc9710c141ab3af535af66eabffd34d8ec72d9017e5def78af381054aa22d7287d933f37b279e2605f3ff92d645680cbb898a24df2e43354c6e85db268c30b7f891cfa052ae1016e7ceaf52bad394d23097f41a5c6e407497bf65f24e468f56ca6200c9f4a726476abf712fc59a5db3679498118dbbfb4668843c3a0acfbcbdf14271efcdccaac50581f909aae2dedee83ab5ef5a3b74acfde9532b8e06b3f29386f84a065ca43b8dbd0b8b4f1c474646f7ea6de9f9da2e087d6198adb373c264b76b7be4f71299a237a76bf1cc03421e4d88db3f29c055e9c2ff02b0fee998d4cceea9a531040f0e8d5de0b2c00530ad4e27a900ecf089613d51c6f3c9fcfd1408b331fe25c3eecfb28569e9b6b5004641954d2b9cd8fbd27a2d7972e9321ad12241361755c371742f2f125add9e2f195d82409c65883892b70048f73ba9ac8cb48195d61194b08c9d19eea0b7d53a8283cab32f45b49a1e957225848be5c0691c16007d11b8c48a0ec48bdabfb25302c4629179c9799ca75b8f89440b346dd543ebb136f360064b959e14d0781fd2e3c9195df3c727915b6ef13bfb2254c61b442b075b4892585081b56382aa230b03955e23182246f26507ec6283debaa45a5e04ee8b0bd7650e4860c3435a08b69b3d36dd5bf1af03a125c6bcd68cb0c1cddcfc87acea5fc2ee7c044dc501639e8104f6df6fdab51308a4aec429a6ff0b3c1b14fcebf7c621541433822d756809f489d60609dc54c3403859b9be9f4f20db54c89dd6fa22e35598bbf0dbca1907b3d1b799bb822c6ef88192529d39c08ff8ecad92d35fea0e6129dcc985973ba38633ee4016d4a40935c57a9b9ea0079391bdc37542d232755f4c646ee7ad58465e3849357749fc043a68191b566d20b487c893bb5440f0db42d953ba048de98f7b09c1638818c606e26502983897c8b51b0f9e88060a2df81539e85a5d2ce3c87834c831b7dd6e2c07bf78017a0a89d10bd8699c80f9f3379bd267d9eae5d3864a9573565ae07a1fd32cf50843c8ebf60ba5883e8a8d5db0734124e311032dbed4d25ed738ac52ec230b635a4e34ee1957d6570627e58487a84e99882c873ead558bf273146c7045c08a1ea5e719a769260603c7bd11a00bfc6413dd7eb5ba138d892cf9150f953c1c9d9f554287fb68b9c88ecc6d0f9ad3b4070f0fea14fd7f10ae270fbc37e473790ee226286bc70f44d08464eabdfce94952a833e7b8d055a5e73a74ce95d39bec96ac50f27393a404386fd5377fff9863b3678adf2009b90e578e5fa95c8770bf661e0cdc8d7e7a0528bb680ad7ab265c6432662ae368ced5360480a2de8c1ec673f80ef7ced0d3073a14caf1131bd706f4a46ddbd01efcc8e5e89c05b210ae06f04a6827539657405a5e8891c1dd92db555a9bb02b76dd97757519f6f8c0e2aca76136e613e59a2bc68579320064831e2e00621d11c0613a8bb9a95a9f8d2dab68650618662c14d62e19d3e0eefd3e74a95749526541d3c10f4fc7a4779790aa01038d7ffc148f3574132ae9b94d38ce2211a01490e9b285becbac912809e448a2991b2301913ed0913508e7ddfab7425df2a53adf7f62d9a3758874b1d7c5686d06267a8335663c69dd056043e66280285012157dc0b8433e235a818353e1b530cc60eff817aef448247220afe2170df23f306c1a91447db7d15925a15553a81baeba41b14e3e572587ad404a205c44871297ee45f49fb493ab3507c3cce9dc17fba70964bdd356f7b7f82a3b4b07b9d4ca94fc8e1db527cd3616e471777f6eec7b84d8c67075d59026d8f3b38ea1a7ac21caeaeff4402df288bf13bdfe25a5f0b7b5b87e55678a6fd2db257bc5fc22dd23a894f89fd4619fc095155f6337e484188a0a0c668cafa3d5127e74b926663648574c40226855f7db23a3a4a7b9c6f728cda00a1a0c6e8390daf72f3410f0da2177da0b11dcc69dc6ce3f61f42f639033454eb8894c86c1cebdd3843d6053138557a0c0c864ada2acbe61f0d62f6f794a0fe7bf9468cf14b83e437c3ef10740adf82c61d32c91b3f81442595e5d67195ff716e6cbfa3236ce809ae162e13dafea00183453a4038c5e70186351ac1822f74a83488db75ffb83bb0dbd37893a3f7ded2fd3a9f0c575c50dee3a40b4377f753920098687d5aa752aa7a0c44d0f867fa1d14e876533fc5d5c44a715a8fee2db948ed4831b7104f170fb77db11aa8caa4a1a37f2d2f6f4f1824c030a44da8b189d1b36ba9e5a4c31a79fbe9053fee8b0e70549f0e11bb27f2206e52c125da02a6155fa6ea222e39eb6275ea0866ac32e7b4d5437a4c5588e9536f89e7bc9d0d2ea04ea760c2d006aa8544b51f3333b24a218227e6f3156b47558933e2e21681784d514ebf41eb856fcbb5a6ea0d0ccc708d8889144a92432a50af01861884cc2e70f0816f44c544340af016bf8b905e41e954d1c52a2625f2363dbcb4edd842a66e967dab72ac168234f3eaca8861e7547a7fd71f61cfaa5726753e4b99f6ef0508b50011ca042300d59d63026616f296a0d132d09ee54564380626165031586cf59102da9e4479e4fc72c0a0e82dcbd40e11cda77c776ee19d8352285f3b186163994fd91a0c57e1c2d06a0c0ddb8a7b7a733c52b73c73ae4a26324007b0bf737ba0035c7a1fbe93848b0e41ebb4c5178c9fd0f1c42d9075d349df6a3b5818f0a042d6d572c80208e92b272c135a4d8858a9e6ba8024be1717f8143e91cef0bd634fa1674c63e4d73ed1c7dca9d19a2702876db300ef4dcc0829ab2235525aa94700dadecb8dbe1282ab972e15da8b1f3ba94211159142287c75442799f3a02060e1963186ec2d8b22606a9ec598fff8847dccbb43df7a569fef145f9c987dcf1a022b12a11f4e9d84afd517c655a25e32831415d46c6a21e24927c070e24b5a463546aaf21d01d32a4366d5266ba4f2ac3d8833dc5c80ed056a63eb14600bf1ad6c1729fc612967cf406f4c5e57b2ee467034ae8b9c7e0a0244af0d4a03800722c194097c9b631efdff5524d4aefc2ce576ec71c3dc8ccfe1dca6ba8fdacd58326cf8e896c0da214feb24e4059b905afa10a3e6e7b1fe130e004349e0196741b1fa6456af0ac84ca6be82fb41c3bbaf5a558f50d95ea0fccc58603e82cb3c5e25b5e5af05e43078bcb5df79030f8991c588b0fbed7100bf2530db582dc2a35630578bf5d232bb157dfb909bc51db818b518c5615a2712f7c7543c6677acd873db07ea14359d0909a19f277e919bfb20a25d147ed2c71c581a62ed2cabb123979e7d65f3e84fa6e99a42b583900d25d2cba9a14561719635fd738bc3fd5f8f9dbfaba643787b5273bec967760e978d7a604f50300652fc993c60027e9f7cb896afee33e1d8d77d53f1383d5f89fbddf62df1df52aa32d12090b6c30ca799536767d4e559b847ef1a4ee431b08392096cdc81a196f408c57d17531d30a9a8dc0464067aa63b969102b6db31441ab4dae8db458ecfcf0ec67c2db615efd2cc66adff75dcb1bdfb094844e83e42c4578dc5327a783557cd588fe63cb8b3ab42e0bcad8c8ca6fa95a48db8d0fa77547c15df921df78cd991d0600843be8793123c468e64ec515c5b0e07ca6aec74bf9f390207a6dc51e0f0b6f43dfab515741ab6fa526e01f5d4d82e5a7c77efe1da57e5f93c3b9635c7ce8344ea25101c5ce798f52c267ba96f6d03d07c7a36cf9e88fe92141eb8af3bd8500d2103943a0136dbeee2fd3de239e5cc56e23fdf731d84c5d87351db31c7aa37b908c7291796e20885d302771bcc8231f38bd5c57896246681b119b45795153f0138c6c16e784aa261b0d51dc8b7337c16bacd0220be85c45510f8960d927beef3c57ac9b6edb16e62c3a1d55b34f87b9fa61be916041893f1c8c384dd3286bfdec8b4d5eb3bdc78afea5f27be29e9fead1c4fa17d59f89f63f55b72562f07e2aa6b4568468a40bc91e4f8d3716c0fb62250c00c71a284b2d774b3e0e2e092498bbcfb60cdd90aae1e13c5a6d2a29f01ebf9e26a3a73290be2ac081803f98896f46e9b7ae87faaf9239caf8e6e5ecc787332977d8828c99560546f218380d2076a07981c9b4f8f1ca2653120c9e6d4945f04c44a454db2938608bdb16757b68535a01dd365de4f683a411619d886e3cdb39544aae5d7a3c9354144aefa72c3d84ecc2f0f090debc908be53319475a3322138892ba0cc05b608ede2f7584b1bc34a1825a3a60f53b79c1313cc1208d5fb3849566c6513a2b0b8afe2ab498c3b2472917e76095eb28fd43f3a7be49d8f9e96dd92518acf760d5121b3db11f99f9a04591b240a410c54b5e37164b3c5c1871cc497e45cc4e35ce4805b33c32a4b8c7f2fce9eb072b1e83d59292385f9ad98680634bb7b316901c42c0071d0c3af0a085daab0f4202b0ffcc5f7053c68a209585d6029c078a20f770f28bb5e32a44e1419e7a09d7b2f68c8afe0d9e34f7271a522a66a8c217114ad1787b5325cca0f3b433da85bbebe7c1dace28a0f753df84908ec4dbedb9a1038099fb315743d38d25ea42f6b871b091e7f3cfc7e4474fbb61d1c41da9ea06fcd79190c44d1bf8e1353737c8141b860602c88996edf16a2199c8fd057fb8f043f0cbc0a525703f4783b7a8abe8af1322e9fddb4c3a6f1a244216c110190014d924d65dfc356889e6897dc2da801113235b6f6ed5e4192edac541bf44d4feada99d212ce274c893e60d25a056377d1bdac92b5948ec34bf24464e4b72ba670b44c1c814df77aefaaa4fb230a13d25e5f9a9aa2b02620ca8c625e8278a1d9808d44cc84d42fab6ee052a0ac11542f798f01ae3d0b93d6bfae59ca1d61b07fa80dbec0b5f5c5540009da64b27b3f7971d570c01ecc0da067e0b17c242f7e5e9dd5cc0ccf8dc3c015c28e6a6d3b8521f2a03061386da9ed520075a7c1821d328d0232e78cb6958651d9fc0a4539ee94c449c5c3f41da5264a347ee7a83cf4e085b8faa2c59ed73933d5cf10660ad9fc62a664956cd01f39fe78652c9bfd378311ad8ab235f652e47ddb6d3327551970c71757225666bb1857b36ad35880b499bcc20093434b4403f505128915696687b895670da5bc247e8fae073a31a94b8ca1c3242f6ba5554269202c0f288dfcccbd53294cde66efa78d15fde3d6ba3ca0e0a3a970545bceb4b264eb57e2103cb989af5c2a96cc50725bfea2b35b00064ee874f2496230b2b23fe3e4a9f49a19035d88b4d514fd5b5de5bbfa7549ac59bf66b3aad071eb4acc0ee1c85bbbae4ca9cdcc25ba81671603edd33824f5aa4bc75432fe160145d6d9469d08833214c6fd2ceae943a4ae8a801463cd76d279b23783afa598f1a0b90366f0870584f66305a441eb740872c9ece726c1f4969dca79332e8039acdd3758307537094bd013e3fb3aaa39d53330c16b4ea61b82e92f4ba26449613e4b201b442216caf6e0b79ec5654c3ade69105934114ebb1873c5199389c8ae0e8a84e54f8acda718bbbd548a045b0ce388bc824de10cff145bcb87cab834a17eb0b3ed982808237eec1f184b17371d42afdb1fce0fb67b03d99638d5c32cd07a75b78da75d0f10a6ddb02ef9512587871d2e7bc18e0898fefa7cccdc6dc0d3aa1dbc63d0f4c83868529de58e3b377e7d2b47301e4fd9e488e550624617f160cf0747e4d73835adf38611b42de59f219424c8b2ba16b68a1f40a5ed49e1539c202374f8627a101429cf7e8718d480bb62a4696895fccf6ac542b1b28239fa1ab426e36a13e72c08ad0a5214586083ee3bb066677b6aa6431dc12b79c1d9ca85bf0ba56f12c2515dcdf068371b9b7c5a4be39f0ebc428c7f29531d5345aed73bad115b3fc455485eb7cfb968d42fceff957367767c6b6509e85f325b6e7344a10c200865d882d9a24e4b4b3c968e38eb30703c49dc3c4f2f44f9df9e09e68ea853ca751ed3e8e9dbd761d6dee46c6e48b0ebd6a39e77859f1d9e419fe46d30c2c1e94fe0828e1fb140d3ae158e31ac8959ca0154d040cb522bdad3de463a0c516426bc848e72c8c09f108c8a24143c7ca17032c98e5a7da66c21912bcf342c3db30c1545424d560c23e54fa9b5b594e0830cc4fea7e667862841be639f8b27930238aa65a604938e751be20ec46670ace82c61607bd2a688c3896882ac9bb5a8e92a1aba6404837973d7ffa2b86bc559a4dea01e7e7ee1586d31b140c94e09663f234086d4d79725750f15d3298f1ecf3bb0332639c6d121a3c5b41d611ac55eaf76c1b24f66ccbeb4d34c8aa88112b1edbdada55fe9ddf4d2cbe3af5d34b51536ce76bde23b69d5bb0f17adf7c63fbba2007236858052c210bc8c8815f37711c13da31a4fe6fccdd41d871510345e2f2b8513804567f4539f805d1d52aa10a6010ec558b262323107a638a6a97cacede0c35b11da9e300c96b4440f3683751763bb386b8478c7068ab44d62b4d1a5c0ef121a3bb3d08c26e90f56891f9ac9053ef11b93b5cdaea53f899477985e34b02a3886a18cf035621f7fa318659a8147f6ee0d9bf30689bbc19221257c62684c05b12515f30122668267a089d2c694e795c053fc2d6b4c1118ec02d4ad0eeb51f557b7bfc6135c59ba0d44a62e3194f3c1185e5c318e769a2b242bae1e25f51e2c782dbb1fa778943716a8d6fd47d07f12b19128c5ebdfbd95219e481bfa20e6222c2a133a13b9665a6eb79cedc7f91a051ff915809dfc58531f7ecfe16b2ce4faaf9f0d29c7fc5b56bc485052ced4990b080ea5867e083052cd3d034eb1dd00d658f1210409bc19b10b91a10d5b1e09c21f708aabe02728ef1eea7228c630a8849810932585441c9a16aaee125b8085d8d12554ef17118e1e67aebb02529e7612288912cbb1eee1094029aa8f8718146b47329a2685dac9350829324578aacd80424ef801204e3874a41d415e51ff2ac74097531dc9e2a79cb6c0baa46f6c2d7030b2f05a2533b2e808e1d6da631d865c59aeb3059f50dded6170562ffbc87baec343a13ef1bdbc4c5e54588d1eea03b6516df833a3cf785ee1910696cec06df8b993db6069bfdb7549b0b902c507a364e670161a4140d7ac96f9461ec99413604d69e24e830bf048eca1a74902d7c58546b78f754ccc7dc14831f370db06956d9898afd0f715a48dd504e7f9e7ed8d9c74cced1a7fff1680ba32ff5f46ffe70a5a26dc1c2160a2038368f72679d04ed88a140bdb4870b1a3d510d650fa720e4e4112ee96d9a5b7799df8f6f80abd7fc0fd45f6a83174fb97006fec93f15ca5b0ec82b2f64657115cc00ed9a8781eceb978202b8afd428ea3ae837888661a91905bc765265f5ee1c44d35c66c18ee618181cc6f3a650b9e50d65f064001df78f1264395380662b270f5d5549704c65a87aa74daf29d919ece02051c69777487458ce1f64c28e324c05f365953aa4df54d7b570a6125201f08aae607799cde0667d953271fa749ad34d0a68604bc6bd15d224c026bd6ebd1f345c4f0972b1d301854649de1b3a9d0478d4d3689832c84ea713c052651829e92c6e008fa1c4a21fcc044bd3ea497515868734f3bdb60b63409490eaba00eba53bc032d38e9f3a68128f727d9d307223c0b2af36336055b8c02c7f4772ca33833a8e212d05ef13a5a3ed53a701b518dcec9e72baf679185a67cba9eaa57e62f1b85d3e6d4fd90e91fdb6cb8b25e2a6ac1d6bcafe58257cbc5a98ed74fd8fb9b034a5b92b1b3de97068ac769f33f9cda6ca5e0beb8ebda29b726081219ed27518d5c7384100fea1bfee76c175c445fb21290df05b66cc69c39aec8147d4c6250f192fe6146bb759a3db4af388e25315d1a209d7bad9083a0b2e08a0c44e9417e54309909576530e59489eb714fdae608839808ecdd7ee53199c3f8dd0358cca80f2c41a7f0407f2999e4186e87cb14b378601bec914b6d7b05baba36d4fa5d3a72f9bdc0f419fa021e90943f949445c649969bdb8e8d2385490c91dc98c25960ec140f71a11160f3a12939446e8cd1acaca36e2735cf944645065763bc1097d1d5644c72ed2e21fda21f79c57ff5f70e4f3f49ca53b9f59a3ada9126a8fe3ad80a454036c57725ef677ad0c79755217ffee03a4f6589729f99ce0557067ce28a53e0b3e38b2002d442c1ab891d19015824c155abeca541bbedbc9781cd08727ecf3cbf544a68d34585674b9bdd1175cf2b54b9bce5b71c65c09d6243594e74e0c4d0efaa3580f03d1ff01071b36fb1b4e7f44914184fc0717c3e20781b7b75483ebcc0d224a4d30243fdcf47b5c9795d746240faf549ed600fa0b6efa077ea45bf416349df2a0ab623d3438c7f8c0f10b844ce1ccae32dc6dac70487964f58793698a7777f18b9a1c0ce44f4f6f5f137e46eaa506ba2772a3ba7d18af84cceb74f7c48cd6bc6d317974a98a6e2fcce1cc73352c9a7009cc5e7abae48f3be5314200331496c2e8f5b578a93c90975eb58c5729bbb7b8dea1eb88e91c9f6159f649ab4f2dd960bb2280bf2ee45572997b840b1e08d8f062f6ef72f31ff6c9cbb9a20f48f708288548452adee21c6510e8da431b193162cdca1fe51112b91aa9d4a8d581b666ef6599ed381ce517c7d721b828048809436fac5e77b06c8155a74faa3e88328969bee482582a4c2f91fce81e92566989bbdd891f0a412a383872a09e141b3bea71c3ae632f995087b11c920fba8b8be87291151ddb0ab19628f426a6b2b8dbb2a141510655383be8fb40e4b17f11a8d2996fc680d3e12e0550a5cd38b82c06f8f5e9875e56e644697410cabd526fd0caa18ce8a4a2bae281e2402c3981830c7ad4db6cd16827e62cc7257a09a55db20ed33f0b34c50a8fadc3f6b4f3549fa8e9fffad58dc7a6f7d3afb27c4d89d5b57ea46ad04accefcdc662520081eb6b4a32badc2bbaa692016eaae491f60dd4222c503e7e0ec848ed64b425f3658fe5e6a3355e8c173516b4048d033808bcd38eeeef6c8d3f2a7c784610a13999a2c5e8029dfb6ad34521e4a0ac6302cbc7d4fe16a969b837679693daf0c65948b0df06bec82410d4bcf5042c154b00591e3aadb8068b6a9a57580a6b15660de80dd2717ee23854e7623c622f4a47913b8f1973314ddc84e4b24df24ef2407b7a135a8f14931935c20b3351524e27ee2e3bad394e032b6c3d51c1d86ce493f8a28a381669b9517b01c4571354fb317fabdb07ce0dfbc37b84f36096bad0859a20e252c768df1be4b53955d0ee1089557d33cf5ed1a90295d3e84048dca29e79b07d068bce2111f627f5935341ae4f6df80a25b09aca0ea833760cda0d740f545670e050ab8b4a1b359a6fa482d29015d5866b03a5b099ea0305167ff428ca53b7a41bfc5a6edda907330fb609cc565aca985f6d786791c94dcbd2b589545c60d4428ed7675a03f9990323c3129476af0596f764b3aaf8246fe0ce28253f848dd64386fdbebcee04764496836610d67e857e7f71c4133282feb86ea395538b50e17a307cef7b9b645ff568a756befaebd539b60e8f722200b8af4565726130cd7ed757890b7de9a2945954206c9c6a66e1c9181bffab3b7a8bd238fbd13c53e16e011ef82e4b69b0b6f0e5973050e57024cc204640a517ee568ed70f4416793ad9254530f4cd93cf639474c989128061168170001ee93effd6fc27f8c080ce6449f8dc3e1fc517fbc804ab0c53de42a7f04a640760aaf5087b6d0589641ac444ec07ab2ca54e7dd7ef4717aa1467edb6237d629d53c8806873c250468480efd3f161eecfa0bbe1fe5943b12e2598838ba6b19c86bbd083b9199cfbb97008fd583f26f5f7fcb6944519d19a972eb8ad07e6394e5833ffb539ea55aa5cc244a07ffdfa1193d49941bd5d2c97d5d92aee69bf741567c8b1bf2c5eab2c76b521550303e006cfa0e1366a96c72f2c85a1e5c4bc702c02825594518590514e19eeb02745318b29f9fd7c13e8282a2feb4e2b531b9e8c7df7fcb51db225dad8eabac5fe315309cde0c2f244ab1db10ed4e52e6d3de6ead0ff1c1d512c6cb411f16b23c7ab87e8f9f24eb448babbd829403ad69a6ed3552b88cd38d9202f7b9ab25432fdc96a1d91bd97c75274f4986686ba2d5a90bc1d289bc8205759c92a093215b088f25485cb255b9e38ef9625851783a8db0aa649d4537a727eccae1e867a101d8dc969d7441db1e0b203859564851fb78afde2a49f93be9e1d8ac2af04c6181c3f142e533f7ab61cd20f3d8a887538136602a8adf11a39a03f9e71e61c9be2dd1d9154f72833e1da707f180dc89efcac9e1279a6bceb610941650605c3418dc017d02f6fda220dc926ef9d736b950359f53be99cbe448eeece809aff88ef868772604259404b2d44197b00bbb4f3e78d09925454237b98c49cf01e27e2151031a6dd39f6f24b7ac26cf22e782834228a48ac7d9fda15b4f9b781025903509d6c6f8402589162c0a90d1e9b6d3f501c2beb0199491c6780d6b7bcf12d1755c2d93785b2d785948193444fdcd327b8e721fe504ad412eb91119f795792ec994d2b11ae86335f6867697868fb353ad858070492dabb429de90ae12747f40927d514597c52beabbdde932f524ec858cc4effc5209dde3111d5694cabc279503562f744c440f5129efcff0fef43b3ae3e688e2a131a5eee30458384fa7f4ca70310a942072b814395b66d502a0115959f033c0ad5660134b246ddde59696471dad6341a8515fa27a47168484c355a9a7ae85429e7c9000f7e184119ce3ae48de2830259163c1f24a110df64c853e70fe5b14196e7c46ddc4ec6617aca0a2894b78ca8f3c20dd81d63d36f8c4bd62d263d17b078a392b03668098ec254b6f7cc5cad434ba1396d2c52baa49fbd737e9da03b14f91a42ed76d0821d2ca35415a8a2bf4fb9f8a5dd42e9522e3fdab8070556a3d193d5d0a777693171fdc320b11e01919e2e753f18ff92c9caac50e85cc402451fef3962be2b46de75886dea691eafb007dd277da62588895d6bc8b3d12d3f286db4e1da28f44e1ba83e21bcbc6b54fc7bed4017ab8e8ae9fda042c545fa2da5ea293a130f951882f474dcbc08e0b424af7fc68d9bb947ff5c3d14787b66e50f879c7c66aad42cb5b92e37fbf898b52264838fc6c77bd68e5ac5c2343940d00372770dbcc85461dd4809c204aad189e6df5d363cda3140dd465b7723e5688a2a36025e9b5d4f7cd018ed0736f64112d43eae15b037159e01316588281de01971ce9b60b204492083f08a4018986b344db18b35df19c8a3c022f73cae50c3ce6fc822676dc57048218e9a7fc00a6b2079b541c288534803c7e8d552222316c35a74619c5adcba6f0abb1022158027f31c878e0f69ed31bcab5aae8361df2d76aaf1ef0d8f9ca18a922d3da25e8e900098ad7848fd08063c2571fdb38300f82e3ab45666a002f64c5781401245ff78fca8baf8b98265f8e0114b9d93d007af8203f7d9d4e75dd4271a52e3b04601a7ffd03f00543dfcc501c3a9f702bf8591404f60bd605e87a2b8f21a8f7cffbc9a7e6f6986c9a7f5f639f1ca371238034449d894d2d629cdcc1f5a938506c1f766463cad83adbe8f209e109aa06c106bcafb1f6eb8190f6a6e85fb421be5273396b735e6520b7972845cf8188072c2b8241bc0752c0454447f44cdae32a2818f6f1a4a8cc5a6188763a031992229e379d2b25c1c8a97df713035504fc814548db43f566e3e5c6e8bfa38174e7e7c56c39be6529298fb26ea07523e3b1775423da0f385178d62afb25052ff2cda54d3ce0d22f664e8c0bc9772586262fc4f35ee52bd579331aeee629eac3e3c3ff562886bc264f499489c0b766e2170e275625684d9198a528d2e84d40d7d454507db55e05184bad2dc7373b1f167f79f10640628e6f0565c80c3c569e9d6c3ab6918a36c52fb55d1f7d8c083bdbaf119bc2a49c6e4a94aee978f2da2db9109e6c488f45d6297756e7a9d958ad41fd3bca7bc3b619d20acf06824f0f52982c9d7f6381eafcf0e61a0795b7a34331ebf234f4f432b7da384a2efa8c7ad95e8f632691cc0b068bf31d66a130258d5eef2b347726a8e8472124f3c97f110b7db6a23dcb92631c6c4d81bfa29fe87321504b554d27139181d2732871318ac47cff08bebf1b252645d6561530555f81f1566a0c2f413f51992fea9677f54c2ff11c8150f904959003196cd4a5d0ea4eef120907f50e522acf1d00bfcf683e76b300b7330857c0ccf786d31b802f300ff366531af5b1582266c7f91908f8dff1079a6d738381ee2783db18f55014b2f891046a13356a8e40707216cf9c95bd4689978ccdf400861c368aa37f6cdc63f10d525c6e6f21042061ba36f93ed47c4373afa21135f2bd82333db7857849544737143cd6061fb66b0fdc045893e8708bd529865031467e28bf0957f2336ed6a91309b5713c3725d92ea3ef0f6e4a76254f3d827d886bc30d248ff31fe27593a188866386ee2fc68f5accb27b3112cedbc3d6eeb02bfd27573f3f538c1955297aff7824b3db92ebe107779e7f2bc13d47e217849f22442349088eb19edcc1e46900773d22b96cda5cc3c45157d922b7c823f41a0b164d787ff20997e76086b70e197ebe17f7d765a1f1327b5726dbd1e9a22c689d5872990aba7289896d209881188a3edc376bc479489f266730575555c85ed9403756c4445572d7054e8a8192754f20eb34817427d140d1223975cb725d33ff71abea0c86d111af3e3a502b86db69d594f051734e6446ca0698b11d071b0ae46663be269c71557389a4a52ca68bd64a494dd6970027cda33bd6346a86ea91be0a07c853ab81ea343c866ae4db8af912d5aa27ca5ec8599932a4f5ae28c249d14e86d5f8b86ab32105598014f81f9b808c6623c43875ffd15f8ba6f10787de8d85967c86cf9d72882dd2ae98f6ff4ae7d59c7bced3156250120e249f32d24a1d3558e9e2f8bb737f0d5b3a1824a78a49c98cf809bd9199075a7efa73b1a9b80d95ffb1c71557e1a45873fee37b970605e321ae8e309a53b9bd32c862d37f5fce952e61135f90d960b962fbcb65c2aab5cda8a7a3bfe1853c29b94d65b4e1681051c52efaf18561317cb033818ee4f0aabe8ca0f809c764d0ecfb1bc042caf58bed68d47ac31a325eda8de8d05e4860bd747f933d2b88b6a070f0484e410f286f96fb35d2d4752b54c041a033b87b19c9c3c4e0ac3b78dcbb98a7b50ac3e0a2826cc910cb8e06b45048b8536aa6c037190e714b24d6027a024f24b29f15397635292fb92d20d701eadf366af60035335d38141382a466c594dd31116893b160b2f946811b1208e236d25ae4d7aed88ba70fb9862618a6274a40b96455ba2f7aa959b1869caada8eb66d6fc0a40f1d41680404b14de023d9db2f510b0dcd7c3b9166501d2d924a24a27a513568f0dbcf6940d04376ba3df69f21b2eb36c1f4826ab784f24dffdf0219963451a81430906612d4815db5b4aaa5deaca57f30baf779cd638b32a93e0b48b99cd4098967030439bbec2c8ca32dedea1bd572680fc133342fe434ba9e8afadda4382690957d7a313043810f616bbce099ccb5e33cec0d0ce02478103c1f056002fa36a391bc37ee90f8e4b4ce8cfa6c35231d3f5193f7894c7ec4f5b9cce5e395120bd5b9dba93dbebd6fe690378deca8d69e5aa59326f6644e160cba35ce3bd728c0166fe5a0286434b9afa561f6321521cb10c95b6a9c6edad9c580e10f1c98d31c9d11bf2ee2c6e02c55e58ed971598a17d2b066f57ae17fee2751bddb714e93c5e58b7bc50a85437c26e85959fa425558efa47f23c2190e118a469a710bc77b40bdded6e82085641630855687bf8cb0108786a57a1709a26f2e50ffc4cbdec048fa8826bee0557e750bae7cc73bb920e353b7947504002fb316a659e9b3116ce1f11d78bb4083806ec83d0b050be80c516f19695dec586853965dffeae0a2cd4920735a3a2382ecb16dc5b0bd33240d011a7c2e139f009bf4bed307c72034de0668ed1badf02d553d8f62f6550382983b6c317280d61c3a8eb0bd9e3e1c4d77c99d34520c814bf2938ca44fb20e2349db6f88ce43ac9bce7d04adb70a75e1995573878913869501a6199bd3fd0c6fe4a09e30bbc6c53c34946d8d99f86d73e24434997c0fdf5e809338244c87e3edd1f5a5902e31888a1f1f3573df29bcddebcd2695536b2f88adb6be18b4af9fb89be595748c9a84c284e0e1160f0902e9d8a4cc87c2a17914d3e6aa954faa0f96d44b151b2975dd3d0b93616399334fbe18904642f263babc3cbaef241e807bf2eeff775bb5460a3f10bd83bf6d982194a1b0473b963de6808ab2534f3ea98e11f3b7ddadaffaef2b1591a5fca077dd77829ed5af3d5be2d7ebbc5c02b51f7622f31844296dd6898528250df65343e8c00531e199b214a17fb6520617cb459fc8fef26c0c061e1cfb598f535fa61b5c5c25265d535f8c56acabe64f7afd437eea90e220cf88c0df1fea64e9ed07ba391b09ee29bb6093b2f217a2b7fee29bbc88717fee95ea7ff0b7cbe058d84989d7d33370b2809db18c8bec4212e0d95fb289d3afaa49cd17d6804c3f5072355559e96511e656123f2d9ea3950b3b91dd627d826bd0ab3cfffac94aa73f89b017f4a6a11fe39c9f4621a0c97a53218d9ddc581345771b71db0ff642d4378fc670274ddb1eb0d89d4f87c11db0fba03df62d6033af61de8c388c619cd7f48cf3bb82e64c624799138912e91ca5db59074ead5c90398ff0118535daf00b0878a5b01aa80b45642f2cab7925d42924229f3b166520beab7f91416c057ef0b6b5a85891c4a970c0fb693b7437701291aff95f7626ceae02121a3db90ae8b0ed8c02acae065abf4988359525b5a5e2c0623b9a549802accb8f8435f05686c0af8d36d327f26959d307db26f70aff5d1ad1afb46698a980983256c886ff773242b656a993dde1b5565be8ac46709091a3377c436d4fad40fd7ba8ff609820b83ab8d7ca1f464d210d895ccc59a4e4513412931ceefc3d0318d46801df2dccaa533df145a39f46f4ca32317843d239b2895e83690f5144d573233da9b1bcbd436fa009fdb24a33b5219cdcd9af33739d681ce42b761accd7cb3986f26c6fd4730f2d67ed5a4c04a4720078e2f2131e8435a92c79c86626d86dcb34a51e09ad66960b02c8d0e1faa92e64edf13c1e2af8dad34cc6b43190819a2f8f74a01830491b966899eb3dc37b498bb0ea12c09ce337306cc1e5a7b8ab9784178ce54a6836005d69c9e8960b78b01068d11c843d190a3e1d5a98b260474280925a181844e5f80dc308e399a27f9f9f18e7f1b67b2c2f3b72f580b8318c66f477a721fdc408505b8559893ce4d02a51c0688db161b2f45ecf745b7923721fab270fdf2a9c083f4d442046e2386bd6522c315e217513f62dc3f5d2b3400f0fb3f4409b672ffd9067faf8022aa3eec90f21b22784352b744f7efa9a4f635eb0316b224a1cf8aaa4a28ac7a0a83506ac9fe76462fbf59c697fba49ae9ef81452b835b50c42d12bca162ce59c51d2868104eaf27bdfec21f0c8962c50a2ee7f543857afffd41a194f7c02ac6e344976aac8b7141ca5bb447257bac64b0320cbb9697b0d3bf0e3636fcd855c248454dc1f09a705871b58ffe1b89db5873350d282617a860b6467a6657692a410ed25c55de3254d22256f63ec6a8fc9dcfcd00f17d82b9c60aed47e28b53a6df35d93b414ee08783f84ffc7acb66dae5b3672ec9041723851958f5ec6e8666b9ed1242db5ca461bffa69e5a985b506f8722640ca64a21b4d3fd7821a524904ac21b1e9c87ab7ee0ba85a678aa735535eb56f2c1d653e56a7446d1e95bf509e0f549929723ecc888faacf1f5fb671a312e01b27c7b128b1624e9257f1554ef9f1250f1597b9a3aab8dae0d26288062fadb29a7e354b73a4063bd574485499d157081af88b578b4be63ff4d741b26dba8243fd4aa315e18d692d29fb9c0a372563233f3f02c84336923e8fcea2cafcbc3c3d7ee47428900a89e7e1110159ae67a2a55678610362a356409d484e9d7d5b844cb35164bf047e764998e3a0699b158769c3a776b4db3326507548699b277abfae55d0174de62c88b126ecc531dfbe998c146541f0b2501f9025e24f6c0f76fc30acb94027bab04eae641c91cb0c8d2a1b1597a56a268b98435d9723f03b94d912a8b61bca2f010a5f4df70d348803fb1644300a4a819874760c46f3010bec06420e5b3c1b142be370172551116015a47213c1c7d27ac4623d77ba28cac9920df62d7349b23eb8ff7d58ecd59db141481fecb02af4e600e9a2d7b4cc26c822c48628ab90f5ba1b4d304ea104ac47e74972243c4f2fc096262c27a5ab8c25cb031cb233be1addb24f253c112cc6722e0dfd0510fd426d8624e0c10bd6b244130e94fd3d768ede0652b9bfd5816c0a8183b4fca034fc097b640f36712f381306e2f2b8beb0e36ff052b5eeea673906e8f13a194b907317cbaf9ef1de98e53c578c4975e520149659bceaee4d36ea8c7ba6badcc4863501c734484e1958577922c9e5001c4fa97be1f4adfadccbd78f2649fec8671be52b4be38a31d822a591ae57ba952d786ac6c69fc76ff909ae5f53c7940adee945a453a4ab7d5ea8e708cec1378b74d82783409105254ca02162961afebfb960c0ad1e71c5815286ebc442737435bfda20c8cf3544691b81cc8b0074864446eae9810320437d33cd51ba896c53d14c4b8126603966fcb08346158562ee1b06888847362cd084168f88bb3483522aa88ec1c8dd41009b8bdaf1937c1a83c0e8bbf104b2008ea97ea00d9d7b0a276d953e1b4f0176e0d96725a1aee8ac172037580ee5b0461fc8acd058c40d7e8d27295386ee1c6087ca8aecd467602ba0fc63893cfab76350c1a3390f097f200aecc516aa67b00530e19b5478c759fab78dc2a197492dc7ac18e2ee348d287113ee76ec4dd09de7a697f9cc72e8ac06289e6599faca65949874e5b4c24132477784b57f437818dfb48b065cdf814d38504e2eea30c70da9514fd1af59163664fe150a4ceb3c4d7fb61dba31e737bd231d9ffe6a8951c2e3fdf758a0e1eec12397690e7f727efd2a9b64a48e320dd56d663e5748e756c8fb0133cb89f2efb719b3fb8ae0cb8e346e3ed1ebb391a651613ba8de72f0e5cb822d9a3d099b54b6a2e4894df1d74696916af035a735c0f04af58fc0e024a54052bc7e2819c8cf7a23b68475e0de0871489a117932bd9b93938fda00ffb82480dc818cfccdcf1a4a83fbdf854050060ef3373f6b280dee7f1702411938ccdffcaca134b8ff5d080465e04c961c544218394af722c0f312b9494360ab98a9c184192a25b8d7c7cdf24ec6096c4b2c6924ec8977c7b554830cba72bceeb79f4aa3c5945b1379278461a931ed8d8abdfd837de3f6d4ad65856a6afba7e681f2ac99982f910d570730a12fc054bd22d461c538dee846de471620caffc42caad8d17a7383d017a7c030c6ad68dc7654472704bde8cd7ce0364cfe371ee5bcc9fb5db9775ce7938425da0cf0fc79268b3d4010cda2276d3de84a1b51c789f4478d4abe3e5c12c79b0fd5ed77fc2e4ab6804a643e1a9fffe39dd19e1dddc38d2081b1cd8db163c419247453aef7047d0d31e2a90911fe76039f0ea31df13b7b33af90da5211a805f97cb4eaffabf9833c4b73acac76ac6959ec545f8620c4973f6a0a216543badecd04db8869228d0112bc7d591c13d21667f917425618d1dbd62ecbf7232571ff177ef061da07a427c1f4ca80ba11d8ef78b802b32b02745499738d46434675e895aeeb0ab02d600f26d825a81e6fcf06ceec80332ac192c402d3608e78e9bfd991e313f5070e3c839f2afefa54824ac81cbf15a01f7d7e1a7885cd03b28b86fdd8ae138c2d8ebee0f127c01b6a0eb6eaa638e8d0cf1a6cae7aa2b1080541108e3c66ac76e295a13a4f1cccef609ff2ef58c2071f10193b1fbbe5f7b17bc70bb1b9050f35612f24f3f00329de2e038139d76bf04a638391be3af4b2f1121bf601c7f7994595ef68d5cf2655fd8502ec282175dccf50ed81d32d10dfe703f284edad0d39b823bed4d8c0f1741d2c5c2894ac0c97c2456917d96d3be1d1e6cb491cefb5216946951a197e6125a9e8068f0323c11dd5808502edd6568fa1b038eff4db40b75ded1c504ecf355f2cc86f30a9dbae17ceb82d6e024945985c5e2c545884db9e2d699dbb8d45f4a6087e845b84fdcc3be3cb0045452231f4c8fe1eef854aabe5cd794725fc88eee0e03de83b39a733da9a0ca8d236f386274bb114f5bb2fe76438218acd41a4e6951208e315ffa0c92ddee1202ea5702cdd6985e65867f65eec19ac776d9c22948bd8a46b80068a2c18083facaa691aff9c39e735d42cee3e700d8fabb8649c02368bd098b3d0503a6e42b661ba5d6236e3b27a7b1455172a12234581e4399e3097992f27ecd170a893c4edd687deb039a60ab35dc9fdc445a4f1179637feadca27ae00a2e2221606c0f60db08a0616b3e59603b5831f41d1168da2563cdddc01e36b36a2bdd4c9bca880c30afd51b2b65090ef69b97210d35fd8085cf034f365f26788d098f63e472f8c0641b966f9dca12639834ea387b051cbdc7740c6c32aa89278855824d4169b3637a3b7384286e964efe327362d8c766074cf6b795daf084c75c70fa2fa5a696176ccb692fd7bf1c9ae2413eeaf631bdde5c0e116bb83638e96032af5e49a7320ced2754324caee345c7b7e58efa07a0b6172436ba1e012b3d47d28408c4954bb3285a45b4751a21c6cc175b63d45945d726d40a25a594bee41fb6139ca182185669ef7452a7c0fc6de669caa635c6e723eaf674c6e764ef1810d89a4e9c60f1ff851cddacfbf1f1f04a62d1f4e71d8325eb73fdaab9cb9bf7aa5e3f7b359c3d6ae561b5d29ab90aaf649c4d1bc6849a90d974bb5c87146189ece532f0b7b6913f69cec80032a49d7ab5620d851710e7d935237a70e0ef3939311a6337364364249655bc43af0a0fa549ea324a1267918bf84fdc35f3c1879d7ffffef9b07f54abb6e52ca551e7770103004fda46a4d9243bffd6f3f3bcfb624b4b464e59652ca9b0a4b0a7a0a0fe61e30b0a1a175e637c9ffcaefda234ae76fa9fe8700634ae7678d2a9d43536db5579f5fb54796ce9fca8f4abbfce61e5f9a87b74a780e6f5fe0e369930f5e94871c78c841c78e9a1701e2589e8cc30d2f5082e77714e1383b8a28716aa591662ed448b31666487318dbf34aeee0e1d4ce5a69d4de26e1a49f39eb6bf8ea673c7fc956fd8f45c251358b849335255c1954bee9a4711adf3a67f0da5b179470b9273331ced448a2e63da799cd9099c83da1c9bae02d12333564bcf534c6176fa21050876cc423687bfeb2803d02687b5ed26228071e70b8e144e737c184392b16116f81d3b6c067fceb8c71ab82cfe1b42c703bc4df70da1d6e6d8fc36957e0423c0fa72dc283781d4ebbe33a6f8d8089b12d8001c31a61772c0b6c117605768725c25f3fc37378ab02bfe1ed108ec35b9bf3f042b80e1f044ce72fb78b8fe02d70a6c05bfe75beb8083e87f3041e82bfe1248183e071384be03f3c0fa709dc87d7e14481eb3c102f29700102052730410948f0920217205070021394800423f0d7b7780e2f02bfe143e0383c089c87ffc175781f5cd7fe03aee33de0a4479df529be7a15cf5fdae4ffe8d0a1fa1fb2870064018242bce988bde99439cab7ce15af7d074ab8128a3a2b2d01f5010fb098b6e73274a5b484122e34754025c5f4316f3d8ae7ef36aa96cf323f497f0977d15bf2e2caeee8a1ebf81d3abe0c0282a23454d2c2e46909570695509440663986ab0bbc054e0eb8ffeb3cbd029fc3993a05fe8613033e81c7e1cc804be0793835e011781dce0db8cef7e0a5a192163a1e6f20d5400630a0c3888eb7382707469fedf983d1c306520d640003a10e0bfc0572f80af80d4f01c7e127e03cbc045c878f80ebd03952149e99c8f4e3a975fe019e9f44e9b8a9e3a91d3529b21701e29cb829749383e3173b84a69cd2e45f27cc13f0399c0d7004fc0da702fc008fc3b90037c0f37032c00bf03a9c0e701d0f81f254d12bde1e5b5dfb8b93bff61607f63ddbf393ef627b7e1a7f8c3ef2ce4e3d1e083cc0015fb1ac019e33e077b6e70bc86dcf1570b43d1f3b92c96b5f714c3c874f80dff008701cfe00cec31bc075f80278ed3b9cfca68ef7dc74665c7e0d6299ea7f763853ff93737e11d3f9c74dfe0f014c02b8ea67072e32011edc21a71c5374fe5045e707b7fe6e45e7ffb6fe8e7cf035aa579f7296b7c9ffd9c155ff93e3232a27e5d94d1f5f489a79da32e5302933e5ae1d3ee74799ce9f53bfbae875d40e678965b7e3a18368afde2f9abfdbaaffd9c16ba81ddc4cfd4fcd4db9a795bd9077a8ad58395ffb01b8e94fdefa12677dc9575f7a7ebcc9ffa9b9ea7f6cf0d4ff08c0c79aa36c7053007eebd965d045bd5067e50f2a7acda37d75c801871b1eb73d3f7d1ec3f3f2073549197467626c6f68a7a80da695edc1a429db73017cc5b2f24bb1ed91ce231d9f89b0cc739d2fda9e773e3caf3de8a2f3dffc7606f93f39aedf3cca394d32aeec7ad753a6e6d1cb0c93970b2626771e7a3ce3ad82b18221dd78db9d77a40d158c150cb51c204400ae0582a452b1544e39e9b62fea4c85d8a86b1d1f619a17b7ded930cd0cb76056f90e2962286edd561b94e5d6579886c55b292a6edde5ce56dc9e36fd82b6e7e6a7f65d7bb43af5ad0d6299576259ea593bff8f0c5c99cab2d3a957ed31ebd4a7528fda1977d1ebc595ddd49b7b0475ea4f432f8f8fb0ccf3191e98fa927befc5893af5a64d94ffa7c563ff2373d68bb78b66578942ae2c03e59cf30e0fd351aea4a4a4a42b7726d2a51d2bbda3cff6edf876ee8ac5ea66907a923874d17c8a372ccb252fe71d7964d8768862ce08cbeee974f2ee88db5e1115a59e6cf1d93a7bcb7b4758965df65e0ecbb2b3fcd7fa6fe7a2d953fee371d1ec2afff15c34fbca7f3d2e9a4fd2afcff8d1f6f2e25190fb6ab549228e24e24838d0cb0d6dcfbb199429c6a55eab64417b3c526484a8e54b2cf3f22a136dcf4b9dfa71c7fea7a5a5678b2d2f12615911961921c925b9a8884b7d58a2530f8e279dfa6fb3be53bdbea8e7a3a95d9ee5dc2145cc898880cdc553efedfc3f2c31cf8ee2238ca4a19cb68cf993031062c77cb41ae5edae64ecf4d0b7f5c5739611954f8fe57778477786eb447c80456887382f882e6ae488322db7e745db73562cc63acf54b76332a00dbaecc59517915cf4baca8b4617bd9ef2e2918b5e6fbd5874d1eb2c2f12f1549b54ea71db73562cc6424151e9953771957f79cabb3cf578c7fea7c5f3ffc81ca5e52cb1cc7395cc597e871427a78f260ff3d7bbbbfebdf5d94ffe87c5cfffd9711c161f597e85659effd78b3aec5bad3fd92763255329cff995ddf454c499bc4b957a97ea9fa4bd7c7cbd2df3cbe595cca64a76b98acb47ac4dbe6ed7d7fd7a7057d2e5d2fad71e3b92cb86960576eb5d5bf6e0eaa6a4b0ac8a4a4bb3c6b192e6990ab14d4f7d8a733b95db69cab3773228d394af3c1571392967790e6cbeca535f37eb61242d97a97c779a58416af5f5a22ba44edda9b3079cdb2a5f216db83b4decd82aaf244d757ae01eab2b045fa45a5f3c4fbdb82d99f3a6e7c0a8efdcfcba4f9f7a44fb7ac9a79ee9d3d1b6af975e2e5d913ef586d271675fd75e2e5d899fa23b1571a95894fa4e45dcf67208f0d6615b80a408592475eacea748da605b80a48add819be409b172635ef0bc28e6a2370cc3eff3be3ce28fa9b9d87579677a06808a5eb77596039f690f66cd4c17a577673a170075664345af5f1c73c7dd6bb5eaaef85ae7ceb08f6659518c491ddd061d6f1bea4c878bde0478244d3c790089507edff7d59054eb0bf66e5790f6ed98999aba47ec7947ead4ed9d3d9836d4f041c40a1b67ecd20113458b3e22f54dab529fcae8c6f031b2a903bb12d4cbf80482dfe8e9f77ddf08bebc5bd38d515af784743675771a842e4389e95442d23c324a4c3b8653363b144e5d7b7473e7ed7c8ac1b454ececb627b5b69d9d869dddaec2505acfe7a8b2f5da1c4a4ca942d5aa04575f17bc962bbbc070ab2ff9ec5e5fd0d8e424c9aa0bb6f34269bd0b62385221a552e995c76a752efc2a6f2746693dd7fa92cf34ac38dbe5c29a19583844a557bafbc25a295a43abcbb2add257d6756dc559b05a5abbb4974f34be1e536363fa04bf1009d8f370002e9a7db43a1d174a7be2a835bcb489d65e86758e1fd4f7033b1b26ba630248433af8d59b6ee679ae345c23181db9feb97b26afc1ec752831b3bc292addcae7e52ee79a9c5d6befb6505af7f12ba903dcd9c7d1c3d1c7b106d9d969f0687832ef3d9629045db5be64b7fbee20dbf33c8ede69a143326a0fffaba989894151f7d5aa04f399e69c73f7853e7a9ee735c7456df876f6daa5a0e7813f02fc830fdff6ce7474d07d28c1de4016ca1fc6d387ef73ef0c01dee0e9d2016ecfc1ed9d23c03bfbd5e008f0ce61f6908c5debacc3c28cf305c525d692c2a2225b41d115990a4b4a4bcc05e5e59cf1301e03a371226352e335e362426bc6a259d5a86848d9a06e4c1ba71b2535947090726c72671c8d3302400c40480373be9aa793757402c036dc01dc80c34b8d13252596a2b282ca585a5e50b495c2cb299d0ab8baa8957d51b2a8c462392525ec3c77bc72c3b72c5e2c452577e86531ed17fea12ae0cae7e5ae05bbe86b677c01037e5e0e515656308a5e992cc32c4bd9d239aac370a999b18165319e8dcb042680dd15d0995dfdda28b8e06b713e61ad181db2802ddf4bee708cbe28babe515923043f4f3c71e517884f5ef7bb2b64cfa4a573b939a35dbd08bf5c6d63f2cbe59be1e5985e038d78e4197d3c3c247706f3eb5eeece58fcba97e4ce50fcba777467a75ff7c8b8b314bffed9eeccc4af7f3bee6cc5af7f3b7786fa752f0b5e18f526df3ca36a43aae14cbf1f3467faf5b042384e5e5a396e5a6a1c3932d2088543281b37522c998f1121b269722aa51a9812b03c0101a2c19e9c8bb2cfc421a993fb6e336654afd5cc65d240994803692012ba629272a2b0c070f986c3d568b969bd9ce0101212dab19761a56ed84011d1a7122c81a929a9e89021363f99e8b95ceea84641f4b141088790c8dd23600f26c2777bc1be9e5117064da5edd7bdd22d5ddfee97a655134db33bdb3dda5d1337c6d9b851038e1c1b0700478e7446f6f588bcb09e8e06d2442e8d27a4a3b8b31a7e5d07ddd98c5fd740ee6c06bfae85ee6ce6d775157746e3d7f5edce6afcba16726734f8753d7467367e5d434144eba0de78443450b5b911d240421ac853d1675fcf351f961697971930313480009199d9a97894baec543cd20987f6cdf176fe4489a5a8aca03fbc1f329d9aa7606e5fc7b4dc8ad572bd4c4e60de0ed7a9e5803d9f47542a4d2527139542a2aae578fae5f1c7a5c9372c23e3ce3ebf3edaee0cf4ebe38e3b0bfdfab833f2e0e971673d7746f2ebe218aacd75718c7a93bdb030d7af8b47d506fbad9de918c5be39673a3ab12fedfcc1ee74c419ed1b8073c4d597eb642853f1e8c85af1c88a47563cb2e291158fac780480af17c579eb3bc75bc7f1d66b78eb37deba8dc7e1288e6023aac06120387cc4918bab2fd66d9e06eb356f9de6adcfaccff0d6676a78219484481778871019424b20b2afcbfceda92fd6697cbd68cc5b87f97ad1196ffde5eb455dde7acbd78bb2bc4f1540ae0f223e427c2e1340f675d9dba2fa621dfd7ad195b7aef2d653de7aecada3bcf5f3eb45ffef8e1f52f0762e0f6f870a3ff6757f0b545facc386b6f593b76ef2d65fb60aebadb7cefa7ad1d5e788783bac0e8ce4bcc85928bc1dfbbaea3f80fad3454beba52444b607922a8675625b27b23f88aa17b6752435f5c53af9d6757deba21572cb3723d90b7b532b4c4ac3b67e9194b77d73779abd40759db875cdda22b63076b832c53b3b250e57ba3da4a222d2026d9dd6dc28eeea1daeb4be536dc27bad77a8a27d7d1cdd3eb82d490beb6ddbd2ba47d246af24ad545fbebc68877d754b48275226852427894e4a4b1fcb378728bfda387ac9f346174b648ee8a0dbef00ceed0d880efa287e8a7397ccf9bcbc683e79490d740b9acef0cc27e914455297f0902ab3ee89271eaa2fd6bf14b64acbed9d298a8a14e7522078760f38578e76e72195c6b1ec7aeec2d30cc61eb31446cf43d5a6e4e3599eb654dd768eeb98c603c93305f5299e296ca7ee91357097dce3191d04cb12af56b03d7a0abbde4ab54d282c954ed35012cad494b3edeb28a16a53f24b22c1d8d67517db8ab91c7097489fc2b6e8e4a7abb4dce299e2213b35e552d3907dbbf43b7bc0b952b5c9a8124b72a1d4f6cb3dd60e822e8a99481eb2dd655ff654e1ca3cb4412fdd26e0a2970115359d29b8536b8730a0ce6ab56ee4daa4330fd517307d4be6885ee2620fd9b33c7842899c336983e5c1133cd68ea38b62cb536c74f214df4c4f3115299e22e529d681ca53dcc4ca53ec635b96a75889bd32625b3795ee2926aa61c80d1b3b31382f4f3117646892a7ddf6ae07cb304d0b0459670a96ab332d57aa335d79ea4c1d459d29bad39579a6313b5dedd399d2ec74955aa0ffc1075bc63eb9c9bb1e4cd3f5bcb75f5fc40a478a52b54559bac94f3e8624ed74dab274b13c9567c6b20ee81a6ddb01957c576960bc4f5febcb0b73b12dde625b377ddd257ff7eb6b9d21c5434adeeef2bdd8d65da6f72108db3e9d63c97fbb7cb9e97f5ea76b9b5e7be94dbba5daa2d45e3ac9c78fa4914e3b0021b6d665e9bf04601a9a98181475af4415ac44e0be647441178da203bae8f5cfa8da68ebfab645cfa27f9e03d7b403c246ecb403c224cd96b6b316bbe7fd831bff9763ddf28a4e89ae06e5f592c7dd596d2e304dce2e3ee8e17fa17f2710da43d1c11388d1c5cff5094467b3e17c0b007d3c8100fffb1596611f3b1d72dc8ea7fcaeb5d58413f6308215c7fb8a53ef6c03d93bc7a9640ef90ce21302dfbb8532ec409c13b7b0dad46a53a4083ca85a186b2cd5b7dd39e8b854f29c1d6b5166cf814ba55cfa0e8cb7bd81ecdf0e8ef7793a9ee978dba367af58688341a0a7a3cf0653e7293d5b500ecacd78eaedd4f034e368789a89643ccd452d609a877a623cbd3b2f4f2fcf89a7b7c706d3d0067a2d919e821b747b03f3f49a3ced66785a6e174f7d830efa7835d633bcfb68b51e7d0435486a936fc19ee52fbf8388206c382f4e842d032714472d7ae8205896e248d23366c87c5c69747cf9151f439bcb41b02c572b57e8f214dd696873791ab3d3d066fad247973e317d1e2259ce4cc40565ea43a497dce4252ff9ccbfd7f8143f7995bf7ee531afe2a62f651ea5e54f1fc1d3093c81997ccb697ccc5d1ef5f778b6e9a99f0805959ee4e46b1f5b74cca36ef22d377de92e0f731211840d07c667bccc5fffb3e2638a9ef1327ff91577f91d447c00f5963f539ee6a17d3d0d83507e519e821b15649e2dd64a853ac1c09ca912d35996ce13a683c1d75618189818528993a5ff6129daae334577c959be6259eb4bcefa6a61d2bb4b6e7ad9572c434bbef22557d9d9255ff1ec92abbec72e794ac963254729f959f22fb9973cf5e9edd925477d9a73bbe466c94f9f8e40bbe495cc89d92567ad2f251fbd1b6fbb74a6a367771259b72816ad761adaf6f5cebfcf3a25c4c975118d3596d208da37c45a94dee7f63dc71dc30e97763f4adbc317b4adf5cfc7fcd5de93db389ee0773428ed06fd7e7e6df66c58d3b1c8be6e1f7cbb73f859177ddf3aa6a77dc31283e01e71db7af7a3f4fc73fb1df882f6063ef7ce8eacd1d876b462879e9e86c0db9e273726caf4e4b6efd7839d5a165bb5b1fedaf63cb9fd6077ca62db75775f69047d2e30c7960d8f3fb6f5d1479d81170dc23496875b1f85308ded71eb634fb579b9f5d107d358c08ff07404da36f578b6f58cc33435b79e89308d0e4ca3e3d6b311a6d1c1ad7b364c9383f3806970b8c1e69e3166d90b49b6f54e8c3acbb9eea8da90dcb6dc82e4e8d64b15caadaf52e608ca6df2746567975bbff6e6865eb9f558b561b9f5947a631a6f9e87a8a7b9e9719cf500f0d507c0f3c36e70031f739e666ff00100004e4a8541b72a879b7e3beb6bf0d5e3f0fc304df8d8a0091f1bec1c4df8d800470df9677c51beb81080fc1f029c5ead37eef65cff0fb5bef810846daf7ccc3a7fcb1e3d1ad4c0f3b3e41ad0e063e287e7976d53fec104e8638947f7123e6ccf5b19b8e5f176064efe4f0e8f1e1e3d39e7c17065f794e69139868ade8cc332cf75e8e890030f339081d0f6dc3c32c7e0edd7995fa50c02726517b58379948f304d0996bb23b582765a6fa50995b2f311510c0e1df27a5bd5c46ccf3fb64752e74759b1485a657afe739b7e04e0364dcfbef23145d256a72d4dd38c65948f2792864291031062a7506ff7ea6d2e69fda3e7ffadfa9f9c1cd6cf0e8a73fee47876d2471449234f5b8e9e1ac951e777d5a3762c41fa1b37bd0d673d0dbe7a1bcf7fb2c9ffb13170d5ffd81e30f031068eea0183938d9bf25493c4201efe0fd64300eed24fbe08c7712c8641cca53431a8af7c4aa821d40d25a4d5da81e1b08cc8907ac48e16fb3ab6e5c43088b990857a692abf6ff15cb4c79da1b46d19bb81b15ba95442a1502dcfe4e5f296e799bc5c2d6f9b4ca6b2e42992567a8aac75db44b1fe07d34300ee9f3f4f3d27264e3ea72b9c8a4e494e61f0f0c7b2a8171916c6c50549c8066e9135d87367a6eda24cd499494445af5fa1d2985cd4224c630e6159923b338d5cf414863a33a3d072ef4c69f099c6a0ac3375d60af53a4f3ea8db890c7b631e21c2b672d77922c3dc31b93087a260a465ee78e60e36773e738765ee588c5af160997964651e29ff676785f3e7e9ef271ebb893952507db9f64c6146c076ea4b2b894ae3fa7f0b895ae3fac9b78ca833944e3d3e271ba6693901645f3fd9a2d8f764cbc9dbd41d91322509954e259bf53fefdfff9c5e82ff0358ba3e4d232f7043f5e59e7c605d5418d8908a5a20a025ec0bdb39cad5179811d5b64fc6a8b224fb448c8adecaa0e268c853cfa7d5d33aa2be5cd7df32a2c25ccfdfe271519e1e17f571a295839f7d3778b676ea8b16650a3b2209c56e46271f4c63ee60b03c89ddc833f5964f6be7a298a6c5e3ca5ae64eab87b95322e6c45cad2f8fdb29490854a14e9bf53f295efa1f1507ffe7f4f07fdecb143fa9bcc37e07cc8b0071628ee22b9e817215f6e66463018d9e814caf6293844eb9a4d4251096c33258177586eb2a0f1b4a79d80d4604ab0226545f62b02ff67594afb5460a63c236724f367b03a3a202990286645f67c18ea4208c0827db49ee4f8a88484298e6440c23291801038824944942252421929026090db1af9748422763eceb204908dce1515fae2391447dd9e1c3819d305ca581ba81a7f55fa9347e144bd76a13833fb59168f1b8339eebad1e7766cfeb644be32dca5a82ee2269da6bb589f9fe07e6f97667e6cec5a2cecc232a9a87308d98c349549a58163576c3b29e3b338d2eea3151676616509ed2c4a0259efa2a0fc18e7c603724f6e6e463618e8009d93b77060b52511109ccf627620ed3c086a8b22ceac951ec86694ec4a8b22cd41324fbfa75d3e8ce4ec2a8e875219a29748036b1af979ca9fbd857897dbd3c733023f6f59298dbab0dfb164f6ba7c5a3d573d1ebe2b77eacf6c9c7deb4685061aeb76cb0afdb93cfbe273914498865ab2fd7f3bb0d7674d17bc2c8b8280ccab4b5b3afa7b0a37dbdb58369ba16cfbeb7711cf1bbf688f5d7dae1ffc09cb5f1abdedce36bfce1a9f183238ac6a0be6867b36dfcde06ffc7fdfb1f987bd94f10cb56447c00369a809fa73349ea4aa5a9e464a2522a157c6d5db15aae97c98b74226ed4f6ba209062ec3068ad63b7dbe27b7211ea6e55cb92be320cded98c5dbd5ba24c75d909b1c1142d79da6d9c9a1baf520f4fd2ebefaeccfaf8d8471f4192367ea7878ef157499a5ed50eb3885801c8ee32ba3bcfa40d2c2256f8c1ee3ab3de99be3420cbaedef58015e313246f06033b187e5f5771575e14776347eac84ec4328cfaa9ce5a2a8af5e92bb4a53322754e7667fab91d3fcf1e7e2ac40eaffbedf2d5a9bb3b7bb8e2c5488682a0be2a41ac044fb5f9fc66a32ba6e982b0ec82e5cad1988e66df2ec91ebd8fa475b92b3f8d87eacbf52e08d3e021dd15c6281283280c9c1486c0b805eddb25d1f900e4f8ceb4b36dcc636dbd7967ddd69db17bb9abdd4f88e3cf070f551cb71a2a62a64075e04df8a88e8da8a1a77808ef549bcfeb5875d555d74a048b0e3a1ec234980813e1a1eebdad43073b177dcca1e3500cbb1d8ea0a831112eeabedbe1088a788b1abc5b872328823e5a1d6ab7ffa347f0db637eecdf9bb6e723f670e715d5972b851487123546c47c43a063b8185e18fbe223448ab0114c74d1ebdd997e3d1bfb84375ca2685ffdb97e9074fd243d8aa0eb0fbf51b466c9cd2f516e7eca3c95946ebea9742a515ddaca4d9e65f23093978be526df5abd5c4e537957f953e5519594d8e92a8ff22931190c8bc37c4b8cc33c0d98192f2d0ef32e2c335e646a6a78cdcfd0e0356f5343339bf19a9fa106cdec66dbf0fd37707c3f00760e1c377c7f0d3672e0a07dc5bae21a854e895dbde3a9363a3608988a5d1d0b559b1a1252d8d56f51b5c9f17a8f18b1abdf9d6a43c344986257b741dd0bc0d8d513506db2d7d406c04bec632669eef8451f3dfca47b9fbea4e91f59b31b93a2e3973948d24c0f7532a8e2e0974a07ff0496c0941cfc1a951218ed79322897791bef321f9361a56cb8ccdf40b152dbbbc1e1377fd2e2375fe32647ebc46ffe05478ed66802739367597193474d524e1637791458ca4952995cf5ae1957fd0cd56be572d5cf4caf55d5d9aef39fcc753e7410f4f0739d1f5d246b766f1f3b3d9234d0c7da27e3e29b945cfc12951b13171f4646e50683993c39f9291c4efe8b5c99524e3eebb432a932c9e5a4ffd349cf428add78277d0e57ec86a9f5a34e5c3f4cdb2851aebf5563a3acba9635edb57712f4da9b2e8efa929f8f64cd6e4cbad77e744dd63a4d92b44f95e3a1283ee9c6c537116548928baf12ca90a28fb594d5b8ec4f2997fd4a862a4f2e7b1b35a872570ce3f84b5c8e9f854da512c78f03c654aa6ff7288e244df49c075d93fa92db4952e539ff9548daf612cddd747f9989fbab3839cadcffc624471d5bf3b1f632255efb9a1a0c49c66b5f0a6148ba96393e6a9256f39cc73e92da92db73de9d246b76e7f848d297d44e22b1e7bcca4b64cdee9a8f9d2e499af692acd9ade36335752613bed604923ccf716be20e808fb5a4f968499ae8b437c9d0694ffa1d1d771a93a6d35e26fa0876ee9134fe71939ee7cff1f1a43d127b7e9d7d34f547d6dc446990ccf1fc3a1e49abd13a2f69fcba7b81e923e4d2844358e6840824462106dd19cdaf8b4044a13bab42bc893850f975b1897a03e6449feda9e845881389f8f8f8f8f8f8f8689c6b349d1c2f1cdae1503894825d2073a22903475a487e57ecfbdd3a19af371c4242e4ce6a7e3dc4855edc99caaf874477a6e3d7432377a68bee2ce7c89d6d232dc221f5c6330a87aa8df7b9cc99866490ce344c4286b41194992298037360cef39da37554b866f49d46217ed23da2a2813da14c8904532ba9d1401aa80b9df0f9b1efe9e5c21e49eecc3c22e3ce6efc7a680b7784218f10f48218f5c6c381b96aa303e6c09c11567d7ae788a02744332a6b4a6a30a4924c08f6803d608f8e8a8993e38dccf4703450ccd9fa53612321232321fbb644e32f916ae2cef96e3025384a26ec62794310fa70fbfa8d17d80356f103f4b9b3935f079db8331b7e1d04bab3d2af8351dc19caaf83417726f3eb20903b4bf97550e8ce567e1d64c2f3017dd41bcf07eca9361b2462040af13c0d1492544819f1c664e7c62be3b4abbe1a68e65cce8d578ee5c2a6128e1298ef26f4ddbe9bd02a254395364e35463e46463a2637a20ca9420a77724cb0f6a95ce37263097f698370d2e2ae794635a85669439fc0bc1d6fc7e84b92fb907cb72342eecce5d7bfa13b7bbffe11b9b31c7efdc3ddd90dbffe79716731bffe117d46bea20f8b9cfb82d49b9cfb6ed5c6fc6e3998863ba558a61589e3958932910a87cbf13c1913981b15b154926f3805cced6f74504716d6526027da46d942d518793b464626cb498addc8f12ed50b07b932b152271c0e874b4aa2ca0d8c898c9090502d94e9803e7efbc811ede137ed30ecf6be66a36a53226e1d0d6472cd562fd5cc8c261009ed1b9708d8085ef49ac4a83779c8c25cb0ceac57d775963dd0673909758b7dfd769187eccde8825189dcc56804b46f396454c2e463b4afdbe4aec35fb24d3e9826b57365d7533d40fb7a0abbef34855b610c49d9523b5aaedbbe7e32a49e0c559bec3047a8181ec689144ec0384fba7011d9d75d3797903b1bba335c7aa0f7ddce6f0232f95cf49a9cb8e8f594cde4b3afa76ca99d7d61b6cf7aa651b5b9551beda3e721b506d2494f3317dbbab5a7d78365e4ef9022145dff0e297ef0c1965184e3903efe8e1dc7211de3fc200fa0cd66c321a292469b3d47dbf815c7039d7c226038647ed03d91885a461965e4483e66fd43f2f1ebf047ca9e4f1fbedd39f83fdac7df719cd149e4e83b8ea3bddde5205e8fb5f5bb24cdf3ef4bb6d78369465b95dd9e74b4793da36d039dfb4e475b570a23c8be463b1d6d3d25fb3b4b4631284f432a0d36e234546d3e17129a620a1de8a089267cf8504209bdd3f2b6af97b73b13520edd59e7f73bbdceb485377ce28a9bd010908059d872d6da7bb3f891054fd775397f437cdf0782594461c50e1645711cad980126ada041ad2778511c06a106156352572c7a76da39e0521183dbb65c183b243cf10450cc102441448c5cb4864d723b0f14adb51aa840c522a626878e0e0920e2851123b3d46067adbdf708313539747420e245c763961aecba4e035d0572cec050bdd5c4dc7878ecd423a0810a64200b108db9b3b434573a2e3336840421c24266a9bfef03c195590e295a99a5288ae238be56a616776745922489b4da22b541dbeee062a79568571349b3d624ab2fd74d64cdc6f0f96810a2edb2185a6d8a50d14504a858c2ae18d75c8a67ec8c50fdad0ac05ae407433bedb298c2ee4870adbde268a7ba64992ba131acb5f762b12344865a5f3091510365b7eb8da7a48132ef7ae3f1d95665c4026785165614f1c118636cd2d5963788e200ab97a96c410edde0452f127c1441021012848a307204fc03a022b723fbb56a2b0ed203e40a3bc8ce4e41231d902cd4203cd80e0802540448929b75d7755dced5084819beef0343eb8321520b742d0284202040a17b566411208023a908902a0401b25310084aa821491a49922452dd010218a513c60e081e5550e48644e773ebd93503f1a0c80d4811a41016e10a6291db16457c7054e416a4c88d08bb5e140cc3d08ad616f9015191db17febd60ec7c8071073b3179b95aac952a85324f25a6b24422f52886e0e7e50e5f23d648d77d4218edc55df63e3014474dd6529b4a4c253b42f091c42e21adb52527d0c70edb23866d921877d8bb0ee31afc00bb804aa152381f3518af9052a554b82586209ba05aa95641b26002ae87c8ae2bd68a85841f24c620ac16ab85f3b1039c0f1ff4b45c2dd750165ed8d6f513424580e044d9e3d25a6bed7a3d6104f6d81e23762d954a259393a32f9cc04e603e92a0472b80f665778a1c410865deb508185fe8c28039cc6d69778a1c81081d1476757d3a9d4efe3d9d9904140a85faf30768b442a552a94e1450ff80c562b1506242330072b95cae588a50879b810d524ab850595159390d019656ac68180c065b41c1224390012a43653819f400820c1703287665d1282828282c2da34f4a4a4a4a8b0baef687a8c3c54009176d63b0e365c6cb8ca32b7c70b1ad83f609a023d747336066c07843803ec098c10b606260624a23313462686001860fd5aeb81e47a0214343a6078b8ddd0a2df1828d3b239c0c8a50cad49021c71a5d49d4230735666ad4008d7018d7c3c7ae331a0606066666e68923b0870b68d0a0416386199e18027b0c318281a68686465b8b93019092233534d4d4105181edd4050d3634d0a07725a2021be872d72b9ec002f00a23550829c2c612b2b9b121c71b6dbf7a25737db9403b58d080673514832c6cfc73845b820560d08d8d9b9b2788ae5822086efeb071c3868d1cd876aaeb153e9c70c512437449dcd035dcb841d2f277b8277676aa713e4adc4f112bc42002ea09324619dbbaae0e282bae891d5658615f0c0315ec5a83beb9b9b9a9a18614121cfac68d1b3770e0407d8103078e10479723c78fd04ef5c6bd2009bb5ed40e40c8d8f5408f371ef5ac5c8cf5420185755d6d5418635cae5bebf7de1b0b41b7af69addf7bafb57e5f49c66aed17f4e3bbff425c6b6730f2f0e8a13fb06f0986a0dbd6b449b85dd761ecdda71d1465f70559218cbf0f638c31b86d144a6b31c6da92dd45edbd8e31f61cf8de1702975b94d69e2ba35105aa118f1d1d627a88e101e2a235c9b646ba1d94d6ed99e40341fb3a26736c0980b04970f5a823c288c4e8c890909b13d6c7feb036324a505f8cf05033136396faded8bef7f650874eec822078525b032bc4b504188f921c91b1e35e6c79e4c61a94d5346233e6b18bd695592a55b47413bed694ad3d493a284d6db22693a966bbed89c96b2269a605029df82953fd4499aecc320765e744996af0f372aba4e43b8166891a6bea5af03e2ab562b5ba96672d0ecab453e97859ad58ad96cbf57a99989c9cc060ee259255ead7aa745997a9d57aadccd3cbe49a989c5caed7cbc4ac301886c12ecc82a45488d78bac591392167bd1ace3f3e404067307ff41b3d49f973b15157c6d68038c72516c7df718b36cfd7f6ad24e9420fecf130525164b998971293508d6b27641d9e7bd4e1312e5848cbd764d2169292adac9950eafdc9515bb52431414b27663b1949aadd5a4e99595952913a28a2ac49600dddf4315b2aded6e50d68bd633d5bba5765db08b8b8bcbb595d444ae9f42d25e655c49c149aaacd42e6a51b4762b2b282a8ba1b190b5bb7db0653c5a5f744b8b066b77772f9eb5260d95b1581616969a5b5ccc72e6da8a4794d652ca5c5c585e5ec8da6d9931c30506e625266686ae6f3d992c23d3c9c8c8e06b6564646ae942d274186a215df40c9266be6a09d3cd2069f6c845abcf206b765b18181a3464646ad4989989d933cc309b99349a0a43c6c4681a3464ac4ced64482d43d2569d4f59c3d62b43d24add83fe1992a6dd9ea54d7bb833cc686a481a0db69626ad860dca54ef5b6295949918caf933330e9b9999993931999979b96666aea5318317563d8cd8730fe30d3b61b4f676185bc7f6d4d9abd6cb5d79c4a8c808d1877d566609ae90e48c8e1419d9d52bda151361259429f662574cb29ee362dc6ad3caaeebde768eb7284bb744d65ebb349d4a4ea6f2743a9d4e2c122a8bb1c652d40664571fa7a8b35bc7a06a637a0dc1d8d545a26a53528baa7b05f2765c2032925c9a0c54c51276f5dc45bdc9556031a4cef2eda25ed419caab672256f55c74f2ead9c8e4d533aeda905e3d0f559b95d75cc6ed888a3c3485910e8a9ac83e94d8d5b31195260fd1784954dbaede8d5169bc1d8f89ea5595d79457345dd9f5f313af9e526f442416e63af634586d44af3e8e23c65d97415389a9f44ad24acf71c7db7d15f1db11ffdd62c9c68f37eb7fdc61dfdaf10c84695a3daecc891f4030db74b6782eca05ecc83aecc8bf2449244d7bb74190a4e95289a469cfb90afce35344059317cb659e6c4feb0bc71253e9a72416c61cbaa8a9856333892abbb393ed44853a3b1181dde4c57299277b9a46b0538fbd316d162689164086ee0c3645eb3cf5784976ec66268163b72f760b633756ec36c66e66ec76b2d954a6a3faf299bef412f9fd0fac2713c1c42dc08e40d24983ff03f3ec63940533768b6171ca25a612fc87ddb0ccdcb1303e59308a61612af55595fc615b94b564790949437909593aa5b19bb9536461ae973c2dad69f4e3632362559a4327d3e8f473f23911e132a2c2c8c834328db49176f34c41d01c3a0da5484262ce7494c5d6670a5382a7beb4866e403eac6ff1b4784471ed4dcb880a53c5be5e0272447d29dad72b093a0edb123157cbdbacd5bb1f65d53e9ae6bd97f42c477d4a13837a1e32b12f779d294deb4c6350b7a5c9cbd5f2f0f72c144cd9290165e8ce482f0275b9229550cbcf4d9236ba69bdb71b7f69b3fe07060ba0bba8e46a5aab7a9e646a480000000000d316003020140a0985e3799c6561720f14000e638a5a544c589408045990a2288ac118a48c32c80002002286d4cc904600fb3847b4294b19e6bb11c3915d89336877244427c008176c207546f0a3fa7f7f000d1fe93a579733167e455d4be367a25d52db5928873d37be7ab0483b71dae1ff25fd407f7b37d1e046a35a7173419e6d26e720c999ed159cb100263ffe332c53d127486b427ed166fce2b3e59150e2f9a06e126a9b7a91e4ca5040d29aad095d8c0b1a5fe4e7bb6a2cd34505d801c12e23d6a0ffda63aea645ea1d2ed42cc0ec1ef269dab03908eaec0d8661e78b415e51f9406a36175d1168872e3f0595e70c5f3bc908b98efcf3f12bf2ff49012a493578813438f573d91575abcfb573c174c0ee7620cb7428742a5517fbb62aff1db070316571cd53967ccd2f1b23c92c0b465081a2399393dd0d1574223009a6d0c44462ce25be2c986d10a0947a72630f9e464d39449bc078caef0bde81490ab92a887bf7dfc71f5f31ce4b8046af308f78b132ff3ce2063d77c5b814db20876ce2ff5b1c52f70cd17031b386522e7a2fc962b7ec2235448e79422fa44844d4ffeda66757d72a328ef39f9c94a0a745dc7f3c86637115060340168a53ea659ec872e57989cdd68d4ef5ff336addc8aaeb53522a5f28ebbf3d551aa022b3c07e378a5b05f8bc920a7bfb8e5bf922d61f4159accea187e5f1dd2f318f51a6f99bcc9a56130cab2f066032b18b46b92f8e74a84f2682314acf53aa1b3cf8e43b52ccddf37184a0b3166b01526452291a6632973e480cf7c5616ce9cd1bc7b1be9009de41ccc56f7d8e87ed315707dcd98c008fc242aa122bdb39e8729165af87e21012e2732ad0253330351bf76f8c0a72c323501bc9d79d921c170c2ef353facbadfff2c331cba3530223ff668250daf45fbb65ad52a6432d1d5bf2388509f4d4624e5a89df88b83d13281171f1ac210cc6a310e70f4011d16fdf10ae070941cedc625c25099a91a3f4351442af56976853868008a71023c488266aef4bccbea72ded1e8694a609614c172593aee99dee07616601ea8cd3c4b887022752d8cc623f75cc6a0f27b02dbac4242745e4243bb135ce42b317d0efa8a53bdbf7978f84b822df3e3a4c06f84e3de7cb92d1f5a374725110eefcb2d8f338728c9bd30ca483f0e818c4049cb549c8ba249628df3b4913e40a2c025fe1b6e10940d85ae6ae32782d9ed693c459ae97da895fcc1bf896f93f76c7b83ee4592126c01b22aa1022c7b891be9bf68f496741bd4ad54a775b05dddc15b7e6ce4824ade3e8414de3fe62675c3fefc4e83364d238a7de7c9d23cb66a1fbf181cffae4fb68f15750687d1a52a745b49d95818da27e8e86f06f13deea8f26a007a169ae098c403a8616a01938588c3219d5d0b4d0072dff0b88df4b6dfcf8e6e497b496995989c1f22bf6d43d4b4e43d13c2586537f3f2c0acecee04e93783195d5cf7b1fc0c8f322ea3366702b8bed7379a76f57c3628773f310919558ef766c7068394f3c5c0981350b415393f3a14545d5e221495ca7e1b4fa0357952064b3299b99af94217be7c583856a448bc4fa3093313940723401473b9b13226238279c11323f1681e8b0c997375c0c4edd21c58e11fdabb25b62ad281856afa736af23a11690014fbb23610712c46ffff68919cb3cdd00f071e71a8006118912c0894bc4ecde306dd1c527ca80cea2ecd78c0d0c3d0a56c43d6fc6950bcb6bdce93bea75d28fbb7edecdbc57eb10e03720f1447fea6a25553fbc738563013d7d6f723b8a7fb75faab847671086cf684a3415c7f0588d418ea2e9d8cdaeebf4b32a85bb08d0ee2ff9dff5eed400ab3950f87e8d614f662a4f922c36f4432ac6cd513c2f295e346ae6e2d7088d5d2068dc45d336782580e47c5af637b958accd854be8c9076a95db2740634ef1728228e9aaf32bd283187550804a3332b821cda83243260c27d52de87b57337ee11e4d38aaf0e8135ea5d4f4d7c65900117c4ac35649b5e4c45f2c93a1b7d4051fa15f3047b8e48565dd8f7af33a2aa2bdf4c63d7560715c643a2c0bc7fc028fc9a1d4c3e38329f009c3b95949e9fa77f85ebd4f08cd5f70c1a2faffbf8ab2293843cd17eaf503cfe08bee81df51794bb329e3930293c30eeaa4888b4a939e1aa1217211627dd50c50ceaf6e390051c933950bfb6d9193ecdc766da7934dba89b7083a04b77a1e7a594740fdb071d11b79c246fbec465f6500bd74fe1cc6a3afe35676d265679479dd72c586dd45903674ac085bd8fd0a8180d4fdaf68ecb70f223b93e49f74a056ca09a370cd27d60c455ed28d61a03272831c5597a16d5508201d0c174814572ac130fa29722faa3171d92ac46733b022e3f5fb0761114d5b3e860763c8befa4ffeb599d66040d11a26f68f8f188fa2936911a624145a1b13b7db3893a93bae30bd8a6e4b4c1cf85b1d2ae456f3ff1a4901648a5589f9c0d50b3c7aec03a331690abda4e59c4393163d8796506abc015facfe8995bce706b17eb7dc014c512e6a4ae7b106d1fd8cea34d4b4e874de2aca208a22bed7864257da2408793ca026c48a3db18c35c9eef66f2cd2c1b1be9ece9d4970b1d1bd93b7771d403a807e6faaebc7e7463f72197629114faa998e64ba8a15e2748cfd5b4d1645e16082c29dce8171e7b680cfe8840ff9444ca633610574c0be3edea7736145cfed73b64a8006f8689c72c1979c88f91e023148a1ff4fe0e143b4a517c823b7d2df8c5eac51e6434967a1f57a24868e1785394fe4f73458772a83781cf883ba805de2d788215b771907f1536cd1e2c58b52bc58f1a2162d5244f1628a1653b4c8a28b142b5a9ce2c58a16f155330865f0307b4d70d2ea3db7a3419fe60517dcef8acc93c245bb4d7c0e4e85cd6f598a2852468132251452a048418a81f221ff95a11d7ebc4dd70e33f21e3f91d4fd026727c22b2c6425878106e687bf28960c34febcb3b3d786bac3dfe1c20102138f48c7244da5de0492d9f8cad914cb7ef91957e0d753ee4a637931c79c0402a85451c7cf7d356893b10c44812f8992cb263088db55068b8021280fcad78011800eb54722e89777bbfcdda8b86828abb6ec7b045c643415aa0823effd7a7a5999023ca18f8a42479c5642945cdb6730e270c9bcdd7c45b80cafd5bac2ab7e3d53c0745cb76f9990f553b0464701ed042c425c76b170c66dc0e7adbd4beb964e2969efc4da8f4a67a9d64bd15df58d51e950e1c9c9dc0367776ee0b917f403ec93828b557a9046bc4e0b671305b555525d0a67f90477c964ab4c1b81df6448a87a6f0faa3eaac8c8878a0877f92997f13a456dd8771dfa191d1a32bc0b02019bc6414fa375ba1a74bfb2c97c1739ab986ebb504cfa0b48184abb233e3b28c9e424f99ac461a92bea73b3c97344ba610adc126bcd8490d435e273ac37365205d077ef55747a15094eb6b5989474cda5b051c25d184dd7110709bcdf5690e280858ea5c9a2d42d7ea8d3f12e3956f1e022e6939723205c730636b929d904903079bb0e958c9c885d30a37c18550aeb6ae243d5c9e6a1d01d55186e09126b0509f26fd27870d9b57360b712ee5224b11d9427af46949ba6cd653d72d60290c39171fa8244fb9f4af925d9662c60f297b8e1897d4a68933d01a7a3728fc0f5a91989a137d3848c8c3b2b5784fd30c7af4b1d2031dd6ee315800e9b92cd8df2ea9fb8d7b51cc5e8fe1a2901b284630e156c09a24962d03fb2ed50b8ee073b6d9d9e8ca50001de808759ea9e88e98fdfcc4fdfef9c8aeb7ea000d0cf946848404978e6bb6e7e55a5806248a9b81a4dd7450998bc40dceb29296846ca4ab9232a3920c5eb6b30301e8a716630a33c989ca5b46eed1153b09911c8a7ff75d4756505e544b365c6ba2684e13af6e3ed9a5d94c6cb98848729a1d15cb9027cf1201134c98e0e5dc63a310a3e0ce5449bad0d16c5aea1f9935c6b9a593744743042a432bf92158796c25b43771b4e056184b92eaa781414289a3304f1b812f823132b0a8d246db5a9a029636310ee7693277994b3bd6cd366e9dac9b35fac3eb29611913e0d0d9503a38e31357653ebd374011067ae492aced25e799a28d0ebe5328dedf588c9740d35f05f1f68a298f23482a5d88ae8780b4d33851e96c534a0e92bf26ebdd03b05ff5aeca0a01003690eb2325887c2781a93a0d0ac4c74de57587de0630f6d7506e40ac71d60e6a663d945791342ab8a697fa39a086b339251a328d171ee07fc7a0498829d0331114d68e541dc49727fe83acaa20c8b58cffe7d9547657e087c47c09954156667069b2b624dbc97377a050989a994c178fc9a59ae501d1e1e2b95ce9cbc11451460608eda71ba04913705b1f68e1a2148a888fd3d559a68a1214d97c94029cc91aee8d884a9180f04b08a066b5a58966720090ab682f11841b6027488d6bc9bba1582ff8c55e198a049ff1fe72dd4d4ce29658efd8029c43b4710960154bbe8a3ec85f7e6df3e436675840921c185af00cc50ca15755a75885a82004a55c47d8989b759eeff814cc311488f1a60820a0d97d32e6d50250f97cd24c0b5f952a8a3629a1dfba9311319edbfa15bb80d80f24b21982e0a6ff00125c7f68dcb5fb5ba75389a67044c70825ced2c22c1377999d453324795d1c485b72d7dcd3b6057ea37fc0ebb9684a4ac2179681e481884fe1ea635df17812857bd50dbf6977566feec755b0240f242c89b305de1bc079e8dfea1280939d10559864bf222489a5269583441e446775087d431d46bb39ac35e9c56213ea52f6c548804bfa1dd3655d88802f2289a224da85f39fa77338cb0fc6a0e09523a182edd5247b1580881aa14db27e4664e91748cec938452c172a1bd11846962b47cc353a43b30ce11fbd9cd38aae9c1c498369bb5e958adaa213ad9021c452375a09dc85690d3133aed7495a0b90641a1c51a27958f4e4731ad9d09656eca63695f3f7f3b71a75e7bcdf456268747e94c992b807fcd4f1aa3189ab56ffd76b33a5556c31d2d89f5780c2b00aa08df661ac7cb25072dabd6bfcd9cabe434dff87017009316a7e463e1d805608694dff55655208c984307719303850e006e0987027dd7c0481c8115be6d1931e1488d6f8b3931aaedbd2df26d611bda72a3440084890163d154a1da1cf02c9a552ec11b86ca8b42a6e1a7447b12dd169f84911b26fcb09a025f1937cc973471247fdba65f00c7bb4fb6f1d9191da9e49d19a4757ef80445f0319d1a2b424725f9d2df4e5a72fc915b005649b24b650e0233b93c01cdfc2e167c6f825ff987eb9f1f3b119e15611226a51b51b28d11c44098f8e0a1951c9653e06d53d2c88248280f1007d81c6bb0271f2f1286666d329802b505ecc5789fca4f2898de1dacafd4561ddf93573f1bfa7682ea4f93619452a2b908c3b994bdfda6e9279fc8fb8b5c304a7ea8eafdcb9ea6004a299fe09f5f01f5a1b0ed986f0aa107844366d9dfa35fdf4cccec48899745f26c2c33f775e72a609036902c42e74cc41acbbbaa89b9c091f2c6ffeef833fe9508d521091f1eff6c0ee0c6c2f8c9f14c81f5f8a56a554516790d646442f3982eca46906a662e882970e6eaec8a1e299e02647325f4a1a70c02f68bbb07b67362ed2c80d26284cd8a9140dfbb88d723e093aa580205fa35d546d1f51011b1d8e3f2d4ac905ceec59e0c5e9f709a6a280c5e01b2b4780dbe69b71a059bb8ae1e53f8848e089704ed833df7c1b82db2fc94f5b9b8fec7bcb45e9fafeeba5fac47330d035b5a00aa091e4790642bc84bd74d98de619fc9540a16c88c36c349d23c01191437dae431fbb3630ac71f61f35e494e4a34bcf509a37cd5503f65442cfa99df9f1c74a04acfe4315a52f77e25c1ebd6ff7048891bd72e8e96284ab68c1f72355526b6bb16629b5b62082a28a50c10ed4cc8b41e1c621fc2850e734aa07433702beb5cea4a3a87d6a0ca36a070d0e10c59f3e382feed18e50438c2225663196481d27a5e1cdaaf5eabd4296319a7a4dddd0175f098782d33ff89490cc3ce48e6948ab5d5c75f4770b648d4d4eb70a31803732897854c738bbee40c7dbc6c1c65af207a2adc27e63e6a7630825df7de756c7e6e0c657b48550fd41ab07ec0a7bbd37a2986f5a7fca103228d71dc0dd898676dd205c29a5e6e58e4ebac94f002b346c802852c92e49f82fc9e363ab5b3bc3257ae23442b2fd0eab7cd32a7f195fcc7c478d6333d35fc3674ecd88c4818386bab7c624fd086160e994de3c15a9403f1611807313aec549ff2ef83dca12c4bc10328aa9f1a354d4c99844d6614df4106afead7b863a7af4ecae359f6261b1c5a58c47aef133e00f7b4685e896e11f713506699bd19c6a701df7e545048f52916410184e04c48a8d231ebf2c44c50c9160de01af1bd19d6aa84f29d69c7ec0b9fd15cfcb90a35ef76f6ad2fa55e46f937b0311c80fb6d1906a57f108a057c4bd2257b4a07059f2e52ac50ace47ce7702c99e791f83ab4507a164d532532557b41664e2866d8d0e182f4178f2782595d29b91d642448077a3c0b33cfabf2cd4d34323aba30a2f2690d25004d359a2187e53abc86f7243ee12177a9871d585fdb12a77b19fa39ed1e7d2ae6110da83f2ba415e5fdb1148280a99880859405e99543eafb08eda96fa3ab521f690ed8a4b9f662c62c52d16289b69e26354f671de5964542d2be06c31a35ac03d5317ad29e8d40b3b64dc5c8b6c0b4533277407aeaf814655f3006b9db86b33f87ec790a7f024283f158dddb8fc6176bf147c3fc87242e3a2fa93621796ddad3fda79544a559fa4d839cc836fda11d26e749e3054395faad93d12ac8481e4f89903414072d83d9f6f9a4d87d0729c669deaa004b1823b05f42963bd5025f8636d346162a8fd79e175f9bd3e8fdf6b9fca363b05c182bbd87a9996831d544248f3fa678e4477ac1bf01d2df8e4c80999136f1bba660ad29706671e8583322c150b68f87e4bd1bfc715d5d9ceb6229b204e1999b2bcef232c4d9595cec5fc023c8925a74cbc9b6695f30f8a3ebd3c5afbb2c2f2ef86c20bbd50d998794ba38eaa0599b251ce61bc579fb8f20aca9bb6ecbe096afdcb2ba1f657f75fbfb1267aca8f9de2fd6570471b0d5cd5be928d95fb2b154107c51b5e02468a763708080055117e1bba65e285988b01db10da89b0f11ca4a9df61b73a53b1838abc14b118720e30cda342dbc9cc9c1ed2baafff5a1956e599fd9cc071135c84ad1b8134b06da978a1565f0dc214f9dc25b2cbd0a7f832c789b30a3de0bebb348641b317ada0af5e15b8b2db6accd7b45c57c466a82445a72ecfab87fb7fbb0eb9bb7c943d17542413ffc041fc358179b9073453b23716c19e09811a00f8177cba81a49baad6a198b9e41e6600dd10b65d5d5e56573bbc992c0954716945c91b47918350c46d1aa3675642e665ed917f64054f4353e16d4ca02a865c9c989baad9615363e1ef6d0b6339ddab55f9e116a8c2d59e793a3898fac22995faba046fbc4b58a65b34b0270bfadecf8721ae9c796d1d1c2afe46e115328fff7377dddb67a880cc3dac2e7496f04f80d916dc704d42fecea7e22178aa7fe04dd01a6b8c437e3cb7022429cc0ca8689c7ce588a43b7114882f21d1559227208dc67ccd3c630045821ccbe446da38bec12d05376c1981a0bb3482bab3ee485278e994503508d2dc4cdda80625b33e2ea284033d361d5509fcf1ed64f22934c615049af63f3c9febe601cf21069689f6fdc3405e974b15d8116a51dc30817bd47055b5988614c26f6d11e4c6635a3d6d69f704992e304f14c90feba6f2598d902a9afabac9a00cb05e22cb02fe0cf26a5ff7290c5aa73c0a54fc07697a17b053934857806091dc227d44aad940ea50699b00e3eb214e2b423c97689d63b1e66cb16c40310b63834cf72598bfe36aebca683a7340067349fa6a8b1bb7d397755653cf86f9d2c36a04fd361068039e886b070ad8509b193ef8ae8847412be536c1015214218c43a2181cfee88c9c869e9e7b3f31967d5cefafd7e9ab3f1ce1bf2705937c9d46002897320c8d89f7ee8613629f33a42b7ccc792377e58617647adcc679af3f5997ac49b9ab9aba7450a3a3f7a22f44fb781db5e6d8d6aea0ede4ce2ccd2e8c44e3a56b87e07803be83c0690ce65d8d39b283213c257edcfb13e2f752b722abcdeebfd5c8ee3a4859cbf0643df3058d2f879206ff175c4424a0c54d22076f71e1254f99ebee318fc83ffa3600295031db01f65c6baf3ba009c4ab0118e062804cd03b5a47c6740b24e0129bb7a27ed7992e6e9f421f49a965fa79717d254cfaa21636516dbac47406a3b5ac8e2aed5a88f5e174bb60146a2b402f1a5e3f9c24cd2ac07bc6a27dbb26d9d4af9f4ce4b8a8e9d3cffe7806a1653064ba366dddeafab8b644e4c693f7dbadc6da959e255ef3325591d8f54d5f728903f1199649062e9f4c7a2a170d0824c822e80ca8116edc18a1068494ff538ed595e72052e1dd74621238462296a7dadbbcc17955ab947f979388bb5cfcaaa59f8e5a88ae61909ea88d112802f19d6d92fc17b887c05dcc24cdaa8abdcc003bac1388be7ad407f6bb62110e145a9a83a71f4d486e8a8fe8ca03a22e39ab28b974e91a4158d42bfd9af76a70bea900c81e37eeb206d3f65d6ff8aecf0743c446aa6ffb515e7a34ffb2a19420206841546ede95e0df80e20d9aea88084d3a646208ecfb75e66749a1b2811599c2f32736b0fdc94f449bd134a5d88c556c0ed7221bb7e0b3e2ce87d4f245c928a8740b5cfa4bf4ef078a51cdc815f245ee99263e3c6e618bda5559a08406eb3d3d9183ab067fa07bbe12682e476cabc8adca7d3f9d2b477ed74cb178354641c8113911a9fb3d43de64c19fdbc1a80ea3cff74138482b6a0c7b460494a585eacfa01071c976bae0186b155690db285f5bc94224e31ae7d19441a96cf6e33a2cf84a9ba8664b219077ccc19bef7e7a21fd9b3d40c4d59b22cc8264e233b168bb6a41af7776d868b6cb46d8f0102671ca2cd5ae854699a6293b852847f6d94370aeba4dc9b3dc3f3d541ba4a3062ac363d831daf2bb99d8c6f47721fe1ad568e1c4bb69d5c1e83c3e00656b681dc04b699d22faa54c16d4927e9784be6acdcb7f9cec3c8a1ba5a68a9e617cebbd30e5781ccf862db295d4fc641b69eebc6f847cb9c26228ebc91fa2fa01155e8369f15f0e4daa9a7025645592d64dc02c764a60a7f7168d501b2893a58d71fa203275611a911aba016ab998bcc5e1670837bf6ab7ad8e77f5ffce6ad4c368a943c6773351adc143d448d18cfa8ba88bbb5fb3a277affb1b3995cab9a4d5c50e83e6666a4228250c7a971ca318ec42fb95aa01ab7fab96706fd1d72468b8d72037b77d426d50109f093e6ff69a832a7a7efe9471af9cb7e22a799d89ac0e19cb0a05b8ba8ea2892aceff52571ea2e201bdaeb3474799814e959df59615c4f8ad0b09ab3412d8b4268c50bff911a495b73f892f7431a5c0e5f0d6158ac532bd6279abb0d95d5c1f06ec52c3ff84f83959dfa10b265a3392cd69f97b0838d2b14fa20b982bc4fe46fbe0f1420c93be0e43a23500a1f3432309ed14946e15d814dc981ed91e135f84c3900c3ee914ddfb5f6b840e7c9f0770a69bc059c749006f7e1e38e0681887dac876ab939b7fab4757ee1eeb140f9a4220379aa254531fdc3e4f21ff54323deb35cf31d8832241240b137384b4d78cdf8261f622f259b0d7af14d8abd126778c6a183b4e06e71218e0b330ca7017fe570a1bee52f914cd142b12b907d0105c934b8b8b9e422d8df52cc45ed0c7301a327f45fb501e33c0a03da859d305b0de4060ffcd82e2598752124d5d431b068db3b1906c50f1a41f407be6e161b8cd21cd47cf3a16dd189a83408ff7406ca609daecb4fed3df3e2db74d16c50df9632307aeb6210b4cf130f82ff63b584cf5f83125718a5f94e2d5db8d0484fb0bf9ea10683cf2a7dba86011b17430bade688736393d49130c2fb6f300d6101b241d8a7c46441ab7a2fa993037d4e2acb79fb574559a91cffa1d7943961a834d80ffcdf246bdc293bc73ee86222a2d749ec15d73b0e10de963d3fadde474cf450296e4fed16d60b2ffd2eec90178072173bc307eb55a256947188a0c49117d5e56163fd84343c75e2c5146a34520d64a8aacaeb1327d75af4aff47c30c1fd9016203cf992951f7c0ca63ac257dc8feca20dc70a5f565233fd62d0b7731ef0638cda256961d921c7552beeb598596bfe622ed4aa2e3566d5d9d09c41f3f7d902288ad88b121c5aa5cdfba142f4ecdbbc6568bb554fccd0c3222fcd90ac5429480537c77e54520585e3c043a4d05e3cf8524168c4b72d59a53ce69e88389549d68d0b2efafe6f4182be0afb1eb9743f06229a5b265ebee5c8aa7d2746e8c11b9d278a27047d7e7f6db368b74e6a92f24d0e1619d01f7fb837a3b117ead832d7af7f22775942056e99c5a855692a1d2e8596c534136d26526229ee1350fa988c87363c88cf619f78ab14bf9f02435a952d43eeb3560a11f403151dc46623abb0fa6b2d20eed9a24c698c6fd85d6d62756ab4f1923855daf96afa9ce61d4a90b8420e22a92efbe59742f01c0d82096f05edd9697ef82900acac99a0ae9a2409f7c460d4811499ecd98077344bc66115975b1bfb02c4488ee7185d3320d4069e5063bba017a89046e265ab74abc8bebf0a3bd5ccb14e159f04aff9d81acd32c438e30fe8fbea04b5e2027db497e66f75823406a3918fce32cfb03ee122012a6611d8865728aa635207ee161bb13eeb2a810bbc7e9b9ca83c1fb772fea1471d8d575b24cabfa8df79ba8ae60dfd41fd05e4ba36c8bc2b52ff1508b08281b0de383769e5603df2bf117cba463d092c67ddb2edb165e00d9fd34de61c1e7b82144feefb1b8cfb622c89d3cdda5d862e462ab1827cf5dcb155c8f9f243dd4d6befe684e191c9b784b49884e17a3b9c4dafb0c456714984b916883c81c893dbef9cbb6c44586f227eec8106ea8230c6ce52588ada552374a9eb5c8d400683de49a47a30972148870b24fd6fd6cbaff0f464e8caf2b3904d327b6ff2084fc1b39f2251d533eb86f583358d55dca176e97231e38b139de63638cfdff550faca74650d553a72b6a0b6e3fa4f8eb288e5922077e69fd36c30d3b9c00f3ed7240e682ef84d914ec43964fa5c136c8e9d63fb714b10033236c193ca4065183841f1afe41435d500a544a6fd953cd89498ec806f6f2c3c1d26cd7182871662da20189f4f8de729856857e27c3c100527cce3fd647202c749b7b368c47169ffcc863acd7b139e3f6da9bdfccc42c682b519f94ad7336255508a36b76505ced0c06403a968b82c63af4c1febd0f9684072d823e9ea8268d92e8b4234906653e1b248e06934134e880929ac223a46c13b1b6c19eca47f01f6a245acfd85206a17f730a23416ba6c923ecdd86088fc3927438e3cd9c81a17d25d92496c889232cff728d1a9d4d1cbdf099e39d5bc9da5cc34e3f2c66179cefc988fd5c7160556cc6683de9f7830a9be7ab63a548e5d97e175668147e66028af4e4216c5e05e653f47674dc111d9c5319dda52d399e467e368a069253a41c0a36f89b916a19f8306f5a833357c06217af248f7caa6fec4891cf06b1a920dcf806bd543d31016d569ddc9497c826da8fd880cb25dfd99eb3db4d387525f7e0488dc64afec2150798fdf2dc9a96bbfdd83cc7c287072ddb875c42bae7a316f1044c15caa97f8768429dc4f5fda20ea46f4bd38c5b17ba788ca53b04cce88596dfd76364cbeae7a6d653d21a2d49414cc569f450c8ad9472015a73b1350c2abd31ede08743dc5291651c7ea472887814606e96be6f90adc3b69b49bb4dc4dc41435b746b1556230299f0c0ef0f83aac66375005f30cf79301bc050bd1354760737ceaeba019aaddb61cdf2c6df4f920a3c184a32e66400d8b418c1cc6a6ee4360ae95beb7d05eb189e2ff230fef882e414b916ef0210fef145d004f21ddf0e1f350fc5f33c059b0105d7304361a5c40fe844686ecbddf2a42dd325b6ccd304151fe64d864876db2174b3f70526c3d032bf9e6292b09678f7bacd776cf7c292265b04df87874ba08adec0d66f58cbf341c64c055a1274d0e728b89639dd4533c4bcc17280be5b45ea9a82f7f0a3602ff2e0f6fef0b782d0d51133f4423a191de70c6f4db168af99fe640c1769bfc9681bc58fe58e3cc6f06193552bf0705210f5ac1a2df88e29780664079001b1305286f9218291850563904cf9338fe9981a86fc029c8572e02c4dfc3ba90d5f5839b52c69b0c6c3b932dc87d7c43e7beb0f5f49f6f1a67101a1445ded8f417cfd30547f5146a33aa174b4a8fe6b1bb0f642bc3fa7a0d63eedf21e99608a666e282289709e87109a3daf078db687e783519afe53787a066b041f923bf7b9af11bb87dd688ff6cf2b2c09a2b4bebf26cb8cd8c410d2d7c612c18cda6b45f260c1bc2bf5521f9c4ded68e9f9e61f51b293750df0658ea805ed9138c2e192fd8fb7488890919ed006e55ad66847715abc813e8773168420a76f502886dae21ecd3cfca77a8870b4e784aef632d2d13c9ddc10ba5a36c818d9713fe6289a53e492f5ff424b449010a600dac41d0c5faa8df1fbd662f4ebe013e7c8937a88a192b405f1d3c6f9c6d6f7eefcfff42f9868c6cf9cea53925620830379eccc761e40eab192f6af854069f2e8fc3a334a41dbce4fd26ad26db932a3f5103cd39bf6e441a9d36aa847c6614fb8792facb7cd664a26af8e9ebae1d2fd0450790104801d169e2041b78eb26dffdccd93799903ef68470c369ccae354c96ebc83ee081d59678b73f9db4a3847402571cb53db0307ec519a701e1f7a8a362abaabce82ea725470eb79886193f0d9558d34fe11f54445f86cfe79d66cfd6e6599acccdbcd19ef7b6c4e640f13f1fab0057b10f20fb04edf6a7287efea949eee613dc50fd7c961d858c5d82fba68447f3e6cdc73ff3220a0d11c68bd836738920116abc15ba1863b354ba6b06deeabbb4765a1de515182a19da7538d0246023e4d0524e3cbda12e86b26e798d0d8a439ad81c7c47ba5fe3b043cd84a836461399d70ce8b211bb589eeb27be5a18c406734dbdfa9c3e7fb4a8490fba681273b2cbe895b0b7e62cd2dfcccce06b6d2510198cbaea4d4cb303b3d9a4a86777b29be841ee00b4edc89ba90d28060405f38ef8a65bb0eba136b435e65c4d304c6005636f733f509ea4e29f7f5c0781115938a3590e124a8c8aa751c50018aeeb147c86dce5da01afef6a5c211f046701aad00a05028152e4cc30f392e87ceb141f9667d440b619642cc264b6e756641d5602830ac37d24731254e5dbb4e71e164388ec4f0f9da772854f799016e952b385ad670c3934e220d8d2877dfe8dc151ded3ccdf010536c2e916dbe0edea044a341c33091d765df3f277e3eef0064b5035f2e822abb5bba90704b82c30e961355e6e362ff584550b77564ed0e674c401b8ab6d655f2233befd67d3382692990b9811cb6499bbad27a800f745b43821142490e3bc43be087e906a720329757527f2b1285b5329c17546848db96c17d52e110a8d5493e6d46aadf0479c0253159ee541281571a98badc28ec4edb02adbe254e07b4a554283884ca5d427133ecfc1e97227d1b4233d9aef839c1020fa4b9896217ab25156d88064c81ca9450fd8918ca7ef425fb07401dadefdb37a5ca28f786dc92e764b6d09fb9f01568cc1d7771f37587f76c2440612d21536113df6a0e8cff6e2276b0143849163c13c368864a3e0978b7379d3d792bd333dd708b685d752335d11c186652f97fee5b18768cc82d9892b4c386b0547d2a2b6036be925ec7d855f223cc8b9db3b73cbbc2f61d8acda72ebca554f59a4f437df8e12b673827a61595c06c896153016e933cd1288d02879d65bb80db2993d6bfaf902bdc08c030c4a45bd76ffa24fb39a918161c728b89ebbfca4dde9dcc7daf12f700df6c103da516bccfde0160409e145ce6a4835a5b6ae6af691cf5e8a82805027a2469e9e045425bd51fd55ac661fb5c4d0d51d843a9586aae394be6c2c542ac60b5d781379ca281698d2610151429f514a08f0ef02afa1afd8458d5b7482ee244da965472e94970176440d2bbe9985e269dbe9ea732920f6d152a99bcbcffab6b4f034ed60c092a14afdeaf38e5c711296ac874de5c56f57cfe162954fbb39746bb79cd127e925facae9272edffa5cdd2a8d601fdb1b7b990da4e53422062bfe7c13fa61a6ba3c27859cc970304bd9f89c825ef3b42e6ffe1c569d90e5df3e4ae5d3040b664a7916a5f555a81bc2c7cc3c04270c02572f76eccf1845e5da0e9306595c9a5b4ea0b43e73d4f6cd3574ee7acb9108e9dc5a26c7bdfe12e76537303702a7e14bc4fb2c0bc77e31cf0febf9b8361778da2257d8c27ba8cbcec43e6a944bcb39637b0309666abc3c9cf3f141a9d0da52fd9ef6e18042a95014005946f6488b2c62175e045d00cfd544722a6cf808248e24573b48355b5dc1873c48ab8e4261ad32d00c10b49a6ff081ca3ba1498e5f66583e0fdcb59c64addedc52f9bfec6d00f9441fd2653c44e63383231a745550184bc6414cd6dd92f14cebb4386f290081859a203341f37ae7ef75a9b49f4b7cc904aaa1c688ee83b4033cd95edd34a47932cb2f77f7bd8d3d3a93b8280836fb9258c4529c19cdb2dcd1b7809fbd9adea37603ef48bb273b1c270155956dcee6519f54d8f9b508af200328e86b903526035e3864c038451db6d921433136a6fd800608fb81bfa83a30223009e6f1522b7ebfcb2f6e76594c50cc5d2a8a114924758477406c7dcc65b3c44967a5f5b80d846fde5a922cbea1ca2fd7e6ffc4a1f1e1d5c6fc8d0c997f562bade633072d4f4fa4e4643aaa081c164ecd3b130d9dc4d190399c1ab2fd5643c61cc66864f4cbc46f3a329ac9c16ca8cc6443fdbd1f3bd5a8002ff44d87b086d07a6b68b70c073f98da50c3713304376474dda21b49166ac3280540b72ade2f8a7449cbce2b54b60d956430abf9ab501560f9333b53ef6f28794c5a56ac42da8b7f59b5e72df0bffadc493dd1d72fabbff8c0301db623a8c94d48867188b87ed13ade7e1d7ba885e3729da38ba42261186bb780ba95647f59965b20a169eeedc5b6a2c0d818581b3a1404ec8e7199d4ee62f6b19d1dfc9c9e8b5befe0c85b60bf6bc6de1f0bf794bf57851d4a185901ee0f96d1c1bf868e0a98fb40fa74ec20447b5400d2fbf33482ecd681606a4cd5320d5a302f1fa8f09e4870934bfe61ed893d81392035c848296e19a53d21024ae41161d0f3d238d7856a4b20a46383cbf18d8e9aa87806db7ddd083862402dbdf07b3cf09544ef39e00f1d4c3cc5408769e6906a9c6c264d3412eff94b2ee7f5791d359e03e3d673a4b49d6756455b40a5301e552ef7f90d9926a32f71f014aa6579642de1d64136abc6b5d4819bf98c869537eee90deb65da9a013ba16588bc629e6c8cc25ed83cb4631e52e34e30a9ceafc41caa8813fd167ba8b85b3bc7cb3af40b337997f1f4895e1cc4d64a657302365e59c90ee5a22ccca7930151d1082c10760ffe9ca98fc6db4c2566c95d19f68068bcba17c780eaba5b41842de69cb0d119cfb2366e4c6ee62b86946f0d3e9b201443a65084de1aade63271aee36da013b7467c490cd425d246141518c15cdd88fa20bb50ed8656483d556830504a342200175950704d5981435935a43e2cf3e98cd78791bc0eff1a21f57cafcd82695f4216aa710f8cbde017ac23d352e302624add1b3470bb6b479e7ad2350be3abbfbcb34a67db92c91cdf2ad5f0f5e2c6a2fb6f9366d63aee646b3ca2a537a98523b1a9179b3bd336e25f197f6a5977ce536ba3d261c92e9b469e083520457c3be74cd0540860b1776e6c2715e42e3bbcae84f6d382e5e2c16fac605fd4b869b3b95db2901c450f9c9e47c503ff0dd51e994d1b4e8614cb1c937d52fd602d908f434cc11f211aef7de4b644bb95dea7a0c80371e5a8cad63b5bd80d373588c2aed131d81ddf11eee2e36ad0563472aea2535a72c30037e6eb78fe9fa968215d4a116406f042ee6c9091b53f4ccbd7e697ec049a410326f5d2059a2e7bf28b1232784266b4274f31e0aa80206d6ec2255d3b6c52a2c87e441c5303ceca27ae814863bf123fa5861a9833afafa014b702f8490feab9f91e490af0ad4ca6550532700411524e1826b3a0aefd03618c417114c8e07500de05cc400d0aadd1acb2d58caf122641b6d2f6e5463973a6f7a0003b703b05afbc727e3a036ff06b9d3debbec30fd3d9c17f26c96b9b368a101d57b63d4f2b657a9f5c95167294f4bcb9bf7bbaa6e6b1e79ccdd985eb8bb54d8d648d77a4dd34be43781d6c5d952ae6d540cc53cb6f6314896c7a3925b1edac99ade652692af9e6260c2990290c60f42cef11d6e5a161b393ec8241f3f8eef9b427015e8b150d1f7f1a6cf91e6a9a07780b3312e8c7f5e4fd04dea218fce45fab0e06f6538ba60ccc0c249eec893d7009e67df713d17f1650f24af42bbd480bc66b83a1343d4594b4bbb30dd970e198f5bc92bbcb84510dd71acdd84906200f24cdfa7d0857dedd14d880529aca6ff7c5715e50cf0f54d5e0a73f4bf1d720b9d4e736613d8fe5cabdb0f1a798827b25f20f8133107c20ce7e6695fe298a6e769540439218a1bf194c1caf7d221094710b0b29e0938399845518b980a0016c58a80a1a958faef02e6dc4f8652e31a5eab97963761ca0e381ca1fb5546859c499da3099f717106c79bf363ff33b51e5517a3ab27fda8de363f43f048554ef8437aec3d51a93c2065d3f61568347a2a7fc6a4478b04bbd33598140b200d6d8735e2e85006c34ef34c7209c385e36409c90018793bab9db528887ebdde9347a4dd951d822d89f176ca92008824997f70a70a3f5a156e993d58e65424f4835a676c5ff24c2d9d236813d4fc13058305374c36ffaa6801f0b469a890ef30cad842af3e89b0d959f344557c7bde9db99b66ee8ffd40869b0d76706abb1e95d4d5181c9945f99c5bf26d0194533132616841d7429a2d8df1fcb64e1a78a4d6ff8d9038e0e1d805f7c26579ee90481fb4bf9d10b738a97141fd35fdde6482392cf662bef8cf957da60450eb14aae6a36ddfec5bb982dca47d1fb259bb1a9cc58adf7e3ecf865d8bd767cebc0bd15953a01f0b8827b2ef81b01fdadb1ba119757b0339689db2f486ce706070abf02d3ce8deae60402068699da879102d5dfb90fcdf11cab8aa4a98f0989eac9e0962069f3566176b3f28732c8ccf7eff5d214f802a3944ad0b9bdafb09213f69f89f41f2554b87b0eccd0e10033fdbb2cefb53b9de7c4798ecc9d4679db8399aaa069c7b7c9a2d9cdcb203dd11e6ba4e7cb69215d6d8924081ffd673a73a3f29d052927fcb50d18dc6174d68df259591efd2025b3dc8564a2bdf60c0dbb6548949dbe8e3c2864c8246440e6fdab8622e29f142c541d01a1e4a90ae65190137f9e4035205f827e2dc1208a8f7485d2a11132d1c7f5df5c3e6b68ff2b784a468bb4c33690cf892687036a4237071a67363daee7ecdcc78c3d20143154f9b102744e33c116692d6109b0cb8994eb741412071deeb27e8316e8a66f6930eaba7e844aa6f87e85f19c3863a287b96a127c1f025964583ae79595a80cdf18ce2b689c0f1f49d15e7110bff102129611afbc91782e4d86148dd8b8195109825648ca3dbd9aeecc8d87efeb502220d3ac0db227d5e0f538036be950cd0130f41866d784337174292f1a22be9b3a0bf8b377a69f12fdff2072ffd8b8e4cddf34dc0e2738ad69991404271278eac3c92fae84ae36aae8e730590a641a38814507d0ec047d4ada60bcde98a98109b70d82e1b1d724bf589556538d736106b9ade54ae48487f476426f5936fe01d2127420eb2baf45dcb3da9fb993d6af147a853b65b812fdc51ef2e460d1444f8919d5a20279339a90a83ca5d06f325b917eb7ebd6eba86f4d624faa689ef8ff3e77236b5cf4194d0eadde0eeffbe2797388df0170d1f3b8c36c4332eaa4097af001699d50d060fdeb948cb2e0d49317b38a29f335d33f69dd2263a91a8944ea324b6c47ed4dff3d502683eb8237d3fe3f8c50ed644166713ae686a49ad6af0e27ec8e584c1363bc6325d22a607ee1e84a72a4ea65e55d05f47d702893200cc42b81f499c27f102353d2bfcb2f6a843498df3a8d339b905919e4e8830929ded1982c4486b586845d7c372596bb41d9bcc3037ec7c0c863b9ca5bbfbbce97027940519cbdcc0137b7f467eee8323c62d768325f58ee9d978e357d3d537706e6e98fc89b8901a00cb892af9049665490a0e305092e53c3d6a8b327cc35c261ec2961312f46ea8ce505467089dbb29c222c33f558a68d6f748ab17f8cd1b51a49db2796ae47691010e4c2bd9d46bd50f16e3e7f189b8322f86984831ef719541a27dfdab4d8fe72ee1b6e474c1ab016ad581999819c0c2d98884bbce7af2db729838314dc19efd97e71e3e42c2206f2ed33fd1d60afc18589a2d39ec0e766afeb77e9b204752d0134ba2dc66bed4a1f882dff3afb2a406b049796c53d3906faa71b2bdd29ebc02c4ed07a1bbf7f9762724be5d4188ac0d80a9eaef860c014fc9b8f1762ed1cbb92febee2c21c2d1919b0980a80e9cb4a92e010a51d7c85d79e56b8d7772fd7ca4c0d0096fcb62f0ef19cd78c26920dd3954c58228fa8a170fc11b847a6dad4dbd79171f4d32a071b48d4c58ec47c0660c54a2e69a99abe187ddbcbb3ce9be673abdd553570b031024d757f9777a0b9680b77a250f664d9691639ad98d2436dd6aa38cf1aa7cc6e0dfd4bb428076b50d0e696acc4659d655bc6c4b6c86e4e7bc052d79c818fb11659c2d57bf06dcbd029b3c9f79e75604f2f4bafaed73ea368ab24ab18172bde57892729e49d03d83166596fd903963b69ab071ae4e1e5797cdca02f2d3eb897e60917cb3ee007fc376ecb2bd49323a15e02f325f84f19a784d3bc2cccf8a5953f3a0bd77a0d94476b2ff1d4d0769fdcace6a1ca71c294ab268ff9b8e7f606588b11d51f43f2e1a6364cd7146317c6d38567f6a75acfc660560095d3920bb4fc992c10f6e70ab0e851afc83a4b63446b5d045a0934d0d72a3d16289b9b6d293c4490455455cb6f90409bae1c66c1ef246d11eba16d1682746c32e13d823b6de40a0a5c31db4796586b315f777c33453172fe99daaf7a3c486b28a544a65d8175cd83647dc5deab76b446125217aba4ac49e29504df550d74f98f8ff658e6681e39f62cf2c80dd904c853cf4cc77615ca6a629a4781e3d29cdcaec0229eefc297fb985a52a7224e893f47a0afcac0c16c8897873f9b67ce4eef21f5fdbc3afcb612ba57765c69f9299e6e0f9a3402bd323290a4a9d5d091580002ff1f3ddd2ba9100fbaec9742c6a55e6aa985584acc2b1d04a9921fbc3773cce8996eae5453d57026bd4e9c4bda801f0edf92b1e4dd9f3b457aa8f5cccef68f9ff9c8febea6a6a26c15d682ab5ef8ad2e852f3d8b18e20a0cc0dc00ce16af7442c42531b5bdf682b7710ad97dd74f450bb3a3f7215de8c8f194ad496bf28f5d95284b7dbe0e773c1431836d74bee1e21696ca43a515d175b14c576afb6314036c39aa5322c74076bdaac40a48ac40ae4976955925d3911172692d647f2affa70ed85464677576340f592f26c47bd1a02247a7e12e4675a41ddbbcc3881050e13c377dd50173e84931d0c9c6e332cd6c42eeb686fb01ae492e6b8edceb4e3ecdb3486fb4a8539abd8d5d3a56a38d66b729598639ca6163da304567529a356d9246c948aa48638fe881ed7814135fbc17980168a1fbfdf5f84f1d3c3a60b4dfb6440e3cd00e685db00ab3bae105f6696ee43049003ed0800c3d3c10a2125ecfc58ec4b3697745b895e7fb7382fbb23bd52935bc27638c9c549ba40953a7566d09b01d09ec3eb666aad934a2258f40bc17afb3bd5ca47b2bec8af32d7c8b257ce45c789a965530c79d4acdcded6ac87b2d5dc5526e1d5ada7533491a45615844d60f707a5e5f2946e292576d65383ccd1e0d1c14f4f229f36e6ed7f0c4838a022078b9094ea959108b28d24c1dd45c584ddc2a3fcfe6e9bbc6610f977fa4212875bb0b173220c357e10bb5436cf25aef2c2ec9a6a068055e16e256d47d5ca079f94c42aada25b8da83c333772924610470369e7f15743d90c6c191c503bc8466c536f732b6e2b3d71f367b2ff58d8cd0924f19cda00c121c021f5ab9add0f7a2b8a29d96bc58cfc405a984de4a563888984574e801d0bfd4a2a5f35ae35248df4d49af83a810167eced13432ae08251c7505ba410e50aa4e89803e7ed1e042403649c84d063bac08896af11f1b741c97a7924c4458cce4f241897b365dafc228d9ede3bc784aa20ad627ccab24e0b7f6cd9ebe309f244477dc1013b23850544692671af224d5fc52e785849607158960ba0369640252c8aa33e04459129b132f6f6087389b1c073ca1138f8ba5e5b18631afb1af1422ce7b888bc30dc166a71e0a45d0aeeff8631940929de5b5c5ef0441fdcf02b3259588f27ae1d39e10bc58679980223cf77f3c1a17b310e17f883f61baccdeb25ca21cc1fc9ed0528114bccf7ce5ce1a6240ecfa465fca54e50e5868b239cd962df154cbdbaf8c5fd72993880ed60260f13ee98c4942262073ab411b258b1090206bc82014b3a74abbd1de53ee3690032e44e6239c02c39a934f339806a1752148a3769072a48700b924b9d580fe06ad08f0cab91f724b40c040d681504eafda504163cf24dee9d9b8e785f01964ccf285e21704b6901c59eed193f8bf355ac13fcc7aae2a42d18bc46e6ba2d6f57a518039edb102c0394c0c4e41a289fadbaa4c451b9ca80e025c02459e1af08f8baa14f85f1803b2f55ca61dd72f2af303d93dac6349d43d42b5f54aafd0e5555d4ba2e68c318082403f17a3261065aae933dca12cc2c9c4398f0602e0cee7db3304c925cd31d28adb88dbf839f1e8d7ad08c746bce21cb41ec7e7320b5d38009684502c158ee54e61676292079b3cf91d5e6445dd7864dd144ad043a07dd1f72777be23b6190c9eeadf8957463377b93c4369dae2da0d71500d5b38cd84dc7af1f765c7600f971693da81bb41769140220cd0578efdc3341e04f4d0ea3f01d40316b26c4e034beb824561c2c3cab7d6ca6dae102e6298d91687737d66ac7920d343a58fa7b7d635b00334ba59b9555f41c91ecfb26845aac617e668d4a2f7e664a581ee885b9521b1797da7cf95fc4758ec5813c3cc3cb80cf88d755a7ce8af4eb4411e8e1151054eac4002421f22a6637a108739704b8759c70c95553f8908a73f0f010da3d1f96411947720a860ad3e1b83f78f5f27b47bb3266cfe99ee8489710287a4460cc4067f73d0d9a974196274b2dd10759fdab6db0fa0f7142b21cd7af713432a662ea669e3cdee952694d998a68e84e46384752251bf0d25dc8ef9003ebd13a5dc577456147e035a3a8cb70e79e62e610e2bd34024b75ef95464fa605d48903ca8dce9d0bd830611857a171e2a306e412fbd6fc865c32d57dc591ecf1e40f5a2dd1a0b2e8877935cae473ce21c762a14c18c0a55957c15e85b9f4e5c67bb9fff564368916b0ea25144fe1fe7fe6f8825cb45842db994ef1eec09b8067bead380efbfed1551ef7fe4dc3f798820a06ea812f65382416eb6e2de2bd74601a0cd450d400f5a6c18b77828871a650dc4fdf888e441ea8384c3958d61ac207605e18d03d621bbc7ccb6c124a4dcd24f1c520dc34347becb5b6add979c419a85204160fe224b1946a1f80da1e3e33e62287ef5f052410db89119a02943ab6008d79f91587774378fe75c409d99a995b8d2de7a1da6ff167a1f6dd2890f11069464ef2c0fd4197811c11d07941472c3c77c3e5d9be3262a85edafb70b874c96e5fd4d0b219ece0023b8faac8cd673e435ca554c43df41f2b3a0d76221ab09272404617a51fe66af9164da3318b0d0bbf1625f12ad6dea3d886e2b1cdbdba8c6875af7ce1fd502c576a97849006a7e74bab7942dac33d225110f8fa05b24fbddc1e9ae3569694ca2aff6b09c5d9d0d600247b722e8f70527ae297b0df12ca54cfa12b0c695a307756b36c5aab58af4e17ad04e8bbf270ee3966952679e8a0f0e205ca1930e39d9b99af4665c6783dcbf89f6c068bbd2f4ce5fb18ef5fd49bd91f58e48a09dc154291ea03ad522c24ff1fe8416885fd61281e17599fc46c2d322490f8ea4ebc279da2dec6df4b745adf43784201aa61a87577849c1a80a4593fc438c3218e68efb16915742e28336822d3ad6fe4908ea311a7c6be46789aba7547f0fe3ba7b6ceb3234eb0328d4a414392b1e928dfbce0c776485be1ac6b9800c41047def0161beb7a6edfe138345ca41e5be2e645545637b0644700cd30d94238ebe43b22d7323e60ba2dddb41e109139acd1a19d7b5c244665074c06a6210a49e9ced89682308db5a28e0090c032164e35d9f42bbdf2aca5e3964798474c3657c40016a28e92361714a8a601a8617ade7258535377ff57cda2fd7a4ca7c84bf6f1236a2a2d00ff625079a7eed8ff1f2c5ebf6e2c984b6c907f544c292a89ce355d419b02e5293a0b2650506b9ceb9e50cbb5a90a6a836401c20084ad24101e6a04861a8a06989201d64ca3abe398dc57f2c0b8d01b8160085101edf1304e27234f21eead0998a9ab36b27ef973fe4ef43070a7e53467fa8bbd8676c20fefc3ebdba29acb7afaca9f4021fac4662be82aba5f6a1d6c17d121e221808e17986bf87918410d358676aab68ca1fab3faa87f17ee1ebb6e4833c2d793ebed238baf01116cb0c5ba1bdca546611a9b9c57e296c243afefbd46efef9f6920099f00321eeee2e7974bad661d622eb5b17bf3a9b5c1961870c7733580b409f9537bdc2742eab6f94bd7c3e9411ac525ccc79088eb886964d2c66fac7d12a097e56d48dbae8287a238152150fc02d8b54766181a80053011a5cdd5a80f3d634590d86212c8722816fca5076512764fcd9fea764f631bcbe6987df2a24c9106f6a792f5e2f185be0aceb5129573455003825f5ac0b3042b02b625e3f075bad6331e02779edbff1e4883328f3fb1e0302d36e2839e744ad1f448423760704067326e31c0c56aaf889b4a790f876c89df7bc35d772ee3b738b89508b4923109cae28e439f16c341977538fe8e7aa123e7db9f0ed9857a59d39ddbc1c55b8bdf67ed2da28aea54a39d67eee15220d42a2f9e090806bbae675fcfd86afc96a75b0ccc35acb5a951f25454803fc3dee9d1c2678b774c71ac4582344de949b66d0d866d736cabe714357b31c370c7d6ab683ba6e5db0a735c72629dad4542e194b8643429e7c58369142e5177a0b6a8bba87548932565896ad91596d5cf9a5763c8973e0a06329b319333bf829b53766767f3c84ac2d589f6ef45cdaa08ab787a047690e748f80dd4d90a091cc1c817ae2efe0673070004e5972587ce3202c098a529b49e5a82be58baf8de3665fce41535fb19eb676fa65aad874dfd3d1deffab8adc147f6e4a9fbcdb2610296d636af0716b4dc55141b70934274ae7671320b7e8cc1b6e0b97dd79a429f3713a93057ac85975f1744239793a8a2b006f366096420c42cfc127124943f6d30e2059b86ae0b85eec31b224659e3932bc9d325370808f364bdb0ae372176e45f23abec3926ca29429addf57232457edf3085101aaab134451208ec78549358cef741805855b55818b01d109f505bde45d5681090cb983398154efd18b6d00deafb5050b8352096f0e373f5f0466ca0d6dd07324a3ac22260692b016aac461562acc5a35f88821a3a9f18f8bb5f47feb0e3ec3dbb7beb2b09692000f908571315d6409301408a3ae063626b27096abc0605022dca29a98188ab0101b62714d51728d131d6e7ea99bd33d2e6ecce8f9e6bc6b9c9e76bb9e03b7ffbe95bd841b40f789b2afadc8ca7de7f8e58230fe8ba898a5caa5b09a2462fd432dfc80dcf386efad54297f775c73497989e21273874fb919f69954cb6efd3160db5a4e14710d0ca6b64884f103071bffdfd4c053d077ffc501d04f94a4adcfa2dc64d5dd86cf5188f3f58b38e860b60bc17b42d4748271512356e662d88e02472e48f42c88c5dee6ea8851aabb3da8b371d7c61eafcf5216173e08e0e9b7da0715b85e071d184099835b8d6334f6174194557212cc9b41f63c9f084f4e6dbc86a66255551cf372bfb59d1e7e63182e812fa25edb1cc29e03f2b8fc41c6a7893d68997bdebb8ad96270006ee10455b0f3436784f3bb786e91940d0a3b4116fb130ead4e60883efba79afe26d930ce63ccf06268759e8167d67ba0809d279db236d2aaf9aeaf3801a9d6d36e6ceb022f0f42b216106a73590571a26b6dd0e36193c66062dd3f9e4ee4c0e455dfc2463423e56a971bd77fb8286f44f2cbd522d3c926f34806a92bc30f5c694bb53d78969ed571796d31a42b00526443efbef95e5e7692df2434b744a4c6e0f994540cc38f849a01cadfe600cd367d406aa1ccd492a17d0557a6022c68656a3238d2fe6660abe0667f5969a5b2cd9876f6e3af269453a9a216b59098af8fb66c7e17de01156f52e7fe56656135236ebd83d410bd1aa1dd428a0379b9fa48870089a22e4642b0f58c806f7e502aa9cb70e4f944c48f526a7db118de24a9228ce09055eca737a079b3aac0b8c912728700a377419bf1b9114174377c08a1f0ea768e5406e6f85a1a7ac045170e0e5f30e5174d540340343bfe6549da9b1de6d204760d64fceb65cfabdf300089c25d10fb8e6d6827b78702de80d12974b28c2f035cbb86724eab0e5faa16f34f78c09db12a560afe1ab56f82c97c2a10620b3c4302aa96314e56205a16a503912bc07b08c0410d036ae1823766f914d5258b905f4c5d2667025e315ae7badc95b0e0af86bfffb1dc3bd09fa2b711407288ff99679b628e025680e327b3daec8e2365c7a9a1db1ad99b6cb9654a29a51452048e046805a3546655d534c3de351a8d46a4083f8389f0310044f86f34d2ebb11efd75751c45f890ad5392cc728df697fbd10617fec25d4b5595fbd425a189fad81033375b56b65cb20c96aa6afa77966559abaaaacabc592db07cbb3bdf7727e6efa2fced0e7572b2c9d4ddbddbdd422a2c410ce1eeeef6fc00f1bccf47e90ec1b5767bb7aebb5bf58aec372f0fd42c09d5165a6207e3165088dd42c822b9d66d89122bbeb775d69eba26ccf07a7e6f81e645684bb0ed82f03fc2ad092fb1362516138eeffdc797af8c5531c2e5751e87f4aab86c3a8fe3f29942463faa427a27ef5ef71e7b0ccb9877f5e6fdf5d817e1974e9e1296c1017ef6f0b32fb2234769a9da2227765ceceee3d36c4daa7c56b15f86add1d84fa486254a94f88f3e5388f54a52e024fa5bef72d278a63f3b0159ae9f74a2e1997e78aa8164e422619b69b98619b75cffe804c372fdd789c872edeeeeee9c4fc7f5306d6022aa0c6292ae934e893236409512758998a4f74556a3c42459efb4043e138e46e0a6c48a48e0fb86e43d8cfe30ea7bebd4dd90df448915f5fd9974c3db9458fe4df8b7c6b7261cf5fb33288b4ea3fbebdbee1bdddef5767768c16ef6dd6759fe6feb76ef7c1693d62e2b98fb73ef2330253a475402632c337da1c0b0cc9be7cddbccacfff69fc5fc626dccecdb0b3357c92fb250ac5e666bbb128caddcaba35488b28da689c996f578eb5a88cf6aab99adb775afdf77fd70ebeec7fd0c2f2260586fd15a6de0cfde5782a601fc2599f9b9eccb15d38229046e95dd5caac87ce6ef24347d4ea22b1601c62db7abbd98d2e6692f5053462d6bab6b6666fa366feeea0063765fef1680407723009507d6f3dddeb59e2b33cca71b84ed4bbe3f4cf5cd3dc1dfdcfd5503cbbcd97c6fde6d539f80e5ae9bfadf6b5f7ecfe6b281af4f7b39bdb7bb4b9858f1868c4cc43a79cb57c8d7eceeef410bee3e6566e68cad2eb7aebbf58a6929bbf22ba25b582e9ffa2f97eaaabe0179cf5fddddad5782fccacc0cbd4dcd038c88c88a260b37f4584c2a4d887901da725089c94e43c34567f73c42cc6f39349349d3dcb777df53e7df8f25e499fc4e8bd0663f9b08ddab4ac7e1e999a5983c2292ec74e5fc7eccdff391786e8bb97b66b7a7ff7bcc9ffbf3cdc37c9f4f5f21f366bbdf77fcd0d7c6fa0e765bcfd1c07ab954ed2db931cd154292e8cfa7ae14b92debfbbbfcb63ead6c2b9416ea9ab2526e494a3175b31a269622fb1af6d5be75775ff0bdaacf90d569d1b22ccbb2ac64df3a6deb4c913f1e513a80136a34a1b4ba104693ae5b0a773d493858b163417c9bb69cbe934532e2aadb4bbebea40e20af6ab25a78d34bc90449107062d2b097164ab975a5a858938cf8b5be1d97530b5ad1c95338051b1233ad54529beb3ae518f1028df0017bafdf7b4f97655cf82117ad53e2402b49d0bb6c0421c5516a10d85f078135df3a2bb33b3333b78edfb97d747d938c8c5e4f3ba2bfabbbdb9f767777f7e86a982d47727fec5d0f1d1c7ccb7c239f58b59c3e8c02cb16bae7850905c35e140b2e46646636e45623347e42cccccccc2c052680175ce0dddd006ca70fc2020080c8fcf0618ae961d3bfbbbbbb251e3b74682f2ea40c1b5dd07adebc2f9999c9b9ec9664a03e2766f5d9ee56acf56d50f60fc29f381c591145d377deb85bf0cde5245c6cf61433275bcd7910fec45b211a4cd27e2fd908d9ed634d1cd33dbc83fb30af64f1803b2e1d230d7bc95c482497ec05d3463aae1d9087557a30dea363d8b43ef447fe70e1b7de8fc8bfddcdcc9c9fca3c42a3afb80bda8dbee22e70172491840b2f08809b494169818ece8bc1ecb9ccddbda7ced388e969e40cb498bd7935f79999333377593b7959af2ebabb1fb7676766fe6b3ebdc7105a9c4672f9bcb965ed8626dc1a77cdce4cde58c897d0f4210f545f6aba6a88c6d0e9abaaaaff0781413b77f7013c5575a1273350653de154d5859e0800aafc882d2ed0f6ad177c37480917b04e55350b808ffa5c995bea73e5b9ee69fb7e9f5b9dbb7be6513ea5ed3f77a8a905d121a4b98a2cf0d80180120f9211055292817996bbbb5baf476ff120fc899bfc083fc2dd654aba6b5200a83ee1d1791678ed604147104df5c5e094f9115bdcfda95cdc3d06dafeb3dc3d00990561b6bbdb5b5c784100dcccf7153fa2b73801b2dee241f8137f0d43d65bb4fdf7fa84e415cd61a0ed5b906116c7124310fec4850c09c29f780c3441f8135f45fd212423fa9a34340a40612a133b5555ed2b9daaea151ffc4c9daaaaf77098d8b9bb5ba5bea2a82b5599886790e001af6b68a403b3dcddfd598c8594f90b5e34cddd1f1617d390bf40dbb748d09d559d41ff979934f70e6a991b81c191ea8ecba1fb683e966e4da7fedc39fae7dae0bbbb9ee3a6e7eeffdc63323742dbf757bd978457af4d595f7128dafefbbea2ed43ed878f95f901244b92aa23a1edbb0cc9883effea966ffd3c10d6fe2c78c195f60ced8e8a75da9f052d22b9d29ea1dd51b53feb11c995f60ced4efbf38e48aeb467488a1429dd11c995f6489122255752a41091c2c344989999f905b37eaddef2a609dcbc69a271de343145fdeeefacc9d8f9f0b12630ee779f3a27d19bc0b8affa4c9ac0a89b0ccb1921c2297012f795ec26430ae167526439a6793afa15ebd7edfdbaad08581b127f18bd9b551ef93d0c4c552a9f2c62678a259a7789abca0228ae29c62e3b2df9254d94e81c5149329189bd21d99a38918133857435447d22bc4d9458718614b2af448614e29fa4de8a2cb7ff747434ceaa51e212e6f76f43d2bfde84a36e5d6f4aaca8bf35bc5ca811f7b3d642c5a3a50727b1d3df1c7f8e3f9f88202b92aa6281256c5d7efd16bef7deea4c99a2a222678a1fe8d9d0fb453b557da142db6ff7d40db63e2b32730c77530a497fde9e73371baccff22d3565fe6e26ad4dc1da9f052fd8a92cedcf82d6100375aafd596f8718a8fdf990f7cfeaf0eecfb22ba1ed43855592d009c29f68faed9c5b3744ca9128c2999b05e647708ea6ddcf4fc07a8bb31ba1e9af9f786b5d2a302b31adac959819b49bdead106a9ac9c4710b4897023bc5ce4c3fc57e45d2ffb6df53ddeff5eede3285b8baeab92b6fa60e4dcc9d6841a86926d33fc75dd7b575a3cc12e163a3b7a3ef78d58cbebbf4d4b910a163502f0d1b775128148ac976a62261074b48c5b4e2054fcc1fc4b4d2c595f87d042844fe640309393f986207040a858a4c4c241a483964bfd3d1ccb1f59875ea1eff86a47f63462bfa7b6168d6fb2f927e8e48dc1f66deb8f6326fab3fd46b6181999f0f98bd75f5d248839999190a0931afeeee32f3080d1d347474b21ef4c0c579f0de7befbdf7b433ce78efedd8c10e98b9c43ae01d661df00e33f31c3dac58610e12c4cc1c9bc128663318c5cccccc032883a70c1e1e18c82083996318638cc7cc34cccc3960666666048811244650100d61f084c1f3e6e0490010d07bcf8607c653bd07c653bdf7de636607f0179c62fe8253ccccccfc001ce08002ef79f1def326e92b17bdddde7b1c785d3cd47b5d3cd47befbdf73c70831b3033175c30f37b2f043f3fef3d661e016fc1cccccc3d6801a405d07bef3173097c7c54b5cb956a6e66666234a30b726c1393e219d5181eb6196d3d563ca37f69313dd0a4c5f044ed91250bad034294256af792228ae159a9f67677f7c012f5777777777555df7b3f6021f4de7b2ab8e28a399879055964b130637f755a7c6c631e5d905b9cd177a6982f29b6196df0da5e76a049b3366b731b686f3d923e93303d917228ea8df9bd97f37adeeabd9eb77aefbdf71e12558010e958b1b262b552a2063d35e8e98142831d1aec30330c7806fc8c2555c54f153f3f4b5041c5141e1e26a620626666e69dcca1d0a4286561667e8299a3c8c2ccccccef3d2a8fcae3798fcae379efbdf79eeae70c25b4a18433548acca1d0ba189ece09961d2982924c36eaebe6a42345509ed0ba97d49122285b0365676b321e299ab2a723453b4e68478a521b4fe4d723455098a49f242831a12c97f14851ce127292848335c71cf148d1947dd98950d6e625158f14e9f0e3c78f1f3f7eca8f1f3f7efcf8f1d302458a382296376879c61b6fc4544de145ec8010457e534cd514542206021080e00321f00027021188c00324c8783a7046ec1a4b0f3df46e0f1d288167e18066021378b70938806403589224493670020dbc090528408106946460c992de5d920126cc18601aee871f7ec0800a7e2e80050b1040783710170822b3c00b21841016604105882082880aa07a7850a98a28a2081e8ca0404e4e0e05903882e9137da24f8e6812ab1d945042891da0e870b29409654299d0417726b0354e38e1c404a0c8842400052a5476b7a948408a6c77155523b03d8ea03aa5477bb427027ac50a02262cb0c002023e40390c0d69a15a78b71639fc64fc002eb8e0e2015de0e085175ee0f045e60030c000c301616437a8182a868a71838e81b2810c32c8b0c18c1e232a151a68a061248dd780f7b65e638d351a300406c020431b6db4c10022a8708888dc70c30d9c22f4443562d75892a049d0244455024f1121a137de78a30813821690254b13b409da8405a81314a0419306071c7028400a420958ad6207b568d192802da9c85c0313a1512a2815940a35681578681012522ba815d40a34681ca81bee550e9543e5b8d13910f0e4c913042491118912250a11293b91d986839875ca9429364c04d564c912bb6ea289266a9c98e189279e98818a2524c32abe2d8a28a290610aa003fcdb6280a0e550e4e1e13940154f27be6700b6c9864a5980ac3a277aa819ccc0fdbdf79e77cfc00035c80ad0313c56586145017a8868de64832cb5d472198788ada593e85bd7ee4cf648d194e5321e29da591b908c1c29cad91a2731230d2955c791222acb65ec5eb0c856478a7486902660cd71a42867b98c3134483640f7c642aa77a770052c5be82c95149aee76ef6e5dc6759b6868382e2e2b519a7c477b71b7eeb9090d0bd1aab4dca6905d4b85da4d3782c099abbd5768651ff5c9cc4c7d3433b32ccbb22ccbb22ccbb22ccbb22ccb96d3cc647ecff2b34b251ecca6ccb4b2edb729dc3071df93a0e51029a60f989022c298aa1f58217657eaea611b88e299bd76d8c6c7ef0469175044c95c29209267ae283499f7f11d44f938290ecca98ae9634e3a10c7b4614086879837bd691ba2abb004144ecca6437f690d2da534e4b593ca9b648394c30b89590687ee60bedf49bca84c49bd6b3130db3565b9fd9cdc19601e89c7c99d2166db9f1d43ec38919e4f49b2587a25c9ec20f33f9e07191f2fb30da1443e4bfca470621ec82b49071fdfa70472fa719239a9a24adfa90e0af5c3a6ee4d73a0b1462abd603b624e890377f286276f1606060606e604f34576c72963e974c518188c876a3ab21da4981f682eff02e4f263e8b5edcf4545ebf68724b383e97f3c0f3e5ee647e624a37fedfc38c9e8a6c385e34bd01668b9fdd2b6fe86d641201fa722a7e49928045dc1491c9f1ddf65d08eeff149c281c7c37c91cde44adfe3d4c9951e2b6510c53689d39968384ed350714bdb0585e651b7ee611c5ffa0e123141cb35901047d40e6289db05110ada9afd59a0d8ed1971f7275bd20d1d950c115537a8a574aa627ab8621bd30687a0b0dc76576a28ee432c5bd35da9236ccd330a08cb45ceec870181ac40a14d232ed0fe3cb13f5af2b18281d1928f9363d1d7f176a2bef6e2599aa4253a511c517f743d9da86f3d28530e10f30b9034c055193ab3df713630c2321489b87028872cdb40141c82ab8b99d94706a510f7bb16225c9dca88fb6903817266dfc7ca87161f44317f39a1758be251551e2a475ca92ba5ba3c3d88a90bb5114b4d55f5f983042db73e563cb384d65da9219737babd52319b4f77a5ae94ea855a8742d4ef2e1d572dd7c672daed0f0751c1ec0e5e60affd746f6c106128062d9f5abb4aacf88888ab1d938d8ee56902fa850e51cd6ffd592ef50b1d92df1ff304f48096d3b762d4ef9343d751e6a715f1e16dcd3226f57e4d4683dad614d156fa1a6ef114d3846dfd460f24768b1a6dab636daf891af2d04e7099870378666b52c804830ed9ef8d4b9b187466b36f333492c6bc7d6fa934f0e6430a925b207866dfface05a6206b32b70a5d27926d5687537f5c74a6db2bf6adac37625a96f942ccdfb2665152486e3f6b78f5a7bc79af9916b53545966329644d0e415cc6e7a72b9aa1b96845b6c687e5f6f9bb8e39648d10c9ed03b1dcbeea9896dbe75688ac2140ec72b5c3e28c3e49376cc4b25552ec78a52bedef9ce52a240d9943deef736bd4ef3acabeadbbae10bb7f4432d2cfd50424a343abdfb5a96b51af67513d8b7ad7f55288b53bcb735a2ab645f10c3f865a1d1e155a4ecbf1d14dd550d5ceb09fd3a8df32b447686bd4d81a151c69a34239b351528592885b44e42a686a45e3183bbea28becd1ad73c522758bac79bffe9d29350717151aea77276f326f34629acaecc22a64a91054212cee70eb4c2ae46e6d1dd43454484785745448270aa5106bb44168f5239134cb47574ba1f163a9a9bb669c7f92541fd391f9a9a374eac4ab1313f10cead4aae3c3d6f412caedef13206ba0c4fd4e22b9e649c56d26b4c4fd8b0aade315764a9cd4f87d0d8dc72a6f7208719fa79036a3dfe79596ce6139461b2f7cd9ba17cd9242d4094a3487c241b438a4e84db01016679f89f2263dba50f47f2423fe23e6070c07eb58a590156a95a2d941110e626f9d6951ac4142c012b7889cd9bfe06a4fcfb2a15d415b73c6d6b42a669af32a2eafb6468be2db3407da43c597134b2ab84494a07ba468cad66c6a6bf44b5bc3249829a8d46c9ce82844440200042008b3160000200c0c090443611866619cfa0114000d68823a5c503297872281481889411c8431108930140420409821461187144a3a000e1da0bd170baa2417eb875f0ffa2883360c80c4d20564f648023167a8d75285371ae689228b7d2b5b7b8c13acd214d1bd3fa9804d7f3e1b5dc409654bf38ec610379ce2b1ca1a8f08bc669178536e54aeec8e9fc7584a8f59cf2881ae3447620d3f1ee65e636c0c31d31a7f896434c00087d05a6de807e4bcadc2b1e2e1e95890a3e93019c14e3b951b2f30b62970e5fb2b7e8ef44a352848fd9df42ebb8ceae3c6cbf8ac7d9c064ff99f3380bf830263f8e3eb76fd622e5e182e05328fa6db4ac6a34ded5d59d0c04a59465abccf828f7b3af82ce47a09975da77ca29ac6165a826c44b9dce199121439fc9506dd4a4543c6d6afdc56caaa3747f0d60afe0223494d81b8632ca2f315af2709b56db6419bbe5bd0f71ad75677fdf068cc74d449934ccdc9feac8ea7ebf0c47cc953f82c0a4ec8812098687950b8b8edd1d6dc9417842c71c6d34b8d4a300d377fd15e76615ec71749c5d15d438030cebccd8ee9df8b2c043666cfbbe3c273ea90b458c54e3db799fe3ef411b9992a325b733a8eda40c7907d24a186b78b88c81e748df23d039c06460754393a2e95484b0ec62e5eac5b308ce8e54e437e080c10c68a2d0ce6be8a88b9d60deb23e8315085865e382b988a05b2a49aaebee1dacf1c9e0fa3173e7c8073e53d562a2a136fdb61d981b37fc315a5cdcedfd2a0065c00c1f8bafcee81c15bf8e5aff8b7526b226048891221b34da1981209600a4269475d900eea13eb00cc15958950af88eff01590992243c1a82b72f63fb80547b79f07c701a28b8dabb2e0523c8b6dcab62a36282d51a1ce963a90604be3e2ad2cff0baa796b47c3affdf335746de25e508691d306a83144555017622dd2fe3a580f6756be46eadd6f9572e3e0be6a2f95a244516d4a5b01f8f507f756d728fb70af1d4de76b80bd2a26ca63a917e76160dbda1f93d4750fc9b6354456336a0f8a2d538b3d4540a38bec57832f35a6bb18366467608b9e580e138b974bf5379477a3fdeaca2c2b75539f3b24bffab5190df2ba074cd90a025ab0867dff70cc88583283c3beb846ff2f88ef74238bb621e6d0829d0b15325fca09e2e7e93dc023bc952a2f7a7b253b9da7bc4422f4e062c148db4d89d7cfd88946952a9e8ac326723c068ff696b51f3cb8f8a97d247477e0e4cdadf720541ae000c0b8d814ff3dc7e72bb04ea39e998f86ea77ddf3d6d5bb10d260723877bd261138b3e6629b44c87386cffcbdbc0b946ee96f1bfbe8ce33494a1af2f72f8083cf099792f064fe42ec151be61f91a2508f259f3ac9d2b6de808cc1ec5e3d5b86d58d17da77ba80202d50a274fe04b157ec7ba4c61010721e1c38da71fe125f42eaf4eb7ed49ff35e5fbd7c76817433de15ffa0bbcf095b38f2090f1b2b4293625874f089d086c2898636056039b8783248f76dc9219eee3a615a456e5af16116726f9ae056043d889ce67e9d8b3d4242a372fa61a2e47c432f195d52a4a490c279993b579c928d432ba6fd851f54d4d7104a654aa57351f3a64efc5bb6cb026aed5c7efa37b538c042a7a6a39ba52636e978d37451b81be6ff307d7e1e123357cf7ee9545a8f6ada610e17397c18408a2895e5c0b3967722241ff514e44aeb33b61717b6cac12956eeca45ae032644230cc7d02b50210957db7471c74b7ed0ae9a6b2626d921ab4522fcb54a6aa825e0c1e8b875edb101db2b8ce5762467dbceccad88ebe56f21ea716e2f6cf7a8e17bbc7153bc6387e7004fdef62aaa3deeb485dc812aba34bbb071286e148139e8adcca051019cd2d15d5c91589e0e8760b42228af30009bcb13b835b3a0cee1b31224052b313ea9ed021b612684c7e9c9e7a8d6e20e9bb663ba6c48afbb7bb7bf1caf8e285ef502b847b099d7c8e43849c0cfa9a4d7c4f4247c3581155f3be1765c9bf561112af989a8e2899d57e7d7e67d63f177db9765b2e816a44b85aad9c25a4a60454b7ea5127f67c945c0af2bd62dabe63c6fa917103238c4c78152199138ef021715d4b372632f6ea4a6739140349ea703292212c22f6fcaa2e6496e9ee24d4fa797a20c5b859d4b76b77a7119cdec48a07d2ac837c0681717d5e8a402a07b5b599fd98beaa1e91a190cb5186b1a873a6bd930f297eefb7e0af99c9c6b34c9686c7c82c61360eca65d08e6fbc45ab671f92b4437193426183bc0314a9ba01f928a4e9d993a477362da37d2fb56d107f2921781018fed39c0c065e9942f29ffd2eee5af03f65d74c07d7b8c5c8c72ebd0bd61e60a8a7ac9d86fed262112262360322858325db75f475d8a4ab96476b3de106533d8e32f831d78f406ef027b306a154cf2a504edb08bca9c89975f3a7c3ca20462c4e8f19e9cbba56d08606206e553f0265af634011786cfb8499e82bce34b03aef57b27a54d52b15c709fbc44d3fb81b51c3502851e155de118fca5781dd1073c69f080889a8d47e44b3f41eac9a375dc467544f75dcc4b416d27f0f3c6b725dd95b75f2df78626beccc3e8a51b9c1b1b6dedd00a578e194d14479a27dc5cc2020313cf842a44e2900931576de59123659360bf559123c292f5e5f7103694eaa5e8de151d3683747f63376f6f1d91fa3b718a4554c7a6647c6b671868b63954d763b1716928ef4676affa57b7b4d4eb4d0ca3d8d7054c0e512283fb58d4dcad25e48fb1d42c064ed04e1f8c52a0e5ef4ba46bf4044504c36e7687e06cc2c0f82e5a99142a2a9526aceaa470dcb8def0039ed2364d20b862ab8cddc80bd4528ba2843c02fe59112d2882eb754d9a31874a433ae824c655bd9b8ca55cf636cafd0a0d3247eeb44377ffe649f36b751b1722f3147bf10bb2dba8d54ee038d451aa21088bd3fa9ec26bfa64ddaa80c7d0c947c72483dff14c0c9844c837d355c441ad2fc79d3f2a551887e2bb0fb7eaca858199731a7196c9efbfe939d9ff7bacee1e4080d9b35a8f2debbbfaba588e2cce10bd9d06a7911026ff8083a1059d2395e20e4d4881fec6c00931032414b2769659010857f42e61a8b20f6d119a0925372c572f679fd972cb8e8edc9821ca38014e135db43f4312ad183b699a2897aa0516e01c9cc55c2b312fcd7256ce7b9d42ecac0c0e767a6a033489d9beaa900a34477358a6346627814a5994cdd309bc66fbf658315283d28931751ac95b4f813675dd89cb6657cd092087eb888d5e3a48ebda6e4368a71a67378ba07159ea48c703a6421a9ae17c9d355ccd5acffaae0a3b6bd25b0d7b688bcd866cd2884fceb4a3c7a33ab7bda34f5b316071b36847784fcdb2156e942fa3dbd75c2e3a542b0286850ec4d49bbfb25ee944297d9029d3b71887a848148b9b68493bb7889229d24b5291a3eb7b41853b652b51236838831886f162a54cc54a2139e1151ca25830ce911461f2041f8590f381eeee602fb8ead9dcc9eaf7b88c7125a11723844adb24ca69c777d4dd4ef45d16c5ed3a891f308a0b90fc8423d556be54e0f10c6e37c8bed816685d8afdcd24718b337952b602778ec4478f2328d097af2e522a5c1e31b392f164a46f514f7b40a0288cc89ecabe7415622b989be88e24530629b6b65a29e265243f8e933c551d47e2567ad09e54cf6f312000a815023339d4c6ba46e4a753a657a3097263af9d1ec19cab971f937faca84362fc98125513f50dffa641ddf489eb349f26b68b32461a2ea1448e02bcd87198c5693906ed458c15d2d0a51f401e956811faf29e14ce63b62b6a95d279f4129c9dc8290a214208c84d054551f0ec1220923f6c982140b2e4ee5e3ad3a11249a8d2fddd18cde5e88bf1095025a72160e4faf4abcb07665277912822b6b8c13a91acddc93e79cb2479666131263392a1421fe054d38004d3cebb2cc8f0088d2d955e48662f190bbb581b64ef14677c68f92f556626dfc24088f6c98b71f12e6394f883c64dc210c15ad9f3d2b5813171d64828cbecf5eb2c9ebb4646c64177614842bba8a043f3f50e90a327f094f9c2060c8112656eeea54ba59821761b40122a2362d87fc905999e211d1bc064119a6d41d1599ebaa60c78318cbf685d1a8dd070c5c584a6c344ec4238a1806be31ffee90194c795a498b59b6389114c48871128a94c6f85171bed7b9f5907c767cb1a42f795bc7a8fd7c58dc94462e89252a1d595a1e8be71828ecde75d5299ef2c067c6379fe3a3074379d4b831aeda50a1f1bfd5adca7d1c626d5327c893f6c1c83c70d1ef7eee2130f93ba69f4d699b28a6b8d17a8e92908414dc31a2598feaf9f69ec3c6f542917fc5070d64f5c236366d16f6920f13a54d0b1fde6e38ee4739c04c30fab42750340cc8806b1befa348b5a76f13549e8e907f820129a245fde48d681d9f68f10891d209dd2a90264db74fa349421075fc13239231e83558406c2af22c7e62bcad7049cc52c5a23107ea81493b8735cc10fdfe6d3ae44a92e9d113872dfbd45d2c9dcb6cf10890d60baeabf3d27ae89290b4c40100a0f6110d9e7c0e0d78cb78cc1714c0cef9a434480f61cd574629fc53ad69d5a8174b10d539b3732d71d49377a5ec56e2c7cd1da286d3f528b0ab147b74b256a386edb5117f40f76f09f89c52d9bb29b69ab9bf7ba72ea5346a2b80a4a9cc8b2f028ee9b799206c042cfda960ea21932302a54cfbcec601889fd007c46903dc0160e3c64d9f85c47cce964022ab86429ea97d42f6d9169336c2856d893ce1414e4fcf1d32a31bff7a4a4b7c34c03c4e460754e26642c4fff7822a670c725c3320d0e654a9541e0f6cd503e1cc781753b28f8f567991d0707ef39fa8645fc3860a83043877e232b83ce128c5875e0c5c28de2f33f73e1479703b9c812f5c9cbdbe38c0bd1c14d9d76b942189ac6370e1909b853d9a9e14312070e9d9c1c9959dceb79c67952f90b818dff3e8f53c1ad2550507d1e754932e4253d9d2bd8b7cc82ac06a111a405e04ae29aad9782825344dc51e6a5f6c300de978e4d27b7961501bb6132c89b99bd98d0e2d6d1c6b6dd497ea191e6a4b5e8bc67ccc83d32034a94dedc96304b3102492cbdbb95314b6406965089b1baa7b1761a7a7f5c49561a94e6844e582967dcca3f22886c88827f5096e87f5a49c4aedcc5316279845eacc6e5b1f9b1ec528f19e3d16924f888c5aaee637c308bed8d516b934c66ac5c508f3d446cb8d3e447b33bc2fc5cccfc1bef48f6b44f490c585929c8e78a4718f0316b205b476ef37021b1284aaf03d517baa1575968c5424cf4a8f9fc819124f7f380b020d9b59498cdde8281876ea8e23bea18a30629e0b3ef24678b6c92936556a417c28ad79ead3ce4ff4afa1910d1bd12aa100ee38ab678115d589f69894baa10402d17039318d5b0dda2be7270c6d561ff79124301ce36011191e7e1c80c4536563154b3873080c49530010c22ff351180bfc4ab1332a5429af082a877a0cbe98a15cd28db5f06ba3280a478dae72bd135e225805ff35bae773382458a55958c46e6a0c426738278bb20d6551d6336fd5ced34e74b92a2f4c67b13a0730e35da3981f5ac045b3581b0c02ad0db8d555cd98bea065af1cdea75572dea0170e5ad828ad7de3115ee3d3c7a63b670f778d91f57e1bdbb0057bc2a28af806baf5a9403f03aea81567cb77add578ba65d54511c81564fd52826d0b5471545735543ab2fd50534cc466917277041922fa2f26b55c8d7b7614fd5f200f4b7a146d4c24ab59bcfdfbedf7fddcc2cdc6a1e0091fc87d05045b3cd09e0306ff7cdcaa7667c0370529285e57db98d5c87c6bc5b0d9e09f9df2515c14aae05964305ca3066bd8fc50bf5f1cb6e3c65e261142a93146f6306cc95805094f03f57b9a99255a8d124f0e5c3e6b9be3eb020faa945f1cec1d154433dda1ce85a0322e4d6c8ca58760fd77e88a1f7d7e4abf54fce04febba31c6e42ee45aac108790732165d390a6b15f2e640fe439f16d2c0cab3c98dc0774676812d04ad052c647192d83c90d44c2335352cf108860db48a111d434ae97000108fb7fc6a8c6c31a8d9d539ccf1201e72d51ad7a4deac8547e4bd6216c24d574a1b1cfa9a057002804c190bffedb76b926def999a4430c7fef16211d0fc1763533b7c541f0e1e54c37cf5e2e2f361b12eed0a722bbb8067a481328a4e798aea0f5eb7b4374ed1c61982cc9c12ed86238da7c7fd1949f754ecdd9108d8d7322dc4025d354d380c0a28b4e3221f3c1433fe5f404a769849006749a11348ceed2f04ea9092985ea6081478d4b9c0179092730281d869265ba44667e8739169eb92f706aa5f4cddc9767adde0e335fda1cf2534625505e927b0bb7e8c19ec0735be9fd90083dc261db25847df5d200b615b4b73b985832d604ae0785028e39f5e532846d2ba658d7ebebf6b83c33d13fe510303d9340e789b5007c28590e737c9e60cc3711f79be179e5fd4c5ae0a580f972444980a0a34f5539837c96a658806caa6c2342f0925c9da260305f129b662af367926fd41010371131c617da8448fa2532448d8140edc8ee410f3b3f76f5e086a2e9a7c514715567827fb28e0efcfb44f1ebbb17751525d3d81c1e69700c60a77a5f706ffcfd948acaedc0089ebd495ca324961cd55036e2b4597ae14f90b4059459f1cc5fb083042c09fbcd8296960109c56308622f4bdb3905284b8115b471488059a85c7204086333d074dfd46737a0a825212876025845ecbf4a60d82bf2d843391ac4c51b68a5eb669fb85416858d21832080330870fc806c5360651ac2a184c5e2f28c674b14f5d304a4df1cd12bbbc1d95c95fb68774450baa529d9e60d0cb8d7c39217014b5d6e819e8405c88d23c53c63e1a13e1f8563a3141a6b36eb7b5aff4ce74dcd7026cd4dc86163ccfe5e47e0d344755aa8f2a984d96fe5524f1114339da40b9e976f218cd5d87b0cabf016ccf371591b078d9130115878d91e55523f4cfd98256e2414d0439a5e1eaef0188bf8599e603551fc30bc588e97b08f154ce4305c3b88df9f0c19182f6957e3f6a89fd1a6bd3c4fc3b516c2319b1745282948b83179f16c6c4a2f8e98853d0f27a10f2e7444b9de510dd242ff1752f19566aba5b5fea460b363afc603354e2962e017b22c3badc7a3dd124379b286b0060cde7acede4e61134f2578d06ab7aa9167227b8649fce06591411f1b8082478967c0db5eab02cf5cf21c056f951556844e2643bffb2ed3b9b45315580e5da15eb64ca25dea9add7b24a1b540fd74e35c694ad9de910bd74f0e3641db71ba670143f7be36989ddb515b8785d6ccee296cdb6c0c508a9afbd4b07a7e5853d2b8f23251ce1913abcb48f316ed01a1a5707af0bd7bc9b0b4e31bd29f8e6e400b438a95d307786f84964ca8dd074e0f8de35f3fa8fd9d73f66ff4415f8e28ff9aead21c118864f5e0d7efad5ceabbfbc974b6c73d8f16a9bf7c7cfce133bd27247c0dea3dcabfc994d00c1d4e130c8c996f125d78b8cf4bf84eef04340d88b65713b8cad1331ddeb0beffcaf0958a2a8d432517f3e41287ac29eb3cc68199bacdbe79782c11f90c5afc3864b5f610fcb0017befcbf9f5b051a18ab5c6830f3768254ef5150cdf4ce4384436a85e64d52b8bfd46e2a8ac045d8bc3e51b64b69931479df1ee0892253555a21995c31a54a9597559f3c21befef5a0749bda5b0cc891e55cb17579320c01ec6f8f95dd74513e1c962e0c2c2d9d41e32ab7e434c4527704a9cb7cc20e08af6e78b6ab29a8ef61fea84f3220412c2baa590ffaa8a77137950deac4f1a7091569af87324b59be4512966c25777420dff11ad7f4e303f9eed5fca61b3fe87d5755c25ebd44ce8e0f8d4bfd1222e8c7ed7803377250c93be0bf1a07d013de74a307bd5daf71a61f5fd0ef9e8637dfd1835ee735afd98f4f41cb0ff3e2f214b5fc1b2f05a3ee07e838965d47f82d089cfe2d3daa82405682ac721be093e2d2580f12c2888ae6a68c50d7e56043ff06d742fd23460884d447ffc225269c27f270f7740ff340c89c0dc85e08813136af209db3d8c3f0d5ecdc1d6fb2b6bd945c9a3ebb9ffe3cee32baa09e47b339e81a31bb68f080b6a294ea82df586e4cc4ead81eb90c10a10232c76caa71764b7de211206190e89803b30d8a7495e903c00a76c06a3f310b3dcb37f6b93e4f5ee75d53247d1cea7f0165571e52130bb1ddb32de85d5352dcd8a4c7caabc138d50b597c69bf3be2ac518278fc2e2e4c2063b0cc7a6ed0e7b9890af54b726e8559633202b3ad1468fb4ccc7c7d4db86102da16edb8ae10c946b0a02e43aa28c5685bf4f4641d43a4463ce0029301b298ef51efe5e47f14de9c66f65961675670cdbc7aac634be6c1908cb52c9b477dcd0617ac42050a2589e846715852f2d8639803fe2b339ea7d9e199391aa04ba6bd044f5e2a8bbd1beda6a267e660b27dacc67a2e4ed84ae336539f703b2bf18989253555b0b7adb8fc3ec74ef16aaaa42db3730d4d1811f4de085e3f36b3cb8b9dbc8192e71f1566843fdc2624adc25f0913ce35da88314c30e9b953386f2b1d0431bb2b4aa22a5eaca24fd03339e1829ddad35ba43c2655901c9c8d0043e114f3c2e477faf4f7a6ac8289da91d5d6b227d24003566587196c82d3ec91861af02a3bcc701b8c668b35d08257d9e106dbe0145b343bc8edc35b3b2906eae3658aea659dfd491618205084a13c2362d26bc4fa45c69979206247c5aa011bdc605b6f7f50d4b3c4653f0a814319d61944c889247604e4c1444af81821db4cc6de081bc1a3182c98dc8807b68ef8eb8285965f454dc6f335f49735db27e0061cee06fd9942e74c1208c983c9bb412cb40fca757f69bcbf87d3ffc0ca30d53462b972adbaa1f504a558925e71b74f16fa607fb0fc0317b2c506564db00a26cc2296c9c49f037be4414f821e7c7b3b6882032f447ddb406c16721020690773fe0a40b34941252571134920f145bc488274222e3b11fe8141064bd17e96ae6e767d0899896b71afd840f7b07bf9c6f2b8657b87f48271af00b09e38d1f4d6fdeb124ce217941ab81c136ab9071d42b2a7d960a6496947b76092f754434936b9f214b34e845ed6c058e8112efca7fd5c235037e3722a3ccbe2de27eca516ac9baaaa800f9d7a1c2459e1f0c8ca351ce9248b612c513c08dea32d2d371c410a30d27891c52637faa99d4e7b5278a421d5a347ca32127f5a83a6859db8859d51395781ed233517308ed7eba18ce51bfb6ba6616e10e70acde5670e645585a507a17b43a8cbb92f577efa659439c5e225b83f9688c9b2af1fcace3e401840f15d7abb8920ee34dbeed5806aa3311ee0f87d50f62ec815622d553660b125de7a5db775062a6df48a7a45fef57ea0474d16c017dc51acc214a26154f7c3681f359388278a4fe59ad9803e0addd3ebb8bf4205fae2f076c26add06b2a985613e2579500282ee7a0921dd5d14f84185e24908389277275cc1cb436a303912acf882bf289388daad7a6769c82020ac3bd1d6d3a28bc0b7be084efe6b1a9d9643b716d2db6d24f10982abc81cb479e9032b6c5d61be8a0ad2dc9988ca7255be24f8b2c2aa7944595fd0f05f99caa52a56f19f6bdd1ef8d1558cb9b3ed2927469cc82b6770fad4988ca01494c339daf83ab30d0062c3ee9ae122ce4b3a8e963610d9b58f8066ddb3161896a54769b6d35cdd805b39a19095ef2d6b0736529d9c11c038e78c97610db3ef1111cdc227b1d42d77c567907ebc6c5f7c14c0afc23309b05f2a70966a7d430fdb8288052fbed6a3a2dc42f61d0346ba773b9cbd9923124c03936b37c169ca1c26513674ffded05ee6e4dc189d27f549f07d6b720b03750270b8f2308620c090093a6b7e248145688790568866664f326ccb4bb8343910b4e6e6101dca1a0fd63c2da4e82103cfb0b68ac3ba52d4bf013f78c174ced94cbf736c4cb38cbe09442b6eaf8633b3e80b2afea4390dd2d64f080fd1f63f768a06dcd7c7da2200ed05ebb5a70152103a0bc2236a6e0ae6aa156e78bdb1c906a0baf8691d820eff7a40d505854e60391cc0fa14fcaaf4acf5b8ca2e54c6fa601e01e2d1f3a9087d74f34cdf2fc3cfa0055dad28ba6aa13687fcb17d349675a09c0a56e5930ebe0b257c4ae8e65c4d07fc96208d8f1d84cdc0594103fa8bdfd8890fd6c766b801fa77d2ad221684f7db8b6e91c7c97134b781c9f28ee8814e46b88f942dd2b9ea8f12360b8589bb64ffcb831887b2ee9b684d0ec04dcdf065eaa22e1b775a7b1dc9e8cc9fb85997ef3023908e04be27d3244c14b496d244f43f38aba9c9c63c5b226c2c5d09091cee28ab7df44b91c55bdd1ebda572acbf63deb90215d94012bd8a889a6e67e2a8618818a7edb63c2bc859c52cc97ed4de41325800b944cf466ba198fc5d3d915a33d8529b555bb63ea9aec53b6782d4da26a5a54432a46696c38e1881984d1f940b27cd5ee40230200bd3e51faa397ee5ac641040dfa57a318bd35a913273a2123865ac93bf4b3dbb527d72d5e0ca21f140ccab1fe186e059cce11b13ff25e380f7f66f683e9067e456ee1bd1ec72666580b382c8dc98e1e9d4c73a1183ab0f84983fdf89ba6150c6b5dcaa62022389642b2180bc9ad0541e085772508f92507a341ce9ba0cd817ec047f4eda708b2863a42ca6078cd794f357298b28d124ed8d006645257a5e748d00783356e42da7ea335169a2238188600b9eb6cf37f64161afb92eea9c5c079d4fff803405374a97003a767fb4425410249a08b222246875bca9ba07ea8fa55885c3282fcba065a646166cbd9f08bc5e9d6d26031a9b6b14da394bf64d5c83bce2c42afe0f73a8071206c188dda662a31cab7bd11017c3bf2c61e1fe9c006ec05cc1e51fd92ee5eed0881432e7f60a2329c249c961f4da3853dccde7effb4bfb2c1e250cf17e7e84c62f88ec8f3246e2e7f737d8791800f9a3b0ba11ecea31edc2af41aed7ab45c2b0ce5157730428395171977b4df04ba081ebdc1f0cc94caf9a3720633089933a05e9d0f42ec3c4db919a6e8d817c709eed9e6fd1186162da17f937923c68ac030672e4f75d0900b275b37740e34af480506e33f69cdd86e3b0e12fdce42aa56fd00acb955822b71e0898a52983b09c1a925bae5c0df1b3592be23104c00c326b0fa0c03c8c1e4ffc7a69896d7ac652e68106628212902f114ed01fd054daeb5220e53a4325e40a8097357d28108640f881faaaf02fa54b9c409e3ce6603c4e677029899397dd2d057bc76e49012c7647223576ca0f0fb2dc31578c04c79d11c05d73f5150be7dbe9cef7d8bfca8c753bd5fb3e872a3df1b70a7f39fd0602161ffbe7f9c6e825991a8cc0419dc715e1c7af1f49a5113a3a788dae3394547860fb2b01b300ac7fd16ed5a6e734d7e10493393fb81981f07c6789746e4418d970d0a2b1a769184a4af973af9e0ffa001f10bf5cbfee17a631788e69df1a49d9487deb1c634de3f3f275359a81a70b3da9f7dd9db1779428583d033d8881d4ce5dd44a1131f21654c0eb3887f7caf473080c130d8fc649816fa5f3084384e74673aea3cbce65610918cc3e01c387c3d7d095c799eeff42f120d47f203ddabc9416d374061887fb5e5ee53de20e6581da41405226f1dcbf5e42550b588a5bbcbb32796beb0a3aa9e0902b662efbdc76f4bf3ceac121d06d4c20181652c269e5f198fd4e5424e34c98e859112880716ef54c55b22334d6a5da999b76a3b986904f976f56bd7e42259f59ab35110be676906aa5de3d57ba951fce140e8a81ad33081787aa53d9f374853fb9c6c514cfa5b95dfbf2ab9a4b476756f554891d1c0234ca524eed34b20d4cba0316a0584f624267ca69ec60ff25658824bec92fbe3fcff843d168b395b46cb159106060dc8a345e202ed850e3bf7c142a2ee1cf03088f4ce4395d29068631456e625f19348fc5d84ee136a633702a0e7000899b86634de6b35801c26d0b4a97e7a285c9290a9e0b8f156c85429ca077a19b78aa9dff3ef366c8d2d33caf5ae52306888facac242c5bf32d91dd15a36cf5f5ed3e8c571820ab5a0aeb430c32c0e9f1b96344998295b73f9431e9362b6062d571f375260a1a4a41093eb0c65357011a35065e4ed5509c433bd6fdf3fe773afb631e8cb52dcdf42bcac75553e51bb38ee4cf24c2b9d366bac08d38b3027f9ce2f1b887ca1a163559e42d31c62cca44aae5290bf14425ebc58599ddd609a5efe9140d10452b392bd5796ca5a563ac7fe33aa8adf122d717a2c5fb464dc57b8d5e15e2dc998cab1235bf9d84c9e77ee7add7102ae37feeaeb729ba97c05ef151d5d4191537b29aa55ffcb969ac0689b6fb44eb1165f54c9409ac993ac2407ada4acd8a81c063454bf15611e05ead2b53517e2c89d046a50e47f5c257ef7eaa675880305500126702b7c56c8a4094d4aaf992c5021c82393213161c47f18066c2bad24f82390a251fad4a95eea17163251297471f6c03b3d1c103a8f140e3f79fa90c624571a85f64397e186e4fc5cd8d7a9cf8a892cedbc3ce316e382e2d0803387f60078498c5f6c1ddbf78a4bf1b173ca77d0628415d9e688dca9bdc28061ad15535d0c4570f0722ec462c067d0cbbdf61dd0b96fcd88a63b2922b837c430277f2a363584a9f853503f605168bf10e9c03238890253c237ad9c83217ba2f906e0fd183e958a08f4871ea6dd8bcb731436e1a40444785c99f2634795832a9709b6704431d6dfcf2f8ab656017a6d1af019e6d0562b98cb0c99ec96eccac29e4d1f7b4fc051fe707095707afe8e0293236f2a3b5cae40166fcdd90fbb5e15951ca0f5febcd3f43938186d9a64223ec1105a582baea61add1fbe8ab93ccf6948394f6781fd91c1800973218b17bc9c1a7a8459a103ee01ca1de4b2cdc430ee8b01c0dcfdd0f56cbe869bbe3c0dc0995218ad3cf4a8697f4225b94f643acf9330e549a35b626fab16d3689b161621418d83244cd9f7c79e248c14aa7f54273cd0325fa6fd0a582854de9639eb226008ea6ba11a7a98e1e5fb8a0e4a41749f837887bb9e653cd3879f2ce2c74947311bf87ac60ae19428c4bb69cf2c50c125cbe08fadce882e8be501a2fbb46a4ab90c485b805f20629038e924d2eeb05241f0cff12a79b768f11d28cff1e1b2df1ea3decec28dbd3edef12f87d42667ce1e38fb35c2ed24c466f37fc2536c5ddb9cc662a29c992b12b7ce270b300a38eb6dcf0d883339991fd70ba59541c206d18306f7c790402cd6f23787f958ba44bd1992c8ae0ba7a7bad8bc73c20591d954f3d36e3078017e2084df1e0711becd652cea305d0c7da6ffb09109d009982f81b827ff596adb06f0de45106957c03c0c80228627600fcd70f9cde41abaaea328c4ae2457e69fec171a8b42f86c2db171d3cd5faa63d26a1158a44c96a0beddb3cacd3d737029eeb4bf76a2c2a6d600a81fd32125eae83df4db4e674b5375184161dccfc71b09ab0de39397698ef3d94d3b5c099c5da211b21afb69a726b20a128b0cd7212ba0796737c2be3852cb06ae1ffc8688b9403865df6539eb4f3eeb433b952d41be67e211f686bf7e27b41766b96944a5e49de727b2b660213e74ef9d78e681cff1c6f4dc2e3688f42168421f2e060f640007657ff4599428a4a48ac0b21b9cbb251946c972b760da6750c4a879ba0da64838123d43417d634cb465b45cef8e636fe3abfcac9522b46ba6897a91b3b7249e485467779a243470143048fedc667e2257aa00384afb7129f4b223b8c5fdd1d2266f015ba9b60e9612a14d5488c67e696133d02d7c695cbec20a90cb94f01b3a9c9bcbce23e2d08dcbc06466ed8c0b6aa0098364030ad9fc612d53bfd652f4eb69af8f682f3f85cb62cc1a1734c10d46927a7a32b66eedee4efe9abbf5154653ef18f536721228a6100590f85800bcbebc38c561177e0167c15350c702d1022e5e8db926e4a87dda626ff4ca3842da7beebc91527187c3b85300cb318272f79030c751ea17603700f8a68e2a2a91581025206f5f4acefe601b3a46463ae405e4ac05f0f0775fa1e68459b2e1641c1292b3345206f18314439407de20c94af47241691758e36f9c1dc0cccbcd628c2c43d9fcc6e9b18726ff23960e04c0962a047477aea4a0383bd8750ab377df2cfb6e3060c55c09f98a1cbe36c20102f06f3b0c432f809c4d0d3adb730346f1bc2d0c5a1a80bf50b1d1f2e74633fa76791e1418b363c1b2f013727ef83790ff02e7fd046cabb36b778171822a007f2d66d99842036f7d0357ad265aa4efbffbb80527529f5c4a3489a2f0211a886faa51174e5b62acdb4956dd8df136c3317c023c62fc4ad4782c0fcdd79b5b94d469fe1cfcc59182350e06cc06a4b901be747cc7004b1d6ce6d5211cd7f9a2a0e8391dddeb699e909d4f06284f37e00c9f91adeb4c0ad840c6d267c78097cebb7e425687fb6df50aa5bfe1e49ef0891648d9cdfefe05632e3348fcf0b9898616686604d3ca866db59c0c7d800a1934dd792370bc42ddf6e65a4ed74179f2fa01643f8b346aaaac593ba2d68feaa28f350b57198269f58904b330bcc5b94177849c279c2dcdf1bb62c4503c9cbf1273f99c7c6091db76962e138ad0c69e71a2c151281a77f7f5c05f34f67a8b1b46c062799897a69c02e67533581e8f3f00a69d9b7d218d77a2dd3f000422036509f18222a2bb14289848a0d6a6d907395974dff8918a689b80e43af63030579c395ca0822d190609178aae698e073299dcd636936bb20474361681c1eb49007f3bbeee80c714015f4d4b2bf9596746674f156c00199c5d7ecb81fa430ffcccb9529ad89931e2420c12d99f918261f6b9e9759a36c5589574ecf1975658924ed3bdeb8c39e0ede258c3bf4512234b4dfce6cb3c10f4ab8516e64be25141922db0de50ee5a083cda5f16a19939a30c129a1c50c9817552474c80f293c8cb5f79f1360853b3b1e27a4447522632ac452ab4c4a568a9dffef94946ee5d3f793284d574d63f9bed5ecbffcd02f6aafbbad798a256f09092bff78ea3ded1f133ba21628874c2fee7a01fcfbad1d2d73904b79254090944c45281571823878ba5768e81ffafe269f531228ad7ee92d1400da5b2fc97e49ac24996ac0875c1c89a89c3c1f6754c4b6c4a88d674ce3318efec8daf4bb06f5e5831e20dd52bfba76ec3a211f6cead43d5f963496d5b906973d17cbc75721d85717c4b454ea16afb3963a06cb9c128efecf280c964600c115f7cec416337e288eb1f635ffa125273dbbcf476aeb3222d1d614e739a1853dd96c97a1b378768dd9799c491a9858b26f74b9eca54fc017463911dc8c01dec229e0fecd77fd88fbae8a40fb960122da832cacf7ff8a22cb5f9ddaa282a199f47529c7b58d6f18d67c23da78ddb71b8232e5f2ad2c5301c3639eae0734f6d6cbb280a564756c9c835a689b803c349cfe335931bc4fec266375be3b5111b90c125ca6f93a56b672a992593b172f91b8fb5a614039d27b009a94555e9b8e8a601f64c0445026ff2353e9033596e6ee55d3b3cdd969bf6ba753adbf4bdab3dffdeb22a0f4dc35f3f18c0cf4e5ac3814b2a8d3ba40807a93533af0350e18290ffcd8709867a1c7f6b1ad8a5511c791e853d6be2f6c6e2ee4b1290ee0a3e49cb004189222ccd01ae9b48bc904ea8e3ea4ad925058b0448ee8991c52e5969dd13d6143b7046f4e0cfd286fa800cad2ced74259b5b24455d08a181d464fa1039979302ef06ab8c09fc351abb931435a353aacf08e0bc9dd5b1e5c7490e6a3970d44fc5f14b1d85aecae3a18b70cf960038dd0ff0a6a4b75cb215fc7124b75f2ef001792e22eb3ad1abb90499977fc38ef3aab22ebfc6cdf45c7e70b1fd09677dbd0c636b76cf324a33aba15494c67561160ca87e06af0e1ee126d09c0f5daff7738a7e36e386e7fe5b7a45ee38d6dde044ed6fb04ec7c348ec2153fa46795a3efbaf2e78c5c4f812c0e2b4c394df3c5c7ae28df7b1eb243bf1b79f115a19efb57961a2708d6125f8947feec56772b431edc29b1f9256a0f3418689e7ef5b0f3f6217e349785342f862640fe244529480ae5e36403f2629ab7347b2c54b89271b646aff9d58a548cb372722dd7a22e43b8b25c8d733f5456f03fc21bd7338604d5fd0808ef0ff4d27978b89bcb4191ba6bc642429ffc907e33b3d56991a3a8223de2e6962d49244afccb6d71ff3a6fb7d4b7ab9a794fef1de672bd19126b7000742402cdf61adf1bb3f1fa997095595af21a1a1638cff356f7be58c618136ecf6903f5b5738a89ffd9fb2bfe3ee459be8746e472d8ff6ba53ce60484bbf7f39502b33930963f0620b416c5b0f6a2c3dc84e4b58f4f30796b95ae828b74946e6481723375c959c7866083f93f609deb65b86d0f56cffab2c2863dc9627636fccd2adda66f27eda95186c531b334f33fe4f720665fc0c879767b7bde079c3506f8150aea0c3a9fab35ea17a93bedeb74756f39b57c12f8856c49f47b763d3ecd162b6adbbf492be2cff200f00824d3e4a21fc6b2110d7f6120f0ae5bf3736bea08b5b3e099b41ca38dfecd6a6aa06e56dd6912f453854fa2af0f5533fddedce8a41edd342b6d452c342b47025c311e37f536d203b782fa70762be0f34de7db60bd77627f342d69df3e51dd1d5c1c229123fa9362d265577bbdb51b0498fe64e778e55c7193821e020a8bd6a2a4ddadda5c956c9cd49da019925d4d72be0163327cf6a7357f7c259df0100652df557dd251fb101c063c7a1fee811a6f0f81ddb9d73ecdf946f2a4a06e4ae27dab4581afff82c9e824d349e2ab2d672381f7bd0ea5c5ccb48e1b7cbefa453012673a2b754c189e4a0b57cb9c9ee990efb6a07add52090816c224f972540f2d5cccfb3ec3247eb405be91613b778789e803f9e5fe8038a11823d47368793e73561a0ac5ddbd3bd8e6c904c3c62f60e7355d818fa8c4f1d90305153b01a020b6ae207b22ce5f75c94d8eaa0e4626a15ed6f4fcb267a63188975dde99fe6c91905551dd3df932012d45226806c85345039572a3a14deb21d74469bbdeacac3850ee4057b4ae4ce7b3733d02398e63571be8b3c8fed7b8279f7e472bf9e4cb2c68cb170ed65c24584f1fd4e3ef68be9d4e6a4c9f5028ce9d42bf7654f2214c5bb3ceb2893b5f5cc3d95ac80ac94a154622a23b215f53f7fff2a493764b2aff6810bef4f7740e200da3e9457ce50daa062cc69044f2a3cf7d39dcb1e27825145d3ab0d204211e46008104155fdac3d9883d7d21f4f1c8ad679259a7ecc761266efae5bdd79851d5284d9158395654e7f343227d1cc0fc08649904a9b16a378319fcc92c9fe7cd8e73c2d7f141d7d529218291e7268ac47460183aa47824c1451c07d4b67c85dc71375f169f4ee7951388e4080d6ebeccf9e60668869223e1f3721bce8be9a4ce05b26f04d85219de49d50345034e36bc26bc92c589e3d7a90a3a44c8cb4c0353035c8cc495fa4d6e49f0e2ec99d58dbbeaf38954fd4d290d535c85815ddac628cbc73cc58bbb8dde57f02bfd239fa012a35eaf1f9ea75b33162c5ce709cd5df098a358da40c139958cd9dde2d21a9652d4031502f57cc77470b69d1a37728068f7f496a8f13391880255d227da3d651bf607832bc6edc51f9e0f2e158befc535507a1f4a344fc263d39b1ab1866d6b51041d204aca687f6405e74993d0cce9074f1abdd03e7b5e7edea47ce3c6b9a3bd6322c0a0e2dcb576db974eb4f7fbd7d9de458d1a700b8b884aae72f85f834a3e58776ef68710b2bf8a6348a505b6cdfb6c1bb3a5648a5182592b4fc007690d0bff4b4dcd8f0ff446c8a7a6ae1c5785bcbf5eb3f6cd98e4d4735adb8587254bb27e579c3f43b2699d2f899bbbf12bfd9132c987c16b387d46b7555181a2a6e3b90ac15dda957506211c2fcf1fd126ef1fd201c0a7fa4e3d8632a8e6b6a1d7fddceb6c850a6969e741f219cf2b6ac039d6064fc2112ea2877ee6a3168aae3040450939dc30312a1adc1a8861bd6bf67b065e349dee42ac168a5bf0fc60269fce948030c52bd780c12a51e0b80f8039cc632760f9bd4fe78c621d368e698201e6ac78cce343a21d72bd6b2b6c77f578b8b2536387240009d03721f8dd3ab769f7e705cfe3d7e8cf45d26661511e94e136f78a49c6d770ade19ec6b731428934f5b9a470fe33350f532632b1d348c779509be040a5bfbe891719fc45a785e10858b7486e31f80bdaaef38ee4db49969031f0e589681087442f7089a58de9fa0caf7c8fdd9758290e83fbe02201027a8f9049cd00c9239892b3929481730b2b047158b828ed8a98dad312578feb5a07c4fa22abf9a9db0c16ed00248c21305acc1170a881726abd547b659d4e487f30925a2e599de1543f6800be06b945aa8065c0d50dfc741bead959434294adaf0732dd9a0e9f63ae91b48330167bda92e5c4afd8b65128adba645092a3c0c38190a5dbd3c0b0e91771bb765cb771c8e2f84f3e6701517809c029f9ee2732d6cf0c3423440120c9dbe1660043ba60ec08cfa6c6b4b252a538e5e1ad0c47c2588f5645182720393de43173c707e14a5b5b0c2500e2cf0589ae06b6fae951bd3c4ab903aab3ef05064d02ae0108663dabcab291b37cf3acc1b5019f825ca60130c1c70f0e274f5f034952690bf55de59eb555817bcceac44a2a8c6533505e7794bc0fbd6abca5f52c40c4192181c8de859c3e602fcc00f4f7cb6dcb79a1189901db39f80e1b73171f4b76b1377d6cf11b081b2e5b78fd70c75d829a0ecbf51e22cafcff15ecb94bab911a55d6e7e30cebebb78867456fa14b66b1818637d49b8f80a2cfc975baffb053f966338a4dcf21d52e1eeb6262daaaa5ad6dbb26cfc87ec448f5a39bc9fa5f58f89830884f7148528c0735718b131267c46286d4e920eece559df068e48a6d1e2b9b0033600408655e10e48f2144409a8b7912e0785575496276baced7c5ca0e7b5853178531601b9b2394d823dffc65a031c6fbf71d6025e8aa086787f28fd99232e5d4826c8901f91b11fa5bc7d124419884f3033a73ffb253106858ab0ee21e76e34900ca0c8344262afa1fa535ce36665e6890decf4bd57990cb44612dff3c0717fe80a16527b24ca4e1bb500657a403674e3c8289e235cc5e948386b4611d64b5eddefe9772ef2d539201000b230be70afd0a612728e6cda84cb7c2e05703058d10342a2a63cea84c0d2cc7ccac4dc8de07d139c78cd27c649d9d6ece6aadb576b8c94eeebaaebbf1393e9966e103237227d35a17ab4ceabaaeeb6e94b5761e941eac1f334a73a3bf5b7e938573cc481f5c6bad3a5a37ca1cb3fa37cfabb5ae93bb2770f739626e7aa1b824da749e87608e6192a4f9a44933c5c86e5e21fb9b5fd01afcfb5551664e6dc9b96962a14dfe26ce24a34a76f3267e640e5a8e590e9c197cdf5d77d7268e9bdc9c7db7dbcd834d1c3f8d019fe6cc9c9904802287571200f6b71c54b879daa8e5b0d9a855d48df7b761aba88a838800785dc9b8755d0393eb19af1f198d0aa3f0fa096e8a3dcffbbeaeebba4a826654a60851346ae4459fbca24630391b35d00834aa5514082607b251cbfe222826037cfeadfeada23cd581dda0d7e1083b92e65abbaeebba4a562e70f79de7791e284669bcb3c29e7c826222f8608ed0437ecedfc0f2a79834cf8333371bc7dc828ab4d62a0618c2fef66325df3200b4213a4afb58379b9b217a8dd48d32c7ab9e0210856e0b7f7bdda88cff570680d689013ecdd92dfbf768f550e2a6779a337366cedacb31cbeee940e115e327d0c297a35854b1e1ebbacf85cabc0ca0fc724ebc78b8c90931e4979b3ff4f8aaf0f09437355a6b1689e451e12bebdf13e79b03e6b3d1277f5a29bef7defb75dd07764004d127ff8e013583f74d7afebeeffb0e86bf076dd360006a0a4915e5f194fde7279fa4fcf9e493c7f72de2f08311af5a6195c95963aa1ca54183a23eefe1dfa7f4a6a23ee587063df98bb63cad10372c293dddf2b445941607b4f6e5fa95cbd7f6ab5ffdbad6b37d52bcee757d6506521ce757bdfb1e39a4d65abd6d014cb7ebd1330568d48f6fd3f53ccff33e18ec50c4608797eca74322408a990374c686ec5bb889d669e19a4f5abba134e28502b662f02e14f8a439b7e30101f0e1cd097cb8e99aeb4345f93841a37edcdb35a69eb9a9272145462004d7023a5351679ffcd3c8fee7007e3801dbef7cb85760cf83dfbd7def5e0ff4e46e300ca968d3b7f30bfa7edadeaebbf69385ebdfd87fa7e07cbf7ed7c0b4e9d39e91dc7beffbf05b8046fde893bf8fda35f7fd7b74cd2d39f0a351b41dc5439fdccb2c64ef1181215d160377dea50252dc612cdadc80427bd3c29e4f5aebdc009f34c78712fc40a488114785cf053e694e96b949859b9ea73934c70710f484a793e4755fe75a7eb3e702a4384e008c625f141f6c14eb9a0ff40cab9e628c98328ac518d1a826a24ffeadaaa70f744dd7d472f4059d21a251b447594c195981e27c748451ec8be283d50f74232946b10f744d097a26565130b83a92a2513064a053b23f084386467d51340cae045d53dfffa3754d2d63948822a61543834acfa8314b64a7d12d7c6024c5280676b79b483fb4aa57cdeab8b0aa08113775dd5ef6b7aa2fe7c61411e3ea181695a931af1d94e6f3a2f66dd1333fd4d30a3ae615e344ccabbde813adb55652cc22eeee063bdc82957d6473f38c794d8981b979c6b062626e9e652e86e566c9c788e626bda1e626d5428b5bf68f59e266bb46b4114d897c8666370bb87b90825fd85182bd7cc6b09460cf670ceb8357d0a8cf8beefd635e5dd395232be88c173d92e59111fae4efe7c809a32250140c183af91cc560701e392a0295995199d897cf512cfbf7831eee3ccdf7e9dbffbe4302d9817613cb95bfb250e2cbf96a61b9eada579e170758fcb02c797f13a466d3ef61b18c79ad3a55cccbcd920e1eed81c400b57578d5c43bc2fbbceff33e9b0310748ddc791a25b7888398bb84c17901770f838b79799e11cde38d68231af646b4116d44fb609e487aa90e79c338e27ee776bcebbaae1bd1aa64ff6f8aa3625862105d02010355f61e885e438a9bf6a392fd3b237b0caba2625e2c96cd6dc2034d851ff3eabc773a2f929d4856ec2e929eaa895fadd3e9fc29961fb00c43ef45d24b7d6ab5502753d101ad46be4bb2a73c75e46b611515f253bf7fb1ec62d7dddd99fc70c8c0548b9a92ecf96cd5f9e17cb62d6e6e9e14975b6eb64bc572936ad1459556e9e0131dd0990735666eca3f509aee5bd52a24d97a9e66fbfe38be2bbddc239a18d9bfdac836aa89397859879b4c2652a6593451056bbf477794cf116d94cf98574709eefe8c6165a753320d9ddc3b9208430709dc5fbf2ddcecb2c4a3078faebbee7e74d75dadfd9d6bd7116d24c6c836aac5bc6260314bea9f31af4ff7e1b859cbce4d4d65ea83a05a6b1d45811cd9c41f62ee72441bd1b23fe841c15f2d4a4b8dd2840f8373b3549fe6ae5debaa497b6238bf1a4cce6bf06f12860a0ecec0de855c9fa668eafc6ab9e60f0a45c5bceac9939013fbc6c81e03fb227453f6a87cb5954cb350c296c54c6b5d40913b48e0d303a384f309286e9643dfa4cd1e15fcf8cbf48a2eb2c85fa6352d6ef93bc2fbbe289fcd6bf0efe00077400c2013a9dfb9451dbc5ccb3070f71ef9d5a88c7fccaba23e28f4e439491823f6052cfff06c5554c6df61706060981c0c956f0bb8fb1306b7526bad252ee615f3cafee12b5cf2d5c2b2c9f9d5be5a25bd9a17850240e5711a333eb7dcb23f68ea19223ed54744d75c3aa3baa7c90960fa88f854dde1c119ecf7b5a59ffe8ce88cffcbc85a6bed83b57e3c28f4637d495a54948642c93ea4a57fbce04932554b7137ee0735c8dfa7fa84e5794193c7943bff2157ae92227fe427feb543f07ffebc23899eece943527a6ad1e676df64e73f9288972bee58225eae6f451d6eacb1c61a1e4562adb5d6cb99c05a14373f2c964dce4ff5a9eeb5e4d772037d250b37cad0cabb11268a72223aa43dd37fe6b8b92944666a468bfae6a6abdcd46650d397a0a6ce71d35ddeb88af297af7aa6a7641747b159467f59a30f6ed593cbcd2bf325a879a7a0a6d7793f7f7ef2e9f7baa66022ffa04f7e95b8f7d3695d21dcd67585c8bb425e1f50192fafaa9e688ee333373971937b0cafbca3caaa970e8b447a298fbcbc949e44ba98742f8bca6a3219e985fc7a7a92ac3f49e4e052feb75551201552fe5d3eef4ba4baac8aea947f3eafaa33d879e1eef40097828c468f2445224b2fa511163152a44792eab42acaefc5248ff1207798ce1da8d1d753c863a0771bd96902af3ccb4ff19b4bb9ef3637b9477193bfd7440774087e4803b7c0a15db4e9fc85d2883a7ce2c82fa26ef4674ca6a33f6fb8501b464f7a1f2e623114023b04c3b0a435aec241cdd08fdee5e9932a8af4a2f1298eec42a211fdf99da2a3f7e1247245fa977755d75c59cff8e30eecbe7613f00b39040c54a997279143f41aa9970747a4a7442ee40b590a43aa547a212e2e2e0deb18ac673deb978b6744145c6a54b68bc545737d961fdf8793344522690a04aba843e7fae08a0bae9344a7d549a20a17dc95b9e030c638e4e2d249a2d3eac03a4b3a2f37755cd995f87057f90ac75775c6faaef4959bdc59ae7255e9ac0e156ef2efd0c02a37295e752ad56d791246e4ab0a954f8c5c6e2d5d702eb84eabb5f29224bb0b2ef42fabec4f5538172a2e3917975c762a6e7217dc0ac51863dc781c712712596a682554b1b8ca555dd3517554ae12f910ae6a4acdd09ffd2e39172a2eb89c9b6d8afeecd2a5962e545c702e3937690756bbae8a422f1f0c8a480f85429f18fa5048f454b4f9fc489672183ffcb03cc31f1f491ee155135158765ce1e83f64d951d2050e61eaa7b01c3dcd2372d5e41c653c821c7e9321b2049d701c65fc540a3eb842763187510ea3904752cceee28dcb8748ca4514949045b55afdb36b388744221cae7495a5d68e589fd4919d4cddac2492a646244d89489a3f18ec48cfa01892481731e8e195ab3c9649efb18a2af58f9ea6bcd46529022f0ff3424a3f2a8f8cbef430e5912641465f2afd11987f295f5c489a1a42fa700b272a58c98aaba25e29ff95d7ca1237616e7613d97f8549adb9af48c59d0108f92b3d2eb0fbf0c3870f5df3019bf381aea1818629a030d0a033202ea4d185e13b3f3e268d25a80c7af2ff80e1a2b1d23a578438575aa01c6b2b2649f554df435a1a84dc340d5dd3a341b62aa451a03070b5d682c268d44a120db255d44aab9eac2d4168ac2401c2adb42a0ac4056b41694c01954165723960c0f74f508e07051731e2a6fd558ed2502e7cdc00c46b1003ee46923b24898409c1ef9ff9b0dff9ec7cff6b1111879b9e554b8b3fe5aa6f3e60926b7976ab48ada20eddf75f930e53f707963e00017f6e56c75e5bc85f797611d51331e45a0ef1d1b670f3a3399a2b92bd28772576fde5074db8dabb4db8ac5d92f33ccf63626ff7d1348a3411adb2b70811aa225dd34a7ac6bb09254aec92f1b6923bda256311961c5dee3688fedc91472d25adb9dd7ba17f82659c420922110e4b3952e1277fd06d679cb590236db491e3196315462a64ff110aade1663b5a21fb8f5fd0192fa99fce714af6677d8a548e52fce43fdac66ea25ddd44bb465837d1a8310ad5d8f2f0de7bbb91d535cde70b2a4383467dc0e895957c8e2c8fc77a9e2706a6b56bf5c4057c8e3257e2a6bed553101070d60f44dce46f33d202e3a8f71deb6bed553513262a26459a89af0ca11271b0d93e66b9d95f4d92290dfeeec5f3199540512da207bfe3fd0b39e68cb65186936026dcf429cf11e7295b4ab03c472b4458a73cc72864ffb1149b90fddbf562c2fed9a3ec7add9efd6a07ea25da354631c256da354631c28a8cb08afac8c6281af5a922f7c9f97c41658a74cdfd5b7ecaa03346dc321834f73123fb7faee779dec7e663c0a17c7e6c1f1ba5717b9f16e5cec629a24c4af6ef0881ebdb5384b1b06026bc9510b342944561e679211ec208abe22363193f9f11d635adea99d18856f5bdf75a5294416144a33e61f4c95bd5aeaeb9a5a884305a8c657f21dc565d718a2afe106163c0fda708f33ad97ff4cee053c0d77e93a20c52fc75dd6924db26bfb2d4dfe5fe92acd4653be99814f8f3cd340b1d4c01652adadcac70f9e498156b593bb97f762c4665daf32c4bc8c2aa186516d63523ac6764f55445a33e558c300b039bf4ecde8db05108fdb998544bdd8f3316b2d49537624c64e2a68f2d5790af742c5ea845b8a5330a491d176be38e01775a7a0c18440f8041018421110b0bcb9f37c81df5946af289d0b390ac9b0a953c3edfd154edbc1af8ec18155cce1429b610607a85ad4aae2fdaacfc87b43919f4c20f79765e6169697921a167097d4ba8ec5b487c9a12ff9e017bdec3a477f911f632c644a297f2080bcb91d08b443e5a4820223a9b75d5abb575fb22e65ebd989be7bb46276e823eecc8275a5e44b23ec5f22196677daa6505770538778e9bfc33c56b214716f247879ee56537abd0b3bc8b3f6c4efe8ca0af1bdf87af90a24da491b49ec21f59152582caf3e2925596a21437d10ff4a9e2262781f71f2a6e2a3fb74f0e74ff1c65de4873f31c61d9436e2a79e58f59155be4bbf2db173637e9155fccfa06aa999bf40a3054385b8c523c8994ff38c54d8aa3e2a855f8b2e920fb7bf68eb091899bcd83ecef8d2a5284893137b9c864d5e4f6878a9b3747c54dfec175e4832edadcb271f77f726efa8c42501a6afd92f69bfcbebefd8f1437d5f2732bc7d94805cd5e1798057fa8f8c9ff93534fa3103eea87a45924cfce39c51a891c6f53723e677c709fdca78a9ba3aa6b5c95fd8a36de3852b106f6883a26f394d44db66d1d7395af3ad6b18e75ac631deb58c7328b181399743e7543361deb58df902d7b0adc1726f44ae158c4588391456992388ac5479a92eccf32ca6e8cb58ab23ece2a2af47636e658d878c3570d6c125fb68a2af59328cb8f5e08cb8f5a7ed4528a4744cfd2e529decef091b1fc47e6f299b53ce9858cdea53c12fad193ca234d82847e34fa23a26f69799ac2a47af2f7c6c09f85ab08e248850c0b9866d187feb4309b8dbd59893e348eb4514456c1a7859da30c07591ab8746d797e6c261d6e72d328cbb959c55138899ffca548c9a708f37394d129679c41850aad42abf46a95a493e453848d3299f8a3e62e31cb4d1fdb3503fedc3e52dcd42f37cf66925d22131db3301612263171f3de082d39bac622be91a5ea9a51093a53451e69302e1183ec7fd1c034b37ce8bd8f5514a94584893052392ac26d2945e553d146845d91cf8edd00a48623b4000847d65804458d4a185918ca281ba9b821a334f449f6bf66e0ef45df2a376f0e9fa36ccc5554e8fd31132e373b252cdc1a6d15e53fe654144db1fc8955b7ece34b56511f5badcd48321c8bc1eec5a32d471995b102a629776225061c23dc52e7c7e66d018fb25b9668e7269f227732ad3d4903081a051475f8c016a9a883973f5084519a514665fcfd4528286a54023df9934820d02813619e4755b37ae5a6efc53bca6e395231cebc06ff4b06fcfd39ca74f0fe3e8823bc63c05f3e4796f7a51f4a3a34925c9e80e266b942a0fe2d4b37cca1fb7e909883f75dae883a74ef8d325acb46597f6c152542414ffe62116c4ac8c9a215d9df26c246192cfb8b58a2246e5a0b1b65a2b2c939ca465977aca29a9e5ad6f42461b9df32e93f2d0c8749c70e1ef5d4cf4276fe220179096c00521c2785428994e3db23e57852d7d0d033fe3ceaa95136340d60d3d035202b6c001941cc9e6b15056ad593b36490cb0a5bf60743f6f781a334b747113d58dd835ae00a7bdfb9f4e35bc920d8e487f43e1cfc889d3fa4e752f8fd313660d2a0170a18e66db46c2871f3e5fd6db8dc74797f0f0decf22fcfe3e5a68df7e7b18407aca2fe5149b23f8ae5a6888291d593b7bcbf49c3a15639556e546c67d4a6d0a464879945c9fee2f710829a2d60a0267f92371240f6efa16aa1925b7261e7c2a3f742989c8d560dfdc963c7c706f931f29d902ffbd9e3b889f624f26b6e8a42becd4d7e8389ec516cd85a1565438a9f94f048b2e3c6aaca53c97e868d29d9ff63a3956b16cb1b3660646e72cff3c812803398998d58c250d1b1013e6164366415057ad59383649966c51756b9cba79973d3cdd3462bbb59b3d172f3fc9b8d286ef61299662bbc30c58ce2e66c07366a0ea8396cc9651fc13e567fdfdfadfffe09ce728396f00077f64046c0d48a325af904c54272258301a0519a4b693adfe2a234df836294c63ea805c201122d4a03520272b9e9716782825f77ff37fec63f92d489738f1bffac9b32f1f8585d524a71fd15acba1eee489f63667a32a485b527c318ada7f0499aba41d2542709febff15fdab0d9a8d553457d17778e28d09a15dcac45f7fdc87b50aca6abffe47bfd37fec68d3f6f645a7eac185557d29e81c9e52aeab6e4404fbfc070085e84b051ed4bdde82660300c61232f46b591175df3dde84c098a1f734665305e61efcd5a2d0753f269dacc59ebae8ca8164028f82483347a22b988c2ce0a7760ba1c33130b736662d152ccba6654a3335e51a0588e59d7755d775dd789b5763132b061a3d641e8978d5ad780b4efd64168d447c6e80685ca744d37069df1a72d05909685ec4f04c873ebc01cb36e50aca2480f8ab9a73c71eeac7008ea404e40b21ae0ef91c4cd7e2f47f607d94035310a0df019c242989b9dbb94fbc31befc3f1aa49d7a7220f9dba513e89ab806b793dfd0261e7f73df87d5ebdafa01848e6662dfd9a337a225bd4e146ae0f8a819c8064a168d320280692d5af0c63bdc44da0582701836420276ef224e033c71162a018ce540b2c8ed0a143070108308001e4c8b10202bd07047c02d280748d8d1648ab28981cf8a451306ad868519a5933ad107f88fd365a5d6342a1336a342d57947985398523b2839d152e75b2984f7366cebacc317353113d84a0e6c77293eaab418f550d14d9bf064669ea7f3442647f1a15a5f13e88366794a65b726eaa01355bd2e8978d5a083b41b1ef5b722d6904b12a8a65ff6e1d4812f41af92b4b3978df3d0ede77bf82cedddb4792ef77e40de54255442a376fe9725387a4399413d9b263420a1c969d2b9b0906cb527d24b9b66ae5a6dcaa98185c90219fb7fb3e8f5887803b458cb8c93fd7c55c75d664685c93a1436ae459d7f48dce8472f4d64e989101d725e958fe39ecf3bcc3b2adb6ced3e9dadd3d767675aeced531e166f79fcdeb4051f43c51f43c8ff45717b158ee7c76b6999b1dcccd533413e1b8d93d70f3e526ed22e662d22dc9fea118de20e0b3737559fadea12842f6efae0f37c1a84c07ab9dabfb0b04574585725de7ba4b42b950ce43b950ae3b979bba57c7849bfc3a81bd3f3b5727a773869b5de7ea5cdfe7af58e7d9baf2fc66b95f541b7903c600298e137c826df8f4729efb3d35bc5ced3c35ba6bc615da799ee7e1c4087ca9f772e0166e765ececb815000298e7314cb1eada84f87006af29ea4e60e3779557cfaa4f77727ed70d384800ffc4a36146cc927ba77927561a67cb78aeade7bd1dfeb23497db68aaaeffd47a3345ff6fcbd6fa1511a4acb1eadab790d2ef2e0a96e879b9ea77fdf7ec7f33e9b37962b9ff5008c7ccbf3bb65eff37ac7132474c69452913eade964fc7d156dc0ef4891fc6cf5e4bd7f5bc8dea785ecfd3706adc17b52457d3802786550937767d833e88555bc4dc0fe95fc6af5e47d477a1fad9eaaf0fcf79d8fe526efa3c24dde7fb2ec3d08520af294c207ffb379c2d0f39f943afb685fcdcbf1fea36255de7746f6be5cf6ec2a7b1f829ef37365efbd112c7bef79ded38fe626ef3de4577393f76de126cffb8f86a7f0c08f9aaee7d9f2890f8e932ff6aa27efbd1acbde7f4cdce4d96fef3f57f9b59afc92b8c9fbcb04ec3d6843db913465adb59fca4dde8e7af268de872e37ed7b1f32e1e6f71e2d7b1fcdb31f0eaf24c12886a33085acd6ea3e7c740d06ee0c035d63ada5526c92aeeb6ac7f3bcda8f7addcdcd6aa160f0d63cd7b6175dfb6a5ee0bad5ba817d44f6f7745f71f0bed3d5ce941c27f87802e48a38b0e4b35ffdba60d83ea0282028ae5f1407254adba4806afe20091203872f7681cf96e527f8eb344ed3c0bf5ce0fa9e13a9b99627927ecf835ddeef3cef3d8dec999bbce36dd1b352a659e8c089ee5ad756722d1b37bb5a6481efe34c653a78af0357cac1cbfd5eee92869e354edf6a4c074ed47af0a860d975f67a21a59f68adb516873516054ceba9effd586bad156d3c2e6ab5da0f6e7ee3953d0048cbbaa693f49007342a04495a16824675127dea56121f5d73cb2e029da1fd83247d8459fba0632f1aee2703d32e6c50cb4dbbb081ab5720d1e6420197beafdf794f7127068ce36667fc5e59e596b3ce77a0c067d7beeb7ae6264ca659349146ee32ad3d91e5b36b5d6b12c5e6357c9363cf5aca13b58a02cb1faa91a6e1b8c9b1c0ddcc5754c69f808b107193e3e0ac98a9f8953f6c19c2f586ebdb32bcb6829268f5ac672aaa5f9d44a33a08b39e750d901f5640bb7d6443a1dd353d069d692964218bdcb52b707dbf56e0b36bf9e5a675f3abd56ad6ebea4ac7676ee0d5dad5bb7953bc9c2b85d3bb6552bf5cf9fcab45b6d5fb95eee1a2dcb285f42dd93f7df23aebab031037b53cced70110f6cc8793bd09f856d4c81b95de087ce8c17e4165ace5020f8b05f7bed4addd9f1f0050f3fb666eaaa7cf09a68f157009dcec94baaf52cc50a22252c4888ad258f0a76029b22894765e37093414752f78bba719889b9e61ddedee920e830d64879bf082816f3e0f7af7a7b57519f430242d159bb3511a510ab5552633954217b79b47f7de7bb1277ef77a7f436ba332359fd676997cc40fe70beca3f6e0839b34f7fb0e371870f396f7e5a6f0435e2654c6ef925b5995b8694462451bd08f4fc5db82b9097393a3807c968897bbef938803a9a88f764b11025d79c96ee5eb3737dd7b44cfe8f86615b5839ffc6b237123e519fa1420c9f7cbeebb1ed133f5aed47c619043d786cfdba22c5ea84569e8480b12f4b37c477a19043b64a94a583e449e487268e53da24da8c3e1bbe4d99d43f215e5b3df668f44922d49c4cba2b21ed1332c7f5940cf428ebf429e3583ca52ff79f347de20da8ce55d22e20084cad4ff7c47d61f9e3fe56552052c9636179696ca4bd5ba9ee7f7767fffb4b8ecf76dee5e20d5e62c158bbb6f716e52c97da24d7b166773e5755d5648ded555d27a9db8f1fdfbaa65fdbcee5a99b713fce1b8f9cddcfcee2bbb9be75592fd5fd5f18ace5a2d373bdf836bf1849c4c0352b3652beb4a2b2bbdc0e737fb6694a6bb40a80caa93b8151495033db949e58c33a64c912225caf9cdb6c827a5f1e06100039c4e94767e334a41a06ff6e91c130338404d6618b25f2ae05b362b0aee9c94f659f1aaa8ef8ad3259fdf2c0952c8fe5f16d96753c0a7b5b28a6a71f3f376a85f0da27005ae4e3969144ab3241aa501b9b685ecfc54fbc6428ee30c20c571c65c1be2f8b538d7ee80b305fdbe2942991737f8bfdb0e9a685837859b6b06b61f8af6073109b1f543d70ce91917936854a8c60dd70de99a90f65dcff7759febc3efbdf786eeaa910bcba033370958cc6768bb387766f30b05219472e8b2d4791e493e4dd9fee721ad8d470f1ea708e302be7f057c3b8fa7bbf603475a48eb9a21f466adb5432816b2bf157f5c4176439c2121c00965214ed7dc8063adb558647ff023fec8793ec35c8806458938105b392cba5c8c75f7b70c6c69166315e56529cac4562709124f84a052a5ceef46cbd2f7632d9774186bb9ffbb9138fef329b6c4d76d074d64b175d1b0b8bcd77637a11a61ce86b29e09c3e8ae0de3cb91d99660adb536a4e5e0536cd9db3fa334443a0f71937b809afcc7a715f5e97ce8811e18cb53c4c9a588cb93549ac0c0b70c6b65b885fd33a4657f326c7783ad756498137e41656ee81adb35b60ccba033fee12d8760f4c94333406badbd5ac0f67b7025bc5a78b953f0f72f370bd8fa278355fce1b7fcd119885e0387fb9d7db8872c79bebf6d3ce50f7bc3c02d2da3916823624c8a2e51899b3c24182c60d1558a4adcf47d57feb0b9bc6160fbbdbda1cd167e4151df18a1cd5a6b2d142cb64425312d4a43b5105ba212d175c3f64fb12506c60f7a2ba819cadc34a334fddd6ead9f42c4b971d314224e76d10a6a72b125b648624b6c99baae4b714665bab28732889b6ee7bd27ade5fb373db879fd5e8c5bf4d44390be9ffba2b803927c7bf8dce597f34d7113ad79996661063068219f62eb8a812d5829b5f65e01dbbf5d772d4b78499115a271c31c95c9658a1ff49d3abf5b98bb35ec3cf1de5bf844251641cd20d620fb7b618e0b7c7e37dbc9b81f408ac3eda0a77e1ef72b3b9fd08b30dd0d21187243d7ec30a4a2425a081a153ee97076e89a10a767681515e6eae949a342351a876ef1e1be31d46814edaef9c040677215f561e1130316d93bfb51241e8fc7e3a1f87b81c20d640cf1ef2605773e694d861f3f4ddd0471b3ab379c89ad8a62f9953f2f486cd92a8aa5fcc6a02ba52db4e1128dd026b6566e53dcf4bce35a624b6c7daf6f49eeda0db989041383fbb80e5de82146b086740d7d231a9503a4f73ad8b70bb8ed1782145b6badfd80e440515d44b3288dd6014269c4668d81fbcd9882eb37e9b1d9c51b5a4fd6459bcfd687211fc0e16e751237dd2fe0fa67b39a47cf38896a16ed1b5094e7fd6c1dfc4065fc79740d1074a61c524ffe34d96fb42154c60b2c5be7a1967e0e6badbd650568ad01d40ad93f871e8074017be73d5be0b3593637ed8cc562dd3ba4a29e9efa4d393b70f6eb90dfcdfae70820c571aea068f0e2869efc6b08d6fa7dff7962e08681724039a01c50ce09cab920334039548d22f4d4e1af39a0336698e6cf140dd5ca6b858915256ef256911afe2db6c43c572cfb7b72dc73869b9ecb73792eefc91be4284412e3ed7202a4384e97cfdece5b22affcb7f276c532b14b60150556d4cafbdb989b8dc408126af60ca8c9573d036a5a24a8c96dabe5a67521e1028d6369e448e845f18f88fe8384870f1a0f101dcb11874421aa128544a2d00ad86265e4e757babf2ea4cff8c2020651fac2d5f8f87df88a0c3ab5f260872da4a79abcb40b5e7b97d0b52bb22d8bec39b7db94db854265ae142e166c60d4acfdd227bb4aa0280b068bbb3259ad5f4b9bcfb5fd4d2103ff4e31456d0ad78c4c4dfed93b228594da20d21301d7fb509f074bd6caec134bb34f3a08b96b6ccbbea86d59598c9dc2ca9e644bf34223300cc1d07e46512c8d20b9b9c949f03f64f8a0edb0be23a1c72b2c20fe23a307b558f0c3ff4a5e13cf83fd21574d3aa2f723b92202a73e0f8e0f767867b2ec57d69e15c74fdf1a174a5e79033ecd41ac153fb4f35e1941f47ba5cddd42e9067c1e680a0c3f081e686ad5f22bdf9525ef85880f7a219fc70f8648522059eaf2bb5ff42e8f24057e5b510d4b067af2bf396423d12df17d7823f1a56171d4f4e5a8c9bf13494f8918b464103ad426484059233f21be9051d5a43e0c9af453d182a63b855b9b0235af19d45473f3e6209173dcec1635f97f4fdd868f68b390f387a44f84dfe413e05bf209d64d817fc9275838257e2e4e585e2a661525ba3437fbbd334447e526bb8475d925a869a7a02e37adcc656eda1b354d71da5b6da9f023093f08f8e1f787a591265d06091f97468e805e3cd2e408fe0f928b49d7e2c6b738d05310042a8f84e11b317204ffe79b80e0d3d4b7645ae3c209f9ec04c1fff920a0179f8aa52e2380fff342402fda31609a2add7cbe1444fcce1f28d8454569e813f26d511a9ac187c56e0bf6eaa8a87fa828a453616904492ab434d25320b96af2fd8a089c02dfbe885cb176a4c02702a7422c9ac044a657c8148b2aaac8a08722f2f320297e93345b2ab8a3aaa8163ff42defdf917d82484167cfdb07bb4e7578d5a452d146069d123dd821b90a5ff456ac7016e6a65db2badd55bff219b62aeae6e4b46e4eebe6e4e474506e570a7dea1ace0bc60d23fbd35be302ae654f4167fb5e19f8bc2d0b062aa3b261e8937f481b01a755a386d39ae12b2d2ea7c55bfabe2de0f3b670ad0a2a72e7dba2560ca1c4cd4f767a57b60a8ed0db02ce115e999be10c6c27b462f71d1befad686357489af2909dbbd1b77caf7ce83d24bda39596b2032b3b2eb274839f079ac225a87c213bb2197ea139bbf0405321f854e481a644efc33baa9bf03f320cb853da9add82668fb0ad4ee811a93896b775c4e7815d0db1fd8ce367ef076316ca72bfaf45fcfe138543f257b2b8d27d24b923e2b584b6db7e9f6def96428abd2f8936f74b2ee1155f441c3a9722303ef841c6074b234dba77298d880ffaf1bb3288e8c3d248a82c796504c4077da9cb20e2834a413e8f450fde16d00a1e4711cbcb0a4d815e6e40114953602d6d8e9d725b8d1ffe0af4f84be20df816d751753aae31b4394ba524deacc4ffd81d5ccfa58a09dbbb5d5b97dd41f77dae277ade8db06f90e187b4e2b7e87d4afac5fff4115ed72d57e789e4e7c117227e581e095f7cb03cd2244858966ec02ff14053e097847c5896fac72360193e7e21e08fe591f1c1c7e5912641c607ff08fe7c9323d9717dc81648d21426696a4c8924eda86c18f0755d2516d7fa88a41503a697d5ad23c2000000078e1c007059000260a2a61db7533ddf6900a24dbd32e167eff72d2d158b1b80686371b857edd89c8a45474871e775ccd019b2ef11344fb72cad67fcb658f2795b9edba7dcef56e4cef52040ab5bdda25d27b7eea8cc8d470768ab8186f00062f734cc841ed12608fba5af7ffc95cbe3d44a06e38f5e06f85b5e244bfde0d3d4872cf5874f53a047f24053abd54b195bb9941d58c745966e44cf034d0909c08b08505823152a3bac8eac33cbfe42d2940b4953a4965488a4291612894a45459b9b15b2f49537e1d31c24002f22d05429b0a35a213b360cb82bedcc5261c180cfdbb2387cde561b911d096a763aa066ed9a4e7261d7891a78501e1d15a86c23404cf208c79a10a4b87ad65a9b001ad1a67b9aef115ef209f03fe467cbabe4831320dadcd617f09f1d15ae13034ab3843c04edcc80d680a2be5c3d5919d01c1235a24df7a00dd486ae1fefb3a5298baba88b037af25722bbc5f94095a1e8a83eb5286e7e1d95cacd9bab57e6e57e8f914126d925648f7d4e72df60d9544ffd5d253b2e2b6879f75d4ff8c12ba116d20b8c8d2f71e4300d404701761c800693074d0f1b7ca46ef801a4013b3ce086878e7af40313a4dbe17d4d7cae1d5d733fd7d744a372dc1d4228d0c3fd4f4569eab7649a050f8ee0a28c7c7e2e0b5c000319d0c0068670a0031ef800084220021f467092a0042638010a7e002288141051c10a5850a4052e788191233040a20a8590a9b556191d1e8abd07673ed5abfb5e6edafa7db5f3ec8e1d3a7a80eb7f0658755dd7c99434d0d0440d28140f78d0d03051a300052c61830f1f3bc80c60800e256ef8f143870b871c72d0a18306ecb0838e241c000108e8c8c10d0f3ce8502281094c400712427ae8e1880a5ce0023a7080818e3e29e2890993e97b9ee76d6003ad211de8c00d3c0002101881ef7742e0830f361801094890a4042738410d5000041034088208912254c00216b08ab8c0053378c1912344c040a5ba3210628821ee6a45041143cca088226280eb7f34f8922411c20637b8c155b58e38620d24729003359270b9cea0c4124b5461a28926d2e8810f7c60861f2c598206109e782207138210ca3084da755dd72494117fa8a8cc87033ac3ca5f0c90c8fe960cb87e7f427c2d2ae3ca39677caecfd5c980b8be820948719c2fb75ca9c883a7bcf79e923c22564579a508eed8e131993affb7bc89583711eb2b6f2c2730787f803b3b81cf909629a59486333755f156c5dc4745693c571f609a4ff1962b0b2ed7b7dfbd3fc6b65c752296eb2b57adeaca53f4cab57ae5aa6746642fcf9b45ac9753c4caf55db97e5c5ef614b8562f5f6eee9f1544a2181937518c8c463516a2588b8e208a91716b27b166d235be84ce60d164681cf5281cd6650ff434cebee709ff5281efdb0e19caa9ea839e49d07f1780eff7fdfb9db764a8ca14b873e7eb3a56fbe5a5ec2e8d50aed28eb9d9334f839a6d051acab9d93637f9dfeaa99f64bf5d9c9e69278d459fdce53d52a4049571895a5608e52a4ab4833ef98b70404d2211ec89e8073352349bc2185ca953c4cade0528a953d4ca77092ddd12376fc7a48b754edc5c915131eb3e2b72511aef641792a66e98bb3cc7ff422bda8c6fc57ed155021e41a2ef20464fe466d28b1ef438f0e7107a16b06bc50ffe19ca8172ac656e628cc5173ffbd9effb44fb3d885fe6a4634d1cd07f65b803815049822e4f507349879aebb3945786c3bf417c942608fbe283fd2c8f9f8aee1a919dab6342447621b25382cef87f24119cbb252e984f91ab934f11783b572887f1b5a00f459bd1b3e2799b8e4df83f3a8b5a942609f8fb53e4126d8200a24b229d597003614b229dc5925219b03c55501f089bc3bfff919eec80fe15d41c96ddebe60fd9c1ba15b273d2c93a2adcc4c44d5e764bdc245285aab8c9f149c45b35e94a110ecd6b702b88664e20504b91123187ae2c9974b44cb5f4caf34ad1df8aca50e1bb176312e9df64dab1e374d8ebc2708cf4bd299f8dfb6ce8799ee779c2da2f372df12e12b057abc31c5661d561d72645ccd635be448a463519bea46d6d85b6b5141d133989b9c96bf54ef626038baef117aa67ed2f98137c8a62cee408d861f55e8c5dedaa46f07c8b36224845207e7406df66ec006103fa1f9d3b55dce4b95b04bc7276bb9870938772ed12b1442d2842392f5445a4f21adce56f2c946be960d52d9a8968a77debb90784e6ee71a0b92b475a7848b20ff7e1f75b4413e110019fa259d731d1bdbc06273d46bacc4d54903e231dc7696edac2bde6a64ee94e6e144df0d9b8ced5b8c6354e84f35ccaa18fa8346b3578591043866804000008b3140020280c0a87c30242a14c52d4e21b14800a7aa852604e9d4984390ce3288a610c424419420c03c010003032246407846b54c05b1736835bc1ee4720484f75c9ca8f413d08b03368dee682fc6f0e72ebd2aacfe7d8240e60b12fb2a29f648f720d38f8d5cc1b9986e09a1724e2e6894759e436eaa5590e7d332257e0de0160c902fa7afe77088d6bd556db59bc86081544b152d3d7678e7b89a08a25d064cda06c42e13ed9780aceb9910a777ee03901e9694e27d0398cea45169eb2da8b2108ad7db90e478f89fcb44f4e50189e437d59e30aa28f91c03343ee325c7046301069aebed4680017097af936b433c57a2704e434be3e381820f52569c1d97ebac954862f9a7512f858399a67b2c915cf4c24981723c158b77c1790ffcc89df298487dd0c7e4d93b20f968e8b6b49d1ad85db963e4a07716ee4806970bb01f32c5da4c735e20486d8ef56e0cb6c684265b12e9d8b02346fb1f9617dcbde8a9bad1080758bbf34ecaded7d95e0e29cd60238f046da22b7b905b88c80486dce3b6cd1bd9fd7235fa36e200b362780e98ee1ee483160dbd817d4744ce76ce34406e8c4804e131837b855447fdb4e943c86693e6e7575c3709e28ae665cbc941433d406e710632d076b94d2145b92c271eef6579c1d4e9c7432c3a5bc8923393bbf13e862381a7f2c9c41d768146de852313746e98f71f0b3b618592305bbc9cc4a823db13ccfa4aa406481774b64bf521eeb186bb7c2c4f5e43f848627c4a800987dd38a1bf9096ec60ab882b26b7c1325315acbd582c96ba16f21dbb1cd4acced0c29b16200abd7826a3e04a2cc88247637ff9f00d608bb6bce4d2ef4a69efac76f569e13eac2407b5798653924d2a08e5fbd87852810f4e7cb37f85d1a1d91aa4032d98383af5773b25f7e0636bd5034fd0c5dbc1e230c3dcef03582a9b2e8a9f770dae3a9775e1bd3c55348df0431e0eb9a6990305572c9eb24c982e33947a2455f72ad59daa0c3af63f50b643c2060ff823e04dffcff6213c850048cd019bce8ae21829cb377157e57a898136961e74b867491e69e06d9a3e989f46ae0cc00e44dce870bc0f9c4376a488863c2a1720c2b0bf24784e27d100410764fce02c4943c5d2dba43760a3b6f21729245eb57d496a75d7747af909dc5bab80811a2ba12f5d82a2d4f35503f01a9312b4b2d73b462ecdfaccb42e73188203cb9dcb97cd5cd92c58709259b3649a2089d2771431e541e06e02967c9e81ec93c00e54d06001e2f44f3e356b52d93a20a7a1b7ced61fb181b73108ff14be4b1c49442efe1374b2377868914b06974b248f1f349659831f6be63827722001caf5e08469af76b42674e4439bacbe7b5a1c191f1d2ea10769ed0fea101386c761561ef5abdf0d1a9c0f28eac09becac03aca9ac72c84a7c03cda37434ec3f178fae9e581564364d0b09343840fd2da11b9ca3baeda510c6e3a994d2343d3ceaccde01c7caa789c09f73ca54621c32951f68e1b27d3d84cd9ed823e7a61d514949514e7f289feb7238898c187974b537064ce3b7781c382645e6c45b92a627bd8e6ac2fb1763b955f2132a1b8ae50281831684c178c54b4c805236409b23eb1a258587cf0916f2d674cbd6dae29e6e124faa4ff575e867f27aac393c44794431c51e9372f9f568e583795d3ada544ebaaf3f15e90297fdadb537afcc037822187019d2b7bc65088cc4233f9d8964eb171fa8c6727f0cb86f212dc4a009fcdea813240075b93a228af3539ef5151d678ca4061decf44d91d115e3bb3ee12b79cd09a508370ee152ae6186da1f5d6c4c4ab9154cf08a95d2d05d97fba998d8937c003045fb781805eb575c5d88e33028061904da71c9f32c53daec33ef6c9add1e9504c7729576933203ec6860782152dad881c618e97d15feb191e8c36c79a86773de1245704a92985a182f22e1f5cc10e5ab2360c09a8c7020b75f765d04c57c70e659606a13eec7dcf693a161d206909f7e274403dcb1673a6a68cc7db3082ede22b6e993f7727b633a0346a1982ee85b42d39eecde1968aafd736a9f2e33e245223bc60aa9df10330b32e6da409aa3809a259d67aa6bd00dd78fa0f86a3f394238d07b0d965c13f16add1a2f7eff484a30a69376da6c6004a324d26b63f0e38e30932cb693402ae27be42a3de48be9526108202ec992b809d87da6b15580b699a0ff65a7600014eeac4b5983ec3ad95a53d5b2325e96075bace0ceea111f7ce54c905d0d0cdc5ec7c01a5ca96e7876bede7b048e3717b19d9a5da101e4ac4d5eb535a81ebcdc0bb26ec7cb8496f2ad501a67130973dc2be0c8acefff4065e6f89a218731c07851880a3331e95bd443eee132fffa99effd69c15eeba7e50cf4ac3972f4e3c89699210c719bc13a85db17d920768c7dee4080e71ba76ec7cf38ba5e4d71d72f9010d08541272cf91eba96a30b25dc12e47628ec7d789fa7ce106953c1d80cd13e01b0eb1206ad8bb0caddd557fae2b941b2bcdbb365f515b624ba6a1df2a4d2efc15853af2a66af2281201b069a50ee8a38366b50500fbf3e0b45ddbfc80e978168f75c22baa21cd37c495e45ba7767c2bd132bb96df3a8da4d5b3439d9c19441a24e0eba9aedcc53b251fc074f899befeaca4b02ac09644a8cfa74ea5ecbe0c8f7a3dd8dcb5407e5b4c9110a0b9f548f01890337a73cd92cbab33bd06478e10f917fa53e91d27aeeb49efd4e1bb648d1c2212ffa125ca4471ca8b761da3d9272d84c34e2569831d679a33a269d66e76b445e64d72bd52379e236ddcc876d1a2b595b55ecef826d21a178334129779f355c92c4babd4c466fc01011e60b257566b661cd8316d63d36102b7d449e45392aeda7145d33a3929e8323bb6fd16eff3a170031b4620bdba132017f428f1453377237ec61ba3cd01613dc8c67581c10a34362045a843da9826464d3c20d34913a33f8ace54e69d3bdc0d8388ccd6bea95637d2d70e5de21a8250a055a2491f774f1394bd2a88bf8f6f6d014e358baf12051b272a86f6ef0b98eae12aa67b9206115a3d84097bbe75f5bbc642fbe0381f09a48285015a73921f9c81e1c20f3574871fde44e57030632737260b76709ecab152c85ae2d4a5cb1bebd535eb02ed60f5af200a938179c3a7974ac1d2ec2b64411d708ac28618e3ed1c00028e4d8f3819efcee5286caf14cd9dce58f1328b24d4133eaf28bc51fa826655c7c776921c16f9dfb204711312c0aa76504497e24d7c3132ca41698551fa8d29e9670c3a436d396b7605c20f6318c92a2c617ce49f25e635cbce5d23f19faaa06b8778513730e1f7b52b0de649eb9a7808570cd332aa0fdab83133ee9f26c6ec319c3f61985012c37b91909a43448a1f872f8ae05ee29487947e765e7c42158a2626d889bf4b21946e48c2fbcd433596fa35c0b5f3fe0d38d0527cc70598c93a6f07a04422d72f243a052bf90328c2b6bb7a1d29e014679dfb19a71aeef8830941d17c5b709d016ceed18acf190d9db769b66691549590b0adf81917db210e2735caa22a2584357d906c1b29a29017b6e82aca7758d636c0bae5dc0e7772433bba19d96c3ce49b0ee2b3a96086c33fa23930c12de5ada80fcb86894d68245e7941f5a6cd1f5ca0b7421187c42cdf2364b133826957177a0a80491dc45f8f63b5b1955188c82218501833b04918dedd44dfa3b51bf70cfa8f93125b52cd0a084de091f531a1fd0b7e52e513707c17d6237027d2248849e4929096102b817b025a82d809ee44340962267427a0498849e09288963056827b025a82d809dc09348962267027a4491093e0928096285642f7045a82d809dc09681262267227a0491893802e09482409264ef65e2204c199d827530e91222897ef8190c09136cecc36d43d74cf7a4aff5a1385526af652aaac1ae3689a649c9231380c9c7f9c0c2df773fb0761e0825bfcded84ca6c31233e961655a3a5834250d6b4ca5931533e961c554ba5830350dd64ca48b9566e961c59474583225a50d38ab13f8b392dca149b91f6e64c0bf94a71210522ac81d471f8d780a680cefc55a5c11b8c6bdb626ac4bc0fdb3f3eb079cd68c801d6df5428e47c0964b29b439496fb97e351030d725ba365e8729781c6fe79bb8034e6c58000c28d0005dd200181a795270a51263651a3f2a36dbf5dadc825ebcfc74a1d2a3b088c667175045a70527e5651fc0323bf5d3565768b5a9aabab53eb806818c2b8281992a7d63e99011a0a2d7f039f0118fc897f9d8c05839b6508b76d4e9199f119fc4aa257d6b888be6a9caa8b81bd0187460e2aa7a1bbeea3c3031153d4d1f751f449c8a5e862fdd0f2656652fc387ce0315afb2c7f842d781395e952a7acd1f43de42d00e8d695881f629c9992c2bfd5bafd808c455647e44438f9cd5796e4fb472afc190fb8327e0cadc984b19fcec278091542a1e36ac109b9b55f51b4a820c41957a9be7f9e746528f364fbe42c7514e6b92c1edea0c65401f0dcc54e9194b868c40153d0c9f818d48c5bd514c603153a16f2a1de60255f4327c042c2255f70631a105cd547b94bee1533ad8bcefc0d55e870f205ec015bd436940963d4e5ae35cf271456ccbe631c5ce250eaeb028b817aec86c050ec2e77c9d7f2e8d333daeb81fc400d84957f53a7c867c0056f40f25a34c8f2b370731810de94acfd347c0137085fe513ac8f3b8f2ee10135891ace87d7c443c8056ea1f4a871c8fabee8e6282159384fcae7b477e7267c9624c4a335fb5cf2933f735000e59ad61c053119c8ee16f6c50e3b0baf4201eb00269e9f9f0118e2f72f6889211058105d3f2b163db0545266e209a2948612fb2d2417a1462abad750490fffdc39461f05b43e083f001340619208898bc92cda03c570c80c93c85d61af9d0eb072a9fdb44690c9fa8d34ac42086246f5f628eb5a1b0c44b4c8c46b03c304202d011017349c9ca68e9744676b233abb0a158a6df0448f40ba952a57d397ef2795cf2dce547c079bbfa4a8f6e1d1245c03952e61d33b33dea009363e7142a6dd69901ba6daf59bffd863dc902b437411a72a224d1ac5fdbd13a42d18e827eac2d03b56f3249e89da0eea08d3f7761fd33599c5f8d1406c987a5ad98dddaf2f547ad6e4952ca5dd63fab294b0b1b9943388a7f019e31faf8e25d691c7241869d3bc2de6f63a8a6b8345267c44a900151860e45082f8c8f8059d16b47d9c4d78bd4dec7a6342b0ddf6064da018c1e3905fbbcc80b258ad665034d9f87d0773c7ea0bfe662d4b96833c882aa42c03710be25a140aca43def08ea527de9acdd11ae14ea2e3d9ae3fdfeb41d7ad31ea394f79c36aaba462b36d22372212a7d6ac5613e7e01f335eeb385c0c8069a367b47c083fc19d5c112646f0d6fecbfb8603002f440e942878041e0b6ba56e0ee3c9073919233a08e483028ad4889b882ec73d03ba26170d7443e403662c44d7df3fbd10abd4b0ef8a697511e15b8eb785175345bcb242a2d357ab458759cf4e459263ca6e3f1d64759836ec75015233600ac302efa52a4a552c39fede6dee2ac114de53ef954b50004bb121ab0de8ed6b16c55f71b01a8ef080eabf5e4303b76d78953b56ae54a90c13e836b0e000a727aa8b1f8140a9841e6c550926a59def9f28575bd6038843e5e305998cf2f65fded12f93a8323c2c568638f3a4487330d1a0434db2b37ae6ae0553e2f73636f2aa353f621ebc91e248f21630819fb6905777d2ec8cae10cca3ad4526e11dfedb596d0c6420591c212d7f560877093017edbb580d5fa35c4dd8d1eaa9eb5843854eeb0c38e3816383edf05f1526be534e0d84621c9cba3bb98f0ed6bc5671f47121f204955916e5dd14ec39e468e3be0221c2ec49cc6db0b8e28de8277bd97b95e9bf9aaf51db733c20376648a3aec031f2c1087dbcf7607042a0d9802386de826a3576fde2844a1e232508b6046b464f4df1a11b510f0881323d3a1176a4eaeaf0f8d488656ba7f2ddfca1b6e4cbf0c18f58f18827ca9d7a0365358810788442b69abec71b6e17bc010cf2e1e49dadd536e6862e4864de1ac523843efcb065e7f0e2c84d43e350a82caba89c259b84531480338fc946888d85d30ea2830e8941e45f064555b60356de59871e29ea2af051a3b733c74793e647a399220de3ae4004fd89d7d0aa4cdc1d2de482c1f2201951422f2de07865e6cb8f294447cb92de64e9f929150263d6c2d325a527b9903fdf19984bf79bf30b06b0f3b5c9fc26fda14d1e0ae863b725d08360c2fa09b758d629165e3e49e53b4ad54d2875974d1395483cc329fca81bec78ac03adac57ba2ff505749f7a4950108851502469089950625d24ab5d39e19e4ca060c358916cb38738c1f1e60cc219debc131dfaa7c44f06837ed50b0bdfe9dd4756b624319a9b450bb81d324cf9e041a3ab3f82112758c33979df201c1518d57f756a5beceac969117a21b9d9c03ead2fa6f59990f3938f575046f4a9e48f00a36f0e2d56a029b044b1dda8b8d321358c62c19031f3a044f01c157184d2073c449f9f3686b39a6f00e9940031402b80712a887147e9ac3d355bcf72dd5973f9a0b3e4c54b05684ee1c4681e7f45fbcd81a16e00d82b4efb0a52b8c156d7988e20c170733c5eb81f3a11c7a0f26a5c2072ab9b7ca16ce4716cc30b0264529fcfd5df97bba4d5c805f45e30435dd5efc576c110180128b777d2000c951a3da78111566f33f4e54662a3140d54c300201ac2eaeacc124c9c01790c203cc04417f5bf34a75d0d97830c366e90ca973f84767dce564e02f481f907d8111d370af6a0774242da520bc4a6a4bd74fa3182e901a6eef1eb33b026b85086e43bfec2cb195d6f2c1d4792507582dd990a63317198ee983652bb1d2f41ca738888e5dd8a101ac228eb41d4ffd220a2005df3f8f045e4f88bbe5cb6d887c060c94190dcf866642a5bf591c48d81d9979b68c24e244de7eda22a4e8ada49dc69c8c0107b411811cede95360a51db45e9aa00f7d539c8d000c4174d4cfde848ce8307ed319c103bbe3c6e313f2ed6d1a025934e9b64bde307c7a8db89cbe7201994867f4a163251068345251e8d9b0b68a4a226d4b728b203c9e64c17d1dbfe54e4633843e09e56a3240109792d80aaf9bd2ed1dfdc733f2dd2d3837eb166e8792cc0bfc5c637f0726968e239889a973e1e1e0258ca313f62649d82d450fec37e73f353c3e713a088479f776ef7832019c5094b09cad6eb0358c8320e81b11df31f848168e5d1ab2db89e7e7af7ebfa889c3aac558f2830aadea11b106ce53f52372edc6db8670a013b111b800f0e241ccabbca4cf5e3f15643a897036d6c78d9edf1d38c31d36746a9a8852052f430a3440ab5198ea62ad82c69b5b3c71931aeb5fdfd3b0f57a7526dfbdc2c2152c9014301727250498789106365115277501e8e8fddb8cab24aac5184dfd4238bc9173a1ff486a7f582c2ce8b92d45e078097c334644386b6b20deec3ff0e905e4045e81378f9860108d02ce681dd18502d5acb944552172cac4d2c6304d20674ee8fa444d2868870d352f7ce3ec07df58d0f0ae930bc84f6105a9956fc9ebc02be7f0124a342a835764b9817d4984f62393a078599766c5d9ce31d59f7dd9492730ed16013726d990617c974c22607ff7612235547ebc045fc8ec1a20af6cb3710e53b8add9ddb021392a68950f06bcfedb07c59397e5f4bfac636ff30d6586e606ec7888d9e89a1f37078c255d0b3d5eea245b4a39f9b061e458548e565f7572b90c8503b8571523eac5bbc30ca58696060c3408b9ecfe22692f00436d59206164a8658b0ec9c22632bdf78ff73c430fc12fcf3ded6a0d57e05f3571630b8da988d39a78b88ec47b7225b78bd29f702138f925b572d2d091174768a0cd378c2756b8334534cca70be1d1eb7a3f10e2b7c568a15085003b27bcd4e9ae483f8d891c5c8408dde5f8661be7dade9c67dd6b121673638097b207a73d6cd3880a81675fd260d917e1e0ca9180c494b3788bc852a961d76776cfc94195a713bae7b61f3992bf8125aa30d84587b144dd6cc23dc6505eaad81ef9b80becb9bf3f5b5507e7a8113ddd6197566c8c5175a7be4148e5a2fbbb0d22f00ea78be561d2fafb89b803893d48057512d80bc1894715a633995843d97ebecef5d94d3946a21cb445c8b54d20b7b85a8cec17a0caf60330647f229ee82a5699bda7e3f684f5c83b90a3140c6a20d684880302d41ea2930a181f7a3fcebf1635a0299cfeaf1ec83ded1ac089042c73e11ea9aae38b3377f263fef8af7df876b9d26dca6b57829afa5e892684df70f721ebc3a532e29bec3eab381dba77a0a5dae71232f6dd32cfa5a46bc5f80b0debd7fe7080d6618637bc8e541c625d48de1c5b6aa05a639208dd3df38cba1043a413e72e2aa4e4c3b56f8e33c32a215af76048e8061c2617883c777f2c65c47a1d00bd97039fb8d8d1ac3f0c8a76b92d8264231f5eccf10a8cab6c256851394969079cc0d1870ea3e887ee4d357e484a2c45f2bc284bfed1414bf1917fab63c8902968a183bfd4ffe0ba6df7bb43c7928cc02e35d3ff442fa24f27079884c2a2fe4233a312237dc29e0769b1f58f1ce8a7b71a5c3a3d9a1461ead823df0c70ba0e311858bbbf256ed1f0e0a1a79d70d6bfff7f0e19567a31104562a5fc1d9aa2e481032dba8609f56c5d9106ab472b3f0b63c0295c94ef1f8590bbd40295aebf2766c05ad478fcd2e6316e94db02af5b5236641c9a1761e7c5108d02596d3460b61fe5efccad41e0f68fac4deb2deb293dab9e8de2aab9a5a5afc821350edb145caaba650b130eace05219d2d8b6a1d381213d293d3532a36d801c2377f49bc5d14596acad0ac568a6d37332e33bca10fca4d5161d7a6c02fad995608b9e7913382f0ad0b830fd60b5a7e8ad9951a51f8210ea80eb06f93796140396b43560fccb141872f718d7bb25070389723e4a8d1d1a7e9f5799796a533deeca9421c254e467a02d21974c30a5a6c630b4b19ba9ae650a695d1208b4c3bd02425b63657db79abac0c53e23a0afa570183f1349dc13117e67dfd911c693170fb6f770dd4245170497f7a26b1669a10f19c448f3f738b4893eef2a69d5c8178084077eb1aede4e297f207333dca2c4c462bfc1e02640d17eeb3bf87945fa9b09ff062864093e8914307fd47f13891362959724ac656c81e1981cbec796d332290cd302bcf67ce71d275dd46098a70edd1ed493e0670959b52f661222dd858405523c8c63214642b7b449288e04c2c27624f739838e04223fa923bf9b05eb9905ac599a3d7841bc0796e985ed2ccf9cb3c12ceeee8474a87052f3206f14406bbbc86d3aa5d773843649c03225c15f91d95be31b0a6ca78f5a756494d7b1959c474ee1e355303c20e67f0fce446d1bcbf5b8313ed4b07d65f04c5f2cddefd20debca4df2239d457c33b6165631a8a44cab3a957bb430c5668cbae93098e0dfd702db67f88fb0ab46754c3c27138a527c4905eb18650951c1c330759c40690ba521c86cf54d48f998cc846d47b2e140285c0ee603a3e4b0c4cf2d2333b66e4ad24e9c4d9f24df73d286490dcdc1f5aa63ef78a3fb41a5a96374b46acf51ee82fad266de1f3f9c1fcd14391e66594beea145016986fe8294fd42d8701d27947609f321039599d53e8be0f992cd5d9d62a2329f30754f6c7a7e42bfb825f06013b152cc8eb0f69e50e1619e148afacf94b19343a141208aae2482a070c3eca3e0ecd1d1be60b38a0feb0f39e4ed7ab6d9510755d75c0ae447bfa94cfbb52b35b29efdcf07567deab322f4ccb50fddaf03c56a57f497c82a1311341e4d16ef15fbd007c17c7e0f5660c0924c2ffa12165a1ce34e7e7185e12c028fb51f917eed0752c211fa9e0108afc170bf6e54fa9063fc0a7ed08825bc010337380e09912dc8de771185032dbcbfb884a266132609f8703536607344f911542029d887be24df1cb19a82c1610dd135e6dc403673986003272a1b720d0023ec80c9aee856c6452a66a07b25dd5a3760f64ab9182b69dddbdb6bfb4e0c8d6296212052c202b8ce63c1ce6387a8806075b1bb250e5de4ac23871f3bf2a6e02f9cae418e39debc7a2ec39be6fbb1da6d3b7275c0e04a7044bc6218ce34dc7cdd2a2b89032d8a88aa4068498c34681b47e3125ebf3168347cf61537a71de095eff49e73abd60cafe326a71f4a7e6733e6b0a80f073cf8b99ce88271a54d11500add9e18dad6a5683d9305da60e8730ad2069f2820c4d6cd1a4c224e21abddb0d66e3c30cbc582915645b981b05f509cce8ab880e58842cf99fcf0a5c31ce5fff3604c32e6ed19a52609dd7a8aa29565f0e3f496ff6be3851d4dbf9056508234d7040238a489f550d1fbd148e38740f4b69adde86ae54ff7eb84fc9c4f50069a284ddf755701f24beff5fa94a03e0d32e6a20ed4eda50b24e17a87891a243c2f180fe7039417e288019cf3431bbe554079ffcf92c93c203e4030a7ea81a48a66ea1a803c87d0f248b3677cde94b7893ee20905bb24860b2de918452cd210a0ff2f47f86adc402819dccd261f09bcc44dc0980771e7a31d6ffe134140763b157f316991fa142c9f8e9d4014267c1c498b6527b6144c487d17513edbcd6d6570f5a81a3199750cacdc09bc3269891615155590c32300a8abe9f573ac1d8955771a49416276330d6b5cb173783a718ed4ba1cc3de83b65f90438bfbbd1544d1210884e262d7f8fc1a44eb210090183112b2da0221362b1de676aaa1734b0ac7b651fef3fce58d26be62c1713b5174e58e12e194f675b1c969bebb3e9bc55fa27658d856c22904e7e7d8148364fc30fe44bf0fe28ea5a5b503e8c0e20d4f719412f14169460b607f3c8dabec7f096c79df7722fbee769d3c8a60c59b70ae0ab10641f83c3c23f69811833212b20196c65b233aae7f86e835e0fe8e8c30907cf1be7aa6830d265cef265d15b2193a1bed855f93645e7cbea3dcafc428bb5ac7699b18b1de85f3506bf7965b8c166867e465279719e9538234ddc1c16ac0a276686f9fb89ec77e04a7d7ab3449fb107f94d0532e1c08882221fb66b63e0808b8f584857b30c730f77cc05a7e075393b4c0e34b681f1d7cb4759f7bdb3fb02f46934454e12d959094d3b228d084d388e52d782db82fb2067c9355d4b45f52bcee8286996ffa033793abe04b51ef18acd9e9124d87ba3f7dcea9a8867d196ef0fde2d8c769db331d81f9ad58a22de8c3648d6738ecde9f3761e35d8fb728d7425da8225785e2cfb7bd2eba35ae28fa7064c329e12d55319ca2c0ecff686186c31aae8fbe45d60f5bc572af2e6178fc25e4663abf12909d9777c3b9a40605a770759d20b9d89eb6046e5f81251ad2cbe08ec3cb65fd6e62b191da6cde52cfde54db58c5757cccd5d9fdd8331f2331cedfd64ffd120249d6a1413ef62765df55f64fa931ec036d145de0f00c90f9d7977229c9f95ec932ef35b82def5acb602cb550cc7780160c26643d2463717d0108150a391119a99770ddcbd0e94fc2651aea882efe3c44067464c4d15893deefc86da3fc910ec7d9c3e7331fcd63e8be63acc4313a7b7f3f8dc932582f1db2cae5ba910675ad48da3bda30c192bd0af847504855a5595b6d6c5ea28cfe3c0a3298d552f7d6d65f1980203fb55c05d09ba8377700bbf0950c14f23911fde75489f1d85ec52c62a1163bd0df899495753c02a3f2582976e588197c2acbc8cd76a5768208cc97582885cd36245f0f022b976e68a60a628e328d23dea90789b5650b72e40195e035881c4ce155e7c6f88cd42488e59c87667f8f8515d72a2da804c36c4ba2ec910e102db863c006c1dca502acb6bd316258f37c61bee806c7a9df240d9d2a3128ccb9402ad3352c4f7087a01503c354850eaca5c6e1b638d62afed08651699c15c54c1eabca5f76e1cb3a9968078bc27bdd68851e4925ddae3384d3280819f283623b00415047795ac6ac9e7de2b015d913e560e45cab2813285914ab42b8c93d62602e46acb63a3edfe346d31c109616647bd10b225e0df1583e73b867c15e9b9e587b8ee0963312ee4f7fb229a50e955ae41958a586e6861856c89e4745ac51218981ec1b827ce1a711c5a9edb70dac8bf813ef066a638b2c26f62222f41eed29fd0381494867d5e83bdc12024311e9c0918cd40e2ec3f961e4efab56d9a9bbba7963cd5755982a93fa916972512420164d8ec36aa8d86656d731d0310403452aae25513e98ece726d0ae04993061e45d6cc903fa81d8f86fc77b0ff5b3939a225d5915c6ec9546616cd0059e20b068363434664fde58bde7b491654c0c7f8046b2830e205c3a3db367620db14be4214a228f332962f084a750e240d6ac866405dc07fa1622c0b37ef5aaa8ba431ebbcf0a8d1da8bb0441ea1b787b80e580addc63fbbd3f642487adf378f995c2c4128cdc34ae177a56f0918747b29877d1a6268ffe9c4b0922ae1c203f2754b1fdaa32ec498506ede0065f585fc9323a2c1f2ab902259517b8f61d90d3baaf38d30e7b87e957a51e0be55f83b8bf37dddecff2c55b407c10821b57e627115625eeab4a115232140178c4a70b04ef925a926b01ab09ca2b68419580bc60886effbe4777d648af5f00c4fe76f12d8407632bd276ee9606faa88a36ee9e45c16caf69c496fa23e8d018f27fcc2671340e7548fea0d50f1d6f08291c8ae781e90704768e54b5e8fbd576399b58f6530cf51fb28ec950e35a7c4ff0ed45daec2ff1c4dde914e2708cd7ee0bce83f8f0453d077c61a9fe91739433fba8c83fbb8a2e78d4f5b677144936216ac51c9ed0b41f0930853e37475e5b7ecb9d94648afbd57d0cffaa0a5de9ae1b01950a87cbabf9ad6ccee9d6f5a2060916fd51982924063d687e9440e58367a906883b63390b85878d0e39a4a09a917bf35c897befd1cb486072538b6c6bb02804052070fa50fbfc53070e6501c987ce78f9470310686440fd5288eb571c0d54201be8eaf744291ccf8a2d64321dc3d0f10b4ed002993c18cd4021152380031d75e22c05caba12ee4866aa4a28aa06c06e6b935129c8da5c5ede70b98f07bf07999a5f1462e9adacace45288f0bbb4df00ea92db2a62e7ced8206a35ab73c7def8a786bc2e719954360f1e1b2a105078449ec083fe213e87916b2f166a11bd62266d1064cf634338440a3f9824a702c443da66b518c27fb7f16e7f17e5f94f0b611438413ba1fe7835bb8953c38023f1756549ffe39cdfe2138a8e25f05b133ef886dc0958793770d4f2e9c1d97e2482926dc7e6d5e2193a754009e4319f77f340c281f13bc25c2e18d70115d2a874376dd9fcc68a2600a7a204995cf26d6b8a4e03bf67c84927006d4c261ee0764992521cd7a50ab6da421cc4a83a46842ca58e560f43335890d741daadba711d2efbd266a7116988de0c3c4a4e087ff852c73a3ff4f123145f82b6e8247caee4ed087e34e4043823cf4655a6ec84d4dc551568721798421fc287f297994220aa430d3af060ef3acc9b9a3a31e621813b63267a3c00b29dfa6592b2290565aaeed3519cbb0b035c78494d4e603b4565691b3f8552e158ea4413829a6cf65ac720cafb18919f6a7524c7939f1da4abae9032ce95e3eaa1721bc327145c17028c4e1ca7fb763a4c9e4f6199a193038662c6642623644a0e516a6f0549d16f00c662cb1083824b5eeb823af166826659765851f111ff271665468739db3c3c657ce289de2c6aa5bf810e1a24c80eb169f2439e5266bc3c88c847bd501aa21529c0bee9b2b5bd989201a60d040b95af421101ebc0787ad27bc80b3c38b119bd267a1f24096b18000f13f380b65a4c07c18430fdd619ef547a5dc3a381381c63213a7da261cbc112d478ac801bf8ee9e8e4ac5b2874fa83455be249a3a835c025517f6a13dc5c0f918814e599b23268d73c176e02c8bc28bda2d107c06ee43c9474ae269b1ae18d4cd1852aef2bd33624d68e4173f730ea52370dd99281d91d54cbbea84ed0aa09fffda065080bbc5add7d029bba5d0c62f04441fc906f2704dce36a91c27fbbbcbb069709dfd058d565ec2b51117e5e5fdb7f5c3fdb670ab94164ab841a346aa450959d04b32c9d854370a2a9723296dd93ccdb2ed5ad0ab3193a44ddd17664fe684e8c21c9b6305379f3d6abed9cfbf97b2b3bfcc8b9f07095e1cc191f37c06abcba303a5a7a3671f0789cd5c4c81a81abe69c37d330a45a41860012ee703f5ca2beea680de69642a549f0e5ae44a2f97be98b341b1fdc3c5266b8f9a08be3bcbf8c44ab132d780dd6192abdd2afde853d31279b311aac448a6068f31e63a51085aa8bbc5787d852eb25388092da847354755a01bbbc9e21080a3c7ff240e1c25994046001f24cd6cb9434818e7d8c7ccb807710478b479c90097ba7a07e0389323d65445c4037aa4488b49d71a79b67c91fc30833f6205a45b921b6d82ef28e2675392049b692926e2af20801d05b446cebd3b20f7b3869b162c10c6e1ff0cff1bdc748ad2379a7109ec5d4364d8f99c782bdb3c182105d119c48182b1e65f3327c654b6f68e2480698befe97de46d81c8ded7467e1b29acd660a433a22ff4e8a6b7d980fc040b43b701a7eddba03f61f1027a6f2422c8362c21fa88e85f03dd25852ea78bafdc77b2d695b346084cb1806c096a1a5d57b89f14acf50b5422159f15cdb87df914c8e8455be21c10c6473a8ab19436f1c24e912f8316a8ee4db0eb107d5e4bc46981ab6f5d6fab513d2b97c4026166f16d933cd709eac61da500ef14eb0b5c0410d4ad63930f9a55d25275958d892b6890d6f7065911baa28c047f2da3012a42f228326a7d842eda6ffe7729dcb711c109dc7eea57ba2f69ad07dc10201aadc0da9610eafcd53f8a1809a7a5ddfcb429adc65ba1ca7bdf52190e02b7e1507a33d1a0f4f027378319a5b01ccdd5145dced6b5d6326a9dc5c7bc6ec67b846f03383bb43efb4ca94507609d7be3b2a43b59d4ed48e5fcdc457c3587d6767e58704855f0eb16453a237d4b48c64b314a2bb6043090031840af1421f7a88c2630001379463051649d282d1748b7053eb72559d617a085a62f12795bb2e21c2bc0bc05fc14b69021008a54d5256d1c1490fe4df025b5ad774e18ab3fd6b618fbdc25d5bc019a07872aa964c24208853f8c03899f220a0b69e0d37012764d711fe3267829401a581c82314db1ab3b0b2bd2e2a69e52a39d5e4a0f5e4465ac4e9d8cd2d9552769249d854ece14b5ebdc6c2b49d78016111cfd097c6d2bbe6354a34207c87550373d30169b4cb5981ce7cc1e51253cb45d04e6d0f30c2181dca35783d8bf210dca7c4f313fe8bcaf0a0d35527892668c09c1ddb0501e6c437cf90de747530fef557a24b83035e928d8b70254594e3701ce9c46dd37b639d0129dae1e4652a1b8dd0b656029e61834e943fcb12d8b343d96e53c7a113923813f694d2ff308eda77115ea25e73cacf4b27e2db5df7525d59ed38f51f95a0cb70ab41143031c837de77c4048e410961dce2666cd5420a21ec68b9213eb17ad8fe9418058d93ed607131d0c910a9bad14508f207d545bbd1dbf00d0b01660583fe775880f08c91e87c8b85dc613ec54b650ac99c36214e3b2166272f41b4374b1293cdce743bcf50e5d80e8e29b9d368cde404d7793c6f1ec352ce7be7ecaf06456ce8912d4cccd6fef22052056a5058f3edaf7e4093905f99cfcbdeffdace54498064f2c5ebc0c6ec135deacec70c9b670fda471b750999b7743a8b3915dbe0eac5858c3c0a7c997638edbeef9da372fc2c33c5476e47894e7b1360c4c6f910f797af0d9f7856f907c82d4c918c1e52e70efb6008803c1701a02e737a0e56ad1b2919860f21ed1481841c9060cfe7add2110d4395818121a1ae4b061a1f328eba11d816b29668e44feb369fd4c89a20763d08d7fd5d07a7f9b71c8b177df36450862a31fc5a063e63eb2ed12f25d854e62950cfbaa795705e19f76179c5cf3908603b03df7236beb117a2b1fd46d6287f8c41e9f8782dcbedbc534e0ae89c8d3a5797da001dd0ac573fcdf9dde75615534e6953b962e7884fc9d3822434bea43979178d0228689498b6af8709e4eb7ddae1db95723a6a5abc4fba74238d3122a04bfefc98d37180573a0c060ffc5861afe87888656b974a8fd108efd5fb775ba170bc7adb324b836a192d01abe5b1eb9ea4995b6aa9e0855c042b66e4a79bfc0c0485726c0e8555c32d48dfd0742a34bb34870d0a4a4753cb03e4ed838a52448ac4d527d22a7e40e2caa2bf87baebce85d2ea9c4dc6de95d829fa3726bb6b0ffab9f34748bd4f49707fa8d0e760afd5a2bd9e6179965f85a1b7e1aba853b3c76b90077a72d4b0d04e448a8d79ee4006019afd1de80fd7702c61b1a6cbe0b700331f48d8b69fd03aae3c33313d7c74b352d124458143bd517026b53926d15925f0a944e36ad87e5e89e7ef9c712de7605493443cb505a14a7f0fe6509faa8d4035d0e513de7eb19839f4ab9897c65d9c484150077b6272896be5792f0250e3670095b67b4584ff7aec652b83cfd6a400fe8bdd7ae5412bf5af50c8c3fcd38e96a9ef9b75a166eb4b1042b7f513c5d958bf0307fa39b64a9b1953923a8398439a2e440c4520853f0ca6576ec923a869d13d6de3373e9af74a3a715d15d66e64c15a325dad49e6d4f6f7ae4c8a76494b8e2ed5cf070a2614c2b8bb1d3cec688bdc678239d302593af4de8a3d4a1a3039debf3442236403725c417e06e129678401b48d1d07f84af837037f542c04bb4987f424488a4ea9837c0c1a3b34a148800addc64244059558ab26c2555e999be7c6f1302d92370a0d20f289a81fac330353fe6b12254e7a57f1b87b9bb6c69309a97aa40b1561df699b66be1f304dff6c1a1dc6b50c699e5eb134a96888b3d5571e33430e6c80a084b8d3a38308757c401d6cacc0a6f7cca82bbc6ea08289804466f2ce5effcc6897c31385d2ca5991c37b4f439c0e6a610fa02af63d5988979d8aa095a88ea1285d4ccae888129519ab28dc5e38486bd01003481e9a43e4a27800c2186a27d34c231a36892c87fd46afed57050357211167161b96deb325250030c208a345cbdac944a425cbf2d90847906002840a24aea640df360c634a23cb2517d2eb8f71998aac24d34183202c52be44e909a21409bac7c5f3bd79dc7c5436163f4f25a352fd39eddf0b7ea5a8787bb940e8ccdf51c7cb0f5f4f2210321e44ed2e0d9d72864212e362873093db9ef37e32cd4eb40c27489267fad241b277de090c2c0eea86a9fddfd8ed4f68beeb073a1146f068b7428d51f486873a4586bd302db365fc781a73b9c471babb6b200fdfffab90bd1d196fa00b7aef33a83cb60b25a203ebe7bc4b2767c7ad18f46708d820419138668c03429d3696856421cc692027d1b200504dbb9f6ce8e60bec48e1fce274708fee4c5dae77049852b210e3f3d6d0827988de70ee544e97ed08c36fc99e72b8a9ce5e3130eced7a26bf609b404426e5c418b22d49d2fc1baa5a11fa3e63edcf14af91ec8917bf8819354dc6d6d9fc40bb342847c0d246cc971e95a3f128050a995a8e74d4feabb9e88e193a4d9b143cf78da34dcd2523e72f9dd13c4b6e300da9ab944e2e2b3f2a4d1f49780d0b8764a5a933b9db1310e2c1e1026e6ad6c58cc5ff59273cf4f355087994556c99af5a539c6753029b7b75cf19da96a3125a23def5ecf7186d2d58a61e43ccb6c8853de7c8cc1c2904615de68d9a7ff37d9e309071b69cc6b115448c8986f4b06e7cd821771cbd3d893a0baf35bc6155408598d71540839fa6d14e3607390e77530698ca9524bb9db4ee29afa9f01dfd0f4f63712e38c68685b485bea183b49883104cd7a37331f07e43e334bc581f4dcb99532617cad4f32e504cfa0cdf094487d7377073417fda00037103d90dc17e102630019101b6d8144da37235061582c719504ce4838e41124981cb96e09aa7dbbf1eb7b10cc420de017b11d285e4f05373c95a2f0ad2a23341a63b836e36e166f7be6036ea463dda886418cd2899bb86ad1699644a8221b3c11f88a2180a008f02bd58083c9ca070a31c0668b66499ba694e90631e41e20206e9661abf1ca6a37a30801bd2bfcea0b97f8502a916de99c65cb5ed7cd52720643b1aac99e59fdab390096e55331fe6941c0e589f41182e400a06ac3ef86d3f075370e575e4fce142ada103dca2b355ce3338980d2ef29e0c391b64d2a1aeb11b7234ddd5b544f53380a3135a66ec1104a3a9aac4f548f458f0c2210e5944d4a613a4d3f584d321abe011f5e1a3b0d6042513e633d3802adbed5d9954fef15b189e78e9824a08d68d425c135236c898728b41e5a275562488170123b632afaed86d0a61215f08c5356d5261096c1865c485fcaa954dfac17950bc36e996b4f327e7033b8fca01ee935e0e779c9ee3c886e284251ba707738bfb6c8cf137050ee80dfec5f1a14ba43fa2ae27437a8be69ab9c2b6792f9a71cf21b310a433b583560d944206cafc2bc8b61614c3e16718c6ddab06d96a308f8cf8580116fd84ef2076cc93dff9b7eabe979d2e82c31f7319e6fc618b062c29d1f44383be866c28e284f59611ddfef647d803183daaf87dadbdcbb2290f7001b1c9956e8b8041c8c786457f01f550a8d0c0c5972c845359895271b995f530dddce23441c538b0f91ca7542ca6c3902731fbc02f7e4125dd8e8db24a9d6e562c4a89d441216e4696eabd033666061d558edf58d6f5fb94f2d872a315dc1941c3127f82127f0434e7482156687f423ce08ced70a275749e2241ad1a4d9b4cbca907b321e2b64b295452c2cd6d3c5d8429de163093e3160e694022a40fd03d84d3c330ebaf500becb8436aa7b2dc100e5688ee418feaea0a4b8001caa02d66a10b980ffcdd55375aa6a17c633c488871c5297ce85606534a2c7bf8bba085118622c0ceb1e873aeb69e01998c19a5fb7ec09473aa0c3e348b81ac9592c0ffad8c2103ccaef1b359fe95161311ff12617b4c1a78c962d584ec4148476f5dac04772ec2a28c45e2d6dd11d62900ca5514b6c2e982549490afa305aa4ed2e6a7fa63d089a4c510270bfb4286564031093eb6bd665db3aa3b8190d22bf9cf9e900d49abf10a89eead895249721bc3854991b2ee8f044a706a527ff8cbbdaa97c824abfb89def1deb409670a138d9b29fb3bedb14ed9f4f4a805879301c3de9cda6d02c3e89d7bdf9a1a87a2391125eccdf2e34340bfdf81aa880bd4949a5798f3ba27fe2ff998c7c16969d048d9de41e30d23c09559dc2efaa43c5d99fa4a00635e5c3bf2d4f1b37f07b33609a3550309258995937015e16922ddb15d265a51567b003204da6e3db4f161cd948dba1a8771cd049a48cd19529122f382f071a2652f56df86f71ecbc07e5eddd0a336b94f0784fe83843542b774f11701131d41e2a8e7402200b756cffa04a9555d30a3c99703b746ef728fe491b508c066378475e2ada6478550da06425327b77767be0a44e00ca9cb743e42133617800dd3ba43456a761d9a655d9099c5f11fc129b1b0d1257164019545ed492cf87e0d1b56f1eb3c2eeda1c1373b3896af229ea9cc1237b02043f180c8a447e375cdfec9c80a224ac98b4522aabc911a57c9c2b06266ac0bbb78d53b92bff0513deb3dc480ba90d8d2dbbf766bf45ad5435c6aa5b53827212275c4f8b2e0a5f306e70c4a82606847e0474d0d6d501627067f1172c916aacfba2ed98187005070353d75018f030d44b2b8a871b52c107e9b9e725bf6c91cdb6ffcfc6e21ec6f31374c9aec7d037e15325e3b84e93cd5fb63e4a6fda0dbb5e19696fcaa62a29518d5b828b01c6a0a34f0e5463383454d3a6c3d9192c59b712d771a4c69596c62f8a78a1516b670a10ab84407bd589ac14311cc9e0320959261da9297d93b6ad70dc22e2d71f3da30c821f0dfe5b0db81af085b77b2cbd606b5c762cfbe5ad8315ed468bd71ab850dec483480106a75448145c63a2d867574120ed2e41d56eb7347ad836df8a8604487a96d9be389d9e00e1b0d86bf2794916f3c25ef742cf4bdf0294a59ff55a2597d3bace8be19881ea1e7f8c4c467ff4e4994bd308d1ad4f8f129adec6c6c508eae44e38d0378127982209c0ce963c6623c2f8c4df838359fcb1fdae9046dc6ef1c8e4fe7ee56d9fff8bc28b03c24fcab2de058d8640b1c5d2afe4f802ca9de700d9de5acba60893fcf6427aa96a77699f3012a4aa4fba36e4d814c0edc4b2e87bda9695c70fc77805411aae01c6ee26f26d80b82fd7b29441bb207e178b224818409c5f9efdd6f93779bd6acc251370b144ba56bc6f139bcce874cd9f05ea43c4ee3a438525e82cd9d259232e71670c89f3f9c79bd6f57d206374493fac549ea687f670e8115cef41632dd4f4075b6290385f8b08a5956e22e404d043c02b065a2bfe2a449a1723061b1f85518f86e0f7969b6aa0919181e1695b846b0ba868a1e71416238fc82dbb0e8ac20e1234f50abf345639908d49fbd3dfff5db6e6a786e9d0f45fd5d3fd2f9e49952ce271a1527d2ac711ad8621176b928634a491c0d14c02dedd67c6586237b4000a578c0459aed1cc26907ebcd89620c1ae57a80093b56ad11f42b20b43603e25fef061f90b18abe5913bb3be060b05fc28aa17c300c499c8e69248dec56d604e2f9de1ada384f76c5694323d8c80382bf56850e508be09aecf8844617903928829d65065a86bf41561e39ae9c3da3caf9f281c9d18b170a4b8b1a88c256aff42d61250ece449529cf888111b9a7234791a9b35123c1c66d21c3a23c89dd5b184a129005780a01d19428c00a916b6cbfb393439400a6189d6670bd6d2f497ca3b5ea082e043031dc40e73c3d7d159eccd9a6c57f0bf5ed867729b0aae0bec041964f227702405d75101f5c2a64072e14545606b9f2e39d5b576268b730a6bf9e2c0d8be539d431a65b7a41469a21c73fb22c9b2e798af8b313da6b67b2e3117f0a60b0e2fd2a39bf1c54ac7a901ec3a8a2c28c9ffa7357463f0bea17eebbc09cf0512ff3749b44eca319740f13fa6df1601684aeee2467ae3e725869eb7cab91235e4f017b505acb73e2678901985575e5313ccb8a265d7c87a53cc48c097471809ac46fc279450ad0382d7e9346bc50399811dc4503d3e8f8ac0d18d6cd0cd6ef6877bf75c23b761774ef9d81243d0182f9efca3aa2df9e07890d4a002f87c43be7ea37826ab1f7eb026a41d95f99381722c6c33c6556072d0af3ae26cd9fa8e5502bfc085fff42f9af460fb7374059930ed2fe63bcf53c2df213a87bf56efb2a089e917b90e61ca913720370bd38c91f3199b377f281853c36d48add2be184ea59adbe87afc86f58004c03925fa4c4968799122c8476c7e91bfb319834231648d25178036b760a91d7f1522c9c448cfe82c8b304f8dcc1dbace8494339b0df0030c54a225c1e4022ad6fe08cc1e954901931ffaf88a0307b06a519006dea93de1346c80490e82244b90142592eac92a4193b8a35409abd19bc4a28ae035ac9243445b308147ce657f908dc5acdd6652a0e5a94fbc22afe7ac68813b9735c881624173ec660f8d3cd291d246478bc0ff72cb3aead95adc4d71126971839744625f4030dc779e8873175583f07c925c71e9691acaad8486cc418d21485a312d17c5d8b386ca800f07446a6d31061bf213fb78c047353f82edcb73a3f71c999cf5c9141e683217a5a049f795a8332d0419af0f7bf417308656cf3335fc17ff4d499403ad1925caf4bd3d428f271c531ad68740c095ecf60670912aa9fbb613b35fd917e7eb322fae7d198bd63d3a21c775408f5540e23beb1d026061158c5c3d44fdb528218ebe35563cfb3695995bbf3d8329e461e7aec45302bd93d7b39bd152c8fd0d490b6751c04284681cad1e438abd4f6ff000100e8484bf4f70638ba5182ed5cb919d6d28d0cb69a60fe6e2779080e5a2bb7714002a45b43f6791a29fccc9722109592e0ff25b9bf76203bdeaaa1d5f3b05d54e2e05202084757bbb2526ba294661bfac2f25e81e11ecad2c0201845c1b66b2980a81e3ad445f26e95d4ca75599d9c43a49e047b472f310d8f1a4f60d213895f0c3fb9f5493140e8c7c33dc3c341dc7486de836d132fc0f910f157a464b339a066b7bb1da9890c20aa9131fe06cde2ca70ecd3ec76d83d4ccd559199b273b0da4b78ae1d833f26ece8ba16a40b0f1341d583cfbdbb7132ebe71fbab39a93e16605e017ff3d61b40df9aa401ff769d3befed4ce91b7b7db50e7de8ac9e1327196dffa6bbb786fe2896ef208a141600816b1fd354d7bb727ccf5c3aaa1c643e75f6e8944d97d1291955d1292895cc859bf1641b4873330f2d378bb52ab46bc1f239a7703786da77ebe3fe9e4eb88c85dd10e14b9753ec73449433d0d71b5ffb1063866c8c4639e58b243b0fab2fbee294a9bc99306987f35128e03b067641ec3411728349327a5dd10b89d5291b204105a8d7b311e3f99a4c7ed2ff8266b0ed47d29579bc06ddc2249d61772ceebc9c15ec6e64673784af9278a09eb04fdfa97d14cce2595a51a4cd4d2727b6c8c7a5305923787e122eca6f7e59e3182e7a794446f3f8544d423b23cab6bb4bbc3b5c00a5cd764dc8a953f34049c3a0f23ecb92faf6422add9e666148dc109e6a9d563b44e23927c3c1d0d422035b5a3758e755c10372d288dd67201221b0427e33bd1d1a955d1e49282c854a740cd92bcf3096b4ba67229d85ecd1a0529b49e85681522024f97aefeda0b018febb2644992012bb2f667f431b5cdeb2857e9a770a874d9feb4d7b280e017aea0537f4ce30f97323a0d1038c026785efc36326348357fb4dc63a1ffe3a2cf58fd331e01fc4aa0fe736eac1a0203d02bd879ace940f0dc5899c984519254c1f2eba2bc46cc511ef9596ebc5f3a2e24acf389b1bd4aefbe2589cf45b0e87e2c189a73c380cd1199ea0c874bf2d5137fb7e4d5e5e9af28f942030428bc77c839f6b94fdc1ab0327638a19f26aa4314d30c8ab4ad110eba06f984bb350657be19129426c16cdbe92591886276a1b1cff79120421429604ebe3540413dca21b237ea9d80048a215a1fef6b7575707b1d573f5633225665bd444ae761f99e63443c6ee9e1f6b0d45b0f4ee539ccd4b08b946be71c16894a1cc96552df049da7fe7f932e2063e26c334cf9abad3361b524bc32d83bc9362311a0f197d4966593ab025d3df9d2756adeef3ca269cfe8e19836b1c7971fd88d9d068fab7f6e883f632d12ba05bfe208d1560d483000861d1995e3c2b94a0b753b7768f12778bca2bd64f0d3514abd401e9c832417b48be4d93dc6b051c79ba9c1ad0579e5f07fab22058a954c749476af7f05ea9b71d3d3dba82822725a517595f629bb91a194f0a64408a7815f1dd407dbcc0968ebf28e3edae2a17908b78522080607996182ffd78f36a160a1c67aa6e2abfdaccf41708a35467c6bb2c86e20fd761c16cd03221ac19d460b44c7221dee3ce04a13c24e0a67a19c936ba07d8c28ab2b0095ce15315c8d8219e0ac49f1f3e2cb4306c07f73b7f3eb819c538cd39948a940a1eda309ac51e109c669782b8dd0e0e7e1791073a85781df5264204bb4da67e36218a4dfd22f58e09123a46253399d7919e0faf2bf19d44453de49853f56e1c87422f77686f152f79380fd92dc41dde5bf1f045df06f25fa8e60a07462f23d74dd9fad1d260305af891be115a138ea3baacd92b6e6dd95d19972bca1858b7647f70c8b0aab221131ddb429be95a0ace4b6da7ac898fe424c1c6743a20027b62786d34a7cf31b3c43e7f422407dd0f1c227fa34e30154ebc4f6fbcbf37fc309090461050324cbc43af598d45606f004d0bc510aa877dfec1323d62806548fc05a66126bde2e2b9b71dfe14ef0ce97f6dd89aed057625951d995e5b11ed7917f758700e50977a5a0621aaf1c0464495598742eb7e43faaaaa1f2e04eb2fea62ed5711d541d2c7044dc2359bd453629d00e2b7b2206132112fe7aa84e49164621690056d44974cde41e6e5e77142300da6e41592a5f24ac1424c188a309fe233d36b34f6f8701b38be4924eec1728b84f169a4aef77808eba91195a4c818d85ca118ffdfe5a8f9f0766fee94ac4dfa354d7da3e71829b4804c052b9060f85b37b41717f02848d9d3a9ad2e24c63c190926f645c042506730f3b4de923e1c1e67a025448f46ecbfa08414433cd4ee886be5a627780ee0a3581e0ffd4ce308ea43a7334ddb78767c31c54d9e3acd9ba5a90c47c355dc8baf4e258dc2d3e27eb1c33ab86584818417b55a2c5f60d4200dfb62344d8a12d35231b8289b9b8ab24c06515584ddb341b85fc632fbfa3c92acadd518e24614159de187021e950ab67de8f9e9c4aca36d2f03f6d164d4e1e88926453b7ba4f1117fcd86953e07dea81413240c1700fa524c20b4008f834026051e93cf7c84cf439100ae7e46b353d7b1eba82a77a84bafc34fb613816258a2cec9e9b22f8c6063f5719e680456a18084a30c792911f8f79f3a8906a8dc94e26469757e7e78a7a1f384dadefdb13bc58d1cce79e420af1a791216a8b1c1531cfcc81f46078c9156c688631964e03b4a654fe2db1eebc8a9e582d80882059e210c2980e01205ab37b6254ee8953a2c30fc178d55b5f9730eed7011f81adb71515c708e2c019a23c88b15f3210e85091c03b462d2a89cda18717c2d59077a17f2dbbecf24de8b9c630308ba0872e2a19fddb650a0a7d3cb7a4af7f3eb80ad7114ef2a4987f563ba0002e077d90d8188c66845a74f7cfc72dbb9cef50c492989dee88f4a2b67b173d53fd34378bff363c90601a7050e9c3026c19f1f2400eb83afc080ce0f1f7677fabf8a049f3e3649cb31f7d940b91511dce2d2868364ae2388851c3ce220b4496bcab62f4d787b4292e5725de8ef82c977c3e24a45fea32281facda2e329788c755d38036bab08e34ce5d0364de306935f42f47743a8c3ef90b23b14391931249cb7eb00a30499196dee6a7f9781b093888cb25a64f58e0d4378c9f6f9ba377bb16bb2505800c38263bd9475fced2e78e269520c3ac641e710dd631c5fb49a9002c7a1c92546803935a676f9410f5dad80c5e846e1a0a27022042e34cbdcb3bccb34a4dadd42318d1cc9e0bec8f36a2976b2f404b80eaa48b99f56d087ba05bc77a126c84f42a177b07ab8043ea6bd16d96a3b60cf4aa21a7bec2885c1bbb8e9c46bccccd128cd4b63caee7bbee40eeb831df3f460c9f0243b61e6f1579b58194fec8c5444c154a8d9d94e67d05d5f51350a315b9671f0cb88a698c8708d816f418dd5ed4abf0416e9e35393e6ab24e3e266b165da91f6e3d9f7a8fcd0fc2c13430c5d556efbe67471f676f429cd44cfb9e10517d91ae5a6b9e9035679e8a198671de19a0a1139eb28b8c8ba99e549c1262c72757999f724f9a521c467c0fba44630bd4e326b4d77a1dea1bd1d4b8b15fae9bc88b4eaf923f93500171e52b7f7281a29a1e7836a75e4d3699a3674f18200135791fce29e0f16e29f94787bfcdd375f5f26d00c8d59e4103c1880e3640c0f99f543b7030423b7192218335091cc79c1f249410329a08c674fe11e9f1d23c40f458cce4187403d577f700c43364cb96f6a8abd35e6b0beded727daff8d6dd07afb35e59d1fd571a9ad0570cce07d115cd77e9172da438e45a6837a6c092947ad2ae6105b3f8a285dc6dcd63ad7e6fafb0aa9bbb0f45233b51dd3e72b1f18d80d99f0d91eb37d134dae2a109b59bb685206a74ed51fcbab394f927705ce1c0dfda99bd2011b4b00c6b504c76163edf7f3ef21ebc2befde778ef3d6dad2eae72f45d798953df6f4547c2d6abc2f8c7eb179fe9ee00d8752a27fcecebcd80220274a801844bb3bcfec3f12daa6b965019ccdf49f762f04ff56b61cab8fe928efa8aa8708d9c374a58dc4e4ff9aa71f146a6353a5099def3fef577c07278b338d36711d4f84fb930727345800c45e1793b4999230d289810c9494888fc1ec443d53100045d199d39f52b5717b64e021093e0ef308dceaea505d60e50355a006db90ed3f283d52df0c1cacdb73d738bbdd680b0dbabcd2b1e01659d965cd5f5c24d88110b06527861652fd787eeabd0c6e93cdb57b18fe9186d248faecae28809e86b336e4089e201122240e9434a45db44b38fc3ab1ec057920d4b288ee0ef442b29cda36b2d301a9978ac795e511c38030cc0c73ed4f047c5962680f79d3a07b9bbac5b4f4a3948eb1e3a114523c563f48174a765006f13921f3f4c0eab843fe57ce388ecae151854a42fa8c597f489aaf6ae5dcef1447240a2c531f18f2e54c5c4fa5590f78c8e3388e7f84b729dc3eac8e4b40db47e74b6af437b9ec45cee5550147b1929af4a0529d01e7187cabbb6c0fad3d4d0c3b85eba42005122c53b97f44478cb17fdd163d7379e858d103479ef426c64ed9315ea8f4199d0fba9514c4b17630ea308400a754498d79bf5fa2892e0ca1d5f840bf3de59400aee317921a7f4d457321f51b343e318d8dc56fd91c37db2d669912d05e20918312cde37d2f7218cd685b3d03da5a0e2a7f12cd4509e9a56dfe3d5f30d4dbf1cc66a4a227c78258432cd4c1dd1627f1d5c2dd433d6274516975db60c2d2ef69ea77c0546ef4e346085d1c04fd4b4ca8b02138f7226c97300b7438469c06cbba55ae4c33d82c7d338db7f92dfc8842ca8107139f046bc938611ad160ab1f80d35a76242e18144b8e54f5b9e8f2a2ee5ba4e7e0ba87fa825f6265d79bd136e496d2960ad38f9eae75844012875f3c965a9e02cb2347aeb047a16c45aed77c80537c2d2f50d8b10d6f211bf8bfe592992d24c45544e5b6a6b6195021d82a653c6a400acb4d868001ef013d21c291e3cee53b1c28c8fd8d6177d008ef3a2c4624e98f92c139fe33cb4bc21e2c09d317f8141da47a1c76a85dbda2b8b6a0f9cbe728a3c1aa2224329a908f47f29b1c803a26d62611120c46582a148ea81e4d1cece283a7d82a51df2ae8d47c495de584947cdbe4b49f18aea78fb20870bb1b75834eb9d868f56c57b74cfcc25c0226539a3ee284410d8f6ae86f0b2d12334e0d887b9c9d445c3c02311770d11a0430ac5c7223a28e9bc7b1e86dc749bcb9042cca984b40e42bccb6e6f949208d7a80539edf8f46f67ff01457e2c29ed568a5b8a831da726f5cf17dab6e8237db442e6046c4ea1e99c26e53951594b71069d5ed356ab2fb76b0a334b589db848b0875741b3be2fd531c896601561b1d05774024aa768e761a3e9d40875f2ce1ea3ed2c2eb713f128b430581889e1265498b076d96ce881a309f04ff97f334210c40513a81b0c64a2560b93a11be385d7b67db0c9c44bbdfa55dc897e59af06a884d29b8403e24d5cf9d791e8b7f058994b643cf05ee1e0c96ebc62b165cda14a9893c96affb9d1161603f17a4d950f8b1fe2cc39bfc56bde0e37b0f53b90881c281999c9adfee7f0a40d2327281b3b374ec9b3360a927b386c176e69d83c26513add180bafd4ce6dbf594fec90b409d720c4aa0432767e10ea8068ddae410e2cd4108935c46c1674da1bca4179b0c9afb7e944001f61dc960f2df5ccb41875471afe8cb708242c309687012b902ad0af0e63f14a0ec682a6fad0227861c7ad257665ece26f7008505a57039b234cc517d768d2f5a2ea7c1b0d06cb2111bdd2a336d87f3dd462c9f51d13398d7c7623fcfc981a3d95b4f1b6b3c2f0380f61280f78f167bd10e052621136166ecab9f01b4efec2a1855cf8ff2f1ef1400b21d671b925fcbdb0ef0656bca2fda74be006895740b1d0a6fd5e551826dfd4a8ec5796c02533adf0462fc8bb9dfdfe27c97324ec3776c4ffbd6ee9474e5b2626f930e7a47b4f18c334b96c591e530fb10a534d64f00985d4dfca93c8d8399727c2703fe4722138213e862d143c7bc959b61298e5c1c2be57bfaa400dd98fcb3504ece2a8e4f3d08b32a74ac53134abce3d69b4cb83b9020e7df11437c7c28f0b68cc42a9034910248d40215b3e24611c18fbd39ee9403345f465088caa455cd078cf8bb30db5259686d38a5a549aed536429e35374c7631a4cc827350ac24e6fc0e7e8c355eed6c0216d4cbcacf574059d0c837cb2f21ef684a6b0a5141e28c26434a5997a34ae57a55b04ccfb7b506ff8ea0af40271d2a9c26a91405d235fbed1a8a8298e751389201f45840cb1936976c598a2b1933390aacbc07ba7b120a36f1eb52632395322f0ba9abee5b01b08ca7b2ecfcec006a0f363578ea53ed4b70e43c14d3162e25160a40498ad98196b17cccd00982560f2692aba0fdb1d93aa9adadec860212d67be40aa5642a85565857408a4ff80d9ad801dc32d6696df23868b2fbaeea10b3e4270412e45b12fdc123c4d6636cfa81784fef670f4839bd192e6d30e16c47c4853647f8a23a8a32d5e03e5e642b8e1152f4463064ef017e23504be5ab9367fdebf5d950b171902b504c8af6eb8b95240748369b1516bb96c94e0c18b7dfbfe4a23115c7305189dc2a30fa8f3d2ce2ee683a521406031f01c8911829f554aaf653a8a1f7c23ef3ee88d7e76d347e3cb2607458899087b27a5b9681c722230a91771bf1bc10a9bfca9a37901320c20c9b9f01a014da6e63d637d2aebe1152834b6a4c165c7e811e72c183a48928430ca5580685563f63536e12e685c9a5ff19ca8232e590c93f74b18051efe51366846362d7f1a4ad3eb6ba737b1eef448e2fba9d22b8b33ad7e38568122e201c9cf65ad6dad2b7376b7795d7d9bbe2083d29b177af9fe31b43412dbf90363b7c4bc4d66416ba419bbd265ef4b6d1b87b714b43617af7d9fccb3873a8259b428cfe4beb455e7c29866054480a2d09d1c1ce668be75cde8608e7ce7449e1f67062b39b0ec8d74b55141e160e7deb57c8fa49592b902633f1ad70f64e4a95d0a68f9a71bb8099e42d562c44b1b4ee22f4b713436db82d5016a111614016d6744b729aca9934422999e4132a5640fc04cb72b80132526343a551dcf74b3a8c5b1ebb7c7ed1a6af3a301b062e7868406714e2c7729e008d7e1348b23081298910669b91a26c2050e85b8d7afed4e00ccb268dbddc8fe86136ddf10fe919ecb663b40fbf34e0499633a89de7a082b164fddedc77539505c7ef0d28b23c053ed42b250de38a5162f251875f43c453fd14c24b3a0ac00a94489ae21d186ca2c594813448792aef010fdc6bcced4165d9445c504f1cf65575352fcf807db8a4ac9fa1f5f2eb46b67391805c2b078624cc56dd8067865a58d17ebe39e7a2048e08e91fa27115f46f454a019b18af4d7caf39602190df7106d80a066e52b8f008733665dc3a3875e5513b732a4075967312164fe7f9b99fb2a6dcde885048559695e0942d8e7978f374e79fec7f808a04ff1cf73e1da01782c7fffbe36deb9d7a6303ab80801b7e91942fa8a6880ce89ce148c8f043adffbc85b30d6d53a077a70eec0bd5f99d9cf0dff79b0db26a9ff404b24f9cb6570710bff8186ac293c9f9894159ec185780b243f8402003070be36600834683a6600ae6e9d57a6ad81ba8529b4ca4d0de92fbccfe3e9937071932449a44c323f0c590c880c32f8d6a49e3a9a6aaa7f4f6e754f6e15857b96f7a42f9bbaf1591146a729a9a5297c86cff3f43c3bb3345bc3201ded66b3d96c360f92429c6474157fedaa926b70af926dd48d3f25749727414fb7c73d5dbe72941fa1305df8897acd9edc064efcb0eb9fdb891ef454552795a1a323ee8f5d1f53515dd59d9d0f2ae3f694f228dc8a8bd9935dd997b5b4d94a3682bd5c9e6767b31126c245d13a88cca07ccaa7681d210e74d04491148a1eade9fe668ad1ac2a2c1b4dfbda7573cf8dbe3e5843184d3b3bf7fd4ea5daf7ad0eac13d4be3adbbfbedd81ed018db1bf796db48912c5ae2e2a63f1ca9e381fa8a93ed763f583c505d9f5b9d5e5689bd8bd2c20dbdf1be98b1776205b0bd504220262e5ab5a6dd5d59e506d6be9c360af30bcddd5a52ff5e8f2a53b96807dfc1c0e87f32187138a5a2221a21ad1907a72f97a434d44abb334a27c1bca021628cabf7ea80995e95ea4ba8968424d6a0977319ad7a3f902e188e4eddc7befad3684fe61695259e78da3b76ef377a57803e74532b60daffdd1be478635b12ad9196d683b7bad6f9e46e594a60e5bf4b9346fc041c626c9e056d8fe9b234fc4b6bf8863726b01a439b453fe2b7db720928d660ebb0376dfd079d0833eaff21e941bba0fbf7b4b01d1dfd9be4f3e11a2886df3b26a88000429ee902b64d719bc0fbf2bbdbfa579635f4d12df3e87d4ddee76b7bb1d49d4244efe196a4ddabcb1c3e768ce8f6410dfe6b7a57f47e2bc0dfacbb7a1e5595ec625654de2e819ad675a74f72c7e5f2c4dce6f9ecb0ff87c7e2ec7e0fbf3f7fd5b78b0f31c01df3702be3f4fa386a5280863a7bf85a759029fbffa86cf677d03f99753e22063771a0719a0277ff328a5c96dcfd73c83e8ef7b9681d45c2d81fcc7bfda54d1b4963ccfd59247ffa84ddf9dff4a7304fbf32c83dd5f9ec1ecfe566087f683c0b5bba3cd1d5df87d0e3bd4e6f81db0fb4bd1293f7edd294fa37e658a36bd7b7fd7fea4b203bebfa7e3f85e8a82308eef6307381ba4ff32107ee85f5bf4e1d32c81cdeefe96a1cfa57963e7b7b946e84171dca239daa228fedb13cb8b0fd6d0577b2ed39cd77ae5aaedb644113d8e3d91294f41a4a7f3b1c1d3da060e95857ef39dbe2154b30c211d3e0d9b671041e0dbdd872820224214353bfcfb1c6deab039f6419e8fa8e22920cfe7a98a364bdbbe98a243daf4c88ed65ed6e28fde4b130719fbfee639a507b20c7577fa863b83e8437b2d58c35ba27c6886cf011db6e71aa4ee397fbf2c595e972d37ba12078b2eff96332def1906fb39648a7ff2b93447a0a965d1335ad3d8e6e6458d838c1da2fc0625147a1a96548e3b548a7e2c7d74d56ea1f4110719db03be45ff993a9c7bf3a3d2bcb137ef5906d0168920e05ba4cd11f4b0fd4be5cabb94efbaf4ed79e34837324c197b73e489d7a64794a889b1e99127ce7d599e378e2525adf18225dd2e65a9dc4164a6d4ff3b56be72f7f365e67cf836447aec18d2206d7a1f6d8e1eadcddf9b17f5d56669b3a97906ec846d43aecbd225186a3bf4cf09f5061406e80682c200dd4243f84954849b5a53888a88899aa0e2088cbe80d486911ec1816ddbcf0f5e97c75eaf5d43326bc25018ed73decbcbaaa5fce5d3d47dcf9e48599370e82f3f87fe52efd841ada9d35aeb904c7befdac461b4f3733e8362695624f60ddd8b23da48068d8d5f826da774c7cc22b1699ea1ce60778fc3688bcfd117482dddd692d1bffb1bbaaf43f60ddd8b657781d421bbd3b7554b75c8c64f1839b35aaa577569ee4adff0f1cbd2898caabbaa315449c9d1ea0bdc8c9b0267e3ace0661dcec659c1cd389badeb4061745d26414b6ce8bc3fa88c7d96f2cf504b8768f664faea792ea2d68da7facb959696cf11e357f40ed3c7f816d3c7f81d2ea01b95e17c7d90151c040695b1ff66e8070ff1b01da67b73aceeee4eb168826073f405d202fa725de0a6fa970657758bb82b973759d08d732969caf365ed20b2a3e5f5ef58f9f2cdd07973a1f4313e87cbbba077acbccbc770f91d2d5f32b1306da4bbe557de0c9d578c24fb332dafdf661a68eadf5492ee9b9ea65cca9677f91c2b6f5a799795dfa1bfe5692a74b3a7189a743fc6d392265d4dc2517ad3e770f9187a47f92e6fd23b88cc94eff23bf4979e92acc6d1f2fa73b4cce8cfb1f2a5d6a1c389906e944f034d95da65da5d2b7b2a7394e56b967f9be9f59b6e73e163bce930176268a7b98c45a5729a49ab4a5d72d1d696a5e8b4a7b22c3569a6d4c1f2e50997046ed683aa056ed6838a9b398a5b39cccd9a4061702e519030fc141ac24df6143a57365112a12144352218ac4437b0895a2221f5e461dd742f47559a5dff02a931e1db93fe916ccb62a3adc578b97b311763f8e665e9af2c1d3aeacb9aaaaeb1cada51be7e9aaab58665d69a1ffcca9bf5f57a45d759ac36938245df576d6b1d32dd7c8bb156b3926668b04680c081108490854a95d2317bd2ba96af4115fb045bb35f58db8ab5b5ce7e616db6e6288e865bd92ffcc4f1b0b6d695f7bc3bf0f013f70547b3e4f6e0beb82bce664f97078ee60ab1e4aa74e80621d775c16551134b1772d9eb82cb0ab91c656d0e539f9b59d375819f382bbc0bb96c195ad11730acf0d3edc14df67455d6e428fbf5b995a3ac0ef940617a0059c1da4240702bd00d5f31721c74eb3a2c7a3787baa4a950120e1ebb8ea885af18f1eeb609bae12b46719ba09b97a2167d0175e9016f77fa0281711921be62b45a14e3cb9f89f1e5fb972ebcd63a56de85d7c1f2315ea3d1e96688b6cd502de4213f618806d40474ab31219aeabf810e78fbfea02ff53b30eecb0287a97fbde53f93fc6b28bfe56bd0ff0f5ad738ca6ff999d2df5feb78a79b6ebacb074059b23c00ca15cf35f4979a64af9e295f47cb979aa66826ddd04ff74ccbd399f2b5d641e4be0e962fffea19fd2d5ac7caeba7514db0399b4b795d2f92fddb2a4b92d537b81bc784b29070b43c4b8b26f9b73c4db1946f7afde59366f4973756fe75e8283d11d20dfd34d094fe1cdd6d8827b4a00531952ae5f22c00f0523441e8846d31526b2a5f0433b194ca9721e3b6dc268ca29808492d895afb6f8c3f78fd2695fde05bde7c4d4346694d2229469a62d15adf9735c5d0d49a5cd0ad96a7fc3a63860c19ba53fa20746e4235b010ebed836ee5836e1f52bd3f0bcbbfd6c1a249feaf439fa5adb12e5da3cd5174320af115e3669b5755634c6ec6cdc4dbba432e8b03dd404deaa97cfd6658965c6da47be55b565adbd7b1a26933fa75b4680bb3a6aaad2c6567170bd9658152341572c1767dec84d10c3dd662d42dbff239585eeb1dfa597e453f8b0edd58bec56bc2c8f23a747634cb986fe80fbd383a147295282a28a12e4331f186101663ce32d8774db22b1b845d95e59d651c9e2a1f2c45b0986cc6725944406a8ccdf88a5b598ace3c03b74933d81b4440f24c65e24412869085940e1d52d4e6302fabcc362255e86007a74a95d29a94e35fbfd63bca7f3047f9ff3494ff34e3a029cd52be0eb9ac4986908d54cfeaa39e59d46a8de67883e5432f7b62f9fa2b654b19725199cba22f307bba35d65484c65c20b4144a82c6dc1fe6352f6bc63643aecb92c12afd65b1e8dbb2a62cce10eb2cc50845425c0fb91449c9f2fea173874e7b22d977791d52f5432c7b22594dc2f1dff233ff2d6fbf45eb2062f5cccabb681d2dbfe2f23a56fe35c93e4dfdeb905279eb9d7559342bdff234ea9d62342f2b00229835b1a80cdea7961b13c12e8cb5d252de56c64c18e966d1242f7394cfa2771099597996dfd1f2e5e760f9ffd73b569ee5698a84a3e5cbafa145876abb7e0d2b3ae40ad196308a42376bb24746f3b2442c7bfa13081486f0820752b0a5ea632c4633740bb96e5adf01c88071985084a4b56135e6d648b1eb0f00d346dfe5eb37432ed9480018be62f4b62982ddd8569b37b6f5920bb9ca2f817e8e2e43b290cbbcb17190c111ef55d1ac6ae95e20aa30e4b2a7d0cd9aaa3de9f7bc710c855ce59ba159d6a60e9b460ddd54ab33d4252aecbaee1110e22bc6ba4d510b2c452a11cd1523b7375096174835d5d757c8063a30ee4bc4e9b2ac18f5b7fc29937094afbf86f2b526f9b3bc0ea91af4b7e8f2597ea67c16ad8388bf8e952fdff58c7e9667d13afef5eb90baac5ad2252d4b9ad2b7554b178812c6cb5242f7caff9ba2aec4dbcb99952f35b5a699ff1cac498bd1b76f5304b379897757e24d334ec2688a60bb3e09c7ca976fc3ca979bb7e1ffad69f334dbc0b56d7f5663aebeac5a52526336bac6584d727d81d4520b09e3a7b91bc7a496f01146ee4dceb6eb908df5976b70efe51aa41b2b4f034dad7c8e90154990a096852ca4fe45307b0a2d218a858ec8425004f922202721cf7017b846ab3159d0a80c3d52eb421acd895a57c334328b5af73d7c2a7cdc0449a3d9565f5872449c51999a7cb3a70ab3a6983d71bebec8b2a7fcb097e8dab07c83dd6e62bed518cc95a4ec081364ace528728acfcb9ec826482a60b0eb3b8ca4d1979b84711c9fc319b507f3360ddc140830806d6f53205821886d92b470c40515689c90caa0c36015566115b6eb7f66f6e4d1190c6aaadfd961d78fe8c3ae9fd967966fb47c7315f15622cd4f1f306f35bac0401d3675878b9804715679d4956371762b8fba12678e2265e2ac0a3fe52edc5493614d06a130ac9d023206bb3ed9a3455f60204fb2474d082ae381d1a6323eea293f10218c759be47992670b0b61b4db244f90765f9920329aa4cd7e14affcf2887838c3ea0b466ac9bf7b0eadc674f6541339ac73a51ac51a1e350d85e11a43fa5e87fdcdc0332eaedeb9724dc24fb7e392462d0501b2f3a651f8808a0e0761ac7bd43b88f87bbe861de277fef334df20c992b4813ca096c7f27cd8f167f28fef7a46fcce8bb5d126dac425b5555723494f67a4596c6c8be101b5e4d1587ee52e722dbf1ce5a91c2677e1277288d79777905fb98b5c236149d017d251e4116a0337d57782875b2f0752d925767d527c15d94888411d82df040901d55094380b025a6599582375df791d52a44e9370707efc19ce8fdffda87510e9f4ccf81dad43ec9ea66ad0919fa3499dbea183a7f32c492d756f6699671b24e922e5c89f437cd2664d98fa15bd7ad620ba37eb6a8833c4dc10e72587f0139945989804f93a42dac08924e849dc42ac89aecbb9a84caea7fc20562d09e39b62ad24e5787701183d10219535e9467ed2cc2f00082152d0a4b2d641c43462e5a9fc39549a68a57850000d2c350295265aa9ac7da52fa0d2442b45ade903d6f483cf6fd29b026860298e5e8154d6247a24e9f5e0a93e32af074f2526711832c796a02faf8328aad895ccaf3aa4baea0bf5b11423086ae5570b244db4429c913447552198a46526fc5479b86955856459e5e1a75c050ac90492265a21cef22d6f5145b66530284ce6225f0164027a22b3404940633a47913287a9ef9b77f9205f28d82411767d9558139788b69aadc6d0cf546419a904e92a6f9661255c59e6a8ba7225fc242ae1a6fa35cb7029b2441f5e5442ac2b47893f7e1089f861d79b6ba00bae351cf90569ab35e4177ef27ac0b5a6078c752ee22f6bca4c7c9124a58a44ad21bf206d95e5037da93f284cfd810825767d2a03395cc3ddfccaafd7e75ec1ca2f23b15d8638fc98f093c8e3654fe2ca9a2a0843b56fd6555967f4a5660b829f5ffcfce293b41a99452dad68244d743db1e4084f5c5d71c5b954ec7c8ef1b3de917ffc8ede416426ff38b2a6aa49568f578b6710f807a3f7b2a60aabb2d81176754ebc7f3506c28835394b524be07b6f923297e78de3193e8d4a22a9a75a434ab1eb9347284a74914eecfa248ccad8c04fe2cae528fc95879b3027ba441edb45d7bdb686b92fb416735fd759994c66b34c9665d66659ad467e98cb324ff4a11459f4a5722e9682dae06418c495b812571c968d6442dea2208938795aab495e5be269f24632711f8c1e696bb15119da44f5f07b4035a01688b53bef79a7a4ee6be07cfe1a464f494a754a927dcfd3949726a7f3fe36da791ad596241cf93d9f23bf47ef2032d379f177909a643f3fe7f3e710ffa3777434a9e33cc97244cecab6c59b8a34392d92a9de206d2015c9227d9036f289d1246da4adc6d02852f0daf5492635a6ca406bc9e400d9f5495545f3edcb64b471581c7eefcdec97e36ee873114779b908c984a442aec2659ed6b9d6799824730def4911873f745f946ba390ca64f2a4828292b0eb835a54c66e92d39a506dd336a1da9e0548080e0308bb3e8dc544d86b9ba09a5ddf0445b1eb9ba021d0bc7375b588b5b7f26836ff8940ea49ac5951c8ae2ffea0a82c12c1a2323ff8c93b578eb2de106e2299384c7d5cd2ecfa62ad062ac1f6c82fdc549fac814cb06bcda4edfb53e628dcc461ea9f4d4859f7211225b915f1bb44f172d692276e429e24eb729bb05b51e154f2abc2466e9bf995e2aaa9c1b0f1db667ecd70df8dbcfc556b48528ad1e63ccf6f03779be2aa53c118835d5010d0694f9d0a4690aca626834fa3922ed2e5aa435cb988e8aa43aaa97e919711189232d79ad8b8020a42ba72ad561b6b4532f8fe342a0c9cb8aa31662eb22ba875027acab520a8cc7d19ec2ab60008a3d5648dcce2fb5c84b6ebe79a3d89b9e6ca35d155dbf5ddb95bfaa63586137b783f82c4366c57476528284c1693d8206d75735996ff5a71b5120202b23700a2a927d2654d367b123d0cf2e10205b12b91ba5aad4a4e2cedc889654dd95c2e9b47dab89c9f46bd29efcc327baa599665b948ddd025270d6b72ff5b57f634534d5e57f5de7b8dc82fbaec296b97cbcbbcaf4bb6c16dfc3144d1e56913146483b452ea9c68eab0b106b53010464e675b364928726de48879e48819bf49ae56ab95e8d74f1fb00abe0820040710829e5f306cbcdb245dbbbe00bc73654f9e3f79f44853030531f221d6ec8904d1904dc8d8d8bd49da06107a49ba5c981357e229b2c4d6f544d7489c6517d1b5b7bf2922c1fb4a8fbec8c023e9067821f402749b009974233f0d34953f47e581144e108394f8e001420c3b77f5ec32b86a07d2654fa2172f110cd8e6ec10136ba4ad05580b486a0caea48db49136c73e60d513cb9135d9ec2203ab93a004d837de39087a8837a5bbba18b4baa8d1a87a507b040df203bbaf5631d8f5cd166f39b61c064107c2576e85cf3015f6e5b22362416248326cb06d521b6c1fa096ea96c1dd3ce0fbae6aa9fa382febaa569bcd6cb3996dac7d937b50741ebeb235b56457d5d2ecccca46b04741f914944ff9258c75afbccaef50f99447297710d9b1a2751099597915adc3ccc0ddfc0c05d839d02c7020f746d7a067a090296a4d31ccf8418c6691a2334e07815292fc37ff9f523eca9b5cace6442b564b14cdd54a1c2baff229256926e5513ee551fe4b92eb99154dda907270a4004b924af91c2abfa277104979154ddabccad3548e941c4ed0032452299ab4d137527ee5698aa650f45b936f1e6b3ba49636da03be5d5b97db5b4a795538956d72ed6c46c158024817460479ae346f6c2ee4a183eefea68c8d379bcd66b3d93c2e39efc5a7517df36dfe6edeb3799128e4092fb8a12876f336a8e0cdc6ebc4cfef95e2831d6fe340bce5450051ddb6a02f6e859b1c88a39238cab5834161b870205748b2d15edb84c2214e91b719e2c4ee7635849f2c0ff796a8d851160a0ac3c5be34bbe6bc6fcc9eb818b76bca5fd8756d1ab792a5945ccd9a9c8e1bcf2b3b155cda88f2299ea5b1b520ccaa0ae29e9ef69717a9a57b4faf236377b3ae9b75dd5f9a284657d1d1f369e16fe8efc99f844f7ad1e841de73a057e2df888ff18fceb3c5120204041f97e68d8d35e9fbcd77a4efed73165bec79a44f93526e6cee3b6ddea061399bef37da0471b829fdbdee6d40f994b761e555dec31863ac4956933a8d03e5535254f406ff579adde7f06df8588b82d0f14a103f183ea84541183dfd9da75902e1637d43f856df307abc294d2f3c197d7e90487c3c3af13cd62649240abd27f437743725bdde63ee5eef378f43f73df76e3c4e9b9edb826ee13d2d3c8d7acb1634e829fdc9b203a3e7bee7927fb9ff7cdedacfc927fac22fe53b3f95ff1efc5436b8eb1e45c5fea6047d285f1fec424fed69d4c2c948140a51de861410e553e9369fcf9ebee1f3dc479b3ad0b07606113fcd587c8e7bbc29cd1be7c9532b0a3fd2dfd39e52be05d189e80bf5d7c2e73c1185ceaacdd22ef9a798df73f0e7ef3c4fdf80e90697a60e1b6fca9915148c4bd09b9bf7bc9452e551ca95df6c66543ee5bd12878af67039b3a251de7ebe1fdf7229aff233292a289bfc1f07fafc19f7a714dfd311c7fc1c30cb00daa2a676881146d3d6d8d355adacafb6595576e579ae9575d76a1504775671cf7b9e271d6b935aaab75adabce9350954261fd0713bf7dd346f9e46adaa6aab4caaaaaaaa0adb99a55925dc8a5b71dfc6292b2b662b47d99a993d79cd0a3f7917352b5be328ef078539f70f3544ecfa3eb040e455819bc79b0dde941ec66f836eac3b0f3fb729eddb075daf4a2354ec6ce32a7ae3deaa7121558c6655b5ec6c367bd9d3e86129a33882a4bb1c465f92a02f759bee128d429a61a82e2a5339fc3d7843d073e7394ddd5ba7e2689b5badb4d62928efbfc9359c085cf8b689d1b4ab5dbbef7bcb51ceade83d78416e9c11765fca8ab7f12ebc86edcd615c0925fc6479b8c9de1ce559282c0f0bb32b7bb23ab0371b855da2b431fb4a32da2b46944f79b3aa686ae9aa928cdc36ed2bd7b84183c2d9b89a35d919b7a201344524f571ed79e3381afd974a3366c490c0a73b3fe7b4a64a91ec2bae6c30e23745559e61655f6d44e7439a1af3e973e49c32b6c9396750d8aac69cf644393e921845edb946f36b6a6948cd69f18c8ddb6c8dada6c614ba906178d1977a3112a3dd35ecafd25aa9b7d017ffafc4a1b43f8ee383c3c23f832f361235a031de0d6a1c8599d4eca0b45ab1561f57a2267f1b13db17bbfec7e18ff3f04b08804e0035e2bfe96dbc048ff34421e8c87d00720def03208010df3c5ba332373fe7b362e7cd202221d688618837981b7db9dbc4b7027cdff7ad9c546601a7a7323da84c0a1ce53f9262446264346414b22b103a7e2fcac4976804565fe83caec16872ce24b53413653e382c4ecb9e6ae8f83d0603dff00d068edfba30028b2c52e6af70bdd5bf6fa132de8b2a6bca353eed63697325a6d5d2afaca9be16eb77727cb0ea0b5fb369309a9c93736298c3883b5053fd3f9fd5125538db6a8c89619b498da92da931f83d16adc67cd861fc3f7fc0f77ddf77c5e8f9ce9ba2eabbb69273725a6349537777beabfdc6445cb97f1ccebdfedd6e631510e298a37e878fd9d38a145e082bb72741acfb04e41a9b4f803d4078af183fbfa3f39edf41fee7cdaf9911aed8a8ccf7f557bca027ce17c3397f87e73bbfe3f36426e1e8781ef4ac83c84ce7c9cff17952ebf0db1250b8822bd5d1351c60092d8aa4688a084d915edad87b500601214662acdb5cb9395b1b8246749e461dcb0df87f7fdfa5e057a0277cb33e05ea55a06e2bc18d045b455f56e229ee406148405faa485363441a2223478b5a34524baf05741b9758f3f5ebfe55fd95e6b4f96d2ff10d6b1305a48916dc741fbe36428cc4e86d53bc6198a3383e50d10ace517180c030ef65c835f0cb7053a19777830d08739e61658bcf394d8eedbcff5d9b6d7816b6efef6dd8fffed35f4bded3fcd9e8fed32df4c57b1d36cddff7e150daf87b4efcc05c43fc7ad6181f5466f369d7a2ca9e48fd1fcfb326d50fa931dc1bd1f977d953477358d6545363f2734ece994bce594b22b7bfe65d74ffcb9e68ea619ae3031319432dbab438a496f090d1c4b7d56dd7ff1fb8d842d0d38acd9a80a08f02fa2b4789b5152cace88a8d082ffd3e8d8a6ff8866f9cafd8c4152cacdc442e76fd8fbb1b8b3d0884df789f73c178bf7edff77ddf8310082510726240845d1f2331e26d724e1c068725ca1cc509c249c13639e70c9ec15f5125aa6ca2caa6b2715cb5545d738ae023c6eecdaf51893c8824a0a209a808043dbdca5198f62a1055bf1255dc8334501abec69edc0b6aaaef60ecfa353bbcaa0f5f63e39c9fc73939e766f3bf7abc84ed799da59f87bbbb791aa73a11539c14c5950450632eb8793a72740e0e533f0747ddf04a53554d6eb5c4a49666346bb2d1172fae87091e5a155b46e33c3447815432623c34576980bec438ca7790614f1b20010fbb3ef8dd10e760bc39187d7fe3e769ac3f6d3107863950999a738d1c5c45eaab23a3320cf0d046afd1b53f35db431bb1c673cb569b46f3786af5851ae6607ee0f779a1e7a1791e9aa776dfcaa8e9bea7e6c9c243f3d0ae87e6a97dfa032d1dc8818d7a34bfc1e8a965514b32cf67513b327e9b46f1840fdbdbf4074910629b2e1bf18b3807630e54c69e3c595453f52ca91aa0280f16940354c6c304fa52bf5ace03fbf6e6fd056e8ff9cbe8cbe9033df98c19042080c9549627279d8e6ce5ba06b1c428b31e9a87769fb39ac31df837cb50b706ec8d03945e00325a481c4e41a90c95c94eb63513c0e1bb5ac550ca18cbb7a603743c921da98cd126c3729d92a6ac27c39ef2c7785c9e1d50cfab96b2feac3cd829c351539de74acfeda3faece0aa311f1ebcb4030f1835ebc2363d1e97e65c307ab44e49539edb4745656aa7f4d05e63e7c7375db672998354f83d327bf268f7c17bc8fc872c88cc579f4c26bbd7b68408c061506ac90331dd9973c1c8596b2d28044b8fcdb3c453f364e1c182be7896c08dcfe9743a32727014a76f382e8d107fe5a5c4076f38ca67e030f5e9cb11f4a5be57fa90af88114f8da3871d4f714ad24ce7c71fb50ece77b48e7b85121cc1be0e295074b747c1a1b0ebfb16f485fa371ce55270981c8cdc36ddc6d55ce3c668330d35d5d1ce299de5ad21562eaba9142665d654df49996f31dbb586736f7a92d45275f741e633acc4e89e25bbd2649e2c3c341cac3c300f4ce681c93c3019ae315d9600cd625f0fcc9e3c5bc4b6171e19181e9aa78585b8cc7a973bc17aaa02a42de608d39821061a48f0bda2c5dd554261e50c5389bed4b7808c8d000a030037d5b7c2088c3dafeb79e5e0012f79363d184d3f3db684bfe88b4341615b071e05366234fddc1c750fe4803de04c54e04529ad547e18638c31c6188f5638ba94e7d575e716def2025f70c36d6ae007c3103331da6d96300fc68f664f34e5efb9e795c3921f8152190aa332d5bdf02d76c5c21d1bb171ae84565ff0bc3c6f1c795ef6e4a77d9d9e97119b9adab04a9f2959f9cdcf999f36b5d19ed78fd103f318a9a58e359ad4e6390283c233c59dcd665d67b5798fb0fdb1d84275bdc83987e3a521a301df1bf09f21061a333061578096ea23c1a59f9ec3cbe62f6a7325f445092f7b722bbc0b8fb916afbd8391849ffae979798c989e970789f7a027cf129ed7e9f1f3c4daf3a22f5807a3e9af9a2135a69bf9cc67d8fa8fce574137c378e6a896c37c9830d2e1cbc40b779dceb36e565d173f01d73e4c7c5ead4f8b7c8284e599cf3e56cc2aec63859fea13e22a83f1846a8b3e5098fa5600828b4f0c3ead27368c9aeadbf8bc3e463eb0cfebf3fac0c2aeb26eadd5558bf05355825595f053b6c24d7946432356222aab16515b97cb5d3441858a84c1a84ccdf909b9f629e2d3fadc5ceb3e457c5ab5d5aa913db8cb5157e719f9c49dd58e43c2c69fd9e708f494c1b87d66b7d1dd2ff7995199cc8168b32d8b7d40472dd55c8625a5e1873a0c35e946e8aa300c43ed59bbca943f7cd34f100b745a533db162e6a9b2157ecaaa4e93461c063cc91d3e45903d280f4230515b56ccec14288e028df11c55590e1323af5059c0454a8d416de5acd2d5d60b77d1e92efcf429c25f8e32e2a84e5796c3902a6bfa14e127b207f2c532f2897d96e8c161fb03c547079f282a0b76fd7aab0e294575f83347fcfb9f0f139dde9ca7a35dee300c3fad1a83f5e7553bfd31f211b2ebe7e5c2adfc49daf5568ec2358721b5f0d3870937d5f7562e7840782b520b92763bd15a6b3f4cf82987e1a67cab39ca1de53a87e127f2095cfbb43afb293f4778c24fd43f5b20e1f3840f0ceed3a8aa5aaa0f526d423a5a8b4bbaefbdd7f1f7beb9fa33fbcc3e33cfd3245a4bf54d3a8413a836574b2024f9b6c16aead81b2319b1265734f64d52e5d772201d392e87975392354bdaac4905dac1e384eea95c565cb38d8ea62ebde1e7a7dda009ed91f17e48db87f6f9882f91c68154323b2cc594583a143e929ca93b85874fda3a15642d1363f79a79835d189259d895c4c2ae4f82415f00f1e0e70f18a6ea2cc755ef447fd0b3e7951fcd6a183a9246fcdcf1f3fabc3e2f2a9349aefbcedbce0f4de5ee04adfbcc3e33fc35881f7e48d6f39dcfe1017de77790eff91c9d0f7d48ef00c1484db29eaf01a449561b11eabc7defbfafcc11fef8b6c411fef85ee6c8ffb1a1d261a0d263a4cb3c5afcf03fe58e8e26618d237cf149e2cfe4e75c2c2c09cbb35c0b69b4ac84369bd16a9ed1382befe4b0c49aed0613958835e24b747d5eb0b19a22d8e28cbed43a0aaf37128944f9969b882a2edff64d556328a93a39c936542a52f5794d6157d0157648753b3b1a8d9f993579915a72d592d7031735fa8428ecfa30973641b1bbbdcbb6c2dc482dd55845524bdfe79b3d7d66de585fb5c8cb511e4da619bd3fc9357007ea8dbe9cb59f9747f3f1c10776b95f49c936baddfdca67f699e51a589ba0237bc7ee3408866d7849f72686d9530683ceeee54012e642c2402d7ba229504d8bc22c099b0134534b7525c33fb359f79d0ac61a40352d3a8b815a17a8b1a66aca35c0070110968e7c3ccf5b95aced44ff0f46c23e2fd8e745c23e2f12664f9f2350537d2cbc2d60fbe3c5cc55047f5ae15fefd3facc3eb320f6064e248d5c52633a158846e5112155355245c359654f248da6a2a968f6846b351aaed170187eae305ca3d58ec3358b6bb5cd75608601c346fc12826054c68edeea08bb7ebe796792cc8513de4a0a24b15c3bb2391f7e8ef1f33a9ad3e48aa4f140988c1f24002540b562f77eefbd9746f3562ad815e462d7f7c69063807befbd1c01565e10de595fa88f9b184d6fb5bd15bd51994caa4815f7dc766a80ee0d906dd49a6100b7996f2fdca8b8f9bed9b58fdce65a9e5499b9b6690450ca356f95f312cea640f8c104040b3f47fe10d46dd720187d413276fa53f311c2bdf9691dc000997b2f6bf8342a8d169634c4f2deaeebba985348034fa3827474ebbab11fb0d71563e9f34e566edde52dcac4e52e84ba11eb39f78d966823b2159d46631fdebb92116d44aba5aaa496cace4f60b5278a6294a4963a3d9af94a554b43183be388664d5c698e948c94d451adc6789024a3d9122a4377163b08dec97922b2799e9facfc6475727ade38aa4ece204e5627b6e61ad6e607e820bad55215313909a2c6ac6accc979e2a3067baab5b32397fbf9040cb6ab54466cd3db2e57e89ee8952395171d2d46a291c80b954864ab272b2bbb4a18e9a659cd48e5efc14b89cf799aee7bf052f95dae5a721f3b50d30b0630f3365d85825d1f64593d7a8d8a20e1acd552eb56b4cf9d2d608942f024b4e5a8c7a8e519f318798c98c7086e73748e4e47d5d5c63c46ce3647278f51c58e4ecec7a8e2b20cdf836ff30cdddf776ec36d4e20d69eb5e4a355c35380841abc1c8dce5b031254545454f00554c24dc9ed940c016f7fa3f354a9ecb9b9dffb7fb906e6317edba4355b825eda2e06baeddba0db6a5114e377e05aafb49f6bd8df949b7db2a232aea22f27ad7a22a4c67cda81d4920f27ce722cc498030e3e6a09482d69c729701161c6fd4436dbcdcb0b7cd7064b6e4063700e76e0729488e6ba0087bbf36c896c3344365b87d6a1d1444b462738ea717616f318bb6d8e4e3ae27721d7e8de0553107a1a9dac7c89912c89237ef43859e11ea38ffabef2466e8e64309ca33992d9f0bc931f9a3801829ea0e044087a1ab9aca93a8a060f76fd9395eb84888bbb916c24a3c9ae4ae53e72d118f160f4a24d5c4bcbc2b35ed775ddc84565aa297b3b7c3e72dd29c68e8acaa0767ddf819e46321595a1359afbb0eb0556419c9c6205dfbe1319cd9395aac670e3c9eaac31b4ccc186abc2f0f3a8c3b3a68e054a08c282144d11e1def40090bdc73cc64b730c75dbef4ad014621ea3ddf6c197975cc37b9761c3e55c837b51d569a759a1603447b23d92f598d16ade5987312f12b6fff77d170f6000b9c6fd01641b7573329c3a69454b84191534061b21092399a36a70983a928d4632e7b06f46ae99a3ba1dba1faece87ab23c1e8acd5e0a8ab042a6c3f6ddbcf2b9470f48566e559cbddcef33ccf334088cb13d5c836f282be8c6e7b89a34e76a03023299c90a000b9c6fd02bc30928d64aa4e363a57a39a20a3f3c788d583cc35fc64a5da6189a3bc07cd80f3204417bb3ee631fa3e5dbbfec845657c07fa92043d390f3f622938b27f58e26475729e04514bf804a329b28d5210c616828e56404744d05387664da7a33a4d3a50189db4510c0e10436344367b3ae961fba10a623b51c9b047e52a577d1dda92268eba585018ff26b67d996c67c2f6373b616c0fa62ff7d6ae920fee2ef7398c3f4915027c39eb5d8f91613d87911101efbd1836d772a0c7799e9f4cbcc0eb27132f3f9d093fb98072bae0a71837a1c4a0d8ca09416ff45c2fdcc40064f4255eafd14b23c2bff10ab5c31640eb0ba6cb46ef6320c20666c1c8bd2fc05f2135f2c3f32ebcf7440a2d8a6c7fe75cbfb41ba925d668d25a78017bfa610b626df7751cc771dca53213f6fc3cc00d1dc0c0d5e6ddde7df751b6496b648c87bb1a0395a9db5f1b7053fa3c90522f691040067da98f5740ad7dd9d7a8044408ab2f8bcad8af2ee402e689d9d926adc570e6333c511cb56901666cea047e4eae719f936dd4ceebecc51fcb711cc77d0003351d38c1bd7640637a4063bacf327f798e020135ddb6af603f1f4b8228a85af19edefebc8bbbd0bdf76f28e4ca1a64b813a0317ea2ee003cc468fa8bc6382f7ff9ebc4d12ed66a47f8f93a3717448db1b7d7cb5f5dac93e2e273535d6081b4c58ca99c7b5e68b4e0d3f71cd532865abc16477d2dbbfe00fcd4f5d03571d4a7c1240ece7af013e8609226bbbe578237b004b700ad006d601247816080337b02b900af0056816929348235b0b6053d896854c682b70d7a118235aee37e141b8d64f50517ecfa45d4d0c0c5d5a2c46bd716d017234667b1bdc9b2458785c9aecf5263d982a23c27cb12161bcb13762d5264d75709424fa2980a4ba565041597ca11383a7b12d1c08f896e30478d90388ce7a4347b52e1a844b41658a7c4a9ba411b821b7016823311046716ec02ac8133478d0e53c12efc74b500c1d9ae8ef2462dfc0432e1347b025fd6a43b55a70664c24f1d1a80ba53795ee7080d62db519d29284cfd4e1354f00026019c815d803551acebc05c5a2e426a8cf8428bca28e00b5554d8b7fbf785fe78b3d96ce8f76667ded9defc9d626f1ea46ea915af8cbe6c3e0447229a8aead25a10d128aa8596a800680cfe5c5a8e02b5286644e73b9d17c51c4573946b1595c3d43f41b970d65af7eff33c0f0c375e49100441f0fb4232fcc08a18eb363b304f1efffbf07f250e63ae9165c0c1e2e0f9e63d6bc0b34083056bb6498c9b31e3e85200b0d5980d57b3f1ac041cabac5468442dca4245b56bb524a289683218af16c944528868a3e9d20ac330746975e709b6369befdbfcf7750e6ebeee1ce2d2ea589bcdad3bbbb33bbbb313a95421cbb3a29b928d3e198d46a4b0855aa748c7d519d2394245e5a8ce0c3a3531005f336a6aa2db68355271484437d16d468b6c06d7817560a29b3d7560a25b27ba5d9a3d852db25a07d68179b5049674dbeffbbe0f0525fb8f9038ca0588c3b800d9758464d72fb24d15245454f6a4c2a2a6ea71b6a9a202b50982d906df4bd0a93581250e5dcdc62e2d17212e353018f15ff0330e9cea527e95b4c8eeec26b932fae2ef02c451770a0ae35fa970c10fb6f9a9744a6a7396000edee3b0799d75a6c1a6280bf52a7a3c3689d1bfe3f8e93671e86a76f737548560a7c69a2e46ba14d635a34b4bbb08093272744bac05492de1d6c8bd4b2b54d993caa8c21ab96a477f1556193ef8025fe04ded2fd4e028364332925d70141bc5c0516c141bc52eedd240f03de54dcd78d9d3a561f005bebe19e0a55d5a12cefb5cc2afac2008822d614b4bf68f11ba906100411004bf32a4294117c4aeabed3eeaca914ad4440406cd51222b388ce8f682d1ecce96d981edfaa27308477d61cb9ec2d39a7c382aa40959d6f472d4a7c31ebc0745853b5021a84c1876e70e55f4a5ba603455582aac516c068d2171a9a931df4dd5072b67ad0a0bc9e7d272a9a132dfabc8e0c85669b15c8a6c9721bbba1cb1ab0b122c7b9a6155582a2c7abfefc0eca9d3e9c0700b46d3a505009b0a4b8a5ddfa5a6c66ce07329524fa1cac5654875718527121723bb7e073ef820111a68aaf32aac5d3f54719ed2fae7296b2a54d9934b2b54852a97963db5c85c5a2d3297967f58773adf7bca9bf2f888ed96192e2d9756a7c69ec24a3d3e66940df05a64f6f4806af26f9175b25de94b17a3e2541fa2c70e5ef7e5bd1c8563577c7dbd5a94390c96719856850cd3c42a6034cc044cab42764bce8abe80415f9ad875480dae011882a5384474d512bed9912cb661f65ff6d4e990a51f52237bb7af893d141ff3ce9f24ff1c1ef2f39ecff1d11dfdaf19334433c6fede85a2708cc2d4ef783ee693ff7d4f961fef065114dffc2145c6fce6d7805cf8b1373fc6f9f0fd402a64f8c680806b108a9ca7f9b331fe57d25c637c9bfd576c4ed0d3af645e14bb3e67079c1ed09897a3c41a87a9cf811f9367a0033b72596ffc4aee45558d199f23fd4152cd4785ece6e2ed7860466461dc704ce6ad3018f465e50b37d962359e0f2b5f602bc43c203e0cc342886198a3b012a709c14f580b37552531fab284167e126920d6589312477d9f16a110451d7c4a76d55188e28bca884bd09328ce8eec7a850b7602c38410e39ca32de88d8305e70849d04a39501c596274c418e48727c60880a81255ae180c2643acb94cbcc41a4789b59adac7c430fcf442005fb5e2057d3980287a32cf721b7cc34d3c9567576c9b4f5db1d11770c586af80c15861524fb8c94a937af268dcb662e336f70ab77dbef671dae7e2bf1c023c223c166708ce29733d96c71982737e4fdf63394a947186f0935885e8b1c42a44218eba57639adfac1886e89c16e7e4f4a089dce66bc41ad199f80ddfc09e887c5f9f73b6383519063036052f288c14f5e44cea27a9276f526dab8f71208d0a6a1d1e3d7ede4b003ffe5a1005613471d0c1c8f642af0cc5ff4a2f6ddfe009ecec86f3c010a46367dfa927862227088511f1121e879e0cf6493370efbda76fa0b5c43ddd9ff820187a18fcd4b898b5e81287882d3e4e30460eab964a9573724ebad19c22f4a57e86d90eb662aba9b1d93ed8a11ddfff03b7b9e15fd50de7892fdb576c3098ed624d1480d3978d3d40280402793c1f51e571db4874424f3ecec3bfb282824ddfdf630e440939ac5ae29c9c963555abc5591251254ac1e31c8563ba64a12ca2eadaafa9a9f9d8ab6005848b3dcec337461823464b4bae815f54dd6acc06fc3f0afa52ff4f2f85f9311744993d89605cf1b197e265f585fa587c9a9d73da93e879e35845dfa6781345d6c73cbfe3f3b15d9104a9d90cd814494b8bedd91fbc57783afff91d9e55e73f9fc3f3e4d3a89f8fe759e47b9c456b5390a2960aa81ea8242c344949cd3286060000000000c3140000280c0a86c30242a9500f44513e14000e74a0566e581d09c420896118650c420620030006604000044666140000897cdef6fe2ace80de9c648b10f7087c3ad48421003d86c8f51cbc9000ee59ee57351d3b74a259041703b192b9445f4bce7355b4d067d5b0ce0f69bb66ae879c1ad6a26cb17e62693b1e21a557c3dafcd5334fc5aa559ff522384ad2c9df59b309ce166378deab68805e58305a7bb13e6dddafb19d978c3b1a8aa519609e0f9c5f312243eaa528e5f97353e80dc217ae68a03ea37301733ccad0ee048c0dfbc9e6307dd17591816e69aebf1632debc7a602e5e80dc74006ae83fc8d4bea72b1b5ed470d9ebf445c2ba57d04d4cfe1f0b03844700dca40f8bc4133ab31887fffbed2549170191444e4bd19fff0bf94f61fbe209db7ca424d39eca56f807db025050e0f4e1ca098f0c2a4e6e0864b55141333d9e46c88ec78b6dceb803a733d3d84d3c5dc24d4dbcb38f32c49db8a5be991a0054fcd104cd419eb4d04c36fb2902d1bcfbf025d401ed53942dc09569dfe351dce33ca703df72f2d1031e267a7f8f176775baeaab1615ec8f773244c78cae25b699273379a4d8433e8ddbec936a28ab34b4012f04384c7e210d5835b46823f534c070b40db3b944a519616006107a68a47285bc70f90670c5cfbdd43b08120d184ac6251e831943b730e93dff84aa110313a1720e6431ff1cbbade9ca116e52062d7cf47a013a53ba3dbf7271b8aef1a5f3fbc0020b8b877ad75b2e1e0bcc39efff92c0d1e3b22f6d1193ef0f246b8353de38c294aef01bf0eaac18887fec554c4827adeb1e6b069c32490994a54e0650ac803640cf5e33a20281bbf8ab6ec96948529e5e338e8514fba363db754a08529abcf17c2baa9eb4e71a63f32d7296dcea3c017e3a112ad747e725a5c2b1a566e42c31d2917bbaf4fa25db6a44347da026a856b6640bb9462c2f73b36a2eafc69111df14e7c15e450b96b64e6114446e490cfe73fc1fffc9893b681260ed2558e3ea872b7588914c068e007840ac1b2b7b5cb0d8373165c5576adf65e646b9ae1d6d4d2c8d3fa83dbb136979ab76b5ed44d14b09784fe13701d4579bebf84c40791d7fca3840283b06ae6e2f4604e9d5db17a977423e9275821a0c394a6652ca53695e11f61f30575db8880af7d2a833ed4afe60968036c555f3992b251cb5c35705fca1fe711def049c6010cd3e86d42307fcfcb466ae52ab9199170f9bfb119e28e39c02a5aab25257c1850abb22673bfa5dc51cda092712c32822f1b891a31296a1763fd9e6e0da5c0a2930fe541026ca6e345bbb58c0e25a69221db48655cff5cc0f11ffbb6dca5a545e957daf12e53b874cac3114f75f315a1c446f494db54731318b6c19886902e468bc75d8383af3b0124148f8f2694b62808aa1ec693957803132a6d2000076ba30afc0812c63505a700eb6b8b9b0c1ff2648e7ba5a2456ad741934d1dc94245594496fbe5258620e3b93678a3896e56d057b1b89fc108f5d29b1eb29fa4096476fa8c5de463097f7eca967fa877a6ecb5d462fca8ebdb5f04f7b16a19ea3cbcb48308d243dc56f4976518350230deacc6d221253ab15d08c326f5b7111775cdbd93a9fd78081773199853c40b012362816100f8d94da5d6233fe9761dc1f41d1ce164ef9efd28f4a2a5a3b379f0ff5cefedfe82e9ac6f1daccd427bb35ea04b03a8320d1ddb33ab92c1b2daaeb78f3d59bba750ce1219c20b9fa670c67d08e025bf8bc2530cd52e57a6bb8d312c353263747e190cede0bfd84350701adfdc587cd814ab8c55c43df821fd95dceeddcbb97e0571468a502e5a72e2a27c751fbe3f45e64027f2f953ca243f1e36aac9deee968cee2870e7c480c0283ac145ce254c4d7aa249e4b46b03515147eef08e476ca04d110ed5fdab1aa85f7bdf2fe64557fcecbea49dfbccfae446b763dd882b5c1b3b96bf76cab6bea5baec04b77eaa3380426313d6549f0d206e0579fc1abf2810d8f524d45ee49077cf275bf4dd260c488ceca7799ff4e52ddebce9d69f8e7dafc266f924497418a38ef9088a40f643c96f4750aca6cb37a669a217d71fbbbe9df5460bf103b6a27ea04a99b652834e0f2d9cecaabde6ef74a03ff2558d42b7c38dbeac905f9a4d05948d94028ddb7cc0dae69bb693f1f56777695167d8365598c083e2e70ad2cb9952a3b7620a7dfe4a02d0626c3b561f533b686626b804f1d62c4d8f2c09aa2b1d491ddee4721e105f86f7b6e1f92e2721598c7fe7f07149c6cbc4f1a24dca4ae7fce1a811406a1d96ebd1b669b58e64b0d45a3ca0e97104c9fc6f4b62be85c5c757e1d8177385d1e32dd4aa06c23fc547c804a1c237a1b0aa5614a8c5dac213937dfd45a2bc74e5456973e6140f4e5b707c35cf72487c0bca3c7d19ec177e1494b18ad375d14f97084c7937cd024f39b2361c614f7c6c068081f92972d8944bb937464a2e2e73b8db58b67edd2da48340164dc4c429529419e7116205e94f5b4d1a3214789db3ff8b5f1e2ee0ad8bc673a491a4a761d7a2595e85c0c80e81a8fa32506816892a24749d933031c03a85d65cdded4db67635436e0d2f1971d4929895821c29443fd144f8818ed63416a62b5d84a9dd83204de97db2aba46fccd7e06b365e821b0aabbaf52a0fe4f38910f6f782329fbcbbffbd3fb6cdb084438d8007e34da8ffed1e17840bb1253fc71df1e816a59391629902e180fc2db1dc026f5d94763e1feff23b69828090f2997aee43a17e8f14c23066e394f5e7f046bbb0536aca5bf951ed907c11c64bca798823ebf595cfc3c80fd35c40e19b391f06ba7b741051bbf5e8df78f2d0862a478196d26a66a3107511163a2a561d185fd2ac4da6b2c3023847d1b6cba94bcef98297391a4d374643f3b70637fe25f514d862d0bb90dd2f78e1926b5f5b72fbf07e10c3a04847214385aa84945711809ddd888ebecf69653585cfe5a1b999c3711b77514011e3afc9f54daf9d6587671aea113165bcc6d3e9ee3588790aeee4e797a34a20d1b048cad4952b572f27b098b380685423772042bcab92d7323de2dbaa44ac5ca798bd23191764f383f47f77607e82d367a036542a3cfe4fc580e5bb94b84f08d9ab5a07d8ca2ed3c9c054d2c24555bef182d9c90d2d11ba97d291c85d98b050bc92c7380ff4fd3ac8aa5a0da701cd8ed3520d985772b93ef95cfae6712def3145689f9fe4f6792bd6c30dae21d7f3ef03535959ed720a5ac4d957da66666f4baa8f1dea35d023b664cdc976391d2f68376d649fcc729bc385d92686c71846535a26b2a819aa02f83e16502e59d8b58177dea7241aaea12e5d13d57bfa2ccffc8371cc2e8cdb2ecf1f3efec1ef403092211a794ba6ab41754a7992cca1cb1c7ee279277c32a40b99e9fd8aaa3b9c6e74380c46b02b98dbb435f51f3f517cc22c743ff732a656270fa1820d9f838231324774a1b2c964d848f6504c20059549ed68e26e0b2fbded72f64e3ba6ff1fc852ad80cc7de79ba9bb42cda3845c58529d79935ea6a8c01bec600136a161556bd2c22b3b8db8dfbdc334f45c793e6380c3f093dc8d4c99e22a02de5a8db33abaef4f9e3a87b732b7ce42d72623a1af4e6c0d7bc68b22f43157325710eb8bafc5705001d671010c12d73ebf5b9ee72e5a2e963674834ba0e4c7bb8fe4517b1e4f49ecdbad2ab7f4bb67ba34e1a564fdc79efc4c861a875e5df2cb06e4f244db084c079c61c47b4264b2e478f8409e5332855e17d98061252291e3d4179dcb5ac0d5003191ed2035c6fae5224728c09d434c4ceb2b6056fc12869c17360813aa377370799e3785716019227cca3780274961cd45ee87d52f64be3d5d0d45e3f19bef325abbdb055419054b0066b38152ddb059e8472f1fc470045adea1e7c68b7439d2ed467811ad1bfb740e26ef14e902231797e41626ffdc8cc1bbef2ab1844b174d084f10cac4c46fc540c75cadc6652896c5ad1546bfb34f4be8d3c15b01be1d229934e3d77145946d692b486153a4afd97581c71b9668fc1ef1c615223df7219a805e3a6d16446c630fb73bbb3f066af859f86ca03068e2db71abdab909c46faa9893af92890fe51e0d3952e7271f2819e1e4044846e3b37540dd6f930926d3b6d4f7b6dbfce6ca19cb11f040e7fa5a8ca49548aaa6ec2b0c72b8a20cadf349dcad189a2fa82777769e38e2183e16099bfbc9e0679b53d957bc62b5617135323b4f50a47afbbdceaf20acb4d06bed4b7409ae308f1dfadb6d6ec3314bba4eb6cbc18b62f6234aef2720f349ae8e7cc9239e2d56b768534a2f7c0c189904f032a83aca93a46a15b0f83ac97ebfe3db8c0a11cdaab5447b60415377ec95bc22d9fdc254cd7fc46beca11a3dfc41b832e68c1cfa3bc1710c64004ccd42ab4ed5eacd2d4ce52b60a81d259273aabde39321ce69cee240409d196a08877d53e2d861eb82202a95d2d9a61ccf75de4fe31109265f24bf33999232196ac74f23d36cbcc793330c804eaddb5aeeeea874bc75ce5aeff638e6557cac0bc0162d528aee4661b08202814b0a7f6fbe2943fa0b9b5633c7522f2ed8705c3332e58afe684d27f27f3936d3aa8c8577d955df57bd0862597e018bf29d992b149dc97755c4d4c2dfae08ee74d7f8df05d3119891be6bf309fe877947e41cd6afc7a37714dcc44684682d2c90e97c6be0524ecf9a7bbe03e0ec0282dfba1fbb5b4da69313745c141ffa86e9a5496be6f54a4b3866fa52bcee3f831e97803203a46ec8075b87d24c5d5c6819b532a1bb7b31b22255451cc94c9837815d4aa2906340112ff081a0714e83687cef6c658e3d51926f2b4e1bc27e61a814e12222df027414cec082674a539dc65c34b46d1b31bb0027e4b086670d128da55f13969948d5275c756dd2fb37288a861a1f2b665f7ad4cc8f110ad106744719045b5dc95551fdd9d2bf4465cad814f29f59391a28a1a375e5a3209dbb5b081e618836e0de719bff8c5295134b526c0934ef80d14a6a856913c370dcaad01be74fd9da57158e969b259d50038980dd3152b354589e443bdb5fa65c164414374e38c6ffe42fde10f35a03fefb2a64a0d0592c19e79b946aa296eb8ab29653b4ec513d609c55d16294358956ca32e6971cd472bdfed4f933dc447400bdf41dfaba52da194831d36c75b552cfb9488461f8ce6abf7e2a51bad0449991cd641f50d45e75b0de8c901be6faf61d200edbade81fa7ec8f94b75454b3f20f58f422ba1c434e86a3c48340b4e980a6217b861957cd99bbc2e31295861b9bc59d55277011427eaddeafb2df94c4a98084a766a252b21e915f2489094204bd4c040d4e49b3ead3e7d422d06b4b19e56273ea6c75b4c17c6b13265376b5e2d13b88e4bfc623edd9fdd10e30b9178e2fa35d3dbdf284ad1a5b574e4e635140aaf657180a573f7b504e51d2afa0953efd20d192824bfe0d63fbbe8f9844a378417f2822458d11fc0988f6c23b9068327c2a38a6428be50250adeee6c1d161d8d33cac6d36e1c5b4913650f6762b4dbade2c80f379d42b3eabb88e712a56ba24e30c9a001140dcd445d08dd06ed3ffe222393005147b63051e42c79d7bdb432b9e24a40e80105a577a0e1f064bb13fc14d111ea701570f6a6243fbc2df44e7f13bd06952c9626ae9f89727564a74d42377fe0095df67b5ae009bf03465dc0c9277aca2aa7ba63e60c52aa27188450feb0f1f045fa41bb93d0e65081e23ba05fec6fadab3c0d73b08bbd711a4c12920ddff48b607d8cc6af9f49bcf3a77238cf6cbcc585ca4bd5a62e9efa6782e8eda8267b8d0c13126a9a731d8690e8f568028e24c2c00ca1885e1ece447fd20ff7811a8574b1c8be9dfdbeb14cf336cf1987696e2cd5ca7141091d8b7ae5d9a3d8707837e9c13df29555393950d0547ea26284625a58f2f58ad6f4d37453899f00280a6c31bfac341ef51a73b8f6e1f2610ea7702b736cd322b3d5bd62acf74c8ae370c1f68c833e47b43c58d55710d08952d112a88ca16649de79b1611f577e257e45a9c18ca9f29e882de71931db2dba603dc89dab208a237532afe6326a9ee03b632108f126d8718b3de0e74717536111882d04439210432841013e46d2bfad293a3141abcb6355d0a76485d468e93e8f6f1d3ae79093b81edc0c1c6f84e709c612bfc0195bdcc354cb62ac9d4b84baa73a2258b1a154a400c253609569477481ebc05f3c268a4ef7cea995d9e32c95e9fcd3e622e519265a5eb83e29212d270100d17f93ae65f5c85a016af9e7eeada29b93695fd989201b8532d5533e621187d067e93a3af92785240df5506b2e9929bac4f5ee1240d7f3f6a2490f499175800b1072d91d95db528936f0ca39c36a564671f0811f1bbfd6765c6f84341a1cb32e57f851540190fd2d1f1a276c9602f4587c9e95bd4ddaa1909bfe1370ab9cfd0915478ba6652f67b7473a4aae243730f447b97480878bbb924bf00057ae3a10e9dcf1fe9d5fedf025ae215b85f36643acd160c3dcbe301bba83570a84bdb3d40cedad8af705767cf4c4416d0159b1baaea4cfcb489b7ee723d21425aa2ced3def9cef9bdba4644a0f38c2fa843bb6cd9bca8355b883a5eec7bd5a7063fcb95c62d155d6e8dd86c7204de4b7b2dd55100a0e8e6c7486751b53cfd0280594705d48f85c5c51b654c83c7ad1dc63461706918e4dbf2217a771d76d4f0980de731fa092d89342052bc5975dcc796b5b0e4b8aeb594f043a031314bb60583dd0421e8d686c1d32323f0ed86e4ec87b68526f0eb5613724ac304d2666fc5e1db3f09ece9f528b19034a3a4f56e1c5fb5b69b5a93091b09aa88cc523585e6e099b5e820f4dd1d58a4d7d7518b0084b3bfc3de21624fd2b82d1947444408fc7f82d588ef26d62ce6bdeb0e4c789f48b050af15c4c87030943f835c9f46af39b539886bdf2028975e245fdb4a2a78758cc3931580e7a6377ab718d357316ee255c78317a1d5d06998d5529ab57d6abe994c918f35ba7b3474785c9931a4160efcb918730cddee4b2aa72c01651bbde06dab132bcbb032daf6405a34ef0062f2d3d4740eb0c0cd2e13d37eb6c683c0b4fd363559b1e44d5735b69cf136b342e4618506106424b47d6db835534c6d1fa8502ef3cd8ea19169b5bd6bf0fd392bb548cb8a6216b795250082d71bd656bc05748dd1039ce78c838ae434d2f2a37a9a66dced4a3c6af50c7f8742a0cdfbcf0787a19e49370028a62c42fe0a1040f5354158f8d094bb096f21e0b4429fb961bd304e931f0c68e9f2f57368e80372c29e7740504c5625dcf0e97054603d601e61ddcf388c32e6c7e4c6d7fe0559e8f0a2ac40a19a25a6b00046605bd37faabb69f046cc70774721b3eb089c09c90452807fce1e86c8f1b95e6231ddbc8ca524ff971875bfed192c909e61d62d7b3b58982849159e8b4c25b54c187158927e10af7c3974b2ea71801d4f9791ba9c3ccc2e780e9de0251978e242be7549f061cf51a76c0206b1e03dca9d7677d38ab261e00052233e2607b11fc3bf21152695e00a9f0427c3a900f7c727c46de08602ca8b5e03a067c758489e1cad7462330a2afd2c94e09ba5b64b1ec355131e0e365768467818166ff02493d032f1116ea1804480bb664fd779ab2d214b47d4e8c1da1f6b7a0df462e80e401cd612562fdc19024b978a9d48fdc997d4e7af91198287c8ee5437fe0f59bdd02431db7e421f2d6079042c56f5e07c4f9ee520ee40e263d2ce4f0997592a251d792e42d31e0f4fa91df99fd844a6e57d978ef938b56b3299400bb6e46c7df299398fba0fe9d4c312ab3df32cf48a3424fc2c417441b318b9b2d4625a5198dab27783b59d1f7fd954e426584315172d083c9c72854560878895e39fdc8c7a233f8016ee0747459a151996df4bcaf9e87eac9b945626e1ffb5de4c7e1a759f3e9cd4a69d60ea1399aace3fb4a8b43176272ee771e8964edf2135a83fa8b2f60d920fadb0cc82d24a84610ee78a81df6289e88a59f97971f8a24bb50abd8a0417c63e8a9a770533896c894ffff946a46afdecaafa054532b62bd4143265eeabe1da2ffba4ce2dd43e85b0dda728d465ea9c76e18df7e03a59aba21d4e2de6d91622456f4d8bd57ccc0f00a56733d5432b60ebefff1218e739280ae31081b667910504363ab3c470caf5a267448a7ff79d90fc8aa24f646b8e1e073d4cda57b9585d21c12b236a06a943b285a1e6203bb19f9fb65e2e5e76efa43371dfa67636053abe4ae80d61a4b99bf50c4626ca5f8c31c5a36c471fadb425b5a744f35028efb035c228d2dae0ed74ee615ec893ceae0cb41f772e8cf91dcc0a16529cd9292973eabc2aa072607ef4470bdf79d0faccfa2371e3d1dd31771a5cb275a25d0345e31a9e3309b31653fc49b9909e8f6a2311427b2c0e2465584712c616dea57b4996723ed30530cc788200e328d2f82fe1d631a28008a71c76f372060cc3fe73c54944589015b996f4989c7e7212d18a18504140217e7e958cda2e7c8f2381e80583f79d4972478ac1daf33758c8a22c11bf0647816e3d08776536f07b33294968cb053f58784e00e78f267052978d75e2773d98680b199f3a8c6d21049b3a5203458faa2643faa64391d1896c24b4abe92f7bc089cc4be754c5f93584cfa38f48e26434d531634875d908d3ca2b209d0419e9de52b06b24c2b01b984a2e509c84f0566f10610651165afb66ef441b8b2b728b5340f02575e3a3a9a4091642b588015bf36a5f721c7aed93819c9579991eda14b064acd570b82142b93aa1a23e22576f93e97d514902ac49941a184a77defb86ce519d30beba526467180960a6544436e7212930c8bfe88f9cdd510c3969864783beeff77fdc153cedd43f33be584c903690fe36bf7f3e15fab524620ebaf0211b52b2d10bdbc5458c395237a716881e18b158fb49c22651f850f5301fb9ee93ff66ebefb26058a4d40d0201f26d70898d64e6134bd6fc22e19e2a8c14019b755a4418270c415eff951e3a84fb4694150111f478e16bd96a17218c452e6166a227a579fa9fbdb5a8d4aa4b50659e751dd8b26ba6532b71cfc55cb722153b3f7060e7a1cd31ba375b120d23ba30eaa548e42071cce1480600755ca71275880a6b2406309444e54c8e6e5d518c7235111eb82096f9def4843631ae1a10dd2df0fb8bbe5bd76d775290e601894fa22c8a97b0f68f9a236d681bb0d07ab2dc2a4358df108226ed0b40a8e846c2bd221344f751bbee5e9e4a33e65a375b9f5039ec0144753b7d380c1ce9647ec0d49302fd0eb39f5a3aa30495cfee2fd159e24f1eeeac085bebec2b5eba8c7cf90a255e08e07c772049770dbcb8338aa9d2f998de79538e1e73670a924f89034d049a8a45f2dab2ab2012575ab186fe0e6e4c42cca2e64e66e80f2c423dfa5058f4fbb821add1ec15cb26c1d298ee46a64ae4a34aa75e103b306adc531b8b19a1a9a86852ae32c670385afc3b880297ea6734d544a99aa2fc95c33d0695a3baed66ade91b8afe4225087863404b78b24c49af95343ca74a1662472a1115937a3c0e022e8adfc703a34e86753715019699c7a0404e140d212bb142353e78f893a61b34805308468de17e5c8816e86acee0869fa47fd58ccca8683281d97e2c3be73862ae1e8bda00d6b21a2003822b94536e9aa459a556997910055accb852a17a3587ae2cc6155820b04a7f225e3bc8a96b76e8be44d21ebbe0a347525456cc562939346f7b6b2cd50c3b5c6c8daa8717aacd8018ce04ded0fa8a9cf4265c95cb1bdc7c69972c07f3d1002b47818951d5e40847f0bc703e075b0db7c17a9f09e69974b5a69b1eb0550e9352cff16feddb354b17052cdf713ef2530a66703003f7b4bf531ebe1e43efe46511533f1dc309c096b590b768da790c8426eeaa09c5854e7e2c9a24e466315a9b5dd3a466a97211890e853d38d941e1ab80337f8dc2aeb723c019a10229a133dc28c995670d25b95d4c8715f351d60d5bf4bd221ee4a85809ffe90254d2c4ec9750889ffffd5fedef02f6696792e410ecdffa562d369e865d5266a14a88389e0a8acdf46ed44d46630bac1a8e21c8a7b36dd483e03024b4c2f0850c09103020fc851ef8cfe03135281d5a76378662976502836fb42c37f5c3d470152a015fa149842432a770cf1223f7314f119ed1488be4af1b357334461e96cac94432ff89d3b52a3f3a451fe659c6efd0b1ad835e78856c2badb706ee54eb97425be82f581acd0d0965ef20fe0811a4778512552f55a7100affc7c475122abfbff1c1282c127675e6ca78ea086695b031c2e66a4de265b117ac6311daf8b9610156c64d5078bc551bbfedc16b858e8d6c5ae5a5cd73c04304b59099549fc65c9d520dadc535cf10f6afbca73027e7506c72d27c16e8b1979b8216019edbcfe661e2d498840f51c91af045dfe8329c2f9aa95791ebb5150f3d73902b8aac28b2c0194267b8f56ae2c4956a13d609a501cbf225cf5e94ffc31267b37f6f73a12b9addd78f93a06f3f3684ce353919e814edc2781246e742f4d9d8a947966b3cdf50363fedee91df31d741bf9a0d569d161981475cc1b554e209943c16b4f7147588e657eac3714b97d2b334ca0ad4759de6542d6a86acdd5a8dc85f3a11946200094f26749c65190c1708956d6f05d5e21b6a1df49a5648955effdd13942d324d31a6318cca31e159709db176dcc24756c5d870ed0510167519048e08b2f2233b931f6d121082887ea422b30062dbce06f91f70ca70c78060aa9c88cbc306ea35a329e5d4095e21d5c1a3ced0f1635416be5770495b8f114716b813fe7f5099d8c36d631866ca1f518edb11143bea27b88cbcbf77e3be3769dbe482c1d78fc619ec313d4ec4e2d85596c289d48f6d3fee43029663948f30c78db565c260943b0122e2efac52b2507af1f31f979dde5855219cdd159df6fd636d28ce87ccd319cd11abf129a28896ffdfba77f363ce6c3c9406090d7c722e997c324329d45a14a25df21fd44dc87cba29c05d0a5d9708c5050616eeb4880c1db8871934fc450260efe954f572170ff784f0b56598b90b17741db0837177e1224daf721192ad8387ed8dd4c2b831dd4ced64fd74f0099f33997202683357cfd26ccaad50e3790763d69d1065c8873a6e0d8844bc9f12eec312f372444ec105e7119a69cc1030c31355f4d7e2d76fc38f6c91d8f9e6b536194b3e7f7af30fc064e9048195ade675e135a409b41442a01c1332b5331619bc50d09691b83d4636c70f6753ca21b22721795ec14821ac5c3e455b159f8fc18322dba2bd7759fa9397a49189fa59e4cb7774bd2fe817725a9e8861e315d6555e0dd11f720364747a7df9c59426064e4a3d45fa3cdf6d78e44c99c1a2c54c16e8562620ea4be9713156ccc48b889f155b4e3c4c83bda281ede3f883d7caebfc9b3a756139acc10b2acf2f20a8813d25b00247f713b234c1cbfd4225f2be86c13b4feb4f64ce7955c46b6e59851ab80c894ab40dbafd62252123feb41e3fe40df5fe428ed79bad9ca3400c6f71008f5c9ba6d98975098cca111786cd6476caf5581cbf1996913f58765dae2fae803b7307c2b16ca837ecf603f50ee9c8418172d550f800d16ad901060c8f41f4b0550514d20e2c1e06aecdb816e78fbd7463cf7439531da63b570833569570ebb2411ae81827ac5ea2558d02b19e071709f50ad98a7781e328489f3e615e0f9d480c95db694ffca276ed20511080aca10c900295500451212e9a8c68aea59f0bc24719c8a6c4522522f46e48a321e89bedbe59e62ad2f4703092fcd2e7b2301fb5939116b7a16b4380e92bc775a4007b6320d1059a1c3413b39cd1a58a974a0f79f8951045743b010e6350e0ec60867a1a854f8a242b6e9ac0a8ebeac769b34de116e4d5f7f455c88ac4eeed61334232c55831719b8cba9b7f7fc561b64a0015aadbcc248793f4cc84a70641ab71d5618c2fc307185a3f90382dac9449f5d5ef02ac94dccbad329cab5ff967fb3f2197592d4da6dc8207a01527d6a5b88a7ba9b0095c1fe9f1d90ecba07d29cf825d2d7377612c5e6cc4b3f8132c7f4ba97bb8ff828e45f05581edcc1cc8f85e2a39b4e9e6b16b5dc2d09985dd24720625eba7db44afe5a4488829f87384b94803911ad007d9c2ddcbdf30347106f510c5b9e55d09ccf2f78f46c511d93e31c8145a3a88fc0e6a6f66c8ec709baf2233b6ad49e9bc62aca7986e80ec7cad677c89a30de199a5ca80af8cc265cb967c4eb5d28ab467b28025a368b63195f544e47e1825e9578b9095f22a6776c0b7de89ebd12a8d6675cc066232bff9286a95bd3e3333e91a4d5bba08677bae1f18a311c9a673d65756783582f6db8611476da98f5bacecd8959d56444319711c9b2200fc352b25bae1706e1945e73edab379f9856033b178990135c161b56ba78ea6caa7feb726f8aa449dc5d8b12d38745aa69b4c26050b313a47a5e8ce51d21d2a29195181ab412f226526fa6e1567ae860d3346b4288c024aaa6f1d1137c98b9534f006db4c734db31404c6e56618cbd52edd1ef98aebfd2a1b4a34684d4e5056b67a543ceaaa01d7983e90895eb63a0cde60e36541d92bf733d37e946dd1a6a28aaa5339d1907155b4180b6764c4c75a1c00de586620d332cbbc8be5b6ae4dadb09e10bebbd30a426952e1a7ef51e87c467a5ec0867b0cc8488dbebeabf63c9d9fa71332a9137d1b50c695911bc82a3715b8edd6094bcb489d9405e779f7c65511d36065c859bad1eb5ef5c5b0d70e9e6bf81d99d20223b83ad92acbfddf5a5b1edc955d42b0fb0feeec7fac34455ef72796851e7771b1143a3786bac8ded26316a78861fc6dc6ea23df5976f2bece8d50b9abc9a9fa25a76984298a2bc45b5a7a86c21b01319fe02561b681975819de15416ce7cc4eaf0c17ff3eb2c8d35dce921ccb3f73a0cdad9312260a4df1581a4821a0c7767e0ee23ea0a55d621ed52530af400b41fe0ed1a3c86d8d9d9d7bd48e00e880982084910997f487de6e01c8ba262858ad9591b445ffada0d25fe95ae78b9042aa59bdc91ee1770488623cc01262c4bccf4e0ea14c009733c734afaa21b4f87f500cb9a7a0020719f607f14238375d0c27bc1f0ed6ad80df8b420db033be1567cab980061be761905a61d9d79b98a6423cda8e43cb357b625280a7eda4490eb03e9005272670b07c5669b8a5ba534d9481679b6e56e5e809dfab3079066af24b1bed7d9f7f5fefdd044bef06f02716c4254bc68e7dd3c59c9d8abcfffcfb853e0fc8cfb3f11acdd7b22ede910613dbd062c2231d8177f8c635022c2e8f7a80f77543c799bd0b48c7e5428ff962634a8aa5376f21c245dfbfad37333a386785a618f371fd5203bb6f6f9e0601375f279fbfaf9c53f6125ea30f8067cf4b99181da490767a3fffbd8fc894f94b3b80538fadc9ae482c6570f7ad10511702d699f69d71b18219722ad8318d250a5dc91d6f642d471bbfdf36f679a08734adbeff72c4d9510b401ef2e6a1d92b1f799d87da4a61a0a521c3a649332a4ddd9c7223d1a41764f52c4e00dbd5cb4181ddcd40f0cb30bc40c6a327dbb8b98fd24384c3f5248301664540a72e62337f4721ed0ae96941a210b3c793099fdeaef452be99f0b884d904e6dc5b182d507ff0416e772bf2b768f1b8984f18e5c846f7f849f9d5d97b5aaa293e740dfd348f160ffc70ea29463be612c95c7141f4c2e87000161c7f4370c71a84f069e23d7a7cf4e7938321f8f67b1c263b01376170d8ebc8e3820590edad1cf8d2d7d1c308b17857c1ab6b9ed91ff90f5ca527396e7bee48aafbefb697283b7dc2ef8f199616aee9d6566cf2d53fd69f3ea23cef55e7326f27fddb8e394baa71a4007b53579e09c0003d3e82c331c94c92b95229b4d7917cd5da62d24012c5771384633e6d54c04aa1f7707761c4589389748dde62489f512326e464f0763dbbe5ad5fac16cf127b6f5b4dbd44be212034c52ea3af7c2ae411535688786b8aa7eb33b56b26c2e4db02c68c4bcd0820610b50b4b122edd06fc47613292ae9ed3e46b31652c81842da8064db1a1309b3b85a941eb444abfcbec83c9637611ed16957eddcd40719e5b7c51473aa1713bef2bc52c680fbf43a8c8f860052a4d0bee4f3da28ddad514ffc44afa5a9a1aec5c181afad22df102fb85a41eed6219a138368a5f1a255e5225bf6d093ea33aac1c627ba94269d4604dde70ff93bd1519d816c0c729da5b42b326221957738922434d82b46cb2651bc34ff954534acdddcbe28c220ab374fe80b3ded11b3e019921665a1c6a3248c32270f2826f7466734d99060c79c2f1494be00ff8860bf6f9130bbc87b4a28b64512396982910bcfd460ee4a7c470fb712c9b6309c9eaa89c36833ba46aa0eddb7d06020b642f4ba081cc998c9bdf6daebde9dbb8ef32f17e1e8316009b03f9a40f9995bf8c5ce659fb8cdc11dbfdb1615669a06d6407ec44da5e56fdd12e98251a03048501049ac028532df7d1e322f0dace9b6039fdf1b17a80910a95e270fba104022cbe841122c83cde7f1d6d1d5b48434fc9b6ee1191ff76f420a445a84b7774d2f37c19ce9ea5e3a1573d4b9354a67504195fd9d19b202623ccfad83b13a6a6d6c971a939276536964cbdd775833e7b145ec6ead7835906f5a4744a33f7e424efe16dc84c5dca974158d283b2fe9e6d3e6855c9a9ca15f695e926de2e087861fa35a96e15df7a79d2f126cdaf1dff0bd4bd333e5d57ac30faaea4ec981eb0751263841199a7043795b836484157698a0e53a5279076a9a4c90f108ddc1d2753d564e2729e177caed8f62abab14340ff26670c4064cb7bcd97eca3fe658c92877d5ae0c2aff48ce1547f5f629a54739cedd65b1035332c702b2cc03d62f21329b6fb41a802a0dbdf474cc3b702c134a77defda8071e19a89420be485bd216a2ca2507e8dee6c67cd1d9762211091f2a7027623145c14357b2208411313a1f0bd3296e15fbbe0513177b86320db983f74fe72774459fd8fc91051550554ba4a0cd451ec8acdfad6a05414aa7141633f907e9f9a831edf2dc9e7cb2a28308cb171c84d35679d777c19760bb3eb52ec6ac862d5dfef6cd826ef04707049fa9e41e79d3db0220cc6f0db84f1c08c36a7e25b80cb685888671a6332d4bb6f4a209426cc562f91c9335aa4453a90d9ef0a74001ebb3b9c188b7707bd1c9a44119036eceef31fef54408c8add25e4f36fe078667bc0b277d81d784c11790401ab7b4c266b761888c41b2ccf0d3e58ec541cd9ecac92ba9c2238c524c5525633e686ea2dcaade7864328c86d0775098769c38ff536c37ed507b55d8e1733e202fd0f336cefb2d4fa0418e06011e31d40a5356a0907add6ca24424040af7392a7706d311b29adea2251e6a9567053d7486f0482c68d53b30dd4f36c087cadc8b3e782d13a20877f7049b782a34449c59ca20dad67a0f88d1ad4dc9be7dbb0841df7da76e57c5c8e55f57b4e4de6f1e592febc28c601ebcdc51f22e79340c16ca617ef532b95fb04f0b36c4ede4ebe22128d86f27115062851c1d8afee9b6287cf27ddc6d487ad26c158691a4f5127575a599cd27557e2ce75264fa3c2d5693a56cc13bfd72aa474b4ef46c52de891420b735d71dfd029e6720a670ad26cbad8702f86b322bc4122648d789406ffbd6554fac28854c870cfe79971ef117ec5c9f28f83907bbbe7bc4e5c59a33d95d7fb5e07cadc1aceaffaf41d762dba60cc1952eda90a07a08123a62df84c6790a8718ef6d7acc3e1e1e0d98da1ab1bf0a2e43530f4acbeb259efcd12b9ac472a81920d5196f31ac951766d232e77ecee010f6bdeb2b48f20075f48f258b519d18f4607abfe6014615ceab7bbfc2cdc1a1c8bd587e1e783fc36b0bc2c0314cb5c62de60006432a227b81e384f26230010d372fa6864feba696515ce8f7fd791216ea305fcae457123571cccde4e0775619e800714df5b6d2d22e3c9680d941c93b822ca7b5df659c39966124ebd36cd1408bd4422677da679b6acc6d6408e0472f138ac4d55502b9f2c2422d985a30e10292e381dcdeb37587fd076b4bb36ce2e7b0990ab0e7c301799873e54545157a89a8705e25e078079280ec9b4bcc804082eac5f4630588a48186137fe6c0b945c1aaa4914f417e4cdc13d643072c80ddda22af0d3aa2c141c16635d9d38f7439659bbe8bb600c08168410980c23a4994bf4cb723de1d8038ff2863b0cdbc64950413a86939ed67e15b77a8d0655c73e256347fc37b6f07ae1c2a680b9f2d917dee34f755898efc187c7079613a81421810bb91f82bb191085ad3b36a0e038f6c475631900fc05f478b1071b20fb9560905e53bc1eeb4c6b7bd2bd2a1d153426ef10d6cd283bc2efe62a4cbed4d299590eac339a43cdc0c8a0cf5f8d77c95e5df8064ac7afb6ef272576b80344504ccc0f52b238d389e300ce5c0828c0212264ceab3f49f48ada27593a386874d4e687f428409495505a34bf26378912f545cc5671319bd11385122063637f5644f269fc73e189bcb45f47d09fbe4060f57f19860778b35c0801702a30498d30c9d14510421ee9039153793223443c1056fb46c127566741b4a5c8990c848c2b339e5db45ad49f47b69467805269666b7a4b0fca58461402b4cdf2f59b74a742f2914eeace79c863837eac0a74619021f4b675e73a5d8d4a5a2e8a0331a310a084bd50d502dc2567776de9fba12b0e24ec03d1c852c01154bf0c929cb856b91017cc773bec74c2ca02f76f23bfa51a55856faa40d0c4c8e794085941d7925ce8ecee6514c406598d970d0bcb66a361928280a6aa9afe32bcbda0ef6bf6de6d510aeff1e15e57dcb8d4b128dfb5f05d123f5aca8a6be694ed003443c10f1bdf00e63718c9c33c17fde696c2205717f8767b609e3df78f51d3b52871ff887891e54a6eac04f5868ac356ca1a67a25ab5a156141af354bb9c0a87c35d59a12aa5df293928f75d14e42da71bef010b7a697345d76b9b76070a67754de01b1082af031e77b87f4d3a4a5ac99c25e76af932884b9588d4d882cbe3003e38578c44d3479c1dcb9ce961517f483f7f9db99db8af7ee339b00160e5d22cfa101b76af16afab64e90823836bab19c10a3ceaf093f906fa2a2786bce751b65733d74767c5bdd4feda6327c2c5cf254a1fda5ac9b56f264ca762260d09bd1c005bc953b0d420293345b6c0cc9accae7f1098bf8d7464f0b760047721c5bd2127029d003ba52e3614cad3994d66a479fd29d7fb9ebca83352d5d97d66969c40932454604d78123955313a9a5d371ab3743bbef924926dd8808d175ac468809cd2be9759395c5794b9103dde690cac75de4b99ea5d1bbd06c7dd566c60514ac332b6276fe48ee6b27a86a96cb00296adfacaabc20b5c06bbd993732dd4096ee03bcc25c9b3918e0d33a6e31a6944f0821a68419faab5d1418f334e13cd2617dd6e3c179cc0ad9fee4675d94ff5033933948a8da3a2983292c0d78596f8903feb243d820d88af7e3155b109a88da8930b6a7245ee3384d95930e29b755a48cd62c68dcf26dda3e259559cb14760946f3b716ee6f8d50ae730e1b708213b152bdbc816c8ccacc307204da5a38125f34365981e01c463d7c7bc9d6e491100a75cd07a1c71c3297f9d9311a41f79182680f6ad7ece260513099b5077b73a9e4f71d15c902a3849543a427864a147df73ab15ef26ab156f471df5bfea340d321b0209da84bebda396c49cbe3fe21fba62550713dc9a25c7209f5af3615ce94f68b3ae854d4f0f241ccc7b0d6789c9e1619ec4b87a60860ee127eac4476d447f3f5a767f5c1c4587a43df7ae3ad172a3643fea0724889299a13ee67af95843f3e44ff7782c441b207c0566951e56f9baa18c1504ae89bc821163e41dc2386d24cba2fe520a98da6a442b1c1e02d42ebad4fa82c8f5953974a8c2276f1158582ab2398d5c50d30576be4f2857b57a4b5ec3fbf86b3d113d893bc026cc4037b105cf579687fbbdcb4291e325b90107068c2f7d2d37721546569d6a9899eafd1c69700c8cd47b14ffb67fc07ac4f85b05c0791dcaf2c04f1288b9235e731f06b8cd85a19cd8f09c24be1f7b73ac86071454a2bced908fd83786d64d23bbe78061c149b479d35dfce3c8a530288f146b379a00af71738481a13807160910f6389d5ec227e1a09412ecb7b80f959d49e019897391ee268dc7880d43e3d91b9962c48f91040c15c46ff8aecb87af16df3f23608403f222280b53d706e406aa6ec51ea61f0f0751d268e92900058fa028bf61350bfb690567cbd0101370ebddc0306a254036f7358e580576050b40fc58ac7b4d4ea4e704b07aa27e3744d8b5469688df55e984b1a8b93216bd84ed04813efae749db6478acf81e26409b1ac4f90f91050828f306a7fb1d313500751e0822158c055d824c713a88b81e869989146a496b4efa0de15eec43bc6dffb608849903d54df012849a1a2a982b2c374f86e50e4ba2fdaf75d4a010f2318925fc4901dac8d71c406fd120e2b88535d95701da912b94ead6215f32d80752d2f98bf2b86a4dd3102dad0b9eb491c15f757cb0714bf1760d75a036821a642b308b952097ebafa0ed5337d2b15ee2fe2d089d369071c93f3472e6a2fa18de1424c86ee48ae73d4dd5b86cc2f1e6392667bb4c2ad74358e384c8a00bf23ff0d7c5750baf26cc12712705c42eda5b26bdfb50aa466518ef25f4d3a602fa692092ac077ad9d74103833725c702213c09a9e59d86d3514422415a90284f74521a0f66c45efbd96392247c6528b28c8dd3317805719246171684e346170e779e621728aa104e1443e8afa1a71dfd8d5d2c47df4d4ab63805357ea1fa288dbc1e05799916145fec8616886a3ddd591c67f26eefc72ca004fae2bd121b7c5af4422a736580425156906574eb7e9b9149e905551bd12804cf3562ff2847595494381c41481de3a9355c6e43c6bc0c2aba0df77b6ad7416eb8ad21b0853ac5a30967093d657b065f7dc9d2c24cb58edb1873859fbf5a98228069c00fef278a56028c877871bc9f232a07f0f8732643c4c900871eaff9bbb34742b61d268de7a5da0663771462114c2d3fcae24a069e3e2c664167e94aa63c2bdc1358cb98ceca146bc42c8aa8f45843c7eedfb5c70c366b10a6463c617ed258ff6d56b583b84a45bda44db1a23858672f3594a80dfd35034ce10bb37059874e58ac62f72a14c86cf563fb508d558317c9818c0340bbd30b6f222d7baae1e12af96b96e176aedc4a9c5d0eedddab50bdc8b2462cc460d36a86300b7822abf3f31bd66e500c484b3929019d8477e8378a455028b54ce0605a8216976c19ed08263c1a1a13b4f01f9a14e446204e5928b4856ca858573022af675c4e1a130539dca3465326e897e5ed48b72602f075552ed600f53157e4c374e4c1f0308602c9dc81a8087e3cf06605eb12a59541ac9a9d88cf6bcf865f488daa436068aebc00b5d2c0164a91c5f2fe1c519c725b3528367ece3d1e93c221d28af72c561b50fd8270f18aa09bd5043716deb468969a88f49a058db8267cfd0c2f81cf056e8bc00cefb2d461119f6bc8becbe3dc4e3245b7631d80eea67342e60641c8e6ce6eb9b8141686a7359fbfea68152709566287590978381345a8bad6a7e054efcd7963438bc967f178a06299c834d5794aa4d0b5ace3eca9f99885453fe016e21f699ba4bda988c2305e8ede41cb6d7a7f365a060d118df863e5e866b2568cb4fb516e9d0b4b1dad4baf4754a68c47d8c6d6d2adf43c6942cd609cd3d980babe826394de8b4ea1f8ec2fa136db220e964f45f3f3e67c1a4884e8931e263d3c4401f925404f073f6538fa8e24c1a602f55a74426b65fb9cc39ae4b365579a137784281848cc3efbdeb940e074b1fc586c44a7f4b963e060b521905fc32a4c099f828001f17b8f0f3fdab3aa49fc6c65ab202dcb7e78a4c621e23f361081591d1ec5664ee6c473d51fc0750f3583267d13024fde9fad1b4b10b5c7ec43b847703c4f8a5946764cbf4ecdbdf3c6bc2c380e8c4f41b6931d55e2d3dd332279de8220fc6feeed3e6a7cc3944dc989fd529a241aef1dacd249cac00d827145882534647c6d60d158134bbb7c3628743747d67b1a97e8aadc4e277c33986dc30bca85f8c7db50d4b1964de2cb71b3eb21799aa0839521e925716c64c234cc4b49464365c60cee8f3cf10e239de7503b635c757ef323d55d84ef30804e6a529bd3acab46c4c113b278c10fe6df4bbea53db51f8cf1eb75a7c563fe9dd791b2813b151c7960be8ecdfb00745b170c0bc681bfc8de0b31616192ff1a7c384cada201fb0045eb4f1002a0ba1d229c9a5519cf8b968c19f35e157e0783faae2a40ac68621c1f71db7537bb9d24d0dca7a5a156de78706bc117397968e6bff15aaa78d9325f1becc06e3091892022ddf29d600f5f8eb54c6af9a95d45d518492caf89b6932a6f4a6e6d1c0037f0aa96575e90b4a4f2bf0de3ffbe1e03a5ef9bd5e24f91a36cef9c032483adc3c7c7467ffbe2ea67a0b8606a10c4e515547735ab1007c41ded0e7d63f8fbe70037183ca69e4f97392180e118c1038ce346ae506055b3a60c6117e395b8c8520276eceacad34767dc01b2d2a1ebeefbeacd0f77d06890da0935f018033fddf9528bef19e0298d40a54f476f07f8070d758353541295a907a9bebbe6e0a3808d40ff47fad71080447c8a1e156e40ba2a42bd44c99832cf21978a506e2360ac5ad09a8a30e2fab0282d73a158cd08636563bd60947eb443e7c6ce04908abe53448b205eea22b24a90bf2c82136defb0084ca014460f0293c5e13071bf9593c6a1e90f385531550ae0a8d955b7b308f28ac27c95f5f1639813ecc45ad26a330dcd2b5378cd4a93b0787b432ddbdf6d413eede03b1c0d2b43c5e3214ca15f8a7561cdbb6d681ddc2a017f3784ebe46f9e0d8279c50fe3209ad53bc28b2739bd4b1fa9c2e9447ab044d9a11c15421c73e6499826869e82d44d3f3259972c4e51a08348711ce17309608ec55a9f4c8b8c24a16f80f7477c6d03ffd1312b44c0537e0937fcd968553d663de256bfb59ac4ac0ff65cdd56f0ccaae292112b4d4be06a9c95cd091ad379096f82672448e159649106138476c23a42a1714974754275ad3af2bc511063ea26caf08abb580576a2eee87e87899eae398575d695e79b21d73b21fe8502a931266a799aa1a1e1d3a21ecf113202497d84f181e2dbd8f4b0ebe489f14b1763483c86f138fa0d27fb326efde2879029240418d0e5d724f2e6ca5277d3141a71c78265b0097c9710e00ffff1df656bfecc9af9f9bb870e2b0876bf0381ce21e76546977dd2c796c0eb4af60ac367748aad3e4d1e9a49fb6098b4226246a266f2cbe76dd91ab74408aac1043f222f49b89e4054053e0d5b2f8f4be3657cf13fbe9c79790f310f73228e7b52e00634c2db1e014e33312ad9e1836ed64c943f2c78bb14313d4dc423a399990851bd34903ade97e14db044f81686fb2171356d5a0614fd40e1ae97b33647a96c262d382276c9014c280818f06965f1ace0a159845c6a12c93affdeb5c7a44c5e38c047a30bdff302d2d01d97f8c3871d50aa2e4bc17600be087a9eee8448bfd91a98f51a8e763d0deee2f8ccb5cd51e4f8f3c1c4aff554bf1cd466f4c2e330b4cd8bb24ec40fa8a1065d8c4e878dc335b3f93da3ef8c82fcfaec07c9842aa4192138421537a07083882932e486536e6e04a32798426504d0edadec90eed41e083a04d435a77da66a9d59266ff81c2e485ec0cc599dbb7347d8705aced5846d956c8779f9de80bbdb6cb00fc2b5b322b4358a54ae4c7570935ff0a5275c20050ff08515e64ed1e46e7725df91a5a20169b42f05353e5bdcb61c1c7deb34bdb1146c80ffcee16eedb270a8f7cf21759f78a1578cd6653650ad4563671c6e48d145c9eaeea905524fb5fee9b22cad44a2a1240461735ebbf56c607bae54c4cf856c9a39cec1175bff045e8e77728f4af2acbc41030550ddc7282c4b0f379d6fb7be6f0a177d4ff1b1ee1f54026cda8069bbd8c8df1200502818dc4722ebb2ee5af8c5835d92f2219cedc1da98ab6ebe2fdfa1d839e1d2dbc0e46a15cfc67361eb155ec287481e6fd9115cd96969d9e4293ae948c76e044cac53079b3f79394ed893411e0f42477a916f5b29e80984b28e952e0c943551c9964082509aff0d50e49f966bcbdfec6c8ff0402038e72672ea08a7e1bda7e0c9e2e1862a8121ae65b578c260047db4085e4e81613cc1f9e9fb33a1dc2d98ba13ab0dfe796c8f4c907000a012db692ab29793b8520ad05e1012f354329f81627db5efcb8ba79b85617aa4dc174eb0311c0cef22417f4096c6d7fc3ee702cac9009e68492eca555531462a855580d0b360c3318d0696de6a57a77a009705e3d9e3685267874f213ee589af76d2066a05a41bb3513ee4667b5474b5d8fd299b947c8e318d56f58e67a2c2db3872e736639a3bbd074fda2a4a938cd885598ff56fc9237d116d576b0c3bb080b2512a6319c534f319e0dbb3385340bd58f2133ad1ba2ebf651701c33bfc9dde4b025758ace1777f43ac952b49ec7c732cfb9524317fb06c41ee457f194af2752c9390c28e665af5be16c2829dcfb633d483ce9c309ecb6e6e93cc765394b1f3e50977db20e46a164edc4b02fd6512ad7eb35d319b83d0ce3afa8bb874869cf31b702e7d89bf9ec7aa4e415470b73d62d374ac0e23d5b9efe389cd98df28c4e2b328a6fb11db750c4e1148d129aac76a990904a786b324ca6f113e817b3d450d6457147aeef328e0c59067f5cdd8e4bc81062346edafc6d5991855dfe767d0b0d4b2498c229c1bfdda586a78c54e6d704ce2411cb24ba86fa2a7435f3d33696a86c377153d3baf61206278e8b19db52ceb2dd068561d41ec7f51c750698f443c1c0098d7d2072155a6a68eca70ac811dfe3de049162190d5aa9f17d0d30a6d33c681a8d65e8ee8e2090e118824c9baba7848691856d93d8af8cae037895041aa4e2146d1686dce1cdf6af8b683d85c2dd9437080d194692d5e21c2abe7848ed091455c4a27ea0d985a4606a19abe0e7ed2660e65f8f6cf1df6e6b1707cd8dddeaffde6c52c0d574d76f3ff88e681cdd1fe75d16e46ac6b7d620ec80c4c86a2950e7945961eaf2b0f62ba9c8cda87e13b31e0ef4862552e1899c8b32ecc27dbca94a62f9742a8118037ba5fbc7930cf654b2685ce1bcbd61d4601270bf61c70a6a7b4042f61624bf2cb9e6315ae6a1d4a23ef67a976870ee19eed494a0d3958431d9e419da6c252a66421b5915e9ee843ce9c5848b9633a0a753f3209d4b20aa7c86ab069222d9f56d4746fab1de9f95a8dc881dd956c28b9f61d95e5a569cca82e605776ea3cbc9f2981f8d7821c120ff47f935962c83d3bfc1a4a4aa697431809d9fc8d7d6901186e3b0d1d6d7b173e5b25194d9cf52a8adfa2af45b7fe9e980b1bf1b4c57ab0df6b8ef034e3e0c30855b3a8bb6fd6da006e47a37ab03edc15b5e0c5162361b89225be46d9e60fbe5f6537ae4f2a623a5b6935602e579c3fb7940ec7f222b7785cb57d616bab711603da68b2fb4e2cfe508d3a0d4ed2354985d31e7e741d53f3c6c3ee003a2ad82f9a366f052fe20b4cb43b31ca19713c05ae7548fe006d41f04bf53e25575e4c6c0a92a54857ba42770ff39ba8d8c1b50283622cb7657b0369b90f70469aab594bc261898ac4a0a180099ccfa603918eba3afda96d5cd2ec104adeaac9b79c2fab342564111eba7d3a9eee4341d1293c7e0453605d0267997ae5cd98097620ef630d78074678f133d023c09347ec4867b29a72a915cfc662aa26007de293e500212c92b496ca1c0fe88e1d0f3e9104c252b126b8372537391a816fc936f3df4b403f6902cf676c41822a0e79c1eda8d79b4c5d4e1b499baad04baea3f82c7f9162bc5b48699058f27f702c1b45f9021601b5ab3b278cf0e98bb1907fc0b076b66f486985ff8565a57413704dc4dd5ed8d3dea393aabb21c19f6926975babd72c129fe2d2c39b117754160a15da75f5cb292f00c6cfe178c65a114b9b5c0e2b750c8d13a75d5c28488532ed4addd6ccbc8b4ce288f51d977092dabd2db2f5bd3bbba5b36cf6ac794f5d586e412bc68619905228b41290a057d525e8ec918c99c1c2a0c91de48e2d46b5012d8ba26760134131bd8f0047707fbcd04eb2c32b06c883f2da0658db165d97a6861d41ff8c38d7366701b61095515403a763e361118146539d81cdf13fbf0c8bfa72c6dbe3320d2dee80164d54ffa296ac197d8416788793682c94fa2443206f7e11a27852098fbc9738613f8f89b9e114821665392b68791e4087a92059fc4d19c4834a32fcc793d6df4aa14c33235a214ad3af2a0791a0a8a59e5284b3a5d4a618f7a33f391318d6105c09d4e195df156b7ba1decbfa1c17a9e132c3fa08caf9ef5ca75151552a8a0b1d0d24c0302990c44c875c29de30cab87cafb0dafddb5fb36ebe0c835edf7645280cd5c20ce70dada8f580afed9230ef4f806e1b273a17efb0194d1a903d7b04e833da7d5645cffe5d262b10339996ac828359b13b871533d9a4a0a41279843c324fb224ff322441dcd7fbb086301729dd0e8c8ef662013c3264e211782bb34128a04255bfda951b610ffa5332adcf5ad2404cb54503c5b8d017b6218e529b32509cb3cce699cc3921610cd36e938f046986a6112ed6f9df94d0d62c724e11d6090feb538ffa312263980008733a0cc20ffaf71b49b6943dbeeff9558a5cb69fcca9652d6e10e9a4921e3aa1b6b1a10fa9bab4d54bbd9bd5c1eb217d09c02a44082c57a0cea65b1cec820cd5f4867f755c4a8cca15b09fe7dfc170b90048547f0db6a48ba9fe540e958cba28dfa49d05403f997a2c49ad53730a727bbf46d41828684964984ea15e815b663ab1c87f02f9e019e9d82cb406dfaeef6f5f2034b27517b4e7ea77c137b34f26ec7fd75461763414e20ee44984f5100e98335ace4e561432798c53ad25ed069e7c7e7ef5e01b0c24323501c107bacadf9e005c7a8d765039c804de6ff4952ac2c9bfa58307b49e74b541cc459c31c8cac0e63035652f01534199ebde81357968b06f23191f7b73ceb34f99cca1dd9c53b6e403fb50bb1a215b31f6b8b654864cb53a2ea386dd606f91fcba6caa807d5606e1371040e22e2e7cd9c47c910308550cdaf38c8308fb765618478f54e9d19495b357be02c2a37ce0f2a533862814c504e4274ee29d1690e6d06395e01fef6138e7889531df28cd90e64b44ee64a77c59aea86520c52405944db25fbe949ba480a4ceba991a1d46a1a9291c304ea3742bb8caa3c49a7e0d85d3b73e0088426d944630c7b1127ec13c1a8a8c4f29c2379189f0a7a4f138c6c2ed7829a51c7551136c6ffccc4717891f2ae537315b050a8a76c6ecd04028ec978260902446387597636cb4a68b824ef8421f83b801478c5b23ae011220e3f725ea56954533d87fc7c5106504e21026be9f92506eb8d2fafb124758b29e17957ecbb8daf62927c6c12b456fb6b00a2893e51996431ba9b08e03c3078aaaa6d918ceca0c24645103755a918d66a30bf82494c5c7e7c65dddd8714ad3dcdca0c99a93e4e88f31333ef6d6b230fd601dd43febf7be3d56d90a2ea4266c643927b721f3f17a0dea82505124ec9b5fa8ceb38324d40b0eeae843a68a5a5e78f7a3084c723bfee913ca33338060d412d875121098c16003af14a5a6ebf05b489c594bc45c2325a954d244502c825128299795541ad66bac8cf9af4a2f28652c9ed9499821f4e66d83aa27c4928a25ecfcdb9d49776ffbdceb4b76b790f0113a359845338e52c6c3c3f920a7245865f415173a918925f805d131d94e8fce190f1035bad76bfdf199a7e6a5fd4887afb649c0bcdcfdf2328f6345a7c0ea6334800e594bc296cff25a68c20d9c0fe087ed37d1ac82886050d82644b4a579bf51c972e3966ce2c8f7ff8294ba1d24521a2fb90a8b7837fb2ac8bf60e0f50c44fb1b39545929feed3436270482a89874f23a5c99ddf28e3ca6daa8fd1cb0b282b3b9e3e7b8b27692eb6f6d242dade2816b0d7e71f619a32d22f9a78930f0e8f069220203be4abc8083c60fb81ad1d39605304b69c27bbaf5203915731954cb39692a2ba3e6e97452d438aff80e651bea89e47b96bccbff6ccaf0fa5c347e75d49d77a1485899260348f949aa850bea11ae3ee6872f279d926c30c9205f939cbae06221eaaef62c169d543effd64b13f0f06d0b57ae220b935e9ad7c01ff0348c9fa6e4707bd4c893a1888c4500c3f89c0e31c905e2d0311a9d8b6b3a79d23b86c2d77c125e19357829bd8b1bca7e2c9a392fa42b98e27f2b5ce64bf68d6c0817fa3774037c57cfd3a32e6595a956b9bae2dc8c580ffccdc4d0ab0641a8a86391e9bb738cb2f9167aa240e3edbfb94cfbef50d600ec40a50fe13afa53db3c95abca3b27c7a1626a54a6ad7b4db4d1a7547451c020e17bd86713ede5db2e92b98bb2fccf58a29bc267efe4c5949cd63e895c31a0614dee48cedd26723ef0fb0f0c8c88fb7ba10b0c1a387e373466db63c46a32b18e8085e8540ec9318697a606bc7751430ac66c7f3411ddd56b53dd60db86efa24054d944f0cd963e1db7c6a42e69cbd97506d14686c009ae2d69ba575848ba283014a19b1c045b9a8bf20f03b68c8b0210ae6bb5e0e6682e0203b17b0fe2177e7c37c13067f104eaa91669cabb28b86815243afdf31d9133558f29121986c4f3a93366702eba2848065f2a8d72514b2f528c8a69e1caaa26f7027f6a1de61e20aa9608131705aa7e2539b00329759633009f6c965aabb9d6bd237cf51aad79cdac8b5ab707d2b16262e7b96d0e49a05b7c17f570a5132c3693ba67b4ae2eb6c126eae324a8164bfbe82a8278cc4c961ac1b06e16045dc56fed3e19cf04d4580884ab55a92842382346a366e27fa8e15c4be6545133e3071c7fadaa241a4d45115e424e2ec809caaac7e604bae76504df3c5828fc0332a1a242d1769bd3edafd5466f86f8f03db32cb11e806860c206b421ad7b3b1951d53adfb870e95f448bbcfb6b5ee7c3ec3c57bcdcb8afadbac255b0df0c817871b341fda46683690ef18e896ad5f53ddd8eb1e8a8782661d68fe407062091274c94b330664dbb5ba40aff17069fe4f4c569728a394f4ebdec7b13c57492b8d396026730f3e9ae498dd3a4eef1d170f20153a971352f20842526ea0ac3cc179896ddf8ecb499498357609286b14272260a50b987c944e11efc9d6e418c32d475db7630d5cab59b7318a01fde53b1af06e3052ec4b039513498bd8bd73e718b5370738e648bf25fc20dad5cf99e8e6c66a305cd5ae5518bd8c69875b48fa4cbcd5b2a507e65b9e48617873633870c5dd1af76636af708f7ab5072ae02283bd506e06682265caa9bc8f013114a252718812b1cbc0818727c0fad43e1a28273b9c8120ce38501ba27a0d5aa91a16c7bc0b67ac62dae0bf961c22048314f6930a70786c8bcf29c7c03a68210a4b29c79eac050c2c1ec7822acda28cbd9560296ca5acdd431ae666501971499d5f977a6ac682b172e8bd2fb645393648ef44cc715a6c7e697346db674ad80e01151e09c9e4f1bae15662468ed8fdab69f689235c3b699768d4bd3106ef1dcd1f6143145c019b51dec4989cd2a831aa51ccf456fac9491e2ac982522be40ed66854a262255814450ef830e09a559f1554fddc124de15fb3b3a80268945eb02d0309a3b8294c656b9fd5d01a8c595834ba82fb217c024daf6cab0528b6fd9ebbf507f72176bf31fa6e04bc4f7caca934bdd0ba69246da648028a08aae85e419a3037abb9a4908435b7f50b9c7d141a40451e32dfdb39c36328f8891078037e98af3f0e8ecc3467460ea06da02382ae2a1143b5ada6b5253f864adfe59fe1046b23fa2dd3a52479b953c51f3b04b53ce40b48f2279fa7f48000829afa54032dc8f88802879872668fd4959ed326dec1cbca5542b6a0906ed66b7d757c0314d511704f659da8e17a42c8247e1ee34d0da36968b444782060450c3f34418dce8fc4f3c3d290523651fd9a577508d76aead51ca4fb880259d96a7f5ed4bdc1d205031b2a056528789fbc284776ad69fc0b4371c6324537e2a860d6d7ea04ac2382fd733a4f5651d993551f206bd7b0fafcb05087b783ef1b5776e333623dc8aa720a9b53e011139bc786d81b43e2a2cf6e071b84d7769e379aa5753c01c3cefd552f5a4c67cb2c3103cce6bd5e95c2d5d8083b33a45aed3b5e51bf7dfdb9f78477c63444d90f72ba85f527de6a7e14ca2a98a3c4a07cc48f9d2f2a8f08da8b585b48ca80581fb2bb58920072a496a527822b4c01992ea9b3d93caa321b8344f2b7a17a9bcc13628bb2efedec55672e3ce75143ceed6ab0d6b5001dcf78a224e6c36b9b92142c742b6d46b51cf870817be2c255e177f4c20dc00a963ec8c92b7a5033bac36519b4dc115241312d059396405ed485bd77490d38474320328f63e0d75ee1ca7a691e7dec94dd410dc59fd4d7f66621dbfcd9ceb9c54b16e631fa3fa388c065af5fcb6d634bc7a1e11497acaab7c65d8018da82ec9002271e8317739ab481859ab201229dc87e6b8ca752f380a5dbff2b0cef92a99cc4223e93e2d56294df031c7d75f97cc3fd7251c4fe1f160151e5761e581e245db9cd92458dcc105291b30b13fd801451b21b1347746646fdd039d7ae5a3609bd58f9e379538f8ed98d0b911947a576a09b1ced18ae7c4ab859a4b5e663114d8ad2946f282cee321f85a5caa4aa4cba51607cdcd8d4d9ff07599dddcf0cbe741ccac18089cc0b7c8dbd404258d51f236700c09a1fef83f5ab7161c1b5835682a54bdbd791852c2db0a34c368ea3b39b1e4b5192501d5a3f29145b3509d75d0e06c0a235a40c3366b1411544591815f90bdd5a7fea31c839e7ba8f7fbd9c04182dff7b74bb107a224a530a6aa1e4128a94876a52d08398dbc47132cadcbf24aeb61c5bc4851d02a3bf24881a7095b8fb354f73417f57bc2039335632d62bb776e607a05ab946ff1205805be1d4d1b4e05e3195b09649e73630f3ae09dec162dcef1bd5bd8447d9b1f298c86a01de00775a8fb19d06a2696c641c82564b0b6a46d1b55db5530f7754bca45c5a35e716b6003313dd3e650e0d40bc0461caa88b1ce45bcb5d2ab14fab830da8c7284190405033fa3d814534418e6af61022c6dfe7261155d38bfc38a62a206d5689ac4de5ebec20a7433a00fb286b854149a30074cf14a4d576d7525bd563bd7ee7b40d98df5982b1e07edaf52c15057aee20d3b05aed705d7f11ea269647a4483857769d58c135a2eff14a011366dfaca1e69390c8bb966e7fc8f16ef878e1d215270135286b349c3f629f103b0c6dd929921db97e1621943946b4306eb0d4e8181c99d1d4eae2e8d13edb6ddfcf5841a8258b0e74a3a1c2ee59f907e2873b67fe08a6312e96ca7856448637d3c56ad818dcdd22c6572b0b9defe72343eb9fb683f88950c65fce0fdbf5f7a2328d41315e8d57434b21196c0ab45ca95a36be17b07978d9fac350e58fc123b7a3809e130ec93b7410dd073813b8f38f3c24b9a4d9688607727e3b0936446e51d7ed9198a324a65c027162da8cbb94ee969f4003b7c9c5109fd046756b69be5a29eb95c45fcf469144b6cec329738490e28fc8a7d1561dba8989cd09b91c3a3a76fd9a2d86083c0597fde5766f25452faf6f7479f3f7cb1edca5234d78c56c59dc1f66b052228fdeded0358db69578727647a2a4dd06dcc84e3c1102a05e1072c70b7c1933bd83c94ea236e8e15a71c34f1ecd2083977248a83eca0be1f79def8ce51090f215d7ab13de7430c91bfcbc4f290fed0e22d944513f0c7156d118fb9e6e6fde87734dbf823bc078e1d416caef148b0ae39e17f9e14afd132ca87886ed55fa95a4d53645b905761320cf2a6e6f103914f260c3a4d92c7c71a27a947443cc025409b7f02d790e6ac6fb7df17caf5cbdc9f58b96e5dcc00bb22e87d8feb234de47bdb3fad3fcc2105ebe6f24ee66196f106201cd2e6156a857d1a8f57dc2a8c1eac917fcf08c0905da3591412ef3df0d5648abb653c849214a323d025cc3dd650f5a922add75a93446fa5e707a3714a132e0bca371390faa9e9824a9da5cab9ae184ffd25d79af112418b6ca880dd3ab1be0d86fd28d2e338b8873eca7822fc0d74136bfcd5609380dd44464d13d887eb689445d05eb297e0b0e22f09c630d48517f7852edd4358417c01d0b5a6fa92d36f6906a28e07d1bed1cf183dc499b335e22e427a1252bf7ef830c5bcbb73f54654dcce05b06a2953729e01b188536e6a4f1dbbf23d805e50a7c8dbc643ee16064c04badc316a9729f376cda589c7fb3051f975fca85de69351859b3069046fd6a066875c825cc3c5be4f9ffeca6d5b7ec5b25e25e94326968623fd00fd0e593e6faed65ce3a8983fb5070c5e15821663206a850e0075ccea6aa4108568bda68f38fc1a1d43ee9c9a31b8dbd1e4eeaaa1446ea18bf31eb60d6660b83f18798132595260b4e7b696b97be3478aba21996dbdf9cf4263a4b35699f07f98fe527dc3f57cda762a6d491018159e39e455a56bc830119f7784f8349d39031688dc3e7927197076fe794c84a58ed72f240125aaa0bf7c9c5eef882d2aa5384ad5695ef9a20aa7d785f069d80b42e094553ab2015b8c9347ae40111315262e8d7509dbd64b9f6696873a0ea7416a67e441c83ab0395108826948109ac4c52f31cf89c4c5267c4ec368f089885a941acf3aa391f068c81a2d9a0d886e90152eba6a723763fdd4b7d45a2cf026657eb1631fdccd041c8f33cd056f89ced35870084eb7eae5019272c888144ff2f43b6a9b605b44b5f210685e1b27611b382b5cb52909e198d24c5227fd73450ff0cfc8a67364d174c7969109aeaf5bf137854f51acbf758e454f152ed91b1fd494c7d03d2e4d6194d4535fa57c49d442b06eab285ffa1b392361cf24670cf700f8144dd465c24732a4985014b892df19bc9f5e96927d6c4e06e6617f66d8b5b52cf98235d199c5d35710e5898f2e78ecedfddf86d1b1d4439c63a2bd4a69bdcb5f17dd9be5272a58572ac3130e3432a5206d97e6ca1990bdb7a65f3e5ce0b9bce1bb5e65d0d4b78c4fe33b871ae1bd22659f785add704163659b0146aacba468b759daa739f15118a77af56aa77beaf1776cfe9a6e2f0d6d8af204c51449605f179288f92740bea8dee3c5faf9bd3b57e0cfb53b7719f834c3c3207d8fce0bdf3901b56f953a3f8214eb0a30ba9cf0a379b9078400d95019791151ae65c7b2cb85a783ed5c2ee88c256590028136b42c434536671818e687e6d247ffb8e1a45105062eb5c4a323194ece138b389c1881d6d5480ba0d0881d08b4851d58b96cc1a3cfde745073e930d88751a612dfb359c868810f63d47a0d2ba4f355472d24b48b1fedf54970c18bdf43043489e1718bbcfe45050343a2006c60a40188bb3356c8869cfde1d6a9ae4b11f77f73128aad46efb427229ea2cc6c9fe6a113f92bf467d1d0e66adceb927a1eb75195b5100be18499214707ab18c1d4a30c4a39e812a4c94768876878512881861a96bd2eeb621fbe15c2ca526a768021c2bc686bd6fa0e84c7f7c1ed1dd7b51fc00463fe4461c2e0e5e0de1b85cc919f0902eddd2116a9678883e3711f6751e06f30b0520879047620706715023378f449c52df8ebad957ad146deb8b2a2eeddca106a6e9828681449fa3ef13728ecb5adb8779963cbcc8b4174303fc7eee574c3c5fff463f0ac918933c4913fab207670f86b2ece22f0e6003775cc6e8c22fcf84bbd7f18f00dd9d042564bc860a2611713984701eaa0a486121ebf8b52982d5a7984e15aeb5171a13895f942d110ba9d6f25f2d04b2a4662af52520b541c9a22a93c60c0ad03f32040dc1c4309c1fa6cb31a1287e960eede92bb9b22c998c53a82c07d4e775141976fb6e914b7f4da40297f97df7c28590641667e7b416987cc3a82e336eaff5f345d3826b8901fd1bd3f5f2454f72ce2d48d623798ef23be74e98a96a73202846e61a3d1810bc538a86bcbb689c100d69cc8d8dddfcefd2ab62063ea0a4451be31b77cb86e6c8234c9b0a9a2a71fb169d93c578163b508d42367be5d80ca55d1e42468f46c5c6aea2f682dd286ab8b34c0d116f782282107f9008e1734d567867615a20fd5c8d282951a759f86ab02a95c11f976a0aa9440951841c1a2edd3c9455a30a370fc2ccc424169fe189c9966f4947c334f3bbb16c2602b65eaef96449ad5a873e44e48f567935a28238d2b0cbc626c30ec6a9b63172eda542a4f6d7ac55589aca95be5101a4ae64ea81896ec2fb01993766f9d1f35af526538c214c5d65c8b7710a2902834a7146edefb6d4153a6b0ccec06d6fe64c0baafcc23294aaf39e18adde4a41d5c8e67d07230942e0e33d6cd1e49d4b643262d940271b268db3c62e5971f04865bc4c3615ca9c761c5b513d11e060748354065e7718975fe734062802971361bfb28d73b08c6946eb1bfe1e2174b24f3341a9f7b7105c67ce2f2882a36c01b3b381d04b9b40f22ff5f497ab7b7b0cddabc601928cc7789b92c2106f3448f89649a11f431e1bd95a92b69eb7bff55509807daf097d4530102a57478cba5608eaab4d06230f900e1f5420efbab8138c24a1d0a254e36561aa77880543c24f2f5e6ad890cd3b2490bf0ad291534ac59e041b73cfec144596c2d9281f01eeb2840532f36c95c1d61759a20c1475a6a51ec26adbab02224bcd8f9e1ee5eb7a5f6f84704f2455818985412a44f53fe696b6c80377597076679816150604651d1df16621b8199fd4ee41b87f29122de44bdb89a254b0a31d9b4e59387426ec4481faf03b0261da29275c01b0eb0251762e16ffbcdcc8944cd38f3e1f4e547b66ff1763203992e8860c1494e66c444996da1452a583d666f3690a21ceed5e021eb104fa1cb62f420cbf87c5dc42fb4700e42080d7781b783fe62190c4ad68d9a3e3e9449e30608e4a3abf0f6222a6d194f6a81bb8d9b4f821ad9cafab6104f32e86d96f0105ac899a05b94b946f42e30cc16bf249bc81383864b1685b2dfd406a97b63b48bf8f78fb10ee1bc5f26126e146891351a6c680f27514bbd2a343e7f293e089129e8f296ea2264ccc2b2a7de15e366a2c6769376c72df51645ad7584da61e882f228034fd8405fdf3fcfe6c9ace1b3f8e5fc664f46eb88c1032d130f167807d54e2d9850c43a9c240f7f14ae987a9502beadd0c39b5195d00a61c8365ad41222dc26285edb93a905037dff0d68003989e619b01664c789d036c8e3947867afb1f2ac65b35f6ea12f0e6f25b2a6fb64f88925d758c89362a01dae8bf557726397686473fd210681e39510e423ee27028dfe3ce7b54e2e792840f9866629b59cf54fcedf177a64db737b727ed0d49a029df3d880f77e196841bb31dfede10708c5411697397685133a746d8a9ac7a61e1226a28f9c323975e8b89b66fb87f875e6d92bbb1f74e2deb20d7d193bbea10f88669fd199620637b02637c04221bb2c3cc299bd1577739ba84ff1020e531e6e6aca6e6ae106be6bdb6d3a7365a8ff324283893950191a4488843de58ff4e7dfb0c4c177633a9adf84a1a95b63f1b41cc1c0f551a7d5f764a57307805ad722ee02a81e4433611e5cb3c62f6e93d8c9eb49e354cf424e6660a998a4f24bc8933448ccfccbe658d863333af5b1596c300bda5e3f856c8f8b0f2dcd38e942eb28554e9410b462282a2042fed406b07143877cf7cbb90045ce2493c70296fb31eb26cec0ccf0826f416ce28b4b1150adeeb2807b8b0c0eeee59566f0dad5b7f3bece58da2cbf70d5e67400209600fe888e5158f6d0251a2810dc02663f9599d968084163e35a83f56eb5f46a2d691885799dab8b1055e31c741104466929a60562ce4f5b1b6174c496f70535a85683f582f262b0c0a12afd5b17c78d9f14ea6cc2cb2f656ec63c665c4ade11acfdf4b14ea597fc9f19db89424149aeb3de32bbd1af0c1089467e4eca6ab1f8182f925e4939e7622dd3f74413121c6676bdfba26a6b77d51a9981305204ad874f770291cf6dd8a9ea58d258e7e95bb89b925346ab811bd0489ab449e4ddc818110d8c2c7d0875634adace13d65385459848b60de0bd238758b2be59d2ae360486d74cdfa24fe82e77844dc8dbafb59c05b4dab4603c69f3242582abe3b9b544839aa7b990c44bc89bb74b9f7290ace1455c0869c1418bd7eb7331aca21764fdf7ef2b201577dfa5ada09a7f05e9ac147713a47db11dd2dc147e7625d4d94387ee0a3889bc084e02c1edd82bf990e6d7c407867d0223566839ad781218d98c07570fd3f5b50cd024a47f19fae46ff7f316f9f609edebc5ef297375f5c04a58e0e4c352df2571180114525ef6913405bc5acb6e90a273c1d0be6219c227448e9f102a216b8986e86def983247aa9fc2310f8454043a3dfba55a30181237078a6345aac29cdb35634761ecca3c094e47a67344ab7346b5f6e0de992a064871495be5dd23c56d081e433804c160f800d0d71f92e143efc1c14b113c663a447358f7502efa803dec61bf7bc59aeceede227b4b9964780a240a360a40bc1148a7c3a2dc88bb5073895b61e08a8d5b87c694c3686d59bd345047061d9a0a443575682a10d5d4a171188ae32ea60a84973a40384e87d2224078a916e195a68e701ce63accd5748ce952c46ad35aa21487d2d28691b33b47cbf942b1cc5e1dbd17cf5092eb7cff998a45637543c343d114aa8342a1b447d5e03455d121645012554bb9ac252f9735b2661299349367c2b9e4c60be3a4e0c6f99281b831c3a1222028afc7d46c3aa93d0477cfe47561a7607a2bf05a304bd833390c6de1305632b06164bb455ad212bbd786ca90cb4e8f25f2ca89cc8222b7ee455b380c5cd22e6cfaf7ca30949ab2de0e3c94d794c3744cb4f476e0256ac474efbd36ccac1be62246bc848df888d6122e2b8dc35c9dc7c046f20a97b594a9703ba69bb35069566a106acadb8187cae56cf17818ca47c7c622b9b0e7d1048eecb512f07bf8bc2a44cd97056cfade4d45cd176d7f5e09225153e6d3211b9f0fdd007da1051e2b19f0c50e37e7bdf76a1c39301cf7fffe0d7168ef5bd34a946c30f45028efc63f5738b7e61d6e1c47edfdc842deb477f2c8c89a0fb3f4bdbdda109bbea805514b9feea02ecfd4419d6ccde4e100a122d46cf17a4c5067085eceec18d96a4de341459695456edc74aca54e8f0e3776b8911b51d7dafb093d1b4f0a0f099e11367d0f8a5cd69297048f14637b58e43072e7e1100e9fff3e87cf7f252187aa23ff4fc8d96cfa1eca2472e38544163d143752120eecb3f7b08ab6ad558b6fabadd66a1c39b08d71bebd16e2a09bc342bab30c047277d171d8f72deed85a28a26057d1ea6c524ae9b41d71c77eec63e8763103876c8ac72d76505c4aebf8bee445ce86bb41edfbd90615d2b058c89e8de7838aac1859f44c30a8608b1dfc90b32f1d433d532d757acc11fd254c4e783450d474501ecab1a7a4742978e6fa148c960e834b77a1d93381a12604d5428d87e643e930da0fee42ffca1f0dc0d8d99ea41639d8a287d24cb3258c8cb7a8993493c6e3be77a2a4ab6bd0d8de41cdd076541d71dbb33d5361d3177d366172e92e5acc61b7fd4c7b3ca8c88291ef8b9e69d3fb13d3e099fe06705fed9dae90454f8565b2b0a809124ab235e5a13edd84e8a13c5496a172d969a283ca3751883575d54cdbb80e777c7ab42953ca2308370320b2605cefbdb73bd2a2d9a6759d1d6d928c5fb4636765ab18ed9884552dc574ab0e0c56b7ca1debab3542483c9257edca982e3809bb0b5d36756dead22a360836e5eeeed796a5ffbdeabd6a6a2cc3b2ec17293334c65d7db15b05a08af162640ca01bab18b1cd584b98acc24b980b9b6eb4a48dc3541b04dbd4025819581a8b035b33d6eec55a1d938e49acc2abebb91b6215150dc124beb0ab6212ab2ac6c140a8888e241e8255be5a4901ec66157ea8c2e0821107a0a17118cb844dd92798a0e9ca2ec8135eeaaaf05aea9888a248a7eab660c28b286820670ba5a2074436ca9e54f480b539ef6c929c7d3a9b2477473a3068c94322d959a99ba21b31179814752e2fb6b465d789616d6c31c88e7498aa71982ef465a610ca8098bc6bad158fb57224d9e94853f6bd5aad6c642fe411ac9e7785c8f5bb557eb0e46b28862e1e3424986e983938aefec0fd0a04b9670ed7853178e66ab122d9367415c9f6f0bf967af8fed34313f57bf0dee4318fc9dff0dea4c68def3f3ae45a44a38e4458dc808a4a6b2704d5f1a99662ea883e0874aa220ea98c886f44169c3d0366e12478855935a7acf1aa0559c4a5178a795f220876adb8cc4f1fe75052cd1a539989711678c4515052d598092aea56b68bdb111983d888d8b18b93ae04dba235e96a771f846736f7bfdbeea8d938fcc5eb7a11afec73dd45a37673df7bd29d9c803a3bce174fc134e099ca5d7f693fc39d05eeb50b2c68c1840196c174185b83b2811dbb7bc50451b79a1a742af8d4f1619631c79801dc138b33a070a1f0684ea5abe1b7697c08e15508cfc4cc96e9897845850fa0a873ac73c435dede30741d395f3cc82ce1231dd9611a6ad8d924f96eb123bbd5c4a6aed5b1bab103d2e18c1d104ac2a14117ca5ac2a7b2c4ca10f1c962d6aea80cddca5a6bf185e29e325d4116bb554949d907f1338652e183f18b2b745898242c0b73e530b6c65da816268bd39951b18e030000f04cf700a8b5d62e65cb3a3262cbea6ec7cff6162d4eb6577bc458d261b90ae0baae7a81c8d8df1ace9002328b1dd83cc488f884676cf0c87a568fceea4805798467b624d94e200452a1227ae1d55f21bf3cf27553b09a2f748427504578e6da465d173a17532fc65747765c58dcb5ba23b3657498aabb32a60b6b775f74665c2e2e7806c3a76efb1f6a3ca6ae9ea35e335f7c35830d3fe4ba9a2ddd65438c77ab96bbfb751da0d65a6b0dadedb8c0d9d4616c90696bb0e97376ebb22f9c7bc1def3e2cb3ee51a9c6200eafc909d8c4dfffa904321f02f5047004d923d5bb4b9fbcb5ef7baaedb5d182749a6b8bbaed06eb5d65a6d92bced1a8258feef453c4609d47855c7f17eedbefb940e8a7dc36f4f4c63771f433905745d38a9e8810ff60db93d310e2e43a38ebafb3174dd0df7abb621c667c053c8d89eb1d57a752f5dee422bc56cb9170c67c70e2a86766e7627d5b1016573c4fe9d3aa74db33b35780482712849cbb306a6c1b5cd9a1c4c87613486625f03078f54d4c140e8c62f76e4326c4706d851c33855632078c4dc1c6580b06b98a42e460b7325c4433b8edd09c7d46d65539d165616089b7eddaed922eabc19c68e8b8eaca3c3d85447d6921d3b2ebc64ab488dd5063c051e3b2e3ad2a61c0693f864abf0127e0199dab4ebec17f2f6b7ebba8ea9eb3afb57885bf8c80b3479da625ff8c82cc55c2a1f262e038fb1e96fa1b8636f7888ea8c2ce2152633eff3cf557d7fff1c7e610d7c723f651baf9264f7d5ae13a8bfe9d0f573ef404962376efa572d75739c2106b7e86ac5eac66eb5e955a757d7bd974c3743adb542a05b754766091f5975ab954c8d4004f04cb675ab95d57c79c1bef3a34e98c6df7a217b3ebf884fac5ad23133d83093147145cd49e6765fbb6ebb7fb50d3154b437dd23ffbe7b6e710416043a77b5a3667b0d35edd9b62716467c71cd7436095e5dbbfe0d7998ab6db50d55cf604616f1eada138b2478ecbb278945b9bb80c128238bf8844fb5b4e788fe1534416af67c022a06af367803464149d59678855778e5d57a66c782a9271182a951458cd71ab786c3dcd04103436adc70985af51c52358765ee19f12d6c1a335de6ead66c7462b6808af0114d8c0e524c171010c62952298a6b56a3860e35549c37bbd041033a0c71971355d580974eba381952020ff84917431cc604934339828ab617e7eafa0e6041a4982510e80939885f4153a90cc587ceb47e0e87bf96699ae68f82b237403962a31ce119f14a14100a34448fe99284ab745695c5b8cb07467bd2c5892aa6963ad085973e6075966559e619f1d10918353c235ea9ed67aa9fc96174f04acfc86702a18630e2a5932df8885547d461320dea315db650534ba0188092d8f4452e002930398826abd5b1232e10816fd1514e29982f50d816e644459d888c654d267795116e4e0230afd001cf8857aa302d75a24aa19cee1641a917649af2b17b0bbbcd7d2d51504d313af03ba302926dab664f5855d3ea1650946ab69eb428494c619d2039494249dea6efa4932e4e5426209ad9f282ec6f1de5082aa28f8202adec8bf5354805cab9ae212415d13ba4bd96678b13c74a31c915ec2b4c16458389d3aa3fbd96d76ab5b22c03a1ac94742718072ab3f2dc8a8a6725d4e1322b187b4e25e4f11f3e4ba863e56f38b787e519c3f3a1c713860f41032fc8ace810c602cd293a01d11f5b304366caac3ccbb384c2d08dd0b33c8bd6c1f2a167f9ff7f161696677916938da5eb58aeef7b1690e563c282338b92d275dd779ad65191d65151d77d3ea3a4ee3f5fb33f8f43116cf8339ef974efc1339df67c6dbabbd7bc7cca4e1d257db4e8bbd32c210c8fc3ebb52e5af3f73d4b48f17d968e1772190f148a9ed649287ac8087d2b211595503865543c9d2d145209854260680b5d2195502804aaa88454b4add96751a91f0a855e84c16ad64449da5b5d012ab3f2945309ff59429595d065ea123bdbecac44d6c1a69f7b50d274919879b0e9e7541dd1bfc93ee056ba150aa2248c85b17090b57bef65f90ffd4a486554c2d08a4ae8213daa3ad21e151df2cf34754459b447a5438edaf944534416bd932c28e9f3f413a2c4ea72d3afda8b5648d61ec330ec04878ae8a5bdd667a2221e5474a2a2cf6af13ba2eaca041f6609971f6e19f10db1bf20365cd692d72a6b2b5b8bf25a2dafb5591f5444abb63654e4c3a26c28c92d8b63d591b636d647fe367deb83929c92f2a64cccd1dc3367f898a3b9fdb56f73ac5aca41e42158d908568f4ddf6a563ecd166c8b5b71f9e523a8ad2f5a949829adb86c596b6b28a492b5b597160db0fd5ba11014002afa0f773d07808ed338dd7532a896fefd35c6aa236bdf9f255e1685039551c1ee8583ecd7df300ba90a07806ee21a3a2f3a6b977c7e9650470f5108c215645842d137f85e8b92eaa6a10f691da15ff91d3202085d26534225149e321995ab45675d38c8e2ad1f70d6ce2d1da11bff2bff7ae5431fb6e64b8d5a82c14b4249e0bf08ca1ed243a23c24ca43a2c8d71ef23d248dcf0d957f95df21c385427b0a2d4d1d853ed4f6a4439fd07f18ba11fe7fa82d8d0e7d3ea43d651db94ce8a371a032a10f690f49030e2b229fbc4f28674e554778c6c42414c2f09ff06a95565db466cf27f47c28a4325a554bd66a95d52a8bb2a8fba1109311f27db0b045df25aff1cc47cfd952a24514d0ac845386253c811443f89001c3b9bf50041e8a9f1030e4504401cde6de1386c0bfe0b184df4af87d34b3a5e633e2a3c137834dff1b62caf09928e9ab818be87f3638f8228b9e87d7ab845366a57e271439cd83b376fdab093c08312d323d6913cf604dc8a2d7da273816f5f9d8f43fd3158296f2a98e5a1f0ffa9d28c97ecd119be6236286e34238f924ca974a58f35509a94c3ed592d7caf2a9554b99c86bdd4d8742af1262325ecb6b8542501dbd01e8c87f625765a1502fe2469baecdaeae343fdf00b4043a0c8cfb6793a6bc5490fd2d0f2ad24edcc801e170b8d131518deb9f8fce25be0f86a09c7bcc92679a20289c98d9129ee9e6baad34b712b1a9918fe79f4f5633cdc4fdd89463f1e0e3611712f9beb5d662bfd8f5343c93e71e109e67f2787826fc0943fe9fdf21c3cddcdd00fffb4febb061878c8737c0ff681d31da4618197b718ede8b9374ef472ec19c93575486b2e4e97ba9939b4def75851f9840360e7ffb562441b3a71548902e7b5a8144b93d54ad9de35a1fafaeebca817dc28fc342bcc23954843d768d0b00216094853983b44bff6cb1578b9e14b5c3b0ebca816d4f2bf1f9188adfab5fdabd404ab02f866118f63d9cfc374bafc76cf190700f08240ff0c64ebe07f0e42f6fb49fc3c97fef38e41a879207ff46c9838fe91b27ffed886263155f3840a8e80aafcb3e16e61004b3c5c304b2ef9f3061ca34715f9cacd0f3a03c1bec9a32dff684737fb0f67e64216b9863180ac33a94da7befa5b9bcee9fef3a48e9f7e95cfa88fea56ef293186ef293d354fb389cdb76a16f7b3d2dbf95c6e62465ced72d15e2790b9b81b16ccf8e0ca6a84343459fa665ce719724b9f44b4994ecc86092a8908c965488d562765dabc1700568bd1ffeee314bdef496d8d44b29016ed5e428b7c1b090fdb9171d55efe4b8fbf6adb596e3b8afa07daba67d7ece7695a3d6524c43269fcbf00de0e65eabdc6721f71dccd35dbfd776cd7da1a88eae21f6329d78fca7afcfe17b1b396ce54145f42b14f6e6cff7e1d3c8742ea9285b9a8d784989b6f9e2402f29e9eaec3a5f37a9d2e3466ef44cd9be5f37ecd850d1e7bf1735d3a59babd9a6594549ae692ffefec25c47f53f21878d1e7adec88ddc12ec67f6ca301d8ac07dec6bb88fe9cc86da67a196596d272ef10ae768a6d2ed35519140909f0f8d4ff68d14c7d431754c95867a86556c7418d7ab8fb65650e2d2546660f6612e4d5a6c6a2261a4fa556b13d0fed27995737279d969fd5af032715a70383f6cfad7e6fbb6e52e545469caec4977b1f7de0bbe0812d5a02d877130f496b3eac87587c65dae53be2ffae7b2567d5db6e599c68ec9470f1b0f0977a19fba3cd46ca9917df697f64e54a42d71d2b41abf170cb3123a4e6b33ad87099ba199c0cc6674a44aa038d762949c4f474aa2a51226899a3145389444c95aaa540c8c9239ee028452126e5191199384cd98a2acc76c416133b42334247ca4193145daa96a298d079b3ea64d5acd6c41714a80af69277a028d414ddc886dfa9e0f4aaa0ffa5cb990450f65935594743551ffde7bafbff7baae9b876c215f2f6632935444ad38e2c7bedebe83ef7908155d6fc3aca22214a775e1dc6038656ea66d9b86d2be2d6aa8eda14c18be01dcd773f7eda5d9cdb2afa1bec5ee357369d5a643ae5f6e7be7a14c9ae973b65a046dfb6a6f773a35445c55c8628726d750d2bc430c6184113d7a2cb184134edcd09b2d7650a187a2a1a24e4d47bf777c5c218b1d94cb80d5071956692a13f506325acd7c9958662f1be6c8df5595a63271031f5c15ccb0cff7c9becfe3128b818de0b27234b3dfe7d6d07d516f97b11a975494d3e181cbce16b17f0cbd90c86207a5c4f77932f909290d2523776c3e7b260a7a351fea7bf077b0d6bfebe868e2c6e7f33abeff681d9f3f791d256fb2af75eb6eb5a8e30b33bf30eda78c0ef03f1de27ecae8f83ec4d5ec40693b6e622e1df60edb3f94691cbc043bb349b6656d8f7f08eab63a040fc8f724cce3904ddf24c4c936f3658989e30409854a8a925be5201c1654a266eaa0c025e688be1338509052a8ea156510158785156c11386883856c75ada94478a197f26e3e25be3fddd51d9b8e0f32ea0fb49a6818253631af1d4bcb306889999c54541d62b6549a9a5d7bcc18d424b0d8f45e8b247b1ac869a828ab3414ab3416bbae4a335f68ed6cb1d2604566c9ebe1a1681946f62dd2b29c2f78dfbd6998d54cdfef90c100f8dfdff89480ff691d5a33e9a9bf1a8d884dbf2221dfaf3f710d1a8dd54207081dd14e8e5712510df1c84e161e4edeb487f2ab95005f2382921c556feac873ed50316170cb5d68270b4ab262d3ef8c94247aa60d64bed84e6756bf32cfe425819b625a31494ac09c904a73e512538ce9856929307580e8984c1d203aa6cc8459b3a5c86ca1467c44c931a60bfde954a02d9c8530b29ddb62597d8acbf942b1d542163dd4385f2ce71119b26330c5f64c5978403695c2c3f1020dc05ec36e9a4a2824800288aa181de0a5e92ad5254fc8f922c3a66f8364d22d13b25fd6aeec13668b55cd965b837dd9256016c3306bed4d1e5700d0d55a6bad71187b645a28f8a8b457d078067bed61dd5cd8fd256be9dad890e4b5b1d65a8b5dbf6357bf9fe199abed0d15fdd0228b9607dfbe3d98a41fcc1b840cba45b8301a2eb95114fba0a43b5597bce425a70bca35d5ab727b75ecaa41298dd640860e2a176243079a6273536c1c0604eed202c1c9612e4d7b4c97e93160d1242e2cc50729363705e52eb5e435293ef092e300c5aa275a460a4ae530f7a6a0ae6a660104b51485398549d22e8ec6d2a0b0298d693bed24685798a5142fac3049da2b6196a698242ba6363a4ca6755079516bbda8656142810a2a1aab5274a6097142761db241025acd3a77ce0ac94ee99c734e7a3b3ef3bd19156ddf736e3d2f7adf79f1fa8ef55c37607fb1f6efb985163e96506e1a7b6eb184d4d682c72ec09e5b28a14564771bf4f7573b8a2dda730b9288bd0599c5e6f6f57e03620ffabdd80ddbbebac3357c7e6eecaf1b4ac2eed5a1eee7e67ec333dd7393f3ba0bc3ec6398fd1c1487973b187659aca371781e53a0f3d77e0eba3d747b6fadd7c9d9763c6b27e0798fe3bece31ae5b74ededd081827b893d6cdf0e648b39a817e2c88f82ed21f8dcc5debbef69d1a3d8620e1a82ba45dfa0f6fe7a3007cd41b789a745baf39b68916ecfcdc6fee4b5b8b3f7e2f5a017fd419ece915fbc3c7e73feebed7b5c8b11b8ef14b8efefd8fb0f87f1c7bc132db2bdfe138abe3fdfe11a40ef1a87bff71ce87a2ba23ce88a28d875e91c75aaeee3c0f4f51aa640b7efd5e208b2d5e63c98dd399cdbe3f1bce7da3902dcf6a827fc61b5a7a8c8b57d30ecfc4958f226217e9530e5636a0cfd84737bf00cf6abc51cf545cf83180e8fce51454fe7a8dbf3d84f7cc385e990f7e0dbf768d1f7130e83bd15a76a83fa448b7ce91c55e7a83bbffdcea7846f1f87938af097841715612d823a4715a7ea7a1c597bbe620a74fbbab438821d1b7b4fa7d3e1b0ed85d36e4f3481dff06edf1d1c016e63579c4436c937f7b396eeeb4049fef7be3fe130d96fb606848e3e6d9cfb394b24e7a73413fb899f7bbfa1a2ee453c83bf85669392ecfbe5fa3ea6a7bb605828fa8fedee29ff21aa4db52c52426daca31d5487e879f727dcc5b1ce45c17eabe15091964514b238c9edb1315fa67b1388154c1b7b9ff7ca2f66b9dbee144523826b8b94c642218b9486e5557ee5298a92dc0b2f722a102339dbb7a88d22a5d9933ce20a9b3e2582920ad0c1a1a409a40a596843b42c2a70c6888387211c3cef7a776bed7bc24ee72d9ef1e4f015d871338431a21046119060fa2193a2776c0f316fc7c642ff91a3eeacfd0977b11f533d217e1b76df09c5bab1c6ba893bcde91d7b7b8a774c3107dd98d6f9634ccf4def75fdb14fe0ac36ce179c83242727e5665394941ea4d8d4dc956b85695568e3aaa555e1257ac6f593c3a4d8b46a89d29ce1255a049762e3305ee32eab3aa24578a98a916283851a395ba4100367534d4b828fe86b4236fd6bd3466c8b9a8e7d310dd74f2aca72645665031b6b85b866d6ea2de9688bbb2eec72d9c5211c42aff237587ea5a32d1c6c5de980ad6801b4da292b2a194bd542740814cd8bf4e219eb846cb5918a68d6341c2eb27d511b67beaf82612a18c37cac236d74fada1524255d5586be86335f3051cb116b1222558b5553c2be1cc514e0a1eeeb79c076f658585915496d51519549f9187a43ed2645d7550caeab011d780001092ecc49e0257f818f4e97c35cda97982ea85d0330bc073c363522f49a6a359c5a4ad1c81a214d4d7594425b76db48d1705a37456b631d35215f9d924a61828ab27b536ceea7a4a428e97a9a65634a8a8a9848b1b98fddcf6e8a4d4aead64ae44716536c5298a0243105b5698a0d25a588759592b335b2c71635953662984df1418a8d3602a1e37c216dfa764816b5df686a89e5e96fa65a0a6d359bbea8596153cd8badf4a7af8db5845b9b6661d37c854d7f6a11a3c2a634da4869b491d2d495cad7d5beda98832476b6e71646e0c21635cc9ff0940e2070984bd7204cb40b212de3345b684bc8f8a11b3f8bf810a408232819161dbadac6cacacab38472781653890c4b05829461d1221a2048990c9459014af910a5186dd50b7b4a534b95c6874dbfaeb4b18636aae826446dd4c62cbba19a12ce3a1269a48c206a011437032082e4c4ea45438af1a2e1801a5602ee924316343005d070808f6a708e69c0992daa7bc56ca1cf69f5aa62b47197400ab1e15edddd3bbdaf17b7a4225751d2a5ddc69970225e80373eb231459d8fa124861250c77987b1cc838e58fe8ea3a251043ee3aa2a644cd759ce5cd13736e74b0d735f2f62b58bab5eb58bd52d4f9bde23f9d2ab2cfab869580bbda43210a1221bdac7d04bc361962f2b64d1470e939aa65d658dd49d826117a796480ed88e33abd8aee7c8093026607bede41a97be46402268effb726743108a2b04716ab045b2fdab03a198a50dccf25ef7aea80c3bcc96329798749b6b0836aeb22166bd24728bcc92365faecbba476e91d952d2f4a5828fe87fe1a93bc610365555217b49848a6c4e1631e945544754b7b4f7562cfbf2bd17cf5c9cf9e217a7e4e27c2e0ee892f709178b1b05524542314be00d13c490ff1cbb7062cf9148189be21adc8aed40f6c434d0ebe008461164963472bed8c74fd841668ba6f1163eca9c09d9634f72f65effd52ab3211625e0dcaa3da7efeae6f89e37e7ae8e6053ae3419bcc83bdcec3654c8e22d6d282986ed15bb484ce2ab9b259fa50d497a96e50c02711f433bbc69afa2320ca1a21862d8fef5fd96e58d61cf7caf0ccfd492cdbaae7a2f4cea68e246e7f3ebd0dea363fb8e0e613f65f253d6bdf76f79eff558c68a64d06561933442c8ae5abcad4dbf9c65921fec4b973745491725d5bf2555e194e856d22f14d511e843dd755dd795d2b92f61e3a38d2d926d91ec397869ba041cc6834c174f829025ec0bb4a1366516f19c13931393139317e7e25c1c8cacaa215d64f1e2e0cc171c64b65c314b780b2f0285209b92b3c54a21cf0da64026666541fe7d655912b6750cfe166fe9aa5bdef29698aa96647ce48f45b90514b6b354528c4030f0d2182c545642a190af8442211515151515156ee52b663b154d544f595e6109710fd22668673686665f09a90c5e59595959f9950fa180fed882192b61e83d9ee7aacf15968ab1aa6785e5c5bc27c8662a3f57f6842144b2264dd3342d1452191c0a854218ab965e04025d2e639508bd4a78ca6442cfa2a275a8fc4a1325d451401ee60d3fe02ceea35954f5ceb02887f190ee824bef0c2f61239a45794887e1f20be8b7b9c9b1e417702c8e0cce061ae459ab56bdae821f43336d874ab06b9530949f05db57c69fc7209048546fa8a83c16de08fd8ad6b1f2a1e75842add2254b18baf1cff2afb58a4587aa2eebb354bd7299d415b28e56c2f06b784fa753f68bd6ecd5d0f390a487bcbfe2d1aafa1cab8ae03209bdd00a4bb5998a08a492f14cc9db9086b64ac977429187ebbde43d5c8665cc65b5667b3a9dae3dd5802533d7c75ee072e617e4d31c65cdf3c22c859e629bbe9764bef87df113420501a54939226ec7715ca73d099ec0fd4b3b185474bd786f3aafffbab82e7424acee4507c3c1e8f0a7742720141fc79aa2b31a15ac6eaec2ea56b22809d3998a625fb5786dd1559b0ba74cdd72664b1566cb9d0ec89fad0c42de50c28dc5993c5986b1bfa11227db8b8c782ffecd6ebff37c97982b28e35139fb79d9e31d6735be5bfc8d124e97a9a16755b8f7f2dceca6964c4c3630b6ebcd888b3624db17191190ef9390cae8cc1483725b35727276fa5eac014516b7dbdabf8141bbbfbf1971d2e51eff96e330f9e96f5ec06c2a77a1df79312bf94f79793e251d2dde92aa1c6623e29def6ebae740956a7b5a04ed70cb71970d0cba1971d146c461b60d09d6e2f6c57cd9bc982d1b914d7fbbc9260f7afb629633d5b545ce8412e63aaa56c8760862be2c276e474a93dabd711c8f6d93502414e03e747f8a9e0f5d1d82fb9dc6215ecfddb086b971e76f28fe8912ba50dc6163dd7d57d623ab31e8f6722bbdfb108d18686c6504b80fdd50fffed5383c5f29c0fd755739e196b3ad36249c898aea6f25156d606cad2d89fbcab32ccbf2c5b60085b98e4c08e0d775b5742726275a076e820c10988c896e628b2b1eda215cd856a4d4621e633bb2118161a093eb07af8263810dc28d23f1fc7d507812eaab9d8527e079af1f43a989922e3db3ce7335a4a6d093d417bdd5ba426c7742b79d11f8c6ef37ccdfc9970e93ccbdc3ae9ef0cacacb77081e8043df35cc816d4fa77d9be01aea7b4945db598e8496362474546496b6227344b772b6502ff5c6a2a2fc441635d3c4b24dc3aa56abee4e4efe04457be92b13bdc3919c7df7e25481666c983c48f3e00144964941fd020f8d92c0a00e00954e1ef4146735b504d2d94d8a9e75d4f1d4e4afa9322636d458e02ef4b5500b62b6841a117444a3094145da1174a4fdc86e3426341fd7c53476571b1894e494747fc67d4545da16b7722baf6bcfc17efd66650e20709c4307defad85a7365727283f69dde8290dd643759145911e6cbbda9233abae7509197eec5a582c97f7b84a70d424609b7158b33713e703fb80b8f1b72274e8890cba1a4eb20d0956926ed40a808878a7c741c21dceb3fb6b2da3c877370d4c95b1f19b445476de5566eab3aaa2f6ee50dda7bcdca5abaf76665ab8c00c518b6c3fd98aaaf87dc4b136dc38b8a3a031460a3712f108a8989c98b30a8dce5ca54e4bd37f39af3de2871f226e1299339790e14ba0c4a18ba61f2a007691d286fa26d307913ad23e48fa2359a1a6da316c9d9df037426db17362f6cfa5b1937b5c49dea88fbc1451c4d367240dc2064df226702cdd8085d6ff220ad9dea28a59c2f0ad8f4b72accd2c996335ffc41349b75daa569db1737249bbeb885b14ff4566e5e6ea547eba1a5b0b0b5adc481ca9868cd5444deb623b3aca56bbe3888f32197916d636c2fb7725be504b1adea864416b7722bf175351dbaef39b09de9d02a74b5e76ce5c6dacaad4479b1c5acc89132ca2dae5aaf03f80d255dfab63220aebe49b228ee19dabef7deeb82850d11dbb7b8953e6e31abd93e7f6c485c5cf004664eab96b2ecb6b2f2de7b6fcacbadcca9fc632bb7cf91871a6adb34ed8692321d806ba9c800344263093d41c9e535d66d30b20ad0268a236c764b8400335ba9f624fc7a0d25f90d15791293e442a688929ca4221a85bbf8116ee3372a1732491e8592d36cf97c490c4a82981f0db6acf68f2ca9c14796d0940c315be84783200b2ca129bf0fc9d76a42fb52fee4f3264f5b775afb1f122afa5af7fed76a228b1fcb59df97b3a232c020c3b03ffaf3263a8403ca7f3ee4fa06ca7f4237406ff2a03779244e3ca1892a7a7005c992a117e42d14bf39464a900487a844174192e6cca22590f5b5742f8661573b13998325764b073f2ebc043ee1a39493ee92029ff0d2b78592ebc02c80ac92c8102aa21f39a7d6f9762d3eae5609b97385dc7333675998ed92706ecd8e5fcd94071a584908b2c016158160dcc7cfcde9612f8225d696f038d5521d4116d802935052cdd9f44b88a05f7204ca8689cb81509297592cb2e836572b61f21acca122130d92ef78062412450a14c1d4cea1a4dfb6d485313166637f351df2f71bf0c7fb87d973eed666375a08661afc31451641d6cd275f1f647d6800afb0f360c7d362cefafbbe1aa98ea63faec8e247c6d493f013e2b0dc427208255d95ff762436fd0f8c7b35e62c1c56191bd357fb454db3e5db028bfae064388bac59f61c0696e0116eacc8a29f36abd2204c2c4cd730b70882648066ccd29c20cb1dcc02c88549aaf72f2d969c2ec8c29ee9615d1747ebc78b920b328513c458c6c5057951642d8125175e028d7ce56c09325b1ce6eaafc874f9547b0bd4e7058975f42359796e114c65fb17cf5c2ab2bfa5222fa34563d71769ececb33f09bf8eb2370939aabddf7addebba3476c3fabe27be21ebec121a4a4a51519641daf5a5b3c71e648148c016902cee60cd9718760964b1e6cb75a22c1fc016cbe44f7e15b371f88b733c094dc2cb08396ff123377dae6239eccec016c8d22092542d8163549005a6ae2b33b95e625e3a8c9324e930f75ef2aaae0b035f045319a601d39eb2b1a9a58ff57d2c166bdf90ab738cd997c6beead0fd2c87dd602b74358804644d619390c58fb5ed89b1bf61b659d763a16317c3300c732452647cc35d69dbf10df65d67a0b3270641900439b003484e121c424dd966b140ec042441236009920ee3a5bbf80d68c44b7e8412248fe0a58f0b1f7d641d9536527c5cdc7c642d8140b8c029dc0674e286e008a666cbdd63163f560dceb247a825102ce36bf960da25418020d64a827d3e252c943ff91e409ff22298fadc4879d0f770f228df43ca835e0453183da19d100e287ff23750fee45ddf007dca5760caf05001ca9fbc0a409ff23b363773e824d4117a95078528e1c9c7a2a2266ea07c8ad6e165c4000b2ca09141d1a03aa29a1241434d1609f96b7d48a8c81e218b602a01623461027d3e07137cc315815bff1d06b822510c3468609183200cb0356adcc7f6d533c48c5b025444673ed6577e486ed898794f320b2d6cd1592617c82a39cd9709b22ec80259200b64812c90658d90e9768d6fc8bb3e98aaa5af8ccc8754109b3e98fa581f6b5f118bc5b22661beaa2d19417a0116662b56a7654276b29295ac97acb5de7babfd715fb4a96d5354446db5aa2a46075491766b0e20ba350e73c35d2cb7dd9a036c2edc838dd58e0e2372db45175eb26758552dd9160685ada5cb035111ec5d62b6dccc166aa7b8364bc8fee2b529b930ebd53ee1a3540098782200769c2db766c433b72645649fb02951ea0991037c34d62c672a368a794b920997a6402cb5d68a6bf05aaf2b7c22756dc69b54c5ec55dd213ca8c1aed6ad196dadd1305b6bad358a204e73e454648b2cdad6b66d9d26029b20b3649fb0e92d728fdc80091bdc93d442052a6d555a655b9d4a5fd43de2d67401bb6e50d1820ea3d709d585c2aeae1456756deabd31a9e0e2f2300cc332b6b55a2664ecabdf20b908f9aaee90af6fb5c83fc8d8576b671536f67e5dd885596bad7da26af689f1864d0919b2f348b3ccb65ab6657f90b3bf7ece6bc359cc5acb4d0ed459cc5a6b5b1a5412d26db5c862ceaea652d94cd5b5d11e84676e083cbbda2e9831bf3053306dab655b34bb30bba96a69527f1aa770a8740dd437492d1591080000000083140000200c080644c231b14c0e64d17614000d749652765ca04aa42088619442062143100100022222022333b40db3c70c77ff15c16d8f6755edc0f579556dc566a0391b14e1f9a33f818d2eae706ce06ebd407fa4066787621427bf64443e65f6e2a55fab6c8ddbddee90bfafb9e7827f1705f54341a2582f449ebc64ec73ce9190cba6c8f4948fc3fd1dda8e4d1930a8da1ac9e843408a6ae98b1c44878a492316ed520c3c6aacbea43c8233f09607ca87c2358633f16e924b0f3ca22fa6e0a240557f6c196000e7a1dbb08e4eee5dc533378936a346ef7e9de78f07dc166772f0561a7e4663b75156c9aa20e170c61d1ffe576690a59e020de297a1c7197f9401d8d488bcce9433086a18039b63208a1bc4702c22d0f9a0b859a80a02ee2822ec1ce6e66c54db457a19a53f3674267f54363dc5630550800be8d97a6dcba94d57b588decc1f17fc2bd087ef9dd6f439b6f744b11139aa2e68adf562ce2659470a9583ac26a62de516c7109318702ae22297d601152ca1c6658e5b429f97f83fecdb009c77c1c23a14a96d1e63622c1aa900d07e777835c8226532b8c2c427621408201d88fef4a0ded7254ec088bf9bf05a3db91de7e9ed9b56450da794833c60abee373023bb363bdd19d77bdfadccb8226b94ca4cffd9edef2ac62e53cab3cd7a44a5850de9d74a3feae7709987a1607966fe48bb2bf2790358bd059824aaae7c441ecad3f44e3ce78e7d8ebd68e46e680ea7ee7d4722f1393ae4f6f92d983b0e80dd4804923d3e40f77b1538f29fb98b207a2d2d4663bb11d678e4e2924dafed284255219a51f2197c32b4e0f113560414b6edf3d55c9585ea93c997deca9628a545a8cbc939665da34697f6f8d44ed7a13b2b28851c94ad3648826cc122de90acc3df4f1df4d90045a5be24e3484a5055a94541bc34e0709aa740cf48125c0887d6753798102878286d040071a53a643d93036543683608c639fb89cc61e518fce407ac8448b3a16487419a4a03d02ea6a9206b2be88f73ca6f84485dc59a44a6d273e74ae78668034bb5faa53152bbbbeaa92636ae572213fac690eb6d4d20430f0a3210329d0190764451eec6bbfec5bd29d31162746f252651c12b9557a971d429ec3fe7cfa2c6c26dacb1a60538e09ac10670ecab1e7579e2f88f1ff4d46b336d17f1111d80897587a88076f948e0d60ded562c15b644022a8cd6b2e5ca85953001a3cfe6bebbc5a50202116aa450c3aa4df013d33d1c0a11e09772bb0970f98d88897cccb476829010a6384da62ef72d0c7f37c6262374b8173e8ea93bac3cce4ef3833d4e9557251e77cb9830b6e350cec00675ed9810490106efcf173a808e3384375d84dc5836a0bb4f330399b2445fe959ab626bf5e61f1290538e2749bb490a80499477a38401b728d62c6fd4978e94464d0d853edaa3f380cc12d070618642477cd926ddb6781a155c83e4755944cffea1896f98705d4c393ca92e5b37f7b6f0418985078087365bf6815747d65289df3ca032bab7594cccea4b07f0ad0c6e1bcbc12f396f63688a25e242978c749904a6cf654523a20ff16400b6b03b2514209748b3f8b4b4fcd9e1559145ae1e6e4ac617ad3490c7e104e17fdd69c0cf6b0e625a78042230137e323dcc50c54913653ba7e6c0cb45868982001705c0d20ade28f5a6fd5ae9c9279931ac6ab40e2aeb947d5687120f2bb3d12e9c6f7cb486f5277111060ba4a73b71c0bab49cfd6003ef1f9c12bd7c61ad8f3db7d0403eedf037d181801f4153e0ffcc88d50e92ba3a320de2e757e7af776496c393cea099ef46ed02ca23f5d4910fa3883af42ac6dd6d98e51aea83bf4a90849beaaa65c4aa1bd5ec16fcf0ca9e2cbe3f79f64cade5e41e3b0feb7c6360c2daba77a72adb5a1ec184116c49a4aa183d470d83ebfa2e0ba27510e9f90590f3e3b689aa09f8339d0ca60d26c4a1f2489b0812eb077e825efd8c7a30886c2a301f50148e2ef79e9025295ab781b08a556c04f734f704dd6e23fa3faa14597e1e65a73d8358ac90589ae816dc32d8c06ef5e4e84e8a6a01b7ad83cc2da15fb88d71fe77c0273b90d87799091a1a39494b1ae1c99163884d87542c0edcd757cc2a98c0fa0091e8d4ccfe0ead1fdac6ad027b75db5dbabdf5bbf2a5d7abfabe265ddb6302d17074445a663f48c2e7de76a11292705a6d54803f51fbdf0b00361adc2eaf6ed9cf70c5e95ef4271480d8f38baf76c2cab62dfa918931c7fd3ec8c41d67a9728097183a9d6e8060befbe42805ae4c85ea147337671d22908d5eb2e48137c0d8f4194e5355116363065b2e688866dd42101f80410267baaf98114898739d436e168731fd8258bfe836a057240bdded33ce336897f14946147a2dbddfdf93f7f5ed2d06d44e33fab0154a7af53c2f84f5e25b8ef542b57eddc9f7a70f8a02152501efc99c927dd91aeb8f86d83ee9177db16dd3b30c2808a17aea497772d532dcf0dec9119bb5fcca3ea37739c7400644489f0dfea50cac416d854ec52e88161bfbbb75896025a728cb985c81f33828db0166f1e7fc4dc72d3e14b27cfb826ec46c32814dd76fa8e0627f4bb5acb5beaa14498e85f339568b6504397c1745cf46979c129d6ee1a42d5fde4d8c807365f8f90097ffd254ae167e2dc655f9f46127d97be4d3f43aa86e25182b45d600f1c3d159833ddd5cc1089725945b4e4f58aaaf5e8dbf1e14fb87043b76a74b6471c9730b65c80fd1f0dbb84b7aac9d6dad20fb998539c111345c5a07193165405df60144673ea21920689cd6e9b714d998404880bf743df3dd802b04db18ec9f6245f518b696402030cae8338ad325268224a5b25f54605c31130757ae8599224dc913290f891cb51cb855c04e09f52c2f8eea0e4740a1f35d77e55fd1f318edaeb05795ee03ee653d178716b8dbafac2965c2dea433b1eaca1eaf6eb1a04156ad9c3516ce5b17101c1e218b22616ef0cdafd7359fb0c2e07ea2c40013ac27fd2a1fbb96b1b041b708af2683af634965657adfe3d44af3b4a2879bf05f36f298c850c1aebc6d62729e78aa6bcb3a4ace1419a5f2fd861e420f5575ecff26b530c6661d9b814634160e60529e5c2a01ce37ccf3acc214185415c4a9a3e13281d02733a19cd0d67deebf8161fb0c77073744e1510a44ff12812eeb4310e945a07a8d096369a5b696a9d64534256a7923552b4f606a067da8629ce6d54e0e395b14aab8f192f4b429bbaf5ecd052e58f5e9241bd416844b9a1fa70213bd44fbc594348788061cc95349f3b20b4728429e9f145cd2393cf2574215bde9cab94e925f1ad8ece707b6879bf8b9559581d8e7f4a11c6f1907ce194eb43afcc870389cf44310d3139d5ae92e0fe17aa2f46222df3e956ce5200433fc5a2cd3831c0be5010a33aab79b47386da3dadf61e33721d0275597a4393b756a8a1aa762e12e2c1453c61ae79a71b8221160def5dedfe335bbbaf91df1b590e3441476adeefc14c46827669b89430331bf959b8db45968cf6203d6dfa31b98839e8fe06dd0e148d174c2a56df0229b96271c22863c36d058639c83da24f135a9d18a2b194c2cdae03e008e086b96f0e0ec936055503a2c35a6ef45a469ededd6d10e08fe2f78316b1cf3ca84a708bc6c469440befdd576d1e2e32f6d7b57e4fb46b7ccecb73c121c49c9e35db9cd6d9eacd68d10a66e3b98a35693d349ff68c01f06f435e17bd8df0c8290264b59f53ed06a06d0f24b00dc92f68a2af38ae033e5501c4d5310a850b7600138b2e83fbc6c739f08ef2a68dda783a5c52e9d65ab93e62314c3b881fbd82ec5d83aa1b90a2125995f4a49cf936d0491859dc9e9c2c9d5d69087b628d755504bc109b86eacbc229725a9adc810079072dda16a91ba3729b46be70eb4653e975d930a1b8a3873b164cf58343c167079f43921f2263baf3223d95ffcf9ca73fe2cf2dafe7a272d89cd66701236a3af3bec951229671504b0b59f2d13e658403f2121c49f30c756e4513822164e85d47ea168d3edc1c3455c08fd9fd2fca95726c7da3d5a583179dca9dfee93884a930a141983e22bdd513241587928379427c48a1296b86dced43f222eb1cd9d9af37802a44f021101a6d42779fd285d87f53828bd49f5cc968d58ebd6f068c94016f42a5012fee2eeb2738fe2e1d97efda28240392d35da14358bc0582da5c94d96c2d71d297222b1f89e802dd7e0d3295714409098db7ff8bcaddb94545067fb29e36f644eb9526efbeb65b18846b0dd83a8e11d549a548ee7383369a8080bf8e6b8ca9572cca8907fb833b7ba720cba6498d9055623816f7fee0096a5f8ea47e71d66659377f0db838f84c1ed5042536b9a845c9cb3950c52a268c48a1edca5d133159ef5e57a1909a64b77f1ebb87df6df3aff758ebbe0d0a9e214565475db3c0f7fbb10ccfa167fe1d8f90b6e7a030fc184de1a17e66065c0faf25aa4368adcefc222c6ff3eaae2456aa3bbabddfa1af08e3ef6d68f01a1ce43fab982413cf0b8cd673a5db78a899eb0db929882ccc5dde15f34a0b1f209134930fdee356753152af3db78837fa4d5b124c053be5a9a5a7229cc81a994e6353e8c70f4f18efe7f75447805b5d9b630aa6df71898c41e53708b53b257214d351814b095f1c61f4cbebd28d03209722bc4d398ae5daa18dbaa7610a38d534a9ee7bf5b637ddbe85795de48b080743b2472b202ff88931c9a2d5760f2535a2089eb4e2451c85f07478c4a9d119f5f0cf177e4fd86e1b49a4eb1801a0cfcfadcfc4a9494a2ed59475e27756ecf6ee356bb0b93db0a97d0035ef31f5b82567c9610552e7bfe4b2c02b7e8ae3535851409c29d32bf14cf950f9f63b89865440ae3d064b2f805c9ba9e13694f8692fec827a428c0a9f571278f24e112b8248a3ff99b82db8445c715799e20e04f04770dcff0df9b776be10c5b4dc7da72f1d44286034a177600bd73901572e48faebc02058595901a8070a6fa3ea2976eafd172e2c226067bd64fdb53b9d4b4b8279cec7087712b3af1d4826b746b3fc001793a188d97a5ae2b3bdf8d00d24e08078bb60bfcee4b49db1a07133c8df698d5978f48659165976a00ea741d03df1fbc9865580b8089edd143028f2562ab5e99aac01a20071e5446bb7a49228166cdcffe58624e81188b565fbb6227fffbe4e53505b2ff00e3567a861a7291df4a3baf1715ad09d5390c28c7d333b6d6dc42f40a2fe2d5099c2e5da29d701116ad28819441f633085a90ffe233bc359aeca2daa0ede538aaa558bb6fdbfba50bb88b7aadf8e01bb9ae3d4b307df9eaf1e2c9721113b92ed1bc4604abb0f4db2222910c0864fc0f7b741b29e439e224c408c553e062294a84916db8bbe770d2cb9c8ec20032461b6c17f5f0d52fd20b0afb59bc2c663ba559f5be8287f9de91be8f2cba6b06338f41a8ca05f74b4802e3064e904480801748e018ccc95a7dd9867a379f97343520fd66728d3d40146602847e5b43d7f7131039d7865357f0a58d9f69e594dd3353bb85a86d35413b532ea5f77ffe5a1daf570d6dea77f16a767341eaf24c39c50893a300d9e88f8a8245a7c395c87d4c4cd493d4a42ac92e5667743e70e934a237f2853f4df74b4cb6ba60df38d07e077e42c64b838b1345a3b1d78537d6e884343b4ecc9813c98efb6ac6c2199fde544846ce800a652adb22305abac174280d5de09178c37b6772f76f035c17e7e537cee12903a29a57f65dabd1c016cb0bd855ea7fef1f43cb50af77c0adb2ea565ac2d2fd7da465922c833aad75b627158672241b80cc291c8bfc4905970c3224b60b12e3472c60a51fd8fe100da31fd35b0010bd478556bff098108b40e2605df55cae88f48fca7be54ab07f5a899209e960fcbd92ed81fa7ae0821bca700ffa280c35c70255660b2e6755cc6c12ecae9d66ed731fe5bf50e946632d5b1100edffb5a11c08556dab3a2330421ae6a125dbd457d0ce4b5baef05bce4c470f84cda06da5820e30e9556c54ecf697a01a217085567244cc8888112db200c6618baa3b869aebc91a01db0a42af392328ac4ef58e9a8e818531daaea156a6f797971201b893da29a6b4973ef7d20f14f52e27b22e957fa788212e4d1800332b828fb88e0c461f825503090fe9668298258dcbb5644815910c47bc1d4e04053667d1f1e59c8e21de33ced88267f0d1b78134fa2fc3cc3a3d1610c62e702565d67019cc95749aaab36da5c7d8d289b4f51602e2585549ddf1db8ef448275e972b50d8178990592bf1d4198e40a7a015580e115728fbc40124454749d334fd6a6662b285d26b32e5b8886414659280f7c230f008c999d97620aff3c21919273bd23b9512ae551e6f6a549e0be5e4130344656bd25fff2030d5016b1ac423a320b9c6d66c8c0a96d8f715ce15d9482521b40f8164cc323eb6068f280674e33eeb8afe7fe449b58133e64776495182f98e840f90d07a7320224835b31608d5f0ca6ca0f38695706ec35d930fb2b8146c8c6c1b31f6e8f3d00d443e4ba4453a5b086adfafa17339e8b50d8ec2d04a618ce7796ab4c5ac11af025b746b47d49ff165f2b19b4a1a5ff06e464d03d615df144a4a7de0c869a02b0210e4b850436ca0a2064d87f1ab32d19919aebd65ee88f1cba25001fb6c9f5420bbc90a4b66710d2b894abf2e8e1e0492850ec1509f3acf1fa59e94edae650f1304f7f5e5fe24ba91a1d2388957faed04ef5d4284e9b5e9c94bcb643e541cf2a04ee76a61aca4d586119a5c6966212649e657a2fc9b4ccdedc80b33e004569673428db38933f51f37acf78ca33130ca50507cc5f392a875c73f49c81594aacd4a0eee6972f140c8c9f8c2ac516fe255ff565ce89259561fc2816cec6b179f5a677ff81a6bbbb1db9de6df63cfec75148a84c872188217ddd0630f04968ce9c194df679e5592606195e633fce24231e5246d27d98d73c089784e46ecc910083c0e5caf5b9c60a9d4acd897222ec53c54f8ad8f335619c1e96818bccc92db14b54c63cbeb43305b0baa133525b12eabc041eb7b8baa7e397c0ba2e62c470a3fdcc199974211f5fda714fc1b877bc56934807365482f8bded44ecf5135bd6c8e6b2c736a71b821c740d5c9261c367d424d10506c77a2a1b9051dd5587011aee428cc8d27ce86627db0818b0882c762cb67c61494a26ef7133899cb5d7bd5240e7f072ce21bb805e1223615f4a7935be8725b6c387e17fb8ab2e7a605bb9e1ebba7150daf601905d3ca5fe9f2d2fa0daff810129c8b5dd8fd4094d024ac31344e809382b38daca5a05aca1736a552036e58983fe570964557cc509b110700ab5684a2dbd8ac0a6368e3a7f82542fd6a2220772becba616ba7b03b2b481cc2f18f79109527da148247918301dcd929fdcf30b92439162add6d6b88f93c21897b20890d02b0737b938eecdafe22e97c65d966c8f33fb9a92bd341c3737e692c70afe5dafdf3eda2881de8f653ccef001f57b8fab933853d34fbfd63a2dd2c269f2d1494bc01d1bb9e4b2f25d5319eb2e7e868659181c47787488b6411e9067cd07db6d45750dfc2416dcc9f0ae5ffae85a575fb2197329a211f3cec7472d89a89c495f2a707a5d80e7beb3cd5e3934b9a689adfb307c52cf92048b2517b102bd990d09ddb440419984b3efc0fe3cf828132dc7099be1a2eaf60c416c98977f7f7a4948168f9084ff0aa1a64763d86dbf2fcf1c47de91b3f0b46ad0dd5233e3e52a04857ab86d4f4283809f89f47eb14e8259a0810bbab5b6d41e96667443e20afa11c7f7cedcb6dda7a97f36bd326a2e5f29bebd89f28ce9d2d9c928a95b953b81a665c9c90768441ae70d390a42ee61ce50910409fe6a2adeac96d9cae111ce2e871e7a3f5c514aa3490123369b8a7b3b573408e623a133538ad8b0a4c701a186b0655fc234e434306d226c9c3af0e10b6e8af579b8da3ae357c5ceff0ea0bf1181081cada8fd6a3186e33118e6daf6052583692f460afd49a82310b4e04ce67a6b0503c4d7dbac02e855c7e78b7aa9838924a84f5b4db48b50d3d3dd4eef893426b6440e6b65bfba30a38e236909373ea6ed76651e58390b83569a8883daf7386d34946d18d8610bedd5c8fa491f614546c97544f5d38cad1e9bf47381d56d1ded32c788ba0b17a41a654891ebfae880a1c9b1935023579d8fb97fbc894ed91a80753829782f02e131a4c46611b8ae34ea2f5274a40a9d153a7484fe7e4401a7b5dd0c2f55b920e6caa27cd66dfba7d2e7b7c8dbc5122c3610b90e58428d0e070d5f7c450d1fe805b9594b0c3da8ef38679e7f6c9ee6dad0fd50568f0a90befb180e559602092899a1c66272d9587a4ae491a2a57e290f77ab7f6186f8cf476d049a588efe3b5148b98b7c75f32a23fdb539649ae71ca55ed61859b9594332ce57f2569fb2266060901357e631576d342acafe2b88867600cb11f39d04c11d957886e71ba86ccc6a6d535c386b1d562a7a9629c46beaa322d6ad570acd8568616f09b29e14aaed8119b71333b4cf078947a592f69048414001b617c14cd7ca63ddfa0feaef76cd538b004330f25f9f9e0976f7e1be07b25b82e73ab0d4159a6e2da015e312da26e22dda81f48e421300a10ba5d6038d118f714facbcd5b2a80cf0566fc2ec377f10c432c39b1e913c7cc5d1973f12190db0abca41402d24245843980ded4a11fbdeca625a99e43f3a60ee87a2312c3d6aa167bba3675bdba90a116af79b4c7c1a3c7ec2f34e9340bc064abc6b68422503dd4a661876718e14a9c118463e58129c70401d148138172ce8ca4e6cc77308b2348041d7356e0f78861dcbcff101a88a5c741e1f5c1846e27d802cc2280eca4b8698836e24e0fbd324b61d094ffafef7184807c6d04fee52f1027730df1dfe53baf5f5dbbbc96ddb1fa4da63fd57c8c8c9fcfbf78f482557fe118a54ad1b07759b420d0feaccd9d3e8cd4c309ce4b81f0a24a9aed2c5b1ecc53a00c957251e0457dfeb5c9cfc71a2e1d486c093249ec99a10b09e5443bfe0d70520f66ca5fb721cf442c9bd0d88017379745fe678b4f56d752ae0a8acaab7e09f36197b69727a90e3e165614b6e101d08c4391f172f3c077fdd86a50f8763ba6e4b333d676af5af3ef9e8fc22868954abb3ebe15353e3f188972a7956da3e08cd75d65c7677d8847fb739481156e31d16364558a459c282ac04ff3ee3c823d5598a1f7dbc88e7408725d829bd1e0f2f6163d9ce579446fe81b0731d6e318660ebc4d23d151f27af214cdd6bb8e13aac7643dd0d2e8a04e293e8b37dbe5d42a1a219510540f0ffb72bb597ea2d166866a1d01dfb6158dea8ca4d050d64c0cbea51fb40f99365f1c4a9096ab3bf075ba1e9a39d48ba4065b0cd336da2c10cea0e7849790de4d9347ca5c034c62a0fd77bb27c2e7262a5cc612122e10fe194e1674816ddb390a6f3c2f692b850f31dc5357a703bd8f7d707ffd68441ff7c275f019528bdcbd691ec01b3ad60d8a9d46e8c6f79e1f94054a2d09548b628bbb4cec503ff55a2fb84bf3bbc706245a203a1cce26fb8362cab05fd1946158becbeba3fe5f5bcb98b793d7b398e90585cb7491e2967f301b7c4cd74b509de8072ceb85a62a82a8a2b6a787464721d84637799ae404e5148370413e168a268ffe202dedf763e61cfdc1fba1d0592192f127c80ec9b243ca7c31e182abaddbff60272b309f4ba81c724282b2e136946643f453ac17cdc8ae2902a2552510d55ee12672c9d9cea754f1e9615814236bc0402a530114c6c93034f4a8f96b02c4f1881853be1c47cac9d926e07833457175df8096ec4b7b6a3f95f801412518dd3e928f493603916d15afd00083165c9e0f4f119fa564394f1e0027997f82b0b59a7f31fc4474839bab82e98c1a12deaf1c5b5a88c5e53302bf0fc12a7d214a6c886a6039aa29660b9b9b9e9bb42c1a8c38ee41e423396f0734ac720169321d003f189921da6bf72283f9f8b74c24a7c6e3a97c1940984fb30c16d06b9dea5124c74da858fd04a70bbd7342c3d0476af61b94171121700c263129d24a6d453be2d69327f7b8c1e150bd238dac63a4c77b56c75350bf4beb1e4a5a09c53f6d682a64d54347588f1e0c8d70185ddbd333fe487f87f016b14ac749832227fd428ec981f5223f728ea2d5037aa8806c87e2ad62cfd5917d33dca352fadbe7df7a7f50fec613a9656d2443135773e59565145afa396f143418843cbd79d75f03da0cd43fa8112eea0ee8290ecbcbd520166dc132f5dc2bd8dc3fc180daa8cfdd397e8155604bc82216bddb7e79197237532f2634914cf74bff61541ce1f71d930064864107584d05d1e285f50149964af462f102068bf93f6b6212b2a84197e9ccf7d8e83e1710755a6857fa0490723cb04da87a2421f0f41624217ef98c9df807de165311cda1b03980a68275734633ea88dcd99a3316dfc62ece25029ee8829cf5a3fbe82cd8137d49af13c1d74f7e706b465f3ea3e240eccf1a7d95d93c00d87be524b321ed0314aebe50172cd904debf8cb714af18bc93397b7eb7c98db3d755fa6ee8d7f75dcc17a329fee9768b144e38baa5b3540138453686172cd9e71ba263dfc76217ea7a59e1b371d9c57c0a6283b4e21c1bc2e7b9567fcaa4cf61c48ae89087861b38ca7f66e1fe73c8e22c6e0137bd3948bed541b532b4f724b992af915656b85259d9067e9e684ccb2cb18d93f42ca1f79c62f35cf54b89f6100d4a84abb2808a5d878a2cdb3e5e7b7a2eada21553ceb86ea377f0710e08452619b9905c2b4703027a811d90f8191fd69ff22a10ed5d606ea38d8eb5d662e65a29d74024acbf40c956859ab8dce77a16c8fdce17aa68ca03de5a4da03ccd50f76ea632e65af3a5f9bf2fd23fa712dd49eda98e8ed34c849fa331fc52828d2995a3a0eed6e09ba476b011adc86dd7ddd058cdcaa23dcc53e68f6859c793c72f516e0f9c5088febf794c31c1fb1aca7726682837a6a7c3d0e47bcb82c4bc34f5fc3d5268088b0d77d9993f8ce1a2412e9c2bf60643377175580154a3ced2d24535d3881b7f18965a8a3984d249de2ef91930cc6e485c722b903f45ce043e1488f3bd367fed261a7fcba74de4c9f4d1489270b7ed02ef3419e14c4b7590658681afce3a07811e84af6b68825b4efaecaeafa31dcca8d624051a1e232204a38407b0c0cee3c73373e31ba4b6ba77a3152f0b7275cf51811cebc81e2723a4a4e003c65209c5c95bac45177793859525ac6cb7fb86e360c2361cb00ee1b8f8cfe74b1fa94f7b168920ddacc582cd2c80ff3e5932d440cb8750d2e5b6d0f38fe07eb4959ed6cb62ff7ef6a03c50f6440446b17207c60d87fee8b57a4e5c3cc4e915934f641f65b1ef5f01e69855d308d828175baa1d016f1b27b1389f0d420c1869a49b254188e69c981691fada0f9dd5a922a3611ced89996490ee95ca430ae77d10faae549cf66b8008b32d46d8b91d85e5697229e89a0f324ff124f6c1ca0a0ccd81fb861db4224a16adcb128d02630ca5d841b47821ddbe3113347c7e06b6a33a6ff7dc66c6a31c00f72f30090afa722f3cf36881c86f525a56b2913912e8ffe57ec13c9f70f7fafd5bebca37a4472b0cc7d753a6560170058e12a8d92534fc15c1295052af2ecd79ffa62658144c627df953b03b4ef7fb86d6a2a5a55cdd37c70c939252bccd93570052e11f7fdc104b62a8599a392d43079d0c7298ed1e6b5a2c4eb3a906a805da1b1196390fbdc19b96f2bbdca54952d9d14e615f2665ec40e68a5c3be287076a3f5ddaeb86e1ad1e1db90bb44d843b59df47878a2e83921628a285df7f26c975c48a4ccbe7125d00c3a4e96fe9bb94f2bc6bb3314c566265bc47f42045dc41b50e93e3d2091a7a1bf8509922bce22f47644697ab19cde21708da4e4e76bbb51a52a46e2406cae7247a797ea62dcd1e8e88f1cfe43b546465220082299277b449e71155808735297c84e769dc8cbbf62a8259d0264c4b44ad9486750fc8976b5270d97e2a784041eaad2fb5bf37c800269f42e0ce91027031dfdb1a6fb0d64aee397794f032ed899dc53f0b13f65b757b422f8313a9820f219caa007d602998f73d9cd63aa6217ee60c0c4c1126b03cd335538562c245977d6ceb7eda2a206ea39177d54000425c888011f00ec222a3a3ac2548acabef796e89bc338fa41b20c52f7547bebba265caa134222b8a4f7d169f52125a7649f3c6f5b8f0ba00d588bbe8194f0262fa0c4a4b4a396d8fc6b3d52bc592ba46150d8e30ea92d9d12b16a9aea41d3232b75277a3213dead10cd625fce9c214555da6373c5b6c141c1e33e7968fe1308d88a507cb1dd7f955f632d38e18fff68345ff63694bb2abb02b6458b134032f845a90b6516df21ca5cd0b0014033394021e3642dcfc3ef34aae1e7630d15d79614c216d6dc58e1dab667d1f08f65a250b3eac64ef3eac67d5daa1b5bd5b68ad76b7a6bb5f90f242837bce520c91ef12a8ce7f20e09b6202d583443e48eb52a58d4fab617acba303a01f0826e2bcc1fea91aacd7c67bb93efe759ca783cff2878ecf4497aab2534c87406dd4467bb5ce623c6aa2cd3e88116262a8c05d09ed07cc6780694c1de57c8d410b5f9b41685831a6509a3c0b581a90216cc45df43cbedbfdb11af3f6bfcf8ae111c382c388cea1ff5df4251d0f4e206a514148e2f476d8080181282a707129f0c604634c0592a4a837e0deae917bf56351498a2f28ba38c1eafe4747b358328726705763a81570b3de11b6e13b90aa6744cc217c330507bec40f86f3447ead40b7a90c1f088828cb96249da1a06e92bd79e31365ddb06e8b74ea9e51df1374df2d42715122a78b884278c0e24dca6a8ea3ebb82c22f0f35f7219a4f0f10daa3bcbe093f1c43249cb6eff4bafc865d57fc39139ca9c1045e64b12b9bb0a7d53b12642d08b6d5679e56a6b550a8e61d841584381a35f8f49ee163a65b1234c0d3b5b4a03455aab3b377a75d8d442a82b167af3ff9ce0100bdd8a1ef35d5404ff3a2c96baffc051c238787865501abcc5be77086a4bd63c900a7ae8542961d941592136ffb43551703777d37117881fbe01a6a9d73548a4acd03e325fb5bbdb82a9224cc9e9b23dbba55ebbfc5edabff648e26df157f2f9d7aaf89935fa02bdcb3dc43196d7ce968578e4c9b695df2756734f9821e9d9c1a239e8348e44c00e64b73098ed72e7b3e89a6a476cb30333d1f6a330505cdf046867d45b02614386a9d6661ea86dee8978547ffb736058f951102276448c5897c1cf8011856ac7fbfc299c026d9a3aa2427a969fb13ca540cf04e0fbd60bf96a2f57f3a4b2619a6fbcdac5ecc6c813e017dfa726ce467e16cf3423f013a773ae0418c0748091cc1a994f5da4fb8d37d907074592c107da77b22a6ce2b223cf59c3919ce788d4d174c949aa6aa158361b04caeffac5acdf05102cbf2b62fead442f2003677a67bb53f03e912bb56be3302c6b927e4844afd7902aec9d8247a6c58944bf15f3021eb138cc6a8e2748c375181fe511e0ccfaa5b419fe925c05b6e6410a03918e1926117e2210052ea891fe4e4b7bc23e380bb54aa47b90c1e2149a180d6b2e9c185911c648a2250cd61adac904a5079685c61a961598e247e832f6df5f703aa68595604b04b59ac11320d1b5c4958c67155f2c209a9457aeb201147c60ecf5f45b60ff7fff725256568c0b2f29f95026915c10f394503f9391c83e2c34ff7b892718008f3edcab9f53d1e296fcc08360bf704fcfbbffe6db51f83b0d9ae89bba981027fb0f1f4298570153d3ddd43088dc8df93741954608e421dc4a14a26f56fe6883bba2731dfa10f1b2914c640b9ff5a1aab93978078d847aff1e4f7e24325152ab1824324ec73138eba10d77cbd115191e8cfb6fc552218a7693ff7255bcffcbc903591dbf7123c0a4d6204dacd8dc4084961ca4a252b553a024b9f1e46e42c57683fda241e2937f99f22bcf2b5fc9cfa0e07d1d4adc88066f8e85eaf84adf50b50b893e1c4470135d76753911262536556c8fb6ca68c95025f6946df7a9bfd21cc4cc08c564495bd0c9648c24af306e021ae8657c788d3b5f399ce020b471fc81cc4cf92c3f737702a3d10ff97c27e00b3134baf81e7e4890afc48118f67b7d7087e513db752ac386e7cf4fe430cbc1b6c9f0ed4158f610debd4fd735b3c20697a11f33a4efc90e6f65d09e1905ebe5d639a2e31ddaed497c256aeece2f0e7239b066120be7804213bd6195b3ab9d57d34dbda28de6fcc8e47531079f7534376e811e75baa5a3abd4339515342e80ddd130506062c8e5c5f0c2507f61f4b8c312addbebd356ebd3ef50665d05e987e88ee697976b10ab840883c4b7a3a759fa8168b50f07d5c3deb315b2001ecb2106b375c39524cdc06d8bbcc2e98ba1cd991627301ca5ab1e15184fee0fce65afe3e322d3918770ecc3878965e8380126e352fe2fa2f9e5b3319b0c60a6723e22dd033d11f003a8b7e3b9c031f20bea3e99298218bb4b49f8d0619f91e1f21dd15f0de7e28da0e7bbac3c6e3ed895b17c7bac1c5fb1bd45e040bbbeb595c6f8c0f48be85de8cac226f76fabb980da37e83edd6a10488410dc2ffa5f16c6f8fd5f0057e35f8798a44490c260d2ff328946f3c80a08f22b4d1d19e6ef0a8f96ecacd1317b59c47070be7587a22b52810836c8c89be1085ccccb543d80f9d0fb2fdf5f8dbb3bbdbdc92595cebd83b0c063f566dc09483b78693b9b9455f54d1115db06f32d9cfe9901a8cbfde98b0dc5d6bec3bde104f1b7c0a90f9fd814a615727289d2865d345398cdde8c2c1735f5c31c164732961f1bb0d9f75c5ccf4167ba8abeee24a8488f8defa062a730af350261ed64c73b6e5ed15b25414a20b7c2d03b840f60eb9bd99acc32b9265c2ba9fadc18afa2836383a488583f1f41870fac8872235db2d8c7c24919d07715a4711c038c6bbab1daf380df9609bef808ac533d3d8513db0b2563bafaa682674ac5b16016e18c19c00f06d27dfb60a9df092b576a8e197e970d347eab9712865315f35c13e8fddba92774501d101c2a5bad8fbc1a6aa2b58cf4f1ca923dbcaf7728b4601ff6860c312d966eb84aed3422405c382a8b7b49dcd8a45dc997ae34d4f50be08ef4f5c6a3cb14927a437f86752cb4761081dc875ce5fc6c8a722f65da62d84c51b75070633974734bf092be60aa207653ddc51603fdfc30a9b26daaa1f61420af742375015e6f21f343c49a21c018d8f5a9ea42c4f5e6dd176c5630538c913cb0157f54ae4fe57ba8e9b6de54f135844f2bc35fc40b3e4b197548021c9e1ead9306a70a27312781960fb3927877de1a11ddebd10776ff960fc13ac925802b0492728e5ab7f8a0cc78c2a16b227032dbbbff896e0d3d6b310329bb866ef21dc64a14e11e4100b092b4e52be1a432c92fa4dd72959e616fed55c77777ae3e7d92258040913043fbbc02132f1da5f109df3253325141cd8757fd28539cc3d48dbb68b8fc586f4c8e4cb7e7018a0584981f87d9a88029ceb54ef5ad078a2933f709864a243858a9c1ac8bc67b40333c6cb774c489a854b28b9ff39802f0734a56ee743ef5331e6f1a62ea5ff5b2d2137d0ec1e0db6e0032b3e9049b3657042c4eaa8b3b60aaaff5bec9f52e1d96d9039c8c5f1e1f529f6507d7bac86417e53d00231e78f773bca316ef039a53689709d449b56dc1cec425b47189652642376b8533a8c73a1aa3f6d8c8356eef8e79fa1542794a71c34b9851fb85a5110b7becda9e37dc9c4272f2461b0a00b7d474251f9024505b58421e0124c23188fe82330d01354380f5bc1ec921809079d855cb026928569e29265593cf9d4fde0991ca4cb2c89c1d8481236801034124e2e70aeebabec85c8adbe52ce6e5070cfc0f5d4f03579a8955c3b161dcbc19eb1b4466d3e1694e297bbab637ccf34adb810d04e58d706e20f6ff08e496a6053cd069bc5c71a45755658972905600e5cfff7e754e0aedfeae082ace20e8d7ebb0253f4ec61cf0446d76472b4473f201ad16220a590b41a2212849df30a88d98b89ca50b91834d1a16e0e12877bb4aa1bae74b7b9a80252b49f7595df60524cfca80a4cd295ac250cfc8d268a4ad35d23525211740283c8db47ac3a65223e1cf0e2b469cc0c035a91c4064355fd095690afaa164e28a64a2e122191427bd6fc812fdbbfd8ef9c7a27b975b58b9b01809261a0923c1220a2e8c07b83645c7c3200ed0ae8ee1d832acd644cca079461d05be0b1021591d8aee221632299a09a083fe6e16fc2151aeace5545390bb3f9e8a0ab1060c49cb2e940c1756aaa6a4fe7c02d05b49f998c629ddbce637a068db15189885c603112cf429123128534aa498e0107fe72789596f446431f265e6e6a4290acee745f2f4adcd329064de423bf40d352fa1b32c49b23d39cbb30c9614dc5335944bd79a0fd762c5fe4a6fbcdeb5742007465f774d56cb0d5ab9334007b96a4916ebecc9c273bd3b9c2687475be604a55df687d738cbf4df62aa0230ccec27e225de1c1beb5784d87da48dc8fc1b3b5c9fb8f900f0b5ae01a2dde14f9b7edea585abc27a09d74f4016c88656422d36aa1020c6203b1f100438196234b2cb8100eca5c5b4f30397bcae3c565a4844727273aa5b15234761c5535d138d7b4ed6c96b4093a95598ad054e5f7758c262e135683c160ab23f212b237640878c8480a2a80817325ccf1bcc8f9d4a265d512a1076cef635eb4ec8ea0e15fcf007e96a35ee24a337817958d76ee01d531a9b662c69f8749da0b181fba609d2111cad3aac6d1b95bf9f1e5c76735e80f235563a649d0e231cb43def1f6e0696e6524791a9b38953e72f350366467586529ddb8f073dfcf30b3c6448270eb758d5a309b6f0ac62f935e9207e2e9dd019e82d152b5807969d1e18a9061c5bfff7be7e87f3a8c4b8358af71162cf8ca4a6caede31619cedae4e40e938bce4c0c75baab7637ba77a120851bcc96646e2874eb2346a961c92b496e28e8c2e1eb5135765ccf6f1b63475df73fb744cd0bcae96d6dbbf3fa2a5ecdcf6149679ad39aa3ae9bcf8713059f5bc8de26e61781b7474335b2397d1c506bcd21ee651237d551f26334df409f9a9e85f5710fe11374d9edc4bb6a7f7a8d5e83d820432f23ce4257fbc3a5719571ad63274eca2cb26ecb562b76bfe424603e93894d01d43a4b5486b2996b163e08ff06b4c2c8f908fe3e3bdb4839f82f0a60776f189898baf47618a6559867962a0183e327a38e6c0b3c4edbd254a65feb0a6cb89d1365ef87de201cdb5a66dc0af0bc3d502936aeee100cac4f6e4598ddb1d463352e3d02b62bc60512227331899636a3fb110b7f823aa740f0daf75ddbe86ed6653682b55cd438acd5b5a25714803a488100d847f6e592d116e95314ffa8d80ea2e562e50f481b7dd8aec1dad0cc3db944968ae5fdd3af2c462a9603a5602a86e3b52700d8b0de7aa4c932fb8a36d25f5a0483bb064f34a26677896d06fd1332580d705fe18d52617aec08b6170c4e3290f765e15f9e117e2f28b859032dad645a1ecf6a934c095edf9c326285732aeecd9ec66e1fdb4d2c653f1ff4ca430e7cbfea3cf6f23e4caf838e7cb09c0fb0225840c2bdd7b8eebda305f16f056bfd05ba11c8e2f2fc1b93974dcbd06c1ab967d303bc199911a808df56c8dbd3a42ea16791a03256cc23a0c0c81ec66a4e81abe1c7a6ba5876054d5ace9678b6a233e760307e1f0ed3c9f1fcdef7eb7b50d0b8ad8c400f623d1ce8ccf0b0fe606139abb43e387773770d7a88f540d4e358d774f4f831f62c2c14654cba1c2b49f4dd9b598dc0fe9975a699e6f03608f9b540d4590a0b9ea020c0658ee8848518ab00c14c44fd15d91ab9a8048b833e57b978ed26d808dfb341dbc3111ac40f171a025a2ef82344ecb0206d4e861ec1dc8ca5ee8968b94b60fd759f3f8d852762c924d2a43b08190bae50aa4a937e01a5a557d7956c95feb7bcde0f3214a5fdaa70a228479743befd25d88a6dd384de85d7654aeb7c5968819ed0c91a6fea5c1d4573dbe78ddb1445635824eab7267ebef561b8883cafea0d24685723f6aa6f54f03db17767dfd05f40b4f8d6c4a95154cd1fe4e2e9aba6594558f8ef5703bf289240f7e8057ad16d5d519a9c4993e4bda03aaae69e534b47f9464a33db3d319595b3b99fc697379aa5d3a7f5372af8b3bfcae0057e6953c6fc27e68a279061ce853d98339238849aea161e34bc4764246d5297f323b54c872c12a13f8c556e9a49398269446c6f0bc8a068ab4c3d604ab799644ddd89bfcccc468b32f48bd2616a59ecb1764bc86196793f001621cdf8547365ada1d87a7ad082fe627df44741692e5bb5fa432310481eb72264a943306347b661fa7aec07612c010d565936bb39e802d25f423ec15b70dfafbb17bd5013a7cd85fbb77ab49afbab06831334c9a71e88724c1dd2b02d5bc2fdab0db6d43d0b40024844215ee1a632f819bc42c660a38348b52df3a63f031b853ee677fe01e0120bb716e1a032222aaaade21d8453fbfa20969bd419148236903ad1fb05e6923d0c3b6610ccf73b7352dcefc6ad2e302da6063aba881106489c8c319cebbe34039f71bfa943c9e8893064da486732170b7b372f0c8198bc3a9077b367acce96fcdc12716a368111d479a4c51edff8d3f291b73f1091b55efea43c36843b556b517bab6321081291c0ed6e8a56125f43c11a1e18c86310a371d61061d9918663a123969c025acfb5d7996fa827eb3e36507f9a4bfc06939ed9ca07c5fbdbed338f7509a29ded9c313f82befaeea1ec856c30309074c1fa93c54b8e328cc108042225b0c924471434c350ec8cc97faee33b4f30189bfd0a8555d9b7aca56cadfd04d247eba1e174eab1fd38b311d30ab319d09c5197c00b941cb240ce7c64c94e04569b4ea492dc48811079f082cd858d47f3a1068dc5651450795a5ee35fb7324bf303f35524fa43fd5e71eb7b77f0294ea077000fb2fd1772f8fd53bae12b59441fa1200b33ea7a80e2385c04504ea0e92dc631cabc5e6a244a92fbe54fda3292421ab914ec81773b03ea46ca96aad98ac6f75a5e82167e4eeb70af6d8ef74eed5ecbf58c95f6def3bd1b2f725c2426388d8c1ae9d22ac1d84d48540a0e157fd49e7a196749a6bfa088f81e8dfe0b8e5a6c535bd97465419c0b53794a60a1e055a27b3d8e83f5f0c1c2c04ae1b6acfd67c52c9a5f193f763e99d313425ec0708a29cce991654bf8f39a1961aba5b17cf6d64777db1f738e35b0d76c1b35f3e796d75d2a258623337f5e4867526eeb80ae025e6866c9c09c5858bd8c1af7c57631966b2902453648a2c708f8e1ba81e26b81a18871967f746117c80348f65742c9d06d1eee94198e2c4c18b33c7975646ed1b53b33b9311f465308216add7f941259bf9e15f1d92923b1984891e2bdf68db2d35d2ca15346bd9af3faab9d116e0474f5c6db5f8c8029da24bc47ffbb5d4ccdeb80d65480df04ba4b7d01d50dc5b00c02f4791182e0a587e031a1281d06b498ed28216f3f83ef5e787f07b574395a0d695886a69e303aedd51031e3b85cae3af8c0fe8ac88257260373f0c63ae6eb34a3e0541539a3233e87e0f45302d39364d10c6a0680bef4bd1264f4a678f1f260bd11ea2f0a7e2cab783439bcced5e8686d431e52d18dcac8aece979ebb7e04c7da930b500bedd6af8c569e2376ca1c98e1a40e5d3c0ad6e5dfdb13f4b8c10c3d74ce109c030509abc991552f34d2fa1abc4e5cac5f36e550651c0cb257b89f24805e8b366e53d03148471b6f8f827e540d41f186946e20bdbba436d9f93e3e25110738f056d939de449cf42d81fa62d03d1eb2805f329836a0b5b493f353da81c05cd40a8ac632ef21b7186471e0f082025f0c4e7eb5af23ff9290e415d4f540ee47ca5180ed743be6489817b8db3301c2e39ce69144ccd584c2503b17ef7160eda91c32aeb43ea88a5e69beb00b1bf20f28d4684d25904f1774a9795b6008d2ecfe6791dbbe968d3632422d60414e9d2dfb044161e27c5684776cc01bf3c9a8df1ce6245344fdd01140011020158f058c4559c6ef2ac3ac05eb2bbfa99c7a10019e8b3675bedca283c766c0465becd247f4925495221a4b6cc6e939a047a9d2aca5b159f885d21c59875d6655414fa3c3d20c8cd1f390bb4a39f734f29ce85d8d51732e90c982bbe6ac1f5c64bce1d6a7bdc7473a85a34c611a56e1a347c1f5e4e3e6c1be73a1b3e8513f896f47c8705441274e4183481178e778ea696ebc231f19ad0b7e399e083f5f2937bef2016e34ab95f809a445634735ebd75c03052bb862bc82922e18f51fa9925fb68d3ca27c31cdf32528937fe0e205dcf985e4b9705b89f2470fe4d41a2382bca5e2d5c9aaa115a20842217201929e409cea70027ea03246dd69ced85c87ee32c0897095c0e369210dd1c549cf0198a1409649444abade8921afa28916f16d0fc68c0a8544b7e4a565d005d8a7b947e24a3200b5955139d4b5207c5d2e2f020e6a407a6d388c275228555914795a32dfc4502a7faec062fb625f491a013c7dab5e9d0b5f457d8fd7c31093b692db1da5a55be95467c178dbaca6d390dc3b73a9a0f8b2efc81d33daa338672f0d854ef7959a2aa3f904eb215487ab0ce8424ed754b0e87b6c027cc7f4e86c562c5b2b8e81b1043e68679ccd72e8e9cc85943c77c5190acfc178c24bf7c414b2ec93e6410f7e855d15bd67ebfb805ef1aeea618f06a0b252481fac81e6fb96a76e1d2e77db2f07ee673be832b090388b6d4ddbcd250608a24b39022dddee5b3878dd3b01a15d584efabfb989852a2d5e54c5b023f2c420c9f73004c19b81a03c1d33c918e7576df715eff2e771c6cc7597656f9e5f646618a289919eb7bfcb7f9fdc18dd28c66ecc145aeffbcca51b995cafa27a1f94ee20f37a8db9a221996232d603ac100ce03336cd29442753d282c12a85d54f815e62fa4a6f9a3987f5ea7643edca682c1e1bc1f01780bd47471be69057aa91a3590d88d828ad217df69e7f2af681a9e5b286b201cfd767013adda59204746e5c30cff23fe749bc4f9d80985f26bf8cad8e5e4e43d705f3f7006537ef1cf3872a4ecf4fc481abdd8d7883ffd9b3cc2d3f6624fe028123ddb0e04efd46e1f5c9ebeb5bf7289737d82de0a7ed0dd853245cf4ece7bbfe818b03615c542ef7a1f395dfc8f35220e7be4d22cfbbae1b8e30d4a0bb586b011f815f0ed47ed13d3e88b757a1855427d01c2921a8738c8a8cf6fc3100b2e390a84d1b9c91ef790e831bedc615cab8ffb4bcd260238efef2a99b95fd208801270d93164e14526c73871f911aff2b1a91eae07daa56158b082206e262513ab852a60e1e8a1bf83da5f9bcc4081c601be72c7430cb84c5e998aa45bfac072bea914d2b2131700ae71a1135a59ba8e6a09fe2e9e59ca11a9737f083b8fe95fd5f9385efcb1b5f9628a5b0c55bf3f8b8c249385c3f6caab5a38b2891c3c94845adc95cf2df6e49b60d55ee28854a985cfbd410bcdb00f5a6e0522413c900812b4700486c50b4149ea385de12a9bfe586c5b51b4a6ea9f611790d1d60ed44a08c23220afcb2739aca365b1852afa8a006dd8cc27ec22f81b2f2dfdb8868cd4618da2000ad4da40756667795588238a38586a8e2e78ecefb49314202af892468af46dab809ae0aacb7971b8eb013ab311e515dde794d5bcd004aa6ece01a74777e4a5b31464e11379962be5994c39a48f9ac2f66cd3d23de95693f6eab12c29f7f8a871117a35c5863fbd96daf5f04f767c6bc0dc5938ee412c8cdce662f0eb3f1ed463da21011db3810c302da8c5f8d1c48dd74c4113688105a33aa781b562e0db9de0961f861d81b842d6b96940242253b6a84f0e3a6a1b1a46d4c46599c33d76e46b47068fce361f37bee5478cd5d30aa1f7c3d4e55b1ec84172248b1d15a9b1476021b7c953da58e42396b9109f4930d4cd30e47319b49ce0f718663bb898f96e39331f2606d62f770c163f4175b7a4923fb3109035f655d23b57288b800c269c74e76cb5ba9428906a2fc324c4d31339c481f715d5c6b912d7cea1db566ef2bfc9a4275b3cb692fe95117d719fa8d1f6fe7209ea3e71727168066416bc72c2ab1ef6643a4127948be4cec198d18a2804a12ca3c75ef780edbc0859281a209fa6919d7d656d699d31fe33f0a0bfacc3817d78bb07082bc630731cf0956b9881bc0db7022fc64b8f46e80b4470d51fca8e712a28a8f5f8877308e2e60d11f4d2e96622e42e81ddcb32df11b70460b43674232338554f476946e74fbc5fa24073fd9819a056c0656bb3e59cc708909f339b5208e31d7743296a152a6c17c54f436d00f1992a20886c2d86c4dce379c3ba2aafe68c596123cc44ac7b211d159063df8cc4c0788b4ab53b24a0a947dceb0fde37e4f53e340a8c42dce5f8448224cacad9f57c9691b52070ff223a373d9c6d82478309122cf1820ab65c132f6509aa69abb497e4cee72d589e00bae12e03fed61ba08c230e917505c5fadb609d4f548a9974f7ebe12a1be90cd361cb03f315e7502ab70f5de8a21c03a89cc84019ec496a4ac0a1e7b2a21d8026d36393c36670209a0266fff01cde196a54faa9683215bb9990ae9c5a4522a3cca17a8159eeca4f44f8b094e4b7244b1617851496803c112a2f7d14213d77f8b105948862ecc2fb92b8d352580c1033c9904d2c3556bcee3191cd1188c83cbe52749245f167dbce0422a9f8e749cb07297598d8544d09ca07c0a0a117342fbf50d58227cc367cef93a175eb159462cddd342d352083f2f99ffebfc8d9131cdcc561b6419399c6feebc4b2d26376873d56f4be802a209eb286c2b2f22fc21f1d642d3ecdd9d9587be7a3d02357211a8e2d413dae55c78bd64398583f0e7fb36ad22ad8916e0801f195839e99180114f0d584541b77269b0e25df129575aed5025d1877660485fd87ce00deae0acb1232caf28584120f8b803b53ea9406cd378e01bfcfe2a5ae296e79d7d60ba7e3dd061c06dcc16b4b046131a1ebab37b1103731afe01d59695e54338459592672c64d6d274c188b6edae9e95e8e8270327cb4943aadfb2fa5f0b608ef5643efb382f46ab7cf9d2c9cafa27b6e560ec77d845a0247de8f01b9ca30531b4537dd2d4e280e3fd0d0d32029a4dc4ca639b72857aa61b0d2615940e9d5a0b9aeb607633f40d5c9a04e5da3be884773e46a563d269a8c747663c58a0d7a81ed971404796417f783fcb074fc70656b75cc3fb5fd9230f182041929902625088f8550fbde9e5eea84fda678004c5e2409678e2c656b68a592b2a60b5ae453b1d4d3c216d462cf198399a9cd7794da8703438270949b34f2c5f67c59f9224d12e3c7f5bed9b517378dcde32dc56d4d72dc820db89162c4dcdce2f584813b5226028dd4b5dad75a5404661ca480fddac6e4ea2ce0c45207d15fa8209d7e5c1f441300972bec397b920fdb78691ec3b0999a4a9c632e056cb0c5513463f4c70ca12e175918b56ebf121800ff718ab2fc03f97d0d75d83ab7d97563d9d64207dc1c483ec8d339bb85210cad83d8e792c6272831b37a67f2bdd71d6c693b97b883e091e1615578492a92c66b3ce9f00f24d287ba8357bf695925d80047fad1542984a913e0d0fa5883ba25ac0ee8022b4cd74b51a18d0c502cfe994340adf46dd892c60068991d9e5d185b1b2eee85a14aa585c5c3bed59313c6ade09f0e04cddd88f7bc157d8ae1c619b054440fe313e03938fb2c02e1862717a2adb1653d3be5c65d0498afd27d6df5b9e6536682d8ef78592765467c7262001eab5ca401675570f1e7635d848753cfa69698006c2d236adb327cf48758dc8896e54e3a693da93e694626f17c2b7c292e8bbec5149e4b855409ec2b0183089826105c5dc963e912832569e5c9cfd7b9e858b0c745d1619f72f90055e21f58141ece389e170b5b2a40c0dea86d50081b47a8146750e0ff191ab6ff1a96c2a28e9ee53dc3f58aa8b454555b32620a93cdbe3cb769f0d54979f700040b1a521b1d5d21857f9568ebc4c372ac23a20ce5be25cfb9a28ff5d7d7b6bce6ab434017e78f3737585413b1ca1b58cadf0a4b4005b5458efd47a4b3c34845617906ecc5542a857f60b649a515f5b12069969b96587b17053df8dd5bc73806df9ef93530a61d69a2ca65ccc7c3b59b9aeabb6ecdeb01697f6fa7da790583e6b406da07e8d55f759e7c205bf5ed866d6156cb0c60f91e65a87f0c510a13ff7de49e872240589b20ef2c8c88712d066651b99db6a7438c7545c9d914eba1c2e8b3dc3c2cb4897fd6850c6d296334d84571745e14abb73e02e05416899ef4c04ec76c7fefa5a96e2a44f049fa41aaaf850523e91dc564fac05fa8193f5260ee9246677fd666052921a837e83087e7b9a37c8e835048d7d85a8dcb0a1311811db37e0db15372977b4285ba9fa367ce1f16f921c132f543ee4dfeb7ce3063ff04955ec05a1ba64de520166542cc77bb1b0f889f1162ac1092042a338a43012752c49c1eb10f73785fc0414f1f4dbfab854365ec80f047c3c2313461a195b9a9c4dfbfbea88a0f8cadf29ece29195f3e38f905538a4e936c47b3066b292626015beca4e370eac4cf75aa74807062b0f307fa69179036be7e46d8fe32a82b8866e4c3b1a32438a19f65981f069852a098912e7083dc3420b084251ddc20379ba146870993c322b3ce237eb67fca84dd7b2aac5c0ea037af1c3fdcbd156a1c925478739886b58b8ff6f9ba779edf5586b28de8e5d4c2708bb5e32b3916aebb758e7ad667e3f746ca50ba29cfe1385102666a35b81cad026bfc6ed58e3c84e4336734cee8bc9addb4687dcf55edc6d68c23608d11729c4ca664895482bcd8df826d4bd61ba0aecc1d42e03424bf259e889797bb61ce761533167c73aab090826b088d667a5a608c54a2a59250cc3de4ceca5ce64161cffae38691566a1bbce6926675dccf8663717d9a90a6c845a14f65b380a68cd0ee5c10ef9ebc415a9aa5ad0103efd9d0a8c883f3d76eaf7690c1d23719610544204f73f1e84d1d81f104c3ff6c10e90f5370fda0c03ab16011d59a0163da3485c927d6565c5fd838aa90c101dfc6eb339dfb3e6c00de4c9b9ef7c98edd2559de3c8b33d3ac35a65daec1450cefb2bfdacfda5f83024f9ff992880b1fcd9c2402f5ed094a048fe20858223361870f7adf14d424d9a6ea15d8e7229cc263ad97013841f263087a5df630695b104b2aa1c423cdeda2123ea4183c14f722e6658581bc2347155e0f1154b3521f261148b066fcca70641f22f36e27db864c1cd31a0a99553fb7fdd742622e79b30b1cf2f6960650aa82d87d7c3a83745d512e1de3d30e0ec0b1cc81853214f5988c21af5c8031c7fa7515864f622ddc7a9176bfffcda84bf8c67670a4e49205fe2fdb877e155900a6494d8a0e2df8b12be0e3a63426e5e06cc8da1d432a9e903efae1acff0e580fc5b0808b929b30938f155ed1da30f9a6b95b34b8e620a9d5ff1f5c1da46d3eb37f71dd3779feb5ba0d8a65f4d3843bed9031f60b7bab4dbe1fdbe06961e09b77c65fdc52a12f089663575684f64548d440295bac8e9034090ddc82818636047e172f38ab0482adabaf783fe783a03b679b2cc585a90a9e80790633cf81941db6d7f42eeb58a3961e43694722cfec5108bc4420d22a17038b81a83f5ec3a2ef6dae50ab74dd376b195ecba62bd9d4cfe7442dd4a4a8d9069073382094a45a24ed9871ee59042e15653f7fb9ce9b35452f09c204456d0a24a1627001e818a1a1999fc87e64c694c590d87760a2511721276c974c7305c1777e502dfe8eb6db3ac88b652a7daba47b550832ef2d3738fb1cd02d76585f37e38816ff031db9cac2c24eab87ffaa448eb456ad524a7646dc3ff3a5a75a0c1b3af30c3cdf35dcad4415c7f8f7072f655707c418d4a754ddbad7c6fae4e51ab4968348d967aede4b71fa49958d85766d1e31e8727b80007e363322de5698828bb6f497a360a4e06ab9ab5807ab80db31f5aa8e2e156ef4f8319b9c4ade0af800ee24d27b1b71aecd8ad1cbb7cb553cd2c09391d55401a11e1236705f55eca19f96784efda4eb94013888ee014ddd6e8955356064de02efdec8164e467f753a326e8681e56c462b57a97b411f07ed114c819e4daf2bf212d71bab76d89776a64d17145101b5145c16dc9621d64a811f321520fc1328b0c0b5fef6a3d1188ba281cea7e8ea3d50cc8b81f00719bbf8c820662b1855ea4f9cf715cf2b5b1c54041af138a13689e1521ed999fa6da50f40da423a45e0f89de0029f450fa14f48b88133ad5fc720ad9ee770e0ab7282085411fe614743bc70ed3144fd90d6ea79ca03282bbe3d1980986b3cac1331567571333cc4a28b5373c141a2b7e9bc7e80ac57df62132b2d97e2071ea14d79bce878789abfc5ac9e1a169714af0e0959c9945951a1c44bd5a3d465ed418d0c8f2f532e4e2d076362656d10df8a12b7c0617544d06b857fdaa3b19317b90dc142132bf6c1e00faff45466e5c81cba85d4ea8a459f5ef864850995adf423705f6c09b85049c841ea6ab936f9196d4fbdb2cf67a19ce9d71c4f0e37673c8199eee83342dd3632a2a44157c65b93ba929aaee13b4f4ba1b2a951d886e5a15620cc7ceab8245657cd3041ed5cad21a02232d3a24f9ac6293d377203c33cec9e67b0732d4f27be90eec3ab8566e2f81179f5023f7488553484942c951eb0d18443c981e364f101ac5affd801ffdf6ead6c37777f8aab614d1ec16dc8e419f8659bfe95d70df5b31112c39b40aaf0e67eecbb65af5e92a4faf82e7710f8cd0b08b2e6f91c8fd0dd4217ea4cd0dcd2206291c52297072af0ae5e138ce2aec73e35851cfea55748fb51400eed3a39e6f0aba2ab4241cd6041d422624a2b5756fec57bdcba7e4f74dfe6cab0f2cf70ede7d731e76008ff70dccc355edfa26acad052fda71e70db31a81bfa25785e70eba33e44da32841d7df7954347f961598f8730207936a7581b4111e69eb0c88dbaf9d5b9cbdc5bed43a5c88e8d2605a14a87b95e7f8b0e73c93664877ce4afea9e71929eed5178ca51831d18a60d18ec2971c1cc1577906adb78bca6a96c0878bc7ff22e990f4b0ba6e17d66a5b43e96018c8d31406616eccb523177a38d7cc0fe3d0ee4c72751cbae3b1f5876d0730c3e72132995816ae0e39aee305c4f3b87f828fa6ace861a293a0a3415589fa571cdaebb40f44d3125a97d777098e719a7c201d01b2455c0e2946bbc9ad08163ba18918436b54af8ee58a2e77757c736b69afd8b51ea392918503993634d3406d7d5c497ce8da83a01f7e7e42b40933bff3ec042382ae37908c210efdd2aa8a3d4c55be2abd3abc7c152d49417850bd729f21f35e976359a6ee6b80004e7c751fee7c7eb436af83ae136889589fed1e50bcc41223ef515a1b1e3709ed147fa456d78bae33ca991fbaf8c344a7098c1ad10ef6d2652b4caba186e74373b921b8dd6399a2ab74ef951a0ed5a312b9a4b1b78b14034d18b511552e5b8876c15600f5435789ee9928d8c526d86d6118521b9a2e68ef5c8527bc8c515aa4f5b9ec4c96069ea9202be5125540fa894843476617f513a48435055232e904c6d1470b6aa722e61b006862f0432f9da9db85e1c36361922d2be86e0b291b308dfa82514ff18281c1edc6ea0718d029ffeeaba96bdf9269648aaff94355fe1bc83d683341f1356348953951d6c44a9ee96f4adf23de825d1b02f5f4d3fa00115934f0be9297ba6c02beb3b274b755ba1f1a1e2eea892ae143b1c3aa89e294a3e015fa49d947fd3fddbb18d8cd02619ebb6d6ae0c17c73ab2308bf272de23f80b92a858e04bae3a4983813a26dc7028de4c15ac431acb9ab384eeda5c78f95655165376366f851a49c91cff3518175d73f6fed70216938674990825a3c68b886c1865e876527f16a5b41cc5568ac3edfe3eb0281c0a12075848a4542db0ed450b1a9caa098f14f048347bb57ce47b1d6003e0b99c23df4e7e0a02c306b666a99b56460928c8dd13ac6efbbd2e92849e47483e9accbceba16934ee8d9252d9e6de1facbfb9c1963588fd19114656d66fef4c038601e8e272d5b558040728abff90573851ceb3170068c3b46f36b74ff83e8d918a50362174599fe5c1a93f0dcbabb5a7323cc0b27ca586bf67233485c38f231f79b0f4904123e621bd635772627d2fae961f5d28c1551f5b9c7b47109f9da56c7d4536af7b688526c1f5ecf48e556b879213f9abc9c8fe557c0f170608b600f690d0c857eb77d36598d308d8c0c2dba5820dcd123080531747a61a5bc7140d617f498416d83b33f6ea2992cfbb0536784a63ff41c2cf9a5e5e68d9f7bdefa4f8b77d84d8366afe39cb639a8a48e0d5178d2245d9a3d97faf5392dfa29a4c6e0ac039f69a6ee622f42427df17611c7954f578da18dc9b544fbc844d4cf530a73fcb0477b6d8085ada288c2181af75802640123829e4c5467e1d05eb382292b08b6fd4d7431ea2c123f595956c60eb078940e40ede7fa6db88539d23359c1e0fa9298b480d2da3929091ba115799a005d6c1f5faedb296091bc3b42418d64cd12147f5e8ccf3275290aef9796e91e6efa3aaa651e3521764891e104afe18a672601b14d2b04d08f9a8ad15f3d848a4e028bcf02b8a232a2a79bcad4909d9922bc13be4b8c499d64552781ee933aa74d746946b8ea557f48bd339c132edd050e05f783eb06165dd32dadab95b24e0dd15c36bfbb5c610ccea3b35bfba6915e42a88319efe7333c5741f1840f915f5399d1f5721791159b2cad9e48b10d31c91c767025d1e7dfd209903baeea0ea70d3baab145ff710c980fd0c95371749fd018a99146d36bb6ad581346cb564770ac8c494d451a7929c3115c7cce94ba0217cef98422e80f1d8d47280c6930009715e2b8be6e53f691346d1fb577c31a6fd4bf0516ea0fdd26c6b955ab37f3d3825640689d979dc1b4de87ee0cf3cfd489270b6ed120b610aa2e47d70bd09c60936b2cc9f837eac6a17e954167a3dfe09840802e3d98da3ddd2e682bc19e6e37978ca47b5657f0b07f7bf06401ae48f18da64c4217e0ba0103233e55a38b72c22e7aaa469ca76ae00694318ceb84c14d743c5fad930d20e1fd583d49d8aa830503ea363371860f6d5484704ef51ec18767f18b4e8b7ffb3bfa85ca140ec513be52324bd4f03f3bd3cbcd90b95760230565326f6c800bce0eedf100deb1a52d84d92cfe869813b64e7889d5790a3da371a0c253bb50c89da5bf4febcfeb726a80cb08bb88bea46c6ca95d545a0eb0761e07c8dbd362a731b6febf1f5c49cb37539b7d770cc7a98ea1d0c97d59fa80afabc68a8234d4ad57b88a5413945c5ed187b407aa02b7c756a2f03589f9a0ac580093aafb68cb2d375a73bd61b0a1dfc2dd32db560073221194ba7bc3a6c69fc1c7f6aab58dbb28f344c50cf0455e1c5c00cba381b602effdb35a891827b03779ec34bf63a3bae1ba321ef015bd66cfc34ee875b3b9db1d2c71aed630f63518b02d22dd3e2071bc8f0d704e47984579cb989b3cbfaacd70a753ad17100b9b55eb93ba44668efa486e379c986e65ead7e7e836e8979401eca47ef133bc70f45287508977a1bbc28db7fc09140ec99bcd12e0dbe0248cd3b268857612ba9c492df80ff1118989e87d86d54ed1b02028689d45f22794f376b43e0328388bcb55f2220dd92d64666fb826aff58c2724da3c9ea8f604e66af80c1280fb22f08d86f92caaabeba80189d4d04a5478224c3f7fb93f5bd4e1c0222a0a32b6180e8dd9db29e160fa920edd3716191b12b0dc8525fb5b485454964ab5994b475bccf79bd7fefdcad5065743ab8200a929c18551a12a60217f74da9e9d832d230eb2b5499a1d468589b2f02e07fe574563590392a096ec8f5e0ebc18038d9d602a3f335c9ccf3945a70c5ab20858d65c6bbd910788a9882a4f137beecfd91ff1dddcf166c843cd258b0f7e64e7bacab8ac640a090f496e15e6564822af2422b0a8c6ce59831a058867747776202c9de54fe5f8fa36cf0095c387693ed8e4ff24c29be8f458fbe11eb4c431fffd6b614e94265d7893de3ef96e600eff0844d95da1f3ea79bb31c0c7842910c3ac7a76e784163c3f551e51b40c801a480c52d59b66675b7b2ba93f3a970ddf8bcdf80b462ef1df57c89815b1ef96f4f29fa56ec7bed532a5394c6a710ff781e0a8add0911e3bb780f2641518346087577543a213d057f9c9a5ca6865a0d3a3946696568382361ac8ffed72c164e111422df0f57a81ed290443bf129b7b2c10e63996786e7069de6b83d609d109604f13e39e1d14ad0bd88da8628bc606700424488346c28f5ad32271ce1cf0cc6e47685168ea45399038c842613232961f807fbe8f836a1a98e8ef71b394b8bd0ed63a466ad1cb18d68405c9bc79861a8f33be3d2f2be46f9bc83909e0331d1b0135867d277e06f8e06fc140039853daa7115d936db712e9b36cab38b25fd567f5169ee869f572ed25c65e65d18d2a86af731805775cf2ebe2f816be15f27962adf108cb783fb0fba46864e0146f5e2027b6fc3d5bc58d95062e412954bd321973a2ad703618da4709df39f933a6740e88b64fd269f9013e216e5d04cae134f4a9e01c78ae608f76ccc9028e766ad80529f0a3c6135d59843ace785f56c5039dd13d6ebb70e3229c33f9ebd3ab0d0803141c527889b9f3f06b5d6a09f3fc364c5b8f89213aad3a027775ac7f1e67094a8b32a6144394af1972a9cb276b691fa7ef8b0fa5245e4bb7c73e7d8b89b14406b1681de6c135b5528df1b1a8a292970ab21fa618d4ae3542c93f4f1a2ab6ff3c14ea511535b6c22e5ce92679bda6a00831e872c1c6911bf5f8745e3e0ea6039066dfe52dcfa31d6371fe2dadf01aaf4298a3c5dc471ded50891a0439f049c404a74d9df6587c5efec13cc98a6979554e6956945dc279b9d028c6f5a3c6e4bca9c2372f85edccb1813f702fadd6349c6a42c2eee4be9b802a693411426f0ada7a80f991b5dfe446d51b45b3464de2584f8d8ea8caa5bc8966db2b64a51b1b540964e539e62b5fffb3218bb5ab5ad479adf7ed77b604bc2f33e02ba2bafc35df6730fd8e577b8fe6435dfa9605fc05fade61f6db187dc070c8a88a232d95c699b554582b28dc5333445a143cde34b756a134a73d6ddc8a732d32caa47db2f6604a935289d748f9ecc321e7a6bec9ada2428d77ab32a2841d4544e5db7b06a0be402017f958c179275da670a3b6ffabc29c718e7be7f307d7a305e399a55a8eb91298126489117aaf9b426f5c80d203631cd073d33a0775ae09fbdfba6add639cc1d020b8c8b22c459501c199dadb246206299bb0e8052a7f0c4b09414817af559ad6170df0c405b25a9d9e60d62c6a4a44221da296929bd9671bc016a94167af3a5557e19a0862199eea33d2af7ac943e8683ddf6fbb8e8d6482a02ea6fdc50cf61b8fd2608c26ee45da9d0802f0d80e9d2537df15ca933a397e51255a3f30454fde77713cd9509a7ef63f6032da72d9173da4635863e1527ed0ca58ae0fa587639f497acd441cb0353db40c2e056a58abc83610e2ad441403f4fdd74b062268db01731da2cd5292949d95558f29957977b90d0fd5d1f09521f71bb05a86a082232e0ef321861943415ed0757f797822d52b2369b3e7895837327e46d3c7141850e4b6ce8dbd4ce32bc6c321465ad504d0dea456c177be2198150705c3ad2c2134f430ab6589a53cfc596284b5ea7ba8fa56dd5ecdaefb17fa8a6cc964c3341099779a72399c320905bb89eea3cfb5855ed884d3cd45d6436032173257b7462c73fa7f894af6437621f5c4fbd9bc763a60b3405f33cbc8a5f475b720f7feb7c0122b2e80bb1925754f554a366b475585a5e74ba55cb47ffb1d30c6cda4d66be61175f8d4c75830feb68ba4e2aa4ba60566a241226950bd9abd0fb5d9a6ee2e004a1c369b9a62aba0519ba15412ce06ad82b762092b114e549dc7a834f428d40a40f926d4f0643c373e9c0d8aed4093e7a0303d79894a3936017364241dd4d2b2796b7d2645824920ccbb7e12c2a41dd484cc6822f66a6b62567b367f15a6125c85c6e981f8867b3015d6ecabbc120bec61ffd0581a6f331a8ce131f6ba814ab41ca6eed29fa1f6212fc263c0bf232f6730c18092b91e3fff0d4985eac14153de80a7eabe76b5f04524d17d597fd6240cb4dcadc1394ceee5fd20308d0a84b5e9c3afc454396cb72c546a5f05a16cc457a0a5d2223e360fda9797ca0ca0faf1ddb36ba41a8cf00e18a641b8c2a6e2315de3ab9a5b49a541a61cc91da553c23f5eb1fda7247ce564b287c0252007a005eed325def91a7213f6541c1855f25cc1c4c4caa7acc62385ef8dc38ca1944506fd061ac3a001c21e9f427a5854e42815bf366d70a7e34d332445ef25e5a2d420a96dbfe22ac84c05f8e5c75cbec9b647bd340182338fcf60fb8d47a01b7de586688506f408a5af2206e631a4f72f7f1d64c4956fc13056619be510852433d480f9666978c90ae8ff8daddbe58b266e98cb72562ebf71d55dc7e3fb22ee119bdc4005e5942eec42d5215641e343607c043223b4105c2ad7d1f5e38b6722cd438c7d7b9cbac80cffaab4920195645ac9dc9f39b7bd1c76fedf811cd2b5c98d1ad8c2e97177a0d9f248507bb731163a198f42a47ea0cc3883a7691a13696327a246d4c609be87f71a88de0156c3e1d8d4ce2f2836cb15e260a12336c6c7aed6915a36acc29292b0e25197f89cc89f7ff70d18651ab1753488f491c488ab4688aeaae4c529cbb3a59927218cd8cec05e67a0c7ebcc4114da07bf3ff2280033f7b4fc12fe81308c72fc5ee2e72257884512189120c504315cd1cf9f37b2a42f642135deff4dee644e3fd8dacff694d80883d11b2a558cdd400b05003792e8872c48561985b6df5cca5f2dd89f88c87e42a86709fc70bcbf4a51b469f0ef7a5fd0f12491e084b15a2d80d189be604aa640782073f192b9654339def94c4c1f00c91aab70b078dc884228b7a7881bc70b3c0513f160a05e15b069a9c820d7117956c42fef9555ef16e8844dc4532b356d50441c2664aea614404c79a508506b5ab2944e21398c49f4094bbada4ea08dfb28c2c7bc8d916c9558f46e8bb313ced12054d4eac801103536bfdbbad811ac579d2020e9b447a075b79cb43c0c5a3d26866fa23f51767cae3d30cb5955abc2212dd342084f9866f9d017d13a5ab6195f605d5d26fea203fc240436aa260d274c11f131dbed9e3297b1cd1f2edc8ac02ad250ca258ae106d949a17c1abea2ede2f652f47d75abed166ff7b4c887c010dbd8ed169f108b1d7b9ca1cf85887c3eec6d1b0d492120f1e47119cd8910e6b8649ce5beda552b8fe42231d999b062093bb16e3cd62476a4567ff057865f7559a3985ff198de5591a660e3c62e43b3cd3fa0ec5bc9679476de868f5fc57f551888e52d398502d51e899850ea56380b3a92d0b615b5b7e0cc7ab44f64dc19959b381c1b59482a852d0c8b5a4deed0cdb70e07d00ee4ff44085d090fb5a0655fa6bddd61888a8ee9c4ab78e4c94e35b32be6256b6397be89d8b8fb934f72b512b34d370e2e9f544abb83acf6891f92dee91863bf83d0a3e8f0fcd0de454174c4f0bb5808444729461a643d72d6e0819443e732deff6dd0297f261ac405ffc52da9269c9e3a2e9421c67c57ceaa49206f7889328bc5cf5731840f376d11117a392526ea44bcff2632d3b1f18b9e6ccafdc0c31b9646cf815965beeb78134130e3591fb1a5b1090a17de88997392ddb0a03d40f9663140c00d02ad1b9fe57e92b17aa3d3768c83fcff1cf742f816158c1b275e444824e42ec01538ff88f383054dc1e5804f3109f31023dedafbcf162168667bfb481e71f852225f423031a6d36ba80224098fd986e82329d20a2f783f33903cb8a4e352dedc44a68736c4cc4f8ba7151c8cad0c73a1406bdb1fbe3b273bc706435f49e918df20e357f04c291e11c9314679c6b23d89a6e17ec4de6d615053c4649241aeb83cdbb743fff61d43cbc69d52d8147a955317cb8d0e33e144cb9a4a1e211e8ca08cc4f783f12b2d92fcd0dcb4c4066a897dc226a24648bd610b755a94040d8be3b4302caceae49718870c957c03ef958d8ebf1baa4ddbdf9b1d70f0cb466bde4870a63f85df7e00edfbb9b03f05ebb44d056dd47c4f4efb3b91ca9e1c047db0a60ca82a9f871a8fbfc955d112b13721b60dce602d7364559fc90e801ab83ee7a47df244ab925b3db1c5e5243ca1f1f05c5392f2e5bc4a54426cf4591d1d00215f7839d7d6219d54d24c15f53d83693924a4e83460b475913ccf45ea2722ec6d7f3b70499d56ad364a23fb47422f4be3b4cea362e98aa184757609dc71f6c6f1e027166acf1caf088f195580fcbb27bd6de2c34b0dbd36ce0248334fc4d4b568036bf792545782a934687bf69e266d41a6923332d21bf022ccc8042608137374194a26db34fe555c0666c384eb902df81b1cd361f17265e753ac7a4e4191e90fd78f57f0b2979e1d3bd0f1b2cd86a297156e39c7a7d8f55780594076ddf869c28d0b9290ee28db04743aebdd03d4d63f9f92f75c26163f2b91d5046ccd9971b5fe5127454045962dd74f83ecbc811d50b5eb124a037c0043c0fa01fe7d5c4e84a9c327ccb8d625919399005220f18466344c4181e1944f93c08107e939e5949d0f935d05d46a6b5a49865775c5efa9a7e51ffe57f9152034de0bd5e585d3d9cd1f6cb64a7d4826f0fa24e99e71f3b17c7c0a51896db1ca0e0ee14df2b82221cd7a14234f67857181de398b4f3673d50a9aabd70da062b80f7af3551aa6c20ad42c162024c8954d0f1ae9d156deaceb629da1d86bf86fb5bb85133a4387d0f43f3039dbbe9b4e37c20469ab462e463c23560c1ac752b63b018d94431da1fd1834089a1514eca8276ae00cc17640263ae2dff504a7d9f6d5461d55fde30d5b49383d917cc3ea023471044abc0fc15720da15a903caffdc312e4c1aee4ae5df6913fc702822f2629a1686b3bbcf55f1f9bf97c098c21960e638503b21da989f6a32f87bbdba32baf43b8149afab21c88fd8165548451cfb6dc9c6472b84ec5d42f6de52ca2465450c440cf30b2f2fa0e936592720681794f45310db3923f0dcf64d371898898bc9a25d062c8cf640d550355b7bb04158d88908279f49ca3e0a722ec0aa3db1e879e20408527e200594578c7c17efb91670f3965fde25bfe859499727c977d196d4b676f65d5e75dfc6e7bc48adea580f03f32cadb23d6cfaac77791b6fe3f2365cb4d536ac7779177dc3d244607477bb9be3b3bb5bb3e9c3f8a8b9bc32bb9351d97f79f25db20ac8cb93afb23ac88b56d57f79957dd6e7bcc8877997570581799787d1aa0af32aab55405e9ef5362fcfd2373cf66f5eb40dccbffc8bbe81d1392f17fa9d2e6bf6d543646896b43edbdb5d55ed03a8ea0f14f4bb9f9b7f324df7f469a73d8e062ddbaa2ed3add21ea8211a6322c3761366c2abbd76d671949a6e4326328c41032b21cd8d1b37c07c6af2c4649d7eea290834c61f36b34a2c8d6b011eb918bba03deabb28cca7ba74c8aaeb744bd15229a0ea7a793cbba576627b5644aa2755db305fa6827ce68bcca22b05bb649827f3cb9f6ede521d80bef73ebb88efce3b3551e22e1510f273bc8a530571f99877f998f73f0d55174bc5e920e4e778f2737c3932d98e826de758b0e36fdcc82afab567475651fd83cd8ebf516937f44d9deda83e3b7ebe5cbeac3d3bc82febec46ae3bf2ace4bb7c59653139c6238af91c312fc4bf72bc9d55d70e1d3237de259f6e95e49eccb3925c49fcdecb23131d30fe15e7318a0cebe5860d98183d4ef11d7a865345e4d03129f8345ff46a140a0accbbe7f22526bf1c61b44d62be68da7597ab564748535f870d5d3e3df1d5292828e7b96dc8f325803c5fad3c5f38f27cf1d8ae3c5f2c610eb519de875f2958dc256ae2d27f33a00d59e75f309ff39297178056d187791baf0202f3365e45751098b7f1ac7f795510d6bf3ceb5f5e9ea7f462561da07bfa9d56e97091ec22aef29f9c4d5b3816609867bd8ecf9123abeebb5025fdea1f6c747c8edf61be3c87bef1207ddbfe39af9f2fd6972ea443cf17cc971e94237bad02955eebeccca7ba6274bcb332ca56491b30cffa6f06a4e10c2f803cc27064940d79fa8e16cab684f51e2e79f919b2b2cebf5e1ed5e33df72828ee42f568dd33c2fc06987f79b7a151507c8c794bc75b22964645d9fe3df269269b0290672551b7ea32d9c81fb9c8631734c67f863c8e4334a7746272ca679ce2a4bf8e9ca255d295533079fa8e3215dbfe397ab67fab16b4e4e55f2f3a558493a9234e2a41e233439e3b479e2f1d3079be44de16f3dc2827dc355fa826f33543a36832344a09f92f6fe3f2304f7309e62505eb6e52a6cbc22a6961a6956df262b6c4304d37dac322a15dcc191bec934fe9746ab5063007404f3e271fdb5d0f6659b0ad0db67f775d38b2075008dc51da3dd78db7fb40e3ed3678a5606f83349ff6f7027f8094abdcd3a7096938af729a0004f01cc09ae9b6334bab7a24e38ac0a39dd19115e0d2743be22befc0c048567a0949eab1f44632994c0518cf00d30023316752b806d806f806788693c038c049b0129c03ac03bc64ce54125e02338199601ebc03cc833963bf1860b4e4710ca39471ca6491610c63b2ca9082a58048a14025a782c8a9582a059325c4640d91d2c92922264b05a915a48a48b120d582940b26cbdf25a7640598f133dec619cc3b13e3fdc731cc19d5fb8f52e64ccb9c49bdff48863933beff18c69c41bdff588639737a0e8839838239c3f2fea920e6cc4a6ccea8a460ce083167c0f74f0d31674673a6ce901ebf7f6a0573469462c19c11df3fd582940be68c67c474b9fcc9431c9c9ca4e9661a3ac15035daa30333867e1736fd185907034c16fd967c80c9a29fca0848c064d14765052c60b2e89b32901d183059f455b24d6e4010073c000211982cfaa52c81c9a22fe60950a0029345df255b80ab41b6eec74866ba9d824e0600f18e8df6187f688cffccc7b6111002e15ee47779c68f976740c1bd524846b25b2561be9a320e4d562ae66410ed31823163fcc5f1968ab96b9c55729ce2306f5530668ff1671c9a337656a68ea48ad83fa3941809005d32f79aa10e45e8ae3fa46e6e894b107fb9e89b9b17f933d481a4f3a24fb7ea0227497f4e1a280f93d9c7648db31fda438719431f0a9ecd298eb0fdaf074e3e5fdcbea0a62f36ed81b29950b6930f05c188038a773e719ce11e4c9b11814b3bc33b230e8674e38cc6f88908279f1311e8177b467b58d88c39dda6858db3aeeb3af1a3276e08d1cd5b51bc558ab1cb710bcf566e24f36c5cb5a82bbc8e2a40013520a7839348616b7366327197e956495bef49078a136cfb9075a27b15b541b1d9999d8d53eccc3403f794c20660b00b004fb7aeeb3a9310dea9e410b8b433a1195b78ab34928d37eb85ffc8c5ec81aad198fb23d9bf33d1e5ca1b75899262fb77250680379bb838c11a6003d26c620003c21ca4e0ba4bd3cd74f38204d92ab63f2704b6ef7b8c3294026e46bafc7873c923cd7728f1151d67a3929116c323de79ae9636e8c9f60fa2efc34b4b85059a33f4ed8c8ae94ad96c54a00215544411c534c22eed2c0001d8a59d613c8e7827359b2c18b65f8eb7207039def2a9d6e5d3131cd80078fa39d5bc9582f9caa7ba6cfc95824b16003370ba4d16fdf23401d0eab41e87c7e3f8d6cbbc4deb659e6a1b1e8f03c7f536de9f476ee55949d6dbe4d0aca7dac6a5a98dbf9171787ceb63320ecff738aecfe1fa1c6fe3fa1c3ac7c7fca76d787c4be3b4fec653205401c9f1316f93e3639e6a9bd6df703d8f2fe9cfe895e35bcfc3258190e6d3ad1c7988cc7701909ab605f08fb7ef133f51ab74b82f7e0ef7c52132fbfbbef4393be3a779ee0a84345833a1fd1e6fa4f7ff2a0096b2b76dadbae66bda822a501327b65b2624cda10087fa64d3a727bee280c0a5e9760acaf13a3e48eb77d8596bcc31be766455f72eafa29fe3735e3ab2aad3e403215fc7db90afe3e9eb70f91cfa06e65dfee6e5c9cfb1b44f5b25a34906c7fba384aa8b555d36de1f474ec1728a96ba91533279563266b5534c3c95aa62e5048ab1c94eca89242fc0a16eab53331b7984e1fbe5781b7f7ce5e30f5067291d7f2c975d489897121cd2755d376448486367371317a61bfe0078a560f701bc548032da594b6fb7db78b3345f29b1b36969b7eac23b1aef50f25de60c1d872a9964ce8c5e4c5612b3c7e842ea59c93b46419501a38450507c95eac61b0f1bbcc0e3d018c5577ec3c076a7baac1233db034c7ba2c7cec69bfdf11614deea6817821080570abe5f9e7c2e15b81c6f7366ae6e73e6c6f61f87aa8b4cc1c6a131ca789b2c32e01255c33b2924b60ab844d54e4135eb04680c693a7ada334cc0c4628325aac6720adafe7f0a3a3d492171d7e956c9d448e629279c89928d711d8d647887bc537039de54dc781ba30c798b56d738f3f4e9763a7d1ad4570abebb3cf99c7ce6cc13d3358e271f9f3d8ee5e986c457de8f23ded178e6ab31e072bce15b75994ca369348d37d3cd7433dd6460e011125f996e748e64a77001130ade294d378cc4741b4d37531498cc7843620a2750d8e578a351d5b8fc0ef3e5a283803f08c2119a788db35b75e119e0d36d6325700ff0ce102ec71bde59ed7224bb95e30ceb14e12d6e8b5ddad92e47b2233bde8281ffc888d96304a2313e67eef394e36dbcd9d9e8e1d9b64db6ff38ab2e97f71f69d5453e61fb732e59a692645e215b7200e0ee83217c8045155210c6c7f560641b6dd1538e866aadf5620147bdb7d61dd47bebbdb5de9a83da3da9dd93ee49addd937a6fbdf7de5a6badb5d61ab40447bdb5d624b7de5b6badb5de5b6fadb7de7a6f92244992d45a6b7d7299b838964872ad8039252e0e1cb70a97898b63892457c09c1217078e5bbb27f7dedb3db97549adb70b252a8e5a6badb5264972efbdf7de7befbdf7de25b5d6faa4d67aefcd42bd1547adf52649d23dc9c1bd3749ed9ed49aa426a93aa83507f7de5a9fd47a6b4d52ef1492e0a8b5d6fbe4de6a8227549d1d773943938e3b9d7ecc3738ae30cb82ec85e29adca89a50c2eb04251ebc4e8fca27f02b409164f42f044cbffbfa2921dbdef9d0a70d6daaa990af6cbea2b79116e9d2a316d501599f94fdd4a4dd88fa46a96c84c2aad0e735f1167dd11c9520f98e8ca644d9b1d14ba937f3900401536a39a7e267974ec5cfae51dc355f2efa2ac2573a3ad78e46509cbc968853beb6adba4f3728a33bbe4202c57a3b1e9299eff0e778b0b7e3cd7e7c95a2a2fb13e5bdfd9a783f1e144e7a4f7841a031347b4d7ce53fdaf371d036b26274051d41e9be527751df292195ea3afb5d17044afa4b7104167b511dfda150d09871f41151ea73454ff7c464f9fb4cd690af465bd8d3734c465d90eff0ef725785af8028a5e37f4538dd33dc68548291042fa0d7e32d7008fa2a4fd7947e6e1bfcdc8cff3e26e3c1ef559f02f053efc3bf19f9f432b2e9c7acf23132ea5599e55379e5bb67f998e9579e66554d1856f122fd47e74b86aa45c76229c881c16030d4c762a78fb17cf831d3c7569ef41abf89a927e40d5552a7923b22986816be543cdbd502a63a2d170bac027ac40a5c305bd5d509793ab4073d12869b87440c8e89c3b3754247c2d0091d51c360e9db9143878e56113a39421a4fe77bb013f2ae58626331bd2c66ab0bd3b3d11edd107abce884aacb33a21b0a3d1dcf46ea2139e12d8ed63c9b67f36cb4a7a3d121744bf4385103a72875ca11f907863ecbd16b3dfa79275c2d0dda83a31cbd42b89c40775f2d26eb0aa19a8c1bf32685e037faf2ca442f9bfb3af1d5fdc215eade3b6d830f1c687436541acc071c6cf071c3fe88d025a86bd4770b27ef94ba7fb0bbbc5ce0bbcbd975349d052b139576bf70853e4bbb5fb84295467b5c1f1a73bf405d23f0d901f5a13dacd52b1a5353e90ea82bc8f5c1d6dab9e9ce64113159feb24d67a002eac285ced50297b726946b34ab23546937215b569ad0bf6dffe0fb6a3159fefa0a4d96d53f0a307fa60da9bf5188e205aa592b0a693a5ddaddd57c75e9a84375a80ed5a15cbe4eae77ef05f21db27bc45ba8f7bfa8bf9f4befe5df2befa1b2ca7ba82cbe87caf83dd37ba8ccf21ecacb734f967f1685e073f9e6efb9fc231be640bfa43f7bdf3007fa35c4c1009c1735d8826dd3bc6f98030e389cb401b8298ce0826d533de7aaece613a408f251bb9c816daab4d26a51da28ac12db1251562dca1a45133190e20a922e2b2c4f610417425dbacd8b1a6cc10a5097fee3336e0855d062a44b8771225dfe668288235ca1736bb55aedcaaecffd69e22b306027b7e62dce8b1440c962fb5f206ffdfd02bef788afae177049a915b48607c09bfb1adeaf218dcfd53557c8095f6901d79ecae40a4d20aa336d480199a294ae4a545a5969da089526f435f1d6a557e85ee77207c5a66fa9d0a6fffd9c9c4cc27b5a2ba574f4d70bcccd68d00e1976496db520ec1aadd59ae7e11c32530936b0dce059eb398773c8f0c821c3e2790e8eeee590f1aae7799e67ade779de18bc1c329e37bbc025add580aa806d6e3667284d822a417b409fd83eabed38126f75b4466b2291d7d97a3b35198940dbffa32a2290659cc1f2b2a235a2d168f4930719ee192ce35ed9d108ff280f01370914fd8845fc72f4226d9379c8354023cd438de864fb8b4ebc457f24fa9128beef4f044e1cd28cae18b0ef72e5954536f8a35c827b2452f952f4a216a1f095581389a8f657c923300f01494f7a2f7d495f84c257624dfc5940f71ea05aacf96ae5e2cf04126ba213be9aa2022c6613645be5499ff82ab9a45be5a7838f1f5c21812b2b6011d3af90c0295e00838ade03f397ca11dd3eaa4aced9a33cb729b3e41bd2604db1f821185bd443c06ded1447628f4824016ff93ec96abc35f71440fc6cff0c788b520186cff6bff1d67c180c36c4ab9c516216c807b0dd330510407bd4e3ad52fcd93fde9a514839b2fd4528bc35bfd8242d76f72d282694de8634a5b0d401991868cec88c5a4c2bb6ff9c2a1d46ef5f432a8008bc62c074d3508711d538d04dbfc0e5fca9b1e1210cb87ee809a13f42d8e514aa69624f204ec9049a403520a8a91070f621f2b83bab108154fc74c4376994641843610cfd5821a80bb5054adaba2866cc164e41f899827e75d4c138eb2cec04c4e9eb462c469b37da50b31135a33d4e51cc183f05610a1c6c17fd373b19613b0f6a606a3667acb746fff2303762c69b85a16c36a7987812b3c728a331fe55f83881a289132f48b275fc8e2f4de0294889af681f299fb8f83e8bea413981f20143540f18beb8b83c7d97177d7393d33dcce774fa86a7fb9b179b978779187df392e3bdf742831242d9b081f9171bd6dbd03730cf8ac9e4dfc82edec76497bf91615e584fea59491b4ff58bb62f6426322be9b39236e4b3f40d85a16c9524dfe56dc877d1373c14a6862f0ff3e5bcd1a880c43cf936314fea1b1efa37e48d77d1372e7f83be90171e6aa68d9b4030d9873f77a9a8ad11c8575f8bf3de05e6471f98574519b6a7769878cb3f4a77fb36cf7d1712e6cb7ff972b5bbd1eede1361198cf110995dca9686ff07bb95cccdc524b99833733b8c07fb04c6f607a1d85d9dadfaddd339c37df73e67bcefde1bab0be6bbff6ed8f8f98a8979d607b9f136348e8dbf6143cf4adef8f93debe78b7e5a05c4c6b33e888d67691c1e9c59c9d2026d7f1b1eeb6dbc0d8d9343db1a4b4ff14b0b9be287127216cbc676b95c33bc916332fd785097eb79501efa86baa8ebe7abf52575e91ccf2353d94ef6c9eafe1b5158a06c29b0b3d94e302efcf48569c8a2c02463c6fc1e541350b51eda6394a19a405da830a8ace736595684415d56888e0b2bb4fd51578c322f5c37d1836a02aa7603bc67ec7336c0e578ab794ba543f7f7af2eef772927defa82463f3e3f7a350cb483f68f645bf80a07b7283752831ee680b2a176891aa2314519f737de4c43a629b71fa814bc6968dfd40575c13c9de19eb6b87ca9427e39e2c24565b6d332a2953fdac136ddecece4e3f2a7dae9e78a518a8b4b26ffa301b50594ed45b4fdc7b107d3f6f856075a7230f8d954ab6ace06a7f88a0e8dddd88d5dc7715c57b355e28db71aa82da06c3d281b6a0b3d3d3dd6258c5bbdd5db89763ad1aaeb640302ed71aac249a8c70a73c606db1f253b5161f64021a127211b5fa03da8ae61d018771d5818b4c7090834e6b4535d275a254fb49ddd3d15a65b477e79638f4a7833b8013165cc6454c68ce75ae08af188e8f73f4316bfc71177a17a043083c365c397a958005a3c50b5391362d53e8d7a94d1b857732dc0a31744d8346a781f3f36a131d565e3861d31df43db5ea66603c8910aaae14bffe3ab14cd57aec45718638d318704c6333c0280ad92317ece96d1f4a79f9345a586b633a0ff60b2aac64b56d5a851c3857c99cf79d5f8595daa955601b9f1326f73e365f40dcfea6f58baf4768d5f699b1b2ff332fac6c6dfd0b392353ee75543ab7ca0bbc69723100c0074b9dab5ba6c5aa5cb98169dd2e5eaffa4cb51afe812abe8508f461a60acc79aa747286adc9bcb54124364760dff5190955fde4686d991555d8e677df7ac1d6f43dfd8f81d7fc38ac9aaee6fa880e8f898b7d1f131df7d8cbee1e9b44dccdfd0372e1ff337e4ebf89cd7e7acbcdf467ed11afe9555f46f7cce6b67158579fa30307f43dfd87898bf61fdcbe7bc6ec82afa329ff31a401ec0df789b01fc8da77fe38697d1372e7fc3df903f80cf79d590c72996461ea300edb821a768291d3906908255726c4267930164f26fc82e5f439e9b469edbdb3d39380d7adcd17a3c22b3e1ac9787a1c1e81b9fbdbcf8cbe7331bd9652c977d3c728e77651dff75e0a7c35eab47a01e544f0fa9511e6aa8ba2ccce542922439fe90b9ba641faa24d5a36da892a828e0eef1ce8e47c444b96c019841d9743043d950357afa827e0270996ea3992262a68018c1a88da028954aa39a93ed5fdbfe2323747af43359421bd726ab24fee7e3248493cfe94445181414c47960c874b2ed0f994eec570fd8af2399af9ceb3a274e9cd89452eabd07bcaf5f80110c46b2f18bf1466347b27124a3e3f805758d3aa023190ac6850ea8eb44044a769d4611e134b4bb4005b1fdbf2b057f077242608bbf3c8948d77eea088a7c2702f6d677644609a1a0f80ad6a16c2821f7bcb18382b2a16cdeeaa00871d55a1bb357c0250a654309796b86010393e799b88e3a6909f9a4be39c16b1c123b5a4ba219e20082e964d75cf764754f338c93dd43b13b58f7e53884abf838d85ff9aaebbaaa6baedbb728a36cbe1a329d6caa51421a0505651b19319ac12889911209001390801d1850eb32ca890c8584f6404531631c15b4839a62b2fc4b940d1c30261967fe89361abbb433bb83fdc02b059f7679f2b95230dde5c9c70787341c112d13539a0ae9ab5668f6cd32c4a476569297da99ac91ec749b334e3392a970a89ba6689b8349c12127639a2aa9ebf4483659d7495569a97646e31332ce66ccacfe67baf53783373a82cb916ccbc0cef0ce911d0b601a429a314a0eb44648233e2704f69fdb621d781cf2d548d695b81b87c629db9fd210e250af134ef0ecb823f0f73b42e0fbd4d61aeb6d7cad8d230c699790efc3c79baf86720a96d219a374fae299b79cd81ee8be9c4e76fd5adf662fdf5c75399deceebd7bc7dbed36ab1d329deceffb349e658ce4082e310c7039de4cb4641bc9ba6b673a740c1ab70400b0ab51a3c60abcd9f64c5612936569b60733c6e2c0c5ce262b87b95153e0efa255c5305bdc5cad5ca6cb773f2e9355e916656bc464d517525df60594ac5f9510030eb0a581954d567d0f0ab8a430198579cbfbda7dadf55964f5598e784894ecfab36461b2ebdf3007cfbfb2883d42174736f8fe5a15640567fa0ed76511bbf2e0bb2e73543638672f59f98f8607700f94618b465a65b3f2a7df61e54fda6e912e5df49f2883ef796e961d5fd56739e2abfa251aac64188fbf2a88caaffc8ac6512d39fd7cadbccabb2ecd00fbf0951b846f3065dbcd423dd34d453b15c85baeefcd575585c30e2bafc2816eaa979cbe92378aafeaaf68150e74bb5eb2f22c6f3a99b20b55b2fe97fde6abfa2cb9d62a597f942b90afea1096ed2bda4b32c03e84c50eed3a6557216e739b2d033db86dda767d91901e6cbef2af37cf733d7228be7221df51ff09b8749bdbeabb90b7e6ae258b6c688ab7ca1ab4ebd7d261bbc24cb35d597a4eb5be0fcffb52d345c739434522514c95e3ae5960aa925dc7654ea36a0fd028f90c3ff8fc3fcf34a829f07d55f7f655f6559d9e9c5659adbd1c8e5e60ee7b6da640b817c759f72abe49a3d44052e0a991f84aef883d3e29f52cce7c45c5d9a68f64d317e1008b9f53eab9b4a2129166c59948732dd27c455f1fd1325fd19f89dac76d3808636b9aa63d6d48d3b6a679cb5ffc999cd843bb9f784a488387a4c0259edb6748e8e9f4d4f88687fc4b1c45846d8a6d1f977d8b4cbe52bab929faabaef843737f91be58137b7cc42622d01351888a556c2a9622e79ae6ad52d4e9f156a96b9a89b72811e22eb5139bfafeaad74af392aed3a2ac92f4a98844b6e98b3ade2ab15884b7fe9dda90b860972b4fecf971b126f6b88b3d1a083c82c55b43c05d3f0a4b44818921208d528673165f7d36cf5fd11002975ebdeed5ab7bdeaa7a09f54067b2fd47fa3e3ca7a3fe836fd2c82506a97bad34f8be0f3fe1188feb02f8ea87efa03fb38f3150faf43f1ba7ce838db77e789939796cfa75524da7ad56f1d13de79c3587b97da0659873cee94dd67c9a2f90afe63b0abe6f6277772f959c54dfdddd7d5ed29230887d5d7d7382d7ace409684e75b7a2e6923e9993fec4b09ce51e305910e042ed8a71d2f0230db3f53257bef211f5f2c3575e0590b9f2dff1e16127880795efeee0289cecd285029e404970b24b4ab3983323c0540bb387787a7f0a850a798b6b89e8146f85a42f3d7e7f47e22d9639634a62f6f8c08c81798b6e8fb98e17e1ad95f707431290de22747ca5f2200782be7db0b0b5b00fc562ae6c584c2bc8f020e3432f1518085a92bba436d18db089db467d445d7a98fa3c3149ff1deab34377a8cfed3acc83a3a8122811448e07a739e88ecf0cb24d2825bd5d77b1a862f18d07563b57c03929928e0a81dae6ca9fa3947e47bbef9b21a7cb1e3677f174b2bc214b222ce3b69de775f6e70ca59ca63e73a553970a9f33e4f68f4da02b86f60c5756421afb97e3388e2381e008bc3dfacf94e764814f617683cf223e8589375b7cff2f4c40dd22dd20f82ae10e3948f400e1c5114ae08942165e7392e28359cc25f82aff39c8029e7c3838374b06431a95ffba4c75445de66c71458f0fbee7adee415d0e01f7caabfcb792a90c08af7c4975ba497a3b0c75f0ea8af83e5c25a451d114832f0a7fcc0dea21e0ee5e14852576a7d241fcee456d3508c04d636038bdb9029fc2c0b2c8ee7eaec027650a93f9cadbe083210e76831aa4b10f0ca94e11b50966093185c18c38919086ea224ec0258cd2a82fe78da5a24013c7711c67e7f4992b1feda614095b38b2e774a1bc6db31675e2288aea80da00c5511d1a011e22407564e80ef77eaf87f7e9751ae2e09bfa38c125b5754e78eb8e366a9bd756951f93efa0c13769942a9e0f071e01ca7c401978047529c0e37c144047cfd37bc6ccc0e1392704ce073c0294dd98ac79a580ad053dcff3bc1ad2a8c4747c87ad02972ab039ab301a7cb14b90b64119a5a5b74530fc45f13654bf14a7d48cfeb360cdf7149471fb4ec187ea328501da6d8ee21b74b0b4384ec8adc32a41a8c07c25e3ab207ee890a9aed18fb41019194f68da8446cff33c6fe6719c8c8c016615442121cff36698fbcfa32bd85381c988429e27230ac9fca80293662425249acee6de67ce5021dc139c116c5c14368e0ace079439146f75a00c94813291c8ebaa8aac650a2ea8b9e087739742d623858c9603cf236c71db3f8fb085908eedaa09b3717b717f8330a5b4a70adbfecf16367defbbbf437386d66a5ff4f4afa779ca3bc4bd4cadf65f447510ee6f503e2fd1cfb04674a7807f972f23a3b3ebed64a2ecaa4b1efb3d1da3b229b8b0adbffd4ef394ff95e460de12e81154367564f408d98fda0fda6388fb717fd404a95122660c6c9780ae200a0926cb08098098e2c724455f65b49e6b8449fa03dd60fbdf1cbe8f8641872877c318b2344a941fb407a7a90f3d017551257c341a44b140f3e9c16d4ec5647910ede1564c7f8263b1448909da17b721afaff33a98bbee36033df817d40502280e02eaa261507288a33d38fd83c6380d83bae81113ca0fda83fa18a9e4097c6a1c154ea0c534525db40ab40bf409be4ff0dd25fd017f56bce76e0fbef27fcfc3d80375c7a3413448c4bddfe6cc4763c27330261010596cff9d1f1595fd955a75a96c90d49b4c2a8bc3f2a957eae7cb94f2db4a1ef7caa746a74194a6c47439106dce7c34b532d2754699a0652176403cd800e9316c37325d0a0e645d0a546792feb5569b7d7f33189102900731a4729b332edbdfa59461b65aa3f257e0f6bd0b29372fc4306bb8670d020973e50f7affddfc565d4e2b3d579100ea1f0f93c651a4a98d3e197d4983ae15dfad775612f77ef3dcdd7aa34c059014820eb4b2da14e809d0a68d3c3b695e04515f4d64c81412793a27eede7b4d469e60bbcb79428d2b2085820a39c1022efd5693982dea93faf9f2f75b9d0961aefc51cf1264e5531a27f52bcfa271786c52bff23863597758be14894e9b2c1bfd71a0ea42e9310ba701e9a07b054cb7d374826e3e7031471df760f6db64f9fe749d09c1b998e448975567675181b039183b68fbd31e26a22f29cd9798334d4ca759b1e9fb3b139307b3d595770b2118e9aa4ee8b46baf500bc8853838d0135cb78aeba8e4f9d2f946a23af3c06ccde93589b9f2f7ef4673d814a7824e0fbc27b0ebb8dff68f0785b7acdffce6378ebbf6620f40127e92ec1138e9103436c46c5117cc152d82ba60b63a24e6ca777c86c44c95123fd58738e1a7726478fcd01e90fa907054a9e7b1513de9472da54aa5542a6f8618e3839f8c19f3f579be6600f37c8d9c873dec75b8a685e28e7e0e7f864cfa2fb33c98577e869cfe41e595f0a766c82aada2da82dfd739d131d15e4f257b7ea8e703eaea6c41414f82bc28aacb93e2091d55793d73e6f660ba3c2b3c5bcfe57ee0feead0a35e1362c74447eb98f07a9a9862ab9c502ddd0e10e817bc9e19d309cd18352476bef8828c1a123b4834d109ed788bf608d5be6fa2e7fb6834d1d3c46c7953cc957b356f8a9d1a422b78794913277aaa2bc6433f2684f71202bfbe0f61b0b0f9aa4b3257feddcc57de2599adce0a7315e42bf756f7335756a83a39e1db943e7cfa61f8247d8343fa0f8707277c306796688f8a969ef441c217352914ffe606277cf0796cbe277d49abe87ce5f13d9b0de9bf9fa4ff3cdb9c79d9fede5075793ade577108d67ce51ca5770b5c7a7bc713aaae193b296f48c68b74643dd525eae9f1e1b407e4a4b34111f21d9d135c4de93d5b256f7dee6364f067e42ffc1999f45ea69b8634f563e4f99aa1ea3e2d68c21c0b9ff4decd5b244fc88be2d9ba9eeaa2de4e0fddd1a13b510449417ac2f60fa550d3a9713a9e77cbde1534f685edcf05cfe6e9cc90866e1959fc3197be7e300f76a1fdee9a7eccf335ca22a0bb66fc52f9f4aaacf2a8ccf2a6bcf257fcd27bb1952ffd5579aa0efc7e54f9588ce563e2871f5bf958e949dfe51af04b2b2a22cb49e5553a753bde373f4c831aa903c3b004729d079248218dada9b0b0b0ac5c2c5868c7719dad20bddefd530ecf366f9e14def574bc1a8df11878322f46ebd5bc187852d45a5a5a5a62e8e87845501a5c4b778648e0871fe47b92c621fdf7a1c6e1b12169554df82a559027e9c709358914861f3e4ee9493f5fe0cf9727e40d8974c0124956caf3853d995784902d949d10dda1410dfc1933cc906738ed9f864943ad068d1a6a5879badba28690a613d2f9a9a29f1e5b8f6d62d103604477baa01d9d1d1def87f6fcec04edec04d560be18d0060813dab71178741a65c264466595c49bd35489afb610b7daf31f0b856fd2701effbb05f6f1c3c3781cffe70fde1ca5f4c8a679ffcc1972d3f731672a15261534abe6e7eca94bfac37dfb5ca63a99ca9c1e3182f1887f5c594ce9cafaeafa25c237694ccefbbefb20a3e7340e87c363c3fde871baff9ecbaa9aaee3381d5cbb8d531cc21aefbf8969a535b1cf27ada4f7baacaadfbde88370ef691cefb917691c1e1befb9c7117df7f3653197bfdc55d2f6a033e0ab7973b743cc39e7c8a3ba7c4cf28723c05b6ee3adeeb197e704e78aaee8fc9179bcadf68108b87db8f170b8b19ef7ba7e8da0b63e0264bce25aa9189cd86c50f81a90f3b23f6990f365d35fcd1952398d500600bc4159469ee92bfad2a924a51f05298f4d67edfe0cd2a92e22744887ba1669538b4ac01cb34f439a158c8cef58dd80efc32be8f5cbe06be6a3baf48cba4f9a386b913615996cfa53d4a92e8c278b07a2acaa198d5441447fb3c0a0afc4a9aacf63d63a5fc71537250081c9a66fc30f6f4d253851c5a66f23841af1f2233c810bfb93616197d32748936c1ace9f61cd7d1fbeca467c453fc6fdf5be63c07cd917757c45657cd454528877f37cd95525a928d36211bea27fa1e0f9a5a823f68872aa834ef81efee5a97ec9ed5935114a72cfe94963acbed1dc6d6a6f34179998d274f08dc81ba9ae4a7f388efb0e0b4ca47b82a7b5deeabcd5651f627f36a42989b4cde86fc01f85aa9a12f83314fd8dfdd283fa464829abba37f21a6555a76b7ac880afb8cfc32ee321f657c12e8f86d89f05bbec3afbbb6097bded0db1bfce2389bafd897207fee82100fe48ab3c087caff2b40ac8e84b6f33fa92bee1f1fe8614ea9bb0cbd78703d0ed75998a322722429b6451cde87798af91c6e1f11ec7fe48e3d42f751bbf8d7dacb177f77bdaa67ee9b96c4487eec0406e721cc7d91fbd8dfd91567536f5559d1e7dc5fd7c7d25b98771443811c8fa280b4f24ca73e29a63bd2ea167dbf7e6cbcaa8f0a82f7e107f152d6a92107fafc6d6ff1e5f2b8084b88e51a0236cb03d6d6dd59f166d5babfb93340e0f0efefa20dda55f797b9f747b00aae101c87bd17f231fbeb205f831ba5be01a9999cc4e4bcaf4534886ea6bc1334cb9f438affc27535d250f999a4eaf469a06d0d758fb170a56d28d443948059283f8933e991afb9f3f8ef8f5491947e57df4dd5f19d9f43332fe1932cf9d417565905ec607097f860c8dc363a3f2e1e3883540dfdff7e19e4a56895e7c153ac318affa11e9ab98559e0e5536e1fb9f3e353e299b3eccf86fae897559355f748f1f8ba13e66fad2c74e1fc3bf9203c3a731539d92092563a9e1a1034886c64a8f1d14556e0d0fe1bce90092e911397ef2b32d4e8cf7affe3832be5e1931ded23dc3884d884c0706dabca7b112d24375893d3c6829fce5bccd21962f258e3caa8bd27a7e78fc08c1a0cd1392811e6cf63d1e42ec7b33324fe93f0ae20cbe88c36313e3c3c791f1a45fc938313efc61f42ad18cab05a63c4ca01a1b6f91b69d51b6751ed5f595b46f7fbe7f783db0f49d10db7dd17d1158fa19aabe25487dd207f10fff2b65d5fdd2e7bcc0acbafaef7df1677cceeb9b53bcaffa78dcefbb6fa4bac69b901eec0f29e22d5a04ddb1b8f6d8b611829062ff1b5d1e36a880e007df068f7e86be91a155f7eb48db885fd23731b44aa481d427bdea6a9bfa24958d7ff8fee18baa7cfa9654463d29b37c9857dec358248558fc4f5c31a132ddc12ca7998a904ada985883ad904ada2fe51e8c80301f36d878cc97ff782e4661e3ea319e499f93992cdc7576d695adef96d357db256a7deb796b522c6e1f8ebd0ad3fa0df6eb4f276b6671d2ead5585def37e8bc0e77d9e77ee5ad1f7e28e2ed2218c65df5b9ff6eb7c47e1177591de3e95e08ef659ffb9a6342ac5ef575c697d5e1f1b22f84f7aa5fce299ba3b1ec6d9be9ab4efba40f523fac4faa8f437afbd375382ae6ccfd70462925899694de87833edcb3bb97741127b9f7e14438d9884699f4741e0ddf88a04c628b8ae584c22631a71ee7fc1f913944640ed19d2332254470e795b28f4a725f0077f1f890f50f2a91e6febbe00d699efe704fa1f016c95b1cc711f13047e33c6ac40b680c4772a48c0bd853f967a812adbc0fa7544c16f734d327d018ee6ba65538f9c4498fa9bee53daf89e1176b6250f06681f3d35025d235a595cf59c848a5632cdfa26b622d9fa9cad7d4889d1e7fce319617a70834675e9e06712ff315478ff88adbf11ddcc7784622ee2950cb7393e338db737468d3f84fe7f95265554e7d3b95e49e9b6dee695ccfe6449ad6469a42c151205f71df71404a462da2963cca3ef315f7a2ef450fd648bdcad3f0fb2e83ba86aafb1a7ebe72ce5fe383fcd7f05fe31f8764cb948a49725f691636f7940a9beb389a0eebf938370b9854b9ef9880bbfd83dd34a351a6f134fca75540489f3f48f8ff5e75d5f8d1e83f2e5401c1188be28f728db7297d0d7dc3f3fd8db659f91af44df82b3fcae5b46d8e4696f134e4183f431e5fe7199fb3ea3fb77c27e3551f8bf12d2fcaaa9ad4ef305fa90fa2f2a221e7f912e5b9df1b7fc6c762323ea6fad4c7627cace55566c83495961833522a1963a63d94090d3ff735d5253ef7dc0ba9ae95af419d9e05afac882c990eb94ee964e24e3807e6aaee4b6f3fcc2a20a5b7afeab44d49db277d7d950de9ebe30f4b8fa3ea3efcf9c21fa4f4e28b1a87a449a16679168d237e58b9a74195e4be94a95025b90f331daa24c73d7e92c33647812ac919f115f79d2ee22b8ee3fea360cc7e7d959eceff094948c18f18b68055ebd5da4bc5376994a2ddf62b0f9e2f95e8553674c7f839f16dcebccc99b9536f7f56ec2bdbe9e0e13becc778aef7b26fa4ba4c25fdf951496badb57496e74b866ae54775511ffbe504fa6127d0b6442a693f06115fd9b7457c655f887f19f11d3728684d786b3c8d0019397c53069f85f431622d9ffa6187f98ab1bcea55a29637e5d453704e574c089957f83af8557f3e385dfef3e9ec31026f04e3a67a49fdb2eef99e4127ed7bd5daffdc7bf0969d362196b4edb66fc4d2d8b6f6e98fea92a9a4b5323232a3d4aa8592f65d6f5b63dba723177c5089744d4bea3b1f26cbfe10c6188808613aa223fa9f0d55352d2da9f7b0fbd1f82c1f43bd4a87c9b22fca3cf8ca7ecd06a0a4fd704a61dbf9846d7f4c96fd8e09f8b6bce979487d8cbf35323e9571fcc16fc938a96fb1446298ae16300f1d32db7e2af388be25e3d40fbd6d7a96b7313d4b7dd35f6dc3f22a7de3cff23edcd492c34f65b07ef836f5c307f73766f15119ff4afefe9455de944bcf924931fca4efc09a582c05aff2b198f8b1d2871fc31f233df83f7c65df4825ed93ece34c772a695f25535a25ed877956fb25d9aef6bf6cc58f48256d8d4680afacfd0b05d3968b454dacbebf5dc9e95cc4c29cf327497f7e97e7dc56cb4c16fded0391fddba98c45f27345e36560f60faf30db61bc451f570a2bc313f3e6c52ebd09645de0eeeeeeeeeeeeee4eab53777fbf5ee9cf09f3fe5d9eaf6b5f5e9e93f436ad3586073671842e940c1d1952423fead5eec07dd22869dc7611f7fb940a5d283ee4ab28be1251cfa3eeee2fca4358b6fb9502f6ec1b0809d894c13e02e79c09f77c1b181a43707769c8e60c6b572bb3b2ea3a9db4ebef9ba12897d63d91f7448a18b9b1ee5e9edb661f8cecfb9e4bbcbb5de66c1f3ad59293873414bbff98db7511dff776f7612bfa042bbfa28358f96f257f34d4309eae7bd743eabece8d328b8ce5887b222eaa4dd727a25c833c5728373cfa2d5ecc748f234c248295456e15f2d5e32ae4abfac457b5d62260faa5f730870a458bc0d0e32dd2d76771c25bf4ebb3f8788b646595ac6fefb8410a529086b2ea1ae9d2e37489455a36ca55a86ee1ab89bdff4a212c477cb5e33bea8bbec0a2c4577526f2022e59642c4cbc555de3577bbf7e75a44ddabb65ee7dd41ff38f216030c9b951f9c710484c726eb1c43817b99bf435a8ba58645be0e9ab591b0206bba4cb0a65d72f2181555e0c23164bd1f69ed6f73c7fd274fd03dde5b8a92e723729d463a5c3a8742c1557554f32866604000480007314002020100a8743e201894822489aee0314000c7d9c5470549e89a320c77114840c42c620620821003020223233630100abdbe60593963e1b286c0b0eea850dc4cc3b210962eaf75b63a5a6258c86e03bb094183f3ed14dbce4964bd23e1649e2c5e8d158a6f10268b68b7f3b3f81f93e17a62112a0e20180c8aa1cb92c8cb8c43c20f45ee6f3ab956727fd6a0ec581e4129fe20f006dacd6ef84ca5942278f3c6a1963e3649332d8d2575bf01e9281cc780b2391e8afa1721ffa8c026af3201a129c211651b71b06e5711477515fd05203756de098195e34154c17a7773c4678e2c49bbd08b83547580600142018b87c642b307d4fdf1e720aaa2d1bd583cd4895cc4056d0db2a06339bfb43f62c9c3152d6cfee3dcf0aa09f87c295ff6a3fa353b68cd7bb62c38ed206a037ad163bb7b28398358daebc7b6163b20930294e82841e40238a2e63f03c3ea4c5175407344c8b604564869ce4640539fe1238d2422c678bfc2fc22bd08459af01a044b98bdaa0eee8b091449b5c23fee7259d4b9f5188f45c231335f5aa5c63272b81a24a6e136d7560a06f04820a54dde1b2b55f9ab694037e966ea92f9231b46726ea31107ff2f759ccb79b060a44cb4a31b50794fe38810647159105333b538395858c7845be61172548ee776d74577f487406e11c6684f75adb8fcdac0e45944a91a17a2a996fd10d2edffd6e0c7fbf2d30ab014c92fa4b1b1d86590243809d417ecc82be97e13588750c3679de8a451d4525c053f4a6d0a2039fd30249ddc2e511a93431a99ef007da3445470518fd5f6654712cb5f03a039e2f41affa1623197ab2051e9395d700f63e11f34679579cd4e4cc3822063c9208c01cff549d55fdb50c262c00a7a5e92e0f00b611809c1600a40a00171b833b447da7ff9e378275327b571dc6cfa73d009b2aabaf2f32a7ccb345d8b4e2e5994b8d3b42baff35ddc73f3624cd9fc00a339e9f0e2f939067fb99c83556848a2f071cefaef489418b36c3b85c20a1d7cdf87a9fae7a70f498d1fecfe2aa0311395c68b862adfd0f6ed9b919bcc18ba3107379a8a84b4c06020ca0e0e22e230d05f024d56fbb73e53a2de3f8ed4014a95c36dda28830b278766e575a1c8daae9701ba1152ad02a4bdf4c2a525db6f05efe434019bf06cc30c22c1474f6b06723ec3b2db76420013e18fa48d8156813a62e081628a6846acc3c8e9af165ab4645199f49cb0a0b6393460fac0db6434396a8e2716c890d1622de3c2a6a982a1e6a27403265d6513b7d6295ac98cdd23f9b7d1fcbd5ae07b07396a7d3cd26c70e1a84d9c875132df42b9cc6ae8d2bff019a97a78648c641b15a862f08d4c795b8fdfb7ded58c6bb1db8e39a9d94392784853efb47e5b695861b0819f964b842c0b15888c09d94abd7dd5aec51064f5fd64d7948c08406c31dd196f62b541a31546da8572bf05428ecf51e300e71a3ff53d243bbc05a4eddb45cda3504e45a52539ae724c381dfd5db13e51005ee8de267d9b3842a6c6b2c378302cf56b254c040473e52eb7d5e49ad65307e1335f8e407a1ec86beedb467955e95ab352367dd3d69582f8905ae7215768bc25d50c77a8a9e3498932a27269ae452d6cc37cffdc538e701b7ca8fb4feca4098ad44a0ba5e2fbbbaa59433e6b5c961896b19e5ce0eb16b4fc6f641031853c7be06f5a7e255d7a211339244250c74ce1ca51bba434d74374fe9f7383f40baf9f0cecc9f188b091ed37379a43190c1799a5bfa259b16d39477dafaed330644e880b66d07d178bd0d45731ec0c9d2797122ca216e931d12053b5bfdad62ffbf7accc088988976fe93afa8a18e7bc25cc88a10a66b6e79e921e066fb9327073179094e2da88c515eed17f85a0317601fbd8d6c38792937a5eca3389e1102e84d7d3bbaa7682205eddd9f3ec6ee3eeb2c8f5777aa16bb027ffefca1410442b95700e4c68fdfb4635d89a0294b8c7a0dbd0a102efe34490627cc6df954459c777bdbfc7fd2f9aab495de757d672dad447dfa83f08ddf9524b8e0889028aa3a8b8184ca25975624b83f1ed7e5aaeae381c45adb87376558fe39a295d38c7a1b4f085de9f5d093b3b79928fb556261c4925ad094cb40ae258e98722a6cc4b7da8c43e0a19111f73d6d95cc94038586ebc3809f68a6f6cc4476b91605cb7f0c673e7f632d6c85a9b1124c8d31bdf7a9698804720bda008f02b8389dcdbe998ebd8115af202d0fc589df0caddb3c096c3e07e02ee7208e91ff9dbad4241f940d3b7858e58903804d594173ed5ed8adc2714783e381811b5b7eed8628b5492c2b2a35441dceb38beb4098aa0b2e4254f1086057cb57a125fde81e280ef9a8a71c1fb2f14571fbddfc01c6f40a34d60fb233055c71d0cfc5e3a36726ac33fa9d34bc38bd5c5152dc447570c8eb26c89bd68da263733900c9d842875120276aabf873807a1439b43f1bf8e9164d92a1f020a5121cbc6b7ddb60c7229989ee88f7768764492b021e86059f43ab365b530b6bd4f821a4686ef333793347db5c796f34b5ca021ceac3dadd0b0ce303505bf4fbded6eba67fe47947b9368e4f72ccabad1022ba3651d3e87343b40c5c164a0b45cb4cb2c6922c32fc33cddf939e7af1174d7cadb2b86e32e1f782bffb33c768e71f43a0a6552c04236190ca6ca0e4ce0c4b7e436be087977fcaf1e68a7e63b555faa8492dfde3cb3a074d53f1c8d712a6431d5a65b0cd9df2e2c05f1a9368176c9e11fccbdce4b0f653dd74e8f3d0cebf218949038e095a2d53852ae79537321f9e2227b7ce391d490cd948b113a1ab216cde9f8ad5a94efdedb8b1be80153609cc05501880121816385272d2c59f2c635299122adcbb6116cc40ae63a8aed27dd7da66fc8157c7ea377310c914d6d646d0dd3c6e0951a07a15bd410f24e852c82896b5ed14e3b09fc51643749ceb92a4567b93a81b1fe59b4363f5195f1433c6b0e97c56c56d4b324f1cd65b056378d293c6dc929514d272ca98596d6597973867f7636e5dcd9f17e3b1751f2b96d5abf617fdf631b381d892eab07097ef605c92f35378aed6693486a4c7ac1e85fa83fed3af38d230ab041499b9854978d778217371a1dae54f8e0c621b04d8cebee1e387e138804062ded057b375ab8d673aecb3d095f1295db4911b1845be48704eea8e5d93d257b2460d80b9039c6190844882b3de4eb320a459e069a13cfa88dd6c5c15368085398cad734bb81793d2b8a69a716c71f6ad9b75e1fc5afa89cdd8554345a368b6204a0c578a957149f0bfbb5cd7e12823b3163b77e839d5d8af3b66fd72ba707e65cbe86bc95beb47aec33becb8e929e0f8b6eda50e4791748f301a76d49833cb3f82862ceda999614035f7daa11773fd3c0b33dbdfebf4921dc84f7e456a4625ecc8e0cbeecb8d8409279d56edcc9b3806dd7bfa3063b30207fdceca64d4534b83c6b82d95a9b35a489a67b655be04bc4a9b34601a64abe1b59d27efd05c5dc2b4ea08ef64969ca717dbc77d166ded22c078da35c86f52f2bf9a96da2debdd98295c0bfe200559dfebde2c0a287346ad496fefdc745617faf9a827617bdf0e6c41b86c04a1b980796855d199e66cbfae91ab03f1ba6b8fed62365367b3888ddb9b8df1aeb9ea1757397ab419b5f4e4dcde6bb0c4e3eee0448864120b83379b1e640f8e6bddf8e1d2a4988677dc808c95039f154926b878656c60f1e76dbbe6b4db730b478afee651fb4b03a6bfe35b592eeec3266f94b9ed8211aa1a5b38db91c4de42259906f9387cdded3b29fe39622f5c1f38dac51de1b1b2458b7308cdcb5209ffc1a73d9b1a9549de9285366e4dbada238811090657e40d26174df47981b8b1a9f220d680f6c326ddcc4204c7f0cd8eed22b82efc61c6c21162c68fe1f56a4eba3c4489fa59ad8a0735fa10af82c4a172a7b2f776e4e9aedd201e0b09a9f06b6811b414ce5ecdc51301bb1a786b752bae1e509829002544466a13b6c985796d2dd9737bc91bfdbcdf17504b463a6d168d7b0865867a54364bd32a56b15c36862c4e72f4ca8c45ca45fc9d689091697592eef806c1b5cf50824d331bf8e5a1fb70ea9b1ac15ab05184e8c1e076adc9d5256e69280fd86a1a06f8e724a5790685c204498f47e8cb2086b5d339c12c39e2bd2e0235c083a5c2f4ab4be06e7ad8b0773c94c6d54061448b9722cbb3c06b007a2a2ce240e550af632d4486aa52b3552346a714b6bc251011a47673c74a06bcf2daa52463d466ac4aa53ec99c85874c88d180370b738b5d538019f9162bd2d1a7c72d684d58b7c9b971e1c3511c61837f9bd12e18132125810a31012b62962f957a4706ef9dfd88bedadf708fb9a90692a232c22fa0efef23fedddfc3de38e938843ee8a476ff7c9ac93f6e8ca9d04467bcabf5e022488335a97731b09eee8bbfed054ee9f256e9a1730957127bc3087e08066e4f9f5174fee578c1286273b93d4066b96f7fd3699a280dbf57eddab6a5a0d8e985e778f5b62e2b0a4bb70f2bbac8bb434e854573b8914aa8fdcba7bf5a9660201e72343d29d9f08b807eaa801048adfc688f66bcf4ee207526b5238fcaf7ad8adbae5477227737d0621911b2800a4c5c1ae82b8753078cf53da68924bab8bac97165e8c5bd5ea94cda4262050e0a1620bdf0248b54b6b4aca7ce132a88776fc7fd4e7871543a5fc41f6c48760ca4a3bd2f56ea3e41f8a34a795411d2cc6dd1a9d6cc3b5e2b898aba7e5633c2ae0e51f1b7947fa4e4975d3d17aefc9cee86eadd8144fb4877798d6d425b2c64fd4ace74e2b1826b24187c09d247dfbce6c53ea45ec3d8f5ed95994632ff17ceab0998b041662844ef688ceb6bff2a4e4589d875fc7d9d4efc067c425cda8c2a319122d48dc47393761062fa0cfbcb7d1428cc222913b979437b1596f08960fbdabb6500b4e85f3efd5a139de78039d252c1e7ada44a9d5b7278b2d40298542526cc37a4d5f7ca54bf169892dec94642d91a04b02b720e203485377e321d7a8a7886d015dc98ed77c169e6cbf4c83f98812072c34046434a17b9c84105d991d1f947728a92ac3ffb9bc44ffb94048b2ef8023f7dd314263e3a85f862e33291534e401ccbe0becc9c47465b9f872f7511d092a63627de27d0f632d73597ec969e671937fa78603b30aff253bbabf8cb08ab9c441ad9a07a7119fce8b4bf61a5294cd91fbf8cc01ec927b55762bc09bc7fc36e3859da224939c497502b5349cd3dbcb80cc9e8e3dcf3298c490736b779706231523ef41796fcb91f9643b867ae661b2b455113833d41ddc4708e0e8d3d9b9a755b4d6095ba8ccc2182cb13d8262309d7f47d10a23509b5f2db87580596578e87956e445bb8dc8a31c8606a4c3932f4f50dcf4af51c9ce39c31acfea1c11a7e0688304b0ce4ac0b7be33b07f90dff187733ccd164079c7202f3491173ec4f2daae9b7251311a46e2231f225de94431473d99c688eda42b9d3d5e7502c28ff3b100b98d1caef74326459d2064f6e7c090bebc6ecde61086bcedbd2f254a6fb7b1bc469be052164e980428b586d49e482d79efb94c39dafda37085604647621f8a972ecf920e8aa6686ca0b2b76b11cb2d841cf5560d46452a5e4d156555ff596e3d5bdcd4a1849b69491beb2cc7d24e6f8f55e3ebbcb1c0dcbcc3119076aa754a3ddee6ff053ec1b08539f6d2ba4cbef0c2588b7a3f5a3edcba6f5e721e16037d0726ea9758e72257fb79487e21faddeaa630a89f53b51dd6b18c54eb359f809f0cd553e2788681e48b1e95a2423c275e84c18c66144bff8abc2b6aef6970c7271e2be3bc2289be0b7f381935c7ef21d64e99162e57bf7639be62904e673c3e94a697c8a1cc706ca51d47454d5d4f0d139fa95a6a322634b589151463a1f3320225f5e4fb843c02a4a712be3bdd65459d560e9bdb66101d992aecb240a740e5703ccabc6de8e037705917e7722ae4ca42e5db199933515572a1d50f00908930d462d094fc8a1e0dcc9b63e4b1423835c3eeca7fc1fe380684c44aa709999e46b78acca0f87789fda2221032f2719245dfc4b59240e05ddee0f508175132ea9417e2d58285479be7513c8477ae99842ff8762984964c1525df76f94fa478afd9806a67352e4636c1da58e235a67efc638908622e1b57d4dffd9ef3e98ccc7c79a57e8053401b6c34854c85305223ba3b60b744071d054009c31938e611a65f465496717a6b782eecbe7302c8b5b5db4c2b4d911f812762ec6dbcb768cdabb071f17cd34f33b08e60d7a9c6394d851dc0a6d3c0526bc41c770c2e8615c562abcc5ef2745e89305fc31fd6aca05f4fd65e5c3d6df8ffb2bcdf0972e44b7b30174d9e74b80df5fa52e757e24c29148e62623f34c5e3d74991d79f9e34d0986c95893cfcaa24c940ed7bd2fbd0b58bf65233a670a90d607058e815df9fe4f4ccbf736b898ef6f248ee5331061c7a1a7e329f3d39795e1f0fc0890826ffade67e7d1fb9584c5fa4f6e713c5647f384f9393704603dc6e05fd59870da7130c70f81147f126ca7bec9b5d07fbadafef3184eb48962b49eac3f9b7fd1022a9362937badacd5eb78d3d95c281552d8da5bad7757c9f1e83e8edced7867f093af7d35f2a259926c673879157866d430a388f685b7459bac2401dfc69a26c35c7c766f42013dc52108144ef55f3ad6b056b183879eb343225bcd38d6d59cd8bde4d50870064da4f64162edd79b25b80c184d4b755bd0a9506825b970513d153ccf36f365463df87af02c5dbcd35484694a4902123cfd965eef0938d8716de170c4ae14fb4524c8647706e8249b6b861721d2f0ee2a1e5c53c0c83780e6826132cd4e18ef842a08259d3e96145d86b9e20970b7bb2434cbd1e08f14955fc9bc78306ef6b4853513b170a6f967eecc2be31270ba90b1ac1ff5c8d0ef4ff9d072c412554e8817ab1eec6d5e1a024606a93c8b15df0fb23265e4d360b57f86bf8705ba87107198a748a6a74415e892bc021196e261becf802a0d9a43b89f155d939d237b9e2f61e3a0464f723a3baf6917a34425c47e9aed6d0e58040ebc8a0559e4624d12ca4364f11a72535509b04ae6345ab512d5cb68e9fe551bb48c6576ea817295192d6f4d22481338fb16539bf5675426d69e6524cf0153d119ab9060a5fee104f8f506c2154ca29fc88efe13261156facba9a5d537d16d9110dcb61ebd12c16d01ffeb28c4b0018e8d12d3245585579c69e33d4a23cda4b6ef21689543c1caabe0ff5a8f0304b0c1bb5f7a24c1be6f6b68aeac612884254049ec5fa49d3090c8d366bed098160322b04bb72a0573466f16f6029af7bb1ba4a20c6e05d125b55a5e5f17ba39ebb415550066c76ec9f3a31e81a58b24e12af9aedb314203267afe9e4bd3f845756d2cd574933e2ef2d654f707eb261473dce5bdb2725489e0101348bb8d5bf1589a92eab2384cba549ee7b8ae8c23bfde25eeb56d35f3144a16dc489d6085d25583bdf73f0b2139205b00e373599dfc79f6fbb0b9502b5ef9323344f6954c5db485ffd934cbd32604c982a7d86e4614af25a1da7f87c4ce3e056a39f656385f75894fd7b9ffd832e54be3d04457941212f14c8dd3020cbe48dc1aee7ce8fc4287dca07993dca75f4013be0ceda7d02bc8d71458494e92ea10e73c0b97b0240e151b326eb10e1deb7a11ebdce3b7f0ef38597dbcc2cf21faedd17cd9fd9ba54d73ceec0618cb401fb22b3f846ba16e3f990d4e9b1091e818ddf0fe3f33fc7eccd6b8a39e3710a877b96ba48021e051bad91ad4476d5feacc9fa07bf6e202030b2497f2c619cc44996b02050def4b16e9a664ac62bb65fa23de912c72ab1b09d24353b97a8930ea72468d4251c6feb4598d0ff225a918090096f0f69af95e9cd58c29ec7cf56591897b0bcd54a5620406211c9d4dab238bc9b6dd83b04368105e536ac42995ab562608629308d223d71741b66584403af4bb55131c58ea05105bb59aeac7dd87c979f4dabc4abe8d6720abea85c185a90b41746d59b7402dbca1efca355645e180b1330c51792d717a3b958bb6e842a2d6dad6bd4bd7777b817c09f30acca7f9654b4ed074bccff34964cb21aab13b2230c7c589045f42766948123f4b1df223ea5779c355311fb4fd18cc27cd05699cd78d5867e8638eee685f94a6abc1728b2752610e1c5f48fffb2616753ad3a87883b075830d322bc8af27f08df8000be0b0886b76d53f67c48735b8f4244cc944f94b0d9ad1f89557d505b30643f5f2fcd53d20b77ac78c09e7c75db38bac2be1c841501cfbab4513278dbe3ff7844a65e2300046cbbfb11a5c0c8cf6bd920c57222630debc287286064c79308023a88accd49036a388e52853301ace6a3d48d62e8e8e62fd6ff5c65b037dc47075ef623499b1561f6f53d8895193660ec23ebd245fd9ad62975a288b75435ede1788e305141aded07022a9190296ab432041ef516f30495d4a3ae015ef022145d5aafab9716ca632667b56d107a40ac83aa2d144545ae73be5d5981460162e3d43462a761f5774a274b95e5e3a877028af10b7dfdd94c3d7eb540a96e0ebc50af87a3535de37219bd08841617403a0819202d5ea16c3446ad64d4cdfe61204f0dc1b90e8fd3e75a76de12b86f28ca7a3f5326f8418e929c28bea577cd2faeb3481f08165aa9297229bb56e714d7dce96e84603ce59348965a4d5d4c55741a7aebec6182c1cc64ea0a7756266ef7aee2c12a3fc85c9e7d61b9cc1d20fe730ae4587719a981633ab265b656f3d95bd8e68f7a8d9cfe889c622aeeb5fd52cf27945f71704cde29d49d2a69d15b9ba3eef8765c49f8b90986869516dc15fbd032e634e6d74f9212cc6bffe1cb1bdec52d6ec5247fdfa419c7cd57e8c2e8e2f7b501370c41f780742d457b20b618471a998e4e78623ef30a4683597375eaf08b21c2338144f6399003693d313691d448454340b90da757823fa719126cdf832f92461961f629ff70822115a273542a776fcd82aecf6602571016cd570b8058b01cc384e98821b08727aebc94dbf54422556a13a90b7e79affe2981b60e2fafd60463cf86387b5e9ed61149d6f7188084d29cf24ad22cec97e3a3f855bfe6f33118eb03b28172d90ca59709183eba52a2fcb350ba001ccef53b9e76dfdbaaa50873dee21da21fbccb0975e9e1be8eacb68710812fe0b81cf3f0463bc68976bf0fe9bb6d5bfa112faf7061082f84149534f9eeee2be0916d281c434dea70eb28c484c8b64578e9678517cc49d07514ae31ea1d127d7c0a2340610e2b11a1efc516d6d2fd58569f9ae4d9db3310fc4a05d120906f0f9bf94945228583e37766cc7b04e29a4c39af8a8680d4699bf5208b919f3071098887eea5dc31ac35bc69c1c9a868a3728924e8b1afa30b3cf327e5421a2295dc898b6a194cda766724bba4f2ab60ee8578c3998218ab6ac552bc0964b75838dae02cdc6d828ea3000f0facca062673fdf6c95f1dde9417f0a2bce89fbe84f73564912bd89f455eaed43bb053d42b73904c26bd6121e4c8a72d2e3d647e945803288bb22595cf51a6713b7e99686107de7ec563d17f4f8ed0debb06eeb594e3132d08546c017087a437ef831bc9ada4a339cc7027af0f2e32f9dc01e5c18afcd259e73540e03d35347608a701abdfd55cdba3f3ed1e6d6477fd4291cefc199af0fcfe0ee20229a8564444589024db82387db87dd9f47628bc5653dbb4127ac04919070b949f61470b8c58b35bc9c1fa8ce308fbb373d5f50cf39189b0516d5a9bbb2621fa3f16dd0afc03c486b5c129023a7c07e740d81654745b062348fe9acd69d39a732aacace96c6d0f98038a08cc219ce370ccd7e4e7406ed2fe2e55e3b570a787f0bb4b3472deb426c817303585231d10137fd1ff563e7c27ae0684265424443253ab0b4500be0b0b10648c69c910c3b749222dda1accdf862ab2d67cfd5ea013334273cd8175b380d8c5ec02300441e0c191a6c8047601fc4fb538d341b35124bd27d9d3640fd7ee89e6bc9d03f7e7d8f366f19f5504d5c1def0d69e04bf7f4bfb3a8bfc72981b06c3c4b6ba32d76765dafeee260dffe562dc30ab9cee17f0bd63b53ea032f6796fc4b8fc5a5457c4554df4f5b2c64e878b1004e840a50efc54bc1dc528d82f524ba07dca46e8944c54d86815be82583eeade06c71a97b2ef743689a5e85cbe1080edf5dd9a910c59b2efbd583cd7b0718c0553af60d6f2aab3c4c24d60a398914622d741ac54793326e2b70fb51a52039951b856e71bfc925c2a960d1ec654dd38d97eb7e05a95d2be13386745ba5d1f59e78b9646a9357339f578783b32907bf2aa861c57a254419df58dc38df7d295a77e47c38e4887046de5c42c08565b15be21ee595017abfbce6656394b4eac244f865bed896f555dc67686be936b10828a6c2afa5a4ed23d8297d88afe04d9d494512f6308e5302e494933d97bc316e294a7661155f20db996a43a653efe20fca1cf5a3d2bc32de2addac5845488dbc3df4a571f599e081116127f4d77eb26c9c10f4a60408feced765f29558552332d0103f140205555096df123c5d8e16fc38f0c1fc3c2de481081eab170826fd70e1aa267895143828cd59bfc8dda87e841ca5752eb81f8723088b2979a106fb934ff74f2e5b880641c7389d6a2c996eefe865b80cf2a86248495b12e57a94d21576acc7b0b84779d0ee8ff3cbbc419cf735637a90f0356ef88c8d043f677b1ad569e03778bb27dcecc9d8765a7416c8f7cc103c53b3268592ce8c2c6b0d891fb5244acc7d4cca9b29296e36456dd888f0c6c14e6e2566ca2f3362c453e1e6845ee980f6f3808f74d22ea1ed1f9657984f53c4330b08e8b9affdb9c4c276a04f70e2e46c6ac9a1463b27758895156eabcd2e65792b685941b8dc62f1e53c9a86806f2642a04381017e4db918d367f30c94f60830c2dd984501f11eed5becbe332d146051b5ec660e62939404966646bfa2c9c185064fe07d24a8e83df4d14cdb62fe6c2246b78dc5c301a09fc7d0a540d82a485690d5421ab72fcb0950e9ed8147b093f2b519410a3605c14362b8bcccbaf43919b8af2b501ca78194b986dc25f081c4c4f887902aef4e2f776a03072d05561810d540646124370947b1fe895e4fd0d61a8469af4da947957a43334bd52c30d6918e5cebcccd4bf82514666dc3a280805d9abfb702b2e36397a676fd11a313571f3937f30ad57f59c8c2d532d721c02cd4f9be50d6dd79bf20f2343ae265ff031d2db056ac3b359bb7cd2d1efeee7b502b25390527513c000aac2e09bcd9a03736ad40fc9c9a09c1009ed5c3b2c07f54b44dcd87dfec5e85cc3ccdd6306e79d81567279c0c9f753a57de4115d58809fed25cb54e016a883fb3ab5a15e78450959f7ccef94745d3b800f01b7ce3f338373dccd44aacac3ace7c4a622817a51ce69843416d755240c4a0c4cf08acf9bf8180f532682de4abad3a03a1649c6cc7c15665bd789544d591d463385adbadd45b2a12914287c78cf4157f2343dae548c30ff3c313e8154375c91533483c5e0a6ecc8c2fc8be7794b9b227fb1969c0ddecc31befd3efdeec29b1b668704f61c4b290813f248d278e4dd6e4187db33b3088d2a0cbbb878775fc6fae2cf44250ce5880bec9288fb99302edfda6a219d7680ae12e7bba9bfc09c41defdd6da47178c0ebc1736eb5ed4556825f7d4e20bf4940b72a219a8e7b4ec9c14375d8c3bf173c584f3bb463624fe8bc8ec52ccb63c5cf411aa6b53893734b18832ecc7686bd4f58ea4280f5f1bc6c79e81613bd91bd682c5eb54be5c6bc1b2bdedf425aaebed2d6f380364d1b0265d53023c6e3b6ce19860edd52c46e5c79706e6d988337d334f3cc244c93e9fc5904157128704303a086e51e083344eb91add7425acd3413dc38f062c5a25ba8ab9ea3517a5ef56ea5c11699c8f0b6dffa3c68fef378ce7b96341cabf23e9adb7e371245ceb34f2d7b0e21c425767874d528e657d47dee58d8362acb7a5882fa29f28354572c2adc8be32f7412a00c7240099b73590aa0d8d33457ea7ec7be301e17fd61f706ea441fd88ecfa74c9c2c104c346f96b4c81e5ab026cff28088144b1991e379207a96011caa27134287a477fc785bc4e5edf14bfde7bb32e82ab525b9a8d0845123618e09343b6b2cef0345f7db3db65a1835b416399f01c94f1e539e7e5fcb5f2b9c4074350abc09eff0b8113016edde1da08afeb93fabce2ec329f2e2ba388a0bd13da82dc6dfadb4884698a1c7703b3ec581074f0f3f9ae138b661626bc8f1811d31a21949be11fcd4ac948dec1ecdba4e2c708a52bbb2295136f3e3e36b81ce4f729c10af63a5ade76e6c655e240acfb075b647ed6d92548a5ee9b53910b11939c64a759fce3f4037f675a384881c1a655721eafdbdb4f283d814b585e497ee70220b1be745fdf26361164c316b47548e0d2c927c9fab36a2fe4620783d5b82170b9f5dcdc6aba062acf2ea2a7bfa900496e2b5b0e7df29a86c5dbbda2373d07c2abd0363198b86fa1cce5d93653c0e50eec44ec304da8cbab796c8881db30bbe7b397bba70475c44095276aaa5908e06c18c87772d27fc28e7d47862863dcbacbffd693d86b3d029982a0fe1d0d8ee85c5d5015a815ecfd95fc750566019b0fd666fde71611d1c63629c124a7dd41cc48cbf20ebf387944a209eb6d15fe97e0640c4a6b2d82f18a9cd0d77e08b0d840ef8970d982641b94203dc0544da7733a004a232fab61d8781a405317b8dd9182a36a035170e759c72201ddac0a187989e18b74c1a8e6cf4bc25863b5856f6c910831e301e4ee4ec7e08ddff99509b6f9c963571bbe986e763b4727c0bf7504db088b35d3951e645cbe1aede35901d251131974f3332fb2f0d008d0f34901c3bd9b13e750a1b4085a44d9ce45b1cf47cd47b355beaf421c80b6d1d716625d403b5d95004d3e0539910ba5784b73da9523d34a14066f3a66b8e120843040719cdf8b25274135a75b660644845f4512a5642b48b40154a62b2c9fa3bb9e72d5254207d0810bbaf75438c9833b348daf016751199b5a764ee634fe9206b2a8065e2a66516c591c8de6487677c3ca679bee50cab677a73a92d689b05dd32610a5e6c3104f254ceec847092496714a711ec8a8906b1de84adbf84851d03db9fac889d9cf1d684ad5691d76987ce00756a597e84c1a502d9855460861490d1b8d7a5986021d68c663eded63a982cc871ec8846b63e6b4ae9f3b9423e46c54fa65b29a481fe769d5229233ebd044e37657d1455b262fe0daa8285da8cc95612ea9a2fbfd543ffd5e1843ffcea4d07b77f44467aee82a78f34799fb65466531a384a8e03dd0a7c9fd3dcf399023f176deee5ee24317e968a82d75819cf50449840a8a6f74e6bc674a85c9c6b8696c12807a1b4f41a8532f3957723e96731ec84d5bc762fe47ea87e04f595041153e23e409e7cc4fb8c7b0e20a404c7675b20d9a319e62dfd135c69b40603c0149cfb494ddc06a4fbb437a9d2a20892ede7cdf87a60651c9c8202077264d5d8ffb7c9ed1fae24a32c05ea2d08b4aa4a04119a9bbd79431a2f072730ce24652a46458143f5e53ea56f561de9233f3a14a82c2f3fd28458ef950dd82b3804a7734294751cf944d59e47b2e1bfeb922989fa6c88bff7384e8ce46cc8453b26c2cbcf697d0fb4932596e828b95271550e485e30974ef494faf744c74bb5bc5686cc264a9f26d0e308b229a6aa897a1815bf51b265edf6cda50101cdc1837061fe18c0deb8991b93e0856243694c6ae0b2a35049808856d7fe2a015afdae32f059eac1fd145f7a6308512b832e13b3afcc1d2a65487a4ac5e7e61f4b7f6b8fd8fa790af253575ee9b885a54569d655210926b9a7d948214cccb9639a94ea3408d957a52995314ac5d5e686863e2f41da74b3badc31b3b20d5d85e67cfd52ecbbe0ffc46c0c01d44de23aafdb8e7283859d0c5c7f3c53cd9478a65465c3eaad47d0667452c9bad6c1aa355c1dbafe77761b2c896e0de40d7b814391ec1ad6da8aee09f2ef57e00aa5a106567df02f37f60c0a4b3367cfb54403e7fc8abb060df9de95a59c9f32ecd115a123ccff0d031faf630587e81d31cdd50ec872464fd3507025f6cbd108ca1d489d807054e0744811d8707d2f70b8c6815a4b7da172214429e8cacba5769755c2050351547cf3b1fff6313c9e0d0bf8c206086b21c5d2d49fa33ffa28c8a4106ce5dc36cf36773707ba38ea51820b09ff9dbe0c54b1d43fc8e340dd165c1727dbb860f19c71c2558d582651434b10d72d7b08ada095acbb179d5ebe70f03860d26ac4476c3af19c718644d465358ae89b54c43a02736adc96e732dcdc952441bbadc9731769c7390a81472a911f5d41f11a33af332d9f41aa8990e2c34cbcc1512d4c753ebdc834062c78fd49bb9e10244939551e9f8f649dd9a6a6ce7b251d82472ddb6a8e0c292ea1bdb85547ee5e5c601cc50fc4dd7b967065c46ed12bb83bb083d9dbe16db363d9d25100ea38928a0a77c93f8df2112e9853b418adecb375dd375f4711935b506616290047c877e1f104c072f2154acad248192bfcaa0fdd8244ca2d9063e5167e99ca47a0a4244fb8294cb8fe266ee91f550f3a295256460992ae377995812b6a22da973ffd25cc50f9e1281eab6b450ddb9412781938ea3e6f85bc9eb9771fb9ac7a04916db6ba53f8d44217c50a71bb1a244eecc20d6d551d4aa902a6860c65a20aa87dd099d7bd6e133fd45842ca1340b032c96fb9b5afb4c7bc2ef01252a562a527fed495a88ba5209683f68071239736ceca6f8c18321399eb9476b9caf4674e8bdbed00b705fead8f866abd286a7bd14b1b338a14008da8bed676d6b84bf733202f019d60ce6e07dbf7298512c85d11adcc9eb4f8cd287932f542e4f214f428b2028eb25a736d60f5e4f50629c7862f443aca2740a8d049a5991a889eb2d66813c71cfcd310ade4269201a2e2f697301252e832af9c3c30c4350ac2db26455a908105282563e18716f70cac9285ebd17fb1ab4be328acaba3a3c66dd495c0d4c8a4f93adcb61e0c3a09828e2a216534c71258af968d62a4c699df9e0985fdd2932b29e5b2a7ae5f36f6bf3448154424d1800474eeb3839a1dc896442c635094ec6e17d8c6311690ae453484d849935b37ba483a9f170a300db187bc8b2ac29b21344967ff6ac6da6fbc0fa33f6e885d7253b725978d725dd75be59abdde2ba969edd163f5b32bb0569e0290b7c2ead132d847ca96e824c04702e21f37dff8fdbfd42e892664770969fc24ff4b6dc7c2cd753e7e6d9543c2947d4c5c94c1b3b8c3f9d25b59a79ddc1e2ef60593e35b5b8a44c33e0bf7d939a6a95bfa7b994210f32a1de94911b2ff4b090555200bc6b522732c4f5b8ee0062449fe9782bff207a606ae32ab376a0263152a63e90215ef4855741772de245099f928220285104a1a7f403b53f642f7252e77b9b9364b7218abfcfc0e0c7a3625895e74d78eaa25572d994a6eb00bb8330c6c1b37149a4ac1640660215d6074da5617bd541b89b14b0a6e512b19635a1711ed98c44c14449c6989a21f664f82a0cf5c9d2f657cd57019d7dd6d878346d118a437082a0d62207fb294e4f9529be453994b39f05f514237303aca83c56910ffa25c3d5af868359382df021784a6927f799c2e8e9d073cf195c48694b269887cd4c2c23e594e99d0cbb352520c0e38a5ade1a2b4c9904e1cb830cbb4906d03518f27a9d61c773895033e32e0129012b4423c8de57fe2e881829adc01bd239e2eb07c6142b0d0ec0896c2fdb2bf94691d0e0ab4cc1971f352c396d7bea185d2bf8cb47241436cece8c819db84d242921a8c9565634aa183a0d566a12a0b5fdcb3341423002cb115b6d904cb38ff90f19af8fa8cb2590d7f49d0515fb4a9445029eafe8ea9633cf2a0f653466bff24a6f55536995f05bbb96cb00e5587a98858030393ce76aa08dd72d624853f4e84b4389351ca2929e4b87afd1d87a271d4745e825556934b226e2d79eb4a7574580b3a6ba4b447ca1da3c1839416aa63b19f1fb134d334d77d1ea4cb99eabc8b6c24dc889bd63795a917ac5353653a2c71b6f74f605b559050feacfcf8373d106ef4d523e31b85fd590233c2912aaf2920f245734b276559883e089f56ca9f5556fbeb446a0ca49f6683ffb452ab633e1bada6c4a93a28931687ec94aaff52aa95ae5c702c76322214f14e2b5520c73c5ea284fe53b1a6b7ef782a067d0e51168e1a6b501cee124b3558e18e7cf045480dc53ba5eafbb44f6e668b07983dcd05294a91d06ab314ff1f3059695f932877baf9914f115fc61672d4afc92fb6a5523162d74cd4adc4b9ba06e6fe3189123e1c82174c3baba50aac574915ead2419700760d6e8d947564391d339588f5fc055dae2d3e2a4667e57d20269f3965aae260e80ae5a0b252552b2504e57553983d3d1b7d396ca220aee245aa39b76a43ae9818fb8bd235b97a1ec5f29efd02c96aa5d6f45871417e7f302bc357c2ef739fdc46c31625573c12c5ef825cff0caa9a2ae9ccf7e2bbcc3e8b4156e1dde6d32fb7079d6b04b1302cec27fbb46566a20c30a419f305adc0b7bfd354608d7c728589db49dd41377a633ca35427cabb2411be5698ca983ac6941034d4c21d638af976e3481b264edd3f8736b829e2c98d53241fde1853aadfc6a6b709a84b9d92553226e4c1a93efcba9d7b0cd7814922065631c0f4491b20334044050b29499b1ac89ae7664c8592a937b7e2c7a9b1064a2286a8dc2cbadef18c309a20159543f0c891a9a4a01d87dc9bcccd4ae07201ed39b9e4def0a62338655af651cfbfb927f3d3f4676926a5664c199c94f1c30bb0c97860f6508670b58430095e2c89b78cb9516522adf63fa5e7040a0bc5c3216ec69415e694bf4622be22ec4142ac839d658834ddcf7a8b6b9699b99d3a417558affa99e2949921e8bcfecadf8c299216575d9c9a59a5cdbf69016344019057014d818286f251309f77a798faefda585816d6fab339cac6e6a9aa27076609a53463ea4b812d3c531b907b46ad5f7daf8b1ce01682779defcd5a9aa8252bc7fcde3cd7f98b1653f4ddb729ee8b4cf5561a18d1e9fda19e989d21b30d48464ec844ab40d8c9c4c543be3770e3fc0c836e2bcb6bd1aaa959c794e33c8562777e166a0aab89df14131049a87ec79ed1c27670dc2134d322796bfee9501f5baebed48053a232c22c2242bc976ae1f9721c9598f294698e5b6303757265b7da8f72576fa87a3a90702efbe5691058a6653fb9de644d7fe9502fcc32fe07935face1599c8a69b724cdd5233b9ef94506ea6e7f314703a6c6cb444e486e2f51156b66ac0f546c1cb3f96808b7ad735897f5884aac3488e1289d244f3a554487d28bfa0e47058f4f3997088314553d5ce3c7865f8c28a9229031cfb28149158a44da537aa7b7c72f558d7ea9bbcd7583df9d1dfc2dcd8ca70a8cd516bff555d74691105fa4853a574df2ecc5b8268b2beaf9712780aea9e4a631215b3d8fc39fe566262408410e0eb850deac04d332dd5290f325338846653ed6691ccb9909a3f277b9dd4045205b2526b0d891d3b778775ff9f7ff7f0c35c064246cd99c09fe06aba60a71b243067660aadae238c223f08e701bc00c34a74de44803770513aa1118f3572c890bbc44bc57836b926a85ed5eef2b513dc84a80fa4071b8f57084c10ec19aa6df1f3291d8266cda5b42ed89d859aff84849d2573cfc7ee5fb61f155dc138bb1d0c4fc0bea013453be722ae5730b080db8a33c7e4a3ae7f8280975c378d519eabd76cc7dd9b766c3ee9e2e36008f43fe38c3572b3ce234d49e9b146ad3baf6fe7c466a6de5d72f1edfb01c73e82dd47e887ff003ebf39711375127e4760d2807b8684e75d44dc6759caccba845bf5eefb68be2918f89e390b3e71d992f03ba7a60c43109309f86635a784827d03dede8baa1af39d08e3697ad0b9fffab4a3f800bde47176fa0f423c23e4c0ba6e2a170cba66f35ee38ffed1ea044178c3c14daba84297cc18ce3b253f7800b3645137254c0d07c57c74d74e66c492b7ff78250e7b5b01e88332c078498a80f003a8f32b25fd7f982801d12f28868440af32b1f5328a1df849e92ae571dd4dda9bce61f226d5467a352465adaf8e6a88a3c9d8840598c501e1fdd7af4d4ee85afcbd932446c22b6be38deb4ea0ddccd9c55e2d730aaacc7f0cb4a38919e89cb9389504426b6e99d5f01ea268b44fddca44ed94898ebffacd9fc6142964fed1ecefc2770c1b16c9b6b74044f13dab83b2232dd50bd6f2e20752d6988c08bafe5ba3d31cbd641d58894e4274b439d27083feacafa8148fa39111b976934ab0a0859fdc554223e648610e1119167c802cb5e25eadf162bc6f7e9ca21321fd60f15588f255667bc1ff4a6f6c2490099393949174dbf7ae7b931eb8231d1f590d33a4955677132553a08d7057f4a4fb357f112e8f65c370a1de5236229d4656da057376c5730fb769072bf403421fa6f96daa13e58ac15cae0a04629da4f9ecb069b781a2cf689482322641601b54cc1d99b419cb80b277abfc87a0a4f69d93af910fec2bbf90db39f1fb78c710b8f9475e9b8403c62a353c871cb0ad37deb628e95dbbb13997ec399327df93774de47d3436cbb8e063108b6740e1c0f5785d86662047cb8c14b9368225dd8d1f11fb81df0180ac2b8ff0dca079b7439b23244582d85b962be6320145b6e2e0264d8752b7cf3016a44a6f6d408bc04a698fc9672e4109bbec216d90a87cf924a17634da534ade1bb25f66418c7d4f7a1d2764e73409579472b5cdaa6134b9b36b2cda9ac29499524b31197e2997c905e96c6a46992960ec7bdd67f41ab329a651e2c3f15f1b176616b99c1b1760aaf3cfda6df5eaa18914a8504e39797c1cc04b0e7f4a74d0597b55e36758160d889329956c5a40679355db275cc2028d5f16929f6c0a291d1d2e2b755dc2b878091a638b3bc53a35237f02c977d7ea2687428e05ad5661eea180ba1ccb2bfcc3eea92d1c59ad195612fc898e62e2a2ff27e7198176e1d552923cd9b23ef4d718b683d142141a64266d2caa3c9be18326d84ce8eb8c691f624789a8a143cf5b79988402f786a46cab38b97ad052f19e4b4f2bdd72dc06914e92935f07fee141ff89e2fac5947b90a91ba93f356874bf8343badc86a8866c80784408fbd885cd6e9d270d8bf6f1dea251d23a956d3423634efb27761edf2dd586b9eb2c3f04e25e9845750aa4e5909d21b236468bcb9f010bbe9e3d2d5c568a7538bb48f14134be525d775f15f654cc9ed60fbd04e555626198126dca7ae406cd047dc2e46a887fd3f32bfc68676caec2bba6c7b2b5b2efa8d0b2e4cfca415c33513d11f8305e672f0afb1abe4721475cdc8bd3694ed3467d0c538c8489091804591bd66d8e504bbaab21a60353bad5da232dc4330e7a252373273fcfe5deab63ba1ad43c74f1fe87662987c9853d21eedd1d4eaebd3fcc789a03cd5a80b224c8c224f36488eac3ad08c244f50ed1d3b2cc77810c648706f151d4ae045cbf829d7d347b377384e76a80df844fe1e9bd6c4335a1a749b481ed44c2be3a36dff91dc4ed9adebc833e1255d327584dac6c2afb31eccce1b1c3fa463977b6149a4723f53e93ce7be6485341126e53848c6cce6595efeda050511fdcbb73df6e7d9d824f8ac4afa6abfe989607f5d7bb89b4943748c13c9e09ece4a3edb9c5d0eda959df66deaa0a7aed08a5c3a7d51b93769c288b5a91fe232feac0bd5425a22042478ec2c34aa2310f29301eb07e2a4dff1fc9e4c9db86d083ee65d9b87cd24deee220a39b1363a099f21079cf9318ffdaf7e2d54dbea5a0c1da4ad20166be67f7ed4dcb5736f45e9707c26e875993a2da902dba7a312184ce412003c774a33d74bcb56065b024cac98c987f3f2bd601d04298875797555c5ccd7b615b01912e32c60757bb40985f1395f5c691c309a6d6634d3e3bf42b06b6a5429dcef59ab6e1f8d032ba5cd9b901d9cc68e119937551b19f70563c34033a7da2c693b70cd078d96b5a8cca0de8f5856b02e7430b74e1995b8f2cbb21c1db8056f58f61f9d8ab81a20452c19728d00f2a6831379a56f57628ca6405e98d0dae56f8aa82855be779ad05a2479410e57da9dd7416b9c701f6d3b6d09cbc214dc2d2d31db95303a6c56a2793ab15e0c7bbd93d0a052879286b4ff64b41bcac6204d092f45ac443da6c87ce0d5a532ea98d562ae643b503203f5ca45f715bc4d2692af001b605f92acfba040f484727911f97bed925b5c5d20a3a02a236a365aa0bf858e3713e3c3eda9acc5723e99d27e1459b87fbe0531dc4b6217240333ba73d1fdf1f20a53b12aa866ea8368510cfd406b2045de7db6bc29beb0b5a2c60b41de8fb05a1111c5e7b4f23e908d5d4dc0a707b61428801cc0e32883c3125fd4f11d504c255cfbc241a6b878cd40487300bbc58711ba5fef8e39986b942e02051926a4e9d5dd8099bab3c6a6ef27f0cf8939b4da49ae2f27877cdc658061d2074ca4da67c09c5e48d13175ce81121e4883697a3ef8fd1e2187fe99e67bf17c4d8fe34a2676a60e8429f6d2d075d91b59e33a2996658fd7596e574b378586ebfa5407503e09f473472584856af6078ec9cb9f3e9cbe02918408358f89cfbbd0528c98f020cfab68f13e23f14e07c810029af1a5be1f63c2509617999da9cfe04d6c5c401b1eafa3677c0fb87fb5ebc5bc57e3a8346746c065de97bfed975127ac90f009e4ce15992f5d2e4baa71ca46af7f6ae488ab9723593fc7be5551ee60c5e64ddab2c9751d3e024cbd6bc381315d9c833c069f2ab470426a3508ed1d2ccae247b7a3d7f08b2bc3dcb97087a972b632817f441feb9a343b70ef8c2909004cb4cb97f47be5b212591348606493243a833aaf8e0f9ae9ce7991b4fe46c9ad54178ff6fe17ead5de4453eb41194003dcd9c66be021b18ee4f06e3db31dacf18fe9410306128a7e2304e2ee701fc35a893c7a8888daa7351c0087bc07adae5c13ec0086a84a7c26ef65b00f2ce831f278aa965ba2974d15a60c065e9ce1a9e53d5b271bc72d6f390059d651fe8ea460c93faaa4f8db662c1d063ea8ac593eab6b2670152dcb9a9e106f3f5c8ea1c4daab5a7bca5e9aa946d89bbb36249c98250a216484b6feb5cdc895d48d73e9273a483a0ef8a5714daca73e3b3c60b0870ccdfc6a71aeaf8043fb104411471a80ef542c5a4de97c461a03baf6bba022b7284b6a65723eff4114a4296b00c7230953162257632f15f9b0aa498bb3e81c4595270e15e1d30ad5e607171f2faaf77d1bb2b28e1df14eb788af41e1032a50cc878c3f748dfc84b73fefa7b3d7df4252b774df48ec2e10f4f5e51836692858a939e5abb27703cfdf7044cca95a95183658f4268b6abb73e2825dbdd614430713c9b03148f07b274d194295023501cb32f433dbf51be11ac2e4e78947bd332962cfb24f4f43022e7ba09546682c5414fbcdf87a81cde5744e1a50fbb7c1429612efd2386ebd41b000e01cd36f367dfd8d8dd52bf53042677be763d94479337e3f762b6edc52f4fddc3379bc874aa2e7b82928af78b22475889dbdb07fca68d6187b66c50cf157960e204f0a075a850f1f3036c882610e847a24b6c01db7209c7d0269a58df911343687b1283f3774c86c62c1931042f3f1031a7877e409762c3d4a934501b6caee56fa42452a9fad491e33e52a8fd878a245c021db0857191e457ed40a00be94d27d2fdc0ab44ca7364765a4457e3c392691760126d6d6d091afa1e6606e85cd962c26583eb9f05adc502543e8d1a3942aba90b26e9e53543e43f9601db6a34712e89128b08b4d45f35d07a24800afca6c0af67755e13ac0b1b2800bccca8777d4a73c53997a58879f431c3d0ae7542ed00e76d45d81b4ccf2b9051fbe0e7e32f9d78edea91310eebc2e2d072e26c09bf9befef7be0036023d20f9482d596b8e43de5f1c583c14f67edb9d161b8b58f8f254e10ccd2fb7bbd49b9b139d7672cd1a6ec03921525a2ce553e04cc2bcf6396718574b553c5836c89ebaf2dac244b6a70866fa326b1d0ebab94849d05bb1c2ced74806e2f6628d2b58e8520f3b52fc62536bff89443635aaa7451cc04099c1328bca135f20ae54b2a7a000cc006616d82de346d6d83398de7e9e25f4167a61985bc02d6fcacc5304e84526895dd3089322e01887b5a3c23eff429f9529b20b9ec4280f5acaa4af724851f368e574a0170fdf07776cf3b174fd574431dd54230e60dc3a49c1f6ce83bb22224326e3cc0cb0d3fa03379ab2f49830ac344cde6582a304288b8515683e1a3f7990832345ed008961d6ba2891f31c341acb1bfa1f378b5f3bc6fc1c564bb19d1b67dbdf8588cd113e22d07cfc682f3d007bae7185ce249a6f1604012c983c831d82d9fc69e71500d02cca2591e4faf53a1906e7459744ae98606f1911942294c93e2104096b8b5e56ca4d47b64add70d1a9e5214ea2f6786e29377703027d0bb89f9bc8321bc9851de75828de71741480c7cf7bba18fef06a29a117e036acfc8215f0632d12323392010f18e3713988c64510082082bdb9339392a008a71e78f13ba374327f6893b2dc22f76ab64534f90ae11ec1f8c13f63e1b7a8aba9550e21a3a4d793a5397770a6cd085e55a99a7d3b4d9ab34eea5f53a664d40ede46f063cbb33b06679ceed8da0abba924768536158ad2471953b23d5957a555e9b83c2b546a1c3a1e94f8a84d35cdbd85920e19b3be74608495431da87aa54cbc3b589b8c80c0ea2f7b812f166589d5c82bc0da9332bb3ceb8aa017b00a6f0d4423b605b0298f2db1c25c7f2ba665574e9670f5643e22f45b72dc380cb93bf19b624c8ee98f70dbfa3013a4832d5507d8a6aea5e739b2a432e2865f947350633959c8a95b6b746b7cb809b9535dc2cb1149af16d957ed6ed533a390d70b61a3fac36880da1a9c3d8adc2210f51480c1ab044c651b344f7c0d9825032c888b7396fe73a324f9e799ebd1d3ccd345be2a397cf3b159568e649cb8abb9c90128fd227daeda1e4ac65e60776ac0de88d79e91acb6a79e4d2224a7d3b5217dc8ae8d0299200b75069cf9b3705d35d3103d36d8d00c75e15901c62d641fa410a5aa80376500ac6990a592a519f1016f91676b82e124fe3b69fb037a4aa8909708bfb76ad66f8b703a545f78dc8ee5c2eb42fd00a51a1136bd759a22aee68c49b07d1df655dd8e52b019c7b5d152d64f52ed1b152d33922c9eddda3acb2006fe0ba28dcb8f1b03eb6b910c3bc48b9f84647761831f3d97281694127f03cd8de3f222333385cd0e66221d03ed8160e569b807065a8109229252e2a6181f96330c0052f33c0bd9fdf507802de50dc5a6ee10efa36cbdf0568ac7e48004cb549f0e1601fb8c2bface7622374aa950770deba52845cb57cf1fd48b48fc413878d845efb779bead05093bb6ae4da9b59b097675775d25b6aeaced5a7ab4beaeb0261ccfe96c1b35f92b8e82715bed8c12d3e780f3cf0de5c79a1cf5107df47095a084edf0242e82c13275f419738c2a9c992535ccdea03dd3a02a2477f9873ec99a6a30a9905bbf995c5321cd265ea04e88d266ebf8b0b6c3a5103bed919d060af8f97f84a304de84d1725b37154d5daf696f3387d7cb7c1f0d9bbfbe4c646f3cf662e073abd1895cfc91aec7aa9cfb2cacb4aab0b94032f18c7380d4edd9b715f0394487bc52d4c672dd3e7b8a6e1453564edc92f3cc9a7a66311fb184241a193076c96485554bdae6bf1b011a368d1a9b93fe34e1323ef3cab731100064b02f999d4ea6d47296db260d48f5ada68c863cd2e7bf79b2060f66edeef16ff38929a3316d6ecca20c31dbd301abb049b13db5e783a3310d15421580866b209ab802c4c65cf905ce3c35837ed2e90886fb156e7a293b6f537fee09d024a6c86b0d913de7bb4c270470e28e36e0d653d844868619a6c1bb6b0a0409e892331c1f7e4b9990f9b03322aefbc71bd604062adedbf27e4272a5005f1bf249a22450f3d4724a50b25f812d9bfc11bfa651a194be18190dbd4b3dba6d912895dc87697d2abf82b2045a7c6cb293ab00c57b302f18fefc7fcca5d5f49f5c0049d6eb0a52be2b624c369c1e3d7145c2039680bcad6f8c5040e60080bdfc85918a66bb7a05c9eba05eb76a73aebe928d012fc3abfac17c4857097ce1514fea607b55b499c75cd5c6a824aa04df7bd4a100789888d98cce0a63fee7323b14c8c896c430a2e87afc4ed2039f8d6e01234752055598a5fc58424614e8db03691c114349b0f4f05ff0ba55b4225e090f2c9a66e0111a77d5f64215cf24a6147033aa648a26ffe5edb23cfd845cc932f326543c339a092a7291dbbe2f04655fe52ba9b65a971383021671fd93c4151763a596dc02220b052e137ee10796e14eba69f0073fb01cb62246a16c82aa6d58978d30e090dfd28371fa3633ba085371ebf484a16fcab0f84e920c7c62800367dea3a2b793abbcad97e108b012f010e0dcd914f67020518afb926d3d6ef29fec916a3777c0b888d991eab5ba4330eac8877ba28a4fdb44ad961eddee14bf2e913acd67b51ea63e24378622711b874baadca284d3dea32e58d5bd6a8d9aea8e9d3804e3e0379ff59776e5186146222e1a900d3b2098a217e7031e3492ecaa0c80f0082b3839044d79df60eb3e27388549b132831e5e1427308aa03c146d2a23524ae879ceb3cb0215274c211cc751378e5622b71eef1d0981fb073bd96aa3faa46e808976fb08c6993503b4512c0af485268b95e19aa9dc6db8a4d4efcaee41c8ab34adb441b0b430a9f93290ae99b7bf6f83d8d872005d4eb4f23a8ab9596d88e60c964e829d945c7836c1caea17d4a760bac49d98383e4bca436c8a6f2e671db86c65bf5ac2a99d22b48057164cbe0924521a9f26160b60a298e6327232e6e40a0eb96f2d9e75d937ba844e6e2540e8b4642e30fb959bec7c7f11842d92541500576f64ed4d8d666b51135e2d041025c4cd1562a82286bc807431027cddbdadbafba3e258c28eae5c57cf9f4546b0c869af9eb078ca62a0bc515d810cba330824cb64fa25a6421d9944f6f82a1a79d8c8a819cb563d535194184c4c08c25e75f7b73d32cfb599d7459f4ac8dff350b95b559ca3a6709810f8f315cfa5b0c072ab1e01680c66649d61db5c57b69ce856fa871ab07466688cd9a4f9d09dadd96531d4cc97bf15b68e7ae1841ae002f5ee8f5eee02ca27278c371eed32b862e72bf4b06c9a8b40f64b34c1813cb0249856904f38de1c779aeb54ba1fd99e0bad0df23a1e9032e2705d22b033eec112fdbd6bc27179aafb068f3253db8740e3f00699cfb494284b0ceace8213ad693284ab25eff91c3a2b613bd2723cac300b201499005edb9b7888bf23ff9952cf1e30861b874645ee3972e9be76cd3e21304a254a387219e3ebb5b7c6978a879450ef288ed56ed51c1e8d9749c926b1cdbd6814c6a247eda265351cc307b17b99b9cd7b3c2c7a3a01da040e0861d6df2104160933b39bd99d855ef18badbb724340341357715c607da6e594e42acd292a08096bffa5fae48618b4e3cbe24e011d2c5d2478754c1246396923582de82129e522241f311d7b6f3f44dc118e248a4e05dafd6294afaf144ffa8bc9e832a36b5d2dee007cc43fb85568503a162ab34944e0fe0ac73c447eff43ff2322cb2274e9d73fea5a6156a82849c59bc22795accc8bc8c8152e614416c53f2fb7ecd3ca46d464ab248e22ac2189284409654ee698db54b4d24d2c4b7713ac1e5cb43698aa15eeacac9e62957c04c28c01b703ab3f73475b98582adfd222de095c6848666e3a3cc116fb63faf9e486462cc4c8e8a471375d87adaaff80f5a8e1b308de7490d76d8e496ecfffa77ccc3848b149399a74233de29d31268eaa8697d1ae9b4cbb4232e86988c63d67d678eab0587d7468af24da0893a0621e42db3a3a9de94311bee5883e9a61a1acff07368104aa98c1ec72652e028ab048d4cebb6d739b872482a0cd3980d7ce346f48e624caf0d0bb62b8e24ac6cc7674a3fcf18df37920bc21c74dbda288cc1c9712b42ea00b2a43850c141ff54180e2e0192cc06406c1e05a4edbdbd07a055d3452407aa1342376372b9478c960ad6db00979f769977bb816f36356824f87856cad23900b9ba946438ed703b6833b7f05ba6ca1eb3e0f7c3bcd5a224e56d2b37fb4b1d0623898f16488c301d7dc55a74cdbd111efac15614322fc0161321472a676c946a8157145622fb42ca5ec12b12fadcce7c8c5881521613c21171d8613bc0927c7ffa8dcdea4175004036dac1cfb9070b89c0739e144e56dad19aa4bfcde83445a735183f0f80429ee135b426ad8429a017f0acdc062e3c9b741d6de1b085c1358bec6303805429cc5e7b0aa47787904774d29cae740463770d0c946c97a2b9353a3c57ee4c53ed5cfbdc41dd8df51689882a8ef4bf577650ecb724bc502589c8b79fddc69bc284bc4d5c5d747394c2945c59674a136acaf6d5a582684ca44eccfa1a295a0f29a28c2b992b48b639d00d079d13da80b93cca5af5156e68751507b3a4252b98313a3ed1dbee061e46003de0e30f584b16a2a46a3a6dd46b44ba5e944d3420f24ebf885e06e123cc18f2c6d17749372267f9f19069d983b83d94f5b0ea4ad4483949ff59e31c788c87d7ab5e39880dcce40a600207b510e63be18366c96191bbf253b1b54f2e4ece58ada371d6c11af22443c3c3cf3f9e72d6235f93150927e23b4a0a4e6b312dd27eb0840f2998d28cb469c7046a8f2f1c4dc23593b61f2718f3ebfc3ae34f21fa1bf4090b5c5c48d7342f7cd94b28a097790731210694f8081140b0309673670555388125f0ff01155f2c76c89598538360c1dc4178c012a18d0e049e2539703e619deb8a48d594a3b12b8de8ba70aa7306b19a64bf4ac4263b813d69a478d0cd5fa1ea051dcc0db59bd4fdad87db94f27b2332e6002032af0a198090f2e24007ca7e258c2bd32dccc1cdf12c995834a1f12cff576eeb7d72c21d8cc43beb112fea1111f6f90cf4e7811967b08305d9dba9a9a7710b520d6f8b0474efd10ec53d660bc0b2f2e28f9ee0d4d0626caa1a7d7ca7adda7203ea2b7e2b3e433b588608cb78e2894e14be927c1e0e3a8df859a5db0df1faa20b989331ec355eb6dbf032151f832004ab1bf18ec6e03f2f02734a52bd621e8a8b5b6acabc3b5e56bcf34c17040c5915d311d487f45b33eb95b856235791b17e76fc1346f34c757b22fa07e21a51f93c986594d53046c8e5d24f2e9cbd6b032804a5fbc37c99ca419a0e9b9d73d6e8158d56a6703749f7629a57e5a6aab6b0d15ead138b39ac2fd31428d0156cf0ecd40f0500cc65a99c38d0429abc12d3f7b4688dff2e69e4204655e1a723a4077dc100296998f60445f2868e96129a683d788d3f4b3773145c8b8022f45f30de14be9f786256efbb4f3331e1979bb1ce23dabac4722d3feeed499a4bb163ab130aa11b5482781615115de50e8e426284d7a8fe7bf49a2c19ef0df73b1952940d1a1d95c3047de9d6c493520f503f510c5d9127b2e3ebf24bfe3ec02d683e920c5c721f504b91827ec3e94d15d083091600c1dbf234acd85435929365da0e344bf99f5ec12a83541e7c30c6ea4a7b60ed65f909281a54a9ce20555788a1e15f997e3dd4f01acf4c0a131a52a767592e4dc373e53de1683c969114b4677633b86c95d144d44c4d8ae391d125a68f11b23c1546d43d8cfc543fb4e0e54e2ecd3323622943b2bd010c2b21729adb12d4b2503170020106d7ac30942d8be90d85f3b5a125948dfed6c833c220b8705f1a5ae488bf83203d1ccc227728d9c730d840bf71cd060756ab17f06b0ab91995a348b0cd9013e4dd234382dd41584cfd88b800e1a4caf2c085a3efe8b5711a35dafd567a8bceff69cc80fe1dc3cb13046fc440d0abc45956a9f9f95ae145573881f7c160ec97c6878dd2000e3ffc5b4fc7da4dfc1964c64b111ae386bfba2c3b854ff6656ee92dddc5fd62e6bbe3f9e3c1c5ab3a56a0cb1d2278b2c6c30c875646769d1112a23822387177c3a6d1bfaa568db2da19bbcf2c5f413533f3df93698833053c5dfed22aa433c1810bb9ac923656d5c30858714c064ff49e59d3fb2450e523e072a1351ca0b9bc3715b46fd773feed9473182c71b5144136b76640e2f1c26f5cb5fe24a3a36972ea7a8653f2f8571ccc43b2a73a6ab3a3f849ef8113ed5082bee348a3af9a38ea9ee32d540ca76b9454421b435479fca657ef2ce98a256812e175bb7e7eb6f3c46a97e8f0618a59c0c02eb53c46c4f675f529865f7558de2189711e6903ab730412948bd8d637012e71a2e9c50cc4b7c17b4f8ddaa1619868da43b68ab484792a6b6fe90d7d717917ba0a6a069a68ea966a89c4ed838c322ddd1c57ea77d059330b58d564993b3c772bc112bd1cf0b25b7ea58be899c1943dc60dba6817b131b9dcb35fc0a440da4b28e9e37778f0c9350ccf7a5e2d7dd71b8219092c875742816e62a526192e29b807a6d0f431379d1371cdb35a07c1349730aa26d9f385e6d36a7e9c27ab487dcca96eeb61841bab6116a794ddf443c2283b0aa87abbe05fdb7cfb99f73f8974af916d71344062b7b5cc8cbef45540d2a24b9836490a09b85de643145cd4d49a7d041f5aa22156d7d8e30521e8e822413ad433fc8078566fe04dcb4e960229171b354a045b7b4889270206fe90f5b3803cca47ff1f4e03825a2805954f34a81a6ed649be20962ee577b668d9b039cd531a059cf9aaf21b6ca05495f2c2b02bb22c03215e3548b0c9fa4a65bb4ae3cc0d691e08152861550684bb6d7822cb24ff613a2a2218bb3d23226823e9cb6ce7013ea00e097ed4ce7f057501a05a914d082daabc0f5977b4b7e21bc03619439a170b0a2d751104833dc88540b831c10a040e58be4e278c9fe9c47cb8ffd7d4cb4d81a199c5ca079711ef0c80ac463a4c1019cef199d66ceb40887118fc864675cd02c96e56e71b4ac721596b4e608465f317236f9f0a9d60de40aff07e77594d39910de5f60e11af4a0bcac7d6f41768bd0d422c8c9227828517cd5bd98b0320e2ee1862b3206ba899b2d824a01cc0a3978c10c4ccba319c93e5f9e5afa9ff61348798b85b02db7d63c89aea0c4846df3e753f00d90360afe3fb83c64eccdd9cf733a7b6779a98665425d5b372a89dc55350a2c21452fba15c6681e043c6d8a4dc1d4d63ecfbd400d31a0b23fcd682b074933268296c40b005f520478e06fa5e4235584ab2b84ce082125bdd94203dc078134538510472292e0e96f243ed411bcb3d4a5086268d0c676be469d167003f8266425ce1a43d0017470bf84e742ca05afc3a99bcf580d2f0b2f14b6858b31cbf300e861fda6fd9cc477036115cdb4931fb7eb9ba72a848c9f8392e42f6605856ddbbeaa4bbd9b1e506d64b4c18d70fa51cd1a92c2adea720816c4f856bf22a844e3405a1ba06b6a94b1581a2c4392f7345f11672932915b445adce009a58108bb3e08d6429ff6b718e0885910e0f36475234b2689d2324acae9c9ebb231db5febf0d56833f5f5fb3430d832cf04653e497e8fb48b075370e016bccb0b1a9c4e4c7fe2df14191e820d3585cdef27cf41bc5dbac80053cbad2a8cf95a42a41f50d2efa2d846e280803465bacc78e2efe459bc11702b35a3d1920326d1a98d9ac89ba386cb3248ef238b772b25829346ca13874711694e988df75586640aa9d627720c488d47be4d523f9a7a591e2928bb937c5d1cc0d61560fa07298b2db950ecb11e65b613a85409b74f35042d96191e61018a75a0ccec13e7a70549f2387a941f478cd0cdf32a54322df43c3976d4f2ac73a5e9519aaca9cfb5535bd1b818f222e53c4821b066e441b520d26b9b9240eef5ad714cd68774f34c2ef06a5ce4db030c91de05889b30b7e7fc76d577b85db994797149fa8e71deb64cc75ee44cb46341d105a2a5cfbc2ae8ea205aa352175c2ee54803507f83571a068f7765d69443dd26ac7878bcc13be91d94dd6fe7f1bae92f7ed4d59221f129ddd2ca971cae8d31a8d83c9d27358e42527afe0e09d3548c92634f6914eb4c4d58e4a84420d985d0828971ac480dc58e7b2a3f636b46de8d893b633ea05ed1bd922d35e3d08f6bf5bcfa69f2a187cd02d736d2119cb2f4838aec021abbc7dd9ed4522dfb01bc027403d507bca8f8f4be8933ea70c9155f7bf76d205fc943533f76d94116345d4baef1091e9875b22ca49eab42f66dddb3353278c8cc37991e750d3e5799a57436b574dcc77d2ab848e6c59a4b7bb45b7a177d8a896856e2c26b64c56e7407c7916eee897df6c8df2b7ef473a8fdbbd3722a3e787ac7026cd17c3ab3c8d9314162d51d2659d68dae3c6434c93f291c6201b579f6ea1485adeda900d458e93a972c4927967e3d43a8e563e5de717b21a1bdc6a1d7f36401f50c76d2e59585ca8f8f9d4f96e155ba0425077a2fbc65962af88eee950b0ff6210778fc3c6ab11edcd0587be3da408ea5b0da5a96e281235893fe74412927f8a35fce9c5612f2ff9719de5ffa71fd227c03dd0987db280c22dc062bfea3c008111e950f4ba42c17783104dedddd20bb7f2a730d990261caae544fc96c0eac8a4c3d5cf7bac28848a8806c5f5e7ee92b53cb6de7c7444b8b88edb079aa4bc81784dbe283fa0c1cb53c2f66b75b569efc15ebec3a103a1a6f92ff72e0e4845771ccd5c985f4d7bd78fcdf605241247f52477ab5c8134d938ec40546f8f25b2393cd3ee5e4cb4e7b8eabc3c2a473c937694d250b52b61a3f33c4582dbee81b168146d2d2186e31b69aa6a8d760a80bee097a9fc2439ee348b9cffe3bb4baea23b0a41c7403b5e8833a3f051a985bba5014dde5f23ec163b3095588d51022156de1f90f92a384ab8746c8afe8c5d8cc9b2d875a01f2475af83a948dc0702f343fc9ceeacc24eb46d3d21812c2aa75113cf17c2140efb600b32bef09fa1f67ee750c067eed8799521c51bb62a1450859d9b62196b88696a012d26cd17dca00924616d78228f60313130444355e984a6e06a267781e5f85ab8767018892d398004d032b4fb0fbcf7a4bfd7b6b644aae8732438f34b15f8c2e026c15a9efe8daa61650620716b91451f750dd0fcb04d29d463b1f7fd549e5975f6b3fd32de1ff375098c4cc0020c4d62c2bed1d1690ac975e9cebba3ed76c4d40a03ae873e0abd38a7dd4d80a460f02cf1ff996af5549a0d78e87b68c25a411b104edc6fbc34f9f8886029e1478ce2c6b46f1af0a4e083ece3ccaf01d7adad8349b4a43fd1bfe82a73b2b276cc4184cfc53c4135d2e6d0d4341e719483de37d1d561c51c8fa280420442ceba86322058e5014ba8ee679d44d52ac1e9caf5725e437a86bece9c0efaf8c2518971e48e08f53828eb297bd04981aa9d09900e0bae769b81d02626f42942c019b01bf669012498359c073266a89379154f67f64c06a21d4000f225fce8781e2caf32c0ae01e22e4b704807cbcdb0f9682500124f71a5372da468b13b0cf0091be3c42a40fcfa12b20eaac3247152927d443ac4301152f98289634db5c44b2f70733b1bf02ae07f2e3c2924a79655974798d08bb598a6d728ab61cf9d1c66c48a52d90be9992f0f7cef1427491bb1d831b700aa583b40dc6bdb257073ea8239bbe0805cc2cc8f00de69789c7de2db4268741ed71e20ff10010e834e84e07fe00028531fe2d99fdf02b8e7bcf539b92cb6dcf4ced1766510e8e8ea798d16a4d10ee124e40be09f2fed0e2c1c6414f8b7c6e057fe53c3c1402f7c6851d748fb3a0ce9d24216284813cacb235b97d84cdd14b82a91f1108b7128f6493303477b97833b7555df3dba60522d856055e2f3815de6a560d04a3ddde45accd892f2ee3730260bd394869f850ea67e34c60bd471e5f2f9b9920adb7113e050adbe4ba8dd773a34047f649c6d01e9b79db8890a5dda173fe89e9ad30f3e29c494337fb94fe3371cf70a169ca11104e4f39cd062e8cba3f5078ea1b969df616b2565b1ba4d44752d480f6ed5e46900f470fdb0fcb0fde79adb86986a4477b5c206a06220d6aa841edf95396f8feb3d12dc81ebc705917278cd0b0ffd5e3c66211a137de5b0a4cf72fabd34d388cdddbfdccb7d006c8e74e9f8e4fd6a6a0b74756817a84a7c76d61d8f1b89fad331fa396fa0387fdddac74554f0adcc50ddcb664a24aff624c6e61a270efaef45c6a93b0123cf40ebe03e4a105c42461f137aab7d8fcf02b35fbb0551599b695160124bfd01bf6a9082552938be8078b8f3d3d0df3b19b3dd571ef2ad0ba9062f82610e304fd675af64f2bab9773f795bb597f039d8cb5b216e3fac6ab329f110d6fafbbde5f73c3084f96ada8c5eadd449f22d400503582c14918d03d5d230f5d9ea8ec31d6beb2bd3f42a799bbd6bf3ff3f5415641f700a3640ae24c5e684a607d813ed30b24a187af97fd4aa19f36301376c2b0ea4f48a5e530738f7e779c533327d4fb2e8633f75fece1cd256e300b67756395c4be911a0862dfdd3c7e8f37f1a5c88fdf3aebafb502b917147bf0f61713db7e1e41b73829f3739fc762992073bbcd907aca6e6a0f21ee92bac41bf578975ab5b65311f390bf6e30f71fbe562adcf0d02b6affec86b782a021d0e7a26bb2135cb19b0fe6e3c9d5a2ccfaca9daf387bda549fb698992f959c7e4a65ebc760bf5c941eb3b8ae68a36fabbece01e0de4a3cc52605a3fb4f23c0828339aaa25627a0874deb3e06c84e525ab7594f90929edd3a9ed26707b6699461b1825a9e0400e69b529b53b52921a3ff28e4007d89ac7c1ed189cdc1d7bb9d340c6f649295648a59c7e89ae2092cbb0ed98f22be68f983838df43f34dcba4c2c2ec285ec0fac111b23b6619b22804c5d6afadfafdb2cce92f5e7334e3ac6c45376d3307f7eb629550f1bd3b94df9bb0fd836ec726d7b7fd956680db358d3fc962ce6d2ec61cb62fd8bfe80c93eb84f18d2d582897e1bc53b82881562069fe26955bae3ee3ae8e27fade3cba28e49b96c59a336012c2a834f2b07df9b0887c1c7fe2fa578aac4a4e0cbc2ca0d9716af42b7504ebbe77e1a9afaeb8ebaec90701e375550bc0964067dba0a420a1f9026154292e120975374715ce11769eacd602ba2d0ea49a2ac74e7be0a1fb22386483732a606d91ba99f716405271676d4cc4e2ed734f7dab6bfa2bdc43d0a4e1644a9a24b6203af5014fdc2caa3afcde2836f9f5d2f3d58f67dfb19ac43c14585c229366107ee2ab33a8497a82cbbf1558b637ecb015a0005df321c2c951f9b5245d77163ed0151efed5b064eeebcaeb1c47779bbd884c43b4b676362173f0184b782d51b67b3fa0e90df933b8207a6a32a8877c3ad6f6c4115d67670c82ebca9b5609f464f4d8c3801746c03550cd60455436e6d1603c29986e9097e130d58e65c594e454a094a75ad491e6cf3b806eccc0d71b6d99013eef63e6c2f8b65dd023613855191d8b68d976180b6a2023dd8e0340d65bef73c832f4381082c96283e9c8ed1c1cd0b2bbcbd9e076f81bacf16ec9dc603eca3105abf8e7d1450c32c4e4d44fbc224e694ca2239dcba94f6465f9247b756d3130f1f7f808c0a8a824175c9173f7a1c08b0a7c892af9f41443e25ef52928200dd1314079d01c404cbcda46c149f51a4819ffdf09f96ae2d32493e088840ebefe34efcf638663b1323e399b3912b60eafc691369da93bb12528ea580ed10ba6cd784dc665ba81610957e08e7b3caa4e1e6171705668197d0dcd08d6b0ae8812e966b2a9f06fa696e8aaf9b78ad99d2684cb43d089415bbe65bea3681c8fa1eaf9c8b04caa623b258305c19251d9945d2ebfdeee37b0d28aedca772bd3c762ddd806db59e20ce8166cb430856c0c1988b2aa13004a0ecd20e0460bab7b90826c74b120c1c098b02671c652d627bc998cb6e3ffb7fdba41142082164ef2de5de01340b260beb0a9a65282f263eb3f0075a81b9389388bf2aecb12a54a242265588f9528f28c1e08931c6f86901da2c4a5a708b18600fa6a33234052a0fe73c16ab53b6abb91dd655653b9d1d79e3e7250228db4d1dea1dad683d1dea1d63d48078af5a803d98d01428a53dc65885a840adcb533c635546c89bf5cba5a8bf78338f3a897a5f22e0953eadcf6a5fec599defecc58d97a7f5852c6d53d478e88bd9f463ad7ed98a39f9622df100ef7c89087863880a8597efc1c88abb0c315a0c922a6a5554b4027d872f06be18678d705a215bbc3d331fbbe7b45d77f723cda3b17ace38a75551b10af4c55a75d78fe3b391b9f724576cc0b04b1b7edadb2c9402faccd901aa43cdf3f12effc366079e91c701c0d3a1079491972fd23430971fa2696c5ce2b8cc2165f7806ebc7b56b8432797ed4e177b268f3d3a0473479893dbb1475bb88739d9629909e6b809e7203f4b40b9479e9b4811cbb619793eb6cbd7340df710699a1b2e471dc932cf5fffde67fcbd18305e7af1f75cfc49edd80db5447ffabb89a9256a4b40c45ce641dcf0d30d97b9e14922a5614b409c2ef3204e97917912fec9062193045ec6be36d1bf1b6a4b44c04bbf59232f46de866fa844da24aded3818451e06c99af7aaed64eefb6d2733c3cab01d17c3bed8ee256b3bd273615d644856b7c5f25cf94902ca222fb250069b3cdd2a4c5eabaf0b474ef7821707c7713870e078123ef77793a3963053359972e4c8f124f1a663f5bdeb30356ad480818781877912b6514bef462d3d5b02e2c60d1b7d1b7d1b4f129ff67454c85377a836ccc95bf50773f239e471c8dba843b4491ea6166993bc499e93bf215fa34a79f91eb2e3ae9cf29e0f1d5e8cec71e58dfa6e6cd47753a3be9b8b2461aafb7846fed5779fad793192b29439f96933e6e4bb9a2b3f93d094cc600d07559444989352be9ae62a0611e6fdee3356f772af3f9aec33b38f4dd42d4b717cd96705688b802c2533333333cb19399ec980480bd2b7b2efc550c7a1d807e6780811e6725e0c1b01e782f685b9cb56cc1da1135984f907331f432aa2a7c37b32d4e147a48a3e1ff23abad17d90b29d751f33f7e39c73be187ae2c564540ba26f2a8b24c9cd6bd37bf2a781e092e88321cc613b843922539ecd79910ab47b5372a08d0f124e2086db911e848f3b839cc2b20a9eb1fe745c88ddf7d17b56fdddaab20ae6de3196545e9a9aefea3a606e7cd7d250fb617e075e4a43c0bcb920841a8457f5623aebc29ed38fd3f3b1bdc337c5e5def14cfc53dc3e70510f5f000d4ede7b24d29b9284db91de6180f11f4c8741b9f094b126b157b127cc4149a54d503e9158c04b2be6e1e5153c0324a9344ddfc0cb9fe7830fa5ac72e17bafb51fde7bef9d6b455196ca8587ef9da482411941777aa5782352623c320491220c38022ee9c9820913b71f21fb5005c3f7ea650fe7d1501aed4ba088358f46f497f368462ff2685cfe1ed04f77a22102433608b8854e09092e5f7bdf0891a60921e07114065170a937cf04dfa22221cec009b7f27826f8161f17fee00941c8bcd3ee783190c7012e8b9e099e56d333c18bea8b6782df6afc8e1f2e6b35c86f7c400da4371ee699e62bedb13ea1e9b8f335d30722ef07f4c74d1f98c3a1d4a50644c82604cc9d7423c522f30d51336d8d06042767b3795cbfc3b677947445efbafceaf9b0b0e6f860040a7198ebc3168f6b2860ab92756e7cb7b81c84de6a3e23a88bb9745d564bd4ad1eb520f45e8f9a90792bccca17c3afe1cb7612b8123e0e5936be981a9e8056dfcba17a0ddb9a1f48ec8f86383e50474ed53c7f9ead034ef747cfc6227c1c92f6bd1858c3333f94681a5f4a2e0e35210d509ef06030989de41ae875b577ef277b0f00f73daba68dbc18d8c43335e047660a5c137e847e4000b05df5382cd4bf10d1ea420b9b40125048c5c22a98b300cc60940274d2e3d492880f038ce410116208244caa401ea8c44c473bd8a4a12664bb3f72ec9034d0f8acaab95df343074722713074e444073535dda307a90d506ef7276800ebf8ea83b0c73b7adff50e2d658e5f5b4b22bd8f98631273dcd1775a49b4e99df5aa551b28fde486424a15281da519a6dc780628376ab7a37da8c528e3953f366b27a968757ac62e9e75fa7d7522471f9d121be5a7477d4e22d1363d9feabd670d54b834c29ec27beff2e1c1bce703c63b7ef1ae49efa28b77d2e5dd6cf18e1abdab5ade592cef2ed13b8c6eefb4fbb6ec1dcb7d5dcba8c57d2ed7c57d32debdb8ef1d2b552a62a92da3162e2e48304a2f2f861faa315e0c5fab3266bc5a1a30356cdc78317c19956b133394542afa7cb03c1f30ce6f193d1fa4f35bb8b820c128bd3c1ff4fc1832663c1fdaf9aff6f980793e6cdce04c8f46c6bb5b03d22f2f2f2f2f316a75619916daa888a565d4c205e9050cabaa2f2f069eaa315e0cfcac3266bc3e530c3945fe94d8cac011c0c0e408606872df11c01083fbdaf2d072afdb55f761b06550412ebdd47f770873b002ca32a804ec814b6013c8029e79103a7121cc2a36c233dd6988235004abfa6e122820c5e5db3d2b30093c175e78061cd15a10be41de1296b8d5b90a41041b82672a5b65efd11c6187a8478040ab770fc80873f0082b417e275722cc412347869095dd914266341b21efb44240223c23ed13daa3f501a769345bb3c3a987cf08cec034e38a4ae4bae3aee2c7f81863638d35162fdec18bb77ed5773b46aa76d57dd6e6c45c5f07733d640a9c2976e0ee1a217288d434293a09c2ed7edff10755aa1f7905f908a909290a5213c20beb7b5c7ca2aade85f1cdf0bbba7dabfbb4b6612510d2581f1150fe3b6f22a0d4855dd5ac3f2def7e8b6d2bc2c1400ff80714f645af8a56ce2a4e175d5593e7794617f30a897e4457cc2a0282f13afc10f688558decc05f970fe683f96098eda03d421eb1731d61aeaf8ae4dab148fa2c4b3438a5e8a7aaaa2af4d334d0093d82b9fe559158d61522201f511551505f9405cfb87c3e86871088672084160241f80381a093a67971d233db55187472d98ed4241e173c17d5775d2a0e066ef04cefb6a83818e8c133bd1babf6331f50bce88a1bdccd76afcaedaa56be3a7c1110cfb06db6d57d57f483054b8a25c53a7c34386dea5b96056f5504bc1652b63bf28642f62fa63503c457afb8ce4b99c66c13019d972b29d6174842a2e46674c9a29f2a540464455730d75b08e8bbe9443fd7b239a3cf0aadc0691a980598062c90e1767be176bf98f6f199420a15534457fc545888aadce63e0621648c5903026ff1b4a9dfb5f458da1f67d631583726e8e82cef5864f07305d33c88c56d58e536cce2f679c9edc75cd7a2c46d9627b72ddbb16071fb2e94403b1626b7df2c5525ea6e79476bfeffa3fa4e3c4b9828d1637d368e0182124451051a7e8c549c6403ccccb029bd153f72ca18639c314e1dccd54ce9e3f00f6bc31c1b286483096ef79f980286529a929bfcb0e14e3d708614e99e3272139ee9f7f989133c0333d0310b5e11f042eec5f4109a7b98895164284d85abaaaa3ae9e06a6a48cd038632dc5b753301ab18be7a347c79aa3953bad94af95567eafc7acad73e3007e37befe13027dff1fbd57b6f078eb68a6dc0e2ac2eac05d805254341777794289882822150d99c7352150bae260f3aa18926a270421354c026ac709f139ae0c2ddb66da314053f4d581959f739418a24606da20df731371a8d462d5a34610d52c8fb9c90822368a1f762deb10f600d75d0ee7b0d9e291e0106e1f2df833bf011330d08a44228610007a83375ec9ad7652d999205165cd67c3283188050a25c71f9508ac78756006258c5652c2e43861c9a5308518401cc553aaf00e54208e16329108704491d9b401e6bc45c892222821e9c1c262efc102186608513177e8767783c70d33d2ca0cf10b00086db3d2b584f093a004f4d196113d803792a89424a22dd9b6e9a7783d326785341281307d883e9a7041a20cfc4da8512459448854891a6a16de257b40b57561cb8f2b3a769322b6d57b1edb6dbcd9e2badbc1829d3f04cf2928907ae9c4930991608bec1aca4d226a95d29022aa57c3381ca1b29e529cf4c9973ceab563c6d92b76ac5249ee7ca57499cc4f37cf0b8f215cff3c157564d5c592d716506483f91650eb007d3c59e2b4fe3092cd87cdd2ece15d0be5dcca4468378867a3c37d182e8734f9be4fb09f4cf07bc7c7d3ef8320cb708be45f0edce4bbc1879d680489e2d033432b17109f7449ecba7e00e9c80042a97c49c94db12b1218b01eb60249410fe74e1e9412aa59452eac4ebc44b29a59597a9aac36f7e36ba80ce27e2b6047defa88f8c4231ce6afa715156d7df5ff8d68fc85c574498eb0a09c4302aa90083787e245cc2e408243d4e3c8132b8f0d494457806466100c4e2c25987b03b69a0ccd13e5175c51c0db202f5896c70f284fbac800489c816897828828061c00112786e28c0c3232d1214b8e90820e10927ccc0638627210b564cf58c52c6d91457f09a3c904731b4868d02147ea340e424e494b32265d625e7d48034655d58a685366a5615165a50722febda3040e13186af2d2ab85211ca4257873420b1ca121d1b95a251e4c8c22dd845e4222391ac6a84c1286d17a0f0b3ce60f976011611dd5ab04c0b6d54946949701cda9664a25265d14859774659a29c211a451c5962de5b5a70f7c58d66275176a565449390e3360e11b3bc2af8f66129e7b52909f7c6ed432db49022164cab008592ab8e32ca6cb68cb329fbdab459a063b65980c257546c26652f2f3f4e3162c8983143527177f8b29dda1235e2bb63dfb8e2beb34f8999b4b8d0892ec330dbc1dc3e7747bada66895fa08cc8bd349c0c53c30696957854cd1855a3aace1a1df78daa2dc62b0629236552da987386ad6e5c99dbb751afae44ddedf669c8382f5236e3a36a5b12b242e16d5b3564ed81ba5645cd2bcee07abc2bedc0dc8f4aabd263ee5a5b056a4803d2d49d37d2e8b6dd91060d18981a92fb466d6d898ab22c8d8cdef88872d48d1b1ca72d418fc3ba0f66c2d4b0b836e15862ca1aa4ec064771b0b6c40fd696b8c1ba913d2dda6e881bcb007477da860624f28d8b6b138e1cad83636e9039ede081c364619973fa601d7e04400003b0b93704d8219b4078e0a107ee0127480184f8608003fc903381980848808c18311b0528765d52f2b3ec7b319dbc1328f6bc3aee25efac1d77af77d5bbdee7b6ead380bc57ddaa15550a122b1e1ea06b3d5ebed1faab1d57b9d63159758668db2901c23df2828075d68d2f05817ded66f7baf1cd575c0bca1bb9c7b5e4bcb2ab38c4bd1eab752b5967bcefaa3bf1c6d831bea305afacd6754929653377ddf8a909e92b576295cd9e1cc547ca14d9849df0138612ad300f33e19ea6f20314c4308a8f942990f6adb765bda3d629aa7647b8caed5b4cacbae4bec3be3e1fadcb1241ddeaca57575a23daa6a963160ba1ae5cb96dddb653d4ee49b9547784ea67e5be338eec4d11698e781fd491c51b6446a7484d19abee68ca260e7ae560d1118a095d534776c3ebc34723735c31c7fc83f8c937c85c3387199849233305c0e394694b9c34a25976050786af77308f89c3eca1238719b65b121b0f85afe971dfb32b4bd04e87c5236b1c7a502d094b7360a6a16a6ab40a50f848017025d4061ae3679535f331d2810d1466310aa581c287b2a0f058680d145e0d149e092ea486b6550f96dba134cc59330140a3cd61ce50b70ffb70668667683420dd35d2d4648f39be7c401de0997bef976eda3bf7bd6b97dd96524a29a5ac47b2db2fd9851c84a4addb6ec7102680ee286f87c4ad56a896849604859f2aa0f011fad08048db1921dd3e0f8fbcf13e2d08e4e1b9efb1b2cf95f1f2bb72a583f769420670fbdde808e9f601a001e9c83e58871fd9eb3f9ee9dee1b32b149e5febf0230095851b15b1b4bc376ae1e242c68c57db2387191ad8148e60093b5091d3a67ec65c8f9c98e1d4dddf81695ebf8667f8dd3f3d1fd40ab3258d968d31c61ecc750efded0394cfb1fbdddddd1de56c6963b7a8a75580f9fca8e1814b98f46709fadd0b02ea810c8300bf23839c8787270ad6655596755dd76555d6a37561a76cf718438f9162d465df594b3c649fe73b1faaa56aaba5ca06d10f9582e06fe76fd9abda37ab4f4300b5b44d540d55487a31d4ad53d53a665d56157bb0f798e38b81dbeb17a17cdd163ec9fc8480b0bb5f9117f32a282191c64e530d3e51528973c6eeeeeeeeeefe944478a6ab8a084165a71e35449e10afc8f381fd41717dc905b81bed1778b58409e946fb05b89bd0df690bcbc9878af79670fb1d0ef7fdc7a339b5a9ef0394e12981cb11decac21703cf97df4f6c6fa9359ee97ab59da55e0f5526dc4d8874133a13ee66cbe15e424e4d43a44d50871d28d3275511d516348602ae2bfb109d1656858fbbac09dcea7dbd85f0c5ec7b31d7736cce102543200f12e6e012de813a44e0c9f4db89dbfd0795f8f1318117d347bc98eec7c1cb6e02f1c69c213916468bf73df3b9f872011418a023a420e1191e2343ec40264c38814a34e94f19391faa9891affa7194ebaac2aeba04de5ce037f2e510cf13b833be98783d2ede398fd33446802a208be3f238c085374222019d6e87c4831c822f86df7187c485877c23eb8e579f89b9269168077bdc4e097c1c7a2f86813465ab8a348a502ad0a38636f028214a8c213847d1733e5a9837cff10cc330a7e3727f1c84e160a7e3d29f8c3073dfaf249c7e7e753ed734d4d9e65d9ee09b229d6c9eb4dd937282b6ed12f877057b272a1f14df0e1c455152b40f7c02250a2955fc5041059e921c91226f0a1a236d82efd1fd18bd7b69f16ec4b954d882a5e58fbeebcb874f07c8022883f19fed8a0801024a84390899340ba0911c6eb11d89c57694a953c80252c876b4fbccccd45b0ac11cfce137b343989301b42c7c6204caf173729883b6835114018944698f3c2a62bc75c0a3e20e89239a913d9d7c7e74d5ed2a1277bf1573500b5f5ea0d51228a839bbdf7bcf76fdca76f729733c22310761bc364c710eeffcc96a88f389db992e7c67baef598f9a22697af572f782a9631042a841388539ab08d883e9dac7ba1d85904a11404497873e4dc34d3c537c7cc2841b53706314fd6950988b1d0573b1a3700ef14ba0c03286c23710c18d1c05cf881ecf5178262405cf6c3c431fcf55f00ccbe3d94aacc1d38199803231a834cd8ca6a98f4c9af458617263100f377931f1987cf482dce4f9d0f85d6c73f9f6ee745ddcc5311955c6692d09e13f462d01f1e2325e9236881797510a22c6d37e6034f4dc1dbe5cca612efedd215568854f47a9bea8af4d10004244f562f8b21eb51e9a401daa1b8fc513d08ea17094a6c1a2e5f16cc5e8f17c05cf00f1ccf578fe793eaac73394a669283e3c85892b41ea25348b1b5bde71951b47ef580b24180ca59db42996fe6e6054187ff1205e5eb249ea5ffea2fee549ec614858fa8b0751fa0bfbe249fe52102f5e7ac92699f117a4180f35f9970aa362f3496adc64e3376efa901a374d3bc4c6253d490d1bf1c587d4f88b93ec101b2ffd69fda4695e58d35fd824385e7afc4b4d62e33087519340219fa4c66198fee2434c7f716887e078e91da5695e9a2609f71b8fc761aa34fef22134ec4b3cb443386bd3a6f81bb6a1b429d6388c0fa9711896c6615cda21360e6393c0fce5add5b88d77271bb5068dc3f8101a87716887c0fce5988d5a82ff4d8d1ab77104c4dbb048a87d5de3ef06a6068d77d4460d1a87796458c1d0a8af8f9c9ed6c7c18b6f274d537a7c3fe98ee2f9a01ea9dbe25d4bd1cca12ec92582bad56d18b275ac926ba0593ce65275e09b74175b0ac227bd073ec93ee65cb83c742577bb4bdd0e8b90b77b543a98811b99b8b1caaaf62a988ba795a73017bf559642546bfc8cca54da141fa3b24f9be26d8dffcf8d9751819a86ad30135007aec193896fc24c7a6e3c132b3736cf8df106ccc48d74ab6e8cdcf6d68070c7a8da502b80920be367c511caf1d7343afe288aa2264551dc6db43adc25673b2597b7a709a111b9ca7f1ad3b894e71cee3aea735837c83c4687cd71838db19ced2a1db6da8ee6b01de9f665bbdbcd768d77edec678c56874fe3f29299f9382ecfc99cd8c5f3b7db38571d79be4a9cdcce7ec3718b83e36ed3a64ab94ae3d50d1ab7513b2482ae7d8ddae55cfb4a0b32baf1184cadafefb6bb1880ea81a9ef624f9b9346ede0a5711c644e3b789cc62d46e32e346c575d1a6f9ecb53a8fc000559b11204f443650a3be12750a2f84891e213050a3f61274156988799504db88995201f2953a8fc0001fd509922c507f6c026d0097c02853a0e7d1e3b64ba83ac401ec80432813c56821ed089e251d314455194edf8f63834dfe5364da3becb562eb5b99c064fa16cf73420d642bad923a77b0dc85fabce6d2948cbedb738e074a5d5713955e22bb9a1d8163144e63c5e0402de0de469137c11f0446c47c0bb399d874582c48865d271976a1c785ca6834c64fe6e665c3081be9b1d7f3727fbda2463dfa69f69fb87adcbbbba7cc7669b3491db5d36974f1a50be8c1915883527334551efc9f88b31c618638c31c61863bca48ceb3c2d19968c0a2f5bd88c0a655c312a10f9185f6a07ef0b45512f2fbdd8aeba2f9f5dbbc8f3031444c5ca14293ecfe749e9eeee97a3288a4a9129527c6c4e7d99e613efe01d95f9bdc9a74ad65ffab112415948dbeec80bef0bfb3007ef0b64299887993007ffa2b212dc539909e65c2c2f610ebe450d898136c90e4387a3437876e20ccff2b6a3da524b415a8ebde54aee9ba694a3e4063e69135c0205d6dd6d5956cd1d3d741746a033a7398fe3f00e3e51f21da7f7e777d45395a9ef7d3bc47a4eec4f6bc1de622bbc38d4b7c93a8f6a59d14b39333316dec8bc1404bc9139bc91b1304a9ba0929b1ecf415473b38b34212c2c38c207de0d6517499982a5602e8b1acca0159ec92cec71abd3e9fdf999caa366155aa98205cddec1a00b839883cffa49f32cc103376b27ccc166017390a3f0cf95b5e36ec753a014f0ec03cf553c1fef764cc585399cdea3ca503f1dd4fcd387410dadc09fe0658298baaa48c127569a6aeae7c70748cae9359c0f55c578c11e22fa46dbf11fa5e1bc4bd91d79e9334dc804ae5cb932851ea3d9a9d2b0a2093e3732f5ddc826c00bcff2f37cf4a0d476dc1d395dc11c044ac275280b81988330be294515a11a4acea5ba79a8eed9211174a9ee81506e67bd77e67c8cdbb177ab6fb574bdba57d7ee74290e87caa3ee38d5d726994ad9eddd3c39dddd71364a7a728650c23345e627ca23e270f9cae6f671a848f85cea3c76d4d727992d8bf773ad53a187cef5f52b117db9292ef5abdbd7ac4cd334212d60000518dcec942d1db3a5cbb214d5ad8222ff4099fbb0141c650851823ada8ead5cf81f28c3b6abe0cf6df1b9816107bed0a1c36983251eecb17a3ad10b1ac333f5ad4a240a7a310d0178f9b306f0ca20faeb245aa1aa7ea70d6355b52c6b47ca8be75a8fb58b3c3f404154ac4c91e2f37ca474132addcd9da8d44c91c23e9665512932458a0f6c62436b145171f2a43c8ce73e2bb7ba4559d9ab4a1141ddca1231235a7647de18b245008925511551152b9f53d079895355f459d64f3aef9ecff5f8eb57dd81b97e280c94fa45c51a822e1405b1f088acf0cca3620db76359729b87ebd45b2a84027ddad4dd5d6155f51edeadac8b2450ecba2ed26b346e1a51509bfa1525917eaeb21d773ad5b4d5fd2591670a9abd9dd063ef694bd1edd545414d037b7ace9d7a4441b70f7b9aa6aa2825917e8eb3de9dac2cbeab1159a9a220e6fad5d54455b2eb845aaca72756163e6951cff3d1e3f6bc4def13658680be9fc651326f070125d7f5adfa806af79d8ab7300bc3dec3bb987591046a84923b3f77a0aa28c88aa80a733d04dac19e2027b127f6b040c9f8c508a723a72327aec2a92652efbd8a8330be19dc418634de9dc903e6eb28c509638471421ce6c68a4cd932fbc1333f3a9b1b8f33b93bbb9a3bb3d064a93997f5adc24a551ce67ab079cdedfaf78367ba9c1bbb9cab05a1b72b72a3cecebcf271079ee96977e68557ae04b19911b386ab0b434716536bc0fa0fa63bc563cd4d513bf352b6eb6e19fb4da119aafb60201423459806f2c429e39c71ca0821d4a265001228a62cd726f81e1c8c9326bcf01852c11e8cc402c57efd1dc218cfd193adb94d51aa7522022cabb323b9aa1e3561609863861a10ef9960a45d6d4e91c21bed0e1308b42d69fe608aac424dd5ef992a18eefdaa9865cd6a5e170ccf3c4901335091851264a0a20c4ad02dc45a671193d7b4a8aaa2ac79c94c0b6d94728216b4806173ce89d52a627fd89c735a7f376653f8d92dba85b49ad9115712dd42715ed6ecddefbf3b63486a724adbed488c2b29728c8f9aede89cef6a1576354b7d9206a914e8008788c30d6e70831b3061a2052de020074cb4a00576a2852a3d55a26cefbdf7f8925a0c99f2834de8a92291cf87c040fb44246571e28b9d16933d680f6d55e858a4a3430d484b8c9b0c68c75dae69e8c90879754677793c037866f4782242f04ca9e5f1384d433a356a317a1ab4eae82e5a7cf4a70169317aa91a21af0ecb5d8e59c75c6af7ebe2525d9718efe875b98ccabd546cfeaa3ab2cefade03f56c27af57ddaf45711d61d8f8c25a27d98e745daa0b1797b6631e8c33971fa83927cbaf0aad4e0823ccc58f2a03988b6fa945988b960873713ea1d4523b038a1343a742a1889daac76c5671b03a5d40270d64ed7ea5ad6ec14b28792712911425e38b340db4c00f4e4ebc11092ea0e2c6184110b907092ec0429ce2640c979291448ad7f14862aa828a6720dca810a55194bd1e949b569d87ea8c6d0f86866e212d3b9655cc8eba92b690c6da01dec51eadc7c73f92a4ef8ad506026d292fe93c750a6a40a8c62abc30f6f0ac12ff511206a927ae8a45eb440ffd8a2cc6d956bb223e88ded8e1256aa95a5844da0d55886150a35a9c74d2ffe22e307442c71eaaa26f95c28b4477412b2a2eb71c7ba8b65c54bb2257f4cd9658be3d5462f9754c1369224da4d9128bc579f7faf550c8a67bab54fb767d3b46655ba57f5a10ec21db11f16b048c1bba0dd3dafd52914824b2259c772f1bfaf6d0b7d3db74a88aaed5eefa1b8d2e0a9d5e866a8be81895856acb9f7600786b177a8b75a1041ab23f8c2eb23aa36b56c7c545df9121bb23efc83ee648a72efe0ea376f1ba5c74a8057161237324fb98737964cec51aa0458bda21712b7ad1315a45b53a26331156854e2f6a61bb24b73a55511bc26c37ab2dab6cf6993db31dbcd5b7cd76d8acdbadba036fb555174aa03b7dabcf43ba136ff5ea3af5280fbba852d7fa3dd2ddb6635ba5acf110b29d921bbae8b46e9476a44b2163da43353b6f0f6d1fd116b6abe86d47baf42cb6236da72fd5f74cf4f49bed7ea9ed30ab43fd0a42d9ab7b1855457f9a08feba2e62b19d92cb72ad76bf5a88ceb8f4db5b36dbcd902123468c97975209068c172f4824172e5c5c5ab4188d5a5a585844224ab72d14d2b42cc3b0ebb22eb5eed6627b0b1dbda32ea5a48f6e5e7a1727b9b0ddbcdb47d4ddfe5c6c17eff6d15bd478e95b2a3da5b60bfd7d7b4bfdbb4d6b21160cb33aa25f414438ef528af115d5ecd7bbeb105072e9b1dac15b002557f4ad7645ee7651dddefd6ea7156a3cd087687d97d21e1faa1d123836b77bd76cf6d729b917bc14456ff5292ba4445057ae5cf9b93bf12a7152a4c585ff0b7e91c4a9eab5fb7d1deb8ebc902d10abd832bec8a5a48c7f937a35e39c8ff2d484d63e2e907eaeccaefd7a4e691e7bf6d2b45e40bb8aa7e26952a8ca2a81da249f55f9d3260b3c9aecf1ca574b9e8f784982f2849d5c593111d5adfada346311218c340d644255b649173281ad0501afdc6476c7334500dc78d8c38dbf62f5c30e7aa90f5940ce83318767729883d47c3732efcc1bab1ba46f8d98a3db96c52427dd2baea6546c1181086d88024626421ba6b81dcc7dd9ed381d0f07f86ec77d871a37336cc80c236c86d0c9fb7b244672214d037d2d0621a251137a80e892e853bd99d30703d351c14415322c2183132d1246eee608e5abe2e4360c6ad834b70f9b5aa92aa02b00c58e4d43a749ae674f02857c12ebd8a969058df75941f9be43e1b98f0a1a04d1dc47050da2dc26ddfd501468cf4943b4a9cf1a10ef46bba4214bed92b841274d538aa1f890450245b44342df2c92902dc5ed4828cb435772d35344a7ee63294aabca52a7882ce533ed86066afd3abffbb4c2097482b9beac02a734f481328a2694af59236dea03019580b08e7d8875cc4a3be47a664bb1a1cf940e52f1891a9027b0c7ce69226dea5b555a41a194c905ea643286060cfe4119a1945046c85a108e79624ef8f71e168c463d7a74f7782f0d1860347013187792483fc7f5e944c30c5f0cbc3ad834e7cc3c35a96acedb3c1fdae7efa369b144774ee14e2b42f36f0c773e2b3e6d9a9f224bed66df26e63951b6a3b9d7ab53b7aef9340d53cf6c47c3e35294bd148c4b1db31d89e552efeb52dc330f3b25b70a4966c23325eb45c079d9924551f689d0e77909f730cf126625e6afda53d5f782cebfaaeab658272f1fa5a4a8577dead4ad19f7fa5ca26ac2898b1c2464e099dec566dceb42645c9deb96855278e6fab422926bbdeb4b25b9d63bee2065bbeb9566d9ee7a759bee4b4999a2a38e5dab3ba0d461dc0efadc49d9d41d988301e3cec329ded3c107284f9eccbb1dc9096df24ceffa00a5e799dead605c1817c3aaea80eb56bf6a90eb5693ba35df1b98459cee10d7ad6037e376f2f338d4b43aec243445147e6ee0ca3ffdde7b0fbe634ffb81b70065de7b967f780b5006329064a0f818057b305b48abee035ec6a01d51ca5dc860562ddb298136ab951768f7a4e050e65a7b17d677435ca03ad7e5edb88c9bd9fb353fb24addc8033a42655648bc97bd4d4f685f2ae9249ea97e9e91f0a12e60f13a16658680a8fc237181c28bc33355ce10224214b91dbc1dcc019481769e095348249c1c29b7ffc233f3324106b7bb27e549e1600ea0ccbc6cac7e909788be62e3af53870fd5a7d96e9eb27d3bacaf653b48b5dde9fb82b01d889a5cecefb451c7b81a3d8d083e0494dc5802a2e5a30f69f9a8a31d323a5b248cc58b559aa66955d5c92e4d9bafaaa9e958cf3abed92906fa0c550c74adaa7a65af784336bebbecbc5685b487fe7826f4386a7994ed34dbc14d13725d2a3ea4f5b07db2f6ae46c5a9c54c0312a3edaaf8aa3a5f115f7195acea5c47e246fa78b567d433c6c2ba5651ab635533947a143dda8eef7591edf85a67a0ab4189125fd9aecade555a66fd7a57592d5e9a2d5dafac181fe3b5f3155087782d73a1040aa4693d881ead4e7cf64c54bd63b9a8a2a8772c5a8b5a89a8eb68d52f8aaaac6e7231ebb22ccbb2580b825928d3f115771e466df117d5e5a43aba8bda725a43737e9ba138b52dce0cb3d845efe245998ef6ed9df6ec5dcc325bca2efa7cdc02d4216210ca156176275e2c5efaf9d05dea6b538b2afa64f9a88aec4eb4b41eb0eb50bbbdd27ec82e86c56f96b7c03f94062414abbcd9bc597d57d3b49b658f7f31f216a00ef0c64328cd0c7431a8cd77d0c9eda2f68951b65402027ef420de5b2c003fb216786fb14a6e8c95921b77e2cd2c6f01ca4c0cdeaa4fbc57f5fd695d8cd252242a25217611827c8776872fbc4e5beec44b7ac429e39c71cac85fa0f15d0dff07cf5091022e74c9f379fcc197d5bbf0e728fb60204e19e78c53c6f77e6c5ecd7926be56ff9e89a5d4f89c9ee91a0f6fca8d39171e62d80c01f6606c1e7c95965d747b5cf36760ef13199253bd7b53aa43cb1d256589d821b7db01e76536961932ac9152989005162cc0220acb92c8cccc0ca3804941833c9ae33c1ff04de1358148f7fb11227b42f81ede85966bf807df45c0cb5cd340e63f228dd31d2461dc003bd1020f70c091480d39f8c3137b010950c0820c5190c1e70811c31737c050946081c3eeeefeabdc1683c21c943f6dda82fca1f22a2ccac5a2dc9e2d9c4e53983e92a9cdb41087bdb7fad66a86cd4a8246a0f31da57fa6fe55df3335654f7f1a90283f6577c33f9f2882abca6e892cce3e04cf746f8a8561d6610fcf1146a05cfe911d9e59e20496594384a80e8381ecc01cd4b16ed967b128aafe7eaaeaa7932a08a3a3e607d32cc99c5019c1e55b57c580eae5c5c0ecaa967d42d3805c16d2aab2315eaf4e3de319eb7287ec15736f7421ad2e67100ac6d40ab00783ed60b303e334cdfc7b56a53cf3515f92a4ac39c8639cedd003bd440c71db8e98934598939601cc49292fe995a496186c8e677a7878789278950bafbc8e1d726c9085f4b1411657feb8d2e7061e90efc8500875a5bc808fccdfc34e955a81122e30059f472b18344f15094cc10948c0a4e7e674998a04be80091cdcf038f6e8a9ca9c648e0409580118b4e066c791605103d0166e4ee7519120d122cacdc942313a920bc49b1d168a962331e2b991b150d0213a602ee9543da203888bd29f1643c69f36e3064983857bf9d380e8b80dcb731478777cd618b4c32e813f565cea32efcb26d0df1ed2f8c97210656ddf503deaf2a70181711b8ef96c9a991dafb9550fcb51686c356371b0241ef5ddeca042c50a3c76c4788e025f94537d9be6ac2e5aac70dcc051dd0dc80d34580ae6d8877380cfc14231a7a0db31cce264fab41dfb4cc58162a7faa2c0f081ef253d4e443955a7edae773ddd682d5b65b5cb717bd68060c7b00ae94edf20f2ca952b4db8397a5e1a6f4d88ecee8e5553f5c8e956e6b29a1aed2cb7ed59769bceea557d3b262d991a024afaa11c36dfedc890f6ae6fabdde94ebb5d7fda46aa165e61032b1e7c3c290d740c1776e1ce1ad08d7bb18f39aef20f5fa1a3321650e8cb4b1bbcdbb65dce6f9b3d55e5b7fa2edb6f9d23d278cc41839b5415a5ef6140683cd647a36d472af1795ce6542d95e499faa996a40582c7655e621b040ffb1d3fbd346d103b7efa0e7b3ad48044fbfa54ab366d5dada701c9518f9c2e8d6ac4bc3a95877a96faae55c76d987998a288a0749c2b1103b88e9daee486657efa10999f4afd1d941d22536a5bc490d3775824278b80774344c62ab9998f8a85a2fabba1ce55a76b647e0fefb23d42ba91bf616fec57bdae757a6dfaaa3a6ed5d7ada386ab27e674d89aa376efe7b58a8c60b52391b81e21dd192f96d319b9dd43e4f69fed86b8ddddd4668f358ded08e9d2c068d0806a0f1dbb512b6b1ad71d7963d4b4738dcba91ae22c608fcbb57723e3389c5d3f711c8359bd0dc7d40ec75d42dbcc11ba36ad0a6197b5c9d8e1b8cbb91a016f7dbd0de3a8375498c73a735498b571a471ae95af909643ab42d865f136ed2ba451216bf0c65497dfa8efe2a82e8dfaee0d0c0f39c68267187620680d5020d54df894c603256a6173a9eedb708f2b3a5577e6cd58a63e3675ac04f39f1a9c00055d78c682abf0f0cca3420d5694e826b004375d2fb9b0bbeeb9308302e832d556562bcc1fbdc1c51cd3aee33afe1b9ee38f67ea71e8c463b152e7da344e51af41f00c04b9ca85f5b225eb7c59b2be7dc61863b4256ba326cbd7f8769e8f46ccb8105072657753251cea5bede46de85be8b376f1c645c57a85e2af23a71b7f5627f4cdead4533f72eaa23d72ba21fb98d3f1b69d3d8e1c344e551d94ed6ec891437bd5da31ec1863f13cb178eaacd92ec9ccb01fdeb45d85750c66635ec5dc86b11a13b9b7edfaa6ba032fd6d82bcc744c9e0ba957a8b44c97b44d2d999a190000000aa314002028140e874422b16838a40b7bcd0714000f8da64a78509cc74990c21052c62862880120220020003092844900ea4399496c944bc457d2b1f178d0e5c72295841fae51bcf0c74a5870d860b6be9c31da347d7bbded82a98d760173bca72d0978b72adfd0f008bc343c0017163fa8e118862e9433f8678669db4aab87535a7de60e50d01f0b2833ff06fb9f288c74becfb345adcbb64f7346b4951bebd515c3525f88d6407f9ec5b8fcb8883c1bb70cf6139f4f25fb31106ca8ad1225a6781158f6535a490e77bfcc7ec8e733dee14dd6239b8a69ec094cbff527759e015f8f9ddca03015590db6ebe18c4a7bbf012a25bea9d3d08f4bfa3ec86ec1f6916a792f9e756af0c029facc100d861c4e26dee0ef31de99e92d0da06848c7bf5713f5062b858385ca54a707ef6ef00a46b3b81ead45e414904138f89875c15bd9b621a383c65226f0d629c3c1363222ff03589c07a096023697a8784fbf821fc9391b5be99ae22b3220c0852f85900513f0d348d2c8b44537fd515b2b91cc5e715d48116e887583c1606a733ba5bbbd9960abb3e7a1d22d91b39e35d358a6328a19b9577aaf930ba0afb78a3b77aa89235678f67fb9c0b3fedc5b7dc2344110058efc8e83947d0e6a359eab5d31141c54e68710608198dfbfe3df7532d0824e411f844796a2ec26659cae75c384b109509d253642dde8837ec1ca2bc074e3d06ce5ade169f3d8766501d03b8c0887a90a1f713355c21a290a173e887e587f0508084e24d8f75cba96453a60f9481f8ef4813e24299b446b2860fb95ae6951025a115c890d7ad531cb9ad181e5ace45e8abd391549b7aef14784a7506f6f051fc82d9625afb30104f8b356573510657d9786f6ad91114ed96a27706f37bbfc73d38f8daad54872adb2df5e939ab0de7ce276f0723a93a3c5eff8626f547646321ab9ba5e9509e0f3abe4917038cfa19f05765eff49c23c199401389d6d441f1ee393bf73b4abdd6179ae8f44ce59868cc14fe92eda1110636f1808ea053cca28ec29265f2feafa3e0d82f4c47523e6397ab0d3e00838c4dd01410e856eb821c90b031b3b7092852bc8be4dd1761f19d221dc824b6f506959f35f757f665260d0c940a4a2c03b56a053a004e152c8901321d25ff34a162015686eb626946cea139d349379091612e0333281138f468801c8012f1938fc20fced7eb61be5d0b3eb19d812d4901f99e280361ea07e566879589094fd534f85f333c06c5b1cd2833554d68b09a09b0a36135ae9bb070169d18767301b46ee1cace452722ce318e669c3148196977fdc50287eef7fcd1ca18ea1533e88742ead7a0a561cf8d68a191b5e8e68883a8abcc3eb18a891d5c421fe9088405f348805740adefcd95148a3923333439d6c1c73f845ae669059c6ab0aba9df4298244b30525da43b08697355c4d9dac92fc42dcd5caa3b34aba631b1888f783d3dabe64cd56a8345223fa7c6c3a558bd18c3553544275e120d39b4452d4b0d0c54ec342dfa12f8cfb50e8835fc6f597b7a70df79a6a6f28fee3dbb0d778c84dde44e9d81e9874923b22f589dc7fea2d7c53d8651eb593c47b6aab66c1d62a42b602197f19d87b79f4f75b9ff82262b10b3aea648102a21716f77fed04956034e84669657c2e1999ac98ae04ec83d2cc518b1fc05bfe6e039bbc0816ac2b8ea8d027dd82cb2431a1315be6932ed1a86b24d0a74e93b3f00d4062d62b179c6e83acdda0213948a4fa377df06ed779ce5c7484e6b3fe6a61a7a773e928696eb9e458a288cd1b930eb0d30bf22140e64f7416e4e691d040be64bb5c46b80e382be0fad81711f0714d484139e14c5c25c154f384847cb4d808954ae886742548aa274520234ae180c86560a5844259c75fa217bc556d53b014badb71258bda4ab463d4d3939f19fc64ce8a8c2963d770229063ef927ef61f9e749a1c6e82973067a76821358fe02f1b0f1c6f4d3f253e788d652c323734bc04007488eb4a41e708a6b3bc9a8525db524962567fc2c8560ee942f5d2c2c9e8fedda502e4575f1cc7c247d81b2eb0166be0e80947bea4418bb1a64f176d53717760a15bcab9766fd45726f294edb896107d2742e07f48228c850cc4a33c7e03b9e516127467a2a453c743d2f51b647b12d8a37d87c346c32083dc59a58bab2e1cdf01f5d9a0c4991550fb02faf9a4a248c7976b421bc28875e5718de4426719fcc3329bdaf368cf98d966ebe8508156fdf20bb4abd4e21ec98de1cfa9d9a88211c4300ec4e5085406cb944ecffcff67d6a7e3f33cc3cb4cb4dabac626eaf192f38cc112034d810a9b351bde0301cf03e2bc7ab46c1fccf11af4084f1247f65e70405492ea9110ffdb28a8cae4084e3d42b24daf996c524be3f7b3bf926d518f36e00f342e6c4ea7edfc828f2506b7cb518fd6d85a7bce8592ed146bc6d18dde650c960fac42efa290d6f5ea238e998f381a30ea4992d11949eed56b5ede6aac7c932298459530b62698442c1d6530ed072231d2e3cc594b89b2d1aec6a558bed6ff3db05361e95f923619f164c1147b7099cb42296d5e03ebdc000294322d87f9d3dce1be86e1016d121f61f37639224f65a616ee3fc0ab0c4238199896b035a4928c82dabacad0f658c9ded8a08d902b225b00062e857eaba07065ab4bc61180ec2edd274c3ac89ab3781732665c1a7d260dc1a16af796f302d8bbc0f4cf59f46b24f1f242b0c130b2507870461824664ee84578aa1c8ed612fb8ef0e930b54194fb6c9b59373bb7dc5169413f12ab5e6c20076ec0c72425903e21aa7def4a127e2aef5f4765180f0b236cc70e92852cc99db3a696c41fd5ffc4d899f90224a94881c581241a41882faa948511fa5e45f5530f892143404704b2a9d24e2754e19b72b1d9d29db65455934f32d28973bb5cb3315d0d6a7135f75338a74ee37d1525c79158565b489c5391d090d982e855bb7084e03b817f896fae9d04ab008c6aabc5e883f853e3eccf3e84b9a4050028ac39261ac639d4a73e5d5c0d2c5101c7d93d42e762e7e7825b1c6ca85b64e5a47aea332f4a96b51e7b4048afb6751140d0480933834827cf2776fe31ff8f5aec6b7120efa45a26d5b0f12d616291157ce7fd2db1f0f3ad568a45437aa0798630fa9847f1c72d1bfe755c68c1564444ab5104a04dcf9ca281a2f414f2959e223b2f74deea541042719d5f7186209542912b40138518d070cf4d33d0c305adb1912b788646f21e12bc6a8fb51cc736181b29af1b24a05a5792a93b8f8554d5ed95307e281f572ff08d532dfcea31c7c473018797d5222933917b709d199d98b9c4d21e8cb92cb6ea548b23db317326918aa622a37b31c7a4191790f9ef8ba271ac3816b592910b35d935a3cebe3ceb3d12826371a6e1a6d82a085778810693bcdc468ab50749ab1eb887410764851c0c401eb9b2c753a536dbc15247245685ba6a155017d0261d917dde261b540b55b13986ea155276aff4ea763438ad0ba82448613206e31dc1983640fe2e6375bbf08a11e8e2788a434845353cc8ca912f78896fcc24e8a5481b2082ab71894a3acda475375563f2f5aa512b1e0ef1c4a19ccf3e32ed2ebdcdae3c9974f4d4f04007fd4f2b2465ab787d27ee74e64e36eac7aa8c89df76d84f8d37f1b22c16805fdcd3c8f121572ea80c18887dfae946c8383a39f4ff0a4c4ef0e66751d3e45950dd16e4114d16be2d40a9b122ba48998497f56715827df959e04edffb3a529e50925589a6677dbcb5563053de6dc9598a3b8ad81d4ed4b3a86fe8a9d7f0b871bdf40ede070b5930bd5c7182f4c14213f99911f960a168fbb7c1b4d00ba8c891aacab30e657fa636465570cf99c49d298aa0bf721f896f4535b84cf1ee7a376e2407b8e2b0bf437be150926322e6454b06e2e3d8a9a80677fc026a32e6f4e3a7cab198e62599b0cbc0a23b5cc75d152eec5dd708dda704281349d24e33c76e7d062ab8aa9baa4df337cbd85f89f4fd95b6bff1aba551aca2d0bfc821e8a4ca36bbf61dbdc53618d14f4ead16643eb37b10f86c0f55e402a140e8f7e38ab224b85d796989b0e733599ce913cdeff4a43815020ef4f25b9ceb189e85c1cd71485c3b0b9d0c66fe4ff9a2dc323e33e305cd71bc8e56a38032048e6e26e2efdfe328d2021d3d3313d82bcc7df8c39d4531e8e03ffc81b72d7ab056feea826ee96c303d260d4f1a77a3e28235d0cc8e6be065bdd268ec1130ec526b334e046277cb4e1733bf0b743b9ead684991489c167b27d84d783be27924643bd08789e0592be466a8cc416e07709fd31349b7c8f2785cdf4e40dd5eeff2a4dc325da49711c62a0d8f17774fd9aa8cd40f48277aaad0bd6d1e67d9cb3b6a912ef3e98c0a0811a768a2d30314cc467e0c924452a87ce7ff4d1dfb0a134b8915d44c32a86021ac5af30462ca6c9dff5873544781562c02f2f66d676887c32b5d4768a9a166151dc6f880d3f9e9c21ea89d810efd03012d307adf5f84084a2207a60e964fa7210425e9cbddf766e118ef8222f438513d8dd0cac58db54fc216d3e1ee93f00e029f723ff32c0c73a29a2521cdcbe557f70d9968de17b21141a0f6b257884a90b55653d9c3481300d5ab904fbf5753a7367e6292753ad48006fb48b0fc5e8412d7d22fe7ad2bbdc9a8677dcf00773ddb7f04a8f5fd7fcf17662ad16595f9f09245c55a2952c0b5d2405aaa264f7cb87b040bd4bdd128be17ed0b9457be40862e5de3d2ee7fd0e4eeeb6d782e4009376006a642192bcd6bc4cd78cb8cebe18cb7459f977c28053a2bef4931d1d5d1f6734144407e34ce928cbea0776107cc36b79e8f651f617ccbf70c812f6a5121a78277b054a0e91a443b53eae09c31185cc5cfea9f6616bbad23fe76a88e6f6f074a84ac100421e442b5f805f2b7b156d30c3124b633c659db064ad858aea83349fb0232b7610bcd0ecb1ef4fbbf46e034c416f7e3f73a554b2d5c698ddfde920d7bd8abd532d0dcdeeefbe997ec23632028f9e075bd66949756714e22a95af44acbcf3dfaf36123655ab20f30e490e50ae27ff74512d9c3145f76c3d5917d92070775d59cdf8315d69c56fc837c452877748939301e79922e3720b563233e42477da8f2558a432351fa0e24543991e2070943d224fd67adb15e3ef4bedf6ed0cb1b8af15947b299cdeb4f89a33bc4d0b72dc7ec02817de53819741ef860bd595d3d0facb1ba39309374e245014a5da55c75574051cc5946e11d15cde863156694574cb96546e6440863c1080cdcf367b5cfab88dcecd6bbdc2ab7de28853fe1361b8f1de94f42ed0bc602e5964f188f0b5fa8c2dc09f3c85e758e60d29dfc4b1cc6d372af63b51551f391212d80643250215161947e2dedf9aff531b0eef16be5dfb37edf8b30c8ef2e46976bd0f103ef62899b8d03fe5fbe7af1dfe21739dfa4a6b01e9210bca0be2a5bdb66a3d230033120f501f92333224d2b78a3167dab89fff22daa179bfc027500d00120dc0e08491c520ab3433188c0013b2a53d66b553ebee52d385d45e385a43a1760aa1a68de253239a25f7c98cf21babc8e521ce211280a5ec0bcda4d56679e89efe7ad246e9375fe4c3fae5613ae1461f6a7097f838a6c3f6b88968c13f48974edcb94609c45e6cc5a7bc499a80862806b42e8adb4d97203af9be064a39d5ffb96deb838e65eb37e40e78e69186ab19a8dd60aa9cdf3be29c6d3b769d336af97595a076af38b8f9cb3aafc28abe4e4c75bfa1e82bad475db681e6ea32d559e53831d9abe4e8ea5390b99c434df198e481e48538ad3cd650aef95c236663ffbea3cc0b61100690ab8b62b2992e0bca700a6632004c00bb55daee5e3a4d521274d653969f597d3fd5666ce496b358f685fd405e817d50fd1a27dc9e44dfcce59db1669176b747c292232efa4c9ad5afc1231a434f4a1fb49b8197e7a0850d44cc43f84646b69679e58384cb10c76fda8410a4f4d23db8b6758f47330f611b0233a0e4e6a60299ac072ef3ee66166230cea016f4e5e340efbf054bee2a4182259a23f9e237399df0f9df00a2f9b949f74fdc4c0d2d0f2a13b6328a5c906dc0b4a17c03c9aecc59b54842274f3b41383a6b3292a1d04535928ab6287bc1745d4239e5b7f9ef49a34cedabb5b415ea0380e381c82867de3d55e5e78f23baaa584aa0629d2117546f99cc492379e6c033c57aa04406996ff5dc56a886624e8c52ec2fa0055416365769ab6c5a1159864c41ec953e441dbeeee0ec36610c1d63cfefd1d88f33e89daf057cd2f4364183ea1be14f9b55819fab9914b47e8085c40006bda00ee4dad3e2843b75cf23fac0588c4f5ff3ffd6867fd08b32e3882540f51b897f1f6c17c8b5198ff158a850478626337e4415e6431e3c03e64e07211b94e9989ae31809ee822f04844ecb6eb3d9ff945c02efe7cb306f4004431fbcb12ce4c055699f19416430bf5b90f92eea098172ed53c63eb97808193d73d5fe22d225cde0245f4eac9f2ebcfdab6dc3382c188a5e7ad37ddd3abe606bcfb2d934446fa53eff9e2200a64bb007fd95d2fdadcb208ffbdd3e68daefe322beb3dd0a3329f24ce535552f4454546772183deff2f611880b6c41156fc8356896c46f3cdbe1ce4266335005999e2fcef48689c8ed2b8367bb23d7f3790839cf9c2ceaa98f22ec5e374eb515d77287087d047cb240bafa1fe29edc9153626310e158bbd63a5e2b02f8e2708efbee6b35b7340c40b6a0ad5765cfa969d505506ce4be7895cf41b06c5e32686fe820b73162555c4e16d38edbe4ca8f13e534e9d12e4059fbf965836c573aaa10880eaba0a9545b520313edf52a20a699e71d5c63ef4e8eae64a0d15cdb48f3aaa03aba47df1020fd8aa9629a47220bbbcec56c83d5f467dfe04db7d833eb82da8e145a51eda4f2cc997c8dfc8044b1a71a7db07780e1a780be24c98b1d8c410b2ad7f86c77a0b2d64fecaf52ae95f819e7057fd3e65f962065ed95f0f427fe5cb65e61b24b6faa3b8524dac0aa0a728c9560ab3a4a062bed2cc9904029c8176031f148d57b2e9b0f5c6664f56106df56720b5dbd2393193eb64f925a2a065154593445fe333063174f7fb32d9481182192819fa04c02d6b1db7064ef2b1fcf8dd686044fc5ee1cb97b2eabcacf87838c6dd0e77e372151c225d89d09276d2f1c90eaf8eca0cf928c4d43d7aa23695a4ffd5916b6a6c1775ca4bd3e8aaccc9b3a4fc64ce75e23e2f542e8a0b84acd65923ffd898d94eaa58fb60286bf926e2d1cdaa557d30c9481d7548f1f5b2632c0be993ca51a0aee82f52670363ddeb1c65053611e4d0ff9a4360079572a6f9c6f740c033537c004df105b803f99fab80cf0e33013614b12dc466c60cf66021196db19d49e10913e8bda23f0263834151ee5d7d289abd779b063ccb89df6f9636acca0b0e428485352343901fc8c4092517fef4f9b8be90a020acd9a574107e67bbcdc4a0538a8893a888e5e64472726e05b102c0f3fed6addd035470d160d2f8aad39b4275c32fc0976cb44dc972941eb5bc6469a1b43a884f8d573b1240ec91c4a13c9f8f64fa3ba73bfd57b1e5573c23a3d72e912d81305df07e17cc3c4641ebd965fdb2d154594084bbe267335f93ab45523aa670c4f34ae25b79421009f8ebcd4b1e2b74b84f397c89eaf1ca04a34f0aeff1790dd4a684a5b943239aa3c7af908585efaadf43e67fb49e03cc9b5ed9a483c51f7e346951cc425b4693153a313a40b016f3cf9ee1ac8fabbe54827fd3d44768bcfb2c0eaa8a68aa648c3bf3eed4badf60106b9e2446d7f3a643a04f73b0667bc1ed5315c0dd879af461090f8969498303ace9a4b70f9a4f577f44784fc5d206024a583663efc646b29fd9d4c822d55d033071f57ed0a3e30c06025d8978c4bd5459d5ba32661594fd49a3701cccaeebbdd9aaeb3972f34bd4ae76cda068227317ea1c44d1ea84b6ddffdb8dc35709cca8c721358157b7c059fd23a038901e96756bd0a4ca1b1870c0d275c5c68c2520bd79ed5ff72d6eeab91c622e72d46e992be311b38f0f065f1036de54109dd787358f305583aefbf941f8f80c1aa8e9a471844d3e4ee851f5e25fbb0db29ba2c2a0fddb78cb1b3e4a63a7c6c5651cc56b6231f75beb59b75251852917b320f32a7bcd584a00bcded2c1ec9ea625a4894392447cc2393ea4415fb80e4a1aac0c4f4149ca6a5d9da43fd1ad156bbf34c4252c56954cc0a88afa3b2162f0f46f2d0ba5862b44353483c588fbbc179542626c6aa16985f87a5f70cd777226f5b63ef22eff157efda3951514fda50f22b0ba60a768170f71c3d0fe692030c8c1dd1a54279f5e27d3bc77cfdb6ffc3d08ca28f48c713d8706909d0b198b0027a0bbfe28eb1da1bc24b788a67024249c97eff6ff0658bac75b07703e2927cc95c6f1372fb54b9ac2ea7048e2c245796896c2c83059f11c673a45712e751a0f2b5f62ff5fa1cc986572fde65a21e0ffd7e39d33786bb431f161a19c8d8ebf5d36cb6264f63f83ec36a119e58b9437b449a41fb3372c661e7eaa2cf36571fe7a0fe1d1c31c7acbce7a7beb6491fb46819ce36748bf82ca7933b2560b22eb6423a0e4d8738214819574c83e5fce9b055114e64be5472726d8efc3c2f6cc449d6cc09b750633e3960777126a31c9f53eab499f668dd6559fe9b96ec0d47f564383495e17fb1152a93100c30022d14984cc15d0f83cf184a705811ec490e7f14132a1132b5baacf6a854995901bc0dbecad771d102e92794505a1570d309e4ffc40c38433f750d4736bb0c8fef310411ace803f497391507f4044d94bde4bd68ddf70bac7c7ef74b53bed055eb47861b8198b7c5d02d1fb0a15d3f4ca5bab2ce3ee118d53c2e7752c32a5b6530c40c6c8268b337f6ec3b1b39bd1c21a1a7192d0977d573fdf36f2ee63b988f4935cb48f50f2675cb68a7b659fdc4d713ef1f173994e908823c372093074988ca180b7d553fbd020eb14c6329537c59098809d53e3c921131dcf87c07e238f0e7944be7136032ff0f871ee41b0c2b69271bae8728228f9f5eb232cd5ceb9bf75f9b1a602cfca88d52823b8d72d19de8743d53e0a8cd91b3509dfca83ab9d75400c09de219912ef26575624437712231a9b9dc9385ddc81de57453496e5a0b1a61188cacf3e4cf92fe3d7d0f8dc98f83d30ee240704f737f758ae506affa130691685dcadd5b73085ae9e941565983f8eaaf31626fa90e9c405a1ed0770985b532e80e25f74ebb11496f65439bc477d81ebe553673c612d2f87eeb98dbdb79114af5b6428eff2a28c4ded9d37b3bf029d9664610e13d13d88f3ad867a7c59e56e5c00fd11d1de53b59c2a7e33172c97f120d6bfad449b88b2634b53f102fb50d93320920660f651f96d254a5942a1b74493af6f86ed126eb9d65208f13607bc7ef7b4716b3fcc2550bd518c637b7bea4371679319cca2af109e6f5d19b412f8ae366d1e8084c65a9107b215e3656da6cec36cc7151520ee0174998cab3a7c69ea39a9c15bc24b7f61dbd7b634d2671019f53d030fdcd7656100a9af0ea1153f6708f7a8ebcdd0e10b1ce1865d0e0336eca3a0cd5d72310ed75c5be1e9585e0c9c2ed13009594349928e5d041ea7aa66b613a435acd35e54c0945b244318617c3ae26449dd4d740ec437f74b00b90b4adaba3bd2ddd022d732364beb65b4f5dc6594617b26ac2aa334e091674ce8e37694c070503917d250c020c67360035f43b10005cc8ea74941d97cc85b9b4ad579927a330bb9aaa7a404aafe34c59615e79c8d7b75272efee12666fbe43882de1380d96ce67e318a2335635638216d29bad49f311b73acfba9adbdaaa472276813644ee7fdb67e41138965e869a8107e16cf9e736081fb1a5fae44196f06da41ddf008e2e64d26a7e55260766bf64d5e446e3850500fc8e014cd5a70c7b994b7764495ded894ecc5c6426764b475b8e5064f9286db0cbf8dc6166a94bda30f2872025156a2152aa46eff2d17cdc82f55a723968c828e1a07c7ba0f5c85808a3a714754492fce884cf4c5d17c0ac52b7ce218dc31f3c86c2a3832466d07851651331b855c4da0ba2c4331cb944cb50408b8fa9d06fc4d828cb168d5535d155187e2c2881c3c731aaff8163f205baf612c2d3453517771692bb395838097a5ec48729933444412792186b197bdb6286b014f0fa255ceade4f1320074b185a1cf9ce76591e07a2bad342c6949512095adc5572830de7b994420af52a501455e56a7cae879a34eafcc56d3ae3ac315fa278b62dfd897846c537edfb0d562ab7dc08e11314ffbbccc44ea4364081103122f93b2ee5b578432dc7409cce39f49279f50831676c63bdb9cbe0e3d2f2b4bc2c61ce6ec9380f3503d1449528edb2fc08b81241e58fdfeefd9b704de8aa8cc16379d57258dde93e38cbffef5ad36b306d885c1cd64e063cc66f248bab8e3ebe38b06b3708f43aa59f101144430023084273e2dcc0eee83de0a3fdabe05b4a9354b5d6bfc7f3dd42bc66b475ebaf769b4e88db84e224bc63ba20f9c9909deeb65d5323b3b6d9d568794958adaba0f1203a4e4dad6a9b6fc0a800a4398b3620a66ad9a1fa3dab18916e9e230b966a8c537c42baa866326988f22ee86a3f387debe6f42bb6ae3ef5bb87d75b270557c2faafa605c9c6ffbe554fa1add8660d723d3784fcb77fa6a40ba7951da2ddaba239a6dc3eb355d30cadfe7c749388deac28d93e622c7948e32e0d80bb7c27b7f000828a8cb842b0d15a92592b52ca7a689ac8a35b313aefbab3fc5f1454115a3cf57ee1b320df346e10cac90d7d3085821746f3c9cfcba9f19d3c4b434c561a30db772cb03b0d6bd9c3a66e1c78e34e85d494b554965a613f8bddf4e5fa22e94dee7d474c47dec9b5f35e995e381097d058dc7419de1b7cb3408f0305b3a349e4d22207117d7eebda5ed7e3b3b8cd37a3cad04c398412ec074c97a4d54deef8101326cbadc86fc8acb2e30e98da4aaa2bdb8c9a49d120e328f7825884932c219a6c59c3856851521ab982cc03e97c37e5c63f5017a4d9e776d076f148e9f3707393ad41117b42a132fb42d070f05325d6fcb402af11ea12aecb73d028fdbb487cfb75d1478c08ff05d52fbbfc6c2adf4d6f181492a59f97a5bf5aa38b06b51a7550b5b078b0fddfa40e1b18dbe5551a0f3ac454cbcf9e9549ca89ce6e2a4fbda3c2322117098a06a531dfa046a04c420a3265b553aed98b5adc8b6471e5a2b1e67e992a04ddbaa5d03fde4b5777653901d8d8af84953ceed56f900f5f231e8e4192f128128a4ad5679d6a95daf689aff6109cf41de763766f081896c1d1bc69d7e3971a220a188b11a476b8c0d9967e93c0379a442b39b6a6ff313c56fbfd26a13b7c4ef0c733ec0650685e5e0f8d56fb1862adb076c4243bb76d0c59c38714d302580de78e4472b48ff28b69da0ea88812f03a13b14328cf85bec1cf5c105f4d7934892e31e4e5e945ea1e1c016146bdbd46eaa7151275db0f88b7eb03c79a2f67896f8320a8a73eee21849f4b1d367cabb1edb8e31a04507c630f5f84ec8b62e885608b2333ed788fe9fa57d474712405a3bf6d62dbe31161dcec4c550ff023f8d048a34d146930cc1995c7636308aa66a8992aa25aacaec9e7e8be973b28f751cddad7e25db5e5a7dcb01f3ef78157f7ef8a9c2d7f60b8bb22778313b82250006006883022382595ad5f8ca95a79b9645a5f13711829d09988a7d53abcc6b4466d39060c07a2b42f26600364d719fe7e08440c8968c6eee81fcf72feca26fad0c088325b5b2a2d06953f0796288f06a268ff70924bcbea1a81f9fcfac4aeab612dbded71b0b823d7dbc8fedb0b31329c0533b6596da3c0bd71beefd820467ae37aa5d8852643ae3e26266356fff9292be428a4c9271f96df5d1a26fb9ad329bd30a1fa66bda5fb8c1b0b3f59e888ad093e0fcd80f6a991f1aead0f6a3231c8d3b83727322339b0c68736a5678b688916f2424a4c0685d65fd54304306fe567ab05e091487810ab804bfd44295d334fee0e29aeff1eba6f481ceaef827f27a7b76961a5aeb6866f65ecee78d4772a6c2344047fd4eb7f1f987b50f605d2b2db0410668117473487e82eaa9096e345d82c75243f4d4ad9987d30a2dea429db0595c1851753eeca329085a2954acf4b2a6e4fa29b05e40f126ffab466be7a8a454115c39752c1e80e8df042551240cccfea10ab5aa0fe990d59dd24611f7f268b4421e6b33f13055357d8a62582eea8a372bc3eb2f91d7333f8202ea29a0fb0f1612be2ce74ef2af7904fbabae7ee82e1b7d87aa1d4974c674208b79f9ae677df262488ee39f8584d524e330994b0fafcf2d537c2958b82fce743f5167dccca5c8e2ba4f331776aa5821efc5743ddb0057e9bad33d59b9f98edaf671fe6ef3323b55b72b887e1bd2541422b657427ccdb81675b962a8ed8bae759c4cfc5ee69f071e2a92cff3354f53f812c498153421b67d4f8898842f1571d53bc43052d73f27efca3c2ce4b3af2bd682abf5953b24914ab313304c06372fbcf350f8a825fa92ecc475e0a0b2043d01210f94df1ad1fc0cd13462d939855e0452b21ad0f28dc748508fb70b33d0f02dad907be8e5da2329525df5a9f172c6b9b31a63d0c837119746e444293c1329dc9142b526a60ed01508aa093f6c2a56a09b7d1a88d7d786313416054aea0474b6019378a93a5c3d4d14230107409d5ba1ec0fb5c6243e31144df8c2f0a3d34de4dead2a550e7e27f8b1edd2955e08de8cbc8cfc31391ea30030dec46061e0961510968db0492b8923e9e4ea5edbeb1e893b77221acaeae986259fb8ed88f724c6f53c40aa736fec48bfa11168e93e726bb5707854f7d9af8cbbce936a3c54a0fb7f5a2557ce2fd10bc2569bec6140e4b54a67e70336bfee88821ad736d0073026d473ffc6949697833da0cc4a54277d895284a807b44357818c7a49ab0f8a6223ad0afa8169d5a5d205bcdd29fbf89f21bbdb9f389de11525d05f01062e29f932d91344412cd1ce063b8099a5a50cb12f5201f6a0e8c2e5869f86ca9c0811a0259aa1a0ae7dadf482e6a206d8288e97bd3d9d82fddac16ac39e9fa52efaaa0df7030e6d8290ab4d2d0f8a5eb8ff1a79d58977eef3c5bb7f063a20a06e6603dea96c09b94cf587eb323a06b8e99725b8b08436ca54b7d85a30182f152be833bf484da8c0b699b42d00a6b837eb82ddcd43fb740505411cb70cec5935ec52f9aa3765c5eeaf16ac463c7267b8da3606134ebe05a92d75fcc4f0f3da4fc6e48ed0008de7b024de6b65ec97e34b9c47d3b5a99323f8a928482c936b2fb549885a0b3753ac02e70c22b0389a817ecace0934c4f08f97c7f69a4273dd2a1d91f07ac1dc3cde450a8d6a9b45e659d560dd9939332f358cd43ac8a5e74d1ec220873c480870f09723e093f094228764a6cc967da79febfa22ea89218687f1a196a32059f6ad5d65c9311ffca219330fe15b1839f4afbb4afe627b7db2ecace817b381c920d2ae15ba73e0634466486a07f15fbbd752feb2d2144b9f5aa41db4c1d72220ff54f80788923e35868f379925986b61ae8a845d99c5039462ede90158fb27e32d3db2df12d00d40146f9b3f5ea8d45926ce654ed03a0da25ea3789d395e359fe183dfc680f4f272a06441bc01d64962fb18989caa61f464135b8f8e250cb0a3636d5bf946754e9eed4804c657a6f1a20ac371fb4b6c475cef37b6f6eb9406e5a1cc0f68552ce1a79785af9040a7fb1f533acd80498621d71aa23335302022cf53cddffe70dcc1b43c03da3b64633b07defa262e8937d0a50186c99b3d2c5466d280f476f1eaf452c983f6b31d52cc24423f83a267d1b8d37b028c0d56f0c53c28bed4018c4974a36e5b027e1890b805fc27fc6e7edceac13d50fcd780f79e6ad688096b070f7888348c12851521682803d9a712ca7501202c8d4d32c0cc1ae0d8436d10009862160aa14f5f47622488bcca1a89b382dc8bfdd585f80ad0936c207ec8cbdaee2630a09e2502637fc5859f1cc1744151fd0143156e2b28df66b3bcbfb238f210e42e2114b8ea6f020a40aa21863224f8e00870b9e3c9c0c5162f03db2a60100c1bdac17c5c18bfb331bf7cafc89dd40852ac93f902bb68cb0efd24a61c520734e82226818c915ad59894bef5ee9aba89661c6a40965b95fc53ab5b309ec1c56aeeb1c7db03cbd4cf93b2c7afe21cec6c44ed42d33ffb12c3eb50cbf002cd48c9ee9bd51c5011ba72ebbff726278c2928500534651e7c1aeed640bc829d9d6f0032db7a3a5b5c9f333b2a692e5a3a8b71fa323ef67b51e8ac770bd7ba029164faf230c1cbc64e8a133162c41bf2a4c271c89dce233fed022406c573ee910868d7bea2e6529148697698d7e8d42931bba46b10a9abc0da91dbe8537c2baf8edac424f4a9f79e0fa75feb1f2d0c92e8c1ce0133f661a4f359a55911213643f380e44af0579b88f91d2be75910b67235de852f54b5fbec7bffbff93635626a17d34fa9ab7bc8735bcc14e7fe181d8698f25380b044b497e3adaaa172092f27f17e22a2b49ac02a80c43b01a5078d4338e490ff127489bf71ba8cf9c3105f1241240e7a05289f270378b9460977736b5bf359f77473ec5dd33b412277a6a8861bc1712a2123724c75d0889c5231344287514d8e28605757e8ccc7addc3a37c7715c7976ec913ec89ad2ba3cac0834070b948b29bdd0a096241466ff72720f153a18b24ea96a8d1f33d2b7009aa7db25ea6530496cc9a2646d1091d1280e0289a53474d7fdf51b4200f4471351a14f21136c1bf4be89d371418b57d40b3de862dc6a1d00fc468e5859809e03fb12c1ae8cd4134247bfa3f7d05ff491fbb6ab5fc47a1ae9b505a4c0671a5bf28832fe08555c9ca78beec3182a493d0efc4acfcfd1987e17bd2229170271ae5e5bb9140b683e47d9e01df51c85449d285467d1df1b929af29ee0c7436775d15306affdecb17efeea488ee68fb843ba492047bf9e1a606dc5670650fe46b39206e22c4ae14b62333f3e4d366a679288bdbc97ddb4dd366ea463afcb8df4ad2635d904a77a9bb1af492fe6c70c98c3401229e7825ed78725969f8b9833d0ab2ef8e932034d6d050f11a815eb4596e841e5bce7e35e9bcec48e0c75a42d7db94b97597b161a697412e56b506907cf13636a4e575cbf8c65933742f1e101412e4be65a432f990e4d771547cbb0d50b5242d1609913151940e280c6ae8f3039acb5f4ff801c57e62c6939f6a2406b9e94388625ee1d4d57a4d5d9dcb21dfd92042b17b2d7ee505f85bf4dda910b04c6aa6ac9a15a5093a6b9256af6cf657439254c8243104ab5924d6fbd08e85c11becda295d1112464bcb60acc7f72db7ad840269c79f2f3078249e95706640d68aa93b1e8d07f402d0952e1e55c398fbfea3e3e377bb7f808f317dec15cb064326bfe77e5f8a6dc3f292b365c8e629d25e8adcb8abb4246e02498b864b152f593b52f361e5a44b248e5109aa35f742218d8b2fe72dedca9fe57a4d4d61955e825bb9ce1035e6ee51089bfb5bf0ddeb698ef6e0958e4a9064c877914f34562f8e52c1f28ed88c275380778f5a90c8c1106fed3d21229d7e307e6f7ebfcbef82a1837fa20ba4461381c6a798f2ebed7bee33fd30006e53a7565246e8e3e698df9c63606838bd7cb932e327efe37b3cae073a8a2553eda1128056999f167e8e48c15a7720c848385a4dcee12665506ea6401f4dec1d452670393e31a346480e657b45d119c301629760cdc3f8d0bdca0db9bb8cd6d2e555b8d8584a3dd12d3d34871616e223211ccac1d49dd50499ad5694c7c5b17688ab5f02c585ba5e7fbcac2065e7ba20bcea1bcf3c7f6c9cdf4286e51a713c9c6424c179008b9e1cdca515e1001cc22fef38e244bef6050f47461901722068dd6f66350098404aaba0377e552e27c73b410897b5d3483a006dafd0b137749fb070aac0c6dd58a1ef9079781003d560b32335998f705a5e0dc1a36c4b313590e380e7f61feca9987d8107d065563a18eefc4d60fd985b4640f2644e687c4edf026f02d14cdbcd0f847c27a3c6982acb441deea3c757030e99ca91339c978e8f7d24a6e8f61b9ec0a107b8d715e70f289610cd41c8d3d479c49e8fb3ebf4877a2af8a652549572bd497347482789a2e0c9e149ed88a3c7ab43ed6bea2b6389b3505246e4cd76e2e6f6a2d6634eda070df40525bbcdacf3881d775ba441beb9d5520f78244e0fb650255351437cd5ab80c28237efbd92fb004439619dfc84fc39b193a3d8aa5c655f483a4b843b00bb53c70dd093dbfaf4bb1055b08d5fe8129084d3d1ba8c8ba931d8bb25c88fe7b27ec902d98aaafea475c060d620a9a95356f7233f88dcd0f77cd23f30dbf9a2e757855ec45e79829fd01c3a60d74f9f7ed9bd1edff4a798013472b5147e438d5b137c7414d1af0295c082e87ee5aa3029b5b408715132a78834085d941d2d0339745f49c7423465d96a40ed7b2db58ca511c398278807ad186aefe3a53caa8136a857cd2c2d9cc2cb78832b96ca6d6be52173585408a720fbf803fd6dc70df7876a1faa31107ef2551e598279fe16ce4da2cf752fa9e90d8f4652b63128cbdc8e61beaeae78d22e10060f4cc20f7d1699dd167fc973a073d349d727be5b4502b93d3c35d5d722043f3e122ead1bca2d9875789fee9ed0bc300e9cbfa8c20e01bea36e5ce1490716ea8f87f50e026045e4bfa5ef66b3eb9fb451d271ea4a60d15379228c08378f0461a027639d3ff5eaf0ca4f30dbf7bd9730c7219957bedb318910df5806743ca043d4194a0c1554344d6fba4157a596cba316ecd8daee07c0c4b5f446692fa16412503c6821fca6f4ceb5e6bf152b6d3b7077e6a9bf1df0e2a0b3b6064ec4e90fc1b1917931c67a724c0e70838cf52a4ddfc1b39ebb1de00b28acf00f767c61fd6a2170e99bec28c0a73c7a80c49403f28da0f3cdd08073751318d4e488c3a77be24dbb53c2fe9b1991ebb9458a689756a2c53c53a2dec2962991afb94d84c8db574aca6c4723ab6a9629d12d614b14c197bba58a68e552ab69a46ef3236e73c5276e894e1360f6dceb6c37903c999e341f71c3251e2515c096f4583097403d3ab4f107a8d3b7f6b427ab180a05e5381918e27f8254ffc8e9dcc9f4aebb4849f503c669708186a905df8d03e31b28528e74c786267941994b9c3bea166444949ee7f27d8503ff1625cac220f90587e3d6f0107bf00db93972689ad7f1bf915f6bd60e21b8cb05c42b64d0e50de17f80c6d9794dd9af0a5a7eed1fed8ce1940624ebe48f5763d1bfb95bbfc179dc926585dacef02ae02bb177ed0e2f283ed1bb944af00853a518215d33e27341fccc99c00d8caeb6ce88310ebd9412eae0de429b00da58e7b556298c82b91c737e2bdf0f78b5cff1d1700707c9fb5164662ead7947522bbad13319d798c7fdc0ced55e3b2a1e38d0b62439811984ac905682cd0e10a612638d64280f44f237aeaab93b04d2a7a9fa5d7e70bbc19a5140f38b3c7222656260690e43161622e0151026b5f84d051072b2c5cab6a47bd20a1a819b00a5ea03983be05ae52fa7e0ac865baeb532d1aa30586e5dc1815092cd62f000a3821b129b189f13e7c548b188dd554b1e970212d172368d7e163a32a2fd0841654c555a8f1ff47c7372f3afe6d16f61d4ccf44f79c2801d416a45081dcc6aec65b8fda14a9df3a6f5e1ab7e256d392e6f8d14734982217355ca74a3161538ff0ffcb7bc1dc7d1a8597fc7d575b7d56fdf488582d3fb60d3b03f90da2af0055df80180e0585d55fe5bf8854508e0814f616dac43542762cf0c1285bececb19f06b8fee1a0f26f9a5dc4db45004e05d07c370e65171c5b728fafe1d168ce1ae457ce2d380de218b796dbc22d331f59e3c03bbe698582e2c7a25dc64b59704ba0c2ea30a3b6d9c611df50b397fc64ccc08ba6c23e1df696e107ba04ed3a840a0a7f8b5b09df76c19959d984a00ce2d14d8c8a94416684484f6f5f6387f1c03aa7e935d7a0ec2aba657793a2c53f38d899f791e055ffce1dd64769e9545634b7c1d35c875890f3c5df38ea5855137c8c4c58abac4d4e0f500ea086599e3c1e193f123c9ba7f38f48c4593eabbbef699267add85ac181fcda3c50b980df26cc2c6cf695ccaab004d3ababb8ce94ab1c45eace013bd64d390500bc3dbc4245e6dd005618fcc5c41ca7e6c0e9c264bb3a26d413dc20245aa0db6a066a7eaa9f104456d835f177cae73602228707faa0b89963388e53b24c305e92495cd9ea5041eaf3b43077acdc257a8deec5f2e735a6fdaebd1a44cb16b4669eb49521bbf639f000297cfb57ce5324a7c62b48cfbaa87aaeba25d544b42b41ddbab8fced7125266d1b0857d77b9be62d125ca86a61f29298eec970f008c396aaee7f649b61dfbab1fdb8b84b717e9bb6bede33384447616fba7fb1d75cfddd55298adead8bb87463a1b2d8a7fc1e8bf590bb4a92db4ea326baefc9c94e231f3c354749c0b39f054a4a9aae03d61ff74b9f64c6bf2119591767bf1f876ac31ab958db4a3939cc8c4d5981af5aaeb2eaaa940d79f8d48fdcefdd594eb9d0625c1e76b968822141ef26ffb212c665ec715533f950eed3e6f44a19c4a4685e25d54cf07958b2259e8e8ab216e11e52389aee2295778d4f9bc4b66ba2925e6047384855f8889ba91236a2672a858ce019538f267bc976d84b3f7d74932efb16940253deecfa07cb7c37d898673a8bca1187c6360157b31bea92e16bd9a3088a958437587fee14c715cbe04b8f885e0b304e89336215ecb235f15b0d1436b41f72c3eae1b3c16c940f06fcb83b9c79202a03271b4f43b1dc941a4fff9b7fd7bd97b2942cb3392c9accd58a165f27cffc774a9130f6e9f5d42259cd3794d56ac6bcefc00474c47ab12c56e7c6ae00bcb8c2b4030f6771e236ef6660cbec9516f68b0846f09cd892e6929f5846d47d6d14a97ed96e1cdbb2e35bb3022c27253c89abe99b0eb3f5a58a24b8a51c25228c6d268403aed5295ec425ca2949e6f1a44e94166186551e0233b13276aa1dddd313034310a268f69e1f4f8996cc863f0b32875d4c8daf774cabb42ba8c7c3e780b400c10afb63f7f973816c55b1e318e1d19ed893696e164221c3a268c6c88ff8743db3026e78329487858db5c3e577cb576d4df0e6a304f4a691c3912023fbd3d4346917d548d73e41093a5d48aeb97a7d98e8242c872edae02f1f6864dd04ed14994ac08a83cfba9461984aab14b12f7b3ffc9feef7e9d2b454e6430d81b66b4c631661f8fbe1ca47007a93225ddef41c41b2d8001aa8cc075617f6bbed3daff41e3534394f4970e961db11488dcea2cb295088347e589613bbcee4f0d5b2cb7c8e37ff7dbba61498f4ce46c3611676352b6564142f82e49af3f19a6d4619513d5c46a35c3d846989aaaf8883aa3bbb88d2ad70cd89e841035c41e974f6e68a843162bc0fd8457f81c31db8ba7d9121523a99ed23118e7f67ed3ffd30d25206f4f03c9826452a59b07ea52581caf1dfdb72a1f5ede24c2e4e36cca6245cc97a0473106e24af24fb562794f9f5c77fb3c7b8c838d5ab14758cf3bac4078d632345025e75140914e8ee4a6f63625a043e7c6dfbbfbe63ff27a7aac06111db3e2e746e7cc3d84294d79857328552f9924c9cc5d80e75d0561566439e44ab0b61eb1c7f929d5cc7c51db5eecfbc79e1de109636369b9ed10286b927b66fbf95d56cd933d0fc17ec33dea9de9b95cdf18f76ce5da9b7d9aeeac880cc7f55c5d0aa849d352c4a918d99322a4c15eb148bdcd60cc144498494c17c6a07b0ccf0cbcf5f4f2ecfd7861c10919b87a8b7701e4a11429fd8231ac1c97eb8933d613db050401e132ef986f58db27f8c42da9fa21f3e3a79b6f289c2cd08cc696991818eed5268221f61899b3e370f158c4e828d1732148e5001d272e54ca4e67f23da5565f944c8f8de6c88669a1853b5f726f9d7bc43b09d41358fd95d99cf6eaa5b24aa9f4fd3e1bcccba0b44099d31e0fde6713f260252870ea73a1b9f9c6a4cb5ea88d0e1373e63f2df3d1e9426ffab65edf64e597f7d0d53beb970ccaeca12909abfb0a73e12d5877d4f1947a60b0e20207acd9e1a274ec8f24f68d7b2319ba662068cd6fb6cf71aac0bc435f17886acf736e430ee77e9ff758db0be427c10d917a789d31959deb2a5a209c81f59e287fb919fdf9f44f6ee5c2898266197c0335f589322baef69a4415e21d343e4cf73597a5c9f0c8f2e7a468b29b1ef09cc789f0fe2fa8edf58bcf54dd7e5a8bd068957f05f1268a83c45da97d7fc0de5204015d03d946b023675eff4713b0878856686f82a337d178924e33fb82a686d4486c0201676513c62c13f1c4041a12f39911fd9e2caedd3f50790b6a0bcd45038edc243bf97b879cbea599cc19677ad4ce31b7144b2669eb6a22c5103678111677e7c5193dacd9b51eb8783ae501dd9e6baba6f93274f51584e3bdf8f18f14333e802b940d13f8070cd47b04689a2c018af866b59a1c65fb82224cf820f12511db6942422c8a42e22108b2569a19562643528ea62d8a13ac5415908f021b1401a475c788629ce80d42230e8806546cc24d527f8d430a7d4f41cbc4b891921c5326f61cb337e9cbc2409ca66849b3541bf18d19e390380c9167de1ae45d88ea594dcc162cced3de10dc1440e41e402f26dd872390201b8a3b1692fb00500386c5639b715c94a97449c21477d123d829d0947e964e658d97b77902c0056ae82339f61d595b7a17d163c49aaf1d99f8a43210d38a506315c83486821459a01ffe00031f420f39ea28831f38f16132ee599e366d03bafa10362452fed72ad4ed3c5fb2c8547a2f677875e6fe058a024a2a8df4afafc2e56d4bb643f1848ec8d4ec96840fd448b693a950e61f2430c43478d7f1566aa4cef7cff5040bd742806914f8064c7269c75cb875f42b08bfa790411c71aac309e0cbd37c2bbfbc022b3a21d13c2699862c9a0f1f6357aee344035948112405a23f23ef722e85b56fddce6a852331d6ecaa31d6f0df6d950451186359300724413ea5aeaf97dc8abaccceb0f6e292bf5fd740fb2b4aa468d6727bcd5886050dc6307ee4547c8af12a7af11e0d8d4ef23100fdb6d2b794d8c33cce194fbccb35328888ff14fe0f066ebf3d5f55a035ad00b525dd47bcb04a037566f7805167a07305984b5684f87a8461ea6a73035836430ea0f8c6af9e0593a975780790da71714c947c22e99e4e19e617df0363f97cd0890b23575e09c96a68a03b7ba8580e3ba40bcb934287fa1ba0f7460d18fe28897a394a3bbb9681b0590533020cea0b0015f7207052a213fa72c74e0d4e238e85eaef73136d4e8bb48359e2396bc147993a378806598c51b268da7640f2baf7a2656bf19bff21603ce29d40188f2dfcb2cafd2ad1f2e8d6d3244243864bac09cec7d1f6f7be8651b191fce98050a90ad173c1d0476d04c87eba3a8307f2c8ad6e4ed9a8098db1b6238530af9efb81a30489024c1faa06d2bebc6cd0fdb01cf9b3d5000008064f85cc00855891088c1805b65772172420eca5375a70bca892d95e3aced628fb2d6228dcc47e63d78ad5ffe63ef8d149b13747d8e00568f74622815203ca29b7882530e95b0d5aca20ab403d09a3ed4562f133cc9cddb8458ffa62f108118f0500a90d5cc0d926bc21f05c27d06433abe1597614152c175de2b0a990046eb26d2fdbaa0db2182efa297f1aff67db86149b0de7d497a0095956f4a083ee0be5c54b6f34f77f2cde9e0c56f3f46b5a0b6d46e1014b0ad76724d1c0995a05e01edb5a51128ebf672d8237430a244213f712a00516031512c677a631a6e54b564d47c6c4a49cefba915bf12a4114622dbc800a422dbbc116ce809beafb8238c6fae67784d2c199b750f9bd7deb4e33afb021d8bbcbd26c53ccf45fab5b0be151ebce176fa4975ca15f0bc0b992dab3d111595658a8344a390e7c5cab0b8446bc7bf01513994ef724d8e81f114a8106bb499a820a743bccbdb6801c75cde204d31b697eb61d6b20df2d639d45e31482d9a2a95219235829098b2d8ea06b129026121ccfc13beefa873d4ad16d3da7fefa327c32d221c708f26fdf3ab2b41ab2842e3d59242ff90ae38b86b849b9474eb8ab7bf0d4b4a1e291c0c80dc44ffa22f8537e38d154e2e145299c4836ea3fc46085166faf53f9ddece13a2231d4b21eb229905830455fcb24717ebd9086c06085da9170641afa40d5833c441bcf0a7ede6bb3239c5c17023615b101065e230732e7b28bb92775f60b45ebdc9f06ae136ab8e7065528744dba66d1933297a338019945a20736a403f95ed08505cf890a73d336ada1c0fd82e4a7be81e59862151af69b780de4fb1c7be734c3a2dc9ccc160b6863c839bb38aebbd313f2bbca1fd4ee891780c38f2574d683dcbdab3ba277f9e8f57cde02cf952003a74d10d36829cb4c4a14565d396a5fa1ba6ae7540615544583ca42a077c32d53afdebb417cf84fc04f11630e101e012f97ffed4dd58db04863587caa812df31c980868ed1b2bd7e20be37c2c00d586044ff370a927e47d87161f90139640c38dd0869d2df6655190ae2088c289af894af5b89ef6e22f0d28e3b9c56ff899791cda9278b963a367abb3a6dac315f425cdb49a27f0f736a74af9eec02a9a99786a29faa4b310fda3d8f98e66f38e5b3b2934dc7970196176248a214d9f2a2eafe01508c5a0d2a8d4bc7193ace457979380ce8748fd8730a0132a668eaa0d5599a80aadc9ee1a437706fee760e1f31b122e4129e5566ac2a2337a182d414a612c68ebe56ca0def2ce9d13e28aa181a8f0ff3ab1e0fc371bbba202b0f3df869d2421f1ad6de13599975db5c2a19675ca1badc30148828090abe3ba8e90988e09169ff3e23dd1f222e034dd1d174c5fd352b5cf58cf9a81cf4d70c445b514576d4d36f88cce9da0914e58501ffb319625c506e44e5a4c6cd6bb6a58a04e45178bcc2c038be7a31e406a583236416d30329928912309bff62531cadfca6f25668116e8c60f0685561960720e3c62b3085c027470264da7a8c0d9804666c4932a3d8da5f1b155af537b52ff03e102ef6195d04dd14881b7eb6958dc0f85dafdd7bc370044f54637b1e2ecc473ca63efad7c0270cf0bb71a98919cae01d0bb48d7b4ab53feeedb5fdc6215f1e0cf8823d10ec3c02236c3448847f9f1f8fb7f86d3d83c0cec30e5dae77e19984fe61a7ddb76ee70b140dbfa985a36a76b20596a9c74e9a7ddc488f6322d6a7930ab11f7823558dfd06f9d905ed6749034b3aa6828fff9c2c294768d3294bce27ae51f78f67317eaec7d414dad25b9bf651cb95e01b45cdc9e88e3e97dac9c0441f28630490a37a4269a3cdce063007e538ef4b26ab3e79a6ff8810c826a56387602b221b97b299a8bd400b4b9b06949bc324dff75a4ca605bc8d222a18885601374e63357b3df38f8bc806c18cbdd360f8501a8d50c1345afc013f000372fa9ddce0cd19c252d293a549b5372c973ab250170a1990402a68697e5ad3c7cc3bc29f70360680c7c9fac53a8703035de6b1a0b4f3efe44051dc5b94607b2023646369566c9de992e1d6c07ab06872e3e0d3f534e7de0b2d3a54472daac8c49c34432c4ed387fbafbdc22adb05f17aa22f37651b0ffd94a71c4335f4759481d39ac7938e6b3d5ac6dbf2e295cfe39169e4e3b12e4cf587a6ab24bfea69e2223b49a9bd5b6652d47d42cc5cebbf152277da780696c8e1c4d5b62569d1b87aa26a0960d6f1340e1897c6a5088be9fb0a7b5955b0a98f85142b00283ed251137a51a937fe175dbfdfe99e5fb1b0acd95b0a21d7575a23956ee96d09c16aefa55c053cb4b69fd68ef7c84d37f1135bbc470f21c4be31e6cc169f0d742dbf4c287571f9baf847be61c4bc7d171432241a9ff709f68ef6f58933066b00f98d2005b9903f05b75f50593e2f879fffffbcd813cf2b83818bd6a94fd3e0fc0c28c9bed530265ec70a7894ec4148cd9e3298ae4f33de82b5722706e5942903a2c52fd731b1767bfadacc95db53619e56e531e98dcee2f9d40aa8155211cb7533adffadbe02854db514105fbd809cf974756fad4b8be94160687d2a6a0f35a807964c887c13eb1e5db9aedd9074ce2ac195cc2ac6e33a45dbe6f4ad0048db00dd6a4b38666e2320e68034488442bd0041539887c2abfc78f29a698d596c53058a354780722e9b74c37e3931280110aab0966415da26c6c42f769ae7e40db2560fed1bd9bc03cae9930b349f7d11dc5269d2c3b2a5888413f9e9be5a851eee7474ce1d0c5106cce24b9e3700d14757f8d7a1dd7471193b7587b192851e867b8c1171a0189f07cc4ab7022a6f19cccaaea6a1267f049c5996cce57e5f463da9b6fdaae2bfdb6f965109c60ee9499c22ba5111a35557a654989111c5330521962d06838e43e00bcc34aa3d03ff95c701b0716651c6a18a510acc6f5fbd56da34bc65f89c59d4a0acd55d1e760946ba5b35007e02a06e2c43ee3eedcbf5cb2f43ceceb22b5038074a8a16b273e5650c357142ab66eceb0e6db50d89797c05aa2e0a8c3b1c6f3c92f1980ac05ce1333f96f54e03beeb39f24b81ec86395d7010b1b170dc46c6b425f1f2fc7478dfd83b3141a05e5d3864ee2ae492ea18f76ecdc31ce6daf88bb9583e1d50a220e16c760856dbfcf53acad19103841c39cf2ea0cfadc7b5c320efa1a33ad51707b10a0b21f2d6d14e41aa07a9a71f51f4c5efa62d5ddf4b920cdef3a5a062299ed029919419859e5cf5e54c77acd568adf29320770e2d751cec116212ce3de7e609c702297f61e18e189f5220fcf6533de62def1367295ff0696da3225cdb7bc58830293a202d22619b016cb9113135ac2d5217e8bcd47e5ff58356d77703022a042f750135a2db19782dc6baf20f710c3baeb95170c8f52278fd2718f3258945fa6aa4a4e8f85a9f403528b82aa8fae236d74f5a8abb3871dd274977d5916cb3acdc53bb357fc960b332a7a71a7f644ce345d4613dc4f0af052aa90137a76ed346b964938246c72596b69727597d811547f6c3e50a8f07c486c3f4e8388fc9349aaffb1be9e6bcf65897e752e3a31439621f12e69a4488d0b45e1945994a023489b6a1067a85f1fe06bb7b474fb7c3c4945aa191a87320db54f92b9d1758fcb8f053d5f9b52756720c0d0faa23372f1926e5fda819854bc24ed810e9674eb21dbd0d82df4274172c949b3d83a3ec5697c066d6b928acac8a74cf99c9a4d25c44e2c6d749e9715d210fbf6a345c86d5b3eb1196727aebb841336503255edaf262b8641b3811ab3b1e83b4f11fae9cab0594010cb029c504b3c926d2dc3b0f3b95a43c4f054862284f0051c3eb6cbbef96270c340239aff45a635093690071be1f0deeb1e1f18a94da1d4c8d89a27e17dbb4d92c352004a25ce04994a2537ff1a404d1ec52c16cc366103caa9b8803647552234f664942c30797817025c31425c2ac0c7d13ca6ba4156386f42d74f7834c3b62679d5d512611c32f5532f53d9b265f3898c52fb008207fc90c2326ee30310875df9eb1cb3a5159d07210e2c6b01368163c80440adde303b0ac818723e70e5350125ec9820825ca4b3f249736292d1d19c4602603b71a5583158538a890955350f86bfb64afabcf2425bb042f17eecb44405614fbe07e39c77dddcbcea680a4d91716dd0e6652387bb84959f4b034e4413d1ed9107bcaea9faf3453c7b9edd90cfee36c909e2799eaee321a4395cd65db5a71c2f589fc1b2c633ce289ed2bba874d437acd380ecb137c486031461c3588b8c5eb8f887c2e07fcd35a3ea702fd472c3d6a85084af2c38ab6596debb2bde60b92b21a358c18116525e18fc825904f68e1c90f3e7736ec44122e2050314905b8ff7692fa260586e0c7c8a798e9e187fc891a0afb7705be8e8a80dad29f0038d4ea272d3b528854faaf7e7b74293141cc372f044e48e84da00c006b844e3226d9cb9e80f8720e894eef990ffd0fdd65844d2e6c7b6a2e2016ea309f52758683097e21af9ac774d7eb912d0cbc5617f8c6e4abadc168a82b45834c833fff605c6c50449f13963d6928da22dc63c9659536867dac8af69cc6f6652225acaed42b0ab34277d64e353221636268b119c54b574071918d3770fd76e84db4ffe17cb05f505bc85b161ebcadad11b60a6f87f9872e81639afc1f602798df45ac7e613bc8c798701758fee10b75811933c2b34b126209eb47020f0b683d6fb5488f0529eacd7ececd3970236db9024457e3d999e503c0be2983789847ea576681fbab08adad6c32ccb49256d066737eea3819d131ee5f93e591fe2e1020c0f395193af4cd28911c8a9619e0b98d054bb692c6cbb7574dcf8a2a0a19d0a5f9b6c6b1ae4b263f613f144d0c9c140de39bc17d831411049a8527bdfb5140354fc689de55212226fd69062e548932bac87d4bdf354056d705cf0d0c4efd5ca67baae1220f4be8b879ff06a090514d694dbfff411f6e3bebe69411ba206885d0b4b7350083061538f62ff1023bf853421ffdbf75f408fec7b9a38ec272145cffbe3a98351b4791072f03ddbe0a9720dca599c25d7529ec2a5013799a5ce3966aeeefe22ddcd685eefc5a53c26fc55da43f82ada897d5ad5a8519db8ab3d8f8d21e5292d588b82225456a3ad2707d2fad570737343758518dfa493c0573f68bece8f5f90bb1dd63ff7b76761f2c9b98cbe0d8b30980644a38f6b40864105dc6792aacd53ee23ff2d4a0a0d6b12ed93340c39f493c856f65f42a9a5cbeb02e09435382f157ca8d2335d0e471c0f231f51aafda1751e404130f10dea7bdeef7993654ea03c9f4ec52edfc3820335f22c8c8da04d2da4f884ca7d59e7d2e9d1e4d9689d05ca0ca9197dbd6e2fe39c8f865ed2a2d2a49392a62258e7fcbd387be1470f08fa98aff438afcca0ba1c6364c960894c87c425ccfd066fd2f8cf544ffa7bd63d0c195c6a2e1528f7985500010d8948ca36b5772f4d2451487604626252f9098c1dd052fc4ea4df7159973adbbc3e04bd70f0670458cab9699d6ef0f75e5f45b9f2773c6d65261dc0a09167eb7fea21626049e3cfc631fc986de641ad88ab0dfb9dba6f5597350074ce971a2b277eeea80069b78c9ce6d645b3471dbd88e986a6676feffc441d640786e3f979f43320b24bf071786d517534200ee825c3ffb2c19894f8f5fe39ba4bb2606f2f080aede0a3c560a51a65748cb2dc863f05c4d95c6a720ee14eda00846b7cfd32fee2e7648d303b17c8dca0270fda4819993fa35d8cf39db0c4395b7cb498936f418a8259bff5077068ba24df3cd0b2c0198d6464f50548e63b7aceeab0b748543b515805cab5eca20247e559df0573ab2f8e5fbf73297e91bdef80475c230304d1d6b8af363f5bb8fb6cc6fbe0ad3d2ddf66acf13083d299087bedf7816a119494003ad127aad4615ba4c8d6aec0bb995eb4d70adea6eccc8c9312f876b6537ca9f40321d7c8c5ffbf8049f6df36036b08bc046ba8c6038eab7008a11dc6a8e5601ece35a732757c6c46cc23229818da7b8746d78b97408e6b20798aeb03bc7fa75dcf2d0ad4fc0a17218d2df5215b1dd2473537cb58820f86f003c086e792245855629b0bcc104c3e758c94198dac8ec2a846b82e31307e4f25a015bbe68a7f14aed784868264391145a55e5e1618845c1195af3a1da6b38a4239a502a016230c153cebef12b2d580b71efa084e0573bec6a52deb85c78ae63c43e215679318009725796310834e79499ed29211233410cfe85398476362b57109a241e23f63b6ea2c9db8c61a708f555e3cdfd7364a847000c81833d7246d258e5eacd36e90d4bfa4b63eebb36cc91f158fefe9626e72c3d0b220f49b976d702d75f12aee012e506822ec6e175639e00822b48cc28d7c897afbfcefb0ff205a9a114b21412e205fa7af7a6e90468d1db114d9787dd39eb4b030bb3235383ed9fe13a8ebdbf82ab761d8cb5ec8b0b66f73f1fde9d13b87be629a89c298eab6bcb7c3cc88a0661f7f5a1472392f7fb34615096cf5f908c0c02112bfd3297f09d2af01046724245f959d4ce807a7284968da9bf078fefedacc77e2678726a902112be0444e36883f58265c44bc7d99dde79dac37296c29702d627cf1443c13689638eba361d0e7515209b4bc5f7c0bbedc01c633a3c356d8411fbd1b37a8850d34d402ed5c9f5c5e7aa09273684dcb613b2bdb2cc8cfbdca2d0c6038c25d5119523db842c7aed3059f2775723f3b49abaebb24ddb4e4c18b29ef4fc315439e7d855e2506502f2736583b85f284cbc266faded287f1b00eb1adb1ba2075ad9f5225e38d0648cc0fdf5872bf6b80d349aeea96777cd5a0f24e06f2f361e271040fb9448dece190359c509c20368aed02290f950de516b1eb505715dc9d401cfc3d1a48e98e87b64399de6405446e7de1d51efa72b75343fa93feb93b8b8caddfe19925b87a234eec3fce01a93099bcddf42d327ce717a04cd8ede487964cee353e33d4e305be6a160bc0547bbe48815605a495e2a88501c5d3e48d1623fe4cb146fa7918a7be730419368735c38bb6694557aeb8eb2184d4001313cf45342d099f015ccc52a684867d7cbdcf14c8a0a6dbc9152e753470854f41ffc652e0fdd01abdee9a9ab7b8dfd445c3e4b02b0599e4c7c9b50a6e0a27ed33e713ac92c871e0f3a5e45fe58e3febefed42878394b1dfef324f29e26ef69c13bcaa3fd1d04fc93bca30e2f2c4e042df58388823f47fcc8b180f6916c2d78ffe6a50619334bce731597ce66da196ceaac06f34fa9251802f0a6600444db0933c8a4e9ba44c9a3262abdd160a242bf66c48bf78b43e15151fc4742c5c2a3e294d55b96aa7d322cce804aa0d0275427b5fb05771a73bb7afa6e585e6cd5c8052ff9c162ce69913bce37cff8627103b048cf1ed1a3fb1cb4847798687b23c75891fadbf79c382afe85c9f1600bb4b174ed1bdccb5d7a1738dd35f515b35d7f6fd7bf1e46a0cc2d0b8b4d4ef110393c1595d304d8987de7b91618232177b9fe58c07fd312715e4af736c77c25cfab3cb59f58839b5348154e2406334cc41eb5fa02999a439e9f74e5eebbdd33b77bd84ffa471eba7c498317ca66536e6a25d69216838abee08c49244af93df1ed9f08128ea4d16e6ba7af918a7223043f3788a61755457cf08775d59cfeb548b36b8760220c6c89806160b540e1ffeb8c2913e09a34041aa53ba3aa0c037a09c961fce59709aea16b64c96d77c1c4048c6ddc15e4f0c03ed83e877a56d244c180b16726aaa89b5d0b70fbf5c73eadaef6161f2980aeae7157815d3dae4d72f875196453cb1982cdc3fcc7656f81ccf0f6ef4ca60dc2dd484c890e51179af43efcd1c297be444c8ae199c4bb11d3052809c52f8a230126b7a04fcb161a7dffe7c64234d4addea8a6a34e171b939a9d8dd27c91272904d34118d10218f5450c864e1bbdfed14a7dc1c57f83cf08026ab202ff11e3d1f44e3e6fc09c240c8c45139dbbf1b2479b8216c42df2ca42b22c596556574cec9f32788c5d2f065283207ca0936fda575e027803b5bdf09523c337de0c465fe4a4fb36159b73fb3d6966902f1ba626207d32ffb532ae8a8b7215417a227e734c9ada1eb2a3a1c31c401f72b0aa18c3410f19ab9f6c25b66824fe17b47562c2b52a363b9ac5f046833016e73d4633cdc47557d6d438b546c76d1245655243c183015a9894549b1de890e95293c1c03f32a7bddaa1b81c2f426dec398da0d254191f86afc014d6f450077de85439ce2ff3801d804aa060a89884eec647877d2377086e7f07caa4b022cc28dccf25fe878c798b9d16d0875a47bd7f34defa01e5eb82e673294c62e798a8ee19cc80eec949a9174ea05477e4649a47f51504bf0a70efd64c3f20e8d6a32aeec7f7dc9d3ec66001caa3451d38c77aeeb732b6588fdfc8d1f0e282c601798664ca38a4044c21584ea21b8b8ca508e7dd6e5c0b877921813c11cd5809c484300e37041869d25b40a9136d4f334c677d23bcabcf65871e60317a1fc58a474c78519d59929494d0e536a2261c375de7510ef6b053c47d63c57b756829b57825d7e90d45f003f365239258f1c12c643c62597034f4c38c6fb7defd07f689e761befa6290ff1c795ce8ced2764f9a7036390a04628f7accd73a0bbfbeaff66ab259b629cf27b37942ef7cdb8e5f6cee18147deb6d96d19aaa7d2af5adeb78d797ae6e088e75f70e03c89c14530519b25aecddcd88c87231113adfdc6f2497ec6828d9490f310bc8462b8e84b15041eb6c3b61a24fd23c423a62ada1b859c7ecc82c166e46a8700382e116e2b53c00af633988d9940f5bdd17f8dfb4f37615d3f9ab2fa46c37d7ed6540eb99c52900b8ed3e8a2a662a575c8178ffd6f204df7d1e190080912502a6a020211252ae9fb1717a6ac25865bd106a0180c608d6d08a748286ea618e3a0087225f330c4dd25fe07168b84d19614267b82cf40b22cd1133ca6a6eeb2365ec379e1bf123ab4cde8416201e758ea823ff872b8f7a120defa9efb9a3c7bb1a7ffa21ea7a701c07d0d9d824af1d1ee633f80e6e82c1db209511a099514b8a4b60fe3d678a3c6d3242db82898727e884701da109cbd1189b3370e874af08e78c9631589e0ae2147f720b0a06a9bdadd0376deb022d9e22055d5638321875f0b49ae043c8f7b50c3c4b3221dd632ae7168766850db3293906cdbd1f4485d768bbc6627225af49c290c043969e1d57c70e3033c432d2f20b0d1534dd80f86573a059c38e461887f8e891b49ef066b500085449354813210ec150c661d47556a931dadce2228c41f44d9d65ed0f4d8f3be62e2902cab45acc734d801fea051a04e667f0dfcc4239812f034e3a90b5f366d1657c0afe262aed4f072d3ea3c18bd25b98a91d1653622803d9e4e670ce7274266c4da17d1f823686b502d0cf62fc4398268cae400391fb3b9db102a397bc69ac587e1b758efd12f279ba492536620fcd06e4b26c047491f6339954b4ab217c6be35b61e9da6b5e521c1c2d64e16a1842ebdd0498142b50a3f3fdd0d83756f45dfb1a5942da50d6ce02882af71227df6524c82cbb3934c579fda971e2df265dc28efb694a12d2154e4eda2baf9f4f93ce9e3117d4c21003b0a7e25063d6533429caa14fd2910e7a212e426e238f25625caf41adde91be3c0d8dee3515a1468a8cb47d9a87c624d4851a737062a80baba2c229ea349b79cc353ef385de17453831158438778d895a7115742ca37770dabb8c39cddd43b584b3b7fce3733815578cf049ba91d15aeebe6ad086495150a4511bbca185a949bf8b380c87d07e94b4b24354eacc2acc627a61353dd362da1edf697cc29178a880a9a8fd50f3402679b356a72c8cb305b34ea527ec99058a4ac2adf58dad0ad4c209be24c6a30579a24a9f9e9a7d25793c23d9e80918c702624d01b99be69e17d09d161cbca07b965fcced2698d1c3088756475ea77683375545a36b885855a895d686dc0366cc93fb92695078c9b211ee9ed5f1a88726d40d39df35c5702280244f77c7c3df7dfc06ca6828306286b4e037443904be410b9ea0d39b6dccae6788099ed7925d4c67a4dfb83cb1f4c2e7094cd10e09c2df12b11da9ff30c3607e5e29039fcabbdc1283b23905ae9275afb4909c442e6ef8b26a34cb29d1e70dbc26d7b9c42300a98bb9330edecf370d072099990c5158c89939b6ec20e4b04846715827bdf136bc185df993910f3f4468be6e70ee80b3d3cb29930ca4efc5e0d456bd1e287b0a9b5a6c479af2b6a9a7cdb27a9f9e1b134d764c87a6d1ad6e63fa7daf3230b141510d8c91f20de739acbd036a8aa543294cf996a3c10205eabdce5162c73bb93de1d019f8efbc497eb1ea602c6533a60374831246f45a154406e684e45aba0596a321e394b5c00e5994678e78bcaf4310bdc405ad2e827c2a13852af013b43cb7b9bd2192ec1bd448625e81c33b900c2adaf5a4bb3239f270aa402bec59afad65c3a77dec0027bb7fa7472a90b2e47a3c260533f670b2c1227914129dea08a12cd75720da6bfd6cf6a63c9cad5870fb66d0d300753d08c2c44ae70a22fa91697bae4f83ae41ac044dc83d2f71facb0101b433a631f7075f4305989843b005d14af571d322d62ce10e1844211e41d889cf9ec9b2df0d5733ca83a5b4b5c2ff701d5cfa85719e5c2b0075a5bb6196c05aa1cfee818513d311dff72f9b65fc768c7753822883ee914dec8c11ef253d2f16668464b07a2c754be610805922fea0a1091025b28b6f381d7c8fdacd2b450a444d3c057c17d7b9b7bcd688dfeb8e834e22524a767961897909c5bc68c6bc048f7901845e52e9bc7820795e92dd238c392ff145b7edad47f4b279d1c0315a180878ddaa6e828f75b6eb357546c3eaf8d31d11927729dc9b1e290165dee465deef33102d02d1e88b76669e7ede6d55ab6bf75677c1d5b581977a66e8f537f4663bf4fe334fbde977dd5f0c5a37c94da6fc69f8834607fa7a7581992f79fe9ec6d29d83f3f74eb112421ff3d8e42c2ca9586b9f05bd1f644b2811111cf02e53b92d7ffa63029423e1d6881be82086ef54aa49d6387c764684479d8631829babc9d2ecad63d2bf1e357bd751d991556e9d051a8063155dbdba509fd8bc56f3b9119c57e8f4190f0c1ac7a4cc3be2a95df701bcf02ebd21fb633b03a0e6e3e672e70b5b77f501614e190d5e7775b85674ae2fb9b41dde970dd27e63067f6b770ca2f272a5e8e5801ed4bcfe6214c4586dabe8257688408a151f876158bad9d9d14b0072aee50fa6a07524276072dc2b12a04bd04e6b6f67e706ed4947b6ba67c423fb922c29880b6bfeb9e9c8c54d0a340c6df67f5fc0198cc5b4fbeea12f8d535961e0f8f38d421adf7a64d9d3a996064ce32dc34a2c6aacd743c3624162841577e4c16ebddcbb9f7442bf4493f1627c4004d075b07c6c27288bff4be9061b7063a0a2b3cbe0010d1c092d81b68b1ec7db3dc7406970243dd8bd76e2475161c325caaef755534e518eeed7f2b767c4c715e39c577d1961c783c48c00713af2fac3f747a03183a2ee72d1629acf409f8a12e867ab93c30df97df6968d64e9134368b8e7ae99fe118a87517711bf5a6d3c06ec28dedb337640a88cff7ad4f6ad745db702e8c7b6bc95264ba088e4879dde0a097de416782c17ca8969f8a8fb6f4ed3b3f11cf8aea4bc60e59433eb30268d4bc130785b2332dc169b68ba3ad8c01b37e16821e383043b4450d3b8c689c0f805b568ae18f921f9bcaab28cf3c96df2861d633f03173b14a8ecbf1541aebd896e7b35ee7d67947380b7e968e81178a57a78dd1baa1500e138514768170ae1561770dad26c6fccc77fa3ea11482dc6dc7396a85dfa66f13648c1403d0552c04ab2d2962cad9e5e63b49a10d69fee1b978dc197e8f6210d303c351b26a5b0374ec514372cb0711519cbceffb7479aa4caeaae02f3fabb7e7083ee5829f24a4ae8064b8db8e8a5c70f4650d0866924def19fae1c655104313ac0bf8a5f06e136185f0dbf9e7f568bbb76648468e6ed3459c9c233994f6d72788aafa3fa98715d62887d340258b9f144bde735316489d84070fca58a75e49297e38bb2bdab02d06114c4c7cc28c8823eb6e3a28f5c8c2a463c1f3412007a3e4014cc1bc20a70278a3ccc7832965146c12427acfd477b7fbf49f5f7699127edaf6ac225fca6c48114ebc1999ae1c5982c60dc69704366c3831d2c6d3b1dc6cfb7b087bb31ab2c38fe107fcbee32c136a030edc25e7de13e411ee9ac17f63b201cbfed7064883cec1a0c3345c104b79a1b1551b604238f4205a04a365fc4de4dfcb2ae819414bb1956ec4fd46549fb66b50fcb337d55efee3116e72df6eaad1d2fe6f8378113e3624f02638f5ad8db7d99ede8462fd9d8537d3876a708021e3b66c5699ac789a4e8801f869d5006c944f74f8a1d7355e283673a1b501ebbeac5ae534d9d9b298c07c7625ed70bfb9d4549a7875361010a0466304a6096f802b263ea9c37686493ca7c03b23f07d8393f00907d0c7c8f285464c04bf280ef6a4c47665a9bfeb87078696d280f8e556eace0f28d8a4f21feb795d88ff55295793d86b6193c7ed4d67599de01770ad383263380211023194feaaa052225654d307c0ab5cab9524fe872ca499dd31b0f0eb87b57a58695e1147732cbb5fe90e993ba6df8306d8d1d4a7d8e32e16682890ce68065b46404fc7d87d4c8192d0effc8392fd958a4b456af757a9a5e21f80e9c96aeb08118fb3f2c0e795efac97832ada1b752c28a6d47d5fc4f62526030bb0c4a24631e7b0da0d612e28ef982317d7e76870b19556543d5ea8282a8c3def313771989296c69ed9ec8a18bbb07f9a96152c9a4c2d12adfbd05e7bd1772ad96f6c2d47cbe31354f0190074e9b5c2ee2ee44fe34361b939d4a97bef11f896eaf36e268930e92df1030e0278caa4538ea9b08bf0bc21489b786918f5d9cca013b720d23dfa11c199eb4c811d05881a803a58346a1029ad0f36c1b0964050116c3803f7aeefe5eb1f9e734c534caae04d2b648df92098cf25203f5cf3db1d59b355a92ac7b680d96915b32da237268c451a543f554878fadd66023444c2517a7dd4644ccb3ab185554b881040e7dadfa12cbbfc82c63f07d12d9e6ca05e181408390f6b55914f03d9c9b215a6fb0b90a59f0f15d384abf94b4009a581299f1a662ca4532edb64a12f1d8dd5604505fc78d529825c32211fb91eea58e74155c7c7931ce8d1a5a55f7ea6ef12a239685cd11f5497c9c557700b4b204e8fd941c892d8169620ee8013d14b8d812bc33fe84280385a65d2fbdfde67083c37b91a54c2a74a80480f840a3c64b2b4eed3f79892d3645cc62ce36498fe008fb82e29905f7634527de823773b20729487e9d6c27bb77902c38d8becbc5bfb7aa871f55e1143187c8e4ce13b4c6ce2b407dbb00e2540f261b5b7cf64d0ff1388a36c40b02f0b60dc87c521e0b0ca0a2fb8ba7d6d9379db2e466253a45975161a8e44e03bd28fea338eb8d3a5c6ea8136f00aa77bcd8ce22ed13db2f1c2659c53cdc3e2c240f978934705e2aa5a4ca470c66df14f52bfba655bc7068bab301b939ec94beda6d81558bb146c1d59c75807d42319280174856bb5ce4b6bdc474f7ba9e43e4de14494e95e11f2db6d16a1015755594428aa32ab26590edaca060272a491ccc29c7a4d12a73365334847583b280788b8dbb590b64933cf43d4808357937ace0c0ee525bbbe9f0bf4ed3f38dae859a481a7bc8aa6dfc57d0c04e950b3ae3e2380894d5388fa8b9b0aa90c7a87e87902ce8f0d044f978b8a720f7a6c39239ee1d6cdc4a03675c8aac7fa36748861fa9bb791b9b7a7c1aa842fa482ca2d786f8ac900b472746f5e2667eb19d2c5203e0ae0d226cbc376a3a0311ead2caaede19421dc1072f7395509ecb33ac2161182657c884c01d7dce8d8cb5f134da99b76c51b1ba52e22907d1429be9d2850bc3fe13ee695cae4253dccf276d2addb7fed1ada43edbb821f91c626b2abe5ffdc14c5e46d51de12ee9c684924a87e57184b0cc7e58923df3d0cbbe15c878725adda3cd3d3d9eca47c54a62423e9b284d7364db6efe0c7feed915edaf949feae35fb5908ef6067aa5c607860da6a533b50390f314eeac241f5c079da5574fe2fdbc8872dd89de46d266677f3fa66cca11212b8f8486d4346d4cc6227fec2e6a912077aaec8ae53d30c46d7543d138365dac39c275642e2944e848b5005c7e30f2aa2cb46e77459392857f9642781d5fe5113eaca2eb9f904c68de1577d701adfb6be6714aef6c3d242bf98720db9ba0947c5de869b551458e9647025247163ad5ec3cd2efd74237a01fb456c211885b5af768ec1b51ba367ee391c0e538ff697d07fa2be6e17679b5b78871eac7e4795d530ebd794ed03ea9c406a2a1d5ad66ffc57590c16343a8fe219a598fdd1602309a63f07a0cb286931b4684d6fd42efd5bbce792a3148754053004c11a4c29a1580a198a0fe63105f7f211a19a7be3884532c83272fcda0d7b073b451c0c60b886ee67cb6814fb31c8b91b1e724e5584d664b06a5272518234441cc23966edef52572a3e22e004cc19d670fcc2405f924bbf32ae12a72400b30ceb4e5db06c90e0a76cc7a1b44ca1eb182500fe293d2de74793a1dbc952c4b0e99a9cbf1209443ec415cb875c0dca5584cc558d403ebb9c4091c97563eeb7b976b874e3a0d69cddcf6905692339a4936a094589e88c68e0cf38ce257db2c63a52d32024fd7d3eb95fc54093b7d88d3328f531b453e74aae6e5b97880aa89ad0f57c613152e97f80ac2c7c1e0e182a8db30c3b85880a09bd3e6a49da527f49a10b4a8030ac9468854305e55a503ff3c49cf9c994c0728760cf075838197587b5ee66d03931dc3d83294390cf20e6f0d36038543f98a55981580561875fd83490ec6660f970e77c77204e522f8d08245cb56d9c8de7b6fb9a59429a5145608880868086e2ad9ac7c89512d2dcfa816175aa45403fb933fa15b17b5d2fd30cdb16c2bd777b6957aa7361095eadb67c7888f4b18b18e56f458bdc76dab849590f9aa975436d71432edd924104c9ae69aa3738c1c9dbb9f12cd358e1c97fb4fff96895cd5c22402c72fe7309d046afcadde08b9b51138b56568b41239fa69331ef79ea905b86a4473eda242da41f2bb61606172ec68c506b6cb6a7bed99afb2301de39ce65e8fe6dea369da6363de5742666b47942aecb7cd58229c9679cf6399e4d8e086638485d9916a8cf70bd8998c46c6caf4bf57c0ca40a681fd9a7bf11e762623f5bf2fed8b9589ef425221d5c7cc70e10c9b1f17b524ad37155d98e71dadc02acf2a21932dde9db166e42fa9f68c57c160ca5f9880ee775af855ec0a7ecbfadee4f71f1289d4321b9fcf471f7495ac65ba1d71e39cfa9e99cc7d529248be3d49aaa2cd8d87061aff978fb43588e6dcaaf0c9acaaec985292a00b3087d1fd618fa32e335395e6485fdf6cce5548cdb9a8ceac3249fbbefcaaed553c937f67732417bb04be201aefbdf7de7b77667e710bac86201620a962c91a9e280915cd018a9118663fff2bf95bbfa8defae5dbe0b7a88ef5fc92ea14b9b19e5f47be1866c9bf5f7e0909f9fd25a736e47fc986f5fcfcd33d5691d24c8be4ce49f80bb4046cf7642211c001db9a1e6b352850fbd94fdebb807ceb33eddca3dd98f7eb3e1e7711b21085acba923906f5701377cf2954b3cf3f5e4d4a6905261889d38e1311a1f638f5bff7de7b551585aa194c206c1486797229a78c52c6f851057e37eb9b7587880fffd4a74d876fd0aaaacaa29dc7f7f8f1afa81101c6354c4d6fd049723c01e9b594fddcda412275116c4f1373fee6f8b1ff041fad6a3f032db7b5f319458eb0dc3e57fbbbaf11ab8efeae29c70fabfc302d13c780822fa8a1f62f566b5a68a484a294b3af6f6988ae4b786d77777fdeded0e45ff6c17ac247492adbdb6715308dcc2330cc2591e6e4eebd3b0c4c0737c01f7d61764b042fd884224a6a8bba4d2882a40a334685bfc4cecc8de19c082a3b51e1ef6e1c2a84107ecc62e2542baf0205ca9f321c78822d1ece49259dddb7d852b6f5b939cad7b44df5b08228dbc94dd5af54afb2d1b8a9a217935a2dca75fdee751ae294bcd0c967f6df67faaf165b8a8eeae55fd71055b563992c93aa037b1d3b9649ddb14cb4ec5b1b227bf9abd5c82e8d76d877f1772c937a22a95822569b61637e8661cfda10a3bf6887bdf619ed3230da6436f2c294bfdd8e65d2da1118f6da167f88c52a2cc3fc51fea67ffaa3d0ec3fd03f6986d1aeb23ecab736f9abcd902842d8199a5084c91a96101179811dc71c23777777777743dee96502a43b881934cd8ef14653d3afb272e209dfbf65cbe1a6d6b257d930b8d50441c584dfd1c0942dc70986fcdeb146546bd93c84513646cdadae899fb055a8fcdd831a9f445a8f2db713011c50e3f76fcca9652ada7da2306ae03f8dc65648772c931a5613287cc0ffe8b3c032a9309aeb35c2c9099df35d10698db120350728b4112eb8e4cd7aab7a29e5e9638c9176dbabaaf89272ddddedb8b79f07b085793598479a6367ee244ada00bddb3ebcb40c93612769254c5b89d0fa6b212a9a1e69252db38d246e1d54c55ca63d2405f83075fa81db986e25224eb79ce36d4aecf33b60698fe6fa75f0ef37d7bfe1688e57ba89e65022c7cd7623f5011cccb46308b7493e675d9f2b50717777777777a7e15524b5b47938b55b45c5e70115f9fcc68827f727c92d98c250fd0cd5bf3bc0ce20b126ff8dd46663fc312acc8eab27ae65fc7fe0f7d932fdc3f38144e490aa1ec48966c77f2e0f6a99ce797c8a9669ff712047e24a9638913b15d5ddafa8fe17b7f3b8efd4b4e7ed592dab6c9e8c52505ab0aca8b491ed232bf3b3323e3be303b7cf4bfabfe3261d5fc158b8f8ae7fd47d9ea26700f01d2729edb3908da1f2bbf2cbf2fb0cb43148bf2ebfbfcf3f7aa687ef1808fd8e83bc08a267fce7e646cb9cfcbe045a66fe7e8f8dd1e2f7735a06e5f739d53229bfdaefcb6c63f4ef4f95130c9a9691bfdf22d8991e6ef217c0778d8200ecf7f019ee817901d04e068076279589b00c0ec202e1ef41601ecc008076b24495b0327a8ad277cc84bafd03437b7a38062ba38f1ca9be7fc3fd650fb493946e6065b4112373a8fb387a868b76ff012ba38304d1425d56b2bde02b1808e79fe11f6d6570928dfe0bca03ddc1795c509e976f6d86cbf78b8bd014424225dab53f93dcbb65e3ea3c3ee33fd2e5b7965ad4979627b5fc4bcb934a2d6acbbb501d2dafd2424b45a8d49757a92f2db4a5e56b9a443bd2bbbc501da46f21bd0bc521626bcbb7b0ac74511f49b2e353a48450fd5b2505a57b8ca0748fbfb79827451d24a508a5c8ff8d64a5e40a254aae1822d68c3a9047d19c3b92362561194e8405c2934c9fc2081be39fa4fa476d88589b6a8f615bf7f8748ba48582e87e80a1f29d8b2e6220fc3be5c9a69a8fd0bcbca739ef22ecfdb9885d857613f3942289423b39bb49faf989fda5cd68ecf9faad8534e71911b1fe649bf734e7f551e7d99825ce73752e8409aeddd33258fb5cda0c8df26cba63832af64a45d054eb69aaf516cd9af3e13dcaa339df709af3e769ce799a83d47b06c0f9c0037a8f07e1c264e7f9617a0fe0708f613e6c11a2b0ce7cd81e0af3cc87cd51d8cb7cd81a85c5cc878d513f6cfee32697d66fcdaeeffa311dfd17f69dffb404cd528d131d756bd7b434eb8559d5c87f36c6df071f702eaaa31ff39f229ad6d8ea6fdafca70de03aaebfaeb7de9f47cb6054c7454b45bc4a2d8ae33fd5df7fb8731e0fe23def6b1a3eaa32476398dd12cd70cfd1dfd918fd03c9c54b01a07be04214525248b1822544575811450f1794e90fc64a5a179669a3938992227b78c4994aa57844a1d72f6b37efc41885f8ecf00419720488c80f7f7c90532281e66e34c7afddb37ff2dfa9f28f02db56ebfe016e07e7ae658ba47af51e9c15c368f7de5ad0ddbd5a247ef54bb2484e42c004312c922d08fd00f3dca965da8f00b9fb474913040d0c9a206ae6732796c7b4d1c9f60c0d37f922a9a97e4023a58f68f71c9705614135cbf5f38efb886247ba9432be1aeb61b48c254fcd390ceede9f26889a45b2482a0c9f33d3faf2003b61eed20e42882d84f0c97d01b6a7f9b2133e3929ff3bf5a9392a66778a69c80fa339082bb9c1680e9e68208924a5255f2a7c182df36685347842b0c005ef61712b6d73f0b7565b49fed6599709435380f3df74f81ed4c26ecb6c5555951149fc8486113602a8fd005819350b847ff69dd4fa4facef1b5a06b1a26db9fe1bd53b6995488bfec6f456bd7b10cd79b74ba061844d738fa399ccb22a8b2a20610111352ca9e2070fdcfe60ac242cc2ba27a07a7c7f99e2f7e7b204d53ff2f84ca43205ddddddddddbd999953f5bdef9bc1425498fed642217fb955affede230d3d226c619cb431af13c0bf8b4b5d1200d6a5b519f261364655f7374687ec304278b544613ec9df078135695706001608feea5b1ba2aa91c26c0c5b8f6d5defe06a5c82eb8080fa2c593da9b9b7f58bcc8ac824e2eed5bfcd7d7485f9538584adf6debb67a91a312c6ac48a6aece2901a755cbf0125d5b74e3b02bb68b70125d5df9a51e3d6d66d4049edaa6fba436ea55769302b9fbfd8afe047988db2f11949fa25aa351ba36b3fc1fd3d2a7e4d73738594534888bf44cf917e3a49464ac34f34cdb54f1cc113b2c0a28a27aa30b283bbbbbbbbbbfbce1239af3282bbbb233183924a055955954b29a5fcc5c1c5954bf9d8ab58ca97524a29e525bf92cf52be94ef524af9355d49995205cfeca7c84f59d22fe14b26b500414a1655fe72ca1baa9432c22259c94a56b2675ba206bf2762ebb5d7bbe4252fecaaaeaa72f9ed52be5fefa35e52fe5cd725af21cdc9198934278db401e48f924c96bf3092ea68c123bd80d12e0ec1de92b9ff6a8c7f3d16af9557f5d7f6aa5fabffa25b55455c98fb1d14aafdbb31dd57d74523086a5bdb5a55fc40edafaaeabbea5f4544b5f0f923dc1187782185a8babbbbe6f334232dd3553d5dfdb59fd588684053684a5c4352dd352aaaffe3ae7a2a1f21d5130db71a1a11043c134d1059106500af78f90e3629f5432a7ac6c537a9287e20fe882e369c97cd825558190ce43b7885da2a421b0325280ea93484b0c2648f4a709c65544118087f528ad089d048a34052551096a109c95a7c479a5e05894aaa573d7503ddb0a65d454b22a25e283605b89ca501d2c6cb754b0bed38c0f2582f8b36447b22e5af0991ef5a8aa6aafc8b0aa9fe2854f3d9187f4c8ad9d1683e349e0df98ce673423b39a7960d557fcdc769e804aaf57ef9b847ecb15eee514699a67613d7cb1651349a8f46a4fa47cda7fa6b1a0ce390da71488d02f8cccbcb4b216ae97964080b0ff0f4a0c56b2ec6ac4996868dc988dc547d768605a2eacc6b7c8281a8e2913655f13bd949987148f552cee9581cb231469aabbeb748e409961173b040545fed6063220e6ad5d35c15a4399e8da9e290e6aa8f5b24d25cf511c84da32acc2e63923294b22485494a938df1befa14356c8c2daa101655b18a55ace21235b83a95cdc9eb59db56292b592d26b74ed688f5963569aefa8cc9c6545fe1607aed3226b5fa2ef5fba8a522e4f78cd2d6d75cf5232350ade2904a2e4c4575b4e0a9ac80c508c0c6b48b9e21798b6dd9d26419300c44ff0cf6fb01d8189e0991511aaba5aed49d1696801f2090a8ec3dc0754061bf434265cfb10c8bd23010900ac140f4c388b131fd9b5fa81d120afb6ca3f1191a6c128265e06020d6092d5198f153b59f024b44dcd188f007b5217436a65f02f12dc7183b46bf46ef7336a6ffd1086c4c3f90d84f834b10819dc9e9135448985c992841edaf44f081da7f83024bc34db08135310b6a3f3ba1f5d8987e23cc1c3b70a003d8890a45d626c984103821054f54114515422ee650f5875558765994b5aa5795b4a4252d29ad4a5a6e5995ace4655dd76549f9efe543e2e5d631c9676a639ce20966acef40e882145a60032940e10d289f30c0dc3be6afa1d1324cfdd7da68c87f4ec39b72cdf991298411d4bbbb7b66d4db63b532d73fffc732da5d2f7726fb8bba6b996c2e32fd7770272168b4015cc8ec2276a80d5748b6015b98ae81aa93aa16bfa3f898c718a3fc527cd22f4a52266e58be65465a4282fecadbf827511dd2ffaf50924e0bcd912b7265930fd332292f9fbefce9332f5ed26e0757fbad9e0ed23c5a98d5bfd8280d37b17cc98685859624a356be6483576849d2120448df42758adc90bee5576849becbbb501d122d72d1528debe15f0f69a906cac7165a4262e559569ea56483f42d4fa2b424ffe57da09e96e4cb1739f99c12f6a5d1bfb8d012fc927c97137ab33d7d4a734a1865e2e6c5eff04994462f6f83fecbbfce0e3487c64dfef46de8bcd01bbfd112a44cdcbcfcf649380f3cb8bc8bd7d9680ec7c3db70f12fff42755c5cf840751bb47d47f3f28b72b994bcf845edf01d575fd022a51af1e54baa03e5e3ef0cacb3946dfeb5a1fcb6edf02fb6d26f2e9eb51e4af225b561f91b3dd022f15bbea661de076a6565a58787a1392bdf032d723de97da03012e9577a9028efec6cd1f62f4fffbfe31dfadbff0b2dd96cdb33eac58b67d48b9264d94a92966c587eab94f4e6e57ba0392cff428ba07c4b29622d1b0bcd2992c4a27ef4501ee226ff295aa4983bbccbf3f02e9c87dfe121e0e25d68897985a3f0e79f1863ffc418bba7659a5a80ab3ab287cd8361109e9c9c9c9c3ce9a4e5492dcfd2b2b2f21042f80be1ca8a500b90501089f48c62617946b1088d1e7d12dd1529b3ef2dc3465b898718bef523ba5d29ff82870d66dbb983e311f76601aeeab83e669d8f2e6e2db6d2e859de074a6553f995bf51f9951ffd0acd2932a2372d9e85e69446df92c3f22b6f83f42d54e7e549bf425d9ee517a5f23e50f15b66f4f1615a46fbd8f22ecff2375e28937e35266e5ede85e6301337f9bf50a2dff2bde9942e7ac342a2254991f06ff952456df8b7946cf4b37c3f4bf52d1b7c162fdc40395979af32b7d1cab6284ee1a04a5086ae66898347ac8a58556155c4f8b16dab112d6361d18a59ec526af5d6d659a05a9f23fb483b7e0c63eb0b13c36abcba5a6f55494400ea6e0c8d96895b1bd6b8b5754dc01c4fd6d5e233cdc0ab906619bbb3035539c016067b2ca3ea99ea7d9398f7f5fe3630a73a456efa313755efa68ad45cf5359d91603ad529025fc7fffaa63afdd8b3fff5374e4befdd6f9a9690d0fe46a3d9f7976eb2efcfa874acb2fedd5459d486e4a6ea1755bda43c9aab1e2e999c721b600bd39c179c3231c3dfe6f1fb76830c33871272878852af25b7942db9637c779a59716af5f0698c1ae5bb69b7c32fb975a72a234be1d45ac6a9106e34cdf9efb0a92f4679aa90564cb2a7e2637585831f261d4e40263859988da359abadb0ab3bd346dd9936eaafdca9bfa3e94c1b75a68dba336dd4d58ffab5ac336dd4d58f7a94695df5c65495fdb1652aca03d686f4757777632dc180f1855dd88561bf3ed37115c374c8efb7beaaf2c52f3d066cc5e8f510fe65c9e622dc4af4554ae6f428740aa779b40cfceaf10f1c3e83fd5221d2ce2c2231e1611e1e168a51081a279a206c2490c33749f56d52fd09b58c91ea54b47caecaa2e6fc87e6ae80c2ec38a225966b37f82041e51729cec6b41527a88c634dfdd7c6b90f9f2ae424d29cfc43cb30b740cfc833f28cf05c174ecb74a72c3830c147491a7ea441480f18f8bc797d582346d967c9d7c968341a511ecdf5788fe234d7b2c8cce981f1680ea7b966ca4cc4fee8475b8f5eeeccc9ce9320a9645433a2ddee12f568991ded3eed465b0f1ed786d32dd1e82f8ad3a365b41fcd3ada9de934da83c76673e22891a30a737be43497e21f1bc33f4f5aa430ad24fb4b5536d25c4aa22aa4dd7d920a739fb39062772f7ef9a043ba52bee92e686f5f7f74ae137cb086ba404c88420aa26802a4490fe7841f0819a262e80a9c17e1f3e626b83bc2686ebfe952089f6f588cf1db234d779fb0942412e4e8ff0850f7458fa78d59f818c2e7dc2789e1c266deb78210c1c14884324218a18c3d56e63d11931b42413495dc97f333ecb230f08055c39261bde06307b59f790a242f379a2036c91448dea8a2300c50a3f6a76c30230d860476d4ee729e76f5f503927c0f66610a932ba0d9990a84a9409aa315921e2f08ed46ff9dd56f8dffa2f657dfbdda1ffb2b971f2b49e97b1765fc6fcebd931b179f736f989ee1285f198250e51586706c8c3884a8aa8818e11e9f277e9008451154451c211e8e05f7f0ce6e518ec5a1c4cabcd77e4f3573df66ff64e333ecd27bc650f72bb0323808d300c767d809179050c0734a0953bb26ea50fbd98958044c854a2cc739d614c453959db9ebd75c511c1bc3cf33b05152b3263ead6979a8f667198e8d11ebfe10d6011fc818a59cd31b4248df4e77c37def216456a2b9d7ffe23187a16e2ff57ebc753cc2b131cf46c97beffbf1d6b86983767f9ebcf71eed98194302495af407a780c80e38a1491a8ea461a887cb920fc64a5a1766030ca802431b03c7c6e038587186cad511e67b7e1c4ab40c7cfe1c3e139f49cf31d31346d9a24cb99394739248ffdca92a81a3f4de477d8c5a2d894671dc16e58e8a34879bf89b9bc2e80182fd96048940a8c9144d96fc6e8e5113e68d9b1ea120173c7eff753b1c24929433987630955ba430bd6bae39f7e798721252fe9679d51f867bf319c9a209f5410f0c558f9144a34e7ef74a794149f9744175da3d0adc61f516ff686ee31d4e35e74ae48862566fe3a31f3561763b34da6eb4017c8766ffe09dd1bb9f7c06c7d069e834f444e0dfc1da8d3a9d5ca3a3772b809c28820fea84ae9b38c55120c211d4a2b2f7aedd9bc15bf5279fc1713a5512cab57e4c76fc7a04c5fcd66c306ad29c2650279477dc04a46d3cec376e9a1b21991da7529d4aa56af5a5f89266b448454b11a20c599d016177b3b62c380487c2b0a68643d8cfc38c683698fc7e036c614ed39d26889a57a2397f1eaa5e92937fece0f498c2d77d641527e947c5c5f41929e7546182930376342a131111c935c11d39de8e1e7077b40cbf738eebfd77b40c530934e73317a571d39cf737539ae66048e1247f600bd3b9f854e90b7763205663c750adc758fb9e1936b37fda8cae728b464caa6aa74a69f39292c867a604924572c867e2b032b62516555aa92ae51caafcd114b39b291a4599f457585a3696ad9bd2b2c2e4faf547519040412f5da8c63fb9497edc3837c9f738cc9393ea30fe5cbf29ff86aafc111482b40bbe96c1acb0ac76ff6430350c6612c8a503c12a4c2b073b23879080368602aa7ccba765e6cbb79e681994976f0db17eaab47c50e5bf74f1470ef119094487c4229b8aa7caaf76aaac52445593dc8fcfc6441aac49be3442851a7350e57b4c41a0d1136617e5472776c6c64df245305150a50baae4816098cec483e48f829c562f83dc6423bef42bcceef13c9e8d71653f9262b6f8eea5e24f57da8c66cd01a3ef52de88ad44a31631a24bf8c809f3ed54f93faa94ef87fc6a0873f4ddd745a1b2b9ceb1a892bea20dabd87b4295df99a49054c29a28ecc2ac684e919beae3e7701c88e0441a88501525b907bc0e754015e9f7edaaac6d732fa84a22cc4e02499c940dc7671e8f9c23f9d8c9b68f07bed583e2f38654989d998948da14a3689334420f757f5b1991080b84e4665a0d6142f8fabdda7e3a716fd16a0893eba3dd6330638225d0c6c41f3749396590e624f35cf49a3f38481b40521ed18ef444303be6a9927b5e9ba1d1dd18f8951066bffc8ccac7b6184573f2fd270853be94724e0954e53f1e97df9a037649b57c0977f1d1fa8340054f132cd460e445865d96ac227cdefca87cda194e9e57b1e13ef9baaed57b907e731072af7a98eff7305ccbccfa88c819593c1129132c15521ef0518149c9f11f0f88bd1dc2e4df198f35806bacbcc18f0119765953400821b4e4c34d5609218410563046580719658c51c618659431461925109e204131c618658c31ca18a394f1e5cb28a394324a29a38c52ca28630e109e203d4290b83891c229cc9c92d203084f901e213919679c65cc3d423805659e8c3805659e8ce63c39414149496106c213a4a78794cfccccd285c4cccc2d4c62176616665e61666656e1169cc28cc2934f78c4cccccccc52327b6c6116665e61292dc92a6c710b4e6146e1c9273c626666199969c7d633f3c7582a427efc2899999999a3e418394a8e1ca38c9299595a1c394619254b1599cd93971b86f212457bb9b2d2626d2d2fb7aa662f535ea6b478d942b2c81529999999995d482d2c2b2a2d525080f00461a0ead2018d35660ed203816a7f133b55caefb849e522d2c808731fe322cc89915a48f2504f90e4255576cca4e3265532114fc147f887a3183244ca07c4c342766897f432d92bb668b1e014ffe01d0642c573e1a2fa7c4484f99ae7a552b4bb51658f9b6854f95d0fd23bcb773baa5cf9eea64a1f515459a3f29d1055be214376488bef7c3c512590ec9a6052e5774d34a9b209a22a9b283af9ae092caaf491aad2c78f2a7dec54e90348e783a7cace47902a71aa7cdf3a1e55d24ebe4b5195720717455ada9ad52b788cb0000c46de80d2c11f2886a08882e14fcb7470488da265f8891cf80ce189523bc2b3931a3f892c8086b40c11232d239b7120d43c53c04151ed278cb4cc22b12249edef283aa8eb23b5a114b2b0428544576852ad2847d55bbc56afec64f4d686712a5547f3ad239c2755a7f69603ba6a47a8a8c8dada11d5b3bcdc2a6bab6af6d6163b4379eb0814972de5ad23525654b4a54b701dea6808b36b9eead329689e3e43e5e99dfed1a9e6b0d878c9c6499a63266ee2a2d962e3233d7238b5f1cec64036e6e11e16123481865420254f286ea523277554f9090d48964a55e669aeae56c3795295779aeb9ee6ac8f47546f51c6a2b9fe1a2d24d654aae6d460a054aaf678959734d79272122ac453602f9d83d97dedaead4408b36b82a776fe3bb8a8be6e4e2508b39b1088d4a438cc1f9db2d91b3e53554b75f6799f6909eaf057d4fa45b97b8ca78697d5352d1f3d75456d7eb0fb3cf862f7892646f9608c0fa3432a9bdbfa82ad1e7b1bf12faa737d7c8cea14b9b93ebe0ef6d55f5bc906c3aeebaa3e5e18865dd785c5abc22a2bbaea1373c5836d4275c584cf920db3e9966ce2974a36e04333ccd65c6bbfde8256bc2a2bbcad250c894c62845fb1a16a43c586ca8a32c0b6e94ef5795555d02b29bb6a4b429774c0986b7e87e15e5d198fcedd0e18f719907cc0820eb240a2f1a9b189d4b83591fad21234697cea561a9f8ab1d63f524213a10912a2397e184f0328d48e9b0d57c07278839be075e66f7b587d3e44d643a2122c410da325a9d39fd1925c148430755538ef822fb7e71e7bd5bf0ad21eb49b3848985b9d3bd292fc9b525c54eaed7c72a8545124fcb38cae9b1ea36d985bb70a09555de560a872c3280900f0e94150caa662c3bc7153592134c4829d2aa45dc2f47d2730ef27e93cf127036160de83d9fe55f49ba3cefcf1b7c69fa43501333f7e3ef31e777c574713cbb842c051c71f7b59a437fe187661d85fda758db0ebb1bfb91e7b7f61fed848c3aebedeb628e7f7d7f5de7b4755efadbff11b7fedafb7a1fd95bd4e29fe6523fbd18fe2f536b0bfaed73979ec6d5c3f7f529d51f68b8aa36d9e4e340c5e33a4b528299d80f17aefcf53a31cc83d3a69b99aac86624d9b326a6ea63511a01e80880a1f876ba042b9314febac4e0315c2353d38fa93cf8ce86b2e668686c9b10aae95ac64e5dead23dfa79b604682f901f9fe1fb058c60b81d3d2a3a5477df86319bd534242caf7e88df54d73aa52f55685ed8cd526b7f88210e514b9b1fe3dcda33939346e825fa464f33e89453d0a69b809be45496e82d6eff6ef722e8514af7d4733a237825e0e374158aa7ef43e502508606ffd8dc6aff94031b5684e4e91920d0472761865bd0718653d0f7113fc920d7c1f28ee295594891bec359a2329fc2416d5c34df07d30e19deba5e09ec72007b909be454b36acb7a8cefb455db454b9f5d7f53a192d59b454bdf58b6ada1d3fc7bf23bdc1be6d521ee517e50375d1ebadb771616fbdce454b6fc3c26889291427162d55148a8e832a7c212141f50075915461a4ae76f3e00ecad63c930cb3994008471b0f694a94f4549deaf141951f3fdbb08d8b76887adaa7c21d68ed34d16b0b8be933d5c3903695e5faad6d393949282338e62e874ab695105054158928a8765f65c32d6c261d4e37c131c4d8324c441eb3f677d3da18f8bc5d0c047cdfb2364196114350f76943545f98f275faad1952be94ef6f59bd6e7294655dbfa88adec8cfc95ed2527cac646365f2adbf916fdd589fd19c7eab82a5f81325b752a436d24df0170521848f4122aa2f4c7fb979e605ef5802251c3182062068080ac048cb30182b695d58a68d465a869df480e1842c38557bde79d8cb9d81737f603b2d036946f907ef704a0718e55415b58dc7ab907ff00eacd958361c6d80f723a0c7363b7c4da71c25b53106505f8b2719a9380c834d66276bca6718f578f8277423887fb8877d7808b6c203aab11b3ec34142433786347acae80f2f7b2fa525d19464bf9a8d8b1b9c62a1e6de632f3696e7614b79df50beb7969fdbcb9fb8fcca56fa1db6222d98486251254de5c5a6f23c6c2d7e87ed04fc2812bfdb7f67a2da459bdeef9c50fdc39fe5df7b9c11ef04f1e11b3ef31e90921a269989c6079602d4f72ec3711ab55169c14e74c0d9043c82fa22e554f65af61a9d81a9a8ac36dfffc7c9cba3fc0fd2b77cb5956c583e8945b1bc0d1b17b4f43f7ebcfc0f9467f91fa4ffd1f2295fd3584a0ba9c482f2e2d2b22dcaa5ea9dba9ff2fe3729ef28288fc9ea25ed783ccefac25c962175fa51de379d22d5ebf8a7a4fc8d7fca57f4a61fa524ff7aeb756098a6511d1d671433411df1b9b23ce9754ad7bf07a1b8a8f4ccf784490cd58c8804000000c314000028100c06c3e18058382418d7311f14800d8ea24882581c87410ec394418618400801048802000000002303ad0007789d3eb36dc3385c24d098c3684ae6c7f2fc97fcc797abab4b8035a7d0f8961593317a364970cd8fabd1821b97ec79a29e9da0602f0ecf31414dd11152cb4964e431e72713ca4ba426af33874c5e4086abdc75feb33db3227617fb4e1ff8f6fcbdb6eedd7d9cc812d246c61558217453312903d99337e8453d0693121a9fce76c4e491f48994721279f25a7348a5f22411cadbc97d54f5873f166549db8c815bb7f7d9634c97a076ad971824ce9a357229ec351f23850f978745751fcdeddf9caf97094908d1d67a391a460a69f556dcd68b9d9b484914b22d1f51cb68bd97d8faa269c183183399584da0adb90aab25c3d457462f8564e3af24a9120464ba992a63aae412e6d6ad569c0f6e710ba7d1855955994576d1ac9bf122aee1365b9db92a43c3de455aa516d455554e925a445b81ac1649d8141ab0e0a2978547e7a478090219b913ee499cba06932232133d6bef1dacbe69cd1aca94488618862cec4ed592a4e2fc481a73c43206fa0234a60289e4984402c94f1fdf84b869067b7537b083ef7c68ec414b9824f49551a9f20e523279795f3bc59e449a26170977d730ca4c4bae2d20922855debe2eae04b2c9fe9505ab363344ecf8839370811498546612c8a38e5d1548e704d091444b095ea75eae40ba355072212f2966987704f58a1d8ba7135142a9aeb83455d6e03548818c6365d8bcb65b4b1fc5603d840783f1d91fa9bfc9f852d28a46de7a21409690b6d697e7284e18bbde038b9e0cfa51badcb0588304183971e9c7ec88f24157dd82145ecee5164a2d7d20c3d231cb4a3bfe537ad6c5834545d22da2563267dce6332d17febd9ebae78bef3afc28b806f8651346597f7f6bf516e620a4647f2a94fd2fb60545d37d9f10b75375ec177a5d5b6a0db57f717601ceb4fedb1c04e46b1f6b103166783b87c2edc8c26303c9ecc5e377beadd7722c19c890d841d91829c2cd939b101e95b92a82e6979ffa47bf042b8512b5a20490609730e27414c5a3d26043b3c064f651c1d420566d8d038fa16c15283951f9d432043dc1a5e4ce33b685a672ce4746b4892119af99f63a43d4ac8dde311af104e5a79bb73666b29a3881c16f6f06116da7e57ebb33b155027c5a8e26c861ee05dedadeb71c1e7418d0e3bdecc6f5a10f337931853e7a5a2fa7504a5b0397da5de8ff870eb2a047b15424880a94dca08dcc517a8c88aa9d788bc4b28830dbfca7328871c70fba975039a8214a31d999ae71c0683869427cc00f29b1364456251ebfdc7f9d806329f4e11f478cde8ec4580ace29986dcf201561fe9fe42ecd04ede32271f3bd442b2a212988fb2eb149f74918ba34098d643140cf0156136567e4a910be0fe469d8b6074dd60162c519a5a5cedf345cbfce9b74ccfaa1ea9caade468a2f1ac42a2b66ea90b6c0a3c81dce4182836b81dcb6ca8fad2dcc41cbbfa617e2289da52b58e3d04f9478f8cd49918a5bf388720706ee6d0bbce5da78ca4175a5ba76465fd25ff3ea6dc924d1ebed59192504156e8b94e2a9e3c435d261d098f78c06663dc066f6981c470b5c909c827dfa19abbd294b3a1c8f2a3fd2afe39b51a5f5f6bf4083e0da0e7ac4380c6b8cce6f35a0170869e58a563decedf15f29b6d214d53d5b628ca4ee619fd005f187264fa3561cca1a89a2e724ecfe321d1990681a2db25a1b5306798f45fe53d6a15686004dc196580252c915c85db9bab27516ad54cf34f4c89c471ab8ff1017406561c1621a1037e9694c6ec1434490330ef0b71252812f24d0dab2d6322d058a47c273045a6e2470937c3056d7bbee56078af319683b550c36de01591487e87b3a674bf5edc6fdf68b165618c54cab61bace8e0515a219e1b7d8fb3e0aa74b10998f7b4aaff7e84f9b07dffc0a929643617d6aaae158b8e9a7fabc2abfd20729789593bda1ccbc0d220624a01871634f58737fa0cbf6617644b7c1eb10dc7f845258eb23a0ada0a86312693eee25895dc7253018ee4ed58316fb84a5e84b7fc284a4c0f04c1ffc90672f3b1af914e3a5184cd8a2c186458be55c2012bca1e8b3b743cec09e69dc9c8aa6f157a388a8be9e960121e8081cabf2e5536633d857ffaa4b0ce693006b05c1fc86a217612588ba2a99b26d6706269868c18f9828f0cf25da091dfa921ce779ef071f663e293110b2197c123d9916a634007ac9fb2221658c0c069be4a48e383cafbd85bdc635bc0548e896974ff5b5a061920cb4b1e88c55a6c087f45733bf30258209057064045e9c32b9685a57cc042760a4fe5a7ee486aec0359b4aba40c2b07d499f32d7caa17469a59d57f3896d1e9df8e47d678b455973b254a13a34fd2e7232469ad3b2d2cdc6ee684c7ea668fba8577374bd98ab8a620a8adf9e2a220012d8dc83430c78c35f70b1c32506e1f167e6735bbc7217d272ff63333fa0c7669d35b88c59f6103568c2dd60d266dd5dcfbade2a8314f99895c8dd376ec097d72aed572b1553eb00dedcd1070f74c1b1b6d391f337221f16e998f37a933652a06daec6ba83bcfafb7c7f6280059f8807f261c58d00da2e5c8a58165ececdebf526d19859360adeac581d4b7b2d68a73ad2d34b8189312b670c20a5b84cf16d22352bcbf6c0719573f70b4cf13684e195b373c5309ba19c60da5367c195b393f5ca043240ef956201ab8d45a399425a2b67d645bfe176a98aa953396eb04ccb3e13e8150626b17bc40102c146405874362b82b198572216e7761062b1e1e71ab03a2e937549d6d6840d23eb0800c49287beb24fca66f7891403b3fb4d21277253a6b03c8440390c85afb40031448236440ad512f5fb6c8f430a42196c131138302805e0c6d3bf90e6154cdd17bbbfe15a15ad07efde8f00e8cba85824b83f01852c7e4a346da75fd7cd8be7022d3e406fd95c0d5ddd30c90aa474b26b7496fe90ac061e74b5aa320786d2ec85ffcbfe72baa1bfd373bb30a0a2d395ae7ba00693b94f77fc89725c43c1b01f85e94bd63abeddd620bb31594c2ab067130d0a2a7d1ecda4712537c5bddf74f712e4f1e668a2f61c5d8ae12908be8f07a987f64b47c28b7601f66c200bc120e5184310033ae29d493093510de4e88542cb4dc367c434444b89f80efb59a0236e9e0001b4204f3fcc42bafc65b7fd54e2458ef0784aef44d27fefe0131c45cf6248fa7544775f879b456d6fc6630375f553f426e7df2998e978f9f334d2492bb535988c8785fd389df90d6b5e54db591e62b6809408920cb6c8398488731c1989b7e75da59c26ea1754086c03318aae08ce113ad573c53ac2116249a7846c760e533ea8311b3467e9cde519c96ee3e2fb454ec7a38cc7f8c171276f1482b223986be9016023d158af0dfd22b7e2ba9cc8dc11d7b08903f727e71004412eb479842448452e6d646f3f3be60290e85f10b42364647f0641ca481ec8694f858c4a6142b1c709ad45cb9794308843ca4256d02a5fb52f282896cf4e76c15a1b3c694e88e267411e2472a0b15721f426dd87669c41e0df54463b4f1bb09037a72647534c3e191f42cb9052bd09052af3b3fbdbff37056736627119248e0346c76044c39fc7048db00c9fee16e1a97d150d1a7017b0e419523411f3a53404bdac46c7669dec130bbeed3443b3d4c490b0713e0c5194fe442a7e30da81540e62bc0315638650ddec0196397e26a9cf6fca7d9729dcd85fa8e420085da48451290134b08bcefc6de5f81153ca156fe5d8b112a5847c1bd6cc742f8e204831b297671a82a1672e5a42ea8041e39aded3dd3d88562609f0911ad4caa9a6b6f5971317c612353a4355768d81632313a90767633c6bc06439c568c4451d31828e46826602c568c4161489b10c92ec00e43a91821ad0c5a07a292fc646209b8d049845004589aafc9e331149cbde7219d90175a023486d10315586d63877297c960d354d87610c0e44efd824da3cad489e19daa355fc2d081359b06eec2751e83ba7fe28c087e6804e0276281bac1e0e446eb9d4c9b6f94744d19aab33ceafc01502bf3465e5e8e056f93c6721a9a31d9fdbc48020100c79a42fab1c88cfb3fe5f38b540caed13c9c8dd36a4c947d5d0adeaecd2a5e4553f9dea8171879c46d4ba297ed5c293a6b1a0ec4b7068d2013efa29fefbd433640f62059f31d997f14f8ee0e8fd8f6f274c30bfbd054d791909568d7a4591c2316185366568394daf3e24463039f20902ae029a2d61011f6328b9268729cd01a606cf51273cf97150073d697614001cf13955e0cb1c2e8d754fef7cd5fba01c2a661098467019317990c088d87c6d5b2751f8ca253e2800f24ba50c8d4eca0b598c36dd0bcc2680e5748b78ade9a2989b871efc7adb518b18f9c8e01359a84689057a7b6c87a8d9b05fb16f4a57c7c23b429f5c4189ca9497a84670aacbc12bffe8922514b19c08e517f42407384ad1cd04dfe7789047d742bc47720447248b6e9c0a8d071ccf89144f1c0241458717e6d1b37880f87514395943d7b690a0808d5a07cb51239332ba096e9e1b156d5788750a190d175a201982e5a63dc0d02ccd7525ef43cfd260b3e57f19f993188e0164a43960c91beceb918f84046610eb3d28ca0575c93537634837889c6f2f0addb1f7705b88c8f953a0d2688c6edfcf1b05f221dc969cf5f2261b4a4a2bf204ed5adee21ccd357f4d87349b51a9c68be8a04b39fc15fac75008d28f8ee9ce47f5f3a4972fe3be9e737c804e6999f3b9f49db928a4859e0515e812b515aa669fa960277d8032596f44bd64f6b98948f639ad8d7126d32026a8450ad036144c9abed7eb60681bb6e5c7ef08aa1d905ab82fa0835c7a557cc3f066c2281d8f2ba289e421e86a647640e7a89d3c10182128674758b9a2c29780de279e4f3bd7962df503d6a97cb9c5ae6e40e5a80127adaba128fee561c56531d7a93eec25ed59e333807ae6f8f7dae78851723857d2c639f0f6d0baab1df0fb942d96321f408e9baf150ffba7ef0bc69ad3160260840272c7dc837715a85539c07e6a724a15d4069a0660512712e364c0e7e7bb39a544765ca5d4961f95e58c44ecc42d3e75139b1457d62368029b8243cd0da480ea34137bdc2da4bea190f07acea2a9569fa41486d036b0f7700bbe83e53a3a2524aeb2d64ca69a1669bc8bc829c99bb9ca3c61ea23e8ed9b15b59edcf48fde1a19d8592517ae68072ab91d5ebf32cacba875e6280a4b4d57fb019df16aeeb1935836e374cf2e08cefebb9f5b6418167e1d69a1e8ec7ab6357be79767d0319f2d79fb46e358b9b1726cf8ca7f4ceb6e63fa25d960a7d509c8b11ff8ebcd438e556d16d10ee43f533a758d9a0a6697b7df56218f0491eaa21da88d8fc83adb5a85b808c3a9a5b5fed596c416b2b6c1680aea404fee413c30e4f5cd3b87f0c2e9e9a1071fcda3e7dffed6231e1495c367feeab54d8b53015cb46185ae68a9bf6a98aa68d540d23efc3a1282298a78e779bf9b1dd4ebe34d67ceab9a624d9dbe7457f6bea823c99d29be18c05de66258b4d5ea12935b6f06f67d33272bd75ea77d070334b0451ca4b71df6d1e36bb81fb07bf4e2be42ee7d43dd7965f75833c988cc90803702fa196528538699ca28b34ccbcc49bff8b3807e4396027f2894e72d854b63cb4b4a04afeaa797313f564f473a780b380f413face4a8487c832a519e49abdbb22dda206347d7a29c4838f181c050abc4332ac9f4c3fb2c0c0972987165818c075ba5eb1fcd07b85524b63a448b14d2496803e802755fb6128fd4733c906dcc5da6959dbc30290674f68bb3bb603e33f32ee9e7f1740a72bb72882ad566da9a960185c519a6d347f1e49decf7c505873818ccb09e1b7c1da66779684ae10e940ee3d08a385f204ea8c35b185c12cea78e642714c102062c44abdb30ea6d0f8e12000a5d622d26eb31818cf681b2ba35ddabdb36c6ba043383561a37180b3a42d019932ef3bd025dc5045fcdcf4d77eadc2412bca602eefd7af00e25f7dc81cc95ab462b5ac73e3b4a8a30e020f88142d8859a70281b40667ae95334b62fc79ea90070673a3ea5ca8eeae08fb966e3b570e9e3cacda302d58120a292892ed227e21cf41bedb20d641640497102c72f5b7f25ebf6dd3a4c3f836406922a39e6e51c028377738691f698ab048309eef400a341eb6e3ec15524208bd9de5bfd7a3afdc95cea447fa2fd0b8c7ce0dff691ac8374923187375fef4d4a54d48ef3f8c74cabb2ea29859bb44812ceba8fcd000517369093691a7df3050b4b7ef99caece1461763ecb41897f67ac2909719f9351e785cc023866af1f56e6a69b7de01826642e8c2eb22531328c49fc5cfe0b30442b5fdb7bfe20d5e10a3de423e958315bd026cdc79e11cce34357c5aba50559b9047b7df7343e80b8e16e60583cdb5eae7765545fb0956b7bb118ab6f9b001ebafacdc854ef07634e1a6647fb2f11a801bca97b78b1ac1c1ee53c306e979906994b96f607b1ffab299485105a70b5998bbe93c9d5a936658776f95f16dfa3a42c1857ff14dfab1c090fed0f874842bbc115fe3c91d7236735e9684baba5cae44727358ede86ba5ae72f5b23a84c592474016c5745c81b578fb3cfaef2a90b1a137e16457123aee05c81e81420f9a7551790d865f58ff3a0febab16ee2871f15040427962aed92ea72d1ca97523e527b79063fa7c083b6b8a56085d1cb7271e5df68d9a5373e516e1b5ed45db6709fddcacf477bddd4e3651eff9858393257651d6fe88cb7adf4773655de061dcd71c7dac4147aec8ef5361eab74c8919a6001bc64eca3c574f55cb871f8093a6c02021205f2208e25e87d166a425dbda8d7e268637756a3479bb606eb9420888a1e5adc476f4589d63637665ac7b79a0347166940f178a163e30c8e025023192bdcf0b3c8aff690351755cda7f3ea40eb5545067c43336afe0845b907dc097767364d18861b16b8b41a9bb0c85a54237cd9223ecd7cae62d340a6bede95efb87b3a8fce306925ef140c36a48956d5943789e550c76d2c2b0a99c9ed301e20e7e5dbc9d016cbff86fece67bd0d13c4fbaccbce74499aacbb8840a4d91cd92a025f653b7fbeae904a91bc150ee0f320e34d5c16c9bb546171842cbb515f55e439b25b63143a673335dc316676bf9b800ca53cb58717ac343289df94574446392ea31a93d3d24ac1cd42ab1929b0eaef2d480a1f8a55f4d7684799175187c3db9d4a430e3025210ee809b505396f709804ee1fd8dc78d74c8209b38b3a05030c673f764de04c4662a19122e4de3aa8a89052a5ad83781e07eb8284d7664f4dbea0d8bfd755d9fb73c8d850a84beb71fd84183fae7c40d05c8c079d3f106a412feccc740a00802261d5c01af8fcc41b33ea6b755eae9f11a7fad726d7325d4f3c53df6ceecfa2b615139902abc8d809eef5c6f71e7fdbf4ed3d3647400d5d2472bc1223a5d25b4c3cc5b1e79f11b304cee64baf2f5f8ffbf6a5ca42992e92e4df215e81a5e05c7401a64d5264ff5bd49206edb9e89601e20e6760afa6ef85f65c3dd573a1405d09f72f6e7f7b12d0c3e0e1368aa2c482ccdf11bf3252722efd800b71e7694b9d73f979d3881291e4827070459a726a0960e1507262f4239b39631dc431d60d352332b8979a20bb645960355d857485878fed34449f201a2641e97b851e5317d244972758a7435e9197aa20ac551903348b107b4fbb783f6484f247ef02f4683e8d960b2d62d1cb76e284d880f3d031cf2278ba8b7ab230badfa700366a8dc46519dc45a5692f2a23622c8db54e692ee92f808175148d4229b44ff6ed9e48f626255e021c3aebc90cca7dbbf3600e67475d8cfcbe36c055c89d8d675efc1c6a901a8839ac8a862ae60412099722429c8b175bed4ea797986dbd97ceffb4cbe3cf53606e9e301aa12d729dec9f3c7e864187e8058b52dadb09c1d7047f20d3fce083e6d1deabb57d969d2add034a5f923c2a38e9df08cb4c138410887af118d89df10154fe0b0016c9345142cb3b522381ca36c00426a3237c783090680d87fa8ba51ce0be5534da2cacc47a8ae2b4b62f15ebc20547e3fa111437fa562e2fdad4716d969aee87c7f168558a5d44a2b48bfc2d5f61b9bbd62999d1cb218dc1ee5d60318d447e693c43600e5da8a7ece2b5b8c949bebe80b67c3a057863acdeb82f5befa8be2df53a307fd933caab00fb64a6f3b9fb7f09c4b59f44a32ffcfc2920150ceae1b37c1dc73b631cfe1c3b409de679a51ce2b75ee8f213a7b9b51dc74c8a6830e49cdfc14a266fdd734c361d7b711d635299bf63b5f098e23c0638d25f554c7b9e90ba76567f8f2c589fa90832186eb2a522b8280b3428d283b8db33589b8c6ff35eda4502799b8cb8bca9aee3f75fab174603e998f444fb58a8cc881cbec4038175b1b8e60dce5ccc77c2b5160df7e5ccd5a26fe90ef69b3505ac0f2decb2772e4fa251b668baa6fc82b93e7d7a763a5d1f2f3e90810db3bba976a8349cc5bdd9eab19e866fdc180bd1e1eb54520cc3b58d094b3f7051b2f4f93f3d66196777d310868a82aaf21bbc8b862d3bce13da254ad3d33e0e0f791cfa899096bdb484f753a711c20c6d6552c0b3c6e5fe6fabcd06a575a58f0b17b9e312b7717e627f4484e3c049eae578fc93e96211fb8a29c7e43e4b738bae132d6f528b86309bdc4052870c74de4f473b543fb54f19cd483f5fa8d5436113d70f9cd2426c372cefa300d821ee81bea2cacbdfe0062587a847a51a4897cf351b0c7298d7a7fb9edb7ad3c91f601d84e6753ed06770666f82f5070276101d4fdf127f069c25d9eddb91ddfbef1e22c89e664a98811b821c4ff9e4c566415a1c1c8c20bf60897dbec55be8fdade3cd3a964f83963e5480f996fe3ce20718c0162cf69c1c4548641ec94d26054aaa795718fdf5becac3de6287bd98ae461e491614c2ab39ec602d6b3e9726b01215ebdc2ae2546024d6f1d5dcd3fc8658d09a48ffd7f87ba147027f6a5841fd6db601be248915e4dc0bac05823c7d8b7b6055fdf388adf8e5b93ec43ebb8f5e33c45a310bbd84794da3f7d21f6a69c0afde5892b68d38c6a8b4bf74172b8cfc4c6c94bab25f83d239f2de633ec1ed9fb26805ebb05feba0ae361ef45163be068d3d46b73422075750a8ede62e0a9f30dec0165a2434cea422e8874dffec4aa337735f9c75223cc2b94bc89614a89a34e9e9c6e238ceb2f804ae3d1281b4e828ecc17ebedcd387a4d4e1dbf863eb058e5029d308f28d47937f27b5fab035ab8e8604071f5459a1b4a6abb0406569e0ef5008eb27da45fbb35d4662388c8e5db184310b8108edd52c8d4f0ee2ef89e8d95730a17d3dff41ca353e2b72f367ffc3f0e395a84e9a775381d4473edaf6cc81c190350aec6c639017487974fa637c67c49a4cf1bc8e3af14ab8f48a18698f04ac9ff13a209982843c09862ef1a52c4212d5e358af83f721eae756be3dec9e19d8ff869ad9505a377ab9e4825b2e713bf31b688366f33a7f88b1a7a73f527f954bb8554acb4ae08ec8a4783cf5dd9d9a77a2826b442355673c19b779826bf3ee7a37747505b85d7fb60b08f7bdd760da26c69bac8e3774933cac087777d9e95a784279a001ddfad387c384ae34d9fd190b3c85cd11fe9b98cf909e038b6d0e721113b017a9fe3579e2f045a0e46ad4a78c2fe57408167579a1414a4e97e0dffc426f97bd89c8619c9716825712d765417bb5bb361eeb6b9c85a97549a55a3c090e09fc7c2e55c1dd6e67b3318be4a2ba72e0ccd4b4e607e54aa6f7224681789b8fc7c57cd988d03137792e2928f83c716bb02bb5d26945fbefbcb1a08e701910b5c9c34c7d658d5c4d671b122ea2711793b34d8c0f6ff61f337ac06059e89ca246b8feaad6cf4a3f809d73e78167ccef9aaea7979de8fb436cc4626269fed92e0706d0bf15deb5dbfa03d812f3f74d56deda9fadb5457b171002ed095065d92c30628ea97146ef1f3bf05b14293c52da672d1568bab0b990ea332f366ae4706cd8273b917d1c90ba241a66edaffce7b7c78aa0fde4df68c0befea84d26bb34f77199de47e42d05a421d1216ff8b9c946d9fdaf96c8276569eaf81ede9e51f65fb141437db8c005161c7bb7746f7a2cd7d8a642755ea29c77c957a52132d9ada9434c33d9fdb225b33416d43653c1540399d03aab790c2e718bf798225520ccff10be6b046a03d1df4da415cc9640ad8386b80085c98db9c3abc1d71842a34273945d1b9c5b3caff1155a31324300d89e494e9abae887c218b61cbe306fab893939905d880a3c342b0f1ed3c50755ee3a27c24f651fd0b1b1bd71196a1fe8aaae32133d4869a40f7b4d976fa4593290d569d468466b8833c8bc96ab5ee6229fdc8566e7f7e48c9c206cfd29aaffff98d92b793255b28d210b973c899846b0c108aeb8289d511523ec92c138e3095ce6b8729edfc4c0e85c129b99359f8d73dd52b0072d349fbeae5f2323df39ca63a204845f83cf6572c949b547d8e8bfb892325e053c35f8b85884fa26dce0676cf02fdc1d264a63f83cc99f4d0e7467f2a482344abc1b7224d5c6641b0e9bbe539c2d7b1c74785acc163e2aa7c98dcf2784679e30b99765cf18e21ff58158dabacbbd3212c9baf7438a5a4f2165fc6fa6a8c2dea9f02b02db5ac51f15e48812fb193c7a838b15f4a33547b3cf606762a1ef773d3a3f62f15b39f51c70fb33bcdec96dbfa4b304dc0abb7a82b034e15577d0fbed3364558482e161892f9b0c27938290d8dd1c9e9de085ce530777bb28fa513031e4085ab3e9060aa23488bcd790c8fc511b516beeba0f38b54c882fe921c8f70410994a5ca030922494d5062e0c3c056a2ff1074fba19782abce90d1a7ddb9aeb08c983c096a9a784ea7d5122f9ac017f5caa7dba75e81b3bb449f22011a8d2650893ee8f239dc38964610b5b0860c42c5ce531365e13a632d4c6dbd89ee8c05ded3d7b002ebe5f5fa7da20be5302157e8e9d8f4be150896f22e3d7cee837c1ecdbe6f8705f0bad31734d2d2f364931f1b532cdf94ae9b2271176c66827f1a949292702a53b832a7add1f4e380e6b1088be719d6ef3f9ab038e8de7d32fdc0dec158f0a7c9441d79c6b7e7556254ecbf387d266863c99270b9816c78241a70720ea79b9ed29baa43da7733b959f3f1288352fcf30c9b78a38643ebf1a50beaf03842cd06e16306a6dc9b2c813b156eed0dfc72064f6328756aa3e09b8a5cbc222ba4124974ce72b6602bd5913fe68e2367aaa15c0f07e6b0dd80ee66260044772af3ace272bd50e7b775129ccdea798a05d071c4c20162b7ecaef015a8bf7c816e8f904aa589447756fcd9edd05095c7dc6df6fff2502d54b412150cbc8fb5aad08e3e755d2144b4391d8b6e47a0771ec4232a6554f11a04d7f35536f4ca7c9097bb6944a134c78b46fe7bc25ee220c0061b2b634d9ae79a7490982030e6057fc6baab40f97cb5b5a317fec477ce6b1461c0ff0bbbfab8018ec6ed34a4d39617a4102d5bcf850e23254a08c8129199a218a350c3dc0705b50b2704fe8bbc5946999cd10357a6819cb30c38c322c33d343fc597fc7202707cfb78600611551bc1e40b8b40d798acb766cd6bfc56c55d1983995fc3141a6cf1f087c5caf4c0861fc1644beaaf1eefe00342f1f71fcfeb65a9458b1a48f2a5b177c7fbf2e50ffc1183e41e119e3ff3afa8e541a764bd36035bf569ffae4f82d7673feb2f59b613ef2e8c2ceb664e10121562c86d60f8a6eb75fd2239a2285bd1fa31e7c965995844dba22e8f5c772a604ae862165a52b50e6382ab2b151b60c8798ccb7360ff329dcac68a37c1819edd6b9e71b75272bb15f16f6577c7eb4d27035285e282ba8e682ed716e8c328c261d2c556df6f3077ccd253dcbcb37c6dde18f980afdc072483be2d128b21bb921069beacb3d1ad3a753b6f729948adb41597e61a018f0e38dace49a77e450e0fb0d52e6433658539a97ae37066acfb12966853d7d067c943f2afb2f3563559cecf7f1dc4c7e828f32a1ee824a1dac8ddaa36448cdc2a1c5f50f5e669396e9f7a6986d813ba00d7c525fc9c6b5375b0ada6954fbe6c8e04ae05731077d98dcf6654a892995934c53a1194207c97bfc8bc24dbf395f84e6086375db62911b7f0f55ed28361340ed8c5c33c55fd0eecc26382ce01809c03ddae6370fc41ef7e3ae3ec478a8d5c84fc7b02f033e90bde62a7410ea2cb77d5e1f13c4605f78025d4d3bd6c3bb98ba0fcf1e12d6dc10ed8b0d34a1bb27592b42d32857c7bc4c8eaf62594fa13322f6c05419dfd36b737f3649d6642e60106835ed334db832a4bdaef826374a4372925a5dea318a955e9730934361b4c920f157f30352ebc6e44d56fb93f243aa204be10add995a50e712a962f974d562944e13e4d417fe0cf41968e17bb7a4d89476b511559ec12b92dadea779438b2b8a5d240dda9d9c57298525fb724fea30d5e961922c691d562780d1976813ebfece778c64e3ded03541f5877102107c3c060898b56e5979685b5d70eeff2667679bc9e1b1c8629b16027283df566f13bd84e0d634b1b32587f612805e010d8dd8469ff5624afd34e1831e3d718e1a3da005619b872d5cd2d53bc2016eb98da2f9c0fe8a5d05ea9637a750261d5a8b563ae75d0f20f6a3656eb6451e17de763624e2404370bc744c2500ea526d35c09f64337be1a2ed97cc5cc0d903173d3c99966c26ead111d27db195ce7ca137a16dabea5b6195a24b62038136a4103a366ff405c4510608232ec4015a244089332684fe11ba2db7f167f732bacf6004a7ce3eaa54424301cb72df418897358550f963af314abe1a05b99ec0a8270c510a581154d9e5b6f2c0904e5495eb099a98ee5a50fa7d0a3c75ed56ecad3978edab4d42521403f55b1d1f926b8c07c1d7bd703af31d35f5ca76e9ad27c25f4c7aa24e9846dc615605e19837e6ecfb4d33e8fb052ee5a16b14a1382a8179a3f4c9ce95fad5328ec6a542963530ca94a07a79f8b0191872e952a0132df06ab39593938eb3f85d1bdf5303694249f3b52d04330fd1316194ee2fed02aaf5850beda3c4de826d1e9e466e3f3008b4a76b10086234bf48da9c491e02edd9a3f476fa9d3c095835e6ec0bf8feded4e99cfd7d869c56d282c9066e0866bd3337ad9638bba09e77d290e4fd1d4a615f4634c05b0ba487f9ea372bfc0a997a03ba8db54e4a3ac72ecf897c3fe291e8c00544f030e251818ea2d872f7a25234b7e405a5c12358aba3c938cb8d179bf556213ba6d9c6874c819bb5af984e73fc69862e4088d28c2efd4c4febf0c9d49f679dbd657469e814945cea7be7f0ea55891ffe25e0687917aa1b159009e0a9fa9cbb729451dd1a00fb4210e6e28d0c8b20aa6a60595bc551d866437efa95df45ec632fb2eb9bead9c8aa67ab34222ed0a3fb3450a37f960873bc122251aad37e86510492e487a8e00081eec4a8eb7849714338f365b975e5152de2c8f6b0d4e2706eeb4388ca4ace8cd45b1d7de700547ff3ef669246e6e0d6e82d17de539522107fe1f28e0a6bff45244ceff923217f2d4121416fa4a7bfac8c650c8588c7d667079d5828a1332ce9c0dd8995fe66d646a6b5593c8870c072fff29971ab9cd64e7b105444691ecb636353f36889705685cd8099808e31c6dc706e52c2868206db79881a4c20b6e2315e4e02177afb827c34d1c7bdfaa8c506d2e3598f97d2d993a5f5cc71a7bd19491e2b8ff73f6bf5f9db6c3141eb0fc23e42e8e95e5a6e50ba6bd477b16915f93d758a3ed1a6be5ca96ce46e11af6dff02cf2b079732b04897e006afd0c8b42b22bc39504495cd32896ff4be839165cc773a0b6602a4f2a7784f66949c938848245c5fdb411915f56ee9c004c4fe0fc45928ce27b682ca13a45f58b716284309b22a98939a808852783236c79c7e2cb765350a646f04c043b98f56babdb72d4a1e9c685f1f38113b4381cc7a820cf8beab5d5a79a6110a137573dd3092df4adc0d1d12562e8b7b9716d581d2dbdce42459d07dc2ed944a0b62b05767c10bb70cb1fcc70a7179bba44595ed98c8f0ac01c66465f99491ed022b898ef20c19afcc091d48005a2bb98131d7dadbf7362389528dc39c25276950bc116c83a4eb2edc08cf2bf226747a00f87e082e654bc1d2702d64c1ef61303dde991ab3aa7421c5fb39f137ee158bab80acec60ae68b91c6dd3e7675e5bdc7ced73c9886804566b59509780c8ee1dbfc664b698e048b2c0306f68b15809e5e210f351201bfa2db3a74461708b389a5ae1f2833e5acf3374dbe6f20326741f0eb8e161ac0d14d9465e2e181f77351178087508fb8dfdafa1a64aeb8d0066d34562772a1977906f4b04b69e68267b4090ddb48cb004e5bcaee1e5ca59c2cc93853c70071bbfeb9fc50956bc1fbbdff488221f0075300400f6922acc76ad0d5c21c665fd22011e0b3051c53229f2d17cac3c4f1805da22bbefde04aeb2af52d6647f418520f03c5d4070ae985363b077f890e9e7b310c109dad7e57490a9026d5e4e33eb8718f85f1cc6f3d860723efb83885e7c9848e0b6b1219725bbbd22abd301f2724e7355a201a0a3db39cb605693f3ea6bac8cc5aac208db53bbf3a1e60a21cc87ed11d568f3c766dff5d6329fa9ea6f602e0385e4189e94bbb5e584329431799e60aa6517b3cf5c39f458c08f0d2f4b3681fc783e25ac0da8ad5aa36165bded82f01178c1808d7701b41740f04ef5f6d48991fe4fd80cf618898d9318222d5fee3410ac0aa10ea51752e65d0e6a8cd2ed6fcfc57bed8b81876a9512a52e4767805fbe06eaa2e80ea0b2c24e54ff81db06ba02b739ae6393f9f2230d2eef4a86e2c44ad3ed3d94698f6f5fcdf8ddaf5bc5e74ee0ebe60a65a050b782fc3e066450a69498e0b7408ebbe674945ef87fc5f27676eb0f1fe80833dc73b43069224e6406c993322f07585a86983af12f7a0acf2072a9905355dcefaf4b9f86a1082afe798dff118b96d30a5d63cc90c1cc329629a319659451866546384a7efff3134c2e729fbc99f7888a8f7bc6b0a5fa5bf9b61f233ec9dd98099a1015db1903e2130a83cb9a100f01af65e096a0b7cf6064049899946aba6af09b880efc6884295ca8c1cf44c6915c40a6c232d2764b9363a741314148c258c6218f1651570e0847ca8f3a052f05a149e2d75ab6b829d844c9a5c5dea45a6510ca84a56860828f21c173f199bd1a75a6f0263a804c57fea8c55e6002dc268cc187eeced50bd63597ab135ab6bacf0377f0551067258ff3f13f478b5bd5ba6f8445cf443033356009f8a9fb2bb0c56bfd9307ca0dfd60548bfda348f469a6bbd124887b25e987d022c7e0b54a3b4d721775dd1c7ef1f64f19a2034c69e5b698c32018ce56f3610ea1fc73ce275211fc11e82264673c48a40bc849f4fe997f1ed7910498434bb86799c6cd4c112806a40f0b37f5c68aa849a0ef974b83ba5c4328f50aa944e569c6bbc5124f7318f6be25f0a27eb69ce29ae1a3b03c216f9f1322297c46af9a0d781cb5d22e7a75333cc655066dfdccf135ad800e176fb153232df731005a6e4d64483a3e73ab0b8f4e2587e3d270895241f039d967363881f1f43f04e19e8eba27e9440854cb5a5fd4838ac9364a77059db2e33b79fbe6dca4f64dd5ed9bc99d1c9a440bdc9e62de1ad5792d1f8fb131da80ae364d4ee865a29ada7305031b9404dc87381e438790f25305fa800293c68029535b04926a75b6f6b1e1251f48f0789ec9f365ef067c4221cf797ce118aaddc6af5809c242214dacc515da4928bd75797c8b321428a7ddf7628c1e1b9799e01b9e5066b3b59164d2340372aea64bafd404c9f85eea047c606c5a206dde708aa1968038d9cdcfb36723455acd89c44767a295bfc13b13e740ba8d1350586ebc50deb2d1e843aadff7571f82717279fa3f443a25c18997a6f8102381b465d256ee9f70805ec06bccbb1ab935021937baafc0927059c9a46854c9160bb2595f0a596a1f1d2b9f8d0561a8316dd3e6beaafd0ae68df973a8a8c78440cfc1301b58e75290b87ab19fbe9a104bd68bf20c4cf7b6cfefd62bf158bcc718ed077f67021a09156d96f69a33bace4365e3da89aa94050541a6b7c8fb782a2a22979c3ca9ef2aeb91d5b4169efbb6417185ad8180568cb54802040a5112756da8291c10efb10c0214aecd17fe3e2624ad301ea03bab0cebd4440de83d4c56b686933fb275f9b58799d7cda6d7653eb951008703e3c2af509ee0c7c19d48cb3e8a2abc56baa73b8161add029e79d286f8af58532266785911343a401c2b02c29113c87e29488b0dde0c32cf3106c866ad62f2f1c98c3681be09d966068657e58f0b286275314c12390a4314bb7f6906a940690de23decde85d62839be1cc4867c65ca1bfc85c900ba10bfe718fdb65feb63fcd4f4e09ea08e2dd90ae1d935c64acc354f15ee4c84b22311edd07c30c5067d82208e88564958b8f2f0a60ace6ebb6a7925c6625d4025a0a4f800733eac0fc8359482bf461aa2d274beb21d71c6c6b0dd3fafa6e7f0fa2570e3a7a47342340518776e2ba584bc815b04d387c723679b56efb2b981cacae4203ab3a77853b052099015a6716d54f27dd3346901d62a4a73a78e2066c475abd2c9b18eeac52cc812c3548d1863b886b8004f089fa9021a5c4998230cbeaa9893ac897f4ef890e1bd470bb278b00d770c2f3b1bb3368922b0634634579cb389192ebac9c83144b158714c1344589e0bdcba5ce9946a640ca25f7e1d7d3c6bcf4090cd1e57f5618c19cffb6c424d8ff79ea262b68739a894da833c8344319325b660c36d8600b1ba1ec4f652328ce3f643a6fa9c42f5dafb96e0ca3c7eb6a673f49f1bace8fcaee750b4d1d6c8ec589759f6a8fad8a9c584c34976fd8cd4089213ff62cbb035ff473e26269aa7f1564c0ecd2f20cf69e6b5b59a6fd5d90397495c6827907d7abdf951a23e632f9ea72de4fbe5da132ea3ee5459025fefbc5dbfb939d46c12aad8496f60ecae5a25ea2b5c06792ece19aebc0419a761d2c62d378b362447a68d3272c069123ef6cec1c80487266925cc1cdabea1115052a7c1a2ab12830845150a9b05198d909ebc0f26703d3cb91c08e5724274fbdc30d7eceb2cd5d162f7f76eebb1166618f57a983983afb34c406b3ecae30ef49d71ef99284991aabf6a4bac08a8c99943b1cb4cb6069ab294c2401155298374605cc24642b14e1fada622e77fc5ee0630e87a0e6b5d35a74fb591f8def3044e3fb5d1b8a4f21c4cbada9d70973ec7c859b6adfe8e0fdff20fc6b804b1064e762b9b638d23ba3feb44642397c3496f259f6cc45ed006f91a7c2a75d45400c10e6c4fe977fe22ea7ca7c4047ae273c03635a3a4577ea4433fd84a8a36d6cfae39024abc29e9a264ec40e5c93fc706a5cf433c8f75836e9eaac1fa21bbb9e154af8de3908f1ef1205454de0973f16e929d645ab539ac4192995d678ece26ec10060c24a40b3d6c951448e6ae9dea569a4b53ac70a61c3f86d13d3a04d8be61cdb3698e697e6c11fa2579c59cd6e4e1f51363101c46830fe8b7cdfc8e30025b47fd28f94826d12cb6d073351f1287cd6e691f56b947fe7baf09fcfaea15b1b26cd8ebad44c370bde65908dbdd9eec9a1c06354907233ab76fd560bb78a67f4fb25c773c1d5e0c48655b6d46728430a3eee27a1c653227c967542ec23758c003b4733d8566f76b4625734bf9c962aa91dadbdc18d5fd78aa3f824aeda2e43ba16ab8f04d41b8733f7d8802fc26f32fc69563caa072304b875fcf6d8ff073253ba2cd420b27a7285c687f8b6e04af368befbc5bbc5c9ca376e00dc2c9e1711c06772109977b42213ec6f91102901b64dbaca4167e1c37cf8bf47a173d00caa9f07bc9d429bcba9c32fc1ad69dc3536a1a7ef4f9b815522df785dc9f3c9b54cf5e6f96729c656bf98a3838170905664af58521c10342c685ee081627f7148630b48e16a3c64f006100bf8aef464a9e2ea5049a6284d6bb492454c67dc4928b3e88f1f850fe8b849dcc7f7f8510c5c61d2cf4981ec6562b1da8ec846215f7b4437459b0edf58e1b1400a5997976e5484cfc644de6a8ad44e76f2eb46d49ba08ba18b7a0d580e2a0ae1852b8e5a29d57ce47547f27ac7eeb0b4639dc3d813f42ad7e453b055ae8dbdb7d69e2a5a3d70605186fa6e65d7036d18e5c76572a3f828adf639568268084dcc774df8acda73c35478b1755fddae8bd4769b80376156980fc3ee74737bd62941daf0276834e8674be437e0ca722464a43da674ca7875ac62e6a8f59422ca2fa3634cf53ba72d3b9141162fd66538527f169debdf00171972e8b37838e9652c52c038d6d44254aa33fefd51a57683ad08ee580b29f0033e93618957498d289b8858165fbd6899889094925e9ead247c448b862771dd9671d04d3644df8ab0342122eae3deb0c13f43db25d4d63227005003d0a7c416a94a666a256338a184df89f8fa397d5e50fb018e6e8e27df23fac6c0eebf31eab6ffd7a57e9a205c46f8424ec69ddbf2cfa9fc83480c110efee5f14550cdf6285d9cb70b89ecc6caedc2168da6d05e732d9e695153d223150cec236e0abe42d52cb26a7c49fa801c69abc7e368bcf133dec9045bcd33c794399d5595903a6db8a84aaba94d894e5778fde7010d8136a5c7b281bd43202afb8931cc4337036bc4d7d8e38fc439b6401e93c1fbf6c7491cba52ca408a5407736ffb223f2c2a7aceae6efed54855d53cd78f9af0869a36b629de88defd564d8b69df7e71d592f5fa1897da40a083081d4911710096a29dc8a632dee86805a38f2620213a1405af904fdaebe560f8ee1d04b367559c206cd85b348056536f3e7f5d1af982eac22e22faaf8424e617d8c8b1120cdccfb6b758ce42969ad676437e2578048dc99c44b28bdb2101519a025fc626ae86d9c81cd130786cdba806207ddda149180693db3fb9a19d2ad93794162160131e6976f2c4de53a83dcd67dcc3320607042930f625eaa3c0a20b8b8df23f66e264cba6f16f2801bca8d295d2dddd13c7bab256e3c3dd8a1d1b8a47d524faf187bfdfbfdd1ae387a671c7f9422aa66e16b6a4ed2573b8eaf503ed8fd0743f2911a8ea9435a50f0416a61810744425e33c0f124d2520ac626431958dee892b117cbe645706fe642c460f84ee3bd340bd37dd67437886c67cee84c2b4c62a532fc522100012c31bf1ca81d304ea04dc6e5630f00252ff795fa540b9414009d20d0f5b3a2e437e5b8759f9ceb4a167ea0260fc7dd4e27040d14ace1263d5000cf70d680986e93bbb9269752e8b5cb9b659a60ed54c119dd51274cd8d42263575727577421c52d02a3cb0b7a6ebf69bac1aa2632c472e03984155adc007813335d0d310542ac3705e245ee51cc308d1a04efb920f139b4964872fc7b260c93efbf9b3096484f4c1d453c070d5ef2a245f3631205f5e5f78758600509463fdc6c446f44b0560671c92c1021a68ddf6ebd52d9b7e8e840a7c62a6a74720280ea6ba75aa7465139b09695353d81a41787866a8424ecc16d09b2fdb480988e44dd9e6274382693d6fbd93bd3fac31163d82eaecf0a4461f446635cb8d9251642facc04b680b7ec8bf4d8d68a8f0259f6ed5f82f6cefe74ad3cdb89a05554b2c394d29cebdc4f74412d4ae20179ca41bdfe335051581445c08e7c370d6f11db6f885ed27a3b5385f5abdbfd4aeb987b67c2f51eaeb470db4f402b8a912298478d2852fd56fc032b177c476a143dff66f5eb34d4f44b1a4f77ad89b34615088400df5b8f9bda377dfcd31e049a31b2ed16254f5751dbb038456f37c6d2b98da51902148421b9a1885d535c7955670a8e95baa8af784cf10a8e7d0b3296aed374adaa298a2fd32831426f0be6f8f88f96f693f8a98aef82497f6f115960702a49c35edb676902eb1d71b51a6c2cd71b23c7cc95ee4924cece1b699df73a2a8b037e94250c99ab2bd8d995746df807b6f1f04356dc1689a12d2b02d695a671aab8391427ebfc939747570686446419d665e11ad9a7b745ea21ba299bd34e7079fcd5b833c8ee371b00d6d5dd54f9ed7fa6c65ee91b09f70bd8614a38ca324fea027490195905193641f262cf772872056208c69746c38cb9ab79f5f6002389e231402f912bcbfac2c4d2aba9b9d7a40ad580cf8376ac4e0e2bb11019840fff41f9de31cb97ffe8f63ba5c906da7bb37548f5072f11ccc5dd677a59ff40084eab7c718702f59c6fed678513d366f32d95e1e241d06ad6bfe7eb662c1185506571b95e84064fc687366860be79df177dff678d08255fe24877434ce35ff52bc01e1c9ce306093e05d2609d490ff3fe4132e71997cc9731471dfe913c8786091b4e5a4ca82e0d89aa6dc95435e7169796cc5928b0fe5cde4e000a0c706b11942864a870c29af4ab660b21a642ae15517cc369ccc9f8425150bc102d454b4601a2d86fbbf937abd38d64a1e7025b8f86634b5abca0956cff233e1055372e603357b062a7b74ca679d07ae8716450988da3a8cc6ce16b86e706161184c2425939b8998194c306f93082eee80663015a4b32f1e663081d509c2fbede70340a8c3a7cdc3517529f5c94a8767c329c49e347ef0e9a775e2270da6db3f71962ffbc8737fb190f93904b086c20bd2f9a0406b13a58b1d0ca8adea218b722035644200580368320bcc23f55a684ebb9f7dd3135946146c05a89d689ad9b19f75455c1e5757a83fd5cb71fbbc42fba36a5bc80c80ce119152096ed7310df44c72fe57f313ac07e360cd18e1d0624314b53958c8876e8563fb38324e88c814b1c619198408a47ead36a47fc509c0adcf871acb729b470faccfe09bb577c343b6299fc10443420394481b56de4f619cd2788a3ea758d99e8725b65022c1c68c6275a2eb27325022890ff2ecfa2446500e37afa83f45c3f77664204dc44b869476d97fb9726f6c3ea97cad8df28f83dc63815c1f9d45341a4d6b9c5e1f824322d5eca926d4fe4a4d295c5a0d5d8c16412d27c165eb39078edc86d94ba56b57d4fc6525b908bc2ed7a2cca4e69f132dd7b9b6672943d74ee0375b45370ef08f26e00b81128f37f36b64f1b5133b04bbcedc7b6117210a21d0645ad38c56ab7ecc6603eac8c29034ea5bcb6fb77f22082463cc5e03972044a1096016448351e7535a4a516e2581099f1552d5a58817f9ca69f79e07bafdca88bce349493c62208527734cc926cc7c9ada448a1377735b070d7fc32cf8bb4ccf4920597b07d42d4240e5fc0514ef00be3b49f5c6481db1bc2cef05c7b00ba92c17b7c7f96e978649d8853170b361d8c87cd9c644bbc0090c1c34eb844ab3b6a3e55d6951cf89336b6fb083d8016a44e21eb1fd6fd20b04bf577154f9fc9a1170e96e92ebff215edc8529614222b1b1b889931ba1e83a67bc4e9e139cd05bad774aac8bb7560737949f42b5988ca7c28cee6925bd06a456c1aa560b09ead0d35b635abe65084986d04e786ad0a6b743b0724f30224ab6f4f9cf4fa0a393ca753e922d192bafb1ec029d45320dc637208269b6f330e0415aa25613483d167a947c5ccf4d22be07e14ca6299f54f6614d95a7866bbdd6ac3760be8e4a64c8d452dea420d1af281288bb7700630e1b7db69abf8f7024ae1821ec810fa193e6587f4683cd0e45e53b894b60f5a67a26f2872a12d283da6f189d102b61662265d4c004a30c31308b8fdc92f37e366171b507b7919da88812f5a72d0155a2682caa1935254b07d1678c8ead16506fe101f2e95959550b77e6c08dbf73cf606b663495e532276f03574b90d5d7d0e4996ac5843c96e77a920f585ef698472be49c7531fd2985ce47cee105ab2e14a2cdcb7af738c85a8695c9bce942422ca629b160513e55e9b07bb761b87c3ba68b94580c4db890ea3939a4146f540bd65b232bcccf06355b08c394a1de5e3f6b4757fc68a06c4d8eadde012dc0af1138e470c58d139836c4568c83a2a6824f7f7668260253279061d993f8e00d513e0e4c413521d94208177d041207af24c4ced17c87552bc76820388002dd10a9fe23f9e01f087e41d7e4bc1bd526fb3c0f4a22b449b2ec211a8539a872b554f0dae47e8804592b63cce86b9ddf8241082402df5b2bd8602813f4734806e77fd65f5182140e1edc3097b6b9014d08410604792e66a76b5cad01f23cdbcaf786a808c923eb3bda6563f514b0dadc0297e807615d3dd6d586391e66a9294747b4d1f4c120c4bdfbbb5e87f21e813e2991208bb05b760e824d98a715fee7e2429935dfe6081f1efdedde4b9c98d3e81d00cc06647734ed1da15129705596791c06753611d80c8a009dda8a85102489788f6dc40a44649287fd1c3d76223eaa5b06b4c18f7df8eb5adf639abf46c19e916609a9548ca89cdac3e4b10278dacbaf67b3c9cebabe9d137cb741d8c36941f58d3d714512d473ddb22c112021368e81dbd19dfacef78f13b610c732005a7fa0c398de2071844c689be50d468ebf88bdcf1cd66284789d1293866ff62de6c7cdd45fe93785cee117d30711fc16527de7293c261d13a1566bffa39cdb0947b60a47cc7d00811ca4b53ebaa4d1c1d134c54e6b84928755ed1c417c1e4569d593a7e8d005b67987059bd4c7d9e54988b553cde194bd397d5c4792c3ae45282a55fea72abffe6e9039fad8ea7a879f9dc50509905a388c9b438e0af6b68245620c5bdac245828770746d19a25d9e28473b3ec7a815b2126dc03a6203c0618e69bd3a8499e6cd04dc85afad44808a9a030ac9723fd80d2e6ef14232e0ade3c273056809ed41c40cb186f61b85c19d9a5fba5b1d60d4dbe33615ea10e376857945335c811c0a17d93a9808282881c54941326a8803225fb0d10831dc986d0180e144e7d44e000bf15817f3c544317a7127f3685a1fcc53dfc23632af7828abc90f8b3dc148c48cb39eb00afea45bca9dd0f1589994636562a364b2f0c56df4012a664d3ff0fd10efbe4e0b58894440c014cb533284e5f892575e25ad2fb71d1bb9190a6cbac4cb2694ef65dd2301da3aada1b1d65f353f4671acc36844b3e9b6db48fcaaa8ae5773074308e720336d1fd84bb80378da4071bce22877d437fd9df1c9f03f7a0ade304498eb4e2d247de4772a80bae872bb49a27e5e58b13656c0f0479eef3290866934dc6b054ff3140f724c5ea66adc35971156c261dce7405d400ab9224a12a803455fea356e9d05f13457d5c2d252edd0f7a5c32b48c6525bc5b1f06d419dd74c3359c5edf0ced25745a0660f348f2049f9b2385010597294e6221d5d72cd4d431ecf0870e2dc7c5a80a9cc6ed34829e95d7b2d4b5d1d535cd74e4edd202c299f99457187e4829f145e950344c5ae73b2aa24c75582f947298d36b56e31657d1eb50d7a93b8683b8322c00c3c95c0c489caebbab9813e9a2966921ff5214a3a7ea6fc4592b164f2d2a835d83cd612b8e034d093ebe9dd578ddaeb6ef4a1b841e000ea39fcd205275d883409b2af41ce239de1dd5573c11cbcfe1c207819e45db4a39d0cec587d02c53f9ec161f3a226e46d997249fc5bfc5c483d77557653536d82d97f2983ac48203620802df85ac9ee524f653263dc96e321207f9e872068378ec1ac337dd0c8c506a94df1affa79087cb1743ab84d109dabbf8da67bdd2d1ef1084934a712f7fd160cd9e93ae40859e33667dee072e64bba447654e77c131171442275248c5b7040f7239eef3fc3be45cbc3b4782514ee5e573cd127480b5a05cfbbbc9b83cb1302a02c2b78f11b19a18ffa4b9a507b5aa14ccce30a9b65ba3a0ac645dc1c5d8a37c714b1712ddc1c2c6c1fce618b52a94dfb28f2d325671638aec0061a093d13e628eec71c82440119428e97be406513fef1a6bb11d299ed7d04a7a7cb4b9bc694838592f7012d659adc83607e54befc4939f4cd9766a6f2625af422f97bfd3de2d6f264659bad29cb4f4025e9e9f5ad7f452a06a958f91af1b7e9f47989f02f7f6fe4a8fd0d25577e43866fd219f1b9aa0a4e281e0d4d3248eb63fdf4e18486ce5ac9b935260aff954eaadab77226abec02a4b27a47ff7944b243b3f856878d0a85b393b4d40e717b27f301c3e652b95f92cd39fa621c843afe7752085989d4ed0b3e7acc77354481c2cd978352fe23305ff3879fe915c9ea69078e4815ce361226cea79f3054760b77c06ef0b8ee804d36c3b1e21b92961072e3d69b3d06e1ca8758c05fbe898d0ec98662fa55c4b43de142aa29da252e7a7978eba7f703e2cb7e61bb91a4070eab8bebb4787612c7973896b7b58b039a576986449318a1cf1cff7f108b26225a5c60c6cd0581618da1dc9cd1c90916b53b9ab045a71bed07b355ba1b6d3174f06ffd689b82c5851132aa803ed568955858ca9762e9999c6e80fe01e3dc01126b292c60429cf67f5048af7b6ffbe8ec875288c4ac235841fa70bc874784b3ae17072324d87349f2e67de4f1f366c05d9bc41b53fa88de5e83b63461f970a6c4c8db438d4facd1fbbf231af9ad05ba5485e0088b121ca83a08fb828f0191baf4e0bfe23065fbab2a9ebd2e27950f2c3bb75d0fc3715d9b86b032d67d302db7c760d096ff05e7767e92281fcaee66a810bd8cffe5deafe11bfdc525d25b12db75a7be55d59692f358c10f0f02f1e2ce893f2593cd264a202167409bb4e132662817fbe03b18000d35b19eab2218e71dfe108eaf84f6c271e461640f61bd7f8175e85ec330fa2b6bcd39d40368dde6c0a2942d82a5fcc216daa10fb778695b91026aba156b03c3f1da58fb73fb28d02e610170c7cbb64ee4ef6e84c41faf3eaaaee1b75305b468d27b2ad1b8421431e0da61df5b0e404aac4d353da47e8483a2bb0adda5f3a9b451b8ab3f41e976a9080e3891b99171c3fa6bb20647c31e740d31e6555e7865f16a56854c664d5ef6554cd3bf44e8351366594d60df8c774156406a5510f1028220dfb5c2ccb29b3714f40582650057db00d73edd6e22a83b48ec29ed88f56b70e2cfbbda06afa9890a87fe8c9aca47742044347b0e99e09e4b781bcfa92b2e6d17dcbf0989d509d577df85a532e75f4f2aaaea1b5f801ab47aabb368700221f1d0984bcb7f6091511eaf75dcaa694037a14bbf4249c9bea6425091a013acfae6636454fff966d97509fa3cfbe1d2e7411eff2ac39903256483cdde20c70fd28024b9ec7085df8003c0509f2d6b3830d8d5c12b88e6a768c4209cdb5ea85c62435350b9e023a0a651bfd943c12abdb8ebfd3df23302c4dda3c20a71dd5056cfa522306e330eb20ad16a5cc95f4395b0b48c2789b0cf29976ef036cb59b79a2290c746477b446cf512d1233c586f68963f8d9ea9e50bad53aaecdc604aa656b19d0da6923157546d45eab870b4e61106f70e7366c51b7a169fe1fdaba839caf3f6765ea2b3cf91ce8a19faa61de0fedb3a78950fd6b7f70a32b60562e09ec5d02e1c54012588eb232ef2734b148fd676e18b3f11a1a3f102343cc3a371ab49efbba8f71355f73d91d680b9d09b9d6e3e0e0d13b93bff63ea59f13190264761a785df317fd4783085e260c350c94b0856e0f0ec7b68f8b305ca20d49142443c26f291c828e00324c7f2a7e74bc7d6f5b68a00f001368df354b7c5e1b3ad9661194e65f973a79147b127a11505b8c6abfb80d3bddc410e88eba84cdda44182e26f1513fcb17e3b041750a8b31fb640d99d8dd9b679a1d72eddbb90ac2b8a68a6fc0dfa430a3adc5501de470d1cfbfa6fce41e49ca690f907ef0367883fce82d0042f7fde3ba2c38fe505078792529440bd8964d299036d47e0217769fee0a024f00bfe615449f38196b87ad27f991984e8db896403555ead7b7466ea7c1df6a66a9e0a168b4383c52834d25f52528ce3cdf9f227cb7625f12b0b64d9ccbb32e92b363e3a5ee66cde912ca02510462523c7719862aba56db247783ac0ff013d358abbb17497251d09b9b4b2561e92e60dc8b0e1f3ebade519546d7ccb1ca2e8bea637b3a20789df88d870ab89467499b64392c96cbd46637730662441b3d84fe05ca878149af86e55920dfa9676d00b5ea882561d6303ec0a52616107017786047517fcccac03c3675ed3fcba1bc8c5bfe4ccbb6e00c03ec3753f3f7c9ee1ff539eb11bb311e487800994daa734c496fe056f19c35841f90e150365614d0fa0e0a04eee27f6668bb2428dceb6cb9c2623b9595863cd88c5a71cdf52cfe7a91b5f09dfb2a9a48b4c60165304c07a410406db97a6af35363ef405b77498aec42a31acd34a565334c55805a8cf278b7d2876bbbf111d066f5621ed4144b3ca98194bc743f1051d8ca0d8adf55dd3f24286a32aa72e13340f84a82bc9453cfaf9010e0147d2b536395451923d5b15616ee3f61d92dded03012f10a1d98a95310cf8d199f91aded1f4bbb8e34083d704e5d96fa82cf1941c24b4f006c13cc68209de352951df9ace1f2dc2ab546bc05b0893b3c1a399fdd100a4bd2f868521e00aacaa0aac2364101f0d49664c3de70fc2ba76f54a8e9b73df3c62040ec64f550e74b22255aa60fe858d0dfc252e28f7e5c8e7f64830af634993fb38e79ec6dae823dbd0b0a5f98d3d18abd7f39ef310084843ca0c255fb85754be3d4752ba38c8d32924f929da887490de8e7ec62e0be135aa67df0c39179e6cd2faea90198aca2626d4c76b15584e3a3fb232d5c151517d77c257a7ff4b40dddc5ca4a5216098bc5908a097f43ca598f98f71a2c3cf51b77c1bf3b3e4569018f0f1adb50a954968a68f9dd20d6b99a755bb1cc400ac6b736adfe21cabf7c96e5b6f661cfeffe79c5753458e0978b37ac2d140799bcda70040d4a2dcb5fa602f5b731e8855497fedda65545eb9fb8d58957427bbdbe7c96f2e237cbe868f34e1664be413f3d665921c8446f5d2f84d940484e4b420333098f41cebb9cf397d5f857ade1727b8093bfba371887bd44fe0c6cbbb21191e3c6f9eabd1738e8e6903cb637067b217c037bdaf27e440033107ce04675cf8c5bf3526f0c3deaaae9b4708c1289e48341902db4a6f072b829b5daa555affa7a8ed313665e88036648cec19af8f64123e49345aa2214eb1c122d9de39a7d93a9a96d19dece84c7f8851ba62f81dc68544279b941480e4ae061641f7641fb64e767d5e51dc3149af76d50d6673a8a88ad1da8eaf51e4f825c5a15a8ddb146beb03336e9ea4d53120e5d733dabeb145ba6b784d42a37259f4c14f6523b89ec6c031d7ce24f912c448476dd6169533c0919eb96d3288de01ccc571be0d743bf78259043ce6aa0e2a2921863874200740ee068e9cabab72676130826b95c108dee5481d1eb37d72588865b3be5d4b00849ae8174c35670a1de92dc06cc82590a08ba40f15bbe4f7c29e65547c8f3a4f428d2bf55346fd6ec9bbc85371b93112fd0e6ccbfce194ba09b65328bef0f19a8b1fa99bebd26c421c802f137adaad734487bf3b7931998df3e8a0a0cd35832a6081011af6265a5e6d963bd02b7da49fdc411e60d03f123b3011243845f52a87989c8fa1d789eaf460eea69d33f3f9ef2a420918b0fb612b2df5823792b8b016d9cca86bdac275d69a3e2f94157a5c5b98b5a057866f006c26cd50dae7257754bad05a5c6f74be1ea3ca20c314fdb242d5e7433ed2ad23096be892fb1222c19761fa4f807c27627de098d127a47c015f748da45a100a399eda096b099bad7e4637813680af8cd6a237ab6bafbdecdcccda25ac54bb84a0c712c1e1dc2cde5e3faef8cf3d9bc9b71d2792af1c05dec63d0e4dbdb73fe5d4fbc7aef8bacdc1ef97ae0bcb407647815992b3e0db9e4bd51f89fd94edf025401299409839488b73390b7cc31d1713560cc27e324e42fc9010f174f767b17672ceb17715f9e888357ff976202476b3cb0d3cc776a350b0634774121eb2c29578aa0bb8ecc28fe7a0c4856604ae913f4a9666ece31e86d660598a97165afe5826b828faf87f67f15bf511ba50c3aa4d086a38105d6b2654ad390a9382f402e985059e3c37387472bf9682436ec546e67c6028f4e5c61234cb5172f8a48cea3f38229a1080cc3a2bfe57ef5a62c83851062bc635884a06446ea8bbbc9d042931f4de2ba3ca0d6b80f6d2e408710c98df32403a597c548288779b1a14bcb69984ed84ca9e3b3316288e44f001a5502d81be2c8015624dedf368f9bbe0701c7e245af453d93e26e0194a70aca1e7ef6a266013e29ab5128f72588a4324516d3168e325b3a6ae17490b58aa36efa00cf794ba8640b990c85ac8ed2b34948012148372fbf52d855ebf1600362ccc4fe478d0c4ad6bb5d31a77133d83bb5b6493f9b9c9599bc1e8a849bc9182340bf9819be9495a141f92430193587ed6c9029a17b7e9f8c99436b2355ec4a2b2398537c16051c7ca38088e62d1372e427469573f319517b7e820142e5719efa33c2b5b87a42625747bb04fcab1a69511477e8ebdb46d7421562f30a71f34efc839f2c9c0190c317701ed9622c7592178ab808a6c08e4c5c942f39ec5da2670131b2a040a7e61a3fe6d678243d7533b65d38241260e13332ebb4f5ab22d3c3ff6e45903eb354adc42dcc533ad1bb5ac1512ffa10f85abc424bb04b4d07b039b4e258aaa792d1e60d072c49711e236b837d7cf812fe9cc2fa82d686e9fbec93358339b3e3e37cff2f38674cb9e4e8b1b13428e84771ae5ae0896f78d5c296bf477a9f42eb1f5cc5624a498b206cb7c097ac4876cd299b810e9f86a6034c7b86480b4f63fb0ded45a8dd98d951efde94d2056d97c527c738a75c7d4a88010f6a18981a070a6860a8214f296bb80c9812fe6b7b59ecc637910c2f8aab11449f5f74e4c7def9397e1c1988995ef40f5023999e1e0550d1b29f5e455f2390a202f393ab9e503e932ccfc68aaf18c954ee5586f4098ad42d406379b062b3101314177a5e33079be8f116223e852642870d6dfd9c105968baf6d8f2c528d3052ab3a9be9e8252841b92c0bd8c21a0fef1b2e4ae8d86833b26b74b350da45ade9aa7ea7376f5d98e3da89c2e1baed8c9b641c1aed66d2029693f23f2d619debe9177b96103b42529a69e34eb07d90bc5299efa0e4114d1bb7fc880a9c6c0a916fa8e245da3645c18e75b877ec0cc2e0f81dd05af4c82a99061a8a36faa5fc0748d67824824e4d595c7e7dc807f608f094fe49b1f4595f0d739aa2aef8bf3371829d0746f91e1345501c96504e844dc71610ea97ff951e0d3c8fe99982fa289c564fbe159af5c7b46bfc8049675730b77aee79d263623c87a32d0818adef97b4a546852872fecb38d3b19057c2435a56bba007d28ae121a68dabcfb00e1ee2143780e441c8a462c0c1c70684176ed55e21c0ca52d79134a3e90480c0852d01261dc01cb9b5d6dea6f4bb595ddfc4944f7d3a1cb538d648a8cc776998439696e00ff9f85df8aa6fb71758222cd0f9c9ff45e5c659a77db28c2ce484b463ba4115259ac58b22db644c5ecce14fadd87175048ade608526b0c78dcbe1b3f3bce2f5dea34c2ba90ac9bcc0982f697d61e49aea8040c080c15b6f45c08cf62328047b7a576463f853f7757e537c70d0e6e08ad69df1a9c4b954af19ffc092f4f10d7319da7cc695c0557878c8591df7df32a8da19032768919cc5702cce5ef45ff0e8d9debf9b59f6ed2956d0fe87a9cc06a5bad22db73ced370274336532b38c32c83443996592694619ca44665e9ee461879810adb1a860fbc4ca093c9a5034e7307693b3a98c40caa8acc9cdffd81218600e5fcb0eec75d446b1b01166961d0e209ef801cbba904d486928b0a599b65187637b6d414568dc8fe1db863a0d134e57f24a9a166d53d43e83c20f1c74c0fc56bfc2c4382e5411d23bb66b65cd8b670a1b7a031a8e8d4c0e0224a0abe718f985db23046f013a6311435c801615251de9cd4a644ee22c3bc229eb8ae3c5bfe16153f42239c2191b022fceef574395a5dde72aff9856bed129bf11299496b7e46fe66fcb24f6fb093c9f459dc5607f68be35b437e7606c96b496b456f209697b932d496b724b12e20337042b043b8270648240418fda6d68503b7070841bd46e63a3f22f6b674a32358fa5a34a3e1e4bd730304b82ca343dfa47e5198b32cd1b71cdce70896b1ecbf402d3a851830c8a0349266f43f081ae6057b061c386d4a9dedddddda57a296aa95e6a29aaa9dedd6db8d4525453bdddd83be4ed795b6f91ea1a9cda7b76edbc3f6fd0bbbbab26f59bdd83a92a80408f1d11c9401f0f1672ed884806fa78b0902bc3b090108f47e47061d8f77e3e20d08848102a400a521405298a8214a4280a52100447a00884bbbbbbbb249b36225223c6069057632a400ca5bdac8e8c7b591d19e3ae65753a3138e2c65d1a35626cecc0c38d11fca063776bc4d8c84185191c3b70c8f810638c555555958a0c64518b231146aaaaf846155355d58648de9e37802db6bb1846a3c6622197d5918b855c5647625848c8ae65753a35626cecc0c30d1addafaaaaaa3a4452555555552455a8daaaaaaaaaaaaaaaaaaaea5555555555555515f7aaaaaaaa1310fa2656555555dd5c57155755d5abaaaaaaaaaaaa7a13ab6f2ada55df70afaaea31729373d18f8f5d555555555515bb8ab18a5dc52ac68e5d555515b92a563176ec2a46cf23f6f879bc1e47aa09cfcded7176b51e418fdccc1e638cb1aaaaaaaaaa6a4324ef9d6266e60d8739bc610ec99260c0eac8b53a723b1d291f8d1a313676e0e1860ebbbb104208218410420882235004c2dddd8dd9f76036480f1ebfc0102038024520dcdddddd25d9b41111203042fcf0b1222b2222b2223c7a80e0081481707777779764d346446a686a188237bcfbf7de7b9b2f8713999973e6c78f1f3f7edb8bef3ded510fbe91c78f1f3f7efc445ef640ef7d9ee7612fe45dcf7a9d271f6fcc6166fee3c7eff1beb7e9ed698f7af08d3c5e5eae9ec8ab5ef640ef7d9ee7612fe45dcf7a9d27df4e4dbb1a767639bc6fdebb39fb5ecac119f5c53cdeeeb76fdf7bfd7ce0c8f4eb6566666666e6deb7222b77762ab99feb7b8d8cc06f7fdb77765d6b41d9375b6d47162ef51edf782fe6717c92f97408a2cd116666de219b38f0d2e063736342e65e7377581ef606f399999999f912b86cae6a7c4f18e906e9aee8f2202d896266e60d8739bc61120d9436037d3c58c8b519e8e3c142ae0cc342423c9eddcf0704da1a313678ec767753dddddddd906a081b520d1b420a52dddd0d1b420a52dd7bed3d77bbbbbbbb6bc4d8204d81c37977777336adbbbb475aebad5ba4b7a9cebabbbb41fd694f37d6217db5d59d961dbbeaeeee6e0ea77bd3465aa4b7a9ce9ac321e134a849fad39e6eac43fa6aab3b2d3b76d51c4e37edba5b207d869cc3434e77777737e434840d390d1b420ee47437a761c3869003390d610521849d86d447de9382f250420821a4e076777777d7a54f0c7569c75d7ea3d31bede4a8bcae82bb8bcaaf4c2e9e98dc5a9313f954473e6272219f2a7639619ec9353ac03a32ce9d3294f8c8873762c6913265b470958fa60c132f8122e5e44ec5bc114f2d2b62c4553c28f3461cc40982a468b4dbaa9db74b6d76c9074cd36628f1a965ad19549e42bf332eb544bf62c01c1889772675639b2c6c9b267a2c9fcbcbab3c96ec940c8ac9c98f3ea32507587c47dd68112985e3c667f608644091d1f641865334cc46bb73b4dbae51320dc3235aaa41ac60472df95a2de6435f6be9096803bd4dba4d0d5dcad9a402444fcabb132a2befb66a05604ae1c2ad7817a6c984b20fc0fec400ea7ee500755f420775af0284ba4f2942dd8f9630b93749d34ed24ed26827ffe1a88aa775f233ea95813eb3e3acd89e553f7a6841057db23299812e41a0cb98b40c8b5aa6a3ef5099ca747aa35068b270cfec08502d5a32e98439b4b360e100d3cec23f9b74030c4604b4bf4e553b0b945beeda9ceaa8e3ae32b919542c80e22a9f5a3a933bd9dcd46d6eea8e2e31009b482288a9f2dd8c1a61477b9cda5eb4e3be8f93ad17d60beb1aed66d4f86b5a36c0e265d48efb7edbb6b958dcb2d95602a617cbbd90df2f9daab4ba14cf5d72b379ecc036ce4dad1cf795b3dbbaa996f5aa3c89eb71be3c2b07751f6fcd9d31d49d71a35a168968fdb22e5fc765e95af4e5d5853ade88610e4ba298a35338b66e6729a59492bbdc2685635223c800b6f18dab78423c96ebbd37bd78ab0698bc0c29905445c4e3ec7a88ca1f816204a5761031291b34c0ac77540c5bbf1e270c8b72cb2d65b115bb9a844dd3f437a29d25e3a91bd408281c8f65aa8bedcb8bbbd14281e2a38f505873238d7142654e2d28e43669385161c85b9752bb3e5db48bdf18e344a5e144ed62a830228bf82e86ba6d97d6ec4e408861db36bd8669181c33c0de49de759386782cdaf90da4833c16ee1062d8b671224abb78b9cd194a3ce5a3ab783265ae3c452586b241ed30d5a49ccf118253ac9ccf0142c150345a42da0cf660eab4cda99e984c7a28fc95c9330f859f3279c743e18f26eb78287ceb87126f26e6d4d2c2755996156322a1cca9c56359160786007bd5440c0c74300a5a3a0ef2c2566b0125cec9d0c2ca5bf88c96c99a41e59c0c291f7d44658c6e82e60079fc8004322d29a0fe4826d33001a6d12e86aafd9a9ca45a5ce7a80adebf91bce6e249aa33592df69a5d0cf5badc28e7423bc95b6854c46754b99dc9dabe4d0b5a2d3817dc5ea1426bfb6485be71ef366bb36877829ba48b648069ef0ee2ecdadc695953fe9a914b4cbe01d671496534dacd6869417469378d2827c3e8294f19cd683951ad4bf99df22b736a19cda9c5a25d0c2bf14b393e382029944b4151c1d6650b9ca3703835ea8ebaa531de1002e0928fddd57e80834b5c5a2096c4a59d992131122614be137048a1854b25f6a1ee72d7ad33e9a22c5ebd58084e01000e42478f1e3f18c863997040f3a3f2373a3c96295272670a19bd8329ef3095775b00de8d5c787702e51d0158e4f708782cdcb5ed7b94e942004426271fcde9a5e012097a42b9c4a547b9c425d830c6fcdca8fc11509db55a84423f79674aaa66eb759e48d5e45d8c89744daa86a2a14a254b0858277ad7a4995743db0e21866ddb3f1a3d0270e7e382e4f22adeb3c185a4fbf9b0f0fc06ae39d8344abbd07baaa139d59369326dd83ceb9dfb7376cd699f9a042115af7819e7621b3b517001082a45c598482f3a5170c1072a154593ca41e9b05a784e29a7ca86e99886e918f8028c2385382b08188c08c478ee7272019b482a18a2765c7a2cbc33adc363213957920a622af38fca2598b1e1c5a5abfcf68d7615e02a69f7b97cf6c97a419e8692f75c52ae79d8b099377005fd335dba8268935e2c206472769b2a77f3c2359edbd91d83b5d3d9401c64d4505fea5e6bc9658fecc2555217be42e8dc86de0bd7d09ff5620a6d6a0c71896980c0f652c28800f1524c2dbc333fc03a2e7189691e8bc8f9960fb0918b8c5c4e283342bbad23d77661462e722932618e1a6c64e450064683890dc35d52f9bd9c26182617bd2331227172a969f2da463b1975db4c80df4e2bfb6e02d3b4b2d245468d5ca50b93983d8c2e4fe6045ca669457b0f34de92427b48a17cc23fb8865178022e4c33adf0a7696529fb98288ff6f8d1a436d746e558c0f8f540dd0ed4c9dae540d56e438cf1a24fd60b91c2d8c32aba8a97dd648a6654d1a72aa7f61bb86ab45bead235bba884bb0eea87c7cfcbeb939d7d5c9fcf7566dec11c84468c8d4f76c10ece5034548fc7e2014d96cb0a92b3d80aeafc592e2bb2b3d8fab9e4744ae6562a46c479a2de554237d38a88ab78222e2a4246bd2e4ad9c84a4a98ad159dcf4b4a3e9dcf27cbaeb30f84d8514dd4c907426ca09238e083dc749e5572b9e17cb22cbbcee7945c723e979d95bc3f9d4fd485eb874ed34af68c96d08b5236b817d73fcffec22eae7fe80d5c33fa42d50c74ca5a11ba5c0b06d03f7fd68a0fed5c363be893e5b23530303035a35cf346a1c9a5370251a6f901eb5a4cd5a5ebf51bb85ebfae9743dd6725600303532f6ac315bf9389f88d3cd7cf0daf72a48b4dfd7c63bde07a5deee3e52bcba80bd78b4ed3ca8746dac38852233ab583e6f6d0143d9b26ffcc9377268997fc20bf873b600e1e8ba4bce38df8871e6025aef20ea6745b09ba9950b8ab508ee3de8823313b3f999f9bcceca219fa3641d726c9b991b975ce0e8e4019393715e6782c22e7431d8f65a188c8a7494404669ab8498b2bac19517540dd736953af33a7c5abe39a8e7d742c44b734e44dca4a6697aa26601eeedadc4ab7ca1d718f379a4cf350989530ad30236132c19f4eac3c1e60909714c40faadc01b65d9e9b53a59b1a53e524a7cecef8253d16eebcbb96a872bf2e371db93340bcd13517881260dcbb9de1be33a54ff6ed336ae7e6d69d792c403c96ab53af4c44bdae6f5e0a2e7dda98aae271d3ad1e980505eb09a3389ae151679c8089f847c073be79ffb35688f835b75a35d85e5a4db05e0805b1829a3a52a71b1881877a659a16e375c5eb8adb54936587d68a8ce3b8cfa8db2da0cabaf3e5550f7dc09ad82e67a75237d0b7f9f9354b40883c44bb374405358fcda5c584268df8093d9fd3b2cb6cb54b7a619e73a7fbdd67b38395a3dd0c15efe41e1e9b2abf0bc40235aa3cc7b266b08e4bd612b0fdfc84814426f784ee79f739899394c8c75ff452fe4d3e4a3e00827f648fdaec70a8da526efb7e3bf7c85149b54e3b767db76fbc8481bb767958af8dbae0e874c967b3d37ef239f7b9fcf599c9cbfe3c3ea31a10b0485f787eb260f85c52189f732f5c237d79a3ceaff7939790d0cbd7015d927cf34624a11742f73c34bb1caac7733d42a06a1ef948696701aae399af7a5e9074d26807f2ac89879a1c646242bbad1ed19776fb91f9b2a0afe7208f8786eb221d93e8dbec70a8dbd95a21d22105d9e732bba41cf646f2dcdcaecdd0630af850e4e54773f30620ff991d8441f413d12f11bd5ed017aa1a4573358d76d93b87f1c2b5f37729b377a29fbc06501a324a726d7658d524f7e9a5d8b6cf463bf99373281cedb49f5c056b53f4cc0a35d44f87662f5c5fbdbed60bfa6b7743bf4e8119751fe5d41e6787438de7a60b8dcac7797d13759f03e0dbec7270211fed4efea12f5caf9310ed66d4ab8a4458154d7ad004525f5ea500989d04eac9b1d9e1504fce968baccad32e9efe820290f4272be9777639e8a84b69b7a9f497011350f71d0e1978cbf450ba1c6aa8f22a98c693ffa75714405dda29a0c610c0c90baf18c19fdc7ed3491193db1193dbcf408861248b06ebb854d5a9075588421020500236e2949504cc420216f2eee4216761daf021f1123949ee38a08aea6a20ed2be415babe822b619dbfc200234e35d21bb8ca431d98a42c040fc13dd8c71bf143937f30108bc5ab47c0e4236d1e6f14e27e9d3be8dd75719cf62bf4eebab45f87413b8cab94fd5d9e2bcb66979a47935eb02063162189536094d22845e5a068dea864f22a9b599e4493979433727360940c0d49d430fcb0945b674f4df57cc2a4944faa8cdea4c7d279cd1b005f1a01b37ed10edb998d648a666732475c256586fc592b486ef1c04e660c51256998d6e18d98c61b00df2a02d6354cc3700a8cc663993620a0a99d3a9182d8400fa3194ed134d645693851615ccfba47a4925a3c76f630ba94dce56c1f34ded2a4d9345dd33dde8837b54b319bf2527231d26eaf71fbeddaecf6d7be4dc67353a54a3abd51a4dda8ca6c761bbda29c11550937dac55fb76a8065b4db360ea4cd1068be6b2e26afd077ca50e872afb9a91611300a07c96c1d2c984cf049cc76c1b4d23964a615e686806df5eca584a90048980c10a7027c281d3964d60091b20e0965362bc9be02741a8a75ac01284a091c83ca314a29634c24169386895461ece7038dc6a864091e099b9a3a3443182080006317000028100a040322711a853010cef61400139694506a2e9d053214832886821020c018008001800000880000013284891dc30d385ae5997d2299abb4610e738240ae63ef46db964cda312d541a8270305c22e6230f8835e6ce13e81c5276e6d1c17796fde3df6c72e9d238c62daaebb688980a0e120b81887a633b51fcc1b66532b222cde1f4a02c2854c53f7b8dc007fdf4f8b991599fc007b2da0fc8ac1127c250f4fdc3501a137c390a7d9d8632bf21a09e70d93145ed6a000b93288cd594cb17df07bcb57486f95fbb4f5dabc1f25f233ddbe483acda8a7017249b61f8a3475db833cb3a87235afec9cca14f2342ff5d2279789bf8e89bc293569141af44a10ca431e22368bc2bc7dacf707ccd2356db7e4ac26b2f262b03da954f34a25c54d842c00d5748c361e5d3b4e292d5712d53ddc93b4aa296538178044a0c0d301cee4aa7291b68791106c629883fc5b1c429e447efd069165310f155d0c2364ae19d3da3029515bfabd27e60ec586d221992ea21acc75927a02ac18ae6fd4f597b613da0df76f3b97d9263e810d643f5f1cc3e62af14a1f652449710ba5c46cc512560b6d24e5cc9779d91d4d0d4887297b5f004324de94c6c8d060d6af450531f4708431efcd87b4efb91d2818814ff648a81231c7e4291d02947cf1927c6fff938e3f8f9232edb39621f23cef8e89ccb762a265b8b7cf25adbf537d42d64d52cf51e501ca9db4807333754a0775dccd37bb815dcbedc5de1035a0bcb674a14c31a183a1a7231295990bcd570b763798ddb1e49b691a6998281b39de0fdecc5c229eeb93c5aa38dc6c360c9801b2be0b6877cab4c7f9512a567fa1cf023c39893d44e8bb1ff683be912f681e4bd413aea4ad29496b8eda1daf0c2e75ca1e9814c59cbbc40dc3c49d1af945923ca6f2ecd87a967fcea621b4bdf3aae8306a09756407542be5f5b8738a74a34adc16b45520992dcf231bf8d7b251d1d8d2ba2f1aa8b5499c1ef05c77d75d8c684fd38d52882fcece171e32104886abd3b8ee057da7d4907b94ad2675db8ee4f8cfa1b1065ba0a50c65496a3b99f9f3303b3ac22f687a19fe847d03bda40e4f6d73d341018a090cdffdc816fbd4ffc0f6d29d58d4e61ef3c0383c0ae8575b6973e32097c248f1c7d145758808f83b382598d36965051d88a7e0b551fba5cbf63900c6659e6f3310c818745bee4755b8136e9a353674a8670832bca049a29e2e2b1fb1a51076dcf2842ab024b412f70b46047adf67c344e4afa9a2bcaac95e8345485b0149c53ae62676d756cec8b25ec213a8bf869c5cba1242025e27e1849993367e12a29151f50f48cd771d300c714b18df218e27205338b9df755bf7269171fa472d29b41d78fb5f7c3fd02e010e339dcd8defc2a5b84566a11c612fb1b5b418b241fa71cb227cbda7f0324297908451d588c4f6ff41e916e2b5d3c8b32932fe7ff51aa8e53f14d3cea501a6296c9dfd5810bd5b28a230c9b1b561085cc592bdabdc1fc81d2af2f78394106d86177acd26215a7c8daf533b3f0d7d9c92562b6380160b8b5dff6261d578b2bfc703f4928e7480b0413ac454be0792cdc2978e02a7babbd23112b358efa118f4defe9c99901c6dad39c6e5a11e12fc8e695ca3f2d03749ccbbc0619d88d8a327909bf8fc55e035d841fa26e0a2d846e9746272215ca71cf896f9205f2bbd04e6cbd1f84c4ae326df84c522570e8d72dd60367e848e82b15ac8af44a6319030f10db5a89a1a736e5434ac70b2a8a62a92ad7ff4e6efef662f1829fbe46fb8a2a5aa1bb6334f3d0f403419df7d10639017a77f1b3454b74b18be348890dcddc60c17dbf513c0eea35099b4cab20b1aed491764a483f02c8d03a89a60be612207fae82c7f169746a048169d407429a97db290a3f92c6133f320ad3fcb6b015bd63f3b8401290ceaf3d25add9068aa213848eb530f64041667113633480833539412a4368b65a9fddf32cc05ebb084614cb943770a9029177d1f6850947f3d3b905c557e1d6d55973858da49575259e5155f275da6b8feb27cde2b42d326449c30ec9a9200896bdfb501a41045b786add609c3a2a74d1098153b06e894dba0d52a110a1a8caf3bd7f665855d9a00857186d2bd4223b6b55daadc36c3dedea16f158c280c3f5df98af27e5841cd9994d8d4280702b6bd7a8169a8bb2b9e0f76ed80df6eabb8a9048a2d0e0cc035dde16bdc21ff7d0a420b098dabd4717f8b7bab9c7b98177b2c9e67eeca0e189921ac969d59fd9da5bc2653d242e90bd493e096f87bb14e6eea91aef8730d659753a3bfb423cfc95bfd83cf0c139c1bfc20023868b127b32c5b67ebbdea6fc57a73b59756fab570d830cc67638353698614f126b1cb7cacdc9110d7a6fc61a956620492103454e81a86b679c49ea65e720a0af7063a81b60b9da4b3b12f0bd6949024b9be5c8ab207174f0c4997c961ddf54efd32c79bf769e631e93e9e7fd089b654240f6120acd090a0621c0b2cc3495c51ca996af4401d9fe7227c81b2947c885207e5ea9dbd51dd6256acdc06615fba3ef0b760da80ce3afd9a0a200e067f3226c63d1624768907b5523cff7ab6875f8dd4c571bb3810446793d0944ec9b8d2020886d2e211ef017efff8427723810c5b54862e73550e82f0c1ea05357880f8206de3f0bb8c449f212ed22ef00b15251cb383c941853dfa36636f963a1a145a7b916b85997718f9a556b84fc1fe813915522f96c2eb69ebd4df11099b12975f0731f0fa0805e8e454670b3630c0d3a758e1d4026d046853201839e61a4e40ffcf48669e2b6a5021e8c5e24b3014c4fe42db34445b5b35bf72f15ba5a144f94e9532be52758bfa19928204e873348b36b9d84123a039da2dd4691ff892918a10a0fa3234dda2492bcf05fa1a8c10dde98b1d528f66fbb3fd4477aa2aa8b64b22e24b1eb4ac7539866e083e8ed9307f4bc6ca48b576c5b9920e2228dbe3bf3106c12617aec13152674dd57c64d45da584ec5d5aa49be8214e2e8eabc01f2727054a92d02cf68d37c558014d6bca1bab69145bdf3e10363c299f1a4fdc68219c647cd598a8bbff403158efde4fac2fd4c99e2719204ea0d878127e89b620fb8dff012dbacbc727e963a8139518ff8afe8ee7d7131ce4ecaf9e39e58f1e7b63604b260391c380526a624d4686a808b806f0744598d8aae850a6e334b5aa9ef72b35a9152b72d3bb1c350d9813faf120dca35974aac33c3e6e2e52c045298c88ba204b4b4089d09bc35a15360fd671dce32de302035751926220407e01a01e49ddabc350984148106a3f839543572b70fe6b47cdd55ec536b7859735841417a0477b7d00119303ad6416e3cfcc0255447c6813e6f4cfa06140efa9775847cdd944f97acda24f63ff3774e904e43fd7ef502041de7a463bf0fd637f5f1b2f4674b7dfbc61591c5ceac86348a53dbc255a0ee18aa4128f86e552cf51854903057355c8d5d9961783d981d85bc79bf7b4527e8d3aafa8b27b05185ad449a36767a4a65c9325845e369c70a7b66bc46f80e2eab9106d2e9e860252b313b1d2156f851338a05a855f89978d13660fccbb2e31144407de6fadc706a060523462a54b4c13bbdb3451b7a520fd40756376ed413ea80d4f33315bf09b1d224ec7fe59498e7e7ff88704bb8ad94ad7cc17d50794cb22aeeabaceda1d69da1601fe1a29a2c57586580ee25d259cc23818c176e28897799b0a59e9889523df3753e11fa049688ad930ee62fdc27065c545c3e156391139d15b4e890069a8188c5c6ae19ed17c53670e0f5076e1812dd4e5483b40fc468647e3f86e54e0361ebce00112c59edc90256673ca0a3971c567ef3891fe1ad56b1bca95170c5f69627c85db7025d13d3fad3ef8945e113e6e7592f177cc21fae99298ea489dcca0091c0e6acd6bc3b823805ce4c3312e0537754c7184b6cef8161a1aea0455f800d9bacdd79d206fc5a546d55142556ef96f2914cb2c3d3e1089fe996cf8577a163fe431fb1e5f583c9517ab4cdb40d3bf2a03124897929441d9cfbfb0b723fe89c0a19c546e714743e4f4b9b697e9acc78e0a7e776ce9c1d1004cbdc916203d198ea260a3f2b0f5af89fe8f5ea2ff51177809a97968dd91f129bd050141e47aca8cfe593bb70644d515b2442f8753c509c253bb6a237dd3b95d3da5846c5b6e121cb6f7295b1fb7c0ff33b79cee5a46394984c7b63eb7e9c4e8070980c813b9d4382f8a6d3d98bb1581d534c61c251199b8316ee44e3fadc213c6f8583d9d667c0baeeda63e7ee973e9ac088d5eafab6cb122800741fa9ec1c936703c911f83f2c8ceaf88737499fa688b1ba88646e1856d762be91c665181825a645423fa0b144263d89fc8f039ac7e7712ac0ce7133507f537cd9bc568eea6ab430f5d7a355fc84a24f599a090df3600fba580cdf79a2a2f9c3485506a231725252d5021cab8581c54d0003f708a2817a161fc9402a8227afeb21f8950afea79f49299400f513f3682040ee4dcc0167cdc1300a844c65fc73b93a644e643968a4572ba5e8b057035fae9789835fea045f96eb1100bb70fdf09f5d7ba7d9f1c1de63fb91ecc6f4f58e0a53bb8cfcabcfa5cce319934e1884bb4f148d43ab2e2a8a3ef7eae5caf9b7c5f4b5ff61224c8f9ccedc109c0aec43d0df2b4b8823f505180b0db4045d6bda3edc5d4245c596c9857efee2f2f9f7dcd8ad4055e61a9805c7fda8fe9ddb6f2efdb769ffe830741baf5283a2d6b73083cee9805823b6e4da13d3c0235fb42eb604125dbc398a6ee4307084f07deeb76c5fa7d365382012882112101e82abe78b26176484dd2144f0613648247097c4788f07028547fe9c38cb92cc26695ba005d0c9379708856ea9e869a9c7f1125b5c14fcad8f044365e42dbeaa63e5d060b180cd0d399ecd8bf1e370eba53a4345336f0af1e90731a874897e3049ff0f68ca1ec8bf64893bc8952adfaf08ab95691c9c77fb2474c138599f4ed146e7cd498d3cda299e9be6045bc370f4e930d39b88ba9b51f4dbfdcd56e96bf221e05f149bf06d076ced00051cde280828bd8be0f5c476b0b77f448850051cf1071818fa7078de1c498a2bb69b2056af7c0b4b92a1354c30fe7137a97dcdc86e8056c009306d4d02f2e945b82bc5a7c93ed5673b884fb53fd505cad3f5cdc6def3ad38f725805c40461ca2931b3633b98918f6e5a01bec479b91e962f73c25f07ea72d198fc5c36504989bdcb9dd31dfc07684f92e1f19eb56a33990cd28f703b4f4e7185857b5db734c53abb42cda033c278b1579612b00796435413076db46662a40160eee5568631315be652ec32243669fdb23cd1c84fa0650af8ffb430b1d1dd2aeb93928613bb7d8fcc8ce08612b909e38fe2a4095a48e53c30fd1c2a2ebb5a1c556570e668b242799f65a85e3d521d0fe0044a1e3fd0195f0508ffd8d2d52bcba079d2f9d6e1e0fd1e89b11307f10fbd8714289d291ee34f3a7e0ab9c94123279dcb0ddf7f9493f70587464fac21c39029c62b1e9caec92ffc307adb31eebc55e27530e749bc4a779c1fda5adf90d937d349cf071c286f575be829597700e89846a9670c087cab4e94efba8d2eb65c5daab9966004729a9156fd9f1df7c5ab563a3209da6fa3987f429b1f20e25debdfd41ecb51236f0c21cb5b2cb242c28c2f607e36a90eee9a16157b8af8d5d7ffe7c7f5dd67d18402449ddd1e87965201f4faec014e55b6cef9190cbfe909e430200cf151f73495e3d6f124b761ff6c90c916db8c570f11670ec2d84c423bd9a30c978f324ceda72ac882fb123d57f5b7ee1f5dd4d0905040c98958034944abcc325a978dbe031eda18924eda3633c47adba3a77eae4b9b6dd43d2e385058eee6b077caa17619a03d4a671285464f843964498f5647b87a636db6720b86d977d13dd3959cd710adb66dd820de5254b4b553055e68582b40360a31b3691438193b1b87d85f0a05440929b028926e5c834daecb28205ffcaa453c7b314e0b4281b0d26c229be569572d966f0e39b2fff66f737cee0b456ae5044927ab7e10d7062e7ab2f9fe7248d62fcb0e691d361e490b93b4ca04ceeb33189193aa8e2652eb90109881b264ce248405b4084fac97c8343b992bc3d2de5850281b48da255817cc42986f7829023310ede1a60eb8b118b1e57996ddce48212685c60246da25d27f4109defc43f2101920185c1a8dbde69b7b413d32a8049b412f2c7bd9b37c8dfd2b4ab27206b7d3aaf5eaabf6e787235f06a6fc5b437406b4c39b091c85809e1a3b50f07f57f76b4d13f9be5e61de74541aebf225369af30dca16836813d1d0a3bb49f2086ccc9e98937401fd94a374617b4972a3528804e82b405ebc7056ba704c8438be2a6dc23cbeecb2de12d7f62a599eda1618e86201c114f262ca1c8047bd894b80fe53464b106c3b520786f81ec85c4770fd1eb0bef2fad1a6d513808a0b68ea7d1306cd35cbbe50af9cc1e15bc98a28e0747b1cb8ce4cdba445bce01d1440d237468fe5099e94cbf8f49657315143d17d9e590649c8114b6d3118db5f9f600b009a5278f90b9b7001f112d2196586b5da867cca4adb9ff0a386c1560617f1672c67a601707faf28aeb7d3e169b3b7a92e32f4f22b4e1c2c6300ba68216457be07e4e724aec9be8560e5f2ca484a7a03b52c2ec884562a901025fe805994c68ee6e4068987e75c22a59f0e6bde4ed86ba65f026d9a8f6e9cc787f7df779d25f988e5a1bd7c14685464e24fd8795fd91213940dacb9b1910db13b49a1481643c788e3d8522be7a5ff4f77ef4d9d5f5be25d805db4d09b8a8c4b6d03827a054fc8d4dbde12e02ccda080b608bf21270674bfb92c7d702e3b313b9183a8627a360d05a492afb5b586bad84e8d80e39c9982d5144299886339561f918f863774b6180b44b4618bdc3c3b1a06ad1b7a31c658904fb0501f27676076bb05bde30286cf5e3ebe52b990368d1ea556f08a83109a87bc1fe0dbe66d3b61634a52d69ddfc662f9631948a02a98d8fa2ff9202c46b7427184a5c68904268aa4054ba9d2fd802f93248d721d30cd2d094aabd010970319bcc25a5452848d8989a02107255392e3a3cdc52a8ff81c9e0a2b74d7990f9cf8d6c5a363f7b18691015249f6cf87a2e39551aa45511cbad8c4a83b485ab433f141328242c6fc3338182870c1b98a5f004341cea5ab6862fe045bd54cf02b32fc6c67ae94745d8e6e91d9d02f769f7d5f131cb801d49cc46527945805d9d0af4d713e17c9e0d6c7404b17c4b1d57ab8c095325a85d0f47f8db36eda8aaf0184e9a640506773a824e62772dae97e12a0bbcafab9057627ca6d30d03bf37d7cf44630ec582c2d71ac53ec9ce77b79c668379e390db2bcf56f225e358bf54b4e78d17c3223d9ea179c557b9f0e17a621fc058d903c206efa84740d6188670541ed23c1758018b2a4d90717366cdf733681b619d44c2eeec4545ab512400267032bf7796a7181cf18ec0cd8683ee995f1125eb2c1d838abd04f614bfefe176cdd8b817e46191d0c4aa2b3dc65be2320388279141452f38b09ad703bd72c662fda71e844f42376159cb1cd9cd8cfdab5ff0dbc75d66606173ee205e4e8909172dabf15270d2dec80985c4be7d182219137c6c1879680a81aef6b957aef630ac183b583e8286fe91d8009c6fdac1389da2bdf91d0aba36b40990328fce7e157c24d0af700812ccd723e1ab1f7d9d7e8557e6bdeb3814eb6b7c25266490eeead093c4391432cc24d35f6ca1590a193b8a8ba094780dc52ecf782b956c86e9b5b9a6a7cd45e9377445fb58fbb1c1bbfa71c8b1a8af6f2c4ae3ac1c71a65a731e6ce2a13772836a3aaae331fb8fa51f3872f42c4ead51fa990c2e5a83835a33d02f7603d4b57286b290ab39654e98f3811f985f6fe49bcfc82b9a011fb03ef94917f02ce04842076a65d34edc7584212e1d2312c6fe92f4d4c0210a443f0ead1c4b9cf6ec722c069f0549c8ed80e8376072165c6989cf973841a61826c749f1e2b0801b3a823391b4e4edbeb2aa1229f767b384db59cb68bcb5045c8e21b8758d2f041a88c35bf1b9b4778b9d2fa4ec5cc2402dc11b0459a958ec5cf03962b4f77b74c3477a8c75bd4758a93f6bb2afc9a82bc6642d59a403a0cfbca3358fd543072576ef16f7fb0b48eefdd2fb87db14a4026df5ff07147a96f04c30faa683013bbeaccf1f61e1de2febfd3364ee7ea9f3079dea07412c5517b3d4d827087abb3ac37aed1ae595940d36cb8e17fdb08076b0bbc588974af8837eccff1168afa322cf3edfeace67bfbc9ebfcc6b71f78623eb09b3549b28b8183307c3414208c44f248b934ebe206f92888b37120b5dac2182af3a1db29d88df169a85d3a89aa46bac3f091cc871823f5ea9eeee688ef82df2ef8f0a87000fb0589f9787b5bd61a540870fc30ebd6b138d7f7ecc39bf16af5974f4474b065a06f1db6c30de7916198290f075d19705895c112a53887f056905e444d329f028ff883d8815b1a4daab0a6115ec6e192db6a00b198420c6252d5974e830f7e425aabb3af83030043db15084ef0d731359a49054812f2c09412cfa23d38296ca955306f09203d6b889832cc790512cfe8925a0f336936207b1c9426422c17d681fa79e3bbbb8ad32f8213ab7a2c5021f9911b6f25d1f3822711e0a4f9364768aed682c65ce4a425514129e4af1eb43dc4c25862f3f477ef9195950bdd5cc98880827169dafbbec4a1703cc320f64b2c76619c04eea1aff811d25ac43d589e5b60ee4a3ab5f77847ec592d7d15410621aa84147334cb02426f5f2d477c0d4112417e267e24b614bd2058a53abcfe8869a1f45364013e20a358e4c9956c01face867bfacefcf90b9252bb04b27221ffa4ad215644aea30348206d14c675bef395d91a9c562e02f54e1c2f2a97b68731d1a482833c06ed93de50c19531bc34ec876f2949eaf0601b7355330380dec88d9184bb2274151f887a9492e79a82674867ba5d75bd2afd2ff53c88dbe3710a45bff61866c59c0e59982d196f846a1812dbc2cdb35720a32552f899a6d94ab897cd4777f08b25bbff4428b7cf28d7f64a2a869c211aa552a0710916571032649d1fec50312930b43cf30045fd7f8c0a081717851e4ae539492710f6129021d636bf934c8a5fd027a7fa0057a7eb9d5371f24a3f99b3eb491e158c6cbc8154ecbfe526cd07c365a450329dfad6880f5a844bd104a6919349de5dc0090530d146589866217b40c32f462dd9ff7bdd9119264eb46d73830e9ed501895f4afa9748a2e67e17811198c410564dcd2247bf5e7dd45c5af22d1c29a37a0ed90f6c2b1c194b008d7f741143e4d32e8104a373621744239d9863490c8ce38fa2b5a9bd5a1bba45b7af7c1ce62df460913949e0f54646b49bbc5ae3ca192f5e7b389271974915db5543263f1fd1590cb4c963c35eaafdcc5911dc40b2fa0dd1552c02b0345d8a2c363e5f3c9c586e71b7249d6054710a3bf72099cdb4e43b127f79213065fc60a3a16aabf4213e0377cfb2dab9130ffed981235b7667e6b7c7fd8fd42d406e19fce25083a32e2f56cbf3f1f5152e95d9cb0002cd202adb08ea1fd484865fd005f2ef3e7e78b5684edcf47c61e3fa58979f95ae5157d94f9f32ebb1aff9d71420f4d26040c21c931bd0a667fa527495719b74e17ff05fd95cbfb04fee8e5a07fdc67a4fe4acf8c2506acf7b53aa9e9ba4ab0306cf35b8cfe0a7d80ac4106484450f1d008898c237f7edcdbfe87a557db48fc562cafcce214c12c87049e7a0f9588fd2e21c6493ec0fbf25e3e38666a9a0eb7f6d293ec46cbb63867054a5e762e22f623aa4e1af8f3dbe6825768a9e30bb42aedb0d84028ae57e8cbfe9f6402b08aee88e53dd4952b803200a070688556bad61f6d1b6065b94298173f003830e9e97afded17ecfdebf3b85356902ef824e0898460949afbec0aa1ae863109c4ee32efaa0144907191a1758bb2bb9406dc90a2c75bfba29d77477f7dbe9bb102abe88596180f98ee132161c27a902760c886c37cac4b410030bbfef3195296e723f492de2f158210d763daf6fa10c5c0d287c80e42c6e0ff1f6789871ce669c0d4e3f0ac57f5fe2b821d6e1ebf386a0b7cc86cf897ceeac0118973693ccc249257cc42b72cef430285933060af63b31f7002f3d49500ecdc97c413ee231d14db457c5c55835dcb50d60d5ef244f00f2f90c846a850f0d3ffc8fd88145bd062e3e543d1886051e7b21029a0b540ebcecb01a221daa446422d607f7c91805f59ca71121157277f16038740888e6386b1b6a0ca33323f3e2c308cfc3c84342843be2cc42ac0e8ee803a7620425e6179be5997ef8f8b5a2f240ba366d4e1243f2662cfe0a6e0c5916285efd4879c74d6073c9e989f40c32c9178c5979c668a2a467b37b4a4061c717581c18c1a1d0d24c22d55372153933cfc0bb796388e408f35075f56783db0a5c33c0005ee1404d40c56209e15a703b7781fd7ff9c6c0be99c6c2b704450658f8aa1339033c331b59c1a39da215d2eababe42b47108c336a3253be8cff6e9683f0af817b160d5dca8d4f2bed924b1f6eb09f9a889090b6f7de5bee2da54c3205840c5f0ce40cefa8140681cc5aa9350ca4bfef0f813f43279243cb0182b8fbf45a83af85a304559cfb83e1188e447438de421dda0d0d77187a0b7638a2febe55ddf0efcfe138c7c4ede22804d5a7f7420fe3daf7a4d1f775fc91bd17850ee638e5ef2ffdd9d388fc23fb4a1ebde77e0b7dfb6f47cccc71d4e97b1f1e69fe1e7c0fc3efa3964848887f88fef4e87f73ba27bd39b17774f4e7a64c6d5a15a83b5d41c2c4555829b7a51c532a97c18457399dba039913ab5c241659e8b3b0000a2780aaf42878cdd2d227769c6a6ce2acb004c7d1697ff4e77ac8cc91544a2a6f4ef59cd699b98386fb826cc652aa69ef692d6ab426d3f749eb4bf9943ea5f2e7cc3eb8e81c4eb16844a05aa59a46b54a5ba75ef98c16fdfbedbcdf2ff42b907104597e7b3692a4f2dcd1e6cabdf7de9099b9eec121dcefad14d7fbbc7e9facc2f57eec19e8a0fbdb30199142e09f3e14024130040e314d8173243716724ff1829e3cc50b7632f70e020137d11561550eef458fc37bd1c523280a85a717097196e94b7f32859d85cc56c34027e11f236c0457c5fd7e6c21dc29fad055a9a010f27b18af600ef0bdd65ab321d05b79ab813640108bdde2425c74d04ee17e984ea089e7db700bc71ad989b8e84286582873da695f26f79a485105245840e9428e0fa4d11585c0cfeb38bb695574472452c932c1ab3fe7046d08e9d0ac01a1980113361021894ae27771bed8d83137051707c5ed1899c510c2e0031bbc5e314c40d0652a44075abe5cc8bdd991481d261d17bbda6bf83f22438448e1e2b05f9f889021360ad7c3f2670a07852b7f6eeed69d7b72479923e4fb69ed13973ac9f33d21b3256bf00322cc579e373267c33f5c9cd3fbcedd8ffafd586b7d1f3fa6b539f6b7397d18f1637a953f447cecf8c4f1c9db7429420e62676e38675699435bd6d76ad881faf7f57a654f6e333440b31e3bb639e7237151c7899c7ea0ce454be6cb488d7ef1da51dab1b0ec732fde83f7f68ad22863432101b99f876eb9ccee5b2693d16e86d296751e36758b3ce51148f6febabb5b94a6ec5f8da535f4abd12ffe6967a8e2568154777729e96f526e2eb1be8c0fd5615c2ec254ec832b45615d64584f5581d72ce37ce5f9def4252ece1d17a7f7022359669fb9cf0b6cf29c323baa845ed56f82560a577e14973e35c37599137cc58b056d501b26d47bdcc77998cfc9ba68d6bb4885ac5fea03a0c5f9a37b9117f0e3c2224f172bc04cb1d72c5ca0d59c2b36dbe7382936ece73ddee3acd1839cd8afd962d3f420fa1e04469e6f5d8afb5471e54c5ed1ddb265cb2d3416c9f23b26d32df31da40f000eb43873f46a2e20cf1fdd329f4e1fb753b8f2e77c5936b8f4c2c50a34850b966499477f39abb30b92e47a44ba15bdaadfb1cbe5de72c7ae8d5dcdc66efd6cec521beb3e0b5a298cd77de6aafee09ae18eeee33ef2b68ff4971bc9f53fede96b98a3c2b5d983658e8a2bbba3bf648e237196d4711cd7e997cef6ebfbcd6c715f9f9be28238c0bc61f7e996fa350b97c316ff5cd929eef8650dd4d12e8e2a4e8a2b5fda6479a3612eca95cf41b94dbaa53e17854eb754f771b1faccaf70b1be9d82fbe410eb4b242e56fb833bde1cbbaec3f338021bbb32b7741bd2dc59f25d3383e749504a29a5f4d91e5252bfe29a11c4b4e08926fe2dc5134f724bf14415796411e5521ec51ef2e8329b209d4e0a2323a79c72ca5046cae954b2b828ff8720a4fff576aae2625b45436be81789894ddf7442affdc6b3f56cb0d9f26659bba1ecfb2ef444df857e0620dcc4c81fe8404e9733a4a43e0d3b4614bacc95f6a1d035579af65f283357da6bef55f0064b767151fb8a45cec8ed988ea94f7fd48066a318a3693f9bad1db3acc5b2bc992b6daeb457719b8eb3460d286f3bb385619de304c9cc5acb6c20c5c916d3c6ad49a6021476b40993d9604a1e3728597b4e6a4039412f67693fd8c9a3a644d35efde265ed65ce6baff9e0cc567ded7f688ee7d4b4dfd1c387a6239128b1e5386b0ce2ceb2affd766443e2acedb32a38fdd2b1acfd06836e69bfe1f44b67ed07570735c8da7b3d04e6c160e0c17a0a1159de78f2f63408d77f8641361ebae16c392e6a78fbb2b8a3cc7112b8a6e32cedb597399ab556d39eaa00815954fae3fcfadaacf5bb729b56375bab56fd83084376843c5afaa2de54087f5aabac437cca2ceaa7beebac3cc4b1e0d9b0dd1136c0505f53bf0d93d0a7e0634a223c0ec922cf52f923d37d182785a38b53f88ac2205f9e7fdf9361ca744512ffd87fb1d72fb2de6f1aef8bee285c01c1ccf76588c4115bbef8082219c825dd17fdc54366be1d822098a192650286d645fa99522bcf228e9ca382d72ce305cad3e2f1ab013d7718a884dbd96b7c091a30018bf1dcb923581e70dc74c205b95bf3bdf777cb32b473d573256d17ed166afe8ee9059a401efd69c750aba958524ae9113579db36fb5e6bafe4e66338ac84c376aeb69003477cdd02cadf34b949185424e40f99d986c44700a16ff37cad560c8299e53f16531cd1e1820d177a5cdeba5c176cae305b43169b56c71a4a20546cdbc539a704832822479a1d4b2fe834b1c59878fc6a563ec67b2ba19c421541b098cf451b26a4b74eeee94b6f0a4fe128f349a37fcadae9f4273f85a62f85970bcb842bb3e9c7fbb3127a942952bc62aa14d77df2f69fb3509ee77940cec3f632de46b9a3cca71f4dd334ad86a3978b9b8cffef6d1af5b4fee9293d7d3dfde9ebc8c60bf7747a18f76a48bfd67795aa82553950aff23850af526b0d4f4fc31ebd9c45faed4736a31b2052787f2489e6d16b7473ba5cb8b8d9bca5f00c6ce32f1c4861057ff4aa56fefe380b855540d4d7bee2517e7d0d8fa8d77efb95f03a0fdbd3f0cbe29a302ab4dae94ddaa34220f43594caa3b02a477dd4e3a88fc2ed221e69e9f4a8f0449d65faed2f17ce2afd095f53685ddc3adf9fd9fa0be4aceecfc54d8447372e6e4fda7ec91db91fbdf246696737fa7d0da9b8f7a65f5a0a79cbdb7fcea2bf8df0984a5dfc7ddf28afc8da83da178ed60bc7af0b55e1fdc9dbcb08ad8ae2205fa61f23ec98110ec9c12254d9edb2065124578c04cdf43a13ecf6fe59ff3effacff30828b6ddb7c541f2ecaafff43c8f6630b8d98f910020404547d18f163036d6c9a262bee0182efe2f5ad8acfabf45abd4a9f4ee5df90d9b5ed6fc84ce3e28cf771a1ef172717ceec98dd536f91bec363902fdb0fd1c8aec531233329ece64124e2ba9f606797b34478ac991b41ee44364c46a4a7e07defca16b977713fdf874e24b35765bb1475d88769c3ce33343bac7d69b9971c55e500df3e88bf0e7cd90717e7c7715ed82d725d67391bced07058dcd145227d26917347d0f005d82b0cfab60bbb80edd4c82e3cc10a584cf71e27a51978a6888294233b94524a9f64fad5a50d7d69237390e904b5afe34229bb900bad0faeaafecd6ce5a0945e136843a65be8e77895d4ab14d77dae91af9d3b68c36b288d581e481c69c4f280524aa9284466b6a40a7a459fd2a71313e1600d042681e2380f5bc7c91ba712471ae1b2b8f2edce1de58dbca12f65e00abc1aea185b238bf04303584c3fe1d21f5d7eaab573478faad570fdd8df7e74d1344dd3b4dac387a4cee27ef37ef3de7a3f93c592f4f07e6c9f69c36d03b2bd832056e5e01e7c1cdc835bd77987b3beefdec3ae2f7f3b7c781dae315f23b2843ccad78f6dd336ce86c9c6853f5cac58fb2124146e219844fbb16d6fc344ebae75db8284de24b205a0fa3fa690c22cd71f52a7e0d13922755c50e9144bbc2a8f135f00caa34c92eb262798c3331161c03cfa8542952db078c5d870e996fa1ca855481017abe5c1adcfd90d77b77d35d2ad7e1021ce437d0ef62379445a78cd22e32cfafd49f2d85070cba4cf34414cbfe402f67ac5f414605cc079c5b42cf7cb98136f7db0d7eb9582987ef9644666896ce22d199304c9919851da8829a09f1a6f750ccc6c758ce7fe1d72263d96e54b29a594524a292bf598b3e4e80e45ba942c654e13f7614f9ce5f88794c39820421ead4c0bebb38d89b9696d5fdb5cf686af744d93762ee1b66dea39f044b9a960454fee183e288392574c5d71f19d87fa364a8a524a29adb0f6c17266206edd7d7a5b5883e3def3d0daa7ef2110eb3474ed71ccc7a37d10c82219c6b9f7907bfb7576e1f0bbd8d1ecb2c24d49cdd81cf0e4319561aaadd6b3d65aebb53e88845aeb8347b84abc968753d55afbb53ef7855bdf7efd156ffde0fee5f1fbfad66218b00968844e0696072e778b245ec062bcbed236b9fe224b117a885204584ccae4e211aecb9394046a4317d97621050f186e4a35c960885197031840faa4b50b5eb38c5696bd51ea0401c1ae03a594a04e10539632c9f5b2d7d91ddceaa00d1004b51d2f83b34bfaf1370d779e078260f75ec8f34010f440180753aaf764dbd757f41b680387547fb74429a1f86abe04bf70412f5c110e01a00eb08bbb8121ce9efd3cd07b300440f6c21b5b06dfe3e4e721efc130cc5e388a2083ef853edbc5d5449fede27eb68b0bd2f9d910108f53430643201e080241df7b1014a2fe0c6fc85cd85144e18aac815c0c6e7dfa1d3065ee27a883160196390c643e9763aac0effc49a0d9cc17d2c83a6e86a4292e5afb3d20cd803c8b63be6a470f172d10b7247b786409ca966566b9971972c732e35e66c939c7b564a4285078967c01ca177e62d227adda5673589ec2607e68c21dc53c451a2b296b53268a6385a02fa4188307deee173087a691e70c17396badb5e6beadc5502a27546892614fd3401b1a17aecaa71ef5d39e522ab385c2266b4fb834d217851a0cd49e447167460d9627cc45d9088f461ad6a4c8aa4b76992d2dc76a5ace1367897ebe16c3229198452e3f5ba717896467895da6b52eb3be9c658ae54b5c8363c21db51c2d6788cc3690e8384bf638cb088e8bdee94bbfe384779896704b6f9aefe222a239a2bfa137690dc7f7a185a35489e26a39a46f508a5eab9984e90ca40ea21f891e66de50bb71518435235a8eb5de87c754ee7266cb195272fa068bf09813eeb912e7aaebb6e7ead7b72a89352459d3d17280aeab864686eb9a116f758c4b7e39cb66bacc2ac5b5ebb7078acd15506cb8c80dc5e60c5d1ee5d14a8189b7bdc6d57051037f0ba9b669da734ab8ae1f1b4633edbec10fd7705113323528597b981f9cd5af69a6d75edb64dec24eb92d3d986ea959b827ec9f44e9e79b7012a8d74a5fc4fc122ec21f852dd7337a94981f8a0d940c7e33231de06f2e6781df67670bf5df8722893e3c06717960122e0e1717b59756a4fdf6e0df30882bdb0fe2d231ba3c30fcbe26876679e4ca20b893413c8e46611057fe422190d15f3c16c9171c05718d4220f447f02d3e628a4220e0dbd07f21e80a6b20e1bae8086120e06f2f04f82ecefa58de8358c645edb5b7f2a569ff7918e876586471517b4b866b9f6255ca45ed3b77a0816079c50bd8cc06b38d7440f7d2e0f5cf9ddf445a7e089a75532dca0f8121916e915f0ae1b1c17056085a942f6f2a25662c5c07c303327bddb4ba6d75d3aab50d269130323fb80873814725f7a9da8daf8b8e470da74cd482e1b64bb7c8b7e16f6275efc26b164dce491d0a5e4b77cf6271a7dbecdfc49d3329fd89399b57d6ca29e16adfa14a729e87aaaefec4639c12aeaab1aadfc21ce6fe2466b10cfd89c71cd65f254f9d54f2489e4975e694e2e2f4711efcc125ee386532d7f2c471d11d96254e1eaf86e58eb46ab814db33dc39d34c40a7c401ba5c53eac2c8d45aeb0fb0f9e3ad95ce39e7dbee67d8c1fc2093ba1346c655e3ba63d5fccef48be4d1857a767c63cb47c836c84cc3efbe78fd06c1b5a7d56ed5da6ab7aa61164943251c27991085cfac4eca052ef29c25c9734a9fb46a9be53aef03435b0b244f0f8e177080d9ff0ab12ead987750da75df390d1380ccfefa38faebc4a3dbee6bd871f63bec226766ab6134364c44c343ec92abca41bf668a55f673976c98e8e4fad72f6096efc99e5b682cd20fe3357b2d240977e6f18eef23fb24328455afe41bc0678a3b300d077b6d28f0fcdb95b7ecb2ba4b29a594524a29ab4b89bb5b6a6cef325b1b7eb965d9acef08b75f6742d1a4945fb8feda0f17148b3beb9c6507ebd3d0cb3efa45e6740b0c9a25a1e80b744bd6a0460d510cc3183146239953a9a5160cd75ba9b1af68299501fdc2fd7c18d983fdf931fd428de8160dcc664376f942ccf36b067c744b0ec8030da86641bd355d2e72df3f4397b9e29e86e25c71168bbb691b16d77f4491b9cedc6f1f7a19dc0bb837c259e0f7dc732fc459de733fc4ad16b7722f71e67398fb24e8933e89f9a30adab0da76815e71df1bf6d12d5cf749909e4a503504e9e9d3909e62d5f6a6a7d3fff4f3898b9c29c4359c9118480184d72bc6845373c53dc784a6f4a637e11a26df973093eec79474e222f7df6f61cd0e02217b5ca4199c81e1175a6fab1c16f7cbfd246f7e168bdb71dfcd66f6315be25c716fedbda9d4bf98b951dcd12ddc77dcc378c5c2fde122f71d36c245ce82e156160e0319c1acdb2b78fdcd32629d1f235cece1a26c0db4e1f5ad219adc7d77d885416868368449099b2dd2c360248c258e8c1251e08423ed8e133284480d9d9e76c496ed6dcd3a10b20d74140b4730d046ede7bab8de6bef03f7e8c21da1114057fbfe202087470dfff0bc37020721c368369238a26e04fbb4f04710b0f1f87904bbb1315c292e4a19278651fe48caef4492c7c3a395a7d1df1326593bc2bd3d28a3c413bdea5c0a659488a2579dbb70db315b23d85c492eeee8494c8927f287c72f5332dc71041bc11ad7ff7c35ad756121fdbddfa3301cfbfbedf7ecad1b0fa940e02059429146e8d22217950068945fb774edda759412878dfb0edbd73caeb9e7b0f529791cb2715cc7711d675fd3ac8e0db7d481e3c271e264cd06d51e66b6f61b25922be528ccac6311fa5b68b3f632d739714583d7ce7269e9d696e38a1cb1e56d2441ae9aa6798eb984d461ca5c69e8ee4b481dfca9a4c21570aad72d5dbed3f977e6c979644a2f72c4267b86e67b112babcf8aa8b1d0b9dd0b844f8260b54170b95ce0ce05b2fdf7dd7feffdfc1a79238d749ba4b8c70e1f3b9cc5f97096267d8040f8a83797903a7c781ea9e00fae0bdcb9328faeccd11fe612332757970c4c8f1f425c2ef9f3b384d4a17ab8a50ee3c4d97c969a0ffec5a315e1f1ce25640f128f45bc777d2eeae1d103782e2175a01a17f610cfda944c8b84e4a705af59be5a975c4301ec1e6cb8953e6ead5be93bc68e79d6becb863241b8d8c30a1f22f2ddf38c8b419c25259671f11584920b7eda7b0d769e89c55a7b7bc8b8e7d2a54b97f24b5724258b8bf20bc2d285ad7d5fd3f73da156be54bae16f88f1335e957a1c38fb3274edadfd1411210dc6fdeeb9f0ba48f4a1ef38cb59ce722a510faf4a91733a42ef3d37b28caf12e11c21ecb9fd1b8ede7f313e14e32d568962ba960b6f0cee2f56897239ac23c65b0c24c6873090d4db202cd9e2202cf986778c834a7b7b031e4f6ff13843a53a794946a94bdcdbae54e25ebed5be52e81d1e4f255f29e1953fad3c8c97c2153cca5c7af757297ddfd3fba9a4f22514f73644bd281c7b64d1cf1832543f3cbfef3644c38ed787ec4feecb88de0b533f66eb7ffebb0a8f56460c3cb264a7bebab851ff711808ea3b14eec68f4359cd7adf2bd95764c8c0f7638463ca945d25fd3fc6cb08dbc51878bcff3162e0d1fbefef7fd8beba780cfd178465ec912ff71c0662bf7bfbdd4b69ad4dbdec7eec386fec91e3c67d52ee704f1ebfbf38084bee82b0e4d47f292e7721ea6654a8fd293485f20661c9a52247f2f7a370ec913fedc31daf71a18feb22f7f0c8f5f0d9f1dd15fdc8cfddc37886f14e87152598c329cd3d7cf897bfee4753febeefa9b77e7cdf8f1f5710deea98ce2ef1f4c1c5a9b9fee7f78b9b66f8df785edf2041be2c7f8a0f1f9a76b19b308516c00086054cf74ee553df4fc3a586b354dfef72d6e9fb7d900962c659def7d3f4f7f061c48f20ce327dbf106791be5ff384384b729c7cb97f32347a53d7bdb59654b2df2934b588fa1da8f731efe984c7202619e12743061e85dc18a18d11038f43be8c7aff54485b44a5be0711d4b7887a07859859e57bd4e4d3fbef40e11d27dca344027b30ba21b082363eeb228769176e177e8bdcc892b9b187d6c3870fff1ddc8ef0470f1737d006c54624e18ead61252b1507b1825df407272e589c948525fb7fb3bac55977b3e4532a64bed820031f4eb20cff7cbc33dc2feee7f1bacc556a14777226e7c5057fdaa8018eaf3a6ba18d1ae4b8d8d94a29ce92f3c3a34b1ee58ebc62e7de702173b8ca0ce313b46187e4fa72fb3697efaae1e2d4c17d971e1d1ae451c276b4996e99725632dcb165343fc11b7474f1e2621a5fcd177191ea2920e6cc70eb8d3ae794b557524a5aa473cf8a9afb65b7ad19c68bb834a22506995c4a5db4ce43bf8d423fa65fa98b2ef65f1bcae262f7f784ad215fd992a599905ee7df186472a557c4d326fe4299bdc3e7c29148e642df3942fd2391dc789cdae770ed7384b86f30c7a7bde63f3f87e389738434cdfbcedb97bf19e36bb52fe3512aed7bf75749894f63be8f4432d858c57dbfabb80ffde4b0ea067dee73502e14c223913cbf432f7a0d8fa1f6ee019943af85b0ea06f8a1cf01865e0bbd671fb4ef58fb91879b7bb8d98987cc916320f6410c44e57dc81c2dd654de7bd5f72e03853af5e9e43f4fdacf90ff3cb916c223a9768c8a633c29460c3ccaeeeed2576aed3b2cfdb695ec0dfa39e6d39bfbdb31e79f63bee7e04e9abfdf980fe21c167be93d2d14fa6eaddf9b63ea81a8bc3b1e4bef695f8fd3c16f6f712b4fc75f09db57201ee77ba54f05436ec89c37a3dc2eec76accaa02720a75fe6dbd1c573a39427f6706ccc22dc0400758bfdd4ac5bec7327b8a38ba71424c52bdb97d193ed8fda4eb6dccbdc715886a75bec7744b876ae823470e3a26dee69f8b8689f0b695ce1a27df985db4fc8b024d7b1bd70d323039389396b74e5c0623c322e1e6775ee27643062ca94e7e3e9d8f71ab871e574347c685ce1ac8f872c89faf493081f7fc5a6d75cd9a718a4e11173d17e189682e64a73a28a204a9d9092d30297f500c809294ba0e1e372c59c35b61531268090833cbaa0084169f8cc78c4386d475b520a32bd781c99f1d0e191c303499813eacc160e4fb323c62ba6c56cce2751746c005b02cf932851e09c67d62383b99a5897936c5d4fbcc9a4a08d8ee3280cbce7c2201eec661c6258720341b3b5f66164666b7b6bed17511f876fb008faff30fe85ae1e17ed77a18bc745fb4afe3d5482c377ef6ac2718f431c3e0cff3d1c3d180d7e1c708d0c6caeece3779026fcc73532e13bcf5cd9ef98dcb11494ed73b9fbb06370e04a41ce02c23d176a3b3de2c9f6553230678da52099d86c7140f665787a9cd54f54c1899327f6656457c8fcd88e49c9b663b36c3bc645b6df715e0a72d6a8ed94c07096c4a9d95d3ceee2992d0ef364fbed0515efe1c4b6cde99c73b662971e3330d20554833cbe9d753cdd2fbb4bd2173bcc364b7effd6ac8f45371c5444dd9a99abd943972375983e335b3268ae72640f1d12f073f26c27c27024d370255b295c4fa74be22ceee7773bceb22dfa7fe1d63368b644dd21f1c721bb8ae3eeda30e14024b922c9f36b8eb3e85b97a1e74ec7c5f97db7d325e1a14d7a9cb8080be9131a73714e23797e2c8f0e0ba278de743a9dced4c993ca3a9e4d0aaf59c6159d28f5d52dfea1dccef2c245af412efa0dd99bdf0cbfec51af7a955d69dd4215c845176531f77750b89b141909cf327bea0c1b0dbd272e7a37ce3232c251c1fa2587ec9f83a31d59c11deb0cc85953ea8fb364764ae9df06cafeaa19fb16d7c8980dabb422315f7f72e1bd8295510aa341ae183631ddb207a3e1f5bceec9f5607945476b3281366ece973f67cf59ebfcda556f8a5d224b0d17615c6635c88c3399f807175dc6c5245c39332288b328f67e32d3b0ec3c98b3c5406110846ec90d42170423ee7983e25dd17d8f5e13dd8f9e94eebb66a904af59c67602e84e9ff306697addef49d975524a591fc67b027db2336723dfd3401b56d621ccc5ed399b4b6370fa8594375b6f9792ff62e68cdcfba311e3bc662b9cf56a7b7b5fb315ca5e79bb3e797b5650bfd81ff57440b83271fb9194c94229ceea7efbd0c759336f2b3eb95bb9a2c334c8dbcb6e619c5ec1640f1e84dc8d6cba3caa18c95deec61ac331e856080b6198090ccba3d16834ba5724f2a0c8527b499411b468168ae73df9558ebe925efeed54b74a2ffffb25f412abbcb2a769dba781df733657eb7e600a77c829eda093b7a7a08d991d70e66afb6ee78ee12c9c8553c21f67d1bc7931108d7838a20af228e264291ec9f247f126cbf76c40842c4701f0643906400559fe18809b2cc11c28b29c719325c699f13a4173c1c22f2db27c0ceb1730cb1f712c4bb9122405151d6409eb972ecb5779f54b29cb9851e546c674b73a6654c1c972071ae4edbbb5038efc1d62d0ad95978b3b3091b7a739378cb9b8fd87c3272e16e09531ce6ced80835fce5a79e113346be5041ade0167b6503a2b2f67a1787ce504cd4219a1c5cdb19210e6acefb7dfe1c80e39ce12fdf6a35000b3b9da503ab32500a0b9e2719600ac689411503a52c05bae58003eddb27de783bbf2ea172e6fbf72d32f356fff3ffde2e5ed5b3ef5676c01e5300b28cd0a672d2bf2d69ae5adfad4f7565eb399431166517fab3fb9feb87233ba35846bac5b2a56852a5006a2be0889d00fb1f91baf8f08e31c1737fc85480cb919c7a05bb6c730681db6c747f00e389b87e3aceb138366dd2cb4b8bd6703af8b9cd385df8361f7d767fef6370b734ec9d9dc0ef42a272537df9bdfcd97b18ea75fbcdfde5341b3c01cefa65fe46f4cb4b8fd589f8c548b3cd6d8caab09ee06a5841acb1b17ca07739c559b68d17f7b9089fa24fb6fb389d788524285750b2b0d79fb71d493a5923cf24045de7e5c79f120eb961d504ad801a75b48d9c3ac2019dc2764ee919098943d4c7fbac55f66aa850c8b0310d42d26b8e30e18276f3be0e4eddb09a095299a20834e17c0c8024a1625d2e88a42e0e7759cdd42a23b22954a264e522d28a63f1556c309eb96e75e337458a8183cc9fdb58966a170bcc2eab78e6e8d603df4029a018da35ba7ef1525b2ffd8cafd520565ef49b967ad3563fa65f7a0eab15e8e818c4e86431e553f339b8d7ef4a75f48b95f4a295138ceaa54b41803d48fe847b09fa9ea19fb0a7984030ea914c63d35eca49472ceeb33897047ef664e71bd1b0fa75f7ca494958a66d5998b6efafa2c0a6651a568e14f835e145aa05b9d6caec60643f607a259defbeb6856f7fe0fe808740bf53e1b5dbc6b8314b2ff8a57ca227cbb34845037ce52cd5c6441b354a21256a1706404cdd60a6c45b6335b2b4073d544f630e32575f08765ff512a45ba379e6ae6ac3b5365d1ac9b85afc066eb249b2b5735d12398ca8a1f2ab2ffb8325341915d2643b5ce9c85baa959340bc5029caac9664b46d05cb9ea66b6543b7365237b5035913af88b7ef44a90bd9b755d64d58eaa8967536bad32826404657fd5cd6c9966cb8467d8f48ae2f1e29b9c4af50c9b194cf40c1bd90c9b15a0192fd903ca46eae0bf029467bc4caa27de3acd5455645741390591fece9c35c3e666d1ac194ab4e8a81f2db27b3727d96ca97a4eb3d952fdcc958dec61452675703c926591e8343b05e5101e3d8f289b1aeb16f933949861e3ac0a458bf2691759fe1752a06e911447aa7a662b025ce86140e368205a948f0a75f44af5335b347a50a141f5935dc4c9336686a694afe4a37066eb5764b28766a172b2afc8724bd934a4e4f4307ec23549c4987073337448bed245b7caa59a76abc42b59fb15cdfbadc945ed6aefc94ff535b9296bdba6cd1cb8d4708529e7a13e0daf8bd5599d4dd4c5ba85add95ba5cb2fa6465f5eea1777b48634d377f9b57241a081a75b3a0781869d5e75aeb83d085cc29ee1746de84bda36dc7eb7b63fe7f39c97c5c5395bd634de54a6733ee10296a7cb64b0275c8861d36ae8922af821c47908f2595938432303636271da866b4aa552d6d2ca06efba045543685f9f46c3342e77ae3a8652217ab838e54b27587ec659b36ab25aab36b3637e3f4a15e4eeaefd1ce8bccd881ffda285c02b228d88cc6f7296e32cb5df9e17f2d022027064fa9e4b3007f5ecd10ed7e896fa230f40b074fd2dfc16ebdff036a594cec8643251e6cdcc16911924eba0279a60ef4da5fe4339b613459c90fd89cc96b5f7be86c36e9123ce15a0df2d72e61a8ea105b2af8033ce9af44be235cb489ae5ed8beb7d71bb2feef6c5e57283af8136bcdfa4c8dbfbcb6dfc660be38ea247a406ccb96ebfb836f77b5cedbccf7e714b9ffde2a2727f7145b971705f03e290200d6ebde135f4cc95e07e0fbeaa8a7ef434a21fe11a26f56be88bbe629ad1dfd1178991df3d38830bcae06a123c72fdb797dbe8b7973309e425e817d92d26902d6f7ff54be85f5e76d8610003c058860c52ae3f92b2ec72fdb1cba3bf6e5489d32d9be3b8b8fd28f4d75c6d0f6eff85eda2687b0bc695dffde82f076370b9191b3b575bcf7569c4c5cd8240481b17b7e7b02ab592dabcef7e037548d5b3b8b839cee6e1d173f2e6af0d6b458c70113fe66afbdbfd8ed9babf7d0555438c46237c71cd7c11de3157db8bfdc2fdf6446abce5527cb5fd15574c99c20517ed457bd1603418a3c81afd65a4a7c848c88c84cca1bfba657b0ade98d96297b9b86d3379858bdbf6f60cd78b27b210cb89994f9a7d5dc071d7c9f2e54b1d1ee4b7c42a51ae0d2c9594cacf148f94c5459f4f59269e92841b2cf9c2b4d6ce17b0c1670d50a4e099b47a0149a65ef022d30a851238efe33c8f73bf31229303f34310dd6e67cf15275f427eccec18d2b407a69989d93143f3b9e776055eb3b4cb36751cda8f44648cb452bebda6951a30228dfa727a8ccb66105ab26c5f95e5c759f4edb370c102345bda5b9619ce4d0accc1ed58b0e8950d9aa3cfe126db1c5ea3b1b5a304c4c12dbd9cd582429acd566bc7de1dd20cbcc11d5b50b27d9629ceb22d28ad9dd96addcc957d6b777686e818d2b76e666bf4f627776deab66eb27dee6f28bac8bded5168e78ae3b877992dedb91c6e6accfaa5a364fb2c336779b6cff2335ba7b7cf12345ba6b739e8649b434eb639e0649bc32bdb0791dcd19b5eb2cc4a380926f79328fde84f3889d39b5e364d49352fa6396195874517eddb3761d5c4456c3fd23e89d26f5f84a67dc7307159b3d6cb59fef65efba31066aeacdbf7b61f3dcdf623dc7345a3fd0c090fd13218bba4576e4b9acd9525bd6c7047d2ac6399cd957dd006b77bdb85ede2ce1e6364f025a863d60f0c475406df411c341f5193c1f7b40f0c4752067f823a6a1e93c8e07bdb07862398c1a7a00e2d8f24c8e07bf603c371cbe0d7ef4eb89f47d3cd9143d480067c02a05a0801017e9666e14a17077c7dfb1dd0b2d33145cb491b6e44680fa810284246a0bb657a04e40e1e23d06bc0d760addddd78eceea6fd39a40cc26bc007c427c4d7208efab3bbfbadb43695923d061183006df47b7707c4679d45db06f1cd2999745b7b459b10df74d15c736eda9c9b36a70c1d60d2519a210d3122e23e40244310008bf822e0ede8787012b013486d52ba340ad40a500b4c1f7e010c7cee847bb3fb6ba90c984a27aae3f319298998335f38803a04119f3be17e1efdbd077c4ec325a11c0faef42f420bf5005622014273cad8eb091abc9ea001ce946dd081fc4ac5ed9799e51be0f2f44b5321cb6f14b8f4162e2e6775142a76b27c1f7e989152b29cde29f95245b26409d331977651e9977e7a04ef5b76db5058f6c108978bf27ff4531c36fee1a3868bd29fa6e1fa98e9ee87e99e8e53a97f5174c96d81281964ed5ae70db31bf9b5ed671fd02d38c0ba80b9cbdbb2ab48ad1021251fe0793265ba2ebac440706f06e1fabb8b0e84ebb56b7d59173feeb5afe94e5088995d247d895d72b4fcc63554392a0d771717678d1cb248c6345c94357cfa47f70d3a51806479be1141a6d08323f306316383f10169a1446ed934545fe2c9633f1982cf0f07721a2e35445d90954a40424070849318bd3f38ea44a2f0617c04b608c8f3b80fefc800c08ff2755532c75b51dd20c3fb388e030ae2f430be8543cc1f7d0d85cc15a59f040eff0f86490020041f87a33064a21aa2f4e017017af8beaa0b01200c3d0e458f43c88486f43233a09006433bba2f7923c445ca94ecf04cc81352bfeb42a11b5ef53633fe7ec78442101cbd082741fa1109ab86207de8dd55dce8fd479f4409ab3a4c43faf924acaa1889238090a447c246f733a4577158550418c235425af08420c48cbe8493604233027f3ef8cd914221d2898bdc0d61ea67842a2f2334fd4a18e35521ea6f787a8b7a1b953ffd36b2aa8e89f13636a9b7413df83264628472074c993ac6540a3b665492b921919b1048295529c2b94905df6732adacb0d430c257f25d6eab7f80fdcc14fbd879cdc005ee3a51c8a3ad578802852993562d48f901ab0c63ce4927a519c89c5389a741c4f3d9adcd73c75cf9d8341f3b3b66cb637ec3736c1f2aa9b8422c2bb10fe3d6e21a12c45499c6a3cd60ca6e9cfbddb37b9129e5143cebac395d5c6c4d56fc2ef6d35aab74d1c54ea5aced39038f03d773539186236e9e38158b1dd982932dccd6607dd2aa6dd6725bf0e44b0d09207404a142e4f0973afc657d9beb77eeeb02425d6aa880f0ef6f0ce469ed6c816eca44e7bc1746e54c1254851a74a200c3070b23582c916a903e69d536cb040f0798e5378bf414f70407722f64dd3fb03db86edd65bc168f293937dfdcb9a741feb2e93eaeb08613dc08677576723bf76bd626e17a1e5359bba6d47cd52578f2fc5f71710ac9d2666e0b6e0bdf3eba73b76fe3beaf5792a97ce955b08c1255f49c3c947a967bf73894bbe7ce7134a33009508f42bd877a31a9b5864ab29294928f8b53baa57e050dddc7c529257fe722a752f74e1e899d1bb9fb153e778a879d0b320079512ac9365b92894422d168ca666be27053e63f2e92be2fa52529259fd294d28c0b67cdd3cf9166bb71b2b99ae1944d1f5bfa51e2e933a54c9cd98af1a907f16855fef380662b064e59ab726f8a3e29aca25759e855e75149b679494a4c892af2c5a34fc9d49d704b3fbf246b3cda3c45ea8ee8cbde42779c197aaf6e719ddbe2ca10769c6e992f0a65ae200e2b4b325bf2294971310cb7442514a3c4e1b4b8634391a78eeebff73c09fe68bdb0411ca0d4413e8c4ff0c6873dcfc52493889b5289e28699c3938a6e995490ab394517f2fc89853cbf5b7eee387964539667fbe4994d9c557fbcf34b3267694690c114ba90e797a4784b74295de1a529252ef22c7971670a068e3339e93860b8edfe62e1b99eda028a2459cc4d052af0e41047560f94d4ce4fc659333945fecc96e9bb5f7a21832418fe7296cd8d4c02c539d758c01ab97dc8fd9fb7bc2714763a5d926e67aee693c28ee70bbb265dcf0d3f32da24dc8d85d92736d6335b160a900f3f357c46c37d58642b3187e5ae0968a3e371b103f2efebf5caf20a57620ed643916c3c1d4f179bad8ea7ebe960dd1367755f553215149d3f059dc040d97813b4d1fd8ce26a4fa9e704141f99135078e8f4d04882acca117ef61424672e004322e09135e4387a0a5241715156f18ff539ac82e2e294d51f55b12ace5a863baa62a3d07b5015e351c554326775941acbbee32c0fcfaf3167c93c06a932ef0bef4e78935c9d1cdcd0fc19e5ca9875cb7c15acb3c8df8bc00e07301ca5f74adffdbe31fa9fec78940f7ee7d289141e21db904ba3100729df534eb74c5118a4ca3a1f9e3cffea382b8455b19bc459209e7f779cf56195c42a281e56c95cbcc11d4faa18ea46ba6c8b3ca2704e4153763e794a717136f103946b56c59c357652f2fc39aa622a28f34f41ce7a39ab9bf8e93192e77ca05ca584dad75cb967f1e2a25e27306e6e27abb22231a9b733362830bd8c47810927c1443504ea4d4f83ea98d3178931e11a2612d3a83e856b2eca76d97e85d558bf68d9aa7c8c0a9425ea5742d59b422652e5694cafa2f2266cfa18b826c69bb08c540af5a9979866e555d8f4354c6866bc09d730510d617ad4d730915f63c2d235e365d41a24810c34f0818914d3ae2e8310bc5842921b08a109308c1d931176cc8c95b0633a4615768c0c263429155c93c23ef9c75ca17c543ef5a63f0599708d0e5768a20d3129ac92a850d531a8d7090c17b71f4f41a6254bc474cc29c87988e990dcf1f4da7ba72067d53c9fce36b4e114e42cb90025a69aa926abed7130bc65fa59fb85fed4825e61f2640d7146beb2062fe8641b0029739871d13e8d8b2a1c23727a48756324bc318293ed872f17ed8311beb2e5429bfc04f75686c7aa745c74ce393cbbebb752c072cdb152964267098fd834dddc6fc37b1caab8c7df31e1778cf704f881ae264ec8c064dd7bce2119925fa9d8c1d32f33b295cd160d1f974ff7a36b96edbb520d0bd1eac50d6197ab898b4df088d93b65785c4cc395e9c1324d5ca45edcb163f40cee842b7385e50e8faa1ba42ce3a54ede7ab20c0f0c099a471a3ea1ef1e947922237c6524e677aa1c24d93e9824dbffba300a17b9aa7272b22948c593ed47794d21fc0089b94427db22fe314ec2f4ef6112b06c1f87ef9c70c3affef8515024a67e18aafce9a3a0484c385b2a7fcdd5a31a22fced69c2af61e298a6be866bf057ed6be88758e5df31db1789b1306ecaf0d8f0bbf7d08677ae6cc5e2be85d589c78ec5b295e1b121c5189b6ee6ca36e18ea52050278fda4e2c0cb3990ca6240c3df5073b4a7cc68a06847fc20f710dc5da4f91181c6e387365c370bb992b5b5ad23d576a62bb2ff5cc1961a9a7e4a404735667988ba552d8fd8c902b2df1962c357156979694784a4d5cb4fe26530ad528193fdaec6109c845fbda4f29a6fd684025990624e6928fb7020bc34b4a1b749c3359f13cefbd04149bad8e9121c325e3b39092c5935177c3ec6eb8a11b808e17528271c3ec960841b32ba78237c4ecca7151ca1d8ea3feb9411b5e4122c2d881c1f51831bebda5819bb9b21fe3bd60e35fb784db29e18e2e1e0ddc88d95f0337a9b79e4a3dd75eb8b9b1ca8e5d3c2ef6dc0700edec34e1880883870b8908a34912707cee3b1a1161347191e5bfa3c3794484a1e32cc799883078463bde92e1c9448491c40b2337b70c0760e429a18fc2e07aa5584f4c45133e7e1ce2926caeec1789b1df4c6e916c9f883096709c874484c1e3626722c2683293e838d8724444183d2e3611612421220c1da9e36267581e69f8f0d8e91799c55c0a7368f8f40bf7de13f22873b285c11dbb0e78b01bde831eae80e1e8bff2321c87c0b2e3209e8fb3c41e71b3e3e6e4729839a1748a2bb27d19294662665cd89f1da7ba41ca9fea3b07e1e484f4c4f4f65d509c2595c0cc192baa70dc62d9be8c70bc792cc5b2fd100d9f6c3f464eb69f0a91a059254482a2c251833236cc1496c271cbc97647ce937ed96e7a7e4a4b341d7712339678b617098f5ad0288e204eb6ff336e3bd97e69c7590d9bad077b9cd5f68af0989239946c419f6c4770464dafd92ac5e6ca7a8c2d996eb2fd23618e8ef70415747a9c3c41c50d19f90805e4ee16171217ed1355b07942ca1450021acf2ccf54d95ce9905d392e5a241506c406864c5d740fd6851c2c82b8d9d2768864f7d9a25fbf628d8786628d08115c297fdfa08eeeb92e7fb8bbc5c3d1f3c9dc77f6f552220ff1543e62011c56d5ec2e24ae9c59935cb30c0face789e9a614647acd8e94824a415e6fe0766c0b29d99f0bbde7e1ca712171d67cfbda8ec6f384478f066e9ce5c9e7debe068c386b6220f23de4e1c45920e6d104873466210d9f90c78e8b96c71217ed7b0de2f87090b2103947b86329a8023c5d939b00a07e01d2bd0a082e3b2c8f6d452cf470ba651ae172ef9d116e277dd2aa592ec4716ff30882ccbdb6d32f5cb6aff1f44bcdf63d9c0af0f48be7652b62d97a39640e8f413c589eef599b67d8d9431e49421e3b3c96c4b6e8b12289153b309926b12d72381e497834e178ecf05842c387c60cecbe2bc2b5d1c57a5a8a17157eb28d72e50d528ea293ed874247cc0fdc9003aad5a8646c35b649cf3a452302000000002314002028140c07850291583820d372c97b14000e95b84c78509aa6418e420819420c210480801400080082242000ed460c1557c01823b0274cfec8d6bb4210c10feba7c12133c615b1697697a1c29201d99010dd3f4e3ad71c975248459cc97d75efed2daea1cb2dae7d7d466dd6412caadeec13ec89a2963a80f529647bbcc329bec944c6747f5402bf8b859ecea591a4085f36250f89c3dec3392edbf0c3c64a642607e2568a6504f50af80e848d3854daadd8406f52c25302981f9fd0c7de7bf88f500157378265063ef95810587528b23ff0005c9a86a71c84bf012d02bb327dff905171637cba761c354758ca6d22ab3780935d3b958b9de8f912946e7e776ca878e7d6a951c3ecefcc13aa3440dc35f945d18329be9b77f64277ed9cf6bf2e65d927936c62bea924f7290273267c06d42b6a4c9439313c6470b11c88abf0c4353ecab9a540064ca099ecec1ba18ee52fc39bb76ac4ec1e42a62b7addd9c8adab28d162b525b4570e6e828c0203fba3b1897cb17978870f5c609896e28a030cb98a65387b9e47b82fbb9313c37841eca5d74afc05d5ae516bf96903667ba5698fc7e9adfc722242d523359a5b10e47710dbe1e889e8e681ef7ca565308398f1f5ca402b6d69241309c7dd85a8058b8d3a1a4a5d618809dc286f45d00ed9ad2e3ca9733d31c9d6cb4bd961362c09c25567438bbb0df92137cc1841713794b818521d302445731bb9df9f78cc61e7ade1c36746bbf06fe016bd8d049923101763684337939708afe0a6393142d2e18e3c3ccf421e44d090c2358c066715fc72f5b1cf81e1252eb076c4c684453bbc50ca3808282f7b15b7224f396cb72cb10cdbc4dd99292d838940a9997ba423286b43245a695540595a08930aa54bd79c20a91bf849dd907e1454f72fc70104d9aacac94e92f89b120e4418802dea23978385f11edd0aca405b22171d1a2ed0292909e7e117486ab7c5a024c0c354677ee50e95d0e0a7406e30885704d1fa4964ded229032fc01aa461b03aef06f297267b873e9db03a1efc9ecf6cf12bd245ca9fbee57f993e4b8e995571f450f7a03984b642758a4c6121a0eef9ac91aaa5e5c9ba71adb3766806c1667b01c853bcc2714c093ed127b461561823b5bc91fdf3f15fcfe99163f1eed76a3b72c4d37cca53b3a66ae0c7e2f3150568345fc796336a88a87b5fadc9cb9b6e855bce45f08adf27e0fbcb396d0d0d2744ba05367e7a3cdb371277971d5b93a4053bf248c626ac25cd81301843d53b788c923c39fd188f587aea00c0d0a235c1568e88a9d3811b22a2b3b709d39d07d3d69eaecbc3838c0e3c32ccdbbbb61a0bb89075ee9c798b34b98bedf1a134a8bc87151b3f1a3fa51e79b37007340e4a68397e264550dc7406d578da7c5a76bd4ac09a860fd0065d681ef832229649474b3eddceef6b3ec24c9eaa6426ae356cd4162d955c2add8eb648016941e6c8615cc4fa4dbb6dfe83313f252bd26c40fd545da38dd1c1d375049412b84371b96dab5c58572c894f12d78aa7ff9a8796e1fc1b5258c290816c4aed526c1c60491e5342cc9bb9e3dbaaf4342b584d0928b47e8b9be73862c92631ba2093cda469121ba0243d1134976e4a29ed09271f33fc0dfd3e9e9ab836f91deca64aec52b5758cc3f41a0a95bdbf966cfa83d8fbc24b1eb580d6d0d1c13c170bdbeaf1e40cce72b3e4214d8a86b2ba8d897c3e1b2b19084b5cbdaf02b8f014ed154d44c924ba8c5d7489883fda4d576d9210d47ab2790f3d93acc5100833a48419f4bcadce571ec461a4f623e267b27a8f66849ee7f105c3753f0e89d101a4840ada8a939cb2c770d8e7f71717918d11f99d67463fce251cbd6e54a2376b491503249a8e6eb255d77e87417e4d7be4220e8379f0f404fc368ffc8c65e77e3ca27ae603b7f87c6c80c7a970c04117a558e8bd203a2a6ae81a25b845b4c8b573aadc40a85ad60424667ad5351dd3216b6984ececbe3c6dd3d82710d56f5c925f6f03401f9db416b2c9ef646c3ff7e699581e091a077315eb2d27e78214409fb244b7b28762deddacd0558e175363f67bec1dda4988a3509305a947c7493a607871a9f7f737ae4dfbc056642de8e6b58148bf3e9a737c0a2843bd1fc905254470da00afb3fab05f54c7465582460bfee68b9dde8ffcd13d1681828da58643eb1f378a36a09f7e3f3f781aee05b22c353171de5f6178bf3f6681002497096cd7f90f8e2f0b094a09399cf86745d106d015a4397e0504da659246fb0d4bd2061d88c678d6808bfe3c60b4de79191e83e505cc2fd10340b947c712c027ed5fea27b2d2ca9db60ad188f1bce6c003275fee3f91302f5f1cdbf4fe7f7ce0fbf2bf47d806640848a63642f52e4daf762460401b0df3e37fc820eba24a0acae95c587e3f74201185d6466c1c3f7ec17620d07cfb7c0aba034b3f7e5bddd56982b305ba269029205abf27b1240dd820e848fd9f42d659717b12da43f4affe5476ec01e949ca152467aba3458c395ea076e81f3957d46e53e3b98cfe1dc041054a62f89f8b4998a80b01bb5d3685e558a205807f9b0d45ffb263e20fa3d56f82b2f96e96516f7eab81309dd9c7268f0b2ce33533401f794a25bf308a7a3acf2945a2b9f32efdcf59fa778f93bfec3f273dfb6fb72945ed2770026f80e834f50f855afa5f7763dcefba6b606967ed7adcff260c541797febca252c0e49c2070388cb1e161fed108976f6bc29876a75f194d038436325a54291802383e30924ff3f1e76b963637d0d767bec43c7168498b636a35915ac7a5b3f16b4dae51820f4023cae01d37f4fbea4131218d37e250cb2913f22f23bedef3d6c6e748ad8a086df2af4793441e93d0012c31f7fb32e849e303b012039b0ab2ff5a329756117d3650821a31fc8fd0562316393f06d058869d2f643e27bc4fe587da5052df12a643401ac4679f9376f7cd62ff43da33559e1595379ef9a9ec96eab84f7d5ec6a6a2f06ac8967b96e7985375112576c03c32954d87bc982738add02c15a16d0d1fd4fcecd2b2b2f4e912bfe1fa4fc0596b2479ce7af4de19632e951021f893f22cd7f1fda69c51796d2366192f44cc0eac8dbb55bd23d7683c2c89a929081b702b4d8066e8877a95ca3f7161e4cedcd3db5fe4929bdcd3be4cf908fa289c48ef80bcc32b5972510f309823b34ec98b4ecc22f49abfb49187aea2798da5d71c503f640e92501cf59d62f752934c65f3b2ceb42f7bbff3821e8e8ec9b9102b82385539adcc8c6ba8c668da6a1ae748944e2c02281ac0dc56d28c5f0a921307f5101d495c26bfb2e58c5618ce5b7ec3a9502cc554bca0649ce2811c875645624ec72eeb1932916f0568cd5894d9347f03f25e50a03ef6b72dc2ea81d03cde786bd529191df16971ce28418a115d952cec3e099d7a8e4e6f417b12a53907495c9436f825c697f35454436782b4332849b9381ee15eaafad9ea57a4a229983b6596580e031b70d241d90d6c14eb604a9e962ccc2d2be5bdf05b9632bc5c2eb4cf0996dbdde8459675caf51a2efd58570bc3b9a3a3d1ae43a158efddf4620529dcfaa312ac282e71c52cb106027e008080d2847e803b006a6383c71b5cf6e09c6e084469027f5dc04569d5b699b8efe651194ea89018e7bef75d21c72e242ba36bc1c38ea6c63dc25326508c2ccd950962dd5ce665dcc663d40c5ce9874426842e5c78f290aff0128eb25d0492c5854eead499b658f744326c99432010c84cefeeadd237d6e6bf944f90e312d0f74b0a1dc144a89dfdc27bc90c76f39d07eb05bae73f1a2c0bf66b8fcff05ee4edd31a03b9f47e1d037ad57ae2ecd50f96b642b18b23e6c59ae1e102fce1475a933d5cdd54911a9bec4040c9b89f0e7e3e28bf60898475e62e989fb334e6562d1a0428b06e018f24d37733382783f066053018a869f8409d387ac04054e7c02efc515e1e1e5766f241503a13c7b7acda68560035a7ed2c102a8c8a07cbe93d3600ad6619818b82b537f3ddec44b29f40ec3ee360ba40411f29ae0ec5a6d389803de0409105567430b204abdafc14fc1a3caa89786eabcade22252fc0a4fb5e358b7f72837d623a59b82159c3c79fec18a845bf73cf5930fafc1d835bbd0c96ee3ecd656bef9e505b85f9ff479d8b2cc0e08581a816a8a759018c0bf0ac0d5b715f8f2428f4cb720ebc2065300c88649d1106e2ee19131b59acfbc2fac6ee348a649824174ac9b499e819077f67ac9a1a5edd4ad70a5af5fa417eca9acd3e93798e44ee27a807e60b998bef8f18895c315dee690783105e3a57ff083a88e3bf982020c9d08f1a895ce6941ea4a7479e769ec34b7fa1276535d3b7a98fc9f6da8d440e59cff822874d0a261a54065e1532123995fbe8031a75d83d6c60bda2837ddbf591c8159b65b142a1b15d39d5db55dda0a27b7254dc407708e17e9a11b62301381239dc2226de08346d4ff1ad857f8d66156e307de6aa8d828ddcb63d89ca68358aa03c08440c507224f2bbace01dd5448c077717e4fe420fa6d14209bd8fb0b967f6656643779825d5c0c7416e14e0350c28e40a141d8d1e0fbad99746cc9d555eea4ce33f0889ffea99360be01ee4d39040285f4bc4c239c787af62ee0007abc11d26975b5d8cbe8d80f06956bb8a1a06b8341df49b88935a70e731ab1499f8d8cc5eae73591034002e4ea559251a91f77d9040e02fed6058d4a854c380704ba7a0a13056c288dc136ff916670b6843ca2848e7d0984d8546e5eb1d52db96a904bd0c2b20afb48206ef75a529e4bdac8cf52d7525e7276244de0a884a2427eeda2d4443d2687b780624e92d135ae66e5b3751e8b92673db0c201faae8bb5bb142b42e600e57ff760f02e043220726419b8a43a3fd29304c39524f38dfabeba02ec00320f1375212926ccc29817f8386cb6954ad9ae096695cd83ec8e8fb7e01ec6c23540134f7b82f901ea51b3b317adce3d23f32159992e149a08c5f604b3895a4ad758b35f2e1b6000bdcd9d7fece07fa86513bba048c941d66bf5aed5f9661e22c8f9c9733c69f9e0c610b37dadd9995ee02b444aa7d51f1c60b23e4b347dfd2e5c47d96e9155182a1e3a8609ede1a5a80d81e97b0b1cd942ea42f7936718bf91dafcdd58a8375ac53034a96c49505422c875eff38974ccfc424f5447e1fc854979ceb36256cd046c3588eb7078d7abc7071fc764fb151c29fbfd5a6794c207c61c5bea12cdab930ada768c8139331d94d52f6011e46ba2bd455c057e0c5327ba955a9b689f4b52000aa36b7478cb799fa0fd0937c85d80892e64c9f4ea57ca88cf24802cd8e1f0f7b05d89afd854e0ae382ec00901c6faf4b71009834decb65a76207583ebf0a099bd1469f43afb278df23c8d09502e816611ba486f6e8d3e5ead3b015382ab52bab6077a7372fce5302e4ada8d91e46692a194faeccd993a64dd9b19d4a6214ec2721419c63f80df295ebdb6989063b0a62c29a026312c62a1193c29bb03c18102858ad8951fb5476a01b889d3671c5a3c317091e064bf8d173c699b2ed842e1dd5041cfd80bd35d5198938efdd9444f6a834e94d2c5065cb975e93dd82d71b583fdac7cdfee0022647ee5023624e7a40c7ca2c9ce535f04c209bf9bf7a7e37996801aa32113bcda8a142ba4773e01469360277f061949d0a3fd46afe2b7d4c6b240e94e59bf324bf6160661db87bf8ed7ecbac521b0106d8dd812289addbaca5df5eded55f182827ceaf96707611b6898c198b3b8f3953678d75f1d8fa383f9a6f8c51b228aba84d382498879fd74454c4052eaabfd1cf717e0385f4f7797386776a5505a936370b87ece745e43966bb51d46fdffb5fa86c856bab625561813ae868d860652c04358be0613d831a958a7f4ee54d52beb67720d1255bb5ebb4046bd0494f51ceeac173e36a2e59666dc651e22fbea91c546b1a1a528d770fc63b5cb7d6f004a74f31c3d8021938ff56c8fea297a0db2a16b779b9474343f594f9eba7fbbc04484c1fd7b8974a5c235609131dbe4c89cd0e438dcd5e4d0bc355fa0f152ea4e281274d798bd059a6ef262e26fe5f628e09b60348c2c910833d036a6a9d026e85bec5e0ba35c9599482a24903a1b351961e4defa3048486957b365ae4a8f668c4971f653aed93f91036eb6eaf42fcb7a0886cbd6f6a92ac7c0a805289e62f7a1a6c9cf4a2936d85c1d48c7898d5a21a935bd1c7d0b628f505970fddcc5a0fe26b62af2423bd1340ab5af6df13ba74c7b962df4bef3299a4b4292faacf657c642c16b5ea6f8bdde4082749020e3844f5a0383e4cb27484e471a1ec0d7dff1360b7f860f4853ffc4a26a52e2b5672fb4da148b45a4987c61d490017c283e94ebc2a91b5b555a101de18509c4f09669d25a3bfd56c6a0e195fd2ef8bac45b3c7f2edc1187ce08da46795d4469e724f19351fd0edc907c7893f9f4f7f4d9842292e3e6aa994ecbe9f3e6072f91d2d37c6752954008bcac98a517f6ecb7d871acdc63156ca36c82a43e5c1f1bf4730c8740e4431fe89f62292c9e240ad7b0cf59e4265c10871ed4e9a1dfc0a2b3eb14a659157df93ad5739c582b36eaf37307e0a9e3de06ebbdc456b245c1eeccd4f6e6134c79a36d0bf5d66e71152829cfa17628ecda3a64347c7d1d54423f9d4f5a01aed9b2a1b06556746620a7ad149cef0e124121fcbed70dc64504e384fcc94195236862566c720a4cd87be421f2ff829adbde6b510f222e313482ba742451272d66207682d003818b568d6d239b4beb2510506dcf4c385f481d8b7047cace1b470a9156bfe9a562883dfce887e9b26d4c32763a1c737523e2e4ad4cc729d09efcdadd511c786b022c580117902f5225cc8bb2a84fed16ca19b7da6c56d71ea603818694b8a31468d227c1af44362f9c323400967df20a1adddac65e4273294cce44ea712dfbcabd1b6fb46cfa168149f40e6274ce69cd5eab5b7c301f30a243d2cfb33c5872e8f68b5c23c97e6de4942e76dfb9d5e81bf365a475ced7b4afa735cce12d0c1f9e7a69a4bf8d00fc7fc7aa83d56fe834ebb03a0a40ef54ceb07fb0a7a79dc924d42c6261b8c5822274367d29225739dbff839802775c05eaa60849cc6a14a2999a33f0d84e2231e68bf9258503795eeec2fbcf4fa5203effe04f8318135167fa229793b5a734bf82e7b561d989000348b1b1533bbce2551820d5f0efc1d00dbe24474e0c221ac1edc84dc0825fba9c53ffd0ceed03c8bf93ac2eb872c2996839f07c55ce1b9c17167e6795b620fbb91604438703fb413158952d09f4e578b3b19c3e5b0d2ec9735543a7c25cdc8da3a60eb31128afafff6486906e8d40311dcc7aad93421b7621d0c2be94ce560de844da5e08dc3cbd31950134c8f48c3afd41900e7171014532affe0f07e4c1999f9220c55d7b4fe78064c66d5e3a29ecfd3f639d6cf64d32a54e4ad9d8759aad96603520bdc96d694406ff20f2bdae88b407a520a9592cb4a452d50d4fee17ed90daa3e95d68df745deefc984cab610d98d0442066e6fdeb552d34a1a4798e6a32140bf0a4635d92f688b566d98d4681f9150e3ef3b2b2bc7c6a725d5caaa6c74465460574b606ad4d8357b43d1a1fe616be7c46f304fa373f66bfa1a07bd3e6a797cce7753cce1e92d4c4e957783449de2f142256940be30b55fb8ee31f848a1d861bc3685704f6eb00cdc36072c2dd67b5b1f44886db566736807ad177e38d2d6400335b549f4d34616e4e5852f2de0887d65f4d9fcffffcff2e3b30bec5924982c0df19ef3d5a02eaa377fe4602b7e9056c2d82851f367c95197c24996f15d705b0ace6387a29aac096176492f492d6af3b002c8ac54b0a16d98dfe669ff51c61dbc094ddaa7210fa8a6f6bb264db414af437bdb8b95e7d83aec4908f144275f8f807531c3c59025dc936038a08f91d8f3b34a004eb3edeb37f1d1fab7e985dc067473dc10dc456549e67b8ceaab8555458b7a41008198030a58b2b2d111a75a52e03e1722a8e9a8cb59756827bf236d8e10ee2aaab7bbc1f3fa8d46af0b105f631666511ad1d01668a27a80569e3113fd9dce704461f7142c6f44dec9e2b3af8e3ecca03eafaaa92ca545136cea4f4f6612da3830635b5927535f6d2fbdac359189c7016801f83fec05898d44abdeb364bdbb2c63e3a17c1a36603d5009319c429eb653c519f0eb4d53d9d01459ced4e3cb967f489653e26ddee72de0a6013a2ed9be95a23a4e1e41f0ea1fbb566aa282e6eab66b30aead1d68f4192d53662364c4e121104778dfca5da50ceef771fe8f468b4114c304ec5be87ac9e7822cc408eb0188eeb87f5ee821e01c6d60ef1bf46eb896a8774ae15ecb80de434f0be1b74c10050d66b9bad7031d0bbb1fef3ddf429ed9fe6332bca5166497a914baefbe97c3de99c2298600ba7607c82af23451f4f46d4ce107d443f29f5f746301fd97fa90fab87db6fb2f2a3d1339f12fa49f3b9d035fef1849da61b2fa945c7a3b3528c8dd3e5b33d33ec9f90ffb031b65252fa1242d9d7200dd1600cc50d13840b02b9a7a44947631229fd211992d8b83c665d463801ee8e3139de6f6cb3bd704bd7d1cc04744b12aff5591e3ed2fbb39dd302a1718341e60868e1e20068afbe0d676e62a090fe5459e1242c144e5fad78dc5cafb95b8fed08d81a06cd3abdce9901f62d243d1a220ea8b7dc805d693c1e1df341515f1276f48e8382f209b935301a4effac233dbce1f620833f8afd354c3da203d988983ce2676808e36daa13dbc5d5b4198b28d5f357219fec323e1f104c3f66e821a057067a10263df1ad76e2b1cd0eef471ac3873fadaee3c95f89aa27a33e623d29350dccb3a00777de608973eb84569c26637df5c495d9faa04482c02f1f12c7e340e5e683c2f6e8e139710db85ec46e5b503edeb0890686b104ea00957663d81d9204799ec0deb4d2e36f04d351f6375fcb8e90679e77766d0a069f4dea50dd9314cd3bb175adc213fdb6ca039d517ad2b4184eed28eca321e760a5692046376dc18807389034989c349f381ee0f986bdf55636a7506b108142afaf9bc2f629a120229e0c7b98f95d0410f46c6c63b14c8de172d0441cd1bb68ebb226f22c57afea6458a1d5cc05e56a0b2a066313a7b3740c969ee51ce5d4ffac581a63c282b4dc7a558c7eb0f2fd10387074b2b93cf69224aa28f4a7ff9036558577e7a605f1456df51641f71911879fc3c1d0f15560ac5585a2a6e09c9e43e2bd9aa3a035225f9ec147ef20ab91abff3a30a39d1a96096f1aa9571b71e036b4f20167e6d7407b6cbf5690630eed6335e53cc2e235e2654bb62e8cadeec90a1e348e6292a5a1107582c86f9819619231e115d55014db8ebb71f7303d5c27943164590406b460983598aa4318235e2bedef92636f63fd6d99a5af3bce75fe7eac5c825f9f7bbb3e473ae1c331242caa62da81d851d3727a14d326ac0d9ecac123a33b769b3ef8b76ed3a676e24f91a70cb0df9d024d92f739643ba03e38587f6c69bd9dbacb26c1f1fea01f04368c6a409896e52389c12f2349d5cfccb551e3c9c83cbadb7e7e2acc9359880b5bca87c2010c23ca4ccfb5851028c38da63d8a863c521c995f6bba7150c993cb7754c9fa753e35d3254cdf1b9abe52bbb4e9fb9a47e6530ecf0a14a356e9d6901fb89103b9d7ab817f72335ffa9915f76a221ad6f787ac9b519e4e129ba2efc434356ec49b0804427215a623b7dc8b8da3f1ed3d661bee62a0ed930c68d43faacfc482690b05bf87d932b38611b6788e28e42247a1101278be0beb1a63c726f85f6f0ea016b7f9cc4d459e69e3631dca0fc596fc528afb5f147f249ee3a8cf39081c1df336c958f3eb2375b9eb709151f1e10de6ca2afb2b67912d50b7b7855d44fa6df86ae1a562872576ef2056b813d993ca7ff20d4649a40c298cf34e248d4fcd3566f418949182019971660a98c8770a1ee4c68bc5c69fc97adecbf8f49e2bb218fe2db8d80b2b6dd0be327b8438dda147d5bcc9a2118cbe4312743694a8f06b8325f4c40d253734b4cc560b3563703c9c6a505534eb1902ff29d2349614d4c9f8f56ce0075e6ee5202ca8bc85d968255d215f5a30991d0e93bdc89ebbc1f86b4f18ba36b53be2b631a2994535d2419a966ada47bc33d71f2c1197e466380d124c9cd35869112a732222ded64e1ee6f43d21d87096c484aa24d727824e09bb873b463f7e543b1e43fcf41e67731167943c0530df45c072d57fe85f1dc39a6cad2c74cce248dd3add28d473f41a6d2e57f5ba0fbd6957ce78db70bc89c3abc1390e95050e7e242f8e25e399b2a48c141d0c80d4a740f2733e1c1c9c6d28eb3b3ff4c7cdfd7e15ede4379c6ab8f06276b6f170f68cfeaffde3023cf4c3d8d091a21f3dfa85d815fa346554abe3907c35aa08f7ef60f027fb7c6c508496f4aa66501b5990107cb4dab0d0dc4383e4c607cb2dbf425004612008d569880d62d53b8f7ed118c79a8a0f69629de131a8a6dc76c8d3903d833be151bbac5b731178fbf0f4f050a5824f98ebaaf834b4aade44682ec1e2696a1265c21bfffc69f64c6bf04d7c8c5cdefcbbea433a152f585a2d75943bf5262324b56206bcaddfcababac166d25dc12da261fd78b12d37f5b0f13c279ed84e1469593da0b82f01bc81e0ac5461723bd974f482f668f97284c5aa7db6425add3118f24ad13b1124357639a4b1b675c120934862c89e051fb9db8e3d6d1d3c6e23d79f9fba0d6fcd2235610d5078180be51f2e60c1da4016107ba12262454c08b5418ab6562fe0669f5ede59d7c36358ba3d683898bbdd7e233d89af60a294c0d3211615abf9dd3e659122414cd9d97748f56c9e7efc26ee22cf7ce1a3091b0f743eea1c820a8854a279a55865d9475ed4044f51301a96f57f88d62fe34e1891fd75ea0dcf8be3e10c53e1322c105bd316c9827675a511762f5ebbb006ba6161f2672708c2a692a10c2203413eba5fea8a5da22d5277935602e5d0046c78e47db2cbc3955c657652ddf61b83d6e1b6cb3b71e84c22d8517914d72260fd205f975683a6256bf19cd90868d4842a5efdc64cef019961ac79e85fae69374dd7c03d5820f3b7b62bf7f7bac57b8a79948fb41ec0442b925f298fa7a20d578864f69721125bb39fb3f3b0fed1d3c1faa2a499122f9aed297caa4809e2c66ef66c187b6fa8f940ec9bec3e1554ca2aef2bd6930f000315770c695a85454704e5f8efd155468bc02a19566ab2ec9a823a3b7caed38da1b522c66f95e2c7f66ea0d2d8df06f9b83fc3483d666b2a9a6c96385f475294e4aa72dd8009f667d34391cc4520281e1b164def2d0e88cdc6dd9e041329c6ee6e9eaaa714098ad2738eb45de231ad8f3a1c65084216a9903ae6bb6e5839e98af2e5bb32dcef0b17ef22300c619016cf3faa209783acaa755d0da80c5d31b124bd4140734f7cf4bea7fb07c9068c28e2c81ad94a909ebd270e98c7621a5922aca6a6bcd979cb54e5bfacbf5029585f05e3a56005acef42c59f506b62229e0bc6214acee6e0310ece9b3c9b8142056bde0737a88d0af6c5c17787b70456191ad312e4be8fe8b52fab9277edab27238a93c68d5e8747e8e2d310551722f4367536e89fab646057e5183054188811618f992d85147a6f169654da99fd0ea010b14f8e22c928478764732c869cbb695d5b502a49bbea25bd27e07d92be09b44d84853c3a37506386150103243c35807d9dfb03515ad2cd7b381d7457533473859268bc0794939e3bf945715066da873c428fc286dff5e3aef4920f0de312d38cc82ae6b76c75cc1437d71356daee2233e88ae4c1ab8cb4d952f07ace30890946cbc4c6282d66f41874aa86bb813ed5e64127222f7062a0408e8936dca8eb8805cc4b6f0fc800a7ab395b3b3d774242755676d76615a6d919b91155f625091f48d68b6ce99e479fd1d5bd1356e61c5040b435a827604e08701f42bf0722df74a883e6f5a51bf97b893fe1f732dd08faa908b8f47a2c24fe5cf96fd1f4ff9a7c9bce873d253e100c8da8d764f78a04378f1d4b9b8fb2bdb6737ab004fe2036b47285f653095b3b41cd99811da5083744e849b81a42fb69890dd107a175e5f9efa8c5804b6b4ee6c8c8fee5ca2b38863d95a34e5e4f7910fcedb64d8ef30933c5dff878e47c2149637a7381e6a303bd38910ea639db4f78cbe7c5acbe8434c4eea53a2a6ef3f98e0c436dc85a45e399270d622411f18f60d10179a8b43432867ceea7a41b2bebd1fa608e6bbf8deb3aacac083b7c9cb5499aa2f8a0e9aa31987aae36f1abe4b7601ff640a49925945364754a6b2cbee4867af37439b08a70e82e200619c3b6d34c1620b9d01213fefb245423af422484f3b517130d82825e836da6f7f6e7bc541a0141813a01c3535f418bfd682fb1b4d1773f53bd78865ab031aaaf463794c575867bd5663f969ff27137a20b71be552a8bec917dd8960a1993182a89ac75ac8ffb002759c1d20f2283d13645dfb3e06d5acc3bd272ae9d408829a52cf227a99da8f0aba9c97ec1223e4b40ed2b6b13a9ad101ee2bdfc82ba7a64fabcfc0a907d6cf78986cb7aab9c75245f3af14be5d0207f6df22e1a5c4c78fb392aaf6781843fb8d60b5191ab82c9f2824108e761d1c2949ba310d324b2e9db0ac4c32abf7388423db3f546942e1fc81d62e288b74ab4241c438d55c5799a3adc5eebd45e8ad299c66555c584faa208ca6f9a1f02d78a96532f1ddd1eabc46f6f41e96703f5ddaaafc5e80b183e009f8ca771261924d0ad5dc1da753b15fab43ba0eb2a6f37671f5a256c17b16e577b67858b812c830563ab2af593aa6a1204a132400acaaebf4fea7a2a654b532d224e5132679f9f1f41fecf9a20cc911e6807905ba34a934071342154782b7b69e9e4dad31a846539a5475b071df0b4c768e5b044406b85ac80a95442a573ed04f8a9c3e2900c00f9b784e758dba1cd06f47ff4a570ec5c0e2cbd9f5f15658fde6410b550690647528d3c4bf93dec5c44e5109349e59b12cda0ebbb32aaeafbf198af71179f20787d99602dee522d3b631638394e77b5a9e8168946d9227b348121cba66e0f7fb0653007e823d49a7c9eb953ae21ca4a271ef50c486c2f48a54ced2ad852f1d2bd184d044c9e72e32a98f356313e79a21fcd60103c03d001653868f8c6f71a68f52584939d9e98f8fc835605d94fbf105316a768ed77c0f900688ab19aa6d12e0cdaecd75f513963a4bbde3ee0bfb44fdaa5c06ef92560b0bfc70373655e04b8d6ea16edcea6f69d75efb940079242e7fc2436f48fa12a5942ec325f34d2cdc24bc27e3879b907ae8410bf81bb13341b01cecca683de2d76e524e7e00ce8342cc02f301bbffa13d93fee7dfa34f997cc20557e0601ef9d3bdef636e8f8eeb527a75efc1cae217933862154bace21647fce22c3602346efc58a99589590c88d5890f0f8a39213688ef88d2d72cf0402c54f539ffbf4bec29d75f5f87d45b17039fab3cb018a2e8629ad0e71b51f479a12241e9d1c6b29948c0155e63eec5a3cd19a550c7456e38b858e31237d527149d29118bfa86d41e9f8a51eb9167f3909696440b15ddd5aaae625af9e2e2946a045852b9e1fa18bc0cfaf3942c5cab85b54754950e67f63de2078c5b5926630a347db0585099b8aafa59600e8aeffdfdcec7aed2af3e33b45a1ca20a619da2420a636b49b190d79d6699706ea6e8667bfd51cfe490a915e725f47d80c91972ba36e6715dbf68e766236c5d21b83aecce15b5025753e88d5bd75b2a8fc76d4fc74d717222fe4f6ab02ede19df7b4a9f7e24aca42c97504cbbf844f35eae9705e2cc5014f9f2c5305e88dc54be701eff61d790986b770370e69172479a54ccd6961b3542a40e65288002c5d016404f5cba8716da855a9001eba948082b187f267558870f73bdef541892014539313efa1b6b34c1d1bfaf968b4493cd2918a460109c54535562b4779833a43078803743bf651611a24373f85787b4516ef6ffe4f221aa7001dcf3e4b6493df7265cf86d975df4284580837c961b192b27a0f1f25c36609b57720210082bd27ebebc00f2ffe7a4f422040346ab2e99514bb66f0383df7ac42f50ddc02df899ac5c9b90944278ec01fd7189c3c5cb23da73e0d1413a1f1103360d48624edb633184f93a962645659a2fd0e75030168894601eaeab3e4f078c8011e372e9dfad97ca64b77252f48a436fd60a1332fca08183c77825c0ec4281eaccf6733635ab1dc17696a44a30da6f0ce8c45a3bf6a8e9eba235e374fb330ebb9b97368115ad8f9af95207518cd81e0d2ac17b20119a4b743568acd963863a65fb275f49b954f2e453a26bfa6b4097331c6202856407b4679bd6ac5ec354512265ac28d1f22b7c51944dec41c77ccd03b76879b8e5a2810631e102c5d5971ff69e155b98011bc18a5fa1eca2636e0bf0e070c40ba823a15c0c95ef97eda6c3271bd3306fd7ef416d675c6ac049d465f71fd361b4919ed4b4b63afa7ee2ebf1b61db411fa212847b253a09fc8718607e8b5a35b5db947f78f48fcc5a49b5eaaa08abcf8e6a3a24c1513f3a82a5f21103fa5be532a9774c5dee711ba5be4ba9cde5bfb5fda542c8c42a4035c9ffadb9d209471e826fa460b3c78fd33ce20c706794692668686fbc50e0ab05ba7a2b2193a848024ce4cc5f7ea33a2790cc27f7c047427234628461bfe9a62b289d2212d2bb53b9ab806d35a24ee20a281af15cbf65165c20b649c67d81b03d5b45e36182976e87032c7f4a5203a10c1622b971c15cba41cce09e80cd9e102d79203233f5467d76b306610ab0f2e4aac3b8162ffa1c40b803f059d4c8e46b50c577d714c756c23c5f62480cd08a9735b93667430242dae3fc536f8310ae71cb975253af93987bf2feb9b58e95b0de23be1460a58856705b0528560bf5a3c175f70e5b622cdb0404fa33910527f81bb129de2c0fd35994aff539e2de2a2725a10bbdfd8abd5b995a1d516e5b14126bfee6a4526bfbd889b2d73b31ee9be452791f6fdaeacdf8e719780dec3842f2a8268e9d2dc63d71426a1af7c392b0a0ad03bac72877d31bec2cf5a18c560020c5570e9101603459ee2fa7def922167d7c10f5ba97bf64a645cb0f6ae446c7feb038059690086638c1cbf3e00139aa66cb2c016540a5332805155460397a6a4f452308f6a11f0f90fd6f4db254c2a07de7a53865815063ff565664bca2e0d21f944233ed7ca5b037969e08014732d97104c428b089182c0bf137ad0c6f08f0034bd39bfd0ff133ae333462e8252feb624215dccf2a2f107d368f809cbe386dacc04f039536e7d40f40f446c93943ba53a9dac9b684e30f36ffcf05f5b7bd58bfb273ef4f6b451cbb721697d99a798f3bcbbd8dc7286abbe85f6cd618a11837a3a5e2b83338aaec8654d07067f16565a7ddf2068aa410a939df64595d7fcdedb9a4fd4042693c05357c9b1fa8d301ed0326b277b5eabd90ac46cd88c43425b76d79af9beb5db8ee91b45c4b90bf354d8cf2b484e9d87524b84a65e1438893a0b473bc39043d5d02ee5e750d3a09a922e98cae97074cb03b199434ea5b111492991df98e92dcc0123a16ac7970e7fdd1fb7be4637484bd0341da7a939622ab6c0f7c1c150e8085fbfc2c47c287aafe81237e613306f92e9e8c4fb28b31763682f30f284b9647f7015a9ed21d3c8b4f08130a4ffa15bfc51b3beef5337f0f4f2b09dc59de4e32458f85d181766ad867492f06070bde8cc60b4c372984ed6479ed008ed37a6887e815042709dadb196c5f4cac26bfbb7147ad2044a76efd324f778c07359b8cbac4db9b5d03e0080591a5143181594525fef117332650bda2b3c12714bf7fa56e2899cb3d6b9226c7a0045141a3d346bc8437d30c04531b7e2aeaf3d2fe27fad268379a944aa3c40dda6b7e58d4c612f1fd6d4442c9aaca907a2bbbdb26dcc9e4fcc2768e01cc03178740818b8af7d6095803d6a287e045e864114d24dd6e4ad7675965e96e7ae81b89e993c7d9ebbb7ff52ad2ddf7bf6c0c462f0a881f523dbd1a46838725520955825b91f7ac7f1d1cba4a0e5bd0b3077e90fcf662dc7d12a36de80c855ecb6e219339a0b9b39d14663dc1d7abdbc1d8e74b618ab939c96a0af6876fe7fcf427bec69084bd90a944dcc63ee18556d61808a531276bab1cfeb4c949d896dbc8fadea848105b2fe4c4e176c51f8ecf26e13cfaa6efe934a5eb21421a2f3e78d64a3001372a1044bc2805d5308c8f4c4d1feaa3fbf31858b4957b5ee6884afbad58e4f8d989f6c387c34f56209cc692bd05d01eaf107b21a3fb5cbd5673ec5f9751d3057cd2c6d8890eebae97755ade174e62ba9aa5d7bb1fa33ca68f86cf05fbd3030cc1a88d5d00c44b0078a97d2b156ebb24185607a5e99b7a683b738506ee50067f54485988eb6ab176bae265fc544e51cdf8af2fa4a1ad09e6a3cfe95d71a6c69dd5708c9bf9a33bcd1494a160609aa68821711d2891aa05f7af20fd99e39763cb52448b28e20e6f59412b6316c7865f36801461894e50151841dcf3dcfc7336cbe9412ccb2c3d3743b92694a2a3f9e241374acb5f717002759f397eba3663769a8e3071be302ba2de917273da97ccceeb8e949ecf8daa967ca049170b3001b56cdc2a5c638f6a922136fffc366c62549919b691679f711cfae3feb6ff925a627559deb5b727c4e75a04a1fcde8526dbade82018fa21d69c1cd9c49f875271be647214b3984301cca1966d6c73e0440fb056ca75bcef0b2a268a4054e5679edf633b7fca509afa9ef9c95648306adde1aaeac2168f05e38028df4a0fd268070cc84daa62e351bad03bdd58a3feefc24b64655fe766fd4db76bf08cae84b75fed14be0abd60699502bc7a474cc8ba78c488fb44c9648289b0c604aa042d4a35da205bb748372979cb8151b4735b1bedf9021dd360cfc60fe157a383a83de24c3a00f1e89eb9d793d879f74edaa242a274478d2534b07a47d72c541b0cdd015fd4302724cf3d6030a20281309692998f9dcd8f0bd940db217ee8815420b47825babb6b4f5b6dd433683a1dc33b6ee6fd5794c9c7d680907cb9a3e5c00b4d4fb5b7c8042816b6df12ad1a4a1eb8c7f0b9b7673496cb766a89e2df435d8598c1cb66284c4619381eada128f2cb6717c9490fe25db3fdaeebd7291e25bab9ce62ed7a49291c8c830ff34cd4ab7d8efb2a232f12dbcee9d18682ba09e714bd407a860800af2de63924afe1cbed521f4119fa4dba7a45fb8d0dfa126b0f8d6c0df722d853081785c0942d5176533090ef4ecf1ad07d996b8a1585f8b6f7deaf2802a0a34b30a003c505581bcd011079537d5a17c1176e67b28a3859d75bc4c7df1f3263d08b57e14e743850c068797cc3151c16f0c9edf71ba3b7028242b36c944f3fb2107d2dca21dd2d32a7f7cddfa0665104c9a88744f81738e720fe41c48aa6aa65570e59a3e4fda8859c105cedd0f4d562eca9be45ee2778e987e476fd29b90796d19d072e95d60fb2149ec6c3e1feb10e4de9643a461f11c7d245cc5d8c43f71d2283491835f8d1047f6b4c404de9d0166ae9e57ba5fde471937d0877949f26893c86d0f56fe6cab893b62ba304c943bd58960b405fb131af9da940383462c34bc9297bd1312527e29d69571630dd867983ddaa190d30304a9368bb6ddbdc96ba9ca22f40466416bb37cdb93a3598d5fa1be60a281b10a024677bd667be918b4b75d55b2cf09279e30d9e693bc4489dc563236b890d18bbb03f8c91ce0f60ee482ed9f874d4610184a5ff7a38ffedc1c9873b480a00460827340996a2f0ecf50e88a94002441f5a2ac7141781eff55336a58715be03aea6293fa6e71b89cf8c26a154b5dd02a2e4897ffb22249ed66f9d73a349a2e35b1479c7b323c9a9484b844cf593e4ac385b0aace375dc83d81af796d93da67fd892ff0766cd7328427d06c6968f7dcf29b80d503fd5b296f9465e41e0bd43a77fda53dd019b008d50018b668ecc90ae27bb92c5d8a0f85f14b1386cbbad6db57ca9ed1595ab48f021ee23cda110c6b9a1bfc77ea6de1004c480910b4dbc4465221ed9cc128bfabce32d4ae1c549c572be02d1898913e03bc3c7980dcf652a30815faf642ff867ebb1b58eedce8a46a3f7bf9012f434987a43ba499380d3182ca3ed03c37639284c04e2aa83a1957e3d39f81709018d689b086d665e3f122f1a1efdd9f2bc76f173d05279ab825d98c8b88afc3f6cbed599402823482c2543c95aa7c11f174c2d5fc877de5f3e3f20edd8dc1f10e54768cb6a3bca7ac0398207bab5c0a5ba32a35e01c1274d5a69e6f41ea070c5283a743d3d713eb459ba1ecf54f97b60af1898158f96a16408005b8da4892ef0e6500d232ae502fd3c781fd978b1fa20fd58b4c244619d935c972a826f66f9ea63b3745afc3a6dbcd70514583999daeda9047c66e280d68c3a02dc2e1e20b35516f207054942fabcdfc817406a98fb01d1622ab9e030c44ce3926eea6116d17a2df613e4c62a1435487b0391e5a02440b244dd7dc409109bb51dab6796971ae0ec6c833816f7f0d70ac36c091a5119253061a30311e7b71ceacae0d82bedd8488da9564118c9f2ab012c07a10dd8e6152e808b774f56875b020a6858aa70f88484cc510863e40b35a6557ba6f5ae646b3b80903700dd6df83516df26934e0f0403d70e063d3bfcc08958ad7d783347a564b1d15780c055cb20747edb31d089c4cf76411068ae804bb15b1ece71a686fcbab4adc03bc63d558ffc923fa4c432fb3b4c74402605a05a8eca91e867597c92e25db30ae3dfbc9406fa663141da7aa0cf93dbd6e2875278f3d815ee35e5a3c027e12d8af63a09aede60ce7c7b2d51cf6657965b01d3bf179f38222a8020d645bc4cafc9cfae0468ad6bba5116805bf2b93a6bd12800bd678f37aa748e3fb6e854d8a214089c22fb8fbd55cd9f5726c5229213651653bf0ad6c62990d46371d40ef0199d32cbc4910719704fe883f298eb4ba5d5ccf148c6a90db6db15cfbb63010743cbdaf658c951f23abeff127f872928f2fe91eb8674d8fbd11302395d0736858d883c3aa658c886793b78288f26e255f81bb6fbf18ce2ed908af2bd71e580b4bdccf1445860f626eb1cd84698d8af42d40ef23a2b0771cacccce3f9719eb513bd70cbf4cb399f089f4a00281a45ec722b5e2232085b6e8f786134992193060c91b409cc00825b9839890b4580cac1dba5b3126f7df33ca77dbb38e646cfac2f353c65bf3fac4e5a7e415fdc563e39d144f8f23ee454e098102887c45771c05fb5c4205468dc3af8ce0450037955b2a74f0484d97030e8261824b8f10629898e0f594d2b0fb491467bf1a350c17cc548163eb9d8bd664629bfec73a897bd77110ce735bc507125b1dcf1822fe864a4bfe58a81eae543a47bc33df30f40a1bf536819cebda26fd035543434c766317c25243bf0b0414e257400ec8ff7526821d437e5086aa5feca42eab0ae00d5767012a4ebfc14807ec8c73990c9e100279d3bd1ff110a92859c09c8ad79389c485734bc33e73a60c4b56c30112829576c14d6e7fa9d940f389be9587ee9f26d40aaec693d0fcd12dd8b17cfe98eaf29ef4c10e3c492b0a7b426f17d3a6ee5f2a5a273ca1355d0718853d6eb127c41bf1e2a5ffc19bcbe2cd277ca92c12323be9148927d1363b7fbb13967f6fdc87347b23c5a0bb7f05bf55e86b306479aa3c57f1fd76026a62c08665f7941177d76c1e3a7d52ac981b0cb2175a25c33eee684e841173dc9992fd1249fb3b112a54df9ace4588baf4c54ef0e262850d09e14828aeef005d4e12add34b81954ac277da8be2124172835048799496f7463dfe69110fd901979aea6794f7ce9154d58177137c2e696ee77091a9c84e3a0a6864bcc34ed47c9d10490d12b14f1cd86137ea6eff81ee806a948787a8221fd0b1d0b4cdad27d2ebcd9b46acd3b947f3317e007439511487427a0c46c19aedd7fccd985c78203009304eea257f6cff0765c989f8537e895ac928cdfe05db6e1d80df6fe844e4090c8cc2c2efa981028543d6b84ac7ce0b84546ba69bf1f884f7c001442b42acb199b758a59819ab1c16089b44959faa9e273d9807d86b1a5765e4b048f6d42c6c993841679e11d43116ad59b15118ba16386f816d3e37fb1c48f591fea8e9a874a8ae4ef4c33696add78f355b56ab71b59831419680b25999decb9f27649eb5f776c9daca93cd81296e6daffc2daadbd49a0f12ebf6058c8e3f0d3223c15ab57703826deeff9fe6762037bd6aa9c91c5bcc939b6a55e66dec94577634d69a32d28c9dbafaf5c9cfdb126536a8fb33947a8a44ed10b799219fb5c05f4ba4ff0faae92d11da665522463bc3adddc82d58139f17afcd984b12ddc1ae83788ed484f762d48cb1f686cce7a9ceca86bc07dae666f80c65df96473dbc929bcd6ca92177a5ded8577d4057366055c652ddf4e2f831f34485ad2c9fea2c0a7d2988be3615bf2f6d686afae0757ff94cbd42e79a5132bfb4193d3b72531f88d8ef2dc40c4eed44b00976a0305f63307763fc81d69061951e1e82da0c50afeefd934c5d05e93dedf8e19f1dff7a32db77fb4a71629d51994c23afd42a3a8664cd6e7e7d71103b2069c0fbf2ebbe5545c3b60c7bfe4a21c5b768b2e402b4156870cbb1d66038ed591394c84294e3a761449156cb4cc919e4b94c79bd28ae1b281971d5b13306539e714ba0871adac573cfec5a119fbe50d7da638ebe0467554af9d433552a7db257c1e54d4259869eb157c13c0b6f87a66f0960c62fb9e083dcc36755850e355829938c2d9bf49c111fc1fb92d0ee7b4ba9f6ac5cb726fb3411d416749738ee7c5e39fbbc9066c9ab44db6756cd19e0b1ea5676aabfae37a30a20c8ea9915683aea6d2c533af92ee44647cc0b9295e4e64eb6a14aaada27d5d224aa82dd05d9bd78fc906076bde4c95a9e1b2cad2188d7f53ffe23b4f0d2380256d022c4bf6646d06cdac1f34ea25486b756cac7c8f4148999d25dd2ac4331ce09da9c03b2a78a9a04d2844dc2495ad5b6bc3a6bb0ca00ed4de2903dd69016caa18f7fff219fdc6cca26141958c6ecc669fd3654ebac37e269e09bbc17a396d2bc37566c534600316968c7a79ea0763c1ca9799fefcd4881773c864bd19b094ddfc1edd4903d545846ac9d54c2dc2d45de4b1aea92b1f85f73a0350acf909b2364ee38630b276dee59109b526a6cec068af7ad3b82f2d582f883aa69ca593c18ac0cab8a60c1a174d57ad5274a5e8138e2144b8c621647ece28847b121caf82aad28eba8e4a3e446d0e6b3296a1e63ee766d789ee1e17df6ddc8bc9e356a2bbf568c3be1173ee2d2c56ad9101496d123e1a71c37856995e54f67eae32569e27ee31ac392a069766c4bcb322dbb5b41938134d9356dded9b6e28533747857eaf1cb726419f263ffb25603996992f93c643985f5c7f51d7140f2dae4f4ed88a1c58b8a6ac2833a8610a4a124049d8c5392cbc1358d98b087208dc6780d11accfccf7d928f146dc35c3d06c9061642b4edb157f9c83d54520a217057a513f0ae64c1d2889483c0fb1face15b4d39dc006deb28111372e4762ac5682acab92e904b1abc4ed1833d61511a3c61f496ae1262da8c5079773d25f8fd981e0443cddf65ca942e131ca82b8d1533706a26e616c789cc9864a34b88a934d347da2a759c4ad4c49cef44b5a66421861f67407d4c359a3043d12cd1ab8773b7762d68b231d4ed940f2935dabb112afe6ecb60df715895af22339c1b8fed05f1ab60435ca6f4cf73d44c8c84a10d9eed44842e866d8dea1e1178b224d83acf78f4d1b1b06a5960c519c5bb38392ec0b2a7d4e574d9f9ac1ecfacec30801ff49050820943bf1f6938fb38fd4a01182f9b59dfd2f0aa5f67c49d79e82f8911706437e915a599049315d382c6fd884d5f5f5040d77496594a0e05d087cfc5b36bf69cab09bd6d8e2e1eb6a7625ce48bd40058ed5d6544b56b52baa3646030cc99ff12a93479f1269c5763eff93c7071e8e4fad4896695644eac16b0e034b7a5afc17726ad13e949aa889500c7e82d03cab72ec2a9101abb14d205e6cb7dfaf21cc08ee2c01121539e7fcef1524a468af0a8bc3a1fdcf695ade5e57d8ddbf6c54204a1e8e78da3f2fce2c6d7ed40dbc3ebc67185df2a836b18d8ad5b9d454603eba9bc77915da207bcdb9ada4a32a04dcac4100c90db17192087d6d788cff831d4896ce565b5612ab1b6ae110053ae2c0fcade86ace63a31b12a9b7b2c87584a544838f3109f720dc9ca28bdfb478da8faf122a9eb9ac9ba67cba6fcea8b2c38df91f12c8b9c1dc1051eced7d3d4bbaf84fb5073edda39e94c6a95cf9349239034902ae0dd6574c781126a5fcf08449a6020f74aff448e185c19e0a1564bb48e256240eacba5c597d585fd1fbd969b2c9581cbdd23dfc9632ec046b388f7533f4a68c75a40f049becffb923dba34d2dd358fee0ab157ebd2e6e69f5e2e5fc60f77cccbd44b1cf6b02a00125784a5d270d66f5180d45deff3bcdedffbc36c00b2a580f667737942f5728e3897e2d137ce75a9768a106a2e39078ad950f00ad2fd64d60e7d979e4ae0d21fc7df13d0854651519d100b4294f0dda9b3f009cf4f9bd7d5719cfbf60b8abeeb644cf339177393f56db52eb75566b0e27eba4ac8411850c121d711e0843a3435ea102c277e21b2563237b7dbe8ff9042e091ac2827798df8fa8cb3eb5299a80435e3f4f86c6521f9d3f0bc135e989902c104572a58da9e6efdda2dafefbcdfd883a6ee586b7225f7a4f041b5a2baa7d5a0660ecf5f8341995f6705c4fe808bbe500ba5eda6f4c547f94bcb73b133b2627253f13c7440d24b8b807e47ec2e26c0514b89c49804c70a6470923ffa60c12ab8db2a497c3da15dbdc8ac780f4d05de480d6e426f289e66d13ea4e47710cdaeeecd72a5d95e6a9271b147fe10d5e35574d2781dfdc173eecf71d0fa808b0dc3eb7ea67812966e92794ba29907cadc1c38b6af2437f94dafccbce661e1c7cefc57fcf8d33ee1f0ad2e6a913d3f97e6bc5efe986638f5c320953a8856c6b91d348168a7c51bcf5ff1ca3b12332b15a9d2aff7f08d47e1b9995132c5cc01ace9bd286ab3ccb545a00143a955830d51796cc0c70dd9758b3941e474f2013e950485d4d25cf54c63ebc1c835fe2b32bee0c87332d05c4e48ef862c8d011472735e87a72d1751948a60233c8a2fea0cde6384b04d1cb6e2f29cc77071a66c9cff1320bc95188ade6ea4fc198d2e35da9793f07f13271898bd16b0eb266f4ac45d215674be97a6426b3529d831d04a95935d2a22eaa9b02a8240f87c30116355fd7cd0596b30bdca502684e13e81d77b4f1a81fce9a867a849ffe3d0fee2d7b58562225e1387da09da7d7559fa994bc68843518284dc8b87160e6daf0b5d820d1009b17e3c419784a2175585ef14c6bf5d029fe7f8df6647d6472b02374fe4c78db8cb5db9f709ea91636156ab0c9c9feb94ed04836042263b6b11d60f903a5fbcc89f64bcf8c35c68144d9624e78401b24e53306a6ef2cdea84ab86be7c01c5c8cb5b424548e2a1d22cc81c0ff974b5455ace0acbc6ad6920c36a77bc6ff0fef405d540bfd23409288230ad082901a61e1e25f5d2aa1ae4b3bd45dd5cbf37308af996320c32c512e80a1e340317606d98afd0b052d456b39d8fe539756aebf52278936887ff9121c087ac073c8458f7513e9a3015a8e2649cc5534938fb2306fb5e5a2044dbc679ae6d9aed0e077551191d6d3f899b2a7fa7f4c6936e772afa5bb59b6708c5eced84f483f270e7ab3e05df3bf95af5addbcb61fda0dec0d5eab3a5b502bc30c8090239f55d7f83961af1da1a1c42dc40d055a3a8d8419d9a67b512b6dbf5dca7eefc00e7b8097a69d55e04f873aa41e095d32b7e2988933f6ae85f8b1262c84bc10a4fbdd94a4428684e00df2cc1cf5bc02c963dad099d5a4b3625ba119fd79bf153965b04e9564ce7d41e43a42fb4b030938a2f53b3abe903e8eb3bfdb259749d96e64eb26da8d68e306eaf149be90c1e2de17ddc351d5b24babf2578b7f6ae8bfffc2499b70448cff8a7c4d06c4c3ae9466102a66158873beb26437dff71926ad5e63641d46c028dc84f9e053644e3d1ded2784268e954e9f2f00971beb368ad6d88cfc151b16f2bcb39e6a750afa3d83c5c9f07d960d62150c37c5f7d02298d5c3a5a274028b834cdea08021df4524a833da45b83977f11c874cbbd842078be3f641026c53a9f5caa846c83f22175d4666a2806461c2e79869cc4797b4cb73aa195432ee59b4aa4cfcc5da9bb85b9029ba571cdd988025f3b7208237b6e38bbb2119a3817e79063f5601bfa7f6f42ad83f7499c1458845842c4efaf719ffc2c5dfd41f445f71207c48ed883e396b35f734241bc897e20b70702c7cc5417678acdfa07141ee5d2898cedf1a56fbf0571c1cb347e41701547cb143106c540347005f717a9c8f138049c5f9afb20093ffedebde50a050b478911f3ff98a63753e9c5b522eb2da5cc5c6143c287da51274305793dd1fce652a14b772bc9891393903c4ccacac6d019ff72b23dcaf2d8e3dcfc208dff48be8597a1668d52f1e34044479f2eef46f463861d166e6e0a3de907fe03a78f703dfd4ddfc02f6f713b87a8d388af548c014dd49475a81042efc6a83510bd19e68b8348786c20b20bf4977ab4857be3645a253107822dd7ea86d230939f486457189e893ea289a03d749f7ae06a722a305b801d51d7bd305d38d69124c1509a91a34b390f1cbc5f1984f06653e0a0d69ad0618a723bdc4fc11c47515f8d070969b637ed1f7e458cceb710e3e723ddba28ecb4b7f70d13e6f34737ef8bc6906069bdf49a54f272f174b1ea057a8093df194f89b1c89cf3ab5a145834822a5f370bfa323beebebec181fb7b1947108e87d02a188628c51a57a1f4535e7b7bcb5ad85255a2fae4a1a8d6861e2a437d56ff0b08122a47cadc0213c2609d03db632e3c800e5366ff147bc523a6a347f9379641d8d136e1c7df1869c8654a280633ba916321b9ca81b7909c1758f2df5c56c63f0ef870fca98c0e43e36ed1c9aa90ec595d15ea98a92a5a94e548ee2a2e5dcd42d488672e09e789ca29c153626056b640c9478c1d10daa156cddcb06d95c8bf1a03ea5625801c85f344e45d867f877e1bfa6cf4092c8fa89498e7d9a86b0ff045affd70e9e84cbe8fd9d09175f02376424165df0d03b1c4ce74024a403cba4ef3b5a11fc33bc45e5ca77382524c469facfd4748b6aac620e795386df3b3b6e6f6f6ddb436c50f364f87ba3e7cc2b5161e1683c132cd3e84a6228625ce76bf0bc1bedb7b7f645ec3cdd3765aecfb97c1ed121188e03d5464ec8de9a078687e19181881b5d34cb1756dfa2ab092efb48529aee508b5137797c3e8a5f280dad5cc860f8ce3d4c95aed178458989d453eadea5544f772c03344dd70e68968ba3024d994d73b0e3a10ef3132769341d1fa06235eafa11af598cf9b674e772e1d51953fc2ace44fc830a6b1bf009028cea78f4ca20a20106e24308e16c5b8269514431f8cc7e32617a4431095fbe2ada8ba45de92a6aabdbfe9b832773a1161431e798991dcac56d3f912ad20e518b95a31826332d3bb53faf9b033217e9ddb8fe54d9315a58bebe71cf4f6c05cc53fba1ec7d9870e8dd921be611fc5fa8a6dda915c181ef72a1f5bda719e83582b6625c7186467d32006ee62da0aec82860cdbc42daa8a7c98d2f362cf3852e7e5ed4247b73f574ad83cb05c863cdf3373acf1fdc9ef43eb822f04e5a05d8a1798e7af8397be99176f8cc48e7b4e0b403ade2ff4846a4a10e392737877e32975cf9f2984f1b52eddb47899ac822efde1e87ead4f17a82ccd6cb4a93e997d6fdd196cdd52f2fba44b156fbdd194bfd1baa477d2db1f7e035f9d2e76a038d4709e194072ea3edd8823be3aa1f4a9f4041707c11cdf2b8ac450f2bfab4fd20218450c5e5fa4369a1da5e05e6aaa22277f186ebc169488e21c37408e71167aa69d18c6460463759705aab9966608460f060295874567f65cabc73878e0f3ee392a95024190db680c092772b6c07b354fc5f694446ec1fa469222430958cd08b04aa2a250c61beafd14435612af8701a11179156db8b48b3443ea01997cf9419f0c4c182d7bcdcfde17deaf3619a1cd3572681c8f43f83d0556f26c6311b660b3cb8427fa03f57db3dcb4cb364fe446ec0c8df3b3bf896cc0b859809931bd29eb61a92e2e39b886ca7d332999d672c427028f133555b712d5f778c22d7a94de06903be491774bd6ebbda428f02d1e9f1614bdeac8cb85fe94609a5ee9eb87cdff82d485b5ecc8ad4d89b15a123c49be7000d71c58b713a31c3d31f8bf869260ac1c122627545d2d891b322ec3128f92a322c5ac4e06b9600b6063e5e52debe5f31a79246c0166655d4463402514bf563e5767ef20251f1338e7b159845207e8f23eaccc5d4e6800df6c9d3fa01a1933956b3e9748e0de221e87a98fb95cc31a266cf4c7b8ca245febacca6f77d2fc558468a9ab18fa6490f4c92ba2c5d638128bb857a6443a60eddac99c7bd4510063a36584f815f4092f7a80f2308c22c9a4b446e9ff5739128e6398fb44eec9d5adc077f59778f901a61f7a2d165352a3e2071f5986e60e113fd5f155a0fafcd70d7af174aa4666f9bab9015b503efb4ace9241be7ac983f8d914ac46490f1341e6c5b8d598b263430ac80b479dae5cf6132b8e29f684f50c8d50464144c12c4c3f428e36be978f2729bb6b8979c3abd04d0d34b8dd687c83cf062596d1485e977142b0add90173d8a773be09b0ff43a452eca1bbcd847edd4878fd688806c7f371a0434a82ea570e5a36de7f2f6cdfff0813db8d760bd632517e7df575f82cad5b76cf95d3a881e3757bef73a56dcfaf9bdf224de13ef313ddfcb600349ddd45bd9d9b3891b76630830a9b98fc24493904d0bf2f9b73c7bc36e1f0aef7d47439ff3420bb5b5efe9187e4177e3416984387acfddb0e94689600a6cbefa32dba91af2f521f2a362cb60c4ae5651734623c8abe5d97e79edea2aa5ece53fae6feb7888054cc74128bd7787a69a1a444182b2a29234a5b479ca852b7f4b3be0db7865235e70273abbc1fea9971c4ca8fbcd95bc21c06aafc25065516256dfc78ff9964109ac03aa892a02eb13e0fa99fd8962d9da767a50a837f5c49092d623479dc44b3312529ed82e973b2de0e1bf24bfd0048aff17a29da4b4efe017f5cdc4cd1a7e9fa9581307fcf12b89b052842de49bc7ef1fd20b54890ed188f9468bd19359920469b35f6a467b0a8828ff62a2debc12d53af666c1d820e02c1a83c864f4d325284b3d85507b6859193de9094e2cad587c89b8c3d9347dd8f8039cb5684caec0ee685b48df3bd712f2f931b0c7ddb48de5305196c593bb84c854384a17a0cb5aa67c8fbb2f9ac12936c2acf4656b781fa7c11ae5949905e4d57a26b354ee097782b91f084aa543a23a9893ecd88b9fa03a34d6e6bfc58d591b10fd27b3f80d7642238b61990d114f6eb1fed543742ade30ac435ca635f5900eedc72c4cba04c544f099108576d7f0594256e82983291e5d5cf0d89a3668de57f900dd30269fda11cc628c3c0d4a17e91c0090bb99957845f8211c396417b65546daf7f238527f9f9144d160021750e46d7adb9a4e11a058da43c4f24977cdd4e5d32a11712a4fee1a1d5b4a5470ea4d7f610a49445298871b0f458fd45eb65f3b4b36e65f335b47a8bacd5d689b07c4a0131faf7e75a058fc564de74b180faecd2d0d03182209cb97690e21284d8926dbb86609542ca0601aabdd73d476e8c44380bf51e8c0fa49ef00bfb5a424e79039d6507886666345b087c662fc8d4c1e60faed139568adc2e5d3e84dc4915d4e9214e867747913fd53a14c4d68798a7a7669dba7875173f67843459c98d18a078804a9a64d8fe6689c7a0c03cf84cd269f742a15166181a2311098b32a1962fd06aeba4cd19981a1252050e706b12d91ac0343d931dff673929dfb0fcc155df4564dd64340a80d014f880b98f8fb2f0942eb42bd8d8da8037b88b985ea38e3bd96ca9f5b0806ec5738c05c3b552859d800020cbbe6b902aa606ec088839b117f1045d3bff60b41a2fc8c4263530311581301666617bbca44c2a3077d4a9e6d4ebae66db5c680baf76d12a9fbb8a0b4c0c27294f11ae6ee56c58576f55ce039e10a9021f25c9f9b072165b6b3286295603c2774967ea6d6dc0b797bd67e21440354f92fc975364b5a3c27d44a04f6dad03ea3f0f68b267592aaeb2facf1fe4f5575f82728cf04016f17d4ab732924d0498c94df730434e6b1260ab33ed8870910ee3f61e3e8812f48f47ee4eba1772fdbc1bf94970d216044da8e0f9eaefeb66928cc3356392fd2ec8a187a22a8cd2f0d85ca5216f9ee245b710e7000f126483cdf50ee294b86af8d144ef775f5825a624a25acffc37769d33b4ffba8ea80c429613a642bbbe3162e2930271065939cba9a01e3761548342440bd6858bdb0e06a814b925bbd78fc9a586a195f8ee0224f1664935800610aa1fc74379c0bcdf04a2ac68b2932c5c4e9d0d44d5d3654335ec6f43fca1a0d0f550fcbda9cca4fc8c84ad1c0ff8e175820598e1aea7684ec7d158fb023dca49b98db8548fb358d55dcde4642838555aa4b274012740dbd9caddee1c451e840e74f179e11cf4e411f3037c2679fa277b3743c1e348da1a8af4aa9b4f5caf4171c300577f7618a67568382e2e55d49afc09cdad0bafb9e727d7f12e68827a6f6e0bb65477835b07798f7bb12a78220156080829b7345b42aba22c5f3932e3c97cd50fbc471219de4a6197311779e41a7faafa7b02035ba6d2c18a20268bbbc77505df4f9937f97209909f52eb91cb0e39d0f9bcc35899d9c50ff54f59456bfc051ad06c63f112507b9d121bd98b4111d2854ac56786eec2875d58241f3e739ce2b8ec4ca645ad934aae7b0c528198636192f1730f5a2f1c708c21114c37b4cd4bca0a90159e46e435b986564ea6d0ca53a0e7cf209fd280f10694a4129bd54c51e35ef298e923e82e800a3af2eb36ace39cfff6d675614d9a45c893b90389294e735777424c070a4f2d6d2fc7f454ebe3a221976284ed3e956c9ef9b5133202c7bb9d0a04a53da9b753addaeada7c9b2b8b227a8767fce96f0e985a19c9f3a44e6031c46df16cc1ee0e96f8787a171b8d01c55f5b23e97526dc29dd2b52fc6f158c9d734c7a94f9f3d8cf5712297a0c5cf006b287e3bd90db881550053dd997659ea759d6af0f1e393e93e2108117820cc71f7d7b4663370a0e291ef09359ee9bfb1779ee295294462cc2db2e5fc71519f70752f39c72923388c36eff4d6556a08a292940860b0c2d63ed52f9ff4abe73d005c2f76e60d8a1575b162988ac4157cbae8404f6767b647548bf0bbc34bb91d24aaf62241c5ef755da76f914f42db09842400c48dd16c9dde0963dd0071af59ccf21e7b13438bdf519260d992fd192fc18aa063b76ab76bf3a0995e233fa3f0a6004a02e8799ab69b2649f4c63861314f7e4f69b92dd14cde91b6e2229e4234216c30fa133d0cddb8900b09bc1aed0598869a4df05568ff658edc912865cc9479f2cdee5ab16968f5a7794be7b7a6346ae7bfd45a32cf8251db24cb50061238197a347c23dfc132986d0e1fa517e6aebb55675478f689ff107b95273bee022aca076bf2f61bab501f91bbf123ba973dc9a619de2338906980b95d8cda9c26eea257c66bbc57fe15725ea872d08a9fbc51e53d1c4507f3f433d62af16ccd9d0ef90bb4dd4a0ae480ecb6117c160555b2f5ee0b93c9cfa57bce787893ebd3d0edd95c590a6ef2820454fd832557928f5f9e6b19ed320be51369cca84b3add1179eeeb30c45d12f0b31f603f0a1bd2f8d8f1c43a078cc51065143d6a0c8e240b195f7b8557f0c0227db403cf120f7615cf1e66571268c7fa02493aec6e7f1169d08e712ab66cba530d1375b487240d33d73471bb63d60b49cae6aeb60398be20799b1d306131f3494f83f4f79de788554026102d704378737e10bac7d7b34677773a40ea4cd6f6952939c5a678b57f64a4ec601e68390fff3aabf18aa8fcb714a0fdebe39c52e992a75b760b7be3d5e7e4d416a0fb1ae990edb79e1ae861e9d43671c89bec1b3a2e39c3ccd11660042c9353ebf9bf96a3d13a1677feb7d987bf49bd30c187ffc7a9a62e551300d1f14d16d7f623411cf07d5de5b982631d30f3d1e73a93debb7a5ab538e216bf586211537ce28a5d2c62170b05c6b8f14325ac52b18fdef06988c3a29bf30a6ad1cf532b4e8f80ed657bb067ba44440ca83479b963d475819a6889747d8eb4d79e08226cc365dcb769854695f8d081ab2f6b4b442c94d019193db09d8f2b8c5d617b9d196d1774b17fbbd2d265c54ef6f0cf17cc2c344132cec093200dfd62289c508053f1e41a90f16567ee34ccb67fda726668323a3ac3e22343e23adcf52dece1b97b4b94a4b311573e41da76b18dc83c753ec188d0b744aaf3545d7a5304cc2f463e5911bbd1240c4ff96cb8b2bbf0e2b25f918e705775e6f11a108087852845cfd11541f78eb1f998afbe2a39df2d7bce7fa4c2761bca72a696f559428a07e6c7eccc878d4c09fcd2dc3771fe43610aa4f076aa9890b8c8a9678f6fdd0f3bf01a9f5f6888dda0196101643078f0b345e0194722ad43238602ab13a18408347c274954dc1181d1767181d398ad79f2238eca2d0c28c5a6d00cee45ead3971c8ed4617be608cfe9532d708fa16faa9765af1cf9bfced6b851989e580491b48b44038eddf03cefff148361cff3eb70f0d5cce778e70415902b2ddc6373c53aa9431ba572664b1cdaf4bc6afe45284d1f090560e9045b88a35c70b23b5df19884fff2fedf4cb8bfd98d167f79d6a4630064915aee877864335c9f1446681cb79853f64fa36049bb1a142c11e02111372427144eb31c466163b915c6dea16fdbff96c491ff817df75f8cd3da9f1c7c08d033dc79f05263d68ad8c21e8a7065babf79dc14413c7e7cc93bb8c9dcf676a8d6b249dafd3ed6a25a0a479bc5cd17eb267b9a8b70976c2de1d48510362e87a9113a6e69041f20148be88b2ca75714d161bec0fae0267faecac52811e135b4c92265cec508d51f1e78a8b5704d5c8ef7b251f4d5161eef1741cbb0a2cf59450c6d2c419bc74034c399cb3bf526cb29c42422986f1d00bafdcaba71797b220b93d0c6def08377f9cb62f15efc224bb3bde0fd60144b87e26167b1e769e9c7175846bddf9f8fc67680512cf2a187b9165d2d72d7c5a0252186d39819ec87b88e02b752e7c6a0cdae3bc07a33a545d882e873a89222225a9d3c425cc8e9f1b86ef6bd90f2619d1e38e7abebdd0b18b49497a26857705b90de086ac062c42aa7a134ebc41d5793009aef07d6590746668f1152742a9101bac83102d624c71ec843fe0fada2b076ebcd78b78dea4cf289e5c89a4748b4008d406d5b259b44e66b14652d8daf401b2f47015308bd347652b5fbdff111b45300683a1f60d5d1d61d82f11fdf46986129c6b80c2477022dad0b70773915d0ffc1fbc628936a38cb54377ec4bc2d204cb5da55d2ca20783d1594993ed8567c106f27866366aa3d53acb9fbdbeeb1cbfe10643b88051d9a784b4ee4c1d72d33c481a37be0aaf993d785e3d8e282eb867649cc8f0b17f36e6727abc6c423e08c18a4517c3378647380402bed37ae7d0a563957c084456ab4bb253fc0ab6f9523cdaf9d994ad750fbe4e45bb64464ca3ffc38aef1958c68f93f22c4a1c63040ab9cc21172a153a1e70eed992f28309e8c958b58f6149149906671f82b99a1b511ce72c9bf7d7b1a2f88c20d33396e267fe2ae52fd21465202ad6de12584bb4b1325abcbda2a20a65cd883d56185e2bafb16b17ee1c14d12b17c9e8b44975c3603d1a23942c82c8b67ba6a8e2bb928a89e2c9c943dbbd0a79d994918c7e816f9d7f5873629d2cf69da0c6d42778db20fb4d67ebf4a923c5c8ade4ead5cd750d6dbb77b91e49354c180b51764dcea5ecd1f36c508ce93403ec033dbb22959737b415012b99be96b6410b399db545a305bdad27dca42fdd7447d0fb6d356b8910857bf4fb75a0356f33aff5749711f27bc75536f36ace41d86852fb20200ee307ce885ad6e2efe0516d83a16f12fdcf9a285406c2681175b2e3ba7692b9e8500774899c81b33d05856484e8bf74d016268d32fd01190be8e89fa6db5b8dc2630083bc3d13469bd61328bb59b5bb9ba907ae30b08f5289d35b2c13ded812fed1663df6b3106a231a806213d492f1df6734aca7174ea3305c6aca675f7a284b4cfe90dabdae89a81dcf7444f2c4066d68538fcad7aa90f53b341ddf087676b17b34e2a7eedfb572b9b8bc75ed7814be3e0d8f910d8ec03940aaecb332b0cb0cee3fbb64073196d8a4461a39b807eea106c8a369c1325474599a4c0d2a396711ead6977a56fc67d8b1c7f9723ee5f2cd1dfe520ae3de6600713631c8018bab585b12120700ad1a4d1b181b8bb7ee161ad021e67c6ac784a90efef79177e4ae4f015a06d898a084304799f0c977c120638fd0d3d5b5ef63f1240ab0d65ffb6de53e156b781d5fdca3792915631949dd866f4a6318264eb42a15c4c9d7622aa09c3034738895d7522b93a9ce4a329dc892c2a1377ddc089ad2c39f24783a86dc4ddd908b10a34d5fd1fce7123f6e1911c56559871105e9759be03bff618abb0ec960ba0aee83f60a98f1bc13a20fd5e712c78625071b0a5163e18f8f14342e8e172ebe80fdfce902ad95add56e210ed6b4bb24d6ac98910fa96c50eadb7d71b4a2e55ba62cb353238d8a96b24813a0043bd1b865ba34781af5347ce27024f0cb072a330e024aa2a950e7fa98e063443eb57402deb674512d2147c1eb671be8f6b0d5a14494e3449dac70d4ab2ce8ef6098045f26d4a48d4854e6ae58160db58edfd12ce1ae6c4cd8c29e2c4c4debcc29391a741c5fa834e5908b1881bbcea222242a95f38c45749a432c27ab955347010e57fa4fb1a9aa813ff9fdf8c121cb1309ed8f7e88c5b468799a36e375b65fcc4275d9e7333dc06fbe659db9a813565af7f4b0e8030301d6af4f17515571ae7a7d98459939cf22a8096a4c4923eb1c32272d746b60600d40c5b23b5df243cb914567f77fe4ea5c77f2b4c22213206815934c24c67f0c84dc12c035d0f001bd53bcfaa8723796c83dcf48ebf73d5b89c2b3b13f48cdc2ee7aa22491d45a424ab486c96243fc0968d52d3f4c8a7706e8d6f85fc39b61352dfd9945b25215f4f6a774556094132b92eca7fc94fdea903ec034ac7aacccb15cb7032d007b298aa5e748c7112aa76e014788a694b2a372ea5c37df151beea88f5ba1776024b2865cbff0924c77ff586be37a8f990b4aec8095cf8bfb898b2d6fd3673a98f613b4cf9a09b8365b11522a25ec24c4ccf68af3d0b9c5f28d6d7320111a1b0b39f08dd72bd5e7c46d44b731e0d5eddd2a30fc819c865d6119a839d0d934b94a4e304a8f59893167e379979c6f89890638cd38e5b31095b33e6cd0e31f6c2d5299dc626384ae6ca444646561d35512dcf3077883231bc5078a679ff72ef4f82aa8f53789c7f9db6854345178e4c1380ecd5a6c217928a2e4cf5dac2ebeb133f1de23bc764e1759d8429d5f3db150e6afa2554819b4e5683ffab8ce96779bba2c6c592497556db4548dc26fd53188477fb5028de74903701c8611ff2a34184e611ee81f85afe67c8bf3b683c7283a162a6b483f945998c10e410f366f5ab2f69b24aefdfdc7cf8823214a536a9f5a74d01960c7cab55139da655df06b21ade9aad82ae3a762dffa0f05b813cd9a0995141f861e57a94d771c1a29952465314183044cad26b4228937642a474036c737c980a94336e7624214f92e1f40399c751ef9c2b0913697eb772c4695208e21ecfd20915a33db1bcc408acac4d53865e15d90c223d38bc6b4c15b386c769db452839b86769a7dd3d5cdc81d734fd8a0d5be110e4ed790ce44b5a03d231777234747e7a185e2e7cbc1803e8ea527b32c6c82dd1188ba747fcf01dababa9254f818f2f2f8fd59173f30f74ca17bb9b90bd993ad1d66f63b8f8865f47b666772f5e419453235540f049e5172c674d6c7f5284693461bec2ef4850f8163615bb86fc6d34f223ac7f841e78c3934d669c1431d40f08c72937117fc7bd555fad545be49e175ba3992d6c11de60b82f34dfc3ffc2016ceb5d507bbde74deecf704aea050d3e4e7c2d3ed162b3d69392ba3bb37263cfb1102c5e8203e7ad3f536f6cab221b4428e53fa78d83f11ca60f829ef83f3b5f51efc3662d32da1929806b912ace29cf4cdc9ede0a62a3e3f1da54ea1794032e35b3d6d0fa6d890669f48f8c0e45a5c8a1a46793532004970d46e412d22814b55e5168099594fe2c14ba2ea4b14b4d46461d47e910f8a3b6581f3529881268067cf9b2c695af65038f94114b501d5c0575371cd2743d6b526e0965cc34158218d17fdbb813de9a324d89a85d7d9276c6888802d96a05b1d9391de61d11b5c9aa632a165b8bb1da3f18d14fd39c4808d49a843adc8c1175b4fe2d4b67c9ca2240ab5951d12c5503f111ed1ce43ef4c133e4a2cdb38b1b21d1aa7ed82e9f5e909ae7072a3b82dbca6ba57288165ba28e943200a83682439d53bbef4dbf1c53f47b2c1d3cf4bfce80decd7cd5467e93f387c7a6255cc918e63fa7bf8792366c629f4567d673fed428ae660bc4b7040ebf12aa0e5974816175996ea131e1a330b013e2df41329fc6f142b7cd1c4084ec60ca50fca4ace77473b6d1ce87cf3bfe9acdd6e87c5f1697cdaafae98b5622f26d5b27cd2773fec9d4d7e72762be6592f0e1c33df829d2b6e1758a238734865b580b168892294a986a6ad3a13feebbba849c2cc77ff8fb84e1be1657a7fb53d773c18d588c1bc0d891af67a8b75350e512614e2d59960e6ec9ee23376ffec86fc4a793a861201bcdec214f21cde85806d2b0a539a75dc0d002e66434d891a05e589932b3eaed0e0c827317526e7792169dfef690f9ff2c05f9afd7f288495f3b1d995cbd20e9f81bea4c80b291ed4a2b452830ab76ffd1f94f47a76b6f281e7872cf8b521896f522dbe563575ac5fd2c314673c46717a9fda26cd82ab84a812b8d0891571f5e35fd02490b4eef1e19caae06553df44713b874f129c237cda56e972fbe434ac6ef057611cef7d0eeb7b8b98382f2d6deb2e27a075ffc9f46ad0d34ed513ed55eb1d173e838eebb92244a387369f4958185a5c674cea38e75c075737d690752239b884eaa977a54191c091c5b030feeac0c7db80594a49c0d643bad80450e2e195841daf71ca4b6b961537a3b9f8971cb7f1ceb507b2c98b742057daee20b14b1442ec8f5c5682ff41fbc5f8b26b6ab09b93e0050df865e9d3a44a221cbaf532adf9f5213b94c3d3b937547fab1298a60ef188f9be3ceb9f90e73afeb215fbe798935deb50fc6e2c222e30497110aece02b470e806861410e635997fc72bf4b145a78b192a290609bc59d12fe26faa5e36f62cd59fb83ed6ac316b0acd24c67a0535d68d091c6e4d3eef67aaf71ebaf077785300ce70ffd39d317eafd2e6d0e229dd3d84265f0cec75c7461b5f539e01d295fc6deffc9cb1d07e0cafc4ff700cd1c128f0c1d280d91b5f7b74b0e06dbc9624dfc4336a21b774883479ee86716c7220990d2082d43875d7540cdcc74e9a2fc8332896de234178d9c6a057def1a91c2764bd5ff958e5e70db3f5a7b242056a5a5426fb9dd74b4b9447179f164db3f4187af59a97d70cdca8ee6022b699ad59ef2112c83918c530406b73ac051ec1eab34ab328f575ce8ece03c5c0f34137309a1fb230618e1e780d933f75d28be28c28f5dd09c38f730c244569e0c31cefb9067c4a7611c45d5b182d347009c26d0bd2520522396ca43d7f4f5afa11e4bde154694f54bb9a0e792cc35746b6fae02fc7529c06147200c23c8c0c7916c12964439f80b99c57d0234db7b6478a1de99e9b9b007f2f785ec7407a68460f53a4010f639fa268291038e623317aa9e330dd224b7896589624359afc4a5c39e7493ecb6e73bfffb430ea8851831a63b8f3649abac5f3efdbd85ffdd6f990c128e4f7efe30b265c242c01db60f1a55d59c2d93e3dae737ac473a8a31baebeefea3dde41b7da2a9b5f00de7b274ddea795302860ed0f11e5684939a230efbf1ffe6f09ee0131031f685ed3f1c04bbcf53d90449af5869b693818282c9a77ba501539513345c57d331875ec3a92bdcac468130443f06e75e1ad96958e18b460127067e10f42466b3ac89898129ec2b03141bd41acb9187bc05fc30fc7b9b306b46dc561c65b19788471a4640906d761f2e29e25acc7bdada06f299afba7d5abdaded321f0b148f63c57548b4de70c8b35abd1856632a137bc78a49cc7cd38b46bd4cc0e8b1339db4e12ec3707b40b2a663a03ba43fdebc2f91f302e005f75c836a298bba7344887edef3836d47054f6f57e0e518cb49a24bb8986402bbd7671433d8fb8f44926a2d3f4f364de95d6f948911b54d0152e765632dc2a21d47cfb1fd3e006ceda4724b19c97805605312668e11d79c31be1f2ce7daab32696de44929450761a9f3a048163c01dcbef693450affb36567840416e1f21428e3c6c919bcc6869da1483b08d99513f2b0579a10995664fbfe5b8b85572cc1806c904774949676febd111da38f458a65129a938af91c240dfa7ba598650d9c5168002183f179b9e27cfd3d4d0ecf6ad1d432e6082c2adb0e6b67cb2fdbc7aac1c01c6cecac876c961c800ed3023b1d7d40aa3f34b62a196b693cb406625e3673a1d1ba9d72a6cdbd88d1178e37366754540ef3acb49bcba2233034b491b41ab8732bab7ae2e6eac66508c6586656513331b5f25bc61adce36ebba88dcbb57c41e6df25cd9036eaea082434b8af767a5aaac9a906c714cad1e0f37416c0594e51b2c89eb33eea992e1f9b1f37cd97ee584bbd448022167348488e9e15fc0d9a2f7d413af9db4348e24437f5d670c9d288d807e69139ff82264685055424ef5c18e958bf74434a478305e2e9f8a1a92d620957673e0d52a1c46a1be9591113f00abd5b1dccc9300b1f6fd3c02a93b9d1911d32ac98dfeb2b13b907dad092d1ae26b3bf2d94422ad9b60229b1aa288fc46695382d1ee82bba713b45fc9c8c09cd6d65ed611d1769ba3159f46cc9b48f07bc6501c8d501532ca588a9d43d34d1474913191e7e2b8d864d57401def63796fb80b8379f04e22b4ef7f38c414b8731cb68392fd2b1d121b8bf4f9d4cd1a7358e9a00db36580b6a5f4618d3c6f8d2456dfb4f24a2a83656d17f0d80ec59e49a9abd20d2d0e0bd1d6e58b270433e41a50a1bb9f5593a4fa1e93cc48ecd0f3d36ba0a7e64a5164a9339720058cfb2523bc81837c9dca82f2874971ef8878faabd9b2e2ab3631a22e2df9f3054fb4a93b06f8c124f0cd5ddb6f3a766bdd6270bc1a0d9be55addbbf460a02163a77cc08c3bc63a0f7c696bf5e9c8b02e3710245df4c6c3d57d81f6c781f11af083d75f03da515ac5db894245155668a2592a68359163cb35888e495a3e666a6b6ff5b6db9e570d8601f3a9c2992a71e51327e3c21cea330fc0a9be7265fea018679fc5d6c666bd032245e5d5a5d43ca4a8d69078a152b9d1b54e309972230eda6546fdfee599693d20c5a120c8ede7719892d2054c3c1d901d03392a90fa56fe84b094baf080e55699e17add37b3e987527fe7c5dcaa9b376b89cd5bf6acbd48aaadbc550428045df4c65bd0ff41a73cb06ad479cb87b94723ac26775eb2c82f22d00cd7d32bb61ecc7ecb10b74582206fc22e91334b7880e4bf98fe1659c499364b701eb406bb70e45729bb55f8bf3bc1a8a07e24b44aff3231da859e91f6d7c0e548384c72591638e72733a518681a228b483a23e660909a1e815e8232a58839a3e619bbd84e87cfc76e02076e313267c19b7bec671a7da22eb61e156ec726a84a84ddcfd7309ac0bd97e8ccaf28eb452f43388bfa96ccdfd314fb32701498d9356d01052d59aa252ea0a6b755b3252e2f37ada3e0c973dc7a4903772234d5c5083ebf322d0d54f8fb2d79e69d1013d9f6da1cac6234509d2ac40e97d8c1f87c5eb518527a114c54c9df7b04f2dbbcae6131aa61690045139ea4a5cdd4bf031eb9ed3b856d2771309c92e2801e7c609154ef114409840f6cd11041d8e8a941ccf6174bac35c19d320be0acd6b8771131edb85e3fa0c6ad16db2ccb3ccf1d383a7933ae20a1a088694c0b492727d5b2c4ace434b9ed5b359865148ca0b03de34b1a4171fa15e409fe56b0c36947f11b426ef2ad628c19dfa5ddb7be59b385833a58d19bf9ef471fc639225cff9e76b68878c9e85bea5ae38bed296e6c52bc98ef8e009341f53d1ac1e40537a47ddfd74c4f6d60f172de9e62500edaa7d265335e062d3ebcc83f06caf3ad8fbf26771383ec0202c823aa46fee8c60798e3abeb8913db1c1d0a0364a685b068909ac6751a430a163903ead212ec284090cca7a03f47bcd454b04b6370db2eb5507c5f23279a86629ae4256d19fa10629d0de7757d57fc546b658ba24e9565cf82a5ebdaacebcf3be7b3577c5c8cc5da50d83ef91a00ba1f7bc8f019c73b009164e503f898787d7491fd419887d8060c27026838e6250944ebe633db549de9f7cfc52f2dc3b83f3fc5d0f6575b5e4f0477e9cd4bd5d3579f40a59fa75425a444ec3eb5b4de4dd7cb899e8d644c59fa9ba54cdf3781f522272abda4c723c08b5b0e9884862da5d77d50fa158a050a957c84162a8c7891f5acc63e3035da8f09731fc0c0341865c4ca6da7451720635b8bc9f88a01d4d198c106e63834826f0e7ef2da53476a0638b3619ef044d8590903fb457093bb00a8fa685290b75bd930c56cc69af4b38359afb83f1300d2dc66263af962e909e542a126e90fe3af502067e0e3fa9634303a7573ad4feb1fe055343b98ba4f81294f62d49304b450c98d738d4e38f2355d0b69044c1f4a4a5032a07f10543f05e841996b8a459d11a379196a8f613ca44d4028ce0753b3ca87396d52ee47dd05901cf51962439f4174c6acdf47b22ddf14e33a695fec2dbd1cbcd05d93132441dfb50f098affa3cc79ff8abeb74d80b602cc69047a4a9fa357305c19a703994de5b582c281f8d697fa6310dfdab5aed65771d3642a7f3ed79fa4dc6a1e0bd92a2524b6100a072beb862ca16524cc0c0274a9453e0a61230002a080c8cb0dfedbffe2a7899090b6f7de7bef2d534a4906310abe09f2093de2dbc06a8fcda2461a4b5dfc2b262ba08beb392823b28aabbfe41a633af9a0638d55ed64f2ef1480ce1a167b2139fdd83ef74f2cff3ef631477b2139db6d5d37a4e9be921cdf87b6cfb1bf6d190d927d7f28149a3af83db07b0ed4fcf973f833cde17d4783f07bbbd8bf30ba44cb484dfbfd1cda2f5dc93448d320b1657669101c6ef6d2907262ceca7d9cf6981373b40fa224873f7f102539fafd8328c9a17dbffca54194e4d8df68906d35d3204a72f02f0db23d2d239f6990e5a1ccc31f735ceec71ce64c095a543223bb46031b3ffc47c472594a96cbdd2ddb6bf08a6963358660248668bf81cfc1c14074f31781d8fd5e1a23d64ef2bb0b91451659500f88fb0f34cd4d5818e6c618b9193f4e97587fb6fda6f16afb8c9757dba6695473fe95eb03f92d2b8e35c5babb9b03ae22d4f0b58662b95d6299b6719df7854473442a9954564e2c2da894cb0bcc83f4861800e090834a46871dbc1a6088305ac4171cae1802f03dbc4dcdc7c719a24611bc42dd0000d69104af782cf1c3021c643484c816e1558b8f2a1f7f8dec0e4beae523ccc7fff8dbc32bf0e3ef125ed18f2e4d789503af6474f8f83b7c74f171e6e32f145ea93efefef0eac5c7df28bca2f15e2be24dcc471e70f8fdc187d503f1343cbc0c0d0f3438fcfe0b1e5438e4f0cde01a43a3cc7d9b1a1c4e38a8c013009498014d313c5c80f3862576003dfa430750032d2083c3bf0a0755ccefe780030040878979b98146d09bc1b544b72aa09270014fa993121434a14c3c4070b6cc251ef458bc1f30a076b2c00bea575c502ea5df4fa15ac0a77efa64344d5121c6102ca08dc9a6c609fca1f443112b200d8906870ae822aafc472e72b4005551e5bf024f51c61f08707e365c85c40fa00fa0e8b3314f4ad4b63135d3572a9102d083e732709ca6a08db8d16886c04dfb4e3f33266c00067ef63a3e3085a592a8fdf2a444079adcc48303674f8fbdd5da75f020f20597b0480308585586fefb287aefa37664f673cce93c7083700522ac1c1d9cdca4265daa787815db2155d28c886b62efde638fc33ab4293d9a515ec1078ab07230a9f5cced795667bb5110af306c93dd0634b73f05993798feb16e4569e1ecec35f03130e6b18c8e5eac92db6d96ed264757d86086a35795aff96086abb3728df00a95fd6f0f26996c5898feebd52ecedd61955c1d172a9e262841ea60d28a38e39994afe160f21783553598bbb346944c7ee984b93b231d29abc45ece1346627d4f702b280b6e6215871ee46153b1971b0fb4b9391e084699fe4ea6f6e3b4beb39f557bd7b88d866b23019beaac5a8d2592d88ead64ef76968bd1d3c679da74c001dff1285cc7a3701ddff19d9697ef53e7471a5177f6bd7f610a25331a6ee8bbc155f97d344eef7de4954eac58acfbbeb3cbea9aff151592a4db4ab9d28fec98cf44952c52ee6fd96fabb1d01d5cf638ec512c0ced57b6bc0cb9ce6f097da7ae945df8c3c31f2ee72f8d969d90204c540b54d9657ea4869289ba60d855be7b95afb91435ecb28feee0fe3b4266df69bf4b43dfa9ddffe0caad6b292763745feeb76eebb6225524491a31f7b99760b814355ffb4e87e863873ef6e8a5fc3d42ba9185b19f6dbf812b54cc1f5cf7b3ef3ef4f9d168f9a5a216ad85e963975a3499695a4be8a3217f47a6fdf71de1b988866836a252f37c6ac74d8ee3386e6e747f5f846ea476cf60ea1b443d0bf83d4e9f4e60741d5675be42559e44c353c5e99a17aaa365fc273d9d98c69631511a5ba644a728344574879c512c8ccebd7a1ffe70ecd7a33be47fbfbbb28b71b5adb45566c4687e88a2424ffaee75f89f4edf8522125da162fe90758e50dbcddf31daf9a3d168443b9f34a48090ba2ffadebfcf3e199f94358449297f41afa1efbdf52816c6d6fda9b3cbbe53bbfdbaef386d59da27a46620bbeaf6751e8d20f629d61070755054a3ac8e2f4b6a30d3504aa94999c54c6659e4615379728c9665353810f51b883280f7dca330aa00ef399402b4ed3b0df440143627d669ddbd07c61cada3b165c21a95878d5c3abb468b32feaf62ed39302a8073341a51fba02f75568941b2bb809b7b9999b99fb9b7e94ac18674240924b0e99dc262e1d3bdd19b1707dd7e8ca902082ca89155af6217107c767d735019082e9a3d4207eee61a136cd0e3ac3a84253c35dedca00975d7b0ae23ae53bc5902129c55914bb0dddddb7379923c8986bb53a760d5119ed569190ef9ac310789de96bba08b35ba14353e163db24a01af31ceac62ecee6eeede41e76584bbbbbb9bdfdf6384d41856c595e91487552b33ca0fce7677191d17431407b799b12e4d8cf24cac46205ca9b169152db0428d9f8d95ca74b928baf83126d69667d3363b6d785df6d8e088a1a7ee0f510412492c115d2ef9f7c3f9430c0e6ae8a2c6da6ca7e2957ca7aafa48b0c762b0caa37c9ea6651bde449514c7268e564a1c5dc3438f0d4e114824b1c4c62875257fa4220c585497da26661aa30973346dde419664ad8a95a2c6d6075450b7361867d8fba1c6ef60b049e1355211861e13886a74058148508d2e204c0967a4020c595407cce802c24f8ddf3552118621d5fba80843086a04390a1da6bdf421e3fbed7fe095260a7f68dcd779af72c1abf931aa441fa3c802138c5125fa902844c320d5fff39c9d7ae40e71dcb6516e75d0e7c9d4efacdaa1a62ba7a2f521741fbf197146f46f469cc158fc7542d731c6185beaa87cbb5b3439861a8ba0dd8a7de2c51863642ec65e8f9bb3dcb82251bcd1b05028de6498e679f1466e5de51bef8e3908f366b32c763716c6186326358f71f3345e79ddddeddfdd1c7731c6e8c5d80175daf228cec4df65e6776621bd6c858e833071c841a59291c922076f4322b33aecd00619950e3258c8785daf41871c545cecd062c685898799d20b1e5ca41734ddcefc19d14c3288a44ee803e2874f469450a40fab3cbf62eebb7c55077be82257b305f172e9546dd362761361685146254130f7bf593e31bb9dd81139c3dc8fad2d06733fae8c6affa88612d842efbaf0380873ae36c383f642c35e489a0ec0697f0022092e3c6c463a0dfbe033e361180d0f1e12ba5f784bd3c3115e784cd3036f009a7708c00068bce93d34ef94317a909effc9a130b7b718f06e841180cc076ca5afba866dbaa1b8f8077fc23efebdc16005c40f40ac6a6c82b881114300031002a7e610800043c89841a3868d192064cd696a36b383e09b8581c90c8795d7d4c43dd9d4e8625dc088a9f1f8c65536d29b0520c40d4104c137370b03860e31be76c3f0b6233386121c03280ee0f3519b2a1167e2c3c088e109200e618765fedd17318e83235f04200530001722a685c0e10d636ab4898c93e322002f15c38bf1460ec0858820833b040a47084db4a19c8f00ec0db1dc2db7db6a3c3122e4fa76018ec0d182b355ed9411001b42cae01a6b83884dcb308ea57a399ee311c06bdeed0876cae9ee0c33c9707a9b6affee6829a5a4617f779708a00d91c9c0663011454456916478de1c4732b0192749c36d1051843b0d3d5339cd8d22e60c1f68d4f0a1860d1f6cd8f08108227cb8e143113e1821a231bbbd6eac860db91d2122c6993e79bb134144dfb811978bd0768dd0dac54c01b2508d1a1bf263778ca17c1f3666a500fff6d9f6cce056b31fbb7d6f74c7f6fc19c8445d30829bf6f510550a7c8c33a1ff114d7d930e64818862dd4fa3281bd9f7ffd88a51dfc1ff63aba43db6f6d8cadcbabf7c6835cb6ad4b40f691f1289befb2ef39ea1742f8a5ed8fdee08bdf60e86214cd3b4973642af5931ff06bf68cef7306f9d5466ac72abeef07e7e3776ce97a0f7f35bf45c5d4daea8f245eedbf6d9f6991549ac60007f488a1a9240e83f9641912adf495906864e1a7d361c73cdf36afa666cab4a1a7ad5bb6747f6db776337d16723fb2d935c715a2219bdd6ad48af4b8245a76527ef77b5e3fbd087a6bac3b5e8df8d95c114958de00ffd67e3fbd0fb77835f7e772cde3ee155b7f3460cfb76eae387a67241b3402b8557a26fef7b7e8731eaf7a1efefe43703d4fe0916bda47b9a4937e9563bf199d53910e3687cdd3e738e861b456d8f751256b5b615480f51d5baa7890f14202b3a68888e144996b49efcb8145e8dc0ed3b09afb6ef86494e30d22afaa94d0e6cd917324efb028ac93514815cf73b82892a7a9111fc221a02e1bc6a502abf56850ba6547e763591523f4ffb8ce83a2ffb8ce8233474da49b66ea1ef06ff06aec8f34864869da4dbbcfd3cda637a4600d9fadde8bb21d27eb9ef087eadc7d60ddcc1d58e3dad333da03a0d98bad11e30553eb6fcb5e8515d5439da43b4b5a3316b8f7d55b94060b83bba8d260d8c986c81095139da43882a777b0e0c9968a272198d7186fb66c8e757f96ef0d7eae056ff68085137da4388ad58ec650f51802b10accac7244f1ff157885139e8ba65cdd06b8c31468edd43ba18134a56f5df656996efdf544c7160a6feebd46334a954ea31b06beffb2e769be75d27479355de37b7e20dd7ef7d363053ff09e9dc85d47d9cfe1ea7f909aba0d4f4e7df35ee73a2de7ac2fb2e4e69fd461915955751796e655e9497778e9a946945f5970ff90a0d4b619f0f1b604dbdcb639f0cee53a08c05b11f8134f6b1191b2ef61521a4ee0b18bf1942ea479d552a7137152fe5535bec8455fedc90e911e969a950effb5dc05879345915fa7e7601b77acf921d97226ecf2e5926db648934b9c1144d6aff4e2145eddf29a6a8fdab0a8a4a742dafc4c2f0de3fdc1157a98769914599fd169f023baaf63d30c6173bf3e03e0cc87577772bf68b7df71521a462dfe83b30ecdeebbcd6e7d372c2cccccc23668ee2fd1762669eb123b2cac549cb69a8b50e9f23d20bcccb88f7dbdda5406674eea554a9f7c0703f5ac50c766affe7eefe3cd57da765fc392766e8b917fd7c8702e42d27eee32e5e9ddedfa1f00a8857debbf713a5fa872ea5bae7ee5e7277f7a94e83eb884656a5503db6ee03f1fa7d0f51fdbeff2890ad4bc36f0712229f03f33fb62281f9a59c0666e83d2aa36db9bc2fe11a98f7f71e5ebdbcff83aed332fedd9cf13da78149777cff636bff0eef7f6c75297a22abba91a779f577928bda58a76a3b92eaefa658e33b51c6bfdf3d0531f41754776fb54b5e1942dde0eabdd73003d5fb17d061401fc2329ea4aa60204361d7135671ab65fc5d68a8d514edc9aa47bd481313fbfd966701c3ad2c1eab42cf3db7d887ddb4525a59f9185796c8f671ab6b9468197fadd56aa15b448e3c56897e7e12e24d8f7dd53da27fbfbf1bfd2d1ff9dde85f205b3b96584f60187d4286129efc5b803e32254c744a894ef1779c2178e571a19f2f7affef7ce49fffcecf59253f551803466edbb66902431752b3cf501895ac1c4cd0c5988e43c1c4e9ea343fd8c4e095f6feee7fea1ad2fb4712789a2de32fa23eb1c8aade1a5b976edea1ef42145523d6d0d38835443370a4863e9cd87fddbfb71f0d394add3e8242aa07fe90b5db1ea76974cdd5b18fc1b1a8692b1b416a3f263dd332c9318829535445feccd371114303c4b8d3b8e7674d8b23ed3749d74dd1b3c373844913aee134159151e525a2d3daee2c6bfbd3a9e368e4df21bcdae88f10e5ba4d906800af328a83555c04abfa25186a95c605ea382b81d3744be9eedfed4e9702919d02cd33dfe447567152460dc39c9d9dbbeeb1ef3a4c7e07c62a3f19586fe1521c4ac32a771183559e443f370db7c60c280e1a7d74d4e095c4b1bd6486aaaa83573e79c8f779924038d05c8ea00b5b483572353e479b198e32fc2d4c6bd75048e41086bd8f758ac29068df5105bae09a98d3efa0cd2221f2391a7531f9db081d8d0bd7912e1d6ccf3266e65048adfb1db8cfa0bb8e47b12ffc793f037dc7330962be0f822ec6f436da217a7d64c7b166e89867d92703278be938f8a38acad9bbee4b8790bf6521e56f6c982eeeb7a9f25531474a23a080480d639c09dec5d886197e8d16b969dccca88c9525cc6c6b86f94b2a362c660d56f1abc2ddddc7a9c135240e33e3d4e0130f087850c3e78941751d59664feb9a762596f0b1353e97021965e44781da2f3150fbb7bb76dede2f4829b1c7944c13afe2efd41808c9b27ad0bc414fe5c0d045e59699b9d7bc35ccf0e4a29e52bc726fd2394f27f9b565fab7a7e230b7d430fbbd97f09a8665d989a59492462965963db6856146d5f79257d007582b68ce76773762ddbc20e6b179296b1e2b3756b3ae819eea9357d9bfbbbbbbbbbbbbbb3bbbca934883bbbbbb7f11bc5a1a83babbbfaaf6fbc7f08aa97f83a6a018f78113223fb25d0b52b4f0f2176f2c325b68a0420d4fb1091f68a2f37750c622d1353dfc8035a4a93471060d33c648bb394489ec46c3ec8fff02b0869a0646d7b454c77c0d352f743146c628258661b29f419087a9faa95dbb1c84affbb33b848955aeda9c5a665fd3e6f6ec2ef669d48711aa7d19ccbf953f03514c77c99ec00439a8a14d45196073b68faf3a376ad3326498337c0650688008b527941fbbf230559fdd86196ad526da041165f6a34d9c79adcd2a4105506ab8db63a4f60099a0bbbb7d9973b08ab3777fc762654d5b9e12f0a4e1a7b14cdb58b610a494524aeffe6ec0ac6c23742e114ce15eec7d8c02e92a1d7bf9c9c056d2930948d77e16f84bb90e8506ae576dcfe0483da99e547ecd2583262cfad1756d6cbf462356205dc7a075d728c1ba1c0e448e8d6e94d92aa064df9989ca4b7dc8178e0411253d6cea8e1f5db357552139db773d368b21db6f40490f9b1a7b6806fda321a4f26fdd2a13a1df2855f95de6ed7744d77e0cdcaa040867ddadc95046817095d8a3a043cdd81a29d7b0c60fa7931b552e31b02f23734852de20225e6958cbdf78e375dfcbc6ae305bee771b06aaac1a08a4761c28773f903f1b5a9c911fb94a263670ab1a5881ca815d3ff0b3a17120fb67c3397025f6d9700eabb2415ec9969804e29a5c41ccd1bae64312ad30f78464b6cce66c5648a28b31caf620be13e85032e236dd5dd07422420ed80004ce42052f2ca068569014a253a712e3e0f7ec1e13bca086a7d86ce4b5384489768a74b2aa658ca069769c4bdb7b6f56d39490f90aba28bdba45612ea00b3cb29eaa8d803a30358d9d577e586a02db969e969e6d3f6bb5f4742d2f35e1d2aa614ba989ca5a2f616212e85a5df618fd68b8d4cd6c50b70f5b7aba168d545d9ab53e28f841a078b33368b6ac55f941206f8607043ca46b77f0c414eaf2770caa9c61ae0c4f924406b6b3d38954ce30e3679f0c95336cdc0102da249b24b4513943acf19bd194175b4117634295d728c98809afc20ea87bd464d41a2519f58c968c7c46508078f4a4f2e8a7f2484ae56fdf0ea89bc2ab100baa523a2bdc794857dd6ec619a246f6da472778c15fa33ca4abc6101c0b5a38d8092a63418e7d889d60fac4192ca8f2674dc50c3b201aa43a7de20cff2809d08c5255d139c2c4e9ed8056ce2b5d1a958074d57e6563e8ba2e7e34586235cb362fcbb20c0ce527f2e4ec485c5a5ee92ab1c98bbc7262f13496ab85b5dc8661db7998f6119929efe3808bf70223faf7187f4cfed78413aa10fb307b996119e87d1b98bcdf139f13310088c1010038e4f03521a383e7c2e3a498bb0ec6196f1e80b5df90269c984cc11b6e00400c0e3864600e8b65983664ee0e3ab870317ff6876fc8e40fa56b81f0b4e7954d0f4104217db8e91be60722f28a59db8a816196658e81915559f63559964d0d86878137df8dde8fc118e88e619f6518f632730c7bc7fc73626a03f03e27260be17d4d007d1a98fc5f13933fd4228e101527c7bb80cec79afccba37e32b02c7b2ca3cb35e7050f332e76a009d118b105001098a7728d21006a9ca9f187984a6f80f1a08ab58f46d61a44f410b424f430a1115522e2cd42e1d79eba48f3524cba66adb053d81fa7a1a986fb23e2af86fbb35588331c85ca3f269821b398d5cf622d61635331a7a1ea9b86a793e408248427b6aa4bca3cdb102a8698fc7d9c70a4025163152e2b64eb6c2ce5c4bc4b3d0aed3fd64c8211e51f9deae866eeb306bfceefbfd7bb93fe728dc823e00e6c775dc78db49c7447f7d8b793de3f7e478c9e4443aeddfb4eed7777e9edeede5ed38ceb5feadbbfce7d06ee90cf446d90b9cffc33daa3eb8eee8890ab7cf7e7b8e6fe07d78e54c3f2cddddccddd1fb2340d7f7877f792fb95ededcd46604b1da428ecb3e75e4749e4f9370d6df8b74effb6ecdfe732d247c3f43d32652b5485b6a0fddc0f534657a8982197653f3f1afe3b9686fe3a4a347ad4fb332ef4c9d8e87660acdd5dd73d0e37f738bcfe384d02bf67014f3f02433f41ee57d3e9edef71f8259522772943066ace7fdf35b3ef56b6a76946743ffa74a2d9b7c78540ee4fa0c6321cf5fab58f86fc1d1d0d37d3d1fd90327b6e7bd7c13e199b06ca8a61ee8ed9e89ca79c4eeda00b56b10abf38a1db5f908858b5df5e488d60f83a3afbd7328a81214791debddf5c83ce9bb973cf89883562ccbc4c576ea1108ce3406721953519a5cac7690c7c56c96e7e2e74dccee4e7ef62c464d0711ce856eed4f8314a23f5155b9155bbf137bef3b8145804fd4059d2d33a5dc48a293e4e8e300fb3b088f2c364c91a599d178ff8a446b731c6c499f8ce2aae04f3079a38e31c5d2a56c508863192be7b7b87f6bb5b2c366732be888914f50ba591ba2b87f815fe0a5bca4a293e272cc3ef58746b48fb14a9a1e66a5765694452b7a2faf01a6aae96b2ab61e1c745dd9854ccd15cae3d21d66855a85c9b7e2d56b958c50be4aabc400b4595136f3c5e714dccd9eed6bae3f6c7deafeedf0b5b42c7eedd4e774a97cc3aa8424950bda9b10a254d2a922475546315488c54172e7cecc73823a486fd92a21ad5f4065585d8cb284d62985be3479494afaafe184ad21baaea5455bdc66f179c8356e595acda99ffc854c96255ca7fbce54e5c4f7e6a034599c2abec9b6e3e5de3733a9df6745ae9ad5671988b62f68b57fef22bb8660b8a418e458d4172488d41456adcf1560d4fdb0e6b3c3349cf94cadb6b0b6a4df31f1a5fd77ffc27fb993d51fba28f555477a5dc7cb2974b7774cbc749dd5e5916f2ba8d93dff77dd987a790d771fdec21afe3dc9dfa8f47c1b490273ff49ffdcc7f4e31740c49f2645f46987014d6119fb6426749bce1709fd4fd64349dc11e53e5773b2dc35ebb6b875bbbb912cc100dc99daed9962c0c8ec9be602e88db5902f6cb6f46d7ecb7a0aed97c38260b836bb22f4e5bd01654994996712864a2f6e89ad106533e4dbca31ca54d2a3f67c4c7d6f0e43f8e058f1339a5480f142c488a562685c814ae787305cd0fee3f2dc3af69739e4effec3af9c419b6e1f8e29bc1b5e9c9c80c3d4eb23e0f478eeeda74858a195d2f8974cde6d3279eb9a369739e4efffe53f94fa7cd87c6d7de7c5ad33adb7c2a3f0966463faf416e04b3c3d311dea81b2b2936a37f6c75ea3531c75fdd749fb06a9f0bed8b932ec684fdaadbf7f6fddb6f9b6def76f6db6aebdb5658a1b2896bbaad10655836936ea058b3404c387b4e04730a56f14f509b3cb54f9b126fb2069a126fe2f6ad6da0d330b2ca5365d4fb85828ed399dbcb960698d4d8d240912a62a053631530585277a8b10a184051f90b1b5b36d41a114aa7d5bc3090fbc00cff637ce9ce1f6afc58fe47a7612874bdd140695be6f55823360a245220fbb2e98c7d6c51fd19aae90d559df1430393bb4c8bec328bd11d926a3fb6b214bae8c3dcf0f4a15a4513a63019a08b0b833f3e2f8cfdb8f48838231903c219bf588f411bfe1d11c4e57e363270437ebbe0a7f60ba9fdd11052fbf733826b3310ceb48f3dba1ffb3542526755b6cd81b0ab4c3d005db7de6963e47ec5b5fb2178d5df7d11df80eea5d474dd15b5dba8b9721054a31535ba725084b2f7d5a334a2852ea5eefb62ce8b454cd3b6fb888d4c2e1c0455f99df603afbab7e1157ff7382dadeb34ade300cfd5be7b8f57da771f633aba6359180dba2947a42f71ed39bfb3ac9b9b70cdc9093fa91886fd528ffb358d06db2671b4a39d996994699a898634272c9bfd1de671fb072f193d9635e8ac5aa26b541e7baf530137caf4b7006394318151c914b57b92e847d8771ca753b31f7d9765a3e944c5a7b894d6f76db649e83cb6347b0cfb24348de44dacf5733a8d98b00a7bee89997dfcc92fc642c540f3b16717e9b1d38a8b1f62a8b5384d7004ae5095d389627e45ed9d6ca7f667d248edca23a6a280789ddf435427fd12d5c12aec88fd49816c25d1309b31a256d4fe1dd97a1ee781c9756046970ea6d4e8d20150ed62f779f19b812a3d10afd9f710d5f74a600f234814c8caef08d18f2890ad9386fbdc7bdf8def331da8d1ef67ef81a811bda1aafbfdf33bcf5b30a3d1f346a04c5d57791f0d173b38aa04ab44348990d7ebd8f77617b95d6ec442a81b5b77a9730ff7f092cffbbaeffba50dd83ecdc346a18fae2544964994262f273a462af61826127d2016dba3ad03c37ed3703475b51412629732c42d32a30b07532ac76ddc3d73dc3e64a9bb7d38da8da66978b5cfeab67d8c22b8266e5b8cb239a9dbf61bc3aa6d7bf934dbb66d34be721cd7c9d8e6382d66932be81afb4c5641032b6a43204ae5df9e541a34a922edbb70b38fc616bf87a846fadd63df11d86714c8d68dcaef884b9a3141903f5d3344c98a04ea9a214abed1c7d4c797afee638b8f328b56d0eb061ee4af7853fa88f19c1af537cec94c23711c8691380ec330d1148542ac178b08ebc522e2ad8048623e05b6b0423145a43022a22d445eacd72be60b7d32b8f7ca7ab188601cc68d5efba205090fd01658564ea4d1fa966953143af9ec0b161d56b190a0bfa5f4aafc2c4990c49a9314a24ccd0a0d694e34549146eb291f12e8b44f4ceb24852da695c5b456625a5a4c8b14d31ac5b44cf4650aeb9ba290c86bb0ab1b882485eda46042230f0391a462503127174b12161a8ce4148538cc25a608a21067fa6240ad65f653e01c8598d0d4b9f2a44f86ca933e1a5ebb9fdfc8e5e462d1d917fc9f6c52599220e161395143a37a962425242516139624376049c2928425098925c91c51962418d8c2625589be4c0931e15ea3a08884cad110060a222d263c2e608b0e91962147586661b434115f2ca9fcdf144eb0e874b48585bd3012c775261333875c565ec585e5e4bd6dcbb39c54bdb385e5e4bdb9b438f122f462b578d5bd38acdb345f0965a08f45811bf89c988c4303f75af9b08646f52713ab1454c222a6c582f169192743882ff88b50f9b3a0291c1ab8d78a0c5aa0128598848a30104b5b587166092e4c246ad1a2c5c95b58fe747af71696933738eb8bf5e2b02f44fa42a415d007c9e5a5b4534a4189a7d4851250e9552a83f7b6b22a2ca520962492c34e3ea725b0982002955ebc62e16155a90c71c5f282d33b0d6b68361aaa5668f827159625f89c5c2c2660d17901cf898635353cb9fce4729d5c538548cb845c585d1330ac38d3c28403b1077e17b0e589e8033f0a6c692202d1d332fcdf4867478a298e90680bebc57a71d8375a0191b8fc062279f91006fac0fe87567dfc9895fbef8525c9139f569ce1120f4c12e28b2238a92d5ec10c61bb25040186f59a1267609a08410c7a190396022bea8b1798647183209fb085d5a203b638d1d2049122acc2680bebc57ab1247995824e2eacc4e385ca1fc6b42a7fcb4e0bebe4ea9a9621462a3f76721599f4650a8be492da564024e07f2012fa1888e4e5331089cb87c01fd1d75c3e050e49ddc0b32ff8798ee40c711f1603c2dc00c2242102c14f419856cbf083208c088471c12c01617ae24c926f42e587a1020a97781606cc0de20bfe2964105530522abf0613a5f2937250f947535e7e5c21265df3e205a0294071024fcd0968545c88c94b1a2a3f1662f2328610931485f12905b15e5c8610c18a74d8c68119b6b05e2c5e75438860453aac1484711b8824f5a8d4c9757a424cab5992300b0da22aaca151852f7d5a80304ce20cffe909304d2af30aa607f125087c017a75cd4b1aa20c12e2cb2ba6d5352f5e8832347891c29284572f5980e9a991014d0b5427a82c820a456598262abf5c359cb58508d8c26a19c22af6e84b8849e5b0145a6102c20481302f982b582f169152d0968148528f69298cc3380e63b16a8358c54024a997a90daa21cc8b6705336c61d51616f8d335a857500d5b582dacd5505c55b15878c7119c2514b3bb7910a1ebe2eeee013a292857f9ab083f526cf887c0c1312a43a9fcdeeb555e9dca1c8316eeeea4777756699d7c612ccc8910aaf4302f24c705449528ca002efff2395cfee54bff42832829d11c2f0f4383fcbf7c1098777921392e20ccbf80dfb93aa06e0b2bdd141a6e35c929b5f412a8249fd4d2cb24c4d5bf7475cdcb974af34bd4748669a211793ee6847cc1bccb3ff1fff2292f4495ea3b40ca0b51865f250d5186a77bd82743a42f7dc75fabbc0aa9e5c3510f8d14509ce157797da5e78f023348354524cef097e80c5255d989334c2ad11e9b456a4abc213d7fead5355ff23ec655d855f4318abe6f01595e2528decce75f6175cd4bea636a34923f9d0b680b2a34944faabf9c52fd575cfe301e3f1a4e4a05557e5398f242e8c2aafc2330544943e54f81e18a4ec73abb46023daf64126ec8a6485794c9de058c312351e9d3c2f4130c2fa053faae8b9f8717d0092fa033a344437f92e9fb5b4a254a431e42aae9b39e60ac24300c85706495163d4eef6bf118c6f2a75fd9517a5357024befe0ec2f95be65d43362c2a32695476ffa12387a91c8292af4fe25940824899e64fa92935efb397aa7a1cf2fd1d82c2a6982a17ca14274c6665149b4f4fea5f79f8fd325d267a9d497e8dcaf00995702471fbf026c4d85a61fd1152aa689a6be4477a43e4477b47cc9c1d0949a2d9f3daabfd4e27d898627cf31acf4a6ef4aa009c4486f9a1846fad29b54a8871951a2a449430ff316186df15e8b1634c4fc39f0c756ecb1f730eeb1d07f7334fa6e31f9ced1934091c9647aa7a17c5513fd3efb3e1a0aa93d8a383cff034b1fbfd2e370971c3499dec1152aa61cf598c0705402b275fefcd0cf289544dfb137c1d077ec954a3fbb687a07c30be8ac3831fd43f9da6f46e9498fd3f50b47bf3d090c99a84eccf0074f0db31f855ef459887e8fc3fd5e07eee4215f150838eb068612e8b9808e8d16b53b760d62a053fb71d805c5a81f489f7893bdeab261f22fc7048b29fb7d8aa4e16ca27ab75349fedc1ae6946ea7ba744d2031b066f7de64c2ab26b3359df02a722ffa8937fb11887bf1ea0a2e88c342f6a07b2139eb79def7de7b3488f75f976175391a8aa6d083fa5337cabdb82bb8200e0b5671629821f79a4cba55b97da6ab07e062ff6894614a9f2a3f7e422a0fe953251d0013f64547ef55f9bd4efa00491e74fc118b4a6892ae143c4a3269c55cd487ac83a36536c855658b13496322ac23b316abe467521ad9bcc7616a81deb980dea3c02e052af1fe09d65a7ce95118cba3c0964f0da03d308665e47fd734b551f51cc24878dfbd02fa996680bfa5cbb725b5a9b9767faa6c498c869a4dbcc95e7e10375bb35b4384ba3a4aa8f26548f9db237fa55c28552e8eae9172c723106764b832f2655443952fe5b3127146c629327aa0308a83a5e56de20df6f28388353b04d992eec31fa332b4dffe1836bccac12aee322aa6945dc8a8985d7dc82f6bf14356bef4fbb34395bf3e52abf2d5ca116ffce5272046f9e157c3e892544c6679d423d1f2a98f1f0e97770153e04e792de19487b77cfc5038bc8f15875b4016b03f077ff7f35740d19340ee55c0d1b700435f02bfd7429ed799463f6488e8877cdf0961e1589f9691ffc90d8a2d135a20d9c25ae416e6924c4224b7e842c742d27865811be20a0351c5ef4baad86718d88f51b5340ca2eeb37f28aaa1aadae0c499fdae23160a31507b887a6c165b5d99a8aaacd29a307dacb634c619d98a3365e8565c1063424fa7f6771aafb4ba9e5795f0871334d522d46e3d2994a4d4aebd2a8c3ca9b10a23515406514b5fd4559d5bb8626548edb08d58877af4a92e189d202505b5931e4b21254a6551a580660234540f8ada691e83a412d46ef316dc4182f237790c72212852bb8e41f99d8883c1f458dd7921ae704c8937fcfdddab6bb46f8f4845406745d805d566538d35184fcb208180b862405479a7613c48ecf73340634a4f72fb75748df6dc2b60fb8c6620fbedb9ec378adaed358ada07a306ca2c84180f96640595a56e21fe42d89138d38f81180ce24c3f835f5d3056b1f383ea200fcf91b9357bad050fc61acc052bd3df580f6c70d0d80c6a7f28c3bd60a678a4ab65f6331ccc5daef67740f1264a9f3843aa184f6d778c49d7b4e8604a38c391da8f4db184571c95608624b51f7bc2abd825c80931273bb2084ad0410db128b5dff9945a928253cad371f666782a63718b18d6b52af6fb994bab76fb1a771d0d03a8b5a844ad9685410b15cd08000000059314000028140c09c462a160349e88b2221f14000d8ea04a6e5098c8a31c076298420619820c11000080118091d99000e3243164b85a6ab514ce7976ec0853598da5107ac5cf835fa3ee00d3d7035cc6117c808906c57f01b9a76937559efc07d00f25cc1717b2c3608c762df81c25cd946349a30bafaf2a30fda4a89d816ed4490277013ec0d47dd1323d52fe8ada5efe76315a50f9ba7f8be0ac312a51f7da7147bf2784a173a8481298963c64d802ac459e06133088690809941e6e365d7278addd19645a13b3d9836a43ae09599684226902e118109ab8ccb093c43bae01d3e520d45cf2f137be45f3d73c088357dc8e8efb2d3b5dbfac7d90fcc53d806dc8aa0c8000af86195bc988f6b60b4c67a4803b8595874d126aa9a057dd04c8b87a6b55a314f2eafde5c9226d495783c90bb566dc1d6f7cc73cf753c5f6c5aad673d8ec36fdd68d5e140ec4723db3ddbae7d8c0447dc360317863d36a40fc59b4052fe8e3ca044c97808be3a1c4fee7d4c33949316dc7ee069b0f80a9684c10c2cb769d2ed3d0967a253993cbd2bb36c51befb09e252e92cc3101057645bf2815134c92976a31cb3ffd4d2890d52373c177d0af1f5309a6be32c821fe1b0531a41cecd61da29bca1d6170565abf9ef3a3ab689c75c7959eb53aeeebaacae5d22f260e448ee9d2a9fb9ca217da775ddd02b155915c1b9d7e71fc04b592e6d8bdab678fba620868b29a970fa826b58a154cc64a80ac2a38bc6c235a54c558589ad9afd8a552b3d7a05c497d601cf6e6779f998e7fe98608db7b94e1e43e62e7a170b1e0a6cca08e4a5e160855e75b9cde8d01b0a2deb57ab693c930908eb000fc721d5653968718c2c7838a4cbf54dde47606359834bbfcfbb3224660c82f1d063f06d743595d8f5466eee89772747ceb720377029f856dbb0685b4c2af684fc8976c329cf0c138a7e9cd1d92b9ca607709a79499d41da0ddc4f7ee8e65552e69c6759f248445b97f5e03ae7f226ab57f7c8d8aee46e289cce72d940a2a68a5203ca6f4c862ffa4ed561d53df75192af4f8c174fb7b9f6c81889cb1d97bce6aafb61b03b4170a96d0e97a86e446a8be00bd54d5240bf44c3198d459dd235e3ae924745307df09db3f758a12985d685a9846a266a5295a56ed9ff0f083e908f968353e5cb63107942197aca5b38cef8019923591724c04247a5f2b32ea6c7bc8c8e0746329ce1a1e77f9942f9a287318fcb5b52c81c3694baff251877aa9b21bc37b837ddcd9df6114d84d34f7fabdbac9a48ec4bf5302b2460356c96b44657d3508ccee178802e0725f7e3b67d72dfea03164845344100408fd1eff16391326f005814effcbf8f6289d1eee39b2ba8b0f4348ae368518fbb9a44e721c5e508bc8212c506dd8e491e46068b8f72c180fe1f94c88e60096f2e412e65a4f55a5de0089a21b46137a5fb6c64eee783c98d6b036988204a6523a3d054c91760102437e734530535590569a70e52770870c05493aa8c3df8486c631c23b461de405fc06047f14c16da2649a4d5773123de6d697f8e1c7c32454bc7661834d3d0854f94208a54124a62b91042fb09fb56f7e3ffa93c81dd56c12bdde89ef574676a00599da8d1b7bf69eeaf84657ad57fbac68af3bab6b8ae13b883a9f99863acabac930e587e1a0af04ed4a674322a5619d6885dc54ae2231d32342d181f28ac2d1042e796f44bd14a97db1e6a8ad217a56c1d03075f57b9f8f3f0dbec697d94a25a555683243e93ac461bb3b5526f82b11e0f83b06b6aac6c841bb5931914811e7230e2e4d991ff5aa0d78cb73eff71e07e94957bb64b79667898048a14a498713c059ee33f6612abd3eb835eb92d7dc085b7d9e32e5e9b9ec0888b43d4d2b3e9b1ee7a9d1b1de64cbf2af56394c07ba929898133c57c55cb8bdf4c4187acceb98986486390178699c843059c90d0c090c81da68822b8729b25765ef5840842113d5d3d4291333956332937ad5382f52987f2ac3d173e144f0e71e91837a4ede08053e0f8e631580013137f1a53b5eac18bea359af24305054d536855db9926815ce295c14e031c0c27ead1be657e0b05a875ce880d43f8b21e4033951bf030ed917713fa83a499645862b6e3c93cf7198bae7e00c710c3d0193ba1a4412e027f816bdbfc3a4f5a0b66171c660878c2c6e234c49ba5900ca8b601ff991c3ca923801c3266e8e3dca91c4bc13b70e47a6fa6950dbf9fa4515070f532fc20c255edfda20f22159559d44613949a0e32b0f96b6985dab06dbab7623abcf602b749614eb4f2fc3662748a13c45ed8f50553094dec3d4d712954af737f7a7dd53001281bc462ce12e03c37eb9d39e8b1871609a49bc1a8845f361d636601c5be78cb1e232c3f5708d9b785ed94c003e9fb251156a26c69fde70f5d9792ae12000af6ae4923d2e000409d9880114561305ba28dc6874d0518949b7a622cafc19eca9788a90455f0bad32829852f689500be524fd7fc0c43ce9427a4a83b025d4df5583a53c688d6d2abee6b8cfbaed4f3e0b1c495aa75261f48b39cb47573688abaf943d969adda1a8097a9ac512d22884e25804dfdd5bae24a28f8951e8edf21f25a4b406760cad98a18b714ceaacaa9361186b97672ceea2a7eec1ad0b0cb1a7888fc0d08ac30de9b2cea4e8cb1f3f010448cebd564d4253241d0a0705a32b4b1543300a6a4c2b926342c9ff951adc2c25ac8a9bbbde0ba33e55014591ebb1fc091a31aade671831bd1ac2086a9780f7a981d888455915e05d931b9af3ea74fec511c3d5f06da4709603713f06a3d18759ba43321021263a7cd5a7621979ba1270442e3254051dc0d42bbf8ac1f0479b7224bbd85625683108d842424da92ca148ffe21881230f5ec33488a75ea6019760640a2b27fe542cb34f5638c1b1548f40a62f362efb4a79d8fc57adb58b5550d8054b8965802be18bd05de52d22598ecd62496ebe03e3b204eb5174a41698d0acec83dc1b39352ba9c0666510361ed66eb392c19fbb09b7fb63900e3834291d2426d6f534c9af0f51904364d0fa27ddc7938449426d273c703da471c47f89d0d9f26345037776135ad08415bb987e4a136ca995eaa803f718f2c2f4a84ebe4f04d3b587603c7ae07b0092d08b1054e9326c28082695800b8fcd16cce3ff651e0040fc7f91df6f1e7d117a6c6570bf5cdaa874f085d94f04f822f45a4b22a2ca779d2278424a0d291ae6b975214497163e9cb9cff62ea7741c938607be4c65fc115bd018a26613edf43dae86cb24da00df74246da58949de68d9835d38b7d247d7232b2b1db183011806ed445bc64f5a8f9ee8782fdb32316a15755f3629a7031371e21d3710c29500378f0ba62ad81397324731ff559f54fca317a160acba6da167b3a031e7ede9a1096952bcc4c1143283887d237f6dd0b9f84990eb2f4776c17b2bf659d06391a03c7fb7450c4dfaf38b7dc649985905d063afb61f76f7d847e886aaad569fb05dd159f4b75a1c4f15fecdd551be33c69962fd8dd095997844f9ae29341168ad32645a20b5867f39b8d882ae2c3c8d7e6b71a1fa94b64b5dcea7640116a45c7dc69b565f5cbb6a44ecfe7d27152d3f973881a6829ab8255b84359ab199f3896c1d184f7cb289fa93e6c5e3c252a3022fde965b60ae8eb68b89810169d1008163319fc33ab720846944c4565c846f728af64a1f7833b0eaf8c68ded178059187278f25668970875b51c7d2e768916611883c58c6512dee7c8544cdde836c3cb3f460ad0a6b885a9bf8b33fe75d2d80465490759b473e5af5842f438d5433a05659112cfe1310b1f7191f07248210d6b13fd47ad797c9b58ee5f93e683a9003d2686e338852585b7e68c3aa21a8824719b675ca420437211f7dd4eaf90bfe78885b3c375870719c92513d58909c10b35c283d4fbfe9be0965c3188df2625c0b79c403e0144ad89b7f720c60b357a2967e7cdf3d17cc34f9d3ff0d9ac3a5b9b834dd936ab4b1fc350e2e2d1978e8373872e900d3c662b041e0d5f2323e0dca16734ee48b913ae82ed02fdd70d3b59f613e194a92d8ad3aa10719f3b94f0d96f9f8dfdb9430430a1b54bdb50440e2583b62c2106ddc0ce3acae016f3c23758635f1f4c32f3b44050d90a60e059c69db7450bd42b4300b8a02beeaf6380435267f93593b21ebac7a83b91293f84a2fc752693d29227905c16bc71975c87294242e0443027a71c20c8367af8e2c8343fb87bba6c2b308f1a8c4358ee9383477239b1528c8dc119d5bcf70f71079132c5f0e83a3ef3437bf31327714c0d560354963e06164a48715c4f0ced646bc04ee7ce854cea2a67e1f96bf894c6f9b59140bcefd4b6da87378794e1b43473e020a6b1a3eb88559f2eb389f5064da479d385d194b98ace1335a86bea2806661beacd25f501f0201b1115dc2b96b796db392e895e8aa148a4500207309a4a286a8be57c7bcc036dddd5491d3b424fb9057385291c7b60fc645f6dfc019aba011593b31254b300147df7fbe60432f628836a0d1b98090a64befdc063f8f1ba92a7d8cb5abec74c4b98993fbfd833499a70609f3c0cda681383cc3850404e9ae075c5e38ebfddb8316164d97e5a187c3d199f14d2d8aba0d3c1f9f214eaf53740b764449961228f3afcaa2d8e3a28456cdef40ca782a4eb9a247cb2923e484847a5dd3c1cd2c3630ce477fa7c5d25012933e1055e1c5b7a01448f7262ccbe908a4534f5cbecf860ceb5efc6d6b01fd747f59fb7b9f84c31d105b7d161f26b6f25f24840e3bcd2242cf9ddb726a75cfcd88c4370f7089f818766591bee4fb5a75aa682b5ed048b38c1787d042c813baf2fd2fce91182cea778d4fb29b1a64858149b4520f4b3a29d1b9144f3534f9fe41f2d1b9a76cd0b4a39d78b49721cac113ab9459e765b31e8905c2009a18b1508515638107d3cb0cc1579feb36a27e0be1527354d8bbec4bc5bfa46a04faa585e5e924d184e49e87b349fc190e6644a908842b42d362453f74ad56444993e747436229d670406c2519a34f951f7fd92fa752b73029917d9115d4a3a410b6e7d6a91776a32254d0bd7c0a14fafa404d7e26efe5dfde50fd68af9d0402330e5a31a5f73282f87de229d02c6bfcb9f0014ad1d7a36e350546d1dda3778e851cae7b5c7e384841e7739800c2d409537be7b65e1a678ea5d19f0067d5207eb99a9fecbe79bcfe630a59bb838ae1bc210d60d40858a7a77731cc3531eb24b8e868cf5d274fd1eea3d587fa0943aa54d0801be5d50619e9475bb46a7b3cc63a195c7891fe7417657cccaa9549f87a07477e74ee620df92fe748c87cf47e9b83b508eba5ee57c0c190d5efc35b201f6bbf3b2435304c87ca9b6942b95e55dbe99b2c64a1230475c9af95bff1a39669b983107829c41546468aba8898c6e518b29b3e8d227b7dfc2e213898cc8205771c814a537cbf3b5f13e0263866b4342824c99b89ba54134b2d89c1d8fcce04a52128773b36375d2f226a74986d5e333b2a597b68f1e360225f79e2cb7faaf0e287e4f9cde0cac02e29dac11a4adcb8441350e77364a0d46c55005c26156a87353dcb240d8ea22452ffaa3f9281bccf06c98c95cbb35aa24ab0cc2044bd0baf71a7566f93b8e0ab661f8cc258ae1a7530983a75a1dff2a34acb9d60e7be55f75c449fefc530f934193fb8d1a8ea7e9078f56a641d6086cc2a0806369118456a0b2c1f748e0e6bc769dd861c81a8c5e16fca782cb87f8ac56f662a432ac3c02bce192c9ef8bd4d9494a04359db9dd38381b87cfe426009ae8b804a7e79e2f4faa96c673a1aaaecc70f236276e6db42d73197944f96778c333ce8f7cf969d7fcdc9f4382a7d2382e523a062555edc572508860f56236be235ec7f3c983009803ecda04609aa3495dc69b582ee329e8f2e48f405ea9e6239f6ede607d857b8d95e05a2b6ca908af9454dab1b218d8c767a9a2b99e339a16b2eea72087e3cf259b67dd5243eb14d471be4c3f3cc08a719ab9da47f247796dd3cd28c6a12774fda1a7a3a64569a9134e4707ff7dad12cdb7198ac66d3a9b5f83d711d0ef047089c14a079a00ac69164decbc5eb910add204fc153354e6696ac55e3e657720270f26e532903488ca5c001843e2025cd03c6bd67c4d4942709ef95525d49dbf24227c9fcaa5238209b5ecd3fadae14bb54f25be114cafbdedc973c51f05f999279dc57844bcc4f1a5400f41e11fb7201c7f35838b691cd2b9a68d7c21ada487a181e5cde1179b8687daf59e607452a5c6a1223c04e80aa8a9cbd7e93fd3f9d0378dbcf7b229b614b4693769a8ec45906ced32acb5ba4980380743fdce6679c19ae0416031e6bd24d93f98a086ed00e02619b9730d4d6eca93b62e2179e053850cda946daaa6679ef53df0bf5e50ca478c3ad9c1b8fd6219581fe3a2b2acd1a02ff7d2b6a45af07e134328300c144ca9f6f977aa197be0c259a522b271870f82694328d894410f0ee0553d8373189411ef240ab11490fbfbea93b8bfa6e76157d121cbe7ecac52e16fac0faf989bb046fd9211760a987b3ef23c6e1b42d3cda36947afcae07c5c345b5b396de842391cb8439703fd72b2e78bc7fbfaa75b36430c5dc4bbdf8023309590580032c69bfa1a0747abde3c22bc9a18e418bad588537b0c2d197a7e7f086210dfe58becfa7f73f19c58809af15f3729a0323a6910ad34c0b54ea54131041fa0f8f52214df196d97602aefc5a34d4e3d4ed7e18c536a3cb9fc3f792ecc8739cb8281be52f6c26ef0aad48b7a036082cd5fcc8ec4b723bb8ecd18efacd85444c2f32ddc3c9c38235e68cd938284595ef6e51e32945c155be97d0e5d2da40fa79d8040d482c010f3baac584a1cabd4fd9eae5adf12e009b184c1f8fba1d21105af07098a582eb3bf6e3aa0cef06c2f29ac003af095eb0d851af5d314beddefa56ca48de121862798753bb1948605765e5f0af87106743f333c3b7e2116d33141ebb14e2389340dbee0756330375d80f0a234c40941e55b05e1f6937555105363516e487c84d876f9b79bdf06fea4308a4f7c7105cee45eaadaf818e808b8f80f1a72edb72488e61fd8f46e6317350c4d43e30c99032a98af23063a158b8e01070c96532f0f3f04bb5d5292ec4b8b8a9101a21ab82e7e36065986ef400698f5c67520eb03d7858596e6c17ae17c3c2d6768f565450fd08a8aba0ce98c282a4a2986fe7165077a176e1fb30ca9b227a4331e2b157e974792df03b8b6b5baaf8d0af182a81206dab182b99bed4e208e380966f13a78b363dea75cca30b48a494ba5700c6e64512875e3c439aaec9d0535d0b6db551cb3d2f2e4893679d15e7a269fe118a3178a60a3aa59493f20dbd7c5bb5709e5b212e6d0b2f4fde69db2ff84c0ca913759c1b2256829387084e052a5ee92de6f109d4692e1fec588d9c0acce1cfe0f1ac4e95c5e78aa23520bf7dd1f475d5a3de2a1c31163a3b9cf73231e3d23dd95e6edfc13075350b97c36bf70a164aa91274e8a84abbd7e53a3c4112e060b00793f379ea3a5fbc6944e5715e8d3284e34080fca99f69968b6f466239d0e4cf27b8652e944ef8f63320798e255dbc5838ff76e94cc7f1716a49e8dfa7bc69d8bd179da5daab7423e890bf852d2742bbc8de8095633ee31178a153e862fbeb62e5543cfe86c7781c778df8b45b60af030402979892388d93c26202a07d076fc22b57c4c64b9bdbcb989ec4748d3d04c2bb71847c289a563f92fe56353d01d5a69b77182b40ca47322f9927a911c5989a00ddedcc429453a9279e8290dd2a2e899a2bb45ddbf4a7e5a0dc6c97097add7de40d31104b8da8d73d3e881bc689a37b32e297506839c3d3667a528e72ff30a9fd176610ac29540809e7b443e29fe955431f6bd0ca6b6917bbaf7a22d43c4b237d881198dbff4b2c4d56ae3004070a24f56acecb916f1ad3c125a7db05abed53026d57827b186a8b76253e341e32e3b4ba248d2ccdf0da4e458d18e361221d98586a3d08fa9c2d734b0668566f334f42708e225ee243ecf8d9a4d1e47cf1e29dfbe5afcb9c75347f26c8fb175a9b5f1a3ac48716877e6e4343c2d89207799991ad0ff53112ddf6e3aa4f3a513c11a828107d382cc195fef21c7782dfc9574bd02c0610bd84d94eaefbbeca24fbdbd8876f6ea9d67ce56ce5303e539f70101c640889d1dba1580ccdeba1e3c3a7357f40a0594a54d1af2c555b9c1f7e4356ea130fcb4b1be9a530e71e9184cca392356416745a8e1f22f0d1c657bb3fba6c918dddc6271337bddff337981c9be259938913469fc4e7aaabd33db8af17c2a107bec02d6e43cd848372b822a51c0fe50e62965934e2f6282e63709263d68b6a1f1779cc0792f08a6a7d52d09d3540d8037f2897872160044c732c249accab0b1e2968f84678c41c4a2cf3e49f47e72ccb26d3ffae9db08d4f1411cabe7009071b671fd0a54855748d6411177ce57e3af56d009ebcc29fe44d170dfd7e5065555b2cefe49d60ab0d4bb3a829627c0f780a4ef8638e5212531be5b3c44d94e27943991d46f500e8d1617590eeb5e28e2ce20b91d6875bad26a47b42ebb8cee112689f564b121c77c9345eb745d060e2b85ebd0f9da416bc29a97d459c883ec12625a5e4fcd90511dacf86036ec0c05cc7f6894d13f6aa920487c9866d64474523cc5dc89069dd63833a46ccedab5e3fe240a94cb7bbcbdb0d3a5693dcffb3ff3a2251adcbebe8638f1780e3d7a4ee854e19e626676295f4d437c5638e7dd1f75706717c1f448a371d1e5491713160e76a08ce1d4c0438a5791b18d6d61bd0c48c2ef81426c85a518a095ee158667ef12fe4ca33ade1d75ef6bb4b8f1e86257f78ece39b05caa1d7106d5cd9bd438cd1ac195edf05a067cf13373ee6f9976100989c9e695751f186885a4e3ac9a026d75564087cc0789e8a7c7423d64669464a73748fe2bda0f330c345c93673c9b956d3dff818de2800bf3d51631892eb6b71d7dbddc27a714db499bf31f83eecbefa38ed53d7b346cb838e69b4d113a04b6ffdaf24f6d47fafab4a82eebf0b607617ee02a3afd048be959267df075a8d5ff5dcf397e1a8b3c5c30ddee29f626d5482a29426652c45e1d2fcef798e2da8ba2fe0a73cc3f63680c801f4b460cfec477d58bd3c88f4463e02065c959be25201bbd1afbc7490bd2af22cadc0266b06cdfec6194b86b9ae32daa47bbf256be89580734af9c71ddd7757a3196d23278f0b9552ce7ab33f9442fedd9740965dc8f6a8ebe9a78edf135054cdbd07b328ef66784c3f3f0a7e73fef18c7cbaa2c07b1be5624c792bc261ec67d9e611f16a9d46572b9e9483b9145006a583bb8a65e42410edaa8a1960190636cc0d0995e0b65f25068c5c40302bea891b5d7f6e0c287e4e9c6e6219fd2150d7f8d2367b1416f877152e3e38d2a04ff12ab5936d6ddcadb02560c66881bbaf9acfc6d732b2bbd84a95d9b970521cedb0065fc7f32ee83748658509ccf5a6c6690107698db12c74fe6d4802f81546f53998d384dbebec502fc9a6ce8f8e934d503209e0f08417a83279379adad4c94183d0ce2eea9e207e9292642f43d860252bad17f22ae5a53fbb8c48dbf34e85cddfcead95cb73ec5ca405f6520aafaa37f411b70022b56335ab833d5da78ee18b49cdd9344f47f3b44465eba0601988842730ea79e3754eb720b3cf8fb4156a5c40cd663e9bf05acf504654b6b9235a8d625806fa509af3e160a837747e25d09215cb04604bf3ebf344e930871517d305823603a9e262c253e9d9714bcff02b9c6cd5b98217864e77f282bbe05d0062f95b673512b7489cdfb67d1ef6a7f9d97b661fc8dfe93b064e3cd261cebc624a9879e7919888f4b8e68af49d9d8afe0ee9f87a51157d732f17e3610c540c6c7990d63c0e1582f5226ab540289c0b13e9a79afc3a54771c0a9e8168f1201b10f89588b9f30e2c2e78635eeb23b721a74f5c712b120e486d1e22a321b4d86ccd709cc6d06ce44d3b0a8b09bdc0b04c524957fc79498557a6f8095ec57ce825c2399ec4c158a3c9a02f3ea2aef3a1c39edca58d407e633b08318ce9b93d38ce0f7a0e2f2b9146891d94a81f571b1efb206e62d220ce30cefcdbb1bc3a666bf3a4f7418f230008640ef7811c4c03475f8806860fe0b2f6c02e97abc6270f278dc16faf8625734505f94887e605b38f2c03a40ebcef152144c03eb6fa2fd5b154aac4cb8f1d77e093e0acd633845e478e192261e39df9a3c1e62b1b219f3c4c6c513585482d4ba05dce26767b3d24a1c2e50d526bdc07af2feac3349ab5168493257321af3891ce949e6e85993bc7caed34fec5f29e237b170e7e5e745fac732f80b9a97ff6ee53366bc27747debaf44bef7aa53baf09d844890a63605b73d764330e8ad7d239056bcfbcf9c0988ce7e0294e897fcd88db38d909a64bd2c7473b4af23563fd50cf5581e172cabf88b377715a11070584eb80f252144f4081aac561e3a6cb51efc096e00ce63d1e22893d706e75890400b510d47517c4ec5827dbdcc6ef5f7aef965e600b9a1c77b06939b34ebc51259989f5e62b317efc57c075ad1111f10a883dbc0c06d5ceefef755c6a81b460c5b8a2266a765572755c2d90ff1069f98db6f8872253951075be7cbe6cbed49ac7977682143f24d31ade49b3bf6a231d1fb360b3ed6cb80234f53a7cccb2130101b8f905a80c873975ddb9ca812653a8055b5e16f8acfaf8d457281d688710dcc1ae483f18f672223016f9c31bcaaa53c5881a83db380c6e244d787e395818801ee92a18089fbc204f5c79353b0dbf3792382120beb8c80d08606438fc849f3393f02a4d587401b9720af5f21521170e40557aa14bf11386bcf646c3fe423f9e84d9cbce925219c3dcecc5200a6210dce68d0c99395428073cf495f05f039aee0d3143ca6b9d3689e833a0caf4634adb4158f5f70e7f1304840ba38f0be4d4f4a1e988e5159a47a3c3c3ad17059b15b3698e21573f0fc4ddb3c8e39958883ac49722c9f1b46cdafb502c958dd30369ed848429541711cac9d417c256646cfd7fa464c03b521fe13c3db92db7cf3ed6a775b865c15be65c4c362161f2d08b4e34dd628a6ebf0f8e63f0ae11c21d9dd8cb6d754fe2a4a758c69992edca3bb6646df1d97f35435617355c519b0d24147739bbeba1adb3570a71d3b53e37c77e2851b7bd968998959b078d566bd6b29cb9d5e912daf2181bbcea4b5031929d5c7a300ba7d6c8bcf33dcde7247cef8e978bb0f50eee20196b03071d119bd76f69636940d76ab77226e34131585aacafc5685c99c6c4ec582036091c25c1173e4cbe870439dd3d377267599ffd14e55cd39936e34c0a6a1ac20ca9b8460d074edb47a1fde1e02db586ba604cbadd2ab4f63452f9c1e7012d15305f013db669e6922b344c69549c4c553be34630262cf22229a3d5fcdaed7a78f1b82337ff6865c45c417cc21b62fbc2b2b9daee0d5d03ccbd55a789535d433c50b6ce51a0421dc7b7d4e3e25edc2fd86c5ac3093c4e8b1400b9e5768c91c23ac3747d64963f7a9917d68d41e0d84ea68947d35e6fe1ae87d1cdc7e1b691fd1b8bd8f1468e8756e10bfa31e87e8a996e1ed0d87d509721b175f13a715fb0329b6e5cc98bb7f098b2e7bfa93416b3a92b5f0a5e1fd7d60006131cf5ae6221e3c1454641a6f2aa311d3037a53d47a7ecebcc3ffe153822c8f066a5d8b0f7d1bcb168f27d6d70b15280bf67d4b488c661a9a2dd960914566bd40f30902641210ddad9461315a594daf388db42ba8adf4eb277945d08abd3d0a749e0ed7d330bcb351ace7045c0a592b10edc6633215dabca7d72fa6bc0238d509cc42acf2c56e2e8827746bec326d3469ca3f678e7e00002d2a9ded5ff47983d82a54f6faca56396ddec1bc9483fda40b436cabedee743f0e2abd15428754b3ec50456a7d1de561f65af5c667853ef73398ef479d5b6e043dba15e758024821c75cc9e66b6432f0c10a41b4594d7fb0785ead89e1e6754688e80284019e89d85d77caf965aaf3606e7cdbb0d41ad0446072c2821aca03b3c1560ccc3c80c991efdfe011d6756ea2257700542bd7317a49c5016607c1af2d45901a83feee0a7fdab706f372cc25e54f1bd64f5bffd431161970b5771b9656c1ba3f9a6cd5092da43a5306759821cebe8357c8215e7a6e2327d5b47e0bc0fc2a6a9fffd8250c93c8922f4c1fe5e05f918500f7e043c40c6ba04831aed1fbc0ac8ae19cdf05a1ccb81f4d56a77f0380d960fe48185e2d2767bde65ef80c99671d0c443929954cf714d87d62522b7a2027ec58ae4b61868b23c98ea76cc45700537a313fc481df5c1b7d8da12f4ddefd46e0701fded58398051b57b9ee262db8c83e9bf40ecf8bdb53a078a72fca8df9c1140a2adb36c3e9e45081bc0e0c0a09ad283ade9a0ae198d3498456e78a5c317425cd7b0c63af7992e258ca39f897a6dbe27dd0e287ce13ca2ec785d678a02fd445a8b7ffdb27a5c88ad93f99fe4951ae4812e1eead1e6c0c81e10883a7b0ae924f8025ef540cc29a1fdab30b2fc82847e3bb8a0f7a3286ef4e1547e0cef733391903f6a100a1f0cef5add1c1da4dbe3291c92b180fa31863fc7952a44eaeb3ccbeec5c7fafc6f0a1cf6a01675cd6ac3f77680cc53c057490fdfcce6da59d0cca4154636c60ca9dd01d6808f33878c5bdd474f83b4a6644219dc0091e1c1e4f3ddb24a0d38fbe5f7677c19b8834bb0ec9fefb61375d0693360b95fa397a879dd5723dd0b63c82eb28b812bcc960e8c306c35be05df86bc83e4a4ca07ee4eb523c5bdba715726a2e38ff938abf0a3a0232d332d4550ad6232c6218981dfc740d54ee770a3fec1fe0be2870214beeae3cfc9282d7790048bb8ce28ff08915e57940606b9ed0a3ca98d3a80b10cb6b5798fcaa474a101b197946b7017e57aee3c98a50ef692e2d7854120438ffe7fc5747e3726e89040fab4a199130ae6dbd223463c1dcb9457934a600bfeaf7f3b14fe5eec112b8eb467608af82336970ffde00ce02b1b2804606dd3355b37979da4a1b367d2083951ec47ebc29fc5b237bb4700a5975093fe65891ff2793564b1059276128dc8ddbdf96c3b19fc7eec2a460e34e730aa335f50e8bee4ee25bec7088cc50d58254560591b27b9a59990e7038ae2a77136b6db1de8f45fcb249bab3e35001b74561b8abf92ff8b6246afeb2b295bed456b06680a38d4a3edfd54040ff7d9f9c4f775ae5823f0b29c9f63c10dcece5dba96c753756def64960a65d2a37d81f2e88c517f51adc91a9ab23e78a7e1a774f07d63f2b14b7390f1172a1c30ce8b0e3966a2e5f4b5a1051fdacfe5930f4aa62d0148058f49b1fbf9883f01b2dbdb2bf06e813143ddbda6379980d0c050e33da57bc0bffc6b6ca7b32fc23ad5ab550b0eda0d3fbfcac71bc2f3b770f50d478065b93cdb277ad74495cf452a80a1cc0b1936dfb71f3ea65fb47599cf863d4909e2945b1b269e5a6b10a5fe17724d34881939b039eb3be881d40b0563d0347a4ef4ac293375a7e0b18e0e0d6a9bc1471645522ee0c4cbdae29f6c518ce2e1a3435fe4a9fbff607f18c81cf12a7a5bbcac1ba6abe0ed59e3e9fcfdf98944f24d6e1e8f301a064d7a8788ffda28e0029c638e84c4ee90e3df423dcae0ff549a39aa7ae12b652159b063280ba5400b9a4b258f525f122ec8d9f6c2ed9e36638fea207ede70cdda786e7ed6bda96ae7e57019c6b56010ffae68a68174e756e4c606d3f59e7b512b8740479073065b7ef4b31ee5e867696560ef693878cc068ccf236078342e26a65b4b1cfd8dfdd1bc5c213e06e08b878c52f0aaffd36c2ad6e8f9bd657feb7d69ffe706c4d76a566b6313e696a2bd7d72eac39c5cfa5a2fb2635d4e0b65bfe98a26f9494434151ee18ff4c07f072734733cca4435e49ac360949d9288fe606980e6ac2ef4927dc2baa609ccf043646547c50d563a47d179a698bb0350efaa61f8de1028415bd311fd50560448af833bab93f22638dc88cf253c0d6aab144be4222a82b77588d07690e63532fc83fc386d0047ff8cbe6c390b212db4bcf4044ef3b6388d3e7627ac8af53fc470350ec9834eb3434c806d085df910971208649f729a9e74e5e411015bb880a2fea2faece60522faf46fa325b055305eb77200efa9f29ade8149e095496045c41981d33c28adf5e34e603812446f678913f3a97c705a486705c3c1dcf21d6e03d816d29d7f566ad2d654443724a561f6867c9a6e6177b35bf273a4f7fa0f24a66794eebb88cbfba6705daf47a061439cb7087613937a2a5eeee5ef48e3236bf64b49a9650025f0b27c04096e30de1de7f959419b74878a36db121275c7b4b11c5d00907485a60bf826103b9f2205b40089be4ad2fdc2b1ebaa25bef76ee2ff202603a5ba86171d364a43736fe2bad57c3076119905ee351542eff0a40e5e653e3b2a39bfdca485ff7f3df9f27a2b3f5a5b5773195cc1f23f8121be6139d566b269800eedb23441b663c4a0d460a54b7a91ff3f253e93f34ba285b3a895da7a0417c4d5123aa03eb0e780fc355ef1b6041513c60e776ae36ec5fe13c128a13f2066b594cfb64e25eed03b8585308b89bb0046af291581befad6601720225aa2ae8b2778a899c767b6ad53869d447eb2078a5d0f2ec17c9b9e09651042b6712b9e73bee5c69b60cb378f0f355d925e3527dc23836e4396e47f600e89b0e0d9fc544405d23d2402a31583b339a00fae7fefc3d4f0cbf271690f24ace4f9659c7766c0f2e5f77a5323728bcc4bb040cb25e0a9b5208b12f38a7c6386095604e4614dc71657c53d071b27933e543cf3ddf43899969a44be167b8872fc45ff4c794f8f6acfa6ea2d316e0847e94270c58d9fe319f639878954809a20424fe6c434feb0ad6aa86dbd83484b1b6a974e2863a0b67a9ee82c02483109a426611eb6aa4f522aa02db1308409e3b63c5b4c8735cef998a179c1646a82c8df8082db241b4b2ef3c65ec30aaaf7a30b2da3c545b8b366174e68b1d3e45430dfbb987312064ec4a682b8c62fbb47be0cf631032cde574c5f71906aff937ec80c87797a70de0d60e9b4eaf2075316966ed236b1a3f1c7233e784802851b20b5c384046aa70925bdf8aba957a3ec38d5f14cd5916af83e2285821c597f9e02974a7cf67b3846eb63bbb4ab2d57b4b57ac4b1051adbd035a7e5f6c18dd97d3d5f3c7962d2434519a58b6e076656008b6ef9abb425ff029cd18377a9cf3155ab0be09ae67aa3f6c947d2344852acc7fc299d64ceb01f6b0c50af7abe830ce1ff96be9ccb8f7f93374cbb6b3807a36c9ef4495f35e8d92ff7e79827a218d5ad63f6c099005d212ffcfe517fcf76162d54c1c7091ab23b9f6d5bd394b1ad0cde54683e706a00e5f5a95a6bacda6ef4512e1d823233e9d56b9451f93c3fd87b4fff5419596ec163b2b2d6c8030e94c68783c3fe2b0e74d24934a34863d1d89ecb5b8766f055defad7edbd07b97d3e51de0abd06c2c175dc95db2cface8085885189ee5a2574a7dd19b62994ed51091bf83cb261459ae29a16d24b2fec89778dafbfcce04649030d9887d02c94ec9cba5cae869a8ca9f06515e043648d9e232fb084dee100da66248b87e905f24857cd39ad5c83482669b08538fb27d1b2e31d544d31b1c99c0f1895f767b52a87f1ceecb632b44ca30064c0ab8b0403a98b07d26b7aa1b2a72b0295c2c322680ce64eaa40c560f405016a6459d55c88bef8cf36cd416b7b06901abf3e0f49920570a48470598b294ad81da2d629c7d8087e4df808bd6e10aace2665cd4cf720441d393e87101327d928abe34fb9aa7f7671424dc977b2681751bee6da52fa86ba9feaab3e41600c8f8b8127d0f300252204c18570a1e96ef3607618f4ffccc0a225f9ae67466f34702299b906cf9004e0c9677f41cd2c098a1371aa9d05679fceed38623f99ad374d2b4b9e905fdb0100ab9b92612012b41e36c7dd451dc83d26d5c1c4719618a1004d86aabf9e2558ed4eddf0c5d80948bd2a9e02d37f75150aa6b69974f1fd4025b8d47051d91b386cad9df34b5be77e197726c87a7b453b0fbac1842e2848fd33da39495dd5e17f84142c0cad250277f1ab5d93f51b0fa064f6b1bb8a8ab863131479f626add1b0ff05409c44f8ba3937c289c418a5e5438a57628bcf082ed43209e16262751e25b6fa6ca4fa574028f4c0755f38f8a32c1d98d20ae327ecaac1892e27254883cb571933046fe24dc146f317e7d34ab387903ca7c83df0c06bfb2dade1dad954c9cea5115ff4e9f6864270d53f7eb115eb082f56a640b1b5d4f090a8c1039f915fde9aa0b84ee788395263e532f0d9b0cd93ae998217890b03416b0b90e12d3662703de978d534d307b988074fa49ab642f40f58b1f3cf84508e739b907034343a74cbab79c5d4c4e24166fb72fd62a2c621c904c76a25894286e574b51b874880941c44c7de1b703cc8d44118c22a0d4f88b78f36e4528302e18786bd80840c76022fbe2742ec5e8dba7e382cc340f33861c3e48cdf4b10c1645b11579f112e2e7e89ceab54e72c1dd041bb021d8327e7df54f7f0aacee4ee0b32c8bcb09f7e099af8a13a71bc3257755ff5dc2052d2fb7f8bf810fd2c53b95d3ee7f6d798d664f748b845f06b87d1ad144497e4637280eec11fc9dc3ff1afde9b8f9d0bb7ed7e850a91b4e137ada1451d509a81d0d1afb031bad534dc13532869fe83d9ca7f6e0c20166be764f38a19c209ac67c50cc56530b5ba9fce98816af0bb8489f120d9a5a46fd2988b6db20fd675bd1f1f0db45cc5e0a755d50718d1ce92c634eed2bfd7183c91f5229283718406df90376e03c2f9a017f6c83adb4f11e49b8ff43bdef32801861f39df22a4f94d224a6618355548db062c047294132d1b07ff36d6a5a0df6393054a908211a1e4000616a412d29d671ac072b04626f6b25027c8361b9b2d9a37cf8fd406d2a5fb3cf060a52c32947f91182b40c9127ad2546530c600359e669c883187d315040d3bf0c4000d7cb914ab56ea1f7d6935f9b11f1e46382cda7b2996363b03310fac56cee88524ca030a0f751431922bc27483e82340f801b3b1fd04e6eac6634fa7fb357533c81ada9e3e22946c68f0d413e40928245586d96dfc8e25023d00ca123ab2d11bb35fdfe2d928ff1f7486d11c84445e218757675962d08ffc82a7a5ed142a185c0006f2d19773350513e35032e12910bdc4a24a2109b06adfd80512f6980639f60ad73708b691098160b1d8f041136dd00e47e39fb86247b25f4d9dfadcb815e356f5c88e346a4efdbc62b7d7f4dfe0c28c49dc035d4981d3e4877d770690f3f43f8fcc2a70f2d10af6dfabce3c1821efd2ae6c6c79ab471d83c174494274acddb8282920e1e0cf91d7bb8e6ddd482f623552c336151870e5691936fcc305422669e2ea3957a83460472e377cd41bc1efabd70951592f3c5ef821adbfb00ed56575f48155acdb6c24e9b662bb53e0b288bc46325620bdbd5e6b2ab1b58e048f308132a3102d8cca30a473cc30f4524e4d4546c3027dbc0d9170e0bb93acee2a8d2ffa7759883f56ab4317a854f740c46a9da86aff34cab314f1f2832b08846dd24a69779720331abbb27964b5091b7e31c7a10ad88a6a6f7214cf0fc0eb203a7faf7a8f5fb179473963c49a9e0ebbdfd9d6cd6d0103dc89569bd418216523f37478d2ce88ef5ee3f6a5ea5aa66c1f327b0a28dd829afae363794b6bdb31c6c80b4e73922cbc2a1f8764edb356a837216fc75007727bedcb986aae95c1e93208d66960c5d276ce11c2fadb92574a2811bf79632a9164606d0761f28eaed493104addcb5424db502cfb375e891dfe2c67bcadd0ccb5106ca512f9af034be6b30a779c6251fb2c947b5b78627fdbf97b1ceb13b783aa5c6e7d8e50ee4c17d1eba2e5b9e32b5fa77680e1a947b145a31ecceca493632d912d27b6b2ff15fb9a8150a8cd1ea8ca314e8e99bdbc06465fc0468a386b1ba06e17552991c556e31aafc7c806d12190222079a2e357515ff891ec2b4180967e6ccb4f44222f5858985169433fed5892ffc5bf7dde5b032db39e12a8fca57464c3540cff6cff4b87a174d3d64df50553c434d9474feb812de20001f3e7a60349468d13aedeb67ea6d4044681ba6d9a8ade6538f3750a78674f08006b50d7c011464a592932e197d01b0af87097264b79b3ad5682092e733e11fde55174a34115821ac512f895164644ad1829f851289e902cb5f3801cc0a4c8ae6da9d639ec5b9ac6ab6f3d490387aa3020668e64dd074e4fe9b409e352a4b58b07209350fb5c333be0aa4f7738bf3a140800ff5fd23fa78c40edd16fa18ba7afd047d0ca3bdf0c96134638f6b8c535435050294ce6a23af9e5f2d25d370d14c8ef698d426e464c0672e0305c058cc0946b0001adf04c5f1bb99531f40c77e997e228e778712bdef5c2e596be389064ba83242612ccebcbd7807a603ba73f8ce13b6aa893c7fb991a1384334da06f6316b3adeea690561e7611bacb116275defdc40fa03a2325e39d045882cd684e15c229fb2420b89fd74fd2c3310ba505845e0490c609cf20ebee7f151f0ddbabeb5e6748a9b13098a0a929cfb1bc626f4af1931463f8b190769380131bd66fc39c181fe07306c889705d8f21340ea59213122599bb42d2a34468e28bd699d4b46419499918cb2b42642d3a4fa090bc2c43292f0633a4eedf0997461b6f8d5d4f4ba98dadd575bd543bd3b488b60e8b262db43a3ee87d781631bf18b5525e971faebec95f960d2b478f4d45a4352d4093686894405758a61847d3aca5054ea400a86d680c9223c54c83f28f6e6263af40275c288d04aedbf1f0d512028b01f3d5b76f122bf0337e22f075eab4a640c44f19fd7a84ab7b4632d81296611f0acdc23fc4c7132db0d1196693774f3483eafa5f3d67166f4ea714db4552b3ea33d2902dac6b36205375b3658c7f3006862ad4a9e0c8b700b045171e18301dbd50f71ad4dd23c2b8801da8ea6c2193841f9d1177f0e362437b7c44abcad06bb4bd59f35f3ba4143f866f4f8b93c3bbc7cffb52df2fbc2d768831621b1ee111dc8220c81471a41a46df38847255deef8f84a4e662f7e4b22ed6134bda0fe62b048024ed553e416b2e47d2a6cffd54de634ea9a37d68ce82f5e8541e59324737309e74d6b88815a24220bdf90ae401b476b8c6c98d9187fb0f7c1cfc79fb2a79f59575b55fb574eb1091115425902493d0b283cdd0ed51a247f501d24763373243998f19b3b03fe85cf8a8b39a631e3fa0b7d3d837566fdfd7a2c420723aa51fee62c80d618d0f79ef6baa5aade9fc0f5a4234108df1b039d38167ee58f1f9e2d49e09c06840fa6e44404d5364ef8379b4b884f9b8114f59994ab646a4fe54033c65afeaed0b31a717eb25c930bb51833b8eab4757afbd0078e08305f415744798a21cb245a2c0df3138a5139a055bd0b221665257850ca0380ded7aa2f1da493448b849e9ed0294887b9c5319985e634eae7652de49721144999d68d29431da3a492c6b13668c7eea1fd804945a26ab3716c441a4a04dfd500474556237b30716162c117c2937d5982f6c99af0d9af272a0dc8b01089b5468d044b761259ffccf40789246be3112b2304e419323b3ca813f88e0d1a36068531610879af748ae7491c3fb278e796c0eb9772ee1cb797d1061462b73503c8f0dec6429e59b58b509aa16b7e5f7a819a0252653564d960b3de08e3670169a1c506b12e0dd16087740ff77e7d6767c3604297edca0e66122011465e7799372028bb168c1bd0290fd7e2ab5f25cfaf46413ba711ce6bd84acdceae46f0f3ce56f5e2e5e9637e82f17390d35d04f0cfa20e81860e32410eb3c5ef009da2ba3969c79cad083291d1e4f447fbe6a83d0b11b1ae94557db9693b8bceb28ab74506583082d8b695eae864a83b9f5bdd0a3593fb40e82de1566b3662212ab08bfb95d18241cd3942c32eb99cea9c7c5c6231f87b6a4cb63972fb0369d3d668b6b01098201547930abd701030b2af9b13640a41b3dc8b5076fbdc34db4a423ab8fc7024c56caa38be860267ddf218f42ab76c79d0e64fd14ba2a808dff9b8bb5244fe96c84341b73649fdf2ce71cd7581fb34b298a78f11a7995ee9c7e88bdbc054e3fd5557604b4e1cea8a42c86d18406472434e0513a87c33f4f285cba2f752452e2431ac2f718dce779491a26be2914d4f36ecaba8adb389176fc24b3c47e9b6cc65c48f5a968e041fabd5b830903738de3b8276c669c7001fa3d53d35067cbec8b7298fa3170a1649ad2779750aaf5a8f01fd874be1929fa5c231385364f272a565e83c6dc00b7f4f61f579c2760df39020b05815328b5cf26eaf3dd6e98c6c9dd3a271e16a6046c5c80d869273859d33b3c1cc556228cc4ba3cfba4a57ae710d812ffc8bd80dc3451b582d98dab19c451dfd2aa529bb1fede98f80c04082db8f0a3734adbea864e892f6242331f9ce355e356f55056b9643208cf3d4fd2846fb015b1142a0b15dbaeb539d7ef5900069825b700e530a2dec4d51a2b8be5de8eac432153db966c984f35164c88b9c0a698e907ff82a9b185f4a0e345feae08727ad02892ea460ee30000bc818a2d330bcae5c4717f054be194693b733157e6a6c414e2e03fe4250e39697523040a8352e1dd4cb1b0f053a834c16b294e8ccaef9f3cb5191639a6bd54435943dbbb398f4b0f4c51293a678936b0cc4486953bea77645e0cd1e5e19b09bcd1755ff5f00a9e6f4ea089edcf246a8fbf3956a51324d3d6a1a023f8816f82086ed560b0ec9b5b898c49a19cdf56303a2529243a4ddd325dd9d0b76af492cecafa07f7bb1a6b1a4a9c37a85266c9b4703ba0db122bbb7b7ce27b0540e9bf4d50348b51f0ed50c62d0c0801dcd725941ecb9a9b518def1049216500362fa7d8c908b084ece5133d0f8a6a45b9ae0e0ff028f11a77bb749e57788657803333f1a1565153284d0acddcb7bc30b5de9f7b2a6417467256220a9b28686f31bd733aa0464702c7d7a77adfc4bbb4b89ed675bfa1418d7183c62dd8569b7cc36fc893718ffc6e857068cf5e388b6e319b2a2277bfcc64bc6d8c5a6e9ebbb9cf468f997cb26233625280f893d7806fc2e4d21d1a4485068b164fe06ce476afe17728a843ad5511a3002046a95da7f6f16a4840bfa3355f802836dd4bed426695b41244202c2e44c0120321e09b8c8430cc407c1222a98f92f47cbc24f5f8ba1e91bf3d2eed42f63f914505782e96c28a93db6a39308989ef4f08786f382cc5c347a9098a0b5ad8f4d8f8744f042a0b4816eb078c524efea8aa5989ada459990f76423044ff69c5d81f297a836d458404a6581da0219eb505f8e4ca098ad418c9db26cadd087388ede5844794710ddd4e2d84a3565d00ebc6eedc86d5155d0c6d058dc6888ab874e367ccb5a250fa5ea63cbaecc92554eb355abf71ac5e785b3c65a531e12e2eb4168c2f6af7c2d56a1b1b334b859aa34be2d4d946af384428f03e81dd1b8433eebd16ba737ebabf5399bb5ba58b2d295c5894307009b4e3d86825b8bbeba3d8d29ca3a0670b940fe03c9876ea3b8ba8f9ca36fcda244731de8ef9d63fd254ace177befa27fe3147ac4db03cb2b39ee0a9c444c65621125e0337034a989ce3c1a7fec6f8120a0d3a8901a321f6e32b1cbb36f12395b3e65da9d2a83931ede105dc84232c0198010ed7deb80297674e840781f1d2bc6c5cac35d3e19a04c753c371c59f3f50774c0dd8ed1104d537b0b93ce90e0db586312b084ad5a0987b39482dfc8ff2832df72d15ee7e8558c4bc85cc6f309b28c95287114f0f6d8f729e8839b4a0a767517bbb0724dcaa51ca752461408d3f43e26e7b03c9c0eebad43c23178ecbf01590571d2c369b33f21fd9312e9a22f19846183645816188ba2a998e902bd650607b96c1a25378bd828ca472652dd8a43a75fb75460f3a80544c205d6e1a9cef0936c993532ee047d6e22642a53e8d6b91f90d35f99828b7dfbc5f0f09df58ac9ecca005717a98b602180ad5ffa349bd87ee17374a505ca8897f2f5cdc64d226504b140d536bf1a061a1c44eb986ad265229291f73cb016a7afff6a50d94d42d42393ca6bea6e5dc8d7f808bd1ce30a44e05e65f091c06f5e59882fbfc9ac8a44631ed09b60e2113ffd3cae6074e3e7fee78850031ac14bd6be02ff68c2e8a05708d7f8c9d06ed8a2c44e81f06206e864e356c106f533496a113b4df01b8b2660d76e9a2dc95d9b03bda4d8c1d0184f5a9bdb8e00db2838059174e6084af297602547d0611a3412d27456d31e354041163645104be896d1c54e3eaa1124f5d6842fced8d43221d51131067a58f3b061d4e71b83e9d3686273f483bd2cb929513bd9389d4268c46213e3512e931d2e30ef1dc04ecec519e4c7557d18b07f75fd9105ebbdd32379a2774eb05c2664f6018281e715661222d26e8ce34d2f93c7856b9204492684f3c67780c722b517851191bd463debdbc0bb021537ac009239d87885b52cfcd596f6168745675e8aa216013c3d95b9627275a2cc8229d653cd7ec5836138c377d474c700bc918f781c76b720e6a450adb41721d3acbaaaa17790ff3592d37ff13d501501122fe5e86b0f037482c50a03944c8041cdc1bbbfcb7c715253467d6ad8777be71fce84bbee07b8b395ea496c26b7b9200410f5ab9702017d217acb6a16baf50d23477e2d66a633f5273873d7612952f520ddf068e6c371e6f84c3dd095c47ca6a6ab34861cab043bd54291e1fa43c7ee28cf1688b51aaff087b812a3a91efe0f10de38b695d91a715788614acfc5da5e59123bdc59fc00b5080f42ab61bcc4ce283768f2f76189b14f4f458293475e44c0b6a86dc9d93907d28dabf78c22175150a4b21870a6c7d5172eb396dbcc18741341586f6f23f2dc1d08717c05f3e6b3208637bcc43089561672228ec07554b6a70cb5f363605fb7f81fee121b581ac368381b0314bfa0b7ac8b7f1a223de52a766acd7118ec2c9eed47448ea37a14e9cdba691b966715fcecd950ed76f90dc170d21b05da9022370f9b8bd34479638922e214897f13094c6754292bc2a8bc5cafa4f10ef0881fe07404b7373deee45b33ce285ca2066320a6135100fdc52ebb70f20910e2eeb8c0ed5da681491805f4ff1d4141e540babbbe54b22d035d858f4fc20efdb038c0107d8d8df673d0e9340e7b2004dd15df4e8e0f256c7948981472d1016b634b24528c7fada18785f345424e34ed70fddce28afd15eba4bf7995d542702275db0538e552f4e8f26e757a732c578abfc5e4b925001c766bd1911d3a45ea0fe3dc3fb1bc125ac261b7162d92951d8694388ef093cc30e9a04a48bb0c47e436b18d8d78f0d8fa73029dee3971c1162e2588c7d5017275105d76df19457a1362ba5b2ff5ff7a83ca80ddb0375d7e79614a073226addbf21f792549935e0e587265c26d0370f40cda5692cef5f7abf51be49257cfa35f1819dfa8bf181fb412610074cbbe74dc10a179745783cf1081f3668f8ed5991dfa89522c29236149e8f9e3f83b0b11799d0e024cd2154867fa6a0908c4a94257bbcef3239bb49a6d906d32f749cb05ecf3412fd7d70d1339fc2054a5f268286e5add17c6149cf26b7f50adf7f9fb41bc5c61ad3a53c1e403ca75a3baa307a11a6bc452026263de26433146af52e195b41f146ec1134b29c09e8530f977f6d39540fffe4d43f9ba70e7dc68909beaf62452c2f991334cbf59633b77ce46313353843bd1921726657990d5997e3c0b14636a9471a992af59990530c2594851046c18e6b1a204431e218bf53de659824f632547bb3501d94411508d708cf9effeb03208d9d45a8e56366784cd31951f21acb9aa6110735358d511723d381a608cfa97dd0f30f13e280f152ada4402bb5e52fec028dc8856a78660e7ed8edc8270489b6a3f268d75692289a04407accd469d6a8718b284012537448a68d3d32bd35d56605cc02a3f0ada03b6e47a831acf10a44f2eacf5d4d5e0d4bf0506d83d2488b2802beb0e68235ddeb8e22935ab059adba6f804bc795e6bb4e81d07b34c0d8860a11def8a9a72ed562f0b3cd40ad769c9325b8ed6ca99de289b56cc7233de4ae2e206d826f6cac7f87fb76e327f5e6fd5f94a5aea2859d7da84168ea069fe5431a1b5c9c973ee55e65412d2989bd0fbdf0f565570932bdbb0adc86efc070e2bcb68e6263fdb1108c41ab82a81292fc5cfd8ce2ddacc4eb57d62ad4cc625dce069b668c4a3f3dc9c1c3b06c0eb514cf68068b29e726a0a93c7c8b3ab0f60283519695e26cd0511895379568103d0bbef948221860a59ee609ed4c07d6740e0582a1861f2d0136ce762687188029f83eb214b4349cfda2c3910c5f67ad957fc61d32a22d4b514c16bf59c2f51325c223b3df1cf6372fef5b43881ebf83dbd716f1c586909706a8abb9e78f0f504bef1ef6b61a1fbb6c0cbc411868d2e1c405813e74371e1eee2afdad28193bfb315e65a409c1b99c27c16a35b539a0ab328b16ae416472d69cb1edd3d3893dd9af887318275350027b601d9fcb6161a3d087517b9845fe06a89ea5183a21348e27b1b0089f0ee310f921793b115d0b9c91c8a6c760884d029d7eb261970401b81780c64713215cb56cf573025e1ba9fc46aff5f59fd511542cbc90a48f04571e613d1146b2ab924bf28ceeb35c05232d9aef2ce5cdfe1f6b3249e42e0c0fbc66d84e06baf0aa972e1afc4232dc2a28f74af23ea0813daefef4ab40c879dc2dd8d2658155cc3c61dc8032dd2286c4c6cb09ce2415c02dc90ab3b64e27a5e9d83925f1d8021e5cd633fb11222035bd16abf136f7a685d09c3fd7d5577b43887e294715d599daafacb10a3d39b9f50652fe400bcd46bc79ba99a113055e74618f55f4b689ec8a749d7f3441a6d25125e8c914ff55ef4c2b0555895c29737d861419001439ef57c79b3cbe60b111dc8ba9d2ac5309897ae28e3f2c91407c9aec9105c5d7931300f160ec6d8be8e8915d3ec3df47b30bbae60db6b6a2bb4d5215ba2e70d06eb8f795507ff4ac817bd902fe355115e6c54edcb5105c1be1b623d487786d5b14111a4fd362c5c0b6ee7ae495e1e3a201bdc28f685a632258e93d03ccb42ca00a5775567f44587c969ca953572d61d0dd41421b0b4e99874c2d4a25fdd9094366d54cafa10e30eab60b2f69a644a280236dde4740c145d9690a486b4d67a33e5a11354e1c412c448c49c0246a62e0eab3e1db9422c2e76e9a5b16d9901d304bcf96eae5871128b704492b2d457c14b4ba7a2058d56319e77fe2d0d4da305018fbd6baa9a96e68341a4658fcccd28b1354d5aba06406a7ffee99fe65442feb7a14cce72c8eaac90dc27fcbd3f7e3a460dc1ba60b9a64cd04b9e30e9b4cd1b413bef0941994e082faca72171cc3d99a0e0aaf650a59fc6dffb0292781982f24c50b2aff54f65e8d77296fd1935aff3a01bbbc5482fe9ff67846fa178d4a0dd1735f3951f3118d81f4cc0143b75bcece964ead264a30f8f10381d7a76718d1e571c19b0a399b0286125e099a6e3919a0c5ccd4405c2441b654c061152282e12599a99e84e17100e38c68208ee7d1a661dd4358cc89bb686b22890287304d490be04647cc724b8906d5f73f7ae27d91820b5c8ea67be1c13ffd8e0a02c4a871793920ff1a85a72e149cd1f4cb7d66f56c168045f07b2b183427907c94e1e39edbb177661275f4cf2e520ca0575e2964faba659368fba02f24574bbbfbe530aeb1c477d07912ba01e2d2dd3d33b898c9d1e2edc1522921a65184ad4ef0f4e5b1926b56641e664b333d44427dac9d2bddfa4da6c6e7781b88d7dc847cc6529d207e801c7868308ceb040b663440d050a8826dfd2a9382cc7e180f30a1ed81e9e87bb969fc92806391b7aa9a651026d8c02575bb2b05018f65e9aa0e096a1932ad5a18658ecd4848b32891d10520936f3231bc8ca6207060f7ea8a42287bde23d728b2fa79c165ace311d1f06d8cf84c102aeddcf8469f187b810af2a324535a0786d5931dd6671af3d0ca266d8eac63c7ba1288e261308ab4b5424191fcf523387a229a414ffd1a6eeebc1ca5a3ad8ddc1de7021489142f5632ef31f02fd63a5762f4ef3be929a076d68187c16eab730fb43eeee5e8d9434db1bb46171ceec0d599caaef01f39f5a93db1ebedd463a0afe505259ff8c5e158e65430ad0f44d0f6caf0f28db26f2559a5ca95b6df7cd6ff4aacf4c50232afe0cbf00e79c5c06d51ae2ffea1c2978258ce1988218e5da345a65f3b5f4844a9c52de57770f4e6112f4ba9e643808725fe865b2c04ca4a1c19d4ee40bc5194e2e3e6d56ee79c80a48273ed909eaf97cdda128b006b988866824c9cd5ae1ef98daaf4403251aaf70d54dc314ae2b3a150ef1199a7be7048677be9f3a830a543bdfea1489176cc112b3c17b71cd6a5a9ef281d4edfecba1a5ff1fed09359a622d1ff1e919b2835383ffec716f1a91d03013716722d1fa73f815a1e20e2e9d48f1b14ea21bd3c4149cfc8eda3b30055f1310f616cdaee60461877a7a20ad1dfb8b65e6821afb8cd986084725d0163c3497b1af9a7cb82f9fecd3aaedc4f5c8ffcad585fed18e0124a23604a4d496b7ef05305c1294b0705c8231bc3cf8c7b8ecfff9dfcbd547fda538261334e5fefd83bf73763b377993080c331f7c3d092f9976189a156b37329cf601725975cd1cc503a3e5032c9bbaae2ce664197d22982a6be5b52dfe77b9905942617bb7703cbec3a0b4865e7dc696afd0c54900990609904e4383e645de05590ec5dbafba1a97e00c2bb108372da63c0c6e7ce49bc87371b715a88d4c065a9a4c38ef595774e98321e0361e73acbe793b6872d65e98989b08c6378dc7e2d75e1a041274dc7a3efd436f5e018e12c8d0e44266ea2353546ca5e77bae8177ab5158b1d48a40bc7f88b59d845755fe39841c313197396785a8016cb5eb23183698139c8bc9ae4875461479387b0cb066a0e198966007b5642513a58adcf97de565573059597694fb2b2ffa9954c0fda4619d8179ca481ee50bd5cde0d21ae867a640de1318c5735860843548d1866a358f5ca10b5bd8a1640544d78aa93ab76c9907d99962a40a4cdbc660eb6e07a31dd27ae9f7364657771996947c04207734e9be061c0c925fb9469e1636f29e570d6fb7b28a01e947e403ff88bfde4bb8c34787f27dc83d5427fc32ce5f4da8213d5e3cb6c678bc8e76f0233ff20e3edcdf35dc2f8af4b912b68ba155ea0a95bfc6e9d962c01e17d5bfd0c2e6e8dd8c55489b997a1a61215e8ee9f5e112c091eeed6259c03473298736a1a3041a3f017aa0db11a572852c911b3e32733f063d13df5c309b81588cc488f492bc32035faa1601f9aab60e8c9b1c941bb25882f2550253b7f4d949933b7ec0affa1224de9f8b3d607e59fc53ce550c0a1524188c017c02f80c3a2e02052885914ce4b4e6113fb4820d2c0b93e9167faf1a7ec2bbf0b618a3ff2b5f6b1383baa6bda386d4a11894887dead6912961fa4e734644ea17fb03375073dcc083b2448eb0a5da91d3345ab77512fbee06d32186bbfea4d7b3954d8d33045b77b5a6d125ac8a3aeec24a44fbd90fb764b96d01d8707c7a6a4023e1a8552c86f9c4a83492c0df2cf66448301b41f90ef19a725d1de0b4170eab5255e698fe17cb9d4c084c98691d6efe1cf78695648733d5a275c18f5f5edda915bfdc65ea13565abb8caa7b2d7c48b968fafd7b16c5617c8d119cb9efd58c9f97b1c40695b98d7599fe102d2afdffc2da3f9de2523197d5b278d6c3d9765681ac47fd0042c9b65c8682330102ca0e108a5ecc25cf9e8321beb3ab748050469011006aae8e8ccad369922d30ffb42056d207b06941c7a9b4c502e801a8cfba07d771ec32983da162f58683100c5f20d083304a8d0184e0d8f873ba70acebeec02723431a569c749a6115f9518ccd4988571983bc5c57292f79068d2182f39ce108af3673a3e5815d5d239d731a6b3344a00a015ecb3301fd53b37d162a2b19cf532824fa12b16d5bbe83c3b0ff76d7761c4264b925881f749064080a52a3f2268d6e92416dcf159081539308c19fc78e64dcbbd3aa6a8d6dc831adfd8e5a168ba99e3afc63431ac277acc08a5f48e71fd1569778dfbf6b7cbed45c4f6fe19fdd518b689895d19e530a92847c65e871ef09bcb0a4b76f0f32000fcab5eb500dab9780f67d453d6833e9c84242f1bb1f8dd6541f0255d0b85443a24ee45d287dae4479ae7a8f12834d688d4b0432e0142483e079978b18823ecfd0c924357dfdb681dffc4be8d733b0a8ef73c7d8b020aaf1f701c1d4229f529d78671846ab8882ddced648fcef5c03054bcbf07beb798218e993ccc468dc85105100fdc3e99ef6e00e21180967d534c83f2e4265943e6f7e82da2e80e4859ed4aa92e4e4b7598e17f3e07b4bdd46125b1e0d2684072e985f7287577639fad96759e05182dc12fc327d23ea8728556422c306e4f1c7e44edcfac49abe07d99abe9550114586387910645c72090f5ce50f9b9620568024100bbf267fabbc9fc202509428147de0f087e0716f148c3d124131ba614f3b95a3f98eeab5791c4dc1d217232fed286db28f7ac7e672ad8e01461182de4e4119457cf985dc21ba5183a9433e89fab148be5931e41ec4b3a2932807be871b0ebdfa5b6d0cda40b24b9f4e749ac2c736fdd4b313219e485ffffcdb9f3efcd1df3ffa117f9a7b03bfe3fc6fdbff5ba95a772e7e8b79ab823c5292d6e46e9b9a9f422923ba6f879763d00df711007125819e30162b9182c2681d3c6e14f68690a64d452ff789fc136ba57521f2df1f12abcc84d1d4985889893a4df9900b5b35c4ca022c2ffaf3df3ec27cc579592655584004d166edbdae063c36683aee7ba3d459764ed0a92eab5485f2ef386f0b97cac56b0a231665b8cf8c80e1d6da206b61b624629c6f43a4f52d8d2abd0251c9de0db5915838cfa79810cb4bc9c50fbeb98e894e38876a46e05cb1a4fd9f029724e3d52c99679070420d5e7cc8c56861d647a924f789ebdeb21a8525926d839081e87fa71ff4a30547921243cf37e8433fdeb2f27e399a3121ebc763fcba0366be73011c1f9bd233f623f6d4e83f7200d95fea144bd95509f9b9722dab225fae36da6ae6f8b2501a3880fc80f9bc42b729e708d5ebc5b09af32bc570014aeed2cf654d517b55020d548a3971dcd30a957fe64068bbdb3dcb897121f4273c6190e63b4ad40f6d3ccc69255baf1a528cba2ad9638723f590430bccb6e2e9315d18f4eff43fe271750b54acfb9dbadd6f6a94d562f309ba98a0ea7413e15e3399f83bccd2107b6287a7bafb52ecc29301dc87e53a8f9e29c2d3a86c42dd710094eed0997d54586863c90b188bcab22d16411d7f1a8115f5bcd01b1a54fee0ef53581c323b31f2422bf5fee45b6132ba049ad6f07740150aa92b684acb67ea88a70a55746356065af33cc9ef0addef4b15564988ae0e57287a6ccf01d30b825a123fbf16cb757717610decbe47db71c5bbe108dd47d2f55b5affa10d28fdb3ace4c8ddf2d54655e2862f492b7187558f063ac0c27e507037941ca54ac7d8ac05dfd3b53fd622bce9ccd0924195b1c7508d4ca4692b0f30a5b81826fa68764c0adfe01800c9acdb487125c2f3bec0102f3cb4a1255c960268b569316563213bf15ff43fc5898ac43da1696040f60d6bf4ae3888604c7a4827fb4bf00973fe92c8ca3f1bc26fe5755c7eae32becae7703b0306889d4887786f3393a511a041aa00dbb2c140796e8d7233aa0da16cd6901cc65c47d80b1b3302426abbd339eb607cb837604d7b543877460ec2b3daa0160a3b4699c2e48b4ce3538af966209a5bedb4b47f9bb486c124aad0dcad5b6cacc3ec79c9b618465fefb43e4c7302e2d0f251411086ab94a1408e98a9440fef43555cebc5f6a3bee0aa888b0f928633bbfed3a1707534ba7fd51c331419a529b01ca6419c39a1d97675bd81da143d4d1a4fde29dc464ab1795edc2aab3d210beffbca5271ab44a2060811cc5f436c5a1136a3f09f4030e1823de4ebd7522d3c0562a882af68e1729725fa87fe8cc99d8a049e4c662bba2d8911afb0f5cc04e6df1b7d62520130d50eb74fef20601e6231bdfb3e4d31c0ccb6af4e629e8c11e9f89fda176637e2cd76a397dc510df92988fdccaef800d2c66c52b947329d3851d8be94a7da0e605b92c87e314d7a2b5273a85f060378858cb41267d9037e68ac3e0dd25a37caf6efe8ad72ecd885de37813f0f7144eea69a89a28e80823b3ae0499b1a67238c36ca32e6a5d63bcb8164169ca7a5232e8d96943c533e89a29174fad0b5c7ac1bf5c3a39252876c8b9005672531862c19396ef0492554b0b60f02e751c947a262fc4f0554c00fb5363548890420bb2b62291bacc0ab1d41468afd055d267dfebbdbaae5c6d9722d150c6119b71e3ceeaea7e3d5d3bdae44bdecc53811ea6d7b566fbff62c1431caedcf0ac8d3025da7f06b37ab96c799e8d5b216b0b8a5572f4f128096c301ac31bd161e69e0ef2351b793fecda07da65eaae4a6410f613dd5af980fee5384189c2944834dd7534972308695b98131ce3ae2355ca21ec079cdf26e9540569e8a17abbdff29d8e8767f1ed277fac975c53319f24bb45262270836ec3ced4da4413ef2ad5c8b34830cdc2210a02b1a38505d856151f65a80ae548a41c68599b46ea3595713455958ee9ff654f0adbd9b1859bbab113ea47a7fa130b295f7d14c4df0e6f54e7c0a668825bf0e2468536e37cefe4b73869f799a5bbadbb386b78c49db9c52dde3c48686a2c07695aa0fb8ee053c9f21813b90ce9eef839eba48b741709aad5379b8c347c1518d1ad0f017274aab7e0fc7739245cd122498ab221b29298b9c9d224581a7189383a6ea608ea8efc8c91dc7ed3fcf426fe9bb33c5720e5ed50cdd38e162f9140c0e0ce8920a5ff2e3a64d8fe20293cda6b1dc9f69302cc99dd0d3c4ed458716ac95ad00035fad542f81df05d11296e6a11e43ebd948f6b1d2eaf743732ebb12f317c2a4bb404ca71cfa58e9da57c782440df2568eae8db484c39cb3d1930eb2497e9fa615b430627dbff941700a4f86cd250a6ae0bca814faa921ef384aefa25acf2f5013862c9268fe4e596ce33fc6a38508b67542c877574f377358f1214e797e35f2030467a1a5573e2e8c6810b3c09fdac46f85cba7d0c929bf071ad9aabb141d2cb6de6cb74395284ac33162b709f460125ed077c769a2ef5e8bd971e778823b6a18acb609f9d685789a6b6cd7221d5df33b833cd0950457f63139ce621a45f744c20c542a23d610707036026a328780453330677add07614f4ac420f01b70393a1247a444a3a6b205f76bb15bba56839512d568bf7a8f440fe48a9909a4dab3957c6c570126ad0fa17579f0cdf51acdaad0b43a4b6594eed006842888ec8d68b260276ea0983d7549d6f6255625fe24df75497a6226d9118e4a228bd7c3280a9dfae00bfbc5f92a61c50ab95a117e7dd837786de922639686781e1827437e3cc5876c7487479dec2dfd04171ab2370ec95cba2c30da8d39001ec139feaa8dceb504f1b83281e9876ebe22d13e0961d9152947f25da42c9161cd4b60f820d4b12ebb3620a744ae6961d207151ab777d9886b3721b822d922840bd7e0d7a3a015d78dee3dc1e56dedbc9b265483809b692aa5c6817f64799f72d2db6560141ad6f9cae8cd4086058c9f438253f8d0d17b0d6acf4751dfeaf9f74ee4b8e7b1f1bfe325e40e9b4342efeb72a7b8f40e43e8a8e3943fc9d2ae5436e96570e3787e63f8a7f228cd809d5729aef532d62fa00facda0d6ad476b933d832b5bdfc69246101f6739f748661903f731c95a2e7d4299c58495ac6d8fb074fca98f8354148e51cdba3e7216af685787796aa4a593618bfa8eebca957311d2b4712eabb8b1642a7755f9247d3fecc718bd9a6d2bba3896d153a56ef76c6f4c447c8e3871177d4c8819b8a3a9b36cdebe89988a9ce7c4d8ee3d33cae917e93e190d917f88db43fc9542280fe91ca38e191fe224ff25ecef6f7c114db6e4d0ac23c4e36e8ca5b14ace224d7073deb907ea037159d49472ac82f00897db1cb6f86f78dd6c404e7af29c362329d0fdf822f4b2e70d6b61da7744f65343edcc3adcc418b6e0ec04351e6aac0d04803a4c11bf0f52a636133153777707617172b27b791d0c4f169936b5c12960b8eda13d3461c46f80c57c49b3dc8643709b276b067241340c9b253cb8149496b7ea2b0101450e1f2035a5d045d87a42f0da146f9486f868f62c6ffa9395a77d5f301110af90a56a2d8f5a2041a58ce9bf12f0154667369115bb3cb3a184715777f89e14fd46f08c257990b1b1fa3bcb3e1bf206a2e63c0beb013a22b465ee24661aebd9fab100e564ea69bf082adb9c797a8580fb6764bd0b991c314fa862f0fbf3c03312d72e4e72b9b2533586f460e98bb66810b3e346727ee89010c7ad1e95c3359b1aa5674a9a1978e5f93162e2db53948672ed6474a3b39eece6ed85b81716420a1600f95fd6d3570394f82662afac1bf4c97426cb0870cebd8c7168becee081b988213e85d119c0e51310505be8443a94eb647e4447f9cd0c44115678542858c80e6f44b6626268ee1a0b1c24e107d08f9babc70ff11a072e8ad089424d91158360ec1405af56ca57445be46174d7cd558d2576efa1986d0b473747f1af9095eb63c18bc7c30ed8c717828b9f102c67a144fa1430ade2091dad35bdb5a0f2306ecae8f7a0c4bc028dbf1888829c11abe7326be1143861acb69fa5b33582946b7665e106fb23af4e0f1f8ea387bff41f968d399d73fde04a9071c11d58e8984ca148a17e32290dacacb22f40c73fe40928f1ba14b64d09afbd4cd7537782b5d508339c0fa2956a707368f2e283cae014ec2c8245686e84f7a7496ed5ebe9332d2ef6e416f802080ccf58b4b0f01b38a34c8a21e627f1ecd1f737fbd018608c3007a0fbbb3d1f4d316fcfad59dca6dd1b78fe80a23890bf2615682eb26a62f549403a28af1de8c954c50e726effd1be845439fa866fd34096ea0268d674ef128be4d36980f9a3821feaf85b9424518bfa9f4073edb3335a7bd66a36c722674cf08f2040e1f84587482921a471d4e146a77486019d4bd22ed0395e8d5da7eeb0849172ab6b7dc59561ae0c67b97014a8cb37edc07eb50fa44701e0901467181b5585badc89a7d475e57fce099f24cd6bfee32d5982f6b555f8db799e9e7e6331a5366232c9710428c71bac468cc5f224308f4a9cad9d968030ae8ca5226654109804f2ca55cb3228e62b029be35e1ca2d0c1d3a1944a300f38f7b19e242c4328310d3c574bda888d63ff7bfcbd9747e199403ce8d1c74cf0f2fdd40c0934feea247351c7e3c4bd178ff7b928f52ade42862808cbcc38c7efa29101a7f0c8ba63224740444d0989f974441c31838ef05aaf8a84785565900e2562b0c4730cac51004c9409cb95647effe71c229b8761545a37184931fbc97f5341470a5ac0103069a6c5190e05696e6ea33a86b50e389240ee0f91841849b1fc75b8b7b125e0d88eb712a1d8af4ec2197556ee60a58c98fa8a3316fa2577a81ef95afe7b6fb26ea3802fde9769f672143f4bfc81963d694b24c5158a93510f54727b311722546cbcc6c7458c910c5a8ca21710d45e703b2549e013c545a6e01225b2483eb35673a1585d958e0e3f37393d4f9a779ed5f3a5d6a3fa48f37b872f6536679e9c25b7a5d4c94b259aba898cf3af7030daef46b2dbe91a2ee441384c5c6aff11ef0e7d2e4ee08b3c0077f8d8e49d9c63549cc0d59045d44922c594a1da0dd0db79252f592560ba01c163fba2ea4b24bdf719bacbeb903ecdadec231f88d537d46d43a622754af26b991344ced1128a0a5cef1e007facb1e83421222937df52b00c77a84d687aeb465880ea3dcd6df5fcf61dc9aa8a9c2a2752296faeca2675019e687f3f46bf3e8220c8d3eb97b6c7b521ba126675d1b25bb2ed90395127469ef7386f2ddf371af7be7eb7f255b95e0af85ec9af0abc6ac1aa61b1fe0805bc4cb9c49a3c9e14462929e63d366352d9b21494e292c9d72cc00eef3a7100e21b988d8c5bdf35ebc370d89d469c0421435cb44914fbcf83d18938955cd674e11895c45f56b30f4a86ae3ebbf7795a14de7ae5f94e988200949e18314340a15023f29ecc5098a835b57cf43011aa1cf0f621db51e0c3b7b2fa70f94f99ec04985d5693e2d24a625985e3d40414c4af40d647ceaf2de3fc3d97f525d264fb2f62f57510011f52e8ead7c09eb05cabea931753a2c8e2569ca024490f1bfa95174b199b926ff413d74c99d9b0174cc2a4bd2dafb06522aa27814a389337255460d6ab04b741520f81e576052e19f3f8cfe786e3c37c7adeb76af34e8e5ef897d63fdb12f9617ebc47e635e16a1f7c57a62dfd877fae7c69b1b6c9b67a29e588756b223d6c1e88d619a9dc528260f45e38a6f998d1e3833e890488cb39c27462e264dceede18a70f6b193b9b4e50624b61cbbf3da87aa8ab379eacf580d322e95f67f31625cc4b368998ac4580184084c6d5f84d887a47bd53ab2a26bae438ca59d81860b280e50da50e257e4cd905002cd7f2814a0896bc28daae04ed6dc393ed0bd01ce663b50f18865002b14d77fb0b6eb7511c3de50349ff5e5d88d65ad8d7f7b652bc8498d4cad234d34b6c4464b1f7afd128a53e97bb9c25ac4e047ba8eab3b8272a600914997280d94f82cc54b682515f5955a1ba82198891421d199ffa8d1ed044ff8c8c595886335e29801e8b0c81bc63ed3f1378a7e2d2ef5853ba588d777c54d1759ba5372b9f10a94275f4c1c36530503d117c7dde778d0eed0c5da0b40975fec041344b8a6aad261697bf7fe554b89c94bb66437656ee2ebab7e160c79ae7143ed29ed97cb4cf5ac4fbb954274aa029adfdfad3af501d196ad0616488cc8aaf5fb510b5c45a65ac4d121f93e14d1fb68218400fe0e0f058e1fb3a630b3528aee2dd7c9c5e31ce84195243810061d1654b800f4493088ee7d4c362702c66bd9374cd4c9fe277e0bd43c9cd9958ec5cc8b75ffc68d3f1affcc8796e621ee83ff354a9fe4130253f97ff292c6610c1aec85841564d35320b254523898bf8d573eed2e114ca0e2d278fc370ebdb00ce58a6b5c53023109523516397353065a33d6bffd519d969837fa125164566f967c48a87ebe18d44d8fdb208a1305b6dff10c719612cd1a0a84a8de3cde36703a4c87a30d4aea91f9d3606018d93809b19ff37e5879227b45e94d0a473c1383e02b83b8c58c41fbfcd0670a58479d32ef092a782343243dabfaab70bcf9e706269f1b251cf86574f1e36f37105965d1d1cd0f1559d56caae9d51e5778f891474e61022017bc2555bee22b47da8c9573b9eed6816bcc60e9e00e7afb3b617d7fb116c3a64438a55c5eea544218d13857c1ead6af3d6bd48dd773bf9a27f5f89d6d57e6fa3a5bd4ee55a288584fb4ab0ccf859fac99597efcfbfef894c5fc7fc1fc2e0dc77731d10275f8b190f266ffa2e4733d7504a89523e384d376f0a8c9e05a215a774460fb451408e63c19cd890dc8d42b684bb1bfc2f472da99aa6208ce7cbaae4c5495d89e13c67a5d355751b9de4f691ada688a956aed31b8a9e8edc0b15d9993050cd9e2c945b51bbe592f36e35e9a7d75a29e66c0241470dc22773c4886c6e4ad2e6d5d4ab3454d3283c32640de2a3262fcb2b9a2d333fa4d3937d60a8208650164dcb90c8329c3dea860109db1ca6059f214ee03af390e9eab341a91e04cb159a1a5c5f96691a1f6dfeb29286d4591c05109622564954f75b55d39aee8873e02c54116d966d215ce2b4b3adc1d3f95bfc2ccb3ee4bd2e647d8b28177304859b6246656da57780349a3a08f401871192f437c42aca21e1f2086480f072446ac871ac5a0338f32743a00a3177223df53e869f864bf8a71f34c5a83c03160b92bf0ef3072639e0b8810333aa8ff232d31bbb20df57a5db336e7cd2effb0f7d7da86ce4e5028c76e33ec59990af64630f2d0607a474c646d3bb12796cb11880c99500d88ed58230a732ddc617868ff4c937854f1111c734a4b719053c666b07b9f7e79f81b35eccb3d7bf280a09b42afa2ad8575361207144b05de4c44b60d5a309a15a903b9316aaccedcfb4cadec88a0902340fec8c20323575fdc9cb04ddc5cf2dcb4dc4a62c99b16b04510223f3b6fbf001c789c791b20f07d43e4245cc075e90d00e8244c4af04f46900d23a958b2b7dc7b6f29a54c5206e00dcf0dc10d35bb5293f9bfd049bd70e41525a7cac2aa95c566be906651c6b283e58685074b0f96168b8be547a5aeb3c2c54a17306c48a0e3ab9b4a550f0f9c1b510ff6fcf8227245910ee20b104f785dc01153a7eeee797980787e785c2d57790157ead5a95ec2e44b946ca596564af512a4f7fcf0f8d0f1dc504a638c1b885d98240986ccf0e269798e785dc07187a68bdffb7be2a1f4a43fd14779d39f72a0bce951b4496f3fed3e519da3f4a427e93837ee06384cd53c200fc803f2785698e0d08b380ec7b97d39acd9df0467acdd6ab59ffaf9c0777006dc361d7ab8227b6601979ec4d9e7b8196fab31ec1fcdf7bb9a6d3310fbea1b554819dcc66df6bb5ee76d017f39e4b6b59bb5968b22baccd8de3e2139a9d84a138e65c716fdd802fb6f3f5cf5f2e8046152c3fe5bcb559209adcecb410b1cf33e4e0bceb8f6b480436ecba129acb1d0530406cb574e9dfacbf3db8f2d7fcbdd7bae39ac5900f7a12776240ba4f0076bbfd6efef0d99ab66e89743106c5abb77ff08d2ee46155246b5eff94691ed169ca9fa46ec4615528673e086ccde90d12ae3f4dc9f5b6b08b9675cfbd8617d0f1019a967b4529b2f1e180c36df41b9effc461c37244629a967b4479a837986b84706ab3ade10b0dc26b0c391d831019a7faa260310f3c5f452e492f23d267008de6c299f44519e944da547d13a44390ee620bd49eb10bd56289541c95166a51697dcaa7d2604898d28c827bf05d4ed3f83cd176e9eec78853875afd553b2090c168c1d525251909a4d3171d56422beacd462cbdcf60a17e20bfdc2b0e7df0bce50d2ce027f2075c2448b2ef3673b04f28043a5da95a6ce94a6903f83820c6000e9a3a5565a6bada33ce106f612246d693857d1aab112483b461ad73dc21a9a2548ef5ae49a2f244d3f37a4e7603f3858aae6e131672a955a99adcc5696d04c78c7d332bd7f9d6dd23b076b49b2a76ed1892e58c02297cb8be887b74c20fb0a38554b318962d3ba755fed6153d58d1888ff41b56997b9215c8c2bc2c966cbdc34076b7962eeee9e2140307d0fcc55a926aec291aa51d54ed5e6cb8ac74a6dadc9bec8355b646bc7385e69e2aab9520be26181c3151616cb0e5fad30f1d58eafc21f4c2b2ad18f97ab5c2926cfcdc5189ce1b6158ac9847202ce703a0421bbd81ac01bff87063fc8f397c6f3f4bff7fef36fe4aa1dff731d9a70606912b97ed01cc57d9fe9379cedc655608f0823559dddc27e614b9422b60c2d1696b6e5892d59018325138a090525e5f4c539a7a9444271a65a74a9828e4c480cb2276c06286680cd10dbd3230416a3ca757895d8fef7b5fd2bcdb7a564655f79fdce4b27754923dd3c9f5d732802c251a136bd2538f499b5892e2f74f2f6a670441232f9a21f3eb6041264cf1fa99d08535bae927673b0f942bfd89eb08b5ad99ec03a2a2b1ea77998b0e70c007b3e7d010eb79b227785c593027677cf27caf2f9f3aec0a42fbde9e96f395bbe2973343b5b7e7d9b3396a914d5a1076e6efd7c8c91488c715aed7f72c8129a3eed75b5993ac3f52d676b4ed5524c5ca76a51c0789bb4e84769b443916be69e62e22f269d6a42d230764001cb959df982f2292939559b2d283ae579f1455f8976111c579e807f87aba90cf00b70c60364e3970c60c00067bef794e030ca20033873ff62ef55e0cc6d69a1a1067086fe75f922aeef02ced08f00d836585d0000007046f457860c7046f44e83df415fadad75ab5bad9e219eedf3783c9b6713fd98a257160511b96880ebf53a8ed6eb755ce83fa0eb755cf883bb2894c4b5a391296802d8d188ce16762782897244afd9e2ab0004009ce1b64f8234dfbb16a56a0b18426e90855f8a255be6ccf7b87c158548f1c3572a2a8f7a4f8be471991ee55134113bc841fa1211a537993ecaa098265e79d4afcce6cba9ce5fa1a1fce91d90f2267d327d8a3ed59447d1a7faf9a7c715678ba7e52a1f587a6040c4688aa4faa55aabad9e9b0ab21e580581aa48c6c50dc8560fcc57e18a9298af42cfcd104f115f492020259e1bd00c3911c6862c1ccc5622b1257aaed8fe37c9f6f7acc039dc806cf99f1c6eae2d3fe460a1298738cf00c5d6dae12bb33d1f95775091cb3dad3d5f52bc7da58338c0cdc1a2cbe44e3ca43ce97b4079d33bb843a9944d5924ab54c09e9887f426d2979e126122620754e7303dca1341fa124a3ed117bd644af944f50e22974ca502e662dc1057d525384cd54e3f53a1699f50f48c02c72868365b46e99b6e4c34fa5f040254eb020e0be6779e56840110baeb7b5a40cc4889852b6cb41d2998f0d8756bc1094cf54acd0938554b3199dfa21361ee9e3fc32bc2481b72cf37e144182f725dd7d159cb5d0a2e14d1992f2a3f5f345bd2a59a4cff95da6cfb9b55f20eda1ac246e51d960b51d85f0ead3733a35ca6b3081640135238429bd64fd5e69c9566ba892e5567a5365f2aadaecc95dafdbe385f48251836b39461b0523bb3e5fba8a5365583c18271038395aaa56a2926a91d183b60d8a498a49aa47660ec806193aa895c9e3ff50cc0ade05aecc962c3c59e7f572bb5e89204cf170f6cb64ccac1529e0eaeefae1fae87008203305f398dabeffdb9b8f26014f498207245982ec2d43d6ff7fe4244314a2db52b4c8a8043914b94e3abeda528c755b526726ddf16fd788982887e8880888288602217121ca66afe97c3dee23ffda9b7041c7a60a712caa3bc89a463668bcc717a92038838b95e22e5495f3ae91a19d7618c6591cb0730eb4907c9daa50f371e5be620b71ea9524ed57ce03055dbb5d48eafe29e2e2c725195ca67a1cf3b82fda3af4222d0b7dd784ac0e176e3341fc07c538eb385012ee333233814b94c3ba626beaaae174f6bb6cc9f2fa6a74382d44a4fd2a217e800d2a3c4d70e9d09cc646ad29a2f3e6bb5ea109316b9acc0f5c3546da5f6c2404418a72f4bd574f84baa068385c49ea99d3dbfb664872220348b5e712562220e1157a21f51e592e1d0033bb9c90393dc36dd4497f974ab764c51aa7184b6cd962f5f8c2fa68fcf2121be98f4cace6c995fc249d02b3526ecf9237066a596041c8a5ca995d9cf16f92a6e06be35be187a609e21de323d7330ed815581c395d003dbf34db508b303840de6cad3f299e926c2d44fd52ecd76bfb647a9d836f5924c676683f7b436f784e0ee43918b5b028b30574d9046b4abc6d99e0e7fa8b14db5e8e5aa29fae1aa22cccf21b764cfe7442ed316619ded50e4f221beaa315f5199affcc8cc57f2e7734b7cb5d1b2f015135f6d7a08b96776b826dd0e5f9d5cc3606d183b5c35534d524c7672aa06d26c7b06af08963b14b9766d8755b69dabedf997005f686d5430366ef83e32c450b5cc40034b0d2e00903123003456540480b26100ab97530d02c0d8b881563b5d46fc992172edf9272bcaf15598b256049b2f30b2f99a0109228ad1f67cd111d10c47261415d953a4b3e7475b69aae62bfff92926be923e98612672390517a6308525b166be6acf1cd6189bf3554dcd8cbe6ad931fa6bd239b9d9229920e1944e3aa7cbf8fa31849be8104ef5498738f51072bb8ef209b3c8daf32f126c843eac46d81e6328c72823a4368a2dfd694fcaa53fc9a41fe59317e5d187b2e84139f4f27b8830a5972f5f1186f4f2dd26c29cfce8e5cf9c08237a197af954d68debbcfb79be1b48911d65ad2501d8d1c8133b5b08b94f5c11c6fe7cd0f62148067b3e88fb10d4853dbffb305464cfff781f8a6cb0e77bf6dc0f4560d8f3c1efc391157b3e073e122eb79190428b2e9fa739e526ba9cb83c2218ff973229f7105d5e6ee3b399135de48332bd892ef2f109b6e77f4e5c618d628727ae3d3f49289bd8f2bd1cfa115b7e9743bf624f28b64c62cff0eaec69b91d52fa328a3d2bacea9062b1654a3e919f0f40347a2331bd9939fe9939b3e999b90d05dd861661be2d5f5ba65c1d75f65661b41ae7d4b4e6728322eac8978dcf664e486f4e39783bee90d2a47bc20eac841e60b95ec7514ff88107e18f07ac9e5047b69e9008bd794210bcd09c949ad252f26ba4a4b17ba38c6195ede8e56cd4a376fc18cfb54937f0a30d4e3f4481dba3481404e9a35028140a8542a1502870a324d2959148596c97e19ce8e24570c8d560c9be2faae8365f50aed9e2ff6f32b1509605e620493319311cded8ac0287a82034a81d5d056e940bf59a2fe195c54833948b8672b16c120d0938bcb10d045b4617fb753bc5a20b69c6dabe371d71887625dd09ea30b7f717cc420cc16d8f2ef7c19ffb12b139195deecbd805c1e6debb7f3d3fc5224cf720254e4448b39c044809d04ea737d1beb11b7395577066d3b788abfc51f9c65c1bd5456a6ecc57562f51c45755d7e02a97796ecdcdce04f9936014317e4a0d8e321920b1c36f7bca875461be442e87e7dd1c656ebc6e04268ab8c00af302295c3096b471c8175e395e0631a10a1c991b2f37822b90a0453b2a7001059bca7d5eae4c9cf092b91d1740a8c95c8f074eb864eef77999c3d1086248b2231356cecba64fd2c48dc9f3795935435e01451397aac6ec018e327e039913663261d5f1b29c89e1092f998b8770858dcc051dc1053832371464072f992bda81c9dcd1e7654984255e32f7e4f3b2018a00024be6923e2fcb2048a125734b9f97bd072c09990c0dd68e4be6c67b729818764ae04cd517f545d0bd76cae1e938c9974b88d0646a64bcaf91b9f944c40ea4168226ee185dead37835ae5d63665861bbbe099ca9f7f479595ea10a3a3217f50428d064ae8a184660237357bad0029acc65f9bc2c8ff840042e99fb31d891b9f9f3b29c020a5c32577f5e9657ecd091b92f3e2fcb2884a02673539f97e5106a3a32574719a44f194a329850524e5106196440a9acb07cd6333cf46b646eae91b9997b2f5f30c77dee3e08e6f07ed33a76982df3bd1d60ff5a39fb8139581da50c27a5479027f4c0a65a2635b50f71461de4e20373d3b5d5f268409f46ecdd000b41370b8bb892379616616af3057cea740551453fb4596c6a77244be2b86a057125611055f46528c58101f8d9e02a8a2f6ad3ff4dbb4d3f5e14e050b6e8cb96af7cf8cbf7547edbb5296d4901c7fd3df81e23b8c30428309bcd6e8889309206d1853efd1d5f640d640b7dda32551d4a2740528732079bb66ab80afcac0d7d3ba3d50657d197afe8b6c99cf982ba914b7e008115913a540ed954b6a9924d7fb3d5860bdcb0e94b2054fa7cce3d4eb897ca00c75a4b8c31d6485981eb31ba2ada80e3fdc4546d010ea5a4d4622968bb46b965956d3247c18da930466bbe5899538da24a3694a26ce68ba976aa9d7680ecf9a726a81da81b5fc521b5217b3eaa07cab5029910c5634f161376886addd4b8d821eac79e4fa5dbf8aa74e3ab702b2151e211e388b8c2335745985ac46578c5b0c153888104136468020423126d907c481953b8af482e920e308de443da3865e1241f2ed7819f764e59b89fb2d82e1d6ca4174c78f7298b18383ce8c075cae2a4851dc3dddd3e937cd2c2a9cc394ea97b2f2d1ca1d2734e86f92a3c699183e42117e7c260bbadbb838a9d98c415764254cd15d671d53c6971717c45725d18c415490713dbb8135c5607360bc15a970ebc6413434ccb42f85639c7555607ae30c7b2107c95ad88aaf92fa428c4162ef6246971c59e242b807698637bb3a4d7c633120acb063924d28c44b255569550755e3bdbe511615c0971457ac2f29036304dca98bf5d1e7bfe25bde6939ed86a251bdab6d12a91b822bd6677d3484f905ed886f80af3a5cad88f42c8df2a91786b84d9a8af7cc5d12d76e4624f29ecf8a0f88269b39b317ac4c071d56c451799f3ebeee097167adc9d8fe5abf0b66e936f877f6e22ae3857dd0ce47542d417e24b7dcd166f9eb81061e29fb688366a1129e3c4c44f3ba75a84a911c66fcb5571756d002bd9ecfa9a2f98568b481b78e6d5fa9a15d9d3cadc9739c64d9863319208734c861cd50e732ce758d6c9b2e832bd9c245bf11f9499041c03c757b209f285f49a2d4b88394984d97e7e36226d905c52462ccbb215198b3d3fca2c857c644fdafc7c687bb673146ecbfbf9d7069e0e73133b44c592f01294a4d77cb93b93e49236728e94313f9c26bd28e9ee5019a29a40318172a17ea05ed165e6970d3290ddf2556ee293ec391f75b327a6cd97d31762cb4cda386d11654c1b2e8ac0b51317a4b0e7df29258ddcf3eecc971ccb39d206b609736ceee49c4e87a72fec896db89fa75a886dac0fa26d22ba4c971c0b02b69136ac135286259299608bb0e78759b62790e8323fc425d8f3b92460fbdaf3ab8eaf6c7e4597f9bfe5d34e74393139d540ac135d726ccfd0be6acdb3f9525b3996655ad8ecf9f9485692693688edf999896685398b1914769877f6b42c296cd064877ac79ef2e5ab10ebecf9831dda20b53d1feb4418fff95b8489d19241843a2ba6c859a2024b2d1c3b2bba2081e78eaf42ae365f246b32d9f259328b1b53e16a16c19e92a22fe5a8e71d9cf97c8c73ce09ca8f57059b7e38b70c38b3e5a38d7b2365d01f9df8cb80736fa48dcf8e94415f069c79b2099de39080458f23430f19e8e71bb7a89a2ff38caaf9899f1d492b28741a2e5f9d3c7d1a3f7c357afa345ef305e5e9c72201b8f1d5117f3981cd88cd283243e62b19c62137d850430ab5c319311fedcff6ec5b069cf972b2993aac3143f5e9f04b3ac4249d6539a3e4ac335be88b729e85725e9269aea28f73cec255f43f39d75c45df933313575196dac966beb034f116fa230e65513b55c3dba0ea743863480d97ae3f6766969aab98b0ecb88a4a3a7fb8b093d8c910579d009fc04e869cd0c08a93c3f3eb6cd31997ec394f1973b38506568dc5255e13b7dad1155e139f9d4d6fe6cb09cc5be8531c9ec036fd9b4d6d38ba6287279b13d8f6610a13299de842976c21e80e4f362c3531e08ada57060d061403784518f932e06001038a4d67c880b3e9005ed1858601c7e8c2daf3431a589b7e3823b6e9d3409f069b0853377d19b40873e9a774586a9b0d4dab9b130fd917caee767cdab2aefaf673d4b7bf691d767b1d559fecd7c85c60d21e2a87b90bc8d843dd6c7d5b23c2408000f12502718b2bec39a510450f36c036b8cad6a8536116475e495d522a4daf26eed4a50d1b080c5e760c37a684e058041c5f3ee0c65468626dfa7703dfd3e1f56e40eda65f9acd97d353d40e3b507ffa7b65f37b307dcaf7507a94276207394e8ff244a0f4a97eca9b3ee5fda44f53f390f2a63f559d23a5547a942fcdbf17638c4b43c09fbfae08f3f2fe64365bbc1f791ffae8bdc8743d1e4fd775a173dfd7ecf971fa46159e2babd13da107b67b5ff2dee4d927e5906ef92739e4f64887173677488797894df127879fbd259dd26cd3973bd185fe2d0196e08c1034604927c2784fbbea01d1660bfd0a0ac18526106dbe5422987a0e82711d881696926cfa206808280692818e8074403393907b24747365a19b56e8556395487c2925892df4adb02249922b683a74437549270b5cfd4b1f1f97f2e72d5288a7050e4337251d1616930963ffda79f7f2764288ec3074137e9bfe97c36fcbea5eddabfc0ebcf1466afad2f7de7d1a72a6576895d8a27d6ba74338211eaea23774b3e9c7cc18f7e85fd9115fe9f86ae6ab6e49ad7bfaf2ab1009981f769cac21175d3af54c195fa20c35899cc464fbe89b26a223229a5e35e3735f2333a38e51772ff529ea90effa14e70ed3fad78fdb9c7a7a3dc51f42ee53fd1cdcfbdb97fb149f881dd027c29f7b227660758eed14f5a96a1efcb91939e46f4fc4a667785ce0dac58d29f93532329fa696ef691da7f93dcc16effebe100c8832f8191065b6bf5a870ecec4e97009d64b0d821ab3c56b644a456ad07c00feaa98963ab3c5b94dcb2c44178f32d7cb35325dae91d1614a9bee3dad43c705749c2dee1f0403a20cf70c88325e290998f3a674491cdb72e1b8b673df65f9b1e6a9351084cb1828840eeeb9d942254c7a2873e4fea40ccd57a71d76f83b6cb6f8e9b483cef68f3bc7129cf62c7c4b1d39fbc480a3ccb462083868c944191b304801122378f860c9386ccbd43629403712fb677a6c2818632a8c352aa59433ffa0da957ad55700708fd98275d9f13f3cb2e08cbd80af6a9dfff9f9df9c60ca579fbd3bb87e1c37c2186532c5380e33d7a6fe63707015a70f00ea30634f5d836a0367ccd816b880abf6fc9f1a03aa4d5da5c30d356a74356c19b0688ac1610b4fc605cdb0413a06363b4016385fb570be92b1590fb705963a7f654716e73e3c5f369a51b6699728699fec2d6fb1a57e760d02cc81db368ff6b51fcda27db78fe6d0bedc473368dfeea319efeb7d347ff6fd3cfb7e1fcde0bee047f397efbe9f8f666f5fd0b775fb823e9ab96d5fd147b3dd75d37d495fe9337da794140fd536bad4df3ab0a7c701d4ea0199b2e9e86543eda8a51ed1b0f4d569c7bff5e37cc5b2e35f5aa74beaf92ed02a2cead01572dacc79447c9947c416ff24925042091ad08009269a984dc85df5b4c1024b195daa6856d87502b768fc5b513091c0f5291069fdbe42296128a552ea0bc4553787567a734220f4c3fba25e74892e52577d5f5477d1c57f0b38bc2f7973e4cb555bc039fae6007195dfd7bcaf9be3abeb235f970970fcf0b6702eac7b6de1082ae842270649841d8991c5208991c5e8c4e834e14615378adc88dd88b5b8f082b7048ef7f096dc41b6e413f36945801138dc7a70607bd9a3878c83ab9c9b19011907db634a2a65eb09d99ad2ea70550fbe9a3d38e5a6de6c5ce55b0fae8a4db6ef3af34d047263d55895cd17efa58462fe2637296b955fb3a53ee5200fe02ac731ff8463eefa764b69a5102c3f741c9a99b9ed3d1cc74251dfabdfd5ef4104ac5ab50f4f482aca6975285d8eab0dfe2353fda8aaa1c60d72e496bb01b570e36719021c8dc480ecbaa391d88f1d8d3c116bed682476b343ba3d66b3bd1d8d20610c7195f20c504aa30cb7bd9755dd8ce7366f0a02c4167f772be0b0c69e4de00238c81ab1060e525623d6a84258dbffda9b536c4912573458115734a24a471661662f766850b2fd238ded4e80edde8ebcd96147f7f6e3864f3c70bfbd03b8ed89e8a676c0f6dc739a08fb9bc60ee07e935df6a4c0579f72786fdfd371b648cee6ba63ed4dcb57a1b5d91375636f622c0b37dbdff6f0f10a12036261b6c8f6696d7c15d69d8d84d79df962b7f7dfc1ea93033822bae7640eab63d327071061edd7c8f4601dd0e91d74cc6cd93e9625824bb9a308541821a5747ff1f01d8b4085085473c895eff4b3467cdc60474e081176eca0608267048a2bcc3929152283628b2f08220085cc3c42b4b085ece06e07c83b41c8ba40e489c1e8eae064f40392488ad290279886608132848bd168343a39716287b6803b7d2f407970a08281ac7886b07c46820085954f45454565652508344ddaf1e2e405a9500f6044b1630a9ca941ea532830601809021321c8767455140215b210b610a488931d8d1881043418a1470d283e7031550100b00a32be205cb851e5c12488353bc6930e3ac6a7119c71f7f831c6e831fa8c594f8f317a4cc93ac32c9582e12b330ec6c1b821dec2c1aac0d4815859b2425b0162084f4bb6566a9e160c06f342a5eeb6d24a8f14190283c1669e3b0e26856cb0cd5ecf5770e68b968309817175eef099cfecf51997e78e3ddfdba42f719ad79c89afea64cd1dbeda6c90983c7cb54d9cd9c357be7d9e126ba752306a2a653d315f859e580c307df798aa6d162bc1de0ef6c860b3583b6955ea40acccb60e25335af1c9dff43525ad6307ae7394dea4759454def43a48295f727729a54aae31a3d68a4f2a5ff3f553525e45eb48617995d781b2f22991a2bc5da94dd2fb5796951c8b59bd6114bb529ba40994de14672a2415bd129cbe3a266dd22cfb137d2967bea8b866884b39a5598d66ba027b1e07eade9be1bdb7bde779da07efc10f7f003de941207dfa7ef4f2493a942aaef9f271273ae430fe601557690696a8c02f023d07d2e0f721a443b97d2c106d0657e024b9d90e72907cacc3cffd98ebc9a52560aefbb3fb03c14cf7777516db1ff48038eceef47d4597db446c71590c22516cbf50e4ec199556b3a835af4c4ab320389666db2b4d45b6e38b1a947c209f93afe6dce6a456da2ab3ffcc52c7c8a4fcfa99d6d8a239ce181dea2e6394faefcda6f3fdb276d82041bd16fd9a53835458bd5df6be664e871e0d36fd507aade842af9ed59c1aa4c2ea105751e9795f696ab84dc74c9b6bad39f7a69393085714d2b350be378755166fce7c01c9643a3ab3da9ddd99630ce2b03b46179ace6362676c034f3c883ef439443ace1cf8bfdfc099effb1ecce00c0802811e6ffcdfa766e30cc4f67cde7f99fb9b3d3a47375b3d6e8fd030db3e7e95cd974d5f1cb75a06ee19e39c50ec89433f6280022bf988c1155de6094cc9094c0cd861e4c88073027bd9e9081a58148b23f6a4ed4903eb657bce7f5984e960e49cc0429b86d974ba84eb534a29a594fef19a29a51377f4e1d1fd8c4c11a37cc5fd065efbd9f71c9ac0afe0e73fe2006cee2fb7e54f02666c70fb64b9b94f91fb93e5beddb7811f317e077308c006b9f7bcdc1bf73897a8c0e06fffd11283dc831f8d5286b7fdbb4bc3e9215619bbea1bd56049714cfb7e628c3af4480b6a2c16cb04371c21140696198bc582818d1f5c018609708a29765e2c160b05045002ebb0d448820a3625c4565a2c16ebe6250927a8436c7cac20b14ac20e2a7c2f168b55820184a12bc285eb86c5621d6103173858c762b1583602d04215428dc5621941830add0f6ec662b15210001d256a33a600953082198bc55a818c201f2d642c16cb0500c8a133171f1879d5e0c41cb2d150c512d7080fb61b168bd58219b6a832d8582c16eba6e505a09112d4582c160f150abe2d662c164b89185514013e41c662b178c8e0855b04c662b19280810508e4c562b1705249b00d6c8bc56225f1820a21a42ad3b0a9042d419e8225840a99018dc2582c96097e0a2328019c22892754168bc562018b146040e2085860d1152a72b8d81148a44065054870020c5052ec84a10e618112903851d1032262d022c5b5c9ac30010a50a85822d68211a4c024c5cc0839e0a2348326f38644058e16e689144d7a608b80a4052f998f91912170c1c59260036fb158ac1488a83042f4841b168b9544a80bb8081858e02c168ba544ddf2415308d5b01229701085198bc54ae2a3848e0a198bc5baf144117a02c662b15e0012810947e40a47bc582cd6cd1784214c90b4ee131b0c7c48168bc54281f79a4cacdcb058ac127438f0ac405b2c164b059c0cb817c41a8bc54ac1568487273b62cbff92b0454210a2220845e80d8bc57a41050307862dff4b01e5c296c50c168b858279854e960425375e055a84da922dff33818c58624c4d9bde1160fa71b6a51412852c6cf99fbf7c3232a4a65d43a502785cfa9766fa11f420a66b1ca19a297168d69ea8fdb14bb2e37b31ce5c50ec199d50c294043e62c7aa025bc417e24b55416c8943e2d72de2aa461155b1562baeb031ac4c360d624597092a22b64c9a156eced5b732d939ae9ad26b8299d8f3e9e7568491211baac34c03101332b7a2cbccd9f34935d2ce4927c25cdc8a2e399a5595dc18e3f4172913bfc6a5042695f1a91730f5029e2fb79c744381c4da33c4c518611cc554229d6c2f9f7af7033d9f0f44df73cadb1c82bcb0e7dfc82d4405c84f7ce0491285e1b369696464ef6a3a6102dc298ff281dec3715ff3dd2928a612e964d30f3dcd5727ba4cd187f2b2a28b0764d4fdf71a892cce4db2e783f0c7037ed76382a45d1d4a98fc5e1b841361b89f1f8dcbdbf7aab0e8227dd26a395ba9876a92495e2b6e4c1de053adb5d6d25a3ffbcacf66d5be115b1bac29096270982ff4ebef5023cc22616f52bb7e7e5755db950066430d6b43dbce2aadd67c55c3beb0bd632a0940add8cdad919dd37dce39a70ee3b6f4ba15f8611146b5a92ad636fd9818e98e1d3c97673e80ab24f731c13878514c8ec3f871cc5514c7366b7fc3b24d8b60fa544a0f4d9409a6541fc0551446a90a708885a03bc4786442c949dfe61857d103b88afe1138c4308c87e098ff74296bad307fc1b089296cd3a73818c372eb93410cd99e42ac6787ea3855e22bfcc19f8f473e7d984e8cc206cc28a51824604691c51cd12175a86ed489baae8b41221aa1483a64d931b29823313aa5528c2c06c9f572e86d46f9ed87af423a943b84f2e0c71c414141f9181d5f818ff2129c41d112c7286c50321883048533abeb920cd7d846e1cc170e45a370503848626431486cfeee0e0ff434d8813148a40cd0e66f834e38a983e27526922bee98e24ece7c9938238dd224cf217dc973e60e8af6b467d221cb9e38221d72231d62140e09c7e341d121f67473e7b97caa45184e7f4ac8ed14bc534c3be5637cc91fb2ec14bb515ee79c636cf9ccb282f2db5f52f6e772f728374aea9eee2f4a5e7923f23fcbaf7c0e96951c59e7d7a79dd93229f57dbd7216a3649ab84b25ed79afeedb95de5f8234a5f7dec11c4aef3d0a29db1d415dd2dd2cba783c1e8fa7eb4e3a169309cf682c6eb708e3b5d65addbdf471a27c9c9ce72412a8fba9431616141d9ab48e7176b97b3ceb682c579892606db767bb3d21f8040ce5b84f44886c9ecc6312562b7648ad774b68b5cedbf1d5b7c3579ecd45e2f2f0d5e9cedf7e802f3f46168344da8811a6f3637ade5e5b7eeff9e86d6bb94a25539dd932b5e967c419f4eb72b5d9f5e963dcdaf7bcb9bd0e7cff1bb2aeeb5028940e7fb0a00e1d5cc0dd5d97dddd3d6676d91fcc27f7ebe594b7b9a6c807d5effe46116923e6ab2a6ec87c6500d6ee72b87bfb545e86296ddb1b3157d977fb386f4ba86d941d8d2ce14817d807fb755325bef21e9c4b401dde90edcebb5b23e8a24b23753cd97326ce9e78f639b3e4385b481f4e9c957caacd965922b934d5e962de831fba60e648ae28437acfa2edc9fe5e56c9520685e399155d482ef7b87b46277710f422e8271f925c2a79165659664518770741179d6aa5ffd34d84f1fcf43ec4b317f8de87da664f154d59b389aba6687b166daf0878f4e1dcd95367beb84cf72ad9655272dc289b2bacb21d9250382457e8e5849e923d3f94c38ec89eefe5f8ca0b02f362bef2bc229ecc573a9e125f81b325becac25760cd63e235f15505fdd6ba11bb5185b4116fc8b61608a43dcd09a1d45d7f16d583eaa07068beb23bee4a7c9585af74e8cc579672f973d55c80ef916b2cbbd8de8b00dbf75ff91a198fe7a9684faf681d5f23d3d97730a34c51f62913e05035012076c8a1e7efd0c305ba9f8fc397bf8c5de57bd757d530e386181d42abc3aa750c598adf30abf6f1bbffd49f9f3f3167e23e3af0e7c36fc66ca99fac9a2d95f3d568c71742ada574a3dbd7df81da1faeaa356266fd1e7cc5d5b8210601beba5f7f075f755f7fcb35e419aeaaf55d55bfcb2957d51abbd6a7fe9f37623e7e6ebef87f9ecec7cf83ffe773f87f749c58c72b3fcfbe57c3e65086f43db0666f19080fe85dbdc4e7bddce994abead75684f176fdba635936f3657eb5371f1dbd857ecd9ee3f4a4c07ee270e0389b32ab359685ceae7f0004f450df0240d4a864d7fadc66b1fd51dfbe626881ec8a4df3657e2ccf3b25e27b4be008ce1e1a70674a53e0bf7f3f128c1fc10ce008e9ef8241c82edc528f7f8ccb1a4dfcb9c30e23be37c23e1106783ec6165c896cef9efbcb9d78f8fef339beffe81ce0c7c95d1ddb92c6fbfbdb5f8d23b4e347588de108d196f0e8185baa0e2b911f6a6d3b08688de7b40a4260dbcebf28fbb465803f346af385be7d1a3b026009c046004794a889061611a6236dfb2a37b3a50538a4310b5d6c40adab022ce30007d0ff681ab3d96269d0668b05b592c052ebcc689d1b1ce61d6d9377342bc2786f5f171161bae7348e6e6d1a4b68d0261705c01280cd4d0116006bbec813179bd9629f73b10969cc4c9b2a81c39b235d582e45b8eca881896dbf06170d456cfb3d89065222aa403db67d8ba3f828914138d1c5fef61ef50727bad470f96a7b161a335759f92e36beda68d07c00f667a6b1c406873160d733020e69cc68cca40d2f42b5ad13780b4276b1ef160a7d29355e4ee0189f46ec4621005b6cfb01a84518ff1944b6fd192e36930adcc1a28bfdab69cc48b402ecd4786d5413db7ef862c5b6ff22db6942e566db998bcdcd3bdbfed4a10d6ab9d8805adb565874215d11572a37aeb26f9f348b30dbdb154495fd90b4646b9d23b67d92fdd12cc27c6f63805a19fc2f6faf72e358a8ac206651939858c4f070ddfc5904dbaf566099598419c50f69d0b67d1ab3f9223f00b50833df3e8d5884e1debad8d0b649a2d1b6756ebe10d9d6ae68b0ed87a4552bba8431708436a75f62d1c53e65010e69ccb6fd3e74b1d9f6b9df322d546dfb1e10876873fa66dbbf79dbf7f4b6dfbdd836bc3adbfecde1388ce7cde1e4ae612ff1d1c5da69bd26f04721b05213587bfe8deeeeeef5d2974e79b067cc6c99b45efa43ca98fe31f345eafc9036ba2652c67ca903a33f76f5083b80af405856aad335f940ef9aecf9f6c6f2f0d5e8e75b1cc72942776890170592e32b3ae4061b6aa02f296173c69ebfa73749fa24cf9d2694355be68b32b5c91409574dcae393290eede1aae9385267beb80f6f99ef3819b55335d830c3a58e9452ca4a5d4aed8ee33dbc25e59c82be8cd33d16c96fce0da7d31860fbfe110317b6abf6816a1fbca6ca0fdcfadb03375b7e8c76f30d17b18142aced6a572d205bf215a50fd77dfa32477a3ebc856e9dbd52578103a511e8d0d5d0545da34ff5ed5b9d81aa372a2e0ef99172bf7d0efb95d35b7c23acaed1e9e4289e2fa3f4a799bac6e2746f42ad5649ad3dffde1bf6b9f9127a7b6b200856f90175f03c200d57ff8260ceb47ee04cd5de5f9abdaf44723db2d5e800c10dd0de50927df8acbd11b3bf51f083df7e8c3fddfeaba0c73af4fd554e8394d65a41edc3f6df831e7debd1b7379480b356ef6fbd7164b337aab875fb5baff6617bf0e98797d3de53aa69cdf44efc557ffa46151ad4a78fd2a7f46fc46e14b120984b54e00b3e08743f77a07b753804c5568751f0e81004cad16106bc6ddb364a391c7acfa2ff64efed8dd89435fbd94ffb50bffa9ca63ed4b79f00fbe09600f03f8d93ec59b321fdee534a54dcffded3e1109403752837b8806f5fd003823afca1c6e42b677b9de5728d6db5d6bad9a7f235436bae475cd5fab8e9081d9d3f846f99630eb1ef8f9b3e04a16387740fe1a1d471be2c888c076d0b6047235600d9467426e8465a3bc68e46ac0067fc25e8aec3ed3ffcbec3cf3d08f4dc873cfff73f1f1d5da541997bfbf33fedbd284813d66cf99bf6c1d31bfda1ebe6dfa882d33ec8df7290bf591deeaedae3f6043a408cb04267b65d7b4070685bdbaddc70783b084adbb4f704a6b6e5c30c42ce3b93b41b234ee8ecf0ce84903bbc331a5c4e92c6808f28e5f893669bdf2340ade8622b88067bbe1542cef772b02f39502bba4c1c8b2e73b6d19f3173dbd9f37d7651c4558c62c896d2593bb47fd2818b5b9f74d87ce0f4a6e3766abad1e2c654f8e2061661e402eaa743b93f22ad4ae4538b2ed4a6b5e9187048029faaecf92a32d44e7429e56caa52ca6182ce51916d2ae9df27d9805adefe72c482be27437d410849ffda6c5a5fdcd01134a1879126b06047234c803501891d8d2469b2eb8e46927421092d8698c60d5c2131c6cfa30d334c2ac9c9f74fcbe7b0affc1d05f1e3453175292931e5642bdd68cd02cb984e424e2a89462739ca7848e6cce7748daa6d9851eb90cff471f9db41ca4770d3423020caece87ee69428633a492aa5d6617ad781f2539f648d8cccfcaecfaf9a4cf305071c623366747a87534ad7e919ff362a30356d7a3aa5d4d1977e87e8497f4f59e55332ea51f27c53f6dffc46e5737cafa275eca0be8ef9df579de33e4aebf053fe3e25df47b9a71de63f57024f32e84719bf287f7608992ef8017d9e1610b3853eca34aab10eb2a9fb96649cacb1fa81f36b0501ad74da1a2393324cedd495b55610ec2af39cbfc97ca3c89634c6e946e90d99ad73524ae9bb8aead3d438e68e356fc88d3776de88fb68b565091c865ea197fdcdc57d58f868aea2af371f46e053fd9a1da67ed49cb2e13ef5238584abe86fad2d6f3eb6561702e23d87059aed5248bca0f96a7a1b28038bb88afe0b9aab689842a2f582369b2f2f68534415ad60ac7e953da569892b57e7d4271d76d5277dcaa1fe642273d5c9643ad265bbebd73abf3aef0469b6dd7d8a9d39bea04d01c67c3ea5d53a1dd6232f5f4dd311d3cb5554da0e7faea21f9a5ea697af2acc5715488eaf4c434c3193cc74c4579d8e125f2df15547f395cad337656162e2abd3e7ed973e1c6d1521305fc55de5d439beaa1a9772dc71062ec1c01c561d958aaab8de6a6f42269be892e32ab94b9fcce950c7fcccc97193fbe4d047909b273fe709f63cf740cff3bc13cf031d854fb497e77ccf7b9d0aceccf927390609a7f2368742fff99823a1d0e743a10fc7a9eccc9698fea1becace7cd95ab385fe89ca8eca0eeabdc94d7a75c85d10a54316959d78bdbbe3c62091f555bee6cf73e08cca76238c30bdf03d411ca623a24ab5e5b929c883e94514b185fea515d70fbcdbf47eea5065c7135d2fa37ecef9dc9c13b5694e7ebe80c5660bfd0eeb08ea1077ddccf2c118fd44230f18f33eef45a3f7e99dbca783905d7c5e25ee2d6f1b94c317b24dff0363a61118c339f47a11c5a62ba093d3834c361166dbf4f3b9a9dd10d44e21a8d540a7f772083ee84f7fad0567564c1ff107a5f227a13fc18f4b0ffee9de74b3f697eb7e72a2e7445aeb096ceb747214d31002bb7e08816d45b5876d3b9d32aad56ab5ee76271ef2ffe778f1fa8d78a1b596208d74ea208d4f6e524c411abab7afd915a4a97bfb6bb98e7b9a8d1b813c1e93a766f2786eadb63d9e06bd83a78b3f25100cfde72474727272123af9cfc9c9495539d12aff5151895587f793eba3e421e43ea927ff3941f993ee519f13513f1ad56e5853f315c1d76fe28f9bfb237d78a1f0fdfde53e0b9e78f8d79fe35febfa39f2ffed367dcae17bd1d37c5a747376187ae9f9a3e772e83db994b38915d652feb5fcfc8a00cf9f54c4d1cc2dc216a4a116c421eef9204ddda2bf9cdd44bf3d8d68fea66bf6fcaa75f820d477286d4f1eca9ebeebdece590bb74ff99a41cf799ef6c17ef712d4e1f45d376f08acb5a1ef3eecbc999b1b829a8762792f8720b82100fda63b500885fa988992f13e39fdc7747a120be85372e8819b0d3a3d48089ea15c8f14814fa59d91e56c67c1997afa2a739585a9e89005656fb00ef1bd11498f478a3e469793cfc23a8f1b9de490d2b6482f91ffcb1fb8c4d34a4a64fdd970d324fa6c3e9aaa5a58927ac5e8ab0e495c82ed18a78d74c862d22f683422d1875b8b7e1fad3ee562b74dfa795ff8d9ecd2174a4a6cfa1f12dfcd67f3f1f870be1e9f8fcff5fdf8807c7374f2a3f7bc4a93d0cb659e1cf728c73ddb37b24a3ba52626966987af446f7a8127efd9d1e7efb2f71227ba801faaec845e2120a19c50105761fcd6c657204de7bc5ece255774a1f51be5afcb55c7ccaeaba3fc8d7e8ebe7b1b0a8574f84337d2dd4f6bf3fc1bb11b456c066f5471a243b94fbecbdff7a3d1f78d7ed475f5391d0e41eda79464f2d5755babeb7ee6fc02365be8ffd652d9097d119c1f127d129c1e023267bd210bc142b19ad0105fd593af79f42070e624c8cb55b4e6f952ce2fb9c098cd295a586b455bcf224c7dfa5f9e2fe266c6e17dd52120aea2a33fc9717b46fb93c317b110acc4c4a62a3ba4a71f02e2ab1c5f79a02f3dfd50105f959e7ec91561469bfe68e48139e7f4de666ece59fab06683bef4a0bf5d07ce90fef4119f5cfc22cf8b1e7cd47f547c986fdf9ba1d02b443f444343360d05d9346f3f1a7dd527ee47b9fbf9a3efeccfdabde9bd1f6acddea439711a87ec6277f3ab374bdcf7537b7f9a799b336f5f68fff4115cc0f753fbf03da77d08fdec72d5d99ff6425f7a151d403984f4c94f1da23e1f4ff578acc7ceb79e592bc61873f553aa28e5453991fb99ef6fdbcf145b654a4a187a559d6df5fdd2bddbfc084ed07bdecf4e873536dbaf757bd3d71cfe50fa6ebef712d401f49e3747ef85de67e0861e70795cc0f5c3aa331fe53b1cb28b3d846c87a5dff40fe10f55678fbef431eec97d2fe84f4e59e21faaced680dca017e5b85332de2839c4c0ce2ebd29871e70edd24b3087930dfa12295b96dd31060cfad0b2406f6db575eb46b98233a3bf1b4823bbd8b576279e77a26f543112fdad74036744577ef5abdbf6b19e59bbcf7576b656cee2b88aca8d2d8f25feb91cda1e9bbe27c49fbd992fafab6cb6d0e7388c4da6ff2ab3379bbe65d91dbeb2a9ff35a56a2d3169b6c1edce9f59258b2ef4fae76501e7ff9f2f68a1227a165d68e805eda3957022cc7cd3eba36dfa2aa9ec74f4b34a36758eb3255a2fc7553404a47ecc0cab924d9f7b41dbf4b7d06b531ad622b2aac45776b6c432a94decd35ac45793ab9bde9617ca85c0b93471cf59b3e3d5b57bf1836fc4e73def600edc3df8393eba7bcf5f39a9536e564c13f77d5b89eccefe77b5c705dc79b9fbb8a7acbac6888080d6783e7f70e95fbaefe7ce09d27c94d280b5561a8fb5d6d27cb66ddbb60da4c11cc7711cc78134201aad034b3b3a50878ec675b7254863df6a8d23341f4768d32ef6ac7203038dd2e61338a43771e6cc9cd9cc6de46b3e31539a02bf04717c3240693842dbb5c5e266cfe8c5209132e4ddb2dc313224320689e7fdf7355f8fe73b8f062b4803befd5b59d6da4457b1ee87b8c62091323c461683648bd1f195fc4dda4ab7c0313a74be04a5fd648da2e296ce0f8e7e3edebbbfb5524afbe10f5f2883f2062ee06e19ca17072e800bc1907e1f7ef9dde7b9f9424db11b55c8fd79fa16d4c1db1ffa15a4f9fcf657c65de5f7c975e6aa5997b86a565a74559d81f2f4073376957c85f275e3c8be21dbf43970e6461552060de58d98a471dfd8f2497e0ddf90b9aa664b09d25ae3818a94527e0c1229a594523ef87d20cd66adf57ecb3e8c386b37cf7223eeebf6dea85adfa397d9bb5cd06adb2f6d1b8d3c2f01dd8f3ec66bbef2bbf6afec82b673456d7b2dd1b6d7d2ac8abe6acfd339a59c2fff2bcde6cbd54999916627b390f7dbdbec79993d502c8574129aa018a743ceea104b0d8a555ac77131b298235c842d013d08b6ac235b1fa66487430431f7e891481bdcdb217cabfc48abd84aab49aa152f5767b6a83ca87b10c8ea13e8b7f7be760fe2ef1373c472252af0068234f2adf6c1fee83fb99d504af6bddf2af71ea743e9a9b8e2e7938fe583d2be57ed6fdeccedea06d90db27483463a068994e17d476363a64a163d2a8fdeba54625c8193dc97f2863f542f6e09561d23d3992fa51c2eaebea8b40c34b7518f32443300000008003314002030140e090543c16040a6eaa28a0f14800b96b4547250996749905308216210010200000000000000000800081770dd6657414439e5b47a62fec163048ec868cdbee666f4c1ae83eb54f27baeda43e9dca789a1ff260a4503728e117a7fe84a83e4a6d628e857b399e57c3f891973c44e82a523ddb5daf096916ee57d4d8fd15195db7384fb7c146eb356c51331b7dd4215fc6c0b60dbd97924354615a91efde02e7f1ed5f699201836a1b00452f6ad4969d036ccf4e765ad2536d06d6383fcf96d628de2b437615f65156caa0672af620a5e3b178ad0f85f6cceca2aa4bb2cb04c28a2add4d20db58d375c0659e3160740200ab33e02d0876ec708f1ec91f38143ecbf1c51ebcd023dc5ad0517f320688ff0066ac294909c33fbc6487c51dee974654c1dd7140fe15b6d0b5811ff057bff564a04ec218587239156aef0bd0512bdb8e131b47674553841d7473f39af62a0944ce9d8febdedaae55eb9a681ac015569a05769dedb75849d6a2a64f456da34bcb52e7f85d40a30ed57ee5afe080a4de10febc9cfbc824d46f776bdb63d282fc37d8a24616be01e93466ccfe29531f7763b83f2e431ff787c9217ce2d0903d3e8709c2866fd633ebaaed3d3d6a90aa95c5802b05b5e67532a6c4ec1179ef0fc62874686f0fedfcbcc0aa6645c44731795b3fa2ebbe7aa1bc973cf0d3efb51142d3d62f77d8e1e282a446c43400decce80156479eeb14e7e722cefc13bf7ecbcf5dd738f0b5a2f2b1e1dfb0cc84b1b050821f001310d409b4416cdc41795c84d809fac75c08b4f9102f0cc8eeb895e0b0cecedb594d841e14f9c7bdae5ca748431b86f55d41a0451a6a20e338811185ddf5dc9bd8f007c500d92415162f417d4dd504043a32ee756eb5771917366e3fab6c0144e986a1e7e21016528fc055f2641ed99ef55d489d6b804c773370e78aab2ee8306a34ef43bc75fca7d6e076308dc234b069fd0aa058d5db9ebd291b46340b136abf198e0622987fba560b880d4dacd274b9bc1f879e8f52e661ae77d2e6f9b993f8c8bc7a828fc34de0c0952578a5afa6b12073ae449d258358c74e913e0dcd607544852275becfd6bb925ac2729fc38f1dbd0cad501686568c7dc413e538fb6220f5248c8210834259829298ca25a7d1dc2426735292b5b87d8b181c41db100cf566631901b0b9e55ea92e95304858d8202a276779ee15682c1e06bb0e21489bb32aa933c26daa09127522faf6f9500e0ecff8ae7372054aea74422ab291753cb69205e6e9c3ee5a5d0eeb5df464a8baa5bf2d18fe0a12f76fa0d51446d955657f813726ea2d858a6d4818421efa470d64e1a3b42a2357b00acbd341c078a75ef0fae5e20858f8c7e42f74cc4264ab6edc860c023d0f3178a302c8b3258aa97b00edfdf3ad5a9d93b77bfd4b937e42964052c516d5146f9216d730e71ba59c22fe3be95d0246bff716fb5a3df316c5f73faab2a41e5d05ba29699a89034c0ba32a60610cd5ca109c78a6a7eb1891e628db4b4503f27fde95f5fdd689b74a3d05f27b9588a8b37da309378be1203a82ac6101bb88f45f9c93336f552ddca3a78a39358274800a70d2b01ecec1c0511520148be85734bc96fe6ef0a14fa3cca46190be3554def725a299459e780e7dc108ad93454f19ea4582ded452c9025aa0ee2f4a47f83c2c9d406b1442028d7d1e821791046c287777745bf8a0a70660ae6c0800c42abb46e3bb886c53e416b8ffd101383e5890681c907329f4ae067eda29d7780428e85ae19e7da443fdc84f2f93502e28d0544ee43f9956df3e8eddb45d1fcee20b0d5c3b9407dcc5f3117149d014bc74f0fd40bf8815d325d73aefc7ba7031e87f29cbdc43ae727677713bc81d07f6c97079ecbae9e6b8d855248b432322c65a43d49afb673128c1f74ca92bd4f005cda71f5811beb22127b4e616973d29baa07a67a65762eba8b9ddd553b5a4806642ae9dfac1b81864becda304b90e42a09e6c8ad42ac4b6c5e1d6b2aaafcb966af28135313e8e16024de16294eae8e8a61b9dea6318e3932caf2da287824cb9f3fa699acdab34f3c321ceab152baedc3b506ea6ee07d8a410c511414b06a8c508f99df907429b5a10e0d9300d9910032f5ac6a7e5a6fddbfeafe0524e46551958a44c8be95add495742f82d3fef59a8ec55a1167a85a5089c6c741507a2fab59d156c7739e4c3c003cc51edc375e2a2ad22ac27e18e66538828f0350af8f45931adcc03704bb9632c2c7da74913cba0e026d011c9a1b9a87c4c6e886b9078e152a3c984438bc514e76911f4541c5d92800f1254e890a95372ac6f62391b327274a26d3c09c8ce117065239424580c4f548786fa250c6229a4ff035b23bbce10ef3a5d38e4a3f72d26bb813a34ef4e7cd6480dd89106a57c39804f366d64bad384ac1e86de512cfa97e8e4ea8044e43f6eaddd399109ec42cd4926a054feb21849717d6d3457111253d0bab36ff62f7cf797be1a0119e014088939724abe6c954b22ebd13e6e84bf3d5752b4e62c0f0a35ff82b5a62c69bb625779539743b53590ea44a690f8c0be8931745f4830dbb56aa34303123d2361c179c2c79d3735a2afbc55797b33a7ad035ab151e69989201e28a7e69d93a0fecfa244460aa788884787dd31d2d214ad5e5d9fce61ce4f5fd9584c312964c238224f043559998343fa678970d3badc014b4b5d94a0628ce3970a6e78f92a3a275c18406e3e959f0ea41469035fad15ead6890595b29c0c7e24caac2aa9f70edd0973ec0ad2f894126eed9eefe5549ce154a38190e6b296386e7453dae9f8c6dc56d2efb15a8b7ad5d424814e22be55f4dc94aad03e409550f0f20f66bafba1a75d706913c75add5679b254f5e170acc2e2d26a0100c8511cadc906d392b8f583f07767b98408eac298a2a35039f06878671c3ccde242cf172d103c6d7a0fc117a22aa4bfec85ed44288f88ed0c2c1cba5c64524e8cc381f10d651bce4438647044511d203884f442a8781cfab3f485c981b317d2e423a982e29756ba1299fbffdfefbfef6f90726c0e6a97a1fd0127cb3fb5dd0299b6c887d6e267dcfa5f42342d67d4052a1358941efa9306eb2ba1a674a9580548dd784d05800aaf06473c388ae2c8cbee21fbde2ec0fbf197eed03a6a694f522bd7073fd2876440a1161712641602737df7d8dd8d1e2fc610ec20b4b42b822ce2f901302558c44005446bc5590adfca87d002cfb9fed81517a0d3d5c8b4de08950d6e3118cb84f52d7360fa0750b60786b2704824a01480c94a26cef3563506053389ea618b7040114cb83028b5a621c4424bbd9820b954384b0613081d251a51ec6b5b0b272187f7218ee647dc0fc0193f4302636914263189598f4301c5846f149d9434089f5006664430435c20e017e92a1113c6e670cf6a4552d035b2a77fee4ea41d8ac22242c3b0ce08f5886f04f358785b448128ee76875530e098f58a4db3ba9853a55643fe888135ff87c4e58b4d0a2c65aaa7cb4692191e8009c8fb29c8c2c032a66d8646538a7f9e8f87d709e42b2a1924bd2f7cc976df7e0111d8f108008c74272b9e5c805e150df2f23e4d272871b9d715c879c89efe474ec558ce9738fcb2a56520b397ca1da1ff179c3363ad2b690d36e22853a7691a75ea4836b3cf30838cf5ce6239a5b088cc28ecca242c95047dd445ef6e539956d120ba714ccdfcc1fb67b3b7c766717cbd5346340d13999cf9b9a8f24cbe87fde78ea1a09026d17f10d9a3c29a85e87eb22076ea736c1bbac45a279dbfc83b70b850b35cb4a9c4573977da570128c71bd4e8d749c0156a12af46a76ccfdd080e8d192474bb49901552aef159a885f585c33331254ee06704a1e73fc1e8417375a6d6f464659ff574d034d1d19cca1ddf132790b49c4d81a8250a88039f2f10a81e4a6c4f23bc304ba93acde85b82d6157bc56ab0932883eb9a4d02deab60db0517dd46c8fc5926ea975fdf82c56c835e5eb7602081543af45394f1a2ce9e9216d8a8c1e766e03c3996e6d6682950b6943d9f1cfad743114b53cb6683adc41315113d64006b11be1783766cecf94580d5fbb2263edfbd847388bbeb1fe17b595266388d18175a735994b34348e01dd62fef168845d64a942472599bc1a2c4173581419f920f2b42a2d675fbea7fe6aa4f24f211d5ca3c90423a625d322a64f60d4b4276a08ea49d85ade43330c07e40210d54b8c8a63e1d651546d8422795d6f0520d836348cfb431c39d35bcf5fa111dbd792cc14198a9481b05875b2deab1b40ac4203d3eb91b32aa5b9fa48ed5ffc8890312a95c7d3985d54f1d295e6ddb610ff9ee3aef46bf25111771dc55d6aeb8d974df1fcd08625f2f049c168dc797764f3f702ce9010cbb35e0a9fd3ff3c4c96865ce19d8fe7618e11ee0f550839391b8f80e26ba8b9acfd31943f21871e9b54f72ec67c8397317e9e90c04c51eda175726a22526b999177302abec3e489bcb70e6b718d90a6b234792bdde47150daa44b086219602e608890214ed6dba9c5fe6cb15590c3be1c3402553f4232c0495d81b2308303b69d841f0f1f0ed08dfccaf5ce42b3b56f7711ec88b0a79c91208b14b04a338b7150ae035432f2ff0143abce002fa91a35e476e62cc16fd18e3bf78a5e915ac75806323ea89f412caac00d8b4376c2ebe50c14acfda93b4b778e18ece9af8073c635dff7135355ce845b5b0700e329a656e40f60aa949ed7c59d6ffd260bbaae475ad5bab34c1ecad1e8de08b4d6800e980bbab39bd59f134373f6e54153a49f46d57b8e2a7f3cd1164ca52544542aa851efb51b8d2025c64b71cc95611b82e12e7f4962ed83fda40514b5f05f6eec7525de3b9835778308ed79667224c490bb384a02f0d337cd4d438895fb97a9f4205d968883a6bfa62d37ca1f90ebb4ee5e8873b716d298a062b395bd16064f4bd6eca1168b515e1ebedbe6a9317e947c23d37b8bc22955bec1e2dc41c6c0e085db6dbcc2330c5f6ab6610e9bd1cc69c8706d87f3b8a9ed5b4059ee0cd1b15aa22d6b29bbc059fa7b1501f6a24bb6d3afc0a8eb4fa05fce207f9edce17ba9d1875523a29726edccd20057e2bb339f0d412cf032f9318d9e33c294ac51ae6efa829d9d73b8482f3e3c1a3a3fb1794e99b23d3da713e30b60be65ecfe0b2b84f49606be1a4956c6c8bb171cde5498f26863075278b079191869315a8cdae104413ae0fe5ff629682f43dbc48cf6ccf010aeaf7b35ec75ca238694c333bfc277c8ab619d5423eab024d1c4d249ef5a14cb9c632cad658d20e84ebecbd82500877024afae031ae4c1f495374b009d90383664cb9d45ffe0cee2e95c67c30a4426f3bd02ed76f18e6a4f116d36fdbb70884f4f333b0740427ccc86b2b641af711734b8b9509e0d695bdecf3ac72474b604f118ad30ea09d58414461b0007a5618ba58f78e96f208421b1674c4cd35ffa41131070432e5f7f9aa1611a966e9627c755dcc53011e0815423a81bb821f13b320235fc38a862af917e18266108eb0bfb75490cb6a2757ad588a04bbba12d088fbdce0d32bda7953dc79713dbac0c7a4c8a9b9d21e578ed8493f413c59ceac2d2b778e9b9d0350512055b58e94d46967c4dff22de606499c7f1c3c19dbbd7673bbf446b6625a1b5357d0327941495b8d324588487d51671683b7c3a098fa05fe72f38318f0fe128b192f734d0ca2791b9cac16d0bf1cc23d150882fa424de9a687a94d0c6d1786af0f0c43c299894a5e1f546272e043b519debc744fd23d3d40082f32db9ed86ed0ad39df0f57d403455b65053ac62f7b50cb0c5c138212333f815ac50fb042783da0ef01e23371f407c01488bf2afa21ebe5e0a8212ea39d197635a781d1b99830683c83da5e94b73b9105c80f70d526ad4db9ac6779facf3bb9844d28f4dab88eb185e928fe65057fe75a69e7379263821611e9f4a68294217604df832f869f63efcec473bce28fd1c5de18b2909c7f5e4a280925d805e0390bff5aa6e2268c6552e10f2cb593848c8817220c4f3357703efbbcff363ebe911c5a9fb9d60e7accd4b222518bfb92b18fac4c4e69bbdbe89d04bd634484a7c5bf6f4fde118eedf19bf8ede6924001267ec68fd15df754039f5a131f6dbd2656061a8a806ea3cea4f18eb6b699fe568add06d5c132840bfa3b8db9c5209c7dc01ea4b9fa4acf427555c07f90f07e13dc9d16d27a8e32cd137e91236a76d784f985c94563e3adaf7e4b170dadcc485e4ed7ff6159f1c73bbcc39bea76459a62bcf616543046fff261582aa7b7af1fd208f8ecc3ee85e3edcff44d5bc4e74b4960bf7f4a0afd9fe133b6421abfa2d06b5a6ec4663f8bf31e1596eef50b91e3fe2a8f0b03b115ed3a90f02fc976adb736d8de2fd97275089266b1f716c71f87e76a507810a53d8a0b1b988ff000f8a5da3c374e064f353d92b629bf2e6a110187ea4125beca318d7ed68f60848c294195d08bba77d87912ab1e4e0f5550cbed4c8dbab41f112d0182071c14be18a3feb637c0731748168380054f255283ba032914ad3cde07cf30cdf87c8b5552663e26beab0792dedeea05895f3e228295595945112c8d10c2b5a66ebc5695d61c2ad8b01f3825403072b99f9c5908a309933d722f049811b83653af17d0fd5551aa0300ea68dbe01503f2c0692e1e64d66e8a26ffd63d3e411d346bb0bff02786120ed679e5fb7ca9fcdc9a395fa495924dc45768b2627fe73bb257e8b76c6c118cbf4acbddae061292dd9d06f6c533e29692042a5c85bbed06389d73412d4fa8ad20aacc77f6a478eac37d24e4e7ffef7ded3624be998f8040f0f4058f52bd0636d7a60f2458070f345693f49b276b9859ad04367026b84ad8ee1ce900348ba713bb0a146142026efc22034abdc5496e4e8485fb4ea75e78a796e455c56df965c59ff054ebcb054136711b68011691cd200a99af5b61211b5a46354655b2ea4b7aeed2939d6160856b3b0ad044d94bf696154693fa23c217d670ffed5b3340794d214de6748384fb5327903bcafdb5cf189335a938e284f1a2901ea625f7fb1b5c35ae87f38b5bf6221a9c5a36e21f4697dc8f2e58ff086e0a563d0442fa2eecada1d7ce23ad39e7ec9aba33d4116153af91b0060d96c46ab1ee8de75f6e4475e459f96d42d046841cf20f61fb8222523e4737654f9b909deee4467f1014511a0034d35e612ce6ae8ff69b08751643c61af89f23665dee93145ca748f49b17bd2ae412c3f7bf9957ebe319cd8ef63fb02757477a201fe44f8e2eb4e20848e3ee5c5e1f58036dd09ba2c21efc39e602d78917709d766d39a3d814e31f53a48312d8f88c9ae881c70a8b8a457e9af20bc049c48f4b15be2deb5ac5693360bc2197cf1d9ed5ff644f3c1137d2a608015f691ed127041a7ccc3b9dd8c9c046d27342cb00aa86d18ce8c7d145d078f3e1678fa4fe1a03f87e8ba5958708cd0b7d04887bd49c77762954e80358a105486cece805ccaa943adbeea23457bbbe29feee2197e58d8b5a01cae06e547e76217173ae6f02e8607e2e5609aa0f8ff7772867986b952053ac62d273d8fae30452153c1f9467427d113fc019a812b0e05bcc7d1f974be207cb1ab4238ca1d08e061d3dd79e64cc927f5107310c2fc76a98a4f536d68c4ae856a45983f35ce36e0890787d22ebb6db10e230deb7f0a99a27dae22ff23dcf471f098c78a11b904efa5228222794f14540b5f2233220e18bba5506d4ac46a1e5a1bd8be07dbae5432af8b0b4ecfd7c5ba3d754626853f9538e43d63f85ef7eb7561042a35730ad8384547c24d3da8f6d4790d04271e21dd5daa3df572443e4faf2f04c465684694f1204c70d4613d403ceb93f3f9cdd167d52dcf82c91362ee7345b7e7a8ef18fd9f94061cc0b352392a2ac268a4df4d36ffafae0ffb57861473cef861544ea53775db5cd87ce2fb3d75afc4080892d0b02647baa75e1470525c8fe1e061dbec4d5bf968c1115d76bade384e3c0d22c972dd92a3fd099e586316080f37776b632f314c7de5a3d88ba9d17fb9a5a69ffaa1fabfa6ff48cf6ac98b46a7bdaf7cb2de8bf35c34957ee5537d8622e335a8203b25b4467e7265f61fb0a7fe6517d3dad950a2e39a95a2eda9e7aaf85367100fef036fd36a6d21996c36346269f833c498a39082992e5ef68d16c76b01574f0b7385aab8aa907fe272489f8bb306c079fb517bca0840646098f3eec73f71b200cd499bf7d417359ad5f5378980057385bb244fa0e766fdf1040d73425741a0d025cc26a8759d83c316289e495f7df7d43144a62ee4b3237bd747754b080ee74fd6d87fb1ad99e72fad680b2ef24a2e781e11871921d38a17cb28e4887e6cef39c4c8fb9c34a8eb5392fb05e34ae59591aee90c906fbe5ca7b4d1fb8e90086c0523df1b9ca7be4bda61bba5e4fb42f963f9117e7cf2235436263a1056d23d03ee6c93e5c60f8e7d1a8c917a145dee0620272675f1d86e9db137fa6828f17ab1d3d08aae74af02aae86293aea9e9d38123ca815ed8334d100d7a3cb7a45ba92f1633b561aaedc767a5d84ddb6aa45dd9f50498d996062d86a4da4315c0a1c264521879165a0c398fe5e4523addd6d499f410fa8292e1d1c7ae40e5b33c2fc7fe77dc908ab2ca2cf589d6b80d281fcd4f161cc8ec82c6fd2c63cbe9ca7953a388cf2b92be48df04a8a9a140a1dc2e726b02c6f00f4c01d6031073d984e20df0cea2de5bbdcf348065516c6a455419ab53889ca427bedb38664a4a001ee1e818d24803b4573a2a6c4cfb9d48ec2593dddc878c26a8e88c09a7c7250b33888820fee8f1828524c3127cd5436c5082e3b76efa63ce402424b005c894c9b8afca180943571b86fb5be1be5a68774fb35e60932ee55f5834caf73a5dac9650dbd2c3adbd93554ae5b546380e7fed01756c699480f90e64713f9e3708dfd72225d6e3a1ccab79d1869d593c93f564295ed3f713b1d0481be6fe892bb8c578cde647aaa7e2d35428b5e521c9f6916108025792a00cf64ff76c017002942e836519bf025a23d879d04e1407b6f393042cf1535bb7637ca8b9b745b09906fedf8bf2a1528a7cd69c5b331a04a986b1b63dee9c65f32dc9aad08b0a9d446e8f98af579f13b9a7b907188f526888e986f80dab1446f850299b7f5b93be12c1ffdc11c5d2c490c572c0e20e7fc51f092bef9d45c6cc809b562c7e50297266419158108ccc5857be382930374330d801cc7ad9e47788e306cab388159bab65db69d12e7e20e15c0be0e65b1012a9f00bfe4bb7e1839b3c05d9eb633268236ba8ce23d2160c219a70a5ee37ee49b45ea161091dddbb6e80857318822eb3ee5e893bd7a9d0f1412657c85d2b756f564c5b9aeeb40db6aaa7c5193b755d88a7700fcc1fae724180cb5c5cbbcafd3dc02d5be5fae14d985879a7d4cb0a12c13efe28829245446b8ce61c462106a293d2f87a528202e37b544770f69f803e5a23a3f208960cf5757a6e37b0c4a9250ebeea4da18579b306fec6a671518e50516f404ddfa16c141b76f513cbb2ce5feed5b07aa7f3dfd1f75333f549cfdf46f87794290a58c257d904816aaa7fd090ef936afd1f53743ebddb9f04af0d6221238eda7b3bc010c22e35e41f3b890ee712a444fb609a8510f31d8304b5ee02946e40fd13030232fb727a5bcd270b88cd85fbb31d720c9f3be5f8ba0ed7f2eb72c7ea86bda67cc249c0ca7c4a3387d6dd78321198918eaeee316d230aeba4a655bfada9440813e4fc83ca4430a46b47f32e09c00895ff7ebe3302fa9e5e1873179fbb7565a7ffd0490e658809f06a05d6a50c1e6fc6a0a2c9484c521f46f10f47252831e35205277ca2363acc63fd77948383888b8cc414036072010a6385307223ed5c224610d7b287dd510c3caa4cf22b309dc82596ed41c2460eed5abea0d44b87e9cddd2645482051cc10d4a9bbeaf2990ca1f9512b867317948c66600bcef8eae9a996fcc2f7a07e4bf72d0057c75249923b02cc5e9757ed62965734542d62b2cfaa1b65be46813c8c16b8a2a5cbf04629a7db8a442296d8d57999a565bc339a845946697ad6e26cc3db0c88a84c66464c68ddc2c423ee6da6172018d0ad48351d1721da0af7c686b7a16b2ed0f81d90e17823c0fbdf078f07c96bda06ab902d51041d560b51177a8e6892ca1374436b57239d53bffff74ca53c8edf7137b5d2e646a11938cd58901f2d96b994683701d85d8b493ddacc80acc52d43859222ffcd753182a084bd8624cd9408e3a47256c9e69b41d034f96e7dc5c20df03ace238a38474b0791ff93fa7dd132feba0d6780ac8b608a02e7f5528c2ba2631053d90f8a74e077a44965f2616472b056b4280e62449344eadfe9b408f6d3ce78bd5ea312c2c85c16fdc82484e3efc98173fd2a7f6b38401f7010eab6a4558b12c72d20263aa8a7dc5dbee5a07a04da7012c62846d9dbfaf2cb3b18e6dbb7862a643543ce6a46e8f819b81623a8e392e59c456133f0de83c52334e21aca879a53b83ab3bd36b7a44c9dd5f6ef5e0c6010ebf0d5e9b92168be0089f6a583e2c45ecd259834e7895b6441dcf241f7ea3608b69cc89a6520a92270f666f07aa7f550af8c9fb334b6ea69554761fc304c396e5784012117dceddd571fc7f04ee632f23a77a60a21fc5b53681615b7a52291f1cbf42bdc6a7f71db7313a245844f91882bfc1db449f281cf3f21bc4c8a85dbe85e79fa5e0ade8c10fffd1a99416e1244383a62d94314af1f1299d9e32c9ce68f293e2a0d5258c427428ef80483bdd6c774889cb40dd28bad36eac0a36dd05c050a730c926b5e3fc1d852f0877420aee2a148d686dfaf5d7b0600a591daee1fd081cd0a7445f26592ef94b89986057e525b00525a7fb50dc6b5ce0a5d08bb5edfa65ea87e23828bca55fa5f5b0740fa5d643eb8038c2194b6e9c5ff8cd011a378ab0284c312b6832987e9127cd0c25a4eed2d0439599a6a413daaa572c69d36c74569c300bc14ca8de676f054dc11b4c6b31ce276b0868ade8b3307eeb84093f21482fb2b98874d9d94e2c0724bfb47be8b12c0350f7520beb20f0a1eddaaff86d64b691c00460c4c50561cfa1c984059cab711ab5c3097de83cb22bdcba3b1c8c50fe00483b5fce5583850fbb9b3d078fd2762d828dac8298e9041c36df8596fe32acf6a6a7cf5cc22c051e3b978c112e312d90a77224a74b975e8ae23fa70054277c7e8c215c5552c334c96fd49b88e3b8d9e2403c4c95ec8cd1494b6a93c9304c08787f2c3d5be29564b470c0c94388ddac1ea207d093fec6d5378d8ab03e75267dcc567940d75f3331c3537b76eca5138692f298063d3118810f3ec64f27587a23c40ab1adb33fbf5cc9a2ab36e42cb314fd314becb02cfb9f5e9af7e224438c6b522c02b2682f910f0ae6adde730884350871b8b15269539b56e1686a7f8166170b452d2806e6ee2c7c5283ceab7318800dbeaf0e536771274d2bd12e9eb2dda2c4d6e57406eac35e711bbcdef1f6979a9091165cd56a2f1cd6103338eade386104acbd8996a42d1ea4d71aa70f095a6a525e20e67745b9b9788626e84265b947b734e7cb81e47b7d4172da13c0d119ebfe9b3fb3cc6c9f7ba68016542bd41b181453f2b6d05461d494d8fdfd9a4e072159d5ab1b5b3fa45734b3b5d839e48bcfa74414f9e17ec0d4312fd957b782cf07a84c1706c8965a2d5ab460162c3f8633a37dd089a2ae14e527f14d14b7a52615f39865b484f7c91beff89b618161fa509b98b5a5b2512ddad0633602ff4a511fc31175493da7c7349c5a51c724547bd14d32428e0140e43578528ae1b3f717d5368ad9897bc1c171a326229600511aa07820dbb11e1402d0f9a592b5e1ea8ca419c086981f7e0a03e4e4660e9cb5d623ae4d6fe5491293c3d0f98d70d82d2588e681c670769577c7588f5838d2d7d2112b3daed83cb0e61a9d0ffb23e823a6cee28b6f0c8507b6c1395d820a9513fcfbe60a10e339be635de8bdabd0eb6182fc3084e9e6e1ede1edf1c75720362e954414de9acb591665853a434d1be9743825efc66aeba9d74b004bce8dc7b06139debc21f144bf98ce34311277c02c4f83f8356be55c6400394c555a4554ba96e6bad3b46d446a2ee9439bfce47cce50da82db364eb036d659ce76c5c571cd7473d1661928d1c8f7e18fb7ac425955fdbf61d7c6eaf91792d8fe4a1f72c43bd3fa042ae0b63819c8750658b3c4c68dcf647a1156c0042a68210bea15568f0745750c2492539fdac32899ed724149030203eabe6e4dd4c58d2adb6f5da3be7f8a451204ff2e0f902d9fc60d227683a70ace86d729044c13183edc91821ae1d0d63ecdaeb2f938585a8dd7309fec37a00c3f83ae439b63a4541995d7dd34272bb02f50974a0daf5168ca2b50c0f9086109f5bfb2f353812c152451c5d8ef8cfbaa57b12548d89110f3db6c3816830063212344916e3e6545e5bb1190661e0e4af5c0298fc82d07dc7a6f4e5ae7292a481a49099905ced6aa221bb49c6a8966a47d2540b87d043c443f0dfcd5ae8cde2cc9ce4784b4f7a9c02394962f228f1c215b691aa0a4320c3581db8ae4d9678ba3a725266bf33342f024a42cb7b550464a0b51b26b88997f6c32bdd397e4c7044ab0a56fd173031d5957d3641b96571fd36ac5567085530a1e34a3741826611c00d1aadfc522386345a33551b568cd73031bede4098e613cada04396b35d174120d245f382dcf04e2128df9fc69b301fb7354f6e8a8c0062a0d657d1775f4191eda886e783ab7dbdfecdeaf3cd348df632a04046ffac40ffc295aee51c8354b33b9eb62987b611f9194579111b202fdcc0b784b4a216c66bd5ecb5819d1eb6762d041eb6c3791896a68bcde6abd265dc1842b1c59f59b34f8153c2ee709a904858d135c1c2868ede5ca69509fa09ce04ee7b328743de3b7abcd5132d7feaa343317f361e9d157da39afce2e3d79b0766e44c57dfc2d54e12efbd23b0a1d9a26023a24196b99db8192f90d9202463758a259007d615c3481e9734e7eb8eff7e7b45a25eb08758ae36cc15fe26836d000278d33a61d0e06767228280bb6630e3d4762b17d5e8f60634ac5e334594c0d966d6273014c47337e578a2f3967bece64658ee8941b3d7fdc9478d685434796af449ba6e890a28718c84d079c4f4c4143d14b6b54a4a65e8f37d620851479433b6b419e350352720b527db65254e7fb4cc7808e9c39c5cfb228bb44dd93cb88e2bca1190f8d5c955e495dec7627a95e2d4fd1a1ab4655378aae46ce1b7cbc6b7049af853eec178e83f2462496e9c7dfc60eae5753be59f27675e679ae45f0bafb79a2f79d039a31db05a55276ca2aabc503e57a5fb1df97108eee56c9ceee9dd390bb69a6c8260497e272e0d69c954ca325c1c7395699ab8c23b2ccd42af641dc52587794de9f09ae4d7bfd148541e71e6aa539faa6d03248a5dbb5ce1e3f756f5dcdde3e00c8d43f5bc25dae6031a1c2fe9151b6bd4e8144c5f235ba1d86c99f4d4116d74181a943c70f03565399a1ba1c2c35d3d5f801e3fcb716ca5f1e2a725c7c9186e47783799e0b95e02eedc2794df2d2c15a47bce85aff6d9a877437caa66b6373f701424e56a77648d13b2c11ced9119faec1ebf52f68aea72a7c2ae7ee21dacae95d8e9ce8279704ce78c98dd627837348b59db9052a626471aad23bae556d666bf5fd0d85c019c37e6ded8fdc2430d29dce54f96cce1fa4e005c7fba4697aba23f327554080ac66b98e172f73aad7923012f3374c3cd60a63cd41bb6bec2d23116e11833a2cad7e0fa9cda215ade52c708f8c5b9459f84bde370b9a370a8105c8e9a4e62df855eca165faf60771e91f6736038ac821c3d9ab8e8585f3478d87cf2f29232488805c0e5be2369ce222ac5aa61b078087674335c1a7139f056a6d9e6cdab0ec79667bc7c7ca75c58a781d407944251f3f122da13f99c1d02705d926f5db19f51678a080f51d3d47b9a24493afc5f7bcf471a88a00c2ec70ceda390c44126f15c5bf2c3ac2292ae30efc10f6d8d66b04d1e53b0dbff362f87f687394dfe804aae7b0e4bf1633b926b02ef36aa360fc9aab684c0f5edf9ce76079bf5fbb0c4458be3b7d1e96ec6721870c0ad6f7ce24f814de0edbc98eead77038405171783dbb6b999f22ce7795d2e349e79eab3db58887e88c111de977100cc5c2b66c598dec0c06aeccaa2b812316e56622e9d2c75eb13020fe0851651d8cef09ba003a4fc55e8e68a93b4fba92452c3eb134df1f6647eeab1fb6726d78a9be01348389528fdb1162f249f7d094dfa579ae665bb0b7e2a0d622dd28d3f3723cb524a8bd5fb1d895eb2829f6a0bb7d86cbb348b5b90f3e660ac493329128a05974dab1ed222e257d8a3d38fc1e2a0ea7ab8306c90180e260c0d2b416191c1c910f8b3d3b4c623ea0237b8b54ee9c31c87be5350e07f24dc66b4d88017a55411d1a049dbd53e4bc9da4e7ef5c510b2f4a560235a572b2951024956965d5b6853f9a15b0956083ba751ca7ad0e7cdf67134ab2e219da96d73f7ee1addd061024ba2f92d5c7291f8f0eae112c362a6ffdeec71281b8c56ff661ab35bdf49fd0597d518d55603656615c7e54a2219b7a04a3736e7161b8cc56cc1d60453eb5a8b2ed8f1cd15572ac83dfc60c52d392c3c75c25ea1a7a9d9a5fdcb9b06769fd3d468ae50d482917a35212aa7d2aeb6b86e5b910239232eb02583c7dfeeb3c9abbc57585c70095161f527046926823cebc54344ae09b96d8d0e159ad2f0988e5fe6dcc39b5e0c39271a350a8883e27563f945526a7c9d97ea55b197534d711ae598a29249842c07ef3daffae4e91660f751a9c60d820e534391016146a602eabb382b2e910ac2a7164865282f654188df0eedc1bdb80b7babed00035b82abc20cfc524828e36a1d7249789b24b35000d9db09f20d2e418e51551011da8bdad150f4540650d3268cd23696aa7de110526629038736d30ce26150eea9d51e2386cc46a1441cc4cafabf5b763443071e5730eed54e61035b195e4e23a367c8bdc434e6400036754caedb6d1e3dfa8e46ae4d536d99980b04291c89328b6d9889533ffe9bb817d42c184c481be21430a71cc7d5f9fffdae2ccfab94ad3f49c401768af5408ae736641721c82a9a09fb9ac006d1012d88423d43474595385b7aff602dc0f96c0911b892481d38121710e6e79d7fa09eeca26fffb05a68ed73625a6b080636e4f9d265926dc1c55352c83f6ac48c826eb922842511d1e12f6b842c23cae7a6ea0d821d38b52786d79d882c885a67e21db514627e3076ada56f4100fb444411ff24c9ce94c5180e4493f35f6f7aecd5dcba5abbb0decc97020fd0067cc6b5d170a6b657306d2c7edaf9dc04317c423abf0039c4b8bb43d943b66ad43343bc8e1391b7f4b858aa438fdc6b81c386a29e61b3e9cc8370fcb18c703f58da4d8c19fa7c0cf409c77ba199ea20f3f733fbabed58e27cefe6fcca54825b470787c4b054a1f33eefb6d6d97f6279669d9837fc69740d5da52d6bdf81ad017187ba206d77e58445a1b6f1786106baf719f16c8c586b66a4c279b7fc93634da06ad907c92784a3d20f28a172b94382293dc3a462d490558ec0bf182fbcd2eb7ed31a6617fe848d4fb3abc6b54d9f51a0e13dd3ecf792e8b8716835c10050e90a78ada61bbee24ffea8d6911f0d9fad26f2bf7a01bd4db543a477412b65968dca7edd7de752ac5ee2fbe4ad010a507df7f472dfb930cee6eee5ff68d59738c0d6efb0bd1ac9c3e052a29fb4ba05d7ac3d793d6283a61ababfa5d008e94ad82d45f95e32d6eefa8afd694b731fd5890d0548b6a3617b856b994b8855f98e35cc0bfe855f4ecb2f43da88fc2338f16bbfe85212dbfd92361d774529d5547944b775d812694e72c456fb029e47bbdece491cb4acfdb0dfa47d9a4abe1e6884a2f56c91c556b6d7c71670f5502c3d72eb9bc80596b3279b1293fbdabdc831fc00145a77af901285854f8427bdaf8467d6724a15bdb79908fd2147a4adfaa6b4c46a74cc0c4b7dd282405418e2aceaa9eddf1aa22902c3bcbe4c5ee6e0c2ab1d6a6db3c173415d4bae7bef7e3204bcc45464737caed9c7020b015024c0abe8c1b20c3b92235a3281b9cd40aaa2791cf337bc06454e2d9ec42689016cc0bf01030cb2a09057f1ea097a53aea6edf51728e370e609c5b632e891bf0e4e32e2ae1921a34ae17cb65b1490ed45b999d9b6124dfd584d27efeb503fc19d7f24df9170ab9165fdd86065b8599abba2c9777ae6d5f15833e37cc36280419f6c8fd3ac93ef9578ed0f62565a68eecc3d6df54c4dddd61826bf4d8917bb115ef8ce17c32cf187617d2db5994fe05ed177e5df094987d0d95128d6be4ad78ae711cacd744599f5a89cb1a73a29c41fbf541a120b1b34e80f0ed52f302975c2da4b018205e6cdf4eb5c1a30de4b67ba4f850de94313dff029b3066553c740f62da25bc59cd55a8afc6a4bf42aad332601ce370dbdb5777107225d6b74aca64f0e94cd9afb596e27983c9c2a013a687e0685910732bb89871de6db907b63ab05efbc4196c6de1d12a7ddc80e590f7d295724f0a18824200faf18ed86832e83476151f128b231234ca08380d2037828901c08e0480fb4753afbfb71f47756d6016b68c170880330d107010d079c8ae3fe51eea652414a0d4b1c594e49dfa9f0fdc20000f25a31771ac1857b0c3882a188db25837938cb51c980a593544a628e7a23f46ebe371c0ee3319bd88752c5d78ca58e2a5d4c681fa93d57e8adf410b94ae476d855e74d72f661206b73839fa50a65285b63927b9c0b9fa396d776ea743cc8b2602e2a3b9c9387f524ba722e3e5fc31ee45d040d8d161322a4c11aa6bb86a8d4a6f654f08a46a8620850a5883674f1d234664d8d8c4058c80cd98084af9a92c35b36258353f60825544f374d99cf8e35cd14dac08294a3a9abb105973480154e0744c3ff7eaf4edeca91b4a9c481537b2d5a5e70cdd3aa7b75596ec17b328cbe0fc015b0aba368eccd44821e8028365076d49bfe63ba4eb8b7cab5b0dfc3bc84fb034eb569a5006c3a982ebedb178b988841d1e94e0fce079458bdfbc002f17713cc24aa1e098e9d5e0862304f018f32804abeba09d743f855d4f077efc32e8d10bfd86c0213002b24c47ef211baa90c5d1063b0050e068e6f8c01279cc7bed2317c44361862ed95ad74af6d707bcd3447948f1e10c67c3763a1efd60862ef6e6f1ec03d9917730c29ebf2193744ee65839b22738d241e3bbd984f0cb4623f2ef5cc1104f388ef065aef11661c8f00b17ae59759ec9401fadc40ee3c131644050e95c0715bfc567945c6ab3a86ad10ad36e472d1e613c7545de3cc9393224e6a91f6f4bde4235764420fb7fc1e9912c9873909c9b4272e1038eaf2a86f902cb164338cb896962c4ae4bec4c7dc462c33fecfa5323dfd43b64b95dde43a3a00a963e91bf4dbee1fbb3768804194fcff7dd508395179ae96788d9978a352a4da83719fce6e4978848d626489053281b0c6aeceeab693ddee3c17529df0d0926e67a7ae12b9e2609b2a705a3e6e0279009ab183dc81a8c800642326abb9e719e874196d9a457a52bd0876378deaba73dfcf82716298f4d670ac883b348ec9a7b8edd804aba980a4a056f26add2131e5ebedb7b3ed5a5dfd0cb346ccc445db5d9b864c5e27af5d2ae4a687aece667cb4ae7122be071745b6d570d5b47d453bec9cc16b4406b3044632ad7ec190de5d22f0deabcc71f10d8fb383fbe73149810170ffb1526462315b2b74677c82e68412eb36a63c2366ee35c296df723dc5ef0071a8d2ed072c704e667a676a92aee25a3384a23c52b521082a2f20432d34ea79f197b4cca8ab89103a9ce6d367860e418d8b0c363d33df6f901bea81addf31c5122e729cfa92f119ad30fe931d8408b5c03809f43d63aaf663400897379c5b00075d5de9b42835fe03d554432f91c7cc18e29c824da476eb0214ef5b3c3d5a31bfaf2f512227a762d449d283660417dde22ef41f2bd6185f0ec070017d6264607d1497277d449d0a3016a490ab21bfa364977f6f77de0f6ce5df18c253e3a6f5f6a2b9ac85f46f1bcd7a84d8fe59387bcaba030c25ba501ae22d9af8195163d585a03761e7877ce2ae13cc40486a670fe0bd0850f38d36a5c563070b1fbe9f4c24b4dbbcd3e5edee94a8d6c62782c803af53f662827fc203e28a8f9ead71c87edadfd6bcbdd879a15d3e7f11caa3e943dc9c2e519ac6aa06546985898b7b0e24da2921c3f94a1d478203f1bbcec9b7bee203cbec106336fe0f01907e5003d6221f303abb7dc5615bc1284bb10c95669eb0167141ed3bf03aa9f5b61d1f5233f0b602f7648d1effc0bd80afc0e01d9996e423d3b88bad6f4b0b4e9fbdcbbdb8e2d91caff6a8ba374214896da820a69bb99670247aad840d24d82d3bdf3526e97061b8ad9bf0c26f855281e531edc6b373aca5207b15cc4aff05bfb83511f5043b6ea831a442943da93bdb6fb76946dc7e5be8ae608d4c532452784ff71ad992ea223665a53cd80fa36d3c5c7f28dd311b7e7e0da3abda71de4fc35fb10b808abbda6bd22b5d053a626abe42ee127f17f125bb8b51b1ae286eeb7293045922af8fb31d5323ddf9dd91a74c2f1418cd30b0ffd016f929bb119c0b90531f18211a098681d7b1a0664213671fd2e674bf88f284f6819640b637b34193ddfc17fc965b6be59ee1ad28caf5084a5d532f8873c0a23cd89df6213bbb07a268bc9ab2400d0e886e071e335279fb44bda3244270149ce100f55f68b27f899ceb1bf187e162cae13b5b5f7f91d66414afb8d6967a5bdce752c55f629d2ccb9cb572fab301c17b359ae8af51b68552b6f27dd58c72484a36ea19fa76a1757183b8eba871c1d24a27dfa22d789dff234f2d3b2c629fb4262d9bd6c81fc4e520ef60bfa4309f8feb55e884f1fe473aa13ef25ebcf69a78a16b35b797a486f87a22ad9cc1ad6a9937f9bc8a21b115140a1f416541ec6a8c0db6815a16c3dfd05df42ab76f59452b99eec1e22c40e1b66777b946871769806fc14546cb966e6f52eddf0e6fe6090ec924d41748a47f239dc4975fab63f65ff7622ec87453597a272febeb5a2ef8176d44ccbb8061cab3abbbbea82fa4625552bb0b6d58954e4bf51dfe021cadfb16a017143cdf86ef369a89ec7e0933f0d089bb6e52d4af5d4f8be4b56bd0b1357b3c22606c494b59995bdca6eedde40b9dea5f5b7bb281688bdfd27bf4093582a1e1c5d295a3710b8abaf47bd72670855f365e6ad8b08ecf7ac65da04aeb2cb857909ca7e61218a8740bd4f13a99c2ea49a7c978d864ec179963b77c4d7139b34c12c41d17420615d875b33a2e5a0a0c2d68dcbb8b8628a13d533fc69f592cae70267a2bd44f17fcf82bd276cf56c80a2b031de3968e0b65005521863dd54433ede699c6d986b9d6ea0660df78f02528ab9995de3d0363b997760cce45bd502322e4d233a404b7b27cf8507b853a673907bda33b1dae81aa18f01ae59f19dad8fa5847e20ddae9c6586047cab13eca8b7501c34b86e8a843ba7d059e201248b266ce6e6061c91e9098b1aab6db4013fcaa10184956a164adc19a0af7421c6e52a4eb3c2df52dc150d8ceb3a4686940d99767ad06f1d932e21938ec3149d91987e301a692ff96d9378c47f1848b1a0071a3a2078f30904ba1bbad37932234715a94e42d78296bb7d640d005650801221f68c3eeaf64d9acf62ea0a22a16f7efd770e7144ebe56f99fab7c32b94b5ad0328f34623a926d6c1eb381045a202c8a3dafdbfe32849dcc8dc5dfe55fd207cb915e040033af23c228e367389b595542327a4da3f5f0537c286c3747e491858972c66c018a36017a3d4b5b1d07f68967bfc472854bbd79970dd4a08e3712ea8a6260c39839ac4c76c355b12233cdd362a5b37b384390f105205866f02e35ccca25090fb5511919833ee59bd69fdbfc2b2c262f5afbf8ac7ac36656fe4ee0098794bc5b6e135dcbc507d147c7fcb1cf44f2afda555cc342e2287f0027aebf800e1b5b99157eeb86c6ba3a1cd7acc32dd71a410ee22ad1d7e0c29b680fc41a68c57b5167b1c1ffa2240eafed28878d4dbe8f2fd30f14957d92e66b81de2cac2b220a13cc6dae13518aacef0a6b2244bbfad69e576675eb3cc13667269b6f80305ac7a6ca6be6461aa55c52496a4e456cc8085dae2afa9447d350af55d0adde140c1b6c290d7b4bafb83decd0036753feaacd3c38f99e70af2d443ac7cb01c862a976cfd204626fa94f81b3c80276253faa1483dedf7b63cdf16c51a2d3e122d32db16e6582182311562583958c9304bc10b8736893f244d9a7c6e79c2f26f2d3600943f0041b68c8208a9707ec928411578bd34940b2655c38a75aaf0c7c974e44a40f91b6c15a4da5499b34ddf509b168d1c91c557f5e8231b5db4799920229d9682503e29ea4d9e80049eb01873a6e07ce3d4a976b1e4ea45bed1f6938ca8812c49d36ffdef5f089302b5ba39c1b3f4ea12cdd5f8944e2dee3600905bf4f652cc7575305e3b584ba79fc76f8906af38cc4623df6044dc802c8c16e7534c35af05944ec5410472c75fa174eabf1400ba12988acb9dbf356ba08a14369941d2a91cb6ffe21ca2c908f7186a1457501d04111cd83feb9d668a45b8e6969c3648329d1a2f0d3e889268d4d13360677ab82163dab0f33a715314733eaff6d262793e78b8638a16813ec81898f74b81326069c6a14b198b7a7204886d92079febde7f5329fc0e20db361ac053faf169e4cec0509921417bf24040ebd1e2591b857f3b03a5d38dc1711bc967662102993eac05079097fc9a163cc6e7cbb9afc4bdbe46aa662215640fb1343d971ea7bab7a93464a7dd00874b28d854d631ad85d6068367d80a5dd5c85431595f13d4506ef9734a588a949b011d75424041413b2c9d6077ba42e232f21fa5bbed9f48a8d22e420363cf30a6c5a90870b4ebfc92a5349b675802f239832ef1ca7519404a5c22552a80db881704e5d311e0e5272c99efba4004e281594ef62e792f761e1eeccdb1f411f3dcd9ec531a7c4586220fab4cac3c710f7d42be4079ef3d7e0496ec67c2be09c0153f79e81064b8601a8ccda0b2e3cd9b8940006c6331dc3a395b0f60a41d4f2424ce616e7b75fd675c04e89fe91b42ed0693a0b9f03c0f779ff8056c9e16078deb3305f6c462ad8627b3372251f8e7fc6f0352bbafb8a5673685da284a793e3a051a361fa99d5a5113873f4b8285e189be0d84fd967029e2b2741056b1b0243edfb486d1021e1cb15b04b3cf8a7851f7a6fb5c4e9307928924068fac500874d6f3207888d35aa8ea7d72c89aa2df72ef4bb413b7625e85376d11afe64988ba64170e657ccfca52da956684bc6db34fb062a73f36873f6e987f6477f725ca5921aed170eff2b367b03ad0bd51374c299068b0b0a78319ca666a3cca8a2f42651205beeb86c42f4f536a3698d598705ee80637c7473fb93c409dc1b579872983839b972509ba56525f717d3f224f49707083623405f2df1455abce5ff2bc144d80351ee778bed1e9f40fe5e3e1b6c5b43d19c56b40e38eb996a0883d027e4fbc19bd42b8e71ddef225cabd598f147577c882dbed792674bb9da34e55c1a6f455c51799ea069cf2c853f6d06e11c3b4313315a56ea2c4d2de2f6eea5c0336db48f941e50600c2e1d0a5b1197f1cd450d2f4e24193229d0ee19b9e65034fe22e021640aaec7b35072a027dc69b5ec959a81622cd01b6261e81d106c316fd5642338b7fcb978f839e19342e3f20ba887c98e8d9facd48b8a7fcaee0c768b54bcf2838fb1d967ebea2b885da0ea434f32b0dc36611a940136bb1a8ed2514a962ffbe602a0e5a2ef7399b474aaf5b18b380c2be993368a931339b16b6c5af61807ade14b7909ed04b63fb8876edc201af2ee17d59d10cb92ac217be07fd4d963c7ce71c0c13be8da859b84eca135a06057c8bb204d7fc885b29695911ce1782e61f3751c6346229defd5755d8bb05877a940b9e67c76947f4ced53a28e1615677f8e242815806ace340571c8dcf9c69c45f725f5b8f774a994d458725928688b1f299a7d42a7b80785010864dfe5270e99728170cdfe07a860986d4cca7940d14b6873e58527f9b652b0f1f782a496931dfffc35786d2608f07b447695ddffa38b75958f1e28ab24df1bbd81c2e6354a741688e1f0c14f5fdb7e165f2add7560dc3a6609b6853384e86a76c1ed73dc3b107501c5605f85474bb905f1c708bd3368865a5be81f6559395368e6dce0e7b911365aed22c9aaef2aad6382b8ef258f9059781aadcfa44e7998d3c46b3ecd9ed52f243817514c02524ba4ad4cf1a7a5486c2da7079d16086f75613718fa10929c9c9fcc314a8b769158cc225025dea98b11b83940ae1a1500c2fc414f3b4ff438d305acfba70fe46797c24bba0bfd71f62115c1317e1c2744b7718122f03244bfc4730cb23b19511d3dbd9e90e2cfa41dcb88cfaeb54e602d7ac20f51c8146e8c4e099d21bf3aeabfc6034ad85c5a2ec04cc0e368500e8b5acdbc9e7a70e2f507ce393bbe7bc7d024ca6ced61d8c71772cd58cb3145557b0bdf57935596fb4e6e1b3967d74e9f5ffb66b9dedd8ed43ee7d5a2d2404d01488594c78ead46db44364006fecf8dc78864494980bbf37e5f2a68b7c90accd5d55a01d2c12cc5f2edb493841145180ff72c0d909c4a404a51d11068af52d18bba1a41617876acd37d6212a6afdd4d34e199f636e3fbcbca16715da7531cd2d1600f9c1d3ae07c55bf2e379ce0d6ef29eaa49e740bef6b3a39746295997f140ce1359d0ff02fcbe365fcf95a11fef7bb14b60c53a0ddc80e64eab6a3fa975f52392c6486ca4c046fe860a1bfe1277a3ea703c669368e99c340db31afaac4448596f3ab3a080941e29564aad0fe6f80cfcc47eda2fdf3bb328eda75e113e93b8244d5f4899d7a4f7a38356cdaa6f38ce0a325f0d1bb61cad998090efba6ba6f6eb257173db2ca28744f70ab2393f94ede8a10dfa9a10ea3f839b5aa1c2d48d3911a7ab6434f46deb5b7f91c12d2bf32f5ec089948e92ff90e086d600b1dc6941a5556cb6af9eea1d12d44e02bf51d218152ec6a4473d77477118f6256148fba073a9d015288b85877288977c0b820ee2b8d23496720485ef2faf66939a004d272c079208e63c414ea05ef7440c7578555925a1f6472344e144733fc23b84b610b2bcd844ab41729f7317443bfe12cc44b59b84dd5e065794def1635d2398188533320c608b1e650bdad1a4bd160461d363f22b0674fdaf83c9c48548d5eb0970dd7501c74fb5c3c1257ba3dcf7a1d8117945243f5ac53421c0accb141f8d5f44bf49f573424eca44a250ac8d26f39ec4015da43cbb11ffac42b1484741bc9d197fda8e55002755e3677428d9043bc536895e02f8d958e9734501e52fcba999fb978bf4033969e7d8983378d2e279c2d906223b9c914b31e78de12198e1ee853fe27ffe8f426dce50979eb2293080def6145d86329bdcfe6bec35a425eb1d16f0408bf82ebe4ba2910f8c4f1a3a8e357337be1022cb350c5c5f1e978067b25ce3045dcae2bb4bfc8ed13c3c7e398636b7aa25ffacd30c3d9d79797c60a726d15eb0de63e83a54972002323cf8ceb3ae31f9b2cc58814794b27000eb2907d78629553c91cf895cea3388b9fcfb331a217cb5531dbc5926ecf5c87b7615cf9efea31a78b7dcfbf4e0240a9b34e78d713ef2c385959f2567bd14c50b2919055461e4e3e0f8c492b7a0f8a64372d00e59e1344bc0bac6b4330cce8c9f637694db584d28703ff0da3208399c98e5b36a0ce166a15ceda81e733546111a83bd8fed94a93f1be38a659ef25befc183a2e02b13e28884764ebd625e65cb22b0b96417acd4b7848509f008c8386e32dad9b570e5ad94dce6eab606f3b0da19b16845138a1a24385d2cf6117b940d28f0aefa2454aa2197050637758b1603c0079254cfa38f283863c3249a1826f1c3f057ef8d03e65452e02911ca1b378ea77bd227ed397e08a64c7b32bff63c7904952b0c70caaae01ac1d9efe59ac5066dc263052e9b5697255a1234fe35bf6d8e9cf13180ea305730d7bf29f83779ea9df9533155da3eb482111fa2b363adb6247d3fb6d0622d35379f242fa0db65c736067399a28a2c3d0afe6282101836e251ae9233e1a066c2039536e6574e8156b0d3f997990f14174abde6921fe6cc53598d9db4f428d443b730e766f5f0bad32dc12c0ada800c06d867741646b8d69c592827305559f8f16698cdb2c8819e398e3ea46e15983c8f2c69a5a401950d1c77cc619cc16770fd085353be493a0ec60839c4d4be2d0bdd1c3a317f2437926c6c5f5ccc66ae554718dab570df96569881aad8cf5c7fb96ef5862001ad068b1a7ec4ac1ad097dc1168d341fab16b22dacc1f40a8a10ca2b9f02a215089dc51ef56f71f3cee3f9771b637615f8fa4d51205ad308195f1474b8fb51bce375d5c2e50f780c5b6e2cb36c20350026a95a0d423d67fae4fefaa6e620e2aab207e9dec47c352f6ed439c8bf3077cfc7e9fef03a28bce2a6293e58838c46a534cfc0191433379aacbfb36298a752be9324b6fdfc413c4353099e4bae7ae97127a85a6dfe158e67903cea6582c3251f8172c6e79b2ea7efb25d8a11b3025c2130b56bd6f9f59d237fc5346ac72e1581897038bbee855687fa9e28e8179f3819a8722d0362dc24416b368f2c20d3599371c52688a453455fe5ee6e78b077c801d10a9eca01598fc61752ea0ccee92a5f919b30d176de69aa7acc8824b3dc260a44e88b4739b48ad18e61234b858aad09913d0e7780d5c9c49578c1488d1a360266f71a3e122bc858cb8c5f2e2bcb87c81398a2575aedeb411d081d0c464352323c1342f3673ebea89ee341dbcd62886d0b758fd7e8bcedf42b63c37e7b840315eec82a762f5650f05c6e45e2c7a011b55fadb41ff059010858b8309fec9883e2159c61b260d56169d8e66cc531e941d0458825a05c68ec9f3dfaae4da04564192b0ac92edc4ee03fcbfcb558b2050027f0a53282e62e019acdfeec8c03c57f4010437f6d612228c5e870cff4a2fd8362d3994a3ed41775cffbf1fd12a7f574562545c9e1571ec39de7a2846b73075553cfbe126b9f048b93dcc8f9a6fb0a401efa8cf78a0419f41c44a67d93b7593e2ca55c358fd9f6996999f1a23f0b2369cd23f341ee04ecadb117ee50ece0e227880e3f0ed7ce2056c329a9b9dc113416d10892aacf9fdd482620a34e0c9484e81909caac7fa602cb434a8fb04d89ddc49d5679fc1b2ebd870137f70f1cb52838781606ceed366c440531431d0580902adc09dfb86190b5e2f6bdd871468e5ea2771c472d04df030c72834476bd785b5a77205b186554961c14fbebcfea2040175f93be66e4306ff7607d0ebd5d43371045e452e937e38f7ca6326c3d4217c8e825f9413c1dba19846c2e213978bc51676c0e18d69147b3ea9d7f10a7c54ac84cd793e4d1689d01d168c4b822afd9b1d1713ab6af4bcc6e6a383fc461e617ac2295e58d7a11b4d16788cbe4df76f57f90dbf28f98b36ad3dba7836a817a69a0253bbc4d22e9588d2da429bfd56e47f68e58017cb657faa8b490d0ec97da6dc9f95f603581e330bf643aed2edd3e3da1f95fb6dab48ad8844926e35588a4544829e0c8a29956506f8c7cd44aee9dfa6fc61bc361e1849e6304596c57bdedbc85294122039d8e02ab85f04db13f4217d7e60e65d854617ab9f6a0a47be17517d37bab3ec409a340544a0a26ead48ac4ce540c0041027878c86065abe6a83b2624165e37cc28434a156ebe783477c53a5efb2981fa36c20427cad65c8814abb2b41480648d57fa0326cd86eda1c142e4f746bfeeeabdd4c42c8dec4ec6478aca2ac4976416f845f7a41c7c63c1db8611d7b69fa094212fa636b2075e0b47a83eda80faa43d898aa3a87f88f2545889dae64730a6312c63d24b6a3ca383972e7ad813cda910873e06928e9f48008c6421893455877216151f10c6ba35aa05f79906094ce2d2935aad54da09913f089b3dc794bd0ddb3d7c9733d589790c53d3bac29c46680f28fa5bb42f8cc3383b35fe3e555e6e3b17b0b1db4d34a45dfffc29160f3ee87a04d51af7b5a379cf6dbf77dc837e6a04c2593012db0bd2868662c03f8d0790eb72a275999c9280e023732d1dd714a06e0a38313ef87aaa0e211c2fd36c181bb23b9f146d0a2f632b6a0435739016bb9df46304c7337adc9b03eda169893d4edb0639a8e4f1da04b998da5e644c5f827639663a41fb43299b3a7d840b36f44ac7e83c818ffa537fdf48aa5da199ce009abc71897f717579bdc0854ed51c7262f450d0898b8586eede0d2d307d7b1e07d727498ac5a65a0a45ab91e15c1333f055e9b795c50c76388825b4d72dd69bd5b37091a2f10350517c5b09242d8cea965a7239d421909ca4f0bbb1bb48540d7bb3f06aedb02092ec8afa13748a137e0b7a02b85d84c955c744d204b9753d9b87c25f7345ec1bb5aff7116e006aab3b215d0c1b64cb632c0f989ade41ba3404c236821603b9b427b841bb66895de0adb68e1d33553f51a169ac9579169ea0aac0878c1c46de6630abfacbcdb4dadb5efa3ff8a264ae130c426e916e74b98dde97b2c44352e049783487256538dfb5619413d2c67787e42322993c2cd107c4235b8dce71bdabd2aca9169a9257d9e7743dd3a2efa6bd87e532299600135fa5a27943c48296c60cb3e2bec379a7cafcc8328af18ba7288aa6e963a9dc7d2159c05843936c59756eb57ad63483e26265e224fc3e90c937d0f719c26bf53c2a9bbf6a77b94d51ca73a6556724b3815afa8002ba986e304d4e33fd50927d6f89640f0b4e538515173c2e9bb6595d0e6b71ca77b4c9d4e269c6669a0f7bfe1347a8e53f101cbe54b38dddf7092cf7cc0ca45c2a941516e38a1077f9602278ce7a1f5d677ba78c2029d6ac21f441ac8bfc22ae9a2606a83e4a74ca079156bd6f29437d5b540832b7a5b88df574634f52fe818f33d5c7a9ca4154f44584bb3cf2a0238ced1792848c2349685c0d51687572410c77f1035b47dbbb80a40e14c7352a6ab4b937ba70c47337e0b97fd6a64b1d9a0ca5297a76948b62a0497b3e7b10f4671e9c3b1dec4ac5453e41fa097f482703a85715dea9b06523f1787a2082948aa96cd6f37427f2c451f77197edae433872d336a00f7425346a3b143fbe2fd0a0b1087d0b4958d8bec10ffb644e241f51438e2e352721adc6a719dfa349a7992904b2d133b5dc49c70a6798392dc41351ff5f972c88c1878632d5c26f28f8b514173a80aff47d17500aa0549710856105522776c84dc189b4bc4126771341e91d10f18191741846348bdaf8306d409add0ddaaf0e69ca6f61828393dae6e6368f539427c17be922ef95e822d4ef06ab95dd69f33318c8a0ffd73aeb2844f33bf75e9a9566b236afcffae6a5a7ea6a00b4e0b09ad3e55ac62f177190efa75d4567dd7578e3f7f4ad39f13c9a6241b14d09951bf88b72825d55d46103c25993310fbd11abdf503b6ffc5b9d23f828c48d00463970e2ae8671ea5e4091c1ccfa39c59241f2bc22771acaf819988607f4e5dbf2cfc23e82cff8f282c19e0f127c9a8472f84818e56c09213dfe5a09214d7947d308ddf6b6a8912e872cb3049bf3a71239041c137b76056f6ac95532ce28f4ad06c364f68c2927c9e8d1086d75545f59185e22437a8423271a6b0f8b19123f1bc8b4482849cb9545609bc690cfe8003bf12c417dfe714593008973cf128ca89513eb72108276bf271a8c994684f8a7082367edf4f2ec5785dcddef59a5e46887da2491acf2bf1a41ed8169ad75c253484bdc7ecccd2af1492684569385643ac768c65e769235a94c551a0eed878aae4b36f4a067ac441d8b3d11103020435390aff44ee39c6ce13c6fb147db1dd43e508e93fce27e1a0ea6e25c9fbcc0fa5b3c043337af76560a03ace247e36e8c25b6051cab9d9163e619698137eb891195470322f21efaf42a2a1e5e75385f9bc5837ddab4367a3c6457886ac1ca9b79bd8de68e44b940db61c0b4333bc3fb045eec561448b7217cf770cb8e6d525d3e2c8ab1afb746665476c53a25b1f219aead8bb651f24b9f28b5409d33aa3d6598c9eb3dca24a735735e76175c6ec9721da59a90302d183db39dd35c4cc7d1c239c4a2f6593d08493e1bae192402a197f81e7cd636b70c5ee82689c77c1ada2c053a3a56f704e942113695b43fe0a37879b4c2eb4fe5713d57dec5ef73511ea1df72d30e588d00e766f1721ece23727bedb0f51142211a372e375fe7ffe57fee16eb387ddff7e1ca03ac2408b770a0fdda3bebfc3663e1267fb28b9022462556e3c3ddc3d64ffc6c09bc703ece35a1b0e7a97a2b37eef30e3bcd9c9c64c2c78df71092e72b8dbe7ee8f31536151a4b170ac68e38f302f8825bef49311413d8f3668c6f72e23b615ba88de8ada2f3493d89c4430eadd5f6368a2e403ea78cb762f78db3029f9190b9f0c5fc340c461db9732d0585647b41e99a567b4e513da868f4864db08c5ed64d0f6d7f30a1ccecb7c11ebf281622deaed307116280322f8236f04aa974f48dd673ee7cc6c400abefc2583dcdbce5962033dd31d81337d262e11214dbccb546d06a5825e13081369f7d05465aa6af361769360a13471f116afdd20eb5ff4ab0016d7b6e7922d11d5b4907b8ef1e363bf52ad21bc8b1ab43cbed26340b20bbf2473fe809bca8936bcff444d05df0a04486d5ebd148c64ee7246ed7772f2d97b6d38c943867574b12834e94a7ed1bb0ce4831d5cc431408678c5ff7e0313013741562de927ed987f0f02682831a1ea36f8c581af918a8828c71b4f886066f1b2abc178094137ecf2feb7653bc329fc91240dfc79fdb0d6937e521b84b4cdb2165050b7cfe8c6512bebce2d3322905c4fbe46bd92da275696b24247c674817de1071606d528ba8ce183c340d5d2af07715327682051266a08ddaf1fd3f9dc6ad280cf001a26d7a70d1b6599977ab8fb2255c0fd50786490b4ca5098cd321daffa547deb8186666e6e91ef203bffc22be1df813c87509e915a199fef4fe357b39c3f09f2abe43b40a07ab1a060cc9c3591d648a2fb87c6d6bee746e56e1cfd65389cca5844f97f13ca5e374aa763c4e842a1f5bfa9abacb6b90dcf9565c4e0260d59e7f1669e780001077ce9eebdbff6658e5fbaa8abdf0d7c6cfd2ea19465532b3507e7582b52b5070c797ff96ced61d8203b1e1ac3dc9898c316132abb4d503e65ce2cb29145e6e8602532ffe8e16648ba9fa3f60a4d5c4121cc5cbdf9430e636bb177c81d1477e5662c33971a506a17b55ab8f117fd36cef66720414622266a263ff5ba01f348e1e5d5bf1153abbbe76cce8bf8a652bd3b5ff77800afd49150d9dce6023dd99a84ad9cc93d360b35a9e08c3ec2aca8d74f0735aef02b4a9b6a153427613a27a231bf23bb7d5c767790304efeb00e47c141b874aad81ec0e08f9580c343bc60705aa894d66fe1f6dbbbe107758e506a6689d00e1a3cd1b6a9173e4ca527fa8d3e21bf86ce3c9a2cda824e8aa1ec2662c4cb34194730820202562e4a749170b054db4304d8ffbb555dafc77e15cbc34fef36c106421d07199e771dce077037b6e14214747dcabb2f97aae09763eb338d19ffd10d4f84c700232f8e6e848cf98f551438e5beb469c4d6583734edd4b6f3ef108a26476cdbce5384aa28ea421459330eff097c353f16519319b031345eed2dfd9a986f4bff59a85843988c182a94cd2c23aafcfb6af43d28e40b323b536a330f98ac583d88c0ba5e09ae637fd6f9e1b9da10a272ae068a1c2e82d0262297f580f246fc5fe1d9e5bb655d4a1f0aee6d4b87bdfb45b4052ec0e8dabc9432d7826e4e0e95845b5a7f00d1fabbf247a9ac7c89395877319dcb85b540b0ee19bbed9e7966de857213512266d58276b69e44b44e78ee1f9afa13b988f6bee457fd8677ebf616f3e6d43ce10bb97e41bf5885e524fafd857baf075414730a7b4b12dc6c2e44b4c3a6d58560f5f74fecc0d87ffd2a185fbf138f59b803b4f30319b6f0449d0294a86bfa69cb19b10507070540ce88ff6b7c9b3c777cf7b9787487d04b06bf8dd61339f99b7ebf91f5c9f942dd9e6dd53671a346957fd228cb1a92ae8946ef77650cb6088ee2bff514f7a7db6158f0ae161822687475656e6b54513589bd83c28f7248901cd56ea46d60d459a434a9d1025ed3fe23fa513ca29705168762f7369503ef9fb943723c0ae34efcbf03884865be57e1eddbbcf9caf9b8d2f9b0a976084a1a95d76ae98f474ffe3e8cf43f37a5b29febaaaa4a8b1ed48beef436f9bd8f1ac414ca50d6beac86c7b82c2162e200d6ce3fa3920d78351e1ccf09fc84057cad9b526152d82dfef4fc63f8d7a893597ac890acdc150903b738a6f20f67a8aae648f3cd16fe6744abaafd570f41cc4d9041aa253812d362d13feafbe4af9c7fcd92963cf24438657e3ed85e1537982e72fc3c78efa1fab950fba207ea7b4f8b837c4839c076ac8e3c7e7920a2665c5ceddcfd12e51c411d1a8a12be4f869499aac65ce6a89325c922350213ca81b467909f52824b148f51afa5fb4d82fe88c79101810aa94280005571c5d9c16e56aefca2b1f5e56cd9c0944469f743c58deb376f858ee4b14d4ecdeaefa88ccb54dc60319c0c1f45c50d6437785fa0670b15674f788715a9a0e2c8db2851a4b8b7e344df9735d6e85e9d9e954a891611aa80d2005ff664455ed41c626cf66079aac8e7fb2109d5d8f20ab2a697888490b9631639ab94024f3debe0463483cdcd85508b8ec2fc5a283e75c5c4d4d37a657975b95031f0597837253c66334ece0e5887c2dc9787d98be804b41e014c715fe1166e5f1a6aed183c569e4d681f32ec7fded6a2d9dcecd42d7e23578526b048a41ad6feca4c1a68c70eac3eb68616713ff861edabf813a4bf6adfc287374cba29f2dd4884c3d582a745a5f2e77b1de85b98a950d74bc30604de7352e7e9c29d41abc8a68a4b30e3fb6b3984b81d86a0cc5efa102c973c550487c9c947f6530cc11838dafbf048c8845814662d73c1a9873b53a1587d40577d20de5c3abe47c37efc83cb56a844dbdc708eebf822dff41687fd1bb0a2da72a952e2a5fb252f2dd00dc7883984bd6a787d9b51d2f0254ebd68af16630a4b74573e92a448fe18723aff918dd0049ad5e56f23680c2824322b8cf8db40ecc2dfc4cfbbc7525539c8001b6bb0db170c320436ffec96e8f90208cd901e962050c52f834e5870ab228d16ae9714d84dfdce284f39dc0d58bbf134b346703408f0ab66a700d0884353f83ffb9538fc033dd34a0dca709628d7545f5751bd76e57989e30efe7dea3e5f651b714f561287540ff2d6ad4d750aadb100f6836b4137f65fe2f0541bc94914b5732b1ba641ce6e978ff91a600a418d90c8d38bf36af0401aae396b83831a8ff06a6026db761aece2bba2dd42849b4ea82ca55cd2983453646df4e5e398348be505fd90ce18fbc1313cfae85406ebda2a8589ac0c697491d99601a0dc9922cdb2f410ff9b1993015d3aa69783569da8dc1b9831ccd2f883a7940a95ef6fa85a6558b5710932b139f1cd53c3d8e4b757270bc57461c55e7606476c2532965e0ae97b2079bd478a97e420f1215be514a457f21ee070e0f12fa203b449d04d921ece2d2567d39dbdbf75db9214cb69d1a391f0189b5dedb2caee093d533cc219269af15d98c5d890259e12de1688f2434cc449b56a93090141070123dfff6ae969b47a4c5b5837424309997aa7cd528a1da773c2abbb3f950e66e414121a3cf80df562e809e2fc16e55dd849d57ecba05e371ff50906b5c6fde2df6071e91c597f89b7f38c93b6044160478834a1f6ab97af268b290e9cbbc9f0c3c3113ba945fb138fc181b70b8795a8d5f22d7c5cd58e5ee91c9e5fd61d27ee671278048ed9ed41afcfe1f21cb430da0a851a22a4b442aaec5c232cf5b572773ab5dd42c34bb4261337a2e040eb1dbe5b9b7b79e26f180cd2d44831d77f585e4d407080fecb6d64a40b6f7543657bff81f42d4301e6c8be151f5cd720627167c3036b5c825a49c5c679673fe053146ea2869f2dadf3da1b62fed4ba35f146b1233c7768c3df6cfc4212dff427f33229a240a66f40357fbb625e668b00f0dc62d183d1a872af6a4bec2ed9d865e5101bc7e642ed7290a9dfaee64b579b106478f6d1ef629ef6b85d3a18b110bdc6f08ee27c8d97f198fc230eda994405a9e321864afad6367268d80864f4a7c180e5ddbc7b757c1866291d8e0e82305465d7d0f840377ebdca9223e9bb37acfe2496bce2a0dacdb60be2206abbf3cf548152b2a8b4c7ac201215801240647a3ea82986bfedf0bf364c361ef2622185044eff2aa8b1741bb41e344eaa9baa7808c9a3c17a7afc4ca6c8c81567e989da3cf2c86ba8c204e27e70ce070d6cf5a2b3c38761c0235ea34e833a0a640109b084102908dd1ee224f89a335d59eae014c78cd1201675fd633ea8ee1e7d6ae87b8d464e016784793a921aac78c4027f20c8ccffad56c11c5410499b222d92554afc91f74052ca33926f3e47be806687cdd18837f2cbd61b40177d5b5a08adb14ad911e8b1ea8678128ca8b986a99af370fc7f847e7c08abe7c837307809a9d02bfd30d9ddddea16315f3f7638ffbfde767cda3d0dcb24fd9d2c726732e070c7e4c9acb6e78a010cd866cfa78cfbc21fe80636fc6dd3889a0f312894845d2b5bc5cdc5029d3547cc824ed269691f49f94d3b458c3dc397e8e30e1471a31d03ee7f5033450121b23176252a7720e8c42c8d72a158aa892519774333cadda0f8cba650f6e5963a46d7c5031a98b78ea7639112ba7eb4851202bc87765a50e0476493695039d4f6d15269ca06838ae98b7b0e80191399cf766de9bd4aaed5f9897e0fb6cfaa432cba019d11ba056bb10f3f26e2acef791b228160e4ff4dd3039645f8fc15aaa57f7264ba5e6646cdff4a91caa63dcab6fe9ac41765ce72c7dafffd55e98616eeada3fc9a1a6f7bed1b180029ae747514f17c9979e6a74c22bd03206d8a78944bdc2a017b7383656b675bd36dea7315a9e60933f9a36a9e7003f16e2fbe67837892d34aab81aaf3b75aee2d002dd80eb7111c77eb976b68e56c4ef10b2b688bc3533c32b8c9479eadcc8e18520ae62d3632f856261ce6e7f0ae83249a39dcf65492445c41cf02ca25c55ed77e6a793ffad317c5d9b4763125efeee46a3a54e76fd907198b1f434994e7a4db53eae4e2a07ff6b6a18c0328896729708ef018e40a9ad914f15f2e8cf56ac21dfb2fb7666df918625d150d22218d24fedc6ea082317244de65c0062746f7b008c4b352b24e4eaab8949a7df647f4dd1b4983b80512ed8bbcc8fa75c519e5e7cbec23f2c7c21ed90c71727bc7074f177ad6ebcee6031681061fe2330673a815a66456d5b1d4ce16333bd060f0de52410a129dd42551968c62ec2db0f98edcf949d0a1bda01c3289714790cf0ef6ae3a773db4fcd493895ed34177a43df2f7076842b353c1a3d44c155bc90089a212289232390d1760bbe82ad9d1cc5b717b3d1a080d9b878692d6faea19beca8bddee5fffa3644ffa612144e8f980fe339455c1ca328c3087742a87e2f21b80432406ee49cccc769a93813237af641e178dc6abd4b834bee549c1ce1b780d49b2c64b9348c7b3172d0b97e122bd6362554f522280d46e5197d562af75ed90671d58653560108a982f3666b7aa33473bfdbb2d72cdaa80fdacb559a3ad463da813cb92862c9c257aea2b44379bdd69cb81aaec6b8ed73d542406962e5ef38d44a6e63373ed34348e3652b0ebf72a247d656abe6b5a48bc08e913373a91f11d2219f402ae6f02e0c97f14ee344f119166a373cbf6ce23d3b5061c2fce6fe19cc11347cca901d236ea7cd9bdf068b7ad4e9aa7c954fa729ec7a20d9263937e683a0310fab60745a8eb9155f020d8ef44623622ae674e8bf5cf0d92d006ba50b1fb9c94e1209ba0ba9cee8307d670ee4a99faeca1237f5e2f74b723b51d145283a42d6e5502eb9aca1fe4d5bfb6274235b763d7ac7ec034181d780edf65f096458260e6b18658032d34e87ea0478312f88dc3aaa053c880e01873feec661d97fffd089e7165c8d366575d1a6ce2bef257e8d7047da733aaf31a9bda1342d7d6d945e1c995bbb56039d12b70803652d226bef010d70110b4c0b9a9a0adedc2baa57eb6266d0d436db6a60f81fd2f1f2390aa1d462a3b9a7c5147c5b879bde6d89b5bd311f5a01b84e43b9dd17ca1e0418f9782794bb34616fe73ff9aafcda525c19236a549c2e2a05663cf32c95b3d6ca012fce588634bc6751f8ec095107c0fd70d28d39888ab3b0e4ba8b5dc4fb52b9be9ee07064e58e3b58719f3a98fc6356ff2dcaaaa23b2869ef20ef9357089e9574737559c158167efa51c0a6bdaddeb4cfa00eb67277376c44598783bb213a2e94d255036a6aac3464f866b4f6bec63518046f640261e300e0662924a7f6a1d73f7a8cc47bc2f03f09f5ab91f8743a857c6e96366289145f3cd0ba7748ad425aa5c6a222439a8c76a529049c768c6eadedac6f06c6f0a3559235eaf9369e64ca2b23d1a7482a6c245531f5a324551603cde47fe19a2e42805b735a8448705c090490380ef639435aad62ae7ae4ce5afae20cfe036e3029a723707f668da552949478fc70734397d6d3f8d5955bdeadc47e402cd9925be0abf3a020b19c22a695fd88cc40684fd075e58707b73b7b16f18b41ec4dd21d97b75442bef700627cac64c94d18dd90613b927e125adda1706ede28786401e848b565dfbc6405d42ba4e80ab7017c6dfa8bf61aa26223e849a185d1d0ba195439fdf0f6fddc473bac4c41c62db0d1a10580ea4c8f9841becf58fbb9edfc87568293cc0f37760e7dda5c8a92ff62797ca37f9c000dbfe719aeaac97f36472c05d44664349a55708bcc4a92a386a841de4af4ce6524c313dcf0a0e6985da1570fc4fc1cf8d748321a502389e5d64534eb71bcb892cdff8e3db276d5841423d57eca599d202f7697d840940208a806efe5a56158b3eacc3f2a4241e67ccf099f115492ddae6e311d7bd98bcbe8834ed47ec0f843327cd8fc7ac9bd6363ea7f9d1832cac67d73696248cd6c8a908ff012fc66bbb708ecec30fce1df32731853175530747e9362ad07af2cb524c894b63e6f0cb6cda7c116139f10e82e3cdb79bf668ac7d1917df34667f272770321f325a4c10f265787526940b98479ccce9cdc5bd5621102ff8588acd0f826d3e364e1e71515f14f921c7205c32408c602095520986283a9064a92492b780347370d6b25a0ba4bfe09dca5a68791be164c329d400f40cef5858094179e4cee6851d1254b5b98fc5afffbe917cc266fb089bcfbce1867ea76e55062c59558ebf9cab03dfb563da84a573a292c655ef6c32f0a5ca32c1dfb8ba5d14d93f85c6161eae4dd0190573a9c9e2fa1798e6e98661fdd46af83e5c2559b6d0818b6d8053c946e4dda4ab00a998236371021d8b27434697e1d441cc7889169d1dd83a2c559123907a66a958874d80c6e158f51b2d71839698cfccaa592ced12ba3599840ebeee31f852041015798addb6d1bec90093b1ac1b6e364bb178bfca0c8c55124d29953e0b4707c04c0335123ec5c726ec09c8eb6eea303560712c26cbd56d6162cc0a8b9136b0c960f1348d0c978a3648a6500b534c5e5b506898cb0915539228e426d697467931b594ac7a99236393feba4aa5b0421446746a6cccf98ca74e4d74015ea17e4a7d4755b9b5c284447a37dd20c3e8f4ba99a5bd01b58417fe8d46c6876886a60199612a82bf5944c1db120ab7fb5cf599eebb1496612c871f97dfa404902d44aee2d0d5c3cea60de15288e93ca784c708ad65fa7aed1341c0a5108b769679e0fd207d94983827a5dcbc9e8721d395aa2ddf7f110fc148d006a3bab79cc02d563491d9e833d37e11c6d78078378c18f11d959c602624621fe548d33ed5abdd67451eecc95942b0269590acfe8b5d67def2695860b899eb80b89c3ca996bee868c4a1361539d4195fdf5f5afe919b505ac6fb9737cb98dc86e5d7f250f80fa222ae94f84e66330c2c4476e1539eac0f565b22a4aee749ebaafa0689285bb2a960f389a19e68d0226695998fa6af799d50dde14de43fdca3a1ef6de9d1097f87f8a3ff68b630848f49a7ded4901392eefae01bf1626685c9ffd8bec53803813084a5f9201689412c08e33974bad7d340408d499f0f1d32567c14fdb004bf9c69438d8a1e9d0462017338fbafd492f859f0e308dc52210c69ea47b00a8c2b4aaa16d84f4e16c5d17568ca967f71d3ec247c034efc223cc10935ae95006991b92798bfb91b4b3a18f1ee61e064405d4c0d7dbb5ae418e4a7f38a7439657ace12ebf0702df5870faa24f6c8b13139f16959e133024e4ba38238973df61ec9cefcebbb379ecc652ec001ee31cc0be24868e56bb939c2ea6cc85e849b14ff7dd2f0bafb6cf3fb4d3731b9c6a9153012943c29f10434745e857e391be206bc82813a2530dd7f4bf026a2cd6499b32d17ce2a11bb9daa2f80dbbb2583f61b11cccd53f054e0893a9a1b7537897cab4d72b121f1c1171230048c55918a4dc93803e9d002157a7f42184d480377151b0b680885c5f6d01629ff8710236620ebb8e72028e955a454906326047ac2062216dd1cc323fa251d70722b975b282cb9b20b89dc0c0376ed5922e83c0e477495495bc09ae803defd8afc648f21358b31c3473c68649b32ba59ca4f941922ab3b1783e41653da7a2bce6f3046e326dc6ae5c5dcbe61997075ea8f6980270a6b25e46efbbf39054c27c0ca25b86081f55f7f3eafe72668adad33420f2af6a47a306cc15233765ae205901e0d13e7495e0a9eb482fb742f0d4eb39267d0552f5cd5207a81951dc7474c44af74f8228b5dc992fd48cbd8df8e35750c6cd5833d1ba6c2fd54b295498780f46424080a402bd9f95a71ce91a7a7c876716902f84cb060010f00f4e2812b115b7024068e68796cd506dc23abf936247362475eb5a2344c0786e0504adca99a642d7a6e51aa96dc20eb7fe4cd6fcf1379eca012a53740a6395c2ebdb0914eed641ea332b1c2774acc9ee67d761dada62a446ec830679f531906e726302b13789601c051718c3471f1402f5f3b34e1ff899ee6e1043ff9216952cae7e243c35a3b573538812db56ceb7112495a4bd833598ac5c231a1fc2a73c181a41c7b1c53a9e3489c6c37379ea8947d4953e59af394d406bb677c828c26efd19646e0face3b94ea81d2178baeef03e53b44e99bd96b4a5f6f929372bb46fe0cadaa4a8e65224591c773577400ed214d1487a7b075c164f999608dcce4bcdf383f60d44120ad307627b45f609022ffd89a68eaa06c418a21356cdb5474a031ec182ed13cb772b5007f2138e7d08d934968431dd4d528898a006da8d80de9cf5618e3dbe06eca064ffe4c93dc809b13da03bc0d7db817ceffe470a9e874918ef5a00651d46807874de805a6010378066a777e7903ca1b56cedf54e98542aefe3d9d75982086bf957704e0c52aa28aead0c2e083058b4f9d0f98e5c7660ae14e69d7042d0f8d0c1e1ad84e5ec8c349043802aa48bf5931dbde9aa18c5d185467155f7fbfa614495bf17a968ceff49d57c50824676f4b7acd546a29e4f173ee08b3727bd441432e73c0913032c46f8208116d18f5a07a5b79e54221f4eac144fe6902c9a9ab2c271f71e1bfea33f7bca49512027ef7d21160cdccb0f7da490f2bfb83e26a2895d0a7555c348ab13b1a1c5506348448d374f2205d053ff6e889dd8200f14fa828e18b6bf20d7a5651756581fb1691b6fff8d01ad9a3151520e0db5c570a1b5e1e71b830509a18838f3b4049ed77f9aca5535c941aad80b3e5fa3ab6d1efa7a03eb4701085ef1fcb5695162b4bb081b54632d4e509a3db394ecb90667b5314aace8ffce41f828936183486f91a2641a1197aa596f3f029fa14f55c8b6f8504bafb7b8a65f2225f12c78dd02db65b76326132a24add480beabdb7302c3c63cdfedab75f9a2af35eacfdbff2b40207dac76104b953e3248ec63f535303dfc6523934aa2ce2bc35bbd3c7425655a054c3d64b701b7ed49f222d4e0208d6225130c93ddd7aa16e40b3d7aa8b7a53a4b758ec630a2a0a694fc507ae79aa2ae56a474b7af8cc4e872d52ba3d2b98a2c5591cbe52af43954443ec7199252fd1dce322eef4db53a1b678552d5a2ca39c68b86055444335fb867ccda4573b8f517ae2b3fdb5a705b73e4a917babd2c7d3daa88428a1e71b7de92a4796eaf0631079c881ba9e2c6f234b3ff8146f9b9b357e4d46e051dbe284359db8401f4b1dae5a4eaf3288ab7f6c967ac320a1e2b727c42730dac527760128376b84309912b889bad4d99a5c6a02af8b04d6f37e4c5e376fdbf1ab57ebfc993f0da2bb00646f21ee10157d959f2eaa63f35bd6c54b427c6faa7d58d10148be49610dc52d50ff6173e0ac37cc4edd9beb5e713b9d5478ed3139aa0def60db50ff3ad6961830f38b5a2958f70e709efd1908bcef254a93a10fd222f78164a0d102a83d46b62b2b1bae3d8600200690c64639a04978a9f713097521cee71389541ffea34966da3a374a62295c42579d7b418dc0817c9338c334046651f57cb43045d274768dc8d048a2bcaa19c65dc5e97a811b94e656e1d09ff0db2f90bbb2d164eabf9bb97506cb9ed6ab38cf26efbb5f084284ccc485f6dcc890c496302b2fe04e87726b99010c0008241c2dc05e5200effd5df836136ed33e104548a7578e3ae1f68c8c70d831878cc49d4de2e809d83b6b2cb21d0cb71a6641adff6ed1d260065db5f575b19f3d9187cdcc830fb9087e20578cc8e39ad5d6f1395d22f04e1d3e1fcd6aec4c73c8109951ecfff5d4dfaaad78269d2dd0b1213589c5fe7379ff9d24c7ca241fd0c961579de57ab31242539a57746a1284b5cda4bc9f524af67de1e31c6a38a70ca54d521c9b77832cb209ce4102326b2f402a42f4416a5968e82f3f61abeec4ba52088657ad4eca71598105b1951a7f283dd5bdc261e570de6180a3b560cb3230935ae924f89a49310150f1a12ac54c275925b4b14c6a619574c42aad290177f287cd5c9a8a16ed89242165934296f19747bb528aa4585e245e9cde6a1b9215da9706ae0469316426c799a05d8942e20adf91e5b439cd7b251cf89534da505a69f5f5cb401068a3857810c07f30ba242a55bb133b4e690b60971cee46bb229008f47f22fbcc2a7770bc6ae172b0450ede525a95b4e6ae9629d280ba3be28debbbd81d936562ba0679d3ba26b39b59a7dd802d6bcd682c681bbfd4149fa0a54b950512916d00dab8ba2ee9888da8dc4636f4f7af4f3b201db8c34f30b7fabef10f45fdc173b7507d04ad521093845f5d9ba1042a884b7c90aa8388c95b59ed92efd9b8efd01c51e4bfd2632a45f9034e0c9abab1834f6ba6808698a8b2d0d1267776cf709d4bca7230e88db26747967196955dee99cee542506e54477f2cef6c07b2c330c5f14616368a63f6c2d1f48182c28713867b9be438fa5bdcbaba1ed4314a900f7c148a803bbe1bd76f055cbee4b9d5284a2c72cab29e88510516672c6f81abb0d324fe244c60c10121d1711a243aeee140a2e382bf1d338061011dde5b7566945bfa150fdee167d9aa103f37c74dbc2cf0d4db79428f28b1672670868599414a0b622ce5eed6630f24c5829b31bd5098f6dd85347b4c1cb4f3f0e6116f085a35ba19d248a0e6141a37737bfb8871bcc825adf0ad959d535b51cb256134da59df6afeae4a45e1835a4d22aa7292a2c4684f0cf55154e55363910e354c4295ba1248a8c78cd0acf4a3b69aa14d618245639ce5b300232529171cfe58b6dcc217cb75276f77e83f73a9d9444833ed1596e10684396e8fa15cf35715c9035a524db3de6f1ecd9ab29952638cf2fd5efc9ccff587c39599e01d674856f08c9c962e141223a1162f642d18bd5883d1e320fda14412421444c749204d4ec164be395e2841857a122dec1a3cc6f71b3edabf734b9476245dc66273931441642711178d526a4e44c24cab75975e0f5240982d720248cad49635ee38b9fa0809f3b3c04a955ce2c0c0ac9f30ab38cf398407a6acd4d5a2651ef7f1b7d6ab391a80997d5b8f38009fac865cd6cae6d95a017f79b9faf2c3b393d1028d1b8544a9cd03e485e68148f4f32d23ef9f47abe03448dfd18450515f93d8e028c50493ceef974ba58a2ddb45680fdbac18181bb863414ccbd75596d7707f51e438687dcb361d3809e87d6a0d723d5037c5bef9795303e99b07821800c3eae3add9295fb57dfd1c3d19c66be30cdb39bd5651352617c557cf5c8f863c94a318c048b613699df215c0e3422a2e211d55dc16dc105eee5092598da68232b9fd5f1c2937e92ec69584ef8924a35e7374671f91757014c855894dd83081c178f80d99bd9b0302874ba4412c75fca2eb43cd2786379b3c00483c78ce8dfe4028f865fcc3579bedf4186e78dc8a4380a97b641f92f7953c09203f2a2b255e2f2846b94a412168050b1005820cdd7c8def6450fb3a343a37d950813f7c32b0c634677655ecb10f7a7fa6ade00f551700482f19c2eb0929cfbc2a897bf721f54639a6371fc8d1383bf951d9ac723f2428e90345f47f194db7db6d14b9b8aa1ab7130a412673412e2b58bbd90d1c95230e172a222de1eca01a350ed2532dece36824ddcbc76174bac9cefcfc67bbc3885a017a67ecd87b95c9691b56b8ccfad37951c9d3eedb8ed6feb5fa3ab54d7730f1fe5e64a69d6b499b6194a267aa50be30abe01335a502b5ab9ed950e610a202d8a79077ef79110c49d12121966918dc6f75db857508bdbf4a5e9f97cd545049e7df2cac96a57dd7b6b778756eeaf6dd6ea950fbbdd547cda5db507945261e7c079882dc115386a40e2cb8ad36d9f0161d42eed2b1cc14de79e285407c20564a52e7faa91c338b2bf7eb1eb6751244c30d3cdb0a749109710c254ea53f1b53cdd7f63a4c4a3bb465787d57ed77cd8afb1c5e0fb089ff7f8024a569ec971a0c6f52664a2c61896ee88c5b5c60eb1e043d42f81ee82378016e4dcefe2dadfb8b749579a061101d75eb7df9f18f420cf0f5f59349081047155975a3daed0f9398ead556904714f143a358df762cb91267cdf8d1d986e71e57bc7a779cdd2d88bb3338842fb03cf407046ac03a8e58209e514d076d682a4a9a83948eecddb3a32b8e2e02e86e4e4daad70ecf165df7d8d8318ae1e24ab23cc88a018002d0964c9b5b0f79ca312830d001d79ee38864e0e062427f4b1638d6d68f7624216defbdb7dc524a99a40cc308a6087d082d70ceb67ff3c5450a8639db35f4971299549a9c5765c996dc969ee75096e30d7c69fe72d77b7bc0b6cf6956fa8b88ffcd9a36b25f7dbe14cce961397485a65d8af66e60d39f3ed469e9088753aaec88845672c3c5d23cad78929513aeea638c372e9ffc4ad8c691b81ff5749ef775d756b0470c9aa64d25e4f9390e246739231227e3a248da4afab377eafec690a27d4f77faa610a0b514ed93be1e35c6020240b77e00e9ebf1255a89e99a4e5c88af51242c47a63c87f4cf02b845cf15b347ce3a2bba93493539a11904ff73a42a0925947d148832ed2862edf60c517bf0d4c4d972c6bdc72787544931d030c70af054e29946b0942452524a75644e4aeea5a623c76b360d5b8ede6f447a1b9bd54a14ff41d0f35e4a23bb2342d2588a1d4c7aa4702c62a4ab14bba7b233625729c56efb77d1b710dea61f0f747b9d178e475cfb227e9de76dfa1ca75f64ff7936fec545ae52fb953b793be795cdca0ab41cd02131153ae935568a9456eb4820e213c4c26495cc949f268bce688d74d17f5e81ce9cceb6bb10e7719e2af2fcd12b9dee49706d77775a379f9e9e5aedc59367eecc1d2299f29f5d4c2fb61f394266842cbaf76af8a6b6ad47de9bf61ac59b9db366ced983edd307c7186f5c95bd7e0c05f1aa8fbf1d7cdfef3c2a6f6eb5dd4b591197dd88711b9e9238d3c89cf17fc57cc47e78d82a33229319d9fef3be6236a431ce6d24ecd39ee7fd80dae6674553b18cbffb5ada773794b2bad5a86df959b2a6cc4a9cf99a33584b9afa89e5397311898cb266124fbd827cb1fd6dd8459daf4934f146dd851d9ca29b37f371bd5eab54699a667fde4a2badf56bc92ac5ed65cb952dbbbd9d9cf53267b6958c8b363c8563a4eac56e1a8a52d51dc1de5ba94f1b836a1feb2776905b5221cf772abfa9235fdc9d886e3a9d61895459d30438992aa1c0a62f5396c3c0a63f6bb5ee93e9df79ea8a7455e4f9ddf5be7aede9e48bac9eec8187b4f5e9b67327888e1c09c3b73342be61e30308af82fb7654641a04c7d9a7f4ed0f2efaf7f23f84112e7a1132a2487dfc00c4b51f0fb9692804c8c38c27e4e009db5a1b2105c05db56fb5717505043b7aca99b2a64cd9975b06b6f4beb913448aecb9e84f8790a7c49b942ac95d0163ad9ba22b4247843c3b29ba214c8cb54e085164cd8aff20ae8be982d0795acc800142d6b4ee07f5ced911c9537b695ae7833c67d7833c3b2872f7baf56a1a0ff2098a0b184328506b61e5bc83fc044aca8a13292a2a3ac89f9283dce1a003cae28a2a706c88d9b0f603e3d770525474836c031d62718ac59ac85d0d344deb68c0449e590b1d36acf9e46e069af62f0331f5a5667831c833623a0d6f1cb7616d89dcc1a07b41e782dcf588575e21abc0b12146eb5a9035ad6341d6b46e059aa67543ecacdb8b8e6291e75b592867ce7b87e9d3e7fccd04f265e6c0eda94496d3a2208f187bdcf6f79b4daae60db75bd0a94c973fe5cff972b6a0935e73da9d53cfabab3895aa99b7a3b6e329311d4da99adb358fa2e18b4ddfbf9062d389378f6a6f3be7aeb9cfc3eaf9e7399523f98dbeed407cba16d230853420d9322cc2579d7c192b6cfb77d3c6b661214ca756d850a4087d4541eb4ca609aaf2a6c67e9a0a42a63fbebd3abc0b8e0d3145a40a08579bf941d6340a0d2cacd08135b3892211bc59412524e4838dd325992d1f06fff780eb62a0c8b4f3b4982193bd525bbe4b8c0d3cc8dfc89323d24c86b161079992b2040292b1e5531e4896486910a67842c6d8f2a95ef9402732fd112c9580e105890b926d579754e9a0c4345be0525d2ee6209b4e4e4d4e1e07f904c5058ca12115a0c0969c814014961f9494951ba4a8a8d8406545874749f96f6285450b14198c4c0b2ed680a5250b50e622db34c82d303cd496ef92c51555fc8be26ac54486f16b38298229da3eafb13031e26264cf20eb108b138c0c421730986062e0f25292f39c59e22546194a5bba0b0652dec8d166cbf1652ad370e3fe4571b57a810c982b48b11ddea6465c0023a28cacf8ef11535f6a864759af16e4193156c058416ab17d94585801a341dd3f278adbca6563c38218992e38221722242b905179e0364445c3932a259ec68cc7c51edf5d2ac8332b6f4ad65be17285ac628338d9061c43dd258a49e4550dac33a62957ae41f3350475c8ac9d021a1b3cf0ffb77c18c9a24033db47d6c614c8b51dc843419e3f52201b6ad0ac6668d0a86864686266a4441a181934315e685c421afd305a6868686868586868686852685668545068684a4a9986868686066b34978686c656ead37746f7693eb9a554d950c2aeeb78a1c5cea53f2bc6d6efadef9a7ea5f7beae7c17a736ab0e826cbda513b018da4b1892bb1087ccc52ddcc2aeb585f3358c3ba03ca32cd2536f1bfdf6a4cd6e5c28e70cf79dc66164771bedbe54e2d979a673d1a6db08847e381e117a7f0120bbfb90fb4dd37de9eeec08847bd41479d480b6ec9e6af2466e6aaa97d2afe5799ba6816c4ee330b2b75b4b9c6b5074f2654c99d1ae7b8efb3c278934e79c3b6b2d2709b6e7e3fc7488b2ee4b59739530b2f3d324c89aeb2af93c52a19dbbcfe128049d6d5bcf90edbba4df0ebcbbbf3b94b50dd194cb6bb6998b4ac89acd0b29fa7fe156b4c336db61db86282be5455333af4981b9e885ac49a9226f432fca4a89d114cc6b52845cac42d6a46c21c594174aa6642a9b9af85a261f174b3b7f8ec7282b6588a65284bc267bca16b2c624c52f4c19a2ac0fe6a50ced6cba51fa91b433659d64aff1507964016d7727445ef3c9bc0b59f3652145304856c9fb8f5edb0f246ff2639c3308fe8fe2e6483fae36c7daa41f599be371940f0579ccb6647ee983c9be2c3e216d77738f3411e98468a424d841badcd32448d6eda14da051d81cbdc2e628d1e69ec6e44d7e2e34cd4c82ed1cbf38bc3d27443edb33ad78931ebf7f3b6c3bff564431ce9c648d30f6b8157d423be09dc3924f0af227246ff2e8a5fca4ec3eef4c7a0aeeeebd70b449d86389094ce59ee51ed4edfe54832e3e016f9aa708022ce60570c613720005704aaf9993524a299d5ec46fa1596ab7507b6a679ba31bfd31070a3950c881c2179dfd393bf7e85747d65a9b4bbb3e11513f7ea4f4f88e8b51782931618032e5af2259392e5bc7268529b3e2297fb909e55963aa9d616fea6aa28661f02663e36270dd4be7b9785f58bf911e919e9461d45c6a2995b06855ab359798564c279593498abd285bd34f53204d45e10405a24014887282926292a272525931adb094b0b4945a6064184f7a3d3a65fc7978db2274f15c5eba97185c0c199b0c180c236a62eaa666d81931354686caa85ce5d1b096864c8c4a3503ca605e18adf5070fe8e070cad4bd7e752e101b2eaeecf5173550a3f8b1d31f0d36fd5e7bf1fff07e10117577a355de5cceb4e5af7849e24000f4b21e82076515018189130139e3bfc92fa44c025af851ede6949b3596ce8a5e94443f7e10fd18420822cec5b56048dbda1097eb1eb1ed50d8293665414152e56f711133ae32a00342080241743d50cffb5adb7bdf0ebeedd3ea75e2ec9944c4ee7fc25c946216213271661472c67f1e9946867089d87006d54d07ec1b4df0372c480a17b3c85363fd234758db7c0bc39c76039a41beb963af868b1862be8080808080a4ca1f02410c710c10e27c749df755cd4e1e6ebe5ce4a1ac49c467131367e2204ba183703601843f1e2efa7b5f065f404e006970fbb629db8a4cfd80fa94a294a6adb624256bb56a5198cf8ed36aaf66e38608c4be1b963594dee3a2af8020de9eb248a3e76a3c174df9f5f15d3882d88577f793d3736fdae4b6b0e33e7bcaf5ebe132c8d7a32cd2886aa28cf7f87017f6b4dd3007f69d52c43f1fab7adc553f8c2bcdb7527be347a1bb5cecf121f286d7dcbae3f9d7a34d4edee03b03dab80ab4619f6af83f4e284fcde6da2ce0080e81a8a00d2d1c6512d046adf66ed6daab6199efd57e2561528336365aa85dcc7d3e892f1ffc4eb40333f63bccbd36afbd2f5f3e36e26a1e00cac3ddf6abaa6e9562ee6a9de7799d63ee6a20075af73f6ee3b75cb555bb5e7d5949b101e78cbf9c610785cf076d73ef53aa34fcda56d90a2e0004a2fbba8db332050b79b5ee6e587bab69186fda37aaf4da13962b392f7ebac377a7458c05e89b5d5b9287bda68af5efcd936bcdb083429e2feffd5a7744c4a69cadf64ab1be36b2770356aa2c076657bab04fc8cc7adedebfd6fd5aef105e33599f17843b5177e3c10eb3441df8a573aee3b0a7bde3f276451da2a8853c7cf4f0430f39e3e128b5805fe8e791602f45fcd29fc39834d23c7bb58f1e7ef0d4c4e9c18354f9631e3c629addc1f4cdad66efb5f75e0d5b50c3ce840753387ff560b272e68fee043025974cd8e8afb747f0ae7629deed2b9e38da4b2466aeb66d5b572dce0135bcca363bc6f2d71172402320377166b5e069cef8774fc839f266be7fca6b7a1048592ec569e238147ef257c76d233b4b9254eb515a6d47bb23b5dacbbd88783b81f84d505bca648131c6d58e3234bccdb9e7b67d2dfb1b273b3b317ed951ad8643aeeba8bddce6bd940f62199ece3285bfd3b86edef77d78885d8b789fdb942d5560d39f9f47aac2f5a99a766987e577576aeeb3bd8634228d4824522e7d5fcb7ef6b67c5166256da462b6375dd54e5751a9a43b82cb6bb86d635e254343e86d9113b65b190fc9922bbdfd8ca6a13f089596d1f54f30f616d601683a31e11034e5847c871451d65c42367166124e1435a18495b9847c938727e663741027209a20c40f2213ecf2e9f2e99a2eeff321d36c5627ab550f3c6227284959313f6619191da35b3f19b4526cfa471821ca22db5a295c9cae1c6461386c9adaf4b564d918968cea5fde704f5fa565748cccdf596d6152683db0413648e6c2a451f8c9ea6bb6a33874b9a81705790dcdef6f61255f2b974a1f0fa4115471854d332db2a78b04923567fce849a4507e3a0fcb5c992a3365fa4b9c730a7bfe2c02522171dc88912af6fc099b40ce7218d3cf9f4dcc60067196cf84ec971fe7903de77c0d481c2a64ce0841c39e6f84bc217df8a39cb97c10ce72428b3ddf0012873af173be68ca7f90371eee7e5223307e6c19497b1a59c91b8e45e7277168f5d9b3199e0a47927f0d3271ea09e409b6ff585f9465c570c46e64e2582173c67f74af45b5a8d29975c1c86484634632716a9239e39fc4916c7f9f51960df2d98bcf844c9c3a9b09d95e652eaed0e5c4c4a9489038b1bdea70c46f64e254235e5d94229938d5890c231c73cbc812ae388f8af3fc3631f95aa612106502a2645688ec24b442b6bf34a945b8a888ba041f25816d7282124aa942a14e4c4e269b6488cf15d253c9b0fd4f25a56c836636e84d48a34f86a424cb3224dbffc4ebf0cbc8eb6564fba3ee8efdc245776b0639e37f87b870385e218e06a9727f3044a9bacaaa90f72fceb2f4c6a63236362da127960da229c7a9b9ca58159255998bfe41d8066dcff58571b645d6b5ddae7cad2a9311a5aacad8b65fabae475cf46e166151648f5821175d86459e3fda29d4778d44eba52105646c5717274a45f9e44a8f0fd06bbaa66b859447a4eb93f21b5c53b6bb2a038baffe8d0a3f224d51418c4d1ccd9ee4c4993875e2cc8f5ad91ec0d5d1c2eba22967c246bdcd71ecc7666b5ed775d10ecc54df77e27497053e59d73571ae0a268b47ab876e09b9c6268ec450cc169dc90267ca53266bba422aa628643344b4a7cf9448c0b1e5d78433b6cc5bfef6988ba5743cbce22d4ccdd8f60029046cdae364228113c682070f9225e66f36e10382d5451fab1c1993b284074d19c9f33b4f4f9daa7748d12a010859d790706cfb1b12b615c2a6323e1adbd60020effb1d27a78f624895953a32b65580894c59967d171000d2aecfc344025f8fd3aedf719e0e564276c228a5b025151b0bdbca60534f3fe03b8bfd5301b53a726c5dd9fc88a2bdf4e57f05d4b0b8927942a7a115221e3c7ee4b5f50cdd8bbce99c72b46ef545dee02d4519e958bb9af42695b1e1b117ac6511d7ab5dcd8aa27804f145de781abe163f31afcb9bf9f7630f036d7e9833fed7ee206b100c701102fec3c449809cf14fb21dc902a4caff08104295faa6736e1b4ea9f24bede71a4bb1630c44100c789142580b41275fc6eab3bdde0e7b9f98573bf6d2c66ac583a6b28f9c1b3636f27ad0f697bf26d5325fedf10144d4941e6791a84737038c1ea90764e891461103a547931e4db3ed1fead1d36f828d948a1652d168222262aae8d114dbfe29fe287a34115167d1c9887a9df6887a394f127b448d2843b67fc904ab7b2415a1f88c294c6cff4f8f6e863da25e295718bd9d917a00a35edb5f33c1b63b8f0b711775ed354b80440aec41b4d7f61d159f1bbec3112f105149ddfd059d7c39e7e4e2bff5c2852ca994aa18e08e8a6dbf0a22482aa6b578af56a268847d8bb0ab84d15d8974b8f428a2c67b74d9b7017a6af8d69f9abd965eaaeb765d4db092346c7da0b3f7c7e675fb4a7f7bd9b325996d2772249b4ea1e6206f8f3d1f2eeed5ee86f01a7bb59d19640a99aef00a8de0cef42d72d16f12173d11cc68c9a37ef678cde644ce9a3e3e856fbe6806e9cc936cee713887b8e83d21c8dcbba60f65555238d69e99ff286b5fabfb14961f6b8fc62344597793e1d069c8b33d0779bcb3fdbddcb4e7f27a7cc35aa26c5adaf628888aae6aafc71b1224d71f3dd72888b262bcbfd795c211cce1f8e18f4641381c57bf85a34d8c171b1b97d52a0c37d9ca4bca8211aa84292ff1147bfbe90d71d173cdcf8711a678343d977b43bce67b7fafa7d6aff5592a78200d4de181bdfd0c3d4985c6c3023bfd26a3fe776662f22ae1985336194a38627024038137b14f7ef45ca3a00fe4f9e32828c7515aee134d44dde328685c39e971658f9b6c5cf9e9eaf124bbfefc78f0212742804b9e7e8927d93dd5c76b346f81f15a7479d1d1c38b76d1a1d6fa350cdd320bc7eab38407b2f663edd96156bab33bbbb36e467f8a5ced71d181bc46299b541ad955b6356d0917dd5aa0b5fd8eb9ad846d8e6e321785b623f3c74da6d51eaf59c26bae46a75c916d7068c5c11f7418ef8e8435207566c04ad2cb39db73267f1c3aa2432d214ff97da7d2836ef99476540af99aced5b13a1df5acae3a54a7b39ed5aed35dcfeaa9a3e95baa80ed349d098402a13e8dd0c9270e30d7a05343e93af2f3016f2f406cb444901864aca586db56e60cbdbb525b356d37a7abc452b4f63165e595bcb29dd378e2af3fefd703a36219bfc573f7d05beb269631d2428d065d9dc55c4e0e0776861c9054597b35cfead80d540dd77aebbd15e36dbb320b03428af6ab8551d60744ca12b69bc4843060b52d9c52c5d5b7d66e92656134e52098b3856d0f1d30e9a88653e3f0b53ec2320b34fb721dc7719ce7d10d773c1750245cdb67dbb51073b66b9558ded8c7e128c49c9520d797da8f251b7aa7442662d297154f1e2a0d92e0c173b6e92c0dddfc6e6abae7defe92447727a7fb967403a9f388eeda0dc4ce99834ea587b6e56b1a4e571da4b556207318d0f67df92514d8feb3befc1b156dd3276d4b430bc835ec1b4ea9aa4878e5a2932f4f689c363d4e5e9bed9681ee87ca8553de60354fd3407861f740c84729410245d08d5f126d203a4074bcde709c41b8cf41bab7a824640bc8a25d04ddf7a58e94354620dbee00b2a9d419c1ed54577d3f1e6ca84f60c02fb594d2fa8d708801b754bc96628ce9944e3ae5532a29650285edf93b1b465952ca39db72166df9538ab984b37266135b4e27c64964cb6780b382905a4049996406d992ca937c20c6a725369365c25a76b6a4b38663ae5f388a7b8ec271b5670e479b3d7d481365c2c070c4a870cce09f4cd19e86e88e4938e25238661f1f87954ce17c957004e905615773d88afa8e0f68b273da0129f5d9730e8dc2d1e60bc795178ee2d770047368062446b05bf4d461ce39e76cd152a7d3f3075294330a2be60faed856804417db0720c5faaebd944fd8f5a5943ca8e1038bdebe1ea55d5f56c04758aa32b0828da0300a44c53313ac8904ea69baa594daead3ca6b63e079b57a944aede477959d677724f9f33bdfa12cedeb0ce2355748287976100a82d37a380a51a9a4554a29dfbed45a88d3f6242e7ee1e2d442d108b2fcb1ba5ea62a0f08e64c79f6a476670d94d65a6bd55ef52983e0d5a75a3f7c71913e95224f4af309acd5560a782ab02f228e39e30f53e27114c141831c26c19ca9b878a3e25241eb55c3fc8868d5bf78e38d76dce62b94d66da35a46bc52a3235bb5296db7856ceb8ab3820882ba11c45ae9e6512cba2853af57e967f057361bd026457bb70c7ca3ad5d29721810ed942a6b2bf69aed7a4c4c3f7919cf4c4dd04979e3a41e77bedfb81856e48de679cdad52ac2bf50680ca4db5f9699656fb516a6fa57765654e2c412d5c4bef8abcd1f076ea13c87a16f23ce25f6b1379bc71833ccaa7ee04d559fb8e229554b8528a8dcc8b6fac9b6d6c971980eb5c1e2c88374c54728a0a56599173f4b5a89c2b2cb68ab5b2586b61f2c63ed5e2d3a2a4bbc2e86bdd9f3cfef6c794c57560f658393445296bfa8c86a60f655d9ed6156285ec9fca8aa62cbac5ab37c8986af7bb2ad80f3f3b6f6d556e569bc821153e8c6d1e9eed71339eae5301834fa7d55e0d6f5ce7913622af97866ba9e4c282a400227df44b4c977484114541a6cbfb7e268f8f8bfed3c774b242fc30199212cf20f0ce204d9fa074f09019618f33680a3934e5b11f9b1f5020bd30584c2654b708faaa773beefba19bef7c3498f45eafa0b5b552ef30a65374b4de6963e36d78b8eb95146df4f03275efca470048dbff8370207474d3aabcd457996a9b9d78feb494523abbaf75ffbd5ed66a65237dc87e70510a321f407c2d1afeb0ddc7f43b5a438af76fe0a1c6be3e74abd54a3f80a4e17025b32185f96dd73d2b3a696453d6370a83f8868684a02b9bad4923fb29ebf424eeb750d63f8526d0a6245c954231832029e75138ca701b43aaacad9a57dbf93542671d1b51a578ab566db55ffdaeda23eea7fdbd7e65adfe987a9d3ebda64af15e23a6bc477c6e69e87843c3bdf8ca8d0a9aad3e619df41a6add3500811f4e5774455774055299b3ab0c29de9f9286e38c036c1a4af1de2ac37d1d60c3e9d56a9aa6dd1ff206ff90375746fcf143bb1ab62ffff487bcd98eb0d546ab2b1afd8da3a79cb98e6a1b1b1d15ef9d3fe4cd76ada78257328327afc14e9772e5ef38114dddeb4334755f4ea27dc39b8ba8db5a221ac4bf7071fa8c8674274b3a82b7d525fd4984e9b4daabe18debbc6f44caa512d3c9e40405a2a4a8aca4f45818914a0a0a883ab12c2d304a3dd5756272329594607c1ef2b9331a54caa4d1f79ae43c3e5a6ce8f33a2db6fd391d8e527c9c87c33edb1f872edfa7cc0f8bb6bfe32297974d26739c03d238200ea8dbdc965ee4f926413af932a65e5726e443b31c3fda98cbff688f6c8af33487a76f89284b0767e647db84991f6d161666a3a0f1a385c2e2c032615d60937851129441ee6c3aa32997f447cbb3e9ff58679b6e3c3da91f7dc8a6dd9008237cfa3e7164f48e1b4f1fa4acd6ebe8604784c6209309edc02f5c866d6703c14d38b2562b9146388aaf0ac70741fcf2c12f53d1c6d3695a074ba283091d7151e6e5d792375c7e7eadd0481e41188e0ec0e7a005f02dede36fe801fc8bb6791dda879fa171f815eb67b4dc54ff1080003538d8b00830001f3e3812d5865387a100b4bf8080b67f00b4c37e3ca8b32e2d2ef45659a3e5ab26a35dceb4bcd6558a2d7f4367996af9960665aae575e897355ab04cb5b4b0fcfeefb077c3bb699ac6618cbb6ddb3c59f8f6394e165a17ed02932a1c194d09719f0c5b4188a80bd64a7c8a3ac979e6bbafc585d8849828138f8842e29048a4b3d8fe2c319025c60353043343b1428a504a172a442a28df12a2bc4c88f26188f23644791aa2a0ac0c71d18fa0ec94ec1608f33c7007976474225bad44f19f9a9cc8a8c9898cdb5e90be1b3ac813a1cbb774109c97d7a1833c1163b50771d163fefb5a2fbeb3e3429ce745c7fb5a31433e17ccc59720798f37e13e24528997834eca0372a6a54392a64b11af91bba8846e1dbac0c46082d1546abbc4c2d0d200f81b9a877fd1365e87eef133f40d9fd2363cd53f3df4f0393408e61a749ea0895d7183166136b4f85211b2a1071e00d0c3d4c2e252d5f2a653c9a62526262f9c9cbe164e4b4f592c36878484e6cce4fb994493089cb093ef670605a150283121ea6bd130051c5241196a691e7f43ef00622cb485ccb7f0d0d3a767073d5f3f1308484e1f514d756a4726058451109c81c5164229df0e6da77866f46d2c450239e36088c5684ae50b2b2b2b306c0e3a2b2c2c2c6f73d0616161d15e4b07d1f9d8152d3f13feb3b07c2d1a7a21e581a29d203c1a04629054015d114bdd90d169f976d80d03c6d782f111a0ea8060ae4107e7bb30071de4891c4098d016cfd222b30a5176548458663966c480b00f41184b08c25a4099105c85202c0c6988b2a32214d2907a3fb2cc6c0e3ada45f6edb0fbff6badfebf1dea76d9b1e3717058663bf4872f63627268296bc458fbda263fb2cc5caa902aff50bbacb44b1197988b11bfc13f071dbb5d42173050fda3f339746a27c5931292ca41a77a684ae76774ca04db3f450239032455ae23a4615f98a1459854bda4ca4b4c26d356525252525252723262821393098531313cab67909d1d217388cb37b53ee233c245ff0ec6b331680c1da3f583e3026399955866a597f1b55892dce091030382a6dc070f74872d2c53aa5a5ec6d7faef6060402b4058ec0a1a3fb256e28332a1dfe03fa3892822ef99aff1f26b7151438b619033fe39b428aeb498c4457f1c2d167921f6c468d1055aa1812c576c4f231c59aa705c7dc812b684326118926210c9b0192e355c6044f16be9ef6678261f1317261f938fe9464b878c91aa9850bb845f8b862dadc3dfd039efa263bc0e8de36334cccfd0e2e7d0fb535ac653fd73e3753408a3a91bffc92895c9b68a100b162c311621962da4e8acd54a14ff4130679f9105e5646b51d4228c167d548458b088b16cc122c4b158ab155611521152115211521112dadd8f2cb31845bc1f5176b67f8e4bcc05e6251481680c3da58a3eccd772f94e94d347e1eb92ed12e6a073cb621c19208cb25c30ce39e512bba153df8f28cacc50c5d0c079ad5d70421799afe552c40b2eb0943803236366067562327332cdccc0f8960fe7c3686159d1638c1e9830c0984124c128ceb63fea449672b90cc510a110a988e1e3028b11242621bac0051623263221e2408c02b6dd654784a67f3cc828842066a483c53c967a6de982040804079f4184a8f81821ab3ac85408edb5a9cb7ad7e535d47b78760e3fd2d9112bf3266cfa7587b270bc8edf3fd2a1adf32315daf47fa4b21b3f5218ce8f340a1b7ea450ac7ea438a8f12365c20ad114a5436a2e8f8b2c1cdee64717b2a9e6bab9b191e3e967cad2e1e9835e7383f672212eba0b49b6cfe3ae1cdacb211cf1cec2bd200fc6e3359e09648de78514fd4638c23c4e383e6843388279158eb946688546afe82b72755d47f50f0e1df4241a1a9a4534359bf9fcbe9f299bb211ecca9ad1255941cb5640955042b67f897e8986281ea93215814dd8490ceb68940bd583dae1912a8f91918939919dc4545e944a2a954aa5a2615264c60433c54c321d7aeae080b17468a9c39ae19d52d445a9a4c424a7690b3201bda44a860c9592a292198c28c2c0c0c0c0a44a4866908a26d0f42999764c412db805b7e016dc825b704b490e3d834a4abe9ff99a26d3e96422a789673a9d4c4c4ee43cf962c478f0e6c78c4390cf76211a8ae9bcbc7c51e422ab0687f026b4210d59352793919313949ca80fc5735284da8101e32476022bfd974aa552499b48119a622822444c4e5028504e90942307d06619a22921184da9a8908af28edf40faa2a5a5a5a5a5a5855464da21152dd173922347cf46f971dbd9fe3ab4098736cd68ca1433c94c30a021acfccad314a0ed394c465a282a27b2ed940959ce143aa29fe33434c1a42a474b59c3869e434c30ba51af2ee80c119db080b6efdbb66ddb50e0cb4523860071e8c9037e3f7387a5536052e5c12836994c26d38988098e20180e5633208821213e1b2a2b74c5c68a0e5aeaa05e529a60252f25cb042b7996af652ac2b262f97e44968f07ba6f6adce82955359ee56bd9f05d0b8908f5d2d93a654685528682bc21b07cbed1e8fbbeef232d81648fa8c7a149363489c84513ac888b5c084a99c91b56e971e94b3fce6c4cc3939809c6a2242212d1f66f3931c14c453ce59fa35370d0299ee7c56c8381f907c19c3dd8cee67ef48ab6bf8d4671c2c5bf41a38258614a500d65421e4d467658d9a698c90dda04fbafe82b721d3496261865a1307e7d3b606c541067058e07354a48f50f8eb7a151af1f9ed7c111a25cbe168a08519e3fa25e2ba813939593696565c32b239aaf61c3eae7d3d458cde8f144438fcea3d2234d42468f28128c289fed3f4307a84ba8144d39053dcad08c00000008005315000020100a068582d1804c13e4c8bd0114800f6e9842766e421387610ca330869031c40060080000181018126d1b001c2bc52398f1fb9fd7d5bba891f949bb39d53d263b97eb1833faebf3d450c9d708562932fba5c0d9449252522e741b5d8c4395c8ccf550c92934d9f6240f171e99a38132d3e7fb9e30e4cf91728e76d4912c8e52cf569d5ad2358adafb2d4e250bfdfa2871657fe83dcd16721caa24698f922ade42bd867c4206a02c3785259b255abd18c7f6e56c388c33bb3adb3e3b3bb1c2e0d5ae59f7d4576b0ec95e74125cfcc22792a30860c8be344c3b0002bcc1f442cc98322d2cf17557fbf33718ff8a5234083c8dc31f0b22f17d45a5e83fd602e20223a42349642a954aa5492804194da35289742a49412188466acf73902614b4bc3ee8014763bf3ca4da2a14f7b40715bf78b3cbad58e8862ed00ba03f0e7a3950c4addacc49d88711aa90e22726805ef0bcc6c6746d7fbba3dec486aed0b7d0cd097acb5582f82adc7d38fdcd95b5ca2c570b49f3fa4f6ce833e882850ee8d6d9f1bfd7a04cff469479eecdae6e450a7d92a0892af17d4790fbc1c0e143d12b13ae51fa9968a19b8432728e7295ec5d337f86423f6b063ac8de97b274836746b62ef9f453a52f599259fea0121fef2f0012007c71ad0010fceb1d617e1e2b030391430e56900c9e062202842dd84b22aaf8a8df8f5052a97ba79ded030d74e879c6b3dbb00ce408d40da908c40e05ed3b552a11b34ae37543c315ed8b75e3203a39eba9296355c309fc72c3154b0d9dd37a7d5668ab9ce050a9fde015a990d535a286c06e0dca72994fe8171f025faee2d0eeaa383047f7cb0caa01f763f382050a8c9e27db420a661ba378c4b3c50b6043da061c09a1f02b831bcddf7912a2ad719b2110a833ab5d2cfb8040dd0200a314a7592a18bcaefcc9fb6650a526554ddddca484bbab76d7865e48096906cb2f09b1a22901ced26160f6f6894800da062e1312eea3a09190f9b457156c67255716aac01446c080b2c90d5720934c2f09974c8b518010bfff9a9526e8fe87a195239db9c101d1f3d0cd42a6cb443dd53692d472af263c25563f658b8b4600d1ed76e819f223b4d4a3e712a11c80f7d0a8e16c9e1f195512d2c4acd30ffbc43040a78e92a0390e97f400980f1639ac1f18ac824281d9b2be741245e90046a080f1be3935a3ceef538129a2f674345280429f33d2e11e34c6a6b5681d8985b9bef38c26b3f2f3a986361573a316a784adfa3a7110c50eb68e58d8d5a623a8174268b312c80f77cb20967153a0538681a283d423eccbc434f4bdefa8fd4c4c47dff78fb48fc56ed1d597fb3c1cfcd23eb59527a2a0a0016d84d8f95244fd10e17754098c0778f11126645ae9537afaec99cf8bbb9d70be370cb7fb6f609296caa55103e9aaf1fb2bfe273ef39060b463c8da0bfeea4abf2d7df04ab7618be753ed1fc02c38a17b6503507e591ca3a2415818b55a5607771018b06a1ed22829c9b4197140a81c63cfbd957539d2ab1b7e577d7f69ea58f0461dbf1fcb65027efae81817d582f4b7c433af45d0ba073a37cf67cb3efa2bc5f8164f1bac72f0fd7abcf288e93ebf7a72f35f19a17a95a4fef1fdab1af753f7600fc8706f614a93a549a68ad1263f4b5bdf34790e29e57656cbbeccd6e89ddb8caad5a63e5452acd75ca1adf7255d102160b18e86ca00328a6e33e7103da049e594297b5b805521e7c1462c17b9268d68ef5ffaf59d29e71acbfa2615661c57b311060bb7a739d39ee95516689735e264bb64576aa9299c73eab683a83f082d9c47b812c3216d87f2f29a88c4d076fbfc8328c5caba88caf66643f4b1beb7c8bb8b3f3fa2bc0a4467a1dbf29703b67cd0182e21a0c397ecbd04d3ff29b9b214c7f02e43d8a7484a9ac37c4bda34e97440f502f53e6430b688673f305e7041fb998a757c0059633c98f5fab0680d91a0d65867ca208fd151c7bd3a18aa287aadb4aae491330caf2d1f567823ae2a05ecd279ee5ac091a3d62d3ee40fcea82c22f64862aec3095b5ff604c20ced145ef87fa681e6d5ff2d2d3110b3f4da8ba18bc0a48dc8a1565f1e6b4ea69311639611d0ce4de594c0c7ff249202803e2ffaa316252bdc7379e382cfcb35040c222cd83280375f76df04257181317c0efce76bc7b04203930c5e78bf7e22c692aadc2bcb8e8469dd64e36611173ebd7851be14e21bce589fb0523051804d01b8ed776dee7abc7f25249f18601f47395cbd9a32fe6cb1f630e451244254c76a9cc54a9e018443dbae89b61a166dbb1b6d357eb4ed8ab4d53069db252a6d91266e4723fcdf0a82c20122a02e07570e7215f421229d99f77902624d62ddfb3ad9fdfc0c2d917e09272f10d79a6f252f7861c17dcb9d0c022f4dc509cea97fd7874c9a558eb1685a9eb24f79c8a296557d1cec5f313dd76e651f9989f68a67c3131e5a42c66f35556f9681c0d84843ee601cc54fcd9cda1d9b2f0842804f8d9a0c487f0fe2d6b5848f2bbc07951b62e8f56c813fe6e8df0d49551f6728707965242aa86ee8efd96340305e6577578ce38fff6a92f02624694e5245c83f8683514d9efb44dcfa755384886370dd776ce80be7806b35986091bb01064ae8b10b322b5b3ae3e7b59c4775b5b8a782a48f0bad0e962d8c86977cd60ec0315c97b4dd7f74295e8b224fcbe4a9b02ca2986921429521f2904d0c511938817546430001cc7cca9267f7fb392540b546690397b5545e071d606b9e1207c554f04284449d995e38e44dfe621e1f35dfbc1eca0556e708f457e5fbd4bd4b192c699529dfa05beb513fbcea23e099a11bc46d555094faf96fb5eb5cf5c895752ae49612cea94af16c41276db3ab472917a44b2aaa390a6ebc3690298430c83f03c5f9dbdda5148bab351e942e23f01345de95cb3a8f92b2631a9a68c59e12d181a553939660b14e2ae818bb8a5130df1f38f601ec8b05485a96c017ceff9389f147db436794c3a4c0f5d0c5b87bf0a0027b2d525856c429bf034f0c4292f9eeb7b2b10100f919828ea9b6495040c2384e3637b99e4057b97863f9cc97474212975b2c8cd220f6cb2899983dc889fa14cf75623e14c37d8260feb93318c78243982ec4596f50e3a71db029f796c3b51a10aa8d59217d3d94f6e716bedbde027910ddcac720203ba2b935455825b7798afe8c80b9e2817e5ca5fdf70ea73f79831d4e5b3db0cf0ef7138fdadbe178b443627738a8e938961d2ef9be93428aec26c778d6298eba21cfa47ba4fc07e36f5607eb0b46ee1d015ecfc562851b0f8a0abd838cb8772d40b8f7c043a0707034e49d0ba7bf75814d6944c54eeaf661c1070566433fc0ec49fab43b442b7da85e073334ad1c373369ac1104d15798a14a88859a12a605cdc949fb5fc872bfc8bfd2c674996814a7fb3a25a1fb3d4e8440f3159f421478525b6eef489bc2e1a754e685c08de00ddae1bedfd84afd7503c6bdc0c765315506910c21dbb31f4b15d9111f2a4fea06c26953e311fcf644e9398b7754c9f24f0b4a7ca40b7c45c44df0beec087106a33b0622a1472d72f42ab5cad78fd92b02bb2348bde2cf7e83b0a51c09188a542d35f094f2d1d4f0b05e24cbc0e1a6a4e8060496a3d92d482160c7a71581587c2a3260de04261a60f44dcb24c795c662afb15904262241fb02eb11512d05c268804eb4bc96e0a05267e4715ddec3b8a481a15706b750e457bf7f8c8b8426fae33488df0e8c5faa32afb8cc18a4f0f23ae21cd27d76ccc088b2eb0eba64a2d40c8547f206d7eb1ba7431a8ff023b600eaf1a05379f0ef3c682a3d3cd6c38a2b8c21bd220bbddd1f6c3fee63c66d55a56b8f6ad57e9cd3ec68b7a29dd97ba7aa6c0a5f9afc623ebd98f9333e59f559ec4214221804acfd96e57a1518b4d04766506cea163d810e899cd94f5c153a0e3bcbce33c48f9790a71dcafad9e46fc83ff9bc430bdbe90200c433ff1762c0f43aea465067fda9186dafd876e66a345551290e7de7473d6b66f7504815aa8838584b0e39302e93a55fa821c34ee90c18899f9fdcd8c988d13bfc79771c7f252d8338938e7fb9f00705f1a2b8f92b95cf4eb1420258e23fa7eeec70e6edfefad1347f844b6d0cf7a58bfb7b9d13ce20ba85cddcdf472dcb0b9a2742c15476a018aa3000048f5d9b29e31f85aceb8f2030a666346e11b09235bb692aa92e9a130deecf6dd2e1aed62c7adc762dc3cb4fb85bab68876de51c590c580b452c633dd033acb6940b59fe8b9323ff0c9f18df37f9e855a69382620944a1b6047a457015cc39a30e428f71c67c415702193aa84a7491d495d8b864e72b22a358b01d17252f4d63af3f4f72e06e8ec7cca366d3ef8bd4318ffa242860549f120ae0b233893e90a24b902e3304f2be8a094645a3987354f1a01d063926f2eda9bdfe652035998526105fa15e2717af2e9cb88bd10a7c91fc14ca936c9c65afb2c9ad3131037b5b7b34c82aa1cc052f96b918f38c602fe854621d6144dcc652da239535ffdbe64c6dcda7c0495414cd33217703c1ab4ff51caa435adaa3c41268134ae9ab08f75434172d7c1d595c6529110408d396ef8ef5cd619b80aeb1df3ff7198f9a4bd4eeaea6b419c9aebb2cc62b5af992099b3f565ff2e1eaf5cae7170d15e0c2bcfdb1a563250405eee879e20788160299db8dd3a84d561edf8a3849018cda078fa0ae4eb2b88ce487f8c8aabd91c9d4965ce40a5117b6d4c975b625318ed3082262fe583df60eaf85104a265d1681a9c6f683ee464309a7a832c1d0d46864cc0c69b661c686bb669cc1cf4fcc666833f0ec9140bf1c84d6f7eff440ab2927ff76953fae320b4453e611e73e63cdc2f932694add5eafcf81ca2849a01aa8f3b8e9efc655b16ea29638a3686150c284063f73b76c92cc519990d5865642b884458c92fea07facca66de1a2ef2d725818484d666614b53b7cfe14d658cec1864c1def067a6e199afc24b2ad5a4d1a280204ab2bbf1aa968ec28f3779bbd8c02f8100d2291c91b1e293b4122cb149aea0f5e6561077ec9d50cb90dc34da44cb34e123e4031158419cf2750018603ff9aec9b74758a9a75d1df0750a1c4445d14657815779be5edcb939880acb04a0e3f9e5de19f8c6e9e58ce5cd646d5d4c855a07c9fc0cf95d879d433f936af0e97cf3e88d579b1883721695720e9baa34b99c2f347ea01b3ab7392b13fc9d612e37a76629d510f5b804e097c7fad923518fb9a269995e89d8c521b5a7039d3e86f713503559c5138ba28610ba00bbf1b7752c755f35fc77c4e891d3099d2ccf1369ea4e5a9d9bb1dba3978a7567e659764564b8cad820a12eddc14a77792597238a89ac39dd23d0605aabca90a80265a21e927ff1dee13dd1a1e42cfa1cba3c3424b1c1d95bf9f108a73998fd443e4b9d019784fb28502cd9a157dbd62221a755b51330c75bfb5bf5828b12bb0ea66e63b04373b0e66153eb09335e071b2cdadde7865a2b715e237e1d4f7b88e09e34b2803b3468dc09caf474957511a3865b33844dbb88082463ca3d3eaa4f54bf4f1285e7d6342ba5b24450f45863b5fa5fbd8706577c829a974918e0922ae9b860d5ae1d2af78239740b4a3cbe0e37d30b6a9a78d63a4cfe3f46cd296b75e4226763af29633455feef2c3a59790a4eb65d53c6219a53df6be9ad1a0e80378788153cacb100f8c217feea17b9a77c5a19e1784059b65e6c461620505d5ea25a9417a03e3d2525cac5ca180f656456faecb9281a01fabf4a9c21ddff65f057fe54080fc6aa700e98a165589481f9fb0fd5f14adfa18dfaca77c8bd95fb468bbd10475a48b45644d23b6e5f520fdc5fe4cd60d87bd3c4ba1046bc4d5105dccc04047bc19b446b5dc6bf5efa142108ba6870c6b25b609d2441c08ececfc0641abb7d4e1d46bdf9042f0503d488120dc79b03f887da9388e5ba56c0407bfaeb1ea20cc0a5c83f4841ec3fe3599fea457010f0669687022c8fba947dc6af9ef135bb8a55878b784c2ceadd29f2473dbde4c38d7d972a5bbc7c2231180105c78abd5d140b6e7f7405e37f156f0e95722c0dba96934aaba3daa09354f68ab6e396273473a87639241921ca921c4b52a90b6e47860952b76adf5f97a0f83f62b50578b362972e9f70615a9d5a14d3bc5d20427eced22383bfa9c050f9149ebf0b0e638c7902d152cb2d99e366b20163018145826c8966d6cb8d22604609367ef80bebf1da729333a530f5a7400616e285815eeac48b2248222c43b99f40e28e9a9352b90a81a73516dce89d20617bf422622328b7dcc51a659f56fb3cbfc48bfaad67b974411a5a1dce08795e7cc87916cb54a8031f88c0f63d07614019f94728a3f50db067b64453c3f41a76e1131530bf935c3dbe84cb7276ad9867ed4ccabc58602769ef38569e30f72f70a7c97172ec345fa01226289e03c10989a2a512ebb8903b1236862e0d7594a7ad3047d140d81cb4af97067a119ad54b587e85a87cd92b90fa08726893e7727f0afeff5441f083ed2bf5b003691e562d033888933af19d1899e1ebb071ab39f83a0b8c0c4d3dd171cdce24c19cd1969e91a8e577b6eaee91335f9e08c552b9f0e7c5c113e56dc5b42010e1b9ef6e25d2b5abc1d4a40e3391ed04cda4295db7661fce4acb207da82d8face14a2a075f53a5e485be8f6d899fccbb07ab992c86fbf3cf2b9b05b5e2292fd51048f80a369438edfc2f82bc8cfea19fb35a0980dfe5128d5cc550a917f65229e487d6dba0a6a2ddd4168e66e26a940b8932740a32e2e64c3ac73d663fe26601cef93770a8fafb0a2b753865efb6e95591f172e45034879a3f576a491eb46da16c467e95d18b26101cce5e30c4647ea4c8cc0252533a269ac735755dbadc43ebf1098a4a1638d212da3551ad4607816407e70869323dda923bc1150b0dc1d43752181ff7427db8e40a5d527337cba002798f00ce9e7d591e212b45887750fc7e6b8f6de710b0b159f897e3d6dbb01393b2ad275f4acb66380f5dab9946887beb4cd62a2e0e2e516807bbf6ea9311abb0f898446a0d106cd29352c0ff1690db16b89fdec9bddf5aa1c75974b3aff92ccaa5c00ad768df624906f02ff816575fbe42091a617233e60dfc9dfaca80081a31e89e2860ff94c7902c628ba1130e61902ca4ed870e109be6cc34e1e5153a21496dacaa2405855b2d0b0b0e5672e75ab91f417e4062595a48b6498d46a8629e0f8f5d294e3d1fc22114a66a912927325fb1b7bc9955448a5916ff6df7583d782d9893e286ee92ac1c0a59a0443e00c5a0da399368070afaba826e7c71a6c6ab50296a8b3fccf7bc492a4019205c0fd0ce8c546cf3ce8302bc3f93b7633f28d786ac2d8e597fd1e4a05227b6ca4ab3e8e7ba33ad6cf2d5718de43f1471c72121c29bcba5d47fee563faceadb66894aa3dc412bd3822cb27ee9a50a3ca8aa35bdc5c0389b7738160766bfee7e69fd4b8d711800baaa86ca6643dba2dab796214849ac0836babe6eee9aae788d0f9eb4fe6618087b44e0a597c8a4abc4ec4dbf7c218d5d8ce5f91a3aad670e5732050ac72882bd8a84544e617787b329444e81ccb0edc2ded15ef8ab319b485d2ed5288a5e8d134f7f024c8017c033e94e2374641639cd57be06a4e30919795e03d48e275e8a66150ba3c7d4fc098d9c1f91829e118af6aecc33e8933a834718782ac05ad37781eb39b4f299db11683b144b8671e24d1280119a494d50673b9274f78eb0a34ba92b13b0da075cab04bcb8dcafac91e2590291a99be19a5e9093fa80a7f53f71c8b9c5091ee2a313d2299ba18dec8559d5f26e139b01c47762cfd793adbf488cabd7dbc376052024464d3969e7a29815854f7fffd4dc0f041abcf7a0526aed427f307c3ddac539a851e9918ba575930a644cef8a3745cc98b72e8ecc18b900011dba7995ea91e7441b21b6a9025b3480b51ee9ec019060abbc1d4dd54e5658f518b3528c2f3a7c34e9d22cbefa2833057c924d3bf98b40d4017baabf416f69f1c3b818f6446f468b379035683bf9b90d1d0dd1be00d804720843b10a8f0803d96f63c9dce88aa12bfd1d0594c9ef72f6097cea52f506e98ee5240d8cd25806ae1bc3198f9dad9c41614ff00abb2bb53b8597d998c077068b1dc43111bf72e7ddd541b76048b57a8486de2d82e4753f514048dba6368416e8a4763d95d3634d1c00be6205e3599306c4084767ff98b12fced244ada70eba3fcc976e9f88c106637603d23f4554416f4ef066844d0508a1e9030777a5194a1e35459d6ee25545f144b169f5f33b376c8c98969ee85ceaae8597e662183aa1a82a6d34952d3e511c402edff209c0b98df637887bd717013ba81a35ab2cc706af39493161dafdcf78b137bb5cf41b0e3fe03736c3c35fc5fda533e9f236af18a2b974c5df948c6aa8605a5ba595058e0e8e05cc5fe1b2c5bbf02faf1f592a4ecf8cb736e4b105f1e548bf12a72e2d506c9709610f92584041247e8d87f25da8260a223404acf003c05fe087ccf179fac6fcc3846aab9adb1be03c557efc0c6c51dabb2f0ada1ed7bc735671e807fa642d034105a1ffef01ee16de8b5423f2d571a8e8d297c904fd8fd034a511b7322edb4e510d231c66509bb9b1fd3fda9f694989225015a85c7fd8a248be8c29245664404c129f6945eecb99dc14aebb53c74af602feb596a241f2e6dbc7631179ac753abf880fc52b455abee246a4fd6a69fa1de7c29ee385c89c2887692d92cac63757999e4439f7151850979fb04b65c6b9ac1e2d94492a7be725777ca832ca90c57d5fce7a6f9fb9b7183b81e395d27044aabf07cb7a7cea9cd7f522995bda37abc48a9e9d500c26508379a0f4326a8ba068252316a9051064819019d8efc04217f2b2a8a4c24cd6f9a360eaa16a14f5c0890cdd414d44cde0d3c8c9e7aae135fe6cd3b14a94cfb9857525b62acbd49182213128333b00943826087e38aa84a80be46368dd5d8ddc6ddcbe4722eef6da744929697785e442d49332a22d6060f2e8a304699f40e38ad725d94f1c4ee30e704aa9b12bce8481690d448a48a36215ca5169a8afa1af3007bd1d7a85542034413db107ebe63439123dfcd3ae1bb648e085d3f50b992493c820d32a12046bd76eb4a490da5ac2042c0605dedf99573a465952f453c82859fb9ae0c7709fe68a45f84e24d53d515d00935011b4914ed18a70988d19481f89067f2d0e843bbadb61f365eb9ee54de604674ee969c2aed108a0da3032b36fe2efaca76e051e19798c93713440353bad93624c5e449edec17d92b868219f67ee1fd4ee28e81fd11948dbd12d0d30b0025d610d63d37f9ba0228e222e605e452368d0f8c3433f5189221281a94a17d5ce582c5fffd29ffb8612b864947e7520071de7f2ba7bd2460d9e4669b16287c6863b24724505c2f2e3c2753bcaf297cc75d0e155fbd5eee5cc87a1b0e2e59d27be7593796953a69a6732bcb7edd65fa57098510fe8cc4ee80a0c4979e0a1763845013f82808cee9829c82d8a8d90639355b8bd78027d604845f54796a6f465795c8761b7b8170a866c17e2b2f960ffb252425f98ff1769545431aa2864fb69eaeaf29a374f879059380f16a27de0291a7c289568942ea05ea14812f4149d121f82628410500aa9b4359cd617acef71e24c8a4d57c0e420eb2fb3df0a0d654deccb0f1026f3c9abf6cdd0a854e4388426c1da3065449362c6be5a77b905d3a67c1df5f94a2f99c25302ea7db990f4c8cf35983e78dcb52cce6de0651b947b077c1b4ff9a30257b848f107b94526eb824f211712973c6ee606cf97dea54d7ce31c338057bd4c6ce0dbb51da77b68c31094b7ae86da56f407984857a43543fa1f647612fcb87637606f566c19cfd2bb0fb0daa8142175f1818f68b25d15b992e7a0b74ac1c5781665e02c258668c40f53d9cd5824b039d977099277ed04738fa3a35e9db670d9cd5225581b0d433b6aa08050ad42a8b50c7b6d7f883f2cf9a52eb85fed299923a2c3a7bd955b109a7cf42969062ac93357808635ad534694b635fd24fcb327d5185782497bdf5d9491596caa9281b69a3942c389b3f7e55868a2548a4809c86c431cfd8f8ba5082b75af35661a6bd7cd683ad9899be8d586dcf5c5cbbcf852dad8febfd09dcf4135a97d4cfdf35d6909e07a09c40d2316ee2e12e6e13569e81d98841ea5aa859ff970e8f0de0b54ee7bbca943e0f2253f5fdc9dcdec17770b73576cc410ec6909e8f62cb874667e28a4f1b700cdcbf127566644525081984a0c852cd7348ec62ce587b17c1ac1275d6d2f53ad2667ce5c3db3f82c1fdfbbe306da7cbd9fb0647ead39dd012d542c49971694fae5e6d19ccabb8042d339e8f5139bb468267f38c2b2cc399692715163675ab5791c042594914f89ef2db431e71c78713a4d7600fa07faf44f7f083f847d63940c2f8c0f3a4f4d474310a550c51dd84b9a2d74c2469df90429d85b3a48d4f586c9d318073888677e3dd40fb53d18f8fb4707a5fb5a883a417616f5e15d8ab8b5050eaa1b6b43caf73aaa950e73e90c0bac57baae2ed3dea2a34e8b5fbbb535a04614501e8811690a3cfc6846574eb4826078dbe37fdd66e6865bba0e012c3192d7038ea8608b81459c79ab8dbf4b6dff9d314f31fe8f9529dc592c769c433b58bef7a43d3eedb1b4184e6387f36ec989f82c1b786014d005b73670676965dfaa697af403ce8d9ec00fed157874afe9928f0f61fe7bbed734966986dfe65e6804cffd10cb81682ec10ae7094c8a32fdc1502dab01308e5703e82f6fa0e3c0f50feeb5b217d4860fb34137baa21a55cdfc10cb02438303f88002beb02fe682f21c6d1176886abdea609c906f84ebfe72c843993343071e4f8e2445d6fd09559fdd14f399dd14bbd19c3c9c22795905e5593651b20b42c2b31a536c0c3021e363200caec5d36c2a7dc7f0ee236b31684fa4f0ef6e243512d2c731e9fa8cd45a5a789c5193c9879e8351b8d6db1e8d055da45a3a821e1adc75236aec35a39ba7507ab43f1811a4132e5527c6e164d78e198fd8785c1ed5142f1a66e020d7ed8587c7c5e59c2c9e6e61fcbce5276b8fd05973ad81aee7b61d8fa29fcc51d07ae1062dc1e85fb98d7518b9a56f925ee48f47e35398905686ca673d9046fce93fd0e47c3bd13fb0bdd876a16e3726e7b1b59d4caefcf37955d72fe275a46df47634cc932a882e475e75aeca3f5bd6e32ba830ff561aa654c63fa20fb58dd7aa932da7008f468b4b9b497fc5174643eb12549f030bbbfa738e8c6b8c061c4b34a1870e86676c25a47d2498bd611e3ed25ae6aec5f80b3d6d1562bd10b68cae990ee188e6d1ab1a948c21100c94fceae9b6207366b40ca254249ba5fd0149347b2456cd8673205030c044022fa3c14a9157e58bcac9a58d15757429d615912972d00474ca8bf6b46cb71e8811fe940c2cfaa71e1db1f24b9582ea33bb02468f54f54d046ddcdd122895b9a1a0847c4fc3e7eb4b390997cdb79048d071b7b4852665a1f3a26d0306e8d4af4a2e70911b11fe7967d2fabe38ce3cfc790357bea48d810d4d9e6a0caa7a3c582114463c8a5a0281cda3266c3fea8b91115d8399843f74a4ddefffd2803fc9a01411c877db699300f4e3e1164f9a31989b4827c94e8792ac2e550e44f3f0e3ca0cc43e5f9f6613598290e0ba2921e872ca38a8bb7912e662a7c0a6a77aca9ea4faf47bed6eff393e29d3706bcb3c42506f61038d770745d1cb6383aa58352e4047127ad4dd5809e2e861fc295a9613aec5a758e58be03939de750c6e881c635d10047c3fae090f836b1666556d4380ce9cdd2750740d32ab2cdf962ea5fa6b096d233fe705a79cf24c133c23c49a8206932f5ee1c004ad65ea71e77909494ec0aee207d820ee85a241f4184c2b617453a923075c56d843b828500406ed1c5f2f4678724313365e5a8257f25af9a78154c2753f8e8950a62e24a0695154b42393ac2f6a019fbb67615707df86476d575db07883ba170cd6c3ff8c60484acdcc2205b3f915dafb15e3352b2cacc810f67bd404568aca3350fb57895abdf91cc331189e2c2756302dcb798cd85387fc38fb20f73114e1f6096bce7d441c32f1f9a4e82b5652d08825b511cc8966b044108b2aaeae5c4b0ad1934486204a50fa983c466c6cc3ef18a1ee5793006d2b06b750ee71713b5643c5851e2f841264168e2d72e8bd27680ea80bef76d77cbfda9c2a1fd2a1b669e872aa83bffe02526a5036d1cd554dffc5da5dff53d2153d3e20c8e19d4cbed9fc9368b9646950ed455e882dc8be2128ae3b2ecd639ad1345339587fe81d6f3a105b1aa0878d70b8f7056aed08bb2a2b0e232503271e9b4634547741d0b1744620b60ee7d71d7b0a8d2f17978a01952f5800455fbde69f2165fbaed65d75a6ddb120cfc4ce08efc4fc402574c00f61a625847eca9ace6b8bf9465a0307f8509f628f19ceb6f98a0f6610cb268ce275e805b0604f692ca567fc0edae299d60a4e53c6a983a9f6cf3835584e0e5bc5f915b6bdfbf98a61b83c7e34ccf3d9efc5a6a1f40c66c3dfbf1aa77dd4b6c36aedca4e4d52222de39184d7f26d3179bfb545f209141f908e8eb01e1216564c322b49f53c9382193eece94ca6b77dd11e2e03a60e2caf864acc6b53448737a8fc8a3ec274b14dc87e5a7b35296f02993df78c7afd76fc862bb39541504ac2f675e493ddd48902d0320ede265eb906ade01f917afc3b4bf0c33f3269ab795cc2f549725ac7573936207397cc1bcd99f183a5bf295e8ed13e2fae4eed78ba66f1cf260c195e785f7601b6777c7c1dd224677ea59b57529691a438fdea70f649205848ca2031601271c6283100670d6fbb414f954443b02d791d2543eee4791b75fb5832a8c16e745bbb5bcb89b02e73851f79240145af6cae283f68772dc893aecac772a0c25fc165d8b8fbb41c20843080b506984112d93735c267ec9f69b2446022d46b40ca10b32aadc2dc0e001cfb3536975cd8ba976ced2593b591aa440b075713b74788e147bdaab4b1164f01e6198fde991355f12bb088a289582abf946c3439156d222525535d22ecb1aa08d73433fb07439f15fb254db1ba4e4a6b14f216efa1a5b9551b13db6003326af141d8d546086fa64cfc1e6b636ea054550f4a5cdab293b04fdebcc86310e6513e07d88194cd7fe621db7dce6e7c1c26956c2c10d746d9334ddc30376a1e255d47006f76bb2fdcc78918efc0dd6d418c4b87fbf787f3b44446c99f55e20f6362d8ad769f3a29092500434c1bcfc64657a7720ceba3a2ff29365ed6e16072d1a8dd95fea50d3d8a1cf65d31344f13c9baefa8671cb7cb2f5e67068ef07aca725280eefdb899b9ad74f927b7c4cf9a404608f21f06e68a05c3a0e3eb54327dad89c6e5982537a6e9982a468d390dc57f0e95f890f47baa0c90b43c44c07e4201b7c12d22db2c3d31213733351c040bd73893015da53598b7e989e982f60746d923b8e2d38c446c071fef287fd367c8458cb44ba823aab8392b08b79dd3cfc2b567746e0c40e988e04fd621016acdfa973395eeed944992b59a2f16c8b6c407002f055c5e18b0a3adc1d8a39de6b67fbf3b2a55a0b7ceb0a1bc64d0bea4ce1bb6edf4283fcef195113c8bd5f8e2247df689ec109686027a02b81ee61d73af29a0cc1b056aa0a8a399d522a5d35a729c0cab1046e542107a056f3e48b14a76065f9d5e07a2b641d4f573412c87705fe482a39b29029176f7f5c00f61ba8e6116219e61239eabb3c09d6510ad16907ad4e11841a11c6182b2822d9989b5dd79d7ef4ca52295031fd58157d23c569985e4b3465a402b819bf2a6ba03a9fe90a990b5b7c6c1d76db86997007be0881c606ea46b8c119941346132efa46311dfc48d35b35bc87a0b8bce2178b3c6b4f130d5d7f8959fb2bab59e2e4c7fb90adbc090b60506d80fc9669c8cc7048d7eb8442404c7fedfcc98c2b427ca2937f968eba6f8030f362680d033786cebb40de98790120382c6788370ce6c0dc6ec6a7173f7784169641bcf6864a46cc5b20120cb5ee48a600b4c1d6d80a440c0123d80e7f4fc2bd016e5a294e9bc687430919c2e9ed66255d43c4f0690ee2e449bc37a9ce41fd11d827bae2d602b8a10eaf8db1c95da1609e08fb34412b9b0b05d8362304bbd6da7f6b22ace716addc6cd2ec7a92d03c1102fdae422f6a893b74d7aee4cb275c0b9f26041b6b5df23dc2aa0871c720d804f45581d678254e90c9124214a0fb8d75d6ea61ae5f08c0a815b978991855552608d5cc3aa1509685dda5ea0be9b1de3647e899bf2cf88b6453e0b4f0d2bc2be0556bd50e4bb9fd39cfc150ea3711cf4ece717153ba170699aedd721d7f4d9b5d14398d21c011b72407551218352c0fd1aa3525c74c31852232d2e1dad2e797026652fca305109f724adfdc21e4444f51b2dbc25e660b443949673c12e6ae44d30823b5901c119ca096c951c9551cea314a2a0b62d1c04d9ce453f87505b5cb38a228aed73d0d8694eab1bd506981e242ed526be546b051254945e8ae660cc3393bdbb8de9f31e5d1d153eda7c88c71be426a8f9e4bf2b81c143550cf990902a8e38e8374e62eeb7a29c96f7d1745574cdbb919829899daaa199185dab85013d46fe09141066033c832dcd0c9632f43298099d11dd4baf7b8ec94fbcc8dcfd49c0bb2276eddfd3d71d3d2a024aac1a8dee883d602708432149faa1e24b8e57e033c0c125fac811d9f0215f5311fca2a55af83c659c92910ecb2d6096bc14fb90637eb54b2b29f9c517d2754bf2e003cc5510a56e017748ba96af8e866717b1207fbf196a9cb7264daebb501f1626758368fa7de97f2e2193593501a43df489d13162acaa80c5c19623d7a24161ea66c79e891554018e7e0918b391b3c022a047bdcb0681459d828614fd8dd76e4ea40044f908d1649be59e0b8aa18fa0bb34996ae40a947166d30edfabf5040f398689e0c087ce2320ecc68101ddaa8a46ed5ef142fff16f097e223055a20cf6bae28f161dea1a580f34aba30029f7975b46640234736dd6937753d2befa1ab7263b3e6c53f96139426ffb5ee484efb25555199825c7a3d50f63b7bf08c0e1f1c749565875fae6758005d156ef180053f3711e885e9a6875c7ba379440b90b803fd0f1ce2f8556491f7a2711a656934c6f59db9fde357633f0c6f1837c355c18902b128af6aa8fd0f4462443da02b1476e91cf9b5380c5e3e19817e3b2f21ba163acc6417d34c618ce2380871377bebdb5b6a2ceba1eaeaae062a54fb458be720f5a5f21727c87f55b425ec13302ede327f2d3bd83683e9f6e564ed2698439c397b47759da5186cdc5f566b8e33a113c3347b959af4cea0dac86b7e7485c2830b22077fac7f258f2b511dcc8d22807aec9ae3f0564b0dab6244c56b27a2e2d4a66da2a9b886669108644eed0a9c6f925616337e9fba7a3261ba13d7fcb2f4689937f87f00a1ad21f2c19e4dccbd332e6d7bc9795aa7d6df9bd1c8776c315d8f5e701d24e7bace3df0b81ebb1df8e4185f5e61bda09931eff5da3693adb99e680823403201e30eb9ed2299df5e43384d3fe5f7652e00c8e52cba4a735bc4c3d75df9a7135a67b6e5af12768a2fb46248559efe51ea73efa1c4e1aad276454689e1d0b468ee394c1ae028073fb738c445adc85d22383a24ecb71862c28348c94eeddbe3ffb93f11baca56f7d41679932acf1dd44fce7457dbc7f21892cf4541dc1e4b8b92885e41ff2ed6ffb59903bbe4749e902ef419100ed5f1f89b56972a4ade52e2234a81bf0d75e1d2fdf193ed58e1c4c777c471dadf993373ac90cb352fb8f901d8362c684269fac5a5a9aa834e2d20d16b13480895fd71da07ea071b104ae7170243d1bdf8f7c733e26bb3377ab361bc7db95597aa887b2f1d03f268647273b092d3f9981eb503271665355bcc08928f75cacf58eb02d8947819f2ba3ae69df7b7c21273a25ea250f8762f1fd0a05eb9dc3196f6680f78b2c654dbf3877713e3d67c8c086cca0e8321701e66cae0cee53ca76b539210603a9e8623e1697c88191898aeee2c0d4f18ea80acf749139b890eab56b7205b41592029b5cce1d13caaf91b998b7b180c84516c331db651a958de1694abb220dbcfc4e470ab110841a367faeffbe436561eafc232e5a84aff53293205220a4c5e89b2e330b444e525c140ee302a5302047674ce6af9f3d718853ec1ef3c368d21a12595d82ded1d6d23193bd9d0776285aa76a9829182992e222f82d9721012125252f86af791973007220a245799917300933e422e41a878f526098b83dfd48946c3539bf48d52dfa61ce69d4c5ca7610f09e09d1de18ac54ae0227fccffe4ac588f72c0353d738a599e116bd8a9a16f4a3359257a65ec2352d7138d97e7f9e6c23241c6a3317b8a6e8bb90ffc17e705fc89c8b55d0499f5f9b73873d402417a3bb4bda11241cf70257065e4dc0203a5014b0908f4c108607df373d96a34486e6ed39f70034d14fbc84a932a9d04ca90107553be8b61baa46c49ce45b85d5674170fddd3824759c488476684ca949bd6d405eee93fd5a8bb41da3ca999dc0fe198aacfad3f13dc03ae0e738040caf8a5df96b249f68207f0cf90be4b6fa652841ca8d9f323af7958a7d5327e1694365e90f370292a09af357b8bf6196c67921bf23d5d75d577a837581eabcee9ac4a438a79813863bf557baea5226d28fd2896d6815614deb323927a916654820d42305e9958d546d89a8f1ebc657c4da9c7a08574946a5df928d4199b24658d75159cb30be748fedc2e16ec9d951eb98cc232a9bf1532b875b4d6cac55954af875b7ed67b4d6154e9c6845c70c476f34c2b3e0ef298496f332385374180ff1d3cce093466bdbac2cd620ffd16beeec34c80ce166c8d0e6fc4a46228f8c4fd58779cb576b75675bc89b6cd1a3d73e397ba8b490f85a317187e2a71bb5cf93a545a51ded50b64ec6945686823d760bd331b5398c5b28d11c1c84316276e2446f53f956b3c4555f068e97fe0e4288e9cb24dae14df222b5a112f4dbaa22b422578fa108be51561ca1b0638cf99be994d327607842febaee42941e6b1ac29d4a4380094ef435846cce45a466cb56b47ccbdf7d4b1765b658d2bfbf1fa42b925f9504c803781f77a0ad207357ad03fb7d6ecddcac66fe255c72d16de5b95f30a43c4388a5b402929f10df8a7f350727e5ef855e46db61112891c3a9a5fecfe662f28b7f2925c7bb792b1809c00d1d5749b878da0449d4ef990729d11000600dbe5c562e05af706eba676ecb36aebc8d7e4d4a119b9d64a2eeed03b11dffd0a5f361ebe69890430dbd7746aa743c41316992b1a4b74ae5be90a48f04ca7999a3ac26b0903480604f1b25c9b9b7d79ac2ce9dcb14b7c0c78ae3e0f9d17a9e91c5f26a96bf8bde7426178674d3719451fb7c3ddcba3dc39cb1fc511596ae98bd3d57139f05b76918bb92147e2b91a0fd84c316a0d69c4961fa90fa84cc255053aa0bf5477863ee9771ba6a242b9f846b06d370771f87c43ccb5bfc1edb418f21bf296362759393b45ac40bfea38efffd2a4dd8ec9570b0d7695f29d7d27e08f1e8080431708b369b95d1476e445ebd92d9fde7e75e357c235e99059b3d00c3b9b708bb7133dddaada6c725b1a3a4ff4c44be6d2e37647ca26935a22d4ef35c8647bd76468b9f8e49ed5d17d3678a624a2c81f23ba74d241684bca2546dbf2d0afe9b4b445bc290abc11762782c60739ba9bd8e955660863e55107683adf8df57d7598be2655b2e1a894e35c123ae9188ed1793eea2418e2b34f31321ef6c1c1a9d24641427f921084fac38150f6a2cb958b50eeae7896386f20097ec8c204192a802cd56b49a804b7c6bb46f3d8e34d201e4961a427acf6b27da3b797c2dc97db70074c56b7fdb1020fb22c22754af4569460798a09f5b56da934f8e8d236293a84ba9b276c572ef14bbce6333f72c3e44ace757f3d7b9069eab5f7305dfab6fe7b228b857b3789bdfc4fc763842baa8502ac3f0eb09e7249e85fc55f5561bb4864d6107242e72f58b2cf79eb0c0fa6870e3c2a33ee07fe61e332731d7269f330b411052c04d58cf823f15862468f0d659ef63e9e28abd1697bff6d3ea7723170b6329db145c58ba93575287946a80feb95d3323976d35c7826fa44f989cd4865825a8c6320006524937735ce0495b26e3a18d5db0e0cc3bb13c6dd4604f3e29f515a94e4b314d8614a1e8c4e4f1cab4cd22dca828346bc1889dc6533b4daaa79355c0c2ec13eea73654ce03c5096fc71ccf396377ea6f95f74b5204863524d697add0ca0c9ccd4975b81079878b42d1d3239dd800a81d6069b193d7520acfaa65920a3dd95498b47529a9bfa7b4efed62561646ec834c574dd027d2c1639345c60d58c4f0de82aaa99f871e80e1066425f15ae1c78943ccb0840e83bbae1fc6eb80304154d2c685a52685b3a9735209c71dd8d4228658c85ca13efd24a5729334d2f380f4fcc5a94e5be5b8195375ea18c13bba1f1ad2b726a6cd6ea16e7e013b194e0b4eafcac83398a352c0c337995fb8865a6a01edb91acf48f31e64aeac7f109226d72816d2c842f2859f42d84198f0bb883115d03ea84769b7222bb4562f29d3aa1f813fe6b90a046ba0574cbd3c631d82d3541c91296e5a3345550e9b141c11b9b5be1c2eb46aef96f80c9eed62203fd2b4d2b32ebf29d0c5048342499cce9a85920d4fcc6d030a383a8648fb4a98c8a1cbc156705af2df2b4e0c53a049fb7ee737c10aa353335cd15fb46f1480251964818d94edb0a136da194f03a5e1372cc145273fc4a651772d147f24e82cc9868b36279f5e161d92f376da22439485cf14bd45cf821f5e13459f3125c649d242285fa53dc4f1239daee7ab6047cabb490bedbc9116fab6697728c142a1e85ca0631e11c6f658d68dd1cd8b8542c018652bb467bc8a17a6df266a3fd1548cacbde2cc31e37a17697946719d2ca520ab3c5a832bcb42ef1d48f18e39c95f7224ffba0da5373f5e063ee0da9dd91abaaf607729cbfab509a6f817246c01f2dd0a580690b0769834f43a698c154556f37fe6367a0b0eaed3662c67ad917d2cde362a8c0a7d17fbfcf447e78c2bfdae6f0a0b342e21efa8d1d7a1b0a58a66ececfd056d3ab42d0ef3844e48e66e62a8ca4ab26d29be1f82db3dd5f3cb4f4dcb68610e77307f5050526a74d71ea488fe5915a1a65ac2929f257a3edfdfda0c8c575e3366a633fc18dd661b30595b0749e59fd67bf14f5f92d35bcf48135db2cff6df1afa3a144b290b974f1fa348a3cedb28a329a16b5c9cdc3e8dd821f4ddb190f7f6b30398e7028c187be5fbcf04e7c628028acb4ff45852ad00cf6af5460e9a4a9fb0f7fd84e7bac26e07a5f42fbf13d2dc1f94955c3ffcdcf4315a4f5a1ff2e4389b3f1a5b0a0744c728dcabbea69e587d287b57e99a18a258974e0c0ce72aa17cd6a652ca53ec9eecd14ce4b377d8b17e0d8b65a0221005079424d712156d24e190e9d5ae4fae44b9092314cbe64829fb2044a2805370cae09c3a67448484989fe78c7154f4e083da38602804ba8fdc279daf6b6c851ed6a375a1e9333c36feebc8a8600cb687775fa56798864838cbe6ec4647bfff5844c50319523ac13ad58253c09e3ba094390d67466f00012f6c1d1f4953689534faa2a711f69a03787122e4f23a95d8065fced5b49a72df334f5dea7b2b88b3d57445b9f465319b7cdf2a8f9018c3173765a83acf0ebc591e2feeabb8d70b15f2c63c44427df503665034f29ec023d4c8e43902fc8eabf65ddf1a86978f3161cc51e3f6d48c48ad258b3394df43e87b93cc82215b37e7663287ad99c5862651f11a1905b4f3d9b35b86cb1cddebdc9d17e33a252ba67c3714bed43d9a1741b6af9b8fc50b19506a9111949e4d5705cbe7ebdca51b22b8ac9698f63e01a797650e8d2ae7589b279856827b5a5e8b859c78e4cac0c021af7f27b17fc2ec9f23682873070b138db0119b2df05809098183e646239c8d1415d5d3ec4708231c57ca839ed88f19fe588e35003d201f317a644101ae3751848a54775bdc074e4e461a6f0cf6e8e07e4f426d0d130496da4a16092346d63fa78a1b1eb0f4e355335c72b01916078e772eb1635624eacb2f0b3960c44b1aaf52dd3ef2b8f47d8de417ba69840998da8d45ffbe5c226d6c1b070ac2faac1b3e78e533f584bd762884b20b0518224f14b0cc2dabd65dd08ec7981258dc022879eafdb2e73f30ed1484a19ba164312bba03b57739e9f420f93a0c6a408310e0a57e12376332cc2878871fbcbca3b0c25935c45911650b09793e9dee448bc54cec9b401a4398413c99ec0223fecec9d28e796215f02f92f08ac35460b5df8e7effac51d99277350ba18d878340cbee38d22a5aac0a605547dd7e9728ae9e77a841d46ae0939b2b06cd14b3c73421a5d887165092a3c0b4a758e5d327e5e4b496e2df03898640471882d2b86c264abae2ce264bf4d9405c7d128015be03c3cba81617b32972980ce8908d218e4832ec42fbb1bea53a7c8ef9f8c77a782e9183d72264103c46bb05bb8befb094122c066314e7ad31c7f9a66bdb39733f0ac5642dee4abe28b8a919d9f6c27e7fbfbc5614c57dfa80af5a22e1f3551a50fe6b052c340245fd9476b61dc503c4b405ec8e460ddb2bbe84a9236c5dd96aea0e1de69981e69265b089eab5a70f5a9e301d69dc397a2eb8122f1ac29acdcf8fbda78eb2fb37566f55ffc5c8f1c9d2cad3016f55a3e344f6ef84cbe2014d18cb838192a16f51d38e3494fbf2d75f42b1cfc6ee9c86964834a86a39c65bc87990efaf92a09881f7a92ec87538424e74f5cdae124b04be4fbea8c67c3fe4d9a045d63d7fc9c13f554e2f42d3e91f784034894d0685d7e9d0ed70fc1c14a5766389599e84e5c72d2524d86918db8f3bfb79985a2b0712d9fd885127848bf9246ce6c2abe2e7a4877a564477e3a2427ecee37e1dc3bee0c92d77fd24ffbbb9f3ff815bec4af4bce9d1e4b9f69712dc290214404d3c3bdbf9addd0bd890287f6707d2f5873b93bca64ba82973dba30fc03fedf72308065271d8cecfbfcb747110016f8369bd095fd37790699db38a0ce457a074976dc953fb895eddb5a6aa9f32bc8016eb54e8c33338e83e03b591f093b1e148d56833137f141e1b1ac6c092079f2d92e9223feb5ab9cdea3c841a357191a383f69b3b85c00120d2e4563d6fb8c0929eadc550cf5e30ed23a63264a8eaa31e05cf0e08c3e0310168deb63d9cbdb913a921069c0bcc696a06913386e62dd5448c8274780485a9cd228038156b4ed9fe10d48a2cec138e89bbf4da6f7e6b63faf9329a5e61c2a9506387128b03ccc167c1eea93d948ee1b5d30cead2a5a4cd38076ace4df3b809ac2f7481a0e6e90a67192caaf9120a922c89937ec971ce0c9dab76f66ee5a06d3424a30e4b3177cfba51bb5f39186a330a14304f4c4043d95f93cf27ab054bd630773ee6a130caa9207879b503cd8042e3ac38d86811b2a735f3ee51ab01721ab2a61490b2eeb83d9bd6b1e61a277867681c564d8aa1156ba640391b6330b7cbac9ecca73c2e4446f612a95f33736a1b62df0da5b83c9aa768aba6f44e9de73cfb796c7e0679a9d9f8fd91c25b6f522bc85ad76565031cdff9d33e0c942f561e2a0759336461f51b7f2a411326865c6e41e86c548a4728492a8174ce7288e9a2b7db544734833f7862f7e618934f0a0e95172dffe3689e13ee67f50e9bfa8e654b52627745b65ad63cfb542d1ec62ee36174f3dfefdcec7c71019c13f4423a3da36738110abebc5ba4e1e3cd9e444e7302167c4c5fd3b30f54093cb1aa5f481576a2fab74bd4a565509915f42c7535dfd70f518c065c65fc6a7e2b3340f121c07c05444dc3a80f6170238a314fb27d6aca7297229f57a1fcf1ceedaef8a80eb33660aee5de47b53635f7b71a2f08585771e0d1cbbf7c3196ff881b04092fb6cdb5414e62615441ca507b4365b412504af0afb50d313401ec37d02596e8f12b85f22ad79a208e7aec7fea70d26b0135461589b03475d36995dd95dc20639f5a17983cf5240560a29b56c31e65663cc52f89d6361122e1c3a314b255b47625ab6326d61332ca246b938ad5deece8e560a2ccf5a6e8a13cd38e88b8150273bd29b792bc5f21e64d3a684388a2b6931a781eccd7ddc873497989c93469c1452c6bea58f22d30abd91f1303f7e23a440228a6d9a9f5c3473d61ee69af3f11267888c2d225cc22df966253e83747e0b69e2a057181988d786efc81660cd25331e9c15534be8de87544bbe6fe255ab1bba6900475a7443721efb2f20fcc4d0a6b0f6b17adc10c863021dbc093e0e3b2e1aaba9b025619b54dd6f380d3a2871d02d7493d51bdba888156411ff21ba0b83b05215588e497e369a6db456b9abbcddfd0fe29c91475945572f8f570a23b2466003dafebd7594b8912fa7f9f22a34a2d27ef9fe620ef027084bf8d2b4875a12eb28093a07dddec03bccca8d187493ae50d94508834a7ae7fb5e355f684ace9ee749ed45fe67293817a487e66f61934ff52f4e9fba22b5bd58207bc413b794684b31f013e445c86ac09228efa525b3c8fb1f71c18f24d37dfa3623e1f9cd5208525e5f108d7b18e0ac8d3671a608ae5fbdee9936acec9e03ffaededfab1bf7986e1f85026c63e638b9f4e6d904f7216c6fb52f8003f2f06555051348b4c78c52606d60abc2777917ff1b596c4b3065dde80730cf00c444a4b772396a2886abc2a5f487fac52aba5a6b2803e371604f697cb391906545c998b9db8bdfab72b24f611546760cb32a04b101a88f1f1196b9c31bd67cae9e7f630a4334f187b8f290972a35d6fdb0af5d2a6f3cf877216e88eff7e4b5394cdbb763bbd1b70396b09d427fa885ecb62a7211cd43b7527496dc1cb9437ffbd15e85b37e70c204481c3af1df8d7149c114ce0c24287fe6094e87d324bad7f385bff59a18cb5871bd296c2f22ba7dd4f9ef68786962a0703016b9bfc147d031895cf5bb6d73646e5d1793d9eb3f71a1a804121fe58bd4070ada73336b802de4fc965ac7c65dbbec176d5e2b22ab4ccc216b842ad56ded5814ea60f8e907db1d7c6814335edc141aeaba4d467e077f6418d96219227b32c235d7e13c8b4b53a3a8bf2917ccd2fa26ddacb89c25fbcc484ce04e114ba09c99615787fd46f0b59258f9872d5b3b50d099313eb446d083c12678d4f1f0f492e458061fb2144d2e7fdb861b7957faad2eec8facc3b28be4577e87f062dcaffea061498b405ad05b9a20d6b86582f9c56ab1a33e3eb216619bc65ca2667db40cef21116443064ed26ecd8d66b651cb5248ace2d2624ad21cc159f026ceaba613f4945547d98d5d9166fa860a45c6aa8a34ff4c2d8636859f2179c346bf658c729464850e42ab4598dbc540350db50e0dfc6ed1158ae415dd5d825fe592ae756a959fbf9c906a46e636ce1d011c3abda4db9692191fabc03281c7de8599a57951297b0964a184cb88d9ff13a080e02830f0d2a8049c63cfbdf39314c83ccc3f5aa61d6fe9fd1f29a7a5b534bf145a19e2341428bd53b82f7e97325a9bc233243042de11ae5562922b0f64571a263c02e1c32d90ef261716f449374ee6d16cec909ec5fee44691a8a9eb570a51850879645d473a152806ac1a098cf17c65345a78bd7386f2f4cb31b03e53935b1d9729c6e2f4bf7b76582aa70fbe5f27d81f7212eff3850e50a78dd421ac6322f8349665044d92d38d5caa18e0f9786c8a3d9cfa392615deb360575f20c2c10121f2a227a0b489260ad84234d1930acafe262da5ebc642081ebc4a7041cd79d648c61a031ac8e940065a69cf404416b3b50027018b2c2d4925b53d4a3c613e7761a78abfc31e7ad508e86145ceed42641baf8aa411cb75ad0db40dd30b8f15331e15fc9ca86de1e9be3162046752c67a32dd0e90fe44ce490064cd50822c89037068077c353ab0ab86e285762b799101c1d6010bb60cef443708b23c4eddd18f91b738621459460ccf97209da3e8c124051a08b661d914f5fe70564951d4e5ad15272f6176534bec04349107ceb94094a9181a7f7a919302249589aeb32c57b9881f17902265cce2406a44aa9c7328c7ee24dbbeaf4e6418bccd08d5a644f5e72e6f313fa9c7f9730d10e3b0566d91117f8ac3601aa526cc12bba9353e377170db8533d0c6c32d94adc79f233163aa9275e44b811d456110f4b7ec835724267e0b507dfa100bc11d8580143324dad504072d1953209377b97b92c01f75222503ed0a65ba7fb4326df7d072905c167f94b16255af973b9ca5bec5359b24d822e6e1e3064543f22704b8031c442e051e2a96a6579aa7693503e091eb85c45c7bb57acf93789f8dcaa70b1844ccfd622738df6599a3b67405c6f114d51cdb1f60d38408a05902c88b743f0233552c634120cac4c860b5c97c1587f118134b2fbd152caa9346a6f95710175c1200a68877b01fd95919748a17bc2e76443d62f58cb52668c4f2e02897f8a03d46be272b2e1e1a79ca3eb2da2b30c420ce1959ea44170727770438ecf1bddbcb1879be3dd32dc6f887346da5e7df5d46e295bdfaa75cbc184ada3d746bfdf91e7606bc25f301cbf39ae52e9a1751cb8cde70745724bbfb3fa1a682a4ccb3ab9f60ebff95a803aac4ee63082cf71970e6e6063e27f50fc4e4331fedccca58abdb303b67274f4112038573a2c73094ac8631231b09a0f2c3f75f460236adf1d8032bf6d3cd053bbfd17a0654a723500cca4542493ca996d1251d64131b62967fa60b5c42c42ed61b8c30428cd4e8f8cccdb85d5fe72c1cd5298757e59d465eeb137e3a87ace076ac8dfe4f36b6a5666f1afde801ca752bbd4d583546918cbfc271b83661bae4af0c0aa7c21478f19dcdc7deb618ecfa513c450d3029423541fd602d0d890049354cb45f2b4b464c134f20659dc42fa2aa62e06005b8715a9ada30c91d4ed99cf10a8cd2083fe042b4b6738f05d9a7dbd2c019cd9214282937731d33ef6abc8a503576aa99ee9c3d177ab35f7726375db0d7c99381a3b3fc39a36465206b53bc4864f0327d63e7135da5a2f58fb0c90f4b6e26ba2c527d475d73f23e7afab447b835f2274d85649bbac0d44d09ddaff7c4a306600135697c92eeeffbbf6580cd37ec7303e1a451ef4a646b1ca76d258380e76fb124d90b6bbb57c4dd00f5ddeb79c2a6fa918ec9f457a953a0b50d5788ae6bfda151e24cd83d1302f7cd53e8a2ae0dc8d2a361fc23addf210617b2a6347cc6988edc63be14ac146da905d77b87db82a05fdc32ee626462b892ce777067e1b08e4fa3ecfaf76deabe7c19475eb89a55a717c9170544d22352df2bfbdd41ca0c689183c2a02294d44c04ddb233e42bfe047c673327989a6a7f15780b89b8af3fa43cd601700f1a27960209e8966e6a2b19b91dd74680e95e535096aabc14e312a6b7844144f9aa5c163b85b6273c1daee1a272e9bbbcd51e3a220aedadce6c73dd11ad60a6f723782dd3eb4955824c705b77fcb955946457ebe2d4996406568cf3ec20722d95514fcf998df47b2af2d53217bbc6d2dc917630ab1a4850e467c973459e7a8c46b3b8b32cd3d280fe61a5252a88ae3e85155f13a6a55d2f6c0a385bd770e483626110467f530c29efe2d7559b0757703e03a9f0ac7553412eece17108dc6096b421e17097ed26cb7f2e98775fc2e97cad3b272d8328f2573534d42d8015119584d39d24a8e36e1a1a69804caeb7f615a1331a9062d8aa8a3c588c289cfed56f0e660c5dd58c24f45e893903848d95a0567765e129bd0f2a4825b6f5fac4580f72ce572bb1081a2204eaa4495ee1ada0a17806e6b732166e0334a3c2f20a5f2c4aed53a068bbda6a69977b04a30f8ad777b4ab1e0792080e6829763b8577604eb7be4a216eacc6a5f006d00324629d2d1f8c82d85688d480461df213697811c31197a28a536e6d95e1b11c9719dc0205fc5f7e5c90c4c3ad9a10df069c07cb74f60a34afe0014ee0009aef316c45a069247d932eac2ea745ee57f897956a6523f7c6f0828535b09db8a9d91aaa174ea5d617443235458f77ef24073a9322a45181546d9e70c3e20a2388d806f3771c89aba33609d1f8754ad274cdeabda2c034d64159dc9be865f114585d80a64346eda1b5582fc3fa0de5c7ae39d76a1b11f69800b89274c78e938a58cb78236771c56346d5d21789adf3a345cf57dfd674971351b75718fabbe109a1b9403b9903fd9094ae987e36072ae0f56d09f924a0abd19794aa6ea3d52928308d8e9ea0dde4a040358823f9441dda26c70cda68e8a4f14c41bdb0c6f925d901691927a94804a537780ff4fe439cde90970ae0e7e067dc46a46b6e8fb849886b42aa17c156beee13f8e0369214f19a8a41f5aa9e789112a818bf20aa568b4e3d8dd6bf8e48ce97f43a341772925f000411d388672feb8a4492f4adbea434acaac85686db284950b61b1086cdac6e0253c205e5f5163e905945c5d7907ad941ac1402539c9545a1200cfd4ce0060ba453e8f9f43d106f3e6318886cbe5c9bed820684525dacb01eec7e3a9ff809d4dd0a40267e316491bb9e8c11f45d73e7c2a3e7ab2f5818e8fe18597fb8712d2b08dea9bd00ca09838ec34269bcab06f83dd87aef48f80ec2d6ef4991723ba196da3ea141a716f889f136ddc26a1923ad1466c482a38d4196a876e0c46a2f2f7df5ff3ad4788a54f71374411ae7c9505c994aab74f4c661ba48a4d82f90be6a6971f1b08e7088b09964fc2047491d39d421bd2ee9fd96ab9654b0620b201c3f81f7c998d3dcd03479c5b2714633b1a70acb81342a3584aabcf420a284f015c7fa29aeb4a6cc584188c0f6d5063789d3618406054b4777092684516a57fd162859e1286b9da81c870c0951962b783f6348c9d50c69636c221a06b47044d9d0941366c3558020f9ec62d0cc20bd248fbb3cc676d17ee9607b2e302f6602348b7c11ee96c95e2d956f2705c109d7a390748eeddea4f4af359a69c442f4d2a913ae1ba9e2543f6822fb904bc0a64fc8a41c48d15ace9837688d2a923d6d05a9b57ad5032f8c9045085c25a12406597a0d9309c6ca58968419b44ff70936c0027898fd1de683031ddcbd03f2711146897f8b02ae07a9d6f6f71e7d716477bd14a527874e78ebdfe21cb6d73d417218d4b1e0d563b7092a99569de4bc295773ac3295c22674076298658bb12aacb59708332557e9476d231e5ba0c35ac3ca088e543645ffe0675c0379d47dae1e7cc5b9191a29e9a294258e1939b9ec20291c01469c81021311b15f4ef9531f50b9057228e37a7676feb1268de62bfcb8dd4749bdc5fe26bbcb0b65f507be54ef9892e524dd48ddd824294e919d5440f3e596e14be77109ddde4d28096a0511b1eaa078c83c9b9feaecef9f400ebd7143fdc1c7fae4cdcc8d552065ca008ca6ac3f7c90adecbc7e371a0ac3092d7baeee9acb682cd015f193205f050eaa5e97d29b258b6dd0eb5d59e637c8d2b87fca91322998d8f3de34b5afca771ba584a57a13cec4ac91b034e71047c538f2fbda96c1eedfdea4c4ac2886a239222363e16d8939d448d6710390ce0eee18d1a0c9b8fbde40978bca99e088f83143e08ee78b688c54301ee7622b772ed29837e86594a9e9d056429d440255e265df1e66f448def64dd242756c738e64bbf84744db169074955265e80253811e05424cb8f836f68b9745461746bf21aba5536fd83c396132c517ca315ba6335faec7eb933d1c0084f5a70bb7241242a2ab146ef3a3901df35388c7e3077524268385c99ad957f05fb0f804cff1e65db3f039f1c0916312441060b6418d99fb07f5bf8816a6f5a7f8a297bd58fd8612bd42f052ab9e4798d92ca09c004d458dc5ed8f982438b79626782794d6275b3f34f847b9c577b470e350a172360f0411ad49ddd311b20113b433423d5d580139388864a0e5d7b07dc197aa03472871df903234c3f1725c69203d0caa69a00cec709a274b9001cfd549708c8573f9650b577f48b8e667c7131b7d0a56d66b0d5efe89139dc4aed84cb703df9807b82538da4f99906386410057769491882d9209219bbe69bc94c9e45b802cfc4869c8540b5cd87e8a951d24ddd820b49a46ff15844abebb12f9d7200a9d54f1ccebb784117ab3e3690662f0e2afa5f8cac12afdbb9da12ad6081c0326d3866b3cbf31fba5b424157b81e8a0b48f0ccca03f6546239b2a8b7e6c9aad6f7ea0e03af806d42bee8e1f9628323e2b4cf0cf24a33af1a90dc0870a2542dce9e95e2804631265ad2224493c87d703e0d6ae7eccb3dd4052dffc73c5131d17e1fd0b5cdd0ea2191103981d1f281d0144050bc4ff6209a238827a39c584d8c32760e613bcafb50eace78257586344a8f5887899d9d5fdd9dcc2332d97ea88175e996ab1d880c3530fdc4713e721a6a485c3a63e9f2764a6523a1d61abe9ba4008dbdec626e1be31304d2bdae2fca74a181579d1392309dd42af06deb4c7d65474947962f9147c1b7b96cb1b1062d9e12e3e811f222024651aa9c0207925afc76bab82fc1d88f0a0a5d9a332153d698b2bfbc3adac115c9414c80ca20ac007a43ee60ddf9448b1ab6cbababa526d3a2740e4e77be91559f7856783693a158e82f453d610b296f3c77dec6d10e0af3655a08f32cfe0fd175486d18cb8652c16c457ad606b47c8e799c6bc206978ce725aacb5141198bfc07888f94bdf198765dfc758c7d6bd27a35cb4e3cd49bad4a12b6956ca9fe43d20207bd6922205048c4d88ba1680be43acca40f16acfe7d38535c6dc570ebe2a8d638e6c01fc73ae2faea51946f19ae85d12631656adc9094e817d91a49757cc1e5fc0161ca7d27dcf0bbbbfa90e625460c427f8db2264a7eb3796fffb8dfdecf97eb4a1e0adbdd0ccf17bbca59449a87c57236f820a691e1197fd1ea946de70a3e0a0bcd404cf59505484986173467c7f22949065872f1d1fbec5fef7022aa8b1a77548a65dae0044fe66e3ca430f3616650639d7a4186d312fdf394b22f81ce3fb452802f3276086f355a02a0266ee46b28645283aee4885976747e4bf2d49cf18d55a5abea2ac220173ebd4fada6480c94baace0f12654a97e8d675d9e9edf2f9511caffb69f703d8053124d474029216aa49863020a53053d2ff5df47e3159f8f736dc308936ef0e1a426faee537d77f1af81e346633722eefaea2d16bd1103fbc627521a98d1bdefa77f8e0888c09dcb78161870094d64d0478f67f8e9948e4e8d7fe2227583d09e868e95e69a6e5276a36495b65acacd9c12a6c01d1357ad3f8b70b81d9c7274b7697c2bd0b519f1b8257d574616e98119656f51f313e222e82f523ab649558c701bcd5184a409ed00faead026117d00e41ecc6864237fe437dec8c8618f181e2858d10201e0109be4eee590ca91f2d7271da41bb1120b6532ad65eab5d809b94bcd2426e36a6f7ba94843f22118b8a92808227718435de05a01ad9ffb4702ef458bcadd6edb0d8a91b0ba6c75da4ab88cde727d151821b57d98df9f71b2c093fdd179d7a223ec332509ab7300cde0396b7056bb6f8adb7de22e332d7d8a490cede028b0f9f7242ddb62f04dec3aad7f13a322339a15303e4081a77053fcb2dffc0cc9a23d999bcadcb08f404ae429e76f9bd68525d7c5fb830826d34a339ec97821d63bd74c7655751564697de203fcaf619d644ac0d5586081dead8476e9277593d94db3e41547d4f11bb47292c66b27b5e57d9f9da3466ddd6851ca58393a0e8070e5e4124294b9c49640d7be8c61a6e957f1fe4bcaf2184b0b0b7d45a34ac402aba3da0c76784c2e1773966e9330e0dcd8ca2d301fdf1dc12811a6c69f631ad71b3faba850d2b4106afbae93fb20db22db13c21086ef22f4908fcd3e985292ba05f7cc59f27c86f3e8bb6a04ffdcf43bf4e372610ff6489699434f264dc6bbecc167910f8e2a284f36f596a8675a88abce87c78d22193b29e285a426fad2a6041aeb7c7870fa619a1dbcd54b75118e66239a1c6fd570e3daaf84738e9b3484e7b8375bb0131d3769265dc976e5b290c0688da104ca4deaf75dead35e6c993db9499bec9ac17ddae4ff3629d7bbf1eedb2401919bb4557b33f051678f6e925b3f925140647a4d0506d802df67d0ddb56fa195e64008f1353d9f9bb2e969dc2d83bf9b3855260186956dcfab3840c22acb3a498a62ca511e7116d70ea2d357ce05691451939817dd6528cccab776cce524530e8e8c2a2aa482f90e4060af7ae80b72b50990931bda42b082f0f6ea360aab56047a46fc2e25d4ee53829685cb86b78008f2d4b3274d49f6912f5c2c46e6f8de50e53907786af3207b42ad0ce85cb7cc3b7f22a39cff2731f3cd5de85af2450cffdf3e17de6825ce33c8ea12b19b9802491ea84c32f0b02a40e1b3ab056f8988de0d5b9b10c3926f9c1130f731b846b109a3209be368d706f43a37a09120441a6185b8c618938bd5b32b43e7c4379314afc387db0d581327b35ba78c0c69bcef7f50e0d080b91ddcda5108ac04351771374e2bd34b33bbf9952efdb73c3e53aecfb77ab98eeb06f434b9e35a506d2c13f2f40ad618934b2dfee053cc89e7b3e6b1fced96fc4614fdea3e74791f930929ffd7dac55bb497d2ab347471f79b6bae213189593806b16c8d63245ecb8288d548f5a82bc84a2480557b2f26d5bd941081f8b1d759e464fb351752d2c555a96a3ca2b4a9d42dd7d72d34ad2174fe1cc4d152b0df8d5dafe79f645cd1a49da7ce7b144053d5da64787ac62126bb9974a662270efcce1955f4561f462d1a13ec9a54f3c79eec880038c23404bcb7042a5c76adac936ed6ff224f4b248f1fb413fdedea864d029ca0f17ece5c386c9909c4527f6f467098bd00a8bfcfad82830ea1833e8971f96f858f2bf66f52a64a93d54da5712f383acd457a8121938d52638dd0a17cee3a9caac1bab93eb8a3d450e4dc1175d9acb439474be9cd4a65ad6e195a470b05485e4cfd9312465e221012a7421696afe40fc8dd3e8561873f54c69459a64105429f90e10d0711a7d48a0cfb6ee4a2ee68f8732b97b82f47513513f6824899c82067b4cb22ad3ad08c05cc67ef7a73e2709cb2f9ea1f4f98236b18a069c4e2420c024ff2fe538d167dc9fdad2da7cae549a409699a9964f241bc68de6e4dedee9dcf04a6a7306dc628839af1e53c5df514ea83913995007d165fa7e81c840fe99c9716097abdc3052d912a890f3c22a93024cafdf497cc7c923ec8a937f9eef3a89153a43a67ef612ea9c1968944a3da190d7a0acc9c647dfeb2103bcf5517d024598b446276cd961b0f5e6db2c960a56f66aa00c162946d36cabc61c19aa19f36bb94acc2df19bf941e2802b42d87790164493fdabd924f604820b1d20ce3e156650f98323009576ba8520aff34711376d125426313fe640d476b00c4bc2267b518bb97e036ce5f8fefa081878cab2425d6f03531badc00a57af7b2ff439c4843ed0f914e1ed2acf87f737579f898316b68a7487aeae7751d1fb1e3c362d890f0b66386ddf4fb4e86972c6e50d5fbe8d73af4d91c73d576a8c2a48f156c26cf8448ac8cf2e82c1852530b8c09fdd8da343a4d566824990d250c40217c51efb5016309739c442cf572f107b7ebb1af3e40e0262125aa9ba45078c373e9e6e4f8684a475516a26d2acd2601419a3e368205fe8b0f6222f25e1ab4023db6abd288b06b5af75f068b1ae2fed15f42c4ecaec512009ba700f4f50529637abac2084372d4bca283370c5dff94617033417cb63366099eeb0f2af46f5a5db5bc5f3a0791832b601bab01671d3bcd05eb1bef004abee53f671e6dd6aeb40399cdbada74d1f29819987815fc28c0ce9d547c85c4d7dd80e9ae7d40693b78e9d9bca24ec9d6a95f6373c77770daa6455ddcd44915a87f083497c3139d891befa779ac801c8656a8ef5f139fb073cedea461bb2a7228d1779b542715692054fb220b801a6ffec330d294976a0fb669a3cb1701d1ed0b7d30531f1dd44e667f6455942a1c227aae8cc8440d43db4d4479a1421a20171edc4c2e0e9ed679b3e64fb7043bfebe51bf9c91c124cc8ad0080320cc55df269d66fcc9ea4b9d0914d596c82a58d86633cfedb2a5b306cdf8b068ce4006bc1c50cfa450dd646fffc0e23374172db681f0bd69315399a64e62a5ee407feff745eafe0b34fccecba74647ea0390223c10d9ee0043184241415216c28c9f12c8932cf9e1e24e37b851d13471d307af24f9c32cc7c3a14a3d12896ff526c5dcb0a764482db00e2334c2c67ffd7b74505f398fb28b596a02be815686c60eef804c552d5d212810cc303568428fe393aa49aa20117bb65809110b0c288ceb55791dcea52c9d102b67f2f7250b033e171782081add8d0c4cd3fb8eb431bbc0daad0f71faf117ec67eddfdb0d7a6fed7e88d0f701c34b4affc02c77035dd3eb4335d22d2b6d530db33c706702fde8f7a34ffb98f5826854aefcfa58b98fdb8eebf7453b34ea8839f861bc8eafb483372ec1f04d4e207117b978d35543bb7c8d016820ee12918e049dc49147ab5737a1ece54e04f747e78308c3648cd721b642fe36fadba12d12b874f5dfef511dae052053c0b818c8172d3e0e2e3f2a7340cfb720ea0271b2e761a056eeba8e005e1964487f8028786e0f91ab097e53198a4c57a4f12eb1608d25384522dee6ae4fa2c4c6ab086e4f04ec68ecd40d6fe5049af425455a0b2b4d4c02fec3cd23634968dfc13f97deb885dbabc40369ff53af1ae5c13c0101fe6d4f439fc32aaa741e5aaac1a743f76af035528f590c98a94e60610413304246d42c953e60d39661837b1ec07df35b90f254d02a5f0720df74227f976fe385597451521a58f41532633971f15f6d67acb572fdfa9594600acb5fb4eb8334d3e12a05acc1895a27fd6565247123e30309a17947bc897bec4e24ea7791b0a79f110c35f800bfddff85c1167c4b217fa270a19826eef07ca38095b11926910817895df9a2b0a9aa244d9eb4d62e76fbe567308c3ba8009d4e484d54d4417606fc69d2a92127214eae75375b233fc632a0dad2b1c62f435fc35d0d2440054cef7c361f666d1a7e7f711adc3aabb498b07e89deb96b3848fe01d2c75abbb2468b76780bd92208555e088d373e270cdd47dc84dab21f25a187a65287435708e3063095dcd4ec601ba89345aac718c665f146f08f0e5c2c7b27b2810b4cdcfa84d487775af3caa076fb660310570deebabe6ce249838c7a01bd087c6297a5b3375ff894bb0e183bb31a086cc896dd39adcc4245c0a88ae6c54242382d3cd7d3853c6b14fa5802c15967611613416f4bb907d0eae1188c94f833417b208a07a54c7dad73118d8eaa71cb33cd9173e936f472ed98cb2ff12b3738ea3799365d70a643e42649a19b6941ac090765268d2bd7f3c0849f7e13007b0119f719a267f4c1797f55c8caa1ea20aa2aed7c4d294be414c1cc73405f54635ba01087c481d481f2cda00221c6ebf7aad392fb44b6a39c6e2cf9b699f581cec7a180feff684d6841922aafd3d0695e8721a57f3fa213a5a139db8b4eacbefbfd2fce617cc100ba3bee211e8d507fa95495c5ab3d58a6af819a06dc98a8803586e52c9960a621296ea814644a77259cc4f957ab6a509bf3a90c744e03fa2e366f57401c516d0e09ff02058398f10ad946f6904bf807ade087620a1006232d47cd3cfdcafde9b266fb7f14a9e732ef206bf127e9548201b8b2c129c085d5f7d56e5c6085ed844f960f5bd5e6ebdf6c6dafecde524a999294327007da069e0699e78586b4d65a6badd50d57c4a0f6b25a9d3cc001ab66ffc26da5000d4610670d79d3388d8323655c738237475e171412e6ead0907eb70a7902915298db43c33048981b0719d27755e12c107d25552051787481887fe031e6ee34ca69206f4f1222dd919f8fbc3da10b74f9f6348ae2db4361a8ad42a2303e41d82b6f2fe7e0bc22f675d8c3abcbc2adcbdd1f77bc232837146843414cf51444fa947ecfec6eaf39ec60a1a7d05248946c2e70ad6662d26ef75aadbd7e6fd379dbabb5f7628c3986359d98bd18635896695af69c9bce9c69396f1bc77d3e5dd77476dd77b3f74f7bd0862394e9863f04927bbbef1ff6201b8e29866c3458c445449a4e11ccf5d8392cc7914397aaaa1151bbcddcc953578e28698f8fb1c6e686c562b1582c56e78ce3388e3dae562b2466d0acf6d163d7744b7eafae65b1582c166becb1c71e7b5c21b15acd40e3ba504837d21adaa236f486e2c81d954aa562b1582c168b9593d339e3d8e32a053974e7d5d32a513e75d9aa52a9542c168bc562b13aa7733aa773c6d538a60034e27de6ceab47b5c2adcec6bb791cc761b1582c96b3723aa7c7b6011d690db5a137f2a98f0aea542a55a7fa50168bc562b1585a8675ced8393618b119956858c6e1d6bc993841e01e954aa5721c168bd5399d859eafd943557445593a8ec110d0062404829d49b5a9b24aa552a91c27c334168bd559d0e4d09594be62d6b35e49994d94524a29a50cbf55f22585a21cc75913067d6fc1b46949674db70d576da854751f30e5791f62a2942df44921dd9753286bce982f2c2d6850ca96528eb9c191ae1c95d451e1b4c91486ff9ed7758e83654fa7f256cf7a95aaaac21ad47130061dc73a4e751c779c761c4d0e9d26aa624c6153e96a1d596dc014eebcaf2a4cac3654740f12a5c4a8e8e6f72a641a400dd876761a872ef5d544438aefb5fd12ad29a6bfd058682824aa46afe0c89d98170d0e2653d6324cde786b0783d75b3675df209a284cdb0ac4c4fd121dd735cc305939bdc808431d5ab05851e28d504e52b015494985b4830b0d991d7468c1624589379a8192a2e2e262523a7d36fee5a3791a1f8e77f972fcc907e3573e29ca7c371ee5337dc8030f2f4c3078c881e346ca0b52298554a2b161323269eb4243664a1d46efb7a4327ad2e85d464f63f432a3d368e495942c76efcd30c61a8661d975023282b0c185864c584a499991022a99ac9c5e64ecb0c348a5e43b90407a7ac27086c9cae945062964c5a2e725e285919133887644223f522955d9a08888b74a2f171a329aa6b9d0903199400f02e90181f4b0e1e3424306c5e5e3b18392c9d3f8789c4e5ee6e3b183141929272732524a2713126986c9cae94546484386347b4e24171a32271a9ff82edfcc9f7c2c2fd3f2fd252180192e70a268f9c21b962f6c7dbaeef3f97c3e9f8f0066c41a292317a3945187444e345c4ea79348344a2991ca702a038d2f00eff20120470b2a3f0a00004825d20c9395d38b8c914a29140a8550b49013ce305971a121a39d5e64a88144229d9cea8b0c52a954fad21a4a9fcaf7f819198f3299c2f0dff3ba2e1c7d582a29f148e10c9395d308a526250a1c15193227a7d7c09797126fa4b252da81340325a5c41ba9ac945c68c89c944c4adae945c68ac90e3bfce9d442d9c185864c48fa524aca8c1410a9b4727a916122e36798ac9c5e647c28c385860c0acac9a7eb3e9f0fe853202e3464524824527883f284cba9342a8d4a48afb5c829d47734c2f05bb49861b2720a431fac9ca3d8703616e00ab004f4c011a80262ee06c5e4c5a44422b9d090394931492991687c30eff2a152beef677c312ff3c578940f7479192e3d52a14ca630fcd762c06fa41aa946aa916aa41aa9462a0d26c6c99b944e4c4a2f279713e944322af17e3ec9c8138d888480f4acb05074421f844d6c30305111fc6021b1d3d2e1141303e6c58509068b141596965ff12244b9b440e96082c1224525db93cbe9746ad9d1c5d8fa4186f1071946d50815233d4bb45a36a85e59bd7a640bfd40f0bbb141414141414109b1b9a1e11331df9c39b1c4b08cad9d91099b7bd36a9d6a407c8cb2a554baa19d908ec9c98989898989c97f763e6870195b63e8552365584422914824128d3b35230b2aefb8185be3c884cdbd69b546d48b969293cb06868484c5450bf84224128976c6d06bc7c5d81a47266cee4dab35d233b233f2f2bc119b9116371a711cc771249fca3d204cd0f0075da13fc6b1358e1f146ac41573ef67e7f342c38e4824128944a29d31f4da6109c7d638a250209fc2542039203a36bd425d841441e4a9c8873c0b8a099b7bd36a8d2fe1d81ac711c69503028db2c5079633866118161a7fb8701cc7711cd71a6d5a2c27968fa475fa7c3e9fcfa7138dbd52d35aa20524a919c3305c39897e9c5ea8bc506131c27c2439243a8d43e23a692c1f890fd9c2cadb9673cef91482b4406c9668d5b0701cc7711cd71a6d5a2c23202d10929c968f64a42f974f348a6a444ab0802435a38bb1f56364c2a6c507d23a7deee9ec221ac7d6d8aa39fd184f1f7ea2f1048a7efc1857a8b048f9f92b545250405a236f48895827a41144670724070404d4223142a49f4712298ce308d26299a88d216872029e94c121c5ef2ee43d294331f6bece063f0abde8033d0e3d0f900775dd87e49333231a2bf23c78843c8fd077cf03e47980fef3f503abe943e49b355d28a495c930dfc583f9721b188182f93299e48edcd9b163c78e1d120da8ee070a537feadcecdcbc6ebef372469cf9ed29d0f13c497d8ee3387a9eeae76b0aad7a3e886d323d09081a52ebdd2929ee44fc291486c2708f9f4592baef51d075ef810d027f033fe0dd68b8d7fadb11846c83d8fd2e65403b9afd820062d5d94fc99d1d61105333a70d229d39372190904f7962ada1584dde27fa44be0f778fbbeb7efee7a65114634a75a80ed5a16004c4cfcf0df4b9a86d34732b00cddbc6c38bdd6b9ccbe9ce81c01e224f49e76ceee90761397076d6b66fdfbfc91df165d89da3b11feecdf148737a882a67814e417594e841a7a04d00219be0e1a38304124c2b3826650702496cbd628f7b7b01694f3d8fc26cef6d3a5aec1ed4812921442e07578386d446c8c4bafbdb808bed9493b2e5defb12830a3d8661606a03de5fcc8bc9a47d3978ec40fb1e3f5383c6c8401bd86b589649f125a6b18d066fc045ec1beb983572d8e86fb0a3d80af38738ce9d9cf2b44f85daa762a62b35c7a9fae1ce9ad9ea6eb9736ba8a4e7a3e96a145d87aa44dfc0d410aad7b1e3103a53a5c2c0117b8d1fc65be3ca6400420ca4cb640ea2fc104c1892e94929b17cdbdaa764a841bc2d10ebcfee5ac48b00b1ba5cb99f7dceaeb5d60691ea7cd371c569bf0fed636f8c099de32f8647fe1eddcc06f228415751c5ccf63dba19138d41b3942e76085addda121ec0101aba580479737f6072ea3d439619e26087b38b72522b4818c815fa4618010318d4a00639c8810fc45497ea529d0f33d0500fa847bbbf54a6711a7a5393d6ad6d3aadfc1984fefb1227b4f1cc622076eccc8c62948ad9d7438eb641c39beaee18868958d46eaedf15359ceb67450dcbf5aba296695aae5fbb88573e56938584488fe0818a0798c38d143138597355c407a6fc646f12e54ab2a6108230670e85914988426545cacd1b0a23a318c18d486905a78d0da6e5ac619a060a0182f42936e78c1ba5759d47b8c79232bbc2cc2368688b98ac1b2e36a855817da8d0648a7f30ce19638c3f1f10fd122f986aec0d5d0a374bca84bc62ae7391238014ef101a0a1c55e43602dc27aa706fb00d8715b9d7b25c374d5a0e477eeee5c6437ece00210ff20908017144fc20608e883f0137227e97f638ee6b5cdd3e3fb71b38640e297ec092ec2be0fff9ccfff3fee1a13c3ccdd33c3ccee33ccec353792a4fe5a93c3c96c7f2581ecb6379782ecfe5b93c97e7f25c1e1ecc8379300fe6c13c9807f3749fc701facff7c603a873ff4ff7f9bbe1083de87be32104026d1ab80112fd41e00d90d88172c301125db664f9bbe2e7c371450e7f365b0e2996f857e0f3f91d1445d17d40fc3d05dd706422f7b3371c9ac8bd46f3e7a3d4371c59e45eebfcf9ba5be4dedddd6e383891fb5a6bad77c3f111b9d76cfe7cd65a6badc88dc4a1db3ebb71d915f5e1d2a93e668d8e98f21a117f2a46c41f70240edde6d9411d58e4b8cf20f680a83d11220f9fcf8fe3034ad992baad9b0f58f279ff92fc44880e6ae003de00891c2865cb96c11ba05e89b85f220653f70ac11281e8ee7681e8c8144d573530417bd277aaef7c813e1186dac7a5bc6145daf6e976237fda4feda3547bad73feba5b7377ff88da6b35e7afd65aabf69acdf9b3d65a6bf327ead1657f331556835d3a3d774c591c510f6cdb3256b2813a3051d31e03e59631ad7583bddcb62fc19e0871033590c11ba10c4c6539374260ca7752f5d522cd1333c28c0286fdd69b31c5c2ef5616906199a635cfa49dd50fa464cf4a4f4f4f0f1112357526989a494c1a782fe85880e709c270eac89629328bbbe299eca5e8b65aff12e083eb010ca08e2b9e62cd9a4ee9956bb0f6f0667660a98c51e99434e32cf89724e4d2da0c879f4d2933654ba543640f93a1c3ca0c01621452331d40f85a6f9166527ecab47241b2e56c356a6270eacc96ce6c892cc4d4d419c10e0a5e468894d239d21338f8012d13ab6b9fec1446668c3f19156935a5b49b52da4d04cc033ff63247f38f85b8425411533eb3984511c1ef4f4c4aa2a665ff482524a2c6e54f97fd13891a080463a0e532200212223222f24699a4845432c9278f92fdb322f6c9190d4449515991fd9ba2d66225e41cacb2a57e0862842bb369e501b1702a4e8cda848439c9f00664552d0acd060a09a3e1d030fbec15a417445a7b5c5a1412c65241b3d1ca605d6be8919f8fe66a57875c8dca3a8045ecc25790a8cc7ae193f03c7c414ae3030f0dc4626f9286775e4186fd42a275795ed7b93e097443b016ec6c34da20166ea27b916873ceae3a1deb0dddfc5aabd603ad46ae504d0712558120d23474b30e4153499990685552c6751881289f6475ad0c1f0bb4beb29cfaea962851e818d75a5badbd8e2daef53a8dbc51ebee36bb419329292b31f6eeeeeef9d5ec5a2d6fd8f514714e20e413f7ab4cd0903ef65a0824444444446444e43f3245def47aab5f91e7f9ac2d0fe35f7c4ade9c5e87930ea617239279bb254ba1eb5ac43657a3d9d242925d5d93944c168b9580cd07d78d49c2891e6898038754aaf3965ae68b172d26b34d4efc8a6c34f762bc39e71fefdc318c7f18df8b1690df6bb30cd4719b88e7ccfb07910ccc904d87d3d086c278bdb1a16ea3b99fcfc54f886cf867374bbbe8ee9336e95293ba5b34497bdf25dfdda2ef9f351445672bec1ef9ee0ef96e16bda241dfa1efee4e6995ee6eb0519a01dd37f79dbfbbbffeeedebebb906efbfd201b4db3b8684142d22b218b152a2928e0f7272625d2c87b1b8dc87b21259ea8c4231195949478dfa26f128f044c1131f2443d22628b60894242a0d7bf07d27d42a0119733c906d69eec6630658b6095f49c5018ecdecd9df3ce3f0ee221774fb5e8efd831b61948a8eb7afee464a3c97f22a3d075d64ca2d0b5cd4a9b0efbd8e30f86effbc905fd6493279b0e0b8213ec3e2e288c0c3499921ab911e9f631bd926fba3cd2ae24cfeb1ad45d4a095380ee8186f5a514a2bf2a49ccc0f74b66946e2eb1070a635320436a5720d2a7d6082985dd5142a42d5083c21a91c76ea7dbd9e9fa9a41a49fd2a078424c817e8c2015880768b5d328501174853e123d22e80891d2771128742d449dc1be0e60d17eb5b6affdeb1763cc6e34b73b9c7140f4b72010fdbb3a9d92ce68ad468154bd4241aa4d4c815437c3248cfa20156805a01508816cca8931569d72f6a9086c63599665b31ba256af1d92e70c2a4ad1a5f2288e24a03c6e0d8254a016010c3a675c500f0c5281483051799c794c81487f3c218fe2cd72ce1811fd987306d3c78cc1447d60da40a4261fa4ceb47209d4878f1626aa994c75b650c08a6cf1ae767e7df810a98095576294c945a2ba1922ed70c63eb329a9c96b776f77bbdb59af76b359fb70a0973137a6bc7d73c66fc8d01fe4dff9f5fbf1e7fcfaad57ce7855912dfef913f1ebb7bec9eb8c44997a85ca2664c17a77d87ab515a3dc4d36e57c6d8e1cd7d6398724549fd32bbeb63abe764e1c34f527421b3e65b5414d393ce57fd26adbb1ece57b6732870f3d607be945813bedd935a3cdf81fb3994c51742ce1388e17638c2f06a6d8b3661cc76e42d718c618d72d634d8ace24657a3489b6765cde68a4a9473a1631e2ea98bdefb94dd3d4283a8ea6d164e9d828cfebbafbdd590d0da905e98f9c04d6172ace50dcc7d756a730d666af19e756bf6eab6b5eab63d9c7e626bc6e79cbd53313aa87a3edcb9fd6d9105751b19f6d00f7ca1baf36e7ac042962f31272aeee75cb5bae9e9550e20498cab376f706bafd27ceb00c63cee56ddbb66ddbb69b456d03e2c51fc80368711342bc3f430d94d3d6efb57bb9cd9ddb6cca94eba6c335a7d46d88c1977a371eb06ce333b11b683225d52fcc035586b5e74beb4ddb7df688dd930a1ab6cfcdfbebe6ab57e80c0667cf139df7abe2683225552babb2686fb488bcaab488fc854655c72d9bbe7935ebd53e5ef4e94ea3dc85436b5ad5e37a793ab44571e8aa775aa773dae5795d0706d1673080caaaac2690f07abd5eb3065e70b5e775dd0886a8a2e5792df9051a38cb59b20924f4ab5f12a8ded25ed527a5d5f1ad3ea7577c6d12acb517db767cabcf3bbd3e76c7186399bb5a41643b3b1c5f3bafc5d32bb6b785c380247c90f65a9c9be8bafab5737a0dbffafcaf464caf38d1b5579f539ae44baf4b023b511d5f0bce9db63f99981663f5de8aadcb16ecb7bd5669b5acfab5b9896ecada037bafbde9e6d36fce741df08931100b885f48b6842e207a363b20faf74ab5ddb4db278baee81148e8261154cc1538b7a6425cd1b9123b1fa19b3a5a3acbea4c960f5efd12606f0f9da3ebfa3d7daa9c47639935424763c67c3e4c6b35cf6ad5b28d26e391e21986b1e422740e86f84b619a4d87bf0f14c6ef57f7ea9b49cb4ef2bcad433a8c79e0729fdc5d7fec6754db81465f34faddeaa68342c0e90289e2d1236626ff0acf905678268733396626cff7b624489336ed124e8ed18889c82329198de4c8e1ae2779af2770ba1c41f40aeebef47db67e5a92d5a0265f7f49cef0c8be079e6900f058c5ccf4f7c0db0e2b6f86c62a9ca19f0209d15ebe5679d0ef4af05318c7181c83a0304250184a5948d41c21535309d11f4f89d3440972444e7489869add8e36cd2e8f1e3f23a74b4ace41f49733464ac2e1c6266a60263b885af2def32079d16bfd754f674abe8290321be915909f049654056c5f5249af80ee499f17413fe949a09ca9416b500de4473e2eb3a32a31444c511555a95eac4c866e3bbb091a9d2bbf218b48b5bc7d2428a43d0d659913659e5da560750a8aea2c0f5ca930009d99ab31a4e66409fd8a8ce011914e27f2ee7c6f646d9da6d1dceacb3462216979e3b15453af70a699c5b06a547dd1d7146178699b8e11d1a8d4231292099d60e8e6cf1529fb8f2e555fa5fc8597a6d3cdd74af65b204c98b4bdda8b31ccc66402cdc06482487fd29517ba9e93896dda649b8add8242ac6e2ed1caad1088fde4c963aa5473b5f2c16229510312523710c4abf35614c87455576e334cec429723891a0f6040cef16a338c457dd091d6d0155dc9162e7476cba410333d8d981c435d3d72ce39674f82d6d4ce59658c3489d8bec810ffbff420da9721feec7bdcb86df7d6ffa9c07b15f7da9ab3b558b515e369abf50cab6ef316dcc6d0a1830c3dfb30af15638cbd5aac5a2c6fc1e66c319d1cc5a4e00f1f500788b971f56a5866e746ff4530651253afe3c55cf0b65080e82becaf6880ca1cc77174c0c5a4953794888d5e2d38b59756cd566bdf6bf55b6fadb569d75beffbed7abb36ad2ece8c05f7e29b9b994ae713549b998ae0d57e316badb5268b598d9a5ee7a2d657e0c7e0005ac43fe7747904a8a51b8db55eb7c3f16509f18e8a318b61597bc5d8e69c6956cb1567bc036f5208b96675e904fbd85dd3f211996bef5fbe0e8449b4d94161e6f703110485c135d3f2e6e0dd020de96f349b7f267873b4d0cd4fd9d727d4a2e13cf90ff41446f4fd29210bab41ef606809d017b201d11618b2a1214501531825945953fc1e6f34d8077a75dbe63edc43efdaf6817a40536cfffa1ad4beeccbf362fe9eaecb7db9312ecc455d8f41f9c37c2f9fcb77faa45c49c99b1a187342889f6f4ec35c8d9a18ff6cb97f7fbaefffd0e7e27f3e9fd0d321421fce8910bbefe7bc03750f7a81a600f550982972987fbe3fefa0f70781be83653f40f43dd74ae15a28600b7cbc817c9f1441be144efdc95fee43add012211b0eec6df30d2ce1e1e4fb4fc08c37bcd1689a48cebe153f41de24e72ecbfab393cf9e8a21e3b24fb67d96e5d772ce1d6762f2a18d665bd97206bed69d7c08c302cdc949cef77e3a5008dbb8d6df3f79ed9e5caed528194f9ffbedb10f3de8bbffc88841c1c4784971413981d9bbf6fefd7ff681fe9fc5ae4f568027b71f77fefe0c86342182b64e05c47028ccc9d35f6142e43157a324b5af1410735114107351104ce1904568a36990c3c942373fc56596484322aed528d08be35a1898f2422d10d7fa0f988a71792ade0a8f85873d71c47a29bbee25654989e7955eca52e9a524759b4475ae7e1fc089983fd4d221d432855a30422daa1a42057a01a66234163015ba0053dfe2b5f20a5f3aa3bc71b170ad70a9b8443a840ea89582026a81205581581feb41acec31104bea1031e6ef5e5af70281ddab7b89f4a5acd1b93c57e7c21f0af59f7c5286522c7d299a9237628a6b498e6b7d5e3a628a1309017950a6e50c5aed41409216da3c1a3b5b3d6d284cad2797971830a8181b397a88093d0ae68bf1bd7c2ed6dacc25eaa7666bb66a08d47d38d74646369aec47360cd0687f638a58dee477cf9ffbf87c1441020b9d83ee030bc14f6d2afe8a534476845c267a7a42ceb942d7eec26b8b6a57eaadf7b67fdf7a31ce51745d6f483767679caeebadd73e11ee299173b698e38b5d9be987d74b79d72b866127b04fc1adadd6eb756bafd76b330cdb0cc33bbce66cddafdf6cb39541ce39bbe79c73ce5e7ddad49cf314f26d43ce39e79c73adf8da8aafcd39e789a34deff67bef756b22bd98fc87e69edbabbd189b73ce9c55d7ae4f2c5d88ac7db3c7332cd3b8ead8468e39a3312c5ff335e7b4b5f3f6a98e3bcbdb9d3d54355f13dbb00cfbce1a9b1a98cd9e5b6f73b66a5274f4e9cf57cf14ad6344f5f66ab18b6d757cb15c052b74fd038eb9310ea8020632c056ecbdb8578bd55aedc5b34a2e6fdc96bb63a636db6ba543c0d0312f86b13b6badb7b59cb34631e6f59244db978a1417e08a18635a7bbdb77a83b90a1a6ef9aed8f67a310ef01c64c026c980dd7b31c66ebd18637cafeddade52a8384036a65ffdd0ae9b38c456a1ebc6deda6f8a5c06b42fcb3a90d56bdfca965ab1bdd98e78556b2ff68a6fa6bd0c35db61370c753c91a3fab5b7d68bad87d65a6cb15b8b2d76bfb5126152acde6aadb5f6e23a1e611cc7d1061365ea4f51273c92d1844d26af2ee78ceedb35f64e131ec7711cc7ea266cc2a3ad5dbd14dc3e61f89cee354ad03983d640c2a0cfc40d640b7d1fed0471c2559d66af5a53c1ddabcd1c5feb354f21b4d51bb3d52bbe767ef54c73eb01a4c9a8e4ec9cd894d108000080800043170000180c080603224112c561a44d3d1400115d8a4c6e603a158864590ea3389252c620620001841830234043b36d00dd0ea8bd0c64a36ac24bc258935227f89b6e8c605d951e1a7804a72086d1188857bbd0b3d86a16288cf412bc42e72405b94aec9658bbe137dee0f44d9a69fe8cbdb0fd9f9e47c498a57411186ef7af626b875eaf9cb29c89ea7803c0882eba529d6957e4bce6e9f470d5b2dd1ed8bbcfb42aac447532daf4c76f6c6fee35208e662291107894708205d11a0c3be5b40ae17cb9ed91c94cb5a1af21525ad21621554ea02f9301b94ae08bd9e74c055b47b3910d27d512d0d76022856816472373434a1bcb689a46cd7d69c7e0182e4d6ea718a8fa973a36a0084aaa628cfb155ac869f14488950a9bc297f7c3e962c98b972ad32ae3264c5f96559af8279d8acca2b214f695209574eea83cc87e4df688cdb740a01401ab57984e30169ba0584c0dd695b34e2e59be62a70a453cbd16242fc65e05b38b450eae2d7bc13d6e0b6f958b04d7ffa22d8c1761b277bcdf3b150171104c5d4e92d7d66008b8146eb5974e0e28cafe8547c81042f6af1f3093c37a8745b8941e6e84a516867f2a4bd351ff289f0286a3f5b2619799cccbc8efbf44014b34745136da181358db558aca0818707a238d1eddc4ade32f7784c8cd116c15701e486044258c007ced35510f629c817bfe59141056c86bae58a028c3ef7deae28785ad224e449ba009f9a8bfa044b969f3859e0d2684d5810b36613e1d84196513c76c50b876b0ed98dac23deca6b88391f2243e3a6f86afc1ee88cbd9415935df67e05c4a923f6c3505abd349ea341542879a7c3c70a849a938c884c8ae113e492effa58696a8667a1a49e8fb941dc4fe00c38832336acd610a0603fcbe5921bab71d1bdded20aaa06cec8bc5875fdd35fd9e510b188c79a35fb22045861d820cefb5fb0483e189d1b03c2d46107ec62271f1369bc14a3a80057d73a1527ba6c464d976638202976ecf418574d007bb822203681719063058986bee0e44ddc21e63abb38877a9d42125305f16077a252c8e9d898a448396a900257c729adf480439ea102b7f047bb00e45682e8893496a2debd3b68cd96c2354ed4f67bf9badd46352a137c72632293fa13686450b3098e2be5d47adfa8ececb902833a5f624e39c488518639897736ee74208e5bafa5c223a3f0479d46bf90b06dc33f9d2d8bd695e567919ab59c590ecb098ed3e90b5e73de2cec2637b542db7b333de02b7d6ca17ec71b1aa88c94170059241cff51d080e0d61dc5016aee67c1917d101072867a935f182896421162a9c8225d098b65174c11427e87365175d0948ae33999e70120529a99d3a8cc7e85b37f695b81b9b55d33ffbf0aecaf086175f0bba45ebf9d972595143c34adee456b08483ef23a4417c26bd8405b7f860790fca138bbad5860bf6dcd44c3d3f97c53da5bcdd4baab05c96876de805001dc0e0c45d81d10d26e9ba881c9344f609e5336987dc7f63f0daf8e04488428f6d26bb0e93568688c9d8a0049d272bc10e1b3d83de27ddfa434827a34b1b201107bd12ec9f2887cf0117a701b184ac5251f17544919f0fdabe152272ae075ab68c6882f29d67e4821d72cdac04e3950610e746aa37a69db296374fb07a56b7ed2463a7de816d361588873f289930699d62c4c928e1078113dfba1eef380a578241593d3184ff8d9b7bd4f6f8629d867082c984516012970b6582a0539437f5e6d3823372327fa4e6721fef59074b97350927d32a0026d491d2ce7918583d0fd89024c2fc5a72078531c1d2c39b4e4200eb1a126c34bb325bffd4ce8ad10e456f20fede977a6f1e30af5ab945b483e144833cbe0827930747875f26e8d3f5472f21d83fdd9ef6cd09ea89eb4a48df5fc7bbbdb319b0c612b39e0c6a9d1b1c56d615930da4a4b23d3a287bb391c076785e6efc8135c331da0baccc5d170729515c5e54519902dbc28d4cb6f3e0f8bda57a65de344b8529b9e55f3e168fe2f6c2b83b4077bbe529ed821e1d519405f810a7d33c29e3ed4cb23e1ee57085528c140d4f0172042663e2f15712b6ecd2428c91e242447629268b4a84d0899df78b8929ca41002a096c8212e41ca74adb32d3173c0706edcad47f504c3a005b18b497716832d3f80c40e01f4c00bc6e3463f71d0198dd789bb84717a9e32afd0b55016f75f5d5d9b5ace9f72eda2c333ba47a6c6b08acf6ffed3e2f79bae32f2efb90a530e755eee3705880d1013f6083d1aab2d94a2f3ef3eb0eea11265098a3e9a367ad46768fa87ba6e92e2f4950b81133e6ae070cd9758ea1d89836baa1e693afb079a94107e43484892ca5d8e4035c509c8d5e2fdd24852dd5d60c4a8612010baf8e185241a25b129a9e5e1c54dc5aa140de4aa859fd8049c19b9e1cc493af2c851c30fc65f282bc8011d6d847dc3ca27ee47aadc502169c8ceab76b4850ff3500643403960aee3c4f8465e00a4933e6217a4123d45c48647a3b658e466b606a740bc0eaae5c2f8a7647f4287eee507817c879f90476007cb79c939a118110ea4df0eff511c592287a7b4ec9003a9843860f95b6e330cf49baef62e19dce5f28410f5d5db019a72d94db61ec5c0e817823bf12e15f3b151a81376534c08e7a866d45a9a720abf3adbf034886876280c9daf84080411a378869619d74bce42360b818447426a10ffd373e1d7922abc55cb685944a845618982ea404c5f1ffff8b41b3fbd741b2b3f757b47c5b170aa9992aeae1e09133c065771e6dc4ce2bfdf77f5e3ffba30717b37e22f79764da79fd566aa151d1d075d5368481498f94a102ce25a1bc616bbd31addb685b51fe47f831b3ccd6bf6f7ce9761002ba46f0c99910698082bb0bb0efe51694b3495f0a9d67843b45a30f85d6e8c59e9dd59ce2fa918a4371e577f94ac5fc5add26eb8e490abfb1789317c10376aea1e7d1503d363f26b838df804c1680f8c9035a340c790af678356e6382bd0a2b64dbaa651623749cb212ba79634665c5096e560041de91308f01d52ef5712e01fb4a0639151b4369a5c0d6cae4da7a78dc68a39f50b5909870a9e03fcb4c65a8303d5ce867328fc324a464eeb570e2fbb784bd53c6ff8c3b81ebc1096084370d882f8e74b0e999d9c15cd980c9387908eb3be72998e80b4da00dc107dba5b4554f468777d97f59b8cf375e059184c82237a8bf6463cd54abd27bf6d241760d8e5d83beeb5a2f4affd4b5d1f3a0e753189f0c74257104a6fd2d5b2e8faf1456b60c03723ad52e1fd72d63b6328547bc2f07a0ba418f9585863b478156ce98c1ff90eb7e4da4e49c13721d2f31fcc96a697ce1745cbb42684a2404b460b645c667ddc9befe99deec25f760969bab4f372479a155c180b4ac9f4bdcb214d3545bdf7b411f34bc71bdbc4296f93a721ed4c6f59d028a6cce3233ed743c37fb54faf3f1c73582d1b921865ac0c2214146a5ce5f7c71ae080634a0a98fd8dc70c86190b114b152280f059041edb51db450e5022697a06ca7a554e612a07545cc7a82bc1f37aebf9423c918cdf512d71da1d2c053ddc72318aee6706de861509d1f265dd3e807281408318f873d5bb74285e62568ec2051895c54f2c349c7c1162f033bbea90aa959d76a5fb7f594646dc98dbb0892021fcc1e5c9f07ced4503723f39e4ffcdb69177ed03490b6e2a1979ff0679c9d275e732b6cf7745a8b53479df66bb3b2b3591645378e5b8fcebfc419bae7c2631b574a8855a2ba9677f698f8ae391d13c700066c3c787c534d6bf05a4275d312f316641505769f0409e322bb8d98a078919ab467774a082803e91c10962c7e8b07691761090e6b89d6a164154573c457c84e31d8d02ed41e884430f58c42a37aa7f3d3af6861318afa40f858080e60c608df7444bbc82f81255c5e759058b7bd8b7de185ef1942b458b5bbb0cf4e5dea2faa630cc994790b50140b807511a5c74dc9cddda27648570bbaed2e7984fe511177d944997dbbec58d48dd29e9e74c0484121bb06db5215d40c46023ba8c32aa4250cf35a358110e2dc752dc7106186798cd37cf6af75c7a712bfe75f246dc758e7d5a26028d68ae244930fbb427c11fa2d5e79c0301d6019f6661fe4e090f9a97733f728d68b448f17a1df6d3b2797645b6167d10533f7a3fab779953b9aef8fad69d13c9f985b86e6d59a93466bce9e11c9d2454f8e597d918fee116400cf6eb04fac6166c1837781197a7e4fbd38a2e02f9f5aae18ed85d4a742623dc3d14832c5667ba6dcf8e4daf63482f6a9f5b93236060954f355830d577ed5e534fa03b3cc911b1ba7198f24d3af04882e025a665bef2aceadcae9d6aa10ef7c5bb5706f0dfcb652d06dd7ece546cb074d890c2ed3849610f28ed5d2b4958499863ef426c84fa86fdeb813ad16cefd7887ce34c9408acc4a30324fe34e3b043d2f54ac922380a38d44fd0387c5a992b62436bda887a0e0c8399ffeddfba6501b1ba3c6c8bd7c0e8c6110e5e96346f637edbe3c3b0fb8d9200f76bdf2c2efcaab5603d3afbf7c5529acbfde2755bf2e505c02eccf27fbaf4b3ca380d6bd6a4468342e17377b8165340d7c1b6693149d37d78f49ebd5c7c472bb5d09efa81a8123a4dbaf8053cefc0a30275ccc6936d8cd8695d1ec46f4cae52b66d9671dca12b1a804e8a900e8e7ea48a5a36b2180563688ebb9233ad5e924927990cff40b91bb2fdf05e5f436208399292f4b9df30df17d1899d21d61d373d11114a4c92d7a2fb6e1c96b76274f3bf63d34bb418537dfcb85179c738f7b51e3f56f613a120620a49f87bc230120927920af7f1e8b3910754c1076a63adeefff58cfd54ce210ad0060f1fd1f0cd3a735f4d4b1abad9c45c8e1169e8380a7cd8ef058c59bcd0d226d48d3a431083e84c3ee2ae2fde3b9b50ac25c49a896b2afa1e4f384a5807f617f65137f6261bf2be7b4d242948c8dd5e500b30fc28c1a34d233fad345baf0e1cb9f1faef4fe511cfabdba5dd5bffdfbb75cf2a72dfd67889afdc1743218fa29e8de4a6e10c7ec96b75f9a81e366332f758f7ce2689db8b3837af6376e45fb80b6358c8fa02605832e87979c8083af3fab3420dff95f306d1dabd1cd57807cb775a9053b01848771aaa6f2b83c1f41350ff9ff71972b3cbcd80ffedaaefa997fe01d959bf24d01d32f5dd89dee29e377c60e2ffe48221c3b0bc88d749ac858a599253d0305bba90857bd617586fb668d73a0329c6c9c0174ded1f0b300bd44e55368304b48e55daf6f54ac4f8df4c79b3715f18a72bc3980f7b072e6c443390b9b482f6342b0f16c42bbf7be7e51b1c797c3c64545c32c427b47733380f0168d3384e81d8d6602c23b1a6f563a3da15213c844ed139790f9a04edd0833eb804a70d06c86d0dea1d10ca07943e32c427a43b3d980f08ec66756e1232afa69c0d03f58513b6faa5870d2d6d2062b494c786a722ff35ae552c9043dc5ba2930c2ab250f625f381113059f6e0a6b54016aad414b6340c37d9a7253ff0ac574866baa03c90346580f6c30de631b714b0ed24cfd5ee2dda6f04e3b1ecc5dd5118f21e25e634182a83e9aac913dda1d8a7929ecbc2f76d15c77ec12f84902f49e186b0435b584d97bc2d140cd426a17087e60c40852ce078149ed93a502e4b293ae68221733b24d7170a32109b520c1f45d80fe2e599c72cefc0a551f7264da2ddb5417df90ac1d8240b6bcf9b9a12ed8b1fc7d9ed9471e69821b7d23ff13fdfdffa079cf88eedfe9b510cbbf14fb7fd22e24914e6b9ffe95aa4d753a84b89316b371640ea3dd2e3f515bf7ad36456b35a7392e36b34e2a9dbd8152e3b8443305aa3a5a32e4a0dc32fb547cac06c67475d35f58f937ba5ca6f9b863aaa372876e52aa3e79df2dda54a8a5cb4434fad6be04a3ba9b6653a17c21c124e797c20b4f6108dd6633f2ba6a29ee32eba6961d0b6c6a2f15033035fe1333463b959a4df912dcb72d093a30df367df3195ae9cc40d628b68999199b7b106fc8572520be34d3c9512cb796b435f34decf3c85461bb5f53091efdf5327c8a754d05a27803f2f186415d36d69456d52c1801cdab8b1bd5c7d2afd5373ab3b00ae0532a1d5afe3869cde3d5afa9ea03af11544df598ce4ea43879b357d823e76a093cae292de7c746df39ce089292934163a41662c10662c7ed33fd0d10b6e33e5426574c309ec5f3dcfe39da92a229d580f44efba299d7374fb8dffed7ef0c4bf4d4bda6ca3499d420cba42ef33ba199a31e81befda9ce424a95b29bbfb2cee4e95f0c6240492990fb934aa524f46b5b44f83ed4795f27d9cb6f53bac97e2951ac3e3152be5f3868310f1da3dc42db22c80ce5b891749a2e33efc33555ce937e765123c8a370c37b8e94cf48474ab089868dd8a34e64c013cd331385f0de511f0ab3f30ef9e5669edbca142aca44f4ed5d52f20c962590078c5568a4f557b22c946591de699bbcd55ca04dd5705e656348ee3f18da14326cbc09d7c1ab28dcb949d4ccc9d0a6e241aa2274757aac9cc3f5e87752914d850411be520319c91199a7f7b25a6776e8ecbb2e604d19846f6fa5155d9848b55177662aa12cc5369a20cbf8b75235156817ecb8e2f844f8a8d461c843c5e743c141894a6a2ac857a0f39751cac08ae6c08281b998545789eae5e0c0f2de3309ded48e354d2d2e8df7336503a3d5ce0d07fd3ebc209d5c8e0441b9c4d7e6d5fa73f4f5a003c92662d54c570c9a52c6adf9b7f88b1410c0318724170529f9e021d6ae573ec5fa544ea722f04c2127ca54691c68b14a4f5a9e22404453910be252883ab7fac30c0ac04f4db447b2d5c8fe615cdb6a2ab9a090de038aaa7c71ff17ce974cb538ca3a350f26a5ed3f72fa60ba93ed1a17a7830953fd726e8047f54c463191475b8439e26622db976ececf8826c088a5bbb9f6276a558545c0821f70aac1a4ad700fafb66b7e03a3dfacffb511e1d6393aa8396dedf3019df4b4fc7fcaf6c291c167d982b354b0338bb18cc21fba99eec774d3a0383e39724ce1a1548c7fe147dac8ab6b3a4df353dcffd46755cc311570ba59c63a401ed62df81868a902223cd6284ebcbe671d1c818d94ace7122d378bf4e0e77530364fc5ce73efbe74ec7be9c375a9e5879625434c32ed20b80a4b9234f831c794d09ce3310a17756e3e52feeb27744b8938d906e171b9f011181bf058031f173f40ba9616f8f48d53e8f1b5840ec3331f5c43c4e5c1af8830a4fc0be158fe6028b96b98ee6add26b50e14f8ea511bc9be76f327794d3195c0158ca993d480212877c8472f403690584d3195367f851eb583b7c169d9f210a22e759168ad890f072bf3efe2d07a53da43b4bb8090622ac48231b501c1dc785b178a519ab6e0e7f26b1fc5f4b0357fad0b465fcefcfd96498aa98274101bb077892941f7098f6c25449c261b370c89f4909812ea93da2d865fe7e3c91d9bc1a78acae835d16742727ad1f62a73e957c38c612aa27258faae5f6ade31d3870b3b86ebbf618a67b6a428b41d116c4f4f0ae9478af4fc617a43a08e025772dae8a1cd7a627893309539b25b9d1a66f7fb5c4554927c2f514adb7e0553388d0cdb914314eeef13058285a9359d9ca4b773ea5358e25333913664fd32482b178530256ea51b22eb33aecaefeb2ff711794cbc9120ab866b374ed45d94ca886d70c1184c4579f31f8a37a09a7c0a9e33766591440ea38c944cd4b04c28adcb30fdf208772764fadba206410378fd6b3a838b00e51ba0593b92a8cb7708b7b23bf6c46dfd9d9af2c4d4b7b887d8c0c24c6579a985e94973d94c27a05aba849c21b6ef2544aad1ffa28b9775ded3a3380c9df416136f30d6123b30aa2f9ac500cf7992b3206f900c1caa4c5e4c345c9e8d4b490e0d86e69a9a6ce3d0706bab30d62edfc5885398570d971411f2543de85202bd1152d69bb3b12f50e57c4ea04bc18aa75d297cac70a648869b2a4f45296ac46d05e2fb41d502a42773c27f99d1532b1abc8ac96c3c3a77b914112192f107533cf5fbbbd80c3a5885b9feceafb1a959c9c6d09f6fa263cb335e49331bc383e3525ab931a6f72281d7a440774df59f55f637e2d07898a3f3ab84e15284c0ab85d712fa9d1ddc596ff35b4ac7ad587b18d0d4e32d3c2f568dfaf1d25d8ef72e839cc6362c929fb491aad7e351b74253585d19deaede503118dd387cd14832d5bdb6693245ae55c64917b1397b9af26b5067166629d8a44cf9549f5b868dce88b9f1291a954cb104243ca9832983adc6df0c19c2cc2e3047c5eb8f853ba8787dbb87f6ee877ebf3ec0edefd5c883461578355f1c6fafb6429a54fc51f28ce18d5460298b7e9aa4aea4d4a5493d75c268e730384853649e548d24a9bd9a3bedbc40820ac2f84a9e17cb137c53665e5a69748d889ccd0bb7717367317c4038f6e71b591a5ca4493dc7dbcd3a3ff5d701b87606f104c24e2609d7deeeed86bc33e768b912806fa5494db485cdc23293a6591c9a8757a0065e589b56121a457cfe9be9d6f4ad4eb604635c8c463b099eb2579f0b8319959910bf39167c0bfe0391a4ba330efe67622b5551e69d2c3903156752dcc553aa8fc7d4a3094172981af1b448a713fb2f41ebdc7aad29d591546aebeb7271d4f8b0658896496d014a0a056068f8ab671620288c367f95f2f4c1f00967738aad3a110269811ea261ae2945be3709b2a4eb737f4a594ff0750a53c16b4a158fa18b480642facdf4fd1c21d5619d0334ef8ee861fa4f89bbd28ececfb2555e07a2b5aca03d2443ada64f542c3e4729c1e49def8fe7566270a0d52911014ab1dcb3f3af47a92ffb73fdc69f0faf33fd885ccd422e01a8ce56e17371a428a5d72c385a77eb50483b36f2b1628d3b1bd1c511a8380fc41978085bccfb7a73f0a328a55522502376b57c3908d38cab14a51e2e9c92d559473bbf2bcbebef82e0d2bc768a520fdfb8ac8f37bfda18294addf5e2d89c1cc4e6dd8a920872e733e8894e17fe1948c5eecaaca88b59a18b59b1ab77578326d44583b27c1e941bc3619817148879263c54b18c5068d142d52a9174d4284af16561e9eb35b070e3f1c441987e882ca4ae2c7a3c12a5d2e4545f0ab5073e949ab400d5e5a1543f59b957a2b79b1e11abbaf6162314bb82c1ee8b4aa25ee6a194266af7060a97cc1e4a796a6b5615b5ae3e94229ef1743c479312045afad743d573835a8ff0bd1b8bba63d39d67d24822a19e4f42abf793daa809f98ac25624d4ff19fcca52723a5911626b4f2ae1ed7005dc106cb4d40c05bfa7630a225571e421525ff1037cdace5f27ec499d6df3ef17bb3b86836f6e521fc4fb3a4ce686245d3a5fce80af4f0e87ceb7c7b7fdd92f63597aa5a8ec51aa7f2bf14a5159c2e9ca3d5fa964ffd0d30575a5d657307d703b86a4ad9370d3ea8347b2fa75492b7506179ff5445a1b2cc70089dcca5fe8caf9fcf33e272300a1f1eff9ac9a518d67cd7163cc961b26d555c48bd748f560d1d3c4fe1ca1d1941a632a98fe4e770b20e3eec8978de336a9d85da628042282f2897ce38ebea6543b82c5b2eda06fc61ba39c096907cda9c230148b4aa96d53bc7cfa6450372775c302cf7e036312be241705b16e999da0c287153adf480b4a3153ae6d3d90de4fdfea847b064059ac31b2187bbe8cbe630ab9e9bd16ba804e4abb7f84f3ba11968ef7c2b2322bcc48f814d93f60055777aa02358df86a3473844bda6ec8463e51878c4b98eccdeed6d68e5d3156bbd2ab812792688e1cc89f9e1f9543461c7e3684ae824d5450c18c02b5940e58edcfe5c892e435c6ed8f472dae60a26e0d5c249182076baeb5c4785dd0af45a783d9b41ba0a4e41bc41877b761f74bcf0f3a7b018efca9a13072a7eaee13e996d3b9b78f4528e7aaa7e253007dbd078b9e35a0b0a0676ffb7b8561901e9beec7595cdcea044b23310fe4230624f8d741268519da41e1146e8e35b2625e1d63658604e94582eb2f405cd7a26b7a733aeb1308c2a1021dc80aee90c8dba1f7fd3024a9189c9501eeb20827aa2e9a30caffa8a49f3e1dafa041bd2adf10b14c052b47f83140a84aef130b0c0ffbbaf0b5ed37ea425a10046208b0dda658fff85879a99a4410e65f43afff640da304be9baa09930f320cad44834ca73a509a2951f549c9febb6898d504542ab231e3e74dfabb7782d7304c29cad6422d9ff9c6c8e1b3f7c231addfc22059c4ab4800be92724dfbfb18da9c2d484c5ba6a459067cf728fabeddff83b76b4b1f90bc39886d1717730e83bdf911433e51d340738c46e6f768e177a339c8919992c9cc95cdac85453e76280ca12bf40ec8f4208dd1b81b67513102a92bee3fdc3820af9f368e9dc19b540ff685ed82f3e36410613507ed1d8851d0125638b8d3ceda440e4000eee4848294bf9dce2650edd191517ba5471c5d6fd4bb577b76a67dbc68478506e8f91d6a0ec392f57d1c00e1cfa25ec3229f3ac0e0974e88ef0751d3dae96760afc21afcb79c69718389d99da0ca460224ed647eab8e9bed344a21f8d2e104050432f941e5694a27cb089032bc7a753f1dc7c2c715bdf9f9665d34540260ea1a51d40b0257434485bdde463fba0a083da95ed8a934e2eaa0fbc817e756608751dd1369e8bf2c32c4ecb0a2e3309736eb5a7b9e74dca0276997fc4bca10d61bbfabb4c1cce94b2f5375eaee6112f9212b3cbde4ead419cda5d16765170408c79af9b356589d9e28600edac46841690914b23aa92bdc9a4afb94f57574635eb126d008ce7090d509626a76d3e0f8d500a0833ec10f728f414e56a8aad3b96a514e4ff5a133c1429a69bdea44bcd8ea2109aa0028377c808b8e205975faf32c8512db165827fc6f3be27a0ebba86b5d90a9b0cca2bbd6b8f9712552f67d0255fb832374d1c9a0eeb0530fae0ca5447410331be80d84638d670d91d0677243ddff273a65c16a3e493c9aeae591e7d9e606ff945193d8cce1212c97a187d1209f29cd2e86ca72ebc090e00d78e8a4ecdc9b6e3576fe926a0e938cdca204443a7e1fe4ec3e3f765b8766f0c6fd7fecf08a74f299ea62cd2dcdd00a791131e6be169dd4e58dc76b31f2c540738354669fd673a0998e0b02a2825826938344b2d1fce07c0c12df39172e5865b9d43a5de331dfd0093ce9809d975869376e1baa27ecc8fc5c3c6bc1fe053d34739329ad6a22b796711f9f81f8b7ee1bb6474327f17c190edabbec5905c8f18abb0165e11b3a6d615eb8d4251acb3bca75a0aa16d01dffa388890d50228bb7d029a1f68bc12c684bf720f09e14eb4aef3259c903b998110211b9dc69ebfa4b967f6725054ee9b6afb606c871556213277730f7ef611c06b57fe40c3e0c62d4c2cb17621a6cb255795c1602d2217da94527a04b8dcacce1ef825b0fb8843f8e9d8374d169d096840ac0095c20587c2c0df4e73b00dd2ea166e044beab6d93d1eae6705fbb764a500ad1897489cd2ceb68e81491dd130fc9aac787dd9912ab17068f4a6e63576ace738d7f32dcbd47c50608e5846fba664111649968547452809ecc1bba376c7260b13ac247081a0d99689a218c34f6e98ecce8f5a7808a46e34169098b3872a569a4fae42e486d828e50900a3eb19249d45ae8c5ee3789bfda1b2b9c9609c88ec28c6db5578fe4b261214f373a4036dc7fe80061274192ea49c19cc07d246ea400c44515406d06a505e0e524d3d3baa9e0574e3abc3a44b56588ccf9c83c46534f84db9373293bc5ab38e5e5ee1995e22baf46f5a5714f378e83deda48aa0d528a30eb48f64109d8c88de4fd2737c04b89de02dbb5f5d27dd92179a24e935d40e65474e4d4640028ada2aab1476e15638bad0cb24330c1591c3f00e6c48b0d45c5efd231c9b6ef167bb87367ca8f4aa798958c9238390a548f8d3621182602c6d44a5d38e4ca9c822f27e2562b34042d170ecd70b65b8f3aebed2829fd833f6a1349b092abe57d9fd917ba9cb0ad5a262fd1e2b8a104456c4eb74b73293dfb35676c378456051fb332739d4930b4b3d6a5f130d7a626527cf10141ee3832cfcb44b91259d238101544414f56123add2caf0ec474694a8451fe497a7a38f28b0f4cb1767e35cc900c7b9803ca469351e9e9df3b8bd702df909e253450d3e97b370a9cde3ee1cd895e66a578d7e3340429b0b92db3262f4ee9bb9963722c1896aac58b0dcd42d99326179ae965c74970c78a2de735d614eb9d83eb95e384c409e9f351c6cfa6c3d9f515ccd509cfc935a5ef7aa2515a39bccce9c9450024842afc2530b0b9e96acc056935e5113ac2ee948a89c630e9b513b4a3e628706a9d3575780c4ec2f402c1fe60fffdda31ea36d97ed31a20a8def139affd4d157d9c04d86802b73c959f838e53127ee0d6ef90f9e0b55fbee2a16703af7fc4b115cc74bd7ea697e7a78d5d9c04cf33e3425e34a48f842b14675679c75603434ee1f066483ee14fa48b7b7a36251d277b14a82498bcd31dc3a95d95cd02f28ebc0ea7d11cc46a1c0ede9f3b94003f0ec2b7758fe2604e834159bf41a37ee071df20035210d073b4c7b2847d4e75fc18acd2318424d1f421784d8f8058fe2f27ff4a385fca89c999fb372e96699f87c50309936eb39abdcf827c934c07f5b997243a5251baa1f773b5667a1a65b77e0b485eb8991109cce4e42586664f4e4ef6fe3f18004c5dde5a81608172b957917c79cea61572c22e8785a23d90ac5a2c4c2d91959b92af1b77470e9012d9a3e86f0ec795637a9b67ccba37011a1f3f8ad37d81d976d6c99cb8d2076853949e3d7b3cf4ad9a6271c86e7aa7fd3b7b56257c24ef1ee5109d6f01871cd479821a046d0aa0572c98eed0c700490310ee508107020494f11405e0b749324570680a377c8f8909334b0c33b8a7c6c2eda90b054e64ccc875300a4d06b4bf30d49da85e825315829716dad0ffba0714ba5710d5ede360bc183eea4eb58a197a67e9a7aaa9e42d68825bb8fcb4d2f6f178fd64e19e891e154e9fe9cbce636306e3245131b3ec99dfb0406754fa69ac2db8960d42c3d4996fe5b18a026b138f973a7262c6f3bad3f626f60458728de55de9248aa1df143656c23a35a4e1ae9a82cbac5e11180a7a7747c31e55bb596a881246c7b6c7cb806791464fc5227984405ea10a625fb3505dcca08bce12f7454394eea78d924d952c68f6cfddac43683ad4b3fa80f7f25e0d175d3ab73448acd1d8fa3d720a1d5d8ff446b98d8d1ed3ff0d32fbbaebe2c6c7593246d84a9a2b1d1122ffad11d6912f15f9e1f1a01e7c4bed0398aae9910665d23eac8f101206e7c7ea3b14d15241053fe4b1fa36d8580351390305813dd2ced9abba1b5aa2519c63fc737604e703456e1d7363fcf8417759a28a4f801c671e0c9861411901fc5bdf874fcc1c09ae414e50ee63684937d42f8ff811c4b41f7ef000c51e3c433c1041b01c4cf7cf9c125f865b8fe09bb08c45f78b4d67d0d052ac5f83812b9375cedce1828076ae6138284af690151aa770b9524f5f911bbca1258fc43da8287752d3b960b0ab7aae538401b822412533ec0d2bb7ec30f07c2309f9f791b5ba0ed860e12219292d6ce9398d604e026849ebe85fb45a1b5c656672f87ca0b314a812d1d1dfa9bbbcbff3735bebfc046709477abcc3ee1b6ba7051f72abf04ef42cadf06aaf7683fa0d6503912b1769304758321b49db15464b0d197612943b90dc5240f098436b9dfbf8a34fbf2dd67630ced26220e909d7912864f088ed08906dd18d9a25fce3dcfbda38e797b6cf98d7cc1c280f7e9ded2ac0a3c6aae26e9156ba557ab440ed26a02ed6a7bb2a6f7b3b12b3c4849fd47b9a0a29fd5aef7a593294ec89c85a77982c99a2c7dd2ccfc659da53797da099e03de0bbb01a9c3f2cc774d859fa4efaa54a225bde754d88c5241bb4be59a2c47a94d1d5ac252853eaafd3831ca84c46b89aaa078f1e1263a4b892a13d7c0750a43255160f7963089c5b87588be14a428a7c8cdbef6f21d9128ada688686ff5ed87e041a94fb3a660a8b2388f29117b3ce0d50d4c059d4ab70a7bfac7b4ef6c158ec79a4a042c461965221172179a526230a82418fd9ed49f0c52f6241ce7223891c3dfd65aaa03e02465146eaf4ce8774582881f29d2f58f056a65b304dec470b90105697de9c39836140e2659590a5f4f4845f8bb7a442faff3eea33bbfceb12c3c45d4cc5163ab04a31f448b45d54e00d0bd9eb3a6f6d47b62ba5137a4606b33fd61679a05bc8ba52c2913509a804fe81a13eacae2cd5ab1949e8bf7c259b8e1ecdf085c61e0a5ddc2b54e9f6a21f536344f65e9837d3ca0e775232caca5ee9f94a6f7218e5e49d9438774301661fb6951860c1c51269dab218839fe61d50a99abfe3ef1127254442d54c39d948886bf7afe64c9ae6731dcbfd69a7fb4ea9f1eab0d17486ac7e6526920f9d881b704af96779000102382552ce03d11cec235dca672a2be96fa7ed126703402deaa17b66c7ed40771586f1a9a6c2408e60b51f2683e3b5b2b0769c0be6054bb2486e4a05d71d6d822faff9c007015780a76d07a4bfd0c440d93f041d021ed5774451f98b17b0f7f77019f9a6f3ce5f5ffc19301c07f2bbdb2eeef8553c076e331f5e2bcccb804e80dd7e9eedb96cd0aad58d1087e2b751c5aa7fd32531dc7fc3d5e4490c1c327094adb2e66ffd51d1c17c2e65c353518d5255ced46f282d8c9c8d3cd930594215f379c453a3905499f964c3dff90439bcf40f4fdd08d42e2fb2a12dbe4d6af074b40498c8cf396a5852468e61016901613cc307b284b7ac8ce7927ee68cdbd17f6ca82dcc33771399b0f922be2af6588d43979534e88d0fd556ab020f3ae218c228f8e8a059d0f99183e29ee41a70767a270e100ca642012c788d8f82c17e2200cc81190add47aa2efdbbf32c7727c15d2e3ed5128b1c3355dd38c5a3a1c8983845ab7ef12162d9f3fa6302b41b6cd331fd17a3e90940340e2cea70f35f7e0588aa0e56dc136a9dfc975b67223044ef39d26f7754c525d65e72d7aee133f77ff356061a05e705f705b42eba535a9caea0988a3ed8a6054d1d6573b39e2c4020e389ca0d57ff51354658c10a28cd85cea9089a5ff2af14327dbbde1ecc2f7ac74863f4c4190a23b4f2eb5352a85a7113d7eefad4c4cac8a3fa28770929c36eabfe71ea6fa60e1e41733f3bf086a51345c07961b0be4b8b79ddf760fa559fb80f3f3371d73f1798e1228ff5a9e5d929cceb408b11439a6f485048dc18a4b913efa2526e48d2aa9f1aacdba5f36bf731fcc1574f894aa59adfa42fdb9126d963305aeb3587dd5a2e0ae9e28ea02c31e937f4d9c2a889a18540880c0cc9d8ebbaba8cedd78ee183230e4ca33db2f1d569de82b335b226615f26d42d8b1082bd8913024dbb95abcca8ffcd6f454db7a51531c7a16540bba3dfd6ef1af3bb2c28fed35bfc9d7dc78f6483c3647dc906f1ded859c8b870277c99d04215696042a7f7511155183244e180815a152c55591784615e487a8ca89f6568c8c77024ae81bc4009bbe4eba6d98777994fe24ffec4c6cd5ca6de4eb37fd136fcc3d9dfd9f998f1fdc01ad085125299c0d263fab8fccc2707807961a103982b15a83658c898689e726f7b034d773fa09186e6f59a8767dc0248cf88f6cd1dfb4d7d56fb57872f2355dfa137a25bb13fdc76dbda451cc74a420e04f37ca245d262a016515ac1d68d6907b7239ffd5dc15e88800b7f8c62bb92feca063a28c9f3b0c303c4dcce63b604b070f810ea050ab861f1cc9e49ef8016967274947e16f28ebcb31ff696d6dfbd82b1dca463c80c9bc081195fc120065399ec15dca699e0e66281e84ebd068ebbe9bef938a1e1f93bb76eb833a5a65282dbaea1feeeada0033e809b85c0f3944bcb2e4bfd0a4c02b719604e8c03e4514c2ef977e43203b982f267612aac5f0d08e5477c9c296e0bef7c68591343eaaa0b17f918ccbc32186aa4ec0ddc6583c3cb243e22d5030b877ccc819b8c60c435543c5a2e773e79de5c7128c48a2b98c84d2c149e1a38f571635d2f201431be99c4c8914a4e94070a8894addde1a400aca0123a70b6d1c179d0eb7c70a5e73fb90a24c1123f169d2994515cd3bc5e9d2b4c4d870dcbf570041f8e315979798aac2b3a129ce2307aa3efb54f19a81d275c39b3e37b1a4cd67bde18c357afafbd9efc69083e205e6a18ec0a21458893e3fd7949d005bfcc56c63e7bb5cab5f9e87a10c33c95ffc31394805bbf841b764e6d24f54e12ca88e050f5203a9c049bd08dc54e2d0c4e29a7c58e0564cce6c8bde94afab70e1bcc262ebbcf9d0341b8c9a59673841c53756be52147b99b23b433454b4e5802091247a1cdb19ad62e780a587a51028fd1cad20610321d9faec269998319d240dd6360716af7e84807b094475e318032699f7784c68f717083bcb342b244881abb299931dd657bcacaa4d1cbdafd206dbe82f2f86ec69db91b5d720f6104e0657b01fb8ac67033d2ebc5b0ad980909a8d37fb163e94e8ab92e9df1b6c72021023aaa2211cd4723ba74cd88894308e7ab31cea97902306b2c9773921055993bb35fb40b51182e24b16f318a1bacba7a1eea80ca08bd0afb4dbf28179ac5726f25f4f2daf462cb8b9554e41e6800b7fda288d0f272422fe53a5394607cde130eefc3b5bc913fbedd8a19288d186055a14f27ce9e2806156eeec0192fec0b1cc4679ebb8413768363161618f68abfcf41c2a0cfb752d2cfd58832e3c4fabd736e45477b5c0ad7e0adfebe8ddd66970963985e2b70273db3345111d26d25c42fa96dd14f25a70ac6d841f0a8661d272bcb2e56842459708a9c8cf356eecded67dc834cb975a7d88bda9fdfcea92c082d8042899a0678ea3400be6f3e1cbb4f56b9343b8c10e8e08084c7aeee6b85edba7c979519c285b84fcb6178fdc447ce6977fdb02f6d74fd7b901b9486683f4a4cb942ebbcc8450aa1e8a3e13e5ba21fb55307867a16e55cd8e9048dfbb2b88295f0248248b7127d7054c9641106f66cd5b57c8119e65fbf21e5610b89c4198d43b7a333d919077e40d2eeff1c114ac4ef5024fa4b2f66f40eab0e25c29c325ca088c09a2ac37020c91ed2567a329c87aa89e2643dc1babc6c6288936e2e8a9d771ff2b32ec4c42f77ef5e56552f6cf42fa0f535afdc6d9bf5f9e988d5c33103d8fe01cfb72b75941f9ad03aa2b1bbe44d99b74e8020e3ede2e5595e9cf71c659150595694cf2302850de7b2dec7a388fac894b47afa3042c37ae889f3b96159694f3a26e118eb2e21d4a51d49a2d0d1acc2c66a9d021108040862c7dcb2703e6cb2fba336719035e6f77e4e6ee2713306e01f9f73d617289652751cbe96b5fda464e550d216b53cd56d6d281144732afeb065a8270eda0b5fc8b4314c7f10437b3da17146179b87281b48314105a4fed5a770b29a2fad0dc7339bfddae207c7d0127f7bda984580b2fbd85fa52a0934cb38e756ac59978d555871b203d2b615943af30d2fee7ad1cec85439fb463f7d77a92912f635443d1df5d5bab30a68903fe59901231ef3ff52e8b8664a0ac54f7cef7e824caa84e894358e1fa279568fdaadc96fda92c3b3a665252896a0775b533b2c8b3ccff1271db6382c5edb779bdf6d65e224090d7c82015481762083374f78d3c28be07d79144a2f2da238f78ce4cbd4b83ddcd2606cda8023a1698d3b67941392429a788288b454c930289a2a3d19478743164b5556164e742b59e3eed66f8349a8e106d3471867a2a09d93639eccdd06c6846e0364c56d900b8eb245b942b70d766c641f0ff419e38bb26982cf426340f5a55bea213fd2b7c01daed804971b4351d8f677f1d8268385e161c776f3ef74f03f80f3fb44ab9dc64b6350644ffe30971e1b72b03b1fae444df8f253f3466cea37537ecafafdeb7dadec3af379c0aef8d6184420599a91155cc2fd122f5ebda7606b920016ca0e577113da5cf15918bcd00d86be13d37d47938edbdcc0d29c03b9485eb1a6f354c11f7b1c59b2bbd7313e330cdaf7f9b580709b7e17bea2d03c01c63d21e3bbb293471a9736e0985697b494b27eda08f1262a10436f5a1d431bc24d74194e31efaaa171adca49fcf184b19cf1b07d73404d4688763ebabbc7ec492716a487ec286a6def090e05c43a4ce20e15604e82def29daef9dda04587e4f11becbf06562d7ecc2cd784c05afdb6ea61d6bd9f3ed4476bcb0e12f7e37f15d92491f57aab6dc1236101219bcfd11100dfd5b4a0cdca418b1c4e820c86f3f0525cc63a6c97309e8cc5fda5dd9d2b87575e841a6a6ece34078561d76944b7ebd322deeff1e42fe382609494c6b04eafb16cbd29e6db3843254b6327af4ff5b6c08cd8b04f78bd31b53395091a6832e90d64547918a6ef4714ef96e47801a33b3735eed60e23da5c077c91d8c526313c2c368080f76520e10d1ea1b7942b81de8fd49fe89a37b36fcbe0fd6a1090e7a911ab87812a8bb843e7132c017d4e024b738701e9b47224d9c907397d1c01a7be9715822a6e1ebb366084885f7b5387a4619bd36fae8bc3f4ec96e8a231cf2b14ca152c30095f70c33c8bc284aa42eee302251f54b6e8af11293b2529f4786deb686a684d3658e5f37547455aa5f47d87a8c5a316e872cbc8fa4f4ddbd0f9a06a551184f7a7c963f29cd602145fdd06f6320db63f3ea35f6ba7ae343a8ad37549bee0372e02af0c7a2b19eba80d895d8292589104c3038cf8d5a41e99fa0fc22e34fe4446fecd81d149191bd985ca3e093ed285ba1853cf2056c1a71074feade89f1328ecf6370a22dd1ee610b3ec4aeb0ee7a0742dc0bc1a90f7160825d6652bd41963896863ee7f01d8e49c1585b1f5751983081505a86571c8d446a4c1774e6dbe27fff10cd64eaf9cc7e7ad8c5c18ba95b555fce64e495666acd04d7e8d1189912c5247c8a80e1a2bf0915301ec79fb8aa696d0221dfd3b391e372a261f8f51535256464164ebe09ad83f5a61979c87d7500a38e03fe0f3aae20c0764f181b5cf87f45ef22fd724561a41d0c5264bb9da2f2b7ec69a3b089055556772509cfe8e6b0555b58199136c71ec8374a016d762c4a4db565f7a6219a96482f08bdde3bf48fd2d24df96de0f2135ec18cc32b4918a190cfaefff1b6410d38a7f5c3ca96a94320a77892c234654ed07046194bb946382052f20fe1f4a409a5eac48d1a29aef92cd9b536e191c7dc1511ac0fbcd65d79e9f724c81043a8c22f3fccf13ad6791d6be36352c6e799ec4d8b2eb696a0bc67b43374a64af642a20ce7a4058e1c2e6aef695d9b6d27b1b6ed3c9a1e77809f901250827be13e3cf37e83e57ba5f02843ca0e6d8846124dd309fd43dde8fd82c01220b75a744972216930fd20ebb11d2b06c4b33d9584bf6b0c2d58c02daf0729f5b024ab8a77b70dd59c7f58e35faa0a8f7aa901450cc0b19e1252b164c0a28c26d4aedf94d7930c8e5b14737f5aa4a11a3040de862a4f20e6263e07ead2ee06dfd40eea0fabdfa9fc32bd7d01739a0673e12ccf7ed29de9af256d7e7f1924db1858cd22c68ab408b402164f48474cb6f39201b4a5e90511a0596e8822710c838f030d13634458ce0a089b8d612a70c47a858d1a2b500ba14f83182f62b50b020c15dfccae5630c315c336c4e5a937d714b60e30fd9a536ec8f6a6e92f4104d7392c5477d698efc5094e1436641198cb02bdf1590516a504685f7e3de73b0cf2f7a385c638b16b7ac413e797d1628de95ff3ec4d667e405597707c4ca69e8a53ba3d6a08827d155a6d7d6d8225332687fe82923784e8eea59466f43db9382f5c24afcf6b07c6bfc0ee2a453441ef3b10ed625d104ac9688f435a45415f14b9287980de72b5cef83978e4929ac23ef6fdd6f088e18db2108b83bd4ede864100ecabe985161774fdd1679d63a70909b7b6f6391a53b53e6b273611d51ff2c0f6923911424a58b37749ebbab72f7d9810ce6c77ff41605b084d617a5d5fd444d9b966ce3d7b1b1ea358aa12c3fce09ee20c528f2081b2b035b7967636c9515b1cf6f3164f4c4169a66c57dff184d03e6cc311fc71b248e9eec1815a181f615a0847bc6a1cde22b5e08ce6a139c4ead581ffd460e0b585aa7ff125055a314056c95b3df62f8e0ee3a6fb46322c1c01f74b94077e55483bbcf4a30b4d0577639d296778a3319b1255ed05f2395a5932a9993253390a92fc14c0b5269759e098f73af9a770b5ea1696f2a12cb3820870958c66e8d37f7a4fc05c73e1238230cd79ecc8b8f10a5a83133faa49aba4e4943025db033cfcbb5768e4153fe4d5e3f9a046ae8baeda6c161ea2d0444c1894ee6667591d8019596df292a04b2b8a7e8180c1fd993ecbe97290e3572b1fe868ec1d80a1235ffb861d229a5bf06d9efad424ab4d76d6616a7fd18b023901cab040f0769294018bcfb7526f0733d644f921ba0672e870f47695892d314fbe14245be1e0b52927f08e22d5490dae16e1aac2a76092881ae5e1610e5f098ab4b6b370d7d1700123ffe7e001bb6f8dddb7c95708f542441473662dc10f7c6bb00f7a314734a43ce4c2970285c87a894d40ef79739ab002d5f3a9b0237f613d30a36125667b86734e269cee11c815a3162d67b32b2c9ffc1d7a406e1f3dea4ed49344a7f48b6a08d15aceeca747ff799670a670ae96afc2fce772e15d46a96272324b1671d8bf91b86258df8c6aeb51e0545edc576de46283872e61c7e2aee9a71d393857065193f33a95accc5a25a5171541b98e64481f63e6c79fa08a0f5cf96f7abaaae7354c4ec115667d4c4a6e8e874ee15399cbf8e621ac79a930b36ee818dea35ab8142a1c8c907abbcd733cb85eaeed8eec152f9acc6ac108ea179a627e665b8aafff828dd2beb63f442b70260d6762b9023ff3f0a25f4145b8fd020cd2739a55603b5324b37d5c169e049d56ab0d2775d1e662bef8c1df5ab698b81df54cb2f2e3f4f8c1e00e388703d240d10d7df6c9db77a480458bff23cb6a299bbf4fdf4d5fbbdfa5eb61e695f7defbd1dffea93cc5f58515ef18b82e909201bc343209b36d7ebd1146fa346bfd7debeab45737ab31f5cd25402574d15e73efa57ef278872b38b41ef6f3354d04e531964d45330721b06da74fed55efcabe6fb7363421086b37c7e40eba6e57636a920ad2f170d10b3ec3f490993942a23f80938932e2f0b790436c28df0c9e4e509e3dc3d1569a76ed6b6e7bdcfcfc89c2c3cc92eabbd65dd462c9445aff7498c19bb62b82a87dbe42f549571639713a08839660bc0b87c2ad965b7c06320c925f0bba3e60186a96a8cdd94ee84a7c21edc36ca3ea646423a427a83b69163518fb0ec8ac1e832661b3eba82f80ce2be5cf9bb98958766f7b99db1c40c219a98adae5132fa2e2c6de9ef7159a33867df4beaafe8b151813e1146c4066d3b47602ca48049252b9799216d5adf2c92619f238b6ea3bedf6fa277806e18f286c6df4250cedb81c7a5c1945d34423cd3929845fa48dcaba4de373cc090eb723892d86b278711f15bd74a45ff868c2676812473e6413a848a6ef671b6589d78d4557d26b30d5172f5235b31b197a04cc98e43913aaba891ea4cdaeceaa679e08328cd47e6bfd0fe6e3523dc77a0f46ea5ce99aafed9a5303e43ce0b8cf7020b7b508bb97a01107aa96752eb8e9827bf6f7760c04dde9d255288f3f1957bd9ddbec10fc01defbe2ee76496011f8892fb63504f2edd70e17449b6ea896a4a9fa20d470c49649d43fcc807877bd57d816d3e6040e2116606b9008f090443b411ef2640e8d80c867df2bdc4089ba31780590304d4a3def7b76bbec1d8d524711326ea18395b302c091273ca7aadfa435b7db386fbac1be45ca7f4561b9412ec12d9d1c2dc1fcfd570568a18f3f0cfa2a6c208c2732d0eab27c4af1681fed66ec8e41158c1bc3b62b3feab9c1612994ad0dbf49ac2df92eee856734756f6f555a7134a7fb384f884996a5aaadbbf818af3687d7c689014db131280933632779ba9beb5c575613a10e01f46e5f83e28142402b57e0f68a340e062f6dbc533ae6ccd5587078e416f9c4d163ad0d3686cb8fd3f345beea5e8bc9cebc95ecd551ad964abfd57697f1848244a289405240171513cb61d01f8f0070f6f4d4f2749aa11b640345f4a75dc54741be1240fdb13da1ae3c4bed9467c1fc48fcf74e1b5b639219ca9cc07e1ad9acc2b8793f9ca87370103018d04b9cf2a07b3bff828fa7783193634b46680d50c4d3887f4297b7443eb04e61bc76629a58a9a90fc9a7a16324ae3cafb7607d324477334a5f7439460dbfd831254fd55e3d7b7721ba46c09c1881a4ef46e06bf5f45ab09524c2cfc0b968c958b9ae8c4213a84578cfc11c9a8c17efbc27fc397ab1290eb5ac78d85b1cb9c15092feca45268c8724237620298d59853a698c233bfe874aaf31d41222cf37c257b175198084016109040d9744d97075d8b315548546ca2b103dd71554d95aac31c958c834fe27e210fb7a8c8cdd814b28b6390a55dd2ce64906efe41531586194daa1e38fcf8d58085efc8ba65645c9847cfd8616ca7c71afc2a9dcb02cf75c5176afca8da34478b7b43530c35d81567ea476fafdafbaf0f745a6047e4c03d028b24da1bc2635ff6d77c6c44c3024b7ede834761acda1ee1bfb017978856f4cc42349ebb0cb491672610cd26e177bab4ffad966ed44bcedce25979dd3e56df20eac31f3f10c4c796631a45f5871f5699fd0156f4eb4d20f2fd539fb24ff286534fe4f5de70c6596a2059dc8d5569110310f93ca8ad3944d6684a4e57fec1285667a580744472a03dc0d1fa76d2b858853e8c864920eae6ce48072914dbde088a3a60115c114ac6cdd9c9c9d9d337b1ea68f0b15b8054fdd21ec345943990a95a5535a9485035001634f01ccd1eabd9fbc564cab4292c372ed2314961ace783f65462e490bf28395d80090b8993db73a0f88e6f21aa5fecda02706b208209460f0219911ef847e2c800341af62684782fe5402d5aee078644f8e41e866e6b45abdbdd52df34ef903a98008bdccca8c8fcfc79bc4cfcb45aa15e74d220e8fc6e512493be095b67335c707d5cdcfd84f88f0499fd0aff8c3799b2e1b9b3f30c259411143b2112b4d2c59d08ba9c9e9e9f3ab7ccc09a22f7111e872b035a7399e66d6550906b73ea7924bdc0b1f86b37f5eaaefa10594014f17eaf29a1fb686aaf2173b4760d05f3a69a47306398ff6b30678eb949024a91e14ccf734cfbfb5a141c220e85640949d8deec737e7f5ef58a57bc6bf44f0db5cd75d3b852b1d963e25b0b732cc6df1ae387631cb722964531c743b4d84229e67a7133a9539c947748ca19be2b40a86e9f454ef53183ae61bedafd42b160fbf0faeb36979bc4f5b9118c8bec1da8b03061144a6e089cfe747012ff36c1b118c235d6660bd3b14d2d8c0317c1c496d34b69bc06f568b6bc4f3c7d620074749d67a6c54c4731b95c4b04bcfcd64499616f6abd751b38c41b2b8ecaa88f0bec6ac5499034dcffb46a61552fef3e2674001a67d12f3ba5e7fc61979d8749fa61787cf571b210da0bb653ecb4582a68d2dfbbb29713c7a55d3a0ff7b0966e7c23b7d2cf45de9d09ba8e414be38c840c462284aac69eb3093d6758734b6761821d7631406100c3c38785746fc7931964004329836987756d4931fee6ac90613d1cf39255c9a24ad75955fd164f7c08b28417c49f5c8340a33410c1af19fd622de1523b8842b0d9e3b7fbc11629d8e9c832d51697b6cf878d0decc78f8e2927a644b384e5520363fce660fac92dfe9d53ad7646a4573b9d5d20d3d3cd7dfd7ee7f6a830bf8f0fec94fed110521ed2d15c612a1e1c3ef40c078709c7c3c88dd1eb8a6569f6b136aaf5db7c54746d3b74e4766e10887463343261bb38444ec814edbf9d0e6cda9f73cf438e54843ce17f9030ab3483ffb2ca500a1fc78f4d67430a52f6f06ccd0725f7ca97c79140aa2682440f5bf8635ead482665ea2cf0805048d5d528a8f0da3309c993f1431090b6f0cf29bc54ea73e1143eaa44b06d9b42f479a4ea3da168a2d0e0dd2daab03219aebbd28053c6e140bc54eaca268e583b7ae53e8b0231748ab960d915307e228a3891ab49086eb2c177fe7150cf2c803d72e1190df013cc4f40cb8c4dfed0b10a8f8d067df10a7c5da4b1099f717725884ff79ec686a9845a9fbea3452a53c47a5a55f498f6b154430668a74a57f193d6c1bbbed4451c6bf93e242428817b8ec8afb654b94119de49abfac885e4b40e38e53af27e1361e2f3c407038f9fd100bac873dd33a7e1bbb7f1e6e13afbcc51edaa42781fc028e26998b9a8bfefe5cab4c884429d67027ea1072292f343cb139d8b65830c77002a3ea8961daf350f5de09cce82774c496ae1952f46157007ca691403078ace5516845df6b7da3033782f2190f56dc7201578ae0c55e1c5b8103792d2bb8ab1176e1983ee0247d85c846d47c0e0d750752629af65b72932f0b2b46f807b4bcd7b6b9f688e05678b4ca44225d3bba4a98e8b42bc2a814a4bf46be62fa397db3fe844a90af0a75e643d68606d82591bd246b301118fc003d26fa55b162ada0b5a15292d71d81f8697352dd534ec6fea340378c039722b9c6006077b86b403b6b3a3c599a692e9eb021e1d9f7850ac7605d8efe21a1961389d1c1d16a2ec9831ea5e19061e706c215736fb8ac931b11a65bf26a9acba0db26d02386bee3ea0c7d15833370561ead026cb1c6929767f2284c6091b002434561542177bdb48be1c676f066af4c7aee638ef9626fa256bd667d6caba5f69c5c7424d47ce8fbd4ef8a5b851dc5a0902a05df43aa09d71d6b46790e1cfa7594dc5742f1503769c672341d965cc83d084f9a39094cf01da387a9d2db2dc183dfb70d4a55212e68c9b4801a86c3349380a04515186e11658799a5561aca36c01e4fa00b8dbd47e77ce47d03f3938e2c088228ab666e9357229f925ebfad97b94ce4f162107b3ac08fd7f4f615175f936ad08fe8f93b44d640f024a1c589dabdf8bebbefe46c0e3193d9150e1f3cf3a2fd7a06cacbbdfbff910cecdb891065d34eaaa715f45977461bd51c53f84770787954cee5e8b9fd7b2f632c8a75220407b75ec71f3691725eaa48bcac654a28a3213ea4b992f47a6529f8d98eaa1e9116b3fa858605f8c8b8cb970c37588b905d444c6c3820568d499c2606ff4847de1f6055cc3ca91fccb0a73c3dccf1ffb4a8161eea70fdd0a02c1d84f1ffb6a2dbcfeb8c9076ed154ecba79158a0744195e42e8f80a9dced986baecdb5c7439d8865f8408a57dbdf4decbe47985384e89c200977ea3473dbcdd83600fc83de97faec4ea67cd8aff80cfb2c86dd081e2d57d0105514c1f70d0eb0d7ecedc2b5d4b00c305ac0c066a999c9d26e2d8667e9fbb8a54e47971618abb58f7b32e26d87b2cf89b8b7399c88501a1d32a1c364ec7134fba0dcc99e507f51f27b66ee710fa9ddd4198ace80da929fd7469b9ae951cd9594baf1899f23abd8ab8edba1b2a9d4c3262516a3670ca2afc62e8989df86100f79416ebc44000133ef7f1a44ab07dc8c17b09cb243235c180cee30fb124b11b1d17762d1a59fbf7c860feb3575f2829db3aebd90be16cc5f4012a2d75017270d33e8024a4d6d36207b7304d3b3835f29ee497ffb5c994692ab9fc22116e987ea704291e30493d3c662b506cab3cd6787c86d19fca2bc02d1a14565f85641f4d9404e343e35ee416749e90c18b69a689a82528fcd45598bed1ac56edf9efdfda8b36d0e50b54e3070363ac16b770f70647f343944caef8b4de234ad86b1631162aab1f2ef54418ae2ac3314618993a2937538e5c538efca61c29a71c69a71c594f39d24f39f2a0a2f1f05252fc39c198c2e11fea5cc04cee2c511349d71167c5c243ee17f85ef870cc15c5f2abcda103958ecaa59deb549d3c26d168908b354081603a519843a595e6ceab1bb73286340f860f11be55b7457b965b1c56d2b24337d7b2bcdcb6b3d212097d336c14584776fb8a1b7d2c664e8daf78d3fa488fabe79e1a1fdfdf30f03629bb580c498b5bd1f1c7664a9e603527912fb8443fc433b85d0c2ecea1fc81c356fd6dc72e7bf67bfa4f87c4582b3787bf6ae6ea85d7b57a44a0b12dbf3674b6c2d7f2e7f14e7f24a83f4db1e60be9a74e9c0215369a31c36d5292c06950960421794914a1f6e5f505731b177193b3da1866469f4f874b80b19c99ba8d1f4c748a877138c75fe24316bb4e37b756c5a04003d50bb9b6ea8eb7e2f02fcaddf8a555288c5abf79117954c6808009ad84b5fe4a79ec0b220e97610b8341a5a937380ff375a4b225a7311ef53587a8e95b2ec821c4b0c1f04dfda6ef82002ba0bc15a0ca9ba1cff170ba44258169834a231db0a4e4f2f1c0d604882a7992437b01034bf5ac456db10146ae0568d9437ead7067ad06f7c40e0427324cf1ef40f20fd3dcf8ce217c616ee26eb0fa5a6e7f6a201f941b3933841ec94d9c2bd91fe466bf250d36777c382d10d452fe655150d9e4409d1538007c7abfe7d5c8ecacc36024e4669c82f2b2a29049b6f42e18ac3904e78392b390d34e94688692ac605162a57cb3f0faa1725ece13709fbbe0d1b301efa3f0e15ce638f2268b7847b255a4bc04024c4075a090bd0dff854268b6d694a5720335c451db955552457562ab51abe9ffd17bec9c2bbcce5a805a63cf60f19502634ff715484f2e89a299a0ccff42746251540e4713219b6816a01661a0174cd39425cc388acb083b7330e5053951591d37a56d844c2ba68f2d4a3ceebcb6404c9898b59d60c751f9802af8e6955253e832db07e19ec6dd3554bf95b398c3bb86cab24d971554da48b6a02cdf56a74ae365d238c9a828601287f2ec5be6f6edf5619277c6d7e9d62c954715851eb895d972d1d67a116a822a0b0d12f2f32e07ceb24546f92a223542af85a0f550b5054bc53e85e694d1c17aa949b38270b369a49983a5544152846bf55a6974b9b2d46af16ec600b2e99f7bb3caebccab012f66a45740c645f92be745fd57c7e769e1ec2166c9071ff3ebe6404b75928f48be91c2f105dbe5d42acbcc321d2ada6d9c9ebcfbe3aae659d3eee524241fd275b52b316ab2562254c2b5d27aad9cc11c2a0f9bb5f45707e9253077b42319cc278752850c965c7f67d7bf2fb462d6e720d9ef0612c65be9805ef5fe2a2acf5f27cb3ad4f51c16cde2a1f8a5f1f47636b09e03a4a280b2f5a3c3d9b5625e7a03060c4bf43d874a66cf1e3149de230ee6e46334b37af565ad2a340e5889e64132071a7fe3188d22a0c4df2433fffc20c0ce769f7684de3159694d075a4a399ab34de86f3ac897878f34fbe0124c01e6d0d37066b6082ed6478f73165933eb2ee347a7d867c8ef66ddcf6866067ec76eef77131c799d66af66cfcf6d343f24e33ad57400967fafb0bddb318f02df69ea282ba9f7f04cec6ff9fd65ebfb6ca580bd9edefbea81d00c5701bdf5e2339b98384c65252a5d9728bb9da9e463cd48360645c6255b5d00bdd6e22665ca36b7ce9c798b4009f14fd733a5830162a2e6bda1920f984eda10e6a7c831f93264654da2fda189ac956073ce3cbd6927339989831a44721c32f23145593a997382b34ddd34e6480b49a4390d8faaa03f7598a27cb70242d35f23700cf9f64cd21e1073c1c6749162ade1e95614ee79ff57b1f9c20b6471f21927165973bb882a89515067ab5b6b14527407adffdc2e657cace7acda646a90cea8b3b29118586675910c0aa7af9419c4d7027ddfce5c9fc3bc220510d5b2dbba7cebbcf672239a17244794f8055cd6b732b15a00f461bcccf907bc5497323ad8e5af0ee3530c75aebd90e13923dbe91783e8829cf61490e8492f60f4ff020ca4ca821ebcc5e6df7fe6f3641226044db7789a49c0056555771a0f1d547f7e3a235424ad673a01d102dca0efdc56877b16375201747dcf16e8f77f50fdf6c73458d57e4193d021317fd0048c3b483819ba0069072942c5e76bfad00c5d83923a41353e7413a4acf52223536b7fc0bd6effb130754c351c2ccf29d69bc29a3efe82f5a07c1e25d4fd82a464b69de83be4c9eac8971c6ab6c90c1ef2018cd8bf24a298caa646fc930a7a3559e5107d49f40b037a2f6a725f505655510788c0cc32457c03f30ef9a192dbeee0fd00cf2f7bea7dc8ed4e10b86facbc5efb13adb708302914c467cf6d42391840c86456e81e4f346d13c6aa0881f5498061ad4a0eb43d1f91ad88cf6b6e19a0ed5ab2e5de7b4b29a5945206c1066f07d90629a440e102ea191a4e6f3b3d200eca3363b381193c42fe683d3384300cf1877885abb3340b150b8baa25d3321ab5b4b4b4b4ac624eb0a081a9e1d435436bd5f0e90ab712c2306412124d2ab21f8c599cd4ccac70759609499ab464ca9595b22ccbd2444c0a86261cbdf7de7befbdf7deb3e0342704e2a0339168340a9934648b40151d2aef7d9f87ad569172db5981422551cb164bc5a8473be2711462827167e919cebf7e4b66348ea3d168343ac5a0c0d0ccbcec2384889860cc8285cc07027ddff77da698119893991a9789090b9affffff1fc16c88b078cd82a46666858b5552a244494bc4f34444444444483125163430a5514d34b18934d3a426fbc1781cd164c6919ccee73cf14263b1196d9684153b373dd3c2f940a0effbbecf746364860109f0946638b5298138288cc23e2d912d2222222242daa19443136516a08006ecbc60021ff0c2630c40f3145a342fdc0517347cba664242be04ac19f530fa150eb24485cb63152e5f02d6a4f8cf44cfac7075d6a725030a43100804028562443ff34203b36fb8e58dbc9137f246dec81b79a45a4be3d96c87239428e9992ee37d9fe7799eb763c02fcd5ae3480e11985547b4931625fdc19e0eeca9600f37efc946827802e208eff3997fd133e33ec2a9e4c3bdcbe314281ebb8cb392284eac5a585226564a520585721af99f9894908c98c412492424148286e8b5347bd9186e8d55ad31e6ba0af505ec5a6beddd45d47d841cd86badb577ffd8b79a71bd95f6150e4037a4d16aa72fbba3db1cefdb36d8b7334c56854d19fc76bc36c833884eb950dce34bfc6f7cbd37bec897a6b5f457857da01f9dfe0bdba0d3cf8e4eff956df8603e738ad0e186c3ce0d3a36d82c8d1a05dd7a10daa9e25ed435cdb23e33cc923e8d9a0db61b7470d8b13adc3afde414d129fdfad944c03a7dabca5fc11a4e67fba15ec237a7ccdd41692b6675822c5ffc3eb5b6c390a9dda133e672ce39e79c319773ce39e78c65a073ce39632ee79c319773ce98d31db059671a7bf3845c4e2fadd5be76c716bf0db90f78c0031ae080a7ff4285b90358f588378ed33c5c0fb7c32ac385a96048bf8f7b2a09a447786b8555986a5365983055045c892872b50b09c32e566f6c3cb006b43d0e04b9ed550413e6e6ab61c179b7ad74ef3dbbcf773be7d1d8ee66ca4381f45c0edf8b6d487f1bb5e9d96c96aa64a5345629e80271453f73df5951efb0c2d87092a10e617bf3076a0c7ea2504486e3ace4078e2cc0a7171cbda75ece23306770c467e40071d7fd4bf4bafddd83dd783fda69ec97bdcf5eceaf3fe7a7a9f7d2b860a183b47539ec9a82106c2f581372fbf5d329f379f0f5739605fab0fdbcd77fbbe7fe7ec737d0bdde710bf46f73cc71b7837807bee0fafe0e8441fbe7f5a7036bbcae77fc027d73db41dc035f70ddab1fddb37f0f7e9f2ef7db71a3c5302e676b056ed7335a5fae8315eb7cf3cab8985e40f73939fb18dbba0364adb534501bf6edde3a7339e79c73e6d4c5716d2d76519a838b2ffe59de5a0f80efbdf7ea7b35be19df7bafbe57df8cefbd57dfab6fc6f7deabefd5dc7bd6c3e00beeafc638c776c2760215b1c977529e8b82f055ffbe40efc4385f8c330dcfe5ccf589625d40a9c5737a60d348eccf4ec242a7bdbaf8cc94b2e0d64ebbb51df8542a586badbdf6627badc5af3bcef85e7c3f097c521b729cb574ceb9adbdb6de12c8b80fe5810bc9157783b643abc68a0692cef824442021111c6d01ad0fa794524a29a5d3d5008e0b4351fc27c9958ce3c250467728a5edd068e6442204dfce4e0c74f0020dfba1d55aad48f2bfc2e8672d0c0683619a43b7048059580bbaa53b3b27fad5a10874db55cc4dd5e530305a990e683a7f62e77d15c0f8da1a84f83fdd594d53f5575d495e8cdb02b8f7d10699385fd5bdf7662fdf979b33c6f7ee7f85f7de9b31bef7e68cf1fb60e28aadcd16dfba29ce37e36b6f88715f6250ce56cbf11c97bbd65aee6770f7bb6aa7cbe28b317773a637f6d697ea1984d6de6a036a436d30a59ff35b3b27cfd3e69c337f71d4a6c2e4a7f9290d93e23c905a633d42b09c8d8d8d4d9e23ecfac8f81f675cf76585b6b43963ec753bdbad23cce3e4b87971ce77658500ba20fbd5cff4f3884bb5b0feecb656f045fd5bc94ba90abe4cb1e5ba720bd46badc5385ba05e1dd43def5d91bf420a7736c329a45067530cf3bddca6691a66c61973b09bf17314e7eed64efbe7d5b7b7f33a7b29c7536f29bd5a7757db9d19849bcb2164f8596baddd19d39cca9a1a810429d0ef7717c0186baabdcc71fa075ae7bc95d2db37a7cccd19d75b290c2a8d29ad3d251586629a27002cb699c3586bbb8708a9c6f9666db1d5946b31fbfea93f7398dbf7eb283786bcc22972215663fd24a095561c6b23c2aec5612e9ec2c770157e85a31e85ab7ce72b3c0d57f12527637e26a6c65aad48f2bfb602a922d6c49a58136b624dac8935b1a6420a2a525028a71494934c2ba635b69e9ffc490993129211d38846228338cd62a0a13284ca0f72074ca51ea0c56cdc50c43c86c12cfc8253f8045e6112a3b00ac618ab300b5e69295b294c68ad5b1ac589158952c92e295c5c5cc02c4bc966376eb27dc32c9897d40905e544cb494949494949218d6629683d5492ba5c54312ad9ec26e321a7a3cd662e38a31b998eee3655a29de7fcffffc77f6d22526095c96432994c26d90d47a66aed5b4a3693c978c8e968b399acd562a26363959494aa54cb38a788e796eaf04b4abb8ca858421292300cc3b064c5440a971b32598741ad9668b3650d07077403f1107133994c2693c9749389786eaa528748eb754be45f1d6ca433da19e58c66a56ff190d3d16633d94b299bc96432964dc765a4e3e6fb7cbeeffbbe10d98d146118866118ce643933958b2a140a85422111ce6c07967104475696e58a4be94649e652d2a2f218f5f85b54be04ac21ff3361c1bca4b40ba862f980409fcfe7f371295398c0aa300cc3300c67b29c994a26bb219b9546b3d10e556744f6a9a8fc6fdaa6ed37b10259aac4c9894b2577f8dd63770ad94c36c371b9212b7949e632966edc90a9902815540aca69e47f52c28464c42492444246a25008ead1ebab2493c94ed41a22ecee3ffc213ff86aaedbe0eca050f89f497e3ef633ce4adaaf03b7f5bceebf5a4331c4da4f482f0f30a521b695c692086995000f8d5532890ae321eb6be98e12f6d168586ba3f91706820a4d046da937cb72f178c5e3fef8e78479f91706e2c5bf70edc4cf60189c02bb601446c12958f36018e019e66e80391a5ee1155c62ae865bb08c7c8ce2b1ca63171ebb1ec378dcc26317744c134155887cd096fa34e3abd6d73c9625ba59184cd6a805736659a3514e256f3061463a265967c6176b0533be5624f92f8aa2db88ab916e3e14afc11aed229f036b60f002bc8b1be057b80adf0227c0aff80adfc253f897183154e070f1df0a2aa410c300052080a7c2fb46463a17b77b1e5863310a5e674bcaabf03bc99477f17096290f838bb34cf916f8cf1329dc2c5338033ef0c577d47f796f5000756b1004b9cfe7d3814020cf166aab43b0c6dafa097e8a4d55f899c46ed096588a490b4c707274a0f8176bf522df847413ca99e1dcb89199c44adc38796b123bf994d1e4868a4e8df62de05cc79833fea3f827c7d7eaff84e2a392a2aed2d383300c1db16fec6a4592ffa21886372c784e0047ae1ce4c3e033facab7c067e428352f7f83352b3a3a3e2ab9b3e2e2036b4a22f6ad923afa4bfd10b0860422c2c31302d21e119170bf70120817221f3c3191530d5ba8329e624d08c54a374060f000bcab050e805f71996fe1adb71cc70006f02f382b56830c2763244c444444444444a49462438cdd04809f6400e0271c53c9328d942adfc81432220283531c998ce6cc66b446a391e80dcdc4a42496f0613ad51801639625dae4d096fa5c0a987f8928150254970be32946ead8d2664b931c98f125a6185fa1cbf8e250634eafbf32410e382829285448cb82eea40e13486abc7663cb711c4df40a17e3e9742a712a2939d930f88c1c281e04ef72f1acd58a24ff4591442151524ac01a3b16a10344cd4642f23653d55343acc585c2050ac802ee2929604d4affefb79e91433f08deb542b4c1c9012505c50b69c51a892ce7c5aa146d50c6176bb54231bec897f1f5a46863473c8a351299b5346da3bec85262281458f3f2a813ff71175ebbf0bd8b35176e643825f982cf798234a1e19c64b6b4f1c24f356a2323cf02ee27c6536c091c878b7fe1a41095dcb1a54dc7c58b7c984c3d53555da8ac463266c48a93b1a9824d5515dd463a444444444444444aff1ad9a86415ff45ba39c56c7923c3214bcd656000466900a90dca56e1155801259062c28409f0013718fd1423dd9898a4c01a93142b9a566b00200e172912e32753600dea3f95ca07556182435beaa7781f2209449b5875f917eb453e8a1f9d93354bbee0648e2d6938a0afa04f6182a37253d9114920da84358462a1ef5dc6170b35be5664caf87a14638847d1bf4c52668c622b150b0b58a3f21fb945b7518b0b460bb3241fff093ea7aa1ce7e9a6929554e1a7184b8d22be161b18bce65d9c853fc14d7c0b9cc5975cf52bcef22f788b6fe1a977c1451bed7a30d675b12ed6eb8b6e9645226bc164999800c7b24c6226383a268c490e32d14d26bac9443799e82613dd64a29b4c74a389b55ebf169cac91362a1299678298490e26382cd66a4592ff1e898c444622ebb57efe25d66237b29551a5c79ae074aa4a72858bcaf105036957e81eda58d62916b389d9c45a6e785c2d2b2b132762a9fe2afc14d37d3cd54001d69c6aec22622e25a952a94a241155c848a5e23e05859232ce5a8372e22f949bca0e151f5482206f789134b1bf4837e40ed9a3d71f39c58238cd5264640c34504eb1141b9521547ec47a7d707f5e27daf48a69c5b01018662a9de10456cf72085ff8a9d5608da5396b8d57c018e399c7358f5d7805ee988555c029e014180576c118e31696c13806cb708f9f4e19152fcfe2f1879645f3f8c50a03f378b45a6b167a051502346c9b2a2136109dfe09d6a3d3af40e8f404db967796735df39bc2b65c77fda95001d6986ca858c1a49fe3384cef99fe3371d37f7d57b0c54ad7ff7ddc142bd1bf9efbedd65a131ffd1f37c54c36b849d64bbac74b5b59ab2437963c6608cb5a3755d38dabf588c3f424dd14b32c7c338d3cb2fe32e1d01739d6b125fd6f26fc42226f14429633d756d08a09aa626929f994bcde1213a91df0c117032d75f0d9ec203bc8e7a3d55e9cb7de3b48d8ed3ad210518c21a436210debb3e0d58e3b50fd09aa7809dfbfe65b3a6725ebdff8f2a74e52a5fc508a47f03dd5b8d79fb6d65a73af418204096267fa3c715086ca42b55f6ba5f82bc6fd5e6a7bf0812108e7cf0743404310d2a7dccde1034110d2a7f4e778efddd5da5a2b4a7fd57fd97e6b152f7fd5bfff555cfb0b64ff00080be9cfc9a7b55fed585f619fff8172c545f826caeb2f146254982b4f9770ee48810d71ee48418d92100864eeb269b9b0cdca85ff33b77dda90ae6bf32efc156401db882efca10bdb702e3c470bd83ebb0e43a5cf114706ae0d12a4ce20415cf81130e3c2e3b4e5bc30d86a85f08d22cf3e48c3d62b284348ebc35029fdc1ac24ad0098e993d2fedd1c8420fdffe9ca65e488b1bfaf1b637cf47f7e3ff8251ef76d2d3d29e44bff3272c410797d3f06fd4bbf1b6388e2388051468e18a4a7a38c952df593be3492461934a3fe903aca78118f7f3fe29f07f91e65e4e85e46c87bdf8d3146ff1965d0982df58f9e3e1d43461912b0df37cac811e3fb3acaa0b018fd3acad85fc706d852bf0cf0a72db90fad44b6b913fe7451fd251f84d305fae9a24f63418fe32f4ec4e9cc963ae43272c4f01e34caa02e6fa4325bead7c17bfa4b3cc2910332721460baf0e79f2e6fe400fee97ad159d75ffe0f5017f63408065bb311421aad2b702d4944c97ceb6e75fc96bebdafb5163bd0f87a510c3bfc963eefdef64afc1fdedf2781ae43ae02b76b1086be32384af37f5ad3cc0843a10741e812eb4b1e4bae385dba71729a850b6de9e5381d6c1a846dadb5d65a4b477ba3ad8ecb79b9bbba773b6fdbbb9d77b7f3a63d6fefbb9cb9cd719abb7b7fbe7777dfde5f9773a8e451425eb8317c2fbe38df7b2dcefa662874f82fb534bca1d9e27bcbb41ac7d58efc32015ee77d9fe7d920fb888bf1be1befbbf1befbee6b31de590375be5c5c33c6b8ea9c4d2f749ccef8de8c2fbeb6e6ce6690879fc7de7bd6492b0d30c618638c31c618638c31e6a54eea22b7bc18638c31c618638c31c618637c73900dc3dcaa06049aad8869ab301f1026cc878449d6d7b1ac4c82f9dd4ca0636fdf2dfc6ed8eeef96bf22268bfb8ec09f3b0eafbc9d9eb0067a31a614679cb1dd3da1a55f35fe4173d9624ee71ba555365b6e036849b74f53adbda417827a3721b49abb18e7dc81c290f6be0d7e6e0370094d25f3db3da2e84fbfd59d5de56c3139a6d0f2b4f76d20a1fdb615896c11658569b13cdd96a5380263ef7e775ff0deeeafb5e0febcd5f83121ccb3efe28d6de0bdc35014efe5be7a16282ffe9c31bdfb0939f2bd6fff52bb9f80f5cdd4f2406223b191d848e0fb8493f958dbfc656eea622f5d5217e9a08e27d0af9e25a639e79c73ce39737cba72ce39e79c737e9d01559e2f235a5b8eb322f826ca8b93d98ba170ed0ae37befbdd75a7aefbdf7def195b36da184ba4ad1cadade7bb7e5c927b03fb066645a2c7d05a64595e2275fa6a070b5938971458c8b66050ba64595e227df4b1413313127177ff131bc854fe1299e86bbf892bf3c0b0c182e3866fe7b4901a385172e505cc613ca78926991483337c64583d25b26fec7ff987fd73fcdb7fe4ff8066bf87f1a0441eef3f9742010c8b3c5b868507ac88c4c8b6585445ab1e24d9c682bc6116d449b9169b1f495119291d799aa1aadc4c44447899c12256fa2e44fe36b555232716dd6a6092dc6450382e076d1c468ab1549fe8b621882312e9a32e6f42e3ea395f2347c460e94199494941994d6691c595a3233292e9ab135c6b8685a2e5ef3319c854fe1279e06c55b8ea3458bef2bd66a4592ffa218861f0a18096c625f0d5f0b166a58a0acc613ca786abd2ba6d56a999894504ee38fd68f1529550bc7c9e1e44d8c27eb5a41ea9036962eb6a852bccc01e7e44d8c27bb62054b8c8b066cc9cc8ce368d2d23233e3e9742ae1a24939ad9cc096cccce8e23372989889d5703ac17f3af9b1c5d2574aa5b207a586588c89199a94d67f2f2373f226584e2bc65ea29cbc0996538c8b26e5b472025b32332c2b2b567cab55ae8871d1a09cfa296446a6c54222adb07052084eee90b715910f11914f41998971d194654a4b341a8944229148240a69c5b86850c67144c54e35625aa73f79fca7e7a32af52fd66bf5125d2d96bef2f9cfab4695dbce0702852c3e9e8c3f8ab5af46ebb15206e5348e312e9a1494d3e35f41398d0157e7e6e606bbdd605f144271a4a4c49892b25b96a55bad562f69f5962e01cbca1c1786a23879264e0da7868383f36ab91c17863a3b74e7c6b2ec2d14c5ff7a73636f6cdd07556157405b56e0c3c23858125485ad4193d03fc2f15fe26ce9f5b5bd9118a171784a78787878783e65d43828a751e38c1ae7354e89128d63a2717afd4c12cbb01c43cec930920ccbb00ccbb00c2bbb9e733ee7e49c9c9347a68cf8f5318dc942e118098ee1188ee1188efd09ae95302921c1357bc51dd3d7bf375c9d2b13afeccaaeeccaaeeceabc7875aea9dad8d994297d7dabc364a1bebebd59166ad443cc12f52faef22f13f9af506967e5c89125e32b14aa8c261135ce59928c2fde5f7ac78e8cafd234be486d6fbd7e2d914472c848140a6f56f0f5e70d05dd70309dc4e6ba4703d13c44dcb40f986571336a5983aad043cc961aa1a5a1d71fc254555b63d42423c21777b3d3eb8f7025ba4fd779502a067ad012ebb5f208eddd57887066aa39aea36107d67cff79de17d680ffeddde2766eaae8e7bfcff684083f604d28048135a0707b20f75f68db1f427ba70867cd759ee8a3a60fa7fdbb7c73da3f90d3fe7d9f6d7f0827f0d5602748afc07cda270843c4a901cfec479f6f8798f607207a4c185b1bf929fe7cab33edce94097fbeb54d99fdf3881acf0e5b0cec0e0e374c96b5d952061b7dbeb561c25c58254530c91a30d7a692327b3365c09f6f635306f4f3c656df9746af7128ca29db3f70674efb0639edde0677c8e7f7ed708f76e6f6ecdfde96e36ee22a98ca86a77a2a8c6a06eb02150dbd7e4d25914222554bd15239291f523da476a478e8a9184c32c5f301a57a84351e3620eae7e3384a3e242229d15a6bad4bca9ac90cb7e5535fcaa9166dcb2b4873b7878c6c2aecbe13acac89c60c247fca36657aa840e36704c70e89cc83c2c93cbaeb399d4e9bf4ddeb03a9a7881f43e808b1e30354fa388e910f711ce2c8dbda0985dab3ed02512824f54c5512b41c1f6689920105db2ee0a157520c2649e251098541f4e0239a2d6b3c6c40906a22a07126bcd33bda3dda41b46f3ebbe5a08d07eb99aa3ac32cab37c38b618643af9f25fb2bf368982d6bc468b011853290d70aacec29796c59ea94365bd6328859fa304b1e65ad1c628f65ed622fcc3ca0cc038e2f7b831d5f1687fe8d5f08fa80dd170a8135dc87c0163c0863de9429a125e105893933d1dbc1d3c183753d1d8f17c32c6b12480031c2c33962b4334feec93c1948b6a9a489098a2dec7b0758b331b63f84f7317fe1a37d1c07c96f5e829566946220d94835128dd46396f545f2a1937ae8a41d93ac7f84cdb613c48836aad992d6830d081d215e7b67eb6c1bdfb390ef9ca9da3cd42f2f6d65ad8e2358b37f045b5cfd3c085f99a7046c31a26d6f73facba110f401bb102a4481501f14888275df98b2f1844c15dd5da724445412df46233c4701a66b64667fb03391cc7392e10443a14246f6d365ad7463e2a5982d4b36ccb234c3244b34741c50e9068ea443b2916a53450a629624204828128f5e9fe44309a64242e28d20fd200d31ba91f0910ddfd168341aedf89863c9c6147b918738ed1fd5b65ca4658748fdf251ad9223daa8074f76f1eb05ac55b6ed4148967c1c8728ee992debc90d267942abe409564f32bc543e4ce9a46a33cb4a0531cb1ba48038a15598548f930c291e1d5661523e4cb2be0aa682c4a78ce8f5477bf6234c0df199c756d64a1ea58e10e54ee9634433c55033503615e6eab1e4716b49bff2dc31c721fe8853f18af86a5b4ed2bf13dbf2911ee238c417711c218a6db9a95b5eea5f8a96fd61f0030a43222b0a85a00f88411674625f1e212d04e24daa4d191f26eb04b3256cca8cbed2ba11ed1bd1f28836a2a160a7b276827527d80976829d42bc67a01d931582685326837a4046807eecd99ebdf66ccfb60e53c6c33c8430cb3a8185611886212c1c4f60e3096c3c818d27b0f104368e23cdb2ca194a1a6369c358de3096388ca50e631983b1a48d28186a4669c371e30b0741078fb4cb2681fa53ec70f6cb232481781cf5a079bc1826eb04f6f2727af574f070188d2fef06cf866f7c7934bc194e68d9880c24d7cf499cc02c6be4b850fc273fe309ec04763aa1d12c2be4b83014c57f925c8db4d709d66b48b33b7a7d1f6c3d7482d819e2667ff0f01c01eb28d89e5572874a722328c75482ef7e26a03bee315eda2a6959231bd072b4835e7bd0f7f812855345df0b75fa9ab73e8e83e44ddc9b9548defeb0825eab301e0e93ac5d4f07e29db5c900e4c22a4c67c4243b22fab5a930dd0f15957f519ce3ab43a2576bdbe9d9e37a86865532895cf294375eeed8b2f4a1ec616b607b3896b511d042ce64c354c3c9039dc654718251536ccacc76682c6d95ac65ed8234842f53cc1433d530dd743d530689c942c1ac379b32dd574bc3da606fb038e86073f60c05b3ac548f59d6a781b2296bb01b5037650d252b6b289c5ecb1aaa05bd82e188e6dcceefa92ebd619deca3d3ff3821821002e760cd7e0eb6b81dff09d822d4bd31651341f832c5b8d77ce3cffc03a10fa8446c1cca61c848040000200873170000200c080603425116455120ac751400125a806064523e190ac44112c3480821641431061102040444466846ab002837e60dda1c4f0f6140428b4c32f3f59d0fe95740ecd6368754eb5ba5c78d2bd6e4c87389615cda68eefa1f4c58c7aecc3b9ba2ffe1b3637ddf698814ec7b20698c8c5b46cc3b490c0df1631d5534dcf70ce4261cbcc8dcc52ada2e1438b95aeddc6d65f55105a1b94152f2368cb70c38bab96718488f89975247ddec2a5cb8c2fdd71a2113267bff68e10b1bb7e4c3693eaba085064de8d2a3e9925571e46dcb4713b7b641ddc045bda810673d29ccecb4968cd556f877a801d8b308da623e40527ac580ca58393d64c004d7a514eaef1815285fd77d417d1ed58b83c3945a3c5d37e4f774cf27f05d75af1cb5e0bf394e215cc1bebe9f4f41962c1fa4d4a80064a9928271fd64383b70299d955dd55b1d1a9ed166d2650f670ffe8a360ab254b8782834eb2572ecee82a07bd4b65615801340e8ff06d7cee18c18591bbf338711cf132ef1b226de8fc89b087646d54bd293c2e158da363e2e474df5f3cbc6b37586077550371e07f30712bfdb92c584babcf9e4c6a377a09727c9a8fd789cca02abf3794929c1e1a00eed1a16aa729159faadea3187939efeb8daf3cb8053db23025555138308400b0ba9420eeeb10c9abee401581bed73465c089b9054bf9adfc028a931b22e5f8f30aa2227ee6579f77c4ed0a38659eba5b8cf3e8bc93a4a018a0f9fa723b929d2608192294477c2f28888775b429927fce85def28c161062aea7b16673f356b20ff7684571eedfc39e0a3e57d099c786c573e863eb601610f70cdd977560e4c1952ce6f0e549c0d4afa8aa84f8615a27d73015fd8856e93e29b1339a00a18570259ab6d1fdf8b67d950d3eaa308cb674a0cce4b95b2f6206abbebb07e331607c612e0c262ace8c5a5f79c16feeec8167cc6ccef34a6eda8c53f57baf86756fc75f0921a5d9fa6aef957cc2e9c824205baca8d062e2eabefdf9db7f135892d2ec71f82d86477d54d6ef684f8b7a0cf608d860717d5e60759c7267f62c6740bb2d6f3eaf9ac74ddfbdf89069f61569baca5084f8cbb11c3451cfd8bdd2965bddc7887b5507e7299755b2c8401c3ea9e197029fcdf3e04dda30cd3170db304fa2ce1acd5ae0da22f96c50e85c69b41ba66f751bc54143cd0792170c81ae8ece05045ed3c4dd60fa891dad3160b22372ba3e3f420f411daa62449e04d4b3d9e327949ac44005585b06196ba42bd28fdd50e47e56f758ba550fb85494666d02370f96f85de9eb0e1b394c7d44eb04d120a6fcfd23e052e7439ddf04691db1b74255d51a33febd6fe1402213f2962ec2a3c725ed79bdf3692e928b46f657662550307a339d0577c5899367c30d03b4a5049c3857f504bd704f1fc9107430603b942e068fd3507b86ea65a8a266e161c754ffc13f068c7ba9dfb109780debca9bebba223b3abb435dff7f91aae23b2174d784c1c4f8694f31034c613357f59b93e0cf3e006f71a91e34842691109180c5522d45edd82485979b46e4501792871046b1f4d40f3fb9860c438ea4d87ad29b7e81e26e32a403d97756a5d8109f783e4634b52c356037c107194edb3753db2b2d6f8db5859d4edd7b50db135b1010ff24aedb156264b3b4a0e99ab323e710cac3eb86d775da7b04bbeac0c9d22fb12ed4ccf042bd077a6996199d8bb5966f074c4d285f4e06cd70e84fd5330f03053f0e17faaaaaca830ea5ff77734d873aeb8feaae253b249c36a48268a272e4cace218c548517ace6ba5a4bb238a7d99dc526f413f5a14ccb5ba443b4a597f732eb9b81f08c79174a44207c966f5ff3f1003a2e1c82f1313b12045ce8c81545f06ae47d4b007823848fd40f01eac3033e4430ae1e31d99f8c39a9414f1f5ca6f05765246a163fb9801ecaab0e1aaf0141a4a00eb91128ec4c8207610320302c0565200b1bd0c306eb00318cd189d1ea8d6e73f0e60f958dbad82f8579900bb6df1efb4e02f0bf558482b1b49f4a400571d9529f0c5208b6a2c326d5ba9aaa54e4ea4d884712e7e017be1a21adb87674cf2e941bf981e7c178dd43f9b6bf11ff773ba34a0059bd6a322f4405c21fc0b0afa313c778755b906aa6c8febda06d791206347925f283e646f170169f2550eab01c9166d64fb49137a58b51d4b0132bea274d41df77f0e5c877475cba4dca43bb2fdd07e3eb553c6a5018e40b6990b2fd7bd35844e25c9d080ece63cafd6d10d4a14f697de73a6226629cc1c0925d38f93ef7f517c07e4055fe5243902f0419daa5fa55b742e28cb5deb718f83b6ef55cb3cbd52e6defffa18c1a532bf714d8e6082544e2d6e573a789eb0a145d6a070213ec10329d9e7611dd11fd8fb65127f09bb10f8e712f6b46654336b3a47f4872a615d7fc6be3ff74631bf6878e2b71fe6987179acd79dc97018c6c2880af23ea084ef0fc74ef263df3adee2eb2eb79058f492353ee556d8fb3657ff967331b9c95966f01b366839ce4e0970ea2e0b209301b8878058bc0e97571a87f28bc08a2a143bd9d478fd270a7aa0033de820a8fd9736b07a51dea6c1aa1d0aee951e71fa08287e3763bd5faca5ddd1b5bb034b4ee6a44d046b41bd3310c2175202bcad859fad7a9e572fb71e7c5343f1f2d3be1f424b34ae175b164cfb40ccc1822b997fd2986324a31fe15a576b1f1fdf22ff7d1a6fcf6424be3128f56b901e87fc8462d04397da27e308655804f5f8c15c0a5b3f06356c20fb2c5d7527123a8c13840959a379c1ae2d74cdd06920eb6838b797e0337d82b9f43540df14fc403bd500eb4b1ac6c3c9f76f8600741f3efcc40d63a3bed3b8fc8651578f5cbb266f1d57cfb4fd8382f5be85646faea338de37625d0442bf17cfd042c012ec79d31423c552066bc9d7343d717141b6e22ae5eaed1d388ee47fdc833bb8d423f4dad6e19efbc30445a0ff66644e836e5ffa7dcda411d3b5d28271530cb98e4dd04d00dc042c3f1e172d505d4223f6da9759d8404a1005c8108a1df824f3226e930e0bdb99661b50738b65dfc52974ce661dd5aa41f4e875e128c042d0badd66bec4394570c1447e34bb256f93914c0957af54ab43a8035d437b972bfe71803f213857578b4fe85c0277eec23b3cf213ef5daa05f4efdfde9f424d6bea0a2eb719048b44650fb3a2c409969818719162a8ed5c2b5389cec64c083fea9c26eda23f9c47c720d36d1c9c5f2d18918d4246ef9e3f40235ec2161695d1633022738cd655ba1ad82dee1a98a9dcc57d8878d6e233ec5833c00d10d07334e1b41960a9dd28b1be88686b78781d9e086ad9f3c4dcfafd77d6714a9a4c2f1c848316d34f824d6c99bea4dacdeca70783b5641c4da6287f21ae6abcde6b885583a59129fce4daa233b0079bc2b758128be171ca2702c749581cdcc242a6eae632fd6b130a8e25ebec6805a21d1fe276b08ef10cecea78d31d7c47b4a21837596b5e4e096c6d42d378ecc6530ff53c4ffe1a253d6c6667bf5bc07cc60fd5f2c57a158b8004183369930b70d09020ed2bc9d6a3585aadfdb1bc4409fd5f245584cc9d5524660383d0f06c0ebbcc8b33e606af961916af8eae95c9438f7d7103405cde96d9daf42a55b0644de5068de1916a3810806b948d7e18f56fa5e06879532f904536d90909b1d2fef50be9445a59e674fdb97b263094cc1fa93ba43cfd09df2074939d8563114e1cd5a13d2bfe07ca6989f39f45062fae7e1aa49e23f46a46788f54910ad18ee898a9000d516fad8cfa5fd0491ba894ac32cd9a34d0ac4163cdbd7a4440e6d18099e51c0aafbfe4c7b09c3dfa531f59c0183e4e581a624a51a4f06e30da1638705abdc0eb74de7e27bd4dd102c683893e38c537c23726837fe30451f407f3d83fd623591edd9b4f8cbcb11e729f6603fd8fa61d3b756d435e33c439deedbcfcfc0b8e1dcb0d14e94129905f3ba320fabe36a0365ee502eed8a89be18c419427238e586676086524f3895ba922d4ccd1bb46440491d97a0ce10bb7e676f133447d00425e30074c1d2dd9b82ad4ee9f107ac897c0078a840f431a457d80635e5d853d83708b2e5d86b58232e8ee56e3addca8b6be73686599bd36a79d0805f481ac0cb5351922b8f783895d13316f273d77658e2bd71ebf161f79e89a4cda4f423384f38648cbac01ce10d8e496689c8d12f114f5c8c710168e5df7435e42516e8e2ef875f66a12ba8cd8b0c511f889933c9af9f8a290ecd5b3b4990d7089ce7680a528df0eb20649a1c9e414e49106b0ed0be0e29a96888f370ea2b0dd0f3eee7b70ea8418fdd4aa604a4a1770274d9c2d6355469695b51219843b70782812c086dc7a75868198a58be08a56d8c14a363bb20203640de1fe48cb7140f6946a928d50a733ed0fb03d1be1927479382c5aa42bd717f4a8f1300f05a88b69b29666442a9e2475e9a0b4a3a0c9b162f2c65921275f120d1eff963be85ab26325dfc3c07c4c999af7aea944f4636d1686f004711021ec62ac506bf45cefde8df8da6a0166a1bf081558e5e5da34ce9ceab66d75e5da4f0949f96596ad31a8970b35424e282594b2ee08d3479ab19a44e18b367b2783fff32b517131dbfca72a0238f47bfba7cbe7fc148a4a076aa951be720a639e6f85991135eedfe1e4d452e364f06944b0a4f4bed87408bcf426f2b97289f6906dc90129dcc49b68280d85f460af1e4f69eb1737ab1080ca5121266a48fb3e51c3cae8216019b8c7c4ca6c41af579c0fa12543a6e438088c4d84325f47c457e1b0c141b33ce1175ed5440687026d1211ea0c40c03a9fd9165c220af21671c7aac67443ee9a5d9c8effdbed60d1456857e30427354514cea504485151adcdcf4e7a7f953b620a98d85d802d4d8058c7cc046f5cb9059e195107f8097a03e6a74ac013dcbd80705edf1a490b4508bef3996a9f6aacdb8d28d2097a15a5076beb180aa8bbf005b71217d24da7cafc8c1d9ad920a4eb26021ad45a90ae980a68d02b4bd5c620730d27c4e4d751f658ad800565191ba61e8f57ae19b2ffa51d16aa238e4451863481492e3a4ab5e27a8d91a0a702d0893e5eff47811edf34bb7a5f6b767be14091c12921a2c361b462d82c1c82d7e1d4e2cd7b2347b451a853104825d4a59368ce961b3cb16e4dab1f5c450b394dcf3fc707aaeca5ae5904b0e188cda5d817ee02a7b80aabfb66c089bc62686784eb6312d2abb491715037c260d0eb4bc4cd571d84ebcbbfc2bf40aeabc35c44aeab931a275f08355e7d7c81ca07ff55a87ce2bb4954b2eba1c4792fd08fc1cc89d853cfe72f11990c76fb523f3f9ac6bb9fa97d00d59a84525ea7aa5c8479d1947c847c7b15c0176a09327e214ae455b3574fdf6b73b66717cd581822294a34f955a6c7c63b560193808e9ff4bf15329cf503b1825fe6b31061a910e7786ee169217a3dc95ec08f99ad54ce0bc5880bc7412987c4b58fc8bc71f0ae0565692496b46096137fd33678e360406983daca67150ca2b8148dc27c9c9a5734801e7a3b47ea4a05722cff8c5b9fa4b10b56ae10ef9efcb53fdd96140920272d959e728c7cd58e8bb9424ee1c18ed005564449d2d67f35de5a5caa575bc1afd6402c5b3e42c54b2e17d163d0959856a187841e1e1754e2d3311a08b2d7736e55f6a4bacc83c9d12d197e8b9b8f38504e3fe21480f04a45d44c0e606798942bce2605814bcc8dd5e35642b3879e0cc9b02a11488e4744089c4e07e0b743a72b144fa16462dff7116410a5c8af696791cb1e04b581a1eeadab632f371cb3fdc59b4ece8fd5db2ea059e413c7a9fd5f1c223e69f1d163cfc01f8c97d721c4a8b7fb65b4ac8806a52d72b503b46d24deddb61bba15c0aa2e930b119468c8397e40081dfa81b0329aecda7710407239e71d1d73719fa547bb9504d0ae8e9a12bb8fcf302abed08a543f915be7bb6f78da05f433ac54cb626671c7e1e46de49990b31d378ad625e41673884a37e446d91454a05993c40dbd6b826d827c4b00a06b6e74ae4e4d3600cec1e944cc2ee71c6f100eaa54dd3843135d2522a1d8faa7b5c0fe000f121111b4caefa155250dfe03aab8060d191fd1ef89c9fc1ca41992d2ad035c881cbc6848e500c1ceb583e305d4a76b9c001d2ddc50373581a8e8f3a6fb3a54d80e7d6be9077f7d2f83eae69ab80ba2bbea5516b8de84565ae0d50efa6a4b803e85f90a00d41d6eb28e5b0db9496349a368430766a4c181eaa159729ca297521d6e251243910416845931835d0fd2969b2bae06465dcdc3c007485b3a093c950c4a2d36e0c4edc11bbd788f0314f5fa97579ec635ca183b23dee68e715445ae9f43711de0f850015efaae06f58cad72104ceef4cdadf422c34497a211e62897c816ce66cdd95525748ed056d826a6465fa1397683ea542cf752cbbec27f7305f50655e44b11d3525faef8da70e638266aff96e8eb4ecc9261a9566a33b95d50d7a057f6058c28c04dd2d0a9ab00285ad9f151ecb89f8af25f62a934c27d52bfe4833088726873a42de1470457ff46321dedadf87fbe5c9d5066ef01ba298a78a111408bd31e98700b3ab78a14c25ca3c8e147d3e0c373705777046d0999582fe881cfabbefbace168afa830233ed10b9fcd4eec689bd3dd7631f3657cac4811ffa0c255a4308392970766ab481ccb2a03b207b8c4c08acbae28e5114374ad13d2dd6ba6ef68ed4f0c0d6e9e066d9373a3024eb8ec4c69826585bb8a18226d586ed11091d05dc7f11dee07645a0bca85693beddd410347c37b11629d75d5b0032da6ebd840a3da1dd156ee828a2d0d8616884a8ae04c34cd5172d343bfe31291156cfebc91771f2071e9d9fc952854b1d835e6fae22616f3f23e43d22602bf8ea0778190b77edb981490dfde1de434721180ab3c156dc1a307cf113db202f02d827e0dadb5f8e9864475d99e154dd622c48739df65fece4f3ec1735b71c7bb59c66dadd2c95007152ac78afc41a7f020a1d717f6a15137c9c1741c33cc863c74ffc4e1a9f8bde49f0a1bb33bce0544eea9f346ab646ff06fd22b47694a4ae8290c79c5c53408a801250be63213b015213db615b8a0748af5525b4482a7dfc9be67a90b85c22cd491c0a441546a0a1d8589e1ddc1a6e565d36e27b1f110e1241562e1b3be17d154af5e4f86031a3f8d05ca33711aebc9b4814e219a37b6f214a3c907cb6c99d1c90765989f69c33eb76ab80230ec36262bd57460b271a88a7bd42568edf339a6b75e3ed09eae8039552c17e4d42e104a04f793f80fc3154190f154ef8161b754773489a2fc4154f380f71e61ac3b5a81376fbe8447a4b2072e5e42619d8f12a00050da16344c6d680be3e96495b293085570401fe8ccfa18801a0111cc2144085374324947518c47e9e93853d8d9931b1539d6c1b097f689b7f6b0b0f1040a0b2292d3968e10a75d58561e88aa22f73aa7eaedcc3c53134a89238cc941e70ee881af159bcc20d9518cb5d6881c10b3a0f59ce67743ec77a05c234ac85b5550af15e2d264db834d5ef7f68b3065fe120f3909c08bd6fdcf4b0380bc7bc018820afbd064dc7294bb6e3053b52883179d3ab85aaa03775b1919d3c78d1f596b01d1a1c9628cca7ef33d8b03d8949e24ef62ad0e4780a962413f204f0ef2db85751530e15ef9291a363390a60af8e5d6843999946d1e2ae8d36517f5a12244bd9a5b08cb5474698e09de34082a290bd5bfccb92d9c01b44bde830d7928034c7a08facc1a4919bb6da2df8f186aa1b2af53ace2b8b3d21c9132f364c63da742b24b99b63fdf227066915dd9970ab94bf217d24a519d11c35154e4f8f69788055216ecc8942622d2f533259baf6d1240c6a7abc04d371469f4c26c2cf32823c6f07166ebdd0bd336910ecadb67cf7828e131c1a4d898f9a3ed320fb466180c3eb5a3a1f8abd014aae438fdca18e69f5303ad8d29f43d9746bf4472accf7881d622793333ccd78628996c9e5f25007d9933019a79dc6021405582bb01f71c87a68e382efa65a9fa1a6119e2923f179f32b28c89d837b08d5b085d06cecde2dc5a35b2c0ec57d9318f28c558730b5bc82a43ae85eb4ea2bcf42241e57706d6ba636196c145b40b600a73bb6590d657f297e39bf8f1f8d1cfd55c7afcee6db7c2c4b3d3765ab4d3c1b7dd5af27673cd5b37b570cd84602f39aaaded96d0a80d4b89270d6801908537b58ddb6d297b8595205b99446c49bad239666cc5762bcca202ce0a087002f1aededa4db8a3fe55ba74f68fd16ebf463f72f86be066d1d4c8332ccc502413bb77efd21b9ab0634a03ce9569952cee92093bc7f82d9af82c301acb1a7357404fed36e0c96e1668b1768b27cf679b0dd05f344bbb5130ef863861b35bdbeb36a4a4d9ade32a9476438d85f326733cae8c63583c5b318b63ef425268b7f0d38c79f16d43bc3ea7edd630e572d77087a5f0d95cdad9cdeb5d478844ebf98a4a904d6d3f16ab42707c617caee4cb2b3d072331b99d6b85c1581cab1cb358d6e33eb3db30cd7990bfaeedb9d97ecf6e182e8f940c01b246e9174fe4810f2dd7740346b9ec16d03b6737461703cafac326a76f41f305f558af5bcc855776bb007e26059de77fb4367a6437f80b01b4653c658c09d5820674b754805c04d7b783f78892e3df53b85a2f6948d1d782f03ec494d7e2a93ba1685e1cb84338065416297611482b7d0a765665d48c3f3a3d3fedd01734a636ae979f8b1c16a9a1d46f667f8ea898a283d1a9b227fdb2eaf7750744cd9b512ec8ddc620e5fc53b6a26adc6df748084871d65eb28001cc501d51413a85bb7568f110dc51cfbdda286ec91fb7711407f6eea9fef5e6e4940c735a7633141bd0aa5e52b26910eda8165178afb7ec18b527188f73d661af5d6fe1de8a46f4a6581474c87f41aeb7f279b300784bafd82fc528e55a18bd67f1292609d8327f04b93c9b308aefa2f9aeaddefa3e005c9b8d83c489d8b5fb63e700192c7948dbe98dda89b202ff0595e0160983f017bb49540df11e3102f463a29eed083809a51ae2f8582b04ab197033d161c91045545d6d3fad015eb10f3e1a110fc9029906207c4fd7559de01eb2ef23720dc169bf8fc8c15c9a2b73703d586507b546ded2f98d63eb3a9f8ea10be21609f66f37e8bef53de6d82e9963668438e093e93fb7cd69f56e04c3a10ecca2382db656088f7b7108d4d8ba6a0d9d152ab583bb88ec1c4a552ae42c95678faad8baba4e669dcf06e7448257ba43f4dbcd530c36a3a04f42a8a8b3baa6144625042f9789b2610e07a070ec95770b5bde082257558645e93d6595fc2f61e00288cb15467664bcea7aaaef5b895d40fdbf48582c9755d57e2cab5b34702684d812f3e3f7c3a96b0828a674d0d8aa7d27a23b6221bac20f7142d76fb8f4fd50d3465a69a83561e867d9ab33e8d9b468e4164604403b438357aa18793c53e2f17858398eb2371b5b2f6a95d2c069d64c6765b136bdf8a8d03089138b406f114c98670a3495d476f18a751121e5a80d23cd47b6858501e8bd1ee0cb088ada7077bfb46adc008e9d0cb8e0391da52107511b4288856679ee07c79e194915f718983928830abfdfb5e7658bf9b4c9a131324dd8cbb8161e8175afa78dafeb810ca76c00e0c008325b0f9eb6c063f88b8eb44dfd4c0d7952bfd838e21a1580efb773d8b50db649070481648823d6d8035a6f3919a588467bc4c957f7a9d4111a57c6a5c40de573683ff02187202b7910c75b6b4606a2b91eddf9ea3f9570840ec2382871b379b06a036fe240b01219e2082b7a40cb37eeb3d4513575e152fe9193e183397ce82148258fc4f161fdc870ac973c6e144b39c812209d9f382058e987f8c4a63d83242bd8e5c953d9ed9b26fde06655dd10e3609b4726ccc5f49088b0690b92cbd6048a1f43ab5f895e189ba1feca1bd20ea60d78214f1c966ef97a1a4afbf73966b66098289a1ef6a03baae0a95e4dca419585dc37b3a8386c2e65b48f5d8ca2be22cb095758bee5f962135360ec8457c38f59e850a31e6b98e89c8ffa1d45f5acb09de101322bf29e447a8f72a6ba894c4135f58400920351441200d3d41d108049e4f60abb2160a276ed165504319e09784541ee67ae40ecd9104030c25840c4b0f2fbe09cc407979a969f22704f44601e03f7443667303db9184cc7a913bfb8beb60a0ecee342e8005d5da8b6c39adb8e134b224d31a7d5e0d989198dd3b05c74ad4a05bb2687ae810a59b41e32da927e6159324a4f082790e34065976bf239d03485ac7693f2e0d6538970f359946b85b8869ff4801fa969b116e24c1c1ef58f880079fa50f1d50d54666a0aca5ab7b28925baeb575eb9f6da915fb4e4db44bf3840770d944fdf158d3c7d0925c16c7f64f1645aa9aca67f20dc1b4b4838eb3157b7b308f60d5605991beddf57e89b2fa0e7b28ea93149140e9c47ff37971452e042a7d14068f58c79d4f54e3d6c7057da7b9982151a53b2f73285ab3456d2fb99072b535bd096501727a8d5cefbd0403b463a07749d88e6f0e3561eb87e42e1665b1b9fc9a1a7347ac94a962379d239bedac087bf3b013254a39e5ff9a3454345d46203d4dc69072fc5d2ff2897fe37c524871a8cfdc5526ad546d5a611c1cb64594c90e0fa1edac5d2ff92fa5ec99ebd4dd6a7f6e6aaf6d6f66ad93fce2703bba2b1ce4b574806a9a44ef4f2fe5fa8d6c5759160d44dbdfa0ddda11d4222b832064f1bb5360391f2528b86d91968ca98c9cef525e8735cf20cbc17052287266ea51b62f2112c906994348fa432b83c24139a991e1e31d8a131e65fe781baf83728880322853343fc14df0d2bb4d3f487efbea88579951472bec2d2b0e5965ec198b5072bff91d992605df6f283bb0aeb1aff493cd065958df2c708596c068ccbf88af1d70932469f316394b8d1f8c9418d3b0d31fa9a8d59460f5b256b53ca4b18b88a9f630cc9c2bedc72320b8d156022891bb81d515a9d2259cad13b69fd1e4cbbf9bff2762ffb7109f81e889c660a3bb231d95396e85cb37ff3de1467d95999199d2df00cbcff9f7ebf2a3b629ed3fb1390334887446cc0241369b92e4e08ab8d9695d50fa10331c1a0abcf24e8dc9250d88f480cfa3fadb60d94c4356f35f6cdb66f76da270603fdcd090716eac797ddef6e3be05115adc5b2c7521da62254305b8150526e8baa043b6ee5f774c3a705273cf8fbaf7190501de6a018343aaa0d11046b08012dfd14a8fe7286c33f10333312d0ddee12f0eab38609526f9b06c913d3304c4045a92e3fddecc97b4ad5059d42a1fd7bd892b85a5505771432b07c52ae764707948a89898a927e3cac57d56ac4bb5c13685e82fd33ed9b0d958a82ca576ddc8cb6475735d0c0900672b6ad404f57ab0b3431fb6e83da56cc399302eefbd36382c3810a3631e9c35b35629988527cdd274476d59d57f9df8a3087c08c650ca71ea45d5acdb55bfcfd5c5f2a071333af3ec91f5a0d45736cd2511f2015ea005fcf9a48b60fac62b22b5fbde121a1c24342a7b475caa940df13abff8ca1d358c7c6c940598401e15e29ed11e38b15220f6717cdc45e8f4a08460f6845c52515ef086239c1e6d1c9fc509369fcf06e1122944f3c03fe548f808fecb34235fd40bcfbf0cb2c2d88bd4d26edafaaab630446fdd4ba966312dce1744e9bbebcafc47a7e45be6731d670756bd096bb27d5c26f3a8b0450fd2a44fe53abdb29f81bfb29320a4b676aa5f6046d12faa789a3d4380cf4cce5e6c27b2093ebc69913e81e95b18b21068c69f329a9adbd976863004d34b01522b8c0b5da9565d3c2757d6b87f4a43ae80a5408ac20fcdac39ab07f2e2ea138053c8252df0860bbc11be9f6ebc3a049006e69306c7c893c980c18f53f9db461ae24c48d895fb71311fe538808e87f6f66f25540e4781001c702edef0065af2bb2e304edc70be3edfa37767da839e3dbf553e09ad17334355bb4c796ceeb09dda466425ca8cc4bd5c8dbac081c50a3cd6e77bd8a63c7f9270e8e26f9fc54b8c972673ff92e6471fd48308183d48c1679d6d6a043e4d6574e135d8b84123a02dd6bd7af703148ad7dd07f2da1ba8e8bbb224ffcd7afd702bc962f633750d833dd6e22f8aea356916c998a383016b5133b61dd2515a791a45f93b38115714fe7edd38f23d6518b7f5fe4a1cbb074d0be3f92f012f2003df6968d1b1b4c50f5e878f349b4fdbe55191958d99340c4d871a3f2a03e6ed98171b0ad57d4d5c720553ff7b8707121c707260af6182d992b34f8741754a516f1ce1eb55247ad508fca43abb160c5103601a8ed69c29bd1a865d4eeaa5f53c9061a51a366336b4a11a405e2594c622557f500a838ad25a6256685cda036b0f45fba498b48c5edda55f1bb64a383e8b03ff4a367735e1004674e087fa52e3aec773dde23eb7cd09358cc2a4112a1690bb6fa8639e8a276fd8c45498fe28c474fd76f82c24493b077648d9121e1a89f1c25bbd814d98befa6e17ad986ab4e37a17485496c541912ce7f0d3474d3ac0e26c14d1c5c05263616567d4829038b5abc5fabb90208c5464a0d7998d9d854512cd6b38aa7cc1c5245ee3779191b267b3a679a22654bf93ac893b9381593c1776332847fe3d0f3192b22de203519104c5580b4c4827781eeb4fa8c9d316103849970b08d8985a216da655e8dfa888a60d649231ed5260a918047fbc206a3e470ffbfa4b11f10630489155299789c9b54b137ad33461de49828a29bdeb4ac651dcf8355de8ea57303a1a655c7cc49595675a171f3c72cebd8518b27c203deb4b9b139dc2fd66626cf75ae6680c833cf07cd124c2aa2a943f90aeb17b2d13831db43ed265d79a52abac2f121a544eb616e6e6d6d088d8c4df788b5e86ca6c4720b3712d0a3641fc401476a4d182ff5b663e6d596155ca620e24378811b2d8828efa446bdfa28d1c7a6d19bc576919d9e9e39bd72690f46503ea84dfffb2c114d2885763015c732f503fc61e466fbc679821cfde6687d924628399b2a617d3aa83356f59e8e611e89493e0fe50a8aa63100feac3aa41c47f95cb59d825be662489e47fca0450da8590227cc56932f1a2b0526729289443ca09654bee9c7a2ac1fc96b33e3096787d1a387edc004d44ecb073af674d67c95fbce616c396fea683329f57285822faacc98f6d2fe9efd6350d21efe25f653514e0f5e5050cf0b17a50e7a747b048ef69fb68dead340cdbb323799a81bde227c8c5c2b627f47a881ef6942391ca0a7bd25920cf7266258401efa9710e4e1bc1b31b59f97f1d989e9c7830034bef488163d70fc258c1ec39ebd78cfa0b1cad2277855309351e7e3a54261f905b8a098d7c5f78ee44ce5e3e19212626df2f110556db83b69f1a16b70b7ec10d1831457a5870ac8a770f302c499c4de327eb6859b7f65725c46dce3c161e0622a7d4202d1b5240a14d2800e188e0dc96b247c67ed7cee2502703bfedb3b92a90b6cc9365639b61d399ec6bc6291cec83fb2fe9683074092c438986b7838249780e4b9fde8425635d7aa0a7638f00c19396417b98f2f9bf5f54829e3919356597a5d876a92ec18acf56280206b9d8076d9c2fbf2fad4299e99267854c92a5f375f60f012800c1c18844bc6a6f987ec05eb1f748c72bf004c189b068dc69874654d5e873113dff8e2a4b911cfef6f0c5538070425f8c0ff7834635a2234415b7771c7e7e26003a12a4129a89965fb8f6010590d5f6dfa2cac96113a34a0ba9e64e0aeef7513d2578cfeb188b1b78f3e2ea8c3c31dfa016b674d99cf3ead7c4ef3ba9c90109d51f00041436289a05527301b4e9b3e1a3e5a8b47a458df60b73871e28f04f3fd52cfda10dc2d19931e64ddbdf145a5978c612894f67191f327ee79411961d283476cc235d58f477c622b0e521dadc3fc9d87cd47ddef1d76611d5cc04af7c1e965af08790d38b3ff5877645737a9f3d992e03ac3a5d421dc3285014eb9c178671d9be37b207b93f8fccc6088ec3bee9b62e90f81d743fb0ecc19689fbcec1ace639b72b15d3754eb383bfa36f0c5575b6e4b7196b7b7dd58f74289d97fd256e64f9294544470627d48d1f47ff3dfe257a07e5709db7333052b681cb6e73287ab340edbeb2b5310e14a68b2d409b5c95e7ccdd5223672b889c59d6bd19047d2b6df22466ca86bd137eb9342f5d97c7abfbfdc2e2a70caa4f65a186ce24ace7d297bb0b6f49ebeb769f883f13e1235585891a7626143c14e2f87e7b5e1b56d492a67d719c6e02524088eb2831ab52efaacacd54931dc95a38f4d512a0e2759e7c2cac3286c3148fcb4a0727f6e3f5235ea56636b2c9c857312dce8b3c54558fd092405fffd70a659bd9c48ab10818c26bfb86fad65d4356a9bb50d84ad1017aea22c4e5e117606789525f629142e07307b251d187d13676d95eba226dcd2626e00917253e9cfc2511a9a81192a4082954f9e336defe7d996462d0357980c2df664e2b46fe73b4b3b3565305943e8adb7446e406447d5c9588b2ecfb7167e79f9f2d28ba5b9ef016878752c6e6305c893a4adb71c77f88b99a9e667918b3c1ac30565c06d4fee35830402b404044c77a51d3232cff8c09d6e6d01735d7d6e6219a75f037f44d3069f4b3ded0074c55b1db7cf8bb49b9eae817f25207a34e375d1b83bd4ab46f3d113a83e31e48ddbc38098ae4fdb66a8dc6d8aaa49c26e3034f52925e6280d97c2411c550b3d6b80421a370ab396c9b2481882163e4833d627374915691cb208dc17711eb0945080e8aa1153b64c08191bbcd08900c75d18839f42ed86879787174f5e826aa32d121f32324f9e317cd0db700b4c84821ccadda1ca4e56472114d61842991ee6f8f5b080cbd383865f334c39a6de301a85c385413ac7626d1c8284ffa68561ce825a6428b7509afbee0eeaf7ca10e19e6289ae7076ab98aedbe393a5e46a0701d3a9d23ae306cecf40e155f8b13356fdbfdd28bf849092882c270e1e011ad91bf17253731d24c396a17b48184c386548692ed8a1b2f96e64c3d6d380d80d8f4336bb87d55e050377b49f0b43f5e42ad35e9fe8c7e8b521a5d82be48206f862a0ddf0a35f8bdba1cc3512cce35b8ca0ef1f1cb3ed996dcf9a7560d79ad81bf725dae90846af0d67071a60d715c0803b30ee0c81a709fb5d18fb14ee802f792e18ed6d8fb61659d80528c860a321e808769a00e04d5a14288b6800af5a0273288469f2f57a9c115e8a4bc96fd47e2cded41794d3f3702f00d7f4ba1d5603d437115d3a10fb15f3889d74de9610bbdb991264767c356e25b7016e97a804f11ea9e71851c7c698cdc6f3c7a8c9851bed8010a667e50f109a1112483b0437fe0121f70f6ed790d7418afa3fee0e0f73d7d35ccdffd02d43c67a8f079af3a79d6565ca92d5ecde4ce73d59a3169c0056182676932aee1026f1e90fd580237894fb98001d3e2b5d5d53dfb41f19cf30b7b49d3596e2a16a27f80b26f6fd44f037e1e0cd713167e933a36b6be46131201f123330cc01dc4582511c3810c801c0db5a41d60aceb86aafb4a888e246eff4a9dc20f041a4c2374ba768d1ae2bbbfdb92ffd0b9f4b26e3cea2bf51c0579278f6d49cf10e97e9215fcb3bcc321c31954362192a201d90c3584f05a9828023bf5dbfae159edb35247ecfa87ae1e65dcf32e4f990219671468bd22d31b34d6e4a7d58cc7fbedebaeecdbce1d60549531de44db20c11c3e550bb17129c0db3a6ced644c98e7865d2c78993b1d290a7372f6f0fe8a8fb58f77061ef2e933285bacb178156658e09eca46eec2927536d267ab8386699fc684fc391cf93854312bce969ada8c59a52bebb126122712c2cbbab48d206e37fce9626e0c96df8fddbca53e64259c50756d3cb45777880886d41595414c0c829054e8d5f14ae9e49a56da1fbe2d711a12c07d331edc88248a5a7ea69a48301399211a3aa951cf367a5b16279bdd854739af5e7b8438aa54f15613b42ee1c4e439ecfe3e1709a25cd099f7668b7e5c871a4fbf21311aeb70a29ffc7db862c32080b00a5843b70c856890ed84bec82e221df3899a845d92325044d3fcc7026432a5befd6d3b75613aef9ac265857f16149bd959722cd334d2daffec34abdbc37043b577da22fae68f874455656c791bcb54e701eb5d4ee102fcaebf19dd810e8795de4e7a4ffbb7fe50addd97f4ea09ff1277f67c70212db18f8b1a820f1e38b1922e0a0ba4b426011b6038ad980fba253f1278a4a2a5f8ab9bc21083e9020751506d11d75be34e2dec26bc65296fff814c2612ffe0119389918ddf15e7f380ad1c0d6117dedb680d9aa31fd44dee1c4bfe27c554c8138212a424156dc5ec802cae7ac999086d4018c9677707ec0d43e647d293770d497101580b771ab295418d4a1e68d0ab5802db458e8d00ce4a096599db16a4ba897aa161f9349648aa3f32cb6aa006de0ba36b1cc5cd5b13d82b5f879fef5f48daa900efb6a6a057e42e746285bbd84ccdf5a3ac90939632c1a4da88ebb4784baad4d6e74bad20eb54eed00e758c34d275556810204fe0fe708c3b69725b52cdda6c598f61fa58854e70ee011040d81dca10762456321f5035c2d52bd7bbc086656fc43b5e6f3d9e8f93cbbb0e8add9b677df9d3eb95042287a768d17a5f318cd4ae66d138d0d125d5bf85cd9463d5bf312c64428b62d742612992cda36e6b6eb701192606248488a6f2be06a5b722e5bd73281907400e5b0857f1e2e36c9ba757b6bdb762735e97f7e72f0cdfedb31ef383164059b686cae6d0884dac12e4475d4cf5ed2ecf15192224f4d2adcfd94492b72a7f9934d4ac696f2b34a3c6da38f4ca1eadbb1334c245b1c06f98db4cbc57c2a0ce84474d7af5dc3b5a76d9e8eb3bd574434a596471e907026ce42867936c684c3feb2e09aebd6703ee0d921695b88008b71770e764437159fee9c570178dd38f16e64eade876d8930541a29b253ee2797fc63cc4867ed8eb0d4330daa9c6a159555d5e14ca8844480056d0b03a95e1fe634b6787d4c9a790f3f5a250f8a5b13f5ccdf7c509c095f62049774eaa77b86f87b9d346c1b063606767d6e6c1472b6c526d5b0c1b561b4ca66dbf723f7eb7df10cb7c5d6b80b4001968012ac04da6c1be836967f4cf11ec5488d99866a88aeaef0aa8a5b5634efd083b5f25b1c380e67f8bc4c341f86200279ecadcc88984088109c64ba630f470332d807cd8d09ebebbb26685829566294c081fc0b34bc67503a4985bb3c83003a268f06ab313ce528c3843bb4a439a7e4732149ce9d7201923c9db39ffc835c373796788561435e0b757a9d1afb6d7e381b398855d8581858a5f9d9a96886138e88c3577a7ae46e6bd990ab1cfe58fbb461fd3c78580516566c2c7a76f5ed2093090c6a78926d4f6e54486ffd2c114e2ba1a36b5289d39ad62f2c9df350c52c6f3652fdd806aa5e0c1f1f3cc323133ad33bb7555cf40bbcb7ddb8b1bdb581dbf49053706c1b66cc0c67ef505cdce944c1f4cb25779f690eedef304b53c1763a7a0cc87c45fb4a4448dd4579e4566c19a32740e2e27c234fd34ae91cf8bfa12194c0c20b6348b18d6e11a2d210723252a447e7f4721a54975fced0aca681bac838416b58bae1351d075e63a248c2672cbaa1294d67aedd53b0e809d93bffdbe63a2f54a67399dc6f6c6ed954093ade6df3b3f65c14bd0c7d8e3f293b6f672bc560db305fa63efec5b217db0bb04d82ac360a2e76bd5f9b7a30aea1afeda32f18968ef909b0a96a5ba97e5b5c9e59e10c44865e5b109fb7c75fec26c68e0726f6c35b35bd83a3149b894f06a39bbe58a0bc09d1584ca06ab668069b8f83c04bd436266410eac21209e4933210f05a3d4b484a9546da3718eac27d2959d746df6155bc3613f04ab717e37653a5c7dae8f541b741236d3438df3a63429d60a361b198a0a033ac28198fa525bca31958fbed2fe90080637d02939f9d79723240b5b505bebc170bd8dbfa8a10098991023f07b432cfb3d955c6af3cce409d79d81058c207424e274f24a12d5bfb6ab72d03c1caa6b5255d30dd3ab27c890e1aa16887f1b09eea6808789e35f8013c47026e61cc60db3c633fb43608a7bf6d25bd6c04bbce5c5b5f9285d060b1aafb6f21108cd8d0accdfbb0a6ed43531fda608731be770b2d4c311748495c884a79eadb8d2c02be0f7855a8a77a56b69dc53ec5c4f163961b9aacdc21293f204e400b0b2a6bcba06cb595b03bd3bcd556ed479546ee2d83c1b36bb146565b0986331cd4655746197204d6a6b138f94b2923351de43485596dac33cac8618d73b5856ccd5a8d494ce97f5ebdd874a3c7c6556dbf8c37d10e6ec9b03aad0348666113de16ff0a0a22624f4bdc96a49ca6134689f1894bfcf0b4215d7686278678c8c904598f9ed0d316f56bb3864e02c536de1a75b0ae0d8f3e0608c4ee87765ec0be7f0a86d94648de49185dfc83c7a2c536d413aee30f4c32d6860cc006495b1f7e007c774d22a9d69ed4ee20bd76a019d39659d77884f4b4e4d27f8dc75d7ad8bb13b5c2634875d5c6c7314f1b8d2b0fc25605ff0b9ddd9a11bcca4390b80d0a089a7cc45004ca8861e6e76d252cc33f0a036dfddac84cf7b1b6255040934dbd00247dfbff97958054c630e1c7b0a72cfd1a9cd5ac07df14d83497364dc682bc069ad9340a04c8c5446e2b5d2c7ae2edf588d8dc91582fb943f2080e5639dcae61c242439d5034a55282994b12c110a98d59ad4880a381a9ce0b9f1cb86a9ac46a50fa4acba581ee2e4c0733caca857a7a429fb73184a30c8b2bd58a6c42310ff57961f6a385866bc42bd3ee6b81262f1a4e902d9040b2ce9885d9ba940c8cbd2b4bc61bd247a4654dd501ba609417101a1d9db21d9d8f9260e6b11666fabbf69185ec2df37ec03c9d4b6fe241ccc62718280ace783690c978c2410cba06a5a6c97921261fd95ab00c36fa73f82b251a4f3408f1d09a6034c9b27c7293d108823e4d6a9a016a6cecb47a8ed8b80fc6587d9cfdd869399a5ea0ced26ea2096db0d76da54349496da8ab60a6b307a0f1d0c5a93ca0a02225c98d58201bdfcedc1d07f782534f582a45b02ad5e11ebf21b68fe81db4bc40704fb91ea284c34ff8dd64e35fa5a2488808c52700339a61bc5897e1199def402dd0d6c802f04e7dc04dd90c0000008014a84900065d906229f51a04d110c91d904bc842d1976424dbebce7c528ff62263c1f34e853f47b7febf00eacd0609b1f7e4d7b6e5eb1e2cffe915867c01d79e713328d6a1f8ff47f4260c20dd055bb18043794e4384a03c16e5489348b76d5f90b1142855d02cb4cc3b929a730ddd1fb162c076e109d61b70fb4c407194ddc396273d0492de9037161cc1ae3571bbe0721e5f479c228054690e8a86e2ded9880deb812b04f934374b64215a1c4dbdb73a254eb22e5ec531ca152d454fa2a9d0813d2f3ab985f3660b6f3bb2a4d132ff691cdcf22054aed7a4b97288504317d5a686634ed83a6895d7dcc73ddfaf734d2e3419af1605875aaa489bf71e302e231f959ec5f526a2e759855ec4d93f50de27d820a322a5ce076c0f426f372e491844cab22dc6eb4124da87e3120691011ea2c30266573ef05d0953204d93acbb821ee4dff5814d328d9d75c30d289ad655947fc855febc6c7eeaa5d13f4d82f9042427c328818ac3ed8150201dc17ced7220da055c53628c876ca7cd7469cf8cd5a5a242487437410828d017a64677eae9eee1481c4c7ef3f60ac0cf1dac792d8c0d2b5ceb47ca6e9cbbce3c674795e3e5f62b90271ca87eeeb067399a25ba43abac419002b7e2dc2950b0efc5f56acca3ccfcf8b98b4ffbdbe12f444427f25c54f4167edf126eee69d9dc351482dfb89dec9f88b4f14ba68048f1dd2d969a05d901674139aa6c96d6ae6267a8bbcb3d9c0ec867a90fe04a7017661c4484063dee0c50936c6f320d6dff73808e25f52f9695c3977d002a84ddb13f1a4598473a527703d9fa1f4980ba034c67333e6774eb581f2c2b24e79334226cebab7477f835e533f06116fe09a3ce7ce7cfa33716bc234f1d987d0c43ab854dc093c175c2829f649d90e17942538610c1899ba39888b46ababe77b49645e64731cba2f42446da7d151f6cf6537c987abfa90aeb7a06d3d4f57a7ab6d95d05f908ade27a223e6def2a556a32b563588911df7c0083c57a0efe2192649520f458c095e9612734d05f0bb0059315100165dbd4792c87de3f54bc4a195c2f80ec604f283cdd2d193b84d0231501345f37487ec3e785460a3dfdedc5a384690241eb4151230a98c7031750607b8e55553d3af78dad0f691b020605b63916fc445a5396f7ff551e738a2527e9a45881a0c7e4a3878e26feba8b70fc58f545e61a26a9daa59de5e35bbe1d2e5e5eb02c86a15bc1d82a29465dd0f759766cb6a5032eab0dd6c01e5a65f8a5d8a92f3d34c045b94953a755213e3cb4f58d7052563434dbb4839f70a606007c8085c731c7ccc25a6c7135ac478e0ecd5991d81ad49521e20fe2bd55d54d6be7714313354d4f47b5ca730733f40d8dac81030ae5d0ca289f9404b45843105e53a4fba695185a0916f5d42487c6eeba6be9032d243e850ddd2cbecd102a0e6d25740eb6204f7168c98269b77a1e1673fccb80b65ebba5bd6bf1f586b65b29e825e8d06a0eab2c7df02648b9a11128ac18f47b64ac0c275aef1ac6fba89bc6383b6c46fce81a5d2d46c995202d262092eb2e7b1bc2cc6db475f077db6bbce28d1610048064275d2cde1f65a361c4f664d7a4361b4e7f9d6b346d46ce29134892a11434658da69170afe738a612379f55aa4a94b9f5ad6aa6622570b9ed366ba5e3cc44fed6a3aa6a12c86f912fb68a6b3ca13a563db0833cabaa904ce256f3b2d5b8860785bdae921cf05a454d425c666e4cd414d1c8724333b652722fb1b738c542d45ad1a824b45418c9a91bb0965a2aec16242e38f389118a1e5534b169ef45f4a2018c18e9f604141fb8a7d975b7e43231ce0ae7130aed98bdcb3c85cfaa8e5c2704fa099ec8c46fdd908bace9cb6363d71b1d3069a6e7e3bdf3ae74459a8d778ce224240f9bda829262146df26fd3d709cddc802ec7cce4b8bd87668841b97fea4de6a7e44063ec552b51dcf03186668cde1524c34f0d0a9a3940687621d91c8cb201cd16accd88d8a7e7dbd549409923747768ca1e03eb0f17028b59172e3ade635a93f226b7c68e05fc8007ae714aa539db44f6a9988976dd8da51da226b3f52aa61b7204d1ed35046651bf11dc7244870836439f52251a9e1130df104b6ab4d18e3353b4c3c42e5a9035c3c51cb61207480404590828849a49e4d019fc2d5daac12d541da0ab9a5e4614f64bed3dc49dfdc6bbe86566a573a935906c2393663e9b973d4c6635e66afd8afc9626f0ffbfc2ec791c414990e42633c6c233fcfd25eac743d1c6cfb2c6a5630d394215a0535b7f3ac742c2db5167a1816120896efc6b1a2c71c298672c9b1128684bb0398aee00c811004b985846a42e8d0f456b8ee5488e54ac764cbcc43a8f1be3b5dd10940f90df66f8ef45cd901038a42bfc0029aa70b712ed9d45976689e860ac0118c5ebf07e3d40c60c6743935e7428e5c4abf4e87b4cee23984e415638233c005ea111bd99f717fdfcb7e3f9122ccc2ade5aae093a678a95c91ea21cd636cf72028b8280bf0f0449d26965f068551f892b8233e186ed774491086153e349373e448270377c272d400436c2cdcd14df3d983979e0a758815a99d3fbaf1456082b150a32c66f50c2b2b3c0e98d5601915b7eacfca7cf162339b8150fa01549a7cec882cb79466c73fd21d8824b32dadfa342127c09bf3cc0c46a7401af12a3038fc8561ed04de88ceae712c191d86e074af2b5fecda62e6f59fe7d85fac47421aaca61267efcaa55fb17871d3caf53210f98ab2218ff89fa9aaaff0bac71e31c803881f4678ed466f974f634d4373b455965f1663399163b52591790df42ee8eb8e58b8d4854c4b50bbdf2218854b30c455d10a585efbcf04ec2f55f1d43480cddc01a2a745ca3a93a5532f9c4ca41cf604b96c930ec26f31bb969336b130a7b914e7ed80c711c1189c3764a217b617e88845aed54347b4895247aa0a3257b6aa604f7c958b246769738cd4cd2ae48f9e56363ecbec42f854840151ac904a4ea4e8836e8243e995902d9e407e7f8e1579f91ab932a8b7f6e6560ffe8699129036876d91e1c955b332c6319d3b7d6c8d64971d3d42b8d4a85e170c3dd26ead393d19223000f19ab792d876f791eed22add9f32ddffa5fd1383462b17b01ce9352b512bbbf730ddefe494ca6f96ea49787df0ac9a2a1af0de94a4f6d3f9e2daf450cb6e8270380fa1371282b201ea802c26b66ff11033f18606ad731ff76d1e51cceb97ef6fa702fa30ecc387513448f21144e480b8e4a6299c8ad6fdf48d81ece1e468751844045cb9f0a2c5635462ddf6e06a725f0a2087f3d9771b9c055570a42f0455ad469370c9ee7faebdf82b1e9ac3c97ec74be1dbe7b3b79ecf1506756478ee7d2a2007e493f4818969c0b05057ad617a96240c0a2e206684223fd9dcb6d13ebb25bd1eb69f4330978ae23c106fe95cfee5cbd4fffb00d5aec01704e76f2759970cbe83c1a3cea09e29d078669fa513c97e4fa062670d3a2527c4ade2060308520e084b7e63fcb21a9922780eba005a9c0dc3b1afcaca637d152b9c776d4555ce01280b42c3c4609b6b3e9edd705993f51bd3201753fa62076f5dbddb5a6dd30c362a0987906c030605affb56f2a79bfd6f114d6f1edef1fdd1a9141dd11d5ca07988d19d434a81e47541bd1d7a18ca227365197bbf15623f564975a3d1c51cdf12c85f2e7bbd8b782a43890e66e043da2ba3533f4cd13eec7199ccd50fbd879ef826182ef812a24cb24a9acc46b320e3068a783a1a8577ac5231c1aaba2bd0c43cf31261e7e1c6c73a9e65252ecb5174504be9d0c8d4689f7267c35c034701e4aaa67466018431f0c6fa166354ce001ac738f1466ca02c03cf4e534fd59e0227cad06659fe4819ec1fbd627d23732c710210c1a48d681a9163214882e6a4e93a546b127f36e72b9f1aaddce250977d3df72ec54aa05aee3c1e7cb275f64d5473f3f71ec4d13f10444bb9e8045af21c87036e4c1d41c24636e79f5fad6216e19b5e1e57de7c19ec63d67d5eecc25f31c6617ee31b17dd5c30c8ffaf031b39f78f937f3ae830131a6d19d79b5838555b24d2bdff6b41784d531bc180f7777b08d6628ff0e76fa2f45aa9d8c9ef90651f0921a8b4fc87c1d32871d07a58ec4fb971aa50038d85bb944cae85700fe855aa7510495ef0d124b8525703027558b1193050b552c2c99eebfd59dd8d4bd57a2c1fe4fa975fd6b033c132f83a5e6fa513fa02ac124fd203cc7cb9eaada9e4bfa51b154aef9735f842cabafd4bb5e2d095f41d27b530a902805b004afb562a0ce1a446c8081662ba8dabc3ee9519a04f80851f9db6c4cc65b5e67b8ac22ac78c05199ececdc299d4c5f066d5315b5e47a39517ba61c755f931ae29aa6441d51f0ff4f92138f0bd3e7cf36e041aa02b232d3a5662ba00c4e0557dbc37a842bdcfb5581dac5cf88b0a46dcbbe90c057df1b4d9b8fa6629e2bfaa3308ea9f93cbe4e51d042c12b3648879acaa9f828bd5122a1431b1307a906cc03c3955567c0aebb7681ae30192ad27c0c18c9b12780eff7d3d1482c8239da2d210f3b1830518542d82b2d602896cb897ddfe2320f8f4e4ce0ab9c3685300d9a3d6e2746fa15409e2fea58754db5a38d43f9731290d0bc7793ec2f08805f9cb25da2553ee40030a11cd03130f918ccdc6c92dbc9846f8f21edcfd9ff952ae2bd8afc1c26118c22222a99039e86d8f1fe2f82318ecd4b331e3045facc51cdff65361abed0e74b4032ff97ee851c8fd6b3842ee1f7c7fc5f9cccd0d89d820af600ddff62b57b21966aadf6a1d72c85792a44a6d89901e77f6d135404616bbd0a7e5773a929422707c1c8fcaf5b5a4640d77f7d25eed9f27839204023b6580c8e9d2d4d2196cfb2dec4b30ebcb8442a328a80b3c7cd014db1b3a9babd8c0fcb495a7deda59d60d09fff222c90d67ccc266c39c49ef6fd2b569bf86eda094647587ec2aa667efffd6b58eefcd75a522601ec74165cdc2b225e03d3bfa28ef34d15e33972c4f72ff6c0810474759e1df6b25e500c7f2193c73a477faa1a22da8a32ad8a1d76a5f7e5beddbfaaaa4f0bf14fa48e7d9969d1bf069d2191d476544c9518fc64e66f26b656b9f84026f8969a7816de4f33f053041b223c4d5508a69611299b7de4a6d6508c4928f1a1d269d305fd2f7fc2c71e2aa2c8ff92ec4ab6ec844277534123e1b76d16380a2f5d3ba06b3d2182967ee330168e6423d44c6635ef273bc8096a720b9214292396236046cd89b7eb4279cc0d8b0bf9af8446d12bf743bc6b057eafbf083f7e6cf767f57eeee3d039a60b663eadbb3bc46eac7752fc96cc44390831b72c562ec57f2532c6e18b458c3f1665f98a274f628b06fb6c58636a5589f8af8457922a323508bcbe00ae07faf37c4df9376071cd737d0aa4c07ea9e1d7d7f8ea40bcf61e1fa137b13ee34c87cb4898380844bab9e7a00af86be488ca1edc925346663239385ba2eca0b0cd1d75fa9ef866e11538d8b90771a18b36ab2a9091df3cf86e0a184b1e5be6576a070fbeea165ca109c0534e93f572116dda8ebbfeaa33527f2ce766de0278e90593c88016e086e689a6e407d87a9068b392f2328353e0b9fc9b8407aacabb566e820fd2c35be94cb1e1a36bd72602dc5794cbca440baa24d5d9341a8854a072adec749bd5872986020cb405eee6c4050f53201e59493ae93918fa9363add0d493fdc9693f07283decd055cf96c91658aee5dfec64f2651dc96ab49ecaa89ea3d9fb0ebc4c7bf515a4067ada2beda168e47bf33c1a471b636baded463621841042c8de72ef1d0fe50eb50effecafbb4458363381b9ebd172d8a3c671d8afc78d7b73dc85693737e703777d78fbe0037795da5a6fc11dd6e6de5c8b73f1a5120739697b58d7ac3cec55a665da35efd26e87d76ed69fa65dda3c7fd0d19dfd69b7f4d375694a4d674a37dfc4c88dc531cb72487d984a16cdd9696cfaa763d86d7cddf418657704df63daafcd8ed92caf7a531e1167b748c4d7a975d3e3219dc1569637c777e0065f19c98c766dfce8986df9c86626b077331338fe74ca37a592e9ede20a6c13b1a985adb8c5661b7cbacae623f874169b273e9d85cd48f0e990cea860938cfe4e475997958c0b351fab9cecc964ebc90d3537a4353258f6a462bf4e6de4e180c42a1fd6b56b8798b6a1d26bc76e5c129db0a25a4ba8109ed7ee7c52ad29f0fcd5940dbd2305e726c2d9ad8bd9785d9c768dd21c1d202240c010ce2522adaecfebdae8afbea98c248233e6bba808d31f35220dfad18591c6e8d77548e9e8c627ae1b79ae9b496f467265ffe678d58834ae5f78b4d1c7b4ec7c5daea1664904b194445cde3ea9cce8aa7c681fbd87f6d1e837f8b279e2ebbae42e9bc6ddad1169902e6dfdd656343d84396a8c3031fae8ba59fbe85ca44438c1d3df9450f0cc728a24ea55b422469e199be01965945ec95b44caa15e41e995267bfa9b1f59d9a4bfc634a699dc43aad952cd36568e137aa515b2a86fcce717d8ba2cea55e6dee7386bb9739744fdd5117767aa723793ae2cea6fbeafb4a2bf592f84a3d1682489a41572b6a0093c4dc0a3472a33bab166989bb1bf30055b145bdeb92bb617aa5c129f440173403cbb67cfd1e84e38e3aa78c0195aaa57f3d739edd252fdcdeb22722d1171490e65ab97600d12cd737d1bc71ac019f3540d55d563aa661ae71b158f89e56c0cf1e8b989e839cca64e4d2a364f67b28f629adaa95d94a85ecf720a9e728aac9f24ec231eef937e769fdcf6199d7802cf570bfb4b438db75a492f69a2b6a28bfad3c650b3e7deb9b44d3b85b48647c4f432de54f9a0e95f8778149d884fc49e26fdf9d808457cd2df8c4b20beb426ea6f76d16d2bfa9ba753a8f116ccaaa81676f558718995aa1206fe4c1e176d4308fd83fb59964a91220c7caef003289dd32bf8042856c0dc9a102ac93084f0b266e3c822994acd253295fdce9b5335a6e6532923a213fd451e79234f23a11e1f3bf144ca89aa04c280b8881f2481302096392204a7084d2165fd4528fd75ce24246138bdca7108c33aa77562d194de6927baa795344f37b9641cea558e4f2251af723f8951a21547b2acd6294537df5c26d963d0cf14fd758e4ba2e8d57b9a0638479f254bfa67448363197bc6640cbb856133bebba5ec56220938d4492821a790be42f4271faf8dfe641241b90e017265ccb25a9f44d0905e09919740e04fcc7160d0055c83c690e12cac2067ee00f7616fe064d123c88e2eb8ac6959dd04155278820a1f9eb67c9c8086226b1e015b6e0e453f311b820e2b0ce16707076b843f5ddf37fad1b6644548f5d7d25f116a0f3df1e34477775b96d556cfbeec9652c64f582d6be2e84fc6398388993f48986680dbf37c06a1a66952ce39e79c9093dad4344d7b863dcf6fdccc6076e5cc3672de2ab1705dd725a798b1af2c883c2763b7771dc70d42c6b2643716253622081a8280c4235f0ca257d8951dadb6105b4240dc77c67855337d8b481c84a6f1e0e1670f2cc87a052196cab0cbcab02986fa625d28a33f18e3d71fbc66942efd4dd93d724a999aed53051c212ce5ccd9900938e872cba5bfaf6fc4cc0a555608a18c54a842b690fe2491fea43cd237e4739cf4106a864544e48f28d137e4db2621458d871287065c27b52e93dbfc76fddaa6a581b031ec3dfad835b74fbb6d9a9459a7853ab3cbca719269cfa2a8f0d7f3973b7083bb771cbc75ccd2c05be7a0c5e6e57bcccb9b6d1a6337c3178937d8e20eb9fbf2f517a70c0bb13442d56ed4b931e7eb43488550e7fbf91b43a51f65d21a43f55e332a2ac4a97664714818f15eb4b07b049c11af1b75628e1415e6881377babe885a5a362d74514b93b14fec96d559efe4ac5866bab46f92be765dedbafa18596d963c345cf5baeeb5d27ababd467af41cf75af29e49db655db6ca297e6b953edd5ce9b7365debb1746d1a773777e066e4d16eb3b20591c6764ffdf9e0eeed30b68518639665d9e54e36b34c2ae1b62ccb32d4cddee57377fa4b987a6c6623956c4525fbc994bd8eb2126a662c6ccaae5c276975e45d8eda349e5c141aceb2b6ab3d6bb33b3526052930101866610b4bb00e8e2cc0300b5a903233b4e81ddd2061229ae84d3c344c606ab3c99427a618d54eb1db34ce4cd16a5b9e78e5967623eb4d266aa2266aa2a6c7481335bd5a1acf4432994cafb48e1ed3249b99c034f5dee991da58f8baa51d8bd85946a65399ba39c313756569e5da34be2ced317bc454ae4de351bdf77cba368d4d37c7f892255d9bc6f58ee8764fd50787b79baf6bb79f619775b325f2ba4de351773b197687301a88219d91351263d967fc287e34badc891f491e9ce5a7a5b10e25a6230c939fd8a75c326f8dc471de7a4c8f0e0f47175e5a7002d7c0300b5920d242949116aad04213cc7517e9c85e367bfce823cfc2d865fde893f6a0c16e8e9485450a352f6de45f2271da6905f59b5318bba69b627112cbb5694c3fbab9a3e0ebdc592c8b14ea66ef7277bb5a7669e25b205bd0c91f95d6a4904ab01498e3e2cd4870e9d4d6d71b23a985fdd197ac9476e590961ed3f57227ea90af592a91a79ee77924d2a9a42492adf52452ada4d75f54a6c64892ad97b5bea94ce5a85d79c972370fc99d95954b25bda2322b375629c58a95a5123785d45a6fb6b1703d8d82e98548986b1976d982a8a364236e2a436f0ff5475fe963fa122646c5349295faa0d2d05b3f2c9c84f7be10256e708d520991233736b2059146959cf7487b8ceebdb311c3d3c82a916ebddd3d3bba904c88769861f770271f7b9bb20552095e895d095c5a9ec0218a4bec827cb12c6b7677a76a9547484d2a7349797536903879088315f484f504ca79d823615c2b287b476bc7b445b2cccecccada2d6f667b87d5236120ced6b41477773338430581b82363925d78ad81bb36edbaaecd429961d867965918663d83514a2963949089bec15a4a415ed835a7b42c4b66593c2633293119afd8f208218290ab18d39f3c0e793b95b26e4c10d10738e661073803c229e1e0861d3b76d31cd45c719770709770704cc34979baa30cacc7cae4ba998431e5ed8e27e0201e45fcfeac0824a62506e0e5e527b4130717e0a08b8c971619ea17af0bc54191173bbd0387e65284fb40248cfcc1c1c16962471f47af20c4029429705f01462441a461f7312dbab84871b902370e59f2a1a42938979e505bf291307a9470f0bcf938c618f30b8e38702ef94818d3752965d9a856efbab8f8e026d95a8960ed2e4112c62573d95c5c82706b1c66e1f78202383eda6c3176ed660be0483797a2cca5c8b356c6cb843eda4fc2d018795e709dd9804bf188b854b3ac56792232cb2004c213cfcf3701031dce985a1dea9c32eec880832e2b3507fe60481b2706515a07f1870a3290c20e1e62b2dfece80b811d3ec8e145e2645e24372fcf6aead94be32c1102b66ec681fb8dc99ec275d84d0ba55586aa7a8c5167046f592a839a2b7e8572ee182161b89fb38fd9e55f244c762384d14fd5ad65f7218470ce39318965369b1876f375b37b3cf1f1c4abda1f3c8e3858097ac58113911396875618c20f969746d21f3e54e049435face60cbfc4397059af60129665855877c26e39678cd3ba3dbb6315524af9989ed288eeeecbdb234c33ef2daa11c2ee77df2c0bc1cb1535a7eef2c95001d10e7190c6d9b1f5339a6c87f6cce69000c70eed62d7c9766c37e32e1d1c3bb26f368704d98eec3a387668dfa1d9eb99c56a5258e6a009fe2ec30577608427b43518969766560ca90f98060c2f13953c12f8b3c303d73d80cd4340ee610f7df9eb51052f10ecf23600eec02e94302080801dd78b24433774c9147231c332c8f30308832de9e1b0251206f3c17e7e0057d813e0b704530161b4943d7c240cdd9130ba5718507fbdc2aa003f8ceec46c0dc154e17d0ca85730082009238330a05ec9208c0a0cc8aac1f0bc16609632487601c264ef20487b44b9235900612e16b0400632c0010e7ad0032108010939934139bb84e08cc66450a43c21e0e04b75c9ddcf1321f4000732f89987f1fab4a44923ff6379b3ccbcc4707013211041ce084850021d139c00054952a08215ecb0a0052e50f20218c460890c66400327b24cbc580da455c58bd900ce688add00cec0706496d58a837b0a10461f0bd50b29325c0d171cae2957e02ca7903f520a09d4f35e40180e8663fd743f991c54865e9af021e14248551c649205f0e7e2a084d1b2acd654eaffbe176d47db9130232f40187d35f0c0616407cda736a13da926d07eea8e26852e28a80baa02bb23a1ba244846415a95a7bf25f15627e48fcad0d544cc62c70b9fa4c8041006421cc01e40213416204c7b019301871a70c001871d7004c11caac3b70c200c91c641f7a0a885d048e826c01c2c648030b1a7470d3d70e8b1434f37d1d326e8491d3ef600c2c4a1a128042434610a43588039581cbe06c27406ed509b489920b220ca8008cc810a0a3a02c27414f802401d1d2508b8aa704582ab128440e0ea3202ae56728c601341a22b1acbb46dc5bda39e8ebea2a59cd3bab04cd3b68de3ba8e4e4a6547da51cf1b8d6a25914ab354921d4b95542a994ca7938acaca5c59911d572e9c938acaca0a0a954ab1982c58c88e2c32cffa6549322abfecca256548bf6ce9f3dcecac91ca0a9541751195ea9a724fb16061514d954a7654659ef5da9d436db889fa0a3d448ab1e89ad277d63b0bda23e2ee1c4b27297716553769f6ce6a57d11e116354867b0bed11f1760eb6982d5ac88e2d32cf3a0b164d24612e9c5ce78aca75329548b5833aca55849329b98a2e148ebc8aa62a48c2609813d813419794fea65c81939304dbe90fe70a019c31797ac9e4c94ea4111c708aaea2a2a2a222159c9513c974d538f268c76d5a86f128d949327360b5582261ae1de9e3c383f5441a0d330d163aa8a25451515151d185f3915cb4b8705a2e1c55eac261b970ac1d1fe93379b09e4ce3a80fd6cecececececee4e1e1e1e1e1f1b99e5840b18745515151515191b563ed583b2c74503c3c3e3e3ed7930cc802eae8c864638f849953280a1111212a2a82c3e4292a2a2a2a2a02c0e5f9483c3fb0983c2e5adcc93379260f0b163aa84c680a59404444a4d289050044248cf4f1f1f1c17a7a7a7aa69090d09225444444383870e0801bb343aa09dc26d8c15d0407e9535454545454d4227da48ff4913e291629c94207d501b606b3917874ceeb3c2f6a9e8d2cc0d423c2bb34da33ccbd07c4a65b83e1f8d22d2231776b30bc7d645533c7f1df1e4936c334e42212c725b26fbfec1235188e3767c75e7304d36cd746b25ce73b5844262c57e73b580ee10cee1c13169ea7592dc76fa72329bb33cc11733cfd085219edaa66386d46f79a0edc60ed9176671959247147c270028394de234a0cd149b2e18b145980e3cd3de08bc834e0f90c89b25b83a96632ac811829a4bf89bb0bf34524beee10372248a5eb1a38e81263533d540b2708c28e45431062effb83061f5c000c7f86867043d15ffc910288e6a1c67a5ab2a47de2bb4909769bed26dcb543daa39f88bd22029f23a6b7799ee8ef8a9a9b073ec298eb2dd5d123ac9dd33acfa2a8dc4727916e22d1c05269a6fbf61edd375369c459d2374b22ddd6b18d331addcee19e1b077a17ab2b37a77f4a80b30e52b9f4e6ac83bffa505e6b9e8a4f3677d0e535050a75f6a07691a93b24d47323cb39219d564ca14c7675f7363b22d99a88e98433ba43fa633bb491361359e9beb2d26d6f7b9d524145ddb66e7bfc26314deadb31968bea3a1b9975de0d3ec76d96f3386f6362a69e72f73ace23d21d360ccb111cbdcb79b6a3dd29d79d728f2a1e1ea5db27cca1729b63b4912c2825917a24635e82080eb6912c6a2256b1912a76c219929ef3e83bcff38ebcdef33ccff33ccf7b8cf484789ed7754fd5aebb5df718c9bdb3b4eb4ebdee54561d8eb3296fe5a99bbdee2b2b47562c5d5959a1b6e3248c08e20e27a48efe92ddbd5f96deae9eebbacc939d3723cb47300ce4ba2ef36ece50b626e2096764ef582c6c181c8787dc811bcc3dde7becee590867745d67617fd946e25824e2c88da81cd9ebf4a292a32a3a922b1c47da3693f43a4f7a2fadd8d355ace9949ace9d4c92e479dde129f5461eee2025620038fe0e765869162470f13668075504ed200c734e2a63593b20ca30a4020cba0067cedb4681b7879a28da2682b706c353a626469cf58d15e793cd07f5497f52f2c82cab3595fabf6f0a31a55e37d55f1daa2743a70c7c4c4b1e999359996447895c326ba74387da5dd973e513740ed5dbaeb5859b94492e9d0ba7bf2ba73f695d6149e9cf9a62d126d5ba356db6694ec3aecb3a473dabd110d39d8de76c5b81b36f49a4d45c31c44d65fad90bae3c3be42f03e680585ad3b2a6f5126359ed7345117eb1ac26f07c91cd3559d2447809e62692f8787fc88e6a8f9665599676736cfa83c3d6e538cbba1563a465e3b518b9ce6237c76d43c3f626dab6255bd7351a624cc558158b862ecbe2a9776566a7ecac2be61429a58b22ce6e8c1067b789b6bc65f15d0ab5327a4cdd0c45bacaa9749a1db56dd9e9a64baad7c2168df589e3a5f12c3cdf68883ab64bc33d5ed3b47397c6ba85ad26c259a321d2c83a196d774b46e5399b2a9d2624cb366b7e469c8b63ddc774d63d84869836754d4211f08647088b66d1c4fc4b8c31ced948c8320e6a70fe9b96eb8c1747dfcfc54650901f203f461c1173797f6cc4bce0f89c82970cb8f81ff05a8d4ca49987f292b1e2b3ab002182c0012f2f6fbe8937067fd8083a05996532c68edddddddddd1dfb3682c01de36c1977a8209fbb18a53f39d49fecbe9570ee520e220c792a735d53ba4d6b1e0943722e523e4a219b475e665536c5dd6d9ec6019c214fe3d038398b3f435ea294382542171f2d4fe06006dcb1284e91df8135afa8735d97d551a757f9dbe9557c4c1295cc19fb5f77879590436460eb734a251c1675244c96d51ab7d80324482ad5d340660e31905e5d375f413e80af19617bd98d4062bf31905ee588c51b7184106b5e325039900204dcdd97033270900ea6bb2f12467c0cc2891acca088eb2d092f2f26ae7492c73d5299d2cd139790e0ee66f64463aab574273c598849d714b94837d4318cc561ea2a2c5775a5739d35bd64b9f85acf52df5924970feea53b527577e6f4eedeebe9dd4fdecda7972ce7bd744be7b0ae64b9c3d34d1ce9ce9c6efaa9b3a67b369f0ee949f4d5e62eeb4834d77a6d3a335d1a7ad2a56179b569dc5d4ea5125f595894aebdde9c525159b9565a39b68295ded919893b13c6bd4abc72330830764d663b564d376b5809855dd43514aa54ba99d36ce997b589182b61d7308cfbd6bd64b79fbc1538ba249b3353e99074337bbac952483aa43f607769b673a5157bea4aa69f4ea7afac7c74ba4ac7712f916eb239cb4770cbabe92d16360c15d2caabcddee7ca2d122156b1b0bb34a573f15ce91cc4f49de532f70edce0d23bae5ad33b9b91e0ee273b63aa3c4a27952cc49b77eba47bb7b3f15a165fb165b38d64698f0eddaf3cf30d1e71165e96b374a3d58ece59c8c4631a42fa8374e976bb18797140e5a6201e293077952a386e1261baca2dcb457bbded76ea849a65a74ea8f2db8b48d5ccf5f82b3ebbd9f4228d6950dfcec1d318bf9de34143bd775df63cfacd7af0147a908999faaede3b9c94f398c0f4080f1aee10d7119211a45e3cb51c4d77eeb60337b87b772a53bac7bd3b54f1a08787389eeb6c8ef5763637ee0edf24528f6e8e312f710407db34eec00dde1e6b98c0f52bb626e2ca2376e0066f379ef493775533f5dbeba581a7df2e4d770e09a548703d94b329bd7986d2888333919b234eb42cbd39e2606b8e489ced64d64bdcfce3e2e6b71a24587bbeb1dd9ddf34eece43aaf5e0ded16877538d6e7dbb6655a36fd71ed34bccd0c38fee0cf7eedbf30d0ede6e91c6db4c9683b74be35914ee2cb410d38883238de92a47dd4caff2a633a6ab5caf9b8aed54ae6291e0ec9d55cd70379dbb89070a98810a836e2005c75f3f5470bc34578c90c4753723a13f2af778d93cf1e8e69bf8cdb319098616c2095418748328dcec4c96335d8bfeb80efba3984d67bf2ee90fecb4e98fedd22f9e6694c2a1c6eb5adc5e69ba5f178cf5e6c6f5dc377b5da95fd7b7a72c6c181b7c3d82bb17a9f939e24ea63bb5aaeba499d155d7ebbd997aaf3b47f228fdc8a35c67737d1ebdfb6655f5ceac5c357a3de9aad19da927a1a855d18f4efa6555a6977e9d435995afd8d3bb6eb32a47d9ed2b2a17feb47536d33ba372157d7df74cef7d6455f4ced4958f5eb2aaeea5d37324cba9d8d3e92b96bb0ae9e69bd385fdc125a2e9ce90aeeadedd99fa11bc77ae644727d9ee5eb55dc95e27d57bf34d477d0c00c70f91f8ba451ad3a21a0fadd337d45873dd7a66e1317b451cdf38a766b941500e9260161806e1e009cc8d9840e032118143e0fdc68910b0b06f649bc6dd236190cc1d461c1ece7af86c74f8ce3b7c0d72f893030e9f6ac0e15b187018c4e7b301be1d1ee03223c0fec6e16fe3f0380870f8206a1c3ea6747805980e2fc4e9f043a81c1e0890c307f97178238a38fc11441cc67c1ee26f873b2e0f1370edf0e5033814c0691cfec88cc3239181b39283d2492561b13384121d4b721c66ee0538cc0af8cc32df0e795c1ee61ed70e33108f7198bfc378f243140138cc2c87aac396c3c7297a955b1cd2e3387cac02e6305b5f1d3e4211e2f3f0314aaff2ccb7c3055c1e661fd70e1f8b7a955dfee2f0514aaff23d000e1fa7f870f878858bc3374e036c10212fc20241e239ec10477c65715823d81d605707b8d901af81809b81fc460348ab9a630807dc5ce30da815201551010b9c01421c10c4cd406e43880108208400a39b1b700708f1843080004284e468c0cd1e56e3a36b24ab275d231969e4bd011668c0cd41ce000b348001cf7152e3e6039c64817a33023eb200016a3cc78977f3e80d0872b37706dc01405e44058a08520107542088039ee304c8cd41bc880a18a002376c901ea4de01ff4101191448c0cd3f3e000a0029e2394e4a14305160881d0742811f37cb781114184069080a54e04420f123e666d36920816f1ee233ae910c8922fe0309248898814445224711379fb01d37d3b8ca3592c9f800ae914ca50245dc0ce44454a008229ee384c6cd3b3ea302326e4ec0552a704790e364868ce73839dd7c7a11406e563911ff61ca86c84a388050e007901fcf7162ba39e625fc1c1418008dd28198fee33a563c6e7ec003708d643d625c23d9739caca024a023478cf318e21aa93c74e4a0c00b70840e051c217333ea3f5c23d9112b39aee388230aa0809b61fc87235047ace4b83985c5388b6b240bc0795c23198b6ba4cb71f3102f0005721480c77fa0c0cd3dce82027704394e7e08c0739ca46e4e3dc71037b37801ae03c6cd3150198c151c1f62751dd74837848ee7388971b3020ee31aa9a89b65be728dd418309ee304c7cd2b1fe266d4751cc6c22ce0e607dcc53592f958c00b1898af56cf71f2809b7d7c012f16f0806b04c337e3f84a028fd60886994c0006001398b91900f761022f7c581d6602f1c5040070f38bfb708dd409006075730be6e3e699b790808b2fe01ac95a486075b3eaf11ae9563337fb20812fc0c5cd2d24704790e3c4871c272d37b77cd5e2f13e0e738d6098894a883fe01ac9301308bc3800ae916c46758d74980904705c231d0e98e73811e2e619085c889b01708d543c821c272f729cb0dccc721caa9b5587f9cc6364104bef00dbbd01d63a032c772096fe87ed5e84b54e84e53e84a5f761b52fc0cacfd8ed42d8f91e563b0f2b2f63b72bc0ce27c06adf61e5b1dd1e63e71160b51fc0ca1bc06e0fc2ceebb0dd7358eb05b0dc71587a18db7d65ad47cbfd86b5b7617f02d8eb352cf601d8171a56007187119f61afcbb0d863582060d8af7718f11fecf50058ec2faccb65028103c0de7b91c81d46dc1c27f6764f7ff0b79bf407ef83bdeec2626fd1a262619142ada89c4ca5cc6e98642f1434a1e80cb85a29832680dc804716ab82ce1786803d7b79a1092a48c13002d6493cb5ad633b497ff09c6d25ed443fd118047e592610b83e18de9a4b30bcb43c18da6820ca703741dad6416866de639cf316b6ac89ad5718862d402be068001cafe1f802c218d97a28eb9d7046bec11e0ecee62fa730762c3bc69667803155bee794cf435e3eab4a6b4a39a51df282a7a4e9cbbe31d6eec37e2b81832e79f2d4957fd7016a2acb6a4db9f40af595a70e0f1b4626f57c8353bf86acbc2911a9df2cc179e54320114eddb298e9ea112fee1a3da62f6bf1d06e3e3dd63031b235115b1866bf191d8eec152f4717766ab58f6c363abc97c6ba89fefae8ba37dfdce0ece6d32d938bc3da251fce914efa3b1b7f3d62a65ff5a55736f09431faf56891441f8f77863e5efe3a7d3ccdf42c8ec9e6b01ff4dae355a33b43af9d62f427ed627731d2a21d7923eb2eb44be37d7469eaafdb34bed8cda75b9ba70fd3af473a43b202366eee218c3a012cecafc669182aea39fbf1f172d85f8d9b3b0a463da33d5e5e44e226ea0ff501d8138654868606d4dfc4b05fd7900e6334585056a8d7639cc6312a43e31c0d1beb45a35f0ee975baae4de3979fa88ff872ae3b1ce31343fa7263d88e869d18bb9a368508047033123ce3dcc6e1e9833a34a50e5d87f3bc62084f962642bdca9f50af34202da857908a2b823428b9933e789e9b5d4f7fb36b82bb27f0ec9c90b96ec7d3df9c2d344fe7507f38ea39089e31049e71738c671958c67f3866b30d8cbdc843e0ebdc762d9b9d0e559abd9071738c63bf288feb319e59eb31128b6187d8c02abfec101bf83a1036e3c0a8c340ddc5e6885d8ed119956b63a8df7d7f73fe079487828abacbb3061404a4014d53006eaea7113d4b935eb91c62170bb10a04805d225e9b6d3625b7143ed8bce9e07917366f3d78be85cd11a7c5e6c8a3b239feb0d81c8758d8dc3829eba815ac62ad295348ce4f298433214c0302c24c3723dd9cd5d1e4c99ec87ab03be484adf7dcbe6d9204496480adebb2269e51f44a9bd8556113029daa5635214c1bd280b44fc268538030e6b1e00532e0f94fc2c01d1f0e3cf38781e08c9935a021ac83269619a1d2a1bac1cc1d915e87d407cb8f8e1e620d549cc18a3440210aba71b34c3eb1721eb981189c7008db6c7f2001a14024840891017ce1628c242141293d22eaf0e0d0d6759dd77deb3adbfd88a80309215426deb3a4e79bc86d76740a8f188db8d88d6c5324228dedb2f1669b4622ead8e8b64553b4f864739c747d239e1bf566a3256516492b9a287efb1142b86d7be6bedd0c87b28f3e5ff0a6bd747992bc597ed3e6b377ebe697914559a166377b25da8326fbc4a44b635deff3997bac77f4cc5d9bced3c2f148441ddaa599f74edfd48777918834e8bd368df3cdbc345976918834e276918834b6969666e7b31e9389c96461c388d94df6f4181923fb9ac5184422ea18c2a4e69270d0255b38456d4d29734adfe85fd77650f32cc25d34a7e4acb9307bc6ffe800f5ce92e621ca1d943b0847c3d899522484e38b748824ced014a1209cbaf593b31d219da0c6c143b18728fa14c527414f62505cd2b9837ca6f414f110ed08e904451c1cb38583516105c970f9972151ebec28e981e2a7c9932885124486348efe1a47cc27e3a557db63841142c2e89b87d808c233010e1c424818252804ba7a7b23c158892cab90286e8f3a2f2ff0d67d491bbdca567213c6f62eadb3f87eb92ffdc578a5356d9884ae50061ce39d1cb0816dd47a6b4b90d79bf4c19dd4c95179a43231ede9481c79af04a406ca36542d568a050eecd906b22d854782ea3d774a662d2a8e858db897ac3ccb6a6d96265c772cb6837806ed4481a118300c5ac11438d23a821899d40b6a863f3d4b1cd3e1298f142604dc203c59190796e76ce04c33fa7c77a8699fda5b8a9753e78b7a4a1ebdd68df2687cddd99f24bde2c0de2795f11ea5e82f6549af16e248eb0cfcd75fbd55af77f2f6a856c583be83d4d2a4c56e9681e5b539ba7c8ccc360d6dbeb926f648ef3eb279880cdc442ab7a3d098ae6a1e72b67f20d69e59d5fc54cdc35a21ac877648f7feda1341cd29dcd2ea4897646117f57b8a13ada5da09db4b9ac7089f76e1a550a857f407717274fa465fe2d8d6e96f09eeee91dd4be825a5bf963cf209d9e3a22f131f1ed35d24617ac887db9dfad73f32be0d3e8bd244e4d0a3e8afffd3f2a7eb6d17ddbf79dee823cfd2c8aad5fa1ef3f516f90d86d6bb66bd03f5f7d35f77dd6d29fa6b28712076b17a4ce2b40f2ef3d7e5953afdc99c10d4ccbab012a757d3099e5ef53491eff7935ea96427691ddca9cca7a5555c80439dd31f6c9ffe6043d11f7cd237e0717c7872f7ec484a2f938a030f2fbc751b585ed12b4a29fdd7abd1293cdd1ea7f407afe80f5e2aa9db3129fdc1a25e11e9e7884737167937474aaf96c9a7644409dbcbec101b8d537326ce4ee7502835370e8f8c53c8031a70ee1e0c77a0bca2e658149f883dfd833d81d4d2e2f0f0858b969d9fd7aa91386bb1a943fa1275ac7c5e2bd7cab5aecb5ed76362e465e7576cfc9c9c6553cf2ce9e68849a9b7fc25957a4caf5a9e7aa432a91b2b2cc25cec52b6e591ce70b846a461b5b039e21636709ed9a66b441d99356f8d48c37a760c7573d6c7b46eb3aa9841728a068a789bdffe7263bcdcfdfab63d7bdfa8a57d85b38173fc969d4bdd3c71a6df8eb23975ee661bf83aea2b166585aaa56edd15ecd2a4b4a39e6de0b9f2ecdd955ba346d491dd5b23d2b86876ac46a431b75b23d2d85ace691d89c482b2da1b47da43fb7c66b76c3e33816be21e70dbeb629f16f5a32ccb372b31c942dcc242acb5bc46d40171cb8d15fee0abe7392e9bcf47f0bc79ab8d5d273d667db3feac8be5d9d5c242dcc2022b8bc39a2a755f5939dd6e719bdd3e6d6fdb29ceb623bfe96c4ab624585adfac06a98ffebcaaecf39b2a3b766bbbf275d5396bde69fb1b16d3ddd99dc17abb66f37516d431d4b78bb2028b7357a44560dd66b5dfba366771487fa0be5d1ad4b14b43bfdd46e2edda488ca267790bdb944b7dbbb92546efa9156f65bbb5d2b7ae95fe76eb66105cddcaca76e1caec566ef7d48aede6cd5ccace9b560e6bea73e5a995af5c2ffd09eccf7c04671aeadc8c66dd7c037bbb98f6dadf6659e9358ceddb8ceda8acbf8da52f9991faf3ba3e31ccb2b4a350374f2dcb5699eadb3ccb315be4d24c1fa45fbf3e67fc95af77e086db6cdfb2f9c84c6ff35949fbe95966b9766d243e4b2c952a2e59d2532c562cc4279b95c02c2ca46bbb24249dcd916c089eb7b624b01293e56db9c97d92ce62dd1a8929a52eb84fb2f32416cc728ec5c63a5954a6ca5bc54ecc01976b629466a556ce23628bded36653db9cd37e2e32ce19d2553e585e7ae9d294ce72216efa8374b3feb22132beae64bba0fee4b727fa93df70566c2794b2dd5017c57628db59d19f7ccd9e6595f4cbfae94fceab497f1e0ba435322de711318683e56872cbb0cdecc19ee84fc394cc39b579e804162a0b8e6c71d975e9d8930da7575ca82a3f1dc39128396f4ee1c8292df66aa2b217142cf67a7245d19f3c0b7b45ea1dda4b7ee539657d56605945dd5c53a9ffdbee8633974c6cfb0b26845d3924a1f4b7592a36cb2bb0fcc9e6634d9698788a3cb277e94f12f527a3f407addb4139e1ec9c9d580a6d92485a11a56cdbb66d3812869cd3ba73bbf55f94feb8a197da82b9eee41096af59963debd57cf6ae572d18d21f2a1ff3db896057f9b0683218d3429c0f07457f5c13a21d1bac400a49a1b9e17439dbd9e4dc705854710067c0b94215aaf93fccd158def8ca0373402ae0f8acab7d233ecb42858f3867f89308f81a106048a5505fae3ba375fb9e84d367e0a00be712b929e794524a29a594524e292fa59cf0a7a7e4d178de18ef04596aee9800bab8a1c65b9f3743209aa9d416a91778ce396fa6d1c86be7e6339965d7eeccb26c66fd0d81969cd6b51d58717e4aa9078e83157637bc19043eb9831a080bb11b46f60f170f84536569597de95c3b9792abe782e2fab99a5c4fac297f7a957148a029520a39058e3082a0bb7b0e21d25f1541fdc59f496540e083adcf991b08cb5b764807e17925e94870694af4a79d48af668fde40bdaa1fcd8f2e1cd24143faf39aa85b0f252e1109a7e09851991a94093da13f610e5315caaa08aa33d39130aa9f7b77eae52fbf641caa7bc3042ccf8203078b9797d4f7a1fe95540a6edc56baa98bd4070270e9a62ea6bb74f38dbcd61772852481e4c7c4f26641bdaaa25742bdaa16900fcf892770a8b7834052e07ce9e42c08eb0849627bec6eae42fa8bc7db4da2bf983d6741385ea3db942e04db140d67db141d1c9518424476106e12245922efa9837012fdc55738742149cccbe4f47883dc984887e82f1e4710bdfa37032838a752fda3024cb31b53571d04ed4c8147e7384834c445e9cf3a94fe2cac3b664718e7c33d9c9aaf1df9a46f58c7b82afa13f241cd4d670bc64e18eb30f6d930d7ce3388dd3cc405b35c758e45656794a2b2b03ca6af1d0993350ceb2ce8614a4aba87ca05ddcc155d2e41b5385724a17040fd59973ed64ba9d3ff7d566a0f3eb871be762e9e8b8a3df4e04844e464c7598d87cef79425a692c984dd21eb26acc847c258974b24ccb5236158b76ae976d6e2e6cc85756ed25f3dfd59f472533829571d2a8b67ae0863d85dd23758ccb47c74a15e41e995752e4aafb45be7887a959d1bc5d1cd2d058fae711cc771dccda36b1c682938832009a6e14e23af7295739c15dba332a3ae02e70c6323ecdd86d1cb5d1e62c982b242cdb69137ba4cf7985e4206629864730fd5eca262f390866fb134d855570901317787a8ecc458974ea3d1310cc3543ec2546e3aa642851e7470bcc641ec66d55b1c5222542f227187d11fb1690fd35558e466d51c271b5cad3c73c7eee862d7747ac55d8bbb96d39ff591cd1a4e7f96f48133ac537a4ca3c7ba5b838d4eaf2ac3f41691188339e0c866c7acd4749cd4ace1489f6b389ace867d74ab7b1e8d461f5d4b7a35bad6a88eb45f4bfa5362b15fd62f197a63e5b89d6b8985dd0845bd9458a95bbf96f40a75eb76f5e196ea2c829cc02c70b04a47e7469dcd2da526e2223518e6e82f2a33bab988c41d0fef35710f3db6638fe9ec5a11cf66eeb6dc2212cbab80feb0ab6c4c7fd859ec10fd61b7a84c768550798c54b1a5b3d070328cb279621f6c0efc19c2d657ac0af64765b0abd8d24fd6646fe9ce1d8ca4e160d72cac5e4e86148c5d4be81cea88d2e78839e9e3edf46779ef2811dc47f75ad29f758e7623ef5e4afa3342bd052156ff4cc391d6c5aa0fcf99158fc0decdda3b22b8cb27fd4928fab34ea1603d4672d76f73220f0de0807db09b0bce6cd676b0f58861f158fc64e96721085266c4090a8215785e092230ea40a20ef7c8d32b5579eddabbe778effe877669e296e40d35479da853e38f30420370c63cc76db76c2e999e9d746e74ed351db89981715df7d83d768f9dd5eed9ec9dbeeb347a48bb5b8361cff38a401cb9aebb4d0d86b95b24c3dd8d49a21336ee442535dab5d168ec8e9d3e9eb4d92554ee3db33547f076ae8b5d87d12ed61ca1e996d8b6ed5954f9ca3d4b73e234bb44911a0c77af897bc035d1463573ec1d62efd214c970bc3109d638cd7bbc19098e29cd2e01e18c18d9e5eede5336dfd4442570c68c3b5d911a0cc714c019f33531099c310fa977e312948d3a5ea712368dad29d6c4f3f694fee4149897830a0523b2a28b7a35c507734eeb33eb25304a18258c4fe20fe6b2ccf3bc29fd59b149373171d4115327c8709c374a8133e4af380516a5e84f4681e513d89fec2c2cd438441818558a600e48050b0e30470722f63e9faf1358cf5708b07cf66cddc1eb41bd2ccb8314a33ade0e24e59165375e289ed741432e43bd920dc5f6d03f39bd9e9a2dab88cb7232557688bdcb16c252a8a10461794ba343dc601b4b5661a550df88bf3c6b1559527850b1d3c79efe6ce4e1bc8e488a287eb0fc131f28ae6829fdc97390ca2a64d085d3ab6915f5aab11c6a21283c4b9cc052894c2277b0bcb783cac4e51ca40d803f2ed72a9230e4126c1d8b704acf2a8233249e03f62cc3ee8433a665634f9175234f7f725299398b708c822576aa610ec22c7b52b363dfac3585bedf7472f79e5dabc80a2e1261792f5a81e5bc50e485291baad0a444753ecb201c24ab9042f2569125c59ad2abbe70ae9c4be74ad2ab1dcb96ac25c55e81e5ab95a769b08ae64791273e117b5aa8a1f437d45f0b61126baed3def3d6ad4b833529d6bae2c2b1974e7f49b46b15f5276fb5d0258f4dc1d22af27650fbbab78aee62155d38d32a9a2b41b66308d4c08d78c4c76ece70d7b6f818b9cdc00c63f55c56058edfacfce1d5239826f3ceddac25de5e2dfde1697766af77db6eeed5c5dd83f7e885368fda46bbcb233e7bd4ec4c3c9dd13abbc43a26fad618456b0613c6cf38634bf9eb36145f725eebd2a866e2b1756bb05b4403f906c7db58d8ba1db05e8484addb81781b0a6fdf60b1d96059973978c4c1093c81e3a7121c6f45fac3baaecd848d5df7c775454967200769cf4c4bde1ff1f392fe88aa992967e79b2b4a31c87081e72007e1bd1c04c1c39f2b70ef437b6b30554ddc035ee27a4df7f81a23f24ce0f825ba6b232fbc6cec22a5371791d9e3cd1dae8953b075e5e373916f3617919d666bb08b5d1f31decc044e08ea125613d57adf6aa25e87bfae251a07d760f50cbdc232f62ceb30366dcd760867689a16b5a8ddfa6430e1be71f712c675edd6b15fd87569566401be96d034ed33c3d37ad6550ccb56ff45da8106c9814eafae231d91f42a7b76712e2ba25c439710ac41608e9a0edc6079eb304ea0d9f23229a5dd1a7c68b75d5b8dcadd08abe3b6aebbba2c33628b95eb38c8c10d571b447a755d1cb77557ccc16949f9a466d094da75dd11fdf58de8af3f3b58330c0a02858c100263885ee52a04ac99589e83a4537faaedba6abb3c1a6fe71e75240c44030f4e4ef4e915cc99545801c80a403bd891a313160f18d6b0715766617623ae6f4182a8b6dfe00c487f56467ff18884518208481023b66cebbeaecac7c4d7afdfe0eb495522a32f444a1069181976fdc50f13c1ec10ab447fddd959b22715c9975d096e8b38888ec3312527e22312068812438860184bf6a406e9af656a209c7125fa8bd7cdd92356407f5c4c5f11c704a102913026f80eef106f37c3289e9556a761358de56b3c20d479f34704ce885dab2cc93a0873c4ed5cbca75a85d0451b58064515426619373b49bd21e0fce1cd824443b06ebb35228df81869699776c57b42e8267722d44bd3ac21689a5523d288568d971f56bc33de1a77ba482e6e451c74b90e2b8e472ca3a65c6ac4a47b75d7abeb1691b87e3eabb6a5bf78523d84332ae9b3bf7a488920a9ac63314a1014dc61fa1a0593eeb5d78d482412c9a66948da6844e988447a1dd1e7cd06bda45fcfdbbd48239b23f66af71275903eca13478ffad870a5b447e9dc65a3a332d775f3ac36577c8384d2bf5ca7f0077b57bdf1fe1275d4a34ea2deb5fa98ae1175d0954a5df9715ab9b949a8eb5dbb48af37a6afabd23e7a4744fd75f34cfdf55fa4bf441dd74977e5ddb30d3ca2b407fd753b7a6b441aa3bc4239daa39e66e5e6b8d54ab52898f334ec799ee769a47bddb330d250b1a39faca946d4d169da6be9ba35eae9b34d539255d18fdebd46d471bdb3694c2a5d3359ed1aed413a4de9e698d11f1d26bd46d441ba3e9219554a2dc4b5d6d72b62ef79c21a51870dcc45da9de36249b37962ee42bb6d1eed6457258974553efaa4f7e893483d7ab537235b23d2e81e2369ed2295e1be9dbb5a7ff6a7b23a0be6680c8b483cd318fe601b381efea840c14197da3031290b5a918a52a15481e1876818d810ce161820da8d5d9086813b3c1e54edd8330c8aa92fbfe0ebd9857a3d35a7f04cb55cc60b8e5ea9b48b33cc6a63a85de601a1c643973987c04197fe24941987105816312c62594c1de1e56088e8f9a0664864a3847679fa845aaf2b3b915e9dd3ec90fe6494b2b1ee6dcbecc4d7c47848af341bb1a66531198902fa8bf8c7ba4cb8cb2ba057f35ecae84f76aaf6273b26d1278302c7c19a44f7639c71c6186344429f763084db265bd040d86363961caaca47d35817d6020dc688bac7822abf3d3790d4e91bf09a95393b159bf79404f7b7b5cd1dec70bf2f17e976eb9014c66e07f5077b0a6f0555faf4aa7bd52da5e85ea9fa7614b87f246ca0be3d4550dfc8a2a8d8b3067aa6a966b09b6f70532298e4380bcbff00491c0903be3bca72b09a361b0475172a2e2c126e0e453f3b50394e544e82ef70617ad6ee5d0985e6b35d7b963edb69b47ba54f7f5036c1f0b2897ce2ddfbe99c67e513287db64b1f0945af4ebfa8cc69fa48f944b3f209d9a384dae2564df54aa572884f56e564a7147b852d24c15107185e410b4498eb2e52998c09b5c5adaa3a0b92b8a3c5559d7e72617501420fab2d6e4e2c72e20c9aa699ecec2f2bc1cec242954212069a7c600e0884e11309d3e2f0f2470249181f0e7f38271c5a620623c22dfdc1cba4fa3322e2dc88234f6ddbc5aabd39fd73832f6cdb76db53240c781f6c4b9c393fa232db0eed9b9ddeb35ee5ac3feddb67afb6479445d24112063cea2d16490b4918f02d40d912aa8bb738ec6fdbb6eba9a0cae77e82618bef2eacaa0170070e09031e0746d9f122852e8c60054038810da0ec7081a2a2c21d30ca0eb8a30508506f711716042d77714881e4b41c756e76b36609e433b34d6a44cb5d5c201206fca446a0dee20a2161c0feb5a3c5c21d9dcd15ab6c4ec4398b65719da450d749963c51581af9ed9cdcb45fefa1f5cdf27ab659a8b23e55565606f8d32d79cc8065504fd11f507ff03e403f1d9bc0f2e694f4812f45c0119e76ba9b956e21c1acb0c618638c355e9eeaa1768fbf9452bef48d7ee93af1525069628c31c6296d96d5fae11824de3c6408bc5d8ede3526d51f235965770eda3c63534873102cedc4d27e880d9a78edf1469b6de0f8154421bb68738c18764bbccbd7b1bb2ae5119c113806c1b13bc7598eb32c84975606cea286a3bf5661365ea0d45abbdbe17a0279f986bcd8c0ed32311942ac2494e81bfd2dd40c878400a9d8633026da250cd4eee1d4b2ec188661588661a78933aa85da67f9c9dab22eebb22ecbb2acec18cc5a6ef3ba6e5996656dbfec900ee28e59486556600e6cb6f437bb49cfd4f19c61d8ad1ce6784c0fb9accb22e970cb313be4845b2e000e9c304dcb05700ed6200ea98c011ed32f5e582fac17d68b172f5e3c465e2f6ee396e5404bc1342f6e230f51e0eb2a968eb6d3ab9c4dc144bdca9a4e925ec521cb0a4be80757e0160c837e308439d8a3c2201f1061d80428088061900fa0c0d7314eeb22ed61e32f98c41cd29aec3c22563da67ffa9b4ffa9b51f4e7d39fd5e4b2842c28d6d06c43cd969025640d693ed9cf8d1a33fec3a1a4813de1048b4f7fb327b33c9c8a00cbf2a46fcc5b96058afe264b4faf62132d460064344c3645c298f7e100d7887a05e353d371d1e2879b5353e6ed65dc294fc4b0fd4447c2683df7d9cdd5ba332cb7aeca87f5ecd9a5f9e1568feb3517cbb167b926ee215e43d8b375155a138770702c28fd4d6c3d5b423d2ca1c790710867c478a43231ce452c86658280cbb04cf02da15e655805c21cd655629a4380853bb81a26980902ce75675d16b6d69084812ddc71d91aec10ceb02c224ba83f28d8afff70ebaa7398855d3f60d612d7759de5343f3cc32c38fe70cbdefc03cb597c248c0524614c7b673f6b3d1226cb52f7ebb9717356e3e69a9a7173ea3037fffb9e85a15a5880719920e033009789ce77c4581d0ba059806b4098c5aea282d1216e54e6254e7f2c06f0e6427db9cb8bcb8bcbcb730abbacbead9e33bcad9eebca12c281e3e5e5fbfe54aa4d9dc9743a3d51ea8dbc91f7512a956bce70671910da18ea2daba7bfe3780761cfeeaf18db358ceb08b05c963dd17ca0c0b3074fd5cdfabbde228043da4300d7668c9b5bb07502582ad7458b167ba55e58f7c52fdb1204016c6ec1d141383090249a3a78fe64f3ec9940787e65f324c2f32f365b3ab900768979009b352b3a27f03c10365f4bae27780a5d53f05c823dc1f32e366342d87a8be5fec2067103581b99fb6551d771ccda34ce50c77e59cb7aeef0655996e57e591d47d5c48bfb8f20b0f51a893970c2434e323a6eacd6b5e94e879d18b592c3663cdc1058e5998d0cc332ebc359c5d25c77880d7c3de3913a8ca32c8cbfa8341ed3594faf56ae16e3d8cd436ef644d633b190f1643c594f0c9b7537471c747d6ba026ea35a7d62361cc5febb914367886754a65b41d4fd3e96f6a49b41fa00ca85799c527fb61f16181a257f6f399141a10157968827dc0908a3c24c1d70fa888b1efd76d7cb3931271bdc6b32b7a55e3f3d9140ba85740dc1c31109f500af8ba0cd491fd401af317ccb301f075035f335ee0ab05be5428be8024017e7d02f1ee1c1056f3d19a0455209eb59e1e5a4f7f5303da3eb79bb607e050c26c3fadb673db57b6c3c890716ce6e52f7e511e37be9de3b21f50d286d592f4370f8485b886fd34d48d18dfce5d94c70fbffec3f618d6da7973a1a29e3b08f59f0c884507a18e822888d28080f804e23037e300e22b37bf0071eee60f03f1d5cd07e227201e00206eda49e95414445d857acb500b51cb850a02fa69817a0b9f1e1e172e76523e64d8874388ba0fd77a34cda757b0eb2e4aafec496d2bfa43bdd278a4322f1fc0f3b4261ce90fd54ce397377e39cc21ed0103736487874278662b03155271c515f8baca3995eb796a259c27c42c8ccd11c3fc86cdb009f886d6a3f5e438b9aed644f3e91b918b9d97a4660da807cf6b56f42a5b4259566bf60380430893fd4818f300b8f1200983804ffc113db6311601b600d7e88f4fc6354ac48bbf9cd33a184b80df784c73dcd645da63f51891f6f8e1d92f1b4386cd13cbf80b9b237e711823c6332a93c5b8197b8c94f1e2340a605bfa9b3fd85c44e2b079086c0226c06f9ccb288fd5afc3f8c030d457b606c3d9cdd7adc1f075c3c2fe02b11da8346cf5e5a11c754cd108000000095314000028140e880442a150382c54034dfa14000e95b45270561947499062cc18600c000000000000000000008000df370a09b2023debdca7c44c0d7882d0cc7e89a41dfb72976bc3f68a1d601a164de6d2dbeab2f521e1fbd26d85e8698268ff61aa0442cb11481b5041cd87ebe4c35d8410a00a0ef045f62db05c555dcd7c3fea169de45e9dcaca595673589a4a450b3e3fd3e7f0b00780960c01849593a33d668a8567a9ff37d97035f9b18821ddf91272d806dfe34196a3901adc886aa4f73ed83bbb0d6bc81dd1e4ecd9e6eba6a29dbd5343ac8d304f62a871f7d9d0e3165d93c5ae4698b856105287af6c32618769a022fd55ce2a8b392afb68256d998425075b6c883110e320eb700d93941608de80efe677b9728d8fc3f072c592f27d44ec5d1126c42d52915a395ea6e510838e5e580489b389301e7a484e0ac0d5e68a049e7afbda0e74325fa06fa58aa635d9fafa386f7e0d553b958ad49099911841e3435710c6934d64dfb71c8d930caa79103a8a946ce596288cc84c4e8b9fc58953801db28413a258452dc72f0376b98dc7e6046b0769c7ddc4da8c170b13655c8200238105fa99f35fc0f965a65e475d74bb05c72dfa1d4bcad19f7a12196418b72548493cb11d5d9494872aca33d146cc45a2ecbb6f5d5ba02e09328e41b021a68c495f9f82518ffe3f2ff2a84bbb34b01b19edecf68d8c3484280b4216509493e93b8d45ceb53b8f49d455ef79b342dcddf171af0b61e7eb9c6b0f91cc730ae7039a3766d21bdef11ff1f847b0b17a3423765a33c8e9456ddd6c4477114ad5fe9e0a3799f3186e3e826c334c86dc7366b337b1365a9d8487611dd3d91b6257b8cb2758241f7ba3a46b58d26c5c90c11a0046a712c782cbf136aee943213c0151f9a17f5a1169713dd5227879197ffc4d2b48621cf034ab558258c30ab140b18fa677573e1efd9169ef7411c906ddc046592b4515449daf0026bf3bdacbb1584bbf2311e3e15a02ddc5154a4085cb8dc51f40915419d52870970a0bc17f13d48e6eae7e4273106162809794a54d726410e33c76492509b76940c04bb86758a6315429416bb7165ccac151a78ac2be4b3140a429059f4afaf59b76b7ae50ecc699d0509f3273a57b37d1f0d05a89be37b977f050e80eb8ae00d429816dd93707aba0a24b2549c7312d033c9d7bcfde2ebab7fa666f0d0929c8abba070c42918750a19b7918beeb54ca14e3eeb98e1977ff5f286964d1e9edc43a07ecaad4c5316109a929de4eabdb884b041375b8fc72ef17a4e9c82d17735d215d3c7946991d518d41dccacf8380a76ba4adb070ee22c6f425786d72f63da9a2fc0ab8303998755e4a06442a80c51b9787eec30aa0e1bacac71e0e418cdf44a3e940e9eba6b512f7adc454a43e565d254aa2ec5d9a97d1e95c8e499438aecb297e7e7f106fea6dded3569da0a5438a8e20c596d597828bc98c2bfcc10133106fd44a67383f10f7df7368d2848d87cc84f8e30711839f4122e029658519f2f2ac0f85ca1274d30b58750c5a02128c0cb37a157deac56ab6595f24e62ecd041e9c3a0208560b19abd4867aa123607bc646ec7de564701a89660c278c07786bb093930b46b85bf849f457277aee7378048b6b2eabee5222002a24094ce25c01e16097a27cfa6bd5c2c697c481b9daf480fb519e65d692bc1c3cfcb0a8c83733fc919a3152df7a1cbeb6d3cba1d005cfc7123b6a8196eee13a5d42b7b1ec57fcafd5ef78cf2d5aa635fa0922a90218f06185b8bc6bd765053d9de2e287bf4880c99505ff1d21d3f7c3a7652de30ee01c43608e0ba1edc048bccac92250e81b0accb35f94f455ec4d08c91bcd863dfc7b4d99eee29fc22c416181b79c61f56d6edaef76005f27d380ce8699eff5d87dcec3b720aed2ebe1a083164f5e48b4a125f3ba2f7b2d4fb2f37afb42fd717c7279b61e9ace316ecc6ab26895e428fb7d26bc73d4f1b86498504a90aa0e975b9fc57a74dbccc8e00c6f86be4344ed0001288729b07d5253aca9de589f0bd48988d25858b6bfeb0e63c4462c3473d519adb6ff7254fb825571c7821755ffb95cff412ce2926a3efca6b71264fc0461e0975a261cbfb37412351822a0c4a2f0bf3954a735c96b8eb170e8a29e8c290eda2d06620052b516410addb1cc187bec42ec1bea00ee0fe30afe467549a90f81b4c80093d4d099ea2941c697da9d6db7730998d14f2dce21533ab88597dd026daf5a8e1665c6f6932fea4e409c5c38baa20ce8e3f3a4369b22c71df6032b3c8dd5822c37a6bae6498d21d65903b4a1876f79002421ff6747c1f6c295b050fe3e20986ec43787c04f770a65aa5be4f600b80c17034cc9b86b8b56ce7c07af161338adbb4026ab7435d2ac69fddb993128944d9cae87d0d33dbf5066976d48a21abd94e3339192b9e572269a18d003800c55f6400b933363d66c316e383cb15753ab290fef2265545db012e625e98b137cc08dc74ea79f92f5e6e908882c4dd2fd32ac34c7cb816d48c7b628867f89ac7b30c8a7b1b882c43ce66e2f7e30b502410fac3a2b9be3507da144548c96e1f355ccdc988313f0994cd2a20eb3242b96e0a146e20072e1058910c0684183e72752e66d2993537f4e4dca2d5db732e049bb967b071f41d161824f36d6ac1aa348c2b4bd88fa37e9aaf8402c3a74d766834e2db8d4ed4f9e345bbe73831f0df19c3e0ecad15cf92d63c85765ebd57c6cc3180128b80ff0de26c927afd64a96c95aee90703847b9970558804a8aa0c9a82f7602ea8a9f5647773dba08e3767d0f5de1d95380819b20355e14ec0e6c9464f0ce99d9dd26de5b26c8ca12386c805a663151c41c2d0709feb42900bad049e1173e225b34535a82f3b2189e1d6fd5d0bc34f047bb02bdf217516c4412e48bb0530732c2d0bb5a7fe3252e4657b1b6141851acdd9570e1d4859bc39fac958224e7222c731fee4ffde038a830fa69af4410063b5ba1f632603f542918f8fa73825cd10f39173ea561c3864b7b53d975ca291cd8b480a0016684c15dff1085ae905d41e91e049c01ef970695ef40b88c0c0219d240bedf56de8f4ca6dfb0adf1a9a1c3f21f748dccc275d354bc0dc984a942b8c340e58373deda7badfa5efa793d9157fe2262d187380435428791153bd580effc482e8d22988c9186398fc9fa5e1405442a51d4d111bb0e685d2b13adc948d61fdef5ea1c2a6c74611b98de186cede6b70f514e209f27b108ff0ac5f0292dda998e5fbf753ff56510ea164cf3d7d1d4755833ed821befceb7b2535849cd1a54a911e6121f10c9a2e8c0cd6e3ee93c98bf6f5145e5264403aa6b8f2756e86fe9bd3f8d358149f72ff9199021b6a4206811e6524aa19a00eca45497a93d0137a852e678abfdc0a5c3205020630a5b894f7c035808da06f2abc94f0be1509553b8ba93fdba32c04f32fb920a82edaafc5e5b1c0cfe0ec08f31e3fcdac7a7d9ef85e9222dd66c66c377919206ea801a05287aa8b708e28b387997d421b56c324ae2e3dfc4e380aa7062ed8e7100304b118b31262a05031ad406d64383dc811369ed155b7b383ad9baaac2b3c757a05c3c52809b94bcd055b842e80c061438f9adcb65cad2149251457be3dccd9723b36fd5a4da89f4dae7f933fbc6750d35befd10692eaee79a3695e19d6a27aaeb811b14b8064663be60ceec68493bd1c3d9cbed211951e5a373f531d349dc43f39d695bfc5d833e60a156da40ac0ff421e879898f204bfb331808b8ff88c12028e7ba529a5bc5e42284ec8e1d1fa5038fd12caa35e9a3eca22e70f7ea33a0f200d355c5440ff218378343e5a3f5454838c26238e175f5578f5828d57ea7ac05bd1edbc3d673ffa7b03c885603fbc15104ce312130d1ef1ef79dd9adf3066d1ec02561152221698248b5fb45694612d2cd8ad64979e5d2038416bd1183265faeebfbfc432e72d623597062a365727591f9e8540a12ea108379eceb5ff70019bf2035fbe474eecbec42b173dd45f45fc6f9b0d330b2a2b69a9a8f4aa27551a3f63f66ff4197fe07f800e9d87f307956e403a4cf703e6af39c8fa24d4a2a06085d0bc331952b581da192f64a41321822c43f2a70ca86c279b586545a30b01b1dfe3d3148ab11066c7ae78800e7a4d32596215a62b7401b03a60b61ef2b66dfe84e194bea5dde18666b509e9e32e78f60cc988fce97581cd04b27532d416bc83a6573e3874db6eaa9009ff66018701d515e740f5e78780a8013d3498aa9c512867fb38c927d91df387f941262ef4098b8dfd613a0e241061c945de42fb45bc3ded9715925a7a3814d24cbc678644d8a410aed85c1e95867788c46e76a242dc1849060e8dfd3f91e526ae7075b5b324fb5df6d891112074d8a8a1809d02a15201750bcdd67f027ca21ea40d4183de12de10c19fce259394319949418cd5a48ae272ddfab835de1b7cdf9fde7d0e448f349f76b7196a33064ba2a5294739afa8ca9d3087461772933fa077220644161cd40786f83423d3fce0f006db7f94321cec362d3acf20ef2a764666a733aa9d6cc0bd1529b78636a022ac8805c88debffc74255f9b6a0b0575e9292d921e6a0a653d94e0ebba621c61af2756a31c78b3f13a46bcbaf7be82122a058be15ea284658e24362854169f214db428613fa45aad248af22be23f9b789ca229ddfa94eaf45b03717e2ec3266d0538fb043a93d5ca2ad041637ac593c608af19326025f0e11ad8e57af20ad9f34a0b0e22dfa8f3d18d2f8c495bd303d28be32d4f513a51bebe971820317597bede5400e0032c0ca6d681c9a2ee8d6c09d6ddc3e763999ad32631bf4120a8d614f7f55c48c8aef9b81e0b909263275158ee15239e4abed4757b2af78435ca20deab8faee0c539d6a484ca0b7c98f856c10c86220901daff0120b376d23b4c089a7358e8756247bd26d7925b346079e80f516e480cb19f4f427becf6d9172c6206aa22119b2e95484038f0864afa82fceaa03f5039ad9adc15a34741d9c5f80291684d69cc17668d24665f7195a2d414ee39401afcaf967c274bf8c7307720bfdcf40961a9a3f5b3e7aad7ddb856c1196bdf979b0a748347312b646b4c8012146e80c7e674e18388f27d306997acb377baa28743f73a9ad88f3e96d62016bb3459014f0ebd57275f9351e12ba9afe7da89a3e7ac98e60eeed49504d5a97cbf696aa3b12e906a71617260749369518f732f5051154b5af6aaee4653527a483c3aab2402db0a9af88746203a65542afec2ae033408fbca8140c784b3b3ad975e2e5c0d99da2cb2bbc5b781fc4769eacddc3b8e189bcefdde4a4cca1c64c974cba5e8568535b3d57cc612f67b612822bb77d678707576022cf974afacccc10107bd381203c2e5a96bf23fc649a37714b6ffd1f1ed9f1168ae81590e123a5738a7bad3d13f5be1033dc9015dd4b3dd24d43835f1da20d0fa9d8909060f29add572a4748d701e0bd0691a85e773d1007ed31d00797be60da30c1280ac947e100ff88dda9cef1052b7f04157d8d3df273b7caae11deca4ad7916a36546208bfa13d2936ab89698ae25b1f9083c3f38540a9768569f9e62ba3d40a674f4fd900fd3b38f6c39d19a120f9816b4f56467259af435a5148107630136b6dba15c5bb3c06061144de10648a9c7619cc30afc697631884e10ed07af08da0c517d5781104da9d4354191618606c0af8afca725d7ddaf60baef5d48bb1a09c994bfd84d84d4495d0417cdb84677d18efdbaa7c40171de7673846ceccaf6a8818f7917f345f9b86fafdd83da6e204ffdd8e094bb9b0131fd1dbbe96b6b8075f3a4213fd7bc263954f4d4ded3c3be4fe3d64070d2582d69b34465128388ebdf75123bf7fcd7e42637181f1597200b3d94e4adf0f87db77a6e75f46596bf1517512fce6019b6951a8c0a6222baae1636d6757123b72e641ff59f0880d509730611afde4d02ebe7438ac15d5e17a3e414bdd7a30fdc976df57f5fb7a656040661bfbaeb54fc1b1a49add14046781c234e0d5155303e0a98980e81f036c317b6cd7acff6e8915569eac9d3cd09d2f976b76a065da4e2b8d14927ad134bcff6993cf69995f3751ce801561ea05ce0181c5e84690e4da54771b826856634431ae2418c8698320d101f4ae25525bbe6441d7b3a9b6bb5caa1e1c7ad449fb83734df0a36c00162b40d9a96c3e3840eb8d4f5d2fbf3ea5e77b50e64ba618bcbdfbe7ecbe0453085cfc50007f7b7a66b7d10ff07c3546b2e1bea83e822159f947c60b776d7240f0f18bae2142c09e9d070e7944c3d196b756dbed55110cb47b9fd252c636666c6e2f70a0bb839211fd1769353e031ca1da615dd834ea8b2b6bd142c0a52c2981217bd036c4d4c2a15479a6e7920e94fc45652eb45ef63e8ed8db83f549faf99140ddf2f39ece75fb08c410c450b4bf241f0f6363c45090470e7328ab6c22c38cf891319ad620043188ecac326e6a54a185cbc2f157f8e82a4124442094f36bb01430a05e98411aebe2b926b019aa419788788d7d5f3895b6f877b14184c124eda6a67d4919670b8d8d9dbb5fc58d76ff00901ee7b2d55b8843be2abda7880a18e1cc374d25a416f03ab188e21f3cbeb4170be0f28cbab2dc74d0e5f285fadf7d9bd6d2490a07b5fd2f4bf8462210092fb5dd83c061901f2209177891b6386aee8e574cec33a9abe7703f1fb81d324988d73de058dbb710fdce990cf5bc23329f84138481ce76375482a71b1a2694e511c695f1c256a36f324820bd58e47436a741a8098ae2bf461f1703a2f02ba192c86a6cba3024291629c45682ec511bd49a8d3f113885cb08f89d57eafaceea9139cc638dc276b8900fe78c375fe4ecbd8e13c79de08a8da076285a630c45ce299cc0534adc87350784bc047d7200f7174c38d6193337ec4850eec9617da91a375b7577ea190c14ee3e1b86165b2a4dd7a828abed16b2ff7d5e226c2e2c7609909885fc7e3f06fe2703587633eb420069c173988b26401322e2a84d91cf073b0f75f301243f8bb587edbc774cf27931ac8a1da621ff9c154b5c1cf2d2e9669543d26a3f1155e300ebcd1218c937bcbf37098e248213a8be33c9beecf89e94be8783bb50d77d38701c3a5fdd335c3157474b3239c7fa885c6d08d92480e8132dbcca2318830ee8d4e26c291072082168da24103f9fc4680a8511890434916b0cec216aa148e65370f488d04e79cd7d40a34680ff4194e727caa4275ba02f5b9ad9299deaecd311335481a1a2ff6ac60120080ab020d86582f2c43b74720e8e8036fc0f28890feeb2b41d081aa3993ac9f754d850d5e89195eaf6bc00f6e53f64499ccf901c6f157a159b971aa98088273d0b3dc425da23a8b06d824c14e79a72d3f7f1b5a63dc7ab02a964cc64736a7b198e4323173bb4a3249fac567481ad4fb057c46281ef2e882398030211f8e6c0458a4c13ad8a8b489c83214f5061049d5e1d1b7d7a1407b125eef180ead72c2188ee50d780a9001c61b3c1eb6491737be982521b00939fda3a44945faac8b12a8432140c6a64f1a05996ca49d944c70f03dfd35578d4ac8276fdd0d00813c9d0ece57fe71966cc067a48b9ed101267fc5e7bae40f58b8a73913f1c830ddafdebc25c308b4f041262db00206904414e9337f9a15f3948a99da9097f9a65586b2fe07357d95efd1e8d6199cdc2b018226b2f4134cc5636afa7d904450612b8ed89a5d51e4fd0d6b91ae98b5058079990e21fc6c92a3458df1e30fa0e048eedf7206eac99c80509bd288d92581d73805493605efdbc23a39fd3cf141c387b026bb065cc2366d8524083b835d2a4ffdb460df768d1dc10efe66e9396a6063cf2018d1e126ab7b741ed4c1ce3c13c759e881912c1a69bf30a62c471a44bd41eff2ca57b9adeb60931a467c710f95a9d8f09e1f4aaf96cbd6a128de7ac77549ec8aae4f7892e0b83db363b71a8a05a9a2b966762adc978168e1d1edb6e3eb5006b3f17fa33e7627ea5de35ce5e97d285c5b1bd43de2716879856374b342bd6401baaf70e930c726f589fa5e9b4a094f9772cf9654ddf86bc238a27fa7a0b073cd087a39e66900f00c2a3f8b7d8c0bbbdc775b675721fafd284e76cf441edb2c558aab6b0681aff47d288f73a76ca14ddbc0700297a77b4b327c60c6bdf54bd57a380d86122512882a211aeca75d519c01adbb3eb155a3d11085e1025c02972c4bd65daf83c411e79fdca0c0d8d96de43a9abd742dc2a5f696a8e82ad507fc48bc37522b59046797cdb54a4d602c01ca3228d1e3f6a85578c3c20cf48c9b76e51fdc9eafc03434a9ea2adf393cc0c8fb7787bcfa5ad7f39e30a7fa7cb064cf32402d526def96c854801ade29097986e47cb8612502c3f83b22570b0bff1da73d432296d47c0b2a0a2021c2e58e1fdf47b0e20e33ba660e3bcb6f7d3ac32df72057027bd88a9d9e6e7d5efabebaaa12385f7f8534c7e84d207f5fa9c1dbd2af33b2362471090037114617389404f6e7d3af3470a276f27625cf10c3e5b1e91f29f0321ff3e5dd3960bc68134b637e028ab2540d57b2bf2d08dfa74e17b97f626e52ed5a8f810304e8505a1dc819d518b4451fc1beaece8707f1a63a205c5e7db0093d05817b059c856d6b630b02deb356010a282fb43fc3cb5c6df0ea46b571888a5bd3db4593c7861019aed09516dbd07f4f25ae0f8679d5299fb94e5fe5a67b1bc92c3f28db4e92adb105f61ac7bc3e473aff0142d258004d02bf0a71cb0c2e077fbc493826aef19eaca3c10b830de8490aca31e04a619596651e5c21d5a97e5e05a2cf9f4fd3102d180f348f0812368f407f98ec47d186e3da13a0382e771e37714ac59a670c35f7ab1de66317bc3f00be318191b803434f67802ce875272675c3030003f7a4aa58dc78ba21567d0f2d8d068980ac4d76683c1b6fcdb4285f71ff029abf510a634c3d2f0d0580552f4850b2cd6bea1ade1b0a5eaf5281328271bcb19e1c7cff63e1cafea1adcb278477e7ece4fe91d4512769804136538ae8247680483e053e8b7e8db8560751b020f22ab90d444a82d4a85da782bdbdb3775d005942ac701dad46ffddf02cb46023de7f1383145d48f2030846c343ef08393e783915fd6c28b1d379cdda7e9d497f18dd70f62426b74610067844ce2ae6f8b06dcdc5fd694f169100c5b8df67daf9d1c23e3dc4c078960ac092a040b84433a8d5f8bd7186f15df513fb10d5ec9cdd76da3cdcd4cd6c9f4f28828b65058b772c5fb3558cb5c41973c8de01dc7613354758c96c7e10d51c406581062a15744c2424e3bffc4c9202ae4ee5a76c77058405b430ed1208d4548cafe356371f17f540fceaada40f7999ce307b5e42ba47d9301e708f01b7a993d1fbaab0eca61441064a3f020b5d7bffbc6e4fd7925a5fbf3111e5b189209266a78a8420141dc1c149d3a0eb99620fa349420e95340357dcdaa3083c6102010a8c8a9f91510d2d1e3f63d2f6625288e10ab50e71815d80f897defc05825686d80a9bef59fc54a37d5621a6bd64f4cc80b192f8dc2786fbf88442834ef51888dcdbfc1624a37df30ddbe49d71e83bfceb02f8b3bebf43edd2211a8ef62da44bbb81047a0b6c92da23e7dd2439af37d06377fd6dd002e96aff45831b3410d890d0353fc3ecc1c731ddfe37d243e85d6c80d56b8513cb09991bb27c82f6fdac132de3f60e0d3146c6422a1b9e10a8cac35a2ba186c22e2bf3683adb10ba860f83210a14c9d991e3f665ad8db8812b139d46f8775739e61481fe2e9209333abdfa016b290d1915c7101b533ef9f019a6fdcb9a8a2b696c120da15053c60749cab3339ed133b665f13027a1df6030ee875e62b8f98a92f88b0b01a22189915acee3956e510412c4875d6ede5b38c86a442f42802fbefea3965f9a1468a864980d57fde3e9a272161d1dbc20812b5c562048c0a42fd8c023360cd2850ba386f37d43a81a2f47c96f0f8f1f23c2010e8e6306d93b06a41fe9c7990876e02ce2cdd9ecce056e5c1aec82b7f8da10a753bc6a250621c9e8d9daa7d75a1428f65f2e34dc6a5bad0b57df4cec35e1fdb3200605dc5638ad2e4fa99b0cd840904f03e0c5d472eed8da74d21c40d6922e86a6ecb321d36c01e0243aef95eb7c98071bce0ea1e97d7bd76096bb810c20dddc037c2e8722d0d3d94ca9f65e1c9d2f6654120930948a4a906d36ef304e4fdbd6e69cd68dd84d4a9b8bc9b7bea74b35b076a1dc0914296de9a4a869b58201db302d64a74221edcbb4b0cdb29dfa0eb8534039777244385c160c8009936b627cedaaaaccdffea984861e362b3ada1d3bdb89b3f1751e4991043a024fe862d61ed4cab269fc552efb2f20216e96ab3322550fb83fd53b62e889da81618e802a917366323d2b269ed18dc874453337527419b426dd3d9b5b012cfae63829498bbeb178ed75d28aeff828f39bb83d026941e94ee224b52401a150e549af93495e38433c491187c1447d1e137ec0eb51a635a7b4f41e54e900709a40875e221a8d0acececb92b72b04e23dd5230645ddb505e7aad5474c3faa6422600a22a157d84ffb7e8a08878c521e4db71f95d4441abdd65d9d86edd0f1c4997eb93d7584c7f20f1aa54968cd2d0f0f7ffd0072520072a65077deee81506b063bb6350045933fc4818694b1a285ea45ca80546513577238d149859d23192ffa2b980d6b5223b7e592fbcf71c92841928a76be6b51beaff1ac66c8ff50130243be68a648f75ff38acaec36712c60fb444789fdd3c1c495ef6490328d1a2eb570f3de8f690d5570ad222065bd9b3a0c0109431b2dc85331a2685ff5bde71f58a9ee2d0f7a378e25e93ae535eddf9311f40be72607c7664a6e84c9df45f192794c692cdbfc2d2ae3a0e59086b80906348c90a4eeeacc8ccb6660eb750a61649d00fafb64a782ad9c1de09d3ad1d2919277bb23b6c58296a20a2a9843333f9008b33747b366613c6724afb0664b1e49bd1e0b1abdd3ddee569e39606721a7f6cd8d0f63b2731f80568b0a6ec6bdfae3a3dc21ba67a6f22b73b52fbc71f4e55a83ab313835e15a41d9ba1a10f6bd2e2eae2ca65f2f2a21ef6b3ee5bd61d96afe69381aa8ab7e9cf124e9cbd72464b0c1893ac7084d0116af37e25ba36d516f49d32adbbf9fcf24f76c912a900be8c6cacb9dfd94f12befd971312c25bb5b8d556955e4ec41786c99257a5c4711d58929a2eab288d21c3194c496693cda88c20dce8b46459a8d9fde10fa791878f91db5027eb824b269c9046b58b95ea65cdb93d13602172d42f66fee6fa04d3e3e09af9842c3f2b391f4d6078fb2fd0be28a68d19f6f8f8559e6d0f314e0bb1ad23e245a184e583704de93507c23fdfcae47d163bb2e1a0749cf3d185e04de770430c99fa2ad516e0248847cba61c5e261f2a5ebf7c33ea5b35faee5a81da6b0356e46320c17c6215b292b718ccdd395fbcfcd6b5f5981eb9734b8db36df9e40c670bf88131da5a2bfb4f84629febf918928f30a073d463391d1db60cf67409761e1502ce900828bfb0250b2935fee98a6486700c62eafbfe42b45bfbfaf5568933c14883fbd8bf5beb0bd345ea6a07a66ab9414bd5622251b5d26265e3c3c07763ef9617f457d931f95e46c5bc27f3e996f2fdbbabcf1aadb0ee601782513937acfaa69554e2405dd06686e1a10048e2b3a347ac95cc4b3463d9fe6c1d5c1df2a42909525e0e2805af1b27510ddf867b2d64fd7ddf278449d7dbcd001d5f5189411000c6ab90cac2c35e96c269a12da5c5b5c0e4ab22058f293471db8693802938f5e989962e6d8b40926557f6cb0347dc246221166c0b3fba86ac66107743ac3bd9614880f35685e8f632620e6dd2dd41ab886a8d11d80cbb886c1f19b2db7145f2bc77aa23ab936a20bbcaedafb6a79cbf0c3ff095707793302110d9f9b6ca738e44be81582f9467fdbd9e0f69a356e47dee7d3236f9ffd5e6fe7d3825f6f03e4b0e8ccaffb96158771fdf3aec768abca9bc1918075b77b971d583c3081a5ffcd2defa812df40f03f2630299da511e1911a317a8ab23f0b4dc01250d24348379d1722d6d7dd004989abe880851439d01143f6b5ef540501be1520e7d22490b21a8b61e70205a7152db95e5675d635b4bdb256c036806d88daa8ebd3c3b1efbaa66f0d7063d32eab099135c939a83bcd0a6e400f6938a23d11806b51264f698098baa16df7daeb0f07a467c3dade7b6d128fe7117e92d3c2218900651fb25f37e60524d6883c1bd885d85c88077699a4893d6d52f0cdb43604689917e0f14b1d1202f20307bce583f740d3a6146ca9a1aae54099f6857de4b5d2c3feeccdbb4e1e57ff20c7ad186da312013f1bc13d0be82671bf8b07c045678081c602301157eca5e3554507466bb47ebc45fdb1afc0505458f9716049a484949350c0b7aeb30662df67b24a4e9b126c5bda6e1293c782c10528e7df74495421e0b89180a073ab58958dbb260edb19cd0e11d03eb89eecc48604afe3ff487636c964bef64b9574a684871fd273db1f01413d120428cc8ca45c036c0fafe328899ca12c86e41929274d5f31d82d6cd6cb51eab4a03c887b4a0d209f95105e6374a93f03c41c799b1264e2b7d26a34e41377f05ab1f7f22112fe518076dad5cc182fa382165ea41b80e8fcd36ed7a2ab75a75410c01dfecc1cd579cd2e10439d1bc0159ca698ac2040bd7daed6ebb26812de1de19b0e91334ae5ac265530947150d4af2fca8edfe6fe2128c3c7c360f543ce64664a43840022ecc960a4cad79ccdc1777ba13e8710ac19a765920e7a9c10fdb36fd6d75b606e664df2720591afdbd1b15f77f209d48de118b810c95c1f0676534a2b4d54e7bb6cb96c2e8447894cc3b35fe54dc3f3b4fdf3cf09ab94d47003ed6008c761cc8c2bfe23e78f79cc5c59b273b176ad891ea0771fb990fa8724a76068b412b9f1ae828626a504a22857e03a20a12c6055c2c7b00ac26f520a3f6ea77acd85badc3f6e0aee55cac93db60508447cb7ec9f6e9d51772edc836ec5a27fc0ad4c1574f8496d2d6e3b4ba87c82038582c755c2b0262ee59b2f3bb57b9232302b9864f068865c4076a9723d921aba8af5d3b8c509e35e9d025b6ac0adabaa205bb414bd7ab5a356cecd7b1ceecc0ee8b6727027be3306212582df5ab91649e5bf9c8211334ec9a520df8d58d684de5eb763f8c064252f849f280b47f2ccd150eb9418092d9d7083449a0a218a27a804085801a69c80291eebfae42bf99ce53637b110cd332a5eed75fa83421d303b9ee51bea80e3961aa50e0525162f1d408928eb2d800aa0c5e3c102a8f016d39daa482fe0b0e89f98aea91b942a5842071fc6bbe52fc6d4286385b6bc2aecfa3aa2a4b97c8d22070d682dc9d18f14d2d21ff4b7c65930149b556a23c9a7092ce35826c74835dca3fb1924e74e8875e80740b1440b6e53b27cfb5607f5d540144172d8c88c1e6eaa0e05e55748cbf73d027c2c63c80c4eb3a750298ee938c685cf45b204bb007ea7b0411e4f6c6446c890c6fcbc3778bd29d91ae8eb6eeb401a3d0dc5e5330fd5fc08db6d1d4f6853379da3f73250e53324aec51b8bb4359a9e1d91919c2ead4f517d72decdfa8e0976096d5dbe5400aa87c3fbeb82f9aa8a670fe9421dc38611ece2f22634ad2d37fed4e0e8de68219a653765f089210ec4a65ba9c56b70242cf78546d04b6d4185abbfccdbab9308200bfe7e598c933bf8a2a45a473f551beb13b246b9a7809db26a8f56f1920dafd6b06e094fc3bada76de275e9de29d702c4ca972957150581783ceb14245fadc157bd78ec692ae176191fdee5609281a076dda5e4e7b08b082e4687c83d9486b958439355c1d6441009a957e20e3ad09249f39889b265d09314720d93e7bb00eadef9af073e48bc36d52c26958b5b1e73d1b1829e6052154bd03e4c82eac0a588cbe8ebb07bd9f518f03a125d4d195ec3799aaa2ed38987232ee84f64bda477a6af1fd1861807c71463f99ee2545859e51328950263fcf78762fe43c14f420c202ce8f183ef705ff6788a5adddacf1fcf46cd9b7956e96e3ff474376567697658176e35448b561151e4711ec79413576fe7bb261aa965de0760a5c15cd6bb14e3f3dcace968217a1a6bf2084834180ccbaacb6ea49c6d835dd0ba988ef1709e4088992c8ba9e284567671a7abe60c23aaddac5c91e7ae064323808b480dbc3f203e793638800711180ce1813e3664dc27de0b0469fd26246e74bfb9e729dc47872de50ef17fb894144050d2e1fc8f350059e68410b6f2b4952f98b99d9ac9d6843282f39c934a725e684d20561f9ef61afea5a07b6629afaea078bb1b5d4e186076d90657eef3ab8ed3c12080e1c935cca439639f02c9bd17e17a8d4b54e0d2fa0053b51e17c61019baea2f9d40b76aa70b6aea9f797dbef5d388d8278b113be647afe2a4c32aba8e77c7bfdfa1bc6268d2200866e0dad289eda08437354dbd8cb36c541538371d2352bfe70ae7fa0a6b1bd88a3e87c422483fcd821827d085f78c74001d1d9ce729c171df15427c04cb2e6f08593a5b15450e9c88e9a2ebb1b4c9a636060e1115f9b4f85ef01eec8bd32c1856321f80a527f588d08648fd438b23ce4c229af531927cdbe70668b9468684ce73663730bce1a03d1853c2496faf03ffc680871e400479880e32fca6d231c8b55549be3386117dc228329d0eb13ef40afd1d1a3e7f380c1e40d606b38d455d868b0dc5af99d5006b7e02d7a78e3717aded25766a3a7a212ebdab462cdd178702c9d2fdbf00ce291f48a0d6d048ed1c6822368445f1bcf20e9cfbbe02b47ce6ed5e7d355cd370835038562e1ff841332f23db2e4613a71afc818c59439ad1172839cebfe2e625e2ec447c4bfacc303eaf63a6c2d0dc5929201aee6daca304530290056b35b3f6c7ef0d3b1795a0c8d45961ea273f7f7b1d1726649a5f3f9a84a6408e886279bf89c993888b081d0e1ad9a5096d8997fc0fe6b49fe6ef0571674452d8a9604d08aa98b07ae53a7f63ba7c6b43e27aeaebdd92d42144919d1e2c72869c2b415a35b9014c0b10740028442a5a2a5afe9f1f4d5a5932caf912a0c7b8cffffa9ec9281401f1d4c4264ef25f270ca6133ef2f5568aa27d34734500f81d4292b3f678cd94ea9c4547674173b136df34c36a49e0829dc64c5ccc82fe90340c13a4b6448fb1a70198490ad8c5d00f7df090f2e297798b45e39348de9bda4a492111c7d3f387f0e387d54578a8862de6602c93429ff891f407de6aafd8e93549d1e5b7cd1f308e02987880caf29094ca9c228de2f0fa1fe064af0a0b20b308e852b313646b7b6aa6adfcd1eeb16dffce9e05f32dec12176f98f945dba449134136afea441d0e66afbc50c7ff50edb01094b44a94118b4d5076d47db496f7efb55837d54a1a0f795045093d2fd2f861d6824c7770b32cf8c7de4db0579eb2ac3580a248d6cca094042865a2d574a895b02ba32fd86da3f811fb739ea8900a680326789640a5728a79541667f80e72017247059e8899722ccd36fa89fe4ae2fb71eecfa509feb4533927ea69a65602fbade1b89f81a9572f06aade7435a5c5fd8f1bacfa4d17bd42e45351611bf7b8de49b2b21c7e66553992268aa065844f6cd13ef6a3b1e121818eea1e4a7384cbf377c0bb72895267480657c884f73312ac5be7baa18ff54ce4cebb36883d84b35b2a8fac6a9b7d162bacac3e49db8e6c2eebf393a241bb62787353d0f2c56267f53285d1185cff2e47684df78439908af513218edc46f5f2f9106eb2e98f9dc1986ac896052f5c97c53814e4cfd24de79ef75c5a99ac15a9c3cae0904a97443e54655064333c6f90a1f0c897d4f1b3e0e44e1b58af80b893d4a88747b48ec7533a4fb7f2bd42ee2619bba419a1c5459243ef1d12b5885f8eac4a031cd187b955affe91cad74da7a5ade8934e99629f6ad4bfed482ca7fa751385005b3b23b636400cf045665f40678122e21336f33792a412ee33fa7eca01f27137baf47ccbe0cf70e791d54588b40847ff9dd59c456c95354d5089d0aa9b2ffe8fe7fc2a8485c65a5238b09d1eecccbf0abbea5d67533d6ca233b721ee4fe7442eebaa16e1b0711a3062b74e43e7cab23ac747f70fb1707e0d488dcfd2bce4d78886b6b2dae1fcd726ab01a7428232ef80e282025c02b7abdca2ae12b78df8ffc7826c2fb24ef1ff4934a20787fd74a572f0c5669740e733d6fbf23d12a264c524d86c67613ec5a6c076bc76757509476bfb492a81420fcf002d343e25c780e3d31a1b8309c8d4745c56ee64d86f70956270ddbd14c38acde1507a7399de7fe337fd592d56b043eb1cbd023f98b8d3b76996c38629871cda20b47caa42bb0f7651ea0f5bc8c6488d62b94feb470f32762b658a48ffc703de18de81d19ce93e604273cd83ae6546918c06b33a5d76a2c9af27f4d9f93507b87461f6d961cac339c9b604b71ec3b58f57d13d8bdfc50e09182a1fa8300488350d327e86bebaec54d93784efc4305abdefdce39a256d7d6cd48045e691a4f0166c13da68cfc24c040029faeddb6a6e40b3638d2bfe09fed02f98710b779c8d6460c98c6ccf401b262228da6e67ee89208f61ca4dbe56fd5f2709f0a0e8c5ca82bcd4c418b256dffeb38d6336beadadc29b3f8e63c96ab6c6bd311cfad68b0efa3a65bb39ca82ee6343755a3660b763b1e33ca3c5a1e5f5debb74f903a45cf9252449407622ee108104f13d029874df8ec4429c976c577d5003017503e843c64ad2754588ccf7ddf8919003fd0dd8e7069afe24069201a68b4e5cd788dbf815c3464f89a32660be9c92e69f1686039919e41da26c8bccda4a07949ad0ba0c33f14d612f81f45042964b22852e9e45a559216c47e66501d87cd4e0c543945b7716e5919ba6806b8e4d37c3b81edc7b99be067685f251f81089ffb47094bdf15b4dcf93a6697f7f725c28a6d4898a2ef1a605d5c3cffb0c5ce12c0bf82ec898e9bafdadce8d1116fb5a1a9c0ab5f9b45eae812b6640f06a04e9cc954b76469455ffa3b6381d3229794c961de714801fdf6d83812fe959bc78da5431eb277c6c427b7645bcce101498f1b85f075d80def0c00688777b49a39fa1cb4360fa13559ed0edda0b5d39ee58c586f7550a24cf8ca63825c407a7c101f9373d8bf5ba6dd43409e25274ac3c99910a8bb4c75e2b6eccf9b4ba72f676c8c8e9690986885663cf9cb1cc967716df6248e4fb54bcb8c7602399364dd49e4cd0cfb85100a448562c05a6c8224865672ff4e91113877ea3c282803c957acdba4c124acee7a0be49f144d77cc641613c82a866ca46283047233adb0e807e426de7004d1f2ae106bd256f9dbd71e97b262b0c942ac5bb3285f4bc142b815393eaaeef4e16b12bf881cb981da37545a7c7ccf608a0ddd5643499e3a53dfa02ccaf8ece18ca012a4f73e5a6a4712737b2f6beb4bb577472082e08f24dc09a08f6690b1ee3ff2351fe4db4cc97a9aaa1f501456a0777781a2ae5419ce6cc15a2716a89b2bbea6206575bd87f86c990447a305a44f48942ca8a84d9e04d138a360f130f02864d751540a6a1ddbdec9a01117759eb544e19f4218a3cc07204bc1f31f64620fdddad4e64181345e92487c910e097110a204130f07f91b26be0256c18387cbea0ea870aacb25260a39968ec0bab453b3ce31ac3fc4a24962f7b7e99fd7e7f7af8545a899bb075f7666883a2f1c601fbcb28873d6e8e7e9be277333826c569f7b4c8ad6e7b6b8aead73379a8c689605f27e76bcb7b5a4309b4b1c0b13e1219097cb470348bd2da5f06285c6b6da40cfde42b8a7da230c8dcdcf3900793757197595665d361680be0b27d4ba6e83addbd7788f5d0a6f1d96d3b604e8c9cca7b0d19c050c1f0bac717f1b1e97ea7df0c3f8272990e9575855a456ea661fe6b86edfb25d7df4e7118e20c874bfc11ac655e492bb3014eefef38e7d0ce6785ccb3e4c22b68db595dd1dc17a42b98372434921cc0af24349265c2fe88fdee47de415333942f6d8a73fde21631d4086c3ac7f61d3cad6bb88d4f181953e6815e87dddd7e4f8401499776afbc3672dec931071f80ed3d8874d68b8be13200c017e1425831f42bee84aa2ac2e4ff98ce457e865aca19b6de3a93f07ab5229af3e57922b1f455b6315c3ce7e20e9623d8f9f96423b2f22f51c876317362e8e29e5c2e260185e0f65dc862fe93a96814f303df803551f6166d61406f5ee8b67f78e843b3c1c833ca21563f9808e3b19cc4301658749fc13083bea4b52292c31d6dee6d65a536105184947dbca749a7c7cf4865a45fd1522c10342ff20f63d695704065367e27e1cef44dfd5a4e2fecd288c7f48d861215f41603a5efb8455270394caeb7443a62447ec4108c634b619c1aa1df515f0933292e3549d429ad5b2cd7ff9c7775c5cd9b789268e0b8a676bfab9171d3709a718d83f81ed284c7d6ddd3069be1b7b87febb845a42cbb0e595f40af5c6da32d0242507039a1b6bea5ee69182f330cf3b042ade513771fff812cc42dcd5475d493a139a6cab2581d05c899325ab9e23c987226a7aba5a1236e14ab66366762ed8a504d3ed7e1ae5c6eb0de617d019d0df260482df2d2f0e57af06b17b4122cf520bbf0ab614679e2fb091147954c930754e2c0e377f695181f6341d42e89633e4c9a0e677058eccae46f888fea125739a1bc7f52f9a8488b87a82dd611bde49ae936ea4e7f8fb9d6d1c85dadccf5be076ccd0c33ba65f2a279e966aa6cdb824a307402c2b115517a8c19bed20f64e2506d4cdda3f434aa3dee5ec6a5002a3c05e108ef0daf006cbaf20a89ebda5e2985a105f8548403f48703c00a469f8b53408ba89fce3329258462750606395130693e293cbaa410bf857d3afdb5e87a6f42c9a3cd117cd5f51af93f5e99167ca8ed565fab085ec86967c3f49e16e91426aa2f980a6a7a0e10b80e21d981877d2c89b0f69c8a65035145dc3e4d9683621d996a14e5b1779dab3c5f364bd8a5dc2697409251dab3f74ea8e8ef80822819c94779eed34576a85708d759fd9e7a92170d750644f896e2c8bc7545b55dacf96119c5c782001681585bc27740051c90c2d6b5d1c2e73699a3da73605c2708e15748acfe010b0f84be53548c2ed6b3f5e8e64de4fc913522a64c1e3a59ef1fa054eeab7f6fbabf9e9f33022732f54b7ceeb0763d74a4f971d4f01fd3cefedc4953a8547f6d0f6db762d135b586c164e599da5066ad58bea5a1db317ec8464c9d5472d8826d2e64ed8b12e92e5dbc191c93dbf2a89204f56c80aaadfffc619e0ddec6ec810174e6e999063ba8462465a0696591a4a229a0f556af08be065dc3dfba4810d05687810c3e36e1e0e7220142485bfdfc66104fa69401249748568da8e095a55837e7983af48f330bd3234d4f6bc0e2c006aed4d3945bb95ca9ee02d70272882ff4bb0e4b33860385a224e24f7c3e694f924387084e1916bf71ce4a68126c45649772667369584c046dddd1aaafd484340dc950a993b4526380d1f1119b29e3187675f35c3bfc5eb82cd71669be895a6ea178065807d0213f89d73462765bd081482988407966adfcc7b5cd14c90738b43efac73f85bc9fb83b8573b8eecea31f4bca2e2cb25f458278b0017d562a6473a2b971641ece0a7dd1fef01b78d65c7ab59b2e98d5775bc9a534d95e69e1544a8c5a65df219659b6de3032a06d24c1ee27b94ca6efd6ef537237877691aab2d5a10898aea84bc5df2daea4d30a7040a51305aebfedadf3711115f25ea144dc4bbdd90577c4bf548aaf291126d412d3d0ec0ad1ca821026129e1a60285dd051f5e80bbc6440c90076d05b079eb4437bcc32f79c6cc9d80b9a0a2b4d96b5058f8ea90a4cbe6a5f6dea106653a77b39da22d7827ad74fa1fc37766c391742c3fc85a408cd9787628a6e6cae56163dc962eda5018f8f2eb86c51ddd149974e5994f43f546e8d1e39f850c4584adadd0e87b14873928d4fc32d6a84b801273d70e5b5b5a7d5de91caea475a5fc0e3ca960c9da2f61b6cfb81befa09ca33eda53bebd90ed793f02db7c967488355dc8ba732d1c50a378aedd7394586eac7dad21bbf5eee154c68b2f8c4d2ea68dcf90951062b199bcd42c78b42a613fd0a9d81497acd1b8a762c8b61dbeb9835866e43e4c6d5058d1105f8a203110c41e11d1fcfb3b816e3b96ca96cd5115fa843c84ed2885ce1afaf8550dba857ed388e993b16a72dada4fda8378e4687848330714817d242b2e365aa455278de584a3672e28a2d5e0f10e6b8a78cb785a1d6fad823c948dca8fac827ee7b6dc2e2694cb189ff17b6933a85a0f283ebe82b4dc19ed5b6b5f55588af4a6be2de02da199a903ba143beff8b0f656a82e2199d84ad3e6387e6d9d3054dd927d622e2757984a618b8c7575e25e83b5f352d94e02f8b75fc2f752c13b7208ba407a5637ba0b48220279033210a4430201de45d0e8f1a427fd2ccee5030fadb3802d8143de85e2f2092c679c01fbe80596d61d3542fcac866259cead2774dd1695e53e2e18f8dca68514e0d2f679af97b0f1e87bcbb0ffdeadb1e1e798b06d8d6f3eeab6c6e0fc933b17f2d2dfd442c681a1057207ca53109cdd541dd3d7ade73a56e3b1c94ffac3d97e0a40d9c890946f0cac7c7b5571fab42620b751310ce4a34f21b8357c2098a266ac29779a1c28ccb008e86b0a0db3f80816a13ddd7ef69df4bef3609ffd0ddc26fc21acb51bce1c3a0a5b97f27c5102f54ac23430bdfcbaf384cda772026037f029f70571672732ed4129bc4b851d184195769fa269c636aa00ebdbc94ca91892fd3e7d2354af3663360118ed331d9be7c2853cbb14cd9cdcfcb53daf3fc9166d0256dc29b585236058ca489cb7e654c35e841317941cc62c0a9d0799ebe335909293818b49a1b8c8fea948fe598f334c682cb67b61171a928cc711a11feeea57d5cd1cc2d67bcacb3af110237b6395140c7e2727348e6a8b98f6cd8750523f7036be21ae6722a72861895607eb10bfd5aa9426a396f3ab7b6a533cdcb58b52d7d120e96516b42c0ac610d7a455cfa29c5afab1df24190c354eb920f5f3ca67e5ec2522751dc9cf7ccf4c7d769554d41ddb82fc8bf19f77004d793f40c79054a1b8ffb74031ddacde94205eda94c81af42ae8a5d6b9b875b06d76e94fe0bd50e3086cb269709cb072e5b0de382cf7b56b0d1e0b0f176a8320545bec93fa504109e858423b1f5678ec318c7f4d98f55c6689ba498cb9d91edbab960b55f167958644a917da2fb53267255b9748cdc215f5bdfd5f71c055bcc0faa0e289de3f891e2511c5e4afcd28b0d3f78efe203de5d7e2ed370eccc8e46df98c1590b0649140db0d8105e143e5ae1bca9e2481b8272a21ae8380a41768d28ee584752d05c723a6822920096c2c12ac47f7cfc1fdd12150a8919d63c6cc2346f262598fbed659ae2a715f39317d175158a97405ac2bfd0f2ef74f31cacbcf7d2389480b8634921794bc16704cf10c2437acb18821eb301866b0e0a2871d24875a4202957f1d4708995c50581764ea0d58946248604a8935e3289c84da68bfc7bd771afb2c90ad983585d9634fa8605f75c0ccb048b6623b992a3ae691f4f84bcd1e328e353668e0d3ee828a3f44c05411b9f5608dbd090591c8803cfba5d4045559029b78380fe13779a6068cebe0d34c456e667fb846838495c8ed8576f14c750aa650551d36086a5a0e7bf2ce6075ddcbb854afbe56762f3a492b6f6e86134a04432695dc30f74862e54dd9ee84187a0c16321516a01b9b45576a3b1831c59dfa26896521c6621b36f80ead22ce5f8dec147e4d719b61beac162eae9f3a4e602b01f08e56007814fc702e0e39f3d203475f046e6d478cb1a640b9c8bc6a1c43ca4c697942a952e014a9f877c01ba2be0329ae880ec1889d90ee13c28309746549ec97799befde0a82bd8a93872790b814254ca34148d346e8f208915a01ee3d98dc79940e0d934014205d958dc02db48aa0c703e6314d5a60c9b40f1bfcc28df0fa895e4435f136fd0db725f4b48bcfec9278a095dd09462d109a550ae83b3b1f1d2c1a85daf02239c13635f427ce691142fdce6cd1062a3de16398cbb09ba15a51b570bb85ab9d20cdc967f061cdd179ff69194fc73a83294ddf04bc9588480427cf27a14256622389509b403864e17800c2af6fa0049b4e70cd6df773ee98b98a5344703902ed75509a2eb4c2ed43d13924e4415f5ddf64b4e6abb6113920287243a3b9d4412967428f4eb657970cbbffd717dc7cf581b3bb1be8db5f5fca8973a4854ac231d6eae7bfca3122327412021d3b65b3b9e37f8744f316bc9af8196bdc92cea2f63d87badd710e1f85f52ba22b49c62edcc96b22a85bcc2afa7aafc247affafd38a1fd8c99d579f8d64526d590c983d229ab321eb93eb0133b5d586cf920a8084c3f972bfd20eaa847d7060fd49f82c225425a417718a88a64d80cb273a6aa6dbfaae853e1802a663324250d504d5b0b50405271451958b1caaaa62a1a5be46933aaa834c5255e176d14752a7a3a99a0b3c7ef46f4ddd54d02716966b9d9f0e237f40ad9f4a8fe56ea364992d508cf89af0934c9ca7702e39f6738c491c0e6a6a55097683d0ed88fb589d2c73c9edd6c1df1012c021e4d0c4e4cab0de326fa65c522bf8d20b56c6f9ae61222cd4e1a97b1760881a268c32f0c96678be07441de3f0a7a84b014de0fce0d5aaa93ac18c2470aa88228447dc646a80c4edd377a5f0f1e80eec81b2721c3c8326c8d6bd75f690d44aabfdc3594c138a10a0a8be4a1fe95482bc673ee3509dc9821843be913e93c31b5ac05156408bf5addeb7f31033204d09cda88507c57df0f82c3f5cf9202ec4140acc6c86893a8a7962b928696fd20848968da5e171b3cc856f7a5adfef42f026f0b0fdde6d37b10c6111409553bbda9d771c5f012d508e56adb0bcd8d804d02ee82c6853ac0cf610ab0e9c25718f320d8f0fb10f9a06eb34ff9b280326939277003d2fde7c31e049b58cf3f08c2e3843ae914e08310d54bfa5d1a08dd7fc1c4c6a066f8f5af010d4f0341ae943f617819b0c28381d03eb0c07e1acc01a8150682cc17aa5611ec2244ed7f20ffeb22163b402dd0fe84c74a108109ee1af1830920380f21dcafc8d4e0c74c6d60a1d620e94cccf19ce00fee50bc36fcc0637b00a9ee5f310460c8dadb0b98c13cf0252f60803d504ea80f9e3f3a2c197e5a0466d1ec03563da74fb4050a7e7037bc490196368c85cfa80ff3198697fbb1811fd42c39cd6613d87cf80113f62566cbed41696c2f1f6b7409705249153d88a3b89926975580f986b9eaef057a902609d61874f9f00008ef3ee94406843588732243627cdf441cff984971d33b1852073edb1808ead5d356d8868fa7a80337b723d4ce506e1db4098b98f6379519330487943a15d4b992ed152873acc0f93b57ee1c94751fbcde7ec07a26d01ee2d1375bb31a1404cbf69e4a98788c66a802a0c915840ca5d0bb89a1dd77299e69378fa80893789693a482c00629144c3a68dca8235210e81acfbafb4089b10b7899a9765cc002f285ef2ab6dc8e892b938260de2a5cb2aa439ca8316dc56d30872aa9706d7e5e69cd84c00a5ed31f36f2d02aa02a9091cefc85c4bf3c4f60ad0fe5a618b92490deedfee968db8f6a1d49369d10dc993ef41db627bacdc94227c2ef3e7cbc5e63f7f7ebe00319c28e850b648f2ab31917a1a0308c448aa4254879057316d23438ec9089348c20eb79a177d6b2e3763d67e2e8cdde1fba2b1ed7457f1e15cc0e8b04b31cfa78c737538bfc971ded27ae4ec166ac5ce4698ce7778ef83e40495ab772385c1deacaedda7cba901b28ef0520a1f813e621b62946488365a65f56f8462c142e5c0628b176ed3bc585a495898c1abbdb8b92648ff1de66e5105b50b80f144c771510f9a85f324c9374696c1ac332861603b2be99dc62343ca612d7515f3018b7e0e0519c1a3e34e8a5be604f358e5b5b9c6140dc70ae99ebef62f98fc8aed6f5efda54eed7e0962b021beb94070cf1a5bdc538c6e4ac6fde62114a4983ed49e1b33b09f3f92be5c6d3fccc02a509b4fada76d6e9d24408212bb1bfca2e3331fc84340a40a1d6f10726dde1004a85f40e0219621f058882d4f706ff0066c6782935d9dbfd4558f2d5270b1db36aae4c29d2f5804858c1a922a0f084be0f247fe173e9d4da7b7d5923ebc5a36e82a3af0602c1a1a8092c8d2327d15d8a038b099b8b7d4a70faa6dde11d21e25803fe256e72af77b06170a350ab5597571a3158cf330313a4498af9b777856cf0ba60ce5c1f915651aa16c5b81fd19ffd409d987b786a4d4987d812cb126c392cd96c2c1b6a0a02f5a043200eee986c555c8fee9a91e3af95c34008d60119e81acf5011f364cf7c8a28de10fe2ce07027ffb33b39a74c2400bb4736eb7812a6fc3dbfbd42ea416378f351b4f54223e29ba372047cba0821afda7d49a85a8d233205a8b1d5c6c98ac06256e43b5de8d496fcb71a4e091d2115ced9460de3d4ba554238d9a3458fc198ade45740baa5984bf852113ed4d7f88b43ae415a55742535cffa90977ed81d3be08d1c61231372e759d30a9a919fc6e0d90ec36399bc94d490250e7bcc0f3e8c9c2f19d15461bef1f8e24ead734a9b8f30511ec3284d488038911d88a39ee8100173d4625b7aff810b558a43ee111c6547257e74265953fed9eb991e99373aee18c71b5c4f55b64960253864b7bb815f3ddee91824b7b47812e7476f640ea7f7e62bae50f33033c13bcc939073b4bcb2a3d77146a9798d1dff66c8eb84c826b544072ae17af5b758943ab844fd30ae5497bb5c2393618c0a9ab703e416e7971acd30788105f48581d935d342fec5c8b21a05dbb8d1d06934ecc182725597cbfe76ceab87dba6f9857c5976d65ade8b3452efa31a9cb665c6d4fe02be4146d46199c716881fdb48e3d68e97884db9352d38a7148e6c3eda59b4fa6ad9ac2de09fa8ce246e091bf856311fba8017e3711f711679e4f8294456892e3d202ea30d8e29344b3be25c251ed9677fec365b216dd357d1da63604fe2e479256364acf2bb183878658b2df08bfa9666534954f083abd6b42aaa00da58c4f7d6d7e26a5764e32e0819ebcb9114366c3bdd15dddfafb683a80f4694e5dcda1048c52500376eee4b4472aec0a7cfc237c54340310a3260f0c04e0ac304064daeed417e876b4347b9a290572e23683d0530323b11174ae8a2d76a49f05a6ca8395778b6b81318235ea26aa23c4cc8975a41c69a44c52b84eaa8b9d91f918d398f0f5e33f0051f51f600b3e50c5908b9c625685552643b9241b1483b8abc9b95f4a0378c64a55b84c8dc3beaa2a11ea5e74e7a767fa57f720bc1d5f287026a7b59c69d95d871241095c3730d0e0b077dd42c2a918fb534c445edc8a06a2d421ff1af7c82263223adea3fd1542cf62d5a3c0a0a20f3772db7e3699570eaffc14652c08914b87d008760eae0f4260a9d5335cd5ca4d8cccd7472cd17cc59d340111087774f578a34258bb1b41ce912c4166e383e236bd3d8520cbcde4f2d0dcc351bd3b23ee5b409719a653f5cfc0d1f17755bc927dd33db9c5b095d068fa444e3cbd25e86018db4bfcb97e3fae3665b7f21a2762454d96c34f8f7436974cdb43fe0b95148b93868d362a33c7422dc584560e31c91a0e2088f3733555e4c952b7291c31fe10063acada0ca348edda8337c61a962c7f9e3d13176ef9a30c6e41b7ebd4c77d8dbb20537d803b71695f3133d41798ee52f5daba7785946f4d0f3e270e1190b58ae6f86bcce58c1d05a93bb6969fb3d4ccc157bc404c707fa6822ebb081857a1a27e3df2ed30eb7ea3b90d3ce807d6cdb10924caa43c74ae4620b578b50607e73484350a86ce7045f42ffc01d1710298ed8b33e2850e62dbde3ce049f182396820701423ec4fca40b43a010b23ebe15a36198b013fdf1b699e39af004e16099cfe42ad49db4bc543e8002333af9010d3e0e813a2c33728379414ac0a5b48d9c66aa578c3abc9c49dfc5e42e0488cf9ad57147d852c5c388a35542f3af832ccba884c7187f3cdac866cd5223fc5504da08334c90ecfbeaf0dc2181f4ac740a856c2fe899efac6ae5cafce679ca30392bf58864d85941afa53caec00719fcf4b2e7189ceee86ba9514af6378a2ed344b024425a27c306bac7ea00d26c210b24361524f3c80c70ae4f61b85dbc8fcf68cadc225f7d2d79ce6cf8804997d3e686131efafd72a6b72e8778f7da78322b2fc4213ce6af25f94fe6c6ae128176cd782815f4b9ac8f9910cffde8a7ccaa05706abe97e1a2f61bfe538c3874a93f2f4e87af68d31559d6c7eee5e893b2851df21cb9352be91b180722cd2a18152408a77dc5daa759f703b8222bbec96325575a5bebbf4d2a385567f6196e74c89148daec0502738123bbb1bbb745449cca793fd27cba302ed8d5ecb60e1a47d6ad5b25068ad6b424e99790457498a30db60ece46c2482a779faa7556d5aae5500ae1b2eca2a916b6d1e96cf8836db37388ab9e0237c1f9c00fda03130af9b4e987f768fac75ebe1fa8c0a12e577573b91f2dca9d92f23733518f69348f4591830a2a218fee616122a8fb79a2903b8cba0b9a8d63282a4e1aa5a16c5b9e4bc7cbdd82b4ac7ee2eff952cf41112ba9308000fd5479b4dc20638395ad96b99d258a88ad2f97432d5c07ce06820ac59b5acfed4a382ad879240fc1251817ba3c97fc3ccb9133d0bb03b59924dc52c2e47780f8e805e92aa456c2181aa09cfb5d2e476c32fc6e71e1bc9ae18ed2666f09eb6baa61e90ddb0b15f72c043c80c9b842bc46af664ae20822daba8435bec8cbb34a187745088378a1c6f15192e0a80f8e48443247e28da48056c2fcc2baf5923952e6a13c39e56afae2a253afce0b4540800b883fa2a83773f8fdf353e3aa8cfa842e5bc266f16bd0337f858569a5ff6346d288c1d09225ecd812b845401895053895f13d779536589c15932fe7e69398e4f3dbf46c831d104644147ef3862c7aabc955d89fa942b42eb3bec2194a5285dc7806a37615e90af92367b073a869a03ab40070cf56c18de8a90d547c58fe501dfd7a3a6828bb37c19601ea95b310c3a723290382cd70ea96719f1495506f6db615a413f6003462f7434c1169af758bd10ddb9d7da9372515a60a6290cf342952afe9e7be50190be0a3570c60acc33c61e01e0b27f732598751fd1961b406bb22918c85bfe481905b185b81be1096cfbeb42e612d6c93194eff39f76af228675cba0f3be4c8a6edba96fb2942cc59d6fdd8d017194ac9df7489c764130be35e86ba76631f5ad93dce56ee8e9c89e35648616cacaa5ee36acb2a464cab5fd8ea5061c50b0fe3064ca05f9c91c6e68c2824787b40ffd5c025940d8beb6fd5efdf4cea2e7ffd7d88cc923b820e719927d1d5b7520c10c6590980962a9a4ee3196e0f23af81e61552f9fa5502492f3b2961925f78bc21b124f010c478b93cf88707f0bd922d83d3d84126225b530c76c3387ec47cfd8ba50f0fa8892e5588ea2317bfd4e206d9badbb1343c5880447083a2ec5c1270fdc6fe910f3707eb8e1d2f6c7c32521ce5eb72149089ee9c5de8c5caffccf3658170040d10d231cc08b6462fd5ec925b2faf96e578f92e48efa1899b7bdbe52ed265a32ed5b4919b2f798d04fd41132e00a46ceea5bb720f9bedcab144e753391616e3afc1bd2eb3819495fcc4d2dc9b5842f21dd711b80a4147ee7b4e12b70938170b93deb4fb5cd068b67c005301081cf90cad42f29a1795f56673b52cfa0e8d4e4f790014be7a9009e874fe2d187e51fc4736e142b831059f813a7323e664a2c0803ae122294f6559ed98b0b75338d12aaf7f5597b10fb7f159959a30cbcecd08c2ee9784e2c184f68559f619c28e460c49f3cb63c4d0f627087bdeb58c889fc800fd05b77dd2dcf50cd65a59d7d83783233e04e0025a347ce273c8e10a62994c8bf9e362e0870dbc32d0430fb21e1710e6c39efdbd3d1b185110e57ab305fc1db0793f252a381b273c7154c541d6c1a38bde072a8b9c58f1dee9afbd08f99534ae32c7c84db144faf80b01a54114bf454c912d200a17c405d989c35e134dc36c29d60890a2aaa36c601a9dcff054195bdad2a05aa81c5f230c1158696919e01da10576fc8d7222520833171a4afd366cc1a0083ef8c3866e2ed1c9215cc54af4790d0d648958b87314211a5041b13593c0f12aa5096830e00681a6f2053dc03d6fdc86ed27534ac9fb6fc889737d104aa293f618a219d5c789b1641f471306af0fb62b417526f3c6dd584a76ccac8116c06883b53e1ba3156de1dbc629cdeabd2e6cb04052f0da1888b29fdb4a20e8cba0a400de4789cc3b063de7a870043c6bbe27cabc56e87ad89c07d3f07c9aa0db3e9f1b18a0b5e1e7c3b18891ba6b44867499466baf9bd1f89ce2e8e98d948ce8911b6cb80530faaee0f1a9cac1829c6866a103a27bb0f6b586424047dbbfb37e57b3b43cc7ba2e1088880f51df04ca440bc79be2825a02e434499ebc4643f6d0c758bd8f7856ac7fa577b5fa12a13ed31cf11fe6c4e0338688cb877748dcb5ca59dbe4447e05c9f5f0dcd1d1e0376dbd61d59bf0f11d4d9cd2e76646610718339a6eebfc0923033319674b582df42151f50dca19b4cf48bbc2cd4150f1338e53b8e552cfe7b71a681f9227af90c5de48f940b55b37ea79a9e9f22547563228117a615a453951544b853e488f65c0198292caeaae4df8f5a2d1f61fb094172bb5facd0875a56df1b333375f1e6e3c77ebaecaa51359f6540ae6a1179cbed494a22b26fc6f2c7523c195868edf5fb3aa8aca1f1b4f81ef0b4d64da94e32545191c2618ccf2654e63d06906eb74163bbd71a707f174254f337468b0fbc9da15a8d5c5751abaf1b7bf67217cfe19d5b2588204b564e85bef4415f41c1ea88da677980599aaaf317156b95184c2b1065d34aadc28aaef9d32e4fbe0f51eb2f0fefb156c316a9469addc6eaefc9f2e453babc556e301ffa98fdff84e07fe773dd78c3c17883cf216b274f92134d21d0e943d0da1584b7a8d484c2bfdff07d07eb13b41c1beef6c256f8f3d93d27e8b72f5de3e1913aff5a85ad60b19ed0dc655f946e22d0aeade72f22dd6548ded847d609684bf8714d870b6545967e8569bc57008438b54db0919c0e1a3bac79723e00ad0b864a4390607010ade342190e56ae6f83fd351b42ff64292d7490d02db92039ef0d11ac72f450ffd55999b7fe9f86e3a98351b02c5de617e547bab107b513d74a986022d34733c3822735594cff29ce60a60117a1466620853bec23a581763f8c5c82b44218176e3ba81f93613feea6a952691d2ed8cd91cfd95badcdb27db80c1479c96f91312671283c450cc6b1fa935d2f85e8ff71009e0675e58f51144617751de57130169da4aaa0d0de1af7108f4effbc897aa195671f7d9ba2aa039fe810a989aef093847bd3c74a8c65a39351bda0f55ea756405f7843bba49eef9b8a406045551079b73e56c2f36dadf124f18b351d3aa7c22d119924c9eefacdaec620abb365fb30d55caec17d4bffff655d8afd6d42b62f7a05ee6b4fe23d4c0da2a87eb9fae24905b39b01b3e95888a7323e2adfbb159cc11feb6c0f1b79b32aa2a56e06286249a8d390200b13ff8fc65e1b48d301c3abf27f8458b8e8339dd131035e16a2a7ec141e41aa0b4372fd1ea75b61b4399691ddc03aefca62c91eda8e4aef8f1f0f32655d976989d7614de526126267afc56d53450bf84c680cd3fc7fa57890f37f039ea68f678ecd7dfe7dbe9339da3decf592df272fcba72412d88ee4882f3c588f214198a1165a010b3691fb8068518193e35f6b1d68b82701ccef5bd71bccbd118099c2fae0c5133eee9a266accb6f88eafcea93a66cd21515a7b1e18f63cd2c03772abc1640d1f041e0ed008e37c0ab8e8559af1f018f9dfee674815cf3a1d41523762e8f375ad5b7e12a2356417f97757403125ab33e64318e8341cd863879f0e539ad72a4b564c049b8eb102ffa2cc85e9b47e56a66b5c0c2ae8519aff21490bc41613313a3b19f9d5cb4707ac4e969d6589da5b77474de383aeeed47593c28eb620ecd107f0b5767de3169e3a19b1ea5b2f74360fa20f5602611fc63f18584593b415544a7c0000323cfdbe17fb6011429be60ede3dc00ba0c28ead516f9f8b2c77268403a8dcc7a5f0fb5d5aa84c81a16daffa0629efddf17b6c2e4f7318fd2dccc4ef35ef9a2c31119792feb14fa3afda65d220dac663650e969b3c8a707d4cdfcbca6c2e92f32fb1f32c035ff41741b5e26fa227c3078514bab072cd2abb75fefbfecd73476e10b2a5d78fe740b3313d7afa1a0c07720876812c106eed1d6392ecb08fce2e87b3bdca766ce41cf927b038042cdbe7b0aadd6c2dde036bb0f88889c01006c457ea070052e9e6ebf744a507f9d04e5a9553184bcc78213845dd9b3148572aff50b3317135fd4485d8c7eddd0b2966e37a4275f00da34b88683f1ff0c495da739cd333da53e0e4717aa68522c9bdf6bbddfcbdcbe88b63e421580559037754c8dfbc3d7fc84dfb628bb49d54c062ae628d1f00cb0c6ca70e4f1acb73cf25ad6e46bdd5ac4207da425d207cf16f2609769c8619a60e4b126f8e3b2a7f02bd6007bb4a43f1fa71261a80003bda2b64c2a4558b29b00a171f343a2dcbf315f00fcfe3b4886c1a73bcb719d99f2e144c60e834735e6d1e06b7e183c468790984f44725793a85054a5579043365d7a04798a0f0b12cdd0daebe66e26db2f17e8cfc71caa9312c9c4cb9045e610fc2eb20cf632d5f0513d5479d2092594162195da842f65b66961e268d18171a1da274fd776c32272f517830b6e04e31a0662ddfecf66017d103455cf20bea8fb66f95cfe5f1f67cf89b5b99d890fcc76824c31b96130dda478b4dc0d33023e7d512430209d8571792495d3e847c2d3711a0a3e1c845be7b5281b32ef66345c600cb17c99858d5f1c6f1ecebccd32efb2eb5f08b586ccbbeb535fb1c9e3023031fde82d6c9e802cce15c09c134ebd9511d9761b88c59c53491478fbf81d3e0914c158f2eb60e6482aef9df095f4ef721121c6934016dc85c7e1daad30d8f97dbb70df947ee2ddb01631cab97076557d24b29debae03f372f52047bd3bdb5d8f371ab721ebc4a4f982ef21efff3a818606f8c7352a020b23ea4ef3964bb5d558460069480757d06109cff3554b2448469c5a02693b2885c981936f41df4c9a5a1b15fa4b0712688b122ef7f4c8ebbba731b95e80798ff803ba4e707b2624fc14b28da53df9952782183b18c544829361850c46b5ddcd4974b2485105ca381f3dbc8e3d6aa6f3fb0b23db07c1152061af8bf3aa25ebc3ddd061d9311538d4dc01f4ead8d89ca8308067906f9bee197910d3f954a8dc8a0c41bbd6b07ad819f7cce4b8415db1ff0ade9764f15db1250ee5031d34848da1f3bc6d533b9275a0b50f7687f5c0b68588b0661f87c53f535d7bcc4b924fe4d4e490f6fb43fb8add23f85350157a73efed3e39c4465fb56cd24d5b42d63d8f5c5928000fe9834b94319b2decd5c6258b2bc342a81f51489c0e91ae46016bcdbb2b257ee635499b63bd48e0c1bdb2465a97c7bd37621c595536ee64308316741945f90a55c4bd8f3e518ab6aca572341c3462a0c59462e87e903999d0754b6618473011441e343128736162daefeebe48533891d314770aed5bbd38d4404b1163890e7229f52fd26cc65186bb5bb8c2e64929122308b57e400b23ed7ad142dcd2a005454ee5e3e9b82fa0dac74a9c83f9f948c49e28e47e6d1ac5e8dff1f036db8edeb9ddca7a240873b3073f4cc26b83f61deb29c90d052f123393e62621630380c59799742d97e2c5c34c29b41d904223b957925c98add7e903191fa3c6f88b377e67ddd7affd83af5cc20ebc7496e8d3f0150c33c885296e988e1a072bcd047fb186c9de9a2dbc23e35b9b7bbeaf9a32b04203e9aaa96f85b4ffa671b7a705a176c2348d565c4e8531c60071abcce50056e76de162028ea04640f4565fe79c648bb119afc2f4452132b38ad282ef86323de18c4787fba594e9429e2b94307cb42d3df0c235697a5e9067890950793ce33250ca577dc61abfd02fd99e64dd95e1ab3220e95fdc00a33ec587c31d92e8f4d0c34b6ebff204c08840898867953aca83d2e61250bb96baced01a1990fd2e7bbcc6a78c42ff6b777fa3a67398f00f7b4b50f0635ed9799d0b7e71a85ac8fbfbff8f0e853a7ac74d92156398be562cc72e759897c8f57f7f12ea7e1e515798dd384a6f58317db4d1f7242dd672b901e07edf62da4a260f814514211e602285709d1411e3dd5941216816ad63136b4ab23310f47f07d22d513561700e6c043f063d1249864a097c87a59454d6ee2e106f6d03510f86379e5db29b0b77d57a7e1833422aee5b38d26044e929dc7a6a4e5495d9c7b4a4b5a4a93b76c629f5025ead632fefc4440fc010e1cf54cc9deebc56c37403421d2ab9b6e3ed1023c74dd3138d6548729ce61293a020031083b781bdbf57f23b621a00c445e89a32afc956cd4561138965f24f60441c6fe79dffac2da82c229934fce668435027fb88b7a23bbde830ca6c3f79b4ae88acd87dd7c399d9c8fe09f1b89bedf385a4da2d4af0e4a930a75d8d2a6172815f7a8b09c2ba52620cc01a1162a2ff7f655a702a14fe0e4f72737c04e8b4f9ce59830c475abaa18e2707609bc4dddbfc9a311e542a078c651b970172aa7e240932490292d8d03cb9fe38d469169c38206bb4388b508a711755ef7547292d66d92b83a91d5f79bcf048653c21c354a745088afaf10fc92b6357661de1063655aa94e004b4a43a18dc1380c718747b842460c204b0775f4165bc6a82bdf75680c7e1792ed08eb2a08d76b16c574d997b0ba9e06cfd0c0615845ddffe38bf23989b8af2690bee3bb33df50a7d5596db69b242837aa0e8050ea8db58a5a331222828a505471d35fd6bb61c2522285b317b9b8a4801344e626009d2d46b2f9fcaed3ab9478d84966d06b78014025f8fe3485f920a750d6ae5863b4f046e608926beaf32a88a2c7d884ef7b4afb99265e328007248a1d4ba2486f402098032389aadc982542b70adb7c490d20d32a23e8a0c44981ba04502f32881aaffaac6446c84e41c023f7f292aaf2021ef04064f760df4ccae1a0ba3007da91890c38aa5f042539c8f9d31fa806817e9831bb4baa6cc9de0cf7c65c7845fbaa89e53a4cc95ca55590fe07fff9b126634cd428d92e1c281ba0dede788e29cb0f4aadd6d3e6a3bd65289b292bf48e9f827403258e204a1ee1bbb2dc33696d18c22584b820d6eaf0e6a5d1cff97757c4825e38329cb6004e2c3ee5850d1c3c7b80b1fee5ac60d358e1be8f63314e0c3953fce1bf4df83c783121a40aa78695f8490246583fd811eb4e09b6c6a3bc5877e91637bd03ebf42cea8d1304a13667ea6269c94f4c64817ac17f9cc94887ec8bcecc43234ddc19309d556478ea690391a70e01e146c74b6442177d1a82a4fc32d77b35684b505b5733bbd67a39213053fa0e442253a811657cc6853c86fcc2a5bb8457744a14cbc640b8b6b58d602188d1b0acf89af82b86c1ea0fffe793c43b73875065ab8545a8b4828c0ecf42abc03e4a38ddfe76eda26e02692db3d57d76b24c23331942ab71265a4e314874605c5ffd1661ab1b39bc868bce7de59b23c9bea1b28a7bdebce431384c06d8085e0bb8b3bcd5c654f29ead878bd48f1e1e995bbcf2a245c82db6b59386ffc63c46fffe75ec079a59685316f6b18a1d2e2dd365b7d43cd7ee5bc8d3370a96ee5e53105c82f960c92670a2ce5f653472ce8d6fc01db7800ab320a854071f25ea2b5648c2d5b11f5969dd60d170a55d0dba1851f384e5e91c73cf09a9fae03cd4be3df1b6688e428c19fbcaa177d0ce0c1805a8753290833e6a826c71fd47cfefebae398e81768d9a4202f272d25a08284cda2e1a33b13c1ea05aa550ed929cd03f5624c65699785b462c641b25ab32a5d8cb07e6c1a25ba181f0be4d8d85233c7c6feb8b9352edba58b01ddc8fa667c319187225c410e52ef4be0ed5c8a03a8159c6ebf3e42e650343f04ffabb33865a126293197f8b0f93b435c8774e2b536aa4eaf06ab1ce322d5ab96c8ce1547d4a0075d36afa81616553cdc860275be46af824ddaa7505c4e6bf202d112771be78cfc6e52e155a75529dcb86cc003a87e41f16fe5962a9c68b7872cf8cfb4351fe192d447686db08c3dafe0b761b7d012e9a33fadc7b1ce7b4efce963165c17feb0832dd7f045dc925c5748a8383476c261e098a64b2e961700039b54b1cb93e75040e2dd5893134d4151357fba4a23af12e565f1247b30dc5eee09a1d6984df2e0220c9df5a5e4009128a767cace6a710938e43ee92cac9cd0c3465d4148c8090baef45123e80c5cec82e73fe8fe54fcb715282869394f663f6e750072693375c50c1ecea089c18952deadc081cb0c873cd0ab125a54867e0c72c4dc2164c025993b0368aab18a7f32da7f433648aa0bff81180cb3f0f9594f60e30f0417920c755ee6d931b1fcb2ca626a2f8abb1d2a4685cb6bd5c5e8f0c3ce46a5f1d6490fe096419b0afd4909329a178c802c226c9778d838322ce71010ce8b5cf1dd8e83c94d00d455ececc86724bc43ccf4e718dcb011309b9ad4903ce1a670bfa771766c8be118ea6bb66d42eb10c17dafb7a0b6011ad36ac0d3ce8e6fb58bf8cf01ad32df6bdcbfb220703c4ebd4c8be9a1334991a0d3dc2c95e4cf4c6eb851d4e126029c97f84186f004c5d03c96a3278c989d9dfa8231ff6f51b5cc327fc06db446820e8c6a62ee90cceb3982e1d9a390a088213a436937cdef0c27ab84ce24f8a6d9a18056ec313cd4eb2f28bed88e3c2aed0799fb5c0f7afd3ca86032a984ac314271d236ba87ebe75a95357a70114b86e14fffd4ab0db510c237aa53cb7ebade43e5701f82f56fa0197801a75ec910c94b5569b2f8000c9a01b4dd640cbb724a714b22d4f471950af5d10de0f7f2f1cbe9fc34e57df534e73e5f62975fdc83a5d72144665aedd155104746b6e0c3e72ce1308cdfc43cf862755825e58160668693a760f32da2abfcc2ac41ae501dbc988b6a418f717ecd56e3d0cc923d8f80cd9579683a0954ec0c3255dc7150216e7aa996d5d53bcdd67d8cf0f65d531bd141f3900fd2e84ff9b9dddca638d19afb4378d9338b3a7ea74ce646b1c7a8f782a4ba3cad17738af03dae6cc4864b8e3bb22936b33874db53efb5d690382ea89d5652ff856063294f10c1db65cffcd8287542d2b8176d39ba21f6990530c772eb060b15183c1be1801d6869bcec06af0e1d11c8621d628eb184287802ae10dd03833d76fbf849389c4893a06365f643d0290b244e1920afa97c3fe589c383065725c1be0e160a22dbe2a0d10b4896ad75f70f01a9f5244212d56a182ffc5d8a1a281c4e99493b98d0fdd79fa517c50a84a183fb7901df07341947353e141fc0ea528a2b24e2b6945d5f37ba8755fd2ca60c9d087c6b56eaf07742053a307e2ee45e3f86e9dc2433238f59a315dd11f402f9bdfd172169f9c899757c588d3c0826c9fd4cf94097c72288051e350bb12bc12c0a651d39bbcd2686a50ae3c8325b3e80a56a2da2a8de7cf09479d5aa79242d5138b3cd336ca1b483c9034fb023a99627b121ff009a9c60a5e569563ec2851f307dec7d5057dbd9606880b4fc4156ccf9eed6fc3f2ac3ce3345794f2e0754ad4a768aec24fc9f2be7b551fb1469fc88f7a800b089f8bc39557b79d8cb3353d196e44af802c2ba5c6ba0871dbaca2e50e6a129231d670e220390c8393a3961cb0581d56a46047a47598d12e90cba641be18a0507a641102ff0dd7c11118f7a90e296cd8808148d67d089d6869b1ed929026df4658eaf2d1b3314314659035c1214898c5b801c135f8eb120b019621541771a74992ea952c127815cdbe66b67827998725ac98b0e2836392a94a2d60a0e1910f2909e9f4348491fce844c7366d8d59c60f292d1e1eea2008592f62035041baf3c620128393488062d517af1a604ce8cabccf1b2a65ba57b58bb5701caf618121c3cbee4e3c08c12e05d01ba3439151f8169ae071e3cbc0d417f656fbf8e43ecc194ac4e925e9a0a23b55b10169e5302aa2b903b4ee1715ccf4ad0371f7928d5ead4684a2367ce200f9b6f77f6707a3409473770d06df414fc4f62af82a63611c95f303ec50a9d2de3681616a1930cf4c69c74a983abe34307386302b8ead3bdba37cb93c0627cd3656e37097a41530a4460edc4b3dbab8c57f50ba246e289758795f39329e7ac137b66365915adb90ae2ef22cb898e6cdfdc08fd7a55656b608be691057c600ffe0ca6cdd1f01e7528c1af89233f44d55bae0de7a2ae1d95adedcd8bb43707e5f0d07207be0de8ed5e69ece70cc0901f28d56c8859b05586536ccdacd2bee279f89c0930b9f62ce0d9dcfc62736a3034b528544a6413a1847af7ff826a192e8af96fb6f2e2c29ce3915836f1c343550a784b461a4a6f991200a0a40f4b822a2e798e71b9cfd6984786a8c3f911441f61e50e36343554a40de1d9fa9bff7bb192de11602ac6d03297718d7359037902be28d202a00e12a2fcfb4c46bf65f1958e78544a525c4cea4dce73c6a5cf9d0d963d4a58798e49a470c428eb4ae5a9fbcdd9e43cc9c0576e47ab370ab18d6267e30710d5a1c0a59ebc93138d5d925d99c9ec8af0748a212492fe2bffa86edb4eaef41deeee0962f0c4a70efc7766e7da3e9d942155e93bb104767045bb32a5188f51cd03699e1839e7fdf3f42c91879e02d9c11e411a211cf2340e840e5e3d621b924d51ffdc6aa9fd55be598e9cd1c62410f4ecd59004149e111eea90d0ec28887e417083d0455cf27991fd7bed08178ca97137fe1609bf0e21f4dec6a5a648444324a5aca042393e9c994160a7c88837c45eaa210d5eb8765687ac1d597a0a2a2f5230c80e603941b26543f5443ed6cbfa7c3df740cf657caad775ef414ce0de36eeee84dbc01b78f6e8d499afd0e88a8d3fd2850585672d6708b0215927ef65e7f12a8dc5dfa3068e07314f835e2d73f5e8b74149d4386add9fb88325b7da06b2eec40c864ad0ee7970b82bba920c1c283139136ca6fc54efdc7f7a90283dab9a75e0d5117197bf455f29084c9cc9dc3c333ff2b65c106148f710277bf4d5502da62df2845080b3ee00d8635a7fe1865d302973a901cddadf425903bba0bc1fa4cdd322ecf1a731916972784b805b593d1a87887c8e36efa786359e7f924a855aa914bcfc02d192e0e676d72695411fbca75f69077c77f4425566f2feb8e36b901c3674a63b44948915249310dea2cbddb05b9e3fa0f8d88f339379cd0b5527dadd961b969c74816df9d73603ce4fcda16f05579b93467a74231caf69e9b5cefcda184e14065a2a98cb1f69c6389a1c7d3f0743c2601100f8abb0c7b553dcebb7d319ff551853698178231d884593900f6b101d0417e8f8603a69145b2645f30bec471ebaf7f905fdee842c64bd0e16a140bc7729635d6e484c0408a93259043b6e6829bb31119e15103783aa1388c45dcf250ff65241aeb63aaa50567ca78b76ea15434290020c04127776fc2349a84f5338fdaf0cab3d68d61bd2acc3b2310f9160c4d21e265d288cef249d6d8840724e553ce8b1aa62fd379bd0c23930715743706b262828319ae3c985470c82b7c559244377c8f40a1e27238a28e1cea3932d6b808997f8aa4948181e66fa86d81f0b987881f3394c21d48cb97248a2398d076a037f0108dcbcc7eb009e27ed3ddc31b96eeda39e78bad00ddba14340bd5b35cbf74a8fb8c66e33fc8f6e618c3901cc339c7e702ed29ad09b907e0d30161489ff7cf098651b6e17a1e614ca17252e20af5486b8c2434e0afa3c522a618d1a54afcc4c31964abc6b91a012510556bf2c03d21080ba282dc20515cc347753c1e1b9d5087fd740f144e5aa947fd5d41eb2985a8a8f94b471fd1142fc9e4bf874479233067b392ff09b30899f56d188a8617999d9f0ee2b2065fa1a8210d4fa894cdddd66a450a42106b82c1ee8f8ae016af9852ff20272f9fbb232fe6387dd7b5fe82a21a4549ee74666e622d0a6eecf1c49af33186043a25cfa08d3525309ac5ee048d66c01a16609db9d95c7b19b18d041e135252a8ac7ce85888e6ec2897a62d3544d1205b6e1f1e6698f2d4a723c5702b17fef98b5c4c80278f105839191610c3bf80e24148b1126a6a408c0b49dac825879f6ae4873795e21a7e351f396797f344b471f22494c0e3ed874540f503be20cd09d7ef492437c2704c158ea97e4ebde11c3f9433d632d62f52ae08e3258d238735139f0bb59d9eb68075be3c55dc1e8fbabbd6ed76462a62807f859e99d22a897bcc591a777f2ff1f230fa37621833d7060fdc8814cd978706e596429de8bf2db5b4611fa7419a235b317bef476ded57b5bc82e62e5802f47c771919eae6600f71cb246ab600b19a6ee3c1a01d4a1da3da5ff77aac9c734082fa2cf64b6f9bb6071863f9991305d6f61e9811c48f8692d0388162412aae7dd28df2b0267e8e0c87ad3cc773e4bd7df9e0e0eb87c1611cc0949ddac4271553485c53163771037b05d70c2bafe7d6e60d884cd22d064a97e601b86c15abdd4af8915db9f1ee06ebd4ffb2ad2c5d8352af6b074133b30e26b785da487500d2965630b813e33b8324430bbd6bf05831bf871214dece90824568259ca48602e0fc170f5faa6540e1a24fa3f0f31a98efaf8efee4da3dc23ecc4476c2e387174bafc500924618989c40ca942666ebd95e0154902add4f3081236f105b786a1e635ea5b868cc722da0af7c09269f927f523bd7db717e8a51b36f63110ffc9870f78f9cad9d48187f77122f9a02e13ffcd20afe1e837183ab4cd21607bbd4822131101ef1e67ef3c269080d84141cb620b48510f469f8f666b4f7f3b4ad148830930654cb4cd25a440980804b1622210bcdba57c00d19c46994bddd5a6ba62b81a43fb6041908a9cb5e5d93b3732b50e0b82d177dad705418b103283999956a579a79b5d7542a37a7010ce4955c78067ac4ab47c812837d134c81f4e9734084d6317d1246d4bb034d0aa791fd60c2092f942c36cd402570dc2dc0cf4bf8f862ca53f5643dfffed6c3a309b8c04f5ed8c1543a8c4f7ed12620f949c49367a72302cfd346a19fe826fac9f6abc4394481fbe545db6b2edc6d155dfeab96fd4e590abeb7dd0fd5a232098cc87c2f112fa8af917037109ddb15b374a686320a8394ea3b241c3e5768a8f8792180819a616ffad318d912d1d029cb4564a8b2f77229f74f6992d1c38074072093051286904c8f33606ed52f4a075e1998d0794436c2c1873d50dbcc823c2e1c706d8086d9cabd1670739c3f7ccf9d913f0e39bf098623a63955db4be7a8b11e946d43a8ddf8e9fe4c8eb242b2a8457a33d809a71b701f76913d57cd5388738b602b15944967c9bd0cb0c65817b08da1d406eb93d96196afa2f252533416d2fcc33842c159e0a67438bd85f85a2ebbba9116e071d688ce3826f0324f81102f3eeaaf84cbd840d3340a045e911b5ed600980f654509c378a018e13ae08e0e4464ad940a860b08c3864b89abee1e63b495fb6fb0a016de1290819efea9d6316a5462962099ee97872cbd017f1947be5430bb41a59870df0040e43cb675f4190b247044e55849a654e26676ed6f8c0314be32382af9c20f7bca216cb9aa40684a9fc9c0352fda8d8bdee59e1995deed4f562ea877e7b397d3d8739f077a40bafe48d3c7bc9db9162489822896cf98e0a6509c19d29825c3f328c05cbd038026c94fde577675362ca740165293c9a128efd85a58c9653fb039e949cd189d559825dd1225d28c0a0dd390989dc4da617d24a9fd8c72dbae9bda7a74516186c37282b201a27c636e99c5cf2074989e5bb60315ea3efcdc12782c0f2c36a65567d25ffcbebb37bdf8de5344b9f17aba7bc8095e4dfbddfd81e3ece33dc0a00550d675c58a9f2852678cd90f90d4e4a0986e5a42fdb0b066cc847aa9bc20440b4802437b2848a45b4ad8a80fe6c7765add90296bdc4787aa3736e7c91907a76610c439612a31a8c388489c38c4c45315d543a0d07ecc2677faf382b3c1f7eab7bb4b77ca902fd8140260ee4f9de234a53ab96d1dbc295f49e172a3be1870e2149e55e749b4105f549a4adf27cc43120f2d7bfc943a3055c27ea25c177cdc067b71a314399ff38b44e3ceae7dfbd7e9e3024a5df4799559c01d799115e8a3241a14dc0a1cc827713746cb8f30c3c7bc0109f1870bc9825660bcb14c069fc3c861e74bb99b91c1dd4897ca153ad612eac9790dadc72beaa09ea1795a7858147c77003f07a7fc133f769dc5e675fddd304947fa99ffb323533ffcc0cc5a342fada9f9793edb8cfd170ca466adcda4880a169716ef937a3126b9e086f46dbf6e4a871b288496bd1c7dc2be5d203e2ae007b925c5eb9125d01c19864ccc92fe0312ba1e5070fcc5f831d828a5b14819a3c9650ad75dc9b819961fee8e748c70233c98b7410bb2e354b1cd0d26e0cf8fd2ce94f123026188fe4e1444fd9c684a831244a12b88575995e3dd51b083a6de6c3c8d4b4fbd54bc5e41029206945562f532511154b5aa00fcd4d19f46d3069b8ef2f5cab8dac61529263e042c12c90ee027ff9dc14f13481516e0ab674cf4157ef48369d3ec96bb7af3a4eb2c0efd0a9888eebe64f7575d0baa6119bbbb4edd3ae690877abeae2cbccfe567a4f690e63d0c0d4c1f89964ea997ffcff4bbbf46039ba180e7569c6cd7e552a44b519e510ac3b90e9a68496f521e1f75998e6c7c917487b91e59ada1cc69c51afa74efaf117fd2c1cd54763cecb1583371f3020efc219ca9f3a90452ac5ae18451d13a3289e5f499a95cd57eb0b1706de90363597342a142dc749f6d08b41091c5afad684cbcaa74aa75dcd46a0724901c823c375c9e7271647352dd7837618ed5b7eb6453d12ad36272c8a3b6e94fcc30abf5ddfc3a4d1d43cf7493c3f1e6d244a955038c0e63f4eef1629fc603b3e52d250d44828448096857707aa091aee090e7f834da7ef192ff081ec1a2d1a98acd5bf4edc76346191910af6f284941836d7e4c3874b4eb59b3c31fecae346b4913a74e56387b4c530d35c7d27968599e5fe0b719667c4b5efc4b430636e7c88d9aa8cb8f7518cd667625c540574fe007b170273dffc9802df6c7f465340d9a8b591e3b6f94ddfdee5575516c6093b77fd60fa2d8841dd752ca96ba739420c024f2768a86ed4562bc564a494e7b3af80893698b339430b12aceb6badb59cb7e858338d2344a17e629c9f51ebeef128f65c444f2bf8f486ef4510a3fe7ac2e1677284dd15709b800ec12e42072204fea962dabd0e9a3086ea75590464b527e3daaf1fd46a7d1658fd3b8d272506ddbb170e814ec72c2a5b0c8647f31ef3baa905f3bad00efd8daa5ec11a3fbc3648a959a4fb4598e634b82dbad860670b52962364dfcac3efccec74a2c49255240d8e9ea78c6cef1845e6e0c9e36fbbc28027c30e1c9d385d131773e6fcccf226ec71fbd15875976f5d001f00d81f0368ac902bae6b27cfb7f0f5843e3b43d819006e046849ae483ce98356f48b74202e9c57c3010a84ec42f48e7e80fa11ecefd3cd78d47d8eb8405fb9b51faf08500a332f13ef162f35d7f396229128362875203aed930d32223a6dec96e546de67a4bc63a0643a55ee2a5fe44308a08c306024a14073c2ee596bb4326784e30b72d71c57037a006f7e90312501ae7b90797fb30be7a4777ec761a4daa075381c1dec1c738c21a3c90fb062b5261b034ab255b670038d51af80e7017a7d233d803d38951f665455f97155348af08aaff72cd3f728036a2d070b9f05cda27402439736d8c3f617af24cc79a8beb03b34ef03b0b8c9b492bb99f6035d27c94ea02ed57603fcc460077562ff6b8b61e07a31b6332540afe8c570fdf532fe4ccdbfbc6fe90a65d81db8856811ede2d5102bc8351d83661110a277bd861ac9b14426f896113816e36586f138bfbda127d5e1150cc6da76217bbf5949c3131c504f8d9591b7ecaf0f2ea408d53bd4eb0d51abd958e35b5edcd5f397e1accb1a0f47fc4c71d3119bfacd4bb9c680fd05453323fae76a9ebf9e6a12de56bcaa3505df206e5185e188d1294118eb817f1e59d1e044d9a5207e8d11c3b69195fb18e8fbf497fd36654de34cbf37baa73d359887ee90f6eccd1bb08c0ec8ee2325d6e398a3d76102ecaaeba0e210556e13679dc495b8897bad1592ba14831ea93c740f3a04877258a5821fb1fc7bf4a6030311573c1d455944c2a8641570bb2cac671823638838ceeb3219ce164a5f2d7136e979d3052730e0e4bc5ab04f7ca2863222e946a44ce4e1280a13c4f836239757979add166e9b79ac212c8fb1efa05ce5d517bc70d13c76061df7828d1a814846adf91b34ef675760e42e6b5d3b308f61b3a6028c4289085ad48fcc3c6ac86f5a9d5862ce45f39c7f0d66e44e5e687ceb8765ed5f62b8beaab085db170f1300396d1b0685660e2074ac3c71258207f4f1cc70d4e4494eca80af7e354577e071c97c92edc081d34eda9c70103d30f423a072498b6991d80a5828602268d2ce7d4ae6d8dcb72e189d0b499f6b41bbdde9c2b49ac74098961b591cd746ff04502ebe5da875a2dfb15140140f2a78968c77bb78543ebaf6e1c878e13ab928f2a007809886a301eeb646b091b797a38c893bbf67b786cbef2df8a508fa6214fb980d598bd34230d21a83b6d81ea36355b8362e4889cd74d34a39a58836477281fa1501db65d369818a4fd6dadd4b267edb181a94e3b069ee0cedff91eefc66f8bec5fd4973e498b377d09be7776cadca3d3c50ff52374081dd5f9d1e64e043afab768c83be42363112d7629193f161341b02894b0b4ae78d5f347567f623e5e4103c205b1ed9a5540c7237e5d0246034f6c66f6c2f854ee659972de8adda1247961b8c38a3aabd5dd2aacd4b9b5e8cf4c5ef68d48b7fb44dfc7c870a4483d330aedaeae4e9ad954855abcca535234a6246f1e33926e020841bf74d7ac9f0436d5e9be6f1def9247784dd0da3c8bb1b90a2e2866efaf1d137c4514b6df38631b6e590ba61607039d2dc306a2e8759a0e29b5d5ab70f6ce19bf99f5883268d35644c1b0672ffb0b5e80a5df845053405478e72b9e2ca76f009ec393b38b2ba7b2a8a86bfda645e3eeb73fbc32c96fb46aaadf4fc8bb41d39522a28ca62029dda8f89b5469bdf033c6a340fb22ea1e9b70b6dc6ea0b236554d848f4a80265389e84dda8665cb089b07e61b8566e0c5e881a961951ed6c0c69238798cd653a8258fbef3a68f982fd4a5682a4c7a9990471ca9853476e567f21c45cdd3e995262923c8255f592505e55f746061fa887562b32a85d0cda7a574d561a7a502feddd181690546f48a4f84a06a7240d51760c3147d2f9b3c0a422e925170375c96e5ef463f8ec000c0a84f8a973849fc9b569a4a95a9a2adb056776f299dcab3f66fe4834243854a01244b2b57f7f7f9efa9d9d630357b74f8a122bad0a5c1bdc04001eafc3f26712a897de9e2ae07d4a470ca9179e5f75363f3d1bbc3afb5104f9e23a51f2f9430b77d3a2a1412917c20bec4a5bb601900e70545f5982ae738e48f84acb4aef8996acf7d5e864b828422e5f9e5a29801b54e963e1cf77fa91c19f8c840431fc2a60410156a7ceea38dadaf170c1996c36f83cf110559c80e743a5886c6992be4d449cb8df2e21643a4eb6d09cc35854a748e12cb18af4f9e3d648ecaa6a4f58b3ea1b21fcd7ad4d226f956b21b8547b87fd784cdcd64c9024ad07a03b9aaa1ef3a5d8566746bfa90cdeb5d83a64cb7dd76777de17bc5b0b7644aa8013ef09db4d39caa7d0a40b6b411a6c5dc8264ac895caf17c9ba691562df51b9cc3c864c930555b8b35333f57e3433efc8d9def1cb13ae05814f6d0097709e95c0d28eaa4f7aa7e526d105907adc487bd7d79d66135b6a148e2eb15706edc40ca2f540dea4372a686752c782adb3fce3aafc6f628428eb98e907cdea1c2b961716821ea0be139aed4e9efd2077c2d059e4b043d0d5a3058196d3ae93d7276bd6fb2b7eddfe3423921a488f25eafafc670814679f57b5c7ef4ccb1c82a5780d1eaf345c98bc302e876b85f0fce35514f73365199a5c0a6886f1689ffc4b317aa7ea31183defefba9898c9a90bdb7dc3b470d230d0f0d79e52f7854e18257607c719657ae02b3b410bf91076259103e13c68e6046c4a3b04d7e2d3cafc54b782cddf4ab74f0a3e91e36956edaa1741c132ee192e963e91813a58b2929e9f0bd743125453e2217919e097f953e4f6f712f28270c44cd18cbc57c3eeb07a4c8493c2abc4db7f0dabbc974e322e58e29c788f44c885dfc04fe74c2e3f72dfc422c3ca9623db2e9b4b351fad8237ba7914a68c4d8237bc77c7ae6f411297ddf9d9ff779d5ab2ad710235a258188a45c3e9e0b3e11853125cfde4b1f994c2693e9270c3d167ca69f4617f3c188f48c095f5936996eca263ce6d413ea23e683fae960cddbe4729aeb99d137d3c588604774497ca78f358f5a35fa098fbe8ba7cb3cf6a8f984f9f40c0a8ba3cb8cfdb4ea88d14f581c61cc88096347bc137c3fe16742fdb4ea4adda6533e204544eca30329825a5179621c5dc41813a37b31a018a4300684f9f88c7e7d1c61cc48ab8e748ceb5d0dbef0e0f56e06dfc14b49e31a3257d7576a3800542c5e2e65cab1273d03f3ebd8100645ce982a28d26db98a5f4194c60ea5f018b6f852e235262e229850cf7c280d081f76d359be5d4428cd15346644288d6c65df456434ce0cf3ed5e432e00e0b1f3ca6b1892af6f1783b9d893a0945f181085b99e92f254d00b162f173c662bf0f8613c8678fcf178157854b50ccdaec6e9238c41912f4e170917271737c85b2e9e05e1b3164ac6c41762245c60f018ed6a984a25ee2c77c4a0c89818b21188dfa9555789bb85f2059d3020a0930a065dfc1a72714f77c8679fb4ea7a774aa52e1035afdc1113838572325929825c4588a6cb3c7a783a9d4ede4bd8e9e4b1e0c3eea55c0c283c5d761eb650ece85ea67b41f930a052ca4da1a7e3f9946e4ee79292eb3472f9747aed19ef273caa7cc3542e067400d2a9b6ea74ba51c2e3b7c9cf74d9d530e1b1f4928f1e397ced6c989e72d981386b13363de58ea77f0fefd8239f4e57995f49a5543aad5c0ce8d67c4169d97dde093e6b828901c91935632abf8e39a102246748255e4a948e716548bed1bd14d3e9a39b46364e1f7d643addbb5246a75d8def36464ff9b6990edeed14fe024d3704b188033bf81bd8c1108f27f09ee98e0eded129e5d7af11bea2a4b4b5a76b28e8f49d8e019d7e9d4e271b866f2c5d042f33a644e522883120993126b0177ca6931702f9a5a8dc9093e0e964329d0e865776e028fce8230fee74c1ebade0f37e3acee9a6a10b4acf80bf6e026fcdd710a59140571413b87223c0e5312787f744f0a9606bbd9e2d873b992e0f2e8f3b47e4d385c826a17c7ae98ea524f2e905c8e1c30e1b5af2414b3f8d3e7a492ec9a7db34964b5c3ee13127879807a71dd12acd0709597892af218ca767ae2c28c9d72545be4a431f901eca259398c3c54504c8968b8b3a5efcfa1585d2b8f875226c78cc89b001ff2822f260c35dfcc686bbb0e1277c23f3175827a430d7656c5a65089f6cad1e80d3635cd65f5c1b0ee3cadcc52d72c2abbbc092c25c3f0500ab28ccf598ab53e4c6c5592f5ed8f0173fe11b1897c13a32877117d7297213c386dfb8b80d58a7c8a946890cd6a9c981c25c8ff118a0239460090da4e0e151916a7640d0833314b1852444e16302253157b6625cd97a71650b0667c3952d992b5b3157b658456e5e14117970f1d56f5c7c85758a9cae23298c8b13be71f100609d1e28cc751758080a73fd053ec22799b890f4ba644b3ce119a455d7678f8c92af5b2a7c266c4bd842b9ae35f1615778fac7c3c393b7c99d7006740db5ea3a99de5d0df05b8ff1cba7d3a974c2804e1863025392a229f9c29b3e624077e50d5ed457ae29a7e613eae1e9366dfa573a7843d4c11bde146e60a984c7d34da711c43935976efaca377b5a092fbbd3290cbf4dee74736a0ebf72a3f4d32f4ee927f0b32b3dbca86fa53b3ff0dbe44aa8cb0e0451cf50df2c17ded365872a957ec29892565da88b01b5eabacc268c31d1aaeb326b42f854f015a555d753f005a5559716840ffb780dc5e01b69940bfc758c09d3af9146c9d7c12b6f35dd8c74bfd3f56eea2e2a17ac56cdd9402bcd848f5eede3a4890cf54f14204a53738d27ca252f3994af4bcaa04b2ab994f4300912d43c4a84886052fa64526240a10f8b2e8015ea4db6f3dbdcea753dcbb08761bd3c865dd731ecbade59ed96ddd5c7e6e522b79452f270f15dd3bac0ca9b7c7dacb7b45a2b2d9694ca5ab56aadb594878b5ced47cfb049178edbe6ecea6f5a359f43aba694317a68d5b497bdaecbce3967ff0706ecc0320ad383c2cc1d0c10ca3f4855bf470fd767288772fbe092dd871f2e730f3df8d08d7df881013b6837e743d603a5e1b8adb50e6cd725ba7645178776ccc6f5f9ced91d3f0d6797c86e5977b3631f85d02ece95add2384cbb76ec56f9427b87551706544c31c51453b446e7be71973c4097653be376806e47d6b2d7ee001d466284a3b377edb2353c6e3887e67b5996e14e846dcc5f9fd8c5317f6533e3985488e6dae6404a4a290ce54253da9476775fa7d79ddd0e9452fa24cbbe2f0c7f950d85e981c380ac4b502133ccc0858a49cf484aa40039b06a6ec8e1c30e4a9f64facb56223d43477a24096a24d391fa501f7a04933e506d9a84210b501edad333630f691a90167202900f506e39061e99c4093f3d23a467681247906c9c467b2805b9df52f40c69245d9a501e173d23f30d9a28e3e91920b58891f2586c066137d8a4cb68bb4937e99926168965225b7ba45525f8ecbc9954cdebe43e49a474280aa5f17a9cf4939e815204a53423ed11416ba440a811d99ed6beb5972e16e3d8208a834dba8c29414cf832267c720e3979d2aad9640ab5ca4667ad63f255e9159950e40b3b64a1d8285e12a6db211ba585ba0911b6a10a740449901227643edcdd99c02e04bb0a077404099320254e541f233f423dc3c27265a64134a8a5eb3a4b975026357b82cbad443a067dbd2def6c1339e3a7b75190346a12339882a47179a978fc21b3b852c28cfe725dee36b7eed66e1bd43d361495d4e920caf6eb6ea0def216a23d2d2473ad7568db86288ded01532bb3ce8b239a13bf66a22ab2f7768a7e6d9679d8d0f52e7a66330cdbb0ed470ef5c1995e0bd19e9a7dc366ca472a04e33e8a5eebf6ec6d399ce37dbe7e1461f05ba9439b0edee7ac77cc7ae88a307b478c33d1b36f9794a667aef8f6910ac9d951d784533f5fea76e06c96adc098f4e1ac374fe4799ee78926aea73d797bcaeddf4bba0d45a1b6214ad3e2f465287fa5f6509a16d8feb0c0211ec3172c52154cebc8da3ae6d8fa2dcb2cb618273bf7991ac3d2b7e66c10a5a93fdf883b5645e2ed0fa5b19c0a3c7ea5db39393d3614b945f6d66ea5d31088fa2dd4d5e8cc5e5996fdb2f3b88761377377963bb3943435e82dd4bde5ca7c5121225157c3c79458d4611efb8d79ec3bd9deb17a253c3614797b767772e5300dc15c6d35480665da527f68d6239c6357f0184e3b24da2c75b2c4095f100dea99ce2df39256fdca7c4c9b04253169d028c8153d9d42a6f45913be4aa4552afaa6b607ebe6a45fe98f1896559facfa542295483da2fa60af3e3d73442552a39eaa4f256287e01aa555f456213307f5cff50675a8084170c22488d2f49021414382c25b7b5a5585d421487c230daa3d29074fa75ded2aaa357b1d5d3296659f0fd8b7ec3960d79b3f555b6db5d58a3a9bb9b46a629087cbbc271148472ba2ef2296934a5791d9db5b4fc7b09782828498f42d16bbdb7befbe7d6658ecbc6f78f421fb60b9ee3673c9b2f1ab6287716cf6157bdfd13bc87de38ee19addc6b2dbdcb337e6e182c2304ee9d879b86498874b6e79c5a4d3b11246c2552492a22912d9639fe823919c781c89aa08a73e12dd14aef5e2cd9959544722f1a215ecaebcbb9cfd2cc9dacdded1254f6c83b1f573b8ddbd5b7239ad95c32eb7364c756955055756f0042f57564e9f73bba34bdebc4b9cca35cc5786e38954bf5c3f17efe0fb2e773bc8c365f421880ce360a7639fd61ec330ac748b655bbd3ee0c870bdb5f6db4a3ffc362bf876c9a5832b2bb7a1e2fdf2ca3d7d843a5db423248357b9a30f193ca58093679e306d4f644f76c9e9741b84611866c78a1d093d8dd546cd35cfcce50c6b1a44455ee6bd85348d6ce9e4fa63122853267c2b1914be9106d1265b10c293e9294f055283c81992a6fff384be7d9ae4cb894c47b38491b00ff396087da2db20ac44c2b073ff2c9e9d837aa6de93d2a34124ea0589b09db9eae0e1ac7e63429940d124d339a455f4a36b7b5a45ad908b059f3d628d7490e82386d9631fed4fc6c16e7f68967d210d92deb36fdeb5478cb48a5acc089d06d8e1ec110c5bbbc4065fcdf6a767aed3cf1f7c5b964d98210a799465901190d46fdc0ee8cf65a469a8110d05ca93275040d1a4c912b9443ad178944ed4d4b0582ad5ff05f5c027cfd1fe647aea31a1148a4c59827e7a461ea149934c6f8df44c0d74c892efba7409d561663b24da8f940661f7d42a2a83cf662772cd6696d59b6a5528838f724deaf42ef36c29284fcf60417aa6a9103a4484619fdeedcfdbfd08260aa2668b3ba8815a454f39d11d71b7370cd7e774b6399db32944ac65e6ca07cd23deaecc5c61b7bbec368b61980df58161dd3f2594ab31c2de4557f42eb3e88ad9edddaa77ec4e2a048c56d9ed36bd03a362de6b1f41c27147a8b54c32ac9d508261f6583d862d6915cdbe93af9ea056513b44693acbbe8f3693a0254cb23a0615df6c58d2be7823faebe7319c23546160b8b02f08c921136c59cfc8d98449abe4fbf235ac25414eb44a5e091340485a25bf84d21c944e4a733862e42789561169d5113e5af8a0306288909e56ddaa248d7de36f740501d22a1e9d56c977ad2d444e0f1e3b18f043e31e5a253f44efd05669028f334a9ef778f0cd91c78c32a324c108440885a3f99194651e336a4396462062e803434829653da5d4435244949e241bc8737e42610914c0904510d02649a8c8324913276a1434d0640b36cc324914701085125c52888100b24c128527309edc7f9649a4c0811bb24c22052664c6d9006502ad946592293469e9c19c73ce4969c75d057c324913244241e414ba90e5263482549649848ee8c42c930801792b59261182c2c8fb7ea694cf21ad9573ce39e79c73da2ae7fc9cb3c3d15962295fb820715df3734e991d8127134a43ebe57c2267b0b8fc287be6e5f8a35c4263692e1aacc5a576f94984d2fc509aed721e69d5bdda6d71e7925639d193898641d230f32f97c59d4fe48bf97a597732c14be6a54ae5f2393d7a06b5225e9e013ff8d0332d97a7cbebf40c78799b92e9ba9c423df3cb4f237aa69b42660fe9f23df48c8acbffe5f88e5fc69772460d1f676651338b4b39a3c5597c6b71ede45adc9a6bb8aa37d7e2d29c5dcab5b85db916775aaec59519e6aabe49156e71afcce28ef8f72f777636f05d7edc5d8dfbeddef9e11cfacb0e5fb66ae27ffe5ec56db92c37751970775c1ea8db23a755f32a578856cda75c9b56cd835767fe32dd096492e6bf3b7ba6903b874c233e0a436124137347c510a2ce7adafae4f96a2dc536807ac08412fa738422e9194a9b5027740915a24ff23cad3d84e899f99c29459ef6872182e41a653ec9360a035a65eb73d49ccd28b9ada783afe7114af2487f9a46f9c19755ec438fd289f91dd3f2985172ed491224d73aa38844ac9ef95b08037954cd27a2c8d647eb6c09aed6ba9d75619eed4cfb46b98feef8e51d9d39da6ce8483b77714497798eba79394edb48b7e94e34daba731fbf777884913ddc1b96c0cef8e1d9dde3362c23ba3273a565181ef3ce37ad149bc7711c7779176708cb719e57aac1b89b1d46be383a7ac9f34a9e57ba0d2d8d2e4e577aa9342a7dc4f9a038f35de9366de2483867cce99a4fdd4713cee95c22dd5178bdbb3273c56d220dc348f3f2c1d66ddfdd58bbb2fec5ea5f336b575f18b6a32359f39a04c2a9a7936af455d3344dfbc85db85e542b57af1b6cd65618796c7bc7afb9ac278e8f5c6db51a8cdcd7b97b559ef82e3c8af058b3bd300ec51aceeafb32930686712a9699ab6dd61fc8ae46a6693b5934bbaefb0dec5df79d6bdf6ed33b79e674deb04c9dd87d6079be5e0c7333a3949bf40b5b9dadf90c03c207962b1d274073c5e39ccf668d7a3dbbd71d7760bf4e453ee614383ee614790710cd932bf671616e7e389d7cd1df81bdbb1ab8b32cbb80c8b22ca3f6d8adb58e601371ccd70381451c13e3c8cff781e84c4fb10e12cfcb4b2c73f629e56b60473e9b1df9e4b5abe3c2a7e10f9fb0941887f8c94731872794d9f59bdd3161011bf68d4e2b6ded28a5945a4b29b5612a5ff6bad65ed765b9cf56e132cf6f75d6f6b01bd8e511f9447854e539738dfb2ccbb06fbf817ddbc6eb2dda11c9ea11f928f6887c8dc79aa768d2116c73626c235d76dcb66d9b8737cff3eedd867a777e7431acb74b3a77b56367912e43faec6a90308edb64b60d4b0dcb36af061f8e77ebd5af3fdcffbcf74f1e96d9306d741b1de6ee77efce7967b6d6e3e6f5bacb8dba1bde15611866d397ad341bb2d998f9fb6aab6e6334d63c4a4a3118568655a595750786a460882237189e87c7392f49bd9a65b6eddde813d7dbee8677ec9b9481f185f2e3970db0d58c306ac8ccd5a964491918d88501c366e0084f0c45c144144e06b04939239b585fd4d66a29968d3b360a401d08ca93e7fdad6737127acbe60dada2dfc4dab536a594524a29a5f4b529a5dddd3776da733d83d51e6e8272ecf8810154897d0e06b0d8006de7bc0c30e90e288cbb84a71afc6034d82486c1800103460ee6c5e6e52fbb03cc39bb5f5bf5c9da4299281b6ddb9c136762db469f65149322199a7dbcb06738a7f385391186733a5b965645222c73bdbecf82a16ddb8ece102ced99689440f6e2601f226b224e634d1aa2dbaf45d9a786c1c873fc70a64d11fb762a1289aeb1260ded136ba3d38f30f2bc9171199699ab6d1ce16ceb6e888e33c2e3148944d9b6615863edde6de87561d77e75263357dab10c8bf6dbe965260d8dbbd6ddc88ec3651bd6d5d8f0fc60586bb16b526a9aa6515a77f2a89d62cf64e68afeba63cdf4f6688ea6a093cea694524ab93c4fa75c420c9620031e1c7dc2362f5d362a04e5ee5a8748d674b2c92c04f9cc8250e6e427fbc214576916054fe6c2b5aa066d7326a1021297c993a40a4d38971decc085926093af02946905250dc5914aeb97e0b24a79280d39977a05263daf00be5195bb46d54058189269bef5cab097e024c64c406f72f065d977b27d314103068e7060a6210ad011cce7aacfb43755327b4933b91a8a76b292fbfd937d3f55aab84caa382e733959c4bdbb5b6d264d3b1d389108d70c8b6279342a0051f358a370178892fbfdd33f3ffd2333cd151bc3c6d58b638293244c704290244c88526bb596883704c3b02c4bd28425a82c9334e188689465922640b125694218b8244c3490654604b7d97969598af0f8d17967b636331ba0555c2dc09823078c9bb76fe40bee362d44abb89931f1d1f7767d275f77d24d34b319ecdb27a5b4769fb7b3778b68b5d2ace6ccd62a5221448a755ac55de60b7b27c84c20b56e87cb592859368452061ca06617e7c2b92e734eceea75c05e77f0e07276da75223d2744abb87757873bf682efc25e0872c51d8d07a4c845dfdd0ed7e96577d96f35be5671dca364ee42e8f48c882373e7b820993b378bc89c4babb873d7927c5c0fad9a5af89ac338b36763f2f1fabc2eadfab60bbb2e6ae7bcbf8e613be90f3618d5d64a9dc872dc24f9b89aaeba8841080d68b8aebeb5d6dadbd0c995210d2960e16003bdc4910ed24b7410248de40920dbb13fc9d7256b745dba8c32cb340c8f3cba080d63dd8d948bae751099b53712aeaba189b0482f6bc870e4619b26dab48b4438dc87c8d81d69de2eebf6896d2886ba26b24b4c1aa26bf61c36edd848ba6e0c8baea61de38e7d14695854b56f75fc91b56f183bcefc1059abd9b675903ed2409bc5c646b2918ec74692ebb38b3012b623874555bb63d62ac738141671179d553a61d7f088e3da3b481f9934b46f9aa68d2baf1f1b49163de58e33a77cd4ddd8e6bd6e076e664c049a44d886eaba1bd87136cc615b8737124661a2ed49de2c67adb596c3da91ec2af7931da45595e51fddc3e3c782591466092d75a230265cd2bc6bddc411dd5b62d2a898479849a29084af93b467a44b7b87af3be66858ec58547bfd75559ef878f008b36b94e190f038b16e072e5fcfa14978e41166ed45e4fa5e62d2b8705623db349148a45d91766beeb45944cfcc5fef203d53b9af67b85f3ff5128d24cbae6aaf59fb7645ed52d46c0e17f6822c045fb360d488e0cb618056793d48acb5e28df9ed3b19bb63cdd818deecb2d3b276733a67b89798ab5ac48e4c6f51bd0204c32da5949b2813896a187edfb36bda682ffa66a5ad556a6d2fd2a48661bce46d54bdf0c9bb641d98fb52b2e4be952f597eeb5ae58c997b091d9020497e79c94f5c13892019a0ad1924032eb0c011201578a0b5cdecca5caf10946b1ec39e194f44aee94387cc23ae2b1c711a391f1de4f194835cbf86ae1e23f8ecadbd3dd72a2bdf4352b99ff864ab323cc2508dacda05d12be61c72c664817c71fd3a0f9266b660c25cbf74e0c40654c8d78c41be7ce48b8be2eef1ec59469f3dfb0cd2f32c7a9665d9ecb9b44beb2032c342b42ac7c837ca227a4648cf8c544890910ec9d72f3a04bb2aa4a56f9c43b23d906ffa80b96e78ec23d91ea8082542f27539b611f9fa8482c964a2817a26fb90144ea0f493d9a4e792e944ae35b33764aef4a670f49268946ce7916c3f4e9f8b7efa50edf2847c397ab85e73834d01c230bcaeab8dc8717b68d575ed8e3b59bb69d5a5830f7b06834fcb6e06e5fb98fdd46aa9cf6868ab7366e72457b32cba56aab64dfbf470952f365b811a22dae1a819767ddd6c2cc09872ce7768f8e647d6a54ace0c0bf5a3f73af0d54f86727eeea0a6740411445063c4918cca320832189a93f6b0bfae391f01ec38f51389eddb405a452dee2018f86af3744f0fe9fa651f7a0418262646b0028c1e3d337384f861070f9e2286cc597b2072d364761533a07b010a4c509968f64a02138402f0350e99af5ba6d69e9e995276355a089b6933d3f8adcaa63dd24199be83aceda056c9d3392f3a317b5dd6dad135cafd51914ce86a28ea482fecf6c2e48b3e7683a099be5e20c6ec187dad39346358c491290e82e63e76054173c5f5c9373fba64d7bcf0687f599b55acd6573a2736832cf4c95777766ad2aa4c06df49a49d4441444588ae8b848886f44c26042680a879e6eba7abfd74c2d3003b393b71a5206ac629ddbb7704e6d385dfe4570a6ad5f5254c3ac6f5ce06df777ad23338227695cbacc2393de532c39cfe721697d2e552e25fca951628f0a70ba322b2c2028f2a021f2b8f587b2e5b31e2c7ea932b0b9fc28897ad15b6a93fb97ec2b0618e92490673b40abc4df3904cc4eed2c9e026f3269c3cc9326805457c5906a9408abc4d6e25735586f0c57cf59dd64e6bac403badad3999bbd38ab93b2dd69dad550c5e05b58442efcbc1cb721b4af2eec8430ae5ee171671b0c8f62cb2fd27248ae1c12b3308ce0982586adc91b9c5ad2f28be2ff8394510b79098ea4b854c70dc01c13b339356d5a0baa455d5c9cbad00b855a855f542ee3cf82899679447562e6be40b5ff7d1fe64d7fe48d1aa6a87a0446955b51d144336b413ca76a83ecbec8f3572f5489e6cd3dbe8b34d3eaebb41adba72c8b37b74c1a326839f11a8c9388ded9256d54f28bca16fac41b98e5528d7571b7469d5c9b691442219842c2f17291f5d72777f3cd15c3d0d536947c9a7ced495bafaee1bf89049cf7cb87e1bddfeca0ddf2ce2593ef60a15d003af3ed6dd60f1155c5f479dfdbc5cc5b33f2f2e2abe6d295114c5d4c569912851049f4ac188a8d44330698807516f69f1141e3bb7bc051e1b6c018347f0e206b65c0cfb20d807c58b93fa10b9ef4871d0ccb2d3e14511d321204bea53053e143fb2bc3b7ccb459cd3597ce3b105f7471611e3a09e7a7f0c5b5c07d45361eaf3763d400bf8d917c462cbc587bf7a288d1dba8a978263bedf601fc4299527be1a1e8a6771bdee0698fa08bce3cc53ec8735e09ccee061c409c2cce3807df1a4098220d8138ba5ae060b9e289b5baedc726546a15028d4734b2ac48d0aafba2c6f7157dc86b260d1e17f288dcc0b1e33d65dae028f61bd0da23432d80ec5e03164e18fdf986597ab50c1e245050b9797ab08c1a4113614b831c6996739eae2714ed9e160790bea9fa4b13d14a6cea48e7a9533e251cfe40c78d43939d3077051f1178cba2ab0a8e22e0f6f8728cdd5f38aebada88717ff1f1b8aec822f7e7896a3fef0a4eec63d0ec6bfbf7af02bf630eae1532eea6761b9a9abc162b30ad4bdb28a2bf39cf313756b5651839654263d33451f8dfedd99bfa6b13e6fa1d147a373a4aec6287c0b0ec3a35028f19b288ae277725f5064497dde9d0cde16ca9d63334bc8591f4ac3f2c9f5a97b89f712d2aa7af05e432a0bafb06c98d151a35f3d2a3d4f31555c92f9ebae9e5c5f852a14d5c99863bd599774d70bdc0140cf2aace0fa2e2baf3afb94669452da45b69d2339f80214b8b0031ff01869c96e74556bedf5d9e1b82a4a3e7b992b9dddf43ee814534ca0eeeeadd651fda8bb6c76c76f4767d47d8e3e8d8c98c0115d765cd775dfb86eb475473e4a26df2e7bf46efaf6d3dd6ed2700ecda4e69ae65187cffd7437ed5389a9c4542297fa129a3d67edd9b3675f75721faf8fd7b5eed83d4c847517e76ca2f7a8d97776d1c7ec22dc9513b55d01874b3b3adfc7fdbb4adcb4f35e5c86e49bf5e33c329550e22b95e6cf34325f9a4c7cdd659ea3ef9b4646df77643cddfb487acabdab5cd930291c8873e809e7d09ca97c1c99700ecd257a95ebe11cca7d2a3195984a4c2586983ba6c65c508f889acbe6cf3422ca767444a24f23a2c944765d33fb3432695ccf261291487444343b29fa5509fab673dfec470eb776bda964221be6ba444cb29d4a4c25a612dc5462ae1a0f4177b6c650b0b50d103768cee66afe46294bf962059bcdf2a92b6958b9eacf643485933dbb9665bf3eb38a61a9e4a3979966f5bab9d27c01205bc81552b592a949e33dfe2a56ea2bc6c2abf8f57286d4b4b0e5201a47cd244033fd45f32567d8bad14e8739459ef89aabfe4669c50ec4cce3951bdfb077e64bcec0dc8a8a920f87bee9319aafcb8bcecb625baf1bb2da32d48a65558565e037acdf70e4b56f9a0d79ed1a8f6fdae9c93773a653e4a67b5f875e36d6e97738a330d937d1251671cc73bf21aa1a56b5aabe5575f2843de3d2aa7a0cc877e1ac55b51ec3c0277990adaa750dd214fb21dbcbabbaaeebad9aedf2ece22eaa79599c63bf5aeb50127a30546bcd6435d204121be899f1c3b0abb16b5ef5e1bc2e1aedf399ec3b66b3290cad37a8926b0a1700196a42a889298ce458a06d54fa3dfc637415f74657f11fde5f235a52a61f1de31f2a7e14b951f1d17ffcde654bb4d7f17bd7a1e2a3cb4eb49807efbf8ed1556cd8c4303b89e0c1c112cccece36341a7cd2c6ec8a74cfc05b62769b9ed38ee18cd59e499a7bed864fbb4de37b25bda15554b358cdb4210de9993ee9278a4f7ed69ea2e941e19333c7fd6691f0e5f88ec819009033645e914254241941f2b1a42179b6e86d28cd4b1012110a433f4e9e4c3fca28a3648247d972658bc595ad179710234269264f901f12114afaa12a175b6fb0e91835456825d437f9cd9a3b81e858dcd25f2ec9e5a61cdfd92140b6569cc515ff7257ee72bde33b3a7665be620ef15ed95a7181b03cf5ad46bea0b74e3ef9b1e635448fc23bcb818c9e9a789440b6866555f636b49f3df7611c97f990d9310c9b3c77749d0849251e16b5e3111679c0f72e6a5807c6f7a38b3aee47bf58de8662d787d4bed91d28f681c3e8d7e54db98aef2d57e52cf7f4d4355dbca5af5cd245dd1573944aa2968a2b5b2d57b658ae6ca5ba1c28fc9548445a45af4d0e0a430fde1f680f0a435f3aa53889f2d550184afa2125412292a9cc28e908238690aca6594a290d494792a02423995e170c568facdd6674ce3b77d9dc2085cf7a77b25a456b6eb0a1d786451c41d4ac61183acc2cc2d43b3d6908a5f1ae2cb3d968312ccbbe9ec128a535f4cb328d6694e6fb4844869c64843404d30f481d627390434d904109790c6ba618b271224f2904234b40d0434992a085ed073d521035d2104d2b400e4a93d2436f80321cc994d2f3f0f068800625c1063cb4fbb140908659d2909e194924223d2385700327323d29899e01333dc90869436e8cd268d8b15fcfb1f3b3ef39494059ae1802b5b335cc1668b738a5742284cee4c90922c4a7882147e43e66e90fdd534a201481e7f2727f87144d6dc8f4f6ee989807a5d75172a607a96aeddb4339c3e6e93de1a36d51d9be1db0e5d033bd2363061022078b739876d64e0715d66603740e9d651850f85c3a2b812acb2aac2fb58f2461b04b9aa603901d63548598115f5f180abaace14fbec08e9d601620871e4098ddaf55d81b3bc54c80dda666253b0bc6fcae1caa55950e2827b070832bcbb6697d86471eaaaa794df8b26399f85118ec1866c467bb893dcb4e43bef9f1cb01c41e089541760394524a3fa9d52208319105263d4fd0a07ece396795126bc1576f63f87cf23db97011992d40c1172e22f46ac9192a0168a4d26d18be2cec18f4da3d1df18d7f966978bc328029e11b7f4a2345a5cb5b27f6891d8a426e214b12e972b448c824128944225ddea6b39ea1dd47db246b9e768b044943ca9577ed7649cfd86bf5daada66978cc5ac59dbbe8dd21e06a959e5d110598e4ecdadde4a72dc99a0dcadaa5b63dd3f07811d1be658dc745248b44cfba7fa2469f9d428c051fe643840db24b2c1312cf07b62a5fcc8e36a53ee6143668ad96da49e7ccc2464f71f04dae6937472ded6cd4295fd06f95d671882cda08c77a23cc14d39d3a9b09dbd64d8cb356c94992ade2543bbabbe79cd3d69eaccf861e08c806773dfd9cb3a70842d800cf10a02002212be9a5bd729eceefadfafa63cd245b96619db901117c61082d9d21b4265a45e887a84f5042ab487fa7252f63a0a055f12725859910d82e9ba96a4d9598c42486dd6053335f737d5e9b1b6691cf071b39e79cf2def01a1536b5de605353436d6ee89a99c672760cfaecba78e2aba135352c9b1bec55f442b2d1237cf2aaac353db049ef896ffcdef323629394e2d4fa31c7e64ec124a1b2fc9c60d8ae571d367b5d3488b9936ddaa6fbf6f3045b26a5b476bc2e7b5dd99665f8bab2cc5a5bffecb2f5ba2e7b5d6d47d7352ffb8decb1392f8cb30b00e51a09946518522a3f3aa719d240798ee4313b8d4158c31262c084d05a6bad4dab68df5a9b656198fdfa965dd92786596b339aceb0deb14374532c48cd5e6ef5f280f055d1ad12c65ebbd99eb2e5f1e5cc6297350bb3ad6109315842b6978d40986bab668701d32c0b43fac9a03368208fa1a43c453083128666b294332f72d69d68683382f8b2f611dbde5d24c06f17893e075eebfe568b88a2eb187de53abe8b9fa69335e5ec4a21dc8004f9ba2ff2367b88907a668f9c31a4d6af3d24e86d9a629d9d16bd4d5ff59eded7f40d83f28d59fe8500d99a3d3d9466b64cafe0ebe7114a13be7e2aa9b57420296247c46c91deea0ba488d781f702a1073519c4e349299acb33477dada9d1b2ee5e16066b08c06fba7d8010ada2630083877768efaebd9345c8f207375842dec1a347cf70afefce5e7bca818407f3c65deddd6dd76ed4cc9df3be7977ecf0a8bdfbf61515259f8671b87796d9cbdad87dfbd6dd1e6ebc6b3b7818873bcd38da7368cfd1c31c39dc03be91392abde9f6b7db65d9d510459887d157dec3e82b58d4be72d9eae1bb88456de53a4e5f31fd075df98ae92240b68000416120219e47f0244261729c2e298c8e171f1a0c618a9609abdca984c2d4568d1ca4d943c221f6856f7e051bfae6bf3b0ac39479a8dbd9da93c266d666a7d7a151ed3f5ae32786cf1e01b3750a63431f7c1437fed1d73ee9b3df502cd66fa7378d451e0232abb24fd28f750a437f6117ada235a79cb56a862d224114481af0492458018a288ab88672258dacbd35672ee1bd7c685525e11ec4f0891f25938c137e74d2fb8e6eea5b8ac70e911178106666d92ad447a910f760820aaf740249b685d90ad3f772d9d50040fbd0c9a34ec6118fdae4d6a887cf538e58b78e5460d633a00df0990571d62af12fe195795ed15317d5391406599bacbc11c95a96c397ab983f2e72572f3b0bbbdf8dc7ee2b60f0e822c3fc23a96a9fe472f9a58e6f3e1e1e11470631122bdc35775783b485f24bf996ca21778fef87dc39724a9c295701e5d7f8979f0ad7c2b2f295fbd2f189056a44412c8e308e14ea3fad1cc4b8745a51b9a512cb55585858545854545a5aaed2d2c26d2ab7df4245a505c77128d437d45b550a431456711cc7712b2bdce9a9d4492416161616522a75b9d3ca0a577aeaf38607efd823834f5d1ce18823bd06118f292761d9aa1257e2fe5de55faee18e3972ca59f4e81dc2cb567d35c0f06f95f8bea4cf3b4afd7b8b21fe5a25a6a4f2f724f2f798abb3d38a59e9ecb456b855705f51878cb32ef65b7cdcc9fd06bfc5dc1fabebc85c060b46e4efacfba3bb91f90a832d2ee36614262500cf11838522a4c8b1c23ab39551989923e636dd430509577892596489842b30913799804f22e10a46f2f77e26731938a33020dee17b5fe537338ec37966bcb84d874fddb1474e7d237d2335d8584c7d4a3aea10a7804f390abc4d8ff32e4f7d93acd46d7a4bddf9cdd4e411752295efe5a6ae06005e0eaaf856dce4cec58ad79654088a1be4afca8fa5e5968ebff28bdf8a157b5ca0487aea224a24e19b141e7b64f133ace19be5fa82a90e87f89980504ce11b568329a9291e3c8b297e24e29ccac2f356449c53b307ae3ce53f7d9f138f18d7569d584a2596120bcbbd67b9f78dc78de5f62dcb9b4524bee5886c39e98e3d6ab47c1b7df4ed66762a01ab931eded3e8a9a312b0c259d8f2948318a7e5620b1e532e1b7c369fbab2551985094709ece08c29198599a38f2d4f01bf8be2e9ca493775c71f722af476ac50f76ee95e0f941039c463ea29477d75494f6959e1de7844fbe267a7c377f1e24f1fc54b6075593d582b316c49ddc5c33b0e71b3da91c794a7704ecd624ecda7a7e4136e398ba12cfab28b3bf690535ec31d7be494d32e0018c72983dfc03beee42fcbf37edf533e10c46305c1b1471e85c8e30f3b72bb1cbc338717c7079d9739c5e59f38a7ec6aa43c45a55379a77299cb33e3383ce65e60062ea2721cf00f160f2c2cbec0372b2cea68b18ec76c7559efaeeff2d5573bf3d2ac729716571c85c833467fec218a2e2a3e6816b10feae272e7876376b3eb7490d5e5393d0494dc20d8937be47cc8734748eea7de779c4564ec491631972ce2d187acc23c5c7263170606e6ae5821c550afb8d6c8193ed3477b6368e6eebac87628cb56a45674d37cc565cfa85479c58af9cd9f6caab906dd20bd1fde54873228cb56a5dea15c92c3db50928c8014ca38611bf1c690fd720927385328d49ce15f408c1362d2cb733a6f9b766e651b8d46a4f753a4cf83a8a71e1e439170cb88b443ea2109e348a51ee2d10b371a8db8976d25f57da4ed24520a7c83a9de61d475e04b8a2453a2f8ae035552df22302fe2b1f3ed6e1054b9d8771c1d9f0631734ae5258571e649ef4e87eda49376da4912a454185e1ca2f89e544c5dd29ca35fd26987a35f3ec212bfdca66ba4cc6cdaee7141ccfce57bc79cfc727c4721f2cb5beea8935f2e3b1c7249d6827811f1584531a773109d71faa9bfdc49938d725f88c71ab68a3bd6ace22a3ebb1ab567eae265915fdef7e5a99bd3d9e27071ec86d3bc07019c86cb579ce68a7d9ac7388d2bf240f318171beba0790c51078dcf9cc667bea5c2db72c79a5b2ef05bb9ed6ad4f0edf2c4f0e11a6ed35c08f224ca13275c18922707b26c620b4df2169c00c5ff4069449005ae1e83775a168b29504c89a91fd2494a14f35938c3e12bac3204196fbd5e5d8dab75c5becc654bc65d3de63a5897c13f64ce52fd380264eb14a68655ec1b195fbd1bdfd49c857556afb90eeb32422c5b3834cc0edc61bec15c2444b15fa3af63c6631e8375763610b7bc59579c9f5fad1e83755a5f5d279ff59d96ec441e64e8a8b938578ff90fd665b0382ff39d968e98e3701cf08fd563b0383110373108902d1b0a539f0385a9df69c9964d5fb65a5852a964062ed297ad8c1bfcca6d282bafae6c6d9bec5227dd5127935007c15b239c7386200886a48b77f439270a14716d15b8f20e314e0a8f3b39752e7547a3e82b5b40b2c9ce74ba48f4d2b7b3d4cc8233b9cf7995eb885c8dc8e0e726d2417c6331f53e494c7d1e1c7d75337a786c8473b4ea4ae1f0f762a7b8bee2270e9f00d6574f80cc6352580c71eaab1b99d42ffe4ad9b0bff22cf56c62f11f0ad58de05158e5898971baa7308eca674e83225e9925ec7de291e5b5552cf81e163d054cf1c483a1f87930c50bf1e8f5e9744f744fb84defbe0aefeda918dd643aea8e3a19f54d6cdbd908f1b89343f17d795602624ec1149635574e0c6fca65ed12b06225200614435c574e1aad701f994807c1a75247bd2f36f88ec47d34fa3c88c7cee0c3f715c5d545f1dd5d983aed6ccc9cc241cc4cea70e8c0e1981f854fcddba9b053de47c6d1492d8a0f0f7e7563ceba2b9d9b15663dbce2e52106af4e378c23dd5183573d75c721441e568ff9cd0adfb0308c101985c7958338e52b329352b0e9a6cbddfb3815f7cbaa3be6e495df3beae415ee2d6fc916188a58e481c5fac1c292c28458c7ea47cc0a4b118f52687e25c69db99b9665254c6e780d999fd8882c9365135148919bd84212999565135138c105222cb84ee4e186c778026e780c2c2640009f61b1c0eb88f3abcb160b8bfd98cb16886f64ce3a0bebc8f8ea3a647c857f14b961bde63f569781afa3e6322e03ff60bd06db50187cd992c16263910799c7e01f456e643ce63f248591c13a622e7319fc43068ec1b2b5d3eaae06287345f1622a25d6a4c4948a94022f9240192c8e2e82f866c68d8ccb88231932a41416f1c8927165b098ba48126f644ec237331e035e514ce990917110bc2c2ee3ca5cbc3e684e892c587c747d4891058bcb4eac11aa40dda3fe7dbb28962e8a2c275de5291745efe0bb8baa197dacd34845329d858475cc88c122f8192a2958068b5ea7a27b8b4b92087ec6631e5e51441d0c2f734514e661c6632e8258c78c98cbb8ccc511d621e3329781656e43c3dbe2a316979d0f9ac79ddcc9dc98dbe29eec5a84b7c5b7ae4677ed7a50beee46673f6f8865933e3f6a91519a98b76851a3059e9f04ca323157823980480a0303cef995d7cec67ccb6da8ec423ceae4f0f3c65c3047ab2438efcafbb278785bbe934997c56d7a6371e7d72c388fbbd7e9c07da7dad050e75b5d2e13cfc222f898efb474441004f1ea3b2d1b3aaf0f6c9c299895afae6cb1ae6c91ae0f9ac7cabd3e460d77e51202f471577783fbd5d3aa3adf4f5d3d2db7e64bc835249c19776a10c9f313c42b30e6219964fcc678c56de88a7b9de6ca168d2b5b36141bb32c807bc39514d3289268220a23c22c83566044de28d71d0fb3b5711ce9661fbd883a1be1fbe06d7c56aff0289df4ea2bacd38d6d280cd8b4cb5a351bbc0dd8df69c538eb8a8d730061c95c860ca1fe410a754f9fc9337ba6cfe4e9317bbaa7d0049a41136806cd9ff9238766cfe4993ef367064da029d43c3c73680e354f03f54ffb3450f7b44ff7744ffbe0feb13985010fc3066cd3b2b5bab285c2dbc685a2cb45dcbf8be9a5936ee9590a91d44f69622e8a67511af131a38ba8cfb86cb14431175158e4617519d7c1fa0cfc23e6accbc03f8adcc49cf51f325fa964482579cab8ac98bbba35aaf0cd433201532e4eb91e4e39b4b393bb13bf8937f56e9cbae29471673ce68af33197390b87afae38b18d197ef59c527f441ce151bf11a27632ea8ee0450fef08ca2c8aee98223e14318ef8d35118073cea5987033cea2bdf56ee9c228bbe32f3c43d0a47388337bc4e16679732b30f2ade6d72e0955984c2354acaadf6e7e06ac5627d3e0b0a5ac2444666c68ccf6b5e183e1b05cad53324627068084a949c71c0e1f3220f0cdf688366cc00623e9ff50082bc25e3f35c0f40c6e7b77c1c4222641e7322665cc66727f220f398dfccb88c6fcd85357200599d95f1ea44e080593364b2564dd65d5d19372675479ddc52d4ed808958d22e896278f059cf4891b58ac5a234abf7b7d5155317e7657ca71573d6155338071099c7b8383fcfc23a45268e711d9915ebeeb45677a725bb8d75758adccc7c25e332bf91711919358f8175bae633a791798cb35acf38bcf5eb14b9b139ebf5abd5657eb3ba8ccc579ff886008f8175629c0098f6609d2237f92bacd3f9ad8c75280faec701eb480a53cfc23a2d8479647cc6ad39cd9d89b98cdfc4ccebe0f0984f7c33e3355887cef8ea2b8c03d6a197711958a7c88d8cb3b00e95818328d0cc5998ead05c002d34001e01f4d0007a7c581f29d00d3f3468d0002e15c0bd8106d699ad184c85284cfd101e5361dc86cbb0e1307ed870d91a67c8b8b25573658be6cad60cebabdb348c13315b36dca663dcfa7365b04ef5a130f5abc7c4609d2237313198759d2237abb356d72972c3fa0aeb541e0a53cfc2f501ad18a8840cb6e54d2d999a19110000001314002028140e880462c16040a26b8bea1d14000d9cb25870501767398e520819630c0122220200000380a04102cdd10f06d545cb7015d910a8fd1081452cf0a3d21a2f2ad188bc425925d32f55f4abf094a51cfd2f08454c2a1282a3bae9c82d179058c908bad435c5998ca121401d96bbcce49f5add80a3e650659c44cd8a7c4f3333cfb77fb807c59880d216533f6e48ac4f4cb80066a05d051c8987b146f4307bc1588adc75cb25769b4bea38b8a5b78d6a9da3ccf6a9eb24c2bc00aebb0f31d22bdc5ee0a662c486c347ad06cf68d6282191795291acfa1f862e3d4db04b3762fc5f9e0bb73af5cf737ca098ed8d5a28fa145ad74477c3bc32b824f01f9d79c741a34f4e8b0383669485232fc538aa4a45be856a736790c1fd77f9a41a2c4556dc809e272457e0045f9c17abb54b3859ef88b0f405cadab359c045f126c0a69ef86d50fab38f8e5cb32a962f5ff12a0e88a2e96feacc15a0a2188cf2b4ebe8502a6547215331254aa13cc82f488e0a0e234a9fef6245437fb068718a5130ef60a0b1db03afb279d8b0bd054cbe17f75a4a56712cfcbdfcb761bd4101f2ec01936a1517d857290c3a358cdc64d8b85ef0d609b2f8001e209a3e3fd489751a2add4ab84116c94009f3eed739bdc219bca96aed8c9ec2e0f4d0f80dc9943c8ac2d5628d2c4cf9d5ae42ea555410d7da3a85ba59b9c943f7984f05a3667b70be4fb21cb0cd6b0ec39a3cff012526045a57113a3221a754fd44e60cc0f09597a225189e80556ee4885f8fb40f4cc96f2bb1ad611b14a7daffbc240d03dca503007136a01f56daf61daed3c5497c972ddee6c0371787b2c0fec4d6cd99784b8b4fd1f39ee0a0563747134a1d5d914be4629c94dc32237e2c246abc3cc35119b7e400b4fce0dd0cb18ef810d5c1389a40c3470f7ca3dba64f22125c93cc6b47ec3bc67ec9240b52de73da6352cb5ee7729ed6179a492d4c582c2ff821a996d33c74a397593f6b4745d827cbcb8b3e0c0d3eff406506897004a6112e1c30253f221a01ee33312d5fd28e31eb984026009ffca57f49df2d785ce093005fa53f654328f2499eb476847b51720929443cc7bc758c08afd6742d61c03bf31c490fd700d006997f88c5a38ddfd12f19149952100c4733826f7c9f46fe8b299771249ce3a6dcccf3d2d894e5023aef1c021e56ac68d0f50f0dc29696f15547fe2afea9945596f628310e05a17aa91c2b132da93e11c599b5004196453ea0c14247d2db7a895858376a7524412d483e9b1020f8a2b61dcb9bbb6639db4fbe48060dad00a6b80fa912e382c4a669b59144b040309265b37dcfe46477524b25a574810b9160e21710dc087dd8b8a2b7d6593462f94937e240138ad0fd61b1241bea8375054122af9f2c667b419316124286b1cb4fa50fb1453e2c57c9704dc3c1a2f95589d0c8e969d7c735fd0662d7f8d6d380f9b1e375047c0581398c83afbd0930df868e7d355ce7c9b2bb63f3e2c5912de24e7cb9bc430ce229be64c9831cf810468caf50852f60090a64730aa71813fe422e3b71f2823d1996ca9d8a237d03e6ed68dd55121cf9acfc6cf822e685be7ba3576b19ecadee6e6b2d028eb6682fd5023b1dd29e34f192ba44ffae525b6a47c86d78bf3ec21b7a267312d92f07c26d6fc314301ad07f982086ae17efb5246da4513a98cada60ecf6185e193d00c42e9908383069aeec742604dc19a59744bbefb75641ca7f94c35e23f49b0c6fb0e5c44273c00576ffa8be3445c4ebc55accba44750810fdd5a3d9be5b90fd24955ba0ade97bc03e315145491710e7bcd7f08ba44ace1a64a71706f0f320478087b2896444263c58644cacfe4ea534690e14b2af306172525f41986274ecd45b1e4d81243b43905cc13e33b75701be8b19802eecc62ecc8c688854f6515386e1d9aa9c3fd3040389188c20e844c356d3889a1932a964a92e28d84c43beb1514ca0ab12b115c3175290906c86bf49a299a37c2b5c5a604e3e6434308c86bfc646d4f2b8e440e0b7741edf87da3c1ad2500020a51c7df038d12a6e421d8c55bac4a48f68e3b16669ef86fa3773358393e48191ffe7b43efd24e1711d9fd4e9eb088124b55f7520afeb1d3a9c603d3b3205d26a41f0b5ec020057af29cd19881bd00da89098b2add0b6dcd2e7f4d03d2d7d161279948c014f803129ce1b854025818fc994e0adc14283ba1ec364ae35aea3fe4c92948c6740208f123074dbb85bd0a8012661226df201aa639a0550b129d88ed34326ddb19d855539ec56b45aaf99d46e0976af2fee3c7b787fa863d92cda073ca0cdb5eb3e10c41ad123c4ba41a8019057e6cc6fb873773757a49ec467f05e4f40b54187b8b6ff035aa306bb3614c905bbcada18ab94e3746a9a8890de26d0a607d6ce6966a42f5e362e85c78dd596a0aaf40d0531f728e32124b885e8a2a1525e4c69d21bc62d644c06cd3e3402e39fd92d295d8e50d2712258c246f275743db14676c5c5078daf2114b187e3d388c2cdb7b255467eff917faa1683436061a634ccdfb15883fc7c8e73e7572bb2cfaa30b1d881d146257104135314f4b76b0a13d560df597127f6e4a5f4020ac8ff777a19a2d23ad3ad61bebded43e8e30c911636cdfbb267d9b70e1c0c58d3f4bcc28b76914a8ad6833cac04fc10ab03fff221c81c7bc713ea7473f280c9e03efa129a3a5aaf50222becc98ca324e1f05be59a12608cb9991943b5ffe6b6c10d04527773497bfe78227ef80b00dd4c5d9f1a000d2d2375d0f1a7207f1ddcacbe3bfdb124c64a921d52d980114f92bf6bbe5ae179264b0edcc4154602d3659f67f437726f119b8cd2f1b30e146d50d40f0e8d5d0ffe230e8ae12047a78d45081882ec9a21a856e10ff207f1dc71df5bd8981f93f1d92c838b34dbb0537f19c70546fa535738be15e70abad5df0de46ac0f7a9f163462d43d90a63d6d07939c77ba68917fb5a05eba4c028fdf60ad49fc2aafd4e411e823ddfe8f22f77853d9dd6278f40d8854ff096164bcc0adfa86ebfa6f98535e98635e9c77af6b0aa2b89608a30b18fda691c6e6c1c8489028274f592afe589c68893426cf5996a016a842f0cb34cd094d028f26fb537778af792f879305ff4f5ef60709de935d8c2bef9a58bb923211c38e0fd703538841b8dab4d79942582812cf2c449469e2b3be06fcc7fb507f83a16207791a40faf83270c9f6dd57c18a42b910366647abf58603d199c2a105f8d9eb4eaa71a505abe156f66e1bd51d7bd39b9c025423e7d4455e486fcb851c76df959f10912ef896363d097414f05e67ce82b36c460f20a2eb301582ed93a04baf9fb2c8469331eebd4137242193883f76153a947b8ed128605f7e6e4b5a0ed0cd8025b6300403917f6a9990b450a7c74915388f024fff1431fac801f6539c3205e2c132efa10031141861720bb88f8f41c3768a06e626756035f84ef2081c7d25eba96556a44c145739a811f06a6af2e401439b27696c59cec86663e537e68aebe0691b90608cb4775d4552ca37c5241a14b0b3fccc8c409c08c139f6d415ccb0fc355649c1f5808af09081df2e5cc91f9feb165ae23f04539b4acc1e640a1d2dec70863237eebb92b1d99c70812012594e742493297db99b133f3123ca84fb6676d8807cdb79dbe7f7a27266da58842927000d53954e12991e46d576ce37d5c58b0b48105d78dbe4e7543174199efc526c7bdd2ade5a4364489059e12328e5993f769acd40277c2a4fadd941a53c4d6cbb8720e87813675665ad21e6b5b52a2c0ecde283ea479e759cf9c1c6a1329b5206d270c1eb81d76926e4f5362671a7320ba363081a13b8325b4f531a8aa420a954d616436c5a592d9ccefe8b4c8f612cff3d37c042dd1935be6f53be0074842fdf805759c313902fd8075aff5674a95a3ece0317420ef23890ddc8bee507d7552b234c2349c3c8cba47a1588e91193b5fa1c66e0af66f3a70361e0a3a814c99d93b6c5ceca24c573134ed1948158f2c9f577083bca5a09fbab63fbbf3dd4252aeba4c358973d6e20c9598848c57cf63d910a135f4f72cc19c995ab49b2cf548fc023f8b6bae2c542ca23cbb10c44a4d3eeb048d4e1ec4f73c19f51c6a71009c12a0254414ec72180bc74396c5d127c7477e1b8732fd347c420cb7915a4a65f64c8e395ab32c926518928367faf80fcb13f408cdf0639de8b0139a64fe0b0f7d69c036360f60a6340d7fb85797831c3f3d342d090716771719b9ff8a2ed0e42c9e2fea347e7326158a4e28c084d943f6049404b7a18a75db7025fdf7a66dddcdb05b7efc3f96a147f713c3550a3a1b2d425baa88fecda6a68583453c284330468815ba4477b2c6f7a909c13a8901edd2378270cbb6707c1ae04e5c27307719bb74ea84ce799134cd8269bf91969deb8c3590b9bff935de4307a766bb9c1fae1d303704c0947fe06c5321cb3b5f6dbcc052515c336c6599a9204cf4790c2316f926be29ea46d3a18ed822a302f50b5661bcdf8537855d1fc48d3620cffdd568835eb71152da105e8e63642617dc796d71006f230c5283d39836b108e48871b3997df73a26095de6a19d0e8b9bab58183ca788480c58732da57829149faddb0bd8509400e3a3d8cd13ef872dc02ea6f666542ce53af08a48058907d19f1c937d513c82ddf419a965982b53345e8ed44021da2a7b36f48594eb8807d8ed1f532118836271a94b8780260499fd32a786ed263e62fd421bf4e98a11ac3abc3bfba24a4a54655d7a38edadb6903282cd774b1441a70aafe4027c23d8842ecb2640e44e105ef70626909f126e65b73080066f048b8caa6719527e5936e5b353405ac2ec7a8711d2116aed085c23d5c770e2fe8ab3ee443dac493c8ef4161568a83fe40bf1b07c537cf02521f95b770fd15b67396ff4bebbfed0704bc3a45fa338fe0292cede1359ded05d3a3c180f34d2fce8b595add513e88d8cc1f7b686f96bb937507802ab0e65885b30025bdd187f20f68849e0e2e220d1798f53166c5cd93ff1d75e759f190311557632eaf610e716c9b264ef9284735a495d6290835cdb6f1051a68fd2af3d0c65f206338db1dbd6ed37a562d88b9319cc7fc6e2d516cd0464c479facdc78a9cde85f53150f91af1faa52a13f3c0a6ab7f2958a0d8ec3f94a6d020796be908e92155a3141a6299e90c2c03ca4029977d385bfdd6654f47ca8ad037858a38a139b7b9565261ebb1bb4e8d8e9103ea7c2722c6bc78bc58be5172114ae1d0a48b0365154db7445f1685ae35d07650faf45e883861545992c3873d8772b73a005911a01d0932b01b795cdec00dc3614b23a31b703b7bdc8364fee62f17f8d19d6921ca652f2d32b652eba2a7326e37547e1b01f9f0f7f143604522f1811e114ff48deeb735b1aca9a7d06cfe95165686ad2026ad400718e3106a507a9fc4ac07757279240ffd62b1a0d316ba99331ebf614f617f7544c58f1cffd2d0044733e535b2c916e624317a7aea3e568285e630b584ad9f1180b7a2c00a70e3520c6320e33781f529e158ea8de144419dc4038de8c87f72c418673e7acf20095f59212de517a002d32109fb1a79a45126addce2f76cffd9b095617d4b96d3776bd129afbf00ad6bae7f21b7d1d2879ec3fc73ddacb11b76c592812241d00090b41e6891264dc261088cb18e57b1a71e55974b485f50e35cae4a7e76aff76a486c479e8bed0930c755056e0615e9ff5e5071fa23f1a6be9a35a87f68eeb1b03be8d7a26a4e9b6f1e72db19b3110887d04b253c26db89a4a5bf592e6a0dc4797b3c11084674356f2a3a55b7b6782ca8d56709f6a11ed45598d18ae6d3dd71d1f80dd25403f2b5d5657e517839a3173d7c1b26cf2421af2166bea1fdef6e661838aeabe9553df7db30478c2b2461a2a1280cbf01ede6843ff517c55b12af2da360b7bad434bceab54566d495759bf105150cb6d1a8ba5ef394ec1d070763dedbdc886b20d586a7c83c26c496339791330ab188553cf65844e9390df6d435522aca0e3c30ece132f2db6066054fea7661bace2954b47ffdfc6799065a24b55d0d3255c9f84a05859874195b8865de14ad0de8b2776b10aded637cc8dd0081497ca476f24f59e8d8dabd539e0ee13c0c8bb9629fd305799127d3af0a4e0b1d8840c4b8830a0780ea11d2b4c263336ab7c4f4fbca2ae7396e24d612be8f9e729f2399da9db4f9d7f298970a9063146a945bc2de02978363a08891e589c63f6dc23ae13f9f91870573a514b9aab29072367d0232fcb3b06a08b43407070f6641f7ea00786acad6efd9dda429c6fa9314aa21a2c7d999cb584bcf02fad40a615051e8c82da4062fca0cd2cca77d1fb83da05bbd5723d82072390d818e0e65e26c06a06063ba5883150de9a605fbd0399e7c639110d7b032145b96e16fc901754064c3817445d7e98a6ed3155da31bba1ae1d3543670bfa4347ab3296dfeb76295d2c61239d1727b0a0dbf32b29d4427565b836f62217485c5dd60911d3de556179df54401d0aafec43a3d25ede78e457878c864dc7c1d0b46a2988350706eba9a43704bf946e665b289f6b1074e50621bfa39b79fef8fd25b7344283ab18e3b80a4974823bd08640fd39bb7038b335834789f48f53404dca23874f3c2b6f80c70288187b12513b79f3800158180be5a6cbd6196cccd4f4b69696f556f9fa792c8a6f1e4577954bfe040b8e30d89eb5250aa640e410591804f8c73a650631ceddc40253e370fcfedee2c905055a2dd516a47d2bde44d21b1813b8cd5ac78413e0af46added4499f02d36f611e8c827ebfedd75cde41fb48d27198fe6d6609a43b38c221e2106982460509c79b74a35359631878eb49432deff566a76a16f4edeed978809d4a414efe1bec332e7767a6b18cde95e85f2a51e00e76735e23043b88b183cc518f0876e053fdcd9bffd185433a436a72a01f15bc7ff3e9412a6c6143518331fb0577c7987ce9088303670097e4caae62110c68fe467b4401f4e6e55ecbddfd954194220b4041d1c20d24fde7e06f06b35cb1fd8101c08145b40da43347e3e026699ae92f508ac2a6296a6f24e58c225ba304e8a392e6e48a5c8030aff005095fb3d7ca58b59557ba427942ac7dca7b5e2b0e7008f88172b974a4457e06f69c69554c6ab57c500b74bf5e54e0340d13713b0a911d859a1e1b575775db584ddcad6eb748646737543d33e75e7149cc6d910f90ed50545112c58722f31b3e859f7391cd4452f6fe5f48a6130076928267eb55606e326ef57fc5a0c8cd11f04cc742a427cb14bc25880ccca03fc79de21fb0c04dbedb98337b02913d23f90f0f00f13570539aca437cdf7e4747eeb3ce2a5b6487f07b3944f90e072e68ba7800f5eda512006a302dab7157b1bb4bd0fd6fd033de78acc36782664c50913c46b39a330e2ab56d36cd81748b60d0b323254523025430c11b185e3b7ee90b9744ef918d2f6c0daf2acf1b06cc51c8c684feac50801260fd8127a6ef51ab43cfca6f37ec4601d2a87599d241c92903496ee5fbccf29035d326a453caa1440c842acc984f538d3545ae89a6a58c68904466cb89421fab7595ee0a34fac66323a98cf50f7571e9611a44bd016179c1ff5c7ed58626fc5a73fd116fe54c41fa157ecf84ed1ec6b4e0d13ac170e573966efbf047855eb930e5cbf7aab333468dde053d56b633dc54fbff34738b86b51e9939cc549282a32ff3b304033f6120ef60859301993d4e90c9b305fb45b6061391ad3a4b31a89f49f04cef8521661225f8b85c501036200ace7163c15ef20670ed5adc656995cf9cfd96ba91b3e49ced8ec47b7c9200865401051d90576951b5dc1341fc079220a7c96edadd670dcd1d71d769112a5173f046267f830216719a38d3d9382b7d4ac96107edf20a0c5f4df9fafa233d810807de663d969444fd206d48a5d391c47843295841092be1a6de2d258c5f23d77c958ee8a1c4985522f7816aa638515a76e8298cdce409fb6da68bff8cabe4f76c28ca940a45da7c3c2c7508184c39fa801a66614895c9570fb790efb542cc0b5e113231ddf5e89e4324fb55f02c5b7729284efcd23b3bb817d45eeaae857a5ab2caf7c3264f01a0308da7e9aa17e599ff344006b41ff90a10c2f8a98e9c07f9de11d6c503e242951f3d148bbd0bd2221ffa179016038b7897341372194029ed12b571e7c228a609cbf80f625cbfdc3ed96b4c06b4266f24d32d0ff405db6bfd3a4f66328a0c75128c436d0ec3f180522b9885c37fdb3d9969aec438ed0d7dfe14280692f804e963747bb0a8e25a6c70999af52e03e35ebd2b7378cb1c5a55984d555c1fb2154f187f6028b3ea38e49b980e563425ccf4bbab445ef371f1995411688b5db5b345f426d084004c035133a2340d65aca54898d1cc68ac42e2f7407791668b49b89341500e8ed5d2105f7c25078fbbf7df163d6d91a5c25d98d68424092bf9a69d461e9cee21e3b3ce76f5af0aa0da87b31ff800234d95b642cb180762953ba610c004774392bf0117aa4ec937c51e63c1317588da4ea57f8540de536d159183caf47f4f3098dd9e76d43b48a3105c0d5fea3c3371dfbf96ddd030c01de13c4751b491061972f117a7902d4ff7113a0ffc8464e34a6b0020b4e03bc1b6a40c4317ce214963109f1b0d00512dcae7eb44131c9e4c5045aff247a9ca44968648029564c7698c32647f2b0ba04b425bbad075c765f6906f3191def5c22c333ea119155a0ebd3200d82af90f1c2b947f26615d2145bf80668752e3a17433140a8e2efda1b8b32032c4146087467797470d2164ed98258f1413c6803f2b83e21a098764e71bc4e26edae124c72fdbffe93287af7c9c479d628fea546cb1a74c677bcd5fa50d6413cf5e3561c1dca97e2fa867d0baf6566ddb506df4e0970d0273770ebe8db2793b06a7ab058d3fccef8a2541254479024424aef8f27ecabaf2d72bc6c31eb826f650181b0f42ff378ec27a42cc7e727ca3bd94ab0178ec22d818ba6d6bb7f4427e4f0eacb7502019171594c85c7028107971f8db6654ca2231497e494b49d87d4e22cb45c19e9d41ac72c9a6e617667f1388fc526b65dd712e203cb036b63391e7411c18801515c6b5e44cf07327a4e6089a250a2be8fd60b2337d1a0f36b8e971a00e1ba55c6f0ea4998fcee504c21df2c2cfb24f7b22c2c867b5b44595870beb02946825ebcb7c83a090d412fca1f72841a1c814384d718c3bcf641e4f04d53f07bc2e823d3e0af91a3812bcd791846497141358c6dec18870c508c773d8ca70c7780c1f3d6577311c237e74a670e643882c1775558fd4ea6acddc936a2395eae65890da712466cb109765bc1a0560b044fa337fea30434852a8d17ee52e7e6f3aa5f1136b90d1971a03ee1fe2e8bef3140c78deb7006d3cc462de2e73324fe74b73a9b0e26506bc5d9583e43264176ae59d3b1fce71bd3a659794d2335636b8f121f1b6b6319bc626986dd967ed1da706849614b8e255fd2fcbcc091b760225542a8b4c6a63bb18a94145be7407a4cf88abd6c9935b556664146dbb48e1d78707fd7e5527ba73f8a347e27a752d31faa4374b0f37ea7f3cb3416e8339cad45fa5ce6209a43b00037e065f7aa622b2217649e30919361ea02cb1bccb6d9fe7c192ec0f29830246dfa47367af0d233bdbc5a5b0ae1feea8887b4171eb5add23245a1cb7a37f7c93fda9aaa3f34b6402422aab8d3c01ca12034d28180cda152f2063ae7753983216c2b821fdbcd9803a46c02308642027b92ee90e5a02c6a57f681225804f7195b2b49c917cd6b465e00e03dedd14e9077bc6eb0c233815f13cb352ec26dff7ca91858df2faba4603778e54e52672a1dda5072bb08f2020671e1444dbea669a419ea4a552351a75158e5e43645a83b801ead64a91f2837243733161c23cee309e89d27abe8d343c0c0c097f79a078ce0520bf34008dac5d2e58192d350e09176906d63222feae8e27b1ae631e1a03c8c0b5c56858db6e24a32797c9bb473421b171ce9a9eab50e46207431a69de98efb31f2b87ae3b9f8ea8c8b8483df128bf493f8129055e29402e022e228f91343a235c298d6f2bd82478e873fdc0672200aa2773b2032921aa9c694ea22185bdac5b3e10e71e4e00e66df8eb5a9ddf954dd3e93ac90540528b01e9ca0f45461a09baa7e8c6745105224399e1394b359934d35b2a4715481e80f3b83089386eae1255bbf8f7ee90c9453a8b292942c0b9aebf6bfebea901e807d93209d6cfc122340d04362b5821b43264daf59d2fa4754c18907952a49589191f1d36651a83055290ca54fca1782eb7d7e507d903a0c3d9f0375cebe2efa8a043b8cd77614c567d98b632f63daf72cf50538b3cde6253769ab17bffadb9e030076d03ba2cf304262cbbbfe30fd8ab17bdb257228c6a399697e0044d204180637564388ef0065561875d3f6290c1c36b0b83e249e9e7f2923fa6637f5921056bd7abc968a5e5a4ebd760c61d47e88bad8d69f96452355b1ae2d198770d19e8b1d4b25b33d187d81c810bf47aef36034c6471c011585b6df86f545fa0fa30182d9ea70901a17827c574b243b85c42f90bb305f74dc80b5d6b461a96fa5b897dfb90a17f6e29411415ca97688bdbb093dc822a6cbbdf8d8bdbd583b6a2f5a54b31768ea7ad10e4298b8668cba973140e7d40b2f3aa610cf402fe472e33aab0551013740cf451a3b609dcc8bd08d69bcf7d99761871f192fc2604e11a508648817ec40045c118ea39c2145db0bd057d9522e14fd26f73582175c4f7950bf8b7188c50ec17804cf3ee0ca8c719981fb4c155a40645eb1b48528818b02098d4d92295f124d1e01ab852153e4b9f8c0cf2e4c96ca94f090bdd7e8606b5673cbb1661f287471ed2775811c722746fa53a7b326c970258b45d5dc673bf324bc35209d6e31995bdc65db387ac656dbf8b6165366dfcf18a303a3b9292daa15ddbdbc6445696ab8796bb1630d6e5e5c3d40ebd56c36228b9558cf0348ae0d5cef110297aa0509d4a521dba0b2ac3346c72815cb729a45c98b1893636cd61ceab431fb1ec8f6e3093dbdb4187dccaca4184c4546c0aace0d12fa910d58693be85a7ff905666d7b47326fbb132fc9f4dd87e4ed2f667a67f28a5891796e41cb0b0357bb60cd36258023a974d2480103086272f6e83fda8404225cc475e9ca821b7af5c933e14bcf983c975489b6052153e2a3b46b2fcf355ee61bb311883f65c1c36d7e16206d0e4848a3d8ec636862226d5d8b7b96f95572c80f2985206a4dd0bd3d6243e6420d86bf58a85a4e770cabd574f9bd12ee344fc21a9dc6f370fb9574c0502a868d5398f1518919a0d26cd941e1670d7e3d5844bcbfe49fb6028229648bd06a9627bf8385c90b0cf852b6e39e05210583383191e221b6358a15fee9e9b1cfbf21f627f9059fb3dc9287108e675a53869990d0c9d47c4b84ced901f62c31d525c1e67c7c00911c493476ee5cb80e30ff3984f82abb7214d774c626d2efe75e0e289ebb0e814c5601c781f280e8aa3eda0811b4de9902028fffe1aa41b75bb78e1021e671629c27d3e8d21032b51edb6cda4d7dfb3c44a0465e801910439fb1c5dd7db78ee09a31bc973793469b6491c4c37844f99949d537cc4a802a9b89db1e70e405d617c7cfaa96b52167f35608586f121a2d30272a5a123fb805be43c24f26a93a2c1539bcd8258e7afd721f6db6cce26a99f4512b6901ef7c8361bbbe9910bb734dc045783a3248a0e09262f11d1e77e4d5383a8220dec69eb5482633a56c8fe2e3c55aa2d76aebac524820debe2428196475b935025100ab5ddb7aa5b2ede203cc4542adc157a981607cec38b6ca022f5da4ca383a3f793cdd823f9740559ceb28b95adda5d2dfd7d5fd0c752001a33f2611a270d45eaa3139b6964a12ba7c24e100f87ad3d327a25a26927fa9c487ae74d8ee2162416fdf85216ab0c08918083105ac3eb02f0b45aeb9144e9208b679220fe81eb7b89d822e3fb2c762946f8a7dac2d488508a0ae8ae8e1f244e56dbe7543f6951fb6b5dc830ebe545be867b852fc074bfb6a8458834390ce62d89b6593e261c2c743dbef9332256a0a76520847b4fda0fe50f7d993e8786062d20099c2d13baa4f23921422d68fd3f1daf5ba58eff4b1568207bd2495a13b2817a101e38d3440f94047507d237a757572fe056c27e6492c494dde8209ee7997133557789ec7ddde36aa13f7ccbfc1bc628524916910de64536b13970327e68a317892073378ea9ea18d673575343439f575bf9f511d26bc5000cc303e80ee4c4c9886765434dae609440a9c6521cdb4efca8f87e4fce244261dd7b891be6709332f9321946c71e44a698612d5c68a053af5b6a9dc94231b5259e21d7c03bd30dae71b0cbbd22080c76f7e7d6139f0c105277fe8c45033f9540f19452b396b1563f13092ad198df4e27316ffacf508e048dad7c49ec8a8607b200b9ed2a7db5ea8219804a4144adab59ada469c84b7b8e10f346adf4a5fd0b4f5dea28ff6da2693660fec7b83d5ccb620dda69be0f0bf9f3282f68922dc9c9fec8b75c96f220939f4c05019fec667025864d8b02b424bc6fe22def2853da047a2cced285f4793d23c79562b9c8c2034c0656c76237d28624e142c7811de69c40941e6b32286cb90a42c97c5120d3c34adcb4c2c25a39f6371655f645be7c00204b18688f3172c5cfcbd3867c9b35d3044d330834593be16ccc2196858488612cff0c0b553713293c9946158677b72c7a63be1d09c612afd71b1daf5e744e1e72b3f80b8bc53bb685a07a85a956f0937538222b4b5902f7350fe4ba979bef5044e36383093420b8d0cf770125079308912a20924405def8ebba4df3ae34244e198619ed2a68462841850af494f25dd20bcd8ae25547ee1e0a01dfbf14bae96e8a2173fcfa516597d471729ce032b8738a695fe2e890eb2f69975224f4bead43bfb292ace74a3ac401003c29b1122e58885c31bc6b10e85e266d695c09db35e88684c5d443e2eed085809b35c6f47001cd027abcb6a084d4b76c277ebabc6fc55074bd77129e66f4465c063f6ae8f3db331c69018edc2198bc858896ba9d6bd51574ec01c1866f1190eeefbf1de8ed1a52075d9e21fad975f2b96331d33dd35c051abeb6b7b827831ca2b5f8e4f1df812966f3d65429c6771644c5645cfcb3cc8348593c8e97545165963f29bc4bfcbf71cd963d1646b8a91f8800a7130ed7bb04e98eae430e1f0f6355d43201f6049b15ee639974038a86caf5b349fc139cec64f5be8d170dc71d28f34c2cb498629cb3fc0b0cb2f87c6a992cfd2e63d6e0f42f9114e060ede530e3de78108cd465f8495fc14f09df64ed7a814b39611c1b03c3b6086bf7d0b064ae6d62503fa721546fdf70f73fc8ffd1fa8fd5e6008dfc0ecd7e30ac009d51feb0b1b19eca203124b790c9e5c3eb199f9ab4f273727ff579e352e24529b007ca08d0b37fb8ecf4c507b902d7f2be21d51436be5d1934b22970d875f38ba31c5317cd1910f2941abbd8647c514d1f0ccbd2ee6ac85dc8b58b1da2b42be375e5305935c61b024412d8b462465e3f323785c49d2b3c1bd68353422f44d675f58a7a83ec0756aa656ab99c62fc2cec615fcdcd5999ca3c9906f292795bbf727c638c82f495bf06e9b29c14e737a4e92492301033733bd99b04fc01da5110acc17721ece94f7bf0215c6996e19db9aff7439cdb2c69c45ba708bc186ea6bbf49f0662708dedd852d4b40f5368083d90e448b2d1696d5f99b9550833976c7c96cc6e8e05b94d2684c0cdf29a341f25c3191128947e41483320691f7a06736b90b5d035ad84c967fe7faa8ac3aff4024a442d73d6cd26e138feda117a8df9a0081c94c794e6927e44fce286a81b38a9995a2c07394de234e9f6002ab2d8d30af2f866418a437ff8798523a5b3b50657d0f56e5dfc08a4f8e83df8ac71938454ddf0cf1259d3a917df9eb8741926f09db4f75f9424d604061f3d1d031ba6b67dd124b46f9a1195623a4ca8a4d25fc2e0030916ecffa749f3878b87901ee3f164509af2b39c8972d3e412d940efb52441fa6d43eee74583ddd12e53e464a6e14d97077367e6884ff9b430aea2aaa61fb98f40f7ada86cad0fbfe185a5bf4d985d7fb95e8e5a8dc5a71e29a98e865a810452f1d44fcb5e8657a323117d69f209db28741378106e14e8220a2e29c774a24f33b1e053037f13baed672eaeb129a81f8e5f28162709a7717b8bcd72d2183d47c23b83a6a19784aa7f32c70c4680fd4b62d517e899f265376b17bd9de027708b2b4bd72368d78fa7d86abc45003fa48be590c864863106273082f68597083e72eab1b58830fb8c9dc5c8bb76baee75fe46f8f526feaaf16f13239b9585e47a2945d514af4a6a79d9cbd880b15bf775d69262c7c00d2e95c656ac2dc0efc74515f03890b532ef0f8002dc648a28a58772d6177fd98a81a29844069a4e468985a9ef66fd78e90359ef8a911c9adb46a4f09bc695463cd6f1613051e8004ccece36e34257d99b20e9cb2475bce421ace67fabda9940c390b7c9536ceca715f45fea1d66f1e6b3c40978a83653c94371b5d24b4783bf4a3089a6a6403fa5cf7a63a327f18d2bf6954ea726546b0716a102b3bf37466dda78a6ef33698497407ed008b688d0cf0b352c0b62b828d3ec74b0050f08cc22d5ea19830bf658e87c6dc507f66a9aff7c70562c9fc4f8cb15b850daa4b0491be1c72e381b4fa529b6c29b3385138fbd3c5ed0642b260da32049d2653977f90906172db70afebf0eb7c8cf9f0250bcd63c3ceec566d17a043de64a0ec057e0045b3f6322d5f52c85bee5c613427c9340eee252d0381edbefe1d8d3c3143b95a38063e0d29c2473de71a9dc7bc2f8e6fcd73b8b230e07465c388ee755318e01163934b3e65879ae353acce8e0572828b6139063ff03e27d66f9875258b461ef26245c6353533b6f49909131f024c7c446acc1a678c017b9483cb15fb3f28b47ee45b45deb1b0172770962b405193cfeeeb3388f151d13ea890553e8375d332e0ff03f1e3b25ecc611da8d48a88d9b7b9685370ce12be09f2cfbed6abd2ed12c6a23e7aa80ed4b30f3984e292f610cdd37fcd633fa47e1bc82a16fe105cb64fbf4d830dd943ea00d18918945945f0d41753c2703778c5eebe4b0e9b1f64884596e80be33fe4b146d4425c2cadc807a9e45f1bdb2334d526ced0b1c775a1ccedbff951c2e7989656ec2f00a65808e65695e5b37a0438a5183b7441c043fdc96eca2faecef3682b4eccdcd645ee46ab3373465b72a023666711f9958d588ed9416468bcfe1fc509869973ece99ded5404f685522841272f77efa874e023438b623b04f49da5a2cb5202542431f9a885fee0d8561f38b8527e8eb92905f9263469557307d76c45bc02ab38a7432a9cabd11ba1f532573bf687b8a78afd87e9dd971be83476e85d21b8cef345cb7d3991a7a085cd38c697df31ecdc9951ee2187f830c2a6b97a9887597b1bbc25d1f11f2245edb919cc989bdbd52478279984eb75f8ba93c13a55529d1f2c4a2150745485668cb3f48018bd22d1f2d4eae24f793ef36223bfa366fb5c3e633715fde9aaf33e25e0b457548f90a25ad660935258cf3a8f3cdedc1105d678fc5d76169059219a356bf6eceb25bc47f62ea5d77f77bad716d79db05c2f529150034ce05b5e62855f7c44958abc65380bfacfcf7d04f0caca6089dd164129ab2a74902c980f864924b6eb5bf131670178ae947dc79506334bcc52d8d417c638a6271fc0266ca88d42f153608a6069769e863890f647e522b64602f3af288efbed360b8bab2c261297d64868843af1262b8a1c239eea10bb0158939f3b2765e5f522b6a86bb956068b8a5954f3742da72a662d2db075ea38ca60cedf9b2948c29abe06415d932fcd9957fa3b21457be87d9e4f15ba54f07a75a04ded45341769ccd74735847e27377bc93debd575596339ef4fe018191c993557efdcae5b96bc90526c27057e7ad8c0a281db23ae2ab46c230e3aeeb6acbbf18ee7427e72383f1d2d363ba7b2be34e7243d359af5bb39a16f82add0e1d072f2222b09f33675e183b30c02caa23657139f912286fbf7d382bb71249f0037f15423987068b9deaebc0fe6cf03e4a45e5c0a383c473be6e058b526cdb03c3e92da008de547db751605459c264b7b5172e0e0bcddf129085c8206c10cbdb002597883a2db87463a08289131ddc89e15a31efb41be3014709252f198a46417c07c769d34dfccd02048040ab9eaf0596d380464214bf9595981f0bfc3b01a13f695ea7201ec264b89fd5a5a164a1633aa6ee8b1a2f3256d465654a62265184d073395801ac1463ddfbd2a13ac0777d45f34813035c38ca784a3b621904bf04b2baa7e0efc9e221c490d3315d88332f161a6f0e19d0a58bde34b544b136193eb6dddbf40cad7dec711f73585c507c3b7ea1a27a8a8d8c1374049dcb45edbe3e273d5c5516f916bf4e0c7c2e61f2c317c0d8dd6d3cb4ed6e282768d71a5e2fe4471864329e4d2c739735df5bd27d6958eb6a556e9a5c11443069b43469ef3e8d9eb9aebddeb2f887b216f3e0cc320c353fcc3dacc7627f9daa14e577860669f7e2057ca1484af3d7a69387d172ec31d2ea0067a2ee88f686bdba99ea2401b3f12f0a7672c5101675484d197b91f908662cd2e187184167feeb5fa682b11431192ef7abd7e2fac6d46c50ec8182fb22a7fc4e20922e7c5241b8a719702165dc82f65df9f360380c226e8f09e133d5330bb9793998d8008a6db5ab7a6bb09e3cf84b56210fc50f28c5713969a64a62a2bce62a0f157416e636988e0625cf3d8c45f7d0e276982a128bd579d6302e7889cc9ab8f83601f8238484b24a3d6759f7d76d81b7ff5437fc3b4476529852043ed8ab9f6fe110e38b0dcf49c554e34e00f4cd64db3657398f4840f4622eb75ade5a011b1ec4a6c4b7b833e8a807da479451e8ebb10afdf43b959da24a38652d0602f11ccf31376c3719b5bab1e7e156e20082bf1ec807e0354760d38bac6fd4f913f8e72b61511530698935e001f975c17521b62023c97db206b47b5bd38f7751ad7d4b94abcae5809c48cd1ffc4a36639a63644a9ded37143d43c1e4d8df8c859b912a1c63ee9cae6eba146733eab84c797c6b90d549a3477d06b9cfb9743a25841a089b4838980dac299c1ca068d7dfadf7778bf01d904dddfd11df553b7afc1e5bef87ace6c0e4b4a8a86fa3dd626ef781e2fc9cacd215894f680a0d9b6211e2c10f6e37746adc93c7d38b9c48150af14805e80bd6f64e91cd161c53115ee0d41a9a6c3af786db4c08e836635e2b793c748aa37475dd55593c36bd83447b0014f7c7f5f24bd265c2abc960626582174597b644c8f0da317cdc8294fa8ecc975f690eaf2fb9416fdb518cdbbe4d02b362007b2f239ea52fc448256abd6bf9d61866642e74a10e7bf683722d6d890b41a4df2b0bd4decd29776ba32a5af3b4626046de0c6fa7e3d6093db0fc2c3b184a1da3aa8774bc0d74ef9d069f4e4f8ff541c610c60f982748ca14676ac23783ea77cafeca749428e9171f8260f214564410eada148ef6c4585716964c94d6389222aeefe3aa9f2f32cc5a7502dc1c5233dd6f744e3c9b84a7aa8510ee1e030b5eb760d90fd76c23ae13f4abc002640a62d35ae1853c5ef3e07aca4a5820f340e4c08e3288b4b42f5278dc7cadeeaefe565102d67dded81698956bbd79c2bde5c6b74b151fce6d0d345d9979e15a18cc3447837b3d2ad8bae96d2cc7ad1150ae93e92be418dd4b056a2990b21705d98ab30827a7068e06c0e05a38e175f0ad6c1736236073d6a9ba316b305aafbaa25ca790ee8c60a3fbf5f5a1faac8f18efed05f9d914302689c894bbf5077dbe8eeca8f542c15ce76119f3afe09b1094723eb7c42cc4581bce60829fe820de8ce5a0bb13427d1aa034cf0a6c8cd41cbb2598d72a4800e92966a692196a04f43b56dac644094d68be58cadf09dbb726e1bfdad65fdd0ec89b0084391b911cab4ed3763e652a4c2940273f404f71fb8ba091c2a7b7e957f372720b506967d46b1cd82bcc62f7873b14ddc60ea6b3e0906a902d541c5f6ec36a4ea7edfbd0004f45a3578f23fab56f538cd42673c93f6ab019aea3e5d672b55af3c8c3e11d9128fc7cfbef6eb04b62dcd00ea8ab980b94616af838a500b31f286be8a839b0c45592d56b26d840739a674cb1501f4e56b549bfdf2631880a624a01a317a80ccad8a6ed562f67ba2f80dc1564cbb3c00be6188b4b4fe42f6f2e514b56bf4afa590ae2299f5331944e766b447ff76c7f2fd5074baa9fa5bc2aecfb5c7978856a688968e2e270027739279db6a24124f81090939fa168ac8dd672a3780a3afae96b5d8d11be4b81cb49539a3d3238261d0fd5d55c2cb9f6bae74d0006f509aeeacf616536994488f0451a3c1d42245e4351b40ef524b5d9a7ba3eb71d21a5bf53bfdda89afd472fa6db8da82256eb6781a9eb948940a17876ca9d836065ad6e5754ef32321238bb59b9ef5e09bc2cd7e43e30dea9f6f64792f3fdc3d61d6b03fe1e2c75cbe0037a9793e4a56c331106ac46e40e115aed2ead7fb45f3369cfdc3eaeb5fc60a392b9634a02c03fdf546d70d975037c4322d56c54c966f744520c671c09c4d6e07fe80d48473eed9071589add36b74e325cecd515b7866706e7901cf9d62d5cec8071a7105696ba6a129a0cf3a89e004ade10abb91e861d132df9990841f968d7f3a667769ba1e97dbe9c4b980659eedb42cc5060dd11aa57877e19560d30058d0e2547660a35808fae80d7e13bd416e2037f96e18767b65f7ae1c8f02cdfefa4354a701ee88eed1450554c2f825a8977e6cae95d04f8e12dedc7af77ac60926adc9981066b82c63b81db40c77f43e466f24a789583476c4df61b29c1591d1498ae9ce42d276871bf3866a939d0f716c8528245878abf6696e50df169e5897a6c6f8a33765c081493d0d5b44007c64ae49e3c10090e2fe261e0d280d421814a063e85643d80f3d40677e266640b3a2a4afd747b34edbab232b3168f8735733c2ecb327aac36bf661d7c0a1c4f3cead464a8525f41fdfff64f6b50c5810aa462659e5908744cd19c41123a5d29695fb59af777578af1014f5bb2f6c90bcc4367dc0977f98406110c33aae469db8ce2886cdf669cc3578fe91397c7a1d1cdca5a1583056ef52fc140684c2a0bb5165ddfdb860af52f5c5b091ac5bdd8ef9081047b1e0e891ab4af4b1896ab87ea3b303a238b2cb11e02548bc63ad6c2c1906e9f219b195702bb727124aa4f3a03de1eae2c5c136d9bc1b08e21d417613ce7dcc84b3d2bec98cae487b82c09882218dcbf8884326664424667fb579699f652966284b76f8633980790eb78936f3aed231583b1c658a79f3406676b3724d515877d60e4085a7c19174dc689579e087f271fa46d305708d46f0e927aa43f7b5d2a3cd413d7a31d5a39172fd473943012b12087d118d0ec1c7507f44983b83a2e38a5bfac0ceb5bdea55262beea3184e22142c046a6de75ceffe4d02bebed1bcdd8e68143919f1c3a0dfecec132e44578a28426a5e23a3baff3ef9b8ec00f38e1df3592304cdc45cb751222ca25a869ecb8ea27f4783a1a8fe24f9d28e04a0fae3609eb8513ec6c54c5c3e948e046f7a485d81d5c17dfed5866a26f5d6dcf3ad60e135a0d224a77065331ab099c87af23d70f6d9ccb7ed70ae0078f1f8fb592fbc7a8c435a76077d1f13f58fbad362bb937a6d0af350ee0a07ef1618e341e22006f7e3236afacb0465f380c855cdb9878ff51857740a23a78789ad9794c511b4648367265394d7ca13ea37da3376636ad7aac7a18ee85a70fa00ec6a05198fef32a2a71adf5eae4c8fd0928bbe18c9e09075df91d47a526a0d2a9fbc0db57bdb3fb3facf2fa7369f4d84916c7cd01a5ebec54e5846670b6219613d7704bca657aa8e407e0a35fe19d899a4c68e463dff2ba91d93c6d173664f076a004b0436fa8760be054ac6b042663ca064d48dda7a4b60b23cedd98022095f2a45a2dfd68f301ae72f813f27dcca21f7f803531798de5e611044ed89f79268dd7f037a94ec8e5984c9ccb94fb0fab7da54f65158702b35d905b05e612891ca0ec05eedff46ceb3b4ee3e60af332bb650ce52f7b366c09dc79b66b58b4b7c32d72f8cec9313a7b7d585834cb91a0d65b5f3a4852d92ac767984655293865acdd76812578dd828ecb7405bd6356fb3ab462a5a48f622a923002660a5d96e51c97e08a4bf8eb428f8095c22c4ca860496143b6ba423bfff169e6985384a18aa21f818ae1a444255441e934c474b11308ff258c24f84aa2095b0cf76270d0c5efe5f18f5314f4bd9239d8c7894f7418c32a3d3c81550cc12be57e8979d70c730e6ee6af548395c48539362a4ce24cd702bdf37bfbeea893b357e8492e48c4e3938b67323294c09d0f60b57124a7520a640a6971665afe49c564a4b185e60a764efdb5edc4aa21431814c7cca21c29fd774e61c2f9887a82885f5ae9db4cfc2ca5fc938866d914ce0228d34e794b36ec8619e0f0b3965031a73c2a0bf6d16585d03dc914eba26295e5edb4379a897b460b16612dffa8c1892fd6175547b4ed87dff392946b9c1d2cf3316d674dfb81c3eb2bc7ffae20ce641755e47055f1e536674c8198800005687890c686d2b32baabae194945c7b23f6463587566626b1d0135c7c654034ec858700d7248d0246d740e1005972d9be4ebe2003ba8532c84dda55b7325e4d4924bfe21a2097cb806190caa4dae85ccefbc3a64f228b7838e52a161f13c1ea5c294bd301ace88f3d2def8897d3a8c7eaca6e4651d463ffecbade3f64b6036a87f536e71a6e6fe53a756db03b338731eb4c9bb084e1082b3dfbbd49379c0a79d155b96afc31913184df3070114197516117261944f9ba22186b4188b91a61e9b144b2c46b414e73de6059bc94798a863665266a353d4c902ceeaa3c969f3a16e4010f0f9baa8bc7276a4d0dc73e0e4e3610552e1884e701096beca4d557c75c470c1ee18a3944e067cd2a48849cd0563943112c8cc8734f3078544b060400564c8bc2663269d444604b0d014d892634edcd48dc07dd31d73d1426ede3dcb724df8e0fa0b12384ff9d748d104940e239cd5e53723d5b752a90994a8e2ae7891d70ec346c309b40015c2c3797cc30c80d986aba8b6cc0f2d2778c0ce2f413ac8c98f0d8d898854530ace82b259feb8883fd1befaabd614511b87d2bd28121488a8a7624d09c4455401f814a573dae35cf1b2051c94e4d3d5e664b6d780bb27e7e5d98e4e003a9ffc330779490cd3b829049b415ce60642a309388023a82002cbb6403e2c0bc9c89ac21e0d8bbe40602b4215c3126171e19de17f5a01085271c3018c4e4fb22162a7ab1b827e8707ce5add7633e30eeff982964bbb34063cb98ac0a0ee9158a445c218a42446a5195cba3f15f3c995e238c7c0a47da47274c8277883db371ceb322405a1191d93223a6c2211f110961a3090642ef1280c188bfe83d8eb526acf20a297fc6697cac3983c9848f2af241f940355ba2a545c136cd063669d7c6a4b8242e8c36e36fbb0916cd89ef2d20c97164c11b02815da1a35178bfba4aa6a4b0a41d0fa70fb9873aed758295f1867630f450985a052a515c63aac91eeec0157f2f571bed53b892fcc36d271977265bbda205a21654835b3e113cde6c8ca16189a6c0c957ee6343302235b10fd0a891fab36cb02d0cc48b95a1037193250d30566b43ab8e90e5fc5dcfcb610379080a7b7f10639db298a70647cf3cce1be3c95740071a799ee238a7e09a1010217b6c413ce767f6f961135f6df5745840d213b9812d2d8a6015651f7afa82222f37901905a8ce2f6f65398d5c4a25c7e721cc5e7c933121b4bbc2ae4b0885480e1921035136184e93502cd9c4a6f41bebbec6c990029ff1a3676832c35206da25e858f60bbea60e76463dc56df84e69da51f557e184c183d2fafac22c281cdefb686a15389635030f9b2e712e71f6375ea6f856841e65e9d32ade96f148e8cce6603d2ff74e263d251a6dd6f653087406550deeff39a22d7a3707ed3d91cab72d1d862a27104dc9a6ffdbf74e580ac88c14cab5c2d417540ac46c133a5a9c2da742238d7a4459312675b1b836405643d2fcee7f307a82a30919f9249fcea87142b15c241d869630f04960d5a17a079f427d207d87eb2ba35c6714411b35b5a742a839ec8421b8969e496f6d1458219fb9c83a3cd255d0c8a0c496f46861b40c4aff853aac323ce3b3102d8be60398c026e041ff64782709b570c5e420b56eacd47e9bf7b053864a126968ed858da78a3a13c6a7eb702180dcd12941109912ef52818f8c6409bf0b110e30059460f374479d37fce9dff2212197f7b6183f8938cadf98d207ebe6a861010d483f36d1635d5fb18cdd7ab2bf33be0e6a9e17983aaae1f9148e5f10c16be20b240ca4c7b844d431aece820f736d098b3913f2339cd5b9091cc0df2771242baf0636ada1f940a3b459a3e2cccc007966b4ae636cf4e0b54873d9307d6cedbbf86dc8dc005ed211724d499dde61eb42e8330823354934cb4743de246330cb4a0e469452468e5017b20607341c83a17f45abbc419a72dd38e56beddc4353ab5135cf8b7d5510e315c28c1299429b0779d2c13a709cd87fa8a55eae0449c41882d8b444617c40cf11a6d5053ea7ee3321026bf66a8a66f4cf52c0e9d6184790c4d21b68b60cfab706c34b5c7e1937ce4841e7634bf0ff0e4b2ebeb48ba687802d3204b815e549454a28c801636ac7679918394bb17531e9d688a9a5b877d6df7a9693cac6819751ddd631187dc15fb7b2cc5f85269db309644b5e424c06ff0b11cbcf8586ad1c7fb181f8bb051c74d77954ab674dc1762994a09d103a89e3cb5aae563fbc7c12c16fb5838d72f0ff2daae7d2c6c80362fc653fe84b226ff68c992ce81b5f95e2ada385b2be6394271a74507b614fac8d0ca1df500a8e8448a665d60f98a07f90d81b4fcfa9175877ae9a74c789cbe5d11f8733bb167ab40d9d86c6576a44293e560685dc55124b86921fc954b31ba9c29521c4bca815485539ad42bc13a4a5638e0ae2804a74fb4572e52dcc249c3cb26b8ba3115166378711183a85cc5bf48a1231029f8422f73be296815ef013895f80c0960279dfe892af4eb490b785d1a6b8077455cc8cdf50e84fda6b33e352fecdf2fa537fca5f03319147ac5fbd6e2a2080f9fe45f0a0f70d3c244e6ec38b666f670751fcfaa463ba697eb197762451183d5ef337fd0072e06bb84cd3852324f2cd3efba3a51f297ec6a8a0d92738f7e86b6664b3e9664e1911c1ec960b0e491f08c3cc6d27feb835abf04999812fede61ae1429525cffdf0a044b9702fc829b358a9a59524212d6657b5c0aacb7fc8fd6dba743a195c34a6fa32f59e2973cf2afb5fb358589309e7c583786686f9b9e9796c15d6ec9c186138c7b5a9eef2a32d4c0423c620f5a8f22da424bd66eaab6432c6774110ad1290400124c0843a5c3b24c2cd0c10015fd5d73481300a1af705a744fbfc57d424926dd46f17d7eaae0812c6e6cb4d1bbad16307435a1b8ffc9cded254299708acc0682b5f48cc4daca92924f7670f3c4b78bd4452a34ee6340b530b9720040fb50fee4900aede85d1b09830b8e7b48280645ac8898272b6b3e8772a0e17c44830685ec871e57e2f9e02ad4c6fd296ad81b8654a8741bffede199739777642039fc15ca6ef3468c30ad1cce3f0682bbe0ac813011e2d281f9c68b18c1aed10598067c01356bea8d78244c2749ba06a00c651e5102b74994c6beabe8eb7bbfe436e1232b79d61b7f531f39b7fc91a11ec1ac141fec0101409cef073e6d41db65a9272498506d3e7001025e3e50e414a5097e33cb9c93b791af054b52f378840ab3c201f647068b8ea759fe770262fc483bef78eb9807f8108ebbd1fe6d870fbd437ef41a3f0299f0ef6029cdc0cc1cc2d6452a10a84b8fc512348ee56d91e989817eb397e810020204db7702a88ccf40220153258b00b1942d9699a70c6d22a1daf137c047959c60f9d3d6ef51e44727ab185e1e5c5a995634a6ed34796b88710235874fa7c3753f1d3099077d954ad1248bdf52dcae688a1733a91dd7a620bd81194b729b80644d0135c66fffff86461faf9f43b058ba1e21ba0a48666819da785d5e4cb0cc1e4a520e4dce70ad5db7b12691770ae1ce3ee97d187af344f6200c7f0bb7d856675b11e18c0f9461936e3586bf4127995ad279ea6e0b2412f7fd78729d191787768d01bd96af3d1d4d71884061a817b07ab67da1c36fd236c9235005382b3dcb7c2ac4b484fc7bc74c028927d82ce82dac7b7cf7b2a923a41b11cf2467a6a5f4560a3cda91c2ae2d05a9cbeb2ff04f3e6d30bd9941ac63d4c764e4bb16bfcf6edf2d7a54165678b3cc22f9b1d79e7addcafad4e5dff596b37c79449feb6c180fb0764a69c158d6ba85accb8736020d2deab5e7442647c206428cb3f52ecb5d83e1dcb1f0c0fa312dc6721c66c9815db6bd6ae2073ecddb6cf82292920a3cddbed898d2a6305a031b74393f22b9a5e099385b2ad6e55ffd69bed6d73059626648aece9109889d83563d0a1015ed36c16d9159a8b201abb14cbbbc85442242a5592ca221407457c5ca316e91edc9b724e7d128c258c981d9c45aac6731037bfb085dc46995031edc7e31c8c6be2ed4cf934888759e2357d6f4ac548c563334224277bb9f563eb32e1de3d5c00f18c26e8bb28a70c9880c5503183e9c207a98c7d976a093f0d9045806ee7fa07a1befacdebdce0d405e51c50b76b53288aa3889dab5ce377bfe2328277a1f3ed50f55b28c8b1e09ad6e7f27cdca2344bdff5f5881ae06ed8ae972bae38c17866fb02e83b3a3e09ab1da25dc64e74b05928a4b0c78e39b7fddf265a110c9bea37d8cf7a4f35f40d4cda724d9ff058f9f1cf6e6d5fc6ee1d266de223e95fb11d58b7dc92aa7dadbed0d62ee4c9467eec20a491456d6f57ab0123cd9c0aee32e867034d10489fe2712d4538b8236a15aef84cc838d990d89c41a9bd8bd70bac2186d3d431e4907612e47518367021dee5246a6df874956676b17d8acc8afeed906904c66b9540db8178f005c2432db1bbab32849348d4957e4c988bda23f788f54e78e076e82a495e0c43e8b87d956c2ea0aaaa9e79c06907914c1180b5a116cc2ffa430f86b54e4248d2e802739055aaac04510e8d7bce15e8791a5f0cb3c06bf90e0436abf3b0c865aa7d4d1f367088e178d1af181eb233d7b5879cbac76962f1ec9c736c72f4fb44481f14f682aa0b261bfce16e33dce7383d2eec27b57acc8bcc33d24bfd2386bbd233bcb3320a7e067337e609acb1646b3372c1d809589b583876b433f91a7a3284d551628b404c98cdf48dbc361fc70e61eee6d9e55285e35888105d1a3f99a1ed22c0dd12baa112a3f6988d228bffd219179e1c48cbbf2d17a8f7cf9776fa30f86d8e13809088bf904594962936df14609fb066903c208a2c392344ed5b3a941cbd068c87d7f1db7114e067c331f4ad2297fd8c3689a5981fbb9730407a525fd8050a928c88723d8d7a495d33b94088d0dead67d7aefdc00af8138094f24e0ef38124567f9df882a4e197a5c79c80f6c7ee6731af8e743236aef55b140e4d068a80611c59589a4ded7b482579e482274d867ea44a9c5d0031e8d63e53898e1cd0c923a23eb1174805b23530a5c372b4af1cbd7b450c78794ab5ce33ee40a7d318cc91cb904fda62a2b8558343b00e8e728a9fca28653f68488785387a2af72dd8df1039d8c18b5790ac6deff154855a89812ea64702da4c6bf3ea9be7644e4b195ec6f553f2f4124e86be489471e015ba0ec79eb251e33dde023cde46344bd8eaed448e0c3f7a26299ce5ad798104c3f66ab13282b732411c18a9a40dbca53a6ad495d32e85f5bd0835f73c88f6b16c116c2caf66adfb657dc0e1e27b7c31549e72324d6d76e455fe79f9bf6c0ab741cc722db13595368914a556d6b330a026186767ffc1b44ca743b6fd8f234d94758fb2bb75dd99e623f7d518f97be7621fef66d42cc1a666354b176ca145a48fc6e9a878392da05adc2ef11cb206f05c64218b8b649c2c5704153fc384c2225c61d8958b28c438f258d246efd5b91e4853aac2d6681f33fa8f529f20ec4be5f412a8beb9b4b05e3ed95a7e2b8975a55d5a81ecc1f118031b4b07d03f7c6efddb37a8b4df72f6fde37664fee665cfe6a55c3854760fa5235fe129b5bea670630910219faabc3aa8a1bda6572208a3233e962e6e602e8d1f189f8b0b1b334b18212fece3e57b99c574f47617389f5312f891c6d62c6731cc341da66b6f0e5b0e5d34114b19fc7ea093540730813d4e6782955b9b0f8dba5a9a29095da0aa9a4d835503194a8ebcd31921eb11a8aa1d3e4cbf57de56034d06fbeb2603660fedc1ddf364be5c2d4f0001ea3c668ced2e2ab4765d5da23226956b14b9c01e1a4f8d114748f5b997106806dd65a79b4bdf26356aeeaf3f2b20802670a333380827951d9343e97c1eccd542b4a11fc5a8e4ef6504a7f3f29f82d4037e80e5c70e8420361c67115e22eed04c0f2daca31ba75a7b91de09b1d71f56383b8c21c9e749f1aabbede7c20bd101b54e602659426aa8601cfd774c53767f6561c3108894ee78233d8bea19a82a4cb48c516f0eb7e0d72319a4419983192bd8bd0ec57c566dc068d1192b0eb41cd560108f50ca9e0266d1f246c02d42fdaa3a0fab5dcf84fc0c7681a0a3774bbb612ba3dccddf7a26ef6a9103cc496c9609a7feea91a9b698f5ac537b27f2010cf2336975f07bea96f7209e096c6ed513fee581749a3a97b992ed69722c74d97e6a7f59e48086728778a64e452beae4dc4faf9eada54b91dd59a5c029b3f8485653c290844b24cffa5046702ad88000e10e2b2bb2c82d3913bf4bd8dae3c221c5869a6b1c8a22fe33cde5a584dca92c5871ac4e055a22ce70ab20505f3b6067a33ff1e50b9f6aae45cf10bbf4f2da0ab84a583fec053bbf468ce3a07e59c0b4700b1d8ebe948d8eb61adc4f178f45a417ad37ee48f391152fc2d2afaf1519d8d75195c751d960007ef47ad02dc54a9ed1dd837858a1f8a1631ef102c90652c5720dce80595943cf977086e8a876d9d71daf86bdbc3ffd6d25bb0213525703a2a3836eb15bc9206648402565762df24af08314706b9d8c47b6ca0b15d858ba88b5cd082518f985f9cae8ca71bd08e1c391bca1135ac9c545ac7b1dd45c784c56e167ce25a43501570777c29d135127dd280e7336778973b27eed0f1b12bd72d79e68c4635a4a58a16a290912d7c28a2024b4ec00a2b4bd697b6d72179d412f9b09acf96f4bfebb0472337b7ccaec7dd70377229b37fa123dfd5c96fa22cc4c04063dea548248b9eb5c7e5753c61c48964c6d30d96b00e500991f2f33ddcc0bbd426a76d9e370bd5d328880472ccf73cbbbfe8967edad40d0a89d4b9875d6e995dda4cb385380e8cf6198d9ee89b59646ceb4c363dbaaa72a62530e9fded1dd8a45f08d47dd266622a700c77fd3264f43a1e7830bc7035f09b04790387f68f49c343522b1157996424c966c0be6eff22912ee0f4fa9a2c1a559100a2a5a3056005d2c810bc9c5b821504d420d588a8a902f18c57682c6373e3c8d367fb10dff1df8a58e9a6a74521c0c34e166f1a8bc1acfbf671edcff98e85359ec727a4db1b7ffb113873520773f46a99eddcf55120122467e86b2cee87f011e4d62361592e01ece0019ee9844dfd2dbb54a2a7b26d84545b7c31956a0b77d1ae9cbf5829945e2dc14fe30d485d06f330b08be834080a284bac257db59d477030abe94b50eba1b46fc71a9783a4d625ecc88081e5a4d5c50e512faef9045cbb0d3e047c6235e95adf915dae18e8d1c82e2b7dec82f3952f302aa91c8dc38bf32b6dc02e0a65062a479f47412c4b4c57fbce6856793bf6205e5aedeb0cadf2254d5ad39adf700da2eec3733d834a247ed2df87993b04db71721a7c719a7f7458a21f4239c0f7a4af5ee6495d6451a3b6b532f7f90f21be13d923054f836465b18a8501fbd8b20fc31f57e1abfea0d2e0c0eceb13bf299bb0e7cb486db68d11709a102285d179809469706351f00fa1a95fce1139df6c9e9b8030e32cc060450d337cd42ce78c1ba7ece10ad0025d4cb5e3349ac05c11b1ae90158a70261d7e0de0f9d143b3c301fa3d4003778d682ad2e3d0c009bfaaf24b2100aaeb449405891c59b927948112a1668dbb5ab7e6ba87f78bba45151b7f430e6b3b1d20c103ca0b4a9e41b0c9806f11fbfa65a0cd0a47ef44f34ebaa578042dde0740e6b03b9f28e26894638902f6a57e1fd802b8feb94160368b84f60344540872f61b180634e25c17200103248e12146fb20e42440e0d2c2e4804fa8469ccd73c3b7d491c70953beb5b01cdc166ce7929d1e988deb4740f41b19661b4f9a9f810b4382b45de6a418d2d57903888d6cb440013ff974664237f34d55934f4734879ee339210f1b91ab17e843cd1b4c479e4a46f2bcf633a4723e52145fb1ad7305bf01e558e578b3260ae71f69db9334b0ec3fef418da249702fcdb64356df3cbae8de458579ebd0afd6aa2c29890042130535597e5fe82606041eb362d3c3c956447aa468640cdeca1559aa2c9bd67613772d8ddfa3089784b65bb90f682870a9a8ac7c9da0b6564bc013cbd7b964be6c0cf30e454fefc3062360a46ca1182aae9f40b554cf6eedc991aaf595ea43df1f4fd314afbf649bce2ad13dc5cc650c4fcf9dbb893842a47af24e04972732bce8254603d8d88b1511938ab836d99d25174a21262eec31a5a529e714afde0115281d7332e373d942ddf5f7ea23c31b6c3ff8b9bb1328186bb41767a550199787b4c2fdbc396b3c54c017530aceb8002133ceebda6458a647a5f0021ead7ce61a47fb9b7bbd2c09324bbdc312002bd18268ffe382d67f2f46480d4aca5032f4651f96a8ac5cc465a450c7904584d2649285a47396f409f411f8e1acaffd783629efa4d2f62a38ca9bb8f3ba7feba762991ba4fe227ddda6580374970a52888baead6a24f367ecbec2d3bd3118dd589af37cc5b73292b9c30833db8fe4e76d924dee7c76658fc8e4da272757ed3c8dc9a1d3da6e5d23cc9919fcf105c247b33e14c18488e3b0e2ac2999fd841c8eb0de1f9265a3945d0cb37fdc588907c75d203019ca38f03cdab0a8fd0242ecf30a164d8a92e65750d4ca3a4a3c6779eb89a55b9f5caafd56a3491d7d1f5fdb9c3f561f5e346b3f2aa96edc8c4f402df78f0616109ac7dececa67b249309414efcb299d191b76b0f41289c3f8a600eec81979797e8c45c450051d78ae86a15c87e76e2fb8faf0249d32c696d996b8471ebf8fef877f0a1540e8f0fe13f6e748f15772ae6bbfda235056ceffc01202ed4cdba83f1bafd3497f0609c9f0135d612aacae41b1ea1f6f6e7a9088854b039cc61ff542db6a2911a5a4dfb870faabb96546900212b4b426b83801680b77059475efc18120f21bf791ba8a419c2fbf6bfc154ec331e54146a0c8a225cedda9e0c4572c98a92f05e81e17e1804a8399c87c7cba8a94f46b60928cb689c6099083e1b76d6256124a560671b09b7660044895f2aeba1304242e2816313aec2cad8f7cfa6d6e215c623cbcb8090df2840198cfd4749adc7d08b95c61a4be0551f84ada4e383ca717b72cccb4cd1850931782395f433f115298f22c8e03c5297e72020d75b5bf65ead66e7184a71cbc126617a6222496c561bf8d28cb5b0bf05e24b7892af69a90fbc62e894fb0ee24af430c6405eccd09f490111e6d882971ada606aac989026382c93f2a3c3f9a479bed9a8065b3ab4662926e98994bd5d2f82b367b3842521a6eeae02bac22853828d5f41e066020591603a2c353ec362be7d37200999ded1f93df977f886f1a88b75c65ed3196d84d798981dcc224e1f24048f29b9a463d021881008342853e1785739599c639a162e60e12c8fcf8b525bbf7b29af1707d996bb1a1c95d3f18ac8c3b0021de8f3c15c8f8199814ef7b984f0f3330ab8c95640fe472e97074e3b4708ed1006a9eeda76ae2335767ef020d886fccb9cdbbb45194b4cea13442224e89ec1beebbe1004da38a3ea5b41937b30dc01ed5101e33cb2809f6f8855d8b98dfdf82f589f99f836d54e16b8766989c0d03e90786e39986b1421d1ded497efc55443b43e3c1d952330dcbf1e6a5bf47014d298ee27911d95fb84c956de70c028af78284ee4896dbc9c9596774fa638082d555a5338068395f78e9e3fe88c422fbd36a642a97d7ae530b307eefd2efa19764ecd7cedd9f3c108fba8d826c40dc33fd87a6ec21be9961556103c15feae354f94296ea90c02d8a1a8e7e8330335568424ca00ccc68868bc2764f62bd3c642028910b1453d9f9083a069631445c04dffcd80a399628344348d26cd55cc49618d32a4bdc9289dc361b15688fcf5ae78cfa61053eeb69f9c064d82ef11f2d7629d3f6a209c1a3b7ff29371044113a76ff4df767a3a1056c7aa48625071832512ac9e6a6f6db706d90bfdca2fe7718f67dc228a9a0e915ab89b0d7bd64638a1ffe3c944eac9267c00869d80e687b60587b0a14e5a1e488be6a38f0896c9a607222690eb4a79e6f5204ed8e32b4e928e83b092cb422034b320d66fc9347176d0d5dace8dda7f7f15a75e50dad7f0a8298a5ca15ddd64b3a29404e5e775392ddd8a63e52d6b0e5beba135ac0b80744f7dd8e063031f4fcf06436ead2bae7851c07020308b6e096d7cf5573cc77cbe6f1a1958843751879c936fdd38ca0bec5482c04e0a4a8ee0fc3c31e24f775c85ceaf808c4cb4fab902cbb335b4b0e3a2f620c94bc55c59f1899f3b6b3a260366c0f49166057f84a4b1b4613c5029fd0da433ab4ab779402c9d02447086e4c8076c7d2f5d4e4683cc85e41c4057412865bb48bd4fe4f414c9bd21ad303a70f779e7dfcbff5c2fe6bafa028ee16ce6aa5e092bc39dec7b74f7909d7f40271f772a43ec47587add77c06c8a9c604b45d2da3ac42fafc88556b719c58543db90a3425595f3a1804bb1d0c6c9d30560400bd6df388df759b8bdceecbb2d04e47ade97273d6b98a906af5adcc2fa9a17b3cf0bf82ce578b015ca4c3e0a9fd06b5610ee0195378282e57b8dce1196698ba4eb7cf64ad2b928e55d300776cbe3c3e36b2996c0f5a9a19e918469f27238bd98164267d94cb751c259063ed1b85a33383c3aaf1db680012eb6ff8f5df2e63347e8e25f7cfd90a030285b95894d37cfc95ab707a5fc97292c949613542e72e002c9020ab8c424fd86e94db7107c28ea537f192ba2db779a569c67d9acf437380da3a84ab5a5bb953bcbd9b3942cff18a02966c10a641706595d2780db534b37bc80cfb4dcda88da07c5107563d2922459242fec40343cf651288820de146dd2a36c31a2bfcd85fa3e60b55b13af5d29f69a3099c28e9edcd13a88df65ac6288c39ec8ba65931e05c0b06bcb3b7a316263d1dc641f78b0d34822883d870bd744965b9117260d6dd844564c294427b405f7c5ce15c46554c627d3067955d851017a85ad9b4e7857f5a723a652f7d9dc3381502e5e1421a7528db08a4d3155fee0e851aff1d4218d7065fbccae7179b36d1cb1b4df4ed00de51f0b8566953bfd230e992bf003effa9fef28f98d9c034f89fc165f619140e91c663397e238dc6c6696cdef091293c85c3f95f5920c0d1eb56f26e4a1e87bc9f14655e9ec33e2e1906f09e787ba2132263838bf6b00ba3d0096e34d1c406776e148062f21a0a43600b365bf96a614388fc6ceb0568a1c82b416546012739a800cffb964a450f4ed634b96bf6356d43084036d4ac2acfcabedac6eae2d7bb4c6874371730e34ebb6d3e2397539d293ee276590551f93e8cb49e0b2599e5a73619cb438c28e4f309a08603480e65445db91887eddaea5b251eb8c60d71f09828f689915f3214bb8e0ece1dba746102a6a0c9abe164f4ff542c6fd7809d21cac2835a3e9c8847189e26c983f9de57a5266dd7afb40a251b5f335999b596e7aaeb18f15d35133c32ad2d372ae64e4fff7e41dee06b3669b1b898b49b2c2cfa85d8c14fd87c121e1eb1839bb66e396aebb03186c324ade587bcd514d431a8c2525f13bb9987bb65226332b4f70d0d85126f5e47d4f9f970ab5bf2903b21af28eac4f813246dac5728fcbc24e1fecf60c02bf4c274b4436295e85410559d6e47a9e464f3238bbfcac77c71eaeddaaf01bc480f0220bc8eacc0d276a825acdd787b1a491b05d27689a54e465778683fc77d23cd32af481e4014f7268879e1a273d513d4e3f9215fbc71dd4081b91a1e05dba0ec8dfd53adb50b4174e65180cdc8904931460069b7e0f7aab16d41a0a3efa9de4c6bc549bdc1342aed6028978a6769349cdcead61c0ae3319f29860188d57abe356062be7e04367e2adb3ea0b9445502fc40288c6bff964953288ec2b97a72ffc51d33427818b0df14f93534bd2378b9ca1823c9eeaeaf36bb53085b5972d3154960afe3b70070309a7fa55101b0033c5c3ce37611a27028995fb71ae0efeb49e8a2a5d4af9d1c2046afcd4a57c69cfe5332f8f6716c618e852a974984165784ab598e9d168ae8b58709cbecee265bad7344b2d9f2fa91c238179ca61ee9ec96f6515f4021d741b84b716366b99e3818d3fd9385100b3e4425ca0fb73d253e527b9624fd81e05f929bd0674300726f8abeca06d8db19238575651255ceec2eee53bd8725f34c6f6f57388ce2d889de414856cf7c97f951e2feebe4d39bd1c3be889e92bcdaf2fab09f5d2306e5a17faa26186fbb8ea174b69e0b91e96106beb6bf855c65a5f11c250dd135d935ef7eb5ad89e78ce3d0dfe8d042c4892c6199074073f7b9d48014212f8523f5043741fa7ba62f691def3135e647da1f7ae8a69fc4a69890d75b029cd80bf1675a723b6dc500d1586a6ec22e63b6b0636a3e940edba22792b86406d14b2de352b5c098373e8b44454e330201d7822f922bd0285b7f29b1159db77a6978369296f39d8279429ee4d2cf92077d0a0e27e70e0320befa8078293aaaf4b0bed96f923a6c0775f70b1dbe1166f4cca5923849b01b161dec076af7f2899bf66ae1d075d9e663c30e065144cb0655e1defc2f1858c26703be3c245572755c95e9fc1a9b71cc8ff3c3b16544126ba299d52d1a1235fb11c8f538b87b695b73224ca602f0547c7db781cf746cca43182aa6b8843f3560d0823e0b4ad314ec5fd58bc8db6290ab7dd1306e32f7fd190dc956394da32569da5a5cde8dc251277c5d487750ad84a7b0980547882a5c989a0fcdb6578c81618247fe81660809fe4de54966b1e8e2057f9c2a1da89fd58f3d6ddb83d5e5762095faf54793680853c10d9ae45f40c1d6a4ec10345a194a181e75e80ba0605634ca9495d3484b136b779d8f9487be545c355732424773f5cdf5728fb4a3b7b20882338b4d6ad1ff646c088e1d5e0fa0014f5d9102d585071396fe3e20ca79dfd79447b91d44e6d0b6b24dd62ec881fe7b1cb970b4be410cae6f6ffad1086ddff3d0162d495d9db98dfdddbafe5311978ae003ebaa988cdfb85da18963d9a093965aeb9d1ba29ff85693831061643f595da620962040a345f52058eec3d8d6c00f420cbdd23a5462b61211cf6b05cc5ddfff18d5b38c10fcacbd493852c5e302dc3c7e6386d21a1d396884639195a8995d34a386d2a8fc56fb8779004380c867b0406023d0c3c282918c8e7239cd6ac2a4a4a53aeb788b94821753884989a22f1c46807b2e69e40357976a8df14f94f4694280acde41a4269841c5bbce71ccd208f3c9ecf49f7fd161328be27b6725ca26395f8d9ef069d158072c9442c4865c258bbdc8631e69944d61fa6a1d24416df15fce2ce8a2d4480ec9656da3c08398e58efe1ab83359614a96d9c3a4ccc788ec7775a0e909d1fecdba4474dc4cca607134cc3f45901597fdcfde422ce7ed41109b024fcd33f059183add2d051b5a4d7501d048b86a0af09d5d40f85aeb29c8685d798c1a88495b3d30c29b8903d028fa1cdf57d2bcd39e37aa0e6701f95708c0b0cfa67cf94317f4e1eece05105962ba46fee0e5d00da206ac3119fca02d4f279b167dccf8012a5088747c022f7d74ed1246a5ae18647c53cca0059d2006e44da8ac1b1d16ca88b3a3f8a0ae1814a1f687c3e6a6015fa05c1dea4574554c15e2afa2fcb170074aac80646c667aa788758af353004722c007456e298484ef309deb3c2f48450d181bc120c6c6f35151030e2db86a77cb53c7a870182affc07d1331d7597cf14e77b5dd0797fd85018b5de4d7838eb466c4a0c445f3daf6102e24a0c6ef33aece0c5c2d36007ce3a873d2db35a76d5db45e0de41f2c568a1498d819bdc11bf76137ff25f25e54bad511a1cac825efc88f248937fc019ace8056a62690f3eebd39c3d121529fe05f454ec4b97a670d005584fe75d55fd51be52db13993ce6f0cd80920482fc2c74ec4f780d343b0f37df8a6c8099660d8b7f4167c565dcf403b53048b486038016d82e46b23be38c4a28b97f729631d0e695b25f436adf25f832847419d54756da8211c8e3beb2c070a185d157b3970685532b56982ae22ace63c93ea8ac58745025f4028d40e06855670ddc36ead1f19772788e8b08789dc206bbb24a01d47c7226456327df0a55607528aa590270af8b62a7b169d1a9235d54a715c0eae4b04d4de25dd570339843d30c252bd74fa3ff4af2e5fb018f4c0f2be6addd165f80b82582acdf602f605502ae8b088a5b1ca5627f7ac7f511deb319e46b99e65464b9a4144078a77e1eddf41baa20d4fa9823bb194dbd4fa1b3fe24a61960e79f30933597b9c2c8cb40fed4b4dfc6015fb95818b309fe9c2e7ab37af4e7e1a7a2fa9843138bcafe1281f0ca4fb98e96b123bafa9465c7f3b5325b1b289084b2b3d1bee4a86fe12888c772fc3c9da2a7e28d0eb328ca0fd348241acbf45418f5c92f3601b4136daa518325d4ca823a80b7e53607ee54cb0f4574257c2c941efc0afff4a7b07e7bcb164d7dda2f1d941e1071c04379c803fea8778b4eee33447c2f55b98012fd997530c167ae91bf7d2619f1f8b86982f736315b60e5073a47ba11cd99f202046297030349ac8c031de249b2228c687f115f340f944eb9eafe0ffab699340749651814899cd3c01433ff81d75377f9358666e9c06557a397b28bbab249f79751e882aaab73ecb7d5acc8857a4cc6c279520f5aff5fd426f00f91cf65da4005ed6311b72a85bf799752a211499c1ecbb9fa7f21b4cc2c2940c10d35cb5bc22b036434f95465380dc12e87207b7a396f9425359033010fc7eee52aae22f772ca066ae6ab20e2d6151bd4c4f5c928c1a105fe146b4816db294bfaca758141f23f82d34c43b92ecf1e58ab61b477965e41b0a46803bf90228f92ae7a708b946b1652ce3bd1da7fb4217f254515090af53771189d1f742fd0fc0c35239d624a0aca41a95b8214aa838985521228638d1a815752baa95f7a6824cd2c8f7be52a0267a48467cefc007b15348da0d4d6470d3654168937c558f72eab22c4047647d2dc75581cc7d2a38c4e80a41ab3cc120e78458024f2b27b45436c06e7e5d4c21b88891f96bc4156d79ee25ca6e295e97eea0e147d4b34752996ea52a93d696f9261cf18ac9f9102c69c7baf7140d63cd8ebbdc50061906780ed8c937245845351683823451f0d124e5dea6b0c36c27d1df7fc380362a0f224a3814891cc92e824052699c0182a53135f7378afb8f26a2c008944fab910613dc3baf95f417ead99aace350357812bfcc11796124ca7abf9860d11ed6a040789eae6503f83890f515f2d588cf509515da50d6ccd5f6d39cf8eb13998252cfc5b7fdaba143306d8fb635e1cbafa8dc007108549971b8d7f273243f275e333bafacc18f046076b5a2ae53ef0127d3cbd2dd1fc2cd510b19e2f5565fdae61062ced5426ae986f2bf27a43e3ef54616c9e1bf6b1e7bfc24d8eafc20d92188f57bff284d8d7a910bc25f947f19bd53df606c4cf4ff3c0fc8cb4cea937372726ad6dcbf460865c828cbcbd9ef974cb73b561a71ee72dfe6005c413f00d765cf1c92f207bb419fadcfa6355fbe829a340aa6e8702ffd321507ce3eb15bcd6992c83b269e497aa5278da75f64a4749e507ca67525300947625a8b93cc258090096c3fa388ba6899b9bf7b4c14f9321a35a091b024ac8c0fe04f7281d847ee7b5d6bed05f39c9c34e16a5a0a67737d85825a58f28c0fc8bbcb3e082fa62d19d9207a0d8cd2da0968047a08e269485690c2e062874c4a0ebbe6739a79bf049e0c376cb68a4a2b230f08e18b836d965f9d2b71cf527c060e23eaf971ef92dcada63444be28a98f1b1f81b57001bdf48c0ee10cdc83c5ba8f430ec363458cfd0e287b69f0c08f17e6986bb720cf042d7c3c077f7ff66e3e5a6dc3f605fbaa079ae52e30d282c24ab0231fea73b5924d372a2959cfe3bd6e84dc30250172e61da5690569aff37d5078a21b4a3f9db88eea86b24082cd0588b78319ebe4979087768c9316d0dd1247872143038ec163b21dc4c9f87238fd98b3fbb7da6de0a2e0cf3bba4db59328e9802435c4e19802ced5487f5388785fa26b782ee72533bc30fa81c261521104b1d064c700e773acf85c96e64d5340705a4a0df9b88b8cedb3839553d0ef03d141cb763f6f98280e5f920a723fc80225801290b225a4118fbe094bec29bcf8f25733014ee34aa420e1417ca5c33d9f0c43b90fea1f0a562470c74118c79402d36e2a16daffdee3f06abb510086111f6084b1aefc8505052f77ba7f431bb795516b0bf9bf919449cf59ec5efb1ee16d9520ee8edd99bcd2a522bf0edf6d801403578f7d9530b315e967083894f86a13f46c01e4ca5b5ce66fbb8292081b4683ac602fca5e60014b9c28032c1b250d6b04dbb9c4674dc817906779dc8b82aa9fd2071e5580df1650b823a07c77bbefaeebb0ecc6c2cb0724d4a0591a44a64f508a7f7822a0bf5513fca257075a1c3c32bc2e9dc0acef46e1d4adc09eba67952efee33e11dc3c1c3e50661fb60cada731ba1e4d434312ddb402219ffd6eadfbef72cf1073486e5d6d96decc56a63dbb488f88e27cdb63bf6528cb0f7aa944f843412845b6b9c4132ef9013c5deba6f358c169906cec66cac97c9612f2555d50dbcba6954d6eee64e7772a54250c80254f2356ee5d3aef5b12d89aa6160539eaec7887a575282f1c9f43c5fc0ed503b240449a9ff0375b38e571ccb2b931d2074d8830d308e0cc04e52c71daa6bd46d0b68e2a441e8aa52506769ea6af483ba11ce7799e662e0e79a326a674877bfd54caf10ddd8106303f33a5c1794120797ce1366bed7c7392a4ecf87e3b51affa52dd4399d0b2fa80c50102d9ea1951a6b5b67b2b3d2166035e294059625ce4cf03e4f62ddb68d5639df5872a01c92b6abe2ffb91e9e829f9e488d3437d255c6d389e63369c78d045add220120b50d4c134508344a10f0621e6cbdc831c92a4799106974357483a7ec1678e376e6b69fa87782332e71168ce4f1c288030f8774497e1e5fb19742111a9297f3ad44a39b612ed19d0693bdc42036c3ba3d98d0f73095942e0a085c816c21f5f0fefd239bba435186e1224b4d962e6612b50f0ac6fedb6985f9d462766c8d520943d9557d717742509a09fee730a3671f89fbcdfff34874838cade916e7437f8e1a6d551a5c422bec6a6f2811651836fa21c9484cd08307ffe8b0e36485624e943a90f1fadfa69955cd2071450ae151bc0a69d05cbea5327cfe0abe22c61aa484e0a7055de128b7d19b8711da87cbf6b08c77fd60ffe931b0ad05a01563f5bb85700f73e95205ffeb27c655e07a77f8c262057300812351f081b88d06b9a35c1679e4f389cb83a3245641f9d4775e290b19325734c7055428b95b530105033d1aa54100b77adbb6ec9d3733dba2974e50f33c6221034c38e0acc55aa1fe8f3acf3b6c43972a1ac20c65132f325e3c4f877b452d024d6f9eb7f1ccaa10757db62ac041cf11075f159823dd34c83d08da10970d89cc3fd922b5cfb458dec7ea9ca57486c8245fe6e71dd2a406a57845940690e4be83f15960865cf1fe66858e092b07770fd571d84cb9baad4032d8a67e6ebbfe94b237cffbb20724595d6d3996679e5778e37114b18230728bf518bef114c7ac30130cf25392cec700a4378aae18693ba1b13a30e30f44d8bcc3b88e1047aae36f5603e98182a0242a97111c60d680800401e80d6cb008db22a90d3879909f7595d18a07acf9273f14f6c623779e466fb3a12e39c6c4c212330ee66f565d2dec3e6126b8eb979667e689ce09682ffb2961d829c2df8ea8dc59949fe0fc1e6e8bb6613e9f12ec4f60163a2a2815e482c851ad5de5f1ed08552b562185e1d4ea1f2826a6c09ffcadbdaf69c0aa8287124e4c3d6bbc10b5c2b538d7033c76f09f3b7f1021308f3e24b323655f6838a6b2d7742cf3698970e795c9c4ebd66c96956ba15daed47975cfa4cc1e0295ef09e0c6f8d7f32c743de565c3fe4133f6a8c234a17933212463ec0e05c10112c89ad7134c50aa79038864cab4ec01b837e3a9767827aad9f01751505ae9e2c1377691a0682d8bcc9039c69e7c6a44c80381995db5ca998aa249fd7d9fad683ddf33e9be66df852dbbfa3d20fb31eb98ba9a5fdddc0a4d90e1a72df0b0b55bb24bc2dd262d66692382307b7b1da454e88e75d91e08055ecc48e0dc404a6112173fc54ccaa9406c0b5cb85404fcbaecd1b378c2892b8c59fbd16fe3372b93124ee196af79b6ee2fb9526c07d8af5c805052f09b554fd3210f6820c2da978eca22681eefeb020026b6366336dfe0104503a11f6a0b1e865a08cc4743b8be5965676b1750ce9244d0475f9b957f28adc5ce7d32755d8d232d8d68bcf5db3bdb1cb38846bf52e8c60e8b21e43f7050fe710c8614e4ca8a41bfc25793c1cac2f0fc56e291367825318fcee1c94378730142a28f971bde6e12f6f1ce7d0423dc1d5ca15e83ae9d9fb96a5180a1319074f2072ddbac9a2d5e235a4209342865988167aba27f2bbe7a146a73d182d09ad082f56893bdc553f6023ccef5c7053c0a9c0d1ed9961074ca6e354aacf0b45f1eb1bead0fc71873f27fd31de897bc5b07865a6c418df5c3f49d5b262ef74fa8c125ae42875d02aabad32bc416b3968cc8300b1dab59044fcb55880a3d4d8e57a0a4379b7ee6b8b1bbdcaa3b90b86b234eafab6b7a647346ac52d55e4f797d883c73084f5b7baad92ba7593f0a833605d31b7485f8465672bc681e9ea1bd7d1b5c84090896005da72f898dbc0c04201eeac052b8194fb4d8ccbb084e42a5d750d455aafb43446ef06b287de41fc078c50fc0759f28d0e36df1b9ed06bf7a20eca85511cc50e460e7a71b09c7b20fc2aa77c2c4d122277e82b03bd78e995190ec030f90e067d30991b92c580785a87b4961263014938235c2a609b20782af4597502607b0f3e991a3d6d2bdceb24123f7a527771eb1d8412273d37ab90b49c3fc55cedfcb5fa011dffc161c042bf1226843924c6ad112d0b7e7ca288e08538c857335791f7d2a01c0ba45d87c10e1e2ba08fbe0f3d63de5d7e6735b8214848359992d4113114a77a70b3d005503ace96f24dfde7057176935403333061c3dbe5f1bfbb3fbaae6830030328ef3454110feb3ed5de6dc7fe23948c49c06c5d4a7b6163446f943361455af1b47b2046a2483d5a10237c6def09c51ed05c85c47896163abf12420faaf388ff9c8ad92814a4a680e985e096cdb91220b2d8079384140fb029b18a8f53bffc8cdb2c42dcf43c1a579c93fb10020f96584df1dae5710555c1c27aede50e2af538d74fdc6e485f9d8dfaabae45571b8f7f7797904a0036679896fc3ea1614c665d75da7068bbfd517288cebb2f6cf68da70634a9a626e0459e4e1c61e08df7d16964c24aecef3b8aa3b97a46835b889a9d6917293691a0a6cad1961d66a028c4460e0fb743272b591f0a17946be50ea248f337e6485e99b1a495309d03eeaadcb5a9cde86a24fedcbe56d84a8479ee7a94335c2214f32a9b8b6294fb9a2bbe32139512b0d22eea1486cbe3b3b1117683f92a0c4aafe3a4223cdfd44556560475529b6a9b1952338adb4a4c5a38643667537259d3aedf1df5f4aa89cb0320b79d6a5403436ad0feaba7b877ab68e2238d0164c802d72486da5e8748622968a0a056d5a332ab93c576b6ed7ea83c4cc28823d24322e1e44d4c4010eb23911afa89a57238407a1efca03673f6a290c7ab236549767c0aa6a299631be9e8d18c4538122a8d2afbff9179c730984ba04866af3175892a3437ea117302dba092d8e2b86f40cf11ee2636e36921ff2b7c2f22f76edad328d43d5b3cdd7d3f3719d6549a62da209c56081db60803c0ead68489630f5612110b3a711d4e6214d34e9a7662fd983db378effd603af83df2c70093db7e86ba65b3a7bce3f6886522e033f49813dcd107f4eb2b2da7b06228b8d35394725443032499ee09699517b8e4bb3bb8baca7f3117cc29d760b06f5d5167220427f81eecc8caa466cef034a69cfbb511040d46c3ad03cd64d346fdc2db466f1b2b7538c81d99c11c76c98362d813174f1eef7fb3caaee1c9373cb764bb9ddfe7318aa29603be766b56affcc27d2b6df13f6b23612413e6bdb4dc89652ca24e50e120bf20bf30bd8ecc00a2cccc056eaa3d80f22585bdedcb951bfdd442cfaa2a720fa091910bd032b884e824289672f8ad752132ad3905679b18650832236dd8e89652942951f2b2b2fca6c49119678624246c44c18135d96086955d8ad5d24017d8bb1edee763357d28a42bb09d55a30a2a350c5af4700b5f52e0077c905d460cdb6ba44e26ebfea55012f8e56acf406597fb1898a4a220956a71b0eb5b87b2b9689725e34bb359bdd44600b6b5eae80fb4a2915da1fc53d158af921897ba5eaf667c736552a0dc9bf5e5ff0988ac1f18736785ca94826c016617142eb45b1ed482b8e8add6ab7b6f48ced39b2072b051ba6b7da6e5b872d7c99f2c3f1ab894b9ea238848dc661b9d44d4c57774bd4d55f0786105199c42d3c4f7f1d012794f0c54cce0fc52f326a4b549491e2ebb4c75fd3575217bd5a9bc7a1e8d8b435385e73bc95c9f6afc4e1f2bde315772fe2a3a1cf8e663529ad380ba8bc8ecdb106eff55b5317c9f019976f27ab88c9e162e63abedd3d8e591ccd61ceb82b0ede25f70e9880845a35e570e997aa6ad8296928aedd6969f7528cb1ed5c33c5dd7bc77c47b1bbbbbb292ead660bc2593aa3a4389cb516687d1977ef3dbae588732abafd59711cb6597158b79b5899c6cff7467134eac5eeb6b67adb6bcdca83deafb9856cbe42388f35db0d470609954339dd8e47d4f315fd86f945bf19894641eddd460ed850024bed935b572c46a86975136a57b32dfedbfd4dab98d00ba11766d211100844f2e0c1837ff890640281be3837a128a8175d74d14517cd5e74a128d16d909b589948504512aaad569569385e55a6618d72a43a59123d681fd65a5b24a1d75485e11283500baa175da885d8000fa5cfc7617b15456fb17ff5cbaef5501755a7b4d20ae4d666dd556c91d2a6b42d909b588ea3cfe722594170132bd3afbd296d1654f85bc71aac7d76b4e276f6660e8d18408d18609d9c9cb85edc7a51abec6b141b33a1961d5c12619d3af5f0401385df8b657b7777db3333b1320dc19c913d0bf561e07b167c2ffa2f282d288d039a9570993e750022040ed310384c7319028e44313e5d11507865c291289a448030c2082308800412481061000310d134c2085157845e7c1e5e9cd00b8c31c61977b354e885d08b219d908be611e18202f57041816e3e5c91756ab45682b1e166c3e1c682709814c2614c5d8cd95f69ed709451621dc63c1cc63e3222ecc3512119a5bbf0912646fac309b930938e80402092070f1efcc387243303711988cb15ea83070e38107241a28560741027e4022be1846294d587be0e4084e80e3c20d142303a8407a5d05150beca5745726ef78a24a2846094d4bdfabdf56b8a1b89a2a4422e4aeaa218e256dbe96f88b396d2fb1bdada8ff8fbc88dceb7d7156c7e6cfb712cd8a77478df3605bbd9a86e42422f5a74ebb4d65aa229b5ba79e5d1a616db6e014d7bd1a7c66e5fa6488d55b2af3d8e9549fc7b6b1257c78d71027a8efb0d818fdbd06454c522e7f033dae043e18bcc21ce578ee6d0d7bec16b17f9cee73917db3487e218b5c2fdc42d4a560e3e1c8f7c583621f97c74a49fd84a417ef4e3f013ab9013f98de8478390434a211fd22a21a75535aa3471e6c5910a20bdcd201c0e87ab51bb9ccf57b1ea559193308b442fdd27d64d62976653b16ef76050c4d1cf040ee609f44ba7640282bc87735a459f2847e49549c889a272668dca41414343433e3434248ebf1588639f8276d26f41418ebb8965ce1cdadc58afaa909049e4888353401472302f20746452b25652dae0467ee41d1cc90824e7e01c381c9924900c3989111cbc8349628903928925b6c4c18547bec139f805363807f302467e648aad2492a579c2d30637724a9ea3390cc28d8edb290327d460bbd1cf840dd4b46625e3bd10437a9186a82b4ff212c29bcdec2480669267738492031d6892007a09e1485d5412cd16908c08ba92b700e2024a0ec241982d04954c51389a43910c58c971c8736d73acc1dc2ab06087c61791b33964412c4b5fa1f6c32cd8fc1b96b8fa1ae936cfbfa5cf223c987389285fb38d62a59377f47d3687297ccd842a3ac006696969655f3355f0fd90cdc7b1368ee3581bc7b12c47a29a11ae364a80faadd5702603e8353d7b6d74a3d1ec8591e3383a398e636d1c753a23a3a51a3c2a81463e8ed9f4d520d1918f45f967bb3f06d8ccb16878c2d1918e1c9688f2297c7981b58c6bbe1af6a51ab479c65d1fcab6249b69f3d5a0ada49752520249fec1ef83a42460d28824073ad0245142a8947f46461300e14a7e010e3ce846d947cfb59adf967e2d00fd837ff0fbe02d24f907930408bee42400e01f78f067843de8a250e967844da30924b9925f00e841d36c21e84a431efcb50082035de9d742d0952600004ff20d80e0c0e00fc908101ce84abf160060bbd1736ca5d241fcc41e7f439e2dae6f1f98244a084920a9f9079ee44bbf24affd462c799249a28470290f194d40c981be010e3cc9c76e0226196d008427f988a0034d1248460f8ee608109e6492a04210439e9f3ef02537a1ae40ba9227f9063818bb296934f2249304129b93008003cd11490ec22401822799632bd5ccd29b94c0503672007dd2e993215cfc111e78072ef6bc3cf807af423ec283077dc407e7c06dbf14fe5223a73573388eb51f8fe246231c1d6bb8b4fd727e735efa900d37f4233b6792741aaa885c2c7bbe0dcef323efe0483f37c75acd7f4b22aab8458d1cff86293c1649078c9e4d23ead96bd4afdb7018877123ae394ce153f8ac1bfd26920cc079a69e6d5e33babe01a027f90640b892d786be1f7b348da86974cd09003dc9470041d43cfb88bd3cf23bb411d57e46b59fd191dbdcc874a29a998223bf660a7e5e53c1673355f0bdfbf8f3d2cd4a3a60c86ba6ad413787bd1a4432bde743ba8157fb8d3f9ecd47cce36d30872d3ccf7bf56ba6ad977bd9e739e79e2f85a7494a8ef35bcee675a0e9d4c7df06dc6fe8fb6c747d4740df4012d0dcc0759f973633053ec73e5a7391acd5ee30851f8d70c09fd899e621ce759e7f43157cb7e6d8a94896a86df09acdaf8f9ef45372e00f84d77ebffb1ba1e4496efb4d40c91c7f234098d487f39dd104925cc9472499622b2999236ae630033f2c1d67aae07bec1bbcc4f47dbef27dbf21effde88f962af8de842f5df71ba6c080dde7fc96fb0dd546157c23cd8d3e348e976c58fd6bd987c1601ea690343ce16b496e1b3330a6a005544c13bfbcf910253309c26fe957fe16207e362de93d2ea528f4c04378073e2c3d3091901ec26c01c908a027790b205cc93b305b007a520a10a651cd8346d983eec153006be6881149a691903981a07b70a36c5e20e81e8c2ec0817f700efc8327fd820efc711084cb3f913c8d2690e4411f91e441b193bc668e003a07d981374ffa8942a007f2273ee94b35785b7ad281d993b267716cccd9ac392e974248f2dbcdfb06fc25fdc4b112569f9d26208f2a94b07aebf767c77e7500612c5811cbdec452dc8ac1c963bfe469cd610912777416bc66f39b580b20e412c02e0edb718ecd610928170a1540c824215611442721e43710ab08a48b55842a01ecd7efafe6e2cfe642b821891e96787c5abae1e62b5e71d87eab36aaba98a7d73af54a9eb4acf47aa569660adadb6f5defa5a4d87f4432db98226444b2132a53ee2624a48beea22f896277fe318935486f4df670fce1e8fbf2010ba8c17a6fa5dfc4376627608958faa0d111073f41d86d0e859cc3efe867f4135b49e737f27782d0ef017b4a9254c002dc84bafb51f26c1a9c7eb72b6ad36803467ee4233838924902493b07536ca5a2ec6d8e30f2233f32496c7023b384b0cd9c472d6fc83efbaec7232222daf96d671a2d40fc9d2b40fc9d97f8dcbf210adfdefb15ed7eb9a87389ef1f0bf77d668f5783b5dad8ce4de8a17fe93bd23350ea4c71c82c7fd7857ee3937701d54fa84c628fc1cebaf224f11248e891a5c615dfa0943154e8b80984020f0d464d828cd022e4790380a7069eb117acad5db3d88719a2d2da34b101c3d39b2d5c59396badcd555694480d21e505ec0e0d436cdb262c28349a74cd914a33831e7baf284308426ef9d683ceb787906910f2ec1a5808103f0ca1042c7211c5c0e00536e74f0a303a5da6c0eab47101666606cc4e90c74e818c7d75132dfb7a5fe9c0b22c4b17a54aa21297a50fc5345fba58fa58998cbef4b2f45d650afad2c5da94adca12a7258cdc9271e6b155d4d98c2b345c3c76fa63895a09b1531f5b7ea4bedb69157936c64e7b501ee4595a04d8da2ab7fbda3db8e78cb5d6ee20cfbce1e84b095b1ffc50e4c227cbd5560b2d5b2ea6726cbdb0b20503c6569f1a1e3ba5b25debd40c386bad9d1a41a052668b0f0d1db6ad9814a06c0a6da1e1786b9de610b1ac8c6e84d9ca00f0d6a9140f2f5d49d6dcc121e38acaf04347370e2820bc757aa369082f7851537505650a9b186e7c5da12cabad3d35802e44691ac566f196b09ad223cb081a1e67dc08b19bc7ee5700ed18b30d5a833c6f154a434c21e4071a355c75ac6ab49983671c6764a751b4ca050e644895f00214902669d638a9226cb26f29b0ef1e3bc7f0e2a5e887401e577c0d12ae1e7f1e3b85d1b8e2367e9a3c11d2e6080c363fca48bdb0227640f1c243c5b75317585467a4b5d6524a5b9027566aae1d31be9db230ca410567ed0fa4b74e57649a860918212d84c0b891620d0e9bb596aae0a242d1e1db690a6ad54db4ccea6d5b317bf5b171ad5d43a99eb2954882f8d4b35314e499b700e08747df2e52f9763b9668beb39a0cf9767ac2b6cd3acaa89c4c4d64319111e2ad53271e5846939a652de1f3dd5e823a39f9ad256ee091c1e789c68a33546a52b0c8c382cb899b1ac0d26ce96242541b6f30005b8abfde428582e1ad5beb24f0989da727282e5a7805a283060d461ecd8a766d5003f2b45f2b62f876db4e831586635bf1ca0e31f8da19502d42c68abe5d06289490c2e4c79519343c788e088b5ec8cb232fe4bbcae47b21070a7988cac47b212121dcee88003f048a0995493c510e083f14cfc43f4fd4060d5285ca63b7d8c7198fa9c2acd439127625cdcb9a302c74660c09c3aa612a2cab4e98565758161e8f3d0656946287010be429244ad58edcc15a32864c8ac7191c694135ea147730e58e8c36497ef8d122c466721419ea179cd04db6a8a0a0a0a0a0a02a58415d5c5e82a8980f0a0a0aba41414ea3ea040595059505d5a00eaa41415b3c826e4141410e0c86a84ce307f95290880f0a13340655055505050579f9f4411fe4e69e1f5d409e630bb65ef8ec36fb0ae4992d0bb2a05981b5ae82262d3604c842f5ed2a7400be3d05e4d9bbe1519258c20f4bc072440ccb9f2e2c305945b49cbab4803cf6a1d01dac312848813c83ccde763d400625c62606335e5cc6f684c96727a032415b4aaf08e6c7981e5a0235d11356c6cf8ab104e479bbae98786b9d0456ac818c1934a7a91a41609041b33176778f803cb39531a5b72e823ad65cc2c60a2a534cecb0f2c7cd983427d7da317b08c83383b5f9eeb222e3db51f0f9ba5bab8acc8c941c33f870216c31dfed215c7314bbe6a398aff2112b335b651fc5dba5383a08e8ee081882aaf8610825885b5c4431691e8b328872c81f8a67e29fc71c70e31f521b948a0641f3437a843a11c30e25c863ec63e9f3605794171ebb081316708294278f3d0328bc79ec29ac60f6d867000590931f2825df25854995d463b16b8a9cc7621715afc7625795198ffd0358b66d965a52667398ab3ce0a603d4529a74830759961ce87a5265032648e00c862f26e2c4e0a325247dac4c494f7a59e5c9271d48929424b954cce8e0b16ba02c9fc23045cb8910fb98010c902799af68c36878f39484cc89a7a8306952c60b58803c2d578a3a239670382d324c3c0ee7409c87a84cf671eeab381c6eb83b7a3116460b0c2327b4c430f3d82be0bd2842ec14980079e24c5a2e3d2d6b9bcf769cd52a5a3debce2750231ebc750990a76d20509dcd8a664640cb8b15c06f8780d39bbddd6eb77bc3b77bbbdd60e4b8dd6e3707de3c4465eabff99288bfbdb90ddd6eeebbf9786bf1c2dfc8bfbdd8f3d6fa03c8d35a0ca3eaad3bc05a59e1b4823f04b6789295a5c51e17545cfc7971e5b137e00333217606747dfd70074536001653df2cda7cfb027ce388a41dbb02fa2794a34c0079deaad93836049c8c5579414fd4182fc20643e4fbb6391c8fb884fd10186218420929a8b4a478a3e2848a2e2ba6b2ac809305aac9633fc913efb0124200094f38b6b8f9f603d42ad2a006131760409d31ebf1858713610dc593b1a718dffee5bd5259a3095c5b214c6c613a61e244178a168fdd00b86f5fab124364209942a30348c595d8941f3ca578c1902113c8cee3325f5a69614e3d9cf23cf602584bb1406d85c9a295c309192f55533a0274b583f9e111201b5c80cc7c7b19a8c7b7978056ab68244c9c1743cacea0fa74998a3100f2c45e7f607c63fd09fb76124adb3c353ae600c142074d17210ee353c3501e813caf9f2f11b59a54777bf9c3c937eebb9b761ec40168895b7c4e74e0e3438b0f9b3e3d1e7b70a9cfd66317d127edb19be027ebb1337ded9841e8d6f0edfb7611c8b3db628cf10e28dd8d31c6b843d0420bd37beb02204f4b45290094f13231870da02b38948b0e3d01f0f3ed16045bb6b100070d5ad460a182438ab0bdec2f74b6befb5aff6cfacae1787fd56cc96f5905f86108ae1f96f0d8c5a1b805c3e318e828d1ca338789969e3d5a7ba23c76005434bae3cc0767551ed85aa9d5d4991331312e9c516de9f167fc31c408bc52803fdfde41452b009c175ffebce171228b6a8171ab67158d56342b02f9fe761015ed0ad3f03053b503899c2921f6922262ab68f56cb44ae489b5f0c40856b36ea24e15adec269b9313bdb62af5c34af536176bb44ccedb52d8e1c4690637629c641082fd90a2eda9c1569fdee6a2cd36a451aabc4dcc0e9a0b311a6c2cb121fcfa210583c5848dda9ede66f3372dbcd8a9f1c7cb0c3642af1f52a936b6210d42a3d06c69dee62818fd502ccb33c3f643f1cc56d54ad416e5409bcd97ba49294f0bad2856d850b910daba7e28e26d4b7ef84371eb6d3772a8e0e395278d4717a18deb6d5efa799bef2a13ee6d5eebc2e5484f8bc759f438d973624f97c7ce41b0d6018078ec1fea57b33c707efcfde81ec8731c228a7171c95656797a7cce0e24cf3c3601a08c969370883de7318f8ff348a3a89f55f97b3d893caf3b3d76d356d54dd9caaaea6c0588b7fecb5795a96279beca47acaea89993d30fb199aff2911aa4b6a86eca52b6283c5e217cbba38d554f5d48c8175694a7ad5576a8556f73ea265ae6262a0e1b2b19dccc00688b4a868b40c2238443ad38b8f88ec813e77e88fbbe53e5bbcc379e1adf6e544a95c831a44fa01b73b05cb1223c73810f8fd20d70beddb61735a658044b11e508d40c272a1459b4f4c092a3a289dd479ed88e8def31dfdeb3655b3996edbe664012327aeab4d001688fb0b5bed3547c771391675fe77910c57c19e187211e9710e687e216961c378fc18ccc110b4227cf635f125127c96337c1c4e32c66621efb4e4c0b55165baed2c97192c39dbbf3774b75b7c04496951898f899c186b0b3beb5fc08f4ddb787c8b3715e3fdc85f0c323e063ff610803fc3084c72eae78fcd8c752c9e32c35e879ec1e2c92254e9ec75e8ac85756bda73f7d86c4b1b3050a1618a931d85aeca3147f98adf295d09612be3d488a4a953a8ed8e02839fa76f252292d2f5ab0fd6be2cae6687dfbed0540660022c50415254e5be29056623e5299b05bdbd7ca7878f9f6f11adde08dd317af2d2960c2c431404e6a425517edc3b763db378c9434585ddbb49a8970c2186163a38e1f17ce502c676ed65a9bc7da987298a5ac5eead6cc618e92b19e0ef397358759ec290d2acc6fb766d64ab46a253a76d30dc2d0c90293468632648446535682daac69d908a6a42efed87ff0d6ac3235a61801278824d0c752ad441d3baeec2614ccf023c6159622545284d87d95895e79ecde4d4234202fb898425bb246883d5899a895c75af67079ec2154a6fcd8bc2635935383d483f694e25f39e5305b493df55c66453d8f759328b48a82f535cc644fff0d8f27e19b9651a76695497c0ac2d7dbae40a83617cedc44b189fa38be388ebf749ddaf1ebb5f792400e0b2bac30038cef6f0675054b6d076690811aa4e30756a65c40b3aa2eb2b59736960e6b94f8ae6d0193295d76b8407da9a92d72be491a49570d16df476c55564f73112eca454545b5a8081775112e721a95f48349915051912d2a72148a8c8abe70b029b215151539b0a8a8a8e84491ae8816d1a2a2222fad7c519167a9302dc01021f450827be8795c95e5b10f188f7d94f941e4b1b7912e62d6595aabb97a870bdf59768cf198fae2a1e6abc78f6fef1770edb1b7559990b556ec32e678eba5cfeaed17959fb7de448258871a1f3a86601d5dd9ca91e26baac65f6f2105701184cb53076920e45954cdbaf315343fd75a6bfb47fbe81c69be74387d7b5759dbd69af1620347c89c2c696b2a8bae1f65f7681e95ab0330df527272ec70262f2628d42431b373046aceada97aea4b2aaa75d4c8a49ed29d6f4777bbddaea2fd6eb7db715579dae576bbdd6eb7f32a4665caae6c57b6db8dbb9dd7aa3d3ba1dd6eb7cb3bb32958bbdd6e5777bdab3ba7583b3bbbdb6eb7abbb5dddeda450ed6cbbddce6c67b6dbdddd6e1745c62eb77371e7e36eb72bf2ddcefa6eb7dbf5d90db31412d8fb7cde39c8d3e7f3794b91a7af9a5151f1bd9e370ef2ecf57ade37c8b3c735a5cbeb74de2e90a7cea4e5a88381cae975de36c853f7a76b90e7388ede34c873346969b9a40ce9197909ab6c4b9328ef28f2cc1c44f121862b4a9e6f6f19dd31c8b397a0b4788ca1b079ec0da3ec17540cf0d7db45b7e07ae2e3ed86b7de2cc8d3167943916751af20cfddd5d7c8ba8996b17011d632a9b03534a540732249e827da28c8f382e913a3bd5268c6db26b860f0f1ddde4ed4ca6f55814ad68fb426705a68a92b64a86345b50610978d2e1544585c4a6b5aa03ae71b10a525bd4286e98fbd7e9c3c69ea43a91503c4784cad21abba31268553e663d162d8932f95386dc6703141430d9b274079463d799a1419e10cc893c56346c21d9a29bd7eb49476cc9880a973d7643a5c4c907ccf19edc5b294ca214f8ce73b4e73f5ad014885d379843b356081b9ba52e40c45a033ba321ab8969ca86f984c0122c7888c292fc011e27ae2b14db4335c39a932f4b9d7d618946ce1baeaf1edd50d571229949250db502e2552bc923514ad4496a812111155a2269263f6238b88242222221a89889c62111179d93042a42322226a224cd44444554a888888888888881c05a21e6888868888881c48444444244684238a228a222222f292cf1311b9ae4397b76eed8f1f6fbdb229edeeadd735e46945545931e3e3869a34e4d93de4fce876f48a863cc76eaf67c8b371bb1d69cc78f180c1ab470c33be1af23c958d5555ed48f1d96df65a863c73871460869c20e345b525c25faf63c8f38a7f6e8049418909439e44d65aa17823c66989963842985413726c205a879c305358bebda6e080b1f6f655c1028a112979820871529dd0f01956fd7204ccb14787906ff7b958d0d62acae93a63b3c747971c7ed2b8646badf552bb906796da32e695034898edce6d39acf0b10286253aae94d1405eb6914bdd429e76c90b4798ef2f2fa9292d3e0e1cbcecf31c74cf8103070e1c387005127b8cbd8289638a7c799127874a76e4e39154933ff25d65c27fe4c0da246285e8707454d5cbd1276861212a61ef80d4210977257de522cfa3eba7acb695a5ecad9638228b063731a4d8d22406407c727716c68ab2f4d62b166bdb4a651aac506c9195e5c9c2e78f55b6d6da7aa55a21cf3c82005c8883c4e206171c29aad8b639c2dceeb2aca2627b562d6ab1b9baa1c38f15b32f42eae7b17bd21f25b84c19003c85da9c7898c723e2f1783c51cc0f0c286d7cc440e3499f1f8a3ca7a7e7bd789e8bc31af53c1d8fc7e379a129c2e3f178bccce339c54213938567793c1eeff2784ea37867bc1b8fc7e3591ecf51e826176c19c381224d11386e843c3e58783a1e0fc8abcfe3f15c3cc2d3f1783c2b529078f1b3e40814210fed795e563d8fd723da3dfe12e3f5f86bcc0b293e1cc5c6eb745ea190a76ee7f549da8f6e47af3090e72824f6d8ab13f2c45f61c07c6e325e204e4c58fcbd5e85ca94a2506c63e2cb8d3755422aacc25d77e4bc057335a221a17115a5a404b3620c9824b8558821a947c8932756cdda524a83843901688ac50b7ce6ec99b2aa7d017b6bbd1a690ac4c5adc8d717186fadd717ec98c5345243a680919142474b4fad5a5522e499c5e8bcb55e87e4b6a415f225c6a514040beb7305429e984c95faa327d464890c322f84c1a29527f7c8b408c0b7571f35319bbb399bebe5fa0a4baecce57238877338e7ede48246ae96cbe53a97732dd662462e4773344794f35ab5024e0e97abcae572b44c8e990a1db932678bcae572bb5cce2956ce884e2e97cbd99ccdd99cd3a8145ab93297cbe572395d2ee728243d99e554880385cd9f34183dc084b9cf654d39f339dfd526ea7454997c9ecbe572be944bfaa188cf6de5b63ed733efbd612ea6fede7babfc967fbdf620cf5b068ba9058fef027c7787b548f3ed95477789faf6ba833cbbac6c77f4181862459ec7380c2a098b14612cb8a8ea5454fe229f73988a409f578cf9ec55870f9b61e5a8aafaf344445ea5c893080779e2b014609ec7f37a833c79d8ab0be4899f02fdceedceab8d00d041e254c3c3170f7ac29eb6bcf54a83f2a151a745d40cf2c4612850bc4ee7358a3c75558697107ef41a43147d5e6190a7ef45937d4dac9baad9783bd8daf2434ed615285dce70cee338e62318fb88b14e670eb1e7cc6c65e6ab6c5583b4ba00c1ec83b0133c5ad8fba34d50ae2caa2a14c6b48a55102b5ac2acf081014f8e1a7a057976081d42a4ed09730a7b1c66c204f6aa62c9da3b3dbed4acb5620a325f7e77b8a47d7d7b7db2dd562248591247863576b8dcc939a3d8a9e0c02510906faf277c5546464646464646554646d54475224fa3b44080fef8e9c3076d8f9e3c0238cbd4526a82169b313f7830d1ea53c7472d633c512574c08961480d71a69eae0837f8589976bfe10893dfe0bbca34f41b1cb86103dde0bebac1e1d03bd52c472181e40202701237a07460d0e2247472282a148079827a35eba692da21cf0d6d452fb79b52336a24ca4c9db623982b66b9fb08a5a3f5d6ad753aa76d0f20a49cab5c1c51e473a34f8f30b33035a4d8e1836b80439eb9080a5ad6762cc104adc68e2930e4acc962d6278cadbb9b8637e4995f1013c00cd447a90ca850c439a1e48a91b43a397cb8af48cb0b47b09893af45a9f2356bf5b5a1bd7afa9a85b22fbee636ca4ac990faa1adb241bed668353135b2b2323f6cb3aff5d9d76a41daaa8f7ccdfbaaa3c478fa6163f5d6d76a5d75b6f36145ab793bf597595aad6d54957d6d4be7c3fa55b9888dbd8851ab327ccdc55ac5fa5ad76ead52552b173e6ad5c9d76c4eb55aae56736a5613cf5a8cd5826ab5b64fb16a49796a41b51a19596da45135281cb5a15a4dac2656a3b55ca3b55a951aae065503d6eed76ab513b51a5aade6e594aff9ae32097dcd7d6ebed7731a0379f67a3da76ec8b31723ea89dc12396d439e4444444ec9c893080c46d6f3784ed98c3c9ed335e4c9a322bc38655f78bd7df1e7ad5335a20c6f9da6214febe2c9efdcee9ca221cf9d3d13d5e2cdeb744ecd90a72e08eaadd331f2b43627dcdfdd5f175180b118fbdbe2eaafd3323e4bc9d4cccca0ac3ee736e7740c79e6acf551a4626a64644fdd8c8a2fa00a39602bb8c280a989513015317e1c9d7e21cf512c4341a500fb113e3bf5429eb96b7421cfc66818ede9111045131250cc017b9202960246587e72e2b2c5623d16eb265a86f1fd18a484f899e287ec0ba513a8862d6ba160e479cf98a29a1f024398886102ecdb4b20dfee3bc1f50d86e2c7b7d3af25dbd4522a9ea56d21bbbbbdba9cb62897ad63cd34e0a1e74f16375bf044cdf173a666b7b290672621ed891830a7165ab6ad4d21c90a2ca7316d8094c9e1510797b3c8138f51da00e88b4c201e5f81f0a45161555de161c1fd2923f305e8ca156a66dbda4c831aaa2572769cf1eaa271f85c8d31c4ba4914662bb2b1a3dd0fb315ce0a79e632477e9cbefc94f9fa93a44a8f9dc97ad0f8e212860c459d1af6549554a688bb3e333ce873458acfdeda55c70cf2de7ba3f09901f6c5e70c147ae98f3a42fd048debafdbeb1406f2bcb73b9360c6471b2a326b9ce004baaadd7baf93265464fae16e0f9ddf179a0c26be7b657002c7051caaa9212fd0b21f4e376bbff620f9ebf63a5d429ef74a4d6d11ba16e7ab2f3d6654c9181df271c8cb283f54fed0d0d0d0d0d0ce831f1e9db5084fe4390edd7ba4c385311d73fe7ae943419eb73b4f9a9d9c732e53dc1323e450f237479abf7e4df8462b3fd2c84112658a0810eab4c7ebe6aaeb9496634530d01d7d7b58f0f863034a180a237fbe824c40a3db3fe4394a8dfdf5d34d64ca8ead0f095fae17291e7fddf221cf4bafa8950c2c1a4efe94a1e942a7088ea721342f389ef6a05d2be6155e6062b881a2614d8f3c3ecaf4f07a3265c8942a2fec79538e7af2dcd4b8d0e7efed0206c5cb0d226718971072864b941b6baeaa7e08af1518a037b58b274c9a0f460e185f62049101e44e36efbd97878c9caf69edecc898116321064e0b2b2e6888b9a841ec0596d995b5d6c5d0a93263c80f384884b612796b776feb590d52b49c6d1df2b41eb0597167fcfa0b95870e02b858dd9b02891e25337c5d1942b563684e00c48a98462841ece98a861438c45280e550c1444c4599d80a1f72b2b5b6c415156331d640f97163a70a919212220159a1c93546ec84d8df1b6228d0fc751b2767935aa920c516dad58a335ed408af88e7ef548ebf375f5b03795e3436c8927179ebd68a391179ebe589b7516226cce044457d1e01d69c1b48e26c79225683e84883bbb7ac4b931f9fb35b1ac8335f7bf1bdf75eb33e3a74d88892b24778bdbc51e6ef35a9f8e60c00ac0db1e4c500967831f1c1446c86a5eb74af13cd549c92268dcf1d3d35f88802a44f12241962f8faece6e624b3ca1faf405c4f326aa8105af7d903b8e881a68c8f19235484f6c71f33ddbdf726b5214f6bba25234fd34b9ca9cf6c922e35a958c2881521308c81e3e54a8cd096bd6d41912e4e507c744d29135a1f6fa978496bac1a2f3568fd4d53cdcc442c17f8499203888b2f3432eae478d2e59c645ad3445382191917b133d7565bad44b3283b3362a8b921230c1c1d4fac191be2c4963134ebe50d366fad39da32220d4664ecb5d6b45e5a1a6fed98b369c780fd757bbbb0f0f1d7ad18f2bc39df7cefbd56f776f0468637515b6ee088096fd55f1d28fe5e73a80235a37272be61c8f3921933c092b75d5488bd75df90b7ee5d563079db05aac55bb760c8d352b3ca94cd4b33b5b6b453e4c48e245a73a084b6c7d4129c6952b3173548a1b25973ea267772723a21a45eb3219220be162c36514e42eaa3990cb5098735caa94c686662c634b5890a87d90aeda98f416a93281cab46a71a1497ad2a757452c723625f58525123f52e29d2fe06e0afdb2fe47971c16030e8e29a0ffa0816e8835e7ad1d2e28341b762e419b45ff6abcdee4eea1ebbbbbbbb3b77777777f7edeeee5b37b0bbbbbbbbbb7d9764bd90a7699a6ebb90a7398e6ec3c8731c47b75cc873cc6eb3db2d590b79e67bef750b469ef77e91e7b5d65a6badb5d65a6bad75eb459ed67691a7b55ce4d9f96c8bcacc72a9493397157150d05002c68588b07b34ad6d2df2ccf7bacd22cfdbb7bbcdee2ef162091b174cd27c15e121ec1eadb5168b3cc78be5da6a5633cfd90f367764a8b9127677df6bda2be439dad10a79da7bdbdad15a6bade9d65a6badb5d9bcf77a0927a14d4a3287395be5eca399cd6c4bd36d15f234bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbdd5221cfbef6768fdddddddd9d73d9e514f26c2d29b6bbbbbbfb068f31f78d429e0d254a3765a8284f7110633425dcb8c73a4d37958041c7151960f0d8113247c8e30ec6618c31c6de5778c735030b61295c3107b8629c650bc6618c316e8c718927185b5d60940139f224dcf1c33aa6b5e287950c6349d5aaa42c2018631c85e5985d9183f1186e4cb1aca8c1385cc3358c2b8d828331c6e2071f8a631405a32a2cb00d638c8f2a13d0b13bf6e0b8448a1a572d001541025645882bd50f45504981b39492c955f5c1a0db27e419fc61d095fc16540a063d68d22a2e60e8d245062727e130a83406ddc2409e4129a971f76396323f8e595b477ee442f1a35b27e4398e5617ce0a28554e425ac4e54d931c5cde34c991ab2a936896c1cd789ba555969505cb5bb74d2c13dc73e0e20bcf01073e067a0edc2e296d3dcb8246cc4968dd5ed9aac68ccc84b8a2270bcb15ac1b59584bb2b25a2859225ab304051c27a72451b84f7211ce27255d91f2496e91941b941cc91ce4b36ce5c967b746c833d33e456a1b6afccdaae2e5afdb179a0c4745c69b6e4d1fa9bc2152763df2ed76c81437ffe13fb815429e1f5a01edf7c30fdb8307b741c8d3834751cdb961c7ca1d3321d02d10f204fe88fa0c95c5eb7c90dc1132c3132a2f4cf5d133661fb68a3caf942ddbe3031850aaa021491205061d3bbef2edcea5d078c4c7319b62ce639614a9b73cb29d2c45e870add8d1a24fcde10710b63bc844bc754b65de2b15033e52aafa7cbd4161a34af9d0722bc7261520a2eadbb401707bef95b51240fe753dd2fb7a6ca72a939b261a530433ebc9d89be3d59b661614b137b3a21879d3ad8ea0d5487dd9e7f3f9a8effaa8cfe7c7b7f3f97c3eebf309cd30d1e24e10296e68087d388848f1913e9f8ffa7cd4e71b42e6c3f97c3e07fa7c3e9f2fe7f3f97c3e2f7d3ecf525988747dce6e739067ce56aa9473dde2b8bb1bb42ccb1019d6056b833cadad419e4d03638c31763b833c318e92419ebe3ad6dbb5a44eb6d65a1bc3c2f831070c102cdf6e5f94b66f8fb0a60b9e2f527b881cb991c5e66b5dd816e469bdb80f21597c04fa11f6ed968595b252bfdfcf977e49bf2d377afc7abf5fff7ebfdfefe7a50cfff35d652afadfef0708aaab953b3082d8155b5cd861558c36857d22cf5f5b4aaf1a5dcea34551da136ade9ab0a3d4dd73d523c13ad14e83f2215037b5b48493922e58585839a2e64b75210cd93d02fa439e798b0d1a6fadb79f51ea0f0c3a6badb596a98fe523a24692eff6f18cf54155da3d5b6ac4703d16aa9b6a62ad6b5deb74493a9da9d3e968e8743a9d4ef7d38925e900e9723a9d4ee7d3e9bc5625c5d1e5743a9d6ed499e1b9a21bd2e9743aacd339c5d2dd41a31bd2e9743a9dce69944ed765c78ecea7d3e9a8eeeaa84ef74497d3d574359d03753a9d0e85cea7d3d5c8743a5d1daad7e93c4b2599bb2038beb3049192a79d9cc8b3850879d3ade98d873c4d939663df21cfeec2e3e57f3f6f3be4f9336939b61979b6cfe75d873c7d94071f1560aac6d19b0e798e51268e67f6f5b82b46979d348fbde76024adaac7de729ccca63e67ef38e499b1171835d83a48ae371c2da7c7de65e489e9b8796bbd69204ffb6606f2d489e538131289c8402db5fa7101d3848c16133438682cc9e118ae9b2e3a2bda9c053232c30a193f556cf47931de3b92b121cf3c074eafb154ea07d62d5b355d73a42ca5411ba998bb0355b6647949994a7b536b7cb12115038e29394f54340271619bd1f4193c76b677c4aa78f13243d417177546a09a193cf6d5a2cc58d64d342aaa0c09090909a9911aa99190d2202121213910093f1212920d241209090909c9cb388ff448d7ab8d8fb7d69b8c1db363a3e7db8b6c8b986e1f3bcc0f8b8bff30c41aa8356b1ee7f05a93e6c566cb636f307ca4988863f6458c3c91e8a5a2db5a4299405a697e04c102a8469948e3a39524850b5b3258d4da8b4df1d6bb0b797aa989216c6cccf66ecff67a3ddbb33d6b5615d62beaf57abd5ecf29560fa85eafd7ebf572afd7db31d6b3f57abd1eeef57a807ab65eafe7c05eafd753d2cbf57abd5ecfcb18dfeb9149ead143ccdfaa227fbdb9f82dc3c3a54c78aff716f2bcb58219ade591e46d0fa7b7de5ac8d3da06a3653b56fc387a7f91e7e8b54595f5b984cfde5de499ed5b6fae311d3c1e6fe9089bd2b13575672b04296f9c64214fdc52595b5c5a52e6f8b9eaacc622cf9ea594de2c5e67eefc0812e5cc8518685d64ce4d1acb96d48db7d6fb4a95aa52d6831d5473ae9e30f19105e34bbe59498ed24da2d0ca0d405f7074f9f6aef2f5f53e77a0bbbb8b6d7ce78edddd4f9c7877770c3c21e304192d27d2684171f5d89b0ac6de53c81357b328df6edb5b0a79f675128079830aa84a9493903cdddab6d987dda9d8160d63d65a0b853c33992327a902687122a3c5c491276da95591102946ca62ec58f2e20f0adc309067064be37a6bbd9d8822aaee08f58d423b74e8d0a1838b793a7470b143870e59aa43870e5ecaf01d3a74e870d4a4cb469166b2e48a3c3bf4529b3a8102a52ea8848a5b2a8bd639653300000004f316003020080c85844269a2e9696c5907140015628e5a724ca48882698e0339883206296408320680c88cc80ccd3400bbf826629374775f2c58a7cf577c115b0b8ea404c267f22ac7a59ac94280428c769a4de08f99a0dd4dba1c5adf8e2904daff43fca3cf006d4791fbbb3abc0a74d87466edbc10f8b97258df10a1c88fa03b873e4e83c4620fc882c6dbe70c635095f54ddba1be517669cc2e108fe827eee1084a22fe3c50ef79de28a4e22da0c77ce39b6f4b816ff1780b0c74a8ee1cb6a845a4327fe070986b2c6ad70e1b7b262e11a44008a85fa0a4d5b0b57e1200a9c166bd01278bc84dca1d2300632c6efcfa73a3ef52367480e0cf8060949e662546a3a19d3583474bba1a87b7b9e17641beaf147dcfc9b22c058186f90a2af190afabe500ed566941ce26a0b90b4d42ee5ec09b9a0eb9b19ed5c031a4b9d631d89c8e2f8478bae3516977a71ad8f94b92a96e58b1bbdb8135b79522fd633f78422000e3f1621366e55e95971e20677e18b001c5cb6e48a896936c90f41cfea579705b25ebe191182b0b4caced4fb3b464331211386ddf188e67c4d8aefe0f19e77f6e97fc037f1a651f0b0a6441be64823d05b83051319d837d00ce46dc1e46d8d369dea8a6783e0d6b9ce80dda093058cfff2606cf6637091ce0738c4cc166042c9648d756c3f0c86feab5d6657b83ab8134c53229f320061c8535adeab85ce0b54a56605e981c769a5aa38c6d6389432740ad6a0f921098ea563dbcf8139400ed3d3a76d1eaa5184a8ad15775dbe62426afb15e78e55c946d7b951df9cfb8b866083546563e66eaaeaa1a25aac949ed03a0eb37201022708b2c9ab28cdce8301646d0b117b90b5e66aeacdb677c07d1adc9063aafbdfdd9c3a050a424a0d691485cd058cfa69cb6d4134b1aca085d1ad96ab588fdb82406774b8b513a455039e4717e3800c28b94700609d7818bb973e98489ade303fa7b4c4a2f0551ddcda892dedbad618604d9d99fbf32e5861f1c9da81e8bc234d8f67b77d51bc3d17489cf86d4f39038f58c36661fba2734923e84b78809e560660f065d4879e5a86327a48cc14b1bd8a62616bb0905dd7881573ff798bc1be75be42cda1086c81f0e8511131b2f93ae0a72a8acfe29fe54f48df1132d7350e5f7781c2205f3fdbaae2ff3619401986730b4ecf531ed2e723306a871186480cc577a8f07161e5abb94aa392da625045ed92961e40e65bfad8c45eb1e98443191632fc40ae48f123e61bce5bd836d1c543879e2cffb555b90f3b159a39cb9df85d46b74ceb0c4092392624b5f84117ef5165331bf550fd131185a3d06a8824c2107a5f09d1b2fe06e5f3e964ec691e433abc46fc2e03e327129e3d624cbeec95f83467d874732fa4d17a62efb6eff879b1cfec8f4a0ce737d5d24074be6bd00feb03ffdb09f928b27608081d19cea29685c5e3b12c931980ddcbc19686e7896010d59446bf721f8aa8eb07b1e337ce4e600c5c1ed8279200744ec6ae088cb73fd8a3247ada6fd89136d45bd7cb89b9e5dd0e9e03fee10399c63bb3d984127cb5d755f03f6e2b89e4b6c57fb705fd4b8e0b21f1840f3026dcc1fac259c8184f14145a6dadb7431dcc762cef2d3d843c6028df47d598711cdb4d6b73eea410368ea9b8a8c2755aa373db26383026e14fd98f61df4b11f3eab5aeecdd1bdf12f0cb75007f21cc977464c19688e8077587a8b573614ebc586a4ba1f507987de483eb78dd61bb316e1a9b670a523ccc2d696951670a6be162b08ab394fbf7b68fa5348e079ce827d7ca65d433ea01ed70e9e66f9915191c526e6789ad4c133b0bfc616ad603cf2164f4669fb12ea09726bc9056dfc1ffa9647205c13fec856b4b70026aa3faec2e9b977111f43b7f16b04ef27af69e3104418b6e1f845b3d735dc495859a1a98d1f7da39138de5319bdd5eeaef02869fc34c5d2e23a4a8af2e5ff934ec807b53f9011cbf8f10dd0c2dac95dfaa4f7578e9ac681639181b6090fc661fc1fbef8d15d98c2e5db6c1f667d712cd49aa30f06d757fc6dbe5931c9c5af129e84496700cda946d4bfda9e00f1b81781aad8fc28297eee4c9d4034516c6b3d6953e5511df5e1d475be17c5afc02ecb67b4329529e4af21e10df0972679a0affcc44f99f9e80d73ddb5aa5f4cfc62a99225c95ae573be96171a7a2dfed31f13ff015f6bd1121325b8616935d662a97158c969342812a0fd95123ff5a00e50b8fcdda3f39f0fd9f81d4452d20b1c82ae555c247e1eb0c103306524023d9488820cb10f374f3b5dc156141db4f57238e2775e7f68b252f5f71b676380253275fa51500cc615362945e00ab0b27ecb81aee98e706ab48bbc88e1c50a97efcbf419ce748081b4a9ec5f8f97cf67069d8307b3cec0234367c091c2b8e09884e546fc4b161c1bb88591f86d8421e27f451cfb700370a76083d779b277d5107f3630e0b93e33133ef9fbd48d0d268c342a983fe841fe693950dc80a3fcdadfd0d9fdd0875fcbbec39d5dcdd51b87fc3953df9051922f14bc4b396450e3c549bd5f79c570b08d923ea910286d8c34825de9db1b9bf2a2e1a4bc84fb311d7194c494976a1f4b26f31179ee8406657cfaab8b4e3a27949d684af014b08c34f4d9ddfa44929e19801329662f7097c3416465c1909fb8f9987db39ec9f3230c2f2069abf9aacfafb956d7eaa7cd6751a2af615874e3fa66d1bc86af1f224fe4c1d65d22653425d2d0b5babea42010da1e00e28af260b47f8c715f342ed13efc63045cd0d70880f517684fef51a7cac6a8cfc0e091cd3ab596c238af954451c404aa76b4cc32fd483eb56f9a5feb9ab135c7c0c8957829a1d885ca8f9f815ea6f1785d301c79b77cae27d8a97e6e1c63955f0096cdb5a94462327132622dd54c26be9cc57879fcf4f63c7f41a400322392d5c1e44e4a25419b737dae74a66ed0767826ce4d180c323afcf657666aced00d09026e33c9c7ecd7479770071ed83af25240e9662dc8abf246bf0f0b1c8ae424a5b4402a216f129639f768124b9ea959314aec02194a342c52a3514ef99367d2046d2b67161064018a983c32f1cfc5dc7d941c1051f260324d7e9ae411ec6b726700d4e51a6e5a41f28afd335e77711de480f9642ed4699efd457d513a70f4c71f3bb2f5eb12d044a95ce6aacdc5024705d156ad452dd374864ca9f2904c86417576dea52c99f80d02ab550cb510f95361960ec6a6e3cb4e6ef08be41e9c4226063888cae2fe157fbb0afe9ed22969f7eae0b0f9a2567cf8c04f64952c7e62d4d2519ad48406b242ed1ce642a15ecc0b7f5f2a8a58b1016aff0fa4a0d254c8e498862f1edd786b2d4fd1bce803a163d20ae0a7200c26ce844272bde239a59894276dc14b72532a5da620fc5eb1f49afcbbea76572fd8ca47dcbbd08604926fffe8ceafed29447780c4ac8fbef2f98d7fb4e3e0ad9aeedc6dfcc9628d847c39f8c93408b628d4d33b952ce32196d000948a2868a414af745039ffbba63422791688e1e232d411a6c85e913f96257050adb80eebacfc56eb4c8a8412834704174501da38ec91e82ede25bee06408c68a4bac6cfa5119d53b4656897e7caa613a89c1545e9896abf1e210504e3f77e89e1012c09409ac9e2926f83b95291607119cf1f9817184adcf76de9a6cd02e1686e98fd2b05f6f40582cfc7638d134bb6a503d1994c0c3b0b483688c82983b3c5bdde4216f8a445feb1f787606aa9392a00050b300cc96bb2e62aaa9ac130644354803431cc96c567385ce06a3314cbfc4f494853bf44a11a154e42705d7cee65e47f18f9d32c7206dafc01593d116aa8c27fbc3a932948e09aa47c5abfd70139e59925fc83f807ea109f72713231ea851fbdd1e1b6eade591236ff713b20274ebb3d319febb13076b0a7e8611d1e71767ba70418c2546bb99fe035755fb067c735b3dbdf5ea61f75b9dc9b26cb418a35da104975f265deb4baf51451ede97b723a79a7adc3200428bc6af4991f80d8ac09609fa0a3cb2493e841686f953794d10556b87b1c4ac646cc49f123b91016353ca978202812e81f78fba6ee1488e8b716124cebf1e5f8443c0d23f0117508b08c430f5e909e0652c7c45300e11a8524b6b8f9be5a19e881fc4758859f11e328b7881538b902aafb53263c53d50c9fe8d2787932a4bfab40a5ee71ebf76b6c3b00e0b1886068a9bdd49c5543a459b1105f2267dd9b3343bc4509333d81c117d7714cbaa1d4ad8b0f5dfa2da7e57f3cca0b84a9425dabc82d6055117a7ae4ac1e6abc031b58e2d5ee8053a1c3603e72f4a5caf2126e0c19a642d63e3c1d8d50136f756eb271882e90be5df4ab1e92e6b1d008e75128cf1afef51a2ec64e59f3ccb0e466a84aa6a7d801b8fa32b0642ff911fae75cebce2446a112857d41aaa78cd4651e3614257a24440dbf7c1f44322c43a4af204d844f10ac8d4e2f4b1d41a554346e3d067abf73dee455ef58b8ec7de9361e33ffa5386c3577533b15bcfb8ebfdaca3e63f61a11146ba0826402dee498a41b94f52eea5dd96dd47226d697b84d4b430763e80f8de7be5eda26bdc8222856a37355df65cd32a67968fc0aa55ea3ea4019837f892f57c64a4007fca8be38e2ff0f21615a62c8f1b29c86ffde386468506696acb0dd907a94a39a6fb71112a6c164176710fa389ff144efd4c39f1e4474aaa0dac43eeae6d627a1d86ec204da1eb89e07d0cdb53e79614c0715505cbd1c7cda5f087723a21f0c324e9e23ed35423d544d299b079c3716548e9fc0fa65dc1d2e96d5de6d080591655b190bff226816cddc02f7dea60567acbbbb42f7879d27eb3cfbabf784d72990ecde7b867ec9a9a0caffdcd283688a8f2f6cdc66103868e258da6166b9c04494d72073bcc05069bb00e30a43047e35377620a02ffd497c3653c5e01731fecf3d680e09451cf9a82eced0c5cb05cf0be5ff6caab6854e831eb5e8e0c7ab823dae63815398f74126cb99981bd7a48b8dd3c2a8015b8980fefb7596df36e78c3edd2fe113b58f13cbb34a6566d33e9c282a5a1019d344ee2344f5cc5124928ce80647ab77e32df45e47046d49254f34b9733aa3613fcd3ef765b0d1b1e67d3c14486a7d157e61d8ebc8923ee587b36ff3977e3a37ec5df20609ce8e2542c45f5de2a801a8dcdc798051f103cfd6fd04d951213da0e384005e87f7e04c15daa5dba963bf3b1d205baf5ce7ed6b2328ad6d1800237edb87d2bb6b3cb78897cf20f8132450800d9c18da1251f632843f0f297de2205b1c4e327cb3133da8c4076fd23eae6b0822630c6182a5adc6e279e0e27bc878fec7bddbb40e4ec4eebc3778b71caa395cefb1db639a84f46b5b455633412c5227f81ec1c9d8c302115ccbf5de63563573f8f9c181357aedac57d132f8c8a7a5e35ba9097b0fb98901bd72ffc16049e3cc8c50da33d9b4f5d3821b8a85bcb80ca191c660aa44893242ecd12a1dcb2f05bcff2f8b114c4c30219c20602916a1e81d81da1ac946f86e449484c65573ecdaf076880eb7e7cb854d299fc36b95b2dfc28740e5cdfb8f41efa29093d84ffe92b2a1105235df36b4cc5be8ea368dbb9a8f878b86888d88527cb10773899c03cbc43288da30d31cb30f82519235f08c0c456508081c9916480c7f938d8cd089b14ba928b4ab532087cff7cd68c3a05666f219ead0e83f527d0b83c1f51070f02ed8531babc76cc2e6999a870a49cbd60c796ae0485b93b200d0e480a4242b6be70913f60c38ce24a5dc9b6293fd96d6a3cdb05940126bddc4be7782142d6679b0830c94dad7ce64966c1775312424aa334da312564f3216c77d5736f2067d3724b5c57a1b37fad6e9f25dd9897dc9bbcb15e79fa37ba0598fe9d3b11a258b12deec2a5834e41dc0f3ff939e3de07f9b41d4802f7bd905b82b9935ad2439ba576a2a7c060a295f74710489a9b94cddbced15259af7c8ae53b8b5e429e30262e6af2fd934750aa1d1eb47c81580c24e3e1991ec22659980b2592f473785b485e49e49d8c1a174d2a85a98207229a00fad7cbfa6cb50b1560da024a65c081b11fc07ebfca2f8796e477984f744797fb8e7793b7e219b7c7d7ed6fdf775a4a53e137186e72bcbb52f035db30f78bfe142b552e669887c7a0f2c676ad89591b43b61cfcde4d2abeee9e778227615a53a03f8c399ffb013079fce042564dd9342872abeca4d6abd858409310e600aca631d14ee4d677f6465f69130b2c052877591a27a7a39e8ce259e22f2d4fa975d9495a502ca125ce3e4306b5bc04689d85ea2c57db064c9f00678dd9dd0b64b788fa801a67e94f6c38cff363799e7669682343631bd04d554d8cf3529cc3406b9dae902ee15498952c809b36199307b1a206cd3634123e96e87882bc61ae83206801abb8187de138b481822c5605f933d2a4ee2e055c1ab14459e6b6501b6490d24ac004a9ecb617923049eb9d9084ef3661779d3f47781bf394c09ddd88bfea78635d04a1af750e5d12e124fd88750ae6fd71fa1ca67feb15ab460da85b769ca4b14ab4f5aa59e71d60cba2099e66f4344d2f5638c624e1bfc6263382c81b18a04148335f463e9fff00b6c3c31cd61ebe85c919bec3a5d4627fe5e7663d6435ca32abfe82550120a1c802cf2672a029fcd3f56a36a4859e7eacdf8d3d0406afb4babb187fee0736d494504ee8a079aa688bfe1ed3a6f34eb633c39f421aa227e6d7f50efb11cd52efd9c83eb819542d0fb2874ce0071dd05fdc0d59a5a2ec4227db74db930ac8a8ebea814e59746bc568902b99d6f731d38b94743ddf623774bc5b7045637071bf57c178470eaf3785b13a3d5f32bd6a896512fd75f20968de23cb06c01ef95fef3f0d405fe1d69b4b5a981c9395ed11b31b111e0c9b4d55f78bc0d5994c9147e00a4a6c7c4025a0923df3cd52f1a05188c554f8456fec968bb2b817bd2b49870e3bc3093a751533add62ea0bcbd2821ef055d0c2905afe095200678e67321fdae575208a1c63e054f57da366d9018fa8638db19d9ec6c5be4cdfc6d0c7e8038d003d136acf760c14f34ab7542db7e81a4272d1148b01c349e099040dda672a39693e5519603133e36d0228de122ade434a32ea73715b0942a554042b85d9f76754e101132c0d28582c15a9a8b5bb41fe8f9c6b4add8cc5a0557bfdc9427cf11f18b8354a6c107e2c2116ff996797870952d4213d1f4d6bc27c9721aa508e5e2d64271521e058686b8b8f8f021e744f2f90d46661303bdc5f7c6f982c07b6e452b4af191db66b4a53809398f2ba6809cf58882f6a076c9d6d1a40b68e605e9053a9a55a73bdeaba82e107405bbb5fdfda05d8f9e2b9f0e9df87402c7b6ba2b639fa00bb5bd9b7a835ff83b2a70f898339f16cc1994e7afee976d3dd648d03a4a41d0e11247627b25cea0fd5867671874c9da88e1c5809e995a2e0aadcf683f0a9c52a97892768e188d01cd79c48acea1cc3ae0d0e2946fddcf21e167c42a9e51da073488862f57af4987684fe8012921cba7fbe9aba12ab6e49231aead4f4fdf4276a5a7311750482f9fc768bfe9fb6e5f7fdfb1af3b2636314cc6116bf12655249accac8bd731d1ec10c81e69a71c2dd341d4487b94321d44f001c1b204d92a5708245fcf0b98640aaed0ccd74095ee26a1fd34502cd1264bad630a0325d9f21cb5ccd2dec39af1f7329d67a7b79f38511993acd6eb818909610b68437a7a61ef7552badba8a17b6363d09ff3a562425a12440c9ee20bbade53a91aa29498980524283c64a00baab0b1ffefb14fd6c797e439e791379582bafc81d22c5815619efcc40ab14ba1d1d4f4c6986104e1e912d660e2cb6ba24465fc0377dff9c134602ce523071334a4737f99718a9a89c41160ec5f142ff0314d904aba7ff33bd3508f6afa3c1205dd3e6e58fea4275ea56a39f92e4d0e1d079362f39bfd425a9fdd353b79dc8d1bd7d141cc66b5a20df7979822276e41e7f4804133a3265519c0f613a419c7a1ba44cb9a0456682a15d96d582b0a7851f8f0a3631589125b6d9786ea978eb3800d2bb330d935bb4325ff06414179b7016eed9ba63300c7f535f7ca41eb2b653bcc8746c4857e59b8264aad1e2f90677eac19a2da207c5fb6b1e9e30ca22a0cbab993fe665b71fd50c0776923abeb5f482624436320151ad1f17929b54c447ba408c03d18ab151ac525b89cc20736483fc0296f521cf2a99bb4ccc3fd87e975494b1040e28ea49bb908c91356f7b2830186034ee6fa5062920f575a07bf8563498d5d5e64c222e8f31597ae0ca0d20e5edec11bb64b58c1fbbf10f6f873c5b97ebf62a47c8ffe4287bf415e5f35f22c4e67238f1bf931218014453d8dad2188cf9d1e7f8ff36e68e7fc654fca2cdea4b7f2cb3770a27bcf324100a98539ae1e2ed1477294de70cdfa7957b1d9717017eac3907e8108fcf823eac2fd87314e0d3ffc484c9f38122c6c40fae10b4344110779d8a9225c34efec73e209bd498b9e7efc868ec5006f7b43b6472d9fbd990df25ea44308dcdae302d8704ab1200738015a8700fac609a609717598648642bef15bd8202e4c1494d7c77c137cc0942f122745ad131cfef08c25b6be31fe98bfdb035ef9d4c02e3f15e08232972c78fd08e84cc51c78c6e694e38a8542b4976c986351c935d496037f3b4ad18229414e31ade74003e4db696508fa28bb30618780a76154f9db603f2e534832192e430aaee9869f76c0fdb1e33c3a20a5d1bbed511b898a91e9bd5881672f78798b5e5cd3bee9d52ce3f60a0d9eb17a45d0dfe6f662290d4cf7ebebd9a327dd19ca0bd517feb8936d547c3afceaa2f161085062d7e91e57cc225794363534d67c58db9fe6183f2070559c9c9ebe90924d2da3088f76c8cfa13f30ab107780d16119aa33d01fff9913820ba5149197996628ae0dc40d1d1c614573d53a4aa55e66b6b14ae93998cacc334aec0e8778dad9f56dd5633c677946ce0f75777326a8a353b25fadca28dede75bd63bf1bbfca0e62ea3ce928006e15c62116e5c7074554e393c618d8ebfbcb1994c0c5e9d6771bc18433317bbb8b12f73546187df5f176918a560dcea958c27595c4f435e92968697459e39c83d34dec2673149723406b25c5efc84f512da829ada02e48b88fc616b76cfc26830e1f30107ecb17c2180a3918f382160dee4a47cb969aa5b171d5a76741b270ea9054372cd306910d8a9a7cf402728d7fe6ca77d83b689a0a63b67147c9a9d0aaaff798b40ad07d4238486a60809ea85bfbd9eb1d6df9b7664f35750fb428c402b1aabf23c3f586e0ae135403d7471c2587b7d88b2ff891dd5d2e67d96bf1b6aeb538002f8b3b65a289d185fce3845a69b8463ca29f691e0c8b5e633ff652523c0135d5af6a750d5aa02ff01bd143ebc2c93dc26d21b4875413ee68cea8de79c32b36e2e0d32232884b8bc17d072c8c75f72b09255519098bdfc45904b51106ea623085631abea4c0c4801576263364ccc2efe1cf5a0936d49f792fc9f317a80e6c845ed9a428ef31837b8c6b0a6af8ad79482730c0c52ee7d84eab38e4096bcf1c47c7ec75eab4dbc0722502c7f6351c1b978aa9ef49dd2c0c637c8c7a78912f4cad56884e4ded0718577039f27cc742dc27267c716a70aa6eee2cfd2a087bd0a6f2d80f70f360096e7bdc4ca46395e69aab27dcd39e1a641780f7d428fb0cf1c4c0ee14e9061718c4a0d1600d163ec59e0621f7544dd85728250b15b0a7d54270edfe2beb8b1473050297085bce51d23000cdbe3fd5bc92761ed7749eecc5a690a675a11d5f23d4d78604c7d34aac591ac49ca77e1e0a8795ab19e953e91edf663b09ad0e25b90d07bf5103b3101ddfd510bb8cb5ab99b0b6f681da8d1376f0200ded8da171316b412f1ee22c9c9f301c5f49ce6381b9ae33171ff569af99901c020175d98e4408052a8e8027400e32062f75d1f5227ee1f59ddb9bc248ba170783cde78c1b3a7c597164ef857dec403fc87b93b2b1e38d07c1844ee9d51da2ac5d81a882a0346a40aadac1ad03cf5b04a07aa0cc3ec16cee83ec311199a644c24076bd427bc71f59daceabdc012cb51ee67e9cae01fbc31b9b17cbcce016059df1231b520e6979de3b4ca41013e0cb695c0a256adc8036110a8ec1d9f638810808678ff0e3e1d0b4284e33c3f745810282bcdd907a708c45f19fcec33c7c148b626c5b141ecc0c423e1fcee901c9f961f2ac537df7ed4307203dc1a2e863f2095e5e8abd17c52a85deb3e57b3dc6d8980b887f118027166c3df7450fac5184566677fae7d431648c007404a59d1b3ecf68141ab676175a99e1f2e8eca0421ac55f8ae77cdeb1d22830be4641f266e0048dc1cf7f481e2e186f87ee70316d16641231909740d3d9c2c049442dbfd8cb1eca1d2a04525147107dc13a4c6e21e1f4ee02a312d09e12723ef00f3e80645b89d279a3c606a7e0c94b82908a35164e1ed2683d667f85f4946370a99f668936c4bf044596d36cb1a9b4b77f6e4ba99b3cb614163992989dd67f2693a6654c9449b60cdd8acb687f0c19bf7746e052bb49c8115550f018f73965b4e1a9d31d5d324b01471301ad8525be32c96247b195d104149c9a70a5c816def17c756356508d05b0c7991fe655b4c52d58bfb24a6351c3e2ea9b00f3b5642c888e760970676aa0759c5cd5b96ee51699821b320fb750fa17c88182bc3f499918ee7efa1a17ddbbebe81e87f7250a3b6f7b22f7b77d31d4d84abf47773be4a7b3e9668b92b7361e0c31024139f1b0b6ec733270cc0197694963386518d4ac3bdce6fbab54efdc17039a0a7b5473fda69882ffcab09fc571768189918d1013e7ecd9d0fcce2f2a9d41a091fb56d420cab2e646ea3202cded12881f4680e43658625a6faec5ece0f4aeeec8741d219b130abb84409a63b0aaf3d09921b5369075841fd2a8d175413d64ae11fdf3582e51c708f938bfd887719fb193b106cfc1dd1be0ba624b1f77d11acce6e4a3c511338aed21d90da75bc94fbe76ccfd731cc82668f75807bb50b69b062bd851f67f82ef9109d32443c36cf3e27b0bc356542a5607db49a668b3350c3bd55a27b190fe71db4f7e46e75ee782e8747febde083c41179c31ba6d6c688061052b616ad6c14cadaf6b425d173a5d53075641b7f12110686c9f657240264302e87102744481126a3b81c5f7cfc408f9c3c4f0fe4177c0f75ac671c87f6b4598d38d25e779a6cf0fd3ac5cd9a2d4fc9a812a40cfdebace782dc6b03c6e5299ec0ce2104ba0cad2af9dc11833f384d5fd94b179c2209d3f6698380fa9c176ea60447dd2e904505887bbd6c4d54e602ee4316a93c17e70ed86eaa7f552f0807a29670355d687bd9f7f12e81a98e9d36d4122e79d3c9da5ece38234219ca7d420f8fcef1e3c04bd2b4da1a18341e25ecbc8385da1599dfc5916a6cbacd26ebac1d67d76e26ba364f0d43a57ab9c6274499d001b113426b310665746355b864b7fc7830d5aabc574e8b90b4667ea8152a42e1421286ab9a9e7fe41763449cf822267eebea29984da495aa8bba32fb22896d09ec7ae0a9be843f8a20a62c618ebf8defbd9a7b0fa4a552b4e73ebb78034224f5bfc23f4ec80fbbb43851bfcdb17279e12480baedf99fcb879033a8fef80405b833abb027f15809e26288bab7eb19526c5b20f145da7729f162b628f2f77ab792356906dcb433de34c309bd36a5c02d6854ad1c844ed14e19c9e07c13a9076f11293342786fa5c78e159823c097ac9767ae5dc88fadec8019376b0901d3fb5bed6b3744cafd148af931734383ab6e929667951dd317fbcc3d81ef6f426beaa077dab2abf7909c3caf43e13dbd97bc6cbf1dae1aae8c8f674ecf8c347f8af28d6b56464a136276144c2550ed5a145d7229f5fe45cca28440747e229ec7dc1b46ad84ea9896d79e3e635b65ce27829f5672b36b5762a6b3287b8be9bbc41144f3c7fc7d53089b24b97b398305ea6c43bdf237c09d4dee8a605ff9d4cf21876ad6f7fc8ecf74e9119c915d2b10e0a53ddc082ce3cea33874850bb3817338c8a126776b2b5b489fb8f506629cedd8504e8c3aff98107b5453d036c9c67efdc6baa7b52c5dd66f9be1c78a9d638835ea8607a39cb391692774458419c9c8a6c98171ab23d43984564fe16051993b7afbbd3beda6da5baa016374ca3a56b5ddfa59d5e968c0aa0e9a99695510c9494c3100932ad1ecaa11b64760efe59e3d6615cbc46344ac853d9ee6d62718d3472ec2f226d745f64ccd4bb9380fb032b9ad3b6afe32d6abf09e8bc096bc4f249446aad60061fca24a09ab53d9adc645e8cfbc263780b3c53ed7a40e89f75df22d8f2c350bb667a1a346843878f44dc450f18e9057b65bb2f256427ab9d3cbc2ec279583ab3cb514db4f749a74f50b82dcc65ce06a16cd877646b11a638a0f4d92457773aa6a68d51aaf2461bd9aad37b2ef54578fa13bf959fb53ad4a3c80a32fa8e760804cf6394a9f9c5099c853bc1e7265ad11c7482d071c6c37f34ac375e6b149c6e1cf8c293192ec782aa7ec08cf094599ffe3831fa94ebba9641d59746c52335c08bb246ca012757a07ab07695f1f4e36b191a1c8205826c22ee2e436b07fea48ba0b0a5a70192331f3d7da05ca8cfc92088549ff4b8cf49abd8da4aeb633a1d261ec6493eacc70bbc965d2a5a6d6da0e50b3783654064cd21200f6bac8acb61068083ae1edea7c78710f37ff80842c26d26d2b338f4c0e22752d5ccaca1adcf611f66ef4c4a2b0339bb52d00975345e862513c7c018225b791c5b36458fa61fe462a50d478277e3c0d5d0550d17dd9d0c6ff8ae0de57511a5c0253d9b8084450f11b36637352ca6c35a52878a4885426b21136a4cd41bebaf946ec5243eabe0a10a7099b60b97039fc1c1b70c077f37f6d3828b2e1285f62a1d8a47be58582637e24bbb7845975de02720b00deabe000ba5de6499084d26ca42ab381e17a7c30291237e399917c310fb8272067549751d3d76fd6104bb82ef120bf3ba82ce28c3b6a5f69c0d30def8d819e8aaf7fa610de8c5f82ab44f36596a4b1089e119a52a446fb2da51cfdc51cfac23e5b642097c2b2d98ae933a21a1c83b723b4295b9db45aa86d9245840eb40505bcef5a8e6968c21efd3c14ecbef7d06bacbad8a9e28e34760cf1cf60ed8774839fa7a739fa8da5f08732ab775357fd353fb8c4f7bc7f1b31ecca879081f1bc880b454a7461f8a43d16d36d7882e6f903aa531265b7a2692a1bfb25ddce0e3585bced69a15209d4499f9a87a6d9c14b053ae51888c342293b4abeb6291d8ef52d948a24c42a3866ee4db68db0854b0747c63b1834c23b3164930859f1a1381bc5bfcacd0f021aea9205f1891af294fea0b0eb991f6d4885297cdff1b9191c257947662ccd831df423efa777b13decb7ec2da3c58cadc1966d67e603efd696d9462b99580407fa02fd4f448392b00c96cfac0be11bf73c6a376cfe3e0f483b6a60a192a32f0389e66758c4a0d07802d65c41808f3e273c22b7b1105553c7315e4b0330b033c402bf595d451a135e377ae3b4a987528cc019c1fb7ff894d06ec713e963624befa38d3deb7e1bca17387f07a3ecb4bdcf88929b4f24fa6372d39497746c75e0bd7de2f912c390820e159860754b676a44277b06f05d7342cf56d09dea0a348372f944a5a86ee18620ea176d912814180d15772cf004aebfe9704a171ac0333ff6c3358d81ce5be8a9af28496e6fa8f2892b99b948b476b4d1ac31ec0afe588f15ed6cdf1e405769f7b812731e331313f9869a9e71f6fcf00b28e7101525f4cf1b166092b2f403bda3b97556c8fd5f126637b8dbf103a8bb3d86a28e18caf7057d9aec30699f11fd6df5620b2129d883cee306b270dcdab3aad27c840658556bb4e4c81dbe9ea05025b322ada0efde3c211b616b4dc397469ca06531fd80ef14e64e8ef47536e5241c1d8ad313b4868c24760597504e0575efae62e671d8f6c3b231941cc63176253a2d1af038175e03303443e62a6d564c17e95fb6a4e61274b2891a4cc0c5928fe9d7e504c47c902144cbd5d53b7d5958b479e58d30941dd55a18aacccaafcd8c3d6bfe858b639c9261d676d1eed3d684293b908df48c2952953fb05fcb4d30aa63bdd80d0782cd2931a94aeb378dfb4f60bf253dbb11283a28e687eadcd74cbc43e63a77b0d2df072abb9b6d369291a774653aa24fb4f578ba124bc3549f75c04b452d8b41a88857f4c8cf96b228e302bcd119d551d119e3b4699666458e632d3dce25d54856657a0ff6882f4dcd9f7243719ad5eee3af1be7618e322c77f0d204bb4b77528350c67d2389dffbf5ca61c7acc04dc0be53ce4b848a2b60f3320b927a870151a46b1eb8c2508100b885254d4fbd1a20ea1b7a5fe13bc71a43165dc11f9c7c62de5be8f1d48364e6456898c68536430a30064d11f3970795dd107fa67d51ef534198f6b74a51207a61ed61cee4c15129034185040fcded43af5b0b99f460c9b6080e6715f427efca145c5b9a0106779ec740fd618a2c3cdfc6c46420d86271837076482b08a0ca4be2d70009eef2f5c530315a46ce7e98477213ca372d9ec41ed585477acea5b4af2203afccf966b68204b703ccb846b108073041a5de18274abf3b614e779c207a1cc0eb6629c22270454fd8af793ee503395937066d859c1a6debdd0b48ce26789a3143e55c859f80749dd1a156e2df30e85b03afe7affb6a0b41816419aff7ad10e457b6bf53c0c5b996266fe49f3caa5d1511a21efbfcb7c269c98b2fb904bff8902d23cb9286f37d8b9d9c3c519fcf89b8ccce76a2ef79cbceee16ef7ebcb0b4737ea59ad50aa558fabaac645a8eb802ee6e3b3513543ec182e0dc13bd55d398a6f24050238b7a852299e57221a34d607ad17422b97d0bcb53409d212db10cc290866a15424183969166ecdff083e5f3351ff5a1f70d1cde7ac808ef34b2d88daa486fc2179d23255cbaad43de1aa29cb2252a25683943731e1a3e3d35fa06901b81aaca6f28d2f240a4e8bd281f03af2fcb6b628c8ffbd687f0143bba2aa715871e74812002538a8b5940c7062ee63c446e60b1bf26a687bb7989664860fac3d78b5d6ea063bdacc147dfe85d6a90052c5021c0b7fb19ad3b2cc445c9b6ce133756848328a8218a34e3d7e0da35ce5d1d0259b70f1cbd1e8b89052e135b6f4d1d1100d569fd8f1a30bc301433eba38fa93005765d953be477aa2d8a61ffa132599fc90bb12912eaaa44e7378b43ca416095c1584142102e8c842af70a912a71d5647a44409fb92812325b7c6c7bbaa09ecc3d8979ae6d9199b712c2ce4926ecd4c5a66bf8abe8634a9223188143b549507a62bb1b50e30f9bb04719786b5b6ab03a6f79f7b9f3eb1c9fe1e0715b130801ef8b7b065710afa03833d816f97b77d9c758004cd11c666f66afecc6bff693ce0790a65d14fa2668a0314ec1003e7eec83c017864692e15aa98476186b380ed541b82a5c49c89491b2035960e50316b3191d53daf95b8931a5714cb6fcf3a4a1ee4ff50eb390abb2e3938c57a7b9ecab52a92bd65b97f60fa2d1f283feca9445aeada4b989bc8f2d2c28d4aee038695919d4be75b5c446aabdef14e52f1f5f0e613bcfec8bdfcb9a003090f1077f7e0d489b4a6b34f7d5624ed6c7709f86c5465dd3c90aa2a8321485558dc3ce07b960ca84518d85c5c36d9a5394b026a70bc781473b3e9f67571cdbac851acd9c9e70a7562c165e504c840bb141a30da7f60f2f1ef873e95aa5f2f6e621573cc44618894b53ac6fca02bf5a7db930c1d600ee8f46a77e3162febed98f2711ae881724a652b810fd8f4ca6e0ffcfe11f676b2d4a2c515e6e50e9e8edf31ca268b55ed8a1f36900481c9f0210abad249a82e9708414da5a37cc066149c63d3682eea9da7e8ecb5ebdb0a2770d5bdfc47c434cf6f0f26e5a9102a6b071318fae618fed86a1866356dba2dc195cccf6279b52029199ddb43dca8d5d749c227f63e8fcad14c1ba343f908f04980a0546388ee3f6adbfbcb637ebf531fe7bad437e5e6249debdc2496ff7129132b5dcc776a857d81776aa55cfb2a4fbfe57287e2f59e1278c2d475060591cae36c011dc80edf7bb2ec434f152e9fb1030536768e642936a5f333a34f547287aeffaafa9dc22becb12e15b8072459e5037d46f6b2a407a201a3d8dc2dc85eb2cc22c769b00145d7ea77775023811449058a88e30db6c3ea7fae1e1114fc3071cc98870b5535a4927cec81c3a9e7ae632529199c5ec3a1acd92a1ba4dc39b6c87d575a39eb034fa7a54538008e14b919e280300f414ae716af644c13561741dad42849cd210f23cef44dc963adaeb6d158e73c9f3ec302f44902860f41ce7940213e0e77de004ebfe624cfe65c74c7a5ed4c6d32e80006b165b8a3dea210268dc23bbd0036973ca6408e73c90c7f1c3b976cc5a84f32714cf5a63273d544bc2e100125d5b41643baaa6d4aa91d00b4c214df029c3080989ad0939774198e27f7348f6ccfb6ced52146c3dac7cacd3a92224ee616ced909b511f9a08f52f1b7ca4b776eb429f93fc43b9229aef068296175c45a2bf1459a883865b843204281792841716948bca5090b5dd3903f5a10ee05b2996ac70b910f0f268cbb1bcdf853f07d62309c9d4caeabf0f932ae0c381d720613bc9c97a6e622d844269b91bd519ae65cad0a4875c21acf46612f34a60a0d42df8e56c173161c062d68e531f952b34d8234f65f81d31a35518722204581eb827b4694b2631873651b53031ca66fdb895ca27811c80cca414039d2bc06426f7a03e09ab5513402a973b81d54436a79b474b9d6993ca09fd2d1fc59c712a3c93f1e033d9e9821b4eceb0b81fd41634abbfb61cd170cd2b81f4bb265d82a247b8571663514d704efd66cdd39c732ba346696bbd6cc0d5c9507b4c94542436040984488f0725a7640af6f1c1065a55a9791a98f1a9f551d394fc70474ba1658b71d41d5b7e5342dc2852079be1ad7944531b420762178421e37df47b02b83037405dc89725a29c99c1ab10921085188f7b89050398b4a09a3c49c0ba3dc7f16da85fb1a46911a1a7e381130235924b903a25e4736de2d68b5da9e9b2628e21300207683509e7979106f222ed11b6b15ce3c38e3e0baf6712fe8959b800dc4ffc97ecf9de0d42ac1e7b24321ded5d6a751e35cf4ec8e3c091a2f3eb4dd31a3c3f450f1d0fc918552e1be40c94f1824d2c296a150c2de43aac4e269ddd2864858ceec78b2eca1efd07f641fb0789455cf3df438a761537b13b275514fdd1257baf316cec12a703edb0a710ea634fc302b9b54710d16ccccfd0159d84e4b11786e540433fcf9a1264490eb1721650dae4afd4052b82e7414b9d7d24e35ec02732e1610e0d128e42582a148234800c4343ce94d6d7e44c4ea29f3b36b2145818d0ca4feafc36f52eab738de6e08410c59cbe58e8440a07abbe3113c8e73159999bbf11315cb692941278b07e864fbe8c7ea57409fb55624e83672de12fe9258da4428daa2bd1da46023c48a62cd596046c185881c9e39f5cbed9556058d416ab959568c233a875418e2d89450079e85e6b886eec5456059fc2960929e15cd65fe202ea19040911baa65dc3fbb1ac41c6fb782739326a8e6ec4df36677627914157c4413142515bf7726e533a14c4f7bd456fd1e7636a8a4214b0f7a751868db04e6b25c52456def67ec9520408a2639886604dfb96fffe2b60c924e41a2869537fb474e103ccef8e265f18187790e587e397a4baeaf2420e21dfb72bf3ebf80cecdc962460a0112a810ca4b1ade7fe8130063ce21391b44c03f02571c04b4a3d6979c5bdd66dfc36d594e8e02a67dbff3513a720f1cce36a14cac1ccee42bfd2d4ed59ac8dd8b3bbd78d9b1089a16098e5d6f7a6fdbfeb3592b6b89b232fdf61c145b862265b674140ee1a5041b15d63ce79d5ca25e909958c46034423c8f9afa175dc7c4e657b22b1310eb2550365f39f70e58cdf06abf5651af9359ff25d296132f791d2757a56b75033921d8c2f47ac021f86c24e84b14345ff3ce8fa22e340e1f5ffe0b1ae928f6c3c5c75cba07b6ba63822f93a17b619dd89e94ee87661307eb50b1a65b9a3b225c8b5a5f62818063505d1fb4f9c217f94c9f49e6a124c93cc1ebc89a71091ff409cfc0f0711753584b39b76ed5e307971a93f51975e5b52535475c8ecdc99e42a503afc773c6a0e058d340f53f2b03d551d4c01881a01e0da21c47a8bf1ec99e1f87bf812666e7fceb876713fe85691c8b070f9f8d41ea8ec2d9110b880e864fc8b56d351b52f64d455a6df8768e00bf49d88f330a0ef43df547d8fcd2093bbc8fa3ae1a5f014f35b9d6a93a8bdf723946058d9e9818e2466376d8c3853d4af5dc4ba8e8a096edd2390ae27c09f720070ebbc77de26c58b2e39a346e9ca8fa25c1f92af02e959208b6caa797d71635e31eb5d5f5d1368a0aa995c1a3c742424ed4f964264ce391c2e0576433b774dae7358c20d5a205eefb182905b1a567b03e6466d0e9b4c8a3a305ee60b9bd25963e5bb4cb745560b76a009be3e22107a8ce2ac670309684def696de0560577d215c2437f84f9bb4fe863ddfbc4db29329c026a41c360144c45d3f0d743398822cccf0120af6e8298188f96c6f54b4073965127411d0bf0ea5ece9ae5de8e41646a37d1e7565843434569ecab11dac0114b39de1d77825d418434dee3139bb97dd0d9a999091da9cc135d6cd769517f73ffaaf9873318fa3941a4d47813a25d0308b93f80b10087fcb14558aa1565d78c38b09755dba9f5ab31720e3b0722045dac963d273618d6f4d3914d115d13e29fbc88c2ca4c52217463ed2359ef2b769abb7877817dd19a77ceeded98079cb51923c005119755460c3528e7c72d5bd2d6d204916938af46b42fcca240e5fbb08eed74a8292e2176f5319c9712bcf252ddd9990918b0d5fe25d858b8ce8a9a66b4fe48c0ad8a92f58195adf8b91e2ffe36442db0ab5fa965de52b516201a4bbae9fce6f96ab7a828bb622a0d381c15a052cc7ef268639b460e1e5cf315ab41d61783a220a250a5d4abec7d356367529b655f0c9da07aa2f2a7805f2934bc9c272b974b971555bd5c87ad49dabfdea989690963da0ea13178e3eaede7f29073b7d035285e34542dc035a1988354325447532b81c84ed1c1f2b12b7893aeaf2c67208e1c7481d4d720344ede0fc40d35357c70233d622f8e28c5b3051bc6790fdb201d11cdeefab664d5fcf1d93a6c698580113b51c6e5990231ff39887433651f684c35e23be4fe2454c0e9d2ebc612c72e6f12162c280fde11f884606363ed9d078f791baa64f500aa3d70d6032739c5b4cd4968a26f227c2eb61dff12dc9cdbae8fb6a05186948b4d2a31c1f9238d45d421508eb0265ef3a253c6518e00c64f4b77224a842c72af12f0a1ee986848748eaea390fe08144f2b123e68d6ee1b8fda0f39eced921894b220fa3b14671ac14766bc11f1bfc3c157692bd52b8594849711524182efd8ee40d44e1e6ef9b57c03531686b4b43e0784401eb1323931fb98cd3e944809ddc8ba63b9d1a5d55559881ddd9ca255760771ec1d26db91ccfb0caa1807f516e5eb664e3992d505742ee48e4457bc91d638a231766a49e327fc3214aca6592bf736d13286eae445a3424888203b72bbd4f86dc0fd8be9ab1c1c1a0db4176b6ac690a719581109d3693c2b9c254f0e2b0b1c0e6a2cf2fbc781b59b3a85a0312c683351703296b10adef5cbc1652730891a3eeb854ccf6265be3cea134671e8385cb72a2f943c94fbe6c058f925699b5a23b3b37118b195314e917e1ee6c289c6ed958ce5a7fcda41df038f6779b2147af7e7dc392626c2231aa447031c51d2581b95cdad62feec5a72ac8a3ebb37ed08839cdb606d0ed9841aafbb986d81318323b183b31f0aac1e1d7321be0ca3717ed0eaa5df2080ba03b18b9c959fca85dad513ed539bd198f3f6b7574c4ca1998147dd269553650a82103757626f9882878b37265026bf8785c247c09888833368b20d40204f48add46e7b27efca93b7579094c83bcf52b9a76c2b6142b031ffa1f38db8415719251a18582fb5b1cb3e535253d2c9ccb7137b8f203c32387e5124a12907ffc2bc6703402997e408d35601f4a4633c847d83ad82211e7d9f8291672d83d0616623e400c68c048c575ac82c9a03de85441a78a4e6779165aa1e042502aca07b0a27e92f8c1221331533f7eb41be33bd5e3bb40d4eb22fe716d98b8ec95e09061acc03e8b3b8eb9f742d6d2e38e1493a3c455a08f78808922a460315fd6356c23dcc4889b53a0d63b262ceb5b3ba31c7d00772db4ed63ea0829e4a35c2809be3c6a59c81f61941c225078d433dbf5c101dec8aafa17cf1e00d88daee7eafec2d5ce9198203524eec1caed0ec5832f112bc15cffdcd70cf641bfb737b4ceff1e0cbd2dc60d2aee534c50d23e93795c89230769c425e52bd81e872c5e995f82ff7103e385a366e7adda78ab9323b87d50216f3649a8b114eff0a5daba6c926335230c9f63959171202ec42613c2168af5b763a2124e0f8a0b2a28f6f93b216efc062a5f12ab6dd44dad289887385765b73d92de1b592121d60cec67c270917afce414eea803f893aabd627e85f8dbd0178850073deea7ee1a9747ca5c5b632349bd28a64c13fede4bb585a2827794b24a0a8c94c1eed600966e36bccd9e507990098198d7d4430318ead329417f53715b5f58708244da978fe7535c071a60f0940bec100c38b93bbbb83429ca40997671443acc9aea953fe95caa30b5dfc1633f0e77a9a91106ea86defef946159abdc4d2557b526700063a1e06c3205349b5aef122170e0fe85b0e8f1c8bc84c7d367180f548b9f36683f395aab86aec8849993800ee3176dc740d8569613bfbd7742e77254a7219e7ab8d8aac3cdb12b13f76f14cb4e3f88dc8d01b56d63e9c56a309c770bd89892de833bd9cd00fc8daefcfa096f1e8d21a083b1186689caed8a0362307d5f3bfda415124d6e34ce79b4521f2698373cd9cef37c3d68e95cd57244cc5398a7bb3371fbd61775ecf588217d1bf285409c49d2a6e7a5eab15679ee29491ac6f1527faa201aaa3daf62ab06656455d4b88139851f3d8dfd09f93917700536079c21665effe276f81cfd622a21bb3686da61514f240a0a1da8a4ecd3007519d3b7db9202226546ad092da09bf351525fdc5438ea7c96caf9e5fcf7792906d45da395d1dd5a26aa9f53d9c0e10f998b8c712e0e83296a0c8bbc3a2ab7acd5f5ce23d6e9418c39d4d41be3a5974b88a66139556faba0a1247ea404d164945465d953a45e60b5486fe2c76e1d7b374e8ee0f486d5ab48b34590a543cd612cdf4dbeea552b7de434c01842f2d366c32c518ce77a12cd4baa735197427e8ac1e07954c3599624a00add8ee4ead199d6ddbebcbe61c5456ad011f20a839aaa04a6ad0d24316b4db457e232e521821164e8b57969002c740aa388624a61030dcd15f5c809aa064f03b1768f1792c33faf2a8a71cf5745bdc001589542402c8dc0e8e039d9fb813d66101935b24f54478237a2cbcd13d165e910e0b6fe8cadb9d464583368d612b88eb0d908b59a6550bc3e5925e1637cc615befd8dada75936074a58c41f5ce19e1dc96e11613cc94ab42bff4b3792fa23104a10b66b881cc60a67aa703b51db234728394cd7efa9a2d9431ccbd0b3102d72c4c2f896c3385e901a6c84ea60b5b49790a7d838cb89b6d16d00c45229911db6b33225c151af57a57e32bc5583b2da8584c04e9b2cc21b30143960768aab6f3f0018fe2c328cc44f6b7c98d35af2dfbfb31494c2af084fe7e58453a6136689113ed10ac5b447c834f48806d6535a2f8a1a6a7a18f9b988e7c38cc034269960ec29a40615f17fcd00b11ce19f349ee353d50449975391f1ab9a234475a292db9c2731ddf5de30aba8918b03ef043169f4d897991fb9df32dfcd434b027f239cb48dc209840c1982b2ea209e19d514126ccb808fd82b66fbc90f5b5cb10030ae60470b7bfc28e252ff14ab608a2175350aaa4b14db4b81c190adcd522fca9f5aa99305c8379e63cf41775d6220e549437689e178fc7858958db9da3aa48556ccb907b738cb087f5544cea0a098ad3fb0b20546f59a175a2f0fe2e094b85c12b82d9b7019e72a47cc022ad0742d3e0b8e6819954b04f8c453694caf510b44a2c28d88f48d15595026886f624c3c72cf19e9dc34d4f0978213fbe145577787eab3b7ffc7aa503b2b034812904616236e306f804cda9043ddd765d423ae374b1e88af4e1599709f82a35525505f92fcd5233782e5f6ed666082855870e8a1446d4801d8e738b30db829ecc4749557306d4f0719256cc99089fcac993d16eafdbfb8860e465c255f7b19e23a5f07c487ef41f97736b61358d1d5e85727a9a6666aba7501f4a5ff6664250b8beaea1185ec1c53f18c76df101391e8c6f2458fe1ec375fb5728c5b4ce32560447031d2df815004c9facbaf908768f4fd9003d46d0d82543bada364cab773b734e869cb7220f416091e93b6c256be045612ddb8d284999f87409fd2a38854343b81f95758caae8f0a03d49b6c2df6dcd43dccf78c7020b139976910f20419e8f54be29ac25afae9af9500e244bfaf597a537d0d0f04b9aad9e1388ba0ba4e9dffe7259b2e21514d8684760c0a8757d3c3078d709fc3c2533bf9aa39e5adedafed8488b6d43867deaf57273f42c132ebb8e13fce0cdbd3d417c72676bb822993718a488896e38562bd2574ae04a187e49f7b71d9a58e9d90d876bb8c5ffcc6ab1210cbd3a2129f78c9266a107cb5929fc828ed55ad3c5281d8225e898cb7b6e593e0be6c731b98e4f8889c7853f9b6fca5db4cea16c1a9821f749cbea7b219264becaab1b5d9557a09c1158d38db1f09785e0e5af1cbf59f6fd5747f80d6d8da5a3f138566ecf50ab89b939b4e5ae3987ad4e89ad2705fd6532e08cc510da06aaafa9e14d29328e56b00320d81819596f692781d06fe1d854c25d00bc58a6e6970daf612e9c0f352cf92e61a0924a84aaec23ada1c10092ab9bc5c88ccbdb6ce37f50c2039dd9435a101e8548ae90f78b8048989e4854803816977a5a76612056c86ad8d95d0fda3268d397ab2ec71286cb9c1fd66ac0ea3b440db328fe42f49520e9258fdcf713c4263bec872f92a919838b981c7d3296a691072ad99fd195117006e129b02afa1a29b1be68b5e0fd9cb1743fd426140ffbffa904eb01f61bf0404b7c9c465889a4b06e11ab874e539e99149ebca99ed85b6f1848f630b82f7e82c631d481c7d2e5226249507865f74be8b041616a30f011865f57d5f0906574786d046223910f3404f315ceba93ef40f69ccbdcd3d352818ef3d311d1aad8b33e2d74b0bf93e472c70746537b7d3aa0c8bf8039b4897e26a2dc176f157dab1811abec7007cd966c2c70bc08ac1b167b610bab2392f62345a7f69a420fc7f260c1b9d2bf26032ca82e22971549a8e3268cfe4298636e9b077658b36e21bc28ca9f44ee3764090ff3859627aabe75d709614cd401914f7606dd8579d093b7d7b5ee16b930f1056e6471f51cb0842804a098ca7e80bf1ee48a25759001fe9cb796f3c4f83dbc76a8872d3eadd2f1de44d0eebdb0c82b66dac553ff0500184213c1bd3e17233a2685cfda3d838b1d8001eba0a46be31646ac3cf6a6908cebab4ab17bd5e90e309c12a0f9410a299645a4909caa12205b34c36e001fca9573c4fdfd97949fc0acd7e9e5005cd2f4fa6109a979d834ea33ab6ce43d8888df643920c0ac65ebbb6a1dd608c83c74e8789b4d7381fe35b914ecb67db36b5935470f6c1cdcccc7d1bb27e909229bb7636df9426136ab4f623a8d4ad177a076162113f1fd99256f06314daf1afec9e5050847e8b591a4693a27364438bc1442e0330ab09d096968c5ff3814ffb8322246d7164aea80b770f5205db38117de66d141c19faf0c520f907322054233daba06c98f7218893f2e2e0b19d0c8c8610bfbc5c43332d0be2fe390c4a5f34812efc89a31314cce1d56fffa75043bed30dd51af71a8b64af1b37b328f5533db431645b9e1a5038cfff7a2539802833716de0a217e96a9242f5553693c53b6f3894ba7013be6f6380c90346f90bad57bd2a5e5545041a5d0965bfa7df84c36065c836d6e30c44162943a047a7294723ae4f30a022a6119432687dd380502ac6107287dc9e5d5ed6d9a46e4117d933a3cecb4d30e94578df02031e0a2df8d8bd1f0ed6d9c6c1e27a7919347076c0bf95d43e80aeacf248ef9e70bbe08a302183d1f41771add951d44ea451867bbe35891db47946611ca9e90b0870974d3f12c789731b6285423b9f355a65ec1b3dbb25109bc90964f2cb244ece81bb981b8b3a345c863f1f03713734cdfda3c45ac224b35ed631f2dfeedccefd4ced85b9d94cb868fe2542f2aad8ebd9fb1534926a58864a5f903b8bf8cc20b643f00080177c0163c7ce1655e38765fc927300c3b0ef5c792beaf9639908f469826f8706ff90059a87a52ad9411f9a59007064395afd3cc650b7a76f8571084d5b8222680b9f384e850f6654441742e8c620ea7f83956461befcfc25892b5a5b4d6cdd502986abb400d04a25d73fb44527b005b90191c51d839ce3011d946a9a099deb153f9697f97f759b48cc0cef50a8bfbcfdc860db77248e59def8e2e3f2aa4d24b77f36e7cdc994c84e811b1bac4e882db412303724fadf4cf51c612bdd0dd8bbdfe7a7927be4817984fb836422ad6837c72631a32fe2931631261abf69b001c605c3bd0563bf56cb4da23b14763cfc24197f211479a148696f0217e0838b77cfffc8f4b3c63e6d9b4a7f1e42d82428de819b08bc834d3bf0b25d288892d1e763f3abba42ed2f1d0d91563302fcadb1223cb35b4d81c3e7748d7e4913b84b1211b51297790aea41f2a37194b1275df236ffc8c1044dfc6f20d76820748ed19d49eee9b87c3f64f402be873b4a2f5df5e3b5962cf54ab03c151080ef2a9f3ef158a2f9119ccb31a4a447eb1f4e4aad3f190df8ade4e59a79cd0449872611e42a226ebbdc8a3e0dc450ca5d59c61c95154d51e6c4d7276c746aee7bd2eb9c83479ba96a0d7e3d8a1e27e9fe81e87533387ebe2d3b488422bd8a69403cae6a5bc9a9355da1e8a91b4d4b96546f3e555a4475f24186b64d1e29c37e0c352e7acf52dbbae6e936c3f2d230ca8113bf4652869bcac35d2284228e1730dcbb5664152b0cc9c6bc5cce254f4e7e6214622ce5c7bc77637446a0049597e6ed1a8b43d5c3f0cd12f9e97b8c74552af6bb0b63436bee97e110366fda227174395ed05a5d058280dfc7872205304fea6bfd1af094d123fe111990c498d6d1f060bafc290907a5c8afe6064a0a05acf32b1a562e21927b7a2ed455da23cb2bc2b0860a0fd0059d4d700f7eb271df7d4bfc6109667adde0bbd2d599e7edb098c9bfa052cdd8eb57618047886fba00d7da5dbc4a86bdf0cb9f8ae691349c53065f10a336407d942d6f045475dcb386446582ac24cbb5451c0c71cb4c86a42c491594dc97c9ed7fb66cd838c9d900488993567fab7b1d01831e1cb40b1b150d30438bbb67e0df7db78f62eb4104a1b6eb2f4428707adc733b0a24153cec2e45fb97cc3d944096ad7b41dfbbe4c35d408ed8413eb8a0e2ba9d576e31c1d003bb19d2f8eb1f90ab87037033e3abb452cc3340e87990511cc144a78902f115b1a2fcd67e3d0cae0c0447bd2c67a2c82e6d734ba4c8c668976287c1d5972f2c2466076dcb319c528f4df67dbac26f856482a5924c976ddb55cace3efd404a44cca9411ecf8a1e15362050b3ce1afbc526bb221277d69ebf7e23beb42ea7b583c0a503e6439612a70c97d1c38d9515da46af512d2a701ea127344b081c5fe11ab69ac5db46a28a0016422dfb5a95c8eb4c3ce0d9f237d2d6c5f2ef8d17fb87d0f8ddac525c715e01e00008ba142e2d45cea3267c2a1aff5f9b4a874785a20f74fd744d0af2d16ad3c822e5bcd948fb50ade323e68c8f715ca14a9f24217b1a8b8152a9462df43e351753eb37b0f2a8d65937c6da497d5bb2f6191ba8bfdc2c1652c40b8c6c01ac2d6032b40ccb8c3312b0eed86e486eddc228c20cb709c100bbe2568284a942261874541764fac7babb939f6f8681155b9a1d9b531d7080974f203442d35d56531ce2b8d554b227341e1c7968a10e5676675450f1b8975f863450d6d5932bad1a2812da0c8bac3388a7721981d12a51e3ee50a01cc81820673ae7a0babb36a9e714dc3d4a56a62c0988b9fa24632bdd344bd8ebd99dff77295581774fda228e5a3515d07d1dd4722d4539f79e4bbf37f160ac842538a11cdcb08f8582742b19113c460c42efbc3578d4840e4c8876251536ab010fa968d5208d217b073bd187fef2ca0308244eeddff3d9d9862fc71b043dd5c9c3a1238d9717ab0e4af137781412e1354e40d67c6ed728ee78211ec44815c77e75e12bdb3b9f00c2b57c93a4733a5727c11c8e57957eb59453799d12883561bfc937da11836308e6f61674d3e5c0643117be2335172e2e3d871efbeb719df9498f484d705c4f0230c614edf1c39e427cf4c2cdaa8c94b6c84df05d72c2c188e2254852d0b2f260a19d17679902a262cb58d9c1bfdf22e6234d7856e34d6a797968f73551e29a0f41ba63974c2843af00ed7dc668ebe548a96d2954c7f870b2cb22856d46c4b0cdb0840c488e8cdc7b6dfaa8156ddba1f80b80217350c7cd2c11375aa7233c4af3cc0b3128e05196b3a1644011e6daea862a4d2d997b720e3bda24bc9957e2c00e4ce69f58bb8d7f8fcfed12e57fb08af3276f00b2da37a3c933e10218df84423209458df92bf3339d0c469b81c4a462427e810155597a3354ffd617d3d0daf87d3b7028aaeedb8be13140354be352cc56f8b82ca1dab11cc18609633f4ff2d927860b5e7f4ce5051481e4e32bde4f1f5a720cbfe1ac689a26b416f9a80b6be7358ad6170725005549f821bba38f3f0280790b3b1a07d535f0116738ef7f8beb2637304d432ba6dc587320595d84745f8ea47f1301fcdf5014d0716b23333846951616709913b4e9a81629478dd915c7e1a70340d59d73bcc3b9ce08e91c247f20a674bd4410e7624850daa7e96e4e5269e58a6fdec6957f899e4a03f7c6a92fe10fa0c02fda5f4d23c6b1a637f2c291697afc2dcb36b9a37d3ec38683e66670fba934a721be6c1bf72a545accd8985ff5ffcc34c7c1b51cfb9c0b7eec8bd5f3dba38573618245b4f8381283f5c43f02525db84628a66ecba409f4d49da6f5017e4efe7e0ed2b19ddc6cc2117bcb07495df15d1aa494c79ca9d51a139905b2da039bdf7c50a215ca498ee209d228bbe5edb702de419d252959bcf4ac6259154a3ebebcf226087c9e8c9338c5b15c5f28bba23379c226563b39a97886c3aa2e890af47a2df29b4d63fe62965d7384143932d5411da10320f222ab4e40b344939cb7d783a2c6c4c1645282792e4021934401e29768d65ccd83ef49aba0ebfdcb760a8fe5a259fe8b7613ab52ba5e57483c7fb0b6a8113fd607d531a562790ce829ff4d3582e0c896c5b972e883e6df626d4dab802802efe7513047f5200a7705cf899ed5ca448cd973d241f1dc3af578fbe1cc43c3e4031a30308c00bb93927184cb8ba5d0524b698af48b7c30a976a9de912d72e5d719e105e02b6c285bd1d9cb61c34c3d1f8c01d21a0e3ffb4e8ad25cb6d29370fc48275a529288c029d7050db7541fd47743dfe4552f53e41d802e0d257608b34c96ba07c92a90ab59d95db2158b0c859874e87f6575e3ea0838f3584951d39294166abc8774f5d1719dc17005c80f86984d2fdb539004606219e6c31979cec4897e43ff15721276dd62b7e98c7a39259909f047bfed70262dc6c5f83cbbedf367c5e42a55ea03bd58fc54e1bb459c012a53d0ceda892f7acf5bda31463a4101ffad10780cceeba96edddfb64cd55f05c5ec26e584bc201711ac149397a65ac0adf8a5aec8cc9e1bffe9f3486a6ee3adfcc209cc9725552e13e5928bb88eadb62a9851c03b6fec8111dcac363f1e5002250a651811af9740b6745368ab3366c4889b47001416d0943b44a951996c8d3049d46d293da7dde47b32f6741616b5c14aab41ddc4c5c2eede4115930057bf5eaff642ec9e7215410a90a55b6b56850fb915bc0955413bac6d0fbeded854c10aecc337e79d2b5891afbf7b32d040a05b63bb9fadc599c4bdf1e4ec7e55e96647c0cea0b67f0c6f4e21a0657f59aa927599d61899d5830c8ae23aabb988ef89e11b13b80d91baac1ee8b6c81d5a0a47c5208ddbb2b50dea05be277d60e1a15956b26aca3c93e79ec5362c54c0f4c2954220be1aab2418854cf24fdaf96b91cdb65fe45b2b60bab73924c1c9ed66fb465d6e636ed39a12ea33587d51217f9ae3c6bf752662d99936077a0b7408ad2e78162557cc903bae2a3dc54209024a50e9f2705136880fd759e7727158c464f42020130fc2faffbf17a6959c1c08c8c2644374c3547b3d5f24609d329b7b45b7128d50d14b5b28c430887128a6e2529d256fcf362b67b1b244dee2de5de7b079f0d260e8f0ddfdb9ed68705fa6b2d49d74ebf6a29ddfcb56baf176825dd94e376343259e4a3913eb06646e557af5f73582ecb6833dfb39a0832f3e9ecc901139111254d2a301471cfc867f23d27b3d2fa5b79cfcb5fb39404b2ceaf59267e65d55a7867a33aeeac1bddaf3c6702ec13fa2a0812024d6e495dcdc567df59e5cf0e8e11b31b5b4d5a716795fef99d6d3a00529484ddda979788b8b3cb25df59a6d36b6e2d33002b64007688cd79796777581ed6e7d7a004c4ad4dec8d0a5ac418e194044c8a52ace4fbba34c11189b524308c040112f79569268018257c808e2879a2c47d0dfbb2efeb13aa566575f3b0327908f2ebcafb5ab4b2353720d64c7005c11af26c92ad6b1c4e4e94efebceaf2bcfaf18d4acc7b0f5abdac4c65d75fabcaacde76b8a860445d34fd891acb878ec7b1a1d2372e247ca975412a458ecbbdab3066513acfc8592aa6defa68d73bcf212985862b2e116fd3ad213af61bf7298bd61658011b92bc2c952c66b925f4338692d77f804a9aa9114af60bf72e3baf25e92bd59a31bb80e0ab5aed15a6bada3ec98d80dc1742dda046bda39350ac60cb9f72854877caf20c438ef7ef8432e1cf20ec3a15e6aad7508afa5d22b9d96a169ae3fd000d020b48f1aa416598179b82271c5e335e757545ef322d6d46b6e0401f43aabf9e4c6ebacace86b2e02a8d71c055a4c5e731b8cc001f27a0bfa9aab3dcee46ba78fd7bc8d7b8f3ae3c05931aa50213b647162d8282103894487f22b0be881e905c28ae74d3864b5d65a87e8713666a5ec7aa2ac126bafd7413aafb590d7709fb046c813b0f26244f9e17098c51cfa216fdddee3506833cc42adf50e6c341933222369a529d6485e03ad78ad6d6a725fe3de0de242ad35af09ba416b5175932e32e8265d64969363b237e97038a4bafaa1cb0f87544a7e58c554850b3710a518ceac71abdb069bb6f7a89d4ba4170a79cbf61e8543e1506b55d51cd64275afb5b0c646187d8fda7964c90f791ef296ca7b1c46b34dce7943ce3539e78cb5f523cbe42ccc99ab3d6639e7ace6615673d6f2c912724feec939e79c6524cbe4bc61269fb98b8bdc30e56d6c783be53ddad8d8f076ed3dda68197d4d0d6fd5de634d4d0d6fa5bcc71a28b4ec8542de46798f42a190b769ef519885f3c3216fa1ef71380401ca7b1c62358b5e6bde3ed16baf79eba4457b8f59d34802ac6912870026406dbf33af1f0e79fb7c8fc3a10defe9b2264db24306180c3531a2a5ae38242d364b6a78abe43dd65c0df9e190b766ef7138ac8911f6130a73d9d5891f0e794bf61e87355c1dce255b6a8463ef7188650525c663d92412171bee1bda0cb50b15a7d7fa2c6c38ac3900139b9c5953b9638c2bb9031edaa8354138e921d87787608739043b906f70954c2a3e6d129e93c88144229148241289c44c2412894422919873ce39e79c73cebc057b8f397fb130f27a8f1a49d77b24b22c11d6e7cc7d442191a85588ddf4acf0e20212893da59ed65a6badb5d6422271c85be77b1ce69cf356ce52cd136539e79c62cae74cf499b75bef3113b54a5423084fd4782291b75aef91a8892a8c9f96e150186cb3a44ee43c91c8dbe67b240a55a25aa64624f4e36845d05a9c57e767df9028b5037dad798bf51ef50e71f883c6415c3dc9213dd18a98738663f1e37467761764eb0a9108352db34a7503415583340ed914396972c9b4bab3245a9f5f3859dd6d8da928cba282a42014d6b45343bea60458a5d8a610a2f410b98024595b328cb0cacad66653a8aa504f712d884f9ac8b2e91299bcc204888f1f683b806049a1200c7616172b42c4e05cbce630dc942590a8d7b5033046fcf8727115656ebd62baf021f71167d6b899d58d483c6284e5e9adc98a4f1198484452b98087412722704f24f276c87b240e53a5302947d388e619518b382b1bb0d6425637222134e955af295c63450b7138dcaae213f654452dc87098752ec259e90d3991fb86c4e1b0a60522e42dd37b149a5d6003b3c0167d6acb7acddba5a11ed62085550860ba38b3ebc1f34308732e86ab75cd8ff79885bcf5f11e856c4f3f1cf2b68716aa43305886c1f6896d28c8238ebd51a19bb8b770efbdeebcd7bdc17684bdf7de43be372f76d87bc516069b5f424f24f276c72612799bf41e894aa5178ca657db67de22bdc70cc34ca40e98489be33dea216f71bcc761abdc4420226f261e3591b4f8bde1376f6fbcc7bd9131aa5888c43c34eaaafabd795bf41ef75e5718689bc87d7b666675dbbbb5c1c295f4d535e2356f89e02d1ceeb7c7055d8c1f664f685575615a48ccc31aef31b749ae1716869cec4d2fb9f46eeaba2e4f5d1e247782ebbaacebb2ae4bc36d715da1cb3b1863dc66930b97c5755d9eba7cc33fefbadc451283fb376f69bcc7bd85bf792bf41eb70acd416a8410c4ec336f8380ed0c205aaf79d6bc95f11e75f61103f91e5dcd12d9b8283959502a215370a8e4705c228c16489b3599985e0331f19ab73fc05449fcc01387d9dbda13b8a9a9cfb9ce4849e4adcf7b24b62c91683697b65ef3f645d65a6bad75128a4f2a0b7bf830d1f411d6fc81f499b72e5ca2ce6a5655640da41662765b347bdc78ad79dbb314166978076e8ca169f208d1f86868686868686868aa964f434343036d59e876456ea6e8f139f35645cbd3a6788f343459c31a56bae9968fa62545603dcab6a4de5ef30f728ba23df11edd2721e4b0dc21a74dafb42c454bd5523a13534d9da68f6689299fb991ea73d344db557307ae7902eb3347818dcffc054d14739f79bbf31eb38b646f549c5689bd51a14aaf726130180c06834135180c06831ca6fb603018e45866265e6bde9a788f5ab725b644609525f1785e68e5c06973de636e49bcc7a0eaaaca9cb30c1a2db91d4196f6aecb5b11dad539a7411a241111d68285d84ed41c9ba66971eed0e09eb0c69ab0c882ec5c371d936bf2ed3a3a154b4c49852b499d7455e5d24f6bade79428df75b996d3db4d80d1d3082a646d4d2b28284d30c7b95cc3bd4757498e95ddf6b43d5999952a7003720676a6947514e7a7ae24b20d06b3a02e7421bddeea8a222982e4eecd5d957b078305c83a6e2b97ce6b2eb02aeb9a5659a15624d4c24a0fa9659c84363627112793a8ab5d9546e5c135840ba9b6a2241e2725ce2325e07a127901f5e07125ce463e3fb5d69aca7bcc41aea7ac5b736b6baa14122161cb68b9e5440d6e71522405968aac50a9d2c34656c40485d54d4a94f7b8b3ec7ad25c9aad64cecc17d49a0050154a56ddd2bb2ed74f34329802a5e813164f35aa984e753f7bbb6ea671f21e73906bb4f718ccba724687822e4913a01bb4830293ac2b35de3d0b22c59c6f3945d2ec8aacc4598d467207734e95cff798c93864c9b5ed19afe7ac2c014675de280892234d475b53a989481d899f12b3f7b807b08100641da0b041b15e7338e73507ee78cd791af4352f46892a33baeeba2a836e8b675052f389912543c25c976cec3d069facbdd67c43418ac16ecef9e84e486bad735812fdbe3a2ec8755d57837de90b298b974cb3ec738ac664a9494f931fad26666845684a394e889cbc7de61b78f2e23317c113245e50d7d599cd3968838e478c6847d991382157d858b6ebaaaa46d2657696c3c359122e56396617e7f42979d278fd6c6eb934ae5cd85a3a6b37a8d3ae2b3c7bd364bd47dd7c8fc17c41d841c95c99d612302cba1319aeacc8becaa694995dbde6b0122326706f56b3637545dcbd334db0bb4910aa3215981eb313d3224ba25596f49aeba934a74da290b44579253d4936d9f8cc3f70b5ce5259b76d8adc6919db53902173229a6a6b901fa51ed0185c56435120459ff90734aeaa12204ad550daa885af3b8839c5ee5e73201fe3f1647c3e5fe735afc82027aeebaae992454b674c4b6bc96bae8f0c8db918c16a51e35d97eb22ae6691551220ca11e76206514f14939db3e7baaeab9d34119dc5e2492bcb39c475b3d61a096b0baf85bc472dc1178555b68448b3d9a4b5d6ba86883788ef82f0eac5868157629f39c7ba01a498b54ffb94baaeee852904104a3ec8b9997a4d41a16ad2a272ab4262a2d062423ff3043420f30940a5616debbe404922ca13e58c478e253ba1093f7c68251a83ee10f5a4ecb596827a3d957cada78abce6bac77bd44ea79bd59c836362725724c059d54a3cde63502b6ac4bb2ed73b5c570d6a5ebc0b7b5de1b54e52853204600a60b9fcfc74e22949eb090b4a470b2a0bc95555a5ab4415a4f97397220dc5fa238a266b1d59e7d05af27c8063a99759bbba4acec60a2ad12958347cd4f87ad1ca9ce4fde06d096605c8ca9a95b9cfdc08e77504c2dd1b8691961b79899210b2d8e298596dcb1359559a64b94165bd253f736da4a44183060d1a34a81ab686cc7591b6f11e35b8486516621187434e4ad3892ae2eea9a712b5359a17768c0977d8f148725555916f579ae49c4118ca2d8d1d3d5a48e79c736a02143a6e8439a9d1e2488fb83317554506bdc73c438b69a42045a425af3930a949065795b232e7233b9618592375b4bde659730d23671a554004a0c9c1044c47d34fce39d71892697048799de3c80b6258d8d89a13b39e3ba53a33a08b1619c0c8401c9df8ccf3d65193cfca11ca301c3f7a78419ed65a6b0d0134b61a563f3584ce62cde3b5d65ab35861b644ec458a48cd8657510fa31ea3b4cfdcb86574f779eb46d867ae55bcc72cb7a285678b68eab5e61b4e01545266b9b0cf1945b2ecaac68d1a77afb93e9175ab27307442acc759142062eb8e2125bdf32c005ad75710744b0808d0160daead21a6dfd2265410d89c2e74243179b558935ab0bc4ce95297e872b1eb9ae17ccde120221d60de1130a233a56cc695b7ae15c94e08cb1a71b7e1fa6eb34c81130a671064174e8cb8db38bfdb38c36ccab6707cb779daa0a5d918a5d91cd9f0d8f8d804fd6a53a4f4406793f32bf7b0646f5a50e307d9561dd20d2ddefaee81099306a539e58992e55cdc3d687df700dd8af1dd43d483db130f459e7838e2a1c8039287a5b6ef1e9c7ef580ec1e92cfa8079c1d1e17df3df478f069d2e1ab4987241d9c7ee51d961da43a34a3df3b78fdda41e9d70e4dbf720e51f6268d5e81010764c6758723ceface216e47e8c74f0e2442e08ee2dea1f9bd033259a4e27b076507a42787b527072a1cbc389471406bf29d83daaf1c98bf3a7fe51c76d89bb5a829ce0aeb04130eacef1c9e5a543098537ec4883b07e45f7de790d4d9d1e4126a72c4622cee1cacbe73506ae2e0b495f39dc392839419071db35779e760e2d70d6abf6e886e70a3b3facea16f78feba01fa2bdf90646f2c40e3476a87058a660f96f97d8332c4092c1a76502b2daa1971dfa0c4567ddfd0a4d60576046cf9222aee1ba8be6f603a37786d08db5096b541286b68434edfb0c3b3c1a76f0872c6fdba01e7575ed3c4dea44f6ad080631b027222c553df6b96364757a24077d40df98a7b0df3a5bed738616044ad8e3963c9d732ee3551df6b9ed01a35ad2adf6ba2356e456a988ad404a909aa49d614d5f49aa57659d3d36b90bff2a293bd615124bb925d558c2063a8efc5b0ab215c35cc9238a0c0887bf1f94fdf8baa942833e6a8c8d8f123eec5e5f7621c4eceafbcd7ecd4f0b449c5ac36a9b8555cfab5d854742a4afdcad35e6c8b6d51e9574e84b23720300ac2d38206598ab3983df29d1805e2c2892ec9d2dc32ee4423df8b3800a04b128243869d0da3b8178b7c2ff620839c7c2f268b452dd1494b84426c12bd886508f84e446b89cb96c8fc951371d81b15ccc50945b9a36222c54edf893b44640986e0989c84a0fc84c59d48e43b51a9445c2236119d5a1575ad8a3b0d686a6ebf6aa0ebd110d6f3fc956b40b2372e33ecc076e8c268e088877cd790fc6d41d901b6c32dcb2bee1a9442be6b5002ce280143c89619ce57dc35347dd7b0646a68627dd7e0d4e055a7e1a74e038c219d861c0d3c06f8aec1a7c519465b9c61dcb069c81c863dd17e1d427f1daaad0d93d6863c863ec3a0615108df8748d1e14e1ff6fcca854cf62695cb6a3274b6e543220ef25de8fc508743896487ae845bdc85610fe4bb1086149f1b4ae694d8e2e22e64fa2e8c0ae384744ddf8738c31c33e19499b04a88245c123a15e0bb50ea294c0a95bf729a277b1382e76c280e0994c7522a5efa4e03a5c153101b9d92ae2332e24e13fdf19d26ae01657177443c4f4ad4e22ef4f15db8d323f461fa2e440a83b268ccb26896d048d13469bc08f09da6cc49d38443d3439354d22069f9f84ea344b35424f8562408172c0ba2a90de07bd06d19742ec37ee5c11ef626659ba17cd24545841b55dce37b10a90c362da58ebe0799c166525045d2abbc0759fc3ae3f6eb4cdc0c5d30e7d7208fd20c54696646c9dec080476eab0b8dc60c2ea5ef332ce811b4240245460917719fe1f17d2665d349c31ac1c3c34922ee333bbecf38c3facc73064d0bcd1869a199a3199e199f99a09bef33453a3983d3677698611c1ae56e3c8ed36915bc8856c18ff0228ec49740f8ce9d7a38b227f92bcf10c6de70406808902a493b7434e3a4ef199e19a2bfe67871d5f4f1c3a42dee1990be739c1d9e15df790ff7a9cbf0953324c9e094412a435300df3378e90c4a3a43d3af5c26cade84d061244a5d130869c22866757c978983fb3a23726745168d7b069ccff13dc30e13aa1d28b882105d99887b061cdf332093198a32283320adadad5191f192299341fbe0bb8c5a54861975feca6576d89b102d4a503939a38060b8507e97e949ca2835397599a58c543693d1c966322630a86170c3401780ef32394f0ccf27f4578e21c9de50a0a2239aaa7245457cf41d831243130a8c38695392319539e38e6179e33b06a6d32bed3b86300c65631884f21886210c39a91303cfaf187c302001f01d43908bbce056c3fb05baa5f3c253ecfb05e80d1cf70b6a531798a62e04b910f4eb85e485a20b481ebe5f58625ee859798c93bdf941f972a2bdbd91788b097b71e4966465c98ce28a7bcc33063ac16bc96dc9a7e82e47dc63a231713839bff27e61e7d70b3c4c31594c5bbfc62cfd1ad3e48b7b8c538cd4af31cddc14a3ec314abf720b50f626448a1ea09c2a3c51a00c31fa6e21bae168ee499527c8a9b7b85b882bfa1e83d364d75642672dcc088cb8c7ecd8f81ed3838c09ca5a7e8f49c6141959706204c542f3570b4e0b5e16ca84df2da0650bcb6c8149c38259e40a90ae1f6344df2dec40730ca9eb91c3cbc849dc2df4d4f86e0119820412890e9194312124e26e2199fc6e41a9d42d345970ca3f15eaf24f85bb0a6833bc57805650732b7eaf4087ac10867cfeca2b20d99b103fa858213205c98cac23f1d0f70a74388ca03aae4a98d1c5bd8292c6f70a4a6f5871d1276344b0a2e25ea1e985be5758327b05e7af15bc7eadf093756000e97ead805321a7020fcdf70a3e790718cd3b4aec0d0d943776441189e6e88883be039b7e5d6bd3724e89b0411577e0f2677c0732532f1952804431d276c32bee40a78cefc0b02710ad03a14035f60d98c4be0179007d8041c0a25f814871c09d0eecf995c330d91b0b14e14a6a9b52dafa89637c877112492942ba1091838451dc61c290df619e37885272b6f374538ec51d06fa30bec34461e2e89abe037180394e60a69cc054c120c9f00ea304b304e394e13b8c141426c9c2287fe52f4ff646c25318129411d75251cc027d7fd99281c5e4c5d65c9267dc5fa2fff3fd25c4511b138d222e9453717187f1f90eb3d303e303838409fa7a31fb7a59f222f5eb0bf3a5f9e225f3fda52ceca589421c7bd380280ba1b327413884c42fbebf5841cb19733a13f3318afbcbcebbf8fe328020ada437183372f0c4fda5c5f797a412e9579ef617a597a5a9b729380a659c770a4f0a6814d4307ca7e0c6a4e06486fdca29f4b037216c34e958e6a8d3e159f67ca780b4c11534850a203a888ee24e21c9e23b05650a562662c5db32aa2828ee1494567ca7d0b494e2f19d0293429365a2a08265a2c0c2c52d03ef2e712e7414722e7ca7c0d3e4027589b2373a4863e2a20459108cab58c57717250f539c388d1883d1e4197797a6e7f9eeb2dc0155e48755092b19397177617e8aef2ece3097b2eef27441337231323a72e1f99577971e1f97a098ef2e45ac0b0eebb2f32b9fb0646f288cf140bb7a02b2e2448ce2fb04e6cf1042a7e624874856dc2738c348c0c40946960b183d4c719fb0f37d02343ac1ede8fb84b809743f138afc1c9950f4eb04252fee1390262c59f83ec189454e40feca4e48feca7f61ec4deaf505a3884aea8d4846cc9af8fe7b3a3d7786a243383c70c4fd07fd12df7fd1a8dd11940a2b51263ee2fe8bd3f93e016787e727f44cf061757e5fac4e929fd3afbf651bf79fd4af59e1fbcf8bddf929f55fd3af5c4294bd09d102ee696528c896ce539cf35d429c180eb6145052a488e2fec321f1fdb7a3031a4484be56d05d19c5fdd7f323beff90c95fd14ff9435a5b5ba322c1eb57096112ca24a001bf4b505ba312a46cb8841df6266532f9e84072ad1161c622be4be8f975ce4e286d4ce94689b84b403e8bf35d42325dc264068e13514e28b2e22e4179f75d8212f2bb8426094eab99049dd54c820958edd70e476137ba09df25e43ce1279c646f62462cc1e13564c9d623b6fb0eff109a42e56a6a06122aee70dd77d86955865c438a1104177798ee3bcc747afdca53b82c0b16ca1a8273e01d37ee300fecf3fb0e0739e3609c5f798426f6266da275640d49da0f27a3f8caf70870528020cd7d55a97211f70873df23e040489b95ae1d534a92718f20f73dc2131a41adab08dcf708d1086e6b91084c6b910841220445288a8024e17b84a565849e1e01f92b6f71b237a916108ce472ad0af447dcf796b01aa42da5512912a4458eb8b73ce1beb740a1624d7e849db539e1897b8b95ef2d713839bff21e61e7d7083c492d59495b2d4bbfb634b538b548c1df5b9aab528bb2b728fdca5f287b4303b133263548f4b0e28cdfbebfd1a611d1afa715d34adcdf38b7ef2d38ed9153d5db5559588ab8b754f9ded2830c7afbde92fcb5a548e87522f442799b31bcbfced7eb2d8bf0fd454bbecbe4cbfc95bf38ec4d9a66430551db931f712fde9da19ddd1b141e4a4149717f7b5ea403aa603da77e642487e2fe265fa5d2d2fb3aad2a58ea561577bfa2fdca026551736bf9ce42d7c312d6c3f2fc95b320d99b946943445490ac3817686ddf59924eaa9e675d6e3d521c893b8b92ed3b8b52881a5b45a2577eecf0008b3b4b53f43bcb92c9d22cfbcee264f1aa63f9a97b95771618bffae87867c161893b4b0e0bcffb9dc5e7579eaa2b8e127b1342a9870da616289e48bc51f9ee6b82222249465b89a710acb8fb9653befb987663497420d9b1669722ee3ee7da775fd8d387d644f2dd07f5a939f125a94e7c3c7c3ebe205f11cb771f1274adf1f1389fbde8afbcf7e23eee3dbaafded4d7abbcf7aa7a48bda7d45bea39fdca7b4f2aac974c9f00bd51318272f5e416ab7de7412908bd612179bae9b88b3b2f2ae53b2feed76454d4468e2039b5b8f7703ecaf7de4e4fcfc788dbf71eb21734c5339b5ac293fa95c75ce3ce6bf2bc7ee595fdcae435ed38f6660750749e424a5f3f6871da779e958da01d4facb00111c59d07fdce93c224c7634e8ccbc85bdc79c887f29d9754f290784abc25a6fdc6b4e176d946db6afbfb766bdacea61ef6268411273e6f6c5d4378e0889ffc138b2947ee58e8b8c57d27dfc9f78d24c24a0f265542aceee2be95d0beefa6e596daccdd34da2a54a3cdc27573e976ceaf6cdf378fd2852a957e5d9d61654adfdda78ba6aa708d5415ee91cbe3fab841c1ef6e51cf5aa3f2ded2e530c3a037beb7d19b5edc5b371db445eada236d518bd42efdca5b95abed0ac3aec9b433058b29ee1afa4dbeeb688bb3d3f2346904cb679f6b11525f959e659d9ee5eb1269f42c4ffb2af56c73753ebb963dcba660d59e6559d8cee63ccbf2b03dcfb2483639e3d9ce163dcb22293dbb843e9b82659bacf359ceb29c8694b02edd3042501e060606867318186e8459b9606820c100616060666084e67e6686bb74ef714648c9bdf21ef9dc7b8491c14e10271012698491f8f7220805cc1aff7891bdc9f18f1bd99b0e3cbda83c39fd3051945d7c0781d6e2b7c63d758a20e8edf74be97076b884767ec15f1c0c3e293f18e4aedc7b0ccecc748dfdfc4c1ce79c73ce39d0857b8f9c4391c2c20425611f86035d2bef11e6ed3dfe827afe05f12f77dddee31b44e261ee02f30b2b083f0e730e83e46160b85be53dc2a846a44af8b86deff1c7f61ee119429e8585bbd1fd2263e95f7ee21798bf445f5edc7f61029563e5028d8d0894f8a505d78c1b2ffc05868571791819560f032383cac37097ca7b84e17c73678bb2e753a02274f80069642a06891853ff8be1f53f5e34cac059034125096ec8d37976642586f98691431e86b9abf61e6129e9d3ebc2cbf054c5cbc8c87038c7cb70c1b07b19ee46798f32216640670811c96246a3b12f95f2be293beff3c53e9fefc77d3e5ef53f9e7fdc4d7b8f3fa811c8c5176dbec85d28efb1f82b428496c2c74e139338f7b490f239c359c5ca2e70e40999bb58cb6f9ec96aae10855215d13939a311e7cc22bfe0cabdbccae435a76d2acac268e818427424ce2bf2cfa74673ce79e5f99cf3cefce5e5e5e5e5e5e5e5e5e5e5e5e5e5e585bb4fdee38b0a9fcfb701850f2b22e2962889c43e9ef7f97c3db42d3bb819564e8e621f779dbc475f54941454d40feaf7c1eff723fbdfeff7e3451afffbfd7c9efffb09fadf8f92ff7117ed3dfe52c0f00dc3a506f430303e331ebb3434194839310c779bbc47181817732f23f3620813d885d3cfcc70f7ec3dcec43de7dc7dbe47de82f930ccdd25ef1156628cea357b03e8f552a91523962ba056b4b108c262ee7bdc48f53d0ea2e7ee7bdc357b8f3df626022e9624731c893e7dc5bce77198ee7913621e8f0b793c6ee4f15e78a9f3b73360c5c6d5c404d2018b533c2f8807c353ca3e1e77538b9ea7223d8fe7e3f1b80a2d0d65522a5890a18081818181818109f13385094e1429a626576218ee8ebd4798dfeff7fbfd7ebfdfeff7fbfd7e3fee8abdc71f8a082140b2a20a8fa0116e5df14b3ef12f3f1bb2265a40a6a01451c52fdc0d33f67ac2ef754021e37b3d1469dfe33050e97b5c29eabec7c5d3fc1e7793bcc71ed87be455f90c19b8fbf51e3388558143ee88e18347ac775e73f8e83577d13e6e646f6492aabc86c288100131f11d040933dda2b596d11a073c6a518a8e86ccb012eb123a43f40addd31a46f7ecd8d03cfa45eb9fd60c40a144438c46d0cf8f58e70490e6e9ae3e5f67ed6a047cf8608707d70e15ae133b3277bd328956f3f8e65db5853d8fc7813c6ee47117c97be4fd5cfe77e17f3cedc2fff154eb7f06f8df8fc329fec75502caffb84c40fd8fbb5dc59cf3c25585e845bf986047170e09331240a6e2974ce25fb87498fff272b3a3a335058a16988bf885bb5ceff1e53bcbc3f094c5c3f0cc9593f630dc75be4798cebfc7d3b8efc5ef75f81e4f7b52bed7e3c01e77b78c3c5e4686bb5aef5126a8c29b8b44cefb7cdccd7a8fbe66f4cb9ec89fc85dacf748dc7c6f0eeffc7ee3bd7b28d87272a2e4c999905abcf3881d17b5dfbdf7cbde33524feee4b09c4819c53b8bd874643b69f3b6de7b8770c454c4cad10a287731cee6e97207eddf56da79fb76be20fc2084a8ba1da8baab137657772a9c775c3f5c384964708958c23582e83377af54155673732c3fbe7f582bacfef72342040a179d0f1648f18fbb56c60320403fe90e21c4397d5e7358aeeb3507b678cdf95cd76b5e84f1da7945ecb5934ec66bee32dfa366efa25ed04548a376be8ce8b0c8f9f183b58bf299f14b9b24c6823c65ac788256c5fdfccb0b77a9dee30b5cd5d3942bf51e371c89a7a1e16ed47ba469f96eafd87c7cdb8bdb76c50e5306109da6ad1971dd7710684cbea7a954eb6bd99667e208ae4e2ecea92a6e59baf6c98af6d712dbb50dd193020512e455159a53dc5e69a13dda18ad6edbdcead617058859d191288a9fb9ef6a94b6ef2a57bdbe2d9369d9b695f2f57dc541b6bc7644aba16d5b35b3efab124cdcd7a5d5e9db166e5bbe3ad7b037af7645fbadd15e4e9d68407c9cd25aece0c87d6777a6a47d677b5a36884d16d79826a11d26ab261c497154ba5a568a6daa366a67a1d11edfd9284bf72d4f37b4ec8d5187ce1c931135197170df33926dc8f79ca411f75cf46d46e24adff2766d795eb2373b20c16694c488738a8a95ef99c976f73d3bb357cf65f9d981f70c6dd32d288e2537a86c40dd60dfbee7b83626adced13c3e1e78d7481df42d5107495372278b0ecaedbb565659fbae9578e2ae97bed54e46bdfc56339bdf72ed6c75989b90effa2923ee1aed5bada6a3dfea384df72d6f715a9ef676e72dc7f7b68742dc5b9fa06fd9b655b62350238a3a428464c95095ef6dd3d1f776c97b2bf56ddbfcd6d83adbb0b62c95e3696b3382a56247595b8b65e2d9eeb6a5cb31f2eeeef07ccbdd1ef6e6046043538894526d3f71cbf6dd45aa7d77933770778bbe7591be759578779b5ca76fb991f7c003ee42bd470f822abc6362aca4847d4c0c779fde634c5085814e2bc9371ab9bb7c8fc6a00a6f1a9ab7234f73f6343434dc3d028481818181818181e1ae91f70813546163d0e9862418e46e11e0cc0caf22f733333333dc7502a62f706cdf8bffd2e15fb4aaa43545ed962fe297e8bf1881fecb0b07b237e9d20997960924766449e217ee12798f2f9cb7459fdf3ce7558e9e7377c87be4162c58b0c05d21efd182b30de83d78e06ed37bf410f61d3a7037c87bec20816f0951742f81bbb840dea304968f102df21122b0017d04ee32bdc708411586dff77db9bbf41e5f26bf81e70ddcfdf11e37b0f0cdd21c81f32c2cdcf5f11e59a898781b9e6db8dbe33dda04557817b973cdea8b3c17a724bfc85da5f758cc90214306eef2788f197abd0bdf5373db3d0ef738b0c7dd1dfcf7fbfd7ebfdf8fbb49eff107c37ced611856c3f1307791de232ca5e73568e0ae8ebddae04939c173793c1e8fc7813ceee6788f3c969771791928dbcbc8a461bd4c14e5cb7017c77b9409dbd0ab4ddc7befcd819bbb4a23085c1abe0b9fe4188392e38432c4094df199bb54bef327c9efc5d77a0cab841c7f6a0a35d7669c88116beede788f7a0baded8944a793234fe4aed17b24fa5cdec7ebdec7b3cfd904ec7d683bdec7dda2f7e86372f6c321776db844ee924990e7c081bb35de23870a7c5760a2fc0a15f88515849657e0bc42850adccae7f798dbb66db93bf41e5b1aefb1c2998aafe1b986bb42efb1a60223c498fc0ce9616124064e792087e51ec85dd61b230e0e8e4f0ce4175610320772cedea83d3838382462de03c5b89064686bbea7526616cc9a7ccb814a9cbee55cc5b7bcf8fcd6b9c4f9747ecbdda0b473b3aaae65566fc84733cd4e065cf3c61ad511113073a33bc395f11e81654b5ec8b390bb31dea330040f8db81647aa4cec9c885db577393cf62e77596f60975f58babcd8811ba57c0741226eaa3c35c49011a61376519cb425895d368a4b06755fb8db757fae9b01b0a7d90e2a2c9858c46e9ab3ccc9225691de1d11f3ae36a957bed7e32ef23df63a7fdaf1bd9873ce9903337761bcc79cc6f81fcf3fee026d1628c58076e4704597c41bfa9bacc5effd9bbb3fef718fa53d8fc75d9ff7c89b20a624464c6eacea85037ea604993241846248aca1887dbdd6dc75e1b6788fae988abfc0f305eef6bcc70b665fc91c2248211a35620a4f9e02879f9e0277a140c1e51428cc81f91015d1e0d28a8a29b04e2884f5507841415358b6f9497723218e7b4ac7660a3ce7301b9f7358f3337759bcc7fcd482597ddbb6606ddf26f9f12d77576ccd5d5c15ef513bc168bceb7297e73dba29de2385aec78081bb28de230617be5d38fce45db8cb7ae3c62efcc27ab363170d22858ab8a752efd24c9dae0b14ca85ee5dba8ab355e717d23df11ef70a8af078d148a203499038a37dde6af199bbb83bef31b7bdeb72d7c47b74d91b0534d9001236c48887abc9b71c5efbb6e56e09a71794abf31eb59bf31e5d5c12ef91f54d9861f906bef87b2d4e68e16d2f76f5730b4fe1f6c38cabfc1044f1a3cb8c6df92386c464073822065247a8349b9688b8ceb47c224204119be17334f493a2c8b34b4bccc521800b152c80e4b002855ca2769746d5450df1d458ab1a53a2e583c5ce035456712e7e08958930cb9a6ba9eb33b96d92a911594a8ef82a33f2305d0ab7dc55ca2c0fba4a128ce09425fb9dbac10eeb60076a253f073b8f40a50475854854494fdc72e10ac2ef5bde5e798f6dfa6f1e4cbd7950e50192074b1e38fdea41f720f9ab07ca5fb9f1c9dea456d6c098c8d47c7888c4ecd87723548d0a5709e8908a2671378a7d3722e98692be9440c81271f720ecbb073b3d3e61df3d407a10d4b619cdda36e312a3d4af46a6b169f4b2fb6e2cfbd5d8f4ab318ebd49d5c4a67cdc68d1a4089438c977230e061823a474356dcc008bbb9105fb6eaccac249529634a2361477e3d77763526944daa2fa6e54322e51f9f046e503dc87b20f681fd4e8be7f70fbf583f3d70f61bff20f3dec4d88176735b2bc7640ed4c89bdbe7fc0918296ed18e3827222ee1f907cffa0fcd0b49402f2fd03f34353ed830ab55779ffc0c2c6cd26ce86ee43ce95ef1f78a236d05fa3bf721b257b93c219f932f2f161273514777db741b2a44a520c2a40c819014f5522749c493b969ca4169fab4384980bab2487059c0a3e2c9d38a06f448c1f3b05eb10a1e05b8b9050462011a485c417069c27210e0d1a364a0c89b60aac88b517b76e827b67341413b23c17123dc0222c5a9e1d5d059e0f1696f7410e263e4b48701d39b1a5a3e312e30765c604dc8af1f172520445f0084740c1e09ca0a03582c40b0bb5a4ec5d97857aeeb01b3ecdb2eb7a099f66d93d7d6011a3b2607d48ac86bba1274d7e7a38698a5dad243e5e6017a0844fb992d4f819567c565f17f6a6fbddfbcd7d205c5e15d30b06ecf2b8553f7caeabab8e5884aa8444e08222f97c4f266efcfbe91395db12b8a2225b612274a2b2907c9a8e10e1718735d4945b75f253855527750392d52a3839a782509bb431c12573a3924f2d8f8d65c2db736d0e83da729635c0f7eea75143673d1cbe2b34bc7860fd44f9c062510323c20d5663b918f156a158e351f814ab85d9fb3ec56a01e73e2b6084045c551191b11a5752d25a2954842f328c08ee0d96cce5b920a478545270befd944a0ad986c6a85bbbab6f6d7b276e3c8fc7e3f15cd7755d1eef040fdede7bc33d8fbbf078bcbdf73ec189322463543c66acb07abac09be1db7befedba9c3f171637dfdb3d31c405e8f24ef0fcfb2973e78ec74ca79e39cfa086985c569216725f644078644768c898f078eee66d14acb0d2d3246352b1c5249392c9a8bc80e0953dabc866fcfeb48aecc51d93b32a415064d5508255ad2b02a404012201c5e4ac2a56a9b48d211c215369cbb02956468b36ab3531a52d29b80d7429c3b492f88031224765a9b823a217d1273928b820f727ae8b655d2b9ed414927d03b2e1baae0bd4e47399403f26e8b9927b2af9217105540228c71832841195261b45567432709ef29206035b4d39293c3e68d0a55380b2e6cc4e88c91872e4c7930b1b4c5654d18266230780c216c4c8dbd3d2552eb2ae583e5b6d755132a27664a7efc0753dae69d1c5801146e40c09f3d2dd4aa8a2d5b65454e7e22e7689f818c950336e08163180a85668452511cb439af4d610c069bc205eeb2a1765cff269958f0ba622cbaa46238f0c2486e582f2bcfb2917d4284bf894f75aaf5cebac733066022a97f0ea27600db1aa31d856a8d45e64d7858a41c69bd28143bb9b4aca198fb965e3f7a7ccad1937d4a8a12a6e736e876ee16e85d79f36e77a86522cb519774a8e9025bbb290103c8404b1b30af2a3c324376d80b75dd7757d452d3c5eba5da65b325f1dad68a9a25bbbba61e7db5b33cf96bcdcc704787563c9eb4faf8e4eb83c8c5a6d7dbcb0dcbbfbed6b5bdec7c09ad25a97ea6797e4eb090e085deae5d5c4f15ee94232d6160356e7d2ad2e276f3a0a67715d69926271a998728567c5740e4da9b299e9d4968fb529ba8a85d26246e151a547b2c6a6842100766a42dbde030cc1b2d2d694430882a5228d892dc0d2951174003dac98348854898915850584a4aa313601e8a082fa3e8059e5545c34ed0d40ca1e4d95687bf12095e20aab21358ab201cd4222a821696f311d7a62b6d1c0414bb2b5cd631c99b29601063a22624dcba42f8cc088c28387a1a7088e291752154e458e626014449c164c0c41db1560364708dc9a0b4c59114252a8b51360bba6a494fd4baf04f9b13641421c1022716f309cbe3151bd35f9458097b6e0ca5ad2b61f4d5e988a8fb729dba7e66367cdeda5f50852dbe91325a5b99dcf7844ad5571d9bc53253b92a845e1edad83b0264b9bf224ab79efb1249c34b7ddeede7c6fbe5dd6dd0bf2f6e642fa49a5c47bbbad16169d5441def7a9b30595efdca79e40472d4bc3de72901e34c84a13120206d66c32f2b22fbb3d3615bdadf27833db27e50bf2786ddbb6edd2966fcc45438927116b504a9acf0c84486faab75500dbb6e5f1f6de2d67469fb8bb6d79265ad775735c513ab15021c7956482a18332706191057037b464a039a9018ddda52ddf984b51c8f97361b14411d48997d512cf54bc7f4c61b255f96d80786fb7553b67c28bf1e92733bafcf6536654ee95326e967579c9e7f1f8e6f1f6dedba7f43c1e07f278bca2702db6bcf3960b8bed0c8795cfeb1292ca451eaf4a8ce7f1bc793c1e0fe66b516d71a2080fba25233a629e8fe7719859e5c7f38c318f5714aa2a8eda83b9358346eba72a11af75145ae6fc713b5ae124d6dcc8854566dbdab77ccaac92e23b37b2a8ee9905a2c8969271403cc262f58d86488adb91dd13211f4a3d56a44591f291f5e5c543351203a7bc76ab199c52b076a6acbd59f1df7b6f55825bd69b0ea8259db61d4f249e4596c2a0cc505b2117a347122b49c2223a489ee09624bf541edea75c4d6fd4ace694a76c30660da6cf7b17ac4f6e515ab728efc713d34e8e95dd8ad8457ad381df7e625c57dd7b6fd5cd899ef0a9945b540bc45db5c7b27d5c2ffb6db0b304f3ee49f582aecfddee765d7763b56b51edfb81fbe8656fde207ce45b966fd51d6c817c74ebf2609503dde458d420d00508b7add41b916fdbb66db94b0bbba1bde669cba7526e60effb54ea4da997c2c7a6419df255e7b4b3c11d8ca0a65c249cf828816347958c58d51123cd408c9cdd1d93134b72a2e41ce905ae4cac0971828307900060a44677e5adaa12abdf5dbff76e793caebabc74f3bd9f7eef8ce33b770ad91acad96383e04964c4875d4ecf8dd838e15328278d6ff914cad923232843713eef532e28742f42e46c31c5087a8289482aa54c19d2d504a70ad190a30e4337a2d81571d51db98153c573e52c0cec0e8ad19d9228774628944409520434c744a51984ac45958df8e8aecba7c5cff38e280592e493b915403e6c35e4a02c7f3095435c47b19b5ceaa9de90d6755d0efb7897bbb82dafe5b9344f254a64d7a579a2227bf994aa47909c8ea329b2acaa7258e5dc68f47185088b1448a11d5b2786d434498a3aaa1eb4262b9299c62ac7d293f029d79a927ff9948b8a8b1855e5b0aaaa40994d0300a75c674edf815c3d437a5555b90dbcf2083c1656cd30d6679e55cd7b3cad816b51cd997fe61758d2aa9b1c67eec233bfb01655de81af02591e95867c4242164d8b59a16759beaaef514502b7bfc2cb578d3d553791b8e1541457c5855490e69f76f5107bde53792cef76b8c5bb2edf2e2ee72901d4777baa149c7e07272ba6381962629964068d57070bd005eef00357105af5915b36e894755f4e6257c6bb1c7ef12ed79a4f0fe802036117a0854fb9a484bdcba75c6a75df61a52a162c15f897f3f9532e361c1ff701cb2c640aa936a753bc63fc9e6b7ba821927a9e8c9c603c259ac41aa9fef0d32e1f384ac8354e2dc0d6f9ed56907b81c858f793095f51683e829838744ac430c8b28d25ee56144b8c8f6f5ba62bdfba6d8be3dbb66ddbcd35f76dedba5ce5dbddae0b96743ae662b429a333365ef06c04bd364e7ccba75d44577848768e665bddeab6758d7058b0bd4fbb8aa07c844fbb8ad858a52f157698e53d73dd2620f510421bec2cdfb2699aa6bc1996bbc1bc16f576751b8c7179cbf5cc4e8253bec6b8415d2cf60eaf661099eb344d3f588bbc4ddba0aa8254c21258b33ce53ab73cfd74af33ea4b5802ab7ce5c235a80298c997bf605f849ea6699abe2907612cfa38abb2a8eb0a875855164aba015fbadea4c675e86c119554c3d982284e81bec86ac027a447a72e6c849457fe419221974488f994eb0ad8b79f7621bf92be340541e57de53aa720e4ccae3f83f50bb0ae2a8881fa33585fe55de57966abea0cd6cfeafae64b3bb00dee1ca9ce2a1c42ab2a11a0badb353bd5959a624275915a8b6b55d5a507d503613e955a3b7bf853a935ae5cc5d7a6ae76d3d7ed0262c26d75844fb9d89aba98b480ba7a28bd844fbb94987007e3a7217cda7e9aba5ed997e3335261bceb72208b775d97c75beee3b5bc0d420b022fb877d433f97b73e0e617d61b1d6f2e5c6f72bc837d05c677837b2daa6a159cbeca53d62b6a899c2f48f2357701ea749dd95130cbfb3e8d5ac2047677ecbd8146201004902fc38cf0362e37886898d1b8977b6f231068046ee006c24023701b81c6a211e82ef7061a99c66ddcc025dfc00d2c168bc5a21108e439e79d77de1b68dc40d5b9f7565576abeaaaae7bdd7056b90ac33bef9c6155cdc0bc97c062de39e3c070de5cddaaba39acae1bdeaa0acc3b038d705633540502b30a029855d5985560b118965523ac6e10209646956735ab3b7395a902b35105aa2a5055b7aaeeadaadc68e43c1bd56d54d5adaa3bc379ab207611de19c8e16d84e10ccc70de3daaba934578e70d03a3ea86e1bd339c37108637df39c35b55b7aa6e55dd1b68ec51570e733817f9868b79ab3bc3401584b108f3a50a545755dd7bc3708695b6ba6675ab3b6f356f75c35b555538c37967200cc3bc08dcd94863545595abc0bdf38661e786b7aaeebdb931ef0c7378c3ec1368cc3c03b751cd3b6f55dddba8aaea56d5bd73de7c03372fe6a56a54b70a7355e5c00ce7adaac09d3737e67585c38c7b3fe1bdd5bd5575abeacec50cab70de5955f9de7955aa4678e79d778697d1ad6e55859730bc61a3aa6ebe8d7bef0dec59c2b95854b90aab3b3337dfeae67bc3bbb8eead6e58cda933ef0c22efbcb3bad5ad6e0f98df5372737cca31a4057095e90795a76ceaf3bdf8a9550a243aac3bb6284b317005a105c557410b9fc0d30f3c4d796a804fa338383845623019cff2bcde1859ce02b31c78818b651985cf762001424d0091542a4d9d42f5f92c4f53b5681aa7aa38ce16accad3c38177151934e4d925ac2ad18ca0db6155e7b36a98faf4795627ee6af459354ea57b96af382d8ca06073d81e1b14a2a6a864a81caf7aa873c4140d0d101000b3182410437128c9c230cb62e96c0014801060b0605a5c9c49a36910c3288831c6184408228018008c014086a6c80cc9883141f3f1b3c2731648dc77f7dbf08795b7d7a63c4c636e900f1f16655598d1e287e90b4517449281c344ab2bb87a97a85b6158024bf8883251d8811104ab7002ac64820004b7ce5067c51d7a6117554414562ee930545b6cfe03ae8a82ce51e077dc4aae222c9374df24a85eb13ab42726649b899851f7a0464a4bf0cc9d0077d79b2897ec21bfd18709a9e141ecc0ebbc1fe094f1c115690704b79004ccc2121745aeb85b958117c87d58eab0cd8a1b786113056953150557047815002b775ae5b1e7e5893c8706a96fb40cae3f236c0a44193f619f06a1c959e088f17a0a26e02e8a86a515ba37621b35a70eb027430446c46dee427350c7430c33ed8c8eea2daf8638cd997425b4aaeb95a9953814e2acf8cbbe179a81aed04d24b4ec462f87c089735f7ae0e41c3d5fb773b123f36baed464fa6671fd1b3d40e1e67ab3884912e4f60d6ef3392c34354749cdaa84480098a20a95cd32fc9eac030203036d09f06f673b7fc9e1f10134b85ee42a76a3b312a088987084b1a0ce47f520922731943e685c7156ac366958466ce167e2c47281943821e749e37ee92e6478281945a09727e9f920e1f7113da040c50bb924f18ebfae8ae275040320c3b1508b6840570c24c8543113ad2defe53906a5d26ee4e24e283d828562ac96ee8f8eb86fcf1ac9125b8f6695a3cb0f5a557f34004fef1ea3cff62800e30f0f8cd1a500020ca19ca14ae1b0ad8b48dd15775a02d912e79f489fe0e6ae9dc430be675507611023321c6aa04eae176d05c6b30cba90c76c1e42beeea55fe60da1e51d75525fce1c4eaded9fed2c6435f50f9462778dfe730052c046934e1129c99903f8cc3557ade00480620546be289a8f8e97cfe57566ac040f078a2f25165b6963095ae19a3cdbb3a773f32edbc1b090bde69a3280100c6be6e15c148ad94afe5463a2ea326502cc117419ae0087bc28f1e1e13443e6ebba50b83c4cc1ad2f30bb4cb3d35778a4bab193a45731693293af85a52bd9838b6a1936006740f4d64eace430da95e58e4c4c940bb1de00e0e99e95a338aa49a6dfa89aaca2a792c7d9686b287f61160d28bd66cd41a88de0b0961d99fad4082e111c84dfeedb1fce84b3ace61415777f59d34b20922f50f15ce2fe64be2187cc87ffcb611e77c7585770de19479c56b639da0c71f7051eca0a0ded4e78a274e7f03c156b415d38d2409bdf6ea7d8a6dd174cee06c7f2b144d972e537a8a307472aa873c8d330e2f3c15a0bd2e16de29e81623976a66daaa2a42fcb6fee3f03a070eb0175972fa6608b79359bcc77a362d5c728a0b84e14f53eedaca67c919920f4229a1e06710fe501f4c8f049b71322087fba78829e0252415a33ace0793d4296e1f1fb63580a9a3f9297f5317c5ae77a8e280f4bf85b08913f49b595080fa014e29f9e735c12b122402ddc02b01e5f82614e2981624e0a55823518e46ba4ec5c99d4c02b19b8562f98ead9fa7f65fa5528bace666744896a1411b22b63c83e6b9d7aea050f3a3bc8af9e7f2467067908a3883437616a572a0340554e4867a9410b4844a35d6a52622923d14f67f385a1f9a1db685a75eb327fc10a6f78de8db62bde5069d0c46959cbff64bd25bf8d4fe105625ddf755949416b4c94670e49ad2990deaab1309eae81a127c44fe479737fa563dab47c1f6f33c18fc1ce17f8121a0a5549cd66ab9e8cb4ecafe0a75c38f3b5431c09aaad10528dabbeca0c49245cb99c58c1d33f5ba6687d3b84bc3a11493235dc77c3a47f15389506770939a72a34141919dc5176871c0d18c4db25b7f6e6062a00f9f2a6d04bb5549a4bdd9d964d8b809c4c835b1d23e0ba7e697ea1e9ddc3d70dbed4a41df870313aacf3eb7707460332905862ef9860744fd24022394a8352889961dbdf964121b528b4e946098c819107785fa3e259604f4678b8b7d06c94d764bb6fac640cfb067d0494b29709094a9850cda226f910bae849463f9b6455758172104482588e57bfbdcdc5574d008935e1c19e891253c0d851e7121d07a0e7a2b79747d2bf078aef3926fcc4950058ad1747b06860bc1a4292dc5cceee100fe0f24506611175e24b11ddcc9801c126e5cd63b3af96a43e29f7552a120dd3f1b22f6405c8bdaca177ddd0375e69dfc9749be027d5bc3b83dd1032cee092f0b4dd7ff5a6c1452c813d73a6cf32668fd7c127eb0764b76d0ec5151c20f0ed8db20c7fccb6ee2b2c44630efd4cf02bed0720ad755440832409a4289ff258a2a429ca0133684a4fa93feefa2776aaeb5c35b3506c68557a8e02a16516a0999d8b40c6ed3513dfbfded659afdc6d01e01f9c5dc0313bebaed7636e7f26e8c1fefa4059930946e9f57287156544711df0d8b1de5f06c6134928e7653a08650fc2d0cf7a5d16d1b8461968cc6030e00ec61e2feb58110f1b90609a5a5f3b59fd22189c406631e0b3d5c8810d8e1d88e0baa3efcb2ae29ab4621d73b33e7d2b68af0281bdb98e7560d9e004d3c4fada493fa8b41a6ca4c9ec2074ccd2f6f56d2fea29509112db00b5f48661143890caac273bb6c391030fa2eeecfbb0425e9346be8d0dc43581d2a60e3435ed2b3ff30337967cc01649511e76ab6f0dfdaf208117c48cb223e04eebf594a4289731f04bc019a4f0556d6536ecf8716c39f2601c01d46510bae1a84217b30263061d0c7d34d8b1865f6b2ccc893db2223cec9fe112e25f59efb2133fee586e0e6318833ccab2d49338a904c5edd0dc0108aee918fef7f72893e0951153c194a60c067d16d6e5309f6a7cfec534c7121e7c0bf142387c01f6aa998030b6915c7119d83908416b7a886d62bff2c60d62ec8620e4913711f7b5df4ff6fe1ae33aa797490130fb1086f68bd826f62b6b5c42c68eb3712e80b8f720ea71c41f0be9950692c2f8475a8cc9ae232ea1fa9479a6d8ac38651d270561ee3dd07a21b167217aa5b14433f648ae980cec1c74a0f54b62cf127e4d38458a71d9a7c41fe6e6136f16ca3fc737c60a40ee3d88ba2df1c32a071b1b8c93412c7b0f867e4cec58c25d465c35aeaa296e8b9ad8c7ec6a59b46b7c4082e53688f33e426bcc89effa7995b4b5a2b13f6c1197c1e626119a46276e6b4d52d2769ac6646ebbdc0ae0dd77e0da31f1cb2a72a553b41abb48bfa80dae1d80e0da6ce23b899fd2ce8e35f68622e2cd74134f9dd5a0842b8e8bcb60e2a083a8b3137f2cd32bc215bbc536300c160d3ff1219b5f41a0fff15a10f95e84a1552836897d2b83cc1b2791a6f5f0ce41085ac3516c12f42a6b5c67c86e08021ed94ae3b1f30ab2b7b443be911767b2448dcb103d4a3a9b0fd9713e5e0680ed41304daab19df417c3505fcac3393b87ee1508aa1668a4658fbdee60405421150948da7a613b2b4a694ab07d8106f17f4016887c60b55ecfc172db737fd0e04467f64002f9fff5db391dd00c700156ab1c2440eff10be7e6d705c780ee90022685d4ad916165eebc0bca10847fe8a8b3b8eb2c70203b8ff9b903c1c107ccd9872d718ffa1c42696a16132349f8f10b8d180abcd43a1461f6e33c01559e73e28d193d2bb6e9a52e27f183a983061c8c49edda178ccf4b89279b2ffe426133a941ec882b7ef51757343a5f99ca92573c82cf6f49130c2a28a971b1f475993029133d8268d24a63455487debcff8ce913f82f0c7d8622640068af0e826c6df2c0867e99c12d94a19865647c55c02b4e2f6416e913fc732cebdeffd9a5d5bd8d5f246963a958e1ffbcab1e21970bab969a648dbeee36fbd2c2619819a55cec74c2b9d3fc2452f2a89a2a02281115ff1c4a4243228583998e14c2576b2b0777338743b6de5a502525ca542d80c794391ac025a22e5114eec420f00529e50556101285ab0a90a414ed9dffa206d6b3d918061361356c222ac8ca1004877b2510b0196019b696bbcb1689567d6a5d727f8c335c8a306eda3947be26909aec225f2868cc7bd5bb287122235b206355bfb36ceb9df497a615e47e7be029899aa53b0ee342b4758ffc7bef04015462d55bd6b92f5da487ffc038728bb87754de983dbef7db410fcade3bfe711aea7fb04e0fcd754f52163ad3b661ad9826a48294dab86fe7bd87e3c5405a4a752d35dbf573b48b1488348c7ec9204b04f9cade1f6d0a86ae2f398dd256901c8d8f4e98aca88cc27e74c8727d51304a41e03b692bd25ce35ba83859502515b57fe59befaadae78dc0d86a204f3544a09222be203951df65c4628667dd028e9441736c923df12c6d5f7876853afdd983d323090b04a8c5036041e43098889396f08d61da26e7ca60f702aa0bd2f4efb7d160f130f96d1874f5521dc4d3e333ee4d76410d8245f37aa61e5cd5db7654238453453e37c8d1f06258452c438d34d762ebe27c8627fd08305e6a2428aac93063259914a33d2f96ffbe409a1f2110a45732111ae9409a87f570371b28c8350ec60be3c60d60c70dc88edd51885f03391cea17cc05e38900c3f0c5cfc209b4f614585c778ac7480ecc65bc929fdc0a66a41cb19fa1a9e5e50a30e004611a24cab1d9a45a265c81b8645ff5fd054964ad3a88b88e7021de8ce47028f036226c7fa4746840e64b05ace3c07eab1f540481943e6745843456dc01de380a422ba22fa7a7e20b6ad79b8eb0a916d0a546a988c2d470181185065a6230680ca82829fa5407865a57f423df54798e2a21c4be3e965dfc49c7d76abcce0811026a03db16ef7af86b723e6b4ede5210c58735a5b07469452fbb5dc2f5f7e923b871a69e34685a2fb6cb02d584b710f56890c56a298aac4cf88870025d719b6f7cbdb4fdc31cbffba11b134efc47ee342e94a64cd9ec4ddde38b231d522a7bcbde6676cdf2aaf9847684fb128c7060c28857b98ea05dd786d478fbca51ebb53ad9080c197e1e8b4eecce7d6ac85d1f36532ae6b01c0aba7c12a2ab14ca8b7b46d5515ddb3834d6153aba00a46de1f5c32f1946550d480d7cee4ea97fe0348425e2e7b983ed3ab3ccb21b4f3b41750c8c67dfa2b9a4a92faf94d2fb1e94fdc01427a594d53bdadf2df316ea7a8d9612f89565c7a6eb65883b90d55ef4d5551ca45e4d1e1fbb3c100925894116aa321ce6f4cd3c82ddcf6e19ed4a0d8d581d450e8f943aeb93cb3a24d352949014c14380270dabda7cac3088d4f0f3420ba144a0c61aec760e7190b87411dac7d229146251f48ee70cd5c9d48aef406a89ed35061ffce6446107de317a6ce82561e76301a1c204e47841d96f5b67a39dd7d46d17148a33b819b883267c522c4154806bd6907235a0dfe0219810206e7cce74334c5db387819864e36a327529b7a88906841c10e331c2cde5ce146030561ccd43a64d7b80967119a754e8576e3f717aba444a94e2484665963c451e3ea671889057a4afdb38ef1e97475112cb3387f47240d1888ee91db5fd9cbe937460de40a1cae1ee59146bc5c1484e8f5eaf96789fe783544336fc8382b2ebd8056da0d08002be3f1e064d9c02c0f5fd63e007ecb32a6726a7ed6a6f653e23b624550c44e3ecae655c18e6312b44a9fcefb3ee0e7414641e1851178ce967f55699a681198d163a33cbe9ce6818e2c2a802070746f45ef467ffbe221491af452f7c545998ab013b38867bd29ca0b0380ebaab070ef1c2dd8565f011cdf2c9b9248b0d8076073a014d35c0cf8ae048c9d3589796bcdb20b1c36e80f5803581131ca1453c65eacdb2e088e38d4eb70f33d94e73a1df813d0f30bbaa8234dd4a690afe2e0b78d23eac7e79608fb7362e2e5fd6260a37ae52376896e639fdf02501019289c00d54317c1bcb485a925f25832bb84d4e01a34a55984e009a70da2d952743643776e875a1e07a2e19c8548a5a3e28289663110805bfadd7894556455785e757c4068f030650a0938fd79bb9c88e9f69848a0bb783c33f3c4b7f41500c0810f365b4fa491163c444e5d580e0ac18c01aa6be9e4c96193e9b62306809035e0e6a3c9996b5561f2c24e6c65bab057831bc53b35c7efcedaebe803a616c2a86c2a2b08b2936ec530ee26b984a5e8f7011391e6ba4b794715b3a6d0e42fbcd9e39b97800f79fe08ed929beb02a3a3faec97a0632c404aea51f33da8a4af922a67ec4824db0166d376fd34f2851468fb50ccec9f942c2537d20bca7bda9906e46c69227732cb61bacd34e397048d18c42567f02077760c23b4bfbd5b85fdd3987e4ce04dc0a8f60a31c0e42d52bd108d6bc1e767ffb433b3a7618d870844526a8fde2e6c03a2f59d70af19e49859e88c9b85bce9d5e8c74b90f56c1de8ac0d2d5836265dc213d99424a414b6b86ac7dcdb4655d69351049085c5fb3ab91b015ba7b876bf7656d4bad7ccf7eb2da4a4492496ac1bc4cf41b0676a03a67f179899efb3db0de9aa57ceb39675bcc03ef0b64d9fbc8696ec3b8e545a5b3ea05af74463ee0b9fc2cfd52ad156b0708e255200c8b25a6fcd9182b1d9c088afaa0b44b1d4948351afa34bc159190cb2b8f8dbe36d6db4da700c714f9b80f862301799063816a10db5d85cdbd451544c921bd73396ba4a38224f91c471cb05be84bd94264636e36018a4f81582602462852ce614bfcbcc0699b0257ef0a07f416a00676beb0b5acfb0c65c3ea2de2a4067163831058c047c59876e72f70c19b1e487a8e2850b80798783cb4db32c5d509651e1d84d26267700112f7a1a7e22661eaf97136bb78dcda67f6cf28027fc01a2cb23c9ce35da9af3467005b20741f3778e12a4e23abcf281dbecc90269061066d98dfb21721534f941075e564833b5afe6924582cb070540afe3432ccf17e9b97d15b3660dab8a795bce22fd56d5e520f6bdb3cd87efe9beab68dfd9060312ee7edb14596e590b6c984838d6c4dac37e1b811d9f60b3ce3347e8473244f2ea1029ceb2925ac6b7e429afb562c71e16de00a6f2f729b563842d7ad881261770624232744bad795ccafed99c3f74a5c39992d6a46eda90b711d662d0a3f9f2900cb58062b8711e3960d50d7d0bfc8c6cedc7a7fa6a9ae30cb710dc643ef25f9d64c88e34c78de601aa335ae3aa71a684aee6d66c57dafdce5ff28667f817e3ca35b3f4f1a96996da781deda7658b646fe7c11a168aa1df80596581cc5a5abb968f05db6cbe59105d1ba6c4b92af0b1748661784598249422be3be1e9c0bc2f808219ad3812c582a6af89527a03c16454b64c977b1af6b4117e3fe2417ec4873c86f91ece2a03c81fef9ec1c648d502351a81364d8d6ada96202bc332d95135ae9e232c5599b8cebaa5c213c0e6505a73e53d6216208c01b2b77c9993c035661af75a0a05fa065c55bd3ca21d94e04ffa87a36dba0ca809f655e9965dbd75aef7eb73ad15cdf49bf0c2414e3e4d3d904765ab96ca1781e6056cc02eb9ec88437ee4d2e40ff920e1fa0e29eca9315e0b6d3882c21841d080459e77c59220fdfee59b11a0e35022e5f291fd0795d89b806f8ce2ec6998af092663eb6a9b02c313bca896f50ef97b2013cd4b9e55b98e61e7a7d51a0882508d2e6fef6ec2d4297a11ca3c6282dc77d9e0d56f6373423440f455080c633ce8d591ecd3bd917f51938b40b95f56775ca62b6fc0a05ec88ae6e041dd993d89ab42ef36df702c08b8a83b1b7ae4cf56b0d8083367841c3278e93ae617cc87ce2d203fd4787e1d4559a10abc82ac1c67f4a57db87ac6199c698ab46006256e1dbd431d3bd82084b312ee4371516f516ae95263ca39e31712f42fec39939e1f51310315eb7fbcd5a8b35073c2308aa5c73a24b188c0b0ab4e9c4e0fd967d7f7e92e0587e7f57a2449c32769aeb1bd3718963e7e22de97eab988b620e42ad9ebea90bee0d4b0c7e92124c803c72736ab80f911ea0fba8b9760f5e0f2da7bc9bf96f3325e8436bf5ce4b5c740b1ee6768e7aafbf8cede76cbfc081d7b79869392a215cb05a090d1867aa499af2b62a8c873cc26c0631df11a8e303dafb71dc95a2be12e89cf4e6033231bd26fcd3c2af5989a5622e6eb72fc05c2a3c9e02ce84a3bd87a95fc3404c71d61b68851a06235b28e7d6b52a1500d942f99de5c650bc150e484aed468704ebebf203b1218e299fde19377768650f768b790808440fb89e81570b2ab83268ab13abfe2b4296cc46bbf3b5e8de808daf81f73b59144d5894198f1a9927d48b0cbb37ffaddb6d121f5d5658af7a2040826b46a4e99f48549e29cdef3a3c8d9d73943ee94a8a54e5813085fbedab8de4da6df4d706c5277a12792d6cb30f4bd8cc94789c20af94dacc860275eb605274dbcd51a52b2935cbb655bc4c1499d3fd4b6a2bcb976f605283cd495038b9e91502d0cbd44cac13d3b99778e274e3a1aba856105a27779f6bd3d33c88cda461d33e2b589f5b724d5f4f729c53a141e2132f1b559635650e941cc844210eeef2402fd566d1776600a2d4259c43bfe9ef02ece8d4477f1a6f81c735efea0d75a4b8435a06ff9e584f72ed9471686dbbd8a41be6fb89084832c1b4d5db9fbb95d399c7310744e3c56b9f7d2b9eb79c7c3e2fdfb839541721e93f3d0071d4fa11bc3003ab24917227fea3df52d20f7555413dab1fe73f243b593bd2d7cbb98cc9e20d3c89e2ad63183b4e6303d67010d0b2e31b0257b3a4606583f72357e2680b3b299e4f7c0498318f4979d75fc8f0bde9707e25276c201d81a52c30946e80649e7438a55996891ff8e50c5319c234317d256f5423469416fb12dca265384343e1da395ae8407e43abd221171129207cb61a2077a0aca80a889163bce691c29788b74b04d7023322cf4cc9ac56b997817b4594c86c4da5e50cce43bf66f2918cf316ad5a4f132a2f24706a82471852e6b3cf0018c78c92a767a275694e9894b652cbc6cae1af45e78dca9e4ffc8303654b3b2193c27e0ccec78883b54710ce07ac3a336560d40db4d2d1cd59a8bf4c88608e816123ba8b3434474457794256d4b4fb73d8e0741ca9924e23fcbad389367379eacc99472fe871ceebe3068f16d9705884266a460b90c3f5bf81989e1be024b4ac74a579fb11c578da53bf5c116c7f0d7274b7a15096b1503512a7c317206211023dd069a6d0eb3752e470726d221e0f7806947be13147dac2db8b023c2643fe2653f3ff0c84444e5b89f774ce6d952fb307b1c3903355ee7c9631fb61b81801879a310cb8ff08d8f8f8dba451c0f3d70bd041d664f29b0db85fd5946cc8896f1860118c7cb2c79c4ff25543dfc0cd72b0d5496bcc85afc7d00b1e537b45acc301291866a06adfa3b7f16d27eaec8031382301a62eb0a89c58d4689b83c06581d6d7814c4cfb856b303e46e8e960f8b03825a32989056b6349068b1810f520d87fe2f4e734c676412a76f4609c1ff26a16fbd496df35f18128631aa3b22e63f66bf8ad5f6134144f707d16b9b9af6c42c3fe0642d43e164d6467a5e0a064680653179fcae24cc7b079923e4a08e617622ef7ddaa8bce0deb687aeb7bd7f888bd0bb74b99788decbd198655e5a62155e70d76c2447c8f0ad98fb6ff4c03a0e603ba23148569752e988070f3adcf319dafcd691e300009b5f90587f3c0ab22ff15c9279e811dec67882fb05ad2a0b06e37aae5c043ae8322a6c843da6ef043d0b4fa8562a7d840daa8d8d534eab83162282135ec066d88944dcd97f45ea491f59b68b41dae74cb7a24f7d0862bb0eb1e071368bfb7d8b3a85126611b5d234b8e31517d69f618930386bb722347c9e954d7d7c9adfc77180fe2e42cbdd99153c8992aa9ea401c90e59c1b2930fed53453133707b21169df01ae8701e61cd626e7480986caf9e929b3b5225b31eecda966b49695d4928adc41fce96661de1ca6edeed46555e734bc8f4760ba1e4cebb1220c330bb751fe563b5fd8b57315c40d523f45d8a2887c28654858773674226c3951b240aa4071867f8202d143752c1dd9d53176b7c0186bd99efddd5bee5892a496292ae9d4b5a457521f8013423d8016d991cd34cfe52ef7e8ad526ebc6174c127c5d82427f6774b550c65472633d351499e15e5ef5eb8ace7e28c886c41314faed8852d82c82d741b57054eae39c169e2d07a5465b07601199d0353c13a6df5691ef62c1642ff7e8e164e1b73b22cc335ae34757be15d2730ed634b644a34f554761a02cf9abd3a9445b81ff76ba901e4edb90953712b06ec209f011a1d7deac6b6a0e7f01359eb544d09419fff874ffcea17d6c6cc44955793974814a956a51e5a388e785c3a28e0c2b234f5627e4abebb010ac113812e24ed709294fa4211c0c22aab6d979385f71fe23e57c8e5bddacdd8d64dfda9b1f6b9b255324b740b348de418d5d542b1e183d698e338cef86a92102a62d70590ddbda816d0104f25fa687aafc64038cc4281de2a83b855615f910522c895e3800528b9a60684b072ea917e3212a4e32023f4f16b476f73ea4369b91d978f0151d950aa01b3e30e19f54ebd390a191d782d40ccc265117afb090334506d945dcfd1d9d7e6d60ea5cf30bd4e84a39f696a4aff758d871471957bedd8266e473a92a1ec44de804b90ff82ffe43a117f59be16fb45feca829012c7d24100b4808057e75d31b7c17f11508b62ba968fd8165246542036aaa5911a8559d887bac34098947bd7a17513cda2ba5d29fe37d4ba9c70b5237e14c1b9d93d84ff1444c74662b7b85f75e04547e13585c58ab28798ab15e0582acbe9d12c7c20250d6419b22799b8bd64ba8cd185c98de90e88be1049c83291f164059c84359c75cc676247cc92207a0ed970846db2879f709c6d1296bfb795a9fd1a447bdc9ac7e864af5c72a20609b2f9cc81bf70f1f503dbc8826e3e1384ab1ba45e29ca58e18592aa94a5db1279b00e5fd8f5547feecf1ba0f7d3c4702b6ab965c0b57a97bb31e5cc4c2cdebebe7931a34f8eb8ef1b2d93425e0c9a06b356ff2d692599925b51838283caa65d6676eedfdf4f7bc04d81adfa3e22454563233d28349c82a23f7a7ff4ab98263bd0ccbdcf986591d1386a936d089e492da1230522afdb7a8074740371744195f466607c4404ee0f4816b15d3777f18f30515370fac847407bcf31308468b06fbf4fe6682f90734fbc832d3e634c8f45b12bce3e3708347f607c274a54fe13804b9e1d79c98ac271b84cc62d9a6c315ae0051d19942bb417855c2137ab6f4597edf269a55a38d909cd8b04e91b7a648abc4bbfa1de688f2c87b3c43ca197cf86decd077bc35d384128711544948fd8546ebb5509d93b0135e61e41e24a4ed1d45dcfbe8823fe7f033cc3154681c28ab4e38dd5cf4d8852b1f08ed4cbb5ade6a3fe215a3d3763a5500edddfcd9cb1545a674428902ebb32acdb5cfd9397d9ac5dbfa7669e25d46b24ee59e76abc3dc30a78e3ce9177950066f16ef570364729c9c184d0da3b3ad9fff9f97e3859d3b0bc590fffe461167977e23333c8093c649516ac688459d224d6fe1485f6bbedacd07b08db6c9c95800dfdb2cc192a9b5cc4a010070b75f09f9631517e428a4b860e85334b819bf61217c239b3533f985e05d93ba676d2d6b5038121c85406bc2724a7207d1c01ad1eab65bb01a1fd8a3de5626b939544b16b076dc9c2d097882572baa202fe83ece36fa502b7a6aecab1b6381f6f2407a287b1a856ead5e3c3fd7656b9f20c3a3cff90f7f9e3698ffd7ba7c875866cc84d04cb5921ea5abb895636c271edd1a80e041ad32bd429d2fc7d9ba42adf999e6d0073746c45493e4d2db6f363dcda5cd134c26a2de37d532620d89820b64a0da946878985fc2cb631f790215301b019864b931ff1ca493930dc0d8f1cf208d984aa072f631d119fb219b02babb5c4fc32184b01b40f4ba74d268ec238648a46e328f3167aa7e2f52304af632bb644ade3871a0f25a4ba47b65c7464a952979c9458c5b3ac4e453ddbe81b8c1aee1fb69785ec22b4ebf7e05abd790ba5f8e452447fb081046e3d36ba2b90c2b77dfb27ca80bada79cf6c65207a7af51c9e3e4573cbc7e482c7fffa524dca3ae6dbabb3c0a14b47a78c58d532f78bb63ec20f3c7701b7cf94559037acbc0a6b690d715afe4bd61bc6ab73b4d7ec0154e488e2dc6c6e745c9ad58db7eff1b07d30a9ffa410e12971df103acefd11c33f444c32c0f5598bd4f25fcf5926ed010dae1d2ea21386a5f5e6c06fcff769012fdfb35fa0411741994fad9901f1a70290f9d5e1cf7e98b0646506ca36379ef168a0def511fcd4444d2847c182ed040b7cf9b834436eaf03064b72a4a1d12558fc636aac0281e62e6191dee68647c48ba4334a542b19fc75703a1fb7a4cc460ce2801df5063e84c49bf635d769a4944fc19c72e80e2a942343e573b4378925dc9b5af6d7a7852f97e1f1b8f5bd5e9e706152aa854dce9643f487052c25e9294aa2d725dfddcfa197560acd9c94f28e80cc45b7c9f1d416acf53feb4b9fc21a29299d139c83fdd1519304f770f2092b452bab44db8a55f1a71166f9e63ceaa71a6920aa3640834c505595286a4ae18b072cd367d940e2ceda97f4cab4e8b11cfe4598199ea0277d7d7afc10e903512f4e2bc565c2e509c479e135765f0952599950c707a1d3681f58df23060ff8e4e3e0b4adac80e608de5c100c7deaaec98f4f26f040c5e64b9b90eeaea65524884fe1eda0062a9b9edc3ef06a0f898d4ab363992173f120e66eb72a216aaed42764c2b3643342b039965900be0fe64d8d3ff4efea7afdcbeae90fd2602aca746125c03f072ed5fcaa99cc2d045d33bea50a8b73ccc4159a33a164fd7804e82945126fad544139342541a0ac8a1fdce983608fb9e8448f489bea96a5941aede3e7502f10d04c144b90375d94e656058d7b3ca26f0f0ba20844ac2b3fba0decaafb15377c8282b0c8282f1fd98d344d9ebbd30f3a1a03e39645ce582ee15d5944c2f3f420352039d2c60b0f600f05f8751cb0e0e94534f406575d41edbcb9ce5d867f23330a76f7369d2b12a06457a384e41ea51ad5f33b55331b28582d7acfce4d2a21535a69b39a5e272064f2bef5535c80fc33dabc8ac66c1b14ee31209ac4f5305746c9c945e752f1f4b2d065b9a674196b3d1e1c59f1559a5030ab2d518526bdc413e74fc0573a4817119ceba8f1f13ee466ac17e79ea8905b6c494d5e41bbc845902b0155dfa0e472cefa534b5efb78293809c683899b505a9d5541a0816b1df0d403e46384f5a164e258df2f6ed3c177c7bfe20204bef668e0c887a580d01aede0276eb9edb863a503040ed41f740b912e9e21130f88788fec759964227c60ff89b2c03dfa7a15eee4f6f3368b9f6ebe8056de46651f9ec21a82aeb5e8d589e1e29af57a092bdc7a9e35dd536e3bd59424f805c969ed82374942c063c575f41c4cef68b6d0837eb758a0c921258292ecd00dcc2edd42b6c09b787822377debf06ea7c46e5508fdd1e48cb5f59d21030982e6238a1a8c86c4fe93fca417435287dfddb49bd113573cbede94d8064472603a4060d60fc213f5d62c6b62d9ada01f735faec73467d303b83819d4b660158c7b0e3217cf24c62b41e3ec61341f0acf4f2513977cf109f661a0f9f18807d60b3799312b35581cb76c022e34074905147c50f7a64eaa418f64315fdb1f7ee0d638d814e8bf0c8d2abc1efc5718a7d24a88e82a7228bc22b87765a5815f9ae9faa041f9e222eb410bcf45d33313e2c3c1113c10c36ccb3ed4355a2522ae2d69bf2414760209847ebf34d2df3639136d013a2d643ca2b6763a695f25c0e47a07dfdf44ab0a1b999a731d96a5a820910b1e121037fb15a7a6746b5142f552bddf6059546d654df794bba6da92043f4508f0ace3316d18848f72333c1457600a63179b064190b6dbf751b3081dde07d06fb61c6426510d3fb5794a104d9d109c1f84900c5316c4080162422ab40948ec960153def8d49fa53132c48f450c91ce8cdc9d379d4c76e8a2c089a0c3892a98094bc6a0efecf0f0b67b6b771e545e8abb60bcbc3251a9368e134810c84a02792e0a028510bb7f670d2074809bb14bf1582a7a7c7560d7c45d9b03bdfb5d97afde9dfb1413401b12b45db5796a775bd73ee46ba8a38f956bb5b65cefb1043909b69141b426345310093cef82dda824f5b3b75f79a2a983a3ac1c5654d333430b11e138e36cda32daf648180e58edbfdc2d4a70271a1c520b4beb51ce99d9cdeda9b22677065d0cb447ddfe5cfbe36658a04ba6aec5b05d1e2e7dfef68b01a9c2db5bd1dda8a9ddab0777779f3f7d6670f412876a5f2b06e487cf6666a98a059e8ea77c3092076e731af727994a3b1c34ff83806548756f888f2b69bef898cd257f73bf1c74f211c8c2bafe3be4d09c0fe4055f9afc82abfb75d5a14ce3d0c9c55e58e5c57c4a0df60dc331ef7f262bfe496751afe8d11ee965172626f9de1497036c756cbfe9162570a33e262214bcaa4413d0057ae4f93cb20b260bd3cd8cddfb5bdddc4059f842055c2be0e919f054b6420e6a4617ae8d25b999445a43f0c30b1d30bef52f0fb6c7a3fabec361415151f191ee485d084bdd2de99fde9510cc7d8f3b67bcebf9ea9b8a847f3bd7f41db6504ef1815dd6d379ac976f6907a646685612b3c02ca63e1704287f59241c4e397c03ed2d40f357d48b47593a1c0e5ba25d6533073b6e5a3cf061608c252b9263f49ade559e7ae1ebe0c6c5f571c01857a9ba17430a981b438078cf127105244e6aafd4347760f733f328b92006123ac86bb533a31ecc7172400c44ad6ae00a31f8590d83683c10da3da897862dea11c16152d771576f9c3ac5502817c9d8d0504b68f2d15e043b5c64b21c56d70cf381108ebbe92a7b4dde9aa7d990cd1f38a2f75d9bba989049c9cd7278883be87444e460794ed7ae778c9c960879ba1138a74456fe4ef64c31a42e78c46390da6d14aabab40bcaa7f12264c65f2757c82833c3017076b072eaaf9e811e05dd2600fb40994cf48733dad60b55aee4b08550f8d97c18138486c13bd7a897de9c687778ad0efa1d9fc98670d0941f07c119b54f39bd8e5a3d3605678f4cd320ec2cefcb771f29e2d3ef6d0ac517091b2bb42a3ea72d2e33a07a77d05f823afb2b14320a4a433816fde5c0decb8282415358d1b902e44db4f42d9ba685e233440913d3cd116a4ad6de1d36b6ba16c4a408211c6f0eec0e6ab5a788a8f9e2de83790238d0d6606d494a0c1beaaf39d7d958d80e93188672bb52a9ea12d788b706b247b990ce50e7ef3ea820cf4e8e823e6cf8330778553b87e11e4c14704006d4959b197d1bba146c50afd99c5fdb8b469abc0540b7ab9eeaeb80b05e33a7950cf8ae761edcaa7611c37b30718cbe8712e876489c02060ca8b194b6f43efd2e32e8d2af8c8b3389633e86c5fb1298c2c82956136188debd69615bfd5efdfa762fcb9246fd5ac1ece559e71085d21b12dd284e10ade4755c2a8c04cea07d2c8538a9f8c9857ff3cf80250427298859c6f981384c9f03d7e12f9475ac8d4c13f4227d2d5124bdb04a88e57e775a1710afe3453ccfd3e15b23dd42f4ae6c44bc7079b8d3e98d87fae87a4c53c6e9ad9a6b539330299499884f57ee6e0ca006b847086d1e4c75a879a8b94cdd4aff47710a4e33bc07b6a417e033dd8f828b8ff810ccaf9a9ff0e27ffdde64c5619b6ec7a7045cda5c0841d19c2aabedcf510fecd21c6e9859d7c5334af8e8cb3b54dd4a2f60aa0f373ce00d05c174d64e0e942dcafa33d5f64b9d03af12fbca9e10a5541648a8a35e9fd175f6f1e523d383eec84116bdf70544987b636cebb3e95d813e80c10b3502a2b778896284b52db90f81c7034dcc434aa4fd3ab9c6b36dfe5ca0e75e92ee8d34d306c40fa7d9efbe28ea7c0f17318033524181755759353f11e3dead102e5e7ba76ae1420cac2da1cb43582e7927ceb3f19e3437e7a3d21057dcf1ec4347c126b69041d1d472c33a09025bc811691c0c35bc82672368f8a3fabf517829eac83030fb548d10585c0755d7bddf0d13b3207ce8a61c40a2692a56ee5a32183cc4c2cb9690f8b67be80196e57009953626f751a668382d1a7df77246d7419124260fa0163b48b2c75fa8e865ea4b432d62d3b5987e11c1f39e391074630a4b9d1bed1c532a09a33fa6cb3b74f37171c4e1234c1a14bde4615c2a8c308b90b114322a15aa339bda56d7a9718e0bc6d604539c4158cf051848e8747f42b725ed83b5cfa9bbe27ff89c185c21916388a81c9459f44ab794a32c4897031342ba8a81fd96ff106ddddeeadd1c0607c13c069f944a4d0ad34e1bb262304d3a790c535ba4348455d4f350379da72e490180db939382aa95b520af2e4b3488856877dbca8847657de1eee920bbda4b82c8de3689655d04114407ee106e3779823332628a5d09cbb1e66a577e350a23dfeaa47b4b1a04c60e3bc0aced7b1bbf2722ab03a83dda99f53338887321cb3646db33f8a22fce2bb4cb828e3f666b932d70f870b89060d779b9f145349507083b86475eedfc66bc066d534cd2fba95e3dce8c42afeae8145d2fe8b74dc84a022fd1748dc1385a023c7d82c190c6559265c8300225a49b053802aea1519d1cd901bbc1e42cb8f0d09368b90527bb9a6fc27b0b3ab23a9517eac0b35bd07aa5ad3351e582138fd1eaa9019d50794214a2cb310f2f820f2fe6438951a4acce679c8d58008a2242df031e70c35341cab74c0e3bdb4cbc56a47c1739e37b907d98f6907c0122e64579d8f5ffa9221f122dc62ef1727421c8d414c25221651a4c1edf85e257039a5dc879f9e59c50e25edf30445ce502b33da9d752a48a64fbd300067904025802b14d3eebbf348ae42fdec74ab4c5a9efc83c167f6974ac8bc8db5a869c33191e691bfd8eb93a95d3023fb4ed8b51ec89134da8f116a7a2612e47ee582431b215b6c44644fba7f199333da70dbb7653164d7acff0c3e3c492a636d86ce2db72ec88e366998940d3f1874984cc3f682599404715423c1a0bbc68a7e6bb54301bc2685612e35c349d43fe9e32154be41fd0593dc9aa3e41084c96b1e62cabc967be0035b3b701d3a3b32f5ad0c1c46a6e5859d6a9ae902a8938d30046c1336656303638bde8554cdc812e73beec070c889562e43086ca28f1eeb43bf16f13649547cd19a3b813bba1947936b7819191225932a4206d72400a6ddd7bb8155d52ebca384b4203593f8484c53dbeee8d0f7d5a64eae947c262fc8e3e56557c43259284b5ffdd37f4ecf140e242a06fa8eaded0bb40ff9fa6f11982918fb91a1e4fa2974d33382df54df16c70ba3f2ad80777cd46b786ad125ec2b612debd77651aab187acc40ca3bc55f3ad2aab307ac8af551847aed2b5263230d1689d9920a011b809f18bd2756920b44eb187152311a0617af90f76a0ee306906afe6de81b1a9b45644b606029c18a3e288e020f7a0626066e3888cf9d35c4c4ab2ab3efa0c7d1aa5a3a976233402308963a4040e4c6338e9b15f97b9457e4ea5991aefb6520cf842b44a120556568beedd3112c448c0419fe2e92d746e049e0ecc463abb667b1f57b544285f4ed43119a1ab6415e331483a32265a714a8593f8191377450bbfcf0d9a7fd02622fb9f280d73f2b3a89ed7a726ee84a84355ec5854421dc83a41051b78c5a2240a9e4ee9df2fd591dc0b247d4ddbacaee3155a8f6fe0af22973f48018b970f4801a59f9ac122b7d963b524bb0a7d7dee1c37461a14ae5879b8841785c75e11ca26620d1b0fbae0934062251eb188689c3db6b0410918c268560381f600db477ebe81e15db9695a33adee19cc9c2105b66116cbc7a3cfe12826000af191060d8c31f3b754dcf2b7dfc29c89dc0a8f452bd6a0595ffda7307a837b82880fa431b2095c04c236b64a6bb8826f94bf65e1bde558b5141e8f9a49a56a0727c480cc48a102aa122fe2c6ed3b13d09418e49c581c9693a0d114c79fbb627d7f73d9ab7d11dafbf04a44dc14ef67c2fa20d653db96917eb04b7048f2c7cb220335ae0b7b773f29f5b0b3f41e9576ea8566161b7f7a090ff8cfd15b0cbbf488a47e937fa9e0cefb7af0209a8aa06070663aeab09c66ebf85d2601ca298ca9c5da85fe7a60621656c3cd89327dc597497a96f7902059c7c561f0ea82c0f106c82c5f43a5ef100674c94e17874e1162aee59238911df29e9cd2da27723cf7482b652491c2cefb68a2839329d25282a5f261dff24d0d6f7ccafae415041d8eb2790a460017dc3d57e8e5428d621014452a6341698009d85b0b509ba5a370e0111c210c00a0dbbaca63762112451df5eb67a774513cbca03ce83f21302ad25073524af3fd9e9f75976a6395ffcdedf221fc4e38eb9816b3f321392e7b1ee886be1492ca0b04bb1e455e890e37c368145219343a3e6c177d005ea9c6f0a6d5c968ca562393c946df434b98138d8bb08b0376594fa5d2afb9739aa4870a3c400a4bda23c6d1a2391c4c021d7998faab8f5a3392f682deb0490094c8c2240b0a87ac859c3da08110262a57ae8a0b8132b7679f2903b93e10821ad2017a866cc2e72d8471f7d58da078cacbfb23d471b0e6675ef0d5815ae5fff46ce735736364c5f9a457a01dc3b4b57dc4147e5fcd0b015497ec3cfe6a820fa24d39d40569b38109e969e03c0714cbaaae3b8897b2a0037fd6a291c90ca6b67483f6c0a45a8a981a040b230c81042f0795a3b15e65c241a0d044d4fec06cc377c6e88818af5b4cd04bdbf570c7a203cd0acdd805ecaf04ae8726da8f8e4b32d73d8a14e0025985ee4e8c424c41c8d7810ff1b63a519fa2a4656432509495ba112a33188109cc03d5eb2c27cadd17a6f3e51fc6de2a1704e521bc4e26e8c70ec26481394ad472571a675ea079be2104d7db5b041398f1dba34fde6c1e23ef59c081802cd40fc93ffa1c7fd8b01065ee70a56c9ea71354127bb38aaa1cbc5af60f3792ac8e76bc70002e7a8447c489132f88846aea3547cd25a23ecb9f48e7b462e1143d022530e2558eb518aff0b2c6c5b4ab016dce6ba8478511528c42dc307016b73702bf4420c2e850d8b1575eb25bcebc5937cd965b31f6e2a7761a9721749006e40ff6447670b72400f13bea33eed5bd4865300aad7b80fde74d8e065c91579815da08a1bfdc7ab21c176f1ad1779870e8d04db51e9253ceec0148cf5a15e337fef96ee76eef6fc6b167464f3ecef893d546c77a86354cba301dda1833c7e90714ce9521cf831a25a3e713d00fa54dcde23d826494b8da51fbb9f37f8f8a686b9ba9612dc38554ebee4dd4a0b78bea197f97761e5ddc60a8859fd1966128e49000837023c7ffd9e44c9e36440e6c292d801c9bbf7e0b143e1535b4c6d485ce9033ae629686b1ce2b7941cf00e139bdf4d32079a816c4f7de894780237bc13c985d91a48f73f0c49cae33a7c01eff1df7ce931c49de2a084c7ccdfaac751cf712a44ae17924a9a3e9cd5534c96f4c1bd4052c15cece6375d653e2ad5cdf927fe5cf8649939ba59a744ec0856b0166c612b020077a539005fd6bd3990cf09a0541768f4883a76a2ffb3b0dd62299c6c2bd5d8bfe2b3b06cd48902178a3dcbdd00585c92dd08ea33ad88b2ee539b6243ea741184a579c2064a9ddbf0fba9e6df84f604a60a08478c0ccca2b795471131bff4f7d2700668ebbad38160218a28afa92bc541857239207e82ed11de182e8742727d82f885a5c7540efb847f8875ecfe74112157ae505c2003884ef9618c18ab4ea0788a20f97cfce7031e98b983f5dddc509811f5f50ac0991113dedb492f27702ce940dc0ffaf8167aa6c35981b50c0c41c8a5e6849efd152313ff18654092f61682e999d013e0f5641559b5fa82c8f9baf3478001163ac17c2b0e5ba0fed261d9ccc09683bfda54d52fefb85073f9b90de991cc37a4c70b339e1f33af71a14f80850b6db284a7f1ab15a1b33c884ee14b8fff40e1d12130f45ca9a019eb23084b74e47d7824893436fb7d2d87427b362f7bae42a7d1615b4846d03a9c5a5eebbbf58686293cd81d564613507bd099140091a932187066ff4f4aaeded7ded653af09ad6a160d1441d8337c9c45013cace1e668fa5453f5184cd672a20b0d5a1064d1c0c85271d84f4a574e161334898177c618d8688aff409e470a1d360ece4775ef23a62c3b48231d14b50abb2ca15d4b81ce7e08992012e3b99381be1b30d8289239e90cec3f8517d4e7cfe535e5c3b95bfcb53c66e84112dc236e3896552291f7d50a22deb29376568b4f372bf715e4a05264c616f58dd5c49c005b80a54c02b655465ac8015fbeab2580073c516b455101f428d7b5e6744a73293d8949ac894528a78d728f853325acab99f3d1cd213ff5fbc92ea9c574414ebae4062b3cdcf6b85c41c2264dee5a21e600a0c6d549e734aa86acf29392f7e26b69e6d45db99f90843e8d4ca6437f8fc8adfb1c78fe0a7df120ef3cacac068fef789d49993f96392302d6d2d3d1545ff81deda2d8ca66ba591f1b0f57b2bfff278339ba78922933771c2f4e23dbf799fb8c0de5c6b8c795b3eb61e82f89d5ee2fabfac2a5fbcdad367f0d616e754a9ecfc9f3e7d1ee5c967c67bc329b77c8da71acce3769783704452bd9883a8f60f20b868e36376cb217fa0b687d7f382ba5f76c5d6d65ab0f1ab97c63a4f122878dd80b13884f6ac7edd5f7f86a329a8369ee6f1e4aa1513a55880a80688488cd4b9ccef1577c8e1e4dc640f14128a3381271c8cc764ffb98d87ec9fcb986e4a5596fa16934de870751cd4c449e6814171295b8cfa8e1bf3847973a4161c1ad7e2acd02498b07c0cb774659109eadfe06f62cdb997096a2d3e0fb28d817f704492b72250103525a1b102b6fc83cf8f35a2e5c13fd4a19aba434bb4c4cc19c24526bd243d62561ba1abda115b278bec448e5cfaf12143eecd88bbd0452de4eb9faec7f61d5b7ae7d25e53e6a7855ae946bfc11904103f93d65dd4fb16b19abbc3c87d55bc5d95c9dce4e81ee7dfbc784a89c032c20feea9285ab5c5ab5d57a060449613aaa4c8c98becc86646083137170aa40c4d4f8df1802c4867e5ad97981131118816bc12e7e5088824e56a8e4e0f0e0ec01dc74c9cb256230908f5019a0425de66148512a9fa0fc14dbe98f7ad37845736f9213121f72f2781387babb24c52541e5151c8d2ee63a77ddc1e4c02c0d53b3846378ebfb0922ab09476905c9bded13f3011a62442dd59acddd61051f37b8a50eb2eac64625dc1f401b1647e91ff3a57ae2947dc69bf708b9e1258e135030008e24932c588b8817ffb111c99017f27149ea3858858a5f646aa8ae05801e70f78e0be1558a004cd105ec16a239642507a993f42c350a405c34bfc50823b137b835aa3e580cf582c14846ccb6af8f87a25fb3af89d9652ae8071d75912e00ae9a514aea98e782630a51218156b33a0ad6fac81e303b66ea9df1f90a68f4567276d3340ce99ea2d372c8775d8a390eb172c290621798a78670d8534b48a54c6ea8ec4de2bb6a05878ebbb45441328a83b023b4f1b6385a4c90e4ee0ecd32da4994a4027da953b101e1567c8aad4607a8b0f4541608eccff8e05040bf935b0e6a0fec2380a5e7db73016bdd47f95530445099d056613e94980636d29c64b04c3eccc15e2cc6f688b2d80b8e786781dafbb680877cd33065aba84d9a0947ebd6c1dd20b79fafbb9f9a776e2f9e7eb3484277d8b8f16a6c16102d7899334d9a46ab46d9e560085bc52aa094634c956c976d058f0f477f51e1583f4f6d7b40f0508321c135b439fcf0d7112c99ba1bdac3049cb6ceb5ca6397dbe9a97a145015f5d49ef6f21c5b9312204d1a2a254d69d0aa0941a4f2e03176470e6de5f010792092c6a61c2bfbeaa8e595a67b05849099ac92365619e48a60d22cc55940f731e90b790bacb1a642068ffe1cc5fd99e58cfff5a6b45a230667898e5319a02b9d6d5a25a6c9a59e8b7525b409968adaf48ee48490329767e939bf88a8581d087faa920c11ca51148221763c85ab0dda68aca2cc060cacce18419a209bf9b664dc9a561b5d63d37c402ae8039c874e571e701a820bc8180d4733cd2d5ceac132d7f1a24adb5cfc8fa1af19a594c305cff9e72ff478f875ea733500f1e1ecdfe966d368c4f259f832d02c8401f08c7451d54ae7ebcd4f5bded2b862a6e06769f41c9ae3a13f44591c4738f9cc24c682d05e71681a634141ec20be8da55c9c98d90a710a7c30e1f41b1874605823f5e8876967c2ffe5218c021e92dfde99c28450b929f690dd20a34b8de86136ecf1850778916300c9b0344f4b3347afa822348095534b42c6b88d43e6c2712df74d973c523cb2a90282481967fd3bd676d466d8d2dc5c49f669fbfa2016149f6285c55e8729c9c563359d0334983901337ae401c598b777ee1e144694452b03c6eb2064a2c71af8e7d7199fe00c5720edae2669585a9b2c5d5cd38389566d665081182d502c9b282794e48b0fd48c9fec62e9325819d92a48950c732b2620dd3b18ea6f723d3810de91644305c46667fa6ffc73d5d7a71aa77f0f130b3e426869720aa79984d655ba0fb13102130741ff20c8e7efe38743fc6c596f7efb436be37ae4f3c4a62779526ad4404eb1dbf1d456e97ce1d8f35d244ea6f0c5d6182887731d5d42a17c667ca28d81d768e6429f52a0cd66545cf2dd4947680562afa5841fc01d525d3c97938a9ba6584178595ea023199917aa2abdae7230068ff172a3e4cc94b1e0c7d104a1176494711f87cc7c7d9d8daa1ca060d656f529e56868d2cccf2c66d3f99d587dea8c2a90afb2d356514f046ac311de8d523ea5054a1c0266d9f7323bc224c737b944eac43b5c27cc0965b2613c63075d610004b195d3f5d2e73519557bbf6337d5b46530d4d95ac21c533f14fad8b6c509e301366a20b48ed3ff97bb5bc0c084cd90320dfd2b4834e3a8c19d95a7172ea7a14b4b396f820e49cbbc76220ae714c00cadba039da0d55448c2c6c5e35fde641778fd1f78898d7ab73ca2b0b574ba0a7dd254ecc53c28dcaadc4459c8dd6acf2f7b6437b10abe022b3d47cbee923695a3ac80256474502b05e481afdcce2ba6f87ac7ed054d5b6afe960a208bb5f4b1062f246aa795c1937ed71706f1ea53c4b006227d44f4b66b711c4c6417853e2ba0c13f8d2828b43cceef66fd8d28e022cbc2caa80e0ee504ff3d6525f01b245811a47c6debd6c8e5efb9e4d761514293cc217e7462d9e6e4f01171e7638cd2e1a0b1caed72c68b32099e014702efe5cd3742bcd4bcb592e7d14f23ff6e863306227b5500070bbff9870a80c36b654be6560d0fca256c74fd6d6974a6f61e0c91f9382ec11c46fc4c47d1cb28e2596be6b2031b5f19b95012896cb94bc9c04cd882ecfae4f60f13926dd2c765c3a1b8a18fa20a01fdcb7aa8391cc27235c7c2557603368e29fdce3d6155c4942a652887058576c59485562ffa36c3c7921072233d93c251d0069304444ae52397f8ec0b90218713bd1dfc11325d39501406f94150f72bbd88a5a52702cf62dd82998fbe2eb88249f7d47306f393e4db08c03683c7f6e753c19324043d55730621d981dda5673b9845371e20e3dfd94a386683b67b91ec8dd982934a9eeabc561344a0b034d8fb4b9413eb09f3ac12845855ef8474fe1637f80c8d64855b04ff2f31cd0041b860d1ecb4edb8b8878022f3a80f72bed4491df066076a0ea04ec4e95c8abaf42f569920028d5eae56af6eb77ee9feddeabbb10b29dd09bb8dfe7509e874f571573af0a97032247cdcfb2afebdc9f1df03da42eaa0baa2d86783941dd3601f1d72a93877b57402ef1495068e411774e801da8569535d60cc606e0ecab139ef6bb21f0a9e29e51fa2ff3928ff8ba77ec75d96e1d3fab98fdc75c494fc0dc5769f4bf29156b525e2a3a3a3a3d799c14767461f3a0d2f51fb93ddb195829d18f155656d17823775a71c572f19d952a64c222222245f0504050405310b6605cf16b24e763f4533765c941b0d230adb85f5c2a27c577abb17dca681de28b3f7d2d36c458f526b5268c824f25f36d916cc8766a04b44d809225096b0309348c139a5e4a85b124ad1a378450993122345e66c531706e6260966038603db01fb316dccfe101b4577c03058fe919419a589febf0c49136d70d9014f765afcba2837f4290757bf3c9ff98a4f2b9848d871f71657632956e1f99c912cac6a01c450dae6d1aed6355446354d171dc40f7def980fb87cffffff63746fd3455bc0f776f317869c81bcc0b2ecdca1e996181a07df77cdff77ff3f0658d2c417bb5ea14c73ce39f78cd8f5eca4c8c289bd10311c9eda8e3b981ffbff0fe256d2c437bb24aee0faf8a8f911b463868dc8441282c5fbefb63cefff7bfb8ab06ab151d6c259b717c01b7176295f273d5fcfdded80293cefb6eaf23fabc9b6303ca88f3b56c6d257d2c4b7dd537569b6f40180929cb1edcbcecbcf8b1049135f537569b6c4200e3942c168ba7bcda6862b5b1204863583bbd7489af8800f369226922d060748099634f145dd919db5b3572c5a01f69eaff84404e5dd00eb3e14b394d20123e3be4949ceeca6212f2a869b122a27a81848304792d2262e285500af6814e47ad5576c527242c813914b67c2522b5e99292998e6fe02bc2253928a37c22b3225c1b0a2a3dd262e4b7f121a4232b182c0c4c712932526319888ec298aa2696103a6699ae7799e27ca82c1bd5d7404234946894a494b940c5d045d14758d263e782024242a488e2e23475f35a6509d48f2818da059e4dd004b596e216941248b848a9ecd8dab293b592194802012a553c3c175bbf1881a321b47971d5db70654cb98530c76a2b336efd8d65de775f2f5ccd284458da7ac17135b26d79329ce7b22fbee3fb2a2a3bfa770b702ef4651146df4896743233c577777df3ad758829a49c2f30248cd16548ac5ba175836a8c500cd18ca81e44a0becc50ad88db70390a1fbdfdddf9b1c651074b534a3444f842a0213f14f4a42115a135c6f7ec5a227544649f8c7e7b7a5f92790e483346504cc548eaca62a59d31100f028bbccbda6a2ee0652aae1259524ebc34f8606fab0f19e0fdc8abbfb4fc8bda65aaf0987ca81e83a898104303b60482aa4607a5c605db84095a9d483e6663613afd6dd2d2cafc8f41b7afd15997eb953a7c4c13acb6b00b6bd5081889a255a814545232f5ab29ca618f45734ea79b9d73bba697943dc7aefdb97c09d63091714984ad88831c1fca090be2253891828429c9e6a506a78d472e695087a3c0101b1819061972984cca142bc759002670706e3c1d0ca020d18314343e3fbfde404a900f9d3f6f823cd8b2cfcef27c10e11542e929e7cb1706156d100a9343ad857749a11964746771d5f568e2e298ee34b8eef506cd29244839defc9c59792495583850ea15003e79aa0d4bb6d62deeed66b76b4165e09afe85493a23f88514edc6a3698a62c7d993fb1ea888c10486e766cb0780162819712e87a37593f1d404d399388600b02ad0d6a823442448191aab9369db79374bed2d9ca2b328968f21225f01e131bddc88472ac29fd93974e46e8d88890430321456086bc58e2c38c62b241591981810a570f244e5a0ca9605041318da85a8d80e0c45aa60f45ded92b32d98a7809f64547377401fd083bc0200711931a2a881c7e1eece8cd44e237da74aba513a6185ad28123478676bd59a838d524b524f850e3bbd1204a09a8a30cfd1473c32b22f87a83bb1d0dc9b2c265e8adb74da69825b5c50aa61e2124825a193cc6890bc8649211fa095c3f9e12a7b6a5825774da1a2ef5fbbc5b144d97004e09080c152f432719fc87953b6d71cd08f08a4e5b335e0a5ed169eb66c4ca05958c124c09e14487ef9d840b25dd3133b5264097da1bb6368402103e6c6d8a5abee46862322059ece76379a858b5f5e96898a657b9581327cf5439b509105cc54b9b68bd553037f94075213e5cc37de57b1219b51af21bd9a9d331564c0e5341940359293ab097d6203aaad58508bb794e821ee4f68242184bf6f3cfaf1c4913875cc7f25a0e5f3eb7c4b0b200c2a16bf16302cc09d801da19cbb2b336022f648248b820381e6693a29c0ffeeb37411277f7547a52cb24184eda81aa2e1e3b9d1723944caf5f83614dc8b87df2fec8e189ae1c958641d9c801cc27932a4109116b4577fb0b6765d4f96077e730049634d17598476e3e6552ef6ef505f7be9b36d24bd884186edcfd7f168989e1fe0afcc7ffdfd584bd5f1ea0f18a69b32369622fea0136561fa8207d48710096f48a653f02eaaacbb7274f9b6cdbc18e112e1949b6053154d244df39094b61845c6088765fb6ecee31b97689196281780a0f77365597d2530c708631e3c68d5d46d4fb6ef945361c7f240d74efbbe4d96316128f50a35b2c76337e2c9ad019506a4f0d28a127c7e22bedf1b9f0fc7642b064d3d14e85dc79d8f5c82215304a0134eda270d114306d672ccbce5ab6159d9db546436a5025ddb0856db120675cc6a89179e939e7164a088a086a97622f67d81bc80f081048f033f61d47ab64489a6883dbf91122d250b5f3fe1229ca07322f85d6f264da61fa6112ca153630623497788d7194aee67f8f1f5cef0fbbb069bad30a2e977e7c49c897887c69e74b3d5f8af2d374dc64ecffffa788a676dd2c365dece6c68dbb678d331a432b82a3a3940e4913793cc70eb53a92264af1489a585ea539e75c35e51863f7b2bd28c6f0757777de8da489383a3cb9210d3cdd079ea082e06079f9ac98bc7fe29c73365a81c39c0d60c8b21272d09973f1d2082b438210802254823833e00b2a131dc8b3920e9f65dba00a2e02a64057a1ecff1f0b3d36fbf7b14115085c699cf8740b18d34273174fb54271562ad54f24979bbc0b077717c179147737d93bde764db7bbbbb576489aa841cb5d75c925060c346979e0c46bb7e3f2a0cb4aa54b4963fcff8fbbbbbb1be3ff7fdcdddddd18ff3f366b603c9e5c3cdd636edd50b46c814bd189fc4e138f2946b635c2433157904c8b8ad4ccb4c91398fcffff213a0d43786c8f2368a73db06cadc1088f007a74f9d21e553ddc1d8adcbde543fa137b9e92a6babc8ed485e01e6b5ffd5e2aa3417e0e94708a1e84a75597eeb1c1976ab2ad95ad6354345ce9a058d18055eb8130e411d3603574c473c200464f5d312a60b0c563385599a0f963deea09a1f7ddb4425e1b3b56410c63ad964832425f04828939c31ec224d9d686f73e5ebd4978a7ffff3f02e7fd72c5255c5c5d6d60f35245c214e6d8288f7270ccb0a72168ce14f88a483339975f652bce8c953189e34bd3154be13cbaa8b0799957a4143cf5ff1fc746fe9f48373cabbb7b9962097306eedebe371b370a5e49a3caa5adbb6849ee74b9f915dd80e12e29745cd90585ade885130d86aefae9bb946b510655e5aaa6b982b8640314872faec06610a4c4870738203c5f126e7e65906f4567676dfb8a453e5dace9a3735b70c7b2ea2664a8a42689718596c56d01d758d570d520a9f152b1a763efc7122434ca0c67cffeee8ee275f7d114fdc645d24424eec5dec2da185786c586a43d8ea489f9dddd2b64df606566c7677e7c46c867889c023b3d8ac8b0da01c959b29678ff3f678c33ce164c252a3640155222ee8e0acd4a9b212f02896258d2c42bbff1956dc4c449ebee28784a9ae8efaf5674c2ab5f5cd7dd96ec31acba9458ed9c2cedec063e2a30b884a593b5865ec8291516231b0be71f47bd92a282c1c99daa9f6286dfd5a61cef7d376da05c4e08814b6fb688068ccab26d538f0dd5718b35711086cd72e63df351f199e84d4356a77b200a0f0f5eb8d8a1710536847518e1b50376fdcf4d40c2f77564f0968df1e3131b4935bdf81c2f60cc820392343128f4801b7ac8a9b1ac982b23ce2b36692033e2a4e70cc665595a7f191b3fc9b2bb075367d1941d9b466c9be7afe8ad885a61b582cb45d3ec23073f9a6c7b2b6922ea49683c01fa49a014932e3a0a2f14613fd0a6154808e0adf3fe48b62cdb7a509123533c39e2c4e0c8b86713cdedff6f88489ab8eb45650f37561fe42616340a58416abca2ba948f531933665b0f323a15d5e58364b612b0412795df77b8fa6551f0eb854189f946a41e686c37b25a8c150f18f070e0c184ce6ead01f1091dd4cc43896191d4c30cce07ede1c6d38b02967a3831acf08a46414c4848210a752d1f3299b62d108fb37577b5c8f7767777e94b4c482034c6ffffb8bb5bedc61868059fbe575ce240c61bb7bf89bf1492391f29c90ed3553d1623d956f88a4b399f292c29e49feaff251c51efbb3a69f9c47b2f14da222021b95d5afa79df257b3f9f49804b72d4a513042c0a620e6fb4309a2df57464d9042167f1ab2e6511da28c61da07eeeffffffefdeddedffd946acc753f41fc0ce3e6deaa00c2072e4252a4c48842ec3ef7a38d0a71ec2586377dce3924f27e7b114fd47adbd53f8ffff7790eaffffc6eed8c9737c1276781a064960f9d897a4b6cc4e1d3be96454872877f7fc18bbfbff7fcc31c68f3b0d0092955b0956dc89073fb1ed247c2d479b6c2ea34be5f4e1b48c30d5f4d14b3c23b08fe0ea937568cad180f11d7ce48ca2a40c873f0b58742ca8e95a1c42af2ca0e055c5dd5d5dd17bdf4d671898113193838189c1439cb28593cc546f11abac07a747a787a727d733d4a3ebe1ade0c7ed2b0ec5e395655b1d3995dc2a550209a6775e712d64785d45576ad2bdbfa2339f90cb1362a2520cf677ae8d77a0aa4dd84dcc8dcc8dcd0dee66e7e66745fb2c7af199b26d073f6050388ca92e8f532038cd588986eb7d576567593ec333b65552c2bdffff8dddffffc959e38cc8fa5bd1713edc0f07c4057142dc30a796dd5df1e3c0d477b655d2d5b09bed6c8e088cd956820abeff52536accf49e6951b6dd20e8b873cf049ecfdf2b2a1509ddbfd8c6466c464db6cdc5e4647236395c6e27f783b2fef4628c31c6d845270a2369628c8c0d6ee787430742a7638c31761de3d9ce6787a4893f2ec412b53bb7c7c2c127aac5b4600277c58e1241d151f41b3b244dfc1122da71b8d173cf15dcf77f24fdff4a405eefbbe499babbbf97477db12f1420a4206905b3a68b437ce904ad89a104a5200e0eea220cad2bdb720521b8d99db9d85d01ca315fce6611a14c662d7008de64effa9f2fefbb15bc7b8165b5c021f863710e9b55b6dd02ae0fbcf15474ad580efe9a036f6766db5a0f508ca61d2b62e4f8fad23295a63098608dd24bbddbf1a8040491f3886fa93905fd22c1ac64c476c66eaa11d390e9ee5ea65ed98bd4b04d1f615b0e5c3aff69aa858abe5573aa936d8b34863245e74064085d7b5d72c8129e6fe678ff03554ac42b450557cc23c661080ec659096b08c55bba9226bef791133e6afd3a65b9e0bab879c15a4c494ac6b3bc18014982189900186729d0b112aa6a13a82eef8b30a56b51386908553c12b0d619d52f82979275e52784307a45f151052b57ce1c844c66a5b342a2c024abc2eaa86bd530acea816da58153253c310e3a33c15175261602d35dfe25d969f525377cb5b630f7414962288f0741f15309af6152170f78275c84351c55e7c20418e4eeeeb20468f414d5fef059e05a60fbf1086518d3d8388ee5dea1f3becbba8ec358758f901c791d85ed6276323b9b1d6ec5de660c333dc2913471e767c39150cccd39e759e3fc4d93ebee6e21199226dae0767e84360811a9effad7bc98dddde5ac314d0e55052715fdfeff67de329a9dccdddd9386765e77b13bd1bcfd184f312265ca2b07078c0c7ec2bcee7b1f3724b8cfdc4c2385b3b32d11101be0451898938ee9868b0f493552509b9818566ed345e711f3fecf62d81dd324e94e0adabd5fba54674e0c3b8d57ec441af684987f86f041d6104cb64de2117aff83521f361fc3fe35efc7ca0b2caecbd235bcde774f3656e3c263e42c496947acb215f9bc8832d1f5f2e2a3c99309d69ef4ff939a70efff597bdfecd170cd0d1527f944bdef923ddba081aee50020d8949c52c9e4fb1470c35430cdacd44ec5d9b60a6321368f4d35f43df539e77cc2dcf2e1934ac510c662e7cfd7a814cdcc04f24363523595720355d0cf09416a6b928f9c189318db560dcdb4f1d69ac77103101e9ca66352d42fffff6ff895e9004a304bbaf18a2073faada82e4f0200ec934e4586af5136190d3406646fc4d41fb6f5489646f962516acea928b2549ca6ff54cb2894b0fe04c428a4c4fa07f06556d487e32c4ea85ac33087501ab8fcc13b39d051e250bf324591bbfb06a721db16f1b4365a822d598da5318e9eaa5af39134f1070c0a35d486b9e51fcd39670a27b279bba1143a778915d9cb293a9226f27c3f60bb4c09760b859c730e41ae050d24389f09285366703d66604080f12a063a14d420b5d531f70061f3fe939b996343e1bfc4ae5262c7f9ff3d646430078c7cb470d8a8b81d49b90d7bdca250a8621f3bee1149ca0ee3770d333fd145d361985061f3e9545434a2043fc66a8af1cc8b31c6f8ff1f638c31c6dd8d7b3f2d56dcb86543d465a5e8620f173c475b376ea7c0c88894145340fa5eb1f742f8fee8fbc618637335ea2281d7d1ecc5b07abb2734114a62624689d08828b51f7abb1be3ff7fdcc18d132670356e435b984f13eb2304019e966be3b3d7a3d380968f212d78dc380961fae0a8cc88b15c705df7028b6bb6440072cf23d311b04708d0d041d376c6b2ecac85d2a3c1c3f67c46ac44ee6ee3fc312b59b4750a33c886ca48cb2021048286a70db6fdf1f5117b03d542002877e414e27d042d68696955ed51b2aaa6bbcaab1abe8f66b5bd27cae9a267d9903411b7f32344b4bb20ab77babba3abf78c0f3d152469a2f082a961cd76bb00084eab4eb3a2a3958e158f55ce6ac84a67c573b3a2b2da383b5a34c3858a0a092369628ccbb0362dcebde06a8725cf72cef95c5586eeee26562f324cb12ff3c85c35b545e7e27d46d54687e8fc1c9ea2db3a885d3f382f2832a4415e8f78c98a3e04e7546accd5996d7369ce396370aeaaf7c8dbb2f2f1ffff89d84a9a28801af6b68e3e5ec005fc570180846df940caac00c916af9beebed5eb0bb6a2589eef063b34936e38614b9253c10619de055a37281c5fdbb2b82c6ddb8e9b4af4028bebba2c553c55b9aa21b10cfa7d799aa25305a98454c3597b16c6e6d5ed0ae74ae78ae72a8767de66d011d5fcffff203f922602831d8208af76b4ccf11934cc7d926c5b2369a2ed86a3630ecd96ae8cd0eca58acbf137a4aa51d96625fe7f2b1d491379dec1cac7ce6650dcbde503f9743650e7ff28831c8c35ec9196c3732846b26d164f079834a8844ab659a906208000431a0000040018088224ca5a0a3900140006200a7194a068342218c6c160281410842110c3401085300c0050108681184e6b781e28f77ae83bf3c26c4f964f6c326b70260cc4e58abbfc1c38b37b87d5071ee9dc822a4de911ff85b698201befc3219be418421763ddd1d29a4e280eed523b2148f1c0d37a72bf01f54ba9fa8daec07480f4bd49c7035bebe50604d946d25684966fe1f21c9203b08854acf2518b9020ee518746839d961da51438fb0c60f74a191931495bda1a899b26549bee3da34dc723867f735e2fe12c845a58fb2cb0789e58dc65ed14f39b3b04adf7ae0fe13321796a12e0312434ab64ce8257cc091fa340e6a503f149a31ac859f446e78c15964800687616a2eb8289bdc5ce494099d72a2dd71d1cd9ad64a36e310e8be2aa104e02392f4cd17593b468f4be6dfdcae1a073038ee85216ad7879d552f2b54d809cae5aafdfa2a355b4a468c38b183111d632d0c648073f5b19ce1c843c702c7869e89a265682e3b2789ce830541182fbb5333b76383019bbe0f86ce3cf71a4843fd2f30677768f74275a6453f858ddb82fdcc1009ca75eecbdec762db14fd626b97ff237c5aeaed50a7eb7101eabfc7f22a5161ef911df3a3aeb686d6e6bd0018db81d88a31a533f7ebca5804296a259f57de3b9b17411e67688c974a16dea3e28cd1f9b152e8656d1c32d89b9759019d086514992afb72a9395247becc136a20d493dcb8ecdd1b2d3b41036617965ce5f5ed9bc833e95645bfe8c6442da261afc1be68b2bee4ad25b6d948719e14cb9dab6de210bd2b4b470a5ec0d959566745ed0c578561c54c0f7a6f95749e14a5fcfd375e041a9ed814b1433ae234aa18eed565dbd3c58ec7866605283f251acf0bf4866d080a75f5bc8f026b7f0d0ba34e05de40cb41a8cb4cf4b1bbc4c17eb34cb5288845b49f87bc832d6fc832612da93469d13e8f794042e6f38a1a2a5350ccc30b929f068dc674b3884aaec5c748628511f99327f84c452a191f770ca8fb3701079acf670969336ae99ffb0a22fb1f94e93c7d1731b8c235e4c1940ee799ce29f0582a852a95252c0a163ef0b57fe304a55597031c54cff8082990eaef28cd1a1ffc23f791d9a567c00d9f96ac1c3ef98f27965271a09f1d7fa118d121dbf615169a85e25a9937346138f15300cc4cf8c7490240f0bebe5dd6e9538f05c36a40f7249ef22fc9bece9e8ff4b20d4b042c55f41e31bec2265bcad08412e668f3a5a4564fbd1d51dfe0848a66051167d8301cea9962a01b8c20da40514722e0807b20076f6ecedefddad2d3a1e8c42a64a590479009bc48a06df8b6e71f5ed7e4f7e608a5908ccd94f8c12399cd16700aae17107e0050aaf4ea5854341a65c64cb9e294dd5e826b71ee87806670f994cde92280c04d83bdb1dacbef1e9b4812e435ce4bd603f0a93c0c13bb33dd9e0f6651e76e18b94132c057c1176bd404ec079145100cedd99f9d1e685af9d849b1acd4b8b40d084ca1aaf8b5db1b598036d7a5373b8c988a17afc7c7a221a003b5e21c665b5d8e738bc442a6b6d0eec71cb663641f10775852099f08816bc6d75e499aa13c034920f8aa5a9cc5bceeb9a8ca942b118e91bdafe9f89f0db5ce407802be911c91ca5fc8825da2350129cb8d6fe8d817c30aa422d78feb6a5300603d7f3af6c21b2b4f2ce1b838ae40a44b93548e7eda5d3a98b33a0d4253f738fadad259223315c88e084f82bfda87311586bb3624b6f33175651862f816c32ab83d71f25415d0766b1e08d4ea394f3d5504a5170b84d6677403bbc7c0068e95b71c4c5c9028f961f747e1d1b75d14bb48e4175839516fb3ac4770babf900d17414d82bc3f73e7fbe18eafd2ec3bfa8c19e56757bbd17d9654b938e1b512fafb7a6da8d9e8eea7d04225ca7bbbd6a297c094240e6d32096f6157ac6fbcdbbb09954c2994186442c44daf1bdef27a5108783c329db6c6be4cc4c0052c6f5e4960f187e5802fe4c22e6bb067018c73631c13590d8209a80c638ce615b5029e2d470e564f713bd057f63b1afa9c1946d76a5b0c3d90164478f08790d312a205b394e115ac4ab69d4866361d800e9187da105e329f64244f649184f41213027c9992993a41906a4b9601817b83424dcbce5fb52ec55988728418792e1dea342e3adb7c77a028e88e27be4e77e2c53c28b77debb938fa6d930df6a03bff988c65d001136a71e17856bd5602cc9012b78af4a746e6895ec8d3783e5077c140a97d18d8048d72e7cf805b29908e9804158b48d32334fdd85b93e5f95e26e893200db19fa83ce3d8f174bc1678873564b736113e97c85cf05fa9299b82ce2acd5f5cc91455218b72587e7ec27f1957bbf49edfe5ac0171cc58763835a04e94d56d0ca2b981ed721706ed82d2cc20a52724e4de2d34a3409302e6bb9a96143e7c112e65561184c6f36178f42376b0aed4b9e9369d58d9182f49e663557112be74d06d0765152b96a8d2b1f3cbf95cfcf6423032c2257cce567ad90cae47032a42205b971ecffc76c243c3978d801026879d2f307a281c68c73d573d7616229150a975ab07faaa97d029831be1a7488087f86250ddee76b246f4afffae8b5d8d8c2d48b9faff936f59c7b577b10bbc0aea42e9181c6fe2e674bbe69deafbf506694f337353c04ee2b4143c3c395c319914419d24aa17f33c2d2c79cd7e89a393e3f6f20e9ea117a100fe9a9889e5478f2e2a725385149613f89e8dfdaf41805784443d33805b489324b3239cde6ae36b5672db9bea0ec21de9a414a209b08d923cb8f41765a76fdbde51af45812b9eb84ec2703b66bddf074e925142a3eddf19ed2206c815bea59087d8661829d30622f31873742698adfabcd02d336383c828f99d422972e829bc6a8276ed07a130ee96b8a6c38c786efb641e1713b0ccc886c1b422b97a82badcfdb27c0d36dea8fb7d1a5de612f7e0a0acbb151dd838f3e5869c4a7a5f6edd763abd03b1fa9af0722276eab4f570190c7e05267455d9b15f0934a0bb0c0dc6dc8e5c59a39151bc005f4ae4bc0585d99b0567ef7f621ed6bf4de138b9ede32939d47c64667099aee0ac14b7bb2f4f30dd638841218d5683ee365080d9655e7801c406109cb402571cae44fb1417c636bb0f28fbb04cd3153d237b94b2091071f388d282942cd9d8223cbf434f3782789082587a95e8e2b343711262b25c77cfd2ff639b19569c5c91ee8a25d091788d6b8bca8903d40a9aa08b1d8d922191b4b5e889491a3a2995d81ad1f082b48882dda4e46acf4dc7cf503388b8a1d81d0acdb810befa2937e505265ab58fc0b44be66034a65c730cba900d480b18410810ee309a51c9ca3096e96e963e198c64699e2ec21b1cdb65af9ed3cc4123bca50934fe02633415f861be6b1e8ba124d1be713633a1c0037385342e46d1bc727ed46fe50fafb29ef039436c9c59128068cbbd4862c0b01e4064b6803ab77939b06bd35901b679792341294b19cc5f769655d01f873bed3b0e18c1de22d0acdca081f3800b7f1f744b8ed0eee81000f5b70e53eb81870d3b14c6168a544de105cf2e2bb2c917c93634d8a9f9e20594900da2af971b0470fba2cef31ddd08022712786ecc39666712764f1e9099f883c611829ae96c8f8c06fcf2f02ed53038b94bb3eac38597900f7ee594136c7d895129013b83785fc9de3bea57bc44358df1459d618106d9f293a63cef4928732d44a01bb0172574a4cf77d09916271ed13b5d1abf667996316954af20c72764e8ef7e0288edbbd299b6df804f49ff12b632d080618962006efa144144a8f6115bbf51de4c653e7d57cb9a8df6a860bd0d65cb90b7e4afb1a9f9d5079ae51bb1a2fc815d728a0c09b5fb8435ca6d8a8ad39eb8ef044da5d6e5d1dba3b502c03e0260087b29be4af1838c4968a3f86d7daf174ca1e1a1112c0b4a044ed75e9188d8c31d7a32d1707803f3c9b9ac1f30c2485c0d481d039e9a1028fd8f026b022fec2c08e0c2cf94a3088edd25cda97f308abd4af86840a004a8e68c6c7ecad912d05319984637b96779f3ccae52b056f2ad31a1238c1636f62948662648f640423735c3069e5eb6f832c8124dbcaa58422beccbed55a969ab84bfb0a06e286d67da566bc8d5c9e60f2ad003a89a65522587dc8f1f82524f06648d40ab3586631f01cd7655df2d11e3fdb884a28f55c722e0abb25c96b79973efa484afa8c57ac158454bfc420132bf778f50b9abe2250817ead28925ab529d7a4573427c4179a3b6d7368382c917d557521fe17f86ec83d66ff5d80ee05fd43374004c3e2007e090cf605450339922fe53c3a64e32c48e4673c9628c78d840d6f1fdb24963168353219e03c063ce2e4567b1bf6868370bc1624c1d7a5d90760bfe70918441cccd6899d01793bda050dd7259a1b3473bf47b8273b165c62351fe200e52c5355518b43593e54860082ffd71f4acf556d40deec4782491464d7fbcac802c3b730b122ec7fa8ea231f8c2e9213584549f22457f18dee641f2492b0323a90aab176ea1aecd2c7823b93bc979ea40944c08046c4d45e9ce3728df408ab2d70b114dc32d926c982a373f0d266009e113dd546d0be68ec27e945fb0e312c7b24b931ca562736710a01aa84b82679d102826b6df2071a747d519459200ed6d2e49cec90aaac487da9543914f1a2ad6fd641e6efc3460fe224625028c176cdd7d867679fce30b072554f1cd4d6e8dd10bcbda63859b3c5d332ae43095acc298f4801dae19b866bc1c24dfe444d150c609041005941027ce9cb246ab6389c4536a891dd509c46132651b38273d6d9b046cc43a399c5a43abd4cd3ba7b196289986ca721ec31a889fd0c9719aa41cfb1692f51b305e5cc80c4b4e998ee4a2be0041642f7663f26122462d9605fac4dff54d3711cb6e46ea824383b9c53862ee2570dc6c0b5a03f8a82ac2b1d688fc3c0cdd29a1093894b94b387b8015fd685b02ee2dbde3d90c266320f63a5aafae49a189221ffdb9495e7ed187bc3c1c850508542abd8362938d84a11141cd91d7cc0585b910c1f931687e148ced7a639193a0538c84d6408485075c301812ef08256adf1cd829e2c402c847ec2a56f4a2a6290c7421d3ef441c7ddfad2885ec35037ce13840b654d181a653bbca5a02754354a902463188f2c46e228cb106fa02501510d729c31c1371c2d75c38f6294736455ca7d472b6a8d9bed2dcab4aba06a4546da34f1950aacd4653acf89116200e14be0a6c576637112074ed03bb4d5698e4057a66b0f26354bf4c7cc92de6c7af5413864a9604b3d8cc5c7e43167a3c97c1a77cc07bae80bd32ce36fc7a6e22ea113d99756398ffc51b1c851b87a38d3416b7ea679b51a1a95c0c43112381be1903a15da408f444736647a816196bf0f54c2806a85a48f776921b0edc095e5e0bed058c2a60fa122741b1db1cdb28ec601b6a7ccafa09a275819c3a825602f971b481b59f51585225e7ca58f3c807e0f93fdc8a2a666356f14de6b0ee53877f5054f4e713a2963ef908707c910adcf71c7f572139accce2c0cee93216b5a648ccc069a0c38543d9342ba4f8e8458422b761c920d46a43e63ea53052028b1ea6d1fea16d3284623f7b60f675c33e68540ff05360b3e2acbc8a25e7e37b1808e66de11c438c77dc04e86107a8146c01e2bae025fb6b6a2351943ab99d34cea12ac433b1280a9b8d022fe9e238387efc4629bfdc0a306ea7806b7675898d0a66c954475023f81e5b52e239e3a103ad026267c310872a5e7a734a1ff736064ce4a689a817a0dc48b60af5f4b8066ead8b3ec7c0a89e149eee863b1cca7b5ed353e178b48780e225e219a29966f0b634bfdc4e2311c4332110b256704347ae69029e19fb2301d3e5e31b56ba14d7aa28f1f9f4f354d2a9e35f94a30700d825e20fb405ffd8c860b48fe8720e1ea8c1d684988b6c5e71e5ca0b112c0b4de5b63ee9dc961dabc28c883ca5e26105faaac7f84c2753e60b83512b8fcedc012bfb441805970acbc0dc769e6ecf21ce7e495e4108f1329e35118b8694f51b0a63dcf2b5a7ca9d3c9a34967b6cd05abf2111c4932f401e03c22b22a37c7b06096024c046e239bfb12f96813a09a146fe95b81d8b48f55499c8276e83ceb63e1a095818a5c3792ab5420283c3def743f11943761c3eae09b2929dfe8a5153f26819a34b02608f0723d68acb0067e113a973ffd69e634354aec420ee0d75fb81a8019c2b4396ca1f507d53580b09100f73273747c555ce96dda7d8d135167d9fbec41784e06f6534af5168b6cd8075d6b209f7274c0167f92471cd67d873f6a97f81ec48375ccca39c614922715156eab3014da9d08d3eae1dde8450d2b7bfb5c9c7f40ec8d067f8259849cf87dd06057133d58f530ffa88c530ef03521cc2684b3fddd9c372b49ed7af821e01269c3c3e03f9769c9f9ecd94b5473d3e92b729236da4223e481172bb25cd1248ae9be174f8dac534b284ace184f4b17857d60c0513284941917ea0d488da7cd76920a010386a1d3a25b868fd463afa2b1fa8d22b0e31202f154c5fe5102f046c7f3914de57ef0fa8b6fdc43f656a6c5733ae7b87018247216e4272479aae2930a9f9ce8b405879c8220812458b27a41834b844c36b700303a69cdf98401924f7cd7917886e213877f81bc7acbb96cb1a6edb30124102ebafa51cde9f7fc1757d2026faa7e2e671c05f4a3f6b3824a495679e62445fa5560a8166ad24c830da27d27f431fb036b81acbe01b525fff80b225c5de4cc40fc6d412b4447257023be895cc253123d5df9094adc2d273cac77900623a05d9fa9925fb06ab1ce3f764f0c67daa6f7904c5ea602a0881349314d7f9abebf677da0c28fafd390c491b66978bf5ff228e32e8c74a021f309137cac943b61eade02ca946080884c3eb9a1d7a9c364a6318a3e3daccd7f88d3d608468528537db89442bf595a4894894361349264a7cb8e6ec62c64bca26f47eefda4aa7e1a9de92fddcd5345c1dff8499ffa8f023e74ee61d08def806787dca02eb06c839421e6b4136b32b2d978930102b3646cb3bfad04eda6180d7a019d5fd3bf7d05086e0e5c10cd151bd0417baf7268e6415a4adf99342689fd1e080d01898b3f2b00c1b0369c36ba7f0e2dd6839c568a41a829fdf0d0712a023c6e18d243b34a1e196024cc5b344ec53ef8aac783f4e05796cbf35652c21c719bc42d23bfc136a634753c78b519c988134ffbfa42ddead5bbaf486295a88193f47776ecb616d4acd1ac3de66ef8dad523e7be29c46379fdd072a1684f943da30ebcfede9e37a2e62fc4cd2351ce10453c9e1f948d9cf0c7682361ecae39f863e183542ab9fe554f12a9c0e56658c329d7ae69b770ccf10541502dae9327e1cd576a8b23c228fce917e466f3b0c4b627a430d4e522396945177a26469ea46ca8253a989f8068d9db508300bb112c0ba34c6064e503d501552136e9045a02faa0dc8b0312839b398ea4e0d85469ac123b8feae52c2b5a7a72cb4fa0ffa73d879c2a6ac79d8b60b9c2f76c7205709580b22ce267e54955e76034c871e0c71877c6a03dcf2a2507489ac1049c73b66abeab620d8a04333ebcc6377b6ba6fb394caa5871210903a042fe8a77dc3fd7cd6f52cf0ee6637839c9b675bdfeda4fea3bd2c3b75ada1a29d35d99d9387ff5e3f8d08307653bdc72c7d2624f1b4da3dd7b4199694dfa28624406d4517df1a1142c2600f4228426a060d078843ad5036fac8cf1b667f6e6e74c8058e8cb8ade69f05c27352f4e1eca87fbfc86d425fed006e282176baf69fc2e6344c0e00be8b1ca9ad78b755b7b434d4ad08a9307cfd5478e5bb5c6d64356233c260fd5e43ef7e1f3939b861ff0653e2aca156c01c842d2aa2bc7d5254c429e018cbb791693dce0f1e33abffc5d0e499052ac6217788390b5d261bd364b7420faad7c221c55b3958ee89e3b9eaa35c3ef2463067ef5b2795850cfae34b0b3f96f3516814f6c8a7d1824cc45284cf6f4b3442658a52c111265ce5b1545c2cc577a579ef070b5742b0b1ccde38f34b03c8394e70088e6ef63ea339269f0707c327a0bc8ca86e4ee4110965902e9e3dee7f3215691129b134991fc11dc3f0c1515b0d8dd5b5396ae38b2fcb65266dca63330b216a513f0fe57d712dcf9b2a38afd6a1ff7990898b2f6b053e75eb5f85641a0444e3e9ce580f7d28a825d6eb10d30cb0b05dcfb1017e9e9d33d8bb3f5358ca3043e8d08d969202d2047370a5f81bf8faa2e3d058324979fbac83b6431817852069c904057795513fbe9e9f8d04e43be3d2b1c6639939f2b249fabd3159a416c402c9963930136805c9cd7dcdd7eae9e516037ddc0f9aa0789759811f8e20dd98429be5e4c8d6b8f1b9961c305f4ec56676bf26cb1a1b495e908f1b2690c000b05df3d49abaa63d89b6d89a9d5236915d782fbb946ed6c5c7a9266b3d7052f4d7423387c3b1aed1ec2d9a0c96445fac033095f04c671dba7c3171e80aa8b00e79f4c4a72845c6d6618cd19b7458253d41cc8c74c18658e470e4da9ba3a712c73141f380d96a47db354d2d1a90c278a8271cc9b91aaea68fb8ffd5d7b52ad78cc757832e8b013ae9eb862ed82bbc4f73ec51dee2be9b7645665b79bb21996db7e9d0520bcbaf4e5f2714190aa44d49d24da6dda400a05405cdf246c2381364d4335536467f50d6a6a77c339bd741acf346c1a8f2a4d6d2e4865759332f945aa8674bddf27b2d19f990caae0878ddaa119cd565cfd1ee4825dae0e545cf041268562a9fe4f9c6e7e5a3977e2315bba6e6ab02914d6e77e9a55484ce1530fbf96b9ce529a7eea2c26b8119543a346a58257313a8aa0a0ac2d71ffb6109b744a0f6593644988848425fbcb7f5fb7482c0eefd1fa4e98cfc3b6e3d6137779e246590f8ea78f29b8409c10dd8514a1f09ef12b3d4ef157b0283762596395324ae2e8a58d4be24ae3891b718c804f4b94285a5e6cef259b95acd4174bb3807bfeeac1fafa96368aad77b124a6a17f8d784454b7b5721eb97819b5551c8bd8e786d0c8aae62a2c8dbab9fb60646943b0aff934931af9dfada8a4222ffee2f82933797bb91819b9c025aee62fbe6e15cf8a4343f15a37ad766bb8832646009b9d9eb871ecc193accb0f5c6c5c1e7aa12caabeec7b6891f11e9224c3e6102d3adeddb790b013a601408aecb73b9e9b992267287ae43e31c54f3436afd524a77658a152bbb6449ef2ecf372a19da342372fe756ce81dac61dbb7689eb0d29cbef18743f2867dac685e54b2d406a63029750ecf453c1821f0198b331535517cf190ecb36a074f197b2b87dff18fd14de373864bd008ae586e54f40728a8213959c94587b06bcd0fcbdd20571cb2d77e094b61aed6cc9a32af0243b901509ff58cabdb8081a7ff09164dca59847474f4513e96954ca90fbe5709442f264afd72dab852e41895581859f26853d9a42d3e477c2b97bc60d14407d7b1a69b1d33c3d297caa193c8a5ecf5c9c536a125e4a43abf1cbeb92bd8818ee9658ebe90517d7cd31c8349d8050989832d280194365405d350eb38aeac9d2bac6015c56fc3fd1b3fe9bd66cc413cfced72adb252d30cc87b05342e80d37e0bf821733f7349a3f0d8b6a19fc3d8bf2decf941e8d0ecf3fa32a297c3dbda838a9190e5f5ccae3f4ed7f0ec8cf64317037fceb65d6f94d67780d535bfe44248ab3adedf7863467e3c94a5e2afc9642611eb7506ceaeb315a764f614870120b9f37ebe9c995d16f4097f9bc3adf8020e9e0665ea7fedb88e012bca53c9bf0e89e73aff3e8554fb160a8e4dfc7baabbb23f4a4e4b4d024eca560846c45248500d8470f58f2a550c3340a7334869ebca2d34e5d76b9f9c62d6e1a11707ed218157f3b0a92440f2e25276cdeb1cec6662289a7979c0781a3547e994acf83864782633cb22c7ec4793a980c6155dfcfe004073ec52e9eab9c994eaf17ac4a6a21408b62278444beffa832319cda127b7c7391276042fbf61a00b8cbaf3add3473b47d3345a8695683ec75a47c09c18762def3a711a8cfedbe8d27d68d61a091d635d52199752fe0be7314b4ab847eae231aee3108622ada6a2343b3867ba3aea269b8623648a87f5e6d647ed2de9a78829bf093355508c6620c6bc43629660895c1044fad906e081945338914a51a85ace099f07c29d879ace27f860acd525fc14a4198e3e6738bf8446981a2bc35cbe5d0b7c03e744c61670e86946ce7cddefc3d718ad93374bdbf00192deadd24bbcb2568d3d317894b32c601298c37d52f20a06abb4f5afbd16a4ea43ec51833fc6fac75c42f6722252d3ff07de3decf0034acf46072bc04d1eb5ad1d00a09e541b815126a69ed24e9c4c1a2cffecb63e136dd5e51051bc9a231a470d4433e955535d27a5aaaa0ce7a1a573e5a751e8afe612277df0d12106b12043f1558ac82635afe313900cad0a518e94de60e0a1b00c3dbdae91294ce80a34d28e9bb82075b88fb69ccf027738169b2d36d240f61560c31fb8eaa834633cc5a734254c32a4be5bea18e994150513a36d0ead29f2ccb23d40e709a67d2144a3d4815a9933458f38acdde2b495edc30d4014214b197a1673ad4e8d422a1f564ee3ded330f48204b090a2b7c48222abd7c80559e707a1e81bb018859d3468d443527b78c13534e018ac432db19134e59071d6e8cc8c4c536c4c8b0d56519cb89e339c51582833a9ab3ba5ddf419c0347b8c16f9716f8fa2ab487f12374e0b4472c2450014f269721626d463695a87782cc1792ca5c78ae5dd83e98b0be4b62ada97589557175ea2e30bc7003dd3173606f8b75cb465911d6e699d7b55d642ba6ddabaccfd674ad959a9db7cbb960339c115fe847db33f60a14bcaa69e2c37d2d96a565855f0bbbea352f01d81c684191ec3ca0aee036eb6c6d771733b1a6a79f829a8b5780aac02d220a706c9882ca11e29e0315a34ccaed1a42c3234ca4f95efe020c463eca07801f6b28e940473930db09bfbdac6f0b328f39830fdc3374d9949fb36fdf2863eb6b2842388d46459ba48f0c0f9fadd58a9cc9cdf5d89db224cb2ebfd52dea376c698e91418e86ea2cdc5529f4b9965ebad68b5c7a203c337e1a4687e5e9c1cd37ef86a7bbf3178c29ea552c07202366df44a9cdde9d8d9838454af65d54d79a7923c194171c4bd2421db5c6a1a9c44afff14036202ae0fe80e0b23fbb6598968fa73df80b3e4e321f43894c8f0c83518ef12815d2a6d0520a96df70b63de3e94d800f21d760f8ff04307de1cea57d0ec75ee728a5d091ffd47b3fc25691750240e86371898a7ec95cc5e2d49912e3c70b71d52e22bcae6e09b3eeba778806fbc4702bf34a0df93438ffbc341db691e496d00eedf45697590ce1837515fb7341bd158c63da952a69eb14dc11e82d69288b9262490948112474ab7c9a4c202646094b4b8db0f63fa04881d0043d7145dba5f42af208f657ba05e6394869c97a424b69f76aa44c535980730a25cfc2a7fa85ba9255892fac20f3c263db0b867621cbe1dfa06ccb3ed345319a68922b41ff95f17729641da615717ca2bedbc5162a3dba1b24907fd1b84077dff801b46d5b3cdc4f623078ed9be3b718eef7e293c85bd9098c1addacf3296afedd93c93a228a0d05658545b8c1a8b001295f5a69201f82f3027f24481502a84b22c1c901649222d95829e168d60a073c95d41c12400b192b4d3d8f00a24ed2ad1d9c4ba269fab602991f64c34336984c922b190e5e9dfd0425c8c5cc91a8fbe401a995546197a420a1a871a816899b96840f439170bbff96043cb23b0b7822f9d0a2de7eacdd1f362ac831c5ac9f3078e150443a8d0f391e514ac7c910605073cd775f08ea342b1f33b87fc0a629512cd0f8f93bd014e8044b6f5b803667a4df29d94b01bc16768605f68d4afdab28d1f0cd8d2db579fd4ebe5151f79e413d0bfb452e848b12a6513704056da7d1b6cf74937cabd2d68136b47a5d7e58ae4cd8fb271abb0d200f7b2325992b31045efdb5ba3d3561979d3c2c2aaa7e802edd8762681f61837d725473b7d9b366b496debe2d629744a1674f2534c662f2ef459bc65abbea0adb641b52bfe6bd4a2c6d84c177f94a60352d3f857511d36a671962c2bda3aa097efc3fbae89e4e233f6f28fd89cc84420b4b3c48c4ae3688830b0e1c94c2864150d367f350126b4cbd5d00be4d3136ef87ef1d22a9d92e1b12f8d52ee1169af6fc2680921d310f97c7912419ad5e52908932dc979b83cfa5118ebb35249c5dcb2ba086d1a190836792916621e39bc39fcffdf142ecd81c459bd31e7743c2c8b1f4d8efab9d3063a1eea3b8cc1da8468362b9250092df2a9e70053de37717daac1dd619d4e664dac216a6773f4532571564ddc2a553c1606e68154df8d4a9ff2a90e62ae8b96ea5ecbd6986f1a7cf005f76ef030b79a40ede243d3f075c158bb87b4f513c0b9bf0f1dc5565a925142bdaf1c5f2169dac128ddd8cb7d56d85d4b0e97af3946cc9842382d9ea283ad94995b864823e0cff735c0dceaba44749c6b355578620e5b78aa41783b26a758f3563d5bf4eeabdab3588d41474f21083549b83832fbe8c933cc58138450ff355454fde8dd9d6b775b8ade2370650b4986568321686dc579a9b9e91794b7fe968ca75cf1181efe33246637960c209dc9a858b16c1b74593e24f74177b410f6ca7287c95cd505a6d27df18dc707735cb30062a3164b2c906fe7dd68af0b290ab0ddbb3fd67887783c9658cc2020345030e7c7557cb25bf6b0679c406c1c6c73dad7ac4198da77d3511c019a6c9b0e7dd470e68c1939f334ef8ff54089f82eb24d99b2c4b432a2af1da35b16c04e1cf61bb530bb15c77445085c22338dd68b6bedb70e51384e4999b6769f46db00a4aa4794e87796f84104226848afa814e520f7bc9e113369369adde122b8887b13bb1b42818d2e764bc759f28b22b1ad0cbb84163193527b0fb754e489b6ecc13907387cb5c4f0e027b0f55049dc4edc8bb3e69a0b852f845242d4fa30d06502177eae9460d94defa27622adbed476399b05adf24488ae51e0ef447b43d0dcf2ce6061d8294598ae2ffcd30664e05f30f60529825c5d8750a5517a38eca800b1a119151ef320d1feab9bd2373813a24348ac3e1d8394fedd27e3237c7dadb900863051bd0ddb471a2b78c27b45471d8a6d5ab7a62c8341b54acecfeae14bb4c8bf6b90d1acc2eccb6a4188d1f4119692d906a508c80aeadf671a99b3842f0307211bfe6d3041e304bff9f37fdf73ea5a400d008ee1a4cec772ca2732c6009e40ad077083f08c2ea9e660ce05fd70bb0756956362150305a90536f4c6a7e9a0eaae705b7f968a473017e95cf1663174d17e4bc23ee81ba37c1da531c9fa583dcef028d1468e3fad505f010ad80b3c06a0960d621b8b211a64e6c4df05876db29cc161322071ba4e29067ceacdb8c264695197465b2414ff464c8cb0515933767a485ba3f8babc68f6c59cc3742a0e5fcdd40d7153eba5b5a0d2c54fc371271111c0b4040c63658d3b5b2a94ff0c8ae33a965ea677b6a4f84b6825e916ebc3341fed29626f57dad0add60e80f92dd13790174b9e7bcfe3e80628ce1249f4cbc3228d98e88e38c522e1a7e05a0820d206e70c98d6f096cda51cc1a7040020577253b49eec1a9afb2feec0c5719c50dc8541dc9df4cc3ba019f5813006ec92db4919a89387431b4a132c531f7943877ccc2ec1a40eec64bf108ea8a1baee9e9dd4e0b3aa45adf97aa5bc1b660788cf50cef8deba331a63b804cb6b651f71c604a6f0bbb3532e7fa30f9e81b771792c44f23a189b5babb1477fd4baf30086abb9cfccc21d062e68445f881d1a242feda593fc9922764f600c2e46f345bd9e2a2a4066759e5df753d68ca6bc01ddea9513e28e75a8178660e4e30139af38670c6fa0a54830334bbb304d969265ad6a0a051cc23916a254bc6d3b0512e25f8a3228c52ede0ec43d5b0ab22a98c8c944333ef8f0d195d6afdbd33fec00a43458f7c6d3712193982d1eca0e04aeaf828111b138aa9b34114c33baaa3ba00a3ca0a47d8bec67511e71d107f2bd9c54b392232f62887c94445a2f52602730d696ff6a56fb8a073f0c188abf28ea6203f6575dfa356e9efb6e2b8c85129f29490238aaae86348b3148c6c12e7f0aad0678c6722a6eef998820582bf0d3c2483303e3f86a45776a2acf23c509634ee8038440943769af9cfab93f7a26801b429f3e63caa002f9833e756b10e5a56ef5159995328372b735f0cc925e5a0f675e5c9c0e5b965d16cb0795ff7400867b89aca69383226856437bcc88030ace64e9d440766ab826a1a6618f1b8c83adf4560f9cdfc3b198e3aa0bd7c3342fb9d50556c173462ed65dfafa593fa8ed15060cd4249b233956b805152236a8bc92c92e2242d6691e04d8d95133126ac8c3ccdc83b828923f23e2cd7c4e8d609a7f98e833720ed5a2141a00214130a3b8f5594ff02c48a014a29a93e5f7048a1710e9ae96206747f07ce3d796ee748b319fbe83ab24655f5b52feff3f7c91145908373c5e7914ad77fd14fadd642e36242e38a9ee4488ee4488ee408e1be2d46586dc80696b17b6454af13e9c581f5a67efba9891099522611912c09c203a503bc03ff69a7babb1b02995a777733bbbb731fe1f880b0c2de0c0c90764f4f2c729432425068298f19177f7fdd7a49caa7caaee19d5c8454571240f348d5def69fe697ba6c78bd5e1b5c3c65c4bac844edee4ef16a7a248f1a25161eb25ca7522404246a376d4eb9bb3b0435b2b1bb9b4c2bf81ac7929269ee331604329c8b0ac08660042e8a072a0433bca35f87bfe989a6e4f85e142847c6f2ebe414a3bfffefb0a37f6a94d2e9c8cc402b6e2c33bb7777bbeb853fbaa46a548c11b5c3c55103c958627fddf7f767f71a276606620164f9e5ceccccde5ef384e517b9a1815af0587eb9a84fde07a8a9ffff0768d4f8af0834c3e6ee3c4a0ff02c01d1b075d2ff3f3fbdfe7a4f0a33cfa8b9a3105a776a288ab806ad88a20431d51900f9810c822a8a28bfd33ea0d373ad4f38e8f572cf17651544ccbcd323c2429f1d1192d2cbce127a72df5662fdf286f6a065fde5bdab24e56bbaa86b0c5fdbf85b4593b46c73c8b8e51523d216023b1fc9df7f6ff88e61b73bda925ddfea0a192ab5ad313b531f79c5ef01aac1d38b065131331b9c9c97193c36b6e1eaee6e753aa4abb95bc78a1ddd0d0292080b9596989e5e40888a79f1998919eebd975d185674c75095648761cbf7876e54525d896ee6f9edd088f48e61d7caa0eaa644752d0a92e639539db1f36ce64fb2baa9d48b08b271a63a8d622d8f9eea65e8c7270f5e8a8e85d42adc0afde02a94cef4c52ff57751209b4a85939f41b55445547a753e94e67ebb73deb8ef6ad7c5b7ba06f57e87db9fdb6099ea7a2af0f2536bd3171eb4247ff9e911799f04d417a19d764513f40588d7d666fe6597c12ee509f88ba59f83a3fafb63544379c6915f65c60aab7cd83f4141d65fffe1980004d070783a94fcc87800033c86759f4010777db772762a2dff59b2b12e448eaece8900cbdd1b70003fe008c664b49aed43083b9febcf295232ff10b53a25e3dc915d6a0e1253359368acbffac584962ebd2526519251525d85dc58ff31e937a709dff1a707b0ec8dcdfd6b8c58c9abf8a34489f5d1bb99995be58a2153a67e74e446dca8c6a019d59f1182acbf44d5ac5b55fb76eb1947aade56d70f28b0d00f7c3e51d2abfbac7a225ba97adc76f94907455308aa78c6b1c579aaab93942c9f56904f3f92242acfa712ffc1df6942517914ed9a4cac4ea17d475af9c3139cc798e4e844d4661017cc35149a5f3d51e21f4e9564931b3be54875757233fd03ffd418f6871462a5cf9029457585f281da9146dac87972bf08d41baf9862d223c2c20f4c7c5a0552aa72e78fd38d3f44b53bf8002f3a482006194823caab26c458245ee5b44a71a64eea050908476aafc6a019dcfd669304d59b621027c2c25ccbeb3d10f4751855f7deab04c9d5fdffdf7fb959a72965f2ff653b11161221292d3181903dbd029c66e93eeb4449819cf8b0fe1269829755198e04d16652b3d6f783691936905f097a4975a5054d67bef75e36553f028aa23c8958ea7ae236c2c2fb21a43795727710b476245537c8c42d75773fc11b6161973413b82a4f54d4077db6b90bb95adbcf4150d01395f4a062092211491df23b494e1025824c4ee9ec2742ad01ea3060713ab0cb895069d60e6c8e1db0c69199d984928925134ce8c94fdefca8a4a8ae339e59cfcc67b66b367fa84c7777a7e026c2425caee5dd170a7a1d481d374b2879c24784853ba2fb7202e9d59e50e2e01316db93ef73fba7527e03e4e6d62658bedcd8d86b561129df7973d266c7641ac3b6018eb0b09fd0143d11e70b8a1f4d09f325458d460ea088bb688a05aa947bd1a71fcbbd82810783ba060173640ad83ef23360e3d30c842de2d83028851c965fdd228e8f723499ccccccfcefeefe3a4070358041a05a1c95b29a2ae8898e0666e695f39f363bd719b4b4e47377cf10a5b45afd40d36f656a56ed593679272a333333bfc8042f3373fa7f6394a8a9991f333333ffbbbb3f33ffbbbb3f474145455971fe18df9dd9f9f11113f4889a76e6ff7f6e67807eae36541b8a32882740771a2a113e2d097046a908e090ebbedbfd3201ed9bc3bef1af650b5272921273a3b1782f4044a975b00201c5b753bdc47a41437ca82f31bd9cd607bc0e1dfaa67fa23f54cef7a23b0e6e2c738f999999ffdddd9f99d9c5ece25f6a19c1e7b5eb735198b49ec3dee1e8719e8aa655a6baee76b1464957f35f3586c2d57277bbb1fc6afec931be6e8898fcefeefefcefeefecc3f3bdbdb6538d6e292bb1b3ed6c7fffff4645306fbe6b8417579bd5c72f44458e8fb013de4087ac8c53a34c77a4e66d1f5faef2112612192d212d393879e28ee7fd777cedddddd337862dddd3d75ffd9d92e2f1dc653ebccffffdc014e8c27b4251098a1a21cf1d342fd99e1eaee763500a45121a7ed349d6faa476a8c959cd2f099f36d5df4c0c3471ef1dd2e88de7baf8e985079de7befdddd98701fc71553e4509407a298918885ab36debedd4f24e810f93132c44386013ca382e667e7bd773472ef25ba2179277443dddd3d0cad28f1b9ccb81116065d3308d867f9e32327d5d5c742ae07201a92f620b4e3a1adb187a09b0551f7c99f34178ada4e2e9b734899a0e9c2cfdf333b297370de512637c2c2dbdf21a44097eeeed4c5191add9c724164a65231bcaf12edcbc7100b19c246214341cba0b20f261530b06831382351b072e184ed02928e4b0b06b41624231696da8f65442aa1c5c20a9b0537672eacd8ecacfc80b1b2de00d71f3e162aa8682b8c3f925a54d4f546a6f2e38414176dca0c06170b57528eab157689606b1493702b9165c1c561a949420da82858a54950acb406a89e40642c346c183855052c1fc244ecfa05e2c17a6d3914880818be82db0944c905c7c1ddb4276be90b7e9fe6f08b9fdf70992f76ba48a59c05b9b7a492383173ec3dadc6b069b6729459115dc8c8d445cba752215ae4d2d42ec88d0ca91016889f3a20471542c6cd006cb823825bdf2fa7842af094f9ff9fd4e3f3ef092fd5b5476d8485dc6773bf2914835e93f49123c2429e1e9f1deb83e86cee36c823afe7fb01d91f413629d652e3841ab80d6424555092486252a35aafef1f99581fc50dbc9b080b712c2fb7866d919b227b8826c2c29a6de786638772e7f7d5d1f6dcddaf8e2226d65fe278aa446570df34723b59cefcffcfeeeee4c3306f79eb5509a7491a91a9bcd4c432c7fcbbbbff119bea174b6de8cc48f034532333fb82ccccb3259c3333d720093633f3ec483bc24c66fe77777f36496bbacff8d9255f897f752581479ecd224eb608f0d44124e8ac911e8ed13cfdec908a1c4c683597a64372232c2c52a123045543fc1c819622f11bc486f62c7921c0b327aa5de9c6a0ad74fb13cf3d9fb778b3b7d331ba0c3b48cb347022082e862da48759213eea8e1d817794b211a0f9547648cdf5fbedd462cf7da29fc2ef97e1a3ba0f68acbfcebfcc02c7753a9be64f8931a684adebc1953bae62163baccb41b41be1fffffccc6283db291094586f92f959dc48753362a3cbe5d41d24104f1cef9e3b4d366b9c42b41d17d5f546ba9bce7cefbd3cb28b7f77776f63dcac0baa990eb4be176a9b5b4258343a48207aae89fc9929f8e8c00fad9ce05b5d1d08a27189d30120e09981909b4ca4d56a2a3c41968a890edd78acbfcebea9342cb51cba20a83673df150d1cd68a1cd220c7babb33ffffb3caadc091fa0a8c618b34361ccaf12b459adaadf45b2d62acbbbbbbdaac523133f3504f0799d9bf9bd9f5da2153a9fc2d1a0f505b43327aae33fffff3f91ad211542f86a0b3dd1b58de664ed19899d964fe77777fe6cb7ff95311ecee3dcb3b74e28432829fdafdff674111b9445dcfddb712c10e1e8a262911e86417d870075291f2489b93f9ee759d99ebbd5b5d81a23cbdc9cf6e3bdb396c201490fcf273ebf09cdda6bad2e488b090a7c7674784048246a971670904e5c6fab5e1bacd3abbb4ffff1f976b79bd0798f85e01ff7c570120c286c281ca81e281ea81f281dab13914ac60924db86fca7d53dbc45b9b7aaf269f8bd981696c2632a18e2a141b1116e2e4f0f4f88480b27b053cb97b37337313f1485ec022d575098f080b7bda470db164b7922fb4997facb1ded199b4222ce48560d21b4df37544422a5fe424c6fa4b2c82e283751f6d29eea6060e545565121ba888344f345166f30b34359261d7e0465858b6f9dd7f5346244cb43825aa2b0c89a0697ef747b558f419d24c49fca028ca0ceaeea66824fbb9babb4609a666691cdb34993a85e764c55ce807c76558c57aad1f402ca8333bac1f4d1a54323b2b1419521139b5060b2af3a992885ace98364dfa61b8b11116ce681021d31991d3b5c462443248e02b0202a545865701870970390c13fc8a7a232c2022288245021b4e06d92d080829b2188c0d75474e2b3fcea119d659d40368749a65d1114fb5ae619736762a1f379d8f56081f3a9ece3aea8218a163139e080b7bb7014d7c6b931f7b3bb0e16e48946e7d518845641f26189f270f9338052958eea451084cabe652842596d2ece91161a1cf8ee8617a90da254461848e6b0408ac675427421496464ba31b8ca44af97fac807547525d439089b09056bb3cadb1c6afec1c2ca2bad6a891ad94f9ff9f5333022a301cdc62b93675454015981044a0d0d44328184963f0c8211f2498af0d2e2d2f545679cae6b64a9dccbcc38db050877a73f86072fc549b1e2a3a6e364958ba984d135df0e684861fdc0d07157e6237422a3a7e342a1d6e488805baa3764324cdfd6e2680e67043417aa03b5a1d520b96062fd44f1f1254baa367889edc025a20d04c8f23298e943564514fab4186ba8a4801535ad106500040aa6b8e61b0427c20f36025840745ad6e1b37b681538b6e455642e062076043dd90115ff60cc68ac5ff8f9deeeeeeddccccedeebef28d62955abb3bb7d23c9b40d5dd0de324d8efdf5d028deea7078c22a0208d51546c4575b2aaed1978343a12c532b6b8bb09381116e6604c6831ac229215aac812f61311cc13d2051589ce8a85a0af0549aeb5d2646341e2635d27d05a40a224a5c212df4aeca5d204b8da2ca49c20ab7063e502ca5486d216539b922408eb049c2b09d36ac5146c2579c2a23af1584b2a15d4ce0a88932b65f25377f446a828944ff5abb5cea636fdcd402f10fde15e9f427bfe7a2e6752bb3fa6b581be2feaadf08eb7983028b44d2d6198e4f8b6226182365347ca1836946c8485490bc034b91949a0090718a7d8050bc02ca111800d77418270c0a5c29c00d850076484fa6ab5d5ee2e80ac458096009edee7b5680045ae016789bc0e5ac401a86b6d56b319804a923ca30da400c2ce098ab03026a309e0c5730a408a6000ce1e322b2c6c133e98933d2cb8e092a2e662f6b8c1818119033d7c300eea8a0190c3110917005c0080a31a1a36d0e4361c1db92c1003b8c0510a0c28506353c007061934b4a069f091bb109514a8044b773aa7a0000ad31a00000500140672204921940600148004170e5d909864301c9905c260281c1404c38030008a811808611088e14094429260c21b939a9f7a45745e0b3ac776058cf4a9017a4b1b144e54cb51710137e2049832a18f883cd7bb3a57a4c31688e2c0294ba1054363ff278af4661270d4825ea297684f0bb115e76951ccc4893e40fb273d903ab659634c9d4311572c7c6bb5dd8abe051308c6d7e1017f79040e762e4edafb5a3a3d4b97538948414f5607408970815621d5e7ce1aa47938adb88cdba23b0eef03896c75b21d2b093b58fc5413ed1cf2b328bb80683d27a21fa2827c34225e588bf8af44a78c944c440e8e0f5f10f09c70a9c0368448ff0209634b089732311df760a0a1ba3d1c464c5773b4e90ee2be91b56e2204c9624f28c07e39c0c7ede765c305df9d050eaed6bfcbbda813f3bae5d90737789a9e21c05e238d48f80e86de6c00f16ca81f1f401606c83c4bb38375b5f1c80945b0371e9c5ef463589891291e0071e3612a9e7f5ccc00d6ec3e074329a827333ecd3eace2173218771120c5ec07fca67fc5eb4eb926cdbd1e1a6d667afe7341a3320080303dce0871885555380d0367ba4d30996f56a68f3d70bc5d16b1195f1889a2c97b1f15e9151f88a4977bb812d08a6ac0014ae03633566cee218c105339ddc1cbefbe41055f62a2206d7ab0b609aab34d1a818384a25ba49442d1dff65f568d6680318b39c42606af721c9d2b5ca6ef3c9d2cfe12662dded51a9368c730caeeef0659d6486617622899b99897d428842aaefbbe56aa5911adfe346bfc76038208ecb8aa430def350d4fa0c6739a7aa4db4ead0835e9ba5d7d8e1adcdadab78045d2d237a4a12e0cf8e606b42e88a0171e20462a8b822a8044953271ed9f41dc03944e3c306e96631adb69df0b2e1763a3da78d80572eff7e639486c42373d1e41df67c4d501392a30c748fa255690171c2dc68309f415345daa693de3e8d5e1ae6fbf747cc512fb0a9fec0703c9ae9853e0bafc21764d969f69fe085345827cdda9a498b9086900e37d2ea59c28d2bb97e2df890ff47a9b4e503a143537bc39fc25f901063811029badaec4fc51e3441309e6e578d11c9acf8e5b04790c7f0e598498df4b5acbf129ececfe67238955c35bee88de825e938c45a8e6a332ae27cda6f8afa714dd36f69c802c028e1a431ede5b87f9e86d4125960df13992b949a2d53a771608441d8a872a8ebb2bdc6a9e7cb21db7a6491282c4012baaee68e22edcb07d95c76227da57f8c619d25119f0f516c27fb3b92a57f4a1a5152d8403595f2e6bddfd348a45f0479ad89d45856af1db446dd51aa5848beb9ddf3c27e58dac577b0c0b2cf9cd18b8fb84e81cf299b1925aaf1f3492efc43e3bdcf648da4213d9acd7632b2192dda2f3a9c5b389ba93e47213f7c7c4b88f92a0b907f092169d6fc0ffd6fc3830e14baf69c9c8f8eabd70569bd2a08298a5e58068ac44cb28280d4821ca6fa372832141bfd3843737ec6cc291cd22f821efe08a6d18acd44c462bdad4f6d5c48eae1da4a2698d2bb48690b2a60b5af56ceee6d03d86388cdc41aea363203a5d8c7d490021db70f30b1174d82d255314cc7301b27d65c4210041ef6c9e6b41135566c4a33bb28e19071ebb80d17b9a5a8b2cd3bf8e9b1e81dcd6af64e6a24216dbac8b5018747d7388b11f7bbb1883bc8633609436a8c69f66263f622ab353d7d8db2e98410f3f9f29266ba41047578a4213f6ee1300eecb2c0c0b8ba50fc1e530e727202ece94822b21658ad55877473cba56202e52d8230121ca307116ec954d9e28723dcbe530aaa6072d8a0760db93425183a328ece023408e14e926f41284e558d958649a6460c3988065081699ee41d32f43bc9524216baa757370d5265ce3106e0a0b6b2d95cb3eaa9bdc188db4ae0bdc2847fa02040e3516fad3d3fa147bba05454761d80de1cc404797c83eec0b07dadcf764b5d830a38a79e5d5a0c48fc3f0afd58a43c2dca5bff582274e49cf78dc23b216141d3f95a5af5ec5f4f2e0dca1ea8302ceae1d1c576286be8dbb22ec5717d073d1315f60a9a3c2b383f6ba28f76ae8ad50becbbc3c8abba8af4db94476100cfacb0baeaad734157912fa909a15d6fed66550934e892f4bad260f122af2d7af93b2f77ac249b51a4ec46f021592342f567c56cb1a9daf20911eef6aa112d1232ccb24e1ab38798bbeb02793e89f408ade7ea1711093bdc75e53287f57c30bbe9e5e69a9f124a85bac206b5e5cab1084c3e188d030c3e58ba9a1cc19e5207718c584c9ca56f435761824321049e2fa97b351cb3daa9e4832fc9f6972e7aa60d27b34f32f3155f0c743a2e51af4edccf51ad3012f894771281f97a11789efe26ffabb000452c82268d1c42de1370346d39068ba19a94966fe9b75e5b964cdc4a72b5a4ac875b643fef088e09c9e1a695c5f023b95d1c7090bd955640cc764e23a9e8bd3b0bff56cb2908bb2781af0646a8c76cc53918fb510174483604a54a156fa32aaaef83f9d7e35b777350b49698a8bdba804705125948a9ed33afdefa83400a07a1dbb2d00296b8cfb9d0cd7585731c794b00f133504252d03d02fb98a7175da2d9c620465e723ca31af649f85839841367ea03ad615c9b3b93d0eec065581e19ee62517f5dcfc17b99da4e7c61d01ffeb368964c49e52e255e54b2b9cffa775191b6fd6615ec616f9fda4ea45498b6e14f7f7e8d61157900b4bfc44db709d2641bd593b968782133f1c030cdaab7ad724ac24406760f4d40da09856b0557cbc4a62262a3648c3c5b153dcaf16bc11f66d8aef2f44c1dba24a15a11b9ca13c5d1d868f22eb24b6658f4c23c7d59a9ebc8d793a3c90d5edaedf084071c54e438079f5f8da4c1e086da32bd059cb40e51a947fc0ccf226ff1697540803a92587f3dc3b60f3c6d4c31ec61cf9ef128813f53649844b39543f9d4c9fc63be52ef8af6d19c6f914f0e5a66cfcbc83bc3287f87244c7d879a7c545eda54e6645438047bfc485b8a6f691d126360503350c6c17889f6d0b9db3ae1376fba3ac42c7f8acb70f8b5e83e94246150813041a1d32912a1340bbd857d089b164ed08999059d920b200ad416efa4c7604ff63c4d038522dec59fc2810a3b5f8c0fbb367eb7a8a77b4843ef5eebda158b20dcc9f85e8d381d135837c900e392022233c6b309a36284d86911135acc5025d4a0fa40b8d1a9813e7be667264877c5d4805c37ed4d24589092c0d2d2287723136623d258eb938d3075d60447fc3a110f12b94f82e115497ab7101d0603e757961dc4478228e9720ef8ba5a1e7350746e2884d77223f2cf39c7f870200e2741ecde4422224f4c23d16dbe0ac030757ef9d8507b387951876279d5b66422187a495e5172a14c4a49b53fc37c74ba766e5af9bb3b4b0fa492d40062941467128c95494185bf19e8a63f1982b713249378d3be514a972397efa6e67e060bdb9ebf607f565db6041baa23ccdaa0d9c00d3225e905d631b9e4804e723cca53af0c5db2053417317e6e3a6143223d997e93f8fa8194110e1efcf7be5ac8a18d30a684c3c39d2cb8cd22f327f3cbb62799e62398cda8180b758a971295e49413f941371ab333d6d0aecc5132729302b6975818c08c0c9aac6d0608ff3da44d5dc4b591d2aa0ed1f33b150fa4bba81b2a747eed5da982664c6eb1efd810d64831b868e36f154dd630821e38e433b92f4bc18a23dd05ad93772847e020082eb2f538d5687430fbc38a0d84ba6dc92d2f8791741536b42e511b61197bc85082f60e66acf2ab285baad7570c1bba3e4722b7df2a45b10a7400873dc133d6cb5b1e58d992a65bd081f4b5ac9039d2e750ffd2510a1d91e0eac01f8bb32f9d1b2aa6a23f1df1a2402baa2904b6641abc3e30e808e012c3af34f8ba71c13b206d7bb2b4c942a7812b53fa9a8b0be00bba4c6585cdb936bb74ff9f97d3ff3b91221b6619653aa014af236929b5c501a25faa49595f07f838f8e4f923440877f53e86637ee79dd4e884f9bc8a557528387cb147bc43076919854adfb2ea36fdce6594ab353bf0fd0fa5a62f2013dfbdfb411824ee2a889bf581409fa879d5ee38f4883feeb0e6d5a465fb77e6b3f1cd3bef27213c336d4c8c9d97fef074e5fe732680d28f50d9f789241f452664f2363ae5910a6580dbb921c6a782bd67803a18c55beee2df51082e88a43bafbf07caa6f687eaaebaca52e54bfdb84797520704ff27837608e241bf48a27058d248ce1366819eb9c8b1a21c99ab0bef27d908a1d56ddc8e6353903ea511390bc72b03eabb0d6370e97c54da3bab4e13b13dd8b409813fd931ff6a8b793dfbe6e361a865e4869337693f7a653dcde6209ec17f1c8125d9665368c43f6f8311b6fba09e7e000ee7f6612430ae11842102fb274ed7f848ff99934ea133c16f43ab1b6b0f30861d303f0fb7dd0a4cc53152284e4b0879b7a7659cc6ba46fd7100f34f181cb24c4ab74c9f080465258c8935cb0b4417e23675b149696ef06bf3d9cce8bbfa99bcbab8c7a3af3466ca8c3f9882a36c2c498ac8eb708d14df6702d86a6122331a23d1a65cb1a0a0f2bcc8c4df8f86f2c132316cbf5aed878e092204dcb4239b363d62b39c4986c87444f1c1ad45c8c857ba82c060298058f597082b1555e5d6b21fc63c476a601fdc32dd8250d364c49be7a02c7b8a401e97462e6595f0038f058b4542e76b755ad19f88518c14d58e8bd5579bf03352a3d927b41dfdc72dd7ef7a6edc654649af379ba9e83cccdf2fee642cef7c1f27933081817313da2725b3b41b058b59f2b3bb26eb8f6be08dae1bde95e827ea7f597c0497f60b4cf6c37dc0b9690f3f5d55ecd18026503e945f41e9e31ccc4bbd9dbe6e2068d2ad35510f3b45f6148d42f78886845b88f9e538bc03715fbf6873477192678f55985fd6c83ed0e849a55b1765ec92a942a94a26dbe6e956f6a1ac13cd76b56adf1a86f388d2aa8d934b1043015c90887a4c6843e8fedff768bd4d8534f3a944c4e35afdde62d765b66d4f40de6424ca0bc478b7c71799d633ac343f8166da8ff85db198ac4028ec3015eb46a232ba4347f83520096ae085ac8f6b2a41790aaa067e47747849401bdf7d304faf01dcb5bc002204c646ee1db0cb624125c16d2d79125f96ba49242bd503c5076eab77fc242b7752825da8b2999d2d6fc9216ecae19c1db6fe8d1a6f9852c5011871cc56fb661fa3c68fc30b106fc0a7a23c457fc324722d1bdc012b8b91b74187ded0659f6feb9c4732a83cf07e274304786b34571ba2ec77720aaf7b8d9c381b211e541259e564466a68ae27da527b02448f14dd04f266e0ccb5e1adce1f0f9e9447e7089c365f8499292a69c2d7a791509deeb9a4018ba3042bc7f1de7153fb4bb0422fd0b16478ed861eaa1ba8b1c8bae5d735a1a9bb1d99a9129c9623ab77f800e9bd72c1a98475ee39933055ff4e879b32377e7476300684f3a953bf51756af326dc518dfb1a54936f85331acf609fa2468307a83e419eb865abc2c12c8eb1250c7b3b84bb20814fc945a2acb47eda811fa1d10cc5cfeb455e53b46a6523230bdb39bc248438df37f8a6257218b524120fcd678abb38536700356e52a4a1c73f15e92c44ac0b3329858874a17b4dbfa92b473a69ac7018546a7554a26d9406a6803f19259d7ff594c6e38f08092caca08ea22a61ce779bebc9300b52141ea98aadb661aba2c57f9927888f78220ce227cbfb0d45bd2c955906cfc8128e965af28391c26f25b6e1f49a894a3249afa310591e5e03f2b382063e7dc82050f32e6a37c51efc9c641d04b89676ccaf9888e75558f4e22f1eff7a063fe6aa9073d6acce1c8369cc2aa27442328308a453fb9d1413a57fc46ca785d9167c4c19d85de416a64bf3bad3fb9bd4886b2223116f092742c4207bef26c857361d7f033682454813a8a9c7989264dc517058588eff41b36910e190dbcb63428619565cc06ae9bc2100508dec2c25f96fce56174ee896b9f102db82859cd11bcdd17832afba7afaaa4afff23d740ae5ac0270d9b3f72ccb5e79e50249a0127ca18f221b9a1cee606bd4674cfe1698e3a2857c9479acc4334bacbc96c4f2051ee40d1b26d7b5da6ad3ccc0504adb50b38e9c97caa8a87e61c7573a387d8a9e6bb8a80ac27602497df8f1692d0c6099af11a9f61ae31b145b5761569331e49ecb98065b254d256eabebcb1b5ca628ab362a977322f5b855371b014403994fe08302174cb6a41153e480561b5ef0b6ef0425ca43c71e529809f6784ee07183bace2f5b318217dc01f1f86f0f8f9768ec4838273eecd8fc3404b18a7fe09ee9c7d12d267791d8e9d03ce127e5c9cc13bee68c0955f0b246d41d6911aa241340c2edf5b35bf8c8d7c7e61111a357a3b83bc633c77e056d7ba67da5dafbf992112a0662373072b97e8bf2263ef3cc4d0cb7e53502e047ba4cbf7f8a95816d670c90bda6e972e25b4d99ec501325b1dc304665b9732d5badc5433bfa80f99e480e62215e9784fd664ab566c09db66fd4fd51fe7885dd3182bd43de4d67d18d43381945782609c443d15bd0bb935ba7a9ac8793eec27f14bea646215ceab9156f9896d4946d70a1d9bc5ef8f5af6ada690ae21493bb619f8e7493fe173bed2e7dc499a897c18bed643115accf9de39110a75131284a38ccbea0013c2b3e43227845bad216502cbc73b8b5f7d8352ddae6276561edd15cd791ac22ffed132978fea1c852f137a9d18b34a2b18e51b119b7313276e2fcb6989e897a1597061761a8f8201b87ad76db366a0a3f1fd44c2e068dc8b6c8a83c1b04d5cd2209902359c11a336629468c425876356c23c34bff680e17f2431c321e5643aa7d16b2411535dd664d3c5c97d65459a81c2ae9ef014bd40b1783f5997dc04e285abb8b1174f33696d914947d1df74700fd7f22134f21f468f4c688d37403cd0dc5743dff3b8557c248bfeca7397363e045b1a7859af2524a2708b8a0cdee9b571f19609db651c1b0b7ee8b2b179f19a9f44a3127e551b0edef5934942a560021bbc17d087482d47e9e2d8ee3432e496e754ff9c766c43da5272f0aaf11e9425b1926616728be84c93c32e38792b16a6bb9ef507b31d7752330b653e3bec480f0c3b4270578302c8677f822fdf3276ff6c2fec6d0e6ec9d41396a1ef2d149b45a842dbd720f90f1910cc8b4a3ff3816b817b91cead4ffa9b10eeb451c3c6f6c59128c10919525564fa2c4c9781a1dbb328e55b5314bf63b35616c28d7191f0cd9ba5a0651a21fcad0b4a757292f6d0d249e3258b2b18639148d93d2f84a0d08f3c10c84c4086360ba1fb171ac88cda757f0bbb6c00ec7897897d42b1c135003888b5a5844ae75a7355d905af125f6e9ab120b1b70fd86217a841e3957b166ca0ca1bf268ef13b5762a8b0dca5436bf0f5e8d7d9d97a4544d357a3790a75ca3770de23dd4e04e137accbf3083c82faf3b681abd970a594176d087e7d8ec6c5267bdd35081cd31dbeb0a58025483fb14ad3dca21efa7ec5fb2dab198f006ed35c004928ab12bea51c31b314d4fa0c6f364fa08675ddb52270080dc3ef5df3dcae63631b158e4b4a4c9833e0b858d2f70f389fe537803075539b5ba02ddb98763ee6bd60e679a66ddc181fd894220cc04003e8f6bf0213abcb5f3575c850fdec03e8f184a907d3dc07417601688d5f69651f60d5f27020e03405e3564b45e922475e8e0c8f2826106d0d82bfac1d539158601e67546b02d39064f4a43c3ab72e081c63d1f61f760f659d52c2f5cb77c93e0faa11022c408a1aa644763fd9c82086a32b09db758b0066c8998690d15de6bc1f183be550f241993e9b1a231df1ea9563476a3e1376fa9a0b5ad3528400342afe2eec305852000b840ecfb487a468b8af55152542501a35812e6aca08614841100cc03b1c5607cfb130ea1f5e43a93e93d001182d2c32c304a306acc3aec805d8f2fbc20ff4df6a4819fe50becee1f4add357d83cfeb3c650c77e96b5cbc6507f815f6904ff84c436ff61a8a7aa9e6212e8c8db8a7c5fdc7e40db9b5d3d44b2da1d30d81a27b7f07e58368a1e4443e5f1412d146a67004d1de97c551909792b8083e7a0bc989eff2be6abdb9c5c08161cc89866c8895711c4ffc6e065f69249516daa614e36bc925e270aeb1de8ec159f047f2a10b87297cbcb1f653249d29e4c1b1664c8bf81a45d42701d9c4bf3764e1fb015c7382de03b3683657f7999d67d99a3e446961928820281d26e64604d3f39d467de1333c3dbd87017d1e76fdd2c353f1e44d90bbebef97ed602ea365928e8bf08625523c5871cbd4dc1e01ca39ebfd0677bf6cac42b35ae9125073cc40a0793e23489bc92a59b4f795135059a90525dfdf3f24cc812256df5f24cd8b560ba5f7d9579d74ca50b4004865ddd750aa562ad3ed413d0d301adfea176ef4fb4d23248d45ec19a43c40dc4a2f777da4207fc42db7a9d5037aaa92f479b72b2b77f30250b0e7196966b8220f94ea1e9cfc914cf44d596c26889c586717857bb21db4eb8b52388ad01df6f71e5d38c51e3992e947d24a9c12269fcc88f164b186db1e92a63c37190d56fbcbffc796800e193be945103069a5253d0a8d4d9c806fbf70575b7bcde01ea63854717efc30b6df989d08fab5d197f83ab11a6e68bf0b1702a24e82eb0e5fa8758835b8082c6bd3612ec0ffa57b352b00ab794db0a25cc54039e20a1acc53565b5e858540347887ca01702f8ecb161e602435507e57ed2e304686740a27fad4ee1c399b385309083a7a76c0b9989aded2739dc6253e17f75b4c1ce7a9d09989bdfa501b24d0712d8c646bb26ca1ee5b640385722a2fa273e49e94cb1de9d960b10644411ced1519c4a07c55f51d7e31089f14baa9890e34de5d1139a7d85794e352e390b4b18399dc911f02d71b02fb0f2095d6f5e008ddb8c853afd1e8a2d9a769f35cda617321c6cb186e980d2ed229c4a41f7fba5e890a99b8330745c42c1033d1b1ad41519dbd949811ecb3d00f5784bf328d37b95eda68e52a7076bb252b2c918fcf59b054228975480ed729a596888c580cef73f477df2b1fd70ba60cab38a515e06742f7ea8165753912cd95c30913e40a74568bf3efd2abf50a71e39c554f4df3140d1eb8ab0df6f1eb99d8b193635d280f1081bce3f774ef7a3440779797b648cf7a98048dd0e480b519163e065a54fb1602c092135500ea8c1d2960d4e138d6df058ab18491f789a0d0b0740e3bdd7d311eeb3960e26743e0743aaac5cf31ccdf82fa40da948a39589654852afac084cfa7ec7c13858a3dbe8413a00e6da515bdb4c594a1806c613f687eb4738c62b33337021474b4ca8b9b747b363e3a517ec160594c8ea15d65359e03a35fb8982f27c66be6788aa4188c5928506b4ec93e5e0e156b80992e4fd17626efb4db4911b09a2bcbb7159d36066acbc95b5454321a1cf8a3f15f5a6e13ee569508204023b4a6ad0d9f3e65cd39ca6795be0c700eeec57852a8290382fd91489c06351299aa4673740928f71565ffa1d41e3edacd8ecd55a8d93a88a6c0ccc2b7cc6ebe22cb7db59b7548abf926616d882589b6c8c01fd04d94be762974c04584faed9813c3696a70ff15ff496fe4165a67361d4aea7f3e21d067ee618b0b1f42035304f7e4597c8f1a4b8127f660b1fc412213311b6e2b462198b26203060f460059e63d0931bb6f092b6eb0a56a983f0497d3a41160ed1c25c609aacfa38c4e344ec99d41aac402545eb48b5fc9cf03dc7b0b4be3c3d88f21fc1f9936ff658d2e6b30577051f95daf90949fa03d33595bd852f492dcb54913cdd0eb602b5f8b6ef78baf5af601fcc13653e705f3b182de103202f2120a94713adbd9109465bdfb8415612021d52a4558f7f876e10454c906a14103821b8c56e1e8f8bb4008ef86f46378dbde99c34835d4e31182c99d9979d20e763872f00bd52df041565dd511498b4d796cd97cadfcf8730e91f1ad2d2cf8eeaafc0566215774c5a8896713d29725a34dfd4c35befe9c92c45a6a3c8ad0f113fac771854c9838fa64e292c93c01ce6292ff5ab2d6c14f860c36746cc0fd0e03273832fd49e7d3e113f404170000e4cb038a67c7a8551e2ba11e93c759b4a7225824693d42ea0f9efde155fbe45990d187fe83c52cf0ab090355dbc4d0978137fcc4f32c64adcc5bf98ea425aac76b1818388ca8829f0e606bd0d98f33d08171eafef12110a78213b85704c41234637054661b5d3b8b9175494dea21396923718555a0f41f18cdd70e5592083fa8799c20bbfa71e0a58f1556f4a0f934cb67873f982bc212864b815042b6e600bdbf8dfe2a9397e3d4e844ba78593e07e6c993e14a073060134bf0cba63f7b28811b8bae19ca831bf14e684484259a3d4980d26090a660619c447b128ba328b0b79a70cf19408b17726b31a0e224655c8366e7437ce1b50852b650b6279738af857aad11f56366385e09b2e2a95c307ec21e968f8300f8a810377a31dd331bc1d8a17922ace00c8093dbf88d1e2f3a0bfc61a19f1a27587786384ba07ec80a05136afada2546d6543013ff9d323a8f03381f09b33c367a8ea8e8a500d8cc874874dcaa740763518ca51acce17556c46eb91f24ca9246f82f616f6d0751ef6efd63b303456ef5323ec049c240b060d89709da11f990383f2b3b5cb6214af9fefb573cc7598104364f7b18e91b11b49f3f4b767b8f2cf80cea2d0fb7c15be96d448d46435633a6613a00d625f47bab7faeb0dec179ede5f96fec67cfc54e5768aeb511e3f21810ef1522c62d3f37dc61ffc5e8bdf50a1addbe9f795240e996e5a9acf9efb6845b12efbf4fd87db84368da465f67f5d61ddd21c7f63eeda51223a6360a42e360e846d067ae6e2ea19c93d78a56c83825acca90b2528d8bc6ea4da3c62f63a0cfa18637536a35d4a959437732b0016130dc006116f9d030f5310131503f7877286097c7a514d7acbefefb1122be55191b77035aafa11547341daf00b37021ad6ff7c352add35846257a17647b41c74eb1a6e27b55fb05b1417c2fc636720fa7f1474734580ba061fd6d6018968c7ddc51786ea14ad4152c643e0365dd5860f9e86782418325911b47fe9626ea2f050dd6b5015941b8d6392052c175cc1ccf302e3089e63f93422bf45eb86820507ca514a205c5112c29b0568b491d2ea09a1dd0e51262bebc3be90a14c64d9427a2780b92782be22dba5eef3537da6458304f46a43bbfa97ef0d004bd8d3e900d57ddd24068574a337dcbb982c7172f6fa3718e2f60e74c1518774c005e8d1b925fb2de6055fdc1c5fe6ac14f269468f7368a44535dd8f92ee159a2148d26142d1892ac0bae885bab659aedb5031cd1f0a8fcd56050aa8f2bbaa84ad999b5121e17e308864a64df8ad4be6bd1aaae67043d48bb9e2ab7e54ca2a15995b58a8af57bd20745b32e0e3ed698229221cec49b26d408009de0f1db632d8648b0af4a94d14bf7bcec94be4902ecb61260b705102bf2cb334bbc330536a719011621881107f668467e31437f306937f49a4372fbbe6ccf2624f4c28d66d5acc316931acbf2c97da6b9471f0c43b3ecd85edbf60d9c220064a8d52ac99a3e48b687ee827a7f57982ca131668b85fa7378156212fe2403651a3267b902a21ea5115f7d31ff8e9663d22611ed0dab61e0a53865172f53f29514f9e57249d1a3b4cbdaf9114226b755d2f6daecb61fb37e64b5f04381ddb5ef2b6492d231804ab13d67a4c9a7de3b370e2aac8b72ddafde8b2f80ede4a96669ea8b827f9361bed76b777bdd8958b42387eccdafa1fcb45fc3cbe4d9a07ecd0c7f6842044b875712b4b7487ef052757c9c1bb825c1b1bf3576fb989103d38644dca58432f2e496a719bb215e651c1f672c34611d619accec5ef2785186ef71639978d89315b787e73fedd0ae69ed9ce9f529b0a4ca36486ba0ef607c20e9e1633257d9c004a8e2c916bec4d13d28bed31a58c14a48becb66f659ef42d5efbc91c2b62fb2d047c0a6570a6b989f2cfc5df52bcffc0c95f5091b404798f200adb7d0285056680f06942dc83b155dcf639415b376d5c0e582f65b6fe5752cf4cbd53d2d23ee22f3eaafdbf8d963e0339fb0394facd503a4bf01da4441465c70caa04e073a0b143bef2088ccec36bb539976c74d77dc33073b0e526eb358095d8ed9670ea3fe136921149023b04c4d28b2b5b1246a0346c1ba1ce0e1a095e1cad35556ccc3f1ecd136c4c7baebf817d3ad6d7b4b2953927befbd651c0433045404b8078251ba02b3a61764a71315dd032c9394aedcca531145f98251514fb686293427265e379411256a10e1c90ab4e43e57d164863ab309d1a94ce97daf6049bba8ed8beca80894c24cad146f4236d90faa486a0bc223930223b50b7b5b17d083896d88fb29b171f962acb621c027161b98ef070c02c146e4899231b11571f022888269d1becb9c52420bcb6a1e19120cd2cd5caf97591d4dadf5ef48d76a0da335aab5d65ae79cb3d65a6b1602fdfe9e8b44604071f3038cc5cfa921ff89629f443e92768141a18d68c8a668d5bdf7667f36d3f0eaa7234f3ae2ff5fbabd94d1113a4347a50edbdddddd6dbf9b23d170fdfa9f05a9a7bace3967edee8eb5d6ae7f4a144ecca65ed30fab55d95d6bad3de79cb363adcbfbb61b3a8c924f2e91f96072ce396bff9cd19c7f366dcea8bf1edb63ce4cc2f27d93bfa4e12ce7ccc46363c2fd7e1eda9cddb5d6da73ced95d6bad1dc9264512814a80f5bf5371b91f7a0a3da84d21cefd30296d3ddf990b33216d4843d3c58cc99854ba18937425edfece47052bfb7aa7d9b7944a50ffb7c1216977c727686867b3e9e1fddacb2d2a75979987a45d5c6e63fddae46d696db36647d26eef67ab9922c5deb76ffd3f6cffffdf3de79c1d75ed5affffa3e6ec4a9b1b829b18b8d4a94e35ce1049bb3b1b4eafd481a29bdd7aebad717e33544c7d57f7de1b742dbaf776dfbf1bdcec87b385fe36d8508388dab6a1f89b1933b1ddf02039992806f726aabafcbdf7de2a236977c65fb8683c1b3d2e9182652aa59bf78924ed8a95f9ecf76f37fd373cfe1e64583058372c9b37f1fb4c67764ad04d5d9e83679b2d5f2f5fd8dc2acc5d366dc1b499fd3772e216092ccb4a682973b4d02559f4216937e8a53864ee1c336678741f3424ede2ecf804bdf818dadbddddb3deee4fbad639e7acddf5ebf7125d6a1203b69925b3bbd65abb0f7d1247b840e6fb257ed70e84fe88b23be7cf19bb6e2ae3d12968840839a37aa766b2a8c99c73e620f8c281b0a429c84146d57583dc8cc2def7c94adacda073525d4931477d214391be94e5e0d0172341f59663082dfe18cec8e4e5482e7d1903fe50945874df9e0f2108d0adae29aaaaa6ac6a8e1a195455387163459133430fa614aa4795898dbac68c193c008a584d1f309884a1429658418e35a4de4811da8c503c7aa08c58c39276d70a088d18d0dede8f21f5b35f2429adf7fbd77c7203f5fd77aacd6010a3e6ec5f8790a4dd615157400799763df3eda44fd5e4b843b86338e6d3f52248d64ea9c6284da1823af54af74b3daf1e19401e689c34574e255a6bad81a42af613ccc9bb152ee71b142a9e13255443275643a5932add53994a052198037b302584ba05a0aa6880f622eda891927061815bebc72e1e789a8ae548dad519f98014100ba216d472efbd49b8bbce3967ed8e71936f55572f2392767dc0a07058a4801759120e69aa5fb86e7c0646adfb00603401e964603a14d53c44eb68d18e1dc3748d7db0d8efe79491b43b43a383b3eaecb03a3e2deff25b2cfe265204dbc41cca48da9da1a9e1e038cfb3bc9f4780209cf7910049da0d0a872d9222d60932d1a9f1be353ea263e2d854570fc59a0732989b4ded434e0810adde7bef1304cf439de8ed7a9779f33010db9c33be3db0667a561491a8e2a123c4453d22f34025d595b72158b2b8e6208c87b34382c0cdca1c1031188405085a6b67d15ab3b8e66088a92308958d0c5d2d683e48e5207110442affffa7079d2d1617ab7565d996d5c7b6b0eb0ac13f27dde54ad155515611115dcd77209b57499a4aba4aca7a40b6a6a8ab504e482764b4f2fab5bb97297eac9428f78d7a24edfe6a46532afbf5eb4ff19472a2ae4765bc191e0d0f2765ef4f613433a941ac2156a3ccc68c0d1a1457d15a6b4ff37e77f7bd77ea3b850a15555d59fc4e52dc4028f19531cd30d130e198f8c6b1b21f49ca4c5d7338927673b9cfe9d49cd1aab24a3a20af1ea51b2dc8460bc0771cd783f4fe501649bb44b1da505989de2fa582d6bef6b526dcda9463672d1350d091454288848661071dd1e68538a1df7149e9062a0291a980493100644183861551594d270e1331bbefbdb7a741f377a94b75a9690c378de2a641e6af33aee741ce9a4794d566f6376e83faeaaadbe97aba9f6e6ad55db1ba25e9430d241e516e6cc7014b257d3f939ce49c73f6d1f8707c3baacf67f505b1b9e59d88828b688a708a768a7cdef59f3f57ead9eb91b4fb9bbaaaf5b2b4d63a05bbbbef9df7bb3bb9f7f6ed6f4ea96b0c1c49bb395d2d86d19399349752b3bc3fa348d2ee0cb2278251896eebc9965840497c9840a29475def4bc3f8c13275fd45e2c151df19deef7ff716166a62049bbc21ad3509790daf16876c1734330efbd771a011147b0a70311c3854b25a59349df37ef079775ce59c987a4dda09ad25086272a9b15bd0b491432d8a564c0ebd557d59513e57eed874725bd20c3408152bd1ae111cad450d744dc7e8810341d712b6977cc445d7d74330bb3cd034c8b1dfeff1f85110a9fcaa36b3bb99a8f50463aeccd6a3d38a9b376e266b5d648e0869cd442c43c59e3c93135d8a34fcd44500986b3ffdfa65622476519716353126be6e46badb568b47050bda2dfcb74ac2156118b2cebd7fb4b302ff196cf56d0d6d06b77d7dbf1ebf267c42ccfe00c49bb34b620ce5a56bdfbf8eb608fa6bafeb0296917ff89fdac80f75902213a10310752e659339e45b34f130cef26a4baf6a06933aaff3507ed3e70361f39ad636e58f8a8d182c847e1b4c95bbb157fadf3e924025be5ac74564656beff7f94bdf79ed16cd5b5ebffd2b5eb3f693875a31aab24efbdf746b79eda4fa67f5b52a6fb7ec2da614adacd5f5482d5d5b6018fce39e7d918238fe77d31a39276fdb78aadcdb16049bbf83709c2842c73ce61c31d5654fde64775cd9925110bbb48b00b5765f7bdf7768d4bf49d088c8fe172ce39d789020981e9c4962d3882cb198182169f1603b62dd92b5396296bc9000d18c150fd186e18aaa8b9e0325c56867158fed18807bc80a970b14407ea50531827d959e89f265a0c58f30656408c0917396519321ce141b568717b79503225d73b7e218562fc637a54111366110de68b5f8ec3d80b27e1d359224a4c08017ab4687ba3c4a47a4cf38b52208898674f1418319f3c2d061758d4a03dcf5e51e63c9291a4e38a035489a3dac94ea064c07dd2a18093f5b40288ab3744477bd2dede10df58937b7ba3c276b4b3f646853999a5090cf5b2d74b21501f3454cfec5f06975c50b75c7641c960ded3b03ddbcb7b02823a11fddab5bdbca71b130fae76b797f7f484c403eb57241e44bca71cbe751c87889420b243ca51d641ed4f12ed4b65282a6943c41b13d4b4687b794f2f7af06867ede5397140818556b571de11e69a8f8425d49eb497f7c4a3eea8c0c845a357beb5d63bc11de1ce70a7b843260246c40d95e0dda9dacd9a7befbdd535040d49bb383b3e41433bbd535cefbc7f1bb57befbdf7de7beb58a06301088cf7de5b82fd1f76a6ce3987e891b4fbfb29f56acd622d08416c71c939670954a27d6affd123b9f7de252c3adbda45e89c73ce3993c8e974cafb889c53e79c2d18112451b5671903d2079b6dc1081b0b440c7352e056fd69be9dec7cfea74e408a6d66518f99a5fefe9f9752102aa6ba0a7325ef8e5ba4e0d2fcfb38a648dabdca228a4dc051a60543971af4fbbbbbde4827e4aaaaeb948fa45d6070c294d0cc2a8296523d38e87e4da5ee513bf1b82a491c5a3aa8d689bdf7d6b51946499525c4595b25254f87d64a89352469b738018b8c9cd22a42b59a1e7cfdabfab1ea49181dcbffdc59521939bf2cbcde7accb59994d0c2b22d13bc84a5c9ba1e479338dc1ba34e5db827fa4c20067520a6032da110177e80e708afa17b0dc9ee5a6bed5e49cef26ba62d2774f1a43b2a586346a7243114080361c2564e47e34cccb860de955cb4eeeeeeee23691718140e8b3f4108d95740630217705d7d974532544a7777a42149bb450b209195a897453f949247246957ac6c759da30b2b16e0dd50c6522c70043c02a2008d9c569f072f5139146b2f6900a01c5f825e700ccdc234ac185371b39a718ac0b13b794ad9da1fa98a0a004032ad65984e2aa3950d8ab76eaad2a9bc58f97460ccf4ff6c76eda3f3cf66f3be35dc73849fcd7a84ecab3a798f90cb795832f46d8042252c210fd0dede0e0d9a44ad58f6cdde0e212ce6b9638424ed0ebfa8da62c8566731d6bf2b3c702434487090ec20f1c1455fa58a08f4ef0860dfe7396bb0a4babe24019aa4e601803dc2f090b877b541005e90144917321ac8d894687f4dae98504b5eaa6e3af8f6e879f3d47c99c2e118b3d226533c49291a205448525d67766e9be9a9653d1b052093500915aea4ddef0981288d8bef199a42f0d9dd3d454b75efbdcf9d1e4e569b81e49fff9334d7105c3717960c2d93a1ea55556b7424ed1af96a35c0ed239bdb84e741e66550a66f2c314c96a3b21809dcdd9da7219985d0978ecf082a1d5fa7cf5762a5229d17ccafb282b9e3c0657e7e0f84a5004a1db14054e15ce2904173c89170976e88dc0037cecc8c1b2d789411c9114389e7488c059baf0dbd2414a0b1d84107e6583600f7acdbfbe34068c42e81324b9771a3b8c7f90fa4c48eddab7cc7b858f5ffff18e31f5e23ba2c14159f6a7ff6da37cd9595d05284f13578002e24c9b2737b7dc518cf61337efa6eaf8fc88f6189a018d506af9d135b2712d2af669724c7d310869397dfc8d00f1a233b353e1be7d14dac66e6bedd7358eafe5f6eb5bdbe3020ae358e414cbeba96a6141ecd6df3badae0cb4b27e881bce1ea9172fdb036ff5c7b5c41adb7b7c775e2d6626d29cb17f9b5ca2e2225613998a2178cf753422a8bb7635173a3d69b52221d4efe776e2f53172f44cc0e520ca4193c8a8a9adadfa0fd2699a31b274254d05194557b1b5144742e0438011bb47279b7c72444a73dd5f63209d1a085d05ea620c51e72a4318992808d8c1780a929d1d58248ecaa9d453a88c0912188e70490ae00329e1c6c69f42165e3840b4962ff274972878afcf62ab918d371c1a3a3f93a01c928c6786bbf002c030c1019706bd7801b7f1db9346fd25b95644a0415e79cfde6d1c4796f08d2c9b09501d0948b5aaed16694dc116ce477cc84b2125a784e2e9c4ea022f0927a597a784a7a5acc1ef75c40dd511e2f9e1e2cb6c9d4880f190cd527561bf0a2c38a9c051f335b3b1b2ad881ccc822631544e86680d5d020fe3c88225ef91801a2db508685d58c1e27515497af7d56420b1187ff5c3367696431c6f77467df64d9722c3fbd00db3eb0000e90ed3e513ca14c92e444b17a1fc081b125c976bc0ee800d962018ced2900db07463c9a471b2881b463e1b3754336c385246778d1398e3d61af7d21d3a9e4811d52501a5e10c9c0e7d4095998d82ef74784a1c8ed1b1a122b94667a3576a8ec469c4e3b13e9ff3fc6a0850294f63aed3078feff779b5b91103982469a84a70c1a42a9130da300ac4e346e5add5e271ab752882901a91aa730133cc4df1903638cd1dbe63001c6a3178ef62a434baf21a993eff1909b154ca47b1ae3fdc56182c7b8c443845acca60cc195677ba3863439538d59a3730ff93b481d0cbb83c198188e9ec440ca0099e6055d0ad1330258d08862b1e1c708d109630a159efc67803a59b22818538839c1326c69496848e85683d7612ae607cfcc9090949094aa818a425850580e2fbb9f4472fe81a583640d92d88051a535a2f62f7e24b57fe347d46c8d3222f5c3a848065270c0d9e109f169b9309b72085de4844c653873e69532cb24fea0bd51452acf3842f65c9295e2458d1b6165bfdab718a2e19c8950e34445b2c04e34513b6e3bbe3ae07c4520b030a87345188941c5950fa8dc872e1b218ac282a47e401067900ada10840d2298189511153aa0ad0a54287e6ee078dd388188cca516503731965c389d29588264adf02063f364857361c90fe81851d8e9c18148881be70f493444c7974b54d8d61610b3f4f1d9edccb205cc040eb225291fa7138559fa8bb20b307e3c8832ce1345d8d1f7e77fbe482df69c23ecec1c9f6aca0a426a4785cb0a418b0b04127066b324f1572835aab6e7e9f3389549a8f46b9f09c9042a8016c319041400701cc771244942076d031480061ef870a4ac583c241406425118181085621886813008601800a0200ee3289ce414ae01c01f28667066f54065adea83dcd9dffb627ada62943ffae90500f1caa097b0b198a617202688dd36637b9a5a7ff1150cc4017838ab03f5af8deca678b5c76c262ed2d47d07630b5a68846ef72c2ed46801672270095c2f42ed34e1a819a372e8e580309786224536f0085d7c18ed41b57f84bdb16851241359a0a4f6039ea8dbe6896c22fdcdb2c8d151fe093992fbe0ea057050708e071859759266455d4d3e94d0a6a0e4568ee45add22d6bf066ece889e4daff8bbe6abeaa12a9ef6864e60c3b66c0e68c0138b65ab2cdc803cdc2f839aef70110e3d85173672a9697cc8cd352a1daa48d11db7fb2be12ca38b0a5ebf7832b2821c7c91797c811070e5381952970cd584065b84e670b472ffc8f7d765c17a3a24caaabb0917865b04d20b40dd605ab512b923364377711636b0936358988255b4b2344803aabf3449f21f17640f4769ee3523b41f302be903cb4988a402252515811253455912b148a139a97dc865c4ac6a0dc8ae96d8070a25cff6ac182bc165894d08ae32cf20438d571821346f29ddb4ba03ad82021d58f6f49b552b96d061bf02eb46dd1a950c473d505430e7178bfbad74a29ef8a20866a407daf6c02a688b8c77ef95383198e8e711a401d75bd59ef665f302ef528a22a6ed4fd63ec5d737127df72698840438b5598a8e9f6fce53fa5414e494cd8dcbf4777adeceecbb22b2698c729e2d50f33378a904451615700028ffbc2292d8f3484a8a141bdb676746b4f44031164d37a449e16c059fa957491c98834a0ea0f15bf719305ffb2cacbd42dcd704a419f0a16dd18e97a35a4a3f08efdd2ad5b37c38d9afb6da77ae993c270a9ee5d971a3541d9a8ceade68b3c78d43335f6269e91a8172531225237078bea72bbc4434427ed99bee63482030dd0a6304320e26b3a2a5764164af78017ca0c325fce659cb65e4646bfd5e992dc9ebee790a70fba4bcc23515398463b926b4660142283ec02e71d80f08506a6c8c328618d801ad4f1da38cf1cea7741683af28d51013086c980e68f80c0b88ee5a806ad3d78b66f8cd10b0a9c6a460661d6f4a491d5f977b2c9b1ae872f9d48d70089501bda810d7450bc010924be0aeb2b090ee09dcd6466712b16553380b471b5e6f399d76aa8b10014707eda9f14b87e853acc82a88e681755c2babe4d4df6346be82fc01146df96f3cedb2bff6b4fbfd7a9c21d89893b7a15cb460b2a603316570da40a67c144f4a999511c40942a4bfecd2185b4b252475c21f94fc25406816f64491a9ef6f388f10b22bda6a4470c301bbbf5e02d26886bdc220628dd42bc921525e282c51a9cce25ff51ec296801fc205734103850dfc84ac31f3afba31ba178f4d8abda6090a4cf21bce9a4add211b0ba8002bf7b136d8d89115bba0867fe4924c3215a8f68d2442a8a1517d048aab68c53b301ab1a873007b07230619cf4f1170fc89e8776e504df52e52f47f2860b7f7b72d7802e99194a97eb458a65712cb74290a9c11eed35e983d5714ba825c92a09c8be34637c630b1c6b9300039f11b0d299a0017676e1bbb556afb24598bfeb8c1a97017b4b4d66566cc887c70d15633287a8c0eb72bc094a13a8667b2fd284af30b78261f2440ab7d616594b6923e8ea28ccf089c7e1b9f646a4f131f3bd3da0c56e0ec41f13da53d3962533314388cf3112735d98b491c709d9184ee61b8ee0a31e72b02352bfd02a5e95e214a9ed9286a1c588713b5e0ffad10fd3f34ff2f24ddac2040caf2374e94f315db34d57e81e1dd10b81914f938ae6c52f837274a6b029917a92528cef076c2c89310ac73864adf89f2929d7266d182164c67816971b0dbd8ade721a377d5555d15b9599a6033ea5f50bdbfe0193c83db0319be06732233904d0ebd7c56ed26e204a9951c4b39153822bac8336420d92c04cc093e5327c3ebbb4633ad7d76be2204c14b0952259f672a02a0227237f836993920e896772ac79eaa55675fb55eb7fe81ea926553cf065835454b0282baccf594e6f894726fde889fe39eb1742d851376f53f4bdc616da5e83c1ab60f625b8f2efc85930e664f73e9a96180dfceae79cc21d6151705263aca4580b9e322d3a629a52e27ade972d47ee8d744dbfa797e347d757139d328790e56c4c998299a26d3510ea706dd72c44868b899eaaaef4b6fcd3015b7a88cae13ccb076bbe5655f2f2933ddf0709a948f0e78d7e61aedb6408ff38a1c771c82e5bea344a4fb09a9333c8bf46a3b8bbf10cd62f59740b610845f9e020d35048ec5199a81ec7948618b25fd1da47c1d5084bbf584a6c741e8f78d2a4fa9e33d4ed9ace5f31bdf56e06f07ba3ce518325536952d20827d84c15617839515ed72a1d382a9b90bf2303d6932534ed382a5468192298547b098be0bc6740d102b4ab35938b743e2c1983281f82dfa96c1caf2363e25558a9cbdda538726b212f367095bc393d8cf84695fc41880f23f3867e7d48a5570feee7e7d0f5a8d586ecc7409caa0f7feac41564eab95c328f1a462922068fa523e01bdb51c1645323c05d9529cbf74ffd7c114ed05b5186ef1c6ac8e515d747da11a9b04839012e4e816eb057c15a7f327ebe67c1acefa7ac0d39ee7358d440b94bb682f27f5cdb8ef3801760e331f364d600c44edeec3a3e3941650e632dd7fdcccc15b44be55fa82f1f420cfb8e0b4de87eb93a2a4e622f75925317fd1e96e0a74d0b834a89f66ddf01f6c7a61532b2393a34c74e240c79bfe4d26474ae6d0002b06c513dda3e6f654dc6d296c6b1ec29fbc448df2d1cb198070c04f51d0aacc05064584309a0aa179661f301e4b21f8f7c597d2bf08784a757843162106baf8ee29783c4733514951435641297266de7567768372cd79839552973769668fb87d503589df930a038458a41fd6bf2987e7cdf9b5971ff2ae508691a4e9b2a837caf0a4417975d6ca2fc8a873fc5005b609b1b1e8174841f8ba9325e7428bbeb103609f5f4519dfeacfeb596b38c176a6483ffa79d518a3582f2a6828ad847806598278aca080357a64dc697b711878dd4f07bdc53fd803e24f4b1c86701594b0bee7773778c2f4e0cf3136d15100ebc30de6fd02025e3ccd5ab7de53d6fd4a41b0c93941ee4f03a4d106fb8a33783e435f21a02207f5fa7520ce047ec9235824ea4fac30cc911418dc09571f5430610238bde80c8b6be252155dbcc7909e0c77647ebd16e94925b06fafdd3452fb30ef6f8d73358e138008af70eab919fd5abdf3e09751608de823ef9f7fbd3e911543617489bc7019e991d12f846741501509dbea7bc4be338cb47b50d1272aa6e32644266f1ee998b051b91443cc4a4eec148d68963ab3fe1bea0c89908474683976f88178cef055ae575e82f927a578302c2454cb3564c4f5863a997af53fc4452257313de50d95a832ccd78545bf8ba5e0503449ebe97750a76c2a465dad6abb1dfbc85577b7447e30d1ba604b58c2121792ba3c4b2059fb2f482896326ad40b76f41e71b163bdee41e1c3e8eaee27e303917fe58f2033a3b78b7de72f3226e827c7996388001095068449dc829da02f4a53515511ac567a735d68a258f25bcc845c87109c721c456b16112565ab102033dace4ca88235aa9f4e5f045b84f2123dbe6a343d221b66b4abe0fbae15bd721b319a9e240b0bcaf1668dd789e8721bd57ddf7bfeba38a3ced418719427db36c2cb7c338dde10f5c92f34a1b5d173133a2c9689f6a00c7f5394b96e4c3645646ad3854162c966bd66f26f86164ebe63aadaa05bdfdaf669425269ba13eeee7e66774986d7db20e45484d824383fd95476229f1e903a3b081f33fe3489d9c1764a9ff899315f970388ab2864379699f294b434ef6e8d1a26ee93384250a7e4a5ee8c2748ae3bb79aa2a989842b0897a268324a4d0cdaa2a077a5df41f56f06575503c06f443f60c197d33e9178ae097b09f529a32f611f7ff756d3c78a385674075b53be41ff8261960f354f93f26f683ca3687cdb4c11607b592ad2c4ec2f58eca0472f303d50462b52d469d1c4ecd454e327efb51f865a849587c392c2ebf5444b1a942d38a7a6464a990fbee7cc7beddce9748ecb2f9ed1d405320cd7b040a22a4f2d91abc0c4967235977cf3cf8480854a7a9a302f36bf9ff5f922875ac46fbafc17dc45618c8ac3c591b94c00e917e0eb7ea33314dd4a05805146dc70ae8a8e0ca4fe75fec4fe0cd8613acfee372a7e7ab7a271931893ebfcf18cda1391cd5fa8aff55560f0334fb3763ed1490a629ceee7dc5fc955c32dfa81a40c519814641a321bef382d2e177dcc7f7645f3c2b0e06aa8db7f58c6a48e2299bd954196fca43bb77b444ffe6e62b02219067da1a0a374b4234cea426b4408735ff7d86c4e089d040833cbd162542dd86ed5df9d783ec8ec39e4b136e26a98d52618bfee4796d6fb20fabd5aee7318cdacea8745d6b695a42ee3292da4a026344ff1f8afa2e6f78e993515d45d708952b4952e62381887c6ef2e2753dcec96a9ea532211538a9cdc2e6b7ea9f12aa31b0b7b27184d587750b5910c5d84580b984e02141caafcb75b010b58b66f043ab8424fa2abab9eeacceb43a1c5e75f1b88fad6a1f27644e639d54b6001f9ea6185e206d998c3a42f0eb14724a8286071b4cb5ac382372e617e4f61c51d9aa45b60f8a1782c7b929b47f1cdfa8ad032f406c044b990a563ab5af1d8d45f8bf013d10151d74298e6d8f5be8710312bafb828963116bfc6548882aff23a359831e5faa80cac7e3a1abdec461780cd4e8b06e45650d4220d238991ef36414b6573addadfe60f0f5f0646c734aa817b1dfeb44177ed26e520de0bb9c06b87e466e387b63da82b084c0af2675bbd83cb7c04cba5536f41bcec07cc07a10e9a57534538b9a0790cbe187e688ce73e0c059fd4a5748ec35bb460a883951e7a32cbe0119ebfe902232162f0af1725fb78e483be15c19c002c086327821571e2f2ec4dee44f4b497924bc6feb3e254a2138630f5f4dccc380bc80ddbe6509d6cde5a9c218877851fe1f664d2de222ffbe8ffe72698099f9ad6582b2f4e2835614846b1d819a1062424a83735386bc5f2a9d48d11bf1a1320a113bb6dc180ca9f2862eb92d8faf491ae0083d21eba9ab942ab5c152d3cafa1f9ec45f775b59dd2ee840b315c3ab2dc09cf86a4ad42245c14799cab7925da857d0cb381ccc45a0bfcbcf527f097c88793925662bf94674facf68eb1387e427a642a798c445590edb96264ca4435ecc4401285afcf2aa7db15b57024a4fd160ac44caf27a6d2c49baa3e9f1edd54112670080a5a43e7267c7a24a3b046952a70d4db09770f794502885a1f8214a96215ecefe7d6d7861a8da735b7d6ba9771d5924c6c74e63264655d8d75c4aaeb65185098581fff4cfd8240ab42f823f169951825e36dc05e4e02a2eb6b8e22e2336be99d36026a8f11163565ee3db4d31e47d48631faa157fb51fcfe003e50f901912b869e06a211913e2282930ace6e47b4142f8456802e228884614e147382b12894de960dd878a3835b53f38416c0996c699097c8e328f264049459fbb054a0a55e7e3a2cf140517e2e165a955fec97fdae99f51c6835e0de3614ba15053759080d5c2cf03c2d2f744a0f878626041f19d090c85206f04eb15cd1df5f887d9b0412e47b69edc3d8a755f2fbd1d3ce96b9273050ce6e9603ce62f8170052d5e0493c83c8d893a3dd444d57230eb2092fd5b20078161646b526d1bc24cdc9b3432f21eaa99e02c5f6427235fbcb3c39bde74b87edd293066e8700560f637a9a4c5b7abfe87c59cf6387d18497f67a3b785cf6ca03ed344d6ca3628cd024691dc40023d204c5dcfd8cd638bb1e04579c7533ac1ad22ce7c56ec01489e613846c452c0464012bb954c6ae2e9293bbc0616ac0abc2b9d7a4028690f1d77ad43a478d6a743442a01af064d3ed89d2b366428ecceae218590d3465bf0d71b7647bafc7d91107ee4895f837a9348ab2655913c7c1ea043532b9428391cdbd46b0abfcad40a39b8cd4a33fe21717b131801fab917da1b1c300d6d0b48c1506a809d3045643845cb378b559fa358c5b040808f030c1917cb55019926f9074f1e7d35cf530733a625666b6d76144a1fcb2ff13c92b48115fa429e21a28d076360569c05e484d593427a8c8ae7d65bd790a503824f469b5a1e49b57090daf358144f9d03c433604d0f5dc22e163ab000001907535d74e956f90ff3f84ddb688d2482c96a404b69430950aac97ca7374dae2ee5f905f083742a7ebdc4f67c4d8c1f1f5f7aff91a4cb8928c9bfad775c1b93eaa8c4cdc756a27004f7af9101608bc3aea042219a90ef147a45f32175e297988dc47ffe74dc4a23ec16fb5e78dec3ea57293221311465a4ff45f99aad734a50918f2ecb45bdb9d619bb86a66e0345de662604bbb4379c6172029de08297ee1776f75862980cc34ea9ed3e33a4d8223ba70d1318e65585ac0f21b6b83ca7595ca466b604ab274b225ab77d1d5d8dbbb4414e76732fc969b1037a044d620b5fceefc54cd907516433a0fe20ffc550c4e4c0833efdde78906aed48369cc9e436fec559578afd99e2b5462463837eb6ba6c4be877e2927c5cb626a3c1306d2b22fc212ae1b617a5786356848092353a54c83519c21c12ef5bfa1261b8c74901334c0d3c4692443e4052d7b682325d2f0c3775dab1860d919524f55e88051bed801875d8275f16eef6df56713d42832fe5fc70545d08d30d4d06f4d4fc1b59ffcafbb12a0e04a27c41970cc56813e53ab78c90b4c3d9cdad5d97bd8adeb785c689fae478ff8ec30328d8f2855c14f95c64dbe0d3c7a8f4aeea70b6079cedff047449b28674604b094864c824cfd5dad48b6604350c9e7b0eb14cc3b28a6e21fb2f53f8e535cb59317154bb5b0f0170696b3b317bc040ca23ec2ebd543ffc0154b8183db5d3bbd7987d07dcedf8baee4bc4c54be64e3f90658452919358eda940a841523ad4668b77ea1bbee4b8678ec3a5eb3f113449c8e0999d20b31e98cc4296b0e595617dcf84744ed36a3bacc13000c90d39cd995a5309fd99f7c5f2423e8948991c2a7955b78f7985dd220bbfed82bd754c63b28b0078292eb9f90e9fac92d6f56448400d5f6463e88235b212604809c10663b5981d22ba0e9a94641c0d8ee84a01770bb27716fce9e2ae61ff416dc39212cad83115518c4e1473017799ec3f8d610bb19dd43112261723042501c6500debf8fe3fa3fb3e00a7498315a5d2f67ffa14d385282659d2b72116f8aa81068649f5e9c9eec57081d2ca5ef2f059ae66fd7086515772a6493758ccca240c002ec88b1c793a740ec980800b0038def5fcc9f7ad3eeab9215ae0d02884810c684ddf403debf9e0de34ca44dec1616ae0713369c1553d3c2a4864086438afa760b62226b8b4507188cb71067c8268153c6f135439c6b6642e1d10f9960d8b11418b65e430a2518019cfe0940a104dfde0af1cb20fe554eb7d03712ff1415bdce3957975e3b7b0fc7d42460a0c359f2c525d6b1d7c200c7cebaace4b332bada00a08379603f247e0b92a2312984020f9634939c4be67d921c0025963917b2002647a0aa93c286030b354e44e3045a4b2b2527121fd3e656fc7beace8a0e16ac4dfd9e3c3f0a1ea0408064c4ae898084188536272aaad61148c97a63f45df10fe0833cc9775b9ed5e150fe1ae6777340f9e811b0fac7fd18e244dcd7d4a02b5902cdaf361532e13a11754fb01a4f474ddbff2a7e64fd12195e96a5b65b694790e7d64049b74277a2ca34fce77e990ea48f5482cd5ef7ca1d5b007499d23b0a7be07018b27070d66545a6c8597dc261a025ab12473f7e0bfcc0277b5cc4df5b21c9de4265527b36154b0b59abfdab89a47589196d0b9c2d2c2e7c4cf810e62a65d18cd00b8f76869240051c3d590e0850056f8ae9fc4bf044c069626429abf68385df573a11f877f53eca2b2d071d938312d8fc90d42f7c83ef3f2e3f5f6edfa7448701d7982551a0aa1ab4aa8057f67bcea3b5d4ebebed58815eff0fd09c292fb73cb0ebc6e1ce82147619fb3298ebaec8dd57ccccb9365f36d57bf52f23486cc3079171ab631cc985c582d785635b8edba2dfc3f53c67c93173e2f6e31bc1497fb51f0931911b18dd12c829dc55963611d8a5b56400b9f60c91ee664b8137a85b2e18c12c313a49404bc1b9ed33496be738220550254eeddcb728c334a344d9951be3f671786db6f5d0d15f8ee6023b6971483d6a21c28a47de68cbd192544b33751521025bf5a334f637577618fc344fe4ee21911d917fb64892780c3425070856c1d4087c72a5109c23aac64ee60aa499f6a4c6a686034de4180eba9a0fb0ea205af2ad6e8394eca28500c27245821a26ec4e576c736344bad18d730b60a9b9429a58ce20fa8a6adf5e07097c16e2aa6916a41866ad2656942d6fd20306ccc5bdf3a53e713f5465bedb48fe1cdfd8daefd02a76199bc016e3b1393ae2c0044186c59f6f3b97545b48b8e6d1f62be6993ac790ed74a38f78f4352c732334e9a77125d545030ac6ec56c2567fe5b7fd4158781af6ef521ce96c1630f0759b2d2d73da3006a02ea4e9d5c8f1d41b8b0f05e42bb8a257c3e51cc0de4e05bb59ae0980e461a05c84d1ea7f38ece10fa694d1357451bcbd7e734093bd3a25369391cf46078605b042408dd34fdcb1789eca952c270b33f08b2353bfca65b6af5c84c72bf4c530240233b11716be1cd9956b66130fb5f08a27b8dab65a35f4261c399bc1248427598e3b5fe5767ead1ff12666047a33ab63f0ddc50d3268e144892f302c78efd2fa392134e0ad4d082925b2a0aec91483783084279a847323e42602ec56f3439adcb401afcc800591aa56cbbbe62026ec80c3ccfcbdc75907c1d1def7713e1970330536a8c8b6d467b66b3ab8ca77fb58ba16e85bc4c339afeec708bd65892b2940f7f045638ef259afc477cbb4065cc2db4595859a0cab82456445f1e15ed553d509056d7ae0bb351a071f9c905d862ac2714002638bbae1f958cfaa6084f49c95686b9142396604f21c2ea80ed95a6986a8f7756689a32c54e1fb01eaf626460b8e108645138ee451999a201aa130af18a3aea38d38bcbc93106b12f07c1fab8acd550b2c40ef631ad200caf4563610c747de89eefabc174c2c20c6cbf3e3de1dd1d3e7d69c04d937d65b63bc1122e4eb5b8b79e86f800a1d6852f0294590ab51a6b5e2bfbebcee6e0833302aa7b937a6086805230b42d253d4c9664099aab1b0f8b6285361c1cf7288be8c5361a3e65702e90531a725d71477d8cbb79e7d15b006a62330c822fe6d0dcdc05861c09a3425b3980035302de331b501d886b3a2e7705748cf1a1661c3be01e029602a1e82782244523a878808f0f38b9b9c82f266f427735aa2217d09a921dc80b04e4a84182612a600a7c6729451eaff4ddc071f064f49841d4340253a7385e54886cbfc48eddaf09bb0754422b7203bdd926cc888c9552f11aa502dc151da83ea8e9167c230bf18e8364a65f721094d1fa42ff61ac9bb86d5afe014b972b488bb7c5a94c2488a356271b0c53e328961c984b3443876d986ac24d9190f903fa2282b7f08305aa8c642fbbe15539bc4899c8b7f9b048aad8c277680821811544e81696f9c0da12ffc0be7a5446711c90455209ac41694d930aed0d04fd43b2ba2af46a1d3fc68fcb0bb2ed07adcb083a18b470b1d741c991005d1d564011adc6a9e6640bc6a21e801e60d9fe4a75ceae7348670585f276a9c9093a42b6d7ad365668169729ae119b15b98750b4d388deef777a559ad7092b9bedc4a14a94a350df5502f7531c59c987da817cb477ebd65c31cceb38c819927dcd2fd2422226cc0464d5e6c31da10fcaad2c8c5c20544447d8e94d653d4c74ae1b82334f203a64a0004b388c165b0ffa704e1711050f19a24972361a0e375032d1a7e6919367cecbaabcea7d464a3b5361ce42d0e1e81143e97fbb06b3922ce14e2b52fb2d09db6b4848791041513ae22f85589fb3473fca662640ced1d76b74379afb01dbfced1aa5e150ff23ba92d64e327c72aa56a9888a12ea7e8959eda9523ec2466b1693510a8464d09837a3ab1cb3d3bb13427f94f4c607a8807ff074fb1e657ba4f0e52027aa4a630a5e0b2051d350916e5bd448f4ffacdb66586135b080f6d05324550d2523e23f146e7b47de25ae88d50cc9a83a6d2e6f1c151e2ff8c400afb376702ed70cbf8202dac0f91f1e10ec40fdd8790e3b70134464988dac9fcb690c0d853bfd10e22d495fdb6d4e315fe73a272cd4f564d92b2c4e47b0a0a93167c27135cf01cda0342ab170fc9f8e6b58003b43a4ab5fec10f13c3459fb91b7bcfcea5343e1e09a70cf6af857a6db8306fba130e10be73c84d964a34af0ea6e5b7d59f3a14f1f9926d4b34430c8aa09ced7edb1a1c72ebdf018991a330037bcdf275ba1a3d1743ec71836061be546806b82166a90f5803b3c0da6372fa3735308fc0905024c82b30afdf8be9608eab66634113320dfdb6066762ac03801a9a96b160c09a343db334016a602ac63698822c3d17798ae9d896393531b6089e7fb0fe0f5472f1f027126a0a7b7fdbf184675327d58859c5bee20e8e240bece92c889d62d81833c54064d7f7b33bfd0b321d47ec641188f502c513ecb5bc4805de114d18af0e4b195fbf66cb62a26834a749d05764c03d93984a132739f7081f10de28b8d141e51382eaf47406310e05b0d2ced5ead26e4e994ab244f5ccd5687a5d888e466715ad8f9e89a7369b58d1cd1c54ec2d42b6f87bd482312a8621910e36b2f32f8b288fa56d003e8e1c099cd25e2c407622f2858ddf15d046293862cb683c731ec306237eea6e243fe5903773cffff324444cff0d5a4ad20393cef01231bf7e9b8dce9f377482d283fca4483220046a7eb4e545401df28c21b8f8a1817c89d5c9900d77dbeacb501e7d9cea78b66842ec00adc1c5125e2f6855c61089dea751b8015e301bf7a7a637aa1feece550310d2e8c47d3a48b02d78a6c6730a5ebbf7d1ccccc7b4d16fd330240f7cdf4e8e6a6f6845b2aff4cc1f18c7cb1dff5f708ca455d7ddbeea2d6be7859367a4126812ea427bab80ade40aeea464addc3ff9d9978830a9473291c6ff792290774ce3915992805dc1fc67afd4bc74fa345575edb0b0e52632d765f420a056eb50560d20a093c8fd03cbf2485a977338626a5d3d8f17ce6cfcb5ec49135f33b58936f0ebb660305d792dd157181c422ae0a8c848029a244a6802146a6cc1d421659f7acc4b27fdc5c59d326626ea421ebca09b937332dde91bc0d3ab4a883af847befeb1715a767ee141826a117605d9b2896851cf2f9d5294a166714f5e36a7984747e5886394c3cd1f23372cbe79b3e9f5095ff0142a6b60ca2f6e352187284b0a7b467961d6d617aa99505d633307c551d4799d900d70c2222b0211c1b5c6edf82e24e0173aafa5c198c6ac5a6191388d9733f788664e36225d131ee8c992377b68b1b6b2401385362fda54310d14d8f7555062a92dee13c68e605f583ae89a12b988a95d751e0e215a5026520d86f57393f86d68cf21da448377cf4b7a07d78023f5f2ffd3023e7ca530615eb4a3684549128978bb51048be7c162d69134885438008eba862d6fb6d55d5048296a462a5cef4714c0de2c203aa5dc88719839d864439bb1e2d0a68937c102262477d8c192725d6c15b2a9d8a5217e8ca788fc04330967940646943cadbdee384fde639ce1f720a1e727e33fd400193a814100f1af2215868392ec9aa34032cfd99f9c063cf5e6555cde7bb468ef449584c973422ee26ed413ea942c777b22917fb7348351acf5f0b92249067e2ec4ca38666e4f01afd057416bf9f1dc7cf97ee025c555cbfe58a1d40bbea47a220aa156fd92002542d004f296d893f41184047e494c2effc5006070b5b21d76e6c4ccb2f14dedff5d83754e33ce05ddb658146875b85c0a818fa83562adb6a66c8c62467706d035a320a384fb156fb29991925bd2857977ab79b6fe1c24480a53b4a823bd05ad640792ef8ff8dd837b6ca4a1e64c00eba3ee8fbc830bb3bf9aa212c51ad80208633851121724a3cca8fb4fe3ddad817ab1e4ec74152fdde1e88112817b0f071dadaeea93b30abc51035d9d6b47b48f939d340ce319dbf921954f6f435e6c2b413732b4adb9f4af4c7bb47aede2ee05ae8ea685c06394bc7bed0dcc85c990ef4de289c14b67f5d1044ad6fac39ac4b2c8dc132b7c6d191705e9556997790b93a3a98499e93487adec4f95d08b4f52081bf120e1accc7cf07a846f293070f188184be673d8c5b010d7428af961c1c448af076a33247547778b342eb6b9a28d17882e1bfcee9436195a30a520b55a2b167fc82f98a3819638e854ff14d532e14e69af7bf28b35194626e02a22b77cc3a9148e43318ae9b075aed335d9074622fa5d75d2cf4fce156687ac72a26812490a7d39eff5d20a7edccac7531efa0d01c908cc0687e162e333b9d7c433271ac029faa09b2ac772e1b0bf39629257cea0dd0d43a4f4f8375befa79c7fb7d6bf85928f41657c8e14db7fcd12041183dad761788151aecd8bb2a5eafee9dbdad7f9454cda5c0dc224e94035a192eb3e914dcf16f7940cb087697ed8c2064ff4751bd170087a2135bb5464e73b099120f207f26e8c5860994a54db761d558accaa59774a46973f7bd7593a8d2ebe5a16c60b57d8446823a90c55a7dc1430ecd0c46b8420b3e5dc38fe96f774a91ce8e423ca484cb56a12dc2636e241c8d9b44962ab3988b8283668e87527c4e8b68f43f618699bbba06835391b6ec2234fef240719c5240e81a7203af8324520567cb8451af862fed662ea13ffa2da79f7c5e21d6a178323c9a64b07811d2f0316e4c127d3039b341c1e805df056cdb2d7486137a1ba303f0bd6e572a3502fc13665cc100b42d21771e6ac48a0fffa983944caceb5ff8e17b98b6c7c505851a3ce086da9251c5faf1bd99813a30ebd579517ee2291415e5692bf75d138cdec79f2c3a374d0856296afe4817ee2af8a441a8a4e760dd81e9d8f13bdc25e3214f2e0c60285b8deb302294a61a435951b1943933d736da702ccdffb3504fe2302870c8fd74f1ef10361eb5d8ee7fb043c56130797538e433ab361f1eb57d033404e409efff856d48433bec6aa627ed94fea71cbfb617ba732fbe021d50ef6d63e2ed0e00e0473e96b5674c98fc2bc4b2b3a85b1d0b647867515ae4670400ef75567f7811669920069031cf38f5634ebc800cc6b4f4be0e17351b1932b13b2becd7d99d04ba44f60623f7acadbedc8755d1abeb7550398fb147df58da69eb7e44c476dc80976c10b204ea7eb3d3ca296c8efd91210610addd57b7e4e6002f6e9d39d10d25a960252fb7156c34d887ca3a8176626bac05d138c6ca74957237c8be06135c9783b98f491855b8f1237834426a7cee68658416161db7432ca73b06b564f7214c5d87ffe46474189161e3a5206727e24796f2185226cf51660094dad042214d5a5cc5168a5fa438ea3769b79186f343dd0b9a29474601359289e2d8031311810c44a4886b3661a16ab0fdd4b338d3cf84640b1351e9a49c21a2789705ff831f8a32c5f5c5e965ca014a4a358cdce4d9c058ab51963c98b36a46e64df88ae557e61fda14750f82ce02ba227f87059b2d8428d13dd3881260a372f6ada74cf02ee7d861b9a5ed1885c1fba62f3a83779356fea54182314af8958185409011ac2e02b5360502d752bc05200a611ef86006f0049facfadd33b4043189165817c6f37302b2f0510331a349987bfe4c3d92c455f5748f85ac527e50dbf594016a5f27f60a8ffe0eee68cddf775d6c0e06e3225b9f7de7b0793028902f60238bceefefafe7d2da4d914b1c90993e996a3612da8b8a18b4c8a498614965b1766de9470e6e6e6885cb0638de3ddb1e52b2d9ae307f7e63c570419d2c919ca814d25070f25a7793bb72b486420128daef6f69e78dc2f31eda4b6037f8b65c6b47f3b7552cb7291422d5d187064a74c3a37d7753f6c5d8a1066dd0867d22577879d32eb5cd434c02a11c315a5d3c6f4da776a38247ee8ca84dc2eae66548b3c3724361a33974b66e5a3c9063f1cc14d19198d37dedcad98a7480c8e027e39f48c50e8346bd45c2d99127a2973c204d8cc633acdddc031c22852b191ca199978da9159bb41c87db26831230ce788e985132de929b4b3e3a5234756ddbb74fadddd47b48fd635aebe5ca02983c6a228dedcae9144ca7e5eadca6e9d5a43c91479ce1f1db1a967c1f2b1ecf8a9ff2b0b90d0fab35382eb2a04cd60feab0be4dab588eb2232dccb4151b48a9983069559adb85869dee639f805229503d65bcbb64cd8539d59330dcd601e6b30d7d1ef596badb5bbfb9b5fffcfafb317e51c4cb3fe9c73ce2a307661ced9b5ce39e79cb37e93fc9ae9a1c84e9735ccd7e94b3f72cec1fcf4fafbeeee45f9dbaa37e79cb3670dff84b04077ffec9e757658c71da8637737c9f11b9b9cfc762ab5c663a7c4439066dbce685938d5997df54ea7c28a2d2b63c8e8890944c325e72a8dabc17662cbc884666365e2e9ad6b08080c8d5932e4c423eb065a44bacba6da546a9325b4249c95f1bc29c9c545b9556d8de1ba9a7849b883f9003b95aa81ee77a75235643d06acabebdaf5bbef346ac82dc79d5e5aa2190174ec5143749bdde9935457ec6ad65e3a63f95c29dda64c9e7f42a3fd2ef48bfda75bbcdabec3bef7e9e07fd07ebdd3a81d5ebaaac7084bcb4445546586b495238413e45229c13afdbce4da26cf6d8501477b5d5593b815ae6b06315cd7f524042ba420c4705db3f45aec755dd735a6605d637615b6357343579fc6ce09ececcee02241e064c69a1f90ce781b3169d3cc9457480c00fe3f141988d0dfdd1d2aea03f1feffff8bfddddd93445dc0fbffbfef83bfbb7b522713f0feff9f3c820ce0efee9e247a0578ffff4f220103f077774f56917878ffff4f8a05fddddd936a2000bcffff27e9e00efeeeeeb089b604e8b176b3fb3c3fa00b55901cc123491011e70f561af38528a5691062870f9e92df8b91f729c4d72cd148b48f47c285f892089da34894b8513dbf36860776e92a6812bf4f082fe5f800fa7cc2f84d62931367d48a7881a410627694f94c4565b0d292a242da773edd2529693787c6f5558d5f14c9bc8559957889d151a51202dca9938a8d459b6a0dfd41ded855b71827a3168e970b8a9b43aac97faac4dbb641b7d8b626e6a6423c01459b5a424084b34c202d216bfbe9063a3d4b49414b7b72389a586cd7755f67bb71ab66a1ebbaefbe27c7d624fe9d3a9b50fc64c45044931b1b4a8122cb3d6b9ac8ebd0b23b753215c1a9ce4495fd31b0fdffef008477ba5c71636b59c49ae967677f7fcf503be79c53d8850f910b1a796050941c7fb46ae79cf5e7e8d2ce39e79cf5efd42a7ae476bd2252aa4c9cdf5d6ed7fd87df755db7c9b95dd78d63d7cdc2947785ce28b8fdeed4d904b6e25d953e103cb06eace1f528eb61b602555b189a8a883c6519d83b7215545f75fdb16fcef957537307ccbfae292f3b8e4412644938289aac6933037c6bda7e3b6dd69cb961ef9a4c5b59159cc7e2560e0df4c7bda1dc7ae19d3a955268cf1d5fd2e7be36267eae2bcbd6d5832e399f1262348ac6b1971fbb5367535469e7ae68f19c6c2d466c656470ae58cba2403174f5a77d0164233d474f21386beb41eb39118506e5711a09eed4d9538cba2b0c0da2295a228b68b86b3ab00e57513022528bf15597222fa572fa14478e64e9096bcb213c1b4ed894a9398f7123b1c9c971eba80cf9ad16ccd0ee16e2a72105164630613d69e335fe0f8421b424d490e3f22c7a1ee9c968416985c3f40304766c7fb0ae2052866129216eec945449b6d3943425addbaa1abf1ac36a1cabf1076a3c821aa7a0c630b06b6bf6b2abdbe60c62188202e74c8dcdee94e1763392f7d82b1b3fdbb2ad90f5ccb2ee8c8b3de049909e31cf066fc6e56e46c58eae2e4a87361726f7e45115f70497f4f6c24d45db318ab34306864d6604d46ecd4d4d47da980ead1c4c34281c4a673998ad28fb2133712346662c862886170607a6f645e6c5d575c515b5b5a4656463e889c2b9930543d38461c1d2bbe26105659a2dbdaa9a54534a1a5331a45a44012579a05e6a6abcb43cf5709a6bda41030713d8921349a5e434434652918c1b907a62dc1cb9bc18b988805197431b0e395e8815d970b16c518368c6100e2c8456f808e5a86889b9a16525e80ee82d451a8a1b366cfd58f9248fc81e1846163c369cd0d92131a343a268222f270e67eda6cca6ab06ab84140dd3cc910c514c0a98132f2c255c5848b4ec919063612b9e11bf56b2462855114daa0a520f177b417ac63c1bbca2cb5d51c58eae2e4a87361726f7e45115f70497f4f6c24d45db318ab38308c3461c01b55b7353d39136a6432b07130d4ae3409e199911950191196fdc8cadc088adac84e181c57d41bdcebac0b89e5b4b2d281bc8a79193280ba869c4bab91a016335628589b78cab82529d29c1a69e52cb28a82412caa806d113d03b193db8e940040c07225636c0d3202e0328066721c02e785ab0ac008a02e404461210450034344270f3008c034e959506f018885b00aac0d90958024f0496074099200d30121281008a8d05dc7ce80143408f9501f004880b00eae12c0806c0b3c3b2040a8824311a211201828d2137207b301f7b2b3fbc37ee7d6c1aedc79c2b375daab6060b2357601c84e110ec1afeff9fc33db37580f3cb55f2daf7da547de837d8b46952596ed610483d259f1727da865cbdec8c46f51761809ab8f451fcf111b97e05e49d955cbd5eaa08cf2063d3c799174579376f7f51693f71ee3d0f8938f8944a4f1e3dc923a8a4a70c5e1bdc87ac36aeed3bcd6a6bbe113788fbeff7fac97886e25e34dd5adce1ec6f6b3a70bb6e753bf861a7556e5fdbb7d32ab7265c5d344d474e9ddb6a0cbb6eaada759798ea3b5546937cc4c3925d53d10c5c574f8c289c5d5d0d23768a52307814c760727ea7a02ea6b5a0161b2b284eb7dd6d73c9eebf7e546fffb3ee9c2ab75342a8ea4bbaa632a44645155d6184d28194951c0d253d16365094d1a6b396485563587f1d891b807c73b5f7df4ead9062ed08d41ad1ca6b256dc7e591de80326948dc69adb9716afc6a0c0fd7354dabf9e74b9f7ac2645400ad5129e1689c9aaf8c26f906cd1134314b4dce1a14edbe6d0db7f55503eaf0b75365f4cd4854d5188e9325ee47f00389b8ce2fdfd5b877c48f762fb3ef309daf716d96dd190c03833e28c94eb3ec9a5610184bb0d06fa7556f46cd3e5ba4d974dabeafdfa69f47384ec945d568dd30dddeb87c6f6523ef4eabde985e0ebb9560cb394b3961d917b458d65894c169d1b86daa2a9273ce39e7ecee39e79cb3bbe7cf9e6d445477273971387577ffacb5bb7bfefffc314c2441909d2e61e442ec82ae6a75057a217a5b7400df33d0c34e972f363b523915d6e967f605ea4125f9e58bd57b0660a7cb1724a16156fd9d5b7f8e7f71fe0975acdf0ff1e28bc58dedf8d216ddfa7098aa4f6d78aa4d11a998137361ce7403aadee912e669473fabbeca5262ffab451d6df0a78ed4ad8f16021e4d90e0ed7c5c99ea21eb49f3eca6edf7349f9c3c453c40ae5a8971e1e0e821c485c4660c8bbca8c42af8b7682a909b2000231a040080601406611c0972de62071480071ac268789c342c28243020100585016130100886416150100c1a22a0200e832010e35892dd00bf1c82e5297e43dd4a6a1273e9089680ad343a5e2e0f5f118d1671378a790a58ac884c200a82ae8c347bba93e195a1f1ea343fd5eb5a34201282781fb2128fab0f5069658c442be21b00eaeec43a5d2b803b0eb357c63e6ac6ef9b171433978cafbebbca9e364436f37ebdb648d94d439289c4eaf10a6077106c35cc00568ef7e7b3d5347a23feae67fbd745a3faef7b88405ee1bfe6146176f4f99c0ff9fbf69df6a4aa75b0e7d5b231ca71a998ea9b550dcf96031983a42758fd77035d169401ea79be5ed97a7df49f2954019d3e2f85f0061ed9b39088672c325187f8a515b467a58f395c63ecd0338ff90551b98912a6418181b2a056b4de3afceff41669ea39b2020bc9d4431a658fb42a2e9ecc5568b39ab13ef44148a68d0d7a1e5462bfec0614801650aee6ceccf7c9c34f7698ca42c837d411aeb3290af505438d7957ef277ff12c7c4e449a0c8bd4ab5f1883e49703bc150aeb02d910ca7099836ef8df36a835042942178aabd67124117c5e4bdc3aaf2c045e524ae534ac0d97ca95d5e1c48af0054b73666ad2d42c97e9c548aa5f09635e34d887dcc80e85e2fafa9945a7901770085957aa27cb7a7744724bf25cc9f1678eedaaf936f98d1e69129e0108d4e6409e0480d5ef089681bf80ea190e368b593ce7393d1f4737551b3b68431118b540f64c024321ce2870ac996687cfd083f00426934088c95fdcefca9c51546781fafc271a760e2a7d1834aafcb4ab5fa6a46470b49b2f459905b3b86a686f3bbc8a6aafe100eef9a31f7454c3274c5a0159c2de5e287ae8fccb07836dbacee7a59754b1b72c629dd5687a2cc3c31de1de10d1a9ff9a53d182233d4ac55712984a0026402f18689a88fc575a3611e257858e8caed7a2b0a13680aeb5088cbfc0381b093d561fd34969b2890a8b8ed81aededd6cb381e779d08bc5a75f72a8e9fd4203763f781ceb37550641bd61173ff67446fc0215cb2eb355241e080d39e543479880687ac2fe611c95ba481f524235dadfa65af4545540cdf10d0903ac0d32e545fa66c516773cf1ed557bf7910851b9137a4575ad66dc0c1da09e13fb51fede83c9876e8e46534844184b190879d1f211f372e38eada4f7f59c741f637c8f513e0d66d672b5a27f46e0e7762fa2682e09d37f55e54d7d1caad79b3a491fa5d01c246341b816cd259671e945bafdc2f5778e426c70b589e52ba117195670dbd2b138d935cbb5ad98dd09c476ec41f6df111845e0af0bbb33fca0f724049be482a40dd853b040699beff5b595cf458928b050749ae2821cf6d844f97a91a5f01c4a19017b940bd2a517b23e6d45d5e7854c200bfad88637d721e0586e7df23163cdb139178953136323a7eecf37679dc64c13ca375bae84ea2fa86d33cdcea82e7c435c59ba51d92ecf139a8690f24491139ab0f6191f56c24c6b3c0626a68819677d7c57f89acfd1c479cc73e04adfe8946c00fbfcfe2e638152cbe44630ce876d88d312573520e9e6e27cb1ad35b308e232fd60124db49d3e4ff81f84d0ef96b2538a160b7908ae4a3a09087a08e01483abf33167c399be1141fd5871eebddc97ea2a2d3186aec370fb7781f19c8151eb078d69bd2bda35f530e0ca5fb9c44232142770b0ed543b27409eaf5fb15a478e724da13de91f19cc1618ac0ea81a77361f500f950ca523f79b105e436f4e0d6bb2d1decf126a1001e99e6f15c7c88942f01c4c7e1fe7b04cdc7704d17f21064463dcecd416a771dbc5dc18c1b1d6f573a2475a3c4316169c32551e172859aefe0a2a9218419fdc54ef68f371cef362568bb764dde8763001cf2a4173bfb5348b2c69fccb2f5314a79ebae171091fb626f2ef07de6dec3a0efc4996c5fd693f0fc36c36f893d14141e1833e48555056f37eb26d3395c6de32ec65bd598838a72614a19b1d4a75fef81615449dcf4e2d14b381c9366202ca8067eb958dc6b58c931d725a134fef194fb991bdc0038ca96908d768666b5ab2b5223cd07e7bafa26e64135b80e993a0d5443401290d48802867358ac15fd092832e8a53968d31ec78ca0022d525f86c5e69baaa724442cd8a7c7859947d26035d8c0fe9a5396763aea7fd2e8efc80a83a79a45a5eec6b593b9bc88e94b85f4910e417136ef4627b8090ea4a3ca3b620a66b90e9871ed0c82201a5895de984584481b9f1578614f5c93d6d152913bc60e24c54a9195495f4071cb0a7b6e3028b1eb57aa2c75b82e5caea9e7b43506e1f2366f13e08a1203b4b62da973cb5619383939f2d480285a9419528808e70858f719fada3c3a6e80b29158f7fb61398aab724970c600c5c95ebd36b32bb2338391d458802256a40b4477f6bc626007ce253b678082126af840bc7b9be8f40ef598b2008dca2711290ce597361bc90a0048b7ef069d1e6e5359bf8c0117c9953dacc76e7c718f4e9708de9098724a9b7c3dece8f47841d60e652f2a10e46c4335ddb47dd6a0a6fe3e269c8965267e9c6c235bded7e20594a13b8e4858a275126414c1966b6735c6bfd7af8f24d04c7617e6d17c08e1e4041983a9fe08537791fe45eb69705ed338a8325e17f16f1307e6702ffc0f91cf391dc9dfa00de32a51b640f786bbd02f39a895e7c811816ced38dc202d0e3a7f0870f291d01e0ec73c4238681f8c8023f2769f6f38779637e853a85b1cc8631222884517f5f045bca402ee23ac485ab91841e230f2ed2eba254841be7d68e1176d6fc2d998e3de82161b0c27491734c28ea231c0084aa6a884c055e0148e30aacb0283a44c0bb491c071e971b3215ea7c4b38ddf31eee178ff78a0636d8ea91f0f32dec498bcb140c75b8cc98758a413ef624cdf58a0f126c7f48d0532de654cdf58a4e34d8ee98f0532dea4c4e4552cd0f12e63fac6221def624c7f2cd2f126c7e48d051a6f724cdf5820166fb262f2c7828e3739267f2cd2f11663f2c602196f724c6f2cd0f12ec7f4492cd288b71c933716c9789363fa63918c3731a67f2ce8789363f2c7221d6f3126ef62914ebc8b317d6381c69b1cd33716c87897317d63918e3739a63f16c87893129357b140c7bb8ce91b8b74bc8b31fdb148c79b1c93371668bcc9317d638158bcc98ac91f0b3adee498fcb148c75b8cc91b0b64bcc931bdb140c7bb1cd327b14823de724cde5824e34d8ee98f4532dec498f63cc6838d3f19f331367dfc1a631f2cb6dfe2818cfb1ca33f1ed071cf31f2e31119f719a3371e90711f63f4e311b1b8cf8a911f0fe8b8cd31faf1808cdb9043af9e87c698118dc139e6717cb97124633dc6a9bb38a412eb314e7d1c92b19ee3d48d431a6b394efc38a4633de3c48f6332d6729cfa3824106b7971e2c63119eb394e6e1cd3b116e3c48f231d6b394ec1cb55885d1d06dce54ae9ba7519b0e7e8bf7e95a39f9a1fc08707418d51799c67163b7ac5a2383ef238878f8f3e2ec6b1318d396cfcb071111c8f698ce3e3271f8b636c48e33c2e871d2f74de8769638c42c69f8d8b617c9cc63c3e7e6c4c1cc7c334c6f3f1c3c6a23836a4710e8fcf563131447c94c75c3676d8b818c6c334c6f3f1938fc53036e4711e1f1f6d4c84f1511af3d9d8e91717838ac739879fb605c659aec7a93836e6388f8dcf3626c6f1518eb99ee1263cec7c053416b6d016585a5e17422d7f6fcbfdd481fff8234e2968c4038b45ae0968c64e30bc33d3abf9e7954106104d21f6ce72fb4d1788e6105665bede36f0416f75e6c6020e4cbec87973c86187a8f81fd06f978a43341dc643759943c50f1ce849125be4f6012bdd44878edb9e726f543e5995d5c48f4d6821f54e6bd704acff791666f544426ae446ead8c1362d16289bf4c2291c38d88ac241ad97973dccaa24e6e5f8232e9d3c7507fc203d8b39eb1466ee5d51d7ed6e9e943fb39eb9e3cbd3f2c7d073b7f94f43824fdda98facb36eb87944fe3265de2df9bdcb7fda4e7d243d778fcfa1f29f29e6dd79f944fe31f48c9b3f8f883f869c73c7cf51f9c79033eefc39227f1872ce0d17559e07fcd421c37dc8c3809f3e44b80dd810f452fd8452b110e6a8abb22fad70a37f9f4969fdbc88f734b54f20f4a7a4291f0e7cca7eedc46ba7f2d60aa70a62fa1f1caad6e3ddfe79bb7bbed9c5f9763f87fbfde976ff78b77fbeed1e6f768f377b38df76eabcd9d7f96e57e79b3d9ceff6cf37b8b3ff78531321aabb8150cda204b1e713c06a292fe846b7ca56f88bcabe62c1893e1475488c91d8bb23d989308c07fb00ed29850f53103a0a3b292dbb947d8227e93111be84abd229b564bc416d5c94163b8e01e77979ef32e6fc541bce36bd2ea69b2489afc84a2d4a5ada5f6e8ad8db9bb4c4832ed6b900bed920a2640a794b7db55d2d861645551373d745b482edc85b886cd8dba291de6ebdfa6e6483ed6b9f0bc8a47e56d3100e381729fb04a1a2078c46e5c0e557cbb15550daf2f17ddb86c8924d0c5c4255074cdf49f867437f80dbc07eb757b5e725a0bfd51a81a2a867537d20497adc32294620cb4a157866037f1f43be01fc24f08282c93662260d7c226f5839f9322991668ac355eb06c231cbda692b26aeaaee1169b6ab00fcdc115af75124031e2e5d33f2f717aba10ede1c19686184ce127e881c82979514742fa36af8f964ed6228876ec04164bd122f7d84ab0fa9b616359010d4132639237c8f0c3c57f1e15314accb87f96a495872632e460a3d4ed41a750cd74558b8fafcb1b92536dd148dc7b2db0eed52140a7fca71660cb0b92b46ac95bc5d6b46a21fa6e10caa206eb4db114faa91d5ab6dda7ab36c6d24b0e2e52f97a5586c64e7bf72794296f5cafbe48bc788465c04f7dc8972d9ad5950c57a3082fe490e1a58160203e072edb285cc495af9fd753a06f030c4c8e107079bb8adc32e3d8786ce42da36ee3df04156c7c0b34347a9274f914a2699887233874db1bf895f2b69525acf9e268b160c2ecfac513dcf902f127b9074434516f740b6bd3206d6ac80c525c61159c1604bcd0ae3d1907a348b4f9ab078218dd836877d1599b59c7319af5b5f0f411c3a5cda25531ea497e687ecf5863483441aa0f37948f52bd0c9d5d57532c6e9a69715ecffea7ab25d920aa8ab91d4ba7a5ece4147a524f229c5623503c2721efe006fdc200b7eba14609e74644fc5c45b0feeb2365845ebefa25bfdd3396ea73f37d55eb8e9706eee706779f5ef2013ca28ce03cd7dd698f3de2a6b81cae70030e977762a116d56934653c5735c72ceca071ab4b2f12228f78e109191b22a86341d0aded34cf87711de789289fed1044f5d818ab0eb074c47b5b2fab146fb8da753bdee4ece1bdb32efdcc672b809b3a6bd20f4249dcf1caf6f4baa6f638284541560d882ef12c71321a33ac8f6a192c79d92f855c968aff6bef9eef86667468fa0b4581b9df71e7ec3ce0e53a8547d8630ee4fc21eb37d347ec97aba7dacc78354cd25bbbbc13209311e2ac0ea678391d63c61ba7f0a69e237d23f5072f7db2dc7c2aacc0cacff74d9cdd0a2ede2f34b7e3f2e00e63fb585866b0ec938bb8019d282d1ff9a1b5dbd7aa2d4713bf285c54dd5c61e23f9292bb86eb447a527e6580ae90a7c9bea0f431d776710a673a1e125b81d264eac620cfec478e21dc272cbd68c73e3d0d77f1d87edd9e21da373d4a0903a1a433d9f2c588038c6804a117a4bd94da955fa49091cf8ccaa12c0e18b6e471041071841d530080ea1856b98aad0df4a068ec0a19f0485976d2996fa6314a82e783d773978eb40a877d14b771edb94523463894b388e61d9c6f292bcc307f596538191e014c6366a3c0145cbd9b551d4d9b75451f248548b8908a9430b60aa65e95e624149a718357510ad45ca8b630e9a266400f6f0320596958c7589e7d811a0a8647208de22d3dc3c7622d2ddafe4632b464952f5b58a7602c6212f384bb119eabcb994d51c383d671e8b190bfe96ea054ae091f070d5fe3cfaff5a5b3d4c4f0c8e9d9cc687014abeb0f9de72e16ad2dd660e22c7088a2906e23409d09f40709b8820d175360d74e56a7be1a58141510dfc17113baa9b72352476719edff49ef602ebdc9c333a9d70f4b3781e5cd0ed927da709e048743c8c7abe94f75cd589247a6159219c1e0287db8b56d0282e3fa287ab6ef515caa17b6eeac2e57eaf7bb894217a9bc176ea84747e89f518a7ca4defd042cccfb8ab8b6ee8881f06b19a0f2177e5fa36a9e511c6f4ea9deeec0f0fa308c737dcc53509e34364f5a0e5a7fb8cfec9c3a99ac41781c371085ed8da43091486572acdb3da5e043d6fd7b659ccc5568c69a71c2be150a0b7ac11674526cacafc9fe475d8071d02f985e6a528d6cbebff971149af8518938e46bda9fb16acd7db8f8e058b2bc1f848a852ee6c85e68e26dafd20a4716846f80a1983806aaed98744fa256a83809c13828ec835dd3ee52f18c9834c934806b9c76d6fe7f4e65b4bb0e6b407b51775771af1c3347daa06e4e9772b75dd90c27560d5b93b57d08d232d42afcb92e8fe532782ed74bf8fd9d0bcc0f3a4c884b9eea246f482bbef647b7bd12a9ffa7d9db0c62131e82b40d0a50851e4132ccaca51c66142abab37ffe3547ff1403bfe199c3501798530822f21577b4d322c0d94043164e06b31be5e8f0cfe4ddd98c9af82964cbe152a579768cab524a9b472cdd932688ecea4a9576bf6b28700e80f522f4637af848f40e81759b547979e6624180031681a0b7d099123883fdb93fa20e220d6efd481524e1079ed31bdf9b03b04b17d4cae007d1d155f2c8564e94c046f8c0489e11ce6625496864d33b675c2ef60a0cf5ad9fca86153dc8b7e6937b3ba2554d21c579f65c659811d4e530c71cf614754f1f6f34098ffc12774a11a673c0794c18aa9d59685d37cee734812245ee88531bc33b113ae6c4972468873e623c3c6969a042e40993bcd6fa1011f597c0f7229d1fc3bed0e7d498603a9ff678c8b1f1360db2f68e1a582082f7468e4846f20a1cd5ec99786c456087d6db18d1693cbbba3dafa0a3fd141251fdd4c0147dde5a809ba9c00ad7e9d52ea25e168296e74b3bc97bf6e1fb972694f88b17933ba4da4575f446536b2ec5bb0b5d41f92840432170d1149311c4d6cca3a4709888b2f59cb3826bdb2b7e49f6dfa1091ea616ceaaf25b099f6a02ba7ed2f26d748affa5efce6061ce19567056d5b30f0f0ebc1968fc6c07b782513dadc59e4f2d09dfd2194cedd42cbe862dbddb755bf0088c952a486fa8321599114bb4349f765a9d12d613d9d6376a01136fcd00a4f666b84b8ddfd8a56bcd8b852c14cf835605d9ac3e94f95473449d88be8adecee3a39320a5150d560f7f79f57ae1df056a0d24591c7e65af363361717c1df9e4f96f6a2e647b1ed36d7e0fc3008da725a5ceaf2133961b31780bb61dea81677cd4d4a98909774b322b005b7d69cddf863f9e26fb43678e2b68148cf4bf259e88a9bc3c096bdf59e54453dce7f40c523b14fdd2c2555f12ccdd12eb4fc4a1636cc9af03a4f4fc209820661e97243e354e84d5d28c33cb4244111758343dcbc3d96018f355f58b3412e9916e2e6c80b69f7a39c4604c607de561de59cf91adfab146fe562b175a170640f03ecdf30c8fd392b98e00036e064104b3efd94e1e377c72e4489055f581b6cca40bd1f6309da95f236583e18a01e9fdeb881dc855027e6efe61cca9ef100b6e1431ac0f4eb552a4946816f1cebb07cf9e0bd300a4352356876051bd00364a6ec385d615398b90a6544ab8d5b149cd972de044f4579dae4be0dd5c3d288a119152e6f01be28ba932ee3304d94107907bfde192f65d233f00636b59385b785eaa531f2dd2a02cfebcedc163d8c5457517c924b7163466faff59dc910b6b628ac00646bc0a4e6d2a77a0ae4cfa687a63f4475b802442f02dad4c55f5b300cda4a07b61019f13fb60a72d255f7ab24923115bd8885bb860bf9331b0b5b26e902d2fa58e1d24701c35d45f6c1c387dbe9a500c0703e5efda1b0ee5bd50b68e3b8002fe755bfdd4a262612328c6adaff9b6c92a44c524ab96531062006a105329e1e66dc4869bd69e7b6bbb5d6e65cbf76cc096cba6a8372fc720298ac93e39713b2c0904495a5ab301f80ba02d50023715468e226891434a1400518c711b19d771390965ffb7939f250040570bee89dc6c525c11521439bf3f3078c404c0884a84a92250837c4cab31c8dd77df800938f2a38545aa8daa2a3819429122451024b952338214a42a0a3c4c98a15430ce132a78e879d1f6bed6d69f93ac66379b2b4bb8fc0c9f3621a77bf374d98295cbc1c01c2c3900fa0508122d25071210437128aa410021f3eae7038d2054a109d24e8cb0a5364d8189155a49f7a808e00c17292850a0a4590f850994c65b450e1e1d48409643852bb76b361d202d30a566068a7274e881872c4d38f32261859f3a4a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a9a969ca952a54a65cb5f9b1a6e98bb79e6005ea5ab9c1ad384932569864af87949ddddd72ab23613e908832e6881b0e52ba48afb9527870234416094dc20069a151a8720549ed5e7088f6dfd2f275cc87ca6c35eccad774d7eaa11d3f1d153c688e6cf95c2e57bf78acbbb7a66e6c7268a28a9922609a48a7c2992843385145c5ca6ac89b96bd48b8acb5d65a6badb5b6db5a6b6d775b6badb5b6bbadb57dd5ddddddd6da2b21eeeebe84bda113725fd65a6badb5d65adbddf6afb5b6bbadb5d65adbdd56886d97f7c13aef87981ab84283171e940809c2494877aed7bcf2dcdd6d7b5d571cdc005912c50b41ccf4092ed79114623e54e691225a0dc553d1b4b5eeeef6aaddddd7b8bb7b09da00298108225496c0baf2eef6f0e6edb6d67610b9bbbbdb5adb3d2df9f51a155cecd678ad351f84b191058909440431c289153a8805dc3ce90145143fcc406910145902c495d0d5de7a746e3f27adefdb09ddad5d6bad5fdba7eb96a77e35ea4ea96ef03643bd4db1f41be2dedab5d61ad7dcdb15ecdb95e668bcf7deee6ed76c7bebe1da004767aa56b97632b7373ad76b57af57db76ca19aff783290589920228dcb80024053f7c600ded726d5bebc50988a5272b3af86813a8dcae6d36defdf99436addaffc1bce9dbefdab6edbeb26ded29705cf646285afb033996f2dc7bedbd57767d9a9f0c1b629845ee2d47de5b87cc2be65a7b6fadb532c97232430d50a250907499f2dd4295c2b60d3ef8fc211a991b9a7c73f8b5980a4964c7304258a0a81c8f03cefc0001969123584a926a9730425da6ec77d1d23350274a9c6882690728584a74097d9fc8c10b10355faa80e08424db40b29daa92ed92aede28ab406123bd5332c03085021a01214c34c4c9d20585139a043639d54e4571efbdd7f60fd1ac90f0455bbbbbbbdbdaae915e1dd2d028b5eb5d2b87dbdd7bb9d80f767590b5e3f5226bfd60d5b6952fb2526badb5b6bbadb5d6daeeb6d65a6bbbdb56db61649d5d6badddddddb5d67adb0a0fb26fbd56c6c85a6badd74a19aa27b3052b7b8e64acdc608111308ebbd77256287841014b9919e694e2377c3b74163259acea90dae55aae2ec9101d18aedb5551d175921f9d05d67ba2f5e28989eba24bb86ddb3eade96e00be364a68093c771e6072886abdf6694b478bb75bd74e795790e4c9f5ecb5f7260941bd4f3178ddd6dad6a109152024710342cb09aa941992f4c30626d96d80e36dfb094aa7c5c2f240c3d49404d3102c74f161439adb4f26647f3ae486a713fad8291a89112e6b77d09945cc2b9de552c576b79d1d44875370b9622f921d785aeff7babb6be9dcddbde5eeee8ea40697bbbbb79a5448754034764858709953c7c3ce8f93962fc6a32d5d41bf389d6c68350c34e2dec530d4ddd15ea7a8aed5433bba130a3a2a92bc71a71f1e8fb5d6dadb7ae3ea178f7577efaebdd65ab044cd0c6590608284373dded029195af62229d2eef17e497cc89ee29ef95234179fcf7e46a63c9d243958da2914b9a1cb423f232fbc723e23495c9ed1136ad4dc9065881858681e6d8584ef5a23423a17758f3d01450990156ef8c1a597744893c2ca080fcfdddd5bf72a4565f550829a263ffca025f670648c5feb44839e07b6d4ecf8b796bc7b1e999267db31af7b3fbf6e7b6a85a5ad1baf6b7677b7bdf6f68c72bd79fa83c51fae8a707ac2298d550f495de3e44ee1c8abdbca894dee9edccd440487a719be643901c72a8512b258795b7b9ba072777777dfeee7696bfb6f77bbbb7bd7b66988d7d473bfd7aaa987e7d35a2551e17dad6db736df4ff1c5a1833b6ab90518254de8edd077cf028734e3971e8ed6f773de8d21904a9ad0fd9e6f3004c12869f8a9a4611001ffb0ee71e7df5d1c61df712eebe108edfb53983f188a705d30d86d0a863d0fc2cc30490b0283bee7a57743e6a087e12030a4b0fe15b08f5fcf37ec7b60b047929d4f2b47be6f5fb7cfa777a33f7c7d63c7d9bff55276d315d6b27d7a5de4fb2e68b4a1a1cff773cb72cbf29dcd6a0543184fee9f7bbf7b86a3a4c9c08a8b5e260343faddd72fc251125530b4dfe5c816efa871e086cfb76ef8bc74f960ee63b3a1998cf36cf8ffbcf46eb8ec65f8c3f78a6933ece207f40f6931e75c3e5887bf7cf93e36c21cb93f4ec600375d9c97801f1b32b0487a3566de063fdcc77e7a37b88fc9be5693e1231a3190fbf0a8dfc62a9981be71b8a3d64d978fdc808ba38152aeb1b7514103bc52e6bc1ab3b621fbfad2bb21fbfa9c37fbe8fdcc3012100e3fb7ecc3787b6a65f877d4b2fcee7f700c7370e5a3a0dfbc0343a52c9fcb3e3866897d62f6c1adefc12a30f7eda8650ee07084acdcfd0b873972f79c7743456e7d4b495bbce559f4f37b6a1ac5f8f97d9b46b69fdf55d3c8e8e7b7559bc9f3633e559f5cca311be1794612cdb7e1792589e6c7c0138c249a5f842795249aef61f926559ef2cd753a3589e6cf9f533f71f82e952f9c51794291b449a5a0a48d52a9fc93237f0ea9149dba32ca1121723b185ecfe7cbf77ea352b12fe8545e2563cc27f3f9cdab3af9661a4959b292e7d32d79fe9b69149396f29c4c6ffc66d5fef5fbad7f767f487924ad4bd40d53e6f41b5595b48513cbada46dbe7c72aa8945256ddf83f12b04cccb1444daeefb0d2b8754ea96fda9d4349a4770444ce4f9d5c813ae509236ffc355075726554ad2aa0bd728754a9d92b4f9f38838e3fefcc9261a1509911d0ca994224c61884452f14548a5e8d4348a5cac9696967824cda752d216a3485b1463252bcfa753a48dc352359f5e495bccf3291a698b2a2485144c9e358a9818d4e4909ac9b36f39f480430ea99a3cdf6f73136b1ad5aaa410840a08bce0909504763e7ca18dcaf3432a95e7dba81942a63f25b62f9fe338777f1b31fb2be58bc39bef370e3b80ef4731566ef5a337b17c8b9572631066ae20978683219f62a419bd1af25d17c80d604d262986061f93c90718ce4412c020250054be147c656020823d28c63031732276301383c2bde8a0654513b7eaa372926fdc2255e4134113415f0810ad58e54b6950712205831a9c40a9a8471f0d4e48f0f162c4f964889e174d9a60305bc4c9644c9320f9d2a1af8525f2f568712a79010c0ba14f05193e3ebd2024e89b4e4cea2c162923a3c181be3984490db218848993939fcf042762710271c1cc8a6ffa70c10a2cce251798f4eb8bc2052177e7092582c290f04a71822551dc7c5f9b25251011b1a3f3559dc9894bb4604201cab8f72561c4edd84479f91b5a1fd6147ba30958504193444499d0b3836b1a210070bf8ae696ef9440b0b064267b6001884cc9130db8f24504416476a852c5890f4b50d031d49a94bd020c224c1715b0e2b060e4c9971d2078e2171e4faa0c39a10423f092641b866cad1617134ef24e8e639870fa3cde96b2d65ebfb4e658d546051ce809b8541f525a9b33edbd368dd75a7fb05e8e3a342dd14eb070dc7f3264b8dc3c998047561622535a2a1493a649aa06a813dab4139ae39826d2d4da3a71adb7b737c9eaeefa6dbdf534eeb6eb1fa27d07e37e88f6b54bc25baefd5ed8f7b1d0691c76a99250e15e9e8f07e6395225512487df5735b5da4da4a1311ec0872828f438c2829408ba490c63a878be1b4144a9e0468816bcc47941cb8eed4f00d1ca1521719228c11a11846112439315668830bde1c2850a4ad393226e3941b4b172c41236b82963e542937c6f4f7cb44f132e3fd04005cd0f2592ee92314f4c001aa2c17002af8637b5b650c5d43e3cb73647090b4aadb576a7bdddde1cbeb868b700c7c7998815b3da441127d7e781d587c09b289488a2883be60b038e18a99db7fc5620544778b80e132408636d678652a7b532d9952395930e57750c8f5b97af1cc19c408a128efb2f852b48965000450f6ce0a0896a16385c8412584dac902c612d5122091f7898a9420246898f5b8aa06b00362b4c497531228725bd828f1472bef89082b5b7db7bef971e700232627c7881ed4322a30ff1d1872b647c9862c6878ec1875cbef62551ff0cdc2f03f71fadc8fd4638f7db625cd0b3cbddd041a3a507f7fead1d714b9e7dcbc3a183460b8f3cfbe8d5988151f9c32e4973b7512fec13e6ab7fbfbf7ecbe340291735976bb1ef07bfb3b1ef62600c74505a4a2918da977dc5f46bbfcbe340297783f76a607ff64853e21f2ac3dc370341fe7cf466de0631cf5a1e8e02c4891327ff7cf47e40cfe1a3f90339900339907f055e817f94512a78bf4da3a3ac231e34b41d2b7f8739ec4df68799c9fe2dd0b4c06a91d5e27ddc7bbcc77b7afcfdfdfd9f088ce9e396307e07ef07e11efa8bbbef5a0efd8626cf1f000e2d919062c9f30580c3a38c71181f87b107875c0b580540ce7d9d62e8a3d7d7c321d14021210fa5a0f985c327f4449c57238789a4cdd931e9a22ec077a986ef0f18f80691b4f916e00ad4f20b50078b77f1b1d9b9c0f4bbeeca63ae402d8739588439b27d2419d6324863e1028716dc51cb3fcfc20566f1deee370281460c16a929894556f96daf01323e7a1f1c3d929440426fbfb39ce7b403a1b7a02b7eb117c23a662ff4dd4c8883ca21d1ef88564242b2d7317b7f22d977b2658f245bc6f9133dd29c09fdec46301ac42a22a11917e368063eb0fdd12b25214d7a8463d2c521e55c324cb37c9a2e44a7d78150074242dff4419059e839aed512a25906f58343e8fb853ee64ec8aff775a147921ca699521af391d0108ed9d6187a24f9b5becf24548c211dfa1b2ae56be502914cdfbf8b0ef8a2982729c8437f8bb02d4a3e55a3186fb3bded7ed7b7e5eabc1ab31fc23a643ff447475ff44a499d0cebb03ff4b24def08452052d5c836e1d06b40295b07633e613ed92673af43f6feddb74cc675d6dfbe0d7f6b5f22c9da2ec3dcd0cb3097fd8ca481f63b0bca70cc38baa617d377f0e7867d7f1bf61d0c5bd6479af209874fc704ea7f8e968b39f71ca594d600b2217bf9453826551cb30c8c035bf78ba5d7417dffe97550df82a192b7e5bb5fbf6bed9fcd66b57a38b8a1a7b9717fc770cc94e618e6b084e26e0c3d47a9a51ffb2e46e593ffc5210d2177cf9ec31c97671804d9c1d0738f2463b19806feb45f5a0ee4b8d0c6c0221cbd0de8657870f4b68fb936a62fa34090b93e109650f539cf467df9142a35a6fdfd5cce51b273dc60287148f3c5a10841f27da4d93de55c47b8e8a3e7818c37fae8795004fe7870f4455f048650f447e008f7eacb0b519264107aa524db1be19ffb461f93ba0e0c2b16f8e34117f97732704cea22fce34f3b70594ae250e8b185cc9ba4ee6b608073301421880c1c938a861e4936b843b6b1e19f0f8a3ec6c7004588f145e08c3289e6c7a48b3fb0bd112842d1dbc0393589e6779c962f7a38383839e7eb3897f745af468e893589e6cc6a93e70f01806829cf17fa91e7d398f27c1641f2fc1f22797e8c3889421a95e7bfb67cf2498d94b485542acafc7042595d495b68a3a0a4cdf51d9329d2a6f33972beac92605c9fbf949251a6d10e9118f58389b47150a8a495b4c5504e495ba46213e485a45062f1a8d848c9a1bccaf3addff8c9709b8225cb2735d2d62d5d18be16f6efb822be28e6ca994cafa2a7817feb3b97c778db1bfdd13dc246d88663e022dc3454cab40b72d9d672f7b17bddbe0b86ddefa8e556eb77a2dcbe21a478c73ba255febe1c9deedbb76f77778ef739dd23c9eeeeee5a2d26499bf787fbbdd6dadbf832f1e40ea2c395a7db08e0b28c9fcfcbef0e7ad83f4ca8c5838f59e8a0ef4fb1ec1d739d745d6c632f1bd3efa8eb624bc3dabddf0783fd87b46cedd3fb1487b5dc3f6f83823c3f9c67433ee53e7a33f9e127ab3fc330194cc6ddfe180e29072d5e7e0cfd1bf409a3a235e437b7f2aa8843f6fed465b469d3a6ddf21d779c770025a941a5df4917c51e29f873837e056f8382b482e9a23580a2941ffbf833fb2a41207c256d7e05d815ded8cf0ca40e86393217d27f758b97813b6a2a7e7a1db4f80675b47809ea180a79e78376c0b692b40e021bcceb3930f3ebf5e2e20b19f41d27bd0d5e60065efd5c84d14f4390de8d9983be93ae17e680ccafefa8ab861ea0de0d9a839e7a38667e7dc7c9c9cd9c9dbc036260e61718e38b0e0265834e66bcfc8692b60bd63692267f06d89c6b054ef14118e86758f62f1cfb1ddcfdc4f6613d85c3f25d29ff2bd578f9c2b92497c0f066cfeb014358f6e692832f695e58cb1ed87aa4b9e28170d0cbf0ec63f8f51dde798be7234d152d15366a7903c981b4f1bcfc1cd2c65d9ee7a99a44f279c0eb421ac524f9d3f3aabe8e31c6fa14c3c20bd6f96332499a97e7527c21bf8b31857fd8dd66a42dac59ee62ace477555b5d5d9d91364e7eb7b14bfd264bf9ed354bdae4d736722ecd8e73c9bf385320bef0ba7c395f3f8c58d8eb2702e098e4b4acf317efa8d97e3a03cbf8b692afef3cdc56503e0f031a2f23c52b2551f05e89a59a2fc5bbf0062a3e87b4b582be9fe642da5e5fb338f8e99ff14a2faa8f3ee7d9a060109e4c3cc8e0054e289226e7520b0c7fc67760087b0f0c3f0b66c90067803049c32dc3de6df43808346bcdbea56234aff8771c46acaa9d1a8d0f699f6f013efcf2cd7fe967cc2212f923bdc8fe058891fd099041f6c7804bf6a721030381bbe845cf03c3fea00f6159039a3b90c617617f2bdbd72c088735fbcf576bf6b40d627e813b22569e7de7e170249c48c9b31908845f4acac0f0fa7c4cfe0accf33b383cca1794586a244d7e0f0e793cef795e05f6bce7011b8ca4751ef30c1c932c0e419034f0cf073444c8e0697c4ceaa48b003826755dac7dfe1d75b9f0069226ab6b07d3aeb2fd5dcbe53dc59e05bbe9e1b86f5f7a38ee7b20f6b67cf45bfddebdffeb85b98fbea8bb833f38fafd3d5b751d6e30b7cfbf4ad2e4945d256d34cb6f30d2d6491b9765ec1004894317d93ec517874a235065ff8efacd91fd46e53b4256f6bf29e8c592ad578b17745062499a946a32c0333e47777737187adfe5782e13be9c9c479258d266a59497029348cbe7d1f340061aaff314878d44965fcc1fd851922675704389e0bb1e8eabe3823ffef743ce410db8fc92269f7ebf77e5fbf0638ccf012ed3afd4fb8b63f630f7b9e7d56b0619fc8c9f9141f56e1000c7a40c1adc51cb3919d8a839f52fa55d58bbf7fb60b05f9a44f2af9725ecaaae7af98d4467f5bddf1776559575e17eda51fb00031a3fe39124062dcc230363f01d3803833f1ecc08c1b738011125298419afa49464bf7ef46ed468e0aed6ef380c704c9a51cb170c7364da504fd3884a22aea33c2a47a1113a2523c98da04b0ef5e8d3efe287f7fbf0b31fd2327d0a8639e28e8e4ed479f939a6d17bae1d295380e1bd20078630f91469eac4f88dc311b8d7cbe9758239a0e70243987760f8136c81212dbf709444f13bce4b40b46f63823caf94a52bdaf09fcf7937fce74bafd6f8b1ebc749dff1c3bc2b5ff859d622d18c5f3fcbf859f7a98b43a18b022c9ef36ad09ec5dfe8fd2049f78f88480132a9e821206d177cbf04a4adf6fd14d8216d30beff02207440daecf787206df45b7e3ff743dab8efe798643ffb06bf9bd16c69a653d6fe64e570460df7750e4094607d8ac3226d5c54ee175c8dcd5a2ef71600e716b3dfeefd3e18ec6935976b862d781966f115d39ec3432f03f0160bbd63a2bf18000b904945a08d9a442d7c5c0ea9547d9a46f269126159f0f32b13691bfaf93588b4c1b859f44d0643ee69c9820feb549d926758a1e4f96195caf3c36a2413994651562669bbe0670326d14413d6e21b9ad2bdf97e9ffd17d517f6e73c85f1ed760049c0acc93078328cbf1e8e012401534486616158b0b1d70f3fa47d1c95f277795a5ce4fed973607825187e140c61f965167c6316ef38e61996e11db56c01c87d2ffe0e915f7cf47e400d58bc8b4792419fe2ed92b4f9fcb04cd2267fbe0d226db39f02785a0546da6456d2f6736546da5e67a4ad4acda751e8143ac5b5de43c2df7f007f3d288c5bc2b825dba20f0394c3a8148c9a2972d42963330000000000a3170020180c060644b2288b6214c852ee0714800953da94a8a84c1a898523711446511885301c053110003008600052ce38049d34010057347ba815023f10ae6e8b87a07f4a9868966e5a70903c493e80d7c7d59db728457a9a3be9335a4e49152614f64c03bb874dd022d470e98683a6e0ab54f78cfc7df654b74e243769ac2bf0849582ca47c352b3e4e8d3576f0531b7b62d2eaf3a8936c38da08c796cb3fe947c60276891b1c45f88129bc71bf170a0fd8f4bf6fe7928adc34f64549905d18d463bd30b80b2ebfa57c65b8a1653ee99cc55951e77ee41956875554b975e3ef49ee6ac071c82698386df0ce2005827417e20a27b8c9bc5bc23048a5b757aa2489874129c03d625748c4be36eff9b9980cd6dce8183e6b79ef4da8a95d7867ae6dd5e804a024d06dce647002041a6ebf14ef63d26e19c43d56de0ce1515dcdfa5b5b02445049ebda045baa3199d5847dc69aa55417cc700e3a7e504911a4e996ea7daba07f1189cc2596f89efa88858073aef801bed376dc79ef4189a9808e0bf5c3ad72cc20f8fec15f6d26a795ed8223629feccd7f324fe46452d8bf1cd00a969891131a0b7555db20c6e3fa8b984384d4e5fd3a8f4ab4cfe2a8678f6658ebb553977b422ac50005ce2bc9a4bdcb07207ef55a65b5ae62ed2c33b2a72b9752835f5192f36d1753257d9c20e3608dc860a21846aa3b78c0d930e7a7e6e964c94afe3bafe38b511e8cc36ad77821245f4ffaaca68547d703c8d78fc43489d290ff58a8db6f7548ff1331dec24e8adceaf0690c8c9fa88cfdfe71e3daaaa4e1914d3a59f23cb8a7fa4afd423a82db6240d7ec93edfd946d7a2e4ec3bb6e22f6bd452d12558962bf37c7b96e3ef865c3bfd368dceda75bdd6e48e688abc5d445d4a99d8587d9205cfac631ec72580d4f476e54e8e791908754a36bab29464067b980a23cb40e3293ca9cb4a850b34af970224fece4301a790e771b15c20c66dfcef6795ee97b86ed51a34c5d35a62e5a1ae3c8a6f146a34c0168ba79ec1f1713b009e71fabb8e1ac7d3f647a6cf26de5f5e9d5b9888ebaf6369bf67b817bc255d82263fd16b9f0cbdfe4c3aa01956607c5dae3cd34d95b21f6e4a1617146ebb87909084bdd002d54e935d6a18b882eff71c349afc89bbd8c22bb60c3f03023b30c66acdac1dc0269fc44c9a20f0ccec54e8aa813ee93eca33478190fc614d0d3217952dbc4d54f1ae5c6ed424f811ed249eac452137d8d503e1aaa49d566f5ef15bc95d14540f992868e55eae06ba241eff62eeb5b0e680baaf085c7c4458060e9e494912952f0c9c14fdedc503433190464688f0e8c2a97de7de781fbd864b9d953a2d76a486ab1ebf740abf8e2b85edc711ddd4af52be3a039817140674e708586cfccadb4a9e2594b36f90ad8c441ae461e4268202fa6f174d9582649f7bbb094244e8d1e2c5ccb490e8ec30c79992a4db96fe765a45f530885d916bd10ec0e23710f6caed69d782f3424d56f5d9235695a587940f5d0cb4e13e13462addd6e32f2089c4f187320e16c0c629643256cb07e36be40e0f727ca708eb017558a56bb9c9bd4ec769ca21a5bd0f06012c0977d6967ca89bbb95ed07fb9a024bc2ea6fbfbd15f81b3b707b2643a1e69b0b696667441c753e1713fd313451d787c572457291a22058b93145351f7d0562ee03669a6fc6ac1930033de1f98eb1834727ee2d4ef7649379f7f03167261daf3ca6c112adf38a58a9b277a9eb07e40c456b9fc1d95e955ce37035d69040095c3366a8033bfa54dc270bb781b20e9960f0fe173e3f7ccd3a871384938991e66eec1a168576a4b50fb7f6d6169f66cf5469b223a03eda9e4ab90bad04f6c7167f302d63c9068f52f75e59ea1939fcdc88b83f397198c18c25e415899fa9f1ef4bbb7abf675b4d14c28982d89eee3401cf95cbe13f545e71c328070492019e4c752f3d1765f5c52f88cd784dd7d3f4e17bedf8889296db3ffd4abdd1eb215cdfe355422aac404efe8ca81f81b0b032e1c8e603f811553b35893f8775368e74ae08d6c02486fd3ccbeac94088cf3c3826701da01b62a31276bdbc8a25164a16177225891d320e3d5ee3f21ac6024013534e384f8e51e582c01fa2637513ca922aaf81d862dc19c710c070faa63b355f84d43699fbc4cb6aca9c6534ca3fc611fbbee4dcf430ea8c9b21fcb6c8d370dfcf87c8bc88b0c12f4d51bf7f577004a8315ab5ce227e456fe208e48802d49abdf1465472ea407824c33a8f7c6f3ee8e596254885372fc787fd5a2f896fe32cd66f3d561812bfd8c60bf4d73f66839da5d57b4e0ad2c7c1448d1396a2c3f4b4e6bb46d4445b89d60fd26f0a5a5313bdadd0fe0ead3aa9f06b666adf79166fa1cfe2ea17afb8f5572d80865a0bb70d46de6aa7dffe04efce3ca098adaf7e0f23b23bdf56b96c68857e6b0aa715f7f5929a7c8832d09dbfeef30a86fee21bd1a20d50ee34679ffff2ed4f0d465c68d02f224a56f5ce7fc12cb4fefa90ee85b02d21d055103eba581421217a86a0306f3d2c8ec8025b800556e34d16ce13fce85f63917e5fbbabd1b93807e48ecb5d85eb757786017081fa0a5f6a6afbaf263fd0a6160fabb8ea0df5493945e71620719dfaa412e07c83acb0b6c3e2645430d62dcf9ad25d6c456575d0ff1b853eb5a3d0dd1a736319a9ab2f6a5b884c6bc39e8f9580d12574b717ba1f3b582ceec433e284494ffa99b7856d7c2d74b4afd84053c5aa6b19d5c6df09c984927739aa1f463bc6bb02579ead2df57c6b923fdd15b968ee35cfdbdea1e3d569be3e1d6d35c363610cdb75c7a0ec102c1c968ff57ceec3f3e5e33065d0279bf8af10f710e0bb3565893b4d328022cc919fe9c4df34e07165330d7cd5b669a306f1b4242079ff144dd03eb0a06a24467df04f41ddf2c3e4315e447e1cb972118eb48a68252001b85ec9cb97458ab53acabbc9354b2c483a79a4ae875611c42b25811d75084d1cceda18b7c198c1a1fbcf8a7e42b9557b9d5c3fb77a145b4fa864f7a7e2536a376a19b75bd6bf9ddb6b435e37615f8167c5fb4c0f7b20693f0f791c6398f57229edcf8dd23176e1c22d80824b4d154faf94d7ab7102fc848995e61441df825ddf8ae3b400abf82b439138f8d0e3f6a481ba0d0feba4a943eaf6ef82b78397e8ed5dba8300096abe62e1e6dfcd050f8a69851775fe54635caf6992c9a408bcc34dab705b16d6494d59723883de063a1bae5af1d91dbc70d7bc1617914108fdd34dc15f8dd7e166a239e077be066cb1c45db1e2b34c87fed6821cac16636d1fa4d6541675d9a430c8130cfbe7de443c92fb6b63b65066f2c64a37398fab0ed2fd6716e37f48845e2157ef90f12ce81f27399a8bf7d88181ca8b9085f35e7044a5fe749f15d850ed659cd1a8413a62dd44beeb01c67f3bae4f824103905b9082ca2895f77f539f5ee6d9e69a92a615f2e62c81feb213ea83be99551c2c13e56cebe53722f7689c2572473abd7f54a563dc06ab3dd52f6b20958c376e9b61ec8883971f717e2ec29fdbb4b8a69bdd7cb07d6646d0f6e1e3216ff09a28b11a8dc957b76b24e4bb6cfef239d25f9f3ada575c6b2ce3bfe0624c231c131d14e09a8a522514da31baa27a08d7f2380a33f56784b667e74cd4ba9353bbc76514780e946645dbb712004806192f197158ed75e06c4a2746252f511a58acc39186a30a8c601bc3bae14540859b9eccfd4b74ac528dd1ba97ea167e240401cecfaa008da6aa2855c6126dec0c62223d4edd03e43dac7eb8b8af3949933762da998f9008558631515a2ff15507e8f2d1d4eabb0ed4aca0131177c88062d6d7e61e1ac6e4c1a88ddc0363631581eb1ac4b3cd08a5e2d0d2d511bc4db47174ad3160eccb0c175a2770175d2e74732ed78abe9cb32e1f5984052d5f43a1450a414aaa08e887176087ab5e908e2e9bd90e638e10e22199dd3f3edcf8642bc515ba74a7e7b1500cf1c45311e4fbf8fdf10045cb009b71fe4646242fa1afc4deff24a2c079a257ae1356982ea66e3a4f3576d892e8dcd54d0404e75586f6e59c4d707a5125ecb789200b70c6a7063fdbaeaeb75bc977070b2be2070fac378a6ab93b80fe7702aad24ee88af065dfab112cfbd97e5fc3f13622023d0f63c0f2383cd971f4b6d5cbb829bb278b11eb14dcb2445fdd26abfb51fc9c2e533171aa3277accc6e21e9e6c03cb3eee5cfc756b0e508b9e41331f6c5a1f450a62bb737fd397e556f515af3c10fd8a1405b6c0c00a867cfd78438dd42c3d7823f2f3cb54835f4152dc9a30d2a9464d2645aab85b76ef9271d9efee234a1fb3a2569b26472139f4181fd3034fd9c24cff7c356b31c97ac40938592b535f45522b653b6b38be25323a387d552d4e754a828e3c46a90e67b6a03018afe987f0cf5d1eb03902d955c0ff82f5d2cb2975db153d76288bad080b666d2b48b42d37048452fecf534c6fd377d8c1591c9007dfca8765de7b0daad8f40c8cd0f77641a9fc9c0cc54087b0e2ecce5f20c112d01940a7df474f003bc630fcf302bd4555041ab748c8a0e9ffb626892af3921328a72450cd2dfcb7d69255d77e37bb0d390309a48c9d942bb1dd6c6c543fcbdcd2dd5fa90648a54f0c60504cbff47a47acf65d180ad3a530d7db50feecc4f015c0b5a04af971bb56e4778e5ff14003fbe7c75accc934e2cd93aac93ae1844b80b708764c17638e8cd2c05e43e843dd34c790bcde5e1afe572c01cf1518b222c0dde4b31ae81068eec2f786d9f7c7b424410174e8cc661489768886c28244bb50411e0f34f8f8e50c19382f3ea36bff9b4b21273007ee51f337f143c5aa715845a98bd0b231107f298fda827f46314c8f58f5a0402069711feb0558c390715d3f99a575c090c26f9d2b88d86e7061a986ae8a847b780703290f18153c4912a68e3333c8b7cb5bd3c753f063ad84d34c0dae6dafa3f6e18cb0b182c295107c3bf745a8e8fe1eb51a6c4625f4376602cb465e753486981cd9f2d6f9b552c1cbc4af619bd61baf1d8a5bb1cf80575ff5b4b0f4d9c7f0c2855bdc2d6aba048467a1117379e2283de0cae78fb7d777ac0a7e2006e840dea5869e670b8f1303ad2350c103a02845209557ab9d005be33b0428667fcec246960215457fbff5c542c55b9b123778fe0a6ea6ba10af34e131a1fba843c70631fae7b62a99db047f8f250cf648725420af9f5b7affb7504ff7bf5c4a7d53891c3bdb70b970470a1227ce08846bae20013984b35d8c51a57c84d920f2f4c1359fa133a755789f10a0c070bb85f11d3f6f7e563f8376f67417df19431f73fa228b3e964ae76d0de0c52c8cb3ea5b7fccc691045e13f6ba5ce820710452f8e05afb859d9f98c2ba4f1ea354b3cbeb9697a28f4881cc116f4048433f29504a2ae8849d1a02984fcbe41462dd70cd71e4b2151e36797a1f7fb4f0a3e0678b483e94fde373fb2120d5fe55b25ba25f8c3dfd09d015950a8e7dd45832b4d6561703dab5152915dfb1f7fb3bb43b56b8f2989d9e0bb1d906fa5b35b50be15d4d92c1cbf40cc97aad55a44cfc24a608e2c3c878def0c9c33a5e1a8e5de49075f182da1dff3ec9ac137e9e00b0561254c58d7bf0db8eb4e6fd0103a465209c897abbbe2390da9e4991d1def4bdc33c2ee7cb5f8896f9576015692d90294028f25de7e8ce2c1f7f87a6f4a3d76a9df5ad8cb9f8cb8b972a4a24bcbcf3ed217cbeedc80e2cb3a3df75699582bf3f5d897b23e978e1855bf8798e7add54227f841bf675433fb4ed691b6adcb3cac4fd49c8da872d9ce1b5f6ef85c898812bba9f1650a22ef17104874fbdad7ec3643e11f6ba8bbf56c3efb0be7dca5f455ccdd8b2471b688ef394c97ac76be7d958bf1b970c800bdb3855fb81ef9a28c6166ffe51ceec01709708b3c92d906e2e3c8b94b82a14e5526ab836216d85129e2e241d9192f62bef2149b7f29f0058c01c734f04e5ec88252aa6e41e1eb7941e15330288c862889531bbb6c0615f489393c6ab7240ab1ce5028e1c0ef1eebf61e03e58e43aae614de3a317d2b05a80d430b8c324f3ad2f472bee61101546a68ed2ed1f1539db7d5f0b1ed74877ad68e8eb7ab3cc7d9d146a9bbeb790de0b5afbf00ba4cfde8e966afdf06a71b7d1024cd523ae69d56ffabc1e4af612e55c0ea139c61578f596788bb3954aeacacc9de69fe0922cb861a01d4794d7ee872ae614098be49202e93600685a8633c09e4a16f95e91cbb53597864feb53f2d05cc0f7dd7ab6c73129a38087d795a2e5e96a9d6862f41cf29b543e5e75cfa95641c9a91a25b622d868973a05a7e8e78470ed0d4df895773214a9c957f06e04515f1805782305ad8576d2c1c368935b4f36c2135770115918a4e17964edcdc3213ea15fa73adf67078ffd95438f917a4128fb901e522fdc3f8faf03e700aff48ed6d165f8549e977f09d10bc3cf1a06f9b15011a68df34aa6e3bf5751219262ddb1a6d90cc54bb1eceb181f24e0a32cf0d19811feb396c75d4952dea8f43fa5adc55dcf2b7f57325437c8b18fa2eebc1c77f4c9ce66b30abd931b86813a9f38b8483048ea8d05d5df1577b18e4edfa62c616e68b171410281d0ece3b11e063fcee2aadfc410b5f3eb541fa7006a7f72b7f61ecc69e7f9bebec316b3177cc0bdfa76caa75a999a113e8dce4c240799edbef7e04faa02be1d78b2d4d134d8bb016dc6153afc1218524324f5bc6f2bbb20b06f2917e36526920d194025b34cdf0476f27a416fa50dfd85bf6aff9c84ff0e5fed72dbf9f6b404268fdd84983c451cd26dc943712e4ab2c71bdfa3578986b43875662dd3778ab933bcc365927e7f52655795aa8df4ec303bae772210e953cb29ac6f07c97a80ee36b575f4e63304fb869fd7c68dabc8e176bf1731b620906a7d063f002487d6a4c82db2a946f5396da4d596705d863b5e1cff86e0c40fbe0546db54f954b335cd1c9599634c8fa795584a33615655ef7a2a61f36fc7ab2d4a688123273fdbec8910740adcc8a58dda464ce71d6df3fc2f261adb1004a1773852245b148d28d079d21f51f6e3524a0334cd1283abf43fdd3269d99a8df32ee472dbe18f7f05b4a9822d164045de6217b04579fdf769e1853fdfc7686d80968b32d2f2a45daec91eb67c15fd750f25f2ae8170a6824a8da0f25c8913b2dd052a892dbebb505fd0014cdea60b579bc0472802b50c40cdf04f6dc85ced10307d40cfd751a23f81889a2666ac8d4526148f688261207c0322daca9adda49b7b781ce350cac2fd93a784e4d7b7667a7b9f5161dae8a02e2763a7c560fcf33039f5e4e8fb44401e1975f5f9e5a53b7a891fa1c99f063c549e82e3b52166945e432f033e02e676c3adb5fcf57e7004d8402fda49cf5eb52c72324eeca93d9d0b8af49c85396e74e684de8fae03d9a06a13da888cee8dd31ea4d52bbf6692eb07b166ce5f3f9fddb3b90f71d467e5d36eaee9ad8ae375b9cbd6373b616f90fa7c8f9e78cafc530ea55c9106e60660feda5580a8b72fe21c2a52aa5357e082838fca1ee701afbca52abcff6e3d8f51f267dcdf3ee2ba895b864a5340190e588c7b5959f08bb8e23b08423438ba508e75a803a1c7108a69393f9de6280b482582616efb904ac873db35ceae5d2d897e50bc7bd373f5fbba28f88a41d752fb5d251f0681fb5c64bc7a8e4094ceb2c4420f8cbee887522a7376dd09d8cdf2c7c157ba31ac64b0409a961753dc10050e5caa445ca9da633de10af422d518db1cefca967998fcb9db77857ed09576c31a6ea9805d8c62df91011210d9d71693e9fd692ff851d661e7f7e568d9a280adc961e3593cd91a0280a28e3f661b5bb421eb3ce158ca323c2c9983a1e02255775178f987644b30e32189874fc910b2ef7aa5a7ba38516861cb7af35037f48df95efe7d443517f8ebc94c49b106d24b19667778088e3b216614431bbd02c5ed33c736ec2d2daa0a34090d7a0120c3a4d925e68e83e3b4e4ed9bdd17f872f562d3891044fc9ce0d7f3d5dd346f72b7db99a4733e9a958b582f3e784911fd53465c0535fe957feca19c561a16572593940ed96a8dd7e0f01b4c8e70627a29e2fd2aacd815c8129101132fba8eb30f458e62a14f8228d78c70fdded49720e85375c9f348251416992cdcc55eb9b3dc2a98da4aac084984ed5e1317d42bc59c418f9d2d0f10fab6ded3bd0b4ddfd1c9457feb380f20ab1351db59466bd7103699de9eeff85b55cbdcc20140269a7b34ac50d1403eb614aed83dcd896cb17b968d2b441d1802069b4c06c5604d175cbe54c5f3669692937d076581c033fd2d55fcb550658574969c42348baaad7cb43a10ba6b147642a49abacb3d0c584db5e89f70be8323092e541a8317461f6dd081e59a8987145880c3da5f408ca7e1958022bc2e83574bda403611141f74a8fa00e2804196b1fec660e5d55896621b9c18d0f0080ac6396aed159d3e0856167274b7e8280ca2ffd9b465d9cac01f440538b7e825ab551c8e3a80ba61d7d24a611b020cd2d5c852004ed06bba9afeb131168456d2c060c4ae1674d0555641b9cbeda2ea45477b009342ae50b84833abf0fcdcb075e0c5fba65274663aebdc61bbc7215a0d46a90fb11bc2cb676bf045379483055241c84860d2b1754594241c1ebb1293a08fecc717e45c241640097a900b4d683dfb4e04547eb0a94b151261c845bbb554148c00d4a37062f81c57cb942e1208f1588bb0000b6d60c5eea378073b0ac42e1a095087c718122f0ad1bbcb8342047229b42e120b93c51aa6032c283dddcc1ab28956c24859515131021db60744078056a06b30bcb2f160e2ad66133c1084283df26c2cb17f1a1d5cad95641d4c006d2938497c82aa9bc6038c8c7b6e10220003b98be09af1957c19881f34a86838a0ecf5a05a52c53a5f0b28ced7887f8291a0e5a5a6e8b61d44006d73d859768af159344dac2e1a0e3b7be944024a103df2ee1b52afba889e253381ce4ea16d74ba1f0ba75adeb8cc9614dbb278597c8f1f98a54f8aa5e5be115527637481a06562a207496c1695b78e15a1e41967e1b258802b08369bff05abd6e5148652c28552002c1c1ee92e1559582cce0f232e220e7f757f3328c5757a6b8a59a3e4ad52f21d892f020f78ef1224a513228b952e220e77fdfdd0f82d03792f1f24cd4a005c84e26a4ab2e20795a2ffcb22b98775ed5f6ca09bd95de817961ea3631faf38c5edfe0945fffd51265421e94739d57a1fe58c0564b0883e4648d73fadbde0446ed7b87da51fc4f043aafcf06d76f5a412ebf84306869b911383ff9d679c56c5ce7be44a3150711139ff9ea351b529584b68ff39f749e849609ff3da7c4bbef7c57ca1e7aa3722f90fa8ba3eb5e7dca9105a64ffbf0795c189a9cbed0ae1107d87236b982d347db95513ab0dc31dfd55ab4063abd46e4109c56be92fb2c4ee6553ad8a1db04752c08687c53e9371563f4f5d104ed6208db9f9f53993e40a313c3dcc12a66330cfeb357483927b6d8338bce9c8669493a26d5b77101eab50d8bce5eaa46430719c60cc0ec346973459637b64d9c351b38dc50db730e9564bd78483ff4d248253b4fbedb8d37bcf8b66c39d7ce63edda161c2270b82022ae9bc925cc1e873dec925b2032b9b9aa94dcc32ba79f17d33590fe567d6a2af272e23d9762f6dc28b933fc23091f10b387f6b4b5007668475c4e949cc47bf8f0475b404a8825407d3defa1eb032674c31ec8385ba3a52faff6bf216e2d409d12c549214e170cdeeba57838a9997c78b9888feef9d81051b5a0a4fcd385bc31b1547172f3951a9375c09823ac8a5a50eec4fe4d24c21286f3a1937e1c15561ec9e31861ef4754e921c8f308283b77be69c9c31d02d0839296f7f5d8a5570466825b4d458eb940f09ce269d1480ad91825491fcc0c4c180849e8de4d79a69ecc711ceb2a88388a5dd403451c113d9a741882ab0e003e046282d24d166167d72064a1dace355c0bf6fcc23887da7b367046b04a744737b1c1438b6027d5e1f0f26c87268f80cf18efb3c3f013eb99e9683e68f599273734bb138eed9caa64f6666f2f8022eb213dfc7a4614d134179afedf8e0e9335f442e2cc319f27c59e66169c7e502609f90272ac2c9f2886df9c0e892035914b7495d038164fdc763bcdd35092812fe63be88be87e75b5b6621307e585809bccf5421fc0bd18140d10123e3f3e08e6107d480c0046bc7f25c23c4aabdb737c4e5a2efe662c6798e69b7fcec98512e8f22dce33e8d30bb9d1005e4bb51335a473f5a3385adf731777f5b8c4be8ffa6f21ba3d9122898a5d67fc10380c497357d61421bd8da05effedab10181eb509c3f627d910ad27b7c343ea868145c48e418316644687ed5eb24eae6319b905701479f9700dbd0f41340f90e12a88a5e5298c0ffd0b244f07c1c12a990b6bc7c9acfc5fdf52e8215adb1d146e3ede0cf74678d520e2258389a43e3f31f197020bbfbe5e15e3a960bed6b333e90409f44b03cc7726323443c51eb1ecb2a12769a5f18ac25689956cbbd5eef12b839aeb7e66d7dc73822696279b5b5bf9ce411bcbcc55812d373a8274d1e6e134707d7615ee41db7bd54099b57321982f15a23e8c95650bce540b575a37c6502db6cb809c22341632abb17c3f474e6605e6f075a6f45c1d9e2983fc1850dc43d6da2c43fc974e620b998010e75a0a06f3cf59a7f8cf86688692a021da4f627458ace22d9091c8b869fb2af200bda765a5825beecb4540184d6830b204c9f81ad9b9a33ef51f7a484b9d9c97712f3dc94c83c4df184f6e3369570ec38603f0fa0840ef7cc2ff63cd2d0ac460a1da933084c5c3dd56e062874af6fae60680e804a31eb00e95ff14231a8f9e547c339149591def3b84aa69635ea69c30397edaa5063c0e015ee571dc0fb71ffb6278a299300dc41004ac870081d0b73685fc193f5176bcf4adc6fb4770a6219d0799af0ad48d28234fb6ad9741d888289b7687938b8f3d2789e87c212053801c71e3e51fd428657118d3970df10f44a7a7546bafb2509648a047e745ef204b2e74b41a363663ae5dda7ecf8b2b0f8c13349272955eb0a33f228d8e062144e4c257659d559a6953bf99ec9a7a8eee54d39b96822508e0d44d0a46801ec211d7b1c33e38021a7d75d8a583242dc8654a2282ad55f52ed4ec901a272aa91ba925bd00cd8c64d6bfd995080354116b4f09ec2f2fff586551156dce1ee7523e727d7f26aac1e8d1e814d0c829ba94cfe79d03d7395bc6bd0494b20b4310763fdb2403754173fe8f1c44e6f9a05acbc544e6523272d8689264108a4ab1f5d46ba3acd4389c8398c30d97be0c6acbeba451292671373500fecf83145cfd78056931f27b9968d598fb3f5566fa56859b85c84f94dbf47d8217cfe4b2906c84f1a2c197a60f71437a29c657bb247fd91c36e5ee358831149de89b25a3687958c1990c77258a22343e8ce307f2a3675c14551e8a8123fa4eab2295e4298a2eb7b43ddc12171821c60579bd0dbd9f4f949e9d7df80b4dad2acfca9e7cf04db7da0878fae8a449b2a8b64505868c25c5b7f16888363dc8ea0d5eaf7b4eb8c9a2f21d52d851370990eeac28ae035f73b8460317ef68e5e03325c43eeea12bb6b0b2d444c68e00a335a4838e223399c122ac7ca25b2ea0b046b1b586a1ccd532618df72dd0d36eae4bda669ef93108ab46d00464eadead523ffaf7720bdb7484ad0cb1c6731f832a4be2741403c89c5bd0a723d7c218d4bef609545ffafaf44616f6d3085b18c2dae73e04a896c5891e0f056d0a95168cb1f6e18fbe7ae95bd26f145e6e84510c230f74c5feba84b8a10ad90f84d70cfe1f74cbfdfafbb7b918cf2988ed82ff80e07e70a1297ad81da3d30a6ef707e0ae9022242160b0f002f894be92597e0596f4e81bdb1acff8df097a1f309b258a6190f4101248ece557f0a107913dd682b7f0c8d99692c030f9ffeab75f21cba6dc7c8a77360b51eef92b920b4758c31d0e2f1bdc0b8fba86d8d3183ef82b1a7e5e0d176c09a0debc3a3da7047dc8c48f5e36187a997eaf1e8403a6cd61aff6e95c6366e3b0f5bc3234b151847331a95e395cd6fec9d0f21de2133e0a41e3e7fe2297f566f6a8fd8d9ec5e28430044fc8cc63331e9587117f12f1a17363aa26842ebd7786401aea5cd200b3a21a44a22e2704a7b3ec2dfafd624aa28023f806a16fd89588694521f89284fa1b3f681c920fe608ff49c75a033a8c47da8f3d1fc11838638136545c568b0b963840b11092f3f2ebacded4b365ddf54f95dd81d431e1e4a0c5688f60eefbe43b9b0337079dbf985ef5cf96d9be8f7c260adce8e96b3631d362f4a3b2be480487b6fa40bfa5470261d30121790867cf9d11a8f6a3b935c10e68e96130daf8f8e47c24656e47c50e5b694394a44439e22b7173186e4a8c1565629d6e198135071c6384681c47c8f32c2ab798b22748f371fdd119ed56aa95690e6e6629d2dafc74fae839535380d3d3bc59a810c353397b7cc23548e8acb617d91f122106aac5b7428e41d431acb42f89ce426281ac4821e9288798e539c6a858407e738529685cf945b8f9a21fe8bb5828a8ac65ab6a0b17734cb153b5bf61c6f90387eb2c61c306bb19c489e5667cf1340f268f76168008bf8ef620a640ee60845e0f6baa12418a13307bead58f416341ee6a01451e2a31b0ad797766e7b03019566b01b30b56eba9a1101cc382a5e5d7cd2579a75a0050cb659c2d04a5e05b5fdc7933dcbf983a2e55a505f20ba1110092f449ef16c4d102805300d2b0e1837dae6f694f9a5717579c50154dce64282110ae45b697f9b1d8a2d8647613194edb1389ee6ddf6e1b0cef672066469e41c3c1691650e9e7032cf7a39dd0466c96360481c5fdffe7bc2e1fa00c4e9445afcbdaa02c5b304e2d9df73de542407c80a6d099853a62bc8fed3335534c8372afd5dac10746862e0e5e3e349f8f0facfa4545e88b2708a8550606611e339dbbc7f9c73138c9234ec03d42d9aaaf23120ecc2242d5fba5754744f44f5c174060d050e291dc1c205818b9fd35496505b5cc9d26eaa16b582d1eea277c400d7f9aeb6107013f030fc42c199051c54cdc98c01f525179112fcd31325771fb012ef78d39d52b2a9847501ae6d5afed00e37226cd6b87d06eca9fba81089fb97569730935db8f83dc7b60efd0d0f36d9d7447b98d0b7b79b916871c6b19d9e157c712a1cbb29ca269d2d0d0e6f7cace3e211994035e318651d2c58cc2ada5bece886d0494c843aa03b011aba0743e4f1f48bf0310c508c7d1c922a92c892a310c014fc60735b2e11d2ccb243970e667ecd2a44d2887016a32210c4aee1d03c0444a48c021a4a8503b964b6b83d33cf3468561ff1415168e934e84aaf593f8de4e43865705a3093b558280e9829c282d8506a351437043b46c70ffaa8eda03be9d3ea49152abd1aa970e80c689dc7235a0e0d9f23d1adedcbe2e8072006f80d000e4ac5c3413e23e9fb10b7807b2b771996b32db4120c0186362265db41bac46c139c7a87cfd334620fe603f9dda1d13ed01d48c2725b0cbea004730064430b10ff85698113c44122c8db6779b969d8a71998d894f510b80022f0317087f72f0b7c6734b2289f19430f920ac9880ce754fcd069626896297739cb9be17d159be9f73104c924b165e81c27329658ccaad1958b110ca4c2aa127168703a75a0affec2510b174ab4f5e1c6fa87b115689bbe7f3f4d5ec747b261bf2f01ea2c886281c8d2b6a86d9b837d631e0e09386f414454a04a632928e73b324036ea4e205c178dc973ae4004a42d3612868e879b021f1844ec05b12ee8dab5c13ba0689ff016f1ee76dd5d56ac7a191f806000be6a8b7f50c5939b6388d9b422cb057e1c7276568ff6fbd3c1403c748cfe33db74432d71706f31642faf3e9979dc0a24ee07ba2d68a36b7d6d5435d7a28d9a9bd966434eb1a4577ae60cc9d410edabcc8a74afb218c0b11e29b420f6954cde70a78e51542fe96b1a3ab895a14a6f8e4ddccb2dc1d6eba8865654f2825af26c5c681fa531d21dd4576d1e5eb096392427dbd1e19583415bf7fe5029596aafa14f88d99ff54f143ee5a0d48bda498cd351c25254a6226228d1220d198830c0d813b27c605965a0ae4c6038c872aa48c5bdbaa94de6aef1d29f4a1d944052492fce478ca0f1f0367636e40171ac6852baec909f3b67b8ea18cd8f8ec0879aa2b1019e817b8fc23588a91f65f2c345fcc5590cf0e50a7b4fbd9d68c9df2252f2af55cb7165be5135b3c7e2394fbf3ad1b9aeadb2eb49e40e07a7f3e79a170eaad8b680162ecf18bdcb48e721fc84982743a78fd5925e628cc65382d52d5bd60a95eec9c935220a9200d2f18c27582c722a30d136ad437eaa3a67e2e6b1ab01b56d8eacd2d98c23227b1181008852302570006e52a798cd026494b890a2b1bc5c99ec63096c112a3513e4f3486ccea0ffbf2ada0af36686ba09318759aad3e3a2c83df309b24c2335b93f6484fe23a9a6f49ef9cfe50cdafddac5e4066843a0916a7d757239d6db4be4ff0a380b74eba3368397f20c481097c202d5abf8babb7b9dfe2185dd7f5e72ddfda463b202d3d6a010e9ade4a3f14d78de338f4252c0ede5a196d8c1ad8e47481ffe808ca68bd30cd0c8dc2fc6b84d179092949a0b4a84717e4d589e438b46f81ac481071bce136f413e30853edf01136f7f747ff6a2a62e350a0add002d531a50cc595fd851bd894f5dd6d12dba394b55e4717730ef0fdb8b978ef9b89fae77aac370a78c8c56aec1ae3939d444cf7dc89d99547622966d44fbb17e32d9f95b37218b8ac2d9fde5a5e4c05c19278b3c74dbb0114255e1577dacff04692ca753960cb64125058029d63cca2e26d7390f2083367fc35d0d2802724bb33162107f9201f85be9858136daa4eeafe2e4166f83620027cb5db97f00a1c2aa542f1633ea61927510005793e7e8994ecb31e1c0093d94dfcb9b2183e1da951a547254bbbeea61e24774af4833318b7f8837f16e0bafc30e79a9e7c0369be2860c325004c0d2cb8840110fecfb1c808d5ba8fce59f51ad219d7bd343dd37f16563af6a0439526c6d08e602c518e7999f7a626fd8768aee6be805095bf16984814e6f00cb33443b10d6fa0a2f68df5ac799b671e94af641e5589ec55641de48b14a2ee6a19416bfd060741e0095b322e5a1f5067a7a6ea80e9e52cc8f1efdf70eaebf31fdd55f1d560427fd0128eed97132ebce491f0dc78f4231f0d5e36e536681445b9b00923bf86793c85266329b7afe27adfc0a017f1a542514d7d4e7393414a5fb00a09120c021b7c6f5f3014d5d4736365dc3d9e6e94f16aa13a6bb4687f7f5a08d40ecf44cbc6f800064835e88c0c8d435674e6b85745646c4f4b42a7cf8c321456fcefd15cef19405bc5e910c955706b6666690c815a865f44a2196b06d12f36f6909d4478164bfb059e8a131193270a4fcbfd1561c064c83a2c6264a2cc3001d4bb85960aaba8d1d90f7b84aad6eb685018f67f76676e490e4ec1609df86563c0ea50fc861a2926a2175c26037ba4216a8e21829a3dd031cd073db343e2b056b46f8eb502a805794ce95dc45435fde56b699f66762d0ebd38abab0f5d77a9bccdae8b391558e8ccbd04062ffd5c159f35bec995cf23f20fbd4126a2845cb775e65efe4a59a124fd855b43b2f9217d46611735c89a21eaf87bb8c164924aaf07aabba2e29865f49667f655d6493b2071162344f4de0ac3b31e4bb28167ac4b88a0a56b87d8aac420d7f8fcf8aba6fdfede5c51a31d1999859f7f860aeceea1c2a605c3d75aad5a607515e1181b1b87355fa1be32013486f3fd95ae8de93f2dab8d271577bdb6556c6b2b8751509bab1a4e8eb8178d7a8c5187b0c290f106f54a7994d3b2a301e93848d8500f2a5be5acce6fccec85d478c30876ea90b84323858d85406e2c51b606fc36faa733f0bc9cfb8c038af4157f124580883319212fc6155a6ad5d4f96e96e7e2654a0f4270ea3e752407b3c13f876aadb634e54b50a4f96d80b7ec77f076d0dd48426fe4e1c62908f913ac522462c4d6bea5258e235a6e9af4f6d0c94942a844740305684336901ec0101a1a53d2f63ec0efa6c960a4b01d978b14308646019ec437388536b493c8a5e178fda771119fe64dd0b0a7a810eca409161953fe2a6efd94a8a69632bd9bf66d19012602bf93a3c682e06a61394ed4d5b1ffd53678f6d8853ec308a5f8aaf8d5d767e8b2fdfe88508b10d23d7ef5710aaec2dbe36a465f93e96b0449f6d4cf163fd484eb475f3a2213774b1d58f224480192b8e3aaac82e5537e8a57c3e0d288df012ca533dc27830d20e1da8e4287693144953408f1794d2d5c18daca8caa4ea64489c2e2418e26a11f3a31444a15101ae72db12f7c5683a910fde4bb31ed01341f0da9ce8943fca9e7510792e18a1b1e50c66810ccb498904370ebda07c3dcbf6d4895ddfe8d41b58f99c9fb519811f6985f0eea70a0a1a6546dae1bf43a2a29ca1a3797e2cb1bea302573ee39924b05efe056652affc81b168a16646f051ee004820f5a2098594d3a6d37739ab9ee5f57ef11a9418901e655946def56806537666d6e899ce3ba7e32130a95d43cdad71a141632e63d60ff7730ede6f5359f095e5a984115632e9ba465a57b79fe8656879aed879e98684208217b4bb903cf0eb10f600f54d0cf84e4136bd1b22b8fe2016dd1491c99f763b0111f3112ab57ad526a6e8285c0331366e2f6ac67bb34d75daff4d7a6d6de613cba3266ceae87a7dc44bbe4676a2ba4777579e5ba2f368c2163128785228d3c4320d230132b1656d2711132d8488c2a57beb3e1a9ac5ca695a99d66b43ad990a26ccad216b1635756d3044db2fed0dd205c166188ddb6a8296808d3b39e491a292fa3fcac3c8d38ca6d175596980a2b351bdd549f8b5cfadcd3defbccc3b27eb53871ecd3686a31930d572b46ab154dcda42e2663734a2969ca05e5523d5a99366d94ad72a3946f617994f2e035a326f1685362b95dc634a3368a4b51294e895122523c8a216327757b4e2d76d986fd164739a1611ff5508bd9e8a7bac69baa5cb738f5e9f408d6a0755e79a617b49a91c91a693389c35d48671d9334f23d932ce54c935d190f823a7d593496c922599a59585858a20dad1c7de7b51b16b9d3f68d2fd8fed0e64eab673ef07d5f04e3b9c6581930e315370462cae900d0b86dcc750771fb1cab5643aecc4c601895328c9093d832a05df1cc0486113f9b2f6563291d9b3fab7605d36adb368edb368edbdeab7e0999b76b1c9ef156857457d3348e6dcc5498b81dab2801cfedbfb0e1f6360b8a6e0ff1c5c98fa92f3ebe0839761b87c11d2cc48d36e4d87cac3fd41c2de14e38dfc2b172a70dd272e323116e8c1bc62c6e28dff1c61a2b055ad12a60e7c61761121f6144d1c67615e1e3c717df154d0461de58050ca088dd3e2d924d371a8d80891bdb15ed0d4dab3944feacfc588370571e668746a4239edcf0ef5710be3fc458336b70d46a38bf3d6ef1dc6fe4a0232231598194524aa95d56be3a624de7106bfa9b0b8cdcf0468046295e5074c3bb40c62ddd5006952b2f23ce88008d4843526e441a4aba613c7acd1ce20cf9be8e38a30a204429000da36440bfc2df461a32bafd1e10e897bc2b578c7e0945b9fd04bc9061c3ed9bdb5f19b97d17fd9261aa9543fed02e6d645282db37d52865e4c25745119adcc8eae8813ae0bbc7da3d7a35087739ae423c967f0dd668cf69961afbd5728eb126deab4382f0fdb6ba7253832c36646dc574cdb6f8b2e3bcefde0f37d5f00769fabc95ed0779e7bdaae9e82062d7bbd6011a4413421382728fe71edfd5794e8bc94905cb3b3b9394dd5dcbe6d913cc61e74ef992e76b600e3b97a38bdf31d6f0ef8e644965d6d034e46a93334929b5f7b50ac4747bc67e4ce2f6d7d228f87622521474a728a75d2c43468c182ed7bf5a49100b2a39a4961a3715bcd9ed8351d0d0755b93d22ecbb078d52bbab87fc1f2d055808731ae010e2a81ba886198190b534fa7d386ea5acf91332d188ec9e91377da38eef4087adba9ab322eaebaac3f5cd85f5bbafd98eaa2b2ecc6e487794c7da42dfe7d291815120d7db8435ade7297d34b677efacc57562aeb3dfdade0387d664797d3accde2e86c9cf9ca593232524666c57bcc81bf3d82373ccb7a7dcf5f647efa4c95f9c7add896f795cf9677eea79667719c3e572e250a85dab66dfb3ede369ed3eb50309fa773b5022d18cf3453654e35ec2e635551d0ce02692af513dcc1f7ec0ffe667fbcdcbbac2b2d67bd5cf506731cc78b9ded6ab1e110e9d9d0c5c676d1cf7651db62b572e1c2858b958bb770e1a2850bcf854dbd858b9ab2215f17f76ce85d06178fb4858bb7707117cfe91f66e5c6fc7b795fd1389ad2bc6779262c62e4ce9c5128146ac6b2583824d7960d51bfa269502fb6b1b8a8af6cb8c56e6fdc9cdbc6711bc76ddbb6759be775da938bfa4459697fc44c1047cc374d0cee8881398c8cf961601e6363e59f0d19044dd78be03b10e635acd41ff8ca4bcb608ee38839cc71b4e947cc61fe45700737e338aff3bcae676edb9cdb9c1bb77363aefa374db2c23c26c6a29ed3b15ddbec63ae3d6f4e8e9b1c374f52bca0e7bedcf8c410842ecb9ddb813564c0dd661ec1ae9bd96a68c3fd64ac87b2611017e6aa10c889616a8c0c95de98fab7a85c9902692af707bebf8b1a6f0daa471087cbbdfd71facc7fa03ee3799ee7793fe843d45beea9beb272facae9f41d5559ca48192b0186f1bdab39127c0457aefa89b5f2999f2a5f140aa53aea54e39d49ba608e6fdfb9de8a9500c30041b0b2ce9dde71e99e6c0844f51c89620db5abe5f433f547cbe93f300934dc8e1ef3d5fb20b7f26fc5e36c78763e47eee04e51ff017a5fe1401beec41c55c39dcbe4e58b60ebdfe7e22899a3600e733a9fd562fb5cbdeb2eab00c293b841595999f76cebbbf71dfc061eb41ef7d43fedfb3edf7d160768df4d3087d3c14ff00633b8008ee002f89e9ed32b561505ddec5f3afbe374d0fe90ef86c8937ccc5bb056efe4cc3b1bb29ded62d999a32e83da2a6a7ee350f3dd376bc3229b0985e26c3883f250d6c5675c44cf86df4cf5ee32736f06859a6fad3e5bb3f5af8640dcef12acd1e2215af91087cdf8e9dfa97ed4eb2c18c11d2ba7edea0ed6ef5c95e9de7d469d8d43f7530dbb15dbaeaef598cdfe687db66c38448239565b8b0b87c895c3fcc71079612edf7561eb31dbea73c5867d57ceea40f0fb8c930dbbc71cfc8c1a9b06b421f79816ab3bc1acd4785b9e9763f5e9799ecc796e205871d4d90a4fafff6a18c49d56da30e6f3b15d8f09775a2d7a5b75f59997d55135de5ac31e6ecc533504e2c6dcee5f57c3edabd9dd84315ef56a184493bbb2e1bcab9fc88deb7da788eb3d04e27661103eac6abcdb1673746968be12bade27066eae67c31c2e909bfbc79fa6b600f07901d06171bb9b72a3a0a1176b01e0f1c2fc43a1c096a3aef2400f7c4e7b200882ff38ae805f61fd743a9d78e5fbbe99994f06440205512b97329593ddfea986a75f79e7f1edaae7a1fead54faaf7a47a1ce9dd7a1aaea5f8d977ee55f5d798341f8d258d367110145fdfb8ca6a4297d1beaba9e9e2b2b3b779e76ae3744bba795cafaac32079140513604a27abcaceac5dad51ecfed991a46a31a7ea5c6ebf51cdd9917cad8ce4b576b227164c830bafd182aadaada49ab15683ba9eb28e14ee7027a43e2bc39ef28070600b6cad876b445f1bcb91d6d1b2379078f403bdbf96c75a551a151a6dd8e3a2679a011285804ea7d8ad5a454413bd4137486bb086003e333d448438dd755e70d408c51f319aacd035057efd56b5c9bc25268a81a52001ec11a309ed3db76cff33ce96d2d5eef550c44a4fba3c5eb6fa02a17d56bd0e7f4a7b5380e355e193eccf57045b4ab97a072bb070b2d4abf6c6cc8d7e64d80d3f8019ef3fbd075bd3f94f192e1debddfe897eade73e897cb3dcf1e0115566fcda907a8346a0c31aa06e5e6368f61352836558b2204b5b1794e37b81d514105f0505332ea970c6fd5fbf2094e8c1bca980c509b22d6f4b94a0f9194296309de66a00df60212914ff746746ff85caf935eefea8d1c3a497621c6e48a0e75fb12c4a15d30182dd1444a2c8206f0ced3a688473d37bc99305fed86cbf0066b04e12b04870c97711c7ea35f32c4a46a48bdc616b62ef3997f55880b21ac7fffb639a475b9b96ddcca36e73cf79539b76d6e11e4c0ede0ccc3ed2808104180b8dcbfcddb367065f3e6bf73f5c75685801f226f0dd006e1ee8fd3518fa08e93fdb1728f75f95987c87bfa56877455c8837016378837a485e34efbe387ee7a767b72bbe8f2fe79362472bd971d2131d60ce96ef71cf9b12982dbdcb6201b14cffb210877b728624d1fe6119c7683d95acc7b3297acfacd39bd09d670e5a87f5e15e279a7951a84fb41eac4ebc206e17e90d73bd9d6af677f903156088eee72975f6db94c4d7da66a52dad519e0ac965443ed29a81ed0703b4a7d3bc2b15d52ea5f1dc482d2ab2e43cd699777647f9097da04b8d41c6aa8ec8d76796f51c31ec2ceda90d260c3d5c3bb6ce88a11e37ab6c50110a7b4a801380e95afcd8c7a7334a3704189499348e7d68135a6a9450dc06d6aac01f8d6d3afd975946ef762bcaf4589f12f46651ac3e327c8e0e77ada9136459c116f10808d51e7dd865087516db8b97c8e814848dd5fdcf33c8fa5047344700afac302dbf6171fb81df52bec2429dba2f03df576c5edaeb32bee39bd31e9e63bef2f800899ff4a0dc2dd1fac9f34ad6aaa0fb50794cd69eeae375f7cbf9ec9343b2088f870bd21d7fbce79de67e7d9e0b10d7388bc367c8239a20d75de6da53ea136b4abef6a575b02c8b03686db18be552d298618bad96d9da6d4c570fbcf564d4a0cd792945c349f36a434683af5de63fcf172d55be8b70b6c442ef8ae4ebb755dd77d3b588310b9b39bdb6ca1b4c63bb7ae9b30dfe6ec364ec89cf3aa697f74efaeeaebfea2fafcb4f16edd77d5c7758dc5e7ff8ffbc06d88fb3eee03b9a2a38dc9c6442ba645abc11aab370c5f0d09ac81b333d680cfe959836d0d0a08c2c0c0802b988f5bfd9bda0782a0a67dff387075ee8381f940d06a48edea556b502ac871ff40b015d3faf7816f81312d0d89e3b66f0ae1667fffa689ab3ff4d590e6f78d03c179a4cf6e1a9496d249126786bdf17f9ba9457579aba61e430f535fbeaaaa73dbbb87f1a8fb26d4d13837bfbdeb41e2d0b03226096069a0b1ae6da8bb8c35373b980ff1af6b3d5c51f03b8cdd98b4ab8bbe87dbd067b7a2a1ae850dbb181bd2161614fc362463459eac5fddfbe08bf710565656565028144ac61152f8190020c3cccccccce9743a7d46ed58acc7c69971164b464666469552cad65b6acc69fdfeb27a8e8ca1f29da18672289445b72f430d6593db7f51bb5bc3c8929939a1644cc69a4a0c4de576499a947ed9f7b5299c9dd63e469c1eaced21c629364e214011232d6391a6bf1d4402f55cd41a76d6e3ed41706d4ff5412050bef1b10efd72b1215f9773dee675de10ed4b7949d4c5e537dad55bf7957f9f047774f7fec5f8c3c5611eb69811f3ee172072bf7901d91c6bbc871788b12608112fdc0922efb664c6bf987a812031d6705f223b6c0076c900ec7614b35db121499abe4090ed49ace9c758e3d92d8af7eea7ceeb3c9477171503f2b672c0c03cdeceb330f607795177e56955dad5aa7fa899539c829a9941f56d6924372c7a60db124f7fe684eaa15fa196a40d89338f416ab06e83efcca9c1203550b7c1f7c477f379fdc7716b96959db1e6cfe973e0ae5e5f6d48e4aede2bbb09a1fed5ed0ff2aeec90edae4ea7ffa795dde4499e4ea80341c9e8c2b811084a24b8d3d4e0ff4d4d9e4e27edff34f98f3ba1dee77e4eae563688bcd2c65873fa5176136ae5409df5d319c4813acbfe20efc90ed9eee9df668a74e61bb863e6ac375883954556cc6e79cca4edf916a203c1ed796382db365d88345d63dad8ed87110aa3dbb73cfd32d22f1be396ac154a6e734b6dc58cc7ac5f2d6e4e6dceca4de1b068178d4de9993336a58d5d9ba558d3f7a040439ba5db4fa1801261003bac82dea440d268519011883e29eb0b57e3308f72020d776ed4da76778c9eb393ff36b577767eb2721254bbdca1d955bbe67f489bf2020da3d197006e69ba214d0990a9dbd5d9ae399ff331d5c27981a4aebca66955f808e115cf6dffae5da33cd1e83c1d4b92e29e034d9668a5e6b4cbb337baca3767c6e03eb9a412dccd86323e3ffb62062177b85e41c1175f8433a906dbea51c36ff80d9648cd6bf80e6c07d6e3861bbe038b2c493e2e70c2a78929a2783d26a527a240c4143e38c919925114483b90d1807a464a4728816674041db800944a5b919324a42de664091ef8a15f4f124b1825e18313705c5210148cc0d0832946903a01ed18d09393d1089c4c391b819439822d740c0a14a6c1b7634ca3ef647bc10dfb49aa0434a43d53f0e868afe889a2279e10f405af8925b2d0753d33298a244a4f0c210655d0909e1049e1041c629b9213263011057ccd908e90036d7604290401dc927a8a901402299a6889e9d17a78504516669210d292296620c1222d3685112612520762203fa52d86842a484960b2e48b4e2869331e4009ba82863232020d8aa400999236a327a42081e925494b2a42119c1881889e2dc4a21cd9c24700a36de644094d94acc0220aa056c4832158418b011581f2e005953423a2052b49443431922c33171ce1e7a84549224159424f8f15a06a264491096cd0adc4945c405494822b743436a4a48828a699134654f9624ec4a458fa829652a20a23215af002ef940406278c08310483e402272b90624917809e27aa80e205ce543402143c1105125e8c052398b12049143e5c2aa6c4832986984642c0c20bb616312b7c8090e082108a70440ad48b51cf11464034450bb4092797d81350c2e0c4ec0a5770303d488cb0c29b4de079a2092d78e2c40900484adcc0c8280c5dd00195ae4b4292e4c4077c6ec0d51825291962269bc98063e9591223602cc91145928b493162427a42ca124f5ab0b2cd9470f015393154c511dd44eae9c087e40493a022565e14cd991350604a7ac2093378d281130bc90b9d569484640bb321569005ae2526634a48c1a8031df040a4a212505951ea2142a85f93873018f970d84d5649327682881e7602c80b9f13412498dda45fe1f4e926fac57279faf48b3f85be4ee9c9135030814c7a020a4e303d31c503b206468db008ab81f123ba0918b58f1a073e6d165c292d0c3b29cfb5cb77dd5023ac861a048d28626004abe11f0ae90a4f62ccee5b435db1cc808654333d29092a5a0cdfc01a347cce7777770c9f349c5ab13441691ec35d39fdd9d3d4902fcdd2d56a78d8ddf090c2a879188de251440ac0c308c58687f10a013c8c52623c8c58d87011321eb210ce004e839b28c04376628087fce43643b9cd57b094db1c9ba224432360da507b0c7f511150a384d5286f8d52861a630d006a6c57ce010c508026ee3c0168cc1880d09d975163d3e05423b16970a8b1696c6a6c1a01d4d83436d4d83401a8b1696a6a94343064d4c0aee7489a1aafb6c55099f61079717039e042129cb8b6972e0d6f300797032e24c1e85adb5d1a1a6ab800e0aaf172ad3df4ed5c8d061b9fccf07909e5320d4fb934dcf58f86da45cdc4049486873dd443344c1f0945e8caf0aea374b57ad34b5b468336face6075e8a161cf686a8cae70d62e6e2534d47865a86174d243cda48b6870f1945b2fc11c93892f04e1ba6a7723586d0bc794db4adac55dcf5a89046b00000075da2535bc5bb6ec264ec26ed2d3b14f46490a1720d10d490a1744612aed62a51c7cfb6d1940fbf23fd992d6465f297152332758a872395e27578072f904e857bcbfb9ccf55c410a5c0f1692dc303eb97c39fbaba17e5a503282dd5023ac7da6d09c350e7c9e196b3267359cbf9238375c0031ce303e0320d02f8d86f32bd02f9af37b98e10c80b30ce717e718cef65ccf3fcf9c63ce679f7eb9389f85fad53ac39ccf4afac53a9f87fab53aabce2fe733a7cee726fa853a9f8dfa753a9f9df4abc5f9cce727fd6ae917cbf90ca55fe0f91c85afe8d7773e27f56be57c96d22fefddf98c05739538633bff1207c603f09a872e1b1ecab0d157d67083006268b547bbf834d0b8660040eda15d7c19ea8beac3102088b48bffca3332312e5a30955995579567aaca4a5e2a0f3193a27631d1a409232747480c85a3f015fcaeb2149e52198b58c3875103506baa0dd5ea90eaa1e1f499fc066b4ccd2ea9e11c98c3ceed540d76fa48254dd087d3e7a8c35593263355024afbef467202851e4a6bb03c8c14451466120dcd2c90e28b2f4299a449b9cd4dce39e79c2829345e5413280ec3f8373a4aefba2bb6d5c3fbca572c9115dbeab1f2ef9f25f27de53b30f03b30efd7b67ab0bc7b678974b6d5a323e2bdfb0eccf41d188beba298005653bd2169381a79b55b024da2d3cab869979d40a59286df55941268488bdac54cdac54332d62e9e973eb3c2481e29b372d62efe8f69a52c265b2881b28cc926714a26fd8a484e6277bb1c7ff0e4f24a8db08e6ed7d9eef688e464bbf1074f6effe6d41bed92462edfa1f4e9dee494d9a5102bd96c029aa02f64cfe5dbe09238a0a1ec61699774e7696567abb48b7f4319caa18b45bfde64ce466207375c5d3934a7954c8c28df0f5d7ea473cea22c86903993ae589e19b3446134d2010848c270a50d5739dcd969815cf9c9d852b7afc9a837938a184ee8fc4cc51812271a25714d9be09aeb77cd72ce6f1a4f2bedcec65dc5f7451a5da456b1ab1bd062ec7ef1028972724a29fb3f34dbb58b884a02f46b9b536a9a6d9cd8618ccb2e5e5c694378732512a87c5ff9d9c5309a2ff8fa5f579d7885c4381f6408dfb6217fda940e69bbbe3b9235f0c517e1169946e4d66ab732bf79c119a88b39237377cf9797395f6617499a7a433a5f66bdb387d6fbc27545bfba76cd4bd72ad24c22ae6871724284d18925049f96d1171717fbc28c8242e7c11a3c5956ccdd1d7b8bb58be7e5a156e5656a45ad03eaf22217b9f5b46bde050456c8bcfde2a4878ceebcb4e1325631687472e54ddb56bfc545b5fa710a266471e3144cc0e246ac99e714337714463d54e473e5bb7b0a26ccae7cb711b0b095a660420e6ed8454828e2f2ec148c8ba9c55b5479bf9629527e0bac31a7b8cc5057d0502b8a31a4376a499da491ae183162e83a4b7f1bba2eea0834d46415689a96e1776046f8c295ef2b666c8e92653ea75b541bf24e9411e87c91ec3a4a5daeef45d54cb44b7e094aada95d1d117d39d17088c643e4623725db50e3201ff611e88b0da393db0f37152b1d8b76cd292c600efe3c08e260db8a53ca9c611bcd69026b1891b54b16116b648339f8c658f3c1985055501415d4452bd1435a52bfa0458bf88d678b6d3c5a925665e369a42b99ed56c4d633036f200067b83dd4af95cb7097d376ae93c14a1b5da894a6b4c536d9d65374e5b722fa85baf25a52cb0995960c928f8c7eae977f612771ece763489c6ad4385a92cc6c3163a68bb99a2b1366ce7c6ad39baa39f432a954b9d3c89d45b03e91a69126d392244e0c97a8999adf2c9aa91c436d61431ae35ad192606c288355a3b01004569ec63cc4b6762d1bc6b0482b132a5504d698b1ae5f5a13a24b6a497fbcabd61e9234f21a14925c5ce2fb89ac8fdaf50489f24317d7b6937ed23aa09a6cd53aa02fef3311dd66fd7ab17d45bb3a4abb34235a4f0d9b8a20566044fd0facf1a225f52bec28da1489f3e2acc33c4a9c3fbcec858d31d88e5d458d244e844d987c23c5197c657c028b22f9f2ae2d3ff48b79028b226edfb08786fa159f98024f3771a57c0ca883674c1c69fc70066d0cdd6e68f610936ef1a199cf8b6d318fbe699ae07c9579a6c27daecc6ea2e69cf2325d666ace39e71c3a9adb0cc686ab1555d970b56ab121a5a8394536a373fb04e9b664dab624be64e622c9cc5f392799f9dba54ce2684992668a94124793491a79399334f22f8ffdf262c3398f92737e739aa689b524d962c38e25094c327d928b34996466590f499cb8a23c74e53d963145a6d142dcc8c52dc618b538a3ec1863e4fe16d30728023f884ebc3c81ae65f430277f7bc81ccfe74fa0877e314e3373b5870ac4fe042a20e7047ae06975b03939b4ab02510e8401f2abc8cc040d63920c2013e8a15f37a2cccb2419dc7075c3d58a48aea4a6051a3f6d8ba6dab56a2963b40cc177177fc1fcee6eeebada6966a32f062a0a0d8768b36bd7dc8ed0df905efeec67054d63207d42e2b51ef98fe7b54dd3341dbe9da66d9a1623333333f30d2b8ed03d8c5fe15aa7bce7a22b67f06c62ee00cb6a8401da182233dfe3c83c85fab06219a400084c907123c7e04389a676a5d8480f17d1ae0865b6448963a229276e3880cb0f0750a3a459459c55a4e1473a3ba6b172efd3c01adffca665189ab6f5bc4da79cddb2575a2af984c4f39999fb10685777189518206f10c1834813831069e29d56a0e1cf3e08619ceddac3e8850edcae1146dbdc7e894a47f33ea078403b87fe8d7e6d22f8c1134ab73b8c473d37c63057f0c538e3077af9fcb862ba9f34c17660f17415ffc8efb4c7a8bd48a1ee0f766d37d2800d0128c288f1051b820aa22b5ece865c5dfd4dbb3fd8ba8eb4adf08a2a08b4fb7c4b3e1a250106371a5d4185cba04ef7963c4371d9c965b00638cfd54482d88da04e5fa9831228037a348186bf0c12c4aee4ee6f50a0a18cdb32e28cedfd01cc609c4ec627b8871108b7cf952576bbbe6719467c222d1725698a0ed3f445caafb1a6af551b6bfa32244e0c918669c40823b4a9e1f63724d078d9867c43407f6503a1abc208b0e68e9225dcb94bdd443fbc62882e9af8eadef243676840b48f2169fa3d3a20a6cb560724fa79c388c5ec2e1375f4f65fc026accf8038836f7f02fd18126707d6b00ae8f0e518272a6d4b7bbce1108d8788ebcf22d030c66d7e8c28593ace58d96237dec534ba986671683a52565ba021bdddea6e8c9cc9463f38b8613c9a332aa18640a5dd68f00989ef77eceaf08d07a25d4c89702c2231cba8408b0042e2dc2042069126360e224dbc0fe37590345cab0eede253cb0fa36580ed11af0e4c439ecf4818479b0f39098e01d71d220ccdf28834fc235c7e132ebf6d8c42f4c1e5d7f07926713ac6339c79cec207a440e1f2432ee272c8462e3f649e18f19cebdb579a9db693ad4111122fdfc56eacf1ced916bb734687f08dadc357bb94f3b202315d19855ed1498d03dfab4294788f0ab4a9f44be2dc20020591265ebe414450a48937ca4b16631adbf9b2038cd39d7b2843204f7039360c7a244e675b0691b338681e5cbec68ffc836ec1e558d359c1ccdcf9281e50f979456f4a80e00926aca8c496499af859c48d972d93332dca27e4435e74ec765f76bf4e08ddb2dba55d4c5615056df9905150685f76e75941573acfab829a64ed2311067f05b4d16d7b166bf8dff78135e85de66cf108d6480d69f1ae404c37be9ba46ab7a8cd4413ede2bbd436e23e4205e010e58341f8cecf1a504d6ba4990e388e734125f251010b224565a8128941c8420e6800231204ff3c086e41184ab0591f361418e5607d6b718fadcdc889d1b459e2ea76b43da1b21d2199e04eeeb162490b2a0bea8d922a9fa02fcfe9d4733ab5847a4f1d75f032d7a8a07f006e5fa33cb47ece13ddfe69c3d6344dab373a428a321b2a6a9234456989e3388ee378b6bedc62db76cd9ebed513e5917952f33a1ef92ed62f4d9b9a4bcb650d8b641167ec73c6398fe6b926770e75f3e1f4f15853e6128b7f5453a58aa292b19a5f9d7fbaaec69b234f5fd5308c4661f4429e1925d508d6389d4e15870c831031a971555a40b59f2a33a8dd5616682dc41257a593b5e3b939cdd7f2f5f8d6ca328b354658a0b9ccbe8df2cf3bcab3f500a8e8f24f6dcdb3cc5a1e2b9b5c952ec6d954699a3e8fcc48cfed9b22846636b39523b5051adef858aee321da524a686863db4f129a925d078384846edf0fda0d2770b52013b89afdb13dd4bed921da8e6bd272ffac8c6aded7556b793a9df94d7b8ed46c96fe90aa2ccb7ab6e5ab16961656422282967f5676ef1f0b68064d0eaa9ddda49c679d756f75d66a3b620418e69c1d9b820673eb2c95b56bcb51e4cecb6b59a0a1bc52865d4d71c80229651115a66cf29a9025621f4bf41bd9b66d3a643f7d7e036bd0189d96f2a0b2d03f90b141e675fa4e96a78c6842dc199edbdef1dc19b4840225114cd93c90d401a3101c9d0069024511e2207099e0ce9f3ba5bc04bfd5202d596451e5ca7facedd83c70a76c21eeb461912c2e8f190f25281e33d45daa4be5fbc283c9570fa3153d725223200882b397ca3204a485b8a883ff5095658845c9118a62990971b925ae5bc265d9180f1e4ee06ab2b33a9234ab434abb585508f7eb8565c804d6004f2dc47df909cc21c310cbe2be9c6f045f6c0bc706a585b83aa46c512421f56b6597e890d22f95e5a1a45d3b6aec28a25dd74122c20d231451748c3caed53d5e958b9b984d159b256bb374138371c3696f6222a0a71bda2c9d4e346a007121c28837a4568834f1d6008242a489f77427206daf565d27cfc9e4bdcb211ae509b57bb6d59d6f67831ca0a7aad21530879dabb5ba1695ad72ba870d2feeeab202b1e1b21ea9ea3597c66e90b4599234f2533d7a3e0c0182c895720b02c465a9aed520dc655d8235549faa661d641d047330cb455b6d26559d5703595792e27694878d8ee48033c9ba2416442dc2410cf6f14f8c08f52088509f16c83c6b07cbdec430b0625365f3ae40e559d739d2aa8e91a74459ffa703e5521a63599d698a5407a95d3a3a4e74a0c8836f5047e7890e142bd0d0e6c6e766d6af28595507094acb234d7d823ade356b53e526d638f4bd2a7854a03745d82c499c1b44248934f1ce9e7e4d25224dbc9b94396d5756655931d2cc997a4b4d5dc748e2d87858c42322925cd086118a27fd9a739b8f399df2bc14aaf218a2828636556e6e10b01467c4b8ad69de39a80255fea99cea73887655f607edaeded516f878518fabe7748b959577a92d281944b9224a9cd6a3314a65fdd23a2aab9487c6348dca348f52289cfc58a296e7c4ddb47b754bdaa46c499b6643d50d7fa5ece8ea4ad65396c5fa666334868a42a74d959bd89cb759e2baf08687456343349747ceb65cced7a5a56e47a215b30d55372cda35e52c95a50634b459d2340d49880ab771dc4d8fc48952241961ee4150113d9cd4832025dca64521434da1c98d5eb8910a29347173ccdbda94b64d09c84e1b763d885a7e68cbc7f9c71669711106d45ac2418f30fe3903d96d49d270314943d4f213610cf4700baa9c4cd2703db23967111255389f7671334d9bf92cb992abdc505194a1ab491bdef44839990535c258498db0140ae8179384c58c6dec8a2857dc6e36a2b67cf0cfd988f38fe5000b9d8d38037594dec8a6cd9266ed8d4cd3b41f292818228ee5d78c410a42f0c46d4bc4ed5b0f441cce8ae0f653336ad38588c33e0fbacf3b7bce6e56874ce2b08fb58992864e1b85109653724bb1a6852a0f4792069c1503abfb037cfd274d768235ac2665134fbac0e4ca1b9f7461e87efd041a812045c9bda1364ba66f035ffe692606e5bd96a7716cf4481ae9039fdb363613ae34cd86f3abb9ba1858dd9747d006f8fa9cb6d123717c24354d7f0849a9938cebd24bb0460cffd48645aac588c34097ef222e57b9376843469632a73f364590010de31fca479c91388dfdda4986e5d2b25025f20186d92c4501f459e83e945894b0cc5cf8a0e283e56ba5be9a4296b23e1247951465322345d859bfa21345b3dbb74a5400eb5b26b6c836b14da4563b64fd0a39a5db3b7cfab5c9e0f67714b143a85f6cc4ed141234b4b1872c331d23ba6ae1d411348c4350ba55e294240e03751da52b9ba558d3b02fd2d5bcc643b4fa1087b14fd7cd55d57192328232d06d4ce2d058a4e9db87dd57673b5756c789ce9186a472924770074e72064a52f92e619f7392ca48fe499f1a610ceab0deb10fea364b7c3a4b0d4fff78cc50ef3a1e1dfda7b561f74570074662833e2bc73aff5486b10feb4195f360ec5319c6482a833658b37b8e7c57e95bd5c6622a8d795636445e4a2d85893da12c25a31f30d1af18dca63c28039d634418e81ee09f479007067a110eb0906d714d609c91a4b94daeea88dd6656b33cede238ae250a275398858c481ab0c48aa4b87d0e06b763dadce69c97d56649b25867b0068d79b759d2368f65290f95350efd701544e54fefea10eea65243b49bba0cc4dd08a6ec12feb154d2acdeb56bf509d6385953bb56076bb8ba2bb0ce1cb5c93312d5e688cac3b18375e62b954a0b6475e76ac6724b5c17e20c2f308dae8b753c9dac5f7349c768d3318a33ba2fb769675027b25836086771511faaca2bc11a3e94dae543a95da1cd524718da67a5d1543b5927c71aeda8eae3fbe66567a3c77ba91e9aeeead3c7920f2af2611c82920f25d9b252b13c1b3d9c0f7a361f17d046dfc99b0d87703e9682b8338bdb9897c50d7784b8db4eec6e36d43e440a61aeaf8fa57e4d1fdab9aae3e4a85d4fda25ad8e91ec362a75439b25afda7421d6f4bb6ae30586d197dc362fcaae804658d2edc7aeac73724c5e530244e73553578dc46c70ecd298c4b13c94f08cddce1ae966f74343db93648be06caadcc46c7850fe59c23ffc731f49471da52b204b44c7a787d59d535620ab3b5d2851e1183bc2f280b6e76d2cb6989a9cf38619109a206b23fdeaa32954b93742b77b9ae073c31b266e5f93d3c68a6e488d704bd345e2f0cf9bf250d2ae1aaccdf962fd3c34dd39b5a3fe69b55a31e3316b577ba7e9b33a9bf2a12d1b1d6b8556158b333a999f5e6a15472d8f91060f470ca3dff24f9ac06f9a4e2fff4e36555caacc67e5fb6d26a6c1327bd8f7e524a36299cdaeafaaeb88ecc086609f378c7f9ed3361667b05c6c4fbf58de9f36eca6c192fac734748cc29ea08dc6e2ae1ed27a23ab373c37319b2a46db3d69532ba0fcf3789955c321dc9d472d29c241e79f6b8f600f0e3ad087601f4b14e400977b10b09ccee96962a1aaa333eb17d03b5a3174fb5de3f098057dda10c8ea4aab33135201b51938c005aa4418c640f39f7509073e20eb920df08f25c230224314d900fffc00b01d18ff2cd900ff7cda251c28c2402f028ba00efc6389348c810e5439e8291b4bddd0d61aa6b6d4d08925a3942349d3b24a2bc7bc76cdb64c8ff77bbc618c82e412434712f87086191c1b33d5a73d6c79cd87281a31c2083b8e383b6491a63f84bb27cb63c8f25092021a790ced2841a4a11363187d9d0e783a3d1287836c1136e243304c47267158c8da90ae6ccc248e8dc598868e914e073ccb32bb7d1d49368e240e8f99cbf5af567465431aa3485ab16cb8a23e38bad96eb3e4daba191dba45502965cfacb81d8d49b9599699a469a6e1438961f46d2c6653a55d373cb75966365d88306e7c6e7abe106bfa95abb2247196a4eda411dd24edd1662c5eaa7135b2cc244e844596a2db2c43b73f042cdce684cccbc921dadd2c07e638404b7ee74a6ea95f555866126787cc8712d3d091310c3a05cbecf200a55d47edea6de3e189e5018925cfaa9a8cf559a911998e4cbebf432671688fa4e9d3998fcf143db7fb86110ad9e5aa0e998e1efea26a656392a623dd6e6372655536a4d27e947feed9707557ab1853e796b8251da3ae8b71b3dabf5975f4e8d8019d0f63143a6acc207c692cd6481dd030567124694c08dfd0e6a7f274b1161b762e36a4297b578c50c665b1ce46540eb0906dad3cc2d8e70cd660e990f5cb48bf7afa155b3ce10109cac71a0e184dbbd2fec05703731c8059dcb973377d33f4e1861294d387aba9706ce7b673dd0fd661ce71298ed3542a6ddb581dcc290ccc631e298af580dfc5e5e5af076f7f70d7ee5dab9d7c7f71d6eeb272db238843dbb67f524ec9e2cee2de594e76363a1be53dfa08e6d0f2ee31a7a58d39cca5fcc966e9467663e486c7bb76ae7aefaab6b16665fde3d56a75956a65431f545cd556579dab2bab0ab9b76e70a869f63197e08e964bfba3459eab43a66687ccfbf2cdd25b10e6f2f482bab461054194cb73da454a97ed9acb76d9995c5ce486729977b13fcaa5de86d3861faacecfd42114f5e97294cb5dbe22e35d7e76f252013bb795fa0feeaaa7b8abce5d355557d9d8aed5bbcaf2d8761eacb35d0ad869b12db533edf03c1b2d6dd7a432673da725884386f51cb992890f60b1ae9dfa7025d03dd99d81f8a7e648d9faac21ddde5a0e98b3ce9275336995fbc7de7f7497efb6edc776d6656abcaccbea43f71fdce53bab0a02952e401c36341b37173235de8d6b6dffa17d7eb3aa20d096d6df34ce86415cd6e9594ec39dcb9291a1d705e6a8181735d4e1d2b76ae8c3a53076474fbbfa3b643b8cb48ba5e5dc77f4f4ebc5867c5ffef3f96ebeecc8017d7918a790ddfe0e1e2215eb5c6dd9a63572adc66bb3c4ba91f52ba42c6ba32febc6c84d8fcdd24dec86e7c6e746e846c9cdd04dd10d13ac5718ab38a24350ba7d06c16d716d966caa6c0f5966bc751057be59ffe4047548219b2659db6639bab4b3cc246beb3a9619cb36cbac5f9a5dc23f6f692d4fbb240eff586ea9e3919f3c1cf1f064561d27ed62cd6bac879aa6e91cf54beb346d49d2e81ce938d131d231d271a27374d231d239b23256b53c4c50cad3af905b32d2af30426114a3b27ea118e87d1da37ea990bca0afaceaa5f2e5a05ae765a0ca49d0c6bcf23b643bbea347fbf9e9a79bfac5fd74e29f6ab3d438f4b97a132fad73d6793516b0c1cdda54b101d52c8b65a1b2f9d99a96f2d098a4018d21f198c9d58ad28fc6a4d46232529ba558d34a364b3320263755ee887b7234b51fcb21f9542e0a11e86f68b38474234b9a72359ba56bb3d4318a6c8c8768d258bf5a5c92b456662d0f97745bc7885b0ab7edddb6b5f7504a791bb3b27ec92ec6a8b432cb6363f236d62ff9d9f3b3312b7b4c47c749e5aab48b5b8a01a976b55e5e562ba52e3421a2c80349fd97d4ea159df04094cb0234ab45f8c839c90e0ea84556e72356478e7c45b43ae2884d627760abf3ac765dad455828091e200318c042d73162713987e42f36ec924c71c472492a23a93cab51d2fc47fee4002d0a44d46026c40d6a2ba846180bc916546de1689d3b67c3d612d961fcf325fc63838079c1ce6d7de7d29a819d6bdfed5c4b573a64fd3ad916f7ee729d6d7196ab226d176b9711e84d2cc5049d11b439c109661194e726b5259b9c6fe0a3893c0112306ef21d988f9f1e7c83f34f01b2a0820cc637f80e8c6f00732768707439feb68aa35b39ae94120cc29a8e9176c56b6f8aaeaa86ae566db1beb095b83fba735f11894977ce7844ef92618474e7a38c12614188dc2d2c12452a712398a393b85d086475b9eb1869974e0f1674be7b18a190fde8de4cee8fee3ab24e89db599d1e2657abcde47253a061844246d4837f8830514b07063a0f11c640d6c7b6a482291861c4512c06e38e491ad9b173d0c39e41c1483356baad34058d43e4f5f1d33d4aad2869642c9d499a3ec704ca40b73156ed22af68ce396367933a630730be41e51fa2221cc13e062db704aa719bc6711ab769600f5089680837c0414f4c0818ff586b7996f04f0dcad188139d102579a20538e801135188383b457041092d08c24c0a1896b8e10635c23e29a594725a1dfc98afd4568e95cf4f1b2fc7d498b76aab7beb2d1c316f3de6ad7f2c56d0d086757a4b2fe5e79c73d2cb3a15106930a8fdfcd01e235823823b78de4fdd60336ac877c63f0679686d070ec63fb3dbf219a2cc0065caf02ac32fc3656478cba30c7fa152d51956760624550580eba18d6d30ca12aab6217069f5e66ddb7078fddbb48f5b4257ab8ac3eb129cf16f1e617ac57c2b00839f660295fa152fdf950597402aed6ad463feda5e801b23e20c9637f8905e9a47ee2ecd0daf36fc7d71c36fc3d57d71c3656c48ef8b1bfee2850d9a47d006cb959191f959e856880dee8383e2f569c9d8258ce4368e4e2012958f7c5517c052172044aca1815e1a1e76482a039d91a83a5615bc48ac8e241d537428a98086364b34a624a7a04856c8ea43ba3bf33e65a9b7c1061d1bc077a6237a8f20b1011f615b482c114e626d966c6b87863150cb06df95bd810c8a60dcc41043b01d18fbd0304e21ab395de68771f1b06f001e6300fe315025e241c985f1d0c63a4a81aa069ba93f747765bb584f2969f73431506d988e0cbcb5abb593f19da94286d01b63cd00de091161c47808ded026c6c3ee726cc9d2c56e18c45dc23fab33d0ca0a69ad1e61446278602d9633d06d6bf56a7524cddc85651a3b640ca357ccdcf70761c0e25a934b4a47d234ad2a4bae1fef8b33ad6a7401a15002bc036bb878b7aa7cafb8027848633732997ac303ee90bd78f1b0616cb462e62e4e806a5b3304b037373217f52626039a3a5f1c1eb9258beaab150b14da220867018221ce58c97878dae2917178687150a95fa8f7c1272012781467a8fe87ddb5b179b8ba37378f8da3e3e6f51869f8486218fdd75b8e70ce2d451fa04a24c60d9020c11848a8a5b0c414d8d7265e5d88234d288205e19500c640313c3006b244a97f1c9b258bea395d001ea3cbc391c4d986601a79ad4ad3c86b57b48f24a6a1511161c8875b0baebc8c23ee4a05571ea60aa60937125cf96ac3ee1a952b2f63c315169a94fe4caaaa21c51af9943caa6a4bed925fd5ed03b146fefb4013833a7c350bf2b0c1806c43773b3d833bac09b4c1bd743b97ab36a8ad698518200c3c4bc088b0cf838891050c6044d50667d5d6f7785d0071bbb9cd1088eb6ddd2388a36adab669da4b575f6b650af37da7ab5cae6af90f79ed15a599b4cb0ac4e56ccbbbd6d939f34f639debce6d9b6771747ff9047358bd5b1d8625cf5ab1ac2a0a7a697facded91fa84b4eb27c43d54b3bdbc56253973694d131557751a934950d8b6c26952aa59a27953d3da5aa2795067ea93a6f523dd214d32153c7b41dd480b8200349693b56ca3b5759e465e320598fe00e9695a62bc1bf68f607f80d7c799cdaec2eefd51867cc58c3fae9f4f0e5b146b21ecafc45abdfb64f16ebe03d2fce589d6be16c38c4745b6ce8c3e5beba4b877a8c335cdef2d82ff9558ded6ab12191bf7c75971a63cdca86de5f64ce0ac349a60e91775eda1f33477de39873b2fc636d873c070457837016dbc1ee04b8276d8c35e1901bbebcb343644d38efcb6501c15a9f82a957d5784d955e02d410c87df94c0d81b82ff534aacd924d959b18df7aee33ce90e7fef2efa5da547102add7a997d2720db7a1abbdbc7cc619dc5f5e369e5b6bbc34e6fd3610e56abcf1b0c1a2a491f453b340b6a16b4fffbdbb1f8db5be2374ce68cc67a00f1b74109a80b10dce600f22b18a1238c1083663e70ea0869d076e5f460d8f53c3ee55438d0a1c6aa845b9a92108e5f66d6a0852b9fd1bb825300cb72f801835b4e186dc9210aea961ca861a6a68030d358c5274e0e688db77d5196aa845b9a18d6954dcd0c6b60fdcd0c684303fc6b6f876936ea29b60e186373d51bc40938f30b6416d492b44b33d3e2a603ceb61805892241807d913893d0674f351018b306e52234c3e71ae411d8269508760180f47eda2a20b71e5c604db2d67435727351b06715397456eeae7a7c58d3d2062820484a5d89c53090832a57324759ce818ddf4c4802a40872cda968dbe9a4c3b8b56bbb3ae5dde6ea5ea56ab6ea5ea8e749ce83cb14bf8c79337823b8a989b65196251d2012aef62797892d2a142449a3e08964c20fbe9b99ad2116a4cf9a425df923265a90c4cc552b01a4bc16c8fa471a15daafb97aa6c7deec64324e30d399624eab1bed555eaabaaaa61ea0dd6589d2312ba2e6eec0151ec6e56f290ba96aa4c672773cc649145169ff587ce217fb4b86f8ff707be9cc5b1d950b3dde76f8eec860491b919d8b91690e73be796055d9d35e7e4780cf150c2639604aafa80ecf8c0806a3fe4e3e52930a0de6d966e8cf04d6ae1e8e657aa33d8752e5d8caba2aadcd217647757dd6599e5906f27f1b6511bf5600910702cf4e948ea2c91f6d141c630fe69f53803599e210e00e31f2b6318510f9ff0a607e8e746a673a4e3a4074c946e24c28d3d60f2733f366dda4ce6733919a54268b3e4d939270f4f7840da5e409fb03829255787681d9346495394e46c49fac81e298b473d2529cafc7515f54c4aa93425c968024d7296a2d20f50d0ecca735065a0ca711c27e79cddb71aafb7d4c9eefcaa06e1ee26e34e1bdeb8f2713ea72798a3481659647174a50522e36a524a1ddc65dc69c3b93d4702917135f609aa9fcf19e820f701dadae13eee837dce41b688cd528c560cd91e84a1883b2f9940e365a03390dd219334dc072813689434619ca2c7d53be6a54d15511d52744ca1991cf7db734e2520f440892b925886a4b4e10ba8f69065c6326b57c7c3326398cd12b7a4c9b8b386dd52d751ba5a9deb68b7a25fb96eb324a38b7192d6dd256818a13042d281a213258a249d2b749274a45831d39902c536432071cad53edfd7e9d121eb57d4314547d6af903b106f07b11c51ca0d6d965871c60f6931202f8740ba51ca8c28e5e84928a95c1e90fa48ca7f9b1154fb7cf7635a1eb8a5c855d9be4097a49433295cd95d9ffa53ac7f3af5c893cf69d63f0df5a9e8f6acdef4dcdcc4248dcd528cd21b7bc004041de50709e96adc7c22e3362024710d6a84310dec1334b4311e90341e33490314f4f323e413e433131af2f109a23cde1431b81ffb743f404488ecc89894c918b7c42d75b1fb28c2403eb608518fd6143affd8a09ed24a3e3da5a704cd299ba53539e8fc22adc93f3f2f42346d8f1729e263ad4cd23468634a68bc96894a8451a0032833a84468980263a17ff34646935c4ab915b54b3641228d8ae69cb36dce09d43263b61204ea048d4e60220a15b8f033ab1fd88131923350fdc00eb8c30ecc038ce47c645639e8e73a463cfb0eac77e8c149ce40960891b43df888d60e3fdcf9e744829ee47c446b9e93f80eac07233927f93c27b14488a6ed7104c9ce9c531e49a423474757d334242c4fdb983dc21eb90f9e9d8fb045f8e7882d42c4d91e458092209149b32aa7489abe4ce231933845f8e745887af08f6dedd092ad1e3c3b03bd0807d996b43d9004854b62920a8c67f721f3c40cd68102f018c182684182f1cc4e1f49235b9060532697a098b2db05e03182ed90c51aa11a846b0830fee91f106189113006ba4661346b793c2ec1103e8811f9a6fc8f69adccc6b6f783687185182051838861095418c1f887a89b441d373f2f47b4ddab4a3f548c705b11e80feddf0f30c584c676c9faa3935f9bb48dc11a29269493d766c9a60ae7d5d6b496c7c6a2503b7b866e943b647106f398b5e4d9e751e2481f4d4a99e683962436831a615f775fb77f1393380ce493e4483e7f0489ef08f1cc1221d26c0f9ef1cf7996c48920f19da176cd2224aaa6699aa6513ec2f274ec7cc4b6b473128f12a7a5d9d60ef7c1479c93b0458a1cf122b6a53dc2f8c8236cdba4d0343aa2cea323354a9abeb68469349342763e7e807dec8c72fb1c645bda3fc040b6b5d9196526499a96665b3e5ab0600405c63ff7810523213ce81c6489f830821d2801cb696662464962a54562676bfea635ad0e3630cd09d45a4d8a7482c6a04e2519d4b94141c3a91483f5212ab10c6a84710c6a84c1a0484ab9b1264b5ef99ba21b26ed1a8a352d37202ab70fd0f91dac4fdbd2623c5a6cceb9d9398deeb6ddca94cec5ecf4dca814630d90a8743110956eb8fd8777eeb3fe6dfa22a8e3fddb11d4a64a9c316f86be8ef26c475828ba3c665c121ad2d8015ad286ad1c7c5bfe9d53ecd329cfea2cb536ac7f138b336472a925d784a22b9ed0e00b3300821498e9763c1127c21a867eba5c82355297a00edf95d609e0b67648e2acc47d309333d17da21cb15a949fa0ca423e41498eb4a39fa328b7df3e67a1f7e099f61e2ce4336566b52549d33f49965ef988cf73e4d7260de4c1c76a3e49540e3a33a90c24743e52f9e7ac44659f3351e5238fe0b43af0b090cc6a32d593beea89cfed4bb035df837f36b035ed0e3c9bd9f76024561b92342a28e8598933d1434da86a4d244d931d228c87832c079d812c27b90a0aca499c993cd49aa0ae030ffb9c8f20a945822a11a2244149ce4704cd848490ccde8385ce48ce470859229c24681624932df1826f809790052d717e414bbbac61fc33840160fc7359c318e85ad5818708f321610c7416aa457c2a11a2239608cf100780b1cf650ce32367246c0b4644c630f6796bd622443df808db9ae7231e612cf422ad695b3b20710e7a0f3e02094b84a807039d8fb044f8c819e844d8e7fcf31c0954371f49d3aa1fd020310c37033b77498b8708e3a0138946b00db0d037000b371f1f0e3a0b1da911c62f803112bb152d515bd36e433cb39ca44698b604ba0da954098834f30388302628f3626974351220e644d77c4bad4074cda36a0f44b563525742d5d534f3ac44e524aa8c7a981a7646c2ae887055391b6bd7762ea5049d361693884a97c63a1ba39bd3374549d0983c8b5d508930094470051aac60c6829f25aed5be403f1eaabce4ec82ca2d480d71ca05944f637146ad4fa8040c54b7c11e854cd18c000100f313003030140c8784e22199542a6989413f14800c93ac5e7c4c17a7490c53c8186308000000000000000008906c0040c8b2a3ce812d9d96f4b2e3ac2fc30508e6006600f1870a7f4524ce961329c694aec06441751e77781d39cb57af968e7eaaed93be212e577ef8ce34e6562d22e4724029eb82ea42b85e3fffa5a4e3058d9702480aef3980f29571ffe691b36c19d09ee52259a1ce52d7851cda86c3c1ba348599c7c9c50c28a7fa99ebd83f934bbd345e0fce5907754c19fcbc6635a6b40a3de49e21011dc6607a30ef5b30da3c4a24d0719c8714828ca88a317593b738f8080a8876aa7a34914719a4e24e85c55b83bac8fbdb6616fb5657a661be2e8a418305e8b292c0a25b2da0b268c45514d824bc382a03893bb4c1086c87ca21c2227dcd929f7e1e848730fa8bfc11fa083a666521fd6543ce22a7d1e87f44519a37275541738ef0b782ca6acac3ff55ae4fe39f87b164638448a6126e782e6a1ddeeb338c0486cc611973b103a6c4a48ca9d850a5f1fac4fd0acfe22f4d05f1b8fce1114e17fc8a7e57431e04932b11c7852c920fc7f7a9d861b1156f92bb163097761831add1450850710ea90cfc24b214f1bbef98767b8aef0a034d3b341212752973f6e700dd954aaed94b75cff26c597b4839d6015f99533158fce8c377002654e12a1ae31ef14b222c378937b9399e7b315e5a0560abe03e2ba3ca10f1f54462058aa34746778c980e6a4bd44d1d57f0e8c7854a3d205876b2014756fb07d62f95673e0269299862b8d25b8e25d767eb3c2877a2963b03ac6d459f81428b783b2b33f2054e4f3e8e75d29cda6ead9f43cce411e4114df92798c116820779a97010c9cc4e5f102a809f60ff1e5ce4818ed9698aa6226e25caac16449007fa2b295024bf2abca8e0ffdd78e3e7ee5d3a0796d4b31d4de12625a97eecaf3423d49df12c97a4443dc396f4242aa45405ec1dc76a30b086d2f6c2d1d491ed27a5006b2b885915255535a955f7be4aa49118f63073d1a8b550868acf3996537d07b17a2130d483c1c0f28ca230cc47c065576b6cab06bccc08113e00ff42b1da0f875728fff342e2e2704f74613f0b97f510a70d25499a77f6f850b5995ec569357a7e83f4ac8a7e6a62093413a06eb8999fe6feeb368df91fcc76b00e76c9572c1e4ef4d3fa89b194a18fd637dde2651735345eb388a987ae8b2cb65c045fe473754fba915628b1656a8d38a484588c9691e9483b07ca496a9e0d5a6ea532c58a2f5f33cae70ce42dd3c0237ba1ba699b13dddd9079d98206d0a6b75380e048f80ac498fc60e4a8abfe2a8c02efb914b68c9b4eaf60350f8a757a8b9e3c01be32888f0424792a72907db6ce0a240b062d41aa3fac66bd27a7f5a7cf46a67023d1df70f07936d1fa7fdaf2f93ec39622d4f5396639570230536d9fc525c499101e80ba33a2dc591ff6fddfc32f76fc72a27041d28719d1d14501ed335b7f17dafc4dad9c280b28c5e14e9a25f6b7aa3600c0a3dc4a0dd68f80cd0540b7b9e5d2468bf06c5ed12ccdb4c95433d9ae1d8f63863309d338a4e5115f623ae253dffd48df5987e2257bb2e9460982f508cd249837fd70fd3d5344b691de42ad3b3a2cb08746b4c031cf3015cfe673542b8feca4eb0ac529384bd967559dbac1ae9f3881f97655c96997993a9aaed4109bd086beb2b0ece04ff98be5b037fe2871247ca3bcdb23b875a0b74c2461a2ec955bc94186ccafb1841e5f159902a5789602177bb313e6d49b1f924ffebf0b8dbd279ff21790c375b6bd464f313ed8600a84840dfc8d9be0e414e5186f43e236739d6090cdd71b06138d55ac78073e41fa60531cba2c6f8df52fdbb8a2d38c348ece352f543f048ed0a783ca2053e578f8e71b00cba43744971d410eab33345e57d10ea4f3e906fe27075dce27c77849baee5b0ef5b6d861297c0262476250db1a736ac9758e96acc5fbd739afc2bc879d6a4c53928323a8c818856e4938cd2454ab789c1206626d0de1f346eed57ca8423cfc325cd54679c0f8856efbc9bb0fdc00cef0d7fc933c4a9780ada1e3f004f4d45b7bf520784218d91a15c404d6cf772b579af79eb91b8ecfb6f6afd4bed6c9987281dd8d37a11905109a23274ee85b2a9cdec238bc5885a74512f2245027c5941bd9039110a556049bc9a9534cd3d4475f9c6905cb8e6e97ce8c2d2d323633760e50c3dcfeb0df30e9caeee40ac8d325d9ed7f525cdee220522d0840aa8ba673cbf0f9f0272d873f69fe6b6d9647b2461961740960bb2b61bb50ab02f3d8871294d6cbd84bfaa7a3e616b23093faa16d1ee689b11035974812172afbc6765c7f9496825203295092c6de5cf53f2e9d082d138f0f7f3995381e0bcf89bd5aa986b5d9161b4032dbcf0f142c51a84283a88d8bec47bd4c734ba55b09a13dc7b7a330d5f0d560a1a63420b3687dac6f26508dce300713ac000b1458ac742b7f98771b8a86003ddf3936adca4375218b51a9b9f70146688e73619b679b9c34f25febc0384d109c4ae9f3ed175e28703151d0d526a5e9a7002839b9ba48c37635b6294dfa62befa88c36cb173c11a6b1d25e7c0d60b1d19ffafab397520660eda42af2f6230676db71dedf85e427b7dc8025c5c68dc26dd1e96802b7e8ccc615fd7c489ce1ef6e08d7cee810d89f9738367072e640aa8d7944f51dbf00979e1ac9533aad0747bb88d6a3eb47d0bb4a28dc8725aae5fc77a70485e070196a30138a543094381dd46fdfb337757afd5722adb6299c54a67ae22b80c934c93d851ec5143e23fa3771cc124b43749a448fb31d59040e4b001d6b6de21f50e2d5690065914806ef13c7efd901362e40aa672ea08250082751351c427aeac05e6675746809ce344995747a0692f7e22e991bdbfe693ea921ba5ff9f89a719e8a783169c5010f900eadc473a291ff2289e9fe604bf3ae8ef3541aa2e09e5231b80d6abc96b2ff335ad6160d335832f192fd1891e80cce471ad62ae5344da540dadd6f43a4d53881ada5f720afab888743402f48a9f22d2d658a38c42e7aa0f86aabf4f3d692222d1dacef45ae182d2dc87ca60553fa4d9696e4856b2ddcbd1ea3404c3255f83f57eb90a7f8e50f6cb47bf864394c4fe8ba96f00f72bf21eb424ba0125115e4a6604f99da663b458523705c0e4f058476524a095e706ae699c82e1b82fdf4f7196a801d720eeb9654082ca1f52f1908b2e188a161adaa73402df5aff1cf820234e22c251925bafc5c6f86193adf10f90c3559bf4e2f9beff7a7fa615c262fda0b8abf2c4a2f221e583dd2853b73185efba3f3f453c16992ed66b6041bf9cc8acf8511a523461753a3ce70f4a0333e27e87691752484ae98aef0e4fbd0fed219936bf03846da7f82ba581ce0599e5a097f443cb009563410a957b1975e3e23bace715d0d39d09f90b41cad6b2e9fbaef3fcd80451ea24be909739897cf288b860d3e448f8eb26fcccdf8d28248d72bcdcf72bed7adb28058691701e4d296e2c9b6e070facf9c19294ccd036dc5675dbc1f1c2d6ae6917f328e2295e6647b7c38cb50e8a617ae129bd544ebcecae84ea589b9b53d9a7dd4f3b281d14ef271dd2f13fc9d38c456eae8e5022878331c3ce9a795dcb3c8ada7991839e567261b20a0f6eb294eb35ae1b37beda0970fc4bbfa5b4d751a7874257a09aa85531915fb08c790d961a7313e7999c59ca78bf71a0473de54ef1ef25f2bdc23b7fdbe8df827914dadbe1eeeb2ba9c3629237eb1392475bc45f3204dc5355e5b87c21d76f51849d0aa285dbc34410ef0b651ee1819c98398da5b358fdde5f729b084f7b722da9c8525e1450edd3f733449234550cfab2a40886198fe7504705ffea9c6ad8e577fbbdd754a337a5822265fa918ecd8d8316b9af7dccf25989f76afd27207580616fd1a28fd0112893e8995533b42cd52c9c53f38c45e0f92eb4ca43b7bf242fd1980c7a6daa20c8fd9439c0f2432ab3992ca170c1cfbebbfbf5cde38ead814c9b25a1b9a7ebebf821a46fd7343f776af566bed2183cb5c49eb22317bc491a6e782c3f108a72c54ac94e56bbb04a2a812a50df1343c706e29ce2b123d5f23c338422d71e800609de7d1c6abc209293e6357720f1826995c85b213defdf6254e75fbd25b20c4067e5d22f3dd83ab594247197371461b03bdac19cb1a46643bc1f815b925a10b096214743173ddbd05b1dafade7b8fc04108d4d1b027c667e88ff6734599c7b38070c4cb74b156d8a855e9a136596663db5deaa2df774966eececf36c1d744d61954abe371dcf323e40b7bed31088d7f80db738f9df5d2288ee0006fcf8f266a150044663ee07c6db2c72abdf93268824ec73522435348a52893cfb7ef562cab219a45832e08e136ce54e60779e5a9f5d0f9479e5bbb576e60a4bf5cc66a7684a2ff486157be0b92ac58658ace1cc05e1b48962ee05c68ac4b65d24b269d1d8330e3a137a710831d42961f84c30ae376659876599da552ec8e33e59754c04ff32e57436b9e848ad9ae399125b5392fb1cb881c21be339622dd730041d5b5176599aabe82abe66d25f49185fc253b95636af204e683f7b916e317854af07ba792d00f59e2f303138529e6eb5fadc2eb0fac3b90510909a91b65077f579e14fc05276f06586ae87f49466b096d794be6ad48eae45333164c91162e12c3d25e87bcde0fa9264e0ae8dc0dfcd43dad1307cc602f66abd40e324291761f51a7486e4e4bd75d79f9d6e9351e0f4176b752c0b732de39ce588dd31a621ee8afa3333ab91f0f44ba1d9fe1469c8c0b5944955a0ba2849755ab6b291f0341e9724f6088d3e6ac0d42d16fa3ae19cc318b09aea73bd555dde7aa34e619e1619539442ae3369207088f5a019d6a2e821c4aec80c753697eb0ffba096d657384a6bfdb8d29290a25199cbe5297e08fb4a5e7cfc42783a08ef8bee38786bcec0d4ad11b026053fe06e7d214c7410f1bc7e6c389d9387fc37e2428ace2235114805225615ee6f17763d470c5a183bd68c9ba5e1b6dabc7b47d8293f4295c50ad8ada026b969ce2666ff7101ed0815be9b0f72ac76d85bd69fbe1bb74459939fb0ee2fa80ca3244dc4f3cfa4be2b0533e7aff5674513212a1428420a456c01da017df95bade20c10da1e248dba3fe1c6e783cb2b87a1065a7e047048d7022fda1545f0e4543c35373d217a38f1c325eeea543a50c3c2c5e5081748ff896e6dbb06061277ea63ad4637015d8da4556ffdca82f693433e059cca70b8d8014348b3b63a3422ea3cf439e84ad34a746eb255d5d3364b95554840a04c4060437310da2b2480daa0cceef2cdbbfc8f93330965f22382ebcf2d0fe38a02059ec51237e420d773e563131d22d06989715451f770cd990736edf51a282df7553accf54c3cd8118968ffc00afcb1c39d580cc9c3d8ad723377132cca661e26d830459d48921612ec41b297898f917ea24946fcf012dad5c733e2dd60d6e9e77ed257277d0b460f64820d1e223ff74a4f568a3529b2030e9617137dcc221ddd023c7eaa92a380eadd7dd15863d712999459324f1c9aee1ba1d0966cdc36ba133f1fd7378e9385a303c1ffe47718630dbaf41ebc82ca73ae7f7c5ee7aa2b040d8cca0035da17d9cd35135a25446ae4268221ab5fb6dfe7e436ca8cd593fc1524724f4246ceed07d6ddbaae591de6a2a4ad452d43e5d2456f53d5c6ad370b3fc6d802190c887aca85f69a0a7a4c4b3af5226071c0b44729ee848290ddbc5e798d2aae7c18a07a83299eb1e921c1c738587f9f9043c106c16a87c457c1efd461739829cf723044e06ba58163ef9ab00459b99d338f06cd97ac962399a3faf4b5d082ee60cce750500cf31ca413a36df1d34afcf29302957280c1ac4e47278f8ef6ee218ca0f68c16e3a7a8a1971663c9f8b9a8cfc07fea813512f1e6f83ce7d08d3bdb77b87b73c4e2fd740fa003d9df081e207bd96fc5b86ec2fe7517078528e3e9c1d51d59038469e7a395564016a465ce6cdb80068b2c256bfc0c7976512eae68cb61f38327cbb3f127b29f0a7d81faabaee9818a63ff5860df046eb6557f7788040521ecc0960e4bce20fa09698460baaec877896212bb718e60c310426708c0422ca1d424f3b1ede4f82e4c6a5887b8dcc0d910627ae09866bc0969d8b668dd608ec883c6d566bde210173b63798679d82badb451fff360b37816f608f404fa1fe41bac76b8d5422300122adbcafa67f87f6cb974bd62491fb8e4fc00d670bed73057812ce1f12aedad6bad18c03df697138be95934a16182c37c1f51b8a4495372ca27cacac0ccc710a729258c808642181b7993f22fe445185cf158375a2a19c73c2ddea3ef6d789e4dd7af18650d316c6ff88f0411822c5818696ec18983186249d66fed72e0e492d980f38f13b5e25f0d1b3adef5fe7bc6f434509fde585170499ef0861faced05572f89d8cc7b45989c3578773dde218cf91a39d6710dfde9bbedc1482e760a91a8f0fe3f3c806dfbfc6f81a859045d374bb0ac60077aa97a58cb499b7a7e600da6c08ee7850a8b9b76de761434d8709ab92c05360ca130772b206eb388cbc082ed160a8aba487628dc410116adfe626f179388229a623b3bbaeae9270b10b6b4328f154a08340fcc0275096bbcdf0d081c5f617228d18330f6693a04d68f8616949f85d2914f3f34fb7e81fc2fc192ad2722ad359e23447258bfe0c1158d8e28779c21414f6008e122237db6830b134498134d3d92bf52ded2b7b9becc88157c9db697fba1347a7af91331629871b0d8746acaa6119a135a1a5b9c807b97658851a298fad3a9680fce101e576dc134fc4fcafb2aac2baff3d41a5b22ddc477843721f4c2114e48c89aed60f60746d4d4f727c844448be068e619840627d688b5bf02ecaf37e12e47bf813d967b570220eaf80f9b932093c301b9c2904c17dbf9edb7dcd54c988ed02eb3ee96ee74e4ea36625992fc3358f9931c34f1785e67814ff448439789f78bb81fd161d044cdde09f6a7cc662e7ad1c6e1503d629452834d06184a478eafab26b5f95d1789d3779b974447bf53c53bc879cdac5991cd2c1e402493467c8cec06f7f9c79519daaaa3e5681d12e0626b1fd433f3f7b95bd2e29eb2a7f76ed182e7083e7d567e3810d1faaf10ad57aca562a55eb84912ffcaa909971a127b1e7e1a4769c4c7ae739672fd4f20931658b49dcd40be773db5eb429560648d84e674cc5773d61d2bd7876f4e3bb485d7980af49f3a4c17115397a393e1c941b4d80a147fac8465773f41364151828e499016b9f49d3b0e60d56f6bcbdca324f7d50a698c4c42d3aa7ea3f1c1e945569d6740b09f1a09d1c850f378bd41eceec29efabda28d9ef4ac2baf39a58071ea9c3ac29493b67d6566fe174030ae6509b641d74f8b6b543fbf8805658dbba8a9ce6d361c011f2f78a975b3565af9ac1e61370d776b81bdae2a6258d54e91dc73661261b97c846dc89c60de9fb74bd1aa3e5b61c7f1b35398355df4bdd97a84c60f1c6314e6b0f627598e2cefe03d793abe64466f6af950c8aa1562fa551270b4be9efa4ede9fc1f644e1f0fb8501df40bdee88f850c4ad0e51f13f1a5992ec5a4f1b5c809d089a782113594d506cee3b29f1e2a6021de677d45071700c81d2a76933b841e15e19beae41820e72a6af5e872b48fee6c4cbf26a0ceec4fab8d3241612361602658d34565e1d1a02e9a707e142cceb14e2e01209a3c65327104eeb2b5cd721dd7f0606109a435abbf06e767ca409a9d8b9e782aa5fe0ff33213e4e9ce802b8d385fcedf1bf81d11989c79e8015081d1ee2aa19cc047c2e68ac81a4b2cff3b8d067520318d9fc7866d9eb6e641abf2f23bbab04eee7e192fca01b5e0d121b32d2296a8e93b509cefce74e56905cfd222a3d4bbec2174a3ee021e6449c3a59204b4dbd05c8d6265a955cfa2f703d0905905e2f6007a0e325f6193b88c33053ca7e9987ccb92de60936bcb945cf0cf82e80bc5b5dc9632a754833743eb4d2da165b7270bc404096c6135e32035c6d16a1ff82907f4dbaae3c38c6b719a294bdc08ab1943fa7aed911bcf005f2d3ff4f0e063d53cd83f6b140965b3478ff5ba3babe8123acd65c07b60993820e13b7a3428255c269f69080da35a163252d8fa82f9c776fe00726264e3dd7f183e3cc765cecfb29a696270b5ca98490f16aa09442b98404bda2cd5cec88fae42a739e38b3c08785c1dbc832e19993da42a3300f2ee4a0a08f802be55c7708b6a5abaeb405f32285e964deacc5d319a8999ab01e9f6e61a951993810f1b8d48f00bf9b66973d21f42d5847b03313be30e7662d5be1e597a3983e557af5975477a5e99bdaf2c2cee9ef68e1c21b3fad4a993d76fa03ab75464301bb00002eaa5f5707366c1fbb9c52e20c560d112a8c6cb231f9bbe9271b8270ce2796506367a19e42583b6cb4925cec2ac2d31ce7be33c44a36df530a24b6a2b9fc2834010c517dad905548f38b441850f94a7ed0bdc9e9552c8d9853c30649e208fe7375d7bd4ae27f98eb3b3511cfb0e70cba329b12a4480ed24c1aad955b4a6bd3b4593879142a6908984d327f74f7cb06df2cb49359a9c0e0c4d57d744da330e5a2db7e3056a7e111061d638290252e687495c9be3c5acb6d193b3856d5fd56356c11a9187500e9a8baacf7284e83cb86d6f973a310f3e7319cb2f92ec5b079bd80a4968bdeb93fa252731141168ea2c651be59e87007ce6799e2431e9aa4d7ec958b49ae95b46fdc9c33d894bb965dc93fa2b6829390070c0594bdec7ead5f2b2caddf4e4e6496b780c6d30139fa37884b859571e58cbae26a3cc06a7bed15ee2b3cdffb1e86de821bed6f309c0e8d2b256e794c5a2275341727fd5c57ca573f8d0c89d98ca3c37241519d282b28eb2012a4093877a3f15a6b3e0eb0f6e0a92c4a5be65d5fe5990669c57fb1cd4bd104ad973a31ea005569c5390c399904eb6fc435a0d6b7e6ea8cdd95fc3f54e4e0ba63e0ccae4e1f51ce4c35ee552cb6769ee833dcbd965fa56a5c72e39e8be62f7b830cd38d72d8591db7cd1280c89471926167bd38310186300e15bb8893ea188cbd834c4ae8898cb20bf60fd300a2f76b9356cf4e1b69b70ab039ace7a98a67121f871cac68250c0a86db4f08aab8f2c6663f978148a52af7d1e7d56a09a7b4823e21371192188f47a81a3764d2574674883813c396974b1f6d0df4c7264c2633ed69cd7b2164f6dd8f885c503a1fa6b765750abc49ca8ca320a512eb84f48b5e5fb9d6c9509368c7b8538d8b7fe74202e4abcd38ea7f5aed4f3c6dae8c0ce76c23e04d5282d23803135368499b75b503be359c04d6326023730a913ee1cfc8987af6c86dd25b985a66657e3fe4b13bd3a7d66b982691e47a0a8d03cd80056bf6eab8b393ee1bca6fee58a95da4e1c137ebc6c0972aa2f4c9573d6212058d086830d966dc1f63658b882f121c05e6f277b78ee8f15318d3259e7e5139c023740325222200cc3c8c208b03fb5736f1dacc65abe8f849443872a92f249ae4ba4b8c3830d9d50994004f74a11766c74375531bb74e5de376c109fe4fd479fb90adebe29276cfd24d4a3ac675c91b0ac1a0b920a5ebad5ddd2e1d605bdf0a3e3db7046922ba9aeac86c52b156458dedea5a2a3b2907b3e23459adc85bf034e7fc7b9a658a75c35fe6b2b8bf39fabe7f7f276547067ae12a4bc81862d7409616f212b6bd117a8592951478f7d367a0a8f731dad701951d9caf9b98d59f0894d23c836971691ac743da8b0fe3d2c36e51c0e1cfeb8631fca55a1cdf6184755d0acf9a2d8b1e3246e92bbcf18c2d888fcffd0e09777939b513520cace7276a781c9933b08cf6f3c1cd7b7209c40cf0e75b52a2fd12322829ef27d563bab533c75ec4366def312ea6a0dba0240ea64d01de8b585cb6812e38a0dc205d0cb0696d62055a45c72db4a566359288b6c73fcf74544490461044b416644aa1644d74b037eb6f5ee8cc0ea190375ab0c630377d6afba083950298e45e5a99f080ac07610e4ea305db8020950dfa4633a8cd67bff1eab99f84ff65a90d772b8c8778ae9a25dcb92bcabec91cbd2663af56a14cd885383610a0aef1f51d51a15b081d3bbeb6b14a99ab46e75bbf05f9f9eeba5965fdf35bc3686bde74a53918c2c757f894532aee5ffb99c5b34e803858d2f539ddd0cd237650af83a125f65a2902de226384117e902e6127854a9b720d088bce20f3628cbae86b08ddfae08a73fbe92ba5f712f004aa58ae71d82b0804aa66158660a311166267aa7b89e652e1bc7e725f39a851fc960ed4680d930ddd2af7c86449088ac76de749ecfaf3c18d81c8c6711c5babc2126becf5f41231444a5b957700c516b44c324886bc1f156ec1703e5f8fab35d1e35c341cb7955db2b6957aaf8c0d8ca91952353f74a4eeba59f7ff6091d10bbd9bfc32366341fd5fe68bce2e55067a8ef9a629bcac1a480383740baba92345e3ca939a920f81d765a469340527c9abb24a4fd490bd2a0ef7bf16f46a10a4d1e9b138be128d8818e4200fd440ed797eaed81856fb1c4822fd8b1e1d8d4a99bfe4318060e551330d5ca43b54c347fbdd74222f08795cdc024587da0da1208f91e98c4fbf81dc2480d2730952aaffb11cd2b887fb4b8393b8750729d6f4a4d16708df423189bd4cee530b95f45f2a3eb524f2239b721b6b7f6d05a4c164f906696848f6fc87083d60f30a53e01d9829b8c5361b66fda9c424e124e0d0691b19345b907295ec9f047f3e4a63d8e715f15abae031cdc20c72cfb94b0dd5e3274a70620595403611b6ea79054ed7cc191175f4fb1d068f8beb7f06f82949c12175ee0b0fb2c3701d015f063f8bd49358eec8d141ff473fdad586a8f6692a3fae6893f258382c49d6f51a8d2b573db5ec4a0d1f73e3ebafd60cb376e773855bfcb23937ca5950343c1e19ed47f5fdf7244d7c7252c89be4ff6e6eecad0810d047309588a2a109986d1da6001aa43333829ba13b658216608f8f7db91426674ca1b25e11a5ec834b885d9b36811ade3ce8fe7aec3fe2aca6f8e66f24408f22892cc01463fb793740198394787c75f6f4f742a220c05df78febd2cec8e400e31c2a164086dfe424aeaf92a1fbf1838fd593b914d0fd46b1156a492550ca85bb3df08c934bd84ea0d50f307d974bced043f398d516d838f6f18e73bc274eb20aa0347a37d1da2716e1c28f46fb115ca0af57d964dc9027087fa52a43ad0b36cd793cc668d7997c19a9d23406f4d25d43d3f29679579e768fe3504d1b1e0d96523c83748891613023ef335411a944206b5fad478a892d51e0106f8fa4994155a8b77c03c7ae1b3d7c486b8367f54b2147c0dbfb5c9e0ab894eb36d0ac850c01dddc2d86b6d7ef0fdd8327dee1f380fbfe98e9fd5e2f688d81468177702c09c1210ba0e378d1ff094b1e20b547204a623849cf64686f91e8792731c0c80abd19cb7027be79bca06201560ab5df22e86b1743d5201e67e3044b140d9eb79fe815c94aa8ce5b3ccd2a564cd3cb429bf0d29b93aa975e7b4e568b92b0ea3e38f50221dd753f1b987b78b05bca2c851d7469d5f6698aa01780126536e38529fb28b44385f2855a340cab6fe9730aae4ed54a914337a5c9df2bdbbb6609e431ea59d5d2e946207fe38882c38c3e9d7d6313ab8bb2d351cc4d97121e667b6d40fe182e1d533d09b90a96944adfe0388847ac3dc2e688ac0221915a6002afd56912989236bd8019a384761edcccaf562832811003db3652c19d65490425c0e46279b8d5ac256a9be5a0d85f8dc3e5949f9062b8ef4fc649f7f6abdc6dd95abed7e4555ea39897d868c3e0a2fe2bcadec808e0a19182fe26c7fe43203a2c128e0b3bcf775d2aba0a02c19ad57bfed936840a0ae96cd284eaefdaa2e25a3cd0417b691ded86e4ede4b2308a4a6d0757bc8f24a69e002a1c36a471534368b4a7ef4a9e2356e0153a88d8ff401f22d758f18aa8b36e231fbf83c2ce6ba88d3b3018539911155ee1677d6aee28c01d65912e57d65bbb18162f0d948204a5256603239a97ca2cee6e7af641045d6ac0dc397144c0b1e13ca1d9d5c31c6ef843ee9944fef0cf789860522e5b84e3ac49e00ca853b5c9e521464d5597df22530c24b1cfe86bb5f53eb94f1161ef147ac915bd0ad54f2acefdddebf0c972ddfc8952f699b2bfd1e3359e3b29c79c73ff3756d075d51561bee3b6c29e03871160a3b3d631d46223cc236cd28396aca6dd24da35003c141ce3347551913aa952f17e65b149c95c311420c50c04296279283089c13b886332496abd6e70461f400ed122abb5a53bbeb424516c98a276973529e82b880ad75c4d93cd14115ca553ac59ef2414e527df374885e8989f8283d36a9a42e3c3ce62a511e132922905ffc6ae699cdd7a9c217bb1cacadd65bf555cea1734d8e7362b8a47d7aa78e9004089c4c381c35920e08fa9e69affef8422caccfb6d163e4d41082a65bedc788dbb29f63eaf823cf45d584aae62627beb6968d2848bed8fbae320437d4137f9ea4548be152e7e3f8627319ffc5a3606c446b49b3c486c34aa78de099f7c353b530fcd5acaed47b12c2a80f76075b711f2c71053ea154fb1f6de2db7bf946badd81b8af4358812fbfbe0f8458d4d39185456145b9f38994707577b8b4eff07e3a59b36692ba0856d9a7dad5870b4ca0e1fa7680a1602767bd4c4c57058950beb714553d36ff1841dd3efb86e455ed101003e1ab622e52cf2192f2f3a136d46e6e52eebc8b475568b7dcebe295d7e951ed117ec7705dbd611edda7ef6f3a08ff95ae3d87f10cd3dda31930bb9402e8e217bb614762360d11fdda75255a651029c09a634c6c2bf02e4cde91d8022e307b8198157b226fccaf8be28ad2a967cf7798deaa41a3e59f82fa7280c2667add2141600752f6d203ef24925c6747f51086f401b2bb4b1b4ade721f65a36b8adc6e13ecbfa792c566d9e5403eeccbad3ee49e5b4044a5b354739a8562c974160a3c3687884087050fbf476ce280965b6a48aa60445200945385df5d4e639f81f05469104476572f3117184e9f368be2a88894484732c19a749b01369e45e20f4176022361c412e7a9404be22028636bd20a81feb00eddf0aa756ec875e7b4ea6fc68d0962e5d10fd1428305e6cf42323081a1fb698b0879a002c2ea765583e3e59161c40660410ac62096734efc1c97884d368efed648be47f18d4348ab1221b5637339aa925d9d696f8206c54aa3d844b94b3447732d6402f150ab1ef415a6d0b1d0999530b4c111fe119f76ee76295f9b88481688f6522272874e8796855d91ed29d28485c18c0a16a9fe0810e0a1ad43e4d2d128fa3c3bbf45b1b9cd619b270195742a642ed36c429fa99a6dac2bc834d5c7ffcd5a4a5a91d18674c9e343b576980d04d985eebbb6ddea0b0acab1946e4060907e6c47f0c900a8af63a3fbbcda38172c9898d80c0f8df7e40f4ba33e04f5820333f354e074874bdd13d440984291f846ecea999c2912dc692d652e7723887a531fdc4efbd2b9eaf96ef66dd92887a897b4ab71c08992fcb119b7435cb948ef473613807f067d595774f0b58293aff6a341029c146b1973d47cc0c47c5025df6c5255c04dacdde73f9a5fdee8f1d7d1dd473bfc36285166888e5370d669779210904aef81c3cb5a236dd8b9f001c0f1ff4e93430af0fe1164d5abdac2bce509ac289de25781bcab6a33545a2facf9d41cdaba5cc1d18de5d2b95c7583fa6c9cf16d7f5fefc184f3fb66179c3f1b7acc6d53fa37233c2e4117116c35a41aa7ffa85a3860188c07aaaeb98d6d64cc799941385b674aef5e9cedcd503cea9acee6e1461f7cc062120bf6514696d3defbf4eb2ee233814f1645546b3a3ab4fa0932463584d5aa74e318ec7a36395117d69ce115f8508406004584744a3e9821e51aa0d612a4868906348e14d0a0b31e664b56248b893a233982c83e392a9462c5897f8566f2909bf5c9020017cd195316193939a771e3bcdefc8cb0eb0d007c5e204b4cc7f5828980867b4ab4f304c76c48566582489d01a73e04265c82d79e4c728985728103f673bc76f9760869271f75d1463dd19bd30de06c0a52637255ebe1ad2154f298b09d629bf3e726697bbd38ad3007a551364305c8b77c979bfd65b43e87d6f2663531c10ed0647b34a5dbbbbfb93d20937c0fe6d423992197b9fff0c9284ec5e8d6a5c3255c950f3234923bd056307a15a6fbef9b255c91a0895f910f842d1a3d113cead803c241a0c10e20c8efc08b782409efd1a6d32f6c97b5ff34da639fdbcc6188b245765d06f52092349ed4ed0320ef0fb4b5208d81118bd3cd15f50601a93bd917b500b60056242188199219fe10d91297578229f70d9ac4926f0c879c1375239173e80b0252fdb6986829fd049cb01dd3a12a0a1c49fd4146b77cfa05c378d47aa99de654a9a89840215896e12ea050016cce66d37a6c3eda31237602cc2d98b1d58720184977a8ebf4bd53f8beaca69d547d9b0fd9d9765b78be24049b3baa247439117a511beba612035eb8a49cab81bf7da91e4be9dd6bf5078bb4d6acbffa8355935de7bbe872144278fc0d82d0714b66cd99b9f083db8a9646af9d1cb8292a545fc3ff66c38b2c443e5065d220e8e6382f90c7b0f8d3070ed2ac465e511098999d849703cbcf019ba0ede5619509a93e8cb9ff0ec5db55f21415534ed88408b0a6c825042d666596d9342ada7e90d16ae41026b3180a9e248dc265d6b394ae70dcbe40e22c69f5439f18f59de9537a928732b467801dd4493897a8b99cfab63b135a7063e822c34b7baa28989926c4d32a9b5282b333825360a6cd37cd8b71d15939cdd8e964c6710069d044b112083418dd775468e4beaa41060d3dcfc78bbc636b1dbaa5de1dc2c4e1973bb26c97d48fa00c6fd96ee6891a1189eef8ed1de6b2ea42269e20f37c7ca3180786f3705512fa50d6628d98c6c119ce14ec2a91dc14baab358ff956ffee4ccda016dddb0b55e2761f4ca0c20b5c1e7e92079646a311fc85db9d928f1919f03ba200ff7229a26b2859d8d3b79274da1eba206688b751d9a69611cbb35b4ba75e9f1718a4e65ff2ab00f48f2a416ab81a4548dce95c39a09387af3c00a0254b7cde646ba147c9749d725f48ea799f134d80a6f16eba8f8f9c0c3cd08cb3563292792e90c292cef5169cc734d8fca41be46055bcc586538041c6927552df89c72de84f613f621551caf3ef7d5cb64bbbda73182ff36f8bc0d07f4527085e2d886819a320e179ea670612d13f0e1c4343f387d288653e49bae6bccf0d04403f68eaa19a3a20eca960a859b03be9ec153f6b375fba884169723716d11d5f81fc6e01df8bfa604e39323a4cae365e032086fbd2b3e64c8598d2873d3b2e5cd74dcab745e23c3e07fbb3a0cc7e5867915d04b2ce993498bfd2046174ef32a38b8ed608d7a693da65989a28f6170e30f78cca041fabea3ba637923e2bda37a679706112db658cc9e623f45a17b2ab71d0a109134d639e9ecaad836f44635313d60297bf4959f8fa78eb2179a3849c66a644c4381237af44200ac1b5e1919a3c67ef51c2e5df86fe880e51d9cdda92f07d5f2fe1cf840e68319eea9ef08b9c38b5a94b5a89711ba515088f7d898d3d265b05a171e2a084c5c11bd6a0f8a5a633ab42cfc1778d825dc9d96ad040b87d805a39df76875ce833faf2ff4c0b30ff123f610f17f18572fe389237b8cbc104ed405517a9f23a932cae36ccd0fc1cf071b2681e2ae6b207dfd09748abf8d2254806b1e086f3887d91fc864bd9821788a2f5b5d1b28653501c630257e95f75e1ea6327fdc7b76cb628bfba703f8f37e969f720053188c436c86efeab8a6d50e3d67ec4a15ae9251c3200a30472d8aa8db7de082f809b18d9fa54ec8ed6099d3e2d61a3c85f23b6ff110b31c60be4d844c8e62a86a70be87bfbf14064d5bb474cf4eb2c57458d8c441927383ef688e7f0ee01dd2de1b8e68f4aa772a6c65856e172fff45adcb31292b605d2b01e558753577179f7079da2e1d2048db156803af522f3a0d342d46c4bb6cff5828f8df47aef85085124e4a968ab5fe1ea9afed650b370c1753d058ce19a2a8d202c54a6153f9ca0174447a0ed6af3351dac2173a4208987a716884426c08aa0d108908b6a27875ce7748a73774d7306daee790f11746d325474106c5e3665dcfdd340cbebada84b7b35de0f026bde8af14d953b2ade954163730b0df3c7e4ddbe3093667c995c5847811b00a736e5764d1b5f6c0278ea952865c247ef76d146925cdafb69781824668bde8b6145ac04dc04f0344f776a0e0cce0918063e2e0ca459ccda40646b2a1a4a93219b6ed0e30ba8672cd351b08562cf92d693805d0efdebdff2fc78101e9dbc6eebb2a88590d1c1389b3d96b692790072df5895ee2639403bff46bf7da74e31afded299c04bf924320aff19198cce5f9932fbc990d0d504bcdb63a56d575a5af41510423343fdd50f42143b99a564c7d53da6de6866da3230e9db006570881f57dcffe2b51886e6961bea2c7fe651ca03ef4a4dfee64fe46d4f85924e954603460e4f7920e826d29279f0db3c1ccc0a97f697b765ec8bd917a7a55ab60c9033fda5df6d654ccf16db03634ea1870c257647f227ac4b7720096cd6c444c2039f4848f8a2f6e0eef2c10a72844d4b484a2f423e8665002bd95d82400bb127c27bc60418fe01fca4e464a14dfeefd59c9a92509e1f2d0d7a27c86d348db6b74cd59b99f68036c9a746ec3e08d9c34181aff29bac10e136235d3bd60709d54e230f508817c1a1f34577208784455d0814154b39e59b449d60b2df33e0b8524be44d8ce07a1064b497853e0233d79c1492b6e09e3026d04ee73d7c789d9150a9dcba317401e770dfb9ff8ea6a9d4109e10cb2efe50161fefb58029b1516ad2c897bafbcf936a31d35fb662f0c494d4fa932e97a36644fa8496b28fdac6b5dc06a17ccac23df34a1329f0a051039b02a02080d126761ce5a4e7d3a95f761376a236a056d46d1c3c40e0d46ad515ea14c6c1390b78b55600c60a01b51405fadf35b0d0a44acda65ce187b8046abb0f4c6892008cef78c25b941c3bbd3e86af153d7620d2bdc135d7039007765317a0d49803940c9e8b68f255cdb60c58db4050cb411611fea2e57abcf9edf081ccf3e62fa597ca58185c970c25094e886a88984b90ecd29a60191ba646101200c6f5ab5e3e125b95c0e58fb6aa18acf07157320a9f21674c10c206171a2a79b4ce27bbffc02e0454af17d0963f9515058bb4a612ab331d5aa56c1a127ec5eef20b5453269fbf53bc7aadaad27c47cfb147ebe6b15638e3fd3628eccbccce3ce728e97591349b95bf26d666fc8d2a46ec491204afa19ad8f5e1e5f493aa84b944b06bdfb5b5727520ee220add950135a082f46056facfc5db180d4e9b0e570c8071432dfc272253968a600716908ff4538e47aa9606bd451940983cff244cd9219b2c9bc477526d931e10fd91bac7e8b6735add60fbfe8d8c98f30f902ac88849a9d8b4e86abae689b8402e1541aa1de94bf34cdbe491845c11cdd26707f80ff73251acc44aa63eaacc6db5947125a0b42714efbc3e2ac983ad6355164c2cebf7c8db8d0288cf3e540274612796458910835bd40a5de7002e28a9980d80832eda05866f60c5f057c294282f3c11b267729843ba9a6360779704b491bc1603f9ac73481ed95e51234b8c18713b6b8992244f7834e8303278ea41b9ec0e84fc381c5a9e4050fb2c03e4960ac412e6207fe541e8898e5500e5b9a78f9dde2e327a0a9720295108e40a73e38c0158834d576ae5cf9ce8a49a77a1914cb37d3466cf914fd169d477d88b92803145971e82ff8cf891360c43148fbf90379ae4a60a9e19891a1d8d9f5a61b276b81326e21cd046442614428cc45e2879e8f22eb2ea965216a13dafbc876fd8f747fe03d22b90474216306ae50702cd09bb43107785fed20c45b825402497da69bfa243df8568bcd822f055a5924c510f7e7418b5fd1c258b95dd057b84a1b6267464e5fcd6d52196b61b7641e4176a39ac9d5ddb6e79fb6bcf36a3d0154b0e7b06ca090cc45d92ac6e9065579b7d54229ae10a4164193bff04c41b2708e261f1c0360061ab230cacaab9a76c9b07a9a8d2b603dc3f0abc68d4e3c8f78487dc8cbe6ee04592012279eca3524c20c379f3df949532b343e30b3ad16fd512dd65d2aea42eca507abf12982f04542188daf190100e61fa0b780fbc6ed28785751bf0bdae9e3ec461e2fd657108bc7d795c088dbb750a7e9b484c05b29716fbc94ed934641fd11dc3dee8025104087da60db7a14f4b51374aaedb562ba6c714ed70a55fa7b6d8eff356ca8c8e748c68e3f18ce3edc80e363443210eb52864f5b25be488fea00408c6309fbd76706f8a678aad7e177d40f2786e6c9d81331226d83b41c87c8ce877aab7ed2e718855fe72fbce0d0ab595389b815594ead64abc46afd818e24871062374f41004d0012614530800c48bfbd8405c5f6c195926012e0b7fd8872c3fb053891191a877774c6fb595026bf044095946a61005370fe2a94986abe72a51cc1af8b66949d376caeeef8e3f3c39e521561461f86edf20fa5f620f2f977cf0dbdb10ea0fe3ddf4427cec5602fa4ebce1259421785eb1cdd535f276d9d03523a58625fe2262fe806b0a9c185bcad732ca66d5b2ae8166b55e33c17b5e16ce5fa4bdc10d0dd6a60c1264811b705db4d3e9dc868a9b7a17a6e57bff339d961c356c7b064cff57dd6e0dbcfbc0a18a90d73186e4969239ed3e7aa6bcd64e1b66fa8114584b6d17573de02d131138f9986c2c4eb6176a0e96740e2520df3ab79f143ad80d4ac5c4d99facde8723eaef649bce3def92a587c43d162687272bb81523834e06988efe6ffb9e22cc397bd8d45c5bc98b10911ed06b6e0dbc5d8a0dfe7f561d7578bc0b8d3055955d9ce91e16c9f650f8d4530dd8e1daca473af75f2e796f525094c3dc8676184ca9dbca9e2ba6cfeb0e21c5d9fdc61cab265068ed606ec950829e2647590322d2c4d71bc6368dc6802d9b348a349a3479a33c785bf96e91cb2be76a76f9b4974ea55bdfb7a835dfc30a3defb1acc9ad43b55de1adfdfece8b8570d4c2aa2ccfc0cd195be9faa7a83fd78263caa037dd401f784a136f174c0f040a111caf1de04e176098957a034857dcdc99005ba072abcd6be2c629bb8839aa57b28ff48fae72ce0bb7546af274eba5552b4078089f47c21432eb8c6a65e71334fdb05f070e35f7e3976f655c4104eee3095f46500b5c786e8f497d2a0a1162dc3952023022189af30ffdcd428ddf9fc2dc78c8c0cad0e6566e209883908e5202fcd1d135124842189d8d3bc473ae23df9644864ef39d8011545d0731e99988f72a38e685513a4f21b316b8a838b771e0ef15391dcba28f07064748165658c18daef71e3ed2a33c2f6c22370eebd1902683ff9c18a88bdfeee28a73dab77f7c6e5d0ad54bf7d0433226b9d4b6dec11925af52e8f18411e34b17b74912dc4ab2bc082784b778a94e191c5f5dbcacbffd0c73febfdef592ccac143f245314623006000c6905b306271647a27200f4bd80e7b07ccea9d53a0772039417fdea1a771b5c892f31873d839c2015f05a67ec34a50bd2ae1648e7f552291ae50c0c7cdb110ef87237f5121a1afbc7ca877f17904041c03c9fe32fcae9e2f5e35fa42fd733fd4a5a9fab87f916685730edd82a4aa1a38598b5db6ea16eda398b576a5889e3d65122694df4c8a60f203a4a5f8e46a2c59ba6d385ca98b32ff259d1a8d7b86f292fa1697eed94442d0c086247e40ca734504eef28f2064d8f437ddd48c3cf672683c0a007d2d90614c5c52a2e1ca02000593c26cf28883db4ce989a6415bfa4d291b15f921f16030a5b1447b87ec4065832165183c3988b3932ceb84bd27c1d6b272876977e1c8cb1788ab4c20f4d1977780894440c9c624784254e4d185f1d240668d24c3c9d4d4136da72d706843f3e00b82da79c93210eb1e16438ccb42ceb8374d70689535580c6ff1eda4a0d5a8c0b7257930cf490cfdfcec0fdebdac0a7aa2243c00d5d51c661439c64aa885340f54f96fb9fb850bca7b5dd6365c58ee50528cf2c277d88f243236de7fe9459b799422a6d0fd860f1a3e231ad49b102d071c842790bf67b305a6c40435da36443cdac016fd00c08a00f5d10d4c76f06181bd9a4835e3f26992e5937d1f8bed2d85b917427f317859a7d33744c9d83e00822248072dc99af8a1c3285fa1aa40a91860c7202983e3848eeffa50eb3df1990437d644481541164266c0da34f5859362e5279688021bc205367ca4629a1ea822ee08d0c63f320cee640c765bae924a931574997ac70c6e10d82c691d278461adac2b0423da031d1561644444a3fa6e7fc3ba9671640c1112fad31c70f2f7c160527b2e99b1a2da8df0f27ae02407113c134271f93da198eb10fa5a486292410f0190cd09163113b8fdc760c991b91a4e4f855c2f426187a29ff8290a072223673865d925051b947af8c6b0badea767456e875c6b44587cf33729b5abd7f900cf52ad27024620633c3c09ebb91ad8424664e35b05182124cf3fc99a4f8ff8c97faa60e33d1faab5c7fd7e26db3e67b7f5a6017b5af0f8e982b55ea7fd15e430a1c927b5f9181f590017d15cb7e4d67d373381d81f45b7607a55e6ee345a7892a932c91b5104489ce4d1170f83d984d96c3e9c4fd4d8271f2629c445572e97e638fcac89178a5de98db512d9729038bafa8f9e4c084f6a873e5cbbf4992e8f2790768e8a4b67dc5086d93111f62affc1452bb6ef793da21c7d3a86814aaa42dc52bdc8132c94dc027006973757ded21d284804da8c8ae4f46971a0dc3ee9377a7892ceaa9bd17465072f115d6af2c7ac020be2921e6f441c30bd3aa1d86291a7c510f4a023b6f76b2fb9942770b87596427bbfa7df6e50ce74df2e795cfdeecc3efdb8f708a81f83038d51afcafc0dcad0ad83788d1fc633e60133ee4d709903bb055d1fbbd81174333a805ea601837b3d626edce895b487a9f5f049b5e8522a50f91eedfbce28c08a0b8e2e7613ff50cdfb54c844b83debffabbfe4ca25d7161e48b5e4563a74facd36ef81ff7f3a78c1f2530ac47e25b7dd6bc136b532d5a70853514fc374cae18fa69c87597ee40ea5aef7e1ef7783d1ca1146dd641baca533521badd3ab838c81764d3496085f3bd0b9cdcaf02b0b1de34d56359efb1bdbbb8fa4e26cc1ac5d2000f9d0019abdb639e75e7e343eecb24221b3d1c9d56de8509fd00d3f29fc7e752712ac340fbd2a0ca24bd11bf90082baf7e6d12ea35fe9ce5d124ae3d9fbd08b42dfd7b7472426632cb4e8a7d85fe0ae0b2da719ac0c25fc8a59ec89eb59962d094b90f7791a89d46b390a1615dd08f4724026426dd12ddd7d356a6223eacccafdea4e25dafa832e52cb534009ce6d214a706de2fb70661e14fe3f80f6fdfe456fe4a24da5cba768b816d0951e49c74e8daf04efcea86b3071985a3183ef06168056441ad407a06e56ee47eeaa5555b18e073c2a2c4b211a2e41b3b92004e16b4ac483c316ddfa73db8e0721b1ec967a0f8d4dd5185792338bef0368efddf2071f52138919f5c576cc38ecf89121f06996db39bf770e8e62f265e854f033790daf9423f97c7e53c44afda64d8594f751ac57b24e8ac2ab7113dcfd552ccc2daa2f2b51badfc76fd67e948e1d576eeb0a3846b3d4b3544e600f137885174d4f0f3d98dedd445c803debf723ef7b75d3336790e015f9889f0fb2de66fd4a45eff9115e4697952b202f220b5ea9ef9a7aa6c4278978880c907f1f8bf52aeba4085e9fd8f8f96e43ea79f820f3d80dbc82bff953fa1d33d39487c5b8c8d601ed108205af745cf44ff29257089965afd48cbd2c30234a67f94a3da33f82e6bf7e1159f62c0d565489fd19093abf0aba1f41d93b78f25794d4b25beafd342a39881ab3ba9fdf59a2721aa3910b5dc5c043454d6a6036a731cb779f3f41580a2c616d031710bed04dfc4f207bba64823c60240bbc12bc23f3c2b3fadad5fa4a64b7ed00427efb7bf1dc810a72f5f9890c10dffa877a3fa0a6538a7f685491c179303ef16b1aed6784bed867ef242811fd82bf7f90e4d964b49cedfbda927f3570d2590fd3f3761ffd8e3223ec430bfbb864c10d3a75cd67de8172995594a909f074e52ed8722e5d1c969f4c9fe1de6265e5bbb658d3dec76f82c42a0e3af3a47d13299f94cf7ca70bae5b089b0827cbe93c41531950bc3828757ba49b638f214411a76710a29f087e91bbf1a3c6e2301a11f0794bb09b29af9f0c03f425f7ee755d28232096fa6d40d1d5feffe8a1420dcb66ff598e0696912b61d89f27b97d9af79216181374aa1c037dde32208406ffe0ba9439b9b00f5cdeb15212b7079574db949f865fda521e5d434cc53cb2d596f182387f78f21c0a3fa7b0295f6937911c7141033afbeac88a35d2af2179df4af777f01f69c0251b9bd3eb363abcc0dd07cbd8b608039a326b0920840a352eb71aafa5c7ded9c36d1d69158d11e4c86143d537350438481fb8c27b1bec15afeb526057f30bf48f968e7ab9aadfa165447089fc6b5938dda0e5963544379088cb003b376846b157cefe43a2060244d818d636467138c85c693859981a04b06910cc3f5f30b5b43e1def87e4c348ab9c93dd84e03a3171bba1a014de70cade979afc8db6450f59709044784aedbf41e894c85a4e2e5cb2e9a82a3319bd002662370dfce9bf793c051a29278741e6ea51e5adef702857a2ac1794259f22841600999d4c6d7fa1245bc99d7f9ad4d7bf1297e2ad44b62971c4ea1670c14211071e79bc427761771c92814b5ea1c66e4e94164f1cdd1c2d1958f7652f475ad5b416bfeb09de8f2341d63ddedd9f85e7e8403031782384130664e7a5d47200e7641c3ed4707f501f1d37d0220876bc8dbe0a2163d29c5bab1c6be9431669cbd6b4b9d8e4483e2ee2041434303a101dc1d875e67216a861b34b8e5f08f16b7b2c1f67275fa939fb38190fa979e90b81c077d77f636d669ce63791bfe4eb5f20e1973c7f5f28781e3b0f3d108e0b686a58232bbb933df3485959cd06eb1d3dc4e9dcf048c044be319f4b2252cda97c1c84b28cef9cccb5a38e53c5bc416d70a47a93c478cf302bbf20a9f075ce17eca63276d9342a03e10b2764ca1e318babb0904f12905f95bd9e816fefe42d344cfcdd1158719d679eb64709f84014c574c37e58d4999334b46e1cdc6db7b18943db33ee47ddd0225f01362014fba1a07f21ba661f719d41f679d4ab614f49781003bd4b41039f376205c4acf9c631eb7f47fc5249850870cafe3b5bae97d30826fe4e41ec94dfc4dabb547739d25057a574bf6fc1fb4dcd7b131ca443e06e654d92f5911ef6ff97ed2f36f2da9093edea21e1937249f5d3e205563578b311decb55c8771a71ff593b8cba8e7ec87ab7bdf297934e64db5e2e0f4118ed00f729ee65fa27a2fb621081f6bb24f1d1254bb9d4870d886c1b3bf1f1a8b380325bad317a19a9f4383118f54b474bbf27f8be028e1b42cc1020933f17679b0efa2c3cb958b0df03f946725a2a32186656349a94f7bd3301b60a95c8007ebf8d0c3fe899c7c28a5863024a2a113442cfbc41f497247a7a75304915772c0da8fb938e229354a76b36542f1e550a85c2046df6f3f151aef9396098a2668d133dc77527abc0038f5cde6232ef67b33190acf80bf6c80afbff4b59f07c1a9e471a89773a6755e518b2ef201aa8ac26206d6dd30cea1909111533f869b1ab393020dac5f296ab78cffc3d171bb35dc66f9c4d9c98ce85cca146e23c68d5aeac81ed10386d4399c0d130c455e5ebb73e9293192aa5d6a3187185f4e4ec8090cbd2b9a50bf6067028dde0b37cebdf97478bbeae82316f8343c2655319e3f3d693f046249673d0bad8dcf30e3b59c5fdb8978cba2fb2478a3a84aecd849a015179f159234d48e83e072cd6acf5d7101f3623d9180f7b456870848374b3f2170b640bec86caf5e755a7cec0e50762326ee6af6938cfd290239d8d3043fb3c8f62152409807d44387c2c0068ad9b8b3d1fe0750a873de7f1117527761050f49910113e01c49f8ad95c8d0babfde8e73095d76aca4e6a5f4fee1e309ab2f6d7800fa77b4ecde24341f037004d17ef2bb422a15849eda6dfd697d5ab5f0ea17d59d57e15c80201c85246389892093e6176d5bf1a40670f8395caeb4f8bb8ffcfd9e322025aa0dfdd52f3eafb2463e8a4adea83638be6eccd8a0f7a340b112de390650f030320194b27aded16a06923e87f003d320ad7757bc53203ffb72f64a827848a73986031f06bfe784399231d78e1d052648444178e35ac52d4122d8db5af58b66705dad666c1d2cf46b4a9f45413f3fcc32be55d295588ade748c92b4549b1276609253a8120bcb344ef5bdfd1c182450f179000603ddb80e6b8a86679e8ae2553903b3e45c10fd3239ae2797b26324dde1b0cee88018a6b424f30e5d45d8aa5278df1a181e68d0819a310607546101753a39cd2574aff630062d39a289237c5d73347ebdc4a0a6262c9a1f693b3cc62339dc0698bf242f29ed814db39b346235a3a6dcba46ce088d86023c1ab6cf334027df6896c04d106d6c876efe1342b568f283e1940d08a91003aa174b62de40d221eb2e93fb1ff9b888c7d8676429c5b39b98ce2a90d9d1fd51d9dc4d4ba98cb8ddf804c4e7506b4b0e41f43830a46f00983ebf8daa3836ddffdd70fd5c93fac12bfdcf27368065b4ef0ff9b937c3ad61fb54b9f533762defb2bbac299cf73e6694c939f9b8016309f3911fea008bd4e24294def013a1d16fb70a5204589db716729efbe87056d449dc26e246b17ba0b71b60c6061cfa95254d1bbf238e2197ea60b261b9b804b7f5ac5e3c30d8f1a11859742aee86cd8bf9d3d78426333c95b926f215da51d567a028f8c9600368557ae055b4676a579bb7e60a9c8f84ec87233b118227cab6b4428e8a73131dc334cd5580f5abc8b232f3568028e3dd91d87fc4014443a550a97475cc3ebb626ef6b783fceb34ccfc03a777af9f8f417d4a0d70ad579f48df83de5cdfdb0ea8657a24d26451aa5d8a445947bc2813e83697aa643df251af9759030e6203750f4cfcd6ebeb0c43930c0a01f378dc7ea4e442a5fc27c1811a87e4bd4de80ee8cf2100c104983da29e287bd44d3040331ca82c7f08fe6791a6012e66369f0d19a460f1cf4eca30e674006babdbd852dc41450ffef1153635948e94fd4f114c0118a25ead722512513659214000c482f123921f2612178704a20f0873c9456bdef656fdb7e1b7428216c1b4eef728f917a6c1ae241d127feb231a8739e38141f812d81b13635f9aaa1694a9cc63898f470e4153b2054da2d7e3e8e1505102a448cad5a211fb8ad15f08229664c2efaf753ba7bbe0278a2068658421385d5359a2308e82c46b5b738764ae4cf85699ed403206b450b982e1e4cbe6c8c2350caa3200e3cd58ad3d05b0bca2cf2ed93b32a9cc35fafb4090c0c0cd6e8bf2517d9ec85ef0389e0fe170c986cae5d0ae6b36ab9dd09dca7e855089d21627d47a603727c0351db937fa9cbcfded8638a4b9525516dafe8b03191c5d325da7b1985a05207c3a151b3891a670d1fca81a8b9810a451f61d528ba85293296a59b8434863e082905c1739ca6fdb9b6c8a9423114cb285f10ec19ddac6f1c05d184f799e6328e173f4323ea68731aef43ef453f9520d7c8c6ed616c2b18bd5a92185d9792989b3b6e34adff1eacb67c8589998453b6cad365ea7d4810079a6891000cffe112d89eefb77f17e8cff6187fc1fc3b2524f1d7631eb1457e6f4eb641216636ceb9a8acd48f33ab54b437675e80a62d891a11b06f6ada546a02e6e4fef9a7611ec621052294291f5e874378c01c8952e46f957067ed768a6ed0141ef5028d68a1c67ad9ebf98198015bd002f8decafeb40250d69aa4ce2e752ab7f45c93dca6d11248739f6ce431796e1c4468c60a91d8f647833247f51e325ef2f13213bb5acb989cadf759640cac8994c655800d6a871ffb7aad09c3335e0cd9f2f7908c6120f5cdca1b68e172b373d82af680ce27170d880f87999ef0a8d46f2b8c774c300fd2ae0399f861bf5103a6ee58f49d4072a5237597377c997496011ff726d2eff3363973eb4e67ef38d93438106d9f68af057d51e4336ff4106af38d3aa8b94307590c6a4e6325cb47e90d9eea758bfe579d9d5469cd77f53fa806dfe289269928979cfb4a259afcfc52d68f835cb9a58512ab4ddcc21dc06dc6035acb404b9deea01a6fd97fb1a8fc67d57cc7127b055b573492884b68f548f8e2a59258dd2e8a9bd25b55374602c609781e533877b92db0e874f9761efba135847fc1f1040e759387da8bff169dea796ec39e46df89f0450a545cd46d0c0a92a5b6384812c724fa749714ca9758b1f22de951a04f6b544dbc47c24dd43a6091d09d146c2fd64160c4f1018440cf538dc543f8aa9c4950be4e8b5bba796b43b849c76d7a935fdd046a31b449adc9fbaa2a845aac49ab83753e6eb33c480447f4715baa5d9c8ae9910d6eeb516216335c11b7d56a73c146f1701b956a81c1a81ca5a3d4560274e336bcaa85c423c479c7e091702487cfe5462f40995ce7a8527cfc925e2c0ea4fa2823ab920a9ca14b5daa5a4bbfa98f42f20b72ed14ff4f076069aa28992be3189abb25a2664f3ea954ce605fdac008fd2885d5a4bfa0e94e6c4c653fe92c5a8870553727be5078e07c3d10b2913d8082c92e1e26720d53c65bfd2bdcc99d04c5f341dc50a42af6c6300e3575b1a4bd0290fd41f9e475b7d7d07389a1778957a9dea747989f73561b30297631f0915fc12d46e2d37fe2a06445b430e58ddcb492b6d0eac05ae79dc20c20d53aa7716e7e5404d2dd6c30bcba6912644a63115f8560f5f44a2e0bceb8c5e713803d7ebfed7ce039e34a85eee96191c5f0abc0b1984d677686a18d02e6fe4b0c88e92221d36b5be8033ba09e490aaea91370e1a106b259a49aac5f6cf5b9ad59d5052a2f31b0e458e75c2f80d5e41b60a2006b2bacd20ba4cd00dc6c7b6691509cbfbfca5b98fce155bfe2d05a6dd86594a89b34147ea56de15a7ab2ed88f9d52b884918bfaaa7d23740406a2a81965f9d8ca8f9be4c2eb25a2f8b9c6c1af6750effe257fb9bc08e87a362ca9cb5007a7e0a9f905a756af3187f901868d6629aa3caf2bcacd6f74ccf69e4f1ff0d508c87f2b7e0673b8517146f9b49808f202ca1787ca35a106a6a87ce4dd0e9c99416ca40049a80d1c50ae2565df0a395f3c571f5c35a0ea9838c71a951f5efe36afdeaade9630db104ca6532b64f3b32101807e6fc390398390ee0603871857cd6594eea774e9f791a52912405c9827441a48b402d9cc4484c8ec6ea4317ae626b213843e2bd4fcda576c5c30b65253a48b85a4d466d4801ebb29932dea8dd1064c6e4704586ca1c92fa96693296703d10d8753936d9f4da7324c2546a65b584e7ed86e35f389058762090f6ecb7b78bd4caba62245a94d5dc1f07ee879f4e9c4e761ad41a23512bb730b7fc51a0f96dc8ae28a63e84f3f7480db55af0652dcf2f8bfdcc62ad842c56a3e8deb9d905248dc2316f4f1415ecee5d35d8affd87dfd62a102a2e9c17244b2cf5216ce88d5ff6c2b86ecde2da117b663de4e994fca380e7f841be0bcccf0d26696acce37812fe7f7c005e62c27fb23f423efe4671116f6874c5c8835260354b4c767aab93be847f36eafde5068477964e3520120ed7ab416fb4fe571c218369ad477016203a043e11931f857400fa83704fa83a04a6c624694466cc2687995fb705f7b292089c6f0847d3cc0207a88affbd63c23abbadea1a7b8919334e91a6b2d053579fcd75a65e8466c04d0886a24781695e094662fc187b2229cb0e667a8e644e53aa894f3f2f0d86f1656a019cabef0dcf0c7a6e6689695cb6453f1a1d44c249e6703540f0c08bfb2cb9a05cf55ef00b9dc3fc9b037fe9fb8be9bc01c8fe73629413c6a58414cbbb4e27806d0f421a0a0dbcc730ec004ce351cc73225be78a2b8799dac49ae98cf623f4cbeda5b8c84033e13306ceade5639138bf2bb36f18dc4bc0a99b1d88fe13b6cdaab6948fe6945a006ec0082a9a2cbe3d6066c262ed0afc365551021bafd09cb89b3cb8e82f57827ffd185fa7783942309f20319606f07c31e463a7b000e799d052c9085c31ea2dcd2dddb623fced9c457814597151b29f598e8d570cfebc9d6ffc2a6b387b1be0cd4f071e6481ca29da4c5da577f1ed07ba3a0262baa0e289f935c6b2c2662788ee0235589d9f7849604552e48b7167f6eacfb8349cd7a3a5227a3512410a043e40686180d1e92ff086ad3b3770c855c31d059be6eefb86aa830f9c5e1e79c8c201edc40ac07a58230304b64410cfc060f8f54b40f9e5ee0253b67270178fda47d5dfe7efce7776b9c5f4208e793994691496ca81fef69838b25af3aa932812e0effef1828f4dd4b08b281daee7003670e99a171eb83a12d7ef327baecded2dbf857dc6946c60ace0b088c7da8bae7416ea089d7071385e801c5cd08059edf89af514d8970ce52140235f22b18c6710461e1895e9823b6bd21df33cc1b3d1466ca6195dda69d702995122dc424f8799a7f26d480469ee4bcd32012b27af34cb5d5f9c615ebb8133a97325c45df34fd89bc0f1cfca3cd90e1eebf1602df41fe9a43cf240338d29dff024c3ac4822d8baab33828e947485f88016461045e4086df4bfb07863b47b0056d846cafd3fb08b8c61e9b41433ddaf79fe4d2cba2d5f0f2bf83a6e61bb91b078ea145b7777d1aaed0a8392a66084ad72a3fc349a6e5afa7fede6accd601a542e883ac3cb6e81814efeaff361e1192e9ccaa55b37b4b7569bbb09b60c82c922b30a650f82896de5c1fda166fe1e22514adc33c00aa41b8c5e56b3a90ed276d33a8d074cb9702ee098608349937582052a102df9195988a6ac2fc974a6503b5ccd0604108b31b8b0524992eb6ecfb0bd822dc54f443f6535823e80e007abb87e532ca4ec235c71f1da9f4fb4d7c7d9d6d327afb1e13fa780127bf8071619e5e5c0b73653c8d941c2436ba0b14dc940822a53592adb39e2ab528d897d3273b4332e6e562e619dbd2b6ed7408c989be51c12201fc2a2fe3e46aa3c3460979916aeffe1f855cbff147eea03e9f39f854879a5ece8cb3695ae8fec0102283e47b04c2de45f38101f959acf89448134c67108c36d32915f4028b3bc5ce8698fc8c7dbd63da7ab9fdf63c690325f4b42a3a63048357b13351a24a869db9f69ca65b9a56a9e1cad93a2bdf0fabb3f7e63c77eda79b4f12a943519655a9deebbbe48b6fc917783dcb4acfd850ff0a323719e20ea1ba6c4bd4aa1944ada90b5c72f2f85b9cc996cd8567f682694ee3f6dd582124a02585e6c869ea10271aadb409017cb3bf2ca79ada3b6772beb97d86de4c57814554c1b711bd5ce255c4b71bee3f99e572eaebef56fff930d122d0c93138534c0fd2173a142f1dece7816f22a737e337787cb4d9cfb583f79df2c7161cd2bdec4356d0126ccd45f2fe8775ef464e2c82c595ebb66910af566b7a953bbac8ef47cf7a11f10ca9bbc26918f5aa172f1e1be82c6a4683979f24457b3e3c864e790428c9e0536292c50c5a818357f9dc1f0629bf69f56956daeda2a32141106c4bac46d76ba8221fda6175b4a6411b58f24d8c3baa61c90d91c3b4409e82473a2b64a75efb9708fa425dce8ec7f4b0e437586c4cbb61f7229c7ae6c24b75cfe74e87152dc31477b054a0c057bec83c6e25328cdf9000887a797c8085cf393979b0c37e12cff4a1233ebf8162cf320acf5d123e117264161bb052b42eed979ff294997d5deeaccc12530b8875099e094f219baad6123028ca833ad782a8075887e9f9913da4aa4435bbdcbb1384cd8873fc719d851ede1c0fcccddfcce8f158f19479137a1e922112dc9926566fdd9f2249f71a918675ff30197fcee2e5a8b5016837c18fee08c94141e12803620e4020c2613a0585d19223e06bdef925094ff922893c05cb00367e587e6f107e80df4d00818e24a36eb5ab84bbbe9b7014e3a5c01696d4f34407643939b3c4c533a8b3cbb275c9c5d9f7aa39fdd5844a1921539251e91ae7724b45d739768a030c81950c2342707282c387703ae8506bf1f7a5f5017d0d40095c396c51021247104c4962b00ff3a97002f4f2b40b6964b10f8624dc012e8b25b47f8a2c77983987ebaf3290883f1fb07be91a2cd986d825d2135ad40fd69eb6b9e746d517b96fdb74bcf0dda2b26712efb3c7a82ecedc8f3c2a92564a68f14c7e422f18daad2ef366f0e2242267abd84d11772ddba2f259f59bee8fd22a4265a6fadf83785e9e42f68500c3714765b6b2810b05a51782fb7ba66f6eb02d67a037c556f804b5fe2a50d6bc175c396d2c0742f5e983793261b06536ece539837778a8d41b6ed474ed572c6e7dac914e6f66ec515ef07ad1dda04141f2ca0c64ad280ec6950a4b2bf7e4cf2c7f095051d1f733304ef3dc43bcd05a6309681fbda0bd3eb32b4b8ec41a6eef8d9738ba69d247b15b61e3857e99cad5304a68cb3d2784b78de0a4cb978b3f1ab545461fac5b520d86a74063410059c75953a57ac7886a5922e1008ef7b144ea9d3c36c4a7b203196aedbf116bb0b263407e3a399a634fc6a89c1ec0a43fbe54f14bee59154369d34a1fd9fbec4dc97d5d5d252c666f7942d79825bd405716094c898c83fd67b18fabd0239b6ecd7e59043631b84104404cf7d8df3ddfad7e0de850f40dfeae97c0207321d0b4b236ae448125431cd0fccc34d70aa21537931fb8d6fa9488f64436175a453b0e8202fb11164d699e1052be6314a4319b14be3c911c4f85690cfeea072a33a60d89f3603b4cdf7f15a8d8b90587a712ef1def2bce194e0e2bb7d6048af05797f19f70e9a9ac9b752d4a44cdb64e8a0ed7abaa70da8ccfbf78ef0847ffbeb510264db7ee790177674ab81e5184761f1af4d7330310845695c7b6df2d27c83e49045cd8bdf2b0659bd7b0685c0443cf918b2f7ee16f883a2697595c3ebf4dad9f9900b994e8d86ea0a19c84de906b0c02f5a9044ca8a121a1b0baa9b85914a08f712cb896adf105f98004fafefd3d11c824a456054ccfe373a3c2cfbb0d67e16cabeacef7460db850689b79470592031d9e67b925d7cfb4a8bce4ce00f5f7368f84c59b401c3013b9e218912bbcb9525c3077530d5f07dc6af3dc1a331ffca49e98da73b94dc809c5875cfda62aba249f7bb5b7e6620ba8b48e4ed761fbf15e5ccfad3364b56ef4a8b76103bad4438c510d27fac820c52358610093f77a99ba05aa6f69768b110ed260bd13399a0eb8e76d5f3cbb1a4119ba0c22f43a5bbd4ebe24de447976acb08a0a7aaac32c7d340a8d371d087d2470f7ae9e5f26308ed6ef6e3265fe47a06908cd9a2508d1555e29fcc02619c0d6a220a318bae1bb170eec005eda487d834241f442dae003a8a24ab7f100bf76e09ae8824bd5bb059920e9dbdb03fef47d9c7c1a654292faf3009ad8191ad7eaa73d4ea36a1c884ce92cd816f6b0b56540c42e3ce2bd92e16b6647b5ff16777f379819d2f33b54aa7178ee56ac4e0b5ccd317c4b6e3b40824ac6991007f115c6c97c9a6b4cf6048b04d6b3aee6807226ce20facf73e7edb235b5c1a9f58d6e3ad05e1953afec019dacdda68aa4adf462440938bcc0f30fbe61046e44bab2085a1e9ae92246312b9e7a106ff6ac82de4b169ca17938609dc8033d8cc674046197b68b720b5d36c934b523fb3893a6f1b773a6188065e4040d3636a889cd4f06e0f3d2d463c782919eb03cfc396fdcd016ebb3ba829024680198529f52ad81327e15a6374ce1ef84c6c589ede0d7d9c4701b53d5db560b34b4e0cd068ed3b3c80e225e39b19d6f1b712bb3ca6cfa0c9422a6529e55f7ddead2a371f1ec0a9cfaa5e8fc770a878968088e86b33621948461f4f9cb17501ce30fb593b3838bf3218c31c694ea2dc7316734bcb7dcaaaa1dd7272e8d531d10f403a2c4ec89e2240edf437e0aba7225e401b3c22b9948851c64d26143c289287d531d634c2742a5e3a1c117dc26111313f1703b20907f37cef8abb5474e1cfe79d881bfdf5405c78e20bfd7c35c2b0bb01197ed996fd1c92e238500ea5cb0b8917a5e4394eb2acbc1aef0356aa2d12b24c231c6f021326dbcabc5c7605ae5a1c1ada50ebf22503c20ebc05f212e6e982a701fadff4ea30800c6b08114afdee86fcd0596e10fe70017ac203d3f9392aa6f3d540d63abe46b4f705b8f0b4b64d7a959c2ca69fff7a0928b877600da27fe1a3e5aaab2d2b712867e18241fc14df15c0d7dc906cb297fbc3f95e947b31a2f88c43a4016a6fa0447ad3c065f8ec46b7b4a57b236aeb7af9ab49f85227858aaf95b2bdc697fbc08d6917527fdc1e73dec2b4135d0fd2414eb554b058afa06df2fd3845d35d90dcd1aca4da82e000c397db94a280fd5fa4b7c7c311367abce1d3ddfa8d01f2405beb0acec6487bbe6c6a37cb6247b54e412810fb17a6410de948c63f04e3812deef0cf5b4067561a654b1738cb502030202fbd68df44a5db340800ed5595543a3a71f52001aa84de2bbe5082438486540d6b165fdb51c5b55e292c0ed6964430d246ae50c0488aaf2eb47ddca4355df9b6f609aff3813884568303608ed866c6fdc6112eab59356ef3e8b0c87a246d72c2ff218578322c2af6998ca35baa254d15a492745d1974d6ceb5f2b204c3c73ab96ee77cdeccad7a91864362f9e1208724bd9b4e6ede114b42f54a5504c184f452d79c56bfa086df4aad39c1c549e0f36534d56da19ca3bcad7934b6c8595db3eac6fedc0920d9087f299250fbbb649d902b2d660a1c8915cb43e22f3ebc52bc4b6e6303adba979c02e54adc89eb8c72ed5b98d9021efdc681af2c2ff3f1629e29d42642d96f82416cef7c2800b6260934a060e2b5b7eb0d7087593f1e0005638c30f4447421f20242feb176a881fb97fe78251030279d8c83c08587496623d36d7b03b09fc7ddc7061aab0b9cc938704dff1275da6e10543a080dc0edd834f3300eb8e19b6dba5d311967e01568f559aec9e9e2a018ad2b0c91d334b25bbf544cb2b905b87ee85924b2ce8b0f151277661f4e913799a6c8bde02a2a4e5569d20da8f9fc611798feca8a92a9f1a2904777592a8244b2b047c53a5ff52bfb12bcec8cc67d05266b7b525b44861e164e940826f128c124d2c8821fe9c26b3049bcae703764779dffb383ccd11d7ad8ef30df0174308489d36730713eba4d965e3fe09cfc1c8df47e4b1c5cc6a743960385c0ad79f975ae5c7711aa85bc971ca9340c07f3c95026c179dfa4b8a9b00559405931572bb9e86a9f3dc06d103c3ed2d1cb96c82f255005aefce63e36f7bacc9e1118a1d642b14fbfc624ca47c54ddd28c1dbadec30b336dff7566f477bfea5c03ce927dd06da57d940be3d1d869359abe7c4d1eaf3b385938319e326f6c9f0eee4336e984386f0cdaa6b6c09420831c07c959cc47009efb888825d1052b18a52f7d21700e2cf20d7ca1485874944728399e471dcefa7ea1ca893fcf8298d73aaa23af81eecc143e8b55b4e97dc55e1df10438bcc1978a21b6199b95df8d46c95148a7abdd484a78e9d08c0afda6f983565dd1ac046dd69dc52e56e42f62e689e25890d69dfc07e938f04badaa9dac77748b9d9bfe45ef226861f57d11a1a3a3897d021ad8e49f45b4a5c152c9ab2ea816e2cf934062346ecd1ba1d703020fda1e9023078434c47005ca8172cfa34aa89696443d57e4b65291ce730d15d24a60d8d9323d9fe9425c3f226fc203f40da11ba949db878ce79b8c781e24666d7c1b2425b0812bf1460aaaee768da5777a71ae0810b7ba88a3a95c5613828c6e5a1eb56f1bdba33c4433131e1a7f1d532525f3ab6b8173af969ae2c99d3323c88faac10cdfca499e9a868c494e28e8af9f6403f111818538018390e07b849ec2246c4b09823d0149a987c9d07726869b0d7f29076d7c66b50e2c8351c8a710dd1466ff32fa7d03a0726192e137ead2fa179a2cfee85abebffdcae2af7b9d3d834d5e407fc79bdcdda3b8bd8a2cb31eba856c850ce0f96c7ff1760898279be23cc128e9de868b08abcac6ea26f7fd964c8d7448e3960800643743c5463c1cf07b63a10ca0892611f270692cb5d141a0bebcb0a1452ce7e0839f964af0a2219e51a44f9b3971585fad572aaa2c72b651165a61449108c86e9a3bd5d0baf2c095bae5540dedc47f43549d3e6263c2df9bb5e85c201db015d47ed45e8b2399bbc44d95f62956fa807c4144d75ae343439ec217b041e8b4a0c324ea6bfe044b3ad7ae79c006b204139855197027e403fcab4dabc7a99ba0573d9a5fc71fdc130d4217aeda7c2572f7c5d5aa0e3131c3e4e56f4b64a1b8028c92db015743d83d0dfe9c04d8d15b47b82fbc78d0d9fe2d8478d63c82670d8cdfea0c026e7df9bb6bf5f01f2b6a6d53cea01210feedb0c18180d8d3eb17a1bc90c637f7ddfd4a2bbb0ca841ba2a97b83c182aa747bf88edf8e1b1b77e9b84d778ccd482c1026b2f8e03601fb1004cd240e36695a111ba78980a88c66d37ee4afcb28fc9488508ecf3aeafc8a5c7e8aa4842334387e47ff9b0d958ce8b2725c4c97105ad5d461e2b910298f00a9015211585999620245cfc5f688871aa44e4bc4bf0e1c1197b43bec56c6297431d71d6478bb7040d24c411e596a0473bd9709f8abfcf8f8daf2723fb1563c8b003e0206c3cdc150d37ba1e2c73f87658ab85b52c7c5dd2c9e88aa2c7aacde5fd9f2f0d0df8e23d5006fa599f113fec8fe214224a70769d505b4ffb2ca19bffbd1737fd8b202f33d1a91b6d8a3df38d9dc3cb44d827f0b1a9f1c7ca6b53a166f4499aa484c67a6908f0ac5715631fb179b369bfdfea809a1b2cca7a1b9793f24f671cd95e797dd80349dd0088ad57340955e8a92ef20d2001832fb144945c5723a01c9bd214f6c71dc635b937ddc28318ade770565c482870096a32a4438a615be876d473c0fb3583ccfbe9e6a618e700fc8fcf5850d25633e42c6ba67107ce5a9c946805b027e326aa5e11e4a0c1da3958f2563c230e30e6e7ffdfa721aa6c963233278c0a72f54462bc7114d36514cb2e7f9747a8f26a9bc3860699e2590611714ab81aafe2c049cad3e60f43b488001e98e04bde21085ff68841dd5c43df4267bf7187928b5c63194b3188ea6a1e523c37a7a28dbd6ada112478096da024e8cefe429f6c4f5b89d26d05af3a81dc24e3b6f5e7bf946037fcb0eaf4606041edff0b010f7a186f3a168b49b477da65e8c7362d316f3a7f78fdbf76e382213dfd9409caa990f1597b3a19c42315aaf5f779f0450f01237c054e4f10f0e7042b7053d10984ffc1269596688b1e98fb24c1a29312b0ec50038273e63fa1368d01cc7f427edcfa6896f27a63fcb7a61b9105e89e4cef208d4135d7f893019a18c5640f51deb1962acd84d709bb145facf39930f4a7d583633ce8989b9644ef5d27dfeb3eeea9c16b68fd4e5f83f063560dccb6a0adfee81db6b18b53927f853a459b049a67088b57ce2016dfef3e68c5e0e2b8d873a030f521e937bec51e2837d18a6292741a0368b0ae39a8e914c1281e9f8406a91f29aef644dfc2e5863bb6e7c0c08b46f946f8baebe3eb41d5f690e6d90c4b866da0aa29522a78b4cbd5bcfa303366a5f8b78889fdeff3d1a6b731b1a6f52463eed781d6e7ad4ad2ac34827eba972757490a775d3838d36a9e4ca5a6335ff3b3a0fc7c53bde3265fe9ea8b4b5da5c7c90436b161ec6ea40c936694bf0e413ee891573fb1b803e17c87082df0d228750b055cd83f39fbba5ef80092dbabacf3d2266a0faa6b15116d6c954b39dc8dcd8b54e991655615eb9da28360c239fd519c28c066c77944bdb9621551e1f6832b4fe33d7565a4b019181106eb4d013f378538dd2fb4586ac671b9c1630cd175e7f9e784c5e5f2d51fbeba1c3d693867cd52b4cc958686e6dd044e77b71df1195cba763a3439d7ab3d295e435cb716ee361da0d37494a55a9812bc8d7f2d5bae90140cd76523e5983f6fbf9da232a781e51936d157fd61c4de07d3458384fa3a6fc03e5474e57196ad37894f21700e88b3edc47749740b98c3f491e7a391d9f0a8215e82165624dd73b6aec1de823d5bc4c61645a3b12aeeb8707897471a16b5e4029b6370bf9ff9635ad7f7722b03f3cadaede87e1385f9e9eb2dceaf5e3f943d450ddef0baabc12f9a8f484377b3e43b084a6c306fb1b3f71716a110305ba53e7d34cf8637f05f8f149f298e12e03fb3c1a250fd7bcc58fe6332693a6a2133c716711e9a44f23e069837ef97dc4e0dfe9e4832ee12d6ff82601aaaff2a53dbf782e6da6f46b0065a91aca9e9843aca6288b4985e4e856f2d677515eaf9b19bef503365ebb2baf73e94b4cac93ab9000126d5357c2e4ab42aa1c84e39e1200efbf7f38f6c33f67d18dcf5692fa245f7730ade2040e3299ca03fe9c09059d9d911e462a39dc86880fdaff52eb0b0ece0a6076ef0b7b4576df10ad24f85f8eef0dd9c6d8d9cbf2c4a835d75ca8cdbedcfc8aeb5f3a4e4a2a0ad75d0877c023d387b18b2515a7cecf825ca71fdd84f21164e9ca402d574372b13887cba492566d8f91e55573ee1ca29e9e3f25ba87ebaedbca0bccbf80e1e79df40d0cdfe71c0f57d3a6073736b2e381bf48f1314bac2a083afbe0ee6810b27a316b63701f3b0dd9fff115549ebb06b4097c6700795e1bb58dda6ae4ede98b3f97dfe7a09838edaadbd87d33029fdc262450f11a166f4738f0e1ce220edf8809aca498230f7ce1222c97190be0187f6da44cd666692ffeeb07cb9ebacee5c48a137156bb9f8a5909b73871235c9cf86c2e4e7cf4ba38f105be29a4c3774362d469150478efbddd987dcb874a422b06850c140148a7a78b1a36fd167e905f7d490f2ca59d30d489d2a62cc2f666ebc684881e2eddc4b2476527284d691e18eeba107bbb7cdacbb7927c2a9093b0cf8a163c04bf9ae4c2edc65926277c194a3a99c5c0b4f1f1d590106ff043f1083982a98a2320b4ba53881e4622a13b856341ad95368582bb5f00500d89d85653f7485edea1d2b82ee74b47304d7309e16b108518d011f3c3f4c833d4561e2f947b91c9962e93b4b2dfaa9089187e74bab2661ab47c9762cd8d9d2b5327768f954d87e870e2289750451f2c005e9802b13ec6952d6696fdc885f87f9e5f0ef0595716b291d3fe691200196784b7638181fd7222925d9079f990316844683bb3e55afd5e4837f4857b1040c61dedfd1263a3fdc06a3647c5eff34c19d257ebcb93c612e41286c788fdb0742a38101d5ff958760575674284b1aef51cc84dd7484349c0d624d3d6f185caa03012188fc53c031558fbc18a2b5a7ad4fef8104a813c1930338ee6b5706145116903f5580e53d8f9a0c1a139274b6ae30fead32a2e5dbb96a22b794527ccfe3ab36e5975407b46df3d9fd46b3c88042b62ec1a3d2ed68ffc957a982f28794380fd559a9f412aaab235b1a0655f25b70acc5d0c09268b9f01c8468878591d0f57933760aad95e048a18d40e4a424b62505ac8713aed59ed4dd005d5d3c37b1d69e562a91f8f0a9d467a0d1a29d6d697bef3f4c1615d42e6295e6c51cdd39898d49555a3f105871a1bc18605ff3963ba41183068c0d44d9c55a28a7148d22c929cbfd9c7075375db7e83a291ac185a67c0014f82ab186d5ba88a0e8557a8f4606af10a1140f7e2a3226d425b7c1189d29001f381e056a8c967df8aad47cef17540a7145c2901aaf07b557f752f2f7aa2518efed070f88ac9e101c2539aa88bd5e32e974747f0aa54c62ab45aa9680ce4c608542e8902a6b5bfb8d34aadfdc4fdf0988643b9786e71cf9da47e2da9cc47a9ea6bf868313d0586ccb9607d5441802f95abe3c05b0ad1f471951269189337a9737de93302799d7feea27bdc95cebae9fcb207e0861c97af5ad259af99b3dfc36458326f8be9200df87a939c4a5460a6ff93e24eaa4fdd286452c398f08d87865b05819306911b01473e39dfb5f3d18db0309b522682726c1f0e7308ce19e1429ba9021ea3ae4fb4dc9e8fac1385e37fabc28f5d1769bfd3e9255e1783e6babefbc95287c2b0812ba99b35aea74c04cabc3de96726217c6c19f66697540503d27e8514e4721905f32727b4e0c6e9408715153a64c5135cb4a52da102dde36c7178f35c7981e91b54a6c50c497895391dbb682ce3ea39498f5a18bf9749c425e15c9912a18aefbb94f13c86264525e04b2ff128e480fc9d9cda4fac8b13bda0fa44b2c14029a69ffeeedb6b29c12411eb6c6ebc7dca856676f3775a608c174757d54b0a316fdfd22840e69bc75509cd348b84c0d4e2be3122c64d289f48f576ef1b6b21f02a9f585d7a4f1b809c331dfe9f1eb97dae765f76dd3b330c272831803261977ba73d980d5f2be2861920d11965ffeb64f4d6ead47a4929dec8035c2fa344d50098a72f1b8c2e56eac0c2c107405e8f772f0a432ba2e41a26dad3505865f772fb1e116db4c0ed5ee9100e81a15d404fe04bbb37d0f6cebc8d1250efbe9f77723ad241dac9c168b4fb67a02051bf3a8875ffaef5140e04fe812d1934c5e36b16f1bb1fa60cac06df3dcfdf9a8895009e66f758fb864bd5d51694f461b12f08f02a0b61d1b37b93e168e57ea1eb0582b11ff00ce5d63f7d94d6cc7d3dce3a4f5a8e44ae374eacc60b575432ccfd0d2df5d5f52b2211c6d47fbc9e2e4e6bababc25b2e010488f746d9de3246ba1e8a41bee2e3692b414ed4250293575cd59c00f50c18edfa422ffa126e45e3cc49cf0b6803232b545242fba5c7270eb2ba8e163580104585058825fa201d8d7fb05cea5751ee397f506632a73155a89c98767dec522f15107f872c40b804c4f0c1277868a92fead6b59daa65e040eecf351e2d9c8ffdce559e7d9f3faf795ba3c019f13a3b98af4cd8401a35b9846e35c44832def4d516c694b5bac8e1b8c055e21cc81bb04286b0530b642e9af527083d75bf5a8f228b16feadbe61a9f5094a0c7409ec35288e5f228d70466b0e016c7104d92b42782c2d092cebf959efdef934850ecdd40f5676b6624816ab717074593dc120ecd0fdbf7b3c813dc156727ef7680bf8243340c3613b0f43598ef0ce6524ba29c40648a28aeafee3c783f2199f9f243c37046d975d488682c28371f34c21195caa18548fed4c31f1326ad566cac1ff8e788a9c2bda8774a1d6fac601b905c9c1b475f2c651be9335996af0cb54b632732b4332a970fd3365bb304b9911f103e876e55670f76a9ed530acc8dfcbd40032354e58c33ac0cbc1e45941e69d81450cc46c69275b1eae6c14526e4278196230640e04578a83bda630ed54f0a1589bdb937de1ad18b388bd0feec403e9da33667ca0709fb1ce426ca270fffca9871e166462d31523a3d7239571ff68377e32e3b824136b96cedc7b36f7a11bf8d48d52a36f421adfec19a3a22d87c359b6c5b4b9f71f8c8d2e59b58d5b1736dfbf56c4562ca2d57a17d0fd2a90f022a57f6af584d016bb350fd4cde500ce1677c65b14489a30ad9a078242e02686772a31d47ce03ef022f1d70e628bc03ef827ed1928a0a1d30c103183483f6d3171977b8105b70d5a8fd51735c9b53cacaa6ad19c147654e39793642fa2e47f177fe1618227b56eb88e6e43adaa261d3399c8bb9dae912dcc4e2163087c991fd03adcef8ad36bf64bd2f3028f0e4c76ffcbc32561946c65c17d7413093cda366b662e3aad6d8b82237b402bb954d7d2b54cd8af0c54f02486907655b64335bcdf837c9d9507500050c975132361547b9f493d2663f72befe25616c4543c8a2ea0be07a4c8506887bc1f606e26d7da20bb1d1293c246094bbed7ec78d101bc51bc77303028e8eca18f9340a7fc3b181443c4e32f6119a7df06834c2231a159c6631bccb99880a95ee9e03cbcbb42c3fed2d67496b6fc29fa0d0dbf9154d3bac6245e12f369a562dda7036f7556e0d5b9b5aeefacab241df18e5a5744a383ab7f53f7a58a90b0e65a7e29842eb2ce46e9aa8e4b56403120785e88ac5c4faaea3d455da46e4239ac9877545884eb435ab732717c7171e46b70d87a117d074e0e59ba4d127a5a4fd6da7a07b7484e9fdf184ea6125ea713eb389430961e0d61503da2dac5449bbc2df6b9cdde7e2d8e5f93e39c650153ea86a1765f157c91103dc04c64a3952308131b7a0749df52911be5052244bc050e68055a115210687544e980f1671e8052cc97b929b28183571253a62f2f01184090b1a1510107c0a1419a21f954d05ee56170413e1133771f4c19bf76ca5b51c0de7cf5360eddeb02547ff9d6fedfb64f62496b09d972ef131204114911350ee3ceb80e3776d885f1185787ffb8b0cbb8311ee4fe38902be3436e900bb9400ec41d722257c8e90a2e100fe212394dc1a52b3865c2a54b3855c10de2140597a6e0b4c9a52a385de252149c9ee0d226a726b87489d39a4b4f70cae452139c2a7169cd6912973239a5b9548953125c9ac469092ea5f9924b49705abbb4045772971c894b6b1fc15572da844b99f0998bc44370477011dc997fe086e020b8227807ee07ee810b8273e076e049ae076ec4e5c08fb849ae816bc437708f3806ae069e81bb811fb91838929b815fe01e39ad7269135e5e24afc0bdc02d70cb532a2ead720adc0a5c886b8153175c0a9cb6e00af121ee33c55d1a4a0585c1a993fb843ee1f405f70955c2434ac56d41c515a9b8245a58451a836b6b6050a2855ded9a76b313956cad61545c138d2ae1d69c469ff070da1f7ec045728ae874cc0bae8f5327b7c7a9132e7dc26915973ae1f4c9a5559cdee0d227a73cb8f406a73bb89407a749b8b4e704b835d32897ee200a9417b90478012eb539cdc1a53ab8016e91cbdc029cd2e0d21cfc00d7003772654e6770290d8e807b8013718d9cd6e0d2195c0117014fc025a20667c055c017701370075c06bc017701a7b64b9ff803ae035ec46dc029cea5b647e03ee010b845e07c02370297c085c029152eb812386dc19dc087b82d7238ed1040fc6020b014c19630413dfd223bfd223c3f5c6bc35d9bd3b1ce39812d67146cf4071c37387470ece0e0c171c3d18303872387c307c70f0e201c4131b5989a189b989b185b8c4ecc4e0c4fcc94985b0c95989e982a31b8182b31b9982b313e3158627e62b2c400c56889098ad942d6481ab2866c42da904ec81b1287b49139a40e0985dc21a5903ce414f24652217bc82a248eb442e6c82ba40f8985fc21b39040a4163288dcd2640e198dac46d64466237322bb91e1c86cb21c998e0c8a6c472645c6d36410efcba6344957f0beecd62455c1fb322a4dd214bc2feb699236795f56a5498a82f765b826e909de975969922ef1be2cd724ad795f76a5496a82f7653e4d5225de9761699232795ff6d324a5795f96a5499ac4fb32a0266909de9769699292e07d595093b4f6be6c4b934bde9fd59a44e2fd194d934ade9fd53439f3feac499323787f66d3a408de9f39693204efcf6e9a04c1fb339c263ff0feccd6a407de9fe534d981f7673a4d26797f06a5490ebc3fdb69f288f767529a34e2fd194f931b787f36a5490dbc3fbb359981f767549ac4c0fbb39e2691bc3fabd2e491f767b826cbf767569abcc0fbb35c9316787f76a5c90abc3ff36952087c84104ff2e521de9f61699202efcf7e9a9cc0fbb32c4d4ae0fd19509311787fa6a54908bc3f0b6af201efcfb63459c4fb396a4d3ae0fd1c344d36e0fd1c354d32e0fd1c4d9a5cc0fb396c9a54c0fb399c349980f773dc348980f773e03449c4fb396c4d1ee0fd1c394d1a793f874e9306783f07942665decfb1d36491f7734869b200efe7e0699200f80823e81599002c783fc79426a9cdbbe61f92f01fde054d1da8fc0355c27fb8e75365948aff70efa7c99a7fd821ff704aeaf103a636bce5b07b55dac4125a58abb0755299f2031fda14834efff090b2e07ae0cb04c040d486d00ca919d26488cd1027436e86e00cb10dc919a23304ca909d215286f00c9932e43684ca909e215586e0865819921b726588cf102c437e866419023444cb90a0215b88d488d010a921d284880d1127446e88e010b111c9210285c80e11294478884c2172234285480f912a447044ac10c911b942c4870816223f44b2100122a2854810912d3617089a3110c1c1ac21e359c1954d51c195dd527065549a5c590f0aaeacca09ae0cb7c49559a9b9b29c09aeec8a1257e6c3e4cab0d05cd94f125796a504570644822bd352bbb22557b605893bab29b9339a993bab19c19d3511c19dd984e0ce9c80e0ce6e3e7067381eb8335b07ee2c27c99de970e0cea01c71673b46dc99940ddc198f06ee6c4a06eeec86813ba382e4ce7a8edc5995f2ce7017b8332b16b8b35c05eeec0a05ee6c027726813b8bc09d41e0ce1e706745dc99036e8e06dc1c0cb83916707328e0e648c0cd81809b83889be3003787919bc3003787cccd51e4e628c0cd31c4003edb82103d4b3865c2bb09a755deb73a3b96c7defab6a7ca2cae6f73554667f0bef5b13f7d0b54655407efdba02aa350de3fd5aa8c4639ddc1fb279b2aa33c78ff745365f406ef9f6c55469fbc7fd2a9325ac5fba79d2aa34e78ffc45365f409ef9f6e4df678ff44a5491fef9f7a4e554eb8939553ee74e5e473c272fa396539019db49c824e5b50281a540daa49932c9493268fc241f551394dbe9a74f13eaa8fda414941f1a0a634f9f23eead6e48ef751549ac4f13eaaa749f27d5495267b781f8543596912e67d540e75a5491defa37c9a94bd8fc282fa691200efa3b23479e37d14509336de47696932e67d54509334de476d697287f753b5266bbc9fa26932c7fba99a2663efa79a3439e3fd944d933abc9f7292ba6932c6fb299c2661efa76c4dca783f95d3e48f2681406932c8fba99d2685bc9f92d2e41022efa7a63409c4fb21016e104260223a93c68fdb8f9e1fb81fb91f3e3f7e7e00fd080252035203c406c80d105b8d01d101b2038407c80d480f101c90dc930bc407c80f10a01a03d2e306a905a1095213a449109b204e82dc04c109620b921344270894203b41a404e1093225c82d0895203d41aa04c105b1122417e44a109f205882fc04c9120448bc41b404090ab245484d088d901a214d84d8087122e446088e109b901c213a42a008d9112245088f9029426e42a808e9115245084e88152139215784f808c122e4474816214042b4080912728710e0ca80b832225736e4ca845c994e902bcb017265b62bc3917165373227321857a6c395d5c8625796e392352eb9c325b5903197cc42deb824002ec9c3256597d471492b248e9c5db2e79254c84bde2e39a561fd1d97e46958ffe59252c89d869150489d4be690af4be29037a413f2976c42d69064cc9698a0182da61b03149325e627064b8c4fc362aec4e462acc4e062aac4f4c45089b9dd98293d6e0ccf136ecc8e136e8c4e1537c65663313731363cb8313531b528174710948b03480717c74f8de1f0c191bb38703586a3a7c670dc6a0cc78e4e8de1b055b9386e9ac08425f45c98a01aebcec990daa4414447099774a3e2927a9270493f5c52ae61a5485411c19625db006ed98d21d36e99c327f7f32de7146dc87d1eb7cc6125f7c391e8baf449cd4e1a2ccc0b1db2598e9949f4e1ce2c84d1888a16c874bc80a125284d0ae0fdb2b41380972529194ac3786c75c207912443a74800a572ea047ec95027680c6c30831a8d3af1253419dac49c821968b4892fa1d11acc9e538545e5998f8b111f97ebf8a5e7b95ca17b9ee715d1f9bbfcbcd787bf45748ae414b1752f5264e60e355dd84890911f976b048b11a017a5f7ae64ab18081bf21d1aec9c824f1128a48e922db7788bbf70f9e5e0e9e173923f6a339fa9c37b896b321e592d0d13d62f6b6601e17811472ffc39653f6a6c998717b189d2506bd208d08f242669240db30a1f203edbba0b2e0fab5123e31a8c11a0267ddc1a968649fa9862c2fa50e8a961bb63eb86410d63af64eb80a8c1f1b2c6cb4bd84ff281ea5e90aa993a5cdff1b24626c1489f93444265efb39be37673f04c31c939613de0b2fab83539270cf53e15f23deb57c623f3d18303973572e93b7059a3f4132e61222e9f4b0f71f992cda5bb947e1fa9ece1998f0f1e1f3b303e747c48f1b1f3f99872027d40f1b123a5927ed43cfad8f101c5478e0f9b8f1c1f3a3e76423e9af8e069321484dcf73165eaa03e727ce8783e9ad01d3041b3864d93dcdd784bda5597ab3608d64260485c3df1750d8aaeeb44e751337562520125371546c8fdd6fd6c0de68aba846c08b25bceb690630856c72dbb27a0b834f3d69352b9b5110d47cd14b736a27d345f0d638c45d7ad39aca2002e4c002e8e9a0f1866cbd77589fa50c4d6642e92d364edba920bcf49a3882d54c44699e8b0684bc3aadca6eba2b8333396a021c1b291e2ce748d12ac90a3eda8c2072f3cc0e5b01e4edbe2e35de33d27e40e81b7ab7f68b099f459651f1b2c8e2b0d6b2b38720dc3a18322f518338e9e867d6eb0dd5b6577af8531aee1fa853e8cbf5a0d33715094cb22558ba36e8963a7b433394967d62913f72d6a58f22b749abd979687bbee0d3955ef0654b9ebbe4b71a3b8f376e7ed8a92b00515d02812ee0ca9e707c706391a45c2eb2f3c2d581cb347247638f4aca88dd6bb82cd62cb36ece4249d8d1040e6c073bdea9fb5d1b04e44f2f122578c63d2f86a68bc65abf510a9c7f8d5b5356b6b4a59ae8f993b4441b346bf529c56b5d1af7596c7bb4e9fb8b66b43d7b534a3ad95df37eb588adc90fb5df77289adb95c9fd5751b4db6ee3a8ea9c3bbebff785dad0b6b988d1aab0f45b449059834ca84e7e345f6f09c343e5c76178121528fd0b1f850b9e5d84e34a71a161f2ab71c1b4d889e4e58be3c6b139509511016e1cfa943f4f2fb41e573e1f1aeffb8fda0f2ddf47daf2188a04ec8ccc06aacdf893e1b5fe8b3a1d05885cb65452e5125023c650213f18944b714e55aa58a953cab54c1c9a5a84a15284da0c20d19ac7404b996ac1f5d3642744589aa0f43a2e896a42042b94402e730247e241fa21c7228a260974a11dc1c7e7486bcae8a4425efad7b7766307bf4de69137716a5b2c0d6779f2509050161f1c9e1a8dc787672cec9e674a239d58280b0f8e4ce51c98dbbe5a69536c746ebd3624b51108ea0afa649936f92ce20f73f27de3dafc3b8eb3a2038d8205bcff33c4c834f4139f4d2d626ed14d4048609223561431be87c70cbb38a05cc9c4f0da08486e050d5ac9867952a587209d3753eb8e50e8b829070a712a680a2460ba7ed484fd812a36a14b43594886a1aa9091b44954547c21dc7b0851aadabb6f36a0d85bcea853cef0371065459d475dd6c43adf30112b2081fe94e9180843b49b626b2817dee3ad3eae1a4809d5270414b0eddeb817a8d477bae90e772796bb05561a7db15f2401954d4c31f3d4d8efd32498ff747cf0f2ae38f492a000d3d2d5825514257a2e435046da2091cc23214076fa0c6faa1833cb684092a2790e8a4df8a43a1260e0ec136f199c1b2469c2d816424b3ecddf3be1af3b0f7665d519068cb47f3e19bfa23deb18d136c53633a386eb4ec4861859a68cbc520f7bdda8115e34c1a340dab15241159923b0ab3c5ba6c938679af9ff67b47ea913a3dc29d5935c10877096de5051a6de248a4b2203333535d20833604d16690a84105296a3348783c2ed8321e09fa040b545a0fc3ce7bf75c7d80e34377fcaebae5574774f2e8249dc799faaf0b36f47ac13c493f3a7b3749e7d65d0fbf2dd8d1b7052bfac660bd53d287c57ade90ae7b7dd8dd92fea0578842a1fff070d9dd037449765d1778d5d8a3459ac28eb76958e326786c02c6386a3041305b9e604f76ec32784bdce4d946ebfedcc7374d868090fb1867acae7a5fdebbecbd95ba5fed302e5b1826c8470dd95486bcbad1d44c199840b74c67951ccea3e911eea45123dc49f36c94181c219b56d80b1d04b9d69027b2d565abb0a157f0ab2291e82c4843b074e6f1961eae0f2b8b8b4421ef2c58d02a12b110b958dc995d9da509b512328899f87011eea4d12977660689a9051ed0d4423500ddf0a9ebbaaf0aebe22ee2c3872ecee2a3bbc01f4db94588b9d2b08ec98d8e591cf5f0d5be8577121042f70b593d6aad43b0a69a59f8c8eaacbf7cacce7ae8d21c760897aa57ebed4d95d1274fa0624ba64e9aa42ab03da105b5bec8ab2129241e6a931bfc1c3fab0a0751d3b0c61db620b69de8b6f67db6d6d3d7b01f55ee3d755b1b5de3689dd81b5bb335d666732c14bb6379ec94d125a24b9c40fc83ca93541128a2d77a51104d826067c34c2f4bf8fbbddd139cd864d62b0e8aa0ccfaea9414c42a08d62b8feeb112fd5badea14b16e6d2c4d8b2e4bb1169774702987f4038b425d97771f8bbb3cec447c0407c1c7d7abc7b7a4199f9286905e7677c177e6ee8a2baec83749cda3932a1e9d2eaed5d39dd9c5c5c59d191471696f6a21ae380607f647cd176c89c96045ff1e865474c1f715042fc9e1b4400051d3305119ec0f3ba308024f9e510441279730794611845a8609c271f3597b132e71ee4b74dac44be4048e12ca4408844d0e5ec2280d2efaa450b42a8a2ac1561118442d20b000f123b2bd24852d67153e05c0758c480c56741c3c3041b43e0e1d97cbe5ba9f5444a953db5c8d3fb689281450dc275ec2043d815f2516ace3a829b1605162c12a01e153a2503c616bd80ab7f0ad757f35ca84cde64207ff8ceda6033c436d35466f33d879c28d09269ab061fa04b6b6effb1ed227eeab756abbaf167e0d01c58d602830adf5122688a56addf83597d0badb3b6987519b49a3bb27065228aac28af2115d16c2c5f54a7acdcf6c1f7a60b0e52c7742b8b8b8c01760d2c0dbf368cf0a7bb2993a74c8dda98ecdd421ca5deb96015307cdddc1bb5c06bc0b09bfe6498d65ec5d78a6259e7423e24958491499d34b5762ba7823a593bd10344c46d657573df5dbcf2e75a94e8d755f5dca03da58f706a26c3c2fd822265e8087c5c5eb82355d3c1127dac9744db759420b4d97747b5e13f79af83504e9a6cb906efabc09cf449958c6748b67669d9975096d09b5d558f72cd64e1af43f663ea23569f44f9306fdc8f4fa9268125fc2f3bb247136fb3e6b5b2310dba8b1d9a8255eaf9210ce17a5a896bf7f55e6fdf3a1ed93c079f17e567c5fec1b866ed9d5f2f7d12b5a35f6af775ea12fa1811fdd57080b41c3a46fcfa4b19228af2148efcb9026cdf425342c733a88676024159416d0262d0a1d956027adf4f0250cc48f22937ae932fd5209ac99d5d4d8f714265d348d4ebacce824f0a3139e215d7ca3de7df0327df0a33735f61d3cea3351644847e19928af2150efcba0429ff98ec23c4460ca53631f09cfe88078e67b379e311dc416a8b1ef33f63fb31e8f092198210637b0a9d56893d628daa4dde47e66108aa633d1b0032d44c891c10ea200036dd29a471ddd4913ef24d93b6926104791199df4992832e04707df93bf379e8922d32fe119fa7312c633345763df497884677e66684f8d7dff4e836aac2757d29ffc8df02bf4dde6bbb705fbb26b7e6c72f075b9fa85aa9420d7b63b62827dc980ef83b8fba9b1ef5f2e7f2f3e7c4454a31dde203c42ace5eef34519f2c99d41d020f53a29d81904024a6f95ee8c284f95d91aeb7fd356dcf1e4aeb5767884270647f6961d0f908ef5e7cbafbeb4f4650b48cbbaf01f2e77c0e58b4c71f992cb490593f04b3983ca1d4846c4244c575c71c5153422668d515cc25a3644743a352653ea6e705753633c3aa42423d3c8ac3171deaf922e92f0349db484269e92f00ce926fc91644c37e159635d5776359416d24a185663f33c6a6c9e745f32e0fc129a78c3309cb70cd5e4b6c9de8ec713de2e77cb2e57c272d9f1e01178bf9a2aab5d4dcd75353557733a1d4f8eeee4647aafeb78ba1e1de4b2c5d3751dcfdc21546ac2b369d21daa9a8b162c5e5e2eaed6c85aa952a8933595486238023f51c8eb6a4f1a93c6935a71d714e09a501b0338aae5d9cdc096219b1c7a5527776d4adf3755f681a7d84658756c7fab557ab6164f4d7fda306413b2a9df37293d8989772d7b18bc3fdeb4b4fbb634ac83bcd71058fb8228586bb249c36857433554b0e51704d69aa49452f09406822f4a6fca14a499fedb52a3602dc8bbe996b05f50935b483da8775365a3f7433555d6b85a6d095568fdaa437784deaf39983a44f7802786dcf77ee80eda5365dfcd8e67ab329d2a0b4b0f676f351e32e4236a3ea2e6b2ea845a10ba219b9b2afb786a6c0cb3d5e19bca7393c910aad937e4fe7269c8fd7d6d9835fa5b9a0c4d401a9f997df20f19bc5090fdb680b58ea12fbfa027e227c9888867c42b29017811bf8c88279d849584177114ef93169e4e1af8491262f4ec69b1a734e80bfa78aaec6bb5ace5c914fc7255f691e05b22c782dccf55d9a47d5feecb7da00e98f379c1ce5c82b6dc3e5fad16544f40d3905d3065740b3dc3ac8182595730c9faa6344e7433b0b46daaacd2f26c2972d94e6e1968d2d042474d0377f2cc01096f89a85964a035fd3559d653faae6143b0ff6817c54e3abbb29ed69ac52629feba5bc27654b0fd92f6bfb0ebdc3d1f392249979fd45e46282d744a0be11a9116faacafee34bf82987277537e2282109ef55d77dd16a8b17a06dbefa660fb35f49202cdda24adb526f3682b8570be285d70594709bc2237a8c3c34d1a9e1533d6f7b9a2f47680bc9d1a23e514164bef59fcfa3e17d0d411cb34a1d328f1d192a057d0a288ae84a5a55090bd1f7bd4e9fdd1cd8889a9c3236bcd84fdec8ca80f35a7955e8e6690fba32668004aef1cba4f4c475db163f556749fd47c34d353b7c6d24caf2800715370e14af6b217d464596bc09a9f390517aaa4bc2df4a6d6545bcbd3c30dd9c9257ad43ddd5ed3cc47cc49e315c2a5d7535f3276c67e09ed745f32a69f7ec233269ad2516578cb5d8e7e8ca82090e9fe2a71daa430a5b783dca73db0a7975e7a3b5e0e684c3f743d1cd094a7b3e3f17843983b4016cc1a0dda1461ca4017cc58df860d1d74c82187d72b97de4e2a97602a97de4eee1dde6948f2817a1f4cc3947d3de019e60ef06ddf1fe95499ea8d859a5c12b93fdc1b7297a39bbc622919cf6a9d751656c27acbbb122523250d31b25e1ac79f3eb2582f9dc51a29196c7d09e246b630d5055b7e99dad6619fe9f6162bb137e1cf08eaa69bb012d3519856561defe73524d71c76b5bb5af24e6f59b960cb5ae3e95c25a6976eaf12fbd3c3106908fbd327a900a697cea3437b5f32a7db9f6e31e8f392c160aec64ad8de13f642a402340d353f117e0dd1b414085463bab561ca405c8d352e5b2cc87dbb446e8c23416e4ae9c1b07ae0cb1d3e2289cda3839f0ef542ff7aaaecfba9b1befd74aa8cf5d4455cda561e5df00493461fbce0f75365200a26aeca5818aca9b12ebdc3e5ca09eb82efa7d44a610f0762dca534d55ddd1af8f25e1e9e89e27d46f5d23d2c733aea283c93a2295d85678dd19c9ec2205078baa5abf3a24f7d096da7f4d3654a3f9dfe747a0acfdccc1a9b29619bdcb49310a31aea4e5a2a8bf574703069f44db7f76c372508447b3cfc41f976a897c5ea7c553e5cc78c7646525a06d69a91adca4ae568caf759db6a9d5e8e7442a79f4ec37ac433d2a9b1d14eeed1948ef5c11195dcb740b5a6d680401fd996d46ab5daadde996bcd8d2df7ab4edd01816aecdbf9a08040608d4e6e2be080935d795af1069b3c490598b4d3274988d279b4a7c57e54a37454931b04a2b49ec4697b20f598356c9ece1dde67cdf3b586eef0b0f7436b9c60eaa0d813c3a4d147dda679b7b3136c8a0ab60481c0a0516d5433b219dd4c1d941a911a83ade019668dbe07024d1aa41ee03fd1c8f34e29a5dda8e68e6a239a519391cd1d3969d8e866d210832dc19adc3fd5ec8d2df7c19a2a13e56aacef63f373934540a0ad69253c6bece645c1fe7c22fad2bd97f4220c6ab960100884c120100804aa354d023549df07b58041222f8b07d431fd5418eccb068e86f5ef2c5765a5d79a9e2afb6c361d1defa79c3da5f7537a3f3543545a098b523fb60481bc1f108807182c7d39730997880dbbc2060e526a778277b9fbc25ef461b8e2170da3366c606f592958db9a4da91bc2f922043fdfaab251f8f979f18654fc6cebb449731a466988e29aa4a110b54273f4f4d643ab3459f3b4e24a96204ab1644ae9964c1ff23abad364d93599523a25d31e4f66cbd28f2eec9fd9b001e446448d51216a8c96544747d498e28830ad293f27d487d234f94c3740675011dd36a10e4911ce17e5a4422d3765a28927a440821192b091c28d3643841b2148f1c1691358c7064c6e5ad8a151197c864ef90c1251eca3b0384e13d40999286e472802cf108200841f9c42815f323487e2e034e721b5e18034b0254cd0f759db6a1d96652f4ea051284e6d7726004750a246a3b0a38cca13f7042ae88eaec9a8ed50c8d65663b6d6b0198a736a63820da4e49a4bd1ec98909325f7b1cdc97d11e3d0286c2887324c502331e2a15128b0d5e99828d6e6e410b4d91958ef211cebcdaf8631c67848975ab4050787da70985091676b1cc7f1e3776852f5f1369a043f8ee338cbb33c8edd2a23d8ee4586919e52e52a665ceac69212b61bf20a02825f92435f92434fec6ec906848bbf8284c0491ae2e221300482e08f9e4f06d5131b17d119e2c27b68a5d81d0b45a7e632ad3c6c733e9bafa6615fed6bf2d57c4ebe8e32713d27b6105443a34e5cda821e1aae4d1da4dc2feb4daf33a0020e07a526e49ca49a90a369d089759df4bdbb2aaf4dd77ae04d9325c581030707a914fa1cdf3ae9d61d7c74b36124ece23041ae5b334c0983a911b72aea632935964a6329350ea9797c38deb248fec649c3f5f200301f2ff2f3888d98c9df87f113ca04ca7b290a0aa289574791687cf98944f75c6f2c72fd7be8fa3cef7b6dcf1b7f27ee1dc4e5f7fb16a7a41ea58a65c2b9f716c9b14a763292592ed99c687191cc32f8d62d81e4d6c3975c5f34f9dd8523a4b547d7d557dc9540f22c03c9ddf84afae1baeb15c36aeb6bdda576b5eb4cb55ba96ad7d5aeab5dd77decbacef57a69aedd57ffd5cfd5d5175ded304be472d6c1695729f0299e09b678cbf4d651aa79c4bbe85fe8d6ea95b31cbaa17bf7fbbeefc7f7bd03cf52a9bc7778ace2d575278544f744de90ea795ee781a07745f71efefd786f049243f780e40ffca853fd43327bbd928250bdc34754fff011129259ee1e9a6555259d75daa1f0ea1d2ef157a6560b1c89ad9609974a46abae458bafba166fdd16a977ddc3d5bb554bf4d04ddd754b20d9753abb7c4175fda249d5bbc39a34bdfb0e2dc3d135290895ea23fe83944ca65cc751e5fa777f186c878ed5cf16285c5a126eb56c2fb363a52ad7faae63f5a56b32bdab3d54dd55aa20ba17b09aa259848fa43c5290148864267acae5ddaf761f2f0e1b3676d801067bf1e2b9bee4ee2557d378d59d0d1b4d4fe1d2aa7069ab6aecd888cb8f052423992199e5d05da24f11f87a447410043b74cf0b853c0fd7f3a839d51948eebc877496bb6e8a4e3bd17974052f9e0611368585483fbe632af27089448723878e732a6573eaba7cf5bad42299650fd0ecf25689442eb33eba251097b36e919c223a0dcb157f345b445bbeda27e65254bb104f6ddd5a71501470f5365de470eac49d341737a4f5da1a4d17ea70e9e1ba3e84b1ade9d0604b5b13ddefacefbbaa775d474941a617f2878f883e73489f70820d4d78a116faa0595461d9d696280245ab886dfc5c456c1d451128ba771786dcd52872f752746fb9fbc05b77a81fd679d49a475c7a51d428ba3020f170393ccd93d4e3d2279cd851850f54571479a0578a60a77aaed77b1edde1b256b07ab5e2102e978440f02e570d55d7fd701011ec8860874775b9d48b30087a1849e7e1eaebe5795e7d7749ffbe57afc7abd264eb1d7e0882202e4558f4934d17ad2a88435e95868570e8e18d87ab9e87c4c379d5f3de79b81cba2874450f1de7ba0bebdff540f53c6f75ef617bb54728f40f24b9eee260902a029d5cb2eec2483c5cf670797516e9ceec61d279f4185e75f1a5168987cb1ea0f9fe756d7671cb24b9ccbacb2d45b093599f39c4b64664ddd14732b0254c10ae0663f872e8c329a987004e493f3abbae6a812d6dcde5c303f09b1f330c838ca0572440f49934fa25439d30c1961ddcac200351f744fded993e716928ce4557c64d06151c1c9cd42da2532407a7888956e25acdf5b6c8b7cc3aaed558bd35ac0895225468a619d7ba7fb5bb97ebbaae3b580aaae899c20c03089a3a6e9ee28a9b1b4213d812d7706a6c9aacdf6b165dc66deae88c904680568ff13375d4c3b84d1daddba0289154365589f4b2640b79260dd4cb59458d27e39934eacd5682a28a82429e8ac555cf0ab7cad5d8ca6785a5619da5e9ba892ae08f9b1f4e7ea4b2ea67d2e8ffa8f9e81318c6d66ae150f158f033b33e9248e4c1047561b8658da2ecc2904b5cf3a2c8250e40780bc05db6b4d3b02e4109c0bbfbe003a636001705c08700e092949d9e82869d1d9829f210a7bdc7cb2ff34802154ce0d89e80a20633a0410e740025ca0e787083275538813aa12fa02da02e98800422008107d434b171728363cbd181b2238567caad4902bc9f834a933fbc9fa3a7ca28eefd1cb826690cb430ba29404ff7c0e5e7e3ddedf1d0adf76ef7ef865ca2b7aeeb63ebaa3b3e7557375dd54937f5d2359d754947ddd27f596f7151c7f7bfb7c55f17dfe5de8feeeb2eaecbc53b7a28fe74c3e3b82fdf714fefe1e23879771ce6f6f0d925afe3c2fc874b6dfee2cecec3d571d97df11b978703e0ca1e736fdcc605c077b831a74bb8b4e734ae8de7b83bbcc6a511bb0163adc91197ac87a69709fbe86cfaea75a749ea84adda749a9c4ed0f1c9526ef5b54a9339186431adc6b90a71c29b500f952c8ae23b5765b3d0e7def08aadca6698eed45893e3b1e4152ec78b6765800665d327a987c95ec4250dcaa2e95f93ab5449b3c8661fc7afcbe3272e91d0a0cc7ac7ba5fc3560fc35c980b7dc25c690896f592eed01d4c791a5677f2bcca89f8b2daaae4b2cbe2cb8f27d7ab6eead29f8689475d9aa561e2679702d59878c242946e6f29be74d32d91a8c9a278ba43797255466ba14fe986380dab27ddf0a66135b4d5978060cbf026575bcda93a154adda9dead3cb74e6958bdaa66bd3cab680319449f3c7340e25b12d3140c57a095d64a2be5d1b0a69452ea5d242fd9fba161d4a3970710d1862f397585f55abc3473c68c4678d6b09eb9444cfbe511eea1612dc4474358860bf607204d5f5e2ca10539dd4b1645d112483dbad2d9253197ad37f9d230cf0996c59481333675d04cef9a64678521e45091e98b4c2fced0ab1df889be1aab2705f1bdbbb47be9bcc621f1487d0bf6f255f0f4fb5aad975615e156a8de9687e7297dc368cbeb2a9d5ea971eb4dceafc315846eaa0e43a15a6b855d55e8a5b5a1fa83fff045b3139d7eb588c69d6d55ab0bbdd65a6711e6ad23418bb445b145da95d4e33bbd73f6254564d181344971ada25863f522fdf7dd1f906079344c88b0e22a5845cde38726bfd71a529cc4cbddf81524d4d8035e16e11e1a16c262c3481d89da4251a0308f7edf9c457ca26f92a88d1da4d872deca598923f767331caf6d10c3ed0b1fd0d441e79c7427cf9e37794e1b35d67306e5f98913a28ea814fd53635d6d6aac3b5cbe15c2f9c3a08dba93cb7e41cbc62637aefe7cb6d5fa742ebaad75b353f37dd6b6ca0f975b9413ede0aa6cd2723edfcfa7b313f2e980443555c6048914cdc55de860928e8d846d37673243baa9319b960baca4450b2c12d58872553613e35dadc56beb9d0bfcd572e7f37363c3935b14c2a5ad38d712edd0497bcbbbb580bcdb8e17d4c353c3dd6aac732dcf0727caed64a086424516bcd0840a9840eb7f3b55565fe0862d4f72f0841434aa040aaa7045d01553a0f50d2530c1852b94a042a34268830b6ad4d0829a77239ba1349288df2e0e54ea774125ac8484bb1e385841aa91b012117738b146c44a30ee72ef7b36182b71813b9f1aeb6325adeeaed655516ed2e8e77277bf11dde98df531278d979dcecc94095b76b5ce065f24954c27dbd95029d58a35b6b88bf7bb9a96cbe5c5e2e530deef6e680b17bf2f72387ec7c03070c8eb660c5da95863cb356fd0b3b8dfbc41ff7245e0289c37e85b579c37e8c7eb326fd0b3ee6bdea05fdd9779835e7559cc1bf4a9dbe2ba9837e84fb7346fd0db4b9a37e84dd7346fd097ae9d37e849f7346fd08b17356fd00f68d4e317a80dac93218207102245200141b78d26cb6f5683a3073ae7acf8c51351f7cb27da6c219c2faae759a0876e8088222400021a9b2d354e2890792362a592eacc0109e5fe0f441431b3781e908655212eb6f2e8f4a42b228165218850c10299317a22663d34387375f650c48ce210eea1c9faea514adf613103b0dcc35765a351fefa634271d8e8bc50a8ebeeeeeeeef6bafe6156e9cb94c660c559ee22cc7a689262dc6ac15e58951ac2f9e2457d0993427928147aa35572dd7dc5eb604df6cd0e3866487586a83411a63634d89252db9c342a8dded07753fcd2ea68d7e473bf6d4f3041cb0f13b4f8e850b39f04b2f7524a202379c9defba6b6a87055368262c36ed7b21deb8bee6cd8c41f92975cd229d96c29e70d3eed2385dc75fc5785f55ee9c3f2eb545654b98ea127e5d821f3b36165bf76259d528ea7c2f9b870ab04e5c9f87ee9b3d94a1f948f0ab9e252f5e30415d0a9c2e5364561cbcf966914d6cba2dcb9a10b43e2904af2315f3f61debda6def4ba128f1e7144b12797259e9d86d17b9d25e5c984279a20821513545d79819b3a6ad4ea795ed787ba2f3a8c5a2fe4816ff02bf1745ddfd640258761e312f68d2f55563cd1f7b55a9d0a17c222efeda9705f89a76125290da3b9fc3edbd441fbde3d1e3df1115e4602cb259e1ff3a51560c85292528252069dec932b834d85c5b392bbe223127d390c892937d826a9952647175de593290c96bd97cfdd2df02e77ded7587dcbfbe72181656a1bec3e58eefe9186b8aa7ce107b9fbf224f508cfa3c39118ea3e5b28a4c272a5c91aeaaaadc932062e7f5032fdfcc426f9980f85fe8d4e5f82d2a4e8529aac2728e549e7d21c5e9a47774e1849b43549c2a1ad61540c73e6e97b8557893eaa2baa9f2690008b0a885eb56555a3d292e9aa96e9aa49a60f479fe863fb72442ca114d420775885b3d2065b86dd8479b40441e8cefab03f9e3ecc69b2f129a76123f63c1b0f2714c2d2e433c5350154850876f4b1922baea993ecfd25c438fe65a435be62d797b42e33bef56e6ced9297cbf5e2f2ba0b1def78d78db4ee8295b8bc7517561245c6054f0a2ec18e6ffdd5afc6af215c77b911d75db0922832afbb5cc9cb5d37e27216678195bcf0efebfee5b0c7cadbb0d1ba2fef2f215efc5580497bf19711da032b78c0248b4ead4683f1f1be78937a0841c364bc8b33195d7f5dc6f597eb8d6558fc05cff0a0fde57305285ad6606b41ff72ffba2c3ede288d5f438ca3cbc4b5f88885b8ebad3796799d059e61f1d7c7cf449179715d66bc0b88059e29620377915ac20b49b0620bb65a0d07ce1c3120820c5aa0022ad8d230815cdc497bb993d6ba93f6ba9336692c26ed51645a1f3f8e77b9cc7897d136be059ea1354c6de34b5a1bb14dd7a86dd24e4a60e142197c6ab4b26bb96d668d814bb0321ee332f0abbf841603db4a221ceca4bd8c50da784a1beffaeb51645cde7aab75d7655a77e19928ddc23899d65f7886ee505ceb25dd51124546890bbf647edfc22f23f8f718cf447909d17a0126ad45676a9ad63a12b55ffc9219effa7817fe6c35065463b4854b30be66c3cad54da6b779e25ac7b6059b4fb732ae8da0e369abbadb464bae3662c8de3b3be21c70945285f369ece18fe3ed6c785c3b2730f769636a3dfc129de69b6d4e80b6f528a5dbd088264a684ef1a525791f5659b92a5cd761552e54c225cd73da6e6b18fdaec4696311aa5cabd5ed8560d72a2b399267d3649f5e852be594743aa6e9f79d6c275b8d559cc4cb5f4ec3e8676bb2e257dbbe9c2641fcead34fa7490feba482ac2aa7b2d2b0141b6c875fcdd3b0290d6b9edb13526867536354147a3677e65085abb2920d4a53294a498a29f784614a13954f932955aec9d91306285832bd926977b2b54eb6ef6453e1e8186c49678787de443378284f29aab8c9a93c7bc25025cf0ce62945152bc8a56aacc2d6dae7528636920f3a032bfa94684fc6956c259b4fa639dbf8646b526a0db68c818b81abb22e06aeb38981eb80328d81ebc95e577be6d096c31797bbbc58906a412c5e414055c6c475164c5cfd4963e282459f1aa358890b8b3f5db22961328e7789f11c943079bd5cb092d1e52fee72fca284c9cbcb5f87f1978bdfdf98b47812ae3369e1022b797939ec2f327eac84098b27e19a34262c3e530343938195b4705dc65d31b01216984912e399b0788cb3b88bcfa861f2d85b9cc600ea183f5e0c1fd18c193846ee2dee8cd88d8183dd193f33806604c1c801c900daa13070208c9eef4c5a6018b71aab32262d9ec488952441afa045a99f34262d300c9ed74b6fac11348b94095b966cf539b8c9658c077603944b19403376aa2cf67da2193bde8c9d98cfcbd84fcce7964b9f0dc5b1258c1df14724c5c0d1262ccd25eca7940104fb81dd327dc9d662d471b9aa566374e775cce279f955b5c04a5c3f56c2e22e280b3cc78b76be7c75d1077673acaabdcc9caa4ced883ea932258a3e285c4ea05ca660375596b2f1cd4ad8cded26b5737a194055f67dd6a6401940a2cf0c7aad58147d1a8b3eb99716a7175d4e2ffe8ce35d4017835e989e547be5f0172e3a7cc6e9493b5536f322d554d98c6bbeb8fc8595a03418af97249fd7eb315e5e927e64b078490262c1e2b016b197a4a01831fe7a39733264fce5e5f4816125280d76162fe74f0c2b4169b1bbf85fdab8c72f6739fc850e78c60c3c43a7c6e88b3b23873ba3e7ceb8a9b119b7193633786a0c3723a8c67a6600d5d86dc64f8df1ccf0a9b13b237767d4dc193b35562a61eca44a183b319f9d72464da68ff98c2fa78ffd8cafc3c8d518057229c5825ce28cda387ed6d8e82f2fa72f18066e66c4307a665c18c62d74c1305c273d8661ec50170c23575dd80629c6339bb75cc2d889f1cc9e1ce3c9f4a59e2a9b412245ab81a1e12a9b79398bd39774aa6c667c8bd39776aa6cc67517a72ff154d9ccebf7f4a55b95cdb8fc87038ea684c55f6cb4f8387371570e2fe76da6068696c3fdeb18cf080088a6e47779397b9cb0f386839e5cce20109733e8de183fbf315cdc182d6e8cdc8df1726304d518fdebbab8666309a8ca640001fdfcf8f8cc5ac698b9984d0c5ca68fd97662373a319b140c522f48399937e867c77c36c881e2064f70908a418a8954132927523839d00194283be0416a062919a4685083271aaca732b025ec27d3973076323d2c28f47a0b172fc7f7ae1a9146a3c48c96c35fc0882103e6f2f1f430a0ea8acdd081c50eb11aad86ba8c344ea3448b0680d3286169364ea3c4470ffba9b224ea15575c41a3840dda0d8c5a038c8652c3bc414fa3c469282df3063d0ae8366e8ac9bc415fe3a668e60dfad74d25316fd003e0a6949837e869dc9409e60d7a979b5a62dea0dfe1a2d8306fd0eb70516d9837e8675c941be60dfad845bd61dea0775dd49679831e7653279837e865dc140ae60dfa1837d564dea0877153299837e85fdc940ae60dfa1c6e6a05f3063dbea838cc1bf4f7a2e0306fd0ffa2ae9837e85ddc1409e60dfa163755827963de189dc59df38678233c1d839db75ca2f294a28a1a9488cb2fc4a51de1b27558c5a58d5ca2801a0574b23d97219f4c0fe68983c2964d3f26314c1df53db9f46c42569834e8fb8672b406bd4bfd115d97cbcfe6d3b035d8f264cb27dbf7593b4f369c67f379a3976e6169470cb6a493d330180d83edf2b4220c4ce452853bd95a5e49a7c952f4c9f4743c7dc926834cc31d9d4c5fba3539ad08031032bde853652ff093e63a09972528998ab854e5b264c365698acd235c967059aad2b7eea9317aa3a2c6752e67a9ca878ab00a47475cb1adca5456cc187d156c5bc8f4aa30647adb4ea69f4d859b34e8d5502281e5face664519a8bca443ead1ff5e8d4b39b964d3b29382ef43fa48e775dd7fd48770d9a2920e15897029a7613995065baa70369f2e553f2a5c7b4d9aac7836acb3f52dbd5aa61f7e3256db783a1e2877b685a394255b471bb10a5763f468b065c986dbe9924d8553e5aaac64ab31facfda926d54e15eae6ae3cc58b2b5f04bc635d3baab64a3fd9219c72fa1b5ee4ba6d53f5f429be06936ccfbcb88ab75d727ed65c445c978974fdaf4b0e449fa31a5808295724a81f3e97ba4e257101f72df87dc437cc04744ef50c8039d8fd4fb985240c1ca24f579b40fb9de2905ce27b7eea48d77d2fa6ec0873cef725f46c6b7dec24a5c3e7ed25cf7654489eb2d22eacd4bc6f5d75f78c6853ba8a5c211e162f39269dd75179ea9ad11ab54b84c1b4887d71256d39a2b84f3856745ef6ba36114cf1a363f6dcc3aef55846dd0bff33142f67009cb738672d8d949fa4173cf17131f99b73850d18326b994e927a59709e7bb36dcb4a1494ed25da110242527e9dc81564e6233fd92a6488447742d7b9f2fca13509eb9248a253c539a012f8ebe84d6d948f7a84d27be3fef6747adcf46c234143783ec616ad389e20e36a72a0b69383a827d4df032227e4df032af214607ff9a586674f02513bef4b0f425b4109cb470563a6be935eb65689e57625faa7808515412656299d2ed9588275d4994c632f6a52b215d7c686feba3d985763c7a030deb68ad611da5a9692f8b05df752f696e7c497ba82da5623d1c417b5d0e5ed74322ba773e3e4454d9a475210c82c649a84dc3bac6d449c3ba9b8ea136dd976269581722853e49428046f4277843ddfb1660d2682e0796e48a8f787d584422a2ca5ef32e45541978d265c0933e4f22dd05cfb44e72f90ce96065f710fe91eead978685ddbd6c89f091ee21ecdd0375fe48d76ad2c3df0bc53f669eef8c10e57a1a0a838d8aa3619f48f1a435e8e7b511f2406b50bca4e7c5df39b0248721bef3f3b4e61089fc4d1bb99c1789c196a6f7612651d8a41e9f3864499e37bc25cd33fc313f3a8f5e92a74874bbab4860a100e5e709852a724cf5f3a2e8ce1c86176fb9248befeeafbbbbfba65b6bf7f7d52aaa221ee88e32cc8d79a035c2f0a5cd94149654b6d58735f94244ea51c23cd01afdbd6ffdbcce1e2cbbee11d87734baa193ae78f036052f5ed279d4f0d68fae77f076fe6a99a42b8e4c9ac2f63d23ea15af7e97f1ac4178c3ba61270a02f59b7cf10072910d6eb05ab672abce395b2d6b7974a7f56b5867f1c7a3ca5c33565f6faeefea0d1eb53569d4ea23844c713973b5daee9fd864f7da4d9f4abd9a8e44f292eb5f2ca1053678a13ed8b2951b96a353ed0f72a9cafdc9640a2fd4f28c620558f20e794e81c54adec9f41268d99725cf17d164f756cbda3c3f65e0adf298b2d0d763719382b727a7ff49cd6927fcb23437a5b4fb8dcb226ab9ebbbfb7b0d7d66efe1b44f4ed7c12e7666f0d6302a1da32095122e93e4d21b97fdeeb41fbeec6ec220957741803db7c613bcd5d24522de54032b026f2015b087e62e8b3a7bd0c6ce5cea42f1a48f1e96eecca597a0351d0ce9580b8220088220167a300b103db8a565a8d35b5389b60f9426e912a74ced056f17ecc12015d10b22e127a7d37b5b9a14319582c31f1abef36e21066f1a46412761b0f5256853bfa3eee882a26e51fe2ab62a5be360cb2f67536560143346bf844c6f057db5016f93060de9dc423c219d934ea66f7567513885a394e08dd657f7a0ae136b55469f843f6117b264fa504b93a44cc54ce7145bc07122ae8056bec865f843a7d002197229dea820fcc9f45d6d273c37379c9e1ae09ec83559361499beec9c4ccbd602d094d26343adc9f2b3d2354d96e00a323d3db882a9a35399d21d4aa5ca8b5cd29d2b587ac825dda15ae896501172d9346537c9b7711b214f78036fe00e6893e93f2b4d965fd097cbf41e1b78dc6e38518f0817d209d5784199961eced3c2e9939480aed14eb8881aa3a86fa0c6e8c4429bb413c908ea28fc9a0d5b42f3c9b4da649f1af3c9b475a0a8d1d49ef06a7deb5b4d939a0673b7aec1d431b13c417d9212507d6ab51aed74d4ad51a05a8d76c2484c2cb42814a856cb8d5fd4c6890d88431de7060787c2f5767abf6f6c394e9aa414c4d1aee212cc650adee80937a659dcb17d69c083ed19e17aaa8cac3479eac42e0716f5260dab219a508d18aa7941e228b2be9aa7d5a9eeac4ea7551576ba4fac55cd24031a7a7a5d8d2c1d209f2ca3eaab87a6f9a2147d5e58e4f0c2e5d57285a6348ca76156d86f09d68707e0f4a9ac95825804fb0ae265d6bdccc2f5e18d986b4ed501a36067c34e387b3a8fbaba25890756e5c303002ba9424f0b76f61d7fc2ea3c5adc716737447d9252b8de268d265814668255b154afa5d59bb46367e1956ab30d93ba09b55afda836b1f4d52d9354fb85dc8fd5552bcc1a7f3aebaa6ed730f1a66beadbdbe5d215dfabd761ee4bfcccacd7aa336fe1959c4f155b77a0c865e5e1c91493eecce2ed151f7a5600dff130b7e32c4ca461f5401a56b930e77bb55aad2c8ab4e3df14bb3aea2f3d5cc1c0bcec72ea299887ab956965c2af20ac9bfe8375530a0573d585097d72a4a6a208845cb236a516d8d930fb53dd11c0c5a1db4c4e8928f12cd44b168bf5954f932c93655df5d07425975bddc7b43a6b95f359d9429c52d0ecab9f6caec8e5930d1f643b8aa214b6ec7239ea114791aac48faa97e3570fc795f851fc91b17edfc8faea212bc41157acd56a1c45d5a8a7ed4f2e43a071a4695845bd87a73ead288ae111435017cfbaeae2593708d4c59b82a02e76875a14a4ba61b1ae22b170c9ba8975d24b9369089675d38f98aeb25f994653ca64c2a5fde9a1bda10de726bc61a97eba3db154372a5c3edfd11629552c8d7a6adb1daf1771f38d59238b854576f695955cbfe38ab88cba885f3e6816a261f31c5822d3b07adc4f0f98a66d4890c3dc0cd3505c13f20a975f10f325cd22ee5cf0701e2dabd23305bb628922ab13c3789309533b9e47b3467c4497937459bc6a14e4d51ae6d5d0e43a8e5144550aec2ca65e9c22959689b81ab369927cbdd8a4c998d78b354dea70fc7ab1d6648c2d4deec0d3a428e2aa885344282d0b126d4d96c24f140ab5884e9abc52c49c5c459d5c6fb28d97224eaea6d1645a9956a3ea4e964aa53aab1345964ac50349c562b1583c9cc522c1fc4678e3863698df0802e6476e1c00e7d1a10d009ff19f26c7d59db93b87351a311a8f3dc61a59acd56763c10f6b3586376129627b581e6de471a6c2e5c76a59f1c2fc803db4350903a362adc21cccfff037c481912143860c924a942103dbf3a83097e626895d3ebdcd9f3f8c0ae62c981eb187302edea41ea26a493e51d643167e0d19cffa8ff12cccfa924cc31657979a9698300fb4868ac65537767bcb679877241fb0c360171f6378b48506ec962e6e00e0ce7c83ae6edc18c771bcf1199e08801be61a36e3c60d179dba3163c68c192e3e63c6388e2b312ce5c096614ebc0d517c3d690aeb81ce221eabb0233e827aeaa88ba7a38914848d2d3d4936cea34555133b6fa3b3a1d88363994cabd56a65baaaa7cac2dc1519589a4d26d62a6552ad542bd50a9749bcbc3a8cab2eaa5658bcab5b8a47b19eba250b75d97dad3ef36b85431c5b0ca68ed5c7af2ede51145160c71358f1e3a85a625ce1d79079a4c6bf294c11bf885840ee0787b3835c864039f276400dab8fb91d161d6ef7531fe376a18f88bb36ae0d5415828d0ec70d733e0d1b592947b85c57678d82562ad5aaaba862ad4641b59086c403c88551c4a32a0dab220ec79d3c5c69587d0e756eb65c863bb93ebc1151e7d12e7c543189975d5c6a5ddc206f14ea75facc2c1cda54f984431cd40d6f6a6c19e66e315883ad4a142dbe9b1b9c8f146a11ad6e563f2db610af125595d4a3f41697e61ab7a3f9c3819d1d5ef1f9c11222829651c4d5707eb0785bb4dce787f26811c5152994225adb6afd6578339b658fd423d4123a85360dabe5ea6119e682ac70586b028a739ec4d528ae2a6e6ad991c20a352af5ab5134d583a19349a35628577dbc631556fc0a3f99893527cce1f1e2eae36b6858173fdad230160e6bb8dac29a1aab2f439a5c8fbaf9465beac39a26a14d93a121e4fad0c9f7adc49528be493dc4954d1c2f001ce5c55289e2ca268e575d0914d1b0e9b31a5faab1b9d17483634bdd04f330ccc96ce01f3fc28e20d921d323d921dbdb2659f808bd7d7d18a2b038b65830d878841e75d6533f8d678d2c152bc8eaaaff585d7562b16ee2d1a9b36e6a1c572f58b87c16516f7bd48f15b6289ac3d4ed2ea38e52dd23a2eaa43ae15790f1a7ff187f52e152bce94b72ea2cd6f8fa0265432cb99e47df1729b4e5aa6c565947ab7f91de651286b9e14d106e3613b48a892dab8d94ba28fe1b6f8a762bf12b988c2349c1dc230121d94fd290d2e9c73b5387dd20d5a60e93ba8c8bc41ee67d49f735248bf808cc29cc510f6df6e3f8af49f1a3eaa487aa1bda703c5c7813de88e5a9b4b4ebef889ac597ab8baf46d46cbfba48ec51afb0874840a60cb1cb3c2aeaceac325d2366b2bd8c6b0fb3ef9ba4b3c51ba0579534a77006e84b0f6f98946e706ca43858d63ba8611547063c2f3b59acef5a566d1dab4f9d964b72c9baea20689f2c8a399a1d7aa5268b2d9e475d5d23409057b94422ab8c10e5152e39b024a74e5525921db28ac6aa9efa91d44741a9d3875526af2ede72072c630b05db21b31efab06e88c58965dd7e927e8457b0549b78f1a88b3f8937bdc8e255ddba4876c8a9db7b44977158eb3bda92045b8e825e43c43f146f06ee4bb81cff87d3ae26698b8d1d7bba0d9b18d0f0340ce663f598ea07364fe8f9a937a9c6514bc36a9865bc2150c3c29f86d5d0671c028d571a373d6c71746bf284eb9747d5e9361e9eeee8369a727a39e2c995e73cda24fe23824954d17819e66e246165bcc5554f661f733f658b94f1d9b2514f8dd58faa9e9f2aab36f11497add4a8e7335528262ce65458b412c50a60c7492fc39b2a1300eee1fb76608ccbd5612f5b79e615ebd2b832ee8d5b8659727dec26f1b228daf879f418e474144f6d5c9a61770465c61de9f08c6ea73b473b1f0fec142b7ef532cced8ce3388eaf95555cddd196511069b4a508f6c5fa92ccba95a3a0515068c3218ed8e5f0fbac6de582465b9a64bd3eacb15897da9135ae6ed519718bf835a4cbe2bb2ce297f825590c6a589d7975bb1c4ed224ad70cd1171129a7b88a34f16a026c5bf443cc28dac8c775465d4d3a637fde9a1c964b237a554b7aa9f6e69b3eaa1c96432a5542a7c3a8f9aa2b74db7eb3ad53fdd4ef5272945eaba4930624ef4e9987f55580f9c4eeaa7a8e9f4ce86a56e69b3e9743a5d4cddd4258527d249a148eb59f774bb86914817efe4d1a74bb2d744fa249970e80373bb0c73672691707805c46243531a56619c66d88921a94798d3b09b94a5f674f13451523feaa5788b6b8e78abceada78b3f22e24a71b5555bb5d59caa633f7bfaec863e21ea863837b4d9c08a399b3aec5f8ed5e135f77a3a9df0c8863a35568fa271e3a76be3e0147bea7c6b5215ba356c8aaaf2dc20f6d8c597b4266753a3a0dc2d4ee1d2e648241cd6465ba0b0e243128fade18d0c989736c37ec37a65590c834bdbcaac9370d9caa71e285c6d93464d9dc09b058190431202d8052bfe846bad44b02238c5d22c5e55022bbe1cf5e43ac576165fb259f12a12d872d45349281e6cbcd6787da813426992c6ebc39da903957ad979c6e7dc31faa135ea679c70a31fba23ccd11ab5739453ea3cdcb835a7ea542895c6ad527274c79c357678dde1658739a737a148a932cca1aeaa61a857294d76bdd5fa4a0557afd49352a872548e74723d5de2942b0a579e49a396a39f3cfa6992e227b3d78fb234694741265448d3b09afa896734a5c91416730daba2952358f1a5880bd5da34149a5f49c430ae62dd23e80ed5f6e970cb3027d7cfb8a52b97e14decbee8cc14ca386fa19b08c3b66c9381473d4dce1c4e2b7e927a545bd5695815671eefcc3060e011873e38bcd2b00f8a9d7f71bf1cee872f58bb17fc05afb8b8608b3bb269583d8b3b7ab9a300dcd04f8dd5fb1002caf5af3bbb86a56e2aa9546311232e9fc7d756abb42a1ce28ce3c79b3eded096c4cb25d5582a89f8882e9fbec3040d0b8bb77c1657abd50a97e24defe1eeb873c666b714df3a25f910af3a8fcaba25d5a5a41ea687a64bed29c84b9fa4d30977a4212eaeb8829a4a3f994ca20e9164bd146fc2e30d6fc48fa7950d158e8272fd28a849d4ebc39b26c5d78738a2388ab8ec7292cea8f0c7a78920e432040a732bf165985badc6873fe14e98f3b192eb432c4d66096a723ec1e60a8d68f3704baedd136ccc904bb126d727b0c02697a2935cbf1a451fa872491e555fb52ecde25d3df469b2f43a7bf9e5d1e6711c71eb3ddc39633bae188644d70daff8d852e9612ef52605a1fa78f1ae70d9f9305d3c8fa639cc3579a5c9d6eb439f71643df5162908d5595f5d119777bc94d46334dd72c4350dab17ff0a325efcf8d343f1aace9a5925e9bcfae9e24d434c67e1d5254561c7ab2e9e9ea4b34a342dc9e3bb2c8a3f8db75442fa517329e2f21b6f79251b2893e41204799cc9e3a7f8f1532cabc8a38ab766d63d620408b2ea338ff7618ba328974bb2eaab6b8428aba8154f6227aadea93c9ba7d364f87a0f0a786b924a93ddebbd9eeae9c05172e02865b53539bf238a95477cb589e2bcdd4777784a41b065a8673ed4c33a123579bc784551bc25cd92591fef8ae66422d8b2da464ca534acf2744c2d19c1da9db1f2e45a6db58e39204dae07eb5d7202ae1fa8a4cb52d8c11e854ca1110018000000000314002030180c0705a3018148284b92420f14800c8fbe5880541847494e29650c210488000000000000000d0001eeca73000074b226b10a76da5696f1a6847f414268f96c5b45dbe27df3c6fe8f9733f0568925615347c77567d043d29df1c737267b6ac836b2ba37424be6f7877ddb3a0c47e904ac215bc402ab38dc9d1199e8ab30902179f21502f64cd36ccc2051e134f6207873f19a78b64af87ec4e8c0c0c0b7509ff868cc6e54c744824dbf1f22b38f9149e0806c9d71d76942c08b83f8c83b556700c247ad79e58930aa684cade3dbd44ed1c0661702440765e2277e3b19ba1713e9307dcf7be70005c3c9f53b8025f976487f403c1b31072f0ea59075cbb9815ac1800f00575a175081a4931f8b4018f4b19435c09aa180f9861e41fceb0836b0720df3353d3d9fc03cf17a4483fe50e8cfc2d7175fde6c19fec8c854572fbb2d767f1fa4dbb2156341581b83de63fa0608c936ea790554432465724e6c4bd4a41c4c1c6e2b3f466b32f12793781fc36b7d391de27a33d105d44c561bbce9a6d7150a6ecd84266550acd17ced23756b2999c16f42564c546e085a6b52cadadd6f2cf3f4dde5e60fd04f539e8aaf56a2323a5930835df17cf3cb47ba59eb29c14ee00ff051d723917c4b535a49481cf96e26b20264c570ad9e2bba844be79162166f6d69232871107818550c4a9fca4982e3428ceb812bc65455b5d35f857b5fc7184b5b315c62fb98f6667fdcdf116dc564c11534319c2c7a8c535fb048fa43e141a1834ae9e80d3387be210b5cb66258be0323b212334f2abc5c640458a539021b7e269859b662281502b9e0cdc5ba01303162a231f76603ce6c50163ac4d72716115326c6522c6365b88b8808dcbac3d01d1df6a951bc50129b6b6ef54841fd963955a548ec21b6aa3d63a140b4009fdb6eb8468556d7984188b4c917825080c805743a3abbc19b0becee7b62dcc2ab34e20a7902e5cb17afdb9bf7b403b9fea3707b51d176f0e90baa33a1155d5c70c200976ca8c0f9b04f4deec7c1a0cb5912c2764b59668ba02e6d391996e4d1d786c1a01b5a85e5f82426ad37303ef227b22d21e8122e40934a2d6094baa31caa25078be210861172cec963e1611651549b28f9f86cfd6a7be42056677df66d530c9821e1ac3aad3e21a0d09818d36149bb5385c48452d1fd724b8c4b728f2135fd435ad25c3b86b3ff2e14fb4f343cd901fdf78b6cdb50bea2a71d879161d3386bd254731aeb07ca4e721503f967a5f87a40e04f763d8a3088f1d65256f3446e2cd9d3d14375f1922453967d3ca099c8e465847d111daac2798f78f933640e685d07c57c80e3a60971f40c0b4c0f0d9c4fc3860a4e741ea6dbc74fc6d475bff63f018f4a451eec7734e5d0965915f358fc3e344ad128436e0be139ecd5f50ad190f0f0893282dc4df02d83e9c12797608d4c4c191f70c6450e84463abd7fb12841285e764ba82f7c5c939a4a4469ed94131abb46b448bcad27b6b1bf21b281ac3ba804c2942847ec3db446851594a5bd2eda6116a6f42bb4cad442a8c7913b8be1a6c3fd809b3470fc3924ffa6780b39258f4dd90816422ac23e77424ceb30b86e0125ca0821945595fec15d0cec26f01645ce30297a6359800ef9e24cc147fddab49ceeafc0f312dc3d488dd9bfe4da469cffea88ac7fd331effc5cbd9141f98bb342ba863c027d79075f98cbc745e9589c1e107bdb2bc23eba62bc96e763702ab3aa08939523543436433faf75ea61714330bf5c124838c2450975a2a28aeeadd3e2f92bd97d260413dd2aed171ed6acc9639cd2df5ea1468187fba0ed64ec7047bc38ee0e565f4978ccab1335712fb647f0caf1c02657a1969916eb52a35c847975d0eb0f957b61e42813d7b7f1dd06113620580d30b167760a8b79244f12c8df6f6d2f546f6113c39dc6c2ff3e5e1537c36e49c05a9af70640122abec97849334f660bef62bde2267fb31c7ab31c462f190a4f5efc2a2b17a85fd6ee90eec4fed57b6a26539efe7d40f1a3a75960748fa3ac4c305ac6118773a9fc7f7224d124a305eb386ac7c338e56c7614b9c13866931fcb3b58ee256d5aa76b54050576fdb4b4296cb4bb076992b6310035ce4216e2d27e14523c34cb5fa6e680901bb3d227a78fcfbe5f037a529220aedd2e03ef4567096ed382928f73eb2931faed5e43d417eccaca2d4a1be80d46f2ca12892f47f68473fe2156efe03a92d7f69d685bd63e771876230a318ec9068ec0559cb4b446564647a1745f9b5ca60ae60ef2a3a9b4780a2508a5b2e150d477ec3afedd1e5599745598f1bc2cafcecc2caa66fa9415d40b41bc1a14beb3542e52dbfe56dda22632b838c6e47aebdc3442a7522d25d72c85f35d5bf10d6a004cdb83886e942b4b3d4e72972a33bcf59d2ac76c820c3ea6b1e2510b5ba3f9ad336a9857a89ad6c6549ef15787b3f209bd227f78175c30f0979d7b5347a6d585a078774cde506fe800276fe825b557df0c6dab14748f197d001f38438062bdb4766b81ab92976fb93aeb5cd530e7ad8fbf9e07d6f5ed0a3040524d294cf4875eb9db4a9b5f7018b726045b096257c4e8d814ce72598741a16a8530f788cab07bd17813f5407059a16be415b5c963b942e81a93d9802d2d3773c15e86498c30aa10d434a7eb5e66914ac3ad54f4e553748ff9587fc4e8808375cfbbdc7e06a731cc8e42f85cf45fc35a547e2d5f233dc50a7a2fb8117e2f00d24e48c0b8b1c10e24bff3b2b7d646f7297a177faf1d9ceb3e33c015793f9701893f315c65cfcddafbce2555f5b80064678f2185a4dc964374383292076dcf1e21836adb1f6254d271202ddcb991523dc746d4854b64a52e31c6d2a923c287da61e42d9e80af9450badfd04ecc22a386a982a5e0943ea67719d70286a53c2621bb65b0dc201632e07756ab0c4620a96f78ddc781a3b997f71cdebb171e33d9d5e84f793bf8ccd2e88632e6c5b2d7b4b612a47f65e7d6dedf5c7e6199f69d52a3fda00e96223a7d839a51f810535d12b13c4eb46c9e1d6f2b2c0e21dd766a8f40c268538dfc563a7f7d8fbf1f8aa90e4c422b82ae440aa5bd6c3b5f4d5e769ae148831436ae3e02c2414f88a3541a4220790191d9cbe11314abd858c7a040516da6d593a743f896c82473e21d5d9dcada54570934ca33727232a07b6f924d3b404404ddcbf51cb7869d4d15e33119d281487a5db97a0ee4ffe5509dac33fc531998a36aef72df2733e5a236f1d4b975092593dedfdda2f5d698a6de862ed38ed97001599eebd30d8490b6422834edc1bb8151dd0a8a9a721de7e2674bc7282bc07cf22524c2c8765028eb9e58c983013734a492a78f89424ae08dc7aff683f16e568d7da9559883aa8374d660f71206e8b420d2544e1c4c02aff95855b3df3fc0ab108e2067647a09b9ee62b9fbe4aa1d2836e16b6fc947547f9b12a1674105271376e86f6d1aff44d91a98cf43fca55839b9489930ea844b25b7e2d64c6fee100d0906d710015b67e68731d7f47efbec6cb23df8130854478090a858fdcfc173ea8a2937241e14a5dea842c16835b190f5e63c584dd423aef708d72fbc47835f03738bc91c337ad827a09f7d8e216ebd5ddf1e06a5f2c363c5171f46445c0a16e5d8a62179fbe0a317c7542b8c8967358b5e72dce71b57485b4b2120a78f40e0a3cb8ca11c93912b193872d615bf4685c0280a993030d12024404d770b4e5e60a6f4cf278cd8136a3b932bdcc17d351246d067843c5e846c483dc928fb4b14f0703d7ae13c7be453faac5ae58e91be6b956b41c15c78ad47484360f5fc30b621bc14bddfdb8658fa67d27dfee5fd453526a2b9438dfa7ce026f0a9ddabbfd621583a505614596a3b5536eb9de2472b4b555943c42a52c2f658ea0a96a926f7c5c071c065fdf3108c5b5decc20f9fbfecdc7d421aabc18d1236a594327257ccb81ee419babad0b9bb1ab25ce504b945e7d3895c32cc09c7af66f413b7d75de4895a0648d5e3c3c8287dc2c08f6fe502cff7c53de40e367a2389c83778bc3b114f9687deba4b994429eaf2a0b49e18ecfd99811aeb1003fb163773d7771150d1fa51be2f276abc49046dc9ee0c1b203b2c2fd9d86801586b5fb741526f0fdede126741003eda0fe4a9fcecea2582cf55fba2858117aaec890cd473da48959e59603f9e65f2aefc2aea2df07285e25044af291b8afd48d01f2c3e707690b0ae635d564c347d92ec50d002c2a4b10d303ec6a4379fb9dfb98a123cac66842f27da0ef4d6064fe18aeca8a4dbd57bdc0f4d7bcd47fb4ad1fcfe2c74990686565205ee60aa59084cf0c3695c0524966e0a82c90ed56d882a7098311b38bec4434adcd82d16f1f65b0d271bba0f4eb91b18817c17840b07c5335778282cb673abb00daa016190bd260e7ef8d0a98b547753e9b02010ce7f6c1ad70115af4ea1822042816cab31ca862537f235540a2fcd434417fcdba7b61b53a8802c872c99f4bf48d186ac299e8f02276edfc4c89ce42c4a8388e9ce626239730b69532259072011359e7174ef5553d8b05d52de813a1b3a1771f1cae1bf7f35f1a9448b0870b994b5a9356b37a3603a70373a4fc668c84777c91cf9e9fd6be3088d905081a419921d1b72e3449b1d58fbf0a19cccb32344e9b56d7537ee8ab4607b4dd3321ddb98914b0187149929fbf43730e946a9b1619a29f7c583667330543addfa0d63b1885473e96b19a6a2eba5189b4e3e1b3aa427e25c916372574c054fc00737c4063738001726b6f00d786fc3d7692dfc0cf4d6ca31e0ede17720ac3ad964304fd277745ea8112824edffe8f4c942a2c7cc5495fbe06ac384ed62468542d0b5840cdaa7119c0ae599cfcf05d67b11c1f8182877c854b60ddb81863e542d64fa1fb90f023e2a3735d46f1720019f749e840a30c0d1a2c65273238d4c5ef4e44027882411bd8f2bbcdfa05df0b7be69077f30ef78ae18b33b560e8eac744cea2d1ab3e7d525c1cbf122098afff274909dfac936fdc3fe1570ac7fc7b4b0430d4fd5910885ddaa7e0ffd05aad5370db9090654964fc41c2639c90e779487e23d8974df244f122adcfb3941746fe352e3b332ae5e610ef34a543df8e61721c178a28cc71522d2dceee454cc819c74a0419776fb9c1fc84fe6349759802ce833f8b2983fbdd5cc3319d7f88e7a98644b00cb202db6fae4d081197f3d1d4e1afdc6c6ab6e3b26445c6214c003447ad12903779a1e668166fbadbb23e9f6b74749626d48b8ed6ddc957ae33816d9d173b6a381d1704ad656dfdc773c3c3ffea54b51a911c62a8e232eedb2910f1f111b5c400d08e70d87b36091decd3de88063616364f54495be337e19a08838f7d10cd3d2d69058dbeefcae77c6939a3d1dc252075b0e76a2379238e3f023b8b78ebf0e4a228ce85f1c725fc71cd46669c0338326570f0c674370f7d80ba87606168957137ca03edd1e7e7dec6edf0cc6c08bf36b9b884db38fcb14a764c143e81ebc6bbbca2c5e3c036cb639d9412c70d5a1baa9394c536068e27d1bad345e958dc74fa07a2079aa80b04c7465303e940c6f76adaae47f382a1410ef00f59e3f39e05a52c9d4b978c7010816ab268cd1b676c593084381c24c7afcef83b977c7ff99d24917545a90b413eb05e3c51cd36ecea39cde8900e4cf8481adb903e1e3c135cc09b496bdcacddbd633c94aed2d0bcf452616a2fd30e859de105bf627d9eb1c28c4c9e29fe2b701eefca7539e462371c9c5373634ebf6ae3737095b30365010df0a28659dd2b2f07d76d693b46dce8333b3869fd22e9597e2ba24208fb058177d1b6eb7984c71ea37f68553bdcb5f3f706702e64c804edf165a3126c57349896e8e08a39a9d305efd80175d7873c8f0f63bbe8b29b529f25ec20a1841278c5e51bf5b350532b67eb85b02e7045f901f2f9790728412ca0585cc429f8a701409516aa20033d7fca543e87c4f48989c6f9217405848686dc30b3b39389155a02862339747e902fac73e98ef1cce5dff9fe9617b8555e80d17ec8f311e4e95629dca0adf970f4fed6bf44df865277dc33f286a1f39b73f4b201f3653a8be38a364b6da633579186c27693bfd1ef0858627d701dbdba831ec45c5a012d326a397d78509b47ee3f2becd24a833f2424a66fecaeeb7f3c26060cc55234480d6ec6fc7a37b28e36df635943ce7d4895f383197ce4fdca545c590f70b5f368518728a8561887d4d5c2c03bbec8b0fb74f8719270892162b98033d3dc82ce0818115c49b96f036d8b6db966476725f6f9373f905bfa3470dc5c5a84f45208d000a2f329d0cb47ad10a24f946cfc5d64ed59c25f4289debc7c838c7c4768828c429c9900326d71d51ef6abe12ff33977ddf9d5aeb711e6941b730a0c6ead89ea07b2b1d0c03361b0d54538dfc62210fd76f46cf6e1d56b33b4e6b8e716afe547f9ba376ba562d279aa45912d16b692ec12e14424dd30cc10c6e0d8a5ffaed33e8e0db5890e7c41f8ba8ec63a0860b4fb66ce438c4a81646839c6c95810729637ebf944682026b13595e4a63f446c53d06a191f56e9c9446f7903f83299c435a27ca3656e35c36b0bef189d89a2b94ca719b67b1680f85a4759cfff5b420bfa880f5e3ae0c9910d6649a4f2b7390eb7202232848a06f0080dfd1fee1458cbb8f4a6e7af76059bf451204294773cfeebd94e12cfe7edb368ca1cd940fd5bbfdae6abdd7aa06f9efa966c8a388101c308b382d683ee4bad6fd080c53571310c51542729026ef50881446ae70e8e429daa2f9eca65a48ef719fc2e5d2f976dfb60b8b06ba954efc792dd7da9c4ebdd414bdbde88acfb373f8d6f79349e9ac1376e1a832bcefd5a13ed34439be0928497241333edc03c22c213c50845fa97b24930f9c15b272af193dd03fd0c4b166f6dd4347985ca35dbd0f096021e344c49abe1d95e2a5d81cc47747f1a713ee129d0798fc76a2443f7c0f60472cb0d77a8323997e8c0b5a3c26bb201028072fc74a604c4fa21d04a39bf12b39101a2f98a41d44eabc15705caad295f71d032be99166821eca6e514413542f642281d98f319a94978412039faad60e0d0ecaa929dc48f6ad1827243bccbea94e20246564ce607c06311047273a64a4eb08d0228314835f9063abe1b8b3ab0991709af0019969eed6f35028aced99830fa4a185c126f1f9500321f807ddd678d8cc398abbd1f6d52edd98a20c741da8fb0c0bfea16fba5588347294c7e07f31c1d9e34d2cd37380a40ea209704e04ede00ace30d420cd51dcb49d27983f8891a8ff5d5c2671db73c3e43268c98f37c7bfa4fef129955eb2f2e1bf5ac88322c20087798e2db264dccd05e4c24d961192c47eb520ef72249251f34974a4632c1dca639b1244191ffaa31dacba381cb15e63bad2f945b9a8c2dc5058a72dc5e5c0432646e72409bcea2307faa7939a79e560b6d13f1a1c822cf14e4c07b24eacd502cd8624860f1e1d9ac7388599f5b04361b365ba7a82b80d80339fd15c63e4ce4e33db3bdf0813cea31a949c3f48c37ec00ad41e57b0701d9663390f99c3f68f8c49055e17c8b96efe87c08295f28ce2cbf365c5d08342535bc2c0f30521bc9e2abbeb95db70892f77c6460c76411f441ea0e085baed21c576443bce4c221851b25c4c299a30ef820e6cba7c699023e6eb2433247bcf5cab8835cc8a984c26c5d837c4397f721c1bf2ec32def1e468a41b0a530256d2d96203b297048abb86022cf0d20abb2cd20a46d2840b500763449ee6e6f09248d17dbd27fdf63707b614bbf2889115575352239001437a9625fb5a802907cee48b93c8ea7c201a91078c9a443e27876afbc28e37218a1f2ae16904c435a060d14c9bd7985bee264f7f9307b78a63160d82f23f096e3db19603eb00264d201ab864448311e2bb086e968f3f42012eb510ec1cc207b96d00fe9208cb7a7e93d8094a435ea61137ddb7f17d082c4cf58d612f101bf87a174c7ebbce81ffadc50a6436c04640522ce940e825010390c890870245a890d0d8438dd8c2f986ecbaab4c7ea9fba253e8ef254a87f02c831a1cd02788d0c161883fd09ff8aba76b445af226bd871c16802c012d437807ca85d9bfb1e12f9e5e00349bd92f86e408144fca5b1c9e7a04173d0c05e1a2871b5ee0ea24df41264f2c22091ec5e7024929ca99dfad85b68258bd21ec9eafbda2dc3b58b48d0dceaf0c7b154abe86b6735559eee85eec160922e688fdd0a3074cde0d6937811e506474cef3437381c08dc102b8563d4c71a8b62d0343a7fb1cb1b6c0a9fbc2341f7f2aa4138c4ce698699f693d8ee423307a787140aae8c1a4dd4958490235aa4a9577ac1433924a28caf334050bdc5c1c2184b064000177462a2a96667539ee065649c4ba34ae4f117cc1010be0af556edcfdb101ae409880a70ec8f51bab76e5881a91a56da8d9de5395859dc246347f74719292bad8a4fd8d82a4d58d4127252181f4784b1eb27e44c34ebd17a985e1d90d83dba74f79556944b6711903916beef2e3bbb97745382cf90a272649a6a4e7bf8f2dbca91d9b9283ff3eaf2021401daf77cacde2b9d9f507d19ba604bc2f080e9d568a7be6232495e138ff301b0d208192cee1e52e86ecc4f4c04d749fbec4e32018751196d2bac703845858184aae23d32e1e6a6c2324c73d1dda045c62d976739abdb0a63be1ef964bb5c193b40bad0cfcecff7a79ca036fad6efd83a5020a9ddf0d893a84abf91b4ebc147197f71263a272b80eb8217568b8908ae10d2a058e635bb913356213d62f858b14b0a9f6db8cf13a42b3e74aa0a83fbe71aa5e09dd44df7952df1a5f3e7cd94f64caf935889e48a08b24440a3ece447d511ca930eb69e0c2acc796d5c1840673a5b5af9a5c3a702926f8688c58c197d2be3ab68280830f194fd217e341bc60f2a51dfa16b8af5ccb4bd90a307c5f6ee80ee120beec094f7d36d51f9d3a59c8f7b6a39adc95cec44e3748d2400a24bb48af8acd92bcf833e07e08eb20a83a42c31aa33f1ea186abd476eba80cdff55549faea428162b462962b4ed73bb6cb973f06db040a0b4ed9b063f04cbd51cc7bb01945184a967f4a7c375649a9e9e17e031c30598d591bc0fa832513eefecd259e6abe780804685009bb8dd0b0019b2a39b6ba4e785faf35b7c49cb4710f0eec6724b2874de8b8c8ebf38c28f6398efc5f255cd9786d8c6c1eecb83ac402609493508ec866abcf15754dcc854b5cb998e49f1183b5d883c64c53e16c0b74278f67058ba3612abad87f2af5c7612fa02e120b1572d875e6cd9e1dc1147c8c4970c9021b29c1f0dc7f37ead8470e1f1c5e0bda04b2c473289422e745fc49137cf58104e6232ea1d8367c987748fbeb520cc67c5f2c5e73df79520b90f26aad75ed5c7d26393ecb6e73865c71f164f62c6de1526322c6b510406019159277378931a81cf2ef51b1d6ee587924822a4063b6fee58c1b041a56b41e03dab1c3a229fc8da110b10e25cfd9dc2fc86f151ce621b51cb235092e9490f1a2b0c6f5b5bd69bf96f409ace960fd3bd73d29c64698edb7f4d789c09bdf8eecfaf912bd9ce38727c66762861bd6e7a4a7f296f946deec23ccd812c749efad9e5fdc3046a7151a4ffd30e4d0bcd5632a878bd9a51fb4d6ff7db16cc7d93e67cbb3995f731e77003cc5a914774ebf856bdf51cf5bd74b1eda9b66b5a61dcf6a89231a50d943f9cb41fc5205f1ee1d0974f048dde3da37db5ddf61ef03d19b52b27350a5d117ed750bd726acbfaf4b9e5dd926d0416558f4d6cd28060edc4737a64fe00ed80eeee517d1cd6ba0bbc4783b7b70e8dd869b8eac4ebf54b7af7bd45e4141754278cd0440f9eecfdd92a72ef9a9579bb54b87534e95f1fc4237c64080d689deca017ba23b44a389c66feabf9d3efce9cdbe49491d8a69500f03c36c51bcc68a0f68c2979d498ed19a33e7061b9eb5205401f8569ac12754947abb3989dc45531c25f2fa68c53948798dc8ca677c7fc65ca88e836a535f680246c3bdcfe738b601e72d00ae7f96643f27a35dac0236e0d938d9df780923b5f68139022d24f9c4ec0d736b00cc8226a81827be9a1a93daf67caf7b456ee18baa29554a621c2076933384f20de233384539c39bbffb82210216d4db9633bf364072ebbc2405631e8e2730232176638a7897d4cba38388002ed2e704d8199a8b3a6f62af9d2cc3d60d037761fa97b288b3fb9622e092aa34855834477a7e7acad2a53f1561c65eec173c25aa980aa4774231ec96948875ef8d7c903cee5f0e3b7eb045f8641f3aa36c8ed48fb676458423eb8f5561231243f00c3465421e6743cea7c47f102e3e98a5cfff0b18f15a5fedca5a9bd16dba513635bac8ee2765e9ddac653c0cfd796ef6644ebb037264b5a1533bb67d5fa5a4f17b0715919ec8dfbe4f9eb5bb89ea4c6b0a92599b778dfccc1b8dc318dd59eadcf405a5b7fc29ed6a403f0205438649254fd3ffee6e4a2b1ab9a4af45d18e3a79ed91c90e50de4c5017cc7be5500744979f86a7e7a968530cf1e6c46877420ad12c1ef48f70e842e376ec920e0320c952bec830ddd15349ac47e2ec1e344204880eee6d9265f0e7bda981430baae7e3cc622f3337581fb04f309768b7596c4799316812629324d1191bbc9d0635494caaba4a7a9994e429d6c4f952e0b2b1b583a66d1066b4b24bbb8a646a04506f4d1ed778c3b288c48d41c2efaa71b2b7d30b44c53c10ba59053b669617d8c7c0fc4292550dc8bae2f5c608356d85cd85b8c6e00533c9ca476d29d4362d80ab9e48cd5bdc3edd69ea38330ca75eeb54cd0622c469a0a671be79bac63f5bd939bfb1c2e8a9e415ac808b41a7e67510b780e4ee79cbd4f3f0b97438a5971a7f8d2eb6d992a1378360b56e0f7330fb27492019e632292edfcf048e6ef6d97c4df7745933b126553e6ec7f49399730b532ce72b8f9666ca5f41cc41e67dc66e2d40bb22fec003e01f03ad9f3944fe0cc43877726b2fb5c049c946d3f69967e05361fc7db6409185eb1e1ac69235e08a5d332a44ccde4dcf13660fb6e53b133b2ffe09055e4bb90336f106827212a5a8b20e9d5bd89c9b5ea96c6c722adc19bfa4748c5c739ab05a2f0d75583b919dad7ab333b44450e73b1b0b0760ab35b44000c2d5ae9d9ff6ef8a793c339f0ef5e545039f6c0790f4d2fd92bf7252580f0b641b779d9c73dc5a61d541bd780d0e4ed9f1a386dba1af486bc6352c8c9e2eedec88446e9a6e278cb8ad1f4a3e50d9cd34f3d37814e62b5242b2594906dc67767d95f3a8d4eec8d746586865e5dc94a00c531136ac152be7a9ff5e15ed21e94f2144eedd7c00853138923196cc7e1f35544b670baa5cb1ef261772fa90fadb60a6b714f3a4ee3f9565b152d4324e8150c05c90a93520d9b8fd9d22c02fde84836e6ce5bbc381952dfe12e24c7e0e108da4ba6889ae9758f226730277e81cf6a1525ae4718208927bd07c6b717e663255052cbea51cc082c740e643a3bd7b9bbb8e838152e87c2adb78cb02e5c2b47b84e32c3a7f7a0705d531eb11d307be7b46cbcbc17f7045ccabe2ee9f6c6469d85380b8ebe118209b3dad7d0a4489dc4b4691629acea84f7f4614f44e52b7b425bc338b032473a965b6c1afd6747d5bb296954663194924465729abb1846e053cf19bc1f58edad1d387a0a6b440582cbf36813e8f02e81d570dd52c98206afd9406b593096db972b106b323658421b60e6ae2f8120dd87ee3796c130d3435d48ea2681bec055901deda1b61092b152fed315608548f6a756557cff0682b57f4dfe7cadc1d3318ac2eb03d83062419f62cc8eeeff2ecff1b7669a5fc32e5a003ace9068aced3ba2cacc8163ad264b3489cdacd06980437022cdbe828bc77eaaf3c5251e27e2e4ddf387a68a39c5df19da97d2bbbb2a94922fa86fb202d633744b10e06f49e5d9907a5a40f3af60b369f0ba40c748f134ab2f3094309a2be74f0dd9e1072409e345a0f9393d598d765b883eaa5b86c9258e1713c176e5117748cc8ef2250ca75a17c7127023ab584a59500fd02e7efa7352fd8ecb00f827a086f2d2bd1d48713311ba7b722a749006a311f2fb709d68d01d46c47735fac2b974321ffe6e78c4db4d2c30d121911cc8f4eab01d193640e21b5a270e33c100db5290d45558c46ca71ca0b5dc3d106469e1bdcc34cebe104291ef0484eec8e7f1bd1a5131563d09c603926994f7f05fb3a8d3e78f3344f1bb2c89f8cd2a06f8ac5a21d25bd5b44047f87228fed2689f84932342e1201c11eee5e3eec15c532d412af663eacb0cf8760c250cdacb87b919816b3db17be1c8ebe6c6023da39316252d650a96d75cf50c6ed03d3759aff4881938d6ce2b4965f54232905faa144a2111f5106c72b016d6fb3a1816c13079dd3d7c3226c304fd95fee3edbf6e5da6bdbe1cc308bddb89e984a91935481c638bef79e75d06f3864645c116edaac7157ba7979001db9e6402e3cafdac41378e55190e61071426e8d9514ec07395befe42807638867bd775b14d966120f9014729138be7892bcca5265a47db17634599858dfe6f6ad952986afc36af8b7fd6fd57f392f7d77bcb2329420d1e9ab86b31850a6a702a7ac2667162b59643737b5b0600c05e29003c9c52ab9a6bee06bb239cdeae15a199554816969c0227675df7836ad41e24025dba2f43ddd66360d228e66fd9bfd4a5a7450cad75eb8e6c0378b2d405ae38d8834ef10d75bf236d41920a627f1722ecf56a1cd88edbafbb1718977b97d14e427f6d91fb1dbbfdbf5aa4d1c946dad8b0a9e31a5a2f5bb4fe06575981c304d711eabb73515d76f498b8c5d717a0bbca9b5c82b2d7e155dc57d094b52b58687c6949d19d15bee0a866bf46f3f57b0b5d184f415969a6e647f58742f42af957957e02e2555c726fb9b0a43fb2ec99e5b92556a8f92aa1cb1af40cf5d0ed3d1371b88ea6649350fa565d1538af6b3acfb874817858d3b1458cc08ba52bac71ffbe79a47d612b48b589ed3ba5ba9695e4d6ba0c9dc5ea67fc56ffa746ebabd4273683c721cbf8d6fb5fa0f871656c325a904b32f529cd1658506937f03a91ee48d67e8a22ddd606ef1846666f8f6d1cc919f48fb72f8f9e54083fda050dde5d972c02632bcf3de8d279c36c884bb41c5c4de8cdeb3245ff82f37bc46bb58247b97c8af37b26a5e6afae2195d2f475c19a4ebdac58f651a7ce2adc78b0061858cf96e0998759b201c3c07cf8ee8c2e568920523181cc9ecf80e07d5db4b8ddf10f81b1fb817ba7071f50d78f2d85467cb71bd450fdbb0d68199e9e3ee95399003d3e45ff83c48bca28310acf9e114efd77e91b66131cee0a3a8614f15c17c7fd4d852b7e7b01a593c8b369e157109bff84b3de7a80cb7dba3a4b1b286bd298f360252c2d7c074c6df2301a2dcf09ffd5a9145b8608a7cffd3b9174e93041381a9ae73e97102c7f22cb7017c1dba0ef95ec06183456cfda945d228a3fa1db9e6531b83ba0df568479d216a83a57f3aa441608dc36048d06a2b508a19715d494302416d4dac163006da677cbe5ff7d1c9167b0a006a724758aba74140882dad250a8af84d37f590efaa2d4c3946fcd59967498b14fdca2914e4d7f635ae49a109071a5605502df77f22dd808216c80cbca0a2b55dd1b8a7398095773252e5199791ce1c1a71ddd4a93a6762d169352cd784cb599de64de9a2408594705c0c49e4efc456e6c85de1b16112aabe5eb548cc8ad5b6b019256002cee048e22e06573f843683e2b3ed1e72d6a2317d1ddf755e27374a60f5a11048c02dfe7a8d04bbb63e531649bf4f1448683d8e7e752e6c1998a29354605bfbea2d32e9aa19c287787e2c0ebb1c05c342910e71a308fd5c4b7135b1b28dba1a920cad92f3c9a2d3d83a9c87072a0ff3c404f788fe954593488b23d9194870e4335b180f26f3a7c2dc68bdc66c30097603548cb0d0c7604fd1230df59c1240ec871a925beb6206ac0d430e6fb1fc36e79e660833de0f81d649b05b08fb9087552d0fa431167f6a2dabf46ce95d04fb907b681a630404c9121062687680b358567cb7fa70b43e609c0fa4a60f1a3c8226a74de8e226b8a711576a843a4ea01297138a8a831beb000bb7f841b47fb4a161f3732c738e1143fe696dfe3a860bfb61bbad45f394791abd3606e525814f6033fa8311d6cf21a596344eae2c4381403ff689efa9dedb0ec3171b1be510e47c2559e99c503a225b8b97b22853a339ef24968ae39c8da7155ece57e6c1cc641794f271b212a047c5b9b411d4aeac72b220c02b5cbfd6df8a9c46a35dbfdd9a34873ef636b528acdd4e67e2117dd7f1c21ad1caad2c505648af941389e1d114db7524752aeb2ec57b6af01ee53f44b314cf2e1798d9e6325dc070d7e86cd2b4c21099d65574b4b4d1e1b6859589f29b9f56380729a78ebaffc82ad67d2e7091d473d075eeaf9ae409023afb945e8c8d0e562fd515bf011eea9908c83c9477da55004dba060bcf124ef3a685f622abf8d7a6ca2ac24be376052289a1600e8ef25ebd96ac22585f8a27c3ffb704cdf01c844efef4ce2ab42af43445415ee80390b154c09e57556a58cfdab01e6f147aec73c77011088690a192b61159dca5884846398adc5338e5e9b2f2ef96a438efe054ef2f88a47268ac1963ae25c619fabe301b7e20570b6fab41d5ced58f4c8e235b6b555daa2e2a6a4ce65656b51b1004f556ccc4a9922f6c11ae7feca27f7349d81d89dd002ba2b507ad9c9df59d05afcb5599b010b1cb6ac395dd643a40728e0963eb997b6482e84ef5ac24eb310150a04484569c48e3268a8ad761248b09d5f22b165919e73c4c17b45c094e7afd61c1d71c11976d01b1785872e4b346d0238f59d1a39bdffe239fc7994db6988e061fb922a0ff94b0432c2a4d49f035a594153b071a42249fd0af1188d2b5dd2f440a07db2ca33e02e318841de0bc830a4fe7fe429bddbce493d8127cb3bb9d6c10288af1bd927b2929af9004192dfe2f3ab458884f7f0850d7a575542a5205eefe2ddcad359e73ef404ed1e281c5bf01ac2dcc5cab634e2c2739f3e3e142528bf6217ebaf67cb775e0bee6cc0a8c764801ba9a5e9f7396d7d83cf9363b6ffcc27de1971eec68d08768307ec12811e52accbf85d4a046922de47af9e22f2c422527da2d591db9f0f54f11767c271dd9b116716d7af30e334c5b72f6b1e897e6a467bd1159528f81a6d54fc38e42a4c8ec1442eeccbad0128a27d39fde54d8a821c0fa9b5efdbe05a28a171d9ebba831c9ff5b06e5ec635f421a7c990e1d2d615c5e5a5cc3bd3843b7fa678dc4bde78a034ba95ecef0a5283c9331f20a1c961e445945c69c1af42b67225a1dc2c3fddf388f94c0808daeca8b9613989a0ba4b0897c8fd5f03e3729dc9a6082e86804f2855a12174c4ab2341731623673da847daa015ce244b130aa57de94dd3c5c0e0700b3348fd0fb9fcf96776a1be756bca181f331305ce848f33ee53738b7e14c1a96c06c372cf05f53d24579049b6c9404cfa095dfb06abe6df9cbb5574d987a40216211a2bc6c889aae2c3a54d8db89dc0465e8a4fe008567b9484288c8b5cc885d6b334b079c70e4baec3da98668e289b6f6edd27c015a2155a258f651e922e5d3d1455126f98600cb2b4b0d252b5fb549573290a7173845753d5f345fcbba4ad8317c4c082c5487c8c47adf238b355877a97c4271247879991e2c301b69285dcc06c24c552780673065d2dbd90a72a48d669020091a81863d5129cbd687f8807a5e88fcffa10ecc235685d010e092286a5a6564f03b9543a27d56b36740d6e120862bedac0c1b01702a0b200bfb663b29c219f161c088fa879cbf8fb7fafae1787e9a06a8cc4b7ce09122017a4766fa5da98a183d82a5870ec588b244d46ae2a180140a83bfe3f4cc51b33f749f07ecdc07674586a4fbf15705d361126f63cc0fd8dece5e4b600e031f8afcb71f2f2eb9f8c47052e0224f93c8164074aacbbbca7cb642e57a3baa80019b4de2f1350b3516db4419ab9ba811a8f649272bc4ec80d689a7412230819154f647864aacb0001ce138eb7cee9e17455f182007cbf2d94d812c9d1b134302ca2e443e3db2c087c8863a2f0b5c190a34714ec641d4ed3a2d3daa0ae689a376984fb8ce8a45d71e12361ed271ae2345fd762cf299c94edce388881008249856ae3700cefd884da2759384a13f6c15e97f9e55761779b7f9baab59bbff7114e0d88af4d66d1a29cffff149860340585e5c36443ec48e85fda667a85cb68e468e1aa1349e04a03c879cae965c8d8180044bc58d0968b2686b77d0a3fdd1cfeab6b58510d7399851c66401b5a16490224786dc38d01949984735c6ccc39910198f4ca928bcd3cbded2a0646df4a6a827742c3ab184633c144c8f79788f45453eee34f16760485b98cf8f8dc66696f24247cd40b15a0e450705db0c79e6ee3318c25bacb36a1c0f15a69cbbcc0c6e75425557c8688182db3fbb43541cb21769b98705024d5389e8b200f4df297f9c0c271b83526c533bdc1d5cf1d3354faf60cbd78e683b8eadfdc71daaba9c789d7f9d6ff8dac6883ddd117fdbf0283c36778ecd69d74af7e203b03e83a9f1dece237c063e95a568c667441b4deff6ed1d5f3d22c223611d30b4c9ec7bdf0f01eb1116b315f7450409374aa21f512d9c9ad5204b1c000c1ab79bbbd0ba28a2d68effb0f50edcd296d214e7ed4984e6d92c61096f0a512c02788e2cbba145da689ad3f4a1811858c348753b98d4306ffcf319e7ffe5d713ee7934d70989e9dbef77d5bcf253aa5a28abf68e662bca685cb204fed290b86fd951d913dd9175fe0994a8aef3cc96895fb0c34a7df57970f69a13fa64438bc84000edfa89695a25ea4b12856b6b41c60c39917dc5bbcf27e86350ff6d94a0fbc6546ecea7900523f7551a1aa243df0a1156c29be61a2f353c7ebe55d83dfab991b51d11a423f472f1efe0548cc5179dbdd7c08f28747df388c866c07e7e3d6d78a15b6cc258492c027bad80c956b22161d87379c4af53dc5117aae3c19bc8d942ba550f05bce048d0e5b0eb5f543d5a6ce6aaa4a3700673eb4afeeb966c69a4a1a848930dcfb2a4053d2143a9c42289bd852131d927d7d6ab9dc7a400f3ee389d189a74fa8faa7c6365028cf2e8f6f744164a29e407fb8f477fe1678a640d098885835632394dd420d07570333b792688b9a3bb3a9d7054e1f963f5d4be5a8a8a57d1255d4558c304141ab0c3f4d9c6fbae1e03285c0d6ec13af0b59852327b8cf8727d8293ecc835170735d676d35624ed0afbaa69427a364891fa251ba9c790a8b7001f3285234b791878176ef427520e5a59020c0a94328e06d80a4f263b0f95bb5e00b7f4e5d1e4fc9f1f1bc70baa52eb4473f46bdafde6d635736c5adb45cb50838a346af335b83273657ed244d33561e458a2d5f35b5a1afadf307a367fdc10fa0a4ad132c87920fdea8beaa22c8815bf334c1c11d7f3d0353f980ecb0f2bcaf2c60d827881f024d0c99266c757b38cae97c73056cf207bdf1fe92e61bcb64565781b7c28303093824ba8f185425cd7800e43830facfda690bb924cb08a8e46fb16d0e3a448c9e7694f78f7c0394b5753614e8bc8241bae40cff4bf40ee9199b7a5c244ff6856ae84a8c6e0c3f9a423e98b4cebfb2a7c280c29abe8a560c92bc19d8e25ff0d98ca1b6c7d0bd7af52472c0af7365d52e9cee4db7b06351ccb8aaf13ca1c8ec98486d3bee7b025da6247b966c766265fad8a3907fa0166fc2101cf161df01963d4eb29c7b4acc5226836c7fb0a5cfce69b7d7d5c31594bc3407c063d5844c0d0b0bf7fd0652707ec1e94611da6c5189fa3013cf971be3788f703271869750ba9e0f93555f428432fc0c421ded1e42514786e3ab066f5866e21111b7e041bb4d63c64bc5e1f0df6c7522bdd83f5b0c130975ef00c5e0fa3ca0e2d685b4399991e9013aa0d331f67492b9db1faf4b236ad241075c3f06eacd3d93e12430199085ce4ee720b3f9ac0b8b91bf9acf920baaa31156c0e3245f78c763c541783acd4587676e92531fe19eba0582722d61cd000f8930e16c74106c91e37c3249c9ffcf3e9176cf36a7262e1cba2762df5c26669ccc868fafd886e9a6566c67541ae00268347723b95ec3660a5eb9062aebe59195c0020719ac75208db1d89948aaf3ad4a1a00ad782fc129f3f7b3691a2535769d0159e3994865a8cd6b743de740c00dfe8fdd8e0a41214116abc8d6b74029dee033f43279273dd8a637b69e19e25b570bcbf0660414ea568ca1d7ee8ca19c604d03b907d540940189544147e79de4c222dfe63345839fc9c2e18dac35aa0709d297af41fbda3fe5ddfe520872a885f944714cb807f3c4eabcba6a2fbcd2ba8cfaaeb60dbdb9008e676100613e8d802cdd67334bc0913298a7ee45c8a60124a35367529c275e8336a56fd5cdbeba676990cba23f06fca4484e28160ddb452cf8f0b5ef4c9adcfaffd04bd5e8659fdb12a6985b33bc64b591e5f0ddd2bcf834ade6d2df001043d1f4394d263cd8260cc7d365bf89f0b9091cc6461f74234eb39a5d96b454f2f7fc31f84f36111ad7972a75569010fff7a530ef6b4314e72d2378abf30ed8513768298bcc5ff2801854c4af1dc022a2bc42bd1178efb30c20a678304ab4d14e95046225ea51668ce7a08ea6679632f787eab366b4c3cb448fe954abc5ce4b4eaf5d28ed52d0031f838c1cc87d92fe6d1404012faa07b40a74994102a8185083dd757e1c566651c36f5f2f6f17e0c2046184e92e00e8ad4023519da4d5865065221b57cb6e56db8a793e427640d5054d2d5f60617564157b625143f20a6b930f759e31e8b9d1e2a8459690976329b8a3235aec23463d71d58b2b15afb2e0ea3d6774eb9f5a4237afa05531f3e5a3487718e59939de563ec40438cf8d3049cbfd919ef1fd369111cf6bb805c4b116929d74b904a8da77bea510a4ed698c398ddc6f09f10c79dc65959cf295a11895db8163e617ba30be87e37059c7eb2e7e39b3653210f58a9a6e35e9b04db5049fcc312c3bacc943bdb05787967d6dcceeedeb300e941f9bc78f6ded25de44eddef386227d47f8cd4b9ed9f7ee87cf9872f73dafd39735eca20deb6d000b0bfddb8f2ef376d4c954530cc0430db051b8df0076406a7a7deab093897aaa3ad1b66a93f43e2fab2a93a7da5d5d67005c8bc2389a1fe03a8a45a8a7bccf1d65c488a624844548a841b2d4e4f95e05d79aa792254b855637fcb826679907f4da2f0fe086e311ed46cce947af9838f46f7d7df0857744e2b153f03c39c28d829706d2cd33d1a250f8c0a7eadc70574e07d60ac4cf6547c5444d6639c9583a919907d40ff67ee1cd20d93a63505df05abe2a8e4fd44a43c088d404435101a3f07c06f6531d875c1f0e480c26d53b65ad29eadd3ec8bf528096d461b881233215c90097f125ed52bd593991323bf32e466f1a0894c5634369198c144c412302828241c0fb165ff217a0afa52dab27cedf2d4eeb6a22c6a68bd955d0d720e8e8ff651dccb29b02a4bf5f7380fa7f791294a3a6ce081156df6bfe67f94796970859beb6c7251e751c34d8a5a315cbbb0f08f8eb5c338f85aabc54144ea6628c9d9a9f0fce76e44f9b2a6225023dc980c804f33f34775a1fe666650ba98476c03f7dfd1a5c8d34334b9de4cdbcb25bf7af29ecb69928a3743add23af2ea8b5c76293218b9e0d350ffb21e6067f011434ad56795a427a85e800bce0ff2e61b5c1a5810c7c19ef2aa9b24242b5ff3b65cdc3a66e3d5754f1e200e6ed863d3ed2ef24a18f0e70810386f40809a90f29aec2558b64faeb5ce39bb3af97fabbb7b419e6b499e6725897ec37a8ae7793550bb0acc90af8d393298b9745aa9579d9a7a72a20cc846d4f1dd0a28970eb481a7ee25b7fd3524bb4781db970bba497635c4b3b4feb8877d24102df1904bcd4479079f0226e99d4ea06020aaf40311c908be57e09f3ced993e384d64336b3314f1ad408f616af6025951f0dceb266430dd656a69eb1b52e7fd23013db520328528dce191a9838e1121a5f8ecde70f1803f1205aaa2a8211d23094203d30b958c414ad1fd1d44ef6a338f9b71149ede8f2bd362adf573a4ad68c4c8a4424da16f9493369777c7943165fbb0fe79ffae94a824194c0427068f625891610308da11ee6882c0d95c47956d6cd10bcfdc94793eb6144259c4d5a9b98d6004915bda1635e0b816f180df9abd4b456cda3ea75e40493ea843ce3771073c3b332998d0b580267db5af7467dd4aa7d07116ec18a6d0eb4fb4e5373f366fb23844a0bf1a4ebce57a12710dfad56888680532a22e5d74ec1743cc210a83c32e533d1c1dcc0e403b7c64b372c6426c6651d72a1f8a6e8a4aaf68909b5fd62ab5940a170ff2ed0a2edb614677401620ccc51a7ad97418c5a3ea14233a722cecca4db392726949659b56272684a840b0e040c770349e92a4016a49bb2f4c9455616af0d28af79b84254ecebad1c4b134c755ea770d21c197365693104e0a316c7bba0fa19792707c231c0508919fcfdd8b8092205ab94ffbb54569c6f4f3cc394d32691ca4789ca70472c42838f8ec0776be38cfb645c72c4a8ba85cd617baa2f08ea2ca2258825857992e3344ebdfc58ee856b7b80d9a2069306acd8c8f2d778dcf151127e87aff04626e505ed4278f9e40f9f42734efe77399b58aa08ca014d1ac971b1be343f42aec40caaabc8cfc048c2a1e57668a7bd33f83ff35e9d371c57ffd22e147f3470311e56acd2830fe04dcd14923ff91ddcce73db2caef1fe7e26a91b02aa4d9ba57ae00f938c8a713052581a9482dd3a3f21773ac89461c403012b98c3bb4a5b120942148a75e4e390e5e264b19fdfb66ab5099def0f3a32393c6f7197a7ba66f877bffbab60af4e8b98932ad8657639cd20afe34f5f030140875e3a0dc921a5da8c566d50280103f0d0489f30dfdcd3d3a728ce1f2a27d304c87373b167f0c31dce2ae8d7c6e7ed173407c931dfa480e116766ac17de0b72c4057842d941548a2cf9fc1d6c40bb45c20e2e5a95e10a908b54ada8802463c3b09f72a2e28422df44737ba79c917ccbd4cfe9b02b181e876d6511f0bc0684fcecdbc799cb839e4f4efefe62a6017effc98bd4fd7f83209c5b10a9b60433a916748a2aca3f4b4cbd3f217a880c3234e4094acd2fa3172c238a2bf1ee9f5b039101972d3b4f942332a0dd9bbfdca141cabc1313c6ab0a5469c790b1ad288cf5e3c9ca08f4b2ac2113ac0801695c8eb2d29421bad69ce029b7ece09f1f2a78260e28f732e6c263a510df17161b53774d60b2ecac5893926ccc38cf616ab40eb79b5ebec0a1016cf7f99b141d213d9027c01f14d0764146319a2a937b81787931e63c76df75b7108c8230bd3fff707c17818f689b8c73af3936008ba90f4d9773bb24866363315fbdad70c1b9e8dcad927a284276a02f3e76b84800ec13abcb2b391d44700d2d8e9b89e91924de37566f2183f5f4ca8c342aa5d9d3b1c319016172213eee1aec71e179375cb41d22cf0c394f2afab665868c4067403b7b8b62736237fa21ba61e1f414e6e7f7e581718bb8fbebc4b569cda7b9886d1a4236b4561b81581bd91dafae3d032ba3f3d5f2e71edddaacf967541bbdaef259c0b9c375864842a46ce762adc625b8caafdabdc97f42b2ca1ef2fd93f659f31539dcee023e5fa94d7696508d9f6c6362fcf1073c1a6df0caaf18ac55f3d611eefaa49d0d75004b13b39e8b93107b2873654ad9d2a483996b5c0d7a5480c2473f162e0a693e7408bc902c74c5b8d2c6a9048bb2234859736bc5efea845bca6c71dd213bd29861a5600d7b23ab044f5facca954497024e2861dcdd59cd3786eb8a2be4f33afe1e191c5f6eca2566dd7967d7882507d9c298387069bacb4be9081f512c7181ca238c4c3dc6510576428b0d915367967bf312c9b5474fbb3b7a78fefc6c1a6f3f269ecb499ee91ce5e18647bc2d2a7c5a32da5c18d02a799f4785a710518eecac4c50e93793be03cda9c57c520e98f9ecb2409dbbbc6d75418f9b79c5e546ed489814c88db8e0d1b61f016d1951d7683eb775947dd917915349d5ddb689531e927dab2fd093c180e0a24310b36ee791adc0dae5ce1d6a619fbb1765f64fba00181a3c36d2a4a57e3242cb47a7b5129e99e923d83003372a01690ed84b8060a265e8b9153d64a80e9d1a859d7a52c591658a3c6fd0a50151ff15422da7a35e990a7d5bac17e763357777d4cb701c991658a6f9d95a7fd590c76a5da7c6a4f8120aace428a86e4b82c947ebc9dbc56749bf609f2ace28d1e7c9e023b86d251832bc4428ca37b23a69940dca9771ae8ffe6c5f429e51b2d6beb994028c08c70e384016a5e024014568466f529f1ced83b7579e94bbc5ae164580206b2b3ba5754eedc3cd3957ac33436ad24026aec80eceb2aaa4581f9125a51da0a3223feba860fb0a33ddaf031816368ecfcf8b1b4cb5307349b9912991e011ce78effa6c49a1ecbc435d1313e60f10fbb7568d75127e61ff2c3aa9b636b69dff824aa5800fd841606f04cb74b5d54044e8aae703bd38da77aaec380c254cf7faa8cfa27864a5b89f8b1dc241d950d8e80d6a104266057258368d1ea56ea129ca97c8b6132d319634551410133f9d41c29883062263beaca678503b5ca03a7af4e8cd44da1538202258d8a8c2375d87ef5c2058ea93ca568b7297e129d2018517744106257ffab70843dcfc9768336ea68b4e3aada7ee78ec4ab6e605cd6bc3274102745fbc92c445edb12fee694a3fd720852fe302a7bbf1b355cc74b0d9b41fda0b9eeb17edca1a33d436a0b7931cb643d5e143a7281647f2e16538c53f0dab3186dfd1da22adf744777c3dac47c6800738250d7f24f308dbedf43836ca976c75907ac83f4e5006684e36f57fc21a983ee6c664a7a32156b992386f6b218eda84ac999a80fd96c7899298e8a72e5402b2f42f57331e1b5e8d2e0d6c8a2ca77d290d68bddefe5f667822d7b0f7637451664009806200b61209ab7014122e8aea4ad1d65dbfaaebb6d63b75a576d5be572a65c6a40e92596a5fa51cbfe204781bd75a8d5efd884bd74ec3001a80701cc6348b862699025cf873ddb1223e4920725304260be5f0aaff4f11904819221574975d372acbae9b766c67bc9a386beb9929667ba0fee18e84dee82b9185bd198b7ccb16e0b946021874e21b98cd64926f46bf12e2f294318be3db141b8ec7a2a7350f84d4a23419cb65943a6c190e0795485137df0001c872f9cdf0d25d10ed9283aefb7f10b12f8cb8545372d02616ff2ce9d12106573bf9f0a798e995cd2d31025b087dc7b976962873c4380811b715dc7216cf0884d95fb02396df428ea3ec758f6ada42c0e08d89d1aade2c6e79078debdc033c2bc797a40e05320e35c0141f56a9785ca84dd66625a6d44b656d4ca2a58e55b2e3a7c72d4afeec2ea7434bf96a4a3b5355f08e5734568a33875e4965d8e5ca74cea300fec5ec1cc88e1fe7d501fb1cbb1a662f0b79882fc2ac7be831c8ccf6a9c491afcf0b604534afc8cfc451866daa9f24fba2c7963acddf465929addf22e99d23233488e500d037997d0e247c0e7a3e56d00f2751aa9e5b979ca9253a963ded2a8a303923fb5d1eb614e210c7085a889e27b1dacf91711ed1f8c9660ce277a3896ef0c8012296233bd38772863690464b83c72cf27b615aa50f7fb2690321f25719ad5e2512485c9fa3ed8c34f9b0f2675170f2b2e5921b8d0b0ed65d14541d579eb1aa5805baa0cd88c670251d9085065ab76568e334e2d705031c921b0b50b54dd7deb07aa702cef8432291b1e4fabf1e41ebbaec469ab83a96d3c3b8f119af4217f44212d36a0f5f7c4ad35cd793385361ca2bf133854cc5563a33f20dc611794eeaf6486ec01a19a96d227c53fcb73d3069dcadece4f32b89a13019ec7d4209c7412bd3c89c35db602c61d6f9bb2221bca658e273313a80f8392ef818a9969490fbdffe96a0474b633d14440a34df892c667d486c091d51884e30bb12aaeb8cf51690dc86db2db029db5c429d060be5766c2fd5f8e4b1bdc1df1a34187a7cf1881903b592429ab9d67922ecedf350d5aff0c048a1e88822529f56bb2680d82c942e81187cf83d2ac1d4170967ebb8ea334b283442d0422720b75eb01a263add628f4dbda01dab7e74c402e29b5966ee4dd92a16a8ab8c4dce73b65f2c0e8b5731db58aaa49d5c1d3c49690728817a466959867e3afb8a9fa93a26eb5f18eea164204bedc25238b72905e6dfab05d2d0993cf29cb8b0dd0fa39ca3d3e529bc6eeb373b05bc7d9d2503c8ed76c6ca85987ec238f6b72aff296064cc68e3bde7d23330cf5753f0438ec3db92de20896f0d623b145cf6a853ddae1ed1af94328dd3f107336053ff3b059c60c915c7d9d2f5179c48987b2f4bc504d432f9300c1c8f95e1e173bb6ed1149338aa7bf509d0ecf1312a519b8faf4e123938e910b101f15912dc0fb3ed555d6ace8218331d9a63f2e95b05d9ac8e8c88b674242d8a2651107c9d88ec1455c048f09a6fd8c61181ed099e9743839c100c1f6569b78a07e7720ae7f69b1f04e15c2ce3cde8ae7093f358c45f180828a68144d8d96e8685ec81c422daef3c2afaffd5bce37a9a6653bbb680cabaf5cd6c78dab2c77f250b78a84cd9e594399f6937b133d0f2106133c41aaf91ae3e67f0c1c81d9c7bb5be052dbcdc29dfc1abf4fa33e180503d5b771b7ad0640147b6d57c983aa90920ae53dced41c6e51a3016cd05d358948bb24e3842d587e9c48769dca923c46453ff47dfb88c84414186ed89f62e8dd14483319bc9c290ba07435a3bd66f566443acb8d0aad8cc6d5b641e3ad39bca498e27233d30269c3a0beca6d89b82ee3f4299498830466fa87256e79933ba24ba7ab1b5f92c9455d965f00904528b8129019eb2cbf8ea0716b228817a71687cfb3d4bae70a51c52b2c6623f4b292ba805b4be8ee9c5c567e5233e6e61d578a2ad148ede1a3ef6ac7eb7b4c8be4334b50839a95c47f52956b5cdb50e49c2ff92b52354bb9aad86eae0725f0eb54d74f803601d8e255c82932bb7fda7ffd7ff4ccc8d4964f2accc1136d16c65070548fcd33bbbcdb48b2eaade13bdefd6d0192045c59ff42b11b4467d7177b453c99e13bea138c6456e7e1d1b9b99a792a1446af200582e10b693f22bef4b519c8d0a4de65022eaf05126394e39c11688cc1257eeecd612e7417188bd6931c898170464f74f6fa863f34acedeaf0ccab97d46c78109bf4f7a316fad1bc55ecca36c6a35c2c607ee4682873b123fcd10fb2651190aba60d0461b863c4066227ea4c035b84c50ab6f89c292d86712243eb28dc5d45ee0cbf0d41803f28e874b28d037420cfd630e2a79d25c0b84cba15eeb2623caf8a4e02eead568448d1dfa32c8debc4e4b11b377341edc65f1a52a427f78b586c71df0ea6b4e46b8c7d987ddd57b2c1cb4146682886afe9c46b48f64b75cf5cd18ac195a39f8d20ad64f09f10cf631be9fb9d21b0210f48c5ece0396813fb74683635a13efe5dfdcd246b188ad1d0b62649a263506200984170f0a901b9600195de41cd64bba539e1792f4dfd44cce0a2d07d0ffd0e71948d25a2cab76999951e76bb0552b0c540c24731e3de2870eee4a2a7042cbb077f662f30c19663729665d4393a106647626f9e94c0974e90a4acea584fcdd09629d8734ddf68c81a9c48a5332c81655ffd6441403d4ea01240518438fd4981eeca72348dc7eca60cae1fbfec274997670aeebca0e272b1216cad61c7c338c67f072d41d757140a41a3ef9fed2ba63913b92379c8100f6aec3c1353d2601ab67ae2606bcf22d32f84198310770c7b5069668d403f787e0e9c003dd0dc4d74e7696d13086b532a043cfff4ae7c626acf88eadae45b7c962655faa457fd8445603000a7a2c99239e496ff03ff37425b60b5f9f36d1b2ef9dc9535071be8c678534d6c5955dd7f72d4ee6bc097d004309b1f4a2930b5586371ebe0f23d821507f23cfa11b89b7f895eb9f7f26f95beecbf8916500dbb456047ca3b04d401456d852f82b70f96888fbbef2eb282ab04a04642f812bff00d9682f4ef27942116315c71084d4b92ba711fcb118578618e020f7941a7f99d908f78dcb67c797e37c6b2676a76a436529ae329aee02b6a7e8aeb5b1ea5ab04b0590af3ee56edb3ec2d40efd1a20997a64a9776187638a41e2c14e2ce3c799a4e8a6108a5531fc514266367c057f8615a366601ce4f28899abe7948d96decd4e5668d4866b356920665ceb71f27eb9fd9c5e026d0756d3ca0f41cb1f47064bc9ce7d1fb553f288e71298b69043b3112d8ef444e08ce45948c18df5b4180609566d8ad42319270a030474892c340a2b346962b55f5aa3daab63728a2fda20e070c78254a60d76ff373421bb5948a1486b72c1c4d940884a0507c753576cec3971689c4f582770ff4d80a032010cae2320311cad1511df88994282a3d4f1952de001302124b541422935499ecff97a3fa7298f4095365c69fcd749822d08dda4b20c7117f89c331610a2bb5f351acaa9efe3a8b52538f7c74793bb116057c52e4bc95b96166d2e20158518c20f15641cf198a56959dd2713a218e0502a57c972836ef57ea0c5de50cee03ff510a8287132c6469b58f41be0f5017f7271f1f0d8e8873fe8a7a55c361d0028369327afafe4700fd07db6cd092aca44c60c7e50df1b98e60d350d0286cba811a0e08d6c28bbc0bd5d0a495209925e849f563c7fb438cce03606a05b5d5148ac9605e64ef80811e56f87b25640b631bd4fd1c314cc5fd652b84225f4a7ed2abeedd687b076a0e9302eee67e79f864679457935d6f190cf0b0f079803b5ac6e51290e8e3e00d041aba0584d429dc1084d53621293f009384d4acd71b0cf93e97442e8271402d83840c60fe977fd9059c5a4c6359b47466a9396e8feb1bbe5fa5a1b695cc31ad558831a57a979d49dc000703a4a8b1c5086fd364595587fe163da46223973b21937e79d36230fb059b399c0bd62304bafa115afb8d9c0857f8ef3d335d3aba7800802bbcbd8a3f7cf79ef881160d25f4c649710aa6ac3044979225b3a37e057e33f2061b0c693412bbe9708d2a6471f7eb858b1cdc98a54933298c8bb73d89f05280a29f356535d42876634a45731ba35911357f0f69651902a8c7624fd0e538893e80dffa15f6d761c1d427de2caf2079003d74a04693e2c63a2b06ee561453fd01eb17452985d49062151f893016dc374efbf5072d167476e5318fe20750eea48d542d1f18afdabd1a7219dfad1d55bb1961a171898032df46f4aab5d0db9f740d11eb67881b0c6ee7001a075c6d38f08150b7aebec80889a0ca73161258e3de7024c30b271983a9cc0a90b5e30077c3c1c270e7f50ab7cec38d47acd32f889cdb373591744c61346706aa858e176e2a25e60327c377e19c747a5fb19f769d8d0019865d1c3e244f6848f792ac9a930633fd403e543a032dd46e717e7ccee8316d7898adb5310c9b8f7e5572714771879af1d2dec9542a575eeb24659e7dbf9c5a4c78141a15e8a53a3b02f8f20bc00e3b88297bd63ef6ec964977148d56a2492b1157919ed3158ad32ccf196c8cf22dc1ead0b2883c07e594f9f4a479d0075018e1e91a421d4c402449d66a94a284533ddaa6089677e3a0cf74387893b566780a6a797c08d5bb85e33a763f04527656e12a41ff84370c3e291c6c09a3a29d1c32b6e764597ffa4344854b897633742bd51e5da77e8ae7a18df11f414a5de2e4fec4282a1c4785795a65731dc2590b808283e308026a7bddf05cd19c27a862beba419f8b4ae0da78c277568da949782bf1318bab4db706bd337f9db93045148d7ca98ce658c1ea67601f3691709fcaacfe15e67992e8ccfdd281893925002e1160f088fc8861120f360cc2ce1c274769e1f4c08c794c7a6b088db41c7eb76f0be9a88acae4e357d72a36415236ea288b94b1953c342339edbb2a0b78cede7454037a6b61e1bf46ed62be5725eda776ac8a9f8349d7a3560f8113081c70de399eec7aaf78e23e45d420aee5d19e7576890468d311729e66935c0d9c055e140a04930520322ea4070ade7d0bdf09640b82e978bcd45e211af712f17c346121566554b6a51189ba662177b27d29d1365151158484f4d55a3bcc2f706341341a672b10f715c261e554e74cf300198035543be72b142b75185f58daa6b1a0b4c8ce1e91465c3e4a3af1ec06fb2b8788816e29819623982e0936c1dafb552997b90a5bdb90c929822aede46fcf9334c809cc916aacaff608b5c4341ca0f7420824ff56404fea39559349751ab6fa0d175d3d75b941b1ec279050f0c2ad0812fa7ece3d3f99315fafb3bba64fef581ab02f847aa5f818f6ddfd581dcb9d46b222da96d499ed62dff3bd46a65bb0d10b208f0bea1130262945552d4bd3e4101d0525d9b8cccaf7b003bef162456023608dd332d269c09309190005247bd4f16ae860632a36b1010e1bd990a75505f26774a83d6e0445f995bdf8f744757cc137567be726c6f0332346705f55f41fb7d8093a021046d580449f015515be17923f0c8578a63d8abdad36918a8af0979c64bca2d8d16229ad20d077ec61318900a7a24dac1d4a165e58dd44f1947d444b01d0f8ddf29f1f17025aa28ee09c677d668d7b99a990b7e58219398520a6bf186bf942b041c9c571205f609fe10752e80bde5993b46086cc5dfb3c4a96dd645c51d19a40c06bd4546a6d4b14514c4b6042e12a188e522c17b41d90d5d26fb7911deb63dcfc3a70b2af584a74b2c7b300314e9e3096f117188f419139ac54911377e07918614e3f1ac7c3dd93ca24c5eb1c3dc0cb95f8ef5fd7b14bcea67300e3c4ccfa81ed5410bd2334d03c5a3874af805a35e5ec4df84e16337da1b1810d6860b4eed3fcee93a0c705e098f57fb0ad1df3d7c8a9dcaeeb52cd223b2c1d9c558fce01daba9067a905c5b082f1d18f9d2f9699d7b118fb904ba254345ccd393f4c6158de627272c8ec4021d10de4948555c6a203398d9310a586efbfb78ec0201721704144368b4ad5299507db75bebcf52b0e138f5409520e1ddbe42fba543f7e5684c43282f57df2d6e9242371e58c609001221d883690b28a52d48ee09a782ceda285546aa072505e1b273390878c0119b95b7738264f85cc066711234e638f39843594a4dea39ae4f134b5695ca3368100fe1c8b78bb7478ef98414c37971ef571f9ec89b0939e9e53d38065bf1c67cfe1c96f12f81611901f46ad839599ad54f23e3067a4894d580b785074b0aabd6e1e2b88039c079320c2bdbcc6cb8eb8a5576fe2353a69e329cb2ce04a32c3fb2ba04d6c2de341329e4756e8c531bec6a6fdef7a42df82c18878a767ff0491412aa56eaf15d3fb602b2df15ec8b84245781b9c70f444660865e18c5b4b38fd047dc76bb049774ed4913e1e56d2dce598248659b232616f44a9b5b34afc177fcf94aef602caf40637ed370de013a2b9aadfa334288feb7268e09e104a277db6ce984017102f5d9813f77e0ef0efdb1c37c56fa38db1cdb973d642e1d92b6cf0b58534b4763564ec46cc933dc16353a526b483741eef9c2beda456d1b6e9856b76007a859deed8697f38597281f4c7d25b38476eb0941d6a8c431aeccc1e6fe9cc2231cfcd15b5596abb833b9d78320b49dab563d8be39719c4fe6d5705b3eedf181a7894a5e450018b44569e0d677c6a1f045e737ee68228d3f148a938b468c7444d50fa3f173c5f451cf3e75d72c20d98141bc4c488d35227073d49429f8a3bb08d0e9c2a20e4ec5dd011a0044eb1ffde4db09c473f6c3671f910f5777a2a3b1c0723440aae533808c622d1182ca9ffc3e8fe9f9649eb0a9b2e4947d4b892a453bcdddd9c9a73db642ee62d4a139e7d70283b6e6a343b1bb31c7c0877fe53f63a26ae1e27a0b650c093362be0a2f9d224d3dd54b0bf8da84dd26e36f5ce200100e8d2b6443a5994b9c627196ca239a2eed0a7cfc74bf386748bf5ff675bfd0538c10bb6f21a2921acb8fc8fd777f95e003ff74521a199e8e69582172748044dd9179401707fc9a96bcf0dfe359d1b3fa24f7b13e1b0361028094c8cf93d854263a22cd22bcf673480aa64c83d1d5e112af05c5168491cec53767759398e4fe005967780107304cfd32267fdda87629da6babf2dc29e4c861c6b987f5bd6a66d18e9ac205b8007aede2f3faccb0e8864b0a586ef9dc21cbcb6b902c2a601448c1c26ae889a93daaf9a3f9ca2d4ab785e5c726217de90a280851a73e7124c456c4d8c4a299a7d3250b74374086665659d1fbbc17a33e8ff982d9f6f3aa412646c7b5a8802519ac7a6acc9d231a90f29bba71894b0e6622424485bcded4571383dc45728eca83e5426c7bcae31eadcd23e2e336080ba48ea683bc0717f711f5f1c6a27fb2cf48f6943ab405f2d5c460c3484d644b18f64763663276d326e0a106a1ab2dcbaf8c7049cba7d6978b39c19c560f403ef247282a687ed77f624b80e42f3c259a5a599970b8e4b3c57bec37d6fe9cc1c5815bf113a335a26acd84c71979484035fe5dcafa5a1599bb08ad5e56a36e4f59fc050c2da3c2ef30ab6cba4169eb27566a20c9a2cfb2049f70424fa2c2f27bddca8de5e79e9ab834ae52569c970ca0b5b88293748e535aaf899152f363f4f18a2bcecf75189cec9281f6d0a3c79b98b8349a665edb4e415c04c997ec09eff2f7ed9a67ad5495ee930851850e3a6c9dac9ab097442fba8070ca9eabc6e85dd89f00886120b3c20441eebf4f7d8208e88f395c9f4c8ca5dc1be4fbb779385287c927547a1d87a110db694a0f416fed6ff035edad763ba21edb502787dae01af17de584ea0f821af9c5bcca94dbb49aa7543c24d38789e13c18ec60a81afc68fc1656b5e43769a6f63fd3a848dc7b68aeae6d9af207597e1ee47a601e9577465afc5ba6634a740cda081aa1f4a506ac6965e0b2147339f1c9e04bcb9c4a666858eaaf9ed118fd673c3caf732d5e91e50bef52bb53d5b74d95666fc3670c7bbf5bb06b661224cf99f24fd0242c07a5bfa70a5b0c8762fe58e6b64ce8a66130b68da9e2db5c3ceac4dad813f23800b556ee6c83c4a3991f1893158cc0c03ff16f4295482c8687d7e6e3eb97dd6faf7083b8974ac777411f3a25e60e0e09f8bf31c3a4acc46e1f30f2f71f6161e974ff1966079920daab79a516a99bc32a7515d0941a1b938d776a8f751db425bec9d3b247ee18de19ce11c5627e84b9c8153edafa327e16253f33864ec1248b430d5cdec9fd8aca591e50bcc184717d56181c09e8b4af8886fde208104a4a880dfae6a518175ae2014a877c8975d86379fed54a8027b96b9c035082ec8cd6042bdfc4b37ff4fca6aba17240586494c1c4b2320db1429d5701132194ceec91fbf2139d59c90210baa7bf47b02571ab30489707f97e837f73aef613b3f234d9360c1232367cf2746fe2a05e3bf9bea26f5368d688b050bf03dc83bb50649f3e9637bea52ca03a1de182208b840c67c93858cfd188ddb71ccab0af47833898a5a7c3935b691cbea49a3e778db67ca16d4cbae7438253291956d67ebc9335fe6330b2e84e1484f248c7da3a712fdeb981655549f3b538f9e5e603e3546b198ff2460dec97282e0b568900906753586e01d0b5b294c82f7bd9a09f86670cbe5664269760a26cad75763e1c7193a243e9c4586f032d3431e2fb6d8b809375112fe6693e29bccf5bb43f8f2bbec70d14d4e8be0ae2a8f3d55f52cda3f38f275bb5b0f4a7a38c6c81f5568cb976713dffed50aeec5cfb3b61d6724f8ac9a3ebeb6eb78843ba136da1407928d3d100f64a117091e0cce29bb053b301a47849a20d4b5d2f7dc46c455aa5b326d9ed8f5203998979f06f24af5f83c9088809572c2daed191cbb82a1d37b108eb4e2db555b8835095045e550dfeff6fa4ebfd3f7bafd5ed7a99b3954bb9ce5a1aa72641de9282e3a097a620a18113ab0099b9be545a52340840c4c0418b2c0f9405417d27887e2763caa1b95c8fc83488facbacdf1c7fa7092a133210e54284bf610950e6a4064fad8e51e913e0cee7069f958d3d142c3ba43780254b62c5ccf720cf6d61b01ebf574b6b307d5f3c17cadb52ff0bc39fc4d9045d6ff6fb7dbb2ab30bc3de997b4b19b1591acfbfc5af726cc16bce4a55f5fb35eb0ace234ab00f5e2f7e4109dd034d6c347fb8f4f95789905d3a5f9018413f660d7b629737825b81f4cecbc7248a669ab61f3390c3d9b154f07431795f30477b07a9dda2a9535e0ae5db186117b21f27e5c5998981c362e3fcbcae6b4f5f999adcd6e6bb7b7c5dee2b7f1fb982c2c2e0b8f97cd66f2597afd6cd666b7b5dbdb332cbd54485f1b141c448223c2f2f0c868ac41122c05e3f3c249ae51d410be18bdb2e572ed73b252bd9f06f30d07d53783589c20c770d96c06eb707960b56f90a71448af05eb98f3415060874919d10f465e904a4f76b99d8fee164e1e7cdcbe7cfff8fde0899ffdcbfc67b478395d7cbe66dbd3edf2fb375b3f9fafdfdfe3cdfbe5fef33f593d5cae1e3fa3ddeb747733217239fc1220b231068a254663b7a2b44d55b4bf9005c7af276c755eab0590dac9a6b49274af446502549b3b03703389321fc1258829afa7a423419680637b2fd288893b3a140f856a593056c0a4f57b2c1f80ea1d14cf71ed0a4ad7a872140bb75837816553a85a80a225d4ec41c9002a76b8600ef52a50aec66a5154ac855a09944a51a5020a95b04e8f650250a5832239ae5141891a5588628116eb23b03c0ad509509c84da3c280d40650e17c6a12e05cad258158a8ab25013819228aa48404112d6e3b11c00aa71500cc7b5282845a34a502cc4621d049641a10a018a905083072500a8c0c5f4b772d4d45d31aa54555456a8ae52510114bbf2cbac5a7a88405b72b410ed3229bfe44a2c4d29cb579612945e6e2594ad4ce52b79894a2ba5fa8bdaa868fa399b589e91ccba6c3595ceba683525c970310e76f1bb7e1d670efb0a7f445dd2d9175aade74b399f555c20cb05e32cf41a3b4884c718259c37375357191254c9e5c07efbbca0effccd6d7287569cca463685dac13824181b7716a4dbc550bdbd62bf3ad93aa7c0a2f011306f9330ef21b8b0f495106a597f4a5dfdd19372f274652ccd13b7bb5c9441a94b40ba30c5802a22bbe704243528261c32c499edf791ee86cb26c55fb31952a59f668f8aaf960bbcc37f5f235309f42a905d6f9aaf76f1cc787f5882eb6ac458f91cb5eafea7685ab2f98db449e842f770623921ceb6a10d06d85776a093dc1769f63c427c8600c28750a9311c1f708c1769f91fdafd9fc11bddb93cbe8dee3a8de7ce3b0ec9d2f4e41608afac3845cae7182edff1a893c377feb4a11f5b7983ce1983bed075359aa7778e74ede7b51b5d7b79ec446316bb78e841530edef3ffd13d46ef453a17e97bb92bf29ee23bd2ddcf75b7a7bda2705ac4a7f02ef778e0ee20147fa3dc99f036e2cc285f1bae6cf1b4e4c8389f34f7204fe51c3f3db63aa99d69431ec28c285f7d683d03137252919f36e14b52de10cf5143c49e81036c3726e4664f34f0d71c495103212c39bee4d79f9633b08e8b2b304065120cc043bfdb2ca9cbbe6ffc2a0a43b44b8d90c06be7e8a261f07b56efc9de6e562ff856113be55a803c6018b7af2339aa831bf63759c0e002782c24b9ff7eca0fbf1a0024b526d9faa24d552306aa8cfc48bff641498ec6c1ac2223567ab5b44238380d09961045125be2498a990279b771db9e103242690b31a61fc481aff08ff24fcb437912140ecdc34cf5695b94456a11ea58c709324032d973ee15757661eb85831b20a6eaa23c595e46ce9dddcd3d2bfe553a327e65a08bfbc3b3506850067a7abe37627b0c751c89782c2f108c83bdd9f687ea55ab5e10f54228d8ebd2187af27525314b54d1ac578ba17dd081d23fba57217a7510fbddaad783e11b76f8d5dfc537195906bf0784e04f88fc0a750d4b5401f9651c9c866b8c27eb23319b7ccc40242e96b204cde88cd25059c4df680e303c18db521d64d25c875c1d6cd74f8ff3e9ccaa5cffcc887846a783699bd83711e31b736aa3f43abd1d62df19529567a641208718396704550cd0a5ea59f1f6fe58389beda73c078b2820bd7cece8f2350254d6b9bfe15f040729c9406062e18c8625ec12ce88e8f177f5147b71298fa31ce28ee42a082a9a8042a985f67dcdd4b4b0310aced0459c7b88920bc4209c7a060cb5ac01dc9bcc25782216b32af500a3d9768906f3a20c3ecef1df678f106ee06cc1bc7d1f306cad19b454629297e040d9009da0a025da5203e91d362c142a662f8743105c83d0c17dcd77a497a22f9f830c9cf90a7a3f480fa0e001e21a07b299058dede2c1da61972b6b1804931cc9c2ca5e0ef867fe382d5d193bec2b476ede2d8fa6464c11c44107a0155121b60c7659c5d070792184efc643fd8b84950804185ce550746f49437f04c9cd8e46a2d8b275c894c0be9b64babc0afc058e0b149d6447c54d1d36d2711e54888103a273e89591c46b0d65526cdeab7a6fa301eb2a948e07570ec4a351746e2af72e5a9235260f890cd248436a00626161ef7f68cd486acbcff8c50e4ae093cd2faecf377d7ea3aa82f71be59c7fa1aba01d8022be2918dfd90c0be06b39d594b6d69087f2f3d044721aa54207ec5115c7d72dc0d2434db7d7de1a45ff3416f9f9f6d8cdc42f401dd41eb666b25a8258925ce35e85223642d9e3e85282f40319b9bf1f15e466c4c9632528951441c1e64cec0fee626c150402835f1b957386383a96070fa794fda2156bcb03bfdb9fae6424945ad023989391938268946145cebe9189e1aca53ea46c490ae4ca23915c45b0a9c30b794b1e876c796baf3a161e69ef940338c171569016664cda83b22197dedf85f4238f298e8e30f9dd848270e4f024683f1e4a93b104eaa09f59adc36bd5e7c1dcf214c63c5a0160dd13b6250fe7d5cc55bf460d943f130a754640b77603ed5d56eb8f9148a61585c2dbde5bafd62fa537f327eb12e2d7cf7ff2d9bd2b5c933595e4848a5ca40d836f1fbd5dae91c18c3c5e81396608fb422dc8ac4a9059f342166f6ee9ce42777d0ee1d8e939265c2835923a8e5583a7ba68f56aa631b45cc08006cd0845b7bec681742e71a088b303c92499e9ae0ed2eaecc97df737b9116a7434521236b109abc535cb987e5cdb6a6943d1bf33765c4cee20b3156e5f8d93c9fa8b696cfcc613ed758cc18fe7eb2dfaf74124ef42b56df2083b1c20b1424785ba94e70b1cda8ccb9b871704e4aa7558f49acd71210fd9174bad3f298b106d1a288f33b4f29f6f66e33bc045e8695533f8c600ce625058d9604b207930d2448d7904426ef1b7e8f41f2d083e818176f39329e7a7bfc6410be45f92373a5937435f7486028f9f0405ab14aa597c49921e132a6b356b737a3d5b5992a19df28138565daa4945a41fc8d1d85ed8370c8ff7f86a3e4c5b1c995347434467885132ef2eb1c20e5fb9f3ecd42773454f632ad5eacdb2daecf801351f2e33299c348328ced70394e3d48ea314a6659237be49a22067ab0678a0d4e9662934c7e06c1a19d9b6fc3e0f90bb879f849e3f6132e0051ac77f50bdd3cd2440f7f132cc88594ad6f563d47a495685670010599cb90242effda082a4771a4a7420fdd452cefc839a70012646ddeffb04fbdf4c28cb78a12276242e15d96af60f3fd61a73dec424c62724de2f065ec699c9ef22a78755685fe321f18f8f08ecaba9b60d45fcb1f0a9a608dc362f3524d7c5ecdf1dcccd00cc1e165f6524eaf205086cc5f630feecc80b00e71168888001a69507cd16eeed22c68b363d0cf16244c11b7ffb569e36860d53382ee1a0e4d821e39d0e0a8699c5203032c8781f9f2fa9ce8a8ccf9031ffa8b4d5472fe6ad7ec8d090b113e87badabc1d5a7d91dcfac8aa95b3b7470fc879751783f183f42801788c3ffa199b8fd0cd8089a3045465834b468088d224339726095b53111bd81e5d1e2584809d4c0292d17b74bb2c495ba08416e25316d44865c07531d529656c57d7e443fb1fd6f41fb59cb3efdb6238306766427a172ec38dbb4701066a603b00760b200f8fe3bccf22da1170370ff0de17cff79e15608e01c93fc4ca0f86eaecb9e8a3151f66112d145dedc85892a3e7a3e4c13258827faa66eab07f9e2dac0e2acb7f54e688a20ef768047a6df761dd020857d406c0b411594af728140509abed80ab5c4513a40f5aed1e070f335285509bbb6ecd1a06db3e02447097d71a3879ce5c4570e961ac15672dad5719f6193aa86da820f58f7007205380dd7690e7badf9620c9163f7602b9029001b45d5a237e2c37991d64ef155bda615801e6a6917f71ab82875c5c23673efd6c22bca7162d8d4e23148d2752d5b6b7a46e6818e4145b4ee48b92d2027fc4f4befad861997901ac250d3f2d22456ac781ad08c85945b03e25521a2fd12a03945d7e204a43ff193ba820c479e4ac0b6f672719887a1489e8df1e31cb3e8d181a5cde6e4011c9bd870d72f9d2128f75108c8c74a619dfee9fc842481f43636206c88ba2d6748a134dfc67e4e29922800eed5da3ffd5d88bdcbc20eb8bb81302852edbbcd99450860ce1549420ca6de8232b60155599436c7e3133ce914b542c19dee418cadd10c8109ce94c9229cb6b2e5ca2623412d0a3ccea2bc4304f33ca83fdb62fddc2a5c7b467eb8b42463e3bd16fc8e223de7f38f1050c79203f016582656c3d515ca8a7345a3f334ab08191b82963a85ad753c5c21128c518f6a95ce772304ae2c54aa29924197caae441d7d8db25b29d413de2c6f81e173519b8c9f6e151929af9a03c6f3173da73776ea54cf9ec5b4172bad0cf9afd6ed839bc80d688e31548f34484c3077254e14162b5d00d446e3b2dc0e23aa15c0edca77cebd210e9ae38632131b6b4749c2ea90f87c8c3301569a076753aff7c6865e92c5d3cd8395f0e4b07ee9d24ed3d20d76b8658c225cda06ab0d3686641274a6310d7eb1728c59fd075dcfec6a1a1be928cfe159bd4b9526be3bcc6f1eb44ea171830031523d4ee13d1b8bb634bea1eefbae3bcf93a909c6ce1c50f122c866fded90e6e32dfa3c418292a3efb0f3edeada586a3e302c378770cd8761bffe8796001bc1215e2325de2b98b0580c352705c0e9518865810c58688a01a85c49d1535120a6d7934ff9a461fe17e8ee0fcc6c21daea42610f61936e01b99101abed42644600b942f3a250740d63443cd530236c8fa60c65cdb3140b6aea2e7e459b16062101fb536bbe15f105404b95ee450a3032951a9ec8677efbda625928ad79656a8ea8bef72ec80677a04cd7a051678e4a2dd3da46a6c58e883a0811bb592806ed11b16822c5beadf56d552693b23295b3c3b978221e400bcc8af3d3f9c250572619f8dd57f8c1972fae07905e191f985a4e342037a3b6a219b6edca0283796508b5bd12fe090571eb3ec3883f906acd223a8db386975abcc2b75a3fae4c56ca82116dee37eb1749ca5297ac45fd2ac5d95aded39644e491174287941fe902f5b75fe9b75af9683beee650c023988e1cecdc785a98349729decdbba009eb1b1687a126bc5a57a9a7934024e389450016282d56783d49b55149f44924f498cad3154efc2741721923d8eb2ee423587ecf3196479fcdd004887e48cfd3de1b1f9397b275256c7fcb64d0e9a50e69fd3102d4e7bab90405665ac9349d245bb803ce55005e4b031cee098d56ff4914e70f1a4881a78101b626c250edbae314a797c94942250c708e49fe4768faaaa25fa37eb1966c7acc5284f564d5c46aa11188d8ea140dacf781e53310e9343d49c6e91cd6343d824f1ddc0355eb9279996877e6ea65ad4df90c53815accb75b55d77b2aacc626b0618a1dcf5ba374b4881620e447152e077829f23c09356a94343a8f10b4cafbedff3c2f5f8bd2e5abe81648531de33061027fe687292ba2e2dad67be9c5a37499612e4074b64be79a76a67b9f83d11fabe942489de6e431e82e8c65ca06dbbf9c0ae93eeb497f596921ccce74adf2fec312995d6c3bd042c72048f27b7222f8b48de29350d788737a921d17896240ead1e6915a298c73ff0c50075b5d2b955404919e6d9001513e2deb974da021447a54754632033200403be81b940d97e003f181ce2fe8f72754c3faf9fa78804c8c3075335fd0fa09582102486a120314ddfa2fa2810441c54b9dc128212ad0184429aa40954431cb188959e33903686925c9907025892b2c61d345d8cbf6fa88ee4bc88ba9ded8758f8e6bc8c9ca0c04e335c0099698533d568bbdea05394294ce3ae8848a5f5d04e59ca3e0bdb8e70d226fe5345478f17c96103b8ed8b9158a288295b0f0dd8b6e0401463f9023243b8501662a5a0f4f6072e7a032ea1797026d487e222d9c0a3d3e4dcfc1230dde6b16c67ffa6ee89a74ecef45b4622b47671093485eea12ff2a64703ebe00d6fcd914ef0b267e73259ebce2b8b11ab160f7273adb90372b22198f16679caa1b94d2d946d1366bffa2533c5ba0fcec3cac0229715304eece1b132e76ea231077205da0b968abb9b08a2c075652beb6a048271e791947a840c356a08977b4ac64fd146199270312f06f8d224521c022f2bfe9fbafab1920ed28aa03a84506065cee71be4855591b19400c7a00b32625599a08140aea4ea55dd753aef9fa52a42f7704e187861177afb5276c4415fcdd2a014cbc7a7b10cc2e42ee99e962a506518f89b0aebd4410788ceb1a5288609dfeb20d268f2f83934e81718401d57446d8ad5ba9697bf1d94705cc965e5a63da7f4582293ff1bf0c22af81e9b4df14e5b2902f4726b59fe932e7536b0b1ff9ccc71a382b07ea79018f9b477ad93c79a3357801cd8a6047c6122e26cd789e903bcf844fda6383ddbca8ee7f2c161a4e8deffac03282ac74ceb0617b5df1997fe0c5629455be791d8b7a985797207098348347ecc1e158e90b48208b3ba0c46d1cdfb6c3116d52ee76f543cc14fec689e00c42e747e9ad635f5298184c59660929a0f3d9f1b46504aae2f04c88efef1a2163f98a50290b4cf12e571508a3878ee9600f266c10edace59d2ab4177009037db24ee545c68cb96a640caba984852bb6b635ea0d18b6b6923cc01574c77185f3d97fa00cbb1588402ab66642149a8888bff025f5316f448e0481b257c584eb19e47738374083e87e155c15920529540711672e47a16e6090f9cfc80a4541290f80ef98f4413f18e84316fbe22318e71a3a39479318dd4f749490e2934721af32fdabd1a1db25750c5f0f206209cc38dda536fe29a509c11d270366ff0172e4ea9c81a04414cd6ff93223bd80f95674b01c8b6d06712cca24d221066816177da2c8a610a9a34190916527da094d18ac2e2e0ee8005cd5662f1ff09b4d307fde450234f7a1fa4247bc03747621bcfc23527e56e7f7b97680425185c48d1d47f6351441e8e6b0d3df405b887eaf55586e5142f33c83167a686d11e9bf4208ee8ecda5a4fe41b8596c44759ceaa4bf96b8f80f988b724e0d9bc7baece3dda5983ca2d149184480c44fcb619566d08156d99e351dc0781d80adf2b553d47136f96b9b64d839513a9236e30481781d61d83cb2936131dc37b3f825b9ec962d8cfa1d8495222432cb3fb33dc47243df6e4c6b59b08f1635bb172d89a08fca80e8e51a8e883fd54d9982ba081039a40371c5951796626161397b50bc6546097c66b405ceb92b33a903ceb4810a91d0890f9607e0bc267c6531d27ccc45615602e9dea58cdc8bd64720f020c88912366984ad8c43b33c88aa3a52cbb1406548c19f4e86287a1762c5397eaef00187245afce1faeef804f89524ee24faa7c52d44ca867543c61c3e4730028a2c4a8437710f9b4cf7a3d231b85a8fc6e2e063db9d90165341816dc2d4ec137ab6ac4db9d2edbaf87e170960fd8385aee17f5d7c8356eef7dda8160a5253238e14052ee5865457b7dfca29f725e4b2595f04f14523dbefcd9b3fe2e064ca7c4bf3c34871b791c90ae65de80edeb2ad0add547aaf493dbb3469a6eb97f42470de4edade78694ba7897061465076fbf20dfb8a43a9abb7be1727889a5d28c5e30633b883f2e3eb6073101642f90bc30e06ec62bb011c1308457b396c57cc380962bb87264c6f57160804443fdace41bd77a32915088fccae23ed73e0ca9ef04ec140ea4fd381100724d8df56f77c3073109c7159a0a65f6e3f64442f77d4a8eb2ab1b8de6b94abefbbdaf06ea59d0c7e8c2e5b1d4e768d15297fc3c18a8fa4d0a732c5829f5890e8a186dbb8131384bc74ba9e2abcac4f004d816753c95189289a316eb18bc500322655868699b7f8f941655585576ff73640d9092490229ad3a727c44fdaa4d6d91a8206084f0700314fb2a26fb6344eae14f67dc58d6e5e4eed8337f811fedccee2ded92cba9d9c4bb60827785a6796d8afdf300dd3ae66991f1f93e219ead00e9fef67ae1010b128f7a3eb0712cd091e9e31f597cfe315032ef1c371d2759507c85b834034a2ddc41d3634e3c40b67c21943a5ea0393e5204364ae0140fa8b4cb603ac990921eb34aeae0fa3b886de692db029350fb90b18a1f13797fcd9d2ed6b89180233622e803ef3102d47748dbe45f18889ec4913d43cde4a1403e3054629e2d908191157febd4f12a38ae9df63202ee2fea0b4afc3ca73db69eb66e72769f9407619d6c64645bdc330e209a7f0073a5e7d613838c0779d9e3d56b2a49a841d372886e1c17e089b42acdfb0d99b86c846c64e2510814c3faf421fe38268bbd0b48e29a5ac4a36a9dffac68db34c509da80ef154d2deaea1f721fb385bd3adae453ccfc2399aa8733da699f1e99701f286e6c135b9d2a3ad44b023df26f208d747422789ecc2881a8d907b99ab1f0306fa271fd974758729f641bee536add31af479389770ca8ec9ec492c699863bb7e3cca47d424a8c1546d41ef06d0772796a419bb7d6154082f8dc2dc5df41b8f836a34a420e1533e0a4229770a983829fa8117add35ba4c50edd26ab49e920344490e12d78c2aaf58eeaceeb6696d0780ce409055e7184fd0d55205a594d3a4de344ece59a380198bd6512eb6f1789f6c794e3f672d1ae31c8c4bfa995619081b43ba2f5158596aec98d023ef7df86a2699cb0a8a7655ff065e5343436f18884fc7b7fb335b39610b209d97bcbbd037c0d2e0ea80d25d476ee53729b9765509e11cab156b8c462d55a617f2719acfae911dbed9989471411672b948b6fb7edaadf6e2edd1e196c7f7a67bd3eb16c6153a95b7b9aea209b3add86f2b36d572cae2e68cccb216679f3d5e7644f53cacb5bf993ac1ce420dc209497af1c5c7910bb8818daeeb25b797d175e1ba5b69be2cf29d54d29058e8f37c5dbf853e762e545cc51cea76379342fdef7d5cc54aa4030ea9bc762d55ada7e6a3579736bae721c5747d0144ac104b8db480a0cc5161855f2248c9eeca134d55a6f2e19d95ab9b3587bcce50ccc01f7b414ed85ce02c79f3a0d38def61b325cb32c93305ab9775ac9e4410c73a93812b1e94667c0dc566ff2502cb025cfe6c606b6d51b5bfe70af5b3369a08e411993af98d3f710cce14edfaa87f1226cd568a53f1a9130e54aa818572a7172a884e2625398d06e82297753b0a63a81372730303716c9163d8d58f447db4749bbde82366c2b2936c723080328b852d94f564ab0b127b8de95116c9630885f6d245bf455895d62c484be50d4dd8e6b8dd99e3919efb2b53dc683f14c2fdfbcec9c2c79d16ea5178e7b297915776dd44814ee1c256d422d047dcce91a6ccc2906f5530f2dc771db4f1e4e8d1faec2b897b41a58eef287e3eaadb56e31576e5ef6f22c225996aaf4a845673dfb1c8fec8d48fd7db4aa6eea3614a17e79410a03e6a290b67197daed31b7d620db41d57463abb5d61bb5e88f22b510f499b51de5e5ed9c6732c1a44f7f746e9b4cc29264b7a3aea5a8db6714aa8560b7a37a6c17827a359da62c9626af8fb0a8dbdba82136996e9452461b6d26506c8e455ab42bb78f114b2c926d614edf3e1042784a5dd5b73615817a55adce7d65f22067bd224ccfe27949e0900fa61fa68f47510b16cb5aa197ccb743fa6dbb453e6cc48463b6478137d471c9b341c4909402b6e84180e94d80e959205f70a7c629985ec2600d95b01eda3a28096910c2d009182601090d189a6ef0c85c2471c70555b75b6b3daa6a42b55acb1d751b4ac9abf5a56b62bd636d3ffd458c0b6365eec9e4f2cceb624ac5715bd7dd86325ba85ddb8579decedd8622b47d3bea76507f5ea9a64c5e0a9ab6edf41d44df42ed82b5d65aabcaf326d10c9ca42ee55edbd5586c5e1f5d61b7363a6a170403a6df3c0806a0ac34b973970c536401c31d57e26a10c72d695753214ac3154c255783da956352d3673ec0d4a4a3b712672a0282014330602aea656ea3512db01d6b57bd254efe402704d124205981294c4219948c20062501090bae964c46c4ee548aa624646e4c922d7a981bc303f3981c3cb2451fc50ec5c2b5a798245f302ab6c71c20e63297b950c4388971b803e33137c689cce14ecce10e7d9b6e6c17c5021bc3fb01c303521444b6e863bc2145161e21f19ee88fc2a48dbbf5be6481b4ba8b6cb572b1ca5eb22ccbb26c95ad562b96576350e7280ad5d88584f9d0cb2512885e4e2185289551303d8adbdaa85dd40c50e81b0b5a5da8698a0b24e09a055cabc0b5d667f31b8b4395622dd5a3aa8b7a677f59cbbcfc6ab57a71a9808941d1c3781bcc515ee4bc98c40aec679331d806ed46374a8f4a8145a281a29d981f9a94884f76626ea3db48be626e0cea7107e622893b327ffc912d7a94cf4fcc8d01c53aa85b25c1d2430c7112ecf6885197309944faa0bc86d29f507ff433a83f5a830eea3a4b5112464fa6eda59b5b0abc3d3bb4d17d74996716e6582e13e3c178909ebb190866b919f5ee5d47bf79b3bfeca91a58eeb98d30ed206a6f10a6700a9e58c26480e3690b810738ded437c0f1a5b9011c9f4ddc58f451ed3ad9ed7487f22561b2451f81b084c9d7a3ddb27315439bc5c427fdd1c39d188405fb641b0a03e65e6f2dec679303e66e96314c256cd32cccad17d6593643af1049d4d211082ae8a60793df3e52b68f87731c32021a8a3020a308337abb724b8123ec4fd36e36af0f34dd681f58337f666c49fbf4f3847836394ad3324d7b7ff1b2ce5ab5ec534e0cdf5168b1570044822050b141f67399c462599b3483b636d8fe0a09b6c6f9f8291fef64d2a7f929679491d239f1bc31ce0cd65fcf9bf9dc2c09a4252d60303c4fc7c16c9eb8c47dc0c64f589c40fd451ce3bd93499669f31dcc62ad70c6d9f424aff4d772356427ce99121ccf326f96a4bfcc2783cd0c36331f979bc1329812dc7366496491cce34e9f9ee9d3cbbc52374d45646b288216b258c30bc4e0051c9ffbe76be8001663c8a060023278610d2f108311ead3f330351591f920054ff8422602272401890019144c4006231c2f845fa6900a4f28b2219bc04416afaeed4d26a99ea267b0945e8a457fd40c3622c957cc736511c957cc85b0a53a8cbcb9aa1e6fb6ac0b616b5e22cd4aa22024dc44f9db8d79aba7794120d111603fe21430474953f183222c01370ba2e01c93c42538348d1b3667593789f5e902ce2c19b18dce7e5c742a04b4a588b8a7a5c0f1f30af2d239ab8c724a0a276d4a298c4c640f4b29abe7617a48b110dae3e149257d02aa6bd7ecfd2e8315f6a4701e6a980749b006e2dc3a98076d803420ce24ce32287176888564b75e28b1c4282c670035292b8450de0ce1e1e7a390196384b7462984af7e5f85365482dad0cdcf096bfa1d109c8f61149c4bf07052af09134a48618c31d20ba305b2f0f992e668456bd17d39214f7f31cf5b2734026be2a709e04c96309b253c9cb891441a48b131a59cf4b877b364f23aed79461892a813af441afddc78f647a98948240236121f712ca27f657b6f2fdddcdd49b206b0bc0d8335fdeeddcd0dc35cc34af14ad810de90c5ba755e4e99b5fca49d75ff2e16cb46d9b2d112c7186dfa93565a2ce394f22b17501918e1f9ee45ce09a57c1181adf9063ce77c0f813573d6be73f5022b5f6485488b2958f8804860ce76f81cf025218c907ce590ad7ead9605a7c026e022716222f01561abbf6da9d40beee7971cf21585ba355fabb5ac29424fc817f7da1de99f0dceb597bb1012c19a18a508074f1b9cddfce11993ac40aa7244254ac1b36fcc0e9c8d68387e47bc4ea0e02eff431e09157d313d803c17df3707c17d1bb267665e21d128eb4d8a2ec2961d638c31b6fcf47a5858de981df0510946b40a83c18c683d83a1cc0a4757855f3c7c85af126c652358ca5a0241076148935a08f824b39b52ca52d5e68c3efdc1f73c829daf5a179326f52252524cbad14a74129d983ebd24b9d321452b3129466c3a7c3492afd39f90af8eda2451d53f3d44cc01a088918f3b48e2ce0e8c3e743e015155b6e0a1a594ce3967bc4946780249bf22524cd24a9630c14d6a4492affe89483f3122fdc8d705600b1e4a81e12908303cbcfc81b481303c343d016b60ff004579733fe99f38bd2caa9e5fedbc2c9eca7a913bd7f1c6681df36ee6b15889149e16d9941783236227dd4f2ff9d84bef1310751b09b9d3bd7ffa83744e19e58c31c6f7a3474f5e348a47b2054b5e84c916ec599992903bdd7d42b620120e40b0812460fa880473c4b13fbd9e1596973bd0246fc77a497fb07f92983b4fc816fc317c8649361107c60ec2508606b628ba6ed9a2833330be93d9a5ec1a33486bcf8eb7b6348a47134a1ca8c4a3d872d24cab1bd7954c27944da958562d2e5c582f3031322f60c4b87765cce8b0030f5fab64a35d334a910b50a7c5a50bd8071bf9ba393c8e7c0571781b44e468178cc3f368d70fcfe3e347bbea76eef04db4ab2b1dbea75da6c30f69d7e9f046da853afc1348da6561d1a75dabc34725ed6a397c842e878fb176b10e5f0e1fe12394397c71983abc8733873a1cee70c8c3e177f848a55dad43193d1cd21c3e5a0173621cbee627ac7cd9d078666120fe038d37e13783e6367ac6e3cd487a98f1dd69c2d138c666858dbf0962876720323c191d649a6879ac195690cf5bc958f5f0e0d96b87ece095bc92111dbcfafa048ef1f8196fc2cf7b0c938e89651c7a8f3723c1313eefde8cf102c685a5ba055bf424e5f988a932c50b8f07862848c6e3f142054a8c9783354508c623c2454a14515e3c1b2e6cb0bc1a2d358870f1705638395c78372c373c5a3c1bf8cdab5233b5f268e0379fa26101c263fd604d3ae6f703cb0a5bf3343c54ea64d261712aa532a552d6879a9249c7c4a61b3c2c160d9e3eb0fcb8ef07ca9bd966963879af0762f2643499264a1e2b6305c17245573d58da69876c5e49962afcb2d71e9a25061df4629e3f1c4fb58c6a1ad5321a238634608fc67d673c27b542ad65940e56dbc1199c1b1b9a4c7efdc199fea076cd93dab539ad684847585831fb8bc742a8112a6f53c170ced3098f2784f231e6e7a19c3136ec121554c07be073de9c4938b39969edc1fe6036db081f088203176cec0cfe9cc133184a29673084aef8392d2c0538b330946ef899c159426674e2b4cba6c6849bcdb6f900d2cfc1b3441023b0865ae0ee6f55c3c1a971d3d9e5c971797cc1e6efe3dea7e1260d039cfd4a02a110b03f7a0ffc30be548af238226efae8d9fe6869f382a51d5997afd30ff42296b9b1f1d14439e124923ce890307d272323d3ae3e2cd55bf9aaf7932d7a6a050e855420c5d4be88e816bd8dfee86d74890336574c9fbf0f531b9fcd4d0d1f40da057b7005675884e97350c134c8072590b34d3776681429b69c03b4995956c2f2ba66fa8bde674342584fd32e58fbf2317b07212c6179082184d0660332cad8514a29a594d1650656764093851c10e1ccc23868d2f10654f03bca778718c21c22168802370dee4719567f1f3cbd7c7cf6cc8a1052188b40a81d1e9a8c2012b7c035ce4ba4881138b56b5788fc24323f0fe332c56a8738da95398b85b3cbea2ff69434fd75dfac033858be676666bad335b158f4a6e16d68be6e6bc1afa600786ef7cbbcf6320266e0e127678820c1f331cbb298c5ac2714649c47bffc7618972864092185a552e9e6ceab50d2ce8358f606e7c5f966faebaf09226844cf935d49448e3964d433798624b62c78822ca35996655996655996d18c525ae9119e48598ac55dc8761d5be976b3c254ce70be57bb92a1b9b1414496ad4a35cb32a8bdd4410bbbf47a68d251ba1bec158eb366449a14f9da5a555e2112bd6c64458832d1738942cc41447f393adae8c75873109183f673f4d094f89384764098beb398f6e711cc61c1704abbfa2e51b074ca4c82397dfa99c5cc621ee1bedc10c704fa12bd45a245d48862418fdae569f193f9607a9a44af644b307dd53268b2432cee7740b24a85ed9aae22f4163380872fb0a6bd557f10c208040526aff09bb90d8d0d38641407394461c88461bb34978d2525cfdec5d4b76bd63a33ffcd5bd6bc8ccd7c8d1cf306cfaac958bbe68c20e8d29cab28b63f6130dcb7d4df9cdbe78d49b63b046922e96ff66390ee69575f20fd794b44b1f2517a3cfdcd12eeba346dec3e39d9be5d241ad861c9562c63818d40fdcdcf2c66e30ca2454055583b04737230f7bcc19cad8a498416020b1f63122ab8b3604eccd142c4b747447ff1191c2c7c1126dc65576c87a317ac0d2272449a9dd6775dcaaed8128ef33b84b4672a226e812f7cbd9d5d6a1e7dd3b3b22bb671949141c32224211489b848c4998559b026de4663089d8023ac02c72ce0788a0479e210ce2c1b9d02aa4a3af9ae6a99a95432552d3b75068b30844439e40bda1c47be3a784f56584aa5c7aad78b3b97af1861c904614d76bb8ea61b3cd22b99629030bdf403984a3f00942d6a321d4977d345c2bd74e1b53c49bafb1eb03f5c5bc362093830420db1b90f37c7e88387d3ae3ce31dd47a43e92f3bfcac257978ebe3f0ab021c9e95c4e1659038008e73f89976659de71c9ea65d5900363487bf695776790f5addb8207ea05183935363081efee9f01d66647425d3e9e5120227a7c610de6f8c1fc60b944da96e7e3810346e53731f70726a0c7198632e73d87217878f57da9559be3a44327710700871129e0f267e84c7b380bb3c1e0ab88d77e339a1bd009e4d006e2ec05b02b899e734496436d4c5a30037b79e04fd114b6447f05882014782890200e0669ef7c084ce0c26664c26241333987031e19ab2f5799da8f4d975a26653328001ad9b03f08c0174326046eb49a890374fb9c4124bf858620900fc88257496a0e921bb0faa840c253e258e78122a3425aa124af4b859c68f50e2d37828b1c402ea7758c0cc75a22ec075c47b2cc0889905d805b88eb879d3766edee1dc75a2caf8779da8dc1137f3dc88eb44e9881d6edef9cc75c2cab87900e7ae137646c69350b1ddbcfd089e9bb91bf11ef59a123cee524273f128a144d5947029f1eda09da7bec75f9e0d8e9b0df01745dcfce297c79350d1d9945c2fd73d0e1ed7098be3e552e24228e04544bda180cef597028420e2e61f865540e7bad9a4dd9fae13f5c5715c27ea4909d7cd3c2e84122e21701c86122f6e2ee2a7eb84550286e966d35d3c6e3e5d88bf7ef3bd01ae13b554dfdd9c87cd5f3f5e4f42c5fd8f52f7e3fe49a8b8b9b9b3b9b9f4d781006225030410b7b9b191218001ae131abef9e6363fee83e784869d00e1038c8e9b61eee23a511320e3c2e6402440e543027cc87199eb84c54e120073b3cc5d5c276c02606c6e4e693b6ed671d575a262273f5627c075a2aaae1325ece4878d0f3f6c7c78122a74dcece2c709b0ba59f5c3c5ea49a848dd9cba8dea3e7cc781b03287b1373740c8dc0c2303f32454a06e46fd0688ebb80de9c30be205f07e78121e8d23e1d59cc70be23dbc1f7e8447e3467835e7e105f11dde10278057e33abc9ce7f0705e8437c47178357ec3cb39111ece07e00df11dafc6b197731b1ece05e00d71bb497ff001f06a5cd89f8e97739b497ff000f070eecbfbe12e8fc685f06a7ee305711b08ef87db4afa83b7f1685cd89f0f5e0d8dc77a0f9ecb67782d6f792cff3c1e76f074883b08f88cd772191ecbafe7dd8bd13b08380caf2506c67b41c04d4205eb3ec1f0f614fdc1bbf05aae93037ce5b11ce042d952a52cea642a75dc56b5f6389c79d4932c9062ca19f0f43428f868a107587a5916a4788295db300d7051f8684f0a3d383d3816ab8270773a787107b64bb6be679c0a5f879a120bfb83fd7d6f3d9f87c79fa11f9616961695fdecafdac35bfb932a17962b67bc8723f98ea4f51977f5d48d9d032bee39adca47c504c8fed4059b553155ac5d164bf8fa5a97d6532951fdbcc1aa7c707f6f1edeadf7872b8bc23cd1ae979bbab5f7e5f2a943f94242858f9afb709a4331e350b6a40f1749cd85a287cb5ce6ddaa07966bd9e149a868893f1d0bc7609df5976bef62bc8011e3c58b182f60c480313c08bf1795121a9a21464260e53d34ddf064c4582c2a64dc03dff1e15d776bfef20a5f347fb9d9e52c8bbafde2f2f853e3d1782db7215b5e7e0f4d385e0ee3363ae6c97ae7e2c5d8121c63ed8a11b3eeb2841c50c130fef26e4ad30e495b5c8c346ed92100dc1297c4980c4fc5a43fa099dbc3c22d57c8cb61a862b861ccf0e04e0f1edce9e1358762c669ded2c36bde72190fd2e265166e790f1e1435efe1333c28a8601d0a9acf7834c52041f319f74173a19c71d6f551737bb80d09b10c2f470ce331bcec727b5818c6ef5bbc5cf17d174b3d782c32b021c9d1ce0c18d78b79f18a5fb4c0f0625e1ce2bf63bdf0e00b8c182f3060bcc088f112636cc909092f2fad01ec424d45b878ea42c9d2652fb087fdb1dc46c71f59258c77b2c80beb11d2982e305e5e5e6e841febd405c64b8cc7b84424667d08eb307a48bb58d7c98cf78da89464a5171e6c6971b9310907acbc6af6973a35e180398b8c8b97c35ce5c5c45c1c8d65c48da1a9c7cb6a9662603c2361b02fcf10e90547ca1bd25fc6d138f5d559bc0c04cb169696a75c9e6a6961b96861b15a582e5a5a522ea9140b8bea2c9eea294f0e31f2c44b0ecb616ec058c9721c8d596ebd8ee5292f03c1a94bdb9ecb678bcb593c89552ece72ed9d8b2771fc79893197d8e4494cc6287108b25c54b176e51412eeac7a825bd5c4e54258c3a27232e32a1ff99a71534949aa24fd75cbed732a09db1b7dfaf36212232b9fefa92e3d154bca5e5e7bad7ce14c8143d5087ff834ef6eae3dcca8b93417ca1eee8cfbd6e7f1706d642baa7c320bcf187f70c09d202e0a19bd8351a7a986fa6b9596049b5543900ad84330d9067746914a4a21e706cbc3049b55434590095adc00372e6a1fdc374ab598ab047779706438fec09a86589362b3fbdd190d0916089d7c4cf62a6c0a09e6b862906c2159e91ea7d8d820c9f8039f1b567a143bb57be93eba973a1f5d3de7060b650bde1c8b5a3f7576c11ec9966c2ae6a128bd8382fb76534c769128bdbb12dcb7fb28dda2e9f9e06e24ba50b6ea0fb482ed2bd8f81c9154445acf2188b49da0c91320520fe1c01cd3e16bb46bc5d2f28244542a10089d5a7ac9cc601869306c180600d4b18934fa1571608ec4104a138e9620ddeadb9c9e3fd4f3332b158954372243d13ef27573737373d337f9a67de210132f0259568cc19abe0dc7d9d4daddbdf9fb70d7f58fe9b6127854baec6e220b6213ed68626fc255dc36600487b0f31af788a38eec22ac889e5a610473e0777a3f0a451e409d1c9146df54c55279820f701f66259b6406a92fd14b0f4e5529c2b8f753b0e8931d77a75de8b653cf08c54e56b7d13d904a1c6c8792a622724c1bcc8b2a2ffe7431497f5b587a87dc9e3807cb51dad9681aa4bfd6b40f69d7767ba3cf6e4ff7aee40d293dc3a3d21dd25f4f2dd80c8f86b4ab7bba20a50f775a8f37a43f78f423c8909f2018de22ecd663e5883a41be4a6421155c6f8ee29cf7f5d71de5d59367f220ace94a1d24ea4448a37bd75deb4e7177730fa482b52323b44f8e7a2bed876c19138384eaa91f607596d7db05912f96c725eda2d7622c0a28b80752f9215f2ad58db0d4bd7135c5ca43992536b38c506dd9d0c1999c0ac2fd8ec6c176f0590c6c87848f7675cf606033140ce1f828706b49603067bb95add6a4803a349106ac4933894be873e480b49c3dea36dd8d31dde4d59257ea389ed3e569159607e610c19c1fb0b517756d604ec40da4ebb8cbd3a72b899ed03c1ffd2dd1ada88384152db0c0058035ddcd7979604d086cb6b12187d0806d5241a5d68e704984e99168a0e8c90e4c0c7718b391c1c2dc18549630382b8c676b6acb9d6464148ba215d1e8269916c8e65854d4481b4c07b88d9a6073c784e40b49effc3475fa28254e491d9a4eb203f0158f608b5e0a8a8632d03560fa1510cc81312e7f5830cec5c870376f325e8c07e3b940a16e3f6997e934c6fbf44dbb54e2ece9748a32312e92b8f31b833adc89f173dc7694c7ea8f3e86f71f183749344a01aeb56e97bbcc3d7931aa33e1e1ed9d3dccbdd65aebcd1cc71d0be15c9cebf1b049e5e93041a2298cc047497ff40788a029109e6effc4e8c84600e4d395228b2a55ea56f6642ad9d3cdd6548a2caa54b7cab22ccbb22cb6cf4f11ab2ea93f7a175ebcd21f4d82db06498acf124ca19060488a4f932421a36c044038ca0d23b2110061ed362c26c9571b19c10ea2bcec61d4b7d376532af651ac351fb55a82ad958b2caa7b98f35a7a614a71f9863460eea62c30f71257572bd4ea78b57a8c49b08449eef41cc7bd664930ee5dd7c1f056aaa693a95432a14aa69f6a8635d45ab72c904c3f9dce954a198b6ac3b1711d8ae352f6549248c0556601d767b20a5ccf229d80eb5593436d1b0a8542c162b4918bdbc9b459d3adf7546b856df59d8419c91fa32ce4124c951871b7a7128eba6ddb7633b76db574b29c775cbde6b85bee27ee256ea2b6ac665996c1b6eddcf67a5c371b7d039a4c26140a55b94f8cba4628de7eaa8cc1ea4ddb76bbfdd4510638de00c728041cdb946d599665198d42386ea75abf6ddbb60d91c19b9757f8a442a162bdddd9df8f445c6f3dea6b5a9e4914d6348b2e7c8cf8623465efe056ba34dd30e1e0d81511278e28b0763312d883e33bbe0707c3e71cd8ab000ed68ed22aa429db1fcb391ffd5a9d9e07e66ca71185e3b309c7d9baf166f9526e6c6f910889720e8cba3d38f8c4b2fdd11cab2369178f151636063e7e06de1d1f8e37c7bf8e455936119e271e3ebee278730f0e8677e5010b71b61eb0dd6514af5e11f110c78d02c7293dce8b91af8fb163e4ddf1e5787fddddd946b382b54bba046b97520ad63e2716581beaaf33e968e177a61bd06ab1fbd3a49c27086bb467b26336b3ec14b323e2f87827527fda2f5fb35bb3b4a21927539adfc82e8e88ad7cc12156965af5270f898460389f88f2d40aa537f3317c6d57cb084375a98ae8a329c10b69883e1d2ab6842868abc83a3f1afb34610d84137ad45444c47dd89706c3773093d5dae80eca8e739e3aca2f7410089bc52888a0b009d6c4532f7b313b5a887c1fe2ec9da3102ca49e90c39ad855764b216bd4892b335858aa30a7f13c8d021f89b82fe3d041259ec47047051d08e1eef2da3b2d6cfce435293b42085f5665b0d9e35764b0f9c35a8c29c674777cdcebcdb55eed6ab5ebe9e7f6ce06e6c41c2211094358937fc03766c787a9c908f92e1e6ab78139106befeed75fe4b84bf35d585759d8ecf4331a15c1bc44cd76092999600e3da41e1114a6975e6884fcccf25087fc94b5bfc8830ece74b2e79c8285b8dfd3b6000943a2201ce1e36ee68eb026d68a677f5c082c240a02130c8982b004772d63cb53e67054e10883637c8439b066832d7a4a9fd1cf68aab0867e25054b0f71db609b1f3007e6ccd4c0f4d935922be8e0b3912b89ae903b85d04b21f296deb79aa6b0f1c6d8904d2592af4dbeb4f7a99107e1d747b27955b626ac1e3439b15cb0f99a774aef4c2cc926c9c6e8c3f4a3c93429cb7e923258e63333254d829e60fa2c96318182e9b328e7fccc50f794439757583bac3cf2c54282c160529420c910696146e7274fd3ae1396d58b9967695eccbc457c68af4f02eed88ba44a285b51ecb84cc1eed44fc1c29d4dc391ad1afd49697369fa93740ef683cd9664e881900f6f16f8af5d50f055751e69f49ff880fd592460cd77f5a4bf29faebcb7273e549b52bb68be5adfaeabd4275aa15bcb8af622932278b0e162314a7ee91b823de98f828b00af5d53b223deaccd4994ca65229afa78453b402252c249e762c1f7deac6db78f32c5ec41d2db17ca647a49968f34c2b953a0abe70204e5d4217eaa96bd0b57aea1d74a9ae731369b07ce5694fb152d7b3a16179fef067739b2d8601e310c61cc2974318a3bd634d0617cab572a9a2ce4d8be3381e5689953a4fc3c874b085c82b9cba2d452fb370d0dbbfc3912f1ed9b148a3cf7aab05e6b971be918957791dd4a0845f3c8f7ca13a161f03c7b3ae4bbc5d6d215287b9b95336da7ab984b3b73c37e0be8dc9cbbef266cccdf5e5660be3e67f366dba61fa5439b3291d748b2db6c8221553b131ead1b4836eb1c51647b8bed36ef6b0f6e8e5a75edbd553c2a867f761855d10b2a4585880703c434998d2c490be913e927641dc491a568529b81f95f459525e127380e16852dd6857785bc12fc63a266d4e85380461231213d1b005264e755ceac627b0a69180a3973bd744130e38842da03a91d42da0ba10d6d09b23cbcdf1b96331c6ec0e877199622112309cb82f4500b33a814c0a622d19839619d01ab0a440001302986b44c96a04ab12c8fcc89840e6f6a000c6121837880a5a2b68dd2658205b2099bc00062d77092029037a7fd0403661b141ca498a0430677587b800e6a82e10f904e6a4b26091815506964a0b9519546780392a34ac90566958a961b5062bcd069636b8a193e81b60ce110b079873a4e300735aaec09c232a39c09c23dc1c60ce91c6adf77b0b9853c448865b2722007c6475a42405cc3932318cf7e506604e112313c33811143ee2820330e7c82909cc39926199f76507604e11238d654e44e2232c0fc09c23f60330e748c530ef4b1d66645cef315ec4fce53c3e7e2c01a489203d433c235cb01d63318101926902e3496b8a96200a85e54d24f5d5fb1da55d44525748ea5046538b671f4d3862ccee702d10311121a91b9b600b441c591e8f0fef87b7049016af89203d438c3c8124c26292e81395c49fb824c62293feec63139813b3445a1eaf8239b0880560c06030cc3a1198139139111827d23a117a222c27b23a110b684438de027008a77ea308eb6622ac1f81c13027520446e6425e9c87179181712131febd088cd685fc335ea4452fc47b0f2f42592ee49ebe08cbea42649ce545565ba42e64e6ab17894fc9e14abc196eb185179480738f56054e5d880e8fcf46329cba475c60d6cdda165b5cc1427638cd85b878cd61b338ee1dabd966568de7c333c2f363891ebc2682f40ce90f09e62c7145efc26b261d4507af63335e03c9f0bac9f5fa89d7537450bff05ae80a9be393f884f578b35d1d4afa488545ca0e5166a0784fbef7e3143cdcc5636ebe0e7f7922e3acfba425759fdc4abcc24a12a3e4c5fbbd24f597c3dc5c612e73b3bdcc611089570891b805561d62186fdd7cdcf2f1e062071d66645c8f85a5bbcab3401438c66c1122f9080cce5a15f837c6f58183088c3be4c5ed91b941626e133017487f7d222ff7c75de23be2165b6c8153d78817eca391b577e1511c8d6c8c5948149b54c17028ca139c21518cd9885d6ed8b729dec529a59066277b1b9d0d4589613814c5076788e11011e480797cc09c965b1d13670d9401ba54ef371a604eea259c05d4899086f67e1fc11c6bca3c60d304e00beeb4c9c4c291079c219147838d10d1f0911e89edbb11c070c42a4fc24f7bcad3608df6681f002d426ce35151249efae848c6207b38f7510f95706e2ff778585e7b566f4f69e21965504d07b10526bc297a3ccf8bc101a42354e962c7a84d0b6bfa39bb96cdec50b6642b8aa981ee7642bcb43047ded4a887b730c7abf35a5823717c02c7284b1d056d69443bcdcb136b33c62c66dae395d134eff9aba6e7f86e4fa66a50b61ea196c9b0e2eb65691ef7e8bde78bf6ed6a4f63f5a75daadd5f99fe28eb311b4ebba88b1a55bc41085281455bd558ac2b5c500c987e15e18cd24215d869e49b6947f689a30145016ff7a3a62fc654884b9aa669d98771b47a331cd2fe69a75a764b40e86005e6514df071f70e05ad10271a80443b54c839b739e55d3c58b90b6f2c11fd410d24ad88ecbc0dd9137dfaa7a119f08461c0137201cffb8039d10a78fec56379ac3ba960733f6ba25839abfd5f2c821be4b98dcdcd2c2dde5c2f4f7f596aa6bfedf2f614a21b2cf8e91b2c88f50771bdb9cecd9b77426b9a3e133693cc273836c171ce26fdc53b61b046a78811d46d1196607afab9030a8cfaf4b2c5f5e658247bc4f2f38fdfba6debbef5e82e64cb4c3d3cdc4de9e23906de5cf086c29b065f93484257572a3d5219e13ccd2458b35d218a9f56fad1c5cdac964bb4baf5319514b300732accd91e0f5573763f7577c787bb77575e29ea2f6e3824de5e927380e1cc7495976360ed16a159cfa429dbe923a51e7deea1f710bd3db47d9eb694aca7447bbb7b480416d543a8db53aad77835051b8d1075a614228df8a22cfba4420eaee0ed739e0a388861f98c480847a56dbb9347bbb66759967d66dd5c5561e7b3ecce20f03544b6e26112122c694564b3b99d33f5d862aee19adb10f9827965bdc0dc20b0359f5d9efeb63b216cb97848ab29d8592aada6603ba8bb765a04588a7d12d2371285cc67dd0ccaba1994f5e7d09432a74c287da7509c43a5e9099937e2e5ee757b574d4664dfa8308512bc3dcbbec1234313066b82b29fee84d2aeedc664dbb729d4ae7925d1844dd8844dd895244a96306982631429384f18d1fc21aa029b3e71c23a98e4c4e536dae5d66aed9c4445483214e4d3ae3c87703ca442ed824451a2e0f829a55d59bb60ed825498c2143468b592829d873853205c0473207699ceba6511f52a0a365320272e43b0ceba8dd4ad7898cf4f0c5582565631e9686179a5bf285464e208a53f5a3427add2271a046be2a95821d98d4c987c4da2499484e3679276c54fd8f4994a60ce08600e77ed9c470485b3762f05da4c46cc6fb0029974cc6f745eba8282cd4d813050bb62a6acd2a45d31535ec1f1f1f449bb24517f503a9e06d12a38522a385229348a7c7552b7e26138fad81f22220984a32df594a196d2ae89e7b5b8412b908c0c118a459bc79dc0c6108978fec8c4d989a0707621acc9d9b96baf3de467cc0ebc5d21f3dd35afc87c9edfd65d2311cb1b93dd1d1f9e9f5ff522a6c139c6588e403d239cf434a4bff809f1c402e789e7d663c4b60989d1519f65376735b00d1bfdcd576ff5049b6f70bd37db9c77c6e8a8b8dfa3e12312cff7696f8169f717a14cd1f4577a8de1ee8e0f73df5e8a5e803942709fd7ddd99f248a34e235ed35fe06e670afd5dada44f235a45bf1b512459d1a69c46ebb3c8ba7f504de2ecb6bd30dc971c0e548a2d871b97c8d3a46401adbe314f0f6aef280359b9023123bc180cb219e3c2ed7e59b975d172f03303c0f4df4484270200ad810d08f4f920d4801dbe940143b19dca1463719f4bc2213affacbb277bce92f8bf15a58937d7e36cb0eef03e6c026604d06a4bfecab2ad86c63f6f8d3ae7e269f3d661702ad18225f36642b7bf69e09409d25228decd983c09cfaec9695651aec2f91a11530cc89f19d6101c76778052ef2016ba2574281a59f16e6481c815e0059fff76d51488d10b23e9b1b2d1cc297afc3160e6ef87d18c2a9514dd36ed68eb56b10d23baf1ca24a3a085f51dd717c37abd85ce5bbbb5b76f7dbc562d54a35cd93a95afd9c01f9f6f42224db07cd03148b46a9b577908cc921f98a40ddd23e65c888256356bb8c414c367901445dbb6402734ad7a25828da2350bf6eef9eb79fb4b34c3ab69bfb9b90eda637b7012d53936724e2ee7463ea8bcc26fad3eaddf1e1faaadd89aa5ddb75c272ed2594c7723fd5b541c25a6b44dd0f25686b51acd25ff472b482350835086f24fa2112c11aedd38b55fa2b42d26215aa8034a215ddd21eb58858682f2617a6eedcbb9b52f424d09378037959a61eddbb9b3c7131ede8bada5f7750248a44ae928d9c5d77ddd6759ce9ca1eddb97b24e22e1245a248d40151821608938e168e54fa5b81c166da59b4d3e935e1018135dadb2b62c4e207c01aed0d8034b45b1cf9eae92b587b378b4947775113f5500b7590943208f5aca160ed2ad4b38bfaf6ee66f324aee7def1982d63dd72086badb576edda738d978c754b2bddb66c31f568995d3e9352c6ba9631557733eadbfb2b538f0e0a0a0a6275d0bf604dcf90289bbeddc67d3ab76ddba5fc8c5ec422828dd3f2069f86252fbf22b27962b9f5909becb61a78db6672f4fc40298a5780a4f49192a02a32a931f793e906e74158634d3dba4bee9168bb7cf72c5f8a443ae43d12f176334a082bb5ff76e505ed59ac7432f5e0de3fed5a1263306707272f6f365dbe83e409650f65eaf32a9c5e5bd19f7695d755baa53d12c95707693fcb5ea2d3bbe7d34b9c49c7e91e899bb732b240640ce64088259376cd6bf4a57ec11e5a75c1669c78b84926b046bbf464ac0ada9b07f015616bbbf60e8239a66bcf1db475d0f6bce1dc41db4d5ecf0c8e2112f1e94726de4e0485b77bf81a71ae3d5077bb47223edd6c7aed4f33bd8b261c426a7fda8d446c12c2dd48e4366033ccb57463b61799b1898f48056bda7fb48bbb11a83fedda81c857ed56df56963deb5a0492af1b29960aabcaf3a7bd03a2b4a9bc2ef288c4231537946e698f22056b4d056b5d056bcf3642faf259eb201d7def8c2d27a4276dc2ba6ee20d1d9cc9a51f4c4fa85415db72d24cab1bd7954c27944da958562d2e5c582f30302ab1100925a882b3773225686394fee865221698762f5e94acd74919923f47df0e3326da9da6c6079b1e5a337498000f413c094400026f42c6031cd0800be405130c5880cc123114a0440260fc7841c0010cc0f2115380249080e169e97184112b1e2e3b08a0c3450ead081c3724112c03d8c12a1b5100430480ab91e9e40000e7e51262da9b1f800882466fa9193ae8f0090491400420d0c4031cd000204c3060014b28408904fc40c0010ce0a3004920c1d3e3082378ec20808e1c45e0b841c40076b00d010c11801a3a3900c079b984b80902881f6c68dc879b5935a7e921b678f874d861c60cea8577c5bdcbc8b8e27a8f797185cc0b19981857c07821f3f27205eb85c58ab902e685e5d272c5aa65e5c2e50a172dab16ed0aa9a1e48ae50a9586922cf18aa273de953f433f3f2b2ed8dc2175489d957e51e0b42bdd349bf44b26cd2926149be71905a33e81da05c4519f2f803a7349cdf38c0146d11cf509ebd78c4d9ff92304d5c351976f803a134a9671c0a86b45bfa6d0049e2516419ea516127896568adae55901eacc2a11789658c0a819a17ecd22083ccba1269ee59407a064f000ea4c2b0e78963ec02818475d2ee9d74c6ac0b36402e459366102f5e2a8cb11401dba8401cfb2041875a47ed1d8029e3b698967095300eaad45bb621c45a128f1dc65c028182afda2420978ee2a3f9edb0a043c371653daf5b204a843ab1ce0b99b8051324ffa458b0cf0dc413e9e5ba800a877939601d4a15692786e1a60948ba3de49fa45935a09cf732fe9816a9103d4c9961cf11cb7c0289651bfb2583ce2f11c9176a05cb20075322804788e5bc0a83e2a138a52723c472a45a0e20fa04e5605c7730c0246ad8e7a8cf52b2b8a40443cc72703403d2e8926803a99959de788028c9a47dd48bfb22478c5c6734c2200d487b48be5a85300ea684b220dfa219e211a30ea3d302775d479faa5c560518d6768a493f30ca18051d70175342834fdd28464eb06e7b9067ea1ce03f51c30273bcaf5cc03465d07a8a35589346810cfbf796659fac3b3c4a847a8a3598934e8519f8139f62814ea6e54b013cf213c63f2d521c916bd0e6fc55a1712831275ba28a8e79b9b76b10908051192805095a8d30945a00aa65c12048a9a287a8095a8d32d71c0cd4db922d9a26fc00532939898494ba20e6785014b30bdd002624bc41410831275b82a4a4011922f2e9600a11f420810aa1275382807b8b9e9966480221f4505b899454fad441d6e4912d48a7c6d4548dc5c796e0f9ab424ea6c568e581293af4dc8889b2b8fd88e1894a8b35521c0cdbdc57408e5102a42a84ad4d9a0e0a852245f35e94611114503b899459f59893adb929dcc0aa6cf92e4ab16e19b2509204b5a1275aa952196c4e4ab0a05205623a6138312756a951c2842f255630010c2117a09617aad4ad4a950a2ab0aa6f5e6661b043d105e27c54ad4a94b228da4b9e407cd0abe8d4c3a0d99741f6452cdcd8df35c4273575658540f485166dcfcd6fd78b83b5c1d2e12ea7651303dcb3d795dac13ea8430bd6ac62423a95b7293bca4a46e09a64fc128bd28b202a3e8455191154c6fdfc5e084aac0e0845e70429c50154c8f82e15eb8180705868bbd70312ec641c1f427992d26895b0293f49294c42dc1f42617b5a5c88a8ba296225491154c5f62692e9b5015179b50cb265405d3779dc52d06658b6db12d0605d3732baad5a46d4967c462d95b93b625124f2becc4591ed5222b5a11aaa88be83b161d62aa1bb7d08d5977aaa2ba6db39a865315a525cd25aaca35053ce923238b395a81a9b442a88a66af50959e5246a2e935ae312899a55d2097eed39ba314ad2e893a5c51a441cf62d94b9760fa4ef3a2944eca14d92a8a3a1d12200d7ada19b1baa14e09b086724598de4537b4053b656d578e4218a95db9e435965a486f7b14ea691925f9e4233ad1f71ad34c38ba1beaa47454ba456f85edaad06e68b5055bea8fd2e76e0853edf4dc15a40ea943ea9030fd4c49fec857372483248b65ed50fd199a42f415610abfbef53458246223151731627137245f5d17054cfb43453ce5c1093187029bbd4f857a089feff49c7e8505eba4e7f545e60ea5d9ae89218b666605650ce8f38667842ff9035b3b4cd971077a3add981d8de527ce5074d2154fc2f076ab177bbc18a55bf49a17a5f4372552e98f7e4a14b840c3f24a4e0a2bdfb3f4238186a2071d8c4030c290080845b8eb128b6668661b922d39ab1aba5ac34a615139ac624545bad9862a5264b1866215a9c65a6c6cb392e710a699694117612cd2905d95afed28168fb6231993da917c75b035278b0dd3c423de982905c739ef1c9a2f802f9c023ccf1a24f13ca940e279fe80e79309cc79037c01e9f13ca538e27982c088e769021ecf3396047358ac005fd167c7b3e402019e6518743c4b33e4789656aac01ccb03f88a41453c4b21e0789648b8f12c9d40c4b324c27d19047352af5834806799829d67f902fc2c6b60e3f46a98009e3b0d433cf71b02f02ca5a8f12c7d504b80af7ed251c8796e2b00e0b9b980f3dc58488139a5f75b06f0d5447d03d773f34088e716c2cd1b08e698e4005f9dd41d00e2b947f0c373a7c0e6b999e07ebc0273b8f763169ac673f402ee9805ee98064cf31c93604ef73dc72aadb8841ee20fe04b4e912d39f31c8372bcc10ecf510639b200f7a309e04b22c9968439f5bdc9881df09ea11c704336e03e05e06bc6644bca3cc3a317cfd00b1066a1af03bee6906ce58039da631e002c04ee1adcfd823b855b07f89a33700e611c590b70734de24a2470786e8e8181c8d736d4e3e67ac4cdd60868c4031af9c8d716db717325c0cd5687cf16c3f2397c2a521137571c37db1b4148589e889b63e03a650037d79d9b2dbe9955a760791bb108265f15480037d7216eb60180c9d78061a925e9dc5c736eb600b8f23837c7d0885e3757d7cd56887b730cf94e922fed896cc957206eb63f74d2132c6fd3491a8cc6cdd5879b6d0d9006c3f23437c7c05911927c6541b225dfc3cd75c6cdb67533ab08cb7f532a0f3757bbc3cd961584e5676e8e21339f39245fd448c6cd3158f766968d61198b318760cca11773c808cbcbdc1c03e73984a9906c1dc9c7dc1c83c5b242589e35abcbcdd5bab8d9b262e04c7f62742a1eceb293c964026be250178a88a62425c19a7895d7b8b35b6c0bc216b43dd9806415592585b49560f3d96049413228e535b6a73a650c464544528954026b62053a8250d093a3235813f1a9a425d515d49fead3525a8a4923d2d0a01d69465a5103c19a884b5ee38e7bd28421a1a02b57604dd4605a0cb498f6a3f9141579ddd52237201d194181b26541d914b229d95026147f604d7c97653e99930c288b653f5af468465b24211dc12939604d7c268405a2294333b0266e47b066cb02faec8093e2c91bb094698861f995166c3f4eaff16b3b2aad66b6216d888885c59cac5515366e472b22dbd5d463c61a6315db8f387adbd10f385e1271b0295b129643380868b28135b2ca865b12b48a63875853dedb119650671b8a34e433c5f1b51dc9960c02bee0a7b30d451d1b4823e6162c95806357a2719585a339e339242210a00ece6c9fdb677db74b6eedca514a794aa5ec31a5ac416ff0bc35fa8b9f9753c6a3a36ef3209647734abf1a896816610d6684b547ecb32ab2f57942c1dafc01d6bec2829de79ea70f912199a9c7f6796adab17d6ef3dddd46d22748d59292b0760867edafdee8b8ba82cd3e8f8ebad67e7cb77937ab2714b1dde09be150e4113d1e55d87eadb783f44b2221499884eb8461ed75abdda37684190e1d59d6142221084d3105aeef2998e0fa9e224aa795b80f6813e640eef1286a21374f6ad19f86eb9547fd59a11f44ca6a210b6eddb39447f21577b4cb2b5dad24994ae692e965db8dd96c90feb4576f487f5a7ec92918bb5a5a55b1f906c7570dc254a9bfeddc65d55eed11ced3676aa63ebcb6bf7883a329b6a96f4f7fda214dc9c823ac952e8fa416edea6b97483087bb76990698d35d7b29d60b678ad59ff65a392243f037538ffaac14a8842bd72baba2658a660000008000e314002030140c884442d160388f75cd9c0714800e8d9e4a785e9aa8499272cc18830c410600000000600000029204c8b9f83559839c30281351bfe6715eb7b5e1571165dbc2d9503d11a9b2a666aba525527057c34a2535666ecd4d2ec3e69c48743c49064071f652434b7895f0cd0e0f1e99a71f63e154e13f96f54a210a7db3c5a07dd8faf84fad796c17d0cc2c3d40e1e79be2b3f4f02d049040d49a187092f9bd593174dd333cf64b98e7f17247947c5b42c0c272b1f0f947189e57acb1510296bd8ff9b38fdad284af566c3526481eff0d436b8956e88f14c3c7af3a8eeb6c74b7d074ca13bf07bce6dea332126b4c7c54c49b7d214431894fd64f0cfb3fd74edf95b34d65b7528726c1d6c38068479890901180d765a6ea824cf31efacd143a45584f7d6706cc3da4b08e0720889a30cc80027602ca702f95a237e40eac0ce8f7544eb70f7f080075da697d5cfb70dfb4c211ab7498dc08c5989efec01d2357d697c6ac0061379aedfd707fad877584c6bb62a1b8530a7294311562a36437da73465bd6007514f9baddb61a449cf7337222f788995362fda231eb9e54878103b168650c32ce1411cd67298614694fd99cb08cf11404661c6ae2d453f6d899645ffb396d7e72cd0aaa91c03c03b8b2e582957063f882f5e27620ee60db64b10d11dca3d7a57d3b10e2de997cf817c06a17a7ffac09f3ad4997ac355025f8a98f02a5bef2f005c41fcd57b61030651acbd75b44873fb79402b3bae6676165103994094015cf8d16a74d222b84d84494db725b639fe471b134f0cd879550e56a97a76c2404ac47ca8c28336d5acd205036af41696cf6997ee7c79361529ae0f944b8e7046c944938fad804b77dfc6a0c7b05138ca178cfb5747cd5091706ceab6636c5c7d1184333215f11d01e0a8f019ca50273ecebb60b87b88a19046c364aa143cc687361e1c333c0c8a1fcb08519cf77d4467a9a581d693b2109fb6395d43d743ccca656bc3ab797897cac2cdf2ef4d163650b66105ff258a1d414d8e38baa0d939a87caf5a6ae36eb52e7096abb5473e0bfa96e5eb227ac8bf84a2dfa06806caa4b1441b1c762a9becdd4ed4fb7c6ce43263675f84accf904995b64c6ae045fe498de3dc1ff0251f828f82b922c213fdb76a6c0d9099fb0ad2276f72a30296b1d5f36b259e474e81bb264f053e81f729d406d9fdebd528883d78b333cbc8f0e0e49b302a33f54f9f70b4f9de8ffc270678ca5157a6fe31cf6b06e68440d7fe49e745f9f365bba5fc314a481ee4e7018ffe4799682ffb0d8411ca45f004f8afa07575348991c87036912ee6c06c93f17f126ac81ae35edbc5344d93b90ec32e66d2cb763c15ca674029f2b8b1cd40886093cfd4ed52be813e1d6829e599e13e09fb45e2187e3e7cb9ec7cd4ac73755c201b4eeeb1a292486e6834cb5478898f5a75aee78b8246575df2249dd152b8d94eb105e7dcf975a932d948c9628ff4e07087696c1e4898aa0c32d1a949de94f8f7e1574391aeb297e6a0cc81f2f877599b158706bd32cb65d610020c26720719d12c6b861cfd26dee5d8659d381b6d22c8ce5a56e085dc99ff22704c1c358e8f62435e2dc4fc31faf134d5cb7f969b766d02175de26844e2857d8930cce62aa4e6ddc5f1a47ec929127b6565ad33937d43a00aa6a207ac00a3dca545e60045f80324f73bb246eb755921fac1710581b0fa0fb5b1819092f00d718b749c886aacb0e4de6e26bd310ea3e7eb0da6f24c60f8f9144d6143fb3787e4d412dc42fa70a987c98d940d3058ff52a2ca6f260ceeb745edd374c6a4058baa869ab6b89f7e70310c02be008157bb77ca3aaaec175815b7fef29d7ed5d0e91e5cb7432255aa5894015dcc8ca409c709fd1cb3eb1c0e4f5cff5cf9294f8ab2dbc95be0215e302a54d5f3f7dae2a5f89d0066d1bfa0d4e90714f2251bc1448983294424c0002cf3f58e34fbb570578ee210768e1d92eae04b8193294bc5747e44dce610068e632c956c7a4de43517fb37855e0676bd1f72fa080d269ceee648a96c0bba9e0efbfaed90e885dd296c0743240a2c0c3fddcad577bbc40fe5ff59cb4c215eaa863ec08e22a66a5a04369f42e15fda5a3af24f497043da5a2bb84e8961a5da5a1bb14e891523674e7e1eb8a91957d473e124ecab94ce4dbdbcdeba15ab1a5ea7fbc6a6308bc4b630faf25cea83045ce500a17fa8b2cd39e104e10a56da74f1e2a0ce556a6991c3e208ad6a79a083d0465acd39f1a361c852cd39e104a18c576a74e1c2e0cc556a698183a248ad6a79b083f0005ecd39f183a18852cd39e104a18c576a76c78dbcec94a46d070f3969b68fb1816fe6010fefaeff2c3f564454c461f9bbce82929d3fc1689165f89ddde06fddde4d6f4823e6e74637a49af9bdd147de8df0d6e83bef4ed5637a467fa72c34dd3073ddceec6f4907eb777237aa1b71bb8017dd2db4ddd4acff4e7861ba60ffa71d34dd3233dddec96e841ef6e71fcfb15702b1c3a469de59ae069d0e0e0c6ad02ed4e067610441efa7880a875cb33256a351032ca8169004d6661af3a94dedc66c7b915ca410b5a9f04bd7648f470935f8ed6299946d03d1ff2ca81e9811b72bd5b4a59daa0b709d81b87412f37f9f16c9932d180bc4b3a70af1d20fddcef46247a89096fa92873366ec8f4ce613eaf1ce53316e104c6dada7a322cc15b651c518ea0ae21057000d02eb85b90340d3bf24a0ccd9b2ac64544759d990c183789db9cda5177138bdf5725002557f6bf85214afcb84e14754e235200323eb7849561ab54478c56941c2d08897cfd680ad3030b70cb19f73b9d91413322387ad6a0673e2f8fa8af0fdaa171576d29e3da0aa1036f35e4197116bd3ca49a35c23d16d1f3ac87ba2c540e411181be7894115b755c00e7a6453df8f6239f1fa4d18c11cc1c6cb6279fd78f186bf36e1bbf21d4518e951b97e70352a73315de8865cccbd9ee7c1b66397aa43c7f8df580e9ec8b3f2ecb725fe915b965bb8ec884e48da5bc192a93e6d31d23321aaf036dea8fb7f4887203f350427074a944885b56932223912f13e0c0d5041a075dbc4493eef9e78b14c433e9d7382dc25a85ee1b505c3f15141e4f35e9f4b7da7327b4308b39dc33110e40dddd8b03e0307503f02196a60718a77440c1672642953edcbf1e149ad53b3da2bfbe900b21c99a1ac100ac85d8563502ba3a1c06f50a5f6a99549cef542381aa55bd13797deaf54a89c0cd29ddf94d50df07e078a63902248e7326d92af86ab910949bf4645f7d709783f098052c106ceabcdbc808f3e44def5918a6d3aa6d10d9a7b82a11eacc80e86a9c037ca768d5f8e6a0d842fce0e7270f455f90f754ec52152dfec51f344a9054384cc3e3c1a98be1dde04c74924c91ae2ac6c7e1160990ae8161758122dd3361b06b7d6063f00a1551340df0b174339d066327ee531f619fba1c429485d60c884aaa24be1c28dfb645c36e7b808c016083136ce2e5771afa2808bbf9e660d4f877a1d88440e2c3071e453d21f75eec2a200dce4533b45606944226b899f56e17958b46f4e5c131f9f10a912cd21940733fbfba4d51e036a0ea2c1afa6453cca32aa91d82ecbcc0e4cbdb4b1522fb0b1d9cf4e5cca9ca0bc3625f4254d8421ca8378317f88fc6a7c6c02b9c3ac3441b8715371158b42bd056e166d08ce2039bb1944c063fa2322d033ba847e34ad0202014ef20300b94b15b2511df3a252bf40c3ff5a77b2ee5555e29a93ef716ca60b49c3fcb7fcf71fde393c687f246249813f403b41a2bf4f823b9481dd16e2aaec2d0c71256a2200dca311e1addbdc61b14c17ad4ac0ee6f39f723f44a64ec6d281d8b1e9fb0f4dd31a462d452b1e33abd4fdbd357ea438e270b4d23effa76c664b4f3663077553f5a93df59529123f82bae9c7a953e2e7fd7823d27687e42a20f91103e9e0b0d5147dbed3310360c50cdb522b9aae23afbe540f771f7441aeaaee24e410e51bcbb442cfc92061723aa8cec25fc83715f776f25399cbf5370056de405599471282940dc8bcbfa95badb2aaef8a23d1267f3d59e1cb384938773737e102b13134b6076434410bfe5a785f8783cedce2313710b27737e9db8afeb80d817b87b03e7ca9a3419738ad9a4f8ab91faf0287653597b61dd3a12bb0953cd374f809dd722f7d7bf70933e130aa50e2580748e5fec9e6475b38260a333ca583880508f3d33129ba34786ba1322334afc2b38756dc1b0b6bd04ca40ebf1fac4936b8f48cbfcfc0d3a5ee2f0dfa14e669c2629bd03e99f014cbad73fa80341a6e22a617e3198531d3a9651ed8ba48664bcad2f49bbf620df53af7d44d0a69eebfa2f60019b5cab63337109236020cbadd8296b628148623a0f9f22c0a32db369a604c4b40a2c71500c1003d1f705a081c24a68cf1e62ab0cf339621d76e7d0cf3226cfdc33e8bc045a554d9f5f157f118712f1a70471840e428cfe05b517bf6943e6742eb0224afd0cdfbe9a002dd141584baa30aa2fa765210bebc42e0dc340b6657cceebfc8a0ccef28b36ac092e1308d73170a60092138afd689690026f1bad78871b79b70f1b38afe8e463c7e6e5d8aa4cb6d63792e6095693c7b38b9f3a2d3001e6f26b007dac2b043f1fcb2a609b47a38cdd4405469fe7a2b321af6dd64b0cc488a0521e99fc89343b71928d08817b80897f870d42523bfb6d6581548766b3fdfe6ef614a811a4563d53c4ce554499287a1b49692e1bb3154a0ab0f3206a409a817f3a1ca14fb448cf313b87de01c293857321453a9a52b9d9ee0079732b9e2afa783f51ff2f79411706956ee66d3bdab8eaa6f6f665a0a40f9cd0a3921cdc5c127b629238629b4376a2c94dceaec002814d811794d549c26cfec12db60323cdaa936c99109b97780eb4532ca9c81c60bff64ebd91236e3a447426d94bca04c3089f1281d48ee2162640ce9a366a8dfbfe51ac8a7a928380b0652b662c5771d8d7482d079406bde15b76a3e4379512debadc02b80d6391d367f1245ce56be7427a38a100d1a6cb056b15d0d6ef0e7ec6da71687f8f957b94aebee3f362f8b2b9ef26561579e4555006eedcd6d97b12a22c0538560a761ad6c3bbe1a1557695e2c7336369bc9caf34e94b5d62a6b07eea2e2e2589666b345a9d3cb612835b994e7b0aea04b83e68acf4808c07e8a19d651b16664411d32ce1fe530be31ab8ce28e1c7e0ed73e50a6703e6fbc93f76c18e9ca90a1210c093b43e40be00617cad3b57b41039a5a70bd6040f793323d352efc10ee8caa7a8d8b93e93cdbce68f583c39205560bd07599379af257d65ed432ac92d4d7084809a6def7778293fe8bd9e1f2fbc4ca7ecf5197b3a3d9f5b9aae9a46ed83562227abb0b97aa3303e9ff1f62fd520c614c749a84276752d96430236cb3b729983393c961e2c17a5762141d1bec280f18e101f545241bbca2997de1a557d91c241b6011db66a87bcf77878f699cb9e035f4a5c67579eed9263ba4ca9a30bd1a5851a6fc943c5b27cccbdf842d67ae607f780abbac0e356c10031df670400288eb86a3b2eaf4341d48265ecf8f31becfc509d5b6e089a13dce25157a6fd80f236a040c008929c66a23c70fe8b045fd78aaf013f1a76112194bcafc140d33259be63ec7a9041417b108bd1fce82b39fda8c1fb3a849e946b7365cf849ecc7a086856f11cd002580658b0987f64f8126f0cd16492d63a1846e0e491b245fb3e9fb539d27d403bc7773060b1de73a9774c11b6a224972e4caa409f50a42e9b50e681fa28c884f96d3bfced1a1a921b4d63e2285d9644173b85cfb0dcd8ecc24b9b7d62508039d88f22c6b1fbdb2beb7d278e2f431fe6598ecb0e29607992d9b6b3a32447ed7148670b0af6511796c36476e34d20aeed884e9177faa9cc94383cd743e579b6a95a0cec5bec85d4edbb8ce6593357519a69535db66f888f3ccf7940698567a412f6969e1eebdd8a456bc9855d4b1b95039348c53ff60a8738a93ebe4baa41a99cedc4dfa5278688a41af2dcad6ebf72b550f6b4b5f9aa2c22b5e61db16aebe2e07f66019b7d7e7da20b7b245fab6271dc4eeead3e61f9f2fc9771090d08e1b5e01ba9c75ece47b6e9e0e7fb61ef4c937074c1dc9b6a6e1dab9f9c024fd9098089c75126deb40a03630f09beb74312f72596731406db52ddf527147d502b0ad9f76d1d7cfdd19f308747ffb7bf47f13d64caf81c63ef5635b6b0dd728928753c2159f182d28d7edb365bcf669fd37a303aab7015e02ece63729baf7724d80e889793edc230409904e2c8b75e048eb0dad72e6cfcb414a3f309220f916a1532416cbd7b6f89e514170ddd6b998bcfe9137047edcd468ed5f13bfb5ca5f5545a30a737eeeb8a0df643c03a51aac11a885fc55bc2cebc672ed3ecbe288fbfd910245bf5c554556002ab2a10253f5c24865fff0484038bb5e9c59fc134ea1b9777d421400b609b621c68d20249fbb2c6616b527f01e379cce475e3b263a3225a6c62e1fd81bfbce8b28072f5599c4772c3da72a140a1e0542f17f4f005a0d3c88184bf857a187f7b3b716d735dbce6d301351848397b098d9f67718847db3ee98640cbb4374a04c53d31060dcfce5c7be2d1d05e78c192c1f2c9328d43aa1a6d9864502f9fb486d23a8fb3ebdf0bbbfce72ab0e3e31c4b093c29c58329b701a0ead5d745625c804a83ea0b5204b6864c1b23001b11e93e2255c78db0fcc94bf272d9b2380f1edee89e59a8861415ba780f97b3a3fdd1b401a7846c1c3ea59367b2c5284f8cb35ef3592248fb2bef5185caa3e937ea28923a5fdaabbd7d15234f1a2c5181c1890f2565c3dc0d39bf27033247c89cbc463b4f10561013b1febde93291b01800bb63197a442320e2d7dbca4af8a70dc48875c2febd0fe3945f62cefe595b11381ddb531fe1a701a8f87c3b3a6d202e6c46a596383423341ab1543573377131ccc9a71337805c48e266a9c0c46fc7b5ff1f5f8e791ca12c30281fe7ba726032a0d2b22aace052e9f52344cf67fdda5ace70a6385c52d1e4d42e475ee8c89f1971c028cf09279dc58b2b49117271c1468e8e92bc1079c26724722ff5fde9c4c4af830afb7a09653df14fee61127fa256e25282bb804361d3ce83e629289a1313d2ecaf1cb806340e06a90d3c0ff3034bc19a1db7f966a0aa1711e3161c72cf954c4986f3db7b608fd0fa2892b914d6410c497e73351894d7432b232f44bf0efbff386d75a26c9c94a69a25ceb6d5e867d430b38504500a79a3afb80ff24879add9d2a7ab0bf42158f19a970077f9c9070f01923b3a3b1f346c9d6d1a8edece99358b74aa62174530a7661cd0942bf3bdef36d364096b95406bc232d36b6b4664426bf1a7bb05ba8370d44ef6af8083e138003f77b088e3267f172061ca2a3b879bf98df314b0c112870a236f42782bd679c452587f710465ffcc22cad2828c0f17f195a1097981c2896e86207ef74ef0d50adb8160bb41b786416c16a0ad118364d278d0f199fb7e692cc4aad2d64a5105981a900744e2e49af11e9b1657022ef37bae8f6cb94c6a002a426416e804a4744409a6399444b5202e234017b6fe341deb27eeb5b31db9811a2a89e7b6869043fcc5287c582e02dd41cfa0fb0f5f4315112af12963cf1e056cfff171a335ee4c66b27b17435b6b118e88daaf43409eb196f6f2396354b2c0e9e08533c6d89952ca71e70ee04a9ad25dd63c77d54bafe360d15a142706926563c6859a24da4cdc9c132872d7d3712400e6ef0b71871283ed13889514d465d1fa44e1747560521c267a0c3591dd444cb2edd717f233b45a9b2c713cadc97e84a4c85bc69c2bf89d90d9e8b436478512dca857555332a31d8341c527277318f7a5d626f51cd020790dc813e250c2b9bae95b9edba584511dc86d5c449c8b603444d77269078a83aca5efb88ce81be98e55686295697a7371ed653c3edf3bea17b5db5c0303a9f87618fa251a173a909e206d36bb6986dca864cf7e14a75a4b730466848c17c9fbfda8c4e5a07f2998cda1e868618f5b5b66ba545155e3d15ddb6d34951f788b58b86462680f82b931d5d04668a8db106de506cd5d41f83d6c5fc019996653647f7d9a720b4ca96c76226822947d0463d26511e30c7ae8f577538410b950825e5d4745d83bff057086a37b7ce5dbff26e952f7228e631c5f1fad01c831adebe16fde8b3d9da5ff0567ef5e3f3c814cfd97c3c1dd82f1edd31c6b3b14d9eb5fd23cf3293862b7e658d441fa6764827598d38da752af7842f9556c2d7d635b4282cdf6942c59622eda4aa70c92a0ad55524e5416703d9809e654d1ed063d543015bc08498ecc07f4c92469931e664fbd071b06eadb746053415471ee9b019c0481fc198fabd32c66f6772a6408b320361cc69ccceca1c530ef34ca6e64d5b6853d7c4e7dc8d0aa7bd0de025f0d07434c4e4986d6930f936278d8f3c42124f8c61a8458f09a83ceeea8e1e4bca6075359681af46965ddf145329394972814ae8bac464b42e3a43d0018f6f81f953244694e48c9c87fa607a6162125cc3540ef138b7bb0eb9bbd9fa64c4d8d26cd74ade2b4e9c8f5315b19dadc7593341d7307177682f6349f21f315198c862cc0f88061493258c4f529d28dc49823c31acc70515050497551d9cb43156b09f57cd12d762a1d0d84907048ed069a13a70c342d90551b9719164f82fb1c236bb5205f85eb9c9c2707a48a9eac4eca7b4040122090e77310f37b2d9abebfbf11c389665ff46cf278fef5d82ce5c0b8967e987af00217e6e651e9034164794cfe4ebc7a428b97c42cd7b75f2ac44e7938ac9459c7ad83aa91444bded9025015407a2cc616dae049fb21c73613d0c7351d315ac0cd46d1343df705ffe5a9ebbaaa1b2d467c50877ff14ed1c698cee93d748e3314f34d1be5542e3b840c9588947f8673e67d069b1f60fa1495fc20f25a56dea6466436846bb4bcea001db350dc14de998317c12ecc205d9748e0b490d6912ade9c578dfd5c51a04570c8078b28db2c3e4d195764c519afc0a0123d87e9b94bf25170d11116a164c79e6198d5b48995f243b490d38a22d1d6ee37f4e3ee34a69df80ff014b87555d46470a07285552c47e7a9845beaa522ea0962b622e7d4932a6e5b5b59452af2ac48f9072dedc87825b3cb8c41a76b7cad73244783cc01365b81770433b4b0acbded3486d773168803aaa230aeeff7c29be082f7157007f0d2c53e6aacd3fff7b046cacd1f2ef28e7d843def8da20c7637d3c4df3363f57a5d2930368f633a51d0a97858e11d3e8daa4de7bfd40f276272b4908cd5d19fb81724bbad7f4a489ba15f4b05974af48134bdc0be2f30599bce800a7f46a2d350975c17a1a7685258d760ca3d769d90dc84282d5a026771a57d4e844162708c416d220eeef2a0cc28d94743eb07e807faafe9f14130b4615fe82226b47b4b2b86a2cb543c54fba81ba3f5c12fa47c050a68571c17f70613508e878f8f555e557748214d7abee47843ca28e5785dda9bdad2597a7559590041114912996e78432863e89691393d58ea70a381a8d93dbf4e27fa2ca07cc99544d2ad93b467e617b287396692a3b7c2d81e7cbc52735b0a8d7559ea341397b23a8e6427411ec1f3ebb44deb6098803ed2e39107b30d61675961e0664a11850a8f04886a41a9287a6634ddac0190a29296e613fe645973ecc1f6b581b588d0c3952ddaee09850d5ba56386ad96290017f9d4b4c623f83b69ed023127feea4d94e4906daa694f9ba420c3bbeaa64727e474f0a892df5397644f05846a09c43da1d1aeb66c0bfb74a6932241feb1192099416b393c93a3be681ac8b4f78bcbbb75708bd8dcf23f84ba47656eb398f95c2d7f8ac84ccfc6a85247d20e722d74fc9c6fd315a2d9cd4be6969721e11f0c64e85a574d7fcc1095931239699e1542334a2765939f635b152e41bb5efc6089a32d73e6db3baa202674c79b2fca72ea19617497d21211b6aa335e00f985739bfcee47b1a62b48811974b87682885827d8a4a625668c8f1f94ed2c90df54ad618fd789b1fbfbc7211eede82891f21de87e65fb2b6f2ca965425d3f039094f4702f7a1909a9905fd63c93235bb71666acf00b236a9cbd5ba740b20f1cb59e46237346bde5ed98965440f4a542c3d35bbd3d0aba86b9b593a7edfff9a078664bb1595abbf0b28bac34ace9080d41b3ab164953c3008c8b34a5f56dd2a5c5e4ba359253b16b6a5eb685b728c9d2f34c06202d4652581fe671aba732ba851df561770c64a935b058dce41126fc734ba06f052ab43397e6f5f0964ccf50b38821fe1f94e4271fc3b604dbd4becf88e7aa100d56a3ef0e63f5c5f8a213dcbdd2fb46b9acff9883eaac7b09a01ccc7dc6b4301f038dcb5204f9fa2e464c1e6199d72e0e95c4462ce8c0de0dabc3f54e8f065d102a458586aff34de0ae8b38a641a5c0669665010c574b46baa0b14e71826e726b63af1510eb8bd04d8f543c11f0b8cb9bd3308fd076a8c42b8227a3422b7680265b25b3411f7f126ab5e905de98e8168acd05087f69383bc09eb4f92e3024b6d71f0160477a1309eb15b49ade12c3ce50f14b2229c40b7a20eddd9211d73518e528293c3cde00f3831d73db318a9cb86d9d82c347375a3f207c670e22ea3ade20b337f4473d2c523ced5ef33b2197169755a43f00743512aa9b96e33a92c17348df00c31258691ec39e83d34787505c815486f01cfc94689d93e9c333471206171a75d9e56797fa046e7039c072c8f7312b0966f63fba14b070e7290c5d31b89374a392a57910fbbd7508058f7b92ecdec4a3dc7726ffa47420a315fba1e509982372bbc1c4d76f053cb4dc1feda1216fffdfcfe4f8234adc03fb5aa2c604c0629b87e04573003022cb9d2f5530606630c5a6c0d052f8d2c0424ec9fe4be32f43546f633f981e99be8340d67037a229dd9e241f5477df06d0a607a965b7be90d581d00a63ed23cbb48253b9706b918f338636faef1df8cf630ea844452528c6d4136d9616d5ae49ca21e8cd6254fe48234e1813eb39aa9fca045fe2ea453fa6bfa1e3abfa6e9191c0708dd00cf08e063dc7fdcb329eeaf02d45b1e7be085d5fe0aeb114a21a1144652baf5213044f911100593c3a22a5855f3bed4b4a53ab84ba81d51e50e7752d8d8b63ec72c3b6cd6e738abb8877d8eb7260668e8dbcc5dff27b13e747e7c886f8c116ec68c5d4f1e0ad43ac6aa5d7c5cc4ee8733a62bb668e6c78de79a5b812dcd04bee8fe6df8aa68c4d64fdae8a5a5e517217a61f01654c214c7d03a7808585af39bca227310c89e502f44012c8a2592dfc69a63199ce823410477cf03b5f2ee48e3625665c21526992b067e02e255f08b3d76907c2041fadbf9621048678b1689e66cc6067333b4e783f505c158c437df079034895e4013ea34664d5857871e28fb6d0f2986dde943f9b10817f82a31e456ce7da6eb93d222663014c9b90a006cfa3cc69dea93230cf27f1e6ffee3aa79dadf1d6160dfa4f9d8350d0da11910e147864efc64152fe104f952f6232853f0b7d48c9f641adc0a443b924f6b918b685bb2266d247b701913002966247182d73001960a420062f6ece5e735f67b28b49575219205434473d380dcd37409a3498d6803a2d8c57f0cab113fd4b0dbea44d2dabf3a747057eac68d7f26b31b5e21a9ebf5bb5ac33df913ebf38c9009d779f8132e9482d232173cacd8217ee269dfab53dd4ea0cc74557a30204ffb7f83a89f60d0a608a10d9bd844b88e28e2422d362ab80a03fddb9e5623c8cf57752ec1fa2e482f2085cd220b3dde2894c49d5fe254233a1e0b6c58896cc67de1ce51062808b4b360a43296992ade9ceecbc1b370506fe9b729cf3da032d7a882e846cfd43c4025d750f36647c7553b0164f812a9b65b8fc2450af6906ec1e1d965ef8b4bedbb9f2f5acf43c97c01532a87adb7d2784beaae9c42feac8b2cd13ade040a64fa06800b0c02881b73d11f92f9fb28629461a417553e785f78bb09932f865d143d0df16ff0247966fb2ea75ade3bbcf4dc772ffc78329738b1536e22a82f5bf065ea4c9c9936cbe4ce26a774a4abc3923d582240d43ab09ab0717af1353a90a7fa438288f8b754915fe6223f7c456682a1019d9d7e4f192f78c33eb13337c90ad55df40aba0f596a5780a191f433d522d889c99342e34cb7095c01083fcd8853c0381dcca7165e6861a986adef94e5a30124581e500a66656328e2e2f1d880d2e4c2ae7140db41b02fb32c402870886b688eb896086c6deb116bd587dadc1d69aadf35166847af8420490f5d56f2b0d25a159952e1b1e725811869f7bbaf9a99571de1aa83b6b586953d0897da616ca9c8a1cf655fd78ad19fd9bd8a64b946f284452f91faedafb15bc8998f04a4a8866093c574f0a9e95ff8724ca3edba3a303792ef811e7e7fa7dacbd3cee96facfaf0f9911eaf37659622156c9c53ffafab789fc3df968d69a9791482df9008485f63708608e9a8a6762f9d30e1f7a93cbe39663bdfe5b5b6b36ae661c21b453577bebd627bb2e9118268993ea1baa1de26baad0b13fe0071d16636293cb15e631ec980fac99fdd615fcd58d8c04f5edfd03aecff5ce1273dfa807bcdbf62bb31a1a8bc0136965badb3ea471c55ba065209802f9f224ba157caddd763453767f468796502760f0ebc764c4d1181531e06d4a4f7a8566ddd669703731670fa9097501608c97817cd415483145800585f24ad0226388a429ac914311d93a958af87b4ff612ebf99d9e81cee7b291b10c7b8ee41b5b5476994e28f0ab8a28b6e65e45e456fcf906f130a138ebd1a19138d484115d22b787daf66da628e951b7b269305940a673ac0b2e8e5fd418bd7fb545dea9316afe7281ab5f8fa61c9b34b2cf26c8481f20d1b8ca13b5cfd54e902b20285570309bcff38aa65d57483c74acfba99f94ef2e308154800b94a4076022f44cd3901c495f8ff948720828ed818212db98b5ba8e9b8bb6c6ac770731c539ea0d1c0e10b3aaaaa7e3fccaf7c2063e07eccb2aa67ee9333e5a2f03a0f5b5d6e169d821c71946bc9916c68e4a006de1eb0e5c7d7fb216535d17dd3098aae5405363fef649e273246605df510065b42f09112cfa7ccec7b669d3355948c140011e48acc02edf7309aea70920b4bea44203b6f466e6ab1769d262427387a13697b9a0e2580388a530b5a2ee39d82bafcbf9ff0d98a21f40697044367ecb5b56e39205afb79c1a70ef6bca9af72a00c826d4e0980220641524785271aad9d964c579177224bb8713d3f8d7aa49b5b799e03de44574430948c26ba0cd0df50102467fbbd12d0b33ef8b54f0b8ff4497e4b51180ae59247c503f4cbba7e2de8a18fa1472e7a37104576ea884d251b49f44215448115bfed0291340bf74f4c77be12e07bc9e59965b14f1badda40f5eb40bc6fbe0db2d2dfe0808cedfabcd5c54c22db81e6573001cc7d1f482c4a1c5ef8360335b3f609b837c789844bcad8a9f91d9f2a0c8697d5b3d84dad4da719214bfd7120b219197caf31799784bdbdb46d2f52ab5f31b0177aea266191b702e5733b0804e06ba0fa02919134725c3316f11f2522919e3db78ee9678d765e31409a6bcbe40b16aa39ffc01426b40b0d85ad4024f6f9f161644bfaf2eff04f2ff26e4515650902425355b1c02351563ad405ec38e590c579dd1a606d8a88c5b60c49ebea818b8d7301474f679aa42236afc5903962b97f999afccb123ac9672ed91e88dca66a6ccd705b9f0c615847c2b42c03a4e2f6cbd9834f3b6f4ba5e7a116bc1c0c0845de4d3d49961e57d15bf71caef2af40a298aec203579ca596ed0472f8e8841cc3fce3a8c3a08793c8f36acc195766d5ee0bf3e8593fe4d2c1310e636a27b04cc01e12d48a28aebe30d94091492d477a73d553f4c7c5f54800c70c52d0989bf31a20a30be043bb7719cd16fcc3e3098e1b05e1f356d310ff9735ed62961d8ba4354139a6a908ce3d35d81c5ace414ea855f95adfb5fc9c43ab3eb2a495345b0c8363a398ee1e84ee0fade81ff6b1f0215e991a0152d35d3dbfba4cb9c664142d94aa59bb02c319dc82b05722562bc4875cb9f0dc9091774743d76080fdcd8834a95fc3d0ecfc19bb6cba76a213c94551ecbe34bfca7bd559193a098dd6113c7d9c78c86fce0c111565b37752307a64b5e0337ae8d61ccacf721b4a809e3715039568ba9ec6d41c30948f2a23267dce761e0d43e6699a1eef151be621f48692cfe3808c49eb091ac5be1d1fc95f39196991874d1691546079f91ad4ac0b01a6b277b92764c64873392018cd97a1e81591ec15bdd17ae35d6c01b9884600a68f3acfa9248f334bcb9f5824df0139c92199c2ec7008f0010cacf96ffc6e0b1a6e06bf567ee1beee238b39b7a820e61346c372a051d30131aec68f2841c8b24787c25ca23dc82e6a1549db17d755cbc72be6abc1b293f8f8a8795f04494755ad3eab672fc9172b370b9a110f8a7175928827d4f41da0dbdeddb298d89c96a6e5e344c3b2ec9729f6829fe7bfaacf0676f5db740cc4ed7421355f515325344e6829c2cf6c7398d43e00f59c8877b819dd033c0f853e827ea3d7f62989b068a6a6751daa0f456262ca084a763ed47f9599606a1214cfc649d258db7a30331bc73299a0ee4889636745143948ba40a8acf62c2a2b591b63b8f1a3d76c040bab9f3101841040a8341c3668c05505232b1a9af71c2bb50f99836e4dfd38bc4ab944700dd8e420b561e8e16cba497f49b759ec76c1cd211ee96a6619074e9d8b508dbc8178d8c1836aad447835d14f752f21b0210632a833a9d01e06dfa86927db5d49293dcee1681fca999f88b6cf0758cc208b8a41f315599ae84d4193e26ceb7d02d91f19f0dc00eb7d7197fe80033990655104b58f1e560ad957669ac233ded514f1830539a1d9151a44d3f7f47bb7da221632ec2f03004d94017c0cfdbbf13b9834d4ab71e24f57ccfeec74909d24daf1f8e7f6de1e3a1774afae450742583726205ab29b833a22e31d421e9b1638558bc7430000d8c2a227b5a776e1dc35db2fa990dcf32bdbdccbe8399dcda5599c4c69fb18f1d9ae76693e704fa36abe2b0f127bf9ce873038d3e693ff85e88fa1340acb35966601ca3384e70c0be3784b17c00cca05d50c85f48393143c457767b443a8704ef878abdddf498a4978cf138c73d097838c701398696e0338c99417b31f17ac19f82dd0cf83bcfd09236e8419969ba4f678d45d07da770424eedd3032f7ade648d8db88d48fae886043b62d51423bb580677805f8128583dafe5413481db64a54848962f094f285023b0a41b8aef696ba1aa3916e6851779dfc45780df0643e3be54fcabfaa48545b66c84cb74bb4a2932317180faea7dd45c1890ba278766c91a5740a529170c516245146eb6d524fe8109057c168d18b21b5f9a49b10e2dbe05d7a52d6fa429f0b4edd5c5fe60450f51c19334d6eb7733f8421038b7e14e4b113acdfe7fe97e3877ac375ecc1daaf11c2161f5c09b55b4c610e3281c47ded834e3de1b01c8f22409b89373b413c8ccaf63fb609b63ecc01f78929c047977301e7816a3d6684392622a6b6962cf525976710d22eb0b4f39a353100a712cc496bcfde4d1fc28f21f926ab5700a9652e9881d19b696f25bdcd3a273a15a717f319b03ae012293d2d8c3de13c290ed84f59aedb22aa9187073df32aa09d3cea479dfdd407516260c6d13109c019b696a57b520aa880160afcb4077542da2ec796add2c855e411c9a41c1a3d02e2dd47e1290b9e8ccf14d92c10c145313dd236db575827df15a251c605ca428e46d3b04f9473c50b1c4d894c32faa10ef787d864b57e62ba8beff540388a29ffd8b0513f583117a79a0b67dcb87370bf47c05c03c38efeb800f8d4999e1ca329a01f0036511950d1cd3e5fd0ca52cc7f1f4f4db60195a088ec731e678f03043918cd9cd0ac13f61b99593d9b88920d571422072ff72f19dad6df0f6c2a9e6728a5b3b851104cfb9c0a06150b70cc91bbd878e014aa12f7fd96bb368032c625ac5b1f542efd49b04cae1ec293154cc289d14e21582919f9cb804dbc0427a3a8f047412661c9eb7ce03f05bde1482fe9ea1108d5ffbb96e814c1dda42dcd8233e0e705c4ecae687428c73ffd478f518fe8f3c548ad156b8299fd64651b827d42dfd7fc7347dd8d476dbf4215b18e4ab23d46d0ef3f5cc3d1453c6761404348b82b6dc8e28005eca3b8aa19bfac000d38b617f152b84c1e6cece43fec373a347af3fcab591d2619cf5666561716a7008b8bb3a714605443fbabe023bf0a3a1a8fff0b6793634a16c76a999f6e047b15d59fed67c5b54ebd217e55cc393197ca21eb6d6269bec786fae1d44b898871a2c38e00dc06a3b1514156486ca6be8b2f0234d24e8e99c4c822049ad0cc7acedffd93d017b8d314296a3f237524e81a58b3d92c3312acca48a4d4cb141de7db292f6de671ade18c65c171c908dcc05601dcfd25d68b539510c2b6bfae987955eee20ef505620d02f8682c2a0b15ea0ed04ce53708ac16c96580d106a5cdb734a3c49d2b4f57a2964329e9310672650d30e291a2f83cf28b844f202476e5af77acd9d71fcf7a60000ad83354012c7a391618b09168afa27ea6d5b0e78972afaf5b015f3c5b634d37213e870515d0b87f97779c012e97cc282340b4f932a216c04274b7db4de01cafa9b20f112359ea827129ba328800ec1c384dbe6949db7e94eac0baab0eb07f1acedfa91bbe28809d4749bfd964803c5e1c4fba75fe8cf0e761812e78b2f81871d2ed6bc127853321a57c5adc9c189fcbe34e631c31b4a2d60bd64cd97b2ce2d68141a1b9e64a25957fd0070c0f4966b214a52f6c7651b88edd922a775e556dbb313135d52d15f93775deff6eef4bae9a934981fa59a6c7b48b5fbb6483627d0b990e559fa695a97dbf22e4a3e1503a4b87802cf0adf9a4b5e20e9ee817142c030cc6497a2cb24eec35139093bc3d7e2eb1b7785bd1e9ac83a25f2ae9c24263c8412fc164fca9c93c2eaa4efa8972c7805faaebc8e664616d9c7e1a5b531209541e5fae436748959ae059e86d1411d4a4749079a2e6730c7421e713840269140cda6ad515aeecefd6435df65bfc2f0ff9249252dfaa18626e524fb3cf5ee8903caa68ef25f669734e5694ae2b9ab4995e132bd38b77e7189eea1fc1863ed849cbaf06f56562b49ef09049822a0f343fa1fe128697aa61019b0ad52eca89eb02f36bb8c7d602738a2665786c04bf9fabd5116bd6f94b9013eddf1c11c63e6933c1c31f0e49efa4b928cf52ed59fca12f2722d2c06995fffa5bafafb2bcf5fa91e3921ee8008ba0937b0d5118fbfb6446457c4f7546bb02b57c65b3ca0cd401974f138b996d981abb6193859df2713368e67c0fe6d8439d1f05025efc5e7a77177efc542e8efd9c5e549e148b3c91f8d41c7c87c5d77e778539e8ab99829f3c614f28d7e07bf50b319c4b9f0030433d6ec00c3d8fce1159855331d967604b945d39cb5ef890cb5ea04530234440ce24d414cb5e87bc57602bcdebd978a07201e5a2c96bbfa352aa503094719619c63ec8965c0480e8eaa1781a5285a976be23e4ffd4850ee90b43345c49e54f435d7e4b3c9e69bdf8f1d2e40ba4687342e4d536a943169fc0a7335fa1c4bb0c830c11d59df19f61c09312ac4305d63434a530c7a342159bf1051849fe0b78866fee923f0001787c36d697f1a733f8619e78a6a559b507e3cca91e7ab987970a383b28c62d4a8e482ad4597d34659cd7c2663a92fdc94b2f0b13f869354ab3ef48e6665f41a09bf4ee0a9c7988c6bb3c4447c2c66a1d8138454f334c49d88f074529a19b69aaa99c56eb24eba47e5f88472d05a181569e1222ce14d2bbfc6ef24cbb0a339c38ca0f3e66eacf82328ebdbedc0234f0a321c583ed40ec24cc44d8a95c2d35095dadf75b9ac010e88acfdf2485bfa79633ad11598bbf456856e71125a8accbe64d2522a3d4625a9c63ec8647a83c1ea5bbb3c6197a5d129a71aaa085305396d0ef70e62a4eb8b6ac31c17a78fb2776386f34d7c38cc5ad341d20819eeccc772284345d7a659792879bbf445393d5e20d683efc67fe5373a90cbedb253284f07c1bc08f53ff1422b206cc7c5f02a5af19673a0e222abc988d82de11f779b81025ca9a38e13e0140d5afa22c09e682fadddbf76db3115a8944be5c3b9b9bc288b70e546051df0f3dadeb20e6eafa40c60f4a52c5166e6bef55969a86008a9c8fa40626fbc996969412ca39d0f5cf6ff19e30f43d4d5ab1baa2f17b45e10fcf4f687c1ffa99c80388befb567a9d28c74ae1abc25153d3e98491eeea107bcc0baf93890ff623af69f8ae5c477709f1338754c689a64c1aea70c6d74511cd26ec0779692c69d71629f5b2342261a94af126cc268819fd255513cfad0cf2b88455f35739850818378119f4c1be3ed3ababac2627065ad5b8a284ac03e45cf93ae2c2bdc96061becbd11d279c28f606e619c79cdc6dec75cad7e79dbfab84134464dbe4a8ba95b68d0d944986e0d7a426981c1465b188053fcb4c0e1fb83107a8702bc2fb4e684b361f46476c1c79689abcef5c460cc3d65bb2150cdb235bcec41f61995401c698428de04cdce66bc069ffea780e353768dc0ec74aded55a40b89737b9a83a6c1e5c78a7b0a02c373f3effc83e2a382a402c9495f56c59009c09089036021e72cfc341ab890e9f21b27d445a51123e823562d3b780e06c207f2d07116be8bef1faded7cf57a5d359faf7ddf0a6aff7fb7c5308947e3e7a772136b38b12a5728f68f702994cb2685f84b46f5c300e1ce7c3733b66e639a7ae4d78d5c2385aeaf354f1c71b805ef0cc239229cb2639402810bd9015f24d6223e4ac37397c50f048f7c10ae2819025d6794a3ddc9b69a5fcabf8d7696f7303750a176f270144a40d71d05eb22ac2250785d8e1372076d7cb3a0e896e60323cc59fdcda798a9ed58cdd500350d0e410667eb105ac539dc90703d3b77a4829c4b4c6a4c0531a0968715cc19fe95abdc6beb5a24e910e6f014a41debf255bbce41dabc8c747e27a7f53693400a60d0d408e728a7dae76235e08b092c8547743d312e1a45e44d88615cc4f8f66f815ddc08b324fa34f0d152b13aa4ee92abdae66951f31153d5e4ab6fd1ccb4f457b5aa808b0275881905633a6b682bcf0c8c382bf78a0d81934ea07ccac46b37b2ba5dedaca5f4283d458aad3e8c0e95112e74873f81ebb51fcd7d52b0c63cbe2cce7d11fa57fc378498c33cdfff210a2ac9119644de6523d6cc784f19488d4db0af995f384a27de507792c15036c9957f2890e593609ce65f1456058a47692210be16fd15fd1c4dc3329623635564dea736753d2244e3eaac3962785ca39f1836cf983897e880127824d842f34c2d969d5eaec9404f7d06497dbb3ea3e46d246f3ab2382fc128ea43d010a62215103cec1c9c583109b56873908d9e7201d5802df4e31617926905900a3682d51832b6be93506279035e2e42e45b706a5f53d467826502b13fefecc804d969e606960585abab6fb1cd4690b794edeada3219fd98613239ce1b9e1c20b0b0d8adc0198bf0ee3de7c22a165ed90d7a2bcf83317ab607801870fe0708ed72a5a3c5bc81e9a6ec3ebe69b3b77c3af262559b76fea2c214830895505711d6de07b3282e97a5be7cd7b6713d1d40129d358e950f736f63f9ce953bb15c87cf0cee2571fe74c72fd16be87f4fe5127badb433579e35a21d0882bb449580505555d0ec89785d31ccea1f3901f2ab8ad4d4731c918b99466dd6efacd147f3028f2f257a4732ada16697ad88b3f1bb8c85c75c93b9d58c072f3cdefd596c42a1fc9140c91552316c1bc8f66e8c7a735872eae00a276e79d6d76abad324ceb62654b62e4daf93e3c5783b282303b451262d827815059d8c611aa50e1ee8cfe57d8d695798262106e62e28c384b8898aa3a6265658daa89e964498b1efacef6bca40fdd4ea97c42e9b59b22ae46d7d22a8b6f4b62e0f4234db40bde730d6ad497aa1beed5f3c98695bc3fb625b15b241438875774ed889cb89aa21727932da98261ff03eb81d98cf2a334186a420e15a58afb2cdabc25c8249384d8ea21b492a2dbf95e0df8eb04f940d1aa294eabac9ca9c59f99d62bf10dc5a6378e9c9823108a8185d5f65623be53f92bb2e1eccb4c3a2ddbcaf3f798863f79c4f57cdec737fc4002c17e70e5c9675b12cbc373b8f6caa42242db864b1f11b0a91de91f725789092db0f68f8fb041c4e83ee98711d9c402d39770dc0d55faf4a304a77da64b81076884feb689f90aea066b906f5a751fdd48e82791227722437509c2ccb4dfb425b11b405930d05b0936ac9fef4ccbe8bf178d637f1e586d3bbbaea29ad826dd455d9ca828fc0aa96af58ee27d24e648ae85e6503b6c87afc3fb5b4f178c26c1b7503c04fd33ea65f49326ffe2a31b8589e01755698ae22a6438aa27d9f12e223717725375544f5f128723a841b54ffdc530f1e3930e2dff77314391ab6b399145e08dd2fa07adec266393816fae65d5f213c4bd9874424a72538644ca9aef9a8105c1cadf49c9780c2a8ab39c9a40b27e2da28f710ae40e3ccaef427b99f909e2d80a148aaaf5fd1f46bdd47c0fc7fe6129f9d98c0764cc28f9971d87c7082922a3173ba87317e6c68a1a2f1f5efeebd22005f061e1cac77f4c7aaa12b1e60a4aa4bb12712670bb1875bbff5d55dac245cc314a84d9b7f6bd593f9534808916c928730b6848f09edc7a931e7b3af815c24805b3ce9b446b3b2bf67428ae7e32eabb3faf257e2ec2ebd4a554044ab61365a98429e8a7ae1b873cb080f42414c6d56e80e22f20c41d0454618cffb0c5968f6633656171141e9f41cbf45050b8ede0d6e240322c8f19a446fd5b47442b93138e22649253ea8c5c6d416350f4190b727ed7febf10ce194adf352f74949dac49917d31063f2e492a37218992b39f966c5754d27b2ce5c31b3ab436765171513fb7aab908efb2f69ee2c6f7d4ff0bbd47ff12a5f2d4d0b1704516b39f93a21708cf9622533a423f0ba78e98be15a54e44d644b2b1ae055f5e31a0cbf928cdeb954ab16b3a154a9e35e289c04b7391a1fcbc3897b30e2128f1357a8f3174fffea5d219513bfb8a184f178745b5b2c8de8d5deeb7d168189c2b8f8b13016d9d59226f0fec426f1d50d17765b51cd265dec736c886d29747bca9036bf1367475d72bc2a9dc003660a15f8e45f230c49a10989333b9939e445fd4dd6300ae8045e9e8deb6f19b8560ff59273c004a54f939951cd891760bfef41c41db162cda8a8f029d965d3adad2e2a2bd77beb08630bdb4fc7543fd6cbae8b9585b9e9b654e9baaa3d1e3b6cf67da996c681ae2668fc96b2069d6e89b3b77f43bdf2d2ece0f0b65c71c6e028a1390b1da489e79dbf86680734cf32f12d1874f62ff809d0ecbe68950d843bb9b8f3140ff2527681016d82b7b98a648175b3f588dfc8ebc3c21ae07695717883a26b3d517806e5b14ebf4adc8f6cf4d38d8f31e619a32d15957c8a56d4340b063446c9c3c76a36108db53a9481f14c2463b6ad04ffe63efe2540e615f64f7d61c9564bae89cafe4354c14ae6b8c882b505a075813d41f4124571ea55dcc9028460806295c26e0b2c01d4a7fc146fc0275d2a3cd041360ed6d2503599cdd302f2eaeb711be3fed1d31624f8e86274a323a17753a946e0ae1786a591e8132c7c65336969559f29b13a9ae10d2edd10ad6672eca1b613b9f72fc09d2782f7fbf07a1813e6b9d1ebed1043510336e0a8324eabd188fb0e70423c4fbb12ccdf8f00cdabafe619a312e7a861ca4aecc2454d24a8718bd7e3aa1300d2d4d4091ef52765fbe13474f17449c0bd9a0c18d18a2c712d8acf51490a05c6969eb87d3389fdd2749640054ab0b1765a9376a10bc770ab42001c45eb1666deb93c304ba064060a2809574ac91ee405960a43a5b5f11b8e6d32194b9d293c93640cdff1c4351a8858a6fea1fc1389d2b7368270329330795155c33fda2b3410c5dc39c7e9091b91009eeff325b7a1dc5d8a6ec64696ddcd75593b90d7dde899300b4cc1194eb9d1b66cb520758a1fb426a31fd231df8bcf31c2df48f50b1fb3cee794e358783ea6d04dc7d8d69349c7315476d3981a56d99d0bf2a42604742498e8fe2b0deef0aa2cf7417f8a809ced47f3d229bbbc7150f3ec210db6256b0c899572410de088d99ee86e897d82acea2b59b03ba2bd9e764ac7faf803f8e87c12582eacf88113d3139e2de5c29eda8e4be4310c7e84ce611ea7b3764da86f108a767be2efb53fd21f9b2d84fac55b1ad779d90b2c8c50e9010d20734e5cbe493d06a9bee62e19031b803265d61f4a2930fc0f16012f84628370a99e268cb82e1c481a3ed32d8c68437500ff5d9084937f2e4babcf84435c3c0b09761a01010a66411c9167d044a0fa05929ea80ecf11bc16a1419978d80090910a46f427079539310e69365157e11fa31de43869df9ff74ea874f96fd8fee9bb50badf00e2f0105219cd90d219815d152d0a354110ab7c5b3f6f19414aafeda8c00ac904f805dc8b2b333c5b05ca45ed64e98b12b2a2e4af141bdf068dd33b13f54e7dd1dfba19fa201123341d957f9922ecc734cb66283ab6aa08bc66be846530ff9bb082cab6d8fff00d1bcabb9ab63786621c2d4e64441b9561a5da4ac3f3575c2b22dee039fc98d60dd8fee1a38fa247ec02420962a181f95207ebf17d315648425ce1997fab36b8e06636a2e22dd70fb4e29ca52b8f8125d5c2f8dbd6cc336bb139f442fe5d1cd0418f2fce690bd173a6a361a6116ef469c797bc1fb378a69b46ada2fe53516317ab84cc399498de8a6901d8ca737fa13996e5959ee03bce0463780a6c4e62bd939e89041ca8f3530c72ded3e45b7ed7bce6337249cbcbfb78fd70db17978e8a9f9926445c97c599b6d589b48ac0a9b384fe119cf653329dd9e0c06d1ebe06474975dafe8718108c8b8072b69e5925aeec0da48a06037cd5b287557bd5a6fd12aae0a8abd45a9b56aaaf8164d1d8bcdf4d4db3f987831f563470de5c9cc6f69b863abeaf8e84015c458e954c694055cbb3fa860ea1d9e6436bb48753074349aa2b2376979a833fd2c2113652730edf7950645a5851e83b93fa82c3f3972fe226545cbbcf4d69a106643e66c407d2c1bab138a88fe19b5342e51cde83421b56bd89af9a03f6a7446e977c0e3b73e4e513503bf34013024fa018ee0b0c71dd882129f16f8fb4feb753c0d74c4a8a67e304591c4e1d85186f4a0bde0c242ef5376c3e1370bffc3e7af56cdc83928a927462676b9d7aa51cf8ed9bf08d21afa8e1c84a052ae16015d1432eea9472799849d69b816c6520aa29c3780cc1c37c7fcea339af0538cbc6c3c33a2b218848ad031077a65aef1ac06217bd05e101880cb05b94c4de32686ca7a388e718bf879fcc40eeadf707910a62c723d0e8de7e838c89df9d26c330895dc58fd422659e7d16896ee9ae3f8814094457dff40f4698784c2c5edae8abdc9e80eaaec3b84dece0ca4dbc9d078bcab1bcd30a1aa49502a885b59608148f18c6e39687f9c65e73e4fcaab6bb3bb3cfbcd82e9827c0664d0b5e66b8fde623eba384f2257d6ed65d4b674a2f2317be76c160fed1f2e4f41306fd2788b24b2a970c408aab0858c482fb9b2c22e18c70a9261c25819a27b245caf3f6983b6c09a95a12a9861f1722b2453f353dfd7a9078ac9110cd827cceee6028acae30436dd8deac884d9726701ce1fa53807010eb8cdd881cbee679e0329bcd1099acfcb4ca5f1a3fd7c552109d79ac97c2f0a0d2f30fd978d486e473b5959f1887179e93db086fcb89da2dbd62c853354259bdf2609c3e80d0bd0a2b3566c681eb9e4cc0f180a441633c7ab37321ededfd2fbb7c8c1b075a429157d120a3bb2cefa038a47df944b852677cfa39b0a8d29a9d780c4f5e6c9cd1ecf9b1971c184705c6cbade0478b6ad793166b7b4158e8c1f44388cfdced60098cb4045744ba8e930009eb679b631550116c1a76d00c1d35e6a6ac6f37f810eef0b48f070e6ada9817cd0e0d489086aa5dbb9165947a728e44cc661fe974d838e259e506c040422b69a4aca55a42f748225ca877b3f3fda8a98f6e7f4d53f06aad843892dbe9ec2f5edf8256478a5e0d53a5386141c7ad275f9c573eb72e5519a5390423b6c236e152f011edc3fe018144c4f9a2fd0981c602010c4493156887ffadbf0208ccca3f1ed283489c8385dd3b6d849e881fd18ab2139897dc4ee027d1ecc79d16c0f4b0191b7de5cd736c951b69b027296fe24c69b5ebc4e522a13c687ff2a5bc8277e8b23f946cbbdc12fdecfdbf0163507724b4f83bd2198543caa822a221d4e571a114e7a483a2a6ffe10f2fe33f602ff01aed6bf9ec77201bbd9209f2fae70844fdd8100ff7c0ca72e098cd76d67d552ec7ce41a5ad66c567dea20d5dcc04bc206fe5231111fc819d20fa26a370910a76208c23dac25c6b26e32cbf25aa14b10fe7d00e2ccda1c8ab2871bd111f3a7d8b4f1520c3efb5aab7201d01c6f463cc84030abe0cb9653fc746af1e1d6fa4f8b2816f45087fb337d25682c93343772aa0d66266c8d601fcaccd4d9d3766b3829cae3f8c1905b7bcf2da012e66f33192c59c54fe4183774d6e05807d95b4626bf23db2af5307765bb851cf62a4d97fb02fa651a40baaa8ffdc8af6688f15cdbdec79971a18cb3028c51698e1229e91d88af3eb58b57b174a0eca4245120b2f6d15985b916b3bc576f6cde6a9759854eaeb8e2ce9204c631a1fb24fa3eb9f1c35d1b408294082e09f51c1df28a39e29bcab943b20b88c88343686896da3589ea7a73e747e4f7917c0013a6ec650ff39e8844ae7ea5ecd9a49f9286d8185c03aa27ef80bdcad477ac5e78e3bcb63e4b0cd11f660fcbb2b1f50a9195c3311fb2a201c9c53310f02dce7c4b7e8bf9a25c46de66cdc57d1b148c1075615fddf79331ec4df7ffeaea45a54fd5e5c8b17f36c7326b12077a8dde372cab3c355643e5f350ec1d6d6154fb90eb9a3ddb66ff95e9a698586d9087dacb299804f9e3e693084859d6071a3467c838eb68966c2228eee752a85a86a647647aa52ce7bb8720df280b1ce380437377b42691d43ff4942c138edee5964fb7333c958af5c3cef88d69b5ca7d09e8022ed5e178759b2ac51a9f9684b2dc9cdb056f32d50f28ca708d12a2cb925e0e4e9764df5c30a449d7e4c9ebedbd17e40d808c5aedbb6e95024d56f2e3d75b7bb500e33160a32a1bc9933999cc84dd33d0c8cc9c6099cd826e16efe7e9f5d8a6b8848de2d98265503f993e161c466a42f93e26d1d81568eca36f639fadd431578f54070148e7cc042bc82ab2cce8ca5c0e5b1d6679b3e0ee7dc61ab70eb90af285d19efb5a6583ec7620c8c93d520efc39bfcb46fbd198dba1276a9a81b3e6794b4991047b378b40909934ca2442bb45b5f6137924547e6e4a64e0457770302d66c818c177d2607e1b46652c994a0e101a0c7f884fb5b388dc4f54bea81067a0886bb03b4deabe829b44266f7715577c8cdf7e2bc0736ab3f173b1d4f4dba66a8fb64b9f69375045d27852a87a5f449e955ba469449706b1b8a65e8b2610205b14289c487fabe7d081472e5e446926483bd41d5d417e24e440ab16dee8200478a5d59c0a3c58f39690c14ccb87ddaf4aaa9c1a69f3f483df251c87a7f47919b37a7537137ecbcf0083dc3f18ed895049389ccbb2a7df08d7939e8e2c2f3ec5d70a38056e91487d27addae456952843e6d898537b1b601d18723c891a90770b6adfb2c5f3d80148ba8048a20c70af46d6d93afd06efa80670a9cab7b31dfae70fa7f792079423e202f481a79a9dd618825d3063716343a6b212694920256bdd79a65b62ee7c18ee3537d1fbc1740dcde8b14a59e98ecad492eb5bc011834ba05e049363fc6f354faa4cd8237c850e6ccbdb66985e957826a9cb3c82b5bc1fc0ae7d02b7fd1d5b3d26c46fec878e3df1d8d3470cae81766e640c79349d3fad24d8eaac304f6ce00d6ef70342a625931baf8dbb68af596cbe1868802de8fcedb72304c736b511071eaf4f8a4d9d930c6ef8057ee9cb21e0a7355cc1020a14a07b10aefb1b10ad5e169b9d6179b6a27a372ca3b51b17a836e3d8415e121ef99afd5998c7b3defee0f3eb55cb90edaa9b0b5d2d68d0897e428e67ad52294b1fc36329716376dc1e9fe51a315659ff3de3cb381927b461a6441ca475eb75fc4f9e7e8c30e0da30b972d23555553df8613430d3a1a1db1169d36c2983d927a32b5c5be70803819ffc9f3e8c5dc2e40a1abdfe59bed2bc1d8e9f07514763297fc1479b0f12c2242feec119ee886b2bfa4f66ec27e5a1e9635429f0baee64dd453c855ca2d0de0d3785cc8fd96042ce0a839383252bb6799235a7472998b45140a13c6abc86482926553a8759386cf9a370a9a1c69d8e6b233ecab7e3a8280056830a068390a3fdeafd0ab02e33c875f1d47791906735c74237cfc4e982d88d631f55438c17e229ed175d39b4aa2e626857a24bfb932cbe68e27e653ebe6b5fa7a65c997f4864aa09b486e4b2ae1492860a48e48ad94bd082e8ba99645ea18e39e68f07baee0dfd3a7964dc5a430afb9b846d79539cec5640315a8ee85ad23fb839906a5a94d35ad280bba538bce57124fe516ab750ff4ccdabdb71cc1ebd41256d1e1d7f78a2217aaf22f0da9c1764057613aa12419f60fe938860d1a533d0d532c16d5e463478268eab3c19f82c315bb1840aced1a47ed62c3ac26d5988806048df1030327a318a616497170c9bbf74e6672a4828bbbb0419e013e489015decb38d277ebb5beacffdbe78fd93d17c1ec329b50648057bcc4bda52e35a96918626619812fbd3a5ac6dd913fe91b9cb809ff53c9590112184edc9091d2fcb3e8347c8ff0ee36d5d96741fdc82a83ee95a15668c29d923bcfaf1d074859de959fd0af724685b6fca0a8745a1575d2b36ca4e292b3d80f50926abdf1d3e7ed0a440172be471c762db2719dfef4f3588f3114490c45157ea54b28f51f8e3f9c6e5f2dbc27faec93948bfea8d910cebfa74a4e8a55a0a412e5bfe03c761317a63dcd695bcfd41c1da335907af5ec7c89eecf4e9c6bcc515a676561a5558c7dda2fc929a477ffc4aa2b6f5c61ca865135d6a7023807f69e34c28133f15df714652c3becea97f009b8384f8cad492496b7fe27aa56fcbade2a28b6da5f6d18f104002114ccc0daf35392424db8350573e7860875c5950501da5065d3b69bc6120259fb1c8144f366b107426bcf280a156dae6bde4bd02ff07965c890228b94154db179c0152a5d38ed6959cc2f672bcdd5801fc5707ab76c75a06f85c8d81049d42ffaa5d4e07162ffb9ae90d98179da43c952c1260c6e323c295f866fca1f57140a958597d034106d2f8f43d16bd77618d6598478155258ce014fb41d7e63e7846b85cb7d837615a4e1d10fb4a80c2aef6771cd077ff6c7c6511486e312cc1f3840cbc439d8e435c0bcbb458c8215290f0b38eda469bb8b5a472e3652e89fb08ad1f39315742c39c4ff9a34fbda293bd656c0cdd4ecf1959fad5814e4dd8e97a7bf392e3dc9033f79b15ed644ee600eee52f6c82c5a8e1c259177668d60c3e70d4580fcfacc103a2c416dbf3255f65c8a5dd68460b80269f8bc5da29b682c8aef74ed0b1712f6db9c3918713b89398e0a86236005066dbcb8e7712e46ec40ca02c0e2333109479b9216d10612586eed66afdb3d9b49b8c4247cbbddc2e1d05041c9a41e41aae4c0b2977d5cd133f5203b6f1b3ce07904573b206c0fd1443d83423a62e0e75d7f0a1dc9df22d7d34068b55467132cf97f45be754fabbf7c87e581fe08f619e68637cb12f6730b54ed89055fd0f23a44ed3a526fcce5131451ea3ff041ea4b1c7ac918c5754ed714cfc4b51d3ea0ffd29516bd444022c1c1a665e792edeb0b0fd61d20823853598084d03187bf5cb68f355550d91f07f43e63bf4e316662851c65afe52a14340337d5824a2c8677a3316de65ecf001c0f77960a3e729be73bf4ba42a04347e474c693fe97b5008adb71e2ed4f6d523a0f5fccd6beb8801fdacb8abb438c7fb0fdb169c89143aed15992fdb306fb0b44418f15d283d2ea4e613ca17a7b2e98ed03d358645ca21413040704e9bcb6840cd05b99c3adc47bbfabcf23fe933bd870da75aa306a4edb6212dbafe39f4f39617a7d969dd6a093c8fd51d38600a0a42efda8b19d85e63e9af737f62de3779bdc38f48b583c0e8865fa3615ac49996b4b2514aae1d61c584eb23e243f84b485c2e4ec40fc29ec4e4cbf1f215b9ae235e0241d57dffc83fe3b0c749ad869376a09978a98de329d374cb502a427f5e3e1c7d9f0f014f6240ba95fb3e13edd6bf090111ad91291c03a1e05bf0806d72db8a67dd34d9f2785523b9e7da277d8fba7839c7b562e21500c09c0a259183deee7f2b0d81e7dfe6161ab588127e3b031a867c216403b1843ba03ce58df4525b184d11e6b08457991dc0e86413d67cb5243e5bcd54b9e7b89f4df1345540182ee1763870f27b5be12bf64e19a428fa19f24f8a4c77825ba90bec500729aab2488559feba37534a44c940e2a5a2c841460428f9a9c4e9c1e55398d61c6afca68b8fd3614e74e624553d84e4b996474b4e7ab211fd9bd56ed2eac38939cd1f6526d971da965d35ad893adf14440ef0ae923d696c0de494157b072a2549d51debdbac696a68b9e2bb6dd604502d0e151c069efe19a80aa543d8452f3827ae3c31513d56b1a51f9a2f29a542981dde930dc23ba00ca7eb99974657c012f2a582547db034618504aeff5ac53b0f3f2398c6acebe117e0d70dfe4bd85f01e2f6eac86f4f747f80255d23c38a8088f0d64acdbb7073d60e13ff54575264dfb571c8b04fb55930793e208f46ed7e7eff87aca76880cd1ff8e058fd71fbdac13412c23d4e103eb955eddbedc8dee5c26a4b41430c8aab4367ca82f15396da5403dab5c82c49e38cc3d17f03044b341205f5941af839ea81e15410df4d69e20243a1623028ff830fef898cacdb188f5c5842773110745e2a7b5b00161a64b4ee94d967b0fdb20dd51ee032e5f17b02039299648a2554a13311c030d11aa7a7f64ba9c3b3b268d4d9050b8d29b5642d76cdb5d079b0f889cae4d3087e0afead300ee96bfae61cd6ccd2a3e38e4e08636faedfccc3015245095ae61336e1c5e50f9a88c9c512922426ffaea594653d2f6f9eb516d241c00ef410d28cbd85981bbc582b5111c1238303c443c91c4ace7e0f533f02fca4c568631d7e45412e85a92de35165049ea5ac7938e29183f9f7c1b4dc096b097816cda3340c462adb80c010ea0ee784a83d93f772f4f5573695aaccbaac6802a3a4b914f03d99d781042cba5ee40affe4cb851aab31d9263b304b42639c369f7ca0da807ce8c2a01bc9287bfd89360e5c294675eea0a53c96a2ea12602546d3277ccd4c07bd77bf8a560049e2184c8e6e7c03997b8824c2a2e1aec5316478bfd4d11d0b60766ff8a66a3dc3a1228e01780b3d912e2321ed707c1bd4f9c6c157bfa06e66207e71ac01ffd487f77b78d91ab906c1ebb8c9a7c1e4d27a6e215417d377d617b0f532e989bbf69f0fc10408ac0e2ad636292789926ce9b582fdbbff4eac48b68863744fa053e7ff770e52fd3b17ccd36372c715736a4f48429f76ba538b39983d1f70584db330522ccc337bb76ed43e8b59efcdcf5d2c1453d64086f9bbe45ff69472bcb9c5f231f88e4dedf44a0c2def1a97b365a7671099aae0b419023442ee282b2fd6e919b1241a4312bed411a59affd393db517ce54c08d336c6047e9c92fe18c0d8736a8885a5bc4182c2fa46fe70aec7990a23939be3c2b59dfb3876862861abf0c68cdaf26a96ff1fd2c5ed734aaa12863237285f9de434714055e895bc5adf3be6b09ab485d45f32cf8a28d01739e355a4e47993768a51fdafb98067d66bf34fdd6c9e25b78a35c2783b1de4dbcd8add4f19f879538744b96ce67896ed616c1cb7c9b64853f77607d0854a0435c030fa5409a5296db224c6d718acd49771071d88ba84ddc364984e47ed799325c1bc872081cd67e12739fa5694739538ff31715aaaf69326ba66a3a91fdf1dcc321e4fceeb516012ce0ac86b035ae55794249dfe2b099929aa74ffbdb5eb4b3124923a6523f774f2687ba62655fc1fb1920b65fea6d8c00ad9d1498d43508bc9469280283a1739f3f28fb667fe5991fbffd1047e8ef633abf05588987784a9f02f9c3f30bc190c5fe91912a6a49d22395d0ec11e0d1add9c63c1f264618803bfe5029491b900dff1af47cb4d07358c64266d8f79ce57da3d71868bf8964369c4ab5cb95064f08d2114c6cfc16a3d86acb7fbebf2405c5d99b08fac23d9a19856780ce31199c103ab9a74796f69467dbe52db05f8dcddff61268dc46151eab5c6f7a3653a4939d4f7002569aee34d0affab67a8e0e9046679a433b5de919bd83b444cd4ab52af1699cf05857dc8e014d6fd8932c2c19c594ef8134a78e14c13227dc2cfc9bb6e2a5e6003045ded4809c14ab9e20750112a111464cb547a2394a19ed180c01834f239b628a5dabe5c8369dbe1ea8994d12a97acad6ffd567e8ca400df996ab58671d4ab31d7c3012b94983186157eb19aceb2b729efd58a56f5b7de97cb8a500567f10cf3b67eff8429ebfddb9a4955596b9d2242b4a953257ccb9bbb6872c165c9ce82fa3f0946f5a121972d97e499cee7cd82c1f012b193490cc97d545e6d1bf37555ba80d879d78126db7033ef95c03906379434cca3fa3b0c562d81abf836e143cd9656621f283a170577136c080215c91a0b3352c359791252869041be4905f285cbb4e578e142b8808e82d8e16711513388a5cbd7e80e591f76af484905a5f2099da14b1c62963dd897f6684aac4a0c764df801c63eebceb39e824efa512cc257b9697560fadae9f579397171ec93b00d9da51e103019ed1e155a9dfdc060898bddcb57fb245168e262463d1fc813640d57c8373003dcbfb979131e01c7515a267d71eabf39e5db4582dbdc92ff13cf58e8eb2cfc8b720914bf6a53aec20a73d0eb091939b1e01da082ff1c0a1ae2800a4a8e5e1a0e2d09447e548e1558f1f4e986892b5eedd37e24fc7618f09fc142ceaa0e0d90dbe6a28915a17d32d71a39440a02a5961e50d3083df909989f9c3f23614d59888e28858815af877922eec173d7d180c5b5e1458bfe00b4e2a176748c40747b3e2780c06d24cb67e2cf03344070e789bb48879c3475800299bb492591211b0e289cad04f9fcf89d6c03bd780a3177cfef39847e5207ba374ad02101c4d71061066ff235a67933af9565d8561726595298d2f0f47e4af8cf2af6e1d0b923f1531dcde94e2694c53845fd215eb94a3dad2646fd06089c8a9f360fb0bfcadbe941e2509d3d99ccfdbd2ca78a02c9e86c22f6deaf5bd8ad631d405e63c639aab605c4591febf3c359d4d7da4ffdfb5577016b0e8296c607058a027f045c644cb3528944f4509b43ba22c1d899cd56f8df4b3d9690b0e2b0842b6f9a22bfa2a8daa26070cebf393b1cc1a6c4986b699d92250879bd36edb9c1f49c07b0e797a94ada219d693180121daf22f81ebc3b49e6adfd145317c3f664227706b674ab73d4bc953890a8a7641d39aca186f9f0d180987d65f9b3a1e1f666a4b931e21eaa0415eb5bffed1a170d23307f43b708e6c3d07c2fa2e03536251a32013c3dc836ad366514a5cc9da8b412035b458d56538e0ae63174a5389dbbe080ad8c65c8240d34111a30d5eefd8761ad8719e567e9decb5f0a443932291ccbce15019ae2c11874b85408400c3a1d207d5f355a82151a53d606e04311066fd76ba4ed6fa91c773ca5dc83609d1b1a7ccafe487decb85075f577957a00abbf0bdfd901b7043ade1eff75abbe095a0a0180d9a83252f59e4d7c673f7fac83662f2bb9695f3c09cb7e11c595a7d78de048cfdcd4a276764406c44e93cce4167ab4440ff9e6db25cc6e7753aff98d2999c43916ce4ec679e2e6c5c3b201005c1d07db4290d2a3beec894e5119ecbfc060d9d69867312cb6550be18348b52f98b9833057c5a2ad12efae41f9efce552a5ec651ee14c499616112e28c5a7b7e31fef090a37aebf8f591ffd0ccd4acfc85d3927601a5803d0b7ab32c6f07f167b4378fbc71c2a5ff8e2606ec7c167c69ceb7bb20fd928ab0765fd4056c2dc509e886a3cc829aa736bc65bdb693644dbe593a3809fd26d47b3fc22a9ac1ff41b31159a9bfd5e72c693c23d588f56da889476090f39d81a60bade4ce995023ac0c9bb5ed8dff92d4a1df109c9424d4ad5b5c16ad586295ab56bb0b9dfa345f13a7e7194568491b3a19c0d0b2e7078a2ef592fe96c368f41f79fe3fd82d70948a1c435eb5d952de2bc3d135110dc6d5fe3754bb3814b439e73206558f33ee5e35ccd1e8deb9954961aff23827b88fbcd8bdda8b2c25a374657094fd51ddf31af3b7c9f37af6fd3749633f188dca79efbbdf4df21c4d1f72c0b3c30eb2cabe6c229ae2b3f4d4dd2c36d0fc360930baa71bddf1548c5fef48b76268c138e314f20cf400305d04c9fd7d898f02bc339aaed5d728aaccc0a90be058b837829a144224a7742866ca7eed3658d0643cf1d3b3eae3cd5c38d7ae82ad2b3a9882063f1d27ad72682ff62fcd43cd7044f0da7dea60468cbb981bb9560042725f9aba3d9c22b6447e3375e0042580a1a3b33b8cfa328d6f33131771865fab3280128097a83b5044ee6df9f1abe11392c36f8a11f3e4d85b1d52926d7bf8bb26822f5394c35c5562d72028d78fd386a86971de09adc692b938cd759fab904a5fc3ee28fc4e58c6a81e687432dc1a46f5c26c37cbc61edec75d2be792bde96a41a350cff6e190eb9c76839f229bcc3827b2f549a29107399f31a214f8a1be9f145155d195a2b289de531b29c853344b7a6bcbb4cc87bbc49109f6ccccb123bc6352a9c58c713689b005dab759d3d197d7460063d118cf9edfd229299e2f45843df248c563a771b63233b8deabae2ff99d63cf945a3c10fdbe183e570694eb518ee32f2dadcc9df703572cd831873f11df97b5fe6a1c69c80124f1e9c27ebaf747f961d93d383e9d5cf70e9e861a8311a6c8347cacd1b431538d7d76af8ebd76ca64c9448f11481f2cd413bf1040d2bc665d5016bec5bc896bece4a5f3b5721b2e9dd12807ee2138b751c75333698de27ee5c7c212c4381d75a28be6dda6126b18b98e8d4145bb9c31fe99994035b0a95cc939d35b5c68c8e44753bbba6c4f7466defa970e84de8a1b082dddac9efbfb4d0e813d05dd73de84e9a4ad41bea5a4b2b62d96abbb41ed1ebb3d8878be3786dfe000f8790a01cc362bf586c6357bdba958d05c30f49b1b39393082a6bce59904577b0d78918ca2f6c42c77fe03a970e97cb97147b007a19d8ba9743118310d5674097597ec3353e543ad4761edc549ecd1f382107dc672476c629b2dc413c9010e8ac3adf28d44d803334a2e66b3abfb86066570375cccf66930d0fdaddc603be9e40c24d01031c0969cece98f6dec7295cc22e4bec52be6a924d9aea1796c0764d98cb4c6c8adc07775dfdc4e85137648abacd4c756da97d82ec530a7bbb7778d5a41f2ef1b634d793ff14cbde1eb92883ee672fc68d4594062f1dbcd752fc68af0f965894523e0ef1d8057858aaab0742dda94e189863f3bc31691d2296bbe47ddd90b68e758c0ca781052a2595b14aa58fb2a35849df07ccaa6daa377ed358a03c58242149601b64303f893520a5b08f50a522fca4f117899d5cf400dd349d6029bf78a7270b8f88a05afa17ff976a754c3426d48895f55e8cb4063cc943b81e28073d601873b3ae5b549070322cdc2d2f60555fa3104424557d63038f828c022d4f532f2da7415a546d15d81fb9eef7fa3199b776eeb136c55735f641239fab7de3c55d8df304c71ceaf7128504328923f51706a7188f5f4252a66ac0a6387eaf944d12c6c14ad997a04415fe6bffa1be9a5ab2c22bd330e88ee29d65c394915909c498c39b39e30dd53c8204209881395043344acc30e9bd2512f658143054edd2025af73998c0e6b4173d5b9ddc4d23d75e01be8c6e3a84172c06f1c50305b070b89df7e328c5ca6b1630130aa05236ba05056c811398d5b6d051f3f2a5ff5c6f2f70133f5f76e142ceb9513af7e75b5e052db6c02606f50e41ad0252af255f301cb370437a0a9b28d6625925ea258c0c8c839887ce6f569a697e1190214ae7c1dfcf6eb2667242a3d7fad0b5498ec71e117e3de7d4a75fde47011def515f671d393710d08ad6bf5a56827d4e29866c3ace6d4a04c225636117bab7a09ee757076bab53541f31c4a0d8001455de02f147d60ea93398e78633e0dc28bd08bcacb0e7baf67e5761edeefc80034bc25cc7c95b885cb0500a7e08640a9f04d517518fb5a5213723ac8721165c8072396860b2848ccfe253c601307a46ca77467dc6c07ad08b43895f6359d773cb9b33f6d83159a63744f54826f165fd04265b696fbff648922c55ed91a21a99e094d2406905f2c488f55dfe34987879d2d82675c88128a3d807a54f592dc52f8d4fbcebd0befd2eff626acb01484c58830bd4e36f043fac91c2da13520c702d84e01ee637c0f1242b662864d15cb2661bd894320daaa0a2c3db9cc93587150bf341e397b8033fdcb6cdb9bcf3937057dec5d44f1855b0454ae206a1f5c913df71d7c208443904c4dd6290c73feb636c5406d022e76d1e938c6ebbb67fa6898da9510184e36a164aa812a84e287c2f2db0a3563538bf263f953213a8023e4477a4382f503ecad3a219e0b76eb8459016e4919a558f195e8222f1f905db8c46915830234ed723e8f015e82ddf5a1b0302def7415a425b6486ed4a22d5ae2fbb81ff5d3542c7c128bad8432e7718445ffcba6ce4f665e9071d7b4ab6fe9c0060cd9e3a8a36a4c5caf53981924829b699b2394ecb1f7da6fa15c4cf1423a871bc6920d478299f3dcbfb770bb9c3f996402b2cc0dc1319d5912b15fa21ca3fc25a5881c991203544ac8cd5fd8a47f15a8705f440ef836999cc366255124867056f1580674b45f770e42c7a4b67d602fa553583e546e1ef203c3999a460c3c8321e2b7a80c411ee2736e7e055014cc54ee07b9167ed52e8d859bc159b96b6ffe296295731e89d7e0d74cc72e8c65ec7e411eda5a63e6a61af2187391be5a753e774911c2d360e68e0841119431f8398f95a58b09f96518e8607c5b8b26997303baf4cdbfcd0b4baf0ebd25443304dbccd392c41b9017f93447bdd867db57a15a6689c6e53295df3a691d9ca194e27ce7194cd252264422650f9f356cab62be98c37fd4c1e1488401e150f12bc878ad738b570072ff40c6b997b0c912a19d0a52c4341565f28d0317d536d93bc302a9deed7437adef8ef34901cebb44eb9809f651ccb96b3eaf20313082e9c13a6e859c058be89ede7d4880cf0012d99ea817dc27b7a34286ea4255ee0a5f2d4f26ad60141245bcc276319c7a22450d59614cc7f8af71b628d6838ef16e2fd10bd18a0f1de3fd10817f5bbc767ddd6a5c57c9e522445eb0e5193c72745bbcb21660364468635a45e5d55f4925e85856266ab2e955ca7a0ab5e8359ff176beaf8a637cfd1c533470bffccc71303d32afda329b8331307e72bd1f63dedf4f6df433b2dc1e2a3323a59c5b6cbcc797fbe12636fca6a54f364b4ea74851391fe5adf4bad6666a4e98144c450043b51a45d4b0df5d78fce6071ed6286f9affdd3b28344ecbc70e1335d1a735e049d0693dc79235afdc99784e8e68b734942f9ea9ac6de9fc377d5f4729fe4891168f4edb27a478fd7805f9682c5ef577b47e496b91f30a8bc75528fc2a455709e6d608a4630ee90e195234bb31394a904ba7f8b79003a4585651c3d5b085cd185aa1fc55fbff1ee6986485391c9a366e33641fe757dd7a7b2b232f2bfbdf11732c81a5c5beee64c83d2d6caf4d0c5ec46f8686675b00ad577c5f68bffb4c2de7d33a08ae496c477096a0e83e0cc41a0c339564afc6cdee4dbcd818bb00837d7be86974245bd5a35c7d5f5262ec3c7d8c1af834e34cab5fd01ff1c6dda7b995818ed3b5a29b65cc13667f7e127334551530ea31c092f32ec673a7f8908177d53e98ef21723a22a6c0edf0e9100e3d167400e4583fdec734650fea7788e9148fcfe05cfb2e134e58494c85348b08d575fa737d13633018946a4264ed20b878053f3d868fa859a29bbcd0aae08fa2d171775ae9c8f34940a0023c6d4d010be73101e3e2dff950e52734822825a51897abd3a14ed73926ab5ef6e6fd44e221fd6d45b142197e9d522c5bc12847ae9d1a00c5088226a74ce3aed25116c1a800e6e93bcc6f37a1708b8926fd573b4817651a642a1addac0b7969425d27c52970ea9446fafa983b4be8745f4f03a56e78b04e2e035dc750e8fabf201b0514a58554509cb051c24f1a5574212f916e194b22ab37fbbb59e688835dc19e15523c0b911ed704a27595e33985a1509a9254761c3242c7290bd8472165c46f8ca00e188a718e27adb4d4acb530c8c8e70009e740a953a6c1fcb873a7a28820fb60abe65c1745835aaa470c40ed5bd161574d0ad7332420684003edae03bb62db2348131257509d45d3edde541d3230fef2cc9ec25cdec08865d2f41619a7646d5dd6ede01fac416b0ba4b45bbc4607361ff7f63cdac7eb7e8539c6ba63b98fe7e9d0cd3b9fb80f614d9dd27ce7df721bb6ca2d6c15d85a337f8496b400300cb3bbdcc62c741b0b0286cead0d97094d95d7ac0c65d4b51ef556667934ae6c56c8c5ecc564e09b900d7a2b8e71a125b2dea107c0cb41c79efa651d96b2fc432b497092ff38c6a8074c607ba170cd66f2ffbbeec37b6f1a1d34e179cfc7acfb58e0311d9817c00882b166a801385d6a1db8c568f98fcb3afddeaeea8653387db742d995a586af2ba5441214f1b4262843b3c227f1e3f26d82bcdd28729c2b1f91c783d0d423f509cc24e7104cac534a388f92f7264ed300c2c71e88067cd31ddf1a647b02ac129c33080a8403e6fa2bea2fcb6b6f4f4610c076ace5ead880f31bcf3c7ecb01ca2d4071e543b745bc1cffe9aa0d3d0720b1e91a4ce4fabfafeebb002a6e0b68c8b24794079944f6f6b98bc1b299a139a6a3804d6c6116d3a377e1558101641d0ca7fe4878d323c159a5951cef2c23c836988cab478e577b6503ec0b63cf15f27c3e9e6e0563969e6645c9eb74fb1b9a7ae848dbfe38376c3c889fa54787db325a7a2e2403095ca5a31516ce065461e8989ac2584b0f7d084084d355fbf9f941b6dfc32b73408ffef95f40398b1ef15173876384874171f88277431910dedb5bdc53f03572e39d8350200b00fb918b5116e99b19c7d510158e64488ab6a425e8d755b3db74ae78fa4b8176a078f86dd6d2c18ded6d4bb7316e2c2ccd4826fd30bb9d0bdb154ba41498c48d69099a9b7bb3a5ed240efc169e615e3c8b8192bd384eabcd3d592fa4d08dc8819641aafbd7bde3a25f1414d674454f1c8f5d32c1f906ad75c72fe7c97ec880c1aba170d3cd36d499296dee1bd97083fbfd985a19c887ad403b54b9c57eb9edc19c2def6dcd3cd3cde20856b4d39b4c76e3f203dc317e208bc4c20f8b3a428852be6db6fda63886f67989d90e242216e96f9df83c5c018bc7d5b48b5654a068a87369be3b5bf357012575573e6543a9ac5c5b4d819b5364d37ea50a93262b203d9e6e606df2b7b88cb566946fb24f9cc0a7ca388169cef777d782252fdffedec3e9bcdb3fd2bf2fc43a7a7f5754b4111dfb3b4fba7497754c2036a2a4ed38a66f820bfae6061bccbba833ab8e72a353bf17221cf74f9aa31015e78d7a30b9733392b5396f4b441fec24043a5367ab5b301895710e79e47cf378e43cf2c15826df725abadc7d5cc5064eef4e9956f3bb67abdf75eba2eb56f97db5fefd0780bee98307f0c0df1fc794775b16f45baa43af68b1ae2307e750bf35a900ab11dc52ccc868b9b6bb7b4b99a40c520a240ac10a29686fc05bc45fc20e04638c31c6d80816ccf4da767a6d970615c73ed1c62fdad46edbf95bfb75dbc73975750c6d876c23fe02b5cc7d7fd7e8e844ee5e3dfdd806de5608685b4f4d19feb7450bfba2d336bec2a7c70841c45a94f1570acf006893f86f4e397e19b739b6fdefabf1bdf518a8ad91a613e0b1a8ad61f17dd732a0bd36881b30cd0efcaf38bebd5ff90bbff73deeba5cffe6175dd7652ce36ee3395e4901f87cb67ead9ffefe72daf5d8b57e57c5efbafdea57ab574474b4d3d3fbd35fdd7bff20689a5ecda90ffffdeacadbc1f79ec97e4904e6fd1adddfb0fbfb13164cfc15277cfcd8fb2fb4392d6919de7fd57e5e876d16c4ba4acd0e3562e867d72220423b6657235c50c306fdf5d51ad0fe5d8d183adae21d71a7694cffef574ccf5f8767d42d838ff72942985e3776fe3150cfee140cdffe381c90faec16cee3ef9130fc6b783aa5531eba3b8d661bc0db72937961e76d79f7f8eb6de1fa1daedfd5b7d6eaf0ef57dfc3f7437dde160882200882e0a7c1dbf21db5b64a3a95c076db655c63d8de1ff67d6c8ecf452344f79ec438bbe85e7c517c320cbfa7046a1c2adbf429fe302c85d88442a15028140a85fa93366fcb4f4d30fdc5ea0ffa9bfe95dca9bb5699547cf25b7849ea6a9130c56a8b98e08b7804b7f926d363fcb8c4e4cbb402dbfcaee4c550e35bba1bf40d62ef4bf84bb95697e95b5c32a710f0b185d9c7b62be9c226d961a9fc9eeed30a6cac71a8ecd2831fe6b2a7fb09d408df7c119758f7749f82a72f41ddd33df8658b0be6f0f129fc32d43d253d54c3e97740e00a2ac8010b8cf0e0043a887d8e2ac48722b31a2451abc10862a7ef5e3ca14ea7d3e9743a9d502814ea743aa1c63f7d256740f9167ecaa8d3e9743a9d4ea7f33c51b40cf5c4f8e78b1538730884a32240b1d3e9743a9d4ea7d3e974fa1f0f81d1087cc4502814eaf4228a13a94f4d811f25d79a22a501fc1812389d4ea7d3e9746a71cf7cd6b0b6429d4a60a7f84b3678d2b59ec0ef28b9c64e316c0c708327ff8e8e7f661ace3fa15028140a85429dfa07a85378c7d1f1578e8e7f42a15028140a8542f91d34a052cef33ccff37c1e37b153efb840103c30810e96300515d86229270a8542a1502894e92d140a8542a150cfe32686d2397650e008081daaa0821fbc200b31145dfd743a9d4e271e37b193de51bd357e8b8bff24a7336caccdafbb7c933c019f6d7e39dbb5ba4a746a6afce5b770532eb1cfeec4959422d3fbf2537097a9f7250d3ff50b4962a7d701f4c576f8c44eba7c118319bf933aa186e1fd2579607c78ac4f7a3fe61af75330c05dfad427056796de26d9a2933c5f922449b60043a7e0d0e99d1c02fad849e7a845b11ff8459c53e0c6e180d46747e0db3e1b932ff0a762808f1fe3b01bd42918e0eefed310f8360e7cc13771971f44f3c284d8f5bd6fe13777f7de7befbdf7de1d9eaef5445a879c9e60da073ff57dfae88e1cae1f9a00831e50a1881c72319dc367a8081ae0820c5210830c1811fb01ead4f7fd57ebedbaaeebbaee79dcc4badf4103fe1a52623b7278eeeeeefe3c6e62fe3b6828b100071b9c80043f088116a301ebf3b6ece7a8b99a1776103132842ffc8899b76531c618638c5defc03b4cf73ccff33ccfebf40ef0b6ecbdf7de7b9fc74decea1dbafa788ece450f1c1d5974d7d5ae2b47ab8fb5f647ac2bdcaebb6e1b50d2d1eedefa6c05e0ff6ed8df7b55013802663542063a6cf03d5c5353535353d375a918e85e80a3b7e2e048b989d449b9ee6e1c8d61b7354da40c4fa73edbf5026ededab88ec59f82381503dd9624657cdf89362fc0ead46afb297ffc36a40cfbdd93a48c4ea77c01f64ba5baef7bfbdd6bd7f76dee0db5e881a0f721a9e375f5e3f9746a8e254ccf35361776739ea2c958fdd503c3355ef9acaee13df838a7dec7c00f7e47c2f01ec4e1dfbd9771d8ddd5e86e58a3710718f44e4f41cfbe72af189f1b8ba79c89bffe5fd80c17575c5a6fe78d59b3f1c988896feab6babad467779d9de121447fd1a4b3add5a9ab15595dd6a493220413932fb493d77abd65adaefa3e6ead1162f5570f7eff14ef8adde7da9d1b0061b1c46966fbe858c6d2f8dffa89326e7da7322f7e63ebf65e5d6b80d820699b7b74d41dac2e24ccea28e8a8fd7bcff3bef0bf8eebc45aabe30ab3f1a7777b18b534f84331f64cdbfbea7dead8be78c7fbdfdf7befdb74fceef50775ddee8ebfdce16b9130bea467affbdd758f3dc8cbc20d4b542c927341ac6037823ce07f30bcb1f45d7e52a7f354e7e554f7a10bb1c76e177becf6de7b2fa3a5cf618a756a6bc5497529e619bd1d6a7bfc54fc2edbe8316d1c78dfc07b5f52c68803ef5a5da9ee2d1e75aad3316a840f3e7e27753a9dea1eddde83a94ec740b7a777363e1df5aeeef4f1a908296e6dac5147afeeb1bb8808d2bee0f7f8d718714cbaa726d943279ffa8b2f7a36994c269349f7742b272798153b5a7bec1eb5f855d72a93baf6c419740ffef1c5f77e57920035566b2bf52c49801aeb4cb9077f5a9ade499d1978d858c99798681d264f834fcce4458f7441963e65bea963a0db7cb3e4ba077f6a7a1c770f59c0e4eaa02147921f9240052d74c10b496226bfc32766a2779cbec4f4a2f765ae9bcc3d36c91662888f6d8b7a4c799853ae63a03bfdcf61633a6a92d88906528999bc977b3a3d64a26b30f91d3976f8c44a4c4c4c4c4c4c4c3e878966c06dddfff9f901ba8995bc8efb85590c08fd90cc6631a03356f23a4c49b3d92c0604c64afee747470b12182b791d31189db192ffd15193dc66331e68ace479dcc44af4036eeb9e9a60a6b5e8c27c7cfc0cf193c310244c288add47c0858126e0c1111f58820f46a821b662c1118a00fa6143088e88dd7fc0859dd8b0041f18a50008270889892bc09f9672a93a7a43bbebdf54f8e554f8dea7c252585dd8753a54db25bb1a31d4638b3e82a51753fb852fb6b82d1c67fb5ff617ad4ee14fe14737de3585750c747fdad3b502b3d92c031b9fa860fad747cd1b44ac2b71010e33f94cff9b0f76f2f56dce0abbb9adefc3b1e2dc5325eb0ac7bb01d706476be9c5fa7d1e7e197e29fc521886226d08beb847cfbfe3352286b688d45a871a0cc9af34968638cc1679eb7b8c26f1474e236691fd5d22e283e0f838f02ebfc7b4cb775b2a953e0c4be17f7886666813d6ec303cc3300c4b20c9139642f24b4f967458fab0149a4a1f7e5a7af2d3b27cf23b12c6c813fea853f14b8fc552eec26cc17c857a20324408108b7e9fb23a06baaf1edffa70f4238b72a935c2c6244ff85e6489e42a4e8d118aef204a82a2f824e9453ed436ccae45444876288aa27beffe9efff5bf9f001d6e28bd8b5fcac086cd3296205db8933a05fea8c3d4d2403c9e4aac646ac01bae282713339ae46bc3ede146bc48f61e9cc881f8cd85f80ffb7bdfc191e36eebbb40489eb3559bd0bdc15f5e71eefefe0441a10d3ee8e4836feb8a2b6469be1f1f34bfc7b4cdf717c75cb72883bfec8be3ae350d49927c512451223fe4ac42cc9c2d7eb5d9e2cb88e89b2e8a5bfcd05fa6ff072bcef8a248b6903c2229964f7e498ae48ba458f21ff9e5a7de038c129d9ade6d57c89e608df04977777777777777f7573173adae153f491731a3cdee6e54f1ca6d09a67f6a83bc87997166047d8759c7fe4976db778178112268a9cd01b93df2d7491047869cc693130f768a6f1071be5bac66874b7688bfee76328633c05fe35febe3011734cd33972bb2437276852562698e5071766a876c27a21e515fa6777bbb6e8740a2c21cc90eb12b6e4d05a8ebf82e9c5730a580c484ed8e7461f8fef8d485ee505de9ad6dffbb83dfdb8fbae22241f9129dbad04fa70fea4e639d82fbde2eec0bba48ec8a6f88cb85a2ffe7699ab7eddf10df66c98b1fae6045ef7f411736d6d690ab6207364432b6558db63dfd86d840c0a8b79dfa54eba58991c997f357aa3475aafc8a3e2263aebbe42b22f215f9de4c01f2d725f214fe61be3d18c14caf0db7fdafed12fdfce89ec73bce87f8abf4fe5ee444688cef0bd895c436a038b545704477500edca6c3f0ccc79cb7fccf93a376144d7f7d6796eecd51c758bc96d4e96eb7e441f3c18bbb42f0a7f7766f25a60997200e77187c1004750a46f9f84b3d4660dc3530ca06c02f287f428efa87c22348ac5e96dfc283ee63207fa5f0dbb0fbbe0df5f63364d7db4f6e3b9ea5fcfdd248961e9b0c18c72fa718dba0c3f6c74e5e3d04f4b114dd625af285c53b1c4af971d44d983f3f4af8539dc6338d81505f37ca0ef30121ced9e6ac91bfb435d2b688a3ee314a1a5754fc5a51bc7b68d8a5927e40031cf5673151fae70759678ed6b4a43110dea99de20e03cdfc95da8e5f308e8ca0a275a72936d456dd308efcd456dd30a480a4b65220114c9086bfc0f747c002fcd5bda35dcd85a97c8d8a2651da7cd1e51af753fbc226d9e357f20539eaf43e0eebb3c3cfe17b19cf3c6fe6cdae061ded3a9dba1fd2c9730cc4ba5ae1edfa68b52bc699930670d200a7b5b9db033305f7ed02d2769daadb67b7f0b3033b6cddf6817bf1bd5d67f1be785b21621212a02169fbe315a417685b9b9484a31669001eabd7ea0b810b94e47e2fc6f85adb03f386e4a73ad0f65a6367625d892514982d762e4c02b3d96c16abbb7e477ef9f7cf0b33fdc56fcae7356feb56474bef79ff7de24edde2bfa85bd42942f0d936a79fd39cdaf732f861f6d9a5ec4576087adfe27e9e5714faf581685f007557d16221b0d69c7eca0030a7f6823edbe61bddfe342e81a5c1efe516b7c320689af67d06f5ebc9796e40acb5d65f7dbbd6209eb260bf9618df8e6fc7236675d4abd623c752e7a54af0c54749cf1b3d4f7b68c655ac494f6cb42365d81fdd45ef6560d81f452b962e3e41c1ecba1988d565f0bdf776f862fcf8628c31c6187718771a5f1208edfbf77700dadf8349bbeedc36b1ab1143445be6beccf8e9f78527336553d67e760c6fe05dfa74fceebffa7de999fabcefbe4737f889d685fdf0e2c05b06f89f4e590dbe8e451dad200882fafbf408ca385a752aa75804668f69bb1705b18a75bcf7dedbe5faa29b6c92ed39cd9181edb38dbfe609f8be6177d7e3bbc5ed522fd57ddd353c9dea348cea1a47cf57dfe75a5b841b362691b6bfd8a3fb01ed254aaf91ee867d892e8c14726f3f170716dd22b0e8e6b6e32360d135622d98849c955995d8f64f0eab31b0a8b62c784445029337b0c8a828b753d0adb5d674588dd59a52efdd5f6dddd7ade8ad8fdfc32ede92489e10c4ba4a4bb7edf6fbaea806b37df17fb999a3e28773b76edfc7428af0907d7398c8be4645f67d6cc45ffa16e270b8f30ccf73b4e01d4b2530d3b382a127d5a4fa5d8f4ee8160a3948336b38db11d1f65b98744110e73048bef86d712ea74be667dff475933ad6daae0b71df771a87dda918dbe26c6fb8fd09d948f7fbca510c4bf7bb8e2449f2fbf42c47b184cb512c618c35cee1221e588e62f729ce81b933a7e2549c9f8ae3b58ae315c77b107991990fdc3d12d443c5713bc4856ffb02df76a7d3d5ee846ecbf7bdfbf66069fcbfbfe597486096da567bcbf27bee2e75ad2ea10b0b937ab02b4a446152925049f770bffb30a94464579480581afff434fdbffa7999e6d83d777ffa2c812b01c11ac8b69f9ef582f6710e23a17542ba231b6c1e112fe986eff61901d2a1e2781215671c6b6c9040d034cff3df9e2914bbfecd89ba42a5d2e8f30a663a96ba1939cee0afeeefd3d8577f077461210ee72eeca4e18240d034cff33fb7fdc52ed77d9e2f7e5f1de2425c88bb158fc0fc4647edbd38f0febec7b43f5dab2bfd429207ffa771d87d533176f7e00dbbb1c6b01ac348da0e71d47ea9b426848875958216f4e12d7f8c0423f9f9b1c1f6b03a45081323edf41611e576edc1ae45443db6589a99d706babb9beeeefa82342b98f6f18fbb9bdd960fd9db6a8c75b515b95f1a88a72a98e9b851235e40138f36caa3cc2087dbe28fefa268c79fe1c2feb6ee83a0699ef753b4567477bfbbd3eebe760f8e250f0d9e9e2cbfcbe397e3f823495e9194c167eb533ec9a9eec92f5f14c572867ba6a0d46c93b7b5689b98fc68d2998c265d67329a746e6b773baf7c19fc959ebc0ce3979f926306ef4d75e393dd58e64f394f7f036ff11c4b9c7b1c75da7dca87a48bee514e9dfa6c141b3d1609478a4e894f7ed5e9f8a912957bec4e4fbeeb4819a3a853a58e81eef1c7d3a725f8637e90d419c7d4f89f1a3f7c707c74935f8ee4add5a5659071947c91cc29be6d6f97dfc2c99ed62ef58d519b882511982532f86bfc2e356a37c9a9eec737f9929ceab40d9df1756a75998f03dca62f79d2247e6a7c2f35ea1e9bc486ef4efb68ca367cf7e4986dd3f8a66cc3922f96abdd7d69539f6df2c717bfc5256f80db4b470d8195985331ccedbdb93dfc2dfc6e30db58c0ae2f7a9ed6f49cfb46f776ef4af6a8f68d4e779dc648bb85af2eca9a4e1067dbed6dfbdd8fb7b94f4998f67dd7dbc68ed6aee441acabb4a36dfc6559fe7dfb27e18bdd782a32458fd4311dc5e28b6448b4efbb113ff2d96de1d2e399d1f926924e4110c9acc14efa0d03b13f52c6fd92c81d88df2a0e09e37ee9328e92ee1aac3027ba39d14a0dfbbf5327721d1cb5da6d621defd135ba4630d0c63f1bbfff388adde4d3933fa1bc45797bf2f6e44513bf3ab536fc3b92fcc7e1775d177e870b1f23ada4504c52dd4aa7d29d1deddefa0a57602196c6c4c4c404e3aa0be3213421258f53b45afe0a4d45a7e029e4bedd288cbbb413549cfba669da4fe9ded4dd22448ee234c70e4dea255bb6a96d745e8213511e9b7c8a8282f2a9d7de6b35506a18a97b9453761deedbfee4b6ecb50e9827da6b8ee23f39d16efb825d81576069f08be32da9c06de1af229289939078a1e290a2ad51f237ecbe5fe61a76a8c47b1e02fa580913075d1307f57014c951fc25324ec25b5808071989a5d1da37736a7ef765ae61be7d9134f37d6bffdefbd6fd1a555790bf3a9dba6fd2b8c7bdaeafcd517c61464646464646924082f135ba34ece31adda36b74da82997271874a7c090d82a6799e97f6ea6649dec6357c4ac2290eea8492c09d8f837090d0490b26c6e17247b372ace6edc151fc2509a3b56f1147b1c54936f04ec2519c76421bbfc5ef5d0f473146aa2e5ce37e7a8d6a1bd368387fd538ba47a65071c82f9f24bfcb69a9537b698ee2b0cba9fd1b9230ecf5e128fe5bcb82995e215a8a691bffb5361ffe4ad95bf3d7b5e1241c23f96bfcef71dab0f1cfc6483676236fe13f02914c4f3a6da3db74b0165b1db414a8dcd66b62652c73f5c106b38d3067abb5ab5497a552ad6460582bcccad9e57bf950146cb5421898524c8c282333b2542a970c0ccb458e39cb3c8ba552cdc8c0b0664a4d43f2f8a659d1d0986a6acc13274a6080c164448102c5898dcd69b54a61da14afd389498989d2921acb5c85ec55b6b1d910f5dc2fcc36fe9c6d787c2cbff803f4b155d6ac1717edd9b447c4c2b9db601e62a954ab15986d601b9b0d118f6f6f85cd15685bfdea7b9cefb6b15dd5dbb7a99935c62213b0d2321aa862bdb8b058d85d5c5cba979717300fad3e67a08fad4c1335d1c75f622c730db2552a95af3cff2aabb4e9bdb8b4509657637936ec92339a59c632d71e80af5f54a93ed57739e715b8fa7c641d787c8cc5ba3506ba606d42d5b2fa988c591fa359668d95afcb7f2e2f2a8d9a1f0ac46e6155cb58e67a0b9f655d5edcd6bbfab1cc316c14b60bb3a9a1b0a12871717171013fcfc5664a4d3bb5a9e100836d3fad4440b0eda3624bcce47e79015fc09797cfeb5e40cff4b613d1514451f0f36a17fc9cb398cbed9265b41a8cad458e60f7b1c632b3d825507cf1e5e5e5e5a593792f9339eb57659588a224cfb75197ee9321478d769f8f2db055daa1f2309a72cedffa95ea4b5974bba2f27c1b84e9c161405396e960ebc18180624c1783b58b1ecb0c80bd52e59f1f25d3abc4bf0113d36ab56c8c0c0c4c0833c462b1645c31a59818531e72f997fcb37a55d6210197174b59e64356735407b711fe0d70876126b74cae81456760519a0b0f5ca08c96e95cae2f7f269a17d5a8735050ab7e955739ad9687616c45576399576c181b13e33232d7f5312f0313bae49f95ec05f5847d990f8bc2be8ce6295cab1c2393aba7f05c2ef275d6efe5f1bb2c7ece59a59ae94072e46186a8f35aacb1cc381bfc1b604c76cde40c43434353cb19a626a67a1e26faad5fe51f1e664c5513d6ccc0b66e60796051d8576519ad26ec6ac2eec407038822d471932489fd28d9903cbe436d03e6606f607960695e606dcbe66d254fe603053e60b2ac765b35f135c2c8318337e59833ccaf56bf5201935598c69c5f5e3e2fafc8298171094d2f26e645c6046564d0cf853d958bc8db33301da68951515323734fc86cab98132e87018699986c3d141ef6f2504c4c9955a4084b312f63996f5c33341a9a1ab7f7deea9539d7980c3a7ab9e4bb6c7ecef9cb0cdbab2ca3b93c857dd7c92b315d2ed255e657ce34663480441a40175663d6f88913271c868b027b2952b0c017edf57a2f0fc1c2b1abfb262cb19e29738a4d43f2789b26662cb3cd2ee59c1f061864b5dba2b90f143629768e7690bc33ff0beb5fb24d6da512c392058219b2b28cf679d6fc3eef13c14f044b288aef51803244a0e645ee541cae9119cb8cc2cba6efb2f730c060f260b0362950a0f0142f1b9b142f180c06f3606f4d356ff4b2f7373737372a4eb8c632c3b05bf947e9750e6f803d36c9fefe06b8c117455566d9d8b0fc054b9142d682eab20f235d90fbd337d53ab95f2fefc2f0388a64a86f3c153cbe45152a4a2a4ae0378e5946c301eeef55f9470582ac17172038a18f5a0fdeb28f0343e7c381780a1dbc65bf95d6203d6cfb28724afae62600285ad45bcbc6c6a64684195519c8b69f42063d81593f4565664655ae3195ea61a40a16e621f4bd3c848aab4c63267b9d69ccb297b32c878a83e3dbdd5b190e152705e3dbde7f382611a018def63a8d63ab973466d565ff86c6eca66623baf95a8d8a131f8e8af5e2f2d1348ed7b1f24a97644315c5e8e521fd611e6abdfe52ab9413e250694a2d9d73c20e850da913430b92e76e50b76871b3b78a300fe552cc8cc94a50d08c65aed93fc4d7f9e74789d4297d78537a18a96353b3b1f92bd4261ab3d0e636f70733fcd4a6665373d49a480b9a3b3d3b9b9a8d0e36361752a704c3f7309031beeebdeff44ae3e8762a86f779ffa52c1baa32195414cc37c0fd7a98759bd75853903a3b478ed26cd07b1146ea742f7e79e7c8d3acbfb0fcdfafb22ab372bd2d534da7104037d2ce11476dc51ded9d23476dce343e93ae6dfb17a431bb3019adf411996dfbf7c51657cc29989ede8efd9d233b48fe7a09df26fcdee6fbefd31140772ac6978ad17d8dea28ae1b469e3736459967b67ebd5e65766d2fcb685ef3141646ea745a862264aa90f93051446056980983955966b3b28c76c1dc9363b6f1dfd0c21bd3621f8ed66c3ddc3c7843de9c50a102061c1c142b56d8e4e4a4c81900af9c5988b0d1cb43ac07b38c565d3fd4132cbdc30d73cb70a834f66368d142c5de38b1d80a992c27000100808e0e0b172e62f817248f6ff0054c7ef1e2458b9d9d0d03464c000290c1c06893766b688cb75e8c4e0740061974c670e8e5bb175d6fcbfc9c65341991f7323e6f068a8a32c3ac58617330d16dd958b8ad178795653bd496fd17972cfbe107e826c67a1e60ec87c7c77e7898b12033d8f6ab4591d6203aa4486b90da8f0e9b24f6437c1e608ca5c91f9fccb21cb28c66695a5087ea0e95878a83dab22f66d90fd5657f450d520bb59822e73582b21caacbbe8c6669ec03e02facc66adb0621da31f166cc5946bb2d592d886ddb6721d359e6c375b0396a9f05e982dc329a09548c3acb68595693e52c8b8174e15b4683eab28f5202b3bed8025fc7b1d52bb15e51615a39e84b8e4b0e6ba5d23939e75b72724ce4e4b0e494c8c9c9c951c949e5ac9c393727272727e7f3723a9c93e33627a7faac94d55d83d064be4f82f04990eb93a0ea4a4f3ea8e2d46a0493cf3b26419193eb381fe2450e2b7d7dcfd5eb75e611f3c8d1eeee1b04d3713ea4e87aced10ed71f6c135c75b969b471cafe7bce5fd6a61439ce831c218808deb588224692f057753fb27fb0cdb6534f2269418e70c44eefcff6b75e77fa2b3d19b7e9ee283d4cfba9ccb6328eda3fb1e1255c1c2ec326d9587b96c67a17fb68af2532efb661375871f01d478bff627bcb7b75575d44a6dd9dd7007b1df63cec75f85e04789767d03446ab873effb0dfa4fa69cd3da0d61e33227e30dbb6122182d9f61c626945726d9dc1fb61fb579410d4c04c6bdc1f3b03ac065b60fae0fef565de6deeee650f33759bbbdf0fdfff2facbadfe03e9830f0209d9a1af420ff21c6c04cebdf4fc7a0f4d668dbfe1874615e5011afe882a0699ee73f8a7a45dbde21db7efa5d5c87d425d920f7801cb5efd9b0adf7b36d87c4515bbd99d7c3618e745bf6bd1b3c1fdb6a4f876dbd1eb6f5807842b6fd950a9ae7df1a9227995eb510da1cf523db7a16ccab43221df650dbf6492c98d5fec782217e4032b3eb103f0cb171857da712bb821604419dc2a7a70fba1381edfdefb4bf0fd756f83a5df9e0fbc27004d15f9967e5815857a7cab22b0ab9eb77b5e2f8a6b16536f8d7fb0f023235ee5b1289999af6af061de37b83bfd7bc353988f67b58205ee4e60009a615e9c088eb605b204168add8515cc4f47d5da57f3bcf5657ca7b1b9d4e79d5fe50083aacc6ec9fce50bbcc9f1fb7becba0b46b1058c0813502178b9ffdaf56179871380c6cc3fb1be0a6b13a83f8bb37fd75635ca0c336060d1bffcd2109c3fdef5f5286ff7d8f74e1b3bd922ff00892e3fdaedb5b7e5f4d7f61fb9eda5b05f04ddc79a2b992228447c6b80f7ed7a5e0cceeebf66e446ed8b7cb75dff760747a74d4b4ef1c102faeb7852b1e417f5defb1f63cef3deffebda48efd7b53f6ed76247c48db7f67f3e787a8c55a5f75a762c640f847fc14cfc4c7330ce42fb1346afc836733a08d576076493b09473ba4ea722f775b2835984f7029d8a838a5ed475e841680ec5a841674d8e977b4fd67762d220b489c60d6208e1cd97707cf822b5f54662e2a3396ca6ca532d33a6bd3ade575c265e5c5d678423b79c209ed8476423ba19dd000c022a77e55bd903a578be8788798e97963916f1e0059c57f2ef15d66f91419e75bb289cfc9b0bf7928c5afc8e72dc593452078c9a2edaf326321a2b2e06e0bc95f2cb9932bd4170b121575584d0d8afe9fa7ca096d3b0bd24952d26df99bc8285175f9b36494360bdf9f85886fe3589060c9f9ab565484c16a6a50f45f65565566b83d7eca82b4fd5564950a965545e80a4a64d52d954d5e36280a2d058aa28298960c8ccbe657b04d0c4a0e322d7466a6c5044b89d48aca3993824299399dccccd4942a5a75ab2e7fd54d754b5faaf0b28514ad0145dafea7939cb6825295d1f62fc9294a031488ed6f52dd54371487ed30f0e1bc6d0f7bee620cfa90088837d21dd7b2adf8c0e018d8e1d6068249aa71871fe9a2b44bb5eaf2477181693f2dad4c76157709c50526abe4520acb2ace4e5710ebeac77defebbbd5a05d5163dc24491000f4a3e766d7b8717d6238ff78cfe347cfcd06eb09ff6b2b4ebdb19b69d8d96c36bbe0cd34b0063d853f7652c6c9be2f7ee3fd71adb5de959dc6e1de77f7d56075f983a06944f4477d67a7053528dae2b66e307861f7f1e32b5eecf6963b3b15a75abf3310eb2a0d6f2ee4868db01136c246b70b038d6ecb1fc46da322236c746120689ae73f8ad6d460a30b03c19a673b3da2cfc8c80332f2828c3c21a30bfb6eb7a31bd2ed76611f8d86a315e52eecf38c4e22d3f67535f0c8443abd24d00834028d40231034cff36b405abd79ddadf9c21938036746464646b7dbed46bbb070167e349a7b33cf3bfffcb4e12c9c85337f85a622b09177faa9c59b4f2f9fee973cbc759e77efdf6b4560da172d0ef26bc73b626bebbde5d55f75b91512abbfdc74ebb6769b30f6c73a0567fad79b8a716bd4edc3b82b8eb82b4ea90b4d2653e7815697655971eafe34496eff8a5377ad38deae15271c4b63ad38647d599bf1f7d58a532b8e2dc772144b64e95597ffdd825882bf9a2515c7eeab69aacb9f86a6e280b7a502d37e8d0dbc882719c18317f9ba4aab116e41309376379a46a611f3c85fde7f6f1ef117f9df9b48fec2ff693369a72741db92e477def7613c8a251193188f62d8851dfe3c92d421adf55230bec7ffe911e3f7def3fefbcfc3a00a0af5a5427234c85f273d4e842e4cbf7d5550c529dfbeeaa8e2bcea0aaa235bc58510ac4c9ca0be5a3713b48ad3f2f64d08559c9556101337ec160b10f50523c482ab3826de3ecbade2a46052c02264c3a864a0be66728e1e5dd88cd16d9d45d4d74c92cf2a0ecbdb3f8d2e4ce6ed9f45cedc5954714c3f8384cacf4dba48335da844b6fd1306154633bbad0c54184d2d650bf5455354ab382a6fff9ca524559c94c7cd8098f930a3c1a69d27304350c3a6495390d0e48e6cfb2839942228462846508efc65f2278cd8b0ed9f6ca1c250d84e4e7cf8ab767282bbb097b77f5274612e6fff247761adb77f627461e8db3f39ba3098b77f827492c44952c511dffe89edc256287e00aa284e19d827b9b563ee58e613a2ac54e63a040f30a65f14c17128e651b20dd4ebfc03fe8ffe925765145385e42f1441a8a8ea0a2884d8f62d8a22b6fd54c5052aecd4a6b62a62db6fddfca542a815a4be54a440050ead20b67d18217fa9408249417da9b8820a18dab63f83447db1e0a1e6fcc5c2e6e88cd185b1209a49f2178ba2992ed4178b232a6a9f68c668c668dba7995d188bdc6dd1d42e2c86194d91bf62a039a2be6298cd66b3dd42538b81a646538bc146930302880c08683ca2f4374c93936a4dc01be02ebd78b25e9e88a7af5e2f4bfae4b6de9232572336cea38e5dd2b175783a36d6b1c515aff4b48ce63af66964e59757655636692f35140fe7ffb2f738df20797ca3720ac75a462bc7224e4c459c506c3d3115a1a052526cf50f4541516141c928e00b0a850a5d5252524aacb3da5a5c3433e729ae64744a68998f1c5c34332a2926151445ecb0ad8c221947cc9831c33683871944443c369e5c018e8e582b1e19d4786c365b6ea55905383a420b471c6dfba9540995c95e19a2a8fc151947c82812223763c68c204444265a56489d4aa558ae10665a83d05e98ecf345938c8cebcb9c4be41d238eea1d2347ed112c30c6d198053141a531db01aa38e7dbdf31aa38dedb5a841c14581070b40216937d712e4af6017c98d5b6d45ba63ebb84e96535b4052cb49a8d85c55c49b1b4b49cd8c326722d459c4100516d2d656ed9602733e33261a2c444666556660195f0e12d3900e18e8648424b8aa9c58486f5656659e5555e651644361e2a8ac2c3f3e54a8a4646ee6938ecdcf20e10df21c25d1e703dd8fcb54383dc36cffcc2e845ae86174a00c20b237cb9929a01610b40a80210a0308408c84d082ec8115acd4674c31511f19ccc08418e8228e15558dec57a2da395c8e229a5b55e71bd978766be64e65daf42c85f336c2a52505f3378e0a9f1c8c0c682872222886610cdd08146c943d9cc059042018e8a8200c2871a0e2708816b46b36644b70c04adaaf512e5338a96d14c542b33789861ab6126039e1aee565db4197408d932206aa6ca844a6505ae625629278bc53a6d52cabb90431609c9816633170af2da90d34c171417958bca9719a55f5e625e565428601eca2e68b6ac4613533232aecf79e6cb7cdaa85c5914f98b27e747d4170f1215b54f00a1198064e4642051d1da729a2d9fb6c42003fa5aa6bc93ab2efb3c45b6bf768c5051fb297987004217b64303a00bdba9a1b66420f12323e7af9d0c54d47ed1b6299d9a3e80bb96b698d0e9990491999191a323488ef690c9b29a0d5794333a429225c990ccaa2b072003b515b44db5757e339f33cd97f924b3322ba38c5081c3b6af02c95f0538527185fa2a80142aca8287fa9aa1838ab2b0f96b06110ba20be3c9b12022daf659e42e4c46eec630bbb0194537869abf6698790c32a8af197454d43e8b1c8bdcb62d2de719c32c86591a83ad08080820db7e4b96d1802e8cc766b3832908d0b60fc30222765a7121304268db375180a302480113edb4062962c9325c75d9c733741064c826f2972c88ca93bb3099edd4b9d44ade21aa2efb67de9191bbb01d1f66145dd84e086a6be6af9d1aea0c3aea0b67c3b69f2b22082084c8555b0b0c4e1c4cdc098628b261a7d50748b63dc93a4759e7880e924e123a498e16016dfb31321bcd4c9615e58c6ecb1e5da4db4abaad59007e0200545d3cb60b0b400db545c40f44d85c98ec14bd03b4025396792f0fc9e8db6d115d93a1fc26eb4bf210100864c6585a070db3592c6b592d0685723a31294929514a283185d4b1478eecd19922922a5e37e4828a22857d15d205b941bd524b2f491595959134a5cc122652c76eb18485acbbe21216132d2c5ffe402030f6a2ad893f3daacc55882d9ea78cc8da545468cc54545e320da12095a0956ea55ba131a33163e52cab5d184d66da142b67594d76bb30d6db97d164b60ba3310b42b4ca3466254ccba20144238886d01311fa58bcb72ff3e12f8fc6cc51fb26b0bf6a4bca498ccbf5acccaa0d79d78963ced67992a79d876c95155bfd2c7315c2ca4aca4b9428c1a2823296b906217c31654b9428b1e81b842c26dcd6bb821acb5c83d8254a942861595858fc47f7264c5cb06bc929badca552c632575c688b2e0e678d866c2e9bc8d9da74cdd66e4396a8057ca1b5b4fcdf3167fd2cd6afb2046ecb3ee0c8ce2a608380802c6dc80aa9c0ac9fdaec62c3dd96260770516e3051eda9be525eb1be12e758e60a04d68b2d6fc27e6e19523d2beb90809849b2ee07b1ee54deea63792f7a329a75015954c632d71f6c70c80923dbaa48cae3bf8f27f2499db1010c30fab4d7e595ea65342bb3922e6ce7e84845928a249b1780805c265fdf536765b4baab4996d5481d92c60cb60ab961db076f4b176dfb2fb96ddfc568db6f1d6dfb28d2b60f93b4ed87b39f6d5fac4280b6bde1b6fd9da31ab67d2142382d6c2121c01dc902a57da8f48c3685418d32453322000000d314000028140c880583f18848ce73c5c40114800c838e566e4c9a8883510e842808830c52c6106380012023002355a509c6b5091883e808433be56abdf69e4039ebaa9d4e09a89fb4a60f19214f7947c176ec0863a1f1a8a45974f7d9ee137398689ddfed50fe3da34a5cd99556392b86c11908fa350447de7e67a29aed00c92a04974e516d3ef96947a0521d3c1ec757da9c22ccca1a2480bddfbeb2e470a825660df2799ae627f979779a3d54042960602986d4c39443ea1c2e9902a099703c427806fd51270ad27c62cd9d0fb23048abedef36b1697af5303d8cbd6979f4e402156388160f936f828aa58601b67e46f47c105ccfc2b6195c768275361e95d23c4b96b67c256ca2229ae5001c282b97a28d64ced6c9ec20611c3dbb9362150b0cff9bd6156b810b90421dde544a62b4536723054fff2c8ead12d3b61e2984b71190a968e427c9e4e6d33139a73bd1a737e5780dfb4f3f2f2d0abf65f355c4abe4464daa32aaab8826a89ec29117805af2a6e5c4dc8a97a486ec1a79847497101bc50b3ca417a68f6bc580e83dc371a2b600a64819eb672843b9912f661d4a8eb6b6b16df6919e8eb3f308a894ffc40bf64f262bca56d69453f6477e3da59530080ffd71b0e85dab72a8f3a26579d47cd1e29458d4da6b5b2d58ad8793cd4950e3554b25a8f14aeb2eb1a86d9aa777395a6f303691a0c66b2d4a508bbcd95a52270f5d5bdd90ac18496abe68794a2c6a4d33e55dd4f41e91960435f09dad25752c942e0df4bbb33a4a4d65befba067a83705c5558efacb68bada1158e3f6aba37de437da2059324e2899715facab132c9d2938a4affe29db2b04771411635f9520c0379f6eeb71a3a8e6f20d43e33be707dfe7c70f7e564043cd607d998338a057a8379981668e2caf52ee8ba5298411039a1fc2a1294b9a37fd201a2c371ee84500a286e4871bb431014d1c0bd7f7835d61caa85c06f9945a5458074664b9ca71a4954e28eb4388ca1bf9d6cc6b23bb1cd8ee56c0713000629ac76b3270920af5bcaf6727df15f81f77a031c4ddc87627769e2fcca217b59db7dfbc4748ca06641d041f193d5ed4634677099ff9cacc2e1d9a6bd0f41044a89cc14e0c27a7b2e78e5c5b2e2df81d4740cbe21c467586088e74d8ce12d95ae56d7712e23824299eee9c8ceb8b57b272bee861f29a92642fcac065f0c7864100547151a1f83915bb4023b5c3d4814a7b3c2066105df6ada65013423d6191f79af18c1523bc42976f3dc319bacf93339c30179a73119adfe3843cce689d1cf46ed6d6fc28d24cd1b0feb41d8263c3d9c211d45fd8877701fe38d726acb429f9279f678c323e860bc56639aceebb898754c7c8a5f42a68213c2cad9a270b85e002cef50cdcf94b8b423053f12868eaa0d443c0cc4aa904d8fd610a2fdc0a42a065f539d8dd88f990708439fdbf19f95bd7fe0c0bfb36ce912d3066fd3a5c78e873a10c62bc2f09e7733406c1b03e5fa111dbffa6235cdf0074619276d5ef00091cc17adc174d343c6a438fee96d7eb723eb296113174c4c064925e509235d6cf1d8d2d40c68bbb246b44ba2122ba570982b2a64b13251ad99f1aa1713486b8293d3ffcc812d1202f9e599b0825eec8d1b82fa991338393d4095394d8299853df180e5d0e4ea02c34785fc810514e7a4bfef4488ce33c017af1b973dd4a9ca6457d4a5b588d80591d7bf8296ae69ce869ac471680dc0ca394d3e451c6009b59aad6d1917c8e92f44668b93276de23d368f41ed279d8b5bc673ecd34e601ce02e4b598058837083b4c8b98872a839066553a33b73548ad4d905fa7767b725075908551485718009c0bce2394abf1d75ea029b6acd478e362946218dc0411c6d8576b66a23b09ceb1c4910f32dfa31b9bc4bd80b922d5638720a234347726e9deb6e90ed23a43ee0272525002ed278c2b01512cd8ac5335bc5f1aaa6dc86e957882254e42bb55c24b8126d989f7dbfe8498bbf092d8064045371a7917a686094bf8562adc03f53b21e9f6fb54e4518064ba978881a90e661b3ec64ce023547973ae46ee7f1cb9ff1421895e6dc8dbd13af4394a458c80cfa553acb7a3786380146f89af3ae211e7fae2d6f23130b7c7d1a5b73caf57a3095141c57a750ee4fa9f229b98f1e069176556baaf0a3f4091136b5378ba19bd259426b08b826902c5ed35d6b6df64b5c17d4283eb474ed363dcdd27fff8b229b3fbb3c5ef641e20a64840587e40889833ee8a60d7f6615e59798c487ceb86c59a286723ec4789a82e193683fd50bd3b1c2eb413198a433dc06deae7bec7278525d51aaced8af4e130bfb13bcbab1b2b29c75bb32b3b15db4a879d0c977f3fea482e288e75b0d46c9cf8311d47695260529ae321e6d8fc586772b2eb9640a6e8627af0eef837ef9dd1ec8c0dd342658bc36404976ed9f82433f460f48794c483cac50b70e8ee29ad2d98fd8c85dcefed2cf88f9184d482c1936d6139408b1d14eb43fb194b6da275ab5394829329cf5ab66ec4f700927f8b572957279738ef92336ab65898adddcc6b68269882974c8d064b59bbcdb50c730511ce915b6ff7c2e35d6bf76ad63ac71d6458b5696117c43261a89e013017f2c35d96f9511e299b2eb186c34bb09f8f5a7aae8178e1107557bf5959ea5e3fa435aec6982be19becacd97b3b08adbb50e389d51cf87eabd8e6fdac5834ef451773e7813a24c45c029796ecb778ba42b8f53d1b1c46ef011ca00c61ddad70edae6f5c70f1ac80a4b33fb8f7fd3c6c838386f0402f7b21e1f0f73bbcc14ffd83bce9e5f45114a35a10c5a891473162a5e332c3e6b7fd4fdc3d3f7b34ff9ce502893e1175568bd80358231bf6cb5429620b715ff8ddc2554a7d3a5fb9da886b0636ce6472dd1d0df66664fd59cb699d7011ae071e47a8c670c5882efed4b4db230eece78b6f8ed2c4dd357bb12849d97825714f881e0c1c080d3a76b8f8c28f0540b29d592fd0e3c7e17d9bbf1d40acf1bef1f1cb78205d46f9fdf192b4829efe5ea3fdf7f7518e7f2b9e2c0d712fdc4bdf97cde4ae3e64e6988c8999e20b17cca453849808d48dcc55eefeeaa60d02261caccbedd1ee0214c1ce39190383a17f7b3970ff8d11bf5ca8b683cd2beb25f7e53d00d6849ce6bbdcd98c77cf35902931a3023ebd7f79b79067853940b2d382ba292371f31abb961d1ee41588ff276053abdff8f417f5d85eb6d49ebb26e38d500046eb9a44a86bc08972d6ad305b4b23ab1eac79ff11256f47f19a72542fcc2ca1c717a00474753623da681fc0f4299e7a3ab85c553bc58d1d93df095a379dbb2550ae3808d792b536b6fe54c2849e6972f809d8db7bca50f2de093990a362c0e3dbc6bfadb6b11b09aab8d3a6a8e91f187bb880ae838d1a7de5edc264b1e70260dbf6987ddac91df83b88bc205c01ad1ded4baf06f168056b4b1f0a8466ded8d3812b8481305684714a8a910966cea9c2a7ff2432ea63ca136646002b8b955bbc4518523a05ef0da4e91e3e7870c4e3d36753e73f4dcaf5b167ea1fc7ef956f880fe8eba7b56ac5a0e10fb0e58223739ebc8f51ea6f85fcf6f7f50213a57e2bc83f7eae7f6051557ff34a46a9583765414b5ed5fde3e3167bc53fd75be093b53bb6a9945b1e004e379aae9f7a0a166ec498857a32a6a97201c373e21605e6e9a4a279f9630b73d30f29590e9ed422391afbf437a4ee3f746d97a066ece0a05c8908e0dc889d81f833fb84da2fd261227de20351ff1d44872487ec6e62d799f5ea36f0e5f3264635a0c061eb7318890793eb7f4c447af13bcc581597bfb8b612b71b09f99e498ac2813faf7d5c02ad045dbcf6e41ec8c94063d6f20d7cd6cc83d825deac77895661fba346f7a7ecf2e484087274ae02793567c21fdcccd70bee87ad95d7bf864f79a2d0858cee5ee5a43f5c3f9060e20582b66a06c514a99a4d06b20d332aa23a3cba697aeba4ae09bed113e5ef674068bebc219911c03afc2954e731d80dbcb5b71df90c63c3311161d1792ca2d71ad82ef44362f915cbfa3535d4d22fa800ca6dfb73d9b9a445fdcb7e3eaaae7d314807cc1d4db8a5f2a5f67ec0b64069637c24a630a15a1c36ea4056dcd2c6d965d5561b896196066c73fe596ede361a83ed444748064c50c88d680633ec18bb77e125e85b225d57ef06b9599e201a677bdcd3dd2ad52d3a402ce687d5f830383dec0e0f33b3c3d43a4a3725a353977870652864b4abb6feaff8bbf4990cb7b6d134364b25ef05cd2d5cbbf4f52da459d54c873f1c4cc31560ad74878624ac4ab6b13fb4c6feb1f57a57f3436c711bbabd0e2ce16d80f6fe0c05c8300b235f662297dc64c190aed5e75df8908c06f29d762f37b0a960af6629a74f34bc0241b05142018fdf2067d0c22cc32027155c29c6b811d9299ea46496a7ff80b3a8d1c59b3da012c248644f0404ba113a4d6f00d6464b6e6a750fd59c997406f219c034a3f1fde0dcae27d3bb4fc7df40d3dfd9e25311bf10bbc6ff0b8ba146cdb481dc423e30944640e753379d3c961c265cb170bff558df71199c0be9c1b00314a8867ad673756d455ced5ca0d43435ece554b6aa2159f0a64fcf4606028890ab97e830035ffc08f9a11cc96be9d3cd06b83ea50e7dad4ab579d303f180d33ea209c9ed4c99b243de615fd44079b10d871b883436fee4141c90293044657ec9ee8261515931f15d47f1a28afe2766a321814038b130992b486a2779c970d12dd3bdfdd84e5a20c9917d0aa4cf14437810fb2b9097b438ccbc66276fd93723f7993e65cf0a1e5f9dcc07a8cf03535c6c8e5a860f6e2881875a2d9b82dd300ddc472cef82a1b4df29a8657c56b4d09f790200315a9a8a43e2904df5c207690ad76c50b441188f4c923c92fa0401aa5a835c0f1f9f58b139a2e79c41680815f00f6a913a86f90b6f40633aa4c08f4e92df40b942281ac916e7eba072fbe2231b23ba98bc3087b4a2c121e27fdd469e630e11ff165db288afe89d2d210347c63e6e3491636796e8e4ebe7410731f1651df68af2d96e356f39437cecf9f3174141f1aac1394ad7b3b2dd6a5020cb9088517a9c40549e8ec1a9276650b69a4e60bbd60f89e4efa8e99f4a54b2ba114d19115cbbd4921adf6c85df873ff9d762f14f55035e6c7f9daae73a58fdbe5175a0ff93c4eea0859cd8c7991509e92a5fa9ef67ca4ed94ae40f02e334e092a05310f77e32bc74a4ba868f7b857d29889e0403139be1024d6801ad2f48f5568bdff8fd377ff31bbffd86c61e0eb226161665d2011b10e99cf54cae5dbcef38a4b5c18097109a4368526406c05e4f26ba5299affcf98048c7b2dbab79ed26c40db68d8a76822a8d9b7a17a853ee46d7f3b193304fb30dc53e9aa11464a8b990d01c51a2c9710a37dd9455c584cb5d25dc16b86bbcdf5f780f220658ece930fb1870137242db940fb414ec17247fd49f53e04072f2d30f5c5f39cad389febdd3b164b552fedf5717f1d62763eddeb7dde8bb7f593aca8f131126992277d79107d4299cc438226b8278af105dd2e11940c9931841e09d4b606d93912120b28aaa1c14ebdafc5cd110946d623df0337b2cb05ba7784bd7c9f336100d428b271f2a55f857837afafdab715a1659cf6610425115cff7976546f28e682069603f972068829b6609887edcd17ec14898c3a59db4e2c0f90f9d75fcd128785e82d8e975f1407bbefad9dc8ebb6aaa6c75a81e83c5d92d323cfcc34f0c6b0255f8d4e2fd120acccdfd37f0fa4b4025275a30ea9ad97087b404a4515f750392760bf031d8fdef71393b775e59cdeb64d241699d564ce53e2f0afbdefedb9d18036cb9134fe0bd39a56b0ba2aed26494a7f514572fe112977b41e9c3a224d79e3db20a044af557471d602cb97cfb074a6d22822d5de5755c20d066c65d1a15cc28f3dc4864caa32342ae5bba60a08d4b08342e1347927f16ddb6fd19547445cc2b1a7771c15e11948b53d787f3fb96f2da480b52281015df001f4079fea6928e6a7b7232271636cd5618ab342b1787efb324cb9a52a6af388f4a7d060803c61b7033ca2d7f8b7702096348aa2e9f9c37c11f131790307e34d1c34b9cccbcbc32c982dc7957e1a0da009463441f06ad259461c07a62f4b328f9c218ea7ecbddbc88a5f4c485928a4bd49d7add7c62f0f32d48c3eb97f5e61e398574a8704b0ccc835eff3e161989bcef9a6b19bdbdaa9043fabdfdf52e6c547bfd5442562fdf03085f952ecff61d345ce54278416328a8d4ef93fa81fe6284402d539502f45d80ebfc6d00d185b0a4d23aef1575d3d1af41bb9b3d9c8c4e160ed17042ede1264b8d4ca42249f8fc8c32a89662a47d2ffa25dec85daa27a8f65dd18d28b22971f1b1f1fab3df661e1b5ca2cf1c4664a39565d6683070c7d7eb951c36aae325e438f44fa77aac69f52354d8d9074b3858c5f621d5d2a485f674e419b2e13a6ef9addf1af5196c0e3d6d466bf73d4615a45c095780bcc1f78b2365f914064f31c9a4a3c6314dde8673fc32d0c4577d09d140cba7d17c551a550b7b3688e9b148e40741352dbbc9186c43f3466afc13c3cc093680557773160427c9a12f01d3439fd9389df1918936eea872845169b7e21ab294fe7540dd7a2c02cd499c3d9e80e97f54e4d2591567ba47d286f11ad7e6d5f2072aa3ae05fd31fa3c029d8553b21dbbd6469278955aaeee949d1a7a5b101bd188d491164f0fffc66052e309d0dcbbaac840c1849122e9728f0c3aa24d3fefce1a6593748451a39229aaf011d8cb04bac454838f6d3fe6fa1b9309a0d3e0c508de8e532511a07bca08081471ffc8506bd46525e8d971503c87785e64a4ce599dbf9df5afc41c15bf7099f88962814fdb15505cd25d8aa465d311c2eced24cbd5d189c5570f9241644619e71c65a79e7f9f1f27c3bacac573ba57378885654962e07f648a4d541f5875c2655eb10a548561d8b728e831e7f1702e1e2a0d770233281d73f1bf841fbd23fcc18d80903da434ec423859af293f2d5f4b49d0283fd54dab98a666b2ed7aa860293f6ee4764a9a1ca8577940862275d9f80740d707b8b8f7c883bc14f6644a8af262d30b719dc1bd498aa87405f8c1727488f92f4c1d91c96fd71e937dc008b296a6eecd0076d84a00c4bccb2459c241cb3cb73073b22d09bbc7f7e630a2768e827b59b7d901ee6006aace6093c2d87402143b00297d8cd8e78a2e1ccdeb32cd67ae65b927d33993a586502084efcfb094b5cc41ad23f72f48931dd982dbb7b36b99ec18c28ad77ffedcca962b23ee55d622d95be951d23b63e0a6e0986b3860b71b26ba0d9e1421a0d893034c4298bd4778846b176210aaa8e1e550dda396c164c80426b40eadc37c4c300c50417059c4675b194290acec667e29829038606003f3cfc89c51028f4782d142b8c6a72d00ee673c0bd424a6bd64a157307450a51c7d8e3159f242ef9f6f244d8effe192b107537d47ac45efa545421733be2d49133f6798acc3e69540fe4ee98ca6e69b2ae84d1381e8e62a70617044be3031fc2596cb69023efc11a02b14acea3b391a9392ebc37067a646c1eab16dff6d9598bb43d0bd28e2bd2dba4dd21bbc67ef3f55988503dfbfd91d3ae8850174d761a342ef31b46c723b12ecca0e3a0fe010d96cef893dfe8a6a173594213e6741d56a4e17a00c7a862f341fbdde83b7168e59910899c77440954ad3ca1c7f09616f0f42fa5a8e346984b887e230b02dc04d7e918da40743a0a6f634984ba4750b1a8d3311a6ddd3676b96e9a04f1595a83869ac8fb8eae58302e16c04990069be398cebbf2233f93e816a5dd7e13edecadaf64ae9032a26dc8a616854a66fc4accdc8f68d081f80e86f1c6d75f6750baf7aedebbaecfe63c7e0805ef9b25b35cae4c8faf802c2cb4493fb23daba0498fccb0e4c553b9cd92fa3a1eeb0c4d1c8f60ae62f88fe946a82ad9b485e0bc78245091b33daadea01cfc2370f3f744d55d63c6944cc81942a72b3485834d450e7678d3187899a16d6f409352a8993f206f4aa63dd2fc14042a1b61f5a7feec19b982378c131bb07a3bed08449171e657868fe577557602ee38dec8fafd571363f2394eb21aba0c823bfc4acf0c426fc85446af0861897208c66deee4626f191a704da503dc3343e201668acc539683549516047c96c9deb4ca58c6ceee39af0e2eee0088e37989a1caa3141b2977f4115d3b5f463a62624a1749b7072a1c2ffc600a0ce2974f4005e658107d4cdc2e44943bd2e20b31203620668110bc726a8521c20ae7587bc8dbfeb7e21ca488800804d7301924fdc2ab26c412d38234bf213b2b2377cea36be867d210a707aad4e0e040ec1ac80fd002d12ade423ec96adca27e82e58894fc03d5b539fa0bbb0d69fe0bb8e6b8ed8bd35fb402b9e9087ffbbdf26ee8aca0fa4bc09c68f8764d17c2a03e4faa296d22989cabe5271b5e486e8ee1d7185110db25242f7bd0cbe8fa68ad068204ae1cab341ac54a02656c22f09bc79845568b1c7d7b026d51b65f352f0eff6af00bb2952cbe10fb77569bcb36f09074423437cecf94fda7a1fa4b1f490c33470ba4eabd4ac6c7302024f2fd4a3a29f6b3ffa3a8d4cd8a3f319a3a3a24ac1a8eb3a4fcadef9a61b7f3d7359a9d6443ddbc72c5ac7fe34e4b77f79336db05e8a3513a358c273539e9e5f09397f1c2b6716f971344aa8b56bcd4c7eedb858df8f32afa8d9558404b41b3ce59f50209286da7568bc4807d028cc247cc441f75e20d4c482c8bf98f08f29ac4f84b03efc19157d350fd610ee1836b12b440d77454b5b30258b8be0a855a5163400fd6019c15bbdb63c08b05717f71069d069832ae6d850dd32fb0fcae5cb37aed9db1c8caca342156b1df0e4190e90db3ff0443fb2d748d1be88b887cabb9847df018ab4cc04fd6ab858504a663abd7f7251427a0c742c7c068e242674500b2b3a1462c3dfe40d12744a13ac0f38ab3c4e4ccc0d7a7423d835f1b9ac984834d36222b178928f7efdcbbec09270a00f3b477a9f16c417ec44a4154cb17f0cd3b4d61d3cf86db045d96121d421aefdc58651111374005c69548a629ceb0a2dd0ac1dd0498444648e852d4496b35f7226504fee37c10c784a26583e6a94fac084d25bd334a76bcedee91eae26e29a8064ad99015721d9120bf05f4e59e2ca5b7c5d0b8ec385c83e6095c998f8c685234452a430c94e92e1d9f2ab502547e91e9261c11289310c24f5366a39adc9e3b6c27b58973388f818932d552f9b7474131ced441a4f3336ba62910e9f1517d8c05d24118d341a803255356182b9a84ba65b7bb9185cc03ca9942717b144c5b669d72a56ed67561ff39ab8e2fa87ddeaae46f063bef663143a8367d4b555d2e3b157f030ad4b67e558e313555bc38dae83d37e8d5e46f77a897516d4c1d3dfea26a3baccba491e0df0415fa3ab2b4d768bea945589ea92ad8eb2062957963a68393a24a35baf50feb7cc9df8045c714b1941ccafc0acdb9cd1e9ab2500988f97186683cd98080204f034645618df47f609cc4d1c744324b1a9b0c9fd012fd1dd04339a1d3f03880d2ebc8c924a0273f88e3553f56e766c7c7f823afe0be4e47f590c6e90e4bffba0e806fd83ab72cf0c1694766645fdef3d8f893311590614142bb7f7241f68a5a1144c4937efd1cbdd20acc6d262070b6ca3c2c09777f2ef8594ed18fc586ffc14ac245a067f4e2bb058b128d38412e08e5b0933a8b54cdfda9373749279d180f93a24b4e4108a8e46d99d2147311dab7aeeeee27c8fe2b59cc0ba8fa2420a5952d9e7f08304cc3de6f6caf6d6fd3550a2b43130e38eb906171d84081ebcf513d6d991419d99cce6997ab6cf28ba62229f2fbd0da8e3a4d873c455eb2fc46e70d2b61f76d0fb9c8695c662e22a29b8da250212216c2be263fa8f25adb2970af49d0d3053f46c0bd73beea4e957814cd3134dcbe8fd7330890dc3336c113c2bad9576f71c97941a944a2e5b6d294c211ac1d266dbadcdc2f26f9d46d1549e9ebdb42adfb19f147d75a11b884ef331f36eb38591a77a2e8c556422d42556dae8acdc855333f3f6ebaaf5f3386e3f61b056fdcd9c0695327c892e354984179d8114484315704896b8677690218582d8c821f2e499f26a5cc9b41901230d314e5be9d7c614e182dbd47fa7845dd518b27a373b3a57ee33789db44fe4e15a4ef7481a740dfc50fe2b5519cc6556ac147b96ebaaafc3e12e7c0c7cde10030392efb7bd65f783a969e24031761fd15caab6e409027d43fb0e2ad0c1ba345a3e1a430916b9ec8e9a3b23699ad96d5fe658606f9d924412b6f26638921a3551cdd561db88a9d8418efb295ca50700d809af427f785f2e86dda1ac929d924c9cbef34726ef5615a919eea9ee7a9882f50ebeb347db0149c8185ca0f989061dbc46dd354f0dbe637e2a88e3682f8e528bc23aac11fd812877443366c700b558a46c7c7e7d949b15a98f773a4da65bdeda7a521d8f452c423a26cda31476b1e7c05a3d51a3b60a18ae95ae9eb6cbe883de96a45e82d4e3a837ad4f3c969437f5805a90bc25e6a697651a7967d53371551e3a4d0747cf33272ee15a7416aad60a3f0cbffe079057ae411a9c19a157a18583614bc9e9f27d25bc31ce3cd1bcfd33254ea2c97878752442465ae2ff800008d7e5d860c90f22a7bb347d06e01c9f6130f1a95a87a7af30f1f52f04f7b3160e1d89e5e023445167c108f044a0372746c05b9b6244fb1c0350807af37e8a407b4606014204e33f8998ac0a2e571ab73671642b283c26874d6875a8986cadb0948f69a9856330e7c9032a8d3d6c8fcb7b741477fa1457ffe262aefb7c16e01e7b01d140262ac8428386cb1052786511be6d8f94982db13929a1eb2547f9c35f8d9afac85ce05b3869ad3be2bc6b2527ec08eea080b29617a04f23390320156eb911d8980a5098c7822ece3a61f54ae156b8da351c8375b7b6219f7e4d77b2d77d7ab4b8c612ae6eeb4f4e4e5456d546048781e4ad2eb5f408899b202a7d4457df918e8e2b1ca675166bf7c61df9e1f178f4069eeb26f0c302067e214b18dee76d44e314a16a65e52ab9d2369adb396bc9207d29dfa94527aa86b9a5d5e10b05e02b2e28247eefff51e143461ec370d18538139c1530ba3ee73cecbfd8e158504537ee481ed5f0dff1e7c47d76e4640b2d100c88f401841d059737789b950140281ccedc2d9a81715d1c8005f63534eeddd86abf9e70d398c648e83e986a042dffd1d9d6081448e5ce5cd19aa415ff18ed47fcc1cb349eb1a0741e29ba10497c2b5aacf043405f029abed7d423e7f17b709e5d436971b9ba2e604ab8ffcb386c069c2600e07e615f06b6ebf5dd1fdc051376855dd0a4a715c4413abcde0bd392e1c745d8a06e10dd356a4cd6c2e1fe4fa2383862a82419875f24ef36f16836810727754cc395ac8715af0a81b938e7ab947a477316701fc85e0e04009343d0cb65309eec19a91f60d9f68c3fc56110fccdd69dd1499f1cadb2f2f9efe5018c70501c75f2643aff0260430dbb30f3ec8a548885e006a095d1038a25680f50404f5804e2a75ed6259878b85fb8eaa06a3fdce0468698994aeaa84e696d4c2e91a915df33e659815ec4d0b6699b9796993c399ec0f0e25efd35bbf3da9fe96a099bb5fa5cf1e02fcffc1e94aa6d9f36d8544a42981ba05e777327bedb3dc2db517fe20e13e7f0e517b8269cfa9bcbe35f7540af61b76d8ee3dad4f22bdefb9dbcc8ba06e37b9588a396c800b59443efdcbda358b0294ca9dd7cbc7016c29be8162b34a5161c48b60927e667a261e1e1902f86a1f1d6e21ae169da84f840b5ffdde99891db84827ae5565cbf21130da01ec327e8bb03b32651c0fdc3b685d8988751b5ac3c63babfb3b37e130490979ac5a086de199b2b609a572b8932d91ae559ebb826021ce987b9a95c2c7695c2b83ecb4d3e00502062300015a0200e95672e59d3fa9edbed4e7c8e594847645771238cddc4a5cabcc10d58b611a9ee21921d1a51f6417128dc98116a2da94284a36e5c55d463dd2c929977792e88e17e9a282a8012c9e5542efde55b8091457e58e9a2ab875114398029fc05a8abd6b5b6d924a38c5d776bc6b3b97917181e35e10f293e9936de4d0d5d23244913a684df9d6d1a32c828c510a9b7aa0f9b1116afe47f624b973c716f9f851660e7fea1a70ca807815b4755654dd4d7a3ab7aa38e53f71deb0c3c7e471de0886fd12e15a7d5f84498b57618a84ddc2cbbaebcadc93fcee8b37820a39e1b5311dd93f26e5262540c34bc88506109fdc6935ae67aac148645fa349e33bf744d80a9c82812ef57ecf9fd40a258a0d5f2d1472151d87482d7eac3c4a1d8082d5bcc4d6588ce5aa7cf1c4f46ffda70338ee179ecb7b4f5e949a098ef505ab54f9dca2a102ffcdda4ab65c9904fbedc49038dc6a556271abcac910f84230b58e31fdcc9f17d21494c4ee395808e202854475584ff8e4068ffada7e1250fc09e13f9402364426ecec708d8f81808ee2673340caa1a5c30b207e5e3a360b0ff4f6cffc8f708213503fd8c1d968218e78c0c91c24a63da829b0d3972ff6cadb0970ddc100c7a4a86c749aefb01be5aa7897cb8417f2362ff388330fd40b7dd010edb12b1996291b38a13dba5cf686fe5d9a8c9c4141c321e67c8d0b36c76d4abf2986aea0e229e1fed62f5db8e17ed7435046ce3eb30d5e0404008838d15d5d11f3fc1a0665f39bc15d963e415e8d8c54a1b4120433b89326d1fc56656f9501a066edba17e4e0d94468ac04687ba6bedffe9f4bd0b5fae36c58c59a420ea6460163e4bd9df6814387c4c8bd75c2b593e0992dacf978d54c3976c5a0ef73e88c3b1b47776459375e7a60d51933b14129aafc14af73de38c7e52fa9cccae415bea72e11fe5f46a86152fc4508c815a3f999a111ddbb1c636609527a5beb70b4ca505a0ef2e0f3e7a04502671c34130f10b9f8fdaf5876b1d31c6a7a62b1927f38f924cb233bb91bc0b87d1b8199b37e3412f9dde224ed5c8ca8d4eccf4511b93c8565b2c4f15d61693848047c8eafc5ac8e6d223b89c144ad3837e7ab1544382029519a17d8e11b45a4df453ef2b68c4ff51894303ec177c47bc721d308643102d72b533525a122b965f90c23dbec6dea4782e6ab2249ccbec88626960e4b211e53735cf7e9c66ee619dede62dc3ba5fdac84c75c2740e7fe014fe5407debfa294d8a5ac09da31be907d6f44b3a267acb6f73dc4d33a88c0c6fea4b35eec8cc534db228bf17831e80093299638b89eb68d85b53f59b577d0c0944962516a4c43e0a9a3e887ce5d5aee32ab67e5cde32ec095d8920aad1688729286c5686b96717833e74c2d503dc3e8a426541332de94ce5440df925793829b3be5a6971aa4fa304be9328b33864396efb658d06163216e0b924415c3e654eff28f8a590dea8ddca24a6fce4bab3fc1a76ba75e6286f78f8ba50659725d884ff5dd2c1b2954940b647de9d8555abb04686482319815dc1e55124ef32c95ec1f72ba9073c7d51972a50b5eda8ec1affd63b45a6a8d066fd9f2cdd2207f9883296b6860fa5fb06c37a67250cdfc2510372f779d0e44bd238f3b26dd786c21318ff00851acf32ec14b4d3620e9d1df4c0ccfeedbf7c0275ac9cb24ccb49b9ff7c86ad15148fd72535cb75cdc9cbd016f59c40966da55ce4273e6bf804c49a8fc48536f8c67341f4a6d01b14fafb60fe84db6ceb405470debc56cee0673cc4855b01dbc32b9e7303dc35de689aef2e8eac8dda7d296f7326808e3cc944ed4e62e1554e1a6b811350b91ad9de0e7001be05254e871803ac74563bc8c9b7b02673e0b13586e89a11d596b376102f5c8f98798f532d28611314ea886c76948a264afea9449fa3d44dd6af72df901327c84cc477d006eef5b7d826ee7ab3fc34edb66c50fc34538d5bb8a5ef0c1737032c227fd6a270a9af19d01f3b6b48c7c74e69928ec6ec4577f046eef53ff03510f42eb233f01b7f7a5ff80187c08dd977c826ec4477c066eef5b7d82f814c9378e30afa2acdc452f835ca0c41814dd8979202d0e6269809878b48bfcb8c5497ce4336c55fb4f509594f46ca6d0a35bc4184cb42ba371d712eb614b96a01cbd0aae783d29fe203d145c4583f6e6ac1b202eaea07265264eb85550d4cfce344809ca0df8f3757bf2697f4cc458ce9d524959bd471935e55bba38d017d3d3242edc63275f646d5f62fc6b9e7231265ad300aeb15a23eb5c142ce1504cd07f3c4d599d7806e38363df96c7083e81b3548d59093d16449e05bcc0cd587cee1f94b0f435104d04f44ed443040d59345ce21b61612d5e7719161cb7bc4a3b30e68164ec6b61b1a7f889279562e1d9ed9e83d74a43e860e380ae5be3c7fb5029d0fe4df97728401bff1ca050d4108075bbe5f9d85537d05f98e832c3c683f4952a3b2abda717b8aa8a1fb362595997663f74f2fecc1861e4b1ac8d4694a834d1db70190c37f8f37b67122d2d027fd18940caf3d8ebff7fe564435cd9748cb713fde09c33a1ee04179eb3164bce1ab3426e518afbc9fcd89caf6b083080cc54b48f9687d74234c7503bfee3cbefa415c43aa38893a30ce40acbc4f5782c63f747727e9a277cdb6d40c8c3828c3457cb8019845baae6548e3193391f8b12e4358b3c3447f7786266d9132145ecb2fb357ac39e6cb94fa0020ea54b57bda28230407f8dadd2e87d9752da61eb8f6d51deb188d2c0a91a63507e7c14260ce088f94d4c239a721242cbd5198713298a3595435d592a20b8f04211f9937644ffc5c88ab1a9fe32c7c5e3f3cdfc44fdc0662c54d3aaa799facfd823d14c981bba1820b27873dc4f08b73d8f566e48728f7fd8181d17f25f288be31c78c4e66eb18f745dbc011d1e41c1b6aa624c7065624d116ac3a4f128e8fe56fec1ed52928654edd5aeaa22f2cacb534df39dc5de000909de7293ef1aa1f6fb1b97c8763830cff2acaf09e2109dd546ddb8c6578eae49af51326d1293526bd65c5b90173f873191cdd07eb77f324db795bf5845ee0f2e9ada0f594e74b46b5f4128d5a94842f50c2486ce8de0b470afc5cc7b2602b3755b37ceefd1e02cd986725dc8d788f56a3c6f4ff90a5861d2019641031388534113d96fe6880fc4c92bfddccd100973088bbccb22eebcc9b73161ee9ef059e7a64a28a3e56b456e4626ab202a4a3991d31dccea6e23bb8b4f79e9819dddf35257de44a9b1c5d2547459507ab64bb1df6bbe48fb58dc1089abe687f04ccc950c69018bef8d19232af224201f6f75345bd7471ef44bc37cfca5be57c2e0563841a68d1cdccb017bdbd4964e81a77b911b305fd5e51e718dca0cfa63ab6077a487c7350079617c5551c24e2f1e75b5326b1899f960a86a6d398b92ccc119f326c2164103afa92af44da78aa436add1920cd900ea14e995a9f23fb4807c0a547b1f8db9b878563438deeaddedc0c48f96f2f3635254d29fe17cd000668384dab7d345222255600873eb3f33c881bdab8c1ba82880426568c1f781f500d98cdf90b838ef2d8ad02de5ae895b112f817930e22556b97365d3d281a4d9b6b00a394f558c60d567faf1f42a9bbd57fd759c4110c73c68eabed3c8bc5804ae7d87907bc072f9dd4bbf006c9f376803531141d30666e74fd7affbf7cb7d0f51627efde8939976f6f56077338c8fb3c8d7c32651e70b6d43793afa78f34ed3f6b2adbbede52d9e543bece4c241cd709bac3abb231ab905443b770928c2bfcc7352d60135268a198ebd361edf092c2337d23d9e332644abd44048b3728598ecae0a94e0dfb6891f515d5abdacd24d9c907ffeccf5872594ad205293e2077b64fbc001ded2797d69e7277c3f88896a4cd6fd53c363969cc0199cbd109bd19c52bed6e0c4967388bccbc122f51a7816ada448be253615afe8a0b3dac712bff3835721c8bd525def8db0370bf8f18de998ed04fd6bd1e81ef00245c164b519b37cd506895df32c151cd914299b67c9f93b896ca5c17ad4352cbac98e7dc6e66eb8f723de159a19333e978d9fc0b9cbca3c6dc586f09e4c5e2ea38da3bb17726a5ddace23493d178807441a75fb9d6ae749de88f1d611ed36b39688ce95e9665ee9fea244ba6801fc1e15ff7349ccd3987e3e3c33dcb6b0fc911a8acdf8054273e1d0532321242695e469a52f1719baf571bbe65330aefc320d2a82dfcc4287caedddb1e95aabc746452faa3bb9e31db2e9d0664e639b2a1eb31add7872f5ea057a64c36aa2c1a9969b25817cb00655c134519fc06900b4c0e243bf72dca5798e46a41d4e56b6ff0524be913bb298f4514bc624cf5358771e5bb2cebc365a1cb9242a3618c79d6293773dd74fc590caaecb23503d420523cd79dcefa789b33f62e28afd70d07f787f98184b5ea2b2eefcc9d7292de6ac9b3ab227b1f553752d33534528e5c618adcd1e40c8d5160a96ee22f61875753aa53c4b7448840ed94700cf4a9caac8e6350028faaa896e081918cdebd1c690cf22932b57066c20820777947e9f0ae764502cbc8b79f62b236e0159c014e817c1155c0e85bc49a9f7b7be3ba3c497062b2ea49d158e7eb1be4896e03b17d0da367332a860e21db0c063a719b340d60f9b173612a5d186f3be1a39f5da73c9268ff49d3c46eb8e6a7a835f03965c587f35bd74aa11be9eebcb247a2904841472cd0f162b6b19c0354b9138839a97bd82eaec42ec4abb52a907304dd98ac6a260749c1cd730f57d25047dd84614cf6cbbe6f7815a762041ad682d33a15ac6c6c61e6be52a31e436ed32ff2e4f9da1f86d4a1fbd2fe00b2ada0205c6c6a0208e5122f05aa5041debf99bd11d76815025abdec39eb78d39a3e20630f00a7a5439048bcf9fcb307715c424ab4909bf9569ca749bce16d616a7bb63646de35f0dd8659a86ed55d60c795860805b5b6d54bae135a4eb58bca62aa9ef62cbeced59bcd040d8f7039dc49baa85c1ba8e426e265f05eedcbb9a7d5637f01fb4cb657794b0a88bd42a40d29d2da4ab41ff71221e25de87c354c2d265b3b70ce8d90ea60b4d637c6272d28ed2fbdf6d8e24750c53e6f0169bdbb0234f73e49b8a611788e43aa0bc8375a3f36d67f65cbe0418baab6e734bcf07c8347a043fe430a51a69e1e0e66995ea5261378343282e898fe6b1c28103cc80e8c23436b2cc20e90a7aed5bb05e116b654e43bd548496ef29bb3c80eee6a253180f42c0f59fabad3759cee2d4d58e5fa72d57679a5b9162955b8d616877da10ffe46038417cae2a2cbdca4406a384f449c76cca9a8efda786f697d57a7aeac8c2d2f304caf34ca218a9bcd6e36b14150e484ce372ec09126d88473b6f8ec3d688c348faeb8b2e1df045524b0a4334f14cbc8588b9c6b82c5edc37e6d8bd29c9429b70fdd5c9ffee136582902179fd4c8d85dc601acb3885bbd661a38013b53cda538665da02b9b3dc05647c5544ed375eae3e921f2f5719b13c4a64b361cfea1a28d11171e144112f3d98759abcdbb213952319323e2934300f95f530e01b4d797a58056f2893f0b76ded543d44a7dd0aba339f63afee2a8ba062530462c0b411cd50f2fef97d18c2154415e69eff2af0a75d078632736e3fba96d6d314131252b5bce055e6aea25f1d01976b643289fa94f1752d035673308a336a0749c663631b7520e9c228c5f28004da3a0777a0984f6b90fed5be4a7c50078610740c61b4de949df8f612ff7f81f4250a45fafdb46fe95a41996fc99a53425fe975c7691f72573a6566f7124034b351e60ae062377dbaf3def1b80322d153013e72bc75763ebe0a3509563e55f7f47a3731b6075c38da603ac59815ea4149f7593410e38b8289e2572773bc2f9a9b42f489af70eb9e638b616d7b878586b2267166f925e5a00920f3a4e209dbeb1d42ffa866af429aaf871d710569c20915e9f58922bacb8a5c2df72d512f4b93375e6f6aebc2c236f838cc978943e99a1ac7bcb3cb21f7ee55084b4aada5b326e429b380271fe2cc3a40578fbe12e1eca3065d0b8baa5db1460c1b3940ec9f9039cbd4812a278dac73b95d14a2ef2c2db32161a6db0e5d2a5f8aa1ee255772c99e5c3c0864e79275b0047514cb38520f7b85c25b739206e08674c40dc5ee3811a5d7d194f2456e638c4742b492661c106eb01d88f207ff0461e1a6d92621c14558ac086c3ae3ed6fd4c562d95aa9234acf3a1775089efded37988bb627b4ea46e9f73f6132a5b14bddec6c8a00410ea0d9d2ac2728f7729e6b143b63331bad12c86d424cb73e09d800188be90ad6c90bcd6d0f368adce73853536bcc6a0476b6b9817279c190a97b446f6350fead3b0b50f0821fdce3630f5ecc1b3fa5152cb3f3e9af287b1262a54a7d24749b30f120af13f6e054166290eaa80de7a4cac1c0db4525a27fea44493cc1e8f6e11e92d41031bd9e4ca8c659be5072d6d12a018ad558d8285319b3df13f939bf30a1811db2fc6e17441d80c72e0446202ffa1e08e1d0a11696854fe505f52ab143c89721accbd68db1f07714106e52b7e09b9390c89aaf878739fb53aa38a1671d008ec9cfbce42a3e93a834424b8a0edf3348a2aa9ca7a18c8b3839a5862eb9ae2a064fc95cf5ef184f2bb727f6cacd37e95676ba17ce0a9b0dfb7e1c563927459db8a84ae696295cf5ea3c28ad2b578cf0322deccddd1f91848454952e4d5cc5e75d470cf1f0fb256251779d7e25b46ee5f2c435cd8579678663aaa51b226964f7e56fbe47d50f008fe6726e47ee7ae7a18803fe05e22ab55d40c933b81a105a7283b79dd2d69b23425a928496b746b2212f3cd68947912d8c9ad5c90552681f16b946f93ff49d22ea459579b8641d9283025b5d16b97c750da9088bd3ff5bbcb653955f476509d89b9e3b6e759916a037bb3f296f2238bb04cec829858e220e1e50cbb67676cf3e93aea31aa5ff732f29338c42c958e0ec64aa6e686030d6788963a1df443288a68076028645baa3a2609e3b1846a3ed23863ec5cd0c831ab188c3d24d644b548527a03be57287acf5a85abdf62264cd43ab22473cb7d0284b6f637974bb9fd5692b1ac9437bf9ab5299b8498a8ec871ecae4462b9b023118aae0f6de24021db0b22c32879f70a381b858c3b5a4be8ad2580e38e9afbbb98e23ee7bb221b4bc860925ca7ab905efa22db645c5ac4236ca1f81a8ce71dc484784090059f1cfdaa1ba0be1470fa366d9336f90b19c60b7a783ce40e28759200f5c570982225fc660345df63d78509ac0912b16fe360ad52c17e859ff102d088d205eea201b8adf8f1471cfd768e6cd85b7afa63451c7fe959f7a2d126e7f132149459cfc6e07b6132394bd9ac3d3af6efd0cb9c95f653b8a99eb30c271440f3d2f5a8005fc8adf021c18aed3d7b044a5336623f5b24d04ab3b1a769c5f610e4f706b290ce45c6186a9a1272001380498b92b19987ddefaecd3da19dac10e2e4e46163070c6b4d6be70e15f36cbf995093a669363def9da42584220fab19f0036f61417790e0bc3f1471e44f0a825f7ccebd0ea3b0b93686bfd0ec2394db1d20cb0066b806c46a502ffd9e0bca02e49346f28b8b685813d06653091c9c43af03ea484af619ace4b8e1ca1b8ea0cb73bc1ea5b301520783a782123476d1d988ade45facce0e2d5f8b736e75f7e41be7455a9d24445a0f21663118531ff9f83d131c595ba8d0e189aa52a17045b75899dfbb056eb9224e29f9bb5267f8dba62290b278e246aa290f8efa034ab7ca1c3ed9939bfb9ca5d792caca6eb5a5b5b2749e595d7a90cdef08bc1f289d11b3bca364433faae74dfe558c86c49b57d9c081492dab171cc50c5b88b5101a7a907c9f50655795f0a55f7291461f5077ca0080153637e7bd5558474e9752b7f482bd4172fc7ce3bed897f974d92e5d84c0dcce4954c63fdc4e2617929ca71c8c49a1a520ac80be83604725e176c8d7c8cd5c01295447494768a3ef7c5ceec643ef5151e97d68b3a771ecf5f755adb0915b4d5dbe210fe2829a053daca06715d546dc466fd2df5adb891b16d43d18645cdbf12c3c697ae8a0451003dd7bb32b7641a6164a0373758d7ab64c1db16ec1b0904cb290f78067c9e51e34a02f4fc4024beaca7714b5400e13098fb976e51a7aa8e9ca6359cb26e86efffaa9691bd977f53e0f4b682c11a7314c6f6e228dc8f378a8d1f77940dc8920e70bdacf433d98d6d4763ea7b31172be4294c6ba89221c7bae12e9fae6cc9f05fd8cada86c750e928c3347f3f973696d08d24814c20c84778b91f68f3e71ba78810faa3402d766c6a03ad284871515e7823d22d67322c672bd4b7c99b65beb3d39578498c5f0ce0094d01ee34719652cb6b73fe62702ec0121251e06cd4ede35b2e7af26dd5dbf0f7ba715a92223f5e613a20e6f81a8a2c1c74ed7bd5c7ccfd049621cc0666ac61fbc003a35943342c45bbf6de1f0c97087a7c46bdb1edd51b4d6fdd6c3f201eb269d933a6b3d7f2fea1f889179846bb57b4479b470a47b7684e6f2d90a69ee5f20a881d84a936ef2e0868ea882ae7e5cd69d38d603ed5640bc9489a88b7811bebb7be408de7048a032dd8ddb94954d1170202dc82fbedff85aef25c49baf8e6b0789e234ab0926a97d68f2a56054a2036057d717a973d9b23b4ea8fcf32432177777a1aebbd9f17502376c87cc042a565971dfbcc12aaacf9349081831885c1d83a5a3d6476da5b27b1b167b5345b551c5994f4508c4ae394b498e046e45604912b9f1adf8553996fc0f642382b83c6e173567cb3ff05fc06d35810c6a155a96fafe02a29d31aa6803a79615df0353ab90cd94d0ee8654e5483ba66d91950208af068d20a17a130847783da02775a96f2666b805296c83ca2ae39bae333ef014074a333d88e9d03a31911ba23acc4c889dbc09e82fc4de4281da310ded9443e437514413718287863408032a27eca60e8aa77e6d1e55c36aa706879b168414b513a06951f49ccd068e8380efbf24d3536c035f95b8f50b090d425cf2525e79d4ab247e2352af52ab35ca984a37aa7be3b26812f514d0300b00b85884a300ecc7279925259409239e8db8254cddd8449fb21eebb2434b9f745a480922918b61dbeac8ad6a65aeeb72c468d3080d259cdd6a2250a620b339585c722906379680d1e70af7cabd38c6ac221de88cc7e6de51dec0679b52f00fd0ede4d8490495595498b057f4347c01578c9fe34c61019b652ee3876034011f24b774d18c26b61b1739ce5ab2696896bfca65c237172e9c42a82c873038491a70cdf639a39b6a00d8b64d8c0b8cee2e3dc5e0a62926b8f9f95237df07fe9ede5cbc17d2642ce3de8d3e4f177149578c863d7ef9c01b54f986a07f7103645da3ff8701ed1979cb22e3ebc9b88fb96d7d5446268d33478dd17e85f9d26e558b02a588ce0e47ad7b07c4a41847037748667441f4363482dd0adbc2ffbe9fa6e9b4f294e1dc7afa872278fb794d29930bb9130ed29e1f0e22e9488dd81213d140ce8ee920dcab2e1ff8c84f5b925189d1ffbaf3a56ca12e598d1c49913f7813b61b23375a6fe3c5240d81e906570d46136a2eacbbf0560bc3b4dc2d0b7cf85f95a33f43ee40bbd8957698d81516806ad08f1180c4fc7f5cea0f6846e35a71b37ab8990a793e32aa8006f069d2ef7e31fcfac1bf2a25e66ef7344506badb7070b6aff3625eefbea706f699193444e1fdaa1f85138d31ea1652c6e679797428543a77b9ae39fe43a9d59766f41e33625400889c6273764e857a3477eb177b80f3140920e8ae4da3ffd049b0f481055b18c319b7a7b145aebd6c4e655670bbc3753dd4e6c8ab9faed723eec6f2d0ee4c1757da5028d50d3ebf9b254b6cb0c8e7e7a12eabf2c7835d5abf1c0e97f16cde4c013d8dabb5fad98cb4a6062561a8ff0a0b7a410c8db14bc4f63fc8b2fc0a65dc832f29a1f26d6aee2ed877c427d942226f6f6486c59e6dff632af20069bfcf32102a6bff7af03c7a0a94aad4fedb4a6cd3d57f28cc19f79c92d471481da513a7e1870ce69cc4e02fc83709fd3b38ab73e1a0b2109e6323950a5ef1b63c40552c1c61b41b92ea3c92b9f42b23995f5196efa0d2c05d968a81ba34c336694b9f8b35dd0ea1c886e9358efdf74fc1705f284e09b4c2a31f20b0acf09c78498fb30bb8a55df76795162b6659c2809374f65407ec278c7ca39b26c3c01ab242549fc1041fe5da8ae39d66a93953247e9323840a779d48be7a5fe18a61bcb85b03c0ba5c55b6d032aebdb6c6e178da4135aa77c0e69d93ea3087c94a7efb893ab1a41de0bd7d0cb82ef0adfc05689fd26e3fbcdc22d0e00fe69fdfa0931f5ec276d96906c805fe0a80d2e49e702e977b3ea238bfc2e79d5adfe4928fda0554da83a80555d110e9f3cf39b2e3c87b6cf36a8243ba16e7693459986fd9cf0a7d7c672441bb5595b96497d6f536db5d3995eb4ba03b60ed9888be3f63acf25f67c73b1ee07f7c171176f16206223d88730dcf44f1ff60c522472e1f309d0f39d75148132e08d09aa273fde42fe36b5b3aba6bac007205fa614a6319824a013c23639e23faaf303a34c222965a61d08c5b428cf72d2e388375491f7d502810eea9b7f9a3ecab88a135854e0ab82020f188eb686d001d8ab4607540d83fc3c8b8775a118d4cd8f70517786656b28594755a57d6041fd017661fcc875e5d9fb6f6ce2c200a38c678ee111e890d5b6c9939421d7145e4708f57e1d6f8c6da4a1ca23a566614a98468a65ab7192423179d6dd03cfd20d541c81b054963a269168f425f83eea9f9329cc40aa4d29ebed8c6410c3765f3a1c2cc5b808638fc2eaf3da4b019f03327ce934b2f346fb2732e9834617977dbd9a369f34729a85a7955d1b53e940fa003b2d7498dbef5c6ee184babc358bfd44ca49d3a00fb25cbe2fbe5414c7cc9263faf87acf3b8efd71b4a4f69031d75d808db7ccd9764b5a81700e2b5f89568d85f1841f25cbbf47f1a3b385572ed7a454e8029bb98f19034b16201c6257c47d9ceab865502792c5102b3cb620f7db0da5b6ab1875fa8f7a2eaabc05883bb8867da1b35b9b4268aa24ea402c1688ebb11c7e5ea9389c297faf9eaab68775cc90c4b7fd51994471d311280a30fc9c53299e5cf10955365e9b6e16f10f37ad7849058c7d1fd8bb753c51b516c3a528083fda50f85d2b863f6f9ee072cc19438e5987ed68bf324443738f2b6a987667353622907773cf9fb9c9e0584f6b1828945542d3e54b4472c577e1681b4f7578fe95ee3e1647fb855a7d9d2e1b49ca85c889cabe12c517e3eeca674bcbf9c06b32cc8ebaa8476dfd258db2fb2c4bf4d1c0db8941fb94da9ad7d0ac58e11362cfddf451a90e9a486b64ea3a3835483aba9d837a5898ff34fc855092d58d344eb2f86b16e0ff994db2807e50f2367fefa377e8eb22532b4a47e686db896a2e129a0508eafa7c518643b25a6a18e16822415ad5290d1ff8dad1141cddea3f47d91ecdc5a1013740258b00faaa17d868f4a6da2594c78d09f8df8ba458ae82333c221711c4cbd781df24e04ede7a0a60272b8444ab448bba3ad7e3da8ea0483aa385b29572c98badd30b1c13a0d24957e56b18170dba44d9eda02fc832fae431ce90236336d584065663aeb7ed804d97c707e4b2573aba6f4d54b7087139cdc349047acc09f266cbdaa64ea6c9586ff4a0ddd0b8d2664c80126c68410b4e0f092406c9bf50fd7050559507e384a7177d59a2f8f623fa841156d1d079a13221a3f922709af825ec38acc88be02447e6a516fb10aaff25a3ac255939095d8a20a6fc5719447d96e05a114828c6ea2113436173a6b817eba4e807a24858350b23881c34244b5248d74cd09c06b9456b0adcb48395ec6dfda86239068b659ad19f9dfd61448b844b7a3f590c8b1634e20e4d881fc707c08a58f31fbf657d337b2b52b630170d07ba3692ff702255f6d3b34e4ebb2df7a4816e7a06768fc6834ec33c5bf995994304996928f580fc5ad09c1692d40dc4882a46e0ac9c71c54eed238e600da91d281b82e72e05dce7a48bac2daa9757be998410e6201acb618b3b71eaa9d7812d6723c44e84418ed6c6eb5c1a95d2f60be1cd614d4ceb21fe71e986f32daff27e1fac9493b99f7b86902f5115619570fc4b216655e5eb63e97e7102dcd69357aca3068ae04b984f28984672217b7186ac3871c7e88d8302e32d67f50932681dd8b91a8b7fdd25e8013e82e7ef8d024972ef7169148d04b51aeee21574815a00929742952f25e47a2c60424e55d290b6abbfc840fed1b868f342d0072f850903533bf7f09d348b0c00595046703362a8875275246b2d195616040e78215ba38e88c5d7da472f0010509c218387c48d34dea6df13f0357c05684ba0342d24bb821f8f763c4459d711f8ad86860d6b73828263d51bb29810e4b1d645cfb2c9b80834d000b5843ec24c078dd075bbd8ca6c4647ac91a60be451806301b69788350848ec2cd72390d582084d063ff1175bf6748e2c6d63b3cf7977e87c69477686b86df1b7ed1bf30bfd6bf0c3565a607eba598cd69fa6599704f4ce475a222665034cca10b9e2fdb5ab97c8bf82b26063bdb76023bfcdd01263e0fa80ad0362e62ea8a69bed2f809fb426ea27deeb857b359286010995c8887287dca6042f927757f559ddfb45beea62e2c42f282ecb0a8112ec6605c34c6360e179366eb41383eecd90e2e4cc09ac341220b89c4814249103c8241e05a1818d7f5bcfe1ef4024be0ee85322e9c0a77d14c2b17313a64422a5393c74f641bf7cca83c2dee22fefb074e4c430fdfadcc0cdc93aa19ba68253c2189bb281b31472876d674be408d343d69c3229c7aa20b3289d4edc4bacd6c20f9395df38e6549477ff33d8fd1e0b42f6aca35803ceb5f5fbe287f92d1f131a394bf7c21dfd0ff8a304f821e586f35d58e68ad67610049423c812e1c2d86764b7f1f230c44d01d8c4ea7722c789d11524d4fd224891aa4f5b14a150ffad97e13360e262e72880da17468a49adaae9137d24a154e5cb48e0e641b5f7fa56b2962b34c911df3b2e8626d83d06ab1b0244b5fe452e325b059f00286e79abcb2c128c0f7377995975969f44076aae5ded06fa9869a5cf44f401cf0caa3b5c9cb39e584b59d120a86563ba63250960ad327b234d1f83545a2e14485a2d7d9d8254eca9f7d3edadcfc09a7c678780830747ef7a31ab11c6048ed12c436260bfef9101ca279a889553dba5672d12d6e001ddcdb8a236eba255c06abad803f9c8fe49d61385c39739326d2b91a659ad0ac53d1ee16882e0b06b10ccca2f555ec5c1a64df8887301a845722ac101a847f7f5cc847a51542437572f8f76ee4e3043f5aca3ff23d6d7aac3837bfd6267828c380b881d30ca1b655b97bc84f4e7e17ef3f18ca96b0de8202ae4fb3a61a3daefb9924927376d5594dd87145e39e17a2f03f2aba2503a38e5845773f93fcc948c95ab6904d91229766b9f51391b1f42efe9b9c4eaff8fcf7a6d7d04149561b66902714398118b839bba8bde16d4c90b069e6bcb36aeb3ecb91ddee123f1ff46c45ccc67a23679ef7cdf3b3e2ed5904bbf2acd9acb5bb486b26195ab3af2f4bf7b8e7b2a6d1892f42c54cd24475045ff9ccaa96eefa4fca811289c7f6239e57b7af015effaac7077604c4aaffb7cc00acc8814af823c02be6fbe2027739f666e052223a8e89028d2ec04b51d8d4eff883cbcb88b118bfd0819245552e096e179c8d1e1a31dbfbee5277ef392e8cbbd7ea4ce5ef61d582636983cd2605ef12595bf9e2a208e505d16111880459c8b3940bd1089d4bd507f10fcd46d11e40a2b891c0977161a0d68bfe23363379f77efbc9c244e9b1542dfec0aabe4643162c2f067ed62ae0147d5b9791f8bb1672f1f049a8039bbf7a4d963fc1e2f97c807d2d620f5678d0ecf920b5101a723e7fa1e8221859ecf33971c6504406864489fd82bb4139ce1038e596f01df243cbb26f0b5a3e1b7cdb40824d070aca2bac0c7411c799dedf62198bf7bdf431c20accf56b30794c6d4e775618689074e0a3981e852b1202fae815ba07748a4053d2d2c3fdbc059ba6ce2b3566764ec8f23280fc65985a5ba5a6c360f0dceca548b05a36f34291b00cfa289722d859efa301a09d5079134654350792f087aa5f5145d331be4d07e266e371467e6292c81dd8a56fd51c0d61e5740e7e6ed687b9b73559c120d5171774d747ced3bb5c8b2e9c0d3ebd42bb46649d28acc8ce7567b06965268226126c84820100fdc9ad76460f803db87a3da2f2f5efed98248761a75e99f3fbcbde2296a9600ccbed4f0bec46c6d271336d2b46f614cb7e59dc97366c61411f2523fec624d15020e4adff23e125db04bb2e75f267a2d0555c13743e635b58187325248c3ad86bd0cae4c3361429706911488347f0fe5d4c08202eee3ac7c334293d9abf459d6af74ccc9da27048ca7d3b3d83a30d6710153c180ac05338335682d6e60f57c9771e0442161cc996bd085a4dd8c048f2515911c41955e1ea914c3677076b3a196f4402bc488b9b158efb66016e3022c6f35995aa8cd1511c36dc5263e4b4f085f12bc985286f10497cda606154bf5352b098126ceaaebed91fdee17313cd0cf72a0922a350e42f71c96f026c70e54b25c0fd6d8036fcc2bd0259d7499093589146703d0cd80d64ad6adc413699849073637ace001435d9402e818edf47bba8188f76e67e5f3e76da12e0e02cbbc708d979ab812ac8b29d0026dbe557ddb70bbbfff5c34c33061c016ce8901d3ee9b1491ec6e8ad777ea2600c6d78d91cafa76ad8eedca23c7531df62a2844ef02184ecfb4bc47f381f3b51b5764cf359bfc40cf23a58f8469e67cb0a4fe063614f73e05320dee47ac4f6e81a479d183d2983b375ccf0773adfb6f97d42d5053b29e05766e62c5ccca4231674549a532c156b4bbf497f2aaffc36c8653b7ba067659db9ed86e0168fe1085afd14312b4ecaa0757b20fad4fe0b209a850a74b3746ee01b9ea8a7f50d3b1b8fa49c0371bce6572f5f48f7d63829d6e7b69c18f4dccc4034eceef2af4727cc30fd3fd6160237371ed0829f6845d536a9fc3d57d594b2d66665c464ba5bf0881d481e20ae9d6c566c95a58cc58410e1780f3ef55304367c74789f92270fb34d99e43fc01b01dc23e619deeb1225e5aa9e143e14282fc52dee3fc226926c1b745e049a48d0fbb23ee3c6a724cd34981942b263b48123af22e4a58686e5a6eab2ed766881ddf17ab0108506e27ce7d17f556433c15f9fa621625a2feb2ddcb89067645757c93ac2c421b65d4e7c8f811356281d3c11be32d546c1811f7e00618f44c0785116b74245228751cb1c79080607e12d8067d8e11d3f35d3cc7cfe90f3367558998122fbbb36ae049a68ffb49d7de5d73e8d9775b29b40c4350deb4039acde8211e752e07db6aa9a0e02303f4e7121518db4650e7efb8fbb11a02c2160d01fb2b6e7e50c9309813d9812f5b485ec8f9b37b3f4202c5b51533cd12af8bd98eb136698548fcbfba445ede813db5250fb3ffe97755234287e5b9dc03bb5c1203be66951248784c11c0b1abae6c1a093c05ecf4e07e84e7ac68122b77a4dfd898bbb3c77dd36e219ce3d1fd4c4b96c52ae840e5092d18dbc13f4f47f2c3b81355520d7d52f0108a87c8212de0aa83d1792d60695dde3ee55385d9b42f32a00a8293717140870002f2c73daca32e62e54b90cf57f6db146e871a54112e9dd37a5d7d7564ac42662af454fbf97c8b530e9d2ebe84dc2dcfcc32a23d832959d0e1373c8764ee0b7ba083b5c715c1605024f70c3df14541b06012210d1a7f0097d078ad5b5d4e3e9b94c169e2f4b2b6d5c304c81114fbdbfbcfe364fffe62005c542d41eec1986f2ded8c115302e4a0751a97c3fe716ad85e0c1fe0cf48c0bc9b6b7dc16e941a3a9e0696aeefe03772804d42727b9c312401fb42855d918d50068407b2ab4559a21841be3eca25bcce9f008468f04fff145ff25b70e23205459487c9b486e1960211b808ceaa321ef65b79b4a315f8c9c5d3725390d69766154270a51fc18e856a2a660afa40baceb2a11534afbbe4807d1588f0be2730ffe949062bf792d7bab536f9048751369dca90d519f70c04aba49abd2cfface7ea0c266644bd6663b32237c9687f4e5a8ae8dd354715775505ba26fbb793dcd02858537df0f94ca6385f396d1f40c8805453b289c3f3604504287e6769850b971999f4a8ebe82654ca60019d1ad6eff789d9f4da9e22082047ed35ed3e2c7ecf62270013c9b8a9315826203dcf1b6b5c90c60bc48981c18bce4dcc733b64ada4ad9473651b391dc92035cbb37147352b95f28178bb410d610daad45576bdb88e9fc4527c51a741e615aadc4210c49242b61692a6ac149fd1ba892a4137f54f7b1e4e333dbce288b53e7fa6efcc499da8ac7b507a400e8d6b3a62cc9971297beb0e37e57a784faeb0a7e9196ed884ad3af81785a0dab261fff1dddf925a27cc293b6db661123fa5b1b4e0b4d8a9c99d8157edf967673bf1da5b3a6a9b8aa964854591ed30172cc65531646ff2d00d31e8e4ac75ac29ba4d11fb97a66e29c6e79596d291c200d494b66ee93e697f52dc2c2e01ebfa916e11192ff9f024211129f602b4b7b6b642d0c89ed2e288da8ca2e3e316a4e641a3447c3d3ea9b5bbe859885ea31687ec5dbecf2af3e9f60172680b4d7b6c4c7f8251f675741e0cb3876265bc4551c21c65a62a16a3e95318c19a598b3bd120c51ff97dba32194314966bc309c38e30e691b2d4e66354089ddb56e6d6dca474ef400c0bd219e613bb471d28b7ce9716ac35b7308d33d699a953fe6347499937b0e5ce35522dcc15a526ad38d6eff2784a978cd6f14a21f0ba3d828b742203b89f417c9943192d6a60416cc6e7a0365221ebdf0b3c8b5f7c8a23e414889b024ebbefc035fd48544d8d6ac6275cf96b9ed5b6e56bca167e7a3e0b9d7bd01fa640901a7b249a9aab3cb4fdd7ea8eec8fa45586723bb089e6ce6bbb88dc1a22f123f031c4ae144668a871f82267df68ae336cc6c3eaaded4f0989cc57009521e24819d576534b604284c5a7866765cdbe60d90983e0500e3498b1709f033b69ff82b518d760ba4096a5a1387ff0bd08e328ab429f861ef87ad8d4b8adb51211bc885647b827508d78a6f1be964e276b79430f7ecf9897fc6aecc692d6a0ba6243a27d2599c567a4a9ba04c74d4025a97f5871fa8bccc95e48e9b8368df8d78541b914ecddaef30e1aa96561b5bad669f4a401b9443773e1f58ddf227ecd87f9642d0ebb7606fa8d95902cdab0403c8f530bfea03cb719762cca8b434a14eec227e1bea54a1a6353d94501f4e0bbd367112383f925f119d79921f1055c401d7a5eda8ffdaac52fa80086e07cbf051148bda9996e1411c800c53b8b6f7c22021ce96b1d239f1ea9f486b51e75ecb175f079002427c1256a736adc1bce2902939b6d2be254b166510ad5768c7164fb1232cee7f9977c55663865fd5c60af2a7923d28f9906e8b93b0db58c70a78d8ad6ada223c9a00b4e5db56366bc05d431208f736756865d52f990c4e03b3317cdfc8c1944ed3f866cfe81256a062a307fbd488b0f5a70c989ba62707a93f377e21a0bc2de5fb501d791bf381dd452105aeae3ba11d09aec0bbf8fb0023c60757861484255230312bfc354ccb2e9b57f7534398f08c2c31f1fbbb18037558a59086ca87b4bb1f929751bbe9a41c60431974d2d516ca8e2135c56745cb1294564db6f0999f4923b8ea6721ac81ed25690d48040788ec237124cd5aeeeba44029f5d40b5a6203432f8917521ef55b8ef9c6f48e90ef014067211a3a9b698c0cfdb3c86b2b409f6aae197af7d85f4962503fb3eee39e39da15ab3fef4e8d911d21daba6161593210fb4b81dc64d31e40f1fb5f7c46648f13032b66cfde57c72764ad6a81e13b9bc562b7ae43d683e8c6a546acdd6c56c9cf843b1f8f9d0ef04218e8ee6a2512cda3017264f22c75b4d528d9e383b254e36ab00d5eb09e19f2bab193fec74019e0e6da7d1fce63d33b40ee41a60d26f0e198cff09b0762e1721929bccdded58808821e51db6bc72c0eed42124f2f882f7b7ac36d4d51faf7193ce6332e96b828cb7e4c8eb450459a5e971c52bfc41ed0ec0722110961e94028e9c5f34fb97a2ee808a60d0b34975faea9bd065638457286993b209a4fde7b4bb4020164d3d30fc260934616690f8e434355ec601f7ee975b37928c76374a715df331c7ca1b156863ec30ba4ef08e9ba9abd2a7dbd063fe25c88b4712d933d5be126f7fbb177d6b897a80bc2e8bc69588593e7cd55104a8e95b44fe15728e9c5451e80cb6373574779111bbac73655bad41488bd5de2d47b1d9500ba39e709464f821b3962115939a9a541bb65623cbd8fcf573cf5af97d9248c9dc5cc3c0d05b1536694e8e4a9a4c658f255058c9e7f0ed035944881f73a2f26a28b9097ebfa2ade41dc41c79975c0853e766fc66515de179b4953d5a76a226f2de812e4bfb92e212ec9f3e61e03395176c12a63f3fac713f3417a825a7a5801f66f3fa9aec09476ce81bf27c82552d2eafd207be0119ab88ad9176c242fb7945c8e7ae7f7d8dc71cf999ea9bad90c0defd00dad40b1e4c3e51588fb4028d16eb9544c7f26d9a0898b8996da52993505bc76016107567b3b985571b773487284a95ae3cf8743b8e6f77bf76dd984a35c421a3f1baebdc115d0831da08e497a4623a4fc16d4c9dbedf34f502d576b815e65421282ac1430bf5c1bbd698f47046334328217394e43434c4c6240664d74e25b0c9c0a9da2290a1bad37b5e677bf237c9945c64c40bf0553156c32a090403bb58ed4eee0b6159bd9d1b295999f9cd73a7ebb2e3e8082aa6181a7ab0da5a3c61050cfb082639f709f2c742a3e93c22723bf03b2d8a530ca86d3c34f87007d88f1206f0521ba0984f65d05aa1a16fbc7616fe44e37222a40421e8e29d8a676032527c70d6fceb38a2e1ec9eaffa525eeefe2f59991df4ed284b890feaea95737bc9276830be85566dfbebd20eb5d90c67e2286bfc3bbf56299c754a12828ce6ca6d8114bd47d370f2e2da5ab88e80ec8edc2bba214847a38bcf572c297e18a9934a9e4f4ba128203d4c783f073e7044fa24d074050f96a859c8ff61fe091039e2d0b937e0d97738dc63a06ecb1e5f3e90d56d73e6878b09eaa4c0dd8a272001369be2a57575699b1bf4787fb597b611b38027af4360c0b522688cdd7ee2ed3dd132a496d1eb2fba7d1dec09d2947e4a341de608ceaf6f4dfe0010c89c7b4985991f75b2df978dc295d27afeca94e44cf691db29a835ce0a1485a03975c882b6fe1cf4b0adb6dc58bdbe63a2c608a38b645633cfbeb8a1231116f63dd541ebc5b3875b53381104f1ebfe3dadc056502cb201143889be15f2ca30b4536f3a8874173c45a77f44e412d1d1c34245d310fd6fcbf6d1a91e890f881eee40b3292856b788fee1eaf105d5865bcd122f17029c173ecf6585a6977045cfea345b3a364ade36cbac324cbc08e80b48103852f03f6810c3ff81f7004c47b2751596f14cbc59a11cb856a1e963bc983a7b4be73658349d1100c969104679870334d3086c542cfbb28aed027bb5a9673e36effea050bd233a88ce3f1ba47c7eb0808dcc4345d6c505636b014af0010e336910064d22c62b46a77aaabc0fc6a397fad2aba25e814ce95a21fc04d2a006a871aba61a4506e1f10ceeaaf602c46e0b02da0772b24f30d936e53eb1df90bb5824e1bb821aef43e68665f24556b709e8685c04e8c21a6b46b987343f1ce61d9d4a93fcfcce036a488de36ef3c7589fc76b3b19257687caac8b1023e02102bd7551e2d22562e503b72fd2ad7505d401546b6e0bc70bcdb47316decba63d30010d744afa1a8c2ed53308844e31a71046708d36ba8c26e74354750fc157a86b90e41e083846dfe5e131bd3b584ef7ca3bc54a0c89b676ad3b5dd0b1f8a9816f16c0562ab70866952db822023f6ed0fca31002ebb31919333edf4b04819bbb050772ef167ec95f39f5e41ca434ccb81720222b40a29044204adb9d6c0f329166946d9bd4491250f51e7d5a8e458788bf1d86d18150357493af57efdbd53f67221c540583131e1dadda219048bbd21fb429485157f8a0259dd91e3dd0ccc4395d324e2f5d4d89c1997a15004528ddcbf709b3abd76f959fdb1edb7efc7bc9c31ffa7107f9741c579d53e4047018c75429f71d6b3687690e9fd034b460208cf17785de4c52e100d7dbb7d5b540df2da01c6b49489034a15152f999d41562d5144141495bb5c3bbf912c78ff0e7854928734248d3dd02010d198112d149a855b6bf9c49c0c70c917da4818ee0e4d266c49f514e5f0397455056eaa048a0e57205c554d3ac998a219d278638a6e7ce194272fd4f2e4f925d41c5374e30b5be2f83d41f29a6435083e2200020a8010021af2b4fdc79d2696c05d7e983dcc4054deec8af45ece8f557eb546b6b7594288b44d08d95bee1d190f630e0f0e3f649ef8a5410c933d262a76b02dbba79c734e8953dfa820f907567697b7d10a67eadd1191d8b0953fb35dbed5ae29657cbb2847310cc3b22ccbb895a692652635b9dd393d9cecaabbd46a2dc58e61a37bd204abbd1e85655725e312291fd8790dcb7786017137e0b868431f396787e69944762dcb4e699675c70c7b165196477ec9415cb26c2394523cb0d8b928df0fc2a17ca6f3928f78c9218f9c43cff1e01a6729b30c87f4753b7dc9a3b7c43a5d1e9debaedb2ecdd493a1543558e51c72f46e12de49cff92189f28ecc452ce0fd06e1b9f13eaf87bdcb4b314f4fcea8e7753853c773f69d06b1a64564119c346791b1df1d1c8c5289771ac47c8ce6608dc00f18c5c162df71cca65d39c07645899d891cdac8d81154f0aa1d86d51e7520268949a2d07d489f22580128b2c1089d1d292466ae1bf5d8c16301f2484cd2f9a5c128b4d5221e50844e14c2e411285c3a54c9610b7391e99a55494af4ad1194e1032c51096ec297294a39dc91b3e7a493624830a0dcd5c66a336d89fb910e0d459a0f0302424d22341a8d28f52280933dab34c3ba768f3a3b8f288f1db362d986a57c71f48e3a7ab5cc519a67e76c7b1179499fde3c1d376b58fa6457be8234d8ef2ab7912896af4cab99ac4222a1e2e60c4ba00691c828bc99993a1a715dc78d469b7c45181969e2b66ddc5da28b5e43650d879208c77175ca7019c665ef3c1c72e4c999a964978c8a32f275f99a32f146fa4822f2470e913e72891bd05b47f2255f253870d165c69b3a65e8cd1fd0d2b8eea67c82023d80f9f9550146afef31c23de23c5982fd118be250e6b9e1b969ed0ad16eaf90fa1325d81fb128de3ccef8e38d761eb2e269eb94b9f126de68f85da38ffd3ce479ae761af5b4fa1f6b9a86d668ab35768665b3b7cddab6aaacc691bd3dc8511070b2bbda73caa9b2a89edcd95a2726966199122ea85ddc102785e8e2c8a044c994c94410331428c94d39ac34ffca998fa4b687a60c563459581d6a1756d419edb833706dc8455435234ddc19365336289258115694c201a68629a5945d7bb59b9c111c58f9903322e3a4a489390ce370c0198936883218c6ed005b8209c926714638239c11130c51bc29c18830228c28f7e591c933bb4dd0795e97aa41d69d662cbb336354c90f42d898c148d9e580eae6890c48d7e86bb45db165ec8d3471bcc146d9fc764ac9f17de90324c27457bd0bde6f1b4781702a9478593300d0e048a2b6ae7b44c99735239abaabb2a328238f9832cacd9395632bc7992220aa5aadd09442478460e4506e796220451c902d8f80003990f6d93a65848b2ee194dd73528a619916354df6d426a51896659a56477134923d4799a6d53a1a6d1bd775b267b7715cd7791e8954b2d15ad9d396ac35994ea7ef43a5622a257ba6529d8987b3dde444d5b5b75d85d24d94948ed2555cad64cf5525619c3c9d98d8afd4e232a5c54a89d4c2ba74c1025b4cb875f9d28ac1b746cd65d77c529ac52c933db34a72398d732dbaf98a34a61c5da7717bf06e4ee30680463ed6c3695c1f9e793864769dcb3acdc3c9d73c1c32db9c8b95d63a5bd64ae2e1dd9422ba46110c4753645477ef3ba1528fcf2144bb4e1ebf8347bb5428912665a5b2d2a2c4f278e96372e2081448a26817ebb1e531c6a30ca1487397c03c5e5ed12e1d625a8f9745edc2331e77787c1389342e8f328f338fef26da0580479ac7b713edaa79acf1f886a25d361e6f3cf2f0f8aea25daec71e1e7d78b479cc8f341e6f1e033081a607a28c8d760431052f98348833073e60d13239926491640a8142ac59801552c8f11d45cb1431455391e33b05b16627ae204720453d2416f5125c883572c810e953854fbca441ac914d3461831c2f97302299f8116b64146ac8f10aa02555482534945cb180969140008832525e2163724c99d98303c417206be6123f14b164c9101f2aa8f8f9f981020a26644c9f09222132a63f5310597d2157bca076ff20fa90fb9206e08646b6f1a107171018e7078cf170c3468d1a1a00ccc80cc90cd518ca5c7698815b313ac0bc04c9d408d2ae8c182d2c10c60b17482e921624d57496528b159555cacffd69f919bd9878bd98c8fd4d120921ea5fe635eecc3952e779365ee3de38c9c321738d73a58ee4d5bc64bb9237f31a1700b71e0e9965ced9cee4e1d4d09c8b277a3acd96a74a72b94d1721b4236b24510864caf4d05c222828a8b3408204492ff9a1e205c5cf0f15af171401b8a1916d7ce8e1ce9fc9c4026c4e62050f376cd4a8a101c08ccc3d971d66e0568c0e302f43324335864841414125244890d81f2a7e7ea8c87dd30b8ad70b8adcbf9ff7727c61cea53a9437e3f8eef0948743667ccea44b79ad9b9c74261eccf1d5e1272fe74e3a9587d3ba2ae65c44a12828b3254a25dd520642c3f5d0f68b87213e92282828682e81a4b3f8e925489074163f3f4b6ed8a851430380199922761e858422162e3bccc0ad181d605e5ed8856b601a98078cba434141412648b24082e4e467c9cf4f2fc97d8ce2b978cb7d716ed5a57831de72657ce5e190b9e59c4ab7f2585779f1960be32b1e0e995d9c5be95a7838acb7f070c80c9e8b2c948565b664a92493f4f1e99f268c749024423297904448902cb1c30cdc8ad101e605c890585434e4ca88d1c20261bc70f1f3a798ce526ab1a2b24a19ba432d43ab20491414a482040992b944eeb7b4f0525ebaab73ef583c9697ee7f0f87cca573b7bbd7e21777d75bbd74558e53cee1aec5c369f1969573d185bab8cc962e9544f2b2b451f4d04fd0eb2748891219315a58208c172e7c9a70e1d3e2d384e92ca5162b2aab14242e5c5af0c52ca49f07bd82825c5a3aec9d6e2fea9c4bd7e2a96e2fca5dceb9e85cbc93bbf070c86c82babda9d3b9171d0c0fe7e4303c1c12a420385b8295f4dda6b38ede2e4ba00e923d8444c6f47b808086ce526ab1a2b24a311285112351905054f6c424853a05bda5e3b651d5b21e6a24389c540cb970e1752f3c1c3273e760742fbced30c00e86a79dbbf5a08743e6ec1cd8b13c9ced2c0f478ce1c588315bc6a824ec5cee993d24632ce98adc4751d9139314ea24816415124802ad405631ca1f9e3d3305389c54581cce2972bfd4d3f9c596bd3b914c28da8906bb89f9339968308a9657c89a9982d8738a1544987e28cd90fb303c204619c393c81393c88834894498fe28b79844702404d000e3858b1c415b27cb89961cdb85db75738ced3acbd6825be954bc1529a5846255a69393c987caf1b5c676a162bb3e93d3894965514a29a4d82e2fb6ab8bede262bbb6d8ae516c578dedd262bbb218db05e6780e64b1b458511981a45aab6ccc0a8c6a533b47b52c9b93524a299dd7ce4d6d4e0cc3302c3b12726e68b56b46c38882903798814f8761b4fbc14250c2dad5d4dee2ee3524f60e1e7008a2af18e6e1f868b083261559e62cffd032869350296384c121d5994479664b841a6cda5e3d47af0f20ed9ad8a710264b807d2a21baa48fe9d85492b1cf249186740c873a93685e203b6fb0f36124f2217ba60c47d4f5c81244579704516ed3ad6788ace6ce11d5a1ee9288ae4a8608f6fb0cef47fbed31bab09782a68cc5a5925029a8b4a42494fbd43bf6b625acd5a17675af06693d4394b1f68245fe4c2391f0d4b2e9dac9eadb5ba4da006b6d28d260b15aab52fd2098698bd6212153ae4353a67b4d5677af219c6e888d915aad8d59351256234672f650a67b79409832864b3e4d742f199bd4bd66f7ea6ed889a094c3da51a1ee039d0b72ef00a9731205d1af1708a9424ca2bc0a8266cf304abd1c497ab6ecbad7abd43ca43625c1454b7be409fab80e98316a4716abedd221f7a081cdb4aeb60b26a37254926d97f6d1e755edda3eafd22eef73665c51572bca5a1595d7dd2270b8cce5b78806477801f3b3b65ac33a20a519164404ca14dba8a3175adb153383546c6c965c217bf4e81a7d497b6c9c34e11e5209d002523050c2753da2921e9126e6d11bb8e812b28e686fa11a0fb124ff278d4fd7e7b1a1295353033ec4a09027f8109302f6833cb11ce489d1204fec05580ab0111c991e6843c47a642ad164b9218988856bd443d1c0321fd222ec34289f9791e67ebe654dbb523e5fa78c0f377ab0c10175150db69235d8201f68e8051d9932dfcf6c4394f9c2102bd194f98426aba75d1f51834944d7a78608360f389469c5c0610b848143f02f7078950b1caaac0b0e6d0bae47b81a9ca679381dd460ef00714cb0935e5e1d0ea89574150dde78bcf14b0f87c6cd71d7d5f11e6e10bf7185f8eace38ebc6cc7277b88d2b808337e6798b0c6000ad1d660c40882004305b93b68edb478c18c9fd1cb7818a741f373667766f0b1006fb09b10a710b71461222ab4f9f24682ac98dd52ccb469aa66db5562e681444e7c80b9a322b750b4a86c820d3a2a8afc85af01b08852460105805a80414028752a490fb2b49522b495e308a6010996ce144c9091954432a93b76093636cf28b2936f9c426264772d8b24274ca5db7d5c0fb8ae6a9689e8ae6a9e8f4159d8a6440ea6efc3d5c21346c5c214face8b5ab771e0eebfd62d5ac96e87c7c5a801af43c9c3713a426d24df40f8964ea6a9c86aba6071b91e4e14c2cb36899a2a84112c822d38d16201a9cba1e2e8ecfabc36d5c206edc987331b7650a206e4b90070498276e018a410b106dd7695e30dbae871b80dfb83f7c755fce72616ee3de7cde223838af71655c10e8e58247707e08c0cd49c63d61f7944da8130c529f8763d2c3954149924821254a2411ca2b2287864eb004f28a482412898989cac4c3991806282729aa1e6efe8d6bf3a029a302528205fa94e72b7f881091478a4823d2c82aca264ddc0284c2ea61bda80ae8fbf7ef4622cc13bc241e16484a48281e105a66bd8cccd324294592240167b05aad5af4b03a0c8de9aa59a9a8a8b0a87840605945e55d0f57c81335aa24a8809230a1e5174f0c8396cbca4011ab872584f5622591fb2c469890e406aeb1b1d2a28587d3c22382de2421e77043e3dc75b954404ab0e0c2a5055f1645e941c282a9805c6015900b5601a9807e55402cac02a218a567751824b9039a2c57cd6d09f280c0320b8b87c33a8b8783e69fc33e9cc615f2040d1f524eb11a338ee4d3c3152223441829624c9f755ba670d5b0784060f9b805a80887bb798bd4bcc665f5b05eac212cd7651199ac9a83ac0fe43ecb0331e648cd8166d0c27241a0087324c2589369b3d65a6bed89c68d202517bb52484fcf90d74b624ffa489f9c7283b47840ee038a10d231e2ee783e481089600025e91a7741c4ce9020874d0f175e911d2ebc2066bed8c58b172e5c5af0bdd143ca05352e383459fd5f9075411a172c02d590fbab0b12c12ee8a34a82f7be0ac86302e91d0dd67f578806eb4f774783f5a6cba3c17a7b7b34585fba0b68b09e741f1061eaa587e3e15683f5dd9dd77888644abd20804ca1065378e58a75e290e661332d39d32d3d9c90273279ecd145fe343fc881f4c8107b16b62214ac2024f7690ca1394657047aca1e20b2a7b12c48bbb26753e2191a2d1a641f80c212ca90fb415a26369054176c185b252624901344511a61c403da855dde081dbc843658c5d0898e208b8686ae58a25287288d11c3a294b2a2e6a7051bd61c8b841a14b24dbb676e808b2eb13b2789241659e9f23d349ba88ba60ce912bf64a144bc4443ac91af16d85510f4f212e7d006ff8a34f55dd4951b62e5aff0428ba68c0c2a2a821c320aea22e470164d0c8b211c0199436c284f966d2552f3708ac422d8fafea65225a0740f23b14280bcf12e6f3cfcea6e4a7fd11b21de39506f48efaebc911e0f7a53c21c48a274e3bd6f3a44228f649cb15d7a385ce7d53aa3cea26e0bb63e9c45d8150d1235d813634311460a5bfb267a42f068a7f947cc224c86af4b83b3954c9979f3cca78281dbb66bda4723a9c991a669dabf5d1d9411b4696744bbe203da957dbb11ed9adfaed32eecdb7db44bfbf69d76d16f07d2aed137bc449ed3ae54b2dd28855a6ba2acf550d6deadc4e79547560cc18e3eba4dcba1a1a16ee839a4431a0ee78bb6c08e46128b3c1a8d70a89d6e235ac21ad06469810e8d422d496eaa5d288e465867be826827e04003141f315f997a310818a302ab22d321220c0b563ef45ab302100e906481032c4af226ba84d99126ea2b9aa8b1985634d83a39a2a8ec9e1323e9a0a650833d977411173faa5688143a418e383c7ef51c31328708f9ce60c32efacc6023b06373f68108d3cf3c10631aebb111093700ca429eb022b76482e2a02787d92b22e106454c644bc48883d33dbbf943cb72022b7967254b794d46f69cd9c80a7942e22c33f2061b662f39a45dd8eca65997f964436cd7a398d1c6483fb46abb739b94529ee328a538a4e69d30c1ca6b3862044330c4d1f2cebc79274cb0d8370fc78cb22725c9e3185de21861228d82089ac8f50e653fed92198e834583e5fe11592edaaa9db6c052ac274f9a459eb4284f4c884e1c4eed88f9ca6190a29c134f1f0629ca140852ce5e60b01da6a4a8c3af336c219df9ba3dd460136d4838924586842358c857bb1e6f90a2fccaeee1c08068c93f7cc8231c230c115466792b9659f04c0cbf5e53a6dd10898c20fbfc74e6930de99027cb212dd850be72bc9d785af96a2c5ca4a225b98bf0ca809597994b8a62174da154166cd8af7e4d99f7a4b060b3232922e0a20b1172006a97bc58a699eb4eca4e5e99d970458e5058c31599468e50580392cc659d8c190c6b3339d911218728321171c8226622e41082a11c33117288a25dab98a34b9454e0e6a30b8f19d8b0562828a922aba8a85050124556e91416ac4c01d7c3cad3d49c534e8b001b8360c0440e552a2a5367a3b0042549b45a2d9d964e4b26a1856576b62816382edad0e6e8b4b4cb2cfb9c73eab4f24a87f6799fa3d3cad8755a99ae82d0704e76f90ce76078357b08f6e8451ceec8b165731862cf91e72e31bd3aad7c442be760f75ab91e93c75a59bec370ea05f5d025757ad865fb90ab317d24132c71887dbbe9c6186339f99294248943ec34f36eb82363ef5a9972a1651b9381f4d142c0c40fa48f8ef4c9988ef4494981055da57ce5fa5047be421deda3df3ae3b98712d32d9491bbdb4633305f1bb447c817a63df309269b9a44382691c94e227b0e077de9a952eada5367923de7fc21e7e05912e9266b3def768f14d6a4b38834df9451fd2a15cdc2d9d33d916622993233491224499024419292849e5135d92db2e6b773b24b3265289266f56bb556459198bc46d6bc86726272106cb5b2367b348c524d20d52c3a916ac886358f265e0571ba3dddf4019c70110c9cdce4f6363d7b983cc813f6e49cfd34b9f470ece7d551658b4d7aae25a251e449d455e4a642a8f652c0c9e893841c238e2ce00e60e523c7b936578c32964833389c29611206231639da7c89868c8920cf4bee314221e3002682589c3144775d441646610c90e76575a1803c1f41964c8f65ec02d9d6d40ba6cb01db9752888f2963234b48267ba4104abb085e07e47949cf619d9492063ea411a21df2d5048bfd45c29e6149691432320fb039f1b22787f10d87316499a5cc408e3c39beea4dd673933dde108970a543fe640c4f1c2291e74d16625fe9d832867f9432c545b2287b644ff67e3cdb3cb1e004fd91b18fff4094b17836d1251c9bf553829e3f31cb2ac41b65ee32ca3411c75194092cbde4ee35d17cddc3a51cce5787e78fec913d197d26b937f770fee4ded132f70ccb91ec993f3f180e29c5ed356605a594d291165252d8f9a825e18090afe74a40c84beea66bb9cbc3742b3d206a1c9d734115597e7427f5126047a6d047de6e4e3dfdf412606fc276b2b8cf731f512f01169b709d2c8e748e741360b1e99b8fd088ccdd34b2169baa3d37278f38591cde64fd0d91d89940f20e181331ce39eb6c22cba09f25b8e80d81f5ecd973b2b4046898479cace6a13d8548bf61ed64c7a37b8744024cb0e904ef845202f96eb77ff61fe95d6a8ad414a44ebebee2d09e0233e7a45ecf494b227159d7dde46e5e16234cc7ddbb89274f92dea3a7839421a1637bbc89f76ecbb2cd84bb9424ef9c24ddae93004fee1eefe50b04e9f231e7608ff986128544f7e9d4a6c7b9336518011f3d79676609f0e4ecdc0f24f276d4fd21f3e6d9f48625c093331c7f3a6d78a5637bf60de7c8939ee19cf80e0889749bb9ddeda43b6b934838e441da388943d9c349c9934d35339dbeddeeaaa6ed4ee6ec1d0e1370f2aebbcd3c853c1153538c39a3cc34ba2bd3b38f361c011da4cb9bb08eeef1d93105662e611df695339ce39d4b7d74654f8e24d993b91cfbef29fbee7b7b3aecbf049cdc24bb09c9fefbbeab3dbb21901cee4cd25de9d86ebfdd0e910a77746c26aa873593545cf4789c1c8740f2a5374447da30e9d90d2b0f9321487833f1b278931b6767ef86790938f9cc6e87ed7613e04d96974a82774309f064eff2dcd6999efda4ebce65d94fee463ab971b236d329dc99241eaa939bf03821ddccf3480f4defbedd15c984799ca04c3f3dbb2b7bd4fdba6edbbe6d2379dd4feedd74571de671f2ddf4d25d915e7af7ef9ebecb9d22f7180169310fd557a49fdce42109f33889a4abaec9bd2bb3c9b34b7ae96657dd93478fc7098e93c5631273974242ea0832474f472acbf7a953825054a78001098a70c42cdb8b1187944dcc0d48192f2a5ebe653c02456c5046982721a35d94bba85db35d92a8c1fe5083f28a05646e0a4a677aa40c7218b390428a5493c5cd4c19e986c8ea7b6004333d237de8c82fd8958ef9d5797291fa8979f0e42223bcc226ce3c398c58f49cb268d69706c139e408c82c8b220c1576c86646c84bd7e899dcb2e8d5ae16927d04224286b46b36116346a711ed0a553e4230db81142cc861cc2297724b3e6cbd34d839b335350dbf4c7066994330e4c9b435bb52eebfb4ba943f65ea5ff2a666d16090eee91afd7a03d88a3130bafc086fc0879b51bcd9aeada7b7c80536c073533f6f9111ae40100e60a002f53edcf0dcd4db4c4e7a401419e10ad417b90006e491b889de8e8a83c81bf9d79c61fb14cd5994674f690aa1dc8f1ced21b95f19b790ee89307d6707224c385ff2c824f2d319c99c2268463193cc2a721f9bd215ce0ee4ce80723897903729510e39ecdc61cd72d81d88345996658f14364e169d1d1071665328c9ce943d53091763ae53e6892f44d143ee4b1a7a5924900fdec4a1cc39b848fd0acb81613c04d9fc204416b9a315d80c595a218ddc44c80168094be67083875dc59b5a851c18a516359303cf74d6232e70366787e6ba434d2693e9fbbeeffbbeef33994c2693c9643a12a6effbbeeffbbe30d8cf6432994ca6effb3efaee8a73acbda18faea3946e1da9548a34a5128914694824cf8b349ed77591a6eb22cce85ca4a9341cb76d9166db46a348331a459a4853230cfd0866742dd2781f1d7b16694a1f9d461aeef4a3db1b23387a166bb218333a7de9661186debb5aaca131867e74eed208337abd5884199ddb36cf7b3db771ef9ec36da66fa68d3371df38fcc3c3453c7032994c2693c9643a9d4ea7d3e9743a1d3b77327ddff77ddff7a14edff77ddff77d2893c96432994ca6d36732994ca893c96432994c2613eafbbecf74ce8432a14ea6effbbeeffb3ed4e9fbbeeffbbe0f6532994c2693c974327ddff77ddff7a14edff77ddff77d28ce6432994c269389435d13eac61bd3e9f4cf6432994ca6effbbe137e42023cd90d7de49c8d84c30ed36fdfb66d3399b66d8766fa8e2c4be8ccaa324c2d4b474cf60ab84c6218960db1cab0cc083623b0531f230c05b44b6f00996cec8796e90f2dd706255ecddb0823afe1ced2c7cbeddbed220cc6e124b807e1b9e1fa7dac3e8ce7ae71dc37c073c3e10d74df30fe518f3df391b322dc3b0e7b80dbb66ddbb66ddb388ee3388ee38e04b76ddbb66ddbb6711cc771dcb66d5bd70663d6032e767dec7aaa9d9ed60c5300a5d239679513ffc88ed19410ec7ce4b48a718c2f12abf5da0debb59aa5867f64b8883c4679303985c04517cebb52e547f67084b5c7ee1daabcb9c8c5aebd07e1b9f168e8236f386787e61fd943ed1498f5d87778de37c073e3e10d94dee1d7d20be58674edfe2842c21ef0baaeebbaaeebba7b9e770eccdc7847c2c35dd77557c530d8ee42785e5f00f23ccfbb902436ef49803f5e358211393471d375dd393073d3e10d3c817dce39b51874a7a5e8126bb2875b7b4174a797dbe61281bb2333768e25d3934c4b51069cace82ad5f89031f41cedfbb88d44e0943531c668243c9b0605e58028a7e41894832964ae3b2d85ca1a584d0bd24e3feaee43d6081163e8e9c3a63b54276c7dec98d4e2125669f7f08e868134d875d4a57c60353c9fc57675d7345a1f76efb077d873b8d1358e8bbfcce207ecf6b8bdbbd16d49e79c6f50ca8cdde754102ca55864b5738611c938f4dafd217386af08c168ebcb8e1e92071b838420c447113db2dc604e4561c35696ff4c05c1ce560e674c05c1c63c4f230d9605c76ef6db83d23a6796cd994d24728661a1cc749e8bde105209193327ee219590311810e93347e7309e1fdaa8669262945e9b988ec3aa8f0b648c276398a31d766dd8d88135a2c199199bdd1bd59be8c0d2976e64d28d00666d129bc426b133366c7018430384c9da5d644db532c2a14d650d6bc7a28c8f4cda9dc6ae3313c4a90925902063781544c6f00a3b4f6ed1cc492722cc13d10a174c8148137c8ef012020f72df4407b66bb0366892031b5aec2ea0aac177a62569420337bffde4d163abeb4ca0b061adb9c6584799ca033b890189c32079c321ad238c52c9db86c397bc85331bf6b27b9b8c50cb0add755d5d7948f3c356eeb6cc6def3c1ca6afae5a3de5f40a591373ca63cc5bcab7ad566b55aa1f8cc92e7622759471ddd6442a26228964980847a42372fa86c3981c4a22a32ccbb21abf2c1b8d54e7b20fe78c5e51d40cb226fb46af29540a879d479428736114b2510a59b967280ca359e4103bd1cc3bbd826291432c1b22ec7c7a4789a819304ad4625d1422d3761965b68f5012ef74f670cee827ec45970884ac90b743a1480952e40d0045cc48e110fbe9f4ee66de9429a24a5baa989a41857756f5de311493162dce72f29f701813e915d9c685272f7938b6733887fbe9a4cd2bfd388cb167c12db00946c1a1c5dcb12e3bca09dee99c5de56527efba92d548144585439ab18efb09c679381e9616fbc9dc08cb46a3d16844733d61237a45eaaa1c7557fe5daf74c3981c56adda5804d73159e53208873120d8fdea4a0f7568843a345ab92be11ceea36f2533c89aac1fee90b787ad7cf270465cd2e11ceea56fe7246732ea2e71284b38a4b9c3abd2bd67ef574c4c05eb67adb67b761a79f4508746966ff1b095bda78c1e7a57b9d5c3317a8e54b9e60dd1e5d1eb949191b5c96c0f5bdb4330f7b6e1508746eede025b931156f1bc671e8ec76159e6b8ee72dc4ba5933a1adac399bcd52db5a1b66fdb6250de3625798b5698b68d5233c89a56a4517dfb8c4873f2ed3bc89a06c8983e25ca28919492ced9dd90d2c89b2cd1cc5d9eac4323973035838cc96e72439ab315563540d66432a65372509171d9c669363791334df3704cb86f1bed6af6ee6aaf3b34d30def34cd956a74892d448d3a9add67f380b81b17b9aefbb66d2e3936e8bdbb3aadbcbdcbdc5d3c20b86fef509e3d270b5df276848b4be68e3f7aec1eeeeb63b4f1facd0d8a5ba9fbe95d77c2254cf1bdb15915e38bca1bf7ec1c45b93a2e4aaa08ea810d755a2659f60ceb54771461fa26b7b3285588ca6195b47621cd5bedf2b6e5ac5faf2973df6f1fcfc3b5c1aeb30d76b795748ba5ddbe61493366234a536d50d206b98d87ddbe7156e42e73ef2e8182a8c85ce61e5ba46e72cfce913a127712a9243b2f882d779c87c3742d6edb69cc346edcc8f33c2fc7c6ea79dc472314fe31734eedee9dcba9b7df10231c76b6ffb0f4c9dc5198b35ce44e5db8619b79826c1d176220e7ee36b3bba1642287d9773a677424f276c399bb9b3c1d33530e873cd9bdee1e0ee5121cfe31738ec91bc2e75976d2370f6f3824e19dce15ef74ee5aaa1cddae6500967b8743b93357f603e8bee15047f7ed9b77eef2256bb673730b53cf3c928783fb8673b68fee715e98c228afe3509d87a37b0e87b9aef336a0cc556b6fccb5d65a2dde644c285f397bcbc5ff6ee9a74bbacd34ddb8e11e0d66db56bf05d99ceede479d5442d68432d7cd8a6d660fe774dca537006ffb08731b5e8d6e9f6de14ee39326d8eca4e39bbd84c36845ceae7938b8e7783873dc7612e9988743eab02c8fc20ac86e527881dce5ee36cde5fad2b67d0179bb5442d6c8bcf9644e7ad9a54fc7c351ef1e5e21e01e9f887b8c797277bd1b6eddc693b91b95e46d67e68aa5123226d370bcf436104be77001d72376777708669d56ee2a62777777b74e2769d941c8d1ae7e18816c8664d28b0919853c21dbe7d9534e9a905d6239a4c1ec19963e2f99659637c7fdc146be92c496514c28a613b389c9c45c22977236a304f38b998bdf78e5af7e28792082dcfde2e502a3bc5c0f2514358ee1f02930d81eb2432bb7758e93e781071e78e081071e78f80a0f6f5d1e5aa7ed5279abf5ae5d33deba6d178cb78e6a57ca5badabb40bf5d69c314e98470f08f03bec70f0f28287714399611c264687e802e5f22895ca617cc6e38b65c4ecb0005c4b29f6e23aaaece2250f07c505cdd3458c0eac76bd701143c6cb5d5a5e4097ab34a83a8c1b33cc0d7784f2aa731ece0c2c6d8ca9d4393923856d832d954b1b6c7dd57aca4535d86a1d75551a6c5d75bb065b971ece8779b8e173ab75eb5b6e58b3cb0a05b09c53cff27a96faab24f7c86028f31f339ac9a1b738353259999cf2d3c594a41fe5625707cc357668ae714e762d2ecdbf319b90de5f8935720a31a6efc3bb54e37d952af4965d3e75e5adc8a9d46d5aa65252a69e92524a29a59472c6d46d7aa652329739a9cbc83c757953038835f2f5041b0f5db2fcc3972c030340a5b5430c0ac68577f91f848834f7129fdc75b312575e628c31c6170f77641d74c86ee892b3bf78bc612b47952c637459ca2963831b83302491c90ac03b067604e530de8facc766dde6468ab9481878fee5c50d77641dfe02b349d4371db04e0b0635e3cabc834dca536ed3336ecc2a57e6565ead622e8daae6734e39e59c72c6eca14a4a17ff9e651cbbe18c0b179f97e618d6dc651d68a8ca312dd78c1d5a2f1ed2c8f1e778487dc10620521980dc225f14e52defbbdc1dee8cd68db93adc169818f7050eb3ab64dc985ddcf8bc71d5ab87734eb63877a3c539d9b5c0391a94352a1a3c131373beab5bafbdb9bfe0a521c40e1edeedb18006e54fb7887e408491387562facca571d3a787e3c2d2ca6b4199c60e524085913ce39cec5c384c3f5dd132f146f66471d2427ae0582e0cf9183162c49831a64dc7887196db740c1d8e6a99001c068cd661c41c068c5b19070f03cb9787f5ce735c1554e030a320767adfae23543927bb7c711747b9ac1b91a39411eba8727cf60cab3438ffe2bab828f705c3988cd31bee082596611b84719b06af0cdc3508e3ad4b1b84f1985b1b84816a160c1d70a89261fce5d2c381c1b141188f8163c4b8f52f2cda2bcbadaf418aef91e54dae348960f64ef248a7718f88437906ce3161e7b8883d7ea9ef0f88342cc74edee3e62d8edd4e99bc627395636fb5cbe4d869b46b75ec33edfab1db6817fe3df61fdad572ec39daf5e2d8856857cab1ef6817cab1f368d711ad9c837217a84bece2d24b29a59c3bb2cf781489721737ccb04e2bab8ef2e8e1a850cebd68f9392ecbb22ccb5c6ed331bb8bcbb3cb9b7d073a5d335a8f09800e3097a7d1ae97cbcfb44bc6e56dd8fc90438876bdb8fc8e48e3e232f3f018233d0f379ca754d60e1580fb066f8bc69d615d9bfb4383f2306e8e2bc40e1c5b5aee12710bfea141ecf8da6810fbbd360d623f9e61b90f8830d86f6eac0d62cfd74e16f6151b950b5e1a4234881de5ee6810bbeaf268b047b3b0131ca24cb0a7309c0a6bfee44f379458c704a4c136e11d9b1cbd8008d3efe11661848f20b28794e56b76960669b0e79553ce6c4a2925d865ec5229c4f26870870f1e22a92556cec7ab13878a646c4b62c26669a3678ebc64a341f9f27a1916d5a08c6719e7a28d326ed32b36bd72c32ecbb8cd94714f7fa93f9d68b8be87a85cf1238b4b2b05061b46a122a68c509d719bee914faf3794f9e5a31bda3c5a6957ea0bf65fc1e1e82c6f217b266be5321eeb653cb6ebcaf88d1b276b05d70663fea2ca3270d8e547211d30f3c0b243330fa7f925e61603f16040acc162cc94e9e1fd1c3bda358387f71710695ada167267d8caeb55b70a5155a3abd3cab556a152d55a6badb556193254aa951beaa81e3d2054aff1fa1a35ae7abdaa7f3794f98b46c8d5255730d7f025bb788e3545c4987e4599263160545eaf52f9eb676cd8449ad214f93b0d211bef1b71f262341a8d461508190f776450060e5b59c6c1b3dc506696afdcd025affcaa5c557245d1c8dc78ad19c66da60c0d6e8c7f814c991b7769d7cbbbc5ef1bd5f2d8729a16f5a7975bef72197f719bb912f36fb823831fbd78f57060682e9974cd0bc20253a8c1147c32c8c15c995568506f989839932b730ca11a8e75654c33e148868c199b2c83278a2ca3ca08571e9aae1395e45aef11514966b98b1bcaece22f379cc92f5fb92c9721e321cda32e8feca9854a1e3d8f42973c027578f8924734607c84c39773e73a21a0297363f4becd0f3dba376e4d7d41954f6ff1be11d11be9706383ab5b616e6c30c68d0d9e2eebd6b7b8b141183736e8527f6ffdefca4d31bfdcc872431e2319009761ba774300707526a7bc7a41045102099a3892018075a2121737662e47833666aecd0f32ac5c93a241d93899db1562078f1e110605ab6e188572fd97dd75c30e3b7cc3d169d02982b8800ca230249447f8f70830e7c4dc5ee58546ff460f37de7aed55b17774ae878cf9e88615eb80b93e46d5a08cc1a806a58ca796d8b07e846da64e1cca47c4a1dc7ddbdebdde2ee5a18d5cebc338054badf576cad498d0a43c94ea7bfd8e769d5ecfa35d9186f48a439d2894b567a5d1e92fa3d16badb5ca78f480183ddcd192f17ac3567ec1e1e8a7cbb8e18ebcf27a5bdc7005ebb4b2e9a7db748b7b3ac7b2facab9955b57701895e495af4c9157561e3d9c95dbf4caca0a102b57b94d8f56aea2f295d7bb02f3f2faae5d325e6fdb15e3f5a876b15eaf6a17f8fa4f991baf07db05e3f5ad76bd783d8d76b978fd4cbb5c5e6fa35d2d2efcfa1fda755f9fa35d7fbd10ed6279fd8e48d3e2f5326ece8b0ee7a22703873c598696638419bd5c871bcaf8cbcba3276304736983f52f326ed7607d8c6b5917d5603d78e3be59f5305e5c179786cb9d69b936f0b5b9f787df1c2c57881677c7ca0da3925c575fb9abab8c708e06eb57f88706eb53ae8d06eb51ae4d83330d561bf7e4d606eb6bae9dac7a93ab6ab09ee67eb2ea53176cb01e75690437e6d846fa086e27363710a1494a38d931fb954c1a0e79724e76cf6aa8d3e9439534d3b437662cc321cff47474f632ab42f954fe3dac99e52c8fed0a4fffcd8e65d1bba6bd450b355163d157a050d10b225ee255e9d85d956e8f9934cd6a584ade286d276f7c86c31fcf1448c91e559acd2e1fb59b6e68df9d6e3a5dc3274d38c92e4acb2eeaf4ec287cd2049be1214ed770cee9269cd3e2da0e76c2b14196b778f77bdb0bc2bb76e9e9688169832caa6b38fcca8aca5552e59fca173f0dfbbeecf19aca77d5f76138547df2c3ababbebb8aed42b9c421a7baf229aa4bd597f2cf1e45bb2828e7625491f2f1ca0b45d3b3bb7a8c598c2b2b580587f5845138b4258cf20ec33928b728d862d10bc2d31e3dcdbbb5d74c377c36652c39862c2d5aacaca8a8ac56292928282ad5c98989492a85427ddfe96432595b2a91489ed7751cb76da31c4759aaa07eba31c2a070584f0fb51c51b02c57507098e5f816302d7098c9c7157c3a61940bf5187198bdbb3c2a34bddbc1c222b2e9a5b7766b1f3d1d5ad4344d5b5dded0845d1bda4b80271671c204fb2ce3331413123639ec6ec23b58b60f77b0bc7a17f2649b51506e3353aecd9f77d5c995f687368739cc4128efd0ecdda47bea8645e4ce3b27a396b9e0b0863ba7731f50b628fc920985c31917b795c1bbb8bc8b4ccbad8b77c9845d6e8c2cd97df70712d9fbe9fe90d9f33a623b98bc9ef79e06bb854c423a9e359c8cd4795dc7962b1d59c414c0b276244ac7329924f7143e449ac88af01cbb9516d29edcb963117e6492320c99444870e3861c4e26725f8b424a9e0e8cebbaccd3ce559a4bd7ae97e108648f29ddf374ccac9d1b755988e50be4f8923780d24bda4b1956c28ae0db7448ea483766ec62c3ee36725131afb02cb1ca73d8b19dc84d243521799713490d875d2424a31784f421b1e4b9318212b7588e939e7679292057085801810c96f8e9b9c12fe2e27231460207f11b42807a6e0801ba4102e327e29fd0def83e11e5e3b37b84fc0d214039bb378400e592a72303c14f6e89610e39e222f8d143400974799cac19f009f1063f9c41800b7e421e23c0e5f8186300dfa5e5f1a694026bd3d4e3e1728c83ac8ab878bcc177c1398888372e6e8fc9eae35b843c928511d7871051146a0cc46a6f22edad1db9428eb8851a636bd32da45d15cbf8c270add6862a797b48fb748d3e518c51fe2d645ba87d7a48bfe4b5c77e453c2716504ca23cafe8203987e650bbb01309093551839890116b04c3bd64a86bf423ee28d879fa90a8856647fb4d614379ecf3524ad9724e9a45eeb7509206fba17c0d097ae32851c28006faf241245e210003f3f2f12648c7476f47e3ca9578e26449223815dbddbb878370efce731379787fe8a2d232f1e288198c30114656974dbb8b8b0a7d768965cfaeca05b63e1b42c33999c542cb5d3b83f03dba84273db9df8410d4bfedf189f9ef72e037dfb968474fd0c738af4e97352deb57bc4d7ff6eb114b9fc9f2e9eebb3da459fd94186cd82f89b7faeac46624fbf49006dbe73571fb3408b68663839289efa1c4ed3344be264bca9612e3a226006188055964951c83587082ccd1acd33a0c08181219d324a053cf5705db4af4ab436900d52363be2a5894119406228d9435f18dfb255b8908e37539882e93202944b07f7abfa68ca967b28e7023edebb6faf8193191020e1b8adc267c6b7a095b9586c3e39656b0212928d2c8f73f231f14a20b45834f09aae733f219f98cc4cf08aae733f229f994f47c4a5028231106d59361346a3262997cc9571f893019462388d188d188d158ad2a1ef9aa6043d34b2a21573bbebe1096040bc29470292957f2473d88c9898837aa20f63c376fa57e7222e2cd090e127168570f5bd84dbc491d7557f23b26abcf73f3dd95c4112022dee4c0416c70d051412678c5e3fbc93fdc63f2ef27f88923e4e7e3cd8ae255024e8e3a3dcac9513888bc39b9ea3c72152f897a7a7203f5f4ec30c1ab1d265ff1f86ef2ef269737d52b40bcf970132982c5ac90ee11427b56f31fea3565528e00d48731f061e0091edf51f06a620025dea01e6f8a30e2042dc835c72026bc2187fd6a946bbd7ea5be7a7dd82f21425047b990ef29abf9d5796e5217f5d479a09efa7ceafb0a0749f91e0405fbf0dc70df455d145ecda7ce73936435f1aa00df51e7f11df579140ef2c4c43c504fe120fd2046729fe786e786931daa894c56c77f2f88307d5551ee9f60a2dcff4cf0671264628299048d50a89e2983c2e155f627262914aa27f7b7512a092b1f7e3e263f93d5afd5aa4c7e4c7e4c7ef2cd0d068425a1a25d14ab02538259d12e29531ab0f4540376dec3c132cf2468b5a35f41d817ac8912932a96d8fad0242845a4a95244a69e141151ee1502bea38ec21840fd7bbcf96e0a0d16859f58618f3d9a4f0e4940a61e2041fa0504040404040494f249f9a47c523e299f944fca4748484848a85f42edd344faa78df491482333ea489d36431d015344167524d2685c88aece41043fdcaf0c8747bd841812935a53d6a2b0cac424a8b386fbc8186cc5a13d627a997c4c444c3f26239146daa0c9fa7e228c146c4802f29932dfad9229a3052121012121012121018524201f538fd6af21b77d428ec8276463a62bd92385f41079917eaaaaef154d1909e4114d99ef242293e4939bf422f59086b621208aa30422016148a64c1c8d20b79661147bfdb42bac3e4d84231245ee734cc466a25f1c917ed19e99c52cfa8ef9449af9ef7d6c0491044483f8022c053d143691d62f1250bf30241fde48405366239aac0eab4f918a28ac3e24a0ea63ea91339a7094afdbb6f54498de86723f4b6ca284ab1e91166c68c2119958228951982831099a32ef697cd263ea001421e24a2e31bee54c7cd88a98ceb48b4683cd493cd360eb74190b29f5a4fca1658a44963c796a0f650c7dbc5328c789cdcf39ef0f6d1ebbda6d1af4ce78d915ad0c37d18cfc9dc13398c6d704dbb2bb6683ac99729031190b59d33d59ca2cbf80965206f528dd5e3e8c5806512193047d4db0720e58eeaedd70be2e024a12f3283d881cc3911bd3ed5dd506db4ed6d068c88455c1c4d09018b0ccf3bc39e72465a7a4a8c4bb5be9d6c9d2b0dc75d22342bbf480905114b11f5d23454a9748a68cc5c754769664b25ae2a1daad212b4091fb7d45bb4879264ed1eaa10cd8b0a5d2e06715cdea700e95c256a6ad7945b3faa14c3294011bcaa13974850d2592a12c72d8434112890a894aa5c7ebd3065032b23c5e057e4db02f2d2e122de925f2cafcc4b20b2b3dbc4fb92487183da5947a3894ed0dc1658a2f9dd3c3993693de79ef660f79729d975de0b0bbf27a51389419b5f2ec52686525fb8ab4bd44e664b772331d5c9ed10ad5c4d89cf3f704a57496fba6b7f479496f0f67cef9946bc393cf9b72d54579e9a68860f90fb68b5e2c0a51f8c930728c02148a668715512c26515f21339d42582e6970968e42f34e8725bcd3b9743a49aaef0f4f3ef18e149afa0e4adf7149a638d4e95438ac2c57fd3746960a87f43353a5527de5293854ddfbea9e34c17e58a7cb1fca57f0533eefceccdf3d1c39f5347f3807058732ab3e531eea74b9f414ec58e958c65438a7f479efed0531bd140e794a38a71ecbd31b02c33c1cca2599a24e6f86f28e5d1d54f67ef2798f40e51cef27f3a54bdb43c15d839ef7d4ad0d7a4fb9b64154f6ced18e744f5e5fba27f770b8efda06492791fe1b9b45ba7743141027273df55f967f37e595846d9f273d641764cd924c529542ccc368a82344f5de91a88a963c1cf2c45cbb6fdd101d944f9ccc29274136f49177522b2957f9e82d72e8ebe9a59f17eb4ee9b399255011a998020b02fbc4ab52e9a457954a8557259c23924eef853a797ab7e7306e928e510ab06409f06492e791f009136ca52f5dea09917252cae9b19fce65d22380ccf4288f1e01644ea947c1274db015fff4e80d91728a73524ec239d9a937513905b7b821cda30cd30657f0778a43157af22f759b3eb921129e0eecf4a49e7ae7b2930cc3d1c3e1979aab145efd5bdda6533735270e6f927ab4df67ea5fcae429d251dd492812eaa51beae4d267cd2accc3214ff6304fc7c44e3e336558e2c428d853e1d06202e041650ca306803a098549a10e2adbf4004694443a55bd7443542e4d8c1dc5660cfb9c292938ac194339cb8dcd42c1a56f3aa850277b57d1932ebd20ba934e7a8e78f24b3aeac69ca333650eeb690dbd9b4e6f0ee99da35773d82387db3dac83caa4873aa87cf2ed84742dea76ffeee94aab83ca1490b9bbe9863fe4edf6863aa91d3c1aa4f4dd9daf3744cdd0470692b12019c370b85d1e65b0dd71ecb84d97ee8c0d9df73ce99267a7c91e3bda4569e6d1aeed2cefec4c1785ed62fd0492b1cfb3dc1859b5664cbb324794b52bed7285c8d16aaf9027324d8bf136cd5d0efce609f96deba2067b2497c82b87f0ec911fddcea2c1edf5f46a9757ca25cd92422d1fbfc9882340b3869b605552a8254779552814e4b08b92c021f7918f086194ddb9132437414e90dc74e72efb46e20dab503187b60af6f3d5ad18bc72387dba488695b563a3144a6091c11ca550c20929945892a3144a0cc930394ae1012b7217451a99655a4888dc20092be04114b0b8d1726c50a372499665dbb61d47ccdb793290b0cbda47274cb05278a08a5c479727cb2e5c0c17e14eefb4b3c129c3e11c604c2224640527960835c1e2b834d859344b48ea8abb509f2d89b5421356a51d4883bd836159966152c8ab6bb4ecb1f6d24af911d9e8cd21aeb6ec39a5dc7a346b7cd5e2cd324ca3758e7a93346e23928c30f1734e596b064adba34b38fac99868569f4b89c186199151133f5ea699bc058294299e9d65b6e90f0c36cc88f84c193bb738313bc2d35aa2e933896446dac5042973222332a7cf4b48cf769bc686a6ccf6ae453d14ce2622eaa270facc9e70fa64447c365c89c2199bd14f26845cfbaa25725f25a29a5a495bd5e5ee726c191fb2e616925d0c9bd89c7374cc040a1bb672a84323d3635572b5d6239127863d7aa35146e7c470a466c85ecf43891acc307a370cf3326ca30cae05a8846bd78b9b98650ccd88000000009314000020100c088482c1683424a8e16c1d14000e8fb45a704c1589511083ca20638c010000000000000000040209821712711b0db3698c72f0dee37e86a01bd182a5c1ed2b1e5219fd6cd9a4d2490213f63145823eccec0f91d0aa84f658b2c32d6ea9aade94cd0ecbd102494d4d759b2e48ca22c61b19f1410021108524a005bca2defd99c6b096a3cb61c619a66c84423d786bc60d8568800828b26ab6b9570c43a141d1600747ff9ed05a3bef14b72c9dd0be9a7913ef8be4f169633cdabdc57797532c5c472b0ba29f95ce3a53a41a21da260e8dc9fe004cb336f4ad2cca1bc4effb6902afc19b3b3c97dfbfc83888b7d48c2ed08b1964aa03bf70fbce33b7da07f641bd8cab69642bbcc6f35bffc8f541eb1ac3f218150277e6791f442280586d320e5b8f06ffc7b3f1a67ed2a647d44ef37056228046fdcec1e5c6a05615cb364f1f44da62ecd44ce2ef636f719ca551f6078033570bcce196dc5a5fcb8de6033bdd0749fa8cca2afa2527c8bce7a129381d3dc844ec57c56f1bd181457a21bae8041bd181a5524b7d5c59baa30336b97aa87e73738708809108f6756ffe06611c30a0614eea6afe0baac2f80edda79406317340944ecf3453e12216aeb6248067ef79eba304eaafaf771b995792eee63da9edfa9d5550515e2584a05807c2a60305d7a08a1d1d4a5274209443e0c29440b55b14594f62eee3a003b9de106ef409c3e87daa0375f01ef28cd16bb8e87844f359ee84999afb3c42a5a8a8fa8e988f88fa41cc37447e21ee03d13e11e983087f88fd85c80f447d44dc0f62be21f20b711f88f689481f44f843ec2f447e206a9f5e2f81f72400ade3cb44174ad2f180812cebfb6975ad7042c1871ea49f50e1796e81dd91c555c5d0ebab73c7e94fd89754c8d2bdf97beab854c8654cdc4f52b5dda8cdaa18d30c14c2b2fea9a4026c44f4dabe8c96615ce92bce24224888065d08799ab1e51a31ea5dff99aee46613c1fe4c6528a49e28b4343885145f005645adc0400cca068a08a43c44093fe758a861344ba9cd15c4d56ba50cf136c3d20784d112bf66407f3d05c8765019dc580a31046a4a3c0d8629c40642c7f32786d1b225c766ebe98fb85aaf100ea08b821c56f61523b11d4fc8255ca82aa297210a911f7c2f55d0d6b91a4a08788d345738044302442a8ec09b715a7e2da081725bd434de8e5441d7531145eeca26417ee9735b5598f78c0eb3300457fb86f0496757b7c56feedda0bffdd0f7942809435bc574a83d318fdae75eed0a8067532ab9f1d6959b65d5ab2d57131df9b107d781e589251584b14cab745153dac080746bd5ac6bc7c48ac0c4c4fbae001b4ec16208f111914f3de9000bc45f3dd7f34a27f824483fabd95f4896cd7d159b8f253603809e845257dd147d6f34e45c7ee1bb47375df461c58326a534f8cc40def35b8cb785bf6d918e9a0336eb9900c287a96a88d604384f354ee9462406b6379a80e2000dff6715905d6ff3e4e490d8606d1d5ff988a855304b8f18ef798ce846683fb34d6841b203d528f23a6e1daf5e46d4d137db322911d901bc581e16f29037735f46503113db3ceedc9e32521815ba34b3d18d3d294cf15bc9f58a881be0800259c9e6797a490e383ac8305bb4194fd073683ac2b2e7914c119b41fe5802ab3a0953dd52718ee0cc33bd961dbe229026048507872296c3c61887a6140acbacd9817625864cc018c561a35f3a4bce720b7da2e8347754607b9dc10e6607851ee997fa9deef577d59e74fd2f27d3c3a2f2466e24aa448c96c613dd71089296e249180ab6ebca7e5a25a3132f4206cd1b65d1095e913c1e1bf5701cd6538d5b00c30f759b0d216c8440c95f27c2091032ef09843eddcb52556431b647835d08e69a9ea17b316f624ccd99769e514996568d6e617b92b05e33bc2b33a8d716cec5a2488bdb137529c9925d091018d2212823a20ba591a91facc422f381486079afc00862ef0850cd50591db8c7bc2d27a2e1bcf5ca904112342cbe8e711778841eea5f4f0cccda9e75ed09c546f2ce57d7c2551020a8cb972f5c63807118283a82758bda18491c5a2921df478448610cb110531fca1fabc453e9858164c2a585ee96e47b3bc302dcc9c031943b6cd82f9bdc8a0021f3626e3f4305cab9579490233870cc09b92e9c477176f87de25a66b1df3a8f3da7276aab9c4486f2141810f50c6e6fba3f50169e43f66a819b63cd5a22a6e94bebbbd0545883737e0740df52adcb0626265aee5034522a6b9b0353400a59852bf7e4d3ce4563303ff3131318dba181ddc1297609736b886e074e6030d8372bfaf7c827c7ac6dce8bbf7e76e6a9f88d8f34f3328b7e87121266b5e6d51b1cce2a9b3cc9e42159b175b2fd801a368bb34de2eaa0c819e774ac3ce63857173737100bc46c411bdd45aae00d641e05f722ba454c5929b96f16ecb9060861f366601edda5a8892a45423d07b2881cd7eeaaac8b103e2f3c5a7a952cdd02a5ec1d1193a24a547b7148ec41a97ea66ccb125e6a323d1ed6baf80fa318251f648e3dd9564cace2d426dc07455be4df84e825a752ca517cb1a7a9fea58426844dda50c4aac82fceb014be23f6b37e1858a1f39e56a3c878c7111c668533214481fafd02f4145b2225c3d387be387b3ff3222da71d25d30db32c9ba5561b8619c1d85efe7b06ccf077af486d1c9e1031fa488d67993ba26bdb972f0d6524f279800ffb54f50941dcf486ac522220a7c2e195693e140016bac481e9c8be4ec899096f284b1e5559698c385c7cef0e93ee10d059656972b1417d010d9fb9dc067ba2ca8c3bdf046b9b3453c51120b0ff182f518d371bce72b8c91875e4dd4b69248e69d866cf8bdc8093ef51f969724409793c6873be1f6e273687f629d68de2b220a1ecc6af59c7f221231b6cb7eb11da18ee73557642a4b2a01a27836260e901ab1d1820678152a54ccd7694fd94150fbe5702f5ebbe4137c62f5e4820617aca0a452e2f873750db5d386b1f406b9e6bd78e2f10f365178bd8dc70b3db926b8fc12f1ae75aa4fc50937d412e273e8ef80088c52efe82565510338d8427961806f37b31279f89b7e2dd0133ee9bc40b5138e2c1ff44c248ae61316defd4a2243adb6e617291d965e737e005ae1601d41b4a44781f45d25b9f5a11778d9562e2aa2c6d3f9fc7838901ca7cd893a4d12165442fd0ca46ff32ae5bdd1425a38b4acb0afa3ef7ee5c2a8df135f920402b515b02524101c20bb2cc4fdac5555a2904dfb61bba088623363490e7b1549bbb50a76756a0633257088dfa774454f950e9f476732e2b2f97825217bb066c53163483093bd2d34055f23aef7df1d943f31cc55ddf4a51b6996fa7ba42b8ae46322798462952eed71d4e511a233764224edc55ada5e02cfadcdab21970315d1c13879ee75f5444feb9edd7c3a7b8cba1b2f8a7de3d4d8ad59bd385e67cc1fe35726fdbaa1619047b9fcfd6606a80e52910b8137ad56b67d8ef602865e79b5fa387435c8f4ed00e7026433c66695574a2f63da0c82342df885933d2bb02f5f2322dbae5179bd788e595b58edbf9689f21edca8f9fb70229825146097aace3ee9167a320b3cce1d3c48f817bf8e95f6d8290a9a35e057da43401d404a38e42b25921502d48c3073ff27f5fa0a0d5381b86d052dcd8780c65fd285b4505e1f539222829fc522affa352fc6f25fa7f2cf131a868e963e463ee1efea42f61528398f46eb156108ffd0fc46df79092e0ea779dcdda9f9a84cbd4acfda897703223b76b93af3ef5e2400affbbb5de5024a65ce14d1877ced1951b2492c9d1cf9e58df5277a8c44a8599187085ab6ae1f63942eda8766ceb58735e69994aec8b6dcf060d9ad0b778bd971f70e9b057ee6a778da35327ae0b380668e6c226860ced0a217158415f0fb0bacc1c6a294b9246c34dcc1a12a3d5b921e544fcf1b7999a3e7cdb541c0d1352c5938730173310c864627977b0e8ce0617757ac7443d01d795035823ecb7239e97493a2a4b64cc0cf552c029814f031ab9b18618ccef5aa3ef7865224b62e57c9bf688a50ce2292fc58ea423c647e1db23e673ccb2243c27cd80ec6d77f799108e720122b61deeaab71373a462bc6f915014787a9c8a399203172b39207eb100d65faa5cff1b138e3d640345ae76caf3e044270c7c89fe7b059f1071e43108e066065e4c47ee5d4c1986665ce9b4eefeb5445ced63c2b88948c9cdc0e02c5f69f277802fea706ab24837c5ec26a839ae4aa1d433a7944aa70fb2b982dbbe6e4c3ea28ccd5a6432a7af814990cb88e07720656d90c65b7acd37510054bf6637288002d1536ecd6791759408d1a409a15fc4c38aba7b1742f64f5027e9c0eeaf50ba8abb1c4c3a308da54489fe6600045a48e3efcf812fb420be1c4406edbe383e0200f6fb5640cc017f0e7f6020366f47b05edd58c068eefc1b5f53914f20dcc5757fee48a49fa06a467396e96e8f21336076f2afbb0642a10c1de8b42e9b6fa8e5f8039babc194c7cf6bc635665268803f4981d00b1188471e9ec2739ea26d25ab6647348814762080057c448b951a24bd9476dfd9c4902ae36dd01023b826c4751351a98055ac733571e5906900138a0ff5889459ec88f6f0a1f46441e6a401e8ab76a1e6570f105cf000044737c4f4fd6f2b3d0c028a54eeb37e3b721d25e83592ec25343769f25f629320d92254ade6a1c4d31728db773e15186802d5e78687eb937164579eb6c5e0bfde4ff0652d3658bb05daca846162b8cee0522776b561765de6d052cbb08f2951ab9abaa5dbadd297f5aaf25c023df37382177bf0927725ff8948f40d5fc84ef45f1caaf9ee7ed191d4e8204939f13a775b382bd12005b7e9298cc2d3dedd94063b209d8b12b809ccb4a2979f43633ce27bd579f53081419b27dbad912a41df7ac4f1e704a4a8b95903865c5542a525848aeec2157d6bf2916464afe1a3d1507f4188949c9932a32250465ecb22c663775eb49dea5eeaa7e46b219c040c3589a64623359e1b1fd7cfa285bdddea30a1c03968f81ea1ecb5b74ab48666bc1451d87cb75e51d3b4db6f0806e4ab0a96e262b3fd303b7243328a1dfcb8602bace78a4c2b673d1548900ac7a2d7fa4869b260084be195870d60b5467c0789d219a3c28c0e2f2ad374bd6bf1ad817b358d59bd029002d058ef2c0040f88c51df029079668c12ed0497c6b5cd58cb6cd3d50c272574e560587080a23c5ad7f461fd789c2d59a9ad6f3ee4065a2b9e4845634c78f0cff6e9a1c79d6a77fbe4cf5fcb7e080696072400f3de05515c30b14334baaec3134d18bb5725910baf4455478889861d7407456c86cef0ed44f6ad998ed087d8f7bfdc2a72d68ab936856891715eec2fb023430d5046c7147c65a6b218de76a40af0470ae58ceff56aeb39d29b7416b7c8c966feba092ca31c8a295bb4351c0dfe60d7c6394ef1b25b8ffd49244ae1da7c516a6d3818e528ae8654b5a31b6d47f750c2cf7c6531f396f19d03083c0510e690ba1eca244ea4281ca41ca05648bf626d2632438583e79922c5000a656b0c4f62100de2241557ae266d268c5838ba78450e3fccb11274bf6fe7d1f300fee7c1c3341475312db01075460d50d2508e29457d779bd30fd56d7afd5570db589ed9218a0c5f5e45634a88132bddaf084eb416f8064341fb07cd0cb513fa639700426899528cf30e0a1f0cf3d389460fca30f9fc47a0070a0c899d157140d928f53f9b725e2157117b6a6705c156701be4599e8f2fde7bc56065af6b67656deea9babe62e9a503efe10dcb183fc39c80c4c2d30e01870cb689e1fc698dc9c61d90f6aeca811db76cc57bad2f2a7d8ecbcb163b954bf6614d2d7ceae0d6743a834955987534597f23e5cd7fd22d32dd03022df103bdec1c8dfa10d92da679c9bd7717524241fd01a57691c2b3276342acbf8ccaa29fcce89e7fa93cd10387ee9286e1a817086ee6bafd41c0344152772532fda989e3452d0c4bc7b53f8797cdee4a73e6844e904ff85231bfeae6e69b1eb2cf8dc43a15247e0211e1402ddc585a8fb7ec957d92f3c5ff86679fc9e773da8986949393e04e007c7847106ce43122bada82d76482183ab0586df1144c80f7169fe634e1a527260ac890f856d09ad190a585e35ccc23265a67239ce3a35235d41de089625742f71cb7d47d961fff3eacb23e030be1f347b57bf3f4e4d598862b78c1a50e44a5747047e54c9098db0061710d646bf92e3a7eda64681aa8d81b561ea0936bd583af21e4d8445659620dd2854bed41ac21831d2a1e3051156c7455547ec14676ce18b01735fdbcc6957d59d8ef19a6d4569c69754c03bfced6dad8126111561a1e138b00aa1b4463a3de0ddb917a4ba0470e6b59f56735ffc0d74f81f2c6b52f9d084f3bc3bbaa161e23ad336288cf0ba216004970864391b672af08eb479c90f0580d69a8c530e06e452e3b82228919e8091a31279cc33c255114411c1ef9397133951460fd4b021d0639a58e8e7fc1b2e345d976709eab21e2a1a6c5d89039b8c92f460fd062c79dcdfb6e201590176b07dcd87b635dc8d20e0b53d813c156fdb82241885a8d56c61c12099963a3d79e78c347eda861794ab629bdf868fb3c20f6482562cd0288a2301b5fa2885a9d7400eaef13377576c857063b4508d26e197ed76743feefe7f36c0954a9be1f3432519be82331019794fe79e0cc5c41f95aecb382a37dcc6808d5c0c2b15819c38a7832489997cfd49c5bfb2fea538b2cb477eba982ea92832f517ea5b1205410be2e64bf4f344eedb3c4e9da8acc7e0d14073aca044d1481ab33c8f6eb9488d8f8f84c692f5c38efa5c55926147d35849a07b536cd3a9671520973f93af2df361006a3f0e872e73400953d637c50418ab007daa5ba6c542214d31ae560912c8221fc9982f4c5780e18d7876c331d96768f57b532360b170b0bbde9757dff7b107731b93502c6215b688a5c9b3879094c9d25aa12a167336c261f838805cb87d1e029dcf875607bff9b40092d103c3cfb1efea6b1cf5b9a23471c821c2ee36f0c94aeafd705e960d9dda0dd0645e3e7fe2d241f77507bc4822f0adb35e9cbdabf6631324947ee7c95e3c2411982a2a106c3a8852f789c4fc24c04381f32476e1de6ff81e40c67bd7fd4ebd31d19aacdf3b0b32d75cbc3e02e12bb98671898b61a9ff28deb0ed0824c07da8eafdca88f9bc0eebfdf404f156b8cdb01ce594e8aacd863f915652efc3a531faeaf4c01ce0809d3713402c61f44be73907a2ede699da85c14642e47c6599a2f85dd16fa118e24a99dfc2cf1ea8a0a0d18533a020a64865e555294a1557f72376058f4e9316552a84d194546c4de9a4b9dfc43f3196dc250a6bbb950a93f1acb0fcaecfa36e69635ae14123456a1f4de871796ff6c2f835063cbe5a9f9689bd7b0d2947f9c884750570471f0e5b40de49de69ce2f78841bef25dcb31430a8d33acb06b4a8271002f45cb3b8589160c62e596701792602cd73bba7ed1caa3d9dcadbf6f74cbe6b1fa7ca3e5353293e8be62b1879780e78325aea55e57f57225da05ecca12d88f42a875629eb8c5ab1575240116eb5373119827d32d21ac2127080ff8680d08b1aa308f7763b0467887e7972fc68d5e963a4e8c2499f763ebdaa6bbd018560a8a7182265ed0e63de9a767da84bdb4b9a5f40ec5795b4f82b9202bc2bafc0db78fd01b70da30370d979869daecae6ffe9284500942c7cbbc329cc2222e917ec9bc02a1c977677980d8960f22bd87a3c4506ec984b011e7ea4c1965761d133ca78047c2911ecf3883cb63cd87486e6351c0fecf0b9f12f2bb4363d9a1aacd1ce290baff1a717be093dc6f16b9da5b3076305b3dae16f7133d2b4d4a095c9a3f17741628c4e00e03f6c5b5dc2218dfea61b1b8967d10e64f6906e5ac8d76e78cd3baa410e4e5725900064539db4f97d97a54eab26e3094432c8392e163aa00dde417205b0716950509f3ca5fbe30b749358a8d81376784d212be939d58f0eaecc1fc72e4e8e106c518377377b1480c18ae080a57278b109740c6cabc0867d727fd4421f55674cacc374534db8a0a9aab28dc5d97cf74d668aeb05e6854756901ed5f67eef8666ad56e62be56000cfe1fcc154cbdff2412332e95d6e49a29d65e536c1783c2f8fd9b8408651dcf82976e4cc43989876c7e68a5181892e43533f9798cb66a89f5610f8d6b353068c8ee63034cc4d2781efc4c32bb5147962a438c3c6a2ef6162d69507ae21febbf6ff642810d75f166172286acc3efcad1ccc5ea12b9b92ac1f9dbff80e295c569145fee9cfd21ff45eb0507d01418003f1d9ec68d3023d550b86c09b26f080852317c6627d20593a54d69c404265364bdf9c236bcee1d50d2f7bb8a5b768420a022bed90624bdbcd632b4ba40fa33e1c45977f984b1e2eb129bd1dbbe08f3f28c9ce9aec246a07c6d087c7f43b84bb3f7aeca02359a0045df9a57e9b49392401341237d21f328d2a47394d81dfc6176125dfd1a7c7c2746d8630a357b70004820442dff579aa79a0aa7a9eb3d4cc8b4d11db98cda7ae643ba4adb71c144cb9e414ba56d94db1409aa5243bd458f020709e641283de35c756b5986cbeaa334a300af04992240cbe6b63cc69938dd624a8daa979f58ef1d8569f533d9b4206d89badd64a267295050504f5d594f9360fab37e5b6041a65547e5631d4b7c05a6e9eb8b1e77d24bd756f3c87d0a1642f74f9911d0e794dbc5172b2276b4744ca15d7a7a79036bf4eabd97450bee066c27030a11e23099a85418932d6008e2b45ff530d1e86fbeab618551614d06962ab095122ec89197f44f4358a7c7ecd7d4670ff363211eca6b000cffbff2a9ccc8991c5e50d63349c376ffd2239ccb187a2348c61c606f5f562246c165dab76950c10f42e7ab5a53c92bfb10d7f1e7b7fff2ff3bcbdf6b52b1fbf481131605b9b27989050cad0bf70d9faf1cb754c3698cb36d666f9de335f99c435d4631eabf49c59059352820d5e1d5de5f3b7e2008a958fcb121d5125ffec968b201a25fa8129ed975e813079e084e7dd59d3d882025cd956b175d22c8ade3d07ab4683dbe49219184fe0defffc64b93c5bd34ed702358406d1a562aa98df6148965611220d97dced6e0604a87c89d8244fb44bbfbca002415f7bae9215b0cbcb11218ac14ec0aac270012090c6b24f1bc32dcb4a6dbd6231e3f8c9c03f6a14e6e15b17d4672808024c24602504804582433a42101a5fe302e683d0e6bc69da35aedb3ec849685bee54c0052f5af737fc02c8e16eb70cbc85b410293bae808ef1e48b69e02ee26ea7f1e3a00907329156662c21f50ef38ab1963e404119cd383e07fa1a89aa2cd644ca03ed935798e22d69716859f45f3e390145e137eb9739933ee4735a679dcff82a88aa15b1584230b9bc0ab67e6738408ce522418dcd9ea62f11b3359a1f3c92b414d0b1abb3c49e068555c94a85eb6f6c55b610499a91980b474c54c6024a1fff93ef36f56068459f153139f20aac1da9695962f97eff551c4525e5ef8c24fede9f5378b9410c6b9a431b874fd4bd6dea50bce2c12bfbf2ca5a702e763188617ba8a75ff18f3a4d1403032143415d7dd9bd175404dcbc50e46fbad50459e50235077857c47e4fbed7d4b4b8670ec5fcc46d72e3196a9de442ac6b87a3eec51a68392cd1deb9bb6868410797f3e0154b79343e04ac9effa459270355e517b9271610c48c23563b945c9db2539e8e74a2c07fa968e1a9626c1a5ee598f574fd8834ae84124dbda7a949caba590bf6eb51789967f8c860fb66d5d82423dd23bde1825433cd91794a55516f3f4847af24842f58307bd74f8b4d80f0aac6c8e87399305c8356d2b6bfc73e84cfdef379891c354355086279fd2309b25d2267bcd68c28479551fb4eb4e739dc62d4636f3ffbd5365916044d97367170e5d477d4606608f7991e8e692c37d55e243e6ed963f95195f0fa4c9dff146935871cb122c8cbe6c895810ea2d100f872a9d23f85524684e39d255229ae13e1991390c80faa968cd5b00fd03540839407a86339e58a8c222ddca17440ddd76420c92ffbf8f21d192aa8cd1e27fdcd5c5d3f560a30b276f8c319761e3ee303ca509f2fd9a4ed250445efe4779e1a5ec2ba090604f6403d68d09bb11f3d6c663046ad5b69e1aceb720b13763dcca2b0da01be045ebab71f06bb574b4022531b65c4366c90b5665fbdd9348ea4a3ed846a9b4c83bf1ea7db09f76abb5bb8d1196a1655bb0653bcb87aa30670b56b662db626a298d223058fd6e615e44b99535e15fce84282efa1586c018471f9579e6da02f032da669502b76cb6aa149cee7ea402ea284dee6c14fcb670f4cc52b3e8746fd9fe02bfce3f7a7682b142c298e10f724d8d10605f40bed1e282075e073907f7ca69c07eb69dd4a3463de07a31341f4e4c589d26cb80819bbf1b48603831c4a9726b7ababccd3c7e00d2d7ef49ab56ec9cd9624ccd1111acf3e199b3060b676853560ba4b54e8ed6ec6289b0750ba8cc67340662a0945fbdcfd4b94fbb1b5447d9423b420dc21c4ab8d820cb3ecb2f187984ce12b9dda983d2f5465a288dc43dada225736a0a31e3baa3af80d52ed5c4fd52b6b5bf891e647515e5fface58d8090f828842c32ac986dcf49978e74b822eee28c5eecfa31a4741821346cbf39ad84810684f3a452fcf2c6fa21d37c9ed7acc071590c9561f8633237f91a740ee0852c82fbc0e47b879c6009f06a66948d48ce9cd366a256783cdf5d05129c8ccbc071b731db5687dc34a50b28980e0dd7fd44f23f3485c4f04a7c03c1f01a9585654db1603257dd4a6dcc7a9302492d54a76fc6feb1aec8a1fce22eb1bab1f16d2bab10a8d52497555ca028c6ca294057df1387ea38039f4682980325cc9e7caa43182c0a0ec79352177c72089c53ecf089deb1f74be9209ec75454dc5d799cf52e2f52b3cb270bc889ccd4c2b253035a4e616291ad6394a385718adb8855488d3a61aa0d8f6280e321986994360e7664e6f99ee6a35ec2b28216080fca7155149a9c761abdeeeb0a9cb926d502a58b9b6cffa94cffb125a523536f3b35c9c023bd29ee0bde168d3aa061a2b1038febb9f7880bf11c9f7e40418e70b6ce0a5cba7d8649bbcec9d2a033496bd815be88fd3e14b98a2e9e617218ea40a4ca39bc5f176beedd4a2a99c64b5feff82a1809e2c2024c38624f0c680b40ce2cf5ff4015ec7aa764a86439c91843f6a2a9ba8e2c7ca2e02a78547d547eeb1267b34d656f62b3c779a6a261e8bfb1761349313b6457f969069b2d6e3f1b39e439e20a58cc7c46d557b4acc81ace989c1ca68fb78787c4d4aa6fbfd2f657cd2a49cc291466756ef18353a7da4b6c5c34b1dc34bee7105fdcfbe1173335b58ff2fe10fe28c26def338774407a08b2295e545a8005347742804d724a6945035279472071734cba38bb29ba1390e658ce94e522be0dc1a755faaff42dc03049dfff8e52bd190a2c9c859a04fc4e5beb6b855597bac226da87e322edcd08001b585192bc1c4fc5777ba3e9dc491527cb9bcf5d899b6fd70ea9ca1418e640f654431cfada30813c45a643920ceeea6d7a94531f78864b3056e6e2461259cae612d6cf461904afd030b2834112053e559fd3d1d24f4a6a4e074249d6c689026c0edcd0cdc77467639d9eb2f31b77aae1773252448f58c4b46aefb474744f69e7b13d85d90b902b6093da0429d73c7bdd573bcf3fa13b6817ce3d3b41ea6ce046939bd71dc67897367cd4b8d92255645081c0e887bb10b9aa3cc69e1ba66de69a31bf4d2497f4aa50148103d6d270fa8b611d32c310c4582f4b6939fb8065c56f0d2f50cf82315f7cd2225645665311a3f04becdd1b0f9392d55334974f4269a8ab86b524ba179a142127d8d5b4611456d1b4b4b7c30da566ba1d0618f0b946118898d3a12280fa1326a526c57bc175104b538c94ee124307d02d68aa271d1cf8e22f793a5f6a130e946db2cfc8a9b6b8b76e5f030bbce055dd306ff9fc99740be41a9310b0bd2c003c1daaf0686bc793e759f5546ec6c1b69d622b697a52ae9db464de22b1f35784900bbe276859292fb1e8582c10b587c3c38b31162af12c25ab54bec07b806524c3c0dfe264f79ec5505bb16d58a86c7f34c9407bf8c2cd476445fdede1199d98274baa51a4fed59c8897a9d1186f7a4ca10d16300388c901d6d9253967c4451f102060fe3feee86dfe42da4d9fcb9e0e7e0d1314291551006cbb4001d773519ea30a1ef95fc25fb968b4a96c9b140cf2e634ccd1033acf0fae9b40d2d42b33dd9d12158a41b03d6813d7efd1e668b487f6dcd2be95c59cb10c4c2a296d2e784675e0d91e4e827ba75d8aacd3d47934ac479ccfd48318f93dc7f7cd97021c5dacbfcb5e2c606928c258009066a13fb0ad7cd07d8cbba5087d3f23713223cf1187b4e8ab0db566a8bfa401050eb6ec94bad53b5634481944eb0cba11ae06e5dc2e40d89cb679a57abb9c601b50aa71dc9232a4e19f75d63e23f74fa3e8be785e39a51f068d860e78d1c09155ccfee8c19657a55a9020b66541c3f2d27bb604323788c00390cbfef2a22e455d66b80479e2992d856f510b6460cebfd8abf0dfc25a6c1974206678f571a322aca7b2436ece480949866d88ca413614a545e20ed7cdedb54ea8dd4dbb641f1b51cb8e730c6dc791bd51098ef89169f01bff783f21660f492f6b002f7661e550a56998bb9c5e60a80999862356c417f636808597456c129fa586a3d06197f42e6951b60ea07095e8a969308ff7f8800761d489ec4c1dea26e30deb924b27c0c812eb5fb47029963d90825f48215534c3768484de828572ed81f5e921264dddf79ba5db4d358e384d8e53353d58ba67b1fb2b924a81fd954d914276076694597b1476ccd7da54da65b334118521ee1c1412b9fced0bef37f3f9ed7d54717623d569bec80d0e440176ebef00212756a0587a6fba58287c6a453ea81f97536d0e429c3283dd620e013192e6f711ae5324d1d30bcaa5f4e1f97e62705b709dd87b78e2eb45eed79367fde8dca71770e48e276af473bdca499e53587eb33d8dd17d8b98e4f7c94f42f2dd4c4f983afb96ca53f43e7127532a1860a38d2a7a92cf3de69479f30b36048c8d37150dc7b705589665bf2440aa9dc00e867161f0cb9f1ed5b4dbe0491481b07926b089264ca3ed267f70fa10511b046ddfb23bfa5b0ed7f8bc00914b92e62183e6489201f07d42a7dee1116eecf81e8d98ffda8104589efd801534d8105f9f2e5a9139817700bbdc4ea1b455097bf486207e4e7842c0cb0b601efc012444837047c70adbe253976f2f45fc309e358f1dd71cee95d8a62cebd4f5371884277505579a1019a46141a20e6e62e837f40ce500ada119da0c62f78d3121c8f0b7bb3a3c864ff93533db7b1cda7d0d3388ecec53fb8af055c04201c6af3bb1bac4fb95aee72da3a5e4e219dc7e370c6ec65c5050c1b9b4739b55289f16103f06b57dee09abfc8e9a2a3730b1e5964b7aef76ddf7d51e2fa7a75a300bb369b857823289d0b35e1ac89b2aafd3396f15061a1d3c4d1358ba71f7804b3d65bf5a49a08ac535ce80d8800d3402dbab614a129b89c546671a98d812e1852fca22e1ea8761b9270764ce01ca3e9bb0e9c716a2d0e696dc42133732fb83072b5c40d560051f0e3b76e233a47e73544207ad0bc72a416db19e553f667e4e89dc5076a68d06a83970ed8541521685b244908877d2d3c733be1726e06029c4492001f921d5fe2370ec2e4b388ff1fa8935a3bbff1bc9d457f1a04ff206064af872a22765d67f49da880a3ee939c08ae26e03cd95bf47573bfb14dce1782f3289812d9e953609122174a24ee6e900f6705480698d5a8c5bec5a93d1b2b5f4900b618a15fde667a54c1324c942e1747dbed25fd2633058098b5c852c7455362ed32aee8b67764a49e69e68d74672b5444c5a9c1c475f5c36e74a16bcae0dbd03b4ff43c3fd3faa164f80266a4899ab0fa9c7d1eb2f97daa25e70e80937c3a644b14dc3c3735b0b8d15c514eb322daac159e3f042c16fa0c47f0ac1c34ddd742f9f6dec6cd51b6011ab4e9c284da635e4a3f8d7aa8afd2d759fa8623e25b812f5982466e1eaa071582e310bb76837f7b9dd92a2a8c6293e35423bf30c3975a4ec0c23a7f1108fec47052f43abf8c5d90e3688e0161a3420df27ca64aa3800501a4d3e42dccb5524dc4f31a72a5292a9ab61bc0854d48d924e61b5e610458c66dbdea49304a4ac474937cc4d01dcf5774a49d0bbe4c16bd6d0f915c8cd076671c518b844cf8a03de7365d5cf3c847ffda5bb259365ca0633d0d96325a6d06f408764078e7cd717c30431f189b0158705f528008cf998d81b3dd3afa080528ba7fdb55080087628c6337553531ed15ff24fef1797a99fa3c9ebf3e6092624819765c0dc8b252ac46237eb318f3765968ab9b503075925bffd810dfe727337ba0a3d42bc3819ef70be76338e07ca93bb9a1c14333036fe699ca26e23b5a3e3371e0a839f4c969dd9f8472ce3d84c37baf6010c27d03cfbd41bb1aa3130487554dbe98ec88a1c6f34166bf5ec9cfa5822a42f026c51cbffb51b976be441f95360929a54e55151727dd58a6921b1add6a1cc9e4e6a998998a798869495a08019346e628449257e9851cf6cf043314c32144f801c13fc1ec83e775c652ea2012298e762965791a011d8d8381f52d4047c28c5ca718b3145cbb20d2bfd22616064c01ce3ee0b07b4a9d9f7b9381cc9f521d4e46538123fb9d383a9983d21800990458760d083902641c1c1346d700c07e9f1a2ed1aa9829f71b31113e6bf9fd9a96a61375de8123d3bf070cf1fc615b0ab4c166552722f0c6d3686528834c148bd667f66f9a9d2c351492a0f4c65e005d1f18ccd41446eb40efcab4eea623990cd14738f151977246df3cde180f9823a879193613637da85f9b068089f633ba6c254328db5848a12e79c03c292b49820d999101f35ae2f0a33f88536169a5b696b9b632168350a95937c2dbf361c66b9545bed407ea86b167ab3a3aac284401af5ec16ce9231661f051d2137f10af40e3b8cb3ca0f590bfe2d78f02eb124b10effb9c71e0640a062f547d54015d1f64ce1d8f812abad555f6a7fe04ab7be6353ab85da1782de0c568a41b47abfb71feb2735527580b15b09aa46148cd5aac9fa925fac6249566ed58bcda1edc9224474e6ad59a1bbd685b67252e7b3804eb8f27f1c83b908824f907c865705e2c4065fa235c411925d0b0905efc8ef975ec2e7778ae90388d0e8d984cfcf0aa80910301feedbbc17b69c7f84a81c4808a68fe3da429b8fd488f35df24c04546d52aefac7b6114c7736907207e9b4015a99df0630b18d9db064febe0263e911140fdb9ac9cc60622e4abe5d374d0d9d664ec183e3235055d863d7a0ae742807991bd1670f714653bb984dd482d5a16c4b22d5020c64b52401bef191fdff216bf81dfc9f2ede97d54e0531cc84531b745ae905643c3da2c88129b025354a73c056818d4d89765c923bfde193b1f93c506581a1242e8315642c6a7b6297374e16e3425490dfb4c6df2078c4c1c97d38f008af31f69f6811761881ee5f1885f5d77182be07e15e0325998c1322bc4dfad88d293d4a9b2227a3413b046b8191d40e289a87fb92328464db97844fbb3f3b439c91eef980509154a81faf7bfc143e4b0815501e6722b04c31dd3c929ade0615268c47811896af0bc57764b7d75c80ed92af50bcc3d92933e49605586a02012f0bdec0344c1d60dc85921e0db45617a4b2b7b706f4eed273092e7b872fdc92940a63d1f53d0d04b775a7142f7389c5ae91d617dd1dcf4fb81afef5027845ff00eb288ecf47bc74afb0d7728adc68160fd892f517bb7c79ebaf602e7e2a19953ec65d4aa6d678276e89eff17bdf35fd7a9d9f31c95716137469a2271fbc955cbc7d2c95170e761d26d288a0440d55002de3e2a8a337aa73829126524d4c2b9d20e20a404b64bf5ab7dc00d957c90f56fe6008d51202eb699a7be50782887ba1afc2bd715adcf8ee3c2ebe0d412bc8916bd92030d16f3f7a8f316f2c11de518f91c05bfeb1f0d7a785d221113f3d4480fd21e27cc90d610a482a88bad141ed925d632390ff94824e588ed33721bab1fa82333dd3afa0ed028fe26ae27bf2c26568eb0d59233dc8270318d40e458d60c148e781774d087b3828b68c613b208c3a5bf86201b11188cb4a9fb9a050c1418f36c3bc721e71bd6963b4e1440704a821cd2c07c9333f11675bde8c7613a93b079737575bf94e1fdca6342a1fbff2539675f666a9487b74eb784738858da46cd9eaef03b1943827d72d36e91ca0162ab68aa3c6d3443e053d3466aa3778fe424056cd3b3a7a8b153a244ebd4408148cd045ebf3a4ea098915f15aec228b6e20324e464eb498e9602a5ebf373de7e4323e25c545c0c96821f9bde8a2a42483a3f4bee955ad0731168a7e1e1cbb8da88b11a5663678c259784a4bccc0b0099cac12b5686d76379794d1882f7d17b6ec0a955b2e8b38fd93abf7a22697befb8505e9960500d01199b45302b164727bb611234851378d3e7e651160131db8c45fd9c77001f128f6158adb22f539ea50f610762bc1301fa0d8e33e91f48fefce621f3628637d8a7d424e5633212883cce3ef06936bb2777690ca2059ee46c7a9807cbd8619fa2f9492507c51f2b5ae37d1c65533917861c3e50d1bfeb143d956b5e871261a9b6b73933e2ce9ff56e96ff6094ebe65e2026c1fbd815b405b0d871867934a2a8d5eb59d2b763c302e48951ec696352928a2f45b74cd64b17ff5a0a4fb8056fe52da40e6253e9d17d21df2a862642ce9dd1476ee481c3816b48780f10b0ca6b0d35386a5147e92422f408c499b3351df92216e70e0c25f9bc181272a0cbcdf1784e29fbc6b74c8e8b12b4e0414062ff420b7aac8d6682ed2c30011b8184665e3edfdd1c09e469f0dd7c99f719864cd390b70fe5173d646b31388d9807cd56c4e1734f992bdce2537eafad224fa6608e40baeded287d40177e65f1fc206b47bdde239439738cbfbf2a272e8bb1e94f46682bf64a5a29c49a86342dc735a1d5febd67b70815711c62c1604048cd85afde70797b218459f1800fe36b84545863df2a88155a33c14c4af006c7ea4adccb04cc75a301a5499530f059e19124ee9a70fa3908a3d097ff094a0c913be7d363c14325d21fb2657932f9cffb6d3fc6b1017685404c7977610b1f2be33788eff2a5c2870d604350fc23d67d5a094a7095e62de7c4e39158be7c3531b0d3aef47a767905bdb22bfa75a4a1c8f7c6fddc41a4c7761371908848afbe02af8b6e94a434f8f8d7bf82a85d6a73636dcfcc3f6fcbdd3d68243aa6d138969770d53d4ebdff70b82ea9e8d54003d8cc9f4a2eb0e5b2533d5255153ef5c542c59154e3839b4932902e86c23407f020236f0f3cdcf0460b77943ce0ecde5f7880a1ee077682a6a6f5935b68df223cf36e106f621624a7faafaa33dd2a4a02b1e64e4efc0d8721b26b2143da9fa69d5c9426eb7f13902282a9c9c8a8106177f4103fcaaac7db2b33e97934ecf71eb7362824048724e622f09639adcc922182d32c5a6a1352a23a8b74d74958cd598c99603e17e19160a41be92329841d67574ceb67695bb5c3cb5d5d084c7a952de0c08b5d95425330b2b954ec0380bafcc6a63f1499f6d96753c61c0c889b6bd5a496340900404364352865b48b99956d4a2da9d924804c9fd6a4cea182a51905bca7d0d0d9f88cd9b899677d42e5bf11c8126d19b7ff8b32a1e3192b439e643e0136758ce463255f5073b8ad8ca30d26583c408fc79851e4ee30751a91df327be8540126b833ad6438eea507360f8db16b2bddf844c537e70ff951229531557ae4c4945f637dc6797c418201a1a75cb66b713a01b1b9fecc193309fdcdf49346dd3021433e0e94eb5e0617b301949979a5d6c6f3aa8a3906410de5ca1bb1c6f527078fb24bbac40c306119dfdb194eb245432456db035e84b7cafa61c2b6a730bc8063d0ff34e85e0ff28a68589c4d95cd319581306b6cafccc8e7f317da836db24140d8611d8210299716bd80db1f6529635467941f57cffde2a19c90216c4ea1415056640575e63d9039782538cf4e7804e05183915c71b39b2523edaf1c9b1952b0da0c12dc272c00a2e1d5c3ce36ad10f96c5885731de483220a448c34a279ae35baa6dfd021db2f99ec011c003cff309918b83db88b98501d3e63de5a46d9df044b0d0cd40a8853c2333ca2f6f6fb4576f4f1c89a8ec5fa31b4735b3e9f503c0abefbd75eee0150ad2ffbdaaef4cf6de9486f01451f071f1569693c02a266d847e16ac962716a8391cdd62538627a2d1f148cbce2c6a3a69b74545357e68fc6956faf790a24f346296229df9314030410618c76ee4e321703c9cbc68cef6db3b00a485d11d1573dda91a1fbb81a0caa011e7e06432acda2c0ad414220f88a9977e560ff657b0e504e7f795a46f3b2e5a173b9c7d04746b9610c1c11499dc20b65cc23b7f2e707d2b10a74484ce72060844528507bbcafdf491608fd548faf6c369ee73baf234e51be2b1ce707446d7b6e0f7594c431187df7c375074f82e32cae2b66d6209bbd81d949c3324af5a0cf550aa395eea523ef198edc598af10d45e6a1018e739b21842dc7c4c019fd09a4e14e422b815c2a6812fe250db26ad0ec565490fdf4b7bd74e79f344970c7b5358af19475eecd612d5f5423775fffa210dc23650e4b4a2056426da90a3570149fc988983c09a259dc8d9e6ac7dae62878beff94a33a09f25509b38175223bb673b50310359fe7c6b1a5461a0231da9b51a34a72c42bca2251cc045bc96176f9e43bcf2563c0b55244496685acd0e106f67027915054d7031091ae9b8f5c85a04d02d421fe42b70a5a0d8adf29a59d11deca19298f40029cb8f14f53b401a5436219e3b94c60fbc07133cf39e6473ea0dc04f41c171262f9508332e8739af308987debed14ce6994bcb1a327875220fef07c71a6a970f608b0e0e064d0f0fe51c93856fb6bd81037ab853eda998d8309206108a8a80889f6eeb1d25e412419e20bd8dd25e19bdbe0ab220d461823e21ea518f7bdb797e88874b62b7ca737dac6c0b5499ac3965df32c385f3dce287ba958c3c2bd1fc8415a2db2d613eaf881a622ab2af77bb98967c9625ccbdfc3850ff573d94bee216017a762a4b3638d2376389705ee3a4e863e45db804d13ef8c61d41a089e7aae2d6f77dc856a1b9558c66bbca744645ad2e0204ae3f72d7c86b18405cafc02026b84e41a24fbb100c3392ca4ee302ee542061b95d80bb09ea0bbb9bbd6590cbb1bf14afc250c2d5c44004a986d7a1f257364bc3e5826a16794e81c461c256879a97505bbf718e5ffd2c0fb709d9907548459ce1000ccc991d314b9a7062952b166343539630bb02c7ecb396aa82225a5231ed9e2d2feb6c290638ac58972e0a77cac97ddb4c970343d5471542cacf91b3999097bfa958d5a70ccac23dff4f48192c65ab6995d95239ca256150059f46c7c62e4b590f0b5bd5cd4929689fcc01d769326c587ad76ad48754c749c5b0ac1757f877206fefaf961fb64e78578b1711a6618338c61a593ae2989480d280dc1f78e39b03c1e1ac12dbe8d6e733fc0bd06feda803e9de3e2e28518b7bbb00801058f76877172b73bc7320024a2d09dd92a8637482993e9a9cefb1f1e4ac168dbbbd5b7e1c7739259deaf5bc215dc171d985fefae17cfed8192f0de2a6e857ab43383b76888efa5fa8b1b7f73afe793a1ef4c8fc2069f1851f91785a95dd47acc5e2297aba46ce20fd8558500d2f515ae66e9a3091edea43913709b7bcf7a961e267ec18ccfba6488b1eb4c4e959578706f9a58fd8ee09999829c2444dc8644e0526d91b15cc1535ddaa6ded9a80021619070197fa9f6c08b3fbcd375df1b185ea358b6da935104646c739e0ff835427f85b9f5253c8cb7103b98a5b08cf897cd6062445e844ac02492a3aa371e3d3cba45d5cdc9843ca59d11a65897ca3fbc1cf97ae88df8f5215b58cdfa45ae1992aca6985d3ea712e92c89ce110ba19637f3a364a2863ea39fda748380d9b6afc7f4a0b549fe331d152228e436b671af40aff1eab7f055fd6ceeaa8904842ce1830e0a5e5cd999c094530e8eae52796e9717839fce6b58f1f45f1070baca94bc73e538b80634f028baed2a5ca56a7b11d80f31e1ca3b6860492a20b204fdeafe8b7118cf7558156b93b5fb0e093dad16dec812a9125c1ccf254feb76afbbbaf167c3268104ccd30f424c40914488a7ae9f4181f877c048805a9bb5f96474a24122122b61c1066e89d8afa1995a30760306d764ba8d74bd1041c163db24562d6ae963e93365d64da2e6bf1ef7bd0217a08a56577a0b122b887916e82e6c869280b095461218ea3ce9a04dfe6103ee5ccf3a1b2c8405808fd80c20b2351636862e43c25ae32d5d9c83b551ed902034138705e653c3d2cd6444b3a55a0368ad71d3dbe349161ec9f3653233957cccfbcd17f714a4a4cb11177b23b789fe4281820ce4b4aef651bed69e93116c744a8c242837ffb09f6a271997f3f8f54cbfa1310cb85f343d58b407359228351eb47ae8e4f6f8e0ec58040cbc7299c3cb7aa5086e4dc4108ce3139ae89c2702ccd5ba294c19113a0e076d8555a1abfee8b9c0b41bceedbabb5dd0be0131f34b7ecd2c692bf82a8f24a2c492cd480f0d186f63c5aa83cff68bf84151a091e3a8f07df20b0cb65f06e28ce6c8c118c6254006a986e71d17265c3268b710fd6e485a885eb840c02e451e3985e3ec148e9bc67e831e1042d81a9b48e6b84340e5127c7e2079953565d80bb2f40078b11f41ac47c538d3d77ede56ab34c5bb2e8e52617b5c813130320a5881470307ba80e085468ea2717d10ff20fcf69973a8c518385d3ecc51f7d2f603b60a1a3b261608c125ad1f61f723e0d17191e5ae38e3a31200288c72f3fe327bf22837a2a5f5991398f2cad318fb66a871dd793d6efde59471f284d45775707a2addd421f2253f87c494b891e714ec6c891c547ea8339621c082525993555abbfc534860c63496e1ac2aa7fec9c304462988a983d771b673cecfa0745ee5e21278a7ea1223a6c0d9b54123c8ce2635bb8417b04e022ded0ebfb391e3add7f8104e6777af59c2a544465d8f673f661b19d3c9975a8301b23b8b02f34ab8d2c12d4dab22c49385056c5ba9c456967878fe6a3fd2d42abcb061395425d6aecfd83beae2cca850da5f222872b62529c727d13ce20d108bce9a022303ecb2543721915511a0175ecd05f24e3718ae05e498d4d3b5685da68d23030d75a60f8db481cf5c909810c1492f53a85261a041d95e3e98cea7d50546610aa7d10b0c039a20ec9305f50b8d7c4aa46a008da8a241633995a29f115335fa8b224d2465352e7f34ebeea5c84ccd5ec608ba2973b719c00d0cc6a29fb95b757a05decf1afa4a35d46c0ade1a25fc3df2522254493e27d57b479b75babe1f6447ee87549a5e6f501e017295a692a780de8f16cf76b3c58521bd666288a58e7c5029c4f9db37474df5ff87a82243149c26e8ec330b581106a6a18814b15bee4d17a6a1e9b515a47bac084f88ccf27ccd9170b7f2e9f6380446f25e17642ede1f39a15664ccf332b0f15d84af6633b70a46586ea51de408cbc0242aa32cd4865624c3afe6c1f79f42ce4b40242d3fb12e065f5b03ecec6966aed9f61b7571b3671c69290e7e7b84fe2e98669713ca1392f5ed53417bdbd34d2ca437ac2b4b189e2841687ac313e77bc3ec5a7df639f313a80a296a9bfb9164b97ea13f74b00fe8dedebbf24c0698066c1aeecbfc975f8a493003879e5a7354a822b66711e58ab9b12e3e7f5617aa61b237e27e1d07444e0af303381edda70775dde22020072e90546ee56bfac224320acff1e94a647a327dd40913788db0725b917fbd18d923ca59a2ff6ed74d50de2d6d80c5fac4b364caca6bd351a7f09c784cffa9f8f429291d8b017920491d7314f1636e641b5372800b3ce3968a984c8c883698c038fce8a15ecbb22bba587b5760b1267e042436beb068642ac6dda7564bbdfaea96fdac725c5cb14be356156adc1c94c5e3098f5a75cd8d489d87986a78bb35163d6760ef868c0b1730ab1d85e54ab3c7590ecf315fbd683f827f08b4cb79ea69cc8973598846c7d40f76b24c04ef7727630de9116183dd0724ff3f763e8f11b330398f49283f781320782d132ad4ce10b6c577b3d48e970355eb4ccb3ac7749108df3f7ab48903890d322be6a6f03a14a34d3715abe7a62e4299316efba5be1a0dc809fa88d63ff05816dc58c31f70152e70b4275095cde2d52a443fd28493736da6e7f20f943fa69c827aa4d749780838425394c018d1fac4f5f69a91db067988fdc2a02a57c84a42e414ec1597683431b43efadb77369e31992ec6bc736377bcbc2460e53f2b5a4a01f0097cb1ee916a84b771a22128347688bf7f37fb50f87ed34b385d2d7e2784b837ac47d996393eb9908f82e400458fd45b7d35ca8e3d8b65bb6eb22910fe2b45aec34534c892e749326f460663e1888d30b4688260f6331e4628328116f182b033688f3b70b6515707afb691e3bcaf4f092fae638c8c93b7247beb9c045cbc577756218b1483c5cd014d2ce30ec854d0d33d03080306edff8b1283cc6d7d9081db5a2ba9584a60b8aad7f0e713276def27b65dc05ca9328e23a45be21537f12afccf1a5b942616b4428b8339f8e7c887dbb389574534881fee7a67981334af69c205484bc01677ad64f71e4fb69d3ce3013482c95c10977e4350b039e5e1f1f079976a2565ef0c4a4e03da3113c522b2910484d056f9b1f85098dd6b90ec7988c203e49e0780b1a7a8d7d656a9e9b5df36399a63cf616440e507fd03d098d278353e6b53fc1afe2513b1b21215944973d8471a7b1aa04dff848f2fc96cad1c79574f2a5241d928bd9a9633b9e432b2beed48b65cd05d1aab120e4ec7ea62268a750a7535816812f805381debac066d83f1c861d4d8547667a6608c6ee4fe5d181e30678b0da1343d3662a2493089a256a0fbe431f9f0d32211bf8401fe6e7a3f60e863c58a894ac90325e11ab926481505132a54ff73c069b179be8b6236bc6c3ea978416bc06348e53ac22d56d48d85b4d0140e93b7b024423d95e5a9bcf72004a5a5eea02fb4080d5e14c6ae7487006a7eb2736fd5f7ea6eaa0e94adf99ff6fb9da51454ae83c07fbae72567771108763aa4c37a5940e6b17fceab02bc4ff60b766a4a8587fed24f9b893a9c15fc7cd011bce38c439a2360037af81c79d0ed68184ca2faedd49f34a13c1a45c3deb20c1d5b2ae4aab48d38d4a03349ad13e06f7ad2186180974304105402128711dea068e1dbc0fd544c694082b31c8c0e04988af02fed1dde6645b064efe91d446466c3c8db1149512e88f867c7676c2c66c1caa7d93faf38874f0b4aed845bdd5704ff4c823fcee31cc92dba7c0c28e6b909991722812c6f0554aea33673618178fe8201ec729d44e85c1b951b4aaa769a126b2f61ece8a3b6664adc7c28517858e7fcc9396d30e64439aa482c164ccc88b6d5c2d57eec578dc659b5e8e31faf092cda3e6710bc0387a9b53ec9374a243d19d9316891545e46de6fb86adc101733b978968ce3e64cb4a05db89041ae12d51425193b77bebee2becf55bf20fcc902e0374d0478a3c9679e0e590d9f9edc7f03543e0484310990e1c068d47ffb8b24c61f756b1b104c90d451ca258779a6ce3ad1e3c2337e56421ba707c517da876c9eb32108574e7188a97aa395c9b497d97b397b2f1e5461a13c0f8af7edc174c1d897224de98914b20a3916457894b3a5a5ffdf99cc3250969722c36c08f2f41728b99053cf6972f5bfeabb50af145b555ce58028b7ae3f2f1ac21a14300a4ceee6b9042599b490cabae92bb8062401ae5a7a957ce22e0b92a74f7952f7d24caea4cc14ab0fe689e133812b2f281b1c561195973318eb37e8b8f263b15e534a298253f6c8873b01a42c7b9c82139cc3cf90b4acd82f8bed666f7ceea2f6db3b20368dacd983126350671c77867f75c82c30aa5f7efb9e58b748b1dd0998cafa92112da099d0155acd75c7aefd7acd7528cb86c0d88d596c99601731c1759f9531f6f487b6abf59653bb334e6721323d745c8d1b32e0dacdc3a8623b4a640e6a57566efa57879f49542bb9c4fac678f70bbb0ea044c023b286d35cf53f6e5ae78caa19051ae01cc8ee8d13860d0795782ea893841352fc89cdfc12b1bae0921d443c16c25c93b723148760ae4919e07c7da105a10604a78eacaa190bac33ca45a53f0f4bb3e0db18bb450010b2b8a55e384c5e150c828cda05022c54f0416db9a43213f528f3143ba979cf0d05d8d197d6c2bcba1905a9a1e35ea14f4b4098ac08487f2b009bcb7a0b7c49170ca7f96af4cdf55caea57870f85387a360403771e118310f9a8d94b58f446668db9eab69f3dfb488886426a0960924c8129a0174332314f482df6f56028a42e0458ab6c2d9547c497b9d64254195b2e358e0ac05945f36e6464c6d7492d46810143217d9de953d98a6c1e9e8ee622e24cec0914494d337346e9230269d4528b9421fad0b09bad502a7f26ca1eece3904795750cd7f3ff6e789e05faaf34ecc0e0bd534302159845af059abb906988c1d4fc7388905928825718262db775368dac171c58de7b6dd5c23e149e89a6bb0cc8f000f1d7fcd8d1da2b2abfe0ce6b804b773e52bfa0964107db6e9a9dbc342509c35dd48fd56d8fbf6c31f4f2ceff7865e90e9748c734802ed1b3175b064e77de92bfca694720d02817dc8725494df71dc9adc603f613f2c870ed7fa1f964e4327a39ec53f0db7f9d77871a4e6863d0adbe9b1cbdc778fc72500363edb5394bc63cf2d4710dcf86d39d07576d3ed2dd2a1b9181cd9eb649334b29e0d7f2d0f34f4af83d6db3fa0a8bf1d90394a3c1e512f06cc46ce84284f05d795824d06aa11fcda5a82db4ff2c6cda808e62fc52ca61c12ae596e1c5d4e490fb9373207b0ec6d12606ee9df5f766dfc4f1e427cae6bf87137c3544d3c0442aef9777c4b631fcda00ef16c2cdcaf4f9e8d6216258ef5f03445d2a3471df84072930d097a1810a00718713342fc2d8ba98188e2411ae0eec27492a581a602e3e75a711effda37a6472ffd49d2e55b4f3b33e4d3ac11cc69c9f7d7ea1aae1c02ed758d05acba9e8c603d5f5ce16c55db22cdb9d1df637b5080e394cf4a1addb47558a93a3e53fbff28a84b0969ebad34d69c9dbbf6fb1fb66e66626b366e07019bc795bef7e6450b8b2bffdf43fc6485061cbd16ac6a8085e4383d479320adc64c90d52133b52244fa377c719387885e8a11b107de80d4749f8879bf33cd20be3023ced67a9c47905965c4160a9d59d76da92e4290b004c1d7ba4e50e10f30c416680ac924fa5d35ec7c3482cab2822b9f9cfa40802144e83173bc9b39c88e0fe70638417874ac74218ed899a756703bc918f0c6cb4b4eed4f79909b8a0cfafe5168623a2e464287d6740a6ba119d8c189fa5b2cb555600aee9aab929011d5523fad06e0009784f90fef1dab8eed4a32b2c138030301388bb7a6f28aa25cf9fe8435daa062f497b689132398c4e8d009998d88495b7ab2536e691012fad8a622b6d4483355180bf20dbd3029d3a8c79ba0180281fea6e0123a07ca86bc0e8b49c555f46eec8d213cd513eb4bfc141e6bb5cc4c1f99303e5431574ead9f28e3732693323b5dd523bb189eb18a492605bf26d316e2ccc2007d158478925b7b3a31e0747c296a7e15a6e17787042e041f01b2815de9800171dcac686488f0d7953c56689c88232c15783edf4f0722f51d24df9c86e67a187f2a16e157c8a86bcf4ab36bd0be4741ede879cbce9c0601a5b66a4adee73401a0992658405e8fac686a3a440b85eff6148cff57fa508a9c5cf0a372c24f2561ff48aca315fd448af7765efac9ee089dac8c061c8ad4be927e4425474df56be80b4fa8f86d9fb2ef691b07dc39472e6379d2e1844b154750265f2b973c608f73c2dc8821d03c39a4e0707cc3a17332dd3e0a1678dae9857d891ace681501a5dc481e362c3b502341e9eeddd273c98b3cc26db599e031239cf805e9b691ae1988c8678f10158dc1fb1f1785f9fe1e4c9ffa70398ebbc8ade24cfb498cbc4231a67d22f1c48c26ceedc13bb3cbba0c059fbfd2a39011ad08c42438595afde459a9aa553677411d37df7eeb675bf6f6cb01ccda0f90c445db20e0eb46c61a99963428dc4cdc5d246b01a6f485ba3165e80f01489c44eb362da34bb054e891c1d98ee4a60dfb28f4f4debb4cf5e3032f69ca1c17de22feafeac06df49c8f66e7c68bb6fb682b478a308af9195c23af463277895942552f644037fa143586d3ed3caf4efe5b25ab1f893cf4f7e719250075a3e905e866fc39a60db46af3da8ac9a83c441291e84bb0bf9b4a9e57ee8f25d2ae57b3091e667debc6a5bfc16b27371be30d5223fafc5b9ba39f17c525ef014f36952cacdd0f1f2cb5081961f6fb49296bbc05ebcf3fcba2cd54a0629184b18f0a693a96bc80baacc1fcd3f7f11af368136e415ce8c114e117ee63037ae26f2fbb6f9a336d1155eb2313b6e9d3d165c9c028700d2f0b443eb2fb1a00454081ffb6e5893bc1a7b6cd60d2614f495b7ce5f1f89c153d3169d5eb01cf956b4ff47587e64e74dfeb7d12c36e915fda3c80c5d85aa6c645114888046a10a73eecfb93df8fe5597a7eb6630ad1e995bf34b257eaaccaf79b82070ba424afc6a80d62751fc87e79a7f8f18607338c099fc401ce2e8c0b8043c886e70b1707d6acf9a6fcb9e27cdcb32e6ec182b59901d03c502ef8c176f93a5f851ad826685c8ebd503b0c00eac56445722fd8a6cd615b719ee22ca883fd597302823f2b09a39f705aea87be3074ceaee4ab0f46b8987b7274c5086e219ee8764dcdf01603da48ee560e274163bbf4e1c184927a63d92bb2c602eace7d5bac8842e4ca6fbae24904996a099d0e6ad445b0f8efec9f151096d93084bbd421007b3143e1da808a082ea6c94a5034c84ef388a65204789770e1e881ba48665c0216c41af3a9e445470828271d10951bf96ae4a46f006bd85b03dc686979f773714b26479216b6380a7a25ec46758c110b1625caa0267fde8950f14891d7f8ff851fbf685e511b8bcab2af0ff7d0766c0eea038446eb040cdab07f3cae82762f51b704c328b5ad6129e6e04434298cfffe9b1f1802fb306006745a2a27c126f839cf8d1f8eba21652425b744f47abc4aeac2d094d77254f1cc79ec3397358251399a267a858616dacd053018477b54c624b853fb3cabad7a63b34fcd28f819c0792b8a9ca1fd78fa91136532c21798856bfcc976ad09cd02525f41c8fe2357502b619210aeef3656eb27d710d2e2e50a812d271e9d25aa1d535367f6846130a411428a1d1ac5c4604ea21740990d0acd8051317564204c538187c90bd3f0f5e55c19d6ef359d891c40721768e0fda29af0faa0b20348fe6a74812da73f4d56def620370990f0a99a4d7f32f472258e10dde116d65a9db89014706b6c3cd3bec6207dfcf6249c4878366f58610e39f7db2e87665a5e2c2df5d8ef06c1032b9d15bc77f3019b4334a46f506a5fa8b94d4fde7f592ba476d5ee98ab6c0838e87631001a216e447c9a75a83da8b85d0e555940a20f2ced0135c1417598c0f21efd998564c2d7743c8c790e321d69ab33a9ba427b82a2c62110f54feb35935c772064be802382a0cb0987ca21f215048a4ea0cc82366664df336f30acc4ba2c0b1ebb6b26a447d251905879d5bd4aad4446fa218ca758dc8cd40195f8815b8d019019f917d3263cf4552e894fc3f63123a197473ef58b47256e6d57a2dbfd5362cace1f7057aed7a77d9a6fcea6bb5e0f600f31a83ac140411ee920153edc7f30010e73d114fb65e9204910ebed95a8588b5627fbca88c20facaa13c05f886a8083865a1367f277b7aa469c3b0644db41d8751c5cb2d329f00df6680fc65cde1962bd8331d540caa663ffa3aa4aa94d26e468572c8032b58465c83440ef5ab3eaa38522b4497cd4777113e9940ccd201f4621d63fee188bac658d1368e77a44d8242a1602af5fc0fbfc3748fa8bcf35c6f8b4c11641bee3f35405cd216b8f70b55cb773c173b9ffd24402d6ca58386d4f93948e0a67797c81a1a94dd30528f264e71d82e1e876e8a8a46361c50d5959faac019503ca5b9936123ce0fa18f322fda5e0626e5cfa5550a185050ba6ef44eec3a6d2b2e0f1a6e039f7899e306994ca12961348959b7ecab591624f0cd7eb768d1933ccd0828bdb4c8b41defbeb4b096aa0f25b83fa4a052eac990dd8c5d538088b92dd00552f5ed2b2e72c90182eec20b3cd417598c0f617967424e6e5e0c836d68357d50a48fb63e325996d66dfa0d1b70e16203b1c2e251c3580607cfc28cc56343a21c8d55e3265841e7b10eaf086ae46ff6b36f30427e38498c25d7168f799c87f0034fed71ac90857ec0dd691516f64380511ea8f1aadb67d547650c707ba3a60355e2b86b9a58fae30a2595143815670f66600c0555d932f6b89ff5ac8d007f4a1ca415d645500fc7c65e6f3fc115fc5739462449b7ec72a7bdeadc5dd1ced91f44ffa034af4f3795f386536797d79575e7fbc8912e7af731d562ae21ed186ca55a92e52f538927225e9008aac2972ac102627bf6201a40c2721b28271f94aa8a983f14effde20f8edaaeceaa50c410a59f50a21014f410b0f9a67d371ecc0bce26adff0d34712a5a9494af115c4fdb2915b31c124118ea29703f98abe92a69147e41e150ce7519d44fd5094e9acc5e0040f19cd8c135b5075c63572086d6c483299d05aadd8aa09a830e1fab37341a02c7735446701762730176e34b2052ec997763565b2fa8e0ae4df344d7dc4af2014a2c5c2a003f4eb0a7f49368647843be1f64e7897a2b6741db79a2f7ef141903c49978c4742f7ae2e874ab7ad15a98f21bc6f37d87cd879ab52e9ffaaf5d0af8732bba559481f4dd102a61e5941d348d3b9ffbea406d440b6f60809be705832ab15d77016910886b6cbf869b405daf68df4509efd9bebd713541f4a00e7c0d8900baa1f669fbf333118a9ea709dd42420580e8c8e13576041eb61a12d58d428369d2ff73924a7f7a6c2a21cee3724a5f1dd4b6f63fdfb37048ec8f3e9cf78b438344052e9783956ac3239a6c46a93d19511c7d7ae7c86958229142b37016fb712c0e6437715654c4507cadd4dc00eaebb87f1216efdb186422e0b7ec10e44688c62cf97a9b8f326d4809a8b45173ca9ccc81ed2e0244defe75209c36ab0ce48fbe902488b57f73ecdfe2f09ea49e6def608247e3d82f88321bdcdf6f48c77740ac91460a376fbc653a15aa9d1c9a82ceed9031de4dd0620dbb309909594a4af804ae36feb1608905b1c0e2fa5c230376c660cb58edc4a0e052c21aae4b4aac76d380f525689d49a51cff5dffefe28ab45d7d71d24929138d2dbc27dca23324779319b29a4faafd334c19fc240cad297447b2e07662a268fca141143dc8ff9ad52704a2a45926bf5c216c5b26fac5ae898e2d038814c84ff5372d7b6cc9a61a26a01a5acd58de0628538f741bb34cc11a500c6bf25c18de8e0ad6df1d4e2288a7438eaa9ec971eb2bd716f23cd5d97d732f50a4137c9748102e55526bce55233cbc714a10d0de36e41fb5987debf164c814fb10dcc40fa0067eabeac6f23993ef6a14e6d28621ca706e2093ed53de8928b892622db3694f762124f6f246768a298c87171379c1f3a69a8b43c2e52425aa5eb97c721ff2ebac5dfc826243ff994b556bda97b216ffe5bd3d6a4c03775e434777fe3da7b6ae9fd13e67f40ff46788b0c404c7ae72c189c61dde35a836880943466f9fe5b369fbbeae7d04e143d10faa1537e08e4ddac747bd50d4836f13fd27fc5437d94db84e9548fa02c2ca0208a03c11b7138f710a4f7730dee2d7c615caa22bfe6a49053755abbc61ee8af5880c0d8270d5de40a218b17285f54e234b5ec9ca19836d15ea0fba200dc4f14a86d9c2771b0afb3bf0f175d935ac604e4ec7ae792740523c5fe941bd812610077e3a7e853d09184b5b40e1421788cedafd4b2d85456032488385945a26f574406c20ff59f9732e236826120831ce0e309de3ef42b2a4f6df7363dd467a4af83591c22a2891b435d96a38c1f40ad659f931d4a09101714e91ddfc7c8ea0c32699de3e87683e9617f3a3021e5dd9b21cb6064cc28d798e0cede66a257a07cb4bea84987e658ea917b0e7ce461f6877dc1b6fe972e73e96db4a6c445c91bdecb92280b3aec871106cb47189980a45c0fb31344dd988f656464643822ca0de350c4c3a614828625043af381fb0c32806b301216a3f3aa50617d004d5ec51527e55446ddd0b553bd78d69467d11a2aea64323a7ead19b01bf2350e2fc892630e0a5138003bd0ab5a1e0fd763c307b3bd7d1e9a3e8eaef7313bf41ab5696a3c7f1effe18d20d7c478b95f5aa5700fadc9b3db372afe90ec20b249f9e3c2d159cd4c7e7f6850290289e19e19b7d7285736ae48043604fcf20865236ba7909ed697918acc2d4bca5035dfdd2e0881b75e0d68bc2c1f1971ca832784b255ce8cf9a72a670019663c57c9a3d173a3b80e445fe75632ae1b549f8287b33c9819fdf63062117e0402482c54c305899accca720f894c1a5bd243af5741dc8b94cb85e6f8bc4894f80032d17e6f6354fc1c85d022afa633f1b704a126f91945c003ce2ec44e700e6d4fd0734b0f60db460f6cead3069a0be84ea4cc746d6e8cdf0852511a61134816824fb9f5496b4970ca7d55ab15d4bf671ce78d6712e7113e2b0c327536c7edd227156c29eb57983e0e9c060af807e444beb2ad72438b682f2b43b0f033b51850043c9fbdebf7ec6f91b25b43c7b5e58d6939589d356346fc76fe64e93b07c3a4b2ae139e0887bfc36aefd07ba430010f9a3196bf0764d047ca19032cbcd82d0045780bede7d6cbd0350a9ff5a04f4a0203d03fdac86712ca72b6bf5aaecfa39f32132bb0c176512eda521678c3721b1d044ead9613330fb21e7f7dfda6763ea981cf2c493db30d9248dc58e22e75b9676878c9ce5b9a2ba67e726acebf0c8b795c3c8953e25c50a2858b59cfc4a532624e75076c1be1cc919d691bcc7f9dffe4f238f505b0633c9b9ad608d92af53f4b7789d5f6e96aea1efff7a73edb38ffaf57158b45b724e842cfa0253325581b14742e54476aa3a47d780cccd5b290a507a5b1f10a97eefae8319e2216514915d328b6d2c49fd806cd642dda9af5c513de434062fcb3ca55ac2532cf9f70ccb2d6042f7db829f702d61588b491942565b69dd04b69840acd60fd25127048e50ae84df3be74a32fa03c464e5626b13f663e82c0512c73381b617d14fb9255a441b0bbde8043652df63f4daf97d6bdd6328ad84791b62b0e0d3414407f92c3c55a7755086e653b70357021d756a30a3395df4fae3e2cb1aafa1dd798e73a5d9f05d36ebccbe1d7d33b064a763dbc1bac64da68a261ece80383b86354ba87e88dec5a82e2f1dfe578df742e97a259941aa9b002aaa0855d3e2bf9c476a82a50963a806bdffa61a8ebcd4dfb56be5e04715a24250ea3cca5a90dc6e6ba3feb528ae666a254e4161586bf61669ba8e143cad9a0151cb9a131c84f444a91cab3d0b85719a51f0d218bf6533a2c87224b1e3cc48accd3a5f72cb2192113227f23579865035cff88f5b788ae53d8d4624b83410d7f6b80c4ec7064093cdd523ee6a5c591fe49445df2bfee2553417c41d10a347c887486bb35e047bee4b21488586ca0899c1f2cbfb8b14e95b79c350093988fd57f3050463434713458a6f68c954b51c434ceddbcaf5b84be21ba25688fc599a061a1bdd8fa511baec0d45ce0c8850d61a8c332d06df5289e3661603aa76813b77424d479160641bacfc419fb18619206f3a863764af1362ad0894c60d964bbc3824d838910ff0c27448117ea5d8875539420c00de01184c3aaeca8b12a9908ae21357d82bab6e5f755df7c5d12be13f4eb7f562e1de08053b2d5744c0ba3515099d3b5e3884c0f03d5bc56ccdcb8511a19e70da6c312023ac8456517f1c06411f29580a0cd502329ecd1a7308a316cff52fc920b1f50c2fcea82a21aa271a5dea8befbb6416ba3376ca2fa12bbe34136d9335b4e15cd26b78a61f2cf2292c3f6b8a2f0331705537f16723f4b4e2e83b1ea594a5032177926932d346a09dc6b32b5a91e919102e0d18f75b8a6b799a680c0e02d712099a2b4d624f4b935f364027678ab2de4dced236476961ac352fdec986e67018fc4de69c5ee363f35d25f7bf83b9ace77943c35bd5d6b0e32f552547452f1bbd4e29b69421dbc75159a57cc16a34902a1d53ead827a24a5c20c130c6b222b91e42b223b3541a310709da563183c3dcfc9c8f840cae29e4a155b18f7f48371d1bb3b5b768f6338f378f6c663b347d1137234f33b4a04502a43fe56f3664d6d86f8eeba156c0b9f6fd987baff62f305f783f4d05c5a626a72960973b11d420d7acc2ce491f0f7d50bc48fff50c386fd953ab1017a679e4ed0da5974e6c15addae85530bec2de92690c728515be9746edc82dbb92a7fe438e2f50c688525ca7520270b17d6951417e8a3a1765f51df93f4562eed022ad36618ea314926bd10832839b46f959c4de808f4316ef0c655cee518f5b2a665ce53e4f858a218c08a6e3e6e6be8e188f6cfff560480eaae6b143451ebb122a6dfa98bd438858a4a32a07f340bc42324fb5e9c87b650fd2e19f9fb8ecd92653a6ca0bf272d8b3cc4577314936dd3c5feece140e52655899428581d1235a3bcfb67cb93e447d5e6917b1faf00cbab18b44d385aeeca2192047fa463b8cd82c125d21e599c4c70764cedfc8b2400f8e3b9b64afa9dfbfbf8001977776c7b5ec116f6ae81a1bb2e05b03e1f75c79313d412e9e79a862f4cdea7ac2c855395eb334e5a00a02bce94fdd2a4f39211c924c051efe72a6151b6fb2ae52594dcea3c90d0c0f9a733bf1c4634ea9898c947c0ba6fc7481250c9dc1b71f90f89ca56a12eaea4bca1d046435d83133205504c036233468206467927b7967e8a57f33fdfa6e0dbfb6619e9702dfb20d6c41382b392963c53d1cd07ae3e3f5ec56414e105557248edc64ee785f658f5d3152fd7f8f589748c480bdc52b915ddf1eb2825980d213ac8bbab3dbd3abb52f78f4609286c0230f1a449a1712077558f3815f7a2854a211bbed700f3465666e1689f5f1d8793fcad02d26fcff82582624a9619a1b61c8a3cedaa317559075ae13596ae51ea1db25c3fd1e1a45bb4c0b6318ff928bb57c091e26faed108c235d8542c47fc13f8745a7a4ca1ef2798e4db3f78f8b449300b8bee186b977e723033b023dee63a0f858e685595df29e69f37c2de27b7fed69f67855a506584d2ccad613f3c7de1b649ecc3eda4af98927afc1f3d4b791e9e136c0e27565af161a21476dd38c06ac569fdd346e19f5680e03f7d9bf3c87ca6cce4bb5e4a9616b3551937e6af51b4f93061f43244877d03ed3113f7e4249c0e1a3778a76a916328f24ec4cbbf98488e369178e86a1d162c6b35e3b0c77107e7a761d147dfdd4877f13c2840b10a0ac6c496a6cfaf23f1ceb44de84d951a078c35e8ccf0c73de4a77fcdfd97de4d733c792866cd7710a750ec7586b57d3e86cb4310f7cd548237ed577d6241ce2817bf7bfe50e33a14b0b3c1c168dca4143e5fae1117a354d06c21b07d2e439207f191ac9ffcb218d83bcbbf70c876c54d9a18ddc2bf61e30ca04113321c9f3304d54913e093909474bf2914792b8b55224d290db0b31a0a5d7b3bf702c1626114e794e34f59a2c988a5debc8f4e8540cae869bf1e7fca4fafbc690e4711ff7e5b82eedf282700f60cd00891a9ae4024eb4b0f1ee4be18373efcaca07fabf3bb7e2c3ef6ace9fe80ab7c56b58b940bfead14e79f1515af00b942307866d82e89748deb01bf84dab9f237cc3a2829635b3fef1bf8735164915f9be45ad0a1ed657c904073c74cc0da1733b682b0994fb44f7b018f990cb497410ec95b83858f05f3ba7907c217e49f2eebe8b86493296d0765a76198590cd78a71664c9fff26a732b9455bb5d3b520091421f8f4a517d9b14ff7485eb8696fe820491d09014ec7114472ad4c1198ac4fa16a5208942f8504b668c641ad19e14b895b22f449ee19f8acfb011091a964ce0a8e880227f4774b3b1e0ba512a23ce44d55534bede3846515f91454761520c5c8062500da6d511b986b3a3178c4e9160d155924f9998b2b8f3aac3844b9c1f38fbb1cde4841075381f7369fe74fcbf2a71fbf4f080d93531cb33c52d30a0c498e47d6e2fa348e118a9f10d379525a5e231c0313894630f6ba6f7fc76756aed2e4fb92838ed78e8c3cd136aac03fe9a359024d299e58c1085b7210e747e170e4484f9283aa029d839ee46ef29881981d5601cf2fc5faf5a1db8b170b912531a159539d9218a8c938797218ee20fa6ec79b154206d940e0f6f81b2bfb7ed6f584494b4687e8c89a03910821d20ec5615c149292198060146f163e4cb89c677db8629337b96eade50046b8308596964c30d6bec96c3ed4096b863ec8ad6154001077ced372a5d460bb8068cc19ebe4d8901764c0c90c4ac6ae037a58c152c00396db2c34d17f3275bd03508c5dc9e37eb4c3acf454b72d61fd6f28323e4cc2cb16038cd9f180773e8d345479ab7c922bfe59237698e616d01b69a06920cc9ecddfe07db12de3ee32bd90007a4650414d39aa64be35b24a4194924163ba18c206e6901d148a68d2a86481095612149d65adc2a5f2190c24ae430cc196902b11d62993f720ad0ab19ba865e96a674045b57c75b2a5ed963d4e1a82bef834482b7b7164a7bd1bb0f21cb2f4cbb23395740c61f7b006268d79399be29ff8b6864149874b414777fbe368a36495e85c7f6cfc5e4bb5c573bdebd830e9df4494098771c3df400ee5b5f857434a1869c145723dcf0c66fb8b95de41edc10b5e8c4856997cc49fec25aeeb4f74b9ae7dcc887c199d26795dc940d1fde95324961ebd38e2b5619fd5ad392ad37e89a8655d54638202923e088d6b64790b5471d0ffda61aa2ae2747691c77259d1a4a5d74a18fa9e3acbc6072651a599a520553d7d7270634e83a9eba6b96a8724fc2f4c10d7fea381d3d62b223319b65efb540a4ce03df0ee8087bf04af25710c38e4aa1340570c146962e0f3ca8bb2a5081154dd5543228c10fba99dc72b37f434eee6ef8d0bb8a62bac06d9f500cf553523bde3666671e3688aac3ce38b33230f9c1f54df4bf502542f10f1113df84b117b9eb13afbf9568b23dd06d702975a880f2b870f12192dec6c8d56841d4492f24985eca4a5bed8313c693762824523a92a3d7a8c5b024aea16fcc013e0e05e9903f403989028bd993590681e10068fe52dcf60fd5b01a63307854d54cc57bc6f5b27d1c1b9701c8125c028e54333c11ca04dec042c16ac85a04ddb1a9b506c819b0679920c8f13fda042c1ad4c78a1e77fb8c98063815379286a485ed18c1221e14fa20f7599c4d9f3be9f5631cd94615559c00daa7759573b8551e30a64045b22d23a09827ca0262fb8af2f4a82fd1533184b0d153587e8160206c96bcd6b7b042a24f8c073eac86ac4b84a1f49ea4f4f10d5fa630df2a8fd5191941c5c506b55379137b1ea2937f973ec8aa3b67694f2642eaea0b929889ed9c0999841bbdff5f239326962d4b27f5b8d06959ccac29d367153a2abe3841b5bb0c564353e612e5565d15fd5da2fe09410455787c459d9fec92e3f302f0fca0802fdcd2cae81af41be336ebc1094cc1acebb75dfdffb6ac20fb5ff111e0af335236b81e5621ed45b1f065f66905ca8c077d635d1a3a7654f08fa7204fff6f9dc79903acdcf1d7c262ef34ae08aaabbeaaa5741f1f8860929e2bfb70a0de7a297782e19fb811acd54c9d72daa74f0c78210cb632264a07202100ef5900c352f2de4c03bb4c7e70f53689b0bedb29cb8440d2139bd880ea15d0752282696f15d9a3092d78b57f08c3df2ca4a6a05ac65a866b7ca41b23f21ae8508c2e693098a6049e383df2578916b21431b969614a30d82d08c71bab9e507bb779d45ce1451a04f8981ca0bc033f0bba2f6dd45cb8bab630b2702e839d05504c272d9ca3904118c00214696f47a98588b71a517b99787c3b253b46f08ca7da10a0bf092bb724c095107c15ca772e6d3a9c5f77e31d48584909218a1b1e80021d724f21ace72f911df9722c3c2294748b4345fd0b0c1798d980429014afef563c07c953570b14a8c1fdae0865ace4fdfae1cd666978ef243ea186e0a4907600e0eae91f1d18b374880de57c6e5d97af899b81d8b06070adab07da1923dc880558dbb60028cfd252e4e6cb12031835bfb1f457d3fbe207b7a74ab487fded86f11bbd0f729d62c2b7c5500aa0bdb30a406d4fc3d4659499bb8d61427923aa1ee0469c07962e3680af083484b06ba1fd1c96896bd0a6e13dfdc0848adb695ca77cb0c67e1ad26864546f8d9eb9c6dd4d6a4466ffa98c46fe4a5664f812eb57aa649ae411be1abec23b3aeee3a936ccdbd083cea2e7dd6b4759185f0a5c8c2a3634e987f44052230f2d4b2c601847ef47a4eaaf918d673b553360a00c205f8c1313b280ea76ee9690f0ef7edc5674ed8f821844fcf368e601836dc995726208b48c08d2a83b6c0dbca1445cc7a9e09cf2f46005e556a8aa02d12034a966407a7f07ee3fff087f07d518434208ac3d045d64ed568cd678c9d9781e72cef6d3d7b96e127e399e10e0d854f6d098429e3f5b774ecc4436a515b4ef4b386705888e862945d540919ab42cbc95cd4f00069233e3946b344e1f6acd929cd00a131267d4522ab315c26930f8a01c534203de3d416bb0100afcc30a1249785dbb2d0444b628aee20961f0fc3f4d8502f913b1799b1a4e6056f81478e1d5050e9579c2e56ab964f68d3a21238091e2a58d30cbedfc1edc744cf5e9de36ae6fe7f0e032fb9ecb4ba1a0218e7b963f178dec91e2d023b9522bf0d5bede8c87ff11dc3ec8baba3924be4141f891ddc7f6a11796a56892a37e2fdfbbe1bb7a05c40751f2a4696774d212e637de1c65bc142b4e82601c5de3c44cd65acfb15f7e9a2e993e560db8cde9a6185926b808f36a7341286e6d5393ae509c8e7609b9ffbee244af804ef71fcb66c53fdf6dd9eb59cf1a2cadc394e855443622bc0927475cbd7e2fbf6ba17c2712da1e6f9918575cc69816148e5c0cd4683d3ffed26eb98dc5036057422057004c0800de554d1d215e23abd22a5257829918ef7e8972ec265a00f78580ef6655db6e1da8743b9743632b8aebc483ed8ec0c109ca13b05263f151d5eceb6d2283bbd783f8f597a562262e70bc0bcc17d8d1c6a55c56de223b02d84736db8790b144a0471be800d2039a12df95de8bdf2402757ff70db91d74dbf8a5845a8bb13b50eb3eee017793fc2ba808ab9fb0aae42665bdc3c1172350d19004915eaa9045387c87967682ed04c4cc81b158ae0efc688453db34ce04e0101eee7d96fcb05e897ceab9966a11544f8d88e90e0992881b8ce7995175953b46fb33944ab8ad2ce04b7250014f247c641b78f819ac3c9312a21c186b23bf0d93b19c040518eefa41dc7279183826d1f6d16eae03f403a6760b68ddff2dc859b3bd84f6c4dba8d9155c3da9e9d5193bbc2456c1da58fab2ab36bc7b008213466e34cecae6371ee02fcdf498c1a57d62d82d82bc3b606085c5f86ef0e747185f41f5a1fe6057995b3d3139d0bc68d8cf8064ad09d0415ecc1ccd5224d2c564c79f896ca0eb051816bd0bb9589db9ad1247986978dd6818141763f40964b71a154ba7e3f1644684357f816d186a3e539dfac9b3404918220841831ddf22a9797d94fa497441b3e9f96602ee90d4c2f282b3a32bcdb438dbd3d4e3775917014bee14ccef425d0d0317074b26cb606d241d78dadc963907d9f51d0ff39ca6a073dbaa72f9fc8e34b1336d6e45d6eff64f67227916c6652ec75610b069d667f5bc646c3a8d6bc4d12ad69b1796417e7d91db88f4ee390eaad11cda231f6806af643224c60ab2334d51cf848af6acd59aa349196eee6d1a56308047fc6851971306ac3aef58a5e23d67e517e171b740c2490ea4637256b4d4df846bf0a09280d9e9c81ee772c147530d90f354985e4ef5feafa9ca0907ce2c158da9d9acaa50a6361665de31ca6e346efe6b69e48638535e6bc21a2a70c0f880ed23d6214da0a79e905a7fc52abcd86394543951c21d3cf3f652f85349df0e579d8d8b1b54840134df416d26bc306728f61a6d6a63e9923baeca7e031a70ef1c6265a562f24b9ed2b8b3e2f0127ff2ea8baf19c8f630cf3bedad4eb01a291d7af7678b0548a06755478884393c8eaa15543ce9d09d74462f358b8c828631ffe8252dcfef60e5adaee242d0c84243c4382c19cce3e4ed17e6e55ad906a6fc5e29123d9e4a2bd82a3ed221df2f5867eb0bdb526fd4e072b1bf4762e04b918708e1186b3a223723625bf837f7c41f6c7cbdaad844a0deaaa33fef2c74a3befb6566031da42b8ee5d63716f5336adedc53967809c385e07a9234751bb8000cea819fc2aed8c2a87ef853ec880d4cea7bfc19734c48adab738818c1987c38a6dd17c1c1934539620d562368c2c5f285e020588726348256f0a9c3cf2b84b1debae955a45d50af0b18ead1be91d228fade73180670887aeb66260d09ae9670902c4066f31b916b40cfaa52bd5d9274f77cf40d394ba7755befc586bb05aaba596447edf4e58ca99b54338484fa6e1761be09e19a4490c62c8885dd4095aa622372741d8767fe2b7b53e8018b09b49e4cc93b6c07faa8acdc27284a90ba7b2bb1162e7440190928d8248c23015e16b7600d56de7d3074a0cc2f2b4265faa91ba5d8eb0785914e8ab1e0b4b9c1c91e10d0426b2080e4628a2466ebe2b3890e14a6d0ecd9bb14c647bb6f70a09a1864c916a6e862b3f759267afb6f6a9639c1cb5981eb7770a646bd34d1c10616ecd6b1dd3420bdf2e5e07a05d7c15609488013a0335017669ef433b8f7ba556b9127cdf7e5ccae2b1ca8316f3e64d3c2116dfa649578080a51a7e915ff2aa06efa77fd0f4f00ec22cf8860c76a454bc16177a28fda735b3d3b9290beeba32fd663d043deae32c5ba8195c1c884d1f8700ddb1062e3d912a3d2ac8267d66c181c7e6fe665760b5142babf6c66026aa0592146022a21e1803a11b993541d74e98cf92cee85166946e5de2f4d8788da62094f881ba0d0721a9531988958557550529d979a5d3041a53d37a0d734a805532f7bd7ee190dff9cf361798e166a52cd1632fd06f2fb99369a96caa7d87e80d5917f36b9bde66ff458ae4bd7017fabdcf497aab457e9d9adfadd6140f518ee5e22ca634d597823a7547377f836356045886d0873834da805ba406a04706344b97ab5f5d4429e8e8a9ba8e101d8b2d1212447342badec09c8d12282daafcbef32b14c34af472158f96b35ddc60aa0e9e4436521b1bbcd6a4e9157cfc4aa9e32ca007224c8a8306a320b37c74e8e71babf156b5aa7145c438af1942ef4c1ecef4991dc78cc14a93b295a75abbbe4d394be4fa1543f71f9a7f0e2d4d98a036a4a0f54284541d96f28412151722207df04694d777dce93256b231d91de95e3ff562fc1cb8f0cae7af586a3e53dd1398aa8d51ad385d8900a684d4501aaf12ae3696b94100513cc46a5229f84a3f34b2e22fa2f7bdf51a8aa292b86a4dd1d91148baceefe7a9c02b7696052832b2e23912d8ff7457f9eeff6e5d17fc5c9df154ca0d2bc801e5a69573ccf61653c2fa111a07118ac14e0caa707cdb7416d944e59844d1c41199e4d46b02e09930dd5c0d9a7cb7b6488c816a8af1ac4076e11a9ebf82fc9e5d8fc1be1f723887f081c56aa508d996daa61c066f3465ae64130572b1ee9a7faa091b6b2ec2600c95a3a8c0c55b3892f1ef567d9fd8bce9cf753e35af6b64c9ffa12e9edb80af921cb56047087c7d61c19ccf1b7c763801cdbf9cac29c0bd6413f1189c446de317695b89f637ad13f6c8e369eac181099b3850afa2f07c854ede60df4a393c22833b9f1e34c094a6b8eeaba3a109fda7525632c3fda713fcf9d8e0e853e9d33b48e8fe64bb888a3d7f20c7c13019f5997f5f80be74e84f83538504570e7a78e74aa5f9f423eb8d70c93dcced447dc64eeb3d74ea94838ed384b6d37df5be9c5f3a58e8dc2f7e8a2bcbca9fbbd8ad9cb422d6f22194a3c928b0ae8fb2893b47848bf5e9330a037efb72109c65a08e65e8eea1962a2971f00d00fb41b1d185749f67f9548735797f9d5844101afe7960019414643dbd2ed946649de44f3d40c496fa9a5e2b57caf47442694b4cdf73df2a75b43bd4ce862eb7c39a48ee11b6d2b2c0675d1bd5444bdd8ad09b455052e2d840c0d52201e594d077568dab518de80654bf9d29b55e281e45a0ccbaea68caa4c97e90950cc6de5fc1458fa600c0dfb64be5bdef45dbaea1d72ad7317f90b3c323ebf6e851b10eea732504bb2f7eeb6e5de5bca9452d10ce60cce0cad4804f76ef17c8109911369224c25575dd4228ac20a3dd6798c780d5f2fe22cc92d8bac3f134d1487a6103aada089e42bda788ebf878169c8f6c1a18cc10cf6f64e13bd08d9db442db0fe4d7443734322eec75d54708cffe4be7b127843d26fa0a54da389d80737b9caf38d686302411063fc25830d12b9ac9482bd4dd454c4146d215cef010eac5f9f81f50ebcde024f81f5be038ef9db042b633d70183301dbb707cb2fc15b01db37037788eda883adb839f43f90f57f59d25489cfc974588c46084dca314c3e567f7890f597d00be0ef6d81decfe4bf84ed694107374b48a9d314fa2ef308f4a652ae2570449c30bd129fe3e2f9e5e9fcaeacd113df3473538ee37a87689691b0b4f6b3471fc751cc271c273a29382753fa384e4ff34f15d516f374e69baa8b4abd00cedc09bc944d7f99721ded276b64741a3e8868cb8f7a8e2bf590bf064f7f42797872d353afe1bffd9d87ce75a5d44337d54b632f9f4ea6d3e97491b05c3b0814c8ed03f75c959c404e028a3b71dc09c53daa8ee8739a4683c80cfbd30f59fe1f1bde7f25d6ff655297f99f5763fb539f409a8aaa2795d3ab5c69398e034192976364f9f36cb43c9d763edbeef73c9df6e73813c771dce94dd547895538eef426138b3fe138f7a71b84e54bcf72fbc79efef41cf7270ef461fad2f7cb06bf214290e0afff04066920aefae81e09cba813c7cfdd5e622ff7df2976cf414eff3df7b7f40ddefeb1df73aa4f7da3fefe6041813e4cb704a29e6bd58d6df07bee9e2b87e2bc747a06eb650106716b3a81aad2a3fec3c1dfcfa83f7d2b691afd27b0c54fdb9657f16c9cba2f9d502c5a80dc95f6e3388e053d714fe2be21cb994aa5cad66432994ca5ced44aec077eef52bd5755d2a7eacaab54d4b3909ab9559effb20ae8e3f4a6e76277dbaedcdc1f6c83f747c7a08fd3af7cff952b8fc335c0d45f026f5501591ef5df7f6c7ad40de22baa57492b757ad89f022f8dfdde8709bcbd7941749619bc2514eaa7a7833a619625b32c95ede974c2ec495a9695ff39bf93d034da4e1ab7f9f9fd63b9f6beff7eecf7241289e378749fea8ae9717c88ed206e57c04e42fbf0bd4a2dd52b735fecd77da5ffd9d07c3da0f139f0d264daf4fa31f025f430099b1668fc3b6d100b0f7fc2542a89f577496541a4e502f86f2fca1fb15855fdb24117fb292ad029b727687c7635bcd81b3dbe7084995df33ddbc1630db4a7edd7b18307b37cec390ed27a681a0740a08387058610b223a5575a5673e98d9e15e8e0b1e30bf4de4c09cabcb07cb1fecf35a914d5f1629999f6f7e8e6211d444b2ceb0ad40823d65f084f13c7c905eadf554834b98076c431c26b7a3aa1de3cce04e7b1bcda40449cc0de3be2888def4e3c9deb41defeb187405589cfe99743b1f2e77392eb6ddbb86de39c0479774d3713ae815eb6b9cb7d07369087fd6a17f7b75df17d7060bfc0088e6affe6037adbf5f27e35d8af2be5c4be9da0d877bfdaf58522ed84ae2c91ed8722c4defe2cf316208406148bfd5a35c24396d730c243af31031726647b674e178dace91191c5716935697518d0ae19509c12d83b94d4a99d5407b9fa8933d05011e79ea2891a8b37bce672511701a2e58aae6c3f05593991ae6c04595e94ae2cdbf84f57edeaa096edea66718e4a01d6e335572b5a02dbf11a1ecc08b6339f5fee28c83ab6a34d99ab3644a1572beaa915794ddbc68eb8cdf41a1e199171655eb422257627b44becc57666112d89db68453273ce24f6623baec4b23c0627b6e3a1633cd8121e7a74a01c4e61a71127f6623b5ad5a0685368431e46d1883421ad68aeb429d88e5644447fe45190c6c489f8b4ca8fa00c82b7bb47da0d050e7b7ee9d394134f2afc02914f790dc7d59449bb54ec4e3c317aec829d53ac7fc72e1a6965656f5da1066368ea0222b4796a6896bf651028f5281e72f1353f3ef19a1b5bdd4f845e51e0a1c809477106796314660882bdb1880846a4027be5ec96d7f412ec8d4b7aa24d9c31f630185734a03f76fced1de63a670f73ef30dba464b88cbd1cf4d2af659f8a124cfda57f5f1dc81ec329c9813c02d9b318145f2f29af2050e61aa7709bf88a31fe2ff6c65788812f300a7d815e0e8ad2fb52d0c4339d8b4107b17a7aa218e9a07ed25374500fbbac7fd4b4679025c15ea248f49848c463be22f4764fb3d735ccd814ec575e13d4414d2c0104ef2258f7eee119ff0e9a3d086a9e1ed1a67b7a7a847a7a80e8608fe33adad7e8235526912d0f95fc78e8355c42fa740f1070c0c36f200f4318d0cb41e10e0b449b8632a708a7bf8d57085bc9f1cc29ec67590a0bc5d8950320f0908540107b700004cddd91a3120f7f26ed58a8dbdd650079e8a1d7f437141c17ede612a87c0195ef537ab8805e1612d25f37281cdf6963639633ce324fbe16fd330c6430ebe8046e02cf2f9723424da0f6042a1127ba9fc00b7162e377cb969dc3854cb13d81aac4e74c1f2f808f457dac5c123b9ff3e9e3359e4a51da1deb9db1b37bb23cbc32d3c76b1a0c5b46a1d327de49637c17ccb3036fca4e1f39594678389fe3a64ffc79c4c3eee57331fe6475b588480410cd76e0f59e3e1023ac8feefb251b28111ed8bdfc0e0c122d055538a6ed7eda0e2c22da6e70bebf3c12c4c3f9d3a7fbdb2fc1c992d367b286f070fe690e74fa1c993e2c6f8e2f3c3351cfd52b1ff5eee96cdf4e5056fc98c46e7dc4437716d27311bb7d5cf906af8c29d878001b0b60e3cfb00dd67ae119d1c60f598667741f7d6c7caef4ecb14af5888d2f3f62121b1fb561ad89b53cacd5b18c3c6ca5c7f4f71c573c471bd5c71aca30cbafa877baee64b1c41e96aa22bdb42470f4bdf400e33b5a6f4c62298f0fc713593afa794ab7b7fa05f51cc733230f14f662ad9f6893cd49699091d710c714a09c05271cb130bf3d27254b51555510af62400e06bc4e031ec783a8381ef0fdaaaadaf154f5ece938e0d90352a3adea7582781ce7847eb81f8efb69c539299d3eb116c7c315e119673dfb021c87df70b1537b9a2c8f01788e3202f8907593094d6a5d52d665352d0b6a5978cab6a0b6c5b42db41629eb825a17d3feb3dffcb3a7eccfd54cb68aab07541f0f7808fc8e9a4388efe724b0e3ef940058c3ad0422f0b3f5136db82c3c618af59f49a61297d77016a040c4fa4f20b7f9d56ac5448eff64e235cce3fde713286e3381f797400476d407e461f5803cb82050afca7bff803a44fdf085a83e86a81fe363defb2f092abfffce205ef59713b2fea725e8f697fb69a2faa716d0ed75d4d114a3282307d4d1146ec511e176582b0b60e160adfc8d4c5bb1bc0d570d978e0b08f04ed22b00bc94f439422c6f572c8f03b27c02349726141f01ac038097fec0a201de94015c2d7f03ded9f23660cbd78042d3d59aab6833595e006fd1d010e06d8ba6e583b70200de3903de290300f05bb1872b607ecb82138aec002c67c1094a6c7b2a7054d423077a230fbfed17bf81cfa312c978a090033b81ba048ae5eac5b44004662fc7635fec9df56e44ee766403b2fe2cf56e538c88dcd1911190d0dd567763dded6e42a3d5a8aa72c4f4c78057e5ebb30dab2a878a8f065552e5d9d2dbfd8b8741a1580d3c78e204db35f82326b13d7c7b39580d3c78b2c476e00f19db03eaa6d2c850652f6d361ad5ba4d1635d16eaec8ca87fd78e898120f9d85c743ff76b056eb3f1550eea70585a79299c47fae0c8d0cdb2fbfb9b0b859a14409bae2afb7262bdac4391f74d136f2f0b448e2e2a1cb209e2596c5603908064c6c7b9f0455f9adced764d180def8a7bf58ebe37b355caaeabbed0d9751b9d81591f57c7dcc97574602a8fc58b9251f112aff723fd67f3897877ee7d39fac0d05b27a345992dee9537fdce8acf854f556253da5cf71a18794feedff5e05daa0555563f4de7bcf75df9503af8cedef643f4e8c3891a5a54a4b84b4a37a3d19cbf5805c917a85d8953f551f2670f46cbdf5d3fdcafcb6fda5ca4d1605a70fadf388873e5958eb875be235de93c0d8e3a125713fd1865d9ccb0363110fca6c75c7c9eaba5884ab9167f4913581eff7a6402fc763fdbd207a278be57d816e55269432b2a4ec9f40e0293259b3c5038ce09d3f76b22600f69c024714016f8092bf943ab1fea60f65c89024e562e43ed61f9ded5e3cfcfeca273d27a594db7f55957ddbef8504be44195ffb8458f921e96fff8b87de4fc289dd7d3bfb3d8ddfe3c4afcae73afa2f61fcd1d90e4c7968a21e7a330102bdcf92c6797400032b3be80674764fd3cda39d8189f59758cab3f81a68f2d0b128d35e2548848c3309d469220cb8cee1972b836c4bd1b42ccbc02efa619aa2699ac65ccafeca9524caa2c0235772a765fde5125e238b780d8b5dfe3289bffc914bdc336591a8c86b6e1c8a52e214afc9a4d080285e63ca9ce8c425d1884f296a9fc28984ba6824452351f439e2a189271ae9e8df2581244253ef71c8439597ab15273dc712c586ac0b751e7a0612a1fc52c1ba52855c0aebc625d63f32711bdf71e1f1155dd6a38f11eed7dea7c455f6ceddd823572e14893cc6df57b46001faa45cc09b52012f5d018302401ec617c7178cda1fc387a931ef432dc0c71831a674b95c8e19691ffc69c2f053726808a35fd6b5173f53b26a8c359330e83dbfe939ae9992ccc7c3561229e8cd7c3e92f63393348de9d33ef8e935f06247301fcedbe311e3654055bf10999ff900647fb51f2c89c71c993ef34e9f0872a61e7a67cbd44399d4a1a7007516c5986db525e1197fe9021f74812e702ef1f9e3a192a197f7c78c348dec877df07f7939f422875ee4d0cbcba197bff4e5efcc4edcd6e2196dfd25263189c9c75a3c23c98fc7f84f15d77e54b6a2680380f71fadbc6624e31d63a15028d4df9445fda5f639dac8005104a07300604a002073cc0800decacacaca4ad7751d17bbdf5a3ce314d4279079269ebc184f5da3754fc763f9aed152ce71cf06cbcb5ec598d3e94d2f4435f347cc7ce7c8cc54d27b4ed71ca47f000dc0b327448c9779163f656a936aef78333c59cb6b4ea00f158eef6d830cc6cf7cb224a36f90e38af40c5e8f044e9e19bd096ccacf5e0c7a794d04694817fbe02d5d55d8e629380a4b612800689e325bad16bb0c50d906001f5303f397837cf8cbaf2c00537302c7f8df15da4f8c7139d43ef8c3303d176b7c45a0f837ba705cee340d39b4235dc02b6054e6f0ca974be4b7666bb6666bb65cde65de5f7302db4817708cbf0c28e774c97e582f5ebc78f1dfa26a51b5a8541c57f93dd426c233a3f7a1ea8831a387a93c3c1c3d176154168a361698f2525251e848c8e431a3d173ed3c1c7dd7c9332317b9a4696cecc3e847e1c88e46a3d12fb1b2b238894142d1666b798c3feaefa434251477a24d4c613be1953b51caff407fb112a6c6e8784a96654273c8ca969616e619f952eb4c75daccebe71ff1e4d0aaf0021cb82c7fcd63b71a833cf41d21627c74c2eedf07c6e626f4624147301f2c894ff4e1dae2b9c6232daa8b97b5c462b688e88ab576aef4ef30163b01eb65272d5bcb642fc6c29c78175ffa1a5b4a5c5ed3a6d731bd49b6ecef69214001f037b67ca4fc213d2fc61df0ca3791ba3f1ef179cf7bae85ff225114bfe71373efa8b09527678ad1ee51f3519f8d7e45fb16f5f4ccaf612cac8529c192c8e76a7a12e91b547def7de00e4f3ef7931e27b6c4efdbc3219f2920f3315ee58191ff5319b160e6d14b093e8bd133e863a43df78e6510d45ee584630535515256547ce9a15072a21e43a12286ea0e05768f7528f0be4b0dd216d5a8c7502e8fa2ffc3fc7d1f3d1dfa1c479b186f7ace64da11e3c1eb811e0562bcf3c498d17bf53381b7776c5c79148801f24e8c89207d15067dd097f4631ce7c7f17ef6faf4527ed7ebedc41d1b4ddff271bf3cbdec97a4e79cf34e0443625499a21d32608c6f8f5ed3abdeab1789184710d9abf226d2ab90dad2d3633f7ac716d1f6f4286a55f50a19b22adf522f124556e5f45cd76d5ef7b4b6e54c2695bf186bf28ccae3b88e49e5f400781cf76af613638c1b29fbe119eeb65b5ec36d423d76848b1123468c18316464bc1af1e5af68f5cfe95554586ad7fe5d049e6184a6c126d4fb64208efb0af4f6c86b992dc95c199087d98f8fc592587f54650f6794d9aae16d31254d548c07ed83fff4c192602d0f7c51ceb9f1d051dd15a6d8b1d2a23c1c9208450880cad3f9b616cf48290910944409e6620c080bc2b8b796d7dc56b22de916cfc0ac9d2dafe12814e109379a6bc5af2db901edbfdacfa86a3fdb92ada5fd78c8da6aafecd68a36ddea56ab5bda12af69ccc346cce2995310bded2deb4abca6ff76bf861dc17c30568cbf184b6efe2189f11c030c634c2bcc3614788db0a83755afded3ce8eed180f0ae99c18182b46ed68aad99f48df55e547f56248ac3fa36a9068558244ae776cf79c9743930219886c37eda97b19808f5f32b98def2b7635131d835ec1096caf421a8ef17785ccdf92edbb05e07ba6cad418558b6c6aaedac7ca4012c095b8d1889b368c3600008f30418c57a273627c8cf7e74b73b3c309150e6e888df1ecf110322427c6d318335baceeefd923919ebd1a23ebbdf7dff799abe87b12a9664be4dfecc7facf97b29f8875ec1eca745a791594a99a466dbfca4576a55ecce766f6ce5610cbd2e5354d22b594400adecc5e8c65efd69a9699b962b6bd6f623e49d081b162cc090bf46e3fbd2a3d8c3646c55831c62bb6c4c36efbf19a3b5b5b6b6b1db1fe9b128c85f9604a30d6d6b23eb3b855cca7624afc31104be2e10ce89dadadb5fd6c4a6440efdc5ab3d5b2fc02896e48e845b021c888f57726af2028afcf5c960906037afb15242553edd4a7e766e2040d7a618205694c5e2ff6d1600765d9c53e081c792f7f798d877dbfd8fba4737a569dbeedc5ec8fce7a74c796beb68e1f8d788c7678d82fecafbf4eeffde022db75d27addc77df169d589db7b55e31ec749ff7d4f557ec70e0f1db38df3c2aebf06c00a82d2432d65d551ba08e37e6549dc49e6e9989e56d2ca62efd2f5fe9c94be562f66ee57bf883831029d499017c09fbdefcae25d826896056659d4b62e2dafe33dcf50b121a2f24acc9c1468555451458e13fcfe3a4c5f7aae3dee5ba6e54a558687de83f4b837551a19487ac0a12ddfc241a627bc9a128332babf9f20d9f99c17209217e08b491d846cc932933a30b1edd9708b3514eb25afadc00d05cf5c815e76790dc65f7aec6f10cdcab8a1186326acf4a6522643661986d22cf6282f8756451555d0b494bee571bcc5961ec75bea8d879685e5b91631adf72bf5f4a6eaf23652af7a2e7a36b4cf5850efe40294655986c3c2a5f693da4e3c0c6a26a57a8a0285f912101e3af65d4bcf31c06bb0d29f9e3e77aa0cc0a156a0272e76a65a7fa5feab5417dfb5997818e4a193d3bbb703e65f8042542cfec5770e8ce7ba4b7efa1bafb96d398a09fc1b6d0b175fbfbf9946bbda07ffd62cbf890694696e5eaea6d14060bbb0154f9c74508bda4c805ade5bbec1eb364681867ffb2584b4a5d2d30f85a85880395c5e0409b0fd4286e4bcf8235e804a38012401d6e53da77360c0f8233c07c61402e661d02f813e2c33c06b4e3840303d959e4fdf953ed614eb4aa769ddeb2fd5d3b7489b7d0b1d6c576a4bb53d21609ebd1c9aedb64544db9f651f245a6f04c48853cb1df23e9a0230cf3503efecd19f4e2edee3b86784855155d49788687bfbf5427dc852ffc5c31504680fdda313f659dfb61e8377f42f8cb08f13812071aa40da72dbf0dcab6d4f40d4c707b27fcb25af46026c3fd62fbe5f3c57242478f1e205095e7c83484c7004cc2391c39e0e18b086f7985dd1c19e8d6edbc3e33866d9cbbeb6e548956d376d7b36a2d00dafe9c08ff4d2ab31b214fcef2405daf5033ac75ffdea8aacbfa28d8e18e38f03c40d06bfe72864db45df03fd773ccf184dbbb59d93ab9c75f5f66befd76df69e902486e494e0c51f618217af44e7bc0085740e783bf32a9058451555e4c1f6771548dbd69e3bc2042faf44e7bc3c0789764e2f88cee309debe7c82029d7ffdb5793adb47afc6c836d856e01926eeaf7ff1bafd9a1693d63a13b79af6a276bf9e40ffa5aa74bcf817416d059ee917cf3c81627ffbf57ad2dd42af6eea65cf95baf674f831db1c767caebd1c91a37f58a7fe33067da44a1e8e7e0c746179d17eb672ce28e3e760d0c5c308245671532997980da93e0022701febdca6e562e75ad661d2725a97695b37b951b77935a203f1245830348f0a897cce899b9575d6efbe79c7412a823432d8ecd06b5a472a88d3116d6204609da370a3030c4b58ff1d21cd0f3274446f2fe499efd12b2858097613dde14e1a1a3010b1fe3276687a6acf53fb39b59d25786e3cec06657868997f7496a516e8a5e9349a6d0ef472d09006f6144a68b59746686a33ee43e101bd2da46257312803087395266b82f27ced479279821ea8710c4f0c8cb1fd44130ebeda78ac7f6f9b2645d0b659477c982672b6ba72c5fc594fb431b53808073fb6a6595992ec273b12594fd89bb53a1e2025a0d89b2db1fe5864a993adb4224d8a46349fc855b69a4fb0257cc5337cb07eb322b647dfdb4f29292dcaf795ed9d6823c18cc7c36ce56160b6040794ad6c0df6726544b46922ad475b42e3d18a78e8594f56245b225bc92f4cc956d94eb67ae9436fb68adc1dc55e93c9de8c87e50af4a15e83bd04f7ca5eb9923c1e7a878614dd783c27f048b1fe2fab9d224b6cafc47fcf65c0ceef7aabeddeb66d0389ecc4efd58f6abb573b5b6fab4dd35448660e8d31f17b7afb4dd1ed7bb571603589e62cd2b38aac243f7da4b5f51046bc824c8971080fb55987c84083dd57db1fe3e908b4b7ae570d829ff119037f543b27a8c20186a05a2d7301cb66200f8180f4fe3f2cc023da90e28eb74fd3df419d774ee0a2145436c85e1568bb7800050745e18228d6bfeb296d81a8bdf7dcf6838c68811ef507bfbcf95f127af949f7f09ad1065e597b58809f74a91e5e2363fc0db4400f7e02f6989ac64f6e78dc9b29acff0fced2341923502d04221472a331137b136de6bc91344f9ca56134e5a1494811e89dacfe8c67b0f58e4f788040824f780441e135e7a3353014eb2f439a1f42ecf9e5c62ae331fe3409eaccb3b30491293c65d82111a5917f439694416a4e27d6bf4bbdbb8c2e013f7a55e97ba9b24282b7a554d2302c46cccb30ccfb88bd8661d1bb1a96c2c0d46ba954c6d2d86b18cb635d05ba67d49348a8b971dd133fc618238e182be9bd9ae2672e815cf7445406fa407d87ea82605ff628d9c9fb96e445175b8b162c58b8b8a8542d2d2b56a4522c2c28d4e96432adaca8a8944a949248dfe7795dc771a3d166e3667b6e1a6a9b5f42c557f9152ab404de59996728b8c5c81c7610ac47116db3cf401f39beefba1853dfd5398f9f4529df4379dde8496010cc76a91fa150d462df6ba7ef4da35fa9b7873dd9985ca0f1bf20981d3df7d51aff0bc2c7f7547ed6a97c8f0f18a6e861253922535ef381b7f42a4f6a3065ca012dfdf7975a92574bdfaea282a98041305b7acccbe1227a3a2530dcbeabaaeddb6ee08b4f77ab5bc5e178aab6781716e06d16d5a57e47fa9e9a469d75c6de077a605855dcb7e5c097dfa1b77bda27f6c8fea9f9482989b2b0a4f3b07da46cf560610e71488bc3a0c1ce16245954baa40c5a09b14b06957898f0136c8892156553f030616f46b47a820d46b0379b62fde5ab8db8e6cf54325b5b4ff7b8ecd69341f11a0e8a2e9e21ddc68f31f38936ddd3994f16e461e68a319e31c97eb21f7eef4975f6b77b3297f4f19a9a2f7b654e32976451ede5352faf6112e43512036f2f61af745996873d1eb2e8f51e235dcffcb963af64613e4fa03745535b10e702d6136f78597e83e527dce0851b0be506e7f9e5facafae6dd0cf1bd7f8f685372027bee2717dd5ea2a7a727daf49c5a11cff80f99a4d022ca81cf2bda18200a415678d244e815f444e8e6464809fd1a8da847da7e8592bc4e629ecef6dcb671927bc98dc00e9ab699bc6eba89ce7073a372436282bab7837e89f49ce33d05a7d38fe4dddcf8f8ed09c1395f7873c3c11bcebb3870d374c3ec1fdd357f9f3395f2d740e9fefe19c8f2a3d7e9840469a616c6981823381d941f63f8994a333c41d4b2fcb2fcc41b9ed876b99c58ff06baa1038a93ef3b540ee8e5976a7bbe415fe5fd8d88364e68efdf3d5ed3b1767ce40fcff0ab7d78dc78b9a20d0e8ef16ff2f202199e585707f5931bafa0cfb2d247e24aa46fc47131d6694755b5fdb49bcbc5835f3756484cd0f676905ea5f49cf33d8954fa5e3c32d28b877fbcf1dde0f0f0085e16b25fbf5a880bc3cd0d13ebff4367dfefededd03e9bcf39d96be06cfa2959439a6e095e7e82938144d805051a5c4c8064dbee0e5535a2c5f1a673ca1865c2128d17861c188eda8997a1519121bd0c2558fadb1f9f7ee7705d696442f0364823c3367422fb586b448be988369cc34321cf788fd611432befdb1841d38a4ac97b9cd8757b1947230f73ac6082862370aaf9d34e70870e293792c404c56a0f9ed1c1430a41372dc32f1275fea980eef018d921bd8c3eab2dfd577b067b091e4dcbb26c9469d9f66c27a7655af6542431451aa8d446dc489b9dd6652095200d671630977409b1b32ee623a5ff441b95a0240c4405112057117bbd6b0151d1b2d76564ccc759b24716913c45988fb3309fa2255e237d739feb4a32cc77a20de613633c7acb59988f27f19efe67a5582e2c1f25acd8d262b5b0241303dda703a2975213e6dab23097ac78d2dc9b2762020d68c0a0c4facbc0b9e11f31f0c6e86f7c0d088e31375a027103e7d9e502c4b91810373c64577f749008696f489b4dfb227b6a3d674fad3107a20ef4861fcac4a9633259069a031003cd01c8c4a3818183797e81a9a82294f4cd2eaf71a029e8d8c183c83aa98e2c57206d494c57ac505179399003b93c4cd280c8f57b2378064644bbbce67b7d6c0410245696f4b1fbaaf69148aaa7cf1eda20629db3219ec1868f3e0d7711b11e49b53ffbea3e6e87fcd81f2f5693e245ee537a1dd1c6c431a5a6c17ff949e94b4f6bc761292c5d7fc26de9715cfb34161c07b98b6ae6df6ee52fb53289fbf8ac542ff2d0a5cc1dcee3fea4b54c514a29fd996aa52af34c831709ab7d29fe7561bfef35144662a916b0b7f43f5eac76a775f179d7a768efd65b5ea37d128ed56d5af1a17de44b956ada119a7622ca52f48791282d3d7b35481ce7d7fea4e8efc33e6e97872d1f5f4035da66f15faa48625ee3668d5a51cf55d263fd7a6119c6f357a6e12e86f1c245ed4baf06d2b08fb52ddb978a64e761be85ce00139b28c6ef8ff4bf8fd4b1b63809a2b4d496a5b66547d5fbd2f21155e3c32081014d1ef33d4bd5f99e622d44f4fdd742df77cf3105f2d0c5334e7978e833ee1c56165fb26de10cd433bf46bf54bdc8573da50bd40656877c49d370965e62fd431e3a3b3a0a498a9bbe3dd47f3c603c0c2844c56faa4e14c567a505e83e23f6219ef11317e8d0890bf472908eeff374e67f1ecbf7cbdbc031fe9ddbc4bcbc4947fbe0f4811a88f6dfee06fb9b2fde06b6e933b4508c691a4dc496fbef6bd1341049cc16e8ed226722b44917de7358172b6a476f2df1bed8f81cd6d18aa2f42fb55eb5f45d3cbfe018fade3b45757bb45cf4744a319573a267a3c51342888a5fc8901c983f02e63b2784c1de73b8e6c05e0409b01cfe119e130a01e3c307f29ae92e9ec1f2feeca9674f9d52cf4cc38fb40ffea7c7aa8ce7479a06057da8b4674bc1d2bb4cf95d37f6819f63fef0c8e419a635be67791418021aebe37b96f7ef291adbd23333bd3a2c46434bdf5e8d694be0edff34ecbfa7f5eab018f883c61302c6b793e696fe35d89c4bf1ef86f15c4f4ee8f5a166d9fb837ecddc2ed585827d847b9ce852597cac2b5ebb2eacf72eea4d590f0c3a55970f992ce82e162b9ebdee562bf1904baf6925fe1f0cb6b410a031da870a893630305231df43586154181d9687b09a56029bdba754af0e5b8a24540abc345663666606aff6dfe344ad969e3d1c1ac6c2791e97624161242d52138ed1c8d391ff91aa113ce3ef439a17715cf994b853242e117be211ebafed60284658773990d704c07a8b679a9a1bf7a83fb17cf6def3f7f4bfd75e5bf9ee97434345443e85fe57310d54915efb928af4f1e9d75f7ffdf5f737b40daa48e00e8e313aac0eabb9f0731a787fd0705aa755fa1f05603ccc7f34dc0daa4ae00e18200cf8da574b6f62f9c8f2fdfc2db0fe2ac6f2384eebfd1e0359b0403ff0074d8b672f08966fd007cb47d087fcfe1f341ff883c6b2802cdff4236fe504a63c8f6314876a2ef5bd46bfefd7b8140a45c19be26dfbd4b67da9e75295df05a75c3c6af2f7dad561491f23333f07f3421a4f177d596630c27c8f18d3a53a1a8a3630bec41e479bed61c4d0360560401ad307fa9893b432bfb932d4278f85c4278f8504de394fe0a5a99b62c1865c9eeb357de9fb569aebd5b1030608337afe3e980ae3bb5e1d3040fee2b3f88fc20c61d96bfa08fea0b1d474877cd445a576d6954fb55861617a977a75e038d995e7ba4ef3b827d5b6ef4425ed4ba5976e03e361fe9a6cc2bf2eb6f49ddbc480268f317de9b5bab10fa60fabe961aae961d4ce634c5cca4c2b311ec73f8e837986f9ae484800030303f30c030686f6bdf6a65771d10f1abbf226534b6d961124601beadf2bb0de313802c6c38c605413f4f0305589cee9e139959867f0fe78b12abf4205d49eeb010a30d3837d096960feae80f1b76479b38989a9cc2b5e0508f37f3c7a155eb16245a7752a9d272d60f9995f9a5f25abf18db0a4955aaaf7c50256fbaf5e1afbc9bcb0e8681aa587c1d912cc1f61029857a2736050df9bbe549b7d68b03f48724c771cd3cd32342d7d188a6a955aca7ec5fb53bd71f5bd58efb54aaa1d245a1ca7206c41089663ee35eebd37d56957b857e1defbef696828d59e3d1d0dbcfca3b321e8ec0d627d684ffa1bc492bcb3da6da6a5cfd369ca9e6f21085bf0812565f2332f876f21085b386249e08fee8bf9f0efbc142606745708c28001539188170dc59824d67fc710d194a1a0d136bbb8f2294ee4a1bfbb2675599742d32b2132a0f7238e9baef5c235e086977e140c683fb7c7517b833e680bb84ca65cb4acf336ea6187691a877d8cd94c1a8e3ff61d7829076211c3f11603a5363f7a39b4df469ba7b3cda9594cfb344cfbed231837f04535cdd3c9a6267f4e2bb1ce3325321adad9ec3b7b0cab92a5d730775623383fcb6208bec40ccc21b1db59fc8885a0b372c6295c8c16aa08f5b26eae5449dfbdecba5b09cbd0a149c9f891e2f8062cd6e973d2d3994aa612223bac5a1ebad780885925f3672ef19ae8d5c0563c114dab4eea2fe61386410f80c250ba85793630cf466f513025580f9ab69a601a71a349b096f623dfc4a1e9b9761c9a9ed6c9a1c9f4d188c9f437eac0fa33f38ce93b8ebd1caeec277ea49a13b59a4b5ba2fd683f134b82f96047bc084a1063f5d0295f7355f1b3a50d3981e51eb2b20c5ed3e3c49137e36b2eaf89da926fcf8616599c0a2bba8cc05b8b99993d7659ff9997f59709ca9e588f510589b1a24d730ce683b5d8663fd166ce49e96348d8666b29c13a0605ac0ad61f8bc23ad6853158ffad156dbe1853894f27d62713eb13c8fa121478ff04de3092b696ebc6bb68ffa98b774fe79f2b129a8f349857a37e8be7e2eba5a4a24c91320015406794269ac0181c73a3cbb5842514a108400042134d30c184bd93fb4e8f3b3cd3aff8c3c9e7482c2547ea26b9db681ce0946dd98f70f13954911d62d9d3c1214113a553e5efa39fdffbf91b9cfe00ce219284f6d927b1a984d03efb1d1ab8639bd1b381c9e699e699ceb297977f0c7b80f6d967de0e0de434cf01f27d441628684669e6d970950db60d0eb1dcf454d8fb90ef8f812c5050150ef9b26df4f34f0f478355d8e9f9739bb6f977152012728874cd095c0d693279d7512a49427031957271b1e1267bbbe5ff4b2ae5a167235ae7197ee9d9903a882491c4f46c6036f36c78b4914525f44a3b3d1628a87fcadbab31c45207a9e6d9983cdba3c4326dfb3c3288e1c0450b162eaa96152916d4c9a4296145a544499fd771a36d6a192665155218e1555d1cb368efc4fc4bc3f8ed4d63b3f1feb35f25e2040b16a87466e296c3baacba95dcc4aa8ca38f7b8eeb9ce89eeba23777d8273146c0ceccf1be3d2e36c7f8cd7b849da007d21853a7e5eaf49adb5438deca7f2b9d63fa472995169e8915d3bc9f158907728c8936abd172a36cc4588a679a677cb86ef90b35936d8fe34979c6ed57e7a893355aaee33a1e6ddf45c24e79e3f218eb65244c6acc3355b39c676650d5a00251c08048085520c31ba0d024453ba8032860831437d8a1831cd29005a5a26529785014a5e041513c02c728a02c05133b56cb83e526774862efe4267690228517e4302538024ec5df59cdaa18649e09c0b736316c072b8bf5cc76a0020cc3326c0733884dd08c678c9e3ae8200c2fa6649653d39cb32c458f1bec20650c196f19866118866513c3e260b14c03b39aca90749f7d073e1379e81a07421892c74117b691294529d0ca36b539e7fce9a636c114a54c8d9069dab6c2664f5399e7641906a62668a2291c34519aa3110e784aa3110e76e66884831460a3d10df0908d46379883361ac9b72cc50da4b02aaecd3b381079730778a09a0bb8ac27c37420290721da8cd22464137ca29382454796a5c881143760c126050f457ea64cb1fc78d8b10c0671cb3cc304e078d0fe54933b2cb9010ff110f98008393082eb0b5148ebee1be0d8b677f38c76ebb245645eb41e112d648be83e693b220b44b345a878dd574466fbfd493c83885e592009b0fdab360211e4bbd714e1c2769c0eaa72f883411abcfe6da7d75c7f9418a88f082ee00f40efb4de1ee03c8ca19dc7f8bfc7f4df21b6e500f4864dec002525050c7e4865c1fa4f22706d59d260d98ecd8a9c97100342c27db4e140299ab0026647af696c353959db326d4e2122a7a6fda6d1d41d3609c4dcdf9dd59adc010ea3b0c18642095368d8fc97d0c3fe18539a28852f1f53ff12de810b7664b9c91d98d8fb1e128e5f4e30c5b2eb044eec9dae13b02c7b3a07a0aed5ca5e494d5e335fbe946c5d06d675d7fe6deb6ddb5e621e8ed6de63b7d5fef6b0edbdb65f20acbfd7db3774f0e80102ebef3f2afaa183d0ac03d16c839d873133bd143896e074955b5987c4ce00c75f95581b6577ed6161c80ab4000625ae108510125e395c2af101e80da575b5004adb8c899d0d76965d2d606225c81e461a7031d46baa0137f8810bdce083852bbc008a09d0bcd7800c3a20021260e0441cb22b442dd069d96b26a3c14e217e9b667b57964945482b41e6c89cb3005c0a2e469ed1324c46af432837a18248ca5805ae7bd5449a4861d9cf72132932b0fd94bbddb3d1a6e626525a2a969b48710991e030840932a803152948831239608215e200850c2cc1083c6879a1880a2a92e470c500c796e5159ac4c1055b141b4c418a24a880e2c166413538031298a4400648b61005db410e5b948c8a2b3051833284a10d5096c85e709862368103176c66b9091c82b4761b40b18027c610851c90c0032f0c21a76dc0fd2073580c54f8a10191265268c24f4e56051e7450cdd95f60d1c40d59e880a829d5062184b10ab69f9e2c3789f2044dd3346d6693c5729328487cb2c950c8800d4d24610518fce0085f810a1e60544c61526124a362c976b2dc840a24b6b3dc840a9e0884199d354cb65e96bdb823084d30e101169c34014346852837f09a50c1d345a981ccb88969da37cbd9a426514e7062c3134b2d3761c34bb694f02da16236199ac2e6596e3294054bb22cc50d8ad026654872645a6e520623d47293322c91be020211ccb2cb070f4d2cc1821b5a2e1f3a044dcb2e22bcec0528bb88f063fda5e529088105ccfce2544801851344cf721332c8c17266b949195060638c51ca266558425a6e52861dcb1eaa72c4e8ce061212124df6cff9901f67e92f076cf6c366418cb0991004d0cf5ea5a6eae90cd43fea1df7a5ca34dc68a2e5408c046635eb1b6c37300896c3045554514590c53a8f69094e1acf651138a6a1a427cdb7991c45ea1249757ec44892f4bdfcd8fb984a3bc2deb11a39fab3d7b1071cc7943d94a66c66dce3be9094f5e9523663dfbb70d6710537375a36ca48fccd3348ef75a1964dece5a655faf1392d665996695486e34655c32ca972588bf6dd735ce7759ea3bd4eda518dd6abd2abd243ae7a1f35cdc56acf3c63859aa8065eee3b90aad05165c1021dbdca6b9e8ef6a3d19762569f67b257a959650f35ee3fefb9aeeb3c92f7246feb46b49a5248a05a8651abfdcc5ebea94e8fc97eb4bd67529376fbcccbb1659f695e9665d8675826ad49c5473620f2eca394534ac732af318af5e8efd2a7145d6039bbbb0fa95e7387f80fd15a0a81ebe717ee02949b88c147b3dc840c63b0777a3ca364b90919b260d9c396b60919d0609972ab80729331fc905cbc4695233ef6d39ff43149c6aac221a58d963d1c7465e5e3e8550d065aadac7c27020813ad5636888a1d7d0cd2b117c4105612012401d67906fb2099cd88c83e48663360e5e3f8a8027161e5671548026cf30c06a4abb052068b1e03864081db6215c732cb5c6fcf86bffcf8513ee7a04a75ae63dd5cbc6693afca01deec4d2f5f67e5bdb2fde2b7ffe839f9d73f931f6797793a2b5ee9bb780d4973f9937ba931c4c56b36efb1aa5a799567afa63f55558ef9a69f6f02552b608d21b6f4aa1c2aefbdca7befa132955a9a3ee2671f87d8f99c7c07ef94af551aaf8604b31afef225286944d036820cf667fb4d3e9fe0f8e552ee84fa13aae268940fd4cfc741551ce9a1ec5cbc66fbc6a13d57aad8bf780df6f45dbc66eed8ed5fe5effc8e79e674aaa87a83d87cecf6a86f32861e9b3df63af4b1fabd8bf7f39b79e6a25efe8fdebd20b620b2c7b8af7bf11ab6fd9f57638825bdcb89fb5355d12f3d5bf66c60bff238aef2a6ba525539b257f9ec5540157d558ed2735f7a4e470a3a67875900177b59aa31c492c0197d781c6847f5c6c39e1a182587814c04daad838367fa5ffce41815ba89187a9809495c5ac07e60e1d0989a7f03e588f5ef26471a7b5384de996a09ce2864309b706f4223d65f26b4618982728e29890c7298bb3b0cd48b5c16a788f507c2e5abb8b2f1bbbf651c5d81aa76c427e80f2917bd37484217fd618584a01e50a6e0a05586094179e233c69b304aeca51094f8e409189f708c116a300a41894f849e043179ad44a159511db221a62d0e7af244a8758301da4a5bc29f8b313611cfc498c391c7c6e727d166fbf871c76bb28f9127c678e3930d9c4531665b65ac9bb16ef489ac9e8c651d0b33d6ec994526cf2c02b39ecda75b653cddcac19dd9901d22728373930d5d6ab516db681f8128c9958ab19b5fd1155f720bf446d7166674613f9812acc53342a4a28df3674319d1909cfb5a9553f3b1fe9af6f277682fbf5fca9f200fc71821da8e96158199101972382703b327601614633c7b1263784626c4339e9345d74d74c59bece561ccc97a620c127a69ac6b2df799040858a80718a6587f0ed4a87464b2ea38036d9f23dc2c70b6cf9166f1479d289bd53ebdd3443c54426fb3c038b9478a0e4cec4dd939a9d0b7b41d70b4725c37041d425ee6ea3d45248ff4223cf2bdc7792a5683c8c933bbade3d436d06489c06c1069e7acb61b0d46f0421339fd640a0d117d9056314c0d46c841122cfeb85eb19569da11a044d1302cf64456f4893c98a69161093d435869142b5ac51d22e9062580c1091eed89d05050cc30ec0c45e849c2c2c0dba6eca9b7a2a7a40dbba10585095186f556855072d3d0e4356ca314e88d45d1796e1882418af597e1e939c2f3c2e34578eecf15e90b5c94da73a4cece0e8654d5e004698c899f8a31f19404c53e93cd84ff89623fb64fa6a0fcd94ff61a8d3da654a6e138113af2b16a35e6fb5ab59aa66973cea97590a6695aa6695af6ddcab22c6b1e4dd334229767dbb681b7c6250cc69f3bc114ddc0e88a4be24f6c459bd8d33faed8e3453d45ac227ff264f5c47fd87f5868c87b38c6bf27f6f4449b2e8a315e4433a2d410158a42692ac520cc105151147a7f2ecb322862b00632d81b7b9899b31ca0810cf67ad1f527d77f640fe88d3ffeb3c46bb2978c15459b58f4d323231ec69f9ff803f640551a187b222bc6c42441fb336fc9b2ac6b1b3b7ee6e9c897a68cdb3fe6996e70957241ec7a582f959a3303db08ab7bbaa733d2f375573d7008175b7c8bee61237847df2fb9c3381277650f8f015ef65f27374ab18d93a42cfb3454ea316cc5afa0dff225950fef33ce8b9f4af774923e92bd777b58c97dbfa7e2befb8c4422a938f00677ef792b91d4c5ecbd2e461965945176200b14f4bd6fafa34fa29fd5eb9d36af6ef4bddf40162c50cf0b82be047dd027813e545e76bd1ea812af59a894ac95b6492d99a21100000000e313000020100c074442d1784828d9f6ec1d14000c91ae4e7c5699c7499053c820630c8001000000000140100401000caf37e79be3edfdd6104017d7d66fee0f3b2619c0ff1e3cc263f0c6b155b950579e0bfda669ac320f708874c40e04102b036da75c6ca3e5e4c909eb3701c4d604fc4fa05b387eb311242710afcfa7e5fe5acb226df7267c41938722b83dc4a924db51882b3507f1580c5df0250145796e16b8c71138ff81e18f3704327abb59987f75c0d0d32636a950adabfcfd552056c0c26821e311690c050fcb317fce2836583d3aaba610d037e4eac9f5c6a4721c9a790079aea2bf188dbb817f987c6f47b1e5a5dd1dc46523bfa73b0ce23e712c212874b39dc51108ac4737e9e4b142c713142943fd55a3e2b07ee71af89aca368878310044f6bb1f54df60068c9ae41dbf82e1707a1ad38a76800ec624f196ed18938bc6719dbc9db9dc9efda99e9bb395b3e88824e4e15645ce52a0fece8e47b08e134ae6b08671ec4454d5892c7b24b954851448419360a2b7b731589fd79897087731e094ae91f310d301ebc32fa2efc39831891b883403ab59f0d94decc839bc4b2221eb4f1c79e824b3d4034b0369c6413823f50bf1c755b72572791389846d8c32498615dce6e895235829714d83746f3035f2bd485b660b5cc1a0465bc37339f74677cb9271f6141fec121a2081d8576e5f3e5720a8573aef23b8e02aefb39161b7883383dac441f814a70ff604b0d477d1c5207721a6c3165f7067e3dd6d13b592155bf14aafc4ca577a852ba6950fe4d9fd2886762384287bc6556f9a8815accc0aac604556b0222b588195accc4aac602556e4c9234fe4da1c997cdda9c4bbd3b75582e126c07b51900f0821d819d6745c3ec6f238411bc7e7bee6eb06277da04d4e94a70613471ab6168a5e91020b614f630665d26a46c88a6e1462b95668d487ca080f4bbada91ca24bb3c4c13553376deb7611b4214473d182e6932da34f14ce759110db7bdf80821d0001ee2f487e61159508ae7a5e796697d490a87c451985263438e71f7b31cda4906c4eeba5afbe682831ec8d98acde1605fcec4c338c7126281abc047a1b00a83b46bc89c30c81712bf73aa49d6bd2ad377892344ec74c626fec87fd6a0c6ff330f3464c491504c68921123d1aeeb2c917ff6645900c86222d14e896449d38d4ee8aa72ed29091288c5574920f9b1ae5802cf1b7f095a2d05633e57bb7bbc200e3acfbca2f89344bed445dcb891b8b84b9354f40017a9e37b3e9b29a8a74e32e6d00e234c5ceab6e9623b8267023f3d36ce6b9496b47cfec75d38a8ed2bd9bdff33fff43f44dc61463d8f0b401284b5668c1b0b22e0254f056e381a07f407cd570646a9a83a76a8636921eb11a4dab3a0a198e30208ac3113733a8ea209862088a5e1f1629acfe54e7279a65b4d8929d3697210a2d415f828612d44f6503ddbee19d3f49f47595c3cb9d4bc2917040ae5bd568ba1e584eac8fd7c4f7b122484f16466f1d1aaadc1e2cf606d5a11e7e8946a6a326fbe1209b82909aa2d8f524fe23dd7cbeb1d38f7f1c723228800a2456cf62f5911d5d31bf0f526c05b3068bfbefc75e7efab4a42c72d2268c2b2239f0acfaab603fa16235a5cc4bca60e0613d42c407f3532fbeaa9c975574f54970155184fb34bca4140a7df76749a080ef0e9b5a4771e72d88a8066c5d4276eddb77d632c114db96967b7dfe7f94329d96058b1d99bdb83377deb0f43479a12caa9cdb600074ee9e9a8d626e568060dd84c1dcae68c893e0f6174a513eb432e2fffcd10dc13c4f851b49ed16a6513b77ea127c59d4e35e1b15f0158ba2c1bc309fa065757c4ce48bbe2f6fced299a789fa7cae5c3183e05e4cda46e5d4e0f88bfdc59ac799bcb9dd59ab3bd9c59aff9dbcb99f59abfbd9c0effe060da81e403187ef451f8e6b3d276e9655e832e353a5699de9ab8e246f26f6f378abfbdbb51fe9db9b2f769f66dc85730f7239033523f3bc6a2ad37ef2725b7af6fbefa7df3a5c0ec51f5e41aa88049396298e40e04ba3b97fbb9b3099486a0b6e335844fc8cdf6f0880b10215b88650f37074def7b5fc18afdbc58546a506498fb86c86cbf19dfd0474c1db1024757cca076eeebd7559048bb501e5251dd54837e95a5d776a79569ec09953a2504840d24a4159b1de0c9adc7813773618a9c3bebdce88224779693e55d783c75d68563a1731883d0757a2aa9780073f3ba4bf403487da3e8d18b556526e5765c4397aa48a93a2532f4aca5a1296a73d0f09847199a59308072b24ed3176ec43c21d4badace3a42e87e0976732e73c1c6074a56cfca68997676fb6b65165568392bf07d94b36ff026242eee743ccc82012146733b191a105c22b15cb19d74dd1fb640027ad5fcd6448919b66836f0ca255abd807bb94d96402dcca633060da20e9bd5cea0f358790a580279b5be37eb605fee790faa68b27c31d4e899cf49a964a39293dc902e0fca333b0a99134302c9779c7280a9e8b6a13aa98eecd77785653ecdcc0553d58808c2c7dc05c607b031ca56746670ca37a6f5da3ae88f73ccf0f5a10f611f283363c72cd5a5f4419522e83e5a299545c4f97398580121df04cb83998eeea86cfa79df2fcd1ba7bfe52147b0c564bab67d1e405f182e85be7f90f84105503e008e6c3367e5979406f11cfb76e45973a79163839a5a509e8ea6a182e837a757131bab47eaf998c63d2e4b789c61a1ee56c0b01f8680100f25f2a2d39a26db14d03855ca475a293d7e35eef7633b9c396382f362c6bf43f3eb630c5482a2cfa83b4f7f68c6382568dff134a365a6bcff38a7f3f7ecae6df397440023c2a4ecc9b651d91b8b082b69bd492aae22bf6ca9ca6c5f225697640526c2c500073b686ec9ee9b42bd023319d83d1169dbdf32d319113dec2e022a9851b278253311e609067b4c042b4587a8659102d480cd56a6305bf7040e1aca20824924b6f9a8b2f716be866f0dd292a2447d6c4f2dd1f544df25d4ed94acae0a51d88161150a4aeac9e612f10fc5cdd737391d1e47339d98a3f4c7461f1f5e4cfeca977166abc006492771c9a25f92b4fa269297b686af6499c42960b80d38a0a881973e6a23099be6663ec79e9d6647cccfcc5997e93f1883eceb1a66dd2b890bf361e4b94a2f62d9af91d8566580343e6e541101d1b7d862bc18761b13e12d9bd426aca256e0b70de52cdfa2c68ad8217e4fad277f3688f465582dc32f90545fab350ac1109883d322eed505854bc2e6e6b941f9c77e3a22270226e6725293958514bc93586231c55ea575ee0fd95bb3c204e77d2fc2cf4652dde3e425accbedac3db6776bd9fa4ef7ec5f51ff7428a0aee19cc36db648700ff13f5668f52bf960159fa48fd004279cd49b9f12afebd5f3822ea3b3f8c8f497e9c487cad6a2d004776dac5a435d4dd0e559555a64b610d2a3361dfec349ac098c1e7ee67a0d84e802ccaf117c8ca61c96b6fac2ebfbf9eba4f76b1fee7ee9f07ea54e861da8774482050c08e543260c7089c346688f21c88df0dba09e5e18fc8b669c213635a38b85103a8cc70b5c93fad6e27417d46a6ee856b7754b74ebbba99f62438d01742c0641ecafa6b21db714e36fb07a440356fa9520a416d229568f3819ad1e9f9f5ee6ed2cdbab65a7077c30a4f9b1cb2bca03eba164bba4886554bca4310ba4aa58b6d979386f89d6a6cbb4cd05f78778465229b81683b4d23b27b6ad6928bee41af94b33d725d5ac29d7fd125ec49e649beec055972728349bfaf7b5cf408b1884b7f0fd0335260a73a4944364f623e238b294d55b5a17767dd21d66fafea669f562f8148bfaa8e68f6d479d7da37575a45acc6858467e61bbb071eb43655982cb6558dca7afb0c161c076709214f4314800b7c3494427bac438edd9d2a09d92c7aca70b93f9b8622bd4c3a073dd4bad27bd455e5d8588d7308f609c739cddbe231a0ae5e2d2b9cd4e654fc8cacffb59de8bbcdea14b380a8e3c77d6503f2dc29ddcc49e495cc4ddbd5c9e8b94606876ade8c3082be71523a44e0552eb40246c80fea70846e054a1ef37bb8ebe68745ff7d3f89ca6af45d7b5799468add8ff780739f821fe2f0c99bd621fc0d26783a11c479312af397ffcc793b56c80df6ddc3b30e986e65c5db1c848842ec7376c38d5f9d46ba28e9763c6f7e7095677ebbd0652b7b97e588fcc02a15a280c1c354f8e7676e698aabe385a83ac9cb69dd9cfec4a322b007443be9501d3824faadab9174baf2acd4ee38ef5e4b669999b5d74b4a22a205571d30f06078ec6b85f9ddaa7ab275e344e6318c62a9b7a695746579546023cb713c47843af55475003b7773980550ba074db86ff7b16f89f9f7f8f07a93bedfcaaff98e2c9a4ee01a8b2a7a1cf614140787f20c1e68b1f101035d612e77e0db3991ceb7ae5fd8a471b6c4e915379579453bae9e4fe1302064fa36f3241e760e4ef2c5e073fe8eaf88b31d95e29fe7e084793920f850203718ca343e578a058d5e07c12ca26300d18dcd3a12c410eee1d1e1a727aa62cf1b5f71ce5e1ccbda4a1c06489ada2ebe748eff84de204fa7da2d6882aa43bd4b91f69d4c0b7ba610657c1aeff48e4825d6661d8dd597076e676e442bb53a54f4013446b56fda8cbf432017ea97624ba1180fd1874682de08f6147c6730c8a333d88983f620ef9044347190a13145deb860119e6113f6a8530e828327f87318262a889d348b6346adeed10f2028a4306159fd6d6589d10de724276480554814ece9d7249ebb97ce1f6d148186fc39e243c8c01067146b7f728d0a87d63257ef59ba6511ee033efeda223365b3183be6398a4f2dd6f2003062dc9909ac9916c51ffea1d7fa58c6bab1a5eae73115fce7c61569d1294161294e6567aa77f152f2048dd1fded3720cc7610b3d3cd150a83d901f0e488f1868f8e2f0f623103acb9724d8cfddd5ef3e96372f53fe2e1130421427dda5b9fcf614fb7d394a3ee483f4c7f139aa46e68bd122999461a12b84674c033d5a8ccf5eb990bcfe6920a6abced944b184b8326f1a7dd54f34e140ef3f8aa4f65e9aac2e2bc8b21da80aa09482fb9621a603632efb102027ed7afc96c4ab7c621c17e77488affe00333cb73986b10fd43f5a78588760cdf4355f4d00407ad2763e040dd83be55770c71867b57c56b302606a4a2a075a8861751af131b4c078717557d06deda9569dd0c91e1289c44fd27453519e4462dd77e2def20cce97b1c94026b021ed2f9373069f11b6df72f32de0bb34cc74c08a578120a02b04748552564142e70a5506118ba025da9a873fdf7f02891cc2fdbf0e9dd4bc562da8ebbde7a899d95f781c6f1f4f01f69fea8c699020fee2ad3b41fab55ff4511321c3d22705daf4da74c3151b926202f96d74bb35cef29fbde0bca4631d222dd156943816c179fe363c55ae9f43a85600ed4b54721060abe4980d82bfed33ee5c01f2a7cb3c016a3412846804026159fd1720f49f0f43cdce30e84f282313858e052a61bb44bbe5852170756af98390ee8bf86b776521db47d2b81ea0a00f3d33a89039399bfe1a4da03807189ed8d305731b455ae4406b530f1858267c5c81e19702e83664b73347ffb293bc0151052155da24432e185b26de5f1153d20fd7d8dd6c96fde8fd86e35693a30e3a73942626f7a4f52b1b350619d19af09c4f52321938e6dfb22e30e7216c248b3990e2c4427928fd17b1a4ed14b85ab6255098fa9129ee75553e5d19e98d430749b89ea958f8aab49815e5edc6d841f47d977ab49fd8b91b705814847594fa0bf4fe06f17e55c676b4dbf4cc8d90a415a7cffba53994163b19ac11aeba81a5f4cd6c735a7696456f1bcee683ea9ee5c8d5dbbe8ff28d1ad926817940e1cbc4155393bd06a0cfeea56215c561834654211a3f086c6dbc30c4e248d2822e055e14d947e2fea44089f07636c6cd2ab8147f8b932b9e649eb2fb5202159579de778b4a4191f9110a1cddca435856a4bed71bb51d4e43b8cb0c53ee37abc5d340642716ababa14887e548234f7867517a0b39c4876abc5ef92cea85d2f233037e9a2ada5564e8ae91dbe951a0041626850b2f9c37b0e55454e142b6b791b926426da4d196df26ffb214bb2cfb075d2490a9121246b7a87ab4c87cca24a192513b5910022bdbbfaf20e838ee170fe2077a90504b8b5d36cfcefb3c2ebbf25b14fce526e33ac11a22f80e868ce1750b62fbe1ad5931208af95192877b63e497086a4d84e9f6a8d1cc9fd5a4ec0a2136cb39f1bc831434e18788a3709ec5517c8e8c79294475ce7805715c977037971c77665448174206c73693710ce26e829e8528716f0f947d20fc98dc34fb84caae8984d63a94fc2f543a1a5b50be78e1fafdc1d3bddd8883958bb66e6b6ddfda0aadb672c946102cefa946211f916a4899fe357aaf2290c243e1385c250122a1d918aed02e2617c7e79956da7b13d949b1ec925519603001af19488d11cdf6fb2e4f240533146302447d8168c2ad44e19da1d202e219a7906e74dd301a9b71d496e1f0fde7466242d41ff4589e7d0e27725e84563b3ebae79d7e5e6671614241154a226a8a0505809d08bc28fdd335492b1bb4d0fccbe7cdf7e1574d5c604f5d13e9c1ba52c64ae79bba6be0a543762b067288afa93d33230e10bd43132ec0483935380aaa700548cc24da2b5fa33842c94a51abe14ec16dd14d238647b38cb997d35e69bcd3db03acd4eb2a19a1980933715c474cd379deaf3fb416e2e44db3857d4da055118753913de5f5216eb6f40a7249aae276ebdfb62bc27bed21510675def24f435fc6423c24eab58336ba1638bd2b1a47119529777890bf85718acad9187e639457436bbae69ace31e50067d2e28840723ebdc1b2f7b38f3eed3d16b224c0d4a793c94df7307bdb1d0adc3f43258867c4d16d8ac02d27a6812894c4d8a354f89d482226c458d8739d655dcba19925e7732a5c00d6798025a40b928158098e3abb907619f77effa54a88b9f7b565208552a3a6158c5e7e9165465790f0d4376bbe35b7fd2c9b21ed6e3bfb2b24d4c8fc1c8ef0178724dd9ef771450490e7cb86ca5c026e430166daf316348ab1cf9da21e4c29a08ba084d48947f40c7ca6e7deac41ee194f799b0eb490bc9ead1fd2f9cbe4ce16342dc2c65698e856d817a963d6a34fa29896b8055481172b38f369a93dc5796a0988fda5d0d7a0969be56af40530a6dfa2112aa2b90bd465041465a5d7a59bfd722e5cc04e28483d64c09f6ef488b20683b32bb5eaba24060222912df231a7ab8d0c8f23d8326e565c30371716145aeef47629699d9220219d084f35b7db10c5f8347dffcb3e8d0482d1a9112bb8058dd1406788244980bcae304ae4ca84e89ddc8c6e1272be84078190f5c23647b1d615377e26700dcb3645c8fc4a30ea73cbadf26c4fe420e44e43a96c97204b5cedbcc1ee8589a434a1b259646d620997937ee8355e116df6987a06a838888e4bb278500b53b0e45acd55304c012f97d9cba896d02bc0dc5912a1b039615b7d5f869041466d63d7a8f50c658407d81854cd57e8584a651041d0637c98209885ff5f115b72414a7d8e7ac7128e11f7c390ed77dd110e5929641677f98ffde19710a8a6096eff61436079af8181068161caea8ff7327d39f94336c3f0e347f43779d14d019aadd811f1852ba41122f20ba54048d6c11742ea4206cb3792a3bc57e3e1136f784f79e65bc3aab4c2a0194285f914e81dec356eb867150534794bb3c98f8a11b72702ddb6dce4906c07ca37f2b7671ba4857aaaf75b81bc15d258e2c6679d24f44d3459dd7f1ba05bb007f6a243d98e740a2eac3c3136d71d18c29f78d5095f32c41e146874b432a5ac602bece6c4446aac0f64c2a057528e865334ac32360628b0a965416bdf1285c51933d7b8570366b9e1d2aeccec9ecc2b01bef86bb96ef23dea5f574c1b4d4358fda8c5a229d4adf6a5f881b52802af9f593b3ed03a6b9734fee2ca047ad7630f394153107af6446244e2365a4c86227404ca60bc61e9b5a448531668c375c729f09ac6293cca052d1bd6ba408e2a4368c77ceb571f7c78eabeb102b6a6663eb35949d3d8c4b1fe6c4692d98add74238234a970d360f393f8f872736f1f254998ece2f251b82e02d1120cd9baf87428d1ad232c8b9d326c0b0d7e2b20fe467336b7d3702299e3c721e8df34a2ebcd543fd126ca1bcb8a2b037954e98959374eceb08b6c642f6fee46a3996ceeef577f66d85a5355830c3a364107dc000816669f708f74f086774189eda5fe097cbb2902274f3be794cb03a6fd895e4adbcbf1a2853816ff130d84555a23417f700fc1faf0d84e2feba3040146b436df871d69900a245c851cc9347f7d236a0c9ce0862aa7c7c1153ab3d879e00ceba3ca4426ea15d581f72d632ac65f12b68f91081008123a4f78a7b9389dd62ee03570ec774828b9089d151b18ca4da8f89654d023c4901993e7d4843b4cc503c47e4347a3217144f39e4e09f45fe590d6e36590cde9e14cee8ffd39865c48b24eb6f8b47a3c3c98bb4c8636afba3830a1d92225e8035456262085269e9f8a7b1a507c4476a7b121bd75cda79c09aea885dcff0470a8f60cca3fa9727c10cbd0f55f785ed42dfa101d24d0f3f2096b3d94921aff8bef13f28620a371d2da1f25f97d0be3259f6f5f2d73ee944ccaf5bd81673b144ddc98b11a99e7c689229d601c9124cd97176b60d35cecc1b64308cc29454fcd124d8fcfa484c22ae7837737ab0fc0f6236282598f83892b8fc3324914ae639428ce17152526e6357a076951d257a16920c96097be0ffa78e8355c62b722f8ee7f4b1b8cc38e4dc562a9e1e3c7ab863d5e8ce45acf66a36ef4bfe3ed4704d27ae569e4244050730a165fd4b9394d55581e832bc74972640af9c22020f580a0e36b9b50d31fceab7e170ba078c338aefbfabd1d4ae1de011aaa67898bd33da47de1d8b84bd3547ca8fefe9860ac82f620331b3ff285fecb099ad447580ff4a32616927f927cf18c923d2efc48729d5596f9a8911b5ab141c7f8beb9f868956c1479306061ab91399ed5379ce1ea075b639fd732dcf78f879d3c4f580b411f7953ea1d804a322f1040ecef9c6cbc84e17d952995d4841419c85d35b20ef605d13b6a01d25d291bf4f96e84f2b23fa018ed08681d794e1700a9d6a32ee0b7188ac5e97c34fa7f62be37a0c4006cb3bfb6190c0110cf164a37629eac3b82693d02b3f597c1036f3b272325b8a2d83debacbb0185d6f91aac90b5b07763e486d539d8d6ce81469c36a5423abb6c459c90bb5d42449e52b0763d4f0daed8b7cf17a809366f81977b13991f0c0671e3cbc419345451088a17e9d2da6e8c6eccfd76a59a9b29e8edc85aea58d5cb4fcd3efdf2767c41dff9ff9c9c799961aeec9feb55ffff4354ace6bbfffd8cf7905e707d4d1dbddad71188f44a3c6f4e56b6014c08a8608e44b64e5bad8c6782da23a30c1214cda080d4e03e5d03666310b21b2d0f04fa0448b68d6fdd666c59d1d25c54cbfc2f75731556f1b024e1d3d1a9d205149922ff4990ab0fe9bf23a796c3031b44697ea28e9744b17747a26540043cb81b5977ba6da4d41cb8b34da1bde04014ecde8f1d60a119df84c4e8e6d07ce0d5f4fc0e1b2a604b29dbcf8e936c11ddaf7882d48cc202895142ad1f2a9596d22ead1f8b2fc99d30e53dac83c7a79a2aaa4866d7438263065e7c2388c8c5befcc3e1d38a52812c12b1679dc52a968628e4975a482224c40c5bb290fdbdf7fa840aab954bcd029950cc0e686354a9c6431f652b071de6e82d2df44e7af2473fdc27a5f42171b3c74c12caaef3c0eed357a27d65cc7f1d25948a599539ea30bef40241152ad10877754bbd1240ce909d304e93a47e9d935b8756a5c533c6543c76f0cc64cfd004461bd3dc2ac4d293e6a341e36014d825d8c36ae8de1337f6bccfd05e0406c2300631436c5c660d3041b098be4b0f615acd7546c30b54695735642aaa433e1bdb86611e100b8a42a2155b11612958df49172b2dc41adc5f1e05b9cacc5661cdbffc9591d4591ec8e5096c2c682d7088ed5f70a6556cfb1c363ce2bc7726310d9ef272c360cf2d1597e5278639ebe429301f0e5df667a8e0217346da8087bdf4941cc4730c0a8f1950036c87a9b970324d58bc79a06168b7c25dafa679ee3b68c3d03faaeb7bc7c7af528cdf5d8edf6bac3fa0caf6bf32c625664b89a633a8afd18d59412554d25a7623699e7857b6dd207652fd33ffd3f8c21c58bf2ae05e856bf52bcfa5e0b7e450e4ead221f48b01b4fce8de6337675a79b3e1a51d18b36c9cf350750e63a6dc4a001e990d9acbd9c77204e2c337e695ac229ee53ed80b4e12cfcd3ebb7e428e403ec6a56a859efb6d2a0dc7cc0b6b695fa31f6f8ee757906d4dd785877805192bed3d496c608e2670a123e9ac42b4d219b918042acd58cb24bb5b54f902963fa81c07006546397efa1674b3bb7a4ec1145a1b57cc8d488e23677774b8c45b6c7b40fba4ac551ab74b4e833b7a63669967c8a838e7ce4877f84faa16febdacf6ee9d3bdc6d780366eaa01b4fb5e6ec3cabd866ebe01d1f3a5f4da0e6ec2f6d46f3797d3ac53a7bbd158753314d41ff86bdde8a3005b02938f5a65606c5d4c613c3f49458337b2bf2589a86797403a722c452a9db91e88b6638cabff21d95a3386ebad35998cda583fdface86002514a0e588443e3383cb486d3ae9189bf3ddf87c4ebe37c700739f5a9bef0cb1a6739a086f674497a6e82dd0171fa79f81566afedfadfc3fbcceee625d870f1b01076ace36490af2af98a16630b8c4a28fb0c909902c70df6856be2dd34876ea2c0f11084dd0062ab8d8235649bffe5e9e596ce6b9d1d532165159cd9920d12c074b28c502628f9eedf695540fc1a892cac46aaedbfb3fef36e3f375bb91e8bdcc13d0cb4cd1a35312258c7e9ce20c615ff15d73d6a110fcba65fe4e922396f93c79ceade77bf29c5bcdffc9396c9ddf9373d832bf27ceb9c5fc4f9f63ebf93c39872df3797a8e5bcfffd439b5d541ca043419e4eea6696aa48499a2625a20759f7c16aa9bc508f655b1e6ec0ca46f6a2c626ccd1969303749e49f075ac15c081da58e82f6432108e1d4b0de621b256ce388606bce8e4c0cb3c2a4a09520537831de860b2d74e95a72bc5f5e7376511a08a23df948162be23d6e651323537366579a9300267ce288e09524b5e3cc5ce759b88f33b6246a7552dad49c891bd829ed386fc3da586e6c1cd252d124057bd327736746b0740d97509f6c16c6f34ecf91547a846a74d073f821228c21cd89bc8715ac12fb73dc4fc2d618f6ec2520e2d5400690f1595bd5afe49594267e4a31dcbdda358ec3beee7cfe827338b347361bb48665c914632162757210dee03b9669c074377f8e7328cb2cdd1e1fb1abd7bedd6534f2e5a1efbf78a46067edd39bd9dfbe158a941d0c65e2c7e9640faeaf0e83219e884892117a96da1ab15765b55207cc25912150e77bb00e443a1a1d96c0c031f8ebdf8fec7e3150605c6233fa29a66d5506d80f0db9195834dfa3bdeaec88cb72fd2b6853174a9c710f3c6478269e1195f8f7e846d373de987664a034b6b6e5756dcff223e601d227377fc0c936fa39361529cdf691fc696e3d890c2053325798a87874be20faa38651d88810bf753c55753b498bef68b16a57fef67996638165f433ab99da25a06baa0f1e5c886d1a1d1a671e2441501ab4b0828f47f23237202a789e982ace87cabb24a090f40ef38b5bf030fadc0f866130983ef900b666d69264683db865a7ac4a103ab0a36cdad20dfe2b8e419ed2082167344f3cf165d1e3710b2a7b43ea2ec1f9a05f07a81aa8e17f548c14133318060a5f8d1f0118f62a2cb6a862665d8c59f87fa0e2e6b80c961d9e86f314f6e4062f0dc6dd9be3629cc98cbf5416d004e0889de9315441e77aaf958ecacce3667e2cb2da42c365ee94c8d2ccdc2b2c7195e428cc1e409b4577724ce195600e4ca43e689783faa13d85381b9518531e214b3eb2ad9819bd8d106f48033fa98196f86b27666850be0d9a269a1ccd84c47a3415786d06475060850772f7d83c8bc187bc3cd00d98acbf202976f88cb6a20fbd43200a09ec896dad028032fe825983922201f80278df0e21be3204a6dca8bcbfbb8c329d10443bb28619b32b7e3247a40c12a30a36cdf12337add02b039c578ecb8c46a8998a1490b38139b8e0d132c98d1909765146d0226013c6fde440777cf09e46d0e1cdf5de1a41f84a6493294d27b3201152971f714fe5155ccc117c687cc78795260916ea82c558d4c7039ca738455da99a7fafab5fc486704e5703ac4c20deaa32a6b24f3d36b65253f208beae779d44b05dc68351172e79160da7c1e9ec33b02174a3ff32a5c1d2a0bdf46033dafe16f09a7f60c38a5765af73fd335cb773c568877a86c7e45e4b617caaa0581c91d68e9c88e6a2316e24d9f4b8a7c2e3f1690a96d7213ac424741873644fe8d21610f4c2b32a5d1fc8c3a46e2c6ff260745baf23fff14f203289a8a5efcf69b25a9f256e8a3c7d62fe981a0afa1f44bca7f41022eb75344bc274e49111a4fe1108837ce41a876cc64fbcc89737793897299575cc20f33f697f86701767cd8bd86aa62fc41e39fccf2e02fea329017a72051c7b84bb4ca7312078230a3f60a24fb097cd3648fc782722b04142097a10a248c04e23882b9b54d185f86e308e445f9b6a78c877ef2cc2cd4fc19b43ea640701606a2980f91e15eb347d7de3850eabafdf8fb43cd1d08136230192197dac655e4dff0af79276c3d99423a31d40a2aa20d6b23fbfe0cd25682e49be6eebbeb2b3a22c331e34162d63af3d66e7ae6e3569153816f1ad60c8b96fbff2da105e043e1a56e56779ab4e602c2db3b91865205fbc14683ee5534825d22890be346a1a07f183169114f31e16e33fef1626c897881a659056a6e6c14bd956670a7030c8d259348e722c158542aa324e037dd47a414517a475a5a62bd3b4c631451a7c4031587e733684e93b43f734de44b34d437ac1b3a7638ee691d560fd55f35fed1f694c4fcaf31e27f5cc0a5082935a59c0a1cf5d28c34db6b36baf5cf37e89a0c4c7796450405e432f5da34102da980e24aa00b78cd17942ddc5a979abf2d60a8ab1ff724aa99831d0fc520e89a3e612c8784e49810ab65eb77ac164910df2c01143f5576d09a3ab56f3138ccaa6815417e34ada20aa1f13ebce0411e45ad815c15cf49a50dea0d6a5759219454e09229561d96e45599197172e071683c5abed18298367579aba57d4175bcc473af12d378dfc8ac24dc13dc9b4f023b5a0b0a8e9a30a71ba9daf2ccd89610312798026559e7a181f01097f0049c67745934543c08e1b668c2e0049ead3a500ba9eb008ff76112805ce527a1164411ad686965fa77d2d99e0d8ecc6cc40d5db2e8ef1a44e049f16a392f0cbc0bcd0564e99c0802ccc81d3255148f1180e3380f2379e8c9179bb6e7c1c40f5d6d81d196f800f1b970301b52faa78519f1511b05e27118b1cd7a0a376189024010eacc3e806c29913bd8e9452cf16347960d356fa40b5b7afd46dc89dd46afb81fc5bb28ebc0545ffa7d4aa1a8d44d8368c8f93fa0f866888a03c251e00b92c348d5910b7e095b5b23bc391a436fc71b5f5d92a0860b4eded0813b5f5989a01a57805f0aee10484b570ebb42320a5258e58544845ba96b61aa804260b490aeab72aa07b64a0ce23d7a10517e06b066f30834b5ef5182df31d5636ed15ab1bd72ccdcbb33decbbf0b270a4be2588070daa74ee29ff3a45999b1c04f90b1eb78418aeb4e02f82a8bbeb9f3364bbb0db851836074d3967803688ca7ec9dc5109d7296c744016d40a9f69f671ddcd26c7c1b6d8f4ac20b8a1f2f22a5fd83d5668b92f665b8eeaf87eed9df8689541d9b3f573012965c24a903bf65ce5bcd4f8070944baad1cb132454f98e13bd78d2f04580f61159d53536e6a3b83b975fbcd30236f0351783f47394c4237393c08dcaf58e2f4a99cb65c7ac8cae86693979e1f366ba713d617f51f448a9ec84da8d38377ca2cf01b9b1e4b2fbb52182452014687db6d2fc2fa5df4a8f4627acef6b6397485ba0caa56ae76e157b9c4f788bf7e60dc20d495b9040ae6ab5e7d78117840b06bf580ed302d9454c17d3e31f624320bcd6e0e5fc189f3560a455aefa5a7b56f44fa326fb4cf27d40fed177fa7d45fcc8d4d3412b4e2f3094945a299826a91b81d90b66e161788f1a5fba2e9f369a3ea98c8847ddf9353542424365b0c5ec5e743ae6d1980d6dcf2656e7e7f3ab40351465dadf5050a8eed3c1e4be3ed125bab4ebfda5e34ddd2220b8e8c7000500bd4f338c70f565a44b63b3201a62bca9008abf65b005855dcf2a0424aed57dab2c343497740c6f1d3f28577193776942cdae3d4c92f54c4c88c7c51b1865a9875f19b5bc0394eb61b20f07a8f8b83518958c6c9bf1971e5ea1a3b7cd25d89bd4d025dc2504440a14a944461c8019fccdeea2fc007ea7f3d3d399d632c98ee8b7a9a15786e0a1322beb415280e051b7f7c0b3d742438315e47dca325747ba1e506293b536effb375e423153c21827453f3eec0b8fe7b5bcabcce4719868935fe3ad807ccd0353f8c6ced4f310520283c479fbdb36aa59e5224b105fc335d2fb7f0afee63757e2ed5ab7b9a400f20d8344efd9bd131073999ef7940cd70ad3a433ffe016e10fc3106162114b33e01f57cd50b4b5899b84e021efe6f9dbb23ec770b7bf841aecde351ae694fa82861e1ff12725bb8d5b25bddcef143f53cf65bc12d90594ea2b9dec8d1df5e595c548f5fcbd51bd56d5c17c705dbef341bdedde05fb993829094d495434a13fe3b2e0ac1b2656ae2d6884198fdc6e3706bb165bb2de2f2123498c75826c15016ca12cbf8fa43b8e7212930fb71e6766227c8b02fe61d34ccbab1c0196efbf9d5fe133fbfce4fa81e51bade4ab9d526a8f06a30ff57bd89ab5758cf78ea8208040224a5eb9bea9bab294f33053ea9f1c38eedbc796aad15237f2a57c4719e43de0f6977ff1e02bc7d43397ce8d680436e2c54bd79096d71873355fcf82acac8e2fad4caa011a67e23859f9a90831524289f772dc55b60e7266420b0e5c60dc4fa3c81816468471b9bcd0ca111d60902815ca2714c3c0660aa372dc6be00c7f6baca9a6cd82505c311cefb247ebb616e151aab403d20693de28dfac51562513f7287863276d903ce8f87fae119b8a3aaea32c4eb52577ace6d109b3c1bc3662c8699f6465b29228720faddeb1293222f3a61d2116d1910e0e1a509373f7781c062773d2547f10936215b8dd907e5a077f70d838b2e8fc3719ce2d56896f2cd2e7863a2266ae928ae579798587e592e27c62ef2b1e69457ba413a6e2618ac01bded21357de117c87a9324786c7bc1e87f81956d2a1cdb711667cc2e0667163b2cc92b1d3b27670bb33a26b37df31110abc44640c4cfa5a08da0e4fdff16bc1a10da6abd36ba0e5592b44af31e5d2250fd766530165987e5f7074bcb0036e6e117126e7dfd15ac2c6f3df275284ce248e0bec6f926111266b27d21c90d05fb5a39e85a5f17db0449c19dca825d0183936afc5856fe7049a949a293d915d31f548ee56f1e4397b29aab9a4393d1dc97cb78c6db3df1dcc77e76d771800ae87a70aabdf8a517a1413567e47ebf3f4d24b5bcbe0f169b5bf51b72f77c9df6663739227adddadac12bc22324094037c5e20e944a46ee214f428b941c5ff45627d4f20545bddd04a37d84cc188b8db70d4905d6686f7602226b4ea308945c209efff86e4520b731a0ad7aa9643be463971578b7e8c7fba26aec26ee0722a5336a91c862300bc56efb03521cfa8192d3a80b5ff68d68df6aa7b5a7a12dc3ba513453197aab4b6f3b39368a3c648f7fed203629720219a43f17d4687151d2a1d9a3d6149a0044cfc639e1f9672a9a1974ccec2864735b1070808923681ecbb7b55a274c309db2d47fcce8ead8b5de281702bcde2decb732b03582fa04a853335727b2154e4135519b2032a1b2f59517069bd4c78a6171be2a01ea715fa34d149e3e6eba5d1fa2d758e66b3eb1be6779f8b1b58d41d7a4fa159df1ddcb048197f6514891025b5ead44e9967389d3e7a8744a2ffeb433c89945f79d78145ae410b447554f966e4cf859ed29f8541991d00be530e5dc767e2062933f89ec52e3c1cee770d208a961f9f5859312f32c897e85d443a881d4760691a319b8419d657aca9448d0378b9b50851d035f8a4a6f765d0bbb35dbb9c85ec07b776261abe34c986c45ae8130028cf92577ed5b23f4a13fb54c8f4e7b3c432bec52137e89c3c62642fb4caeb42affe42b654885d506767b052f908999273d042e56c0a238c4b82b5cbec1ff6da522a9b0f17d565524b7b85ddaf8665cb3cb653d840c7a8eb5b3b3ba4e5e3568cabbf35d433a225971977b82bd061d0e6d101de66b5d3a0290bab961b191cbc48880781008cb1e248155d868e6e56b6f4f4e4da316bfebf8e2857a32365a3c4b6ad38d8dc669ba75ee5fdfa566806518d97483f580c9abdf613eb4c68ee96858eff655b9b063745a83599a55481fcf4aa3f62ee58d83966b7b1f98b3e4d52d6eeb967797568341634f8789f236679b3b90620e7663421a38738da19e4299438a47a9413d4a55808e8d5f62e61737541cb6b74726b2566a8b60cde91b6d46e07d7dcb2d255531b4e14868124fe4508cc91f5c788911172c06742c6a7c49cd9bfeac187c38bbb7e273462c6d8d337de5e28ff4a3d68fafbdff36c953a80f4e038182deaae4090104711d153807c55485c031f52fe732c2a88b9d8cb95a1fd0278342938c4df2af149068f5ccc0aa3b9d05003d0b65e46d73907c14fd353116384a963a5986c58b12a1f1d7790e1c136c9a0ca24f4fadcd08b3cf966004af350f498aa377264c6016151d31d4ff610b48d65b4b87f5fd2a2f04b3d9c761eba5224a5050f6de7e5e4c1ca6abc52f26f84e74af87385c54913a4ee88adb3cef6db214919b078250e10b1cffe44f4a41e102f4fe0f33030b8c78ce3154eea4055393b074fa4405272723086030c66b85c05e0d346c3f6f2e8de191bf97c56740d06cd7986282eda1d5e07ee8ec28a46e1c2b69d11604e90bc9573b345ad325c8e49acfd13b3239882aa6ce56da6ea64446d5fb83fb84a49a4be566ea2c66a6f86412ca00c804cc4ceb26aebfee31cdfe2678ebc0f4bd1b03f07c6bfe599661c0f8f194742fdc14eeda5b7365f66761d6797109f52fa622d5772cc0f034f47a1edc6c5795b2568c162907a4da88768fd5735c648e3cff89b6d2bc77e4398549893acdd667cfa14b62b620d32946de938e2022a3d450fef1ed61e0b8203d40f9af74a00fb395bf097f924c66f42ab65e723ca949b95080cd9b9995d97de630d185e27f548ee438b47e42090beb092595fbeb1f8760d34e92a9a1adb952ae53f71a1d0b017b6b8e1fd9d9f3e201e8ac544a25e5bcf5b3b99a0b8f06c84fa0b5ed56ba341c4ff82d1c283c2b44700513561d808b6a8cb6c82e917dd500484c909cc405a71609cf641d62908a0f857b7a0e2032546494792cfa81cabec9791f35fd89f5ed3406e86c130cc6976e89916c5133f4efbfc31b1817f4ac2530cf0e3faea93746ff5cb379e00f02289c7ab18f7bdd2dbaab70a84cd057838850c16485672e15c9d87865e419ac2d14c9e6e6fc2d7d80706ca1ee551962f1e21beb2e447b78653897e7d5736106325ba0273604262096471f922be1eea9292ae47c08130994f98e80354178102f34c405aed4c63af785b4643fee72b5f259a298702ed58b1faf1c06f749cc02fee77fdd57c2c11efcf8400a908d5c414e422b13e5c0b563085f0923b1cf299a39031e032504ad6db77a1e47a68d02d9f0b1423c8b1937b2671d24d2df821cb1631c6d8f191755064b2941836cc9e794caf380450565e26fac1a44c769a97817a8bc32df8a26067fd4ff20e735966f6442fb24214b5e7d26c5816fd61b465b89982e07baf2c037d5dfc500d683b2074491f57730b0b8962483582b8eec7ead2e88b938e8014f00a9e849c296da5e6e42f924e1ff7bcf907542568b5a6684adfc27f22d8d0838fb9eec9815b44e7b5203407c4c26df9e26a1b38745165860ae2ce5002709d7e45b53cd01b0072c81e43b6676319f608a7f0db90f4fa142af444cd373c9c7bdb9e110af0a1cdf17789f59cf4d89ab13fc27ed762320914ba7ab1fd69946d3c0d49f9e971130de3ee50f67cdac746b3551a1ed4db5ed61daaee4390b2368dd2a366d2a03ef8c53f7c132c6a4c2d6508c5772e2ac0e38c366a7ca45025210863a97b20fe3c3a80a26c8a8e6b7a537fd0b085adc6d891473193f7bd57ba0b3c72ef509c0559b8ff68a702831638d76eecd1abf22dcb780101733d5f76b335f784c69a23b877c249ef9082ed65b2e0c1bb122064d24fe8bbf99d180aa570c28c580d80a9731d6aebb79d30699a702dfab2cd0839820fc15164a084c67e645be2a68111be16c904b6bdbc342032d12a41bd25fa705f07e7d9c3d390077751e0cd75f0bb410562d3c1d9e816dc816807c2fd523a7d5822c57eeb3b7560b7b09f17e547b6292c0b9f3582db8a1053c41e3525e32d4baf16c7c7d560a456c1402f466e1b10f1c456054f403faa007977604279e01fddfd25c1f840f379f21a2a6b77e96dd45dd56fb24475a836867dd656187fbda17f622022a1a675aef0b931b8344b68089321bf59360741e9579251f032cb1132047c0ebc28bf838dac11b5b34c3cc557793ab878e055042ef230f24abcc3f7e1e9f7e89e5f452238e2a8adb0a213b9260b053f4cd61da0a3e3aa65ecedc2121d9bc3053ca979f9ecd3cfbc53d6221ca154a5600d423215f0ac4c8e4c36b782eb66682663237c1ee3924f2537be674ca4bfef52ebeb0b023cb5d135d83be3c396cc9c7852068a5112af9dfe15c4b51ba66bd962eb0d203239b31797420cd5d3e4e1b049032f835eade6aec530e9ab43d3fd70559df462a5a701f21c276197e8e81e53d226b66081d50078ece3845f85bbcf4054a78518000f436b8663b48f50bd3fde7cb4650b093a35d05de72317d0836369da82ac563dac47ca1e81702da9cce0282aae631eb593bd30f4f34f4dbbc84f78dd58cff6ccc7654444594ff496f93473c8540dc289088f43f35055690f2e85c162ac716596f907093e9f510ad2b043c1a7ba4aae92b9fc2a35282221d2516cdcdea2ea4df1f4095b8c17d4b6e54e34f7981f565d66e7683c12d0c8fe034d2be8d9147b58ffe5972ab09c2651120fe6afea82542e4c22b973bbee40e52cb1cd9fbc67e84610b9a318c9f83d9cdc6041138966569fe25e253d4586948b97c26cd967832cf08affdff19c6fb84fa3a33fc4864ef5f99f67cf2dbca378bd9f4708a7201f0b2ccd7bec592e1bc89fc5461c3462c8291114c2e54f2e569ad4ee553f77dd3deaa06327dc5c77c724c5f093651101d42ec646e81296c8c8cb34351037fcb330f0cf4903f09e5812dda00215db5c94db0ae767505de5e465ae56ba84c67a0a51dfed967cb48b05453feb6018cd27a78bab901866ae0b9bf880d86d359a664404afb59e0bd93c05d26650fd8661f6001165417d47ba9b260fe823058402bf5d49452484a8b05b87009c34f5ffae2ecab5e8f40bfb34b77df81ec1c9f055e85322a808944674d6cb5846e3e2f81188e7b0ecf05416c4b870aafe22a8db6287b25a914bf5525b3c7921dc07f3ac4af593214acef8aafe70004ee8b31da9806d6be1a39458f806afb1abc34694f0aaa970bd22289f91b1a35aa7fa2edb828ad245fb21817294d42341fbc26117e4544be295fb62a26dd24e570eb57ea4229e7e4635a3162f736e9e84fa970562334d63a409975e2f46e2fb6d79b2c2eca8f922b0352d9f77ac10c9e27514e60a772ec8263ab6b7d47bd2fe97057efd8db5cbfbf30f0e2287f61846c462bf53e7f7c45f3ffdaf32d51e979f260638df1d6e3ee3ffbc30cba0bf4258806b1ec8c98e9fb246fbaadb0a9ca1cd79bc69c4338532e6117e65bdcde8c528ac295e261c570d46a8ea7d9dae0bf1067407b999dd631f03e6fd34f23a7505eb20c22e03304847fbf0204620fc089fcc716cabed3a8cb33c3e949d30eb84b82ceca697c4b52213d79907543ab95ad45a058a1ed9b714b6e2d2fae114ad18e542edb501df6bbf5786f8a37673a2da5ec0c58c0cc61f01578342274d14f6408e115c12f815d24fb42262e900951b73e3c391ff645ad8d6dda24ec226b1dffce569ab98fb0c8e39e0e3bc012eb3b1c1bb40ab0e85e93481c0d492b3e6b4bc5a622820f4fb5d599d51d6e6ba0c34a7b9c65e852728285a1643cd6cdde8bee4cc0d11ef47703a87637445df1686b3d91a4c0e649ebe4826d3cf120fd2f55ea1e65f3aacc592aaa923f132c59109ca0f0537ad90e54b3598a2408f3f2c3c71de1958061c427d06c3f5a1aaf57cdedd32c74eda7905756ee91d3921bb8eece10056ae8e3cb1b5a649e7bbc8046d7b2645c7bb026a87fbb150b26d5897668055b0de02709ae8977ae628cc65901ee9af4c6780a8a0166232ff779d2e698b94cfb1613af28808d3eec0da193306cf3b601afe507bf27644a0e007da8f03e19f7a86c4a3f51817e7599e6c46b7e57d551fd758d83f9cfa60c81829eda3c373ce33c889bbfeed33e74ad2db065203cd900cac17b2b285fa220f3273589256a6884a69536a83691826adad2fb27e251aa34bd9a551a0d1a09bb6d9c71f5d34d42b106ffbf3ebf4b246ed402a13b7f9d11690c1fa9e4a26ee36a2e11155a07af66fb037cae25e37ee3ec21f12649f6ac36944b62d30fd5f32bc599b0840429c5142f9a269dba0ce4b75bb95d2ef5b9b764fcdd3e4f272ab80fde4efe2175b9fce3408c932134c8ab6618503baf7ab702a0095df89d469141fdcaf0ba15251b519a959960f383827afb318ba2eb75c86abd149241af43a2e8b120fd09d95051f035985457776d20853fdcbc2e68ebabd4dc1c3688762b3678a67a81c90856ecf51a5916deaa933340ccfff5af84b1d7b62f92f73c0aa08d43a02c67ba9090706082046dbf3001f2de3f7f6c9b757fdae0886636287d30d820964ab6ae80f63695d54093428b5cb030badd66220447c81b223b993348658b6552627b2b1691100fee5272c00a3aff2e6a575f7fdc30865ae1d193886a8555d9ad747dd1a6dcb18fc6cce4ca48984b4422f9448c346c0a584802b6db8cb88786c79662a781e20b316dc6cb9bc9a710ffaa96579bd49969941d5f53a3aaa97a1777aa4836f7f5d1010e2b9efb24c02d9f3651e1494f9fc8c4a5223710774328aa7a1c2dc434b8969db5071f062eeaf10db875819ad8824d1056585de53ac9039af6897ea514d02e2d99833eac7fc35581ea10a45a6ed73f89b7cdb6a93ef3706ea34f13bdf9e8882af8a2f74c468e575687a0c2edfd7ae4eb9dc96d6584b1887d9c70cad7f80ac94dee38c039c584ed86943dc9ad467f400d8b281fa3f648e8669bf8e2b32b624335a949a0e1453ac35b7b3b879e676118265e7d920bcc90500126ec3563941d260b8ce43a1b9314f034cfb3a60cb6d5e8593163de399355723c8af278d31bf11f5a2f5f26070e6024d9eef284534c4e0a92fd81027369ebb8d7a4dcee58aca2d405d1cb3ba82754b3d43e36034586bc80cdde153236fcb9313eaef200ce6314a7961264916069956071d2b57e68a97ad963d265fc6686db992f2dc00ffdd4713dc66cc90d6eaa498b56502d1c4559178787aab3a374a172de0b17656374c0b0523c3f9724cfc2308f20c94c57709c465fd2e8f755e555882715e958c8f043521d8b662644da2a2ec1039fb332d6efa3d0fa988460acaa9de1db45de82b34eb77a4156e12640b60d0d51dae7aad644d09a652f76786bf87bfef38f5be9ce3e902c0a261385ac912ad6a1c3024379c8eece18be70ab3f3cae04aadada61643ef97439b5cac8e63b7810eba20cb6b594db640f6d8aab6200be2ea6626e3d865be6a671e2485149babf07888bc6cbaa63e5cd8236f9057a99096d821937753e1635cc52c96cd8544be0d6e1c2af9d9b77e79219fc7b84597504887cefa6b0425657360d54b0d94f22c2fa006a3ebc13bebca97e6cd1323c270518090a1660f193ad5083fd037919aca9e536e7056d54a7cc1d4cbc676e88e531daecdc1f2fa191aa7e1a3dc0c6f48db5c6a86762339b9c09244003185da2d4a29413542ca67d1b6140c79fc5a26efaeab20faa16888fb33d095bea921e058731c55c204bb819f5978d7cf96fa0255a3cc816586dc5f22e152cf6a36018050b5a1de13e15d2561bc1dbd72cf4e08463481f23c2c835d2a9e9a3fb6bddabe54c60fc6cfa346214307a1944b6c6d93a5956b9efba786a8c3eba33abda97951ee78a520ee0471a75f7a20c704c30b56307a0518eaaa54b4502dae3cb5deec5eb435b334825cd6dfbef4080cba8c4a905d8d6356b391bf9ebc177c5c4efd8d56063a178d9271c17f4eb40e2cbc4356e0d2a0fc02d0e2f2a03842acc110319e5eaff2584080d4f6edace9f5c4bc2c08e0d563c411103b4e9b3da53291ecafe20a80bbee5c1c01618c517639608720748429d64468b54e79a154e9fd00ea7d1158fde25912a226311a5097b0aed609ba0e1d12d163af422ba20c94e80025e9a4a314a7be1c6b858fb07ee965224dee7eed506ac51db9caa6d8f3f36a99b1225542e0840f1144c3abf05284d39a87db5eabb1e4e4a82a95505b82be26389ce82526f3890c448e731b07414921a40d0c3d5b47271365f12b1a7052c14d9a0057561179b5e678a5d83af07ecaeba369efab47403f5396bd3d1edf1ecff8e9840af7ab9a8b0f2daadd17e1d87b64e61ca226a1da6bf27ce26eb3468bed9d99882aabb9b0112276b742320890ad1ac8902685df39a7ccff49f2a0df2f1606fcae428330b12a7afce03c9901eb0440eb603029cd77d6f0edf488616212e7f0449c17e754a87aa1333e9f0c654792ce3b9e80a27e6aae3a0d04a7ec74685fc30158236e9853c4608087db8b9516fec0f7eec703a4136702b9d9509fc6aca243f6c63c6318b643790fbef10c31cd7ab2c0f3765c944d2c8be8a4858d26e6a0cf13bada9fa85c0a94c5e76184ce9300d3de06b1746d9c80ea6d670a89872a004b99366b9372b490aa3e640f3e605f206ea4a876f4343903f55eb729361e8181e290d83be9e2f080ab5af82b0fdc45430c647ba4c238c1cfa3c8319da15a0c2e0667efd5f438c359c0484c1711d9490cc93cc533bea9fbd28f3d292fb83d51263084a95d13c49cd968415aadf38045210c14a2e20061435eb9f5b8ea1cd5fb30e60930ccc56196a744eebc2627197b2648db8bc41c7d121a3a9a7a42b9dbdc552ed0e3ca418c560aa30c07b9b6c0e41bfd3e6d843633423778659a79663bec749ec27da1025d4b8cb6fb81088c038620b799c492d5a107ebbce934586b6c230c0429ea8818f0564cd16045bb2270a243652599ead17b11d11ba7df8e28111e8347273e169211d25cbabe38bbc25f1361ea632e321f976525f2291f39d2d122a5b875daeb7de5decb1782b6bdc1cde8e654b090f89d741136bbd2d0e5d875e1af76546b6463cf53387f674ff01e7b534dbe9652c72b1791d1b84c7789e56902f02fb5b0408c1760f0cb18155e2d15e06319646d1935a43f15a8f69ba2c422ef61542f9c365c84abba2ce860c45ce9c867226e10b765cca77b1a13ef6aac068688850af3372667074599d02bd35521cb1c1e44cdf6e9fbcd2b0bef1d6d73de9fbc1f2f819375b354c908048d6e66dae9abd533c32d23e2768078db5c7687a9d54baa486376feb2d1e5765e66e7eaf5ca124424371fd4d8d7be28e6f585a63b3408b260799fb7177a5a66b971345a9f9fbc88aa848fec31d869a0cad92cbcc9da73829cd813b6c2250b9ec5e3a7ed4adfb3d0e70070405a39ca12ece8280f51835cffd7804bd359f4f235264f9dca018cf8c9a16b5f024445ef71709c01d22057672826e28f252f4fb91fe0251b6959784579c701dd8413a8aca82a322ea4f8c9aa6ebb15dfe70245c6628a6413c8cb27b31eeecea3a7d898a2304169a4d0f5a1b30ea5aef58f7b40d0cbf0429178386c16fbaa6462897ed21ea77b2d11a1711e83c6fa2b3ae40d58fa93c248861e7146bde743c8e8a900cac376ffe6fe40d58d93815fb0288c05e2a88dec044f8c60e2fe7a7d58c1f90d3ee4a7083a02e49fead2866e5f1f12e726eb403336544e9ef2fe21b72bd7bfcda1d24714afa127d4e897685f5d361701868906231be2f8446928039a38524c25e6c215b8d93ff465b9e23183876b8cd4a2e3aa7d998d6e676cc7b3af986e1eb128626dc98f8a6ef3ff230163de48ce2a8bd83e2c97ca0b0241baa3fd70b692d8c9b1bf92244c8004d489beb6c6163d48a87404ed541f7dbadf30f6e93f06915499b1bb9015b4dd32e01fca1ba7f5a5fdae62668f91a237eb0af9139c99faaf7da1baf3c903a80f89ed262ed576464af8f9dd9f7ff03afd4a1d0654a0680e9813509580120d800dca34f8fd92bf5493e171f418954eb55aa5a934c057e387405d47b8666488c6c301a25bca11ff72fea84405f25ec9e15a5f2ffa8527b9053f1dee2e6a5dcf6381794cecdcd79658d4afd3cf73c61dd63b3130c127b3b3599903644aae3659dc8c0ee816026c19aabe21f9ceea4723c531f07cf4946ab518fd34193ace648aadf0b5abcdb7839804043779206f7a6f4456c9e69b383658ca0474b2d419aaeb8f1c8b037c6254836e13aaa78bf131bbc529523d72c44b90287f6414f0f896878119d2ee1031182e613ab3f3e69c333ac15ac79fd318890cd8173e298a4783563736e5927c53ac3b42ce8633af0072ad31318ac8de9a2c1e66a49e46955fbee7cc1349c43391a58c10c9f97b8ed8771d106327eb9a9aa722e8e38558d0cdafcfe54799ae62420611c3a0718e2cbce4a16e10ff89f08344de1c0261f06c005dda3c06c5e197147f829ee470d86256442c6e7859c1d2ad69d366c3d23cf5f8f8e5501328be2ffa6d8306e4112868402404cb5ef40a1c51892a74f7be7c256c6e4d0ecdef9de665deb5826c2746a00b8cde7c565a57dfc5a9f9790ffcd271e5d9f7a56a4568c582783f16994f850c95644187f7a49d29a3ed430d580df74a017fafedc3e1580e340c0412a72f1aa606fe4e2ab1bb2ddd7318222601f506ee53348f3484ac9f9d6a261fad75a851907c8500aea94042cdfd664582fefc6c250b830633d0126f586244941cc86cb31a91ca2ec22e6c686011f1d5642f0d6eb62a2440b9903acc27c1fbf895fed3190b082d15ffc2a447cc14cd667def15eac8b9f8ed8d4782cb121b45920618591487ac4ef3f24ce5aba6e90011f2f418a2485522072bf7f1127342620590d9c4d9d031257b5f78b9efde08b04d1bbd99f4a6d0c4e1d0cc1b6161b123ef3b0d077b143a2a582bd0f90b3c3d571d0041ca5818f50e3e4dc330f883a72f618bb49aae585e43a42f70af10ee5ec50a78259a44d8c5de72340c47937a21f954acd3cde429901018327c0018a54a0f16596f88ceb9de2dd03dfd60af7e820e1e6c2c9fb2a2089d7f5b8ff0c7b7da18741e90b670cd39347b1abc9753c54a70da447a437006a12fcab9a03cd6a10e0b09a72c8d55e834eed1d1aa67faf77470f6536dde5346f355646dca66855c75c42321409518863eb981212c3e0f679477cec752c7a3fb318fab1f93620afd328aab95f09b63c98db4c82466152ed4eabe340843af95d967728abcae0ea4199e2b140f76330ddfd9383459a0367b0d2f691c0efb1ad1c89d0144065ca98662c3c70e20ea5e72ebf40b205140112e0dc581b01554054eea16c9de6b4a131633004689b75fe4a8a11906641b500a4e4992798ac1a7e5a2d0c21c933b6ac4adc797405bb4958f2ad0b7d8b4a7cf71813f4bd254dca1364abf66770b1977e2c5cb32ca9534e7f544c632a686cb135e521f8978cc2232803da9dc02c16a36f150a682239b9999fa1980183a1a64ba438e2dae2b119c6ffd492f4c273a8405e93173ba114ebe7bc7bbd42f687c7285a0e80b402203f0e5cafa85ae7321a4b231c29368413f39569432e60e8ac3612291c547a76ccb25d3db60683032280b6590071cb22fb5ff41374dbf8c9a2d6a85622a3c73080d1f2ac8177cb66166ae31f1e366076e7b5ac2a006fc0a9e8047dc8ed5a55230c6a7d8977c441c0f98c81cda835f98a17e877e8d1ccad5281919409c47c4a51d7b9023a348ff6a6822cb5f058813c29f97e5128240bb0e7bf9683f9717fed0f6cd8510283d53043d0a2cc3510755a14dbff854b38a35447f2e849a5e9496e0506d61e08e84cdb372b7abb2dbc1863e937081027069e029403440b9a074c14e2576695c9a7c5af482576f74d28fd60c9c220af449ce91b7be089962192da3c5a7aa4174d925d922fb247ccc2af05ee0cea8916c164fa6de82d89c490d4473e781d159751901e5d92e59df91e89d7eb49fa113408141a8e921da5e514fb180a7dd371baf4f4cf58649938eb7a8010de298f0260e58e54592dea718aa5c63616178cb44c93eba460dacd173281fa87f11a6fe9ab31b2e0e0cb40b624c3e51e1fc75ee79099439b591e00666d00e13f27ffa2a388da700dde1b5a10af3bde8b17641a6b4de6d754e53caa1073049ad3a9b825ce6e9d97ced4825f2d362c0d66eac60a556df4c0f5e988b61c154d83432f77b29ddbafdd2dc7c294f264da9519876f781936083e68c4f8016c03d754ad3a9c62f92a4a3cfea3c99d13d7fef94b9ccecf6c1f6812d2dbc902e9d4669d51efdb1b7e213be7028a511ab6717bdabb5c182e62a1c03247c5de92b1257530243a45983a2d4eb0afff5bccb215d08eb80a07f2a2ee8ec2a5aaabc26a957c12624eb4d851fad26042b9db8afad80ba8fb0c8bbad910150e9baed32b83676b470c55847378579d6035e2bebe4a07c05e359e9971c94d832c7ea42744b4787abcf412a4d5da69742cb469182efc7fc33c80ab2161f3920ee8049dc0ed8379b26ab554cc75d5fd7c183e573ff0247a4074422bbc2bbf599145fb46d1bb32a83fe80e10554b7cb86ffb7e9bff8c9d1f94aa8082991455629615e1b6122b5684d209f0aebc8f2ff2d0e939a96068377ecd5d7190a208b78585861266eaae875ca08371ab1d7bd70f63e0447b9cfe54418e80c5c7688372fce22d9f8392d09ffb79d6080cc480065a364b42d3ae2119c18ad4a588ec7e9df5177644146e952815cb60c1087f3838c3ea160eb87e0ada160715c41aec5c1c18e0d693ed50f74c01bf92e80708cea4885583aeac74713f00da1b256549907135a3f0bac6cd79baf10c29516a11f342a4392d267e51733d70551ec2829063f1a82154dacd79864e04edbe14206e805be516169bf63b16bb2e258673041a95179ea5ec58e361cd6f7451a4c575a70cd7b6746f8edcfb4f5bb4efe9ce1027be63bb9f73612e3d58ed1b193080462c00aee428a5020cea8db975fad7038f947fabe57a8188d27f3289b43dd4ca0769add751f167da02b96c01066851a96805c05f4f90acff12f64386ccd1cfd022db9cb065340e5093b5eb193391e1229bd1c75d2ab5cc9a470d1cacb943409a970d8aeb2230234386e0a4620350994562f400e8abc611277346616f18cb8d6ef8b364918069511b8660ada0f638d10cf013d0940391dea6f959d66b12ce1a95e230eff7f4a7c0553edb4235bfca7926d16a768e0c19ad897b409bcc4d53ff7f692b13fa21a8a7c7b25dcc8b0465210e066d94d1307c0daab7344becbb1943cf78a5b28fb3eb4546f8a2c4e60bb1e84445a4d301ac37fb59ede5f1bb06b4c2590604365676e7e1902a907d1465e9052c0c166893a3c3e03bcff0084b74e87939a57e9e8b127aaedb0a7b8d587729b21c0271ad415d1650636fa1f461900eda705a912225782efefefbf2d9cdb6bd38320c989659085dc0482a1bc916514a8efa4a16b310664eba701c1f6c5982c8027484fe597a177d37e06b6e7404f01dd7a3ca6e3ed570e6e320ceaac27c69dfac8c44724764f521a3ef89f05becc7bbcf1a41492055e3acbe04f1e3a26cb0f1e3cf94255ff5fa1991579d9351a95843c2e5f89534567cd47c8782b41bff74e436f46c4670c7c378f9868079aa0d2b6cef0e23dfcb4cd8058c1c77c87408006272dd26778a1fa65afb1b4551fd41b5fb7c846147df4f7b9cbbdee1618c98dccc6c1088179528f4e21c491b76393beee6d688aa7e8ba9e479034fee2854b8211ca4e63fb1d655b2f885851423d30873b5da11ed1ec30cd7b6091e3140a1fe5a4ff13083f78b662048614298510f5d22b42a08086a76614f9064907ae987bf8b4f15fca004fd36d7f06023feb2b83e3de7a637d3932cd3cce71101ba60aa3f4aa786213f328fea53e8209396322c19cfb21ad24e19e3b38658f895a632971614d040bf7ef0c8ac6f8b158b3d7013a6e6801cd0097051c199db5863754448dcc663bbb774a6cbd46dab0d0704fafd1e0bda4f2b4a9b90502555415a984fde2e9d43f1fb2b6ca8f852cf722be1bcf17396664bd725207e34f506a24e809a9d4ac94075b6ec4c96d7d3f93503008955675f5c6e6e8a79fbfb9e74cee73b3ba300b13bbb15c60541483cc3b54f0a1769ff2fa97fe3de9143d5c0404ff7b99348daabe45ae5ab34dd91a65b40c5b2a7becca65fa953e064d781814b3757324e95a79ce2e557ae1cd27ae34beaf7a55e35c437c56d2bc199f4a1b461b358eefd27a18fd77f9f67e84b86c70480f0115b257d480c542d2e3c66bbb59193dd482f0055f85825383f77a9758679ba6dcb961c82de7d8b1ff780ad98944e93057267c524473f14154164b50d18a552a42fb4fb4d073fd2acd85022367cbd5758498bc11e9dbf7b693d51a196323b181aa7f6a7be428f9df8667722a01a8ff4e7583595d725607f07ef348c5cc160f9b5dcaa551ac111e68cb22764f23cab84f3fb322b1e34fe9231eca3e6bf531ef9f93ebc8fab168224ddbe9b06d34bbed7a541f1db5d8aea014d6ddd75c18effa3fae061352e3101d54f2fbb34dab77d5a5024af18875084768d26e17e227761f1160cee36d7430be0cbdd2734952169b161794d91e195e1c5d2f5642a95884b16e9240c015a4a0b0dcac2c2f189425f9d2b0c64dc4b6a058c00272cd72853b8a429121922dafccbc66d5f7d1d223307fdc8b5c4511aa2e64723e4b0580d45689e73f6f9e05e24499c5107844696ecb94cdd1da2826a9e661ef8686cedd6a42741c4d4ffb79d6a95b068681c95ce69b55864bbfeb6b5996e8fea3d6570b0ee722a959143ed5dfe9e2a9257cf533e811c9049a924bacc906754a8c46377904dab6c4bdd22db7680cd2f8d5abe5f790e9982102e8fd51ddeaf28e1c0f2fcf8bca05c8228f1f58ea78f1b35ead2d22328fc3b69fee3224c931ec1d4dd62006a874f36eca873d4a6eed5f277ec5089de10ba361a50d8a880c9b0172da3a6fa05560623ad6ebebac5ffcaa82b6e44f7b97029786d26230261cdfd60604f35b8874e296e68fc89849124ff78dc69dfcb3f6cb8e3422f0715f19927dbc4d5bb6f7c10a8cf4bec1d210f23848a6679e43d6ca612e2ca1e4d43b5281405a5d5ac8a53a196300462df3dce4fbc7c425431826e1dd36d299dd44ee3aa4b1d37de1f4e1b6d14ff9469e1bcff24c88aa67efcb39fb646295a6342f427d5c85da0cd8600394d6a8761405207e739e2a782d29ec2fec0e2883c5888868bcc81db75d7680273ccd52e4887ba51a0f07f2132d134d2d951901ff821aafeb1bf1c672e4aec3d6697919e14c7e4bb7bbfb5f78cde1390ee4d755bc644d5993ecc763fddef64d2a248eff413a85d5eba280387e2d2de6027f0b108f714e2523b7dcec2284ce204bea7938848b1ca71c7b5ec3d56d4cde0dc4b3aed8d8e5922e3db255b8a92cc08c1547313eac037c6211bf9beb1a76b2bbca0d05abe9c5c1be867495257ac4ef3bab55d766477b96ed8b955682da131e05ef23179f1dfa33ec951bd2d2268c06fbee3556f2bcc9deaa54f6380d826535aed6355031b7424a2a10fc13120c76f864f45ad74c24e5997135b396b7e15b36c16ccdff4c91586b655c87982a3bd38604b8c1187a258e8249f529c0d30b20939939a34cfaf839ea90ddbe7dcb3942396ad1c263d37ecd1f4a3aea4d344b3abc071138e85ea706c20e3081fee55812a722588cf62f27e5ffd959992bf2b80583b3b4666e4fe4d0d3ddfa32f954c7265ec794463b6b99c0dfe338c188af65970ca620a284c9fc8f4f40fdaf812679b86d2e2566baf4eae5da34e1511c025a92a250ee028e5588cd3676d5544b67193822ee9a9c561cf76a78f3be0ba55aba9c8982c625448dea74b2d2af7ae5f7e1a4b94e84d47f67b2a1ac320cb81575562f417277c4bd3f28a0adafe38ce4a01a21691a1f5a9d4612896fb5a4c95cc6bd39acde920c2e2dc3fa1e559d83d64d58e046fd0aeb6b5d4ee8a741c91722444ed2d518401f05c21253bb36b1ce5533f43ce4c6ba3e44a9459b4329bc4e61328791cbbc95e7c9277acafef89f34a12ec6faa337cfb2e100f541fc12c5b25a2488712b9bc5b04828a4ad61d4b3bdb7a92b3159ab8d2ff67d481d506bb93d29bac6958c1d52872fdb36e6515c328cf3b943717e465877e252c756d56cddf412aa9b8a14d084c7012a46da6c6f04eeb1af807d7f18218796c43323becc433e0c99e38911cbd2eedbebc3778eb606eeb8de3da902117333511a4b5067f83c4942bf3a1c14e48e8d79cebc6c6a52a8012ed7534550f881b837f4921566049e6b9b4cf681fa74f7817960593fccb28a480f43962c603a5e1026aa98865c1a241000160d6e3284a3641fb6c3ec0445e79dd35821a5238c180c4f2fdb9f3318b7938f8542135ac80e01ef800d32cdfec4ebe35c4557887957f57a06be0e93b1eb62ea6100af220583177990031d0d2f57638ec2914ef23eefcc0804e5027ade12947cae22cbef83e3a1862b8919d02aef4ddf8efada17197a264c04a2d82fababd626e3e2e304c43153a00b5acead3179346267e40570d5dec50a3532fd8e844b2a81a97257b08baf5b6e3d1cfc1239c56babfdebfea6b120d6b0dc0b59114788f60d2f584fb30dddc7913577f13b413ba4b8186ef37a3f848d58c8599ea013353de3f77e9de9d240da355aceb3a3f5431b8c82b74315f7857e12d8955217a031df62a9be321a56d27dec6d37621da721ea5193f74a7392d3b15862f55128e04d1961924914a51cac241a28ed4392e122b66249698249e6a0444dc5356468c17d6011d7de2e786337cbe3c6e9f864c7372a19c2e65ca793631395e27b8bc2c99c6b3f88995912be0c0534a79fe2ae9c05b21a2e7001886e3a3f35838270804621d0dc7ad444e84682750d6b86b16a16d858f72678fde00df783967d8df3e972b30972be1dfee438659d157affe10585f419de95c5e23072e8be7c3ba98a2269f3acf5191bd55f0738842ce85da327bdc3677e2e4e157716493f2e06e1e460544e78fe1b3a7de4ec77a70ec98f89aa653a962b7f501f48922a029331590fbdad8abd9d1d702d76e8ff2c39dad4d594fcdd58a63c7d58698344b9d6ba8db4d239b631c019c4b7b6692ff3bbaafe5c3f46b1c418c17b6f9ab8be5f6bc5ddba4f1888f7a97263a588cb0b58d73abe16624c18a5abb33328400b7e1f28e0334601518e1d5fe2691e18019517830ebf16b1c0c7ef2b5cf2e5504990d4b6731603f2c56bbae6d4e9531509b90c6d8d2329546d41d0a88281d61c0608d3d2df38a1b323a8804a64e5981fce12e5215e4fd3f0cb536d9beab41ae347993a7a49a74d16210b04e8fe373b46e9e32233754a294ba236bdc7f393e421e099af86fd1bde2c67008dc3b55cdd14cf6cdee405445616c32a6399b1840b635575d5b0cf765d4f003096d1b1a1f3bfa41321d4247ebfa084b7251418d384ca95c54aa8a7d1690f2c98529413df6db4e607d5b13185cc1286198c6a6ccdbc54c8d8bec0607ed93a359adf0004f2c29ea24504733e2a7300273ce96a02dc8c3f062d200e687b4ee43ddbc247c5a6dfe1ac6bad316b204cc03aded9c5df8b54a201a66f940baa1029f5dee6d80d651c2125eeb229af7c762f610e3f714b5fef6033fbcc6f3de1878b88e28ff1183d29457355de7b81ff22bdd7277b945ceac73ce525876b9879bb9aa13c57bc9b098ed31ca5162e46de64a72ad1c66559b59e19b9e861f14b8bad6ed6090ebfdb885c3d39f98827e6c8bb2612de0b8094ebf01b299ab46f6016bae84c000db209bb61a110bdd75d58da03a7ca476947a3d7eefeae847c0e52a694217774fe6417721391816970bf6ad2e223cb02232f7c199b6d13d6482e65179bd6f0e2763f5c07af029d2ea1101316bb0fffed542d93414e64ee133e017dc4159c899bcdfcbe5c70499fd4f87c247d73d4e354ed316ef298ef31d5ee53b5caa00e869799cd3eeed50bb0796f4a35b245d932bf274ec701b2b19e0873958dd9b5b4b0de92fd89918630a8ac8d3ddddc3492869b158a5d2e54ce431dca2f125ec0e8154441282d3dda057de1df131bab8467b617e5ea4650d0749a616233c657440bd1b567897e0e427bc85984f3497c6691a399ea7fb834f3cde81e3490c341b40f3f68e66282447360f821abf12fef0a235a821938d41821c56538f4d147ddba480171edd8a86d52160902a732d1ab0aa5427677b906b0020abf45405bdd430cb49854648f9b678dc95bd487f4a514ab4b896544552fee0a2e239984de6a4e1851394f9626b1b070ceb5c49be16a80cf6f0861c059d4072db65992bdae58e933b4b179cfe18cfe886daefeec614e889f4a16535f4503ef4ea9d0291483769eee36cc34127e819c55093445032efe2b79375377b82456c99665718e9a6947ba8a101cbd57986beb7c0befecff51bca04dda19f0121cba444ee64adec1096a3f2ffbe484d27cbb736ccc841f34ed8a0931571d8fddfb1271ca2a8c679f8a8b3ad641e1ab4c81066c1338bf1022d50a7ac711dafc3232dbc93ebd0e8113657c247b4dcc5bc9bfd3faecf3e5e2b269f1f451baf4c205d75010ef647fce7ef486fc4783cb09e232ae27d58a180d70ad4874a308a779577925a5cc80d0b98d3a512c6290d0ca87a26012b467589facdba6d8780177b42816d541bd6443f5dc67684737d395dd410db8d4932bab0189f63c5373edb6feb15b23fb25696b81d2c50e2c54a3dda43d8e93eef90a9e2dc9142f0f6f10eb385b0486fe02d2bfe8d9aa030d0a6c8403b732e0fe33213bd42205a7449f147426b13784213ecd7c742a9fa1d6c6c2f32c3dbab27f1c471fb0dade088f67cb153df2d47350eda2da185d03ccf52dc39203bfabdffe14864c783aa619e70f4ebb394bb6d6782602c97fa7b5feecdaf70000bec5541886ba95668297d5976743ec65d4d596cfae5f49c111052effdd3bdc53aeed01885ac30904d5bb495050d1bb9d8a8480dcb4cd68b983e45af73fd148a412bb14ae13ebc0bd04e4c355ac77eab2cf30b01f3bf02292e0eea5828548bb130a28c4e91b768fcf6cd2bbb1112e8a31e101a2c4fa3e100ecff29bf1c92ba5add8a79a4bc9b886535cd9ac76be2d66ada81d2c929812edca9accda0d7bf11f69671f428699fa38f46ec577ad29796971416359bd464a3e644cfaf51a0439b4681a79835878dad6e891d871cb7afa04a9a73e48e89b2c753ff7389a9aefbd1de4dd39086b061dcb17069bd795df0309845e4bb7fa8a9fb8e307bdc8acfa8296ec92740101f32b4ef7a2774c21d240332fe05f53a57d4dfc1bb3ec2fb6b19ea54f16591ef44d471c307b12acbfd52c1ae3d99d240a6ee9d61dde92fab36bf17bcb601209b5b9eb3cded7baabf5296793344170abfc7c2b758944b5acc8c46f0ba10a2b9ce831ad0000baf861f2d4f5a6107997d85a3a8a1a73c01529ea88797f798ed5eaee9c8e5c40d215a106ff21170f60861ecb2527217ea8d0c8ee108e1cca4043c6861ff85903196c23853f7af43bf3647a5c75543a1a5d375c4b26c9c3ab032ede9512fca8ec29b60e79fa7e4ed103f8d8ae392ddf99f0dfc14759e6fd7e46dad3d5425121585c3bd3315a3177a824cfe93668e65080c8c3396822e05413828f2121d1263b4f0b1d6f84884f529f0a1cab6bd17350bb22b8ea145b78d163c0a3d6cb707a44a50c2dec00451896583ac7911808c199b04117d54aa59034a8f234113b317d8df9f37d31a041dc42d7ba03a2d9f1385b2ec73cc87ba39b8c08ff5285a7b4228dd491712148802888a91c3a2432566fed1543db4af5006bebaaf87cca93f56e63cfdfdf4993229040a161b103e5a38b2928b03a7bb8141eb599c7b7e25ea0eaa16ce387915acd7e96d5f99fd0dab5a5c01c8cc111baa9b25d33aa0b63c2700746456d6595c333ce50cf71e2f8c8c96cf0bb1a2ce7b1694cb30e838e4be8f6dfceda47c06f6c519d3d79d2c8a88f1631a3b87f1c289c2589e695c83a7b054b51ca683611e5f18eb122fd483a270dbf9776cae72204c8ca193e0b350b4482b61eb533d3a33e5abf0a401d0f613c169bca310bae1f9240b9bc7786c7c35d92e71b508f99fcf541b196e66ba75ad906fa2be9b1027c70ab54cab14b9addeb9f42b54ba02080965b7055e2cda15aed067ee32f09adfa1c7da86999c70030c67a84468d9e0249ebbe64fd69c9431bc273c49d01f43bb2ce7a28b7f5d787dc7fc54845b7b288a7f4ccd893c07c39d0e384f4efe44cb80ecf4f7c8e8d0a815181252787250a2806802c31b0501403b23712bbccfbb5bdd68a6ed64e289700b87f44dbd3320216d830447ab23e8288a29104dba9b84924488817325f726f254b18c5c6b831ceb07b631a27b45c738ed6d712ac03f27c0bcba083705525725a2b0eee2d971b0261ebadd3b06f0de7c2d3654d834d442fc9fdcb0adefacc5cba34fc6cbcbb15bf07e187e2bbbbdbb758ee066ed0f565c15b80aca416629635828d3c425f2c3eec15669962032152d919610a60acfb5a2af66bd212d23b214a50ccce084c68977e94b700a08b249851676955d029eac99df648cb8378c826236f7efee73ca55cc81a82dd4653c1c98dc8fbc45001105b50c04621878c9bf7527a19593ee82f48e071fc3d2b7883c9708292534e6a5e831b756045fe63e9b461866ca7a52766468a04b2948516cd55200bb3ad7b3216ccb90930c3e6725c7e1148ef15d788a9a89ff40c4cdc365bc22256f9ce3ffb1a19128c989614ece92441444d0385b2a683c537812840676654f3ea9966c66a66b0e33a0b6634d83b92eb14ae48a79c9db6bc82651cadbda74741dca06622dbc808bae112b1abec665cc2b391e6a46eecc8a63d1d1398f501020994ce5e8575acb81d3a759720188c44efdcc0dd8b5b0f8ec5b9bde233d2fa03269bdc41c66dc5537fbc56ae01dcae590a299aa037cb34633b63ee014ac79d06334ed6582583b9c1796e1b39e353671cc815b1b1b464c8c3822a34cf4c3a83b5c8a92408080ac22da3f37d68233bba7d1da80c1252aee945b61a68219fb199f0f8aaa5d2c29bd302511801de2b2e5716a6ebe0fc7c708c01525729048c3a23150d9834a41f6ac57559564c1da2a73084eb9fc546507ff9a9f994c4743583e4a510765e3ea7ece69018922316c2de42042e31011babe13f984165f58900286e22441f134d58a096b1f1939df6dd1340d2d61baf71fd9fae7bc3101e3b5759fe7f6d167c84a01828f5b22941cb577bd944c7ce4fa07daa0e385514d8a162cd3dc3d888b7d763d87d2e8d7a9811d9a144da530542cca36b13f9f22f8e07746fa808b2fc1614c8a0b25a0af80b39e86cd335310f294dfb4cbb1af0b8f7eb3c57d35592b98a441f40c1bb399b51f589ce1f5b62f5d2c7c41c9e1c239ff219bf64f324e2bf343adc3e1533be523eb0d7d7937d520b44be5b695bd6b86c11f42fdb8b32021bcefc9dba0147f3befe0bb4ea334eb71ae5464d119c1689b269a3bcbef66c96dbea31f713644b9d93210f3611196c0d82a56b8097fbfa2a493e1a728e878785d228ffdf03926d2864b69ea75e614421e18924a6650a3f056bc91702927a89c465084653da04ee8123c34f109d804fb3e1f6cc88aef2db977db25b82b7ddbc53ca2dd6dbf2e44100ac6d04836b330e43e1c6b6a72df938d7ef12109dc927707c08f83bede6866f78383ac541ae9a6401891d6c414c9b73d913d01ff7cfaec2b7ad91716b72bfb2ee3df39775c56006ea64575f2a3683d1ee04ad465a0f057f5522de8e0761174c534b90b33dc6dfa0358ebc1728dd96684b43778772a566fafbe9dd0cd752481a798668a6a2bd1321d00a6c949b11d92ed1971e82846d4b3d21243736818ea0acb915f0e62005c567bcd8dcfd931684580013f19e1f9d745f42341e024b728c172ed76eb7a33ca66d124362fd7136588872e307d839bf83358c186d629f4a4033b3501e969fa6ff5bf625a915acb7198ff795a4b0df5512cc1d63a118e0e79fbc0efb27f8a7efc450b7f2995364f151aa6d16be0f28c5e885bb79b3d3443835a3351868766e228864b1cf213c841b75e645d12c47b8620eb8ac28e4d9e616ec6312918154218565fdf9e1a7fdd03b3fd910c3373d994b1a6e23754cdeba552fb8abbabd237ab534377b75da5b0f3c35754071b2e9658dca35bff8fc623054127762e483cb4c24738de817c1f40f18a154cb6756eb56b4cac9c7b213366729e11a68dc062c92f5991a51a8fa5431b64b3573c525265fca8359b27d4b96b6612e56ee822a9f5098322710a9b03e6e779980aa4352bb6f41ea46d5c20c60899ad372288614400d2f2fd4ecd8e807e8adf85d83d6bd020390d66a4023149f13764e5c9fcbbcda52f26203e694070f6a0c3a53da7da654557b6ad158b1d82116c3f7948159c56583f3295356d3b05979e1adae79b14695ae29da5a570017de0fd49b774df2665d1d50e1c4f045d570d4c0fc9405d4403369b1d6ce2d96465210a6842af5a0cf32a36cf4f7007efec832a411401df65b01807a201109312ab37c3bd6a241cf8819a56a092ea2ce1c5c62b7288f973a9888005875184049d02e044809064a8cb7ac8319f5f8ff5d4d93354f289ec8ee781cf513d6c0f191fddc5c39e13d36c39c94c8d0b692b613ba0c5d70e37b87e33f9d0b6235042fa7823f1225eafa1b21a6696f13c3e9ecca171f66c8df9ada840d30a4b1c9825cc2cee39d12f81e5ac2d2c3665590a113f3490454a980411c03398a2b3aea823bdb9310727c5c66a68d8adb0b9daff1440824384040b3062fa83933f7afbcd77c4f7f17419bb7653338b2c3a6d192f2b9d7d46282c62b433ba05ac09ae6e702d604ae6f03ac7c6a514add6f2cc2f202a6daa7fe788cc74203273bf8f145befd8b9a08d83b69f3f3c6ba9a9fdab1829bdb4f0b7a04e80e928a019834ed0d14ea4799df8b1728fe63fd5c7b298b6a053df9b71900a02f3039fd108d5f63e4d4b90bff24c07a0178eca9d94e35f2b975f3db14837557517add5d8d48f03e3d468f621b1ff248d240f2b8f7936d08d8f396713a6d24e5d0d2c902143e38834ea6c92c023f382d5641bb632b4b85866cfbc155059b77f43062a06e94515ea919571340182b05ac3cfe9b47abfba399ef6801ea08832d20f3e276d694329597d52519a348b235cce764e217d465f1862647d17c0b302e0f4c6d04a1e88d7fb829fe8da8200e7f4e680aa49cfdaafe457bb2630cab8f7c64432c5a8b1929e341f309938e7ec05f16465631da31f9808650958b1681afdfcfa2db66014293044b49c7125a0ac8d95194e5d78899964cc99ee08d6c4a9261126e9ad253c6ba2ed12b5acd7992676aba4c29ced0753c437f338b763678ec52937eaa7fa7931b11ac79bb0dc36f9274d0ac01ce0a9af6e9e5b04ab3ed314b7d7ea93f35651f6f04977360cbb008ccf2991489c3adb9c69c73cf8935df4dd44edb4a18e870438acf53d1c2f278f21718cb4f40f1115477ecb8583d79f46a4555167b6a10dc93cbe402d884a08a1855e9830a2c1ea1968e17b74fafb4b6bc7e0092bb0dc09c4c1c7688bb8cadb717b7fbae2888202dbb03e02f09747a0cccd884f52d0965b95eb218c043b86151fc86d43a54b94470a61c36c6cd5ffa04de8e3bbeb81b67f2446c317a4b135ae67391e9e97cea032f8de2dc5be845e8ca1ae2950b0ce06bbb356dab2a06b66917b5c05b3b8a1606b36f718644263e298b6097c0ae42f44667bfcaa502ce0da0b202246c4cc52484923da6197dfa8c732c74237cc886c5c2e9f58e5b1397e460bb9d2337831354f5a64f71850b22d80e3de454a7c639e0e14a7b6aa0ff289a29509efdc604c85884b4a604102d234050419ecdd4610f9f9b32fa04b4f3922038d260b6de6fdd6e051a753b20ea140339e546df18f93eb4d485c2b1e9b6ee057fd46a2bcc1e2d481444b512026dd33258b3dcbeaf033c4e3cb7bae7bcf5745535244e6a1fb03cbc94ce5d617a7a8a996b28f05c11f51fca8a2eaa2307a0d8c1d7893c474e69b69758c77765ffe491f57f5b85c5e53f5b637ec291090c2b08d86317fc0b8ec1bda77369fcd8fd40c8d689499916fdc0033c6c56fa2335fa14ea0421fcf23fdf00a3143b1524e1e3b2745d81a002b222dfcefa29db95f0f829ab749da1a20d889902bec205ecdac8c6f214529d33761d6a8ec24a04dafc3d25ed89de4fdf351ad01c6e0e91ecee00b433be5bf823c163f31d91760e80b8d19902ffe5da031cdaafd025e91a4394cb750986be45c526263548e08eb926d1b5b638e5277327a788caebf07c8782b42f5eca0bc45014db0618ba0d8eab955e2d4d0cb7a5215b206c868bd438e947e7fb7221e37755da8d751caa04f220db54131e5cd76403b00a94c999779651b43bbc01c0c0e23a74a27551fb7c96f45a7cbbe994eea8bc2c9a0ae81ef3879360056fb5a43085355cb4b832c8abdf263f857049192c98cf3e1318270da30c5d9225d10c8761107801e68d3d9aa9e35877b8a5d76a06b341c81338b4f15ccfb0fcce01f37dd1e3747f4895c4a6ca2e89be3796ea4c2b00a9900a94ba50e4610073a2ab9b68463bd8763af7aee2671ffdef538eeb9c8b4746427f19a8a2365aee2028072bcf9daed0753a9240ae3bd2f4a09cd53b3e95fe6152d402bf285a8459cd86ccbca2e04914768615890d93368eb763cb935046e12c36125b0ba6e6ba2d3656562575a624d0b98eae0cd6fc4f7bc2c925ea4895551f6ab6090863788574306d47f28fecce88161721ac2fc133f29e7842d8268c2569f40f32107353eb68053ce5f3da75eef930a54ce09ae09e6f1879e4f7fb7fb87211a1a5fc93b5905d01fa6e8086d90980aa99e24bacdc314000ce595c820c567ef75540114a4ee0a53bcdee95048d156c37af05537d34a43d6358fc4dacb490a246bcc0ef4143ea7b5e5cec3bf1a4b4c4c3199e8e6e4d43ecbd6d90918ae69b34aa50ac157b3d1266f69d70904db86be994c4322fa6905d3b6c72458b99053566792b09379d25d46bde2569917429033668fa4bdecbefd1ae20adf245aaf28efa65fea5d1c43107d62459badc13848d86414104472b5aa0961d2c80a4e3eb2821c0e88a695096913fe209149372bf8b9372b18d9b1218c4620c48323103ad667851ced6a12af147dd2f476eff94c53e4b2c053bc15041eb12324691c6b94261c69132f26ae6245d276f32b2562875fc723ff8b5210ef9b48b653660fcb6f371747c0d09a480dd86217086376923e7f69de6bc1c2876e1a612408d1b47bd40f8ebb832468d13407f88732aacabdf5b7ebf047b5c47f51fd4135611906040579f176fff7d9b0c3beee700d3dccb398e3ae56ca264a5ac3b891f0588043ad514dae8ce75012fa3fb67eb4c781eea21a15acec5ccbf0d8bf1e8ba0cccc459218037d41e46614ef66ff904ffa6521c7d04b8e736646e1371ce0466d484308d475f021b68ba19cc8b79c318b66e7fcf4176a390133a248b157171f0d3dd6ae9397f86a6a7b9268b2a4d5dee465a968afa91ffa60327f8cc5419af97a1b529adb4bc51c0ef6595b974a732443129f34f4e8213ca024d63735ceb51eb92620b65a36b1282569cd90ef506f1853123efc9dcdf78125be37f8783bd70985f2df8b4f0fdfe83c8246d01253571925f4798c777e9a2e95a58e668be2a6567d6eca79f253f528f2e85a06902edbc60e75da4fdc633ea5000fa4c3b6798106bf99a464623b162bc5a16a9a6b9924a65296704b7a4fc24f14dcc4a485a81026db5b1ed7d1b6e9cfe2a1246a0d2a3db62cb90192481d1e2c6ec03f4def60a2937124100a59decd24ceac6f14f07c4eebd992689d765af5a5c8bc483be4bc435df2f70d52152d5ccf52497a51eeeddbc05cb12c0899fcb9149dcee730ed125bdb32b86a6978709f62489e99195c11235455d82c286f898fbdfc0e6481aac1d5ff17274a02a0f447c08cdf8c4e77ba4dcc87938f2f52592ec606e8a5ded05b96439d004d1eb26a74a053adff03a2d26b9f9fa8a20818e2fe5b1e861e32736d0e59ed7a353b3d9d0ecd48d5fa2f2fb78180c24e42d0624265e71a5cc3091774880baab5b90c19f79895235dc45176da570106e80cb24329f3d8aa136f4ab6ce3d5f1b38a085a973af59d3523ba86184ea9ca4843f3bddd16022ba0aa3bc4218ed8519713c8295470e0d4ed6a865e69e65b3fe51d4ca1c5a42513fdb9ad9c60b82a39eb5ba7e1834ca147bfe391ea24ad36eb00d4c01d374e20517fc1931b83f9caf4fc047656bdf44cf584f0f46601b43c72bb56d92b6385b1116555aca22ef86548483682011a7f9d9e8004453bcf6a01322810f68e710dfa560ce55fd8eb23f84223ac182939f0aeaf3643253b2ed8171e54d0ab621d6080ab52b045f71629a6dee8afbc25165aaba9e1626839a25c804531a276373e46bce0aca2d05da8670ca73fa49e79c499486e82b050c9a5aa62b66ee99d2d62ddc539a95b70d95d9b7c87f0cf56bfafb02e04d3c3f617bca5c90144e80082a9376fde6db44a037b7e1f9687352386b6f202c28dda95fb0dac78bd09af0a34ab26e533a4da7b819f511e9625a0df3ad6adb2c9026e2255bae17bd671f17128a4640bdf8498c99b886af32ee0fc6097100cb7b2d0e12024cf740b58bd6346f4e9969335c319b646141a77576bd8566d13c188744d5eb8415718a7150aaa18e0a202c6dced26450a82a409c9aadc90938e629e4d2f737cb1d257271e189c0b033216b3729d2d872f5b622c23055e0838f807542e95a97d9dfd6b9d616a07ab5672279d84a08de686352adc0a145888f28a95904a5284497be8575ab8add712e55e1a651285a9226cc7e3f2d5a53a510dcb4b320c09f72a55c4fd3ef13ca1a7c52ac6ab1d74f1fff99837a3a8e30cf522cb3f9f98e7f1e9d8eb3ed3b8d0f48fabe7d1ca1c5b0dd7038056b66ea1d4ba5b1afa449930398e241af8296cb3e570fa60184dbf267862f4b1d853727307d899d541688294a3df1c4da947012c099eb55966f659450c64524b507d4618aa78a450343cedb6e771aebc3b60fb6330295fe4281344028a53cf4ba86a73f5f60140bab24dc9790058ae2b1cb8388da58c51d5ec704d14f321212275e2748d886f13e580029a866f6680bb2d032c4989a86449d07c25c14057b06552221f447005f3c06b68085c7bc4a48a6eca9e92fc4472e771801010d3fce4f7092feabbd38560b6c8f36644e3aaa5a576a93a886057c78d49874c45f954ea076d33f61c8c2add4a81f5fa5292348c5262471bedb2054ad8760593228c3bfdb2d86f8009aede865a6c68b0ec6c9e0335112c9546e3a2624f792f08e05153c55e680bf02e70b0c121a21c10cb9f6e1092bd6ada39428508273ea86dde5a6e64a188d8b51ac02221f2a9222039a331c00fe92c332e0ef0918c0df77536599743bad0392137d9d8620942fb16db349a29b453d671eec323b93655ec6f5eb42125a74c59a55ad34935cb47658827d09ce65fec95daaaaf16d41517d191489da6cf65046cfbf8a825d4edf75fb0ab18f03a905f66b7375e517f44f0e437775ec0432bab09cf9b714e9054b141cf03b40612c9e4d4ebf58dc3de3d199e9e0eab1be37dc95be7e3a42801fd68434244a6bc543b9346588639bd429d387e9b0eb531e7d8a5750df0b8a516e684122d3fa9e85b14c34d7f0abf40fcf438c613b9c38bb80f01525b8b3371abead18bf858770a63261184c671b3aa63a6186bbec8ca2f684de22c7db34be8413fc44d3c614ee7a8f6b3d357c87108dddc66754fb69500cc3c8bc56df15cae4cfcd8c51c3836378ca408b625f9a13cb43c774c57ea5c7011822495e3714be70f53d8016fac465efa9ceb02721b8c9b7efb489e1f55c6f69bc48b21daeadd8ec5299ff427651e48217a01718189464f4b4878755b8b83815febd76dee6432489150e332168249df966d0ce3b38fff248130f248eed1f1618bccb8452e465c89d29921e6fae4848ed851d5705c6d186e781a72a2eb893350e6af1d892c8ff44c64963b5aa12c83d320829f90ce93db71f5ad44b135d1338005942925b1a966998d0d449b94f339cf710e42ad5b72c0c052e34456f1155106058b539e81214b4b16dc3e530acf13f436e7f7b1601240f3d6390853cfcbffd40d91b1d8994d495f5f48b59cb2526da5a7ddb40a05e4a209396749fd3144ff379edcb1de1ba81fcffffa73adecccccc7677ef14a1125d12ad0f3639363e6c706c7ad8f0b0d961736363635363f3a326a7c6470d4e4d8f1a1e353b6a6e6a6c6a6a6a6235dddfdd399a554a01be61ed2c69724df6d78a3f747b392503938211c285f003784a2675007777eff670b777bb3d27ddfdb5f7436980528fee5e35ab1453ba016ec15b3098b752323098b7b2ec49f7142c26d366f5ba67d99330306f619a430edd9ee9350be4c1182e83030e9ea98f6f24d6929aa015ed42b3c006b0c05a3baef98cb9e3fab4ec2d3c96a78db9cc7a3e6d3e7f6cf65ebbd3dd5db34023dd61686a1638001b1c60d2dd1e2488dbc0b878a9785db3ba632d91785d3937368fe9cef8597c138927d7592582c5eba2cd4e5bf2640b942d915959c94a2f5177b38015e50a2dcdfaaae86e98fb555673f9a34f16bf2886a4bb53dd5d45771335eb23d25df3ec69fcdab53292d69c880f121f34dd1da4bb9334cb7b028b97de843fc29a99b1da998c4b2b767b65dfb259e32991646b85f217c9e5675c7169c5d2e7da3c347e36cf7bed90f1f3b5a6790e650b847fb440f88fe81f192b498f4a9f6b7b64d6dea0f28b747b359e9fddffe96e547750b33a0ec42a5913f2086d666876dce488ff2336fef87183238a6f3353b362183b69b9ce6e37e33afb4b6367978a3424152d4973f14993af8dd16e8fecf68cbabda2eef6e9fe5a3eafbb859a1513a5767bb46e6fd6edc9babdeff6c66e4f3ce974abeece9534b1c3328581e046b3382fbadb46b3b8289dc37d5c56d0088e01f886a3add1887f64a3891b80e86e189ac519299ff420fe311fc9d3d2b0c738ae308ef46359fd8f6cedefcba436dcdd37dd9c92ee8ea159dc0cdd8d6f2896371cebf6ee6eec6e1ccd6a23e01be69a8c7637e7fdd0d7defb5e521fbfbc67fe9bcbaf3d69d6aae74a6ff6595973a6b56b654e9bcb4077cbd0ac26d2dd2bcd6adcdd2f34ab81189fac54e6f8f32de62eb35eda9aa9dfaead7d0ddfb0c1eeaed4dd3334ab71e01b8a7ff4b2d14b235e4c235a92a434b74b6fac92dd1eacdb7b757b40f4d7edfde08216d050f5164949aef65cd88210542a23d0bf22e00b4f4784c7e9a0ea0f244f4f8c28a2f3ee6b7129f8beef73313ab9c9898700d48780a3cec1ec390240cf95329d8cc017a393770e12e12aea9c333a21c0f57d9ee77ddef77d9fe7b573de0e7a40d51cc7795e747294772e4ec0066ffe8e3e1727385c465dbb522f4e10e0e25c27e74e2f4e10e052b9f7bd38e9eefcc47559a86e5041000286e8dcf32138ff1c0122203279c911f0048aef804428077d8893a7bc5f108084ef802f0818a2e4a91728bee339ea6588d30b14dfe11c74d3cb10a517f0c5f3f6214a5cb9e2ad542edcce8a1043747e7a4972e99dd5ca51a6d4e7a9eec92704cab90eb5e23b2b17225c9028c08ee729972750b81dcf9128c0cee7bd926387cf579cc85bb9caa5775c56563ca57228dc4eeac9ca5528ef272b5f71ce45889473a8172144409472ce855079e7b292a3c7e42523d30b15b7ce4b2f54dc3c2f40116b757a49f2fca47279a27228be93f253cab9959724cf39e75e8418c2f3d44b92e7294fbd3c81e23b2beeadf4e0f98a8b104378ae7a49f27c65078fc8f3ce050ab79372940b146e07e52a972748146027e550b81d95abba957b2b3c3976f87cc57bf09ea09610b1b3f22284088854ceb91029771142a542959cdbb9410396934ec3557a31a2e2d63915b7a0cec1951c3b7cde3991e73b2a6e25e8f35e09f25c48e72515a86ee00007eb4618bdc3b94081f2040a1227cf47e73b9d0b1222705200ce0589ef091411382980cbc891f85e44e0a4002ecf7b0b97e7dc12d58d1cb03a577ddea71b5f746e0318a4f33ccf5b013d67019dc5545a6161d191a2e3058bce0f5656ab9595afb0b0b0acaca44c2b2dbec2e22f45dc8ab7ac386b25652a81beea3e5f792caa95149d2f74bed0f942e78b8ea5333d31a5a5468d1aaee3450d9d1fb4d4a8615a7129d209a306a73345e70b9d2f582d2d3a5274bc6861b918b1d470a1a5c56bd45871560d67b5b4b4b09cb5e226969f4c2d5e943f1600a4d428aa616aa9e12cd64b116785149d2f74bed0f942e78b29353a93109870ad74a6e848d1f142478a8e173a3fd091a2e3854e183a535c252fd2995294bf9329b3b889e5dd4b9127450a6785ca592e2ad58ae5c54d255f597929e2ac70ad5e5e5e8a523a5fe87ca1f3858a89d3028a4cfe81be02c10ef46e653a994aa894a7565d27644a977216172395b78babe1aad52ae52c2b674975aa55c7b2624999bc04bab7d2a95629479d5e8a5c1d0b57c24207505cdc035c4d2507537081555cacf66681dfca8ac957564a2b2b1fca576a147d2ef08773a95c4827440c5ff15abcc6f7d5f8be156f713162a9e12eb8aa468d156fa9e12d2b2eb8140de132e290b0780d17d27d2d9e8bb81512ac52449a585e64fa5e8a586a780d67b9d47859f12fe526677111220647b3f26263a572a55e8a4a8ecad1f9c9f452025fc02a252ca8be315445de8ba95b9d3cefa67846ed72e2b9bad357c472118180214c454208f13a23748eea844ce970c2e08074fe15799e3bd7f715995e7257f2a2de628cce0854ac70a9bc1593e7208c1c84c0a5ea56a5931791609592cb50f222d2eb502e83eaa5c84bbd90601593a93bbd18b910313820a07ba6d20a7c519151ca4295299c5145f544c5c580555482b282b2825282b2825282b2825282f282159415940ea7c495564eac9e584571f25515ab2a562138a9844c1122869029689cb14a3a038d551297d4e114714f5220cac5e068522f28140c4a50ce78018b73412931a0c48413a5273a9c971fa8cef0dc539d512a39d1e194a094a094a0aca0aca0081103d55b84711232058a1031aab8c02a2eae4a9512142162acac50a1a16a1b8a546374381ff80350ca0af782c3e5b93ad58b0d162b95868a53b9ac4edccbead405ab08244315c58bf1fc7462f50d19b08c500eba8a5451541ea505aa2d5432e870545ea8a6b89c7c8c317e7052954edce742bde808f14adc8b2a041d8ce79f8baa0a9515aa2a542150a9ace8704c9c151dcec93d4fa5a10253ae9ae27ab9c105cbe4adaa42062cce059261840e07f4af091d0ee7e0143a9c335c459cab48e70b8e86d309c355722a604c2902c95049e9708a5455806480553a1cb08c1074382597e7aa281dce161e48860d55149d2f389a920be9389a221d2f5ce0a723a58301918020192e4fe70b0e08f75244c4e7427951fe5c27169715179004a5b8502f45980897c94b9c9f5c8aebf4f2524482525ca69793178164e87cc1d1802f9c8a8c54b77a010fc6e0c10c8a945c2e240999d22f42c668223c17d5185ea568c5045772cfa5b708c3e50999d2c17848a0b8c02a2eaf8888ce5fda13220647c379ae2d58453530e1ea7cc5c42a021d8e1335746b75a3052cce3fe77121099442d5aa5e7563b88aba56f58af3d58ba350dee22c7e04cb6b78b7926387166771a2961722968af3beee3b7920ab48bb56be529d4a2ed0573e2b2f525c2f2929b0522f4528ef4c7e4ab9108f04abb84c2f42c4e068ba22225ca0e7355cda8b6171a181cbf3213e22529d7703d509008d01557b9e8b0dcff3da0735acee54f2a070011492c8810a1830832dfc400a1b3c91802914e80114c4d8017de002bf530978e0eaaee0441251605ca70ea6bbbb9beb9f9ee98ec04c711b74c182a213152eb0608201468efc744d118f9c200ef305c9f1151763b88a6ccc60c309a04cf1a8c004a60a9819a8be66e2ea604c8711628b1394c1ca421715e8ba2eaae8228a0b092e290bac9e72b281572a71d185ab54f258a0725351131d580016927c60020cec94ba265c252a5c5cc14d26afbf131751e0a20c930c58a8ef6445941a971555b850dfa9046060419562c16a8b15704de08c60ad5c9cb34e1cc7751d0c9af079dc0b367ee02a5a159d6cbc0016e154c66705184c5979a6d3e9048220783a9d4ea7d309044110ecb0309d4a306842ca04b24a251378e26ca05e4c3190c1118ab0042a492461891e80a7d3e97412a9407184181258820919008500e3b9ea0827972a8a058c0882091757250a186330e10452b090421935e8380e064c445982e3b8ae8b6201cf0b1858e12afa7e6035151844e1eabc733515141c814a14118052a0703aa1744025064e5051c113ae1f3a1548804a1423c02da64094010d5c91851514b10221f0a08ad3ca6bee046350e138aef3c830f1e0fb3eb074c519575439ad4c2693e984a2820514d52a954aa5542a13948105074a2817d4944c272aa0eac5060e16ab9442994c27d3f7e140f90bb0e05a19c12a054f9e78d73085093680f2042750c1bd2dcce058a62dc8b040698c2324c105578491812b388004df0cdf4b4e099a40e30c26821085123bde0ea2ec0000319c4e2a950a9660c2c15281192ea46e9c58a5d24ea9640271ac20010080e1f4a252ad6002df0b2b1b1d00000a582ec880a585ca8a95aab18232502c4f6058b1c00b2b12b8a07a418dd49417502c38c2ca890551a8612a02554a4206708a962f869309c74ea9543a994ea713ab054828954ea6530d5753c922c93503146e4a9550518a009e4cbd45133d80028616be8872841851b0006bec80e00c38130b2c49c0058259004d25108c52860b6c960d1c2caea6128509ae229667a10241b00446096389284ba85051748106190ae0a2014680a0a48311ac44b1c0e98ba2012c9870e2c0125802c1d3e974024d5848e1740ac3753a4531c2755ad9399d4c2596290ba007aef8e22b954023984e544c20eb045ec103d3d73d7e70baa28a678385850e4c504aacd681c517a6d309bce2890f0b0ea0565c4d058b11b044a9c1b2b252a550275309fc3e0a9c50a6941659b850fa4ea7d40e5a3c168e324ea5ee03a980a016554c204b0b2bb8400f0df0d4c54a959a2cac7042d9d0c1709e451a2ca706984c1138954010b4c104e3cea202046d9440500b2b5c200882262ca2e83e3f35e881364a200bf4d309044110044f262d54f081ee36c05cb185abc86b562ee060e93484d209e45ea85039a83299b24882cb6432995427541650e0b83695582894c96432994c281c54b29882b76342b98a585eaa408d09a54aa1e007a8d349c532a15e5244b8e00665587182164849a289521849f8ba38028217a4000b63a011832a3930b94e4538401718d08b1c44c11452a912eb646ae203209800155198620c215c1185836009055b58994094d7c4690b96d3e9c4c271bae13a814b78a8d3e9f4f5e974844e0127181b403739e81d0c0bcc88220431b8820a1e5049c11650decee9642ab1a8a46009ae93aba9a48008378e50799ee7792fa6cf3b27c2f5e5f09c0857e905832f4552aeefc57b299272715af88a7cdd10dff77ddf0b98c3e4f29c83c1f7715cd77d9f0d6f012cef03bfef7b81018d1b504e371480e25029ef0aaacf3bffba8ec5e2b8af5b21fabacf6bcff33ccf3bf7709194cbf322a9afeb2ea04209951ce44c5f7fdff77d5c7762e99c3b9d7aa61dd5e2e2becf06c7b17aa6fdd4751cd733a592837ec4c94dfeade4d8a1e4a013954010e4b892275472d04d2b4425d0d4715c4ece69e5c6112cce8542ad74eea54ea654e742ba24557f278ff57583200d5c0db21411e1fafc5bb9ca8f4839ca39a215e7aa315ced44a929aef622454aaeafa7e472c2a551e4860d582a57c9bb222b57c9f3564097effb585c251004c11aae52a9542a954c2b4558ae929b4c2693c954a4c555f2325c1f135e142f0a19647056705614f518ae971b4f502baa549f587abc1753a9e42f397440b93c073997c9e7f4026ef1026ed133eda00b97860bf7b8e02a7911175c7095cae899064b60c9f5bd80527aa61d94d2332b0d705d45f70c14dfc90a208baba9649146c9d400d3290227d513282a5254b4b0c26432994e272ca270750793ea4eb5a752aa1547050b26b6308373a1a2850a5c20125cc8826bd941d79ed7545c9d7baea6a2051854b428a3bd6ecfe50756533901142daca045152dd070b5290bd516dd4a712ecf6bf7dabd76cf5302e7f2562b2a5719e016e00b9460f2a28f0932a0a8da57bee22997132e8d5e715674508a3a325c5e948f09d7a78407b838ef5c3e267aa67369e1bc7361795e5a752e3538cf4dabce85c5f315176ad5b9ac5c3230e1f23c57994e2ca8a22357f7f2cd805ba438a0529d76560ed2c0d52f70f50f57db14c5740cfd991c47b74ede7e52a9542e4f56ba951ed05502bbc7c9519e4bc905ba50b1e22617211c4de725971d4e894b0f17d3adbc341753d45fb896084106d4e7ee4ab98bebfbbcceb5724102c78e083e57994c3d9f6b06944b555a9956a08bc9a5e4025daad40a5c81a655ca468f1e452b17074239eda8be958b08b8cee592727da692ca6b71f50a08ba742ed58aeb6ef5b1b03a182ea57239793135340eae8ec6d5f9f076ba1def637149ed802a5309d59db4e04ea7efa53b9dbe178f09d5e7dc8a0dcf63b156abefe3844edf37c30c3870b058def781a00d36b873387014f18a742fdcf7c2812b45be1c397638b9c9894a0efae9a5bdd2cbe985085769a508e8f23e2ff299568a945c1e0c68a0568ab4eb73540ecf8d3affbcfbb8229e8fd711e1f25c856a5411ef850897d79952e055e9a048718457c59bc2599707e506aca2d3c773c3062cce61b8140658de1652704f4b4f8beb7326baf579501ed03ebc05740ce71d42dd1e1405d428c18fe9932f04e304a63627492a1269168c91ee22cdc2e10c4a73ea65ad7daefee4acacd85ddd3dd32c1cb6e8ee109f31160e4f6c39e2f0c221d5dd2149be0d5397999535e35b652eb33e1ede48f294b938d6d1f1e832ebd8357eedb34fedb30fa6e5bae449b366877c2e7d6862a63bb9fc2a962e1b25ddcd82dae76af3e91abf2ce9ceacac34f1b44ac61a7ba12c7ed10b89359b6f34348001743750b36e18a3bbc38c7f363bb167daacd6fccd5ba65efad3b2ccd372ed9c5d4acbfe6e7ba021db03654b0e9d4ea889642cab0edd0d8066dd804477ff34eb861f361ca1bbc31f9a644b6299b1ac463e3409f1d33025b17fa66225cd731cffc75cfed13bae34220fb325b1c7885556c537f1cc58562398b1a40e7b95b662fc18f6a649690e1bcb6a04f3f28fea4c66fdaf5b2fa9e3ea7f9f46f462f93219926cd6175f28cb806c6841777beb7e96511ba6e8f6d65f2bfbd80b1bd137bb3d05bc4c93b65a3f6c7af4b8b9b8d5fab292a57dd5eafd9cab8de867b4e6737f56d61e731644565ae3c9b60ac57e0949677fe4464913572223f9b5e796f18c923db9eee0f4b8f9bf34224a1bc2e971634445f175c0e971436943373b6c54c866878d0ad5ecb051a11f37362a947363a3423e6e6c5408e7c646857adcd8a8108f1b1b15da7163a3423737362a547363a3423f6c6c5428c7c646857cd8d8a8108e8d8d0af5b0b151211e36362ab4c3c646856e6c6c54c8c6c646857ed8a8508e8d0af9b051211c1b15ea61a3423c6c5468878d0addd8a8908d8d0ad5d8a8906aa8bb8fb48d160f740361bd54e9cff8cbd7fd5c5f357b652ff14d4c23fe114dcdf7f821d2f16b280f190f1ee34d4d8d8d8f188f9cd8cde843fcf1b1fff132ba23f63f68c48ccf4b6303021160bd44e9f0be24118e8f4f6ae6f2ef3d87645c8fdcf2e357770ba0592f3bdd1d8e5f49d3a45efec71c539ae7c77ea36e7ba0f16b277e21fc35d76b69f974d91ee8331deb58d2216692fb65790a0d655c563afb237b4dd375cbf5da22fa73df3c8746d2d2f05f5a7e7db647b5732cffbeec8590707af0d871836433be91688fd4a3d28aadd64da457b4b51f5ff949929aad4cfd66c7e9a277b74db35c6068178cff8e237523443c6eb3df5e07b192b133c879e8e1e1d9f17cff48c4f5e7bfc77c3c53b3e303749ae556747bc6a1c46f475c69d96962a5d95c86687c7c42f9fc877bbbcd8e679494f9f8b392d244d3fa9f65cda515dd661fbfdeeca7e5a111d0b41710352075df8435a1cd11cf0e1221a22118d75c151cbee1c7eccdb826bc99292b79bf8644b77744b7f7a4593508493a7149494524a592ba9392ba4b6a2fa91790d44a28d19d6a547743e94e22a95149494524252991d49d9494442719d15cf716ddbd43b36808c18c66aff886b9acd6b4097d84353336e2e31eba9b8766d120a4db46ef0e9bb066a6b4d91ba3614aa32186ee3eb27ecb55a42615dd69981a69b55e26ce22356b7648ab25fe68452b2b7d7ea477662611ca981ea9b9963e41549296b587e7469aa64b0753527cdd379168cd48b4648ca4d9e61712ade904296dcd2612245a132b2eebcd48ff7a4c63f78396e8dcec3883604cc921343153135722f7835a2d0e05398264f1efb5a2675be24c9bd5b7f9d76a2ecfebb2ea8eab3f8dc881c05ed7be46b29e31d8f8345ccb2af3fb2359cf1d7c439c6bad92b5cff826d39c970011d75aab85cb37b2b9d5c2f8cc340fbd585292eefccb5ef846cddbb5998e25fd21e97d1309496bd702e11b0d62c980a4c330c4213f2993e9df99670be421769252f214492a3a2d579bcfca6ab3a4ecfe589efe97fa2f69917ff4e4abb4358b1497f4e656730968ee86b361e1c0ee2e6a961386baf10d6998d268687edcd01c2ac6686c6e688d8f1d228f71c70e1e3935ef63e633bd19c74e4c83f3698dc4a78934b9ce6898d25a2dfcfa1abdb2187e3a5f4bc36e6da6614af3d6e3fb477f1d3f0f16a8db3382470c4cf96428231f7b0e417cacb56eaf64414ff9e4ab7cf2f5b99655f663e97384a4622d6d49c5d202fd8c1549b757c458ed5891ccca7a7fbc48f20fdd3f42727fa42ff4a3fd9951a28fd54ea0192522e99945daed25fde75bacb4d9d2a84c32a344b3b29695d6fe8e3f5af1479aa947448e2c27f993a44ababd214a5b73c5d6ecf684f85a25fa4c89cc686eefeebadb4669e4c6916ea41a9819250272f11c4182644788a7080f11908fcf4e11201f21a22127e2211a82e4c8912440423c3e3d3e44403c447c9c68088f0176901c71f10811e1217209f1fc2401e2c9e9214ac233048827c90e3c43443c4362bd5f3b81829e7c22a4cff6c71e2e9faf55a2214449be5689c60fa292f47ef9459e8c555c95ccca7af350d0cf0e0c4dbadb070742e243e41ae2732428c805434db70f3ec4a7c767084fd0d00c11131e2220244360780186d5f8593cef1fd55c6bf59a2f98c17aa10c18ffc107e7217229f1110aeaf6107000ef080239a21085477f4a4c18186274a24d3b2852052d8e729a4b804d51800454da5485287c0e10e66870024af820810c06cd7907ca4a010f745703420203dc601a9c8017831d03e8d1600f1f20e2841fef8e31e95082003add465ed420c91384d05c0c8860c204a5276d8a01045d48810692fe502f88c0eb05476d42420a134db09046977650d11ca4a4d16da25d6e0193ee9860245c92831fdd5af840d20f4296d05d941b231f1a34b4e908097420e90806da0405209ec86205ebd60107d0580207145dca81830158a2b0a23b213528b8a8a5d125cc801908b9d9da0383c8165e92507b4141473a801044972e10810198c00c1e1a749c2f8870451a6dfad185184510163498040dc0b82d629aab0206c80b6516edfdd400003c206da0db0ba29c5592e6d3dee891ae92046b53cdd45f7f39706d8a821bc116ced4353810068e853bdd7c3eda8bc0170053773a819fc7717da30bbb6b6e87af005c9b80805da3c435c77120c8a35375cd719cd719e13813d7719c570aa2e33aced4c120c47125ced41db7127a5da9049372eeebba8f09eee3b82ec551a1e3500d725fe73dc715751cd79d9274393aae2b7d1dc7848ee34a1db7037783e3bcee1403d033b17030701cc7753fba174e1e0705d771a58ee33aefc5755f0ac5a35b713a270eb4c1cb813375dd67e3a5ba8e8ba981c31de0b351f578415d7fdd7180fb3c980fec38af41097044e0ba6b168bd55c731fd7714fe04e9d0e8eebc0aef3386e0747c39db855d7755d69088743d77d1d57e385aa184e03e0565c90a1a300077631701fe771dd05759ee2beee6aa91dc635c06b9718702da48bcee34adc67e25638cf89ae3bb53c2ec7e3baeef36e8582e1408e33d57c3635d4705daa74ea388ee34c1cd7e2bace075e8943759f77c4bd7427c779331c4ad5712713d709e902d005a0a3c16bafb91a5ca9ebb8958fe57d5d0c5dd77125ee9371281b3815d87120c7e283e3388ecbd1f99854a88e8b9d56a68edb813b719cd7711f6784a3a1fbd1cdd0a93a5307721ed7711dd7e268e8549de9e33cd0e3381f9cc7c170286ec5711c102e005dca3371a78feb38aec57125183a1bdcd771e0c7fde8549cd70580f3bcaefbba8ffbd1759c0c5d8a4371a7afe338aec5d1d0a54c1ef79d3e8ee37c388743c72988470090f33814f7e1e050a896ca871c01e004f0715d57ea4ccfb15ea85119e1b88eeb388e9b69539bdd03115c151e9ed0ce8314654ce1c1c5084d5842893082244320a3895c7ad04352e0e08728182d24450ba01072b1487690a2a84a10e521e48814019082113a43cd0f2970d0fca02c20f03184151b7cb8a4389c21705ac081a581011cdf4287b02c046006564527434607ac8cd7e82040143250a801e6ab1afc860b1d05007e6818f880f62f01dd5e779386e98669162b02dddead7cf265f3cb3c5f39b29ca4db43c237a4998eafd293d40d364832a14ca88fe58463427934ac888a007a39c19452a79c6f5583fb56a5d767f356bc966f8c045820a9c4d299509fcc0a0d3923484a29598d9f0c4b111d8f1c60928f2b058141520e4788b424a0fb52df91182b2421c1081d538ae0c0564b0d428ae83e19bc199373d10109a083a452a986a0272fd4d839c22b5284029262402d59d958d908a2e705f3845355480a925145604db04c286f0534fa16c080224e1f4b8906aec68e2292a001940e319e9ccea0004ba70285b4987e7c3abc0f561801001026d5931a40a9c9a7c3f4c308154851c4880f2c0103abe6c98a7824091924a94ed90512b072538353328307c414034e1129d38f52eac4e2f9f029e2d190eaa94252cae4b33274329283a7278532b21a1a83a820e9c62783932ab500b0524aa9785e80e84052ea642400312592408f0572a51d4a2910484987ce8eea44e17032524a9962582182b2e10342ddb031c300648041c789003a080da088d42934ed58d9e1c327c8b4fa4c32802d0f07b5fa707021269429f5d1803a7c3942383087e702c250833a89de0f1e20384e2918503e8a1802a4e27182f96cf86c287d204907cb470424001800940ddf0c356c3c1a8c480a69384fe6cd7c1e7880172020a0852d493082295b5841820f4c91041142206123d98b30209af4c0444992a1a0230630d2cad90133801b04e05f175ca401852624a1003a37bcc81083298a1b7841450776e029408b861756aa920c5ab002115441c5141b90220a2892c0e040135ec002444369a03185580f5078820b5eb6a58925aa4c0748ca54c50398f081e2c1181ea8a2f503e7860d55ea649a2107165734f9110990017c7a7830e0800528c005327a644182117c8088254a7ce0e0b0f1e1608a1494e8e4808332954a80c40a4ca081094040890364000318580209900e101c565c163c2c74677c55fc095d195f12ba218064703de876d08981d2c12a8c0e071d185d0bba127824e88cbe22ce06ae86130de00c2b317437ba17381b2d00005de85a38d6c7d2a9c054873a9d3a9309ec3ecee3b852afd4f072785a247d0d48fa8e94743867a24211ac0e2481e0ca34801e14f14961f3dd584d0182521249df11140ca91970242009c5c3d7831700552ead4e3da79c52ca03822305da20e91425c9469322503e3c28a2b0124212984612b892418ca4557ed122098583a533010169c0084079411503058cf8189054c3a5868b37d34273c2d192861120014e39a6205e0d2fc8698a24efc786a6942a8520c9539564f86454361ef7ad3e99540f4800130f25194aa952d1cac6ca060a070a06130f9e168c28c9e05961042825e986a2afe6af1b68f85aab1aa5968ff366bc215fd0ea46a9332d31e53001812fb0702595077e2bcff33acfe49d3c546a86cf332531b9502328d5622a9938cff4f1f8683eee148397a308540d200892584250444be9cb712262c42704235e5ede696c21093c180307536820e59c0d3d80099e9d02e4e0c3965078020d0eac1a27930f3d20014ae38b30a8d4b0448d131410620121ec008c19b8000b125041130e6002675e025f2a9851e5073a08838b14103104104d7a608204a8484f0d01b801430b58400409463084102f1a66b0650d2c420882935a521a5bb042159af8700386961dcca08b17b8008b2b4ab0840316f00282872548807cc0f8a28b1b64e184111ff4008c1970e1022d3a3085124724c08c2a3bd0810bb46881099e24fde8c3d3b252e5084610020db860c10a50e0810734e0c717104d7c58e2c303a415d3b202c6173480e2020f78f2231006f0e9e101d26259f9fa084600e38b1a74c18215a0a0041e68c2014f7e30804f0e405a3e78d8c4b8d0f2751a473042106a40832eb860c10a4ae081261ee080063c21c2eb872605a009d1c8c2111d6ab00061062618a0049325351a688012372411a0d2f0b6d06901cc4287851308be0f741ee8a8e06e3a1baf860b55331e013c87a1866e062e8749062f060ec7778383010500175cf0585f8d150b6b4586550caa19522d28d3a964ea4a2510f4b8ee9661861b9a959af2648ce6b25e93e6472bd6f2a4a19b33a5a628352b75c44a95dd3d8066a58cc4388cfb8f1597d571f55c1dc8edd24b69a2d3ea291858315374374bb352a76e98cfcafa349bc7c0e410c4cb3afb186a8cee56350b75836e98cbace3eaf916fb210c431fc62e8935dda0920677babb8666a1688d6f58decf999a343713a059a81b50acb266fca3d3c44c6b9e6de9f8ede831302fded3e253144e3fe8b1fc4c1db39a756ac158fe4bb34e499c909c9667e5932eb3eed7ca2ecdd87f56d2dbdd387437ed6404dff0d2c4dad7685eac246e141201e8461161aad2dd8f492afbf3e6eef64eb66eefa8db3bb92374ffb0711dbff71475fcf891f3c3c70f9c1f3d7ef0f8b1e3c7cd0f9b1f353f7ee4e4e4f8c8c1c9e991c3236747ce4d8e4d4e4dce0f1f393e7cf8c0f1d1c3070f1f3b7cdcf8b0f151e3e3074e0e8e0f1c1c9c1e383c7076e0dce0d8e0d4e0fce891d3c3470f9c1e3d7af0e8b1a3c74d0f9b1e353d7ef0c8e1e183070e8f1e3c78f0d8c1e386870d8f1a1e3f76e4ecf0b10367478f1d3c76ecd871b3c36647cd8e1f3739373e6e706e7adcf0b8d971737363735373f323483c0be8c0029884d866af6eb38f25bdf569d9677f1d9f7e93559b8db00b4a155082a8976f443349ca8c5f9e27fe9729a0040a085200019018a3c79314cb5aaf952151041244ba3b84b9cc685b3098cfccfc69e5e9d77afea123ae38c287237a9e54e96e1f9af54402ac274a3cc979f27577a8e36b16ebf04c8194b6dec89b8e6c4b1d2e6ff11021012a6d2d6d7e5aaeb6acb01712ad0185d7fa8d9ce11aec69b6d215cab80c494bea098849c04b770f35cb08308c2041771b35cb08224610a0081a1481bb5b6c5611acee3640b392ba48d222298945440b8840ea0e1f539a784ff367081399997f99fb8d9a36977526fcf1f05fe64fcb36dfc45cfb5c6760373b605e52975977790b76bbb4fc18cc67c21b25ffc2d83089890943189920981b2630b19ab7ec245997789891be05cbf84798cfccca3a96d446ef28b326158d70bd9e29495699928effa5e3ea32ebb05786dda8699e2e03ab7daeaf7f194c6661183696f6464dffef09cb4a69363b49454bd62c7622b8216a3004144314695c47b7d9af75192b960ff399b0acb5d1e55ec6d0ea3f4398641819fda1d7f8351109bfc8cf63f9582726e774999cf3a6e4451333ad65fac235df1f1f97d6c47f6f237da1dbfd7387d07f86307192c62c59c43fdb9ab5a487b792baacbacccf68c68e710802531f1f9f63f9d8a9670abbd971c2887868b3a59151256d1ecaacd7dee638638ba9cbe07bdabfb466afac24499787d8f1696db634f2b7e123f7c7fa97e2a76599fd19c28488873124493d53c7b9baacba0d3e61e8cdb77a782b353d06563379fec07c7c19189759c7e971e3376f44c9dbebe02136e9cd9e1f63fa7556563b2b2dd28f7f3ac998465426e919cb4cd69e2763b44cf264acfa640b44e45a9a58d2171a82e9ce9191ee58a0a1c7d865b347b091688fdc4afa632dd150b6406379da9e6c817092d247022014b6dcdd3511e935968f6d4a6c4132decaf8c754a9a6a70af9b3907eb132cb5b3f4398d0c49a88447333f1945cc7bf220b37d14dd46a11d5ec956198be53bac35b4c26fb5b45775f2487fd7933cc653e365f217dcbb7bbc35c633ffadfb7b19c34a13b246ab5889c44e90e9d30e198da5c2c274c626e24496bbed484295de619dd1dcad08c63279629e949c58f391dcd281dc68cff3118c7f845f1c466d2486b62cdb466f349bd74993cba3b246b2e4f1faf2d5de35fa2f16d944634fe89b1cf2532d69d71ac3b2c7bc42d8642a152ef3ef8d3885028542a977fc4639984164787b994924b2a565939ab9896afb5597f1a51194587d4716595b42f6d99d3dd2135c9a7f95f9ad3acda19a1bd7dcc71f513cb34ab268390d26c7eadec2fad315113d23d2b2bfdebb4d9692f2d97d56649b736e3ea1887b1c66a38ba4f724a778738e3ea638d915574371049cb6ea334b2a6bbb475ac31f2d4dd1d7ea6a37ba69effe6d9eeb08c96e8ee46a150a9b0a250a8540c0d9ec13458b4e34c59698dc6d630c4a21d617ec03e78cd667c0e41a150291d92acb9aca793fb41b11bade910059121313a352eef19f3b1a45314fd14e9a8618424494b0b047397f97808fb9a1d82b9ccc75b3098cf84b792ba63eadd6d43b3ea1095d6dd4c9a5559342bd0a2e80e69b9c2dce6b2ca5ceb2f9ee30bd18c7478adccbc2ff3a3fa46f487d633197477081bcbb307d6825998cf84d8496acd6b65a7ec6d3c3321b31d0568966c0a320dc82c4b260024cdfa305e4a87314634c338ec071ff04c0b2984c16c69e337d2f6a5bf63d8e83234b2faf869d9818c3556567f9192410ffb1e2f43772f69d6188371033d138e35d69a09613018cc6576ac319f21c99ab1531f65c62f677568f41b693a75a10fa2de6455345f364acbb98a55f6e36b2c4fd95863638dbd5863a9c35bcc45d37349eba547fea51da213e33230f74b9b7d9b7dd15ea57dbd32a6a49168c91d4bb38d3eaeb1ee74770cdd5d92b0bb43121ed918f56ccb2aabf78f7eb0511a51586d3124fa13e2b19233a7fe31fc648c92951e51c725fd5ab5d952b1c68c682e7dc29c7fe636dfb759123bc5b9d2eedb5e96c913ff757988c7928efe47359fe2e3d28a5efedbf2c71c67b3ac3f2175b2fc5cd6ba53d69f90acb2996bdd48a43953af5113636bba2def89cbf7598d79c6f449cf34fb78689e2f9615bbf9f746e23ad698539192a68c96b5f698ee84a5354f1f6b2c07313e4952c7d85e91fa97b686abe98f69cc9f466465b52784c16c89a4c7a7c7e9acac7f6562f5f28f20d19fd0de0fe2c7c3fc3324bcc5fc4f921ee19a1f9715c88546d21a094b8be95f1f494baba34f5048c4c7737996b3faf78f2c769cabcfcaead7ca1ce350dafae3b55614a90cce26cd389fd66fe4ccffe87669ad760e79180383c71a83d5c03ef6a5755cd62faddf62ee321836da7a3f08e665f5318b7f14e2f18d446afa8d7c7ca989311547fad7924e873ca489f4f1c78ae9fe905051f34e3c7c919af82f7699f14dd39a9ef159d41d711221495a1bb296c865b347ac251aebcefdd286245ba0f16f359178b2051aeb8e0d929549c61fcbb387ccd95e1a51c62711fc4667ad66877c5077d7e8e660d0dd4a9a35244a774391c4075131494f27ef1f0ba2e2bf8eab873b44908c7f6dc6e579cd98b1a430f7cbb2ca602b96d6711d6d69c4c3181817faf9f19066ad900bfdfc78c69fe911bfb55a886b9faf75fc4794d2f00d6f8d09b7640a4b84d0edb73772cfe55f6aba636b7a2b3ff6d8289359b246b18c3ee9615965d6de91dedb1bbd6af5965f9bd14b336770691fd7717c551abeb5da0c49cd9a5fe5796da6f7145fb49a48ffbabdc6f233d2d332fdf1357ea6e25ff3c7d71f5592becc7f2392a4af5b9eaffc3857b2e6f335d6d85863e5599ea3599ee38fe59bf8c4af17129d292bcde5696b334f1add5babbd9248805969f87eacf6b527493b339295260281244a377e72a44fbacd8e738d7995c94eece36759a5616e4977d36615b981e4871edf8903a9d901c40d7ef25652c75fbb149fb61efd0dca8fef67192d5ff463f9d2a73dfe6c8f5e37b1fc5a264f2afbf1f54622bdf995319521fdcbe63ffa4c674ffe589ed834122d599e9fed51b6472f5c492391923192626bda6bd24c479abff6ca8f6fb27f7cc3d889e95fb3e6af59fc366ad6fcca22bdf9afedcb9759b359f3cb8e27b6e3895f988e9f1fd3c4d31ed52ac596146f7f64cbf367b456ff9ae559b1cd8bbe30bdf4358ef4f1a54f8af745be5852f17fac2f918ee457d92b3f9e8d67d1eb966becc4af71a4b1b38c9df895ebe9a4e6f2b41fa3b18fd158cbac0852e7b316f37113d6ccc4c699af37ef989979928a7846449aa1a9894834b82622f56ca0bb5dcdea89b17a7ce86e988b8e8f1c06f399128987313a74c0382669793f97d5f15f4a8a3fba2f11050a51588008b3235ab6af23fcfad9fdda51bd3fbe302549fac2f44513338d1dc10859187fac47199f352cb8ba6e6f01a8f71b49331d9f565627aa3169c6b59a6ff9ab68f3e39fddd3ac650bd772668893ee9adbd28f788208ff57765ff67af15ad907f17f65d41c4b7a67e39324cdf8f5d996dd9c0bdd1d6b962b0b2e326c20200aae351a11d71a52b35c4074e33f31ae3434f1f32dd74b4d9a6c3a90f1efcf66f5e67bfe51c5e5cb7e746ad6681e477aab48f69a9fe95ff1ef69e6f26be38be569cb92be6e48f69a33b57a93fdc7caf375544f27ffc23fa3e6cba44f7be5c7f8954f4bcbd596afcd18a9cafe9a2f92ac462f5aaeb697d1a5b5bf3a367407928880dfbce5570e4b40aa29ebac5e243950c1ca41033904f50f4d7c6852b357a6a35941a410e406419084342b484e37ccc7f7a7e5276314665f189f7e23497a96b4e6f73325fd635e523124e939d6a33a96d5cbfcb3f18c05610132a5bb7f6822e3323e34093fe624596d3e96d46fcd0e75370dcd02a281ee6e350b08ad7f68e2e3df7a3af9eb32e3d344fad731b58d25a5398d08481120387e68e23263751f9a845fb3623e6d8b072db37ee4a70f14416849d1e1cf10268ec57a747fa8e4b97f8464fc23fc834f4a43e249323e2e93f4208df5659ef6ce5e19e94b9d20e22bc98f6532fbfa6bc5dbcd25cfdde1870d37078d9c28dd1d7eb6473e2bebcc5ff369d931b5e53420270746c394f6fad18a300f7fb46269abf72809721aab57c4b113fbb53e7ef6fbb45c9f88e5a34a773776d3df6d767aa9e92e1f51b22d6d5fba8be503a9f1e9f29133d2a7e1680136569887f4d220a7a7bbf2a5794876b4836bfc6c8146fa46c8737c9a3834d237f2998e66921fed90b1ac3a94d5e8452f352fb531990c4969b34466657d52bc892fab47e8a541a3ce78bfb65a78c8f8f765b27327975fcd2afed5a1616a843eed253aa9e5f2b4e26b866b66ed1af9202a7ebdee8fe5e39fd1d7f8b59bf19f4e5e6848f92612b3768d98f4e7ff12ede00f1ab325f1eb6b358f65d56156561aa6e41066f108e2a1da7146df9f5d4aba238d75bcd4669e3ba2a87dae3dcdda210489d6fc69b9b4d93bf33fa2b43fff74e266edf4a7e5ee9f9699f1a68cee86f159959556f45b2cd7da7dd34b4acbd7d67268d68d13dd8d6b79c23866dd1ce0864977ebcc20bd97b6745a3e765c6f3a964d1930b5b3569168cdcebedcd12c9b087c1015bb1558b0d9b1f18671f2cbf133adb9ceec972f22bd8dba07fd0819f1a78de5e3eec669560d19dd358eeb2d9bb5dfe187480d16ac9a0af88d2c5f4e6aa3e7e0f86714e3afbdc64b6da5cd8eaff1317ed15ed3de0fc265d6afc5f5a88e4eef8f6fb367b3f639d33fba8f5f687c4cffc8f863c9731be95fd29565744808d72a25c556abb4b59a155f635975c8480f742979cefede8070643b62ae118787088d88a78819946449129a587bb84984ea27b9318196fcd8318443e484072636620c59cbb430cb28a631bad696bf0c431a9a163673a679eb885092f2b432ff98df680dc6c3208c441e80848a14595a912b239dafe47bf0e1a3f628e9a132312a12da41144490018cd06434fa63c4c3a4b683e35a3379d61ca7c7cd8ccbc8903fb6349be3da7d9bfdffcea8df627f9fe63ee593afdbcdf8c592bec6b75992a4b46cfef8125f48b4362beb8c66ac53fb1abd34e8da7c8a8fc4837f468390e80f49c53ab34790e8cf1fd54c818c449a8790e8cfacac3f46e4b99391786a982e111f8947761494cbafe51ff91b74daaf5522f36f90ec28e8c4d97c51a4f8af155fa58e8e79da5b4467f4d1197d88dcdee875bb7f64455c479dd167fcc7af9a1d4b7a755eac63595f7f54b348adacf4d15112447c25e22b19bfe6833f67d72d579d98bf67a6e5ac0e2971199820e22bc138ffd0d3869074fcb1b4e20fcdca6a24d29d5cc54a9a483c635975a82455924f135722f9da986ba44f73d922e5e3bf4137e317ca2792f16b95a87662ecfa6b87e88c3e43c6afe6298a48feda21f8ebcd7f6b95924346fa9796e9a541a6495fb9fc5a39ab1853d3a4af6b3325492bb65a3c8e5a2ddc6a9595d2447aa9f9ca76a446a435c51fed90276c1cc08bee069766c524c07308e2de82c13c636afa8b253d1a4b2aeae8e49c41c457329248c6d285f3cf2e25ba610bf4ff339a8766f5a7b415df5782efe373bc567ca1276375569e3d9f6ded03012803100bc08a862af7ccdf42c3005a68e8195a6678a2bb9774cb0c3c66b4e4c8418ea4961c322d395a5a64a022c3930ecb2a2383ebe333e3b3e8b437ff8bc3696f493c8c817d8d8e32ae8af4a4ec8f2ac698225999a5e1bf32a75edaa839d412431662300194c6659296183c1c311826975fc341b4dcb842086ebc5a6ebc00430f18d002834e0b0c31b4bc10839617ae6879616c7921c87d58900582f5d457467a98cf604a83f90cac0616e421a6e639abb11ac575b499a4947669f99e268c3a2e6b594797ebd0e1d2a1c35b45807c80845c4bc21b923d4d1fbfb4511363b05b6c40c1c613367eb011430b00a4d05abb21d91aecfe0dc95e6bf30bb500a00700a81617b67081d6e24290ee0e5fd4e6b718180c06df5ada9811a6a5258b96966f69796969b9d1d2a26a61cd84b88e25cd2245fa216fc1603e13caaccb484a7b4ad2bf41210c0623492ac29aabc0d7ec90e3592dfaeb406a5cc769f834400e0e0ce632b03006c706c6c78739252d8d96335fab390889fe8432eb31304e00bfd90173230abb368bb4a7fc7306067399cd2769facd8ef186f46638dede080623b31f44c530c7958e233dba328b636e6f04e323adc9fca523ed71b6404fc66899642c93c8c81fb2401d8c10a0ee7ebd5e3437931c6dedcd5b7ebd79232a6d257243b23788a47f49928a34978e8c8ecc50aba513649a2ec734154466f469b5885c468728c8b53d282674b74eb7a044d0823251e1d3b2dfcff688270626d3d85f93d61c66f3d79bcb1fe97d9aed4b18119f1711cfd08b07221f2124468892b85e433e433c403c45606e73ab25237f0826e3e3b8242d6cf4c93f34d62763f5becd71ae3a7410f1f1fcd8e5637eac850e1738bae5ab195bbc2db4784ee8f2bcd9f1d79bec7fa7bbafd0e235f1765a3c1e6d65a58f9595f5da7bff4546c3602ddd1626fdebea9ce8ee2e91b474b4263b23ed6ae95c30b58d2ff4245975001a6ae1ac68e198d8fb41b470ac0ec599923656b19e432ddd821a6de9574bdbd0dd21ecaf1d02a3224f4b771a56846998ddb43474d0b9bd0eb7d741e7f63a04115fc98f56dcc2175b80d2dd46e2d346cfa7dd42132d2c410b4f68e1ef298a6ff3db1bc54e7cbacd7e2375e8d0a123c4245975d0028feef048b3d0e0028d1074370c0936f4a315870c39017c88edd130bb433460ba43933ecd310ef6d6a358ed3176c57ec95016a6d0dd61aeb537ba4855f63728a7c7d050e8b9da18f5507c13df7fc9b4766d4f4e0f8c5fdc737a0c0df9f83fe290bf88c788846f648f449ae96869981ac9f847d2c2b8f96f5473dc1a807be8a5ad34182e5d563d06c61f93b4c74398673a9e97e86bb484fdb579c8f149cb6e4bb7b9acc262646060f8afcc7e7e99cce2eab3b2d6fb6425c5994c9b5552aca62bc85318774a7f3cccb8920ed3210a1296b6ac323b934feb76e6d9964876c7c30fa2e299b23aa54a6c10ae36fab7a4b5cf1e5a4c5297f95925027bd890c7601cb0496b308e3dc46fd29f7c9a31b8ac446ed2a7b5be4681c437877c2c111eff8190782c51cd0e292d1049d221570085a0614ab3404b30238c1970618609cce08019183043026624993184194298d6636e80f196d378cc0d5e73991bbc655a97b9c14b18f7960cf61f7cf8d18aadee528e1f0740c109559e30a3c70c1918da84ae0282a63f7a1520cef1af97d59378abceec91d6634a0b7a4c692616ab6933bef87eedc4a515652f8469a644acac7cd9fb41bc4a5b22e55aab45f46269cb570bcf94b6e227d8e8f696788ef69a7e736cef886f38d2bf19b75a4ee09a7046136c943c622c8cfbb7603f9ad4868d444b1a89967c8d231de9d39ae0f348b507df70fc4b69f855bb6f9ab5cfdd8d6409445802184b9041e31b2e218a6e6f612b2b7d9674733f1f687de0860fe058028fb7ca3aab3dd86649d2756997d66a75774e194c28630665a000df90e6551391fe5546ee6e39c6d43c4fb3368eadd60d31bdd41cad986d5fde3c5a717c7c96e7eb8dac293b325f3f96a7ecef1f9116e33ae63abe7d236b92247d7d3e63b596f54dfbd89298fcd2ac5ddaab7cd3dabeecee218ed0e30838f00def67938ef46584297d6dbdb956cbfa2aa908336b368213dd46a05d23e458239c8a3083ee0e5de31701c9cd9a8b60a4696c6ed64c849a2b119cb89597667c2218694a44841ac2598d39ae3a3e2e43c3943684da106e90b10519072063ec6ef94ce88f2d3944081010821142f011043282e044109a344c48665beeee61cb899c002e644b0762fbb208103600840b000109082e4060f981163f30fa010f1fc068981a69b58888f88c3b2cc6268c81655b9a358f323ee1acaca65945186cc6ac2c1f903ea0f101d80332ba7b28634b6257cdde1bfd19857ac0f1e0033c28e2818d168c61c5184c7a346b0c01ec60ca0e62b003720736c438438c29c440b299f9b03a499e3292baac56f75c61302fab59b3cdd23ee6720f5b2d2298cbac87b0b13ced0ecc6fb1f14df39c5d4a13654437129fe6f93e8c0806232212657656566cc9216625fa92d6f16bfe477566d6fc326b86f94c48c4c7ddac99344f3fa2614a7bec3022189d553c43440739d0810874c0044b074a7420240c2584b182306461b01a071c5c664b3ae31eca2c4c08068b11824102f399d0ac44b6078239ceb5c6139a355b9a0de633e111a1243339333d44499c7a0562d64cbb563613961506f356f82f738cc3fdb1fca35aa5241e6130219fc774c7ac4461bed694fdf760b3128566cdf8ed8ca46569b3a35989427cdf346bf6a3af34917a7dd3d2cfb79dfb414468a644c25bb666cdfeb45cda27abec9e326ffee8d8e8d21a4d2c7f07cb78887120dd3da49f4b7ae93993918f73f5db1bddaf9de5fbc3622ccde63230b01fff66d21f6c56222f6db6d4c1ac4463597bb2052adf444292350fd5a05b86eeae8694be3a8f2d29c2983563b39a95a87c1389ce485a3060dde158c93a92d67d7c9a98ab0dffd0e4be1d4fec431307c3a6db87261ebe91b5d9f5a525825d763c876eafc358c773e88b2afd450dccd3c8da23b62f91ec0e4952f18b22379842873750c00d6a3abcbd91edcb1898ed4b188c7b91042f7ac0f2820a2f0ed0dd3a34cb8b1a1b14a13b8cb18141e23337178000c2d7eb864f1992a4222c5b2b04cbe5d3f2e9c2f4f646aff1cbf365c713dbf1c4e1ed8ddc8e2786d9f11c321d0962839aee1ea30649d45503a03ca4a3d36a1105098264775a2da2d7ebf64634e08206960639a4c8e0e6e5531a693e26492afe84a585bd606e7369fd5ad14b4a064961220500ddcdd3ac197ca1c40c889841902eb2d0dd3bcdea4205dd1d569a78e218d8f8491e866f64693349561b2ddb98233d2da9a5e5bfb61e51dac770add1dc5eaf9292a60b89fee0af328b447ffc6b95288c81893ee3363018878930bf9fbf462f3571b624a6b1af47f566b1665cc98a3fc6b3e3da99d1fb47d4c3d148ac3766b1dbd1edac1c92312565be563bbcbd91cd2e835fac339bfd6bb65d3b737b23a7a5cb67654d8d4d8d794c45fa3098db184952d9696f3032e5c7863cbce5d3e21a96d64b4a71c5b95e5a6dd68887d83cfdcb4ae26a7463d66df6f191aaccb43797a4b526cdb62cabff120f614030b7990182f98d34719581fdabb4b0987c9af9b4de23a4c4330ea51dc72763b4c465bdf41cc9d3b4a67f6c24bfcaae95dd375d563d46e630b74baf356b4fe4e18d1ad9d9e7b2d21236049b6182640991ccfaacac25ac0676cffca5f5b7b90c120f6146888a00f11c791101f1fc14e1197a0121f1211a924488680808c99097100f11a024b05c7e6d36b3c4c38c2be92395c9eccc8ff4e69aff1221200fcb775c91de432cb3b3f1499b2d8d66aa1567a8d3c4133b7e6ccd7b9a97e2fb4723f5b27c6c6922b53d69d628d2fbb53d1ec6587f4c7ebedd3f7a18c7f52ff55969c9fb476fb33497c1a4156db6d774b1d24c7a67369bb72b969507d7cffdecf95a1a162b798b9da4acac7556563256a333bf5616732224404e3494e4e796eb3dc51f720178a84307fe32e35be3b88666a5e1dbeb8064487ba03d31ca0fa248e90e1de9bb2ba296272d531e3840dc384da42459a30809dd96339f2e8b4e8c5c1d4692d508f6360732a389d6e4e94e06e12dbf8c621aa657b4f1c0395d561dc897f68a65bd511b4979e0d0446bfe98f15934d299abc494d69a95b5bc7489970023a62b84342c5b72086649ccf3312add0d6b560a4c90820aa48001311a4b414d3853569aeab3b206fd10c965adbde8ccccd79b8f5aadd2d6da092ed0dd364a2cd1b75a2f7aa9f922495ada57492d39daec15675e2415ebb51957daccebe58d6048b34a80a333a6e4688517dded3a0ee333374a52c7d58a055871763775d2330ea5ed26963d56c83ced2d62eff760eff760eff780b325876c96366481ecfd1e3e888a7fb44346fa3424fa7364cd5a3d325e6ae4c80621d1b252da10facaae91fea51195944684f11ff9b951f2c717f6b951f2897c696ba64154fc41d42192e6d20651af3582447fba9b05b5b37cd54ea0f17970e963d6ae1124fa836f884ffa7a7db64afe06c98e825e369ff445da9b4b2bbe4c9bc5d763245a7be1bf337bcd108861747ba31030a0bb3d538ff9f3f64630fd1911821b345322237d23b5bf413fbbf488b5443fda21b607eaf6962012d4ed39c06b80d1a5b5fb992e316bd748b7c700b3768de41309ce96c4b737badd5cbedb12df10d3e45a8b9df826fec53fbe4afaa47834531391eebf6a2212be5fefa5310f20c1036378408a0748d0de7ac9780b97b65697f1d610a2f694689d20e2fd5186e4be50ab855f26c664a5f989e09fd5319f260d5323d9dafba2616a44e769f747fa465a2db1de2f7ff6e21223f1c5378770c55f7e91217bbf87d2020511fdec2211dd722569bef6a7e6e666878d0a8d3e483f96d4954f3b23bb47659d91dda31e2125344c8df40829b997c708098f50fea16e13ab0339dd6dd26b03d22c0e4ca1bd65d26b739b756a221324ad164e8f1b231c70c2811f5354c9e419f3e5b53118cf15dbb0744d7182299e90c9e5bba6a053f068db9736fb0690b001263610a443588ccb8c3e304a695e9eb273040223493a04e674033068a0092db33c1a88a201b27f6822332beb2d065613998ce50fc13ecb2c0cfeef91420b3f9a25850ca4c852ccd03f347119994c9e2379da284c1045139c6bed69d915e6d2d24e5a06a680c7f23310820cd40cd8687a3100020c2cc140ffd0c465a008018582f5840f9e60c113639324156df97a239a6146348ff409960b5c0003173022f3598cd92c2373e9053e0bf4c0024f30a579492dd06301182a20a5e6256d564799ad001315c8f9a189676c4597f1a14988f1e9d8b419fb1227a4e004069c30327ead665abed665687599fb475fdea887d46962a6b51d9c405160091488c00f4d5c86888f8cfbd0c466ea23a551a086023113c8c10492265023012048004a777519bf91311ab3d7745cfda8bc7f6467fee3e10d9ff6feaca4634c3eedcd66bfb65e73a47f61fc0cc7bf5f1b6fe4fdea37090cb12470838c0f4dc272687c4c83f07fcff849846e3b9095f610956f44891e0b7dbd8df4b1502e7fc80581a0eed048b4a4e7f171340b02304d8c11c65c13e96b380b1fa60926ba69a6ef6a8276e9c79a10d21d3ea0ca49cbf50128a0b407203dc0bb6332d2293629ed7e90875e568f093fac30fe98da1e6062c28aee9e95359ff6529bdbd2a90ca63616134cba3bc4941467fe0e61c23b7c1187662d51a543d91256dc97dd4a5a736b8996a055ff180cb604eb56960e2823740013ddfdb56bc74a4d07e484a51d71b5f92d8624298c97366ba4590d28231c3fff35c9188ca80124286bed7efe11e340a4a343927488ce107c1b724c9d28481024bba3a3130313a42632c9f8471d24bba353139930600706744a444189283afc5bf18fe3cf10267e2be9acb4a4db524809022441055612484822021de29f214c603e133e254f9192a4cd01a55aa824cf8c06ba9e844c2944233202000000f31200304020180c476452b96c1a34e3001480057ace5e7e4c9548c32488410a198408328400013200020202333509004f93c99bfdef05ff1b6fc5949293fe60656e69192fa18b52159308d8a42961e5d28be29c68ca03cdd3552ba7ffec9398182ab8dc959551f0ec827726ff78c454d23985dd3bf4329b9e3186348f01ba5e0207ede35aa9f38c95096b8d4ed0d5a1aad384b64dd458c9be5d3e3492058dd8b6469c49786408fc2fcf6e165591795e25bf9c9f262df81e48e1ea04fa1bd7da5738299423ed2fe53e3b94ab158439898a8af92a11c4c79b641e52a78b0200a187a6d28cf678a348a46609185adb9456a200c2f812b3565b71bdf3782c1b420bebe828d6e79a1506f8337677d2c461d5d957dd15d0af17026483b639ae4c278d296cb89186c78d2ac8a46c9718f93437b3775a09baaea229ad1afb9de7b543b1e99426fc4516b18e742587ecfb9760ecdcd9320ec02adb4e5a8aebc90d4d105dd50395d08060da1f85aa7e6dce12d1b8ca75e56c13a28b33dd9809cca7a7ee0a9361b58be6cf136d04516f6f34ed047e2967e2c514f7b1bf34fbe0119e9c8bc3baad1ead327cdfda9a721bad5d32cd67fde015afa80f4383cf156c3f41dc1f495e2fd7a76a31ff1e3cf398d944c6e5a022a210f4fd94bc23e21a654c813f666a711777a907a6877224c0eed034bab2b3a9146d0d7a0be4536db1d220814fef3042f236e85a393beb030f8f2cc339281fb21ca38038b9fb9368624f7944fc4e33ada172e8eaa225a285db028c8cd478b16c64878a1aa060298a37def82976befc51c87f40fd6ee0a0eece000eaca36673f578cc23b24634fef60aa0c3948624f03ca06d940e1f30d76354dbd748160536a5ad588316145d1aeb5204081abff894e124c49bedae89dd3eaa1ce8e3ce3bd7e3f88328fa4c655c717fdb61df9bcf8f25c25b98eeac2fe84804b05764048f6e189d86559622e36b73903d88cd1c4331034145bd567614bcc0628f44dc1ed773b9eff89ccad5e54bea21412afddf12550718e008a180672ee207754802891294fb974ca31dfe369ffde5f11b70c37d49010f6559777a77de3cd4e1dac1084b9996f24185dcdd1a247fe446c3c1c927c1a6d92dac25afc72178221ec6c3de8fa327eb70c13b5476d7781914a2a688b48cf66d32b94065f0a573be30c3ee8ed9950c3ab902b0e308904262ffbaf9fcddbca30982bc2090690d8eea6737687f5a5e5d1278e7e04a600b01486ed53c8fbbd26851598063c5e26f3e57c40a7c3eb03d21f0ccd5776e9514102d3cec4896f575c700ae266b012affca53c10c2cddfc0c2de64f0b58d4c17c94fdb87ba188956739c46f21feac34994e16868f1d18fecab1b34092ebb2de8e6b21c9b06ce202ef644bab0d237a4d6452a289c8927df8da4c9624236b0c873d52abc30d83a2b554924e75527f92943b2044877b88113256c8691c69fa9cac5801255c781086d289d208ff06f1cab3c2ea1ce297d48e22f21e5eeb606b11a16f2587836768527106124db24c2a991b2b673a805f15ab01bb127db82b9b8f81c93fbd1663e1e3bc88900d9d6c2afaa1b535fe85d3e74b91c989d140e4f14ed48d7544bbd1d150a3d8e52e0d162020e8f1a8ba0e83cfb73ab921093f1b6add3d33b22a1a4196daa88715ec9a458b561adbb5876a92123905b5ac132d743aa7e3be67649806dc9064737bb9f6e4086a83c84a04ede81b54d3990ad66ce1c967bacd18c2e1fdbc0a7d34db7e51a5e744af4f32eedfe06e4dac6d7a2b9ed9eabc87700b2b773ea8fa73d60b031dd0481e0c20f38474e2c2cfc9b8eaa18b629edfa884dea65bd029d667cb24c9608df995ed0e42133ddc0960516e007635484e0a2d4d1a313189af2d9093804f5cd80a3c75caef2223ff6788d11c84dadabbf08f5989c6e6a6326dbcb10f22c69aab5fb70e9ed21dd10108ab658d4703efb928cd01d57eb912b314b034313e1e71f54627d44ec63c32a43a93b33cb581ac8ac9b3a9ed58bde221ccee51aca21bd49af0b004c558795de140df74dabb3aa22c3e820a50a06af9c33e5e9979bd9d1ca02f41a9337346555c05151d64e0b568a0b4ff57b33c8555b8ca2d57478cea43858b4e094b12ab93ee682ce6c7cb31f73f47e553aa9d70fd1ce583cc53ea50c4d86d5731f3530319cc891826d84847d0cd94386d214caa64cb8ba50e250fc856998f3f2e01ad3dc480f7ace45dd13e08609fb49d1cbc095f718ed105dd48c519098a292bc96f49707a8e3ffb5a12d20dbead10369dec848263bd0dcbe15bbf3f4347ec36b372160d8ec7f0abecf1b4f90fee89cc47b42c4d53b319cd41b9666709febe4a188d0a54986fcb014bfea76180c25e0a53cdbdb7a145af3326daa8b2826f7a441e42dfeca8d9d032771206501ae730bd8366366be8865661d1e981c5e8daa81766a34944836230c73770cdab0614ae10645b25b42972cd79558502116506377a3204df38a5fc44c85f70c516856feae5e1397dc6bda3ac79dddf393c50bb507544ec18b3a22111b7645673a3140d2ab638f295c2048f45f31144d8723be6f19fc1ec6dafc24da55270979a6ed52d93d05a1e46317390b78ddde7982b028826e2ba4ed6841b88f90e4093bd51685b98b4359915a235fd88077fa7a5985e1261c725ee10432eadeb671d7c0892283cc0de1d05863aa560e0df19cc614f2203a0a3f2b24945fd020a815645fd328501677d7da7997cf6ccc296facb50c72184700febaa3f29379000ee74daae97835bb1704cc7c7aebd731f068c1302bdabd958d20587d08ddf2e3370891331cf349b13a12fe243bfea603c6d2130bee8f39f497aa4d0e9014832589a55b2606eafd64e74c45d63ac211c328c5544082d941aadb6a4e14408f3d210bec024d966db802e6b1a6e92a31711e8659025bfa861e91c47431eba1679a1b0c667bf5bbcc8eb94924f68a610258bdec139bb7194f5ef71847e5fb6fbfca391838cfdaee388456423c5fcfa0a0b48ad39c036391d15ad6053115e60313341209a595230dd024a01f88f03a67b086d59a4e66a1025d6e73574aa2a665d93f382b332d73f708fd3b786421a1f1c2130432dce54d7266c91bea10d82486b81fddcd81704b15b2c8caf66da6543e4e706e10f0a5bde1ad69c6d25cad03accafab0c01f5fa11428c5437ecfc8c2491813b5d415135f220f447eac78aede51b1760eef1e8ea1d225d77e0ca99952f4685ea5555441b7fe8cf1bc7a16717768451e021c9fb4103e5fad38a4e8f2305ca4e2f42210c24e40d199f418414de7c357e30900a6e03d083c570f386f65ce56c8c664da65534557ab08bc4dcd091f88ffb188430d84ab2dac5f6a26d4716b9a85f230b173034b6412f4caeaec08751061abb50e6598e8224dc006d5e33e7d6303974458437b54b317668eb8ae6ed9374530e48f91a7ddcd9d352d0084d2a751f75c83b17fc3df58cca4ab26eb0a6d87d4916329996fa1768d62ab8ec535a33a157147bf509b885a9492786f91d25e568943c0b5eec4b5be70a0e32844a508ef3ae862d5a877d0984c4f403ba0cd74aaf32f4087b56abf5b67befdcda921bddd5fe86bda1e95f361dd586e0408d777a151ef74941b8a22c282e8fe3d2bb825df0f4918ade91db90fef004830a1f11cc14a31d5ce3fcc20dc25ad56973d65bf37b0ad3b1b5e1f5825f2f2aadcd3380cac56b7a0c36cadab64c4cebe98f56d697865bf525314da7d3f859b8b2a03181ff4ae0c63ae98ad07608038dd6119cd7c68db84b581edeaaa1ee9a14aca5bd937fa01de9b64ed297a0583f96e266991305482a8795edfbed07934afbc9a6d21ac8c1827e2a099c6611a2d8e426a7ae4d63b05106014bd3b7cda41a5847ceeae8a228df082b689a4eecf2859c3660ea57994ea0222507aaa6654327da233671ba37b342473f626d5f80ad10da3f5632f725cc8eb60ece30ab6e663f0d0a333aba9e544ce7135c46a6ead89c57f28e6997bb0d8b15ec60d471bac4f0cb885bc4383f1e54b166bd940342ae71abdab6876480c835c83f40c6b34738fa29083ef0ba9d15c65b0a9de488e508e10869e09a5bae7a2fd6943bae4a757d9253ae6c1567ef9a780c4a916332df5b2904c68657c9064871717ba4a1172dc4b63b09e35b5885e4408fcf1649a37aec887a51a75e5d644197daa952f82bff34f6e2bc1d485872b953078a8b094c74d913c7d9e059ce192f40a3abb35573c53014efa2351598f9e45f5b57b903467478b6739d8b1e9e78f22d3746fe1c84e7d66681c6338e4dce5f5297a579d740495d90af11514854dcca137aa5d57f1a3f6ea66cffa1f679b9e709c06452a41ff488c1ea5a001413b2981029580691dd271c2fc2083947926a3869ce73b1171e1652f841d49ce1e8927b38ccf33d983762ad071b0d94354625dc0cc25718a1cf7e15ce18c16301883a25d8267922cb01140da25e9d922928f7936bf9dd4279653a26c3d356a9492c27e6cd0c119c555ae7e6865381012e6dd82009f19f5ba9ffc4f2673399a1d0e21432c6e4ae1be5e7377a5d338e6bc3ad972fde900f2e832f76f7e61de4a2fc00135087b18701b95e9af266516e6f9406587d6a3f81ae8af41a160925dccd01e42745ca21515b169e1754e96bbd76eb31199e62611fb6aa4b024a4958ea7206a62032577f55d7e2c43a581b3a2012a99381f2f91d4c365b2bd4cd8f0dc0047f909515fb5e980fc1c99415f4d7c8553107bcdd4c92e181ff609701ebc0e4f3e5ff4730b21f7a85e013c8c6b2352728aa559794bb223dbfb79f3365616115f09d7e0c8d8f6e4463a0bc02943aada8851655500acb6d30e10b15bd092c5945b34f1f90ab452cbcb0f98864690e7f0e5157ad9843cd2974349ad2a05a88c458e81959359da0d5158f1ae967423e5c75cc3a8f9b9053c3a36580b37eba0960ba1887ab6b365eed7312e4466407d0536fba8957fd5708e619de1688e2d37b7a6311deec91e5e23db2828943c5eb0ff4c03391516b8be19c01c969378add7c1b1188bd3d0fc3a3305fbeb467864c17f19974c24cc284c5e8cd02b1555a91834dd6fbb7489dadf6ba1fb94cc80f7b505f39a0301b787009a6acb447acfee1f7a0f44d8c06bc207dbe45524c3206963699dea3a5af4c00f7babf6c21657ac91d433f9e20a4352ec7c8a0ead9e45077b0e426df1c0243ba9c2a7bc8f94d05cabed5d1a0968bc322aeb58b1c65c5a894858180cd6f138edc281cc071625707b394bdee4581e9794b7f0d9191a3f753a4a5bdc6f9d663a4c5861925caca09504fd668c00e5a098d9399489966ecc3147dbc5f4b786a5e38e260b3e9c0948d718237daea837e95f0a51a649198efdfcdbfb90bd6024124bed7409663e41505eac67a9a89b89267b13706a1de8490ad112d5553685c02fff6eff162367058d3b46a83ba1a14a2cedd85fb1da5131d8c4fc842d49cff0c8cea0988a1f7a69a6b8bac70818ad0d2ac96f4333c64b836017f4cd1af59d4287307a2e5c78068b68106da9871db30d561c40e9457039f2a975183eeed2f11f4e4af2d653a6b5fbe11438490ff993337f73cfabe5975acce1e1cf8c7c8e4663ae72f6f5dd6574385734d00f33b00d5801a03fe2a4ec05c12b053fe390a8a73edfeaadcdf13ecf1e3cec30363ccff57bf38b2fdafc6d149c383b4a8d7810cd41fe7645c76b5fcbc371476546de25c4788df537d83bc1ec93e7b39abe949bed45bddf37a1b31271b383bc5e5fdfa5e1d87ca6c572c797801f8beab981a37382762be920f830900aea4f4db8e258c5740fbcf830ff1efecdbd397dfa5b6be53c07737ece9b1f69d4969e8cb8927916ad3b33e7ebdf9af0e4bcaccee2b4e28230006b6573de87035d0226ba8ff70532607212224f8360887f11e72c18b02fc4ee09e2cb255cea06cdf9666429fb8039b337594b3929208e001442a2c3c954905a6d6c4f35d780934333ec25e97951e8967232f0546aa38007473cf1a8fe77a7f11a5a7f6a906f087739fbbebd778ca78cff35ddce7bbabedac5eb3cf35e3eda45117b0ad1acd837dc18cff4202f420269369a555456862041976c9ce0357ebce91715fa04a5867c1682731c32030309780dc97998f67280237428a26d8daa1f90ffc8f66825b33e0f89d125a908f95527489b805ad598b898948357950e827c3eca2ef89c158d717073ac0777f4102e72f0f9d82ea8bdb863d0a3abb923849f2599dd63a587048d59c49009097930b753aca27b148aabf5174f40a008342ef31e2772f356312eb2e2669b674bf424994fb780899210a94e63a3c043bfa365dd2983a89bf9a6285cba0fdc07289ffc7eea048c131705ba94125c36cc18c9985727911010e3cb584054096f9de5dd21804fe1eb08414035c41e5424170e001ac9e5798963abe1485fa4a7e56767db4dc1a0c3da4f9c203e50841192c0da9870675a008eb39ae0957f63bd5bbf606ed5fe8f05a4cae73413e440079baa35d3bda33051c4d8ebf7fa52b0186b3c175e599ccd850984fa24f8dcb5f428ddcb1a24fc1eaa2603444fd866cbac1e33d70b228beeb8da8a73f652d842036c4be8f8e78f4b0ab0625a90e32cfee18432a9f497a26a326b0ad1acf960fd4dd39f50198755dd556f9fad330cc7598b69b563808f4465899f52ccc5b20480185f5bd3e8321cccd44974dd3c183152537aebef6d3180de715a01a175cd7782de5c6eaf94ab59ad7db032aff6a149dc01125cea49637a813a4923155ecd9fb2591f013a11759ab43cd4e7e20a0af53b70951cc970e67d8f907db00ab8e421713d3b7d4af15e02fbb44f4f3aa4ca9f421a13f9db6651781b51aa68f712d91dd38fd92d95d53f77f1f194f75a9e339c833b22c248b49e7c003d8f7e5983c247132e3472c3ff66893b4dae8b7bab99606db1a8d1264e2adba8a117477b8d9268f07f4bc5acb0ace9a8c788b30137eace00a9bde7ada13228e283c2705cc008a3f08521a5aba316c9fcc056181c4bc1ec65e12949fd552f36ef71a9fa60802a7d7cc73c5d41f66bb0cac0abc85a13fe0bf39d2f084152235d5d04c5fc07d37c57d6900676a068367cfd85121d7da39317728c41ee4c9aef1f1c8849b5b980f2097f99fd5d46a5315a869536e55c89ac6a8b055b542507be03fb92cb28b5cc06a79139bfef8d8bb07ebeb588f837e3c55ef4f2767244e99672995aa27109988d2b1c7b6231df4817f86713d756673127645b7e18ad80d83fe587595b1a49cd7888b09f6e5f870f68c543a023e4b811950a86a6e5a236c54c51429eb0856094800913c0fe13b88fb60d18ff3853402a43bc78265d93aa5c06c0ddbdffe3a1e814ff26b2873e9724870363c7d0f368617fe172636e412200542df07b002e8864150615e6b220f277f0cb8dce2d8e9ce53a86d2cf1006d2d3162c746990c5e4533a041828b664de24e3e93b11f6d7c6852f97c506c8817669e2ea87a5fe7852502b101c8ed996edaa032e9fbc7a56fabf7b234a1b20fa64842c827160b756dc976125ce80d508899de8bac1af92f84c75cf25e97baf33fc7f884ec94c2ced56fdf203ecd5dca8969f5a0356038fe2460b662dff0537467ff0fc813d987b3cb882ddd4620463a622b0fd739765edbbe5693da9a08ee1ce33b58ef287fcab110d7bb85f50561d200241080152a15f6b035b2a687d573c5c31899d8053a035b04c3aaa042f20cad4ca90572921106634824db9dfe8d2e505b8881089d49ad40624c26af536d4892be51045ad64d84a002ef271cdc55b1ae24998fae6eda01d61cd37b0a0190ab8b35686699d9d5a148314cfb274b045a1b0ed54418da8dfb49c612580218034a4b85e8a4fa1a29a82abc7e593dc6426ce612cd7c6da990499d24222d73ae3da7e017ba48b1b0c9a410202988c9af0480a6e4e4b44a304f8bc3d197e615352fcfc5f0f82f14f1d507aaf4dc612d38ee50bc478eae3f42249c52fd9aabde319325530b4025dad3302ad13af4f175f78aa86b3a83a8c82a75decc716971a0637c9b8415a76b80f7a3eea0af87c3d74843dd0c6bfbaa7b6636d748361d5bcc5371184267c9ecc2aa3cab23361a7950b147de8b2299c8f8634c1afd297bbe77fad3f0f976fdd53bb7f0b92b295a4a5fb946a441de0359a39293328044272e51906e1f9c8f52d566e1b6eef6c9d2f0f61aaa155145de0993161af92c0b700dee77351e2cbbffe9636789bc8fe81478b886a34154cffc79fb2f135ee702dc5c231fad6a06ae4f46eb637939dff9f7741c2c50ef3c8e7e53e494f68838395d15455f522b35d103c2fa475395602b9c3352f40c19f0a03c92faaff380546b0bcb112bc566e5e152796a095bff7e06f04f75788a31d076a71d0ab4b090d453816a4ea2a3dcc31319279f275f692c33cbaef159e20436141166bea8688be058d02ceb8aeac09bdd08ee29f95fac0d52796f1379f4bd1cf97533a942f4a57cf50e3944ffc32bab93fc9f43fcd93ddb0af939c9bea16e3e29a73381df825ff2987550a36e8a6004081ef9bbf75840c79fe08f412c98a525a9b547db48c0a79450add041ba2713458ec9f83a60a8550cb45ee9b11bcb414835ac0838634cad2e7d531eccf389199862710da73615345f811f7d62d93c07822289164b9106d1323df1ead542d0385305f1c81892806a8c91faf24bf359b2be7383df69ce99ce3f945d41d0c5c5101f9c04f2d629240e8ec1293ea5035c4a5223e777db34cabc70fa026457700ef2b454c8a696677c19001e7b584ad3f60b05ee0194e255c9a565d5fd1300361fb772e81bef49e3d73b055dd8fee1ba75cc2a31a05f34fafdabd5672bce705437a89bfc7c7aaaf1137df3e0e26ba4949bb3202e7932d24e9f230c8c39a958b7f327ee8c316d94da27496360b3b40b595f215c85836ad640b124ce50724a8717632930dbe6f0a0ae87ab1b034c76381d6d7c198a1b599a7520959556c79fbca0cc96ac9a9a2e232579307d36e32e4bac05a2871a4214fcad3d70f0f5dc6ac4a457bba4224db1d8d0141560d5b3378e80c51bf5b55a5874d811330a9be4061230aad87c11054d1ba15b4d8e63c7863222e231d45c370146d58c899e25a180279d812c2aa0fc70e3b548ab83bb09377eca5f46e01176edd9065dba19669a852074260412b974b295cb8ad34719fadef1ee475457b154d16e596fa7cfd242935e443bff8832f39608dc13dc53cb30f0350b92c4809423a8daec189b7a0d0b0b381f08d2fca74c006dd700e70f96ec986fd0813eccb12aee6b8753fbb8912a569577d125166902004ea7aacac1622e2efd743b667224fed70a625b1c5acdd141bb37510c32df95eb40f4f14302854f792208467a312566c90d87d987bd42b6047b2a2188bac7898f7e535e878196c9f7c0a0e4ec30442bf02a621d4212bb1d0c930092ea5c4200e8546463f77373d315968dfd5a1ffe3b1a4e1a033be808a9d4df32e8aecad0d8198d3f4f67df15794ab950c4d604fecab007ec3a37b8c9cbb93a367ed3f6b0a89ea130d8d404b440b010143472a98c970833b7daacdaed34241198041d6809eaf902b2adabd8b84854d620da434b9e08295df89c6702c541c2d2f17e0886afadc5552f97ace8021bba09c64c38e2e26abb0163fd583c40c8cee701a3670ca8875729a74cb153c788defcba6828b88845101aeea695fd13421f172b91fb4db0cab58dd89215ce8f406dedf580fcbb4bde8b2204368ad7889f8c533e28ad5eda066b2372e75e9bb48110b0d71dc486237da9182ad185f7bffc36dc0c1b51050f5844995a6f7205e734bdaebe550e1629e99e81022e23a3b8ecc35cd328406cf47b0cb71b313e13df8bf87c95f7611d46bcd9c2a77b43601110d7bd79fe6820ec8df78fa930b763d845d906430fec1cb7116d8d34fb434a146c1a3f8c61aa5628265d227a1c082ba7d7e162a9b20877435ebd602680febb1026ac913a62a23592ccf77e3ed50978f13b3edf1780ed233af1975c554d22ec9597457c46ae1e24fbc7e6413e384d2edef600c8bc5e4c73aca4bd5f8775da76e56258c5ff7fd9fac69e9cdfcb2dde67a7d605cd32788febaec34610e1557c6fbfc79bd3448df74bfbca9f449ef990145a7cf52e4425529274c4e384aa605d894e61ce59603a2ed82508f048fbb603f97b050b924febf2d92a389411941d32c0e8aa1e979fdba2f6358ee8a7a63cc4e191e5ed977bacd1e159e0b49ff4f6ad79a9f91f38abee9efcb3898a4aa39222f093f23e67f8f84648cc40cf7cbfb8959f59f12375999203c1a2ab3896ce8c9546eb9e309b99d3c0ad3f1423672a7c24568066337e7670cd472bf9b31998d02e74a1f1434aba638fcb849f93e18e44a6d48631833316f91da65f961c3e5a303c4fbc9a45fac61d9d130ae80fb5a68a7912a69ce8bbea39f207e0f6b9b6eb19b2a7ecf66daca34c13c7bde4366ccc51db4d1358e40bcdd9e1c632b8e1310a69fdad256daa9cc6d5edcd97251dc1b4f3f6c43567c37086464139448d79e3c3f492aa083eae93746bf0a438148950f3b4cbdc29a28f2be934e753de23111d8dd3376e74cde8f8e2f9acbbc97648fe6bb9bd8961005b9ed8f1748aea8552d4b89d942af6a85c7fe0387eaa44a6118922189a062da99dac01fa43d5424ed22fea9ae037baff5b544afd6125b706ca02e82aa18b16989c56d2c77d7ecde09a0648c8f30c670393883c166293ce382278b6155ad123a01af353ab023e5e2b62ca4b41886ef27be6c2cbdc8d8b26cccb57ab8c44c73ba576ae736bacc4cbcb8a2916e21eb0b3cf76a559db24ff5d0184f3913661c0ad50a710ce57e4a70f0ab16c3194afdd344e66b0fd909913fb516963cdebb86d2e6f6130373f8b0f383b2a494694e8943ba89ce521d4e74b0e667365394b73f7667b281096f1b19cf906f9e6768f5c9892aa9b6244beaf60d4f50e39d8571abb2af6a212596c7d87cbe5e77540e92aaafcbedb35e0b8ecc318367238554a82363c85011c2e8d2b722d956dd10b1e8afcec38f37f7212d54114d440cc40468b60ed2b7668635237eaf9f2c37deec4d5e963ba110ea77c4f625fd14b6321260307e2d6b3a6b3867a846bec0af289fd10aeda5db9023a48c48884d9d50cdfafeb7c7e2ffc75f6bd6e5c03ef39ce0b0debe1a6897c37d6281478c491d6ad26a5bb94b816d8a173d2d0e4f8a6f798bbc743831694eadb15dfb6c5fb828c5ee2156d2839c8ae5db532a3d8c4afe84adaf2a10d2c8ab05f0112951fcb6e2150f03191279e64b608bd14173e606c2dc412aa7c2a55ebe4099c5c605f5069e8101ce32d5f679858fe2d4fe78d8dd0cf901357cfbdce5d5f3c889f33886a78e3fa1aa7c51470984f3c98a911fdb20b5a9a04eab95b978ace541fc724b06ecdfdfd2a7f93979acbc746cd4c5b2383997c45819fe1a67a05ff4b2972d68078155745fa41ef082d37d0766a787e4df238b25d3a0a9cd5924924febf4552dee2276111591dcb1714f6df2e144cf839af6b9403bfedb42ef6ef9278c6c6f3619a22c649e0509d6e2740d219374d379cf058cc13566b6aee0f21fbcb0f3ed609b276d71b9fa2ed78a7606dce0bb83705142a5eec445566a8a53d02ec596c467575ac719ddfdc034f6d2b5e6a295f404e0a0e17602486334e296fcb8993141d36952d45c6609d002d8cb0781a7abbf46190d54928379f5bfded3fc04aac8287ebe861bb386a39ccac1b2091f0bc2928cb106e9d7acf967d3087aa73a011dd92cad1ab65dea35bcfe22cf05bc96d9e30cf40fd2afa2261aeca803fa0af69999fdf431c7aec6d63d5cfde04a2c69b2942232a45ad8780b24c313d7ff95ea7e3a17b654fa424221ca11053a953bdab8f26dde4015c069294743fe1117df1c5b9bfac446ca876a6e9b8bbc23ad1a91cbb6163f9b3aeeb92d62a864d40474d051102639bc5f275de769a8882d10ee98f9ddc8f902c92c46e4dd3f817a15785f644ba0224891583ba827fbc647309ef3745fe31f0ebfc3b3473eea6232cd26d695c587f3d42da0c635eb953be08cf1124c45b3c86ca0fa4acfabb58752ece2463e577c53941e19c9f8f6ff75f2a376ebcc74905e331669a36d77bf841677973c7d6c941f70452204b63c047b22f437211ee612f04b383bd985578999549d1bfda5e93755e3618bf29b7e8c6abb674c029aa964d0d12208a27ae1fde35743820972237794ce5a2c448b539334b237d3d941decbbd0070ec670453be23851ed0a95b201e41308456f5607725adcc4bc31132b42440f8cf5ef103923054b99f47d0797084e8ad7f88cdd7d5628e8dad1233b3e6da9bc4ac3ac52c499097de903fac785e07118238360cc64bbc38ba70da3cbd5dbf74dc07e8df5af639d10785d5a3d389c9a5e0c82e85c3bdbcba6378e683a8029ce329636da5106e09b6204471bbc674b0f459f157f4f2d664e15629a0646553dee3bc20b92a74103ea12a9eeff5cbcbbc5e168b7b859b5e2e3d0380a147d14d8bb649b60da12b96e9596444205c57d4aed6b27e08ff82532935332c9882d87b04123fe40d2cbed4d8b9a6b9f526fb33461f51b068fc17b7910908cff91741134ee9f7a13f4342ffca4293ceadab093d3c2ac793e103bab6c2007a43fa88bf307f8a9f41a03cd21c609a584d4b3b0bcc14f26c2af75da32443e95becefdb7e2a142997b7b05087666c3ea1efa609595f3031e9dba40c9ae49d307a190f30e40252649bf8622b7c9d486696d6a3fe3ac1f1921e6f9617aad6342c52da8ad23c30b54444b26b8eb6c77c90f76c87b554bf3e41585022b9ba7d138b0c981a6fcca2615b556a8355d65602cec65ef36080683c140f29a338aab3d7561519048dc9b77fb1defb46bfd298cc27bba7e6ca0eade695c5e6acef54498bf09fc6d296e8186103bebdb1d641c7e39da69a019c87ab0478e1927d00af0715cdc3a99e338c7638e7b09f2ac7770ba8508f65685ebcb104873eba598b7f9417581d8967fdf6ba0d3bdbaf9a2a6439cdc3520bc705ed7fe183a507593abdc3d16f1039879f0cdf338e609ada005ac5e32ded3719c40f22cd575b215ca5149042385f0c99ed937d2096125d0d31a11b3095cf64b1150b5b051ce92986342fa316d644bd7b61a7bc7092cba4dc3ade3ed861d7957abe008b439207af0f22410dc0f6205f5dd80405c549cce4c3233ded9d31944f1f8a5ac4c7d7f882538626424f3f2d70a102bf276ef3fc65bf9f577ae6c7afa753deaf4a1c8993846b116038be8845a4cc6bf5eb043c32061713b9b927ac93f7e9828673d50d446a0cf1355b2a9960cb6946f85467ac97b84c8986c7ad0fa136246b5be7ff4e8c41b937b94c2b28c1adad0fedc576e8a14fa151bf9cda70df18fe2faa3aadf1bfb7278cdcc65cc4451524717eca7b435c1d0fd2a2c594da746de102f8a9b87e3efbd42e8efe151b0285b60c853c9e585f4d583c3b48db24e922c5b77308430f904a4bf91c4b6c5827ee28decd638787c9d0a928ed016d0a7c6b58b4349773d341fb26b27134e020bb30bd2c6fc68f2f8e0ae0daf097acb0f6a22c6ce96b534cfedc69ebd28f26a36b4c7af50ef0ffd1dd2837f0cb2a115ed37c96a9851addef8ccce64a3f7c37d345bf059c1264e6c42d0928c3f917809154e64d1c46be943e41ecb9114f19e20831c37456468978c0a2e9670aeaf04c529e9f4b1144964513ae5a24d3311cb9cf71184db916db9b7ab575282d763fb1b88393ae5260f158a1ee8e78b0ee70171f8b1cbf22899b6c8307f19eaaf2362cc0632966215b7656fe01206f03463ba0573849eaaa9a573b8474dca3db9538d0a585cf470b6b9764b5c936aee4d2c9e97c23ad3ddc61b597d08cea78077bed5e3ba443c3d71ebedf49eefae4ec076dfc8dde592e7a4563e91f831e856d9b667e3f0addf65d9c008c257ee60791e8d39ceb8973e5dd910f5058447efbd4d4ce002449a05398e97a8834e05b373f314e8ce7de25ac5e24f331c3a0361657482eace3b834d5100cfb395835fb51fc020af323843e3f1c95c070ef1c17ab77d5f327fef00912d55e05b33cea4cb773de035cb5831d27af451f2124bab5f39206d0cebf958b3a70f623ca50dc693d7f083fecf23384d8c6745d8d5f5cf30d0f579a430dd1881c0f9c21dc73e114c30e9354dd4d808108ce79eb744af56b49b1e700cd2ca3fb5247ae845a5cd8b891ad23e705024cb5d1fa3e0da87e6ba1aedd097d014cefc3688ada702f9c26204057214120104a7586acfdfafd5b26aebcf1107bf685adaaac04b618dc3e266ea8d9f64bd507542390f980de3898e59b35dd48dc06663d63a4a75edbeccbdb4e8eddb307e39b97f182c7f590a744783bea2122052245a4cec699403e33d03bab856229e28852f528891030556c15a983b8fd423029815f1ee28e1913226aa72fa26f6a6b1723a9b9c4aeb9b0562526f21abc7794db6124e9df0e895873446abdc28a2010f84fd60c99e1cb89321f543d660164ccb4e4f7a906c9b7138405943b5fc6e2b6014f4bf4ca97823589f9ce07658443e80a36cd8324d4cd3366b35c4d5ae03bc8b3a3b0ddb6997c26359d52d40087173da0efabbac4918c9d0856b3d42f4d63c081b588cd533c018489b9cbe62ba1cf528ccbde4890f5774aada5085d80ffbdcbc07430ddde428414fddfe1362e8e2e7c75100b81f626bd196a9df1afd7a95643caf1c352dce1e2840176293a13f31e6b2b8473bf7645f39cb6c27ebe7ef7eda6da2bdf5099d1bfa33bd41f612e6a3d474f1c1f3f5a20a43556a4138087b75a15b900f2158cd15458376f92fd78bfd00e79f14094bd36985de40dbcc4104860882ea5268266c505239633bcec48469f6e1f20e26ac2b3bb2c1833b926ac1544dae5aa385b1b47663ac4be51e5d7bbeb0b9a3695962a7156234111fa064f8a8bf23815e36f3097cee3b2afd40f93dd0c27d7a86216534518b5c785b3a3b52e61a17468ae4a2607afaf89b351d85331320b029951a6f8dd8212f21f466e9ef7b1a41b7156c0679386138c2d6af9675a66bb87491b566584d542f2db9f86ae549a3f2fd4fbe1a9f4754e3558c4edf7ba7cf37a97932536233bb4052d100c9949e0094bdaf54b35ddf544bd305eb309bb44dcf6d7afbe5fbeb7bf7da05da030ecd70091abf1da322fd22c4811dcae65c2673f3d13abf20763f6ce0afb69ca0c78b02c1bc1c75a323d8d13acdce1f880070baa79100193bba0690ffe6953cd867dddcff9b9b871a0981ffee60e11a85a9011486ebf688227f7f0489298eefbae698cd6deae20c2e7ad8d9447c031ba1b7ab888e02748ff246425303466bb2958103b3715dabf70fc865283f241d46480cae90d129a1914a32729864189998d4b745efc28811ce21598ce26b2a4459a0ab1e8414649316166f0ad98ce56bb7660f4aa1d4e1065b711fcaf7ab00d9f8d8e88df7870e53fa3aa139a405fbafecee3fd6c336464f7cd7c7e8e5c70393727c4810738e764aadd6ac7751c17da7a73c67dd066745ec288e88a02eaf94cc6b1a3c9e977b2c1be9adf1fdd88dc1a3232eacee2608d6c6d32546439d6c8362c368f2d6a30156dacab458571747c3c37ab8ca6ca47df0579ff1062a6c288ef03cccc9feaa980a1fdc676b62d49cd0d493b9c0d2b60fe22481248a0e6e9af23371e8e863e44c4044b5d378e24590b11b9d5c6835481fd791c53ec88d6a8cd17206222a785f614b8ed158f6a12da1ce2e3e5e2ea1527678d9c2520907a158bdf9c603c8d1ecd36cb6dd4fbe96b8013eccc27d7262710924bd0749964f82a02d9e6b7ab586686733afdc4464d927f40e944f399cf51018c04a1ffdab3a9d056af7540b05f6c2c0c8b22f7cfe0eb593811db3f8863177ad72f57de23286599fbfdce9843a783cab87e4992aeca9428025713ff394f6bc1c7e906abcc15ba34bdece0a0a5aa09cd6ac18e2687d174106e21afffc01026054a825783db8b8dd6fbb371b1b17ec6948366dc004d8e818662a765574ac13afbcc4e242cede1bc9bcb19834c7eaf9d404301d0a0e927c9af1704d75d1221fe02ffd3c2d4ae7c74163490dbf2f17eb991db3f715bf74ec3badfdf1a703f06154a00c7a3519a14922ea7a65850bd9c45b085158190ba46ab55edc7e0f70e8f403256e3b7d3aae555e08ba6336611ff7ff24f422800aab05fc1247f7ed22789007c5fc120087902c9f978ed914a50dfa08b2f3df46b726ec561c00e15bb960847f0805989b84e6fe2a42e8d7b5a42bd40ffedbbbf1f0d58a236eb40d99982c85540bbb7fc5575ab568b5b2560dc5411cb4b6e00ca212a3587e28a25d53d2c738f19b1d3383c3ce106ca58396c81f56018b110188f8dbeef9b514642659fba30c59c365f10bd9d5b35ded85314c332c665e0f09db38a2598ed244b0fe369cb869bf48a911184c15e5fbbb0b509069762386ae603147f4af3d122f581f8714948720bdb1686a8881cd6c670c44c8d37352d48dec7e412b95800f0a488ff95f5d969079e01cbf79e632fc8648951172815a921f66a885e26df115ef2c24ea132238f900dab2391681921177846d9ceed42213b07f77fb9a842ae168e3bc71e1c50845ded410761388586ba0ab27f9f07cec354b118240ccc19b15dbd81e008820fa03cd500bce81c0042bc8e2bdea2d7cf160537722a889778cd00894c041a71cd338fdf2e46c32493a0cae8a19517e45b3b08d6f89d734abe268b313248e5facb539fa80822bd111dc79f681c313de3075f1d5f62be5193e4c9357a05b4026741ab240c593d608ad53546138eddcff3c9b6e8b30a364e51b4835c104cb9339e0f6cc34ad7f422260d78616b1bdb22bd97f74740cde56792589b69ca469cc62323d02c4aa03de9b73308e0e239d8d902e9b962c0c0de94a9c3acfcf88b5027c2b4cd46af420ee809cabb6e0565fbe59c801dc3dcd8159f923baa1827b5637a0a2b12eaa453410e99fe2846ba76aa8ab6e6e88cf3eb5d75a419e19cfdbf384fbe5a6126634871fd0d7e2a62c4f8a053dc753ed72ec5ea4929ef49a32bf3d91f5ab20d0ff845ca31dbdb9d3d4f4b27d741747d79a2e644e52c471b15dcd312ccf94ad419fd71ca9c6a8e15691610d2f7661a576864ac067584ff11676efcb80b6e351d6de62a631b4b093c780099ee06a18a921300fc25477b92031e7a9c4388e7c188ba2c805fac80d35fb90b973a1fa5ae016ac9b6ceab45e0012cc3e585ef8563602342837c96289b4db90c680022d24f2d9007e8abea95207ff9f5dbada28743145c12e57ef64abc4d2fe15642cfcf2dd4c5de187f7653d301218d354e5fcdaf6285cff647241f2d9617a2705a4575771c8a21ea3211b2a07f70c96246fb9ce34b6e10d5eaf578f84ef06027baf423aa49a3cd74d8c45c1f8f23b2d0031c9c4ab3f157c75a2c94582770f899f07085c8d6c31e85c9f58d6a46e7b3c5f0fb81d5a96b416dc1566616f3658eb5699bef2802d7eb065c40c8b7adb04b20de45c9cbdaaa70e5160b2782e15976e74bb1d32f52bd1ef7fdaf940bc56692228f4ec24b8a5ec8f86d6ac1a50698c68c60b78ba0209292d53a849eafea4a11ccdfc1e24dd4ab3773b29024c29ed944560d6514d8b6808e9e01619edee8cf474ef91862a048c61d27ca6196ba371c29b5c5ad9707ff210fa77b04a7882ac82daced188b1507689d763caa47fcd4ff239562c9b70e4f01405b7059a55b3abf6da8bc358e09c346ead91a18204c1273e148de6cd1715198eb8fdf43cb2fd1ebc333a9d15abe556dac82b2bb9223263caf36b7f8a8fcd5466fdd4a7f6fb04a91c76c812f2425ad6037e21c8d9829b43bc1386a89e18578a8e39326d28700d72a0c85d58080a780672ea160d4b71394c08e016e22c382bc618bdd9098b066570205ee344574a3f2a76fb8761a25d51ad6cd90570b49233e70b751ba127e351d1285f9e6a7d230dcf2f843bb40aadd3c7807ef74ecec4fa106cd55d0cf46c1ba873a97226817eb0c5a1e0882509e00256007600bcfc777888b36407df941814ece5cfa3c41a9a491a5014ddcfd22cd041edd875e7172e157516a8c8d5b3bc111a6964b919b085a2e8b5b439fb1ee9cd659df5fbc8a699597c204d1379ca91dcea3dd4dd8d89aba6d6c8dec1434db195c13986e98315916deba39629dd14e7c2a2d9713a0c1e590c0d9ca3caacf186e3a234e4b5655b448a71ba34630030cbf448d7eb35592076cbd4831cca600d861d47233b6331c3068b62d07f5bf1fbe68b59c0f5b54fe82ad9d35cb5ae751186a17217d31075079c3cadbe3d6ec1f526bf9006c0389d62e46f1cea2b5c498cd588053eb85ced3d0381394e5a30a4d2f8ebe551aa989491a1269dd15d6c338801ea8ee21c07a689454e60dafa8c6cc3fc5b44936f9095a19f43b4edb575d879d04dc1bdc20253074e4a8a61032831397b0d8f47b603321060451d958616b3d0a32a9d2b1582b0d8ba5f3a9240d4286912694055d75e075256d7f58c6be9fd5dd19e5bb32b67e8832ad5ffb6148a9ea0671331f9cd15ee28b7ca7d263d69015a522db205615e1024a8159dc2ca6c17d04ea01fd49b858b1385dbd23ad02da87d88f32d17d4a62b048867b8189072031cfc0642a8489a0968f35b84e20ba0a6925f2cfa8f95e252b3b550d8c2e927053803fad9c629ed17ff5565129e1bbb9ab0203241ed0802b131446aee9d5a0ab79bd49e037f3a49cae3c9b10e9c508f89452129069890922b5b8f30629db9afeaa33f52adfa664abab9d002228a8af49d22f1edf940b1bc4448c38337e95cd6a7711a370970a8b104057609e7780f4a8cc02eef0af3a7a540742f87829dde7c58fa8f5cf14a301df57c43592ec5a7f31bea3314a77bbb586237506b78afc0a9f0a56ebd193a2fb95941e3d9ae095985a266fecb576ea7d62d0e28a625e0618a40b65034b794600bc1ac530765c4861b2583eaaab3f4ad9ee18645289547d8511beec2371fdf3e5c9b10bcc2993d1987b8204afffaef58c3698f47812608d7381a05afc45ed645c95bb17e307ab4f898632c8e460846a7ddce3a31fed7d24e81f297ae939d5fbb632c741476cbd25764df20da120b2e0b4886d845421510a190fbee307cbc942eaa8b455488bbdbe0612b211c6b8140eb01be5ef7665700a1a24a59529c02e705fcaa28eb97e4a70476e1cad2c5b9284104ec2456be056cda7fa039f712c46b5122d236a5d3d3464cafbee5caac2861a93419dc3a859068bc47d05c141ea21d0403fb9b1c9feea955d2dca1b384f35f028a4f0da49708a68b12f3c754f9731f6bd776926c62138a1fa8daf9403f975d675504eacbab2c60bf1df5e9a15518e7ba0d472a8e4e9717198a4a8fb83a9993bc5cffce344e71c6cce821b51609c68b60dbf7950827b8ea2c1af2c53531d483c36bba16fc5e88799d09c25bc2abefcccf8fc287e0231b1644047df968ec5c24bb8115ed85968395e65b9a9b60a49edf34e8807743e2b55ccaae223f48a4d940a70f6d661a76a86afc3b483b3e8cdb16286280f769b6c5dae4979bfc110876754a8260a6668b36b597eb0b2effd001dcfc31ec3c15c712f83eb56b34db417289c25ab7c2d322c1a59906a7f64d483276cab2eb7f2ab2ee680b72c4e0dc7e4e592d51c777ee0510ad2a67a5ff725cf895cce86081639ec6c42eb8508cf97c2423ce11d1ddc68745e6e385ed4381c7829502bdc76490455d0148e823f3f4b421c53ef17085505d7a00922f173c43a730fd16890630027d2e9c60e1959251a0abbed86ef3dbc22e2c6b0e215d8264071050d3b10dafc7c83fb42a9797ec1cf6d12f63cc0b755d4c6854b271345f9b3525b49dd0f34dd0a962c50b7c324e3e649db670572b83bba1842951f8bf3560cb920f7cfda64d7b3d89cd1867eaad48ff08a46d0b3f8bd2f0ef183effc31c275f89eee564d50911dfd2dd8b9e4ab177450d47542322970eaa9e8b95ee3cb1776e809f6124568e68bb2809eb358157fe446d176141081d373f084882f2825f1071912d420747512b6fd4af84ecf14fecd2554022f7355f2466605463591a7573fd8d033563cad807fab358be7aa395e65931584cff3b3a069f88fbbb2bdcb93e5065b4fca4caed5ef12ae6893726ccf453d85eebd4e04bbd9ad44f97504c1ebcccbde4aa51f0e34905822ac9746a8c98b47cbeb0864bf14df0409dae999aac4c444c2dd04911baecd8789b66edfb0f4f7a72236d5b2ca079da1845caa67a0729690f33c62c91e333f134980b8486156e31fb8e57336d46d867e56817246d303e05a8229f147535c25f8113faebae882745544615d5d781cf1d57133989a299fd746cef678d41fd4dc13034e0f12125aca3e81b3aa0e59695012d3421eaff5716e6bf6745f63f84be785423c03ab8b23839eb0fc4cf187c0f9d31e2959eb7e5a83d686e0d64b980e0d20b6073488246083ac25476818fd8b37a72819fb399788b6eec17a6b7406472dd9175d513a86c2f454f58cc682f24590e48ec26437c3e63910d14feba6fcb03a222c03d36d89ad7f52876a2e10647d7d10c7c9a3a1453d7d1145080be39d7f0d83b03078159c8f8c595f26abf2d84b792746c1262031f2a6d64ecffb268ece86456fac857622f76db0edae5259bd7359ab0e1c15bd9ad8ddb7dd6965bd53b59c3b88d44eb570be7a9d3ceff7fdca5426a6f3382d16a4b64acd11b2eff1f6cfb939fb57645443f31a26c4806a749fc63265f49994775ec4b9d6d0038fda96429d425517f25f8257ef952f9c97cfdd81d5eb5626fa13424f1740b23de007f28e717f7644e13f834146288f913b9283d4f058d35ae86bc7f69b5ac685fe1a75d7482b99fd9788591f500afadf3d125c371511519a1da636bf71a5a70261bcfaef58c85f6a13e10c012cf4f65a35e6694635279378069f2d7b8d43d941d3313c37e92ff51ba529ff8053970cd561c88d6d94e7b6963f3a3c1c0be974a83c9f0a4abe69bb38eb961851482341824f4c16a73d3730ed43c825f881cde02a24274f103eb1fcab322bb83de5c0a9ae9827df2bab7bc320cc9d36883d204629ac4ffed5fa32c64b730d7464d33c853491fda0efcd9879f2d103442792172296a6cc15325ddfb028148b06a01555a6af9d84254ad574655bd4659f472d6c077370831015cd169e4ffeefd2e9eeee9585993dbfa0e25c9f28a81422fe9ec5f994d6185dbd1a312febc42aa1e05c9d482844963dc116e607f6210b1cf10da94442b20400a75bfbc8866bd8e72e2f915b8599ea11f718d8aa9c758f1240ddec00d325ca5d081693477aaffca638469de9cd860c889d110cb14cfdbc2b5c508d8d0d8852fea706f336ea669775e680f70579694e6fa1815a12806764968f432e1a5963d196cc25b263c7a82b59c026e4c91ee955a6c7c89873bd7ee285f63d84da4cd93a4fa21c33ce6794202390c53b0fed1f6fc25d1c00fa5fba40ce481808edabbd40f786211a99db5bea781389be5fe3a0fc0eb3e21999ea0a2aff760bd974a37b9dfc38f3863bc0861736f04e88d78339b7f58d5f86be5e34fb07a593724f71d8c2df76152d3ae9111f3942c38dd7f111657405c77297c6da4fe39bc60cdc755ed7093f92eeb178254addf09972707390b0397be7c4e1f1cb57a11e89b20dd02d51f0ad6d7850c527eec2372ada19cf7dfcc8d741a0c793b59ba8df6de0045ea6db20676bfca461ca928e5b9057bf94c3215a619abec1b1a8d5c36cb97d1c5614619c460fbcbe68f3d081a1f5f278c012dba051bf2064cd251a73393ffd0611d3c2dc3a04737621e8153d27b59405a5678ac2e7284d5194da01761268cfb68163cecd01bb497f7c2f65d46af7cedeebf27fedf2092a5e43979aa78cf3ddf19a863857ab1dd74ac81867544399129b6248490e32c9d1eb43625e7f91fba74e1b1f8648151a391b4b25d6f49706add2428affb8f8478464a2fc3ff6cc9d5a5bf4664daa7699fdad5a50753d0c42d84dc982bac60ecfeefeffaf32bbdbe3f0e3b063d39b449a5f19f7fb0f98b24b35e27e429f6fc3edc631f721d372c7fe1c4da58343fafb3d296b507190ae194e390710dce6553c1ac3fb2dd1aeb7195481b1fe99d0b2f75f57538f0d4c4820c1261247413e77f0693b17679e13f559f7fcc3c2f22325a3560cfddfcfe78f90293d1709eb913a36e25a7b42bd0f315ba330c07709268f3f7be8ae8eda17fbbfb2ade23221dd75ac1db9536cee4df370fc81b0c3c5eb4373f2033d4b94c967f7887c415a67e6cd61a27ed24866b0ecc6f0e36abc6557d9e282d574b962ba5f893a76ef88206809a198a7b30b6708436dae6721ddf13f72cb9015cf5441a414d2d931446eb707a51a74daab0993601f39b345415b48957dcebdca4a4137c5f577f2a8163b12980dda91722b10d7ccb8193e7d5e62150c0f0301613a3d6527bf4d3cdb4d2819f49af611d639202b3494ea2aa4190c2b78986f8c37d760a6c819520e80851518fe603301940c02de3904084c84c7230329d3de0d164f4791aae9de4c4a4dbe52ecb5952ab988f54e99b91c6a48cd8e779ad15bdd67867911e43b8ee2c245d6c9ce1f91131530f0117133f70df4c80d452115b96c419612e12b9d141de0b224d29cd2542e5c12c109ea0a35ffcb0c8a586989915cd3134de5fb34cf5e4bcf40888206468ba2e0910f577b1653fccecd56845e80117d29e68fd45cf682bd7313bed3e8ad108ba972473b3061fed5f56a2235bc56781a20859220c83264480f13d9aecd9a0e19271dca2361e0f17d2c4d36508fb097b890845e412fe4d3dc0ec051f4a8f39b0248eb67818e913a31884e374fd67075170d5491133399f9a04acf8d32074911f3a3178e9e620d8ef563fca74aabb574d5841549b8862b9802b972feda95643c3b23a07898d8310973e4f7861741cc636f6395da834f14daf03ab3b2d114b236bc58a21b4b38edb562ac83af8112dd09a6877e9e6659878c91711b40f07ddd250603f7ef38bc9c546619797fe8be28cbe65aa536787062b867865ce82df74dcd42f96fbea65c811b8fa9dbde9edfa3c56bf9c1bc990bd57dcb3585b9e860c7d6eaa8eb36e8899d1a82ba85c52f2f4ba2a57e92ad3d78a7ba88ba959871839b0bab15fe91924846dc12384fae2a274a13881ddc045600e50933660261064e7ad712f620f82460a8c0f6c869f74715c26f11e6c80614ee3e3ea8d347e783cc929cf610c628634556a548a47ba7586444ecefa78af902ecd249de402f3eea035ac9fd415b08cf2493ef9e472aaf17e797a8275b89b1cea7a4eab8ed9bf03580eec3c49928bca00dcfa5d9e2e2b0b8edc5250c8b21e71f203e1ee99d7b26108938a60f603534a650112d0e4633328bbd5f0f844f8c608182540f7416e1840c9b39d054e7ade4f908014535be6468f51e055409dab717ca7b25ac9779ee2320627cfc027da0e9583e0127ff8ad5b4dcc6ea2189695311c780c2c3b0e382dabee694d7d49310a2e8ab0a18994a1b1bad065f9b79a3d85eed7ec4dc8569fbb445bbcf4644bb301c036dd65df0bf4cc40ff943122d7dcbeb1b0a3adff4af05cfb4f50f8c022bed061d0fa9a85076971b31430eb3127964c832c2677910c8beecb078d2bd604819a286cc89b69b6c36a8bd337f3922e6dca2c5f79fc32fe850921875440672de64b28b1c20af2f1b4c55a8b19cbfe8ba6d1ba66065998e7368a4d59b58747d25ca5a336805aedac1b9c0feb0f2d205c2fd5874e8d2353b82ba1984781b4f19d81da4a1e65efdb8dbcc5a3c23cf796de305863679cd0741b9ee6b06bd644c5b5458ccc57b7557ddb0d8539eb5d0936318c0289c88554b86265b3ec516072f7ff73403275611a2fff46f139a3ca4380a1e82d9ea1a6f148611b2504bac182abe928154812acb00bdfc90df98eff59d830232fabcc7a070be38796020e0d9328a69ec5694876aa32ae23be37cf23596e8670214d96d86891d11ae90be6c9b26918e573e1136a29b62086a214969b9796d153bed58bc80603fda2a81487a18b744aef577f2bceb97ab04c9ec5953359e898769eb0494e1bb3e3fa82c91231f78199a4ad25db0374c926df6d16435064f03b574c77e0b9728d6dd95cc7785e360538b1bbfffa5522b5dcb5d69bd1d7bd9e52dbf7675e76fe69d2b3403839be81c02b2e6db468b35a1d23336cfff33ffc93c4561495472d7788d2e162527af8b8e2c14c9cbc68178e7e26bdaca396fb5719904d4f58f41e46aac22540ea795273d16a5802e00af5325adcb7724e95413edfed942169e5f8a730df79d45174245f383cc22b1523dc8691812a9f8f188a0badbfc40952ea7793f91c78fde204a92320c6d01933265d1828cbf740aab6e2ecd78ba3f3dc15e52931c941be1a0f1ed50c2b655864381b24901deb181d00e7e5dadce48dace8cc419cc66fdec75409f122d78de6dee1a4ded7f391532cbf4cdb59c5c9d46960934703364f935eb656adad4acf1c8e1388d6efef2fb5fb028144e865083effcbf32988f18dcbad15fda44a549671948a859d2bdb09ad1fb471e716544234f01a17f6701a7cf729dc765df49b4bb8b89f228cf40ae1dd8bbd0d7fa6cfe3cea3b8ea0a178004e583abdf0357cd1088784274273beda1a6d1350f013e0c28b806e2620295f906b0f4d05d6af3f6036b2cd713b4efaccf1588c5a6034414439bb7cc4a82b178ae533f126054b6dcb3e786a1e166faa1a8eac8759f01ef52b128879c5a9deb6e1b49a6e4fe899dd7f7bc355ca87500598429a8d4f1003ffeb3c98040ac04b9d0c037d2c6023116328908a2c5e167ad59c1dbeefbb90e6b2425f3d0eb6377d2168ed9d48184086f0de3006733e9ac06c67705d8c70c6e46d3147fc39210cc9c15f319147e2f780bcf34ab6043730446f7aa6e49942a8ddd0923140d2227ccc933f7e80e23f7e842fb1eedded264138232c9bac378efd1094942c58933971796093eaac9a8ca23451896eed9f34e6154823b4b3f1c21b876b809c94b344edd0a8a66f406f1e2bc06c159257d6b90961391eda14100a30dcb052a7ba086c57c022ed9e85651add1142251583d73620cbaae6514a21b7a9b6bea0c15c04e30157ccc428c4100fa4843985ed01c945c6fb198d8248b2d0f835c4d08f61455dc365c70a0781c858f5d974ae0fc61e877642253ec3cbcbdc5fb0bacddfa0299e3a10d3a8a1d62179afe4c81ada2439ebb7b7b3fd0fbd43c85ccbaa66e9b7f30d260a65f41aed223bc430987b5f7aac5e95e18a6b6e12d75ca447a4704ab9490f52ff89ba12b96dc29317c11d7d0a786b4544c8c0906121f09fbc4ffcb1cbeac91f08d5f63161291419619a5768ee0ca8598db4b6da39e36232d9609a97ccf25bfcb374ab0a4103d945567db431e5f60cec1e7ec44042ee0f5e70e246212b8b4aa8f80dcc15838e077b2e9fe1d42262546211ed964963afac1441b6971a532ba8b27d8bdff4f74ac2b8da240d40c5c58c53a6e185b5613eee5e6f5e334d949ab4453d5fa1e8c6311bf4640994957fb4a2095609ca6e2b3c0396cc68ee5516478fad0cd1e80c7b77d0990abac788b2983a3b0db2dec6d32e853b7f4e3f21071fb3265b4c26d672e66c58c2eb33494e8af75c4d42cad70417852d53408993b164d6f650b071dbdb096540ad0bdabf099388f3ec0c254a9d6557e5ad1628993258d8e4ad1d147c0e26b6a4b6d2a74afdd032acb74ec918fc0d7855a2815b42d0222c52266f3c6f732832ddab51c40fca745f7ffd8c58fd946a17503aaad4840a271f8b4a7140efa4af2ee9a6a39a2ce1a1eb03d87cf88bb5faa8cb9e42ce9087a2173edc101d2c09580fccdbb89dc8a98e4d5e82910aa2af24e13ee750598d7ebb38161da987df588585554e366695bb6e4c99a193131925f8e95cde1d6b190ba1e9b49483c1faf3009e96285a2da0780c70c6bc7c1154440e7b9efe33d55ecddfd5692b8bb57455eea17e4a465628694221b029d1ceb0ee0ca12a5623404b366fa4986d44fff3b44cce1a445f59ba9061f6a72d18d9c921e0e880dd37df93af42b8b0587e17765ab867152b306f1cca01bab7b1a18da85ff31cbfe1ddd070a17295e04d7b34b6e32cfd23fcc98def626831d8ba9b62a0c54c1e522f6040677fa1bb72b2e2232fc248ffe1173eba204d08e758962e1eecca5045172e06abd830bb74f57d69259749a6b69f222d5d8dc031cfffa0aba8ed7ee0f20e21f25a18c332e5b09a7475092e49c6030873469e0ffef8600637aa111d522a32ab4a9ea162223cdb1f50fa9ca1d6e209d0ad3449e9d20474c7f64cdae661bc9c8817dd01367d730725f4de8c6404f7050a447cb2166928a2d3e9d6c77d215464f43a31b3930e4717348e989907bb64ed68f4dfef801a0d2a82db3f1dd05a993fb84f14f8c7463456ed3dae8c30081fe45f6d2ca9a9cd70f07071226918108d52833b2b35282042406c56b3b7524e6339a5ada45f6285a31c615a679448f9120015c59072f52941be93799d35d2f6e7cfe2c2907c09c98d00367166e375292a90cb4429e6bcf2ea710b665153047818d48c5af3f5b80d8fa0f46a7603f706646d5ff643a002368f894b809a1540af05b91b492eeef00fa0da811a605ba8a2b59cc70275c45368e29a0d59fd3eb1ee1c0ef77086338d3b510dab209a6dd2cac8a067ea1ba0061eafbef52b8cd3e60c65b1ed1a42c1c560c3cb508c29cfc9e6342232ed009af379d885e777aab7b6cbaf7279e0744c8e5c635b2f6eb848177447c387b1797e58d1d2363f6befa51be104b43508cfaa818ad0aaa3e6507c8efbc171ca06f4e5ee798a004911b202188ffa6379c46494e9c1037b377411913327ff872282c29e320362668069be43e8a8cd15ea4182fbaa5c7fd7afb0fff7f08bd63b18a193849628d4807c446b4619f1c2a7408b0f5100be9ca21fc38e60f1cdc697a7a7ee1df9cb3a824e50a2ece6df2e3d8dbbc6fa16f421bddd7ddd9355cfa9f7e6d05b4f3a7d13029f656ed7192b563ad7230dc9302164f33fdd80cab0164ea93034b239b64ce725b1c669e8c0c8f5e14a46c79612b6124c090fc711436793512fcd1874b3ecbfc41626ee916f8934a2e70d603f4e787291f0ea9d2a4157cb73dcce356297186bd3d686afeda3623e58af7a410192c3a61df6d11132591e6ae8d5690a73a60a5851442e32b28bf2e2448604f5090edca999722fd87247fdae367b4d149666fd258da7c82f618bc5f92401098191a6b651fe99a9512504405ad21ee42bcb8b5e69a221287278a5106ba9a00d4e9b5506848f2ff2d6b4fc5bc524e155c03af07de9755db17f38d119d3d6c8aab952fce1a63353cf14d71d8033d61536d0cef3f565f168ee4dc2e4e3c44fe77af644af24f61e5683faaf490a3f270f6789fcf50287c0c18b1f772cc4e943fba0159e9e32f39d0693b650a4e389c15ee3b3ec2370c16dc50dea986374bde29043bb23af02cf13f466cce815cb2d6eb3fbcef8c9e28e8708c970a03a1dd18540e4b2fb9b033734a13a189fba0b3cb0b690fbab4997bc9df019adc8e8b5efc8c798f3b6e4680d61d2dfc7f0bea657681917227e3a69b31ad32c1260f6ea197f7f3826dc79499b61a9b911215784aa5b54b87b5516cc32fda2f82b967f60c4f7f9558ad9b1efd40ddf13beacd99c893fbe8f91430febe9770fbb7011525f0931e95b8b8c23f069b9b0888daa661dcf9caf4a414f88b354a8db44ded6918fbe8a094c3c38f7fcd88b3f2d330e49c6918efdb3dee55b2159e3de035d8a04212368af0dbd71a0e5a61c756a352b2d441033b1fa7746dc44846fc1e41382787ab3d7ddb6c9dfcf4abb77c7d397b0459f95a052105b8032bd433ddef6832c1aa0ff6ffcdb03ac9cf74a6d32b05cd5ae8231dc89fdf77784f57299b6fa9efb1844fac74e55adbf7fa95a22957b521d2e07f5d51df41990fd326d686b72c91dc5d3648883e40791495c46a3ee2ceea0f7700fb5c8038acb2ebafd9b7f8d2c1c10f804ff227d020a63798275605d74af70d55f83b617ab31fe00fba1fabb0c218fabc58c6c8e2f447d0ff4d77d24656f158453948ef2c671fee94f43a45ecccee0cbe607b3435bd652a0d8734714cebddaa46ccf5e2142f70583a04de7a299510a5c9cefd979d089856a5a04b7ed0bccac56ff009e11723304d321b81ffae87ffc2b1c8727e7f51187059ec9da8ad09520ba9c2006e6039ef86f4a06604832cbb0c3c7ff330eb05247c4a2ffb7a6b276b1babd44bf417bfcef2953b7ef602c5e50382e20e506d26cc79eb7ed5bd3c4e29b2539b04ab22cfc12d77bafe779c21f07331df47bf703916c71715f37584a959907cd1448980556c3901f25bf4f0ca306d1c7b747d806ed947e14b916b7eb581c8b2a2005c44f4de07ef3382e573bf912e9f7d1a6979e24b0d6c7ea5015dc6e5661ccbe67ee35d36fb34deb2199ff197cd7d8d73d9dcc378cbbe4f8db97ccbc91896cffd46ba2cf669bc65333ee3bf8c7778a9a5cd57f381d48081dec409a5084ec0b330fe077305b5f94ddb978297864381f3c2d0e4dc898b806583656909f69db3a5272ce1041d1731131f9da1512b2503ad69d4fced5ac575cf2521d42ad3fe63ba3bf78325fd6b1a9d3bacb3c4ef82c06b5878b10fb521f049c49605a9ce70af64908d6c4e58b4e152414313013bdebf0b6f0a3fba2e6b7d85e080e260860c0d07895d126325a4dfe44323dbeccbd02205a2d55fb27629dee6bfcbafec543e3034011406d11dcfa14aabb69209ed0452f8be7a7545b80fe303c5982ced80c3e0530eb58e00a0a34c983d568a8dd52b583db09b681bba187cf03bd0ccfad78cab6816af21727bb50bf80286f4402bd258a074bd44a2e183f65367caeb9a69b916696f2abfc2cf6e33b8cb874622ea6e3c0624fd74c5886e738a4a331d243b4a72bfb3eb924ab57dd10a54701fa853650fb593e1c499a0fc9fe19e17e84c316bb18ffa8b4575f8c154867a40655f001d17a2ebb970ee2302238a55685fd27844c1844a8adf54cd5053a6f48107cc28ae603530ac6f768bef4b1721402c597f8120e4ce4a52f18f02a740950ee02781b4b6c9ca6f120bcb1cb9d3018fa13c5467d900b16625191e439ac363f7a1129bf747661f0fb1e1affe4d98761b0da5a089d6e754d8964c676cfada775d2901578276b6bc7172c5c1159f6b5c8e31711873168e1348b09b63737c3ea5f6619fea137da64ff9043ee7d33ea9cffa4465aa47490b0a1ea0311ea21b32bd3e309c3475ab0893e8bcc28b66c25acc19191f056141f89b5406c0fff4a37f446056d2aefbc28c5e2dc7b342b5862329caefd27910fb7defae143420643f912703c146a7edd94e3d211b988a696d67854419688a642015a7b627118a99cacc3f206d8238bc63172afe3473d70ffa6dcc5c1d627aaa1b4206c9a157105de3c2ea25c4cd9ab9facac7f9014d99983d8e457e0fe35bf6ff1a2fcbde4f291aaf884f7217fa6e58666d2f756262126c5e84835e2ce6bd1590986ffd2000a498dccfdf214a8e7417fc843145bc11f169182062fb7569177288c255fe79fd822a290ddffc632da249ad80e8b92c20a865835d40f2109bff644db32eb8ef7a7e4eb520656bd22a8136df2c9e1c860cb405f81f487e6f88270e496107a7a513cfbe839066e75b482c7d99eed80c8b7dd92314be04e20c2d14cd2a54c1b7aec353f278e695a2cdc6d2dfc5bfcb0c74f00972766e5910f9be848b6c6885efb096f4a62d1d75ff8469dd4df3216aad6ed615e8005d1f97bd08211df8749110b4f245913f2ac234eff7a67d04d87dfdd8a9f8580703ed5373e17762b0f6174b6824670fa483afae75dba2af3e8f08ac087f2a6cd56f6d2ba7d493241b1187ca6975427c1eca8d6300ca89a43126e67292c462fd189f045e7ff043ddfa0e24442310a3b007dcfa9621666abc64f8380e0d52a152334e3c2ba66ae4e5ba91513c259a3f71db40a0b4331b1765908d066efd142e3610416629cc02f73f0184a60d13ca38bcd11cf58814619380eb85d64a1a82dbab43a4aba5f4caf68895a27fc8649cd329a5c909294173356adc6064298c1fa75b0ed13f435433f2d22484a5d1b7967b08aecab85dd9573950639ba234a15ce1a96f104cf23ba4c16de26a6c37e0e0e5d6e0365cd97d5fc0bdc53dbe07734508beec860d89415aae53093a260b61cc12bbd5a4e304d6b93999d9c110c1ad97728b98a9c771993026befae8eacae65923d1b2087ef7229201f91f3607e909f9d036964fdb53fc4e1ca7ca1ea2a7ec8e909818312515d81b69aa18d6a1054d909005704027fb7417b5061e688aafd02535a83404828ab5bc495b024eecb86a8b029b6f52b0d9af490a4b130eacf5c93506cfc4228a84836994a602e0bf04c0ce04bdef26572326600ef11b85c7b313cb5b6971564f18077b5ab9624f02aa8d9952c21eb457ddac806e334c4e26396748d31c45d2a01c503c04d26f80219c005fd92278ed9636e0a6ba97c66c48c5b15501e1848bf100cce698ea198282e733233ac329a6a59a0c743a4beacfacbd562ddaa36f09e5c3cd14cf955c0ecaaf6cd8d66fe05c9140c448610c24ca6cc7d66bdd882cd02edba61c26c03f12f869f294f5ad4f6dbae8d05049c006f3aeb44183fa4a382d027025dbea1abe76000461f1cd1485eb60710fe2477c611bbd1468850e752d4972a3459ffe1bc3795b63a5df9d9c56a5eb1fa2c3d328756afec6061b27be21cd93861b334e1718206531bac991368ea324e76f46ffe00c8ff5a40b76c048796947ea13c0d385196df061b86c631cddae4f85231b2f48f61f08467d8ccf8b0b3064519d55015a0e2081bfb6a03ba86328fe5efc11a8e80f024df30f75313be96ffbe85e86d19a96d03f123876952343bf81472c7da0c1fcecb33fc7f0721b8fc7b2843858e738dc020786ee4e246c09db51dc418825614a43b10fb71941c7cb7711c12fae5f033077164ff2825911511e65f39559c3b314d22736b19a8eb3fad77cb6a2d942cefcd787ed89546b826befdf2ab0124b0b56df0a6a2aae2f30a5a002655fedb819bd6bb25849ac42e292d5ad2166efd15b6653eaee84dd2e3ec19c84ecdd1836e5e460141be86d7a8559d5b4cc576c3062469b0e6193b35cd3898e12834156886d2180e9589ca66aaea0524912ff46a66153639969b84ddbaefefa0bdab89172ef74279847b7c83598951eda374219c07f292912ab2a02a2b2ab7103fc0497a34234552bba8b87cc30890ad66ff3c7f7ee51d45010c422944da7e2876a00083603730d9512b759576363c0e4b34539351310a584251fdbd0421d13dc7b5239e0fa17d9fbce749a2098bbeb72fbddaeac1a629ed616ac89d056d9cdd883dcb6fa9bd904aad35dde03b0e8383f448ee9075759c6c5e19da1447931e08a3b33a111390eced59c493e724ca3eb23ae2b5d80bae346953346be183851c0f1871cea4654059991dd225650d7bd199fdf44470027e05dff784fd71fdce7fe5f3e3ef81b3e1f2452f15453f3302e8f536f609341c85c243c2cf4954ba11a4148b87d42726196e483881e1cd653852aaee855405187c3cc7918c9738cef5983a7dfe19e9093be0b4b01a5bda19584c1a0da3068c0e8656f44b05271e2f7dd2f93e89613a2f55e94675f015c62f5d817008108f793f62df5954319d23e2e9e404e11c74fb22d4ed3381af632bdac59c11b238cf11fff038c967aeb54a5d11af76a47d55c6431a2cf18463428594e0536587a8b7e45f8e106f321d35b28824383997cb98d940c6cff770bf53341da8f197644a8beda0a5140048ac6859d0ad4c19bf88fe19a8b1adf29c40c7123b6e91db3d6a75edd72a10b853e8f1866f2af62b70cc1218681d67d3c6235a574e6513a35d8d0d9b49f092583fd4de139ef76c4bb75f31cfb92d38b74384bb1c4fb2b59451720663c0060599d5d67d1175f68b739a55060518beb388944305a9ae84d750812f2e247759b2c8d63b85258190ef03007ee01a077ca6c7320b96d487a50ab36d3d8511a657bc8b37fdcf161ae4efef3216e713d0d9399db386141a2a512ee4e598046a07a1c9ccd5e4a81b8be8200753988700b008443976ba388733af86454d87fbde1d2606198ac3b67319174f5b7bcb3a08471b1efff8a3ceab93bfbb236bd465138ea8e3b3677cef60aab63dd2ccad6c3978da2953a320f024d9579390c2010ea0f1b560aa6ae60cff3436ef8e4c9c5f29a619ccda13cfd698fecfe8265aa5e5d173b0cf7263c1b7b844e424f5bf8d498e17ace98ad93ffb4ac600b89818214366b7abbc588d74a58b9e820be2a66d77753a5c43ded03d4a65cddd067b65984bc014e2143031d8195a403c4b558f10163102adbbaf09d3748ef6f984b3c23e51eb60b57785b2d740d0ee52b8391025ddd44621f5fe2639f4a6434c6b59a9406b15c0f74bd2494b403bbdb6645a368fc290fe14394a7933aa128686f9113aca8166c01422bf9846951c2916f86e1eb4694074012360797b331c423df3453fa47142baf359ae25e27ec38cab499642fe052c49f1a9362fa1091d7062fa811bdbdbd8805c4a8246aeaa80c06d117bcea061d4fd193824c0efc066d02d99d3f847e4c55d59567c5dd4381ab2cbdae0c00c07c7733660fbb7da06a7d0111d07f1fbd848be98a4c5e6a2e4247ba372747bb87cceb6e3526613f5faa04d8f67e70e7d636e3eeaeaea275335d135f5e6fb900d7f24a766e275e635105515224d66ad83316f9c8a4d8d238b1f5c228c33b8ca09fb5bcaa868d62091301a05dedd90519859e510eaa91be526b8b448ae0641c638dd413d700ae1d7b29a72043421bfc7d264eb44acba21b3b2c139ef89dc28398d96555ce0bb734a54080da1101325c471a6d3e9b1f345bc3e254a80ee0b97e37bfd1460489a6b04396f04666365773e01068564c961cc8439142df451a6149547ff5196e79adb072e9823d847cdf077932bd3aa9eab49f7633d6bab7954bd58204da5e27a49d20b624cc943a4fa05ad493bd7646bb171ddfb7b2a2dba6766ddae9fa476857f604276dc282ed0ef2ed2353c1859aa5b3eeb7660322d994a722b144869d4501f4ab30083bb59a8e892e515b08a45605aa0eaba1521040ae461dbf887cb39883f91416f7b7292485a04555ff3b968c9b529aee67b33658e7ad91bfa564dc226924696467db2dd34d3ccc3ef2fb94fc3f3ed6aca8bdce7da8d0deca672c4606744cc1d0dde04801c9092c0ac809757069820b105c3a2e22240146125992c0924401224046046c045070f236dc121fe5f99227ce09e9e544da8caca47c383f7179ba8f5727ed4b1fab34767fe6448a14b7f9544f3fead959154b9ed3be664cbff9431306c52b595af68fddf781c0100478bafb0161881e30c503560ff0961c08d24cc50158440ed0c1010740020e24bc20b1457232ee7a1f54a98fe438e9f93aed572e5baef26c57b8367d728a667bf0f793c51f4c84668748acd2fb61acdaafaa62415bc0e846e1d3c915a5666ff0847dd01120101db18586b94a85016e30200aaf4e7b412ba3cd5e9667364f236c60841546e0608407458029224b112922f610115101226c20c20084488e73107c6fce4a1cf7a549abd34b2e977826c22e8539084ea11fce7148388fe7cde9743a39be937e2a49f291739c9fb2ad20587bdc8986a8221ae2c90260b080072c00ca0246d0f2012d5a24d789b8e4c4e60567719c934c9d887dac130cc379ddc336bb6847539df553b905e75f391babf5c4c7a17bff2d2d4814c0474b0540a0a5375f2ff916a7a300a2627622ee400318e579f42504979410ac996d09814277cb20f8e8ee20c27477105a8260757eccc395da9ced70258851771046ba3b4b1852967066c76ec49385004090d1dd97d6c86019082a80e8e96effe1901fbe744bef5259fe3eebd5e9392ac7359d7bb4fb21ec1fa674aa3eb6321cdcf29e8a0dcaf96c833c9547bb4ac5ee8c5c46d9c8337aed4cd7c6cc97d5c0645b6b5c24d8aad59e74cd4877e895aecc886861c24aeb4263d19ad815ed3195213ae4fd6bc3da9c894e883ba28c23674094833211dc05be828ddc302df3c4620e7ee2429a453b5a192df45269ec7c4633a8aa368b9dcdb6ca0f9cb21f3bb73e547b49106181038b115872c05280f004a113e18d3dd34dc28eb3c9b91f9ab2f14bb14a3d11847975c6ea04c319c5d43f951459cef6d85275a346517f2a0a21c42c88e34bfc26c539f668772d376b6266846816ce56005b008ad1248c83e34bbc5af76807068152c80fa12e84b83489341a8d13cf07aded3c554af9c0870f21f0010b1fbaf8e012f98000ca0735d385f27493582c59be14046dc6de7f95385397e121b3802c48e62466a64937a31f12a60f0a5d4b2b4dc7314e147be133e40b236b73e6faf9017ad9784865b5e953e9fd42f8325ecb9f3c403c30bc2fddfda078393749d0f47debfec3aa8589d793e5fc1786a95746044301ac03302360413010ba3a3a087437cc40261cb308d17cd24df2f125ee13b3a02f719f6b4dce999cb332da0dad978df9cca35de74f7321d8ec3ce6258f764ead0f7eedf779d21bda1b863ef326f79c632aab7bcf55c149a0bb456811778488bb11711cbcc810bdaa78bde8d5a4bb4923b7823dda3d38f21b37b993acf812bfb1329a7bb4f3e88d9f386cc3fb58fc3c7aad8f168f371fb4a8873c443dd4d143047a78d28309b25aff6a3d309cd1f93c1c77bf1b41b1d6546dfa481c89a78fc4cea42543eabaefe5b8ac0fab14bf10bd923496ae1df9ac96e6bc475bb16a6f15394e9ac7aabdb46c87b04fefbdbff8a9f43ed277e3f41939ca14e428933fd88936c771162d94ceef08baf4a8d795b0f73e82f4867854e355d15e4c459b7229dff368e75030ccbffb75e2b80ca9ec61d5c21c4cb96479f326db950fcbae3ccb52ddf7e84d77dfae3ad0810b9a34b1e9f0c7ea83e094c13c804138fb8fd6de0311788fc5a1cc3e677c2dcda148916223dfdeffe103052ce9fea0bdf76c2b2a8a57a9900c26daa04aa778f4a63be8c00f548e74cb19755c938728987f8a8b4caf5c61a46b8b5db9381714d9e2a2f56a9d8062009404d0490464e4b395ba0ccf8c9d7a7ead93ec9d3ec2359ea3f2ef5a3884c1a113e120452c43badb8616b13ec0128234c21e7d8d2f743a614e72249bd1cfccc2becb5872787c210e8f5a727ca125ce79aa0f47d7f8533821ee552de7387f4c06bbf3b18f7756dad1f085c617c235ffb88a4b8fcaae5505a5ec6afc291e1e7f8a5d71e0428e77fb8cddd24de3cdee9f8b58a0bb4805babb8322527453e0e9cddd45a0e82ef24477912edd459ce82ed2448b7abe887aa228c2447791253ae82213e8ee16f55869491a7dcf911cd5943c5aa6eece144c4553580d4e589da229479878fff7bba1cc0d1f70d5deab37b0a09a43f500950caa23ddfd5f53a1e8221e2c229e134f09dd2db9da7b221bb8d8f036dcb0f38768678f9d28ba772afd1fecfd4cd5d6f912e9cca11346e708918e8f28c7e2927fd44f1ce72692e9efe895ad05c257ec6674ac1ef8b21d51ce037264a21c118e19221c287070ba5b621a5a93ad7e729334fd1d414c6d8e94b952d9aeb25de5fa75ceeeb33a109c134437b56e59bea9a1adc56ef18d072932ba25c9fb210aa69048ed744bd21005472914aa86224435b46a685102b248409504348a0b54901443445234d02db3b5d7c7aefe481094c5a8c44efbfaf232fe8eb34efb8aad95c1ea77b14a63b1097e9ed79b346962634d3f13412b8b51997ff793e3beb33e633ee6c7a2575a9b3e2956ce2a056483bc36497e9c840452189dd96a610f7a75563a921fab3449f21238f2d3e7c73eb6dcb6ac759170e4a60b7efa589cac1fa23da82879be3cd46a6543770b22a2010c0d4bd05044e32b69b8c24486275fba5b067c25936a297e427a1200af52212f5918956e223608a0b8d26c9dd2d95a20da0402e795a21954f04af195e03c11e1a1453180931bc13afdb3a27f9e319af10f552adaf18a76f410420841049bb91042084184fbe3041d9c221866501ffc4ac1aa1a6133f63dd8fbd3ebb5fa51b58074825e3a36a4807e5cadd7ebd45d44025cbadb02222783889cc8f172e2019193251a09f56b339eb04ab31e0add3d52d227f4d022271d8c2ff40227cc76165628d111b011ea18a48db43181f374224202a9454d5410562a20be92a6aa6911933cbadba39305fed0f784d6f5dd2b63953209c5f766b0daccf8f1952fef69a5590fb41656eddfd7f7baaffcf8951f0b590bbe62955a7c6d6dce1e56a785bd32b6e18b6665f8656b7de5c7b2ec51daeb653059ce8ff1cb82a26767afef0ebdc609d6e6ec15cec4d7ab366795fe0f1463fb0a5fafda9cbdc299f8ca8f5f60ceaf58a5d90ee5c723ec85f10cc7ae7bc1ea88e72c3fae31d37e02fa0102d271244890d45021aa99a2668ba8464bbfeebf98f8c97b265804c32be257495473c5a54891d24d44881a23f73e91dadd4a5ae442095c00c3052faf4a5f3358ed749ac16a22174017a60b51fa054e3f95fc54e9bffe8ae1b52db8d182979699d2be5e51d482ace5086698a885510b46442cdcd13da63ccf4b791ece5f91256221ec160b1e7b900ed13b61f8278e48c919571226d787fd2d81a012991429a941a4c4c311253103d7548a9ac1463c9d5c323c794070e220e9800889456212391eeec5552237d22d39f0f1bce1a6ac364fcc855486aba514662748b9efaf98c5cfdf845758c00a36a2153a68f9511f3b0b82a2544c10611b8017fe5dffce453b72afcfbe94f80bce71bec4390ebfd0004a5b09876c25a4b195d0c55602d82d93244992c4821674d7cf0f904e6f347b6c341ca0b1344fba5b7ed664aa2e9c4d8a6debb6249bc80e51053691119b282545189e53848d1c571a6f4698bda183d255c55f70972f71d87495db0c9beecdcee78b2348a51c276d04a7b492f2399d66b011efcfc7f571a633d8888ee96c362587f1d31d973edeb7ab56b6ab6c57decf1b222f4c49e3c35e7d7a77803611c6fc26c297ba8950448bd0a465fed133dd422833532178e99ecd97b94208b7109e74b38ccc61abeb3322da40f8a34bde8390c40602d72d39aea5e3a372b90ed08f8faf7e3c47a7a72785e33a372d15cf08cade40e060fb6004db07446c1ff4c8cf3e786dbe1fce97c15c06ab0f72fea9360fe4d83c3862f30001d582f6e6179a7486e39ce1b14e6f5a3094fdb0ca7695a58f6e12ca090ce2cfe61572ec5d0ba3b9d24ac1efb3a758a5b14ab722bf15e9166335bd7130a6370ea2e8ee4ee1d07eb571f0a4e5b5ee04a7372266e846a48b5d11a961eb33537a6b2f9396da9adb7ad48790e93cd75428ee3ac48be81019edbf439eb4c4a4bf7636f2b14e43ce1862869c79a2a2140202212120240b4232212b0c728241881824ca2047ba256792f541534d071dea0f084e5fe2add10fc771b464a9d0cf8f4bce86f311a45e52dda0e7064b6e40820d3c6003236cd044da113455eb25416e20c815825040101e041905628340be049243202e0402c81840a400440648097f8cf107157fb46a60480dfaa8c11435a8520302f84186bfb0e43f0afac924cedcc6c64f3f4c546e92a456cc82a31f6ee49f75c1c119861326bd552ad51f374913959668c791535b55272ebfa89b38cc9538cec64ddccce347fd50a10f1af491823e20d0074ac4471e2d7174d061f4231f9c43144882736886f492468e5f2615ceb48aa4d1e8b34b3c06cee574ecb22bdaef58ebc24fafad2abba2d91e6b5d556ad3c76b10dc60a51b952bb5e1cb68d7a2706dfac428ae74ce6450c31e7cf41e6284f0dca3d6dd7b5849ed319a578f32dd9daf1e60baf508bb537ac83cccd039b3430fd489ad3cc63a5de251a6f11083c797ff1d3cb6c8ba1b0f9e6e3c8c786ec64c9b1923cd8431c3a55d96325383193773479996774021affd4418cca2469d8851a828357b83ea441c032f062ac460043bf2b0c3047670c08e1eec28810302f2255e63c597b895fcad8ff6352ce56fc14008062f18c05087993a26504768f240eb1c8b35fa21d1be8e1cd7e04ab9dafd8dc5a68f15d391023ab2d081a2830597989a438c39642dc13265c43254ca74f0022d5e70e5747271336eccb6de0f72c9715ee55c70870bc2b4cc51b9a0002de0a3bbe510f87886e76dc19516004024072172a8400e2d46d0542d16b3245d3b720914072158c461250e2870c4008e0ac071e1c8e96eca0367bfaadae1e4a128cb3973d2bef1c71b3178238937bc6ece3fdb89587622f6ff38cec74ec464c420a30519069009824c0b6ee4e1461426ef4e19d7e25abec4bdbf7f670a9cb8e6ce0771dca0c10d16dab8a38d10b4e1431b2c74cbeeff63c11616c05840031b3760438e6ece4d775ecf4bad408f15845941162b1842b40205ac008715e8acc1c71a195883893578d6b85143066a98d192f49e8599382b24efbd91896485e338eeff93a451382fad8a9234ca1f93a4113e996c167d98e1ff17fbf951ff2fa7a6abe1850817a793ab137177112f1eabb4549ad5a68fcb31e7bfe1b4f987896ae53247e5753abe1fca9ba3f21ad8fc3a114b18c5f3660280e3dc6669670e5258152d0c9cdfe1e931dae18b4b94a6bce29a9fcd9aa4137112d0743f143165420b1d3ee5a7d27c3f4c75622bdb95f7bfb31a856034b7ea0f511b3e763b2f7f34e7da9d464335826e2cdaaff65e1d11079aedf93c733c6b6d78b09b1e9dacff1d9497485ec2a828f6ab2afb559aaa4d1f5c9b3ed5d28ced575540ddd33c140f4d9c389427e20edf2b8c85209db3cf282f91669e395e9a797a9f2dbde08c3d38f3cc019fee9cc08c336690e97e583523ccca0c2dddb23383d59d9b5102949f4dd8df98804bf77f26b02b1374778b4a20477797c0012508bb5b7ac6a24ba64ac06a598251b7c435659ce996658c916510e1d4557baf8c26e3036464e996938643c60924c82997233033022e23a041047b888009114811d38798294462761013050bc16808ab1fa320f5fc452f4912af76ac730449ddc474ce32add1ec2c147d04639576d607af8a34973f44f3b59852a712823442904508b474b73c713a63dc314619636819e38e914277cf14089000410a042331f210430d31821043d65dfaff706c5619e7265952b9182398399d5cdcf4301c10462a8c145a56f14bd9ebd7e62902030c18428031e58b425a9e388fbe3837c1fea3f687702af5f1dabc92ab74fa7c681d4fd97fb1e995663a695266b1f331747c81de03a90c074f1e41bee8dcfd7f5eb5ddcdb52f7aaf3c5f59b4e3b579b26eb0f8fabf5f07bc65e7c5182fc0bc17b56ee9857777efbd77a639e0a59b031c079e7477e354177c48521763641716e882852d78b3684b59ecba68b20133366fa0cb68035624c91bf90662e8ee06a307982ee198a59d8191cdc034693002683969de7b95c21ee8c6fb5ca94f152fcff081b0edc958ccf19e66bdaaa301ae6f3430ea961c1765b800d3dd5c68e9961c17353817475a5a9bb760b205989b2d72ca03edfffd5eb4afa7934b0b3fb498420b1f2d485a8c90051ff58a321848413a1ff4af3272dbc40604e77f74d2b2e892458fc4b25cedcc61a85531dd3a2998ca6284c598ee96250ec3664d078b2b5820c9001addf231154792a3e34ae1e800b17472f0c4d462104a068ec8c095965cb6f6728ee9fd02c617ef8be88a30ae98c01530d115ad2b38b0220b2b5a1828040362b478f368bacd4e1a95f0943908729e4d2d2961f9bdf4b139d619fe0d4b423e6fe7a53c65f9f56037fdd25c1f035b3070a5a5ebe707483eac9b54aae0a30a0f5451812a78a412e9a79fd1dc020d344899100f3bfc6cb6a838830a0b50b103150198e20d792dc795a6ec3bc7b4e6de1d8eab4d1f59412bab4d9f5a2dc5ef15c9572b34343f9f37c77d6673b6372ec5fcb171ceb08b7654b96cd91cf7faa13d09617abdd26c9ea21d795c7affe2fc3f7bff07fac20882f3ef0b487e807474f0558ac7e5403aae940e900dae7323eb8736fc172a320712cf92c5bd772928cf92c549234ce5fffd5ce2d95cb914fa5b9a2d508e60b59c8bd8e65c39fcf8fbec7bf3feecc6392e07e738ce837047a1669e392c2fa1a2449951592b9c3e52dcdd8170f7076938b3305b398ed371259ef37b12675a451751a04604851cdd2d6b6afce4d157097f7fbdbb6d8ba078f9dfc462776bc9de5ca9e88939badb6f80c64c3d2a62a13f4eaf7e58d3d1999f833dd0f2e0b14e1f1d787ae89e79e6782a8c8560b64708667be6cd359dd3e90f226c74a981081a5d4660e5e0c418e9d1ae8a135c38c0c109568e4ad4040c9a6802e4e1ca0e57bca69ad89818434896bd173195a0c8048b32d1b0259e5882d5dd3256e9b5381ee6444bf804ca4c5a6a0236a8284a9c40093052022990c004441290c205102e01b89bfed9fbd9e2107cd9d8e16e22165842688926233ce44e848923580831819efe6c8346f04e1d6b5d78acd3a787a7bb79e8232c2ca899cd75ca58afa16d024e26d081127d48d2c84923cff2bbd9cd3bc3ee224d8e787184179e124b74b71230256c50228a12a56ee9253cbe908736bb7b4d5461013e9c077ce54b024de0941318726876853d7a83b10e1e7f8a87bdf7bc9475618c73ffbfd9c2deffaaaaec5755a9ef06650bb6aeddb94f05c72aade265e1c9ba3fbb3c21156b81fe9bb852c1e34ff94bf3035917c64f6b3fa397952d2bd77e956bbf82e1dcb0329e3d9e97ab686bd327e3a7e09d3af8afc549619bc5149e197f3f3ff86578f278aa7927ebff52e9c1189c58e71fe3f1a7b4649956a9a205cab55fe11c8cbf7e9e3d301beab0f053fca00884c79f821fd30944fbd58321bd2eeb52e1c99331edc119d39ecf36e81b62650bb654b55fd5ef01c1948ae504062ad6a5338359c9f567d6e77472d16c456284040a0d10d380dc80043480c9962d586ca1b2a586ee965c7efcba0f7aa5a713cde1fcb32397b091574bbd3a6ddc44aa3f78a4e2fc3b6eecba29c353167a691c41a61ba3a2a07144041a7bfe5d695e31e5d7ee38d8515c5fe85a1a0d5b99c46238fd851726ed6f1210a284f07409e1998de0d2dd75c28c08db0856771167bafb22bcf4f4eac7685fff7a1145542a449ce9ee861121a6674889e8d2445831552b122143f75c09e1e9420d61c6105e86a8756a082bdd728851778f2016ed6cac0b00a4f1cceedd677126aef39a5c7fc94c9cd107bb19c390e9a68a310c39cae4300c09e139678fcb6a5de5194f5655e1d855095da48f1aace86ed4cfe68aa24e2718fdd95c7de8d1170ae58af26028baa2c03e2808979ca71ab92c66647140a6b3d9ff586b0308a6b2f0c8f185565948dd2a208a00a20362861fcafcb046733a4c74a64c19fd702fc9b94e7df035720e8f3f859ba2e34b7cca0975338e32668ac150966b9f72c9a15c513c15878a723f0ce7adc271fed9e9f7f397e25ab9f62b0ea4f39b13e3877df539a5e7d1d9083a08ceef692921ebb8c63d21389863144310339480a115864ee8f6d3c8fde43cf86907262626b22ca6ea832ffce24189e02594979e88282f3d1185e657c1afe3a9ee3f6b057ee6babb57992727e9d7d0df101382c574f7165c2d3672c974775f2fd96fea8ebab198e90d8b19d2fb98a7b06ce9ce292c1c9651876760bf85645261975087fc2defbd6c57764bd7afd65ab1ab7ac66e954c6ff54b0dfb6b0d5deb084fd9cc4c63bacdcce8eef7e66cd6a5bb61332ddd3d63cd9a7477c72ac5332360990d0cb38135d00a78a4a577edf74d9750194261cb92f7140bf174bf504d7777100a85b29705821367a36dd02fd4b551106476c82220c341a642ac8e9803624fba2537e71077ed4e0832049bdd730cd2f999467004bf2f1f958fc9c3f1497c942128b4e4fddff78ef074f038807501e30136c2d6ddd149a0cb9d0a538fd9c436793ccfb6565a835f9673155fe1fccbb93319f998a9ad26f7d35f3ccae1bcd83829389e963093bfc67869e0e5bde4abbb9bc3970b22058d826cfe20ef837a1043e23b1fc4e961d6030d3c0cc2c317c9916c707e36318cd2b2f350802b28d8ae88b962afd8ec10c8b6831c5b768861dba1051dd8e896a4eb6eaa73caf2c83952f85d4d8d205792e129c33456a957eda792de0f3d3875c0b2e960ca410639886959c2adee0e406f3954d972b0b1f2022b18b062849518ba9b23d978a6bfa1ad927626d30fd52a68545162ab724495203aa408926d413c41456a7aa322474bce237123c7f531950550b9814a0aae31ddd25a110b9412471afd385f98da20570fae17b6561e2d32cdbd773aa15c514e2797e2211772402600ba40326c4034388800872d383861c9c1da8235c482c212fd0cf253c5f643c4cfea47002b3dd05861b1adaed8565bba5be2299baeafc6e8b59fa5bdf0c20f8176a8521fe7ac66c8de1fb226d3c9abf353556e82554a5c53332862935feb25d3e95349516f2b29dbca880f191f928d2bf9fef69ce939a347899e1e2553d69892659b024e51e1863cb61bbc9072d2efc4716ecadc73ee92f3e16ee8b9e1007ba8c868f9592097655aadbbbcbf34ccae6ab2f1ccc193828d270c4f8ca7890d6f6c3658d12dabb83f063a55919c17ab9e05ede72e49e57360f41fcf07bd4e18787fc87a9d9fc5947f9f05adc591a66be9dc91a68ce7e7b3ce58a55e925fe54cb44112a433d777fcf9e6affe5dacd2cf4afcd9f6486f82933482cdaa2367b5d77abdb876e07cd2c839ee63b3bb33e613a45f6b87a5f26b3d9c899eaf7e5a3738e65c3a258d26cdbd6b6138b49705e3dce4d73a141d3cabb8329e3d9c9bfc947f469b2f9b238873385e58c90e81600a3fa1858efb42757aed3d9d9d54ad5335391d9742b3aba2d7f9340bce2934ff9bd84bffcd16b6988a4bfff9b9c54e1ad16c4fac528ca9cfdfd8950c4f1ebbf2dee60f8a559ae9acf2d5aeae5d61af8a3b76a5aa76e57d1852f15e069b0f44b33d19530e3e48800a9d455baf98f1382bfe718e20385d2308cefcd90679f577ea8ffdc0996938611e930e4765c34f0feab30d1aa170b5b85a8a838a559a93617f690efe6c836ad3275669b6414230dbf3e31c67cd140c5bde9d5eb543927bbd38b719366192d4f252d028d3d9ccc2feb26478ca783edba1bf2c3033fcd1db0c6bc86d862e2ed49a41d633f46c338cda4f251fc5ee74c2b97e8d558aa9cc00e4e37367b34d0618b4f7322c860ee4e39346163264e9cf62a655ba9b059b0c2fc86044fac991544bff2686337ec2c9e24f55ed00057d376f887431845b0c3bf4bdaf170c62361836189edc1fb2d7f3944181e1484b2766743bf1d2727322eb6e127a73b2729282cabb3f6441d002e57ac558a59bcd173632f9b26c53436f362834d9a355f4d5644b1304302984c9164c74605292b37fef5fc8e4dd1d8eab5edd640c3609856475cba73e45d6662ae3bd09c9564ac15612bb1b84de4ab5845332c0b6448f255e5a8e935602a7ffcc03c5927c494b292e35fa7153e75ac26d4b6478e105d241e97dd4592fcbb85ef146d62b769825b1ff10f5daf4aabd9142b0d9a5fcbd5a73775c8e309b71b6ab92cf0a63e7b95efbcda83f986baa89f5aa7117c6b8c0c58527dd021f3d2d78d12dbdad851a7a48a70514ba657d217b3bc77463c14cb6950530b5396321ecee66a18992322f7329f1925262a5fb679b9291bdff439232ed3db65b122f8d3feb38894cf4fc491c89993a6148b2e851ccd8821b920178199b7771968bb0ad304692fcbbff96cb4eb5ad30c4b6c292ed00650e00c57600d6014a90a21dddc7cefb12fe6bbd3f8da095d192a8b0840a3d9b0a2cf496c217296049c10006b8c300670c6100ba190077330b4b92c4e756002e5b015c5b01986c04b86323801504e808308224914612a4f3abd639ce813614ac6c2878b09d30c809569cc0da4e706133818dcd042bba1b87de4c081e258d63f7e1ccc24a392a532762efaf7f873d7ae392f443e014b3380a27787fc8ced914edd799417146b105c3f7464e93421f297ca149c1459342025a7a6f39ff8e8e60b5ae6cc1d67d16ce166ce1cfba1229ce31718e7399e5b437738e2994268e69d8f256939ab0890dc9d48978e4349d9cc4ab496234574b4bf62fcbaec41f29b6e0a25b040274f7d6cd4102da4b0950562768e916cd09329a138ecc11aca2ad31a1ccd39810c6042e3426d098902447e5a00d4928afd229137b309d570946d0947085a60404d0d0044243c346d3d06c34db984d8a6dd26c354cdb5589556ac2ae9f9c5c298d88494dd3888cd01c2983e64812475e344748a011400b680440021a015421001d680440a209001fddeeeef5f59c9be4ec4ecf504823d28814ab23479ddc439d3eebbd38f9fa6a572fecfac9b13401b84213809d00884043c219249080041f4830d18c5007cd082a18618866041aba1b1545725ead9bfe86126731acd42b758c8a22b389f3799ff3d4a95acae970f8f3c42a97a51afdfeb2a4cd39aa1236fd655198e8dfb9cf44bbc2938766c12f5b5610167e74bf5ef3e6dad388f041081da00941d62d71cdbc5962b1d24ca309218ae46107cea3fd8ac6c8194b63240d4963a4a72506a7632f792a10c4a101e1053420904103c2100d082dd07c2047b7ac13068a9f8be68321683e18d90cd60f432e8bf6c565d8d76ab178c38f1c3bb7d98274d25adf0b4ddf61fa42fef1057fd8fcbcc7a4318723c777eeb8c414c4a47184eb4fb6ab79270b0922e3912c9c74b791a6e1c0868608cdf1140d112e4d43a4266988a866302ba7938d52b33763a6b019e2d84dcd3b59340d029a96d1f4d8d98fe3e1bc4e3f84cc215f0ea975f721a536e48eeecea13743226088ac5b7292fb1ffe8672de696a424477cf3b59a158e78338b97aef7d2167ba9030dddd93cb5688acbb0b79424899eec671d09910224612a2a509a92147b511e2ddf3ce709030dd128bd586b4ce99cfec1d64102b3d88ad2f73ddc0109563fa9e857916e7750302d8a08ecd0642d8408633809cc1e2cc0f67a2744bce6bca9938e726c9b94990320429424a102c09d2d3dd32903f02b92210afe5064813804000101d4064d8fe70e38f33b63f84b63f46a8c11735582247c5b9122512ffef30c14a94c81c15931b9738e54a94c81615971c13dad71a58f1230e3f18e00702fa28d307127d38e1c30f3ec4a0b412a9667c594e92c4c159edec7e36c5aa05bd4e18f6f1da3c4be19c39ee7229678a39d9e203870685d0400534e872678eca9b34696263ea44ec778e2a9f77b2aef44683146670831984a1d6028593e6b9cdd33feb0a5d2ec31656c5eb367b1c4996522397ded754b1f33c0b9bd8fbcfc9b65abb7acfe28493d6cab6d64ee28e2dec57bb39a0a9e3093d6ca0c718cf16ecd2ad47a7c71189f330930798ee967984328f267dade771a4f178b9f0f8c04c986ec97123783a99d172ad9926324f2236dca14677fbbc339c7e9f753f9438e3391d67312561c7996e3b5860c707a6bb7bd28259922ec53eef64ad1e6cd5a6cf054c6374df4a63dfbd50b3c3b95a1cfcb4fee0fb42b059a960dca2a3b6d1c1a203853b52decfc1859bc34af71c27486fba7dc1152fa02fa871010c5c00021700e182d3e682520bcc58d102af054a5a600039c8c8e1841c9c1c328e3de2d0228e1a0702e000048e312dbf5a771d4c65385f262f75afce2a0e470a6fd0e00d2e84de18bdf19121c10d3cdcd8c20d1fdce87143006e74d086196d7820b7b16a63041698d1dd5e6947bae0250ed312c7dd894dd7b2c0002c38c2061a6c20c1c68c0db9023e5680c689f36bd728640d35d610625b63688d19d638a2460cd4a0428d2d6aeca8e1c19834c62431a6966b1f942489cd3c70dc0e930edd49057c2f5d173615c4a1822854b0830a6648c38f3458b0441a39a48142b7bb80298ed7b72baf521c9982560a5c48810968b08186043ce80d8d07a0c1838608679839638bed0c2f67c0ce98a15b8e9c9b4c1214bf2449dc665a53feb29c2489d0b534d67f7447cadaf4b1d77ea0381b715c7ab62bfc148a23ac23dd1d6c0ce7cc7b338e74bb7bf3062542c94ba76a796aef8d37432c703ed0e9d4dd47a1807278a244817d500794c3732dceca4b3c51fe5a9cffbb732dceb5df0b63cf9b3ff3cc61618c7acfe2bc6761b4af18cb4420a1485ec2f95bde87f3c25a9ec50992e1297b554be7cb669edefdf0666befebaf88e7fd211b7aaa97fdaaea65f37c65d13e88fa6b7144fb7a85a20c4f1e184f946bafc5a8261dee399d4e27d80f65da657a831a3345f144c9310c9d4e42b00d74b7506f62c694f044b7f742f36932481bd9c484ab15c3851824a4f7fea18561afd4b33fb8135b33cf1c9ca7351c5217c63af97d5c40df0ffe7efe07da1a27f84018e3fa17e7fae1ecf2a430fe1e488627cf78f39ec5c1d7ee00e1bf2c3cfef7f3fd604cff77c6fb4314fc1c9cfb5ea582c349fb7ec49d16ad32d6ec65613cef6461fc2dfc18a7c2e983bdf93c38e7fbb15f558591a5bbc3d8610ba315460c337bbf3046e86e30fe00a34cfb090c1064d1be3630200046adbbfd54ea36811e2300a34976e18b177c01822fc034beb2d299bf68e20b24fce4273c71ee7f9e1796adbdd95a209f3b9b7d6b44953c95975051b20d42792a4f357ecb9b161c67e85117ec15e25c7f86a3b37d608bee961245743cf195b01f7a65da7dc0eb9e49835a1f287960906e3cfe94589d402dd47da52380e50263746145acce0bc5c64d2f284ebc7f79df4c74036a1d00b0b70ec8ee4a3fd321da54b409c0682b9acb02da0a0d1debaee916ea8d03621a5f4904895237912188408083194e1cd874f7d75b173730611b56f9431dfea1f9e0ec3b224a109162db40972c6bc84008de00107ee201f641efd915ea9bdf1305f6411b6809060ccd1b98d0d25c604656469b50bc0d8c06dca8f91967333107d35a588b06b89406569e37b938011710e0c28acde30b7161c2b6c5192fb58506b660628b1a24871fc839c738bf6fa14508b4e032b598a2c5a8bb679f8518592c91c5b7cc75e6da0fd9192b9cb496e43c6f725ec3496b495c827d907bef6a0aa4d16d2bfe118b2f58b8b0709281324d1a6520c4995a9c01d21733dd92c4e1897f1cb9cf073b920c4fd9e8cb966e285f6cf0bc2bce20a15780690e7ff68a70bbc2ca0c539f2b9e747b7f6d8d5d81425b41c6ad08d35670692b74ba5b8ee09d8f42a15451420c748046154b54a165aba24a151c50c10615425091372a4a539099828a299298828629885c00cc05b8702cda57e7569ccfca9738cb67f453e74c96290d56479c3f068adfdf09e3c69a923c703b38f81d6772251273391c959707ce28adfe372e6b4865359b972abc5c2f54362f46c06c16b8c2023f5b05ce54c0cb56812a15f0408a36a428629382c916851cddcd5d918bc1a54871d2e9f4d917e7cdbfdca4438ee35296634ba4d7795d024e11574bbb79ad57c79217c5dca2280005ce6c14f0830251e0da9cb158a31fe9bd20652987021400010a35a0006383820b14aee63816e7a613c73d71820e7a7ba28c27b03cb1c2d685902e62da677a27b65c7e639d3397ade3f4da45004eace1c4059cf0c109274e80d0841f4d4cd1c4029ac0e926714e5c0aca890d0bfbfc6792e5d80de78464c3f225eee34bfce65a12e7c4a3f8c8b1ac35b203a8a49496eb20756866000020005310000028405834209009c5a2019dde011400005eb048b46844972892d029848c31320220000000000400f24dae78414e04e71dbce5e52e05b8cb627250b3aff40a2224cba7eb1cefdbd7dad20307558912e05b413acd74c85d3158992d1fba2f01b7f28ac145c72c9f7b8120fd43e48f80e93b9b51a3138e97d1c40827c1db069af9c4e38351d19754798f0187f4450ded53ca0465ad003d8dae25b42223d5e041dfff4db0df531959001290a709a73d082bbd1ce628d8057b13a2d5c0b9eda6af915f8da7f4285809d5053ed11016d45bb155d2818c982233f2451bbd2a2947ab2efadc1c1294c549c932067e89c8e0a564517a391536e6ec6d45bba3c1fdd5389948d99b12842d91f562a3aaa228feaad1da624733f7b6d8f8ae300ac70174e2fbd85224564fc9022499d831bd457f95bb0e00288296974fab280285379f0819d9d374d59c2efb54b901bcff433e4e898707f8455dda0f1b76f7659a3271fbb5a06b534ecad4e2284961964082d22998faee1f526b80508707376d263fd634c4ce65cdc8cde40e75e22d772ea9480059fadfc607e4d6dac6c53722a3a6f4d0e64612b13dec58963b04ece72f7f1eb3163fc0ca05edd9a3c1185fa66603b7c16eeb66ff5ca042b79549c0614d5b23f4882f66eb3b045241f5c8e1bec0440e2e3d082c5fe79b84011ae4ca38ab4cfde3f7dcd80d9483247451377f4a76b71dc5816002109d19ac6a9e7004ba6210df718c0cd7a7ba343880d9870fdc838a73af14ce266c57a02b1a3d081097ad1f2ab6f73848274ff813e192e763017fb55c478d346c6ece4b4b778e81cb714edde03de3014d12b4c41ec53c6dc26ae72b69a817a96b5363236b7217feecbdc9c428be746333c57047b30d5bb972278828c9761c17a4794019c2f22feeb55f6411abedc985b29c61ea1e5fb7df71059ecc5c8ec5a609cc23f5cfec9a92876a89d94aba7364a05614b622de41197d9874b211f83154f4972860fcd54a7563285bcd20e16fc9621a475bd4704026117f83cb6bd2758d041723b4817e0c422d9ae42668d9885ccbf3f6aa5431f7c123baa3f8df763e9d2c03fa4b8daf237ef69a9a8b2768c29b26f280a9a57ea8884895bdac139b9fec5f298abe2e5c6f09a49f5d5a317ea7cb27aa104ecebe2d46e7088e99e75ffe5c1a3db7ac3e5f7d3d3c76c61f7e2d7903471b11a8f07222624685c007ca2cf76280f3ee3ab32669a1e8142b01d3524f4c5ae86bb676d027d20611e31f6554a1973beb118509f2fcc11ef81ba334f69dc12b344aac04f08e5938ae7e1bf574aee4817185aa30984afbd8d5f349fcd94973eee03d9e43b686c54d1c58db519f472716bed5fdbe8c19ef092b97941f90b52c91727aa23407e0aa4b8021ad29c45d6a792e383c8798e497001f0697d4edc37f36421bb12f03c72d147c99e83a820b9fa056c1470eeb18a2913403181af912bd6969794a29226cd845b8e31191c6772c3e0374259a9798731895ec522eec5ae43d77fc28a5a27213e317c2a95b8876fd0bf371e8d99d5e749767fb6d5c360130c03430828f9a6e08b2bc666f5c32a9e7bb2fcc7143c9546e33b69cd4738b0fac6af3260e085f1f75f36a5b3383d5ba991ce8818b076bfef8856fe03e369af3917df7eebfaff38481fae15635ab2369632314be98b9dea452af47c57f5a9e2e4ed6644400815323f51a8876a8496d7cde939f08ec54615ca32e154350683249a8857a5f110483e33ba93b784348f253674d46574fa49649f84f9531f079fc7873b730fb8e64878b943202cb01a07d043314f6e58d3c5bd40c48aefdc4bff2f1404c777b2d94860449fe3bbc93af1715ff9b23aae3622f478e278ddaeb441a770bc23aae0ca4c918adda712cad62a3acfdea7e65584886543088ca0d3ee43a89c69f534a645d1910fa8fdcc3129443f4b38f9aeea58b674a77d05a3909da743d3066ef94c278a149fc48e7698a1cc0c0e7384ce2d45c52c318177a25c1e16ecb6bf6fac49b370309d49fdadbac77b1abacfecd35e1656048ac10192c8a6bcd23214a341c01e5c491e47093c47884c12c502be2df4660dabba038e3886a4f21c22d695fc2c0be318a6f27111b90da01c85c010c6106d61dd7face4e9fb14367c7d9d4c99763e5500e786b8d4232bc00d1cb89b697fac5042bfbf3fe1c248e4296cab41cb7d65ed75c8deca6c25eb4c28e5954ab2e610b011ebef8ef47ae2bb4d7021bd82f0063764b5955f7ba20daec54492ed103f2463e9cabb177dad58ebe7d1058807e1f17d3a74487352fcd5ab23012006038827d74779358ada4125413fa0ee1abf94d97291eeb01f60dbedd54973d78a4d70fcc2374c3853c4899549b7c0f0037f29b3636efa4a23965d3265d3f823a516cc54b12a5516e6f0164a512bce1e676de685c08d328c8d65e520e46fce9c4ce62ae9422172fb13deb0a74b0757aa0afe9e6dbafb14cbbcca18c39858d4f1af65c7212ff9e8dff8a75650aa6232bc5a007fd0a08a8f6d6f0f8d7ca8cb1a5e637574db856f7ea2e59e678eaa595a7e77d8263e913707e9540d2eac97967c6892a8a531e0b75672cdb802aa7477f137faa00506e3441ac2abdcf4758718e88018315789ce7f485396842524a55385cd5576b723abe8cc67f2bd62580d055ed5ed63b02856b46c5f9fb4489fb48b8e48d9aec9a337c519d8a130dfec101cc0294845b4f890c4eb7f91944270c34d575c138cb53adb9ce1471ae07461555cef715fb7b768d61f18d360fc1fc3c78211595e1db86db7470a81adc4f79ca7c5db0618b38f0d100ce4aab54d6c432155ab7e4f03c7d59de3f641e95d77e7509b2a5fcec529ecd3642e0298298c4f356bae0fa0505ac8f08f2ce5bd9cd4ce9a643dec080763e1f8e58f4d95e4fc1daea988b86b62e7052f36e77d73d77af5b32f276d7761473c46f44ac93aea50d433af145e6059d065016b8c9523cda6156a39aeaaa2ce0015386c2f2f33bb7dd207fe4c5591e97091de81d1fca43c027e40b36f693ea4e7c003efa1eb9553df8115868d61e599aaa08f68fc12de18fc040bf116dd11fbd76345da250db11fae4f97fa967787cac2d5170688e23efb61140c16340e79676af54a236914725302b4ab51ff85f9d2e6c31aa412c35d0fc4b67018c2b9bd016a19286ef712774bf85085c1c66bce6277673078c8546774a325008e1b999ef485a534d8bb5e92d570147fc82ae74d5fad0e0f252d915c2ebdfe9c369e7eed479bca622e41d9876c040ecba8674842f44ae44c6c8f2669aae8de55ced3108a1f129f8e751ae7f35adee68d4d43f0f0fbd9433414ac266354a46055c4c8cfaca10df44d08a294c753a221ba7992525658eee5fbe1e852bdb0fc879e358c1a7507731087511164fa57a5865ae65a6d8f03790c60e15ee2d31075380fcf83c5fcc2de13be8de6e384c786563e24328e263821564569684817326084de8bb0f9def88d22ba40a8a979ff85ee3d7ea5238281f06452f9fe04b2c836693caf5fcad50dcd9ce32c71f927f498fa608a256f785a5a664a9d6c6ed7adc849411f05899cc3ced6a1dbfe7425964c6586e40edd00a1cd0ffdc609629e43b31c8f7324d51c6fb4395684799d408b6c463176b7a20b1798b49149afc011fdd7ffb0e20d7fb8fdf32df19432c03b50691aec6fca00dd2020441b95bb1ffb77700bf9a57725964a72163cac329b704d8eee7932686a63cc5181ccfd3a5f44e2b0908e003779896cd3881c0ea327e41484767cbf13ccd15c23f97315a7bc8e6246437e67e083c4f66f0818851e57a4f37f58f22a47a6c02f92bca4868e7f16af6699d78baddf4d13990b0b290ecad10c2123291b8e93c483306623032aea774f6f42f297b209d9d1998446d87ceb1cd41dd0c1e80b7a71813581d846fb70a9096ad01bf421c958319668c5922d3ea351dfdc916964c15cd43a7feb80a56d776f20a7abca0f312632b66523d03e386646d05ba94015a8ed7e35208b262060638fb111149d458c0edbba5ab6c3ef2e940b475f93d411cf3c98b9b77b5cf7965fcaba2c66516c0f1031bbb788b12c74801c7554938f24078e26bc98890dd85dd6b0487692871b2d7fc281e3dc6c52eaac22f43477d2ed1eafd1f4478c8f7d7e5e8f91f8e687e19ed94709d077b8ae3cda429c91b1d91310713c3d2c83460afa0e8df52f1420188e0e8606498fa61e5b329bcc95285afbcdab3614b3dd8e35bd9f5c89b87be427fc1c6598820d09b349fa26839821310133f96eff397138df029dc021fca09caf11543c7978b1fed2d19a73a022542aed5c755b46da0512e0c1bbe1b8728e49d65815d5cb938c73b6295bf361c6f470282afb70d602e5308103b3fa1e0bacafd876c938551bc6bdf1631e47ff13fb6a748bd069dd7bb5b960dd1d6067eae183ce58add9beae9c43a643dca66cd23f27949ab29372735eb4167bf57832bca23c38975797e2af6fa26347dc79236df1abba8d41f2b82363672121636a4365625b4ec528e6af1292c6e9e8bc2b26caa3ba3b9506a2ed85971a82662711162dc46bb1787451d16b08a317352460bc84b5b6f39133253c579332f72fb277be6772b539e781b0c137d3eac782d3abe08757cc829fb2103fa743c456f2853fec365fce5bd85ccfb7dc745b3fdda53d2f885f57da953622dfc9646dbfa90af78707e1d324bf86500be47482e3fd89f56f3bacbe6f12874487fa14e424b6cccc882da0eb286891ced309436629fb0dcb31c785a349fbcd0276207e9b97be9e7bca91507611ff305438dfdd622a46454f599a703956187d376ec6268e9fa2463604485ab37200d9404eb223e245aaccbbb4cea05f8045c5b3afe0cacc75e3503ae633404b2cdcaa922b4b74a338a5c573c091638e456513e0ea5f264f8060fa0790ab606ed40c5d0bafb6f92bf9c36d3241985c8665528687bc94bae9978512e457dae9f3cf0ae91c7afb354406af7cce73c8d8de38dc229c92abdecd2070c2b0ef5da2139791aa3eaa7ba250ab5a71daf6e3ed2c50fb7a287b9bb2a01fe7f4f041005be2a9537c19a1662f1cf191959522362486214b08cf3105f519ec80dfc927ee22fc92d5418456bf2082454223490d3ac1414200e1ae8bbc5c85eb4259ce03c2777f140ebe9fc21be8f08f6adf74ca0286a1905edb17fab0dc0d7ef22fd49360ae90df95f099c5e873293a599942c1ce79fbdffd126ffb3acf5bdc85574b593fb64b56e163db0fcc4fbf7d42cca42953786dc6de73c117231d14a47c4cf6b276932269806c861041edfb468bba531a0c6b92c6ec448d45310ecbb4f53d0f7bd3e9f8b278f0722302a31835c703ef729c3a2155dad9beee8897c598b59e1c0f88d614370b64b12914afc72f3d94e4c6b5469ca78c18cf6f9f0bfd01ba4f4866044da1c7ee7ce3231132c407d4af03040d78b8cec935a12da4d49298ff678bfc0d12a59f1c149f428a08a9fc7082d144f395df8933ff67d49b8d9febaddcf29fa5c93daf668a23360af40cbf1fc393e4077f881c47e188c4f8b546d94954eaccaa595eb103bf847cf0aadb904fc9feb46ad3541d64b7529316e8bd30706f889ac409b06b1a96888c58000507af10c08aca202d264180789b13dbfd2f79e7080251eba04a376b1c03c739060c036dce9bd08f000e4fd4c738be993fc3dfbce6ba08994de697349a26526d21e4bd34ea118f67fc12deb333f6b56e8a1969539478f2308269035ec85eb10636675edb42a43c66b639f0daa9bf833da0b68a534944d9c6f87420f2e5074f7c458a74286f8e1df4a295cc58554f272cc155b0f4e11a0ea221aef16439bd028514e35220403aba2f8c9c80984938d83c873c16d6dbf6d2ebfa4a7a313f23cf76683771ec870ecbfc2bb16070e4f089000884b3a1dbec1b6c4b98c1f8cb3a2d0fc86e75c9e1819ddaf107df5a1ed546be376530f42ede45e195330bf6b70de2d22dc2dd8dcdfb9948a140a041f2cc95ef8565741b089e6abd5ef1320b757f196663ea1ba028a43bb548b09da4882a17b14a8ce9832e11b9bb2e582592d8659bca67b1fd8a1a9b38143af8beffd706142eaad0b02029a413edbf06887be950c554829f43210f06aead842f2b3dbe66486c4812545f7d027325c0fbbd574dbb6a59ceaac72b5ee03c2e62ad4954a1b2dd4ce98a5c7f7edec623b2b9a5b59a6246bc35f13aecac069b05f06190d48d69e57169b0d42796599c9595fad4efb43337955969ba12d1f1678dd52e2e83376f62f7237da576450543500b318f1f4c282f4a958049cd40187e99df3126b1ce4261cc73ef0c61115a9b38e17bc318e36318ea1650a6d3db689376c5b3703c2895092f006b084d4ff3d5059c07176ac5136f879041cd4b2810d58018705fc515fe7bc65a5010e1561a905ff8682d31eaefd8d692f38c4d3c13ce86f6801812faf58b1dfd8b1df56d337b208af530d6d0cff52ab6f447d92e5231ea4fe147c6aec9c3b6d790dde90c6c71821cfdf8eb9a2796dccbdee52de2f630e84d41fefc24368ba3dcd130da66317ad2404dd56cfdda84b83865d1fff9b0f1c30f3bbf92cce665d72ce9d82fd8be51eae641a1f98537b41e1ba835e7b7bf94b0d0886557e22066f38829f5965f9a3818c57f230b8969c04111a3c7c68964a9243178ba275c203ef392eb8137d349efe415d40e8e85a3a152ac5019693bd16441ee2a58c9cbe6595d3f40a38e1fd72b5323136c3e364802baecbd39faa6831ddf7f19f1a7a4d9d36b1479a37b196aeff35cf5bcb17d750593689778d159dc25003225c4991614b6dee9a3e0d1b22fa87569dd0f919c24f1b40a547e4d0c1c3924e1dcc84da32cc9363b4b01f449b17c642a85a87d4bd5a4944684d65de55314e43df05dd7a800e03bd1230b1e1308209659392a29f64337f43eaa8ee798e94e177c34a64a8ed25e061cac2f681297a1f8239bd5154a96d38f1f15de1f7bef2aa17b132c12ff6294b89fde39e7828f08b92ae8eb44be5f8c30327178f73aa06c9b18ffc9ee3a44c52b0cd1925f8e3d0af749cb1c9cf5dfc5d22240197e76b2723ae72c190e3b15ae79462ef4ebd45bbff13e5085ba15601052614769f02f5ae0a47b982eb21f43b81ff2bef73c9b57f68052f5aa84ed9e8ff66e2cbceec45a7bb30b96893e9c4f77b48f523cf61d0d048326dd8985d85ac4b665b4d0eb33736594185eedbef358521bd6ba090762b0910f34ddba0aa55c3a27797ec575a8c68e6ac0772ce916d82e7f16987e3d0e0896f9483928afa18365ac11efe2ce856ad640633cc12ad174b4cf515b1c8091d32d5bdbf3aeba36535a19c1310615dc6315cd651764468feca8b43f0ed8a2f094a572aab43d65ae8af7b876f85c2d9cd84ea2ac29f80977a9da9c37a269238fab03197ea1091b83194d0149589a1c998359c1d57f5787ee0ed8f1bb5849bd695f6f9a9c6fa2327ee6cc723f660cb5a0d6790f166f3e6dec4326b624c75702d41c452470e82ee548f7851b14dd7e148139242a624ef3db4a6f44375c3704e6e10de2faa8d009626201f77d62e6a5a856603cd485790708a22ff29e2c54a1324aeb2f19f0927fec45516f6ac0de29fceb59603802fb33a5ecc7e9cabf33d443866f2f567aaf58fd379e20e2528aec81f49af833587a7124ccd44488e0aeacc48e98ab22365948d8dc2a6a6e8905f1407261995562d5fd7128792448e010eaed7aaaf0e17ef2e87bc68ba3a4b41a4b13559fde40e89bdf3609621615788e52ef89c2141e950344384be06779e30ede2670dadf6139468a5a6d2cc988340d26236feabac162b8ce09c4eba08293b124f13109f5fff68c3c8e6c370ad3cc50e086a9e88417d0e8b5a2426a2a08c98d032147852485f093606e3f7e87fd903e7739244fcb2b04bcc4db372153d08ac09e042910d499e40f7f338756642d3ce9bb449c7c562ea6e4225cf409e104a29545e4d75a026d414b4a8ab3788b40fbc085d2e5348cdda9bd7ae503c45f321f824d16c1dec8e2f38e552e01f1c5126d025eb90c1af25d5ffa70589e3a84f99f13f0f2ca5220217a552803f414f01da4fddc826323aab9f16a7e9c8bb02d2297b64f0b5377f3e73f725bab84e38d384698aa7a963aedcc8161ca98e661056ce4c838c874923372bd489f0f010104de0fce1374c785bc19d6dafb0f7e53c59813745da183d3ccac129599c3be017cfbd4cca851903d092aeb3948f4a70fe4fed28da9add99130354a13a8c0fc6b0737ffc31b481aabf75a6f93c36b98ff7b1ceeb2d3ba333481d08225bb1e0c5d5a57321b94e53e780862407aa09390d0eb9eda0e9e5684ed30c6818c895e332edf206067587d8511dee1cca99abe672ffbdbc9e7782e408e7eda2693676d93adc55334a5121eeda61523adf2ff008c1daf1a02864e19cd21cbd170b6a64961e7ad03637986e4697ba7eebf3fd3654bb404aa80f0675e6f49271bd37656101534ce74a2040adce9ecd505edec5f593f24e0d5f4ee45f019a9ddca4d76950b915dd23945049186126bd06a4cc941bd3ab44c3933079314ce17a223217993d273f78ecbc5b2a7e51c29d303de1ca5a4bdfc887f301d32072b612f1c410544044d7880b9b9480e8ea490f3f9ec19022454e905a82d654b98752301638973adf31dfd84dbecdf4d297251410b008fa29264623a63c420d692372396525ba842a057ae042ba8e121d6c38234f50948d2b0010b108fcc3532b0be23d99b06d6f1f1426c6da85f37574fb163be866636e1287af5be58dfaae0e025827925957af73e35a84450014b11a9aefa3b260728895b2b1200f6e35ba8042118d48fc41631014f7a36875a3b18f14ae6bfe1194fa6ed8258a4621f180f15f6a980cf71cbea1ed9213c660a82ead6d5755cfd9ade79b0e50783bcb23c0273727d5bdbd6ba6650ce35b4ff67d983cde7356aa77e81e4b5e154048480aebf1a45684a03ef32c0eac4c8811dc92efbcd757923ec5896f0f3aa9a7d2c8a20abf5258e5ae40b7f8a81b965c11a2ef8cccfa8ec6c74d1f4a0f4c1d295d735e395445934d6c1ae15f7a88c8b09bb11076d62ddf829ad2750964e851b8e6fc3a702710c67ebe44c9156f4de1a9b8b55b7cb0ba0df28dd1bcf19e2eb0074b7062ab50e8603bd7f5bc22f9a6e4f3ffff9f593663db9c7ecb58ac2082c37c0f918cd1d42e6aa92dd259be02acd65d9b103c64b45e33ee8a7f85918e66b2e6763410f9acb2bfe5c04bfafd165b382ed6a36688077a2139385b6658bd17e5c181437d5b4db8b26e198c16968dc86938c46f093a23fb257ab1f06914fb105036081a98a85aef1a77058e6d09fc4be4d6932b80b1a10c45c5ad6139b536e6a103087f3120dc438968a630843a97a2b0c67690348b786d7e375d71154ec862f0afeaeb3813348146c454629d044e05aad5ec6db9930eeb06b7f8b372f3c73fa1a92ab754043da9e262e0c23d4c16e434c24d3a991ea2c7a02496f9a9b8855bc8cd41326b40416c3e78de02773f68d4370fbe1f120e148d04edd0174031491a858a5268494636a45f3b73a30ebee03e726cc7e0793262b4e38ded0bef93858104872b9bb284b59b660189c00023478be707a7548ec943111cca2f7782d8b8068528a4961b698b15ebb9adb411b0c81cada23a8af130e2480e3cc142e338c53486563a138ece1f38702efc5b479dd75f13fbb1587033a15f19ffbba0e8ccd8d1929f656856d2ee6b0ffa16d188a2b467a5b337307459214ba4298a1970a4ae5d2d827fbbf5269a1cf3a56b670130b72babe7995ef729aa7f6ccf947c5ed2fa8881b894207531715324bf2f9220f58c79cabd9dca6d30a7a7329e3717e379b7a0a1e2dbdcb993a202cb1d002bce920f0b14a339cdc267186a6d984944c342df888c81aa2ad780fcc38d7367fa4757c5ca92a6239ef707d69730a4d5b324477ab1eada0500f02f9896df1d9c0191c96634e994d1d93be6f50c4688f839903a9d4baf2529aff0484406ac7cfd060e7e186df864255d46faf7ea0a02d07996de02d541031384dd02271bb44cfe1fd1e5293e58dbd2ba4d031855eef457f96a69d7fce1d826d00abd881bb7d7bd2501985c59e2819283c3e5e70c3d20f38c71c3c8f5571ef59c1ec88fc5656f4965b99d3ea77dfc5623e11d217c3c1a16280460e91aee3d7ade3199e0dc2ec23b753823b3704563703b5b715285e165335bf318b3d1e0e1ce6c7bf2f92ca9c61bc0bbfed172f2cc9c8fceef2129a214a177aeb81a79dc754588f9ee43bcd0489a76fa8ddfb1585ce60a99908165e4e6ed084e455420ad31b2b2cde3888c9682b6cc997700a69379810efdb420c733c83376974d75621114991bbbd53515b1acf9e14718ccab1d9b9a6d2de0fb35a01c13524649ddc9b33e4f48bf26168a75f7220eee4090d25608cd201e7703fb2d95fa131da35ef33bb52b12f95977c42361ce5f05b6a7dff05df36fd8af3361844471be78402bf4b547ff9d827647a34528820328bb8504c59c4770e8a41dc546e7bbc053d8dbc9d6c68ccd6320329111aa0c04d00e95a74fb831b8e8dc1e38fc84245a993ce78c03edc2a82a73214dd6da850d8ba8876f69743e4686c09680460a7e8a30dd83e5e84b908d11d04257a3fd30fcee39d5c6913284c2294c651ea34997a143dec28486a2714a0e95f0cb9f95ab2973bdf3c7af38f176b8ce2168696280ea9b15a24080f60218e40a4ac8c826993d7180e20989d19624dd157c612246a34ad765c2e641ebf04259807c736cea52feadb631de3460bebdd03aa97861b18a0849e8baa1dcd7979c798b55ed236b6037dce9bcf9bbe683b2ed577a41fd3cfd15298d00134a742ff3b04ce4a682cd786b9f13a76e66d79ea5b992fe73d2a12b2f6fc5fd089abefe60d9a102548404829495367989cec749dccb75fa4afd54f41d6b47188824f1e2f62a8f0234b28d34969e5b5e5f4316148cc4812c32304f4b068ae015d6c81f9a08eb27069ec1db007b47daf3f4686e82f08cb12217166de651d8f06cb574f590cfbee3cb83dce0877f504edbeedc7914195a0f96d6e073dee90a2d730905331be9a6f58475c743af8b3e0a8fdb7da547351927897c795af5793b7b2acb9c7f844ebfb0e69e69061640e66546b123b7234656c4a7b3e8003b92e5f745616dd366e4d6a56cf67a39c05d98882fe7000205094cd86d68a082c68a7814e0cdaf3a9cf83ae66c71101d70d7573b55f7a65cdc86dcbc3d4975974e89eb17a20f9e61684d289abc72802ce795870443ab5ec73ae7c1856f1813aeff9f0242fb77c15960849f111d8ae59be47769e95906da04580e5f82fc57f35c28cc726bb1738b7bdf5ddf16c5208441c9e3e428192bf62160575b7c7dd82345ba0f8a5ac8596be699379f14c7066384f6b9eeb2c6b8d46cb9831f21cd8f7b1b646a7d198f14b722f3f07b2afc7246fac21a64f1253400e9a3a5f4a8038c5250f339c66a6a0040783b328726252b47451208000ae04a5fc384a157d47ad91c4a2914cd380514e42cf99f0660c7b03b4803446ca43ae9fba2e0ee073c9e08ae62ee5fd0bc7115b53cdebf41bdbb0ef03b46369c90799307d243a9c58534d099078d8a1fd4951f57d7ee6991dc772252b4ebb9336af4f022ae95ce5dd4c6db46a7b7bedf2ca2a2da1c5e48d06a5b3ed35600c72c33bfb712b08ffac8703c3669cf9806faaf8a00a94489b104580aca5d6824ab8cc048351410184a4c4665951b9d14789e54e1326fc3e935e96b75a5718f534bda060d543013dc82432e27baa8759f3667f9cddf4bc6260cd3ef93a48c1903dfa1fabc9c8b27708cd1a0191a8ea42f33f58153a770edac0d665f214768bfbe8f065a4c57fcd49da42535eea85405033f32752564be8156973ae58e43308d05c0e6a0455e5b91b47c0754b111baa72ce493f84e3ca38f421c894de8a43bc648a6554b12838d5fadbde721162432788b7e014dd065fd895af9a93a2052fb810091169405940071d1155a4ccc2c2470ca98deab1030f5055c53c2c4354484cc8e61d6bb48732d10a19962b967b959013934b8d0b8a2ce155ee3833ecb52279607a32fa36049500b88a6e41e22d4addf119b457a75978dfef9f2fe23fbb484fcf91a606f7733024fdef001c35a59a4f422336bd0cf3cfb73f91c58a9c06fa0376d25b5063e56b079717f765174e670ce484217f66b9735b580314a95629262fa26b97113a17152c2754a1da7038f8a71fc047ed0b3bd97005a707c0be8b7d071e7ffecc7e32d9b79cf2cd1079ac3bfd9b6ce50d5a0fad72f0da1f86c710ca61bc917b3acbd53dea7948e0ef009a4b0f2cbfaffc93c0203b134bb81439d104b1bc9536d3a29dcf4a0b257d8695d24ee05131921cbd52e792bd11b516e081e1add9dad68c4af9f84d0c586b8902a869c5de89967de3b87a47facbd9f9c7b1f0eb744dafbcf27d42d1655499ebe87f34401295a637f369df80d84d2a06134d8830c73ef766b76c348eb8d33cd7edac3fccd380f005493b6b43338a01720f0c041606a91b2969ea8229d16e8c87058dc5574f387218e64cb81a9ab758735390962c36320f6d2357babb29540b4332c7b53ec99a35d34b6f7472ed683a9a042ac64fbc15956b21da4b5b0241e1dc32ed198c560374626618b5cc35e779b95a37a823414dfed814466cba0023e737b35dbc0d818fa3d768f3aa082303c1548c83faad482291dc18344a7434811aaf435d4556c771b6c4694a79c126d9a1248532a1104cec3a632dd95c221303b42fcd8edabbc0f2121246c64f2329c80e222762c67ab10a426ebbe7f6e125b19d8022d046656adc6ea970c3118930148ea43936874a4bb6a51c9aa6764a4b8e87c45481574fd13662cb4323d5a141f977dffa299ef70de8c30377154c91b9f878802855e39ce4367bb104cd1089e271d6617a86e02bb19bbf6479206b7bee5cdccd847b1a9bbb4cfd2371a11eb7d17c1222823dd1821c5e55295a2d6c1afc5602d336e40acb35cf21f1deddc4ee03ffe2338fbb47922c1ddbf8d275109a6350843392962278cf0517a7656dc469bd3cc4f55f1535f5a672713b6de4c34badb7cbd557d263fde1fa81176f97a3183a04a090260f3591e85f2b074b76432fdf95c160f82749c328859316e2c900cea4b9c96c69335458f80c3ac0ccf174ac2ad36f83169ae299869b3c1a77bfa34b528b21ab57ed8101a1d7a3e9c8e25a928cbea491e90d874e28550494d61d03ed6a0833b6a70d2d2832c6474ad9fb98428d9d2f92f4d96c5cc8f69de0dd2f78918ffa44dda4f247801cf89f06085d843e4b8c8bc9632ed0210092943229a2aa185b4c126a6a45c1d631371a8fc2a323ad5718389e37b097e8d2012b7cd9daa5f97d32b992cba0bb7821130ff04eb9bfbb5445a6c7ebdaa0e08e6cab8ea9e3a5561166d613ebba72093b358d1421537d128437de7d071565c79d2dc1b9b4b8d340d3cd92973355cadf0d51d693d230396f53caba2a1654347219d4801659aeb93ab62c63daccb300c6413a1ad9d0d5273d99aa004885e6b7a17e9cec95d396f30126fdccf00fd4dc4d7c8f94456c527d9e4cb2a07db0f2499c3807ef262975137408153d0e57c291c2c43165f8bdaa368c45a75680969fc6e3b245ca57cc4c8a880da528ed42d6fbc1c90073f680198b5f44a3514d6f009a861c0843ccd970d16dcf4a4be0f2d2b41b2af8d6aa743825187fc454b321c2b807837412d679515a371d22109eabfdf92270f986104d152826359f16ab4e6bfb466df976492a26923bb3729e4e0964dbeb711e03caef160cd8a1614aa285a878837ffdf389de7acb9a488ccfde9d19eddd451d35e8b7b64e253917022a436b0a9fa00af5dc01556cfba9eb3d704c3c8c3b03d7c8750ad60621c1de35dda21a681c645455c6e88c84af91050f65860c4581a34e75ccf33a0a87e4ca646687b55b89111049b91375cc9155ef94205e976e07f5559bea3198f03fed29d49d434bbd324b4ec896b1990a311117c41ec4844c961b0603c066a65d24b3a0c9b3402dc6d499c8f6846c1e49a1b96ee674ec21b48280c1d859005802ae29215c17369ed5973866d3a5220b79dad3dfd1401391864c0224e19c82b3fcfcdc6f85eb27c1e518024e90b50b251100cf542fcbd728674914c93163ac9ea458fc3fb0e98e6a61182e0369e00006c1463c45a620d9a371a018a1e85bece496851481550ecab3a21974a930fc37b26538eba58975634d51acef2ceb26a8c0245386c4087db8be082fd32572852e5470879536d44aefd4754fbff26175c228e951150960002a96ad5ec3478ec6bf13470550e87cb7d1b5f787926a11805baf2fb24b04487678463f8d9f2ac2022614f659e8d60188d346b2c52bef2ea4584427e8f7e434f8d4d1d689ca04a841840c79b0adb0440f7f50c8589d1887b04a93c1f3ac7cc3b96b3e2ba710b5792340a73218b191ca824c54b0c43841b85a054c1380c97a5b38688b9cf77aca8345718f43c7e3567ef72f421bf77a1d102b73ebc2b2a13de71aab382cf1b81887bda7c8b1de5b927f9ec99d29ee0424df41f0174af53da22e83d68206d870239dc0e209a447c04d4debb794b7e936f905bd4abd322fca18aaef4605a95d1ed29965de8c41facdb400e0f582db1062cab69fdfd1dc23b089922b8ffe18eea1b3f9741637c682e0b54edf3226edb237620b8cdb9af955fb445541637959a54cd46df7493d2fc58486c4ff2a3b529fc05dc9162e2fd91e789989fbce6bfbe9cfb8658761d651f01c691070ae59cc3e6df447c6eed8e0d7cb2051b08917ed4b0087aaa501aff22eecb492d1a5979317b81a6ad26105710e518cfd2d36ee5ceeb9caeadb73b3a6db6986d386f3986035458d21db727dae441012db9530022f10bcf1ffea4f184ed802fcff538d62a18e3fdacddecf71b8b3b27620be3930af1bd9c02cfe481163029d8c19453110c0c534e60fa73638728767907def17361ea521a978294f674cd21fdd4e14fc2f2a8c2ff51ea76a9bd88ecf7064030c12badfcab339211ab3ad641d92b7c75b4ffb3ece6f1215d7a3f0579526cd6f5f0a7ded51510a5ae68db9191261d040df1d2d9094383a765a7db4575eea558b1d7cac0af9e1fda0e372873a4cd32448978bc717549680c19fadaf2727531f1d886128fef12eb6421ae0b15fef4a9420439470b9abefe6c5f5a56643191c9b9e619a6fe741591d3b89c971647497eadc8f9a4558cd00103cbc7a951c9c2e8bc270b4ca4143806f2c3785a81ef76453dea181bcd1739b55d37baa2015e54635288fd68e30ba06356c8dedd4c7265040910135cca931865cc92a6ac538625a7c55133bec900c341ff6f576c87ab773ce865031c2db16725e9e4dc163a7a16b8e0c5a56c343815645528cb28e4cca78a90b2440927f2067bd5883c96b1c7f5dae0ee6de97f69c5985755fea4b1488d09f7d86f250e43bad18930dc95cdb206592b16ab9161c1f0329b0b47be95d4cfd809ae2c147e8041a83f912d5781fac244bb9cdfaf2b80c0963404d3972183d9ce9ab2a5446aaa0414ee8a9f699e7a27068763386a5ae54ba356384be7b77c2842e7f2c184610f6a6f60a6a3b31221044dc4783ccf5ad6cfc2d815c91ba0daed5dc80b920adfe17f8382f70fdaee2167fa1aa4c68d2bc3b57a4da0145b1374895fe6a57a6e1275f9ef824841a60dd5f800b431851cbabf82e369fd9696592429519fef4f9ca8b00d1e748b6b8f17002898b518a3c2a386dfb9dfd73c982fec102d01bb5c1a72a15c0a730cbcbe2ba26dbb6dd31df36d7ddffdb3e692e62a00f0792826134d32d56f3fe490e19fe0b230dc0ec07cf8d6f0a6be1bd65eb648f597742bb771ccc8286542cf0ff4a6d967703bda859ff0b053d7802acabd1181eabdd4b15e6cab79b40a22efd94f9cfb093119fcfb44e170db17cc35f7f8143c7fb6df27bcb4d4ff7905f66c7c6f5b6fdfb43d9fc8d505ad1d7197c33f822dc6be841c16e2acaf4f42f271a819c8363cad44025185c9359d8c8cdbeee40ff5b03f3f8ace12409d0cf2aef2164a7df8e67286185e449fce93139fa65e55b5c6f4875e41f8be59d19eaddccbf33432a702f19f9941f98e155204a857451f43aa9ce61830328960bed57536155366ecb079cfff58db1549daed679f6a9be9f0b06ef5270733db983782365dd6687225fb5583db9a59d2d59e69b98cec2e0fb66c86cad5f69f8ef2a99710801d65117833bb5b675beb32d44f2ccf0c4f07b3977bdd7d5e15708fc6e8da3128cf18469b28f45428173bebb88081fa7226bfe7150799e2290670112c329b596cfe6edcc1e474361afd9166f81ef900afb8b26c98b1405cc5eaafeb7716668f7786f8e00665a128e0f158528e99465ff7dbd844432b0cdb5f8a19a8eae786f2fd73ab238756d8bd8ddd5b6586362e77f19f489212640708c64314767ed0251f5178fcc7e7f8b34b5e742770970e23246b2f5f0a1a6f58c4cf2392e7af63a18fc20454470e0bd8e252ad03992ec2bbee7c1c5771c9b72a10a4e4fcb0611ff706a86db38ac99c12c1c198fada2ae958a70077a6106a01b38c781decfb72de18d82a2be11b3dd5135aa80e377600156de90d85c3326eef75cb81ddcca244148221759087a2d1827e90c9d016eae0854c99f85f2b79ca5ffb9015fd12430bcebb23f09a65b22bdbbccda5cfb75e6409039816c384b149fbf794b26e254bb42279733013e384bc31e10f1a25e700a31f2fb2e8494f52d3c07e3f6d673675d4a7f45ff0f41214ea8bba603c48a1b7f8cf4297e9d20f0121498b249ea4ad98667a1cb23b64824aeac6f490a900aa3ae755017d1c767e0973f4e203201984377f887b9ff6830f5f904ca213031c6d8286d52b800819348800165de4f3dbf3bd9f135075249e0339d003870eb9f7e803b71cacdbd62881bd5d4d4fd8d457acca93e321814a4801fbcf98989b6552b8a4093f071b66517ca821ae98a3f419165955f6da2d6c116803e4d9b7ff308a82d3fd9bac374247329830748335214fdc9e83d41e667356ceb2c681e6131ffa33883e8c892db67d6cfa5560999f601787f4d63cd34f469518b41a7f356d20ea9cad56f660ff9d04759c29c64b4a1578f4a4c09db22d2e0605f9e48225f229cd51777cee39b74a243cabbd51634b1116a9a8c9e70035776bcbfcb87910ae9bb8be66d028e708f361066fdb0d40fabbee746374bcbb03298c3a233cca248a52a71132de97a68f33d002b03e0050ff8311000f5151c13e7b8f1296c78e0deb91c9187e91bddee81c48589191df22be1d85afb58166fae2ae39550a6e6d2726421ca2d84128d32dc9bcc26c6c90fe6baedc297fe2770df5b67745bb567005d80e8f6d91b0029a94b58b1b81d7d4b8d7622e50c320b8a1c31acce5320da0eba0f8d38cce0143aff9d009a009850be8aa33b6055423f16ae246cdf1401e3c8c2065a3dcb9543578b6601542c94a26b4f3d0a10ed0a593b4dfb70008e0cbaefa20880a70ae69776fb37f08b4784d9d3ff7b1b313b1e07fbba4bf0e923e45c90437cb5f80c3bebffecdd8d50543e59a3329661bbe3e30a20eb4214925747d174f6149fc285946ffe90bbcf1a38e815a5ffc7d7d1a2050987a759d0e8120c9224f524d85d60f96629b3ead670afa0ba7a4772e61674c5676d13333eefdd2980b7bda6a9b2622b9c758648e146948b7b8472dc87165d4efb00fe6c46dac7fd1c5ab459ccac805f0ab386ddcfb5f2be1d87ce2c2ab7203d75508452962a259e2326f9ee5f872afd55ba59bb23e6706060d795bf7bd990f812944c8e9a4f27c44809b70724f8797fc3d2caaa9b1b5d5743532da05d091d4f68c6d87cb82d4a244bcf303761116404304db6b56629ad1d48d8b27bf83a64763c642f5cf2b856f4011263ca124d5c712b87cb5dec319e1dc28ae13d58e7d26b50764e1b1789bbad28dcf0964cd9b3d178678f5b30deea991829bf14b7ed0a0609f01b9a5789bdbb0c707bb8817765224d1ae71faf8098e5954c41efbc1f245c056f7c1ae8b09f0904940cdfe32f93d5f7b076fde73b96eb7f793105d8b31586eb69499050650fb628d201aedeec5038ac5d4799e01d8296803ead834527c573a53a7f09dd1eee175a389e3ad37ce53ca1b8de927c4b97dfcd2adfda3df72f8d6c2cd26296e9729379cea3a0465274e410aa307cf10ecf19476d56ee5afc0526ee56eb1a3d7914787518d269f99b47a1ccae14911103c3ebf49dfb5a1413844da3e32f084297b6b548d08aad8833bdaed04521a07afed27f3b9e3bb18cbb739d267dcbc60b3e199b7e4002aff89558437d4a8e349a8a9990a108391e434239ee244963d57d6bff41de2c65af4f90bb4e78a3dec0083c4895e778cfcf0c14df1933f9bfd6439fe9aed8d3cb174200c4866c688af4f80e0e24d0f365c7bf0b765803556379638ced02cf1adf791c21eeeab5aa14c1a6c1cd01fc0c3804b6fd40567bf7c2634ffbdb7d12df405bfe65afc6c99f988ccf47cd73033b3e15800429c5cbc62f10bce545e7bc0654dab56341da9e30ff616dcfc96fa8da05911bff39bb6d8a202c588b4284be1657223ceb4d9bfdf2fef04cdd68e8a9c1783fe48e6914810389faf41a5b07df6ecd231e6fbc424638635f088e0fa825a15bdc10d72443bf13e44a12f447293d12d9a6f34d7207dfee92fec28a3029ceb21ea41dcc370115f327c394311bf366b63153dbe3275e052e4de59daa53694bab934d19a3f22807a6eb2c2de81e9f12106e5806d6d74cb0f8f38e2edfeb1b8cb88fd52952f0e9a19b34dac3ca3e6b0b7264f8f44d1ac214c5d5d09a7e86dc8e44b04ba84721dd6021569ea63598da6e338267bc1c8352e84a8ea599bf267459afd53363272be7330b49880a37183962dc9eab5378bedb057265074ef36a9c3f9e2bb6024fadb526822bd047a6a46aa45813046739505ee446a17f2e6d0c26ffdb45d80102fc2409ef5a81f7dc0344f0e0cf38c16eef3fad6571b74fa78bcf153d7b931d0b1d032ce13f0c4d38ccf7161bc67b8f3ed58cb8f761fd1a8ccb52b6e761ae420f3038f91dc19bbd2b91e39535d4add580ed258760aa2899a190119672e47b947ec5708441382d63e0b119c9febfa3b436eadc455ae024e7765fcee963650536dd95e08e33ab46cd01ae4c38efc4ea56042bcaa064308f582c4098cf95a764e99f6b0b08ad5686df758ccadeb88efac1488e2ed0f3a56ed6277e6d7a838ac56e421ec4a764cae27426b6f5a251214eb9d42d52013b21a0ee2a2affea57b8080c4f9f5357dcd6f88ca589c48bce6083412752298ff92150614d0a1cad8552fedd83b8ba74930c0ea9f908923d8e1177b150d8814126a7511349493d8ac166483efe7f3edd6fba698186dbd71a75999bf00d02ac0da08f5a87d6543a897bbbec809fe366cd6c5c348ce0bf0f084dd1d127361ed38d19f0aaf52a99a462d5ecb1f81a98969f37d31096f77f508ef51d89b3661027d4ce5b871d00baf647d8946cd3eb26d62cc34bb5c6ca79e42c813dfc30ce15e73237e7fa027d6f9a95e3bd517187f39af01d40cdbd3777c8c54ecee2603e0287f967be9eb5e18ec3bd9c04e8cc6fc29b5f3ebaacc9c657d7c99e00c28f7bf45e05a22f0507082c8445354b90a1d94e9e47980d1802c7aa178d2f10467bcaa38ee715505205b86f6e911901399dacb264bc1f8076a7c9a0d3a451c7031c04842a6c629233ffa508ab64ddabbbf8938ab1fac60297a68caa70ca16ff870fde4a3eb4f882d8d8dfcd9699a257a045419da89726a36ca0063b990bfb8121cfef073c46b9e51e22d28f4c5dc358db2e7bc47fe1bba962945e4defe3ab7038825f7b523d29df7371971a58a6511780e59c9fa069cb9eacac8b291607c9b8a8823b200c3a7c14b15ed36558a5e21879616e31f8e2519316a7f484f9b1fdcc20381ad5570cb6facd9d03893b7cbe7eacb3aca3a3d73887b0834c6a4b9184e5bc65d0125a3de04f0a47064db3146dac639912918cd5e108df4f873244cae757fc7bd90844d390550a368112a91b7f6d308f916b320c7de412068b6e0844f90b449fcbd598422c36f31417b43eedbb12b4971008652f96ce1387b99f6e7e50b20273d0c0bae2b31595458256281000d60db05e10c604e9253ec4ee290e83222ed7104ebe28f1fd836a44a81b8d019382ef8fef1421bb597739b89c2b7e716d055e3b7ef90967c02876dba5132003d2cac555edbe9b147862cbd19412b409ae6c4626e8b866ce7c3b3547db00b7d743787a6081116163518061a27a40331a7e5443e4934173f2bb7b516eb2d04920ee474a175df302df04a34a85cf4c76da3cbb3a190ba96b017e240e7055035a59deae3110fdd8f80a1d97743eb8d829a5d44c499bea6404e50cf48337e43e046aa1f9ab40280157d5304e42932dbb2ae69d73c10e374bac19ef1d073d8eba1d9f044916532aadc1710811e8660513e5f553f34d8fe6746ec69bf46ddd0a4cc246a2612e7acef569aa60fc93a9350a10e0816d24eaca58846c8e728a379c764a719c3abe0a6f98dad1001165d3c2cb8e4d9f38da066f028dd31ac0a9a06d414205880c3b2ec8b28ba238e0904dbb5b0849a5149803832cc42e59e9cb3fa146107f9f014bcc0fdbf423cd4029dcd1a803e01dedbe191be492c5c6f565b36a5a2a30c394bf94683f26ff8b8c603c1ff0214830048e4f858a21ff680425015f2c10556c5dde595ad9ead571996a8b2c7ca5ab3167b106e4f38a550640993d59c9755c12b93cc3e0895a84130d61c8cf1d3581c362cd526327371c39c24a992c087be0a08a7b9cb405cfa047833fc1445f047226da8f187644bff8526987031c13b1462f7f8e370bcda577f105fcd38f939852bec4b6fa7c1e8f27046c6cc597a8588939bee48eea121bb153085a68b2fc3e8c67a21e9b72b48eecd8fdf68f3d2336266e76bccab7fa8bfb0b5fc22ce9525fb462745e48dbf2f8f2f106da08560c46dfe55ab2e6984637a636f9c3f9c0a53039431692d07d828b6d58f20bd0209b19d04991b45cded773f457493c8e6885a88f84aadd6a2d60a5cd0d70d669f7efc7eb3a014be90413ebf268781cdffc0d7859a64358c283255e2a04251d71c69a17fc96fe4aabaa05d418d01999a3824524afb356af3ce30c054b589b745f63f6eb8c2ac823b6364965179b0248cfb16536e5c514e2332b0b429efd946e349c832fd9ae94aa3185038ab2828969a609c69bc01d659b80d3a398a87a74f172a74ace2947b208a19f6e6eb5ce1e367b8d121fceedf90c43903ef7d038cccdfc03edeec2b6e877b29f3d48dccd5940e12dca458ae0042abaecd3129761300d5bf66d73502b55e408e7d0a83f18801aff075f5173c37da9f4e30c30d193a04af110daef91e0a86696c0848eeabbed8f98c6766a44478064d02b1bfad24ba8fae36d4ae962d4109364a8451d7f81d0c12ab2dc37ecbbdcfe18ddc5d5b3832ca22f4bc013551dab2f23c5e095202de9041a7dee2b5885b3e43153ca800a6a08fb102ce81aa171c5a384269060a844f7a5162e0e9e4dc6e5344f2d5cee83e4919874bdc12c413a30ba66e96e67caec6aa516540f4528973b73e00cf050ffccc0fab687ed3c26deda6084bd4b5b0b9178184d53668d00c15cf65ab78a2f7349a5b81a79f7dc8259acc11e7a850353d5b0ff0f65bb7b33327cd5d60065da6a8b5eb7e907de2a085a67132087c44d713d5f536c16cb7829ebe60a174ee0ecf78349197efd101fcf4162d548ee3133b171fdfa98072e87b3ff6ffebf3c8dc02167b220c97c1766c4fa5ba29b3e2984c3ea8302105e60062883dc786d37af812736220c91db67f903b32d64674631af231a543bd0abc10e96b43d0eebcaff55e802faeaf56379e7c55716e07b67b171edd434b1fb107bf0ba71512d76f9e570e56a0d781538e3888c30e89346238440d35a839e867177698241dcab668ee78c5dd1599b616512e66c615d9bfdee09386a3c468ceeb918ad9dccbf333ec79cebcf1f8175e216ee10a0a62f611212593e5db54d160c72a67ee47631c6ddccd2f0694fbfc9163f45f4a2fde973494472e1f71058dce769233bd8671850748a15feaf07c6eefa872621d2c4101dba4a783a9a49f74472dc17cb2f98b0d66c7605ae2b600fce3cb3ee44b3f04fd8d151261a2f2d53e66e3075cfd3ccd5fbfcbcfa35a83fbdad35608de1e4758fd649ec569ff5a72236320602cef494a5334df1bf99082a054d993a781867bae0307ac0c24832c3b3df0a815ad6a19f2d572f03cd3803f96903ac7c767a0ca0953510f1b5041e0d03d5aa0c542d83ba053e6547af571c61399f5dcde9ee8818a6d7a61cce431b0864705b58a2f179ede66c345b6b5fe495f1528c7ff42b48850617b5e25056391f1f00c713a58db4c1c9ffe6fc747709bdf4eedc71fad1e995c3f2f1b7606fa25082ba4c8e229be17e9f8ae83c2c4b599c436bb13f3a2ad7372f5349b111b9db8b623f3b88f452df48824b400afb531b979be379581522fe52f86168ada22b7a37da0342bdb755cff21b50f35522629f0168675ed0b772c47d75747b7107b0f4b6dbdc34e72394a76556ad822799a6497435db031c5d313ca1c9bbce6c8177273e6dffd3c1b203d2995e01e38f7fbcf368f2d37e95de21ed9ecedda050b00caf0b43339057f1f635b1f66a673ba9c81a0403f37d4fc0cedf65df1f8fdf9a828be5ce43b5db31518f6fb0c533eae01d1c1e2862d9b99d85f9a89ac60eb3fe00b1c08c092a8124d30c90f33b468b203931e8ffbf6b3a63ce29a6466662ee87ab8baa1ab1fdc0428047ee3cacb8b6fa636d81363b6fdadb23ed160a1041c0d6ab818b2aa91441576c99feaa7648f15142c0a8f5bb155a0807172c06ac89b2934d46838868d50ee9f05640e21169f9c10988faac7a4b00ae6b58b6679a78d45cdd0850184a66f8e0413e3b171a6bd5c15395310ab702be0c9a77894667ad6746fe6fb3a119e1a3c2a6548b17abb961cc97ea355845949a87909229d6125d0cb3255bbee43998b41708799558e9a4e85f4aaabed7d41012663b053f4e7f649231c834f2b7e0a6a478e0e13e35b2dbaf626eafecba6834a7b14e1356bcfb9419a615071d36925168630abea2493e05a32c2cf788248ea5f950cae87a31ea62ce9355ee7c62142d65f97f334a63d4f9f1032c6af4a2c1362ef251bf11c7d616dbc0925d3552d5e5a8014909fed5ce55a1d0501b00a3a88efc218229ef2424f7169ec322ae3b692e4294bd04d8fdb23bef6a36106c8a9f425124d07bf68a2ee5fa1b4e926f54613ec06db27dda9c0bb4e54b48cb2d30347488a176aabfa498000f68634f33889ee5f8c94cbd0e74dba7911aacdae32fbb166df46585bca38b030b242acbefc17a740b54d47b0e5c41263e09f2a7fa156f8a1151a8052c0caa8fe61942e45790bd7e2bc1895a235b5545e7f7994f1e938eb422379d0e28e58d24ab60324ad376f06e9cad03f5d1ec4e132760b38537ee2e76d1f46f6ebb942cede85716a07a6996fe0bf5515f08b266901716276da039e80598c52502c295c2a37b08541f8a47f38247680be20d82f1454305e590067f8d3779df2504e9d947c03119ba84ff0505cc41e7e507909bb5c9e0dfb81043858dc83bb2e3217707e6625b8dc4215547560e0cc6f68dc66eafa3c218e8c93e90f4aa169ee1568204f208fdd87c42f21eca5fad3925879029a83a6db80fc2ab067a7ea33f8bf60699b204ea284fea4e6def08375842f08543ee1026e481ce890eed94df83da584404ba75515018865461c659e8ff76cabe717a56f7620f16bacdd93f38097f85ef980911e411992c05167b3509a667ccf9b3ca9f49db2cf971afacbee7a8e7f0698ce14d6b39a6d86cf0267d5bcce981cbd805db8841fb6c3b2a465e3b06813a7b0b7080344b6f9df2141390422584867d276ce86c0f898d6beebc7eec3ccc40142be45c4718f506a25469b1186e3b457fbe20b57f948b9d70abb56305f04f0a41a27171272be376dba7844578c1b9be6e82c7bc03adf342dc1caefd7b82de0a3057c708001ca182d861d095311ea1ec7e7387cae6147a38cf78c6b4c91dea1c75ba55172282f99cc72c4c153fa879de0584003e4aa34f694f3e4583dcb13d2f563f8fe91155863edbab24f398fce3f36fb9b70ee29df78153e170c290ee9de1cda4b3e56bec830fd5e07a77f7d7e605e9362555eb7d30544f71d48da498bd7d556057966446d58310f3d180b95c2ad7b36243760b2a26f048622112f3c74ff46aeac8eff399f0700dd910799a50f8cf1bbc5fb8f835e2a31fc40c11e8e7ff455904132255b8cae0485cc8bc874a1a6ed2f548ce86409ad2ed6474cc8e8e04d3ff3517a8529064aa7129fb424bc92954cb3b20c71a66f5b142b89a53ee8dc5e1815e66e9ebd65dc0242f47384a11a5edd5652d56c83a70daed9159db8bec93f02736ede4cfae1acf1f49968fd7876ef0583fab7ffca83012b74ba7b4eea2f00ac8bbb3891252be9b238fd667ffc6e3e4d0adb003c93dc69edbc08098406c3825b9f4ca95c5164c9c8c33726f31f2f909de934983c1a122b0a22a9d2d8a1707a1a1e597ccda1342ce4d7b3b764eb42ca9a3b02c882a3dbf450a02ad073992fdcdba32bddbf64a214059219ef2d94402a3f8fda32bc5daafa0298a5a4a831c9a17511f01c940724552f02384b7d590666fa0f4055ebfc357a759c540b44de58984a231877b03e7895e00fadaa30d721f48301d9ed32cf7563a842fdd84b5ab277b8991b4a8379599d3d8a20e42c51d85b4f744922652b8175b32e6fed48b4958913437a8331d884604d45ca8836404b7c3730f606a8e52218b13f0aff50df912237e1b21fa7ff197c8283bedc531ef0faade805472fc99e7943dfd6b7c49a1f060f9d4042f05fe1913014a7c7123b720a85d0e2df8af9b0c5784b9dfb679aa2e406c24044f646527ca1930d8f091f9002360283fe539a8e4708a316ad0459bd5f2b56b83e31ea2332cb308a5c74022324310db406dc02e803a5d301103482acffa2a6e752bd6a05a732d58be8d29183d5f9401829e8e83e4444f451c49e922b708e2bcca2abb29672eec8b76f85dea6323f95a60f7915c732ad0a70227b41133767ea6392b91b56c9d06a5357fe5678fca1cf5c41f06d2fc0d202919d8aac1458d277b6b4ba347693b7789f3f81bd62a6c5e12c216e3a6130ab9a9513c189d56a13bae5395734e9541edf32d7a3211cf7190d528e3eeb19f10bde54e8205b9f8efb9285eefe5ffec1522b09575cc0674323fd98ff4af290e49ebfa08406b665d6b10181b78bc72bdbd66b6817517f4a8c9e7cea79852bef301ce96cfad131b7ccd69f6f2cc633f87dcfd5327b12f2fe0c0029148b0cd62fa081e05de8614271f7de78ee4fff948b01c86f5646af15d691b8828fd3c2ed47b9f127522989e899e38b889d9fa3ded932d15ecf1c9d2d8c54ed856a01d3c2db0728aca32aeaf63e900a1cafe35f716fc90f75e599a65b9dffaceeb0206aa0cc241a73ea068032cb12d7d28d4c7da3d05f4e544e8773f446df01fd50ec00d35045b81972a4d85bb65dc9fd068ed017a91ba8922d8ee02b03b86cd1acb86a27c59e6c97d68609351d7537116068ae3ee984677d1ddea27947c44ef74c55d2d94defa47a99a2810379c1bce08b03e01b512bb2b56b633b1d192a6eb5130342ff668567d7d8a97629d2405d67c78e2216adfcc898513b6879a641672e9ecf54d038149f71e04ac294293dae107297b09648fb67355d2b5844a455c2703e3bb91c923d619cd3cbc06231d1ffbe443494f8c2cb9c54a7f385704d7bae0652ae4e79a21534d46c4b9ac32a03bc9e5a12a34999f44a9e408f12cd9545345b62797ab6bdcccd3a86287bc029381616ac8faee8793a29e4fe3a0b5f0ee4b1b9d54e2fc4145cb9c78b443f7a22c3689adb4f9731d13c2df8b2e2d8cbb8efdfae9dadd5ed347c251b3551b8876acdd4525f66596fa30e33e86de04f9fedff154e9fd2f41e2a0296d94ab063d42bd945eba0c12ed61e3365ea574c8a9f0214942b81b39b8866c8f8c9fc723eda643daf4f7892ff6f6327c4be5073eadeb6d6ba1edefe6e7fb8bb9e28bf0141f54db8219161abad124695eef6b32e331380576b4545ba2aa9047b9ccc986d0ce13bc808e0cc57823d3742a5f36bae3350331ff04c851e1b61121918d5541f6c342681230885053cb034fca69713575b2a6d6085865d7fd0c146d016332937ebe6996adbad80f451232a667134e7592240f6f6526ec8399807005c1628f9e7e0b0052fc59c94d32da45ca99e06749b86127e78f0f233e5e57b9a20347dff131b682e4c67c88075c09b5bc5bae30f82026b39b1416ae67ae33bd2ca25a40a0774d449f77afc416f08f118b8ca003cc307ae93ead2749071a3a57cd8f63269da376571783b841152e85886cd53d108e38fd32987ba4e081ecece6af96ab14b77f3f8d232ad5279acdb129b6a33c9f693b3ade9e54e435f4948b5af577bfec153cc3cd06d439ef853b582f5119557f8aa4d484331376a0aed10d054aa1c6e30b0588a5f345339a73e96dce9f09c2da5d791bb20813f5acd496921375cb42ab3f1b790e7d5afbe972840b191e9e60ec39a342198e4c1ec4c271235d1060e1b9f7db0f6d316926e249b761552738eb7e33b14cedcc9822d35322a797848c32ca6c6a9bd8659f32017a9c6edcc44fef817a986bd77cff79c0c2b4f5d3c4a554931409f6bb142d59267fb37846dbe9f643a358f7f876bfcbdcd7c44b0998415753dea341207b8ccb427818d36fad50e09369237b6e53df55f44fc648e2abc8707b696b570a199620fbc281e3793657398744f2d61888796e7ffed48edac73ab9e710464c0f258120f0922df4bbbbcd62f9cd4703a5ce5c3faa2e4253fa776d7ec0cdd448041f3c2ae9d6d63b06fdf73c72dfc537d4e83698c2b994d40a376846631ee9c7791a2e5d4ac604ab38342e9d77f03476c24577188ba656c36e668b7f7c46eaa2236511ae2b18a294fa19d8f515a5bad16dc090e62615ca15a8deb459bc15fed4e1ba9f7feff6d7b99cf9521143f7e72ca2a61bcad5851f271b62fff2393b5662e576b6fe85bcf7bb4cd21d1334bfd0d257edee5cba0f8ed9af47198b3f9931bf0fc9b2c78456af682d3cebae1314e6cc09879541e7bc47cca4fbf1ead130ca74bb4d74d01a4f88795ffc1d7d4dcfe46f77b6edbabb7450526dfef4a1ab8c0e1e2ead50fb67c9777802082173134748dfac309fae6c3fff1d414d8b99c6cebf29f0a968bb02651ce730f347062d02f55727c1c85d389f848bb73f9a4f5bfec1e55165b1fd8941130e6df7d7fce2f1eaa849741dffe1882029e5f909691be7f58f13df20b13d55c3f252b60e48ed7fee9354bc0ce39bb31fb7f695adb72e3fe64696412eb323f2404b18290d39dfe46ddc00aa07af352e019f723a77722280ae021d1af78e4c2f77c489374478f4701849840d0905e669cf011edc2606be36ab3ec44cc1dab88a8afad1fe6e03095cfa21e370e8b1485b226546d3bbcfd6f1afe41470db55a607ac04edb9214a2001cc165707d16db67a1fab30c538026c2ca980dc9e582fc1c25735d4fef3493589052b7467577957b1e3ceb89cead8dfc447fcea6c0b7d153aec8653a8d27972a6098bc4099d6b9c95fb35c59422e20efff76604de17b1500c8c79dc9e624436e6e5fd33118a35771c86c5bdf5f11d4909de9318df3ad7cd44392ca4f27a18c89a587e36596bc61815a53451d56a6538025a9976e15e483e6ae41a61e37e532c47bfc5a7bfbec3d9f57e5f12fc600358472a316bd4ecc64afe58b62bd57d69b90e3a404df80472335199d77c94ddf9e69dbd57ae679fcebc5705f3e680b3d3b0a7f7dce9737ebd9bd568b149c27546950ede172547a633161a7201bd336a0cdf1942424ed19545919ea5abe23d2564886a5afc9496a6e05d11d24ba98e7b48775747c9b430a6d0630bf226e04b507ed1576dfc2e5847949bda9846c6a2b73e5cc24208960c3ff1f824bce0b1ae4320a1ea7622195e6d23ed324e5eb2759fbf40b3429040bf43c4bd17e144c189f29dd9e631916e76441740b178bfe80f12eb42ad5ba55336efe61f44f73404e5a60b02ceb03314e381f3b8cef396c73dbe2f3ab62918ea9e7cfa4622e87f3012e4c3b2fb60c67adbcf48ef3b3dc139779aa9e5d0a7889c78c13e1f0cc9ae531a75e2fc08865c3c1e9f826a3498eaab77d01b3610e83fc535ae416acc5e1d15032bbe58e2b56946ceceb5043121404c6f0bd6f46fcc1e2237a8e4faecef78b575e45c1311bc8ded8688facbbea8cbc67e05435a6e21e540b019b662b0a8b85fcb9a4c9ddba62827850b5858796c25ca293898c3eb9888ecd724e303526f9a1c8fe75d338b881c6d21dfadcebb063e9592f6de4be00fb9a49ccb488c1f7ced2f4db368f256b92677008c13ba612fd1c99bc0e0b13086ef012f45cba33c137bc40c43c60ffc6be8dff46382b8e5bfb4b4990b914a68d920e4e4276624eca106f6c9c283b84f5f3278390bfc5fcc1ff3e28d22c6fc9775ba6b327d1891a4695eb42e1b68a5b43dfe1f62a9be26de60abfa0323ac8e26e918f990f43f89f02ca587ef00caaa4bec567877783541eb3a3c9ceb22d8ca275c0e32857f2c890a38eca6024f44e2ffbaa33834ed22afb72d2cb3f413ee9c9b369f4809dbd9be8fa0a769e65ce20867761368e0a947f24a7188c6845179be563b13ca8e933bb4179a5dadb2df42e6fe11f67bff9c125e22ab4777e418ca7a40033fd9bf32c6ef68cfcf9ca50383274d8f300ae2e4687344a3f66ec2ff54a4f59d447bc96b9e0be248e632380bea687d80fbdd2d6c14bad4deb07625a22c297ee18bdc90017b6a7b7cdeb41f56978e52bb55024768bf9b3b0135841192c69683e3907782c6f4f534467e8dfc318a743ce4b44b4d1173b9289ee579e92918d327594c80ddbb01feee9af7e7c5252ed6e84c868e0951916d847523725daf7977eae34db6458c1628eedb7c8964869de5223029b6ac761158a8b1b15e5411ac3129f28aa4533ceea3431fbd468aa39981b49d8752e957b6988165aba00f184c33216e9370027632074d080f11b94af544da494a196b811b77477eddfb59de66011b8ae82c9dd958c1c33ec19a6fa8c320773ce2cdb1d911d539d6766737baed8648b34408bae19b1d415d588343e6873a640e4a60ff23fbd02aeda62f52b21a827b111f8cf9f3f0d2e0cabd7b92dd5550e8b2876b96129c79e3058171c5bfc80c96f22cf8fb37ffffa23072c603dfa32620478bb0988d369257db9638e94559bc1a2173e66d571310e3d6bcf0bc73945794da3a227cac9029c7a2ca121af82a6b3f19f27506719eb0c063077b69081ed9e886befe23c6f1c36173f060a101030980155971f3968d801063fb70c9678107a8ce469ce15807f13a7dcca837fd54e753172bdc9c260dcb7c68458846d9b62db5378acf4ff915cee8e6a593b55490c58092817823d1a7811c5025de46891a2b1ca66601ac67b3943e1b263acaedebd8d256ffddcc6f09e62f4764cf00162c4e2b79563c875c1da5e838c1a0fe28f1a106fc10efa50aec85e76427e6e71ed987cb6c6097405cea5d7ed7fe8f627ff2ef8664d2f071f1b7f10b1be01fe330186a9c55e191e44108d748513762dcaec9b4eb65e2e874797fefd3cece14c65d1f8c5961445ccc77ba697c1f5b0c34125a514a51439cd0b0cd3fd9835c552c51131b0eb5503950008eeffa217ac6dad9f8ada1671f38f57403023db52fdf975a5a5d80505b43a84a08f82008ed48ce9973ef6ec4d5dd29ec3eaa6a6a137c0b87dde5c1dc66f1cfeeeca2115de002260b20f48232121f75bfbc82a1950f8f8814993282094cc11bb3cd5ff3b72004027ef818109c6e5d1ad6f2c5aa4d4dcee55e5915cf4d6512be1d7093b9879b01249b0e9a40108d10190de52f4833fa6cb096c7f06e60537dbebdc1fa84677d6a7b01939e2778dda2ed67f43bfcebb3a2593b16fd20f2087e2c39cf779544548e366d18054f41c148f954e17e20c152762df888a84a31c951679735477043803c5bb40f883046b7532a8bf1afaf898fbaa70d20613d2ec80c81319a686204d460df92d78279f9ce4afe1d59bfcfaa82265f22ea864dfe40f976adacce3c966c95fdcaf2d29bed5dddfc6c43b683f1a838bef7818eb00b2f58d2d3831490382658877cbf05c7576f3c622d5866555f6b600dbf9e11f03f8efa7594c3867584d00406a6d4266c608806e163ecc8b17d000740e2ebb9befa42d61f89fbbf980700726de2471d3ae135de79cb92da6fa755804c4e9055a0d0b9d27a39d4a3d9e24861e37603bf241ed060780c2300a0d83f2a2495676682f910e60b47390316503a92edad4f27ade959411cf8d67ea97c0e4907836d00f630a88dd91179969ee1fdc375b4d3a26317989c85be0e5aebe61f5a2ae3fdb2613a73d5b017434e28bab5b3b4611e58e8168b80a1bc8868783fc82f3e885c58e3c9be04744efff2dea81d212bba63e16e9ff7d553672e8ff12231d60bbecab8be65724d4fdf01d23c0908a15dab9e29848fa5ccea51d8af7587807df28e10dbc4acb0469284653e244e047596abf0cce6ac24d7042569fc61a960739e17053458c11d5cf6f662475fcaeac4ab113b7ebb0fadd7175e81892a0948cfb7db670f3b45fc56c1e7dd03b56562c5c1a34906d0159c7ab9cbdde6ea1323f3205cc01c57b478cdd564bec7372b36e8411ff965e6de83b122c26546082e8753a27c00e6d63d27b7bd44215ebb2c78c6f49a69364667655f1a9a177acab3d45d3c7bf17209486e373c5526c3fcc3df2e672dad2720e4a407d1375b6dc3de66fd4dc108d36517a0b8237e13d4cf87ed10e41e5b05ee80cbeac39af4e5771c4439a53245b4b6749676b4b5dc61f932c1901fb3feb26bb3f859c85cb013870d023942a4fff45f39106065e7a259bcb80a0e8f1e717b299dcea8cbf0c83a97a89964527ff6e12d2824db7984a2eda31b09b07e29889c66350d779ac27c2ceeb7d2b2c0d38f5293b5a6a9f12520ed9941c5e23820460d405ebd234ef875803d84dbadaea2bf4a54ade418353a5c8bed62743ebe0983cb525371ca0578973737f89535ba12d8eb3c9b21a803e9518fa275a61abeb73eec0f0a7f65a4c50366733ba110ae66f94fbe0fd673dda27cfafb7f607cdb84cfb8c65cea1bd2bf39ce1377a6f3112209295c32921b6277b323225ee8fa81497e6063a64a6da24ecc9e73703b898b3d347c5059b9a930702a65738fcae2ee9cff56a42029430fb35b146487daa1d806c1ae79041df7d9d810ae1a681bb896bb240f2b7d50444e8e53664467d39967ef1919ece055d4b4d1499e398260eb6f06dbb75e6c193a0965eacae9693414ce74c05a20f631c3dbe20683cb2cfc3de0a8e0a78aa89d8a5842dcd7e391543f44a55b54a1f78146e17ee0c40c032dfdbdc7ffd746dce80350cbbca82910e289a9496da6c3c4ee2c8613d9ab4b5a8cc32878e08b7f6b5f312cb08c9a273605ed1cd6efe8666bb18e2ea632e9e44032fd0227ea6a208cdffa6fb87945d13a32f68cac387093bdc337290ea1f194ed2285a6e024f1cfc332d6e3a8637021abf627bc447dc082c0f8878dd15d1721887c27b0cf40602d7c679422a464e785c8e16da5a6f000f0f5f98cfa6ff68874111e9a2a83cee27270430c8a48323831dfdb16e419ec9f0be1b8fa05f553d7e9fcec13648908dc0dfdaeb793d938c0ee7b7ac6745bf78186930d3764ee9c30b3df24902f4a5eefb599d1e0c4eb392d8875f4c047fa0bda3e0ddde8be8ec12b33c7906f5c7d798b1b37b9ee750ffae4231e7eca88d186075fcbb43fd9c3c9a8d70ba582a5eec2c4d93e33e2fe3355a01fd7489e398f35f3812c59f4a784d00f8987da82c4a746c719c6ab40673dfdcfbb38e9b6c77e0f0015c807903aaffddf62bcef26955123a12b41d2a42c23121c7f6793ad50980503a9136670445bd7f39573852c5293ba1f979ed2ae28143a4fc5bd7f23ae46de4bc0ada9d8eb9418f58bca83681968e4490f2a5cffebc529fbe140620c186fe986853075e4271114d8a020f2dd8d21928e9a6c0291bcd5967c7e556f5b811472182274174f80d902610e5631d6d39f03f5ac5f5c7318bd4476f2f4211e225600e2c2ada290901e1eed3a3361737af2eb04a075d6badd0ceb6a963d52b7633d71d1810ec9d805f0b1fed80a65437c071a58991c110542671e5954ea569ee952e1b25d50a65af958b5db7e4bf6005db141e0808cd60483294c75058a6158d83a0c0f72332dcb3b8cb9d64a1ad111958799076b1232338e4208112c08828e81e9f501328b581393000e6306456f6e8c398041780483384c2eb77b4a3a709dbd9c616bf72a41e37aaf26778376481b84cf30ecd3c516057751688624769d71de389c5955a13204f1334e6fb35ca364e661e33f120c8721d03e7f1f53798755e04b43c4b409c08d030544ddcca84f4b44868631783d273d056b88692883e6cd780b84a905bbc6c1ec1dfa90aa17363a6429be288cb565cad16fe726f86468e9835b138621f7c26e62be9e325b7798568f100c7e7ce8615d3be7e2e03cd00f9254530f4b942a8fc683f94a589506bdfe203e909077781ad34e446f7884b87d98fc113e8681e63266fbc5e84b33347cd57f2e8e94982b5036c0cd7dea1b52e4d0c2992542c006d40189f87fc21617142c2997760988cd4d68756cdb50b54ffa6f9100621c8693485d4466928859c605e9f7bd409e4a92f7833517c6aa471900b89602413d141ad40442d922105b5c2aa33d9b6f2be5902550a7fb03556ddbf121dc2258bc065ae855e76b3af901319a5dcef315d1b38531cbd7a42666b6a8e9abaa041da69df04fd3977ebc8a271704d7f634ce8cbd3b76afaa56a75f53a022621c8a9e1d7f06b62152d0de54d39431d790a19b6c0dde500a13990401a68d322b267b8a8cf2a7aada4c81fb3f23441c3d91b8b675c5a41008e5522a07d16c4564f659328569ce9246d17f1274444284f799878ef456ca70ef19519c970a1b61fcd987130be6302e5521c4fb2df79f5c3f1022339be086a09358fb44e41936d5953dc437109b325c8821a27062c8ac3590fe2be6d41d580d8014e11483e652d83954e93eb210205cb9d8ae5cd9f6374d756a3fcd532f1436d9396eced49312505bbafc302f3ebb324be042b0cee2b0bc2ba3c38f5600c2cb3dc9665675f7d6614186d028984808254ce56d5d50c94149aa0d438bc3745f46271d9eb355b9323ff56361c357c3dd16a6888d3efafa6fb364b73d2ae6589ed33fed134ab3450bc3e22e13084e5611935df0e97fe35babbc5d7ed7e5d4f54d9523ffe79e21a7cb38c0003fc2f73f84ee6bdbc4fe22c90ceb52bde9493bd8bd0d048dfa3e2abe194f1a41527ccdcd34769339fba9a73dd8d9b7f9d43e8aec45194986aac6ba0630e5f317f25eb8e8df10f9e5ac0244894bb20e41b8075b8aa828947045630ee18732d62c763d518266974f35898dca6663545da334dcf85668102fd5f7d6f02360d6a4d364b4e4e54952c6be290feefca53627d70f06fd2e1e24bb7bd535153a258b628dac61a49cf86c1f2a6958adee468b9a5dd8eb99cac2ea6d7c93c22a68970d818305fc8809d7870e4f939711218ab0543f58f9af659853eebd844fa9c2f4bbd7f6840fcb3622645cb9f3afc688baf614d75f2900760d93f390e0a67ccc0ce20f372420f2925ee52e9ab391adec22266667e2b7d812473e7baf94b841f9ff6f15d1f58cd5a1ad25e40ec4d7ac5ec18cadfdd3a968a6c124f111b4c2bb010bad6ee2c943c3026de622633829fabdd8c6c6d22c5e2a335a2a255a36b49432cfc78e4927b2142a7d9ffa75de3a93e084ae3b01890b7ce63b319351ff28a2b46fd63da7bea1b61edcf044b25d23698845005009a2febf21d095da9743f725dbab98ba173a92084b8868a706f720f2a6b457ba23a2da06987b6e6cc7e5ff97c27542095c6c51f44a08f5a9d09dab3b29df7d3a877240725a261b60fe5bc79b1abef6362ceda008d31293f0e2c18fa9cb933a601b334a87c3b0ab89144c1f33c30e6d9e75ce18ac925ff7efa04cf234f63e8a6668759d8354f30ef575f3cea84540c8f30baa1457149cf5d6602259dbcb67ee37a41f486ac5c8593fd6bb04b0a7a215a38caa1ff23b0087bb1aad519552c1e0320d07bf62fdc2674b248b81788568433a745e892a086382e5471263dbd6b86f68797990d0e967cdf8c7ea0074f4fecd63fec5350a635d6054f9bb42267dda37d3d48f53f8f6b5faaf1e3f02b985b7edc36409ff02ad3ff0219e2cf6bf398661136076ce9237356b9b0145e20a90f624e7df414f39e21b6c8122ab02bc0012c616f058c37b2b31f2da9e3339f5d29917a01fd463ccf733e8916ef63f922523589e6093451e5aa80ec91a94f8a71590cac13ef6733bd300dd1e57b5b2aa85b8d8210f5ed86bb6ba75af8583b1957c23bf36e040902d3215feb313f05bd4ef8e3f2ff6beac3c98bae556e7dc7a2e6fb3d087d01fa2f36774b9ce8ad96de94597e6e44dd5a84cbe5e6dc30a6ad59cfe8a4af28bcd8f3ce835b701430282e8b6544c4267347546ee7b49f6fd30246ceb92196c504aeedd3fbe383e38d7db3b103141229bc2b80f19c381e009c5640f9fbf862a92c7ed1fff3d6831c2119d3d16bf9f64f27b2cf49c64063ed92a3164c86c10b88bf10ff32742121d41573897928937a4515236e4f3a2d28c725388a4a20320829aa9346e28a74116cfe8a357ce03d144f2f22a8a463b2d7e0d14bb74ade3d33d5d90bcc09c000fbddb723720f4263574b9d48512a4c151d8f0a541eb01425a8c2e84d24babb124cd9676462f9124c2049aa236c70662322135e6abf7d356177ee465f418380b0e19c47187c0cad09820914e821cbacc4cd1931dcc2c7218709e55538bfe6686e6d943aa3d018deb21753c88653cc283e1f83a8cedd91e93b1939f512968c8d147fcbb29f14a2cc73e413cb0085551a51d7a1922fd227e8dd0e500b0cc114caef86cd627f1127cd933dcfd35cc4b22d3e884e35584381c980fa3d8267d090917b30f41f5fdd53746c5963f606dd4e1f0d2c285106ecbc75798d140c5100e7088b3acf693ecd5be0a7c5bafc5531969065fd8afbbe88869247d88732f5f75785d7e8f93d8b78662ee2f002a6c0db2732735a0111c4a8bc6b549dc28847f3421c319071ecf549225f44ad1787ab86725b86a800f2c74540ab540abfb23844560b50a2e374a8fbd56e3746bd19fee11918fa4baf9c2f17b1c8e31299daffbcc06398962d5f62502c40428fe748586c289952d1fed921cdc50067c7ec255bdb630d9b74eadc52ec2094c449190e11a05e3c2a78825788926af99f73d5356d1b295759a10dfdc6409bb42fa0f305c102194096b7626a60dc0feef70dd9096efe7ac6e3ca89c2beb6a74c9b17d6d8c7bfb6418f61405bf87454628bac2922318b9eaa8bc01643049565b835bb3da106fbbe3f9538b2759d720f8ed3365d11a1ef71c6cd177df512299a74fbc402d3d8bcfdfbe8e47c80b2882b4d22085a94a11bc0bc9c543fba06706ca713a81fc3f02670b9310e1a23c47929cb51a66640cefac68d62785cc5d208a705d16262769b03a9d20c08f21b53ae305890f70cdc9c012b573d8c802385c4c7746e8670c1ddfb13242ce00e18967edc1d9a452f04d27d4651e781ccd7984d6832cc80ccd005f9f7daab041e8ca2f8cf8923875c7be325416237f1b3c09d0bee860da25952b94d280463785f8bb8758b60f245aec503b587b396e8f825998aea49fb499d735f8e5ff4deae86e30da0d72b30027d6370c5a2102bb0c848678b6564612069a3b46f73407834a61b0a83f39fcfd651f0f78d13cabf7672ed5e676e45c2aa9a7d31930fc9e9805de7ba14e00f2490b0e7ce3be29c6ff2a4384ae6fb4fb9036fe6774c54fe25e9a0dc2b1d664ce04b8518786c2ccc4dcee0717d8d85ea9569f069118888c9b3cdcb4719b33bc349221f256bf68e6001b300af50ccce09207cee7efe8b3294d9942eb81d02db77627e9aa92951e2b853f3ba44f6aa1ed2aeae68b6d90fc9ebf086829ef619ca04b104504a07eec33b2919849062bf2938353ddbf839023becfbcf9ef66ee9ffb34ca08143c7a1c22348cca9c8e2c04e437cd677c68d171d8304eca8b8c252e3ef182e5feb03620573bb153ec5f0970f4c07ac27eba140a0dcf035dd322374aa062863c30ed24384075328fa4d9b54977c48eb8c9e3288dca507eef371fbe8baff62f0edddcc81c909664f0f696ee40c8ed246f23df0d438009730f73984ae51068a51772b9f497c2b598fdf08c0e04c5b0a6d74d5943e65a35f8f134989fc23f287c6329e13d6333744234a85a78297483eb15db7078dbc0e74044b8c4573b15195ba3cf6a1bf325ce35123e713024836f66004a9750935036bc788a51e3ae8c9aa4223b576d6bf7149d26fd155b23ec9d7d783040cff7be1809dfa860cf6543829469aab2b6a2221634d3d01863771e410cc6b408c14f7c9741248181764aca57369d8b764924b66aedb334e232ba341ea173b819d1e511885a29765a571132107120d8e7b31f8fbdf9612450221363e01f520ea224b9297b198e287da113d9ae14b0af18387e705b0a1bcb9c9650fc823bf88b1e061e95a17073d6d24ee455e93537bb5b2d4c705e929e2c36b643c7c6d673cb115675772d81397b2dc4f58aac7c55c21301f540102b725a900b563214b3e93e3110b3850eed177134ff61579dc982f4780e09bed18760c1fcade920def678f9fa0b2401baa92b24ec6960ab35d7f474eb7a7f68bb13582a233bcbafbede6a2928b1d8ae072435190a560aa3e06380d4efc65f9dcd206c8c2129d04abe129232b12ccf11f109920705b4891b3f49d5a46a312d6d24ca011d54269afdfc588040ad9c80c43aac237b06aeb7f056db6974e3f19706ef2e8d7025a5ea371b897c1397eb413aebdf3ff64bc9fcadad2f75b7060acc70e3aee29e8b894f301cd5da54d2487325f2107b7974be3c3b754d11d8c5daa3492d41121d53b110692283695763b14e53857972c9fd1441370791cd47882abcd02ac48e7b1f23c80d03952c5d0e54105f52211c0ac8ace1ec600782986d7d4adead414186698f29ae8ff40dd064eb8a377612059d216ccc75513218fddc277424994648a471b3aa2f9818fde537a208ab7f9723617e34bd5404b85f15f993cf899a14be32fc2dc0509301c4eda856cc39219ec2a160a06cfd0d44e71873e55139ab86e1e9ca7c5318f38aa4a53f6541fa726c3e9ad03899d2c08edc9cc92e01a364ac75086bbd207507ab5a8c36cfce8cafec8c347ea83440874c41b92dfd26461b31866947037fe692094234aca448a6974837e4a20ef7638b57db246b83594d96b8ee543412bec5611d12950f2175671ac25929c615f24d4bf0736f87e2b7176d7b75572099681128073bb2cb057ab0e0947259281a134921dc55fafff29657ac12b68cd90b1e85792f954bba3bf92392e438096249a78e122a064f13559a793ae068b8b6825d010019170e8b451750b68e59754ad601b05c5e1b3e75cd24e30263bf6e5cea56ab3357ca062952e6f4a6c9fa95febf1d48afc8bcf72bfda0a752dcc6f772a86afd0d11ffa051fbc37de7d0d1b1350793a7366ab677df39d402231b8608de795fc81b2ac6127fab16f69ed3085d1db3680ca7be0c7297728bc3393704825fff4c0af6d1eedc176f89feee93d7f4e9decac2e3050c0f2a54233c6e89509b8ca28a696363008a92f65aea84d401b88ba66c8e247e79654a6b8dd2c019b84d42b0d8adfbd07863fed735811fcbe560c3f9104ca9df987b66806c8b9d04f54cd30cefd72c5c6bbeeb887c57d8a91d51a4a542b931a9e13cd33a0f2d97f539014978eb847aee9af51f644bb9005f7467620ad2a99641f01a8b9397e85ac9439a3cd7a035cece962040c34afd9315ad686350d727be987cc4f02140b020e1a7583b68146e91832d831d34b4ff6f098c373670b02326d861404833e26d941a729cfd2ccac12e525359edf6b6dfd915ecdc96e4e4c7aaabe649cc62604dbd678a713abe14d8e8b8f21552330c91e22a199008833a9ecf5815e0123a78fc3fc1bcd38bb0b178eeb2819c46f6ee46f5c75047603638ea6963a25808e12ce0adaca65a8d5b5007d9890ad33b5f7cc36bd222ef32d47822a13763ab2fb6945eb5dd8129a4f70b70d766203379c97c778a183dca33ba01f04f2fe25ab1ef6a003708cac8f4ac17290a587151af02975ec01e52a22900e81028082b6b21e97a105e136a7c684d585ec04168e20e82adba856fcdd2fd6bec05d094fa0b38ad1f27eaecdb0f88045accea0ef9be0eeacc62b01cb1d09648e0912e65d5c3bccbff1626b8d7ab67ba07ffa620eb81937abea693824c7154eabcb394d42e15856649c772158a3674e1c3a87182d6674a01d02e705496a44dbfaa5a839fbf7b718137259e566d5c7e20dedfce52065db565acefa1e165b199d5d110344b6f8b87ca5120e7de80322063d3160a48123c6870e6131a0a459b17aae1a0e9e37ab71809c8bd5d54e784e6506321e8ff0e235e102f85c69d51c3300a05078c2d70dd796d125776ef9f058e2c222b38765491d84f0cdcc9dcd79b81a8fc2a6c9f68839adc9185efcd066b67fd47178411712f1fd91957e968e7d54c1e088906742da23dd7e4660c2350217dab10433c5755d1a985c9d9ad64cfdaf643797be08ff20ab2e3141143f58a8981fc30818d79203feb50a60b03c7a516e18dca50c56335699ec55ebff3b73edf663e89c1e891bed75bae1762fe9d0b9ba5694c7d0767d26668767e6697f98063d1752b286884e92b5a87d1faf152d709e7f05b2ab33fd85cad1f5dbf1a763434f154f0e90e6c8618b3e9564219ed538ba73d6a12bb7a9ee93c024a3b9ffaa88d759d08a7d969a266496ab6a3843324312c729628606b53238cde37331a07912de257b370aa2f7922880ddf493a39a9ae34cc6cf823516c56cb300c576bab9feab485aa9c2c1820c0dfa88ad30b177e265df15d678022c09fd42bf046d11dc5fc3e6c858d9aff356dd9e987fa0c0da45bae76e550551906cf7aba007044daa7de1e78c6d4613a8b201bb84590fdba30b151c57b956a45777a24c2820b867df812fefc1d8e1bdda49ea7d48bf444e17e1c4e8869c7a61fa426d24e687309e8db7f29ea132c61cbe31ca82f45872f4e0c95eb6af687b5bdbc6b48b8f7b94cb2f671f6b8ef5c9878828fc0f86d6a04642f01a884e29d73b3e15a937d4c50fc4fb17c86a9e7f40f53c03b486bd2c3dbef36737ff0696cf3198cf12ae9ed72c0203df6be2c713dcccc38228f480928e6ac151068b9dfd99c07b6a7c5b6003d7160abae2efa342b275805ec21fe2f50fde0145184b517006609d4de7cfe3644608904266303cf051200f21f8e00ae698d6561f7e808d202b080d0ad0775a345ee32640251fdd79409b6516441c9314ef46ced8042452a2e062073bcbb56c1b2b81deaad5183c02a2727f18ba74d99d67c02b32685ed66a99cbef11a04fcd7c42018542baa16db51adb8e768aab9a58240888337a10f3317c0a0a8d4f9a8d39dd41ffcea1071f5ad85772b13f671424c8d13e6882d7c2ec28e495bd522893ee6df1f00997729dea790dc29c39b5f6b46103e1b75c95dcbce3e6f0b47642a0d083512302763755590955bb85640fb3cba4618e76c045b26d100c5c574bc3ab7ae063e4bc8074d19aea4e1983e73baf5040118ccc3fbb21731ec029ace746918ac581bc81a89e31050c461946f953d233d74d09b5b1ebbc7dd76c525646b4d80363c05a58042da678a707625739d3dacaa21dca20e4d334a738ece20f3b03b787ee2ace038f2f13a7ca6e7e57a78d18bdbb42fb9662ec1a865ecb03ffbadf853b0104879fcd621e5253309d2f480efb438cae7743e37948061772dd5949caf1195e482b9beac71071423a9a6fe96f8c7b955c6c75b93395b4120a3d7bab218646151877f4a3ef54b8d682d870d22af2c45f5254169b7c335fc76cb3e80e5c5c59dcf119f31d5b32ba04a3a7a2f3cad0dd2c84d03a4d35ed64328ff91a2259cd14cce032e69e876135f23faf86c704c839672c12a92f56c39f51993f1c39a3fedf97ab3e1c2d7ed717ff9a786b794730a3dc49f94466b06214a06dd674d1d69d427648c9467e2d369d2e310c6119a2ddebfa5dafddecdc7890bf3e5563dcf4eba69fa913711aa629e62980e6b286df06593c4f7c4ae2a620de84dedce1133ffcccfdbcc7f7104023ce3005f015ef4d80c859c4c4c839c0e36808eef48d2da465f40d99aab271613640e7bb68e1f41f847f2fcae703da25ecb0cd1e498125301beca1cbaf85e8fa1a9d5f82d011130c54977dd7c72897e12328b4de38ea55f76626b31c26385b5b31c1a55dc35676b9826bd07556569ad0d7fa3f211832be04e615a08bf2329d99e15a493d0b5d00d631943ca89c4ed08fe2685079904507fabca750ec903f33becc19e52d724d188600d78e3dda625aa150bd622843d22e308305ce0d6077375f7946ffe66012a2c18b1bcf72ff4afdde6ea323a386b44860c28be72b1c62b7a1fe269d5b91116e3543bc6daba8ede674adec9602052921f7ca07197b2f7514207287b8496b9e39af4ec463f39974d5cb96795277072ad8302dd799a0745db0f31ea42d0c9290c2c9c6c8385f908aca77cb939db2c629fe1280706b8d89aa3946c2cb25ddfbe9bdb692e297d2e834804c80e1decf1c1cf1baf16f6367ade547e5e9f10b8dadeeedd0c253bbe2c4d86cbaf001b1ff2ac85ec87a390946b057e6fab9ebc6fa005acad33aa6e987d599f2414800d61ff4b7df6080daef5bf0964b687f78d893de3e03caff2ffa5690a66a12d8e39eadade922e78f5de866cde41c4553577431279702e1c6942a6399db156496313c1c97615658818ccbe9607719d678ebc549f37163276e8f2b28001408dce261348653963ca36bbbe14aa845940cb7aebea51de290bb178af9e34b8b61699776b47b482a77a1b1b3c114c50ef6ce15b0a67aae1ddf0c9ebf396a5d74c6070fe654eb4f0808670978ece7d13e6a5cfdf4734780670cb92dd37d6de1767be83536c01e43069fd6f1e9c764a6d6a928e24181ff06f46089ac9b0de828f55e60dca0ee2f3a71d030ed3421e5965e93987373668c7e390a39322878f60f476e626f242de7ef2b8ee034b085fae664ca81b0a6aa06688ae6f7393005ff3313064160841d32e11dcac4fc4dae6a9c4b42fbef70dbb79ccf07fb729747dc8bc9b1d025f8b7600612118a47e689f050bd097bddeee947bf8819b6c6ce404d1dc348e4e43021e272b9dc46677932df34ca88a32f802f0df72cf03183e3baaff9dbe3334845472030e405be59b4b1bb644867daf85f3a7b5b576f007863e94db95398e9f49d0e77b28c4f21221061978cd1db2bbf59dbaed686dc3b2c550ab16138bf2807bef746abf027f816200c7a8cbfd1de262a966c0a684726ae911031f121daedb2e4a6e006946be26ed4c90fb92a01550a75abc51c66f314149837bd88fbf649ffff674fffa3d3ccdc50cc68f8640e95b294866c79f76a4630e5b6b84f64a1492f811691932f85fc8ea38ed6115325cc7d19171a907f2e62c52e2d1da3ae152b056a8caa186855ea731be1dfc7d2f0642c0ec18eb2fc207600593c479a2478088d29b8a656b740893d6307cf06d08e36ed21cb46b49e15adae45b264888d6495eadc83d5ee510febae65a3b6bd00bcf04ff7960f40b5dffecccb7d22ddb2272d72374926f78bfb52c94b5be0244f02396ec5701d10f9ebdb0d2c3222e72e10c6d7a10d80514e5811b7dc06d66947a0d1685c999d18100a7fe54f56e3527d1d1a829712826f3fe32bb19ef95a4694c8e60248a81baf8ccbb3a7295e549ed4816b6629394e06cc1252109a7204e4c6d329ec01c218caa6634525f0b808ca630e42a98f896df3fbd51ae909043a053d48a701317ae3a7515e2b4e900fdd36f40da0a5b41c559574f69fb81efd8a41c3342efd5194151095c5340a2a3d4aef3bb551f40e79353fd130492a82a004fcbaa729a8f9a07a9aa8d02db838291fca05c61948e926b7edf0ad4b5df83a7774d0c4c9a8ae52f685c96c4475e1191bdb23902b6a3546167f2449b3cb6c88fe11639240ff0b1620b5784e3a6be8a9c36a1ba823b7480105bf1d1ada953d339635ecba3f968ac27ed95b4f329cadf027b0664048c1304316fc7422bac4c38e09ab2ded02cadc00127c5d843726d7526ebf10c52e3f87240fe4d4ad87e32b23cbc05ecaa7b88ee7dd6c4027372807c44c124affb35cc122cf7451f52709c1828e985050ee96c267d7529c524ebca1afefe2ce56bc4d20d62918c2cac6b1364f593050fb371a8b83f382468e83be33369fe6ddc72453eabdd7ec4dde185ebf17a07e15688eec4eef1e00e498d4d0ff5bceb76cce825b861b52e84d847a88e8806ccf30b4ad424b0314f57caac24972d98f17b63688e22cc674ff4539cb860ac62b98bc6284829dfb8adfa7437f88bb0c3ff495a689129e1fa857c8f5870410f2658ef4f245d1d6a9f57e595ae13e167e7b3ad46779bee93376ca8307f00afb69b8fd7faf8ce15b30e11c427c01267e585e575d2c15101d97a55debebf7697dd42dae55b86424dd6ba02450607a11536f275d8dca6050bfb1a8d9fb55100945cc4a15593e58d91b79f1923ebba2bd3b5b6a2f42b4424f628c39a366b3d79db39e0989995f2042b23fdaa3d8aaee58e44036dc48b10f4b43275a354f7878c08f985581c2be504a56280e47d067017bcc31648b1cba3f72bec1de05ae79eba0b0668b06cd724f658f06492983d9de519fdb311506de798e14f1a0b2b39f960714535c36c564b4176e0e0c469e176941ba697da3f68c7ea49c6ce01681617b49612c0a5e76adc7e33316491e54057922894bd22ca0e8e8763f52d0154cb0f581dd4291bc1feb0ce0b4fbf2883808569d9d8d7b55118c1fbf332648f57bc39d55a1af1678bda7e05ec06b40b234e0d83d6a0db1c63b81479ffe54d8870ae69d2381bf1fa4e1dfaccf3317408008ee4ab238e9707744ff1cfe72cda6952a0c6c16a1d552c5855ca1b08769b0e0d3d7052b21e1174dc9f9f3c748bedb1e94a9c9bf04aa5b1fd4e021d7179e8527b49d4f07ff9bba9d89343d4263419dfa848d319c071b87a94e732434f0d1a58a50e056b049387db0255028307a033d79b7d966509a5b74ac8f25fc850709fe21d2acf4ec0a8c43e2ba5f5ca283f3377e4cfb85e89dd71b946b32f2703d7ef1bca5baf42159284e7157e49fdfe7b032e14e5b5764b309a187ff4f1ba727000389b8a3d23508bb7cc305960c3ffd9c94aff1bc847760f72255a311c6f65ae58ddd56046022085d45250a0f0e000fd5d7ac513cc79e95eb2fa48feb993d0488081def1fd8819ae4e3615c89fc31d4fd61b05b0aecf852e3cf07b26c10057529e8944312255c491c2554a8e40daccbb482b6c417539cbb4aa57e2982070050040e4d44bfa6ad5e59246a44ff1c4e081bc78c62e0583b003b9a0ba8afd68193725adc2b26b44fe8272cf05a3df3ffb55eeb9c3ae60fb295c454e1c28b0562dcd6644d3cd057eaa7d7e3b6647d76fe5563a2554e2a5ff58088d57e8ffa0409474d765e990b556be71f66a4adcbf889fc2021dada0f492f1c38a18e95d6ed16cd797c3aff47bca8d77ee5a726ff73a28a65c224332fc1ef3ae9e84d63c8a19271ff512a9ff233353c9e56feff00b68381dc2d4fd55d257ebd4ca96dff25ce803a3ef458803087a5cf21f2a9689f7c3f8896571e155de920e5b52e14da9de829bb4257441762dd2a30820807f230941ccd302cb8610100a44c1fb601e296a787448a17b3bab516d36d2481dc48c44f75629a17233161d02f3be0d5f84c6b736cdf2098d9395f50ed0bcf84ff00375dda667bddafedcb8bbff045f9f4caef6f28fe3e030e6a191140bc3c3fbe4c0080edd2d6c0fdc7e4cd1fffb4090d80d00d09378f15c05142f4f640d2434c5dee84dc3f468d5a9dbc67f9625ecb90fa4c94b35a40a912eab676d0b3bbfbf076a7f2f9d1ee64e41be759f15fd8bc75737fe8dbcc836d8f171ec99e98aca94a942e331ec495b32ae3ae024e495aa95033de6c0b3a02f887aa73d62e7fca6144eb8934fbc279968e4b057ca35e48bf761da4db7c049ce2f7b6006ecee604315bad257f6a3e6499ab02ec066529b848c4c4ad88be071d58f90006c9d786f23a46835ba83f5a084023df73e7d456b080e957e19dbbfeecfe50e4af8c5abd4f8a1beaf295f1d03bb9557f0d6afe6d1add80352c6f6ccd602f4fe8f2a2f2323700707b851fad1c69e8b35eff2f8e9e0850e2d43ecf31d5cfb9b99ecab328a36626e362519968ab016e5abf68b938be4df3a1a504be20677f9c35846ddd65cbf281b95800205ebbdc8389a853d92f8337535cfd8a239b21cb452f664dd08ef82bf66f5223aaaa6970a36bf4eab3a956e2a11f7abb61e19ede3313a2281851691edcff173833f47eb19683631442f1f6e5df175576a203bdc35b30947629b35de0fc4a71d2efaff9f0aa80454128e6ec05cc29f3834e42ee9bb6b08ca0e86e87589c10da8590bd0893d51850f1af73c50ff7835105f1071c14a3ff22a915beed7cb16c15788405462a118c737f1dd3f57ab45447b611768bd26e023fa1dbbb8a5f8a8b99f53f15b462832e59165353f7e0d66c9a6b5b12cf3856a3dbc6dd38905178d820c61bb4580abae24cddf93a0ba3a4e24cd21b55f5507948fba0f65fa92385983524141fe50b21335ad960e895dc590a456e8a7f42ce455fe92ab42109f7679cc0569083c69dee5896e225f0d220245e13671a2150f3190a44a17648804dd12c1289a96e2854951417c884bd06a8cb285869b8d8493f59b6ef84f726e7fc4d643aad264e1b4ced3195497e3c15f82e7e239738164a81890a40a48b4f4117461a6a2400cb9ff1ad508f497f4e64bb8be464850530cb0b6cd6d92e6719fb6cd19970938f5c7351db50bd327630cc6ac6a9ec42285fb64d9808eb7113a7e1dac3e5a23c919ccea8d99156119ea4405a46c4bfddf433312810908fd243758c6dd6077b2ead3b990984124c7c3c17b5957e1b5d16735b78fbbc462dcc53c12525223bae7b89e90031bd721097a52f9c6e5430a0abc05fd0ede0d0e4c90744c561a955e6443447298d12f17405649be6c1a23aacf7583686cff57e73e350827140b6cfd5d52253b048ede3f543049442f15a98dd2edefea4d863dcddfb6215ddc9ffa9f9cd9d93c0a71a72793247da337f081ad55954cf09dcaa7b7c9dcf0f751be4696efcb5dd39f8e887fbe38a4cd8fcb5aac22c576acd49b8ccf52ca2cca10cf0debe0c56609c21beabf7a38272f415920e0a350f2e76c3e6c38ca391598dd6e4d46ecdc2f1f6ef0fdd431747f4d261b6400c5ba069b3c7845a4c1a91edc52cc4544214d96279619751630f29853541f0d3acb8ea0b3694ccf49bbc55167d4c373e609083100838b2bf46b5d3648322b402ca7e21be477ab67377a01eecea858cb0631b6bc13b2941229503fcadb9101fd264fa54b8bb95bbdf0042fac45e942e3cf850fa59302e3ad7c437763841811e2270f55fa911200e42afef1a560c05a5e6fb840b4a225d50feb4e71956aef20dff7f07beef6a16549481ca2831057c89393ede21b9fc86c996c7efdce636e096ed6001a07dbea211caee62c7eed86d8f5b568e0f36771bc054f95b235ddff665a5d408db4d89d5556016322251a1aa0692410daf902970cee99c73d401106bb6ff366ae00bf2836c71cac4fb62b3270bd6cca657ec7676886f44f2f5907c07c3b542e3edf56ca6bddcf378cf708eab7c27be16405f9449ce476fb6cbed2f0d322208abfc4be997bb8e4774b0a381c39756ec12e3b99cf8cf7c8a52378876e1d107853010087cfb82ac6869a907b289d1da82e0a23d2c94f7d134c2d4a54fab5e1443289afa4e5490a273e1a04e6c60a7f32bfa8e5cae475383eac09453eba2f178410f564dc2996d0b90a129c17e15cd1924fad87c1d88bc79e2c67d1c47f915ff3d4c6192f9117e058a617703e2e30bb4083476d2320c785d7bae1868590e52f3065cfde2b491060807439ed3a7b8199e7e3f08aa56309b93c1de8fd49177cf1c6c12fbbef2f044bb4490ed9b0756e700135ba1262346ebc17785b5aa814afbaed5f6b7831c92dfb2451fa1e265614826f28c5116a2a6797b373eebaa7405314c94feb9c3f1e7671bf0b198e695c68d1dca9d6d03a64d3cf3839b404da222d8b1f2175e41a344fd7346687040813330518a1816afa97bd0c1e98469df5fa72c14694739bc8b90e6ad71aa1f924026e6c764d94dddbf1bdae6f5cb86c908126002b62361a53358a2047138eff76d5bb77b6c0c3de885868b3c2e16da3cbbe44f2d14cc2d3ba6d06cdf1b5a0742b5549a19ce68b4f21b7eceb1d08d1aab991dd392c281162de97174c04069fb47b1ca599700a19bd4d61a61f26df0fed058c7b1044090d0a24c1d6fc0b683ee66c444cc4fa8c1cc573e4e01edff670e8e1264e1b05d87a7823d1fd608053da3cf0c6c725300bbf4c9d98431abdd7dc4dbd577f2ef3274041bf60ef4a721f8fb13e5f7318b8b7e5903c6d39cbbd9ee611860d23b419b8a8f27f89718cbcd70f6528724b46bb2152ec33b02c4e6323b324c320d9f8d96c63d7c3070295b7042b8e0c1d122f27fc561f0d7a1dcfb2c44c2e9e017ddbd72ff1e72d10126d9ed9d9720487e5f0d5f2847ebe9a7c70fcd7fb8f116bb5f7250ee56c798d938e21c56098b595f56257544ab08fc98f8afac26a514573b254c83e4fd9b9283d0631e1565a88bcc3d83fde42c191a5900d81ff1670624272e4991d610426706c59b3619108129ff8bd79c7592f0475c5e058e3fc4120e541050aba83ac42f719c87ec126b8127867f60a81175b4c01dd0025e8215b7b3f9c768d80de82ded59490b660d11299288749e54a580f900eb69951580f9f4acdc723711290162eec45ad68d752b7e16d30311b329580e5a0e790e42fc7fad155d99aa1f20ccf08331febf166d129571d726be68b0ce0f64f0810f7ef0c14b0f54d083b107303ce0010f88b078e0c3ffd7121dc2e8c5d3d3d989f02e754ff710baeaa889f9d4ad793657e5a56f783064073ad8010376e0a5e6d65ad5b7e639dd22e6048d392a1a547c4dd1d8c146b885081c3458685cff5d073e781ab252394db968a89c501d37493b8aac26b9c2465867170da1b89357c528d9719f91c1a96d7473d50e06f9f46c15cda5750d1fbaf065aa52a71487a70b89cda54447675c44e28ba69f9944a19cbc5052fcb95215a741612aa71cb446e38d52a27399658957544e7dca557dae7a3a5d3cbdb9eaeefbe62a710ab4530e05a8074892e1b42171521cea9d0ee1dd553827c5618a431b9b9e237da7c8a010a3f8a70c2dfe6b2dc9545592a9aa8c98df41907d1371fe2d35a47b8889730b0d3eec7041937f1f5fb4200e14725a00821650f19f42512dd043429369480b3832e2a8ab14c5271960543288d06448b312c9d8810c1ab2c8e8660c32fe4f5468a69caeeaa3f718ded7317e5c6294c1c5a8220626c68faf160bd0f87f1664f1ff9b053fbf12af9c304c2b0c302a668581faff1e86ccff3fb6c2f8af9d04a38c1a04c695af609cfec1f8f12b3841d73a9a0c2d1590c052c1142a38800a62480196bf6acab91354c5573d574d81510a74f8228d2f96f8c2e68bcc0b3552680d5a7bcd75ea5e54f162077701832e8c387d17297041028b8b27b848b2c51b55733e7475b2f7212b5ceed3a9bcb94ea70b9f2e7cfa6c416ec1a1c00e14c060778e3b0a1060a1e0090afeeb1e2a3169aa2e2d4aa005154b8b1d3bac1388e004526ac7d7cc29ede44eef64e73cd70bdda98a73a3f4b23992379453c7a82897c9e4a45137318979ae5a637233ef6a924326d6e045f303d6d3cccf45a32373ba4e3aa7d34fcd65ead78c2873d57ecd6099abeeaa73d5ad4954ef2255a69326535f8d9d4c7127d2bbab70cd55b1aa73f2dae8cd49ef144a13ca9ca49c4e33e1452345e674758c3af59ec9a24b9b644a9af84ca53dc8490e47b7505f6123f389b94f9f2193a33310e1bfc700c4d3e9ea2a546a64c808d1dee7a9a7c29307b6f04068050cac20c38a20acc8a9028f2a58504512550c7df5b94ca7b4ab4e578de974ba6aea0aa7f6a9ce9c46bed17e9ac1283e656ad28ea67b680741029d3a2aadab131e796aaa52cccbb4a798f372a7174f4d2b14932579a4cd53dfa88c73aacf129d25681daae055f1137417f9c0f086cfd795a879ba6778649a9cae9a9aba4d945f28d3e9dae869668647a6c9458373d168bc5132a7cb942232c9f242650c5e3906e0e97d8b73d5646badc86432997a788b4c26f2c435e95b17f881f26c258551ff3fbc15d6143e684545ca334586e489a7c02e5397e28ece7ffe7bf44841c593384d77bfa4d87929844471c7ff35258a321e33a320e2df00517051c444f1e32f9acb84e4a2124eb1401916b8d22d0b24790b08a002586a11eeba024ffcd754755a1520ff6f708a954091e5eb6582b5eb5d545eb8ebfcdc0c19794f49a36eee9c8efaf4d4dc290b54e05170b1de47eaa3fb7826da7091a374e2e7df09eb840f2436d765ea5613693461c5914b46c8654a3b2ecd26524dc56a224a13492e2442a8340180ff9e92628844c8156a139327384c60f1ff4499973835bd46efdd6b4c16132e989cb49640fdd7ade8e269d94d8c2e11e4ff52620cbd77d625576288cba484f7ff427092b8238927922092040c1400c324319a561353e0a400f65fafdcf1e89527ae3441628eff9aaace94bc6890b8ae1a249070f93a012cd704365027a0800958a309fc904019507c7eae239c92c0139704bcff97408f0096bfae2b0267046a308f68c21156e0fea4a6e9063ae2e7bff6917723ee30e28a360243f775e2a54a739e254ea09cb8368244b315cb4a93d44a1093a3282b5fc58aa1ab4a37f7c9abc8fc4ef7956ea022def8da4db2882abadca5ea028b48928bb8ae2bed1c0258be5e1018e2ebce16047e8e70ea0177880fb042758e34d7039a100147aa3a8798354539c63b95d374776dea17671a31774c629a8eabd1eec9428438feb3b9ea373d8551fcd3555b05444eda7993ebaa45e426572724b59e2ed3c4465c9ba4114781ae4aee534f4d72c83679cd4aac19473e84a3e7a5f373234407847022840fb1972516c98cf2911c32f755fb367711cab12a677eadf01ed29ba7a7bca19cae9ba0ab5f1b9592279b1999d36892246a74a5d23d743acdc8a4bb9ffa0e824c91e9892e21d114d54bcec5d385531ba8933dca7584e5e88923d490499e75a3f8a8c9514c376ac3c888880bc9b5da44e1c9d390c628ff571eb483207b78f294a8e3209ba023274f738ab620ea5855747e4d2fd2fe3fe543404533a8cef87a3a199d66684e9eca9c5443e8a8724869905a49755214ac8ec67fa5725a6d1ed28e774c269217e18bdc4ea89c4e53b036f145e5749a52cd9b9ec511e6e91ae37429ddab3a6fa16b00d9d8041d21ea3888a8e3740741f67ef23404c209204c206238c3387fb04eef5f4566933c5595f755791eed93e9da624a5e2749442392e24ed3a3dd7136f9062c4ec4572aa7927393046668bea46290324a9534288728a7942b1095802813c9fc40831f9af801f5694aa2fb7475bd2a4f9dcac9e4e40ec34dd471d0357255e61a0795539cc23498dca8b4dc62bf6830cacf72a3d0343c851b3d9d883a0e4a71effb32f71eaf9a53ef55294393925b2c6f2e8d62a30a0442d109ea01737a74ae1e9cdc04813d3e3c974f4f3fa5393ff8f82797200960a1f0406df17548255e2b33f7192aa7d314990bddf57453b7c953e4b512795055765a9a7b67b57938c23c3645f289d660b2a8a6a666c824533c5732081215716a1a6154af4646467c34c925dca77197c944e55473997646ed7675f22ad2f8a219326bd05dc4576989cd7d5e25de68aa5f9ca769899b9cd212a36caec6595be4c865323789a69b48ce8f13224f5625c9338a14d113f3f4004d19c25db5da4420cf8d932557dd4579e45789b16856bd2f6eea6d5e3d7d17719e9a1cd54f6c767267b579e8da54611c8cf3030559d05c28f1087108f1fc8bc624eaebff55784b3c404fed725b220dcec1d79548b3f778a1324973baf61e6f6aef6779a6f03e9d2995780d71a0ab9e64ca7b896e12b58d2e23333b5c462e22d70e17273b92eb24d330dda59199934c6572897388e45aa5bb2c52e4a249bb0a3d9d4e579d39c954e6aa5995bd2635713e9d2e9cea5ca9498ea7d3a973ae4a51b228872b639f5379a664a94add5c7b8f61893b2ef71ec35dee3d86bb63d166ef253a7b0cf75ea283ef910317129ce03f4ca9cc94caf4e1880fd714224c19c19493350548c809421c10fae9a1093da0d1c3103d289142871436a45cf99ff1b94c333226d355735dbbe4e8b5512992ec3332544ea72925992281aeab6e94a75626be5440609aab93178de9ca2e293151dab0a2701105f57546a69326dea58951412b99ab4321038a032c283539b09e68e0c9ce931f61ce4c8e8c8c4f35ba3aae989f7d87ca69954da6b2c439a729a76a04698a6c54d0599637f5e6020a022d203b805c60016d012402e5c8e1040a27413889f9af33325ccb84dad43534bb97e0a542d38b733144f5b293178da942377a4a5523df06080dc082f5f3849f19fc24c0faf97cdab07c6ce003864fca27861e20f4544151920891209f1e22447474d041877a5d57da475e793af20b4a8f108b270b0f1a260f53bcbbcd8ccc91bca1d8f0b8f0a4b0d3cb92a3fcc23815f9c5d39df375764a3f8a2b1175c303161e88f8afa71447c719de63f180e2214845f9752a401b2879758cb20a208202081560051d13e8e4ff8a4a49237e3a5d692f37ba4d941c3acd50f1618a0f32297407dd5c151f6d7363a36d6abd8932c9430add4146bba7891a4da868d204b39a3029a3a298546172faaf2967c2e447dde912349658f14bae54b4c9d7d3e9c24bfe6b2751526ff4079cc1a33f1dad46bc24c99311475dab7277d2e8baea65aa50f2eae45553e80eaa5bf3143e2f93e4a60e3e37d1eee7de44a192ca536d729c19192e6a93e3643235f11039b4baf22af721dbe46793abe2938a446f66421a7e3eb9ea4cdab7887223998bc8c4e8a9aae99abcb94c3a57edbc67857f78d0131427ed2a1e1e9ce2948c92f2219374d2a3a825890b943790c080e408091835e836f9ee16922b902831e54282e45fefcb54a1e8d6256f92e20d147228a4292ef07f838e422ff1f1bf78eab78ed84753a9ad62353522511eeaa91487a9b9bb8dc9a4d31e9e7da33c1b39fd7fb58c605f8dfc5b2635b8653255f98a7253937fcc4b535f627d77dc7c5c7ce67f4d47aecf6e62ce83784a6a302d55bd892ecb1bbd9dfc186ded61f1aaec78522ccfead0f8ef86c8ac2ec97f0db2ba1fffbfc409941e4e8daf7b08c53cb52dee0af773556b4323dbb6f8af5b9537496b4b72ba6852bcf396f60639445e9a15965645c35637d91df5cab0d4a0ecdc560685a776cae6eac0ba51fc574ea676caba25fb84657f6c90ffd36947c1291a23251de537d816ff5f7110f625ee97556404569125aaa9023a9d3611ffb1ea18b58a5519500760d5f4e0ff8bbc5583855573819a9d1a0158346e583442ecd0d4f05fcdab0fa9a73e449baa9336b791f0daa8f41a798d91215c8e34444c9208462f1a93a3dcda014bd5a0b54313d60e04202284d73467cacb21129f299caf21935c6d1ef67922718aee8c4f3339685335e622ab33079188cc0d4a9a1be518856382a60e8a97701ea44df0c439e82ef2f1d1248ede5a9baa259df728e1412b8d796af38c28141a4a40c3dba0e4ee24464fac4dd5928dfea0bb68f77072c7017a0361af1dc8ebc8f5c3e9327202faf9d9b98e5ce4e9ff6d20057083ff6a448a9600a4f87f2a6f0920c95712c7bcf14f8566ca495b3159fc576dc508c5fcf8aa7779aa508d5540a029f973554b873574b8f25f35292eb174a8a9274e5da7531f3589d3f5ce018cff1cae04e5800ab272f8f99a03f62f648ed7961019fcff1f6d2d048a25c4ab35267999f7adeb015766e1c0060e18c0c10787ed8635ac1b5a60dd008475430e960d30b06c60810de7d74e8a1b95625c93aa4ade45bc51297932394f031a0f6d8551b409de0aa32813387bb31546d11bf167a5398ed659698e636373b323377be25dabf2541aa7a27233add37333d50f26d32d2ac0cdb4c6b52c61b589b0bbda3c94a5e993a2f8d424aa2a7f4844a50acc74b402f5f8e804e5f43c294028e58627fc6fb94309ff2d4968b1a38e0fb45431b3d06103947791c9d44dbe531c689bbc27288cd2133d74b4a61809ffff4f802270fe15f0551690c7ff05a6f88fa288ff07fc03509b34fb7f12c7c8bd047099bbc42e24422e73975ba9899da23a90bc081e0438505c41e2e7a110800999afe15fe84b4b7c518a224932821c3c207df1a4142c11a4a3756b6e7e0b11e62e2f2436d7897368b44d14c478d2465aa20486d96a6bf22e2a4b9cc2e2a307a524c6681151143c5da6c9b4531235d278a3b7283c7b12322491c2ffcc651af9651a372a9337944e9e379d3cf190599e29f2ec463ce799bc8de62689a62693c9e748de50c24da2295078f65569fa18f172e3984f74c99be4a4731ca1c4a0c9c915dee13d5440211da05009a8c427a7070c9df838710286e0ce13253e3ca1941b1e14ddb8eb0c02819c8b3bc543f8e4e8b953643384837e4a9cf98e0e10ef4137c6d11827a783a81425716e34c6294b9c1372dbe546414e7a8b1c24110ffa6f31c2cd7f4b1c3643bc44774ab5af9e9eaaed644927af1d044777efe82675dac9a319238e4f5387bc50294a8a17becc8e5157dd222eaf8e8df44eb1116d629c22fcb7c0f1a5227cc0e8c628b86ddbb66d9ba6699aa6699aa665599665599665d9bdf7de7befbdd65a6badb5d66217bbd8c52e76b18b5dec6217bbdff77ddff77ddfe7799ee7799ee7795dd7755dd7755dc7711cc7711cc771dbb66ddbb66ddba6699aa6699aa6695996655996655976efbdf7de7bafb5d65a6b2d86611886611886d9effbbeeffbbeeff33ccff33ccff3bcaeebbaaeebbaaee3388ee3388ee3b86ddbb66ddbb66dd3344dd3344dd3b42ccbb22ccbb22cbbf7de7befbdd75a6badb5d662f6f33a6ed3b26b79523a3d28e03e4677111db0c41b68a33fa99deab410e18d16d38dff9621fcb70801c734e2260f69df23de3ba8aefa397d3289ee94aa074da5c2b38f78efec52c42d6dfcb7b0f1dfb2c6ff8cccff0cfe9fe84b42ecd0cb33456a9e49ff42b2a214041e2f665c030589a654e6eea98e8d2e9e9638c5e30a13f1b4b439d2cf8e53ace4bf25086a0c99a4de284751b0c4a909858713f3689243195128230a65448b4ca6118562c34386e8de2715edb3db7635958da3926597114f3505779df746cf13679b191b4fa575648ee4fd83cafc04519c8a1867891328dabc49796933b34519324c79d9b78871ce13cf689ceaa2d138954cb650d2a3ddb3c40994254ea098242e7f8046b428e5653fdadaec1bfd59fd90cc4f91a9114ec900716e70ce139b4c67e7e8165ae1942519a2fbf4ae7df2438a523ff26f328567ef1df8ff50ef237224cd406a410975c0f13573ed460935c09f824e9769dedc886411a98404842a8a282a01b1939aa46a88143399eed47f4b1a258a82b90508d6ec373c3d57684e6a92fca7eb803e116704c6d279e1b3c82addeaade1a349fec0c9854a6f30b1eff8e8843c0a257814aae09e173e4ae612253335f577b49aaad3653af9d0b851a547d5a37b847a5a783ac0a319fdd0a1814e4a67a6491196d4b14408253f50a2034e17373f28dd547193a4643325e7c80f31d410a38a92e903a6ef13bd27785d78d64859fa8cd0d5a0239970038eca8683d28696349bbd70c52d4860654ad8aafed4bc51aa71408d108d10342ea51d6e88b080c81129d4ff17f2252242888cffef7da96cf97f1abe44f4c6d7547572d374f15475e2e824e1351a35844d62707a434991287c8a56e08408e670424d904eb771208e3080f8c289c9c4b50a6df204257b4e3288ec405bc43836344750bea42c71ce2e455c9638c7646241931c8ac91486e6d69a449ddd641acbdd73429b4b9a4364b7fdda5d64439e37f5d2f9b929921e6db3ee3d863c0df71ed3231579deec6cf29e1999cac913d77daed21d8c7007776e7280ae9337a6aad326127747a57aef4e9e37a79a22a9b9c9329fc8131fedd51631ce49a2c4941f6db3cee81d5610e706a7aeca71e4dac42893d428e7b9f7ddb9d97bca6d2ab7e6b5668b5593d766b2aab66665e2d45a2f9abb03ce456377c091b9b036370e36316a4c533c35639ef6becb700fe1159529a793498e6257413999a70b5f4e78784e33a60c9253ef4e7878d0548a3c6f7845b72e69545725af8d767c9d3419a66947711f3949a2ca5395fa5c75866b949c91b94e1255623ece9c4e12d5c3930c36d3ddc3ddafce9bd454e7e726ecd719f6b1e8ace9ee7d09949f6be457dfb5e4fcec171f6fda39ea1ac93034795d8961d86b8a9b570d8148e9cd240cc37a3aebe94484975c281fc3d30e73708a755d5ddac4e1497772a37d55929c4cc39ada1b5d7255945f1517edb287abd79311374f26bf8c362ae5e70aa5332f8bc8f3268a4f254f7c6d22941b85281fc73aa23b35f51e1ac2417b24c5d309dd45268a77c9f9c951e93e4a7baf45690d6bcacd0be557aaaa3ca7d329c5e175f554c5a99cfa69cab5fb13f25437ca732a1079e2ebb4519e53af5dafeae4e25a85a652e16adcbba7bceca4ea4c69b49326aa77ad02da241ac4c3b56ac47b074df1a4b2949c8e9f9c292518c53f284601758ce29f4ef674b2075d3de1f9d9a8bc83ca1c25688aa7df6c11e3fcb7f880073ef86fe9c17f0b0f76f0df82860efe5b7260031cfcb7dcc006bf3a6262f21e93931c1f27d70aff1c517204f311dd4190e5912322c639c2834c3157c747fa6af330ee2d925a85cda18e318a4f93a7c82d62d1c4e8100c3130c821480e29e4403a4df13d5992c40188fcbf0f2b32e0811fcb8b32baff27112089293e57ac48f1c5ff938e28020e33280a47aef87f120b5a16608302dc2008d4fff7284287d205d810801967fc7f8b1d46380009b020030a23fc7f0f2933a8411641d2c00105febf47162d1811448308e04092b2c980133d6844f0ff3d76400208e188214728e1ff5b6040028b1c4e3cf1450dfe3f850a307107e906a63c92fc7f8f36eca080107838c146f7ff3e2c7052010ad4193fa481d55b545312a2819210224a42aa907bed60ee7ec3464db78dd6b9a830da3dbd92694779472b1af2cc33479ff0ffef93d2f9ff1bb8e07f08e64abf65aef47f4b0dfedfc7b7d08007dd10c65fa6b4e31d1f242038080208297c50ba5247e90699afb99b2d584a36d451579c0799f1ff01f8920d59fcd72857bd527c956c9840c90622ae920dba648392ff8c6da8e1ff4b35dcd1c3c437c0f8c83223137652c80cfe5b64f043059f95a9f97f4b0cfe5b60804589062cb8898d76cf054a349c26597651a22148bdf161867f121aff2fc6970250c3af4c6cee8bac78255eff2d67fcb79851fa94364d26bd4d239fad71fe6f79c17f4b196492195cca22fe5ffe83beb44490dfa536cd6b9b6469921af3928eff2ca52041ffb9c5052d20638c55498625b8a6e93da0a46065b84a31e4f1ff297c29863bec9762f8e2ff7baf49497175f2fc420c52bea3b514034d29061c6290a114439037794a8a357943b1016905ffffe44b301c91272e92c629a854c2927791adc1e1094f7842298a1a317a4a8aff2d2c682135dea8ff963050adb14de948c948a9e4bdcd4573242571fe5bc058810a52f0c55ff1a20b2eb6684181165f81961398c0042d2520c108b238029ffc3fe9ff1f054fbc05441082161060714509e6c77f55e10644674dcaac19d1a29ed2cb112076ff80071e022e2940bd8ad56813a32d558a782facf8ff1fdf427e0f1b6020d9218397411affdff32d2effdff32d2dfc7fcf7f06febfa7a7a58a5495ea80f8dfc20173032d1af80b640003b9f40209ce543ec2a99477adea480f1fd21b2517def8944471917ef2920b60fc7fc9852bff9789041dadbd485694ca47a1cb975c98f9bf4c2636d2264994ca5250fe03dedd2453616892a912a7bb778c8629f943c7647993c9936fa0ad394fdf7b49ba81f04e794fba7752949f47a0f9c4448fec719fab34444d9d5d440ea59b289369ef3c15a6b81fe194f904e8e8bcd940402b154e9323702bd9654e8eb94f9f1ca3dd73a6d0267b17915b898dce91bca1dc229b23a92cc564ca8a74a8f790d20f1fbea35595e2d906b132fa1e38599e34841e458ae8fcdc6071ab4c628d118dc8f31e47152ae8aa3369c6a68cb88932c626e6d7554ff57411b9aecbd4478e53b3519e87ac4a524431785554c61bc3b60bc9856d17918b3c699363f64ab1a9f334e3f14a4d6c9229ae31cc5e34ab235293bb8baa9f53edff242bd613823c013a4d36577d051672b4894da73c60f0df4301df637f8f213d62c8e3481e33fcffa504cacf15e484329c50c5093f7fa272d19c2e3cee48c9b36f7de1b1051e28191c3c649a30c73faa09657c79e634818ada9b80525956135c7c98b005137ce02b9109364b40e3bfa2fc745a4216750955fe6b3fda4b48b204971a939b32d71d58ac3bee403d2ae88e26ff77b8584a80c27f94f0c4577ca3841f142b610049b8230958fe39cd4d1286f87f5227e1e7ab9984157ea6e6745f3434a20ccdcee40e8204eae58df904f501329fec8d2ec128fed1e91150ca7b501d37c129563264926936757a2779f06e82f21c345a9aa002123e40c717ff7454800e23e8e8e92619ea8df21cf6148773a0314795553947933982fcff11e6288ff00547e1ad23547184a16a53af53ed2867a32ef1ffa88e9b681d5d7212ef546519618e9fe2c3558d5002230c71a2d1e4141f4ea811ae38d488438a380ef0d524372a457549aac40be5a408737c11b2f8221c1521491152d509071a0f87155f513850b5f31e3890a43463795d3857ed654779bf094acd4d66539340403c7da7dc7ce2c30870fcff1022f0432ab19eface24c61c15af13475d2327c752355e3cbdfa90894f2679b571fe5fa6b1a80d9a17927672279bab6eb1f1c6ff8b37574d6136b0f8534544d73c870d1ffe473630141b3ffeff42f9b80618ff17aa8f6b54592389b586150430be07a14a107e3e08d71c5fd5d8e24b9c763eaa915a02e5478d243f8e69ccf1ffa88aa761833412f0352895f634ae7f202801085d0021e3366a63376a7ae7efbb494c506f99b8b538626eb31ba66ff86d3d42a1941488336eec72d6e3b839ec6e51d329c1fb69a3e6ba4e1cbf6dfb3411f87119ceb67c37f113b7c518d5d9cffe007258a76d5a968998e3b8ee5a5467353eb34a82d7db386ed49b78314fdbdd75200ae476f6ba0d133dbc8d79eb3654675334fc980c815ad69cf66d9df5440cb357e4948ca06833366eb1eb440e6bb1f3509d0def920d62f8669c1ef3b61a13edb8519d2d313aa4f1792dbad3255f9a1a3d0d7e1af661ace96dc4b651b322aab31aa70ad3d4d86550c336ecf3c62ed323c6619b519dd564b8cf55ba326f6a5d9938eb110a4111e4bceecb18b31a7f9cf6e98dea6c4c1093184f1513b41383ee54a7d68de6d4aa3779f25c6baf15dd2919ea8b59343d3392151cbe9c403d66ced3b66dc377c376974d1e9625cedfcea68fb984f3fac94d21a8e92de332c6711bbe16dfabe91c00c4d977efa6b5aedb3a0e6b9b66159a02d9235440cef3468ddb30ab33afbb78e37a84c21e7c00f7f6ac277219bb9ae7651e4675d6dc65b843ed6e3b9365df70a68058c65ba7617bb76f13b1eca13adb8540dbe12fbbdcb662b6c7bd31aab368eaf58cde683da358d3240fbf1ea150eb01d4dbe6b66f6f1d763d6ec31c9a76500a68b1bcb1f1e3460c6359773be86a3d4221932820d66519c75d51fb3666b7fd509ddd7679aa8230dbd1eda5a931330205dcb8adcbdc96b371e3bcbb65432a712ba9b556aeeb110ac927e0a7b711671ab6e971df0ff382c04d6356eb320dd3b09dc52da23adb89f03ec3740f7d394020be57dc1bd6f98abbe3c611d5d9bdd13434c930ecfcf42cb7874c8c862689c35024cadbed110a799c809fa6dd0f6fdccd442ed3acc50c00e2ec6519dbf6e37637ee8cb3fb036eaed356ec46317b59d6f6cdd75a1ff06a8d3151133591dbf91befe7f5801cb6b7de361167e3266a7b519dd5dcc403da116f3bebb80ebb1a7359d47c07c4aeb696cb63bedff6462eb3180f20b7b5f6b086c7ac472ddb3e5467b35b0010c376e689577f57d4f2feb86bad0ea8b99c69bdd96dccc42d6a1baab31b4d13f06ee3d51dced6cba2be5fce2c77998019bbdea8752397ed9c7587a13a7b725463df12f0b31ebefa72db1d452b661daab37aa318d53ddb8160b68d9bdbb6718bf8b3daa6797aaf112560b7e1ef661ab6655a67ef67519dd5210714b76e14b36c6fe2a87ddcd7a13a9b775188ea9de570402c63df1ebb4ef36e67edfd509ddd262f73588a38e5298e9ef69a6e404f7779cc9a976d5a1671f78d230fbf9d24e0a7613d6aa398e92b66ec759c0e01c0aec376d49dbd5ad77d9e1d519d0d336b2d67f32eca7c6c404ee70febcf6a6cb3163f0fd5d92d4d8dd90090809fe87197cb1ede466ce33cef0898470dbbdad3dc87b7a67d1caab3284665cc46310266baebc69c7dd9ce444f5b8cea2c9af6f08b096212b353128dd2719454958a59e224067b6202350cc39a97656edcdf15515d757276e703b14de38c61bc7d9c673dac519d35e2e85088eaa4498ef67ae0a87916dbe2b62dd6699e87a13a5b846e7bbb98202631373139314131413b31794331c24ebe1e3ad0e64dfcc6efd3da7a3b8f9936492353c5cdd0b3260edc98d630bb8d1afeacdebc2c268849cccd54281f57b8ac696aac31d855c594678a8c09da8909ef76a6c82ca532b3343566e2068a5ed78dd7e6ec76dca8651bd559cf761a98793b6fcc765b37ea0f6b23aab3696a0c874c92e8db28cf5a8f508865a0dda3b66d87f598bf31db465467c3ce765b8f50e8dd14e761a9d1305cc18601c0d0225b876957a161f8429da9b9678ae78e26003b0c11b1212f679891b1e12eda252f711882bbce3a0c4000029089d9bb8739e8c053ac430e3242ac90cbc6e0a04d1c6e54bb01675ecad8105e35048086cb391aea4d240037b444dc76cfbc67b03a04192283ae2186191886869468ac1bea00b3da4437bcf41a5c6c0e0020f2828c0b361cd202d10584dbeec582dda81a56185355989a247a86b993291b7ea8704372dc39a460c3dd674001c5bbcc210d3ebc6b43f16ad1b8f0881ba521ca861ea91b48364c8f760d2f030e6488811aa49dc2a60f1964d0c40f205f833cbeb84902879d2f71404a12205f0327e071f32524e6f812124ef8d21517be7425c897aedcf0a52b44be7485fbd2159c2f5de9f97f175a28a5dd97a6e85f4ac1152e147cb4f4f89b9215d597ac2cf1252b38f87f19dec80064f84a456cf1a52266f0a52a307ca9cacf97aaf02f5581e24b5556f0a52a40f852153bbe64a5862f5979f2ff25ed5db868e2c24511901f3858fe056707ef6294054e1b29bcf08cf12f3c31f8171e1bb8f06204f52f46b88b0a2f3f5e4c491ec6f403630a1fc6643e8c29010f637ac0c3989a781813150f6302c1c3985400639ac1c39874f030a6351ec694e5618cfcc31861e1618cc0f03046060063a400a89cffc779520e6ffd70e10546092c1e4689153c8c1235781825d6781825e0781825f0789825567818233d3c8c91ef5fa2704005d20a28905e5678e105484905cba6be654380b76c7c2c1bfc960d02deb2b1f2960d136fd974e02d1b2edeb219c3b2b1c15b366ebc6593e52d1b26fcff0b301fc0e30917171688788b0527de62a18ab75828c15b2c88f1160b36b0218517165ecca8e35fce60e15fce18f22f6714f99733b67f3923e75fce08fa9733f019e6bf9ca1807f392302ff728605fee50c0efccb1922f89733b838238cff5781051580ac00e58e7f81f2847f89b2c2bf44b1fe250a0eff12a5c8bf44f9fe254a927f89a2e45fa2f8fc4b9403fc4b94d4bf4441c0bf4471c0bf4441e25fa23cf12f5138f02f5142d0c20a302cb80024e86180f8f03040500f03a43f0c10211e0688110f038489870132c5c300b1e2618068f13040ca78182039781820693c0c10223c0c90253c4c0b290481c18af62f5674fec50afe172b3ffc8b9505fc8059c014300b38c1ff0359e105c8917f01c2e44b18b8e35f8038f9172053fe05c808a4e8851f16cc8b1c35ff22c7917f91c3e75fe4a0f202c4fc173950ff220702fe458e2bff224706fe458e12fc8b1c63fc8b1c36f8ff167ef850e3617c74e0617cc4f1303eea781814480f83820bff6fb9c0c8b4f03032383c8ccc36c30b761ee605a78779c1ca85172d8efc8b163a2f2730c1bf6841e55fb418fa172dfabf68c180172d28f02f5a48f1ff2b6041bd0b1620de054b10ef82c501ef824502ef82050a00acf083851f2bbc2094e45d847ade4588cabb088def22c4df454888771122e25d8428f02e4216701102c1bb0879f12e4263bc8b500dde45488dff878191c18d7fcb0d98b7dc10c05b6e686fb9b1e4ad2142b801c45b6e547109c00b005c5c28cde002c300e8c0e3ad2c32bc9565c85b5992bc9505e8ad2c446f6519e2ad2c54bc95a58bb7b29cf156961dbc95258eb7b238e15dae68e35daec862d511e4ad3a7678ab0eefad3aa4bc5547f9961b52deaa8388b7eaa0e2ad3ab278ab8e30deaa23076fd5b184b7ecc0e1612c27f278eb091dde7a627beb899eb79ec0ff2f030c961c10784b0e29de92e3046fc961c65b7200c125c891b782ecbc1544ca5b41c4b782f4b78210f15610265e0000c40520a520330ce1796b48f8d690f3ad2142bc3524026f0d99e2ad2120786b08176f0d79c1c3c4618535e4066f0d81e3ad2176bc25d3e34b53f4b76462784b66c85b32dc5b323d6fc9906fc934e04b4674e04b46f8e04b47b47ce9081bbe7404922f1d01e54b4748e088107cc90804fc3f17809f41080c3bb4d0420b2fa00003248b77313ac19732e0c2bb1885f12e463778172335dec5088e77314ac2bb1ca9f02e4730bccbd1f52e4744dee5a87b972325ef72e4e45d8ea2bccb117f972306bccb919577395a22880b174dde858ba277e1e2008f83101a6ea8018701e880c307b078eb035bbcf5011dbcf50124bc7505cc5b57d8b7aed879eb0ad45b5740e0ad2baa78eb0a31deba228db7aec8e32d2c02f0161649dec2e2006f61c180b7b088e22d2cbcf87f00c0008038acc883fb92141578983c940071c182e2c9bf4071fa17288cfe050a22fe058a26fe050a0ffc3f0b405c70f9165ee2381f268e201e260e073c4c1c13789836a088e38938ac78172c547898384cf02e421c789838c078983868e002232486172c33b0b7ccb879cb0c03bc65c6d05b66acde3283026f994182b7cc788165461a6f99618487898306ff0f038036621ea60d240fd30694876903f5306d183d4c1b463c4c1b503c4c1b1d7898364a0080162cb10e1658c8ff569ee1adacc35bd9be958d7849fe6bb8fe5f70f957e10789052d5801a0838a77a1038b77a1c38b77a14306ef424711de250b0a5f9a22045f9a62045f9aa28b2f4d61c6ffb7b80c49f22e98e95db025ef8201b5e0c28f4fe17df4b880172630a53a363a423a028b9db0c41dadfd86255c81e5031860a28b981e2cf4d8b9a20893b6b9cb178cb689c29404983ed0d020b14172cd1c6952261df78ec909ea938e7be7baaecbf40101d79126c3d4125e50060a5c1881c466dc250a56901cd9e13277d922822862f8f7e1430a3c48bbbe9020b932da7fb0cbc6071a5598e02a5531c4ff65324dc0087bd58ce5ae7b48ef1e092875608cef214827e7e6e726e73a721d69b207a6ea800edfd15ae4c4a9227ba3a958e4dfc70b3de6f0e1c6fb58e37dece00c94f7e740f7af9393f5cc64ba45766c20ff6726d32dba504ca6ac48eb6450fe7db890c993a7a88e51ff3e5a4865297c34756cfe7d0061818fa64e5fc1c6dc3b4798e7dfc78f201b73efec14eec13c7c34757ca8b053d82c4bd5929dc23dbb2c553c4a89b712de936e9e94f7f4bd971065bea337be404aa6c813a4e003051f3e3e5a7a907cfc7f8b1636475a9e8053ac5bf270021374139126e7c93ad9718850be6485a3cd9df61d85679e79144d861be5a88e734c92e794c4d13aa97ca449f389de363334372b4b554f2f553d931c4a7a93e2e6d6296e9a1815b451ce9392e613701791437ba7c46426b5aa00bb9b9b248356e60d4ee1aee2e11699240f7a9b4fca52b504e3f0a052c44d82208152949f1c359ff020bd4b12f31e90f3a013a341e6937da636ba9b5829574a264593b249e1a474523c29bd6f5d925174494629cb9bd1a72c6f744946b13155a3c97b744e9a6fa870d6a3a269f3c9d10e17edb57ea1b5b603410f0c805b779c3762a3e669fc5d0deb4105d8edac61d1d3d9f634cb79d85ba2c705c04fdc99f6468dcb324d13b1acf70885688f29c08b357cb96eefcc4a016e3adb30b7b51db9f1dabba300330ddf4cd430cc6537db30cf022077c79cc5bc3d9ce9eecb2a008a7b13bd0e6b9cc7fb699c8502dc1773e397339db72c7a1b7e02f42e1e3571db302ec3b04ddc4e80db3662e3b869df98719bf735016e77cbbc31e36ce4f4a7651e13e0dd5d676fd679da8a9cc5b82540fb598c456f1339eecb3adc29018af6f3eed572368ed966fb26018adc78f71dbf2cd3b4d6444b0150bcdd27ee8c615d8771fa8e57408d715c87655eb7477cf5b79100bdacfbb86fccbcec759ea7f50440cc8a1a8b1a87e5eee3b2cf4a00fc2ebed65a716b796b5f170150dc2287f3cd3026defb89fa0850d4585b6de3b80ee3ac683123400e1b37f665f7e2adfb382e5b01334dc374de2366adc759d15601b16e674f7f9887ad8879f82b02c45ae65d2c67e3c5ba6b3f08801bd76d57143b4eeb72e789d803c0cddb306cb39edd7afbecb64480dc8745dc8dd8f52ee665de760078b7763d4ecc324cfc34c7650d00359ce16deb3a6b77a6715b6600683d31cb448ec37ad438775d8f21c06ce33c5114396cf5c88ddbf60450ebb6e6b6cc66f66ebc6d9607b8610ddbae1e394e146dc69c13c04e771897618cedecbb22c6788059e6b2edf3f4a76f367e1c4eb192f43601dc5e763127e68d1335718fd8d6310114b30f5b6cb41be6b4b6e1b1d6ba04f08a765f0edbe2f5f4a78977d3ec15d21de078adc77d571cc58dd3aec66addee1324258099c7e171d4d613b1fce10d7fa1d56c12404f7f19c7ddab3f4ccb79cba691cfc7d9017e9ede30ad73f7693bdeddd5fa8516b3a4294875801d96e94f6f9be6b00ddb31a33a9be2f066aad0de24a42ca01eb5d65fb779581433a78dd90f243a406fc3348b3b2eebc611e72e4b39945a77ca8b6a459190007a98e5aec6f29669d9375ebc95a43940cc665914f7a82fde3e4cb34700efd563866d6fdc6cc6f4dd360a90e4003dafdbd91377de32ed719d566bad5e68b7be1901c4b2abed2b7ed867b911fbec8f9398989f9d18ae6f768b360e101bb56839ad356ef3f467b1ad08a0d661b7ebec376a160e507f57f4b8b1d39cc8619ca70561363d493463d6442202288ee2b72fce5bdc23b6a34675f60d306ff9629aee3e2ebba2b679deb540df16399a768eeb110a3d921ba0c689d96edbc5f6b8338d1bc3b04728aca421801af6c40edfec1bb5eddb6e568e9b10408ceb446e634c6759e78d1743757688975913521b60966de3c7d9adcb2e1ebb3dc4778686f8cec97b6a1de2a5c606e87999f865d8c398a6bb8d33aab318c6d971e37a84c218d21a20f672b669d9de4411672cf3509d0d0288899ea7bdab75a238da6ed3509dd59805da4a6aad6126d3932ccb7d53eb36c971c4b5d6ca6d97c84a415203bc99f5462ecbf0d623e7891bd5d98d6215109d5b727348698058de78d4f0366e3adb8c392080d6db3eabedd1bb19d5d91d04f97596bb47980dadc52cb75d22ed0720f6f697799bd5442cefcb89a8ce7e3e00b12dda0ddfdbe111c35d86cb122fb1d608a3d9d70330d35896f7988dde96efed2c50ad970720c6e1f1e6ab711ef77d9fa671df0ec04dfcc64db35b1cf588653c769e112434c0bbe96db3cd36e671f87ab90bed8aa4033073fb665c763bbcc50f7f370760c75d6c6f567b62b6c54dd41b12241c805a5f8b8d3b13c7318f76b43700f7bd98e6b44c671c1eb3e6509d253be9d95e92a90a09c906e0e65d0e8bdede5dc6ddd88db59a9c4c6b9077c6dc97bbed6e2f09120dc00deb8d7dd9d319d769e2276a1b11090b988dbad37a13c56d73a2edb20648330033317f9bdbe2e5b4a67ddaadb57aa1e5523bedab2b03d0fbb2ce469b7d1b13f786d91e31eb362c06e0e761fce53d5e7cb7e6b0a7b52eb40a20c100ecb4b7dd9cddbc35eeeaef03827406e8691ab77d1cb6f3367ea3d79901daadbd0fdb4d679c88ddfda9529c02d59a3b99aab556cc86367732a5711c08482f0047bc715b86c54e8bf766dfaeb57ed811a432c0cc13c711c376f6ba9d619d0b404d5b6cc332bc619be5b09cf1d1d4f19820b500ccf4a6bb2edb36517f4d90c800f1277e3bdbe2febc2e6f1b461a03bcdee636eed372b6d8c55029fed150293eb5557ac500bd9b8956fbba4ee4c6d1e3a3d957f8a7d65acf5590175a9204482c0073367e1fb775b8db3e6fdbcfda90c200b18fb35f67bb3d66ec13b30cd5d96b3d30402bdeec7e5af6655b96bd8f3b39aaa4d693a35aa358c56408d20ac00c1b31bc69dbc5c6bcb9bda900dc3713b34fc4326de3fd69390560d6bc2ee7717f59b76d98dd43d90ce90bf08ef87e633676f9eb36c76da8ce0ed95be22c88e4057837edc3d7769c68519d1d22c5cc5e6b6d17a0cdf9b3a3b875198f1ca7711d27c3b36f3d4221111217e0d87977e7cc6ae3b558bedd46b5982026a7a953eb16e0e879d9d31adbd88e7acb32cefd7a84c2204828003b8e13477c3b6f13bb6d314c0b50dcf9e6b1137797655a77c5138058db5a5b2fd3f2d69b134554673f9d989c98a0144797e47d02d51af4f50885a407904c006698b7e54de3b29b89d96e3647e38eb85190e7f5088545a412805e863fcd7979d470fe38ed61941613c42406adb57a3d42214a2201983b8bf1f86558db70a63d8bea6c68af97d911805fc6e5cb7d167319c66d8ba56c3d42a110290b10fbc64dfcacb839dd6d2b8a241180d713edfdf2bd23b6ed2dd3301b5a0f03a41080da9875d9e765dcb5196fbb1bb79334e38a45800402507b9fc5701ef368bdad7f6a45f9596bf86d687a52216101669e88b371b459ec3c2d8a17d5599e865bf36c82b5d630dcbe7009d215a0c87538d336ce6676e4446c247d00c462e7d9fb699dfe70cec48cea6c8aa3a3566bde457affd41a13b41393e2e8925aaf0d770aa3a3e6019207408ed3b277f586650fe372d6d55aeb104e6b45b7e6ac00bbed7dbacbf0f7615a632cab02dcbc8ed33c71c49d15eda6454d87d401d0e3c6edeb324ddf3de22e7b1c00c5ebdd3b627a7f7ae3bc9d6d00ecb2cfd332c7ed6c452d7a9e06c04f14bf9bc531e346cf5a4dcc00a86d1bd6c48be96dbb9df76d0c805ae674663b316b59db5987a900eda65dec6a797fdbcbece52e006a19b7b7c76d9c16f715ad87ea2c103b4cf31613c424060810d75a6bad5e8f50a8224d016a9ea7452cb3f6db468bf2225c8ee11059969bbb4e34cbd314fda40047bdbd2ebb168b1a1b39320c5314887e6d4c10938e34015214e0c6692d5b9ced311bf5ce2eaab3a52a95952a9e5a6bad55e354b5d65aab677b8442520d481600f5a8c7afb336636cfb3286519d25ba46fcdca4902a006e3a6b9a98e9dc7976fbf4c753cf6ea0a671aaacc424ba3ba7cd6e0a090a505fac8d19c39add57771a4675f6888b2151c6a5f8ed3215559deb110a65484f80f7c3ecf76d9dddb066f3b6a13a8b5d200ec525ce9d23da25d6bb085565cef608853c242740acd3c4adbbee6e6fb39ff6d55a2ba909b0cb3c4fdb32c6306cfb6cde9800bff1cbf2fe366cd344ac775e02cc59c656ef0dbbdc68b36ceb110a9d909400b576f1b765a3eeaeb6eddce1f8404a02c4aeceb22f8f16dbc47b450a801b6b4fe43c71eb9bb12c9347bb09ba8df8ce014857403c6edfb61b87e12cdbdabe1a4742021cc7b1cbf6ebb4b77dddd65954678db699a11b084d81acedd0b47fa40980e3d77d58b656bc9bdef4385a4c9200c8d96dfc3aef5e6dd45be761a9d0dc65675727cf9bd59b2a262846dc5431dfdda4088099bba3bd63b6a2c61bb73d8c3a02c4f2b55bec2eb66d18cb1a1613c424e648eb0c99d849adb5f693dbae4728d42423c0cbe9ceb356f334bd656c7f9b15f0669ccdd8a7b9aebb5967fc91aa801ad6a3cd189739dc65fa5a8ba69d540478bfeccbbe4ed3b48ccbb096a1bab093d77e240880ded8899c873f3d6217eb380dd5592edca98d3a313ab4d96bbb8df25c00d203400f735df76d1dbe18d6d9ebdded928800b34edbb0ac5d9d759bc657233900d4dcd865d8f8755a976f8737aab3611686432a716bee921a006e6bef883fac751eb7332da33acb53930c354e156a56233100cc1a8f1a877998b559b63beb5d9b9186003511639b77f1d6f076add6a13abba51bc8f316006a4fdc5da7699d368adb873f0580dbf8692b5e716b1d76bd0c4b00a87959be566f98dbb277b52804988d632766a3b7e9ad69b7c30800b5ad317cb54c7b3a7b9bdb518200b59779a3b7e18d6dddf669df8dc63fb57a5c8f5038b40247bcd92b6eccea2f7f59ee8e40ce6a4c77d9e33e6c14b1678d402d8aa3deb0b1d3aee8755944757685cb6c2b02bd2d8b7b6bb613c5ed6d91fb3015b85d6ecb9a1eb1cdb40e1bbdcca6a0e86119fb3a9c75cee266bf8ceb110a7de8e0eeb2cc66ddb6a3b8652b76b596e5ceb56e14b5b7829f1eb9eecb1bfe384fd4ee4ea9cc2c0402fc6eb7bd4ebc223676d9a7e91d04d99fd4cad11d84c9cd5a532a93eb110ac513b4e29761bbb9acb3df77f1b7e570d0cb63abcaa8b453a9853dc9143222000060d30000f31000303024188cc6e311995834a1c60f1400025b9a5eb6704497a85994530819430c01100000000000009360427b483ab5f902c31879a24f61bd0c72c9c9e09789d8ea29b55ec7c0eb9834a663388ea7e35d1803170bfd03ad702e4958c304abb68cab920e64b0bf638c9663c578f063d5a9520ce82e863df4d9f7f69af55698f74a58f5dc64d3db6bb2b7c2a057c2b0e762df4363d65b61de2b61d573b1e9a131d95b99416faf616f857daf8459cfc5bc87c6aab7329bde5e93bd1506bd12863d17fb1e1ab3de0af35ee9aa3d1723b087c6a8aa7a75b12eb184292097ad6b285092a274837eb9315757742034f0a8c1dd4c9f689b485dc61a587e27c0ff81ed321023dcb50113bfde780ee2a70279614c727342fc422c81072437e8d1025785fb7ed18bde1cf5c72971d85c22937d6286b469a4dc08224a1a7cc48f63b5983ef21f85077a110e9cee20aa02c0d89067ccb3c6ba0dfba46a241cbaf4be5d89996dce8576408ef0ffb216516bdbdbdc674e2f94df1ce7e3350d345a5999300a5a3f145fa4322f2144123358c2040dd60dd109e587177cc04849a38bb9525a076153e25d6e27b0bbf898ba362398badac386777f4afebbc0dfdc55ac7b3c4056070882f820f498cc749a05767b612b245b4263f2d82bed41228a1dc9130074cd30bcc0f791e9da3f9f94615fb169108f1f95c5b9f570e98cc3a0d1c57f9aafc63e05a03c86bc3f7114a5e3fc056890ef11e6f24a556219ac7a3afc1a6632e10b0fa560d4922c7af45dda3ae0a7005d6603deb42d6d7f22a877e316513bda4ca6381d2755155dc072990ca72e858d23aaf14cbcc53905f094ac7e690a874ebbbfa3cf64669d5362886593f626757ca2b3b5eabc9d9736e0364a023f1c93ee51167a1b026620996f912b57da7914fe700761c6497cd45de9eab057bddeb5d66f3d203d031886b541ed95800bf5c3b5fd024a3932c5cc488ea2a6531a00a66057ca0b9bf418e19bddffcc9b992472622dcba5bdf4502f1963cd1cc3451f58ecd539c39ae1d265f408d91d8999ededc5cba754380a8032c7ba98f565dfb0601de8c684b16b6229d5c540fbb969c2d77c03759291975af6839ae892d26b042e3ec342d85ffa91396cd5dbc6ef93c20d40038c3933c4b91baa42ca327243bd62442b4da4213a09557b37ed7ebf0dbdb48cd8c558b99f7e93317246f12bc969bf59812e20e5b3964af086de529d2e84794ada6e6082cfc25077bad2f5c66eda41a462bf5175bd1896eca61c8c7864bd3ce1c98614fca2614602ddb47b91c1995f7a17b35f2e837660a17a2aff715232493557327058f9c55c4d5c01b90f220b0dfe96174fb4832c61c8f7ff51b2958c9764ffe15f8287e06d9867bb4e3cf50f92710bc844f60469b060774969e27ce4c8d233c450d70943b31ffe8fe94c197d5d6a6b4c78c0cd1e61ca629b55ce6600cfb72002ac44ae98de7f4b4f1085c90b84a768614901096892195db45258acba971ad70a9d1be5449d9ac556765681752b7a2430af5c67423b2ac5627bd1a87e7a034bc2fbc9270af4393c7116b8134c4703c0267d686413ea5455d136fcc87612cef23326df42225e1d6c7121dec15f4a5d31122ef2f78fef5906eb14acc2ec30192e52f90193140444612d57f62698386944925af8e671098bb440d3bde4628a345c89fc5f745802314478777f31bca41537774b4575324908744f452ae90dfd55d2e93650b4aba76a8ae81683c9de3b0c4ab05303d2fae59eeb4936fc0b6525b8f44d6d0338701c60bdd700feab178e04ebf805aca3534d741dde16170f7fdba63d623dfb6ecf1b24840168cf6b52369a89c2450d5519d51257bc138acd4d9a4ce629f51af6fca11302f16307e1726f98bc0279924fdc2134c6de16fb041f09b1a5f68b206b47101a65cc117788cd2c55ab62e1d9fdbd5df5e413a4e984eb91120d7aedc7dc987f53174ed71b22ab797d37da94f0ec04ad8facc978e67e7c70bcc1677fc060c7841a43516d0ed24a346d22c21a00249cd9d3125e453e6bd3a05354b5f47eaf5e363ae2e72f835b56e5148e748bc3c0579388ce2e4a47989895a8f0e696cd054fabdf61702bb11b72d0206bc913e02f0afdb187dfa68c41341a557810fe595b8495ad02a72c40de20bffc3b8092374ac2a16316e6a2fdf6842383011a7dcaa649c58c1680bd9ee8faf32b2c123255f7c50680ae72c55540472ba2aff4d14b6ae8ae36151d7d8ac2111a8e8884147477f66a4523eb44c69d5097f04729d3b3a0055182ab20fdcf5f22ada6c5965ceb4fa536b8c1effacc6ca54591b47a73091e0a286f49a9b9a1192d843ea962fe4def275a7fc9cfd211561ee6c97040d22524b076fe9a74f32dec8e192be897ac0914ddbeec62f89cac5de80a3e267bc3b9a1c95c25c62f5d78abdfdf858e3d44c053a56a46315eb818b97addcd5bcab8da1c3afa0d70f4354b48dcc4790217992d2b162f5d44db9cad41a1bc540ff1e80a72d3a923609d8e9218ed2e70c821b5532188d8d12b3e66bd8bab698a526ff0e4e04542bfae0e728ab4b3e3faf1214ea4d82e4d9d095ac5b38fa25079a2deba11c84055fbaa0e3a88bd357ebb1c89ac4bfdd3e73729c9a71e05edeb62ca01e6988f1206968f5af4edb3c98678b82e98a2ab47a123e3f947d3841810742eb2b6e4605fe7283c4e337baa2f972f73345d1eb4ad182872e2b35864fa0bbd06727225727c7c7c48b7a22e2a41f519fa5e90539fff384f021265280f4da1c047216d64f477270c7bfde8392338fd2d1d3f2a2a918fed228bb6888c2aec05b919702da2f55b40f5496a11280d0340f9cf27598385cd9c2974913b8e4df500397d60547a7614bd142b2dda117e778861e601948558bd976cf1707a0446aa73ec37a5bf0108acee0a35aa48d3a9c73235049a402fd5a11d4d23ee3af263c8f78e26baf2d3e4c0d91011cdb52d93e02eac8ac737ee9f7c2e67ebfdcccdc216a6f45afa5d601258eca5c2aa9b3a44bbe48207deec4996c7fc808d597dec588c067e486c8d2aeade98e0591a2685565d465d654b9a6651741272c5375f1c53ad14fcbdc94d0313282294a58d5c06b4e059fe29870bfc27daad52dfeadd64d968039165e5c17efe7bea248dbfbec6de31419a42d892e9f5af5cddb70b7928bd4df1bf685fcab217cd52df403e767806d77bec8f0a727578050890ec86d16affc0acb441a9f18e28c082dd8b3b56e24f9351706452b8c03f9a470079376794f8d449017ba1ea9052a03cd79f66ed5deef8912269ec2698d4fe413a38791460bf27297103b0c450c635db5e37eed6efc01cb695e6900824c78f004ef57e11e74314b341ddce4944a3d19fc19a35990e17a25f52555f5a927d916134e5f154c0d7f5864d5fdee26c4b2729cbe2f4ef16bcdd120b45ed7112b400ffffe705abf5dab50e22c692d9a415ef9793b99f0eea86535f9fa112d868d37cca12ac0d845f160db3643e2fc51a4c10e5e49ac3a5d53fb05e34bdc83f78969ecabb1e8051d94aa9a73830455bb541cab808df4a37b20be70c15939cfc42bfb064a6a8fb078430d9012ee24d574f6db8ba3e8755aeaddc2e86aa8eb9065003a82ae290518240f02613e0ee3e58cd10239cd76f9c4177d38679a9fdf476580af84fafd34e4c95f326b086997b6e0d1e01d904ae916ce809bae2de19a71b47856d6da08dfc7e7ce7f8b64024a4bf2437aee6aead52f5ce59ed06293fd74c8a868fe0af118e23543fb79693a8df54bf06bbf01dacb713f8424b570919cbc07262ccfd894763889e39458eff31d2a41bde693a4071c6188ee13e8e12f01dc4cb5cb98fd11337ff60b4c25394014b64c49195979e33b5cc1470c81b96cd860b8468468bff47be850fe707804082163eae47dc481c4c0d3c12219a106f33366ea9ceccb23740017aa74957ca1fdcde196cf2845917582b6612d067a0d55066ec26346a43220ddbe7eb3591c41339e0c72416ea83b6b8120397b82a5388e652822d01504333cb4414b901093c6494511a33ac603d955c374301ca3a1701ff690f43da3aa76e717db08032ce97f129ff0d9788c93317a3c53104b211d7e02219cd7c0097f01c46c90404584f6044f27d88e840d63157eff3148b3a512124651a063bb08572e424610ca4afadb8bffb5a8083adfe3aa0aa3200bc8a289dbcdad406b92784635ad672c4c704f18e3d89154091648052ca7a39b909e0e60b97d61ba73311a8d8bcca26c2a90ffcab06e901d0a2b8d00978a3a1b7e728089dfccf210b401ebcda67185fd417fd602d95c2e22d1c43a605303466b290dfe015a5317de41d5d4f5b4bc5715122ed619cfb34d8fced23a3c005d1ff9340783809dc6feee2a164aeda0f39a37d7cf53e95a829206d7f7bc394e3482ad4bbb222bd88fdff8d717b53c769f4b76fd1bdc5355e8dd5bac33d018e9b5227cf12708c9fdc110323d3ca857614bffb15f8adf2771e69ec0f5820aedbf8aae73758261e81b784fb6e30fc291513deb32a96eae5f80d8e188c338cc067d257d7f077b4eaf05ddcb4dc98ea3c44a3ff058af592a01feea7e7096186f77e1962c2bacba52e57b6f430e0c4760f1d9effc4ab13c1cd1498ff65035a630648c245053e064dbd75f6597d62fc27cdcb14fff1f1ed5d7dc11fb83195f095bcde2f83c36017fff2c016ea8ce4255dff94b7fabbb6df5a32593c50ea3bc7a19fccb3939d64907f83fda9ff235e181ea5f5d8f2c6181875f74b696feafd624c03b6310c19a496f46b25e93f1a9dad7c2f5669beb52807747c8283f753bf7afca1303e89fadb4e8f4d37d887d127ba4ff1e0cd79f0df9fe0fc49abbdcebeebc7f64c052f9b3877fb26e337af1f762f43b92b79b2b464ac3da4e32f8add45b73a1a91cc0d71222f0c4e2a57d7e215e07f356c6be72d4caa4efff8f27fa9b9aca4b737b7895b3e78a1f73554f3b36e3d58c2c43d452a9be26506cec4bae3e78b221c3d8e7c8e3385562b727efa5fa3efcf05dbfda689d90601e4fba6f1e90533a2ae9ac3697644732d6ffe0fc01bac26b3b0fabfc6d92749c7f5e1726a3d6ecac3e4f75dd6f3260dcc7fbce425b7ff5fee8ae3dadefdcb74387aeaf72c79ff05ded1f954dbdd083c153d15d4973baee95d45014af3a5c3dd1fb59b9adf24f8a7298cf75771b8b00794a4b5f9c0b17843d9a10f83ef5c7ae82c8e7e3fc8a5cfc7587e37703955c10dbdbd04d1ce1d7ba201be8f20927ef11db87a2b126af8b34b2c8a0fd18bccb1fb157a619daf7a80337793af7f99185f95fa9f540f7639d6dec1cdd196f05cee81b5fcb2bbc4bec0fcbdbbe25bdee38bee9a977e8352a450b16f6c4c05842272983aa696ad8bae93de045c723bdd69cb15bb1a4f415c078637815e1bfab3ac35d2ad06023d6489c2671dd5ee3ac002efdad67e311ceea7951cf2f865d6c84c32ddfd44e0277149e0d7f8eee21413abdd3fc549119f0afe70f063f0f86fdf32efe1dbc498c8b6390182d107c412dfefa2285f48a8a1dec6519011d97091d957d42ea24c34a746f4191139352e6c65e5f290196777b7b380bf0d4db5937bfaf632b04b0f62d0dfda27c2a3408e648616975d8b437e30f825f28e0482fdef8c77c5a8b77743f0e7ac1c646007de5dbe4e790a004a6f2596fdc19ab84fe811f82487c3796de511fe24f886f9953858f7bbfaf82cb95f98d97ca359e4070f68cb2cebbc344677d11cb2307a8fcc8c93e32181261671ec6945699fb57d04ec0e91b0891045f655991f01fd2013ddf636834eccd4fdb50d114d2c443f3e935faf201266ff7864cfdc4611ff0cca17eef577c1cd175e9f3266abf96c8102153f1dabd59ed8f55f3f69fccd609e04dcbd184e92cdebe3d2b0a8cdc1b8ad4451c3413afc08ccb8ff49fa84fa5b1c897f7fea24915ec08a175f6cd6a020ca29d69f362537c84db285a7edfc79f4091073057045c8390e0f24c8ab8a45ef27f9a14e5a7865c43272e53e23d455eb615cac26384ce92c0e908c67e9088ffb55d6d4a46c38b5e2a33d0d3cb46f3b0460f80d392b6c6dd4db413dc2c1d7cd362780d9863e992f3efbf51e38b9ec319ef62a3f236b928ae4d8d692ba38358a2d24a3765d20117c0fbf42f382ac3aa0e794c51a4758fe16dbe2784ba1b5226591da39d89a04e8ce51c7e74e13e28abddb167de5a670051814cbcbab0210f52b627785eb222c86fca957f03fc3e8108e3dbc2cf4bec7440839465af06a02e8a4b2c0b22e2cb3bd45291b38a8d2db50cb17961fb0fce4380fce3a057d87fcb2438a0b3f4d411709a670355fd40e2565a35b80013a9a4e601d6f353e9c0a6796c2c764f8b990e98202cfefcf119857e1e31b38b4f5bfc9a8df376faf422dd919e15b78cdb3cf4387e7011249277d79568fc361157911074ca4dd9b68a05a5e503dbeb3775da0708a49599ef7c7d8c5dd165d0a880757b0044249e135ec8fc22264d341ce464da2f35e4356e4857e53debe5dbe4d225486052da4d51287a8fc63273b5a6bad8a878dd9d9561c0f6106c359083b8e64ccfc01c28d28072c0f4b857152ac3721683c3780f04fc2666c33edcb663deba4b00c1192fe8cfa1720aa95750e933baf158ebfb8db3bb4f17f8d2dad54c07d3f753b2fd3c619970c17d740f992271ac9ba274f1b3af474daaeb825286ec189ff491e0d100cead2e052a88add83e4f731129be6261afb467282ec3d5eaf574a6c4ef51594786f856da86be3ec3e599e272ea8cd8c001e0ed8c14ec84feb19987173aec2ab2519c21865c48ea857b8f73118ff15d3d05cfbedf4e567d8851a5c93949d2bb568b60b749c66c3192a722ddab737ba774efbc4c804e126d48ce483aa889d767e3764d25242330bc38da7b6817e707c56881cf40c619bbea781e1a56001c30e28f55f7015f28b91a92b93848c732b2f2c53c6820af94db83d38969743093053f2a576a30d837ae40dd6ca2edfb5b0ae6fa211d83a2f1d767608e49048486d620f21f2f922e68b000762654bd6a3b774597cbad79b62a3d69903c269b569ae138d8fe8cb0eaf650e53d546f73f070b4a3e8090198c0bb035c926e35bda4181af9ddabb9676f339802da57c2de70c0144a22e126082dfef1e91420373d261207f6f7414fcb5316a55cab1161c0482c6dd00f07a2d2b430c81f6ff912ffd9db81dc607a893c583c8ca2170b92a43e95a5f2e9771df8236fe820f3f48a5889923fec09b10c398079c98f8ab043bb8fc8d274761bd3c2c616089a7bb6cee20436deeb48d66c1cb06d813cc6efa8921b6c32f2910faa50cbd7a9808efe3e9238deab9653766ad29b9c7f89e8811259b56d2e7589d5a5cbbaa04ab8313a91293074f8dc23b58f46b2a6d6a576673ef3bf2e2445286f0b8fd36008e4ce724d1b90d647506c7ca249d4fe26f0f0aa3866f699b74e19558bce8e2f7c07640d1d34f3a91db72573790d2f37d08277ffb17b621c44acc63cf386059fd18e4e33d0ff48436ff0d8a4f68fca2b317c2cfa49efd48ae0f5e75494153cb816307fff847107bb197ad985deabe6b531292def44d0c3a977ee7537b44e31f4db31dc9df232058fb3741d6a7017b8ec643439e2229bf364d2402a1846fa3e6e5358da6805aee00f7659d3b4d43322dfe671ff81d51f11fa4a783b9b39889821815f15a2d3b2f512798e3be51eeecb7275b25137fcecdadeaa02d2c6c3349472d8e19560a25e7aa01107693c3748013fe94245dcb2e87572c02d5f608cb18b83213054353f6ffc1a8831f8236511107d4ade54e74de32f24a39a82d80c2991d7a7b9dc24266a41bdaee54a4316aa4208a01175e980e1f6b8e8274d67a2d10024ae1317cd0d97fbd214ab395d4aecae81028eacf9edd72bc374f24695d1c452319cfc3ffc232a9051f9e65d77b8952857b6abc6df29445f3797301131637fb0ac240bccda4778c7ed9aea8866a45847b387c292e9b1acdbb4bd8a16a4fa158a0183dfe6c4e6160b400ed493d428af089014686b46383615a529dd487676ef450d80ec3580e5c4a59985a4d697f0b7e18ed045b5843184a20698f1b90c432b9301a89d9885f2fc7b516ac21873292242d8131e788b8203856611b2fe3ed697fd2f4f25821eb9d889dcfe7a97e4ed024ecbd5e93416e81f7770bc7b3ce9d419bf1406388ea6dd5c9df5102823ba7c3539a24737a8f53f67616082c51dfd611a4588d3603455d84001661e6d027a4690173125e24bdca8ce12b081ec61c2bc9d4b2f5c0903cc0fe4ee63b97dbc332fbe662d77c3d19851e930a11607d41738e375770b543657b1a5bdc0bcb3bf0089d1c5dd7ec432f1e2cbddfd4c6c809e11ae64568f61895dfdcb8a74f692ea3adce6274d26173bdfbba0d0db8302f41890db0f7e4d2c633292c23d9c19947f7725c759dd90365e17855a1c22d4b14db3e71710c94d9fd5eaaed71d12d4dea8e5645144b000eef869c63e7cb4f4c2be34efa3b3a5f15b521140de6cf624a8c7b66dba89ff7b710fe99483dcfb5b8f1d21f452a884d4d93098b7f54a742d117f59209f7d73af983faacca7cdaf88e71f0f9374c8f0dfb43adb907b4ebce8bf0bdebf47e5561353020fad2898eb8ad0b91188db0244bd3e2148eed13ca18490de40691ed41bd4ce1c7388a4eb7e2c80828c23a7fd92e05ba2bcf8b519902477e51671a284d6707c249001f1d12a17b240618e03d3d0c380b6458be2d566ea295f39bacbb33c0dbf1d3dc75720cb733672399ab672c342ed00913af8b0f342efe9d9dd91199fe96920b240804e1a3061c1419505200101e31e8688101920088f40103470a0e2a0100240f1a75a06010090145f0c088834507900008c2478de41d058e810ca18fb23fd8ceef1ae7cb0704d1c7063b173850a200103c31d0690103120188e803034e0a1c2a0100441e1aeaa0c010090245e0c11087850e900008824f0d702864a04401207862a0d30206240210d107069c1438540200883c34d441812112048ac083210e0b1d200110049f1ae0740cbbecaa65056e73b689d116c06c66b13970adc4da20d544a21670c8b0faa2f9be95332309c21cd3770c7263e5d3a4042e5cfe6f9327d51a50da2161f3223b7d5f6f26328226e26f59e25509e7971b3c38fad0fa8eca32912c418eef3776f9b0f2e92402260cbf37c99f5acd29e790b0f390193daf361279c112ff9f2cf15681639769ca46aaa2284fa8ee9f7c4e9d341931b5b9241f2a0a34f6df2d7852e55469200d638f6b763d46d5e80638797d3060a0719d634efbe694042e74eef03041004c223e091ea1dfc6b44291ce461eb06a3d3d31003a1aff452dfe999b889611a62f3b7a12950d8910c7d1a62f8b630130bc8df04e5f1e502f3f023749ac7c561efc302915e98da3233f5f38898ba8ffcbe30283283c15826eee0f1079aa4e575efd0f8392ef5c11d8cb6cac58888693a18a103fff3c7f665e61c3f3c50960d2f65f310419bb3ef19da33423fdf427f7c759962d3f1726ae90d44899ba150ab9869bca363a7b9d8dcb718d8ef7aa68d04fe565003cd7b982859e9af3d643f6ffc2f7a19a77341d7a77ab0c0beda44a40f13bc8e0921305f2f3d03a5937c7cad41d27252b5832fc87b29875e472f72fb3b302c3f5986629553a8691eaa6fbbc5b879a8eaf0b07437159dfcba908561de40ec6569d48401527ee813c50add2033cc2265b7fbf1f229b1ef72f9925025dba36de48b9210408ff9bcf9560a212d1115405a2ac8a9486528ca0a91b5b2f6174f13ae72582200111b6b3c3b2d321df77199c0b3974ff62045287952b9bb792e17c0156cd94d0b396af46939ad3fae501fec238e29760a809dd0e9cb913195670adbb5ed15b86ee88dcd28267826f629f496f5c1f860cdf471001d7795514a5a58475c26bcdb5cd087b87544fd6e33ed28d1431e9fcb4bbe2b9c481a718a22e6282f0d70630f21b9e0b174bc1fd4ba1da9d1c96c16682f074764c5342ddce8502082625880f2626b835bf0687d3e1f946787b0b7a93f20af7d12d1694a361eaf7308f6d40e37b13eabcadf114e22f24e6b3b6ea10b5ebb5d49af79e5da6b945a70db2cfaf75ddd329f91d3c922d02ca1891ef6d8711509094386dba43f1d94c65d223660446b5278c24297256b37807cfb42e239d4d56046a84dba6753358d91f47042511dbc3bc6d5e1547d57b50984b8fae7bb1901b56bae4b91d85155c588f58ab683dd3dd80391d0777d6004d3b3d9866b03bde98a5967b8cb377320d58b6dfe87ffb9fad155375c3f79f503bb0d174c8304d2542f18332bec2a188c81607e3215f98bc7c45f74bd3be8b1217f1c0a83988b13c1d6ea577ee68324341e0869399a9e6372a90c7ab2048d5b751ed6fcdc4f8aeda35a828e72ab1810724314de9e4c29441ea0f15988bbcaff07e835c27e61a66f7592f2862e7f08619de797630cfa2231199ca89228ad173500b695bc21d8e44d3acc136ba2b4841c740e75ee97e5fa32d4128611926545be8f6c30d2dd9a232e78cc15d7872e2eb3e973cd739acc7123fd3fdf31eca8019a5a4980a31f6fb596b1e88f66d66ddf8b26d22d7b39da3394eee154215eac32f53c51d56436e1118742061a45b26cb4f7975bfefe009b7f5f8fd04f644bc005caec6613ba91ae1a9f158d2de426e55f57a62d3f2e5d5e30fa48903073be196f203b705244c41dff6fb6d2d9bb4fff2ee99da8fd5804d35fbf86ff67663b27438a5f45c7bb43fdad7f2dd2aa781dacae574cabf416674e16d57985cc6abbd07cc5105d9dd47fbfe13076f5f4e66cbd1d8b89540fb5ac7b8346aa1f535b179a87b7fcab9cfc1e1e7cca7eac5f2ff196a2d80c1f915e4766115998fecf51e83a7f611f58ad0e430e4002c69432bd1e4be5345c252a39169ce3180d9c4810b5b8bc56a72e8bfcd1af0bec0d1d30b294dd4a11e85682470e955846c225abdfb7c47089f053c2ee2a64050e30236ea045e53b477fd9f0e111b4dff1d8bb3dbf71aa7ce4df8550a84851e488e7befa2cbd6d3f1395a811e43a7e6b53aa11aae98f7c1ccd7c1f8b08bda74fb7b99e2296e4db7126fd6eeb5b111ec1135fa949fdf4136a1a4731918c2a7ccfc29e093355fd18816d317751d220ed36f1d172fc8eb944b10703ebcf543bc40c57027047cb7b5f3295ea328bb890ab7971c55a7808d3e9b4f1cfb5d8a3a799f4381abff4a4fbad043255a8b4201b678a5e5f37d89580f38cf41c0c96aad5c228a07ca17bcd9d870fc14b008158f8678f3c2b1f6450cd5157b43057be901dd81110fa5751212dfbcacb67b30d7ac9f565ddded36764a9294366c2d2b815af6616cd0a22001d230240960e9a7239086713000b442d5302e3ed363c8c2b0b3d5ff0eb7f68e436ecf6db74540e772252657ed2b714d284855295d98f5625b843485dc63edbf132a673531e2e58dd4d47b40484b1d0441e6c2965c30f084628ce1e85fd48650089bad0da1b0cc23787c7a5e27cfee50e90399a9f5710fcf17602dadf9b058c402d80cff7e12b4de394e90fbe5802d1feb8fcdf947602451fb7f8fad08f237f62849bd09f79664b055d0bc5550ee0a81ef1075ad773530293922899c7478d3914f8baa10e060481d8cdf2247430c4163ada350ee81f279ddce7042a520fcd837504c0a91162f927254750dc52664e063403f60583064b3512cca0e01f543cc52cc2d3c5332985111963f4766e396f2c882cb2d1435031c962f442ffe034c39a38ed0d02085cc8ad367515e3dfd8191bc0fb81231588ba0f78e6aef6dc3150e17ca91728874815fd29a6d349a37fecd774935029eed42ca814aee63b7b3dec989be1438268bb525eb6d682ae04c634aa20fbbaa70a2386e966c0b0ba25865ff6c4cce114ec4fa51aeeddd16410a6273cf2e0b3e6486782f15db75ce7016ee369cd0702165b61e6efb799e8970c994101701b94c9b1285c09d8f2ac061e09d963ba7e06bc5a0530929b71f78a00e7cf6a96b0d6d342b75deb87236c4de501c504ba1e73ef5eb957e88763029927d301bad6a4928d643cca44b6bbc2e179fcdbfd4fa774f0e1f203f92965b7ee03ae3494bad7e68a586eaf6f55e3212d57901fbb0d9d8fbcc18b3afa14bbc9e46c17f7778ba945871806c4011073f19463a88b74d2e7a62f8be8b66c852c7454f944b0e41402c563ad2838b1eaf73d3c494511d1f3e33c5b8e4830dc979214efab7ab5834509ae9814111c62a8ab8c84501eab79e0e322b751379b60b20b7c3b45a50fee36d7c244995a7a401bbcef67dec2f9929451247816c71ac0a4e982b3631b914036270bddda77135b53b690c30fad1ad1c8acac6a08188d719b217ad6cec94a1dbad2fb43f0ad9d5831511b4fa7eaf66f14af99d44b9d76bc66a05cbcd9e72db0fcb0b6e1a850532ebda5a4d1d2250bc4206b55d55b74f8220f55485d60c2e4327ec19ce9313263c250c8d3ae611aca00172a0fabbe126d80bcaedae168d5256f31683a17fe1932d278b5151c22381a35b748095b5947f6ffbf7e499b58ce6b620cca61de8a0fbd17d7df8fccc62c83ecd0f39d7516761e65b135496684838a6b6ca33a509d1ece2270c3a50ffdccf1995b522fb7d4542c1ec0cf12f10727e60c699959740f9bc772b63ab0823f51b86221f72c878b61c47cfd20260f84402441e0123e913d3c487a9cc69da83826b53b1ce0472b465f3f619303eb5d603594cf28e54c8bc362c6d6bd9386b32a5f915b622d20b514ed9c19e3c278c40441f84d8adb022eab7268e3518a5a167e792977256b296db38839339576d41e6899c49132414dfbc659019ad644a7effc371317c8233c987f4566170de0b83047c8be98c50afbebbedfdc1258ace437a7d1048b274cce308151a398be7b0c3ab8577262a841407283f63be73d8243d32c72cbd0b01fba3f77f65a535882d4ad8e202ddbacdb10fa4592aacefb6a6aae8f92ba1dcdd6e2988f43587b8bac1576fcf899034fba1a047133d0a7bf4069aba0e4a144ecdfa9dbcc6145b2d11480253f09b08686910885308a992831acf761eefed701fd11158878dae22570ea97d400eb3160f7047f92cddc92d13ea826001bc1bc72aaed57d8f1d0fe8e2daee8a1429d16167f97efbbdd21dd514e74120caab2a5b5a4c028b2052c3c6e0db24e548dc9bcf612b8e8f6eafd77065e5f2ba028f750307bfc95e169fcf50ae46532749eddd632817b1cff99535c3f56ecf9d395a0ca677f63ff5ea13c79550ff70ce508cbd94e41b58cc15d9c7b8a1c23c95fddb2d4239f9299f2d9a80b44a8fa376ed9cc3153658e9aef1dcea0bad8cb50927242d796439922ff6148f4b7b57cd826220b446efbb5fca84e270c3b79d3227ae7bf0848b71b3b1b96e06eaaca6987b34b39f671f747b6a3122e3dd5a20dd260f9991307dfe2fefd9ff88256065e3e8910284726570664f6e4a1a9c2c941e1d0831fd57ec48d700340b344229dbb06f9720c2f7bed6843f9f66a67610d93e8729c57f8d40e80ba921333d7af1469d0122ebb1029bed8d4cd4e76a1bc40975b3ce42668a4ebf25bce7a398a407dc6d192fdb44983f1c46ba6dd2e52fd7fff6dd3cd30e6be1cd10aac41d40726745b52e3a6d8a3910c6f382c5928e2b3e76c3e54fc3eb6a3aac0c562d8d386997b90e75234253aa23658d50be328f470c273f4d5fbfc570cf4ab379f50e2f724218039fd7e1f2680e0e9ca9bf4cd77c93598f66eafa302c7a5ca2ce2d1d4fb3928e0e9e0664b17d5c2e60db74d3a1c5971f644d3368d7e6af6c9cf8e28b067a01c78f7ade22f940e0638a7b507a282e2ba56f860a73b040ac595e05c94d0d383add515718bb7b7763d5b8ddc010a4fe706cb24f6b2f58148844b476e62ad9f4b7c369bcc78365c9bbb2f2eade525c8dfbe107ded95c8c48cd70fb2d7e2458d363994fa56f8648206ea8254109ee3e058b0a141346502fcfa0b436874e194b8b07de96ad8c11e7ab4017d9ad7536c188ed942e0ffbe11742c7792e7c0eb11fcc9a4efb7be91a08b9ea19bafbffe43b9e03777bb1bb6f5db99f24e97649907ee6ae033ec661b28ab1922d87bb80dbaf2c35ab198ad4ad9a994ce6b6338f1a2308bd0b4c71236624ee28e095bccb5a03af49fb8f494e788706c7e7844ac5d6ec1a15321e35efecc73c899d6b7919cee2cf922db0d682477217780c80911c129a4536861c076a315bbe2760bffb7488d9cd9800f3d1859c4f56fd9f221064bffeb774c0539a0ab56f2aa5662b6b85da74d26559501fdb8643f075e032e2a696df8fe7b62b6518b2c364cdb74767866de49cdae5ffcacf5dbf42a32cd1eb040a964b300293460dba895face4eb93e4fdf12328cb599e009ac0225677ad88273f8ac53f7e2735b4fad825f5bdb5bae4f016fa201c6869e1c85f878859f69fbc097e56352471b233364fe8549cbef3884959bc652c2a2390219bbc592a468e946053a8a9bb1dab642c0e81d774f21af95678b5b3811be8a69981a58c38ff4115188f6237022b9e35d6048ba01e934709a7782f351d8a68953a97dd880d62f0255af03449d5be8de5d85f0512944599002ca7c379049f40b5f547294378501be0c1393ea17499db8c353b4c1c3db917930e22f671de502df607e664a68950249cad3fdf1a33a762fe2ec8897ac8a25b864fa99507951ab6f8ec19728eda6aaf8ac7e28026aec6211625619d4f09f66c46a3998221a59f5b281581dc7566a789ebc87c4fa4834ac688b9b03bec21190c52e0bc433634a5b86ec383c1f972203902dfaef153d214768c65ca4c1d249dde5ea949067059c11f83952917b0f20d5f2f6a5d28182e8987062f8424c8bd38b4ff0357197bb915a3e17d357d9a407fbf567831a764a1295270ae9c37ea1204d8f6f9cf055be3d2a323ad8b3014ddfc5d6e2e6abed884a3b4ec7e730179055fee633fdce21e8846f12bf992a38101b7d20c185334793339165334bc370477620d46bf4f415d6ba6bf93b451383e90c34e3e8fe19e5a29f49f1b6ed0882a846cb217512d816a8b51ac2639add7725ae3d5c2b466d9988c69874949d9dd138825eb1845696acb095a894f163cef46f8f40425d13a598c7c2499c2180bc3ae131057f0067bc3630a077832e765dbc566f1fab0ba41f1f67bb0c17272125ff18abdd44ce305d5b36ddb841b05d60c0427302c608d00470063071da25eea5f5274bfc22ddd0862b889d3556267f0114e1b5f7f5856f232422052f2e29126ab467d0879b506558a197e633135911825590b12949251f409ac9595f6bef2a13c6e31d7dd94d207a32dacade45915e801b1128af4f9738a39d8cb4c7a4df8685bc13dd3667fb035372adbae80be173f875c5c3043c6298599269e930a0c1506360789da4e39465ef3e3cea2dbb827af7f472a758b7867b1c75c20c17e2067dab02b0c458d1be4282918d94ff53487eb91869d62c7a921b76dcdbab8875511253df3ebd5a7275fdc6846b50124f7ea690e6150e6673efd44a1d31354bccd8c0ad0178b145beb4766929122a5d04c8054f80c0bc3681ede5f12d2339f981f28a8c993ee8def9ebc8c6012a0827c1a76c508354440c1a8f49d62628ebc3ac16cd45bd3b83b817ad7b105ed9f8765cfe5ac06ebd611ed166194a23e392bf3ac63503ba54cef0924c330653dc0b60207ad3548d8bf38a163c763544d2470d16373230edce1874b18360b1c93b56c34c6069414510aa6aabee6439f47b13452f92475501002f8330afce1cb629bcc76e9d365d5b54393efc0a886dffd955550703c3d678ab27f0ea87774d7212a968fca2efdd1e5aeb061edb5016d7e1465ff26e0dc60d3e1250fed84e4bd22d06cf1a47d5e75ef177d9f170d22473180a39f38df9cad667ec7bbaece46383db1791d7f7eae3ffc4367ff7dbb7fe4cbdd992d54003613087ba221d8ea60f3c9508ad83429b90acc1425610696fce14bfa5aed5aabcbbeeb5599922156e7cac1975bdebf2663725acc3125d9778539633cee264a4b930649c1b17fa81698562273be3999290589c81cde2729eebb4e91f77dd7ac9d4dd36a6e6cbdd55ad5cea484120dd26d940ad47edab6c792c64e07e95b0b4068d05d1dc27e32b1200ec0226a696ccbd85e43a42508282dc05ad71e4657501c4a3789671739cf5cbbe509e9e22c51a6ccfb67a9ed23ad15b4e4f760a7a56a040ac6444d5b57f987982eda30d4e05b82fd93ec07a965826c3c3e14062b6f07e9c143b13bfcaa0d7aad6fba1d073339160339031a6d221f3d04131032e30d7778a77a6f70737dba6f0f80bb05242b08e55dc57258e5e302834fba76d2f3e189e441eac071857bc3ced5cd0bd92a85b830fa0efabdfc5e6e50775f02e58944b9caff1dbfede6956a3fff59eeecf32ea0c88967eaa7181f524d2cb6b7ea8f4b27ec1f431adea91cd9fdd7a934f4cf079d26a66a0058de0d33c26d02db694227c53b05eebff2716878ab4e8836efaa33abf91bebce9cc9e97255203bd099d96209f38491ef52c912e4a2ba8e6d261897cfdef676fab3045a6a573dfee7ead2230a8957461801fd755570fa6226d9af70ff7538af3b313ea3a025319d6a4ff873697685c9204bdc05794aebdf5621180c873851b13be1bc13b5edd2312fd23fea8c179b2986bb3640cf2a3e26e9185b5909a9ed03edd725dde28590c764bf310150c2575de6f0de2ba0ff9fc0409c2b607b0a662db8aa5b3c91b5ba261be0fc3c3823504deeca140b1ff737fe505f068d3e23a94c2b98b6f2089672e56b41dc1b1bbe6f8196f0553e278ef27e3b1d2f765ef84dbc994ecffe01502f4fa63735e6a7eac902f80ff5c5a27a3138883079934b318693748c6a131bbbc4669889065babee5edfdb17316ea037202e9d84b7080dc824cc5256bd10e8e1260736137e7414cb6cb7685d69ccc6520748870f33b0589e21d040514d90ba67642dde98ce791cf33d18d0ffd96060024dad62badc1d653629f4a7e8861e3f07186f1ed83f2c4f21c141b9fbdcf7d6c5e4f14921f2648a0c1c3cf10dddff6f846d806f53e61eec410b83fb7fffa3cbbb7ac4dc98c5a1eb25bac5635c25ba64a27ff86d0c4556136372f36f83cda5a22375954a2cd5e56d728ce38ce71a19cf3c894b4f9aa7c1ebc8db3b07ee91e290e8f502058422e6f1f2960d444d6403aa0417c876d8b3a039f8880f23a63b8e2ffec89487b18bfb24e13b8303c6b2b8850c78717517303c553197266fc53ada7a76f057537657893e7fb97b843897e870dcde5831f46946e1e336fc62e0ac0f97061a205931afb6ead36aad5b720b067ec79a74a905f50e987ac292147aeb26f873ee70d07fb7f01b2466c28fb9047cea01c15daea05f9c3464111d053fff93d6e00b8a628a4847908fcb3e09e3311091475db3534c3a6ef4b7546ea5a2432649960b2f69f23ed2d2fef6ab13045dd924a5df60d71d41a91feab29cf48d4317462fb1c86bc2a67bee5f59a479dd57cb6ef571339937fe43745246d9b30a723d3a8e1f5dc0b1137c310648ddd64930fc89e19e0c1aa644373f836c0687cf31764e57c99058e1768f3fd3c7a9ecf32d53e78ed9968f0f21e383f2fb249b7e2bfe7000460f57d2ba36f3e19ab6dd6360b9b7d48bc4cb2b07d2ae036a67b45b624681d6678900b465a7a41a8e3003414be663fdba9f3d62956e69cfa735f8562137432794005db389c0c363069eed29f897940fadc91ab86109f8634212d900f0ce23560704ecd289fe0c2b06a8aceeab2f08fbdd7464517543a3f283b3bc331c9411418c248987aaa8fa75b55abb1d9099716a5c1353659cf5decba933beb0bb898badd158d126578bff6452b718ece76e48c22d4136b8f820e202d3bb30bd20d58532849090aef3839d80b32c5e4db2dd56573e5f68e9783b533cca985450363e5412188e2b4061e11f7d484cf88791d10ef2aa6d8201c262e49c9b1abc95f099248a3a2158c86044e36b84a6d0e6d74d45452bc48650293b54dfef1b2f228e340312242ba47d8fced895157e0d60f0c808b5e3ebc97c1d70981472088abf48806250cf6216c8745ff4e9e002add36f0874d245bf14fb0583827eb6e2401c50cfa55428c1b57030da2f2e9f151dee1b9fe212722bda4b3a740535c45b13fc2f8b7e0b5e2fe02daec4f163a765526f15819f0d2f7c338df7bc25436c5e54107052745d7b73f94f43fb7ec213620f56fc3394d942429a2134ba0bf54f5c421f47a4862078bd2b73e6ab691cd3b56aceefa982739832c9b8bc2bc6bb6c6f41d50344242d0e0c0a7781a0741eb09093d0507ea4ce01e8245c9fbbc985c164d6bb303dbfcb7b90dd03e8bff439f76f0025ae641ef02af7d095c25d78ec4a43d8ed6f5107a780e4d5e6b7607ba4ce5699a74cd704dbd646a06345fb255568589bcf2a34ad515eea826a1d3f9fcf772a108c5bd7b604b96447781a5ae1b81a95d7618bc18d8099eb8b874c19ed59f29b5aaf6f7c013e23d9e8e17aae699b9d907c7925e983d286d46290b575c6360a58ddaa0a85b24d17e818e40a14013928b978ae1fa5e65e32f98d5d6cc24fab3353185ebe26ae1f907e6aba397c328f60efdc4f174f231e5a48e143d69bcc4f4f714c18b118f7c5752cd1b1d7aef7d37166da4ecdc7aa7e12c38bd9ca75582a63a09ecc9dd21a8e97cca6541e65a7b8c29cf34aa7e0c35a132b677d1f084dc6db398f5122cd855537b4c43842ec82ffad4b8e8141fb56c50d40e1fe34d9adbd33e01097f01052a574fb23060b38554eac4074d850105163bc39a223b668f7f03fa0d6d686a146142d81675eadcd6d656dd379d050d1d902c350a4e5b8ef74617f9730f0f474e7447c675808181ed4e19d9fbed9d3022323a7208f7c55d9284b1d30a21e223a501b5575b13f50eaa82cf1c7697543e6d71f26c98462a881c5b0bea7f4d6647d280e3911eac8e5558991faa1a513681dc29a9ceca16882f2a678ee206f2a43d545f9c98f5c8df930c79f216fa7b4ba4a42881a505f3cfc824e0ac68cbe3ed60b63c83ef1bc165a01019f270c1d8baebb0385200db231502af73b22f0e877a0b191538bb98757ff976230cde9550872f36d0eb6242e3551bbcd5d5d7a9936ebc51336684ed83d3707265d0e1a52552efba7b54794323131eda0e9ec64cb497134ed0b4b1d8823741edd9401ce8a3929e53daf4532f029ca389a88d33914782962112a9d066f85df0ff8c96f2e2363de5a0b358103fe95cc5dee35ded408ec2765e9ab7ce23ec69a0c8c29309099fd3c0ce1ef6dc3dbfb6b96109ea49f63b18643dd2b018829e1e4a26e3f1c31e1b4bf76267d8756c3d5af5bc26c22d06fcd9b8df10df082531355f9198c3b97d0e1db6d8cf07c54fc96ef64173bd25eeaf55d71d691eb4f2db2413761336e4e553335151cf12a2ee46b63b3a268f04573b2e1e24a87f857968131cde2f3451b3c6cae9c743fbb30331750d28e0ae090d5cdc024ecf3110df9a9b0147ffc4a13e2008c0778eb475e5363a69fa86f494ac4fadea5431e94d10321551b26106ee5aca388237c7edfd3328d2c57771092bbed6d25e8bf8789d7590ed0d1662070ab9ccdd16b02405f1d6aa406bf79e9a8d7f1f7b21f6541d708fc03d0c549b7b420b9582986818a6ec82f89f73945c73ab61c127836f4d9f7d114f7080e22249de4aaf24ab07b58a567d8e2bb071a015f29b0de34f07fa57c16351a8e5d56fe2cb06b256ab59ed744bb5b2f86abfba1ea49100e44846c5c7bbc381010743aa111cc48ebc20aaec8f46009eaa94aef1d1b0c1ae4eeff2c7ceefc42cfada83ed7eaaeb7b1142b289d7819a9db9441c3871741d5cefce4ea13c546927385038cd5839210aaf6abfc15c65723ada7185b12e822f72c40519e2c4d82c53535188064c0b976ecbbf02c55f8c19da6c7fccde517a84785fb8e77e70d38409230a28ce8db3da7f73deb6a559941c2a259076cc96cdcb2041257e3abf6b1adf3dfc5dc9927eeb9f576f2b52848c5ef159dd14300ee93737b1c594fcd726df78dd0dde2220c0632ef6f71e5aacb589ee8a62c95816f326e54f7fb61f053582d206d6441e8b9639ac79168501521492a85759808909d093a004e268c1fff958871291ecf41846945ff13a1a2172627bd5828b058e2cbd5052e109a317d427ac79557de33979dc295c2c49241b49c408593da2e3fd6149547e4faeef09fbf8d1a7ec7b2f50109b4d9e9115e2bd8ebf211e96b81d66c4c2727a3c47e85523b2708a10b2352e67ca427b2d63144034fd12608f22f84da8f36e1c395e0cc62a9aed48bc20ca403ae9e1a7eb1fae503f6cfa3ec7ac184a96701499e17103b4310f3ead3bd6208508d624211642c66af769ab95b633705401d71304b08427d5d85e6d27111b55bc9c65fe641d1678efc82ea58f29ec164dea632f676dbbd6027d4b1c4970ff4e6d47cc89cc65c36a7f51c2f3ccdd0e4214bc899e5bd7a7d92bac893bb80bcf06d7b455e89fe6a68b992f7d11493d6e61853c627388b66aae595e7b8398b524b39a6fcd0b8cccea5871cc8c4d3724839b3a792507e562c79a6ea12db7781bf0217d30524dcc5c0f6956e9c794b2e9637a20a7da550404b0a849e77ff62bcd9cda56fb8b8dc49866fda6f74919f06585bdd09e9b5564eee1e4ac9623f50a7c7c7f23bf0b1836680a9013d23ef69336513215cb2f96b5720fa36b908436d4898fcd8a6b8f227f62ef9996b403e229ef7399906dcc3802cc8c0e8ba70fcfdf93d6c535edc78fb31c650ee93ba78a2e70cc75d672b3c61b31593352ab35982d0594afa8d8ff547deee41beccfe9a931b6f1ca0cb2def0cda7aaa6e1185f3437fec13ee1d1801fbb4da074a48f8d05f98075b0cd6d17640fcc072bd1622eab9a1929a14ca7323dda8537b6aa2fd253be1368e30b819289f3eef535d718f1e2a1aa48a5212288010e8bdfcbcea01a3cfaad1a3474f08ffc6728593b96e7412121912f584d8b4848a771643d4e6ec76c167830436c49eda88db60c8a85052429a1d340c6cdd1aab25ae9c065e160fb650e61a8b3601d7036943aa717e703db6ce211d717a74cc3e5c5339ed9b3c28e59358a892251ef1c72002b91b098b2dabb747416503258ac1c1254af095a150b23912e0c3a2fa9f476a869cb952e74e3263243a3395d6aa493ef9dab75a489d597c9aff16ca5d51c25c97bac7237603ece25c437069e301bd8847bbb7ec217c2d95824e2b4fe7e9cb40823695d5154f9f4b926f1be3d6f723dfe2948dd654ea09f945c0941fda8d8efe4290995e2abe12c800fb2a4088be33fb8c1a833876cac17665de2b2197cf4a1a8fa4e37e3bdcd28a2069a5f7c9709879b54a612e62a4e7d7e3f09949395c0687ce28cac9644bbd70a4f6cab1349a7dbc9d167fc4372a83f41797344590804decb9825f4f1bbe5d3cfc7dc238773349c314bd5cefd9f9209b92d910acf3646ee07b3d2359541c4d0af2a4b0e29c0f50d54176cc9edaab81a5876ca44f365c2d8cec4287e5e7b46c97f02c194a0888da47cf21d39120b915f635424e10a215f82495661acbda7f8f96f3ad3f1c1e5eda6c2c95b24b069959cd2170512f0b4b805cd2c491505a744034d0a00205ce14be2def80c2a22b112829305cb8836d17a3117d0fd3135ede0cab78df857952d065190b47e510e8148f25a66e776b6eae02c31c4dd6d39ceb768d2d4e548307d05e8e9ba74434c10d82407aae6a67c060914e76e46b89e3c3465d18aec3e353c866a6d0d0d9d0776e61818d9f4e84b5edf5da95b7dcc7f6500c62ed1ab7e6ae2596d7ba33e174b848a48674402496691b63a4e3d820637dabe3e3ea621b0b0cb0b8c242d0adaf2d1b976d8bd8ed0bee7453acc84f78c05a5145c218f1b21d060ea4083918671c4070cef22a1132498e149f67261e5b5befae7f2daaa95c99d6b453800e03fa6b81e78b748a4829b7ce859131ddd0397780c9e74456b90e0545839d78546480878c8af74e9e529d9be50630d588a388be4955eba3eba2415a85b0e84f00f2f3e18fc3ad3d82c995e039dbbfa8f2d02c3e85c35df594fc185dd467f96ab830ea73a134a03a76bd29e1f20cbed4059af4345f995a93062553c7fa1e410ec9b95fd4d598af33222b2189cd00ed3e5b81613101a61b2a57943b348548bd07f6963bfbe7e260365589745c55674c7afc3fb3f1265dc9859248f14c79235d3d9261ff043df37354553f483db3dbdbd7df17849a19d7eec4af3a1eb64b985bd218437505612f4a12f48c5023ad955bb599b15b319c8ba30c40fead992859aa90f341f92f950a53adc65291614da44841c5521699fdccfdcd0eb142737089cfb7cbc6e14eac2b90bc27d9b477ab6437743c06aafd35c0f83fed69baab97c4b62fbeb8e51863650b380c127ac67b1f6bb6e16bdd017686c87e0769c52ebdddb1c62025a0cdff25504a8407f542a145e0106ebc08d00bc44c5affa21dc40a35c0daf1091729f04197c01596ff1386d16f6462016a79c07ed75ff26329d9796ab680630e86e38eda4b6af6dba7dcc71a0d4d3d0575d8104c6b1b54dc450081b64fab26053933b3dc3b4fbd4a3a5d005d57a3d9c7df02ab008a57fe873c1890016dabe8117850a46f9548e3821d376cdeee96232905bcb300fded9366c4eb99581b733017278b63f65a3cb44ad70e3626d59a95bca3e6cc592f1ab37fdf4a617b93baf46aa2ba51eb4b1499cfdf29f57ec460259c2bcaff0468b827fa33bb73c02335421a67549f7effcf997e5fd8e04b3f6b21ed32fea7a7f5b64fda298e81f390ee0f82b91b308beb278bc921759164876330239d0acffc4253397b6bbbe55ab070409d12d2eb09c5b300721e6417c81fda1eb2e6cf6e693bac8b54a09d6267b01b4c7bd9dd90350526b70a67c10fe3b0d7bbdcce2ae72b9ff81169f11708ef28ab1d246bc34139d65084019e11804464dc83c37416eaf22f1fd46444aecb013b47ab16129654e6dc4660b0b0ac466a012b4786522eec1f673fd04c24a9274c786e5b3eee410f73d5ec60497f03110067b3a6e101893522a5643ea33361301e42f99532eef8e071785d299d9b3dd61ad80d463fdc712122ed178e4f7f1dd83084918f0525c9648f8d6427230e734175083e8ac03f3e21ee49c40bb275e8b549f19045b0bfa1aaec7898888b7a89a1cab923e198d19f182eba428e9835aa732a8ec88b6dd0d73f310471474b7df4f614556e264365fe0f900348f9eeed2102863b8f7eb3b23ac036fe93bceb2ebee6e5840c7f7771de0c13bf87d47b332d19f342f43ceef983992dc5fd98808d5dc3aa90fff0990078e6b49a128fc59f95601c0e165e6853471cb976c6e478a0bb8c047b0a1fb69a90641819ca8cb24cbd4b77687158cbb914b51a243cb139e8cc7a47818d057576551825509234403d4a99b0d8b044cf45122ac367889825b4ec76fee0990cc71cf62aec07a46ae99144a31f8ff7e253f4f80a511c82a77786ecfa2d26ea0701b540b8c7239973a9c873a44752f5033003c709ac46bfaa7055ff82bb2dc1b1caed629245f0c0620bb35239a0db488ebe44c2270bb2e0cb7d1bd82222213a661d07787d5ccffc36d10a69c2cddf02e2f6529591ea039fe58abe8e42c0374a5dd75001952fc065ba432f78a19b1aad1fb67c4cb82387b6a9eaad57543f84f4ca399b8817315da6ae3e6387a044824714a062a50db8ae3bc8dbcc9905891c8dedeaf42ccce8b900a15f5956d36945a4d739243497587e15688adc4565e25d6bd37c8971db6b1d4eda3d4c47aff938a8281d7ebee9890598bc7ba146f275e26efb35324b7303c2316fa98483eaf08497eba5d8e1d1141981b89acc5f32f7c2b2fc277e74bb20b173f0353bf780cf09b882509728a7a6aaea2de58cf2aa7b4867719c44ef9712a731dac24c04f5b8aff48a0266c7e0704752a4432a87c3915aa363593a8e9b927bbff0933aae0d5a761620dac43afb018e2d3e99ce9c638d5fe73bdb29563582c1e246435a89f8d205e268885b2281c3dc4d4fdadc2eb880e6cd8ba732ab5a87250c74fe1dd78795b37930bf7096705b63c9529a8007d18122bc88022fb151ccfee6997988ba598091325cec019eb442a7956d966caafa0d3385fa4e04da98ecad30b23ec40f63d7b07477ac2eec10f3458913b6924e8c3f31dd31e5d10ebd7e1a36d9aaa2e483fd1a414a1465722cc15c5ad783008a24838cc88f7b311a2b3a6222694847091ee9c4958274eae89be4405282d73a16d4656ee7f67a206c9ae00b8037ee6b8ee78bc2ba4f13945c264ab2a2096c1d9d206b409b9fa4faeb58c6536e17ba260d0c54209677d489a84838a6528386a75b3e5185b0294aba8c63adbf09c9638547002404d832da5a87098df837fe2b14145a7b2441a87cd3955e7c42001a83438be93204664f06550d7e79a0cb9b876e4a18054f02a981581fa7c264e556bcd8265bf9fa39fb7798e242c209e26f2fed015dd413a7ad1df7c312e58f492a1d334ee4f6fad80c043cc1ffcaea33b9dfc21d0a0457c35ee3d62fc1edd1757ba41ca124b3b2a446a9884057731d22822a083371a089b580302200966526e28b8757c82a6ce030f6804f41adf42fc78a86bb4c8db5fdd520c64559c02bb398befea2a033b89a71e15be3923fb05f15bb5afe01b48f1c9aa39fd8bf0a67fb8cf786cf5248ae278f34c3110257c9a815a35aec9bbbb73fc863715d07a91ef4320347e5529d9b6153c84e5f0d857c374bc83af3f39d55a8bcac3f9da51b45fdabd1e5aa357e65c8e5130036163021289d4c0718290239613d13ce5ed3afd8f6931c46f4015f4ffd2d5e37ebc9c7ef7cdde19c8cd742f113a2bec830ead21227394e6244a76d43ee2f1af37d80f39dfadfb0cda37b63c68cb3881e7196533accf6cf3ddd72b760f66f8660c3f9bf92e0f56caaeefd20f9a12dc48933a395d33cbe9c0e0c9001f43e11367690ee22219b04ecaa946265894d9b0f4744e35bbb96732fa5e09533fc6b87f0cf5009ba9cbb4b817613e4312710f1d7ff215387401a0c439eaa398158d1ba341b1a9cc5543dc39f7fc7c0f2e0b26c8f2c5d174b1a16ffe314fb02f85cb1818b77695b968e60593052591783e918cd2f2f2c8f497aeecc31d5b8d2789ad013d46a0ceda0501db8467c0f29ce31490d1db0ab039762a0fa8018ef7b69cb8ea6b6a0bd7b33cd384a5d14a18664b0a52ab69be6def9bc9c0ee07f4547098c5f50daa62bd982c295be4b8eaaf0eca779bf7ccf35c4fc80f8b09328c123f6732c02d96bad0f90cd8b813963eb22c5e9625c7351efbd443674b3b243fad4bf3e6ef01039801dc764951a58e374c7bffa6aeda42db2137994923d850baba7c5179b0a75532cf11d4d7b2bd3309a060a435b5f4991392635c34d2e07ba0203db32b5e020836da3fefca6870c6a5107a2e849a35560f8f1ae2580ccec98df043ffb6e798504ee6364e1537ff2372902dd143244dc7fbcaa2800efd7bc50adcddf498ef432057b16c522c2559b9544f17d1d43d34857a197fc39a90be62d4862b24b34f970e105e996e33c8f69ee4239f8ef5c6c05a136fa57a95b04f4e6cc10bbf486ddd3d7878569c4c96733c981713c43588fcd0d6f937b9e2083ef25ecf89003cbe44fe23365a20cac84800592535e88c761ed7afea29eaf3e148110c687dca9a00ad878cda110ca67c452091c195402e8968470576fbb59063387bf3ac73bbecb0525e76f08e75d8ec49ee62032524345fe8b04d9dcc977e558e31c11fcaa4f77b8126152b1ad7be83aefc17e92fd803b77639ca0cd87013b5779967bd2a33bf11cd57f610a9b02cacde1bf5f7164b24418109b51412168744cd68e87bf6993baa3b30074add2a6f693fd92070b6a333a3d4ae5a4259487efe8e6da7124439cfcca127d4392275730be5a334d131225d08b1f676b636864fb2cdc75bb3096640cc54341f1c049decb977c32fa3d2aea1ac972abcf26659be373e9ef51ae38b3e8e9635652391fc75ebf77c3216ae68d67ea8151c90c93972a1764bec43b2a44789869e318c3ed3e55ac4475ef04157fadaa4b18ff1d9dab1bc09efe5d5d0c7e0ae0e99caea6453bd7db3bfeeb8a8b0ec9c9d33ca2ea82dcd1b1078beb044421e346def02f2d719978694e472239b870204a653bb5d5c6b87a0a5f66de9929c743e1dc3d0a694cd60d34ce0d00c57ef38c31659471397cc20e8ae7a8f1b910847fe1c47b4551dc2ebbeeb1f91624f0c09b521accb526707ad02b4e29022d6286d476c6453baa5b200027eb8993cda0628fdca0849747ed635b6bab3867fde33c83ff7e01534665cc0987edf506f332ffe05f542a12e8808689a219a5e4aa17b425821916bef6f42877173601f3ddee55827594911f3ed2bcd6d805ed99959d96293fc8d5c026b1422ac3816cf06df80248c483b3de0be162550568a3c04b0355847df1ee015e12e853a9d17583caf90a88c78ed175f751d8b5c3f65e6e17f09f7609c7946a23f4c6c43102c3c117877a7698bf2dfa9d33fcf163e126a9bd058b7b7748447d34cdac5f108dfbcd4b6f92265f77886f9315a8ceb83d5e6d105a049c2e43f12969d1d79d40f76b6eab8e414240b964234dc536d619f2b3503eea4e2a3a19afa1aaa857de12eca94af139b3da664b57620c435ffd8c95514f3c633ba5d494fd7181f0a169ac6745e1652b5a158b41d44b0b30891a9f4d153fe521361013e578347f52b5373262628c850004f22e7370c19203daba224032226b6b8022e1517d9f52079391e043bb49c36d853010d1a3dfffb95b51dff52454d1ec9bea6cfd9eb7eeb2ecf4a41fadf23887e192bdf44851021abb71018a69dfb29b47d6f952549450f01b23a6736c73a029840a6e7c8df00786155e04c83ef83ccb20881e9ae6eb794e9ad9e60aa3d2fd49510876ab9fe31d14b3c6a88f6634b2fb0822f232050ae9f7510ae1e87a903be24fe38fd6e676e3d115f4e5e0b1f43541a3b551bdd579bea5e822aacc980ae9239753bbe530acdc95c8da2014b67ca8ac08d8c4bc661bfe13403f6c566db236a05b0d9c063168a6f8d5b7c396186b3f1f50c581e82caaac7a1c44513ccc6f29fe013150165ed69066fff000bcf91874d509c10c25831f67d68727d807e8cb1be6cb30e2648b7b90cf55abc4439470ec48f00a659ced794a88743306840b1124209cb69ca0f106bfa8bc19bca6914047f59222da5271379e6f6587f4fe6db0cda83b8df120804789547d7a7fd8ec4c8dd911c86d08fb53f186458ac555f1e4b66dc4fdb2ffc8b60df356a0baa6d77029636016d46f3284d888b67bba249c24de7791a1a8243e5a1f9c33e27490a6d03ca2be5e709baad1b0dcb4054e63af40be0fbf552999f63fe80c9c511128751a88f971ce43c1f5e47b96de0949a94da7668844d99c6baaa3e929c4c19971f5123d79042212043d82576120f4762b74460f3bcb520d933126534401eb0601df78d2995a9b5eec56ebe85f2294cc71c1ca36c0a83b1d2e7625c3ae95c73208a4b69bef678248e1994a0997618e3f1adcef31173d57b78e9b0667a1b048632f1ad418af28186879833a5e1f993b6637e52f346b62eed1fcd9b6bfa02ec6610b9ad05a9bbcb2261853256e67e8aa97d7090a4d718271082faff3daff881edb135c5f953243f06beb118f380aa9566f970eb9155b84a5c5179297c0b2cb197d2445d5342cb2c26ba19d58a25f22d4b64545cccdb39d6588a46cb8670e895fad936005885a9ad9b8dde588103923c6920c57fa171782eb80dc87980f287fa8bed0438fd72b7f36349bbdd234f9a9020d9d3f9aad1671d0c19660a4c132e9210188503c5740bdeb541a3dc6a08bf19b8ee3866526f0fa699c9138898751531cf77f59b640df4f900fb59390b9883af7c6f14f5a75fb4ed57f095808e3f0865d7599a886ee6a77e286bfb3c26bc6079bc456755521d05a9940c0dc2845259444e18f15704e91c9a955861e4f38a1b3949aa21116a970ee11b951c628287553d62f91f050c94ce91f1d0fd390f406fa3f08da841f4b25d2d091915e561039e8f045ef9573e70158a26f3b63de0ad6a88faf06f8748738c7327f4f25239560129fed02d0dc24823e9da63eaf267fd2a63366d4d98115859cfcc31a7b9a98f5ea05ce5f0b082b02258b2c992268012055a573a2d54428bec8e89561fc194e3cc9e9e4da538b071b6052aae0e60053db1f823979ef24edbe27fd656dfdeae15866cc9f67722213578d1f8c0272f2c741e12f5ec5e50945ed19257945a58ac888246efa9ba352c1835dc04afbc14b500fd064d17ac14f153e5895c5a9a4255e902fb8ab057a297e943c3860a5c31aca6ee41de29a4a0a7490278fed3a0ea5a85226083b0008e784ff406e84980074a2c4f358101ea0eaac2ecd25fd888593811aceead44ab3467b87a73b76c361177cda5a2f7f2657c950f004b76971902ec81420b611f53bd22f351b3aa6200d02b03cf2ccfabc87eca4415fb82622d0314609684da16edd64758272f9148a47e80993d645325007059811f6ac0535f1844c767e9305a8e742c79d9c137cc367e4c73f54f7baac2388f5a2ac881a53f89ee213ff37c031c16bf4d335e33ef9efab8991066c1a6920ced2c141761088920730b6eb810790642743d925036247e18596f74b4a188a8bce8dc9b3068fda613837ae20b6a2943dd1314fef1c8e55d89a1aa23e7f1079fb6b9ab063e4abd3d927cac076087730367a524da08f8753df303a74dcd8846d13807a2fa8d4d8823d560c07d007bb389e307bff0f4b23af8e3c6574403285dcbe16eabcd74cff92d60d6b442ba7bf2e23622291d4548b06604d159b789b2a0bb0a6095dc7444aabcf165d63865e5bf015ae279f1f81d629b946203338ca4c1b124c928e5b2c275940ddeb201c6e9b09f44b6af858c19bee0661e905fb328e58291a58a5dfb59b042f1510532b32c88264611d2c2eef38d1f572933f6452c456cae6222b8c9b0b07bc17b1f33f68597c3bac32dc98855e25c7974d966d192cab6424ec0ed09b9c7de2be24a9d94d6c0ad64e890c9b364b63d77a63d3530ec138c16600237d9ecb381659a87056fe730c4315e8e27f4875f53d18166f8f937c1323ec2eaa0aaa2bc279c375e01b35ff98942b94a4b3c8f4a5962373648b876204bd89818a6c2a368edf796a94f8a390f0095c8c3db97ecc276077b5c8de39fc3fa0f1c8a0fb3a47b30ac277bba706fb44a4ec110b65d0a78fbaae22f67cef9f5204bb706b3c3c1e0ebc4a50e499143b7d8dda58caf25f9537bb9ab6b37b7c0d9541b13b848887a805471882ff1e4f8e2486c5a3506e2428d7a1e79edf54cad7be5ead59440e271278f141203763f8e6d6f9678a468a4d6e37d59b14ff3cc1cd111c064cb2f953d57e972f7142491e5ee4e8dc51cfa8530f8e052973c193dc709658e9cc0f57574220c0eb209c225a9c4e5cc3fba42215b63cadaf3d75909ac1cf0a1eedc8c65dbaa02d8cbc67f4e57fd0fecd05c03a8acaa187f81e0fb501f9bb0a2d8b117ce778793cf9803b8a0e91b6dfa7b91308ee7ca8efbfab83dd3c895dc0f9c3b744079664c99a1be4f61eb73bacbaba833fc5e8949f59ba3dfff875e7ea5d77179362c5b28017261e2648b6fc8b0d904f5dd7c01460a7d6fbc3f3bc05606db5ad3c9d53b791a56541688a66b187f65db9e62371986ee95879b12dea26fca693a22e88653209aa776c94c78995955bbbdd185f6abde064771d6de0b3f2cf2ff0e8298c907e7ca3325cffa16fddfc92aec9d53c08920c34403950da0a2126b2bc1309de6017f8c7c18acc267ff5f7dcf46ba32f9335fed858b782a750b7783f7bead0551d9a6495643a33416bae8de0fbd0d8b6afbadb98daef49f5f5df4e349ae8f9c024dec19ceb13de6aff10e664d6f7d17fec3a71379c2b6687f6807b1440564eb5fb774ae2bf533f990347f9feb667f2c334f978dd6c0ce647ec946ee8931493e143ba9d7289081566b12dcc5b142622361ca6faea32287d342750b10b6144911ccba3c3d208a6d24057acbdc6da72b54a7510f8a13707364eeaef36c707fd1070b08b0799f5907414431949d228d9909e9c462ea7e633932bdd4622d3fdb3a4f94ff39cdcf124e089d467eb864fec36bbffd032d8a36aa308dbd8c4b3a68ec8e284823e8547a40a9101aceb84ebe3a0fc753a33e6d7cc89142c701e8dbda37303836cf8bd8061506c3ef1bf956c711aa9bbf2115b989b733d70c106cb63df700f8bac68d4f69b3eaacfbf850dc64ac6766330bdd2cc191e8c1df1c17d82729cd719fbaa3bbb6024564a94a3acc0b5e378c15b9a38f4a56ed14ab7bb8cf5ba4a2140e0ca70a17a3866c8001fdfb03bc4de9f52727b2faf386e8847170819946ad9af9b9d8fc8da2cecefbf7a7515b53eae19b7f9e411d944d26af77079c68c35ebff79830ae4836ae7c9da7183054e73d6c174dfe3805c04e3bca9daf4c0c87a7224284b94bd54dae4d24f7d6bee5d23f3646d4ac7dce4fc389910d3769464e16dcc57899ec63b341f75053e5af5076fc04e6fafd34e1283ff7eaf1bb2c1a6c1dd48313a18c891e9dee2ed4dd15d3a2173960169157388cf841f4b40824e56203caa2a394dd7d030e68e504b2f9750c75adaa5acdd2c8e0c0ce366e467d7e216f5dad16789c154e0a3f3ba2435f1653593d194e180d1ba7e684364b8b10d9291b3ad295bb81f9b8239803c8b6d80ff050a563677d81b23d214fac13b29025b72a2e9f5b8b93e27e868ec9a91998ec1ab7d7c63eccd9f28e96f9c56bea7933395bfd5a4f64d4e3abb26443b815956715a79f0eed277737ddb286ffb4ec48da23f8afd86e26a4bc59055ce58a4f2df4805490853e65f6f80d4cb5fa679862b0b3d5c95fa6a81f5dcbe680157f8f3b3c210910063593934b2e6a901241056272d8c7d6be078474e73098ee651746e469ad1ca5ba73b00a565b6c23142f134e8a7e93cbd15a8fd37e28c829c01f1296958120c22976acd2f1aff4899ef16d62dddf90f7f27a4e943414eb707c67f00ec2e667feb0a114a6e934dd93ad9fb0e62319195c9d7b0fb7994ddb881a9f15ef96d3c6b387183075199f275f9107cd120aa883353ef2e38fa7ec9eaea50d9cf555318f956247bda9251d0e771ecd58af5cf74c8575e032cb581f6e9fd4b427e4d0516dfd1c2f5f7551fb35e3d411492da42546ae8ee3f9e0402bc313dbff0a8f6ed4e3f6f34626d9886fa9c4469a8ad658181bd010533a240123cf44fede58588d85c9d0fe0c6fd87d5b2594aa7318cf8e361795e3dfc7ad170becb63c25aedf3a3d34bc88effb62c180d214bce12dbfc146142050c76f6884529251c23f2ca286324227b7dfa090fce34ccf09ea1598bf6f907def0f62609b0410dd2ed009a0ae520b2e72189732b569613b15a59f0ebc909a138c306f098abcdd46ffec3bd7572be2ccb11e2adb16265f86936bc3237cdb63a55c9244d0bc0e4efd51e4c5925a1b0a980428fbbda2cd528482bfc8aaf32941a1b83854f015846b124f33da52a565954f1daa66aff9925a22ab94271e195f5e3c9272c85cb27538e58384256c1e5a2556ade35ce4d1dbc8f81ba39b832a9fe5fbd5847aa905a399aa15995cf267913aa49ab35d8cba80a2bf0aa82fedcad346ef2306a2510310e7cee9655c9e9e195f221bab2ffcf53a9184e2bf3ff3f44dd4a0fa25490162440ff6b46bc558377331e39d4015671d4f9facc962f02d7c0a304c50e0c7e03a18d734161ec1f95a24645e999d075ea40d836c31080c65ea21ba2309c70680348bca1bbf5160d083a562e06cf7e5e7133dd916641cb38184280b99ccbdadb22fafff66897eeb5e8b5d60f51e64e56f9219b4dfad20e990f5b148d24b55a67f7047bf07958e13f26efbd277f7b69da37440b1d1138a93be52876315aac127937272fa600a7fd280fe210251d99f773d804c6a5964ac2cc301875032a1f17ead66d36f83740b015d006ba8e94ffbd7a3e131453e8b700d968966a559bf7ba9bc0e2215877c19fc07b17170e3ccb64f612324020cb7b091b6e3b8acea7595a22345bd2d757091ce0a156cd9523e7984c55aa0df88535bfefccff58d6ce02871b62f5b723fd3eea1466fd4e7312d9823cf4770a9851b9f8aedafbad25eaa13448a4ed6c79f0233c0efd7e76012655b11bd34a039cbe5cab3dbd8546b6f07e6a336fba66d26285bd609ff178414936dc96d55f530d6a307ed57b5aceab339666013a5b05e00cf77e30b2e12ce12abb2f0f5760dad1a9bca6c5bc06e842227812ff496bdc77a53ee446563b3acd43fc32904966253a52d5196d520f9ed8fa67b49463f9c6cd598a81fb69f2b1e913e5dc52b238638fc7cbdd6bec744512a7e0307668b69e4cae0d342db93eb2bc81b33e3da7c9f82f96472d6ab73933071d0b3ae91ecf77be482d49b59bc38f0dcc70d7eaf6e9551b45ad02917556bdf4469b0a5974310d01ec307f7eff957ceb603845bb45cc91d7c70a328eefbc7c27d06a0e7385512e3926701c0cb8653b1faf15a417c772b492a5f1c479dedd0f9c179bf2a0489fe51fe9a84cdec49f9fb37f16b47fafcd99ead95586b4a9e34d69e5d8cb0f70aec0418aa29d027b754b72f55c33d61c1b89376de38238e2a1803a8acb25b7ed29d0aa52d13b07c8afb5e13efff3f58f44364fc507def0df6d7b64f4caf9dabf00193904aea742af23a9f6a90760e97bcd681bc1f15d77067b483484f235ceec89c773c33556bfd1e7179b7f31203ac85102fe04e1494b04993c75cbc6da2d06faa5d7c416ee7d16592e8606f96937caec6e3036d09affef6398cba7abfe1c63420cc8786bee2ce68dc31b4614ac2274090a06bd2aedaba44ff0777fd64322bdebae1659186c10007538d4f4fde546f9e261f725858a3bc1b94cbd9a090458d7f1a38d09d9d36c45f1c6e011373bc16fc748961c8850117b49f1b0cea4ccae274ecc5cc6de80424873c2cbd0695c9fe32bf578cd9b00e30a7b3b528ea6d682d5d14ababcf26885725ec2f1d82613245bc5099de4a80ec46a87f824df50c09c4042c6bd3560fd257f9c0f5364e7989340920525e83e029f4baea1d30a6de9aef3ed5cce271cf2537c641ca5ff8ad47faebff32faa5264f964a0c3db5b9165f663fd324b97232788aa4e2fa054aeb9b5bb2d22b345389e152eb18d5646c3862c75d3742460559471e22ed58aa0e57f2eb94ea507224a145bc1d1c10fb2e5fd2563abd2823d3b2bb78f9e949bd27b087aa7dade38f3133c6015831a6129de08a035050d33885477df6eae6026edc32a033b8b30422ee4ae5a20746833b2494bb3bc04a2b0cc53ac5e03a52f759602989d9d50d202cb1a5a253c2f68213a017fbbe239fbdfbd0e3029ead46a575fb7ca1b796123e1aff27685ce88d42370a7dd7d44377f6bbde367f8339de0dad4372234b7c5dcb6f1ef9c18be88b9277b7fa215362b0adc46c2bd68746d93fd1fd5bfbca1c59b05d834e98a16b3e3705856c3d6d9652a1d18e0c4ce3cee319b395bbf3e770128a90b03d25df1d204abab525d3720949ecdb7214e1c6bcecf2452c0e0d052e3cd8ccbc9dadaf2e634dd388ebfc65d22dda800c890a5391c9d56d8afd0b2d3ed3a7cce1c3f2bf6d03d95113cf9e6dafe25f57603a9c89be020f54c6ba97ced3c4a024dfc90199bcf08f7c757628423a82bba98344ab7ff43463d23eb346c704162f9d62e0ce056810a80012611032aeb9664addc6b00dede4cf6b4245586115382ae08444bf09a3a3e3a6d8bab43b9ab5085ece51d732371ee29208d800dabd1a3c45d7db9181497e9d870ad28c39915781b52cf792872a1365f81be8e719108c7d3ba8e271fcea542c80e299badbb7d0c720a4dae91080a6128861e22056b00a9bf5427d08ce92c34f1632d1c9015fc5ac4987bfd9a5f43f5ba431497ff54eb44b1c00df9e55cf68890a6c086c7b88b5618b3bb18c9fe1cff17d57cd147697c9d01c3ad15d5ddc47a5f2ff993b6ff16d858fdc1bf870fb47349f1d93f17a024802dcf4bfa262ad08c80e57b0e7839cebbccd0f8221f09838b9adcf23118c746b82a8c6fe6e37fe01d0da7dd3cc3feb6b3552c48dc098721aec87b1ce8bde0f817fb245667cbfaf019fe430ab2e3089562facd68bc189c91db8a528b553814acb57b6cf6973535774c160be1879174723489c6781ef6db3b9e51f8d47b3bb44169b3a2317f6412b9de6b13f865d34cd9e41f0bbdb657970da773c4d0299299efb32020c54c965986c33810872005a973b70d739e800f211aa929fcf5872c7b9940490db60f8ad539a58cca0ae394e7cf5b753661981ed7dfcc24832c81495ca80f9e89a4d089a872c898c0348adc522cd702031a06640fc1165ccc464c57b36a390d6a5b0d21f430bb0a6ef8a98cc2ea74011b07b78b06b2f2389abf3ae8c9b2459c08d884f0f715a3cb913312906b86b5ceb87a079a4d109368ccd12948316fda6d0d80c038a04037713853c19213bc5b8a9ef433a69a1b6cbe456344a2737c3e8861c2da45390c650e5af6db72c15462c7513a499c5b41de5f12dfd0ff1407361b762ddc6be0733eb51ed68afa519fda6d89f83e12d88e55b82bce1b4b6bb4c90ec6970d749720e2caa61220bfd94e4a8d9fcc82c49b41a2ca6a28174737ff38c84404f2cfadbfa2a0a68af2fa2f61e2a2a84589055c596b05462152e4f2b72d08fe55519d0175989eab0e9309d01085674b5d4dbe62cd80a6db6dd2198146e20bf1defbf7cbe124ba03f92135f272023f81f7f7b74fd0a89e9db40cbadcc6596151a5527291427f3ab37db8ddba0ad09ecdfa5e8dacc004c761a15a8a715f15def448eb321ae35759b91a4cdee26b5eb5d2b2c9a5212a72605e8b4167c0a51e515a296b06cb4f8f9701e29b71c3abbce67228c99d4911830a123cd781e80de1e6481195358c75a1db39847a5077a76b41db55bcf42202b3496f6edfb1b14e2900ff4abf60ff1939eee0f643101dd34ce92f5cecf46feb28eadc90f28d6d159a5a3b74990f9a15f4dd8cf4e3aa2cd0b34b1d99285d004a76b5b1850c91900701ee94533b5059a20c09778705e9f4451743f1d73755f59401cb4a247a6f4fc9bb2b16d7654e52b82279c9e744c6d528abf5f7884f42cbd0b492f6c71fd43611583cd23455931d83c22ac3a335b40ec96a1b2c3932be6dc1504b7223fb8afcc0a3a0c664c2743bf969710552cf4ac941126f403c1f54eca04dd6d895273e60112036d4487a5a39e5aab22363ce817824f91c044421327a5f10fc4494a8dc44bef925718c32e0f796105d50b978559935e4d8806c52ac28fd4a2ce863485b38bc15e8c068b2523233bcbb32e285d0f8cebabe380d3b4bda1f37f2f138dd5bcd0cbcb9723faf0e5072513cb4cf6e31a922f9b90466a53f1053774b3e76bcc93698d3547fcbf412ff44d55e1e6d5f5710f9bfca1827394657011e1079be9cf33c3c38481c6ac62197382522a1666404c2ba31b0001f775126f0df0f95092c8c03a7439777f8ff7e530d0072a9b946fa8c17199cf6e82d13fce979eb993a2fd91d2614cd403432c68532477433869502e7c7034970bf5eb4d1cb2396a52abbd8b4f55c6f6f82fdd764befd5687e55da1ef8b0d8798fb6c685a61f9a004186f4badc84242c82ede5352fdd8da5dda76a825fff213fd568a417b73850401e3633502dd9de17594dcbaf07f0f6c682a58732dc1b38ee0eec2cd23aea9f0ce1a81cdbc49df13423790d66d19ebfc4a646f0e93c1cb701f5539c65fe95b3f5456b6f28867c6f66524ce7cbeda80be6c9e6f375d6e43804abcab0d02402ae167e142d9571a6c168937bc6543bd1bf70bc0d697bd60eff77356710a980537f279baed9225db062cc7709eb175eb06e9b51dba4f72de4e1b6cfb0a5afe08483baa126145671d4e87b2e43968b5836119badd33e74a967bccfb5c87bdd9cb64eefc0769fb3598d725f977b9275d5ba89059b07fc291b92f342d3f2025cbbea42f44b013e618f525393083259c000e663ba3babf134ed0c1be1332d1f13a25a8a811a940476cb206c40523d70aa34baaa628abdbac35ac06fa16db6d2171614a8c08140e785ea0d015c08e970155243abe320baff9b301045f70bcefd769ef58b37336e9f7b951aa81e4d7ed23e0695eb082fff869f89fa9ec1f8c0b8184cf6dff3a9fe4808c73e17e8e8adc8f822f12e80c5924054c08ec4546a257053c650aea5cdae7d92b6289948977a03d268ebfb0b86b195f7becccc7bb26e0135100b0bbe8da1c8a6c2b3d6722a77877069fb647bcc5af6dbdcd4656493b580a68a1c3fe672f308fae5ee1eec66a5409e02c53e1079ac5c193fe486b348162b23aaf7612e542637167e493a7f4ef4d450806f5f75d0724137e788bd6748437127c6f21c50bcdd9d25beef2c2fdd700cfd69fe1d9b8ebba3082af2e413f7a3c3e24cece4bd770c3b3fdc2be210ed4b2b7760228c30b68ee37d8235e17d2cfb41c10525189f8b2abe601debe8a77ad68643ce2f4070670cdda58e941ad7bb5e365b9f7cf15714beb587b147bc039e5c225015ed3b991a6fb934289d169b30a39264bed63d4c7964aad45b0899e27a0906b79010b39a2d16b407808ac19f5047fae6dc5405024f36aa4386ba90f1e6f3d85dcc81950751523b943866a262799acc0fd81a3fe4e0f978704341b4f8bbfebeda8e492933ac7dadf5a20f03efb3fe29d8b0026c252dd9e5644a7b8c02ded053a04138d505feef8c8ed2aabb5dfae0e210c6d8a19ec6f2e6c538f55651f5cffcfd421948eebcec6bd18153017c8d026a654ee2d9878452cd8f7328968efc4b6cb0eb3b68f7dd229673c6cab0f3301d5ac7a4970a35d9785e7fb5aac5d552e9811b7581807b055f4bf6aba596d972c65a4f8ff70c33985428465e3ae64b808d8ed6ce5d0f2b5dc706be5f84daf755dedb3246b226a95b0e2646de79993a18d4bb47af34ffb301a895437f2e77625cec6835627f8df08905a1b8ad3c8af878371f5c52b2274b13cabdb448a732b0d8edd2fa9c77b3674ef73a445a95de0bb2923cdca5d5b8edc176b8a8c3d02fcd227a51920ba9aa57f8a34ef26bc98c8e64bf9a4d73a21e49f46535329267da1c74f98d2778f0532cf3c2af82144264d623664904069a785309f15f791f640729b5bedbdb1839934052e4390653cc984d8d1e8b6d1b2547e4e3638d78346d508b5cf0667b25315518a7984d255b8d25e01728b332e07967e0caf2fb1b04588ce2bc0078096c7b87b252555d704a86d6619616e316f797b012d464f71c404b7a7399e116ab78e110d1c9f65762242779799ea7b60dc2c33834e5d03943388b03c8272580ad83bfcddb3f0dd76885fda3f7c669ac88aae4bf234ada00a9b82b4568e7e5319e70b6be1f548b13da2412fe73715562c6733f2ca0b334b98dd01b420e526732d4c4fb957b042d41f789715734cd49316838ea784b213e1baf23ddd22474d375bcb282f4f17ccf13b6786fd94fe45bd5935254ea7e4647c8f84876c3ccd8cfbb997e0ed9d418aff5fce756a5d94c1f30bee8c8994b4f5e53e70eb42aa5f7d499dc78414cb0094c798a9b8ad8d82f716bfa89bbfb3e41688ebf203f9d335e34f9ababea081b2e0c13c46288e37eac3811a379dc2b959f5ab9c62a80d58ee98004afb8a725755b67ef0c56aab5e9c40c1783d5caf5ba4561d51016175b36d1650469e91ab01c22a688ccc0026fa7a32516f24a61cdc7f10aca59b13f93b2310e7aa8ceb13fc140fbbe73c07b32c7723c67bad16195ff9088b608e90e6e7042d5408432cd4e051ef5da64c77843604c9280fa4a8178eb2956c62fa234625ddad46f36b6a7151720044a9101cda30dbd6eac518f6a29cf9255b4151763a82a463c06e33eed902f4b044dc674cd7e502e12821d47e88d9cf0475989001cbc5130b3f9c9475c492da7f1626b18766b216c08ee33c8add723fab92a76667f58524aab2a343d938a049c47db6aa4684d824c402bbba2617c048fc47a6cbe1028001ceea632f526318d2f1f1c95f941a79f34f56a8d9c622523ca187c3d8049d47a5c079e4b636a502228a1650ea3ed94bbd024f436128ac9c4839d0a68403d39720a86e93f60dc8d0646ef5e5f2e268bb28d28d4ffba3a15845dc1714f482ac3c3be2cb117e08cebc00b25bd254e092687c40c79b40f6743ff55f7fa9589559b47fdd10abffcf1833271c3f1b9457ea88fb7c797b0e2801b0cbf066b0dba052477fcd0d9c53f6389513b7f1ad7602e1008f8da1e87387c7d98f88f597c2dbc4dacc96badef7e880a5d30af095e90b0a8537d1c09494946c3e67bcf10788c3ed3f2f03ea3189b27f30aa5d89b51bf0a187e79683815a6d69cc51c2a689ccc3e49ed668e1bff4df1719a191c8caa6a0b81004be52c94a5fbf18fa3bc7aa1a3bc0aa5b6f6fb5a0ff58a8e015826edc7b5556485c1977a9f3fc6daadf032e10c3918a43678971048ccce619fc1e09ea8e2e64984d3813f1ceed43423e200aa16a080c748ed05ecc3a2197e8bdac0dc8cd08a987ce2c1f286a0a0a3abe0e24a19964faad3bfd0bd69d8aba5d905f6074c7334b2a73ac72e245f545e74d16195cccb71bc09c4ceaafbf5524783d6242a29e4e3c1f905f95d44ee80277a2a612876e68e689edc937d6b89087fd141f669b3afff98b472efe3685d3fe51acf437d8d9c0c772d046852606a36073fe19c45f39bac383e2610bc110188e580c1d4145d0adb34c977b7c2ed617982d48951e607f6a7088c3d22a6035f8f79300656b5c424b533133c0247742952e8eee02f3db556de97d0f88278733b183c188a7100ed46f6ee54d5fb9ce9d91575ba6a5117743d83c206f6ee4f9cc6cd11e68ec90ecdec856c16321e0c9b625b71345ddd9d703626cddccd7a398d126a7905d6453f75d016e830d19996216cc75759e818d94cb49481fd7c3b1932d0aa9fd7e5331abb38bf880cddb0a5878c2fd4a9e9a991d763a907f9f89fa28d97f88d768345c49ad5ed9e849d2929112cba5573d1cc29011543ab357d235ed856e79f0f5ab1d4b27a6ee9d35db5faa5835895c8fbddceba2243166153f2adb413f0e96520bee756ff5b87cec372c25ed43b3dbace670f67837883503d559338236357b079033cbd4e565c7fb7ca79d5f59a7ddc9a0fb78f236d18863344cbd136d6fdc3313b2768aeb1b7f0c52a3b7c81347dad75922fe589943eed07bc042c9f1021e26fd3d91cea2b1bcda7c0cfd846241987676ae0953b34ea54add84ce450280cd7add28896c93cf10b45dd79938465db2a1709b90b130a108b032e0d1dac97e174fdffa009b18ffd0ce66752598a424eff7b439441e226c41a0bc6a38d57aeb2fa37dea5e229cd25a11c2452e6f3ccd2a82d0bc712928326d7b7b257ddf17c92f047766f6b9b01462fbc62351715422cb804566112dcc67a93e26a43edd9831aac4c554c38ba76c8f6e8c379218c9276860d87019deeacc1bd290ab91df5b911c47fe20ddb35a259672de1fa1d5e62a29187c662da05a2d8929e615d7021fd3e3965c01afdb235b9e729e9776857d95d93d1d0dc9ba4a493cdcea702d634b0f98cd76329f21af1eac91ffb6d7527c8c6cdb18464057062f418ac60c4aa4ac864a9817a0d1fb9a4505e090a6110d5813bd8f3e1fe4ce79fae619ef0ed9caeedff14febab644c39ae5813a8e1c5486d0d681702cd333df9df570177707193470efae77be3aa2af6667964997fd80c5b549f408810b66099ae53f15e42fc31af46f3f44819aee9c3e06ad52c3625a8ef292e9f80500a30ccf7ed451f6e015370e9f4ae804184205cda00084a62b8e5602a9bdf24537425ce00fa2b1db853efd438e5a45766aad89b6b3cae63132431550c84f34b52bad2a94fef8485ea5876837e90db3424b3e230efed1f057e6d141fc9fdca142fd74a9651f8bc3f8c4f1431d6b75cfd1cd786fccd454bc1107ab1f49a7c73dfd088eb0087f019f43dcec734e2e7d9fe83db114f0e3b6a0ebc080834de6e420610ffa7272e0b441870f598fe24d7483f4a25457e8624b959ec6c23df55e24adacdb4a7d69107edaac227f14224957d64dd7ecba21ef2bc46c997374d9ba98c8b6d4d995d761d8738e3601ba358839b01d7a85d080300a07cd2b4ecc63568cb140845de59e2be2312807df99c89dfb88f1abcb9b12f19cabcda45929ebe91383d9e12aedbc86ed2dc3a8e52dbb937feb09601531988e2b3c84765474b4d42830a8a3c83d3de1a5c095fc2eb403bcae21ac66f1349f4d47a3ae5de38bcb1ab9452af415f3086239be58e9fb1e1586a3b02f5afe24bf1713c4aeefc07b52cacf6aac3167c1ec98abbee4ed5d70b332de279cec4784f60959fb4e0b1f6a1251eb16e2b3fe366a53bfb9cd9e90b7117eaf62757921fe3164b662859f039c281cc78d9fbac7e444750ed274fd382ab70535d2466ada88ed2394c7b6c923a4370f33776b8875fee0606a1fd2d498116d0c11a7f87816cb18e624f0c4a50867be683feb08abc35a379ff31b1a87800f6131e21196697ea0936827612bd698cfce88c7c398ba5e8af193306c609a1322937998754971e2cc08f7197419e607b6e41a78c3888ea4e0084c5fcfdb9c642ba2f9afd6ef7192f49e6b9a71875324928f92f5222358ec59f8769bc518958a3fe3f74600817c15e6738597fff6b7d1138fdc043bcf96f01109627f55203abd9025520461db4b69956b217256ec63fd2b7eaeefd18d32a3b6a56f5a81fd734409b53890ede48c4f48d6597a4040631f03465796355be88324710375f0d2e870874d6006d8f76b8a1e14ddcf1bae2e48de132867909fa7ccdcc2a220ea795275d57a22260484fdd609467c92760facf4f408a4472546ebee2fb5002fc8a3a61ae5128382b3898c7727bdfec56cddf92ff5819b6f087094916fd1face8e28a295586c138b63de1b1ae5fb0f103d926c33a660471368a4ba79032ad08c8db261fe94e572bbb9b7ace4d6dc35ac028ca6d07976446cf59a84f9c54f0cecf5cf068c8860838915efc55abd212980dd74d560d08a7887b4df80e55b941c60807e8e9028bf9c4e61b7f6efd858dacdf1b42508e9a3335263ae7d41599e48cfd02578807a4012e9658dde87e838ae8072fd1a729c5e714e233aac760a92efd6261dd4dcab52bb0118efb3b91d35cafb3f49df6ba406f82603199efc4094c39ded7966ee9a5d1060402b19e7e2030d9e01c7d60b69f1daf9530ce72f3cd1907cb6cca2a86d05aa3a61887f2b3fd5db4aa2c586b282ebf07dd65737eb68a619c3a392bf6958e52267bcceed7b1df1e96a60e3a29f9e7da08da443d2027f7b230dbe445cc8ff006751eb3f577c908eede3ed3e4a4829a464f5db255dbd31981f7464490664b8d2ef19d3d52ea48c02ebeeaf2712180ba627d9977e43412294cc98832c563e337e3626e6e7abe1ef166f6778dd8fdf593a96463d9b3854d7d73109bc2f95695d7eab9d9717bdeba5f58feae34378f4cb8448dcd80b86f451e769f266b59e14db6c9fa6119529373e2b1135c23f255225cddaa28400cd6b6a50dc38fc9e2f03fa0ef9d1a0cc7aa49673daf268ba8a0b390488b74d22ee6cff8832399246ded8f6465960ec6a3155af9ac83bf7705e12d1dca673116e817c650b9fa4bc4731d200a232d4d51afebe97912289408037ee184b70c3b60b0a6d00130333333333333333333d3d051bff576b7272643269924d1489d20bf817ea794524a299227131f7e9136fc45daf017d9ad6d23d40c02e90cb10c6c0c332eb080ab0f1898c74d0e3d76973cd5191758404888c81e163e5e60100db358fbd0d5100e3e5c60f8522684a89304f5d9741a35dac61608ab15a6942cd8f76a0b782db2e8fc81072b4cf294f89c2532747a15e6cd92e4d26fa1ff2946c382872a4c22f7a13aa9136f6611394b85394ca6b5b68610e6175418d3cd24e9f96264ae50f03885b9d2941246e77ad29d34b22592a630496e23764d0e7af5ab529844b3944e494a429ae08f6c91c2142675a9fb4a427f49d928cc419838e2f47d9ef804250af3781e5f13d4c9febe1b9045167d45284c92a0da53deb7202c3e235b070a537837932294a0d2ff32b2f50953be51a229d3b9b4351dd9f284715c4ec53725670fa31fd9da287874c21ce26fb4af68d12ae1c8163ac183136615d5174b1a5d42747813e6d02ff994a47d4b50c9e286888ea3850e4d98ca72ca6bdf69d294fcc896d509b83261d0769f47bdd6c988cb0313a6ca742fbd245e984df3128613df4e8592dfbab025cc7f9287b464974dcc4d834725cc257405d149877f101d4a982e47f5b6e5889fefdee03109839e5eeaefd988e9970d1e9230cc5e2815e74c322d710d1e9130889079b2884942c2b46f267d4a79f682c723cca372b2b2c851db9aff77eca0b1c311c62b49e68dff8fe570656984d9a29712a6e65a1b2a173c1861ce7325a51e0d9953dbd6224cb13ac754be729ea56069c14311a6ed924facb798cb97b34498e4ec8b6f8d9362fe8f6c891403830722cc492efd27aa8fae94b591ada4bee0718857e7bce4932b3cb2f5040f43184db57ead3aeb8a8746b67ea5e05108f3c5279dc6830ac2846e64ab2a0b1afe26380f4218bd9496f24d2d1b4b8e6c69516a10e63495fed72fa795501ad9e213141384e94e5c8b274a3c69942682ad1dbf0096814720cca3525b35d5ba2ce9226c80305bd2d6f32b5f2acb39b23503ad16336ad018d11162821967b5985190031e7f309ada08ef20623cd65ad4e01d5a40c08602d2c30fa674422c6f9d9ad0347d0e2b1a9de3027d305a7df57fe814c5cb1fd9cae583515dbd93bc20ab32cc3d9882aa78628a58fd56c5c7000f3d18456ce866e7871a5d19d9cd0e78e4c1ac37aa924a351d1542235b9863078eb380071e4cd2899da1835810264cf8028f3b98539e3699bd7dbb201412a25ba47fae2178d8c170979e237fbd5e3aac6645f0a883517bb4cb35edeae4e44f9ffad367e960b67072acb83c0b42be621e7330adc916f9747363e02107b3a71b1dfbb407e5a2fc070060071e7130e78910cb6cd10b37ad120f3898c5c25552792267cd6487c88d1d36689c251bf07883f904792dbe7adac92d914f018d2c9e0439bec61d0178b8c160b6595beb41ab746b83f9ae83ed585eed09a21c3776f40d05ccb86246038c0de6397df59d24d1162eedb1065397ef7fe5a0f5f24845fc4d2092542ac0430d664bb256fccd6d27573dd260166925dc92547b56ba91ad2d9e04225974d7282b85a36b5c8e1b3b3a11f14083d9a493c372beecf2588f6c25e9018f3398356f54967c825f6def6106739968b9841023bfa337b2c51e65307afc10a15427bfac0ec0830ca61c3ae50b324bd43d68644b43d43cc6605e4f9278ba829ce81b126219f01083c90439a7f5f4f9a9130b814718cc3967f7d26a9e7b725a47043820723cc060d0c9d4c75cbf8bab21cba269ecc051c3dbe30b26594f3b58ec328fa31cd9621d3668f48ed416080911a9e128b8f282f92dcde77abffce33db2d5225c44f0e882a92f7c9a97b4787d251e5c30971c5db7f2d789dfecb105934cebbbb7751d31b696c1430be6aa2469dd2ce9ad4f5930bc96a5b6d48e9e6486a38616386a6871583029730f7fa376e1f4e90a065bcf0b13ea57b334327858c168a632b5b68309624d5530a54f7a74a93dd526ee556af0a082b19357589276d29e4242cce03105c388d59b9c25e8ac769282a973b8927356bd5a74ad0b1e5130a9caa7c7d2e532cf1a1621b9050f28983d9c2c17267842426854c1c1e309e67393943e538b361f9e8187138c9546c853b1aaa0ea6c8229e393b589f6bdbe242121386ae4180909292552386ae41841317830c1e49a39278917256b8561f05882f1b3eb62c733395899446a0c1e4a306e257d2db1a96ae19104839d24c82ab7d32989ae07128ca1d47849bb72a12b191282a36b8484848430c1e308061db494b04aea4cfe0bd6c1c308e62cd244fb76c7cdf6123d7814c1d4f72587ca999af3322444474895483103e00e1e4430bd9a9cea4f8ef4304a46b66e90a0776cbd16af05058a253a44e03104738a954c895bdaa4af85608a919d4d287527c3458f20184796b44aa7fa52ac3c021c10a9e1010483eeb19cef2f9caed846b66c7c1697ecb891858e520ef0f88169cec3e9985b25eb430f1f18d48d924a981c65ee1a57c1a307e65c6622bfc554f40d1d20e181c172d259452869bc4c424242423c7660ac64bac4674f2d295921215522d5060f1d9893a4826e78f27e91158f1c18afba4a8ea2742e256b640b053f22428386eb68911b3bacf42c021c10311e383097589fa137c7a47c7948085bc1e3060695a469f769ba5790d9c0203f982ebdfdbaf8f1a2c1a306c68d5f3735b1a0732e758e9e4048482991d25123cfe04103d3660861d153925bee3403e3f7452fd55141751ce1f0908149da7e119ddd7a3e8559dcb871320b8f1818744e268996e4edf6381d123c6060aabd247956742fcdd1e30546331bade4f9e434d2f3708141d547ce7b96d5fd4f2b0c277bf62ceeb93f05392bcc39c8d6c58a268896ca2acc256f3b67a4bcbc75199871c58c07ccb8001633125598c49d5bd89e9e8931990e17a9305e4a69bb6647a8db8c0af3a91ca1f1af751e4b4e619284c512677492564ebc62c615331030e38a19079831012c66e8162e4c61ce71b55cbe29ea92d845298aee57f1214fec2da00417a43088ac570a6ee36241a59448a1092c198549ac53f951e293a0a4de068d0744c18528cc9fa4b67b07a5e3d467a1304982c7cb61335ef32a5098d229b945ce422f471d9324b8f884e142dd5798b728c27a6e820b4f184dc97956b89242e7aa13e6f2b426e5b4624204179c30d7690bf7bfa2fc4c65082e3661cef17dcbf6f5ef15b761346110ba3c2fc5c9497c6b6bd0e82caec884d9ca2f769252f84f79a298309bcd7cc659d295aa06828b4b182c8525dbf24f6977548e0e2c42422c6150fd4bc29fa416724f24c05b0983f7e838d93d577c1e97e0821206a5ab931872be914998bb3f9b7a6d969ccc04b89084e1a2bc868e10aba7c6a18323611e759f24d572d550823bac2470c50c08ccb80016331270a60a09f3bf88be9bcfdfa636c62eb8788449437865b113939fa41928820b4798949b49919791a1e4bd1126b932e4edb5ea96bbc70853fa1816bdf45c4f0c255c70b108534e6a7dd6db93907fe2c0d122568922cc7a96eb8436ed9295b316709108f38792e3dde29e37430b5ca15501178830694b2d26dcfe479327235b2259681c707108739da0826a313127aa212121214e034d101272058035b83084b1c5abee93a0e4b46b268530c9381f6152ca294d0a17431358528430ef9645133125c9594a3a05178330c75a90cb9295bc3fca487021087349d59f3ee713113fb1145c04c234fe56c2f65b7cdc2c0b2e0061b6d491176fa1b58274a9e0e20fa64eb2c923459724fba59de0c20fc6934fa3e26bc2494a385df4c19c25d59f20943e41cd0d090909495cf0c1dc61a17384ced61c8b759462e3620f6651d952187552e7d44dd3181012b222a523f5601e9fd792e617cdb632a2a31910129288145a1e4c325457fc86f79ec95de0c124269424e9d8695136740753f67c99201e4348d1e120b8b083493a99aeafb7a3a4d8edd6c1a0e77fe1ca443f592f177430e55bf83339c7c598d6ca1cccc15c6eb332943a2978021772308771732f25bcd5564a0d47c1115cc4c194f7290962527055cbc1c154e2dc688efce9f7ce025cbcc16852b024878cce61dee40693e92408fd109393bd77d1069314b675cd547496ca6961c3051b4caa94fc94c4bda92ca3dc818b3598e3d4aa4b478cbca95da8c164c27377c97d726e4acc451accdae77d9565c2dfca2121ec031768302859162f5f8e6b25eb2ece609a15ad9115767fa785849c09aece0456c351704f706106c3bce96dfb3fd94eca6251c351506ae0a20ca60abaccbf3b3f070b228371b5e3de4f90cd8bd98484e0180cf361f262279364d18e052ec4603eadfddbb15386d2e5163bc260b6f924d6f52d44c9302444cd082ec060fad1514cfa4b1f479e898b2f98554f8db2a48414cbb1f38249d895125a253a494a740d0c2eba60923de8cdc6498286ce98062eb860127dfac2765f8cb2595fe0620b06f5a14ae6efcc8be7ac48adc085168c95edfa771e3d5f12bac882e14c8dea64927cd2e771644b07164c3274304b71c652df45b3868b2b98e4f5b8e988fe4e82c9031756309a78f5ed0e9b911fbba882496e3e7afaab09974a3a68d8a81bb8a082e14d8e4f3153d225318e00074440e0620aa6524175dd9892e2c92fc7c085144c225d66c4c52d7bf7525f701105a3aaa6eeedef8fef282444645de0020a06ad2863b9bf2c7bdc21212121496506c6baf293774da578fc4461aeca1fbdf39968c82e1406dda243c552bd55923618010e887ce400041446b7b4bfbf8e25844a3f61be1c3d5a98ba91b9cb13e6d461fc4eb2ff8ee7e9845943c3d2bd7f85dac609d386a58c9d20edcb4c9b30b9e5891f22f3c8961a00441386534287bddcc10e8064c224967fe549f67ae16a4c98e64dc92609aad40993f2e81246ffd915a115e4b486235b963099d7853493927c8543c46dd0b8aa8429e777a97e15dff379642b8bb3b5c31f870d01841226aff20ba1737e9eb54cc2582659e8b1ba37199f244cb1b4f50593a5feac33b285b2924818d4c7fd31d9c4bba4f3912d86802e6300041246cf66f63957f808638948bd1ceb3ff68c4e00718449185527aa261ad94a9000d20853f83429c6b37530b916381881d0a5751fd9a29163474f016411864bd949e97a675d00518449acbd7992fbd4d615080909b12d5c240b1c35685cb9300248220c6ead7d2585ee587f883089fc20d45d093ab66422208730ea5e67695ed878fc0d617cfffab87c13443d095208e39e50a2844fd2e9abe415400861d02c2598eaaeb6745a1b8330c9e596ce2e9ca0227c44c3042fa243c7fb1533665c600133424242427488e0d081c58c2b664c60c615332a608190903f18010e88784c02441086d176f17cb10361caad4a22b3d9fb5d2108208cf295bb6517a774fe3b6cbc09deb400f98341c7bd93a6655397627e305568b5587d57962a35b2452325916a8002145043479f2024648bb33d00e983a94db84ad6a3a12201081fcca1c396b6ce266f4992ca40f660d2a2726cdbc70929ba7123c7971100440fc64ded70da923c481e4ceff9c42549733c98ab4eaca399989c57bc83e1ee4fa45d8a922abb3bec60faac234577ca497cdaeb6012ca4ce970928a696ea183715e4449e2ebedf1ff1c0cb7d92a1b6ef2e4570ee66439bed769933e5c320e869383677a4e2384f613381854f2a40415bd3ee9d71bcca2a48abb5e6b5992e30683c7fab8b934a21e40da603acb93938af2a2b5c46c302841c5d95792afc1589d3dc85b92f3ee7cca0102440da652e25a76f3eb1cbc240d26f1563e662b275daa6830c955929cd75225cf66d939809cc194b9b9326f4a96683a5b98e1a897a74556e7f706903298a49968f5affe975a2b0e206430e82463214eac397d6b0c863d39355d52ab5472c560d03dc16cb5e42fc8923018f472122d96a58adf0f0ca6394930f14dbdcfce647600f982499c30677129df275f3868ec28388078c1647a562c9d7cb43f932e9864890b1d1bb7d1b1111d40b8600a4a5c10e9514798cc1701b205839e4b3772b6b2625a46b652471f102d182b2895a6afd2ae86980553493934d7475830575fea8be8baf03202b982d9523c8b66724e3ff1b282d1357c545ac9956bdd1902a40a06ab3115bb7f94a834e350840408158c1de33e496acce54fca2502640aa6bc5d824ee27552e2bdfd01440a0693d34dfcb50fef518a8271d7825fae37b9418040c13c4a92963aba52e98a6b0079822978ba143f6365729ed506102798e7d2d29c104abb69c608902698a28bd241c50b6fb13e8c08102698e3a794239496f956d295204096608ed7493ead1e75da593e8028c16825d7cf5fe429d3e7c8968d2dd01533905e124c7eb2c9ad7d72f224bdbe81b400418261bf6dd75cff2bab8d05c811cc16cdd5b2b4d82759ce0288114c36324a8f8fd2f549bdc51131b38181032982594d3a491a95c54b49f6c87e16a7738010c194e4dd3c4ef2408660f2b793cb2f554427d10c4408e6d9cd9155af77c9350b82499f6a8af6951ce4cf4408204030e9b111b22ddc058b510c407e60ced9740a4b2f9ec28fee6e00e203638fc7eae9f1987d9dbb1e98e4eae0b6a76169c7bfe381b90495938e2835d9436302c80ecc9d2dfda55715ddbe8d05203a30e7dc915f2d49a99c6a901c9883ab25fd52717ea605c18169dbf47594e81b62f31b18b573b09247fe2c546703d32731b17e336bd793bb1a9852ba9c642b2bd7537ac7ebd0d1a508203430ee491f4f6ef229799719989224aeb296928416f1dcb02102220363872b2df75b7229f766025302480c4ca2acce96ea54533fb30d101898e4d0325339e4de491622a86f6411125203e40506f37882d62fbdd83283b8c0a4e7a93d09ed72493cc78d11748d56184da727f173ea46b63e58615a5ffbf3ae114aeb7f4548887eacc224054f2797dcd2a1df872a0ca3d643eb9e96bf14f4910a839ba7adeab4977e59fa0ab8e0031546bbbe24fd4d3c0f61dae1e314e624a889274cae9cb4a9d5f0610ac357792ec1b45d2a39e902212122e5a5e4f0510ae38965b1e464f359cba323391358103e4861ac9c61b12fbdc4b3751406a1464d691151920e221d30f81085312e2de8b0203c58be84c29ce252125f46c997f61a8e8203857994bc161fb32adef71c7c7cc27cc24ec9baa104f94c3d61d88f7157f2783c1fd34727cc29bb25df907bb14709274c399d9ffca5f0d9478dc08c0b603183c5f0b109937c390932af4e96361930e30258cca8d28431ffb48b70cbca49331a59e8d841e370bdd3c8c2023b6820a024f091099316a1a38a9ee51c26c684f9f4c4d1ea58a5628b4242121a3af212e6b0a7ebeac2a9d2a5620993c5eca0c49c244d3e5b098389ef51fb775414cd6d0b1f943099c72c612d2d6aa8bf2661b80a9aaa1eab3f4fc97ef02109539c37a5317ad9717124ccc13d099674592061ca3b292931cd82e8ec15011f8f305d985e122d5ac972a2234c824a1969a7d408c386b6f49e7d49fecf39021c1031c107234ceb5b7b821899ee39297c2cc29cee2a5744283542c9330f3e14611cf1a322373d97a8d7858f4498d278c5d77ccb9ff31da22351337c20c29c73f090a34ffacc139b71012c66548914153e0e6112948ffdef6da98a920f4398f3dbad8b8992dcc14721ccdba17ad7e4c24efe5c3163c605b09821525674f82084e1cfd676dc4497be944198a3e912bb42a9a85c6548c87ee14310c692c5b3524fbe0e262724e423102625a217748efd274c70c6152275860f409854927b82a69cdc92362c3efe604a1922b489d4caae99f5871f4c9227abf4d7261c010e889ce0a30f66f354e2aee74ae7c1ba620258cc483ef8602a35259de9cc05711906665c008b193776d868c016392e707b308972bd4ee9693d98b43da76cb25a63fe6379307ea59c67be46db848707d39958ee9377caa4687207f3b98b12d4ce29df92640793e956f9e471dd6b74ea600e1dc4f8a5a44f996c1fd2c1a8f1bf9dfcda3998e52b2c5f50e27f65cac1ac25a992eeecbfb792713085c9d5ee97c644ec040773e5acc5f093477869df60322527ba737f9eea246e3089a5d266d6f59287b50d26596d1f4494684be944d960aed15f2da2fbe46a750d26f14ef683c7a758a25783a9dc5225b1ef656254d2601e217227555abdeb3c1a0cead1dc77b497accf3983f14c1c612674a9b020640673ce272e6f059555b3ca600a5bfe5fa643d7559c0c26a5c69379ae9373f63806c37c3493c4cbb56fb205c56072cf7d5245dc9a7c5d18cc9ee411519aa12ac5ed030ce69c4d3ce932a7b2ef178caa57a597f733edf68241aac5b3f2fcff31e647178ca2976ac4bae654be70c1a833734af04fd926945b30e5f1ddbcf89db3e45c011598514a0ba60c3956629512d23ab72c98c5ff4f5d494a2fe7688e1d380e7d60c1606172c5b7bbe2feb882416a585af6544a92046d8bf48f35860f2b98dff7ad4a883c3d7322838f2a986e3e3d7bb2b8134f122a18ffff4fd64eda4f3e2529c1c714cc973ac479b8f664ca968249c9bb378fb31cf3a591adccb103c747144c26b8fe8be5a4525f4a0d3ea0605282aed150e3a326c845e3e309a670d10ba7bea3ae3f57ccb0d322242459427c38c1b8faee61d9e2aa44130a6e202024043d0a6e9c6b82c16a46e77422e7d9e9912daec007138ca9fda33cd587e9f71f4b3085a6b529b12431eb45f6a104b388ab9226b2feb3b7249843bc8918d9cc2f930e09398d0007443ef08104b3bac831418eea947fd48f23184dc5931055a2840bc25523988493521c4b4959971491e0a308268d71932efcca92c6443089164e94479f1371e195103e866076b7a4ff2cd411e08088083e846052325c3346bf5bc9b68e2d725c20eb230886b7f47bb293b09fe4d308704044e40308c6d3e2faa544e5a4547e4437c70e1c67e3e307e673f34ba167527c4aa3068d85c08c2b665c81a7e7c307c68e9d776d5ddc4d441f3d304853720825a875b4b08e6ce5070f8c562b63a6adba93abe33dc78e2acef13b68e07eecc0a4b3f78dbb8f985215107ce8c0f42655a7ac1f3cf49a69e34616578484bc8d1b59f8064242ec3f726012a1aa4d14bd1cfba9d3d88203b386b79b5c66a26649f9e0e30626712c9d92ac54d833d9c8960e94c5d328a5840f1b184f2abdaeabf031217b1d1f353057e52e936e233e9faf113e68600e234bf83f29ec774c46b63e6660f82a295534992175f5912d4cc3870c4cd1d37dda98d896b6c471e30c0d1f3130093a29e955693a090626d18b25c751f969a9b209d3a52bbbf895bc93d79a30080bbd35257464673032617aef3311e7c9dbf48909937c89a9e43b324449d9c8a1c5493200e31226414932cd9354a3e2522c6138b94c46e570d94cfc54c29ca4b693a328f5d5498c126615e5daa5946e4f1b6b7500c6244ca5e496532aa534eda1244cd193369552b3c3aa2912a660f527beceedc9be46b6685c390006248c3ffa4413f34a1e61320fee267cb33c7e3bb2b51480e10873fef44af3fb37ca745580d108c389d739e9a41f72df64642b49174580c108c3d5a55c4a49934fbd83b108e39bfc185e23b6b99672188a30a52b6142bfb7497939235b22288b1b2388b2b8311212523d809108e35f4ebebe768292cb4384613c8e50662a6c8e7c0e6152e1ebc2d4bfc6f52f1735806108e3fe49292fa83025c855087328d5d9db44ab7ed908613879a993b86bb12d8d8330cea947f3b60d078d1d5b80210863256942bf45ef94150a84413b09a6e2ff0510660fb284ae7e90ee73fec1f462bae477acf420a291ad7443644447330017861f4c96c4ce5daacaa936c2e883f9e349bf234c54bd4a59030c3e184b92e3862593a24f987b30a89067e51a2709e242226507187a306927b71731b5a3a5240f5f12316b49b8850773c50bdab5caf27a9cee606c3113edac1f6d9f1bd912d9a2988ea75106c30e66534b3965af549218ab0e063bb14f2f2b87ddfb8e163ad65800830ec6b2acfd2ada39983d8f6cdd9c189fd5e560d231db23364e8a76671c4ca1c29e682197af626787489501061c0c27afbf7bab4d95d76f309ee6cdf598185bf285e106f38cd0694c3fbe09bdda6074ef6fd11ddc32949c0dc60ebe175ec44bdcadd6608ebfb323744ea228b5abc1a4af44abaef792f9c11d376c94c201230d0699d70ba6229fc2f4912d3418aca2cd888eab69aa3b83317450de1e4d49b5df8d4d3398736564c4bff3a28dca600c5913e2f49d44b4920c06d161b292f8122bfb8e2d8031067398e9f920bdea7b2f30c46050f15494f9594e965b184caa56ced365cb2b4c186030a5c929e6e464a54248bf601275a7c4d117bd6052297aca278eeebf70170ce273a6954dd4ea9f430b1c96050c2e983ff754c3838d585eb7601ab19e4cf2e8a72696b4601a11d26327e984eaca6164c158b1de4d85cc071deb1b884500030ba614b253b47f0bcf231b345229bb82c9cfb6d4f9d6e9df11d97811b4080e2d6cc0b08239b7baf3eda36628530530aa60baa89b272761254e5c6050c1a4acefc7432c43e9680ac6f81c25dea84ea9752998945c556a72dfcac875640bad6e01230aa6f3dcf1925cbf772b43c1f823ee9521de7aed3ec124b7eb16fb4568f1d00906751e645fb6aa244f7a64ab74c06882499ce7e841273541491e16309860f88ebf24c7ad870d952518b5530e236d4cd25a1f184a30f9891d7a166639b49e0473aebc56519f54250faf4a000309a6bbf6248a7aeff8b446b644fc51f07503c6118caa9d45283988af6d1700c308a63071494e75916fe24213c0288231949ce22d6fc7d96ea0cd11c02082712fa8e09f2d097db9c11882493a31615674289db42304739c878f564ffbef6a104c82db7879fa1308e6d9cff693cb4e984afa81c9e49c3549923227057f070c1f9824a9ae72b4f6f4233b8c1e18540a157b6d43dacc9c5f80c10353fef792c4e4faa0e4db814186149963b11fd91211d9a27198b500860e4cf16b3e7eaf9d7d6e0a307260cab19275ec458f0d1d07a6d47dd9133c07b12609e306a6b74ef356a7ef637c60d8c01c2c7810b57fc249827f4ae5805103d3bd971c74c649764a0b8306e6125ba2e5536974fc1dd982310383be8cea78d1fe93fb0060c8c03c2a42bfc7ce9dad253130968eea9d3a134f8406030626f5b7a46754a809599a551531c078813968899e6149acaacfc3708159f3eb2dff4b0ea7a4b4c2d84992f288cb7311ff6185f94c4eb1e33acdbdc939223bb4c0b1ba0a93f47726dff689eb41a80ab3967f720f5d3f973f15660fa3c2d4585f99585061d475955352df44c6750ad3ad76099e6cce3d87a6307e57a8143d4f4a17d352986e4fbcb417ab249d2e529864a5b4b373d27fb27b1426f197d382ce969ced89c2687ea6935e8ffe775e288cfba26f6b29ed964982c214469689b1d37177e5270c2756503379b45ecee409930ebd930bdf36679f4e982e3f9cdbcbb8f9473961f6da6d7f2fd39d7b7de1c5260caf2608f314ac179a309bd8eabf96e4e8d87d260c4acaf92965c5be9ae705264c7ad94c36396dda9cec12e62f59dabf4a5d57ec9630667a9fe8b592a5c4b912c64eeaf304dde2259b23250ceb63c2cd877253ca330973d847114af070525a52124653a6ee654f9235e645c25872a22bc8ec216190ed953f4b2ad245787e78f108c305cbfc51268d955f46b6ce11a6339d537c2fc586898d309f523ade5b6cdd90214698c3528e77a955ba3bbe1661ae9cb2995efa105a9265e485224c4a055d9f4d0e8e6c99800422a91f33f080d48fc515337668018190901c4f821c5de3d18d0884843cba61c3f43fc78e93552ef6f02211e6ccf6175d2f7d63d2e005228cbabfed512e7492537508837bfe8b1c71726b9586309ee9a728eaaebe2de48217853065be5805bddf33f111c2d86249ae3521b5c3c70661bc0b2f1f3bd382692c087389ecdca617a274a53a861781309e9276d9ade424af42409884b889b5cc7c6a1311d018c1e7a8fd8341988afee59f6669133f98a39c7a9ee58e7529fb60f813bcfc242ff136e383c133af2a67c993a4f81e0c9fa2e4589d548d09a51e4c277b75b9a232d4b53c98a389b1ecb7ea9e1e7f8107e3bc8c74d3252c6bc7b2681b437871075336f1745bbcf2c20e86b934e184f7529eef34b2553583177530854a255cfcd265b2ab912d0bc20b3a184c693319134ad4f9650ea66c3a7e94294bd2544a0e26ad2f7a823a495e483d0e0693e7ffe64b29fdcb8f6ce9a091c0c1144d4af93c3e4bca217d83c1e6b773aada2dbbfc0d13e40b379854104aaf4b259b4b6d1bcc67e1a4688b9f446cc80673b0f7a02ab98fe53bdd1c5eacc17c526bd592bc4fe2a406c37609a7e37859cd0973bd4883492713d34b5227683056124b8e269a78c9a8cf600a55526991f35529a7cd60f60edbbbf2f776f295c1986d9fbc4e6ac9605251aba324fb0a22f2184cfba1cefa47094ace1e3118b49f1ce553f59d594a188ca2c2ee97245d7fc7cbeb008371f352ed4d2f652e60f0e20ba65259a3c43d3163a34d890612795fbbda7ee105e3655eea69a8fc79ee911529b523c70d1d782fba6014d197b3340bed9d840ba6ec2db23765f4620b46cf3c6d662fa754571dbfe3460644ca0b2fb460d2d949b332de91ad1b28a917bcc882f1a42ab14f5ba3e438221b9d56831758308d47bb24e95ce55f2a8b052fae607613eb76f3a7f27b6c05d3782953722c9c88bdaa60d2e9d7e42d37252fa950c16c52ef9334413905538da5182f6a41e8f7699c1c3f8190902ebc908269e7ce24294fcb58f8f845140ce226bacb45116bb9823e780105d349499e242e67bd7af80453ca9c4e6392f23d15748249fe8f8a934776c9fe8c6c130cdaa27f22ec3e9f8a32c1a0ed64dd07a52e785a5f2cc16c52466bf5a41b4f4a94609cd3419e54929c491baf9831e30a2c91dac38b2418b67b5b849b892eaa4782b93b986a976a99d68e6032ad93a436253d76e718c174b12bd5e414e66aae2298c28efb9a14333c9d9e08c6134aae97971cd3f434b218b021012b1b5a88d8b871958ce0c5104ca7dbd767ade42449ee8510cc57f6bb9dc4f198167a1104b307bd36f3b1224f7a376e54e005108c16732a871071d1158e6c896c916347d6095efcc0ecc16eb4b709214358235b55e5082f7c6032b18eebf99fd34c02bf9185c88ed7c1588484d88b1e988212533aec29d1accb173c3027593edf5d2ec78637b285430b915783c18b1d985cc32ab7ef2769e2d681399fbcab99967f55d11c1867b479ea7052cb9c3830dc85bf9b855dced36e60d219f912ee4ed6a4f8850d4c762da6dfbc4c5eb52d72ec2875012c6688ccb8624603761c2024e4450d4c9b6127d52d3d7c13bca081d14f978c4c117b2dff0ccc7ea34e97871959cf65604a714b0af39d24716779110353bd89a524cd0e512fbd808141e98ddae925499aa0f3e205c60fd15d49eeba933a1ad9d2a1450d2dd0162f5c6032dd9d43bde959ce9e5698dabc72e4524fa509b2c270491895d1df49696947b6340020ab307afa1be1ae9efb2a3eb240005185297df5633defe73d646100498531445c10bbf5fa20c43a50d0109871c516ff80195714e3e81a870ab376dc51b2a8e9774a8d14c829cc9d3d9e668fc84fc9a1850e1bc62c00620ab3e7285a4f2cdd4ce7700220a53085f7f79d944c5a13f58c2b665c31e38a198902105298f47ccae9a37217ef62d3c2d1354aed0019453232617e0b5d161727a5ef0513464febeffeadfa57e9c896ab5dc2784ad81315394bd9c46458c2f026aae986baaba524235bba322a614e353a7b98b8ac57324a982495f42d4c344dc224875d8efacada62ab244c295e504ae713542c391a09b3d688df7dda748f1d240cda16aaa246cee432c9788441057f2b0b168432f97584e1843049a8c516f524fa383e8726e56f822b808c46184cf49a136fcaef4246984743495f7a4e3a2137198b30769f8ad4947bdb3eddb871382043118693a25bc97b9ded3dab4346224c5ffd6649874f37a1d52107198830075529e99c254fbbb48c4398a4b812cde3ac72c4a7c020c31026713cf43b2c410d54aa1026312fa9a4190d1d4e268439efe747ab3e7e50fe204c2687ad2ea16287b92a08b3a9891ea74bca2ea305c2a04387d1a684e8baf24616059f280310a62b25e89e72f3dc50ffc1a0e49c95ecc29d901ef58339e9cc4ed162eef5751f4cd27b9c4d11e3c8960e1a574690c1077316259bed29297b305c8ea73c95284a8b4590a10773b81234b4a9dc9f4e97910783f692aa3a6fe67a5df06036398a30f9fb17410b84848484c8b883c953d07fb18d5f1bd30ee6249cd81a7961944a2a75308736b14da8bd94f7ce0064d0c124ebe424191d4b2c133407534ab5a32a693df174918341e5925389276e58c627230ea694224387ca3813bde160d47a132ce8917bcf6b480896483542c61b4ca27ec9962c7c5d44ea06a3aa8ada3da543988e8e6ca5744384044e830432da60925495fe587e6183414931e1d4a78ea773720d2651629478a65c3f8e0804196a30beb60891a783943329196930e74a82bcd3505e5f6f0664a0c12046296befd3234c4e9fc194975e2d9dfb779e59fc88e1408619cc4156759692a3bf7c4946194c59572d8d1e1df219924106c3a9f7a896a4094a70750ca6346154104ba12d6b7d068484a0e0475a86184c29cc84580b6da5c4fc14d0b0401206934a66b1657d5d3272e9810c30989228e24943f6bd7929e30ba6943d57e74ebe958bc9f082712f956ac966a2d789766871c3068d3515c8e882e14c892d4995bcb424c40573953e1fb153a53594b105635e891b2a89fca5490b195a3088934db4eb3b32b26010323aaae74f113ac71d555830c72b3995ce5a23efce0b4840c615cc29899fb19c847ff924709061059305d3fd5f26e813e6546b9051057354d37f3541e77d0f7320830a465593a25c3ef55327412fc89882e14e52715297280e6448c1e8a24db8b5245fec7492110593bc58a145fee57269060563c910da84f7ca9bec4f30e72ef99ec54e34e1c9a2479002329c602c4949fe2b2635b40505329a600ebd24c9ea6adf361a0974f405cc6ac860823988c92bd1796174ce46b6b25c206309e6e4e67eb79d84fea097a1046358b470e9c204fb376524c17c2ab88aa570e13f7890601ea5d5c4b4e4b01c2a19473099d279bda412943429dd840c2398c4ee89fdad6dd2e84e4611cc3196946af7f82405a10c2258292190310473aa9e3f6d92c9c817f11132846036419aa7dd5d172113c172a103194130ad59a6e83395be54809087095593579e1f18ebc365cb558c2d65715d42860f0ca72449ccd239a89ad02220a307a6ec755deae1c3bb2789d8b841638bf4af11e08008481064f0c07c3f1ac24ebc6c15b7912d9e718105d8780b8840c60e0ca7575ab58412224d3db25556c337f0a518204307664f7229f9f9ad2d05253272601a9977ab73627fe974648b4364e0c0f06a596dc9b2bce76464cb72c8b8816145a7649a27e851aec9b0813969b8279d4d5cfd2757460d8c26aa66fb2957f4a9a89940060d4cd71e54ee7bab939546b66e204e1b3266607ced58794abc1d1dd432646016a1a63ce6639509b5951464c4c0d4a54a8815254e8a15c980c173da4686cee84990f102839234e57f464b18bb64b8c09ce4e896a18368b4c2d4f53bdf6d82760e2bcce33989f4d22623db2c68906057610ce99f3c46af2e891e111b3768682521862a8ca3ecf64ddbec78c5908d17317b428c54989320547efe683ae824458549e52bc9939af8e117911be8ee14e6d3f59be15d4a633d53982e5c9824ce64540921b2a145ca2b8579b64f7a53a286ec5fa4302725fe293996787972a3306f8f30d1264a47a83186280c5752fc3397f712ee8d110ad3aa650d9956a3944e29158418a03088145def334a8ed57c9f30a84f5f42b3add39e30aa6c2765392c9d307f97fb9c144d5cad3cb2a51582189c309e367da257b21222bfe3438c4d18d4294b1a3a8fc5124a3744121dfd5854d22086268c673d627f497c92bf202246268ce54143dce84afb1a156260c278d2d2a8ad7e9f13a34b182fe54e65111dcf54c812c6b40e7efa29a7f47456c25879c44a2bf46da973244709734afad653ca39a8b8fcc8168e3e810e111b37b64f60011b12a82b6694cf028716179831012c66c020c624cc23943c9113dfe15b0ac490844178fea49784eacae10ac48884b1ac4a89eec12e574e818441ae46d5e9245d86923112311e61107af2bdef9ce71c3bc2682147c7709392a79264640b87160f88d108e37ddc9937bf6ad349311861aeebf03f962de524c70d911b39ec0b311661b091bdab8e1f732b2b0a3114614e399fafcea74eddf7c816968e0accb86206db2146224c777de264bebdb288ad440a11e637ed16bb53123a96480436686c814addd861a3016938bac6e5210c6752d72555155d738f610893a5527e7372cd6c940a617a6daf789d4aef9c78642b06214c29c9a96a6cef932027ee448c419872ca53f391dd97c5b3001c5de30461ca9775afaca434dedec85655484802665c31a34a9d224620cce964de975e0810a6d696bf4bb2a51c3df40773d6f70a15f5673e913428c4f08341f8594ef1c24a7d6863f4c17c3a9c94845a4bc249925a88c107932ae93f7a55773ea67b30679cf4eac1a4f35cf4f021f2cdc479309994f44591eb5f42e7f060d84e72b96a874f16e26521c61dcc79548a0a91214c9c5e0411c30e26d35017eb2979fe76cd5048088e1ae8ea6050f7d09d824e67972b3a98e3573a5d5649922df639984bc85549952407731e19398ba73a6d491ccce7263eb8e88e69b2386ae4184124c480832975f44b2b5f4987140231de60bc112a9f567fdc602eab1b513bcdaa0d461125a8bd70e9ae1499718105e0a881811c3748608386056af806bc10830d06614a102972ea1a4cf9498aab9e5b949bae06638e8696ddcd1117578c34186e4c747953b5d8214583c124b96c8474ef0c26fd26e78fedea7dcedbb821526298c124e9caa227881caf128484885106935ca7d37edab7781294a1cfd1233a740431c8605af519933a4a478463307e5092141b6331bb64c56050279c902bba248b9f0e83b1d3fd72798ea1762a18cc39c9d6a3d45f347dd7c896b78de725c4f882e947b4d25c790ebe5f0c2f989387d0a34dd698289f2e98a498d10d652aed252f06174a69f9f31aea6dc1d47749566929e141e768c1b01e2766e50f9fd47d46b6ae8c10230b5ff070c90495392106168ce695924ea11deec7fd418c2b184d90a24f92af424ce7ac60b84ea3a40f23dfe66b8c2a98ff74c9a293b24ad91d3a1a071e2106150cf7c1fd47c9f5182629c6144cf94477f7cf27b1d3e7d85a25c49082412975bb0b2e9726638c2898b54c2cfda4aa6d26b743c78d098ceca0a181087040840531a0603639425828797c3bce2abe428c2798aa3ee9d4a6738cfdc909a6bc27b694f8ab277add0483bae834aa838e09a634fae14284c6054b2ec174a183cba74ffee526251854ec539d29394962d524982bccd9f5cb9bb648305f7bfc8bbafc1e361fc124c926dd83e9b0546d1bc174a2448c5925299657114c5f259fe641999cee840826a53e5d9fead1d1466508c618393b397384cc84601227e5ac94eb6a55828260aa6cf2878fbbf6783d108c273a76343d968609fec09cc3cce8672d396c5e3e305d0cff3a0b9d25e4a807a6b7e87d3ae979b290f3c014c498701ec75ff5b61d98c4702ffd73614bbc5107a6a0e4d5ca269ae4395e0e4c4aabc4a8279d4a570a0e4cf2e52c49124268d933ddc0144faae0ed5d3955593630d5e5c597d2b5a452a9060659823cd57596bd17d3c0f45a2749262539eb783983b6aea451e79d97813974fcbfeebaa45298c4c0a0e3397ef4fe2941f4183030e92ecb95f97bed271ce305069d2afa09d318157d0c17984bd4cf69b135c6d36a85b162ffe8b8155961180f6ef1fd9be9d65985e14d9264f6c4434baaa8c21c73563ec609536112ee84bcd26a9d0b27a83087974b418a5c07f9e15398e44fca9fb2cd564cce1486cf1861f16cb772bc1406a1754789c95732cd1ad912b1902b18a430b6a79b74be21c52f34b2b543871648a40e083046611297b755a93f09a7a328cc27947bd27265493a1518a130adc75c2f55b2f898090c509847e7b368eaf695adc3f8842999e5f9d874d7d41f86278cefe1fde9c4fcfc27c4008c4e9853cdaf4bb4b62c7a7222904ea824d265a3501c0a8481188401000084779b0063120000001016900824129150b4eeea0114000442483c6236341c221a188cc74491381c0c07828150201408622086c12888256912cb280f46bd70488be999e92802ad52cb9e516cc9a78cd85a3a0a844e4ec04b0ac599c2d139c9598a0938c25c5281cc3ac4a9b51eb40da2572d6b9a22ee8bcd42bb69b01e808a9f365491aa1d040e5beabd54dbb5182168d03e6bda35a6dbd02c099673a33db32655240e7eae6bb58c9c6e581f12bda31ac51f8f84268038ebf75fbdce8f352d89020dc912ff67635aabb561d4318f126e70d523ea5e4fa5afdfda4ad1325ac7c29770d8bb923c1117ec9c646689a80c3e843ea59d29e0b40800f872b0fc4327aa5ffd2bd09418bc1660a114f17baa36e1e1245206625bc1583286e037929a0d1804e0faa898cdf233e7c9dce318f62260156d2ce03aa64e97ba44c17a941336401751ace25b00e729ccb2efdbd33d41605a8ea9847b3a81cc8b35008a62cf620f72c4b7badcb3414a2d0dd3772089610eef166a30e463eaadd0017577e924f8c03b502d147087c32ed5e6353fc157321a5445595688f13f4ebe2d10638bd7cc491fbba67580a7890789a8c941054db65cb2725fc13e7f41380dc9e3310655d22c177626493aeb16dcaecbf77ff84b8d34e47e70edb49fb05fc6dcb8cc6f5b683689cf28f0f2bd71380fd624731be02b160a6d29a91560d218ac8dc26a0f00bf61cca8caed903bbe2206014b862dc2d6503113dd2475513ba3aeeaf1ceb2e02ce42da7d4eff1c779658672b021306f1a430868403152a83159e9b12ad1074f2124c611eaba535803730191b23c53af7fb18f5eeebfbb41f75e7039c163081c6466919e79808dd319d0e1eba996284c30719ccbaca3130445d97af8a6c95a323c30b433a7f04d9cad03ecef9cfe32feeb48d349d2ed66805680c7baff86a4a3c896426178f2123251371b59af9c79fd12e3404d0c0ef9e7267c7b52861aa15117d70d138c109ad86fb829ea207bf88a08e48803c7bd174b34f4494e1362da51e6009eb9cf269b003cf5046c84a99cb0792190f6dca09804d3f3be1d9e320ddcbe5950a0dd29511130718804e2bc1d23bb084ca63dc0401443b14804bd618d06103a4d53ab65d943427db0388de95705cbfc178659605226a86695d23b56e52c74004f34c893a832d7c0cd5863217979b74b6e71ee5abbed249e9b9adacf549715988f0335c65ae4e884e21c98f35d29be30490ea6a939b0fe7ee90a03c5530d60c8a2a167f22c4b1013c3ec1fef2f67710e932c7e2bd261cc36e16d241f4a1527037d62391dce0a0c505bc7a5707aa661615e6ac33015efe7e69e4e9e51662b7b2bfabfa4cd7ca9d033713748287ee9091f04eaec6274e025b96caf018fd52b78e31f892f2fa7cde8c67c9b4e1230f20f3ab9c4b4885d68ab12ddc052489ac0aa92fd4599814799e86583f0fb7c7bf43b3f07cce80638a4c33e4f61dc3029f3af3f61a1eca42969b750a8fc8046c5a6bd648255ed47c61ab9dc720ee641bcfce843cfc38ecd45dc8c4f05378d142a1d040661cd26b1f02d1013e3c072f93845a9433250774b9877ba8b43827674a235c4fce7fd01d4414dae8634b026d0b5b59cd823beb116d0f61eb7c9c1fc04537dd343d829ce36b2673df4c7c5ee3810db7e13356a58d265b8cff630a09de1f20fe2456f8773f3fe26d79d3882a2969f391013525fab744f229a3d0e7f39995c60ea9a849849ab2e7b30beaedfe635f91665cf4000f6a90431ec662a791184ac80f6a905e5a2e2102fc8c08be2c95afc12c8067bd972217c7c9939d26486e6ee5cda5e346593f0e5cf8b982357a868902746c5a807fd4057e863a9566ec89cb34dce27cddfb4b5425d81a62efab57bec3fb85aff6d02d325ec4498e88725504dac2dbd3fa135d4772c52b1386a26d212885f8f1e2c8ec0bee9102d31659281a1dace9cce6abef74d81a192970094c047861cedcd614dee86abbd40f25ac9fcbe9ad9cf9aed8f7853d7167e4c3aa863b94af2a042d61e27b92195bb8c032dc2262242f4077b1a3e60d2fbf93a806d410ad772e0051dc0837c6f9112ef14dcc49cd2cf17f07ab7bdfa65073c092e129cccba97de286142803c01971694880d1096481663284f6ecc42cf9648e52472552746cad41c76b101bb641d2b01cb399521ae25035cd4fcabd54f932e2306787665927fe30063e79cb8b1b7be968fe56a1408d781961632c172276665c53e054b667b67af40219b96bfc9fa6ed831b0d1ae9739026c0278fb0ea32c09f9ad8fe92063f082a32c2b60fbaf2491379d7fb4adc04642795ddc67a0a581462bf4e40e96d3f36bb3fb42db42eca905b0dce084e202910f866e97b799ba214ea0f6fd81e954ecf466a4b3c3ad9f0c30d364d4ead855836335f041e684c96b1549a0ef4d1d94a326663bb0cda6344f19f8e7f560e7576d6316ff4f0edf8faaeef764e1a89fb84c5de1dd4634755a6e973c7c15aa69939162e80266ac56cdfd350600d56b810ba6620b1177999b62aefaa3cef681ddbbcfd93de6922a4e97eecb571340a582cc9f8ce873df57c578f61e76db813e13ee0c86125f78daa7f871f4393f9d2d2de601c5f61c037880d09ce39a6b973e08d36441bcdeb1a1881dbdf9748e40fac227b4be3171ba704c9d3187bfe4b0e8b5e0da6a07c55cd2a12dbf75017905b2d38880b1c147f92e9d8100ab805ee025bfee3401e6e712dc57bca05887929b0f3f885c28db84be3fd5588574406fba9252b63516bb5dc58c150ebac529708e3db322b84cf6e958d101268571f5373e6c4000603eadc797828fd43800f5b81bff4655eef3f827d04c7a64067120f94093cd58114003e9e96e1c5110f467b082fb6b27ec9344840c408ca3735b6efc4d849153b4eacdb6399a48fae1b5b84c114c78613d0f0e39784254a5151a0860ece9e5928d9fc7ef16a3824587c553fd57a53905f0128fd6e9dc3accd0391701b94d4a6fdde4801fb67c0a29a34f052b981a6ae918de8dda1731970ce7a783601edd2d73640e85e5ed94712b1b5ec0d0c0767d2b5f801be1cb20c5ec5acc02e66b16e67990a82207405ceb4a85b3a997e8516cd44c9a5c16c4daf99e8613af8088218b2500227e84caa405bca7e3e544bf03afc1a01c88fc8a883feb5b90a3c72c9440055ec0e5f4b4e206508a5e76ce605b27c5c97b91998926a297cd993ac1c5d5f0346ed423199d9e5e7757c5ac380e46b35d8ead6630e6afd0f00117b0fb3ca95702425a0ce610cd88c2566165ddb1018731b87eb5f391c4c9f5f23c23b7c8be5133b79e801fe212293c83839ccbae3790f954f866b354046f91b263584bc09db89f47d6835ce017e06c68da46f80b3711953aa8ea6dce8c468fa79b15a534e6c70609b9e9660852c2f011dd278162ec8308ba84cebd61a31306667f124274503cc0913acd2c016823cd4824323b466d4274e970763bed2499eadaa3e629c57a8abdc886d19439c9c26c8d81c45ccaacf91852d327112794f944395800b0e29a1ed96fac8320742091fa213a47a7c89955200656c0ed0545050dc943fb86cf1225278011ef8889ab80a37d35a63ce51f97bfb59f7de0e84dfdf9b791c9177536c04dbd145db8a94a1a2e797f6d93bc2b42e2729f14dbecd28f8514f5d2d2aff241fad19c087f24af50ce08fd89756cac2ebf85be0b4dbc903921149351484253117297500e42455868b3466e2d94af50ba42e312b2452848422908ad43c8f74289146a52688d42fe09454728f8854adc35600d18a1c1097d25c872a01c8628fe864ac8980267a9d09a84fa51a1c8a424b367b208c127348b90d13514ca8b72ab95459fc553e5466346d2ae996671a1f58a26473d717b3fb83da497ff0817e8a51e93d3cabec2d9770c39a7a5f6b360825b2eb1b961b578aaaa486a0d590ffc5778cae3145f2df7a1c7e68ee42c9865f59112507748d345da5f268b2f008f9c9bc645c5aace160fdf8865da268a350824c0ac8d609d980011ca434677dded130cce70f84acbbf82aba2312168ef599bf554df7100edd2851062be5dbbf8a0349ffe0f016bca68901d367edd9411934758a05487e5e2d1a65c3220d292593b133c5e2b88c7d8b509f6c8d60da052ec906c8973901a4d827fe286301e1dd5fb14d8b2f160d833d19fd73ea5f67651bd45251d69dadfd3127ad92568c8b6c13541511d87364a73f3c7619fa782ec2ed40f4a103d7218b7aafcabbcd9411c931911e4e489ff8b5374a86b6d9371f1a4fd5b4e885ecd2cd32ade62faee850c76ecb37c94d72fd4d9285f107882e39f40659200a17ca0082cd274e031a5ce46cd1d502796c63a975b7bca12b64da7dbce22aeffb367a6e9ddf1e55c130d6da92c4946aa094b2e3885f2a9d80319c021ee470404d2242b1efd21fbc0b122996939b0dd6a4ecc12f836010a5910e9536dbd8fbd29949592b982b8f5da5048840dd654dc792a885209332454d38c245c6aef546b6a54e566b4495159ec3fde2c43b759d8e278a820c3bccaaaea46dd24d6a8da4951fdeff09921b4cf4451f8f381175d2ec1b1206025e163180b0da3f3341eb5dfec8542bbd355d4649d265e663974e3aee00de0238623ab48b806718bc9a17f0c6a258527a76803b576622618b30d3ae6c7e075ce811cc10437eb17215deefb950800cf426b30b157de2cd08ac509fbab55b4d154be869e200eb653a0c970a36f27e4cf2200107cd25725a068ed52924d40bca32852ff3e94a0e22304ef91036bdea78c081cebea9ffd06794f78db6c70679471cb89d938df9e38e0c3b78740f630fa3b57262114f227acdbedba937d32397a08e0cb7c3e53583007aaad02b3f9e96e7315c84c2944d5ab60e6777e300f9c86352c59d1c454d3c3cf462422d7ee1f18e94d75ea42a012e5f81810ac1aec2968ac28992a6bf4dc2afb16fe4370fe34b911f164b753e9e0e6ce79260e7136dd90121df59d6c65d627e8a0622ed0e99302a0578e7b51455948355c1426cb93eea392c0d0bf4fbdb3ea4e8c01b2578fead52cd5578e713df545658c7eb88a21760daa9387551a9bf16b8d6dd15736e0e7985a9a066816b45ce1df385725e4802a9ab6c7e1e888b56803d5ea643aab063c09a73870eec2dbcef4af74dec00b8bac6d2740f3f0d4f7b4e006492270009d2405a25099bed29e4993b954947655fe09f4d3cc54a23101547d300938b5eaf32a3e7ea9b478bdefb5a325f62231190fa33084c1eda806916ccc055e936b6358a0d0f730fa89c3d7c31e586d6d612902fca0d7ac00cdf5a6d9cacd08329219c630119f2a4473d0ee0d145b513189ec147a12abfbb12b069484c19f5dcc9d62a5a823d9446657fb1de107d9a253c91d5137883e0c556edcdd892fc8b5a8a1023c7e03d1b59b84d134a75102d856b239e376fe9c41e9bee899dfe08406da33873779027b056d586ba802c4c53e824e973d91cf5c9fee13e96780079ada670c706c08b02c10e0d83bf015174cd81dc4c1c1df80eb0570ce51a236fcafcf888fb1464a7084ff552eba2ed9850b8b70b2da91e7f235ee02bb9b6290a06ecd46970bad9fda6c35973281fa7e9d19519640a0b3946e6c251a4933845eae47b948f6a81f1567770c1823564fd7c30543bc90e2064adcae2d7747705257d3531869fbb1aa074b3b4384d6029b0409c38d2e115a83d1cc774036f478d47d1bda1c70d11c6f476e6a8997ece30ebe59a09074a90dd44b2a1893ce760d8a98ab15a9e9b71010a613ea79bccb2adcf545c96f06c868b707ed0c0859a837c6cdd48df329a85b922019aa2d8958de8267ed0f3c5d705ed4e80f2beb1ccbfedf52d0e77cf700382edb8823402264a1c46445c4abffa00dabc4efddefea7cda0d4ab40817cfcc609a5893cb2d82e676931612b329f1d6214655c0556b6106c11905dd7f8abcc5bc885e95bfa9c3114651b6d4c704091adb509e3bc77fb21aee9586ec45f8b6cb2ee79d912593a1979292f613805167ad93a9e818a3346456b15e56544ed8eacb318f22d24c02d90a4a7a6bbc6e43b63147969e3afbd53e0bdb014894146aefded190d40c8401308a04d352a002d63d1a149ce24369c705a648d89b610bd7d0d2df4c62ff8bd77cf5806b6fea2af8a12483a421782bbda0c5885202f9984b0405fe717834b52eec15267cd476fa0d977fddd86bec5b679a90f4e4316aa8a2bf4c4d471762bb059b8f84312300e0b654c5ebded00af316420a0d624dca1b4b7a5f286289ec56b1d904c49cedd26d7d1d96e325861360f705c08dbad84aa7820b82cce7d10046886b1fc76acd73efb021dbc4c41e925b86d704bce317344ab6e02478bf1d8b306abb6e391c260038e1247e5e222a0c031d3e6c72dda33c764a11096b7615009e000bec5941a73882bd72d6f80ff037ced73d9d104a0638281f53d05c34bc77902061cd8196e7ded3890aaaa2f9ba24094818750a9d201b125adad6835243c35269f8658b124f104e409fedde0766373ee55f73ed8a69e37ebac7e9e5bec4168c16ac265ba99db6b7b3660d73ecbf3c38659908cbd19bce36acc4a88f04a50ab6cc091569fa0f780438af1764ad70a3c226e5f0d125745bad21813f4da08d62dbe023c0dd611d23b18ee7464aaa6bce3ee2e1e2fb8c846a4d9ee346eb0b6903e2e5da114c584837167a5b798c9b742e17d9e4589880a0cdc7ae13c109528e5aeafe5a646450105fbbc4189c70d46df5aaecb9bd1f39e1ed23a5109d6606089567e830d35243f68d629d709e770ad1e4d32f515f9b5efa5903b51dff507e66e4e248ab2ed1f66d26d9687f2d021426bf1d28b2033756f24e0fedb912892ac82ebacbe266fec0d715a992c28a631a7dc70343e76bab2f7e2f8dce61724c039fc4bc632dfa51778a055e6f23952e1883a07a31d157f2490fda1dd980f34e900bb930c75f4c39c901988a5221f7a6a00e01607156721aeb132489d3d61d1b383ec9f800279bf27e6a01c3d1e3449d4d314e6e6f95d300966a02d59f080fe293651c933bd830e2451c3a66496caa9700c646b9adf2b14def24730ea80f36173ac5a29c0957a3f3ca4e1567302c5244b38f32c4e9febad3425106133072d423d1d5b37178f16fd934ff20750295795881e689711005ff73b51a913440fab5e82f3c0717232c073152e3cf906f5d069aceeb5ac0e24d790f0ef6a4aec7f20774d9c2e26ed3665933f42fb952f464ec94caf0c0d4cab77b78b013407ce3559ab810169315fed426c2623513c905937612057b6f8a7d2d15aad19a89146ff7de736629a2192567d6b1345263310d8d2fdf5c627ff7c200e3b5123a2c02475e8f1fa223110a334fc1ba67a76dbea75b898349eeebff38a91e40d4db73a5c5bb72eb629f99ef37736144c3db90f1fecee1ab6cd36a90ef5940750369cb6f36e40c4c9e3740bc603c56b8dfd62dbe9f147d6ebd297dabb30333181bfd16b8499fcf78416ca84a4e81c03ae2330b02ed142718510c0ac29f8129c20e6c505b3074a0c807da0087468be79151731d27f70be00c528df02100508382e20373375d7c8163aa8ed551e951c22965da1ce067a23264633fa93ed351001b032dbcc1e18731a15c9160162b03bc58daf27c538af470f735eeec7db071b8d0cf74cd2419470d63554ea9dc9c1b31bf10e043a831f47a0c941a33fdbe27ecd3f9701083ed824c988ec3ef929cb62d1a930956277cae7a19d5f52be88dd4633d4849459631aee3b66b9b399439e203fa11ad550829adf6ab0aa2b4d6602585b2eb4279ac2e0f02d730a508725a158f2e26b74784ff7b02290363172feadda7fce7047eb9036a1fa2c750884101e7df35a187c9ae5e9a6064e39d0e77244cee68d6eda6927582c36aaaa603efe8aa8006e493a83918fd84615c5ef22c4e4b89b78c3bb45eb9835483857e91f97e9b2cc94a0db7fe2e84d6d03264b3a29436de0702615a8289d6fdc31369a4dd201ecbdf765d62462a88aff07b3c086446ed6dd693591572631ba66199878591c2b424a847e22c74ca53db4f484027b12c909f04cb64a6583e9277d9063b398d2cfd6e792ff761e81bff80b90767a3338eb6b7ad23131167563414f7a7aab43fe8cd47ea80578a6526043e7caa7b68aa85bf047b6cc6f61afa6180850044800cbeea891e5f4da399f6fa401fccaa1bd1480bf51d08e7bfa1a4c0d44f8d1b35fdb8e0f6ae29411291b0f974f59ea21e88135f39228e594735a308d1db8e6665673e369231904823720b3f3978b1bef51c51ef3dba059453419032380b44520621f0da04d8463e914363121fd6f8ec768232834f47e2056e2252ef29728fbcbda800d10ba066393e6a95c37a3d3e74384b1923dcd2f554b5580452e0f7cd147170109108b647601a72729352775578b331fe29252ada560e2749066859e4868c5f29e0b683cf3d22534fec16f4d68bca4e14abb20127a47c4d310fe4bb97be688adb738a05bd88e0e2f3b335dce26a006409935782e0f6438140349827c7822bec495de4866b5f7a61709256a1a8769f4a3ca9a449fe480ad0549c7e28b1156cc7a8c5808eb0922ef229dd91dc8730f08fe7ead6e0392017298eaf746ff6298cec1901fe05c0381fbc2a86ba239781529434fe37f94800cbc250624901c78ee288172095b8c2ce4c9b245580d819b7493481fa255fae30ca2f2dd7e43e5040b2d84cb4e82f842869098ae8da4527c76f22547d59292426901e1ff28a3ae7c71cb898a8443d3cbb1d887c5cb37065227e5d3d9cda843725c9a54c2a3cafd913f7daa0373f5791de857cf082ecadee94562c96c489b896c25d36069d789323a44100053df89224f6540bf784c4f0347e85f981e2280a29b45b256627885b59656a245bb279a9e16211051292ef2614b17424e99ed10750da855de5c4ee286696a5888d8313562dfd4895045b930f944581e1dad912a46c7b244ba8152a08109f39af71102be92497e7a8f10d115371d4b143dbf75894b3c22c8ab127c657cf691d81a92865a061f5524b5898d4690796243a491d3826d52c6e1be663a1c8506c61f405259d8b6534a62e9ae368fcf02b081f5a14acd2859dd7a46fa74ee919e1e04dde20e29dad62ff69418511db226e99eb571b4132cc0bcf45199e40c9a973501ae3513f6a44fc1582f496fd3197f00ae678adddb7aa6193c9adcb373f6208f14e2d5c3c82e47481edc85e0a6a67f9c68b7a6e04a6e129027c875f2852b3458871c04d9a299e49546479635743ed33b9d1249aeefb81ea69d494a25f89631a49b48a6f5989ac86c71a36fff1b0ccebf232fffcd2f75950b71ecb3301cff561a129908b31b542cbbc559a80fa2c28326f0b8eba369429f8c7184c76bf3747529745721b31b91027c74238ee6e26e45442ac205843833d00f36ca9e985728f9ae01811cdbb69a95338dea8bc6960c400e7c109712545c8963623049718a10e1e399f2e63c320d9000b265f803c472a369b41b4a23b3f7cf5962eff0e991143c1ba4e03bfb13cf63444eba49c2a32dbc21f0d1b75400dd14266e082957d9833d6f1338395daee07f33e645a180c454c1d862bf933d292bd4e560ef40a40bb37ba7354f2991b470aaa5f55c4403b79ffb6ccf7c2bd32e3b61606b32c9f09fa9a816332a38d066486ab9c6c48027d3097e70e91b23536838eee720ccfb3982955d81e48a283670338b079ed883414d39705ceab5aca7f160301fa18a4f46936b56e8e35b7d641c04d7c28bac6cd331110cec5d13b5f7ec425240c88bfc76d01859691942e06d3be086f1d4cb9c3ebfeace89c0a758b2eb8e9312486e903b72e7557b9e78f03526c8bbc1d301cf17fb129ee01cd6380044b35c27744e45a8c8ebf5d25ba04e5b3bef13712f0784ea54cf9e81659f92a3792ab9bbf2e9b8cef9fd0232885b568511650022624a431842d4737a258982141440f1905268ada52c26f3f446f4fcd90084e555e11d9e2652833e2a740be1671999ee7d5ac2574352019861fc2cfb5a3d12ab9d50b5dbac11117f8f68e5a8542cb68fb1f68b4b1b3d6799264f18979ece51bc9c6e6f32fcff6fa2178fc0e68569fcd7a6c13c2937cc214f11eef092b7a5781b7028945605e3c11525716ad6edad9374152153b02b31bc2c35bc4da06228d1a41124ec34c74f83b749e2321fbaf589989a33e921515c5632d5b52b7efa566de123484b29e6e3dc7967ece13b0aa53e0c34b5f92ae766524a872208e9aa6f5533789ea8064bb41d4900f5940278d97a1aaaf3e28c5a57cdece2013b6aa2a180fd0497cb05e0c04e9ab6c8a46627ab82a7987f5856679e43a05d152045562c43859c21b6639b492dfacdd456f5873387a1cc12ebe55db6ef0ae22d6a1bd36c5f3959a70c74af6216f2a4252d103299c4a767c0d08b4f00c4ebf19a3ef99b0948c72ecac72aac5a7b4e333e622d51631ea9cb39f0d1141311ed22134e4b407a753cda799ac168409969a7c1176d9380140a377c54a2f3f377a62f4b6d3bc54060135b1a647c05250508080328a7a471275848fa4e0e752f30bc90dae772a4502d713a31ff221018f4209f4d83cdf345262eb2364564da03f850b791b467ae0ce5e5b7428291f72be7b0158b0daeea77928985a2d0ebbbceec76d38e8273a2fbb3b5d9e78686e0cc6f646cf25bbb5259ca34cb946a84d922256436f98ae59fc98d44d00500021cd1a072d6b0baab17256e0490bac6a66cb9f22ae88032ffee7af189ab17e017e1604101abf6ceaf51e3dd430f735aa7d2d0aea83b6e54eff3bca3d24d3e888f894d662f9e504f4a2f90828df0b489a1b4d2b813491e6a0cef14d8dd50c43e53322e0cebdbb32982ba5f8b1a6bc225ee35239a87f53d7704d0d3875bdbf02a5b2e73700afa2af8d54f4540435b9d30647949df7f50b0ac044ed10778f0b507590d96cd75af1c0b08b59afc2842c05e213134de421b99bedc3b28afc7427bf4ca8f17f7cd7dfb8a7ff4a9ba1d4d66a7c1f37cb8f831eca3c02abc0ca0c984c38e9af16525065ffebeb8797a21abb4c0f5d54f490c96381f341a970212c2553a6bc356eaf4c41e208efed8a2d513ec69b38f10b570970b3b1743314e7bdf0ad9d3ad2afad64600d474618d21c3bc7ede818079b9433961f1d9c5ab923c0975ad5e2447eeb323a59b2f8f3abfd3b91b8503992351466cc19f32e1cf4bff11770d697ae56ed962fbec6b96e28cd8577e16c7f95baf8b64178df93ae42f36b0b088d952555f1d4710f84789266b174ba4bb1e8848447664b2af011f162475c71dbf0e48c74260fc33e470ccbdbf59630162c3c7fc6a2f2519c0b1ca36afbb8fdf242027beaf559c88914e5275a20e8053d2ccda9250d28ea32822341f5f3b4f51a5965a1db3ca5485ad113cd9c5486356d4885c943b91540d21b001250b78626630cc3d35b2b86f9a1cce01ae5ada34cc41784d4da5284ca892cdb6645bf8d881eafae33479dfc983e13cbec226af65d80bfccbb097efe5cab07bf836c4e8ac297cd41733932cf15cf1e8243ab44b21aac70bcd3db52651f45f5eb44d909fc4ea97d97ad2e63934bc5aefc844699a64a756fedc89cbd7cc9df723eac1ae03a711417093653b3dcdb5f976234d308a79357b73827570eafefc42d0d3cccb3a945f4cdf55350f48d56f9b49072173316837d8be12aedc7f3c315002c868da267a011b5d8c6ed1e32cdfe2a9a4ee15080571ddb7e0aca992912a493942aabea14d4097c94885da9cd99c7433fa8381933f619eff0411cdce927ab502e6375962f700d481ab9558ad4fb3a8478a1d2d5b4ce68a2d27ab6fb2d9cc5ea5bfd2e0499ed1dda157b936d51d1e052644ed4efd143ac44e6d9857c97bdefa30fb341d1021b8364f37c3eba5e203ddb6be0504555f08eeebcbec540bb8a8c118c555af0521fcb17d8392cf45ae8b8d30a6cd8b5d0eb33e571a43833869725f637ac327d049f8dd7c6d4417227947a29d7395b11291c1bfb173795e17da162092c853580c3d32890e100be9a2fb3a7c8beec28607b600dead8ddbc6c3dad857a0c5a19fa794ebc41424ff9883d1e25b772643b84f15906e633978a107fa81a0ce4c79d4efc2f7361884ed33b298ca47443ee148de0a14e2db04660609a3eeaa74899663e22aed68fcb2b0a69dd165e44869dbee6a129c7881e56d4562bb1390cb333a8d98cba5115d2912b8ea2d9e7803c0f1a5817826c78a3076875ae3291126254830ad6521fa3f2fe5d98129baab00df4f67a70b5fa0d1996ece436a18a386a5284245ad4b4e991885893669b0d9400e107b4cd9f19a802b6f59106511daeccc711f970003234f876d4cc1ae6eea455e13a3c4252e4b17f9ce8ebc0012aa4624bef848947d86bcbefb4c5e4e8125888d849eb3ed1aa1646c5df76c917ef2b73f450e2858052a51794509ef3d6632c4096f0cf1f2b6c4396ef7c2421dcde0acf8dcff0d7f6b471042deadf6e1aa76ee54817d2d79afc3428c3f16b8f8b3b377720c1616dc5ec65cf92c4fe91a3c84fa446ceb551e71217bcb8134f4203e5807c78613f8d5bc0a2102bc9d5a7c3bf323778dd7004c2793ba0dcae67beb0202764e123c1d7250ba446e16163bab69778e0f1098874dfe46403c4307f307b89111c8266d6a2bc60478c11313635c81fe0a9fd130528e9ada75c5c02b45b1322ac29c3b532ad6dce88924d81ae069cdb0358a22e5ac4550c069434d9385266bdcf11c6b0a26aaaf2fd6d80f1a15c7598e1f4465d1cd0a6fe10dc5f305ea025aa0369570ed2660e597f45293cd31398b8617f1c8d9f37d02af328d8f9679ce068ccf0a75864942f3d211651c8c7ffa38f35398ab65de7e6db93e3c49395896096e0cc6964c2df200a24adcd8562d838e6950e786bcb7e9877f701b826c14f52e285585f483732e3033fc6222ac1c21fd4532ae9a90cf6f5f8063383a7cd6f4d49764d99762688835c9c7ef198a8b720c2ac2044f0cb040ccb22f13777809564d672c718992a8455fc30199d9815f1cd637151bcf7e06b0e3625121601bd633a68a80ec9d43ac8b5ea2e6b5c50a8c8b10441ac63c6031b36dc68b6e4e9cbe3338c9e2b84689f10e8d2dc76f09b8df27bb74da08e7c104d61fc38daa5dc8651e4db80a0e799395cbe89dc9305b5176a02877a1a700dc0910528a4816748ecc30076f1c9012561c5d8b8089a67412c7b400b16e3f3ba94e31d43fcc41bf81db2ee4f82c80387b99c7c9fd510ef214f486c32eb281a7a1c29a8c047e850df91253c6f468a60e03bc1db96d581515ac4f595dce93f2da5faa83d9c93768db209569a4ee5c36713571857b60bc4011d2b0162ea179928629d2401a0cf22f123121dc57f2bdc3d5a0846d5f8dc70dba7850313b065e5c7fa7e3e3885b4b053a237d40fbc111e9b730f40f3bd87b6a46eb94f174548a023036d2c4c77263ddcae7e0dde6567c757dfc2d0acf02ed4fb75ba5a2c46a1be81a41d18f6568fd4ef8078ce0817e9548399090fe80afaf821dccda76672d84d798a108aed87b41659064a64c41e858600002dd2cf1e3f2eff53d0ea5b2002f8a12f9890ceb1256f140236c339d5c47996c35230909be1000d8c3a3c2d8494a8cb9bcef46f78b1645628338247918ec240a31021ec839d7061af92108a2d09d92d130857452765a5985115593b5a3cecbfcb9402b6b360184b1fba1c8c96c6ab454908d1addfdf16719f9f4b8f9a44d873b35edd48e70fc37401a76ac746dc3a45559351480814854dc2ff4451224a54d406890b45ed019bbe0e6e5d98815cd9f50407dd765e6f6b8d1faa81deccad6cb3cf71475619a0e77c9a0f09620fb3ead8437d44086776e3e09c090600ac2f80c9900a7603eb0f626c3a523860538581a57c98db8767c11600288883b0be1f98482f432f2c178720fbd3c47a87608823d9965369e4138f50c80fe96e77ba9004f4633b6abf7b39d1b644f9bf3e3962ba220e10a5324ab1fbfc88c806e3ef1c37040d6990b3180d5eb0896751a619a00d637d9a9ddea159c86eca6ae683daecaacdad448cd0a3e7dfc5c866259052f13b9f36df6f0fb74d645701a503fc1eb6012ee6e79510fb9ca15995f482e5cd2258274f8b27fed564b61b116df546929cd7c5dcb4105464e4c65cf08d0e983836870fd9703bbc2d5d053d95da245a40f723aab01226902ccce5904f31be140d874d29a769a9b95d7ea47e8dc7548ef4cc59ca6594856894c9e02da6ae0aeaa8a072be5d7246017ae23c14ad1c2d5b16ffdea2639c9198f0e1c212656d34bd70d1a9c5305362c7bbf7f32eb0101a63e26ab6516adf163b95d9370bb71d9f49444be1bbc4664a8b2428459db8b3ebf952dcf7ad66d059759cc3524816ec4c2e6626f9e5a7daf66aa0046bbfd0bee4697ef52f9e4bb2243426a39b85812711683093fc704f2b1a927e2eccf44af025b928739788856648e93b6bc9622195d09c34f550d26221cfb28d3059cf614b7e2d07f8748c97171a8b650e00ba9e3b20dda81f836f8b5e4f7779196b155603be566d709b0e8199cdaab8ae5584399d97e8588019f54f6e5777da468dc3be2875b9e1dbd13694628f5ee735e1b26d57d286b040c64c1f64fad3aa4169f4f3ddb52d40977c0ca850ac1d62c43038693c146d22b7ee9949f40799725836dd12c665f7b6947762c7be71acc6d4535daf28767cd57d80c76bd3f00ec2deabdad529a04ed679282692b0941de30219478b756b07ae80202b6e1b2c868a967bfc43f48889f3328639ec7cdd459319f564ff2254e62f1774469da42cb32e8f98ffa1f3d3105bb2ec7c02278bcad0e6f363e167b86fb1ae4ce42ab31f36fe43a10a8759c8a236dbd070409415866cc142d1e851d13176685a47f78a822617c9422d9706d8402a918a755e14e891dc52dba36739a3e61ced7c66a58899058ad06691730ca7807f994dc708d04570e85ea8c733ef13e0e713016ecb10b58375629d7336080906ba1be516d283d31529204810dad4c974ee19f5b7ad55c474fad34b290fe19b41eb1867084e33d1100c86425afda74ab0835b86b57a1f84d7985f064f2cfb8e132dda36a8c7cf66eb174248a39a5d32465794927a29fd5cb6c8b0cec83da4cf2218daba2a1a972833ead4c433c4eb6c54a0644869ccc93567ca79077315a6413afdd898974fb520f6302ada643922c5906646224954c486cb9844101e8bbf44a7102f85179b13e2519232102d55b823aa818574fa2a9c05ace3dfc0259ad2ba40cead4697611458a3621900d74a72c9b4bdb5f017b6d5902a207cac344c0136975066ccdae5f057bccaa4aa6ce9a2b52631be136d346e9d32298b484a4a9b84cd69b96a9a62a6a416a928112d52c68a5d416529837eda38f613186280ee86e2b5a91e9a04df24402cb8125a2cc74677e75e815ef830034c92824d8c12f2821591bd4a203b7e27618a44cccacdb6941e3aea9ba6ceb04825f5c66518ef0ca9c16d82aa5f5d43927cea8d98e3c945617199891d93d214c814db3aa46173ea4bc901f19ce0416116510018c8f83efbe0e224ad730e2488db8cbbcd1754e48d8169563c13f858836691b216fc92d65d2c5c0ff418d32e162c75f223efd7a65b26d50589fd949cb0afcd4d96172c129a99c7d4b4d640513712e69698ce4562432cb090ac8acc87afb28dc5c585f12dda6bc1845835f814861a85fe1eaaec415acb0d4e418111127edd024433924021885cec27e00e853a67ea4969308b4d8e524d6df1c41dd36cb0ebc81d3601d985215080f067ae96d05f2c316b81f9c3cc821334a31f5e95b81e563dcf3006066f4fc185f6101dfc7794016c48c402ac30c5ae8d18b96bac62f720920e2d4738b3b9a782cf37f1e28e4d1800a341aae13e86f1e14e842b7bc5de28085d569734874820660d1dec0269b0b4bb0804d6269e80e8d72ce53adea446e843967c789cc21810bd88e2c9acb2dc37b4096ac54f106048e41664f765f66b912147396fcdbd9942167eb2a2faab9888df28b8a2cf94eb302c05c3c3456fa8b79f3000c6f684f1569ec5c5fb9f47ad24e116d247181479ec537f24e0d45ca420f0f014de9a040769c0a2d2d7cf2382aa5d5aa1716900ccc117921498a412d85418e8fb2885890e20202b2d144154339878f32573c78e8d48a07e28748a585595d857cbb8adaa3750b8f24d88b9766350acee185ce7fa5d19af07e93f42147f3e483905387fb1b2810e80ff9461c99d3952c1b25aaf9bfbd9f963498da742d9a719532afb87a242bb61a9c0abaf693dec6f37894f8af2f526a0347bc2f10e8ed2b4f03a68ec248911508155284da31eb0c873c3b85ea1f3f9e7a9b63ee13802e41712998bab31cf87d34e77a1d58fe85cc343c3069a7afcd3461fa18f831e51d7fcbf9d4c31686a9094b768ff81cb25010de4b6a5584c8f2049ce463bd12ffb526862122527d51efc9aab78824348a5387d33a615467b412865f2d50b415548294fb860de9c20c9f2b8c94dcb07316d9189b0c9423952733e82c13f8ca73309cf1dc076c629b7099b34ddbca2ad8d942e23bdaa58128ced2affb3cb0af0b7d498fd4dc414a16b63d5403c7e2c1178397c4289895b59d10c988af3a0154d9a4b3a444dfbe342efca4364ab089f563addc0d066bef771368ecb342321f4d9022e7dc4980b3950b2a4d001c33f3f3f3f3f3f3f3f1fcbb66dc1b606425b48494a2553053e2534882d534a2925d9de2a141405000000000000db0821a4b50ebaeb0b3b0ba30b8dcee10844f0910746d79f586327fa74691f78e004cf4ae63953edf2fdb803a393a67b3b296d3cfdc191c2d06107be8275bc8a57a56df71b3eeac056124f8aa64b5ea25bd93d7cd08191a1c67472f7bf98f6213ee6c0496a4c3479fa16dff227074ee9f0cb9562da907f89039f74ce37a1c4e8267e0a0776843a6dbd81d1ef9a576f3a664c133770ea4e3c53b9993f734a830e1f6de057c3f3850af7d8c0e75c495bfc14acc4c71ad8f4e5f9f364d50e1ed3c3871af84c4b5a169476a650c2c3471ad894b3c97427897d416f12f181063ee4982869a448ddb2d0e0c3c7193829ba7f8e15e3c5dea0728ff830031f6f3bcd4bd4695257233ecac09ae80ada59269df5460636c9ed947941c6cca4a4a6f03106bec4e05fb2b559b6139a317c888173ed0b322ce6d2da6a18f8b3eddfdfd251f704c1c0c9274f66cb1adcba4fbec08595cc29d8f88d8bde0bac97ea53db7d29b9ab97a30b9cb6a80b993babe8e87381f3db6c651d354c556a0b9c722de94397bcd9d44ed402bbb6418a127b6c43edbcc24716f8924da76cef8b1b9e62814b72febf9cf3e7a4a5b6c3c71518dfbbb2ca149e836f56e0cbbbb7c2db24f5a62ab06a329750254b501b2c6bf8a002ff6759ef92693b71548cb7600a5cd492245363a6cb7329b0a6c5732949b4f8163451e0bf3a589af69126e242811ba13ff9758ee942554fe04fce8dee9621cadbc28713d893344535fea70a1f4de054bab6ed6a360b1f8f25a14bd6345171c2c712d8347effdb7feae727287c28819354cc9b2d6990d9a42f09ec78050f6ab43306ed3e90c08789b1ed937c1c815b2d49529572c9d16cf26104ae4a52f14c32d57379be08e3068d625ff8280267fdd13a6b47ff5b53021e90a1830f2270426878a7a0da6da3dbc710381916945613477f73d28710f85ce7a7303240848f20b06515acef0415e5316f75820f20706b8290255f5968ceebe3076ca533b9f35ecc26d3fff001bb25b775ff8f3e7ac0691dd366fa2b0fb87e3571d35bd4120fee80efcd26aabebe433fe71f3ae073e7d74fb2d585bcadca8b8f1c70173d649fb76aa6cd7de080eb9c04abafd34b62a87cdc80d18fa52b287da504a1fcb0015f9b337e09bd68dfa183c60abc08e36425e61eb5e0e4d8f6611d7d9330495a706e32a5abced57881c1db30b330977ddd7d6993e700c32ce0210b3e69f9982498249874518dad1d3662c1d568e6fe561e16fcd99d78fbe7e8c9dd5770f9c67f6c4708d1b982f354826bc7a4723615d71282472b387947b8a69c15c253c50a6edd244b5f52e67e4fad8213a14d76e65c4b829faae0aee4f57eda9d7f9d6a6c81f105ba1c1b381378a482353f29ad55dccd24731ea8e0b3854e2f2a34bf543b052743c71cc345b87d61e8a071898729181fb916dd73523af659b6c37578c0a314dc6ee87965ceb1a4acf88e1e7890825319d447695afd50268f51f06329b3e53799b517051b9a395addecb483634709056b3ac5e41d33aa5dcad7d8ca91c36980d15f6ce2010a2e678b1e75bcc5d2a41ed871238c1a1f403672f80d9fc027d87c2e326dbdf7fb05c7dfb09132ba98808c0714cdcccccccc4444444444bcbbbbbbbb3333333333abaaaaaaaac4ac260ec0141e9ee0e386924dae0bbae38e1c8c0f642738b94b8963729a499a638d2d2b4e70e28d09a5616295d4688d2dd74df03928ed24c6f8a1b9b58726f8ed387acf2bfe891253632b05346ed8a8b1c3860b2e137c0735222b3b674f63a2030d137c5d5051d9b5b3635cf0b804fbe769928a293573665fd8682f90e9d0828725b8abdd0aca2d0733d3568231d3fc7f9ef4ac0fe3944a9d830b1932b8500f4ab02eaa2af483661d194a82c72418d5747a2a8e92e460558d2d0f49b05b82b2d67693d5087844828fe659a295cc77c1aa1b1b9831e3060e2c78408213799bef339a6cba73c375207b3c821d1d2154ede7ac5fa51bbee305ace7e108ae246599d53ce349313aca011e8de0929d124346d1d7d8bac262047fd9c94a64d9077bb5c6d68c19c90c3c16c1c949f6136c9449d253abb155b20b0f45b0a726d888494928a9991a5ba8018f44709762256d72ba984a518d2de385175d63870d11d0400f44b0f5594c89da2c9ec9c1e310bc6a564c9e2ba54f72acccb0031e86e053452fb6b5e513cfb114f02804bbfa39355a4c679d245d283c08c1f9fa7efa4ca26ea898e382c68c195c7031634649013cc163107c5fe95839a5a95f5682e04beaa4a3216dcb14dcb0e12df00287311e81e0bec41cfdd625c9692935b6ae5c0238ba100120d8ab2b5326a894aea78ef6a214033cfec0d9fa7532b9ee2ce9cc9d1f18932415266a56fd7612038f3eb03148db2edfd04ecbdec0830f6c4ed2e79d98cc72e5ebb1072e73d2f1da2b543ce93cf4c065794e2797d0b20c257960c52e956c7e92f89faf071ed88e599ffb25a5b649e271074ecacefba7949e8d0ccd98a1638783c1c30e6cc6283a28adcae95327ebc046df0e26a7acd726571578d081f520434b99f66082d6bd002347183a6820073ce6c06e4e1763872e537265450e6c87793cb9b2443113e41107f6cff54a492925e99ad10506de233063460e7f4f4965c2030e6c124bec159d29c92432bac0401a083cdec0e75d3f192c45cf6218c87364b981d5cab163a6b724eba7dac0ab9ed09d9a57a4e50c1b38e549e86ad096e356d20dbf0a78ac81fba04a4966a1d469550df050031b93cab1448b4e3963d092f44803df5d9db962d09d6f678dd51c3bc0f04003773af98f697af493598f33b027a9ad899697977f1e666075f7af723b6560949449f60ff692815342d9e6202f8e7ed5699c89404617189071e53106c692e8fd317bb0cd39f710031bb4f869134b9f345d26133cc2c0e9f2ac71fd045d933be6071e60e04d0c328fa8f5bcd3e81738b9a184e68a7b4973a45c7878813dbd9704ad9e4dc999336346173825d236ebafb276656b6c798e5205021e5ce024cf2193ffb961e83861e8581daa436d0bec6693d24c9e92a6ddaeb1650290030f2d3056972b6bd0db5fe2bba372780b9ae091054edacc25756b700b964dc0030b7cd2db6672cc5b4a1c67ccd8815a06173276e4f01a68878c2e3040231185c71518a54a9fff4931b9dde561052edae7182c6dca3a757a54814b8270ebd349cede830a5c9e1c3fe851d7630afc6b5af4202d58caab794881534f29c8ef4b531fd419333e470b3e470bce230a8c7fc96ff92cc98e1b8d3a04c6609063c70d4f1a1e50e0d63d9652a234e5a07722038f27f0ed1ffdd2c81a1319d7e1e1043e8fa8990a2665b494770f34818d15f34bf3955da9556e0b1e4c60b38752f2da523dd349f21b2ed8c02e818bbb39ea5be3b72596e50987064a3dc04309fc77ed5b2cf5993024e001195e782481f3bfceab27279d4b57e82140de82047920a1143b895d71dbbe8163c795271c1a404f386ea0f138026f298d3a19aa448f414863ad051e46e03cc524ae6a924451314911f84c62a73852e4497a2b2bf02002bf974dc6249d8a6ad2fa0e1840603d86c0269d4498320f790881ad1cca7733ff444965eef00802e71e439eaa4ae5619a0710f8d7eb28aac40e8cfe62870d08a8dbc8b123f1f801fb7da24f939c4d4ac1e2e1036e543765cf6eff8de9019b356d148dfd27cd44bdd091e30156e8060c7420023c78c0bb95dfe6eb33a5c6e18566c06307dc8feae74e99aafa4e1a6084416305387078613a20830b191890d1050664245f78e8801b5393e458d09126f546784302561e39608489b949877d92abb62b64c38b0f6860c68c1933aa3487070e382b61e974e692f53909251c1a484a29c0e3069c2463ea90513f95144d8d751b3976d0004387870dd8cb6157222c892965cb468e2ef0a4d5400968148000b560e4bf861ad1517b01c617be2e20002db84a9e82261d44e6bcd52cd868b7a2363fdfdee6b2e03fa635ad0b6eab27b1e0bfc4be7cf9d3a8936b1ab0e094550c93b71a02bc82159d21d4da6a5cc125dff26f53c972e88f0ebc56b09fbf32059943e64d61e017a8a1a329c0c52a2008c00aeeb2a5aca27e2559d6c36041dac8d1450a6e145b05777fb207539537538cd7d0d114a8a1a36b7021430217804004609003c74934609003c717fe852a1895b152b66cc994e0a754b0d943275ddb1c4fcda182534b5516f46ad24a5103014ec1c74a15938850569a5308600a4654de24f9bfc7bdce4ac1de49679268450a5e4c3439073f7f9336aaa3e094ac5efa2c73060288824bf27574a8b8516fb71a5b0178a6cd4912370843d6c0eae8bbca54fd18be87a881f3d2ddb234e2ea694c03ab255ec9aebbfc178342d0c06a3ab56d329afcced13370728ddc934bff870cf60b42ccc0e5d85630415fbb83959c1942cac07b12a11945f9d6889a0c5c07ff0fa53dd7a4260a2163e0bca29be0b1ee3f96548818f84f2afa89792a9ab4bcc656ad20240c6c8f92ec2e9b944b881f0206de73bfb65226c9f42505c95370c346da17183377f5efbf3f957e8817f84ef95209d6daf0c2461867cd8b902e307642e524ed8fd8dbc80546ad29c124f5d58c267323640b9c498b157b4c6953add1021b2b7b2c6152dc4c9faab128240b5c3229d6766aed2d41d740173842b0c0667cf193928bbde6ca15b8cd7942b5c656d110865881757bf7b198c14e4dab0297fa544c7e6aaf2b5f41ef402ab097d1244bb13c546a8ec37011320556533211a1e3c5b86f41428814d814a5fe326d4dcc921305feac93d508251502055e7533a7979d12a6926a6cdd6aa004346c843c810f0b5adde7efc14c12198438813551fd39b606b31c643aaeb2439ac0a7b191b5a29398d3c9d4d84aa57810c204ae6ec34e4b67743764097ccaa74f7eee99a4aa33811025b0d92779102594655a0dba312124096c78dd874c299f20c3d5a251a2084102aff7d7a2a4d06c32b532849023b0a76d152ce3567f072dc408dc8beba6713b2da6de22b0ddea71db4676fa1286108193d33ce7c83239ef9f589a154286c0a9c90b227b733529e92e2e44086cca0ef155b14b52fc840481f3147553abaab7df932004089c586b6d3a5fa94cf2d742c80fb85c7293d434a549346d213ee0e26bc9b9c163fabb6784901eb09f4c8fe8adbb3a291ac203f6c4d1b93e6985b45b4376c09d5ea59c19b5438f4f85101df06aeed6295a76707d4372c0953631a5352184923d592138e042dfe99dd87f9bc52284901b707656e95337748e895e13426cc0f525514157ea8d9d2a4200a905aba173de3b15e2a904a105efa75ecfd426492a5b60235d04126395ecb0718c150d1d581a0099051fd54ae55ded881e9d2c18992fd789ea68e5ae0189056b23ea5fdb2b4d3e91192c3849ea6e922599e8925266afe0fb639d12d7bd3c7f1cc415bcbb284bb14a1026d309d28aa41c2d4a678b19082bb84b61317f106d92524a20abe04d88570c0b6d723e1144155c3e49d25c39345f281d9054f029f809d2cb564c2629082af8b8be9bc5d2db9af47200720a56531042539f9c93b43177a6e06ec44a7b69fd5365ba52f07ebb655d4944e42901210567dafa5397f66c4b519051b0a1b671cc6c7f028828183599ae5395989224e9808482ad8f7a52e58a3156b44140c16b4aade9730c99e43c7d820bdd26675dfe5608254fb0e6c9b4c9c9f74e304a66137443e8a86c2a342a0e209c604b99c7ae6c727eb51064139c9b5e937292a2c578d2209a603b9deca263486d137c904c7052c5d2e5bc26a893e298e04b49db49dd4a3493722ec1e69c9430556a4d973c5a824fea9b695944e9497d005209c62421eaad047dba6d024209f64dab89799350d2e6249049b0a25ed3785a7bcf251806104970eba573eabc22c1e658c942939fb2561b129ca08492d3833c0d3a63401ec19f28f78feb2657b909e20836e62949beb13e79f683348253faa7c1fbb45d7d1223b8a424a59464a74f96de16c19e164b3d661acf6366042902104530423fa44813b4e6e7072411dc755f95b85a9ba4772088603d29419976462b793ac821f8dcfcd7a53e97e835410cc158652ce1278ed02654cb0152082ef44a5ab3db3095b4460021041b42e67cf12d08216b1d04173d87b4d89fc3648b0a82134c34f7b42b3922d225800482515bf28daba81c53a84100c10571bdb1a0d7e4d60fc81f5813939c63292dbf6c27103ff0fb2726c6f22ba9e424903ef0961a4deda3c7cd4a1203081f580db6753a28b94383670fec683a93d3a8a0e3efa6072e4969c2d54e54a8dd79e0d73494b49499af19eb01081e6c4bf93f56bf83dc81cf2c4aedc9a7eb76723b701e43e7bccc2ab1540aa40eac95b855922d2a6500a103232f9fd04ff7b7963b7360fc83f61d536953d30e220776c7adca2c084f96199038f067e126097e7afffa82c0818f592d66c1e46cc24b206fe0fc3d09d636a2373c0fe206d6726af3d09f03d206fe93d2d9ee7d04c206467b92ba83529631ef0eb2067e549fa0d4d5e78a9940d4c06bad987cff761655049206c6368569e71a65a9251034b09e4ad2a6d1be34451be40c7c69cd5cb174cb64e541ccc0b77bd98a080da40c5ccc186e79796f97191032b062b27753ac1434d7053206c636e756e99a206260ddcbc6d653f20d250f12062e65b013f4a5808081f18a9d1d6292bcbf22c8171821d2a489255b1ae926205e60444f47484fbd9be40fa40b9c5417355e327592583e08173839c5b8eb9f2b732c71902d705f2ae83b3167fdef18102df023fccfb4778fac1202c9022754788af9d5faa4a403c102ff655e320559feb93d2057e0d69448eb4ad12ac904b1027f92ca9dcbb24e8e390b5205365f851e4df993492907a1022b6eed21eb4fda6c15c814588d1b74d2a96a494a2e8814d892599434d1b5e3a6048902573ac8764dc14c73080205c6f3a40e1aef74d097833c810bbd297b5bcaf74989823881cd22a694a8379e4acc9afd024813f873d571337163ee8d099c103298ec24ff82f6ec122ef7bc4b429418102530f6a5ab64d2a99485079204b6c4e46241c5478fe72048603563c6b895e4b43311c811f8cd39bf9320db739d108811b898a497b8313d4faa0f5204fef33265979c0242044658fa9aec23646b5852ea7205902170d163c9d0fcabcd260e2204eef48856fcbbc6ce3825800481b54fdeae65d24060a3c89482850c2fe5c11fb0276e66cb1ccdec2fe903564de70ba53da8fa4a6103480ff832716da345936f9f048407bc26514c4a62c58d1ba21d301a94a57aaae9533bea808be6a66d994597c620176700c9011f544a9642c4fb52c959101c70da6326a6cd3a9af208e406dc9694f3b8799d60270b6203aefd34267559da2ade32f8a805234a10e9a3f4f5b8c78c166c4ce7af16840e2ae71fb3e0cb77838c7b425334593359307a82e897d669cf5f2d166c2a4188972c76d2e4202c781d9dce3b9495247ae7081faf60c33359b0afb0e4a5c415aca6d226fee6a9c656f264acb482d3dad9a72ee6574997159c54e25ddba9a89d4475155cdef4498a625256bd912af878723c2b35799398742a18d11b119a726dd589b1c1072a180f2528d1abb9e4bee829d8cd1443e80a953b53304a67362173a5d332d15d29b8926d5a941437a8e7e48c19f7410afef24e3c6976a3e0d2dec9eba3df53852b0aae4d2aeff60c77d53e3f42c18f3e9354e82d0a1fa060cf3bc8ad94c4ed4fd19ef0f109ee7ee32779738b14d33f3cc17a9e246e54fdfed47827d87c3e2686dec9a3ece5049f97f637985fc85653f0b109be343c76dc98e4694c6a82d78c19f2d49deadea44c70a759a16a19330926731f98e03f9a0af24e560abee9e4e3126c55096a3cdc37d7afe6c3126c9ebdc6246f8d4ab93300f8e0a3128cda67d666d2d9d9194a7022afe48975aa94a7a04970414b84d2ba174970e926876793a2f4581d094e0c256576b88a8f36f10312dc9fe8901b43c96b2aee81085040461718f8780467975ccf3b8f52f94a4770954e6bbccf39faa5d408ae37e8d86b991ad25746f027a3065daedfa7ea5f04a39b3f47d80837f13741f8500427ebe907a5d76bdff21f89e093a4bdf29ba67ad613307c20824f263b3fe84a82a82466061f876035b5a45c929834049be4ee0b96ba2ba89f588e8f42f0a3b96282ee1c4bce694270c2b22731e4497e92ee0c821d9d97a4b8c91204d79759212c8752a28e3e02c15eb4586f929794fe621f80604fa969922bb3cdcf22868f3fb0f7bb2926334fd228f7c30f9cf45ea6534ce54d42eb03a3679b466fea09bad37ce057b475082bb3eaeff3f0b107365f4a95194ff2e825f7830f3d303a676e34491d59e034ea9a5ed96ad23167c2081678eb4c72b33689662ea171c3bd28a3c0c815d858b6a284874ce9ab336205ae944c417f28ef8a7b5f1646aac0c50bfffebcf42626b19483112a7039d5ec4b92743c491c4561640afcc76c2b5a2c68496dd0a85a058c4881f33f13eff247cf396876e0f0e20333668c4481fd3f9992cc2def2479471881021f4398b292db3ecf46372a50e65fd8d8813a76dce81da68112d0e8c0c813b8644a459da76c4267939f3e67c4099c32d78e9ec44ab469780e3068b4292ea030d20446a9182dc95e2ae764a6328c3081ab582e3adea97c7de112785b913189f294db74a6047eddfde4ce1d1f4902a3ea92748d23bcbbf448e053ecd0187a97fca37704c6af94d66e5bff3b15b31123f06756f2d334d569499b8d148193a477a592493d5e49623642042e8a65bbffeb85109ed9c810b83241bda37dd28810d82093f29ce44682c06d926fd953c68b07647021a3744006073020c30b30beb0a145851120bc193f25f3ec6f02467ec09a96a9682a949b94f9111ff0499552db1e1644e97a708dce99fe3f4d4678c0684dba6f7145efc9cc0ef8ffab35cbed5d4dc9111d70b2f2df67878a99e33f9203b6cdc41c3589a3846b3e8203b6f289655e9f3bdd5f6ec029efd8495bac4bfda4111b302e3228b52dbd5d79ad05279dda51c124c1db829616ec778c3925d939665dd32c780b4a94249ec9d1f28964c1e94e25c913a54d9d9c24049158706a26c89cc4d3ea3e4adc20020b366c3fe6242919755b1779057f1e739257b307197a177105239e2c053f17a159b95670399fbaa924e9ca9a31ace0aa634952cc1f2aa8a462384260b60af63a2df5070f5aebb92a1879c12bba25994907110122a9e04453eab65a524c5ab11a5b5ee88e1b364090021b10d85d80082af83f9946ab2965a5367d0a474365cc41bf6e0a63d075d2c9418958e03aac8c0e1d38c0b841030c1dfb00915270abb9d2e4a47237291952b05e4a926ba546fda3e0a3aaf8e549c9b3884c4414bc89414bb60d9d164d0f05276f4cd371ac4a6f06147cec4e27d8096d7f79ff04df7d6d172b2f837bef097fcfc5d3dbb49d60f3b64ee54eb69f640a27f8dc67222c6aec937fb309367657109d43c9346e8968822dd995635a89f34024139c2551547437ad389e0383304e950e114c304ac65cb982f094626897e0bb723a2966ed7a96204bb077fab1724c2957a8d10b2295605dad2d475357955d94e0436da63a419320e37593e08436b1e26fa693992b4970e6567ad29f88dad691e0c41dcdab2994f4316d20c19f0c4afa95942b8eed568f6083ffe8346eba9fd28670786101114730aa63671daf522aa5228de08452edd59131956a160021883082ffdb582feaf75412fd4570624934699274667551041fdcc5f54d4a892422c9436b92a4972022386169a48775b6c6d60a74f40e1a45450ec1277d16a49572edb7918821f858c1ba0465c104ad9e4821f81c9e49b79e3163c68c19332cc70e302e007d1021045ffe61b6394c859a69106c8a274f4ac54c4a6cacb195dc101104dbb9b4d6d4da8d12992f2e4502c1859b594aff5f7b2602820fd17e5d1e7344fec0e80a255b2c4b7b7937113ff02777ddb665c907ce90b7a00a05227d6064aa79501e9354a6e303d72eda7a474577e8f019227be0d6a44a4aa724738432f5c056e570cd373efa494a1e18ed29dc6ac5deea4e2278604cd4983cf6df49314f022277e063e886cc9f992144c60e9c9061639a63a8e84a8bd481fb1dfd983c290b2ac845e8c0650efd9f46ded76d09132273e04dae5cf7627d394329072e3c597b5f92fe42261189037b7a92aa4ca9749efae0c0976c17615ded1bb8f3f5984b08792525ff460544dcc06a8ef9796bb00d9ca083090fde962688b081d179e2174b8a5ae8510ad6c0696e4e62e5abb07a7705226a602fa81835694f5f9b89a48157d11a2c8729713df52268e0840951a24d1693f47244cec007bdcd6b51bc1d889881b36c32bc564c7beadc8d0de840a40cb7b706b59a31546acb0422646073ae5017844c494c25141903d7f153ee1ead8841440c6c9049ded41bcd7a33b981c30b14062e488f49d23415113030d626ad680b152ffde80b5c7bcc3993bc1acf93b48817383942070d99bbd39325d205f66d93943175fc4efa225ce0949f49e2094a42640bdcadadd96776cb9f440bac962625fe685f240b6cea606a3fbefdc7d86281add0e5e935bd5e67cc1538d1374dba059d73fe242256e034544e1d258b55e02eff7258342dbf0a15b8ab116afd938752fba7c0494f627a4d8ff6779f14f8152d7da75496509e178902bfe9aed9943414180be939987c9b4cac7f027f821e0b955a9e42893a81f71fa5e43c3d7ab9729110690227e9942cfb459a9b740e06c38d20c2046e2bdefa89ee7a495c02274ae5d3b95371105102ab3957f867708bbe9504882481f1ecf92aaf8790c0577ac96e3a05dfa4d923f0f1545d94e9746ea3891881ab1226d355a7cc5a2a022b768225e9447e951a79181e46074a81f14518f701112270eab3d9999d14bfe47c087cb6563549fe10d59d881038f112d7b276563c516b6cd138eba29c0b4482c05ad0bbebd6767dd25b80c05b4abff9dd627ec0ee2753563af24c4ecd077ca859981c6c349ad5223de0477e120b255e72eb9b079cd8214aca41acf49e3876d0480e46183872a4c0ec0b223b60ab3d9b742533a8285a111d70ea82cce9724c911cb02eba259eca5504076c52214c87e51c3dcf44e4065cf00a4f9a2ec4c4924403344ac4065c50594b49da64599d59b5e0e2e5787e4ed26a0c1a5a7095f25ed72ad96f36cd823bdd4c31495452c932b2e0ce6f37849041a9b42a167ca724e7d1793998e91c2cf8cda86dde39f50aae04e1a35d22a36b8b5cc1a6cd93326bf41293c55bc165662fb71cd4892e26ace04be5b8f665929c2ca956c16f7fc88cbb2721aae0939c52704f96cd4ae808084905a7641b6d265f2abbf62d04159c1cede4fd20333b05bf59a25547df6edb68aca03dc7c10a3105e3267f7d0efad101467b0d53424ac189417ada5495b43b57d090828f5a5d6932ea08951c3876d83856551b90c1850ccbb1811d36ce8c193a4a0718380e0081081905a7bc44b1584ac995044244c1081d3b593e31640e35148cf8dbc96b5e5935594240c1a8cff94af4d8269fd8d02f423ec1e924c5a01b3a595896e000e38b0a2417e209366d642c13935452592aa413ec9e0eb24b8f9a98e9ab513e08e1049f332741fe7afa1a5d7283904df01b326d8ceb2928995417461817a2094e93ac0e32291bbd67828fff25e6f7ee9c4a1d078cce8174d0d8e13aea30c1d79ffc529d523e3d992d845c82713d4b7207ed362b395222c4129c6fc892bbc6b47bcc1a05cb105209f7643cdd1b7ba504d7250669974f921d4f7823089904571654122aac24092e074be26e09ad26191209b6e44923543bab5d8304a7ec3decf4d5e61b132fbce8b39047b02bdefd416dc67fb110e2083ec52de5eba714b4e41ac1fb78b624e3a6d1f91623b85ce155c9a40ffa2f93869045b0fadef6e9b2e296e4d801c6d52044115c3a1d3fc90c4f2278d379fbde3e8392564cee397e71f1108208aea3f86de6fcedaacae042466a1c5ec0a03920a30b0cc8f800081035e410ac95a929ad7c31dbe79ee34b8821f84fd2ff3f29e5ef9a2904272839c54bdf63183820901082dff7114ace7a41c5d4d4d81dfe811dae2364109ca4e47b129ec55bd35a632b69c9e862023770d8d091642144108cdcd0ab6e4af6b38c699d2024107cd226c9ff26650f1d5f40f09992ca9f324f897ba5903fb0f79e732d6e83c6ca22c40fbc9a88906e2973db86fac049f2f2e4377925f7753492076008217ce0bf55d77facac935f7be0ea64e8c5248dd6beb81ed8ca5d62ab984e481ed833dd49ceddfd55f27b80103cf0a295fdd43d446b2d373c05090b5cc7a51b9d033550021a0608b9039b3aa2af4aaeec4a991d3861235dcd644e265836a40edcf6a9f1f134d19e736aec0242e8c08f4c9517ddb3aa2e3b3ad148681132074e2631d6e7dbe89f240103478e1d373c0b04217260c5cfcc4e575d7d65438785c481afa44997059587c0817135d1494e4942dec07d901de4e8896a27ee216e60f307f5516b4f4e367a1b78f1562da56971e4496203db39145360d3049d6e7fe4c7cbbb14d8246f650c32776ab4ae0f5b44815dabcaa75492448bbe50e02e699998fbf33ab7f5c3164fe0c4b49cd378eea6d37c0bb18513f86cb2880ed31a628b2670ba26e969af249a87568db00513f8cff1e4c64ff26ee479842d96c0c818edd449aac3a4786fa1043696f86b9524e675bcb748022747f5698c1b7a19d343d802099c586e9fca63660dc263e4b0c511d8d3eca79292227ccb320217164bb492bbd4af74c91cb6280223a6f249525afecfb62c11785125c51b132bc7b43304465b3229bf338b6606e3b08510b80df23ebb8a12c43e8b6ad822085c504f9ab13707042e8f08333fc93ca6ded6b0c50ff898395f7849abee2cfe610b1f707f3a57e83599bc2cd603aeec3e6765cd1e4b5e7580d882077c6be924f80893cb4ca816b1c50e38d5e26e62d6897943c66ca1032eba656df4a8a92d72c0b8970cf97fc2019774a6d9252553ec24c9c31637e05c94d8b5a5dfc4949585c3163660a4464b4aecf42765df6ac1a8be961c933689430c5af0319430257e4153854a67c19b5dc59c041d2ab4081559f059d4effe831c33497f88110bd6bc32fbae67e41003165cb6d560ffbfd104ddce21c62b38ebcc9c2429590e26c96c2162b882319364e6145bef24b1c410315ac18ecc57e2c760d2f7d9a68718ac6054ca669a7c2fffd7b40a4efaa66ac998737b88a10abe3c4b8c667a49deb4993183061d62a482cb30793e42094a5019d541c4400597e4a524b3aadc53f0296a9518c54778ee874288610a2e28254f34287d65b22d051f474bb6fe280b26f7328418a4e03c7385d95f0ead76828418a3e0c7a3075d976a9d9715056ba27abab455ba54ea9424c4080527da85f6f3b4271662808251692cf5a849e525f39fe092943c6f66a9df8ab966218627b8a0b256e3e70c427fd7093e9bf04ed1cce604afab293fa54f6d824b2dd252d4a0793ab99ae0649f7c9332fb67af2499603397ec214d664d65594c70d29df6d839eabfc7cc253899e41425c9e2b1045bea312561e9a904ab1b83be4cd2465fac94127c6cd512e3a68b08bd4d82d315ee6f7267d125a548820fd6495487c893566422c1e9cd9a66213d4870da6e2a3b43f30876f304f5e9638e88751cc1ddc6dcce964d95fc7723f8d77437d2366d5aaf8c605cb77b7cc562c9a32e82d5689ed55ff406933645309642a96ca3a43c425689603ce9e7fd0b1d84ec1822b8d2e41af4072145457308f66469af50698469480f0dc197580c6da1aa4d9b2c13621482bd7ca1253da5c89c3342706b5d82c5efb73421c62058dd60da74e976da540a82d35ef53bb6c94070fa549a97decbb04e4936c40004df63799954e9119eba3f70428849fe5f39e6585b1b62f881735bb12ba1929439e37de03296bc5ea3ff652a1f58d1b016f51d4f674eee813b7f8fa6fcec522a193d70fac1a4b2984e348fb13c70a362d0e08113799a4a4b2a4950a68321c61d386db173bd8f6707f6d34493f288a6eb3fc510a30eacc831254b9eb9c7934c3ab029ef828e1633f5353607368a7ea59c3669b60a6e1562c881f36c71bcf289a6a33671e083953e55925a4f1a647060347efaa44caca43aff0646b85572f34eb566273770824aef53624cf1cca936f025ad754ee949105531fa84186c6093124c99d826091f8d5903a3564b7b727dcf93636a60b5ccbecddc536a12a5811bcdfdb5269e986e3368e0b4592e491093a63b3d32c43803f71d3c86d624b5eb5ece0c6c66ff8ca1d9c460ea2a0317cf04b13c42979c479864146290811f25e8b4b4e0c924d1354a883106de4b9f4c31de7f974e1203a33cada8a5b3d232d125c408031b2fd3e6d0dfa91362808137d9632e257ddeaa9c4a22c4f802fb21349e5decf3f15811627881f114f72c8358dab6246a84185de0db2fe5a0cdd3ea24192e7029b306f59f24a562564788b105fe6209da048d61c9e2470b8cdfe95c92f0dd174be2861859e04cd649e9b3aee3a6c71231b0c0dde5ccce1494da57a56a0c31aec047dd3f0d55fab34a97e38b2ff08b2f0e0e31acc0e88d973b7ffe50a53655e0372be8ffa67c21c5840adc9e24a87eaece14d8ba34554bd9848f47d510430a5cce9b26a975780cba87418c28b09d53cb62c7ff24e9090a8cf0befc62aae79ee24fe062a52456b5c61dd7248daa440c277052eca4049d4b970e4d4283184d604bc6606352b794abca0446c9d1bc595967aee925f0a5b1322679b2c8d1eb112246b9a3c450524e6d8c24702226bb954e71bbed83420c24303a736495bc279fe57b04cebfa4dbc624c84ce749a810c3089cd295634edff00e4aa7085c89f4a0844c4a35ab85089c5d4e3329497286c07959e79c4dc874492b04fe73bd067f4b0d425882c09dce6c31d5de66ee381058b73ed1774d2aa54ff6077c5dfab53c328d92bbfa80cb0dc2c4c27fab37c4e8017b5baf2a96b2e358464b183178c0969e1c7aa6b6bd5bc6d8012726afd419639745fd74c0db474b77693d093e1a40c4c801bf3eb25294a6d7984d38e03c49a9a62e9398ed2737e0929c9ef2e653d9de7362d80037d59a0b23b5e05c3f586a06a54d76148c115a704a5c3c9e49df66ea2c5d1899056ffaa26b769eca417e881159b07ff95f3499e82daa0ec4482c58d7b81756253b9ee48105b76923ab5a372b4bbc6e1123afe062992064501d74969faee04fe992a2741ef1b73c69051fb6a1a3944ae2fe28fd6084159c7532d3493f29f525368cac82db5b935b34b3f472aea882bbe4669ed34c525abd54b0266d9e9f66aa744907155c7b1e9316f4ef2a37740a4e94ead74e29e8949a02868d2e6e38181b983143071836762460d8d8610a2ec78c253d9f880c25c94ac12731a5e4cff6586e168d1a2105276fd4992ea5e42035350ade2afba9cb9952faef44c125eda2ff73b69e983214dc6d0521ec4b52c15240c18e4553c1db7457924974c461e4136c6e2b8f9f6952cad73cc1b557b7e6d4563f49a613fcc55e939ddfddc4d872825553d1314df946fde026d8f4a949c66825ea32090c34c1afe71da593800c2e66cc30c34826f8ec3f3185062fddf46282d113f24285aa2fc1e7d1dfd3d48f9bf1b20427de6927515335c4de4a7016bdb47ed4bf28958412fc5b9e3a359a31774e27c146df1d5d258fa853b6241853f935e88c2a3a8aa8e3059d01195cfc6124128cce3eb937afc91d5f4cf986d4dc5c31f80836bd8999289ef729551b5fe4700ecc98516a09238e60354dd59790d65e4a4e2318eff426beaf6104e79ed3e4887f7cb7ce8c457049e4c7ef6c42c957952258514a146dfab92e5d4a2218957414db3f3944b015af83ca943e876093d2a7436bfe7bcd49de0243f07aa3d3a98ca1f3a95821f824a8b5fc4127594dbb846033dcc22d637bf0dd07c1c8d896a2998a82604495ec2bca3bfb89760a6ed8b80b04236fdfdc2f37e3e6b407ec300208fe74991cf252a9c56effc0a9cb3eebb65a6bf3fdc0a5e0255f34c5f481bfb49644a754cd988174d8f8820f5cdd58b8c8b4d5a4973d70b14e4e963fef4ef5a5074ef8254f59642e3d7193c1c51446f2c009d9797bd96212d3950f8ce081cf3e0f153f4c77e0ee738992ee52748dcc0e7c7d7cd5ace9972aa97560548549aa953bbf9748074e79ec243737556d4be6c0c82026789e6831e5adc881dfd823b408a5262599140755476e301d627a61a3777804b68461040e9c187409763228dd26fb35f2063e257d1e84ffe5067ee3973ef724a90931dbc0897b66525be6d0983336702abcec4368e54a9359035feb99a48c6742299d040b236ae0045161993e4a4893fb481a788b498aa8051b0d9cd0961f2ea6b5febc1933fcf451c2c819f820ac343be91adfa40a63c40c7cf4d01ebd2e78688d96811be19794d21bbaa6fe1132b07d2a25aff78e1a46c6c06aa5784a5a08add1332206ae33fee6d5a04ffab8eb60240c9c9c94c8e8a52333693f63c68c199ae061040c5c3cf1627cdf324175a635f2052ee55119338b4683112fb0612294ac558d8e1a8c7481efe039c514752e706523831ea1acd3e9d60a235be05fc78408213c2db0166347cf708d0d46028fe8a8e4b2459240180a05024110c50000c7cf1b00f312080018281e0d88e3e1785415a80d140005472822543e3a1e2820161e2a0ec4025130180c8482e150401406a4208c82408e6599ce7c008726e16c49711ca6e9e4ef7d22f56cb5cc4e1e622e6881893ae7cccce38d137e292bfbbc89cc53f5a42510de8119e21ea6d9ec397e1271ae97aa2e6588c39530a533bdab909690bb061e4e9192677f0ed0bb90fa13fd106a70b4c0b22c59a8c1305363e20f43eaafda672234a41258a3a30938a9a625b2ee84c35302af2af0ab8ff7accdb608018d420c6eb20314591244934596bd21c08483c81f8af581eb94acae043218ebe0bd58ff03c65f7403c1374cd97a560e4ac7105ee98cf0d64198367cb61c7324271902eab87e0b3240d42dcd63c2facd9108a937069eacf85775b5e8268cce6e35d53532bb648bca737bd9a47ad7096fde8a8131b789ca4faa1fe128f1b63e4749526cd062144c33052db6622d2ad39d567dce95dccac5690b00733a8245b8bec6c8d5011ede93b4e83597c477f2a6d9aef0ccb66be47fd9c9ea964c36b8ba4f400a4589e57af3868db651e3bd6d381d44fbc3619cfbc734d4b7675a3184aced6a10ea7ec2b4141e2dc278740acba7cbf30928d308dca655cc650bd904f8f376521463b6878445523a4c14097abe641c9828867200837add8d2404b0e2c5990e0497a1109a084b23fa1b50957fb1e641c85b0c27ed41d72df2aa0315c8bfc2a99a07cd9053684329d0947122b36a21bd58b1e9da5d1a9ed51dc874a322f9182c4c0ebe45667329c297dc3afe0091c68d21684be8d1b4158dbd6c2ea61f613f601a3e243a405b7cdedd4791182d534700a703544155e8bbfb1f6636761243d0aae061bf36c2816246459348a4a936803908cec49ab0c99539708370c4fced993945bbfe285ffce2a2127013b7551815651833f90cd5e191495dfec67986a69ca618a0116896a21a6f619c2f95dbacdd90f7b7a4da699afa33b706ab2500adf3792743553bf1b4975692a460bc5827fe02944b8143983b8d67cef717cfa2a29d5bd9fbf75ef8a7e18348c45459ce840327a711a460409a5336e25c2a068b92997b7eb08a01798931905aa7f209c29b5e2260eef233381238e0b8581213df01e0c4b0982ffac022db9bef159087fd18e8345379402fe3de7a47c81d6269c02fcf2a256bf1e2830c7186e432263043f4b97da00afeb5eb222eb81b711a61105a01cdb9fe99734d29d8b33594fb8eea33040522ab81cd91f8dca3ac81d6d27b1155e0724676b8e0976b42a9a155f8dce60c4744f1288d7e0be4954ec7a86a540c9ef6fbb6524f2d3cdb8921cf4edc9819ca5ce52f9c0827b743e4f99057af12b10ca434e150fdd3f4c380a4fe042e1b5dd7a50c23c193237897f11da1466cd389653f3d20981d93690ca504928464401ee6362382a379e65721839873e1132d2ae78300748c954362a83b7079071a3fd44b7c7bbe8d05650ba5c76c029357391967256e882910beb60af25c1e5c311ba980384bb1bc0a9c3a6a98b78c3705ff499f5c26a041f0d23ffbcce78c1702249ac6397e531898e6960e2eccbbd88e979fbd1f06f9e7cb9ef76832d302074a343dd6dd365174bee138d3e64c63337f56332c6679850f1123a4669935559eb145e9e85a968002faec42a5627ffc0df162ff0d2c663eb25d0116c29e52cbe7c49ebc8800e0be2729bc639849a1cc2675dbd59df20efb69bab0aa5112c8081d156694326463d7b11bbc6c4f5905dac31f8cc76d4ef8a3570835788c5c6013ae63e654691218f9dcec1428c980e3b2b19df4282d5ea2d2107fe7e23c981859403d69712c8e4b352e01153bd1127751d168ab1c87c873c2c18c57a08bfb4fbee9eb2f5c339b253c70cbae54985892b0eb41fdc3f7acf0f6d5ad68732d940220447b7b310b0428a995fb6aa926024824131b791b2f2f9fa1de460ae6c98de3a56462c6086e6320511ad3935b779b5ff1006338e6ab5ccc732c1bc01e492abc86754c8f9297d0f2dc94d171e2c74a1d1b979acc54e747a818ecdbb1fabf3008940087932ccfc0feafd6bcc20c6e0a44b0c5931dac767e4890e02c050090fa16c20163e49a56ed8fc0ac11a203a5a93a25180914e31c7a4f5996411049630b429680667a30ca62baa7f962c95bc81ae2b35212939b17a4bcff987a98ce633dc13af195a137f0c65c9c93d6e2fe67688f3f6a504ab21647d37800904f197b90526d04e0454859c8c28e90114c099b1368c56fa542c2e5d8aea1ae3d20e24025d354e0981bba564ac44966098d769be14737fa9c5bd3829c1b0af22930b1abfc50f59c49180b41639903f8e88890b7178e97382310803a6148f2895ab2851122fe56110708aed51f4dd9c25376c97a0c2cd7294e412c4dc63c6e83fa9171a0436779f41561cbc3b9a39cbd9c0d7ceff33cf8974ead6c49b9952be696b0130ff9d32b2f745738dc16e9cfc72fc0f6bf164c476ab4690a5a3c94e3321669ac7d9be65ae02c639ee59b6823d5481db277b62de78c68eb281f5dbd58e1d5b22e60aff7e8854524547f6f58e51da8f69bd6d0f80ef40d414a300f350ff6bc836483e7ffea38d6c172c983d8e8e1f203a21f1c21c6cecc2714a086c94a21b64de0516c7686f6b28fa00f8a40c4db76b28d7eb19bc8b7b90140c72212f40513b9dc18e303758c0b16ba4bb1de60790752daad1bed3d0d895d1c3a882d37b6aee4f9b920c2353a0124a3510d5b07138799c73b38987ed3d2319ca02b1dcf10a82055a723f3dd1373bc9823cee2db9751642c7b6ffd279633804ec85277fb025a458dded2d0eff7c94b00ddbc95fbd1a09dea4f097ef09f1f6126342eacc77d5e047efe184a801aa2afb027e66d79e8be068d1b38a28110c303a6dea3905064ea61bfa044ba885ffb22fb44eb252ba7b6c5e20175d56c53b7d58236cea73cdec9ce76b1812cbdf1ff1c7236f04251406012837204c7b99543f2e24c85ec265db065b4e31ea3f3518ac8cef203efdf12623cace28507bd782749fe70660eb079183db8db859ce0be42011951bd490addfb5488a9c31666eff88d6378e701be08df48d288945c940929ccb5176216d0400cc30c63334a4c74523828266a78f5f127fe436f0225f3721d7e61203a0565dd59ea793d4818227271826572670c31b8bc47a620b4e71316ba88a8c6fe4cc6d45c65b4b32ea30d90fe6de9869b704d46dc0d12799b0ea4701938add58514417b30c47ede9262f27a8c778a84a69130d52093d6c3c6e012165847837218ffd1035f2fd3bdfff319b7f6e4857e418c64630f634f7c631baea21db8c6b08b7188d9fc988bfbe3e4f86190e3e4d103f12e29c6dd67462710156c3e83cea041bf4fa5352e0ecb7a0fda8539a5a462e5492b16165c1ccab21a4d9330267d1b544a890d02844524d308caae56430faf1489ac5aacb32f83d750153a3845e5a98bb40a731046a4b5e8db17eb71070d91d57fcace5cc5c0a2923d132a3b9f6d18409517e4fcdfc1fb5cf4558687a8ba9fb5462e8cebe3b1c84812478787ec19bdd8d3aec78f979ac0d59a3108683f73a5424388aeb8956ca3d69cfac73e1a263a39d6288026bf60c09c2ac6a756591ca16cd21eaad5269125e307e3dd89e04248f7981594983201da66b62f1e462be1aa9e9f03a7c8691a58544af3a095c8f818e030238dc1e933667cbe8c6802c1ec33eafec0cbbb7e4d307cd56a6c7b39406cede58aa4021ee8e88ecee255368dd347261dda2a260f8c55765861876c69da6ee644fcb7019b4909cd0d55f5a4a597c3ceafaa2fab77f59aa4a21e7595e6de0fb5f76223647c6ef34e8df26fb70fa811985299df7ce3c08d3d767a07f0eafd2dd4581d655f70b1a5d55d049871443e3fbf9f4f5cd872d3ae8b996bbd2edc829ae80c97117ab1b9e93ec252d063b1dd08d242600cd02e482c2a9ead8d34860df802aed7c7c8d821457dca2e816034c5463ba26634ca85c55875740b598a857553c5509ff630408a05ca0c1b56fbc17d1ad9a262a5fc313b3ebb47220e691b75e3bf04c5397812f4fbe0f5371b90dfa0c4b0927f7e376576a3c47c8ee7a1cd6704ec5f14bb1065e426cc3f9216946c04a2477513dd7cb279b138418f9cf7222b25553c7a47193fb2361855d39b40c6019d8c925c3d51a131436a7d4706711122d6e0a751063990859b36ed3378e32b6de39db8d54f9ae9b997f4e6ac59dc4e3ef687483552f5b4d3e486aea9d6cd2a78f2364ff0150f33556107343f20504a75016d99a52a30825f9bc07326128f5f6724253ec0c9e1ac6e14b04f11a4ca6fad3f5abb66a1f09925fedcb0b8e713d89686437dd1dcce35c67fc5b54efe03ce1c71d8f30e31ab6656d7d5cd80f6fd792e5d66638457a15f74c501d62d7390ffcae5bdc14d5296c25ce7188e6a40a52598c38b47a838f4aef0d06b908718d60779eec23087274d90a313934e30544ac06f5fb39e19addd8ee08c2b00bea0e1c2252d22b1cf141d999cba0707234d161eea024505b26960662d5408fc9a703d0dcca9d09448e58c6f26ade7c6f4f910f6c647a2bd6a8c90638a6408be1a840cbd6bdb3a86d146552b69030ffd51068b416995d01fd38a93ed51c3776465f35922c78950cc6d07a62032dc3b69727681536bb15ee9ecba3584bbd11a6ee337924cde56054fa4ea2ce4d1da87d73fa5a083dc79d66f447b31dc0c4e1debd821e8ae32e827f27005310502451536e67e429a972010289ecc45af514e2b6549a4d3e558ef26fd657b9bfd15942702c461e9fecb5a4466e182f009d7a12874bad1ce04f713366bd2ff22c96122335d97bc58fff186ac97dfdcc74f5a8608b0739fffa89b3a985dfd3b4b95f028c53240033357a3323e7e74cd3f841e8a04e86a86cb27cc79c3e36369f34ab3dce0b1f5a3b29afa3e9eb9891306132c5f8114a5c2b583168522576763a497fd0200c219574f2407c2ee8dd9764d16b252139620c4f3b5eac9778a1b9f9c6a6547844fe3f1787728c81495bc67ba5afdd94190d5202bcd13a12466233562e2dc00a1606b6f4cdcd8db3ee4f321488f7bd1d9e3d36bd585b40557b08a36364704542031bcf2877c7eb5d5e9ab0c255ed440ec1f969470235fd1382ec5a21a2a18846858609bbd9fdf8343a41f26c5f50891c4cb697c7fe4a7e625fad16ae49cfa5f1a2d4623120dd10c3adce3bce82cdc28756f9350d6d1bb689a895c24971fb7e406cd577445d39131d847a98793c9793bd1a36838750091f6d0416bafaf99184bcf11030f705ec62c6dbca1fd534cefbebc5951280b6033cf60c82bfef0b6af8017cb6642dd47a405de8c34d3c3ffdb71d3b22769ca01528d18e984d0666043e41c98a33aa21d9bae15db88de0fb3a1bb80a10c893b5313bc321f8d4bde171d04386bdeadb956353e3e6ad2543304ce1f4c01761a1efb3425145d90a7f71bdf19df762f5289dfad57d9d8bbaf34d8c36e2ff36a3a8125807e71adfe9ceb299b53a77b92da7b9729806bc167aad61ee40058eb1c5324e221954f1a2fb16ba8b0516fe3eba85ebb4a29df0437e28751da21ad30eb1131a869e04192c258137cff295dcc36135324425669ba187a9ea2963a9c0f1bd0f36fa2b6e9a690b7b1933c3cbae1043d0b4745b78cdb9e2c23c2d4ad2767a784ca3f766ec76e367e9f035a2fa40066a4d5afd87e82a94955176a6c8aa4719827c7ea86d8c9406dee6f86de0639e309c63602a9f48711bc77a5739c4fd2b6c8408f9a7a5e586bae4d2f0fb0cab6a6e53c42989464c7a1924e289e1a2d50b2d6331809dd746b171591588cad8c2fa79d27aa1556d1197deb7062833c82fe6c7d401ed76281fdce6d584213cf3b45d2c997955218880ea2744939a1497e75b3c4500c986f32e1d9e1e3954ff1d231d104dcb280f33d2253ff25817051a5970436a500e3e87f844464e913701da3a8302bd739123f830ace163f5025965ae625301b7354475fa007c417a2862425160e3e9dcd0da49e8873446e4307fc9407e5e2e648b7c745f79ace413d6f634515f704b2787f7a827d5be12e06448bbda142f7edfa20f1e95418001934904700cad6c47539ef8169a5222985f6f7c8d17b7d0bcd9695394d90fbd03806a4171af08384b83e7a51326011be84af7236caabb35dbd791f8f39678dc27e1044667e049e551121f3c71a8e4976249910066104c264f0305b93de21545b100f90eba43bc92763f7ae2bf0d2529060d5d58e8c682550bd89b95c9f9821c708e5ec824aa0c386808c2fda5dd9d7cb3aecacc2221f504f265f047989424f02a6670f1899af67e82608a5815c27cb1b176b5b560666c95ef24952be6d6ad52637ad9b9e1f09d73439d2d3d836705cdabf68932290ea0da83a8a864dd948d1e51f68a889919c9453e006c3f81ae1fe6cab77e6e1bb3aab83b93b3b1eaef5eb7e209c31c190ba8fcc35be44c66bd99733dd0a5c7ddcc056aaa97889b11f237f2264c673bdc2c8080bac66f354f166300ddc3d864ebee3835a4ca84fc02b6d950a6ea44a40296351e4a090e28d9100c219472fbe804092ad006544fbc9b3650685f485b3187540bd1bc94c5808dc84ce1cf01aa6e18140e4b14aa0fe4daa899c29b724441cf495a4f04f48dec5f8164ac009f16f9ccee9e544a183d9cd656f30f1aef7830e14d0fb24966c8ee700d49ba7b4ffb46b06cc447589d874de3864c957b6f4546c6de0ad5471e84b083cdacc91adeaac266dec6f2957196f4bf2f8ec85d01a7c8497dd2a5cd92d79f290a9c8e6942ea01ac0f8c04dd554cf4b6e2fb877226f5a2a6bf548b1aab1ae163bf819231d96071eebd2f58305bc2bc1c8ec2f689c9684e008775f1664a51cb4d68c26f17356a58e7f751bcd425b63d5f073df764bdcb36ee7dae1b5caae95567f39dd7899c21e37a9dbcd2a582efa20da08c5f78098671234113c2083ceada726da35e4604f197186b05f4bbfe5f2cbe94008066e9af73c32c0590c7f255a11e622a22d2ea7926a16ca8405cc563dccf11a637d5c5990a39796187220e5db64f1360c32ac448a1a1998a72213393e8b21a4a9a915427c07eb9d3aae7d4f9fe6198f3a8c53d5b709642abbef5a93adaaf08f2b74f88510fa66b666f4316bd8457c3e310d34255ac0d996c52a643d20b1df7eb3912fc0dcfc4ca71afd26dcc32a1a7fd0952ed9a86816aa9d376a459253351857640aee737a09ec3ee9e68f98782bc800bab475106f63c29f195f5f112c9d180bc4f886d57f03fd67a977fa1ef6d3c0c6d54c4ae28bf2ce503292822bd1be6c4a66987bc64e29ad6ccaf31c88bd68518f800282efb18c4b7df9b8faa68e97d6e1ae4855857759d33135db681002cafbf6461649a49e1963da242c70fc022d535da6ec9483358af9aa60cea549fe09486d086bae4335aaadf17ea6a6a51e3b62f06ba42f305e58c2d4b9bc2b8684b7eadbac7c32af594a9cbd9faae1a4f16035448d382326766ad4ccfede1b48d8689bfee9e2c26d9810e9f10bb29c296d047a11217189478f999ca0185c3df5981b6c25407b0915b205f10fc6a3d1d28a89e7f5ea482e3d0b24f577c67f5b5d044912cff5261bf754d1c83006330c507a585c68cefdfc1df9f4773b1d71cfd41d129165674806d0480d53eebeca6414f5f3e46e56dac39af6925f1f3bcccc2263ddac140a8446bde09a3f8296a993a4feadb5555d9f5520affa95e5138aa71a74ed0f1bcc11fee97a17541c407237c300571fc5c74d755ab7d1806464049df9e58fe3178d7cf9aeceb143d48616e4e68f9f8a232664be87134e73edaa310e531bf1e90fe8ded3c70b7f111d486c5734a53861348049101fe3fdd0121a145418eb46d982b739207d7c9ad9ba17a47d677a2875ce704a633e43ea6b0e5621e3961ca1f4e837bed9237eec762320230f2f41ab33b1db144607f8fe62b00e8ef5fd9bc529fe6e9ae53da788fc52da31d0b38f8387cc8ce7108aa59ea51c32e0a5656d9a4a090ec23fcc9b3824dbc95359e6cfb5050d987fd23ceaa020145a704f0313cc2a3b0dd4a20eb5788477c4e230a3fd0b011b8bfc786c83019a2ea7c2e37223b3a50d9825c8beeb5a1eae5ca54b2ed2e5b58ae346e652b08c8f774529f587e1f3326405b14194799003c9bd233dfa93a03c2453ff2ba3f20f5da0cd00aae5d21e785665c8c8fa3c6a3d7444fb4b11c7ce2120e72db102b2d93ad80283081a2948eb300e6333b1207cec25e4ea2a6d75f75aab4a6a4a9850babe0e3cfd7386f471c79e68fae89dcba25306d7151247e9c6698b2e760bdb15776a915cdeaa4c61d9f5868c105c7988f98e10a819458f27496d846ffcf7a88e451d2f237fce3535db0a6b51ef39624cf70c795acb6e7eb23366b280a044f4be4993cf2bcd6e8b182168372941ebfdd2de99c019b35d9753cc9fc63c198139f3f2a7cbb7420f291cd293208a138bb196d102fcf909b27fc025f856e5be737493fbf40c5c251caa9fcf5b021b5e28bf5e9ee47d976869df50d2255e6ec4ceb740e757e3ad010a6837df74292a773d83b9f35ac897ce363f395eff8af35a3986cd69807346001b89ec54fdec5cd781cb4d8a695ecfb53f5984b3bb2e6b3ede4cccfceae3974d2a10cd3b88a1947771882a233c36f19c0848198c68eac934ec8b2d03a3e78b018fb992b61acd64486517846496ea90c012b81af06451fdfaf41629a4006c10b4450123686d7e3ed251c56ecee5d1584c67fd2a3ed57dcc7f277d36aa571e7d488c7d242c7e1f41b2b003bc70ce8f3fc94bba00a64e0c3b6c3e75b4a470d22128cfbfb4e2914300155d78b63a9dc729a815398e5275bacf28ea9fc5f5027a0fe39506b32210936db174162269f9c0f4e4bddcf4080791da252c4ebf3016e084d21d4e649893010c2af0fa0a4fdd6a8270954b2f5cd99c82bc5031be29dd474ff4210c13bae2075fe32484cc7ee8c31ab360b2f2e71722a1188955163161e1cb01a01b8a2221abe1a23d3c3894bc58bbfc3153c3f84c304b313982d2f6c3c80228bb9ad600098a8c58491d403ce117e01bc04309d110d1a05ee7ff2412746e64771a1ee087a0303dcc43ce3f448c0aafe7155808e6d926e26afdec8f70f1402118628af6b6c340d0619bc715715f0ebfa0582bff939d96675a59551481c4f6d18aa20303cde3be14c89820ee7dbb66fb4f96548bb3311c4eac2b763795ddc6a0100bbc9420376c4c85c7a045fd0b8b0933e5674c5b6a6477cde82be8be4ef830a60425c03eedc3781d0a7cf0c93001f3085ea00ac66eb9199da91c47838a7a7af79ff08f9298118ebbc86af25f70045ca4be2c1d77c0fa12f4501c176c56bd8b24df35b4514c2e356d540708bbfa95f93342b5e9695b2fa5c1bdb7f0027387a5ef41aac16fb9b69c556cd5ff073dac1bcbbb2a05d3b90082aee25e01837c7f599e52e98324965c5cc8655e9082c9ea4dc04558a4c700fb69ee912b450cf14fe0489bf36b64864c34fbad2138d38aa456e6393f6b87155841499683b87058888fff16dee0da0f52b7dc7a6144e865497cf5d5de48e92e27e464b08f4aa8a9a0aa80462f7128c1476af27b989077c8cde877160314fd2237aec10c35e32204df2770195e06524de63c7172660ec0179855069934016105340e312f9ec2af3a59abb61ae44a16ee632cdfc095ef773e08bf3453f7cd283effdd52dc9f1423c6b0b1c506931717b15d407436ad96fea3c82333ca6b2eee32253a71b201cd27445333c9a27fbedcca20fd491676f8c5c3228f748cb0338194698994014d566e36285984db67a9d0df9c5b59ba0a932c42abccdc1e078f5ed3240b0bd1babb23a9b5289a045e5ce47e83fe93e83f16ff28fa08ef47e8be51e70f508e24fa31da8dde72e99ac2ae97501d491649e065d5bd51cb1a96db00685ff9cd2944113cee436df7f8cbca1903aa0891deeef3ab4e110c79426a4d711236ceb5c1cac91936fa3f22fb65902caeea481c528b902cdc9b20ec1848d09cdc6d940322c1fda4bafd09cf0e6454171204912e36742f2c603d95c279ac4629730525558992319c735d1917598118a261ffc778d15e01eff6d4cbc2645a3adcd4c59df9416f25d080bf394f6d24f664eb3e5ef8f94236d186f4926a5108326b114dfca01ab0f8f10b07f9e5023f0481af32026d24c930dac51567e808be96980f9ea270066f828b1678139bc2e2c7d4bf7d795d99363669e105232247b033d7b5faf8c375fb82bacb805d62757b04f64660f7196a8a4376b12ce0c7d0445f63f2e3d7d63ea2122a6f9aecbd3699ca62ada5c45cff9cc4127f81bfa499e20fe62a0674947100161622f99b4e93dd4704c72e4867d57374dc93546eb18e1ba025b136988119146ddc65a4b26220a2e6c764c2d4eb60fa3595402f4b2c8489e10d2c84df8cbbf24ed2e82753ae560429183fce119e4144665650f64a17a42596e6d10a6445b9dc0d4c28e306ca511a9317b0f464b855b3ab72cc989ff281e0d7f5c8207e89bc9652754cfdb32c5704152f8fb93f81118f7a27d47a5c279faa8334dcfa3a8e0b30d0cbf7b59ed88e79c2b2690a6266e2435252df66fbf1345612c4f143c2e2542071f5f1b898f3b7e61253b094c68ec8c6e8711861fefd5c1bb0ce5dc07a1e166011af12744cbaa9573572b9001a234be6e7edbeb8c20356e3214faea589380dbd5735d0cac7316a373934624d36470972987f6a45a6df3a3212938b361305ac108c99230fc8f498ef60e563a2f03358f7d58c257f0614173b9515792cdc5fa0e56a997e3b48ac5625618713fdf698ea5ebd6a7276cb9e2e98428eecc2b5c3b2e5563141001e632462cda6631bbaf0a439f8695b1515c6de52ac04ddc046c63e007f5ecafe926d31522147b804795251252085729fd2f1a7b304f4eb014414fbe71e6de0c7d8dfc41189d534e9bcf48bd9e8ed3e61d6c5dc301b89e482ae35b3296ca7e5c8f107a20e0dbab3dcc08cc3c740d865a97ef184c2390478198e0ac7f340f5d8db8570285097d3ae8e94aa51a02a977a2e3750dd285401bab01645e5e595e109f786901d2ae40c0840f424619e694e5408840a7ddf5078b4f2df442ce120225c7c248dc5323b31d67caf8433f9812077e1eb110b1d4c62769076ae996f57a62f8b25dbcc37eda76f7bd2bdf877f167180e526c1aaf10a4b7e6a858e3cacda5cd1839c1e1bcdf101173207132c99ebd520d84f48546e1c5e14b75dbfa4b22e3acdf7e4ede1c4dc7cc4c6eb230480630493db6653698b9f201ef42186b09cf6654d0395885b96e1fe1352afbbe90717bba164a9dffab907ec610d669192bd72dcc7b965ad86ecfa588bd2c64482601c3b8da90b089e155d9a6d3326b9900d88daabc3e76711a68a65ad374d183bf8485c619185fa0386f48c9e043136e98d400fdb9bcfe03c5bfb8d685bbcc07e9af66e74e2f9db52a75a3bcd88f3ef7947417158e7a7a51f33413727ef6eec4d9613ed1455026df62b47209a8a5c2f5d2a4b0d3c5dea99838e1531c29f62e7d9ae2440a47479715182d540d45524f9b0a49601b93c396903fe6fcc480df50c759f1ba8d5dcee10c8c9ac8d02ed0baa832758f7ec449022a632bd0c7b3afd444702bd35363d91c54bf891b00ee1156d91fe34259e08a1f9308d7014beca62f60da7395a56629c96b6f7e7f218f473a074cc0db546d6c4045b54b3a054d09942a02cb93cce5a498aeb31ae60383c20673986652092abee10099897d9b336854b77122c280ebb3263e03b1ed7b7d0a22db912cce60f0f8e117199b417ac86b3a6dd38255fb6102effe71b2b911286837fc13f80b61a890b1a869381724c005828448211a59959676c4333db60764fd43a47655d3a9ed7ef0459816100fe842d8f2e26a2ed0927f810b5fe0522982b4affcad0b01769ed45c31899868c92332065b62733d2feae55ac9e54d59e22668147578c15b1d750c2d76e7189224cda40799179e25e24dd83cba8cd7a1dca4c8edabef025031b67b5b015bd44c75472a9a43e084041cc22491d27a18904004f8702a46082597b1d8892770451a84fe580f905c01a467ad9e78ca9c972ac511dde1925be8896844586d389b04d96735a7a9ee2fab40b8063e4877fbc417bd37d842a58b38db27be6070834d3cbba4dd3045c4f66ea5de3f930a10b142650973ed94c6ddd3fe81d28331f672d4bac618e5741f9d5983199b7cb2ebefcc0f6672ac166d3f5011bb784c9e0c5d5b165220ce630a2d5975064b671cc21bba9da110b1546912e14e66442be8346fb62dd18962cdb601203e72132fbbf7c7db3866c1ed5d0069c9d50b49ce23e3201986edece210f359fc7b7543a7c770aa44683d9ec95527187d0deb75e6fe7c3f0d8c692deba189ec381025fe3f059bd9c535a753e7d341172898d3265c1403c1dae8bea9691a202398215ad7730e599aa656439631dfacf5649d19982d5a039f3de41c294b842829a9b3e9a17bf9d8e173ce9ff13488f2f9725d3b1d37cb47a5313822c2b3bc451966c6ae972ccfb3087fdd116b442eae2b8078fdd95fa9cc45880ce4dae03a6fd3a64b4aff71d2e4403dd20e0e105963d12938c763b9d39a1c69949e7a10d5bb9d94047cc1a4446813409e375887fb60b08b3b55a7a397776b8cc023893d59acfa139508500837de317dcd9a6b9ab0328094a0da8a0e7f32cd9d2e57031601aad69668a06eb9478e26c48c5e9f0fc4e71091037c1bd076e7f838f884fbe8e28e591af36eb0a86fef4d819534749514b77d94a9dc1fbabacb38976bda1c5e1d6afbda209ff58cfdef3a728bf03951dd8650b1893c865dbd131216ad7f85cca1fa09959db64022f99415fa55427d294c8ab2dca77113040935b6cafcf8a122233f63e3a1ea0945a2735273e13e0c80e1fc3d71fa4063b9a5210d422678898c5ed1d788103dad3e48e82c4b6e9316eab43ef80bd217feb45a898cd08c4f6716c098c97edc5872a6dc563a9f9eb41e3085cb152df60b920098f7a68714695325549dc59a634109772aca6e5bda998ac2ee1198e8101a216aa705007bab0bdd4b3ace9a57f1778c91d847e81c5b68a3af737ce72d9339d6670c4c234702b4735c1fd578eba9dc563f25a501340ae2c912ec8316a8aee37722dddf57b20c1a2e1d63ab0fb5dab6f23519e066fec41db098f0e945bfa74b975553a66814f5415ea3cdbe536e7c182df9a1ad0a8cd6e8b2a47f751fab4c2245cca8313f495aabf151b55526ea45eb087c0e703e9eefb89c6fc1b4caf659cd50fea61b9a52c69e138e34b7f0b57aa89abff994262d16b476055dca473164b992579370edc9035238449397945d2557611c99d4e25d257aacb4269c61b7c82c834a9a97f327ac9d56d16251d5497d403c464966db097440a999118743c8eba25f4dcaebd68ea19bfd3fac220f287206a6f66bb6914805559a495418037ccfa418f1e574a1eb53b9252cdb1b4a3bb120b698ff170463eb851264b3e8fdb9f2a2ad8c236e53a29e0d942ef51bb1e9b232990608ec8dba3fece2d21a25aa3426b59ed8c551a5ff121227919ccbe05c22437465e4b5eca211d86c49f2060d52e4b3a715605f9f254a244cfe0e5f2772de418013f1bb73249c902325541afaace076403e2415f6cb5c12894e3b7139eb99c9f0ed4eed40ffd2f0f0408f2bcce6da4ad0dad5e42cdf1ea498d04a6c768914e17eeb54e113274e77216797b185a9c6c6913d6685d9b6d72f8bbed5050b3a7ee770d624c45e6ab2e31b9f2d18b2552f43b565935ca610c26e1093e450bb85483df24dceceb80270aabb94946b6545e0376a2b801b34d6e4bc562c29ac1b9082b5d2edc0f00cb5628f4d2fc9b86790ad559239bbeb61b50fc8c3d7ab698f3c924181dccfcbef1cc9155f536b3d23b907f52ab96160f4ff93b0c6fe9ad927d9f14808f0fd3a7f1bc35c123259276417aed76e6d17e49b38c0e6d94eac16a3efddebf444ca461dd0de1d2b94e5202c45ed169540b71ec8ede09ba9ac0dcbea97814fe478587f7d9cefcf4b7ff55882d96302ef141d5e1b6c6341c83f2d089b6ad905a8e94176de202a01619f48884f5d443d7f842f651a9fc55e0755a5c18a6eee76d6ad4f428721fbb9cba9373aedc328c7197c77d309221a8ddf538032b2e664c54d8c9500659d92d67a864a9fd143ae1490d599c9f2ed5692dc8787a0a3d86405b5208a0ec797b03c54adb17efbd76cf4728ab018a6671f453eca4b78c81225f113397d1c2a0170885af5ada9b0ec3d6a9c0ced81e8dae6c906191b75861918b6f904210f5e0ec41de0bd4b378c5d924d86c917133694e51e64ed3783a4dd0201847183a636d656a99954c74eafef9f8d01133ba2fd17ce10ccf6329c2a05983c95661923fc86d5f0d30488575b90e40eb87438d8cdb41cc50199302075f65d58047245307252cbbebf507cddad13facf476b0a7456325c6f6140f5213ecc1bfa0c6cf531fe499cceb3496febb669af70cc02fb7d4d4e0c57d32caf6f9b996f721738b12c171c0c795972805b78675433d9bd6541a5db32c31de8607f577f8c646ea8e709f92a1f4ad64a28726f6cb4f9cbf665defdf0d85f130157b34856cc8d36e3546de1ece439160a6c8dcf486ccaa78f8bcd0fc37d6d8b961f26698e0bac8e2b46fb23a5c618437a81e7fb65eb7aa20a6f67dc69aceb60c511be0fd37bae1ef88e4ee0b75f47d3aba494452fa4475f504705a6167b41e1155c1178930c651802932061043512391a4d961a3cca4f5a620abb2239fea3470d42489444411c8abf66f5c0039428a54063980e1649a6ad79b336187b79755158832e52c61a688456b0b24caa915a4661f0d8228f549e61c329c44a387fc5b83099ec1f4e292069821f563a2295e4deebbf0274f9272bece07238fd82abc2d00788fe30b3d1093ce90dff2b8060299aa33393c9585c6c5b094782369edb67f5b673b00617085eb775c81ff0bd1ee640b44e687b259d2accf8f8e17b5f4f52cbd23c54facd6ec21f0fb2a3c79142393a351b5cb915eeac32873e83431921015b9ef33f709d5c36405b47f60d47798a789208f3d2f40590913829024bf3fc2931235a7b3ec4199dbaa0efa06f6135928e235da1648239a139206fa101b683d4a5fbdc23567f911300e4db338dd6a3011f005494c846d6d2ddf6ac1846311ba0dc3d4f7f80716fca6fb274380dc68e25b6a45bbb0ae112601b0273d5484b600516d106fc915f66134e4b058aff2dc7ffc53346189477c578390340f6d5bcfa1642eb72b887855bfe4f6a7f6fe4057d7c4d9a42307e4fedbb983a91180a6d9e731d742cc492d38076c68ba3065ff48aa71671e743c6692adb81bf0ff60ae25b834c6f988a8697d2f2df8ad672a72f49cbb6637d26d8d270f38d6969270a8ab11309e5e68a797e8460dd08bc623790231834c717208f1822d4cc4261bbe4655be41027b032684fb55b9c3949ee15197cd6b9da48727e4d64fdf1af653fa34f68ac0d0b7f3cce348546eeb51854c7a337f6059ef423d6d5f8dff6d9c8b753a1fd0747e46e78387e66ced14f68459e77cd723f5358cee3bc344e8996d061913fd48d52d1e5ae4d91564108a3a57d2bc34e31c3bd62c5b17d452c9c2ba8c98db829c5b424d115b0953e3152d53e1b26e9614515e67c2694ee7c878f05619c3673388bb98bfd0e00e290f36260496f49512aa7a9772668d0fa33e60b247b5b8b453e31d0db0601709051d07f1c89de7685a79d5d930a282b439cb4e4f94990ceda16c808731b1dcd6777a1bb84a19be6b3ef53e4005e84f62ebda976a9af62d7bbc1335e9489610841d098bf2be31098815f2fab344a35df2d062459a6359615a7ab06062cfb898b288f800acc042ffd40e7041fdc1435c78d4015b3b962e036416c39cb61841daecb842bb52464ab1263780c49b060b47e1aabf106cd6a3b223b2aeb1ddca5f3fe34959e9e2dd8dbd2cba6e5c2ecf352080cb1d1f107650d82ccd5ff953e94c45b6255ccaff7a4542f56ee17a236d00e4ab6eb041728d2a092a594bbc2314dc6f34ddf304c59a4b4a727455781f94a79236ae3fa5991cde818888da5c922cdad6376e300c6cac6288713a334d1837c61f9794569102d20653aa9824acaf404c0ca1507ec3ca4bf9f75e7b3059b4fb8325c70c530f263b399bd375fc6330f5aafed75025aa8f39b3430b024e900000000008013500cee57ffffd6bc6dd6fac6da2f939413baca50910f6f684a32c99452261fc507b48db4d6e0c76da053099a09bc09bacb4aa5506f10061059a0a55575d3cafc65c52716489ddf34d9cb592e4ef38f8215fc0104166839fe638e3b732a4dcb2bd061b59637baceaaf737ae40cb623cb56a33e9ff20b7021d7e9fb4ffe7bfcd9cc900c20a748c9c472f1dcfd7fe55a0b43656345d45b36c9400a20aa48cda262d34882a4f4a05ba3b69d86a314cb71c6201041568496376b77ea93db3740ae4c9df0b5aba8d038829d0aa63d8ba3cd18fd9c25f0029057a93b99c053d6d761e8390021de5aed2e9640baffe1f05f264dad46d3a538f8755200ac4fab897529afd3499e400120a743cf94feab7856d170705326bbd2d2bbbbc5b5f3200f9043aa67fe1c49709c413284ddb9452e90c7602a9aef339eca86e0e209c406d785b31f936c82650f231eeea702ab59c57ef014413a8b15702c9043aad9b7cb2172e2b575570638c1b29b83106ba31c68d138cc15400c104f2dec3ec4795abbc3d4124247ef239402e8116b64c4d672f251a4400b104bac4e5cebaafd356e7402a81cef07c1bd58a9440e7872957fb8e8d9b49a0e5ff8f73d9cde5aab0078824d0a55f65830b2f49d51a9048a085532ff132c9d200020994cebcead5158fdae511c8e8f905f9dca55ff0a8408038021d3b2edeaad3ca56dd8d40a68f31a3a997a3bfa46504529e3299c55cadb49d9728208b4087f9929e4fc6dad4a21481d6dab550fde29450cd2702f561774b6b197fb3ad10813c7395fda9f4e616b31c02b9ab43c3cf46dbc7630884cad62eacf0ba10e85c6a5abcec714ccf4508d49d744f4da7c5b0974220800c02a522abaf7369b8dea82090a7638da872d14020f325295bcb2ce6a8563a0101c4abbf512bd9f3df1f506a32698df97338d34a7e404aa97d5d4b2dc9d6fde9035ab97229ccfd4efce74480f001ad4dcc5f0b31a96bf33da0256d7eea39c85c00d10342ff3e86f9ce92d4922a0f88ad55fea26c1f0fe8b8ba19a6f58b7397bc03ba73da989aca4d95318a07103b203bb7184d2ae5aaaa3c12923aa054a7fafc8f1d1ac7a6035a7e92ee529b9ee7d3cc61755a56ab94789d1c501a75d6bf72f7ba336e3380c401a9a429f5247ffd3f7c38a0c4bb6c5fd89637a0634dfe7e30339f57ba01352ecb183dc55db9ccb50119958e4b57bd82b001a55d5477e9c5dd55bf025903626be3a59735cbd2e309440dc8a06b6a7bb36a789a520d206940aa952df8cb5aca581945035209d932bddd32ed2cf201e40c68414eebb4ddc50c28194eebd56fbd675599914c5c80069032a0a3eea8f5c66de9e47e6b203ddf45f56b19545d940c282d67b14b9ed45965b4550e13ac013206c48beef3ae51a59f98fa002206a4cca27aa5d46618102f5e9ec18094aedc94eed2a925ad817c01f939eaa4cff4c694f203f1027af33c8b068d7737a52ea0d4eb987b32cd5cc9cf05a479cce27195d2d9fe1690722ea76f0bb7f398d402eaf4c72cf69ef25fe96501f931968b71c13dd66db08054a9fee26be1f33cde2b20b5287d957d6c69c55cf006016205944ced1c57ad662d7d5605749c51575953dfba5c3180500125dc638e6a65e7a049d52e804c01ade35fc867bc8cf14f1940a480768dd5a8694ccbe1a3a290d03b335df24f5040afde950fbfca3fe7f5049452be496dd39ce7dce20464da6c2ac664d44c1766034813d0275ebd455fc5b32cd303081350faa29ecdb6e5da5c0bb204747a498b3fd3d123802801199bb56753f399b52c250179736642e5cbcc041024a03c8a077f559a4ff6097204d4d6ba4b2a37bb735e8a81946dd551cb4123063a6d7a0f2d9d3cb8781a06cae47e581d4ca3c776d1f0210c647059129e5e376c523918288fd9cdcf497fc0407b16333647f5a3f23627f8f805fa3f66ad6553bafe65d4872fd0e27d4c231b6deef4ffd10be446d594e9e92e4b2e8bc3072fd0a91fdbc663d22ed0925473b9b566597d9f3e7481da32176552c2930b7ee4021d4cfedde5b631712fca8f71638c1ba1f8c005e2b7b5baf3b4273798b740776bb9fcfac5b640eb16f5ca3b173f6a557dd4022d78cdcb827f0e123e6881b07539c6eb20ce0261beb9dc74763e6481d67bdf62bfd4d5ccea2316c89c7322f26a1ae5eb6a051fb040bd962edf3f26f10f1faf40bd96ee84be6426671a5720b6e49ab68bbcf04afdd10ab4c72e3bdd5c42c445b102ed253facd6bad778a755a0f5262d56dfc3b74b4a5520a5babce0af65f373cc173e52818c27cb45f59c420532d67c7709d339c5ebb933057a63cc8ee265295fa34c29d082b978962db7fcf352a440976c70a55eae49f81805f2fb45a55e9acdf910452f6b8ed31dae74638c50203e79d4a2ad8bcb9bb40f1fa0407d7a9fb9cfec9a767f0219f7d2656793c2d3ad9e40e67fe184b7aa76b05b091f9d70643eb7ea4d9713e8d25afce61834b5fa973c5cf8d8045ae86aefec1e4cfcabd6f0a10994b0d5d457a747d3e56402b9e2ab593cfe94961b317c6002ed9a3cd9c7bffc2965d2f07109b43ef1f2a877ec94a96409b4ccb67eb8ce628e25530964aa6919eb49cb6b1b52029d4b8dff66d527817ef152fdc77c75eec5e448029d5fd525efd73d9e5b2450f33f9b65ebda17540a12e8d3b25a7cf3fc39b9ec118897e5f2a9beb3159b3b0229736799a633beb15b2f7c34029df62bb30e8fdfe18311084dfaf5337df7ceea7c2c02f5b235eafb9c6d472545a05eee67d7b4d265594e22502eca174f9fcacc9e3f4420bc936a559f79e39f7b08a49eb86a31556d88944b71ef1a1f8540ffb8962fcbf24308848be3a583dc6d3c191d04326b7a878d060581fc77d7926ecbf9a4df8140880b269312aac5649f0204d2ced3b6ca3dd3623d7f409a89b854f262fadeec07e40b9b5f77f19316f36aa4e430ec0352d555d3c6b4f1071fd0fa37ead0fc3a677d3a72f8d803c2df576a516a66eb743ff480d0a4368b49c4a3471b9ff0910764dc797231d8ce6bfce0071e90af71523b5d76b6e9dcc7b841860b6c40427286e6e1e30e489d5bdef1d2cc25334de3c30ec8d0b56f315f96e97ffea8037a657554dfb24e258366c7071d50f3326b7f6a54724564e8f03107c48b5279b74b679fa585870f39205de373b6eff2d296310e28efce2a5be359e7df4f0707c49cbc971eaab324f51718467cbc015d27b5568717d5cb562928230df1e106a4ceaaf52defdd8cbbb60d68617596e46b31ed71657c94f860034a6bb17fd4e675e8fdb106b4d6728cfadbe93bb54d42424242427288f2766a40bd3cada5ea5eb949cb3fd2807c7dae05d59c8506748e2d87974b8cbd2b9d153ece8072b1a6f4ab7cf9e37bae0e1f66407df67bb59952b6ac95ca80509559e96cb264878f35d0b261bc65779e7efc020977318efe2acf5f7998e8956014226d40d97dd631297636a063107bd9ac83eaca95c81a501ef5a6d37277e15c520dc8f0b9efc1e578a931260d68cd2ba7a1a929df5d259f82031134a0050f9aaddb53ebb4e11950673aaa92d26e33205c980b0d3eae934e8d37a3a4a4c78e4f413cd9b143a40ce8f7ecac3e8b1a5bf49c84f120b206eab46cbb157f21034a9c78adc9d4bf46178a8c01a93beea3a866fbbb6c04226240ae18536dabb9353e87019d72d5acb03fd7ea491130a0555e567b4a3cc6cb38f9179090b88084e464871144be80d6b26bcfe0aab30b0de338b7e3757c49ca97a103dd20c3053770a41437010d5ef002c692931d3b3a897801a1bb1e7cc346e1df5a6cb135d97c543117d03126ad932f6b7bfc9c2d20655e2db987fa0bcd2fbe68011d746eef379cb2c077d0185d1efd44b080f8f35171a7e1b5ac8cd21103121283060949c919349087498e2686c815d0e2b76b99ff97a19702112ba0b5aa667c51fc85cdd1a17294f03051a708912a20b59c5ad2d2fff669aa44a880d4d9739a5426fdd5c429fc9a1e4ec7a45152408c8ad76def245eb4140564ec9995395fae44040aa8958ddeba46355c568b3c01193f5d2e2dbf7346bd3b01d96ab39c63f6b29d9301814813904aad4c9f25f6332b291126a046f45cf25eb9c81290724b87976a5d4735314a8a890e1dbe94fc89a18812d0f2e7903b8fb9fd62ef40240968513c8bfd69b31633f229e21041025add05f7dd7521265d709cdb1b64a4a08c123c8f18ec303941010acaf3d861a248487e49b386c811902d6af66f5da555761c520cb428cfcf952e13b69e0921063a7d47172dd39833a638ce711921c340ce66f5519efb38cea5f13cce1b428481ce57d3a6f465f559d6828130974aa588662daa7660a047a3b71a9f7535b20af9c5b28e39ca3cad7f5f20a338ed6a752b2d5d2ebd40c79551aeecf6cf09d70e9393650e478a8909f25056c79b717262c2034742311684f002195c9cdc24a754c995d9056a3378d4e9c6e5fbc6758196b4cebdf5d1e5b8e1940b74d2ab9bd962bc9a960d41082e507a5ae69b8e752b658689098e9217c2eb503910e310720bb41685e928ae6571f655445ba064a7eaa0424b6d5c2513520b9497a9b74e59ada5160ea1053ae55b6ae1a705d1dce69d59a0b3b398652566354b69f849c9ff5a59a04bcc8acd21a7954bd39ce10a89053a7e8e9b95f636618196c44d8ffec65f81f6933a777fce7c1a563a166744882b50da5e878ebca477fd3321ad40cc9ccc7d96ce64d2185620d5b3569f5bc6f4aeb19055a0a36bc7bef4abb5a8344415c820dfae7478b9c50e762ad05a4b5dff77abe1ae62210415e8cef4559a32bca437abaaaaaaaa66666666662222222222dedddddddd25600921a7409a5c599d349a78793338ced90f424c8192a6e6cf25fb7998308a19262638424a814c2d6ce6336d2ff8ea709c2b591e31180345878e12dc18e3c68d316e8c71038d1c6a676e0d21a440ea57a945ed2fb8787991411a4246812e13975ac577fe35cfe82284880299c485679bea719c437995030dc74b0c055ad5cbf712cfce11c57130aa490828d0a26eeba79631ef582242c82750f6ef7265661dc7b9209ce1259e40eca7c80b7f1b9336a413e80f57ea859fbaa8b4981c3a78987002693a6976f9d3f1b450b110b209d4cee7666cb1cd1abfd3045adf9a164ec54571d284e394f048c3848719252839f672109209644e4ab34d97d4ebda746b846002dd19734cdf736fd95f02bd1b9f3e46d3e8fa3e28845802a9cf646cb5615ebcbda908a90432336bf19a7c364bda47a6044a4f8ad362ce96cf5589e3b0a1184226812e71ca4f5e7f78e430918124d05add2af18fa752e697095276a040086aa4d1839048a0be3d7d3bee0b8184a21d1a635f687aa832870f421e815ce54a9fbdccbe9852218e40ceb7c9acd9329cc6e0a647231b813a61db52fabbe7ec2b8411880d66ae5b5b553c3c25c7a2789420d3a391850859045acfbc2ca8c9a708b48e59b3b9b02eba724d2290db1d4fc6ccdbaf5e2b0411281d35a9b815fee10d325270899043a06f3b6ee64bbdb4da771a620864cbd9335517cdfaab1c68841402e5fdfdff9230755dc2104220bffc4eb798f383402619f7c33dcba2a76a090299bfc24e79b00381d03c2dbecc722a9d6c40207d5f269d5d352242fe80f88eaa6587f9dc5f5ab80a217e40aa52daa3fda9536a1e3574e87032b60f485572ae4bf3ccaedef8b029dd4af5a536f780967e44e3aa978b8d590fe81d19d1b4ebbe661e503a9792771f5e3a71f3c4d821040f087b17b7c5d5f6e13f7a07a476193173b1b6034a68adf84c9eae035279ab0e9fc4bd0beea287103a20e59fccf8d0d41f3595c3e40625b723640ec876d5597e59922143881c90e2f25c0c5fed2de70cd5101207e49a6ed70fd39d524638a0f446d3d012cfd095a2c8e0c618bfc33711216f40e66ef1fd640ef9d3b8012de5dafecb283e87cfd521a40de8d7175d7b96e56fed900de838e7f9450f2bf3cd27216943c81a10de59302d8bd9598d66a90159a7f6b2d4a4c549fbc021240d08cf5aa2eac2bc6966d1803cd7eab5b49b5ad24a3c03ba736eec974cf766169a01e1b1334a25e6f4ebac2a0352b394fab5dfb91d7586869035501f54ccc5de6e98ff16114206c4cb7d58ed9c942b750c68ad5c7cb7af5a0c6825f794c9eeda102161406794bb99657819bffd430818d0ee2a3bc9d2f34d9714f205646ab96c5c890d7a9a3584102f203653e9b996a78dd576012936ef6d9272e5e4cf8574fc760cd902f2de940b3717d602527ce6bdfcd92ca03f0b4d7b596dab963f040be8e841f763b4ed0a0851b1ad5a7fee88cb1143ac80ae5b8f1a5b66885540786d9251e66367fd3262081510afb38717e5bf65195bb542c814d0a7b32b99b37dc3668b1642a480ceea5a56aea5cf98b40e31240ae8dfdc74d93f8c180285e550d1acc52c9e4d087902ba94163fd86ae795f58f10e20494bcd5d45a1871e51d0c6902e24c3db36b95727e5a4298806eefb42e99122fae96d44b085902ca85cf9f65ec4509214a40eb2c36868dbaedbfa1a2119204b43c2fcc6dc6cff257439080eea45afee41b3f7616c528841c01592ba772d59f20c540d6eba82f9cced9e386c440e7b7fc16f7f8f6176c02c830d0d1f5cbd1a34edec245106120958ea91d721f558b3ba281c1408beac22a51b5bb8fbf11408081967a3767fdc289ab8abf40c65cb3adbaa5dbd4f2be40671bd7a9dbb224fb49bd408ee7496f51492d9ff302e599a517af94eb6d39dd05423365232139ba4088fa78aba7ecb9fb2424b940baf428ff651d65d6318d0b74b6e793c1f4bbf8f66601e41668d793327cbaa837c99c2dd0dff32dcba76796b485af00520b94bc16b52895d42eb7faf4e8e1871628716b2f344ee7d763668132cdf02caa4871085452810cefd35acc9a57ded9bb811e1a2bd043234f7294f440d10a2a3e2aa740f889ead83a7ecce6208e941eaf4c51a99402e1eba566b45d7dccbb420a946cadf3dce516d725a11f2aa300a9880299be3bab94a5c1714e470e1e39fa72f4f0921e280b031d946425140aa8800229775b8ef9da27d0bae5b54f3275634eb10d6ec8c005154f207b4f44737cceb89975021d46b5eb74599b3565154e204c9ce7ca92b3d5f9c6714e97846414954da0955e4d42d4676e858b26505acbbdf2e2378b9fa74ca05bd4b52c223a269052b5d2e9ef05378f6ae51268a9e2e45eb697975f8e251c17d5aa2a8116b552f12ca3e9702d5202adc5d82e7cffebd7364a542681cc24f7e4a469fd26832a90843189ebbdbed4f9a7641f542281ee13cff2b7bb50709c9494e14bde0b68f08217189eecd8913ba84002a159b2cdd7515779045acade769d2f4a7576561c8150a532e95842e5e7cd541a81965ba67439ccbe75902b8c40ca69e9b5a89a259da3669545a0663788e9ffa4a1b953510462ecb4f813fbfd327a22907727cc5c66a5c15cba8208b4d4ca777c556a9f3f95c123c7490c3cc54f408317bc80846405631c3e540e81fad38246973d29f5ad560c81d896a964729b955abd0a819e5f57cfb1532a57ba129394924ca34208b4bc2a9a757c592fe6536510c856995d2afb94e5a3382bd2a0220894cbb25b899d6a19ebab040259b65149f1b93a315925ead5c9c154540081faff2cad2b6f51b9161da3f207b46ef23413aec5710e078aab80b187ef5841c50fe857dad293d83c3e1a3615f0092a7d40da46b7f96c59963d230624249fe3840c175c850f480dee1dbf94961a5c4e4efe04b550d903b2a4bacc4f8f755a46c3e4a4878941450f68c94fbf9cd3ee495f58c983cadd65bd2eca1f0f083de9f9f25aa7bfecaadc01ed29b3985239cdeb52153ba0cc356ab9853b2d2af3c642a50e689d3afeb494a7961633840a1d901a3d77eb378c380794fa89969d7c5f534a2e54e480fc5475e380f81c376679b66f734c2b7040a7d3ab59a6fa1c7bafbc012dc78ae6965e32bcaf1537a0b563e7d0a8f7dacb556903323f29ef16a3ebbabc28a1c206d4b61c55a78b2f564facac012d9e4b8bb2ea4736c4aca861a93ed6bb9534203649b9fae7455d63a982067407971e363de65bbe18a17206b4a0f265541d7c4cdbac980199e9d2efc9cffaaec44a19d0aea1ebf975ac4fd55fa1b206326a99d39458cf1d6427839baec59755275d650ce8df79615eee564aa6979b43450cc84eadd95fcd1d06849a529bbe1eb37a4c3a43050cc8f4203e2ee55af9025a7a4e2b3b262d8d497342c50b68e1847a9a1455099ba1d20584ae94ab2d6ba58cd955b8809497958e4ffd59947f7150d902d225a5b3dc2b17c5c98b09152d582e2b6f39dde9b38072496de6309eaf7db32a5840faedc62c69bd415dc654ae800c2f9eaa137aaedbca2354ac80ce31f5b4854bca055392f3f8924f2726ebaed7a85401199bc734790c535a56d1c0bd920a15109bd6948ef962cb5d17933e3654a680d2aa9f7dc636a78ff1491b2a5240ee69ae68d2dbbded49250a686d97ae637a0febb2498e921e281528a0848b5ae7a865b5ba3755a83c015d6336fa2e4b952e62132a4e4076d4f45a6fab3401d93efff91e75ec2cbd4c5278acc00a1526a03f4df8966cd9a5e52b393129e1f138524a140f3fc7631d469525203c3f6af16d5b592f6ca26ea92801254c8b99a4164e89936a2509e8a8fb622b53fa65521b630c46002a2a4840cb69daf7352e8b423d9523a0c3b3daf83193dba94da418a87bb55bd39d5c3b26065a561fb55588cf0bee28519161a094eb9cea5df8f2d4a24adc0ea504c2404b61fbb272551f5c96170cc4b876d9c2e6b6e87b0303b59af497c6a4b37097e51728e15f2b5eaad3ab853f87882fd272366e9cfda4222141e961d20bc49d8fbd2c77351b0d2fd04a657629af63a2ab05c7391e22bb400b93d14d9607859003f14e20a20bb43cd34e3f73aac2ce1288e402a5e5a43f69f184eb6c222e90f5db61e4c555633adf02294e6deaf8325e95f87520620bf46bbd2c98baaa55dad5029dd36a142f6fc9ac2dc3710e857920420ba4baa43f4b1d5f8b5bba5543641668b549b6f25099ed72c71688c802193ea7fed42d7fe92f16282d6867b9bd537674391688c00265ba2e4a993a7cbebc719cd3e1982af20ad46de9fc47392eb66ecf310c745042021157a0f3c9349b74b3fc24bb335a455a814c339d5cd6626efae0f8133d32d2701a1ca6e127ac40e6cfafec0722ab40991c17d37d7aad5fea1d37c848c10d55a0d427e93db7ba5dd4720e22a940c91efd9ce3d9bbb04205524bf35ae9942fddd6a7c3e49c0229c6637ea994d697e2c5889802d9e6726c155b27b394494a8b94021d655a9de5a031cb743aa440e7f72cc9b031b7954922a3408ec6bc65b6dda7a65122a2408b6aa347e1d14e86bc38cea90a444281f2605a7cb1e3f3a5efc4a4c4100622a04098f0175569cbb4515f4c49486e8c71e3860c5c70030534b8719810f904d29378964e6aa9c5f49a1a60caa37802dd592b5d1b9ec7c58c12e904bac5985b57eadfbd9892be070a8f1c251703114ea0b566f3d494a3f973882810d9044267f5cbc2eb281e6998a426d05a94a5e92529a5677de94c2077d76ba552ee51b9668709d46d4b53a9c4ba24c5be04da336b3ecb52e2e26e650283b62b885802b1d95d12f52c35dc9e38ce9d98a438e30a442a81788fcdd12835c9ae6d7911598eeff0a7942b39054719394e6c12e81b75792d2fb3a52b2d121249a0e5db3f6152b6c579293cbd8e04cab5d68dcaf47d9c749d94a17f82f437382c498f46cb40041248dda46c4d69662f9d7d047a34bdb98e26bf4d8d1a6f46aaf16694a8b19404c177d8893802995e8bbf8bf9d76adf7511441a81cc7c2fe7f29c5da31b23d071dfc48b408b1dd53fe9bb8f5f32452083ea9d703175d8f81a9144204cd3c7d6e5a6f7fd328c082210ff9249d339388eddc00577874096c92fddf65afa602e139ce0739c90416208e44bcdfdf14946a9451d834821d09e0531a5655debe81e1ce78e89082190bbae731a270e02fd3134bed79e6774714120f366512a2d3d2b452410c8d22ab574fbea9d258d0820509a2e977f4087ddd9ecddfdc9a7e4b814600b6e8c71e3fce45356c007113f205c9667737cac6b596ae9033aebd75ed54a7bc6d7f301a9ca76d4b3149fb4ac25b207744c25f231fbacd2fbd1035a566aadfecd0d325280d206913ca065574a9ff432cfdac47a227840ea92725bcc69517cd0ee80dc4d2eb8caa826971233528e1e76296207b476e5fa9a54fa2b77eb808cb227a597966f6e9f0ee878e15f67facb1c903fffa29b03113920b3f0d0b235a54ce6380ec8cfddfc97c35cccdd9281081cd05a8c795f18d5fcda1b1ce7520e1dcb193d44de8070396ab92ee77ba945358e732538128f335c874943c40de8041c38f881b40f241a52e0fb97e7f2bd010a20092fc94520a5a09c71528000a8b17c40010048413983060408c097e16794c1c37520400028270978010002a002062c400428397ab80202700800a0a02c24041080a7b8222940003cc595c909c9010030001b0938f9123392a7a094942c000001003900b2e46639be6bd0d2eaa30994d216713d9ec74c8b6702b5d91e9a2d356d676d60a25352ece9b8bbe377181b9778344c76ecf81d07b061892c41808d4a207cf39618d7a74d6c18c761bc33f076fc49091a395489ea4be191235178e44839791d397aec304933b04109357a4c2273a428931d6748a2c78e1489cc9172820345474949026c402273a4e8c081e2252509b0f1889447d9f13b0c60c3119923058d1cea8c929204d868045263d8d66c4a2d0b3a3202757eab6a850739ad5a16815631a9b3c713e2dfbb2250aa573fb91c6b1b89406bf4a46388c8bbd6923610814ceeee2eb8b668a67f1b87406e8cbf8c9baae7e46908645c9bd3ab1fd7d56ca310a88d9ae5de0c278310e8745246a96539d918046a47b5e8e7f81f654ae138771283efa10619ba091b8240f777ceac1b9bf329591794c0043170410a6c0402a1aacc834b59d2445d40a05d34a9a56bedc2c3be3fa03e8a7ff3ce0f8897c5a3d894ef79f6b2d107a4aa6d516b4dfabb4dca07e4c7a6ab8e9e9bc7b46cec012d7ea89b1e7539b352d9d0033ae7e48a5bb1d15d72d9c803e2652534e98ed2ec7578409ac9664e8dbafc5d7740aad49c37b868bf2b653ba05f9d6bcf2c5b0784e8e95c7dba2ddc63b34107647d7996e5a032c87c3b07d4cb5fdb92702944b38c1cd05a92a5b52b6de2b334e380ecce2b9ec67638a02529b396e3cb2ef30de854759eaf0575d7ad73035aa35cb9ef4adf06c4cbce2eabba8c4aa3870de8175d5eef0fb25b2a5e037a7596f5699bee63ead85003d2654cc66b71a56693d94803c255c9f1ce4ab36bae6da001291b362ad3a8b2ae25cf8050dd5a9516f3edd2d20c481db3ea19c734b82c995906f4cebea969bffdf83f730d6497ec589fb42603c2cbc36739b398cc3120c64d8b3dbb392ac32906a49f9a3ee57a4d9d92c90c035a8b0fb39ecf85041b604046b1cdb2fa559dd365d9f802ea5f8c592d6ac9a4ccb50d2fa0b376e1f3c32b116d7401bdb2f3e78ad755def756c1061710bfb57134f3c7bc5c2bd8d8024a4abd2eaa1dfded571b5a4069c699b695518a75251b5940ea76e19f25dbf3cf8c136c6001e539c7175c5c4f4fdf36ae8016b59812d33aad8052b29fe356fe2aa0eb5f683e2da8de6b6f830a087be955ffd6497731b525c7c3c6143a552ea20d29a0e567a56d9d0ea3a2411b51408f7bce3eb13a6f44b4010574776733e5edf2267d3594890972d87802baa3da181d369c801667f64fdbb65ec34bda680232dbdb7bfceb5913ab0d2620d565d3679bd3c612902ffafea2f2e7758eda5002f23c6df5a56a49024a9d660ff7b37bb53a12d0624da5fe7d59462d6fe30808d997a53f95a76d432b064ae7d6651bce63f46fa5f030395925e788817c9765bb0e0d318781965a6e5a32db36c240984ad74e6e2e1f3423033346828158ad4a9ce997a9334a0103d92545be736fed578ce31c0f131e252839cc99fd0299c5a86547bd746b19e30c467c81d4fb5a4c97efb4ae32477a81d2fb2557b7bb4b261e8ef0c2eeecff2ed0d2645dd692a32e50ab45ddef2d917b17cf05caa3d6182735a9fa1cc5716e6530820bb4abf9ef96659692ed8fdc02b9bbf2cb05cdd37bd316e8973ab34a2d69f52734b540c99decf0ba7af58697344cc6202149c3e4e4648416a817578b9942b59efd9659a0fba5d5ad49cbb296d52f0bb4fe8fab84cb2c75e934120bf4dde79cb4493b99c5e0082c502f371f75a38c71e133f20a47b6b5f3472f5d81fc98857b93e15dfe8f49465a818ef9b596534fcba4b4560823ac406af933cbb676f97db755a0740eae7fdb1e464ba70ad476ba54d5c94eeca85281ba5732ce366e74560915a897c57f7157d6e7ddf42990bae9a5550d1bd5be9629d01973bc18f7351c8c94025db2e4d59f6ee76c2205e2f4eeea9794314fb2915120378ba5e161fef57d140552e58ea6f012a249df9150a065e1c15b103b296fa5658c80029d31beb67369fc09b4b82f5df8e6562fa6968e2f393129e9e13bee4a30e209f48d8d7f6e9764ce5ea1f876ca8e35d209e49b14f7a8768f7002a9e50b72633acb1b73a294a4a098e028593c68641348f37c5a3227a9ce6ed6043257d7251734ed6f4e473281d44998b9adb447308196e3ced55cecd9d8d92590528a93fa499b2c8176b5d5e89aca4aa05f8eeb5a7c25340bdb1aa1045ac72badbb25cf24505a520b2ec6bf9240ebc7f8b2d33ca78b7f24d072c73eed63f7bcfe20817acd589f75ba6062ff9147a085ea7a59cb52c6116839ea2d219e9478a411a82de1c2b6beed6eea19812e5f4f2b54a75b8fd91a616411a84f9af3ed8b9cca091d45a537441851046ade93fa8d61b5fca4339208f4aace6ab351a8a89b2388409fd6c95f5e7b789e8784240a238740cbb9c4bb9215cdbec133460c81164c36eab1f1a464d06c0d2385408b2da979fa55526a99b6618410695966d6ba8d492b8c0c022d465d7a63d4eaf65f7bca302208c47bb6694cdd2f8e66212161c24820d06ee267c3e4e56c368d310208d4cbd8e2b87c196f637f40a90b2fedb3d8dec96e3fa04c8b4f9754b60c6a5a2e3d0a19237d4049f9b1fd5ed21a7506834f41791e255a18e183c96529ff73343ab207649c1d0f9ba496e2753b4e4c50b0e3c4648de80129fe2ddebcd47963ed913c20b32895b88eee377fb294113ca034cbb76fa65dd2b79e1446ee80d2ad633a2d37ca2ce7a5b80c484870a4a48cd801d992c9d997b743c27540fc9d7e318eaf4e9dbe931494a60352679fd7e2dcbee89a1a99c38d11394c60240e70e0c0c81b3430e206846c4caae146367e35d206352830c2864f3c4c2230b286098ca8012d06933ada69df9c96bdc78da401458d37a302236840f91c397a94a0022367302366b8315206909135909ac374cab7ffb8bb92610c0f1811434965240c1a1801038f7ca101235e2891c0481750505e470446b88038d339657e9999b6cfc816d0f2638f66dd98a6be1486112da053a650cd518b2e4fab47b2103282852b20b37bd417dce5c6eee90a2356a88c5421c5c4e46401235450e3cdc0c0c81476440a298f12859111288c8c3c01078ae3b0c088132e30d2841126406064094a1849c2081250768c1cc16439a0520c94141e8fc613c303956120366919b365a5ff5aee4b78ac921d289e46c9a7f8490a4aa6092ac288402518c0c840e51718a8f8c2535c95f030c9d14303955e20336759cb397cdbafec547881e214a8ec4202155da09c98a4a098a88001955c54a0820b94fdfd9dfecb9c3699e2af8c9fa0f493cfaedc02a93673eca8ec7447a5f43c722c7bb628e940a51628b440c992fa5eeab328d5bc59a095c798e5df13dfa465a2509105052ab17840051648f55837b631e2aa3e222aaf288140c515280ea8b4026ddaedae3dd3a5175d0d12920a2b5027567a382dce4bfbf238ce9d7c1eab71c227a8ac022dd635ec26f9e038b73da8048d6fa844d2452251200e87a2308862000423a70705631200001014120cc602f2704428d5d5f5011400054c3428524832242e20180b4b23a1401c108602a140208682208a622006c260a864155d006e55ade4417e41d101be68cb08ed4e2a341febc9f2540a5888956cad5172244b23f468f5708fdc5fa11e7529d04673bb0a074d5be6a5e3bbcffb8361ac6d394b5bdb6b1284c684d8f427d6f1bf910f10e88d779c5ef3232d1347503075eda15cff437c3d23a3fb44479675a9b74910537d0929a7a2481033403a1c541068751eaafe2778e8e2ecc855a472f192358ba5b310523d4143415a59133331e160b8daee59e429a994b71cc2d278f542a4c6ba1ac584bbc87111794c59e172ab57f459ab08d0ed0246a27b32d19b28326d4cf731d604d1fe0d0cb5295b531bc08171da508d9ba85f4640551373041a451d60a54f1b47a68732a8aef3a3a5e8a02d01cb0ffa5982944c7b11530e74ae79a0354aac968739047b1b52c2dc9067abd72f98ca5b7d236ce2623ddaa53a2c41b1a981d636b467a0b0abaa5f6a5ed333d5be05d758e3f0a31bfef598cd60ef2387500c5e1ba4938469d5072afe2433e0f84d1b5036d2f07e44c83e1ae762d62202f6a66f95f2c8c9463a2d626dbc822556f4f921cdbceb3d5a493cfaa3241f28ba884d60a110b0e4ce8c6ae2bfda3cdf4d7ca4d320983906c24b23e00a152c1e489605a3af0211b46662b2157b664fd1480bd9e58e3f00c58ee3aa50b05a4e8ba0f1a046b6b6e3ebba0cf4369077f95ec543267722ec3a32317cefeb4e2eb3d1252f4ab409b4dfa82b6ee1a734e4a2c70de43b8a6693d0eb8ace020aab36620aaecf452123547ca1cc13b4869bd4e6922c6aa65ff429f3c8df9e46ba18270a5cca75d3e60a8cbf9dfd86dc732351582248950de6cfd9b10149990835c33565c85c6310aadcf14cd3b52b919d0285554265c41af75d42586dde5e347f17dfb991e7106c626ed2ced717354800808a37c3a07130e22284013d7cd17a81f569b309532099e566b064fa706fde43cd646f068b2e9b643f123131e018b19f0587e68f88ff240a9a02ce63d0a835a26d02701d56e5547693d56ed25735e9d26572d40b3064ff2b7e2b2fd87112ebd7d57ca301598586e8e93c6dd76a8a8708190d60f393c770c4995e7c4bf96491b9e59ecb44e992d2d606cb87b9aca956c8937627c4bae36267be44b25f3c4d5985496db89231d2b8db92ea7dd06bba7a46b846336c4ad4187622b712a40b496e8843052cab6ab0b10dd206cb8484f4eb02bf20614c58a449994417a9547ce00099c9d2a64ef80ae6a07e26c154c715a7ff80996b206eb416bcbd01f542bb4cf6a1ba68a0535772bed7ec8bd34f3bdd9036645ea33490e8ed704f7ee52de9d5b6483605de86424a8c9b95f9c3b90ec317a437a9b32b12a4e8d88092b68f7b32db2fb3f9dbd77c5be3524c89efb2bbcc949ea19de8b5d5442c6f0fbffaedd5fd06dec5f5a91b1bc2a1456b1ad5a6d1b44c34f286999af3d9c23f8d68d4b55114b8f99af5f3169ea0b98c8642dd19e9bb528370b3f5100098f632ecfd64eaaae56296581a8c7a14bccddffb0aff519a197f3d224a116fcdbfdc5ec96f66b4b2fe985cca65e1742e380329ec95e966fc6604b4ec1b0ed3d66680fb55ac927022c572edd8fdb1b8d56801b0a3ad7d036c68d2eda2e3c6f06b0ab02a773dca9d0c3d03ece7f5749ba0e276ffeb69036ce1e3901fad9892ba73f870186cc30013e63b480dc9626515bf8313835777ed8434ecedf4ac0ce90fa00f439671897ea6f56c6d8efcdefb08607b03143c49c2f13bf5811723ecc8567eea54ad3c6c503db8c98fe11cfd810b4f64b43fe8e4f0c74cd4813095e17d8527b0ca505ddc9943dc1ac15e3bf8024011b6aa35c0cc3f4194b17773e741221e3c0b500899dd6febc15a94abc368d52ae15fbbd5b700b42713108ffeb9f1d22a1177f5de4acfbdd5eb98e9935bf1a3cbad614b4069798f86d8735f3ef7a1ed1cf73f9b7b846d645036ef929c2e46e01d99cbcd47e3ca3ec9cb589bb13984fdf9a1b409d4fada0c09927bcff4f2316bd3b060f35d432ade666e7881dc64b12677f35475f545b9ed3038cbf6b397cd422b04fabfe3393e56901d21108a8f9ba5edeb50d102f27515f3a3779b0537244f6b79f28d61768b3e67ede01d5c262f5e4bad414682bb0af7d0162f9bb7e9a2da0362f2e8e091825fe9da3fc8459048b21e7e92ecd33da4007b5c7ca08ff6aa08fbdcb4a4db5b5c54ac55d8fb7524716019d85c5b20a48098bd0c497f9a2538815e70926cc2d032220b8a9c1c952033c8b3008192a830e31d668434348e6f62974fd27cbc598ac8eb250cf4c152f89d709b086bc5eda68202769a544218f1ceb3d4dbf8aa31d4d851ef3cd8693f378d7ff9ae7823528a54416d5b58f0b871c802989c5ed407cab85aab17402875c116149c059442de321af6e8841ccc9f1aab46f86f368571130aa587c8af69bb7de7fd1ea682794309e32adf0be330e1cc5c617d1fe12434adb8ec68a98ab7bfe19fa57ff18bb5918e694ba0bf3d8a5cba0d82cefd96452526bf3a94bbb7f50db1939ccbdb0581b01a4d613e8a7d08519dcf5a890e71d58e99058c9e674b9310c0262d70825f582bb4ed40aa082c9d07f925cf9f5da122bf7d672e75e28b4e08b9053784f0b2f3cbeac0e020fe91161a73df72a4960cb7e70bf51ec10a6dd06c27656da5ed8d9c68fc7c82c85a2f1fcf128d769d313727158a5a933fd602bf5c850a4d13430be7f7254a39c37304f12224a29e968bcfea9c2b833e27579390652c5a1c1a2e4242bdf13be4982da430ed61667a239b2b60198b6fd3960e1543e4f7ed3c28e5fc1eb4bd25f56157748610a19cb04d44d236bfce13524d2056a235649b045176a187d892ec1497428cb0854141bc160c435485a45ebd19c719a26c41ad0b4144497b4848f5830b366c0e544d70e205b62809a986ac3cf7a431e1c02214e1b8859a02bdec90283a21a34266772f2c236aa8b054f64bbdae8812da0b3662d42c7e30c0912e49d1b0dd256f5be588bc2f9d8bcf1b34cccca159aae08c86e6d3908a7d25164d5afe809109a8357d056a61ad7cce4ec8c431d792326de5314aae16b1a8af298ec6a806c79a42344b7f3c59142404e40d5898ce3191bfc5dfaa49e59830158ef8442f6d1f45d43beeb4c61d259f3f1784fdb0920cd22407a1984253d0447c957e9097c28d9420a38f4bcf569cfb163ba8e47f190faefc6ffbc5a1b7b3e9018660e56b4fbc447a5e57b256c36a442c0f0fee12e14db4fabc63314194632729d32a5ad8aca32215c8ed48e7706d67cfcb93963328aa0c41b43efc7c431e4800177baaf540ced6e53ac9c309ad9a4925787b05ff37877f06704e78cca3c528f51b76a385fc03a50b53ef2dea6e4d23e1dbcbe90fc83247d34d79bbd70c2dd9a1e001c20e824514efee58286092fa682121854e9202e0df753434a748e8d75a7eca32fe7f47f093bb302bf6b74f55c94f3033d95fc25d2a5807c94e804b1d099a0f4209a0ba0250a09e6289ff1823cc64027cd959fc1aa764eeecea58f7388e78c7056938a45384c7b8481dc3f7946e98c5e2fa6cc98252e63343f324cb487b2ed495d912092a8e34aa8e2d5dc46b4902dd0999bde8e699089b0915f54327c89a2b054c5ca144f5edc58ac7b82152904390167bc06bbe6b5d5579d71975013545f00d11d29f219e7d70ebcc568904185f4a8f9c60b3fdbc846ad37ca549d4af0a42584ee9e0c159a95fde76026667d5db5ac4439a7eec677f7c94c5898e3b98bb4ff45d8c6b6e8a837af41511f49b8bca8b58d483e202ea537dd126f532fa17508121c255824e7d3103570b0b6e1b5b905ecbff12eb3a69ef3508791704e2d9ed5771d7498120028854f48b085780b765a9635bc02cc7c857048e8203ee3beeb24682e6cade59f1a516a8834f18726b8728b7ea94c641491e8ce4fd6ca27327d391daf537d39ab594402993fb3f48a215d62cab67aa00ae0f37da9f14891c85186ce0af2c43dc9c64d6efb298e1ffc309e668f610273905f6b2b4c6c66e5deb23121f266f3a041a5501cd87129113fdf26053cc003429bc13689f7c3ea49d55502b3cb60067b5f64691443f466ca2260a70795096bdc39363e91a70cad32ce14bbf8442069684f78c88ad3cea20c2019936e21f101bfc0060422351dc5b6cc8a98564b56e8e5c06281e0b12d4b074b981e422e4c42fa459f45555b297e70a199413d2f3850d4106710e8fb00757f13b924ada4a1b53b4ca8dd61952523bf9122c998007e2816bcf55f43073f94e9628f81c106047f72cf60008f3b6079b4330aa2a026ce2c6f90051769b426bde644d60d09b9dc3875c3bd2a7c1392ce6d94b97fb07238c19a4240964d84421d2b3e83a1996811d26c7e948722a4dc31861fe5111d6fa02c73d82f89f2c25bb5dc01b27708b5209e998b40f316bb6dc7a7f47f206b526ac3bd24ebedcf6b1132febdecb1102015df8ce0451483dae9e121522b5b4de55b5f17f3f32a70a842a7daf3e1c08b79e344f0749e6529ec67bbd96cca0f6124da5dce7c048703ea524ee8de595ad0926eb549f31316a075078591f8b2131785cbd85067d80d268b653a125d39448dac3650b1fe1b3fd15ba87bbebab1298ac1adeec17ffe7ce738b7d931d4f733b4a7dbc8151cf3da6a9fd52e0480969b8a1078237c4cfe46ab665759d5c5e3898a53a091b9be33d07a12c2f557f213d497f35bf25cc3fe787b6ed511333358ece0a6d1dba56b4f3691787a6833c51b28cb2ab807a736d475572eb776752424142afba480c94b381c0b3af3feb8eeb2048f83eea59844686390ef221c6a5fd7bbb2641063b801f3b462ad52f1eb9afdc08b2616c642859f53c9927edf25aa82beca5951439f29553758bd962d20466277851f7b44961148b7caf2c95479f965ce2abeff019272471222ef5d9d7a67c36e1bcbaa519e33b498c0ac3bac3015aeb63830dd489ae9d243a680bbf8043988f591b82be0ebaede1588115ffa917bf3f082ae04b6742fb9ab03de06406701f9083e20e7e86fc3d090f704035dbe3d04ce3374cffb9d13f97f9de563bb4571c435e34b5ced9fd60469f5a0ac53b3d0e3e4bc6f30657ca2c005c3d95ac15df3394ae28f310035fda92ab0be1cfe640665ad6b735a90da7e3c9f9508310cf34dc00d8231e70c230574bf2ed6a961e7ffe275380d555bfdbe76d765a3f9cd35a1dbb3f86fbc0aec8efe71b675feb0fd4a1cdc22f9d138f18fc9b1de9338d45cad54cf58b14c84a0b5be281e017afba8152cafc3ae0be8bb46a248d74cc7ca37082a1ad302d81d61240c5ba77c0903e36a0f2b4f736890f81febe841a7c78999fa2c03f343ac5badb4997181ed8e283c29e9335c8d9851d24741ddbc2e82716ec43a9dde99c0c238804fac81a0d91cf522312beeba078053f2e577627c18ce73b2a0164837729b02c11267399b203c96e54276289e28a912362c255419fe391e9fc65b2701c95538de10b1a6655e53400aa3329694da04fc561dae535493f016a8b4e64fe89320750b057757dd30cdb332a815fee349398f9bf0f20bd9c8750df367eadd2de628ac02f1a4635ffec925232a8dbc1454a850ad183c60a301af9ebbdc99f884f6a7d160bc43841577b9996821ab078327f81edede65aa37387f0032a0782a8bfbd30802feceaea32d703d8992a802a8707301d234a346fecf76601ef066f6f40714e4c9c9576da9047ca8344015eea17268240589b86e55269a5b87e7fcf2fa87064b0dac44551e789adee01aef7709d43e7a99459e4e8017c00ed086e5c8e897843e006c89232ec132c28bd42ee5e90c280e317a7e67a7bf9f88e5cd650038389f978d468542f52a213c86f74c279eb107364786e268ed9438a6e99f82ed44f5e955a73e50260fa5ae91f3cb746071aeaac06e714909164a6f7dccbb96d080568a37358f85ce24f6f1e94c8c5e92f30458899c6d5e4f093133af566b7677624434c74a2722b39f018a3aa9610595d72e2fbeec95ccc80b9d8729a4d459dcebab514041b0c234415b4e155b7e8c7da200234216e04d1da8ff6def00ab93205bfd1579ba88194bb387844b7349bd5adaaa4845d1b2ff7e548ba85158c18fb23c052e97c6fc2607d45aaca7da05f16b99063086708dbb66559644851479bba0c131ad2ae8c69f8f0369de1ef57141b0912eefc82d938f08a743e992baad0b1443174dca1346e0b2310405d17913bc898658b564ecb33b62f29919be24852f5d5b38ac95a617188528ca285399936af97957a6673e80cb3324191e0c8b28323da7678dd3965f66ed250904799eea3c6e9e351151dad907ed025004fdb2832a689e008e65b5878b6c11b02848fa49f1b46d39f0eacaf71d439a9d42646e3b4388dbb3ed0c18e7619ff305eb5186d3c37f4920069ccb8c979e641a09553f9f685de6539b56594c4178f32b8696d2b4a2e447db8131024f84b640a39f1051ea5a4eda5af495338b3ff2b040374725b6395f9631397d8e29575146d07940ab89a3a1cef42617d023841f83a60169e03225d96fb62f9b24ca1a4ddf3f452e7e6ea2b94070e851b5e1310979c43d50a5c569f8fc46a9a9adc580c05ea6e3b59da984121603f3e4d14378079301f04923f8126ed8df961e70e20664812ee6329699ef9f8b7ae42140688c75500214d889e0422d2340627bf3e0b7626ae0295f24613b5e0442162f5e79b9ebf8694bf16b46a8e7603405254bf06af40557e98f82ec13fddc62a704405b075edf9eaa8c44c08268f375698d01ed5b65b5533001555638ece725d4b0e0c20fd0336e7332d3a50755663cf0b99e66748aa740d580d52a5a72d5e8462b903ea70a0f7f76a437dbc016eb0dc7d9fa4a2d538a1da8b501be19417eec6734fc34f4d0f1fe8d0b53597dcb55ea494dc1401000154cf1c90e9735db8a61d79604713c6039574c53cc27963f8850664778822eab40128e575c7fe22d41439e69ba36c1888bc545dd815a230cfa0d40b84d05bfc67aa861a41c2eaf59b8488c1a69e5c1ec23143010684d55493fe0503cd0db2cc79e7cb299006f5bc0069deccd3a92aec98457e589b91680004ef5e06d0dfa42fec72c1a4ec3a7a48e9ecb129939d2b49a77d1796368ba21fe021065b769fc9978fa793efda463843a4bda1c2e8a1e9382e13b0d36b3f7d56496e9e4c89157262f7fc389f39e21a5de7d9f7021e103c3471afd4e17bd11bc30cdb60c80e97725c40d5635c0a4aa2ebfe53f2394be180cef29ad8b559759682d861ff5c41c0e7954913d889fe805337ca8676842c56c85fea794651ed532535e83935f0f75ff1271c52793788e86159a2e07c74fef4c40f350ee1cf8f942cb05f507a8e7767f8ffc44919c5f8f9ee1168441f4051c63093245f88a8fd563129a79a5dc7fa06fcb77b85061a355000cbe5c351a4cce9e34f3c2fd5ccea79e84e0fbb1161e6f4d8c8a1141514720f7c54c93dabc799816ffa079598515f91f5045f632c1c21da710ffc0098e00da53293242baeebe609a11c0e3abce1f82a4b3904d4bfce6ed43a5f3e173cf3afadeccc6ced295c201dc3c7bab45229a59469e7023703509c518558bcc8924ae6a9fcb2af2c4bd9e4e44e744b2f60a11ea5379f760dae7ac94887f36c8a19f1ed733c1db298633c03d26b2ff2b93b14111723a469ef82c9772ac8610d0a51a3497cf8593622534b906ae4120b9fd800434a3a30af418df01f31e6f3a3c366bf0a94f86ff5cc7191d9144d9f9fef52a2407171391bbcabd04260d3f8b7105bb6074704308559ef8aad2f39ebb4266b9ee1531812f246dbc312a2e52534386bd1ecb89b08c391ed7eab44c2d65d719f175d6ca1428295fa0d3476883872170450b99f9a35ba87c4693cdf47824611528de5466b7587f5437c63e886afba563cb41e08adcb09ea230adeb848838c8175602a48045b42d7df0f9cb652ced3e6d85d647a33c7842ee6bdc43a26080886f35f59c3c306cc221389490df00c82a01d059de097176f840c668cb74626e7b71bdeecb813eb5416db0b5c938642930f0b38e581b28d41741c1a60d68e945544415019b910984036bfcd014d346cac476f06819ce10cb48f879d728c19af7b68b939f0659576988c52982d9d2e8ab4a473e8bffc5b2883a5cf869da28c4623b973eb421372596fdafc010aef9dc84212b2289c11bf7fb6224175e6a96211ed56c6905427f00ef51e180f9b17f2222a22c2c07b8787c39b1b2b6812f423176fec51ed633d19e56bf34b45e1b1f2ef5b7ad2d4000f92d2e8d1473a3bc0a1c074363d5856db0c4aa4dfdb5e637ac12f48a03eca919dab25965028d77ad2cd01f511854802cf1c0f21d2bb081cff7f8e756a81cd20aa596e4335ba990aafdfff3eb1696b354a9b752904ec4aafb5c3587974cd501faef8e2a5195a76b59cc6b00cddb095c8d6d72d1b06dc8b601961e7a6d66575e885a03b0082da11caf578a3637bf4d61e10afc7b4e4b1b5375962efaa475311f266da9bacb177d1a3a90c7113ec4dc698bbefc1d487b8f976267fccddf7602af320a0cd833c3b0f62ff3c68197a9078f4205fd2836a480b4804a6415a5b78454faaed021ab21d82b9f0f8c6920ecc0e7a4e389bb9ce74344f0176c6ba50e926ccf5131de2e21f66bb0d1aa58d998d48d35e357b3a856e14f20c24082d51a5065a8534bd422101df82b854252972721d16d01315d68196a16394d5ad9843081d84e8744700482896a719776b99ea4afd89313896cfeffe34efe7d2e307969083a93b61c56c9cd139d34274a1a9b28cfca5c6e37dff6e013509b7d570d4d5e06aed440f938b7c83c349cc1b100374fc91542ba44f10fba9530eb0e701c70e30c51420316a534259cb6a87a19249b5a590682d7e502639e36f58a75133da6cd53f210b71f4807c19ca50c6c02aea103ab516929db7f0c3c262089bd471e4ef5cc18286977adea0766d569b7e81f959aed86d27c4825722cf98e255c14accf73f601ab7a4a6f0f3fe14ff26db90ae64ce8019484208eed4b58c767fc3eabca251bb1220e887efced8fb456968450370a55c94c20ebe0fc0f714ff68cbf849ef6d139804d1b4bedf655eff4e2abda5881e71388b4035ec7805c6f911cb476923ade0aa5c4e4dd8dc850aa046893f97e199e48b5e6840df9a746321f2889b2b8611608cfe923e3007f9dbf30aa1f9ffb991460e59504026350c6448cdccc4041a9ad3e4f5f25084fd278e8609ea437b43ccc1b37c665efe2092f34c0248727d6d2344a0a9d4357253092c0df339a3b157991384d88179070345a9e0d3ae0a939314274426e5350ba1adbe2709513029bab6b312749c6072a2a7602612975522a57d77db684b4c8d8ec9f1002558bcad6b653044d2979d498f40e014e1de1fccae17da24c58d17ce001c23d939df10e21ee06b0cba50e6a6e6ab2d878f8f54ba932875469b64ea50482a318668904969ef7ac3c9d8fa8dd881109af81cd50d45fbd0cfbe23be082991fad8b3148df92dd1f13274fd688610c62f6938dce7333a0f842cc901f30c2754a9e3913e8cf1a1d42f8303f1b9f734a0fad070a057d84adb6bdce777d065c4e070550b7b2af62fe75a318ae809ab2daa2a5832f51747deab914b702a6dc61f98f7805b99f58a02156d4a6f3b0338ea898c51dbae7edd2649b6e91d3ab6110af7febce7a0762092032bcc2ddb3e0a45cddfef60ccef1786ddec06701fa87be5fd24f8dbd9459a16033ab25f8b83ba8277378ef7ade7a05c9898ed4cbd98e0476d10da5746d19c63cd608807be4e160f7e361c71ede77735d06052f1bec6a67908eda259a6bee06b995e07cc080a8baa0339661d81431fbf7d6db24320f1bcf0154175d7918ecf2766cdb8bdb3a24d3cdf47ccc7cdd26926eac954c96cd333d92fa457628f4d5f831b71d0f0e976362e44b3f05a537643ee022858384a38b26a0f094da6a7dff3f0d7c095f7370823be58e7d98f3c555121037476f06dfb953a4e6ec52ffae1b98d2c905e2828b0a03f1138c9070e5f82e628b2489e5c11c07a383c3865a65014af67883754974120e9da67ffcd6b41014c00465e03c022c269e6e136643f4add6e74f4b3d9e2a9eed720beed544c39275ce600127d212e934afe80e61edd98a5b5566195863a818072d732ce4ef4df4b67857770af606a0a3de84efd48add9e7f9587360566d15a94d6ea3a4c34a557f9b66bcf2423e246b84aea72decf2557a09c5548a35ec4d98aaa436ebb59758911dcf04c4fcf4e3db0dfea89b22a9889d2fab5050b4d7d323097c248a13041491a466fb06e4ff2ce49b699309e08c4865ad18c068bc214a856a5506c52867fb0180d024480a381167909df685a7297e97424c127f896252549bfad90a8b9e1a16128957518a587df4d3b1c49bc41a64cbc85450b901234cb688e45f4cfd0af792021ba7f2017567a55d5c562ef9822e951c1759b7d1747c44f960f0fe00c97ca69f00bf50def2053f678ffd08be685e5cb9ac202448e350cab4c820c1f3e42b783f20c595ba7c97c82b03ac17811e26854d509e8854a9b0e51eca83bfad19d9d8c9838a39b01765891248a2f333791f50dd2bcaaf8ce23c290413e835c76447702f40d7f404037abc4d05559437848f616b0857fba81022c4653042f03936f70645ade805034dba2de7bf787981609a44340420f94c36d1eabe2609aa21f9a8362392998d65184e542301201f3acce932b09a878fa0d578ac918ee91cfe81aff66c87d89c3f21abf94703feb3d463c983a03a4da8af2ae27a2442acfb0bb7971f12f76e60abbb34c295e702551dbc7d2c66146919b4a344046301d30c2d1a550ae39145a3e75738ef93aa181c25b3016b69ab74095f3b540238471b50168b31b5a6b3c8d7d47745c3d83d54c8ba834b44150995ccd20e431693da14ed02ee9444769f45b11ce5a03cb3f2123a1667888c79482e849ea13d4759d626cab3cac88b468a04412dc907060016e45875640ceab77b81c8be1f8684afe9e76ce1fe79e3a64776d815090e64853243ed37ded7bff75279429c99fd1b4ac3579c95eaba757f908d274f7761d89dad1af789dbb0310dc2cc5410472fab8e9dca6955b37193e4cc39fbdec5b2a406e081215f32be64ac9d37d043304fd462d6d6e0f49b941530546be4982443cdf3df24c17cd6e2a03b7e38d895425e492aa9f6b8a3a4de5786bc9ba5bb9e3166f9ec42f2abfe1a3342581d8df21d1ea6e471b357b64e83cc93d91952f1fa3e41131cc4b37e21e8ff077454ebcf21ac4a81cbbb87914bb6e54cece7c4ee5cd9f06600f8906fb6319fba37a12911f1a216c37a85483d16e2f5eb1c6373bb96488dce44b8288999cf613e808aa0eb82091f1658ee19e52e233ce7bf72cdc00d86c06a867f8ac9ccff205c18a96644b6e7b87d409cba2e8b4596429e0ef3fe73e79540edc21b7d9db61bba9202d7d161940e15b1a132eae93b381665e8fb14dc656f9a0d12ccca7a78d310703266d9aefcdcd3f7a19bb56ab28f2f35946c005cac084bde42984cf03f17792fb160aec09b8be8083e7840d1b23849d15ec444d551bcfbd117ee6d788e69cc3d973e5a23fc1c403702ddd17fef4795b462be31d8373e4ac932982d171bd3b85d03e2d46a49ca8ca3fb016510139d0269cf5d302d65cd2712a5b01338338f9abaaefaaf472d2682fec6e1e440b4449e0c379d9b90b62738e22130bea202781e721c8f47ac59311942dbc4b907b87434d486a25e54b224bd1c2e798f5b628e6df219fdaa4fb32b41cc6e3c606bef40928f164e7c0999c9a8dcd07f04fe59fbd31823e48b9746b17dbaabdfc146b30cb0cdcb780133daa59e8d7445823235efa4d852391c36acc215edb9b9815a38af7b8bd921fe0896d6e049dd3d5ded15dca3ac45fda4a8aca83f4ec5bca5c5290840e79c2f599e366d65fd847bf7829fd56631a1b3adcc01ca2fad2f8d2da36c7aaf48bed2e49fa1fda2af308ac12d10191ceea57449fe3a309f5d60bbfe4fdd39a24ad8dd520e0631a56d40884d975ebe7faf0441b683c1a02db00a35317caac65c1e8c992199bffdfc510649a77299d76d2ae720a30c82c1091100cb40684ebc4c517e6b9047bbec196915ac0d8568a0172674b69f789cc33b09124b39d7c34e85240d3a015c4d9aadf73085a771c694a793c200bea7992eed0990de605facca0d92bb7d017a6c195ed17ca72c00ca7156d3a982f95d54db1592caf107d90eae495e6d8fb8c33ae8ecef2162a205d02700036f2ba5b1b877af31517c9d36d416e093ff6e14c7229221ace41426e5ff88cc1d2c51acce27460f1c0338479f9acf1031c8685b8cbae606a2acc42b9b73a8838c32b838e8b442f9248b834336a502f9e400faa2f404165bcc091a4b4d390e15f16520b436bb8337aa57c397a50f0e8f51072732ac788d8f4495ca7c7233ec4479798bd440a0742206a610817938972d4208c943e1ea5dcce11d037651b8ead50af5780ace0c548500734e5ce407ce8fb150762391cfe245020cd373bfc30e67cc7dcd37bd7e87f6f646e26b9e4eafcf8f62cc2eacf852bb04b115c86eb86f0f66603a41a98f5edd434e1383cdf78cc5cff7fc50239a048529df331bf78f61fce2c44fffc08b58d4b2d5051f6e9e1a1194ee655e011938880b5538706f3b3785d194021b4a9fe447f86d65ca87aff1b8d5f7c934c39e7d708184da438a5f0170904344aeac51d860d2c7cb969fa98fd6a2ffccb15a26ebac5ae90d09f14e7e84dab12fa065916e318ceedd7e10b0dadd2052ba3296c8073e6627e25f01484b026f0e5c9c4d100c1b0c02193a6b224617bf37614ae319aebe0f74972a13767137c0e59fe112d854825bb1a94934c17c3d909f76fec40cb818876d98fd484fd5f6fa9ed1b689a7f8ea45d1f8a0891ef62330c1774d2c7c8f5e842af7c29b387eff1a8d01d53a22f00e42e1386a98179813d7be0ebff3220a0dc233f13f5678c5ce4b08241627d6db428f48e0718ce86dac5e04388ee3aa93e1be9c9e80c8cbbfa565d89cff6a7d28bffc48b83e03679395703685bdeaf1442f2b6e78f138d3182055ae7b4de2c597bf6ae0b65a529ca88a05f71952d5978f796baf42f5f21b60ee39bb5d6cb08e994252da400f9906a31491a545e1de65ee83f2a9c2955a17558564f28842fc8e6dd77009a088ec844cdb7ae5c421285bd1b9f7d75cdb36fa8f9b90ffcb50b61ee03553e6953722e09bac856957b63c83f4836a443ff017cb5e8957edb02c2ae8018c9a2f116990bb1b761f6352d64223439f65200cfd29b1f038b04d378654371c219e8001f7f5728c9734870c7a9c0e1c23c14c4c3ecf178c01426ea4a75e2714545046a5a3d8d477c61295c014fce751f7cbc1898c01a38edde76f116499cd3cb6d36d1490a49473f5be223000bd3656eec498c39c65a56fc1afc74d7c22089c5a6111e532a63b6852d74d4cbca6e1cbc7cacd1700a62261ba64467666e053aa98b01d446aae2a1cd83182f528988032726a8ee7e22803a5a010e5ee31864cb4398c6b2ca45d76fcf543d18cab8fced7d423d809aef834da90eab4a21605dbdc611c10d8305cfc2eccd4beb6e45edbe1ac3bc9b22b8734cf1698386c68425c7ca58382010821e4fe8500da5d301b5fcaad406651fd3b59acc107962e4041419240e0a0b23fa9ed0ae53040596035764d941354ea4449ee89c12750d1518491c1802923fbeec8ab74fe9c8f61c2b7e6767cc4bbc04dbc12cb4b1acc5ddd8e4f882c1bdcf1d142908620e77c551ae62dc7fdf8018b369b604b0b85790e74a4313d7efdeb2c22b6c2faa8c1573f7145a17eb74cf32c0d2623027222bb0451a029d48a8191c8e5195d05d17a958993db5a72897c33c54cf2003010ff1996cc183a13fe8b047571f3c7558792e48c2d0519615be2baad92e25c61d028448f2d341ad995349c0d43791ba2cd39206bba97ceab96bc414a4ce69fa51418cf37d0d5014fe41d7f8a3d41924ede9430ff079cdef8c254fc9e82a8cd3362037fd046942cccd32c8511c32f00ccc206f2b5f5aa25418f908d49bd2419686ca63dee365a1e230a0bba1884057ac6b2e6982483bc6db5c5ed2e198343497cf3410f5ed1dd0eea0634a974bc9c2050f614ee0c74536269a080b5165aaba32e377f0bc8d658da2ac5d12f19056796ddf2b8df8c0516693c8e57483464015938ae24615bb2056eb207e70881ce177d4315073b056e59093f77596c998de751833306f76b4796914679b901a02054fcccf16d69b67e02", + "0x3a65787472696e7369635f696e646578": "0x00000000", + "0x3a6772616e6470615f617574686f726974696573": "0x010888dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee0100000000000000d17c2d7823ebf260fd138f2d7e27d114c0145d968b5ff5006125f2414fadae690100000000000000", + "0x3d9cad2baf702e20b136f4c8900cd8024e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x3db7a24cfdc9de785974746c14a99df94e7b9012096b41c4eb3aaf947f6ea429": "0x0400", + "0x3f1467a096bcd71a5b6a0c8155e208104e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x3fba98689ebed1138735e0e7a5a790ab4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x3fba98689ebed1138735e0e7a5a790abee99a84ccbfb4b82e714617e5e06f6f7": "0xd0070000", + "0x426e15054d267946093858132eb537f14e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x42b50b77ef717947e7043bb52127d6654e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x4da2c41eaffa8e1a791c5d65beeefd1f028685274e698e781f7f2766cba0cc8300000000": "0x080000000001000000abc3f086f5ac20eaab792c75933b2e196307835a61a955be82aa63bc0ff9617a0600000008d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48000000000000000000000000000000000000000100000000000000", + "0x4da2c41eaffa8e1a791c5d65beeefd1f4e5747352ae927817a9171156fb3da7f00000000": "0x00", + "0x4da2c41eaffa8e1a791c5d65beeefd1f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x4da2c41eaffa8e1a791c5d65beeefd1f5762b52ec4f696c1235b20491a567f8500000000": "0x00", + "0x4da2c41eaffa8e1a791c5d65beeefd1fff4a51b74593c3708682038efe5323b5": "0x00000000", + "0x50e709b04947c0cd2f04727ef76e88f64e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x5c0d1176a568c1f92944340dbfed9e9c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x5c0d1176a568c1f92944340dbfed9e9c530ebca703c85910e7164cb7d1c9e47b": "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d", + "0x5f27b51b5ec208ee9cb25b55d8728243308ce9615de0775a82f8a94dc3d285a1": "0x01", + "0x5f27b51b5ec208ee9cb25b55d87282434e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x5f9cc45b7a00c5899361e1c6099678dc4e7b9012096b41c4eb3aaf947f6ea429": "0x0400", + "0x5f9cc45b7a00c5899361e1c6099678dc8a2d09463effcc78a22d75b9cb87dffc": "0x0000000000000000", + "0x5f9cc45b7a00c5899361e1c6099678dcd47cb8f5328af743ddfb361e7180e7fcbb1bdbcacd6ac9340000000000000000": "0x00000000", + "0x63f78c98723ddc9073523ef3beefda0c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x6786c4cec8d628b6598d7a70ace7acd44e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x6a0da05ca59913bc38a8630590f2627c2a351b6a99a5b21324516e668bb86a57": "0x00", + "0x6a0da05ca59913bc38a8630590f2627c4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x6ac983d82528bf1595ab26438ae5b2cf4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x6cf4040bbce30824850f1a4823d8c65f4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x7474449cca95dc5d0c00e71735a6d17d4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0x89d139e01a5eb2256f222e5fc5dbe6b34e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x928fa8b8d92aa31f47ed74f188a43f704e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x94eadf0156a8ad5156507773d0471e4a16973e1142f5bd30d9464076794007db": "0x00", + "0x94eadf0156a8ad5156507773d0471e4a1e8de4295679f32032acb318db364135": "0x00", + "0x94eadf0156a8ad5156507773d0471e4a4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x94eadf0156a8ad5156507773d0471e4a64fb6e378f53d72f7859ad0e6b6d8810": "0x0000000000", + "0x94eadf0156a8ad5156507773d0471e4a9ce0310edffce7a01a96c2039f92dd10": "0x01000000", + "0x94eadf0156a8ad5156507773d0471e4ab8ebad86f546c7e0b135a4212aace339": "0x00", + "0x9c5d795d0297be56027a4b2464e333974e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0x9c5d795d0297be56027a4b2464e33397f43d6436dec51f09c3b71287a8fc9d48": "0x00000000000000000000000000000000", + "0xa2ce73642c549ae79c14f0a671cf45f94e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xa37f719efab16103103a0c8c2c784ce14e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xa8c65209d47ee80f56b0011e8fd91f504e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xaebd463ed9925c488c112434d61debc04e7b9012096b41c4eb3aaf947f6ea429": "0x0400", + "0xb341e3a63e58a188839b242d17f8c9f82586833f834350b4d435d5fd269ecc8b": "0x080000000001000000", + "0xb341e3a63e58a188839b242d17f8c9f84e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xb341e3a63e58a188839b242d17f8c9f87a50c904b368210021127f9238883a6e": "0x08d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48", + "0xb341e3a63e58a188839b242d17f8c9f8b5cab3380174032968897a4c3ce57c0a": "0x00000000", + "0xc2261276cc9d1f8598ea4b6a74b15c2f4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xc2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80": "0x55a0acda6b9088a60000000000000000", + "0xca32a41f4b3ed515863dc0a38697f84e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xcd710b30bd2eab0352ddcc26417aa1940b76934f4cc08dee01012d059e1b83ee": "0x043a080000", + "0xcd710b30bd2eab0352ddcc26417aa1941b3c252fcb29d88eff4f3de5de4476c33cf5030be27db95e3a080000": "0x8901000000000000000000000000000000000000000000000000000000000000000000aa2e35d0b0008e199a82e949fea05248f39fd87e2d7d3b773c9f21b2fbe906e103170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c11131400", + "0xcd710b30bd2eab0352ddcc26417aa194281e0bfde17b36573208a06cb5cfba6b3cf5030be27db95e3a080000": "0x02", + "0xcd710b30bd2eab0352ddcc26417aa194383e6dcb39e0be0a2e6aeb8b94951ab6e2440b598d56f335b5f7155d9b03cef974a1a23824f49049675dd63800988661": "0x9a2d540052bc537646db8e0528b52ffd00587c8a053e4507031753105809291df0e50a2dbd8dbab06e5e90557b3cc06cafa2aa2f596e8cf0e5d212e561567462b405fa9965fb7751da6eff21e53c0ff4a602b3945e925bbaba6e7aebe55a5855b97ec36e42f696644bb9a59449a6d91958153716de04fd551d87bf9a721cde97f4a612ee4b3a095eef0b7a09aebaafa6eada7d4157798edba4398edb04fd5582ab6ed36b73785f2a7f35e9dc1774eb39f7557dfa0b7ad30bf6d157fe92f7a5baaf1c6f6ac7f197ca5f38fe6a6faa7ee3bea45b2fe1366df7b5f9abc93acb9da2afee6be5d349b8afedbe726e530d9fde94b98dfb9af755e3bea443df1ce7be56aef9aba9bbafbeafe9d059f7d56eefabfaeabe72bac56e9dbbafaaf96bbb2f7b75bac55e5d759ba86bf735bdded7d62d767a5fd97dcd5bbbc52e6f775f7de5e581ed406902fb245f3abb7c92b5875ed43a7b279ffaabc7fb92fed45b0fbd54fe249fe5ec39d953ff2af3a77ecdd9752cbd2fea15720e9d6e6e6575eaf1be9ee04f97ceced343afe8acdbc46f9c20810f2b8e5bd0fb32b7da55f7a5f2cd5ea9ba52bb4d1104538481c4bf2abd955bd0b35bb9d51eef2bfabc4d93882c88c8e25f53e0cbbb710b7abc1bb7dad9611c8c42588628e8582aa6e6f9cd7f3a9fb965453468ce9c2143464aaa5bcc6576811042870e85c8b22cf3ccb324babbbdbd9d3d8a79c5e853fae37d4d911f3d7a74f6e9452e5bb658b1126331f6e68d1b376bd6a8518306cd993364c8484989119344124208d1a50b172e5bb658b1d2b18ebd79e3c6cd9a356ad4a04173e60c1932525262c4249184104274e9c285cb962d56acc0188cd960c3d39313274b96c000c30b2f18c0004444458a78514cb7d8db8b49748b3df3a210dd62875eecd22df6e88413451401064cfc12bf0c19e2e3e3dedec752df2ebd8fc9bcf4e809e9b28fce6f827493ae79c8538c51dd7c9c268d87ce999a876ed9f206bde899071f42785f10424e83e68c9929ca64fdd97d656a9cb812fb57e653e46797bb95494e097bf1d9912243827e5aeff3d0797874747272ba6edb6a95727e53e4120ba3bfaf8e135c60fb12ce37f19b180962ff6a9f22bf2f77abbdefe70feff7d49f39bbcd3ce8d1fb32872eb9a2ec2261360fddc22d7a934bc2627ce69343b2e39be0671e338f1f7af5993b8ab16f276a21ddea3f1b3e3ac758ec7ed279faac5eefc767a8d3fb3199a117ecb95b99671ea4cbfca167f763a95699a087feb19987ce4d84dbc757e4a1cffb319a87f76b67278a2cf5d1d3f9fb750ffdf3670feaa043e73ba48c2eaa797ef6f6f8611496a0c6f2c4d9ba25bb057d3e4bc562cfdd82ce382cfb7cfed12d13a0b33c808e1d3a7345ec9033b221168bddf07cb95b5ffde8b63766678ebb159da8a127bb15fda7fbba8fd1ad4e0ff147b770eb7a88bbf5c9678f9ed343d0a3ebf410fce8cc15496fca8e7fc1eab7d7efbb93a45d7abbbc4ff0e1e56eb13387047adfaf1d3a511335fbc4abe4cf3c600da119cb3d049fddc28d7ba8a1b35b16f7103f3bf710f4af3ebbeca1e8ec167245d16df42cdca2d7ce1c74a2b6bd31e3bcb004986ef14fcf31e48c4efaf4e8912b8a9721ce0b4b24d12d7ee8d3e36e45b7bd458f21ce0b4b24d12d7ee8966edc43fc5f7d762bb7e8b5430e7a73f2ca6e458f5c51ed5674e656e0874ed4444dd4444dd4444dd4cc15b5133551c31ac2205806f240079d9d5543a8e8326fae500d911ac266081743901892c490160c514228cd100d0885211404a13384c0104243c80821371072239403211f084123a402a130423410f2426805425308b14048034237082121d4859014426820f401a11d843c215445088b900f42a6848c11da80d0152169848e10ea41080842b8103243e88d902e84e040480e84b2086911ca4188072155089942c81921658458218483504cc88a1033423a08b942e807212f10020321461042042133426d84d0086d31840721690895112246e807425c203486102d84643124cb900c0871420813428e20d401a11e085942480b214a0cc1228403a11a0851215486101941b2a03fa06604a92288144164106406419a08e2441016040113244b901d828420080882f010844b900d04d1d2b12023040121c84dd00f82741094260807413708e222c88a20334136081a1384445016203800b206c817406800640740ce00d102081840c2004206102980a001b205102e806401e40a2066805801a40a2035005206c8143f64001903c80c7e7c0084073f39009206089b1f2a80b400f900d403100f4059807600d200500680b000e90074052807a02480900002011017202b4055807000ba0128f633f573841f230001013402a013009900a8cb4f1a3f45f821c2cf107edefc08e1270840470019015404100b8056001406080c900a808800fac04f9b1f337e80f0f3839f327ed6fc7cf103c64f183f63fc40f123f55344c4e247cc4f077eb2fcfcf0e3819f2d3f4bfcecf0e3c34f0f3f5a7e78f831f523881f46f891c60f22fc78f3e3083f86f0a3083f47fc68e007891f237e5af093c48f123fccfc50e2470a7e88f9c1c48f257e9ce0870a7e0cf1e3cb0f227e78f921043c030a017af1c3871fb11f39fcb8e1042f4ee8c109629cd0c40963823011c40527c4e0043227441144cc09506c6ab82e72248143033d46f081c40d1ce0d8e107063d45e0c10437031c3aacdaf4686295825ba60b73a38b550e5b9a1e3282fc90e3881d53ac2adb0e70f4d0730637c59603136a90a30537b8f010dc507343073e8e983cacd8683dc831020e8bee051d0c723c714308378260c3f4402268054161585598400416153d623bacd85185094530210d138c10c4811b6104e940100e006008004813c403386eb08a81093ed8a10208041e39645b340b7a055b17ab3358587494d8bcc8c004d9d2034bcb052730e1679c200647143c6778b2c031c6cd7021ac8e30a18c1512269c8143ca87c02201ab4b0f12f4c8400f1f7af4d063043d3ad003053d84e811448f12f430418f13f4e8d263033d7ee891a507084c60a2c7077a68a007971e40f4e0408f10f4d062821326b8c084264c10d383871e5b7aecd0c3033d44c0e3081e48f030c2d358156135d56c5a8c0ea3c1e835fd45efa0bd6835ad83ce4177d1691a077d83e6a2d1f416ad4567d167da068dc5b56235c68f0e80f4c03be06387a00eec30b3830a138c30a1881d48f0088387183cc0e0c186c71a1e3de0c1831d36d8716607163bb2d871c50e2e76a0d9b1821d4cec10b3c3053ba2d891047cb3638821620861b3e30b8f2d38d0b8117678f1aa04e5a0670d4d226eb971c6a522c70a70e0e04160656015c60932e0c6f0b0e11123c70c72c8a0470b4f173c697870c073031e2ebc1d3c0df07821a4062c2b78bef8398367075c0db2333c28d0daf084e0060f70f0b042e3870b1c3900f162670c1c59785e6002105853e090c2841ff850e29ed94ee01b3041091d5a5c29b62e5d11565978b6e840b1238cbfd90186070b13c630a18d09649880060e19b0622b216420f8916225062b87150f56445805619506ab079606583eb0acacdeb0b4b076581d21084d1018d28b24421a218f606501d266070b74ba98e066f5831b39b8916645c68d1bac8cb071f1d303ef071e207496e081c1f6c58d3539c2ac8670d9781ff044307fc8b4e89912f2e6061a377c20648b1e1e0871316dc0999924e82e2c1d84d8e0061baf032b205c1e4c20fc071e2e38aaf40441c89a36c1ca8c959b480321489cd0438f226e7c214402aa84c7656ec1f1c48d363d6c846e2034041e1004f98007026f0beb8a10334ee82294c50d2f76b4e1aa10c2e246193958b0d3c6a9886184b6482e4376105204ce0a213bb801841b3db841067745c5a2a70cd772030cd9831702cf03377670c38d10318488e8107840cc11e07881902386f4e055080143c80a1c2f3829c0a1020705385ffc08f4890e0c79841e1f6459e0c880176107193dc0f48091034ce744f784c7014e2a870a56604417dc40b3e3073d58d083881e2ad851464f1837b4b8b1458f157051781bb891458e2f3dc258303d86c8314464c2aa20071133cb8d33ee86070d0e28582570344c3063e5825512ab25564aacaa5831b16ac14accea898c8b6c082133103a83c30a10317a1c914309160f37cc085a230408b2063f5ef48851af8835e0a09858e0e0c289c063031e2d78b6e04cb0a3080e055c0ab82e267cc149d13306a781d8044703d60d9c0c566b563e302108ab1e641a30414d86e5c61859067ea6c011ebb10407031c583832eec3aa0caecc7645cf0f565fdc10a387113d3da84bf848011703160e386e10cac08d1ffcd4408a00c7951e3fe8d1c66e61b3b057582b2c1178b4408680a3e287063f6a2a9afa82da447da24ad52c2a0c2a14754c25137fa851686c38363a70d091438f1b6e07ee072e0b4f06722cc1a3816e0ae70a4eecaae0b6e082e921828e2a3a6ee811420f10703280e3011c0d9cb0c3092038210bce07704480c3c5843326e4c0842cfc06c7e238f0149103091e237cfce063081f1ec8b1450e3139d07860bc25bc305707578caba6e7083a623d43e888e88ae854b04d713870317fc2a5f017ec58916dd9317365d06346cf9b1e34b821acac7044e021058f333c68b0a32647113b5ee8a4d109634b621ba383031d3174bad85ab041b129d1d34687959e34b62d5b09b61f7070b92eb831b84d74657444e87eb06960e3c0b6436745a7456766e708398cd899e27a9009c1f940a78c0e1a1d297848a023840e181d14fc9ca94ad416d02a6a1195057408d5088a05bd829aa91fa823a821a8602810ea11940c1a05254357404b40b7d01fa807680ef4065a25eb41e6838c8d1037999490263225b228322832277ef490a100880fb21db22c01a05682da4c23cc3333077307f38c19c624638231dbcc32a617d38cc9c5cc62f6606a317d30d1983f9862cc34738ca966ba9937986b269a0984c96676317930bf985b4c1c4c1dfce0616a6066405af133468221bb9069e41a69464621a19063a40ca4147206b28c9c42d2402e2195902d904900b1810c420e21bf4821e40a2412328ce4800481f4416e204323b5c80ec82c92879826f2209a8938883b88398863441dc42ea218118cc8266611bd8041885f4435710b084484412c13c704551163106510a5e20c6810918a38c50f0dfcc012c54424e21234055189f64147114dd02f8827a04dc41da810314bf3207a80be205a895522968843cca161009588b196010a439a06310411043d03b84497a12da028e8277acc8f2c415d349826a281e81334085a043f32d041fcd8a151d0256821e011601a9008d008300b7806da0016016201af80328053b00530086806440282018f80646014f005d009d8043c012c01ec0245004300498044c392d237c39318a030253969224305982ca180042600c3806e607040035660c00b0b50800b0868818524478d031c821122290cc127341f58f80c6d24a1c5188549d20c4848b6a313a6242424158f22184a0c4c4f909a2041898109c9d21294023c35495a5a8252801f3c5830c3930c4c494b2be0a4a41890a03c213dcd20c393a41a7cf008c3afa5274c9e929c202525c5d08307188612839426a42751fe892989060b3071c179a880098909d293289fd484090a029e98a44069d2e47b7810c1afa4274f4f9ca0ccf02485890d1e43301394274f4c9ca438417a82b2f4427a12e59938497912787cf17878e197059294905e48494ea034497282f4b240521226284f5eafa405ece09102aee14994774a527282e200251e0f143052929424290978c149920c323079e1070f21f8a5d42469862641f0eb8929c9296906a627cee3044c4f62d8e161026ef2e4c5f4c4e9c98624240b2c4589c204c929c9c9d213292ea52650965c3c3cba300d484c9e983861f294d4020e8f125ce0c91293274f4f989e90c08304529a24d9905483c708d8094a01b61e2538e9898993274b4ca6acaa0af8a5d404ca52058408393d35f12a119c5387600b2429213125213d25bd906a98010a13d22b89095214a3231e185d408ac2a4891021264f90a2181d7115085e7ac22406a427a7a4d78b0912125745c0484c4c9e38614a424a5a7a7a7ab2b4a37a80a902b66ee125293470809d386152a36e604811a10b3cbd9e3091507dc8a93db00c494c3c6461214816a800d294274e6ad41db802525e4834242d25210d29221485ab5133c04e9c307945313a2aa16261a7195e48b5eac02485a75ee1a7274b515e4828d400c509d3931a72d41c58881092902028203d35495a4ab251ad2ccd50a502525ef5064e6ac2040509a7c672e814333199e109e9f54a5a62d1233053d294242370056878b284d4d13478898994a6244b8bc0509c922e8094e4b4a244e028494e484e4d2f9dfa03273561b2140589e90992059c92688022c31313141a982ab043879043df70141aa2181da9a81038089cf4c4c449142936281afcf484e909120a4142412cb00425ca193c64480a921392132834204d617a82244312131253052a0eec040a0d484e9c30415a80ab05242c70740c76e284c992141a909abc7cc0af1ab4074932f4a03c60284f6c588624261c2a063f3d3d212d49a181041a06274d79f2c4448624a6d713a4a418742818fce4943485e90952520c6b929e984c79e21405a9c993a50a48890244bf6027494a2f263420357982e4c464890900e80e38a9891427494e484d5e39d48ba725293438315962d2e4d5436fc049343c7972623205e995f464810ad000c52906241b90a2181deda0673889e9490c3130412281da8093962a20058a131c8a053b71c2e40aa7a697197e62824203d292141a386a0527bd985400c906ad8297a4d080f4c4f4044a14a421148444595119f053d28bc6802d10a5090d493420b1b0800520a0c805252b86494b1560f2c4242506a4a726494e4f9c303d4142c2d1009c2fb4cd9a3362ce3cc32c4bc5620f61d2cbb431b4316014482591a9634e223a74e89873ced994c62863730d4a29a5109031e2e0a64cbb7b366ddadd92768c9199635366ca94366b507236a68de6a1b93d802bd318e594d15aca54464b63431882581bad0deed84d993b4a9e1542c896ca8e55b28d4969ad95994acaea4869c75aa5acb5326dca916354d5aa499d5a2d4bcaa6dd4cbb086577263bd362a459edccc618553636654a99e31065664d8b51cb24a47452382765967252ca5b6f316a5a6cad5bd3ba1b328490ce0ae190e8799e37b5a8c509e1a4904e4a994e66d95d29953d2c6b8b29a5519395630fc7d833d29e1da5569935baeaa831cbb6a17100689cee6e71b703203733502b76039a3245817d74773ebae3d87577dceca37d745d77ef745dec60d7cad311a8290fa5b1070f4f07b4d3a3e389103684b01b521825845c9b6bb5927543cada32ceee1aa54ed348bbb94ac9b556ad366dda911976377338b45b3d3dabd8dddcb065c4e19899656400939031c3ac9933fe819d71c63f9c317396c1ceb8994b602e812309cc2434f30f84902384cdcdb035be415f60a6515b00670ae8c1f194b56353cadd3bb03bd688353a46d9355068c831b7ec986394b1ca39a5bcf75e1d9dd8daa431d6e88e71520a99524a690d1376708db19b736acf6e21b2b96db7ec1d5c7747ae4da916234338594ed9554a9ecc3ce34a93526a1a73ad52b25892c5d2b4186bd5345665455963a59436e3e8285946ca94b904c41a2c9bd2489bba4029c71f38384a7afc8831c61fcc9123c7dd10c6a8c5dab2e39c31d65a2b646ed8cd08e8d8cc8a3a5c0bb1b204c2196dce5431daa051b3d1dd556ba14a6976a39987c4e698258ee6703ae270370e8e85c825e138aecac8314a5637eb00d132737353243272c76e4a01402900b8352963cb4969ec1825a5ddcd9167434a3b76e45a2184915ba19bbb699750239552ceee49257394514a66ae3bba46c7aed1354cd8b1234a137698c0b58c12f24ecb8e313237c481a3d61a3520acb5d6187b87ab1c73c71aa34ad513d927468a838383c3e1c458bbb626355942e4c83146cdc691acb2e2a810767794ddb19b76536ee60819d2d92a66ee48bb3bda8835cec81ce39c2d6365293b56aae5c8183b464be9b4b2d638279d93c6383b76741a73727258b26c8e30b68c578bb1e39c94bb5946664a99d2d8b23bca8e9152cab5d62aa38cb2bbe7a494323777addcddb165b36ca6cc2c5b870ec93176942d5bcaee6696b15bc696dc31f64823ad638c453724ede689b152aeb2a38c31468e4c65e4ca4d999969339592327777777377f4ba3b364b2a9b29a5dc9472c7964d69edd832ca8edd926bad4c210500cf9e323253eeeeee5a9956ad8532eeee1b2ca78c94ca49a5843046dadcb24a96ad52d3a95996c558bb69949aec8edc916f307364dacc94d2c9dc33c6ee8e513237addcb125773333c7d82da90128a534a7368570d202b83e2787a8ce6983523a2915a24290201a14a70d392ba591d54d23a5274c4a5904a851c61aa99c3152a6cd9429a534fa34776c0a296d8f314629638d14faf8b48c9aac7293348b526a324ad99169ecce5a4e1a9b328431726c1a650fa1628815b1046e0b3841a12189862c694ad212920d4c68d8b1c3416480b2842403d313196480b284436869e98952d0007a1c69a5d0e2f1c3a30790138c0495e064e9c90c4d909e96a4308921c9890fb721c909d31324a6274c666812430c502ea481394892530c484e509e983481b2b4c48447004e929c929c3841411a424148d213a7a7137a94f0c271020b8102c5298752132615585a82e274421126494e393c4909e9c98624261dfe9424038e1cafa7d7131b9278b2a7264c5e4c4c9c9e909e9ed0400394160f16f2248624a7289749d252939d123808535294284c90926460e204e5e9c53364a8c70628353ce95062d2a44992d2100a423c87902048429294982c55004906a62733bc98cc80f46443924f614a8ace416878f2c424c5e91689e1494a0f093624cd80f4c4f4c4290a5293d7131a78b22727264faf1e42786c1c24e98989531253920c4e007092f47a253945a1014a13222b16e29434054a93242724262f274d929082280da120244a93a4d78f6a86264f4c2a8034c3eb09d393185a53989262003808121394272fa7a429481688e18953d214240104410105214e9290a03c45b1805313a4271b92784c614a8a3f2b203a4645492a6b9484d12c4a15151515b925300883aca8a8a8a8d84ba2202c2a2aca251c1545e3128eb251112a2a0a16392a0a962d898a6a5854146c4645c1a828986a09c3a2a260148c8a8a8a8a82c5b80406a320ac2e6198b684a3a27a0947410a8b8a82c1e6128e82f51286c1e892a8a8a82818e4250ccb6073090cc260bd8479090cc2607209c360b0b88461b05e028330b88461704904a6112033431a5e4c3833d3bc882434b7bdcde99a5bb6bd5587dec741d7bc5a449a38db1928c2e68aa8470e49116aa250fa72ca5b19c88a16fb190bb47ce6d4212b51cf1c7247ed1672d077a04baf6ea165ce881948e54872c060c05fbbe6d0fbda25e4e0356aa7d4a56b1e996fb74ddd887ae6cc25a19e395d419bdfda930c145d7a734ae895624199ef9b414ec9f6f3560692976385f108cb6c24aa81dc11a301466f516e91336237d38b97ce9c11a3f1f236c1291ec897dc119bb1a67f72470c46cce46af5c1b850ee88cbe0ae154aae35561f2c044c231056b291b80233df1ee343679754a2ccdec232ac6459571fac876f8253fd913b622fb8231683be95dbe48ed88cb965dc117f613fe38e188c8c33e220a89e7247fc03ba55ee88dd685fb923fee29be094c61d719bfa1a77c4604c954aa592d9ea8369516d963be21d1c3118bfd94d15571f6ccb4b6ffec61d311a2f5de38cd88b97d77291e33839358e638ee32097715cdb70f2e839f9370341b72cc94664125843d9011bdd6ad3a985aed43e61cfe9d12c83aa2c7315f4bef8ed2acfa94985a7ece1e704b30c76069b3a734775b6a7dd6f7aa59ed2f4b6b7461fd1897af36d7a4cc33b48b46fd7be9d3b804fd9e974b5566f39a534a848509665cf5c5146147d445f21fa88319b733e73459b737701ecda6e11ff6752ca186377bf949c911295f4e24b331a1561d28499228c1360c8d02a0519f8f5c3d80ea1337700ff209a602d6aea23567304146133734d32162bc13fd52437bede157c2e7cf5e951b7d2fba82b4dcfe44f9756d64a6ba573523abd7a4eb456e97456e9d4fb2ef34ffb9c1e7409a3b03f1095a277676e2357d4d456aa893de58c80624f9be00fe18ccfb8ea969b46b8b3fabeaf774a7d1a392430f6d42d4321abaf86a7d5edf4beea4a9947f9994fef93464c26167b7a9d9ab84d2cf64f357eba53930acf0ca4393d6d3f5d73e6944479fac5cf5cf39c9a54f866a0cc9fb2affef5674e3da7ea4fd94f672e09d25327eae935ed6852c1c28d7a4d70ea83f432ff64d389a2cfafbd3d5af125cdff7896fac282b70cc5742ce585cc03e0592a054fbc65b1a6a3f28fc73f47e5e564d42757d4753353a93caa793c3272453a3a1fbd464a33afce2cf3364e0965428b2fb19fde5c510d1dbff556b7a2b717c4bd60432c168bc56e704141c56245a28fe8904bc70f46093aff17f49947f7e921aedd6a8f9e79d093d736e48e2b796b4f2a2cf42e504c1381265a9162a60ad8380483e5fbdb79a0c79eecd6106dbef52cf5658cb74cb3296343ba6206629730b09f947ee89f94fe25db826757829786cffbd897a2966f8fde520d1ab0e2fb7ef099a296576a87cf1c02582a26858345f8291da07e8d314a2865c757ffa87f137a8cf27ef0a57b4a9a92eac6cc6732462dba8c9967d56ba597068fdc6b85e89ff46fa9c9787977a07f4a4b514b539cfa15f8e3f5d27d613388980baa3d4cd301f8a32f805ded449dd110b5bcbc52b80cc613c8283df8b11d426861df6f296af9af5dca8e8f1c20f351cb4324fd83e9d2e3b7d353ca7c3ae492543756bc0c81968f1b78d8dbdea6f73519afa4e5e1fd600f9564d7035d94969a8c8797a9c978e9f2eeacc05f45c3888f5769a9a261c4f7a5a1a261c437191336082c942dbda3bc4ed29f6419aff44d892f6fbb644d4dc1fa86879edd0ff60f3e5ea5cca343ae85ec2a4d892fbdcbf87895daef0e92f8ed7d9be84b8ff76b82531f7d89bed3906f823f45de7ae589ba6111c621a6e7f4c43d74a02dfe8bf27d9d98813e96ea8096df5981bfa8e59b6b61c7d4d47f5d2cf650a29687b1852ee31b03967d3ed8e533b75f8fa4cffb6d3fdd4ed830d39c3dab9b64b7b633cea8bec61b8ee62b876edbfba2d333fcd5a3b783447e75f9d5a943ee00d63ff8540b988fae795763933da43903a125cb574fe3a3bbf9eab6046b636befd33c25cda35b4df376abda2067a479f4a80d0942a779bbecd6746d7a74bb6dac8db3b15536c2cfaee33ad26e31c6ee868a52b956ae6aca19c1afbef2da2dce56d94854fdfd5cf81c9b6c2261a2afb8a2179595b38b01ec5ab96d6f73e6a2a714bdbacd2161831ce7cc25b14ed4f12d092cb7acce57176ae5dd4ad5dd1a7d64de451f997ff53367a99a72d7287a75a2087fe5b16bf3beead4edb4389b92fecab0e3ea63c0f76506a25dbd3fddca5add6afd78bbda34affacaa35c51dfa68e3a7ced0eb94141273cf5606fe1563d1f4f3ded6d6fd56bbd8d5bf57abc955bf54c783bb7ea01e06db655cfe7a917dfd2ad7a3c9e56ef3ef5e05b6dab9ef756b555cfdfdaad7a3bde6e5bf574bce5b6eaf5bc5d6dd5e379cbdaaab7f3b6dbaa97e36d8fadb30c08e8841f55b42ded6906f83100111706ca21003f82fc7803d1f2a339742ba4813823cd6bd52ee4c240991335bcd245ef9f2d7a9ab7f7693ccd3f6da33cf55075e856a8ab3f45b1277047f0ed8f0d72464c86578fdf0a013510ec561fac04efe49ae7e40f190868a30eddfe048033a20eb7548f9ea685fe047145f51639d2adcca9742aa7fe15f92ccc18af7a6d8c8f0e6dd0d69e0bde0bddca3ccaccd090d0102c1dbf046040a3c167c8058806a25bac81ce49b7325fd2ad4c0d9121518aa0b0b5a7b9e654d908dcc240291411400a47a2441f99db30002b52dc267e8c470460046e30acdc1e1940c358f49139c4127d644ed9b860ecb3cfb28f3e808d9b889530d5afb82b4c3462646bafc22d3d94c2d65e75c81db5c32dddd2e265e596005bb52a6c958d68db0b5df4a016eda3f7c7225bf4e0966e65de1ee4d2adcc2357a4412c1dfcea9a0036cdfbe06b6e63751f5a75a2662268ce5c51bd4bddca66e856e66c3e73cbb023cb95e656001b97001d5470ad2e51f491f90ad147e643d0e838faf8586a05b17f8aafb90e2ab856beba904bf491654e3925aad7ae92ea3a69f0033f3090ccab0b802ba2176ae956e69a07b73050e6f0864e7bf8d53ff8ed1fd4f299dbea51583dfe21ee280096c896c256d94816e5cc669665144208adddb8152b67d504a77e27497ccd6dfc07f5dab500b89eefd0d1c3b3930387ce8d1248c8b1e15383070072569081a66b1e8eaf9ece53afdff668c8357acc1533d0f4c89c91f4e9d955825d6a5f1f2cfbc0296d94ac448a0b6ef8ef025dc46606648c9ac3d7fca3df6ee9f472b668a19c51061d6ac99a1fe2d0494e23b9a3003c3350743bb7f6d862692c59c36e9bd39d79393ad187ec49b9a2e9d1db9939fd75532787040622bdbd1b0c24bb6e498f40c7523e4cf19a4fffeacbf890336237efe49feadbb53166a05a7dcd63e59fd698422d46a2cf97543e3d3a750b37cd539a1e9de7dde98f4e5b68cde3cf18a83d9b31bacaa3b792fe783fd8ab8cd8f22a87af729efcd34ed4cd21e957f9f4e44fee08e82503b5ca893ad619e3d5aa47a95c50d4a3542eabf294541e5daaee8efce8ad7a2dd6fbb9f0cd40edf47e2e3c6420ad9943223f3afbc82808a3bdf2e2ccfc3287b105b98304fa0692789e67292d4ebc7c2b37e68eb2fbc1cf5cbacdbe3a67deaeaa6ee906399a51f662dddacd55174ae59b4adb2e471fd36bf43155aef2e956abf7cb1c4a8e5ea34ce5b14b6e2a8f5dd2e755ea66c7272cfb7cdce6a55bc946e612066a98a5eb3bef9248d4304bd76a3e080497b9493602a343e7b2d4ad251fef0c70872e3acfe9f14ace28cb820562812c0118b68a66c2580f65d5ce2d7247197499795eac4fcf89bd33441fb273baf6e8195714bdbd9d0875e88c3c4fb7a4eb38517faaeff4d4fdf4ccdb3f9d970eb9a3fa4fba19a20fe9d069e6253110e9d39381819abafefee86ea3a679bc3e9a53fffca5db4e765eac75eb41b1ccb8acefbc20c4058500e65e10e2b2d6835a8de1b26e9b61afbea857d2dc52afbad53c7ecbdc11eca3dbb8699ed2b4d9063995339724fae49074e4879c97cdede68579c17f2d80737ba1ac73be0076d93b9d3905e8a0828bbbade8437a50f4219da887a0d1b14be5d241059775eb5feba56f1ebb984ba2ddcf852f7ae69cea1a45ebb14bf3f8ab57a45b527e8cdef748f4e95f919744ed396120c91d65ceddeaccfbe077d1acd24527da69971f3f5e03c0baf68fa55e529f818d44a7d02174ea79b11e3df867be3d83cc3f44bd0132bf651efcf63ed8370c94324fd2df9e793b48fadbfbdbad0715a57259875c122d5ec83fb3bd2a70ca152673e694a8b2fbb9f0cc40f3aed02de9d18bffccf565a15b52b291cc8da65396f2c189a76ee306bbb091e9d02177449f426fef834f6df420c70275e690743cfa7966207a9714f575d22de9449d7971de191848ba952ede17ba25bdaf0bdd92feb19a970eeba2b7746ef3cc232ac268b3a62e140c09ecc246da9be0d4b74723faf1c22e0c84a50a4fbd715d892ee83d265684162bb12b2ee620401b4ac0d6e74804bad86b24023d01d06bf4a100e8e2e8839da819688b962d5ab260c98205c660ec05abb4b4d928edb63923e947f0a3c0971ed3f6f14608b7b0db90860d5136eb8282c1c03ec31833ac79f6256b96a4797618d2c070e6d95d38e3c214cfbec2142b2880c93c3b1191028e3c7b912345863c7bd090a09f676ffdb47c9edd7d5c87470702cc2d80db6d7bf53bd68e11fcfa5d7b5ea033d700d6d4d4d4940baa42276a081d60b9873e28fd06df0edd42ee0030183ce41fc81d2559c14e679f8fcd783da081366229a2e5db7d7e8acc1bb348911efa7456e8a1af3b42d40310df6e001674a24f13f1582c1673b5ff3411198bc562585ced43d848fc58df253cebe05bf29333e2343fdd665be48abec88d2631b72a956d5a0f535899c139b9e79c91c76a3a8b8d402861125dd4f1347e7af05b3dc4523cbcf9f69f1e6236f3cc0336612603cb90888ea57860f3f3598a072dcf35dc40e9442d6fbd510a2bc20774cc1d00f652de7a194203882900929b353132b737c702ec95f836453dfbd7cedd1359dc3c4b6539f35f7de93f9ea5b27c79990d71c39eec16732cc05e66b0cf6e6d6fc819d3ade48a66cc7c27bb46f033cf6e7de83ef22a753fd804283584de103710b291e507748cf3c21260229433f31eda316a8f1e95a24bcf32af3dd49f49597e40675912deea96a5a48bdede14f5922b60b959b5875cc228a3042cf750b36779009dfced2d8b7ba8a56b79e839c16f6f8f9e6c96ca02c4b71739e944cd523c5879081f7a7345707be68ce46d0f26218c81659f2cb1ffbadf3ea887be23de435feb480f498ffed343f575daf87cdc6aeb23912efa60a576197d4489c3dddafcab10ba61174bed30c5b343cea85d3a51b7374bb1b3cf0763df0cc5e8588a0b370f63cf6e9b8807cd744ad1f93f18fbef59ea06561e3e74c82981373cdf0fc6a4b337453ebbf492b09e3d7a1fec3fe93b48542f6f13ebd9798b0ffa8fa7fe8bdefcec71b73c8846071d3e370aec906bf2a6eee3d5e91674186b2a2cf7d057db8b2dbfe9d917bd3a6436c22f37ee2169a517dd3264b6b2bd1da3e8177247b2ddca29e5a45e3dd8db6c9bdece74f8fdd5db99d748f5908132877dd43377c43d75e68ce0c7ab243d3a51538f1928bb3b48a447ffa4b7f34f0fbead9b8a2b6a290da079e8b287de56ee887b66a0af5d7af4326fef8bfae856839a06e5ea9bf24d70eaa7db3652fdbccc405f76dbe39f1e339074c9c52b6d47dbabe8904b22e36d8fbb056114ca20ae94b95244f41863bc12fb18638c54d2e44026072cde43ad1efaa44e0f7dee53ad9c81defff17cfc6ffb7618b34c80aeddb2a2db6f83446af461adfbecf6cc6d67ed5ffd78a7f7c35cd1a7f33a9108471fd1a3c76723f51255210203f17f9c02bd4455de3010ceeee675f1db93f42bb5d7ae87fa7ed0338fd2bfcca7dbcc9bded6adc8ffd5166a0ff5f4e85de6f1dbe68a667be45880fdf4e033d7166ef2a75bd1a1e7d3adc82f3d4e5eef56846574d13fff88c6b28f6518838ea5aa807996aa62e5bfd6b70b3d4be160e6992beab6aedb7aa887a6b3dbe9f1470fbee50174f0b3b84d2f73c815c5cc996301f699851c92e9d9fda64fb7cd15c96e49672e09eb277c1bb7cc9b0e39eed697f3d0ad652ebb1fec338fde57a7c7e87d3273a2b6d1fb0cc0d1fbd9f0d463f4e97ddc2d1ba2ccb0e4a37fd1bfe8df74ea35c5a9a9a9373f3d7adf9197d2a7c7cf9c12fa31f39ae8cffbc18fb7094e4d4d4dfd07fbe8d2abdd82db430f7ae601cba0d4c1f20f08838ea57090aa3a589ea57010f3ec3c39cfce3e7c00cb2a74d0d959ebbe317a7b73451d21079d3979a59d9b8d5b7bf09b830e39f885e5ef76d87d611022b1d0a10793e8a46e68f39ffcf60abd568e44b87d74ffd56f973dc40ea19323547424eae8c96eb51fe9beeebbbba3774708bd3ebc3b49a04b87f17ef03b425803cbcf3e5ff7ed36c790333ae8dff62d800e7a84ae142f515be8f54bfd948c8bcf6d6cc409aaf929dbcaf6063df95b9b6fa23fa506b7ada6642c36e20463dfa4c2436f670e898cc51e22e9625b0ff1371b81fe4597ddfae047af48179d9db9239e7acfa7af52d73ab0dcf6630849b02c754311cf747a1f8cfd8440e870d19cde1a74a01615cce2af0c5aa130b66d9cb3e5fbbea46bfeb2b7069d575dedd6a0f3f2e6402cfea2b7069da757d6a093c61b9a8db1460de7b851a96e4ed78693950dcdb2ac861c2f6ba8c19366e5d175d0a89c669d99cd5fd93645e62fea759364347f715e5f2b99790ddd739c7346a5f2d766bb9c57996f7fd5d03dbd2fe9d45f5a96f1bc74cb621e9292eb94f755bbe321b9a9ea7dadfc093e747a5fd69fe06fd49fe0576fc8dcf997a3bd3896f7a7f3eccc43aff68ec5dd17e770755f51aa5bd0b9fb8a64ba055d762bbe62996e41afaf38c5f68a66ba053dbbaf7826a75f114db7a0f77dc534dd82fef13c7b906eb5f229f2572a954f91af62b17c8a7c96a6f914f95ae753e477383894fa14f9d4a3943e45bef4e8d3899aa787e41129fd9539478b7c67d45fd569965d9d6e45ed47d3fc457d53f96499bf542bfff697f4d7ca55aafb5271b722ce107f555fa95437a75bb10bfaaef5beaaabfc25399e6e457f69dbd6ade8aa5a6fd7adc86a7ddf6f3af497530d394fbd86ede97db5537f65525e7a9d6ad0f1ed4d7108691c01065f4309d1a307e9a213c5e9f1f3f4d08b473a8e479de99bc71cead623b73fc9ae875e5de635bbafac6afe247fd3ee4b7315cbdbb5cde77d4daf99e6d567e6d3a354ad3aa7f745551eb76e417f597f928fd3b156b7e396756ebbd2e6a8b47a2bd5f976795fd2b3292f73ebe3f9f6e82c98c3080ba1bf2094c0cf589be73730051d4bdd107bdbdc11fde8d0fb280c59f6c29c2e48c9c2cf1857f8793ff6a77e28a53377d41f9da8a5f7b57c49698097137ad3993b821fef4e127ef3d1e35582ce6f9e9fa8a7f7c1874d5057aadc20f54fd9477f35c17488c28b36ff243ffa2b7a930c670c218d7fe26e7d4fdda493e54a95b7a19dbb255d4225fc86b9242c848ff78bf2b008b31dd8ad875e2438ac0ea7bf9a6eb8e611de17f597f5570e6fe25ccefb6ad271e838feaafecabca97d55efab693acbabd3fb6aa2be79bcafa6cea5d7f01d9cfbaaf765efabc97af51c6fb281e32b7fa93cdea6e825f84e769b6a38cb9b32c7715facfbd21cfaeabee47d69be79f55713cbab93b0f212ee0b7abdaf1202936b017adfc9b5d02e3b59e6277b4aedd2334e89f4f6cc8b9f050dfeeb7e7e3c3faf529c5e7b28faf44d07dee80c53e87ed9437005f85406894a58ee21e90cc49fc9f2edd106697cfb0755ae79732c48e93ee855d1552a2fc8f6c1575279dc7cdbee173d3a515425698f5906812bd0559dca15e8ca32a7923985a13a950a4375ea442d610cc1181fdd42659c0dd27cf4e8c47bcf525880791b80799f67291b60c1e2cd749567380e2f8e673ebd28956b3ab311ead98d52b932eb96e50216cde7f4a04bf3c234a9c08245742abf423d8cf598ca355d3acbf9a30da6f098546f7df390a8de76e58ca0d7d8fcf39496ac6fce39dd3c26eb5c0def833dcee62c967755ea8582b9542c6c97a5b4f9caaf50872e6bbd1dcb712bce2dc7714ed43b76e5ed588ee3b8eb050b85d73ad1cf0b954da75a76a3e6adc2ca1c2aaa7a76b1b82b732ceed26e549dd3a7d3cc378f21102cda4edfb132aa427536b0d48ef5e957dc351dfa8e9db02e1fb9a2e8703a732ecc1a9575adc2321cd3b1d40bbefc57596a0b36b5bdfed72d49b7b28760479647c1e0ea9c73e61ac079548e8e6f4e85731d87dc0b3abe5d281cce39733070aec3795b561d062828178e77cedc0bedea1c025472f8e6543acfe1907b21876f9739183acf71451a1f1d7ad6420e022bdfe12c8fcaca596ec3835ab90def3c129ca87778502cafe19b07b5f24d05bff36c38e7502cd7c1e1245ca8ceb91a1e152828ce3bdff176e97854a0a03676b13ca81e87f21cca5d87dbf0a8784ec59d8a0e878aa2c2e3359c0487d271a8121c2ac73bc8bdb0721b0e39183ac771a1602e96e7f0a0a08b75a10781d5741b974ae738ce66b8f860e68b193166ca98416386cd0bb87ccfafa0932fcd6079bee2a15ff1c5cc9b2bc43c8e67a92bccbc9d1e930a89ea2716407c748845968f9e5d31868d8d3908e0701cdf3cce571e151cbe721b1c046ce05ca89593e0cc466c6c1904a036dfe1502bafe1ccbd60c3b90b85c3b795c7ae1a1e95cd6df80e8f8a0dc7e1247854a036c7e11a0e07c3e6362e15ce6d380ef7020e5fb98e07c5b98d1b45e586ef70ce330f02387c75a96ccef2e69444d7ae650e86ed56b1e19bb36e94ca85e39c17a5727546e6fd82de0b96956f374ae5dafc83de9e398ec7f2cee3a02bf3769244a7de710de8aeac3c4c769596a66b9e398e331bb1f70b7acd55f783de1e9922ac62c3712e147451d9b950391c6ac7a1709ce53bbc7ad437af8a0ddfe1373c281dbfe1760056a4e40ec2c43e73fad95b98f1d0e5b90a2d190f5d3ef3f9f26d6fd44ba27da6facc550f9fb2795e43d97ce6f1fb9943c26b3ea3ae7dfde69000d9028ad8670e392403b022f559155a56bee3424117947777740ee53b7474378ac739efb9513c585470e5007375de39098ec5c31d0f8bbb366779512c27c173dca8cd49c0b9512c6739e7363c28980bc7bdb2ba619a5478a58f21908fa56260e6a14bc7c3e25055a0b0b82b6a73ce6df80d0faa841bb56d4e824345d570122e1677ad3ce76271978d1b55e36271d776afe0dc2a2ab8707c73ee5661c5a0ccc3fb24f98a879786ae8affa697227e89cdbcc3ba7c6f9baf5c3a47220d5839cbbb48a40acb57170bce0d9339cc25b3e94507155c5dbb562ea3ba0a0563b1ce06968ac5eecef4955f71d7ca33ffa27c7677e6caa17f4b2211d6a5c2f9e6d22b4c015e1d0804366007129c8fdc0bdbb6ba1de7c25602ac360e0f96610f7cbecccc4f9518e65a88ae8ac1978faee2f2d1a1dca2ab5c722cd02ba5fb4fc662ffd43d75ab6ace08063db3912041dece0ff6b0850fbe127495abaaaa1d7e2a6f8f763250bc932bb26e33c8bd6a82539073cd396f33136ab3ced99b47ce36eb4195e3f0e82a301d4b9179f3aa1d2e06583e5a6ddb7cd354398cb8156b83b067478723e97674f470b0088eebe138af27e37a5ceb61753676767a1acdce4e8db9e325f84e87b34198b343c676ecec781c8edce1fc86736e396fe74b0fedf8748bc3156d6e739472b8ca833e8733cae12acfb824d1553e3924395ce5d6c6d6bdf5f0709c13924d876f9d236179f54dc7e65bb77965715ecf6d8afa9dcd83cfb3b3e344dde3f16f453b1e793cfefad3eef8dce15cc739b72c71781ffc1cce393c1c383c7e9d1bdeecd2ade92578f14db738cffba01ec7fba06a781fd4cafba0e0ecd24350c67e3a8c6f1a8d4a7577904457caa453676e854cfa971ee27cbae538ce9b73e8ad50c039193664d0902943460c992f6c2d1677f9e6aef20be576fbc9b9dd368ee3dc7254f3956bbe5af974bb5aad562a15abdb3627242a95f5ea2a95caebfdece6b16e53d4af1c7a99a7f2f8bbd5ca89fa59abd5eaee24e11c3a77e1976e4d77bfaaeb518972f7fc7accfddeebb10b56d5ceb3141074f0b6b70bc364ee44ade29448bd40cdfb7878b332dd9ade5c0bdaabee57c3abdc7213c0bf5ab445d9f0d0ddbbe22e28f728d725c0bd31bf56b85170aff793d6b3237cd0ab7ff34b849ee62a2fbaf53e1b5e73e65a68bda68a9e95e921cde737bffc74cd6b33dd9a73886e7a9be9a122262ce2e7e6ce3f1d7ef1e9b509e03e25f782e7ee917bc11deaba5fcf33333bdd22d31e7574789e40f331e7a302604ffd697a94504e53aa67f7697ae61fa7a4f55fd44b1b9a767c853da55eeac11c76e1e66ddaf1d07ba0809c510b881e5c4cd802002d3e597860b91f1d5af1fca343edf8e836e8f8e84f3d1f3d0acf475fdaf9e833e4f8e84e707cf4253a1f5d02373e3a0c257cf41748f8e82ee47c74166c7cf4156a7c7403e07c74a2eea31f617df422ab8f3e84fbe841db47ffb11fbd15dd47fbe85e3f3a0ffde83ad947cf991fbd931f7dab0dbf1d26957048fc21d4674431fbdaa3f794f970ea615d0111399ea580f0a505b7ae43870e1d51870e1d6ee5a643870e1e1e1e1e1e1e1e1e1e6b6d4686cb47cfa1927a99c3d92b4c817a4f146f3e7a0e68a360d34306cb47cfe17dfd3b4e3d3612f42ccf31a48c6ec783fe833b4ed4cc46827cbbddb61c43cae8320ffaafbffac621f9fa39df81cff7e304006bd8436fc7330ff62cefcbe1ed1aa724fa8edb8d53d29f79f5a3ecd60b7be87d500f9de59cb3e21b2d9b5c10309d43c0f46cb5b26e599bb54ed4d56a1a97d9e33d3d3d3db36787efe8f19e9e1d3d3d2a2e494b875bd761ada6c12f39bc0ffe8e471eb7ce73bb4cd0f3f8b7e3ed1967147d073e8f5b9e1e4db3f18db55a4f734a7cdcf01ebfe13d5ff7f4e8b0a1f5cc2e5a4f4f4f8fa7ad7abce7ee24d171cd755c73cbe339c9e7711d3b72b232d6371c6e1d875bd79cc7abaf22a1e7057b7b9ba2681bf83d5e137ccd999f2bd2208fc7afd27cc7cbe1443187c78fc3a36cbc4caa5bd26fa8bc0fea3fdae6835a791f54ebb5bb93a404b75ec2d5b45cd88584f81f6df3d2352db44d8e0787e8fa299b1e8259191b1e1ca2839f49f510cba5e7f86697363da452a95444adb2728b6d7a48a5f22ed3435dd7755dd6c5ed835fbe732bb7ae53e2e3bbdb65bad8a7fe243d5e73f6a06735144047f340cf52517c8982cb135163e6879ea5a0e0020596e71923cdf73c4b8d71620c98313ebcb5b151ef8376db98abf783faead0f33c2ceef236e85d28cfa3542e6e146a75bb6d9ec72ee849afd0729be6b1c629f1f19a33b7c2ccbc1de8fcb04f523df3a31adf64f07e500f1df6902b824e149f5a890feb2cb7ceba934bb7a475e9d63ef55bff342e497abce5a057879cc3fb39bc4ffddbca2d73acfb453dcb33efab9ce78484f3ea486a38773f2724d5711c49e7d5b91a97bbc539773f398473bf26f89cd7fb752c6fbb4d516f1d7a99c779fc2baf09be75cee3df3c7e7e8d4bd27a7b7792a85c73d58d6dba255d7395981ed25c7a8e4fe3f2d2350f76c90185e832174208213a78a9f3cb213aa5eadc6672c0213ae8b25e28a5fba7eeb3eb058bbba03a892e549820e68a926cc684000a17150b40c15c3eae4390eb4a902b0cf4c9a587e29b1e820fc4f33bc9ffe29b6f332fbdcff0886d78b49926008f2f2f1d82b95de6c22f4d80ebd2730c29a3835c9e5ff6d04ee639ef64f785cef37e4800cfa5434f0702813ebd1cfc037d7248705eb8ee79e45ef0dce742f170cf9b7381c7eda20fe89073e15ee65cf0ae4aaaa783c0d8e8ecf3c12e3da479bc43cee8609797ce16bae6f0aa1c7a566594457e95b7aa9b8aa56aafaace32b7d6552aabf2693de899475d95f9acdefaea56e5adaeca37ef835fdd7a9f34e25ce5d1b35ebdcd89a2ddf839ef7b6a96f7c1a7feb60c513039d7bc9d24f7b31e5dfbaabf600fa347a174bedf74eb05c7a337dd32b7dd0fea3795432fe27858dc85e3dc04c0b950519d73a3a072286e14a2e378f495ca63dfbcc7f1d8d57961365f791f0cbf83a4f5d65b6fdd3257b439f4b8bb5957799f8c9ecaad573fe944f1a97f4ecfbcefa95f898f216574b0cbf3378cebec892dac340fbade58ec3b6e7ae5048cd12e2c25b5f843e8a5eb6729186079ee687b9f842968bd651ba987c55d74a3f442518f02b2051431178d1472454db54b0f3597a4c7472f75de0ff6b33d2070f9d647793fa897de5ef5b0b8ab6e158bbb32cfee270910459d1b85965e139d9a9a4ae3257bf5d8d5f165dfa6483d6e143e967a81987f6a1fdde2a886cdce8060b9ade50d16737096b144e962b1d80d307aeb9bba0bda7c8f27b83c3fbfcd9e33a844ba149d7fd279a7269efaf627fa995bca2561fd94cc21a1fec5c91555242da0ffa4479f5c91bcb2760b32b53e12812ee8b2bd3da6d6c70bb9a2d6c75b51609fa59a1013c5b399e726b490113a734db4c99ea59a60d3040e60a4cf524ec49eb567292774f08ef89be09414172f78ee67292e60f0dc245f8781e0e51821c740bd79b04cc7524e74f94fb2941321f8afb2a77a967242cbf310578986e57ea2ccb3d413629e7b967ae2cb7b24f25324fd6b51679f228af4102b890e1d7a3bdc7a2fd20aea21966a42886f1fd243f079aaf7fde0d7ea0575abef4fb75ab6ba65824e5e27f9ed39f6c7fb60cb04ddd7fa867d57a562180b47d041df981bb60d2211c8ac04de10e392b03832dbe6205b98f9c8c4c3f63ed0715f588681a2bf3a966282cbb3940b80f80fbe9492fef38cbcb0886e25e1c567b9fa30f0f042310c14fb5f1d4b2d71e6594acc972fe2a3d748845dd1619719a2d810c3a2858b8eb7828a10ade0020c4b7ae80b1ba1662216dbce33880b741173b55fa08b18e45a982798fad91e150bfb09b9163e2f5061a257ffa0efc4db7400fe8c4a75305916d3f8608b177b6f9841c45cedd395f8d282229eb917fa42d51ba6e900fc71092925c44085810ed50eef0162c6aee87d5eea6b01edf542556f5f00bbea05b30315c49631621f1df6e5e8437a8d3ea40f41a363574ce3832ded7da1dac3645746f7b293b9caa968ee624e01edd65e2af586a1ae830a2ed5a5a2dd30ec6a0f0a8c941d9f3995eaedd1db5981bf04b457678e85e8d9555a62407bbd61e865caae1274e823254cf67c42cdb7dbdefedb8eb48f860d39ae8a740b03ddc769be9d47023de40980811aa6206fe0b85bed44d1473b1530d1d9f5e97ceb4422ada8f391c8f4f6562432ef91f6d1def07ef5bf5611066a875bba788b14b19ad8b104967d6a27a10609346f216743c74f653abb74dc67de16cc05bd63970d99b0dc44a27f3c86f48fdd4cff388dcc3f688556ffe016126c383b472239aef9c7694a70957f39d63f9ecd3f1fcebf9f957f4358fe1de9fc633238fe7113a901bbb0112fc1eb3c1b5e0d8fdb078ed779dc3e58471efa8adb2ccf435779dc3eb4340f9d04afd26c4a3763bc0f5847d9e8e884659f0fb6d924119de823c214e0334ca16fe01200055d2be79c39249a13757309585da8282e511cf183980b0a4ce654c0b48799ceb5053a793fff793fbb74e4ab76bfcd35cfe956f4ba3d3bbbf43ea8e63b00d7742bba170c14d530500fdd57e4a37f508d18279c1073e433b7b08824922822caf6050c982f99c7748489470b535f21f5f0b62017ee48c6a638f57605a319a114ce0bf24037b96994c24fa91e22d343746bce687ae6d083471e5e28d5ade864a21cf9e9f0870efa57645e2f3b0b6017cb590e358388b9581e3d684a37b9157e98d1c4cbeb85f3e62ed4ca7550c1c5ba54a2661031d78a73ce21f75ac0cab90bc5f9ead6e803fa02d8c55d30cc46701888361941d954342f0e03c5e944cd29802b9a1b4c00f4ac097e07399566376b5d6e9bbd3baa14bec2987d6ac326e1671fa55bd0610f1d74d846d52a225866231a8bfab7c4a3e5bfa5eddba32f1d79597d66ae79fdcd2589697cd081a99f578b697cb025f6d44be1217774e4a992234fef4e762196232fa11518db3ea66143b7be2f74c8463a0aacc29ec252b7d433ca3c865ee6ccde0e99a74bee884a66eaf173463da3f436a93095b57793b1ec533a27a3e945d27b85e8bd647e4cf49be2d4d47fd94bb7ed61e0a5f75de0a343bdf43e0c7c742a2fbd0fea63d44befa3f2d183bdec408c0367de665b335d45a1ff35d1871e3db677818749d85cac931175e846f461930a9fc1d02de8d5a7c359a44875c91d499fed2b70107734bdd52d68ab272f0db3a91785fe175dfaf4a4a8beaf9313f42755e6cdd9f7bbf2dff479914c789b580fbd5ef97999e84f24f2ee2089f4e36da22f85fb78026623d53abbed8e70f6909d415379f5ae9e4fb7a073eea97c7247d3abcfea4de7d1e18eaae774ebb34e5df3a668ff45a7378af1f136a930f596dfaa66f5aac96e49e9aa6fde1e230c5c916523aca9201be19f5cd6b4e3a7c88f53b4e7becd9f6217353f25fee69672472ca74ea91735cda9c7721557e464b472e9452a577190634ec51d653e9db922ea659e15d52a79745aa005fa48aec87d8a87ce81cefaf77394f9d690870ecc6781ff9268f34ffd2f69fd5ba21f3de3940c7d74ebc10d744d2a4cbd7673b8a2ae5bed4f103ae58ca80b47da5639a8a5b31a57047de8a07f3fdf4a4d2abcf9a7c85221e8f253e2438742530f9d9eb4aff0e14db27dbd08e0b79e7952b8b77773c9290142b359b7d625972473eb924bd265fc76977077866e41276acbf9763f96736eb9ae736b39d6ca372fe23a5f6d5eb472bbb965b9e536ef63b1582c975c11e776e5ccd9ce83cef29c5637a85bd0b95bc46b79dc6b05ebd9fda2db23293bde5e2f38ee058bbb706e14af718191dedc0afcb2fb99d3b6e69c8ca22b2df19ab73ebd48f3cc9b1eb923f84e46d2ed65e2353fbd4815b915f8a7572e097fc1ff45df41c232f8e8ab319e65e62587005e23b922ea4e2aed7ab720bdddbcdc2dc9d3ad1f5677392051e6773c4b21f105092e4708f196d541c077900237390881153f7041754674c0052ce0c18b105ccc97a38f1944cca5830a2e761f6801db5b5e3a74e993a5bf60df1fece59532b6bebae6c1879a073923e8c1b772835ebf9d1bf4e07375d8ab0f86af1e19c655e6edd3330f3a516c8f1fd66e6d14b60c2c1f61e5a3b3ac4d448623e0e7a69994984ddac033d6b49967ac21439e61149fd7f01775ab4da4e94dd61dcfa86de5ca4df71a06a3c6951b342c850d61952b37dd6440cec8bfa3924739162067d41ec9b0b61eda911e5d294e5f3a529d1f3e06669644d3b6eaf1773c3e3f73483249e5bb63e59268954ba2c17921ffc456b14a327714ef079f4302c380a1e09ba00ea01345160fd634c4d271842cbb69e0469c59f3c9872e3ff3fe8c72144d4867be09eae0619cd497542f7de948967d17f8f69a591684d59b0c04955dc93fd0332a4746beca1bf9073ae490c4a7d26336659c1b6f32fe8426ac3e980a5e5e996561d9077a125fbdbfba10d2db95fa7e4b3c5a345f523d75e8fccc9ef4968e44f96f4905631222b12a19ddda8d5392241e0049c60cc440dd5c513bf476faf6ddba251daa9bfec58754bebde39c4854da6d06829f0b4febfd60dfdef4da8c1bc6d5c780f899c755b0220c7634e228219e3a07ade85ff77d7728bd467d3f388554996fd7a05499d7da3017aff626478be93eb946b2e9a14f7b23d1f4d007a53e4d0fa979a3736489952f53c0365bc4749a981065e2185a64126ca43f888dc4296bed3a1d1df7d67f2d2d8d6eb59bd129f375ffb1541118a8fd1322a28ac5209c82360406d2cededa1b066a6f26ba6f7b58672c1261974ef44f8775eac7db750bfa156a9278be420768fe9368befdc7f3153a00f3d0396eaeb669e9d4d4141a5b1021ba9d26f8b189f5947ae69ff6e6dbb3297027c9cfab147d7a76eb7d82aa2e76b5c9363dd471b593a43f2a1f2f9ca25bd0576ab22e8199e910602c778c2c255fd9ad18e38a81b0cc73cb731b9662f310ca35cfb28b04228d3768ce245144d412b38cc1a60c1921ba4bc7d24883464c12700bd4d26689a758c1055d0b32641f0662177492f946ae288881584da6832eb923f87cd9ccb3db56b7786b32dde48e649471053dc26b61c6b1e95ced2481b24db7d871929840bce4da10f9e696ccbf9985fa37ad3cacfe49374da4a289b24d0f45879f66e51b88af6822910aa67db4cb34eafdaa1a2b6cb2b4995bfecdf71862342b1032b7be3883ef28f5ed9fe69303994814c33eda61134ca4728d22defaa55bedd28b62a0e8be28e61b4dc3331d83ae5b0ea324c22ee8b677f88d8f44987d7cac8f4428fb88916174f6210fb9211fbd21677432e38ae0953ea564ae4d7686cd3cbaadedb689ee93b0b922c90d63942b66a0ccad75e959cfbccfe733eefcd361ad9c39fffa37674e610902b1001117a28fe8198edb1ade2ee8b85b35ee07f563b038f15dc38eab1a4df16b5c66209cfbd9f0d45d8844ea124cc1070289ddfd7458f7eb9e5797bb5f1bc59f6e45cd230351798c9af69f4fbf852c7bf5457d8b81a237135d647bbfeda5b313fcaff5d56fc9672749f7d4bba7f376701e81009dec568cfcd4adda826e7a746e65ced9952deba4f7a993c02cbb3b48e69dd22fe398228a78ea8ec562b1a7eea288228e7cf423316e3986940139e84472d1f99b39e9908b2e71b09009e2e1d291c7c142e6040dbf0ce14a43022eba8c20cc1822d0028c0e0d96972eaaf53245192cac17ee798c0e2e1863458831559c18229b73d22c036952b0011064b1e24b9b2cac24f1ab9f63ac40f173cee905c674749ec7e870021db8fcd7391b1e73e5cbe7e0c4b70a3a1ea303961f7345cc181d62df9e33260732dfcef342c75cd43912c96864672ff44aefb3e1e59c9ed294f8ec369eaf170bc821193b4e4d750aafe4049fa57fecb25b554de7557a4ffd5e3267ae012ab8b2cbd14774494618d2bfca63aa80a9d16bed24fbcc2796fb4b0a825210c425073ef8c18a178c81860eb0b8c2f200bafa416f59459a9dd6eae9785db7b8643add82d0ab10f15f0b4eaf734c95217e7ace0cfa2e47c79fc754c9c0ab9ec7e030f55fe752d42ccb68067050250756baf83c06072fea9aceb22cbb12860f6cba1ef4dcc018ff79c78132b0b4e1a1ebe7313898f18e839bf6710f4285590b6806eadcc0941ec822103e3fe0204486031135c7f3181cc63ceb790c0e47502d26a514478ceea16301417b1e8343acc304e9743e3d34fda787e69535f3584b872386cff986088f4bfab43e7f6fedb0e5bfce75f8f29da5cb791e738313df3974389ec7dc60e6a55a3dc4eed3aacf636ee0c17f1e64061cc1b7e4b3c48dd511aad0e18e10868ecd4a0deb011c55c75e34eeaa12acfa3e3f74ad810ee7596a2a060ffdeb3a2d54999acf5c3e3f484dbd21c1e5f3030f39ae2ccbb2ee40579fc7c44cf091c810355c3e3f6c3c26d60411ebf2f981a54a781e13bbe2edf398980c82a055a6204d45835e35b97455a5d5cb14f434fbae3986b8e93297cce4f34ad16bb7ba6a3073e9df92cf6bae72ab79aa0f74d4f9dbe5b3a684c9e7b3ab247ddeee10e8f8c01763c83041143800c1150ee808c34f10bba4ab25211b01cbe33c4ba5518226410721749d6f29e8a9670e6f4eb7a08ed7758b49134caca1020931c078c0cc1943f3b3379f5d57966510769ccd3631cb2084b566af20c00882891b9a6041cc8acfbcd3c1673ca010c2ae5b393fae7439cf524708e2b567a9238460e660b9cc17e4fa8a441813479ca0082ede9c7106181757f1dbb31349c5628c043d06c518638cd023ec6e973107411febc74742021159a820a8b7745ff7dd2d7fc9e7a33ff523e1110384350f5d7a4b3ef2a14b98a6210c424bd928270d6a60332948c04a81cf39b339e79c73ce39e79c73ce39c970d1428b307e004110ae49c5cfa9c54fcd35e78c2c805290809502186950034b593085460faa4882881bac9142ca46d9a08c3158a459b3c5144f441438a5f783936694524a29a594d2ae5bd2678e4ec753665e348f44b4db451fd2ef579f524a29a5598ef48eafcb304d831a589ae3081da61083031d8600a206613a1430ac4a1a665cd1c5064170c10e34a881d5b2ba7d96f17ce699cbea99e7341196cadcd988eab3fb2df97ce699577fe8328750ca1e4881c5164c5c8185085ab832293ecbdc0824f8cc799a08cb95b94f0fc9cfbc1589b02bbbdf52d0cf5b3dd0f143e9153661a7208d2d69582982118a3084229c61069b2ede0c219edd3e4b9981458c6648d1411e102112dd62a9aecca7f742c75c99b3f7453def20815d7eca78bfa8173a57f6cd799b582faf978c9da86319f3d5e1e883bb15d104412db6c47e8f73be6730f27d05f0dfc6c542345da479e8bcb231c618a333072f47c8451920e4b394195d9ebb152ff70962e4c855e0942b0c03556121129d1964a20f06c05ced54a2e7106f5443b7524aeff19fd41368266c5e265e13f5fcc54f67affaf4a437a3116d5cf5c6b80a2d9f26bd1e6f217310e8f1ec1367b70d2cf3082ca4a2e31a7439b1e3f883bdbd26f8501006fb2d23b1fd58c8cc8c83813a879d75a4e858ca8c2cdfe16023fcac67292010e1b95b3db04df01b0c32bff9f4f65ef05bbb4ad0b5767aaadfc453469bab54ce77aabecca76b1ea340fa064612bfb9e656e3a2e69248df9c39241a75eb9b472e4975083fcdadf7b536a57e533c42ec097e7b19d0332ca878e999af49e333a72d1760f83224c6560fed64ee3ead2533ac115384c81873feb498b9165853cf1c57d86058d24393cbb0f02cf5c59a205966c87fbce667b33f65df3e3d34dda961a5defcc3f7cb9cb922abdae007a9b757ef07fb3ed8674a1f75f62f43422f95acdd2dec2431497693406f9d1ea23e9dc77e11fbcc9b37fc579d59ea09345d8e8ddc51f58fcad443877d43ac52efcbb21b1e7ac7361a175ba392f4e950c6ea444f9dbaac53c6ec7efdb2ba928a00a2afae7149a8675e3de61fe97ddc118c7d13947a66a01d24525a5cf1dfa4d98d517eb1df9167222ce754cefceceed3eaa1cc61aff238af1ecc5aeb44bd793bd535147479e91c98cc6d112f3ddb81d44bb7d2ad8541ad09aa29be7596c7aeaab49384fa94744593d015b55edd6a2ae7d754d7a8bae65a9b97ae72352f5d69ba76b7cc48a552a9aed2f44fbab6b952269dde2ff3ed6a4e14b53ab7ccfb64ec2bcc415c56587e853948e6d04514b7cc8bcef2d8e535c524da55e25c0e0942c779cc38b777c73af5c9dd1d7bab672aa74ab5d2249556d7ee97b9756bbd6ff31dcd7947bb46d3addbfbc11d9879e9d66d6f4aaa2ff3cd379f46d6ad7538a559b7fe19fc23cf2ef6baadd80817898230556f3fd81538fa578d68fe69aff2607cee1859c8b22c5aba7107d834af5fe5c19fedf4aa1040f4d6551eb46e559bca88e99ba2b61d5f3904c0babce58e605dbe69c95b3f00cc87b76eeda6714882b8cd5bb7dcc6b5d01febabc3b9faa2b43cf51cbadd3655c3d5ea8beaf2d4adc7fcf35987be714adaedcd3824edf4aebea887af35874436c557e2327aa311ece75582de04f55fdc49f25920db009ba8032e39589303326c280e3b44d3b17f1b7bf0b9957c9e52e39999d9a85760db2d0bb247ee76dee2833ea314477a8f8f4e9dbde9cd19679c41e70ca679e8b5532f9b6fe79a0e7a731966ccad3de8137ed141b79333e234196dade00dfae6db75abc747f8b00c462b2c6cd3350b9ea5d844f15d8267a9308400e30c92a72a6dfe8339f149d9800c9e1dc2365d84e2598a8c1e1e927996f2010dfee759aa076640ee4845859a6f5af26d48f04d51abff9e7690039d12fcf7f4a5047607ff3dedb083f9df8b8b16fc708199ff5e62cc7c8dc59e79ead99f7fb04df0a5196fde7befe59d6760f9b9c5c316a2f7fddc00f1d2e507db4932b778e9738bdf81ce45df0bf692a7a6a6b63c9c403cecb9c54b6f0a09f03eb5db3e82532f91408f7e043dde27fb6770f9a7fe79bff629f0a3c39ea889dafbe879df4ff6d96113b619533c64dd8f0e616c95b792135fbd1d36f5aa7975dbde577dc7bafc38adca6bb72b6f67e5fc1fec57be799f4a759b583fa5396f67e5f0595c12d654ffb4dedcd1ca398fe59bb7729517376edb3c46e3b9cde3223ce7f55bd990279519ab7df3d808cf4dd872b59384a3989afa604ec462df695ee5b5a772d67ccb82cf4aedd5bfea7c04bdd6dade8da4bdba5d792b8fcb6033dec66de5b11beb2bef457f4abf0c4ef0adcf1bdebacd38b8a6b3ce1cfca2b39c66460865673c56d6575ed0ab3c2613c3e1ade6cd1959ce72bea934dfb48d6a6e3da78d533951735ed38e57f9e6694e35d565fed12e7d768a732f07290d2c44d3451168baf92cd54512cf4000e0388ee3388ee39c7d3837d65a6badb5ce3e960c4dd3b4ead4354dd3bcb211f6d1ae5207c3f0b4e8c0739379ee160351a7dc04a7a05719887a7b508a4e8a4ebac6468adaa9cbca4674601942d17db2b3ac237e9f2cc62cc61833ea99b73110955d74f9e92dea4491a869e6947a467b04f6889fe5969999991b3a7bf021b3360662797bb04cb772ce37b70ecb742ad7bc3a7558a6cb58ac7616cbdb63a1b10c7fd0498fd327f5ec7e1baba4d157df9bd6a3a8de5e2655f7d7be0483f0f332a9545e554df055698af8f92a25d99c9153a9e6bc0d81649ff16bde5e432099db086517b16fe7b7334aa59152e1db89baa893b0d4f7fd88beddd66d3a4b3d4ba5917a1ef37d65a6799ffcafd28f4ebd6f69fbe9d99d5783130636ba8d1e0b49e659a679578f9a57d76c36057eadf1eb556aa16af783bd066d8d31c626a8efda5ad7aeedb07de39de8d08920f5e871757adafe29fb285f9d39244bbe56a3e9f1c86974eab66a2a8f51e5b5c6e89a4aa56997f9c76816d5eac1c95d8554caf2c489d3ed6c77590aa12833b3537ee835413d6c8af0b2f7c95a217b312a819c51efa33ea5fb2519bbe1bff6269ac60d533fa57e9a671e3d25c9b7bdad7d0a6d729a42ff05e3ccdb55b709a6f172074973459d4d3f80d1fd9aa05e3a7fb0a7d0da6e87dddd0e2bf4a6de2eaf524c2293408f5789bd19a6c1ca0127f00816caa0eb150ed270269d0c3a3602bd231482856e59996fcece41afb16af33224d6aa58e5ec9005dfaa5c7376c81545cde351596f0a7ce84d81afc91857f036c5df31baf0ada75da5ead625d7543f737b79e26c2e5d73c8c12dbae857bbf5abaea9ee077b1554d9fb41bd8ddf7700e89a13c526a88f9ac74df123d4a28bde7dec2609f43ca9b7036f13fcec4709b440339dd2f41effcd0bb3748b3fba855992d88853d07b618771180e9d6f76ad5c0172658ec5860bba7498a587725efad2c32cdd921ec46024805d5632874ed4b08aee8359c078e950ec5df48106104b94608922d4c462ae2e2655d04310389ce00997952b402ee8586cb8d8ad5c6917752c047041cf3c2c0470418579013a940bd187f49139549817a867178a28fa904e2f549817328717caa30fe9d90de2e8434a0afac76638083a48e1858c1b10e10516feb01fa6788204241883e688335cec62ff825ef058cc05b3f4500fa8a55b066159ded2a794fec5fb457d9c5fbcbc934aa79729f3a568449b9f37f32fc6245c850f9f5d2fd54effa27e0906c4674c03b022753f1b3e3271155a7efa778197d935f3e50540b68042e2600c1c54f15ea0d8a35853535340b8b458093930a20a0f62d2a197580f0f28d8d2c509005d585026f61485d7bc9718902da0884d198015291b625c851617fb77819fce9c0233908fa56e70e59fe07bc181d7b8b41be3352ebe50ec514c2430011737a4118bb9ea8d5989c55cec1f94c72f0abbf4a237d700205b8c9972b133d7001c2f7290822354999a72b1e77c7c819d6cc6851ea22efd0518a4db964f8b0a087d5a3f30057e19e430857eae9e74182010e92f744bba0b2e4422ce66beb0190fd3e1fcc77a59db834f9bd20b2c99218a0d309659b173f2381bb148a44bc845e6167611539cee83de14f81f9c026d33e496ab799be2abe055821e3dfad34bdf7e0709af799bed589f2fd9bcbd0c817cfd1336c7154320d3a5b7b4bdcc3cbbfc2c839e5df382de5519e4a283aef2a8432f73ada9be0e03b1578fc7730662e73b6fbddfc637a491c599b7acdae6a54f0f4ed17d55aa4af550bbf44aa68732975ecbf4d07459a778e9f60a58458c910a08bfba457ae5d243fdd22b103005ae5d5e562d3085eea7de664427ffab5aaa5732522c4cafb7968101a8529148d5127d4897522f55d04daf52554bb7200bba797790742cf6d1a362b157ba1f093f459a79e95fd5f2d22597848d00839f7769face0afcb3fafca7b7916147097f7afc2fea67e6c1cf620a2f58547069dedece02d8a5b954163578e67a7a0fd8c874e9d54c24625d7a4513894097f4aa261259b9f4ca06a6505dbaadde97f94ef51aa6f81a6af02b6fa73a74b5cb7a3bf526a0c6fc0a745977e89ade0bbe97cdabf7d540c5c7861ce5ea83fd8e0b2adfa12e3ffe4e0238dfa1d705ee56e1a9379acbef874fd49b5705baecddc96e136beabd6019e2d2bc0aeb08ae2bd0a5b9951a8ba96e15aa31810efd09e83583cda667d828a743486b36a747af5e376733a1d3ade9919b3178eed67409b979e1cbee33967d4618ab47cd23f59879742df39a458f99c7187dea742b73fe39e699e238f41db923a398f4ea742b93b23210b71896a74f36029fa75bd397789df8551974edd0753c1251f9f45624627d7a500f693cddaa3ebd081b7106aa5dfba8aef26afba8376a9ccec3e5a7df6023f0a7673addaa5c24f194294eb5f2d577e80ebd525c34f12cc50513dad5e9569d0cc4ce9c6420f68f4458ee219a83d847979c91bc9f077f4a6fe9344974e95552875c930d5670b821f64d5f0d4530c2d467fe827d765ff47ed27ba95ac1e10699a45e69943933d037e994d2a3cbdb04f55f94c2520bb9229e38dbc639b39195676e579e11ecbfcdeb4aa93afbe67df5598a8b227e3250d69c51b7735e759507bf5df334e718f2fd34d7bcf636b71ef3dddcf6668f54ef7673a2e6eacda0dbdcba0a49fded2a69de9f96a45d73a2c89f79d3634f768b37183d20fdd3f9598bf4d00c1a325d67ba7381e567d0cf9f9f3a3fbdcbf1e91b4bf960992133ac80658ab3752ba70b2dff55339270a1873e97ca19fa8fcf489f4eebf4a79e79113af4d5197ae82bf2d4a91fe9a1214fa92f59f33447329aad8750f82fe7bf22dd6a8fbe815b4c3d6d34373c75ea393d249d52a7ce3ef42a719b4803cb10069d0e0fd72db67cc306dad26d68bebdcbd161233ead8285683a1ccb2b0bd174b2842a838d5d628c43beab773e5bb7841400cb800d7070850656a8694119179f8023b908baecb628d3df7e6413f2ed5b6578e5b3d416625efa60194a75ed7533c1326462158516625e3dcc600b363400638917b878b3d0a107dfc20dca6e41e9b55bada4ad7fe695298eec16bcdf537c769d1eb2d4524dbb9fe62ad7a8a4903b0af2dbfd3657397d8aafb985cd30aee8ab348fbb158930037d3c0fef97d908a10a4ff3a10d9f3933531cee16b775ab15bdda2df84d30f39e62d7adbebd751e89c8e82d36125f05e707e227b52842674a88848418b0f2b987a6fb88f9fb2c95c596cfc2ca19357fc64cdc785c7245d2b98794bc3da53ca8f469140a74b1204641c7109aa19126012463146030382c148d4743224d96950f14800ca2b4525aa08af324c7611432c6184388310000006400606466d80058c7b76f9ae5f43fa19457415263f4dff7fff0c9a67e95f940dcb9dea60be3793610c043ce09f8d7f96980740824f1b4f43028af632f295f0eab3120eb62c747040d92a07caccb380d0053a366dca3f9dcca550c938fb2b84ca6fe81dd317f29869bed948ec1533f097507e3d02e087e292db7195103ebd7f588cb12e77baa04d1b9e8c8a039b0f04558081855fc5e12480a5703de2c6d89808870aabf28d698c9c0f61f503b3f415d3058d02364743b54d17e4b493ba8873fbe8ff884cea76101688b349355ec4e02b66ce3f55955a14f492e57292559de082cd2c15319497255eaea7526d256eddff971d8a92483b2f45354b66b454e5da0c064b5079a0fda4321c59ba1a0d5dbe93cd0ff0033a6ad5ce03009720e85d06d855454c18804e3ddc6c12aa4bcace55d5815be307d40a8af2ef3afde024036b45dffe4ff7ee99af40315eefa20d0948bb7814e09bde64a0ec3a38c026ddcbe9beccb198286f40fff3fe77b96d188ff474f7ab77cd2280885fcf23a926f13a08242a5745415f081a64c421f6e400b77430f77a03fc81468710f34e3163423b3d10fee43076e439371877f7dc496d9d0b5cc77f58d02b72bbc92d51ca67481f6ff608cae9e43dacf23199e8888ff3e95fa184235a298ce3087a2385e88915bd233ac2b198caae4bfe5a916041533a45fdef02ba9c7bb36cb23847cb74b0b14e644d09d15e288f0cbe1839fce496bb149504f471c8f0f7451a86cbaa7bd29889e5976bc404890d47c3a8576e04d01084bd893ecf9d6543f58f22906ed02466b0d6bbe5b5204ccae9def650f7ccca7ce89b6e194b2a8072ec568c26cc56aa6fa768ec5a13230cd99b15471170746a92a3b7f81ef09401798325d59dbe6afafb00c444ccbcf8753ab2b5c64371ffebac8f1fdf9d54e69ed66e04993f72e8b038a435a09e0111f00b9a9373ba1a9270c493da663b40ac08f969d40f08d3246cb256af1f239bcac4af872d7a105c466269c32a386d09ebc66f5d3a8a28ea311573fc7421768c9681006e6c63990aa58a216e0599702192e6800d739c28ae610dcfc15483a379988dc69cb43e5636e74c6a407e8ca8bbe69b1db12309a26edc1dd43e19f2cf80dedd303c8e65ae81ae08fb304d05b6e3b1333fbe5c2bee7d2dbe79ac766bb0d535747a472604a04c00450a617489c1292c1818ee29c6a98308aa6f64ada19630f911b3e695eebc7eb57da3574fecc9a8acd468b8a8a4a0920eea90b9d5499e0491587879095a14450720b9d0fc1283300841bbf810f31e4f4bca1f5e48ea6341ed8a31a90b40c6a910e00760f297a55c4bbd38517da97b42e893bf4607740be9af2d59fc43d19f87e532c0d1149a51702738d0f6847849b77c481cc4582f3451b2e9bf69e189de77799aae9c3b128c0d92f08d4e315b3cd29a3a56343c31265763e21a56bccaa28171e2dee41dea6b6d6666cbbf3a0201881eaca8f0428ee1c55efd335d3ec7cf2c88125a064625d64f90fb738a48d77b486c8c3fca9a2ed2387771e7b769e69a2f6394b053c7782167842834622d1e08ca1f24cb16d5a5c0912de2cf7752d6bceb60d180b324e370b8e56efaedb2fdcafed825536bfa2e763d796dea2a3f1d3e984824cfdb0d1a48c2e6c18a3cff6d0efc777ceb3edc71f099640628bdda7b6f32c0a199d694801465b473ab7e74bdda9a77610d3fbe610ab9020e670309a951322e87b3eb99b0f84a9395804c23e14facc5f2d2c595fe1489f4b4ea0d3e98526fb00bcaec8ef452aea9891a38ab95db264e8e718f974e6d6be38f63753be2b7370fb1e97c88b96598409dc9fed876f08ed2c4966504ac38c0cf52ce2a852f3101a11f63964fce7dd20221c880ef434ccf4b947b567afd12f4ccd69a34479bbd69885ce4205c56d5007b6b8298fbb94f6b7d9602b6d733426c96eddb838fb4404580a355388d219e3c88bfb31a71d3301b080127c1707bc4b360c11d121a8c5d48440a01f771bf8d2e8f04810bd0affcd62dbe0bb7ef7e1055b7f4416629b09472cbe6a0e1134afba5a80068396123065da8b651cfbf62e1ca3371d9293af12de6d8c9d4f3769d39b23d174326aaef54af01f319af9f6d2821fc0e738c0071726209d26fea1af31d7afc611628b9017a020b32d7e6d9484cac80f89369c70f48d2218dc08bb12d3b16c93242d72af5991dfe8e486c92d2b8f36f698a941bd9bea7975f474e643263287eff51f9d438c25029dd1d9bc6b9395ae8bde79ec86b169d41d591d73456302f65822418c703e290f32984c4c654c4fabf839daed13edd7b12277ee3eb73a8d5f1db36527063a2bdb9417caa50bfd93a9682698967b0666085b5c8dee0503e1dc418805865c47845fb6de15ef40cb2a5671152e9c70ccea1c60afcf7683daf49c9a3dac1e60401a30ec566c00a82a2284a968366b8c5d83ad1af9854f00868c0ef3aeefd114853d191db38963e1843d26a8d08edf6b884367b10dce2bf249e34ccb6ea010d1eedf34fe86ee2ba442defe6a1b745eadeba38b44fef1a3aa57824d8dc1b10620c07a8d4c0f63c376febbffb527c91605f83b315a650afa999cbbecd553a79540b3998f28d2cb84d4f553b0addae8877dc112bc927081ae1becf37caee8beb34578cc4509c89434b8bca9660f8b9f58e8b028eb6c6cbe0a58209b879b98b72331d7cf603ef84108bf316574928ff16a2fe498fe67e30e998a881a37727bfd666fba2c03610fe7c71ec2cb4ddea726551f388cddc530c3341bde649aff3f57baeeed81fa754a1ccd68c4d850b86ea669628d02e883fe761336a1da41d98f4a5ba0dec5330070fd219a3f48cb767f1506b7fea2f2be6b1324cffc8ce800de4f68f8ccb24d030f10b1ac43abdbd37c151e4a96a86bf7fb1c294505c6f0e749282adaab4b0152b994b49ae3b0f42f7149f138e7ebd23fab4c3015ff55653029bf6231328c276f5493b5e98ba50d9c8784502365e7009fe7d58f62cbe6da64c51a784ffefc804cdf7173a5cdc877b904c68d374cd07e8828d9f0a77df9012e5781a91bf0bad69aa1112c69b805e3034ceb7bafe296c76a33397cbb835e9cc273b73be7593980cf6a99a5a403e870d7ba141c4c015db92b428c894f48954dbeb8e6a5050c7b1f96c3cc90cde317672f6f2800c2d9b5a303c544893fdb3869b197a9bbdc014000fd6aa7610dbd1bc788c4f93fe60a605c1db73af926a593358501dc9ec9497fc639ac322280ec98ba09a3ca4dd37cac9b2eecec9a261978c634c5b8638f81754d7be83504467c50705ad74bf8cd0cabd08522bb2e10fb1d1530fccaa93024d2439f6722c930de0c454a5e773867de85a8a8f6d12e34bbbb727aeb05e2420be209ea6ad293a914dc6cb4a6f41f52d95811c9c411cffbc8d78ad145d23d001a1cad073560babc9278c3ca8e738f8e984cbd2ccdbacb2cac6786ed5e4b9b59ac37bb9711d51ca40f974209c7d5ca379e77ccb36b95c9e8403e734e0ce63c589da736bdd2ab163286c98446df35feb53b5b61161df4ab6e5aabd47a735b22e47d5567ca725c4782e7e36d7a723783cbbc0a2b09cc3a2889f88427bd0ab6cb41758fc2988167f5cea05c91e00d91f80f5b173b6f2ab509891b8d5ebb1907e36e3d8cf477f562dcb8abd7275f539ef7faa7baade056a61b8ddbae7372922a0f8727852e081689f0a03f388fb100e5cf3062afb5339e0bc810706f3aeef0571c7e3c3dc672a206094c8dfc8b55929e63f24ac40ab7cbe24311ab84fe6b2f926eb2d4f69d219884812437e4375aaf105e84307b1d06e2ca6f04b1da4f935fb42a6cd0424d60cf9107aed09b7c1905257b852514ed7ece022fe4e805c09bf5a43022ed7a5dc401acbabbde7a1da1341173cd669e6669288e65f11a8119b4eb5a36d4ff75429a4b4ea78e5641905140adb5171360337292dd605234392193b4a3bd30f5ba48b0f1b98e25a36f910561aa8f1dd5e3b5221de219924eba84b24f50cba53ef3328d5f3237d33c76de97a425265275097d95fed23b900856574d8e648bff82e2d84068f32f7d2b5673fe43499453506ef57307ec02ba2406abc6d004733328c22cbca945ccaa49040eb5adf7b2948e4d86f9354b2f80c1feed4c91c0dcdcba427b7b6e047a54959dad9e29b104624fa47a652da9828e4fa754405295da4872a84e6e1db7ed2387f8a92bbc626be51a8f75b36f043ad78f202b6f0525f77f99951f83d580cb4c1f47a9f4c4918d040b059b7068b9f931aba13bcdfe8b6362f18e5b26157f39b92ba59c1a05be30d43e1be261e5db6c6cfc127f9fa79415b9238e0f3021d5801347f5041a29f1913eeeff1a99b2b38233f73b14cbfc8b9ae968a2ca6d1d97694b1264e5a37ac19cb0aa661470fdc7e3ef1ced59a9baacb1939fe4dce6291b30d4f07d3c3af91567a7c15cc2724a3731d6f21e8a03fe00ea47b1c208390d9a518abbc97d65b92cf1fff96e600f8a943745255a8b2fcbff8651e5141795b04294ef063f9dd76bd37dac085973a49d37c1603927b612cb5d9456de9c653ca04ce5e6486c61d3345b4b817421057aebb366b17783ba57d7d2b0e6f377f459bcce8d420e12a6532abfae995308f7feb17d9ddc7faa67ea59b64690bb7555fcfb6bc5112ecdfd55f5a3c357714541a0b66af9926e13ba169b8a57a5e1a59aa67cf192f2089a7261262a3c4f62723ccbc579fafd978af3ae674337a79a345ca07f20e6b128f04717e246b99544e16495c1c3bfd15c8ec092f73fb4ee9b0263b97295f09109d56b27e80b261b6090a6e908e5375c06bb69eb980dd2df21d1397fdb0e300b5e9f121b85ba42c8cd1bcad0f470f352349a5249a5ce7be5891f611c8148406b1c3d7350922dd83ee4613030043a5f8b2711982a9646957fbe3fd31ff6c07ba460c6d1a087fa1b375b6d49c115f4d316c98899eb5a3ce7897ed0a7d7c2f394782f4d4dadefad54123b8e5462d17a7ae7c01f51961e4acc93aa013975c3210be430afb67b00591e815a878827428e8015f4e1427e35f7b48e92205855d80aaaeb8ed3a54e9c97ec02301693a30859726b2908bbac7412bc67f84977c7b2a822cc86ef258582a5764a398741514a9e8c9342167d90336198297e699a0c07ba4385b0c23e78cc878c00e760821bf2b207ca15f53c73316861e40ef4ffe055b96214d64a10621cae6047738ec4f1b71d3256d69b38121badbc1e2e0c350989f861324b2cdc26e6a1930326c73f82efd570b41a8918113ead45125107677eccf8239c3fe5d8562b7f9d2448660bad6392575964798af483f546a6aa87a2f0e3bec5a42f0f862a3ecc9428782386442133046cd1f2353106f2b541c000b5e2050ca87a5ba5528cd3e0da025d3cf9b49bd078b6fc477ca9a74f976c246bbe219e5d9b00866c2ba8be0f14ea5c38a2b307f1729ba3aa14b1a7f142b4a8c74d03dc914fbdcb9a7d196468c7a50ef0b594a5ef2eba74bde87009b0134c3b0c7b8c329cb46b9e483777e71375ebed7260572240ca3cefd2328abd7842863c2c27902c0f407c8c647e4764f3d8d344060787d5ff63d42a97c3298f667e8fadc4c6bdaf9d74f458cf88283393a7e709fd2182f61e536a90a5f645f3b3ca00c358cd4924265e8d5b53db5cc5ba32d4138d939572b5292580f595e582cca6ea7daac17dcd5772abe9e522cfdc02c9d8adec3cd8029b050e0c0afba63e523739a24774be19013ee633fdb6c2220dc03ae5264287246190064ca44983d7b060038785fa70c52758b23b387c44f1868ce5008155ab22a911a03ffca11dc7260a8032533e9b7a4df88b17e88f8e5f13efecaa56017c9c11dc88c1cbff167b18a5fc20fefbf013a26c18ec3937f6071b78dea532ba3107e49341ca58d3621b372afd98ce883bea8a4427547e9d6a2328b4a26bd88c528608c80ba40223c2764913add97e01e847c2171c61f4167cdbfe696ce3c4eab6c43314e77d7407216296d0751ff51f283a7cd166c9c65ea9b4c822a5c85992809dc8f6a263b48b9744a1e7ae23d051de42964087590bcd3dc715c9f544ab32d92a898fad51a0fe08dfe881dee21425866eb53ba262c6c969e72c112194c708c300004b2a5ad0bdb8bdf7e8650c49bf2a1df9bcb24bf701d2445c1e650243c5cda4c380afc756e207fb31bb9067ef28818e2c8c2ee73c436cc6cb8f665dd1a528653e1977883a963842607a1c398aff58441bd2239b1c20ac25ffd56cc03740b5595943222b51853d639f03cb1251596f41d919cfc65c0d8a539ac659fa9aae48ee976db1a68ac7b18e9f88a3a431de54a4a1abe4993fab128213ab90fbd12e0219d50cea25b7dc0b08d9bcf06d3ac36c9c2e671188e21be3c844645b09a1a55a5101bea1fb9d8752ffbda71b4cb6b9d01392be33e7df43ba4168191bac3b3a4b6ec2264f5b9b2daf709ddfe698f0e70b1039463138fcdd3f1c98a0ab7f6fdb1703dbd7d3ff7f43d1071af2090d323d03615b7d25d000811b8791be1f83447f0b9bdca761b8810e96787008f5780d94438211df3ea19740052aa51ce2180fd186843495b11de24d9010306a485dd8fa90315dd686437ceef8a05f282d2e89f69026711462b032c411b51f89cdc8ccf5b28fb3a25f1282b8582a027aa9fd9d9976ce265a92be4e0f91d4c930dc95e845aa6645d252dfb616494742732dff45402fd9419a791fb37e8c1dd4531c5e3ed068856a81c6eb013c96f4670aeb08bc0c93606e8527811617d6b53a9857331a2edc238f9e7e5e34c470ae2a69719b5b1aadbb8c78d6ecd3bba14d8f3f06a0382420138123bda7f97217e1fd9cbb6cb785af0832c9c9c1741d8ff3b633dce14cb69e9a54a07c7b9e6a02bc4730ccbc02b877f9af468854354ae719a31345481d9ae5af425e8030ea4edc4d768ffd039c7753d465095a688cb949723bf53f423a8d2521c0232473fd44a86dfa8873ad471f840021b306a0931404976623d2ddcc18aba76fed80425d5f0360b84f01a6c55335380d36f8266dbb075a5e7ca748aef8b464536fee9740a76273b3a7c59f310e46f2bf0bab0c26eb70bc8d4393f3e84c05f09a870efd18ed70e96976aa65872f14378d1c01ada302e4b3f093a51df6db75b283af3b05e5cb48192cec5d5b053882548c837079ef2d078c4be4d92ff6a9c7193fae763425e698891169c2073b37545b001db85047105861e02c0c477808c7c2596ac8954aaf47ce5d7f36d58ddb5247346db00aeda72f1d1a50c26150a86fbf115210444eca6f21916887182a559248e76f9920e1c2fa51346170e766e66b682d78318a5639a0c908fef089bf9bdcd3e69e2e039bd694a896ff1b74f3d0630cdcc55059e3b01f2a034eb753202441564ed363cdfa621e8c5dc4c83f9623adf8b4dd0a314b712167016d61e69b1d5edec8559360673d467dc5859c106dfd6f989607d962bfd110e1523c8d6b400a32a6c5d1b49d96e5e06a0c47feb80224d753441370c0db8b7f31f840b21259374baef3bd3834b4f99f5e6ea4f511fd00ca840dba44842f3fa83f0ad37a46dc4a5988722c8ea1f340e860274506906a2627cf2444123b107dcadbc3a748323f278028509b5f224bdfe3c2027a11e9bb1af73a2023e45bfd6ab7bb7d5caeb260df67fb8530b8ec8d36299e97ea50e3f0acd73fa5899b9a406cc28df7798f2b401bf5d39e086a58646953f4f64c398c52e3f9c18137c6ac3b8996e7da111ccf83ad467a02f3a01944bed52a808e84c1972d743923296253fe72893b536f394015b4f58a159581744d06638c104699b3d991332d159c302d1b5928e39eee10ccac50980c490c85e4153d118a7211777f482d77012aca14c72022ccf5c26af7cefbf50499e632eb4e8ab2d76b1d29c3ddacd0b5bc1409ead1467f1615dac21d27fdfdd1a150caffbb2ae2ace49754ecd08eb1721f03ed448895c1f4486fe8539da9f188e11d4b11acb3b46beac833616b4038dde18c95075c0041c73a079a2e30aff410a94552ad828e79c590e4b1cb54a201e2dda91fc328a8017163ab7687fe29d0ba5cd98efafcc15da16985683f8ffba42d782c6398997e77d4a09bb419cfc8352ca9721dce40689c7707c8c52347f18044813085ea552131657cc90b66111cff864318af67f4261a4a335ce204e6ee2251f812a80b70faecab4d0c0adccdc1dfaf370c411b0e6d8b6843287e5ae3d84dd99670d90bb2f2b1e9e327bc2e626b52e4d751ef2c52811de5d2e79e3e4683bb327c090b684dec8cf2065d474a27397cfa1a7c019888854f8c306654540f0f06bb7722ccd7c4065b015af83ab27474708ab368ce4c5ecf3eaa91ac9a58cf07f7fede2342eed11a37b0d12691e5a628cb602b94d7def6acb76b01dc2becd6b07c7c3fa731b6747cadfd449f8dc1661d05c6926afda8756698fcfc0136a65a04d9d20350719fa449fccc8cca2162eb02fb0f15187eacde4ebfc7d8e7cf79249eaf65d8a5b0a3d1ee1af4485b44d5540e41c41eaa86c3ccd0d035b939db3317df54791e2e91962c3a18e4310992a147d61cd670eaeddc3f2532c45779082ff25f6081bdab80cf15bc4bad84d776a5b4cfb1ee1fca09941a81c4736deee8562a77c695c5195d713988c55c3878b2865945ad5abd482da30caed992c202095c15b1190e778d786ff3598781e53fe6c14bd82b431ea7de4c7521d07cae6c2902f5fab83793a441b0295d41c77d0bba91a8ec6952bf0f8f13ea276006710fa9f3589b2a2028e1d2be58b501d517dbfbaeba8e106a61bde22d77aecfac16bc0af87cd2d9c6f3d8419f513d336d427337aeed32fe86e0747f9e78bdddae1ee8279bb09884b495e8595084be80c416fe822d40922f688d372f3e383cb39f6ea9d976a04cf5ebb32156c0177d387d0c8093068a6f9336c3d40825c12fe549b8f6cb6624f9566bed6abc94dcfe207af48ab13488c902f6f4692bd78dd83a0d332ff243bf7e3a6c3682f987be31acec29f3626c80cef1fedd4d79ce9fd16394700a6cd7a4824ba1fb53d4e00baf2373aa351d89d046145843019406ce9570cada115cdfd49172452fdf15e0bb70b06212b8345a2f5dabdd8f8a45a7b0dc762f7ff55ee203fd0c196cd4486bb60e8c4467af85d5f867d5a111fa3a543ada650fbb0130132df7ab81447341c96fae088bcbb4f27cdeac1c84b72282249459e70426ed39c6c765bca5b0175514f21897a2b2cae759bd9e7e1bbb14da2e0849d1c9bee2ca63d97d3a41a12bea422427e5f6b9a497d5c97e83e180131d6919a11eeef640216eeb56c0d3f601e4f38aa519bdfd54483e85edacdfb35d388512e92d03aa44c7a58138cbb7614f763d7c2ed112112c6b3517774858548346cfbee6eede6363c41c25401b3098971a6c750889d0b21b5895bcbb74c52f82342f6969f03350073d045721753d3f040f4b04a73e8a16562a8db0044f3486336b6354d659a6bdcf31925e12e2d1c3c01610c11a41c7cbf4ca153eb50de497fec4deb4d7c581fb7ca2367ed79ab1bf2b8a6b09cc758121a7267aff483eb6bb3f2a98670b98d585466967de1a699db56020cd7e693e118b6889a02631b5f6fc0a9926189176a11017154abd4fa6f442769680ad026e488d218ece50e015eb5a16a783cc36775cdb638b8135b194fa60282bf9c5a936416b9e82ce02029cec195aaebf967ac4cee09d41f10cca3344cf542dc057d9939d78347724ab437f5b1d9b3328cd80b7d1547b6e632b712217485638d324b31260760a3f9fa86432eca51890c77136b325660eae778cd47e4ba62e670d7634e6e287ca98875c86fa66009e636df04bcf142249a91498e3f2e5121d0c8655b1abb00ec0753c1e399801c375ff03f4e74cc82aa7280b35520d18aa2b4226d2c0d01565a3f16d765c781f3482031b6830160bbd73763bb91b27ef899846ee13e8f40cf3e4d1c23de8c43c7ad078f43f397364db8c0fbb46686268c6c0dc0c1846f3bfd0628b3b26aa45382b19d48c6b597e77854601b814fd51e8d4f152b318a3b7e5a083896f57d9d207a9b19050120e0e0cbaec18e85d131c5b23afefd82248292dd4084b384282f2c055c5be1b0fe557dd209d9a35c8be676db9b428d2ac9d1fab51206f79c95b711de4c8d0c5bde06373b43d1537b80a29178559df4c79c98a69666b81b06a0a2cc7a196190260bb9c81c14e41eb665581e85fa7fff749a5449b953d55c50e110d211bc69efa43a08f9305d98cbdb609c159779a12a63d627d0427bb493dede741366ae411a3c1b502c81d6f085525f958ef2835ef26511b2c2237079bf848fd002b94a24f890538b11ac3775701c73a51397c4f56ea3b3e5a7c54b71d8734f8619ce709008ced47e2112195dbb90758fbd7341f7ed4d71665564ff5b4cfa9780ab2da340be803c1945e9c321145661137f14980b261e415ed418308ad76f9c268cec61132ced09e8640acac29397fbe5dfe95fb98f8a878afa835308fb66c4d126ae075e00f027da9e7ead9d98249d514b852f4974a382e82d1f1534d4446a6b25a3f3b145774e089cbf19c805dbf7ee66a2ab43d1270a732abdea9b66e51a96c685a93ca28b170837c9128f42dca863cd6e74745c8ecf561d78a3133edb4c4cc0ca7cc68823e90cdbab636b7b8c80369380f881979db35b5822e4f50c404bb40219cbbb04778d88d2570ae023bfc647a5cba86db5746eeda98fe8a2ce28ad8935a7241f9ce05a577f2168d76da9c1937f3d540a1e0d182de40550c4749d4076917f2a3706b52e251ec9dcf6a6a311049bdcbd22f7a96c4ccba510ef3dc78c56399858f0bd6450dea515a4bfa6e21dd492aab6554ec2e40aea2a3381cfa799ef08f175d75289ac6170fd7d26d4aedefbbc0458efed590c0d13d96031d05eb87a67e99d16e64e7a68ad65732aa62c5a582ff9aec8b0bff965946d85784f2030eb2db858ed8b59d7d41cb8709b5e7288fb1ecaaa4315b8c1d05499ae6b6b4161bbe90a6fc36ef3feb1408cf8ecd8ed0c35c8e83518147aa7c3de09328f4f0a4df6a07cadf3ac35fff6874a0ed4a389d0254cb63721a194ec2956c5dfa42dc051f4debbe70372f0fa3910463da35f38e506aac8013aa349db71368c8fb0375076b23f5640390bb6fc869d1ca292dd5bc954f9fbb1c3c1d79c3406a87935fb09bc842eab9bc6e09b23c207e51ec15a418afb3e779c6c2d31a8db137a93579d315e7d61afae5841f7498832205b450664e63abb34240f227064a3dc902db280610f692ddd44ef9bb3b4c85748fb736c646c2b8621794ec95998df4f2f495f3e3d8db1f2c2f13a99e0171eefd8b8af38a33274631874fac24d5f3c62827a7ce78f9925b37d850ca146097fef33a94f366899729fc2e362516e809c40ab4ce43279ce7ae891cb1771a563e3f45817302c595554bb8b614277f5212ac81783d1fac7a93600825656618fd9a88c6e5073c794f55f91f3fa1be24dbe753f83d837b96d2cd3c07fcc7fadbb538f0c1ddee7b71e2a3dd91b221cee6709343a36b3e25a0891607df40d390a14b36932de04ecbe102e7e71fe2f1bd1a45aa27a16a4e304b5d991df0194ee51167032fd48125798089dc10ade81e1c1de1061ce2b9656b6ce73ce00a09b702423565ec0c03ef7f96ee06fc3ba54c242afbecd73e263df497c22ee1b0268d80a4d046d214727d11d94e34c125e758a647776208915a9e0ca29125b4414b046983863021d9520dd1b6a713127c3aa92c9607d31125948f60d6f1686aa8669d7c85bba8f530382508d2826e96dc5de43e997956d175ee52471cbcb33476b17e8f56e2f25207e5c4831505f0cd6e5edeab5a547d95af92f4d829f45720598d1a77fd754cc05c226bc784f28805166add562479a1ca56270b200645f1e4bda3b8d3e960bf8a85232270b4c9869b2b5c19530b00e14290b316a902ae80f2b22cbbb5420ae4f3ebceb503ac06adec7a3e2a73577cc8ce7c9e254abcd1abcdf8f1f1bb113316a7ebf651ac06c9ff21ad77cc353fbfa1c2c14ce3549ec562af5e3fffab56e73a8dafaf150469275e9d6abeb8ad06f0f3b2bec6935115e49c577e005422009ed03f47d02c6c89ae16fe88080fdae121dc88304014017a96e2ccdb8bd396bb544115a54a451176a7a597af826791aa94339f1946f5a6f80605e15835c70ebf3a78df2307cbf2a086ddc5202b03f82299eadae60e741428a576eea6b17d9201af0ef62c42fc21ccb89a1846ca6624dd275cc76600276e15c0be28710fc4986ab8b8bdcebde1de8e8bee7c86843b64dee718896c3d300f4b773d7645f09aa8e2e841baef3822c752ad4ecccee8e8f8fd6f88bbb6e1c1bef6f2bd94fdab1feba105d7be7b1ccfe1124aa4ccedab17f6c23500fee0cbcbd510a5e2d18c384d850bcae28e6a9cfb43c4271abda68283f1a2b4b605197cf06ad8404cf1f3229cb50165e7ca6a7db6fcd031c0a5fc312175a55e974b9f7905a709e57e78b96bc11e8246b6b9a70f5befc45492ddd2780fcb8a4c0255e7782235e72e29cf941be1f65a5a2c8f9a846c3a18e41f06d01819a89358ef68bcc801443d764e0d8c4e846d5bb898a00a4450a12f3468ff660a50f2998a463c8858326b79ed554506d748ca0d627072f2464e92ed1e8b5c7b4f41e27fe499a8c60d2f1543ac4c2e2118ea2da45d896ea746e5c28c24665a28602276660147fff1ce4b33031f2df7213dc49d6cce221a9feb63cdbd8015c7522275f667b500e48231399aedb69e5c1a7308470ce98a94c96aac059cf9bfff14b9e0ea8e2646256a573a9172db0eacd28aa88d912625a3c8de69adcf058442dc79632a9f6809598f6ad8ee97f3fa25bc0f1191bdeb3ca7a4043f2228de5547e27bba57ca14aede788d2b0d7ee5b2afe3948e99083609ce3c1063835850c1d3131b74be3d8cf46f262c9727332928a4ef5021bef079b62828062c87b54a8917ec1b2ad9464f4b7a670abef85d9507903bdf30310023c48e0cfc181903a50598b984188ab2593057ce177033302d248dc69b75107b0c13bc8eea356b53f451d91c020b83d8a323205a073195f0d38daf0d0def1f5e0f4e6c41e35c908304888fd2a84a4e3612828692a5cde7dcefa01cc6f76fe562160701721b72584549f642d9082e63bcca902bdb6bcb86f8a48d4702f6a153366f61948077a3c7d310c73cb190947b41efb7d98c6a0025d34ab7072089df75001cacfd586ad6451b0bc25c3e901ed8bfb97d175204167424145a208c721dedb57872fb0497c76a29595ae849a5278c0a13d8b8cc814f16fac4aae40669c08a4271c02779c794d86a44a26bc232179466d8549210870b4f05d71497876b8ffbe0261f8c9cb4d85f71701f684dd1195e8593b64b3da5ac55521cd41990ad174edf62ce113a443583431f138ee0654866807af8e1db3aaf6ce65c6b0a09a03816e26709967ef71385409280a904a7c4acea95af78685e053ea2383ee3230e4a6e8fc4fe07f803a56e12d676671461a81b794561394a22b198bea625963ba81a9799ead8cd0888feb65821662faa446d7c6fc3011f4e89fb4f30028a3aae8a5a552a5c5249ecae88a09faa5587848a7c5a5062c7534da8c213479811061702e80b24606e3bb920ada8174a5611f3174009401f75a130a7f42b867e95cd1223a39b00b21335accc1d56ac5db1af400e2270757d0dec4f999168e5ee1a9c1576b1aaf1bc726f2eecc2932cd07fdccb3dfef78df23586a52e2eb41017f03d9149da950172d101091899840ec95e2e6ccb37d55c933b90f2ebe7ba3fd21049b9e98a7d26c413b9192ad4edf40d1aedabcb896fbecafdf9e56ebbfa030cae8e3fdf2c96a2887a377649b173cae3bc4737f46e312b17f2fc5ac90ebb1410cf091a81f24f0eb33e27f921d453efb4a35a559eedecfbb499abf223cd2f126ffd4bb5fcc65266b1e41b2e70321447252be72ade795b4a1935fc263c880b36310d47f537bb72aa36cf0ee78f547a68505c9a8b3b0008310f041078b36bc6d684dbd59fa6b1a092cdf2a700118724503908b564f33da4ba94bbac2943a06b5489c309acd3d66c3c67506e643d9f3dce7271470513585e1d80ef6eb0830d6c8a58f8811f6c92aae2ee9eab4a506e266aa1f370341f57125e41091a2b041e9dd5c341bbd7d9cd952a0328e9e25a0fb280ebbbbc5bbbfd19b5a4ec50242635516e410dcebe7707f63dde80d15b16bf0778c64c16edb6fdbb1a5acb61f096fced497830f83d2f96882d1d6c806634399aeb8753e5b66d933e9c27bb7ed732e859c9c328511023442b651dbc91ed0a165c194bb312e78b1c25a32c8f1cd6f606927498ce65aaf35199eb9c335b46695c6dd868596f865428da2a018327d2609048e7ae78a45ea9941b07a0bb22b7f67f55ddc64b746ecf7add7a4ceab36b398a957d855024fb6fc339221adf2e6d8c465c22d8672584aae994b900a91b85be0c41a172860c428b91e9084b06c77cf2b74fee71c51b9e3be370dffce082573c74c6e1bef9c105af58d0fb96938c81946d6c0ecb41cb72c1d54d6cec2d52e54c7a3f85f5db65d7fabb68368f0712d28685ea4ef805b83d636dc1ff020e6131c0672054a931601c1412afb3e16761eb11cd5f65242b96c2789d83d95c7022fb96e00c6fa61e6c9256474f9fcad2133ee31c60e227643bac1bba275806290034ccc76fb8806e9e210fdd3260568cd91ebec672fb498f302ab94b6c4a00d1b57198b7f4d70574a97412acb9e34f6480079e6c106ccc1cfcf98ee8aaae5c886947e7d7aaf7f9f6ab0b5ba1a965face16cb68610c8426049030ef6cf13ef33a6b136d00d0d90c95b480041510e48d99db0b150bf2fd38977a5a7ba3da4a5a7ced344f18382a9df44eb91199dda3a28bd9008b612e2ee9982f9a36fb1ffe2ec5448b6a6b30f433ea0ea67beb45bd22888704a33da3989c6deed665b0a2b6ded7cb6ba95bfb519e3362682c60bf2584fb234309275a8b89be852c53f05ebca3685be7529924a0699529a2d2772d880f1249dcd61e9a785e14a1377704d97fb238bfb7d4613120c81a282d215ce3e6ab3c100e44ba1fa504826b651839c6eda181366d653e630e0c968bc897541daf4c1c520953bf0ea7b5aa86f6ca53fba0f592dad7919296be935e7dcc9c3a8ce3b917af48a0a142c3f0e7513a9fdd805a2808196d33c7765654b6b12b739d699755cca6935b7e81a88d9ad786c2b08eb442e00cd83f948763b05bad5e8f2d79e9126f7481077cac81fc5f92905208cf58ea4824118eb0e93723f05b7cf00431f59c2a3f22cc49b1468a336da9beaa8ba544336182292c510483e178d5f8f9437f7a01485f4941552fe360addde0447fb23008785679125c88826e898dc892a43cbcb8bf8b14879203e32e6854df586c45263d1fe800c25cbda04558100775276d6765e100f38b451be375b7bdc4247988ebc3b68251649ac90dc35105c83914d31d0e22b5484279c3df3f2e56f2431400074b633ea17e17a4ec5c84543271ed310ea0d518d355395b36f5015368a588ba2510939d02bbb15153370caec64ec32de9890db1d0644ec7a0eb8019df840958b1f8b95a6debf9c989ba1282d791a1c47cde6cd2e991ae77ebb6fb7c4bc3eda6dcc3b4753af854b2f30cdff1fe125184992e31e6a4715231220af6ec13568bc9dc6704fa84434141263018f889dc2a13d0d65b99419261f4585a800a47d81b4e92ad32d2fa0505f4ba9f76d21e31b4bd01515c24a593e7c50758c9f7b48db8d70724fb9f455f6864b2859322a54c68e158a1c6c79e047ff63a1c00a2a5ee346f4aecf13b50426c98e71d09831b146ff063dd6c89e3d5a88498ce1bab5b11b7065abc26d7362208a3a46f2accd46117c77e698b61a990bffa0c9820e14381955139c20174ac48f220d3f54dea53d3309c500eb126e9fd056d52a946415b96abab3d86e22a45e66cad1b9922687ced0a759f95e60fcc4fcdb03df6855cce9870bc0705531fe6fab1840b99ac7428261e00516f9ec3582dcc6c47a027b2c2f2a2d20c8800f75e3eaa02a45548188db969a100f8ff40d7af80972301efaac95425f18619334f6449a05ff3afaa2485e5f0eb059c62f9616d6ecc5803c2e487bbf892313d47fa1f325e926800d7cdba827d4c24ca87f258b348755957caea008099cbd5970ee32495b6cc25408eaab75a994093009e30ad6b2d5489ca445b9b2d2aadf46012569d42d10c43e961cf6a8525e164e40cbc5e7593eb5d3f85f63e7d5c9f119582f5c2379314166a6d6a98adbac1b648aec9000b0abb286cc2561dbdb4338c91c13d2d431ee614f58b78ab2382c8983eb6cb9a7dcc1a4f82624b23cab1f984beb65318854a9318093d4f087be47375d330ae38cf923fa6674db98e58de480f0904093c805d7e36720cb04dec4ce38fbe82ef69fe73556c61456b10d372120f4b90f567a9e1c8da2fb363c8a21fc0a8da1b49a87736e714d63e9c393eac7c4a1ee085baede061ba85cbf92224c304be24ee53becfdcde35117d316ec6b7138ba87351a1b7a043565e432809adf6f471a3182ade2fc0f8fc2a731f9d2dd0a269769da611089c21746a00760a176d2c9f0168b1611a817393dcaefdbba4f283b20c0d02d7aba9fa3952818600bb55cd11b3457f7cbed77eeea13de04137a0024da2adfb41b57a9ed40d05c90128a2d313b23e72212cad6fbfc1f62e591a0385981428dfe4e06b4c21181fd6c07ca44244357bfc104558629156bc8db522d373a18f84705306ab40aed49d649798de33391a01c9379e3b1e38913f510665f2a8d387858342d3890454b0b664c4b6e3406d4f021923c3fca9d9e96543c31322170a00d663b668dd33547c7b0811c0c87c7776f71e0abbc4820984618a7b4bfe30a4ac28112bb1dab4fd43fbc28f1fe779fc8006091765d43115d4db204bf90432d5963bfbf569260b9a02915ae022b8728b92132f01fc5ac80e338d36ad8c29e0409b9bbda781b7a86247630a1c7c35180e903d1a29a865864ec5373a8bda333abc70090feda7479a0d842e2d5d36411a200b5340b3ae3b46132cd3a0d7af1b2d07c31cd99855d186b0aa5e0315fa6504c2efeae0aaf81b5cfc0a25d20374d545cc947d55c4a702add29641bdc97a64acc8d441a00babffcd1f663ff6cb84451098987ee862fc9c7d746711d414e301ac4771e62d7b251c04f73d655248dab1c67b45ff2618b9f108ac4e8fb2f65807c9e7aadf3ec81c8214f24e44f5a531df963b2a9e0cc71c39f981f4d2a29497ca58b8172d1a4fa80b419f71a9e3588dd5bcc3a35643e3b221a2ae8b4f639235bea2863c98183ae73d1044d92e89e6dc60e4e008129ee0e91133a8c482861751d6efc18c39f41ae2b385a1e53dafc4f9e041826cdd89e36e3c0adcad0ef66bc6ebaed219c7eeb80b24213d4a6f6ab23073da0b81239489073251ede98a317738d48725ade6635758a7450579b4fdce6b8c7dac295b00dbc2a5a520cdffc3b5568e7d66112ecd18570e2904b285b339c6cbe2e685fca122158f90b0bb09b3c3c2c47e04f3119aa646ccf82619c6dbb162d0b2dfc89f35447dda833afded033df963856f82351fff19a8fcf138fd75a536a20cd5a08d4a3feff8e7109272d04ec9ca59a5028360c7a8dd32c50a3b5c04f9568176b29219bd1b8421f87f5151385368f5c43b4c8cdb4befb25eb1222b04d85c2046bd25e815df3ca89b88c8c6088287cf07112bff491103354bcca2a00c87154dd6bea9a5632e9307e26b804807454cbbb87eba173b185addf07c7038fcb884c175e42dfb527e12ac1991165e17876602622861a98275119bccb60a296dd9ed2758c924def83d68e2285f307cf4079cbb6e2c258492dce437a532838ffdd4824070f8fed8a14092bb70f1ada0ff5e4b8dc10c7e36a4c2b3e95c29b4178c7bbb065d3e6818acf4d85d6d29a80bfc28469052203d88a6ce7a39f99bc023620437a6aaf586635037220d7849370838386c8b2c69003dd05304a6199d3c1681ab4e73ccc5c5aef21d4d7da407357b6d24c0c6abf3193d115f65ca32e96708dcedda4aa3f924bf6b5b2f004af4b8d597ba3de3858053d394de9722a3bff2afa46f941868409ed08ee31d1cd58d8903f28307738824c84908a035f04b0136ff0fbd8aceef4ac645a665729802947bc87de6b6428a4ca98b0d370c6ec1a923c0da534d790e96a3f144cee13d9a598bc1cab7371102e663a1daf73d7904a787e8144d18e1f089758404e1fa956e2ad5e11c3f36ae7d22ebf6b43f3c824308494ebec9134feaea8d21408347c90fc7920150ffc34421ed926e309a6899071b2ab40d3d9ffacc4118c85829f86f27caee0cf430895f8fa3326b1ca572a612f74786b684e21adedd1c0110e30ed8d86dee260b09885177a304c7b663a1e920b480c7328d225e58d7d170ad551ac53ecdf9929562c4587c9a23ab068e69d2f5b738578a510d5d82452552953e59f44e73659f7feffcd91c7a9f670b06eae68950ff831e6fd197e27c241a2b029383143adbd76ab82524ecac10d107f6031a5727bea6c40be56b3959037ad25403f4cb1161133f3dc8514d3a1a46d08d3ec1eb0da79bfdfdef20152c8222f2e57488c937014fc15a1eac565709366026687aadbbd55958b9953265305de202dfc9483eb456e40551b1852045b70a3067ebdf5445a12dad6a63beeaeab4bc79dd1857263595ef440fafe8137175557824a95ba84ad71c285cf2c79d683fde26578f556b1e05b3034ce7d15e9bdaf4044724d2d986028ce52c9396d4e28ed0481bf86d47cb2fec0faa870d08f259dc23f5202048d6f1a700284447ae18404bb024678309de40bd202d26a7322eb9670770069db861bfe33b13955be23d53b81695a0327a1d79573d1b8764a9b0490a0c2787eea3037b29d6e2ac83477839f6b0a280225e8d341ccb708c18a161a22cd6cfa1db2814d0835df1d0545e9f92974fa564cf1f9662d7771cc592a24186b1198273fda943475a0da2439e2d20d256374fa7515c5328ff3614cea17f76cdf92d9c15f239698b87f7873ff1d29ab168a5e424d8b7f6471a99b3e5711db5bae93ed6afae6d231b18e089677164e1d9b1d2089c8ed0f4687f2bec0683481bb2b7c0540719983449a81b628a3fbdbc1152602299995388309ab4c1b97683f51910d1a0562aa5b204be0cfebbeacbdfefda7ac4673719bd2717f2768559d492e4e9983c5b9340a44a6bf5287420eae82502cd11a85b1590201f1264c82bbf9c1031342bb6190e2ec91429b704af37ff68f9eb647a4881833ba0fa1884be8633c8f8091d958ba0ffc2c65063d30175888e28c236e8a0a65a5d6f5e5adbec0271dbffdce13b3d56ad71000b5324cfc4db548c16ed469632b712e28002a9ce83e5921ecab02e4dee2f8734bf48947c7a0a3494e43a42681433d63df377811e87c5b81709b980c51a7811ce1523096a1f26d8be9b275bb4d9780390197751007b33cfaf194b36d4296bd8e50b08f7fe88b1ddeb7dd66c856024706d12bf2d707cd5390d163a7331ac1b5426c73e39d971ba998aa84e52fea08ea64b4401963d5506f8df04dd511747a454915a24b0dccec7d977304f28a5fc730876ce3b9489dc7829769fa129b100e89dfb01bd42ac1d2c798a3df5b936515e07b549a7ba8441b6f183d04af062da256db779fa49a8c04963a7a760887bca84bb9f1a961d334cfd11f3a012cdd2a0b4781f22778b7694f89ceef325eeb4226424da54cf993f7c05af4632afb459b3141c0a9a1e4cdc44a3b63a3cd7cc7a89e50838498c47b777cfc5e390d1600dafb08f853cd6969846a6fdd355ed82d982aa5450c81845ac1fc93a262e5f88880e8287f6f64783cbc440612c8522b54c062b01e44752a41d2a2c7ea301997d531bd4f40b9b31d7009ecd33778d62e9fe1ed2deeb4d9f8746c8384db26782087a20940d9299429d5efcfd07724bf9cd28eb6b4d2cdcec68424e24a54e7f86bb6c9efad5cea0421215300e7d0d536e9a5d068913800ea4060176bb5457c7df878e570a822a3e645f9c8b14d25476c6a930c12fdf6d2c0b814f90b6670de90f969a02f0ced8b22a59c515f9ddb22ec12b2b223c5c8151074411280ba800fea92b421230760e915d391034e583ac2a6e6654d989108116382fab60704e24aed63a47ad7f243442d2b71f6278850b0830ce777b0cb3ea91badd4c0c53a8d1ada3f6e44128e454443280676e4840d38f770d40b3a138840dc332c1412845ca12d9793105aa1247354d93a9b007b761df15f2dc65e74c0b32253a83d29f2e9888d3b45cc2521d1f84f681bd758ea6b38ad8e0e69fcd5fbaffc7deebf3257bb901b214d9c21397d0ab0c5566287d4f0452a59e5f98c263813aa8957faca0c2c0e1f619ec6b60dd3a8993d5db2b0803248b3f8fc02a7f5f7ccd55a17b8bd7a950a6369f59c9d5106d8b4c6c12eb71ab1ce58c1c8476c14ffb008f986dc44b54024526ac74d88325ccd78469159340bc7f77def16f1e78fc494e5162b7977ac7ec08d868bf6b1f1eee045801c5333bf714336daead98e76ff9a0e1c135c566dac2943afd26f4f1f93aa5715e4b1c234b31e85791fd008b99a0145d8ca267d5fb937df14fdf422b13f200aee39c14f5bc0d836c7acf6fa66e5547bd6152ac514629b75dafe107c73a260e2873870bb733bc5b57d829229a1107873b7b2cc2e02874fe374ef711c4296e0ad5c036b0f233da94392991cec6f397bf373ae0409d644f6319296b734f770251a58653fd7a30d62862e50cfb6a985f652cb0791b9b5327994f661206d11d3aa1e9f407a9fc928d2a527061dee4639c1bd5764843174ae440d50b43cebef441a5b9dd26a79ca171210ff28f2957d5c723073e73783413871b8328f71772608c82c49b6516ad8e38a1c986d6a554cdc501d72aa6249ad49f1a75fbed5ad259d7b5cf0553d4ae0f24f073d3f13249c5edfaaee6715149de0117732606e53be0e224269463e986e0f863169959bfe124b54824d870ca44434a67396c3c388186560df3a8af37da818afebc4012fdf6f36c5eb935c54b42de4c087800331cf20824649c5934cdc942133f624c104227870ebf3af5f6aacc1a02520e258b73212766adf33826f32cd6e654560ccd6ee7ca359c37606626e1f8ef818ff43f14f7ad6578e05cc167df9514530be33598c49d21b19e09bc952c4f650dbf7b2339f8040cab5cd5e2df925b94cea48b33e582495149d40ae030052b94522c6b0d96964f5952a2847d38701b3f63d1dade9e696043d6abd88a3650c634294b18102476ab5064937a8f136ba2c0b2366cd9b0904f3677b74209460ddc8fb8e611cd6e850c43702f3333642a443fc962d66a2ad51d36a19c0c9c2f14ebd33162fdd8c2a9a2fec932869e3840498ca4e80b9a1f9f05bd9359977bdbe27135c926e4db65f462395e090ab206506862c69a9919b1b6d13450e8ebba5aec76ea8187601064c27256ffc4782e17b06b482ff28c96e51affdbcc88bbbe0377856b20514f2a7b8e0292a1ab054c98b730fe9f820eb9bad61c4dad53b4a089e4bd62d70db9da1bcd0cbf6a067670dc3436596b1ff2014e98e1af13562d53a6f7620fc762f011602ffa7838873ff534c8c22ed68d920e68e11194136e97fe8670c1f2ae958b1cdca028e28a9e691bee291a12f82f992e8023aaa7a14d405a5e400ac71063441471c61c0157dea39fdcd51c73dc1ee67279aa902569b55329367d9bb6ae737aab4031693ae67831c83d62aeeb489c8fc0c4172b14a1038f79f56144ea8a47b97ed69f4127854e6cbcc8583700540913a402fc3881e78c7ef8dd73b09bf698ef1d9635022cf6a08c6d93b807b3f6eec9ad21225161c695ea7dfa5fee89d273ffffe5beff67f7634ab51c88c78c991ec866a68012b5be46027d1636ccd01dbb338e2c09d06bcfb6cf2dbff52e583bac1552c9a518cb2f059918c7515d1544584d338eeabd8965a7182ba559f99b0fc0a7ae7888ca09fe14b648956bcf1e75b903a2d0231b59c4d5f3383d0c2010a4f54ce53188a509a37877aa3a284c425336d0a6234bb6669d0b0ef3f681aa5dc33e8d6489d35f878e8a383a93e5e664a84d5f4af37aa27dd99f9c673590c038d625538114e74548020fa9521c95184b5dc9bbeafeff92c0636e927a5a361d3280b7ade82cd12add5a190c4f01ec627805e46423e03861f20bd6c92a9a481bd8a74a2a0987456118e2809ec8e02e9815dea57cb78198ba21b46d0e54f45fae73548c948d9f0f6b620c24b2e6cadd9f71dd6b9dcc7a281b791e9f7df97c44e73592a49ca049bd072a7df91987c77ba92b101d4aecc43641d29c55334b02ea864b926182981558ca4195e9221efdc15c0cea5222b37d38dde0b4e57008259896b636300f242e1886919d5e4ea1c96715d456362dfb522c6e6065ef9ea6d2a3b6ebc4e73d235f8c3721c174f2a5f37517611595b0a0817bd40b0af7371436a4f2d65437d1fdf421b5a50470b24af3b4e5243b7b305bf260badcfed6694e43180b1690f4deb230c6f6a6f0ccb6ec640f7eef46220661f1cedb66667cb794081c60edb4d873221c833b04e7c967cd7e417903c54a4138759a5e0b588e7baebe08b20f2c2408b932d12ef494803fe64bacab673664cfb998ec3f6a370478903e3d745a4e50194a80a7e39000a3cafc7d18c7aa1206e4b2824212fa569097b7104a409c076801ffb5c6b2b7963c3cddb7821c172698036887d99f4088419de8a547b8ed25dac2fa656a9397eb85e9af9352f358d69cb6546b9b79662812e3ac9845f561d8acdfba6e0786ddf6fdadd73d01cf16b806ebca182afbdc9230ebc9f47045149356b3ad97971c1f4dda0703523b2b59cab960b8db4b30299a796667a57d306749242d8231cdc2e9b743e9500648f3b2ae22d32888799775b38028526241c996999bc979a36844045b40ea4167a123df2c1eca9810092d449c24d91f935c570562018388e7a1af1fa75cc1f75accf39836b7a8e074369baacb4980eac022dcf3fec24bdd3cf666b73086827e6750b7daf69cd895b2aa95fe2c33b05e894aa01c06de1dce52bec46ffc814c5310c12f7e9c5b4d24080727940fae20b3496c3ea682af356f53c721a6f179d5cbdd7261d25eaaf4305e0c0f3eb18969617f95598ac8276a794baab7040ac8b2f9e74766cf0b6383a03f7a6fab12d5638de53ff03f57acd6037ed7f8b6609f65384a5fb21d178ec08871e37c68887f919b18de7842c5d604460fbf4a9c7a517fcaf075fa47a01f1f9d750105d448047a1d10da597a1a4f088f30497c97648b92c3016dae7e4941f9932b589de7b25637e460016815b14cb01bce4d7f10b1ca96b5a685e88bc21682bdd588104592a81a7260dab10117f562555713d6de414f18a72d3c4536044ac2081992b100c952df5d9102207a059a519024ae1558160ef7ea4036bc0dce31fff2cca804dd6857e52cf9a6cae10e110ce4ac317c0ef57f61707ba1ef4c0e9b078838784a27c3fbc7646329b6692bbd0bd8b76385cb9743ad54793856543f4ee4b9f77446cc6a186d021bdb434255cfea03c5520c1b74201d9fc30c524a5ec001b4b4a8827db3112fb8ff47e32c81ad15f55688219adc9c1bb333f8a99c60bf52ee08603c498b6c899736683993cbcbad890ba95c02c4942c721979195dacf9af81f6348d9bc814b71ec15ca05e734538b7839c449b88051a61170fcc1bf6a772089fc9131274cd6fb98c121b42fb3f767899829c1b5859896fdf764db7fd4308d6a7c7105c063eed4b72b1087c51cf05059667f07601d5720695c8f440d4b3cfaa5684995bfc6bf2d462a33604abf3a3031656745aee41f369d0aa63cb3ef6785e40e6aa4ea5a9bb915963a59eabf6715ffba6254282e1803d6827db8236ed262d8b2897ba944e381fbef0d1a9aa3e139a7d23ac24fe004911a00106e6277d441065043813d061ca72276434281caa9120d5c0ff34f253b778d92bb69d6b002f3d4bc5ba8863a3d1bf7b542249f9968b7508f11bb88b9f2d83f162d2e5122379a419c0e1920a11b260efc4d483acb1bdfba4453c455613c6a74213e14a27f041aaaff3da9d8215929f0496b7c19280664fedae217d1e8772d521a67cbeef0923f319416d6b2df43af0b8f264827fcc97c9aa350e592c409dc9a502b614633b1e77c5ccb84f0c01ee9680ca8ce9afe474215d9bfcd687a49f089454786faef617307e2e90f3872ebc4001482269b2ca5a00f2123a77cc85e050cafbb37579a61c776152897f641fc27901846fa464679ee75d9d8a61ac3748c07e126d81ad83adb8410dbd611d06c4cd45d4a5396e6a3e3dc1a42e814adae94aa531b39d83ee032ad6cfcb9256f4cd507f510209603c413685a28783d3624d9043825f7666731049a0ab2c74e1adfcb489aff305c55886d2f00c4dbd24e64b7cd0f134e0b10148bedfa1e4c5326e1178c5165ad8e79e2a0fb8394b4a4b43cc5b02e8d3f80777972b3f2b74abd8c74c1af8e41141ed2a0161b5fdccf99196d7abd015f1ad4b6b0a619b64bbc93d8ed17270c5a1d8979e4cefe30157d3cd51270df1208a7bd9d9a06f9ab863433118787e44d1e98f901b986a4507fda2ae7487251c0134fedb8a9ae98add3e7739aba3b01cd106dcb03bc28cbaa2c87b4ebc65c903c53f247826de7e1e19fdeae0f22c276fe8421d00e37185d517756d056335b14f141e0494c14767ffbbfd331be1cda519cfe9136ea6047cec64caf3e75ca644f8801b535b0ef17b6c79bd81d618607357190ccaa1362e24ea442945449396503cd11738f01a58202a900fe39e85ef66eb50a2f38aedda8f12ee3de8d6e882da84417bd0fbb0353faa7db1e91808d288d1453c89c94fd935b351147dd884f0d36808dd0ac532949c03852c50b44071ac51ce0dac2c734a439fb06170a1d27b2c338532ba651104c6ef1be80219d152845b9b81a0ab63ea464f6b8b446f84d25ca88523828446271fb5d03ca43870baffc4a6fd8cd807b783c9b0a2811a39bf972d78e8bfdc622f6d06c65921d1acc664ae5610cb9c891a1136c1394d0c9fbe0e7bce064f1c1ba636a65a5f8069ce17fb915befc9208573df2c0f637979239e4306290248f7047a7d7c081bc2aed2f2d7a34b8d30e835b3bfbf3e35dbc00248af11a12f56d1a911d8d1239d4f27668f816a59c5d876fbf3fe3a84f80390c80867dd40efd397e2a588c013be990abe2a378a86e22c37f23bc1b000f039997c4a777e800deb2c2029ee66c7ef439467b2dbcb903e1475fcace99ff87243d55be078b5c5fdb9faf4065b8515084a6c8b6d70183519380a5b8b4529e8ec2905c5b1f38c68412aa7cb84efda5400226125a52e97ab5021f045db8ae9b0a5b7ea83e6b6e55cc5fce3083f9bb001cba8100f544e9c8c3182e0fd9dec1bdae7863e14ba9a4591bc0aa0ba6fc72d9453f6e3ea30b55917c310b7ab4a71f764ba1f472d5c34ea277d9babf991bab50749ca1a097c58cf4430610a4d689a04346432fe3e1c5fbd3fd8bb5826d2dcc020d2b04c0a89349ce1c83931399f7c1ac3d131ca91ac3398cb82854f0147ba34b112db2a1974aaa4273c6acd212d6db234433a75c60f8842d8a0b70758f9b14d2836a31162914d0adc466f3080b91a646c4045e4e55acbb180443e580eb4c3844bea261d55169806ef88f8e42710e04c5dd1fd2c1accd00ef1617b8e36588e221e75ddd13e7c4056425a3920d4cd21006ed386affc302163d14712950389cb7b10bf850be17b4edbc37e702a4e3e3297778de5967ec3f4a23c2c38cfde54206fc11c2fb9f5302acb0aa43f893e85329191304645a5c227cf79517e5f18c7413950ff33e3035b9d905267bf50ae08cd34d732455e6650a1f7dfadd7d4f58059d29250fa20f50a1412b306229974d4686e0173b53a3fbfddd8c887c00fd65004a88795280d0ae6c8331651c1b531b4c2f191f997a9b7ac8d33153d84addfbd2f89f88ea3ead4c00c3fe7e594d0a906e5ed651caf29dc71a2f76f60397d637ac630eeb789c3a5d6396a0505c40eedb8d55c6a7d78a3fd0d868a4445cc163b22370e14b4ab0e1303c58531ef69cf01882deca26b8762324a1d4310b993a6e5a094de9072df41c7fbcf7bda8f6d8a0a0269450ea933f5d5d935a4ae28c9eecded770a06223743271154435141c88077162a02370d78d92df8b1f8f03b06173a05be2f44dac0d42bd5cc7510430f04e518b0a6a4073e18220223d2178a1ee63ca20c70959bd319bc1fa84160121b7dc359216a28285f5967083f0a8426a6f251b617b790e1494a92cc7e306dfb68762127c56ad3e2bfd691038e0c0597b1226f778518a9e00715fa70e8ae620aac20df852dd15a9eb3ad68a3c9d30c810b13b58127cd942b1fc7ae24c4011e0b2b3dc3f8a72c97920ae3a991052afb8b41ad892d4e8b8b6fd13a20cf0007fd421d0faeea81768001a395e22ea2575491183f02464a18540a02392872370a67b1a4b6cd9d31e21400423d23288071bdf7504c1629b63c97e29b431caee722429d56ffc14db85ae0729045dfe8cb02a71774d2e98c5d90e3716e177168a3c0cfd014d30a601b95e0c5232e9e32866cd98012483fa4c1fab942e90362f1eb4aaabfa6eed09910e529d92b200d5397e72c8c6f89b594c8cb5ebf7e05646a425f4fb5949dc11fe8effd37b3e176e64338710ac576116fb2ebddd538d5c1b10e11dd128d258c2e49826e1d8d96619f011592b68c32b32bad30fadd5cba1149bbcdf4563e1c49fec0a7cf51f8a4f08208943a9b189c7947b5b37f032aabe50244b539d6a8909231734d38d3975392292ab00fb6560a59f3481250fcd98f56a71a06138df6520712633231a8239ec2403bc997aa86b82a59ba40db468e620077dd9f1ebb3f470104903391f34761b695807290fd2c9b941080910dcd51e9a9ce7e23aa2e9aa5c40fc8b64cc649bee895256f0ab5453b01e5b695d9c59ccbbc364499bd4973cab43d94313d3fb40753501d7456e74341c924e151f3cf97c19e22166ba8a33c4f7329c547c42fdad7c57eb529a686c95ad71946737dcb48139d3c382615a77248736e4dd864ed2bc7fe3ce273563b190e60df2f8bf8a26d3f60c1a1d7a291974822e6a33dd78e6f7014b52b42ddd809559c587e102f577203c311832fafcb15774e369cae5eb0af2a829aa259d518835ff3ec63ef04f5d81ca2a9a4ea9b679f2db08aef6d38b6c561b4b252f600ac631afd3c1833b99327ec3fc569bacebc94554952aec39e12c3c660684239867ea866778626abb2825cb34e4a49138516fccfffca7798111172d01a2ed8544ca36de2f17bfbc194db7b356b3d3e77d1e720a8e5355d9e87aa26543d72ba99992c3240eb2261055ab82e911d73df1f49e3f19ad71e61162c028cc2f5ac8df655612052d6381bdac1f90d3987ddf6a85af830551ad47729941b58b2b027a7442886132ae15229d31da01c6da4fc1a13988ba1317018b8ba64976b24985598c0151f6a19ef53b07e8fd11b3d5c86a7d7d2b57724c41f9ae5c1d5504f8344879b7dbd16c581b1c8975350e64689a8dbb99ac7ae88a6d9397a3666be1cefe5b6aaff98d6f20f13af9e1d01bebc3db832cc6cf86b20c6aec23c5bf23f175f0b9cf5a761d3e753cfb84f4ed248b22dadb35f17ca8ce7fbc8965e465632573b2573b59167a92fddad7b2a70e056e0b80cc8c19469c9c4437cc16550175493ac93708d42c19c21e8b70a4c60882acc9dd9c9448b7d5f6aa5c43c62035f758008d8623d2b3280ff20e7920b52d7cca9c121613dff9a1002a32fd00e27318c9d1876f828d9700f153483b8398b1c87b1072114b81b913641e3d061ae4551742596474ea41ea2b82274b46434594c340d4fbad4090ae9a8153a1469060ac2c076fadc76b72467aff2dea34996aa73a2de2c240c2cf8c54cc04a4d1aa9e451da3164155c044a9bcb931f885a57c97b239299810616e626d68ae1c57462cdc45e47619d7037650ca0fb951850df3209ae5f1362224f6426cec189e457e80c49ca5c4240490874c24027408484c01528b2a2154a5a7c23662ac4a3de2f258bb1bf63b75c344615adb35ce730de6879b6f435eb64b8ee779288deddf40e8f24419f6a924b370669fdbb8cb4a135051ff8fc7f7d93a99799f4dbe7bc39bc24cce0878da267c24ff6b41a84d89fdbcdfffcd813aaa9db6a17155083c004da0b76462462027a87309f12a151f87947ee4e8fee7591473760663044428686409a1adec542583479d7a19838e0d9234628abcb499a84f3100ba3038b1da33e55600f78c2f5e3e6449653d3148a0034b75e2ba344c6896c0f314f29d565f91786df562387dadb47cd81794c803992c7a521f28f32e0c2a907589e2ea6790707eef02b0c1225ebbc5399a4247db6e4131aae0a38f5d044a327c73f3f84d8a9204945b583166bcd8f8fede80c970a54129d814a5584903aa23e23ddf4f1c18d153b6880faf391dbe42fcf2461ac5f9958cf5b6534768fade0588564deea072ad6a0cb68ce19188918a43f1ca4b6ca6eb754f2e6a68e6e8279c4aa0cf5ff547b846f87e0189748263bef4ae09b8a7cf3d4851f81dbc32ab25b2c49c209036088a16fab6d79430d4569638e0008adf5f07b65d0a291dee5f36c0aefc85848597a236185e3b97bfbe19f6199b5366f236772bf684f63ef472557b22f86e661792333c6dddc5060c4f885ba39783b87360be2bfc0cdb508b011431d01ce0c2586381745f90a2ccdedf1267bea0785055a72e38247366e715e7fcd2c8c8b117a87e2c477fbd431eac43277e5f476c3bbc08aa5a1e29c1440ff358fc542903aec700ce3a0bde0e25e08d2c5f2fd0f8e4bc3ac953e100095ec03bb0444f6b094a55db893ab70c149bbf1bf76fc50e10a952a713c42baa347686ba41614e9d49fdfcf8ec4d5496833490de1aaca848fb3c7e91c98d39f3ab26b343336f70184be163500ead74221e8567b92c80066e301124b1b1abcbf3aada61659d8fe301a3604e7a7fc906399c77787e82a94570f2e6804bf4aef2fe8091143cb7bcae8d5ff7d83a6c6edd990be25fda39dea205ff49b19f37b4a77f0e5388159cbfcba7497db4f250b7fb38d7d78c8ec6d75d6359e4723d4f12516b58df037a6b3065ae05db1bc0ed7d4d850e3f616e091d15e04cfbbb8a73654cdfb86f95b1fb32d73fe38330e9a94807f25ba2d2bf0ac3c313702f2a838fee0865d82a1206cef1f7122ff5183d40cc86dcb4ad1f07d7bef5b4baa278c9a03cc600818b8a28e5417df4c49b03934ddb98a649a83fb390e045eed4826bb148a2086d6567cb975416a24b5986c0118eff4b9300664082e520dc09aea1ed2c0941e42324a64ce07130f91281a79ab7a5fe1a9970cf5f00549460d0cb6fe0bca520bc6ec4d5f8c267bab9c0f205c998203f856259d622113e36f5209917867227be7ece89805ec26ae4b82886d904fbd7ca74b8658131296acf85c83c8ca5ad1db8aaa4efb5375e74aaaf1986ebb8c3eb75d28202090c87f0e1950398f420899a0ff3a8d6218c0f55862bb27197db3c603bc48bfacdc20e704b3e3eb2e15dd25ba154b7e55b9876cf63a6fe621cb50e2e6b90d64ea261dd14f41c4a4d1d36a74dccc7e9c10aced4023880831822ec63c263560ecca8d92f2a62b50d8df5f9ce4ad8e73816aac2ebd079ce96b4e225979064ce0d102a4b7d2b2b74faf98a254ba712ab68c4f3c23140449a8c0a2af8b8199cdcc704e3f03e6aad5237e6f5f66c44881c4f2ef92030e47b3d29ca21f65be8ab02b2ca7c98b09e47306bfb43b4cd04aea68524f3c67580a9d5442caeefb2aa4571abd2c95b4f6aae14ef5dc9e17a9f6984ed286ffdc40d27f28c68ce9205f84cb30ffc92761e2d42961eb3bd94859e3ba7c0a712924fc2b4c65527b84e6f2ab15799f946ec4ee37b558890c0cc1c448e7a91be3ac773c58551c5dc06c91248ef3fb84c55e2d741ff089d8c2a3287572f500961371427c6445f1a9b710c1e2135e64716fbde2967b5fe8d132d33cf868340a50276ab6445461fd230decdb3ab0362452c2dbff97b17267aaa2c7f8fb0a6c3fdd8a824d860a850171c8c98d5fee81119098f76a2fe2582e078d8dbcc55a8a7705294aa9a5bdc2ad180de97891620e62e2e14de8d59c6152631cd095398d69117060facd8c614d73d926480dd53f90cc9824274c2885a31d096f7c37b5da63b3611cd3b817608260115a579f94bdf8f49e47fb4ecd1021d8a62bb9ee7bff21b09d417e575e62c5b256678598734c7e1a560cf67277d91e3e2f14b34b68aa0d3e1d29efd9a7ec583878ccfb21cc1b60b3a1a71a765404e4501bf335980939fe550319be5d4d71391cdd904b3d7604cf841db469f47b8fa02d89ef4bcd3f1125b33326127abfcd05077505c7678bf77438879631b9503b2ae1456c1a27aa16feaed7fba66470547a039cc6d7321b0e103918e39f8c396cba094104dc2abf8893951427465c8ae6967e8acfbca762142e8755534bc15ce537cac80fb7167854f072fb3c93517d8e59a47ad19fb0f36b2e4b66c86937cd334d8c803f2cbd6e6fd195312139c02646036ea685dbe1a00c9b01eac20edceab2d1be2fb862a4ed033ebb44ae858185ca028600f5913dd005494da36f88b5d0a7c79ab33e05859b102f8f2fab4bee2e2b0453c1bd027c0a044a89ebb25720d23a4a07468e7f1fdedfdf73aba3d7f3580c2dfd1d383dcc3141b3504ebb092bdaa1a7ad7debe75d9107bd634d4e8418dd5961eae779d69a7b68e16a284616245d8b36276f3598b0686c966c42f6de64cb2da59449a68d0a1b0b590b1fe633689a06873ad730cc34bd9ce498a3d309769d5d02e110f519302ba5cc47af71ad566dabf16097fea8eb44173c8a39d4a198a7141a51a31dbaced96b7cbabcadd465ba059d7ae7355227b5986ed538f53eea56e7d4db4bb7524ebd95a20bc70d1e3e5a8ca7de649e46978ddfaac2e1198939d46b3cccc63025ba6ed89bd54a656dec956a3ce653381780bef39437e5b16e4a790cf53709ceb37ce0a7dc07f5b1c665a8b14be0a7ec85423a5be631ef817686fea86ba03fcaa1604477e5d1637e5949e3a843a79d36599d36d1d393363946ccbb4a57692bddc29c3af3adcc9c47cfc680f22e87795b4d9d9c5f52d0fc85464ffd76152331ba2c1d75eb7292972188d4ad0ba57495e8a221e6504ef3dba19cbaaaab3c753844437431477bbb485d865b3f86cc939e91d813886b56161adc569d79906ef56bce42b75c8841734dd3344d839ad72a25e8b9ccf3eb04525e831cb45af5ae1cf3d0fbf387f2a8bf3ba5f054bf8c491831c668adb2a654af1304fdd579cd2f1f69aed3ad5b7d74eb763fbad5ae7910e723bfabefa06ff6d68ad9db755ced5d69cd47af7907e94f83ab87b562ced659e816b5b0555d73cc35dfba556110c5228820c03c97f132c1d0f38673e3d2f7120d943ef3028ca0cf9c7360d90fe19fce97078e8e87656e1c238e611495d80595c8547b3bcaf160de5774502962e9afbd764ad37b8541a568951e2a916120d0072ec11efc0b9947cc9b0c03e12299c72c7a4bb8bf0db35547f6e7e3635cc2428b4eba6c95e3213344c105ddca9983d1a7bbad2ead4e0f4cb0f4267793a6b9099a20520e3efacc6fcaa5137332e85427724e3793df9549e34e6dfd55c73e731f2ab89079966559a6f9edb2f6bcbfcca1f7f597394e7f990769e8994fd769cf32aa79a66df6769f696eb27735c4df6472cd21347984cc495e42d2b8e5988931f788544e29e594610cf5dc62eca6981725a5534e294b10c1f0e42823476666e6c892291674ce49e99c53ca3921857042090642082184104208218410420895e4849027ced05f56a5b74b8bd14919cb61484e9363be654e25a50de39c3a62a4a458ad55f62755a82f98579f93d24923532c71464a79a6daa59473ca29a58c33ca18a38c3068d9734eed28c628659431c6ee1865c7eee6c8bd0dd5eab56b11f394c0a7948f7850e3631ff9e0e459626d7963999a5382a9b44eec48caa654469f53d249e99c52ce296593c77cd2fa43e59452c2096598325262b18403ccc6f589a1896118a65aa5be4aaeddd22237ebea231838e1aa899842a269dbb66d1ed42066656852ef28652a86feaae69857e9edd3e4db363d73ce39e7e1e6e44c5e8c2e63b7228d118c339c53a34051c68e5816888a0ea5f4e881007e046a986a18e34ff7d7da0c613764c8ccfcc391614386cccccccd700baedf954f069026877a039293fb03021a92c2909b4dfe76e51eede5f935a4e5e39095070339066c4012933f25d0f2fc0202cb475f3dbf8090f25a94fd0131730abeec4f5a9e6068d2a527e625f78811c6a0e3d70e48df9181301030a83cdb2dfc832215645145e889228ad0416bdc68a4c4e934dd45bdf4ebbf8a31461d8c629807698a751addf5c7b08e87e374aa159d54878d35bef454f5375d0a1942b792524a380694928dee762f57dd922ebd53c92ae79c6e05a45b7fd4993a1add5d3dfdba75eb4a87744abd3aa5ce9e0f3398bb00f450a762ccf104690cb33d9865f9acb5d65aabd75a6bad5996d596a2b5a9ea9cc9d3fda0bbfe13a75bd2a1e7c34cd714521d1d00e1e40920e68c1f909ec7f3eb07221e329be95ad0d4e84ad036c941808108ddad1fbb950534c691b2bf2981d0ddee67a79a38dda9ca7417f5dd3d6c0300da107e08fb4c079d51506d9d139da3106e950073bdd4c920cddd62972e5f8822c6e3bc86a223f830d3b188c869efc428ed227460a030a24da42a125fbc24d1f1cb07f6e88350b4d2f1eb8728cf3f3879e9aa95d14b1e76e8b3dbbb696dec0468af745aa5538fdc0b4de42452c3f468affce8851656223704a815235631d72a56abf589d432330083b1078da14f4c83e63b08728f86fc4d127c63de9226229550e61e904f98f336f98e1fa34bb9a43f724462c378c21eb33ef1e994dd8a96c2b642eb7835fd7e3a9ef9edae97735150dd1cd339dbe3a3f9adbe24fe15e9fca61ca23701da892ebd1144bf9b578f1c8f111de00ee658f41c9ad890c60a32d05883084548b313fd09d04eb44fa863d666db35f299bd329d7a36c09d1c38cd8e508e9d69a1449731baec38a7fd49b44d4c2b30028a215060850caaec487f12fd897cc29c635731073a10b561f64ca2cad7501c5d50a43f99727acca95fa7f370155d50307f42313a394a0b65b5f344fab450eace131b580c51119e801913a68d2c3b4fb2b44beb5cf497c7c354d75f7498dafa8b56cafe3ac5478c90bac582dece2f89f8f6fa370cf11c3142ea8f5f02c59663b0e8f4c8401a8329406d8b5b7f2d830e7af55b99814853a34a1852c257eb53c2571fb8f4d5072a7ddd6273749ea69b3c66a6159eb922d36f7f1cb2c257ebb3c2c60ca4db9881cc9f51ca1308fa97f0546c4e4b6fa08596a7ce2a427d3a7dc9001a2f4c83bd401d4eaf1eeaa995f0dbe310d44b0c422e1a3799170f679e5f3c047df7fc82b2f430486b451ebacb4b9ffda0a15bd133ffe996f4cc99fc68cf1c49b738cf5c866e4dcf7cb5ea3afef159c66332d7a6b779b23fce6472a6dee698378771ae71b780bacf34678ec7c4c2bc764bf3ccb7cc335f75cbe49967aed3adf6cc5e1d9f79b7a66ff6fa67389ff520f497c767fc30483ea1716b3d576cd0d9c9721df372c0bcbabd34f42c897f650498f3cf26403b98b711cc4a7b2bc71c1af4d38344fa00dec13c3ae6e5001474546548c8090e3704ed3c01da899e65da9d440dfeae1ef677912b47dc4007658c204ac10e0ca301521c71c5111a5ab680b203dd9f39ff2334c6b97269e3b293b3c32fddc29eb50d052730253ff5a5fbc0341f0f1da8d42d93478763ba45a65b9a478765bad57974b8145d351eaf0a298ffea35b28cc93af49ae887494bdfe9ca7e06f5347ca4622fec9ad56317ae48a48ae1e7d7b9db5524e08f6986f5e75e92da95fadfc8d5fd0442d5e2cf2a251172f1ef5173de5452ffd45477911a9bfd83d67a577b797d910ff93cb97942b12addc93659dbcdacd371b894eb62ef1413de7288f455d90c0289589679c0c1141a4e5e8cb986ef547d76a3c27594e527ae45cda6b9f7ae7c1f6d30b8ad29fdcc4158956fa3bd9cee337715d527344940ce1cfbcf3fa4d1c0fca329f70e9c9992b520351b74e1efd646f3ff5c915e16cb47297c0374149fa66eeb179ed261e2d1279d105dc2366d6074622c990a19c27291df3e2d31a84cc973616718ff8a5bbb14ad1478f55a2956e11758be55256a469577bb6520a113281aefb4bc0e985a22b3b1373da9d6855a59bf243b5769d47ed47b7a6b70a3f023667dce4d1fce8d24bd217316094a4181fc98cf9688f32a4e85ac59c5e654819525c6548da8f55ad314ef767e8838e5f425f3ebaff911329cf47399039caa1cb7b01468c978eb9966d623822393f83fa8b5e86c6949dc1389ef611ba4b7757ab69b333441c99c6086149d2d498d265537242f4855b3a496120bdf5517757dfdbe6c34c27979e4f4631a7bd4a349d936c4abb32a498d35eab7444aba2ef0cc9e83b5b3afa3e19c55abbee64744f46dfae4931f28c1423c598ce4457f5768ea9235144e9cfeb624e3b0a5aba38adecf084e8170a411ee009d0a7f7af153879139e5f54cebcd64c88601f5d8be283c5fe1c7a7f6c020d87ba3685d45a6bad59e63ea625391ee5577a96993289927ec39fe0f0272a88c34d0e39ef1a798d6e9da3bc6be4512ebd4ea66abcd6ae5badfc6bbeafe1ba349f724d7e56dde4c5affd955a530f93adce0c44bac9bb99a39218c93c23693ee3a4330331d92539324f12a9ef331d043d9977de93494e561be76c6a75cea5f49ab9b4c99ccbac8fc95e8192c39fd8d8e4c8e13636396edc66b5f98dcd716caef227b3c6bb49741e0440df39e79bd739ffc9b9b3d2397e94c7f9cac3e1ddf09edca84ede35f21c754e831be7973ae7d435de5c4d9c701b69766c3c07216ce709d08e8de598539dcba413a12e6f7d813a56b76e795b7f9bb34f8efae6f5446711a96ff224d89b32cfc7199dc9619abf996b0e391e8d48e6d55e93cdec151b8fdc159d9d2cd235595f3e0cd020a181c194251cb840ef01ecab037d73444e5fed4ffcea53bf4aaff6c2c718c022527f7a12ec97c4a7d2060c28d5352fad3a77add56b9598bbcff4cbe4ab5d92e3a9df18638cd1fad011e20a3a1ff858b5f256eb33fd6223804666f56efd5abdfa9039e762d687da2bd0659046bd1ca863cc14a3545aca4ce95667432168ecde02f8d92f84411a5a1f76c81e3333841032744984a36f8f4a743016d145bf70080ec5222dddaade5dba055fcb3c7a30a8bff6cd6312739a7978c3f968af913769f1878786373520b3b7c99bac0cfd358f11235c74170ec1a1e8d28939edd5646f97c1a16f1ea6dc1093b347be3acfa3b9e6f91042c79eb9893d1f662ab78d55ba5babfd81cfc3deecfc355ab6120c47e1570ac63c7b6f118f9e0140d7e03845015a9ca2691d3509bf3d08a7c0a70fb776ed932dc0b7d7c83bedf1817e3b737e9943f526403bd5336f04f562b63dd36aadfe0468a7da2726d72c73ce6deb03bd9d0619796abd2bd54dd542a99ed5ea19998f39d19f54a5879ef993ea4fb2a587fec49e118a31fa6c9a4614e2cb9796b49ab65357a3daa0076ff09ae2e49f5f42acf110420861c71b56a49493e2e082611856b31b5e689bc96432699b8907a78de338ee84a240486d5dd775a99a9ae7d79435549b8d8d8d8deae60405c776e3c68d1b3856528811b61c3972e418819552c18e4d07c7a343870e6e085471246cd16367ee3545ca6b082c256cf035041459dd4e6dc303bf5200e635441933a82881b23a5ab8084360bc40082c4230d07206116a507162b57091c5156bcce00447547ee0450e334c5a80b2544181182b7030030538942142cb46c4514a8b019e5f425cf1379e5f4260a142cc38849fb9c7ed2084104268821344045981e2076ba4c16698f1a28284105848b1414cc340648cbe719325a4e14f4f487f74cd8725044eef87fdd453f5d743bc7ec84d1b859873ce39adea88856e5d1f467e04f1d1c2d539d22d1f3e3a76c53032006306415974b0c54efb8a5d4052926e8064822d4f70f962a7ddd905a4822286d006181d706041113bed1fbb20c0431758a2dcc0065cd88055584972e9c89c535feec41622ca4387f3d965cba86a57a7a35d2bce89ce5f4c61497de8ab877eb167e78dbf5899993f085fe8fe26e9b687d6a709b431c618a53fbf828854147ddff8d65b733c4aa4a973761e5f7fad011952383678d2e426391e253c3e201acad91ba670d2e6e6a35d32c49c7615ae0e176f9edbaf0ed717a537ce64574d39733c38ae9c61860d2a9d3430830c336c50092ac5266a2cdd3a594f712befa7bfc699e3a9f1208dc373259dffd457b90cd1c53374cb051d7f8d37b7d92b7f731caea3d39f0ebf3f587e5118c16fcec71c7ebd12fcce935f4a426b87dfcde59e835d3f620e4b0682f2e82b0672e3d15e1efde93cd145bf1f37e4d3893e4ae009ed24783ffdb5eff098f4d7ee5e93ce69e93aec2d8165ef6a841f396cdf58e8847bb4a33c1a7c40c12e28af87fa8bd282ee42a215a23822a9056cb8e0b5050141666680254b0a8cc0d8699741032be73c48dc9e7c3b0eefa76f784a7eba3fb912947f3cb61b676ef39be86db7f9ec90470bd5a8b2a9f1664875db26b150234a1842f04112184070a51e7e88f2c5071ea0d869971203dca3fde465009ed01e3d0dc8c03ddae30fdd854452a95b3dd451faa341a70b9432b484d941500acc60342ee8f24510133f2419311082c0b20508a231675061242c5e04a161a28ba0279b6b264ff3246f568910f93e4d7e7322f2373b3d8885076281443dd45120d14fb7ee0783a050b72e2bd1f03169029d744be9fb7448638d21965480e61504a01398400637f02028a94ad111208250a68823c840828d0400018619a2216c60e6a531248a39edd22f8edbd589e32f24c2d19428ba78071ab5f82fb4e2db6b563dcc8b927b488744d1355b8715372f9b7b29437fd8b3f390f3057f29513a4a7fed42ed3c437fedd813dd6da16fef28dd9ad2ad0b89aa74eb7e42ddba43dd82ddba10a989e8d6ad56fe36d1b73751b788d805347406104db0c0cb1367e860a71d16b16bfbc1146ba031c410ac98d9698746ec02a29203335e51ae50d1c6163bedf0885d1b280289305a0c31e185153bedb1ca7711094d8210a0632c4abd7a90e3914aaa63ae514f55bd8e7b4cc73c1bee313dc5274cef283ad9dfec2eba5bb7950a674a281ae35c56f958f5b801d8b1a4a3eeec37136b712b334529a54b8f1ee413a4e41ce93e4ae8b10ac04bbfdd0e16a2bb52c540fa2547d757c22de167f49654239cfdbe9f9c74f3d09388f8df9e8f6d55dc037ac71b5796dc6ddff2dbb29d47fa9b10428ed01ee96f1ae90f06ddf48f104b81c6ad8179f6d7104acff6a7bf00a8e91a733c3a5c93540af1530a71ce74288b640fec62728297958e20b7f6d249cba4bf291d3a730d4d9bd436b8492b559c090369283d485759248b20b442a057d75c33b9ba09adc9eba24b1ac9a4d54a16c9a21dba1cd1c94f8f41fd4ddf9cba9822ced9c1d9efd8b1c327136c63d22dcde4d0733894a2f2d9aacb43056f27e5a01c8f4f117f93dde198b3e669cc991c7ad24dde10e8d26d75aaa9bcbbfd8e1d1ea4b58c61164fcc3d767810932650bacda9df4eaa72f9cc0e4b67aabb8de6670ecf61ab58119a9d546bd7755227d54ed2494209f539d43687be6990e590e3d1e148600ad4a74f8081489f4e016e693e49f05b5d25f8ed80d83843b771cd8bfc9d00bd7ad04bf0a093e0c1cdf361a6abdf74cdab4e74fcd54d74487578b322935191960ba7fc74264d56199cd22dc967e0d084b08a11235a0ff7b7434b912cb245dc098591809d1a56aff6a739852e9c84faeb3639f4530ac59ce9f186eec66f380e5f79f492d46ed4ad954f9747d28b4cc2e177fb2b8b7ea2fc4aa49ff28bab04bf12cc4f8aea64912cea96166914357b936c39fcf299ead22e810f6d8fb4d5bad0df0edb7345c87748f01ff85748706851a0d25d59e5a76f0e9d40a1284c76b8b4d22e127cbaac5282d7483167bac65147814a771949e87b8cf437fd07be0bfd4da7b64746118aae7826e64c29f4d3b5d3c6dd6a1225a54d82d75d3a6712fde6b793601428e4e44c74318939738766cf8c406d7bc9a283cef21bbfe14ca20bee4c8741d0498ddf9af2dbbd4ae5cc2a678ee7862cf2a4517f93724ca20b49cc995e590ccb4a2dfd4dc7e12bbfb2e80626dd3ac117500e3d724550cea45b279fde04063110ae08caa15c025fdacbb9c983c42b24f8cac3e14dbfe1717f99df78d355de8551cefc741b4f098664d6d85b4d9eb2b763ea9d989fde4274916027d24f479d36db69263a3db3f7fbdb557b75e4528e1e400d34c6b9bdf4d4654dd7df4c39e7d2bb0a03895043a1a8f320dd5968143dd939f4a0518c5648f4ce3b1b318fd0a85b1816ea2b2ddee5a997a7ce180631e73c4e75d1a51373a8d71a314ff6474374ddf8a6e5b8f120e74457a53eba8dc79c73336cf28d4dad36f6eaac3293e9880e0e45cba303104278bf8f5e129d20d121741578a2c3662f3ffaebe8ed0156fa9eb6b6856797de5da3839ee4c733b31531081df41887441edde5a5c7e851081df49884bdbc9431a68941a2941ebbaf4840e71a4a850fc24090700fe8dc55e17ce6fa24fcae5e71e86ef7d164b58afa78e8442d094e9111845922ccd027d171e1d4c51c68821e7cce33a188eefef8e8ea3c7a90cee3980337134797aa73babbdf47fecda3ebfc50218898e82c74ab7af416569cbd3a36cdc61f6ff2c815690203135829fa242ce64d12622a484ee29a4691c1a28ec75f9c6f21e64cd78f9833dd042a9dc49298404467c2105db7fd81cfd37588886e557b25fa7458a5d1549f2e437475acc44a56c98422bae92d206120d4a7cb1c32ac4217ec65303f31fb23bf75700fcf8422ba76f9a4bbac24f4d365d0404d42a348572148cd1c26612f454890c41764943e092b3de6c9a7d375f48c3e1d16512ee49d734e8e94995d87aa63cc6f8c3162acc303c7376d4e1957d007f7e0098420dc835dc72a069c3349707eaaa0eacee8a494778b68a05b0a84c8f676778b677885143af5a6b672128f870e75a4d1b1bdab879d998eeddd60778c52ce492956399ecbe4833aa845a7e5864dd5e49a6593e3f68357aa4b55fdf953cb3e2b745b4b09799e24f768ef5efdc8e0fc42f3d15fb3978fa1090d57c7b39704e7d9fbc7376b3f787c98ca67921facc3c3fbeb24ec45d6688221744ee6e9dc73a58e9594524e7b65884b1d74f9337f4ecbfd49e9b2674a26d2a2651f61fa65a72fb0f750343c7a908e6874d0af0ebe6774d0ef0d1d4e4332e4738c3136374bd12217518f923e7b68fcf837abf9ab7d8c4d047b6989c038973b833133c9d4263d3331900a04f36d53a974e8c0c179299d3d451d73ce07a6f181697c609abaa453a264497de84a7c7eea43ff593d9c36456d4c750db4435010f4092dc31bfe480d0da318e6177b56a974e8c0c1e1c1833a7cad37d92d1f98a637acd65a55aac66a90133441417f9b04e5f0cc11896d04fdb5210887a0f7010afa086f78a884b907e63f750837a4bfdaba79d3f38120000a7ab9550692013df9a0970bd8faeb2985c66da446991f2c4108bbb744d92244b71c6d21da4204e42d65b694f1cc263160642b5148f90b9fa1fc163459b784e9c445a348269349d3a200130517518c89e28c571442e88c868c7a30dd5065c362c3509c812208cc4690e7867bb4cafbf277fb06b3a5c808e9553746553cb78c6e6034c6c609ad95feca6f28c43c44eaf80505d2dffa82e2f582a24b8ff9e8b0a17f3c1c875fa0d598710a2cc0133a7ef4f859802930f7e820f084de313d7b94f4d959802948210c04da130f1e031840095fc2ad386e453d3bc62e14bb5ce81cd711a48a54912a920b9105988203f8847635be323e86c5689ec10bb287d608f768c18cae19084c217afb0e0391f6c43d600ad16ef0845a838eb9870bfdf547db427f0d860a515b8b956f57c11f9e0341389eb94707b19627d06850cbd03317417b70a3ba737ef44bfb0664d433d70290157085200f7dfca129b3a94975a813b76915a353c686cf319012543637353737a99b9b9b9b9b9beca6623737289c2ed0fbc034257ccf2034ca9c42fbcdb467679802f4bb3d9f4e2c040ef1816984f048d764928f984b6c495dd2bdb43e30cdc3341d45834e2098970bd05c0cfaf0cd2f17147d148c81c44c8ae4a84cd412149302cb8eea09061907c6cb89312f27cc6c3188ab399c7a409950d0c915a45248d4482d369b91cae4e5868be71baf27c2dcb851d9e0e8aa8416c5b0338e96ee329919ba9584866ef1eb8931f1fbf3a1020b2d44973398010c8081c81efeea50125dc992faec3efe4bb4e7a30fdaf123ac1ec76bd826bd1bafd54d7a37af659bf454af9936e9d9bca66dd2ab796ddba487c26bdc263d9dd74e9bf4b6d7509bf44caf759bf4b2d7529bf4e86b359bf4e66b369a6abbb9a1b2b9a94975a813b7ddc0614f60e7a3dfa40e5aa40103611691f648376fc8bd4044fab45dadcf2ac269d2f4d787dc0bb569a0499628c8406aac3cd1b2ecb807742e45418faaaf49af3210e83c243aad906380ec2f8b2e6dddb8077408466bc82b5ef1eaaa5c38cd40ea0e5641e823815510d2d9c1bc5d87c72964dfa7761c98c2f40640c7cd75a473da63e89c761e3c707074e8b8ab6deb7e7aac22b34736c700e9d36e1df75881e9e4d6a9da5752da3bb9176010fd298974509cf60ae6415a7afd921b923d668f1819d35d28f43d8581446997ec9cf696028439da2ab406dd14521ef21002ed26d9defb6bd7d15fdbc8457755f7fbfe62ed1cbe31b4948e44464f75c3192c5db290799de75796281e0b16265690f4dbdf38ed570ba6bc019e5f4dbc9a28faeff9d584d0bf9a387a0d8e790d559dbf6a1a657b577ea3bdf0c415313de5b8213534f2479710723cb03de678a8d5a857e39a46ed95d1bbdd4788a47f7af37a546ec353efc68f367ee543dfa26f3648a4b06d36486f7cb3b75fe537f6f67370ccc30e452d54514b3957f9dc2eb4577ef714036a62038db7867f7ae1d25fccbd9ecd6d78189fa348a85f48e6af4ffc0b83b4e45eb8fd9b0dd63ffd9c47b7178e79cc7ffa6174cea3c7c8d91e78598ccd9b3240059d73ea41cb5279a7a8437b895088a45b0d5380bef90f1c33e6db6bbc0bbfc651dc901b87b607fae6a71befc26ff779e31da4d16f348d9873dc10ea3ddc0bdb53d789376e2c85de1ac7a349798d9bf4551e3f07a143f99cf750c50da15e937294438fe10918e6369ee638bcd5d778351e92fe301c3e53f6d6c7613de64c3f75f6d64759d8d0333fc1b63d9953873980295c8845e731bf592cc9f69adf24db4df2135fb33dd4db370e6ece05e99f88b970383cfdc4cffc6459d537c7dca34bc7a9633e820f335d92eda1739bf561a6fb890ffdfec4f7d54f7c6e08f49eb69b4fee85ede1b41ce6b7427b37d71c09cc014c815f73c9f16863de34e957117cd6a74b82cfe66744b46f0b73004fc0707874d6f44d09b8442a504c40594108a1101d21899450110d61444268da5fd5cf2f4bb4f1ab25caf89ae7d71260a416655e4b04cdb9041637b8420736c217466362d00316a05185100bbcd880915301316972d8c14a12521401eec082292916b0804585075ab0450c2ca84883832ea00e4469b02087a7373b94524aa973f7c2d245176198e9811636e013a22f28221be0a02200cfaf258a58020a0a648cf1932e638fb4b53f2f5d94b2611990dead311a041350518510be2869814ebcd6178491459bb083125414017308e30056f4d08330c278628b2d1d10e2c116635471e58627200eaf2b452f25b48857601856896ed8887c881956064c8e1fa856b7fb8e2976643303a88339c4320c6239226a82888a1043ec60f086c7ae9cf1187665088fd51d0cc368a4822ad1a58c1253acac3c8ee797123e6025d0e46bccadf61b218cdf8aada43f36a720bb3bcd77bbf46efde9b88377d70a6175d95fad257c4fb517e7bbda2b5bd56f74d9d13bdada5fdf55b54a9d3f737de9a50a561a934451115bcc103981415398f9a20d3286e0c5132e69b840d2a50b1923d018dcd03056b47456bc502b5d0ce214bf58c12165e504118583fa441b674c41881dcf40454bd00e720026684c1b4898a12111843884126cb411c40faca8828a2284c1952179f8208b52115bb4bc784083c4170e092cb224647044d402b4841017a3233344455f8880041424862000a38104191faabcc00a218cc1c61167e61145d85a704499a7f4e911693ca594c616dc6c993b24c182d71162bc8c3013a148a24ab6539444111d8b9278f2dbf3cbca10a21843709dc1c3183ca8628330bc704174c28433889862881e7850c2ca199d269a884de8602f23885e558e221395478d58baeef965840ede881b4c23bc3c7d7e195145f40146a2305e4618d1c882b844bc8213afe040af1d724b59c5dba3de0ea33cecf2415a935008b2c7f26f128e87d220dd4d69d39eecf20241a7cc4c1dba0f842f271364c420215131bac28b956f6701b6fa9d701a44558a5620e535d495cf30462cc6786d7849afd63e2d07fef5275d7ed15d4e4272c36f9c7a97c9d32337a89bfcc64e266f72ea79d3a7c993ceba9c844dcb829e794c52f6d6cede8ab227695b6ef64ca53e9d078bda16be4f6a0ee098633279d4bbd3a1f74f64f22cef3219a2b9669d9abcfa1196c9d2d01fd6535eba91e8824b31477a9564742656754ae7527fb287bee8ea2992e865d14b87b28daebf24420797e41098a143f759e794a67ac3afdc38666f58b8b41497e292457a291d22c12f37ec480a97a606391ff8cc3da6b3661fa5fa46d094cff1fc02531486cc1767be7a18a2cfc218bd34b2d96e239b65b54bcb6a8f56c8e49b9c6874d3bb9283e65be69bc731273ee6f027b2aaf70468470793cde2b3c2b703ed3047847a6659b54e8f596dcf3c00f4daf47aae888063ce750fb4f3d153cb8a724c18a1330e21f4b85b96e5034dd1e4d92b29d6bc82726872cddb39e6547e81491252a577eb65f25890886147509ed21cf3219a631e312328cda75f96740889683e2d4bda93c934e7c9f3813f894cd79c359db39bd5d488d3a577eff46c5298b24dea9cd08499784c26320e2bd7b3b9d33c59969d96d53618f3af1a78f96803313e4214cae39823797a32ff89dc04509ef2f66e7d269fb2dcf3136f6604e51aca7393a76c13944b7bdb6a5bff74aef3765861a7db61851d546751b6862c476a3ce529effc646ba8b129db04d5692e3d934fae761eef4c8f1f55592c99793ff1ab651161496b59d36e4da390d1850c2230a43c009ecc4f37bdc088a9b5ebd8ef4f7cac4eaffd297513e827c733ed854358d3dbab65d6644d97df5edb5f36ecc1fc274e00e59c7374a1ecc56c11193922269fb68caec7e4d22f6bb28ac8c7dce4d2b226e7657900ef689ac9b4e300a01dae3269027738cb3d3ff14d2e35232714e7d98679c7794db21ce1bcf32c4750ce3541d926598ea0bcf326598e54dbd92628db847329bdcca7471de39df67c6cf81dcce3e78640c8a3b126ab3ae612b35e443ee344536739d09899dd640a528ff868c579ed5d26cde49bf374586187ebb657066e0621b8c3e9b0c2cec9357b69e05c08ee9cb420918b5ae63bd5f3899fd986928caefdf6653233981c660e9d86e84a02e175c811996ffaa1631575ac567c14a4856a7293b77311a73ff66aaf777475aa7eea6ab5b70689fd39948185fefab2fca5e696d56eb2574e96d5b688bdc2396659709dec0c3107fa6ad5759c657b7db503db9281435b6f3208236606565a0c96bf52cc1531559ec5246dd1ddeffd8bae08708e743274ee0f5ee3e599252f928cc8205f5e2049311e06c51ce9be060c0a8241324c1723248a44b123c0ae4a46c783151a2f2faf013feec111993f5d6ed13d3b748ae4d9cbf2811246c7ec15132bda22f24ae6d4422c9028ba4c76b5ea3a7f180483a24bee487f5611f9d3a77530bae8ff718f98a5f982cc43cf5c43d5da75ecf163205f7fd1a1f420cb07fe0398c9dfea3fd1649905ed08aceab7b2a46b12075d4fd69001d2b2a4539f9625ad65555bc4d6fe5c079dce91cb238b5544ba4c427a815cb65023890a2e92ba24119164826e523aa5e8828dbffe5374d186e4d265cfafa41b1ce74be2c18b0d280e303258176864d1c50b621530303dbfbaf0228b28279982d845955a451744b48a2e9aa85574b185061f1082073118c38a298490420a2e70e822872dc67c41e38507083f042d7126a98c2e465bb051a9d8f1fce2a20c2ec638b2a223125775c1e9a2d3a50b9c67b8a8820b325c84b112c51428a421841a446c2104251b6c7181e90a9e09184388881f68d08315404194d9d0835218389041121a222d25e85e3ee7a473ce39a7aca29bf30923ba3859c14490953476e0831d88009de082879f2f2ebccc29a9f0f2a5cc73984fa9fa637574565b21fd18adabe8ea3a877d3ebfc9ef8f9f9dd3de5ebd9587d198047df02c356ed861a158c623934674d2078e61483c788c716698327ee5381f7d7e2129fd759d08e60347689860638834889660962c8ec8b26022870e152ee306439851010e3818830ac492c596051036483188448058e69c589753ab56b3d38154f47307ca23bad3f30be988edf98514c4cfe71752096ed0ee1519ad0d59d8b3f42449c2de3634106c215aed5657e80c215043c805a6812f7c18e3873464600609c8c40a42c90456c1c894342c92281634801069e8575a0ce1bbe797163df86205b2caa494ce9c1f7210c40f9c74a1020c221a90e16d9195806261d2628c296aa54e64272a402e2802134510530061460dae781182f362e544258c971c4e5e4a40b9a053342daa60717445182604708426cb135dc648410fa01258f8206271c68da4f2d2828b162de081c63857267dbb4ca2bb4df4ad145d940c5ca23029e6d02031f48c92101c624224a4135d984bbf3fa6867970477a1e7330c76c0d34697a2c18a44f9fb686ea72877734b861461c0077309fd2316b0375e935c902033c038a1804ed5497dec406ea98576b03f51a6cc0a4cbe9d2db0041678858304ca73e93583048a72e9db64bafedad2f93ba312174a2ab77d83514d1c31ae69034c1bca329ae861ae79c77a886b9e6bc93aac19452641e3a5dbac1e13c576ea4688d73986b9c6b9c6659d34f1d94049dcbce3cf4da425da5877a8a92e89c86798dd7d81a528e39ef708e8405438ddbb88dad817a8df38ee643f8a5ca6d52aeb211d2861e3a07a73c7495c792ae52d20e02f83e4a7c90a0bc3a12939f9ce74ae63e3034d17c7394c360720d7248528e39e49050af71234e30afd91cf31acb9a8e0312318939d0535e106b5a2337b648e571ccb1f158d21a317ae8f0c8ebbc55cc813e2080efe3f300dea9711f242c184c8e7294ad417393f30ee6485830a06ae01ce5bc53e34338e5d4b514f5aff9c16ac71c68478bbce3c4e4288f30686ef2e690708ef288647393455956f45392c71ce89b57630e74cd63456b24b362308fd5d608d08e0d3540b26b178d9bf217fe9245509445482a4610a143040e1144d039274de28328c84093c68715c00022c145098c7b7e5d7174c516635c5174c5115dc552ada28c04c0f3ab0a1854d105ab020b0a660ea164c9cf168bd12ff51a1f0440423277ea57c6db8fc187d05e76e99a07894cbdb25f6a721000bd74593dd95f0f44a83b73b147baa9277ae62dd9f3b8621815495d96ba90c1eca5b6a70773c845c7a4531c481e24148df993cea2ceaf2f527eba11557f2c6a85bc6440f4d28f2089ae7814a4852988bedd85d71447df9e84c9d0cee531ef440625fde52527db52505050d04e167e4db1f497d11c31fae23d008b92f7002c63bad550c847b7e011df46fc55e18851527ffc6c71ae60b6d3a1467f7ebbda2d761f8a42e38627c00284314550e412fb021205178935b458b94248872360115c5250b91c2177f07245182788c07c01c41448858b10171fb81dc24881860b0e72072e6af8206b25ad0694cb16528c1465a4702283e092a5b9b4204a212e5b5e3ebfb838e1058ed44147eaa05384eed6e9c34cc75efeae9e8daefa6525196243313e30fe65a582cb47e74cc76aedbad5ea7b9d2a85da2b59e5ced182611886615812cc032ce67b30af987759ccb3981fcf3f4c6ebde1d0de199efdbaf07c8dfc11242abf31b0069edd634e9bb49b1a8fae1b9ad778affac3bcfdc6bbe179e760ae795f7f588d7bb7a04d8d266b7f98db785b7f98d778294fa73fcc3bcf477f98a33c16fac3fce4b5d01fe69ce7427f986f9e11eef1a37330c7dca459ee0ff391e487aaa7180db3f7fbdbfd7c9337a1888eab27ef8b3927efc67c73c61c730df39307391eced6b8417bb7bb6ddd5370f6ea88f9622fe7cc25d1790fe87ccfe6a6ab13ef66311bbf4c1cd638db9be453333cf4ce53a1bf761bcf4774d5d8b6dcdfad2614d1659ef23ce67038ab13c779175d2994b7a73c8e39287bb79377b7bfabeb7f93e0bcecc1bafed856896ad984d498199a818a44e8d84f5cdca4364d346366c810c2facccc32faadfdf173674a819451c32085dc63ca26d23729e3dce2191a16030e1092e0bc740ab5bac9e04b537aa5e1651b3f325e0cc3b018b52ad0abcd83c612bad05f1bb93a1fa345a8842cf5a551cc3ae6cc8800400000006314002030140c898563d1703853173f14000a98b050604493479324486114840c318018428c210000638c1918aaa90ac6ac4cea287ae0f8abe449dc757ccf8d31e4de0d0000431c0d8c198c4a027c2710a563423b08e5542885e638286f334dd83ed7a2e3b7a6c614174c6bf81299b3a01ddbd4c071d477dd45608ff78d50861fc1021970a4571f26446d555946c806fd7d054f33dd14aa2538a5284fa6e8242a208489afc4480165c0e8e04e01a89ca802b1da3d1eac0f8edf856c3b5b29508c6a74a711a21fee161c2c82186c4a0e9a1707573581927d90aeaf424031c8064f6f6fc8800016ca4083dd839d663d82286f1d4912e16c2731b185c8a99a00e8353d06b24adad97cbbe63a26645e8a92a83443b3f5b9713896bc76a5cb9f85cccedf120356342973bafacd34cf12626c65374e4bc8261acad615669f95a71c70f678f62732126960e2016753ac5ab835ea0c7c53ec88ca8c0ec42d9bc755ff3508d754d4544d028bb20154b46039adb69e801bd6d9d4b6244719c6bb58131c7675d43cb00747a64583b1324ae7c8df5326bd6b8c5394267de84bcaa7598941848d14a8cafe542db200a37f7d09a5e2f7f5855fd1829dad66ccaea9b814e1179e59886a6371dc061f5487c5af6f69b6ae1fb858dd9c53c3ff54640a90103c89e0980d035524f9050697d5b3e0c8ae25ef96f80728171c3d77a84715c4050cce93101824332717d2a1d388e54971a0f9f4c3b633bf5fa286a29d0760b931bfc9f7fefef758e233708ebee76046f895e826702c5c00ce807040a26b503c7dd1ee63ca824c75a2e3ec9f9662321ec966a30b8845cb6c09f153c66873570d2011d2098d236791887a362ea2175b122f645c071c474bf83de35580fdc5c9235265160ce7f268c665eb84dc004034a3f58ccfba6b40cb2d6ad83c8082de066d49dc8b5c490587c82495a3bbad96a1c7482defbe32c12cfd578288e5ed63d6f0b0f0ef43b77a1e3c3f2225c402d4d5c90d322163d11d3e9304728ef0b7b8122add16916c39bfcf68a7af5071e2c8b08ae33605503d176ddb42ca86bac7577af2f1b762f27a6fe40200cf1aa204e3e15bf1940435124b4590842163abee93227a90aadd9f00082d5ac1b47ce62790e5e44e9965de73399110c16b9d379957c4e2899fc70050c702d08c94bc5b76b81037a873710f37a40e4d9e516e6893fe672c58c21a0e1fb8c9eae318a07fe89cbf370978a675c3807d70ee1e8df5bd5f7462f642fd7224a22f23a24609359e1870c8c96994308cb1608e0d58c359e51b925ef2679de39e3ecc3eb606d10d24f910ddf7d2e344891690fbb4ac1b0e89a382da6afe36b192c469603ef09bb0011ce485164e981ee8a54faf0017e8839c343747325e4b374010012349a5e0f128f797227031b0215897e3071a0c8189fc9e66b80ec1ac6d1f56cd313aebe387506d673de11c994ac23760ef800e3270bf383c0990b245f114ca19f966d1ca87635e35ca1c0a7c68686d480ae99b400901b94d6e356c9a451148cdfd3c404655f79f21606587260f8c76839527b0844aa713c0885a5480b887e1580090df5c1d3a8833124a9688ca5a0abfe0b7f144883eac4b8d450956c7c01d3060be49ecc8180370415f1a1fe34337ad0d2e4edde0877ef02880c634c147b0cb47ded6123a2ad3ecfdcdcd9ba2271db77652c241197d5e39af48d62ee36f4fa546e42a38608077a6e095223f118b526be2f4bf6a45d1b1edc5f54c1ecd1478335728a95fa6500608aa31f4a7b87a5a07f127b975f671750200784b12f46ecf76c1735fb9e620f0073213b40e7502058d79fd01a3d1f641f91e1093d52e43f4406ccd664d6890ae6f6d2cce848156fa88976c0e9b54f83f08411f2eb8a6d65812ddfb4b5261676603a16f091fb552804aeda63e963359eb952e1dd4d1b91366132177b40cb8a43c1e00a679d88d4b0b66ba042202e7b8e5253812e82b19660106ac848a657d7f6fcecfe1410639949a730a2b9c9d3a92e40073addde5f1d1bc9f9534edf15d8d470e90577612f56922581b72c5517d16c6a22f2c8feaa97a241200ccb9d5d1ffa8894103ca9a5e20aa668e5723ba1bb066a08fc0720532624c9afef2c90c2235f6ea17b89fcc380efb5303621e2e6646c49e443f14657ebe25336806f9af5517f4424550d64dd5c426ea8facd16ddd9460d42691955e2771c450f89a3ccb71c740c2d6ebd6e9e2119bc1fa476a2110b70500e6db406835f8e5b0937a088a61b49bf996d870666b6d276dbffd7c732d363ef0b7d0c74ab4f2af40581e8f5efa3689e73ed5f987386560732bc16f66fba5ca7d7823d64b6377c464fb8f2e1a9dd9ce736b8e77eac574642a0e073c73a82a67fb902da23de55687a1b6ffa40a24fd0dcd434718b55321c6dd04e5058a1766cbd3d8fce80c0407f8c7603e852286dd9a2c3956b22256f4ce99ae9e67ecf642738e96c1423c3a5b217134f25a5e24fc71d7e472d2e1bdf1f51f8ac814153a60c10291b04e8fcc91c6d278ac6cd3e5307f5cfbf54a4086db5c0040ba73747f4804397b0304a06400f560c10a611d08554417f4f06af06c283db05a5dc783207156b137e2b6039f06cb8a8b2c060939c47c6a6f1f7b78b909d9434115b3d4ad2602b9883a8d3631684476d26e86c782a11c1e87a8893fc21f015b92b796b019e0508b98470d0b1bb528c55cf0af9679f4269f91d06b043f970967df1ff947d024c659af24ac51efb31fe603bca5a84ffee92c72365781c9a4039b0b1a7c99665275fa756284aec80f6db95208d327bae380884007cd58c6e040eabe6780d82f4dfd012dc59f151e2512c78ad056f07123e8b4d51adbd768889e31d76da06a36878d64c9848f6f7c76eda4dd2ab6b118148082722bb1a10182f563e13d2a20bb821c40c809fcaf54200a529d2e0e8d6445d9e77ee3727384880f583d24d252e703f5d687d8335a2d1eaa5e1532ee7f370aeead89a9f7c2c461d80172422fd8b595a9c7971a620d676fe7ab3d4a8eb87bea77045aaf41827ab4300c8e86c4e6610bfadd329b4f5f7e88236d5a024e90cd9e9aac349fbc62faf06dee4f198de1883c48120af87edd7b7a12f1ee56f78582827ab63960d48add1c1730d7bba2e0ae3e9c3f16470c468b9b49533cbad92435bfad8627a0d3d73ba72d420b604f7d3ba75b6432fb6f73468257a6dbcc726fbf418ab568fd63f1dd17007d812241c290a5bd3065209f25ce45fd4de1c7bfbd9304fd7a9a3b6bb6f263c7a3699e766633a6cb381be826b0411b2a905f0acbe5a811fc82fd7cecd973bb206a2e2dd8c119036958d92e580cacfdfed483a0249a4fe8015da0c5a4ce6e7eae660aeaad9ec706f84eb7dd6601ee90c16e210fd457b13c0abc656e87f4fa6bfad81185e464a9a57e92702bf3578cd4f16c01197e18616dc3ddf7adce6c426cabb9a9a62f28809b7461c5d6952b43c7d9e490cafbe0316b4ad46503fcbffeaf29e534f1ef7de835dffee7093ec31ac25e7d0a6b2ac4e627947e8bc3a2186b0ddba7f4fbf83a5e2ccd46bb8b914ba271f25440c7e9ecb1e67f8486604541d34b5173167903c6f6c8524d2b13539fc1066cd7bfba5cffa8d158d7c3c47aaf467fee7260aa6b78437feb72522f151adfd41a3d812eabde72571e13636a8f4445c1bbe8509f4524780064b132c7362636d48b431f5c78da0e0002c80b08c303802423acdd4437505f31e9378c7914b540506cc30d7b6bde5e2e9425840985a9f46e87455f1386949299896fe787b3ae87f18576368656010e8871c22282cb0645781fb599c2f04fb3fe8ec53d4890fb9a26e0ae9c05e566d6574262f05a07c56cebbc967c2821ee190cb93ff04217ed95a8898ef32980d0659dba022f4312ce98bd71bf7de990d4ad61718c7a396e57160288038af1bca213d49a39ce6d50b5726cdf31b8b2b252015e1665ded85dae321c959f49fc9ceee0c2e48ac3cd9880422bbf57df04e770c16e8fc49c520594580340aea4e89a940ff4a08d075892bd6744f6e085ce029febb4d308323e98bec0c81379f4a984d21132db6a50e935285023069cad5185b1b32156d5b01d010d7d970c2317fe89cc5fb3a31a1b83d2a07925d0d94ad9ee47ca6ad0ffdbde3357dcc0fefae142ac8c5c95b31d8640288aaaec39553fdb52ac22f03f5e4ad94693783c818525851269309b47f61a126dda8b5b7a74da9b9a6f775d0f658071cc23bf50fba0d3b1156e595bdb2f8dca195805b6d097551d33168648c232564cd0a655d6b623c9cdb1e961f01b3d7a0e7f27a8b80eae266cd8a3ea352d1db3260cf184445d192c2da03287a4b459a17119874505077211c4b7467034cc79071ea7bcad7de41dc85d8e5c0f747c788f5da27c49c3babfdedab934c30f45a027c19fb9ffd03721d0a7278c584decfb3248aef90d3251801345713b03fefc616ae57ce472084f070776b4d4abd98a09ee9b8e9dfc9e1ad5c4477640597a8f71dffcd1921668ff6b3ce9c1fbad58c43f257f45f1fb9112b7a974f14502cf42fe38ede8ec5fc484ebc2a228f14c941f222584109ef242655ac00588f68ee0e8da085373267c69fa4f05f5e12bdfad851652d0e58ea997fc9f7fe6551371aaa04c7764c841d3c529e422b008442ad9024f795cc87348b5ab011a0bb2c527536da4d65f94cb286ec1a0977e0eca0cd6c0daa33ebbbf3548ee82d8c975785092a79b6c6f99202b0c422f770d56af47cf46729d7fbd74bf678272b8d7c1264aac1e01b4d4d9a8f0d06d0473738ff0edb51c9d77c6b609c83a6361b124ceb2d4e2761e312c67d102e0bb063e7e11d50b2214923cff1d68a60eb9387043be5e7a29382db134b84327c03454b8f7d1cea92cdad64ebd1702d04c59fdc01fdaee9cced8a7ad10823e2d0a9053e7f244e557c4912f42fb729d2024ca0cf56bea500fc6ac11bf03e318d0559beab199c72af2b08545498ed21886fdac021a8b59d1cdd8896cb9e04dc5e0986c280b2172d718c5c71ae75d7acc4caacfe77b23621ffebb71e69e07b0dccf5f8467a87426715f02b642a02b2ac7bd59fe6ca8e85e9646276e7eeed71eec5d2606688440283be3d8abacf39fa0d400e430816a5eb375f64d83bdee7a900020c154dbb7586fc666c3a056608cdcb662242b7955047dcb13da37dc6ed6153b9f76c9bad43e0f4a99f144dd750d5287d86f8e28b8deb6d2e79a8e5e171e28afdd9a7d5033d5bd91316b96207615feb394eac3b36ad39bd7310125b978cb2ca19bd4ec9c15073b81eb2929452f93b343a61777f930fe5a6ee0d4bafc9455a1e16deda137d06bf736ac7b905ed902ad3e8524dd58f6dac1c7571894d0d7a38750a03ecb7a208d332c5d83703229156e65a0746c429847df001ce47a440b4cd6a92f21c27008a05332fafdd929b10bf3f684159e442f21db24cff3845bcd75ba3f83583f7ed027946c1cac173ace3d9b73dc842eb6536145e8767d0f57f133d0ac30ec9350e0398e4dd4314036df855887cedc27977b28b2a33cd7f3b8b12df974ba89ae33618a87daf6f24ec99017069297f4506e994fe043b815eb581bb9cce6e85afe95a6e015d1e3edcbd84963d16db18b09df60858f7b33395dba3f8d8345776593cbe2238a7a4dbb09b85e96031d0430717119d992efe029166d74ed08e416ed8a0ebbe49f2ff4f8edfc6419f286210fef43946f34e0c782f9daf8b2aee36f2186ab1b2f8b86cfb78bb651285a6ecf45a086846e263fe82004a52476bf2d13aebd4d4840d435cebacab184a0376c5e4a307b7dffc15e42d39bc1104d160dee41e4a1f4168268625a352e9953a61f95fc020f47df08c7985c79f3fb538453045aab263cc3b9a8d6d9b670a173be7954b34bb84a2f79eec84e14f160b4d6d43aea8713eca1da8a84ed82ba23302c2a3454642dc6247dc4657aaa6f22e9d28095fc9dc0114cc253d6211232b250fc2eaf1171328776cd2ddd84429730d216a1b01f0c71436c7a3265a09cdc1c4d49450ad942354733bd46b0cb0e8b688ad4cd1c8d868a0e9635c1c081464a113a4ce7df1a299356cb6cac88af7a177b988f184061f63cc021f85a40e699e3ea82183c5ba4549c8f112020779f36213619f9eef84d67a68d2a18da72c2bee33c66d1ddedbd9e2dd25cba5c2c6aaaac678b10d85d105b2be6261285e8ff34f1067c753fb815a3476cf0c4199f4002e9ecbbac0f129e7ccaf2889e75baf89a2456c31d7c1c2fcfc65a9f96a59524febfbe8e7205bfd5b95364e956aa4797dffa672c7dd0014e711c14fd4c525fc8ea8982c76e7234fe70d6674f9fb418c8bccfc2024e4c331a203726d6fc190def65e3beb10a5753bb5635dba90a5b7a3ce84f1aaeec17f42256b175773caf74f5e80ae89872361e4f66c06c479299e39b2c6ea855a0df0ec76238cf651ccd1321f470dddc99f67ac794eebbdea43de271b94a2b08d6f8626539a4058aea5dc6ac1450b1964cb9b1ad0806bab3565706baed62290c7c54c6d00d145e0371e8d6ce66df5f0731cf0ce0b96dfb44ea63b1c6fb592d5b45538392b76ca267ceee14b0460574df94c1cc673bc3c42e63ba923c9114d31a0a92f41c2cf4e0c82dbcb3d67fa2d1768e0afefce74cf49667274e90f8179fef486700ad346a1ec2280fb4ec322f58870183d2a3c8c603a20ffa5b0a832a961402836558d0b78e58d83052478aafbe280d5913f9ff309ea2f74388c787e9962eda0be02524697854a86d21a3410682b5672e330ef97cfbab276c72cc3c244c98841f7e9a301f56681b326913d4752434c1ca0b2e147ba58695cd91c90851bf1f8001a26247d8540b20bec78633204b028b5c6d3805e6494059b091502ef6eac665192443ccf078f3a23125ae6ce6e849cecd262b206f63e5cad787170f8f91e6dc71ef49e577956a74587651af1a8aaf92f1f0d855144e0f429a1d2934fda1ef9948e4de61413f56b03b2bb09963fe88f05970bee8afa0061b5e491fb87169c4f858a9cee560b05f76a36f4e76d0752f098425024de47044d421d6e4d08c2c75b28f070ef7d4f749a6ba552a50501b41be97c62f51a1b9e349f3d8f7a51e07094e2a496319260a8186616572204662195a58d336e5c722c35a0653ec5b004639da8b4f0fe0e8c2c62235cd972c987751e944b911aeff4dde1078605377c1bddee08122504e37f9f18f0a24c36db305f018879464266ed6213427c4c19da2038e197de0ced111ee041de09c183d1182e08ac2231dde2e102c51dbc4ffba4f45f2746e5054681eae8857c5019c40401ec37be21b5a58963e53b3f9fd10c1c689a10a44a32e36cf21835b831f45d02a6fd0f2eb245eec399d5d02c04c4052e6c775fba6fda18d72016ffc0a709c80cd13604b0f703fc9a8d66555b3f01abf6640840cea590f286de619e2ac7452b8f9cf90948f09ac139905f665fe77c124c83b8a7d79403f83a91c71a8d67488555c8d8ba020213e0e74365074971a1953e03319ac636840f6832903832a58673182484e944662241456f011b0778ea876c81f6dea69134687b37f8d6186e3ac832e713242a9912079eba75097e656eaf1be7837d781e952b7892ccc2e67701a687b34c63857b529c661517ca263778f3f1367c4c771150613444e6af0821bdd26111a685f3a07a20ca2bbc6e8ed12a3f4f95dce1b6f5a3fffecdab13783e88fa571e0ea5aab1a7286b3ffe0287754c45a93f4b815d7b4cc2b1be14e05b008a8d8c33831baf842d2dc1e01e9fbe8df46a3083bb5b275e965f13932e0c49ac0e0b556b586760569cd0de99d09c57e7b01bd30a52e57eafe4e3d087fea2a5f2ef410ffa7ba76441323bbe84556b4c3924f3ac20c26c88f7fb20d4d5c74703921c815d9a3461f1610ba3441fcfa74e992676f48a6e7726ec14ee40f18470ac5193a1c1a88e8190c7033a5c30a74e8bb81d38bad32c4bf24c02aee2326ab4e2b56a23fa12c0bd808676e239ac2a0f0f638d23c77ce262675cecda3f36c2518066c5558f17fe3f7e37727a1d0e96caef18f17bd3ecd5f9fc8bbdf7aa6d03e3e5df4a72e8350a257a92ed4a7950f805651d08a3f6a9554007dc2f9db6683bae2f454fb2a1a2bb38a7313edd41114d913b1ec0516ebe66e7475a8f4550871a8fef25a19f6bbfea7bad511a73ce962eb6d36469bfc36df2e170034be9d344d013a3f4f9395226c64f08fcaf7e3d98fa060ba7f32ae0ca8cb0f260e560c2b9e9165ef2895efd6ea145843fc38364445c5820b627d83154fcb8339a437a5d0cfb0c21958d07c6f92561e84dea778cc7b47f5047c06e9ee30078088843372bc0b7f4b650cd6995225e1760d7ea18a519482ba5c96919e00be75cf64c26cee9d2862b2d33a4027484f11a971785214d3dd316d388d560e61c064ec26e8be19a1e3af94a7a3fc4e059093f3150e2bc0fb1e3ef71abc4f75808af9db47c40671f45c908445797b43c85d341e5479a98d3efd26aecfcc513fa8976bc9d38b6da25556ff9ab51b5fdeaed0d33382bf3f1fb42511ec33a50fa4ec6f74e87c100ecb4fa5f0e4fa081a849667f586d3a556a9dde9938f1147d8aa8f65997d210d36f4a08324367cc52f17f5c19b98013c8f5a308feeb4faa3495d308de70aefad3be10458a099de9bcf34209d0ee74e5915a64d14e8e6fafd1fac5e448b7766a98b51c4a1ed139188955f78000d24f25e0f590d5c94ac2f1132359e3afc2471543ddbdd8f91ba64b26d9e9b54742fc6a1d4f70b48c47104f9a5514dfbaf4e71f9ec6173cc340fa3418356e37f7a5ffeca4b69d96c9dd0f17f40abaeb087255836c1e72fa6e2eeb56a17c0d4aa834bd8e06da5ab8bf6f8933ac01f9dad53f248898830ddc9b77be29231f288a4b0580009908b89c91e64be0cbf4550a3894302ff155ce3c28873952685433647cad812762beec69356e619798cba70ccafae1ffc3fd650fa71440e713106839f6e1d882ee0d293f848d7ca3f82d0ddc9089849c4c25a202e110829c657f4b205fd82241e88ff7f0cdf8cdd3f6af507559f50d021e67104bdc0d2d99224b23e252e15fab251bd05aa3e98445f00e9c839f21397daea53561da8f985e4afb54186573657c45ce039e853a643ab3f34285a54540bd52ff4cec259dae0aa58978d0532addf7165287e6388c27339e5580eeb42754e2c94e856ca08c366814ad255d383a9ca5b77fb5b9e47390fb9d2034a244f52852bc89e1181e0732e822d7f9aac4e5ef683b0c4df4fa3ebdaa8ffce283f660a565a5dc3263ea1d33756d9fd41f9242b7ac65948b4c432a04c133543882569643863b9ec852b6b7fe448aff28103c2253b0042b7814a3f746c7b1098977058eeaf6bbdd89c7ab82d3a1d739fbf14fd88911bcad82c1b58c88158403b305e9d7c8e28eef262270d7c09b851ace69cc2bc9760ed40f7abb650025086669f66577175343bba7481480f996cbb9e6d9949f025bd48abb79d345fb519b8f3fe1ea69c7df5d31cb98f1107d9ed8abc9b14118ea20be952c10e914fc87f7db1d33cd219977053b5655f8e1a9b6cd8afaef4b7ce3568da4d940810a2cb407819fa611014f06e9d5ffce04a0ec9599d553f478d777c3e8f4361585a2a5cfce682d2a5bdf8d8feac5db5f3560dfe8504cd5b6c062853d159da9862d0cc79c5e9028d3078452f49d6d5a556992a4e92ac395bd7fe955e63ef7f190a028ed72d438bafa9f7e4c4c496d28d4e32fac643d0b480c1451b6f5092e04d66a8b3a374fae523da9092f3ea5c3905d35d11a29eb88ec352392dd51246f837c654046ef582ee0b458db9689aee0b10261329845bed7980207ef30a02bff02c6de47781ad2acdc004c0cc9b20f6f4d2d983b3b772678cdc3e2b254bd4adb7d6619392d6ccbccfedcb53c1af9cc5c828fc69148eb241ef8bf4f80667be75d445ce305247116ea5d86f956195de7811171d5c3260bbea6e117fc81cf258bc8064e5d72630cb6f525f885ebdea74427d09042565c74e4d462f53e0be855427a4f7a98b4f4051816e1c6c616fb66d8542d2734c8252910fbd8f12713ac160c02b620683085c1d6b31a7a6803347e3cd2cd3f5db86e7bc74e436181874eb3aafeaac6de862a0f38780012bfdfa3f4b490f246080488a4b168b41f432d86da29cc134bb407e6562a270045491bbda757f09b51ec67126dd9741ba3ee0438cede40dca9cad94a6200475f107149dd3c1fe16fdaa6018cb959640d66ec3a9be52a0d44f075aef29b8780ac62d9b9315de691694312acf6648c3adc165a531ba47499e1897577ca8246fcea5c457791cdd44b362636af819e09a33472e588c7b80312885c1e82ee6ae684798932e62a0a4c3953516c724ae74dbd9eca44bf40a28367eaba7e0854553cfd0ab77dde809b0b85a2359b144d209e7a90daed2aad86ba68fefb2a3268572229772fb5aa73cc921ee98489c9d2e4f4ffc7884912a7981a21ad6b1c1d21980b94b6178882b21db72619071c224aa861465e9ec94b09412859fff39a3afee56f3d52bd9b29034587735b5b1c4b0817b9ac25f8e950a7334a3b47d941cf4606308d69daff36a387c0dae43c3c8e951d7ec2212539398c01fd47e4934608fa8d4f1f2dd9c4df7fec8a7f5bf7931229ade6cb6a4ab03884637371323f90e97908208ca120245ed623740937611fe52418b8ec720c8406507530211d23566db7d80cefbeff4c93123664b400ad4dce80c8009016c11ec77334d7db1d7cb3468969f6778890aba03d477b1e875186ce7e8b5e9580a52d29075a1a116cdd74f65c3db7042212c998f14fe8cd8187b184e30609b13bc30e2bbd9884b3a9c0d45be10d5c439e98daa062a90c6c08030c82855a0e86eb082025f99c4266b4bb830cca9a308ca5f5d75fcbc538b0990fc229abb1d858dea943fbc9470512bb98a33eaeb6cce6ce1a0653a175e520765ff7e89f0030b3a50052757b06228eb666036c5b9a1342842174c8a8073deab1dfb6427824e3354d3c5fe4ad8f05d28ee80d2f1e014330ad062cd0c98dfa0e03aa59c726a1f56b899e2624613c4838f9257dc3352395049b18c422085205bef0afef065ef008ddc1494fa5b59173b9f436e03869e51fb4fe6eb8903db7a60e6a03b20a13103a30418c995d4060b3863872e2af22a0312b99ec4cc787981b606f9b35f3ee4866a0e205eee27e281209d2b3e49bb2724236112a124f2f5e2e66231d148f544c17c172d29371e1f885d9001ebc643cd991ff1bf32407195033ed565a5863df97c0f84f2f0e17a3830b9e028deef1f1907e48cbbcf01cf4e5a67321b61453a044c2d93e9f822363b4999b6f547264f8b4a2e12b1b5561ca6073361634ebd637b017a4ffa5a969c5975f986bddb602b4f9fec35952911a38acb18a3e4b002772cd783635ddd344de8b34b503cd4b406e49d8478a75c8dccce7b6d3af4ab3ed9b4a21e758c403cf36cd60c7f55acb8af1d8f4389c30441e176f751ad30dc0fb6b178a74925c798b2a4713dbd9880bb8896353ac403e26a5aebe88805283823a16823b8dd2a50f00f60d21efde02ff81d1feae3de49e66ac02d7906ba53faf5c72ef29dfc817794ebf95b82ac309879c458363622432d5ac02fb756324aa0e13f281ab2d9ec0b60fee3660121125a3800404ef1e1400854e3a25a3acdac7765c24c29ae909ef0b8e1cb2a7834bd80ab3c4fffe0858a20b92a8aa5dfdbc486ef6382adb9dad77086bc3b831ce87f25c2cf356eac0c10b66b9656007f2347d9dcfa1a41f98f02e85f8868793ffbd61adb8c8470a0fb75c298d0ce73b76563fa6e8c2b1503d426f14d5efd49d38ae05188dd501836149f0ea89bcce3604070e9e619d694e5b1c7b2b1f92c6660f8bd1d14981decb0fe768c917d297ee8d887a3fd5a82d100e5700cf8aa5b352d5c38ab3d10e96aebca1a03f0b5c3d2bf12e7510eb584f5ae6d05a3c9d12e8c6041610c356090b90ecaf79956e4c1ddd4e6accbe52ad38757694bc08cbedba4987f9d01bce0b1442a4484a43cc5cfc17ac468404f5d14e066e130b3e2448ba8fb3544582eea2f080383ad38ed3f1011723cd472e2999f39328370ed987955ef64e7207e1d4cf50238ec42c03aad7dd6ab6eb68d6698f8701b9f9c96073e40d456b360ab936fa6df5427fda751c00f3f05457e5bc6af6235903914d870271e69be18c03202dcfbaa439a81799bb1e011501b735237310689ebc8eb8a5a7032080b06ee68e0667b5c32e4f0d46c4e86f22fc2626b7fc3b2f0fad0522c2886c58f8ee7cda7cf433a03017d77f440f2da24b14fc5a6048e432a99c2eb64c7f80830ee970a3d48171e064f7f7042cbd5f90dea6957ad83c7691cf70945931185c5f3a9c43e4ce110ceefb6cf09ea75d1575ebb186e00291934c2d1feca11a4ed573a647a581caf6aee832c821d63154cebb45824deece0c56b1ef2418c6c92027407ae37d0f5c0840af00b23430476619e9e903f02ea51f34679dbcfe5fe970c8f20513f1e884fd80d048dcde1aa6084e5aa51c9148e64e1fd13884156621b3b89b2a2cf246c2662fdeae770f69e5bbbec0ef1170242fbed327a1493de792f31f9c750780bde1579be9d75255de2c1ab0c694aae9823ec74e7dcc514f53ad81a58f691c695455d5f65d0528cd3a641a162e3a61751225a0f27644d93ad52c722bc0a6397ec920d9c814bd43cd96ca882a9a081da5ca79ac7e9a04ba05db0f70545a55215626730a2092bf7ab1d3051e62ba99479a8ff10b23ddcfb1f23758298018a43aae512fa378625317a834e8727cf992618346cbc67742d6bf878b0f0a7d8d8aadcad504e3106b080317cfb9f187645b3c333fee57201fd890ccb5328138e52b62585a7457c0e43d1d4e1b567685b7fec6517d7f98f709056c9f8a6899d591a1ad0f35405996537e19dccc24c1d72032dfd1846b7b413ea0cd8cf3a34bfc2e0e466bc3e44dee01a18249976c1793cd7bb93b6f689236e6cb0431e950db3a0169d7f9c4547b7620e4b867d452fb31f70d4e4f0a812c2c6385a9aa7f6e84c9c8a19037530e08dd5f7cf37a276d84a91c41d2d23af303556121053af4ad5a49f3ac7bf47df318daaaef0151711114dcadd9b86c21c00ce0f8001b96e335bfae36c7e055666ae1d6172d2ae68abcb12d1cff08490ff9d6ca5e74317f66a3b6202456ae1b77006fd8a5aff0ac5a7aadca17398359ffdb0b6743b32b566beacf911f9f2aa79dcf72ed8f136f140a9b0e1ae0993d2c93f867c0a5dcca506f0cd656f1167518da4e3a0f5853961539fa7895dd75137b788989166c3300f42db16c5a2e57e6cc97c5bba6f5ee85edd1a5a770221efbf723b00c6c849acc33d1294ba87d6f8d2d42cb02026f5f969182b0c5cff49cfa0f9bf0ddf1ca9a6ed41fbea3c2f1f205b2838e0ad93cf8f61d41a24681b224a1869632868ec81010a3e92ea89c7dad43986db8f1b0026a45d8ed90a97fe52a1def3b7874dd3be21572b93122f819e6753ebbc68612d18c12f796aae1ad7a8000cabd90d02043a377ac7aa04de160ecdade8f512671e8207dc8ae232023b39f96f34e55df5afe3f0ad87181341b0b527c8a704284614e1eb5fe53626fa5fd25cfaf34d47f9a66e4b3a37a0141174a444fc9529b318f7e0fa958afac347c619300e67a4b3892afb46b85d23dab1b4576dd99dc7523284f5e8291c76dad8ad5956418c0209d4f3fc034a82b7bd9a6644ca16aa4118220bc9ef04304ec89e5b721b5c8bf05dc35115f849a414aa2db132b780818ccf1fc285a68153b6dc4707ed2ad181d2d07542524fd35b4df353577adcdcc9683a0a7796479459a88e542299a9129fef4acde5403cbe50050827180e9a19ce442c4b045ca203da904c8153d911c432d8f12d9c1bb46f962ee43240bfbe1c1c9e0d50f147adda756b28336e0d9c4ee47d209b8b7ccafe246d9114b2367b48ae4591d4c9f0cfdef878d92191ee733641bc9df84c92b2ce755f14bd32ec028dd60f2ae5a68ff45a0bc9c27e23775ea6fe5386a170f4e559259305183168b421aa23d5829acb0115bd45f594051bdc3fbd96a4832b1ae08fc486bfa684173e7cffc9c4d61ce3c83523f766efb1afdcd64d3669c2302b302ece62ded708e20792198aff31ee82a1b0cb6e6e15c5ad33a236ea96db30e64151891bbf6093167b742332bc7715dfed13db796990fa6efc43e07445ce3ea4760f68e0efdc12567bff462f4bfc030f196eceac6f4ad691dae0f97f381545cb22df1557f9858dcb87cfbee24bb30a554cd935aa902fc7985ed915ef71e068ec3a7118ab975d009f521f619edf7e545e1b728378c05ec97c5b8412c2d60c0f0d95f3645217b90ec8e95bfcb99758f7b4d79fc48e7bfad6fd9636a8cae2b1f47a6ddc07e09ed85cd59faaf5fc1ba45ee8baed463deb5ff61832d74be40a9be7db3f8524726d1fa0b12a3222747cad9d376e5e4506942eec0366f9da85445d570d2615444c84c510ff3c1fdf969bec6c4eea49a2a60f277ac0695a14e6a49b5f199f6be7df7e4719946dbb3aaa865dd969d598b3b2c337f50236fea468ad4ecb5bae6b0e76a4b669436dd14a38720450338665065c783f4b6c34342ed0deedca146878750cca2e47361b1310ccca0045d659cbdeb863e7d133f7f428ca52524242f5727858ea980f1be414d71b28a314105dce3239f2f265394035810a725cd543725ab842ddcb8758b970c7f849fc881e13d5b58ba553021bb5c433cb16ad75de85425880490a467059a6ebfada32ae06d2d9d5a43deb5c011c3437b8ecb0ab614f4b15a53013846c593e6b7a832021d0b6143275f505a6b65105f3aa2cc05e307f16f4a698662e98f7c98fc21f534605f8f72261881b2e3bc674d0dbc2fdcc3a04409df507b2d364624d6b43daaca0d420d427bf66c2603ed4e96097f0bd2ea15baabee21e75c523d93729478e75c7201433226afd36db5d0a8710ce24946aa6502aca022016b5844e98e302aa70baa3d7acd843ed04dc7646e5197a6d270fcab14b471f91f240fddec4b8bc6bb5bbd1140797afcace155b9a862c88ec367d893f283af9c05c53ac563c50cda54bd423d72833ff48c2359f4c8b05fa868f0f0293ab7b69b8777a0f7113f48a9a88b85c60f2dede33ef1789afe113bda7f11e669a65b518120f44f89a886e67fe9aa62fcdd491d1dae8b3cad6d8e1644c4cf9e9a6c7584daa12408e01c8dcfbf462b18cc334196953625f6b81a1be8aeddd05f12cfff5f1fc78af892050a86eeabf27499cc41a6ef4694dee70162d20cc1c20e6302a848c8e80ff40ddfec28fbccc52a3050dea98d3441033f2d8901f09b3e1d9340a1d4c57bd55bcb7a457b4ce404f05ff6230b12e1f6ecedea9bae6523abe331a4220146cc87edff7e116b6caf0050111094ee6e07a322680614c80084b8a106f53df2f9a73a704430e7619d2610fd1a4a801896409ee6e6672264d1a29b614833f11b9cddefc611f854a9783325905820a923f6740d78ff7d18c8cb9917e28e010e3f8c9aeb91669abdf873b7184f4dde0e6d64574a2b767312a3165d2b60f51328b8aeb65f11adba09d33ba5d0579ddc923a7c1781be897991c0429472ac953a05182be0456a9d3aba3da14d967fda605f5de21dd3e6025734fe3b79cc5ed7bf323e4bd38670b1e458c80016be287e7224bcff622a0dd963686f525108ded8fa7a4b7896e338fd66190b4f1fe0725a71ee1e7c1b2274c8c581fed573c8d9b58e6402e2710fd752df5a03126c3a1b7d20db8961069206758e23344f761ff631d804a7e0b962c60db0d3c7bebedab43743075c84f84cd320c94bdc22a3c805c2d70e2238054dfdcc0c225953e1149f6003f3fd5dd86526a5c90d65594eb9e621e9c2fa87d427a0d742902147b010aa6c2c3dd627eb1bbd8be6b103cbb3a049c67ec12ee614aa5337a128df61d94a3fb8848924434051b6c44d3644d33b9272711a2a3663055e3c3a9de5ca16fb215224a83de055106c34b1cfb8c6a1ef7d177c6697c405996c9a2cc1490fbb623c2944980bf503ffac2169f7c73e51bdfba71e11061746646a6cc8f8864f1764a87db2b09ec9c29cbfd389aa858ce227da2c38e630a5119d363e3d06f1ff784a047fbff6f93e75e48a386d11b790b843f6286e28edd73ac1e4b1bca6f861e1244bb96ec160c2b520590456077fd3c6e5b2b9b861284e6936c6d65cacb948bef228cdc053ba773d60cbc7ae7ea332fee5d701bb7ba5ec6ce6ecc3684226ac4951c40d65353f01d12765a81bf35110232bd0e17916838d8e808928f7051509227664275cb4908ede32f34acdd1c890fe25e632a3c96d6033ff9f12f0d9eb2e8371dc058d1e9337fe5019329bd543022dd770c025fe3621c37313574dcabbb16375dfabc89e17ca63ccd799e5098cd633d2bacdec62872e179b5e2f932f13ad082ee9c56639fc207a6dfcd4fc8836b2b14182fc7c9d29c9ada20a937b4c93d7daaac8448b2f7a70143593c576d6ff5e2cbb65db63219ec69faafcd28faa3385ef3b593dd5424acc15e39c9f31be3afbd0fe5463e1e8cf42a646050944360e6dd4571d3b6c3d9bda5ee6be7010317feae0db6d6a55baf0302df1bf78f52c2e9536accdf40d42c936eefb30d77708f38bf740766757b184d172d7b3a64d12bf52b06a14cb2db8f9a1727051e3595e54d724da525bb8b47eee837fae93b928aef8e4d2e417b1565dbaaa7caebc313d8a2e8230eee23e1c11e97b900badf8c62e29306bac4c3912c73ca5806ee0dbb607327d0a3cccfaf771b6b46b3244723372b8d3acfd481fdb4f00d59f4cef54412de461281d6ea74dc083e5c91e53f9aa69d33d3aee03eaacfa1a5506818bde4beded321080384cf132fcfc89f96e506ad39ec757f97241ec966add97d4ad6fc20c1777c736e3f33708ed0caa24880e9e965ce4d43c0648c6290fc1f26a11bda6b2dbaae8fe7d6a455836d05a2f118a22797f53bc50af2812f65b0be77242e60bc53936221f524e1b51acf421c416baf285e2a6ab4ecbcd7d93d410d5db13c575fd5814a321a615cac6c39fe40723195047d2dbf5aab1a054b1454f091bc315b7eaa5887d1985f9ccc0fc2507be2cb695fe7f706de95cde511e9cb97ae9b929b99f28bc1615556c02dc79714c7954908eb895b199326a3ff625047dfcaafc3153d9265d41353caafaef12d2ba268b7b9bc32f2425673bb763b3a4dc3d7be62bb340327ab8c6b8b3799d68b38d07295e0d430e684021ee427cb08684b209b7122e0888c4b4e1487a21efc85a1095c966e46382d13eac483a6a3dad1f4754be4147264daa79b4414a37dd0174b984453cee5a2d50e2c92bbdc3c04adfd3c5348bc6a37190e54a9fbcaa458537c102235e1705a6cf2e337752ff0fae964d8bbcfee8a5ed909c45cf99613d2288acbd975f1a971cb158630b3f24091da9a7f5d7eb346f88e048a750d7f9155b0203e83a724550f1d5d583ea22b1b1fa1a32a886249ccf7f78c5eb8dfd575015fe3ff4e087320096761e1a0921b372c0ccee792bdd992faac2fda1d1147f5cbe6ca6c60bb90b39d9c90323d4bc4541b3ea8246a0e4c3267e377d98628eadb8b8d43fa20aaad6a46ba3990f39e9d16e6db561f6a3a9bbd873cfaf6c5754b547039610964ad0b1b03491f6259f9b0b088b218c2128c7f49d38dc341d95e098a670345ffc6dff31a44ee07ed72a9a5ebb3b3b3730520d7c3524fc1ca53348c755c55e0351bdcbbc8d6128621203426b883c9aadf47665888b72fcc360ce8fab3d1d06d05d07f7bfb15930ae2f114a1e81b15bfbeb012b1ef5daf63cf889e3a80cb3176f107350d7961652df2225ecaaa7133cb1b75f45c5e58075cdfb84686d8b7f9ea3360a918c7b637acb06452b602bceed1d4227d91fc0c615fff6151b8703057a27c52e5e44371aaa9e7926ac4b212c9599712d7a1e594d4ce40565a2ec4ff60f03e12365527301d72030c45bfdc6bc8e517b9bb6fd6da037585d3c26af40706a0c4b3f310a799dfb392509111b8803b70893b70352e39ee1671fdcfb16c34bca0811c5800bdb9cf6a6e7b52b8791edc78af860e15ad5aebb71fcb674e1430a93f512ab57dfbbd7ce646137e6ea966757b971fcbc3dc478472bd16173fcb7fee4174ad9a762ebff7c75c8f689a2a2dee3f73f9b08bac83258a1c77e17e6215a10527ba246ee7f5141445033f823eeec6b1a791671150fe9339148a50c11801f14b3df6c1d6d08a74d442c9e76087699f7ef4e31839eb2fc987a8181011be6b9d576f6b885610ca93fd0fcebcf639909b1156a195aa14cc09cb71a9a1aea83726a4896fd29f1e80032a154973d05bb98431087f2b4e86d1bbd8b6aa939342cd41813c90c4aaccc43900dc0af6d4728ad27f0ba8d1b52dfbbbb41340fff30445ee1b4929c1da3c6ff602bb6691079193809e64b0d8836ca4023c3faa20f3106308c64807877ec4b881b8cc8f4dc21a075f3325ebd5d288b65582ce5bf3489bc6a9645989ae91edfc51f5f24a758e225144b7e7adeb3fb7513a456798e2c87b46f5e72e63d8be6f626ab82a8834a3e7deee48346d014d6b67a419869b67258c6223c83266489e7ff69088bec1ec185bc1d387de4f84504a39e715a6a6a7af1a0ce1883c35adda840d117d580954982bb444135d1482796461c7f43632e9c02384d69399d7c8bc79ac30f1ea855fa1eaa42e1e4aab0e752d07abb13f84f11e3347a98bc489e46ac4296c3fe20929135ac0542c20febe6a21935d264caf8190dbe0a74effebac8a962011a88a38ef3ece089cdfb9cc334d91e8e3e9c5b0bb35cb322a60f5101a103355304930458049f1263c2c09551b6e869a5077a2de4134156d6e4e7d38505e20fa2e96ec40074d58e1e84611e295062853fea19da939d301a4e81d740b9deebd4df5a223eebe1f8668d9e1bc6b063335e956755076baa614c489c8e7a228aae3306093e7b1fcf5d9e24169c066efed83dedcbd54bf2e0959c70c6342f0613a9cdeddc066c6c49e2772b1b8bc48c2b1c8b9185ef383a6c47f86f9165161aaeab4f442ae89b7cd038d0490439badc9df916a7ad12309ebf63fa431fdca25c14cd2042c607a57c938d8840210d6abb472943f8ca27911cf01d6116e27a8b6841f41d1c557eaf213413953909363339989082875a8391932aaad10b25a2d9784a4ad9a5de528c30641dea5651c8757e2411e1fabce55d52687fc69f4ffdfd6d3a1bfb32e8dfcb85469cde495235a0ceaefc672c423c9c1a5ae12a8451e01b5dc3e6d1f5c8ec6ea21962cb95b58e28dc711df0770a16966a69fc5b355bd82bb190ec9ac93676ab4597d69ef37e6aebccc0c90418414eb4e012cb140803a6b78459e2f5bd0db1f1fa039e975988b4e37f58faae41413d93f09b06226c2706a33a43e9c89c2795fcecb52f382ca1b708287de8ba1d2096d84e6767d8062d0b205a4c61b53021aa342060a7fec97b145bb18a01b2039c99e897534fe38ca49828f71ba8d0d269ffad4da6641aa806ac5bf5afe5fe82dcf5e494e76c7e33e4f34ca66faab311ae07fc08d39ba14119eb15774ae5261e257cad5b330b7ad25b8b7ba8e23544977b0efc74f9568146248cca35fdb13146a6b250ff2f308f6dae31a7c007534eef0ef96d4ed9e4ffa86d5a27fe0775d44cc470a142766ed34165def6b1858ca8a93a001f203d0158c81286196e411f723f9e18d816b94e693b155d507ffb128ac6ddc0cdb1bb7ac7dc76d95c7984156bc8e84c88a1c41e021b3528d306ec6b862bc46bfbf746ecea2374ba54cab11e941afddeaeb6acdc1f48bc374d70827118a84644193ef9bc01844dc51bc3d757eb708dfff3adc97940b91592de5bda811f6fc0f27272144d52f7c854f92374293642cb90e918e8fbbe080e43b9c06a3cf0c3f1ee2ed17a6c0a8d5d166d4f0767af962167cad7d8b666e8fc1c46c5c1fee75702ea475fbee07f10228c094838ed2f0f8e42c92dde475f02dda5c7ee7143ce3372d7b3379784632fb04872fdf2110f7cc9df76c0be0bd0f18ac4214b4d88702ec77d94c9694c4ff7f0019e6ef9b194d9c3799d2c13171a37b5dfa5293a19ff0b304c6d63dfe7ad729efaac0414cacbbac2c141bc96692cebffaa3704eeecfe0d9a182dd3f589fb9c6bc4fe7ff41ac6d641f96230c260e54828dc666fd3426a8f7870a015d7cb0a0b7c89e80caca698cf10c130140a90e1d6b279d8ea0cbc1ca106ebd7efac3114a9fddad5e6165cb93a1f56ec9fb1a4dce395eecf7b31968cfb6ac2fb70666187ddcfd625ad918ceeb9085e9eba41b55a0ede85139dab7aa7dbd9ae0d353a84448d0896a5881b7d1b0b160401a7eb7a903c23d44e827f322915f846895fd4c17189810c7336a9662a48fdadc6269247b7c10e3744e01868a014a24b06db37a5b24c7e3fc345c231650ea8e6194946ab177853b4b8b8780aa53d5644e7158fa659626b1e3d6bf99c742a50d58c9ed87d01fb8c8463e4e2f046c1ad721229f9f3a7f1c391da8c207a10f355a1e01c998fdfc7ba9718e620d1b62ce28879c4812eb7f4351a99e529c151e995fa0f9a26c482383f777ba78fda3ac607adb58ac33878ea282e9ca8d5e69cd13c43036c503d09d36bbc9ad84e485ae1865070f892674a0432796dc3f720628207e7923657190601c7ad2f39a1bcf2e725dbd73fd81e04fb774412d3b22c172223763fc147e8f0711df1613e0df05932e68612177cba9b8a41bfc4ac6fce14a69399ac08257b083644ca549ba47928b938fc6343422db359e57b917cb25dcdd28f0a430fc99687d2bb09727740905acbbe0e4c4a7812948baf2cc8781e0a0755864a3aac514f38f844e2038213ab0d8e18447a0722a0b56b42abf576eac355413668ef0c705a1dbe6a7e378d064471ffab116311871244d3f775c67be1640c71a8343c58be30bc6d331a2ae178dee03a7a319eb979b354c5187f77ba30c206df233d06c42d80276036ee108cfbbec3615b708166f05c3cfa0be487a5e53c22a865937e53b733c590b872a7abe45acbd921f9ae55ea9754928a661ac11e2a489a26b2a80d6c63e126159df6785f534a0b3186ccbc93e997bd0559bffd3bb161c27cdc513cd202fc2bd8aa8e170c6a25dc8bab8438162893af906492eec5696e9a1f22a9a569d84da88dabc6800b8cea7b929c28a701c713966d0e067a751e5a58a9b259f7d702cad33cdb6e1236f9ebf3aa8f96c05603e7cad3fbe4869f492b87c0c279c6d8b4f0ae2c8f800991827bd59c2d9c90aa1bb5303c23b9eb8f76a7a6033103092dc97074c8e329036a639a62d603bcb03d56928ee8e112532558175a48b77a9fa281e28d39103eafcf6f92b2d57fb604b4e6e28e0208a3473747e020b0377982e512f961ea26647ded5d551923be3d2b862787d35a9f06b7d3e8a691d7b3a7be7b34ae07e9e8c6dfbfd4fccfaf40eb5a38061391845971ec007fd9d3a76c4af60fb72ffd40de8ad2f81e9b1a2a7deeb94d82b32659acbd452672a5fd688e7e7705223ff18e625750707bee1875f97b5cb78e3a9c313f6e1796b6419beea4538fda6d0f71a8e9215091f2c1c611e0314881b22314760445439035cd51be87082a94a0851a631f0935453afba640ee034c6243b0156f65dc7d0fe393544ac9e59e75f74e39abc904c36af0594bab6d000da24717c0581bb2da3048cdcdbcfa1cb9b13321a96b9f5a91b27520562801d4de84d68510b616867ac08012773ff73e62544875992e40229cff80919d9f440489d37f03c4149d5baacb4c3c56400490d2fd56ac0f82fd4901a2cc25d5ca3c00f77dda0f34a2e08d0397a9cdbff8cbb362670ddfb1e00d4b5390dc8e45060bb00fe035b11052ce9126fa205d997b4664d3bfcd9b557357bc991abd73e7f66418a18dd8b141bf9db2950aec399eb61f9e32862ee86b711d4c4a7bb6f1fb03861c2f2ea05f9a5b19200dc060505859e8635e7076984572305c524a0d9766dcbb46853655d8333458eb6e3ec41f91570255e7df45ad7c55a845b7927f5c417b46851b619360ce0efd9d6b8275e8c54eb14e4b68ed75db089ee87dd06edb91f056d8a79fc9925c3b7170a20854b0dfaa9966c5b5948840caae835003f22b1ca43420d9e10c706ff3393b21b9dc9a88fd483b95d6c7ec13adaff7e91c9a37b86791909f93b623e2a13255ce5027cae106457a0aeb6568749c72053a1188e295c381e88415a9ef4aa097508b7f52058b6db9b06140c2875d42adba1e4db427293659a87c8eef31ea7b09a60ed483e6dce821864ca327ff769be32bea4212a200f52d0c359b17d4b597b88a19298e6399441440c73e4844b1d26a3bbd1aaa44cc5404eaa12fd45c95a9467afeee34fa7d8ab3a2a68324757f4929cbbfecaf44e1f6ea14c6943dcd95e489b51a35b728b61a52e824a83b84073b1ab77138cd82a3f3c40e52a98236612669f54fc797ee69302e0b94841e205977097c061eefb42389260b638b50e976e083a5c6b835d527a5ab8eb3ae6328958c37c803fd666ddbcbebb81b2b275b5ebc1c0c8c6395df02d5bc58ac5d2b1ecabb495cdf87171dccc01c36d40c90d6e53fb0618c7625d7e247af58c55ff63ca0455f681736a639a4fba21cfa537f49a42c00f49278631df0f0e9f4e83c03e662846bdaca07911c38885a384820157f33262e0fffa2d90ea836064ae3f616f34a4c496b1bfdc4f4687ffe859be0d9fabf3ba6b2a757d12cb905e8abe5a8fa7fc9934e5545a7e42337fa043a2d93fd480ca6b077d985e4e7913c510f4a5bda920419cee4091f427e1fc76c80017d2b4ecc955f24f0297f3cbd267e999cbe5a0dc7767fa42e45eff861b120d6ef5bb6223897cfc32423ece5d421d57a9bc0d0fa25448b750cfc7aaeeabf917051e6b19dd30e152f4e44dc0444a72ac3eaf5f15c4c75639af54cc2939b95e5b6edd3bcbbc73d7f262c78c3956c32d7e318dd1ec9255a21ad97c500c0cdeac755c3204d9aced61814c3e3f8ea564e5fa4afd6af3528e8e9689317cb01116e5d830facdc39099d147316241b8f8cf25b954a67f81b32593e73dabce9d24511f98ae144a51eb2234a30381028f70ca759dcdce9a0e61655a76277caba0feeef0a76055f99d11c03ca126c34d66f7aeb417ac0aa76ca7b7b6541c7fbc4fa7c48f2e1b3b000643cadf2137cd33e8ad3d8e645c3cc6c603fbe3c32630204acfcb5d8f69c2eac6370a3e57dcdb5ac9029d48dd4e4f52104e58936050ed38c3c57dbd27392a34dd941ba0549a14ab0e5144e8737cef0c75755d9ab704d01dbbc0380a4733ed0b5771b29b14403cfe9ea194a4a6d885504d475aee656c976439cca9c3ee665c6af37003f4d62b206df05be7400341e82d10cde6823843d11bbbf2debb355224ebf5e7349255274fc80c3985a8f4df940610102e84e4dfc94fa5ef71142d2b1b5dbf68f9f10d2225d9ac9139276d17765847c13d00528a6329271a396e6e465d7c88803453e89300b4318d0fdc0fc6409e6fe41b0ca316fd10e950a72f8811e50181190e0cd0201af26d8f04026f6c52575267f889891bca8474e6660a08f0fa92f8ee7049fc6f9c600d83f209364b585e22a336f9705300c05656beb626014f8e492ae31d40fb3182f94f02ec086db7ee972d355c21951640ac82c43741e369ed510b8ee195cf53ef16487941501e27d0fc1beb2694172e1d30f0f1ca8b23b0050b02148621f766e57255c3b23859506a6499c1bdad3001d2fa916e4e9b75290ec3808e9227c4317fa13d3d5195407c2a9e746f8ed0f5413c952944b00b0e480017b89307e94728bbcecfc623324a127029ef91183baa07189dd3bb69600e881b5372d4703e024918892e46602de09d2c463cc788a70b7df7f161c6f29f9dcc32c0f42ed58971996e2ca19a24e4f6f4b4992d4e8cc70edd12a42eba26d78b1fb8b3cf073b03695006014232e6865bf2c619857276c6caf5148129ca0a15adc1d262b956993fd653945a40172e1905fb80e60db103372641858538c1dd6e576ea12abb6dff273d4c6f27efcfc2a5cae361dbfe12a00b11dd4c65fadab48e4c708fd4f4f1c1539f5fb578a94114eb1f6dcaf834fe3154ad37597358f554ce8ac5c01ee178405d3d27852f6147d306a401b45774663de43f055fcc6afab216cde1e458f5f435c5896169bf373917b87dc9b2209036be7616f44b3e6e0c88a5fc7514ab5f27ad770b84878765a9e12df044a382d30ed091c3bb4d4bac259f988f6ddd081d8e51837e7bae58f3c08f6f64099233b78083e702877653205324cb66acbe2e24f831d8409a3424209c70efde7dfcab41032e19aa88fd0af13840158f92cef5a1dc8a9b9b877382cb26336289e33a694fd2dbc905698c791b9d96e9cd605f69718eb082c41adfc6cbfa6f8a788dde7b7e9d5d9c1b5980dd090b7ebb0faaf99677789c6997129a1a5a6f6d4548bc98b1a65d1d360ebc8dedd14202821d28ce3e3980554c725975b5ef818901ca23625cfa6aa7ce5d636ea7bb95e370b1c58bad634453544bc82ede5c547b2c513122c81d5982877b3b2d82f2b49db7977d3502c452774cc1a066ddc5bea498b4ee69b61c19483ae317824a8c25e86a2318bb883a64ee1a8b5caab2aea9400f54a90dc34cb063e24e8357fbfd6a067d7a23d05c80a780d5cd58bd59479f6292d5d8bfc029d1b2e311a6e6b6b0a3c7f94d0d0ee735a2edd06824dcd136a0b4064be632809bac9db256ff58bc2d792c23d11c8c4b26e385af1a408b8b4db6625e68970bd7fe972d349997fbcef63e02f2544a7e701c95000aa5870494813d2ae911cb86d4708ab823c84c8991815b217d80973b8554078a3657774134c023558c47cedeaf583e4f331b9940df664a1ebbc1dcf40c49c8cf5b6bfe8f17fe9698f39cf098199de2404666201052c64554a9a4631a75f46e49286105402f258c5a89b99b48c5da32416f97ee6915a9bb49ec2398fafce2ea24f911beae4c303bce868da5dcd539cc478d26510a237a6b218b4724ff3ae408a926ab035112a63681c2b5b8e83a002475222fdad754b4e58ecfea1dde316010fd657fa174148f2bbcce4f5d51ff3483114349d502a6d560373b52c7b522946b3aab73ba484de49ee24f847f7fb09678991464acc3636cceb4dbdf38e7ffaaa197383007a54b13cfd81890ea1b1109f36e0b27df19150bcc3e4548b962666fafe81ea0905f5358e6b1e77cdf876ae559102deebceccb196c1b33a2dd6fcd4252d1dea20cc6e5abafdd1e1154efca02e92856aa200a64e5092ba9168479bfc05a0059725c14b74d2b44b4d63ddb1b23e29b9c486ae3ae088c453073f3364ffe4f61abacfe64c96eab1b6bc19cf97c353010212f450c07987d46ef1db9ce3a01755bc0fbc3a225c590b990e736152630a0109edde38b5bfe0b1d31977141c713328f53d9bebb1def9d0dde115a77382bedc9a4736f3c4c5b2dad6c49b5564589ddee2649a8666e7c6738a9277a02bb8707f2efdfc05f370ec1644c00984b9455ab9e94cecebaf13a8f2386d3cd4a5f7674ed350d6aa335a7e22485e289372b78b69441fe6877d6509a4a2e761712076e0fc9839cec7b2934a78dc339fe74185c7c5ca1302e1dd6d2f59f8daaaee00a3598e543a89b5a215adf1ee521a660ed4027f794c1fd62efae801de0eef4658fd192e57ae4d63bb37ba44ddea5a16d7c44850901c961c28287192809858faf5c86b11f7ca604c83164eb80cc10a9d02010f01684329e9ccb916aaf1b01962589c9920e09a85ba3c666fe4a0a3065d3661d61677719885fe1782d7ea1bf9df1e32efb4547aabd78b0c389e421dbbd5143f91977b9e73e731a6eb46a623d80300235df9ebc1ae60fb4b47a07a5b7228ca03e57e34d069c069916035166aa6b104db4bf7516bb328ea10ed0fe097d0794a67c9b21564d84b53891e028044f453e072aafa94e0e999ad231dd8eb6a819172824834ff66f8bf44d9d30b81f8ca09b6c58ffa4352da4f5cc00f923f9f7fad0a0b6050104d1a1527f031f3b4f2493a7c374b351d01ef97b6e37db22b553afe19f8294cf9e06f7e11970f06e398635803635c11684fd4e7b84c16a1913f7613d1cd9691504616fcae36af02bf1bb740da0cb623ec692309f9d18bf33ea8415d71c49f5034501236ab6b71d1d2d7fd1a9ea8df45f0472cb736a989cacb0df8726b6a7adb6564ac435f698f798514a81c9cb3e5dc8cc370f2ee321bd8a714af01b8534faf406e8faa5f27eea5be93cb48a0abe621a157292620a07c8c61c91a729a4b43a481dd23ba4f82d4630035472c4f83651d881dec8c0449a27c1c7b0f242708cfb51dcf6f6b100c48ce25b2513f6180e51edaeaa91c98a2f26cf7e79cee3d2a2c80bbedb34616a02633bc29900ca36231b4b1a91ab2657d52c4c2e0d6528d68a5fdc6b82cdebd46ccccc528d6f1f5cd4f59eb8f5531fb16e1d8b6b7f8d36a21b9deb325b0b2c061196ecf0e12897c8de5abc369bcf862550ab64e4a9b8485b763fd9cc9bd2df044cc2a3fc697b4a67e29d8353fca43deb7b433eec8cf15d26bdc92df3f71c3fbab99ef514d19dae024a801bc2ab1a7abfe904b5dc9a2616c84dbcd46493351f11f25a8c29e0056c0557a1724b5f3e418455bb05b9d8efd7275634ed4d9a0abcabc5bf2a3692b1aa8507f4a5ca72db1dbeda500a3ef028d8093aa713e209e5fd7c695675454a68ab0d5b1e5869d500db412562922f1c8474fd3428eaffbe179fb66b78b886f3e6590c914b13860b5ebe1016827007271bc6cae5bcdd729736af01edbb3fb0aa8ca4dd63a3acce03c05d367b0137e774f47320ed4853e4731137f00d9b387f11ab60a68c76627bf348ef3adcb21e4550dfe276c143a7a89706d9e4a06dc94b9e0ca7af62cb4ba9711b01b7a4149c43fcf30db18339e024466ca0af5e89150da5afa32c06c61835285e0fb0173092e02fc23c9b603ff4e0c29cf0f1eeac929e6ac7d8689e2ec80df3de9d83693b31163148f7c3f951699ef7a60899d5e0241fe67d43950ab4010c1cc8aab0df8e802a8f5a3e9ffd1a4965272b3d3a34f90cef57f00a813283e75934b77304e3963ae79e59aabc09b06ebdcfc26df12c125db093854583d27dc44c5a6e65efaca7c99e0ab384e4b193b5530caf50cbed0bab51c72ac8eaa7ed09365d860673b31610441ae999e088dcef8d69bf1932c3982fc8d52b056df5717df080e5cac7799f37c2680bab493b327d151afc5d43fa4e0ca3444fedbb11b2acb2218aa35c2275a710c4ca8319930a1b896ccac0862d918a6dedbc1cd39179fc4d6b5bd0d3c8054e1c422d19ccce50c947069c1a0d3f9569fc4452e9a18e899b568f8a0bac6cb2d118d8b8fd75363ffdd243aa72898eebbed1b5872ed3d4170d469f533542dbc5f5dd7882421951e8a802bc0bf9e4437d1292127205ae1c53e327ccf06efb5207c7fafed9273b01b13e1006e05cf629396b8129d0ec2e522110cda6c515d2df3a14ad33aad535fb1723bcd56d3cfeaa3d94581258321f48c9e1a081ff3716c55007fd56438c4c7e1912ea8150a08b78234833d9571e644462834cce9f10341f5563affa38511ac6ab10c481600a07b7ab2af29be3b30902e4a0966ae0b24e2c1ced9f4ef9cf40119909798293d73c80d9d850d5ea7a8943a88abb94470324013e06064cf31bfe44e816e86a987ca298b922a921e78c487c46d26367cb6e565b2c1cd77382e58b6af690e5ba7d8925eff6fea4e73ce92cbb1d11b82eab96b732bb19bb95bc61029f4c95a8c82b97373a328cdc3ba661407c22668fefd8830e862b666b4f0f3cd63933504985914b03f55bbfc143036f62797a79ce738a93dfffc34c83412d5041cfb739acb8c883bdf01cb6348d4e37f033bf8d82559f3eaf9189740536f01a4fa3c0205aaf19e893cd0eb45c7cb69b6793539592663a22371c9baf26ed0c36e8ad8766e67cbb0c64263c41a3aaeceb47d5d7b916e241c8930be4d2740ef51b450a44b5611a2afe304440dd61f6d44c0f9193bf0aaa437351ae0d8092117676a7083fb78a938094c0074bcf90f07b1d24550c3593b5f2e3646ff1c0e67277aadbe9975a0338792ed93573321bc7aee4cb7d0a60f05cfc770caa580b5f4817cbcd1df0bebc53dbf2303f9ce4160ac7891c1e97385b8fce8e9da57be1dfcab0be677682ff5f0c0e7b966eae835f6b22a9888b745bdede0c6c7c97c2915273cf305ed3584d948a0fa0380d78f0c90be144ba234979a73e53b2307a9c2a645181e4a5404c991be02d7cd6e8ff80bd8d2673d4276c6933d0f531c3305c5d8946ec18b14456390880b8892f1ef9d2343b8dbbf50284b75584a3fb087b853ba09f572958b680ffb849e19c744ae7e7772f85303a4c4310f3da60d5d1ccf3feec964762edd34f4bfcac776aa6da3e219c3e826331c3a8eeca0e82f31cf3de5b44a4a771eff91f4f636a145187af76738be4438cd4713be370e65e3f95b2a168159c5062d510537efebaec25f10f0b1c71205a59f03dca48f0f44b87cd3cab898075de0d99039a7972e158c5449d0cbbf71eb248da2bcf9e2b45155bf71950c882f8abb9fd504ecfb789c7108526089c3b6911d9199d19f7cdd16d5aa6a6b8bd5be8cf2bed03c6f0b2d038184a41372df8732b584ba623a3e4864bc1ddb7e3cca1ab20541de411f9612787b8bbda70963c1251f7a1a874f0684aacf56fbe2bd467891676098cff20eb46d043e00346861280a542b3df38b986979eb19906ce44bb2ba27ac141be0484f2f114066c03aeef3e4ed01f32f0753b231d43134659c743c164e10fb781513326226f4102dea169f6bc972730f502357b2ed038b437d6b997469aada906d8861b7548d5828d17c2e2dde106d8fd41d6fb339c90ec8c2d92b2d25485b58eb53227d7021cbe53c938b212abe0ceb4b0386d576dde95fc9b029fb2edbe78c2bb47eb41f4a03aa9ee45fb95f746ecc2e25d4990ffb72b53058be40d65c31616cf9d960dfeb90e33ec5fb7e7dbd85ceaebd26e5fdb2102d084bb6d06b41615f10e243f02cc351cbf122071bef8ef74566a093d67591848175af8fdf4a923b3342431733cb9f8c78a72d011c61c852d738de3b05043701024b48e9b7a3e19d31a3b57d142a76888a26d7b08ad61b9bb4488ca37ef4e908483c5a961326f769ef368539d5fbf154d89c38618ad798f06e2d578fbf021400539aa3c0a14c8c99ed51862220728d110922012655580f9437ad229f40b8137be5e223d04dd6340dc89fe53b856298f7bf1a0d55e34a03770aa65142731095f1b2763011c0b99f134640dac608b27eeb4ceea0ce2188167132323a75054df653cb992a596f6f1381a79f9f727b525efa4911c9f11d20d3373ebd760aa7dd9d78fdffec5e5f4d20e928fe16e109afd5a9435d8f814ab3c5367a3143178e079b14429194a7f528d8bda4e7100e7b469c03de74fe3d62fc4266a0a7261328941468aae162176ce5f891032d83c3af573eda3ad66f962e908012eba3a795d0ce9491a179c52073e414cd0432083c5a9359ac9c5057813cc32d3ed0ace714ea0498c7213ea95643278db2173a38083ef19725ab409287da76da06563ee049179ecf995f85cbc262a9a47daaf2129baccf8c2a3c2d8a36a24e1cf50bc7beea10e0e9192857807674677885b3acc29d822da40920f7cb26d31124b03980b66871f5dd4f7b0e886d22d0743f6c0a957ae2096028e0376d24545728ae06e141d047836405ef4341798cf9fc137c10c18edcdc63b0c392225d4f619e713a582a1644b932960982c57711ec93a305302908e02514a8a239cda8e01613455e9dfbcf7ef98c25f067aa8dcdc20c6e0b4bfbd2a2c47ba9754a223ce1e0dcee3fe8c2e26c911f0e41ef46adefe5d1d0303592402bbbd8f0502e4ff95a49865ee3413cc9f911ba72141ac6a134b8ff3ae27086e98634d6906146758c9f8c044e6dc16d53c40851298e02a5e2bf2b9be35a75232887923003bf7e63a5b424b208c9d06260e06793e11656e5b3dcb82c852ad13331e88f4b499e0bea169435860fc64e2d72910ae4089db869d74c141ff9190d700ca02ee653972bd047f5c34e7115af3543cae9dd78fce1460968902687a62187a98f6cf91d0a418d3cbd441cb384a97c6c427fb9669a3fca13966cf6896b4f46517a8fc9e426c29a0e04bebb2767e274b7175c8f935ba05757e601f880405a4bfd27e57741ff770ebce24ef77f38466d1f089692432bc0cd58ede15e62feaadf9c70060e42f295eae6c7897930e791eb4f892087ecb28791b753d200760f286c6e3125690fc100e1d2adabe88e271c261fc733f1a66fdfe629c8382ba7c03c7ec54aa560e0a03defce019f0f4fa650c4acfe7e4a5d769c3f9ff60c234e3347b78a732397061ca62c18f3250e6bcd49e626e4439a9112263e84b52c83024f8bcf7adf932701f264c90fe4356f10a9f31098efc035ea4df333c5f17431d7e0180c2f65f543667c1c733e2933ba8e30cfd15020cc3030a26eb9d57309c15a5c5b7d5ed106530e131e6bccce2b6f273338ddfafee955966e3684c1176ce552b6ab54ec4f377129eabe0bcb7c7194352513419cdefa2d4981867a3dc2707cf1fb782432372e381ff6d3742082112b9b4b27b0754095909bf0a95930a68d6a8e8fa5301554015500554067b7a8aa23fdbfea35036ac80ea4f0584dd6bb51b15ec2936ee2b2cfb570d5dd8b3ba830566b5fa7aaca2b39a1b0fb96dfeea0ceec598621189b6b576f40aa8d5de5b7baf1622fe409bf8e8736f3c65da6a9f2994881967ffc5d3e95e8c492458eb4976987665c76e662f6b2f7b4df7ac642492141448ca90f1b2d75a6b2d0a756fafcd326b7ffe5dfc7d3f5e3b76e52a549bc07eb96f8a65f54773576bfafabdaf4dc88ebe27e53d2baf3fb366458ff4d473e529377f3f6e56444fd1dfa608cbf850c3a11d739ba637ed1a76a74092051e9af9dc6fa250f79ba89dfd6aee72770631213b4a4d50ae6355662f7b3f9998413e3ef5046d86664dfd9933f4f7620efb95690ebbaefeaef9b0034bf5a70ac9cec7c7c7c7c7c76706d15b9ba8424c5c33c8c787deeb3763114e7be9295406284356015654fdd96107992449fda93f523e99a87b312691fe899a286a42e1affe7c55f4429e6447a94976f4ab3f9b6eec9a706ef75ca5552d7976a00d570fb710b4e1aadc90f3f1913388091feedff499414cd4b89cbb3e51b2cbd7a1ec2efd71d7bf6914386e977f137551d38422bb298e6cb8a18d1c2d67c92f276596af64d261b45c25a7dc25af9ccb2c6fc94082a4bce52a27651c180fb2f296bf641cd283b0bc45e7a45ce5a40c61e4e8ad64e86132e332f45c72f46c8e5b25af3ce52a79e529b9042b3ae52aaf31a79623e59a8ae570d05dadc66ace9bd3341c110a29a048e18518a048b1643bd9f597895d7f29b1e3be6240b2e1be8edd8c42c15c0f73d7efaaf530b77c024b5af2c012fd858255fef2967332c80c78fe9b281fd3a63fbd1cc322cb61ee92554eca2d4fc9a4afe494c36420a4a71c46c6e11ea4e5300eb51b168df3f2202a877192061224e530744eca57bed2236e7bab49007a29e732e92d19c65fb2cb5532cb6b4c961c3d970c3d1b3d181248f98ad6617911951c2945547e6bf505a202514ffa39d1bf08f49180d07ce044d85972b705ceb48e9e94c39fbde9ec2a79080d2f458b6045eb4c731990ec826c7b692ebb21b7d51cca09ca679a64d0a6f7020a26a67082551b2b311cb13756f666328968d3b2d1636ad6c82458b469594ccd197aecbab59b35d510a66d22fc8a46a0941c4f7386aa5cfd91aaa1965bacbfaea53bcb492b24ac82339d92af785d4040a79305514e825079ca6bfe71a3728f69362c87d1c7d3f50b746938ae5f9ac37c907037a29cc088c1b9d9d7aab91bf5f63e546d75fc81257a217b0896e865928b6f4d520ddd5b0da5be6a285563cac6d4a6a75bc0f109b4997b0808080868d39edd039ad386883fd0063b7d3cc5533cc513167dbb02b0e6449b0e624625042cd10f01511a44f5f01997feeec5557f2492d5df5f0ded199b9eda29a0487b6fd645d3e91630ac66e4fc988076367d3ded6a68ced01ea480220c48ea0996e8691970fd570d6dfaa8dd43cd9ecace6a0eca8a09d964105a601db49118d2d9c93fce7c4aa5d53d56cf7d15239d5508c391289440e10fc3cad25a29ad6865754fcae3e855af9048f330c68a524aa925a9bcee2545c71b66619a156cf54a2557aad17b528e674d8a9e1a6bd2a5b37f9995f2d14d8af6d1d46cd8ef47865d9ab3af9abbac5bb6a230bd0296558cd51570fcac2a57c9aa3a46ab48a58c1db4a1bf17cfd813c66a46a53b5892ef6049089f3e0e07ddf295aef40765dfb021133038b9d59cb594528a0406253627a53f76bc502401299d5c885a477ddc2e66e5eb178b5af6ebde5bf697961dcb6ebd925b867286026db8a13c00ddb5aad66ffd77a69bb3b5d65a6bbdf76a4d77b5d5fc07e6e3c57507ab5a87e8d5b23276ab4149e3167f5dd3dbad8c719be6a216a1901751ec6c4dcbd7f5ed35eb10bd7e4276b438eb40c843d41f0e90203a10f2506474fc1a13e786e83564ccd57186de280fa1e1417980799b79cc2d12bd861469ee868d2d73f15acc3217ef31fdd1cc4519638c52bb616fe9b034673d9eea787d805b8707e8e3eba3fe003dad6e8637794d9690685dcbbaf75a362c21f1d6a53db86e6130fe52467581e1b421133098b6003664f20528f6154202062f3636b3177aa8d1bba746ab86e37abd6c6a7453042580b051a244e1625b1be30aec66560f52cadb5b59bb51d94c5fbd20f5bf7acc66eb424a2776fd62b7d9aab799c3ae6bce7bac667bcc4220ec2bb4b4cd9c8491833b6a372a10571731b70e79ae7e434b5227dcd73933843c62ac87b7740f56afcfaaaaae6a0bcee22c8d83de6336d743fa9b635481eda10dcbfa98fb46ccc989f534f75cbfb9073b9c3598eeae97185560b8e90fe40b363d13287430119491d69c37c6b905a157077fd861d6fa757b870edeee76d569aa2ac6aeb66db1fb98d8e3766b375d7dcc0fcbb11ddb32ce76abbfaa7dcc2d6d6896b5dbb669204a6e46171d47c94d1d6991c6d95e6bcea445b7fbeb77fe9b8fffacb58af731f7e4748f88ab3ddba5a6dd9ca94eaa2aaa6b5555d583e8341f228f08b30feb5c3d1870fc175fc12dfa36b51b913dfd66abd570c0d79038e8ad69511cda0f21dbced72c94b2028c524a29d57ee5efeeeb5acedf0f1df53ee6572fb58b8e651c21970e1f19629717691ced97fe300dedc51bbbbe652334bccd080d4fdbb40813691c0dfecaf237c37d33dc9ad5dc8d69c939051796c04435574a6bf7c3fc3aaebaf4c17e6aaed2300aac63daeb4e76520664b28527b6fc8dfce1b45a2b69855353ab93f2dda69eb4babb1ddc90b3ffba6d350efac30eb293b65a4bef63ee6ae3ae66cfd540ee0ff14a5c46e665fca951487634343443324a947422812450cf269dc80e68d66c5a02d1d0dc48a5c03cb3867a53c620a08d4c62cb3d4f336b24cf49140bf85e8cab80e1fe2ca03d63c86ec62ad6fb98f16a1ad9cd68750c2ee07af818b29be7a8eed1deb39d66d66cfa6292b55dbbd9b4bda65f59ebf5305737fb989bd21b567396aee7e0fcbd189348ffba38244fd0660650c6957247be2062216bf08c43524675644fa903a10c0c2291f2270ac9aea2b30bda899a0f735fee4621d9cd47142ccdefc86efaec2903c28901d8cd1c373208589aaf32cdac89e18434a702f6fcac1162cecca3ee109ef3940a1cf7471f77049a35347366fe624c7afcd9d3669a86d2c426a41cba648acf4f1676f61787648e94411f535930491dfa0c8b16fdadf5d7abae4054bb51ed0e7655657da40c025135a7f294575cbc32a7a271d09d825579478e1fc7df2eb31a927e0163213bfa1e969e96b7e81f73863e3b366bb4dfbb4377b326f2e0ee07a0888a3cb366455ffabb2afac3dba6e88fb4adae78e817b00edd48bbd7bdee6874698aa63c107fecb55d5be6c15537f7c08c3ef24276f5a94e0beca36eeddf7d722fc6b0d11d613c225701c9ae621845cd1a9b4a9166f5a92a95b5eb68958215959a4f48ccce1136e9a842919d95158a4976f57502c98aaa2aa96fcaa6bedb1d58aaffee93542a954ad1cc1834bb5ecaeddab6eb7afdfde898fd4dc269acb2ae532f64b75527d9d514ce150f2cdd2416e83eb9494649aeadf44db2dd78ab3bc2acc4ec29654f2999c9cfa62cca3ab140a99b64df24b3867af577497691a85e4fab4f129b52209b82f9cb0dcc8b9695957256d87d427648eccde669965cec7aab45223b49ed0f2c559fea537daacf2b9f5d6f29d02948ce2ae8ab28496765e5317cd26e543675929dca312a68c76b28a0d3bebebd0ca67d5d43a536f60d9564e7b02113540ecae7c915aabf41778a5da1d8f5e5658a6e065e0e23446ed6251ba1e1b91cc6080d0f888b86d1384094dc70877139104ebbe09798b96c364623cdddb8aef24be5f65a6b7d6457b50dc7b961b729ccbae82cffee93598f2f09e840f5f74917b0b6f2d108e31ba3510d89c1cb4a093f33766a4729fa13b247bf328f3c351ba3c33829a518768f240abc1d7a181d8db01136ca5f8523bec2b0975779ead0e163e294e39691cbb42e76e2cf9c7239f2420a8a21d99279e00c9f92b50eedf11a8e782d5a1f8b644e7b9a23eb6391c8aedad316587416fdade8afd2e72427b7d3bcdd9ea48c89c4fa4819394615b83ab4412b6bb3487a5e3e730fcced69d6c0e8fb045770d3a11dbbe886a68b881bc370166448f45e0e7353f45e8ad0f05e6e8a1e4c8db9637f221e731e1b652fa2d3acb240a389adedba4155923baa3e179330adaffd0164c67c8c2c5ef40f73a6c6400386def45e0ea3e19ca9d302592716283567eaefc5d6962f16f8ab92dc27a97a31e9b69a767d35592774565535a7ee799174565900a5eeacae8a92fabb17e3a93f12a9a282b834506f59236c34c2b0db5f417784d98b9e6e8645244c4f348b21ae85286a054eb995795e08cdc6ac1f14b231af666392e6615c6a52e6e6bffba4cadcbc7d45d2dc3cdd302e61dc27445098efc2cc976c03e986bfdc44c3b3e19c0bc65172c3fde538d033d1f05c7e832e123d1b34cecb395d247a2e1ac264e8510993a1f7422f690f0c8cb6402dfabb2cda5a15fd7d03018db00b3567eaef7731c9daaa208a84fac8ee243bfa0945a2e80f2cd11d0b64514fee9015f8b3a9af4a629d5894ec2c90eceaef134f64b7316c0896eacb6b487bbad6daa92d904c722d9e95cfb5f046ba7e7d681af0674fbbfe3ea9bf92ea6edbd4ac99d7d2dfc5bf36354d5fb73f9bdaf53425a1902919e4844e1d5140553673761c82252b70f5429e52afee81194db21b42767594e51e8d606450f361fb1de52feed1310cc2689c97db5b5a0fd531d1a196e3e51897a1f7925d8ec3b9681ca8bf0ad25b9755fef04bc65172f3a2e406e62f9f33f52f1a0746e328b981b90dc7c15172f3729843cf068d03a3715e5eb94cafb2b80c3d320ced984d33dc2f197a51f321eeeb3a46cfe1a26bc8aae1881fbdc6b4197e94e1c69a28e72db7c848e5dc240f2cd55de9b8034b32890f3d5d81bf789a26940cdab5d6d3215c3f736450268364900cba32c8fa259fc42d936417c3eee8f7d23efa75c9934a5923edd72d0d88ebd9e3101010101086ddf8134ff75691e8c6a6bfedf8d8e30eb4e1b8fa2166cd7d7271ad8f995239d2f789ec6a7587f47d4276f537b4ebd88e8936cdddd08e65fa03b7fe0e55796357c5d91b95b454643ce20bd8bd1893483ffcec9833f2f752716467d766961dcbf28eec437652a39a0d4dd3ae69dad47c70da3f3884658ed34e37fe8677e450b94a36b17c45537991966b2a197a2d59c97696ac84078dfc69e7f077e8d0b4286722e8fee0907643bb8e3e6c6c1adb214fa7d3b6690ebffe07d949edd0db8e65b76d7a45086fdf32cde12dfa6c190258cc76dc3b147fa871af99e90fa2b6ca658a95fd98fe4818c37852c970ce3c66526a9ba6e10d6b4ebb48f7ac603a2239cd9a155d434aa987b6dcf1e36724b7175ceb5a5146a9a3badcd2decab2aaca56f656d60cec6abd1159af341bf5566b69bdf66af49895458d56dfcc969b12ae8023ddb40cdb327d5994ab959cf33aaa6301cb8ee65b2b3f65ede69c73ce39ab7b3becd7ed0d76557a6bb5b6b2d2b24194f69bb66971661db4d171289a35233d03d348ef2591b07f336650ed66cbae5999b5aaec63e2b899b3acd78a6a5575c9ac0913de0afcb84376f337dfc86e524b297c62e9e9f6ab45fac79c992791b6ae4b492a638c514a29a515f8ebf68f27f007534feec538ea1c73ce099f88ac6fd7b296b59fd984d10afc759dd4328c5e017ff049eac725f56ce76467e9ef5e4ce9b108699c5ba5238fa686317821062fac106da22dc62a4a2be8135846d16605d60515ced0b93819c3129d087b8c31c618a3fc84104248c980e92b2ca59452caecca7e6597bcae4bce39e79c7366bfb22bcbe6e5a7756ea329a3143d92d6a594f6dc684be1467a9b96d394524a29a534464ab36b5a1633ddc15285dd7d3776e70cf60ac3b6675bb63d3bb755d831ad8bbb833658cb5dbd833622cd5d97bbe69edb24a594721b166d58d35dd56f9aca0e0a898a20b4ad6318bc2085198410c24bd34c57fae8132316af48638cb15e19fcbd97e6aee883699abb4658b4dd4aef28eba0971b422cdaae8e3e97cc22d528a5f4906a2669920253cf925492e46396496467bde6ec57765d5aa669eeba9626d959bfb6577d4476d67b56dec372699a352c7a8445f85ff461f9cad46e58b6af5c36b69b2f91e8dca5dcbdae28046d745c972bdaa7d64aa310b4f1b9a64cb48934b7fdd210962ea86bedae2f602c73d885fdbab00ed3164e6b0fadad21a5e9624c6bd9b1532eab34173f2f7b989df68c84e10b5eddaee085e1f8e8336b562eca22bc54748a1ee98f84f5a6e1ac28b5feedb0e967acd37778ce58a7940e3167acdfef624d7f38d3d6b1eb169cd1753f8984311d72ff18df2b97c09205846d7d8859234d73c6fabd185b264bfa5014ee6009fe93a66d5dfacc9a28147de68c752be729d21046499f7b312691be5ba76ac0f1d0929894a99d152b382308d951934c3265904c9992c8a8ae801dad864cb0e1859835f4a2f01009a6363ced68eaba9f44c23875ef0cfc43023ac17812921dad86a40c3a24bb2ba00c7a05ec68dda9864eb386a6e815f5f4743ae9a8a8fd934818df8b8225fa9abf5a864d4f53a9542ab5e90e10cf2e5540b3a69e2a1e9f2f2125c13c154fc553f15457d0612ff7477f9ec05f3c65d590dc8915aa72520155403d2a980e461c91441cc3175a7c51450f3028236945d305923dda9009174b6ca851013be0874340ca985aa693dd6b38e0872f82be7862fb40617f344ac0d68f10be38c21230b621932f7e80ab26f0002f910320f028518321c4e0072c38c2111e7c6164075f9420076b60426ac9154ea842076578c2143d000312b8d0040b3e7882832818c10944c015932470c544133e415822c99222289184244c24a1848709929ed3ae7ed6f082fd7d3470ceb9869d3de79c0e80f089858435e4cc3fc14e4420e90423f697f78d09804d18314fafe198afaa4f1d1fe3c660dd202cc5cbd4bdf7de7ba961092f76ba78a20b2773ca39b718751505ed06bd0d0b966a7583d2dbfb6a891468689cd000c90055404040342a48c001155f3481841d308881658a7c55810b23e0200c4260f8c213522001230726bc30221405118c208430e01baa607204164888c1132aa2980f6802062a6431841b6cc1074271093338f92491451315f08041cb60a17e200c32e04292296e40862f7c81a782c111575081123c39e0818f179018dd3861e40c4610c30e7e10032878381de480480c524a2083145ae882c6851cb0c063e1138828d1f3852730a18228b690054106378822083a3c98422a08091b8e78826405413b489e98420b487c0728bcd00523b6f8c1132fc8420c16ac2770e840144cc4c008330445b9c1155a60b68084284e40848b256ab0020f4c0d967082d38e125cc8c112aa8084a80535d002111748d104912c3c41826e4c8c50049e9e2a6cc1870a37e0d04511d0a08525967003294c2105fefc20b18209162831540429bc2ce00a63f0b1424808573471e272052bac4085263840411986100524ee01b830d540075218b1c1129c90354988e0831f90210a1f04416185460ba84051f18411ca8003201e9290223f47fc4028e7082ba878021258b58012281081c7065c28018913a0c053abad55adb5d6ea04ae15a276bc7802055468f1059326400109242a1b543690490da54c37e022874a1e2e7468e4e1c2542b1e2e8ee04205f5d2005f1b32d9e28b196ca1c516555c99ff32dd165ad8c209244e37a834a8b668c1dde204778b125c2dbab85a6841613664a20517b478f2846422d2c2c7aa56c00212c905a4aa05352a19e00fef386b62ac6250d9aaaa5690050fb2d001165a24c9e2e794c569dbc9426765676865078b275b6302290058e0411648342a50810a68b040820e1642b83a58f0c0d2c12b242c90c42b8ea421890a05151629a84c5865432657745119b9820a960d995c81852b9c604224328109d6e600e2152630c10281c510aa60841954c0459326964077606da583ad8d4c60828d4d8670656107d914ae40b2adb5b61a52f5542590a94a4013854691b1020b2b8eac210d48cc341c4173d2408359e58836649206146cbb219334e854465ca8d9a821a95529ad5476908487bcd8d5e9c528a593524a6f5ff5ca29476ffdd2d36d5f6d8eeda6eca2bbbd8876d1a6a43ecbf2f49c3dd64135ee81de62d7be5ecdd91a05a687de3763cf5aa58caa8a247cdce159732fc6959db2883a2f233028f3cd9f3d9fe3743a9de4cfa9b2eabdb556b5b2eabd17eb32cddf0f1ddee52231ef84e8741862d398eb7093cc3bdddd24731d74bd325949fdede663f2101a1e901c0ebd4e4e1f24b29b3c684c93ece634cd2372684e1f12dd4176d8d5c4cf4f133f5597e922eac658beb2ab2779a2a77a4d124aada8aa56a87ab584829a35323a888bbad08042c3112ff6f50bc596a740680842039221d3de3664828614bcf52cd35fb7a1ec89f9cd367f3bc43c26cf9d6b21ed02c339334f9666e3da4a33cc341bd7a222949cd9b242ba5d762d2b5372c80b89454c4c8673467b968198cbc8681c2192bb216f5d6ad98b4b978dd0f0bacb18a1e101e9b44c8c06a2e44687cb7407a283eeb2c764e8b99cbba13fecd73be892b9c8498dded257c4b018296334aca49523e6584cd641bb8cc6d15bb631b9bb921b99c7c4681c199977319739f46aa77164344e4c8c761d74f008b2adef4c8718eb2e9947e4e8bf08241f84f52dd38892ddbc8efbebda8dfb4bcf6b4076b30a285bfe5efb2f0ec99e18193d4f325697a3362d02cd733036dc80ba246ad6c893fe968fc3afcf1b6c70913e525a526afdddac3f7c793a0efab4ddc8e8188e47dc56661d51fba08f5ac7bd3c17bb799ab9287764372f654fcc7b6422d0bd417fd706fde1ece274ec0203e091e78af088f4e2f271f56a37e8e5f78f94d144246916189aa545b3e4c89979162de4e32e4b2682ee954c049dc7b20e9a65f1be86fc8123c4d480ca4cd1a7592387e6cc9c3c3367cfa3a0cc23b29b7f89510596b7b7caf75093f7f76a3820862dec5e6b22b97494414d04694656d59c8cd5add6d65ad5ea563bdab4ebe7475a53bb5b077dd41c00f2254f2363c88e46522f30d63d396811c44091325031d9d26c58328fb9918999d66b45abdbdb6cc68650a43f12d49a9652fecc9a1a254aa3e804a2571d88047ff08c9f79fa993e13c93ccdd344324ff3344f529e24cf9542b28918e54f8d55fec89cc6bcd2b209f9336b64b44491244afec4e89f3de769cfcfd3aca19edc53a22c9c3f970da002833219247d9fa771875a3b5b6dd7a351f9a87790ddd43496ba8a24b6cb864caaf8d93f8068d56364a1497346d2d71a33ab34877c0d595992a2a0cad93b6738c3926d5f67cea6bf1d4d8e207ee4c88ee60a76bbfa84cf5f574d1d75a2508eece8ac73d699ab7b49742477e449f22481e7ae945bf5a6fca668eeba9ab3b880b16bbf9eb1a47ce531257f3fb666e57a5d5ccd32770f6f6b54b9d77a8d592bc4c7a266c945a8c702b5ecb2a65c62ad2b45df1a81aa7b4c0af0586e75341d678bbebd86c4d92c587f42f6a6f1a5e8524a11e47050953a5251812a106a186b14e2a28e532c59b26489f594c7a768bbc4ca3763514769127d1ee8b01c3520a2ca8d425128a2b2dc9b96f988b4cdc72788091f1f26644747d867f38931eacf03ddae8f3ab2a3a62a70a539d1b464d6c49d14aebef2f98f66c725f1057127eec417ec9076e20e95578e9481377dd491324c2b39084ef456bef22038d15b79109c20deca71a2c7a283b09c45e308f156aa9c0d83368de77e680006c1200d04b104062d81414b826050c511758cd3cc1a1a1a5392282484420dc5548e3461d2477654066db944765426a1d6d26baf491e119a33f4f70a0909090951fa6bfffd1eca4e2e914bc88e7e1405ae879eb59a0a617baa3f22e2aeba88baaf279132201342b3e65e8c492421edc3ee6a098de88afa93419b3ee6a0d6d2144bc7a099352d5a2e21d14bd3912bc65787de77f5ebab33e44081028a930d37dc59ee6131994c264a973c911d954173865ee8a2f0d0a634b5e9e5920854e77a58e89239437faf5c72a9a5e5921c43a2c7cd2904cb032051ab431c74437b688653b78310317eb8a99fc1848ad4aeb4cec898ec6fa5ea00b56d0e82367611d0c6ae99c167636648b2b18ba030512b144bec0a056ad75a4fb7f6df1f3f443bb6fd356f6b4c8d07cec6da8da9cdcb860f249a2684251ba1e1e90c69b9ca87e8b4682242bc96ab9c0587e540545af4eded2fcdc6559d85c3aeebfaa0f631b7d59c3dfc2fcd59284a9821b5a3064475abb92b6a36a76425561371c37218bf51794bca55cec261578b80e85673184bcb615c96afe0c06861b9251d7a157aa41cc4e62f3b84f873b254a263af5a047256c938384a6e58de7292ca611cce1a181aa745e3b0a8dcae5c765be1f095b9d1a30aa6b91bd8ab9116017c43a4bfeada31cd6d974e813b0aacc34229e38ac065690ebbb48f28bb7b592bcf1983a6db010e6552e30283329f95b3eb291045c9510669c15aa19a02fd698204d504494e2d528d60bb6193238cb0f11068c46f72c40fea2fd3e1b061932376f6b66193234ab0bfd31ca925688224755523ea10970d9bd06068d74068e30d9bd4606857c7d0467e9e88d4573d11219ecc337355e7d0a980108f089d5947bbe832eb5819478817354ebcd41c119c04f0e53390f2794e02dbe56fb6cbed55df609f1a671e3bd43809e06fcf00d69b06825334c4e23fa395ddcad18a315a8fbfb11ead1835901a34f11f8da573fc5807b2e15b17651c211e8e929bd1487355f491b5dd7a06f04796657da471e62da87112d89e3d03dbe819c05a883953892ed2393167ce548f8186eb4044cf3ed240b65f59c651829f69aee25ce73c2de368e7aa26829380e8d733b01d9f9340f6eb37d92f8da3a41e473e7bd537d7b1c699bf1ec4132201d1b3df8874a6810491d79efd66d37167ce549ace91cf0ebdf92feee490c7b21c9fe42894238f50d6893f57d6f9e1c1fec52556f5185b60ab79c0285b473dfce2935d3d461a6625a7e6eae781cc434f041480b386cc80442242bc7b2b47cfca40e42376a17601219e111a9e9cf202aaaea4bee62270d89f90ddc34d007af138f0c713011121de7611444d448897b7478d02d093d89661dea896430703d09bdecfa61a05a0378408fda6712e4dc289b76e455df1a85e896025554463bce2f6789a1a5b8c3922147a22b5633f521ba1e1c5c77ff04976e85d15f62b67a7f93a90ebd0ab596e2be3440fbb968dd0f02800bdf9f9ebf3d0135284a1277272b2f4e6aff94b87887523343c7922963642c39b971a6708173d219e111ade1022f2d68908f188d05b9f1a27ee54374243fed238910787ea5bddd2384a6eac4b8db363ce54b7f43cfdb533b5111a9e67fd8b3c70681e7a968e4b620ef42a111c3d68050f8ef85912af4b2e913b9247eec81db944f200c151621d7a5706821d7a58c4b48e92fa2251c744c3bb2f123deb32673745ef7acc5cd51588de75f9ecd238113567aacbf822d1ab303d2416895ed41168ce005df1d08bff62d0ae6a6ca8e5d021123dd98406506c016c0c484fea2a3567a40996aa4a247ad0fb626a5716889ed4384a6eb0478da3538486276f8a5e3ca687442fde143df92bdf5b1947c9cdf5ab71a277dd82de6565407af55cd539e473c4d79044a207bd18e9e7af1775e4e3e51532e576a4e6c8d49c5b23e00f6fd9e398d65aeb57aeb5f63e48db9e243b79efbd0fd2be1ac20e1e064a29040cca7c7767c393640d6e328326366c328325f6774d4d74b8d850a34864a705fea869d3d324b3461ae151934e09f6773735491947a48c797aba2365c026337882be60d3575be08f26d9940049ee0e11a13d3f0425766943264b28b131296395c558a3b5b5a2536ea83f2979fcd8f635240f6bc37e1e6eb821dcd006c21fd0865e7c6bacebe6cc9979edde0c638c44ba7fcefd62ce0f68236f83fd412e20171bc2d48c29580a0016669259b3a33c913370d70791848eea8668f6374f3b4aea8997932367e0fe66d04c624b1974c3ff788d960116032e0698ee5d98e6d7b639ead44f4d6a8abf152843b43f12fc0f6843a768d34326517e36d47c10edaa66bbe7bee6a8638a2f9033f35636ddbb3a1e913313899c995762cf272175cc893ab233cd99f9b8137364373f852aa9f960a2672b30327b0751f6a04e66c6078302a005fc20660dfe0cd1f6ee8241586b605ece64a4231f69cf8f4493c2f415a60962ce4cb942a2f4da510353cc9822ee96455c1b9e8350c6558130a00c1feae611a3649ff7320ba1010e0ce1811db2f381451d3c6dc40caf10e1daf2904914226c9aaf59054c33698820a060791e2b2d25363d60dc1e3e429b1ea46f8797d0a687cbaf1e2fb70eb9633d609e1dbe429b1e365c3bbc85363d6eb8e81087639b1effe8f017b4e9919f62d3435fe530e62b87d7a04d0f99b31ce6f09643112cc513407ff0d573c81b2cc5cb640d96e26372064bf13a63b0149ff3054bf1c7215f588abf215b588ab72157588a87c9152cc5732f79c252bc4b26651859fba0fb8b169615959411166919665d9b8dfa87cb5fcfc29c45187df8f6995fff62008efd930278f66fee70ed1f2d5df4af1ac0f1bf4a808ffe599aa7fcbb3c5ce59f5580affcbb0c70967f588cb7fccb0e7018ff341927fddb6aeef24f34e32fff3002cefd1bd91ce65f4a026ec33f1505fc867f2b0b380eff5818f0ff6ba1f1fc0f468deb7fa4fd987f2ede65febd34e039fce31c7000fc83b1711d7af4f0eedf0d1bd2c061c3fb907bb861c7dbb061c73b20bf01dc8ef75e76fcae4183012d3b7e012c3b5e010950d9f136293b1e01336a443b5ec6b6e30fa0edf81806c0767c01ae1dcf83b5e36932842502e41d3f80bce34b19c2d20e19c292007200663284a52e330096e475c80b802579006405c0927c0e3901b0242f936d60493e46e719b0249f730d2cc93fcb8025791cf2016049fe861c0396e46dc806c8058025792ef3004bf22f9926130096e4497900b0240f2397f20eb024cf9205004bf22b3900790696e453f228eb9001004bf2a29c032cc96b592626eb9c0f4bf20680d9f0acf9e1fe90b79cd12b6568dbb6dd07696ffabb2eafe54b6527657c725f1a578fb9deca45643c620f52d322058564cde78cbc8c33b5a38e71059e42fbf60898884e110478d9758a30c0cb3e44484e8e8e8986a7bd48f474748a5caf5ea708f6ec758a90bc7b9d22f7aceb14e9bceb3a982e32c3db5e84c6c35e5d66da64bd4d1ff2205f247a9ad6d1d131cdf0b20f995fe478a6cecbae33c47a9193e399485ef621a2d4f5b25f2f237959e7652f32c3cb743767e4e5b19c210ff257d69007792bc7401ee46f96813cc8db9c03e441be6600401ee4abac03e441de00125f42a69c44c0228ccfcf2aa57ccbe7abaaf2790babaa7acae7adf9eb98a5419b1e45dc7d6dd0a64711788ba04d8f2248fbd2441071afeb3e48f310daf4a8ba08d29677e9017511a45d9de5f317daf4b07411a4f90cdaf4b8ba0852862f2d61bce7754c8d8f4453d4a94e1fc4acd1d1d11982c310e6bd7c080d21ce7bb9ce1018a58800bc97174980f7f221337272723c53e5bd6825f37446c11f1ce2b16357af978768472982ee98355348cec4a74c433b4ed4d015f8eef0f1fb1cf2e3b143e6aa0bfcc1211c29e323fd90328aa03b7c5cbc7fbf1d76641eb0440f2313014bf42db9072cd1b3641fb044bf9241004bf42ab90858a24fc9218025fa51fe014bf4388b0096e8457904b044bf6512c012bd9671600996e8b11c0496e86d16024bf42498cbc81abc8301f0079fe4b809420352c6bd1014a22ea4b093bf19c2ce3ac539e302cfc31d7b1e5f7c5b240684c3666117f84ac4025323e9b6481b545628a7a5d81baef9328231e506324805fb831c1efc20d91e45061576228a0be87522cacea44e444141259d88a263e30afe620b2b0ba418ab29d4da85125a85428e0e9a40a16428a2eca8140dd655414b99522300000000000315000030140e07c6c3e1709ea7a2f60114800b80a650745c9708c32089511432c610638801000000c010100021aaaa00001fd0e53dba07f6d3f22eb2502636eefd13fd6939ba98f8def398e8a77d4549a2088fc86281c639610bf21630642883034ccc1cba1622daebb741766f1a6e253feadafd0c0ea4ac8292fff36bc232071840b4148d2d0e3870012d8d0b33ef549142311c28e95cb89df50d922531164f72734dbfb70a7d1dc29b483f63836698da9f172e4ca96122ad9cb830fdc2fd1cc34f4d8e23525180c632c7ce7a0763e78e8a9ed08f2a32503888bf092f47761d893fdefd34b4a0e166b05778e9f724e4ffd0f9b9ba67af4843b149da9a4629cb36abaeac27b961b86b2a9aa59e5196616af0645b85af598fe476c9a7ec0436d3da3c30a69cbf567b04d6f60a4377fe3acb48dc6b9a9edde6e80d3d25773d116be8d2ff0ce4ae282055993411d8a8caf0c2fd0f92ae308c16f74cc08c598275b3b6be30a62656ed2b8e70736235fba9b66324c3537e41d842d7eb80c4cbe972b9e8f84f78e86060003d3b209bb3309cb57f3f9e7f46f8da7c10690bf7fa7f071766aab433e8a7a1a5c42b4b8f0cdc4bcb84c0793bef88a86a2fbcd0f238863afed021a351b8933c7b91bc8281d572cb9d88518b137400872647dcf3edd6ac94c59e0b4443c9fac1aa486d1dd6439404e0439abed91f53793ea81a0c6f4c5a3a338ba224a4186be5a2b98ff6c164a8d2e7d6098b9d5e3df6890974c8eb01d0dbe7822e1bf6c244d9d6d2d64e8efe443fde1f245c4d0e92f142875336d693c487763595367b3ac7458c0821c8c878e299897aeec9f4b08810e4cd5509fd385920579c0b9ecbc73360484d3382883fce768b90ff022dcada0dc5ebf782cad4bca7d017d270d3093393bdebe9baaf7eab9419ffd5efb0cb8a76fca60fcd2d213cb8a564fae2340a41e215c9c4569800b302036bbce61e1c0003378ff86f7aa13958a5b45300117092e2366cdb728a98aac53f38785ff10d847c62d58d5c3a5283101b64c198ccf5af280c16dcc4b241454a730b2b803e429bf8cc1d41f2ba28baf9e666a9a8a9254ba9919900bb09b2ca11554220761893e88f69974cd195a744dc7010ff8de4fe00bdbf4a0259136ae22893cc4b9d1353da41eed5e142fb3bb5157d9984fd7521d474557ada2726544820a18f326cab86d94c7b29d2e3f646b14633db0cee8aff8fc08e04b95f0195d85a8ec87f5f20c12976a8d862be3c4f61f269ad363d7b12a6c1d7ed1e6e75cc20a7db92bde3a0ae3d2249dff951366d5035dac1d8ddb04f33a66c73b0ac48f41a6651bdc1f06c9aa8eefebe4d780514871f3e51d6f412a812d421ec9f465a64b26470c913a118ede82584711d1dbeac2aa1e8b0e8681a4581dff25f56227105db0c52037abe36cd41a57338a53a95037845aed01dccbdfc611c77e9604c1e978f3ee16894b13e0f7b68c43e4fd51169a250b67040dca9503563fd56c7a9c028fffff5d2f4950648d38354d5ef25f02d2af9d99b22902c1cc0f142fef3e17dfe9de70ad341551143196383b680ec129c641a834d31fa36e531ca40ec14472a39b0b87fe8977462f46a242d7b2f23edb810975ca06c04d54bdb5511c14746d996e7ef9f00971f5cd28b859de33b65a90cc49631ea1735dbc193fca2755b4eb94286d1792eea443725474ab25018637c57a9651fec893308114bf543b11d1925f729990a8560aee469cacaca5b3703ddd4b0488c0dbea09aec8b5a310419c89795323bcc174160312706128bab60e4c6649a5e66a3ac8674aec7d49187471ddba2face0595f58a22501a07f5b6ca6c9f11143465f62caab4566e2e417cc25d2b8dcde56ec8076d9985b143ac31eb5e493362bf6b4ebe778c40554b04e2fab08ef46684823fd60dae036970e00d980807adebf25b8adde46c6ee9b28060a95a79f6d92ed526add08f4a21e2a5ed8c5f3ad9fb6613b00718b460a767055de0a28f5e50891a1a56980e67df6af38f28e1525bd097c4b6b24b81932eefb8be799642098aa21b303dc38cd1fc9f1d8c4adddb9d4d1528cb61d8e87518e41e1840237ff185b2bc383e9f89d25a9465e5e40d8342f92e5b13977fe4977e96997973f4a74b3d7f3a7a337fba90f2a73984e14cada4fb030da5bf024ad49f50456187fcc8932d59eb59b7795c40a14b3e862d5967bfc680088697a287323ef35b1bddefb088651e687e942f205ade8d1c8f0f37c197d58d2e3e3628faff7194618aba2f650a824fcb85614f552cbc9a771695e9ee8faacd88ff2c1a133be5d20fcfd9bbbecc36f368d19f2a8b8bce2c5fcb4d0b327122d9cf359c86767e00d7adb1d5e09276802323850b76e94e7b76d39ee08cc15ac35605376dd934077701e099e46f8e41f58af2a65545e23b488c0ce3d85e43c2924896b69a9ca22e384c307c4a389764dc394a5f71af4c4321ed90e1a592b670d2737ffcc4f48877a4595011ec1d1c4a969ab0dc766816301f7fc66b51a486ac9566008b21b1fb8a7544a9524e78e8c7ea5831807e595c3df7388f7f1f55dfb6017dcf50081c4d98abb0f2a7d9d84f0011a8a825e366b7e76f872b76d735df5aa2b1f80d870598d5e599226793ea94186e33113239d1465d36b16068f7597e27e82d4d1a548345632e42385a93488dfabfb1d0fbe8743ce68463f45cb29d6b2fdca4c50c372b4c5aa52dc326fd1c212ed49c906ca216bee3994fbc59cff76a968105371289a6997e5c4a96fdb922da08b0a85b854cf4d51123ed3b8d71bafc46cb96930c4869328370848e4f002f4bafaa17d235ceb866d012ebbe1a0061288e0d1cfa7b9857b0a3154b7a3a21e00e9fbc7fbea9a4bd2927a614794b417ee2cd0e6b76c820edbae64e1ae08ed5e5a180debb9a1e1864bbacd1a903b7a3753c2c20f74605dd5e7d9e76ca4b3022ed88c827144e45452d5d7cd239932c2d3b6267d47af2946c0a710dd2e6acf54555f096970b089b36ab37a29507c408c8a20a2e931d4c18a07a62fc8c88b1fdc4a42297ce778b88488423b843c834be3b4a8da3db685fdf8ff2b4f5ad78acfb7575c4f67be418ed833732e88b868434af11bf866510eba4d27e2d51c15297f60d289a6b1b7ef743ba990a632a2d0863329ce0154475a2c54698f001c046d6c61975e456235ad1605acafcd30287987ce6555a962bb004870a04193d26244c8a08782c8220a3c4996846e70288261ef1d447dd44319c7949090e937e206d2be242bbd0f858159530c4952d3b19fa480d3acb429176516494cfa634ad2903a55e6bd99c7333ad8e49e5d32e777287188e657efbd191907ebebc41c69921ce2344c820c1d960ef5a91fb1de0a82ac22dc66cbdbc8c296e47e5aaa69659c2ea68c12a864646cc0f922e1b9f065b4492689db95c29cff39b7db05d4c2009893710895d80f722be205bc499cdf34a4623d58722815673bffe1e51aef9a5b7a9ecdc549b8aa0ddf6a7b2b585db3224151a1c953de7abccd1cb08527463b529aff0b1ca106633977efd3a12f7fcdffb3390eecadd1ff04c688ad8d436a1e45f91179d151bfaa7404370474b79f2d54abd69a08c2e6a1db5a360fb47797f5491b7f868eadae870711568de6bad35cfc40bf7ba440b80155a4755a29fef549490db1d65fb6591876138a4a630a949ccfbd06ce422f0a9733de6d8ad0703903e8fd3f8ca0902ce0b07791986287f5fa73e5e091bf79b8a4239cf7d4c2bb81403c6110be46f5eb52bec593f2ba3cd66603ce0cb1047e394eac90d14dc181d30939e86ac0a974a712cf112a324c151dc643e14f4eab68d0e5ee333a1a3a01bbf6e3660ebce88c529be0b41720565a38ca6aa942d98603a64bad36e46b8635adca738f402d71656ee38be89d7d22d0f9dfddbb111c5632533fcabd5856e1c3db01341bc8d8c2bcbc571e5dc2c178e8b7361b938ee9c8be5c671e75c2c778e9b7361b9386e9c9be5ca7171ae2c37c7857367b9386e9c9be5c6819b31ff889167907ac29a162ef5dffb8186a279f8896f8c9fbed00785d70e7611c79f90de46798107103bb97d8cf0ffbb6bdff367dcce67e70dd6439a25a26205bc86ab037cd82f923935602ba297f53ba037cca67e27d0eae6903892257f1fb4a3b7f9370e7a3c82f7895f89e5c307dc97d69d76f923552a618bba7564b11a3ef8806a9076ccc00d403ca06eb986a4eb6b4af455fedb905ca560561051e94de4b151e33e4335f0dddee3e94435e31a017afaa7aa0a7f72f2009edd9f5288a628877c50565ec74550cd18e05e8ff9a404865f4c228738a023fd1cf48ac8c6f55ba506e0eef40421ab5992e69637d7002470bf96b4347f1f067d51fa7b54146690964d03384660824b3123ebf0b061cb285d48ab6edba41643ef7d867031af3a950fdff306a3a25eb5981dbbf41b1c93e9e7b66d756976e8de37088b76aae36c389c760f769cea8d6dcc45cdcd9b55501e029807c7e822289d3b93d9df65059e1c700b4d93d5c2591c75eb3436cfc2f1a2d239c518fd7b0956209bf5dcf661f965765e09e02a6764b98130d5117d4160341779b5894931d257398491598f8a0b710d9b31daa93224a8a8182d79e848d1284e2262544e7a252dbfc120125ed6753daf2081e43a4b5daf47f53d108819716f47fa5623bfed1030ddd78b24d4a46a9c2f8885a727e38a1c9e305145e4e05b89f0d5a7e772e24ab2b0834ebb8513b434efb2b0c3b7c3984330a9c3fae68f5a2307d4bd850ad055bad484db691b500ef8c2213c08e158c939a05dc1b58f9aa3e1a660b17cc6609d03542b48549da3a219fff42644e1d410730ede30be92c958d05d5a160e2ed55721fb88022d3e0972bed3812a38ddc1d511dc99c48b90d3610f50f97deee04d34dd14ba05b7284d22ee12b803bde081fb59812d2d16149f48ec6c00fd8586514fadd6bcf109dcdd24adeb2d2ae663eb9338488e9c4c73b3572a45322112d200113a6774104531b6ca09a737f4a8dea2c156198b7297948ca488e98cee2085d88c5b2a2d8956ac139d5bb4621357494907cd49b12a5a7c35a78aa5b1e4e319172f63c558693f9e22314956642cf17cc6c4482bcf727a76d20b3e8d870f24c139f4055be80f4c9cb4d244bee74ed2a3d684fb2916937e4556683d9e7131f2ca939f9e9df5fe10ddf080319ce991ca327bd4269c6f4bde35e99c1b97257626d10f92b99f35d938897e96ee361647b613f10f995bc69d93ce6e5cee9c7476e3e6d649673714774e3ae3c66db4774ca20f12f79336279ddf3eac3fe91cb72a4bf44e629f24b9d968427f32fe90de4d255ea70634ed92277493eb88ef99bdfb049afe5597b45d1ab650b0a8170c83415f327ffcef7b1b1c154eb4657274579078c2433f968e73c059f0efb18f286ee5edcfc4df63201b2b6def065036a9a34112959dc8782b7a483a40e407c04a82f04bf8e00f414f4bf9fbed590839f567a0ab31f3087fb31f3440df1524ce8acfc5e346452008a8a67b0534b65941aadf6b4212a8acfff282a11e2c6e360e95aee33f8f8ab32998e304402a6093f9b8ebae70f0ff8f3482d0e85713d699811107f8a734568c49a3015a89b89c8ba4dd264cb3595ac68c9a5060a28841b7bc0b5d18b6f38eb6d2476115e1d3204bd41394f86c5fa58754d2d2906b2a26c359d975058af0377bfa4577d6a7a86e12039ad08815b1bf9e81980f0e2cef36e7406562a983445cda838ea74d408882b12bccca156efbc3e819f3521ded08def13c63d1e46e8e10a4e335bcc28188761faf4f66bbf6abc86704d7b66ce614d108f33c0cdf670adb4eef0ab630c7e532d06cf325d24c286783cb618119accd16c8f036c707de77af38f7cfeab1dfedea86645ce618277685c8ff8c7f42818a9464d3a19bcc555f5b7a15a1ec9b15aa5ed2c5fd7d500b0c2e1047dd4c035b596e4ea7c887ff8f86813f81c45e51844a3a65c6f57f0ef5111d9c987fb743d79215c1cf2c8720089732cfc83c2d02e3f0254c9c1a2c878b8aee4dde4f27c09692e893b132ab36bdffabac3a870967a7713d72a9d3c281cb36249ea8adea8125cf1a1ccff1d44f252bec5579a5442f356c57d7676346b068cd6ee6150252447c96f46be39e0165f2c1e37d4f4a0b9f81d26f8023b93f2a02373b8368f32efb2ac043edbf7a15b340513d495ddcec66b60abdc06091136e3bfbd83e7d56063e9c7950775077523dfcb0c965452fc3a17fdcbe9d88a2222b93cb1e67288bf3c2b1e9b312426ee6833e2814de994cfeb11ed23c54ce96ceb1e4febacf7af34907e7acb54eccd8fd514449c93a3509ad5e8982395616f55ba43e6d57dd089ba9eff105d989dee49f83fc481405bb1e55403888a2b83346831a358f446dbbc15f3fa646d403480919da511f4035a32733f07f546a9eb2fb60eb7ed92eaf2076bfc218d281a8bed008d89f1685c64e1d010ae6ab772dd0bb569c7bc549143640c55f1d2308f6e8860286c628c61c4ba1e4b160cc80a48892013568ab618e7586b4c1f0e339320481b8fed1cc8632e8fd32fab71d150ea4909edf41ae79835e2bb941d4a05b151017e2b6c210b79d05e1ba0554c69d015ed35c24a12d77ab46efa8f78d020d25da3503d1ad1d42b1091032316cb59d868accd021ae3fdc1a1e25bc9e438efb4d24cc6acb62381e52ef8bb1b4a19e9b24b1bdd2adfd73de5ef140bd6ec59e185558acf62ec679b051416934c2ad87eeb4d5e98ed3e41242324c10dd90f43abc069f5576725cd3ab405e24f75af52ad94b4f5a384a2bbc44c4ad16236c31bd940b97f6ace2df621fef6596a18d8d6cb369a22eb353917e56365e4f8f0be85e9e9223702ffde2d6abef1cbd3ab29997c0229645acc294a49c722d34055ead17ba45b0eeb4901e9847ed35b3fda5d3e1fb9b5a6bb702613400e0c7737d216cde2e996085fba101801609bce6c81fe835123f95ae18e6ac30da7145a8f00aae36a74d97f16dd04266272eb2ed5c1d6f38663a8b48add9c1b5fffbce5b08a34f34bb70f77d994fe309d29e3fe241b58b57d3550b1d95665ec2d67fdd8a2aaec8be74b5210fa10726ef279288f50528706a45c87123497bc5ecf65fdbdbf13388649bb011438e1d10666fa362a6ebaaeb164a9a3c64da4d950359cda45b2495dbb1f2d3f171ec439773f70df218377f9e248103259dd50d6763ce2339f938df5d864aeb00700f8a992c000fcc7533b1134bb7e04ab26073712dd86ae335f2f8b394a78f1ff9344b9ba14eb835b2d42ca16104cd34221327ac3313facffbbd5aae710620520371c4e163e2135f7d9e71a36bd1afdba37a85a874c0baaa56c983221e94329206b3e6bf578a08cc07f156dca0b8886a223b9e1bdbbd189acb0bcac57c98ec20079374bd20a527724a9cc0f383a01b384e132644b386175f6a67d48d4672e85d2bcc62a7464a5aa8fe398f3e27e89207ba02e7562fedf7853e0d993dcb310d30fc429702561759694a89a31846d45315e0d9589315c7ec0d78d48c81ccb047de40c21228ab976c22267fb4b40b9efdd572ce05cf63a9a7fab18c79a26087eae0a40c402c2aeee3e474d1747d3954fd83af1de9eaa343e0175eb8de87e14647f341f8622d160e36e8053a7349289847898b6ea28ab634221132229597d300771149c4d6d0b87446daba3897d848d284b5a1b691f13057be2d3551aee871144c3b1d60a034edf9d80aa43122e420da3fb994d8e7cf1505368e5134849c2380c3deecf7b6d28d1d95b09d2077c39c15e8e89a58484cf519f4124d3470dfc2e8ff99d5b6e18273d34414c284fad3bde0b853db0f137ef931d1ed1041c784e592ec3e08ce48606119a95f2e51801b75be9972d6a3af3308488dae2385f7df0e0cf4b1934ab512f1112a15c65ce55a111ceac8d2a817ff8420655b6af47802a29236b21d2e999c138487038b365e3666d61a4b467651c60392d91d7ce5ff87041eded92427e5f8dff5c2be12bbad6dffb5ca45d92d9b185c5c4fb8a6a5b23628f878a06ab562664d9a31c71edc6d1d4dc9fd9d34a386d71367d18886fea452a7df281cf9ef41b4635462f18b86b83193ebced0618ab0c892dac294be7c94ab342b882d1a761056d2aeba5d9429395767152601e54c24ca2413c28d3407b17b16cffb12a281110d6274b95a75d67c8790ee8258b59581fa24ee267e05ff328e4ddb5451c01da390e878a11758b2857ee93d1326a0cfef01a2b88c2ebea894158cb812e51bc86bddb250bde7835f1c468d9b9ddcdf986db9f828c17da4a0f16c908695f27d1cc5769c926257e0566ff41dd3bc740dee3aecd3698134b74086578459613f1dc9ca5f186c7a79a21fc0538633a4642652a27a184466164480de55552c18f8cb8d0ccf50d0150754461a08701f724a874d97c11bc0840cd54b183ff233752a3fe1237aa38dcef5e396436cdf9d6cd5a9e580107b923853269a420a06c9a4a624119dc7e4d9eb9c0a6885279d01639e34b4faf2da559d3c0c085741411cc7b9cedc1314318d4b84665fc95d07b828509d395656251166a8a2a84a2227308afb1a898a94955b452d9d6a38b74f9456a2b8471a8b8b7b1adb57e967b7d23336f4ddb6ba753ae4dd200cf48bef46db6bf22f5be6705cef521463186835ae212d3481e902e6e60b65829c208e65213f4d6222e053af20c2beaa63a10dec04a4e0070b676f5111e71a0ef9f20b1f2c8f62d92a6beba3a49d332db0d9f3889a5f3b4bab4143d0b7e526d51372131292804192aa736a10adf148b9f48bf441dbeb7aeb44b2f01c72519b851c26ca2598bc2ddd91b6e0b7612ac0c8788e8c012eec849547e614a6a89414fed2a95bf6bc13264cc5cad3f0c9a5874197f4b859d83e07fdefd7b44e04efffb89d33637c201d3a9ffaf6927a7a73613260dea5f809c71cfd6cd4c969e10a29c31568c8eded2535368a2f897fe4a399938214d0e4060b86205936979f4e0b0262cabcd1893a9029e39a0bb6ba4654a09174bd93a131461b78c41da9ec6fc389f39d4b8116846603647428816d6b1b7421b615e01850e007e29215189ca3303c8a3e78374bcfca7da5851694a13e94c60ae82235c469dd7f3c77224cccadce182bf6157d7db8f7a02e85551c44a50b0eda6108eb2add6e8eff6cd7c4bb51098b182aa2f639dc9f781456c8757ad716bd45fd3b7f324ca5cb1455b3e831ff582a21132106e06d71daa98e56dcdc7b381ed643f91be77c0699456e651190b0f3e1049f2475623debc6ebbf035566aa9d025c6b57dbd417645270ed7713030cc4436d60b3904867d8449ae1c537c0066faf0fae951052ed2a7315057ca88d79881ff6185fc090c70ae3f068c0a9b20fda3e6de4e17b39d58d1b3269393ae604f4a3be05a7268d34b0a1da57e12daf42c63abb310aa2fd90268065479c98d92e0f5b60b73b805e0e5538d4395ea2e9ad308403219a4e0792080b5281157d9b05a93b950358d243b01932e0c25be8a80ec70550e32fd0eaa0868a8c1d42d7dca8f26695b0a6ba4cd7c05fa460e6a3167e4611db468d5a3f1a822c3bcab76848f667848de900fbf95a1840f5531779604e46fb51cae3b9db5a50a993941881d508649b8917845f379c857f47f182c5d945ce38cb8b71c074459222e31758e2c2ab5f5c2399e2ecd09e5ae44e7d9af682e1b8d83d07c04297d87a3c215ca0bb274cb8f5324d07c0ee76a579638340308ca9829811c379f5b0d404300201247be056d7828b161c40d68d7c7743fbdae91b27f1cd90e985dd37d48bed6b4e1423b5513c3b46ec6af869b7013526d4057901252bcb5419c1674507eddaff2bca410d8d095018c45ef169c671f24c53b95e88b6e6a22c44063cfa2762529d77139b05d75adb0dc5fd8eff58b0deec2261b56235d368f7fddc1208796f91d72b953cb0e234b234b53a094a33d80db9e3c11b0c71ca81c4be3ec13b0c37ddfded9702ebd627f44a7a783db645c349c85bb9dcac6d50625b60489619ae22424137a16027c528710f9a10bc99ab4bd0d5e401eecc3f22d5cf953ccd04efd20ef59feb3401074d7e87847b909ebf8c13baf2177326efd70dfbe556fe8b858d40adc26ed4eb4ab49a6b57c338cd42792ce760388ab5afc12d10dc05d80f8e6eae92b0bcc82305abffb0ea7837d0d3605dc5b3ea334772061ee40cdea66b04836ee9d1c06c380f028ed46aba4d5089dd25244cc8de2ff8cc120835e54ac1f28c6f4d80aed0bcb2d862584d44c55d65feb15f8830f9be6950f6f6cc69108a42f51890a787b961f4623b90b04cdba18063f560c6e407fce6b8895a1852240a2bc8a7c6816640b8dcbb7b7ba04a4ccd9fb674bf9619a89561f6bae0e402602f5b36f1833f65609e74ab45950994c71dd2b8e86efe9fdce68ad908fdf691bbf0dc06142899838e0504772f59ca1a4df6f94f72983d2781581e18130fe28a5af71905a114444707c2c580a50efb3c6823e83af5364abd67a16e7a992e8837688b6ddc29dfc77306225f089d9d0f6157d341df28984860fe89f031768b8116a0e2910bd92352ed8b846a9fb2da935887c437256507749f95662d553a337c5764422eba1919b69242fa84572b50880aa57a85a834a2d925b0a950419d3b56245c7e118f3327047ade0120b0d1cc095354ba547809d4471ceaf78ffffda8ce4148cf100651832a4c74704466d180dd16316f02b8602379e8a7a69b214b444e9903fc5d97d73e0254f8219c0a526653566ab008f9dbb1765dc30333f8dcfb8c84e281ce78050212d9709f8816d060238e5d4771c395134b2915bd85504b85f7c97d6c39b87d22c21bbcf85de7450c6c0cb07c1455386dd08c6088744afb560310a613fea83cf158e3be88c2b91afb5abd6a3a580101a6f65c62cbd920cf69bc27122d305c37c53b3eae8b6f9fd83e14471f6c6edc33bda4557c4dfa22e34804193d2fb3f313270602fdd1a452f46c7f2335138791c32996c486e60e96b4c03d196f19b09fd8670e79354ee1f09491a4d13110853cb40f64d4bde9b11037fa49c41f5cccded3be0d09b72e90f749b7f7715541c7cc458dd0cca353b1400467cf056891400ced36071e904f6ef7fe9e4c044e6ec04b80b19e2fadedba2f8a120d347ad42c26d9cb0458f278f9d01705ebe1c4d8d3ae7bffabfd90ef88f55523a87df8c1dfd70736c39051879aa7f446d05c8e97611c09419f205ffc469a8f47738da3bc2ffe7279facddbec95a7cbd2af2090754df3b3b1c23bead465dbbee257baf75b21b13c56057ef305714a7ff9402a87744c0872984683989175242ce94819a156d4ee4161a0b995564354a8095b0ec50257edd9952ab146a77ed136b7d9387d1f5c4c6061efa6c2fe3f4739cc15c086bfe3e916d2c3ab34ae643650bac1d68b104b9e3cbc302325cd7cd098617835f1754fd1de68042f4b80840f30a76c674296183401ff33d1c01882119fb4c22bb3b0eb366ef89aba48c7ce82426a6e421f3f3900b2059cc560bd7389e2cc1aa8b7eeb3a8e15f754289dd7b656b3719dade0eaba004e846c390baaca1ed7c2fdd70d2ff9f0320b16269b32a47a9c99719f2ce41fe31666a780f123dc0d42056550c292374b1e86a37ff3220c4dc69d9d3d9f9aca9f8004e0f88ba5df8ee163ca5be9a88ddd25a7154ec253b0c91eee3810fac25331433d518007f652370538c423500e17d22f3679af0a9577a692e45df71fb80a903d213ea030fd94c9a3a9a976a5076130b5d3687e3e70689ad4297094ea26cdc2ba17aaf84abc6da9dcd4864bd84ec9eced08eec4116534bcf59b646ea8f7a57dd656ab6269644f3973957bc6d1c8b508a85cfcf97168c1c6ecdc56d497fd79fc79f800dc17bd5363974aaee507d980a2167fda963ba4b047d7bd93792cc99b0902d69692c2ce931c2f8e07e938303db248b6d92aa801072afc71d40e53d7fde68486a9b54f4831c2a3d1fb7623a684242ef5d16f56c11922e2f1e89750afc2028065fc49f7dbfd71d59ca4c718a94bbe512e09cd12a0f7cf01ff81f80095ab5cf7ecd3ce842060ea975c14d57f28b1101ed98a9c70d53a599189b58ddab1dde2313b7f66ed7f1d903bc8c29284cdf027dded4e21f9a3d601122a988ff8a0291b07ffb6e4d49affddd6040211817f860948558007f3db9654b1144152fd614b9d7c05e5d2d5f03320cb8fcd7b3803304c2c082520d71ecd711c318b55c27e92e6a1c03bb3a4b0190f14e663a383d139a58b52f80b324ff0d98d39dfe0f2ebd198cea26b27107eabe7631b8802022bd196deb347091c1456e561c952c0886b89d6140991a1d034798985349cd6f4d57bfceaea5bc8823106b7425afb66cc145c305843c5a530bdfdb0cb7135746c5ff00c927a9c4a224edf6d83dc2c7255ef21d52b5351aeff5da96fbcac58ccbaf8b9f9f1e36afe27c6d6013800257839a6fd8d90eba94544a76462f32a035a44ffb69ab4b6c056c8197be6b54d3a6d5af1d5e48c12802afe644554012d8bacedb2006e7ddb625e08e6413aefc41b6cb71e7e0340446e8786833ec8c174557fa6d0f7d26a34cdf48299f43e1de392e5dfdab20a6b4a96216f98d0ae8ced2297de9c6990297568e7a0aa4d5bf0496527e8d86e8cf014ba353a2a33340fda89b4b2d7fc9a59408712927a21094ce4d7112c512a5e02f2c3949789c8be24abd7547bf0888488bff029f8a1709e5fc87431b053a6edc2bbb7c0b5c8094c9996b3d1b569fccfa0e192d85ac001fd84d80180ba5a30b1cf6363abe4a99ed6ef3bc3de7b6343613947e79c2e67c7f48d41f004582cfeb8d07f1f7fb1e7dd0b4b7b708a32f25362d87c4cfc97fb7bad7bbd0c8581d072a98e84710508973c872f914fa7a75a9a2621403d99c1e66da09e7d27e90c37f133e6319120f3cf0645445308a082da7e6a00e40ea2ceae39172b9626c3fbe41654e141321a446b151999d23ae9a422f6777c4e388b6a4cc5333416cc490669934917746323d1de651a4e866088a14e2029b8988a454041fec967b129e4a474c0674da10665125e11bb7301904fee8ca8eaf5c3c6dd84dd536aa34cea2a7769246c5f523bfff7cc9c9e56c6d5229176f5d054b47aa43c192ffb7680684aff45ba93a50e6b2c5d600088b6eba8653d33fccc45008085311ca1d30524105a3fbc976f49fa01a39997bab6edb649e4f2f57099ad20b1f7fff7bad710b43dc9d77e1f7efe055b71df9fcd25207ffb6f0100d141b862e6f2024274842db8c4c41084015e56143bf1def9d6bb373b32aa60cac3c9bbdaf6e3c20da3c7f961f6f0cdb3b744920b94febad441335fdfe7fd44fd9a1f77d354915796b4e2a0eab33616f8bbe2b356932c88acd4e564c4fa151ece53b4a9f027b881f2553dc87be7ed4fad522901098bd2e495057ad7fd6110e45af3d7cd51488461a5925b76a5bc26336a58d4381d4ecd55c008ebbbccc737f4c387fa1d1de1ed54ac0029e4c1b74b1ac3d022efb4e2bcd20d3a973e81e3a956a7c35a79f8607dc523d7dd67203303d1f9d800d648a5d7497c6f04996b25cc991fdd76ecef885f13815ea34ed8b310a5a455ab7ff91208ac6c2127811dc51484eec33d05f51e18a1ede806d2815cbe6b4d6963117f0200f184a36b30cefa02f980ecddfe1fbe93f0e834d7a405242370000d19086a2938b161f44d382fc9d4e3764c3de842ffeb7a64e28725fcf9042144176987cf1eaaeaf3edacd9b063e2e92e5bdbf9f5ab1126f4e05455134bcd6c32eb70cf4c952f0ea12a0822a40ce1fec3bc608359fa394f647efcc9c715f3dfc26a6e2beca75e0638688aacb381b2fb59ab56e57d12b675f0bf22a5690287d1781a027fa69f97ad65a77d0042328f08cc3dade9453203bb8333da0240e57d5d4fc7bad114b822c0090130cec914983adaad640bee1225548e0a41580622023eeb9e425a5756826e2b8c4257a5500d5d14379d6b00eac76c251f498639dafdc52b04d0731d86f3b96b0c068c366e63c4c39edc01847b90bc0c58b6e333a3832b08b27b06164f938f0930c8f6d74759fd8af0bc3338979559fc018bfc11e24cb2973ac941075e700f66c818a764d2c381a4ce96a67d48e7d400f3281515f6a19014823a8c5b5e9c70f2f686db4ed4308cc76fee9f73bf91b0c701090cc3e095072de76d4d92d939004638cd4133bdb7b3e2a242d39e174b80f0545cd2259447125f9f93a0ff392844224217681be5b10f0587677fcf716a3c470b9e51ff729f82ead9456db2398700e6cdaa8b607954e422f678a899c8282d4b40daeeb2ac16239503d2a2545cbace629c2d0ada00d3136ceb6c6d8b099fa3223781aba6d9da32d897fc987f1fdf04f5224c28bff0797f096496de154a2a31ab3f56628dcd02710330a1e958857a552eaf04abb8e8a3c86194c9ff959a6f337b9863de39b1200fcfb990d7c2a9e1e78266eaf45483805f37c5f664f46bc5e5524dc549c894b5f7b32072b8884c4a65b7bed75045a80616018ab940bac565f5df35eb03c57cf6cb0db763191efc972697627e06710fd558289fd70d997067860c7b4232d3b6f3fe8677d3bd4feb7d9c58ddade3a8b7ec8cacbc6c1acfb1e03367230ebb93a124ddaf83296919c0bd8a10e68629618fe45e43fcf2ec03a2abcfb497b36edbca8703a7803374f10f5f56a8078fbf224985018ac19bf34668077d09164e1402ee2171ba96152688a255cf15e86a4d0203229ecda2d5a28e0e00baf8fb7e7c290ea23a9f539865605cbcd39e1c9bd70c5f2ec2b1f7509c149a361ef2b156fde56a75595426367ad2697afc06a5f8779dab06352ee1170a792db30f8835ce347ade0cfeef6af083dc865672aff496b273984d06e5b91ce007a8dcb0a2b2b5e2c4a1f9a7521921cab34923e50555c2306247d977293e282cd58a3fd54f14fc46d3ecb51175dca963fa356fe1206345b4fe1e31a263d61330e10d6fb2ec47cb6f63008fdb4c70f22a79a9a03a34dd7743427590ba6a91b5a079aa34b984eebca7bca1ef0e7b4d24832947e6f07931fd77eec241227479488be371a4029470560cffa882841290f41db2c0626a2a96f1245f2e39dab7d8acb6b263aab46da4c1922890a8cadad8916fffbab2b95e7a82c2bbab1aa6b59ca5934c14fb3c4f36030de1e5e967c8e93427c41f0ed51e564fa38c9810a2792c3c6302a21eed88ed7a2955d9cc0df485f92745910f4c8b26a0ec6a30d65dd9c634730112bbe664407fe615ca77be86da8d8d46771a41311ccc98a2693a940e05a7993c716192316a83f8634768f30bf3f6dc60e2ef4931f898e577ec29b9d24fb08cbe8a0f700fb28431cc0ec6e064356a6b637216123bf480c3eab2c4dcf3b3fb7a3889d1177110df79b8c264867d3a0bf78e301fd654e8e108ad845070b792644431626e22d0323dd57a975854d72038404a00a5df33e478a24527ee955e514983448eb01e746d4d33ea1c40246a43b79ce49ad334eaac8f3a4421a27f52c79642772f1f0516d1954f446e44b91b35b087a832f17341399ee0daa29c5ca2967be973d2cf82b053b0057c612e9b4c047b7bb0d7df103651c02a225017e3f4e2fc491d36a9069c9b6d3513bcab75f21b58719ab75b285ae95c14112cf68c973c40f9f447bd05a00226267216bde84ead8907ed6f06efc46410eeb41808fbb67e9a3499cb91da78482dc7782b7a0c1833a18055d7611be34174e438351172aad13b845881224943e0034d260f1ba14363140975e239c20b25c35154bc413e8da1058df327503d55297685d91edf337df77b6022f093b9d19b2046ce082ae03560c5339dd4ed6a4f9ec5609486904b433341b47d957fc4ff725b01b6eb0a353f6a2cc42dc91a6029711d225b80c7029e516cb49658925337df29c1fa0fd7f2d49daaa1c79ea5e3537e85e20b7a91dc4bd1da498415406c5004bff4bc74c88b368369968f1e1abf9ae8bb8e1f6f36dbcc582ddc8bd44fd984a3d9da52376a8fc9cdc20e1c078249a7dae98c03256da4cb0cb873a318fe0b30b382e6dce4bc33b8d0f430189410471a1f12dbbc5860fa2ba5a63e7e8859e53b973bb4a50f618646b924ce6a4b2a4267847807d2ed09d4298b5866469d3b346f3b5b5e05f750e8f2f841fca881ea2a3e8a072eedb7cc3b0348f9b5ab6b213f547d3e56709f6ad2abc960767f8bb35350f4bda51be8bba9a8718a839c19d63a4e190d3b2e6463141a80481f5818a2d48d13c2f0447072068cff158ac333be36c2999f2920be63096373afed0e1ba144da10a96829e63ee38fc8db5ff821712bda2850f642974d2b604a76acdc9ad49467763db1c9bfbc08a3be064548a40bd9f144b15a3b8c49bcb62e9faf90f0a18cac264d5f744814689ba6ba41a4a1904ba21ffb53f5915084f882c55860834f4e827e222b897c04a20a4c45cd43005d9e10c2f1f0878363d95d83404962070a892da0431b0227dc02d8ae6bb89f1e1a8408472569963d5cf85244c267a14723c47242bf24a215ddbc3af4d813dca31808cdf4dbd593f93e9dfa436d88a3508ced791d59654d2d02fcfb6cc960d7e2558b24bac7b1eb388b380a88b2e838fa49af4281052b9c6829062f1166245a818686ad190cdaddca19e09d713a58ced5eddc71c67a5e2ec43268851234e07a94dff2cb6f6330a7a68cc4806bd516b7d8f3298746aad99788ee868c3ee1d392b9475649651e7c81a58dd5e26a460f54a0403941fdb7bf1a1a37a54683ea16123370c1b47ae46eda1e1720a1287c9dfc53eb7229620f79e40672d0dd9cf16cb515b678a677ee6d82319f72950cdcaa23c63650831784a9634c1ae7c5929ab0de74570f79abfc7b951f119f881227f96a1e26edd4806b6e621279073723bda0cfb75dc37196c725b5498a304d37a318ec3ee49df6fdb215291e842c507ca80ba18edb7c39da46e0f59f41514494d2ea19c8ed96cf38d9bd62215d4fe9adf73f08dda04b8759ec4ddb771e426e71f8653980883a48a0cdbdf68da6b3729784911273e6762f4fe5e464ae08dbcb1c9420fe36f75d5372e196803e8a877d0b87a029b77c35b8698a28344510cd998889823573b93f082c3941ca9ced0a2d58811c1741345054d0aa18b50bf4adb2929d8fc5c6cab2b33c5327b9b3a129413c52a2c41e2020f7d1cd465c017c73cf96167a05e1634ef210f00e84e0fd8dfee5ec15079c27741625fa7dc93f7f3d23f1baa709290c63b017e600406812bb03400041fca8725a13b51768040ef8141baa65138f354e664367af3421cf849f1107803b07da0eb8f772c13f9874999e8b2ff3e8ec76a5d06329cc5a98e594a709ca4d494e8529c4402d44d2558fca41ed413ec1bd169cba43ed706e243f2fd00baff90fda4a1601a0c5ce1d979a1f6c0607ddc231e572dc9ab8dd0c9cddb38b522db93c5af6c611a3592f6e27dc0ab1d4035953047d5bea9026117edf8efb4e167b1165f10056afc2de307aa08c289d632c5c4a3aa84a4ca3830025cb69f2ccaf2c7ed9d98ad3636828444eed78910923f682af01c9574cfeda3b0c20dfcee81b2dab28a7408ba57f52be12b93c42919158ead446ff9b9d1690689d73a4d328a2b2ee182329a6df86dec8b6cac517d5367f36a421ab4fba7369088865aee90394b6460c16b091cd8e1e66a5732991feb3b8a93932e2d622b7ce1738d5244404b5b2508d6311ef3f232c00b018f7126428c93a4314e8ac9002ba23a2dbd3701d10b0e74dfca2aa162412ccee33e3aeb544a60eeb694381254751031e73a79eb78c40a46129a87d26369bc38c358baa291c18dc4d000e8ee826d517080149263f83d15fc9244ec9cf81c6b853338155cb316d609fad0f0f253e738c8c01ddb122fc1005a1731f327b2b3d5baa78b21536fe6bf6353f6e6ad910d8103247187356f18d8573d36b30fbed761801d455aa41fdc3c4c20d0630152de7b616bfc1dcfd1d009f36ddfa2c4fb249c3e75865af7e2d78f38b362a6dab25d522b9315fd65c16485abef7a3179ba88a3f099060d76e27c8fc316862f6349aca1f67b86f2ed5b228cfa22062f74ee58c5aef3582b72a2b678245055be5376505c587ce0928cf54a132316798bf3cb68bdc2a4bfdd06c74212adfb6f27cf3627b878c7e566fea8034be45bb3710744798933ad3b55739a77a82edb89c774bfc4b3de819c7bb7ea84cc2dd0e88c1d0d85408e520b650df7ef0c793d52a180613bb8138ee5e517ffc57da0b90e2fcfa2bc2c83aec7c90370490b974136e1eb080055cd24348813146c41fd991525cde4763ada96e979bd648f0795db5c13e61edd9f558fcb573ad7c93321bc69038a4a5679d51572a0109d804fddaa50230559e8d5d97064d95990a62c7cc7ecc186aabd6bc6d42db8a4240d9624151918c2d1cfdc32e0937734f677609c6ec8e959368a9cc46c8d6ba3d1548f9002eb0228c427bab740752f1e23f6b59704f9d4c32f0e3afdbf58852a0cf6f5ba80b268ff1a62495b78e5960c8724427fbd3213c0692c5436c66f0ce345de625e21891fff033772d9388a1405bc26127fa5f8ac583900ad6ebc312210b06454bc82a487d8f77f292ae332e3cef83ad34d725c663f7f176bb17f8bbbbd0a7ee574f3cac5960fa0a38e8008832deb35d95aa83ef283f04dfac61e78ce9286a404ca2fa0a08fdfdcb35478807b3c9341bea8133fc142a4908d519afe88fd5cbce581e04de5d588d5ac4ca0ce3c16fa22a224615bd9c4e2819b0a6e5b1c8a9dc95a74f232508d92ea385995c5e2e319e1775df48c57acd47940bd930560050c8ec2631a0badee3524595b0d3c111e47f0cd3cb852eacc6f3c7b95a40aa9bd987c855da64e4989c777e84e537c3821fa318f452066eecaff3126201f11f8153d76032eae111e163348febda9537cac59b2ffb3a65c36967edb0637466e916da61e53b22bd676584f1f48bd23dba4032c72be855292fc03bbc5f67e4a59a388d786a4c224c81a0306b5d561babe984e2b77f2953db4b6383a5d5eab7e94062f08abd3541822269400980c2f0f7e280072afbfa372d5426049af49d29e7360cce6af6f29784821ad985d6888e942a9c0388f3bab3551298c9d3f6ac452abda7a3788bf3213306f0b91ed91b905d06b52ae32ab1e2643696c0363e301be7f1ea0f178509fb19ce592c970f52c861001cace3c230d451f29f7b7a16ea0d65279299491624f05a1a7210badd8983b02ce3d748e6f11fa49e6f547f6fea9d35d53e58cb282ea0b9c3bf248b971167ed6d3d80bd96843890f308fa168a2236f9225dc744f3253dc5dc606060d3ef8f302a6df3edd822e426d70ba0ffb7568323cf4d1841d68582698c698b388ac25e765c2082df63d5dfeac1fcf26928e2fa89c39cffdacdd772d3ff6258f1d6cee65cf20dec6847f49d44d76a43b392397b2ab19a65781273a2d7f9d314f3c63dd48b0676a16d7a5b80bd98ce1462f691e97081190e7044385332622cbdbb72cb475b1b92485967a3f4ba752876073e636f7b374573e1be4fadbf3a001229188c1729b831161e476eee231d7952919ea9bda00e6f03755a17b4288c8ca553f366b9bf0f410140eb42e68a0c82813169059265c3fa6325349435fd597333cb819e209c4f0503d749978098bfade7417f0702ce4a6ed96a6513a8de6a6999ff3757aca30489e904587896dba01a9ee4f21136717b4c8e1d47a4e931b34898e305023c9d6249f2f7c59955d3769a7e78cf5c46c9853b7b6e3bdd06ae407343dc228251e885fd0d0ac70d55b2e49fd9bd5144f85716fa17c9c711deea17fa11bca8a8011744ea57c1601a1fb160f00a62ade91183a4ade0b164307febad02d30801229cd5b8f242c37954d6b6108c36c5da1705bf85f57cf00f3b232d24a209c4ab7697b02f8ca870018c743251d51a4bb4a6deda4e2954d7805ff5c5a96884511424ff437380dd95f237f620b1c0f8a36f3cf158eae145f4cd883f6caa71a249835c9dea581bf953aa097b8d54d00d72048844592f61c191607647d5889324d10311ee3395d6bfa94df959c9038711e7ca3a10a936e5767546da0af6ae8e9df531508f78efb22aa2bee089281d473124374450401f0fa1da29f85063579ea84b4bbf3fb4de3d7b7780b37e5652357697e370365cc6ab1447ea0d21e2292351c9ffb262638708c7c7a6a75ec6af77a7aba978843ec8a640a6d74b9573cc90fcd18d7c2bbb650df1e75d3a78e3f054b96915482dd5f254bdb3f8d76d096eb9ebd409706e533005e0b316c05a872d54d1813bb58f1a8920592fa0d7fdfeccea3dd039e2dc1e07ea6a43b47ea40d90e70e7a17796ae40dd1ccfe554a0fe7faafde579f9e8a1d99e2f3d588cab0b172f02eae7b9e2cad7b08b86795507bab777abe420fe4820a9470c6384cba891af81fe6e0336c056d1bf2970b70ebf4fe5b3029e3e3b3356752f6eb0537cdd0922300baf2929b343b19f9e65b5a38a07e399b1e5e3e41c7f66994a83ed39d002e225980454686effdc506a85ad3e4ff4a8a81332d0df491feaa7c06f9c06b67cd350e3cd73258e83105576ac3776fe911a6febc551c4da3ac2e175c712abb0e83793b4e8ba23d79e16536dd29e0e4e04be7eb6423fbc7f7d93e7d3b06d8e2fb1e7a07d331bedffe2ec2c32f94dd683351407aa4a27b9fa64c394bafe3c94f22c3a41ad13b8c424754064bf1c5a3929af24c343994a5f23c962ad8a33b8293c29db54cbbf17bd6de2df094a27c0bd4093957d7e9cc60fef798ee8f3300b90449eecaaa321d632e20841075d900238b86bab22175ab1fd917a7b3c060e70b925cc6fd6c68b3bef7f44481a098144b06663d07a1b5451c83d8205df0f4019bcf6837095a89f5b4f3b05ad0eb73dc1d35918aedfe694e87cc1b1fefe88401097265789e0100bc1fbb125664d51b4e50950368be071022866f4cdae2e0be925ffe74a412bcc60f936d30e043688ca82f2caafbaaa139878e22a4435cdcb7c4141b6baf9b9073394c028f646bfe2c487b0fdce424ef1ffa2c920cd06f1b217b3024239e5f0e2cae89bce47b87d7d02d1ae52caecd923bce0a238f448b8d38ec91e1e24c8323607cf530e235f576a1343cb484aacd2347b3ce14c4b15d435e27a2fd4fe3b30b7e88d655711c623aa1ed408e76a92c7e9863a96a5f86afb1dfcebb40b85a384c7e59a9601e2e2e462e61487f8c7ca91339d6f01160262187f2b72c681fd6c70efb676543a17444706fabe4e5c5f97f65075fbbcbae9e0c29f62f7006c0cf50c505a7f1098f633610921af0793f8d588401f861f85f919ac73cc99e181142005185992d6d4bd43e16f4e4ab94cf467c87ce3e325f656c21deff45731752a4646d75b2f191cecfef874191c70c5a0812d7cfe60b4ba153e417c117434100543f2fe3df55421fb8c4ee80073c1241396ccaf3247f096ae99b89428fb7efca22e32f7a1064354f194cf4afea9118c1af490b90626d51318c0ecc9d277756f341d0b269e0c38a7121321f1783429095a9e00339001f26bf46f3f6c953fa7b8ddbe1fd645e4a6839ea9e194f91459583e46c4383eb878767fb18903b8ceb27930630cf3fab1b02725eb42ea1a828d404d76380b553a5b907b3d93dc268d9a198398fb3b978987121ba5631a382671f0d07eb9a94bd56e8da74e1c216f94bc0fcef7043865278df6956cec6b85394df7cb9c7956cf1ee408452e9757b82b261058df3b54e40d54d8750b5bcfb9b910b6a1b7e76c0b20303022636acf156a2cf215022e8dc4af9e6e08b4bc3378ee7ba04da1decfd99ae0146ee617eaaa4ae4505fff8466fe369e9e3a22162d98635365aa590f8a224365b740516b167ef4a1fec3059310473a2316f7a54506cf47593e8c56a3ab97506945a67be488098600a6fb4ba6514773ea954104c4458cf623adb1ed72fbf7480e2b768d80daa993579f513ba61ca45558530d5a90067d46312eb80b272b92d82f928d225d7f1e82fd8063ad849de4486e884b7e41c46291005ac9191a5a2cd381f63cc13623f2bd9cf070ea2bb934134414f0909341953f3f748ca8f0eb33916f4ff5e32aa83a866519c7d5fdd83e060cb9731d3e1a8647f3814e44dc84b2787ba963a1a6cd8e160e35b4b42e551dccbcc10090d785fb8adcbf807774267ba6a9a71341db2aca392a5b1bcdde4e3d2d078dcc6381b8b4c8f7af4a07f62d7fb433a5ad6fff4726bc5dfca5c70feed5cb6c03e6e91d6691cb5c8270c8721b141171c32d18922a61fa45efdf4effad96cfd0f2117fb8e11b5ec406bbb5b485a01dffc4af7c9f997e9c451c07215ec121e29c2a57f7079ec217f9421be6b67c2b7ee0f484b34077ead5538951e8cb4f2d4fd37292fdb108be9d2fc1eb728e9d54ddcba7731b5cd77d2d442a6621aca448e20d76bb09eec23a3adbeca648938d86e759c54fbe312cdf2c5f809e9b98472481f400f2e93e533d94d9768f0fd7f200c543b4c9023ad9a93976047812c995b13561d507860c6569d1c49ffc4ce24825f5c27eb0460d2b880721216cc760dbaa9506c28b84671be511c59d8c3c14ac5c37f64ad8bbce117ce312f253f1fa356b8a99ff8e0f2d66b0129b32cc998e42aa79e8ab3c007aea6bc8e6a1b2c6a68cecf0d33655cc28c06d99f6891d5e127dfe25e8649fe7e15f077c95a7e0160250176162e6c28209e4eef916a80875182fe963a432c7531f6e2e0ef8949bf55e9123549ae067c335518bf72b1fd91ee6c67519a457e66e131fb9a155bc76d24304731d0de790d1b6274a68cda662da540a349ebe584671bf763195f7f1355a33cd92cf7c815c8786cde852236776ef5c523e9a24e97eaffb073a411b68c1fd6467bdd50eb8632291326c18517361bbad855404b8c9d10323a49a7c09b7c172a0cf3f9644c16ff4602f9ecc1922d6469079842eaa8bdbec94e6394a661a8fccf222f082e882e3d17ad26f886ee19dda8db58ac3bb61110b436964489c1e2dcb9d4426ab00d34c0fdad4f3e2d90a30493f10683a75d98319d6411ae0e9451d297fd981f72249d4b67393c18904b747fb4b1299f579e8e2ae908a1e7760231f797c2f1f04242d3421464d1d8d78451e9c1f229e9ecd280ff9032936794dbf114f07ec72132ddfa540e0c47e55c1b603c9af082d7e6f6c912bf3ac112b646e280e894930c028a5264a6b003bdd1ffc6170187aafdd786d603cbcd412291da4f0c09247ef2674913d041246d42205a45968abaf6e4e5e675fc75a61d81bf27d58fbdbeddb76cf42aecbc033d232ea5253b5af17a18c1711ce69eebd739b542cc094218e46f0262d0cac4c7da81e5a1d2b8c0fda737e4b1ff58cf90ecff8c7a2bdb4c16c0b71879244bc0274bb7251be46a3bfd2d29426ec1e15c904ec359af22fe864ed2fe542e042a520a7e15f08d067cc2e93972caf3f0d9fabf4f2e14d9867008dc6694db8864c119bc5caad545c4bb21d9c0fa78da81efdc3f9a6115ab6dcc9d7a62943c39e1dcbda242fadd953f0d762a29c38588e858daec53ff6d40721e4d13665d7005f5a5ef044179eff415a622fd4a1a53afb0e0dc37be7259ccd3494df6172d4a5491ee84f5f7d848a737e1c35f656054d6312d507f8622ac9bf1a205ca8a721f3ca60e55506c87f096830be9509f2401c85b3f4c1f0d1fae71cfaba276121487e691e87f58ec884548c20d66cba0a1924060b28358512239776f40b32778261098bceeea65f529aae74d0459e9198fe49bb113ff5a7822194172d66d64a58c2f7863f502955f7d48e3cb36623c1aec3d483d0db14f0fc38c2bcb781625d69dcba2dbc5beb98708c034b5431e6f8154af7adae20c3059b6787935dff169c7c624d88678b63e0310c9cceaadecd2492b9f48284735badafb0067ecda02df8e3c4d62f4e83f95840945a9454fb03f0a657bdb7436e733bd840d71409482b5b79d67f92ab5ab9c6093d056f6c81639f974be378c78df45dc1f923a16db01a7e9fdbcbd0461429268a28de7347974818a39c6602ca81069f4a9bb5e9783fbfb01afe63a64010d26c3999a6c033303e7e1cc2bd3c60d9fb607a6204f09b8e04392148554cc836ed21ff4a2cc0af8fb0b0e4c242d0f5c29071b8c2c447e4cea0f11ec7b1810061f403a01adc4c70505ccdce6702a79182d9cd874358ad9cfc1e22d1c3642c3e598ba2b2ff6f52e39ce948542ac0724864daefa7fbaf412014c1634ae86cfdd72059f1df2931057f5f284821bb797c0451a16ec39e863cbef984543b266f99e84871e1776bc3332343bb7146f1a7aba794b8f06e671042e5e4ffc26b1837c3e07ac479fd6cfc00ed221675542aa0fa2ad3a67e5c8049dff89357d5e5895d754f1d0fc8f2d87210a2a6389a69b0d595a213e8db46a4b050b240fce94e7574f66134d407bc44f436d7c01fec82aad42168840b731cb03cb8bc9ec8e3b41946d38751feee4f70b061e986398c4206bdbc030bba21965393119ec77a8f39064f0e2caa36b07d69588694bebe32eec3f2ef7cca67629e7d32328c8eba357840acd9901fb20aa0c0c8962e5c9a0b44479dd70e07a342583103f75bd25af75ebe2ebd42b2bbb6ace7dc40e0d96a7d85ec9632a51ef27141d15afdd27805cf679afd23f8275f8139cc50c9af703d05263364484ff4127409b7b02e01ee76c9ab481d1efdba8738021df1f3983edce8f551601dc18086006228a7c064db8c91ad4b3a4330f627e36360d06c6657c0af430dfb72dbd1393fa630bb64a9f7e087958b234ee4bd6b32f552fe379eebc926df040a9643f5df404a5ee1da08336e60e73d7cf77473ca15af66a4aa49cda99d718f1bf65af493f2fe2d8708b82295d863e8847f8f9ee30c3eef0a50c73f93a7f6cb9c36053fa05de8096d451f1e7b8d017a5a976a77dd311b4a0be48f0153aab108261d48b323f50cd56d886af34143973e70c1cc2f02a0fc42f59f7cb6224fe9588698a44ade8ae735ce0d96509ad30983d4d078cd352b30bba2ceb1a3016ae3066fc2d87e19a153683af92503951e7da566b86230b5df726a95ed9ea9d833fe90a4a7e9d29a3732985e8b427c7a9a94375a0204e58eb578e5686c20a56f0a609daa8732f5374502fc04ec57250f64fba7419cb488a128883a914b7333496e258a1beb57646a05466d4247e837d7e51caf72044ac55da0f2f2366cde278219c6ef7a5e73c77be1d272969c7fa9d6925a535ba040ef235b10cfb7a24185ac42cd350022dac4256be154b16af319b186b6a2c9b558d01dc26a9e5eecb3d64070101313e91a51af79f72a3e223706f6cabb9b40dc88aaa542ef0017d024a2e72880ef6e1b827894205e45c375a0aed832e1dcb091ea20a91ba0fd2cff4c44d75f609506649b52876d7ea6bbe1b7c1439176ef3ce7f5deca68c724065c503b5fdc67eeac0e3a920315fb72d422800122741cb2773bddde0742a313440810891e678d366a5f41c1c2bcba51a0b6f5c06a45d875f6c1b5a0866de03141ceb29b1072ed7ebb1e84d22f332cc7061eefaa1e3c26e65e160116b2223a4a1aa127f0faef07174ce1505590d3573b629e667938eca523ce34980acd65041e549436fcc00933a2502eb06f6a7836722a1f4b7d8e2fcf83b1801a40f7f9bd011d7286005577c4cd23502a7049075ace07f427687c4c48a408aa2b433b6d62615b9af7480da306e49dd5f3dbafa2d44c8fd41e323a85b511407829aac141694d4a5332ccbc75819cbae1157b304a90b29b9e65a28132612df19772c8717d04a0fb09f73d4c4b0211b6402b0c964344343e68fc00a03928075eea591e7ff14347a62212dc9f41f41fc83680e41220453ff4765d870a80b06014c05509285f100a4a005aa85cbc88568ab07050f56bfe1d3b10b3d30bef485a8ab6b4d8da6110f0ee9046d9dc1a058409313ff3144720f502576e96b983b107369cf20c9f4dc975188ea64a995211483c558bf0c4d540798b2a6eb232a776af9b883a9d04608163f5e286eefb82f370e5bff3dc36c8775b9ab689e16ec88d1a245ac1af30d145e8181e529b64eb06e568dc552e0ee3ed0aeddb16b846f6e49b7f66ee31355ec3e1c826d804d4d8e8d2c083551592eda359631e004ecf55609a974f2bc32ae8589ade47757bfba05311d16cd793bfe7aa84d166595f64a80c99e9492371ae522b987c0a25e16a55a2c6cd8658d9d99cd13563a8c9a02d4aa5d7a5f10aac8d9e29e87c45631227bd10ba4c60ab2da956dd01e6819e018195301a83192dc360a89d466ee73dbb929eeb359c741e295a9e52baf740675fb60c4d5f5fbaeb3d932c865c1c12391000012c8ed83e18c05644c3b4611c89ad6911c985560c117d931c6e9649771de9a780213898e5c7d864c28909fd5e402bfc490816874cc1d492b29b7fb92b761257fef20531761ef025f0d6b74fd4b5e2307f8a4f5473421435c245134be4004b56808e1f399a29f2394113f3904968cd328698d19591fd14738a9a89b38c18c64dfa2e5672e8a560a5e2db07f82ff59c6c6cc20c622b490c87cff9101eddf3f825ae2840b306a440a74c34010dd5941ebb1252b82181d5215a8635cc8d1eb466a0ca4fde1c9c51c8484a2cec1b40a3fd5c963805ec153f743e6a2e09286e9566449b0109280133f08ec3dab2188191db055430952d28db868b424ea7b2ec68b9edb848a0944c1f748d238c5a13d138685c4ecdb7352ee1e7a340133509388e89ec8efaa31a07d06d4279e3327a733d2932f4853d0360d2a140535cd395b92272af16e5b0763ca3c52d38d939fdbef8185ee1a230678c1ebde71201705e6d57ebf864d282a6045dfdf03519a3355940d0accdc4b131229090e9d649e41c3ac4f16aee9a4e87a36be57a08beaefc253c69c3eec8f8fb3cd06687de23dcf4f7e04e2a19813287843b8abadf8824ddda673caa57c5b0d3764f988a9fb882d147e63029611c672c06cf77d0068d44ca15bd80e4a19753b5e88b748445f8f9ca0e6852313f35a7c2f16826a78f6522f1bcee690c88f5b733b4f1ab9b323d0c1d6333d69b01242f843f0b052034983f2226629e2b6e18ec59f6c76d84717392867b7103af648152663fe63566a1cbccf1042806752b91d7e373b366941656b17282ccf7b1b0e9f1c9bc78acd7d8b3602f698d4e00c5411f180f88c431608042b6ea230c6dd94eb80630e66d4dd62189768cca6f4d3a83634168abe353c5af3fb6b094cee4e7574041ca0cabe84daa23f728a840a26ad9478818d34ae8c27c407bed290b55eb5d108b95c913bd5c03a78845fff71c50e4798daa9d319ee588bcb80883434220bb460b474161f03444dfd73662f7d6f154568e78f77115764edd1d1d4e682941166d05b09d43d91967df7f2841793c1f176277ccc80ba040dfe6d1d94837b2a4d879d1c6e4204608f65c17887338a49c97eb4ed78b36eeffc274bcabc6ee26ffe462bfd2c78744fbf4632c2979baa72b3336bb6f67d5a8ccdef2e33d197041ae89b55a26e70a2f37cb1ecf6ef67f1e9de4468b78a0296f0dcb95077cfb6c7c77f27bc42bed09d508c144e57c664bc81acdd485e32b870516b045c2fd8c0808c59b6ba70a12b2a03e4bd35878f27e96fe948faa2a9334aa89d169bc222ce718ef9df4634a0cd82a9b38312f67023793e8a84a84e2f17c038ab6b25a776a7b13fdcc2aa8dd2a256f00d458729ff65341aa326927506da81a44d40a4172697bceaa0fdd4addde54213d51ff043a697372597745e8b77095dc7f68474ae6fbedf6385471003182a04e295558ec656bd5b3500fd67675c684c172cf878cd1f7e3839476bb2080cc35714b001245168109578657a22f79ffaf3c61c063121072891417f2f2e855ab6cb1c0a6a249056a82bc4d7d37b59ebbdc009c699834403fb7f88ea309f399fa7431cd7d774996a5d06132690fa4fe48fb49ac35d9bfef0832bb05a3100059e80ca218c75363e33346c93b37813751448eefae89e426b7b3f7c24e5a3f1e4fe4a94b5afb854ce07495f4a663d558ad8f62e8409e96837a9b3246a0b366feb4f88e6cb2862f9729c8c6fab20c97c6736333987351ff4e7a64f2496111f8a4b4e16f6f18d9b0ef3cb5de6c0d19686149a5f34d065b0fa416ee347da33ae428e6fe9f5c30dc63179a441daa3a373c59c4b3fc2f4a3470961402bcef3526273f713f14ed1ee2fafc57fa451f4ecbbb33a2979c5d459abf736dc595791fac27fbfac852517a86c7214e3245d6b0a2a177514a9c017bbbb5e5646f690389ce90b84bfbfd5b6948fb064b3f93074f85efa7f3876e1379b20c0c193388812dc0724839302e66e0cbf235f181424e69e050baf9740d43579c69c03a83289b7a2bad651439ec38111e6f007e393c828c6d894a3a2b7d99754a9c1b374ef0197d9891fcd9083673d643c73f741172f0d2dfef42371f13680fdb38b3e62b1485544b9b58a33700ce0f6ba9997717212e01cf3958acc7fdb2e925ed2b428eb69fc9f3a48fb781b763c2c68f45bf798fd6c868972ba63c09ab617f81813af39f215b1ca1b200e1eb2fe318c9fb23a6757ca68e04111d73b061fb44003a22c42023a1f7312ad8278768c94f55b5571fab412f358ca2310c3ef37adcf1af462d19c1b6837aec761cedeff827584a0addc157b7b569eaeb4eba2f11210171bd05fca2f782e4fa50ca1bdc430154bb7e350fda63fc65b043cad0c201f86f3f564e7bf805a1f5dd051bda62312146a540dde60d8b1a6e578014212c6f76696e8b3ef9dfbdf992e606ac78ed3fc72540e997ae7f3300eb032d0046eb62a9b054f7ebe9f8d84050cd036eb6add8208c03b10c1b86c0e881dd24dcd6860024c6681447b03f1ad471a336dbf017bf50f7903973b1863c5a99a2467c688eb809e7488fe6a96ee28d221d9ac642587777a7327d82f48fe164ff42f4d5d7e397fa57b75092ca3ac748f648c729ae8b4d24d1e12670ce205d4674837055ace193dacb53e81de960b25acb7fcef01f26dee7d2ff816f4f7b8262c9bf31233e8a4e4894b92d11d4c258ea42ec90eb77640273ae7cde2ffabc7bf6b2594bc3ddf8aad076e921645a05dbb892c1a5e75fb81c795d671499d1d55937e42ca5c8986fac60a3e0877cf66228b8ae39f1d8e4a95468eec3010415c484e176f3470cd13363af1cd27281f9293802bffd39bde5ed0badb3860ba3e432c3fe2d33a9464687faf3815ac9c372f32fdf20b4c2030a627bab5a3098554d3ae536bfdf39bfede6b73bc7ce9ff66187950203763775178845576ffbb78de8fcefe263ffed236c873dcc5afa17607f08d053d0df6125eb56de1f2b357b70412562dd0c98db88c96e6f8d6c238410b2c9267bcbbd03ab0a870a890a75f647ba57c5d2ac22e952be6c2471dc6e2a6d78f42abe5eaf579d39dea57cc9f1f0b8dd74121e37572c93b64c2a6d9f24acf2e11efe9a65c2970a612d7df44e4d2f892a132edd3573a9f412c6f1308e115475e38e0dd6934455c53e6cef93ee9a99847136ec8a65d58d7efdab5d74c992fcf2fedafeaa27fdd5c3ccf6e0787292922f9a76142945be688735caa6225fb47b2af9a25d13c1073a9045d646f7c431c758f3769238d24c3a28bec0bb6686ddfc0aff2afd556fea5cb7c36a36bd6613ac73e9e3cca44b59f3d2b1b9b47d7449a1b96486431e5d3a64d8cdae512e3192eb71ea6dc8a30b96431e7bc8f5a36bd49183a13ceaf840ae778d35723d066e76e1d44ba18aa57ce94cfa0481e8eccd9344fa9188a3d379bbcc20e8619c0e1b51b12b96fb060e9a370cebdc16bbe847733482fca5ba85436f3f7da0b7d847aedeb5df729c6b7a1e3ee15315e6185a30088dbbf7d16efffe611cee5b876f70df388c63a737411eb89013c32a2009691ebfed63c8dd879a37095e59430550f6ce7d823c58fcb21f6116d1b5c39347f76c152c92f076073f726f8c83e62f739fa0c52a1f54372c4e5681f8c6bdfd78b1ca87ed02347bf8625887a761b7ee276635cb7ebe593d84f334ad40b3bd67ef477b4540246f406c41d46c39acf2613bf70de3dcd37318c77ebbfd46bf1259720fab66cdbf6ab366f5a89051d193efabf8c9979e4258314efd7de3b1bee7bd5acb9ad95c08edeb1b67bb3d4eadb5e2142eca35cb709024b7573fee32b27ccf2359fe246f380ccb3e70d7f0c8d22c1c34c33a6b8775c641b38b660de368e3178496b96b9cf7f1bbf16da26b6616f9523fc15721a459fb88a39d7bf7f179246b643838926bb6130c82e278b7991ec4f1bc7fb2e6e397ebabc538de6bbe51b38771b86b0782bbcb9c21731d5d3256b9ec7779f87d77efd27222ab59da377166efb234ab7aa2a649f0ac6f135916b3f410ce9eb5562bc44241d374644f0381984bf2acfdcd19dea67db60961cd33b7266bee35fb8dded840b63ed48c63afc13a5f1f6abed7c499bd49af0da9149e942dadd66bb8af6c55297b464bd952ca2683f65aebbd2acaecd52a3f2bade00dedf514f4819ebe41aa6109ce2c56f9d22af0e4a401bd95dd8028db57296fd05beb43d82c793acaa4e8824c8a2a849ab5da90f0662993e2093f90a209364b9914480862a9b042855aeb83f094a54c8a1f2978a04411c51850a238c9521605922b5821ab20cc680f2c0c1443d00009103ef063095c10210c4e683640c14031c4063e4c546ba13065298bc20859254b5914427a789265d466b96fb308e525ed41815020ea83f6a03fa80fe5b10517444a920c6da124c9912155ab416bd5346bb75befa53def1dd5902f7dcdda6dbb973b42bef4b77b39aeebbcaf7e1fedf9715de779df07168dea68447b8eee6826e93eeffb4070f46344ead7f8b600b9c8fd11d188d40fb70895e47e2989a99a4cb4a7e98efa887ce9f78f2b843450ffe8293a48fb28e91dcdd3451191922e8a92cc298a10192254a7a843725f13ca334817b3c7e43179ba9849cc9e23fa48534944ac216737a5b56ab65a4b7bda3bbab329ad55d3ec56b78df6dcaa66edb6ddcb75b5eb68cf6ebb1cd7759ef78120ed0976def781e068440a6b18d29e21382291c2b0543299541313dad3c464723a9d9ca05028a99a4ad19ea9d247c557357d5434b9c9e9f451f1e43bf9a888026dccec49948a82427ba2dcd1952ffd934b29a63ce44bbf12d91d84e3ad44b2a6cb7d8d88acd1662f3ed070344a69a548d449ad16554aabc5ac5a2d01376f6fb4a5a4b2524a395b03af6a76bbdce5ee6667b4a76a76bb1b8d249a4043ed635635bb591a49348186da47d5ac46238926d050d52a8d249a4095d28c92a8df238d1553de0ce4c6b447bec85321f9728d70d7026193524a29a594524a29e5d6f7ced4df4c3f330ebdb4459e52cac9eeed0c9e6419c3cccdda947e570badd22a422ecf2a425b45b8e52a426bab08b52ac29a671521cd538bf164ab083b4f22e1fc58caf2b68a70665b4528f30ce58b3ca5944a293bcbaad9f06432a90542383f7ed9a395d6fa5a6b78bf32042d4bf9522fc7751d2a0102989e14eb2e2c5884521605501e5351f8c8e3952a21ec651ea2796eb8877466b3bfe20e24686eec7bee0dc7e15b8352230490a5c43faa6aaf81adb62aed2f802e59d233a47cbd942a8985d85eb102e4cd06f30319143d8200591d84520645910c0a20191444b2204164012417520099681e326c01480b27aa48620b60380289892ff84491021cfc30b204211f2eb4607221b38114400b85943333555e652f77f4dc9b7bee1b1234379b66b110db651e22bcb9d66af8e7f2e42b6ea2388940b1b8c13c4e0ec2befc38e51592d98087ac063f960adb03590d66508318d828e61764358081ac063e8ee0d91c5cd484e4cbc41ec912219437e59635897cc181ac063db2a0225b84d99552ca7a5a44e22b3f1b04ed66f991524aa77ca9a7303c99d099b29f0582945286db776f18ca53ecd1498d10ced75396f7b35a4829ebe5b81eaf6a76bbdc9547c619a47b86a890ca238966b7bb8df2c80cd23d3d4485541e9addec16a47b86a810ab8df24890ee19aa5ad582744fa535488ba33c628510cecbee6e929452decd72dc08cb8d6b9279659e9435b30960a79002264890203041412c4149809ce109205dd4000819ba06a32c654094004162032020469ec0821338983f4f6061ed8fec092064269860e979b725422a854fd8504821961682684fd80046f644134234d8e247176a98b98c2167eb863cab9dc1c6c520fcb2943df1020b83275080c33f8104484fa9304c0109a1219df9c32da49a85f5b392f4e7267362286f5b98fa09425b64b656066198a5cc8999134434ad092d9a18b241f182a905cd6aad699aa6316184892d424dd3344dd3b4ae439235eda92b644dd334ba44ea8841cf0a66c0052514010a21e082065480996c095a20021021255b6822886d0207a32dee9c5589267a38ae0531f821410f27dc800754985a604289c64417401738a105b60530b21f53c86600049b022ef40a643faac0d915c87e14c9374bd98f21560556052c3e64c0e3e3239360469b2c4fa486f604f7decda220ecb294cd40680642688bbcb2257090b52c654b40d14508b3474ab5b5d6f208adede91699b9cb9eb9598247b77061f6b1040dbe1264c104242001097c7ab294c560481eab8f4c2fb3c85216039e0c831710f9545953b7bbb14a08fb1f6b6f718c014452daed47d68db1f2c874629510db7f36ac12c203319e1bfa188e4d1aeea33cb2d51edd4a12d371e35ddedc8fb2280bfd4c21d3870f21403eba0e074d7c816691d7e0cf249ab34c3f6793680ecdd93d12f8e6e2a00e427f1a684768f18e1b9b373b822ed03d405ca08fe942286f2e700320f0f0f0c0b8b14702df581cd4d85ec331edd6bbbd03c06f1fa2bbc531fbce7b7707783847cf8400710bf5500ff5cc7b0eeedd1563f7aaf6b87b7740f7ed4368f770cc36118f77213adc44355a23d22ea63c78badfe7e07274bf1f298f1c0bf2ae5deef70ee8ee5d9500edf74368bfda1b0f71efe1d8f67beb009f1c34086da1ffee800e539fbb9dbb66354c8582d8e7081ac2bbf61c5d0eefdabffea65dd5ddb53bc0fba77d08ee1a8e05f5637608ed1f8e6db2c3b31c3b437898ce680bbdd6611e7b6ea43321ec39910ed116fa4da44462b522114fe5c9f43162085d7289bc9937315e08fb9428d3c3000644f98af3666bfca39a3c371ab038e8a6723aba4943f064cb5ea005ab699ad5344dfba9355296b2173c2152c6830bc5160592942784854a68099e9012b888250a28454e7080a24dfc30d12676b00270073de8d6848c0753f02008de12644478f0231f268b24b9cfca62282677df236d42c2510ad5d8c100ff0624ac97da65a14276d528bd1b15e17cd3296c4f6c414e6c4dd04d8a509ec6a55b309f315fb3beb0de1c86423bd881050b3628b62058a4000b136c4e60c143868508805880608080369f255b1214d915b3d4d6822b760002da3a905dd123d301914c07486c32681e19163cdbc6021859caae88c1152ba0354f29bf178452d3c65a6bfd266a17c797e7646d30a8524a2965e39e092525c3a6c4e6821fa250b31f354dd3b69fb0e9bc1fe7c7eeb0c26c3e58361f3061b06288cc0a215618915961054a69ad561459f185253cd91e0cd1bdb9efc5d0645abbb55d7ea719f224ed5a6316967c49358b5e31c6a6c60eddf2c3bdf7621cdd426774ba85dec565b5a2912ff473460ef48d614610fedc8f291899d63d7d0ccd00606ce68cfe8c4cbf5d8c83022f5656b266febf978b3314d149f39c93369dd32ea13306a02dfd7bc330952202c384f0876b00318426d87824911578b4a701cc206dc89796f207b96fa3035aa594523a2ba5f454d6d42c65be9dabacb1b02dbb260f7863b607b2d75f4bf0003ab2a59fd57a345ba33db5a694524a29a594522089b682a96db359bc9eb3dad98d4755cf877466074abf7b72016fcc37eb6a2c96d5364dcb82e7a7d46b3ca55667f938d6479bf93125b7ccd03eca8442c8177a7108063c8088222880c3089dc962fdb0b047b9d9b3767505316f8f475c79a8e120c843df64bbc965dec23ca656e1e883dd90e5ccc9474904d9846c52436fb7644d21e9f22cdd1a813031b2a6bb3c8d4dcfb0502e6f806ebdb8fc0fb266a5bafc10dd122fcf0059f3cb3fa05b2b322e5f84aca181a545cac819dae58190334e52ae72f906c819a7d4615c5e027286097ee984193f0635c172f3be164bdf44d3af08bbf7dac951af263f99ee7d5fcfcc430df7ba6f6e58880ffc1250a790e92320b990e92d2073a03f348b82fd99b51c1e1adb9079be866e0b0619348852beb0d4c7c0a2942f53fc4a85910ac38b9528e58b4a2525b5b14414cdb60d83d79d439d88da0f52be7c2731013287170e8ff20899be7628924493f843fd014b233636105831255fe843717516112646bed073224b9cf429f5864cdf2273a0d76632bd8ccc81fea2c817fa139903ed1fe85d5c585856324f7c922ff49b68ca332441c83a8a9c0942ce193fcc1ce8e951b20601f285dec5a5861a56565050249131ec812e219cf9a7e7372cfc43b7d0373640b3e8684e3cd64f03411fb437dec49bad28adc5c13c3434ca13886c7321e9f22959f379adee29aa2c7f1b732cda3c90a4b6088d14c9b2f10edda7cac2323195cebd90b01513cc1a6f18e29195bae2cc616e5175a3cf9d03c2bef110cda20cb02f9db07dcc21a9b0c835126e8184070b640ef4a138799ab563f64c1eb3c7f4215f7c9af583083a3381688b1d12827bf4f4993fc63088f829fef4b06e4a9f3cba753f795cae609953ac5c5ac8906ea11f5793878fe9821cc6c9824c671249843fa5cf14489b1e960a4aa79f3eaa38ca930513062ee82bba8572b985348bf20847494448a60742ce80dd20640dec075933dfb7625f13470d86600b01cb6c6a6badb5fecc786f7dcd126bb7348bf5a7ba69adb5e22a4e2c9108a7f7da5f952708e7ed0feec3d453620c8d4d0d3a14debca3031ec872c6cc2397afc671f738f5e3c5b02d8f9da78d39ce5bee9e8af357b421e5cbb89d204f100152bad84bdb23edbd235b3106df0c0313d3333892c024814902930426090c052afef13ee3e2c2c2b24aa2d32d7d79cd4a49cfc919334b0fe3a080cf95303d718d3612f66fb3ba7ea74c7b28034d17617f629ac6443f3c5a05590e9498737603e56006390742344dd3ac95e560882c0758a86ed031566de0c926eaee9ed9a15bfa344dd334928bf0a77e5eabe7e40c2ef728f7ef472bb6a9d134269834accdcc81868686864607d9d297cd651bf94243698e10de6649220d0a6f5b81ed6ddbb6ed72b709f3ce39ef762fc7ed9ec295cb90b0de7befadf7823666de4eed66cbc0aad2e5521a532435241c2fab0c236be81261f2507b4a856118ae4ea966751886a1b5d65a92b59b952452ee53cffcf09dc6300c3319c299491b162ca5dd6aadbbbbe99535dcdc91a7e8832ce2e1e1c9e3113c3c3cdad4e6265fdae29c3ae7040fa0a3e24dbef4a6d5a0ba20055e257de1ec371efb8d7168bd316dd87a1b5a9532d87eefe9cc56ecec6d5fadb5d6d36a43f3b459e766ebedb479b3ecd69535579c6f0bfa70cf7d7e629ceddca5a4a774dae0b0bc8eecddcf87ed9748c886b6dea3dff6cf8e219c79dc72ff5ad58d2ddbf969af8d0dcbebc8d66a376f423ab02ca7afb6265f5c9ad52e303134cdb2e916232f9292590134c11e66d66ecd9a586fc59b7d50ddb0afb79fd774e41a0d041840e00a629eaae86158ac77dfdc5b07c2f9054fb28cf3ebd63ccc0c2d820238423ae3ddc3ac700e115144cf78a740ee14e115a921f7477bf4dbc4ce9aa5b1016fd49686b78ffef66d9a554304617fa4a1c1353aae1a80d24984882f843fdea90426e09dbe0859d3a7af809cc1653aa280ac19657a1c73469f6621d36b3d70f8a7e737de6b349d3ec4ad0421a5524998330dadd1ad7a6db578b4b66edb666fa36fb1adf9a6a0aa6e609cdc33bc2197a7e8323b37a7cddccbaf66b6cb1427c2d0161a9b6ed9c100daad5dabe8c27205d19f9759087ae46bd6c4acad27a41308593f8190746a3dd8b851af5dc3383af2506deb03590e92c831b29455b1237bfd75cbbe7e44e1b8816c3f6ab4d29e365ef28dda3f854ac27af0869cddf0f27db96c3cdbdbe0660f2fd9e22264d75cf2e53b4220db2b205b2ce50b85c165a56fd006d81cf064b768ef548dcec9812dd0ed6a5f17c2e6f2d878cc809409294396201037f7fcd818e7b3f1b8813cafc572bce8c0aa9e798a3352e86ef245035bd4f2dd7ae29526a6422f84fdd193522684284f0c7c8ada5b54f5651e756872520f8c0943e8e57929abe2e71a0fb4eda3bd9ff6bae75ed6b5c3b389b0306b38a65930b4a5c6348b368b6ab626d73af303ac148e84342f2b00f9420f0099037d4c58a7ac81dd3c67c0c25c4fe94ff4230c65692bda90d96e9806260aa114ca36cda2a76916bd26c66821d47abc298570be4a94a792276530ab2416120115320e54ca9313955161848a2064294db294512184a620a9a92c65549841bb29b0f6059b36830b241322a42b6529134284e338aef33e2aca2913322413b2842c5b0b8227dbca1a15cc66927cd17e4f806c3f2f1ec917f054d61020dbeef32338721fc1ac5de25430881cd9a323f07ea3be60df8f7a6257c50ee370db862f40b34e8f80f366155ff539f2e572e2786f2a6d1dd7711dd7e1f17278bc77e32e773f56ce0a5d77cfebba779d77cf89aa8bb9bb26c6a95ef7d7e53670c471f776df94a477e2982377e36bbbdce5c2b181e8dccd73223d89e3a1c33820368266ef7a783c0e9a390ceb7c715cddb97bf55e3cdee3d864d7cc1da6b2cc1d87dbc917dbe46e129066ff81b03a7ab96c3afb71a4eeeea7e4c6de77f023b7853087e6ef328fdf476ec3630e9aa74d86d93cfad82f174cd6a9f91e563369ce111e691e65167974511a5d20401e5df30602c8a3cb66d8cd38f629d96258bfcc4a5f451c572c7397791b378cf361236827ba68e62ef3bd00cd1bd6e9dcd41ed1023393c4b9248fc4f1a0384aa10f24924244b95e650479e4fa52f72885461c72a848387e423e5b37bfa10cf2803e99e25167284f399447985964ce8c18e7111d3b799c4956cd9a494d2242750649754bd7da2dfd0d657969adb24a794733529f8c78789443249122331d4676b2d6b8468d6609658a47297416960b4c0c8d8d8d4d4c0c0c8c8b0b8bc5c27270bb5cf7fe29b5eafedaebaeb9be2dca7af9b1e6ae315b08e55096b97578b33ec1932c63dd82106aafefee9f52a66fb75aad45dfbdbb01ba35dffd07218660c003ba65c2e32ac424ee233c9e40ee1ff6b84bc97dc3f7c57d9442a35feff327f013f579fa57ba47ba04bd5f91b664eb6b8d5a61cbd43a79771b141e5779fcc9099bee4b783c857774bf2bdf99bc746ffbc0f0d224e528279d7c643a18febb9bc9bf16d822b54aadd3bbd4bbaf54db53f0b84aa160d4e994bb9f4c780c73f7d2f7e5eea411fef0abe6ee2afbed9ed8c557b880f142c92a0692211b9a18181716cbbbf0b8f894804858582e303134362dd3a9de32dd41fa518f0ed9d078312b30305c9a86ea05ab69a858de34301ed1212dd3c91b5aa465ba96f9a811ba45cb7423bc85e8ddc8ca07a3c80b70356424d432ddaa5b238c334a8d4ee1d7346e95a32bc4295680bc0869bd5f784aada84fcb749f14a41fa5d427d5d1e6a13c9487f2509edcddebe2d55cbc5ac9ab91f450b3babf4efe3afd85facbf417ca5fa5bf529eba047930d122ec2ebaf5bdb95801df4a9a86ea851cbd91340d8ce7152dd34d212dd3cd295aa69b415aa69b405aa69b402dd3cd1f2dd30d092f6fba88bc6923bdc5f4e91e0a87eee54d88c7abbd7de88cbcb93edd53f08feadd7b47b752f827460b05ffacdebd7b740b857f5ebc3bc138de7f6262f3e01f182f6241de53f0cf0b1c8ba52ebb55c23fab185e1018340a2205854125251f175e17b12053d029e8e4730849cbb40cea28293ca9e641c13c72689c1385c7dbbdbc7ff7a6103a930a426742203a73a78f3695e8168857239cc2a710874974ab843f13be275c4fb0ec7eea6e12674fb3ba97c49944b3ba87e2e4d1acee2471f66856f791387d34ab3b284e259a553f4e1f4e54a95457ada8543054aa17315631700c4cbb00291723aaa465ba1509e788512472256fe48dbc91374116e7a0432dd3bdfbdca25b3f31fc9edffcdde791a6f158907df769a469ace0ee73d63460e09e17691a2f70f749a469a81e53bdfb1cd234563816539d460c1c536195c538472c962376448e98ce0ace113322470cc70b9c23468196e972c48a68994e358448919991232db345cb743807112dd37d0a755a84dcb56f3f08cf9d0ed1197943c2e31d610ae2a1dc7da59b1e9e7413f1957295a78e72d44f7eba49d7755dd7755dd7751d11dd5077959494789bc51d45fc9ac571dc4f62aa59dc4dc455b3b89bc49228deaf59da4762781253ab66695dd7755dd775975dd775972c1dc6a9a56e49c1939fe7c72944440221fc4624ec6f5a84dac703211ca78f1c1af2199a43524868484e1e211f9e4d8bd09e3ed62d9ced3fb1532c687b771d53c93b0e7bd934401c8b81ff5e44d318e118787b229ac6098e8158b59d708ed8ca643bce113b95ee635fcbd8c764cb70230b524a29a514e7902d636fbffd20ac1f1f6ab79f43434343424242423e3e3e3c93e7c3e3f56a58a19b1b7278bc17a778b6be55f0563fb12e150beaff7429a614d38a6331eae118c5aac60a90371e5821ca115be588a572c44e3962618ed897237673d4fb85a7d4ea2687e426e5c0683b292c5ddef4fca67b7753890b499f47abdc38add6969952defb7d61783aa5562dd3e1a442d47adf46ef4aff489737a887240dfcbacd5279236f64a3e8c8eb3aadc278b14a39a1a8a870787169a12165c30d3503a09189a9a13583a2230f85bac935a8e7116064c3a231d2a184a25db22394e78db812a7695a7753dac3a6f1607b0754693412bdd0abb80028aa69dd4795b8d1ada352b775b86add303407146a2b9146e0a84d6e8c92a5a8d2664b288e725cdd6cacd48f99cec900c0fd6aa3fa1a3de14efa1abe00f8385a7a8c7ec4dd3b6bc66b10c05338c63a0a8ea57ed218857750473d76b2b2f569e9a8c63ba59700f058005e1f93f17ecc0493034b0c136e7454e9a3d147a731f0393c06f3188fb1dce4b11e78d8a10031efa8d2bdd13defdef65877f131fcf0b1ef3dbfb9c9357498718f2afd9ec3201efd7abff7f62b8ffde063dc7b7e5333009a1900600160eda8c70280774ad79e038ec1e0d1b56fb8f3aed97bcdc6b6778fd958c52c8c7a4c06de29bd3e068eb1e09dd1eb451cc378c77bfd0a5eb91bd65e6f861bfa58fdf658e314eea3622778a784ab098e99f0ce08df96c1240f7f20fef0cec561cb4c0eef68f8d432d3e29edf687d0fecf45b7e8787c942054fd67aaff6711fd108699468a8e6ed69a82616c2e4f2e63d3731edd8e1b989e9b8e9f94d09dc7278602eb9c961040f0f0f07ead5b86e04765d373a81262612380a4b28d48b952a8542495181018306961887b1226219325e5c6e60c9a8a185b3c1eb4ebc8dfb380d070f87da75323102f0340d2607003c1600aeb394fb4eb45bbfd6d6c2d55602dce633b68fa6354307706bb4cf8bd1dbc762f5b17bfbfa187dcce28a29ae9f57e33ad07c6fb4027c87f3e071b1eff43eb6c5eae9b7c7bec7680f8fc562b1582c56dff39b135a2da5d6528dda4bf14eb57807f7fca607bc233f1e7628c04dae4183860e3468d0a041a312c06646cd00685aad9956abd56a5d9918010400003038e090030e38e080030edf8bcb0d36b4b064c8a841860c1932648434b0c4c0e261c058810103060c18a793f7fce6c54aa59292c23194cfeb38d4b6a14eb6d4f69edf9c4c4ca5908463e0e8c3f2033d5cbdafc3b7f338fc719d86c39dfa8b4f2dd3dff04ebdc5ab96e9ef54bc6a99798a532dd397dfd961d690852725ca93ddf535a8a7d9b69d1b8fb5c672bbe8b4c2939779943edfb2d801cc0e7ae0334488ac3dd08320574836e404b966291b32050d9e279e3ddd01700890213cea674e78db157d65cb3ccb3887f28a6644d3344df30edbb27d4e8e3d6ce61c8b736a35526bad15e7e49c5efac973727272c2d1bbd6ebace4fa1c6badb53627c7e4a6a360336b46344dd3b49c1cd2bf73da737260336bcfb938a73b78ed1236b3919cb9050acf2327781ab138e78473ec4df014da70ce9c358bde84734a780e9944745256b2f61c1d1859c33961bdf6099b791a9973ce89e715233ca70071ce87a70f9e3f343c73605bae7f4e3d6ce68aa78f4e9c40824c22b9e297d5fa38b6dfbfaed58e637bff958363be9f83a3fb7d0e0eeef638b66bcfc131e7b50ebbea1226e81992b5e7e0e0b0ab4dd0336409d71143963041cfbcaed933640913640d5795982d84924896b947b772ea7974129c1138ba55714ea540b7349ca37d3e873ba2569d948e96b3fd9e7ef2d0c7b40710515b39ff61b3d9b66ddbe8bdf7de8b398ee3388ec31cc75d2795b953fcd299b90f9b9dfb2eca8222559022c33a377ee9d4dccdea564e77cfc33938e7fe0befaa5b398d6fbde2779a274bd47a6fdb344dbbe3f6bb6d9be679976e74dbf078dfef1ad465ef689fa323f7b8ddbe7ebbdad5b4eddb66bfddbbddfab1d6b0539a13a42b08fad6ea274e5f0c423bd55a0b42e5033dfd973dfa519c8953e7d551678f618342d364434290354dd39c306529e381188e4828d8a0074338c3073c8083263407b2a8e26691c597850dba2c98e0b250419845cf48a88bcf2407a26cd6fd26dadae2c4305eac6851116bf51777818949b15ce52a1aae7a0d385c060de257a953f8ddca92aaabfc292914d4c989061935b05a6cb8c1e5058717971b6c6861d5208306961858fc0a8c172b954a4a0a0575723ac2a4c854542a0a8598cd662c164bbaccb003189632ca235d58ec22c1541ed0ac2334dcad442ac6858f945112f550d151c283054c42c3d12c1d7a56b4d1dcc1616dc3234c25379eb88866d15a3285f6db0d777978557f7e7569fa8bcb6ec1b83cfd1215c3e5068c8f22fe70721257a28d788af9ee6645396d105b44d92c19220d22cb5442b41d99df8eb0524fb9cac3abf0e8a27ae92b3cc2ac6efa0b3cc6bcb8c961e09106868d148ff218d88663f12b305e88a1f8ddca922847fde4a79bdcf492188a97af38c6c959504e43ca65a85ec38bb35a64b36cc02ccd62d520830616f1bb55b260bc58a9e0141e6d50f884c7980cc300981b9ec7864942829c1fefe8c9cbf99dab5a173b62ce15d78809a9e330c947ac90f0612a241a0e53c995e545871558ca8a4afe379056483015529571984a8f2cbf72f9ecd1acd661a51e96b27257f229152e7491c01a0e53c927a794cf249ac5ddb5f2e2210e87a564f0f212a6926129f9c3234c85759d996d384c258fb0cecc2d1fe78e2cffb948e2612ad9c3e30f4bc95dea2b9f3bf0278dd8796b82b5a19fe8f00d75144a94718cb3a456f008fe3bbeecd6572ebb25e2d1e4a3ffb25b38e091bb76f1b25b29783cf949c5058f28475dc5e5b25b2f7864c13fc4c0371c45dc6165738af95caa77356dca4b93633cba8c7efa0a1e61567ef2e331e6475dc4234d96c74f3d05c778ca5530cb55ee826fc036884334abdf220ad1ac3e4bac419421e2a081458c21aeb09812c5d3c57045fc6e95305e882bef2aa26c568a984241d1c86699cc69c2e32de1bebdbdccd00742f2288b8ab8e0828848899224499020191a9a42346bbebbf7effdd1053cfd08337afd18430a6f3fdae850faf6510aad98fc7e94437438673a999442d208fce67fc54d13ab2847fad3a8fde4f21fea921bc1a78ca4ab5c96ae1a4dbeba6435eb2b91a5596f9698124fcd9a471151e28978eb7c098fa614b2a18981d1a19ad6c7b3972c63fb6c971f65110bcbbf5aa5522b2795f086f7bb29b9e2e0e8b06409137a806d7945beccbb669e459b128aaee091a576b2d6c45a9555c2defae0ba733d3cf0e3778f0788c7d1c12448b8f4313ca9844f3e9a5e3a3999603ca65ce523ca531f4f8efa68f2130f735604231cc1081292a084b9042634610b273c010a5190c2149050a10a56902ff4f30a58c882122d6c810bf2a59510c252baa8b388aa94b0bf3eb8773dbc7f3cc04e42a53a0ee9e1fbdda3521da7d43b545aa54cc8382af8a6e0c993c21f0a9e3b50389c3d277cc23309933879a4668fa984c79a3f2c0f8f3c22910004349b4d21cf8a6640b359d1ac0868cec639e7049a33a0a2a2a2a2a2d96c369b0101010149987ce4c81198dc87e169d68e247afcf0a359fdc154b26b05874986a564174a1f63d17ac952eb755eb5cb95a61d47b3b4eea78ec3a30e29f9e27165c99233e411a6922d1e7558b264099901cdea3fa059fd229ad59746b022491ee5119eed42746b1ad902c9cd2895e46f4908e7373c79ba19d0f7c891f6c932860e95d902039f2372369bcd66b3d96c369bcd66b3d96c369bcd66b3d96c369bcd66b3d96c369bcd66b323720a1909b3a2a2a2a2a2a222393b12f637241c515454d417a267ac66b146d64f8eacba290aad94c6f889c9f538fa1dd331bd8978418f53f98e6a3512b79b944827d0e3beef8c52e752606829934a25e53bdfe9050c4f4399c2d177bfd39dbe78f1d88e868356c0549b542a5f4aea3bf7f4c50aa5e2d88e89ca4ba49bd25058beb33a3d3179eceb0e7a8fed682b1abe6322a9585462fc043eb69322dec3f8f6d88e3d4a7f670785831a7f5fcb340df8e2da3293055712bea3189805c4e187f13d7cea44bc4adde3145e59ccd232f3da0adea930f0095fc7dd51f56581b73ae5791dbb226b5c72498699f28beff230be2e85946127b9af5355640dacaa640dec6b0e8592e775284ac3c0afe6e59a1996cade75669eb02dcf1a18c9db4ab206764db20616ca1a9849aef1ec3f5897670dcc04be5c2b197692e775b4dc309a1be6bd5c5b86a1724f1898271e6129b93fba5640acf296bc5c34c35279c246b9f10853c9f3a3cabbcc128fae95ab3c0ca4e48ebb9bd52ac5231129b9f168040f0f07328e6b2ec9381850c9138f44f0f0a020cb7abff0945a9d25af2c59a2852cafe39b36882769bd2bc5c3c393fbf57ae98cf276d708952fd7ddb5f28269d9fbeba5e365f02ed2100f4fd631c9305e2f9d9b497fe98439bceba672293573e92f9d2d9bfed2216593bb5628f9f482d17cfa4b47cb277fe98019751789878727eb9c649417ac6694bf746a4efda5f3e594bb56847878b2ce29ab5cc26c56f94ba7b3eac5754cab978e299374bafc82d90c03eb74065f8830565eac542a29f2651e259e8828f265fe24a2e4cbbc8978225fe64d624934912ff3a1682289a59118829fd77177b35aa562eb7831ab12ae1595c3525ca49f1e951b93bb4829ae150facec98dcf4a4dc985c9ec564e68df41def61974ca9c77682b687df219dd47291543ecd7bc984f29defa9cba7bc7a9392e9f4d80e0a0eea77df39394acb455201e9773ab9f79df0e05d2b23d5633b9f0dd2e93c5f323db6f39de020eda3ef80f75ca4254b96e41f9514951b148eed98a0782741386464fa1dd2518feda48437e120eeb19dd477144662dea0be333ae9b11dd34d1edb293d0507c5765238681e0523316fc2c77648f71edb018faadff97e82919837a3c7764e137f58b68c0907e9cccce15bc24117d770c3241c64716c67848370c8c81a067150c53b1f0ea2f8849198371d0ec261921bbf59261db7b52c6c45c29619e54b5d47a63fa55ce53253ade2d7eb757294e9a517f8d1bf7b77275b9514f124a2a04e4429de2f0c49e2aa59dd07c5b3441711861363686c9a95a2a9d965f0b620842bde56440acdf112076f23b2fd60f6744bf711ddd246e0685617d12d72878e699866f5b38e712706791e4892fc72dd9d212274f8d09e9e9949ba65660e75cb113d338f74cbcc9ce1e8d62cd2ac9e99431ed033f38a39c50f3d338174cfcc1eddd293c7cc4208a511a600ca2fd78e10a783e7c5b96ad6e90ce3b06bcb77ac0143777c3671bc85e121855a76b79c486641b297a58c084746b35c931c199a1de996fa197056c46648cc15dd52c129baa502e9960afee8963a032ad18347b348f74251663049cf8c880cf5cc48a85b8ef4cc4848b718e9d6688a66cdba350ad2ac0ad433a39e20e014a010f00a50081c0212018b748bfaf4cc88c747cf8045ddc2a36740a26ea9afdebf8f20d10848b72856d19b8e33ff934365c32afaed3f2638160b9ab736a569281dfe3131e1b08a9efb8fc9e93193fb9f93c9eb4714bb662ebdffa3c23113ac9a5801f24605c7824c78042465c323a094c5a31f281a1ef9a03a3c5222472c88c3231fcdaa41178f7a34ab1ee7086a3c9237f51bca8d4741a6107285d0102245bab5c516478e6c468cd8d94c2b52a42342841b32c4fba87ed463e463a4c4c867f4a35b40dd32bd7e04c4c6c64443b3c5c4581818cdc5a563b138963c3fb2e41124fa0e72516bd1bd6017dfc713863b4ea79e546a94c4b8ca2011fd1cc1d13533c52fd7a8c355c23814c3b41c1df2c4aff93b45d7924cc22f57671d12e945afd3d935e71932e97349a6a2a437ebd01bd30cfd9a778559a72ec9d3c6ec2293a6e87ad6d132ccb5650aa399ce3264d249e2a883072334d283108e3e823cc2500ca2a0a8e48a5a6a7b983a45230020002000e315003030181009c482d1284ce238d90714800d8ea64660521888c324865118658c210a0002880100c0108099995255a1ddb2039b943e0405eea5f44434f1fd11e015228979692bab40c017a25ae0cb7fa0c8001a1ee234f8f26a20c735409aae0df3d557432e32b92793ca73058aa72c21ae36fcc20edcfcb732c4412d2a9b6b1d60f9e2a60fa0bafd4a6c4a4bda9082407a0a224f29d0965230dfd67cb2fff5995c732b1b5c62f0b2c199b156ebc40223e8ecdfc58e4e4d30c5362a700cf0ac216760680eba60cbca93f70087fc6c58e4fc4e457f16ea077cbd75da136e233fad461aa7f819971f8ebf642479e18020d1e298116ef473305438a228af2e75cae48b017c57019159fa77c2de9063047128a04fc88718738e2a8a7e0b46d3d6972f8a403808f8f1bdfb4f6440ee3a6ad2eb8c28041478fb8d369b771bc6e67dcb3a578852ef3bc5d1b33ad2fd12456f3c5853c7dfcd470effa8c175882b5d3c1509fdcb4685f50c55a277dd1f1dbe060fa6884139f66d7952e8da0716e177b52a527a81c00a8a84a32dfc0f8077acd4ab15c1c2497bf46aae55731e0bac191b2804700aea303088e2940ad5219efffd77c6ccd310c75d3bae952a6676a55c87bd64d426c8ebfabe078fbdf2a7884bbd18d62746b0eaca3be9a74eeda269e18488aa992121ad1deca8883fea51b3e7c72c6b1b3a2c1e97383e7ddaa05e6788093725f2d132383a081c4b66fb6bae338e8d80b2a3fcb03e99f8b4aaec3e191167dedf1071ce5b88c4fefec057e41b49bb49838c88f252c022f870a637c74aa77ed949e8ab89f88cee53eccf67604508fa35c48cc29d20e1f5bb0d20ff0980ccbf6253662b6afe83bd7cea5047b693d8cac3d94ed08a683b79bff86eee59a289a82deeb98c20c9122359e4f5291cf3c3c4bef2b74fd03e79e500109e325b28b6605620c659562871847d695be70c256d47698384fbc459b1a49e201222396545d510b63ab0b68f97668c37d0c66b2a42ab1f678384e01f391f344a43ef6503d8f87f8fc9db203d9b88355d26ba155a1846347911ca5f41268d46cb2ea2d7a128fb7aef9b4bc0d59f66f5fb626f6041bc54cf340ebe05170fca87a414a23ababd0466b0fe01737b037a6c2d36c425bf62652e433995bdc57226e5856b3331be8fab0890070bb019c99a4ca13113c3c8cdc394a860651092e6d4e9d517f3a5ca147532fe9db2621f5faa58f0ed117527f83dacb03417c4095039016690679fbeb66cc0def1c3855b9ee12f94e8236f0a2e7ba1efd19c59ed99508359c8d9a1b2c46f53c6e27e47e9732cdf914f8b3fbac5f13db1548ee825d8047dc13cb51660709306ac566aac2e3232de161355a737d96561951d38c4c9ec7d1b4d7d99e03da086c43c4364700bc19b461766e76309c49efc4acba9472228927a085df0eb8aa822d63a2fe4b1f43f26948c76d73f4f640c10ba0ab6ea98bee58c20a510a16a5dbc050d03615afc934c01a817d331374f3d9a746668c33b7238b7990b545970f0b029e392931c099eb1d078297320b375aad63967efe11267a9c06aae63a80f0d72b79c5ed63c95f0dcb36860d8f022c2eab847106d685f946b308e361effb9e9ef2a42f9d4c4dcfb1560dcf7fd9b8223b9e31d7afcece764a27a44086aca13113ea580e9a02b695997c3d9d0496862b154945c6b8adcc44dfc8ae1782883b15da1aa902b3c4f8750792b8e8a41b5224174561c4a35a18a15d3374e5ce7559c310d44a18c542b136b33f8a50a05ead5775e1789dac369eea5bade939312ca0d3ec56c09672121be24564245b8442788ea0ef08588d823877cb339a6f443c33dba743bcb161f8ab4d0852401ac3a3131da16b79d01495a189b25328283c3ac8ead6b00ad729f402d86a353d3db4f0519259ac9410bee464f5d0e9cc70ba19d857d9e03e44daa7528dedb3ef04034a979465cc66f718020e0d7db32660dae91d5444b0117158cfdcda16d4761dad6851992eeae4f498b6c56586d04703f0c289788211c843586de53623e78457a3d9fcfa661839fddd6a60be1986915dc5e6f50df9165bd9e2ecd64ee05ea1ab1881b218c10156ea7064e8cd8c04266ca614506584ae9b3e7fcb44105267b4fd0c6e0dea02a88d4b0ff941954d5aecae6b0820f73fbfd919f926a920eaf6ee96e8e208693ace00abcaee4830043a8c6409dad9122628fca9f4d0dd0d82ddc5343cd508cff4e175e6c759a2d325a0b9d878c6d026dd88194945eb9022b0acccc9b823c492d30c4842f1ebd33233735f683525a67b2faca46424e4bc51e41eb93201c0228637dd23032e0797993d6a999fe6a31e370c82fcca833915b68adc5a6df3b424d67b5cc38d0bd631dc36205b89ffc4739fa1226b4d562ac628de21f1ed6d11f3dcf74f3d5bc8329d146a9f93522264a232bbdeb7f46679025a0891faf876a113d1d9c029e0e2f76bd6a59574d6e61b5c105428bea17ae9b365bd5be6c4b811557ecc552d574a93a2016e719a7134bc55173c816b3e9b2a2806f8ab918c54c646338a81373828d30c74fe97d8a12f3b82198eee6fa56ac05fb89e1d95595abc4781d5669f723a5d8b2154452475c6eac369410a4cac788773c48915f73f9ccd06f600e9b0dc287377d77f7e0bb450182311b08ce990237a1c8a6022e2f87506a1a350979497e7a48f87ac2e622edc9800396fb45932416ece75531d868e5ee939482482d64d2a30aa8893f206c6ad81c2eb533997c0d6a6681d2c1cd8e313be08ade140cca85fdc96a7cb0e62fa324f410e0fcbca975ea54adc758f7691e8aa21db6556d588d6592bc0e460e86c35a23f9d747242258b1f0b5d1dd7f45bdb8262d5c9ff48f3e3660ce4a7ec6965694bba36bdf583a62c9e071dd6513468c9ff63dd6345e70fa79a5fa465391e23b9be833fbca49e7cfd6db3714da28e06da650767317879075b5926e26bd7bc6248f7197b124f0647d8ca93a91a217e0824645be6e09f5ec65b49943b119f2e1845a4bbc248322208b7e86b8c70b1c75f80995b31cf8712f053576f8b4760256bdb99f7e6424eb83e28957e71164948ac0f591c41d5daed2ecb92c7103816a094a8e835a5b7c57a0ce3fef839cb3b2ed976987334f22a8440a91db19b4918067e6d0a6a80bacdbb333e861b1fccc037e898dad56dfb6f021dc5019efe8655259cb86b630d5ac25086ca239d2189506a2d39f7dcb0a914ffe65be7c95f8e661d8da159ac595b4b8cda5becdc8efffc76f4719dfdf6de90ecbc4eb29ba015b8e4fb3c3f0ba8533c85340d328882eecb984927d467a98c358a586331d1f8293b066550ecdf53f9b58b2226f41f8c446306db6d8a5c0f93194d1c8a456491e1c8025af2a74782bc84fc42b5be20bfa411dda260ee8bdc4db5bc8f710cd43a86837da9c17cdc7625e4b8ea3e46f1cd030689358a82b1c7844d8f5e104cb60cf7c1a5230f486a650040655e5d28b0e0c2239432c1a8e3c0efc5b9f183ebed8eecb6c18deac16cb70250b7d480c96197cefa183258f70243c96396082812047775057d17de457c1f764f88dc1697cdf7637530618f95ff2e403703e3a5318b382f8c907db2f0c6cab8aff1c324b7ca39280b44d822b56765ec878f619322ee653bc656cab8bbcc93000c09c726de57ceaef44315a2a25264bdc8a861d76248b9bce9c6a73e37978265cdcfd4cd55d2f3a31034c6c6285f44ea6f9116692f1e8390233f4ef53ed42cfa3bd0ff5cf1c8440d7301a02d7b64b625974800ef5707266bf369f3ceffcaa58c214bf1d7ec95f70f2b6dd37da2be7479cd16422701a4c8578f0038260f92f9f75790e50eb8daa60db50956161a9361f08ee1a8aeedaa393a6d54405398de0ef7e975b00535ecb593fc3732c88c0459926f38ce07cfb8299ad5224f60267c84f6df8d2a043dc85b10611a9fb4af6aafba9da1629f8bac6e87e72ba0e75cf68b92a4dcb2fd4f26dc90dd647c96ff90c7aa833c8273b0951d3468d701defe41a2fb094dbb2fb65fc951f2575030aab606f2b8a451286bed9bafa1b322d6410b171d2355d3122d4d058e7e0664bd1cf474b790b8e38346cbc032eef8913b765716bccb38808071596891a006230056ca014ebd8bde8842b70605d93903b35ea5378a69d9d928f9036e74e7fa96971c996895ee857844a168eff8ea53c4a436f66709efb118ede59e0cf6a6d287e03274f6379134433330e3be4fe72092674c986c20b13124651faaf15e911cd331de82a9bb313bbabbea4f9e1dcf03a968da126651f0f40ddd9fa9ccf392ffdde0068deaa7725ec785a4159d586a8535dba730fd0674fcfab444b8d5e1f029cd72f0067affb4d2a0f11d2802e054418e1f1b0ac012a56745aa4de57bb52f3003ce6f1745ffa2b8f2006daa64bf301b505e525ef952dc29599e4ff75d603718b3e41239cbfd93cd05348cc2eeb7178f3fff7069c77811f3cbe93f28db4a6ae12acc71294878a14ff4d91db8a6ba05f4bff9fc7c61e3970c6ebe14bcc6d60f8ac9754627c85b34a41ca04406c56d1d304e98f3fd2df21ce939b482105e16635428b8c8c859415293b7ad9e517cd3f913682129e4b42d3167b4a11b3a5e9a8a559c4917d58094d34c0e6940bea0d502643078258ff0d1067b407f6c42673915bfeef4193dd487a587b47a9c253f7841fc3718ebe5f96a8c0ce79300f1056fbb94d810a148537b1259703bef33f7faa9d6ae9119e2864f875ba0a080b7cf3c9d630290ced3361cdd2447035281aba146987709259c7e194f0d502f8239acde675f8b9e691ec081b74913d701fc1d2f6a4148b2677f6e23ed7e3f5a865c095ab11f152dd67f614aa1ec39211119f6a515816811570f6568d587aeb794f3ea649baf4c73ac4ae5e11d996d1a392f357c30c50634559e7477168e775038fccfebeb2532e878dd742cf304d2b6c1029ab85f6b090c7106eb45550b1c9403da07eff03c0c094b1ab55c8a356673c04cef8d4b5a74ca51235a366159c0b56a6c933c015a691b32438b5c41c7a7e06654bd6417dd71a22bc9bd4ffc067d97ce8e30031942d571bf153676cf1fb7c8ff67b0b687c4a0b504f4c6575d8744a4d8f83a689dc11bd646dd2fb5dbc22507db39bb95504d8ead3f97e867d6b821355a9743188eceb5b6af75c22f3c6b6caf5aebc0d1def30f59cf9cd1a2e8c306e047435fce4fb9bd7b2856b7bc524b4d7c5eb4a6d286e4a6bc12c542b5c0eeffe4d0411f08dd8edd0b91f6667f14f5cada14d944067e923d2da84bc06f71dbc9cfc9f1e727f4691d28becbe8821d0bb2534f31c2d8c4773e200340b77f4745ec19a1c5779ef009d8b3de3941f8f38db5a081bc3d65516cb5758f70334f29bf4432816406295c249094a5e387da5fe38172028aefffea4fd4f5b8af0bf09e3499413d60a0f3cee9ddf9e4bc4a60244dbeff17f25961105071027330d942db92887d0011eaa6887c03240402c29790a4c62a0b9dfda4f2f194fe9341a5ca673061cee50b6c6cb2559f01189087fe8959fe1d8310ccb1b126163818d64386500828b63d48302fadc9341eca6d4b1e26b69a802985c99dd5c6cbc76f7c5122eba243acecfe865c7e076ea50ff0bb409886ee6d3294f4301dd1718c354c622e9d7af20550baf7b0794a433e562e079e48018354031b9b54322c5c3119a8af9886ffc3ba92c688a58e07aee3884d9e13157225a73282de714e1a0383c0983b8727686d5c997228d4a37230232c728a2aec94026808cb842fff9e3ec306973ebe70fec47bd23b8f4e8f8482481f900bd3cfb40460a42a46d9fd10a9d3587ab038764c37a9aabfa0542ce6493d30670290ac74173adaedec52236860ea75b04f19ff7de9fdbf67b6c40d3fcee5fc506009e45bbd16ea59bf02bb240c77c4dc5926900187901626a2e9b825579e02a6b20040b1d7cd67606d3a12f64c6ae90b4cb32d1918032133e83bc79da1fe0c910afb950ceef42c4a44d3ad0da166059282cb743ac5e9dde6c2e3bf3ae8f187e526b3559752c2f59506b6584dbd24051867464289034c6d464ac2f696fc35607cd1277386e3b926e859ce91b43a07c210c66f887db2900e2c2023d70b25601b91b1a4272592475dcb8dadeddea4c0e7bf30641e8ab4180efe7d1e6abbab62c571ce1a760c9e7d1298b60f340c107f008b0e6f0b50bb9d4e4c2d0362eb56376ed7d618435718a28340d87baec7337144884c103aa1689780d0c0b05ba74e93082db8aeaaee2ff652bda99c166c7401b69d0c6077565cf405a14cd1368673dd2a23bceac6ef19c4eaec694965e2f4d7a05adb95f41fa329c8b23b4e69832e9ddf7810c3511444cdd5a049d531c82cd50e17fa63d501a5e500610a5696fa7e6310733929be91bb80047f45a540f055baf6be907485d85d9a5d989862cab4b201cb4d8463a66ab44839501e663b0a5097c3513bc0b83f28a035be4bf288a22ea34d2deefff5d10ee42c0bd5f1fa5de5e75abd547847b54e771f1d994e5b2426c9aff11ef32352580db5af90d7df80983bcad2acba8865ab22af8dd04e6f610657a88370febc796f465aae4bbb726af98943e49693bb8fef1012ca20963987a45c024b8fa042af8ad82835addd184ad51624c4142e8a7948b326dbe170e09c523ccfd412e8f4e80318c5fb1258328c520a20eae99a51001a6bf889811e9a19c105756cd49d491be4ed304fa4ce4a64fc4e61a45f84c08c2e0954329788983344c950777b90e67f5a36ecd36d03575151408882e0294144b77c8aac843d5f1987b1ebd12ccf7b8f00bef76c50340de801a50757ae630356d142a04644361244a85ea830dedf697990aa74b8cd977059b3e56dfc0b39746f3a220cd8870a30a033a8b688eba0b33bd419f4fcc7f6316fa7842c61e8f2ed185b90247d7620b31549fef29bb28c2d41c924d4488331ae8f1cb4f82c660330d62f0776566fd155c554e01dda60e091a6558ce84f4661a46f7ce19e0a394d0a3b8949aad7062c5dede26c5c2c1e454085463fc978ab80ac17c54be43440b8d5b0736a426ef78342d535da4110504c6bc64c5dfa7486c6fa4cf5a548650335a3a0e4a3a2f21baf4ae42185b14cd0c8016b29ce63b9d2a2b5b2b6a21778c1625639e22f83cec65b324ac9c8251d0399c690d8d8f121562ca35494087aefc0366d12c59faa470904bb10cacd198db8e5d0c590949e5a602b136e3fe3a2db7959dec40181f63fed4231c151c8eefd2d81fb87db84d413d4b7e405ef79de1c716f311ae0dcfa33c3094fa037aa2be200a96791632b579e0e828e19f9f232604755ab8e88e9f07e81c9b3a663ba70a572295d026f6ad27603a2347f404d420994a63dd4cf20c7e97bb210453b3c0f5b4569a5150e0fa891c306c8c5aff31226121bb0b3e0c28b3eaabe5f5d8801465f9eed30d12292f9889e34d96b3f4cd6ead425322ea55f728eabbeae7495bfbfb2efcb8cafb648544b82eaae2bf8b4d7551fc773fed5c74a320638630bb39d4e30ee3010a270b3795572cc534a195a9ed4bb04b012d0fd87945ab41064c8af45b4b5b133a7cfb95d1338d0176b897355a5de286ec19b2113612bb1c0c673b1fd43aa345a573f906ce6a33bc804dc84e2afa241a14b8bd451443ec03f148a87021b32daf5a692c55e0164889e0351e0c8539245733a3dbf312654ad59fffc6eca78eb97dca327c2df7cc4cf4bb906e92b246af7a937b1d8094896c8b1313a5789884410605c3c52daf571d52c1e3116f25de1ddc6b8f3dc75c2ed502ab24628f7192952801ea0617be8e9548b6dd7327a624c77c106e59a19bdf6186a0770cea25d26d4d03be3f6229e919221823ec61a76ecb86b3c1ec608ce0b9c32cb609046b8597991101aff0f36c6c820587a89820f122cdf989a9d42f12d62ba25f3ba03ac833f3bafee1bac40ea9648fcab5d7c727d26ad1f6c67c9ebd46d99c77ddacdc9715391506afbc5025561d56bf483c7f8bad2d030b77f27204f2c5b9b244269e03ac97cfb3704374c548feb4bf4aeb9b52262442b282139bc55c30fe39874104f11b2b902aba74eb4430088b1c2ece2ad0bcd751a3849ac0f4ac0f7a9b26a7503e5e0069152e36590c401d18a70c7b330b90536eda54a17a769e1e8100c5efd00cabdf536b122e499e248d1d5a8f3464cc1431aa8c9fb455a7d3cdfdea5633cabaec16faa23fdcf5c13423cd3f0861210bc5ce038a321200e41f187a10c80c1547b2f089444485a5da02fea7d2a1224383fb63afe5cfbb9c90f4dba1a71586922e4ecbe1ec4ecbec850d648c6f94c9efbe10ea9b1844d23e1aec43ce4ddae84fa32392aac9f49eb5bbdc55922f93b3e3341453797f8cc0a60ed553932fd048324f4434006e1233f2a7ea76607b74f0702acd6fb93f238ce461b2f92b4850e3023b8b07cefc374c37f1de1315ad72cabbd99489ef3babd02a6732a7f4389dced7711969d270e4c13c278a0c033218879e9e1a2448ea40cd107f3c2178c913bce12d109b2c3742a05140feb4b19490ab74af5669284437e0639b92837fb946e4676ff0bc75f8a181a258dc513aa411a073ce5260bc9089cbf36879fdde426f69c7c6d923c570d9691065928be54d6996415facf95e11504db83d514f423bb56bad173268af40999e2b213406401c17ebae42f868dece78d0d76052b08c6323e23ddb9a169cc8b0e1596f15a4f1894e74ba5da3320feb60a2d10161cdf0bc5db3ec439eb2d75b538a6d578dc725b1a8611dde73a8c40fdf9a3e663d2356c2262a3eaa343a2a125420e868443dbcd6d78ae633bf38426567148fd1b375177ba0594b3cf4742c1078ddaef167fade811be000b2ffaea72589f11c9434db71aba03a80b97bf884d7837addae7c46a9594c8c40b94b1ca6bcfdb8d1623403c2c2acf7009554091477e9f4c19c4c8df594e0c9307e90d49f4c1943363891f1003957ab2fe02443d25bd6b8330e793840ba1a2ecab5c43dab100b295510c7960e91400225ac73a0d4e003474a64d4b58ccd1493e9657931d7e1fbaf10ae3e9354de89782523d0355fd7c2732b46bcfb887a2724bd43ccf31193c3974089a9fd7723aae4c8b43f226f59cc907274c636ab83453ac90206d08e041a8d09de408769883384541fbe3e527a5f098fb0cb6bf9e0d5fa9d01afd34153f51fa1333d745d004d15f8fda88a23450a7a1db38d0956704bcb76a2398e26600aa3637088504e752aa8f7544118c2528292e89b90bc2f9960b1660294cba4f2ff8c0caa3398f7ddf1a09f3f0d7c90a64a554ad55da8c76c9e8596dcfd212996c9f61f13776b0ec71b90514a1b37cef78c8eb33d87835b64ac4a8c3dae7d8fc66f9193c36d6494928f1bfffda3e369a227c5d1a243b0f0437b2978a1c3293560fcd6989ca9cff565514e40bbc0c5ce4173476c6fbd71fcec522211626bc99b197b7d014b0805fa638b9181e64956f438a708c4c7e6dda82d00b79ed1d2d1e1ce37d2e274267f95de4777ab4197da17531147af23d22469a446f1f481c1077dff8598ed898f073a4e693de65abade292a6a06ea8134e3bba0f3b9c2798473dcde034724488df3c6fd95be783f08241f6884d9af62132eaaf528b5a49a840612ab21cc2c3605c13d9dfd9a69b406049b846ba4a1c3c65067914909c5d299bb28046ec7154d2195051182a5593164b0497537c2a8ea13337e15fec568ce36fe2a08e94684067213ba414bcee102aeb13fcb6d61e862368717073379e4b01761c6acabc0f2ad41dab9d5719e4bf2bba01d7a7a4b1f6d04a1901a58dabf99603474d09b24e23d52804bcbbb0bef9602fb4847b8772af0b4f126c11cdec0070984eea50696d6df26384919f22789f89e34e0d20a5f449ed113b0e5d67e3b63b00044b4fc0a302ef8e91c0e483237ff1d6b490d70316b06968ba69584c4cfcd1c8d3946d6d09423612bfb703cd05b8b62a415c0f18d551a8dff84b5e8e22949626561997a10072de339dee505dd2135c40af20cd2b871b3486ded1422b1ac1d43f3fa3142d21e951ad98d942165ffc99b173d0a2ee78dc5f2e793678b0e080611bdc385e48d50882027e7cf7f7a0559ff5204bf422002ddb98d42d5307f9f3bdb20355764a2cfc1729ab80328ef0cf9cd042ab11f9624eebbcaa82c2514f43445ac5d7d30f066e931391bbea14c4bf85e6d4efae53d03a5ee81e0656abc5ebe9000846b0e70550052cf0fe717767d625a43f2428a285db48a2c7eaebe00a93f160af6c792bfdf3e87555c7f54156220897f611740784109dee83297fc0b09082824604630ff00cefa837e15d6f0775d59986007fab8483373b052f71fef7ef9153c1fdda0ee4c1fa9160f9e204477ae8401a55b92c3f5e15f7c0328df4560859dc4b34c651d939825ae8d9c6d5da291aef894f5f12b553466d40d488339a1147d9ae3a2e13d9718d2a5fbf1f23daa56e280b10fd9bae4087ce2dba3bca84e320ceabc5b8a27342f1b1eb04507d0465ecf56162dc77b112be9bf660fda9e80fee2e4f49beb176db89914a5a6f3224ac05505f319c97a955ac599076965a84d1d13e6031b02bbb45a8ade1d034844ec65c0c3e31bece89452b397de41dd02e3f85e092a70967a65c8abcaf5ec438f36c13ea73bb7b6e92c32a07584580a65e72f117379a9aa46abf80976c1f610b79236c129132df1c1449159230f188a64a6890151fb788e61da6d689e83e54f808bba51150a981e6de492525502ade0d23d2e0ede7562dd26dacce3a96446ffec923ab04595f6b3b14112ff1f489511eec9e840d582c861b9d0b3d728c3d9079220e512639cbadb862eec4acbb4855c619425e159745dca95a3bac11bd70cf0f97ace54a1d52467bf3b7aeaa160ed59ac46f66d35c85999598f321421f2a1a26814ce6956103574ea08e79b2f1df0fa996e1742b7af5fd6235ea305864c0fbde462073d37b809a1c84cd2ad065866f1176919f925c8bc6293eb241917f8abde1f2a0fc57a3fa275a874dc3ff88f1618205e6657e45f927d3a5060e7476c927cfa13c6b85d60f2509285385f01e2b88c19071ac719bdffd6ff507ea507f4a9355a34a066a7fd2f02e34433ed498000235217c825a7ab43ec37461e62c47509375bfcf251d8675d6cdac845dc2c242a1b46cb0ad223b5fc13e9aac2509449ee50f685868a61fe2b961c2352a3a3510449488b9226ca6ea4f978114b0cca80c24341d1f775e4614fb9446e194bd0df26b5d1b5cdc7edd733c68b87919c5190e545b1651bf49a5120de1c8a2586facc38305c3c856ab74ed5b6e4650ef8587d74a599c897d1cfc22b084069822996c0156f26f2bbe2fca273cc5e8aa86af9347bd6e976331bb209ba0d9d7fa43488ceec0aa65e14b4451844614d5193523a7401b0474b870d9dba51a34891dec7b2a8f595bb03301f87edb23c67a1a7f0aa6d27fe0fa70db89f8fe4f81e5fd9cd5c30240118b42682aedb824f061efbe8080f1a7cde0ef6f4390d3057d2fb9f01e0912a7b17f521c8be51eff5219dac1eff391cfd87acbd440e82197cb82564a061e72ff3ffd836043a14ac16cb418554c87cd65eef432a0e35f8a210299cea52f98704dcfbcbbd7f53e28fde851ea284c6a4de222dc978aef3f79bec5356c040c69cdd5f7250dafbde6388213ab73cc40a205f760069fc50e85ed4ab324023a48c8146e0902f926962b3c88e300dc441b2648a88edd5687f660b65357e8f5b678b51c6bcc0d604d37985add60d6a95311c34756a66994ae69c6079573357a5a417c3a4cb18f67fc0c8fc6df62a5c31f1bc5fe822dcd735fe6e3ae60ee1557b4d2537d904e4672517c94104c7524bb6a50ed724d473703d707a9309f660558d9226d5f25b11b1d2315efa3a14e99a7d2cd3859ac605a33ac6a124ee1894b7fd28c3804f1900986e10a8832ffb71993ff22896d9f185d6db17d63b9b039c9bb100dc0f8e1eef71afc1cd4cc86bca40479083d1b6f680b6f17cb4a176fa1ed49d4cc48807d598b15e77548b708de16e0ade450d6a79264db5e69b35b17d549f240906a192000566ffe361b5c0f9b3fedeae4a116961d75e60be0014f6e638d9b1bf90f2d333008819008318e66160ee10f37af19ecbd33ab1670660d8f8dc8c7ee534bc66d500719dc38dd8d509a3610ee744c0c62ef2e6e8fa5944c33aa6241333f1ad29ee65fac670caac5820416ba8470b6f181819dfabe1ea754fa1dcb3ce692f8289f6f109e2eae3b31ed89f9b00233d5f51e2f4cabe50cdcdfec9561f7895b9bdbc4e7737f51ba01cfa8ccae0998fa2cd0600827d9d4642f96ea48ac9eed2f51c544208c607f49d11de0ab59dde0a2ebdd94b688c734bbb64c4719f22afa3483b8d5f42d7365a6ec2a94d1cb015e71cb83a102428358fa36b3e908e6cb8913ae3803b061d9498787fa86598e3a6df4083ff6c78e9cf3af715e86cfbec256af0bc47d83b343b2fee7e067459c0bf6d20cd646fd7f036a88418b0e424dd27d98a2c4383d942425ae00b16cff087217aed227a9983edd26bedaed96750388e7a8f6fdd2a08b8e650b1b72f0be3f5f9da6f825e52fa64bcaebb8d08e8ade1eb1c550298e216a78cabe3816a673818eda94a94c6117c8634c7ffb131d9315309100c8be617423f7e5dbddb6bb5a5fa64a0ba967c41c29827d5da21f5824edd398861b132fed212b2a158ba873a363ec74fd22c16e8ea46d98fd5065246d2359efc30b1cd8a1b3db9c3e27599a70f8daf7103576e42562bebb1fb39a5ed24ad1eccf7d1e01deb62b3c685c02fa3c5dda77097f0f996efcf181b961b9cba14a57e29e78ee58e78491cc5ffa13b518409eabae12861559e912ba7602376660048abe714e57d0527cc9d1eb2513ed342ad0f16f6dae26f129e8820a514304222e7dfbd2fb9dc51802568308b927efe79cf359d611644a0cb2c215f268d2d6b21e3c116bdf941343c95c6f60156fb6e8bafaa47fc14bc7f135329ade6af1387752aa4d16056d24729873143ea9400bdaa8e65601d3ebffdf7dac7ab6d541c89858d32c0a674d9c8275070d738087825672023552606d0bdd50a542b2bb6d30e4e3c00642223a7f4cea15a202b954c2ae9acef88fad81c909b188f53ab000649a5bfff50373ba5c5f27cd8b4049237ad401a5b20303181d130901d0375e4b53fd13610e337c9588c4ed51feac7f4130c27c2eb5c0f7a034b17119e10c56847dc274ac61e5e33fd7aed41ef16f9bdba29874f4bcb197eff62ba2f85cb57de781df5ecd3caa67647e4b39f99173a2bd4ce2b00e95b569e66ef619d560b12bbcb72489b6042f785b014e89e08f85428399f640c38c00482c91cd399fb344c3db6030fda3b1af1a95579675b7c90791ff1ca23d5d7dfbf990664e8a669d61f7a6b7dd7c5d9a80a9d024abbbb5e60f84976544faccb12eb132d625cd253ebdd366edbd8c184f4f65b2c875abc8e20feb3fc73523b000e32f2d04ccd19bb23e281eeab49e1893b209d812897c6ecd8bf903c88e72a1385a90b4730d2093dac58d4ddea832d4c423c655c4c68f976e9802d8f93860f81287d378de9c40986a8500729a44b9e018455101314a82566802eb1af828da56e01bd81abc5a0ecf91f0204840a1d091c097702ef7a9a7479f0ee0f13cb919b784992628ceb15324110b1e90165eac48b75997a0dcccc4f09a948213af1597d932d059e8601131dbe76bd7489d289ef8a43a41f10d16db7e640fef1fd298a468c978170f8a203a74614f9554383337f2a64434314951c736fb6062df0aa4a5678f64045cfc12cd12a84632733ca8d3822f4f69cc4c5adfb2337c37c287afcb30bd099310101a473a875d67705270a98cf8b901571b91ad6a15f0a0ed4a51660cd4cd29e8fade19e74ad1717610e173be9051dbe6498c488f9fd54ca7453de839d6c486030f0c5a619553a3ab9eef77e998296500962c8537e6c6d259b53a8386f2e3871c9ddffc1757d0e03fbf761509bb119e14c61d2f5243204badaf6cec3212156114353a0905f1f3e6a64b78f83d339bcc720f30e65dd836c0ff6a524949fac3b76cf22b00634149af808677122468fb7b9d497156605a2a9c1007c3cecbe91c59f4ecd957305e3113bf0466788530addc62b1ed15deb97046f13de46c28f385025a62247723f5881e81f9890f67bde7942c0562829cfc8a925f662755cc20efc8db2d6cd13085ba0a01d8389decdbd949fde871ee9afb75762deb978fd533044820c2295fb8359323ff4257ab47a07f601d3b5af43a1c844389c84e01e8f660f02c8694b40c72d149ddd9efb25e9d20d7c03559c4bbab374d0112753737586ddb03c0561b0b7b738763822d57a70aeecf8aa5e29acfb3fd972690f5a9070ca830880f43d7ed61d6e1a4a62024d8249685a294c7b138a936b5025c66fdff0219d07e9dfeb152cc690d0343b72933d3650fec43fb322f8015f437db5f1b7a3c1ef7825abaff22cfc288ea3e744220c7373679c79a2d28c4f63e0f1fe384e305266b87a0e41b7b2f3cad0b0a4063aca6450a391939f8368894a2024f6181e9c0833135abf1bdffb0da540f979e40a817a2fbbb83dc26f7f5bec5153b500437d33dfffcd42e895fd508233b3c07896ee85f1d8ce810afcf7cba7e7545ea59433f3c94fa840a9a0507450e334ce4ef12f6dc7b67d4f76bed26919242207e360c7e1e267bd01bf89f55d1e3384e2b00323523bb6342d6a940e03fcf16e8ed6aa0669578d31e3c7102efa713c365a6c1d120a823213342d8d98f5e96c2d33df87b653850977e16f6c3617ec0c73a440e05123c5f643a38b0d66ea88865c1fb5cbb2833d825df63d756ef51b3c7f111191e15a33aabcc0d5b7f625b0f8e6fcb444b3dc01c17f3e2f0244f02e3b14a4f12f7af6f68cdb5382241bb2ee118995d0c9c4d575e389726f6fe44c65b01cb6683b22fc034b09ef3163de2a163f6eaf181a744a3d8d2797ac02ae07e2bd3cf74d4e7f9225155b0ac009846bdbb1ba3383fbbba05804abfa352c7ac1ce947f481b79feadf4bf5b95f124c6f3f1e40d549eaa6449597c1bdb35678c54fc62f270a86b780af93a443c675d5bac1fb2be9cd178522dd3abb094aaa085453f50e5b94f5f9cc49249f6629670f25fa5ee30add19d7a20013f9b9b9b9f3cec0061170d0cc19c007306572ec782d414f9f81b96485f48d6dbe44f25140c70d677e12e4133f23b7686b8e66e697ccfabe62cff54c966cff614006b08e38f122f45f7b81687510106b7517c1d5b5425d071314eb510aec451f484f559d95023db5d67e25a04415509e4b5e7a7f9bc29af1ada16d024c115e7d3244adeb7731912f38edcf81c28d2dc6d0277d4196fdcfe1f5b7457c167eb6b15b175b0539b773a4b0bf10aa62e7c3404a280f1064b04c55b375de6689e08053d307110a2ca3cb6a300db3d77d5a69cc3e9b739e84a1df83e6bcfccc1bba5195d96debeb0f347f0c308418704e0f5b92db9e576e26b7448d72964da81d22230ace63af4073795e462e49faeaf24053be4e8e85bb67cf3d042fdb3b3b916b586ae3731db9f4fc2d891d2f763cc83bd40af678371271a4b9013273a14f748462d6c91dd8fcffa1efefc6976663748e3fd90e90db59ea39d7d00d7ce4a70138d313e4bad4495f5fe58f95dd47890757fcc5f5ba84709d25e501287730a8820ce096c4dd5ae46c27a314b2aa2ac911d5688f09070fd0f1115c872d67929b906fd962ee7b42fcde05d6929975b367e7ba0c235cb62a1c0d53313a1cc8267be5ee36796307deb05b51ca205780eff767436dc10add0c3b0fe81e5d42002e83fefd818a215fe1a77acc9f0015a37c9a902df88959976ebb16d645982cd3f68dee4bd819288c92190d5ec7feb76499ea4286ef9cb5d5e09719f329f4c2315969518807fae28f90315b751c0ddc43f149476e68e3d65ed4a7f643a2b956f203aa06030bdaa9d821cbd423c829c8beca1424f5e986c6c98c9ef9bbe4eac9609fd75325997b3760a85a65ea7b3e747672a413b707f18f423eae5099fc6319b1bdb57bb7b7c7679730d0d6b86dd494b481120598fd729aad5391f31b5d566a9ca0d7643e169bed5a450adea21ec132a1a936e56074f5286906d4806f2a99081d4b24a6629b2d71ee4d839a6c7d196e190341e6bc640b6d073b82e314d88a7490e64c28527aacaa4750fd6de473feb89a067c54044ca31764d9060dca33ea8c785a5e5311472c026bfd26c0de636bf8ad31fc2e8577c0fbae40990216d89d1409cbf1bf5ed71f8d9e55566e723dfe8d448051c11b796d09003e4c3e093a033417314bb9529dc80b1a14ea17ac9b28196106b19c43d1a4ede94bed2e41c24262a3f1ef1d03a53504c5198600aeadb3b19b4409b27d3374ed708047a0047b02f15ef8aba7563036674fc8a43fca68dfe24bcdedfcda13738c3c330890142d2591913e6aea009d8b80483c33ea3b2be8bfeb8f93b9135ab9a9453229a9451eeb544f407c0c1d9d1f003787a36b240a3b5740777f5715f6959c401726184f63f571998e17a7de22ab0b30b93080a6b74ceef0e7fc7ab542ded09ab46e29b95d8c214cea5bb960cbb788c7a036f75bfd5035887560e1904f9ff28557ad61ae8b11236c4a888ba20b13979e1221702ad271674422ce6e9d9268be13a226880ba78215dd64f6801b355ad1442f8353017609d18846214c9574f7294372fb360606df435521ca37ffbfa490e176f2c394f9615260c6515147689c217797205db50921cf97186d6c4c2afd2ab594cc739893d84f193abda372700c1db3901d56f8a1e155e2b13a3a8adb16ce8a81c8c81dbcc62bbf1d69f0c46dbc7004b8caf06964b32ecfaef43f80c51a7dfc12b8057ace9f7e4ed53ef081a662e70bb8906434c41970dc370764c8e1191cf8d9899f50dfa4436efbf8fe22ef3d82881fca87098d721c7b46d720ee74102dcdc744a2428d74e8469f0639e8f2a41cb2d658ff99c75863422ee98a7f6ed1fd583d7a8414e72825bb3b2105cfa4713ce6788e017e0589ca8c5f9b7cb79ee200ab747296171ed7e3d4200cfb9aec0e70332ec7bf52838f27a55003c5e828db8e937b8f1ebaa67517da9fd8637f58184777dbf5f494e56c01722348ec90a25ffa84366284c3729e2e0460e55e48d26f11fdd2818c18d09f5c15a8524ef718468d080f1d0bbbe3634587e94173dd16501fb08e5446b62166196cb20777ee482884349301f6e88087a99825d0bc1c8aeaa5ea4ccda962371b329dc163921ea9e2f61aafe02f2def2d7170c0d991010a9a30fba6d558f790e2ebe6ecae251d22ff2bef9b874523354bf3d5fe4545a804d80305618b9c878adada9b71ebf248f2fbf1a8ccc6aa1a631c77f4538d6ab9ac35215af699f1fcc25caec58241fdd408b3446029069e54963b728c1811393e5468ed1db295e5556eb0d9ecad293582e21e3b88f8b0f8eb08e5ad7ddff6f2c8fa66c50c73276a759d8b82082f813530e1f3f50209644a09bb5a148c95c51b37a0b7b942e243bfab82131270674e7a34a5b3ff59758f8d6fca9473d0fd3950189d4841ddf063234f0453d9ab2ec9245409a3e98197da141ed89cf91bd3d17f9492f1e96ae414f28fe42c1b289ce09b6808031ea50fc18b655296f5cf6b1625cf538438c62df101aa73aebc5b32ad790a5dc972b8f0da39e717179e89dd737dd5be02d2bda8d18edeb29b2d14a64b9e922f09ab070ade31d8ebeab39cc8f5c2624c12500d518033acddf976ceb080d8a0a745c4be989e2c6e53b163a18d628e22685f8d1059292387a6255f35e7817ceac0a3a5e4cce9fc1ecc49aa8d24f4269339fc5476a61c1b6a55d97bc6d9612dfd034c1daf5696e8b19cade192be23328255d8208d5942d6774966289622cbc1401cf4e41fae02c94472c03ee1421bc0bdc19426e00596f1e257144c0a45a73cc51826fcd9d56aa26653ee4de92136e2c0ff96e37b003f5b4d437fa61e3b4ebef3a400aebea19f4224059875d4207758a1446f71c45c56183ce19c17aa8321c2dd44b6ea961aba91698ddbcb06968be795815b6783b4a432e57e815da1651a982fe11ed19fbb0127a38cb83aa959f843acc798f80a98ea6aebfb78cb0907f1058ea2d371ff6485f525885bf6afe15b52029ef736b69721d33cf6f000539529901ed216d6f2350ef866352d53ad424a89f4774a8054e174c99a7973f232f19cf317da68f46fa3a83ff9cf1f0efbcdb5edaeca29776de189f6eb107dfcfe5355ff21da34bce5ed80a90b20cf6cb3112f8962150e1e614778337430da7f88dee0d7225e36fe0d5568fc482a9a6bbdd14a208c189fa930465770175acfaf14d5b9fcacd3cd281a7536266130cca97b262bdf5af482297d360c9b48b6df9cb50bd70fc03f83210f3a099190d2696558b9f2ef7fd1e8b01e18104b0bbc30f5c3a868d0e063d05a88dc15cc22f4f48071da97c9206eb20e2f8dab05ba823a92ba5e2af3df6542dd167a32c45688925bf6cccb850f29f04e1847eb1a8efa08754be6144fee91a70cc428f9c174f4445abe6ecbacfcc7d96309268f7c6f9a269c3c30c983cd6338c435961d01382d982242c419e5aedb33a543268b1737249a09409c130cf60072fbaa18dd6d1dc4b5cf3b44a0b3da5f19fcd6c61a733c80cc70bbe10041b40486d9066a3a9d1d669d5f836253db13b33d6527497efdaad277b65adf02edc91fe82b26135409a5fe4869310a5b4a2bb4a712dca3a2f0e0f2adeba4cd8b493b08dcafc1663d2b6d436162acd2c6a8529a0bb8eacb86a18ebe3a5c9abc07bee907cf3dd35ae88823be9234e8681aec7f0fbb942a9cdb9aed61c74fb330ecc35b1e631d65d6fc187983cc8a36042b3411e4e0734062b1849d31e630c90e95f0650ad3b83a8806e42fe05aa236ee8b0f83963a5c729a3efd3f40cea935aa67fa5c0f618d3dd18a54e835a3b4107e5493eb6d1da30fcb4d427cfbe9e013bcb80953d75991aa0fb6e2ef3ad3b4fb0dcb1245021b4f39e7a79d898363f2f604fd141045f86581e19d21f469d755757344460068c0c7a312e827fd6de5597a2b11c78d58d1f36e9a643609d12db63472202e6dd39c7618be173d6b3d37f4f624173eab3d7d0b26c1c118b315379a20701be81a8543a55493365a126845d395b2e15c76457d04d1d8d56cd99a5e8976dd1d091977f1ac6f995a48a64ca2ed07d495df2bd1468a7c0034dc42d1ecf4994f4e8d1f4266353ff67b17aec2eb9ffee1f09f2f50d6a1e290990fc1383369605634791d7642d7841ac186ed0745cc127899176c36e01c4f1e39c132115f36d0a976ffaf2e0873a9df18046e7b2b3437b91b9412c0272e8bb33633d57c2e52536e9e1e3a21483324c2db07b47eb67c6751c3e883a340ab644e8a34fbc44f870a7a93dda29e229d84a8f9ebc0a6aaf4f37051ec2b17aeb03cf901c92e82e171884944019d7a10e998da45b4e2fa78a0674243e36b68064eb85021c3f3c62ef0d0c4b55e71f1eb34b5abd67c38ce892825e2d96468ae398fb2042002615cb0cffbbded05c89f0c11f597639e499590e3e326d9115c8459b0e45ef6e10b2f7fbc7b4e34599a0b5fac8e31b81fef328e8bfb46e5480ffa6a0e55ad65b2348d5b23ef6ce881e7966bda0a0cc8582b06b250f90d87444a052c1a7c354ba522ccba749956116695368cec657428a56cacd918c5e5f9f86ffd6cb6bfdb83e23bc7d3bd54538ca8af9bc6771f948224c25177082ff2cd39f852528c24c0e4c84344503139407601895c71e2fecf96944499f53a76113ca7ca064e01f7942ec04048d39a1e028905ff47c79848f63acb34e6be1fe79f953108d1ad0dedb334fd4fbb5be046e2a57f4f7965b85403e8aee4811d5609e8a2af2c2c8b00c2c7d168caa29a4eb25d72958b87d96d8ce504bd580609876166806bca63be5fe01de4d3822a32ce00b561bff8e86fe006c479618eb7875ec46a3ddb1a49ec8b4e1b3575639c80d108003b5bb6a58b892c467405c464b3df14b4c65668177623e513b8d25a2ce42b3b1ca3256c55069eaff13c15bc1f3ea260563ac777495b350c5c915282336c2334f5fd622abc863791bea822ea650919bad84781d7cb8e58dc95ddc8210aa5520d133b954694be13d7794c58a95dad678f4bb3a242219657ffffdb8ca2108ecfd0888f4e5443731b5f8d4c5d7d7afd6185faea9ef29b6c66429d3f2285c1d9bc354f1c600fa9a341f76db499bf4d28a0fddd0bfee5528530ff0b8634000afcd624491320e223f4ae7644cfcc7cc1aa17d4cfdea10c01595bb6ab1a3bbe8b98c394a8552a495cfbaf93f8a57bae770423ecc78ed9cd94abb6361459405abfe2a2791109e877bc6c0488b2820969aed61100e65d098507d71d954e87f128f9193b406907340492277b6213a23b25e2885508610338684e3b3fab9c82edcf2fa4409c2ef55021e92ec05769fb4283c5707eed2b2379c3b731d475e748c35f572fc545e58c925324dca3c48015c00dffd6082c906b636775ed412b9e2def08dd469f084365d489dcd96c7e59fee60e85b99a0ac65a851d91ac30e0f661f65537754369dff1ff060857ddacee0e1e5f2972b916699017b60ad89b14d85be7d97a2116c09ceefa9aac731c92b1ac71ccc4803682d2d57785e494c71e9229531c4c37cf68971dca30cb857a02bf1f691b89006c8ab24310535dcca5549d935d20edceca53d95b81cf8852014d5c67eb06c6e48bfd82a0ea67edea9b898189d3171c187fe6124173044cce59fdcd9ef3ca2d821295df527967c0ce97b23d79c0541e4ed9bba72e45bc5faf9c01eb072b0221c872286b2e92403c412d2c4b6e013ac5971d6f5cbced6de491ed59162964f0e75bfec366795e53cae6f47bc5452d87487d8f2f5d372d6d54523f47a55e3de96f38ec5acd1a2b3813050a19962d9ea51da828d767fcf6379d8cab295ef8605daca564cdd28a50f879f80f666cbc25002bb7e8b4bccc287979e3d7c1f3a6ba63d1a5531ea063c297e4554d76e40aaf82589e2883d685e43200307ff249d317a6f5bd0d448263a7c99a288dfa8c43ae7e7736d62fc201ed37cb2360515eaada352aca7beaf2127d11d8a3dd709ee080ccf8696feaaa2071047e4d8350192f9b7b1404e42b0ca35837d13abe8343ccb74e12ed08eddc0606ba17578faf3d517df222f47a223eb7586e47a075cf9dc4646b809f992842bd50e6dfc919f3c827e6d3451c99ee07ed770842ba76473c758878f25b75a1a2907ac297045ab5979f7557fa05f9ce71acc4fa5e525e3e3fd64b6ef1b73ea8c68ded6401ec0bf1de4c8494aee76f85548957d4e9894dc0e8247d2232ae337b6fcacf6e67d23232c8a125958071e00fdc0048835775f7a26ec80aff0207c1a992734088f999dd1d98d5f7964efa091122fe727e41a1bff689af27fc61cbd3bf478f10fe2c6b359af0c9bc368a08f1aefa491cf843ea5cca87eb95691ceaff6782df4118dd668b858a0df0c25223bece8c3d8bc0b9066b5614d1ced33c24289c6f2002cec88967eaf84829deffef87bced87801a31925f6883744404dd59722869779f01a9ca1792e4f3f5e1d9b23b657aec7d22d41524efb17cf9a353aca3772f7f899d6b7efb4523109bd684ead4b55e65ac656f3fc9f8be09f2beaa8b7d4bd3667a6d045cad2c30cd2dc8d3a1efa053660cd78c5363a13f936123e0f0be5514b451365e54f2890d9e22cb269292a3fa91e84e34487019aab49e16dec1995c4afcfbd4124a529449d319264f350313056f31b3ed5067c0e787b7ac0417f5a83b8308b285dfe9ea388e63f38ae03438f8f3e0721cbdfbc48d96951af4dc0f66d733de7ae79a6b59c7f9e20ccae060a49c060693980f54d223d93881e6f98c8533070dc7faca84678bb9fa3fa578f59a65fe039c27a9c0741683c11adb2048106744c3e1f16ca1d5fb31c9fe852ac4094b82f4a2f242fbbf845e72410306c86152b92ed581a4aa299bef093fa9d6d301c0cdce06c803f8e80ca19d55f3d86152410d88cc0394345ac7e9afc8aa49181b5c1baed5ca91e11cc90341683624edea960980e08b669c387cb865c81fcb063916ba4a4a508068257648bff0ea3ea103022c3363e88eabddd007528bca6053272748983151086796aa052cbea2e5b05cac80a8d6b12db213b7cc44505ae67d95891dca3d406ad36321d20b47e0d56e1c952f7124641059c35a01401617929b952da2c3320336d101c2fe88a0f44d45be824a62961d8261e853b7092206a098d0170640c15857e17b9deb20115a6568b1495b27fac3598a8dad8782407f2f71454bb6cb94ad0198747cced315230395a89ff2f5bd0b4b4c5a783e70e7817673ec6061e14becc18db6adc64e2ed5e2a47e859b68521fcbfd0b1fbd7c15487060f1728df74ed5fdef9a26f82130a25052ced68ae968d6a4277f9277efb7aa66ae1be1cfc161abc21ec4cff69c078afac9b4ee1a226512479ec8333f4a003d9a50d01351d14335de61a559339179c43a3af625a769c99d03a4aaa633a3d8c69a13fbdd6e5794f8bc50bf82955c039989ebedd3de0008cc470e2da3368401945903efecafa867c72ed0c1c7bcb07920db3e0682c7cc961385eeb7776cca7cc925f6ef85edd42e5c71c4d372f60c0f26a23eef78d4811183a38239bc0d6b747b57fa5d9240dbc6bbe4a171b7b87d8636e42dd0b1fad91748ce5f1df85c7b26b2076ce3ec462d26119409916682c8ad8d61cfcc028919d00f7c52a218247c7e8e919e959cbff1d46e73b8786e516037ce3d426e081b9398e10070fb7446fe0d96854df3bcb260aa9a06435ba3a87f875c300398b14dd1ea0a0ee09e41baf1ae4042a0b2a6c78f375e8147444121342eed444bc2bd6ee284f9a78a1b123c3d85d521ccda09eb13fdaf464103a7790a6a3cb2b5127d9e4918e0ad43014c26d378e2c9aab428e3f0b61c13de79b5d1595ec708015c8e5aaa92a6a2cab28c56705d25dad990bd70229687ba9115d7f956e15351b6ac4ad8d05fc5d8eebff1dfa7c7cd9a298eb0058fd9fbf4fbaae8b93adafed9d693c85c95cc7e66fa080496c84aef42927c3dab99f5e73d592a591e9a0b129c530dc98d50b27b4c7b591b3b4d381499a926e80878c960917b35604fb3e9deb48f92fc2c84015b617903035bb1108fc55bd942669a9dac7c65254529d5ad0589f92518ed39056ab2b71d3b147132323c0ddd023908d3d44f98f698603526e360f98ca25223973b41e60d326e59f9bfb434f7ece8e7ced446c1769cb84a14c4b3bcadd530568d8872356192b7462862862c5620b8845d2968ee1659398714105ed831f94fb74e37c9e98db8354909e61f3ac918939916a58829b4e21c2c1f0d9c8b83771441c18f926eb76d8b229b9c239e79402ed337c243b4e41f8c4fe31deea147088085281f09e2ef620bb486359ec7be0580dd280253783f8538f1600d80e2f5f285dbd3c974b9276ec20a42d5391982bd86d8d2739ba802afb13fa8a8b600eea203cf61fa96c2047a4e0c95d01c83f2d0338a80efcae7f5bb17174cdf722281feff71c3c07c6ee1cd9ed8cb8a266971339e5f69caa6de7534a09b169207e794846bf3ba5c25f189de1a846c1f61c0dbffef6c70a20f7d30752a3bbf059d8a0f7ec64c2918200284260cd3c1c2bc316b72e67cf14a11dea109368b099e6e252b05719abfcda9de63a011f8f269730e461aa8fc92f3641b707fc93262bb1a5f504d824c18882a1cc799457d5d1ec1f82a950f52946516d4e908ad38efc436e5ad0b4aa31187ec58a273ded43998414604d24d0e2d274bc50800f2385e27c364c6b0cc052561944b170cd10e698b4ad4bf4f1fe43d21bbe51d73dc472708c06a42035c6f3c9dbf5610e1a9692539b7ffd06ec43837305a5e284a59a31004942ca49f8de79fef059b01f0d4ed3d4673e2e07fb20c304f95474f7a8fb9cee9530d13bdab95aacc7a8d4dc8cf0fe16ac1622da2f34c43115e4968cea0efd3dbaea912f602a509252caaef00f96b67f261f58ccbc80ed3bb437e490a429e6fcd7102b5a3265cb28b19f796d52545f3b8c075d1d15ba2193528761dafc95fb1445eeefd20665b05e949c31b104b592c1482a1e0127cc3b5f396e554b01d72bb9d042418bec61f6078ca6b4cd0ac957dee05dd0d2bdc1a64836effecd5fd200400c3d49be9e9406d53381491cd79b4344bd2252eaf107a25ee8eab5cd265f7fde6ff6de9dcad71e059756292dd8991226967f0bed319f4aabff7d2bff3d040f590171408804cf8478784a4a7ecc7439a2964d26581fb084966c299458b1138bc78dd5c41b06f69c54ceb5867fbd61ce1679acd7f1b80aaf1e95052055be977c1acc4662ac7d22986890aa52cdfc2c917d043ce39726bc03e3ee56db549eb223b59fe8df4d130b4da0a70a5590794d0bf80747d7266d16525689c83f1c97d118d62e1455bb2b27ae6a1bf29ebf7c5635455f1f93a9701c5761e2959e34353134d68bc90710238f29ff1c210d8b61e954228a0436701018c55f3261e5ebff80171b15592db634df01e77cb9b54c40882489bbbad05cf09bfb8af58a4b5d9f78e9b08d3bde8eef3e8bcda9677b1614a0158c41138a369f494efc51544eb45893403bce022ab80429e93823e0f9ef1f3c735e1334a28356dfa20eecbe71d3b75410765f553f48a9b0a986d9c1cb50d966684d06cc2ee612c3ab1104812c379eb4ae58eb4e5170276c546d9a69cdc45bbd6fa5a27843dba32d310c30f4d23499aa04093b7db51c1570fa464757816ec383e9fd226ef767dbe491581c9d05881fa565d3a41886e95f50ae77ef1de847c9062b4dba3ab660a3d1192545072e43c7324c05b71886128e59989396d7ab2534b84721aa6b332b3c4d4861aaa6dad2f0c94e727d831f7f5c4c83d25449c1f5ec8117b46a6ba5207198fc0f80f92d432a74d95a47518f67c78b0007c24a8bbf566aebfda76e75d52fd681d20cfaf576c0569ad0758684ca16ef719e004515c3ee992af5442bd3b5200d7ea67b46dec1ca2cd12b92f1e2451e8a715048c1703672b375f474532535400afaac18515d5d80acc3e2a6e300dd48a0ec1320cacee170fe7a0cc9ff61879a46f1dca9820caebd024ea23d719790cdd26ea901847364cc95df42c245b20aa279df897f0c37d2a3682080e8cc2812cc824bf4e98ceda3e39be702b1397ec396a40bf1bcc7f6c15e5536152c59718b3d767197abea3e1f6cfa4c09d459bb18cf6aa9b1acaec91363c70a66c5aae4e62d56489b8c8d9a2a20b79ab028d99b159d6e27bfacdbf09dfd1f81a7d295aca26f08379806efab0fc3edf81d8efe1143c97f5880c15e0b283ee7d8a0b392a433249af899e112a7f03797eebdf1ca6ccfa2ac882ddbaf980f707a58e9828e19a967f9dbc363ed78b278966ed1bdc3d97af2a51a2b70ef2cad2b827b2c22dc7f79f50c5048f5465fece720902fbbd7e69679676ef9e576000e7596668f48367f48d1de288a0a59e9263879836e721e65a4632aca2120239585a14829af43d760573965eb77f51364eecd0183c328f91fac12d9d929366a4ba0a362204fcf151b7c5ab93649387da878c242a3c49e41b4bb54c6ef8fd5ddbf42c57043d220da62e80dd1e7fea06bb395a49e9a3b32d12a9c02e81707a106154fe0a4256b0efe3b831e311d35d04439061fbfaae2cbdd24a326185a05b05ea9433c4448a4c565ff8760213547ca91cd5ae469129090659fc220c6c1f8708d0d388e5f9c10e0b83a66a6a4bb1bb710a4b1d4d8fd90659cf99c7339e9a02effee6b015a2415b9eeb5f3c25c03d61dd0811ad48206d126f7520ddf35ade2d9211cf5330c75cbf95e1c9ec3ff56437609a853f40132fc9ee3da49e07498298cf910cf8351f93c8b0946d7abb9cbc0de13e700c714ff8cb6524fb0a77675155081c60012d17082232eac90aff11e040aa1f03ad36a1a8a6c8ae8acd2309ee3fdff03f138551c0ff723c0932631623e71c4d444743bf1b8061d89fa74fd4cfb8065be93791c4d569f22a5616c50c06090d7a47a937bed66092c66038a484b322892c9c7c367a8a3b70de23a57c4320120b7b9822a2d0f2675459aecf8f201a7d2bee83daa69707785b67691a0ed56b2105f902ac6769e680b1ad5df5924720b1a6ccc0c211140b7b1e38d9f804057e59d87387b90497b53081e87780b02ba920962dc98835bf538a4c1aadcf30ec642a937371888ddbda2729333df1f01b66d2efd5b0b776f36ae951f73b154868efe8584fada38d91aabee831bbd36ea2f46ae754a99b1dde3b9b9406fb582760a3c3516b033a5da216331059f0b7bf5d03ad12072b7e7077711a39f28bb82548ff2a6065cd5cade7b6c19695f759fcbbbd09d451d8ca9008c6a3ed1615f8c5958d547cf05372ed31570415c0e322c617a17a7b3393ed382eeadd63f5c53b2ca914c51148ecc8139c20c4eb20121bc77e0d6349ce1088cc78f849397e3c61f50b2eee957dfe5188bb62b5275fa89c6ec2dbd7caefdf9a5d3dda1509f081460d04534a776f8fb8613d105e2d8d3e1c062a12ebab4c0e91d4f917141af4c3e93bb0cd39476d7c9a025d1680688031c58a7fa79d63c4c9b6489648bb516cde27cdc6dcab390e7d3c75d245a176be04df26484390cb14f1fc53d04b8a4ea6626744d094ab2a30c9a56a0808cdd042714335a6274402a59e327bc5d370ed90c8ede2e8cc0661f22afc43fdf478847ff9e59a84fc0824a3afd5febf74c034171eebaf20ab293392b827a73098a2da38b04568cdf3c9727d592aec18bfca147087b900ac85776b40110042983c4bee49c77c3efc1d870e1333a0161c7c8acdf63901d567336798d8037bc8dc0f0abf51df3e262505e3fcf2053f28f75a529c2b4db856d49c71513b11d5613abe3e3fa02d007df28318cdb9caa7f74e81233981ea509d24197f95b5de80167bb34071bd79f46bbb971ac85fc1c501fc74f0b7bb6e93e2cb7b2485f75b8c59e2a73c91e46974c65687fa2fe8aa323eed25d168ec9822b82920430074b589034ef62bef53760ea22970ea991f6a5cc23ee296afaf3bbb032ba4def1cfdcffe5d840c1aced52f38890903d9710158938deb082eb82f7618fc6acda6e5a7bc6890dfacd22824dd831096e0d0fa06daecb296f110f9b95e5f7d38c62832a3c3152f746856a4ac86bb123aa8f9570824d9700e165166badc95013fc81b0eeefb4a7391428488e4c68a4dc640c598f0d224df21768b7c91d237fde6189f89eb8636d31368fd72af07df66a8842804930f5b282e87ab8aee21c09ab22a60d56350614302281e3963fe510a701f7ac8d88d5889f6b103ded52b8cac73c835fb8ece569992f832ec26d46f3b1ad949b78b0fe8e23c43ed56be17a480f53e8055ce05857c1dd1201dd423528299ed953dbe7599b37c22c45a6f4719c5fc534f48d500e45c8480a14286d1fb5f71aeb9a1e147e50909341168eed76c761e5b68f713e39eb84531a9213c0694d7e0e53d48c88b8a5292356db3dc09fa0573c85cf0e7252493c5dafc33d9dc61860f70b48336169bfa917f9cd9fea172c332f6a9bf20d77684037b4c6abd07eaf6c011c8a7b87acfda672a632ae0aefd489fab3ff6bd5958cd1d55571809cc1fa4899ee42e14349bdd177fbaac6c808d70c1f0ae2995b6bf2844447cc2772d29810d28ce9b39b2e32c881d21eeeebf66404a3fe61f01113cb5a7ab85d3a977425db11cd3271081bbfd02c48aee925f819a0fee7b446dab7081535c62e5a5faa991faa1ff51b98c2183823c6bf6ab159eb58c7e9754f35f68868f2dd7dd59e5bb9b82be81cb60e34aea84c56505bc47d50012d0d8bac73f562bd670e7feff0f2bb2cb29cd9c834d067261e1b6425509045712b916c279d46b440dbbab2b29f41bc376c6cdc2e8a04045919680dc58924ee999ed434544bea7412e6e2ca7df7805c43104fafb53cb5c4016956d3a3971aa07a75cef40ad77a0d663d987eaa7346c9d42d921d6d15e3683b8d6a1009bc0201114f1fb111fd0e820fb12f017ec2ea633fdc2c41a6fd52121e267add671b198dada1d4ff9480f9afd7fbf75935113217bcbbd03c40c120ce40cf84472dd2d919cbc26f5d66d2a6cc875b74472f29ad35bb7a9584181dd86b16d85b5c7c1760615d8864b280b7b1b8bb6b0a690eb6e89e4e435a40b9fd216f5a329e4ba5b2239719dd2166d5b51f195e7b06627b0159fe128d8063bca7358b31533a858c1b61ad2ab016f52a14caf013fa648a1428da6db6ac09f94d876729202dbe8cd5662156a3485dd6bc07327f1225833142ae6097c8c23073d72a4fc5f29266225b0adc68433b918b7458da690eb6ea9c5b5ae0bb72d29398e09e63ad4680ab9ee9784e4382dcc75a5db5a246e7b611b8e1cf6c8ebaee7b066e56128787c0d78540d78d5c909099cc39abd5cb8e6bbea27d896d3c2b61a96c7024bb0f7a14e37855c57f24a2009f6bed58a55eb55ad4a1d6755eb5158855f28d7e974540a9f288b5a3fa2f089c9d8a75a6f1a710965d167d1162d9e455bd48738c7ce56a1cac39e58eb391ce21465d128ae619445fdea2fbea5eed780f8abc7f16a7d87eb89b2e8e3dceed3d7c0d412300861d4c50cbd65da0d196624c74e5550be03a099bb38b6aef2424981fd4489cd1231142b295094a722a7a49391d311e40d89f392cc8195a40e9492d47995a4112693158ba44414eaf1a1423d3959c435a89114295034813ca9d08fa25c831a094d43a79b76394b58252a848e689793a484c4284834d22ee70a979414b5798bf0285338493b5e28908cc0471ce9a0a430d2d12ea7c96a7511a38ab4797360b0db30c9abaffcde4474c26997d304af62242598a6661317310aa9cddb77e18039e24b74dcc032819d6eb06693104b24ab93e934b1ea0456a986a878a85446a87e543a543f54382a1ed54d4a993251a686dabc8f9529a3142e5345dabc2fcbd4116ddedb949b4c4e7c55c23c4949db8ca95c34f19787a1c0fe0a3a534327e24f549178d4454ce3de98784a4ef283f5958fc94d4d22d5bd66f88a1296897585e7eba773053ecd804f435ee5e9549e6e27a3539036ef53e5a9a83c0139119d4894a7a1f2e47312ead38ff264529e784e44dabc6795a71e6dde9394a792f2b4d3e63daa48053ebf157c9229f0d942812ae251c1e767e2933c81cf560a4fbe57dd98c0e717c32759029f2dacbac9f7a921fb4267253ebfe39384e1b385921acad723452790678dba936dd44cab65c8e2cf2b725884d11e11497f9a2cd5a12673f8aca9c9e239129b2c965c1c63909470652d4316cb7a24b782563eb2f502c154aa54e28af20d4395eac4233d2790e745a180e47b6f8844fd746f88ac3f4d3744641124242323222221a1ee144ebfc48ee89757917e5921f58b68d42f1755d42e312d2da8a27cef3181ec9ff7c6c464626ab5c2b0e23831b0ee1d0389b5ad56ab656a91b8a9d56ab5acc945029f2e530b9fa3099f5c49593349d99955dabc2a6f3629b95ccbd36424dfb36f6c798a4527b9637cab2c29cf9a4b4e529e3593b0be3a11458e1345eeab7a82cfca619a0e9f67170616c78981e4de3194587b84adf5b7d567678e0cd485eb2602a847b2886bc5a77893588a37798f326859c434c49f9c8e32c8ecc2e709a674c5e422a6a9591431a52b159fe3ef6b194c7a2cc7c3b0632e293b939436b3ca9b57259755e5c9ede4fb54799676f23daa3c4df2b4739aa69bcce1b3cf7ad62c9e52175de63e62eef5e2d9dd4ef0e60c6f270deac81b79db39ede46bbac9b77493e9fdc9c52d5a20fb26ab826ab042e825c1d6dab5d67befbdf7de7befbdd7761d42e80dd16aa64f02596bdde1cb1d6bb3be5aa5ae56fd22b65813d0bda37700794a206b92eaa922f6f576371f4e98239ac8c3fa1548f57e2512d42ee7156a97b30eb5cbf955a2707c9fa67ad42e275991dae564e1fe0af767dc30f5c7b2a423f7c1afc3a937bda3777850a83be68d03f2c9f65e12c893255b4f09e4797372ae8e76b937e2ee6897d3ed2f8fbb737b5c9efba35daaf81b6188694a2f09018258885b9b1eae11248820800001c4cf0f3fb469da5b5fbbd887364d7bdc038f8c4c8f19337676d881870c193b70c0c108183074626274c0c0e4b474ad4fab98c6779a6b8def0dd99b681ac9dbd34ba9385652b6252f2c97954cd5926a81c2341800484c3bb110310d366216fb3afa58711b2759f431c36ba8b28f1c93d98bf89c9399cbf81c9399ccf49c93590bf91c93590bd4734e6600483dc764c642f59c93190bb6ad9e63325b310366bdc68b8190594fc9452c8e7834618ec44230540a7b4fa9b06a55fa8a85c3b34a30ae4247186933a86f861aa712754e51eb306a9da33602a97724350fa00beb3c21f0a5f022cc74c3aa913aa98bb4cbe9f511ed7296ec4dbb9c224ebb9ca69c53a5d32e27cb8876394b702562639515e8461d42536f4204a901e40788b2fafc50d61f3e549e23d85b0de239b97d1379ddbe83dae52c1de5f62dd42e67f8dfbe87b0cb493e76fb2e6a9733f594dbb751bb9cab9b47ed72921cc5d97a8adbdb3d5c3fbbfed12ee7a97ddae514fba7c4ed1b8889db779013b727ab87d49b891dd22e278cbbbd05ea88c06c90783b84223a7155918a350a6277b4cb3df0e70da59007db1fa44f8afd31bf4271129714b896e22a6e716de52a56b0ad12016a1714b886e22954704de52954f00d14b8020d691713d7ccab9cc0b51357c1b4fada094c93826b293f6102d74cfc84092c440aaeb718aec56ea204ae95b88912b8460cd72018d7f04b94b856be448983c0b802f96997e3da5fc2700df612868138ae3f3eed82826b2887ddfe877621816b240e23817f40c12e2f5c7b9d840bd75c27e1c23ee0fae304d74eee32c13593bb4c700f27b8f2f4689716aeb56e52826b253729c132b8f668d392e01ac94b58b8c67a090bcf20c17567856babb354b8a63a4b8577c095874de15aea2a14aea1ae42611924ae91479970cd749409e380ab1176c4b5f12611d7c49b440c63c421ae999e237ebc0571adf4f0846ba793271cd3e15af7b1c33035a725c4b4fa1a875b2a0e0b886915d3a8d53eba98a5dec0b0b50cb9ee584b5f3cedf2a3f2549eca5379a80e1cdb43cd74668786c4958704d21e7d1f49daa3dbed76bbd91d7687dd91949494946481be96bdb5d65afb5d9f148d8b10be3c122836260735b3dc49bb1adb72d0225825362627a445b04862707252b31c3489083beb984e8cc9e1661d9333ce3a7c11acd369968396a19bd9989c9ed9989cd2cc760c4ece6a9683c6e478b3eec0126a96831ab9b32e26e7ce684c8e38ebbac6e0e4986639e81122bc59c7e4d459c7e480b3ee2e4763707254b31cb4e8e666d627a267dd6937ebfa8bc1c9019be590b3ee4470b38ec9b1b38ec90967dd793139dfac8be9b02d07ed4c23b68921b6e1c86153046b56c245b08a60b577fbfb7916db70286d2172b7ed7731bced3b8e87b98eb2a0174b9e077ed8fbeef7c645b0665cd7bd5f046b867a05ebc5367a13c312f82b7ab8148a6109bc0ddbba3edd76ff795d7b230a47c292c7a97834d593b05226d1e46258022bc95961eaa617c19a99601bbd615b11f7bbd7ddb084fd7ab78dc709312ed195c0da7ac971e4b0096f2b611c396cae025f8f238777e4c8919b9b9b99d82ac1b6be2d87352361e1d251243e919405bd5d95704a4559d0db87b781d846bef42258b3950adb8a60cd52266cbbdf6d4f1971cc84776fcbb13315109f30bfdbaae9230adbe80dc4b68aeb0c2a280bfa22583312db7040bc4259d0a3a02de86b1040821cb38b73ec0d2fc013f1cd6ca02ce871cc4ea5db8a60cd4c23b6e5b06622b6d1dbcdcd2cc45ddbe3b0680bfa1cf4889b9b1911e42cbcd7f56ddc350971498bb2a0b72f615b8d39a481d9d78138a686cfc56b093b665efd300d1ecc892f5461d65d0fbfb874b29a00e830c2acbd190e11ad59875b280bdaa24fc437030065416fe3f0ebe2131765415ff3ad8008338b73d023478ecc727a864304396bcc82b2a0c7a9780565414fc43763798d4965f9a84797f388c8d30e212119191111c9fc204264c8ed668fe89758917e7921f5cbcaa85f44a27eb9ad3244a91d3a5bf26987562e4eee16c08b5cbd21f2fbf7f0de10d9fda4404d20cf2e2a22295d392bf81c9625233b6ace8c1b3a5017a77d2319110911c994d221312d2b6f9194de90417c960c78d5da0c78955ec23468352933a0952d089634dbdbd9b966ea8276ca257c5a8f0219c9270532f3598fce7a64a48b8c64da3f392910d9ba99cf0f26f72dbfc07b02d943bb987ec3103e69271b2730a12245c6242424f108667443110d7cda30c9267c564af456a16fb50ab5e7671ffbcc36e9a7031e4f884f970320dced767100646f10d4d9f49316c106434f206b359b300d9a9a695d842045b9e21ade2dd7d3245a448728113a24a665455cbdc819f485ceea0b9d513284b45d6a94be48525246423a3a3232ca4545444443433366b4494f840810d09021313130302d2d2ca669c662b1ffafd7abd56aad562b92244551fcbeefbe466b5cb3d8cbb1c3a9eebdb6aeab3c4af028b2e345938c8819e1791d890ea5a3abb8eaf0bc0ea54b795e6772f8a2a8b8e29cb8ef20eca7a697a7f72d9313d7099a1d525e52d6c00188181fda6c91599961a25029a9682256be625f3bd85e326eb470a059c110828ca921c204d16f0122864be01f4c379d9e9e9e9e9e72a7f73fa05ca8d47bbaf7a8deb37a0feb3d24ef29794feb3d27ef31790f89a3a04e4d4c7e22f1c2b4cd1376996ec267d57162d232611197882126115925bc2a61d54791baee34357f3885c23fb4494f5373872906efbdf7de7befbdf7de7befbdf7deef45b9f6ca8a69c662ffebd56aad56248945f17e17e5b7f3bc8b9905e56441c13555c74a8e892b4eacdedcf6bad1a2590941e21a62eecec14a8c6b82f8381bae01a265f528e7dec1700d0aa62debf0693d96932517754556cc24338614fbd15f46af56516b45b42287485148fc823e1494e3e8a1752818470f6d00363e8a24211d1915110d09e5a83394f770eff9dee3bda7f41ef03de27bc2f798de33bee7f41ef228b887e6f6743a79c23435d62ee3681a8342d18640e06986785eb5ef7717962f66a9d9e21f8a8a8a8a58ec0b9d7d640b65f3ea21c210b6c0c4d01bc95ddd075e1c4da7170914d84b5c2266e2f4e68953fa1f52373ad99e2c5d6d9e347f4fa27499277154e96af3a873a5ab6f9e3b4ae96af3284f95ae365d7df3dd61a5abcdc3ae2a5d7df3aa7ba5ab6fdefb4b57dffcbf2a5d6d7ef5af74b5f9ef65e9325f9e55bada3ceb60e96af3e0712b9bc727295d6d9ee42523d97ce9254a57df7c899794ae365ff2b074990f1f331f7bcb7ceba279f126cc9bb88979938fe6c7a798d97cca4f4a57dffcc94da5ab6fdef413a5ab57b2f9137795aebe79d74fa5abcd9ffe2a5d263e2d0c7ba2a439a5a4d94449732ce392e6b2a4f925cdb092669492661234bb686ed14c52d2cc2a695e9534ab32cd27315b9b94fad1c0c7c8ca8a69c662ffebd56aad5624298a345bce5115bb66666666c4872fd9a04a999f222da3992c575a566f9a49b118d27ff47a19b55a45ab1511490e89a2d0f7b5acca683e615b119f9d8ee3b6acca28f671c2387a46197dcc5c296f7d599bb16f118a3818f0641128b73277916565255fd3cc3716cbf7b774efbdf7de6bcfdda2ec6d20fc26b0d22e679136a94d326348b11ffd65f46a15b556442b72881485c42fe8bb97f921d226cdb6db326aeba135f621f303d426cd5f5959314d33168bfd7fbd5ead566bb55a9124298ae2f7c9eaecb6acda9aae5c0ac295d33b7ddc159333635decde5fdf69fd57cb9ed620787af2543aad45efc8912347c2d3fae129added6239ed6f7e369f53e4ed8d6339ed2dad498567d9cb0edeb3151d3a909df71c49f188a582c85982c9d96f00a3c885bdfeb5d8c0696ed4a9bf5d5666cee47e883bbcdc3366cc335b7a71f9e68cdad37422fa4c1b28d7a7a5a0a676cf4e969b04c43a3af0aefc392ebacf1fa8b3d30c467e7f0dfe947978873b0ff2dfd96306ca9acef162d905dd34e36a55ab51062908ee371f4d0be876009470f0dfc875d03b0c9f736792cbd4ba55229865f18bee72b896159fa404c69cb28b501cbde670396c5d3d47cde3ce39da66611970ec3d27bf6b73c6d4db5019beb000dc4a5e77d14c57fa2ad8706621adf430fc461f7f55411044be0e5aa8d57d37d251086b45fd0f6baee387a6827533579387a68fd11cfd0f0d18d6f71f46eeb43cf84cfcea69f46517ccf28a3f9249663638a3d0c8ae2c3eef465d3fb4587fb379bf0884f4f0ccf9a7c69357ff7399f71d8d6604d37778512675fdbda5289b3a5d271f4d0387cda72dbce7609e3e8a1dd972ca6690b72b760f7cf7a1d7731ed4aabf7ce71ef6e77ef69936d6e572b9c68dcd31cde7eb56dad7dcf3d6d190ccbd5f4ad36c616c2d326d3282d095d39645797e7bdddb97f23b72df72967c3bd6b72dbd3263c310096695008b987fdda98a673e6ec975d39d0ecca81b6d9302cbd4dee4a5a4d6e5a6d7ac25a6b6dcbd55b6d321deb0bc2f0e2aeabf5d55a91d67ab7f5f4235b3759ba1a7e80de7278fa364052a19f5bfe3215fad9c964beff32fd0beffcebd56aad5624198a62e9eb6cdf6bbc1720b6e11a0fd396d1d37e64eb26cbbdb79c6b389bdb9c9090568fe4b6b6b3b7bdceeb3c7cdeee7e954228d217a72f8fb791caf6a32f4ea429dbdb10eb2f7d41a26eeb99a159e57b1b5de64ebe4061db0e2da3ef99e9d3c84e256d996928533c43f3de1dcfd0c0f769a57717cfd0eefbb4f0dd2d9ea171ef463c436bdc9dd617f169936dc072e373e6879b9b9bdcd98b69b0123e417c923f7c72d9c3270d5683cb2d2c6daec46811b24fc5b62a084319dd694f5f3d5b43bba001e6d6eb9276ca7daf0849314d88dcaf476432d73194e11521c97cc26492860ceef4dcebf129c3621af464a658469bd6f60829dd81be9054a5d6b035b8c634d89132e4c833345d6e8b69b0230f3892676cac729f8606bb1d3992676c98b23d4d2adf1a5cee8e69371c39926d7c2d2b1deb173a93816930f279c3b56dc3d59a8552e922c029f763ed42be06573a7d4b892ca158442c9e502b394e474e662b6a0a5f73325724b6d5a0beaa29a91ebe66bc785b8d0af7744fe18bc23ddf49dc6a19edf16ec2af9651ee21ae29e11ef0227ecb68b7ccb6ccb669e3cb8d5bda745133527737659acfeef7bb05025126339943dab64d27cd445d984c63fe4a531ea98b7114f3578e59a42e4431cc5f29e690ba08c352feca3097a88b5229f7579632485d80fd9560fea88beff3f2e779b9efe58ebae8badcef32475d70dccd5fc9e54b5ddc6bf357de6ca90b6b73fe4a9b9bba68aaa2fe5de74a5d7c2ca6194ee9447cd6e39dcc707ac236e969b09cd50c27c49796f04765d5c697bd96f2a4c162e52944eed3f100332e0548810757d8020598f033646602a9800a219801145240831925bd0653107689839383de727ad6bf2dbb8ec3b61cf6dc73ea2c02f61cce417f318e1c756661585bd65baee16c6ad708f90152a654050b09c8f36be9c09249aac2aedab43f64fbf0a3c70feb5033592a6d20bcf76a0e92c54ccf238b46d8915532155204a45c041d59ccb781886431d71edc3215d2039f1e40201b01288bf9e3414ea6b77c870094eb4d38c6f8be0f02247d733b204c8ccf04a4fdc9ed88e35702f2f4782c152204247b3c1e8fc75364e76672412d6dda7ab32adebf8f245bad7a3badcf37f230668a6b52d24dba4937e926dda4ec9ddeec8bf7a3553c8fe70b420e6911c99ee7095dfa5bea4033c52c204feb63a2ea116dd28b55a7e84627d3c6306707d91bcfb337f6c6a26e077941de6ff9a16e9ba3776fccc1265b7b1bb4d7bb2d6d3258d26cf9be4b9aed461ec6a45c93be8f249392fae1b9b69fc7f391640b7ba6e95d1cbd24b29ee6918731575c937c80dc8082c0d2fbe1f9b4e9bd547a3fa30e9cd0500c8bb9202ea85db87be784da65a85de8e9f17843f6c5fa783cf685dbb12adecda107dd7c60be8f245b2d1e8fe7eb21cf7abbd8fad897ee86dbb12f4366d0815af4e663be2516e5999b4ba3c733829b746f37e916f958da543df5f36bdde382ac8af714fe3e14c6277cde334d4f0be4f70f7ccd2e01dc12a6691e1ec6fc9d1b2a3a4ae28cd4a436413cf24073902f86d1a677d42605ca272dc27407f919b9f780fcfdc6fb57ca6873289f5c104ff6fe79c0dbf174b2077493693eb9a0ecbd0bfa86c8a2d6d1937e7646f2e9dd7cf8f472b2771af3bc7eb520fc3e119054880f82649a494f874bdb04cb93e682e557ba66666632474b5a154b9b4c9634dbfffac5874b1f58c40d33226033d471f4d0c8d336f169cba5ee050ae3e8a1954e62d4b9873ec05bf03e043ff0b39ccf7a4e5b060fc396b49a5cffd55cd270f49430a52de30e9635d73146f7fb51882777ff4e3795a77fe5cd632980d24fe2bff2acd964d34b77cc01c427cb19f0f4345f26c1323c792acf9a4d6d2a691e05d0dd05fe96f0a903cd20a6d95c6fa36bfe07819036d8755d17821d08a6badb751deade0ebc240882a60eecbaee045e10bc6307de7b41b1bb20085eaf03ef05b9ee822078bfee5e8c8b60cdc2308572711a6e3a891e077697bbe04f77bcf74efce861db1d6d8deb276fcac29e9ab4cee18fa8db3e5c046b560a6bf877d54a55fec4752427af3179eb1e0a90c3de572239794dc95b57b9dc77dbfd0a0ce52400701bcb6d38c54bd28423870d886f4bf1f236f3275e046b1672dfc596840a359a42aefb2d919cbc067c0b86f2c76e2b81f2db5e29246ee2e5b61638c54b98b7bc06179714afa106d96d3560db0afc5ae1428d26aebba593d7786fddb6024379fd45b0662e687071394c4bcb4b0b6c2b82c56d2a00c0825550d85b2239790debaddb588ea3824753774b2427af59bdd5e23600c0566e9be1368ce22a295edee622c32d33b4000066a1e22896209ef9f07d85b75f956e696aee7c88efdbf0d55dcffa18cf811ef621fe76631a079b1302cd2957bb35cf46e9c322f78ec32b8b6b17b76c9025f1251b9fe89d23c50c9ea6954b33b47a2473a7d932dc90b9846ba58ba5db107309d76aa58ba592982bae7d0fbfdba87d2f7d9f2dc34d06bbeeeb6ea33369d5c3340b85243173b856e3de615ae7f30022b9c6dd46f7912d9b52e57e00c9227dd34a29a51ce52103618c962f90345dcf93c5dc660b0540d8f672a794aedc73f7fb216bd7dc22a98afe21b9da3ec25a761d0f4da98a2ad2b1bb622d3bef97ae78b81bb95beb143aa22ce6a6f9a47692bd11b1545475385c753c5c753edce18a39aee34c65cd34be7702e8b81f8a8a8a8a4ec5b0d4a7f5966f23bef54ccd5e876fa654450781271042841d9a0aadb5f7d485b679da31c69da9f92e20a4f4deef1345925cad5a2dfc6a2320b9dee43b049e2000e5143c74102122929477e47bee0443c8c935a85f83da8e04a837553c4d772c812fbddee374e877bf35b897f002b837a6f112882b5029c47d7f1c63dc7734ba9b7d1165c28bd8d6033ec4b74c3f4e87fbd22faec11dc40bf8c6181c96d1e6872b509bf5de48807a937f6970b80661faafb91184b4fe001a3a329253642a24083cecbb73f4fb48b2d5fa4df39769d3cac80d82200882e089809981f81c0be065fb6bc70380cf01eb51e63b886d3df71ffeacca0cd2aaf4d8d3190cab52bbeede3b21088127db5f21ecc833642a2408474118cae73327a266b4d979f76eefddb10037b23d8d1d5e09607f5fed62ef61196dc6b8b6b1dd3da0a4a511bef0e4db0079de2c3e7c782a718de91f58d610b76881a4c9de4be1771a03fc3c8ee6eb3e42e83dac1577dc572ae1f366fa9ddeee5c49abe96a36f9c334ecc18725add65aeb38dab1f48dbfefb12fe1b3528fb3b5ac99f68b8b4f9ae66805a8ef3ea303cdf6e069f1e9633c5831f7d2fb647bb5348f3d35bcdbbe475375c81c9190c6ced42ddf1799a6e61355714f73d50049bb6105183206487a6ac3e6fa1754c53d3dc9a47cd2db1916ed74b7f82c05557c9e744c372778f376b89b24fbc6e7e7a35393f2fd9701f2c4c08e7d315bc55e7b7b5bc5fe04a9432a91b3f208715fa35d5680e89758abc8ec80038cfb5ab66881249332cd2fa80b1b2d1ba80b1b640c7561a306eaa2057d89a5a02faf12f465e5a22fa28abedc3e51971ba80b9aba9920a4b12f036410a23bf42394b9a01f9d3d8b4e07d4a34d3b84567fab3c624eab87c9d6b4a7554cc39f05c89325cfea8e5c772acfa7439eb49aef6915d3a8bf21d32eee5b7cbb13f83040d6d3a336ed2d6a93e88821234245829288b4692f509bf677489bb736ed2f9036edfd69d35e9f36edfdd1a6bd3c6ddadba34d7b77dae411b303c68836af4e9b48edb2fabdafc34eff5d2cbd23c7b38e4fbf329da4757ac963a77f916fddc4e98fbac9534e1f4bfde4274e6faaeebae912ef0aef9dc457da2ccdd85fad15297eb7cdfe3d02c0cf9a63c4e267c52962f9596f8856fca4470ccdf0931a1152f19316095af9499388a4f849918050fca4474354aa18c4fc4989809cf849877e527e52211f133f69d00f223c257e52a01ef8271db253fea4371eff19b3035651aa58911a9f2b3a8e00804f33c788053e6338452cf83cd10a7cbe8e189a019f2d23422af0b92a12b4824f3289480a7c8a484028f0f91d0d51c133f7762b0a62e2738508c8097c9a433f29f88c09f998c0e7837ec4f0f922c253029f2da01e189fab213b253e491ec7a718b30386cf0fc608143c733bfc7975ec49b84ab38cb5ca576b458aaff296b4458b162d5ab468d1a2458bab13c48720407a00f223f3e333c3e7c70e3f7864f0f4c0a1c70e8c1d1e313c76c0dc1d2d2c3a46745787e3388ee3388ee3388ee3388ee3388ee3ae4e8762e97c7e8004f1d1e2aad2870d4f953e628e2a7dd470b2f4e1e2a7d2070d37953e603e963e5e5e2a7db81c2c7dc8fe953e5a4ede2037489b164812901fa41f9f239f1f463f788a787a10f5d819dae121c46347d08e17f7c58bdffe06e9172ff0e9c3f6c286152036a46c88317f62503135c47c6a206b70f11f2e4e2e6878f1d060a201a6d50366847959edbc945e5c481e2ea08b4cdc71182f60601c6fc1385abcc0386c5747cbeccdb938f7c6c511341881b12f492e48b2a316a31bde73fab0c1bca4b0cc4fd28b0b0acbfc20b9c8482cf373246b3961991fa3161b4c58e6a7c8869811cbfc10c5d450c2323f4335b800b1cc8f900b1a3eece117a7fd14c1386c51cbec61605e5e5c5c641807c661b361478c1135e8b8d081c366736c16e70748905bcbacccca6432993d87c3e705cccb71f4bc7871390e9e1efdc245761c32fb42b6c383c78e1d4618a1a3a343474ecb71c8ec8b9616c761715a665fb4b8dc5fbcc57b506f79cfe9b2f798eef29ef12fef291de63de06978cf7717eff15ec37bbac7bc27751bdea33af717a44a6583e56997145661b394f9496a19cdb6470ac76a90d1eca294f9396a19cd3494323f462da319a694f9296a19cd2f329a5d6434cb6434b7942d4aa095365124a639ed72baa66f144be48e7601575feb7ae7388ee3388ee3388ee3388ee3388ee3b8ee072082a82104cd0d5bbda93835a7eae84e5d57c4711cc7711cc7711cc7711cc7711cc7719dbdb13836c7eab03ad608bbc3f2b03bb687e5b13f3a93e7755dd7755dd7755dd7755dd7755dd7751de7dddb1d72812e911b7485eed025ba45d7e81e5d246ff4bc9baeebbaaeebbaaeebbaaeebbaaeebbaaef3c47beb9deed13cfda37dfaa7817490bef590066a22a1f5e9a016eaa1262a323a424a2a62e455d25c0a5b64d169310266470c0f183b38f490c1b3c38f193e323f3df830ce8417e90f17882f08b10629c48aa6d5a6fd0ddb6f626d5a4b840810d09021b75b90204080fcfcf8f8fcf8c1c3d3a3c70e111d2b2db37fd162b6ccbe6889ddfa45cb83d85e406eb47e68563e42903f6a883c417c2db33d80b0bfdddddddddd6dbd9515d38cc5fed7abd55aad48f245143f590bdaf511b78f68d31a49325204a948d251129211d251d191119151d150119110d150d0508bdb422db3b72d6ecb11417c3002a487223f32493e33907eec70c423c3a8070e453b308878c40ced8069997dd18271d86a8e3ac3515b4a2c2d3031307090b1c30c991e7ce0401d2376f0d8e9c1f3c3e7074890cbd2dd860011a94155a80e55a25a548dea51456a99bdd56901b34283e92256c3635e36b45eac6e205f60882d9e652d3e0b4000ca9ad432fb59598bb4cc3e97d548cbec6194f58896d9df50f64dcbec5f948dd3327b1bcace69997d4cd93a5a665f43d93a2db37751b6112db3a7a1ec1d2db387299b47cbecfb566badb5d65a6bedeaebbbf8c2e7cdafd37ed1e26469816baa8e951cf3a5e2c4eacd6daf1b2d9a9510e44b0dd17379c135415c99d7226b71c13540b4b87765b705d7b478c9fadf61d88f4411c7126365f293bb3e3333431ef5540cf0df4b37dd73ef4ec2a46c9590fca04aa1c89369144b3f7835e776bbdd6eb7ab7375aeced5b93a4545454545d6c7fa581f7b7b63adb5d65adb3b494949494949b7dbed76bbe9e8e8e8e8586badb51f8b6dd95b6b9358bad6e00534491491c2f0a84441f0f4fb4e3def94e34eef3db5f6b43bcbb832e293128974288c85f8a442a597f04983c0d74781bce6e8edc6588b595a467bc58cfdd55a91e277695da92b6dc6fe6aad48f143a92815065b8199b018ecb017ac055bc1489808a3b7c2601506ab305885c12a0c5661b00a835518acc2601506ab282815f69519564c15666c25f6147fa178b5545aab132bd224c514f1fb7d772f66da75d5da6a6db5b65a5badadd6566babb5d5da6a6db5b6d6ee274b77d7672855aca440a172c24c31112b51afc2fd8477137c4a7813e363a797401dab5eb2fe971c761c3db47a6252c252fdffffabfc44bd69bfa77ca59b28898f89a6973091c764ea656af5af4e5333c96124a7a92647c13d333660b9f56e8ce13a3129657e8a94664952c6647e90caa7ca97a95cc9fc1095a4cccf5029cafc0895df2d6dc7f9e0430f3dc8c8cc98b1c30e3264e080030c18313130302d2d2c274e6557b957c127b0cd8796f5c99f78ce67a6896d3d9c6e3ee7b394146c93313dc504b6cd186fe2399fc562d8b683f8d8fb3264842ff13e0e38948e61c050015fc6c4a87c57818169597b4701bbed29501800cbdd492cf353e484657e924c58e60769c4323f472296f9310ab1cc4f5109cbfc10c9fc0cc9fc08c9fc04992bf84f8182a24505a68cc1304ae010936162879419a64ccbfab0639c1cd80c867b68594f877dc0394e2898e564f161a507532636a365bd43cb5a46cb1a8796358c96754ccb1aa665ddd2b2bedf0448f2e4495b75742839493938483847f5c666bb7183864608216ad408c21644cb1aa503a265fd430fadc33eae0e1d2b3939260e4eac65b5dedc667bddb8d1a2a15909210459a3861844105fcbea0b18c66103a2c463f7c1871e7a9091993163871d64c8c001071830626260605ab8a7bccf3243c30735719896f5635ad6c7a1657d192debdf443fa56c69599f2b7de81718dbaa8e7e0f35c76c597d5fa6e2c45a56fb33eacd035f252949b56811b235a1b79724b354b74ce5f0c476a5d7a5445431414b3c293211cb1d9c882739fe945c223983973816f19110cf031411665f1fda946c22df94c814afe07ccb1326c234702f9a6e4ab14410165d55f84a882da31b13c5209fd14d11c5202744142a98a2c84345c43940219260f85288e205564472095785f8c299411c93be15624b88ac95451c7fc86ef9903585052ea85dea6d775b5b2b4eaeb46cf102c905f53f214fe091bbb91086d4eaa83d327defb4b53a5687e591e98e4c7384908e8c8a86848c8a82ae1097eb3cda79a0105405fd0786a5708cb91c0ca5221d4f30a2d08894e94d47a653d16928d393426d946951a64154057df3e81f9fde691efda3797a87d111468e8c8a2415d51f7548a6ef5b5b20d627d3df1f3748a6e780e4ca438b541d39b748c5b91122a6dbc2d46e6befe5686d6befe5b8ae76de077a1f08964a6128d62a8ea6533d551295a2b43cef8b96e78a92d092f3d65255cb73a543cde4f9d59284b2be5a5782f0f180340969edfba240e407966288f5a4474056fc52ad158bc51ac718b4b66ddb223915d9c2b2d271accbb22c56b328ab8e279292e418034551a831c6b70352f5dd6f07e40a25212121a161492d292929a9b11d90b58584c492909034090949154da996eab482c1e247d2f50ac086e620d377fec09248cb1898d829a5e5c9f2a2afd7eb45495412244890a8288d828282d2300b83c160b6339d35ff6f673a6b322dcdd7ab541a59638c8a2949b762b0461dbcfc917c548549bb5c2e97abba4091962dd204759272d1f27551244bab462bdb775c58cb8e66b3edb06da9c4d48a5c89aa950db24472ed32cdcb8d2d1055519f4355542f734054056bcc8124114b28263992144d50c55309ee15499224494b730b6f3ec2dbbd11e4f99cc0f3e97b2f29b6be3ac6b8d70ae479335e952410597f4455d412a0cb35c6a8e6eb0433228764daa40fbbac34f7bb2425222104b29ea2c0ba5a122450505631aa08437fb12c498c4b6881ac18e9c80868c8ad47ae3b9547bd39e2860c4c0bcb8aaa385581ac78956253525252ba52138ad8828926630e21f9faf004e63a2d4f919628614b94e812f5fbcb129728118b993091522286718912319615b15352c4181d9aa74dbeb618291769191b7568b23431ead038a5c66aad5d0a2e458a9b22450adb35458a1494c4a289947ae204bdb11413274c95afcd30e5c409d31469a9a282028548cb1429445a9ea418c3a42a545450a04891a215638554a1628619605a5858b0b05d4114638cd8ab159e5a0850737d0a152b6a7c6a50ae3fa580ac35b8ba825a9666a99ce9b4c997aaa02ac61864abcb1b96a79831cdae1863d0299015b3b000408b31464bf54ca74dfe4eab1833f4803cc5dca22a4e32536b3a6d70b962c58ad58a15aa152baa0a0f44b5765bcb75b43cbf22e9077a1dc85db071424ae98d9076d80f4f9656896e728f3c984eaf37373bb21d7920c915df0684dda803984d270e4a6f9bd68e33d9c3e853cddd68dfa237c6f02a679f833dad7663570eaa4c5599e24b55506b6fb51d08e9adb562b6b6fc84b8b854057ddfd6143415abcb8c0e367f7545bd0ea8bc6ab3561f61cdf4a6f080a49f91a3ce7272d423b39e7a7acbb9d109d2c08c8e3ca0c8b43a0bbf20643df56abdf5a8efd671923d68889289019136e90cfbf27d24d96ad13246cb17c8ef94f9867d310055b157c20d14b2b5d40a65a62a646210a44ddaf6bbe525425a7a951b9096fe9b81bd79442828a847460f18bd30406445140e13b00b96801203b2de0e4189810e3c119501b29e0ac9a7b5d6a2d8400ef24929a516b543d6db5b0a136eb8a1781d2f44f125031b4651a5478c493c79c2cb492c75e0428a780a32944802693112010095f81da8f856a2eb082858222c03289744343de05522962082ab25ba8e4e3e13b12505937b228a36687d2eb1258592fb12451b90901051aac0421145108c30110b092f925f287da528ea74582499c0dd12e22b0237268e5cb026c49611c87a9b228e30b8f484d87201596f290dab7922c544ac042d2161ada82a854231667b9ccf996cb18918c27a1a0bab8b0779212cc103f93c8f3669ae14d36071801142780d8140081b80810584fc98a03580041a2a7401081a18c208862a40bd7c7ee57cae740723f0d9810876703282480a3e54189002065859908204564290c28d2a89ecc440020492c488e406246b0847d001c913483153215190814f144e60822870005585286060a58128e44809214323aa1f64d785940c480f0b2916909f1492c892127600052194c4000a3b28f1011460b07a01144cc01222c74c854041074b0ce4890c2c219028a42a141da56e45452e205f990a291a821a0399ca5448d10b542c2051990a2922c190a20fa8b4508481214512200922bda41bf23bd251c40a223926ac50407e4650e1901e11161009ea8024022498a99027d0405502b2cb54c8137e62a610268cc10a3df93ccb97145e1a54005cdbeb9d87199a2f37a62afab3b95fbdd778afe30e1cf7cb5d1d609906cb67e5e1b31570e590effd217d58028f6cc275d66bcb81dd95405bb97b39aee3bab9ae032f67ebb51ce6c07bf1596f5c0e03d9e1b383ba243190f496cf9bc4eddc6bbadd6eb7dbed067e644b47270ca4edeeee1e2a3a4a32e263717777dfa406cadd407237cf4d3aed4d52e7b44eeff0babbfba6de806a501d2a0a43d5a93a7547dda93c3e40c01f928cd49cbe6581a14034c8130359737ffd6e716120a9102520e5fa93e33ef266bf83565c08bd2190f5a806f282480f88a4f526534a29ed9ddef9ba5bdd49f5d6fd35d9b7eeeeae3ab4a8bbbb4f96eeee66c9dddddd9d1754df414db65616087f64eed63ea4f50791cc3d4816c54ccf5507f58ed73ba6e7b53ccfab4949499ee7798db2b81af1cc16d79df33ccff33c4cd36d3dcff3bc3be660933baeb334b6ecacb59df7aeb4c9dfb5b957b678813c69dad2d41a36df53db5530094908ca627ea9834de9caa186b4529aab4dbeb98300593f08588aea5e398ee3385487ab913629e5388eeb6a7f60c312e8b95cf1f50cdf7aa93c4b27f8d34dbe7affe42ef225a5e924e5e9acd2e4aab29f2a4f8e2a5d5f95adf25ce1f3f6a750914cfeac0de19de4fe10acdc3f692e9cfa893aed04abde78f5738666f2d369a6cf9860929f70c96b4394b2e9b52150b2e943a8b209b70ec3b64a2729c1b3caefabd2bbaaec9e2ab9a3ca7bcb267c2acf9a0d9bfbb5214c727f08536e7c9e7ed2366b364a997c6d8853263fc4789a626260605a5a585856564c3316fb5faf566bb52249f1a366fda53f196f1acbb39660f97da55892abb2f56ad3a48248b8652e559e9d5334ba93f82473288ae2f77da6679052d384cff0f42188cf902cbbef04dd3f1ef25f0fb23b6dd1ddb12f74b6b3b393bddb23dae5ac16a95d52a9d4695a39756f66c6462b87e1695a39c41629c9beaceead30a52af7deef0d35c37fa4179e9ff775a99f5f267f92390c55a914189247d54c624acf550ecf570e437c76f768066bd2ad8b414863df09c8d316fde321bf1e642dcadced51e6ac91b100dd3d7c4fe4c17f37fd64325d71146fc35ce5dab4f6cfebd5247b9369099bb1b616c27b1a3b6f7a89d2d728c5b51279b27c5fea6c19c795ea278c68a39567681ccfcdcd4dd0cdcdcd4a9542911c582be8d5ea75b57661adb76c0241d479af3dd519ebb8fa5ac771acd54f33e4382e5471c2a7e97dd64a954291254a4b1fa5df48e928522ade1e863509113a6a5197918e2fea2252f2743c5d1c4de75c4a5474f9c8ef7b51938aa752a965fabed5485293beebbeca8595e3c0ae725e377a9ee7dd86983dec1df4bc12c7791c573f53eeb1e338ee350ed76896fbbed249536bed2a8ae3790304c1d7c0d37c19c4359acd366e0e5f036f329d4e9b876b34adecbd464366ef35ef3433364eb2775abd651ba9ece19996038ea08223a820d3d45cf36ea3cb211622d485dc9dfe5492ab4f4401696fb5e0aa15dededac8b43c5f2ed4c5c39486b84623ced868d1888d69780f74a998e21aa5b721668a6b35fa4a2916c3d2474dfa19f0bbd4a4b5fa4ecc15d76af55cbd8d2e574ca949dfa5dcafa30e7d9355958584d05a6badb5270d97ebe9b30543db8ab6d9e36d27c4a98617448d0322dbb778d3c5eef4daa933dd917b4d144baf953a8f0c3defb5eea56fd526cdfd5efb5e35ee1d4d97b9569b3ec8fe8702b26d5e7bbd5aadd58a14c5af769bb17e1e085faf566bb5224951fcbe3b049969f601b7229f5c3f33c497693622723ce0d6d241448e5c3f442bd36891cf0e1dd912095be0aa46ab2779e35a786bf6a70fd71ad74e5ea3a16a3493f74dafd14ace9d3c4dbd468e5fdd554bbd66c23510d76860edc3b4efdceb275cf330adf53eed542be1dac534d559af9930cdf4970bd74e702dc4b4f0de53b8464bbd74167e9f04d7566f5c2b9de6e15aa946f293d768dec59398467e856bdc09a63557a399bc755feb12ae71b572f7de3e5771ad7fab09d7c27f97fc5ed7e6556f0dc2eefb5aff36eefe3e1bad4c5d40f07b0dfc17a7a2a8d4a95b697335ab189a0100000000d31600203020100985a22049a23c917b0714801184a65c62489649e41888518810a3082100000000008c00489d0800210ee800a2b8be23a083d82da9808de0459848441bf284c38d49a73b9fc282d4698245050e8708c73a98e3ee157c357d99e877f6aa5af80e7262066836b99995059957a1e185bd1e7550c5e568b084d251f93ede1e878b432b959db8d637445d3d2dc2846d01369fe39304aef28a833d5b25d4af1b7762e69c6606b4a77996eaee5cbb908ff5a1bee58bc06ec28f80eeeffd08d7509f971efa1e699812c97447fd268086eb73a0f12c7ab061d59ca5791983f42937c6acdce6d41531c2a2fc7ab6654dc5d132d9d0a500bb429837bd309803c9721bc27d01c0303a5e88e30b97b86e8ec313feccd3aa95ef16901c831a451c415640f7f535c7f17429691d3db4727f538d5361cc1f1d1243ad4f99999bcc9f086917471e546554a18c4351c80fb2a036b4097d5de4932b7c0bd49075378df0eac12554811040939f0ec2afc7da57f0c08653d87433b776751760d9c5546d2d58bd289e16b953c40637361debdb55d8e03bb5705b56b99bbc37d66367c1b8b4464ccc48164d9ad0c8577c34c3e9c9be39732b2a4efd6edb7716bd8ff86e96f711c5699e049142ee9e635d73d56bb33397592bbb916e5f06a12bdc95a1bfd86d08dee12d83f5800d56f7e6e55e86a97a67ea9ff105a8500f3d91a8271578549f9e973150fbf0ae8591f203de0e8a7fe22a28f7800faa5d73e8f836c8e2672eef4059f3a0b5e34748175e8544767d33330bd060ef145b55de47b63c57d9c8abf0a34257c47c650f1151025d90e2e13455b2e949e557024b5bff496596f44470bb3273183c5b59b68882e7866a7216cfddb5cb36eabe5363214f3ecff8fe4073075cda2d214531db1e34f7f55b64881cf58a046df5e8c90aa0824aa02bbc9a136193e03b98cf2301411446e3291dcdb5c3f6110b795195aa854da6cfc61d5210b51636f9785dae34371e98e49b2efb603e939eaf57d034375d0f8b42ec22b62cdad58b5c8437e432852c94dad1a41b6d8350b79882649b0e451726d35119cb61c499f0bba7e6fa3ab6e047a01b8adac23b1863332e8e7074f3b19a1ba8e2f4503ab52189ca14b8ab726eb28f9aa81ef1d8be925d9920d73feeab2cbc7cbb56759f8246cc9a5b83fe20456498d46f7ecfefd62ef79361969fc27ff2e22ab5025698a34d26b3df53a0c52e469d6c3ca6cc288e5ba48d9ea7bc2a6dffe370a59ddad6ec9d06078b200fc7b5b1f5690ff106718d9a5f99a738979d998294cd6e7828d866f2960d8232fbcfb1b9996116958ccea5e29c1254c674f05fee1c3a62e32832b13904c4e3844bf841421022011672801e1b9aa61e0d3d1f8b7f7ab6a04db6fe8f175ed93aa79e3966179dc2d851b8e87b00e7b111d7d54a7fb9b09e98f03706678220aff8aa89e7366c0a167254dbece461ca43bec54c98caa051eb964e4d8f61057f808df2174b68ec35e18a17e2f2d2bea7317cc31a0b1069db9923f3e3320c9d46238aa7606cbacbe3634e9dff1e7c2685a65bc824ed0eaa5217fc99f576dc68a06f2b78bfe31b011917795d4751b9f941d53f0c9344a05a7f34ce51043dfd5aa0a588a59dfa6922fed8131b5eec2e7f5bb2f99664610c84fbbab8da2f8a36ae81988598e088125294ef1bf2a2344d6d605f9b275e3f570010ed02c4520aec0c75bbcceec8a4acea93ebfe1c9dcb7e30651d2fdb566496750018c5cd30432f0de1b66f9c8aeb408e5e01c7245b3b8afb0ff1b19d3c8fed59f71a174c9a3dacd17f58a16369d6bafa4be467dbdd936ca0ecabe3c4b18ad18289a28c13540941a3cab9433deab7e5765ca5c9e97cf003b833d13ac7a892086c22db583fbf21dad8e49a9f00e5ba72ebe6021639e73b1fd4792f99827536518e65689a982b85d29c65d9d9d943ce6ee9f5bb3bbfdc1392803721c465a61a257d3546ebe56b85894a53c6e6d3193ea70842b0e240b8fb4c3d51154b6519b94baf4c3e9d065b104917c18e237048690e54f22fce712ae0ef5b710b0d0838580e1c7710a7e7eec0ad56679879021d7e3583185c4f1fce2cb61090e217c8b946bb0d7ce5f70d070fa5a8b0d2f59442d7336d4f0396b70b272f97c211829e5c35a39815e70365a7d6fc58f2502dcd8cbc1c78d5b83cbed89328328a7a08865ef2173c76f1999b641eab77a6a7b6fb86784703264fff2d2272e1af5ac31bd1716b7cec5800266f0b988833a3eb60c95e2c909f94a54b66857f4ff9a6302b97b46dd9ae7e38feccf9d747620854cbbd2e2242a17fdcdfdd4ff6ea1b6b9cee1dd453eef62981fecc64566f7a3f06cb3680132924d0d512baafbd88943ad44443828ffe63cdc7c437baca202e4241d745fc297dcb0fb6b80940e372cf5b0367f4ae3bd0c075aec9499aa7ed36cdbdff38c2a74333423080718dafcfdd93c168e0600789c5ab0d4819554c4754efa23765fb15f66b1c6432414c3fd04b8e769fca0f421435e8bc51abe6b5df290ba5b909382a02b331a95c3a0469f8f94fd5ed009b5e7ae3e41445712c5af24de00c5a30775703e8344d00301395fbd20de098c976502965cb6ec04183d04a98140148ac23b580e2c43c8f7a986fc5a5913cc2c878ba98978550dd60a05d999801f6d3bb1534b053953c60e08caa5ec44f025f83110f880bf625f968769599b4b018f73470e56e63c5a65bc161984b45662fbf8485ded3428701e9d6bde0aec9d0dfd4f99f74cff6762091e5703894047ac939d5a69a6ee9eeef3320bf02f7302b0b037407df73729f037f7ce74c27263927152b8df98d495ab06f32b261dd1cd5e19f8b22461c18da87b07440748a9ced5442ec2e66a84ba2e318aa1bcc9b0cc9007b8e3739e720c05a315c22746d02948f0f0255f473c1d648f375bffa4d3c6f36f2cb43439c0581f527578dbc26f1dc1f530710c305714532095755a479230a9e2988e294161c716fca16f3e0dfca708c2f399435602b8df9947a73e938bd88351a874f14fb2b42c745156757bf5de5e524bcb0a21e83695ed08f91e08172fe45026060f4c1a7d8e11e16cacb4414afa5cd90fd6f54907ffd04861139f8b24546e2a8abb9c94af59db610c1be641f879ac3b8ce1e7b464b559e408a332135506457c8b3610a9fd523717a470ec5627ffb82aec65190b54ad67f78573243ae10408b2487635a1092e1617352d714bee330e9d3e8276e7c39e81bed6d7662a7e058452b6a6b2c752f03b276d615948350eaeab5fa2e8e0545b44c6503876416408412db6d22ca60164f2c8246a2df907e91fc36f7ce52f17faf6290f91cb0e49633ec9165c04cafa2c9fe600187347c9fe5f42389e4dc790608fd17a5a62872935d3b94e3e8c39e6d9b2b5a09a533c242563458aa1c4017dadadf96e5bf6a78c987ba539f3f2e5038cd53440350cad335f140f653bbf8a7c1451c8a5e44deb2d94f7d0f89ae154518cab914cce4228ec38c8992774905313a6140f97612d79d1bd2a805db348cf720bfabbaf5fe09137dccb035f55ad36f4a03a81b6eed26ae893441f5065a68b05de3574b9e5d5e2655c993be0bb505a1ce35977d508294b8db14543ac1026434d53c9513a176231c13bd8bf2743f7ad22d96c9db2745dd2342eaf72b8d5ac1a63230b430c4d407193b4fd7738d294dc0f188c65c03722437b76daaf52f7a212260ec9f218c1cfa6ce0db8a8fb7f367794634c270000959051dbfa65723d9ca8f26213898f6025d48ef2cabaeeeeedfe2ddcf2f93a1adbfcea277bf7d8ab46121c15df5764162e22a354e03d35631c5818a580757b015363722ddcc23497dfa979ae6588af56356fc4546a5af5620909eeaa7b07af1482215507855fa826e33f34c5bd5f4544d2428368a9a8b0425390772e54170e97eb32610202425401396b9fd3205279bd98dcee7345bf730515fa2fcea22afce56d55c8ef5f9f40b7a70c987cde64103528b02583fef13cd934374e0de476fca914be6446805ebfc928e2aa030d8c8dcb11a6e88a4eab91a6931c5900ab1d61bc76e20714d4abdd1e37cae8798fcb7b5b4bab6445c1bbcb689e1791c5c4c1005c482e44ffaf16c6351def7cf331c483c83ba805a4321e3adf80a4c3463400b244b31216d8ac26572c8afbd3845250c4e9df37b4b60f2c7976a8f0a62a3994bab78ce486a64b3915d72a265ba92e995d6c40bc6cb99a8b62af72dfc2b31914a18b4d6ec5e3978142587e0615841c01749356618b6231aeeb4ead9592977db7b1af51425619af71dc8c576336dee9f0bb446d40c2b7379b601edc77d8cc7fc86eca34ee86211dd5d0070077a74714308c5eb14869b3f0f94c0904f429a532d85b916e34a074a82526ad31a7d91bdeb3cc7b7daea36708c26daab1bbc2896e9e3325f06e03699a35b641cdd900574ab8cd06d32003765c50853770f27ebe81699378979b2846e947943cc9561e0b6ec829b32edd0e6c9b439cd23d3e7305fa69dd33c1980db650edc9671705316a0db326d16f36504dc2ed3c69ae3650108372fe3a2c5bbc91b3d5182235ffa5ba8df759330e9ce229f3ad0a20c88fb1aa6450c26a8432fcf66cb4dcaa06940a2d0a773bf6be4b6139e95e59a06cbff5d5b23b7a2205ffd1b1fc09c031cf063212a45d1ec54947fb266862c63c63f21535917c0759a286e8a047342854b8b2c5362e546a58b7814dddba2104ff04ba52657d9fac5f4f2f1204ee7a25ab433ab217f1bc6dfb14ba485c1eb99b1b3587c47e76f7b87e66c2dea6bcecefec9ff8068d1785a57269cbab9a6a725a245892695a4b6c1781887e64ccbe96f9cdcbf49ffd67bee0cf9e18fa6a17be051ed2bdd1fd1f6e5cfe83a22e8fc25b872166e93627727d7b4950bdaf473e97129e7e95f78829e832f9d43b828f463d2b96eeb6cfe2e267df6950fc0972a8d2c9818ab42cf867462c417d483e306ef58165e12627383e030e0174305865c0b9474e01bab0aa4967bea90837fa35678a6c51f385cd7d5463df9cdb39a7b22b468364a7926c4a062d1d0ed4a1568f191185ffc6843ad7880cb5c16c7758d5524fdc7a71ad781ba6ca517bfe92711cefe7de9aaedce17316eed61ae5e41ba36726484c81d45b94f6004ad1050796e93e6fa89051c2001b0ef275da63fce4b29ca03fb3ca335bc304d2370682f29b4d89d28ddabb165fd4fb6ac0dd14f5cfc1db710161e5226bbf2ecc3061ea0518029f7327739d081deaad20b9fb07666d68cd73a52b1d5527961201ec528681bb7d8a92df98d6855d89274c7378dff2f5ef8ebe4d9db1156f8ebd94af835af750ff95701ab7a5c6e4f76b4f05092b2b2059ff0b2d685df17ae62641ff1508cc63fca62ada99fdd06a04db0a7d22e01f69ca884c2309115c389d393745554784639f9c0221377b71a112c66d407516ea45d21e11ecf4775592b127e60ce9eab03d379b5b794204fee4b5f3d4a3e1e7ea787b024e5b49831f4005d676a05ba58b6e80178ddcb972e9ca7120df8ecbd58907daf457364569236782990ff0e6eb1684e14834cc3764457913c30057a9bb927fa1192f3c4d738c777d6282c5d246a10c0048711db906049e366bded454efc3da48efbf2b0ade07b87120e29803cc18860620488742e1a8e9e7cd1818594d82fc9af076b3c896504e23a7e8ee2d04d5c5b7a9d41f5056835f00f72b000eac3df81950b236b12642a6bbe624425460431e9b4cdd694626c1de368972e534085bb7b39886ec75dd15f352ba49b6f3864bc8efbb082fc99a350633d2009e9b61389285a00b14de145a2eaa3d878563819d1e97a18c5f0ed5fe18cf48b4a1a6c622061b646ce842418cf41f3a15373e152f2df8a687546a74bab6cc91153f08cc20c42ee2e968e29b29984b26069e6da268824d828647c09dee4ed70b9529d44427b11af0789f18cbeb6f7f6b5af42e02a777a4104ad897e66b302b68868b5e91bef392c4380c9df32d11e4371e9a93e83727c563b48fbb130caf208d075cd0a11e39c3d5487137f7b8a76cb0c62a2bc5132f9d7a44b6a85561451ee8ab2937c761a369d83cc5b0877ac10ed3ea55484d11665e7789048444da25ef3ffacb943b03682a44c4476eef7647da50d3c868243a6f5fb61df2a4a37aae021de09c2b339c787364aefb9b1baf36306b50e54cf23371a6669405eecf7e4de1cf73b38d50a470660e785d4d616e7e70095c954fce5c71bd36edd944faf0049ef8eb144cc9f0cb7732e0646ac71c2cbd4b97e47bec79f3f0866c6eb481a005f9a2badca7f0a751c4a7c9c07569b720133f4b6178d2b2cf8fb555c4bd13ca9b572081473e056513671d1ad1efe40cc8bdecc7658b6f61589d510452716c08c2d0d75308bb7d19dd5460cbee44edd63b54ed543c1b9b9c69b1f395f0501b38ab3994ac567452c7fbe62f9ccd985aa5c4b2527a644d0ee93009748ebecc9f1c412954802b3626359361329aec9f561d3d1143644643cb14347452e6db6763188f15eb16c1855ed8bef2874ce5beaaebcaab4568bbf1d91c869bed4093727d00d1006cf1b9badc69701917a7d93a11fc0c59b1134cb44c4d508773677abcc12de2c7917ef5cef85d01664c10bb025c51f1cc6c0d3981f8058dd5cb3b3f26559a7445f3871093d17afd4c45f0bcb0f8a773297fbdb3a88c25b04e2f14a60368ff7a363ce6fd4094ba25d499e3eee788043644a900b5896cc70830b7cdb08187884b6672a4b841302d19b88b3f8a5d043a3ef3e3fc4131e6b2e91480c25a6f126c800d0ea6bab8cc03be7923ee482ff8706e32d88b32dc7e8232f249772a330154e22fb39d39f1a798e6e133aaeaaa2f4667ece5e150ccf130a48b50d53c0110dea1468b6a859adce1129770525db4dc4a5dd72ed6fb52a08561ab2de8ca581ce4f434decab0ffe9856091717702fc01cd7ab27b127d04bab570abf82d375946a5aa0ff72035e3daea8ab7c5ca9b7f2f8507ff1d1a13ef9715a5df2b176a8efa5010d84e5d7dcc95d4cd0979e9f26ca2f3790bfab7b6cc60dd04f3b262d90cc878a63c629218010f43f9e421f54debee9d043075203b680ba0677bb9549d60057e11e951faa282bb151dd91aeb8c956859626e145d111047404d8d93b70433c161cfe44c4f4fc55b0903c49f9f54bf9cd3e0147ca694bf386050646c5f64006be1aae7e64cca0cca709ad44346a2e4b2898ff4b31592fe1d782c19f17f7618525c92d3a6cf8f47a9baca2c672f918211586320198637265c4f906470c22bb06b1e4a2040c2b256385a10866d6a2c439da9cf3c34f7e7772ec8893596001388d3f5f838845813f2a60403db0928b72b2708d09b2a3b3757148875a1e1bf8cb5248c4e85c738a337c407a49fbaab1d329f7f288b8732ba6fab970bba2565b920ab61e82531408e23814db262cb289911beff6a23bee88d6e87f86c97157259171ef71885f318e63664120e8cdb19d8fe57c11065a3513acd22b76b3865bf0086ae72c439c48fed85358c8ed533f8d396977594a417dfc54a2e49cc8c72ff5f4472d8ea8ebef65ff8a1028fd21d701661af2b32bdf67046560f1809b7e776b4dee45b0d28c5519943c78d117f7758e8189395931a61d9dfe92bc6c4077899f258d1492e5f9c6cfec348826afe95eff0310db2d65ee90bb180a2d71c9543c9266e3ca4775f2b8bf1ff6dda5323757055307c1ba053f9b35e4052845abd20bad138bf25db1732e2133156b02bbc4110fb9db3aa7ea6c348f7b7201f34d4dd37e5d27c773e283d3b5caf4b75a29e4ac2f992aeaf1dabf2373d8f75481b82fc765027a98b7e39931bc51ff4cce6d7dbce14f063beaf1724fdace5fbf72822d45efa7d8f59283efe5e8e7ce24737eed36a5b93989cc22bad4872b44556e9eadbea5737691b1af85f7a278760a5a71876c789673d7fd9f11db6c506c8dc2fe1fc819f81c91b15870df45cf283b4d4634f6314db245dd1edc347948434d0210a911c3776841894c407314628729fdc8649a0286e60dceead45e8a31772f2781d8215885cde12624c05178ee3660a99f3cb3508318392022e86892bb81fbccea5081c1d939b8cb1c6ac423c515223dfb31699b50cadb7fd8a6b0c24aa9085b0090aac55fe057f2b883606ffd7a3e7365931e79f551d4c08cd3d66b65a99aab92d40c468ef5d950d525f30a64d9b169375c5a717bfd2324652be4b8aa905ff60531150c45003627c9fcede2c0918942cf6276dfe74c107c7aaba7490bebeab7300b0a8c4098d50f95c5feaa27f2de312e1ff637ed7c35c5cc4af59e984cbb51523b76cafed1f27d1fefdde1a8a0e303c1d67a47487c8c61eff77aee305383bcb20851e080a693f0e21464ec244e3e47168b9340f1a0e1e17ff1c8448cecb05fcdd24970b70973cafec58832dda58e926903a5c7e9ab92d15862f28f58c6c98459c6facb7be0b68c085b991eb73e0b8c5598acc784166e4085b3bceb0ca59c4961803cd1ad7d570fbb81de690715753a5fc488ae2f2654e185ea78e85f084c5549ed6b47ae842b61c5e3ed6ecbd22cc75b9906eb5ad1860ddf794ec6bd315d19246b60cea4912edfd50914ac3fd45f23af88ae45036ad7ad051f0a466d90a7f5d064d9e0a4c9b2e168930537c05192676a40a946fc6376fa867f4807503961286b7d043488b4de9a871e77929cf29573f41a0ad2dfb95165d1d2bf035e33dd340ea3e4fadff152045d12243afd8f3fefbfc95dbe8f8646ae3ce8fd7aefd00f77069638d4673f55a42b431c0bcf33820f92a01d5eedb7c34fb80ab8674b6ebcba7fd89dd181a4e33333b55d2967756018321a364367ad912791dd9a1bcbe2e02b8428aba9f7a26967d7b8838be125162d182d6edbc7a5e82da4c88d1b0f6446755368ed0b150556bf5b18954a15a3654e57d596e32e06b1508bf4cc918abdd386c64ef438b8229cccac435e36558cf3c814b52b2ce1d92924ec7fe34d50796f64b982904864b7c5aa505f8c183e79ffd20b974d3379cc968cce25490b9d350e287036384a4ce173d938097e799c4d51a38649f0074b299b30ed1c4b4e7fb010a1b5b4faf7a1f1819b890d57525073b1572ba8eb2fc2f437d04dad46fd6d9f7891dc725c569c788ca5953a7299e829becc007ca6e0c206931df025fc016d448810263b7bfe1e2d3c8efee009b12730c71d310e0e302efab6b07ecd306f99db72faec1428da0be0fc25760d844f911b25dcfd0e521a1030712fea49726a1086bb54ce33748be9fc25eeea2c44d80f9e66ac9bc0f9a9bcdb161cd096b858d02ba093fff14ea006eaca3be8fe77b089e86864569c628fb793980c01aa354d90f0ff17a388c6fd1c929f184554974a641c24d668c9ac39a0f9395e161d020c9ba447a5d751108423526e82480ce306d9a108dcd3772c08fa0b1ca361876a15571135dd0197d88f30a9d67417a8b76bec46391b37b6340528933569523f253bff81bddb44c22a239c0d2770728d8d81e90be187dedb57b8c6e03d0f3f467135aa9f0939d87c8da00273f2333ed4f4286554aab029fcf71fc95c7432143ef0dc5d9ba1b588500a8cea1bce887be1de50a8a192a6d515e796d5ff67389e716f3dc9b21c15bc0ac6b50c1e48891e42f12344940f67014311b10a8620f9703a21ca76c7c45dd160994842972d3eeeb724ef6abeb9212b12179032be6368dfc4503df8c65209e314c01743bdefd1d6ff4f6e160ba1bb4acd0a761c2d007d99d0c844bc9f0842066908834d056fc07cd74f2847ff71c205220c35653b2840c16b311131a986d329eac1a4fb2a87825510d145774f6379b4bac46955d589ba427a1c75e399189dc5476ea10e68c1252226ce04b03e69350c9b141951f74010f07bb5580f846aac8e6cb0d28c2d99ec55d18fc424fb2144832366816d7a1a398f90fd2c92336220fcb419ec0d90cc2d4925c6025699c60623e654766cb81b8bcac3f2d01bb269ee91b681e01605ce6897f597c4c5b7c445327f3a8d8a298d5493bf0cb4a3eb5289760b452f0e424c326b9d028927f6cc8df6cbf806d4d1fa1cf639652844d5d9265f219d19b6b3421f3adccb9bdd763d50cfd57859c0d3d3c32f333c49626f18557840bf5e88dfb3416bd719d67cdfabf67f4df7e028225f2d988f6f1c2a86699b93d1baf8aa5569eb22f12507656feaedada993203477af6b18b254559e198af621b1341aba440fed15c5a8339229937afadffa74378c68c86d0a91944ce6c569329330194392151280846f550df251de1c90471b3341a555ca5148a6b550be2fa55eeaf141fdc5a3a3fa257cfc611f221549207fc09075a5f878c25c9f82114e6266c198c30efb7248e0419b4cd5ef2075438c89780103403d01d79c025387bb63ad5621cb8302253198a9f05804a036ad66b09385c837ddbc54996811df791441377bb857c06a17579004f3a4c6e263151c08939e91d04d93f45d308b8c606d64d617fb92081e76eb1c3ee156937319ea983a2f8724100a2735bbc251f009813d45c52622a7e67a4624ad18b2b2d3905d764c46c9104b43d87649411eb3e5816d8973fa0df6dac1ad41695342ab5c61746ec3afeed438018657059e99d54c459a2bb309526342ea7359aca3edca85ef7d078a66c662eca81b34d694718478825a8ba3dbc30e512b38ad92f9804701927a6794befb80016f1de3674243a31c34fa2a7c64b431cf7627351c690d582493d3f590530fcff6f1cec51d5b224c38510ea1fd9b424a7b77b93640769aa5d40ba8f2e1df2ca5aa192eb50d6cfe503821435c126e8da7736045e5476c420a940cfd70ab8f59b03949365f0cd1b94765fea401570643ab2382931fc62a1c111322c856254cf3a76d26b8463b42d356e15eba8dce7662602bc3e5d698cd6d70800aa5c93b924222527c0cb7542fd8383f11d43bc7154d955b05eea062357ed218581b1ebb43f36dbff234f6e98cd9c856838919c5360785fa0acec9c4c9299ed00e659d88ef0c38e536e51dc104b50726e99c1a5830d1ce285768c7485ee3cbc98e637688e87a52bfe1f66f27ccc4b485f0d89e0ee84eb5792a15a0de2e6b51526b22fdd97018b6cdf35030aad2d9263eb4ccf8037da12ba2ec5d517063c11f7c3cc120d929b1ca539085231e9cf4f82b3423879bb8b232ee31e5874ac10476837c2f4b1d295db2ac75f2a7a5dd9ae6a3a6c1975f18b99478e0850f557b1346cadeadf3c0392745b5e45c0436d4de083cf9148ae5b541058cf448e69c3c3f7acf95d576b3a5c7746513ca06b040ad9d4d0b90cadf2f8607de8415e314b643c2e6159db1eb74e4d7842438f3272e3fceeed52b632596896d3e8dd64fb9fecc8bfddefcb7906586c0af727c5f4ee2db0f1ce21f46a735903bc11991ede29cbe04921544275365d13995517936cb3524b9d4298011e1014504ef95b44b82a2eb26d9f49c20a252a3d03e6406cd259e95b45d9bd4ad5f6068daf0cab91ffd2b8930e884d255ec4154ba09ac26407aa92e8d0751c724db72654b66739142b99742df34a5850b5da1ceedc9a0eae50ed35061d018406c34ca0b983cec552be21d4388fc1790e935399d106c0b35f572eae870233d54002990e5a05c01f47954127dc2b713f79e067752780166834c76c58e428f21760b29820641d4237a4de9d9660a170aa3daf92569f9f2fd35ec046cd829b0619b60c3fe60c3563c07a344cdeccf75d6e96aa9442cbb8081e404d887971fcdb44ba46f4d0f66ec77934b95ccc91c43f6557b29d54ae3ea95915639a2803d935c7ea918b89a6e0cbacb6730b90c1386cfcb16e9642d052f3e4054e46a1bae0bb2fc737af05cd42d1aa37fee9bb19035c6ef2d5a383dc8a2ed8b2566628854e2e7492989b66274f44e4ac8c67508b316b58abdcfa29c4d2a97aa4afa2b55498ba04e8c39c1a912c151ccc9e6a2ee6a4c90bb0dac6318c3a04387a8d49b12af27d7fbab5a56ef83ebc659e739774a548b5bf4cc793f2b019db0bc00c37828ee44e28c263814db9f1bd80ba427ffc810aaba4422fc389f36a37e8f17155e9331e661ca8af1359298f6071e46f4f5c284a5441888a482a9905716517ccc10a566c747b3b906115b52d34d3b8ae9d0a130c879cc1189b043c56f294e8ab1c1895856706cfb1344c7883bafac1bea4a5307f0a9cc514c50b0e417455f7839d269a2e1ee592b998260de2d57677957f71e54fa83785574a9e104cc6b3d434c11fe2913fda364b05a0eefe90c3235bd0525651dd727ce5d3e93babe699b56972025d48f02a624ae550da707d51ad9093338bf3c6480cdc285bc087186a15767237561a473be2a92e0fbd0d4038c36855e215eb40d8e42c2c79ce4d08b9f94cd711e3ec203490f3ea6f4224b5762591e95acc13f4a3b1663d4662610f7257cc3dca99297a77bb985ae4f3c1d4af6bc583cc1de3d656b3c41b65c896fc27102fd34c25be0f7d52e14935a31f8a458f81d832f9592d339ba29adb92ed12365c176197f98e2518ad168c4b6186a5eac66e67120bb4d5052a67f2fe5ca123a4327aaaa56b001b243c84adc2d613326f6be7e588893fc61b4d4f09d510cfcf6b86431db56666c316b4b676d3523cb59623d2bcb59b5382bab5937527ae71d3f16747e515af1e863686f46e64806fb40a58b56b2d2eb6ef997179633f31fa1cf082d6786d829e1bd80c637fedc2846d4867c24a6d6464e16215b636e0e31724179dd82380280e2ac98762b2a093686efa5299e4792235a3c66cf77ba76856f04176df51bb1b5511fc370edaad7006e7de537827da33cc6e8de15d708dedaea199e33266b98e62f10c69a1c84e8d006789c4e42199ce9580ae4696a878defa54624efdafb3bb697270227c5dcca43decc9e3e886c1141b215ac7680e94de110642bd5c3cec218b8bc02a37318dbb6991a33ab7b554e2582e92c9ea942b0f91b646accacee5841ce7d57bc90ad52720a42a8cacffeacaaf1bcadb1d4deb4c0b92cb5aaa3b4eab900893069763c2e4d6385d5f97e70a833170e2890109ea91a60a4cb4e2bc03f768fe7d09f9953b3eb3cbd65d177620267a145872481338fc0798ce0fb2302370f85d85b3abcc03c204220e5104559f92c2e463524c464a84926128ea5332b1636c03c7280e6d11f827e489b1b7228182efa9127a74779833df8605644fb3198d6020a56dc4a8abccd0a72ac099641ee77c1522942a28e3430a9832637969c03b6672007ba61e4f8c6c38d0117314b80af8e3f348edcacb053d4635f98aef66402499709586b264026d781b631185733d359db800b6ceb7b7c6ac04b58190dcca1c79c817186088129d77f706b751f0bdaf840a846df500b42087a74f01d7a19caa4953ff9fccd2f5ff9f5cda7dff9f4c1e72f7cfdc5ef8f7c8ec630adc695b624a619a0e483b69c6fcb596e39a85b763937fa6735d2fda3c3c76d6b676c352bcb99623923cb196a3d23ab195b336bab195b9dedc618901be83de245e909461f881cd4d6ff0676892c666dc9acad666c7196ad6665392b1667653da316676435fbc6e8b552cdfd8b6514469fe8ac79ad44c2ff21ecb9653dabd6b36431632bb3b6326bab33b63623cb19b19e29ebd97da31a9f42f699a7131bff3cf48ef798b93eef06368ee0f4b4c517514021687d4b17a847dfb9869ef0e11c66e9536b2521a15126e0013e284a689fc97c4e47bbcbface8aef8bce53bd586d1df28b33e0822659c63ebc43f47f2c9381ae6f56565d2d7d8b4339df8db21f22d8180220d9b5fb54994cb4af37fc27184c933c0d73e952b22a86d103209ecc3e05bf9261c671015d37e3308c07e82a2387e37820ae1a701e55bceaec3695b156a4068253d018ec16a05659a867f51f1070a0aa6db74810316ec1077d99b3f981f4caebff26f876f06073c1b2dc11c37deffedc2cf68ae73463f8aad8f7c079e34dec09f5c6dbd02be88db7414fd44fe546c7ccddcdcb1bfd1e79135a24181096f6665746c4c45768576e3827fe3b9647f6dff9d8372ead015c1200b948990db70725b670cc5d644f1520fcc71251567401216cb1f71e10abf60782a02093022945d07cf7e6c922b26ccc0521a16e644341aab43372d7911bed5977ac0691880872d3029ebaed9e88f9441f18036b71708b87142ca4e3c02d1656b0808e05b758a0090c6c6b77a9adb02b3d771773e8a971ed8d55174ad157c9f9661fc307af9bc7dd05bc4bf42ee8bbfc7759c00b042f115e787839f1a28a3f74c5479cd80cc36be21eed988aaac88a01165bbc580cc12be21dbf15530f604c7a97715e284c6e8c1a68543f4f46d2d941399b046793e26c529d4d0a9d9543d29ca939e049d69b0cf48bd22b9b1b7baa62155d96ddc65602dd3f8a19fa8179f2b6ec957ae24dd01bf50cb7b137d46337b084ea78ba6925e85c24418d5f3cc2cae46681a9b9a5f8f77460fa62d0a96a6fa7244bd4301ea833a551e04806841d00398628b77362b7b43e02f7dee85b8c2061921630d780c42d48f3007561cf77ccb47272ff555e5436c78c52a1810abbe5a4dfef57eaa420c620452c50a75efab36647b28976b4315101a8c7946bf3322ef626bd646f0792634cd1b428ad0ff1d8d4f64149f31c5b8250dcdf9e168036cf00dd11f7a52bef43d5ef3bd6c0056af2ebf165f8bdb8f8b95fe4d764cb4fc3a91503c299b97a6bbfea8272be50fd81ccc5e5a33beab82384323a807a3ea30fc356df69cfe456c16cd03219455e956881be8ae881921bf31988154c38f10f72caded39fcc20121c87ea1abd7124cba754c3ab6ec016025cc301191f151fb6eb6e53a0fa20118f40fe2cdfed66fa07ce5de3148f4474d4da11a6d17738456d0031cd576552fff8a109b3369ab287c37b00855eec3bf682810056e9e760dca57be7216561675cee2b2d6ec0027160ce6e436be691fddf9cde2209372f1a1131c09260ac37620ec6bb4cade59945131a013b4aa94ea180f68270687f1f28d4dc0a0ad85e080d9daf0385a65ba5800d606d31e9aff623fe208d06d979a629959cb7109fd2502e499abc7e8f14943f9eec914168f247fc90898d157744324b1f3cb860c660588d8cd4951b60c64f746a48ae4bb81313f099d1228b222db368ffcac792f951ed3a17bd701b7a877ae36de8097ae3ada037ea933bfd334044fa8ba175e1aa58db5cf9e84c1ca33fa28cecb2acd15df91ecf1e007ca419b3c34aa5703a27c4ceabb1c31bcc04e515585e0603db33e2906fd9e7d01facd193dba1f4169c05a3fd000826ee84ebc93d5a1643e9a6f23757a6835be0b2e777cd7015a27b892338a853520b710da74929e4b09de042f8d776c25461bee59c29853cdb58d30194c0916728bcaa35d38b88dc07bb4a961b5fbdd60286749576be0aae92f840ee093b0ca3ccc67343c8f0d4b38d54a5d110ae3f6637f88dad2f00210f209ff4d2c4925c5b54b96aabe3de528bb5419d5bab3aee0df5581b54b935abe3de50c5b645450db5237909346e020dcb8a58ccbadcc23e9259d9ba20485f439093846e76d53b4f11dcd478e216b3a320554878e418023673135c5a751808b4e6e05ff33cfd9f6a63688718252c8e1e2447c23d4e6315e6285506b16ae46961053e0bae90268125ce85aaa89e8f8fda6846ee9e6844f0b5eafb8137d9c6f4c4a6818795d758d1dd3c5e3fbf820d768b42f9d34188b16b7c090e21a9bac607d40d0bc7e7d66d036902627ad8d18d79edf94c8a482643460074d13140fc1e01b012877d66f9844637ce190e04b40b7422557f4555eb8e28b29e194debbba349fd45762429005db048082485f4386b35f9c29fe0b4c83c1f74998e5d8ab63f00379c8a5e1e318a54b0a268c3444b6a6d9d46dd7bea9005686508de71a3c9be8fe2f269e13c1a9611ac9c5de435f9c84ea2f296dd1ccff2ed0e7d03963b39eedb9d6b59f11a1bc4c1c681276f161cdb64f1de4e5e851f6f7ff718531e152d2fedb286716129fa56c0bb1f986cd06f005d53c34cbb9999f1140967461c26211982549a9c8c15a648c2b1bb9eccdfa6d5f0d5766dbbb5d9edecf666bb3bdab84d1c5c23284f9a915a8e499f102f1f48b686bfa35ae7f528d600ed6f2f429ef802fd646fb4cb1537d2e995d31346af1cbdecf4d8e89c8e9e6b6836004f061df88d7569d01f25f08706f74ad6cca3f7f44e8cc1a6be61b92b701af9005c8816fc2944c854d9e3c89ee293813914fc8f408e2d9b53c2a820078ecc480861851abc22dd2f327c51c46330c80885f3f3094b127c1fb93499d26311d822db2202d6b937573b710b4bf86d64f8c90006608d49dbd3c9bd9e1145d43ba348dd4354a9738d6a588bcce3fcec1e47f463311dc71e4dd13902f7cdcd243e9ccef8f016f2e160813d4a5a55b9b048d2d17fe1c4d3291c0e0efd934d78c7c63384a907ef47f711e6cd5add8e299048d8916e8f3f71af4132b56dc3eeac0a6efa99b057e79541af6de8ee7e44f7f9826ce6e547fe11c381648349acd079e097d5cdce495400aac632ba761bb37e6867be62d45ab60cd69365667afbf037db75ea2036c7280924517e6b459dffe2d260b7272d7165312ef3d09dba9d0c53d4d0a91af0ca5681a45a71dafa92d0831075a6b856bcc4f22df0fd89390287bef60c90004afc2f77c4d10b705c46cc54b02961fcd9e010d78eb64f354103e21c38c93c7969d7a262ccb6c952b12ddd8e814ea6940aaa0b738dd5d215e1d26d1eb1dbc32a5cb8cf62ccad5881dbb01f150f0ff57299f6318c8f59ddb57e58ca14a46442c507bf0b70bd1fffadf5e07be78fdd352c6fc0f70a241280b72280afa863deb8d5f2c95756f5e939dedc91a1f7abe9910cdfc30ad4fa659e2fb219818c0102bcd03a3fd4c15d3b78232c5adb5a3c92d254a3409014885702790c632ee7c4ef6a7d58ecbdd19918418025d72e00804952c6109e800c095fe6c9ce0dba111d35e5b51e7728aca4293c549df9c11376a81c8fab4cff22fc71f22abc2121a729d1b17a40b211f516bde41d80a5a89472273a0032511d2e9137e3511ba3fe0cb7324aba330ed118f266de6a63445c50330e49be061ef024030d7fbb172e5a83c06f8371af48b683c4ebd06a91790f61157202e2c9da590ccff3527b004292ff126185337187d8602bbc8db55c914a91ac3c9bdf093908294bb89f301d627f9f15cd9205995f8f1440a2be2cb7829b70b5732a3ec9cc5d33c8e0b415dbe8c11b3f4049e04d5177c341ab3bccf2006744f27e54731d16f00c678d559c1d75378b024eaac2d6b05ca78ceb287c9de04b2a7edb37409ac9f23b7723aec34878a43986296f344cc8bfc3d47fd13031e68549356f8d3d05a198a18845f92c8cc70f00e229f0e76d4cea72fe79c9247868ede01d2021635a124f282a5e3cf9e281cb708aa0f1fb054320f91370260995aa0186f681ea02e114235f4a38f5145a6cf323f70529ce61c521fef6d55f87020ea9c97d76a8719043cdfd618101c526473d6fce2364f8928db4a56f3039f2b98317157a73d0de27737f4bec63c67c275709308e2b352cc40c235e90d7f069e8f9959c97b2db87b757225f1fa56952eba766246a8d455f470f434fe3e4a28ad78297c690e4be8bb47e32505212e0c13315c9c0235cb0b9196e9f70e292e899f728dd570302027adc7dbaee0ff7d82632b737b787c7d24fecb9d1c2a3e7b247ffa9ff40cfd0d9c37a04fbf91b83fbc2f871ff7a8e088df716375946ed0df5f7433f0182391b28a4a776c9f50bec42b7942d36b743ae19c95b4831ac626fddc364709fdba8d2d2dfd1b886f599fc16e75efea42be5d0bd6f4d4b9b73b99292af583f33366180ad4fe5510ed3baf1f5523c08caf1090ec25497b33fe6f88316d05006af6e295363bfa5d21d2a73416b73a7506f995af90ef1b0b95218d7e0a29a577dccd32e2aff30be1ca54a6457634cd15bd0483fb8ffc5e265b6e7e234dfbaaa4395e08ded05c61add67937e075d0b5f155b3dea8db7624fd41bdc78adc2e8654f9bd3371f90f177f2d5b1c47dd74ed2d106001ffb07465d57d3cf72e65703c0baf0272eae278074e05fdc5d0e0475829f4ab9f0e92981f67752d73976cd96824d7851168ddaf988dc107a2002c96a9f843768d99e721dc304f6341bb1a04862a39a88cdb111cbb8c1a0b5389b38d483092e1802cbd168236a7c202a1e1691784402f0c1e58a08d5d3b81cdf10f324e8ba3307500ec90519b820d26d9652875772de4793b319e6f483190b1de00b3c89eb50f1e73ce43a5374fef24ef5749773bf51e513adb64bbd460519d1d076d425e340a70ddab125bd6d7d3cdb0f7a397a92525eb0e014da9c54ce9960acda1c6e4aca7344f3501add7d925c397c1ec02e418501bd6a0a6bea4fca58d06a68248b98892bc1fb2ad9480890e4269e611d8ad21fb757ebe9679c26df7d7698749bdd2af14e60b3eb0da28acaf947d977052bc4c274a7f29b33591b21e6fcbcb3aca827483d83b0698d58f39cf29d33768de812cb6cf318f6aa4aed4ef4d8677afc1893bbfbec17d91e21f307b30e67234f453abcb8e26d744e31a3afa86ba95d88dbddd49269f185bdfe47af9259a78c84af908636d989f79a9efa0a2b63a1609b8f840f909497d54a6e3a5bbb09929b188aab86a2140a1ace4a51209a40ab8f1b5ad6cf951a97cb27d187824cefd4089f5d6a87b53489154c2b4305b6d0b3078bb3b86977cc035ff009810ee38d71f9b0972ac13a9b1388ce75961cec1490b6ba4f2a9c3ce55ce794c8bb13e9491704cd856888f2636e7cb54fd7db2e1d27352b66f1d699bbd48e5281baa90c80270d6db5f4f13a353a4906782ddcccb39c0b1f57edce1b276ee2d55b5dfe1aef98f6507de8c04fc240148a5c2621e0ade209c27b1602bfffb88060e5877b56137820a6f5e6b503c3596816c7210d55441786af5ca2dd838927cb4c3206b3de0d7ad343c0c76ea61fb813207e5d7c6d5f0777192a2043974a30c339108de8bb2bef7638a409d27c02d64dc6d77d1085fed63ebe20067d1c3485c82102e55340938601379c433e05aae875cfc431dd4a98471be91dfe80e2d6aabcb5b5be568b2745672945257f78134d76e4d4ed0a14f4b71ae5c4be05dbf1fb98f0d2e2fef5859ca8129145711ab5853e76a3c1ce8a494061b91ab8991d15d464846c30bc36897c0da08be3aeb417dfae82f6ee64bd7345bcebec002fa6168bf14cb853128324998dd8dff878f943bdb6f4d903376ba6775ae239f5324e00d2c760068a92467efffe348652dc27b457a48536f7f28b817f05ffdcb4b89c03204d0a97ba2cf42086cacf8970e4c07aaf0fa529dfca5a6944acb594daa344d508b4d082322bf9161a127d5d9f33732db314d580ed8e7ed03a59ce9cad380d5fea341cca42b8e0148840c92b926fadb08d4ba358e7dc45454a0c06df3cc0c3b738409e84d5eee501639076d750a1d543a63d716a863ef24881d7d78e978561269cf8795b9bdb35dc95bf01716476206a643813f0e267496672aeb54d0b74df5dda2056ef5d6a8b831bb0913cbd726f52be6871665ad03655d151487e6a6b3756430b17198a94c8c84f1f5780054ca2a2e3643aa45c3be2d7906d84f453bfd8ad4807d7084f50bfa9c5a9245fe5c2d233c04d3dbfdc6d454c6181db84858f982014e3810d09f9046e47a6dfe80fd7f0e4fd7e2fcd8e4b4a21d8dbf37a51f232b46ec18d264f048e89e9eb3164e33880d35c65a6886f598a2d32dc384811c3d0ac4d28af0e6aec7686e88de8934d4244ca119985832e08a484d5d8942c1b7141d4736a6f7605414e60355b067f1c592fa884ce5878a692ef31b71dfb95171c79e45481ca6e26a8e34c760153ea87ba82213c1f0c799219af52b440926b96a98051c41fe9425cd8e46d2e02d1b89927503ef800e5034fa037fa04ca6340d4f3319b37b57c42a921f7748b18c5e8b0d5cf5e3b987504cb62c5e8e84b4012ccd6278fb957f2d9176745ee43bded38d0a1e06bc9c48c63ebd7abce4717a0eb6a3fa4409f0a05fbad5d952307b7e69ff13722d5d029a05ff686373a6220ec120076058cc486d746b2bd196f106788ded2bf3fd5645f1843bfffe6654ae3c89779d6a9d7d3b7e8ee4cfe41b330e9e1e17da49f3c2963344a56d1eede3b3a1779a346d66d075722ed3bb399d80ec5ab0fa1bbd442676318100aaf320504d95103afd24311570d5d678fd087ca4e290d9b7d775e40baa0fb47c79535618de101d2236d8fd6d4f0c9057fcba02135d1a8ea051dffe0bfe5e774fa1357c17ac4182d9bfd805bce340bd2494558279341a3e32c8de3f34034436cd91c5c7929906de83d8160eff12fadb45240676fcb1a0676e52e49e590faa60d4549c0902ade1ec8d57739721dbe71fbf8cde5b6e3947930ccffd6fd2776dcf4fb2b7eea808ab9e6fe440e3c86286a299ee45a13a019a6783a43ec6ca0821d06618dd92ece297880ac8f44fd18b3565f04ea2b6f5f8d86fd1a458edfb4aa7e538e7b5a257c7334c25490d397312d7658837eb3e3974391379742fdba3b26bfe3ce94c5d43d44d0249e4f0afbc07a7ccdffd82159c4b13ed91acf4b1198de5cb70e45918b1653923775e3b1de2d8a9fc1f2b2c0dfd2dde758e9cf136f0a705ed92396cd7a0f8ccc53402fe28c90ac22870525648e204d2de20dfabe330370030830a0674b7edabf32ec318485a255d7d9d63216a4d9531d2fcd89162bb294755fa69eee0a0f588f2ec823a55a85c0b145677663b0770305474d20a136641bee4c8eb0fcef7513075675ee01970c7a9aafdf0a7092976ad6e934171457d14b9ad530105fe26e9531699b32cb4487f8590089adea3283ac1f33bde494f3855e02b73267f65160fce8a0bfdf28399b66ad6bec6651782004ed5b70d73454b0433a4a69bdb90d10e7ba40c3797c8b269fd5b30bce3c9ef144ac5b72ab72201b06c9ee34bed4f96719199a55ed172b7c942cc7d889ce7f64d1b7a5fb38fcd5d8de3808e24c6e7671d675abe596675b7adbbf6dfb2cee7141d5ddcc5243765151008c698c4d69b268eccd95d3ec48ab9f37452f83e4aa636def10d096af51fb53d4a689fce04283237c7b6c26b9c09dce40f9d1ac5c96e1d6656a8885ce4df1354477405ca83103cba5c6277ac7052152c511b46bb02ee2e3df082f384d860412d13bfe6333185fb71b345bcaa3a2d70d6cf24afa3be31eb4e9f0cdfa5742db7795671d420a2d5f6c81db2a6ad3d2f853b4b847dc0d703e9db2f173b0d13b5c0af2d5fe95cc1723b062e8307d291bf1a873e1cc29a96b9c5c66e41fab77905977914bd711966e1e85fa63e7f7b58b92f6d0a576d16328cea88bda02494fb5b8ee39442c693e1f94390fcc8194404ba185602dfcff7aa487735af8e09c63760022c1b2bae2956001bf7adfce6c745a5056ac5b0e443f49258c6d08562d999aae164e3cbe3b657e8092840a59eaa8cf2aa2f546ddea2af3466761c28b799dba6f7b776e2db79f3d0898ef8bc1e5e708c00bf733e71c7417d5af1f7581a790b74150905a00e40345fe5041fc035e73a13a3e97c6f9b868dd5dbdf1bb96be0103352638e06716a91687d0a3ea773d8ef18f433afec08027a4a219784e245279b14c0069dda89b6f5f551939e90b50567653e493a7846ed7df3b1282b45a6ef35654b15fa7956878b290ff71779628038f22fff4d5aa7541458528205271e867e217c1b1a31bc39ef40b1fd565cf4afc76d2244925f66cf77bf814d31a73c7380e1895a844d64acfdeebe0840bcfa86b89dbc04bdf1a209ac970538855d1ecb012a058c5e855cc337b636c93286feee689cbc91a0497c8f4bff408bf8386505f4f8509c667050ed805b700c0eebb73a3f5ca10100c17b3cdfdcc164c6838f1bf0468954683a4cb69f54f67bc0c9ee859627d697a837dd9f9a0861b729054c717ede7e9e7a88be838a64d2c853486147b5915869564a9a0aef094695772f058d06382a58c7b04434e669cace186131b9788f9d77a6b262fecdf0e4437ce44a6f1a879feea57ba1d06d6fa05af4293f455cb7f5d66cdf8a21eca3c47dcdf167e7e41d2672c889eaf3e261bae4db4e1c3fa8a64ec97d15e4b13757204419d47442f1c0d70f33aa1d116177f3b8708512295afa5e48b8fde19592aeef9adb886e061a4d7ea57d0014cad5c6f46ec1a07869b85059f0bc96c8ae558374198ccc30e52518242ddbbf07d94ed5bb8b23aedfcc8d548e4331c6c247ae4a18521fecfbdfd55016f4aac554dc39f2e9b19eea4ce600e198444a14dcdedad3c91d72d15a2121051ea88483e3f64522773a82c6462445cd2685774cc6c2a1061bbc12b1015f480e5b33d7e13ddd6e8a5d23a5b6f403f6f05d4da2ce4865db4c2413d2dc2f6df1e8782695a267a1243e04e668d460bcfb9c720c680bfd5208dbf47631d3ed262279acbc2c76f3631ac9861ec5a906990ee386fe29465053e1acba8bbd4e15bc7a2985ccac3bc906c29c4e2721f06c1bee8d3efac6b17e0b8b71223a65c908adb5e8f5bf9318fb9a04327826e767f93e4bb1c6d09dcead1c44b9aaddde1dfdde0d111796fcb808384a9819660fb724aa3a089f2963a52b6e59e1538d2d818d12ac01b651039a91510b0ab036dcc1571e8fbb3c1a410a41f40b6d736efa69cd7894ce640478de6f62fa32e86aa20cc956a999bb21a93a23605c3ad67ac82943ea87b2862e921fad3bde85c9eed541256696963acd05f42e2645b368595e67d2671a13c3cd5079b5cd3ecf406bb1f0aa3e1b4184be06d665e146669564b586a02c83ded478e44ac0064b804377cf590e83aa118d44f25cda3a2273b35a33ae0c452d4ddc071c496c225abb2949348c91af1b947299fe4128cf2923343a3845b89d862a1cbfda3c5ba2cf2b9e4b1d1672d2389ae224b3c57766486cd8d5decbcf6f53f8b0798e4e0ea21009e3b33b4dca11c1ec39818aab0e37cef5501f4da316a07e00c4d1a1775938d216c782065519105aba05f64e78637a40734da7b4fda86cd5ed2bb15b86205ef6a8d9495f06e252aad9a2588d34b7d8366fa223038a1b16120383ae66549bb6addfeb062faf5b070891a17be457cee4550aa8c769206e5236557109c65d8b6eb46c392ce424b53184140993a2db9497f143de3469c39a7cb8ce4be09e1369128121b9a6e1d94d25b0c6d238ea8490e72d4e2899f2526e2031ebea73800fdca0dbd8f9bf8a5e659e560e2c3c990e51ab209208b045955c0f1748047cb044416c2afe3a70f983a2c10ef6571447e13adee40a82c340c5e66b842ca8e39e41b20ca203e89c1091303e503ee067e388466146935b735218852f630beb870cdda2a35b86ca7cc81b88182eff008bcb3874727b3cc46545a3f6275d8bd8bb88fc27e8c848f1b379df18037240ac38361cc85561661abe8a138079a32a2039e2e603fe81c12fcf85924f61e826bb81e937f5b5f7a54d4348f4a8b2af067abd1b4e1f2ecbe13823b796c4212295b60507ff863634515acbae04e10cddd9fe0235a26856ad3b6c64ef1ea1ee194d49ee1c37b2b09459ca60da303e692220fd364b8b4375f331bf1585336d79e25919b38119d8a4293086184b4d25bf1423b223c74bff2dd0ae81e57a0780c909345594bc5807699efe6f854427bcc509743f4b00f40717457e438b0ccbac3093ebbc503368e5e7d134d163f20fb4ec0ed0e7391bb5b0f50ceafcb4d04c4b22a193143133a900ef8eaa99a0dac7dc43aec4700933b9296f4b816573d08f9cc35088e6b6890e534d85f710a323c66224e3498ea8b82b865d0d1cda0857bac5f0a2a4bfcfaefc48c2e8f86d6a56157069299f3a5e2f98602e0267a2ff363e7fcb746ed500e350a9aa95217ecf579cc4fc8100dcda343668f2799ecabca5db9338d60c2184082e3bc55538f1fda0bc0d0039a3c27233ae681766e042ce0aeb3c3852da9ba0a30981d38053921e60bcb6e9140dbf02f10f506cd6a18f10bbf67c7c5a23a02951a88a8e62aee01768ae3aecf5d9bf2f66057822bfd3aa7ee65ae681e734d18bb9f7c08500be4df8b5808ca60111e676d352a28741b1117e7d1777a40c2b9184170644ed465af40f127ced809866e0a8974a260744d45d25fe2ae72f64354caef4ad6f8d176a4f511fa30b03099a9ce1f4d9cd8ce69dfc928add2a92fd0e854ba91f6ff7f6f573eb70bee6ead546a35bdd86b0dc79038d7e310f333a58e59a1c7b1903a7dd39749ea3849cb2caee368da081cbc8e0dfcd377dc680730018af2d75c3658afe632c3fd15c7d9f56e1a199e674431c5221dcc93c95914e6f7019ac98a7b57f02f66bc7fcc01a3897d27be901fca7268974539c22e0b7268f1ac420b4ae8a7e5a1d364602839afaa0e4abd58e135138694b803e420cfc70cf968dd6920375c3ab30cb9bb6d074cda631a283c4faed39075c6433da97a248b0c2683295f941176061b929ca97e039f154ec8ea9e99877596fd6a5399fe91ac7f98a0fda9d9b0ea21ccc0bfa5083275ba0d4530b63ba2edd9757430b16e44299938795530c53f2df333cc75460a8d6788aeb21796d2d4e1a2e886eacba88a87054bf8adf01a2c607fff19b30bdd82b51fc040b78406fb953679472a1a82d1fbab6816f93c19fab7eaabcc600d6983d25dc0f1aec9a01ab073fc0d32f1e12ce1de83b6219660eba2daa22dddead924ff0380647c04868fcec76bd0712d967c5e0fdf36600344bd0dbc0a30ec17f0eac9586818a7cfbbf546dc6affb7312936f7091401abbb8604868dbe8007309a7eebe27b90468dcc62438028b77b030d2f2267d9af7da1c9f10e7d3bbf832deaafa616c1568c2e7c2ed38aa6d886a0699247752d691ee6563719c9c9145427df1f29a8d7f3bf2250c12287f9c05cedf83d3e9efcc05c1b3e2e43fb1e92096350ba47581a31d8b5073c1ddc21f6f0b14f43051461b744054f11890607f48a043890bf2df2de3d388f374e36134eef87f7eba1da7eb0548e3aec87242002a09a4698171a201a576fb0188c0c919e17588714bb000574ee3c5156da6067f3e0043dd8cda007805e9f64e08b543b8dccdfff0615d639268cfc33b9ff7fdd65e902119694622d0e7efee20cb5f1004c5fc671cfea1b2cae8d0ca2058f521a00f6c06f1f08c768830b8c561520c660a213aa086dfe44eb545459e66083a776974b80bf266e9cbb76dc38bd6ec89ceaba2da3aac45dbb3f01370fd74f42e60938f3b8788dfe1cc9f286a1e7321831729030acb1552001287d6d8da3f54dc268ebf768a3fda48e0b3419239e72aadab22bcfa2d62314ed064e28554f146bdf4d4357dd06351e7296ca5a9e289a2e2456add644423d2a999b263a49749119bd69d014d3a5c953fc377c10cef533ba4b7c3bd60793395973a34473cbeb36e161ea91b802b9832451b23a181f73b816ecc491fa084dc0680d19dd48eb94ac8d87f6fda97a001905c3e144ca2fae9ccd88d6e2185599633dbf1883e92fd2b730abcab6dddfc2c332b3ed4cefa0bdb685f51ad4ebb4f05e827ab98fc7c15f6fe2667f88c050a61a7d8a22af2aea0eee236ab50647c599c450f01062ee0d3ed58d3aee191c16185ae91700b7100968170815ee6c806e5efaed29d6231efb5632a37347bfd911dd56234d8ea8bf3d51e47fcc60d88230ce1f1147a347799cfdc0676905fad2f6ef0d0fb38231cd33ba71685733901b8676378bde7828973047495db2fd9f2d14666c83d243cc1f90430c9c500501ab0104be3a93f930d6a965951e9a1dd0e515313f40126cbf32042ca6fe44f5370cd7e37f7df4c52e0c93ac5148e69eb62fe759528c9addfcc30a624e8c4c9ac143e79b7815644e7fd51c480b6f94fb19b8be67e031ceee69eca74bc31f7c2d7f8819017011474d668926fe41e6030b08756fee7202931911ba31b9d5813128a14fa036bab39afb1313707e85b5af451c78985d871c48278dd3492872b46022cae26a5637a2e80586549610316ddef98fd6d4bcb3752941d98c844dbcd15f75bb25404d9382d299e38b4f49297d2a8bd8b9e5bf09390bf4a71a8afcd390c55c894ce546049e603e36b47a1fe28fbe033fcd044b2473217d5d9aa2294d0fea0192ac6fa30d17f437cce5deac5d0586804ddc59b2d7dc69008b4828264835425f1185161a8ff013d78d923803119962e989a43947df87c049df425946dc355131075d8609952553d313653a13d8b988a825609330e09b2861a64d97e51d9560045406208a3d51bd0bfab2dfe3660591c63dc8c195569357e7e39286a92cb24a9ab36a303fdb9bf402d27d8c114b2ca09e73c5430e0e4f5252d6bb2044c693275f25355d4facc3a8a07089365f61d6eeb0266faca3788356fb379fb1bec6bbac41dd9101daf782b54d825a1517334c4d8ebefba2670ea59ac0bc017f4a4412c4e481fb6d9e62bc28faefc3cbe575f5b5bb6e1f1f67a78ba94dd56d398981f101a5855387b9535329ee1baee7a9f6d413317673b3e42c925c36dce24eed12d53ba1bc14e2f32beb0a716de11beff8bdf0c562581f0fed78521a9524df8e5e5446678c025750bcea8ac2a35ef2130e3e63894b8ee8f964775b8d25e82ac79e8d49b47bc6eb91f5657ee69f63b84d644dc2567e47e5c818359dd250837299244b0fb8d4901936d76500787623ddd2a04320666c2076331a7f0d132f8160370a03641565e6889c34f9052d2c84e3b497d2e577c4fd15542cf758903dbac7825eec1e0501e9bb8be60ae6d185cb5cc01bda8ae6a33b804c878677f36b0236017a1ade22b3b9acee34705296f571568971f24d958e707ccf2347998de92aa95e066b68a7c698cae288e320094788b557c36d8b834243a58365ce86084b041796e8bf1c976eda3f5fec15b3d29499eee8dfa1752293aaaa0ca8f17263cf594c3fdeb92a54bf61b1fdec07828aaaeb02dea021b6d56785125fd905918faa296f24ddfc2adcceee8e48d2036e309d130df8c1b9ac7a27e4dcfe4c6f0985a35ea0fceede13af8abf6ee879238d6ed94b435b6e2f9f86a838d53f5ae1494a952d0f03d8d6784d5c85be4eda29990847eb897fb75c6fcea1662f2fe53021a068f4e07a449d4d7674835fb3d3d70ce867a2cbd78c5bb4689a155b2f5a742b3ef460b4e8712c76857f574ac5672495de460c6e5378d016a09b361732d8236cef8482c735efac0266d0b65269485b8a088bfc5dc51b411c008f7c8d205f934bd2aebb64887c1c7fcf30c6f7a8605f7cac63ee05568a680bb4a16954562a581edeb8f864b9b87f6aedbf635821f318e1e3db208e929d9e7daaa19d178a4d53533cb96c92cfa486ed87e81f81aa6737949117400458fcdb789ffeefc3a4c8bd53953024e524a404f457efd5292786152d20b2cf581580c38c0c645b434a364316ae8914f7299696a4f8b2159205236eb8a40d465ad4597c756d0cd5b7d51c29b4c287afdf4f978bf9b466fcca13a816e8c8f2404d3c24df4077b49e20b9487d980bcb6a09709e48ebcb3d046cc96ab3633cd9afca7b6f1157100d1ed7438ee5e7d6faaa49b69f41669c50396dda9707118ecce0e43468eae137ad0560b581eed7b6e2ffe438bf046f5606a06c0d613eb9c555208803e8d95f51ab905d1b3215e15efe84fbb37983387a98ed1c1382287e36fa132a453e1fc396e2db2fa17eb72acd3a9e106b1ecd00378594cb458b9747b1bf08aad173bb32ebd25a8190f13b4da404c00bb48205773b9fd91198e333d3bf08047d10163452a65a031333f605e85a82f6ac65df9c0b0d1425444e892ad4bd4063827d187b9539c576d8170550b6d489cb03ea5283d27cc3c6b3d740ef1a085b83527293dd8c17e46765850589f7a9dbe2cc3de415b4b0f8598bd280ca7d3d15eb33e5466fefd7f4d1a9a44d9b00d19becb0f9a2f230f18229ce5736067accb484f131a9a93c55e50eaa2313241c59861f8814dc8788881d002e20308f8c4704eccb1515ba82e9f5010f1f02d1578b0001830b2202f7af11f09c1ff10f08ba4240763efa924190cb67f240404adc557d7c509cb62c80390b1839a05854ad25a374ed81cf1b1652a57fd2f1e0c353d59fc4f8949cda0af9ff0a05176c495af1e6d4a2696e62a927ac41ab6d443678459a6893c3ece6e3ac403b2c60da1d3894f82e2e1d70b9ff2ef68bb1651375144169d1d24ecbbceddcf898c511b16b8140450bf81279567dbcf683a4393dd30a890d0a2eac3dec57a200a6f14002024088a4df03180e341b8a6e6aadbee8557e885a7d94b9fc63ca9ccbfc66eb199508d8b1faec49d7ea7b041f1e35c1e90fadfa67420b082bf718f59970ca94bcc298309eca64d0fa706a3b2f3206083dc2b3ceed5fc20a6e67d07fed538887a8a1b30108305a2bd29985114410a5ea01462fb7422476663031a4124185470fa2f4484df4c4b555106eddb6d229aff949e59781937faaddcbbe7592b140ca42ab11d88912fb664829646bb62909a9a922762160ed5cedd679024dddd6bf5e246146878ae2fa8da0de45f6f847be57d1eb1f814eb48781010c1eea404f8ad52ef6784087458a642e988e010d8419135bb8b9528ac79bcc8cd6f81967f8dfc399259c9cab7349e7920ada5595c5b82000f7d7599e93231559416d6f2951c6840c5a4d5bce227c142c33a91ba413cab000082b23e7b3c192d1f50c98beefff3816bf58c8dd0755cd28642c0ffece6ba5809a895451e2b1d1c8681a4f4adeeab79885f95c01260add501233a16a9893b70836a518092c0e019c4e515abca3821906d45cb5fa760202c23aad08d9117495d96dba502f39c5a9361c43f65a21b4dc81c2d2523f0dab86979eab57416bdf8cc9e131b91cb8b6557e10a29cb5f3e0274097d3827e0e8ba02196876b64ae553627cb81e82a526780a9e28a37f9f6460087022bd281b629f8e88f315b224a05fbe922a7404b0f8ba5d078aa2007a930ff408b059f6df63d15d8094c4c68ee89d9ae13fd28bdb8f2c453701ca23073eda08bd31fcde3903b02bd4260c2ee6effd0adcf61965b5c2b31f46668bd58fdb8d70a3eb3dabce0b0d71e77e5bc1f2c7dbd7e8b754710748a9c759a70156b185fd7af91fbecd744e3ff8163b23d6cf7ecf2fab89cdd202715c81e3816ecbb247bd2933bf8c6fcb7fc23ba19cba06b487ff48cdfc1eb58d09c8a845c43d17525dcbb8517b2de7ac585f74e2404c06202a591c3d8a6d15c6c3b05a84b36013a562221ef3b668a3a861c3bed98f3a0581abb73cfebeaa764f7e8638a85976a935dfb4fd3bef216eb5d06ca95fe2956da4e1f8a2b6eadff140f77e812825acb70e655f240e6c16a8119dc92df3469f12ba9fc9bc8b3a7160d209c0041cea85de8a06700cdcd031a389150aa9d8c8bf06dc11484b9b7774d20e7aeee1e5d7a161a3405b8068ff2a6c0b10a398a36a38b1f11088b281fc7d4e40df9b2234820268ba77f089b9af95993befc0b1399e761f70ce8035679a8b89428ce69f8421da45fb9545debc1d31699af8bb6038ae4ad2b23290cbdb12e3739af8c46d8d936efb19f01a06f11c6ccd241f7fba01922bdd8a70c3855e229649f22db9f82bd26f089b1901406afce1858206dea11265d78c6cb4db49aaa6b97d5df3001068e61359837224071badbaf8697ac273abc6585728ec8590833b771c28096d99ecbcca78cae26433c2ccc869363a38614389dadc6193b54fae5bf2eb160a6fea4b49198ffd7d6ff136a0b31efd1e663da1ed534e6b744266ad56f8e90e8d8aad8e63fdaf62f565b25c0d121418e4c166914121464511838f2f1d16fdaa770a74701deba3be0a5534441bc52db0ef2b47ffb61a23cf1933ba9456312b72bd0552a8c1b4396233962bc57f422202a175b515a591d647f9b16d32e543ebae33d40d5c2ed269ed950c713fa23a7e1e92ea6870590a67f9cfdf779fe0cba0004f37d3c6f02f6cd1a64a0adccf80b324cdd358fcee310000ff40f52c02509676e8589e88b43589bcd8c4f94983f1cae803306509940e757823be800987fff032ecfc7ae18d1cc180ee0f15e010956228090f6a046348084352bc420d67ccdefcb5b89fa92ab6eafdde9c2a0b7fc4e03baf472a05d30d0514e07f4bcf45964ac440730fcb0af8e88d090920c0cbd3f8f34d34d91e32c933d5c20cd2007898e9a18a8520158e6a8612b73d4a83017e46056a995c05deaa288a4a9f2556767ba57c0df107c43ff437edb7befbdb794524a2903010a6e0a030ab8d43bdc4e864b9e12973b3ba3343bef89d15f5c8e695c68a07289cb9c25354921971d68666884e41297198d0c2fb9c465aae90c945ce2523c7314954b5cea285c14132ebd28a4a8a3fdd465082733de96392fe28cb9c4a518c594f38319991919b9c4e566c24619d55310a0cb29d30b619e32bd60e66d0974c3ff5edc197389cb9b32327451b9c4258e2e894d2e7189c97821c3b4c291919191914b5c06e5129739b9c425ce18a83136a0ef32789b4b5c7a63666374a0b8b1440d2a97b8fca072504db9c4a507458342c273503ac4b0f9e88a5ce2f283d2924b5c663148627289cb4f8c4bd76bf0f6c514632e71e9352d891c3c5142881236fe9a27e41297fa2f5418a6bfe609d6830e28cf8e4a87b2b4c625f8b7a44c3158795bde7f91eb9e894359c4d9ee18c5920aa50b6c2725850d7aecfe25ed8e1f85d2eef885ed4191ddbf4d731266f627dcd2c6cf101541a0c06103d805cc19284006edbce67752fed9c4234362802aa766603f6c6785e1849419508f0ba70a266808fbb6142083765b5ece3e1529ae37292dbb7f1e10f911d223e6c945e98b4650e820ca76ff36f0b1fbb775904576c738555020ed54969b1e22f66dd12c6372303a85603829df1501f667d8d6da17d57a3d5a50e5d4885ea2dc80c1fd7c3902078c0be2979b2e3a6c2f32a4a88449d0e8f9c2464cc229a70b101b0c0f631239f8e48c18fa3226e5c5859b2464588129c2e3e50b4e97a8118c0f32b7c3061363ec822505668d2a09312b1a373bc42f3f6e90fc7871dd74a161fc321bf272658391d2939b72f3854aca0b1a1c20632e09f9d4e5e37223e37ee03c01b7e4bea7241e172a3b381fde162b1f9721395bb2645c0e7751e1dbc2e4717932e26e804f36482e6b34ee869ca79d8873e2660b927eea11eabc2d4cc0a723780beeebe20263a4b8e8308af19e8610bb14c13815be272674971b3c41bc27a7100785c365c7cdd30f200e4ad09634395cbcf0e0709f65b19f72aa33bbb4192e35d232adc66e4cc7bed835e2e437ab1f4da4519ca6e9992b55679bb455b0d2ae2bdb2e2ebbb4cd5edd67e4e98a489bc5cdc4d890d6518f466db7e5e128a7a97aefbdd75a5e6f4b9ffcde3598da4b53741d7bcb454a2bd3d32ce5e9496999a794521bbd750ac4d314ef0c787a00109e6e8d0225d2599e327b96e171bce59fa97b50bdc27f5fd7479553cababc8298d75014390995a8f3f0bcc6972ccb9edc4fbd2e91e5a3ec12594ed23e6eb935c2d9fc24bf6ecbf22cbb291e9ef135bede92a43db3ecb672ed12d12c8b8ccb4f288bdc85b22ccf3599e596d65a2bd5a9221b03f9bce11dec389d282cf359ca571c4ad485b2a034e32c0b90b22a2f77ad5da26f934057e8b2169970d6476d0ea255007e91da5aebad506f293982997dada5e35b7cf93736e19a6d758d58b70e6d51202456083112287cf78834a2152994e8e4883037858802a5814f9d1c81d3ba9b1c436ac861e48588da19fb020d780acc0610c2305974288ab095be032c0b6b21400173dce068637e39106de14b65f7167fbfffdef219084077d64000ba13739b05f7d44916243f80a74e8cd8fdeab62050497b01cb827e51122c0910302c2122eabc97e8ded8b6da3b43ac3f81f07b0bfea32bbe8b2fbe17df8a2fc5f85edc41aad3e05f88b7ac35c8c6c2ce6c67b693b433db99edcc7666620fdbaa623f3fe8e874743a3b1d9d8e4e47a7a3bbe60e8e835d8d0977bbe16e6bbc5fd247892abffd0379df3cb0cfd499e9cc7466586756c6729292feeac831ca31ca81e5c464b3bf2fe0e47072383a9c5d8e2be7b581d8d6b661dfe8410e37b79bdb0deee67673bbb9dddc6e6e37e0b8a56c8b6bbf0196942c254bcd52497f798a268e4365c81c992375648ecc91393247e6c8dcdf2fe48d347a0f9046dc881b73236ec48db81e743adddf19449928fbbebd39cd5da2be837b9ed725caddd6cee6c9f1e49ec7f5a2b983eb80062c080aecb6443ef2cb796877c3c25db8135de12edc85bb70272e85b7f016e2c25b78d319879e088d6aa1916afc7902e2401c980375e0ce15be84a46cab0574fde522cf486ad916f6b78967fb6c7fa578469e9107f38c3c23cfc833f28c3ca3bf33744dd77e465149bff44b1be9977ee9977ee9d75f199816348a4cb22bbbf22bbbb22bbbb22bbbb2eb6f0e1aad46a3d168349ad0282661fc5a6dbe62f122567d0d8de58f6bb48bb2edf7a368c3b6dcbeea6db2adb8c1f80365db9b655b0ad09b6bdf91ab58414529fed6dac158044227c08e8eaeb89c07561180cd9d32f483cbaf99bfbcf3f8f89ddcefe03cb027e5e4efe0e88acb45b01cc476dfc1fde54134cba2f324fde59727f797f324ddd677450fc61e36dbf2afe220db063191a6e8de2ccaae1663fc62b581115b7fe74f85d8bddef2edede5d5cc36cbb274853dbd99b521d214bda752dee3ea71f5b87a5c3dae1e97d08f6a6fcd41777453aad18e8ee47131e5aa996aa69aa966aa996a16d402ce2a5c01747060de6e1d1c8cac099093cbc99936686f00341a8d46f33eca7360daf3726039b01c580e4c08870fb47d8daec071010c3f78caf7abcd07df776dbff66bbff66bbff66bbfdeeef450712314ab0b02789ada4da936523b9c2371cb8af2d42d8b157dcbb75b07584632004f790a9682a56029580a9682a56029185f814c6d01c053927623694148a91bca49922449d2882876b3d3103d1d63baf147cbc6a1c212a988b7928693a302e3c1532ed26e4b9bdb76892c57a52143abd1def25dbb44a6971c1d5511074fb968a4bd0f0445a34f34f244232d1a65d168e3a0e23ac5531eea6e4b67ed79a14e87ba1cea62ecf06c10ca421987c2d3b34383e5a3c2a97094a88bf99483bbcd64776afc4e0c55132a332a315fd3e769d9dacb5b9ea1c05d14b8db35cb02b2b26b3db58a2b9f2dfc29089ba205840d19f9fc90197afacda8e466e4133f405484bc1c152fb70115a020144fb927f3649ecc9379320fc84b108a208dd338144294eb988ee9988ee9988ee9988e29090d4da1f936a5966f6f7764acb80c71ca332cc3322cc3322cc3322cc394780904e5f88643a34d1a3260b688e22dbc81b7ef76bbb5c037a12e2515ca71ecb6729b3464c0f47cdf352c2106710cc7700cc7461fe6066de895edaeccf461831460a8ade16c6d9b490186ab9e665385bb4496b3b85db258f1f46a6c81830eace8d8a66c2c1d783062c979caa9bce5b69d23cb0a82e1010be7292f81ced50fc8312ca2dda64511082c225eae7ca6d056e6b609331eef5bccc7fbee02f2beb37cc8fbb6127e1fb8f7b75a79bc88555444570841f4dd56268b8860837ea858d5706a6f7907776d53a1add56d695385cbbcef2d6f39a52dcdfbc6a2bf6caa70ab24bbf2aec2bde522000040c4f2a0030e526c60967c4808850a152a54a850a142058f0a153b2a543a2a54842ac04f850a15d695524152ccef357b66d736608cc5fd84fc987b7f79380a0d217d5423cc8b5ae0efa3975a16ddbc61d71b1c7114499d9b7d83b3c3b379cdb1383b4745f55faee2a12258ccd3a3716eecca7346153b74469c1f5535766004de0981c7477fbe3d2b9f9ffb37abdb6dc5745b35e55649b9152d577bcb57aedbf27ee4c8520546114a2c9d60beaf5e37a617647cf14125ccd95746497c5fc102f084114c5f8c680922e9ecabd897ef2b595fcd5648e08f1e35e00f36df7b763228591498d47303937a706052ee2def89dd167d42040e1c526e38bc7cf1f2bdc76b33c416031730325e67efe9f2bd27294997254ffc8014040d67efa171f9de53eb3d36fdbda7e953d3d3e353d373c4e27b0f0c74f5d870e9008d78d880463d32de721eda6dd12e2d3c79e2098c935dece93b4f4d8905178a8861c2c30c67e7b1e1bef3dc569025178bc185232f0471761edc96ef3cb9cea3e38972e25172e259e241c283f49d2709c703c3f1c478646ff90eeeb6fee50447132750392c7122f17d27c7040828b5d80f2ba4a0e4ec3bba23beefec844cc9b1c49822602073761e9796ef3cafce63c4a3a3c98e53931d2d3b4c3b4d6ffbceedab59a2cafbced257db81f296efb86eeb0061665052228d10251bb27cdf118ae164c398282b3d9c39fb8e11df777050a2c86d08162e5f9ccebe1373fabe2373b267df41faa1e6071ba8a8b75db593a96e32d52753a962b7f5828853ce09236a924459e1f65d15654b122f37a2986101c9d955332cdf554906c881863132dad0c8b2c2d955b422beab6a5d6513bfab9a3e18aa1e1f0cd591cac65b1ddf55b0efa5f3bd54df4ba543bb2d13072d548e581265061bae7cd7b951732591516107133d9c5da7e9bb8e935ad40cb819b9990a67d7c159f9ae93eb3a3a9da82a3a4a5596beeb24dd90dc90723ab09c4eccf21cdc6dbd3882658b10516a80899a7112f13d870332a526970b3e5ca1e1ec3955bee7ec2c00cbf2041133440d5172761ddb779dd75bde758cf6771d1d4a729c94e468c9616afa9e93748992726849b5172c47f696f79c590e9277438d77830d54d4db8eb3f36038375c4e96547bcb3b8e0da7a90c4e8f323847383670747cc781ed763b1cd7eef5966fda6d51a71a538eb020f1c30c5c4e2adf776d09269488c19059516ae2ecdb36c4f77d6381181c465cc0d2a3093167df3821beef5cdfba1d55652b55d94b1b09d2dbbe936e97287693e576bbeff0fbd64a375ae9e686a9e96dbfa1ddd034cd755b2f84d05a80e1c40a3bbc50e364fa7ef382ddc866486f79bf4922bfdf2ce91e2935ba478a4d0a2a15f5b6a776b1d42d868be5def254ecb65c689105519271048b12e794ef29998b261c3a7c7002a3870e674fcd82f89e4acac095a520617e08a384cdd9533429df53b59eb2dd7c4f358549f50873f4b6a760ba940d5d4a8776edb42be5d2aed75b4ed26e2b03438c722884c1e2c3e9ac7d276b3bcecc8ca6b499bdb0c4d94920be934dc060ab22040c57d2bc70761217e53b99eba48e8c5222959496be93493412098d44b291301b19b391630c371441e4c66397e6851fbe8f2c8658c271e4e5864ecbd94728df470ac68a13311f5cb1265dce4e3ef94ebe3a69d4491d5985d129aba0e56d1f6fd96864326a8a8d49b131c7c6d1755b3a41c06a54e19105470d27dfc710c49c62500304983340ce3e36f93ec2722c0183ab0c0cd90e671f63b4efa3ac8fb311298ca8268cc8468412a37cbe8b3b9d78d389389d28d2f094e589325dca0cb1840fdf4522ba15686c96d8f868727671d6c37731a90422b4293a583862063467176962ad8bb6a0ef629314b18714f148b421eaf82ec26ce1ce65135f6f7948bbad19087123470e331c35118687ef610d474a8c21499270b11275f6d0b6f43dbc7126197078a1c6cdcc8db387b830d7435d1865245432122e854842a4ef61d22c84cdc21888bbad174b5a58a384d6c4030e1dce1dbe83b9118c0fa3305b864839727650c7e43bb803c08c068ea82b4f517e9c3d74297d0f5f3d34c2f91eeac02a804e5805500bc80436bdede00d1b81494620cd755b3f43c990a43040880192f41d4ce1346627a5851f51709c1dd4e13b0803818c1a1bc6cc2084114dce0ee6f01d04412427354e6cdef66f8783c245e9be9b0ea7fb726ff917bb2d0a041246ec30723283cdd392ef1f16345556c8c2a346aec9d9bf19d2f72fa9842a29242942e3468685b37f341cbe7fb5fed9bea66be4eb718d7c47e4f70f36fb6ccc3e1db41ded73d168b7f582c98ca62c2ec418b3a371c3778f0440741082cd111a477c39bba7e4bb4744ed082f29c89e78a2cad9bdd977af7b5e941a4f498db7347ef792ae0b890be92dbf445eec1abdce01404853921966a0e1717476adb3e1bbde79afb7bc7b4639df3d1d52b493142d6ff5cda6996cba09a793709a8673dd161d0326491351d2c4d0440f49beebd70260455c01220683471067d7465fc3773d440f67c2c43852c30d4a9c5dc7907cd7b2ae67de778dd4434d0f366ff3cec632948de5283bcb375ceeed8b1b708822c40a3d7c90d274d2302605588da81d5c64ce9e8f7ccf2c307214a60928356650397b36f23dd77ab6e5260b23f7b0308e7ab69175bc7d65d7078c78b2c68923686028917dc7092042081a475a620822cdd97191ef388514147ce8d0c40c5b82383b26f21de774511def309b2a58a9cad25b9c74bb44486e4835876131fb020720416829824c0c060d43bedfdc073b578ec78e898e2767bf3a21dfefce65a486518d314b6db49c1dbb827cc7af8e8df277acc3c87532a2e56d9d31cd9adef24b73dd96016ab627646c2411720390eff7a50026ae300112a3855c0c67bf463fbe5fd88b241c5e4e6c5051220a9bb3dfd8c7be5fd90cc94abf4b6f6fa8f91b6ca0deeaecaec22c51b55edebec8e1048b528ed998570ce70cdf2d14354bb0ec589022e489b3dbd9cbf0dd26c1008976238249861c3bce6e6931746bf3ce6e9b4240599577db833e1dbde51656026be36d4ec7cebe6c0561e941a04a6f55e0c487991cb21a3cac398fbed7da015c388ad57648521b72f66a83e17bbd3d093b54d9c530e508c7ecec15e7e37bcdf5aa8b7a8bd402c9f75af906288bfabdea78d1ddf7f84e732f963c31b5a981089026949cf6dbd6720dd010e85b27579a82f8383a63e3b4b0b7695e5470c81802852688d3f278b143131258d0a4a1b1c6c66939cbb2b03b58e8b75c4886dd49da3fe96108156b15b4e331ff3c72844cb2b0836cec3eeabef2a01abb93aeaf7cbce5f1f6841336b386dd47a11b761f8594ec2ed6d85dbc7de52a58d85d4cfaca717cec2ebebe729c1ebb8bb0afbc8322760f735f399fb17b98f4957319bb87b5af3c0887ddc3d7571e74c3ee21ec2b278a6177f0f69513c17607735ff98f11bb83b2affcc7697730e92b2f95ec0eba9e76ff90ecfec9be7212c6eedfeb2be7b4dd3deec3ee9ed2ee5ed257fed365770ff6950355d95defbe729cd7eefaf6956bda6d91b44ac3e7e7f474082e287ce605a871664e01cbd2341a9bbe97c75c57d7508ddd75ec2b27d9ec9e755f39d9c4eedaf5956fc0b47ba67de51b4cd93ddbbe72a02ebb67a3af1c884be52b2476c73aec8e6d5ff96acbee3869776cf4950b71a95ce8698f3c76bfb3af7cdc51b90f8fddafeb2bf7d9b1fb35faca4d1e2a37972adf4c54be75bb5bfbaa9c080d5923faf2e40694149f79de4454beabec20d7ee75f6958c91b19188ddc9d8d7ca491a0985ac310d918683cf1c933232765b751ff1f7e6382f0fba4f8ac0e1557ca62ce89523951ce1ae9876a7b9af7c15c40e8aeda019f64ab622b2fbeaab35694fe2469005e312af89ad31c2258f1a2fd87651235303bb257dc206f6b6e4012017cb4fe4ab3540e60ac8e497e8f2f0e321074d4b5a809e9f49d4a4af562aae1cb9b27a137611dee91172ce36abca15d0bd572bac5a91d95706983dcf14753647fcd1400d9071ce5bb5b227e016e0c79655f30b6366dde42d5228228cbde2ac14b58d666f2196225056e614f8a80d4afdbebd694b734b699fbfefa3506e683c79a2501e9b3555f50bbbb3b16409f971e62f72ce3a7845d6f21434046d5db0962829eb9b014957e4cc2bae9c3fe6225c5b5db03ef34a96e0c394040e1805acc726a6a17b8cfe7291de04d91863ddc6a20faf7917ab34a0e8b3c9cb887075c982fa7d6bd3854896409d66d4320a44da9bd0637461225dc0f6b878f37d3ae791755b24cffcb6449e39cd66c7a2577e5eb64702e58012653eaef8cc770e4d9a0d30de09d053b463251fb01f73b1ac670aba2273f079bfe6949582d73c050effbc430d7075d94b78cc29bffcb32c287d8f5bfd376ddab4d159176132224d61599506b0c7fcc6c8b69c96dde6def2dbdb26cbb2496f4dca5d58bb138165553368456bad58f594e214a52b544f79e6a2ee9fbea32e3ba6d55e9c52d9129b34b7824b29148a2276fffef2cb59a21d4bdeb9e545f889203b5c4061868464e4c20c4e2a2cf971040f66971c2135bb72ef03c310fc3c9d83f63ced7d9fa76738d1ba6aef03c1cfd315a8a9c70462577ea3cd105a6bef0343711cc510fc3cfde5496bed7d60288ea318829fa7993c69adbd0f0cc5711443f0f33413adb5f781a1388e62087e9efe52bd0f0c4531043f4fc36867b4aedafb3e4f571bb718daf3b4e76928adb5f781a12886e0e7e92a5a03d19ea73d4fd770af00565c1313e17d6018829fa785c0bc0f0c43f0f3340b3192685db5f77d9eae4bb4aedafb3e4fd72536b4d6de0786e2388a21f8793a870c5a57ed79badeb4d6de0786e2388a21f879ba460ede078621f8791a081249daf3b4e769934b8e8eaac8fbc030043f4feba03d4f7bdfe769194fb4aedafb40f0f37425c2052784d05a7b1f188ae32886e0e7e924705a6bef0343711cc510fc3cbd034e6bed7d60288ea318829fa777d05a7b1f188ae32886e0e7692f55ebea7d60288a21f879ba470f515a57ed7d9fa72b0b4e2868cfd39ea7c968adbd0f0c4531043f4f53d13a88f63ced79da868c6d03aaf4d4367eea248c91cf317224486df7f0081384c78d89558b65a52c8cf9bd59e35a9564fc558c535b7ba3b76f6ba444285e3556cc6f05bb0b1e73caaa9e0c1ef39bbfcadaded5bc6fbdcbc7fc7ee02bbf19bff85abf1ada449aa28baeb75cb435c09c621d034cab4d79a6ce351a42e615ebe82dac352ce9191ac97dce573394d1159677f065d31597067f79bd95da54a52b32c619a7b0411e458006ea076099292b735c82949579a6dd164935f071fbf399f72c44e61fbd69caca610bfe5acaca587f8e81e6e205bf5b6903692bd32e91c5b4d58a664d0ffce5afeb4d61b3d215998b60b5d96bf097d72cba461bf08b8c7bcbc59d4f2cc4e6bb3f7d7f05ecb92fd1be01679c476bad5094f6464a37b5ad4d57ac98883a637db5f5b4d6ba1a20a5f90e4172846cf69a717a8628e4440172b9f621d07bef5d099de159edb02bbfb406d5622222d0f7b98330fb691d090803f5b445ac492f51ad3d54e13bb0277e3841040a45a6b0510a13444f902dee76725b33d1c3647b627a58d85f971e1976e8a4bab1c521787c6cf0071d1c7b2f51c5b617a4099d18393da890725cd89121678a2a49a58667090f3b274d4e10dbd3f5d490d3e9b23f1c9b9db33cedcf4a0cdb63da49e1498db1861651a808098397201c5a66ecca290e4640012d8ef5b1d59add5a7bef139c1c4e54f0d895531c20bb72da811606701a0b084a5200d19004fc3cb5ca252ccc240c20b4a6009b0901c8d70038102b287f15433a845228046d40c4ae1c0828cc8fcf8ad39eb1071ea1dcce1803e9d363349f887b178e37f4b6928352c064d4bd625747a88ab2de729eef19786b6e175d41399012358c8c2bdff726ed49dcd7246a5409628993f2ab665f17fe850e2ad08a30d103111d9438e98b22383464c6cc78e264775bf8ca492d34045b5f69ae01d67fd796d974c1facb3359b3ac6c925048dad22663a410cbc2f2ca4999652180afa492a9442a994b261213a9b6f96e26d14c18cd8c99b2b7bcc4dd167572a68997126c7e784a7359f85ee6663491f1220416c6c6d94b1bdfcb9d0c98c8acfc808b814494b39baeaff1dd7c75d3c8d441f6289dc81e5a984a5bd9f4b6bcc5caa45849c6cad2755b1e70097245499a1e8c90ee0adf4b1f3a5e58b244951d4ece5e5e15be974238e1848d1a3ba0a0f972f6f2bebe97bd2c91c2703561d8bce5bbb73aa8511745ba2e11e9cabde53c765b115833660b142d449c8c5c1adf39151a94546842039a1b33cece679fc2779e64822640a4fc8852e668c6d939ed677ce7b5b7bc731b6faac27b54e147dc06d7f196c36edcf57acb8768b7a58232b4353ec468e28aba32be0f6569c24a0d2686a032c40c671fb27d8cef43b7324ac784871dd6ec40e4ec43b847e1fb504e17d58776436c940c2929195a1a423284f476681c1a87c621d95b2e84bb2d0e188901c99230b720455c18df857238603043e4a8890697b30b5dd777a15d949a1a1b696239a439fb90abcdf7a1571f321ad231de10721a6f08691162126a7a2b741b6142234c68840909b96eeb0434663f045143120c294f7c17da81e99543932c46aec438bb909113df8560d40832493416cef478ed7076a19890ac0bcd78be0b21955153860d54d45b14bb1d8adb0eb7cbbde52862b74501da0fb42c3bbca8d972b2f98e421681303ba20441dbc143767614b326bea3484212454a2d49172e282c9d1d056dcd7714352adf51d876bea368aa82a247151447286ca0d0f116054cbc05893714e20d4510edb634c086071b45c090715341cdf7a025272cc8d0830a6896cc387b906df73de8d6429329638478ddc21c9d3d0897e67b50ae07e9a24425414aa292a0a520246f91de06252505c19282623d48f69603e16e0bc770d8d4dce0c21926d07c07caada9a103861cb4cc705a3a3b90eecf7c07da356961a67404486ecdd9835c51df835e3dc868fc1ea4438401e424c2d0f216e826be80985e4d220c28498401d1441850ed2d0772dd16e5724649131b98c81dd960e63bd0cb8c131f28e8f8e1880d9ab30331f11d08089b2734209e7690393b50accc7720199721df81664048e1d38f9af0e987cd0fd44fd4db9f5d98fb09733f61eee7a7c9134e40c19c9eece0a1fbfe83a2489a2168a6204133e4ec3f64beffe4bc2c2d19b30346161f67ff19f3fda7fffc3429f9e9a1e4e8ed0f2c4cfab11126fde8086b3ebbb0f6e30a6b3fafb7dc87765b2f8c6a483e1c1184c9191996f8ee535b80982f4f357081d1c375761f1bd4779fdbcc8a1424498eacf083d2d97d7062befbe4ba8fce272a84e1a314c2587aeb9314be7c90842f1fa410e6030b613eb1ee237bcb57b8db7a113bc20c911c56f84206e954e2fb2a27812b379ea0863853a3c8d957ba0ff37db51342e409277e5022052150ceeee3ca7df779751fa3d4771f1d55564e55b4bc0d7dffbdf7de6a39c615ddca55219714f7de7befadf6e2e84dacc2047643a2a8e8ea5e3f665c1d13b96483a24c5a5240b9a1f1840b14246cce42d550f29553abe556d73da2da309a1c4e365a4b1d2531416b48ac5c942ecaa1183a1f9cb22e09911b59f7f29e3c957921916d3587194ed986c4f8975f198f689123444a8e162a02f46002a33a216b2791469d664811694df82fa5e12302d00324ca860068b12c6fb602140fdeacea30901b2a541d12f62fb74714b9d0b20a0486325a86848b958a2b478a58e59ab9cbe57262855c4ed07fb9dd2e97eb042d024f674a4489aa99827deab29e79bf09d75e5b8554a0faf4eb6f5af9dde6bfb8d505b795352572c1d33397f5a4e404beba024b0618db1b5a1150e907fd726b390dfe727c2760df846b5fa69a7b2fa739f39bc71b75b382fa960fd11594bf781441403f3eab1e9e1d954e0ecebe4991a318829fa733be74055d4157505ee47211ecf7ed6bd6a0beb0d67a11294580b5b57e40f98bd6f779b5922052cac2564a02ee9bb330e6322ccb7213f0bd97f36b7d548c712e9074645914538c31c678c43ae7aea516f3d89dbfe594a7b0d6567395dbd5de6b93b0b6d2106cb50d68011229feab1c344dd03ce32afb332fe55f36ebb5409010298b4e29a5566b6e35b9859a2fc0e509f86b9632f51ad5dac5596badb5ce69f467130469b41eb747c35ed110b4e779deca8712e5d56715e49d5ff065ed69cfcb3709a6813d1ecc36f72211bfacb5d6596bad2d40a21937b62c1e78d65a6b8d4b33df8284d04b62d65a638d31d638e77c75fe9cbae96aad96b17a92672652a58c90c750814b14c552e753b4a358e253bca358de53c4a358daf324cbb114e9831f17c17e1cf39fcafb2ec0980d54c28d0999efa726d003f67f9cd6cf4bd117eb5702a621d0ca7109d80413e8019f39fe4ec8d5f596e7d7676dc61863d385cd8dfbfbb0ad5d974dbaad9063f3da9cebbaeebd36e9fb4a56817b86a6b5a5b1379bb35035669fcd655c86712dbe62ced98428d5f57d4d948932e5caf475cc9e873d0a250105e829c83100ac1621287a176092d1e2021a679b7ddf98a9164f8e6253dd9635011e5e0dfc90f9f40d33914b36314cae9bfad1a041430931c8a7066bb16a953d2fe7ac69c023378207d8376e9c4b128399782364e35cb6f929cc638e73ba667b8cd16c8cf5ade97c36f18eaeb89cc8071c26db729871cb3671767fa0ed1a5435aed65461fc599b7a537c5566f86d15dd02c0b03f9b98aec038633c03ba55ab5a6bd82b8faa566bf65e79e5c616638c31c658634c79a6a68ae672b42ca2d9ae5bb5fab0cd30985230c2845a9025707ca28ee65c7e7a9c9d7295c73c4ba12dedba443553e770a773d659679d2fcfb5ef5bf6579b39283be5acab49881865dd8b3aec12f593fda65429a2fbf626796f41f4b8fc7c686bb5a22178ba254202c418df5e0d8da29941da67575c6e1ab1290bd325128013f046788c4875ce6403827c8a641d45c6dcca6f599544fae99c8378e43b06e39b9ab3ce3af337015badb3d639e77cc6d4bc7a5af33701e30c05055163ad39c510c0a2dab6d65a6badd5e65aadcd55ebfdad3e50b5c25a8f25dc3cce44365cac5a7d7c0713679c71c6d8666eb5277ad6b39ef56c57bdb51ee8426586c5baf99befac9c72c25945801e4c49a83e3ba1b5fae4a012440ceddbad228ac1ac2c4b7b18638cf1ca1cf70bbb7ee35ed1b0914b693d4381ced4524749656abaaccf684e35a544fa7bc1e7e5acb5a7b76e367e18a7c0189be3a6501e27e071011e0760ab1ef7117f29de2faf14011da81f5c5a6ba2031ebfaf7d6fae96afbefa2a34313902a8ebcbccae5f598206e8d72608e88acb81ccecca3bc58d98d65abce52d6653b2c733c73404cdb3999fd839db155ab3b49e01d61ef5b2a6f7cb606c61b83830c53c9738e79c4fb0d6c4142cbf2f7bf8a31f50e620d7a60f8e5d29fe6cad3a624f389d33bfb174b0d95a9e2db797c7b7c99b54f839670ed2107451985286c863a8c025de943b29149f39e00059e691dc3b26e8540bbc0f79fa635e3d8ec907ec0739264bd0af4333d31531c895dad46762ba22f3aa359e012efa6c6ea1cb6b8f8f2dc2a2a28df7fdbebec2406d7392260894281f808492e8f307c204f6eb0f3c7e00a7d59ed4b2f8f4e7d98cf4dd7b8b6e8ba822104628e220e02d728448c1d1a20806cfcb5cbbd025a63ae79cf3f7dabcb77287eb8433119b35d725c6ef005adb94b0826be50b79ce7c73cb42b3bee8149b82e5576e15ec6fdfa594ee06c8bebc7b4dbc19946783d724c8078837ab2b402d79b394ce82c198e6b2b7e033af5ae49a7f34848f6b0ed21042fc691929149d941200e432449e023fc79a7925806769f0d93e186230ff470d80b9251bf0e23396c1e38ac7c726c647b56e4f6badf5e779fade6bab976faa0b9d73ae59d78a73ce799539ff3cf201996b911ce1d39f35219bdbc456c6822856ad371e41f3cfec2ec6d75c6559ab337baf4c28bd7102985b96c69c8ee0618cc907e04f23f93259c256adb28bf1f19523578e8c3688ba62aa29f53cb36b2d86746b7d4d5b02c61ff63ccb750bf2d7bf49dc19c6bd3e559b1395ca6bdeebd3834c689cab0da3c971fbaaedce76785ece1f0d415317a59a10fc4aa05ecacf31743d7cd61905a51122ad433af7c08febb283e0b538921e07c12456d9343db2045cc2073e73fb811ee8791f68b620bf364dc8f8324171187f2bae72006e711d70bfafa601d6637cc263153ceb710acbe24eacac3566e11dd671dd3eda46dbd75ab50c06b7af3068fa0a03db9fe0fad1f599dbb2e6dbd7dcf435dbc80a7867d9b5eccdd162d17edff6b4ec733bf69af75505bcd333c7db25ba3c2886dbdf14f54aefac79844f636cbad0b2cf15080cbacf3a6bd9ebb8b0cd42686df613f267ee25b1b3d6fae320e0f9f590f674465237ab88a461191dce2a2a6ad162f3d1cca1c06de9983926f881df87416cb3b556fc82b4798b971a79e637654996f41689380c33969c12652e96452ad88f8b768e5c39f2153d36717ef1d9ab46d49977b14a11bbe90a323937d558d00c5ef062e945d293505f203d362994c731c798d315148a87710ef9011e83f098a8a4ad7d390d01e4b80586b15bdc96063cb3af5226698a430f9a2f004ab32bd7c0c51e3633dd3d806b8c29946f86ae7e54a4e32547b84fd7e95679ccaf14dac2ae4b54bbbcbbb83a7186ebea95e61e76fdbdb5ef5be68d9424416b9e31ae617c2364e3fb7a6c76cdab8b4bc2f7d9bcb7d249bf4e237cfaeaeceb878cab7b6c599e2e578431c62e56abc755048bcb6ae21554cdc3329e3ba864cad7f3929c9911812900021400131700002014080744426198e66916cb0714800e6b9a444e48481488037218464210062114cb20600800c00000409129a2ea00261719f6457f11ff845c882bf8177b9ca0540df7fdf14db0731bf8ad33c5663b7bc7989b18a81aa0a67c0394638f116ea2579b3aba4224e3922142491d4f7be39998ed2bf76538e4c324e7fe8f64840fefc06629cf8453cef69a847239fa09063d1465f1d02b7b45c9f98c287b5fecf9c2a3b5ea2f21fecdf78af5e2e18466fc25375ca521138338b7f8c0f20ae63bae0ab5f1b702d28c497a3ee0aa520f287a32ed239d010abb393fa46312199c77812b980b2d488cf0e8411f15a1644ad5982958281197125ef99374a98c2179bac98dd59e253cdfb0c2769a0b1743f3105a6ec3f8abc80f6e9b1d7bc47fef8979c8550a3fb5f2d059b6685232945c627fa79bc6452a9567308bb5f41d482272cdd45754de11770763414b7787aea8c26f40d3e8aff74536fda1b6a218bfe1d0062643b0833c68d32eebcf1e658337ad04ba75bc15ffb09f5732c8b6a52a642f1e0d54f69f8a4131410d0d3b8c435df9f4fa77e6bd5f9134b1f9ab6adf010ee85dac1d50552388bd98869ee318393c989ae697c8d149d8da64401af1071b146981f9e430961864e7041116f32e1c83606352793d4d9c341a7de86bef7c0a408610481e496154bbf789579ef789636660251f0eeb257bf5d9810237b14538ab92143b0f1f8125ef3f47940881fe3ace1f79136b8a41130961ce6f5e8317328c1d44efbd4ff4b227b2a88dcb074c519c14b2630ca00636538523fe619de599d0cedaa8d606ef31e8c04a8dbb0396eb475651a278c348c25b5828f096e58c186f0612c9934e317448999d91ccac6f559c49a962577364cb9040367374c02404d49141ed72437455e452f39c9aa701af9635002a7dde4791eb6489e6fe071985ab04c940e142c14d6f04116fa068dd6421d64281a0b21c54264f7f23fbe37d3e9ac0eda39fa2f0f502f38ba2e1642e82215e9ca56d8b12c369d5a50d27dc819d093a6c229b7c0a1bcda078b4a8504d550ad0860ac1f79bc655272cebc037e4da345625377458ec5cca5d87d0ff68ec58ce5c874168e4274af35f143bb041de8c9a172f0841483420aff203a3a4180ef2d320d8364c728e6b1b40399fd1ecbb40477af03f329e3d2feb9fb9414a360cf9a69b60e44cb4d9ae2c74c12328660637cf48d13ada2187234e5a932c4c5097213a251bac2d3ecd4d0ced5ff550b6f880e2a114bb1d42ceece5d35f5dc990f1f1e02b9b767c3e5eb0810c1960ae5c5d0d07603373fc5633f8d2da430189faf7190185745dd5e9133c1881fe3fbd2fec02675667da79cb7b964bfcaabbe3c89fb2058a3b208afc8ac9b6d656cd6e24d92fb421dd9c3df745931d39abcc640e78ecdd76773c460a14de2093e453ef81d66b8d220ee5584911bcce017e6f9caa00c3ce26f7c5d83f9ab9b799c5eb086c34e083e36496d182129c6592a6503de9a08ccae0ace54e90c0a1c2a30b05c56915878a59029f60daf9fa190b1da7ce171217ddb1be1d1ed83727b06d134a9525ba07d006cbb0e9e280e10d51260609e28068c90548a953a17ef9f7a1af1328b57dd2c4ab7d7b3a5e9b65dac031f3cd1b23e253145703c7e69bdfa800ece04a5e2fe545b68cbcff1f747daf93cbe5bcf78d4244563a98075a27bbcbbe54b0b809bd31d84ee45f850e11b983259529d2e4ad7ccb8995c13d01e0288ba8ab0c27caa186bd257421fca6525189992e94602f11caa30f24a4cc7663625dc70195a215a6831e35450abca8a9f581029ee8254856e32620bc02bc9db2856fdd99e425f714a21574605ef4f34675fbae3a5f0693f7a4d2d7928c5068c0da6ed68f68f03dbbb903f22745e8e29b8483cc877adf5286f5a23c6919a01239966f8110739678bd0e041f5da5d16d7d587bca7c97fff7a2dfe08ce611f46229ffdf7b494ee7a83ef527ffec63e8fd643b6a8ff79bee40b72383d9e4c3d9cddaf747a88f85c57ae7d89b9f99f4ee9ff8481673033834cde911c6d3d0e34c2366d26377040c6a74ff07628d4b4deb4a530dbbcdf95457c3a4e1a0717f007984db8fb269ce08f9340b1feefa7a9d5afe8bc963243b2c2b28091c61a8c6e1aaad139a562fe71799195f35ad3651fc57ea4f7af265cd6cd1e244533720678b95916a083676902596fea31210e8c0c746769804678f260cf8443c92dcf7ddcc1e1672e629d2c7e011a44dfcf8478f456c26fc9dcd9c1762c634e03e60919c809cb445b5777df2d5843b425f6f3db1466e8ab7b5ca7bf8349f7a6affb46e94615728dc011ec723eef348f35411113fdaa962c781e2e201b189af082b31ccafa49ba47c16d27fe216493c92776eaa6c0c9217fd76c2b247035c64accc811aba50bef57767ae0dec03342da617762f687d82772b651b7deae36cbfa1cdc3a9be3f5c4a849a33f02dbe0a1297450b07ae07b748533dea5059071a96c28449558c7014b7a483d757568788d23a8409c2a4f61b392bad889ecaf89e54d80023c5a2ffe730dad1a5e23033e08be0ce2616ba9ec04e8533dd6124a0c2953a569a4b878e321f1a75796a899f30bb935cea2b520356c19e5d3a111f2750620c3f36529b60ff3c0e88c9679c7f0061c4b885617aa573e1d83e3d57c2f1110acc15c148e2ca45d5e1a45468f0f17a2f1a74b1cd91e18bc0577af868d9af2b67ec0fdf081216bdaef6aa70bbedc5680058549c595ae8ee55d714e957b131295e43e7c1d270b6ca010f6a2abc2e1ee18911264c470a998f51421eca53ac282a3c26f0c9fe013f9d95c00ba957a121d276c7f8b2668672ee7603f8f8c881d26387d9b14e85b86ebf8b02b06fc728e10fe3bfe33a4d48919abe33179bfbccf627407754b99926d6b7dc1ae8ff961b0993cd8cf8c08fa1d315a13e4e5e0cd4d582e4d5b2f9af7680cd7587180d29d48d4092aa5bebe0cda609a2df842e49729f35831301623575cb30629d53d35e36331aad9d099b2e50d08fc9c20c82a465d0cbf0646dd4fe62d4ab4bfdc145b94cc4d3ff634a897fccac470e8dc71c7e2a1800ffd2c1e570d8e97f9ba0b7ea41ddb8a07176908c6e7970f9bad740e93341e5d99be0a8732bd0e391edc942ca2b597043208d469520c8acf565aa243484a57cdf2ac3e6a51c875f36c4b251600bfb3191353269f603f32a105c4366294afb61cd82204e5612223df0ad59fd4de1df64f37bdc198f6bff326fbed62f0c82e92047190d097d88beb7aa838fa9346136876cb248ce9d228d12e3c59bf348a928b253cf937e4b94461573c040d4fb7ede8e7a002c4331d9c6dcb6373ae651a6a1835a7aeede040de973f6897b6375274000be1d800aa3db73df3f60b1705dca8e42a96cecdce833a3d3f4d70e105afb49722407f607a9c7824dfc3176e74f956a735372ee44a02b5a4fa8adb7f53a55b47117b97574b51abdff11ee8dfd20fb391741a320290d7b58b42a54e3741a43a0718a0514e83d8ddd9b73ee0c67b8df92eb0890debc3387fd3696b0af05e4906b9f85b0c3194564b1413a407f17b4de60396223a3606e71bc5752391812b146dcdd29f7f9a8cac391d6b1f3bafb89f6ad9ba3b078ebb13eec7b2056ebc8a94ccfcdab19ad9230043fc529031d1d47f3ad0cdc8786886f10056538d6833f6ecc81bc98fb4284774a0402d4b3441a8e08be6371933f320aa787c9c3978f1bcbe3ac144f2bf12a5eb623069323c8042ac0b228cbe37192a3c9e114c1ad128257ee64942e8e3c56c3b9aaf98a2a9e00a305744739d3b74e250525732b810ca7ff6700daa22dfc9474a931c3524fdfcc6f9e5a188347fb00da07f6403dc4a5a00ab8f22195a20b053a278fad923aff462a04e6e379e7f1f23b56aedd3d5cd31d93d3f74af6284561477ff0742b11367c072f0cb6311f0685568f8fcb84bb4b750b836f511a401176ee4dd90250b037d732cfdb572ad981253fb36560df36b74da03becc6de0e2ca0ab67843aad05e9483c7f80218a06e8c05faf10cbdbdedf4224702b7aa0abbadc6c8ba08a2e61c8bd684d1aa0acce059ad000ace9fce670bc4fd590526007f7ad9602958415fc93e2735e3dd16d528a6b3a2c76c092f58f42cda4f28237b6a95587285bc6670a1542872d2bbf40519a1061f11d7206cf504c527487056c457990a356dd21267f710d46998d3bc71f0df15f660dfcfbb6e6b451a7be56952e51f71483ffd001fca55cb978454194f2e22f0fa8dce2377fc4807982bc07404661499fe8eba5c9777db8f76c5552b337087ddd59e51b874dd072ef03563abc39797328299cbf815582f8dff8b287aa6257890556c140d778d57070d3e4689cfa84288d0898dc74521a3baeb6da76698dae12286b04cfea59b3d716e35611db31530238344b2b19d3f9aa0a54810aabdc835c758511e12766703ec14e5faa199962304a69574ddf6b4c3da72c5052c24c9d70bc7c21f08a02df41a8c41a35cb6ba19c35252d685a51e185447605e40047dd7f6e706760632a157943766681eae57999064c4f15ca72e9749055e2df87260c3647b2e4b8c12ba4250b995cb2bbd47a17f5cfed92513f0255639b3a0129ee8756babb55898efd66fc05adf7da19302406f3d80cae69c67a8664f8c5b6139997cb8ab92cc3237c3ae9a98c2a3cf0d4674afe8fff6d9e59f99d03ae8f5df5398e4c4cb22df500c0c1f71ebde26bca5285282b35e516dc0c00efc28545eccdb88429ae4b81f1b22a13972a31f7b1eb8931469831f681048fc5729eac0892c83a66a0a7e759f6a750e6f68179c94fe800c6997d6e2ea5aa6829593f431c2148b29188ef49578991f8bafacf1c6551a313ca562a0d007b5cab88eb72928e1b0b137338b3df26a3ffc45392e892934594bdf1c6be9ecf8e4bab1c8f07e569f3242e7961caec9d88c86e691da4d30bccb7e5e52ead52380676c2ae9fe61c5ef58adc12f7e49eeba571e53fa27d0b750c2bde7df847b86df30db11de36d49b8cb64eecbd0c11a578bc8860fd37033fbea24d5329a5406f75a0b2f555dbd356ee5d2e1c8789f63d5f4fe6ea5aff611b4156ec7b4bacf67be90f775da3c82f3d9456746ee3ce1ae474d6032d766e6c50db858e1a9882306c626abc5ac32b56fa540166dfb670cfdfc66e89bdc7ac3bff6f6de1c13310e2a109b40b36d20872ee58147e7947f8adb9291b26a6d45267f22863e9a08f5da14e026d0fcace79d66a3afc2388a5262a87c084d9795a0ff178bd7e4b30a5e38bc4051bcc37c6bb7b2603230a99ccbd549b7481238e1327cee34a991f988300f07a0d0a1e21c431647c245131a4c68d78a6b0928f4dda6b16ee67f0051d12a7ea18b09154b32691d9a191449b25d68167346c81ea243b96f588c181f396e2010e4c3b7fb14db3e5e7c1808c9318b343d809cf0577182031ad2be5a2a380ebf91e13b3788c4b475412dac035dc9da7c93bdd6881dbc3f2edff53569e497bea0788d5f311b5914a4e89dd6df42eeb2d1a4435740a1d76820948ac9c6d2f074883278bba7cdb34e19b961d9bcf6517332f4a470a7ce370c82d6bb4de1a8a1c10934766b90c74f1d91c76e76ed8fafc81e3109653248987eac6b95c28cef61506053b2921ac8c4238780879c9e853eec68733e15a481da270e7493ee53e0f4ca50a15b1f500b738346dc49d7d81061e4b38a0a4baceda651c2d02138d522cc8c97512240a4ed4eb5bf0dcf73d6d981a652313142223373369774338e9809cf89a20b16181f138798fda90a3765303282d88655962bae0737d892b9323b1411d13b85fe4d2f0585143545dea69150dbaec4863f43880a9bf25fe67da4b7c8b9eadf92595ff23b178da44710a843f92f09ee81ed484144f5ec3dfe11992e5c9fde6f609665bd6fe4a65a369b6530ac45b217aaaa4c24547bcbf359ee5d96dccce87ada3842aca1a30fb5019697d239c08db79fc2102cd014eb5715c972c5a2dd23a8fd65b37e0bba0f99fddaca81bbd6d8d454e75b59cfa737f98fb29f70a3d5157533104ace001c5b2a797ee08460453f0d1a8bb5bf02a8c606910ee440588ac2f945038542b74775d83393d39e4b6cad6ab002d4c8f6932eed55e57e20145eafbc5293e3ed147122c6036e484aa7644a0014ecc029771d9fa35e3c75c3e68de5b2beea2227e7be352ead373cddf59c59fe4d98088420bfbfbf76610d0c77e91aa34123ffc26b5d537c01ea36bbb8d9c393769ec2050d771be9f0ad95409ad87559abfccb7abc82d67625f9ddf96214199b1b24cc9d35c703dbc58fe607be860a746f67b127381169cd3f8c4b8007c5cb40084d06498864c002ed4884aedb228911fe0481406026a8754ea8e07abbc3026919f51ca820234471b463c737190a21979ba1364f8c69748f9ee9486be7472bddcaa102018527167c1438f4514f0c91f78e672a92118bf42a784a1152a58a7016d3a8b9512ae147cddb8abc6ef1103b38e254dbbd806ae9ba22340244fbabb6dd6e8c00e1f95a5c5884f92e711701bb280799eb1609c0201910738d874ec7336903cd2dbc2257964ae3fe0c69b0f1a4d3ddec167d5fd4c42bae496920bc7a1ee81d16ffd5a4b886b77246f84cc45b24785f27c4f06c4eebdee60de106b696db540e89683e53574b4434f313d00e21ba2a334fdefcca4271a4ac5ec6e9cf8e9479f3f20e4483be6f5008141808645e6d1ddba05babb2b98e3824ae72369a6299c715c17b036c120993835bac686b4afb9947f9c2c53193746f47f21c0d1d8ecfedadf5d4261dee0c91ba1c67ff2a52d444f4a967714110bd15e578c3a27f0a65a816d3cc9a964ccbdc981d72e56f6db0376a691cf83d3f4e382d3dc95bc63902ec406788b7e177a849008e750091a816fe56386fe55d7caa61ab91b943bf643e7ec40be4ded40f522aead6929ddee2765c91b0782acdb72cc737f85e627a1fa29651343db72d540e722783f82b2f5d637e23bcce292c464741278583f9a9a62d25c75d852174a4b5841515c3409ce2532b6ab992742ccbabb127a694d9e63899e1977565ffeb164104194188f4a29da9ed924a29e354d361787cd2714afd8c84d3481e6141fa98fd836060b8dbd07604a065e7f0a2bdb46cb4638da2492400777c8348b152307b77dba68d90e22d558d42d910d5d7f4d23d2867edae3e5547ca6eb610cdc3f804c998dccf7de5eedc304206e754a7cc393d6d881cd33586a8c41818c816adfecaeac46f6a51e825c1c57eb65ab05dd87c45b15f7e10ec7216795cab43758dd711cc43d8260821af2d9c599b87da3b3d4428638acc17091e550d296a8d36fb0453a38e5aae03a7425b883f33f599849658bccc2a0a1701de09f6f5da5b8a246a87bc2271312bc791d7230cadc9ee9c239aa232c49021c4de6c7aba563d7e0c3fae8cc5dff2736612fcb455aecd8901f84de196ab9f654eba0d43b1e937b754259c14c2181ebadb98b8ba669a5a678acc4c007bda276cf7911919dcdb44e46918ffd364cd8193fbdecadad9132d195029d9418aa6d807675c093557e6f2b12df2458554ce74fb99cb304aa7cbd43d11ee7e6a0e22bdd448a39ae72a03d1a49f8824ea51b7101664544e2989c6837aef3dbf0ba4a70e49afd36c26d5a992b1383ab4aa04a768545a1dbff5284bfa4c0b9954b781d60476b11c4d1604959b81b4bfa3919b754bcb1caf7b4adbd13b24c5dec30465384ee4e46fabb936f3404f083c09e3871c39dcc12100842cbe2c0a35e91c0ed9c81f820ba04544276e1f2f5b4443260b59f20fff5b09e60c7ed2a40e786815088dc02fdaa21ab54d2ca5ea2150ca661e2f20450cf38332c8b3143147ee9ed65e97788ce5ca1e86f256ee6133eae45876ae34f110a8472afb375256030793b1a05c1712958c6fb7e9309024756c385152f308724b970d11b42a043a584d5647471e033c408c7b92d1a288cb4feec5d20edee573b31916635c536501fb08e1e3b08fb143bce488fa55c39d0594a5f9f2a02386dc17a4207874b173baf4d173040c8f938eb0bcfb7da3487cf75e290eed654350e70981c70a3ad04722330a9bced19117740b2ea68c6bb6ef3c2c3d8c3ccf4e368009fe1ba8e065cc806260e62483865b21e462fac6ba91d32a50be78ba981f2357ee0a68f4377b448459d00a262b4648fd350ed4fe958bef2dc17e7a90e34ebfbad2d45dbced03d0b24e67d756c8f403dde5b780d5f426121db00dd4f9ad5f230515d39a9ab40ba24f2817323633e064c22a8c393dddbb5072e7e3179701da5e0d7d01004704b780b033d3cdf36e20240bb39d83b20152f4a84b3304f421e57d503ce87c09d90847511af204879d7d8d26d313c42644c9afdf1180d63694a5d8030a1271ac16bb81c766e400d900d075e556e2f9b2665c5f4b772d3a3e0ea415d9402c0cbf7987c0e67e30fe06e48e8469a0efd3bbc39c64e1199a21d7f873a8f2ee05cc3dce46ae14bb7bdd59670b6334c689cd2334fd3b9b1e1c72b66b2204e95df12503ef32b1d9db05241debe495517319d08f0b48c83a3ecaf6c457b3cbb087f0c322cf48c6c5cbb203514896726492888a934b42083180f727ada7179d061ddf38c6826c2d8411fe8d6ddb4c7647253d0a590c02e6dee4666edbf249249e74c41ad98f5cc7aef7de94015b1370f6f554508af2660e9285f0beb2d848c82fa802e1d786483694b3c3c4f97960087bd4fd60c09f43eacea5a50c313668ce0322ffdf4384ec64bf2f9838982ffb459b0f82363cc8cc04f403d02a06cf847de2351e9059a2c3ef5f38f5e639b366d043dffa24030d136ff6cd794f1673e3e81a50e732090da12c83959d224cabb0da0d1e78029ff00a48b032319cb09af87fecc5894baf4be7cb5d80909ba3ea24811f5601ed024a44d51a9587aec5858228588aead7abf6bfdd78fdbdbcb20337de95850b18507dc7eab96b93b2669f41bcfbbe0c77873a341a4fa73780047042c1c6d0690d3e2bd34575391345df3dc2f03bd6491372a6045ca9ec8cb66c6fe05721b2b842c65ee6196c6dc4b4a099689f4d1979e51ce994243b39af85d60210697c6503c351651f35aac673dabe5dbca5917162301703a5419a827ff58e4f3dc578ab2fc9436a6a7818e73811ca399865cdc04c20270a8bbb59ab27211c82f07ff04bbf955ac88f53c63db77d69ce84470bdc53abe7f82edfa7552a8c934218406a6093e70c2a29c18c22abf395329630c4a493d68bf0721e8b7d84d515bfcf38af1f750b46539f24b5d9508b29fdf9b4b4b459ce7d1689d2bba66896b0cfc22cf5aeeaf373ab67c9c0912f23e652253d7532efe5f36b4645a36bbc5dab59343ab48bd7628c27286f089ab7e8454d4f3d43ded1104b2116430d3b957c05884bbe7a48ce491cd0be762e6374e9db94a14325edf1fdb73519aa7c78bec02b1f349ebff65ccdc0ff95aaa4af2447fbbed8adfa535490a193c15f79206ce71a43f7cce120160fc88048d7f2b043e9d791dd1e731644dbe505ef87363e19177d54e8f9779d1cd2ea1d69cbdd5df3c5da9064c893d3d7c44109cbe93eef9f7d1088ec88f8731ddb51a01fd7e869c89bcb504d493771a2f89062d46a0249467871b61fb0ad26b8d3dee8ba89323fa5f8dae318ef7f962f982a89f5d7a8f65328f500138dfde51f60dae372fd2ad4ba8df48601fe903c2ed1ef73989c4469f3fad168962f9d38de521012d33d007c975eecbaa875290f9ea4b047a8abbcba7a5a43f88bd7c39a733c124cde5a5c887bd120c20ada1f44ca6f5c272fd8e41c92d4bb202f5941fbcffb2c3537cc55312d8c50c9a13c71a068a5ab982b157d41c48f7154bc5ce3eedaeeb4462936cab82e0ba8f2b45a6497db3829778100ad96029ef533fdd24b5f60249169e84cdf5b0bb09580b1d72f423a528213b2cf2890814818f8bb1ddc97aa13800df76037b0f1d0cfa7080726a5e35fbc1a9da22f6d2de81feb55bc16394ebbba03bf5ddaeb188d7f4671f2b40f7b460928963147d6bf6fe93a8b1480be87a874a831d9d1e730436572c00d0299e06fef18050a72074233652c5f7d737ecf4f84d1c7da080cad84bd321ae64e000e51f06a34306b98be6b007559d9b334ef68c94df4d5fef74b0ce8b2ce35d155253778f47d24291d0e5c25650a52e65aa70b15fcbb66f54536f7e693d23532c6ebd0e735a53ce22dc74ee67594f2adb694f0e27c50e5ff27aa537083f4524c11a4c2d43a9f3efc905b6f9817f6db069e9556402402ebf77228ecba62a09b0e1671100e688147434b8800dcbc7bb6dc50d84197be85cc5853784c7b8f0c5f762ddf7c89ac24df37254241e00533f23b28804a8081cd45a63baa74475ace8c94a900bf03dadf688148a52d55804b56558dbf790411c5c8d8051cf76cdb4ec6736e7322c10b1fe46943e43879a2326a3d45a55186cb2a9c3cb3e014e21f5b1a3fafcb1bd5715e86d48eaf4e18d918487916f1ff631a80ae8e70efff402c2bebcb6541937a5ab5ffafebbb35804a97b06d54b99c907e1152b96fe882f3621f1ceea1cf626383ae0be0e87115b3dca041924df254642ba6055357280b6fbedcb29f5e3c0e02674a5e5c3fa2fefec4d1f15c6917c2e380b91085813a60bccd53be100dc071a2696025105acf87631276d7d2dd60a5b7fd97da8958948a6d86707bd8bed03119a1ab0ee5bc4ea726c773348e9a1613230052cf0a4c31115145d76019ebb9c8257ec99f8f6a4c1df74abd5c17b9e42824eca0342b4c9996cd670a4431041acacf373287829d713c81cb64455bff2dd1d1a156b5b2cadd136e506d9e855b0d7dcd123412a9ced812808db8d84a2f0413a7d58d387c9eb47bfd03f622edc43ce6ce2a5cf7146601b1e5c4595e6ea6d1709de1b3217c486fadc8d458f4d019ae2b7a27e24f94945c5b90172b3b75cf8723c18cf2a9bbc1697b3e314d3ba40a25e247459eff379f52850deab0e1490ddf2064eb086c5cf48573431519ec2892fb6f900688902489fe740c6c610ee6656f0633c4bfdf5018683b3a6e565f0c42a5abce79b1be96816ad5205c3dc43b33e233268b9fb0c00538b3620f4f4702c7038d876a8f206c3ad5a11550dc9da9a74fde596d8eb379fd935e923aea4a33bdb3dc6afe5d2ca7b391a58a561d17859917f9c14018682f3847e1b28e33d452814c7d01497cd1ebe22981c9938407a8eee2f9eefa27ae3ee57186105e33a51e5cc4c37ca7d48e53c23aa5385224650b47ddab000e611e485271684e38dd49161edadc9a54e7fb231105a3d437801b8c9c9baaf9fe64eb3900509a277807f30ac2d981096fb06d4baead40d408683e53a198b34fd0d3540c7232cf2c5cc0763e1071279ad858004cd704baa60b1e1e080a5aa1831e0959e6c8449124a7a81712e36ae0fa8f5733623039fb97a70826d9555c0e2a53c99d311608c5536d58f11371fdaf91623a6f481884d48b2847f4c4bd4b455d6813d1e39ceacc7f9be6e38fd2750b25fb0ec6aa8b894b3b718a6065987d2726b816230db81840f528fc7d640769a6b1a2803fe45e49515371356a951053a64475460f5a193838aa5f068648cee246126e12a04a44cfd39d91fe37d09b2c3adaa8cfae4eb0ff0d1539b2f31238cefe7c9218437dc38ae72be98f978a1a0ebde22ba7c69ab1497c9181dc903d811755687006130d9264e5ae05fea219e0ebb643918f886103662ddf7a4748ca8a894be7886adce8127d080fc15421e2b3582f7a34dcde574d37f082ae7f9b5000d443586b0ab3de891406381fd1383768055acfcb88089f61a5263299ecdb8fdadc8e9ec4ec0d9116c520089f764143e2fc9273541015a72ffa0b92fface613491e26433febb71e6837176d7880ca52e2baa0ccd5ec35fee3c38b358f2ab14c4d11c7579287aed87c5719f1a6d6eb1393e1f6202be6e80588751fa3ec8da257de67b5d7c9f8163baa9eeec3dcde3e93834f409df1f2c66f20a637516372aae40fcb89efde2799b4ace86fd9ac0edc9c8bc4b72b1ce594d67d1cbc0b810e284fcfb4316f3680d7bc5b6e472afb9d44cdec7f765c9533320784f5bbd30496cabf968b7f9106102f8eb50e118694fd3e41b458f0bf5c24acc0ca6258ea88682699cf13a4c5a5df8b45c01ab9113f2618fe974e5ea4f073a9fcebb4f92f727489cf8421687c6f5aea076179f1cfb2585ce6eef32d463919df9b2a84cf5e69dd0758d6410ec11efefd7769abe8384375e1f24e205b6cd55a4b2de22d16c159ab1cc1dfce955a34fe2914bca999e7f265e3a227f022cf6e24b0235ae463358881eebff855e61f1aefba80102e96fc2a0d118fe95fd6479808fbef974db050febb1c2bd00af7e929233da6df492bc8b1bff447a8bf14681c07d77c3eb16a7b9131322742dcb4679d39e5e0d80ae5b37dbd8fc501b64bbeab9f65b256668c1b91c6ef4b252d2efe5856fa555aff19db2766eb2d3d197dd50342b250fe6b5aeec3e3282ee7e1ec94b3e58bbabc6a6031078f57d80536ccaa1a041a039a2d2c73b0522267b48eb344e69cb75f4b4abf4c9de5202d62303f3293032c2ed3609c95149066b2296fdbe08d4dcf8ca9b3ad160854fc5c473e3b2e9729ef2b229898f70cdffd6f71f99785a2b300abc565a3849299b478ce1196167f2c2b9a25b05a4c162a2a4c6993ef8864b1f0cf82a259236fe41988c6fb66122d16fc2f17f92dadff141829d5793606777aba6210a5e79ce571d99b86ff74dcaf25137b24d85eb684f9cce23280d00485983c526de08b75d5d9d74618fe9f4aeca413279e3e1d6f52511c5be809eee55ddd427e308c96ea74c3708dfc8950758aeee2bd1819a827c8ad4727f057c43a728da560398ac51a31f50c28cf9bff9a3a5d41fc6ed1fb3c9d0ba8e4b3e5907ba4666be22a5ab126d9ff1dd8c3e30e829112861f4be95ec1c3e33bff736677a36957d2081b854948785830fb5f14c69341f0c38c850f23b93dabfac81f8d35d1f575838fa42c0d1823d96073c9cb316d3ccb53d787344a962093908cc10bd3939550e76b7985670b93345ab2303750d00cdb5999af7b38976cad95bbabd7b8d3b6d39899c7ef1bc8765ff33ada86c547c94bdb86e28f22811cd0f0be4be5769b140f08322ebd11b25b6aab6b0d2917dd7cd99bb383c85637e529d73417929fc47bd6a6d90ca447d6bbed2819d26c36c6549bc17c903b71c02944933289f0b86a7736f5f35f4406121d08d15366a17dff7ecd38c53cedf71c20c83f69e3413ac684ce36e9341093179dddaecce0f91288dffa79ee5ac4469bc0c4dedc399ad8f246174405d923a5fef7341b620f79ceaa6c2feaac89a230847ce635af0e04c945a1013d9db84fe9cd53c4f3cd678c40f34c5bb3081a97e554f43142e2bb8b97a634cd4f6ad41384213ccb6868eb92beba603a05dc2feb916d35fa111597d6ec0020df2037062c2099144238e0c1f0b0bd36323032759b09ea476e3b931322009c5ecc564b6f908c085386441d28d5860a53c7c20780d7ab3f421ee54088a63e89b33959c21374f951a6cc2c3ea89daf7751afc9441fd75a30af9578b9345c435cf6fdc6448b751f7af2d8ab70d54cf636128d9e236e83ab5e780415ae320cd4d5f5ca8e383c6090d8d0e90278604c92e4e6fcb56c49980a2666fb0a8ddacb9596097dd194a28ff954b1f50ecc12aee91e10fe5604a348a7d5f0390ac49bd0a3b1c63e67abafe0830ad5b63d41b408402029004034ea1f5594cc62e1c5ea0154ec04ccd601025413b9ba87e1bb4975f4ac2dbe933b0daf607fd134abdaf848de03ed62d1ea660d528b8275a28df3d42b1a3f038d712d5012ba2ce7ff14b6fbf8fe9583d6d973445453f21ec4ce3718b53207463d3d15b4ef0a559b31d9d97bf2fc3705ddeb81763cd03189475bd7604cb107b432afe17f0241ba720c1c60f687e1967d79067e692cadfee3b3e6d04092df5af9307de4ba8df0620cd3c3e06df8fe5a834b8b9ed20988e4185d7c291340b2f0b11e3280075535fb131a6a18468c30613371bbd2a1d0ac25e12d80ff757a8559013df2352fd98629902c0949d7a629999ca8a57dfa4a4b46d8c74c341a89a646a807f85c5b6181523e7b5c3504783aff870ac345a0699501ca2ea57cb9a529516a79e557cf2c5775003d592923f8473f469b80e0b1610c7383f7a0628625e974501936d38a0f91428adf549954e974f75226e281c43bc7d111cd29d3124f9de6be6ca532158d40a863b0cf53d5733bb120bd3b360a9322f36e1e746b81aacc7f73745866265dfb90de935e329872eda37a97148ddc18d196714b2516a3c5db328e52490630f1486ac92a318d9d3256d29903910d0f911dac74c819cfc851c8bc74c3322c2fbf2385a1448a86b67561e0a1c0bf2d239336a5c2788f5474a675c7f25f02ffdcc0f67c74d29fbba5df934e323351cd877e87d492c984ea1ff03b524936b3aaf9c86f919aec8e4908ee8669a0e9d2817601078f213495fb337d9c1fe7bb3b8c00c3a03c92776d87547faa1bbb6cf3948d22c18e5c3875a9c880d2be98f27a343657a0767487e099fb21e26349465c854d6b9c04611c7c7f4b19c3d78e10b5a4a37cccfafdb00981583fd14fb556194e4431c6893a50a8712ac71717017b1fe49edcb8e50b88a2a72e9dcd7acd3131d647dc838c27fb2457c452b629121e6850936909d1beced7d118615b80d927a781751f334d8039f25807ef75b64288fefefb7b3e182ebed008533e8a9941c9ef905c8031855e39ddb4e4e27372b209770c518b51e016b1e6c2a52f30f6934f73e8a82351b0f20b88abcbb72b5304a18c6f5538aed6fda185e788bb383cbd40733e7a443abdc74337e535f8a03cf8d13e8c2efe2755c1fe274eb7e505a90b40dffeef5f4b3cafff21fe6f391e2460405704f8e23e36e67e940fba4bbfca10095eb263f88a037993914c13d68cf2efc31a7e1e2472051dc53c7b19561424f8f17dc53bb74891a8338a7816f055ff34a0263f3201e1f4240b25634a0a1881e0ea3f5d0f7c3768cb6e740e3f5f6107e87bd7b0026394a468f13e3ab74df66332c175789077b0927274bd84314bca0f06ebb790281acf853a6a2218dc54a8cef0808fbc87829058cbf6b070b2fb8dde1babc86d14e7fa5e0aed07d10602e6e7c964f50a320bc257eea376c4f531ed5b45f239e0eecfc4533e6bde838edc3fbcdaa1a7dccf87bbaa9b6e26b38bbeda0a28b95ef48eb64c9ae368eb3b594cf47e65b3bbc7b9297e93f28a2985e2837606fc2c52a9eab05af313a6651d3cd54630a6a4d816babf9bfc1d662fe4687055d4f5c9820e1dd45d504249d363b6021e6b09de9d7cf015f71a99f34d503cd6e28340a03d4358ee233cf870a5afc9673c5e214157c85287d9d4012621c51add3e6d8494305521049a81b9ab66ef9db20c6196256f113f0b3c3c6d03e3ace173f737e42b7513dd00f60303c129ec014c02f8f271121bdd353e7507ab11e3f4359d7ce9ad09e82f9b7f30daaff1804bc2387cc39a37cbb160b4de4ca74c1cc79a7710d307f217aecff2a35e96643449f7c7724924b640c03a7683861f07ed2a64054b9f76b01ad3c197d294bc705ea67f801cc6a5ca9ec61287b1ca62411178769eabd6251f71d0c5458377377bd5ebc5f99644d1a6c2260690f6b4473b044a1fde8792b313c0e88d40d07ef19afc2d8034372205a708fe98d67d290d953ca992a13f21729aec007678cb81b980457ec91d60b42a00825de54a0ee0114931734e9e192bdc1c8ec16b5807aba21fb2933e103671296a0191cdc5010dbe8eedf6fe3fb0d26fbbb88731fe17e3034f025d8d6e578d645a20d267478c88314e02633a1884f7b31f76db2f8dd6e2b80656223cdc40d76eb7e08ef22eef13176011f210569d2b5664180d78ec47da36abbd35c46066c8ac18781feeef1d4d095b586e75ded396eeb2a04f91a5deba1ed00d8054f9645216446d3c65d3c0a8132d7ba365b4954428ab3cf50fe7d224474eb719fb5973f19b7e5af81712734a4500edb27fab693b1dacb9e6e2b347c5023128f598e119c54f904af683f4f71b92ed75ac72a694d0d887d50b79ecbfa56a9dfc08e00598b269a61396091e9d84b7c323b9cc4f3d9d1327392f41aa90d9925b4d7d926b4aa698cca009f81d8091edcb32c205164e00fea45f08b0b11cd01135d29180395240bbd5c166fe6e728ea7e62030de8e3c5a36df766411e0da6e0001a2cdc5499f7b33db9f20907632755256d5bed0b723c9e1c6a10b3f173b3a4bf353e1c619ca668f3bef6ac7af2add7e28b3860478dcdf36a2f9c9a4567404d7215e010c21b3f2924ffc5e64bd257c9a50020dcd2d7fc1c64095bc2761abfb3ee9a8382d9a823acb9a4e0e20c38fc8a0ca503103bca21c3ae7a87a5a5bbb8a3b103830e8837500263ceca1b4c718f1cdec152c4e9ba71a313d33ec63c8d8ae1378a622f5fa4acc841dc1674dd805662f8f1da4100f7305818febbbe2dd012ee0dd69d4dfc17b2a190946e21565b93500577572904f262608b29b6e8d8f3cbdc182b48314c391d91c5ee6d36c463e161c60644e1087204853e985ac6030bac461c5992f7491f71ba22b9fde183e03af810d454140d28de6e4eec288fc93e16b8c792bd4c25d71409e70bab45e12c86a4b76b8f84dc574a9e605e146066e2501532985574f067475440b15f51b8060cea038e0fff133ca1f9860dc971d405c467aac64909176e1f131098cbee436e2f4bbe5db61772cfca58da77c982f5db212d11c0d658480e7171621528702de34f45350896b7dac40bb93ec117f7dff3a4330ba8992505d0871c1599254da6103ad6eab7621bec079ccf08897738a00552a1f3956a4c9a11094ee4ce9275edee57de26c430050c9e74d2277650212fc3945727213470ba6897a758fb0535c05dc10ff44022d93e916259f5cc3d288ba377d81487bd799c9038052bebf8ffb0e1d868d6096c365ea03efb385101a4fa58f01f5df0d731dc7828a0626e1bd3776f487e7c68eac0b009ac2c253417dafb51ba839c6b4fcd36a3da7c5de8a391edbbe610e8ed8a6cc3ca8df7e6ed4bc4523ecdfa864f847375f28075d87de475fbc022ffde2f86bdc9102de5e62da075dcafe421aa500fa17af4a829f74866f8d3604a1d1bf9a6d87775577436bcda06111313900e64807bee8bcb432e97496826e9f925bc8941f874b5dabad7202b6ef4211c72246cd69ed310d58184ac3b1da8b46775642578e90ec356b599b4b5364db21ab2aaefbe87839b65289f52a5cacb37a9389097efd74b103409558706bd511d479e32563b50fc7f15277e74cc0acbfd8e6b30a29721668ead318ccf3031ac0fd86077a487b45abb2b733bfa2acf9413097909d83ab536a053402886c4aff9f9b6592b8cb5a35886e85f5e13fafb4a87062e099c405f276944aabaa264891461b50765e8c78451071bbb673b53b1e0aded059e07e0f1f82a19afa2f1e321d2c0176a79be9ac23534f669600b461a9c37c44cca2a95f7575d80b27698b23ee7b1d28b9453c01149ee716e1f50e0cc951029b7b1f3563f747628a92fc63a1d503987627acc1b67f6cae85bb5fbda57b5f4e0382140903e07b9601c333f02ff649a30d6ed7877f2502fff50d9934f1d70674b44ce1daab342e622f9a907235c18d74c51e8495e7392e8082d6135dd9a50f7b971996a4ad7d223321c819af8a36ec6015bd817ba152acf476ca181fd880c726d0ec8f289fe7d09f2732d045bd4b820e5a1d7ec41048f960335298e6e1460a7b1563393e3366e8fe9ba9ee84284bcc3853f417b7348be081ee5cfdf246146d16815897356068e1da44b7b7e61137f115cf0780177e044c8323af325772b9c388e02db701c180d13fa95aef910776130242301472a42d8d41a6e79e231ac7d9ab6406383be0337ee53e8a920c66361801edf272334116aecb73e13d8db86039139841ef27ca60a1ce0c0f4447f0ed0ba03b680cd029e805f3180c7b29894fc6f3cf777f2a74497ebf61d8f38c560013c9bbbcfe8300316476007a62984e28ac2765b4a013f60689f7c2c7a8f56c024d69f8f61904dfc474bf29b5d83b15c4db09ee3ae5927d77e5f83c834bb08e65969d12a7416c87382e4f78b9c2e833d31ceff3bd7fff360643ad41533a7235fe4ece64f606b71ae4ff5aaea2c98de2e2356112c1ea4bf0e00ef28c8b2e61527bd73e90f7fbfb43e892c95a6020b5d5c4cc7cc31030ddd1c520e44b976ef97f6c38230c3025d6069100a3f3ab24feef332d147f114489ff0cdef79f8f67c784ae895004fb07b84604c809f0e0290a76887515c168be1a6d338930121a7ab850f05bcf841a4247c44562f732a847f7601c19119821308194dae28990578445419e68e4e9e029934a1f1e1985996c6ff141055922602803732681a9ca74dbc63aabc6da5a7e60a143811038148466efa5b277eeaa3f59db2c0bf51e489fbe8e3c762a229b325f2285bc783913417206d205a9cc62be0c2b06d01617ed0cb7bc24e16c0cb1704b87e2725a8423aa5088d64a2fc788aa242a81c5ff6550cfa071e45fb81338869fa3dacfb4292c487c0034607be2968cc70944d0dab796dadc836d21cf252de8c9a12cf4597ab69c5ee8f605af314c12182aae5a3471f1b0ae56af744503b591c343a555f9db9b1d6409f16d3bfa60d46b04d32a34ef3978cb7863e08fffeb0f0a64e108f66a8d8735c5e7068f6eee410e2dbe808de67b050d5d28f86008016ca608dac0020307e610c4660d600d26107861850146b6f1f41ce476d10e8191bf69ae62298716df4a806f44ec34ae377c7484203a7db3a88674df21946510833b932a94351b41194335d433144279b311983192a11d2364a5727e614b37080de935db7da85a7a5692ef09353afcfb18a9f289e18e7911c81baec0dcb09d4d2fd85c6d9b0a0e810dd4093eee447e703f5b6577be6e7f28253c8eed3973800fd813a60438fe56339af8139c259fffed399e7c421487f23d370c2ed6339efd03ca5dce50110e2b055ecedd705e8ddce237e796674bd08f51301748c143693692db52481db50d1a938a546411e8b4e9b410fef7d92f5d81c6651c1d65ef8f9111ee387963bf6f78a74f175eca81e8f0b1669a37e32bb44f59c0148bc5fc8e67b8274db33ba21142dbbc7d9cb1e4471ac8f1d6b89a659466f1296698d08230ebf9bb5c7e7933bc22e1e5305876e9be06d2c96b241804990cf4a1dfab1d3d3d93519583b1f485b229ac8ff986d9ebe259d066fa673f7884bdf68e117a43d87be659e03eaec1f40a650bbce05e305af28bb140e5df72c4d46ad1b5c70b1ed97a919013a99509599528d5c85a0a5a121b918d21f14ecae23578a89878f5ce48f2daccbf6eb803a4531dc5d0ecc466e129e4486c3544309370c296a68cf918ae92e1dcba0b7baf2858590cf4073b460cc63b5040576124b7208411113a87d0958200838bdc9ca66d1baed92b99c84ca8ac30c461d64b1041107a65494098f53c77cfb9af8d7d16733159adf68568764ed38fd239187a6877caf773dfa31d53326cc8b81f77a564b5f5cb801b16910fcdeeb908be1a9e206ed29bacad15d0e5642d3a1eade810ec661fb57b42816262890c10e211e3044172097683db61c273adb6ec46921dafa4d3d6252dd07bf808018624935d5869d57ae30ab66b9325792192aa2f6b10841e8caaf3ce0e2b7f87b334ae12e9e18f85b5e1eb88c16e59c1e2021bfcd788c7e5cc061e2789f9c659660336e57c93a395dd8cb696d778019fb04cbf50bd253e90f985c6ede2c77ea0db609f72a8436a0a880166200d2124ee58e5d2527c9b82570165143834ee865c519b913153ca3a304a67e3bab44b148f02520621f48850d911af1885c823e80c6de688ef26a1e6e9ba1dd130e502f28cb3d8f1f810ed65c3d88ed07b23315c4fd8fd4b1041564b0e72d2b0b57c9bfd73e73a3b68d57fdc166b0fd0ced85713a538a8ed861e68ea8768d17c50d9f426be3ac001026e06fa318818c803b613b0ebbe7168fc036c12d6a8bfe076868abc8ddc26b4722579c8522a3782b3cfdc7a23e1464eb425fa2d6ded34ee2dc8d1d06c07957656bbf7877511beba3d2baf482ea2aa9703b0ccc5461717f0d41d12c03e445ea415d2b0de21d827c7df49934ee8b49eba707ead10265501e2a57306abe7c20951fcd096a67112fded2a5bf20b968413ee3d357fc15ba0ce517ec50c37898b0a5af1e79c8283e94411a4358e634411a1ea8c7a2961e2d1f6258e1b403f62571dfe19da9edb8599fe04741e1879ac5b6de497ddd14d4d516491850e7dfda21b102c0ed3f3b7076b47ae5b370eaab339f43ecb41d0e294f3fd1b24ffc47a63a84b0403c9e218995aa35ab22880a397113beb2088c86ebbccc17cca86f8dd0997c6c2f91f680d48089d8ed3d9d4ce240be15540b802ea8040b481fba09452cf80d1d91e562d397e2cb3432ef57fe1beb4815f82b00bc6638b7d614ec78039368c1b63c9ba51b3c309e93565b9efc38b9ef3ba90157a6b29a4d43a2533b440e8fb4e518c28768c5b24ba57195b6f35580fb9f5d4810a9d15e7b9138062cb59bdf2d64a2e462c88cf877151b16682f4257d7e34617e55ae92327f1466330b26adce6165019195a64455c2589aac9039126c95a813438e5234c9890f43edf37a0233cd6edc2843b98e15863204453e03a6f5617643ae9b600fabf8199edc060ccebf8995046b38d31952a0e42827691d9d34c819045afea27bd17b374bf4b2ae42e96fb8082de730028219dc35e30994676ddc74af68a74b502cb6c2c9752446d3307f0f7cc2f67b0a58de038f445c9d13feec8d8fdd5ec0bfa2d8d8cb72209aa42a43ccf77820cc4b1b5b4cb1c407f11872f2c41533bda98f8acd5efe4cee63b3112629e6008bd004e0cc1b880c55057469a68b46a62d3b19e39ce412fe72af17a1fc52e625ae2ae8d302f45d3cfd0929b523d4bff75c24420903eeb8ec89d2b023ad60380887399ae3814e1e244b15a4eae6ec05c588286d8fd6d533ef3d528629a4eb7dd599b714f0c091d70c019507abc1da7772c3a0db8c02a34f668e49aa521b10ceca7aab674a332e9cb3c8fabc8e949f490b5f762e7e11a4e5b4a9eb0a65f1416566fd55b352d2998d5e903d7c9501bd60d47ff8de12b3a1ad956e6a162224624da5a994d694312c06a0763933f21c1d7677f0c4598c3811ea74a0c3429169eba5a1e16a1075cfb8ee822beebbeb515eeb0d5aabe0589c6da895e8a79d064724eb80a97c28af9a069316e14cc2e90dae878e3d77cd52c5285e5cf14f3ca2fa810ca21c5a4874de4537dc415175a2da9d45ada13d8d6502ae375887430d745654095214aaca9f28bb873a586b8d8998f8573bb56f90575114c41f56a086b3df2f007c6212f909b13426364f7cc73db2e045b3f110bac5dad69dde1056f2e578ec362e1fe8e430c982a93e5be43c2953f6869a483c7307ed8bcb2d86568d58acf963a0f279bf9d8445c050fc6f6b0f40e0ec8a5114d158d2594cc1835358788433831b7e76f3bebb7f1c42cf97ef8428570946160792c2784873a47cfc42aea1cc63758af78cee1132c5ec4f7a59c90c0f207bb22318763489570bc81047fd490fbc18a179845ef291bf28d87f0e58ce39fb2eefc8993ecdfc6f2c34258b520bae71bbd72973b415fab27f6a8237e6a69abfddb8b56ddc7d1a3fc076c8cd51b1db95dfd4d7d81dfb909009770be290d89a678e262e1728bf0d4d99a32496154118cb2c00094eea96f353cfc97159b5b0c6bcbaff00d33ee1246c9c6eb5529dd8b9e7939628d4b600f3444c2ee2da1b81e72bc4f577b9ac06b5d5045473b6ea157953d30ebdea71f84e0183586acfb57bf90030e3b94372b8a29e7a406d9890a2cf1675090898ba878a8da63455952978445f9da569f1ddf9d74840f816bff79218f1812708178b37bb9fe5537cd9572ae8606b9d8e66c0389c63ca317750a93f2c09f507420ffeb0951cfe06b331124e1d3f9f8f805e28e153d8f839affbeb900f26ee31e1f51880b008f5894f769227cde9bfa22b7a3935dcc668b015e918a710ca56e23b8b87f1a414369a40d7a8a28392543c170f62c734912017e540df4149dedd15ef87ec0ea7c24816168e301d7df7346d513af5d5a57e754f3d3c517732390b0b3783182a8170a3839a1ef83590d9a1770ed03f2f5f6de80c9293f54d746a979e3d482c718626a232e5af96d9894300f689801a0464072d37765fde3e43ee8043161ab2538985049f78e54169c80b7b12ecf81447b3fd852570bc043f40867632ac4598ae7dd5b4a4ee5cf2b6ca01017618142ab4e54e1f9a0a8f1d0b40f822fbd1e4cda4b673ddd488eb6a65467ff2712d58236839060b846f4b813e7588584ef46c2a57082b0bb41c18a03c55be8a49ec14c8842762a9875fb97e08bd358dc59c6298ed1baec17d62707459e6f185139dc50304a03e1a32fbcc73d2c5e8cded3af0f185d48edb1236c23b5f46a99a3b690b8afe08b12cae8d9129ba7c4f122372a3fad161a4cbea014ec7acba6ceb2a17821dd5dcb9816135ce5a367ca6ae25a9af5ab3d4d02abb0d0c1d426d5dd7f76f4f88a241eccf0064f68104c91de6e6d655e01c5e70801fde0af382992c9e93984647994e4d106352becc0a271f0af3447182587f72d3654570830fbce0eef0b006f83c1a6411f885e4b796a0fd081e9f9458867073b7df2dd955815c6bcd85469826b4346840a7513156596987bc16a067e133e27ff2c2260ffe6c12a4c08915f0abd8dc0cc6c245cb336506d3c73c69fc09df0420c18b4b45d552433c413cb3d4f0ad34f466a9b5b72c850ca3b05188be7245e17657ca5e330fdb3f8be85e38961814e7935102c466b41629bb0efa1ba371c6924d9ccb835d352e3b204ef74e8fb37295640c0f0c857558ea0fb3a5def73a2cd447de7bf989f9e4fec061e10308c69a05250c7c28185e768ecaf6898325e28626aef09f814de5cb974a188ec2303bcee87a8cd8d164f912820d92668f1c48889d741e84a8d6b0955475a1c904aa1b40d84875a9255eef74e103d7f438ae6da3775d62a8155ed200d4b24b181b279f08a29c911c53e43466d1d4aec37e142bb549c7ed0e0b6798bdcab1d29a4cc22af48cdb938b4651b1f68775e48cb51e1cb38f549d22c2dc5211e8532e0e630289c6f1783b3029b7f0b343d4d3ed6dd659cec02a9a7b179edb78e3a16443009b5ba851173707205050c6b3a55b7125a231535310df3da186e35336039c222ba7e0a6f01e30a37f9c50eb987ff6f3c670dc882311f47c7ed093689e4f6fc2ac64b5189706ec06d5390fcd213507cb2855d1ff19fd1ff32a59832261f9abe868de61e4d6a0f01e8290a6e5f43e16f0d664fd8cb2e999a0a4285602fe245cb6a2d57485307c0dfae22ba77875cb11aa1fbb8725b28e5a6150200a91560c2fe537e0e3afcc1178d1911f811741ce17a54409ccaf3366d48e492c9c62f1658b74069c31659b9eecbca9773a23e980091937f19417a70475a8c9773287bd0a27c7e4b7428b64bf7a52e219e4927bf6ea9811e3b7f56db5e8e2c2b8f6c8e43763bf4a77aab605c939327632250331e81eaeed5b1b3b12260e82f02dc5a066ca20c9e2e1c91436dcfc724557a5ded87ecd58ec192c647bcd657c1ec3f408e90c1fb46199ca90944ca603baf5f2925a40d815a703e6fa6af3d476d48acd493696812b04074a7133f589c833481cc09c8853bce21398a374769823f701da456305866128322ed4becc43538352ef0e1a70921e8918d1a1292f535596fc447665db7befbde59652a69464830880092a0994daaf7bf9dc4348af508ce7d40f3fc9a021bb62ad8a9d41e3ebab3cf634bcfa33ea8beacba81666b1a72fbbae721e8e2bd4ee3f900a273fdececa54dbc9fa335a21fd655648dfea44b332f5bbcec264a49c1a65d0c334f385179671af71b4aea8f6ec59cfa9af65be964acd1a380de4412f52ad368999dffddd889c50ab4099993f2dc8b2a66531f7fcdc755d1777434b7123bd6a29edc2a0bf3c50afba0be69662f56ad55db4143772b95796d55dcc97bf65dc5d58bde214b4e19e6d97b48935eea9fe82afb4532efd5eb513db529a445b8ac4aca4d77ff0abd07e5cf7fef2b54f7b197a68bfbaf218f62b74e5e187203c85a0fd4417c33ee4ad782a1e7ceb411ee83b0fc212663ff920eb31ce0a91b7675b990d3266bf7ec2b2bb98df5db00e6865b2a76f599afd68b2d952aea4b6bbe01b525e212b731afaee3faaf25fbd34e4ad782a5ee759f3fa41996dd88652b6d1bc3bfffac922dadedede24a6973eb52da5bbd0e17a6506322726e1385d84d9c6329126c9f9f16b1b25fb53fa306a7f7df4b4eb312cbee65dda430fc7152ab541b44b5f83d6c74bad4c6661bcac4c3f8500c5ec47d356c6aa9a8cd647cd7aedca58da1225121982c8104486b868e47c6bda1b3e1af9d092d27a1877efe20c5a6c13257bd03a6d314afb2d34a2d8c943f08df871cac83991c10cb6cb333bc06f88e6a3914ee45bf6868f864e27ed421fc67ddb117acb0bf9602664705e78e50e59e4d2e8dd8c31e50e697d3449feb4d4931f06913b800bab00c349183b61247133d7e49428c57f1a0a5f6ae7fb68979565274d9adb7fecc47afa6df3eb573fc5787f57a1d47eda3775d17f18cbd317791096a85d79150f366925f4b04f322c6c1695b760c85b51f995d76c10ed6aa05fc171858a59217e351b342b03b2f3baa4dc5e5ea96265b69f6f7d6f109856673f9aedbab54e2e6526f00dfa304d6b96411be8cf97defc1a520609dd6d07fdac3edc76c8a7bed5505ffbe9b17831543e6b8dc58bf170eb1ea6e59603cbc778b9eda8a5fa98479b547ff3bed0ad5feb6f9e566193543a0ffb181ecb8354bceb3366d8dcfc6c6a4ddabef36893b68fe155ef0bdded411e6f35eca89066a12ed5f826b548cf5ad26302df902f7fda48a3d2752734b59b6c753350a5945d898ccdbf6d366cdc30e3225bb54b3bdc3ecb387797d120c89de39ccbb8601d0e9adce4b2d56d7766cb6e65a4462d9a99b6786650a6ff0f972737e716ac633ecd9c705bf42d6e66ab6b7d7f5b9eddfc41b48c351b4b42805a2b7ce325a9f54febb9edcfb6f88474b6853c44bba8c3b35fc68c417fdb6c98a1294347c660b17286b61d54bbb919c7719cc7acf9fc4de53b27dbe2f65bd6833cfa303eb357f13eed761ebcb473b8392df6754ececa60cfcf56c6c5a67d37b771da6f9f31c76973623feb633fbd2fde6a47d7d3b76c5a7324d291700fa2dc6fff45f9c5fd82c02fee7c4b3ee7702f8368191719177cc37afa907ec605a5d92ae342c60b3a9e8248e4aceeeef99627b365dd96353339ad9f1f6522775a19ced8e6632232dab61f8336086057f2739b0ded22b9e5c321886cdb0ede3cd824097ad86534333fb0cd668530cecd3c194dd6cf24641bd6213fbbc9beb76a65b2d7567808d6b15529432bf6ebdb9dfde88d5bf61abfd69fb51d696ffdab48c936bd6d4498c8b6d9d03d5b19feedb5cefbf85a2b2cfb57b2cd0689c9b862257f776d3b2c9ee26699a679f06659d69b03b2bbd1cd06f9a09f1e7f7bf3ab673d8c4b6c08d631b72d88b6651be8e3b3d8f7527cee9b3399ebe567d733c7d9d16f51fea49bfdd8326d1f7fd37808f9d910ac03eeb0885ccc93974a78afebba2e6a591e826fccae5598b0245e2f2241d8c3cf34ed61bb68efd225f70971ca13dec93e7738e5c9ed3433d4a9573867affce68577da5ee7a8eb8f362f72fd3fbed57db47d756bf9cf393de77ed60b6999fe1fae6bd6537e6afdb3a65683d2f8affe7cfb79916bfde6d2e5d4988695f9aaa5747e6f561b55ffedd9fb5eb042fa6e4fdd8eea7baffc8675f84fdbeedfbff59f17b974be5b19ff9e74bea5749b5406c002c04e7feb5fb0fe345b72dd937373cfbddd6ffca7677d0d4ffb91c765bd6ad20cfb5191fd2a0b0c88ed8a9592d952ec4120a656c67e46d06ff7acfef6d657ab55ed37fbf1ad0dc29e822cf9524a49df7a8de96fffb593ab3de74d27d7fa49ed26a9a4d27e42e447af1bb1bdf6bd6a1778b577235807bcdac76d8756e4669e9c188661d845ad1bc137ac1a2c6ac20bcf843fe24cad660f3fdbb8dfac57ab4864f1b6d940e3b987ed62c16b7d8c3738dab6a3399bd1f82cbb9a9fb3387ece7a18b784f4e5c9d4f81f2e7b9f5f6bd47f597f3537b2300db71baefe84f427a4ef0b375cf6eb5be35fb05f5f50c4b9cd6c9ffbb8bb394a33f383f5f970fb2dfb4523f7a3cf7932d7ff709b41f1d2e7f8b924ee474137f053cb7e11e752cbfacff2e9d327c8ca345bd1a7cf79712bff89ee7b6993408d7ecb7efc0991a31acf3dcc0c918c7ee6defa588475f45b57e3b842151271aca7dcd3f03ebe341eb4d9f0c25fcf6d37681f5f2e4648a5ad7fcee2def3a0ff66d98fb519000b006b3ffa2fd88cc9cd40f2d698a06e90d720af6f3f3f8d19a25f91f12cffc522fdeed98f9e3762ad6dbc69126df93943248097613f514b15b1d84fe4167a296457ac8aed66bd32ae5e56267bfe84c8acb57804ebf8b8ff7af5f61312331a38a46cfda06f295588bc9b95e1fe7a0bc40fb21f5fced2ac354aadec6bb523ec414f4f3fbc345f2452594f9f468c97fb0b24e5e5ac07bd047acf03d94f48ecd5a52fc35d3fb81789b8e728db8f0334d7fadf3edeb40bbcdbc7235807bc1bced53c9965d9671976d97804dfa0d69a568653a3a6e1a40b4aee5f5805179e18b9a60babf842cae582cf2dc085556ca149156010a9020c2daa0023892aba00a58a2e2ca9a20b5f384b2e9c2f238cf8e0f6cf8ae45cb8bbbb732b21b6bbbbbbbb7323c1dddd9d1b09eeeeeecebd12b9bbbbf33b3717eeeeeefccead047777f70eb74d5507e640c0ed7ff98e40d478647e08189564243f66326a14df076b14ed485a1cf8e7c7c0b7de07fef93e24711b6359ea03d72daa49003aa8a60bbd902289284794c081157e509688fa996af8e1c3e7ef6e7a0a9a9324c75a725989ce4204b4f02270fc14014b4a9f485de287b243916841734cd6172f35d7fdad1e1ebac46ffddc1ad6136b752d9f3b25c536d1c7c7877d72487e6226ec139b7394f8d8c747ce1d7ec0b044cfceaa468b0c3fba675a34c06841b2280e531853b4cb57554b2c1d6be7d68025fe161970d42ecd004bfca625b4c9e54a71784062888d0aac6003a75c36d0466ce9a25a3aedf2718e9276611f7a615260d6641d68c37f08eb68cb5058967378065bb89e6393714e12f5631ff6f1f1c1ae8caa60d821414a082708418f1f4158f111b861a841bfc0e450f90c6a4f0f1f1e5bbe88120624e4bca91f5c7d2f2fd38aa1a6c2743de04a1514c6a07e704557abd58f1fabd5eac76ab5826108b547fe7c7f1f555eaa4e492aa5c5cd0544164c70050c47c000440c4bb4206921c28a01053b53a89658ef6fa59e647ab126c3a003beba1623a6504959b292d22ef1fd34db45f42493979cc6a08445e4ba4504e65041c8d0468c2d36a81f5c7d73ca14734abb444d5a31c54ebbc49fabd56afe989ef526ba22ad4c3f2aad8d82a18bfac1d55cd22e1f5431993b7e227989ffc95cf9dc1e93ea465405130c02b8c7c40dc312da5b82e0d48499620c49fc9bc42458a2a20288a4a806f583aa89d32e9f2989ee69a4e7133d95dc9629deffc55f7a242fa9309f2f78769adc1917f2ecacb08072736b5c8885145e7033d0ec3bea4183825fa35b487414ffb19fa1af7d0e10259a4f837a9397feced7fefa1c20eab2339ae9aba0ff4c120551da4fcc1bc5efe1a5d9430e10c5c3cec078697e46837aec6950d48e62b29495240372d476a4037dec63aedf6692b48da18fd9194651d9c3b2a31ed68f62b2b73e7beb19e5db8ef8bde10051d967d1cb1842764ea7b62a95baed849e18a46ee611c00af5631b22e603f193f6f379f013a3e6fff013e8e70ff113dfe0b04d93e6f554fbd8a6474d4df6dbd760cffd283ee869509aa77d4ca681ec0c67d9c64bf3b19fd932cccb3cd528bef6342826a3684738648f7d4cb431d86b7666ce4cc6f1d2645ba18df8978d6f59e93adc74061977a97ef63a40ef06ac30c048894a502fbca249915b73608e035cf9dfc34fd7941b16aaf6db5b991db91db9ff8c3f6767febdbe59e68de0733e23f8dcea423bc201fbec63b0cf6692401b933d676566f292e4b701de1e447f3db6799a07330f7aa318edb3d76c66316f149361965512be97e0530f7a7d2d6bf212ec52dd120699f559aa3fa8a2ef278c519857bd64fde559d6d7245478473d6afaf9bbcb77247f467bece9f53428df76f4a8a9c15eb335d767967ac9facbc33ceb2f3b929f3d0d6a6624ed5f2fffb2d76776c69ab11eb3d04b34281ad4d543d4a81927a8d586f492fc16cba3b8a00a42a4b080a20b224cf8a94b2f8625541ec58754342972bd63bc7478f00831e5f2e5af40bbf8659e9a944bca944aa58e407d104a4dbcc1c97a92409e21a4b835b08ebefcd1bd1c9e5879393ce1a449df9bdae563c883cb9e73f969601dfd382eb7e07e33d090529f592ebc22ca17f7833e5d4c0d92e07afb7ebb8cfa214f1057dc1eb515dd1a37f51341b1d959020abab04517a6a082a30981f41386810aee1cb4e900773e8cde0beb687b0178c3a7ede1a51cd7553d5aba50fd6189ffeaae8b1c429740bce4f34d9044219c167aa9513120513ff864aab4d9d37de940c4130223171ae1421e10f8e06620d812b56a85a6a7d12aea47efc582da5ba89f88a5889817e0a4e709502c9102155001359d0577f2000547b459798a80c253032baee8421e10c8200516c57902940f7cd1832941f80a7c408322361f4051247421cf9111cc0f98ba41265812c6dc410e082c8b074418913b6801938b9704713f19aefc2680800644ac5050021b78122e58e2ca2b5f241fc2344c07e0429e267470e3853c2e6041c442651d7c19708510520ce049e207d55edc02358ae9f7f7b6d54bd01d5edc8d225491faa19981e80ac391275081fa810508549208af8842e3600aa8e9cd349126523440fd008311a8247de37cebe77c4bcab7e6cbc93a5980c20e4a7a90083940496f4401f9d64b3be3508c80fa41a2e45b76c689d802158246c9e79f764401ebe75b7686b500052f8240fd40c140c99f7686a17044118e40fd000019a0a4a54165519bde2866529f9171051336a81f3a2da0e6d3a0a4378a910f0014a0a4ad5eeaa7415d4e2a6dd20c0e1c2b5452503fc415f84009d47ce9cd4055103740fd30830c50d3b67402784a8610dad8220445c841498f85f841b5b477ac4106797e2ecf12bf1c024725a9a91a2052281e937bd2cfa81dd9002fb538b25029cdb5e8b4c1a2b9d132139c46326b6155eadcfb4f96651977c34f4e663f9a9b3d895060fc6917af9f79358a7bfcb13c3e8950e2931825fec89cebf18965c5287eeae80aed9f4be94b1e5e9a1fa358f189e4e12720263f857e6a136427953551a6fc8b9ec297ad942b3f3e6917286ce34ffce1ece7a27e6279b61ffc4a41937f86877de8077b168b81ec27ba73cb3219d9f6343c084b99c8db3ec66641f6ab37b3fc218f7a29fb191ef4cc7e41443588e842ee41366bfbd653ff78a195e1ace80b22bada67f46314fa310aebd8ac8cf6525a9a9571a73646e11bcc57e7cd98f1fc8d9af17e8ac1a2eb697833bcf792976478d04b313ce8a5d82ef4278b257989feb434e435eac64f2bf6d9c64fddcf07325f54ddf28643a33aea31f37fa4eb1f5fae7b4ef6de759ec94b4d7af272e7b3177f6214c668b85ee102a55f102d0ace7e33977be9ed44d12b3b51701cb783935da712afe9aae0b82af60ba27551ecf093ae834a60143b2bdf792bdcc3287638a80446b1c34f20c74531a19226773e54321f2a990f952899e2ce874a58079cf53fdeb9df56776488850b71fca97c4729e8a9f69cf77262b1ddf6335dcd7e4134f6561e7a38ae502f1b44bbd78afd4cd7faec3b0fc715aaca7f1b779795e1aecc7ea6fb55dbd92f7214ac43bb2c47016a2e0ade8942621953a6944ac9492a25e76d006a94f3f83abca2e01bf2ab7784e541de914cf3b2bf382f8657bd147a7f6843f6dac7f042cf7d1653593cfad99c730aa196f5d4db00b4e148e88fb03c6dd2fc90c7692c5ee82def0a794c699334ad86ec35edd9ab306a355cbef2ef44b11345d72a3ce83a8ed175b4ebaaca579597af626f58a2f2d5ab95d2fa942e89f12a5e0cfbd1dcaed6e76d47ed2aa595769d1b7182590939b9f2dd08dabd9f64d890fdeaaf88ec57af0cfb7d0e9548a211cfe924a4f422d9e745522a34b693d2a96e7ea4112430c70f3f855e3e101efc04eafee577c6e28d78c51b717f4cdb99246c6342cfd1a04425a86cb392ff03e6a0573e103f712b904a4e710ce56f9707b4215f003da8920446bda6c57dfd9a7e508c5ff99818bf6247d8af3c0d2a46c6b358ff958ff15fb13349b09fa9ef8fd998d0affc8a9d017de86950429a24e58c7c19de0f2fc98fe1491602da909d8a4616b56382d96e095cf167216f847d08b3231cb8f7a7407fc85a5406bbc37ea47da7c2bc505f0dfb217e12fd685244912b3f8890764925418471a408501fd7b80e13f7e31b2da7891139b81f2771e56b19d6a9da053631a2c895df2d6917778d07cc91bd7c07c0d305ae2c6a21cf87900f2cbff270db31c2c19f7b1d422c5e929890ed5ff17c709bd91fd0867c22542f5d96963490477af1eaa13a0fa2aa0751dbc3340ab579955695146dd00a8a9f1bad385f9a14e9fc4f662f233ff999cda426e56b518b5ad464165fb35ff6da6799261fbba4a67d26b14bdae8c9305f82d4db1fbf879ffc51507768971ff181b4cbf741e44b29a5a4604419653ca2feccbeb9f24b3da8d64f39ab3f7c13cc111ffe01e00cdac3ca8198401b92d62a12f5443f45cbaa264914f7f889d25a45224ef5f45c0a737cdcadae6420c0124471bff635d96fef1ea3781be1d03de8755079eedd8328274d92bc6228f255e47712e505b421396f149f7b1a54a79a34c2a11ff431fda019edfbe3f6d9d3c8a2b68ded9a26499215eac73a57ca672670067ff9ac5a92d91eacf34f226db54ade09899ad4036d481a5ad48f53cf5cd32e9289f04d9330cc46a83529663d442229457e82289fd5939f2ba75ce922f768fca05ab63649fedca2faf7cd6c8c093494a8d08a423e9ad0c0489326aae8eeeeeeeed8dedd111b42dd2ee4318273bfd7e213f5a3371221832d9fa92b8944a29714ff23f995af44652354e726b44b435294e2078a274eac94f0a1355a3461e40a2480a81c9fc01c2b1732f1137d4aef0fccd12fad972f8310ab309db0e2caf722e0c9eaf1121398835fbedf84a19a22c50f144f9cb8d25a2961f53812202f8270e591fb7140b8f2bfe904d7036dc89f1e6412061553a4f881e289132b57c2a70695db163425062758dc568f9f38246049be123e4eaca07822c58f5331e54a8e491857dadc2fbbb1e9eeb41c0eb4213f00d9cd95cf5e076db0ca757658a10dfe3470b3fac3bd03d4e34d85a861c4e3dbdd40bcc0c924c928afeeeeeea69436ed8eeddd1d4d32947850fbdddd71f03411024aa314a265ecf93d5fa6bbbbbbbb637b774f1957fcdddc42a89f36d3f3c127463cb6d7788430c6c8d3e31b396b287081081318155a32e92f1b7e2f17da8f5e7e1869e0e127ecaa9d00f9596b34b40bbcf179bba15eb7523e7653fb25e06184d14192fb4b991b5fde1053a90edef087fe32c88d2f67bf6f0ee8d4dd80f7a0f265e0c6971de80a35e0f5c6f80db9dd859a34f21eb9ada1afc77f3f7137f2891a9defe54a2aa85072a50c57ea5041450fe47b6c9e2df8c116bef881155eb0048f912646e04b9e28b24488235c26aec4855f03e6809787656526a8fc96c5a35d2cdbb45ad2e3e1d100d3a4fe71f9dbb708d0c0b48ba36450d1e0f2e1b83cf899d8c2fd44b73f24aa0c620a8783bfb181e9cb8bddfc317e16e163188659f8d01fb3d404f52f8440006efc884514b43c5e9a444393fcdf7fc88cf511d3243b848e28a842da85877a87ccc48f3c466870e3092a17196c9194fe29c2484393a2298b3a5ffe679a2ffb047374a02600eab03e3e0e9843fbf82fed927dfc19da65fbf83034f0f013e8e3e661cff1a25edf962154fa9fa81ff3b6cf3cbe2d43a820be229863bb91468ba3499ffd670a03e4d5803e8ba6207e10314a41f511d3a462361e7165b8df9e7ad8859b0338db83b65d376bad650875de4f742377798b8fe3c59b0186068f8797620f42ea3d411bf113006fc41f9da04e5bafa834eb094416655b625b625b625b228b9245096d4c6c4cc0964f259543a3b4875085c6852f2a41ed72600e035c7f56029e2a8cf62349f150b0257151f9ad8ef97e4e04b547115e99bcc4d785662f62e23e8c32f9a94767ecc4f5ffb6132b216f7bfc9ad728938b44d4d61f54ccce24997981044d14c949a1b49c24fcdef78b3bbc37f9a9474fed113544f9d65fe4d8dc91ed7ba300a3363bf3034ab38cc44b2bee611d2ff933e9e9a18cc3459a448241fde00f0f94d6ea3f9d8a6fd991a52aa90c046c45e5cfe07f327460ffb15839e87fa12bab0982c340651f028ad26c4428ed5f58076679d0c03aa48d814bdc3e00dae8170f43691d03b4d1ffa95cd10a94015d2cdac237fa631603cc215ffb18bed1af591efe9ef7657babc1b211a1b2d73c1e7e02c225d6c129be4129104490f36d9035b8669100dae8cfae08f9baaebe2ec738eca325026df427711b4703f2f10aee656d3c005da26522dc8228dc189454be362bb8fd161227b80d05a481409c5970fb9b858090491fbb808bb8cd3a6c9331a9d047bbf0bd15e81389244397fae391eca17f66a9bbad52d48fdeeb04aec33dac45c6b06fdc764c6d66717b60d8376e6eaf2a788e1b6997691d4993fa9d7ba0a8ecc35a74cee5772d7a246cee884501465976e60714fb78a99fdd91b89126b9751dd9a4e7f206befec01c06b8fc3cc01c2c12823f00bd80cc384cf7257a3b006912333feb0cf1937cfe18c965d28bd89abcc4cf6c599e42786812d35c12bf48c4577a6c624a9945fc4c62a9d324bef24a3b845f7034899fc44485ff99ba9e9945a7b35df8196c543f0f300730c0ed375971fba9b8fd0a70130e24353eefe86e3a24469ac489a07e5d4e87a4cbe967af2be275383cb4cb17e793fa0547934cfed084a3493128a9b3ff0989f426474dfbd2e58c92d40e4993fadd76469ad44f62a2f27f5dced5a1465ce0022eb88007874909a65c88850b79487073797856f3b3f9b08ba981bc108b2d88b856a4f3392b2cfbc11e2d52518dd6485aa89fe9c29e7a42e4b5469a27f246daf3d56c8d2154d785581c8109f79a158a0a037fec5f2c91c87aebb18fdb0eeb337f4b88bc8ea35db09fdf98471f6eee26dffcbbb37e745191755dd6c7b44bdff93efc04b9f0331f86870550eefc08b48bdff93fda05f2b4a0e6ce0f32c404a8be69010a261051ee77514bf4d3053a71e13b5f86989d9e4d02d0c90dad82460ae99c735ea31aacbffe7a28332a6ac89b5e9552cade26002fcf97dea88617b54b4f2de49ea88bea3146da764acc06e88df8e1a88aa0caea6e6e7f660aee5e658cd1a7b47c69bb44cbeeee30423a44d6cf573783e3128eeeccccf0074be98ee4892825e3e83ee7f42c70fdf98817441b408eee178da219f4a2d606010b7837ca77d8c1103c59df967dafd112a537c739e9e600f9fe961646d47ae617d5b0d124117aac22572c42ce5c01f5223c91b489edbaae2af259c52a03edbaaeebe165290f9f1c2fc193057370afebfab75f9016ca4398e2252832410d898c506535a2970f7017b1aeeba256088caa72214f8ac94a166abd9027758415302ae8429e54147e50b70b79525e34c0881d2fc13be7b790786a8e28e2891372ba37e619a72b8bd12dcb7abac5eb611a0e8007d2a75dacef0fc7f51742effcfe1a7dca6cbc14126532b44d536b654f6fb0c66dcc9ba8243940d4f5d3eb003572a5139b88798eca6cfc94cdec066defb6741b2d8536aecbe660d992cacd3128a97e21fb10f83384bd1d80f490bd107a7bb850974d77c338f1035d0ae63000ccd1d71989eb5cc4f517a26d6a13a8c29185fa9cea4ff5a7fa5ff5aa57c5a0a4466e77ee88f6bd51a051da478f5598c779acf3017823889ca79a2531a1ea56b40752ca28230ea0b86fda472fdb764c7a65da74e95a865dd49a5182a165d845ad1965bb0e645721e3a8dd4dbe7bdab6c3a2cc9031ae8644970cf029adf196d7429261ab41bbd1d21c54adb6cbe6dc327ac724b20fbaf76d0f829c884caba9fc5fe4231c6fad791631794d4afd6393e8bc2416a1b66931c6d8eef123df1823e70eba82c864f4660d88cadf5ff422383b1ff10267e7235e00722268299782dba39c16bdfc890bcbb48d63674752dd9fe04015d48e840c0c30c000038cf61c7020a8651646af8b6299a5d18f6a1a6c972d720e0a5551e3471b372455eb60bb80b84dcb402adddbb0e4e851b69c3c2ded04411736e98b79e167d39ad81585ece5fac6be5ffaf869652189d4243923656812a5db11510cea47224999336502219530821754c0134d4d51d118fc502450a0394ba03846a03740a0292b765eaa131308d0078c004a319912832a2059028e6aa722f94c5765c241537ee2ac8025f9403002bd5902c58102cda902453285c640b5130016d4cf7455261ca697faf30243038f08ecc0838f2da45cf91e49325c503c2142df0f128102acc308126e7c7e4cb5d6fa82c3c781681113c407884f8d8f8f950b799a38023e810826373378f1328317476eb018c20d1657c0e066207ed8f2b7422c8cb0733fa8b2d74e853518be04800bf5ab17bae0feed87a9542a9583822f03cc01bffaf76bb9f0a36c62684db0047b3e2082cb76a77e14c62338fc61827187eefe3f1c4208e13bacb0c20a2b74a7f2c70edd51556bdcddd4d313c4ddbdbdbd2d9c0cb6f003b861ace7a7168659561661e699679e31866517e52cc3f8a2cc568471772458064be033a9e52d4bfaef7bac7ff49e5aa791d218194266869099b904f7333d71e4b328b8d584139cd0f6524de8016a11eb6751607a88288d3624a20ebf8ae2bb156b7c18af8f1fe347514854433e2ccb8386989eea4d9ac0a777a58232c6478f5b9fd107a594fa88e9d124f7e117ed1103ad081d428c0a13080308a2c26778858cd2c626511958795cf9f0b52eaa3f0cf272a53b3c414beb9d1eadad6248a9d7a4f2c3239a6647321a4ad4eee1165b1bebdb7a997eb699cccffeff90fe6553c3a97669b73e78d010d3a4940d901d7e6a5c4e71cd0f910f4e619b1f1ffafad5fe1bab67172bd20b33333fc6cdcccccc922333c3740cb31126cccc3d311560d2e3c50713263ce2085613062968b2a2595c219585156c322baa3f0c3cf580a5f89449a5a61713c7faf0648acf2b8e429a14e1cf90214d8adfcfcf43bbd01b7f88901d7c7bf030818518656ca246164419148931c618638c33ca1863af6a4d77f76a8776e90112df638c31461e234f5c9dfad196510c2a6d42ede71d70e4cd54eba2c2171ecd76070f48937a2503ff07101a38aef031bb0c036917b64d6915c192056407e8fd80ef42dffab103ec013343049ad4ff3bf8fc85f02184107633b91ff4f9d12eddb08644752621d571ed2b211d242a954ac52a24acf213a5b58a4452b2aaa7078853f9d0ebc0f22bcf69d1c35bf08a555e8242e78180515c28c43fa18ff10fd5ff151a59549ec2525825bf913cd12e11e638c2d9e080fe26a74693629402e8419daca36d7a7ac01cdb95f4e1caeb10fa153be267791a940e2bcff231fe2c762609b38de96779163b0382e91e360001befc394014cbf7b0837e0f16202e69f45c03d8a2f6c0019e30f0a40703026dc8ffcc593e26f42c76443fc6d3a062567e44ed0807968ff149b49f61d13e067d9a4d9b19a2795cb3ad78407e421e44ad14016dc8d758c54c78097c72d1a77f65231c563ef4312b1f0afd8abd6c0ccbc7b0332bcf426df47c584942df87d07be8fd6342d69fda98957eef4d304e7a61e29c88a3577348600d6d2096e67c07a4d3d3d3d3d303ead10179cc03ad5d0ffce97ae00f48c6d7b07c8cdf40efaf437dee37272b283fd5eb7a403e9b17d086dcdc7fb8cb6079cecec47896a7914505b243a773c11c4736968ef12a2ffd25e3d426c51f24d90f0f30c7bcf287b0bc54652e653c5bce730ffad0e0130feca2df5cd40faeba1e6f03d0867c1e7eea814bac03f28d8f81805bb8f22fb53c401bf207e0a47e5dcf950fbd848d70e0429ff106fad057ee63405c65f1a027c30b7990c58328191e44753d7e02d9117f773e7ee22c08549ffb994e42144a877e7f1d583ea4d5a488dcb0783cf41424a00d497b7c3a2d9a84fdd7f5b8d6241cfafd63fabd6d8c7fc8ce447a33aeaa80ba158e2ad12a1c26f831c2780987a765d8949f2675aa53a94efd74ca4f94d62a12fdebe0300ee3f0f6351a63af437d2c2bd2379d44e774f5ba9d8036e4633afed8c7f863dbbbf6999d01bdb6a3a453f2f9a75da44c81398eac407688f622f6619b1e91fc18c2c91807e6d846fcda882de869f8ad4ef19cfccc847576788a253d5b3cd16205e5ca9f1677aa6b463864ef9f841f6467409f3d6fbf79206ff28e9fb49f367bcdc33e631cd6d13657fe16e33320b723fa30463bb23d44690fd33a9be63d0d2c2aefb0920128a10aa00775de8f7104d0836a5dd65e077f4d07ed9734899b34a98775bc8d006dc8a7b453910ef9b9e1e12529f9a66130989726c9c7a132a964572ae44b5869e57a0b39cb0a922da75375aa4ed5a9b64e95b3798cea547ea2b45691e8bf534114f4af5f137ad0f70887ed575e87ee59be3b2dba9e6e8b2675aa8ec9951d14a00df92060d4f6f1674a0ca18aa8540c5895490c15a211080000003315002030100c088542d16834cef34cf30114000f87a250684e15c8c324c85118658c318400000000000018003344844d001c06bdb14274db1c07489a5bb307b00ce9fb24a2392b2aa3f1477e452030c00ca57288e3982ace1cf6306b9c76f03e3f7c19403418d6074372274b96f820e7943066cb51e3c72022b287638a4e76564984baed0527e47408e779ea9bfcb020849342da3c3392bd745dcee565382a2acf9e29d92668d863abcf6260cc48d1bae6214da402b9abac80c83584aae540c9eae7448d00bae5cb8d12bbdec245e0ad3cb44e6c9742012667e05aad89052074075481b0c12ac6a44be1d6e16b7714bb34868e11fd53a7e9a64e5a4fe767b819c20383c00c1170edc0a913356e11c0dacd32fe015cf301b3a73beb4317fe0eb47a90d060586de4eda2bb53906b1c8ea5efee03ceba9d2a8046666a659d26642c2616497e3fbf7e2bf9d0dba96d46b4d0f8cfd51cfc0a10a6ca795ec31270acd59f4fe322ec42c0537fc67089dc30ae83d33a81c9f4f518aabf368084172d9c5e2a770b6f81d4f3e3eff02f381146e907d3870ae004514429de22ad5bdceb65451d5d91beb9b50375ea7082866d5edd2334f45c5fa2e2d428fe5f1e3eb90686739440d212a39fc5d91ad6da67d55d008a6d075377a2818a56e7ea9ad222d5d216a960955892b585ab798bfff6fde969e6663886e90b2d927d44587dd0ecce8003c59c247705cb55fecaadcec022d8be52dc8c92759adb128f37a3986ade2340b7fd44f0bcc4ffe7c0335ebf83c419d5e5e740cfcc69121caa6b2dc1bb914a5c723a3355916b71847c628b7758a4150b69f192c4f33e9475a5f741ca99cfbbca88fdf5445a675a9c6192a311905b16cb374a5695b0af5cbaae752dab170bf56203bb2d7768cc958eadddf8544552acfd6cbc4f6b555b0369dd0f89cf8a6da4b2f083ca133d03cfcb1ad112a61410361086096eece32eb1c2edd214c5343ebdf6d5851c40c976e2d933dbae0bba772fd96b8c2ffd2efdc0d5d0e3d768f93081ce944e8f1a8c98bed6d0466505fc73325c6bb6e8d85deadbc417557ca2aa80180da6b88a96471dbaac777ae0beb3c82134b0a829ed4e81e61a61548231e068a8d3889bba94cd7a824a8fd89f2f4e91777a0243c83137c6755c4e35ab24e4e35de28c432a74c18564fa7bf22215427a040a02dba9881e7c19cae0c511a9be65485526f70bd5953b995931876a7269b4e8d50d5e8ac32d0bf15478538c011b85abdcbcd7a5a6c084598a4800d4c0182d38a93ce1190b140966d37907d76e56ca2d620fab846156c827ee0868e0bba3f1e29bfbf481ac4bf78c55aa56370fe1ef266ba0b79f86d73875fcb68df18e5c3eb3f295762b582363850c0c8bf26e044b90536184a10df3aca7d272e876770ca319f4c9e09d52c9ec3710916d7447d7e9a8471ffce734a7563f5b9b3d375a5c839683929ed7ea5f6cb6cee97a33adae543c29c705674df4b8e2aac96b2ffbc9e3e9e65f5041364b7f0a9a0cfa6ca5ab9e1b49d8e62f272f384108765807384ad9ef113cbdfdce191bf74a8d64cc907bbe13850ac79678cb54d72571c449dab78a2719ff8c1e609291dbd420cf10f4a6c554528f207ed084d3eddc918de42a4a17b982cc48c2944b021bb78d1ecab2499e5347ab26f10b67b761f5d45cd8f7ae21edf651e30cf6dd624baa70b65f02523d1d2df596176de69457d32ec4f0b50b92b6ab1906e0cff9a19b58281f558a1ae34640bd1e261722e219a7c744ae036bf822cd6c5c15e3696a40309e3497e3daad7f2d7d4f86f4a45cb7956b8c3f8922bcaf6711dd8e8705a2b16f9213419bd7d80bf728a64cfde6b9a98fe1dafbdac1760a875b9f0cf1558813aa242fcbfc613a1a23fe8a55fac08c94b1eddf7d5fcb1c73d7f17b1f373fb0d2f6ede5b7fadf287079b82161eea238ea829785cb0d73ff69fb619f920ecbf5c68c6ff45b3fe83cd83bacb1acb5a214f6535658890715f1121045133054196151521bce40de21de56d2d309f57c4ca1ae813e0231cb1a15c405cf93de769e08037c2f18d88162c3449146e1e877602c9e8d822bf5511555c89eca61d5564bfed2c250cc4cf3613d3801d003042944462f938c0b1905896ed12ea3432182b465f15a076514b9e04fbb41f2b561798e87d8d9a39435f749d4359726e756ba7bc9611ddb241ecb94d2f40e6ec209ec7c5a5cf504f422908e247d29d01b24587a0f68ff55254906ae29796607b5ddf1915c6a1dc456c535287bc2ba9621693ef22868897f4705d25c8e276f991bffd8dc2680c32f01fcf4708d196abb1f5b0d604221301a134141b01b0679b8d0348432712e397bde5f183d808666d254be06280583d500ce4688bbb10f2957da02cfb3c75d27e8ec935284f291ba386f5ff5cede3a7e4a89cdb284ae07a228659be44166050e4a5ec5bc28fb74f11b885048da76594347c12527c3c144cfd9760ebf8d77ae0d59fadaf7fbfa109340bd2fb8a604cbcf8925782b0234de40b13bc22b7980369c4939495027fd8f1da20bad1cd8ac32e4431f1892227c4b6cc9143feda425c0f6067e5886c2193479a3258212d3a366d6d7db17ac464a0f4a2e1394041920543ac9873643fb5bbc05d308308ff3a80582470274d13d1110ccbf46a38e133d359a1be5a9c0174aaf1b18359cd0b686740cc41f10c5f1cd91f7d5db8dee68e34ac29834b18679edb34ad68aadde2ca44228ce805c1f3de1f0d7a957dcf55963e70f8205146d335752484dede162b7e492302afae4779c0c4a5b0e94a428eb474ca9b02d2d78fbfceef7fdbf2b19ad8321d38c7f234f942a7c3c0d3f27f62e29baebe11582b77102c3cdafb4f21ef22884b023538c39b2f9f50a6898803aa83208d8de00f3fff7c741de0a429b8062b0608ce38d863a800d4439b2cadd8f26680de940d75c01f6e7688009dd1f99e4b273413ec964b6623302aec71aa1ee790ef3fda50a3c35cc95cab59f873974bb3a70642f813ab47e7d9201d54087c52003820aeba9b1a7aed13d3bbf20a2ad02acec93b2685c5fa73d7cb1291e19d4bc826443219a2f6e412377f8d5aafa5476860499c28a6fadf13372587381d9542b360590ec5847451c3a766af300293ecc1f40e3dc6206a6e6989947aa5612f327a1200780dd807a814d03b2981ae522a6f561100ab6e028a6b8040a00b13e2802e32d8213f827b02340228f9788a74081c858c3f4c42ad665793f81492844b9ebf28a6962556d7fc58c44a573ac5d46b8f3441bbc5a3b309f00d294798160028e32d2c9266ed3cbb84bb98467aac6fc5a1efef8b083e6d41c702fcf5c37d13f050eef77f10e2941e09b60e15004f1ce744ea83180db39ce59bb472df3e0c1ffc55a5e3010f48d001875250ccc097bc4ac72096795d98e091ae48d30e819807e99c7b2cd7dfe37842c9e250bf2a0684148a74c4cddfb078695c6e8e50bc45256d959da52e9f4b17be1471837abbc2b71d124ae2036f26fe31f5575fc48af7763b0302a310009bfeb9c47adfb7f51f55787a9e98bfca4786707ee4d20df6d2901dfa1890fc634693f02da87cc08aa9d7c534b0f0decfee0c024fcb1cec2ed146c0dcc294292dea21baab8388aee03e9f46caed2039483cb3740620a6351e0e10045cf8d517214cb5fa2a5097e3f6799f214416aa5139bbe22bcd96c4f676d07140837bb57b6cac41598ed46e33c12fddf1f942668f5199e0913ec4de8d36e9bc517512c9f5532ec621fa50c21fea2ed23c5f4375287d5600c4c10366c60c26444f7e172e3c1d3bfca828325687bf670dec46ddc5d5bd72e06262ab3139cf9074a3ec2159fd8ddb4a5046295ce34cb300cebbd92352cdd792a4976d359403bfb66146337fe66dd814c833836e12545e3841e2800582249384347d223f4cbdc5bdcb59b11178ffc6b97aa6deffa5b48e9c9ba398daebcdf725bfe602b358fb9bfe8f3b7f5c85f7f8c9e08650bcffc80264fcac5ec3471c550476e40f3b41f94233d4f99e3b99843b5705373e76e868d3b4d03f2c8437b103200d34dc3b716ceca98c2317d17a8642132a0d2f233c94d8bedc874381b7f3ce2ed5dad6cc3ea59f8912cb8a9e6ecc9810e0080331651b58a7b0112de8794146fc1d9d28761cc7f52b3ba7f856e49839b832945a3816634f62440d9faf80872bb144b746970384bdd6d25a30f7e9c8a64933a0c93339b585d4ef2d517f08593b90d840ec4c7cec5ce8db4525571e0bb77c1022de6a398dc99ce7fc46500c626b683deab277f0a98e40f0e3aa92205a4cd01a5adea897d8128602cf0288ea576dd5b7f11d781fddfe9e552e4c5496d72f2d343d741548fba06aa96106370be2b1f8f6cbf1957a20745be51fc753a4c35bcd2cefb451fe730329fe37fcb0f67bd8ec97e5e777121b1c1ae17e3a1593232cf89078689c7486dcce1f2849d29e6884e6ab1ec750961c41815dc64491b14200ffba3a1f75f7e3705c60f90ce78a41a443acac847faf8f342a68860ff5c3f8a5e614656a987aa46278b136d847d3554bd72bc1ef364a12f63a067e8a7c77be10fd3f410ad54844ef077171427baad00c44274cae66fb4fcb27688246eafcca62244f4ef832bbf94769a10b372e7a4009257cc27917729f5d7a742cb28fb6d4272c15346f57a0b657e23a08bbca6c451f3ab09f23629fd6a8e71b3c6c4cabe4f028dc948f31fdec4834147b35fe965961cc79969b939a570fcc1e34868d30dbf2ad37503ddd5df1f967ea50a7ec574f918aff6997911a4fe40fd5f9d0a2fc62bb1eade61b2aaa84bd96bf5835e11977d5e5300feee0f79d2b22ea12b476e34e15a97687ededcc8a3fca578b3a8d4eb8f304a41586a9d5cffa95b2d9b54a7fc4e1bb2bf710853914c3e311d05d79871933945781da62c70525e83e32ad233b48ae7aba30a6526896b89471333c47b05826d3a143b380d7ca6e36826cb94d4f933a7e2be28e39c29df73188bb14b079e147496335d03ae47c1c700f76a60fc8647f49e0672a19282d14edfb6f0e921f9e2de7f148ef032bfb70aa0621a6aa637fddb3d9c04c24a75a3943e2e01ef08b544fd1a672466538162336dd17f2c54694017654d9b9d3e6912692311b48e4d2de93ec2202a049e49df637dd296d59057fd6c36533d3e648917cb1db15210066c668dd45123c87daf4d3251595bf72b010873c7a7183cf725f8d311650e075a8ed099fa4f938497c383d71179973c9edff816891758c874eb782887556f874884b4f6ece3f85e28aababff650911653e9dde3a8ff59011905b44848b2366ca294e28d96ff3d58f7ad9495262f7ede2aa28d13d8fae0695eee2c197e838be0762b796db77717f3d9f8971a95e02ef36c40201bc36797d3540e5ef1219b233ddac14adae5475355e81fb4d0dcdbbfb2ef1061b8994dfdd05404a89941461c1836d4f57b016329a55e8d5717d1987c0d24ccc52abdeec04761b5e2d190ccd811ca40f6ec56b87060d13cb8942db856fbfe71527b9a050cacec34ccfb20304502f867997c049e24e6c042d48d6a7251dc96252a02bf160e2a9ebdfed8517f4fa8822656c04bb9dc1dee9f8e0fb0a396f6a58847ff6f05216f779b04e487b6fd3eaf2337cc4fb3700fe728571317491d158cf992bab9a58b0862f23859a803c8b341a3f11b11bb7f849f232908b35a3fb493a8cff721f04451a6c025a71f917780006ea0e39a79ab45a268382c0b687a0e674dd946e05cf6616e628215fb3e590d5883c1d5003d378fddaa8f4cef6b5aeba4fb7648c335fb4541c01065d3b4101ca68429cfe19cf7c7c89eac4ae7296b0ba6c5bf6aad4a5a36ccb06de74525a90bd3a7c91a45bcf11fba84ab1fd1690b08fd997e492481d016f0542f4f219c79545775a07e715041f0461d3171d3dca274a2f03f38cb95670d8a5214f02f058805f2e9a52a3556d8524381b6235877861ea0b7d1545130445de6fd4895920ad92550603f29a1da80680136c79398d5809e12b256b4185482564c4106f82070bc22709368a375475bb963f95f85020f1ed41020759f87902c5c472ab19d1754c6ca72b0bcb35eac37736f1599f3fecf8dcc7c63390f9f46254834ab376883016ea382debf93aaa2f7c521bdffb583db9250fab6a9bd1838c608bba271008066172b67b474b5d70d7ef82a28edcb58e55055654772ea64d00c75f1e38856a70c23ba094b8eb41ec2a1552aa888b44555e9f5be033a3b2891e83782ab1d246bcc0b89377f78a4c561f22aa51180a3d6d682dfc289812dafaa5363c0198588703dc460b1e1be552904573c88a80736ef33cf9a868e2d3b962b202ee220b6b080f1b363fbf4a3a6f3831321e7dfda18892ade529578f16aae54048761200c42fb21476859e322e7cae0b811545bf6db9005021285f083421622cccee6abfc7570475544c3c24eb84f594cbe8e24c4049b3382198cf15eefc638beb0469ae1d379043313c591d3fcbc944ec543925d3c5c7bb8f3ce08b011667c211ccdf3b6575808847c487cb02c9ee9871792cbcf1641647d2f5eee3554576dec717cadf70b66afb0b5e0a1d870029f0559f33b2e2ca73f8843e13f6e0c0a335abfd28d52c3c096874aefb050eae55de739e1b7b23f3d32e3b51a1f2cf508a4392a66a7053ab1c97c90c1e29fdf132c1249a1f39ea1151e0a529514aa91333710a6a4f9b1bc47fa12029123d68d061ea1cb43139230946ce05982f61c039b9c502cd01c24abc31e677cfaa9aa0168aeb05fb1df7171e7d868960dc8d7f77f80625dba6dd705686b32b68dbc672972d7adbfd61f3d3228d4fffd45b19591ef085f18868025a6cd0b85d72794cd8c594bfcacd951c603ed4ecd4d5cd3c271299dde688b91cd97979b9e87119a08d297254c060450a7d0541ef7f40342e3a500581a172ef34077f39cb6e1d5952b5e7d65fa22c4ff8b20c06dcfc52b8d8ebf9cf31818325d8948628cb6d33e5ad64a30e563eba27d0ab0060dc0c40c5da48a8b85c47f22df807d55415ba67fdfdfaf738b35a1e51dda888468f3992054742d3fe28aa8ed7e7f9c7d4c8bd088d24f90d5c5ad9392a8d144a7734d41fe421d9717c6dc6f6e93f393b9838956322f1478fd73756d959582bac298973366f44afcc3876ccb3a258b82fbc88cf777e05eb37129368936a97fb2c46b0bdc3f48441953ae93beacb982c1165ed93619e7c4761b104a645816e700621fdf62e9d44ad0ef8346784caf23b650cf3f7e2bf3f22a6c3dc6e1b5b9b94cf08b688ab8b70d5711c574fd643fa01b360cf6995701469179fd06a50e8c06a5b713680a79aa98e1b200b2c274842a0f763b13f3ea4e7ba92a79466ea98ae43614e7e4d3bbd97e8ce84dae87aecede898c08af30c1d45d36c2974ef2883205707b091d6ac52a6f236500e55d8aa0df51a36a4a867912e78ab190a5dc72d337d3641724c45c00cf9fc34d296d9a232570aa2d8263d08b8e9605d4dbd1104551008d65169894f2453d1a0a7c0a3eabbe46a0134af1663e1595cd9acf0ada4159f7de89eb59c16818827f4c767b0f7d5431499747a0d3bc94bff7f18111687a723bd6ff72a682f94fd5653e562ceb948129a1e1d911208f917e5bb29ec4f334356a7d0464a59fdb31abcd80a19418dfda0e0134364474826fca2848045030005f77b79a7276494675a32c7120d3a0552383883d0d8d8ce4c39c725d9ab9622f69ba462e857e3a8c609ebb7fbc07684313168d0cdb133188394c232aa1ce3a526f2fbf4bcc070931ce146e40e23a85017036b50de73a7fb7c3ef131c67459475b3d6e911549ee8379c6c93fc0d9b3ac544dbd59195d3170e230727fff5f8c16f3616e79b28dbf39764e0128c2abbdefcb153f3c11b83adcfed51170ef8adddaf6ecc3bc03f62f2f593b55a86cf862185f019761fba9dea3a63cb517faf5c0c90099435da8e420d545c556169f89d692013a072c0742e986a989e7126187c70739b5c1a721bc24324647cc21093f98fd77011e063b24c78d5c89dcde5c385b4b874b89c9aee175a1c12f3223c7abb1d9ec2840fac7bad49e235c7830d18fb485e3df4d22038e81ebc3fb60747c7448a3001555425467dfcf614793c3b7a79de6439a9796740016e4bb1bb34e7e988432fc0cb5259976bce29e48cb420b72d47e93c8e9fabafd1269d03cdbe650de7577cb2445e59dfb28624a1ff031b4bdadb2afd55b00ac2d7c50f56dd23e7ec30e365c28b3146a8688d5866b0a4042e1f129857d401792f34f01c10a5d443502cf9d017963c060e1bfc5cb24bce7276c9e337ff8736154f27938fa3ae409813bf0b0ad4582a2a545908889c6bba04358fd1b2e1dadd992697082de90ac52428428d71c22ff63eab776ddf9505e0429dc46c0fd1b0a04d17f4ab1605049e5f5324fca343b591d36e0f6c244eaea5ac7a0c87d2fb4e1f0baf67008fda44378d6994eeb9ca0fec647cec686788ef82a83694afc3705db4a5963f63a1519fc5d765e4613f7666dfe5dbe08188efc034f641a6c99c2781bceeadaf459d1047c4dcf2518d4ef7b0f1398a9ca7b31d13dce7e7957d71f578d2a3339712986a36ff990ec2f84ea6c811bf4e7f8ec2bfc0808358506a1454bf9c6c392541f3a40c2ed04b00c9a6c0788454219c6dae8171afceccf7084d47b18c716df335fe84bf6a7b850c83e043b57562cbd902c6ec34557fc0c56633718b015646026b4a85116f37e77c9955cc12b2180429e8f0d18e75a80761466ffee1e49c01b7f6b2793abd2d4536c8f18ff2786fd967bbfc26db7c595e9d9819bdca7d18dafd6251b010596d59ec7fc36e02979e30856897371a69c0e0c9eb0fee77490f679f8d9e6733d46807d895441eaea253b006dcd8fcaacc911f6be0ec71045b14b11567c0a8bca65b3637dc751d6e680f17baee8254a5a200a3010f1ec5520866736cae95c0df3c880c3241617b99d8527036c0d2b091932088927674824d73283b37b4a3f3c208b49f922cc46c1f1ba4a1e494735d6fa749cc6821ea86272e154eb23b17948f8902a6ac6ce360f7f8952ed431199f4399b51293d3c4ab2be71c5edc031ae777b8727f15434d1eec8a15a6e72113945f10490625ce285733cc0bc71346cad7cb3ba411c4b0a82c8a237901f1d22d1cd1026de502f7d8553486c9fbcf9927951f74818575bb9afe73b6ff7a1c6c900ab553fde760d6150e5ff4192d380d85f143f8b064696b63df6abbc515a4858dd91ea968575780f78f03620f8e818dbb7380cf38fca2c35342ec7a12a30a4abd8b9b7f70033ec7f8c9bf66ca8cd04289a228014e7a1a08400ff3a395532faf8cd53685e804aae6eb7b3deae1b535c9153f4984aec4c8d5d92501bfaa72fc414fdd26260fdbe0c8154870e704a629c5403f49abce92c23ee703c690c38085f0874d2f219f0a8b3aa0533b6cd5ee888f7c1da123349b2e9c32751415c649005c7d5e61678bd72d9fb834837a9dd9df71004c37967a558c9303f4210211ab438e7a22e3d36ca20d2c1770c491140192caf766aa75e19bf9d1ee6f4d0f3c9340bdf06b2d0c34f10503a9f31e39883f8455955bcfb4633e967711bee1a945602f67b975dc69ed34f4e9cbf569223964994cc201a41846b478062924516514f91ca634d20506a192bbf6a3ce1a9e9be9a40aad8ed74f22418bed46b5acfb332d93355a0829a6aa633b0b3813512558e9549df85609b2764a1216ad65c1b00683b81c476d917aa24c729ee8e129b1998b9174ba661d5a51f24eda4a22035af00a8057e30e3ed82fa77a0674aa00760a5825ccafec1fde22e71f96721e79f39ffbbd0c8466b13511b2c0b8c5877f883d6674a75c395ee8cd2ecb0c964e5ba194ef8eb8f491abe2dc96eb16e412a0324c42de2cbb24e2073603e3ed21e8ca709cb94c8c00966b42cda0941a85bd3949c1993e8e04f3f448feae548c89ed852c6167ac93f31950a4d17f0e8f25669ee439e1c2c08c7387ed06a00e2bcca2f39a1cbe084dfd97d548acb42aacf5295a78a490263ffc4304bd8205c8bc3aabcdf6f123db00c26b54ed48f7425d1cb867748a1ea42329e0e123fd1b2b1a974dd22bbb016e35017ced5cd1d024e3954004cc7ff5464c2add8be056f6e2609856cf936a0e2cb9e55728a009ef086a39a3fff735e135a3df630c9ac825502c1af9ca703a0a191472990e3ece87c14071c1fecba6f9e9cebad10c2e48bcbb8190e9f1050f73bea5cc870df87204843471aa658d8e13e91399641221bdf1737b3c6cfc444b271a800354d01e87fa620fed0120ecb8d1530459032aa21407be7a57c373d2db711c2f7449db87bf25ab7445bc3d9b0c83b09bda91f3d1a0116902776fc6afbf2fa49f080c51bd33c2602a3d8c31801269c64ccfd67ddf93b03f1cba067bed0734252146c169a0fcef1aeca8528f100f996cec1f9a51d1a31a5121b14f77fb545e639713cb9cfadcb042519c1df3b523a61cebd2762ff073369d881f886c76c89a4da245494586e92c89e79d5fc20ea2d021e1457d8f049accc99d70390a0942c7004b8183b8bfe4a417409196fdd95252bd8124dc7dab617c97883e02a794059f8650dc5ee194c9243397314a31586b55fb093c486773978af0c8499bc89b2b132c5e8d15b515847fbe9ba4d3188d63c728fc194b998347e98d406e4af89dcad20b4244c9aa718aa82018aa6d54fc1c19449675ca97c727a8d8f0a1356ffba611f46c223561c51a6e7029ff8e20aeea50c22ce9ea32e5f793e61b160e2c7caf7c0d4bc5afccf6ad61773b983b8201f304144d1c7928d778ab7e12e2d93d41c10463da697424a676e1c7a09bd13a10e21bea67f403f922dc4f5d77d9b0662fb2eb338dbfceff42479d066d915ac79ca08817cc2debd327c7234a27c24847309fe4038ec1580f27f01e36c72efab3214b3b4cfa5ed21e6e409f9b36b58360857cb0e034a4ba3d08ef0d3d696fed217d2fda89834de0b355ceb554423e1d177f7125aef54f7a67ce450e507d83443c5e8f35246a09d193ba83c85bb0a5569712828d51b42aab47ac34a613c729d525cb35f8154d198a95b36ee8e6810dd023f851012255ad43443334b52887c331c3286815cb240ae9329d290c61255f98ccb8b372aecb5b3834369bfcaada9de2f75cf83176e6e32e5c84982ba8aac4c2249bba8615c5c83f46cdb0fc5c803a3957258477e3923bb30a979ff2c8376747fd3bd309eb8ec8b56675e19cf34a48a90d49bce4de0d9fb9078914b7a216b9f8a5b8d3efdb7de0295617610222600ad7a1a5d674b1853ca7a30855b4245ffb50b0c1db90d38bc7fe2c9179fc941f6d16e7038652fc073a3f4c68e4d30ec452736b6adb281c85104dd15d5f79502eb12f9d56a87f6803c3c9ca7eda7ca3a92bd8ee638b633a868fe908178f8ab4ec30868d572a086d1f93c29777e5f6fb03f222488e65e14ff955600fceb5230c616bd0e171179cf41cc7782dd37605a645c7057bb3a1f8b25612ac4cf7ff150b6e8c3a2e0878544dcc17906a9d8e307361711670915cc5c51e98801776ee932081754fdee8122d9caf6360053565678b3b14f9904cc56fe03f04b00181a7a4d020e2a5d39fbcf28a51636d11598ddffc25f8ea952a45c8f2ee7b70ce9caaf0a83e4ae2dc3cb87e26be62002a13b9670b6d1f9cb319cfd6adf21af9dd8449a701224719808fc58dd1257f74644027037640d690e86a32ee51ba054f75df579791ae726bd0358a6ad57fff4d3b360954ec45813588694b7e084a2dcac5795c882eef1eeaeea73abcb0994b28adc126d3e3cf7fd9c9a3bc665609e1744d99ecf80f6770d3a36bc9e769bfc7c78bb8a738667a54d2582d066105987c3c6df88e9abf9a4617c26d92d0d08e611b8ca9fcfcec9977e26562fc50cb9f928fa5fb917b22cfe6f7ea1576c3855cbdea485f92811e5a2306251514bffe1eb0beda3e2779187f9476de1142d32f07a534b8f54072fa277a63c3ef2d54d1836bc165dc15b4ca89182c6e5aa889e365bdadc37298afcb169c8b9718982fb99c2cc8ba0d853bc4f139a278de0dc01d4e908cbc939a188c929f1e698afd56662266e8be8b5c9a316d8a754a6a76f789abc29ed30380eac27a63d743645b16ba3701df9580d926d9e337d5463d875ca508c20111a6222f80be45289af51be735b0164bf7d5f52d8c6c5329df355c96240a0da64dd8562743d3147e5dbb84d9baa7d64bfce2a55f0ee7f77c1e2fdb31c879f5b91d172fef6f7ce65639772ab5a54ce6c3a8e99dec45fbc2932c285d2743fea9dbe76c421021b5b0de57ce10f6b6d299a39f06065088f4603f995b65fe61974231e44388f3a7eb0d2eb1454d6f16f4366062fbfe1902dac49ffcff203280a8376939b66d9f37288daa7adc5d1718cb3d8eb03c1b1958c63b4c7d808701b4bcd3970c1d613e0f7ab7ccf692b556c5b7277d072f4b51f2d25c8a45776eed8da914fd9ae6c1009a322d1cf4c7c918d0c5bc40ef12f0d8826300b57a8cf2b19d08b3b09b15ba782288b1ddfb0b8352d10d140897c704b21aa2600d2fbd266091f40d6e314683cba3b4126ba0a76089cf925d7f81dff423c0fe0825e9dbe4232b5fd1e83c9da70b3a5efbd4f871961e9105f128244858dd13286215c598a63d7574ca7cca3a9e472d495d0b2e76ba6066f3a9173d204a34135464daf35ab4a679fc945053f33b402ecac3c093dc7196841d88a51e6ac83000c67b0ec2d073e003b54416a9476a5644e4182ac1accd89d05501b16cafa0fa7c0dbff1361e411a9d79c7e6a1d8fbc217303343e8de9e1323c3a2c6da9d390620438fade3fd2743fa75c80409aae4f06e2f05a0d4f9035f09c041be02b2aa2e830adf80feb95dec8efa2448316a75c73f0f96d48203b9cdedbc4967b179d52e7abe96ed866f0787a7aa9ecab23275e740c17f79a0236807235968105f75747a23d92799fb754d52c81f2015dc7b29d7329f6528cce3cac8d1ce92a909168e587edbc2c9211412d1b75c733f5bac6c29284cddc50fd6c95de53d9b6b383a8ae85eff4cb692fc08d5a20fb005440bb4e5db8ec42950352413997a260989d3798f27a2b51b26688a49ffb684af5bd128b11ba74f6f8adbe6c665ce9635c23126c9146b2d48ec42bf2fa3197ddc63be6cad20fa370cb77b8749e0bad47ac5224061af8c8b6fc2c0beb775ee910e1b153a338e15082472e1b66b7a69cecec0da303a2c8ce5acc05f2d9969ede9c0285b0ba3a9e81c9034630e7276f4ee8ae2690254b9fa1cd86bad340516e9878108e93dbddaf8c0a963b9a7b9a191de2875230aff6f148b600cb9c04a96e4c5f41b2c73631f90e1e306c5edf32a11c16130a9bead71a1b17fa85e8caedec28c6d35626b962de39bdd0d20926fb084c0fe349102dbd14306358070fa94235c49f37d81c61148dcfe65f895862d5a11e506d5f64c98fb48a7cb6a84148cd48d8ba69d2a1ea272f5b875dda5f8ce743bc01ce3fd8acca917cd69d391bcaca8eaa3785ec51a49c34bd5941125c2cb3aaa67d9c18f3676dea4a04cd79833fff69b5fe85fe49293ce1d71186efd29b1114bb995289824dc6c33fa110e503474f690652cbed85ca04401bf1793b56b8a6d0d4767931b01d71d4f8f165693cdc39cf48ae34b2a6b390a532242dea12ed934f92055e42a8d95ee8cae282aeb17c107985435dc578768ed4fcdc5bde2f4623705589207ba0cecc7f1845b75f60df7ff4ab825e885aae0e7a2b32ad3c84140aa54d08bbe1285f8835672571f20993e3c7d53cdecee927027e54932890de0490effda166d9577ea81a6a6a16562063a28041b63534e44f80d434537777f848b2e94ed6b42bcfb7e0e3c7224ee084948752a307866b101310de2d7c4d1e7d1d34cf8b624cdd2a0cc1607bb9daeab2b81fad993489473b94da8302c73d9075d83995c33a0e7e0987a3344e6a3fa9f55d5379a02f36b4feee8de6254fa2257e9fc1c8ea639cf9622a8f605d6ae9840379ea33b0e46cc20a0183b9fc998ed673be86cd698df3ef86184049be703ee4dfa2fd1492a7f4b8f6f44bb801fea4eadc80a5cbaddbd482b914a278c5bd9388eb477d20f630bbc04cdd3eadeff340874016ec99cb9d639d9866d3694032a97c15dcbfc7789cfcae87139ddc0f44dfaa736f16fba8d7878bc71c7881abd03a2684ed00ac8883f08e57f2e213b21f2245856e2c0402c4447d86b9f78c70a9cf1ec1038d4443adccef1362e7eda28eac61193ed7653d6d8ee4f78fac5968ef904d017614488f4c003da381cb5dc51e317a1642a0202eaff513532a8fa10f74394b4f472f09e123f11c4310df6d0a5d90d553a6d842c5463216e3523e01f591edceb49952824f8a03434ad6414bac2047dd17764a6d7a5d52e8e42cfe0c5b47e89e81ad3c7b2a352bc075a85afee09d1f0769dfbdee58099019da5518c196fabe103cbbec97814cafcd90dc466137df00010f95ea4e5e3e2007a72743140ff9c480277c0cb131591a8c606d9c624889205f941b998f40ae51efd02f1932c3c504cfcecc1284b0f55d341001fa336394dbb78248e686041e45f80918d40d06a59af386eac2bdc0a941b7795f2c181bfb89deccab4eebd1c0ddb98f63edb79f222c66d41ec518ffd4e4524c2e66f63f9813687546b7d57dadb938b1586f25d52673860dc81e3e43a7ec215d1fcfc4bb469c5be5f8715dbe1a7e2e9fcc07931f7a95f31edb596bb1516c4922d0702162698ed02f510138898223c84ae76ec0adbf163387a13227a168216585f128023c76311cf71cde8d7a6d49a2bf1e0d326137b36c89c5c218baaff794e42147029ecd95abc0acb7089eee48056120f3460ccb91a65cd457340e01b498e8f9827b51e563ec4cdf16c305110b81def99d247a252c98bc0442e57a90031c0fa5bcdbb4811fd7a29ae7569ec6b2f5a34d37f672249deefd0b0ca0c248593d21b2ec5c655a6eea38c6c0c470b2753156490757838a95112c3c1ff2a8327951f180dad55664928194c4d1698f62fcb484fb53e0111b73f4dbf48602a37e89e0610a1ebf1529882e7e2d892606d0e0fc189280fef58b62f77f6459f789a50b215b9609a100d8ad4460433f9957eb350833824a3803cb8b49600fc9ad4866cc53b5a832772d7206e4b2bbfdc3121ef8a9107de51a7b4353eb539db238b50df4fe156bd3a92360fc3e0664dd44821740f5b613855f4dca7e7fdd658fd4916a610874496a8bf08af14ebe564294ad8bc41c0002d2fe323c22d11d4f3cb964841a76ab7b3847720aa70318228e6c18aeb5e5b5875680c7487085c71f2912fca95b3d402d85b982eaf95664fa099a99031873632705fb4535f880310eeac2967f742364c138c8f85762ddb06b2313791b5092af3cb35e2f596e1f83f26cab57cd61fd9c0154e0ce70f56d66ba4e5fd0769ce2c3e172623d608455b8370fd4e5713a92cad44212579045cc65127412b95ba096d0b0a29aba4b76f21b2f6352a5c25acf51bc0526d798902a2a14e143520ca68aa34e89fe95bed8849f60c8c4e528dbe2a2c9e19e9b65fa1f2e5ae35f4f1928e4ab05fc24b64def4a4712e799b34ba7459960f132544694289c5273b2fbac2f466b77883843f822aae0beb2af3295e70300232ff7350bc34de7f3e8fdaf857009a0d0b6952ea79ab757aea7f62c1413334ae4deaba1179b646e0a3b6be5941442953259ffa7e896fe1d7572c6d2dd61bac5b2c238b45460e46caa0dea5362538cff6bf99562240514b10cc7649c2d15dd0425ec4883c4baea4381789d8166350de32aa58067064730897b11808c9bd9c69d55bde7939a56ef56f832d985cc7edf9d243519d386ab864248f29a4ec4d2ebfe398047671e917bd0f8ee27d894d7c436c25d85ec128371c3e6248f0fb138f902ba9853b0f26c6bbd13c6117f544b174ff3c9fd16b226a80e012ddba351ea816172befb25bc78292e5318b29e94a6dfb3031baa9e93a18280a161f25130dea9f8604faf7980b767240fd05a2b015433d06417279b615e4768556ed3ba3caf721a779b12a9f9d87a41b1d28d8075cc5edf75e7ae4771e59cd589a6b48a8f75ac81096ae8e6630c1c3d4fd63ca690a0846d50e7093399e1b083d58f5d36c511a64aa507374fc46d98164e04ae29224529b2478b1557cb3f1e9b03043b5ad9d6392c757cd6945effa9f9102a3f1865c7deaf12c861a817219adbf055710d0ac5311c4c422132b0607680e0151cd54ab164d9708cddb0a886e8e7fb92045876d16f7a2b9547e24693bf7da63d153edb010e1d5669924793be29900f93888872a65df9c3dd38b0f645f6cdef0520c0bd6e3141aff374bf5c455cc413d5611555eace896915431909354b5f387c012652bbbb912bb93218f39824535f508de380eae71c237821d386c63c2dd4c55c979643957eb0f12a1b009de00388009ac3594719515d0c6db498035fa274d482049c226883f536e64747bd31416c69bbceeda4b1c101dd75e3b6e027525d71b1f355ea8b6f3bc23023c26f697d1d2899981a97ad709c72c269efe8c3386bee4d8d2a461158ea2f7864eec83ed8da02f31fa80e2e7805e4743affaf7fa27bd1cde5ef0ac170fef756cdc2b6a473359a0e2514a60de90346e5d5ede0135d492141948053f518c9a3b02d3ee56d7e840c327238fc03c2d673a1c63663217fa6c402ba3f23997dbe042090b095e8b9db8a471b898223b94c1dc5762c95cf0b77b8d1ba533ebf56abf2a52430593975401e7b585b9dc2d1be7d4868efd26b5c235915e778c82499b6b6e72aebd611cef6c59c94c13039ea4c0f216e9361662a2a6cc6bff7b7dd35f1177c543050dde1bddc7c62a8fbe5fc17d33769f55747030b8e49c3f4082ffbca840ddbca0f976c52e2a2d5d58b8d6508ffb830183ddabafbc6cf8c26cb01b45f7b9e2fbaf04763c99de15b740585a7213f9ac10cacfd486324ecd1c8bb21048f59fd5da0cbc223e1cf485f38a340466af848927935b038f6d130d9b1d3cf4ab986be5bef6515097db693591f090cab936079c4066b4b2e6bb4ce179c621aa37ede9c46316a5c385429405b800f3388d6a20c3f740f9bb6df02aacd71871c7c0370add2c0eaca18c6241038b0778d581e254d1901bf038454dae90fec65c2563c26209cfdd380792ec7af365f10cb29f2cfe64e8c802612df717d3ff2403363e19f6bb6954204dea01ff924a223cc89c86ecbe1b8dbdb00c85a2cc02499954f816ea0c62420c31dcba6c0ba34e7ee5c7cc21d42ff667084e73297ea81a6d910ccc9c3fb45aa84f6260360988b25a890acd354429d67c5b1b128b5970bfc530f0f28abca046968352d907733172b8fbe3d24cef739341a012c0ae4569cf038f88d5be4a472d594fe389ecf729ec39608e7293ba377a057737b8c92e88188f24b5ebf530525932baf8bc87842724796218a0c9a94b5a3cebdb9b8b4b51c5b25a28eec36d1ed4b4e88e11ddeda62a40fbb64d48b39ffe4d020da3fd5e1ab6f7e7c98aefd3c2af7b8b82bf70295871081ec5b47566b30b0ca09cc85fbb530e11d35fa7bf57bd70fe5b196a19d5cf0e14148b28ec173eab41d260767d060ff12f6ab05defb9f448b94259c359c18a799409a6ccaa776d7bb1f987fb6889a673a634796646432a30022012e99fe456518cf58f9ebb57a2bf175081f8d62c54900d8f81846dd72a971852b0b70abae78d45da3cc93d688251b85e3f27fed9b93c3431c42f148c7788335378715cdc160da821bcee6ba7413b52079076303d6f00d79dc3b00c16c2e4e63f772728e104f02d4a2ffb73904abce429db60223a3650860436ff43d983584e5657433faab93b83794d0f0b98c5cc11657e45225b0e75d95bc061e2df66487467829242315a461b8e8d6bda7aea248b25d81c9cc958342a3b2f637c8828d7102e1a8a7c11dff60761420ead0b75635a3ea88e39d523bd13a530cee315eb0e1d4aad79d8819f74cfd51fae1850a3e9b36e72713cab388a14e5a8c0f9c551094aaca4b1c97fa79bf0a328b0d255e5ac21f35462e3912fdd475fc0bca7fa4beb6789178437edd0dddf7b3a3cf6edb7e85bf0eb6c8131beba5f413a701ac5152072eec7271056e1543fe396feb10ac5df6d07f672941da0f11280e6213f609fec13ed74334397ee71899d89f0e6a7065ff9adf7bd2f6fecb7acfa7a568c5e3eb6fbce393607fe2d939f2ebf2c1bdefcba13b9cca40080145339c5f10b6c097109ab0200d217bb083918955631872f5b9cf159fb54afb3f3d0ec98a18e5efbe3316d4a5c383d68d646080b0100c45365c7a871eaba2334ff1ff45813af1a836f4ed91c2e718bfb3fa2d4c9995564675b5a775f38f7328b19b76b74947ae5eab93c27b2a0416022699d8afde7b16758605be33ff4edd640fd8c7e16faaa630eb069bee8391595d41929a7b38afe9e15a7173ec63ed6f5937b87314118cf4b0823ae980a6725913b92fd7f906c2286cd8750ef410cb43027e606cc31d8bded83c0b41b70aed4ba7573f5ef220cccd266d482752e0797748081c424a6deeb719cb349cdd38b0f8fc2840602265799428e01f4e632656d885b849357e5c690fd7981d7b687493054aae8593dc303302a9d6b856badc59f244663da817d29264fdbaebd91ede60acaa262d7acabd88ab2c656242d871a700bef61950a6aa033a5085f993328973889b6629471a4e80ba18696b1cb24e2c877334b0e0d18b9005d36aed8629ebb7804d47f8f269fec70b15b29a9f33c5196bc249e7e89d509eb6044235dd87ac950dc630bb813932e94d0f63ad26c80f57d1edaa90c481d57512bfc1f2adf794725fa29034a5709b3eb4cd3bf2ea113256cb20fc50e053046a615c980a2618ad21b3c4beddf4c788c6398d774cf652a801e8a7bc1b8ea3e552ef4d2982ad2c7cb5f9ce45fd52cbb8630287a370c8dcda903c5111b2b4eff90fe57e73a2a63308609edf9a528ad7c723eb1be48ac7140480ceb2e8c918423a17a490f9b0789a8de0ea5b35184eb0faafd2ce934a180ac8d78c0642981fff80d53aa3d3262e210aa91a570a177c39414b10b60e8f9dcafbebb95dca6e01684e9a0da1bbf79c5800ab69845bed6b94a919cf1132e643b6c8772ec4aad3a11e27859b7e862a3758bf62d15611f8484364f4c6795714d46681bdc58169d4bdacfd3803e08255ec6c9648b48e84a6ab1a08669aab9d3ecc09ab2aa0a2997c76353bc04580c35d869e05b1b521d2dd95ccd841f6db41a292807c22e8102bc17ea03f07fc0bed6e810434255f15fbdee6e10686042b805a57ed476f69f07aff6358e53537e2fdab69ad290273049bb511d208309d87a17e20a9f51eb39c22d6b99b884e0f0a8c118e4b8183d547c8bbd562e67c65d69bcef0aa70acf039cfcbf7277fd73a96176034fdf2931f96dc724190ff422cd8c96ffc43d494ff711e7280f685a93c54e4b6980d004b1bffc51686c4527e2417b300e74b20d9b1f7f63cd72cebd085bdebe4d26428a3297b0461a7d5e439172dbb2b325560195ca471f3197355b292277d63c87997125a8e252b9f857b7c95a17c630b39eb0efe9f13e4637dd92a05be945d0ca030b80998fca9615d9a897afee815c9b43b27557dd591a070101a90d6622e88a01bba5891114bb67560f85f1ceac570913281733d6d816eda46b17da2542308031cbccc2bd3739fe3816b54aec917002f72563adb040b8a46724e70a0707cab975ea06d8944c5f15d6e3d1cc4ef5dcb293933f0c96b1a81eec665328e4067a340efbb4384d676af5ae28eee2e644419b67161698eafbcf3b7861ff445e46ed208c86ba04c4318a1d6923611239f2324b0fa7b2541728985616c30b441fe7e780b9ab3a2456a17ec961029e1b6c0f6ea3a837153620cb8aa5e39c671945119f57220f41cc3090819ce991b9d616060c15620d0a1e0d7faf8704aac11d374ea77ef442a038502383d00135a05965e408de227c8769e1af62e5b8091ccbd4b4fb20fbc1692c94142e593e26269178c178a872b6900f33aa67919e877ffdc1a8ca8c107d61d4f5bfa12b42837cc3c49e81daf340191622895f37f168e1a5c59839868bfc2fad58b96ca4c8f5c83ec5c81439c35f5cb55520928caace7ab4651f1e1106f5524c330effd10e58cbc213ca6f4ce5442bd5e78916124a720578f576253ac0d744e49fb90ebe5d383323cdea8260965df13b425869ab63eccb150725047b16108a714c38727f665cf209d7df0ce22ec24cbbce342cfab8bf26eb7f9c03e96f64aaa6ed3d074887b99a052e5369e5e4eead605d5098589172f12ac1977b0b7ba89fb9cde5c07f17f75695a930b46d76456846060a312e42790f445280f853c8899662d833530207e62bccc497a89706d07b2873c02d308f2a18e311c8c073a2b9f4ee9c649f3c193d81bdccc9b65522e1fe4b98eb6a80b24d6c0ef6124b57c3fbf3aee284492284b4d7110ad46d50f23c6d26350e02b5c58e3c7ef57b23c33a49bd2a76e4006c748551cf96f11219b13c9b55ac82b5be1a362f8fc174bba5132332dc3abec321752829bb678f4a52b2bedd2b386cab6b4bcb83dec907e88413a6488e0bd8b3693d6ce2b6ae386d3dbe09f0db59e744c3aa19bec7479159ddaba75be255a5e8e38da1fcbd8be65fbb393627877d7e382a54be6266f11adbe93bd06a68a8ef33577a3ccac0f2a6a45aced9614b684ed955c2d2188cde2d851905af9e9126dfbd026e5e8925f2480cf36a528f940f42297fe4416368fbe09ca8f83473dd5f724b1d804074cf17a2bce96edc0acc4de717d81cd45a2b7f2de5e65101c32d8f66b13193319e6e90567e4122248da0f1a41f16c16494f63f3d9437a14b2700749b6371d0f31b4eec4200fe6b72caf198ee396fde0e1cd5ca5ea220a2c9a1ac9d77a52402c60bec5601d4ccb1b7a2cb4046f29a7c4876ff5c21cf48443389971d4de74df949f50bf871d2080609d61761966024c99b31c38cdba31d51943180e80fe64ec151dd9174108053716285617a1a090bb3cb1049c4752ecf319072e805b18ae2a44986d36d0de5329808f4e187e25f2715b7fe6165866ca5000480057ba51863c02003d10d58b564c6e1158b68228b225eed16fc15173ea94a250af41c5e11c26848d8d63068775c08ba15d5161e10f4bc2801294e57100b90300dc913d5ff7ecbe6754b606925dfa0bfffe901efe0b771dde3968a54a73931a0ea1ad2203aa6c23d20cf5a2e7cec2d971b7a9fa815ddb220194f4d80240032d676605c00aa68656b09760742c1d0d5ee07dba50c68798955beb4f823e69586503ff9e865af03e536e28605509f006eb0c5d0c820ba204614a113ffdc6da2b41488a1ada1b266c4728ea6c917b3861f7cd6c795a6a2efbd0e607ba2a648d437205d116a48fed9c75ff9c140150aa9530c9860f54493d40f83a809d4a9a124822029408be1e0349992905ee4064b2dbbfbd534aa4c3b1620ebf73f0449fd0d725d8a1450afcc11912f100af89b826c039cef7df1d0b8798f9858b16fa78a91902442898985b69928b05b209424fb7b391113ea9473b72ced6cec5f6ba12375730f11cefb9220b654f4b303e48d07e6b38894d8264fceff5a0c70fb6cfa83e2e00e341a1dda23f86a5f117ab85e7c417bfc111ee4a69b93250dde0075195a4876519f18ec815940e5048a9f16e11cfba918b41326c5ab1172924a41bfe6c4889ff4c549be8bf83945ec5e003724483d05e0ea4bd7921c0a5213a5d1add62c9bd2135bd695aaf191b1c19bc7179be29e6ab4939845a9d8203a4890c42c97d1131c2b182ee775cfbcbf5b3ecb9f842d4e816ff1b938511e74e8e00295c5368484270c32963bed8e9b63198c83bf750fc30129c4ec7454afb809021dd9da6567e41c78c383d3bc1b249813d997658a51760e6a7c84cfbee03e948c87367bb2664320ab3fb265159a03d66041f68f6835d016f2d86a255022763b79b8c97a0008984ce7c52049c8d1ff10aebe950716350d1f68f62e458c0f7dc7b2429db947c27b34f0dbab599ba2d8d5254e131a060041589f01fabcbf0f81686fd178a7c652fa2af9ef661648d588bea0ad17fe9a6a89eddd9d92fc4c673841b0be6979920d9f3e5d2af4ac83be2a1001f3737eeb0b6999454ef1be4e9ff7a4e1cc3828fccff79d6a212967352773ae69a98e17f25d6ff2850a6c49835f41ee732bca501b5327b47a9f7d8ef555f15ab9773ac5904181c26050f7e5eab9505292be22e99fc048503179d22a0d8e839ac79898847d244f777c4cf8ab87a35bdd37ee0e93a4425a94e713a84efdffac8ba721ca98ae7c0d6388a3cc6dd4e4aeae6f098006ea774bf09e407aab68822bcb76ea003d7a2fb72a4070c69ad99af33d115839b00ad7ded0be78d60271bd1420f00b875ce9112da9f676a9a794e31125f14ebd469b0e306c3c4301ce2a16e9a4406cabd71104eae05a2d4db34e1016f91261a441c10f0213aa75a65bc00ece6e00715af7dc097f05c5ca074f128db73ee101bfc657beddddfaa3714c8dc114ed59838a33a479507b044dc5bb7b1f0b41e30de7fd2988cf4fa15a7b3a8c5957cfe9bfbb3f62c298766a215b14b1f9ab4a0d6b835e7ca6b51ef0bc176b0ddcd93ca652ea4676a6c3cd47c2a187dc1469dd32952c2ce3d9850049e8325536a9665d6943fc5c1df21f8f898e37f6ca15e8276c74fa401bda0c8f2201560ce0be7804a129d9f2b463de42565aecaf4b03df9bf6f46627869f405422ed5e1323e4e0b07ae24012fdebd28962ccdbbfdf9c80a9c12360a03be5703b2fca7c5a3a14e6a17adeb3eef0f51a8826ea2199d100678f1124fbd44e18e4276a4db13963f207620609cd73bb42b80809f083d912104770c1ad99ab33f0dfd0488bb2acd0c3a88d940df9e6d067d1d2335ee9ffed5065fb2f1a2de17f8bd1c85bc4b75a4afe6ef13cc8d73d399ab26dfb66a2c56f961bee17044b1703ecb1ac0cbbc6b695dec02dee084d4e92c87ccc1fd04efb52c0e3644afc23fff1d797ee6d0ef978c7db42ff72feca0288a521479e6da98ea6cc6790fcf2744627457bf5bf530b4b46705713590e24d65def1fb514c1e9d76f7c24c430da8ed52882749d2292607b0109756fd4767da7a7f19f1a285ed1b53d386f0bf643d8c2569644c79daa6562426bee82d121412665e6337f823920870de81f0df8812d5133fa4e043931fb1715ada49e672ce6a505837e735c16ce44add7a937075cf87b9c2c28732b915eb27396083e30b979d106c56464ae9a833c384d6c1f8b1dcf9e27d67ed66a2f62ff17c7badbcff6803cdbda11632cb6f5cacc5a5bce322e2b822a0fbc168fb78a3c7936d5efc0e2e719979e01d9556b683bdfc9bcd978e2bf72c0305ef39c3d155d8bfe26a27558e3fb47113dd50b016f00ca3fa2c78f6d9257f7c145310baa3d42681cb694815c19b851ed0e648380697a29614e8dd90b49ae7b2511474c92734f57e59538f6520666abcd702a4fa6ca059f0f835ab7daaf2d76f92e8d8663a69c91bb73ea45d54b88d975095a0d2e4d591033c46e3ef8625b8065c4671dfac5a3c205e0f7da6688614c5c475b3947f1de12cddfac9f67199c1d26d85ebc454323235de11700b9415a77155aea90ef27cb78822922982c35ba6fa1a12a9df704bd7caf0f782d3623bcd333e4cae6c1b3618a2d60790ec81ccb24704ccdc021f05b55dd4ad2fe6c84fa9b95b6dacb59e05066384a20d909f68295033bbbd1269295c7e5777080d4b625d0d107489fdce25870e4eba09b46a2f0064787007f6980d8aef001153ea59f5f6bcfe71914849e5566abcc2bdab499525d886f14a97c4a2cf7bc88353dde20b63fea0835e1812a9f8fbbf842d2a0270797e0d03c37fc199844611c4c28fc85349bdc56d5dfaa006bc4243f1719c4d7af630b08a4a8f66a13190b45e044c1ccd3202c9146c259973c8c8acaca5d6d1992cd3386f6ab5cba673c48d6b2184c156958df2033b5997adca95079c9bd5c736320f8975aad4232f4c092c0068d7987a2e5ec92c48307a1f1b8b48634b7dbd84729b7f728a1da9cc802a72d2458bc944668f2088c3e8929894becbbf34307f30077671d1abbc516f581f93c8c09fa46b4f63477959c24fd3246e39513a2882984fe5655783a80694415a5c0d99e5e9d3bb58fb680921623ad97f442d3bc3c6958075b11d93e4c6a05d7da35a135c32048b8343e807c7a9f97c164f44ed251d0f639355345c2ca1b48028522a9237ecc371a4d927de6bf249d395b7671e277f86612766600c8d0b18ea2d095f7b63a685fad81bac8eadd95508e3bc804088a79dd4ada2aa40e20d2986703f1982be9b2b74d0bce93080e3580d553a3b21a943e0bcf6e105a687eeb39e7494a15eb8617444847754693c863124f82fad47be96305995f46b6b17629fe80330c01e339fc4613dd44dcb5541a517f1503dc68888d84b5daf0d37c4625337c1391f8405476588254ee705e00162570ea3e7839bbc271bb6e6eab828a84cc26ad7e7932d5954462458bc6a65e3819546b14e8a87282907b5be0d33508e00d1071cc25e96f800189d06b52a43e192dd5277061fd1958ebe138d98c3cb13e52c5e025593cd432d1acb8cb89088acf04ef604e2245676feab6f21a1612c73fb7edebc8d6333fed6a2fd2b7d28a84b61413022c36e49378b335d32e310f8e4aa8414165fe2a5aa945785a4b642adf61a3759a6fac6cc5575285a0851cc14bea24eb98cf2e75ba3692084a82fe496aa97aceb258510c0c7f1538768521751226a340086ce5de751f8751976626741238192a6867ff010267225ac1e9c482255fb5d640c1c3afb197965a3d4ffb5d45e3da00cab44a0c65435ff5f63482ba9805ad12e348ac0ad8942d3262e08da0a30100fb96d8660021b7bc4b62d357926c7fe4843de48959192405db05f02e1f61d31b0b1a51b5a984cbd6cdcf1b827c6957b722ec496aa0cc02e047bcb4061c678f2893ba331c8d514ee89359afb21c88d95b245b0b1201e7273fed83a11f4eb2bce54c3496e1523931f359c88340fe1e9ad5956b071e3b9bd0295cbcd8ffbcf6367cfe85397a97253ddc789d77cb05c2143954620cd74f38e95067fe624c4c155e37381b68169ef9603264a287613b4e93f8f29e4e5068ebf5c6980d9a849e53c6290b8fa789e0e5664a8ac3bf505781a0a8584ef9c54520def2564accaa72ad91666463497add7b3b61a7db531df0bc990b8011b0474032eb0efa6cda8589dda28c366d57cc450b6607ff468aa6ba5b4eb40c9c54ee210cfab83a0536720b609537d9a4e0b158b395975af327cfa6991f5dc13972a67d7a53595aed8a12753ebd3fd906f79c782ca802009ec87a754c7a6469907ee496fa96489c2d0ad40c01231e63ead6d3647db0a66aa988c167b3704af18a31a78f27b4f0fff62fcc9ffaf39c77a2c46555f8ec7b048b9bacd0b3fe4c63481c8dac386613b8ccffa084c4214a588d2235fecaeb7fcd904b23794cf7448b600a08913c39809da376898b8e75d2aea81a8be55b1fc9c8cbbd577a9d43ce31680b58574279ac052a98e8fd381a8792c6541d44d14cb4047c2ee79434e1aef64ed72fe238bebf91e5ecac3e2236f6e12b417d0574c64cdd4edb44451ab34bebefa2b13451d86d414f291f0218c983fed7194728f013387f25578684eeb08515aba92131955d615547ca29b2f78e0ca8b0c8c591be1f4d6c11092a57bf5c1cc98512a4266181cb6ccc6407a94f82700b54da2bf49ba19ebd71f886010a097790fc162dfc4897120c7589d4d81c72ada2bfe879a1659f8389ca3a8e9c18957e688ea3b1e4888402ce51741cd70cb34769be989a20119975379d4d1a983a29159ae987a498793502a822ea08e18b0a90729a71f9567001643d10ed3afd5f76f3c69af51f06f0c96b4a744cf46330f291484bb9205e90c0f78b778b5cc0b32ffc6a541d2381d95ba64c8f6adbd80449d851c42607fdfe8e0b37a38c64dfa5f74a759898ded7e141472a80b1e7089e9eb00a2225876dff2274e5a1465cd0967e184c5e56039f533299b4d1509fba96e1f2d939473e761cfca7e1630761fae09b83135843c49dbbef84ba679cf06150fe6e0ca4e853b2bb75ec5f51503d9eee46ed0e8d8cf6b472dbb9034b5e80c627dcaea2b40f404a6db7b8859e170c7856dcb972548c4f74fe8da13e0f6ce30be557aea1ce210788ea6d465f56d6d15bc79dcb4b6231cf8b05de9a6b9e21bbbb4128137ca3317efd92e3403b8988a92940b38eec3116e8d5b0865beeeebbe45709451c4b103b3d2414e17d1c50b3937521a1c871b38b805a7b1d43d95b67001d91127534ece3da48138a8853cae25188d6c08bfb70184bccdc9c44deb9917dc85b7834c431facaff1dad14124e23afc7e6bb4ca81182ad935ff3e926b69aed8dfc9a0fcecf551b6836e1cd7d1cc8eb4e35b28083d390b79364ddc8109da21cf44656120fba81dcaa54a8f7d4494b77856661ca33cd684f2669ac8cd5d7349271e1a88c552e4d589f53a6530035be24a27393433ac4b552be9bc9fc0d6f129d9c6d83317b76247fbd22d7a386dac8662c0d42383572c0210a82e70f659a2e5870a10881a5ac40fc7c6b017738609feaa1ca242aa418e3924b9a2874b5cc94144568bed8281fc1d3e876002679cc5dae2ba923b30ee5ac0925d380c1cfb071a7f642a60915ef3a65410362740265995ec35b563f86b94cb7fcdaca63124542f71a7ea77708fdab08cb1ea1c2691e65f95e2c1807ecdaa6337ebab204ee01da1b340e3807149b987f5edb441e4f8137b98c706fe7436c2bf2ce384e36cdf8f8c7927fc1e9b3693f6723b532cd309832f95faaae99f3f86bba1ee044411ff21f59f39e896911293d18915acbac0d6e90e4dc39970759c42049e1ec97ccb27ff3981a74a5332632ca2af3b6c3340c8d4902be5252292aa5e5c54bd3d4477e7ce64c3dac1b9621c6bea5fb449ff8f4ce39c8176b8f83e0472422c8abae10afba4969821e339ec085faea29c91a8cc591959ac11151ed8ec248ddc018d1e0ad5a2daea4b61308a7982bd8d0b4ac0116c382ea599f0ad9b145ec64fd0f0cd5c9166057539dc176b48d1446a3ea725e990d5440aec4426c0f8d994c7dbff3f40dc9795a201fc2c576f1b07073a56367e5baa1b02f07d962a8188aafbe10e170f67f281752245cb9b545b1123a1b9418028936109d6711901a2848d91e6e6735067ddebb2a961822ea2c84a97e84778b76425aa509f2459c5672b030458ebdc5f6ce3a58436c5721d37722744b05cf2f088eaa581dbe35508101089e03a0589f91dcf1d4c055f3d81e6987bc01cf2406773acc0c0c429b0b1a1d678e896512c360a9119126defcbc3f9a8b91873258197d298904be4f76ad4357d0d0f9dd6ac115b4cdf620da192740286d90e96f7ce808f172277d1393a374131b7c57b239dd3e31c9baf4abc79970eb4c351711adedaf3f54a0d883dc704560fefaed97c27dc567bef8066c5fe22ff26d96658f2e53619c59e3798b2a1ae7f961104a394a2731fd0e7da7d413abd99c1be17b9b85fe17047b1636e6d8eabdf094cba95d903298b369abafa870bc4e8859eff712cea9933299bbb8bc85103108983bf8f0b12e881eb3f527f4f55f81751c2bb380c1642be1164f72b31fada3a95cb6cd06cc488cb41ca3889b5974a39c7e8c245609ebd1b32b04c682b839e2aea4bb7ff44b9c8de3d6dab8582b45004cec9dc174977e21c1da676bc594aa2ef6978e342c4969e197710464bd1e889752a6afec281325a7d1ca557779577c7bc10f1374269cb720bc5325d93f9c758f56f7174d3ed24a824fe87783bd58d868e7ffa59e5f0797f12feb8eb0ac4f352a08575924f920a5ea4994cacb72107a9f5e18e1067d78336538aaf138c0329c0684f44ed25af8bbae7db712829441b4c8c86c69bade07b1e9f229d941b7cb6d01f7a218775ed8cc67cb1064f03cb630976c9c5a155a60f09b1af3f9cd519bda7022e2a9eaad60591c020c165d0e99b82948737ca2bb58c65b06e52687db002aacf2fed9f3b26b7b0510dd13e43f948c37a62b1145e84a687c20bf1f029693eac704fd42dda7c2e3ce3dbb4d6f7494020cd99b20cb63ff21137db20f89a228245b388fd3f7eb8ddfb57ee46d566fad8f2678056e924d767290189c56f1b03305a2053a2b51652fc35830d98df9e1195db348654bebac0f2ae72782a8b1f351be2d7b66115954a0ba12a1bcba75fca0879d58b70a912e56057a10503dcd792279fc3b75ddc04c796f429cb4f3a6183edf7ca3caf44542465c55667c4086231aa04c52b8373c0315146dc4c0af57bc4ab28579bcc81b19e7632a163825cbdbd98bce9fff4fd5f4c7819c2fc5e41c1768ba40f026321a4516305b0efb0672016b0791bb270aa69a0734d2af54ab2fa034d7a368dc3cc101b1fee36bb56d4f4f2f7c876ca45b4df5638b7decc6b70a22ade82180d00e9d104a6b516b2b2c5c02481ec23c04e4fdb691a28025d596475d20d6a0e3d6e16f0510c945feff330891927ad521c4d38c8ff0a5e78cc1d5331f526ec7ed5593a5f928e788d2cf8a44b06f02edb73e5e781d2df8b01e6491abf38f1bfc729741ba5144c9676144055ecf3f7c3eca699c76661a166483c6604acb692c99c09ecc1b2f71915e2941adde1e0fb44e08dc6ff6fdaa20bbd3364617b805ace61c8ba9d2c770d0f313d1d45807e6764a23a2554f2b11ff2d4c4aa6dc96982bceed283d321d5718aafe5be69bebf225561e3e7c691dad9ac830f6fc4433efe059056e45b20d6f1573e48e476823b05ca87a0e0958b0d5d23b0352e4d4c758ee10435eedc67ee576cfe8602d946f240c08f455040ac7efc3592052a34a63dae9ca47060d8ca811785eb526cefe0ce51311c185fe91f289564eac413aef36511d7140143308f06c800154aa4bb491a8ba9e55f8ee7cb42a1aac4450bb70f143f9a5a6050bf5372209d97b932db794524a2903870c110c120c39b4ec0fe1b05d7bc6f5e4b0b7d75ab7161ceeb71f326c99ddb5bae2a05d0ec10445298c1fb01186d2d0de620b4eb6760d73b18361c71b72225e0ddfa20b391e399963934080cc8fda1e649c17ac0632bffa1a2e3b06c80fb9dc39ecc51521cbbdc59aae0b72ba3d0d5e8c86c32e831793e13bdf389bbaed725edf24d7c26397e392e3e6aa7054b6793a2373b7431c9559657e77e83ab9cd115110dbdee62ea801950db3a30bce08da6c3899b22dc7f94c57ea673b7533683b75cebb4f795a97b3c1de03ee782cd8ce6ff80d3ab6f31e70931cd724d3146f0eb06b1e405f37d05704361fd4c868f595d21cf3a355eed0fc8ec8b7e83a99df0725734c94462b916377821c39e4983826fa8ac0371fb7b1397d043c8d24db3cf515be6d5dce0a309d027a1620b38227b7d605b1b7d1ac20f636b7d138c868ee2748c683dd323f0af35830d823f008e8d8f6469311087b0f18b3831819dce5d8e8d55459964dea1d93b5b1b13f34281e3589b2e93b20fa8a4a46311e1189b1e596db0890be4362a873426f93b21cd374857768b3315dd4c9a69f7788beb8a11b6ea81b62c36f78ec7688fc0197dd4e037ec365b7c3803f407337781c144a43bf492ae50d9b1be2e8c83c3bb7e13cb013391ddaf4dc12fae280e865f7c2dc1c0a4be490e38036e7b3e94bfaa43ccee79eb53364c8236883c7daa9a18647703b6db1b39afb999fb59aa35244b6d65ed2cd5dc6146a6c05fba98154ac9947b6fd0a41681d327edc391f0e86ba57487da39cf53a30772a5294cd6d7406e84ee9186bb8e3cc9fe95a41873d9ccf7471db89e37ce8b99f8ee30ef322b8c999bacf74b1707aeec7c6c5bb431e2b5fee7cce63657d9d6cab6f911c99b3d11c95f96d55b20f1d535e8d3556c7f9bde8b0eed7b715ae7aa1dcf12208f32268e7d7820eb9ddfde5857577df86ccc7745e838e1139ec43bc9dc76e473e36c377746ce73e1038cfce25ccf381c079887cde7642220881c76e06ed75939d9079221fe2c9d7e07de75d063620b7bd2ac57129eee31ebbd4e374ad18cd8f0ac940aec949def19bdf21fa829dc2b40e2a499e3be49ab8a67813374765ba3a1de6d0b251cf66d05c981fadef66386ddcc166386b47d270f908e20ea673d6ce3cec30cd23030eddf12248e4c60bf1de2b2fec86363d04bc9804857cd34e1c0d5e0465f022383bab81d4af783562407b3dcbef2d07a6ed5ed0be725453cbe96bcb3b24884c1982840c5394cc8f2ab1a12ba30625d31596315d55a782981f0db26109912ea9866ec8eba9100d4ba62b14922169ba42a19d5f9d0242294d5728047bd5a920ae0986d37354809c9eabf2e3f4dc1c2ff8b4f0c302908f206e4edf3199ae9084ab70fa8e89e90a4738014ec3d6534c9c4c5798c2534ccc8f16e53c71014a02a280a7ef8c56387d27c5e6f41dd2ebf49d18ddd27485351fc0e93b35a62b14c0799cbe6342e1f41d1b34a7ef9af6e93b2a38a7efaa20e0f4dd1c07f049fda080ba20b8d313fe9d02e001189a7162c2e98ed3a39e4c57e872fa727a5494e90a57a7ae6fa706b83dedf17b5a00d4d274851e4a8de90a4b40314d57c842b11113aba0034e8f6a6a69b90faf050afce5ad2081775e2a02bfdee619c91e701626408485a7406485ab40c4070c44801049b1a04320f74144052229bc2640e4379e916c0b5180051b5638016c68b57ee8d0c75978e1a665830dadff10f9d0210132203a7ce12d6d04aba0c31626a20201ee83880f1f3a6ce137447cdcfc87e8051d0a11b90a36b4883c051b5a29fc8788003af4f11f2de8f0e62f9c8509acf0970d36bc5ab081051b5a60e13f442be850c886bf2660c3045420400b87818503b94d912134a1cd07a08de0213f4439435c18925a61888d0e73fe1a42e342aac8906709d8d4a07f88c2019c873682c3d45140e1751b30f3380ada48ce35fc87e8a5c3013c6b23d92b27e028682338478728e021e000fe1af27ae97085e721affc1fa204e85068c8c11a5018721a6d24a340f37a0256d0613e789b223c743880bb5043ea9d36826be0d1ad50834d0d2bd8fc8768003a14aae13c8aa46a785764003c56788ecd5d38cabb32e81fa23086d0847fda089621f51fa2cde1e8f0a2ba4f06137418e4296d24cb60020e0a029d0ce72280a241ff10853b0c00fe10a0c3ee286d843b00f7692339003afc3e1380ff10753a3c4016c367b4111c4487335806041ce09d0c5da7c3d439193aee3f4431e850488623808619196e823692674cf80fd10174d81d01291d723fc05110d8c7a1e1c334e06c4fd180a22185fa0fd1d6a1100dc781c047c30300812f00ff21da7498fad621ea38b737c062b4118c63d0f6c2b6ab00ad8d64d80262d8b491ecc25e3d23d9167a80ddd13f446190f097d146b096b1ab5af575a82fa38de49d5f1dbe640af80a16f305682318b67af985dd5b61b7fe8748013a14825d662706f6d58e4ccc7f885e7478ff52bfbad546b22d74834b10bc857634de8961c75d76361dbadc6a23dcd6487e88b61fa2203a14da790c37681d0a011ddcf9b591bc85800edea091fc10e9ff8768870eb7ef70d1a1bdcb71280c407ebc90e34202c08d838003b4e0b9ce826780fbf07afcc62bc057f05cb7f10cf097d7e3d92bc0539eeb28cf00efbc1ee7bc027cf35c1a55c5001a35c7fc74caa7003a6583b773221eec35783b1fe2c14e83b7a3533278309d52c123e104f04678cb633d050fe603f06a78780290a0038e82c73a8d07f3cf3b21001e0026e880cf78ac9be0c15c7bdebd1262147480fe212241a796cc8f8ea05348cc8f5ec6636921d8633c182d047410a6532dde69afbc6bc60e6050d92f5e1606500da6d82e1e3562072420ec1dde4dc245930e5a34aa89c573216e202a2f03ddb77acffd98bb7bd521c680e4800e9bc6eeaedff538b4eab0b56c6bc771a18e7b207157da059175a09aa60b55e59e6ea74ffd4c1777fa14502a8854d074a54e9f52925a425f114c31d95c1b917b237e71c422c46e87d82189dd0ff1a3cf5e0782f8d126a6316ea03473b525f20c1e5785d2d0eb789d10af036280d72da134f40df03a265dd1fc1ae0551f1f90015e04c3288d201f508837411d2f82337817044cb82696cf1de29ab61dc79832c70e2f141e5f500ebf4d6fd11d9a1fbd4e64f02218f3461c038da65d7bbcc55241fe0eb9a64d3f061a4f76d561e7c302d76433824b4f90f942161ba5dc1206d8a27d2cea00098cb57610471168354bbe7ec0a8088c4924557ca1041ec92800c1184d13a0d54452c6b045fa8ab9e5d26402a095237247841d4c7246459503365c56902d0f56964883064928bd5091e41247c63b1e2581d4032b69bdf4d24deb2fbef44e2ece8499ad3e64640ab3b5e56a87b86e7aea72ac662fe564ec969f91c9f6acd1a2eab7457b7ab26d71bac2f9bce79c7335a79e13cb6c5e6a6bb574cee97d639c73a439c6d58ed4beeaa596f411539af8ea6539e7ac73a26ee04d5a25a6f5d5ee3a6b2837bbc3cbd4b3a80e4af865d58dad4a3a677745940e8b2c1f723b464a07a54eaed5e25a2a287542a9936bb5b8960a5beba04d68b5b662612b9dc2de26376b92615bb1b0d54e816d9563b3d6059cacb684be2fc374e633caacbe5612a7b4c43403c46e203159642feb843a294ef6e2acc93eb1aab8aaf64cda5b2a4e5796edf888e5331a5bbe455e66bae8eb726ef9968d115aec301af94849da724a0f2062b083219440630b16d0263b8c4c4c76101522c050fa84124806c9252590c17652a43d9450a26c19bbd8810c1bbc21842796d0823eb143897404133b94493f524ed9524a2679d9445f2c5fe040a9088d2b7a70833ab14359e50820489f9f2d653883b69c4bb64462cb4f26f2319c434908118300c3f964cb28f447ca87538c2d393276d8b25370ec54173bb503d04e21a1c54e1dd5e5a450efba1c546dc1ee8e18daddb92ea7e3be75395cdd96f6f65397b3695dce4997808aad1d77399a0a3533628c50dbf751c6576dc10f3c18818f0f7855bb4e578cc19235b67d7dd5e348bb9c1aa5ec4a8f9618b12909a46ceaa23d24ed3965d24ec94d4926c193d6bdda85e99236f4d58384174b548e7e40c6133b002f1ddad70629d81a8f2dcfe35108fb00bb0649294b55a68f8f8f4f121c6d66a1882b76a50142fecc7754c4927d2f0bfb3edefb7cd318c2be2000af920faeb86d9d2e9a5d83b0eb2a538fc324c821ce54905b9936022158ea1a48778ee0042e93561cf19249137776269326ee10e7115c91d7681a81173f7e50340232647ef88069ae92303af120001e66a6f15819226a31e3270a951f2e569afcc000889fa5244ab079b47a785249b0b1e6efe3d4ac9d49e5e88c17d401d2efd8d7efd05f1de349128589221f90be6a1e22d8a4727446920f48356b5acd9a5ac73ecaafe5c5246509244f9822bf6f4e99064b939ced78a4648b1d3e4ad6763c224249cb0fb2ddf1480914254522302a23524a29b691c618a33743395f40e5035f39114c45c412823acb61a8b3a0a0c0504b3485a2d243948929a44b38cd4304ebce0df1749a05cba9739ab5939f7ad63122960e77064490d33bb74a113e482209241f1f9022511afa18d004bb1fe19ed2aced28cdd2491df5088614caa6efd146a6345489d250fa1e6fe408d22849aa18630c353acd73c467fba6593adcb777470108f98280c9a63ba44c7788d2d0a3bc5b4469e859442cdda11a64b1c3dbb4299de32ed12ada1239a455367de755ce434adaf428b8917d6a100fa41c41b06b229a41165a04f9f880118c60587db2969e3a5d88e69c53ca39e79c72ce2f64cd1e75d8d272ea38f3a4618c57ab5af1950ae8d9566ba0ee25b6cd815a6b95b156b963144196e78935b1585384494709a7356daa9552ea30bff8fca29fafb54eefc89c19508cb1d66a6b7d0844bc3db5d1fbfc6296d57a6f5edde3f8d2bc3855b0c548638c3ab416676a5e2bd26cb3365545ac345315b0e0849625f9ee78b484156864d4908b144c82586204494b80e02703ad24b120458212d215d429c961dd335e460439c43922a952cf5be4224ae5a534b6e96c5a9cdebe63bdaa05b02aac56bbd2d41935567babdd191713af529271cb068525aa2572580380067dc98739d3ad7557323e3899c27ca8fddb31cf6c5f8fd3bcbb92a295ca5c5b338f66ded46e3d214d6f199709594f08eb150138e923446154d813e38d005a4a5fd2915636f4d3b8dc72bad7ab736ba72f2bb592bebcd2628d4b97535b3a05f4ecebc97dd230cd108aa629e68cd4e75991b9137214dc6966db3639abbd274fd3b4a8a1c29e5d594c403a129d2bca43531daeba9cfa22a596a7b5ca6dc7a79410440eab4b094ca80fbc9060021939c42b560f60646258ac18a4950e725865b8158cb0fabfafd5b2b18924ac4102ce79b5fabf2f9e3341092e55b494ef58c271e4107b5c955465b3fc0e6fcf11b67c48b373bc917b3e55460e2b4b2e2387d5042ec69b952de3c34ce746e566cb27d39ab1e161d423cf208735001c9698be726c92e79d3545f393fb624e78264f9ae7963fd19598f97dad968dcd33aba960e4fde52ad2448931e52a5735a38ce7b765b7936748b9748bd828a5b4de4929cd42d639e78cb5524aafad73ce796dcd26a514c79a38e79c73ca39e99c73ce39796473d239e7d4e19c7386dc9cf345c976d2b693f67b6b566dbdb6ab029fb478d2a2adb7765784f9de7b354c2dbd9452ca5d31717705c622534aa9ad2e7b4a676282b8b65a7b0e5839a3a434d52464c9ed6d9aadba27f86bd91c739a0a466ad4564a5fa9b7edbfd6ea9eb85389ebbdaf77db8d9b188ccc7539f576d36f14756d364f49e8a528da714d3aee0474c31025264c9830c937ab28d7843b4541b52658b5622d6e72ef856185d2dc84a585b6719bb050da429be036585a5caeb6e113c59b76ab772f8d8c8ca43c5549519035d9915340ab76da50a9959755dd4e43dba905881b58ba214a4c983061a2ca2b312baa18c3aaa7a1532eaaa9a18e43b164d2c8f2dcd049db3699982e67b58d4082fc22d7b764adcba9d2c80d122e7f617654b56a17d1c8f2131bc5f81849242d04da5009272d17a12eed295dd5de0c6b432d2836996402d3e93c89e4e5e890f0c30d4394983061c2244529a59716017ad09e3baf628f560b87d2cce3ce060a05cc0f88cfcd513ebe8ad0532a6ff77db3d24db55ad13967e6f7ce49963468b3c8a42d3b9ab48dc2c8f2f393a767d9e10a63669c37dbe33f1ead93a441f338cb20a3d3e7c948bb78d3f34a23294ab2ae73d282afcdea924a490a895150c52231650824a0bd8eb5d65e69060a90b0d65a0b0024bab8a20a1b2c514114501891553bc6ae635c6badb5d65a6badb5f6deba7282284a2718a226c7fa404ed905d8d65a6badb5918e2648daf8400c88c00515d0126d5b031c6c6badb59609945d53701a0137c4a2c9c1b469c182e7083a8d913322246936c0b7084cb0123b580952a24a07431c4c4c909c60906388c91029188800c4c4042a284ca2d0609843e9598d1d497ea02223b3ec78f4e3460990209123d71d8f908c800a238640c2640588bced7884440866c72320137491fa316b3cd5146766def558f572d7b3aa6e6121913f1f62231c38e184500f28903003a8470f29847a40e104126600f5e82185192d49fb7e8b5bb6c45e7b56cf82d1af7c45c7a8514cf6e00a140c01c68ea842a00370e547301d207d0f583d16ac561d8b31e8028c1d9148e08a8e1db120254abd07e48672289b26877a54592fcebc751c956d44d9146fec65934a37713159c60c42f005183bc2c9810270e54726157580f4d58bf5f8016347e4137045131d995d805413a526672d376746717606eeacf9b83b2fdcce9a3a03f39dc621e4be4def47dc59f5e9793de0e8bcb04897bdf338603747e340da5dfd4169cf1dc364d4d4e99026d5eeacfb981ddc967b1f5ff431d2a3f8a29a529934f757c74913d239c2d857ff9051ab8ec91a1401083d80b1232b239002d21f21610ab07ee53d20ab45c7a2176280b1239209481feb61829528759a340d70cfe251244a73bfe251a3a11c7e734f3da4464d4dcf9d359a06b8f7e2eff785b2b3a66d449afbab19d117eca658738f3339a25cff2de3ea0ebf237dc5640ba688a3098c1de9886803a43f328fce006b4c004e80b1233208a49a48fec8fc01ab264a3d5c5d2bc9216da2b234bfcba4e9f7435ebacf4d4d6d441ac3fac3be75877defa911cd3ac41645e7b867698a37ae7d4f9be2cdddf794ca745599aeed281dae94581de620c2fa3303eaac20a9eda9c72dbb1950e73e531e459ab469df9bca997a85f22895f9dd771ead527fbc1a34bf7bda34bf4be7a8d8ab41ccef569ff9ddec52a4fb8f4a4b123761923861468f96136648b14550c564dbac65468b6c59989fbd2c41b6e80be39cbf574264fb1be62b46165a04883775bbfd8a4a6423aa11cb88625cbd5dbd61bce999e95ac03dbe8c0cc6186b322bed61cbb65c0433fde9dbcc0ff3beab7defbdca93992eaa17703ae5fe7ddbe67d333bc8dbb167b9eb61dca1eca643ecd91d1609bf25f5b20c20a08e3aeae0b6ca454dcbfca0c775afa62bbbbdcc7cb930b5f82240a4b1ad58636f6570ae2ae80359b544a5647ef3a12a88c9696e5138e184134e0001cd843469dbfb45d624ddd9bf77ea65956e875e3685bf176fe6185b03736b60eef0b28d58636553acb1d488f544a64854cafca85190f94c53c1a0fccc2faee667edf7034d0a5242e507579346229b11f3d2fc6466d2598df9c97f404b91d2951059fac49b1e5bc36ca9da52cb277061294a4129d0420b179c30c70d9c40a1f73828def4d8516cbc89bb7e55c6b6624c19b47a622313db7eabc0fc2c8c96f0812c81be2ea8ce74b1f0d0d20ee5d0d2e6804c57d52cd8c477c7316e60c4a64be48f8c4c2dc62273c0d3a1ea23b2e6ad64e6671fb383fc31d218c2aefad7a4c8d98c1379ce38914f5204b1c63ea45b461f928898827d7cb5f75e90c3af3e9399963f3e648c73fe96cccf5a8c85bd5acaa0a6931a4aa8fce0aa816b265c196d7b16d561d173efcea25ac804b99346c20c7a8396e8ab647ef61f9096539e6db24c7689d254a13a88afaec69b1a0474e5c38ccab6d90fb6fd0da2af4e674b335bdaa91dde205b6589be6c1c172808dbeec069ab4469ec0ddaf6d88c1cb77da6c674cddb674cd325b3259a31a991ebb3a56db31bd8bf2cc59b1edb3e5b8a37338e6d7598dbd856676bac8c92f430430a279cf87c6009daf434ec81c39995cc2c9a34f4f5519ad8eaf1f1f88ccc5e0991578ae28d0b6956115fa7d3cfa2785337fd7c126fe65bf8b361cbdc62d317a1af939e45995eb23a4c7940620d0c9166ae00069bd28fd2745bdb41c635984629c8f49b3ced30e3192df2dc61deb148cc54b02bd838618e1cc6258c8b9ecc2f8827180b28d355f4aa671118399c45bb882508c7d1b4e7b16542e354c1aca002c68e474aa4ec6d729a874cbfcd4833cda2f530ed208b9ee774aa59550bc9ae1d965d3b3d0f113d4f76ed3c4452c3a60e73a7c3dc998e94066b2094663e467de8c1174f10c009987dab7a882c97d4ccd811b9821cf0d004b43f22c330020c25c0fbcc8b1d69610ad06a22edc311167e005e4d44bd2a2d254872c97445f047bc89123f82f33632bbfdce3d96996649693db9846a890115c0218026b7122207a982e28d0a72e87b087ddddb53cd611264fd93431974af7bc8a10ca23268822c1009d6acaa33b8d22c7abf639fb1c09dab5954eb64bf3163e4ef201e3268caa0e9139bdffc0953721895649052139d5b1b5b9c094748a24186c74b4e6639ecdb82430e2d7b7be0081e8ecc6a63e156381597398b7a76fbec282f6e9754f6d89d5028ee855b71305c0c979d1503b37a4175f728ef749a718f9de54ef6845b2e5f8f166ff3c0995be5897db0c599707bc2c98993f166c3b8dee26a7fab6669afef01bbbfb034987dd53122ed31ab61d6de0dc88d3a6d3fe1edf4f9e17f52f2c478dbb64d6e72db30ce7bdb62a7618e8d1c62b9c41eaf74271de88bb51de611c4aae34db384dc0ba375a8bf0f5b7b7b954b5629bd697caa85ec75c0cfba4a14d95d814738c618638c639350cca9be1cebb0b557eef2ed14e6efd349f0581e8ca7f224f8e2b1b4eab1b4d5ea3b2fb79ac7fee52bfb97f3ac46f058da598fa08cb73acc77568761c168b9b4023ffac2196f32ca749d8e776c2d1bcb96dae2cabb6fdd71b752372f8756776c6bc5bbe3acc812ca746110db5babc69406e32ec51de31fe30bb4f2379df258dae54e7df5c7fa97efd8af34cfbdfd8b3d4ff6964db5727ab52cbdd22414fac238eb70b57d07de70e8e675b9cb5ef7d4b6fd7482b1f565b59dde1dc65b6d7fd95eeda65af1b89fbcb8555cf7ad3b3d76dac96a96e3668cc835e39da21d4a67e087dccf400e4bc8cbed612fb79ad5c15667759a25c4be7ec7d6c31c46f35cce1382798c17411c6f249458838ff14f5f305a26512dbe241691061f67bcb114637ef8db92f9612618e39fbcd8ea4536a566cc11dcb9d4b329436a5cee334664e943ca7cebf7362c4cd7e9abbffca652de4a04a38eadfa8a3777ca8bbb087dc15c3e05e3e948ed8766450eafdd6e6b6d95c6413b0af54c872d56ea727797790461ee923a4ba5594262cefa0ecc65344f3dcc599a8708560f731efb984730e62dcff20a0b095acbe8486956c2550f38f2c53967dfd92f8b0ebf1d5e7ba769cfbcb0b5b3abbcb0b547782acc3bf570b5530fbf9d7a4ce95075ed2378321ecb8b9426c68b14c65bb9ec5e503de3af78dc555edcd9a35a967aecb2149771dc37c9a13cbc3b4edb782d16e677bff2fee2693918d71b9354132b6df81bc6da65e6873fbb1f9ac6dfb8fbc1cd8fe658838f7fc0c41a5cadb5d3da1fa76b5dce067260620ddeb458831fbb0dbcd06d4daf620dfe0bdd3ee9186bf071266ecad9392e08ad168e38e2d8e91c676e48509a6609b1efbe539f9a31225b94a74369e43b25ac718849756c51245747e8e4316df1b215f9f9d8885cc28ec79ba5c343049bcfceb3615b144bb8a062e403f2cc67c7dfa1c77a673ed3172241a32a30ce79b5aa95525ae99c73ceef8bc1a1be95807c5f7fffecdb96e43e6e47c2bd6be1486b1893c20ec86c760ba0d78e3d5ac7ae27cd6209e9be1d863af70e094a9f3a207bb6e65dea619cf36af5e7d0baa735f2bdfd762434d339b4f68db4a20eeb8eda346c8b71faba07d4a666a43a1296af7c38c8186356107b7cac75989ffca3e41d6739102424a41d7f391296bbecd02cfb1d2f9885e53b0e63f90e1616cce2a2691eb69aa6c9586c2fd4e4b3f58e474262bc316567cfb4a05db3e39190127bbb9cf5ce460e231290eaf21a9be666b5dfef783dbefc86bd93f693372313ce0eed73686d7ca9733a7539d9678cc85609d4919df61d2c1ecb57dfd9f116cde3f21d5f691e2298cb779ce7c57a3da8e41c5a5bb377f1fe61fc0db95e0576b0785817995fbde2d4bd20374fac61697f790459eef29d1d7fd13c31df719798efd018e62c3aebb4d4cb4cd7b597f12ed0da38dc638c31fe36feca7bf15cbc486950dd498231301e520f38ac02a60b5fd3e16a656f5b382f87d67dec328b35966f6bbf5f79f1c525df2e67bbec5ec03f1d87e2cc2d87d6cebe5d4fd370fd9c19ceb410cb414c5f40f046c22c48f886f9d57f2d1e482baa6ee61434c59b1636bd5c42a9dd743b459d461b269b8a41b9192332093b3e4ae51e05034d74c688fcdde56080729d4d4bc3bfa659423a4ef3f09cdee96fcb779a673ba70f4aab8ba42c405fdc51dfe9baed9d66559ed3855011b4280fe79831d2d062d7a69a24870fbfcd2a52012b9a6e80831d4aab25c9d55bcd4f665552b68320766bc723276af00412dbb5e391134cec2df549c9b3b5c413a5174572686daa435fd16a17e6579574221029bbd6ab2ad3526592c41d65a9544692cf4a881ce34d0bfba543e552bc61d9f59289beb24f265f52cc3c0c509a7a9928f95072287d92383742bcc4c4fceaa120ca614c1223e367118c01c874492f76bd6609d138cd93fd94fda45bf81ad36a69db83a194354530aa6caba554b1efd9364aa42d94c9252a3e12e8bb22cf87d2c78a8dfb5890b94d87e9d2011f676e8f92b59f5e04e3a91d7524a76f2c8bb239694dc74efaf25b2cded8673c9818d8610ead4d23d22b82f2e3d820cab1e7d6e029426924132b9b70f060d74cb3849cbe7d473bd63c3c44307c4d674a53cfa3fda4c1cbb45ada160ea56d8f9bb69d7054d956d7a3a1327601763c1a62b2b7c9e11732a62d849fe9ab08a5a9c7b5e6645610f94b224b9f6c28c3dae5d6885bce1923f2cc0ef18ed9f177322d8fbfb3e92254f324b84dce8652dce29872d8dab54531ce996a393cb146e26787591d7eb0abdfc596e6b43d6c78baeeb5624da59f3aac74ce1974174e5f276da339ae705c00325df7b10663fb4883bfe122274ff35c9818037b7eb38746c76a45dfd8363931c0011d62f1863e9e27ded89fbae3d057ea3af145628c2bee8c2c1fe299854cb04569e661a864f6762fd04d750db23c8be74274a2f27a641b25e5cd44fbda43019bb6d0991b36fd677338e59d84a78d6325a51447a44e77b5ca811715479bc53c14c50b7a9086102fe062082dd8bc18a1b529c1b192b2e2f8c428ebe20d22322d6c59b17481448417488ab0c47434c6145ddc271f483d09c129ba01041c3c247942e40229a4203113b36663b6a298406e4aa58e04c24a663c7a58e3881f274bd288821e6dc1841aa81e12341182cf162150430a272c68c110906454f04043450a55040d2690fc6007212884a05c34e1a6b80235451725e4e0988a2c642a8e598341c50c3a3732ad1f5035e9ac5849398511570a106040a748819502031b6cd9144a644f9cb62886085200440c56529600067514914419674801c2480a34589aa418424b06c614495059b66a020e4bc646f7dcd793f193d6a37b9b9c95915294a572f6b0b160d851c7b027457db04216a4c87bef8db78935f6bd6f99f7de7bafcb947cdaf1a889346c1336d8ab26a4c881093d940106164c00c1861b2b68c2091c65064df490adb5d65a6baddde10422f0c1a20e2abe58022d55c1b674b8f0b67aa6b239e7c4d16a3cf389e2e0b10a183e289002b40514556c31843a9a14418409b070324494386cca8a3c310f5bd42aebbf5a817099808a2806136104d524a21941ec1084358278d04393163d4ca2a4800a23aee0428930b260c2c90f41960e2e9ae8411319654ca1582001616903e3891a355ec0600635cb3b1ea580083e47f2430f2728aa420935a09830458e347c08420b4a80744513d95be6b7aa7a95c32c335d71cfaf665e5128b12c5ca0060e887882043bb06249b7f1fc6617b3da50da53c41155dcb0819212440455717b00c5142558430a326a409fd43da38fcac3a67416f5ece0032150e9e28a376c3044c5ae6520595a2282133f208132851c4a2a1213f4109186cf075870832448554891ca01164080220437ea78428bde403277987b4bc55a755ca869ccc1831b4cf1220643b000ac51ecfad77ff52d1eec2a04586badf566c7e8e3846453399348614c639c31c618a31230c6c7a8b100f65c22ca9e938629cb17618b72053926c121c674c598f406129bfe164d574c8263e846b94f361090c87c3615823a7756fdf6ae3b77d4d67573ca773a16ebd10169880594802031452518f74e75ee56c3622cefded3758ff3dc77e43bcdd35d5eb32a0fab82369905c78860a9ab746c4569282c3fa5635fb6c11a7ff311e438eedd77b8779a8708c6f2ee3caa73dfe958de3d82a10d91fc148bc7b22aef761e3ee7652b12ca09480e2d813204745a72872e94bbe25da3958772c9cbea31ef01611ec1eca184a25d3e4604ebae7a8c084604c3e71e290d8ce59a8e492298ea2c3a2695381d93489a43e2943ac9f25056b950bcc9aa7a07a559f53de089b5b35deafc64c7ef01b52f9de1840a1c4c7c7c40d45b4e045bf9f66d3beab0eda821d8f6161d9b407a0e2dd91e4ea06d88023155a1404cf430de85426968bd1cd3acd23d82dd430ae4f2b9f218116cc7372004802d30d45d74cce5a8afe818116ce5281d73d17345293677e81e24d8a291548fa0eae184a2d22cfb1e70355934cb6a9d9f1ef0425162ed4870bb04b7b3042f160e220228272d92b4644142b0a469a1c40817096b1125db204300655a88f1128488242d7290a0202d8420130404485186c8740c4551424547d26a0545979963f5e24392225a90554ca490c22509172b51248101193c602923091c4b0481442584ecb2e351104a76fc04f1c41a416471c28e474128b5b1cd4cd79430cc3d5da747ceca8774f79029d8c7c7160b93c69e02a0460e31ce5cd11ed2c70a31057b4d87323bb4d161cbbe3adef8883536ecb1eda7b6d166e44bef9557d28839e963832450e62187b926fad08929cccf31f63c8f7fc660cf8729ec19cebcb0d68933c968b35d4e46b33a84d22c26654372ce79c8fc7492e88758099315365d6134da436ef83192524a5cc55da2d6e3d7c5d9c6817e94c6f6a8ad4a29b5b8041eb27c95f79115e4844d2f773864c77f404e2c69b5f6669c7c327db06199654a69b578ba22a594564ae9945d1555509b29d572d1c665181765455c87ca302eca8a50a90ce3a2ac28956f0a955516d5a9563cb0edae2b2c2d3b328c81f8e5b62e88110d06bc71d8a1715bd6c4cdb2286e1437cba2b851dc2c8be24671efcd6cfcece365ad37cb8068acbd34b2e9e51653d301526448acb8f57d383636178a09c5bd37b3b16e59773c712b0c501a0b501a127e608a1506bc4f9a8f9d63b5c30757b8e08a26427045112c43c8da8e475780e08c2b3eb082841575b0c08a2a4c488c333e6eed6905d30eb2c78814c84147113f58810ab6884540e389105215ad20c1aecf5d4ec54033ce40c91060cadbb51ec3510512bb1e9595745dceadf51c0dea182e3b94d9968a216ccbc5b6f6f909946dbfaaa2876dbffd45945c9044173bf8f1620a174c99637b1417044c40094e50be0895b74004c38f8a2976ad3fbdd1ea61430510dfb6d7b00a8e88e0d8828b25592c118389335a72ee9ce28c14767d7677b5f5d65a6b6dc114444bd041833ae87002ac61ec5a5776adb5d65ac60eb9996d371ddbd2601f4521832227b63d2aaa63c7232964b0ed14466cdbba3d5414418e39866882031f4c0163dbff8b268a60494d91c3968dbc13caaeafa732495690851b499688d2066891c640a349d56a1d0a11002036ed7882573b48a23252439228072b32e21c20519a8889deb831a8014444841870b004850f5c0c39685a8960a5c960b58374f1e9210c97255966c7a31ed858d1417e0142810f4ad4610592bcdaf1c8873058a2c82d3b1ef990839716e41d3b1ef93084972af2ca8e474041b4acbc1d8f8058f245d0be66c4d75545f1e600fbca36f6956bec7b6ff79b9dfe6ebf9749f475fabd54fafd2a67bd6511c639cfede4690bd8dae9254e4b1f4a73e9fdf66deb59c0fb368d34f7276f8b34f7d8638935f7d23361671e0a9b7a1a907bca145a26cd95292a32bffb1298c8f6f5614c7a325d4118df7bef755c98df3d4ee68120cb227ab3383f7a5914c6a538868f2c8a474eca8832e55e23eee37cdd6b389341452a305dd40b4af345a62b411a10523d8151fccc27470e4bd8f3466e99e1446bbd6f863ebc2bee9d699e78af970337a36de6d7b2ff7ab462122d21dec8cfd7232c5e22d753d5b35f2f5b7dfb8639b4ececa96fa9f9a57e5359eaaa94cc2af36c5a3d5a90a73e23679740db050f48958ce2beddbe89d2be5ff926395de7b7f22de5453a72ea757eaaa734b7e28575ab74c862739c79bf7145c87167ef7272ac69794b264497b35def90c36814351929ea5ecdba1767729b773d7a3aae39dec496cf4608d9d678d35d75d56797a3da36d5531e0ef7aa6f77e535a5f256529a1524f5ec3f523aceaf45872c3a8796bda2c39b7a7ea6f252bf3de697d236f393ddf64d8b525254a763acc9e8c9f358f7b18d3dd6d53a73cfdfcfcfb67ad8c82062deb03129ced82c3726a298b24dd8710c28d8a83f8156e3fc093b641ce8a546350edba5c661fb45853970606fbf35d4b0c6999c8703fe955e38391c650a51a6107a400331d6d09e4db5adfd4c97dcb76f5baa63a5f40b73a7f2755c404d7fb2e779b4e2ebf30a68f97c2a773054e9e34a1fd247dc39bf4e17cb737ef64e41c8f9f1dd33f55859bf107758b7ac2b18cfb0a273fecc7a667e1167e6af49463d7ced72582e3b18f2bbaf78ac95cbbd72168fb5f2b85958bcecb156e4c89f9ec9f4f419a075d49153b9841f322acc1af66287638dec5ec81ee7a7bdc5e32cb77971ab8af2a69fae99b124c599cc6090bfb7ba65afeaae5ee621c699b93253b669bae68e74670f39a12b5f59b44c9e55637ef4d22ecd8f3e6b91439b14daa54d6f93a6ab8cf9ca244b6652eb648fdb00f326f9f8f8f000c67af963dbf511a9aeb14653bca953367eddd3955d3f69e4ca96af5e133da20922ecc845136f6c6ddf1d5f2d2f1e60cfcb26e2abc563cf4b26b6a8624f1b2239918acc8ffe27c8af38287b45a457010bcc6f4a195446964b661c39fb913e75028960e2200f030202fab99977650a18389dca3869f993ca22dbc3421b634dd029c847498c1c59d33a649a035b1c790155a6209fe92b5390414e90d82aec78d484696f93931598dfb480f42195c8fa4df32c707a23e31f384849c8811d66f2f5a7ad48052c305d9a964045a8fc61f190bfb2e318669060b7047df39b1c30da8c8436a399f9cd2ccb212b9a210f984eb780ab65f490f9950094e969c2c8e169a4d44983aa428c942665db69449bd8f898cc22f9efb3b149c520df87d328a39402d1170bd7c35e75ad9a856315f78015631ded511ece1c9e99cef499b816e490fa50a0e98a940dda343f261ff9951d4aa61549139ea67cc91e481afb287902914a9dce0f924ea78e84d372c9354fae2199628d65a22fca4469ac3d1365caa80fdd21d2d857592d2d838df9d9dbee47a6a952f78352f9b1146becab6cfb8d7259e570ed72369043d7883596c220d6d8d36e032f30c51a4b39cabd91bf5d32514ca78fce55ef8e64e5a8c72ed32c21214d92435442a134b668fbf8509f6d9fa7a63eb1c63e9c3ed127fa74cf6eaf65de048a359d9e3e94c63ea33e4932a53ff3fb8432f541b1b2ae3beaa82ec3f8b17a0e6b4ec7e837dd039ebec54e3bf64e3a6ce1675ea843332f87d6960762918e718ce1577d5adc88d2b2e3593a18e45979b7cc6fced9a253b26013438baf1d69ec2d40b351bc6961dbe300a12f7afb21d6e6dab45819d6993ee918bdf6ddda6cb4d3b39f748c28d3f427cdca1e35cdcab4d0d6039ee2bcfad5f7dbf66a9c5863ebe9ebb5c3eab5471046cfba5a67c3a134363d8ce6c3d63dc2a8132705232121e52c333f9c354d2221d1971cfaea2f18e74c578f399297c31c67b2b4afde03e6c7e417cdd25e563af60293b38c92372d8d9050b807dc7498376623cba1632de63b307fd13c2f8779390c3e0fcc573a82db59ab55ce55347ef849a3e9d2347e828dfced1be6aea6855e2412a5c10ff190ed729220e12f31abc3ac34b67f19e1d4e508c9e46ff925e71c93f3595e4cf717194f8224783de0081e6b5b1df6f2558c6883d1b289d2e01ef0ebc4d8b1692d9bc6b269dbca51d72e91a60b758c92f122a821a19e554a252a82dbc6e1ae3ba134cdbb40eb9aee3cd676b9bbd55d1e5939e7fbd563d95f5ab495fc97d5107d598d35143304933724244d43d290b286a42169489a877a17a250ef4e9a97f3cbb6da60b6984dd3613eeaac18cde6d3f386daf263b7e55a4fdf50789f4e3beee53b2bcdda76585b8c687b2c83d5bffc45c7eeabd0cb91b463b9f40512929c323fac0d15cd0f5fd3d2c9fc30c6d307c604f7de7befbddacdeecdeebdf75eed6a17e36757cbb49be955acb97fa1db58c7eb837bef6d2387ad7defbdf7f4d8b1b2efd86fdfa9efce80089ef40f924c0bb1df3ea4bebbef384f2776ba6e3c21c60401cc6fccafcc5918cbdc1e09eb231c67b25297790fa8ba55b134eb3287b1528fc19c95d230d6652ea36331678d30e1c85658a9c7a4344bc8cb610e7b39cc5387d131a29486ad1ea363f5abc7ec5fde03a674d8daa9b364aa72d911e3c17872c778ac96b3de03c278ac16cd1202f398c3601ef396c7e818518b86c59ca563f5313a660fa37bc0889fe93075ed2d5dc8a29338e500753a187f7aec344b3b7da7fb49f39c679b27eea76c7a27add3b49c3d94770a75b6a6c33bb3b0e73853152587ad6fe6337b12c4d9877c9a436bcfd7e9615a6b93d34fdcad134a6a765a7d3c5a3d56f4b57a39f7ed1bc7136fb84bd4376f6e8ee3329b65dbca63812f1e8bd32c212d2d3b46f88e11bee33c326fb98b86d23864dfbeb5b66ddb369617e3c1acf25f3a174f06a54da8d4ab974a8b9a658ccd08000000002315002028140e87c5629158248b945def0114800e87a6446e561789b3204861983286106300000010204000608686680338914762be71e50111a9bec614e215b2e050e215145f71e0f8563e05ca7e350352bebc9f878f2e0934f015785c4798211137aed4bbee53e77cc7f2f968dc597d22414b3e78546daf014ef2cf63c5b3460c50b623a95df5243d18f29fd56d542a71b65de142d6a37a8fb226e71d54f577050837f51ace927d9f9b7c025d1b512b69bbd2c2312a6705bc82c4aeec404676094a07cc5c757405b224e343334e242609d71b084b6523adc9c18b212296a101edbd4805841ff35963c2855e11234c2c9d146d3f76e245350e76efb57844a002e6f5d174fe14f0410b80df2e2405cdc0450cbb9478b16237c627735000a0ee22dbef5ead9632541e9bceef65b775379025d833333516e97b87a35e3afe32b669dd629c4a0aa479ba8e1993d82a48d33f18425960f41581f08907c34815dcec9cc507c360e00b9b843f9975d80243bb718e707c6f4a65f4d90e6e2c1866ac8647ae72ff7add759874a35c5da48ba9015d397eaa0fe3559289ab4cb3d63a768e1e5835c7d81d32e38cf60d1dde6fe2bc1bd76148fe738112614437cd9ce7836799c799db06e30b0fff9f6afcd58e3c381362eae2ffe415edc679fde5f945e481999b4759d8f826c4dce08e91ce05c0220113621abe5dd48da17bbb2b498489a44dc11290418597b542d571c1e091997d581d480e66f2586196e56984c7fe8f5d43cd7015aa058cd098789b92ad61686b47848aa432b9b951c47bb763ec8fc08608d47e09c10a480c54ce1d164f560f28e2ce8062795bc367d19043c08ccea326d360225f7f184ae33dccff9a8220e98dacba0d71acc09dba68429001a8038203ab277acfa0dff3164692c6077b6211a2867ba7efadaebcaefd11f515c5d5babe8809a919ee1ce93637d680193bf8df9f83401c9988127388aceb658b14a21d0b5d90036efeab33bfbb831559dcc304fe6e32454a6ac22cccadf332ecf06de460ec0d4520dbd3eec79455a88cba6886fe5feb86423e945858830983acc8bd97e90be5bc991ced8d11cc985778286b381291e6409912c6f0121523ea458108d5318ec72c3cb7f349ec1b1253177ce380f4b247843dfbef20fa77ef0bca02771bc1b21405ec6b8a04c128f8ea0d03d477a3ff7b77ac1dc7fa7849b80b7e17669d569788dd2c923e32abc56b3443a97d50394da3d92b7f290d698e23fb5fe609edb58674ee02243ee882b0ad700d5e560accb636e898852f6437c17a5726efc2ae3e8e6a32e744d6fcd4c7e2fe174c6e915f77c0b5f7f6ecf9863040fc88b2aec22dfca44f69990896cb25af3ce4215a707f7fcdcb7f7741d3b1e1ef1abfebcee4df83c749d6bfa8226819e4bed5c1097ceb8682598b607904e01558e263a0b3f79fb00004928f7fa59c79ac83b3d8b91139ac9080801f17d0984dccbce4212fc6ef30093e292ca6275dc6033df9db1fff62f59ca50f558f56b8c605c0770bc818ef722813fce952f30f452a891b44d5dda848093cead08d6a2ae13d00472246888651f14d46462d7c84721327e5909250872d271a96dd7aa4dc275f40673d0606b44047c4de11da0eb62f8dfbffbb833c0dad2d012d1099e0a8e746410febbb2daf8711178add2bc322a78b3cccd9f260848de8acb0938e68e40f1de62ad2514d2b2b8a4b91a41f07879978e122b18d625978cae76829f13dd1565bf815d3870e7c18f2ce384fed63e09d08d8c1adff70c0cabc69c1a5bd8ccf9cad669296031fbd26890531fd329189901f69e68a80cc8157fcf4dd0e0dbe58f2a752e6716079d6c19b8739b7463b31b2c703d649b7c3a27a3c15b6c07d87b76f6ebf83a66ea74a0252fac18b741aa28ec42a9efe921d89c0b423d911e526f6cb9039ac22172d3990c360db298a5ac0775a329eb2cd478165d63ab431963d50f028d060b7aa90b7055b8550f71d05e5cad9dca2759e3ba73882e9e546f4e46e468aad32771a1d7a34682689ac843f2f8aeb44887e3ff2d03219df495b0b97ccfa204ecef0f811d716b84ad5f28e063fe1cdfe4f98f89ac82ba2ead8258d93dee0e99bf2f0a1c4173d0e1646ea4e4b40b161f1a6722c58a6bf29299b7a34c2c470d8b8ae7105c3e98884d5dc70f788d0a4dbf3c8b07a55a059154d098dce3abc2304157e92d90709d69897e80857d3f478cf62e9a97c9988b0082ac33f1c761c8a8c0de1e5d2b6c2c879caf4d3e1fc667c33a5243ce389edbde0065da1486ccd2b92b0ec24064b0b69c22448203e91898764613434c533219f8f8ae818e7996c16b4e68d33f25865faeb27679dd8181535147649ea8639d8203048f0b128c608799d5554685003957ece2d3ded4a448e58c367cfc595d63b3022d28b5e5087c4909611d54da1128253bfec71158942a2e1edbaa762b86aa67cb0191558f6dbc80469a48b749a0a1f788f438f3ea48e355d2dd4a37741d76df938c5c606085ead12b54185f0e7c36b858d12a3a037e3cc8e8a95815c85b0d0d88130ef244a8f7b7c7759f12b032ddf41aab695c06a21f229ee819a2227104322b4d2fc17f42f440ead117a5cbdd62b8ae3c956ae32240ce78d7898c75919127bc8e8d99c15b7679e4577b56df09a8307b052e79c8933bb255e03c110934ce1af8d1186cc89d829c498bf4f8b60494acc24f7e481cc47924e66784a7e13cf870f9d6ad4c4983371cd36a97473eafe00796bd10cf3af6ed01d0fe687fcc6bda275d98a3c0ee15f6608a8651d9c96f497745a9a66cd7d57f222ab0ec1600d382aefdbb6a522269f55a361356f2c3a0fa042b547620a0face689402be4fa212e1aa7cc952637a845eef32b0f77edf226aea9618d88590e76f5a62c0eeabdf0857543fff9be72dd9f6069b0cfbaea7cbfbb3330ad5261d483e7258c578f47b67212851cc8737af22f5115e7413a9144d2d450d9ae73c7ad68973b31fdef517b898300ce08eea1ff1dde9aba8c827871719a3ade003e977c5327af2979300575734c7412d4da900073c0c093805b95846b7adb757ae254c879fdb25d89677c2d6619596aaa258931f489369e2a5e339a7b7c5dc9b67253334e035b22fa09f7cb73b42bc85b83915a5f720be4a8b8cd28e51adc3fbc1ee1e51da5eca36d99c256528ea4a0a47d43dd7292c7e0045b7cacfa85709a2a4e0d51ef5d1bbbcc02f89445108fabca36652a8bfa379b8cde91e59836a0a7a6f0f6ea0ab3fceba39526b6a999b909c6fd1f9e9a12e4529da99b68c0d687e9e52fd5ba0719e84dcb77c9b0f5565af01030afd863fcfe3d0a1f69fa3ec86a70477b1fa3fbea13fa9748285b09b80a62d195e252b4598eb5dd2a0681241e0dfb064017954007a2a25e0236343128858376ab70440a97e93c4bc2142018c5291b761daaec49696f06d0c8e0d06eeb5f46151263c35c92b36fea83d0ad256c6abfef93084ea051b417e5945a181521c160f6ddd79bf697f1934f2659b886e0eb3973463a36e334648306ba3409a91de44a5daabb66711c379e15eaa3875e1f3bd689f95b45df1d3472f30413e709bc04e0308652a348e2a58281e13539d447ab9a57bd7d360fb295b1237725f0fbd6b882b576ad939a8fc86abe8bac74b890bb8b4cbb2f3b850e7650cc189aad19f7e004534a824dee89a2284913594d3a0e5a283a149382c4c4a0f851ab8eca64ca3f6e1846222d01ef7e6c6d76a7085089f832eda8bee86b68b83459e6cbf2a84d034f045fe8e2a081c2afee5de62f73c3289f0a8dea20dce94b4e8978dfc6f642a9adfce3381dbd7d40591696dbd49c41a2c9878c7ab5b00c5a1d32de3a9ddd15aff9c2d5491868749f6b688688aeb7e3ee940b10fef1ab2441d5b4950f92e56214f35997a638eb22222de47925a75abd512d25ce33f2e9612e7e1097c7c6eee7cabde2b93532177704564ac0aa2096edb45a59761668d96d33adb6568a67dd44a1b23cfd2525db2287f5c24039260715c0bccaa30334e5fc5705fc71212098537956cc3af8b41bf6ba93dd51c13eab7975f5760afb016deff36a2aaf0bd97dd46d51d615b56ac1283823c9cd0015fff68d5198c7b423f9a48bb414f76f3db94e2e882c736c4f0722653f961fb88126e2370963a900d18972fd7bf09bcb74b57c2a0494e9e3e9ba8262b16eed3d524f7d6e3bd8d30dbf0009a51b3f25e5d2c5644b091ab23430d0ade0902d175c2a2866a30e4f8de4dc74d3bfe62827b12b4800c9e37d551e749f592ecec4627b26312e7c24a04b8fe0785f37c015970ff4ff54f1ba61b716d74ceb26ec885e5a74923964dadeaf2ee60f2f7b210cdff4121853854c4eaa641ec19c757a100db719da08ac0b7dc02a74269ceef884de7ed3081b6a1430e3e245766f04bbb1ba98427c4af0311eb8750388638ab723ba8992051cc7322d60f304654e9d32e0a3d262efa7f9a98ae8af8d6020256e78f7ea2f5c392cbb43827ba7291cdadf591a8f8bfd8eddbef93da4813bebeff97ec0c7f80fda49c14ca278cd4c4d44fe0bae651b7bc1d36551c03e02b2e33cf050b0c9f54b237227ac2143149bb73df875eff949862cac31b30d3aad7aa4b4e80cd1c42b87bf14f58d294de8c67336ff17475b7156764b05df16c02eebbf7ba489622aafd2896b5bec5887945e3e92edaefc9be39486e26e7406a58ac4ea749135fe61af229541ff1dd87aa1d7b663c39ae81adabda4470d905a0683725c9bfed0d4742bcb30eb1eb06a52a509ac1f8cfc9f42d61f0ae6676fa8c28746ee276f2e4e3df9c24b3b9ea87955ab8918f67deed05c903fe7fcfd4c8f36c786bd16109fec0dd3bb02b6545373107978c2ed0f1604557b83d970daa50d9bea03addb2568441e80ee3ebd4f81583ee7170cd402c555845e2296bd796d8960761b603ff38ddff14917bf169837afb46085180a25ab6c04e2af873fc2d2ba463eb83be7c328ebb21d4ae4b609942795b98f2ce5d10b3a6f6aed227af6e732b48b1847ab55907caea75f63bdd29f7882a4d30834fa354fe70033e706e5314aa09ca81e8d4e3732a56504747a1f13da2bd94e451dc0dd23f052e636014130619a81733304a8804296627a04fbd8831e807f341d363d7a9410a2877e07477dfabf88caaec7890b0f4eba94b967338077e9f027bb43e9d76c1f9c9d5ba8a9e6840d001e4e68cd04651123b17e95e98d2ef2076005c2a6a245a4dd05fa6f5db6aaa528c412870eccc2e625463246c3b05a92c73492d468395dfbe632ff4498cbc4e122a79d13ec829531b0d0459159161700a13f5d684f93f2606727ddc093c3476332917230739f6ea308db5e432e5fb74c508c9e303852368076d538ae2697e89ae31768aeb746a1cfe9210a998dd3987448d25811865e2592a677e9fa67c69b9cb4bfd51a4bd0069ed149a959669ea93d747f82d1838de3f972853bb70fe05ec1121b5ff97c99b93b32a056ad9e3bb7c785370b3ac5874aa9a2bb5e6b96e282a34bccdbdfb2a6335ed0afe23dd05a3475c41b36b60dd69521f13600e91010e81772b1c1034e866c2108a7041dfb5ebde5e90fa465ccadc693c1576ce03564f66cc1466b3f8447531eebb5526847abfed3f2994d6c1342e8654d131913be0b43a80e795f3c41b673f0b3f4d0e5c3070d64ffc56e469f13d13bd9693f33188fa502c64264d4b9aa91428e0db587bfe1ae3df25daeb42422642238a9fbedee8f923565642a24af5a6d827ec7721983be88b2622eec4d78123855150aba7e2fd84986bcafe0194031872236f6180545c824c63c5b596d7d1ceabf5ae0e13bcabd70b4d21e6b49fab85a11d27607d82742b72a02a8bf7c13e1cafe5022575cc7aad774c1adef4b02a6fdca85a8b73ea23f9b6ff426181b66d4d970bef455fcde6ce04fca48b50818e1e4d2cb8ebd7b27764a808815b9bc32021808e7ab80305a97ccf6cf2f6e409c882e48101412968e913c6efd25aa2d3bb9468736b1426cd98b3de80603ed0b8a651a5184966f5024472ee55a7c5b8d64537c7c30007bc7872cd971250e9e629c563dbecba59a21d54805f80d61227cd2cb4458b328fca29736d40ae880128bc802b1517e7e6bd0d270bb2a21da9ecc45a9e34e7eac929ff266c4d75d3b278ded98e5fcd43192d58fde5ace0e79120517de5f40508c8c70ad6e70654d4be8ce4bd1e4b845fee57d3deb32e28ba19dbcc5bcf0f6060a5017dc7ed97897bd5278bf3659994b321e663126a3a3f6fe4aa6f2af32357a452fb731d26b707f8694e0efbd45eba0bc80548d09ea26f909d7bf2eef4421b40905988601babdc7a99419384ed9cd69833534e6695baac384396c4a4b26eae158e09b235d2e6715050bd39c9932885b276144d3a6be9b371f3532d49d381a0a4ab60b764d8e7f238cf81b3b5a38a1dfb0fa0beabfe3718a6a24fda7435dcd309686c2f1739e186c1d3e9493484667e01588020de35e5f456bc074603f500a7b7248be629ab2ca9322b35f085ec6db65b47f16a728992f7bc95181c4fc4a730396823e91029653feeba9ccfca96a390bf8d89cb5bd08a20f8911b43a464ab2db1dbb1031ef6100bed8f60d596aa7236290a379d3e20d5d6fd14ade9e876a36f2a886c92bdb928aacaa29531c936dc9232b8ba5c1b24f763a1b571d0d75a551f80be5554476b626551e96b5a0f202a38ac5b74d13b71d916f11a629ab4d0c405543708a91a0be84b283e6f5f27987659a4f6390618715775ea885771a5762d2a374d90d5b352e48b5dc1721f86bc22317cdc943527a8939c530814118abb39091505181278986d05b81aa03eac5f55be2599671de1b54cce4e79c0083425e21e908c2e89d8df12345e77d9c7e0d073d36a121bad23306010cc39bd13ee35be0df3522835c8c68f08376ac5be10817d806d7bf0267aedaa146694d5e83955d040e36e5dc8a4a42a587a64349d4607bb044104b15f63d0d1a387eb0a95809df81a506b1c7a2446c916e2f087dc6be2b64c8292b85c915c8ccfcc08aff124c5bb38922baf779b3dc264369f47cb0f827509a3e8d87586c7625847917b180775792ba975fdbfc007adbfdfd461803a87752162826c29dcaa2de92c1765ee2cbd15325d16be206a858348e09d6b165f4f4e661b0aba09f98a8fd73ae65fe4347fa6d8d424de4aa721373f44b4be781242bedfb609bb17909917baaba433892a483379243e19590f48276d07dd6dc7293a8b3daa414374648bff90e53eb39d70615e20034adf484f9dcab6adbd015aa84c8a38d44dc5638d84f70b6d2b0fdaf68c09c8cd2e58f492765b21a8fc4dc984bdde9dd462cb0db4acbe246b0d6c97ad4020163ccc2f035833bb1eab22619a883f986356a21a25f2f8b8d1bd02a8b881b650001966fa4d614c040a862d2334c79fb6d78a7613f8bea17cc752548cf622da60f8496ebb85cebf2cef65673aac541f1ab3a197940679de54dcbdb29a29f475568e572db7be66189698bd95323b2284f2cd89397e538c5852a675649bcfb6400061ea4ac92031937638ab6b15c3add49d19eea232adf6805122fc65a6b17b5e36463deb256c5a9fd9eab32d75ca2c5ac255fde7e934bcae44e98985868bbc6418acd1ea24ae322fdecdc7cbff3c119dbb165ef69085327b1f35ce366787fd48cf5fa7e405f692c91eb16b62485719c975d60f5fe05d677a0a57285750c6c3c10e7f4fe8b5f06eab5b4a17c64808dec7723d0962948257c62c8715bc97238596e92d5b919ef82468635801a8d16aba2dfe75af63daec166f9783dc2a989d7625f0a704a13c69872504908c65ec74821480491d7c878978dc3cf569ebb84b6f07524a39be180d45c0a535d54f6e5fbd1c08a4c2dfbf2d748a10389f33e460a4c73eac43d67dfaf8fc0ea410874c4be1d1268f0911316fb53f9e3758c3762c6480092f3db57800c7d5b2de844f37ef73c5ab213131f30bb5fadc00623e8f461f8e417cd6fe61adff9decb2fa96bb138c16e8c0772a4b40121e0b40ead1051dbdcb32f03429874b9837f041c096ca817604c1126f0021407acadf739d21ccf03b001f13ce07a8500db8288cc482040520aa751afc90b81c09f3d8a2515bb5553de9e300fa1dd68874070a66786c45dc96ef042e901d666fa442fe4b1ec47426bf02da5207ef460f7975ee4f4ef50165f6dbee7f174489b7d7c38e55940c91fbeec0aa6004e987e83a39d6201f1d5ab2575a45a02df2f715f912bcc6fe4d9423811c369546186a190fd9a2c4c8993d86514df2ca8e084b3699730240b46ba7ea1b892dfd377af95f69fc4718c20490d6b1623c6e5fb1a46c788a97dc74ce7a9d5541d869a711e8d36ce800be169fe5fa78a626c60c3a9c8133ff775e0a4ec867733f09fc240e8e3a528aa7dd358ba2e710bec6cba710770b0d62950cd8477e9eda1b98e1497b5f496d4f567e1de5c9ecc8549609492caadb8b252951810cefd643275346984aac4a2e3eea3eea0f21474e0128340ef422badb12fb361747ecb853d3406f4e386debf0b3d8b908d8a4d139d164a191f3d4728002c4e7a8a2d29b2acf774085cf661230ffdce37b177cbe49d3693ba2faa60c76e9356d221219fb90bddbe05000b77a4e412555dbecc0e198516ff13e6be4a539b234900a6f0869cbcf62f65eae3d322cb972679a04271a4158b8d9c52972be8af7862435ee9b39393fb46d6a4ccd1170655b054001a83704628c650bf0393e512c107fa20b4f1b41d4845207ad9a1de36e6c398f51d29e497165d70cc800815de9d2cd6043136c837f4f884643a890149322542d6d978de65a38454abf4e65694568307793fd211d284412f12648e39e9bf84c60747a844ab88712f3d09ce347fd3589388d057f3c21853a1446d935d04fa548556654cfc41a01a5e940825280fa9ce7b6823f3ae7f629470058aac3ac68a2244fe4c6a1c49af822d2aec53f54b9488dfd138883a283d250068573efeaede4c85bfa24ca7c8c913fac23726fde0f23926c31d6ff7ae4ee3ef4a53bbccee1e7756115976e6562496d34f51c4b7c342617f19a291c3f4a9e27ae0bb08df1da93ce3c1f0b7e6826768b0261652f46554a2a769ea1921f8ee6cf488eb2a1c131b05a54fe4c7faa7478965e5c45df35c65205d716c9fd67ba77c6b78b4c9ea306d85bb8e8c88619b5692586edfa1e3aa534b43ce834c22beaf16241e559435bb536e1b498d7e2724a38009582a8ee2b5be6757b03e8e10e426c7e9f7a78e220af060a73f1f5259700918a9b1de109b5c338908a3dd78abfe995c5dc967ceca279c0005151dc4340ff66ef4eaff0700319c960e94240e028877a41ed287ebd54748473696dc5e94f2ef9673aae81fd7d990993c3e0b7c2833a01026ae8ae42895e4dfb0fbc2439355c227148bfa1a91319d470fa86e0c305d7256b51eb594a60b20d0529adc34d7ac4d34c772d8baf98bc3e69c75ccb1734fbfbe2cfe61894c3b90efa26663d8cf72df01b22bf7f310297dc194d07d1bd06ec206d8c63b5714ec434ff9732b3096631d6a00be36f324063468912a91b5b45adc8f2c3ea26ba42255f10213d4fca4a28b7724e980826f81655334778ba950aa4b3cb29798286aedc1f33ce125ce42e37f540669519f89aeb6872ac2b6660d4eafe153a7f4c13e647e3444ac6cee7c724f4fd50f5e5dd52404ef77f35a767148191b9a54163e69963ecc432c69899eaeed38428d61263aa7dd0dd402dd3e76624c232f1a0e262e7f358cf5f27e405f29990815128d203e775ca6e0a55a7a3fad19b9a291602d00631c3163a7d9579ac5e4111e30d6f2d4e06023730830882ab691e06c3406675191dcbcf43a7a4a4905437ffab2fc00c8afe6daa861e498f77a72ac8ce7ede7b0cd6e54be1b4a5f7a4a191e895bc4499ec8027bbd8b53ca39fdf144e3358e03a8ed81c9cd266bfa89a985936283a1257b05d3ac7cd101ad60ec57b192f92e7f81c557c99840768698d7dacf61a6d4c469b5424135b77aa5bf88ac5d3164464a10e2061c28a404ca04e462892f14e1feb29f336f5c9242a8aa960ca8878a23a9baf8f0856453042f5a4e26c67fb737552e0082ba47d91f825b51b9e67e6aa6c4b19e164780ae4c730035140d3d746fbbe2ff1e6556cb2589faf356ac26324545e07bfe528d0bb475fc98eab6871a29d1a64e6e7aad425d2082564ee805b34d99a82fb0507e83891f0c249d90d67a162aee7d4d9ee4b103915ac3f78c65ed70f8450cf6f1d93771f83598d8f7b8f50ac0e1a3497c92ccfb8f4eef0a2c07ee449fafa8fc56ef0fda4a49ced06aae1c82ce3043080a8d06ab10399ec3592207327d6877b271dbf9f49b287d360a26acad2f720e1444815111a7f9b285a91ca7e8108773de367b395d616508e00fb0fea2a2af73ab1615c8435833e2d980d5b282568e62c1ea026678a3168741afde56dd65952979b0b57495754c2174a5494e3c44e1559c411b5325e948fb85a50658d4b2eec36d49a4b152fc5861b777e7c63c689f5aef0313f6e2df084af6e0186a23f27a8b4a9ca59ae0855808b72aaaef0ffe12e76b1af24102e0a2a6c1224a2d77831169214a0d98506ee797d2cff475b119c714726cb85dea9b58e770b6697ed27c9566a07db8fffc34587f2ca1adc5e308793cb32d11efaec825e8663951478fcb03242344d74671d630a593a09d5af4cc86fe0e4e3c472c3d457f496bd76f8de8b0a8fe89b60fb9d77de18fe9838eb13e9fbb4b13f534fa56430ba10d35fe1d4e19f2cc7b51f3e64f7f516f95707bb4f93a1c6efa7f84e7dbdbcbd74323fd16da13607bb60975e6de24ea03e525f50edf6188c9f96e541e7a2de62680d18c1a86885c1d16d61ea53dcf3959b1f183378d6c54535ae80262bb1e8a0c9cac95feef179d3bab984aa6507daf99f272cc3994e363396054b92efede45aeacb023ecca15f17be01c710a154047e54a037bb47079deeb0ee8a9ee36f51118e423a074f76558b917837f792f579c0da446715ddcb4331b3e8fbd0204f6029f3902d514f73134fdbf9f78c3503a3025afe683f20af9df710a8a78dcdbe32d896113339219a182b2cee82f434934914d81e2237e38ad0d5f78af2aa786f785964bf709d4ad859434b8ab9240bce11ff0b56ab451b4bdc92ccb6af82db6f44937560d7b57a7d77aee2cc0482355935056a51407f76bb3e7bee1542768ea657a66506eed30a39421a5f9d7b27e2a9bb68dcf5170b805a641276db5b25ebf3376d71a36e865af7d846858d0c98b01b2be594446f9e75c282538a317e17be334b2512d1a7ac327fe77f4121ee0cc4d2e2974d71ace5a44665bfa328153727bff3a285e50d35faec6d43e04d9fc9bc1c6addbdc2f8191615aab0ae6947dfbd06379587cc725f755f3871e50228cd7696c9de6592653cb63bdcb706bede5a14d2cd58b4b149a486c45c03e05d997a31de668312f8e2cfe741e17c76688cde6c8c4bbec1e07e41192ab319d68d62eb43939f24ade57da3365d44ff4b1031a02d47623381e3bec59b79c1cea747cb4d14ffe72ed7a4dc700490821098e5fb0989b8ef3fec1e9a822e00887ea726647003f248a797dd0820aacbf525897c10a6bf2df4394300aa208fbb251c0bd7817fb7b010daca79da20cfd52bf9100386cf06b03d79e34b3a533083bd51f9c6202618029c43476b875184b34931d07e004ed51ea65bdcd23bdcadaba9c4b9be503dacdb4dcc91c0751d0cc8840e49175789178e1bd75e9aaaa40fa9d80c29a61c48f2a609e86be912d4e7ea89a67e422c043973147f71be0eed4b0e945c5b3c97e44d3109aff81a0176afe6047e85221dc007c35c95dc868c2b3c74e3f789fa24ac1b29f50b7efaf2c490b86579b08fc58e0d3ee3565348c3348e075d5921d02940c67d7c154ed64ef2366d0f36999579282b4c7acd6c41136860b7843037c02751f55a81f071e901d0f84f808bd9242bde82109c324d6f31888d93129c2fcd6e4165576d1f5c067fe5ca88354f18805716709731aecd02b36c28de621ffa9f6146abe99c66781abd5e59120342a6a9ff38610d37e78a9fe9ceddb3bef06b25a0edb5d79546c57f53f3050650ff66e4ea993eae34675ce4d152358f8c6aac6df6b44c95cd028dff78ce485f9e0f9f85c2a9a9fc431b253bdd2f92eb28d8d040aa414ff29f53d800d1320b4da8520d46363598c38a17b0447c75ead05f98dc85b3b965708a9506580b0628a6e4b5ea135ae1ad2575d8f90f9b5a6bef5ec9a63c80fa169feeaee01c9f4616ac50abf0363a93422f279390d2c078aee8c70f9ad69bde578f0a565e3ab267e0fbf1a9029a91addf755d0235b1948997c59582c3c4d2fd0b818b2f76cd8fceb4e7a33209e79cd4fcaab222f554a798d4813ac353b63c905a67a1499e9030ae73e9ae94faadd68721a5c10410a4ae6146c48f0ba8fbab4b3ef138a6d0239aa0ea5a6793843e5ffebf66371cc6b2b8a97bb10829851c87a8bc23b6cdfa72551541531cb9177a571af68375e54640b3c6a6d46bc9267c7c009ecc2d2e5f7e7f4899655eb752d5deaca5f80367f59c35d213d49f403b0ab2db2ce207695aab53c4df7bc3c281bdd2253d4e83628bf95958b2b2fdb2300a81dd18fcd142e6932fdfb2b21378d786e2a2ec452317112a04018a3b46779af628198999b97ad3d1b61f867b368fc096073eb38b1a0195cfc2fae4f3020c52ec81d886e217d9ab47da0639f0caa2831027fa806e46afe2c0f6e36405d4ab927e8db8e5ed93f3e2c6afa871e10af854fb18123ba9e4d0aeef452c9996b8e545132686cd000a43dabf2fb9a1304dcf5b4834aaca47e6897c01364037e058206bfb14daaf4edd93821c9b199684e5405c9c31a6970a75116ad544067b9508c7c4c4ab7d8b37c413a2f836f65869a0f92095b8e19761d505f31bd2708500741424e1e95601c260ebf7827b962f5ebe43429fa27914db61474ff08cd98326f79fbafca230c274c4b149a82aa3d5df429a28a5a7f9c6611f6a1d3e0fbfab1a74ccee5c4aa98c8ccc0e1aad093f693d728b5576ed54f43373f031bcbf97eb5b114d0d3241b0df62efabb91c469c89a0b8d756cacc5ce0c51566d6cfc8b0bad303195d4595d7275c11c16061a81d46b68f948d84ca8202f27f848ec8380589652cee0a3220f9c8db4af44cd4f8c77a119a9c563ecb30435d9dbcda3e28b1e1c3f5c40cb676b62b15ea20e8e27ab8cb3f3c7f97c67260a8c18b0eb271e93de9a3f5313e51099160cd7c1bc5acfe434da460ef974eec7868d87994b591a97d42d56211556aedc1b509822a934f9af052d7f9649fbff348c7f81ff6fc7a07b5c316c6946873743d20aa26a3ef475e5b03c0884ab612d91f6cc7c4855dc2721d29aa28ac7f8bcf3d7833611e323131dd5e3c284ecf9d4d46178ca7c97a0693ece9da077a1dece2781929f57197003ca95e744ac3c2ebea3536e1097615f0c4e9f0c02d0a058075bfd796d3e0cf4eafce271b4d57aaf8fd3f067c2daf6cd0e98a965770f136255e75bca99a2e791ef761e4d392797fecaed63f0e34f574f0229a6c43351f3844825ed03b8e27a6fb2ee21b1497dc3d8ae11dfb3b6ae98c33acf9ae6b016963af20f20b58aa40581a64e3c8b181f4bb0010f246f1fc06643fd7bebb0e0f20539028c07108391a86e1ff00ee01a7dc7428f0d79e5bbe4ea55f788fdab0aad2e5e0edd2f734bf13649a26479dc42d9accd2087efdacd7a8ddd67a4fff2a1b1d6a59a3003148fe0c2b29fc99181ae46a47e39b2832b5fe635fae62c4a46d48945a9d40f8ed002ce70d611e9e3461eb7a2419c5bc0bc20388b940c29a2a09887fb326e1bb9ba880e626d1dd7f70421144db37729623fe195550c5da006ed3c00346cf8c7d2362c0d0806e444420e6447100e984b0f78559b0ae8edfb1dc39c9524554d35c4cc62b52d12570653da665dda6ee4159e5f1b0dbf63d7d064ebe851a409ce202858d45d3a0be97334183e1d9268630de5a24126f780bb4cfd926feacc378c6886de0b37bdcec33cb5c57e2dfda840ec2ef29f7d85dbb2a324a3a7b834b35f761432c36469b8fdbb773e4e87f06f7cf2e262799938cb1828ab11357202cc83ceaaa2a5e42b9b4b0601dc0db060fa1ca4c2988393ec1d6b67b8af48bf7510a4c200a7b14850b8152c77e8a1c2a087a1e64122862e464d4f13b1d6fd59c5866da29fae99defcaed0d1e65dc77fdc4a3bfabf1f7c54624057f9b820b6c499be1756abc02162b92eec24109a58f79b495dab5272ff1dd69433f99ebe44eb4aed6f8c4015a9c0431e0499066eb2239aaf6c338eca122aca35bacdb7bedbb008f182291adde0992269dba11e0d011a4700984a3354df803eab4276bdad2e3fd4b14009def788a230a8d8c1f0aba40b2c6611e5b6b4a52064dac91480fe0bf4bf2d9cf6ade3a87b97a1210860b87a1771ebc1422c5a5ca9cf01142713c5d1e30a057facd00ab78d499f8fa78da43dd3cc5078d8d93788a6eae4a914c3661be3f700a574fb76fc23d01bc8183cb41c07d3a6facc3a25493699f6df216e7ba0c600da9b43b5c0dcd652be763b8c8e40a00b7b6c54050d1ef4791e569e028f2009e5c3948631d77ca5b847a2b290d80806af4a92af7a83b9f85e3ba6aaf73aad62bd6e63264fff1fcf9be71770399de25fa5d3ba3291e4d49f68e7e2e495cdc3eec55b848c2a49c4c321a0d01122b17da0e49711ee75a2247a8c5ae6c06a4683584032df972af5631db2acf7c02d7c39e8562e928feca524bc7e2dc83eb6cee630d7be96e7e2b3ffffc2c08d321387ec4117f0dd82e5131bd73b96ef557a81f68bc3d211e96c6232515a7dbce1e56b06c959d26e6547145d15599370c55db085d5bb30075b3a8424ef9b19c9501500f46cb8e2a402c51081f0a014b2c6bb945ac4f3d78c8bf798dbecd0680216c398f8dc2516fb528cc31cbb59216281cd357a077f6407e2d55b1ab5ceff7443046ef38fae4f87ad897e8fb8f590de64414ffaf32b38ad9584e2ca710bc5f0656e2a8eaaa55a5364240b1145abad2ad3bfefd775bb068db531b62b2c1859385160a2a4ca7de8341a0fda0bab3646d602be20057828e62821ee25b6898dd38808915e1d2473591feddf05c2e5994006b854bb152cf022ef1ad5b98413490eaf30477c5a82755d778f5bfe2e00ce5ef4c1cf241f323bc4205730dc63bacd0f655019f0998d0c349c0eaabe708ded31811e6bc90f0cc739416109792dd9ddc47561aabc41e050b5f6a2d02d39273e72d602193a4ad552722444d414ca7ed72fa2fb8fc5f69489295c0bb9b12c2eee4a03ec2355747bb68c3f971a8235d10440ceee2dffbe2ddcdfcb576f08e9eb43f0c8f8c2e0b015014453d323e6d0ca28ab71b50019039f03bf393b10e1849e5123d51f9ace41cbcbee0aee047cfdbf94795d3418949854ba53369042f468f77a46d17caec59e368b7d1b0c064a2694c4039af2e308ba8487056b7a984b401a33bbc56176a89c0eda1c2617816abe46ab5e75efbe915fd2bf8be408879de4af6a4ac275b1b6832982aa7244e0c105b03e3758889b46b9507b45cc9f9459dc4549dc3abffed0e26a06b15de47ad8a8aae2e5949eb950a60a511194a389cbd6881bbcd1f6c5e59cb7a7f35c2aef30f2ba80714ace5484ed2823c96580645ebede1d9a191cdddf21f4e83374c5b46a3cffcc039fd3e5359866a33905031556ea815bc89ecb8ce556c90a4fad015b9848c51dc93d7584e394a4fc18adfb2da1c829169744e1f746a9e98ea939f6f8f4608f09c65b57a3a038f26ed3eb79ce12f6c6f9fedad83d8549333a727dc07ae4ed68080eae38a4a773ee8fb4885de48c2114a70f5a4b2f39b8d271a04dd3bef83f0b065bed2321855122517dcba99a43acf53354441144709fbc3f5c0a114f1b0e45a5be2e2115c5b790c36bf9c753b28b8e6bc2aea36f3ee57bec480eb033ee85ff36e0a74ff5cc829a85fae9a8661378baf236b55a723b38ac54301fb3bd76147e624e0ed05495cab637f1c730ba1fc0e864e09a41e7804de371a20002d8974eb12b02e46139b7ed415796d6645acdedad58a8141d7d42208daf7aab00ac187237de3f268203cb388b9ca203d7151eb6f20eb23a78b84055f842829bba831b3c58e56325bac0018a17ee87fa428320fc322eb2ac58a845deaee786299461a5196ce3ecd4fe8a3c9748dc5e7e2d2b6aba826268025c18326fc9f344dc435cc0bf64ca798f7a5ff340e5fbc19cc739fecd1156e2d9059d83b6c18de544f4af6b3df5bee705e0a9cdc04c5f502fde150023e81c68a63c69aa9a1e69e0aa5056776bafaafc1281fbd57213a1d651499b70bf50619eca6e3b979391a5e33265ec8c497fef48a99be97bf84b1e51ebcf4ac2d675cd83f7da3bff4c9f9e877e7a46e08f386bfc156c02f6a0e6166832a5409c8b36d4b1e7dfb431ac2d2fbf90a691962989713c00f934d247ee76a85b3a9427ba4c4762816259442f0afa9f14136694889b640bbba4cc520ff7b321192e41c399c128620464f0ff0746a8783d6483d7b1a1e19b6b4cfb17b71d89e88ac41111d34fd9038bf8d1d1a5c011997e56199da02c86adac1c7e40640983cbf25452e4b786be2d8377b21f1453603960fbdb0937c5bdb367bd3e9b8f637e10203d49b1b982d1a794924961936bcca0be7cfa744b155b3d268b52e59f45a0f04699be4810ee5a346b4f12c695aa4ea7dcc47e9f82b16efbb99b14959ebdef1a28133e24692cdb5b1dc1070c490d88fb6e962729cfe105a6614f313dc92c09bc210b3dc789f88bfa3c629402821374ae71f0d88967a60948906662e5cfce4ce0b79588de59be046ee2228f736b290200e09ccca9d546264ee22b529ce315a8c6f1ca501ac3c70cc5a94cf29d945fa1b650fa087c03a44bf8b56c30fdd12be225472f2215711286c762af8b4195c15c8af2db3bc74f345a58fb1d218e4dfedf03737867c15b9a20870e04a9880c986e3ba4164e616593b83f684454cbd40c8b980414c36ca7b056332917531c6de2e6a46382c579dc7c35bcd7d60540c18c145a069265f096fa4e6b0cae304c1d9062ae0661e7d0359f28aab45a8a707726065e56006c0cbfef8ff280418616f6a3ccd60a599e671dfbf2fe90f4eba0811f0e87de0d3b4f3360b51204df5ba063363997f14c3bdc5692fd3e4b93f74fb6943e76ccbdc577069e7f9dfd9643ca63e094ad2acee0397067befa0e88db334d89ce28c27ae6809c69eefeee5d6b701b1b8242db213eb63deb5aed4a5d39f92e991350b7194c2fdc99aaf43f3a67218238436f70c6530d3da86f2af5fcee1a7a275649de25faf24c68bdc416c84f4efab28ce1915ca1679880825dfb3bb8154a7828f94456f0a137d16080954efb6cef148c81333b7cc7aa0515c658297b303dcfc633a2027e18a20b260299cf149dc44ada823a094dfbed7b8f62f71c5e89c71a354b6ef9f86d58e2070a39819efb353aad6a430f1c73cbf05f2cf8b5e9e34db574ae0dac04616c8d4951ed25841c79bee84f5acc93c6dfb168fc56103ae73b269e94d7b8b21beec0441193055f1a54b81c7555dfa1ef9c28beecd11a568472e454549ebdb2db84f1600ceeaa4fb0e520cde9e7f7fdb2287c93d01282f68e3ff861871842d3f901e6419f4a60d8b097ad377b36173c2ecae56c46b3d7356c645bc3787c72190a8ac75b7978bb657224c38c9b382cf60c1feae279b207bd51ed2e54d72ad4e2483ae4ba5eb1f421fa579089e76b322d60f15f831e5799804573f8c55471ff83594d99a2dab47c3db8c91740a9209ae4143fe781a3072105c4718afee3f2b05f4c6b9f95d55ffad9cc0f19de52a6b280aae9036eeb7ad16ee175e7358a1ebf6817a2c3f454688087fd0dd34208b0553ba1203b36c8f62943046dc958f57a92a7346453afe17df4508ab34c7945842755a0343cd6d6f4c0ae567e568597d0e68d830b24bc7022a802097b7a296627440c73c0ca2985575ad99dacec753afaf4ac2346648237cf9fa2f979701fc91a1de973482eeb82cb6da3b0490583775df4e5c285e6dda381fdaa0c2e40bad957004e5d9c5e470b87373124b0229d5410a8a4d1fb3f3dc82655d0941cd8b1be361078e52088d7660b72f7432c272cdc64a21be385e64798f9d08c05e1d0e30713525623ca18336eda6cf2de1c7a45000d3ed2c106e8ae752c202e2fb02d01ea72b2a2719fc4fcdfb0adf871d34dbc20962c65f004e7a988de62bbd95e36c3a1aa8bf48afefd9d9a4316c9d58bbf591d9e3ee402f8ca541762e57d63e81cdb2e058441ef358082d893f8605ea27565c6624f290fa176ba6a6e44fa2718ea8afbfc4a36e2eaf5d0945d7956cb6fdd0b505b92960b719d1b86f5c17bbd8a875f5a1e62843267c063e722646888332c316c1e4bfcb1fa9071cb63070bdc8c7c89dfaa719772c520a98cd5669682b04d12c76b44f4ab94c62c2c513ccc67f545e89b29421361d72a73afa5d0459c532dad2d3b82bb993b281b2e982d9a75e2d1289454970e98bc5246b279f7cafe46493c08c0a856a69eb731a41da6d271035f115f51b6d294fd4ff0c3e6d54cc5a9cfae8cc390614bc1afd135a288f637759af7a488682b36077a21cf261fcc60b2b3d753f20cb41a7ebb9536903bfeef5a68b405df05092306ab605c70a3500e2cd75dfb5bc85674b23cb2a97ae16ba899416c5aba24aa69956f9a2e7d83a02792e7605e330a71cba5c6b6d18891a243a9ba3e3dac5b56aef839884d295a1f637685a50d5215768eb2a693ab015e5dc94fe86bc51068dcf68f331b4536e8135656cfff71f02c32d497a91981514ec23a11c30a5929fae1a5301a91e632f05c024d4120ef6c0890d41e6810099cc330e9072ef28cbe14580575163f9ba850a2b3e30d1a2b805dba5e5f9045ba96a38d54efef866e1306e78b3c7aa4c7f45c70bbe7cd06d6a3bf99ba6a340303c05d3142f92ba0c0e7128cd41d93fe81a3ccbe6ce36a40b1fe705ac63330db5ed8cc276b92d5aa33cefd28a6288256d419e57ba91f9b263caf2166fe0b2f429cfc26d3e20278bc1aa7cc1e0c0a8c1d270157b6f4df2a1de2dd7a67611761d100393f83885181106732302880fb00dac5fa94ae872703f227c9cece287956bdd1ec785e2c61128369b4511af82a356b4127c7c17b9cfc4c9cd66ffd771df3cb86e83e9086ef0309734cb2dee1d58633a3cff734375b6620e544c53d9825d6267437ef16ea4eeacc2b5ae50459abcfd05f286a43d8859ce8a3afe3fb88f150755372a297f34dbf9ab06ee3b4e0eff6eb5d96cdf9d5c23faa57272bef3a55464db02d17da484a13d9fb4994071d6b0a829c879c245ef971ebdd9194b5648ce036d25871c8d885490b682dc3b1f61f30135839c475766dccc3ac4fc9cf95edccd508a9329a73ac2498a4cf0bb4925845e1bf69abadccd821bb34f70fcd5162656506756d251d86b9fa909cbad72b0267b737fff7ec371215dc6fba69462a22b0d72853a5000bcaa793f1e378c879dcd57084df873b14b187bdb4f04e5ffa338072dc7b746bbe3ed6506e82eebbe2af53ca91e9a3e161351255ccdc9639709baf604b4be293a3d5b33ab12768190b07aef069d204074dd7a96e5d2bab958863e2a522ea809a7d09f7bc84068ad36b7c2a7d7b5b28e4532b5e9ba832482521467079caacc7a03915d3c420b0d5821def9f5791668f403f662ada6aaf09727613ece2108b3a3cef543f1ce802a282d137064751ca974d9c72c37c50235cfe7e67bdf3fe5597fad9f388ce63fa4b618f09b64b8da54781aee65a6fc9dbe3950e030ebaad5a420b2183c3790af5f4a0c2f6a478c20f6ea110f9e1f15fa3f83e335fa5a2f016f432e65da806c2fef6a3d58ad4de617838d259c96d0cc41fb0d699376540cac2e193ea51c65252eba3c0a9206d05b291f5b3253c1ea7e32cca03908a504c67c36808325fa5e9c28fab332f3109bb383cfba34b39a0b61ffdafc5cab9ac33b3f0d8bbb64c347f2a0b884ea07c1d789d8e1cf756b54ed17217b8f358c5b36aaa48f0ad3645e094d98f266b7f22f9681e6fd4093fdeb28d8a6006629390ac817d1290a08a7bb55445a3996a890c7c24118387daabe94855ab95cdfe34952c5d9b8c9254116e2c7a60277555d5e265b34ff5fc8831c27bfea7a7eb6f7ef41623f86ded6dfadf5ec237fc206c0c56a042605d0feece8bec4d29e1ff2c5d2221d6cf5012a755d820e142cd6c5c44e0766476e3d8ec5b68082bbe1da038eaaacd8ff40b0f9d42e6e0eaafd2ad373303be948e8b18e9edbdb299f99ab2079688fda08df9d1fdc3953717fd694d425e827565a18a7fbf8b37c3330c3ee92d057af7c90e4e850c4b2b74f90c51745e86e035670f70198a5633fe1b9f2687084da778e3438234d02136cd212dab191239c0196ab0b45308fc7de272bbdc237dbed3b82d31502f85642f17b7f3a349521f9a52e66911e459fe65d7b05a93b483549d4e949d5a53567f6bf5c5bad35be54c588c1fc98a45541f2382f00ccd82c2e5c88e055c0ab56d915df42363b148f6b08929e7a85391742ec00b9533e4d4eaf637c7db6a993ae912ce3faa190d46f5496e358b5deda07d96baf6622b597bdc5aaa3da74645cfaca779e55b2758005894c6d0b0c14f2cfa3ef81881721da6923d4f90f006324ede7c85bef5c3571061f79d396c3f1e1bf44f09061996e213d58c0fcbe8b69e71395cad8041e9a5abda2fb01ea09b3ba8719ba85c6008c5a964100299ac9b6b965b3cedb01b40f7a1909430225f8e61689b7ce050c51d5e2abb10364c802e4e0c9d5600d6758db62fb8116cf64ad414c1846e76c8480dfc413f1e1e30c1de91f1c6b760d60c914e9ebe4a9edfea64c05dae081c92c189e0ab27e5f6a20bf1153d3245cd0136f8ef1a8018ffb0113b6263d63a836b782ca4d0424df16a6c7e384d2199e90f17c6b0566866e25264ae80ece9332883e4ac5045ef30a0e7015609a5a73330a9c87c5ae155eb285d86187ed992e8eefff799bb6105115c61b915447c583943b1045026d287a4516971c5f3fdef651f553b0e6608d74360383cd0bcf78ed33057000b572e63a62a78b18cc7b8f54500af32694b5e1768c79e94a951ff4e58af1c7fa5eeac8aad1487febf1b61d94fe97e271125843b23a854a731d406602d78003e85dc870c486b3211509d18771700cc43004a6e0470081b7f173125e066e00b78d260a7ac0a2485ddeca399487382868db28fe042f846d4fb002595b862ce2eb8aa2bfc08adda708a392cc2f95840c32726d5469b111ccb546905db5f88b08e95f89e475bc1e9838b16201a88ede2ad2b730e41cc2128bd0ab5ea803c2d128ab5ae6af69086da0d86629120182411b0c0ed53b652a3ad626e9f9c0811ee82ae82c4a0af2aa4ef664a0527d692d96287558c815aefa04c85a253348b21e1b3c1e2cdbd8f8be4e720a34bfcfffa2cd18d4ac808f6dc4e66860ddebf66d5a9a3f77a18f194df5022e7bba58c48e0eda42c08d3fef4482ed84857bef56bd2592f4b4f387198c6eb4fa645be7b2f11ff4f1e1e48b16524a5cd5886034cc74212b178a84e59f87538823802769a886ce18c4f00ad1b3c55c6ed5aae05b6a1e89f8c7356e46dc5445f0bcb5b8c9f0b88887bcb1905744261688923e2d81757672231c815a0431935d85e773959821e3dcb40d2f2e89a1d8144f930636996abd4131caef539f24438664a9ae22dfd9f0faa29ac222cd36b7bd83fd84088dc9f25e86f7f035cf1c934d85749a82be2954acfd61bcbb2c5e0382df1b804bacfb3f7d51babb43879e5fea9dc7a264f1fcefe79b751dc25420a00e84f1338415a01e8ed8761b68a2fa901c47023da4b03ad1436367a699d1b43e0d6d1289f0f428253c919da4313b19b05cb19f148674533e19f330e1b0c6b15eb8e14dee556eca438a51cb195a0146f8613f0173c03e2d5c105943ee559e2f986dea6d10ee70f000aad7fa0023c860cef59972eac15c10d8415bbf848d512c40f15c841dfa40c69f5caf42840392f1b023ca52ddaa158fa2db951676463ea8721cab526aa4757ecc725edd4bc35f22c6796451ff8ef0a293e06f9718680585f0d1799d65eeeec02d4e6f10de24b00a12fcca0e21a4e32bd82cad6e57cf5377365073b3904d5459128288785ed72b5f2e19a1c35cec195a3f230506788b3640553ba0984783921f41fe194feb2d2d82dbf00a987d4da2bf7537f8fcb5a43fef5af649fdf4b45dd004211619581bd923db446c36d15a2e103c60bb9fa80cbc2665a80493e4912d0cd1a5346a197fbffe1d2102f6f584dc57f629e4660067aa0677e1b68b2907582162842f6b0749f3403b13dd9b16640e8fbed82e38ea0e2385040d82e44a8706d82175e81543b9bd033eaac83a2cc16f8942122b082c5a3d95304cff260e043240a0e11b6a84e05334398005b08a48474791157248e3b604725709fe4639ab3f2d5038fd61f2f8f632e2073cc0d7b4a88683768321d166ef00b90154a52804bfaa901f1cbf89178507423848dc0e33f3b15b2e363cc0fbd190f80a5bb2cf19f4a130328b14a8a31d0fa07a8822ee0834177be1cd428c4fc23df024ac4f8cff6a733408bd8a40a87d59fa28545bfcba46bfd80deb8c229e581aa6bfd122fd082505cd9cd830d4691c1ec86cc4b5a6428e6071658053f0614d63af65dbd1fdec51db14f9cedaeeea7cef44ee62f1a3aec77939e586fcda1f33e0efaa3af2d12995cd6d891085d224a967eb0b0120bbcf9819b72138dce8d66eea915aecfa4582a62336b78216cfaddf0201ec871706d71218242f967ca936ab86b06bc2b344e9133d04de9db20af1a01e81dfb6d74540e7311f352548a4c86e221b07eb7556ecd0063659a0a50287270e24d0cfe50f288253b2066065a5f7665b8cb8df35eebf11b53e8487594b994171ef96862dba0fe495f9ddeeaa37db03e206fa8e8d5cbfa53ad27a02faf570283e8d7ccc31df26665af32bd556a2f4886259a25fdf9bd2b8b079578ed90192b948c4950442388f70fcbd9d6f93b0309ba038e71705b6575094f1358fa2436302b1db1fba0cac3355063a416e737e0b5b0ae5b972b00c17df26294b79486a047de14b8d778b048209108e6acc6b782b308a682672a2b1c74d028e300713add0d36ee7af906c1bcc5e6d3b91d081f27185bb9d61d905a97e7d28b336a2fd17430f4b025395e03d10a39e47b34a5ec96bd19b95914b042ad64b08aca5bd17f66d77e7fa312b812386a4174f460bf87e4ae49842e75db50363053f01fa5f10371e99171a83e0a47d31b9c569b7717da75f25c494fd6c11a3f339a04b6024ab74a1a784592873aa9ce6564307f22b30547ef74ad4051144b12cb930e448d7392850c8c59a58d4a6f2647f5cfdfa34af70c668172bc0efb2cfff13e38fda90a926190c4daeb42dbfe3f5bc5b8da2f28fb03dcb96396ef43f084146be64482eed9409dd088105b237af1ca1d7ea21f50ad4a5def20a3d2c361b6e1927cc0f7ca02be4107e9d1c55949211adeeb2159ebcd12890541defaca02950989f3d4cf1924ea6c19d9f1f2b02f606c8fced0eb1485fcccdf71abf9b47841f2c554b68df1a75c9c75da597035790c5d8674668293a6e58b3c3e9be22a49858d004294ebb444d838077db510989d73f0c97e303ee72176798d9da0d088af90fbd8ef9c8b56d508cfb333f103904204e747dc446307d2770e0a7e8f4f099b19f40a26a7999fa593c5f3c6db1939fe97a8034119322db2ec845f33d2ee4aa7fcb8434bcc331efd060a718e6d06e03081c45185562565d9d8173edf84e510b6c486395203b915b176e35a5ca9bba2f9ea2563bb2a94b617421a7f1cd468c3dd3765f58f6764b07dfb63f724ba871a5444efd353fb0dd99646d4674dab0fff52fa3cd603b419619d58fcb5e6331cac53744eee84a2796846639d5a79b0c60437f8c737646976096092a37c46d88b35a48d887109729346923419203b87754c6e3eff8439f0710bb40bd2af3a6e42dbfcfd5682be86a093e9c35ad3bff630fe9ab0faf6b80c02c51289a8390336665f037b01b4d6ab58e41ed9bab23028db2d272a50b4d303451a488f4549c0f311493df91ccf0d7a84d482d85ba54971920386a458511adcf6140c61e252c38d5e8bf8be4edf4ffab80af8ce31a0aeaec513016d96951360b4c494eabd94ec8ca98060f58ca05140e5cb7a1660fd894234581179276596f75f89758ba9136321ebf2905c4d09a276a41392fd93383446d4ad04c33225c21c5caad818dc3611c0fd02c98dde04f535fdc738592d53199c3c5ef7ca80f9d46c5e5c31506fa2229048528ec4a9a0e9418322777e9b1f027581318c93b26600d3694f84a658718c72f915de3821aae0b633d1326fa9316283876a5f00508b8490f7375459e009f055a0c78615d3fe38296c90971d635485b2a95dab85c5366ef9e21717eb57722a60a4a6fb2b947bf51f9689d83ba9185bd53733eab25d4a6a87d10b824eea291d172009fca1034d3941bf85b60d5969932c111a321c8f03ea8872f128344491029479f55c462f95e083f393224dc52631e0786cacf26324e96506ff38cdd88acc220a98fc1a4bac3c4ac4e128dd21a5b80f9861fc7aac20bd2f220d576ba02915277b5d40a43932304f0958bdde2cd009e019a21be7816991d9702a2e74d0093cf139cb894b148326344003d96d082ccbb28d392f29fe8456237b4970b34102f02a59a22c7265860349d6dec468f76e425c3529da34800404b0dcbbfb805183e61c662b51d1a963417f0b243a97bfc1656c018a875aa14f6ecd35f75c38527f2317b3c95d3717920bd3b1c6bd57cec94fb05c088905ea37e0d1a68bdd24b36ef07c62d7652ffc6cffe980b87557edc6b66efdf9804e8b461ea532fa859fe5e643dbb18ca5eac7b72d81c8334fc7c2ed24f43fb0981b2ac8b0fdc418b1084c06efe918e5c3b0a5f8762f67c1a96afebad23e4dbaeef8251779887886cf964f5a82dcbd531a216810a66a1960bd828803ccf5bdc9ebf6bd4806c2e47dca6745d080e01a3c0cc4ea533c39f2495ccf172947b6c7060ca3381e621d99aa5dc4fffcac8ffa123958287d26f4216238667b3677e2d33b93cbdb9c6477a1d96afdf3a2226b8848042944448f3c79f7702d72d5816d409e3a4576213d565109ceccd81ed9725b868095cf088c5efcd589c410dac3fcd3229bc2153169fa8e8ec1979ac23db48623d9c5d7b9148b0b1f89d8f16a27c9956f11b78729fb92c275c1ab0fe407d1a714a3e2ec53990e680584a096d141ddf46413a1362160e0bf6f01f25f2c5271570906678549873e8106632fc605f4560419019969b67ed92b59f9095edea50e5c363c23d90b34834c37965737bfe019cfbd45c1e402d8c3c0c58aa8130f3dc44359bb0e79445c46c2172e51928c958867ea2e9fb86ee6cb1d778312eba3b90104d1ae6355fe1aaf2cfd96c37cf428b58b17a3c0d5a5c1fcdc0b119ab6495a52a93c42ef3fd7052bbfaeab2305f1751e3a1ea30c839b215de1c296b263968f215d3f07b96a281611470e95b713c23ca2cf5821beb12ad3108c65c9fb39116f2bad605cae740b57da82fd648ae7bd4f4c2ff2c28b37589028013320fa7b1e9123fddea01059251b10e6707cfc6374dec8f29eb17cfa1ae32347899fda3cdcf6020269fa51ad8dc7dcf0ca93b3c3d125dc898ae419cb1ca291d3e79b22b5052fe5f7651dbfe861f9b34db28699740a811c86faa8cc851c02d1af3c5a9fcbf58dd0a7f1324be326edc7dadbdf41a893a7c7b2a9efb536c4320ce1f8d2a68382a1017b415db80c3017d465f8fea996390061f4a93282eb53b61c0587db114816b4e729aa2452568e53e337ee261ad683ee846de25957890673aee6507926273987be2f33e34ca224eacc348419c66cff2dcfbb3578ca79d201a4a7437141a7cf8c5ee05b38cf18964c03ef35f635d844f1cebbaffefe6f4602aebd7322171bd9c29873b47eca821d319ec9200f8c0a4c6642e6600e6548a1c4711bbc8a7c5cfd41db9107b6340177beca69191dd8fadc60bc67002884309fcb94466c29d5c4478f0f615f481c0e25b70a9ba419cb48e648a74f5a6544dddb7484cce7d8e1a91ef052e74787f159722a6ce5a9bb29a340275012ade2e48c44734df3c521e6cad38d21eecef99165caafeb8ca9c5bcd4cd29540b69338bd3368a7245ac8260e72fff2e1fdb2a0972739bda006c2ded49b29e1005e75c4b0e4d56cda12638c9fcb1d756b039b3484b51175aecaca0a3c14014fa3b3000f8187a15de52a37321e2f51f7f9f43c1fdd4de2d48bb3891e83c6ee242f58581f5b68d613dc00ae52320cecf3819ac5d130a073e1504e333624f983c1b313e9993c6671befc4288950407bd9030063c629337b1566ebec50c8d2ed6adc35924f44baebcfd8b31a01be8af4d999fc8051e3951e1436d684a8d72ccd14400c183d11c1773bd4b2e48f3514db84f201aacd2bf09b053ab06180007f0f48694064053cecbab09132034870a8a81af914bc4f5ed94d6b7dd6d86af62dbd81e617d0185bfe642923dd80f4672a97cd68e32a3c8dcf6dbcb8032e85f4ef868b840f61e355c002299d1a20ddfd4c0f6dd7d944455fcc14d287026bae859a09f42eacbdafe2ed683468526748a7c248a93f7d53768a949fbb4017e665a490566aa19ad99a4eadc711d7842a8dd0b8924245571805cb5459d30535c6719f73377609de49b48b4e4909e06fc38876cba60ca7582911c603f52379e076499337808700007c9eb6044946f72d5759265584e2ab04350cc15802348e56e9059f40db812873e03604e50b22ba87b6e3d1b6dc9cfe3b2378a33c11f991eb106a06a552ed9112d93780adff31dc82547a43fd20f24a1b4183c803d1048e11fea05492592ad26418445933e890a2161aa06140dbc7a9e86fc645ac579d338f5c2afc07af410fd5c9af2774d2a754079657e270f2122b442afbc6db4f37cf06e80246dadf40c258d0161d5e2e9f8c639d991bcbea3ccfb736c6fe8dcd652a864359d19d220d9968b48005163daf92b4b4df7c72751b78f367e0544ea4dde521e7160881ccb9052315681909265f83557cae4796c618f8a711eb4347c1c966cae38e134b4377b0d77cd7f6e233b99cbb179697da0ace294737258107467eebc0f46de20d1b39bb4f067250a08fcb28ae49e776931cbb28facd72b83006797973d7e7c641546c0a87a03e8c09de9d3a95f67231a1533020638994bfd137ad9d39f31b4d314b4f5121a7db997886c30b7c709b511cd3673e7298de712dc1ab3d34782ce4cca622e19a4a2130db6040537ce5670cc104958c8eb5318848b11a1a6959617ec35ab4be574f60a816955547081e5c1d1a0f95c69f32f9134d1e65465073328e0218b9e12f6075fef1a8f219132c8acd71da1dc32fa85c53a76e22b6377be326bdcfdc9e46a9f494d64d58a9b08ea84ac243e1c7c942a84afb5ec752dcebfbd00f9227e5ae9d6421e045b35867975f88889a3e8005c01ced967896d44f9088f863f34811311825b15946190a1f60a0ac2c2170b44edc4b536eec5cb4a751e21fe3f2d9f2b482bde30a7e1fe79d8466e7455f4bfa53f57b83d258f0ddf422a669167b1122b9d8936327545f4a607421304a45fc4eccaa43f8130081f548e9be1e95e69fb25be533f5d1581ca6f08e7284b4d8181208f9d9af416c58b953dfcbbc70238c6ea0e0f4ca082da9bcb6d05fb83921dcf3b73f546522a8d6232e6bbac0c901150cb305c348c334f35451ca7fb733351cc7d78b5308b650fd89b2e8ee74c9b28cb427ceba8287bfad080ec0efc922222fae7c7648eb2302d6d1125deb4c12fcd1651cac791cab2658e2b6bf3c1504aef737478efd380d8b26fb4b476a465ad03984165a15b152d81b5d37d629fa8857ba5750e3664f621f65a812221c9589148c05b3a7fe27cfe7c0bd75cf77b104694f0196658ab5b98e3d9b211d5115fc9a11c0ca355685f3efe8c0fe516afeb1bdfcac77b0f37c23733254d08dc8bde35058b2025a632eb6095cc20ff7e994e378317749c405b88f19834f6282435a36e91f8f03fdd6739b4a43833b8c7dbc947d471e88442923116b88845c4dd6b77cee7bb87745a5a63f5b7e78af8639416bdf99803fb50c5723f03d812d52d70a848d2d3de71bce417ac08d3edc26a4308e32b83d73c60f8283b746f73e3173bf64dc254df0f7eb6855ec4480cd7e86e2ff7f19819faea01cb1cded087d74852b45c101bc1f04f40c71fe015a63f6f365a46a00d8072a7cef5310f48aaee7e181ba3fa0d1eb5c543b15d9c7c6055a4f4129a78ec94b8e384d339ebb7a9e0c06230d197ff9aafc860cef967ee34b25f6b9107de6343f95c282dd17b8a57f9bd6574a2c540a1cfd53cc8512eb7a24c9ba5a490cfd436d4b847bbd3c84ca481ed63df19020c7bfc9998d076821542aa1c9bb66bdcabe993ccffa9d53e5f9061ae5efa97b28986503722e322c2fb88c6df3a7965a2de8cc96540c347a3d4c9f25db50c7440dcb60dcf6f5a7fc69613c15b3ec69fdaadd80b0447ab76d260dbbe83d17a9630c006cbb6d13876d6e42b989bb4b0474b14245bae4b6fcea0f045e937724aa4a0b7c021d722ac25035c3691bca08b3ff548149fbfc10e362186723ae570fe0034cb67ded2f400dbccff13495e1c734657ce76c821f7b9a5a6ae84abb2e8bd84c0a3883d2b259f49c0c09c9f64668541350854877dc63e88972af7f220c48a8deb604fbf5da26144104ce73988872e0848547b14f0924b0c4580cfd46f640768c4a00077d5def5d18e8d3216244302fdd459d465703dc3bc3214034e0aae87ff9d43651211fd6dbe682371a3cf0c2e243ae1d9d9f46a31067e4e685bd4129edb4345c42b1b90ad531da3f8b51f66a4eb65a9a51b01855e8cbb3fb33751459ec30aa28e1d0bdc8b7eaeaef92c3fca017c298384944933a93b4e4e5fd0711b36db499540a49af3bf840774f82c6298faf65e4eb112d60418836f72098a1b06b6640bbd0e7cbe43f4017ca4066d589de6eaa990d21bc9d74a5a3f84b714dd99e7602e10dc468c8de2613c4c9f9a10aed170e37a6860b19daa046083a84d62589b58541e67a2accf439e11ff92a521b3451e20928498978f3d9962ecc157064c2c8270128c1172b16cbee1f080b4bc6391b18959aae73a44a6c4bd358e983982a71e7e5e9baf529ea1901f4a269a274269b74e741a64332287c3db941aff66f7ac809ffba95b244a5f4fcd686fde985246933ae2bce4473e85bb88cb7da3f6f13a6a272800ec3ff98551cb03c730fdb1f85e666fb15bc4f20fdfe3f5bccdedd168ebbc3852f94e7c270f517729326054b689e3f94714f7dc285f08a20a7250697fcf2746e1dea25645e8c6a98f5983d0e83c3dc92e9f747bb1ce8c6e7b986b1e00ebc2494c4f97c70b4af630fa6cb87bd9acba550bf2041d6c6c4288c5826a42a5b560b49ac78ee96322ac269abe2018b0d0ba327ad30f9f300951c5c1c54fc35b55e7528315d318f9215ce93569b3a5608e2ed6272392b77639df5d1ad13b6cd5bcd0e6de1fe1d61e73d0b0a2bf6daa7d3338c24818a08e78651fea6acb037548f7e77498d9e91573126331d4ea1d218be752ca162378a02e71d56f1f4b37711ea5445bed5964e2590b1f4ddfa8064cce21e0decbe450e7cbed4941e022114acfa9109ac6a29b64e658f7c9f8b5e5e844f854e1216ee2d5cfa446251f26437f8daf384791674a3edf71cf05af75e52d5a70f203c12831af002db671e63c24a43a4c4975866efbb82eb512adf9e578996169e8f8039512cc9d935400fee012c6282d5c61755c592482304acc0a525ffe66ecf8f30b434a72691171a6eb1855d9960cb9ab4dec8a7394b19f42d28bd81e6f4329e257df6a9a36a8f994340c6d174405f8c660f75bb3f11df4ac2dd0d7fcff016682c3e8d0d0e2b4a4f967164905193aac840bfacc7f512f8397fd21d9d8ec1ad59f8926c096ca93e10d468380031718ff5d127deea3884da0a8871b24b2ab5dc35a2b49464fdf266643a405b2fd5b563a0506b6509c8d8b5f4455ced5c8f6db238c0d0371b122541a4c839729a492792aa694cdb324788ce8f385bf852a60a63c146ab8e2fa391e363efe1cdbd32b0d5ec0da306115c1c4891ad135615275463cfa19dee1039e87bdb7ad502806a0573f03da0f24a58c043bdcb32d092244a268c92227af9c36461e5238512d2a51bdda54338f157a0a8a59f261f15b6405b2d752c74c784e95e0ca7aad5d740fd79287f58e1024cc665a268bd81cfaafea19caf3f667f7a08765bac125621d660d0def046e58f68d3bb92f16a7162b73385c6b7b57c5c0c6e116489a7e08f2e08fb6470f005c0956b52ba2cf0fec240a91b119d5abffe3bbd81537c1b21f67c697d72f351604f7a76a5c7487f7f5c7c832e41dfc9e4fb03cef495953a148b08452dd4beddc68fe0045cfbd5708b504286af33f8dd40a85c9a40d14eae2200a1dba6ed7770de49cdaa077e651058a3883a9241a8bc378b49bb443f303e890b6498d09e93afab3c550f36447724454ee4ff7168c5e3a1e781495753b57cdf806c20979c04062bc1f6b7106ab29f1feda090a7ad0976430594efc17f1eacda36d84db69ddcc205fb8d49aff501b7dbe0ea3f890663869367c0070d08d3066638c18a28e9233439286e5490b705370e1d976a9e432b8c9e2a23ac165375b946d83c4e42075bb8f60dfb899df850237f10e33d4b3f143731a47166d60e316f71b6ef04a739ad2a0e344b3ccb246709bb3427df747103dd87f9c98672491b6a9657ecd6ae2fbc68c795728c9e28939930a3cdb9c38d623281cfe88d0d27e2690fc36ba0e199303e782e689c32f1beaea4062f346549914ff96e3b745819af00766245cb9278e346a028f235d178d0241cc594d11df41ad40cad9f4386b7f5a39f3980dfd839c36a67e12e25e2001ae1785ac2cd9a525a6a9223aad27eb20a006a2128e0a5832a64cabde278301a338c547e283712d2dcfd0c274d4a8f4496fe16f17dcc9516b8d4da48142b8043d2648d4421af41357da657f242b16c50628427a51f943e4b8e5935ef99a937f017d5e1715395487aa0e03efdc4f3b87d3f250ee148e1131f6db716c359153b24c2d1dceb204463021322fcb020e625a751aa57379218fc12adcbcde1e2f6cd0bae10dbce5bd768ca2be75b3ccb383d2b64e9348d5fd8243270f2827e92b45e8b95977c91fe691a44ba88475afa76e800d0bceb1ee90e205da3012f3d8525a37631c5cf76997d455fc4d9f084c135cfd1424ca86e685f6442cfb068acd75693a749db49154c9b94362447317a51bbb01606c238ee26adba430c2e224f2bbc5ee8d761c2f259521102cf8a4dd5a6a85d9bc271e5668cf56eefd4af68ecc494cfa51c3f0613bfc5f89ceca91c540b2306464da4ecf01a0b3517302634014dc99d42902318c77f60208b1b9747d716a139aac91012cb8f9f8aefe7eecade24c3f80fee9a26b550a7d9bbcc6e1b1cd91540fecee65cf70e9d2e5ee260c8d06cecabc6884387942b79174024df26572398cd0e4e6969e813991c1307fecc2c72e714812659b422725375b9928fc7fc3fcac0f0c0809b31da5321d35bdc4521aca9422b74e98e9e38e84881ef755c4c71da91eef51a73633ad9889ab5497059a8193fade6d6c1ab3ef9fb87a693b75c421310b00d022cf25f6e3b01fc1593bbb47042530015e6684981a332e199a2f2cce32f1b28bab3505edd9dfd841d47e733aad6af91418cdc4f7948af32e96fea0a599cd747c9974acd447d52d7590e0a8377be57cdade2b889b5e2cc06deea9d49c2e3667ce57ae48c187eaa07f255533aa3ebc57d7688704ec74a74ad8ee87183042ca6e32e831823e1e0cc506e60530109a1f912c6c4044d311f100ee9420539d8f83d49deebb758f823dc16da1cbeefef50fc6d89354a54010571f2ef41b48fb0cb10e9b38a3b0f3938a600ed4e3137e3d63953b6a6039cfe13a98eb7f1736663c28e5d5dfa50fcf8c35206461c9bacc719035df36ca5eec94750a69e74fc35a0aac6308e0908bad470eef70aa05d40ea31ed28dc5cba646410ef88412341c3cd07c0e2edd7964fce05adbcb65c5e605d19874b864e81e1aec16adefd42d100a56a4fc50edf0542fe1bb7aec822fa58bec35531587d651adaa250319f3baa0d7aa83e28a06b9128192b7d4d13c1b211c604a46b8160cdfdd991b62de9bd4e57c15387e51a7f5e080fc1a2fcc787c6a92cb75f221fc45a67c2aafb99ad00e1d97430251744920346c06583bfd66b1ffc8001dd1439030a35d6725a3090ba4a074b998cabb74e54b1b2c2fd3a0d157492f973f605f292cd6712e1be36a3ad8499cc465dbdc8e14b89f9f78627c6702bc3b9a228cbe2cba41c43ce6fb05ba9bb045022b10fed3143ed6f2911d97b932db794292599021907160705074129ef9bdc862468cf64599665d950d1d190d0d013a22029464eb0d09c594b941d41b7c9e44c508534bce8078782a0d030f48488861b6651aab3acf948ba07320b498d5f0291aed8c3a1e63c5ecdd389006bca0ec22166e5ac888048282860136bc73bf26047986707f7c0ffb1da41353d05bc8570fec2f9db2dd99905fd6010f3c05a999815e1eb89970e936d130ac120e8a408f8ea800afac117dc3205855b2abf10dcb2408e98c5cf9321121e98a0a622f76e4f75438e37eaee51f3e88ef241ddbdfb831c28fc49a3d7eec3dd7db35077c7b029b3c8b9c6f5c631075334386634cd636f1ca57ffebcbb657c9b9c7763c9cc2beff66367665473d5b627ea3a91c44de8ecf09133b7db384ad3f45a6f3e51308b3e757cd1f145c7170a5fbedc25971ddb27263dcaee5e9d59757bed4e42dd9cac67dc89c989499a710651db641e6f17954d8d7168712965098890252062e3522897d2e7e4382c4555b267f40ceb8275f129bb383939d1504c4a33524a554d64125d25258e32a41926b060524a196d6e54bdeae82a33d2637653836bd8b051d5c8773263a3b48e455262177174dbb814ca27768aa6b1383389f29c9c9c9c9ce37cb6a7aac6e294286de3529ce2364d3eca4b36a3d75e9c9c9c9c682846a9aa2319b1c928ca6a6822cd993335353637353af2102c03cd804e3982a013b39e601932e8d00e719b1b9a484306afda5b653735b8860d1a4a696c689d16768eaa9bff4e23da74e8e0a8e77da083d2d181071f703e1d7540c83828a8c86accdca13f9058589683d3c1071bcd7fa0130200787c737e759667a2987ffbcd7e5334f4956c0f7ba147958b509a14b77190c75ca4a30a3e62248c580ab3a0308b79887f8b98a5a30a272727a71c3870400801cb81238410381f7f5f9ec8d3fc3a433d3cc44f5645454545366adc00002b328143d06988c99933373635581b8bf3f117006c82586c8b1fccf624e977824e5387ee78c2b3723c94238621266216f39333b8876df103318770e21654864e3cb43cf84521ff10ae51d9fcc043cb27d81ed5f36a7f748fca834e4270199a33285c35afda6bf7288d2a0002604a23801d5b8fead7a6a349ed40655a8a631d11d464c1c4145e50aa848d0c688da21a1c30d1438d2b9890010a4a33831b28f4a68b1a5b509b2d45d0d50b8e904e200609884a1859b058a14a03cb13366268aa6e1296a3c804842e63bb8ced194ac676bfb1adbcd0a56fc72a92b039dbdc1a0e6dd5373a07ea86dbf01a7ee338cf6265af9342a1e64d9ca9944d4c61353153452f60c0ec17d99e22db820fe44796ba4a4c0b264b653d45a0f7ed92e7d45447f3a9d7549c4f2acb20877aeeb3d728e793a150a82658b947d1703e28c91549713e92a394ab336716be92ed51d241e4389f58c4bb7a95ddfae8c9d99607319b9cf47a43411278b5713eeec1a71e7fa0a5608e922654e1f3bbbfbc0faad09d97ba4cfb54a404892a16d4bd331cc4ac757787ecdd2d811e0b35f14f0098e87e13877106d7f942fdf7f7a9f600a56e133dec3027a5abd5ef34214399ba4dc800a509a01ad4d93dbcc4ad7e2f14cc6acc97a9cd66d4fe11d0c92aa8fb71833014d8a37f9fbb86028370dba39f6679f47f5c3f8632039005e5a2a2be527b0a2630f5e32cb57fce3973f81083ae96baf5fba16e2dd12cead62a800ba36eed5395d91c425a1efd110d6ac343dd266440a37eacc42f568adeb7e31d2bc51b503753b7899fa0fab1529317232cfbe89bce17aad525ce070ae6c3bf0c3be5dcd217aa62ce4788bb737375db855c7927f30bb581ce9ddabae676793077423c00152c58004514415b5c71cd2ae50ab5c2be0ed45869f5bff962595bbfe649c0125d2c6b7d30f8a8224d68ea7c5851de95c02cad046e7545b895802a248906b6e52f24a42e0f7f0dac96ea824346db1240fcafaafe70ddc381de56559dbd3c42b86588b38b0f7fb9bbf9423f5a57317b5f65dea4d517a375878a57f52c2909a86e521345b5a76e12134f55eb4de70b8535558b9bce174aebfa9801bda91f73756781c01d44d81e48826df9bba360c80281352c0f1496892a3530c2cc0ac0a8fe4154ff6fb9a8fe265075aabaab43f55572c1c7d5de9647efc0ac2dc22cc7b09f18e6526a0bd03e05f9507a5f8ebabd5f9959b6b5f5a3095862697b6c4dc012486c6babf4b4cda19311d01c753922382afc8d5bdb73d40f7bcd33c22c2f720685750ae69bc40493d01051f7382c1a6284793ae88459bec3174ab36d0ff7d009736ec3f27058c3f6f0df879c095c851e8703f50ff2056ccbb1a0e551c3f2f05f400ed56108208e099628ff2be3cc06ccbe0414f7e8eeeefba2a2072e6ea8e206199e70819284bb3b5cd1807e3b8f29b1a34689b9476f72022b50e4604490103f20f1ea2d5094a02b0228e14418a88409aa3f37b173bd51bfed8a51172562a01899b9fa400d2735424e3728b8a1ca41c806091515c1508d13353442462a2a57d448e1f044a9945971379ca1015a6d23084a3dc9d144f0e28e74a07430daaa589123fbd1aa3c717352962e3da8fdd14ba1ffbff67050a8d063cfebf73accd3d28b1aaa28a37633d30a422f36d4d58eba495dc4bcae2c35f921880721c6b8828b981ae018481ce0c5182078517942e9861eb3330b0458ddbfb373c58bfab1ba2b106609a533ac7491c6103b3b5772d0c505622044860b0aa0c8206697b434518153125586e852060d225c908b0a9feed293a6263124484a62488c27a0c450436f510419242f65c4a0882e4f44682153c51632a6742185abc320693013281282881184808a2028828000a042e13784bed01dfecf962390f0855c15ea5a3712b7d134c448ff910824e9ee06aaed3f98e7dbf1d1596ac377187f347f6dc59a91618c10870aa26e2a8e82880c735e478bfaed9839a1d04238d49f8fea38eaf7a3ba5015b49f55dd91ba9baa82aac1ad4ab581a26abd412e4f84ea72394116953b1ce8f5a83c55f38d3b3a821b42544dc2959d5ad2b5571576fba24195aac58d8992ca40aa4bb0ba01afddd56451356ce36e2b536b7c3853b56c831d4efce5f2430870e2df6cdca17ca89ab6c12efeb65c8a045c85dd72f9a1a8462116f36cd54225d3026125dd7a8a61c154ff6196bb4ba70a8534066caffd91ed35cf3bc2793e8c906098670cf3ace062aafc2fd6a0fac7330b04be33550cf53e94bdcd3bc157d2312c0fb46159fe3e7da0ba6daaf0b5401be25285efd1a9c2ffe090c69e37c4c38132e91ec8d130bf7d0e0cd81ea88393d0f2f0d751042d2234bbe54320d12b8750d2184f9e4031baa28404d2921125244b47c00c29a293f3d80fc6aa1a92c0c83046b8429ae2688a5764c8dab1b34942e042fbf97d70cbbf07201c35abacea8180b692eea16032f0c10f34d960b15832b824d151042dc169d52d771affa3b113123119312cae8cccc06444cac284c50b296187b503aba22177f36b89a97be909082b4d495288f1b1f3c37fbff59245fd164c167f7e56cdea7e942b372a95755a1e516979f05b801b723777f0b4c508a42d46412568f241688bd19586dc44010bb0ea72552809acea67d23588922879557f165c601eae9e2446d10e0e9d547fdde03f5c5650e3b763aaff474211e6692caa33f95b8004ff9d2792429525abe213ec26d64e154695df070943aa38aafc4748e0fa7609690aaea90a33d51bea30cff74f66304f5f51d98575f1d52df84b66647972058d2eaad63ed169c798fa8105127f3667d6f27b37e3277fe2132da21f35417226d4d425d03b77e29a28526b81b457fe28b440b4ca1f9d7298c540f45b55f6a2d2f29042318da96528cfe6a3e6074d9716a3681eea6797bd904665d8bacb38bf03198f7bf3bf7f96498e50e098826e9296a3242d45384632d463b303c23e68ba50aa3a3feb827eb8e6a33aa019a66b7ed00f57f61a93fa58b72e6cc9aab696a15fc6ea0668760956db4b81f6bbb048ae0ec8ef80ff033a8d34f8fba56704fbf61ab02ecc7b00025c929dbb0cded5767797bd9e41c31106f50a1f4e4e7a41d3956158e751326fbe909e9d27a5b4a93ceecd417a02e9dd28734af63070444137494b925292968d296c925211144e2a27291155661ccf0457f4827ef04eb3bc6ed0ca155fe3d86d1075c5f55ae86417345df1e30b61ef34d2e0f842986799b5ddc59b483f376d6e70d3166318a04a03716eddba6677c4e18e103dd021e1366dc907b115019ad0645da8ee5b558e30d47e9772d3b018637fc3ea3fb5961dbbdb398e6f6567e3b26d833df827c320af03c2d64d698115b0831b827afd76bc0e08dcc61df111b86e5ddbb6dbd234a04281424e0193a63905501c06a49e7b2429ef91ecfba0015db267d6a936713571f1d6016d4f3b20ee7d904197ec19578aa3dbb75271440a503787cccd6c07d3415b9d297ec447f0406feece0118b736a973f20683649ab6c1b84030cfb4d7b40e4813e18b2f2547a47f466ddb1acb382236157b4ddba6641042544c655e0b5c31c73cb8f125a46df74cc1b2233e421f81705b1907666b5937c5fd9d1f4e895c0fe747123465c98f9a020b5de7b36a1357f6da722878d601653fc394253f2a0b2d4293139a5497c3509d884d95af611be416d0b51fcbba292d30d6dd33a6a6d702fb7b4e026f394bc7d89ec32be62d300afef3537d564cfa28a32bf65db1972bc0ba409a1500d3a8d193b0077f5c5380ff55e87d11c2e89db002ac9fac552e10d8b5c0ddc1dad38bf23d05ef84f5d2e086e086e81ebef900131922970637eef605e5072397c61b772e2c0ed2287a72c19023846b79846fabb0d737042b98a2c805038418d0b6ce6eaf360a8d30dab2cb778e487c7fee5290ece10cd9ca7fc256f67ccce08bdf026c52f9b73abf09b4501ccbda7ad081180a5f8b8f755fbfd6599d727ef439dfa7e7c528a4b3f62cab737632f3badfbf58e3fb482298170f9498f3b7fb623f23a036471ab4d5e9800a7e08218c72215602a5468e5076818de88bbee88bbea811a00eb4a010fe306116bb0004f5950291908c9a2061d942bf3d03b154177eb02c106c9b5266111193d799d79e794109435f404829c85a7c9931093c389fec7770459ad45dcf864a6124f80aed6a689013a11d869aa85baa0e1ad11c1a3a154706fd26d53a252c708b593f42dcc127dce24f75100a8f00c42da6a95c37bbd5a52ad645d99eeb9430f1cd5b114151dc134644aa2f6bec9fbda3fa91cfd913dd7f638edb95aece66ec58d49d75deb1db158bba67bbb9bf63c78ecd952b83fa33ec0161a5e9827e5e2152160495ee76864ea54c7bf333843d1652f95e12687346c11c452603e81e0585adc96db9f53e188ceabe6aff8c82c2ed54802fce67ffc34101b6114514c1663c4a299788908df53423c5c0a6944b444ccfd6ccf6c8504b98cc8a6277bbc4301648778f521a49892ddd9d8978cb6c0c4c4a2329180cb2ebcffe3d3369447d379495149614520a298594424a614961b949319162627b6161d00983c85f61eee37266a858f79130dd50180406f1b9b1128af3f1579d49ba150685dad6757fc856f71906f1ffa03c4ab23d5e681623096cc11e0cf3e4115c32e491bc32432a9bdb0fd0ea0373ce6cce6945943ae79c273c1962ba42c9730001520d24a408238b153e64e1428b2c646eced05837290b0dae408a42072b6470a19abafb71851d8be13f8b7924941e4ba7e97b5afb9f796a6af70fdbda9a002b78d8d656afd96130eeb2b5582183371856d490b593153a449a251814d9604608271e62f02507180e2f61ca48f1b34505462a2c3571118229ae9061c4aa9bf4c58a5a5337e94b96fa51953655da10d49e683da09850a190960713aa6a44f44c9205b2f145cd98d40ced414587342327ada85baa9f2806f3381365b18aa85f8bb220c700cc35bb25836450f7c4497b5fd3918162f56361d2089347120966f103e00c85de0ab07e511a55fe8d329bba67c626189b3a3655ee6e89e46505137c7cc226fa4df8f2e337b309cbac139ac62773e74e98de5c1eeb7f0216bd2898b79e6c2f8af4ba05365e3c9da5c28f4c2ccf462477f250d5dd495beab43ce20b615a8682b12c9a03155a1e5a9146d42dd8f48959eceeee6e4389fb824e2be8b74d145820ab1155ee76797c3690aa8d2cd5061335c0a8fc5fa6fa69c0bc827e342a55a667bac5df9a426dbc240ab83915052a59c8ddb6f85b225436000d94fe3febe70b528daffdbe680799b6385fc7150c43318a7558a1fbc4fcd2e4c731dc7f37395eb815ffdb366655e8c117b3ae50ffeea0978d6f2a7cbd98159f5382faf3c3d99550040cd665157a8160f863d538a683a72d41ee5b88d88945ccf39100a5d4f811f330e9c157085da87f0b9137754e9b3a678783c90e07d36447ebbc51a7b6aa53ca9f527b4dca6eab53763575ce0e07857ad9e1a0fea666b343bdecb2d7541bd60dc1861608f6113b324a5a568c48580f35c6a8bd5e98a44ff34728f62ed03cf58ea89e3ef55ca0aff2567a289aa73c50a2f69fa452e3cba305223f66a98fa89fcf7d94d85307a39396074ab1acf8112a31ab7aa93e37662949213fca974f837951697948cf0243e2beb82dc55c0a7bad4375d04b3721273baed22ea810cc010a412115b82cd91e58056cc5c7513ff85a61de07bb90de07b3e06012664d2aff83afd92434a7cc11625a02f3141fa21114a11935b2931a7f62f28bb14c8d1f9920941adf02497e908021ddf06177d75bf739b78dd29b9bd52a6773842c90feecbbbb593baaeca84261dd9eece12f67c28fbae407eab3b801045457b9b0ce8505f1806668d7f4a6f8cf90b9306f852c0ff850088d5fe32e3505f5f1a0a0c8f9d03839a5a6a09f0bd53eeaa3a6260e4a4d41e1afc466aa6a345b9733a13d29856d00b9bd7f53c23c18d002910f9f09bb307be08ae9c70f6bce154b29a592432498a50c6c59f10c1b5c09f330644e616670a84d8127214a44b44d4d88562b4a5bc86af52feca8c97d71f08346864f199ecc88019310443ca80006490e058eec898a2d9ea8a208a0a5fe62054636643027ccc8ec081c3adb324e34492760e02f1afc78e5a04cfdf6c5a586fa2dd3827989b1137fc8cb8b17182d05daea7c98902660083f90c170e2767ad2ab24382619249f20b440a291f3048439a9bd483bd4c69eb84e0d2216639ac69431030d0ca862dfafc469c8b6e0bfba690729041fb0a96302a49ec9740cafcaf09bc142a3ce18b22784a040f88279c0241132c43522bae98cb135712024f133e1cfcffc59b17e660827a01ffc914cdd4dedd44f8d1131f398c1a8f66b2f1803e82188000ada35bc2b7ee0afd7cb8891d7eb65e4f57a7941bf7d1509814118f680debbc30449771b81cc1c41488376c31ef0a5116d5a5a5acaa24356c34bca962e5a8870a2df0a9a9102ef08a5b4b7e47c3c092f3ecb1199406da4667fec972750fd5b7a11fd26d24426f031d09250c0024998a71f42cf88119311d34ef740253f564fd58b13f0058da011141212125a254f4f4fdba4850aa920e87eab7053e142fe76a528047686cd772fcb7eb329e3cfdedd3a9d67b3df6c998364bfd976963db6db14f82c9bdedc19bdedf9824905eee328517e28015adbb703ee3e99f987bb632be6e89399997b325ce8de7bd0cdcc3578a0e2be3df3e7ce39dd71a0a0f0e5e7b84251565014d0ae30b5cb69dab6bdbdae71bb2d2386b9bb7777f46ca76e866277389d6a39ae601c0d8df48edd282b5660158a89c2156065611b6edfeca2b67062b81b4709ded8558adb3454467383b4ddb73cfbfcbd0a9ba56e7b94d8cce272a1fed980fc5fc7aaf5e7d5c8f299a1b4ed61776b4d9505aeededc01a31c60f6ed277175543bbe1b6ed628cdc51a4e17c9626a5d1885383fe76976a37356c2c8f6d8fd2b09ca1344df38de3dacfe86d732eb5b59fd15e854b694c3afdea1e5e40546698c3b6180a250171c6e836c2bd2a712ba271da022fa22aafe25c91ca6b5ae0453536a9b5f19b1678d14d0d1b2a28b271a35dd56ec68d5595558daa5f0037b612dd230da25b89ee9106d1ad58f118c388561c936284142b53ca1152e011523234a4b4eca07e15e7d3d2bd1e9a81159a1a56b4d581de3225a5c42687c5c7e666988f1d2d96918eac5088068f50c8931e959969a8baf343695ba995bac56d44b641da08f6e0df9ed473bb2dec398a61dc095cfb9723e241c5324f49b7c4781ada41a9db08068139f02b41a9cccced4d0e06691a926ef17760445548b27352ba5afd3c6264c37eec703b3b4586ecaeba2894ee60168b6668833b68a1dbdc1c2a7cae617bdae839a89568daa87b3a87fa41a13eaa3c543f28d4582a07b5527b3d8463c666888d84a0500e2ad486d2dc3c51a72b3a2951d5689679ea42315cf08f106c3a72d245520e42ed050f22b88234c6052378fee20a9007f5db2a37464be5deb99f5fa0e0a6fea8aefdcc811729a09ed4859731f3db2a56bf9b8a3df633898b19346559e9a8224c52961f97a4528809039e310651d108b5c548a93a5424a183d28f8b99f38144351bc3a22b314fbc38298179fccb7021930408b2bc804c11648670776705a07edb4d14f5cb09e3a5cb1651b6e052636b4b8c154251bf9bea393a60c40c20cc0dd57ff5cdaa2d323aacf5a2c5114c606c6875a11fed0a5fc777ea77938595e95ac4b0d2817e734752b9c972946ca87c08b261daa2030ac4991aa61b4c9a209a5431c8149110ad21c5fde044c3836a03e22855c469444c5c121cca07a7ad892dfb2148a382ce1f90505ba430a10c8b32347288103f138b1ba08230ca4206d40f59e080227acaa2874964940510d387a22c86a819e283124e1436454e84b971822f6e44e1c418589ca8818d2328f76303aabaa1061345f9604387a29a2e28e6821a55d278428a1a219aaa9bf404989a304f9039a25bdda427c820e2891c7ce12405086296f87dbd8065205f7c293529e574aa3cee0d617c975bb73f420e056cce2897b1eef3fee0e3a00021a4a2b596f8c08c0a3fc715caf02150f35185cc4e554e895896faee54945f0a3ea7e744a98e06a56ddca49486db345486c5b98209d9b5d1c320e44e289a462f26861e52f73633f596a04bdd875c3608dc413ee44ee00a5fe3c2a0cd99101f87889782336b32abf26b8160cfbf028444740e0d41247ef2c11dba14137d70885f4f60105e823df8f909644e45a1791acf05185c292f063151f744a32f5a0d556697f2f22287c22ca22518c4817021bc8ff97388a0f03f27c2f8a87bdad5ec62ee04af4b368ddaddc6ac278db11b682bf00faa7a2098fd28ce0b0d0bf59fefe1405f3082a47bfc852947a687a3bdfcec718ec8cf5e7a38fbac6ea19e6583272e677021840c9a70659f69de91fef9ede1acb7c2917e7fd4331457c880030b42e00318d7fc897a1f63d075ed18977b2e4c6f35fb5f881ecebefcf993e55a57e6bd8079380c903f1f67bd23f227ce91f8edea6e5d5a6f1e0ea86fae01d085e1ecb3002bcec63dacb3c3d97f618af62f648ffd0b5338ef88fffc173a9cf57036ef48f69887b3efd2bc23f3fd5f700f673d23cc423d926ea128171447c3d1b07f40f698f7c29423fdd93f60bed08bc2fe47f7608f7ad48f7d72260dee72f32e33333b179186b907680af67585efcba33d84d9c3dfb1f3a37b520f5f48f7d43cfc2146ba677bf848ba873e840f37880ab007c3d7b6ebd1f1030f1a23f878ac8b42bd5501980022285920ddbd82d725cb35b88358b56dc3ba2836df3ec1a07e41a4a31a9a27433b0801318b49c742a76479f0ab00832cd90d2c0f7ed50eb4470086e8370760b364e3855bfc667c99ca2a21e8e71d8f10c2d0cf7754ae03523dd0f6da546daa8f8fb9505b7fb75637118b392b961ddd63ab3a76a93ae86a158da2f23c6786b92bc2a715e0cb30197d0ac0e635cda6f37671ecd299a99b6e6dd4a6832e541765fb19a62b5b11f673e4e0b8f12e00ada2560e2d589d0be83701107d5361ab0e848d165163db4ea7625d14d5a3ba28db7b17a55bd3a0eca26cde2a4adacdf0aee511e2cb2e8a6adbf6ba0f5e6976fddb799aa683f3c972ba31ef6fb270ebc683540c3ab60fbad06f6ed7010e19facd1c9c2ec7d6e1d8b0ac361d60d06f7637b6cd87ce069397857e5367dbae860dd24d07956c6e9eeb80a6eccff0ae29d96bdd0ad86b308ba8dd0d9ef4a8844db825b5543433000008024315400028100c87c402814824cf945dd50114800b6e9c426e5a301588434110a330086218638c318418020842862052a6ca3800f8bde78943c4442b92bb5bebf424cba8accf0b91da796eaf98062e6cabb340647b0be7f24cdca6489faa30f9eeaab8af9fb01443e1164ab0e0e5053c2c638c0a273c5afa36d7c5a13e09ecb3386396e9f69cc98bb7a84ace6e629024686c302b19dc205e47da530e24825d1cf183643821f53d25d4d4a8878f413045fb70d95fad7e7fed1707645295426efe037d3bafdf009bf1632bc5730df215e6b071706a80c511c5b663f4adc1d97625cc42ffd1c3858faf06b4c2d1461031162cd39501c440f6db593189d7de9b653614bcffaa5261e29f739eaadf2484695e80ea2b451e3adac1b35a3fd1c641f37b8607eeef6526e6fa71fff630b6227f23e385af80528747543e6ccc5ac57404a5db02c072dc36f55fc957aefd7df1fb16da19b49b2518d9b596c121310c0918b7d5f1e12c48a4d3cfba441c00631ef0d06fd70e0817ada5a99c3c01317b1bc4c500b05265fc55c329c31954eabc32b9bc8022f66f749622a4a2184d86f231d06ffc0e46e163d060e6bf01e3170e4bc6f399b87a0ed28dca82910922155cd250f5314272cea61a9c7dcda9b41a4a62c940773378cde7540da7ea5b7ca60e11fa1f395f8e1c40ea8bb10c356831a70987b2b00569486c416be472d38f89b5e162058c206375ff2d64c0f29db0745488044e603a2c8cc10df7ec4b7201ef2fffe275fb54509b61649b9bc05621c4b3a0ac603d3f83c470491f6af2d67f1c614a8aacf73a493f069d87e9fd0bd825354afced7283b1782184e8f0af8fa813c15699e976f9db20f25a84241a01add101738317633cc27bc5576a132d9b99546665ae581906e7d610749bbcaffd643d1fbf7a1e92f9c54fb31d1170a4e6b10ce1eefacfe8046e515db2c9a15090db3d89546720b485eeeeb88f0ad46dab0c25f8bd90b5c5c705d76a10bf62a37771dcc241f919ba422a00ff0c4fe9ab2a287a61fb7bfed3a062d2f7f81b9152ec254279ac14a10fb122ef9b9fcbf92e8592360f8b4686c67822944868f23abd1ced068c616b1d1db9a06ab5185e3b9947b1d514c8f88b0395768d9d78002a8c3bdc1ddbc7f9dfc3dcc5b72ec7f3624da3cd36abda4e1cd0246f8a66c5eaf17cca87da62c8f21ae525e52d05ce0155922793283c02102995144227d928f60183ef321d644619e12a7a685f50b2764b5e1a21fd6f3ee458908f786bf32deb7e25e815fb71d5f92386ddbd70cc771ca2f556c6f96a3e2cba30db1f2d75bb31c5b2723a1002d894753c7c9157a6fd52c87a9b9e38e79e6d69e9301e9fc1a6f858b5a99c462b353e57b745514881e37743590885012e0d562bc0f85b00f5ac2e979215052534d32e9215afb5916ae491f786d311af95afe33fd48feb3c49fea9a049735cb2e5c7bfb2e8799279712cb1e9f7ed3d9e5fd8d0bd24be0594e31772a886e3e122f8e2d40b232ce1b48ddd2c7fc2b5b8d301a1bcead33dd0706d883ccefa3c550d0b380934b34f35c9cc297dae72e8448e20dea44ef6e05e4e309a87abcc2ab0e843058332b187a9f9a9474f3c7f1d80e8feb824744dd67de8a409468e097d2438dc608f65351d757f08cf0b5d6ba3a92ce927382212f48e6b3f300b41a8ba914a66d53a6509583b14d90bcb06d9de585f0e8a64fa722c1b1461c1a66defbefce4709d4aab97394a2242eb5e5455b9b0c8faaafc44ab7c5d18aa4886b80283ea121d3b98b96e893918da223a798723a3cb3215aaa821dddf349d0a6aa621f52237177c20e6ff798a2f937f27506425256fe0101934a152c907c1ac2511826f5b3ef4a25753b39eee0881d5a9488b96b356260b8a05c359ea8702a07de596d8e39ce76e2800bd476e8bbf9656f96fec81d8be7a184889c9d711555b752f065a39be0b99e901f928d0063888dcddacb3b2dff6c943b0ec455652b36835765668a21b22540cf765c4166718508af50ab430cb8b96980b6e69464a19018995a794041aee3269e8e44df47b0a5e041fa3b9b0e79767a34eb31c8dedbbaa9c5e032de0ba1a1f31806126f73a4d49aa5f325e707917a2e56fd060e845278f1dc9bd8b0dd5e059e004002f1157a32d96153e20e7002bd6ff0e865a473ccf26bb859972c5f2c000fefab55ad9f1b771b368738abf2665c37f2c3dcbeb8f7e112ad7effac71e6410a6efde59d4a2a985700638a13ca12e310cdbb30a2ab0a714f9ab85b58a0c246b7137735d8707ed25c4fe6eaf1316e993ca0c2aec10585c27614397c663a260f23070367711d54493182c5cf110481af5bd3e85e4b7f4ff7e2f5c37d62814622b4efb86e618d160d37f8ff849738172d3d0cc0803848aaabb0ab094f045f6a33a76920da5972e9a0a3a1194d0e8fd090744c07cbff665e3a3f5cb020aa464f3339f22a4baa248441fedf403753ab9f66e8e1707c8ab7e1f1d71989af4f595e737db018d5ab2a1134052050960412781d4859357731144cae6e826fc82668c916d8c5c47c282e41037c86d2c70a64afecb9eec50bda8f740b4728ebed4e821a6027dcf6dd4dab570d0aa6600aac82a7010d81470a28cc19617fd5f2f96e0fbe305c7e2de2f86273f1181c7c2f2ca44ae3db2ca5d6a4c215c301eb6bee8f42036c12a13b5e5d9f0d31485a6b06fa5f537d4c2bc89350e2949eeb4d1cafd0bf08347a4d1363a1f9a893a4a88315ec508b80b047a81e43ce778990e7ee482cbc342898e885c7cec01235e10ef4e3b917fcb09072301279b4261dc88202f8b2c48b282c903add9801ad176fc5a5221d40e97322a0bfa00e493f69b9d66bc3d6893a0bb4c921d63cb3ac71bfa5df662213e8eef14066c590632cabddcf0f0b9eb6f9f77f249740e80b2de56c8dd7b30c6dae420a33c3859624aee4ce1f6724064e3fcec0de1b53290001ff59ceb88c948d6053dc87aa276cabc87d3e59af05a7dddf67155bb438095a0ae71df9385fd95d2b9e9c929e135d34fd7df74ab4d23a77e60c25ae1751d47648188320ea026cc890c39ccce0f5678cd4ffd3dfb7543a4b6dcc90a5fc2bc43c5afcbfc12ddf9f5caf6c370c108055f60bd7e6dd17d6d5daa0f96fb3af48b665e8609f77543fbbd0e15c1eca128ecf89a3d28977498ad21c2b57f55b2a7bfb0c6d74889443508a7d181150bb7305ff3a528d9ff172b2f920f1c1847ae417a2631df5fb52fc5d76a20c2ed84959d44d4045e5ac7c9b3b2cc8cd3b3611e09cf3dd4b357df06a73795f7c6af13e99ddbbdaf5ceeb17ed5e2a6c7ab3c85eb0c2e53b71eb15d102e18ca5936d4f3f640a31bf3f99f903cfdb0059b4387af289a3a9861f5477986bad36e16847ab740bd23d69564fa00ba460b7c9af6494e43d2b406caa174ac10d673c85d9d65aeb44737abe9ab7a92784bbfd186dce8e7c5fb0f7cfb7dfbda2888d3dd20d1cb6ddad728f8b9b485a0acbe87d0f7e2fd2b85aaf43445f360be5a9fe8e680e5bcb33224c01daa92e83a033121ef590f29f8a12a16ad2b8884bc773dc460875459b4ce2092e5fdf521821d5015f175829894f7d6860cf6501589d60d42d2f1005480830065fc97b8e0f7f3ac3222536f23fe95e000147886404dcc53c0084379a404ad44081af0a1c095be62b2053b180ac7b16ecba86597c4b364a912d0786a8282a6fd0b207e9372d07808e0c966f9de7f491972d2a29d12cf1667058ccc21ff9252d865bcb9c6e5cc04dfba67b4d0e07903c55391994a628741860d7fb51c7a0bf6dec24b76d8e958d86f6cb921c6cdf3d60b8905bfce811d6642acb88ef3a5019f4eee8a2b58b0d80ce1dd48fb40324bedfa6de35c2b783bfae47ef27e5c7859ad5e5a177df81161ea0aae5163e07170d233d14f0c14bf17a47f2eb10e9f17e8038eccc705c3928a73baf8a160bb3a59d757ff4837e7bff2e2d55705e47a4ebb136bdbe54f74e862bef5836387405d3926bcfa7ff0703bab37569c5a6d8b22dedf26ddb5d39e5eb13378d4e82c2e6cc65abe02c3f8656550bf62f66021c94b382e7cc47abf957d9d3670abeec689333bce3188ddb70240d3a56f357e2be4f09ff98d53c00c66f9526c6ac50e9b13aecb77be15fef2c7023e4ff18ef1a25238533901dbcd0c081bde62e5457a0d1a697bbbcecdf3836f8a0b124a7a39797d8100772075796caeb99378d6be0bf61495ce53b06617e82c28920f1cae219b5792b1e2423bdc1ca8373b30f81fb0c62256bc0766cfdcf180bbb511a6cbfd0db00437c612dc8bcfda7d5c5c7372837454d678cbf973c50d38184804331d1da0f223ad0800b2374b1cf2db0b0b5f0453b58f0b88f415961d4cfa8e69c77e77956e104d6d85c0964a776ab8d33e3cbfaa9aabd27d95860d7388b7565320decd494cdf0eaa90d5c879c5d2371b0fe78aef3c7589b55253b790feccf48feda8e7c40f363c37281cb9e42bda0b141b00591a9a3a95162e625cb7ed385d2e5b03683b5a01cf1f176d48c75b7860ae3902a6e01880b90df9135b7e6c0fcb810384bc3899b5fceb1f038ba059fb960dcb0ea8bb8a94208303733cbab26c2a843852b2c92e5008174a65aa1bc5259ccb26a8fe7af8d04dca143c45d4d4ca9d2c8474231b98e0fc9ede412d0c9b52885aec908ab03660ad212819b496b6fb6adc1eb796abfd34639cfa3a5225465c7e35b2f5d2aeda91a04ffa980401a7e3d5657ac7bdcdcc4e9ef50f23112c6d1fbe86008e8e8c1aeffaa6e0746edce9c2d5a5604bdcf03b8790e27c8ccb05e810924003bcdd3981037509b7c5eb3dad734520d043cb4581185286140ab987980ff2622d64739c4766fa07fb019ac718d9f773cbe11fb6d3d5561411c2fe5a6d60650672b7244578b09ef0d406f0396b24ed3384f045b0a11b5f266fe8da8b4230c4f8fa554bbd53de01a8ba240e714d73835bd17def54b46c2943675843c41f91dbfb224b07f470c233d2116cbabfec4b7a988d475af891040684c7370705c1d34cdb1c3dc3f635306204cabdeba0fa4b92c292985c9d3501ec0bc15363d5f3a4967ef236631b62c510ef8b8f0c43f1fd8402a34e277328e08a9c17e9271f7a9443b795481d793432c6a88e81f796f8d0a4323514c850584ee70724d9c6d962ff9a2ef34b7c0f9b422be336fc325970c9b9031d982f976d2b96a6ad622f43782129228d2d7951f36336b1b970d6e25120bb972092b2c29aeb55f6eeae9e9bdfea1c2d5bdc68c53635653fe248bdfeaf185c624ef2c9a0d1b8802af3d990302faa8e933606fb48a0f4b6c32404f4ae88ed90c4533afc733712d3861f205d7047788e3237eff164216f73171d87214cadb0cfd0be7085a5f40379c42e2d13424cc4a25954aad86ce58b849254d2cf4ac655c93758359c22a9528bfd5227a0784b418b695099b76c6bbbad66cc0c4e7c13b098741a50646eae4fdaff07c7f9a12e001553bc9038d36e0a1d78d99425564c5d18a4fcc15b02e30ede8a2045ad819bd2baf366712412d70849c6756d16b5017a129b7bb7bb7255d1a99d066f6b4c98a8070f7dc1e3680797374016cba1d36a92283b594e3fb8273cdca458546ba143de7c613629f8d2ee6d2458faf130800e33cce7a5d3ff3245e499f038b6867e55459366ab195a4ed5a9a6ca85a0901360c59076ea81ff14ff0ce2670f37df24bdad620798a9f94ea2fec7796146926c9983ed232f621047850d8e3b44386651d2dab3c28e19217a43a6a771fef2ff568acff0e6396a92bce5f6bd3bd02f80ba1da007bebf80c34976d6509374881403d488c1a6d6cb2c3c4cdb26ccd6b377ccbea93f924eacef38c6aa9b508fc48afda0c8a638cf5a4ec9256edf164104755e4eee989762e17f15e472ab135a21341e14386b1ed865a08be3989dc8b5cb5757e6e3455921d651144c2895e344c4482550553531a93eaab515214c222b9f1b1d88689b5a0fceb62c5e66d5d1250d0c010f644571d51b866389a162da7a0a19ea155ef6f81c5ab6bb3b0e209d03d163302acd881c46b9b1e78871cb1df566f28c24ad1492ed1091927da349c5c85455f4447c47ebbd3caf59389c12dfce60d13db8a82226a942475c9b961774b435ec632303f60c328cf26e2436cb48726d5de0ada743fb74008cb194d1f86b4c2f21324a56f8cbb9e7688266892dd6dc5ba675c9b7ce0e7705a4eb43f20318062c128d1f8cc2006d2eaf525d8a030a468f39394ad84649fd53d68b0131f2bd0506d0d5ead66250ccf85c72362370a18935e20dc077cefcd668e3c083d1fc5cd2b6d9fbec17a4c3ee31fabd9182be7139cf46c5a429064e1cda12e26cc3527dff82580d39fdc4fbf0f9d5713536384462c42550cd1b0044e8ab80bf7d870979d7c5147c64088cff00f4cfe656000eed0e8648f8788df38c36d756346226c11bccea5969c33c3802ffd26ea928ef03e456f3c389fd8076bb788b2d53f28800bafcbe55fc62439b4f105b79eb22ccce296d0be3bbe0e67794993c56d677a6456b615873e6fd858f5da925cd0a5929b02ac1e20c25c6d129ca27bf86e04cf0b0293bcab382ba6d28f5c6470962053c9d149d921d1109caae9ed72d247d2c8e9379699bc3f19f5954e9e547a9a0e176da59ad806e1648bbcbc01024bcc25a86a9afcf80b740590fbb084ed3a92ee8e29a3dcf03cef94543267fd46a3cbdd35a4a89aea3f52da4a5a893c097579688672ea4b69d6a0e3961aed4792903e6cacd5031e1e7932d0368bd13e0131f288ba5748df4d3b7757f22235b68e22269dfe6c0fa0ee3f32f948dbbaa2f5a4244fafa1c75bfacf0e425e14648440cff16bd9c035856cd774eca1220af4f12b79feb34db39e84c52eea8cc00758829c6e715e1bce864d046636d9600d82fe3f7d20a6a1e2330cc2268a3cf3e174c6d9d5e490e1e22b0f1a62cb075bf9e3134e6456f67e2949177a84eab8a99d32627460503d09b84a9f73a7c9093f20a009df25b4894a22eb3209004a8119b238ada758c9aa2247361ded8b724c39db2f59741b2cf209f50d7e0ff77fbcd7b63cc55f7e44e59ed5e03b350735cea318971b5c2ad87987bc8278fe365c6c0a0bc5c64a4d292046b1aa14bf6e12c2d2e0e940eb397b79bddaa51cc72403edce26eb62a0f9632d8a02c4bf8f44a3965ce9e7572833cc5184e54a9025fff3ea45cc23ba1d73c0633a20ad91eaf68ec4d7864a989bbbbc67e79aac2ccf65bfe26c751416190b9c340a05856aeebc30aeb82af3f7efdeb254f64cf1fddcac40dee9c85bda1aeb9745898af10de2ab328ab234de5128134446f979cf050dd839d8222e3011dd094ce49cbceab9bc8729b464ed1f17799691e53c8f1404830a23064e7aac253ff3ce015503fe07099fea75ec827fd7468a5ac20bf1a03e22fb42f7bd46ab6478e93500a40aed2075e717caf4ebf4e5bb4c87fc6300382aadd0702e6085f220ac098d2e24160a6dfd51f52885cf7c71301e4574c9a7182e5b2c770841ae7973e15d8aeecd5549d700b28fe142479c69e428fa9c40986c5b1ebf683733824842740d17834d5643f18a2acd0aaccf905d479722adb1c5403aa462280a4ebe48160b4c59262b21de6102c678837045e1238f8f58a0c82aaea6382e10c646943ea29ecc49623ee405374b78f5a75626b29c04973e986724ab4563b9a04a2a0e5085619159168731f3a43beeae325973df8349b76598b90b6fdc9e1e4b3ccda57aafbb59d173b5bd9da28ee5af8e906d27a7478e962a48a469d95e74766ed69361297401b40a6edf866b38c897dbc332796661f7d8abaa08a6bb6660223ff1b50a369a4b48a02e1c6ef4a25329f1777e66f6ed4caa320564542467da232c4821fae8b40b28463b772b4da623f8879a369cc20211b3d0436436eb83e8c07514c7f849809333eb8bb474537cb621b702b1bdae1dde6bf0cdc83b000e35dd252eca776b94ba3fac6bd73e13d436024e46065e0d8ff3091d81c12e29c7bde435d8632dcc93c5c7b1f371bb2a3573ded81d1c1d95f2f898cef6e74d748fe46a5d0b6648684f8f64600901c973170e73caf5ed7f7c6f70c125c02650eba01c58ec53b2219498dfb7adf59d8d44565e54813b382199af6269761a9049388922bfad4ee47add819b521bccd658ae01214e393009adf572ad1aa44ecb640d3bd35ba98bd9fd2c0616239f59a46926509381350dcf62fe969b52c17eea9c3b4cce4d938055f5c689817658345da7cdbece5238e3433f45518bbcfcc56bccfa44f9725524f9d0c17561217c77fa8a195612ac6763393d0601efa2f51b56664d9aacb36ec815c988770996142d8b8401fa19f48e3bdd63e1fcc18b47d987b81d2003bc96c1b8a845b02e4faa69155707fac0cdc3a2698373fee8c874aa5d9ccb790b68d65493f2f0c8d17ba4288c2bac7e48e394d3adce858888614ab46708a2a45d9186ef529c0a2b6e4564fbdd1e116a20bb46ad8bd4c217dd3aaae43a700707b702f2bb8f0edc0d53bdbb3d5ab815fd5d42e547dd79004ebac82420208a5038752efa29baa719ff5ba9c2a28dce746d7547e618423cd9e772f8cdd0a82aa0d9d6f3f84904df71cf8e6e61a64e8fc2d249af8d0cb525e6073096965e1ac4bdc9f6ce9d60ccadd079a801f96edab2f71547d957c5f316b99574f8e8d64d9fe0977b3886baeda3cef2050323c3aed2d1da724fcacc5f93806f271635def59498ade023731bad642c142d0a565c4ababd708d74a4c0230bc6b8643a2d47434b2cad32151f1c661cbb774b6ff6eb92533c372a7b5ae704189d14a902cad4b4a43d2afdcec188cbcc9f305e4a922503832c4be341919005bf34c4e9a4037bf482d04463ac893cd58f6c54c8694fb12ceb3b1a1514830adc51b4daa208f6fa955aac037fd8d005207389603ecf0ad12e0ca204f028610fe69eb17d3864e2fcf71c99b90a6ed9359018f572f1273ed3f6287bcc0c7c9215e75594a5c9b197208e1ee339c13265836790193f91fe719f2867fe82b60ba165901bb5abcebad586ecd8fa7daea27035c32c01e643bcdf6806fc4c87bfadf623a8e51c64bdebfc36c8be356f96bb38accc08fc15b7a91044e8f7dbbadd009dde9a1c5d11b8792d24486a32f885385dcef1e09fc541c64f6407ae2bf33dc7f5b63c4c4616e062ed630362654024b72505f5054d4483c02fe0a50d57a60e1154deaebdae23c93e3b98d8f8c190a3ea67c54be8dbe1eddce3f8b8d0e7b0fb95d475793203db9ab0c2ba13eb00dba1ad62407e1291340bb6283cbe99dd45eb013d8dd6f1771c50924c0550d2f73c33e1197244b208ba008691b5b39b3320976fbb38a71e45d6b1b777d3668635b3ec8b39ce0fcab7099bd8ae68790a0afb235c652f3b7ec897c037047e965938238d6d7cfceed8d8b35398c778834f194dcd3c107d53c5831d8f6802e4b60a047923b8f0e37daefb879a888f365069f413450c7eeeb47b658c858a8415767655aad3a089ad1eb7789ad53aeaee4be528a61dae67dd64aee3232c3c199b9d2c4e3a1bf33ced1bd1e31bf49c9426b7272ad9f2add612dfba9f843d4eab7211c5331d8dd4723388d204fa1d7eab28f11d2ca5197a1c94045b186edff6d6ba9e3c232c00ff0f5764ee0893ee95f6e0a70149cb91227aced19f1b8d9ddd3e594bb16f6b82fffed7aaa6acb14f44b7596764a60f2d4a76de97ecc36afddb92c11f207f043d0ba4a464d04d74b8e667fecd99a56894494be2b9d14d2d810f5e121954b052c2c15be0d3b9f7f5c38b1c048970ac60cfd51b24a265e2a5e676450d8801762304155032632b92225872b488cc3c1f17bc25d203215b53421a125af19289861c1a362c29de2a0b0a29ace647ce8f8ad0fff15ccb93e8103080c99b70f9716d836ceb4fe9d497a3560a654ed27e70c7f52d4249ef3fa89feaa7a67d685dd434cd8e393cf35c62742a6435211d093b932a763a49b27382b70b9cf6226ea3bdac49fa63d5385dd12d366e3dce280b006332e1e8c88366060baf0100f1f504c1290a074fb767bccb2cf190e8d9384a60bd5ad1fa23872ca806048377a53114fbb70fcb4c2096f1473efb78da1de6a00d43cb665847b4f119743e93772cd75c8fcda7c23e39d5d60c90d5de3b9ee0a1c8069f7bba70d73f030b8307f4c4804743a6683f835e02cc2e4c0655d916f627d5872385b845c7bd70c56458ee94c9b9dd7cf93679adb522dbc4732db0632bb8c5221f898bc6ed476a6c16727ec981d51729c0af1fe02083c833462e9b8418c26ebbf75dcd3cbfd98d5d9c516376464adaa3825c28787f242eb02296c9f3e56803e86dacae0a8ff5869f9e94aadd2f2bef0e2691555d21f2b2198aa94e8159d925a2878c1af525ef294081173e40b2975cc40f30a593abf1a3717a55ffd0715c3b85fcd07665eb95ce35728e738363433af94d2720faf1acfede763c1facce0de37796ff1165144ff9d31ede6d5008419e64f3cd316aa9857c80fce82eca3323e52f1bec84cf9e8570fea51c29f8a243470adf24a78e7963274aba381457e358d285a6e151630a4efc3723fcb12416e4edd4aa41ccdd57b6d5d44a4849f718d1e842d52ec34ba0dad7a7b563a2e1dbba34f573927f02fd94244402ecd6bb9699e07bd205878d5da7deecd45887c8dc4aae2604f9c0dcbb068eb64902c4afde504a7b148045cbce00eacd4e19cce2bbb610dbc63124b836dc87873ad0e120b56c156f52574683d301800c073f14e936d4de20ebcc70f082a4f198ac49cdf2e65a056294e9dd39abd72ec2155de68f3e8f24f242e8e75ef7ef7663c5bd1cc6b0c3c96d7dd9ddbec094b5836f4d0629ed9d71943653226e989d9fc8bd5f9ebb903efea38b1d712d809655aa456a38be64b41b4020859cf0c558117f77398e6e5793bf6ea2683dda630b60def79082f376e434d66f48fb1ceda7823425e6069c0a06f108602d03d2ab17fcc158f3f164e4857cb49b4fc121956e3a29d103415e364d583534a2c5e567160e29c12a25278759708c6b87f0b1128058d7fb40841d19d3c18e763fc5db189857eeeabcd3aea561a00ebe4a3ea22d0e9cfeff1a1ed74fcabbe655a5386656efcd9b215f84091b333889ff87e9ed5fc84b37bb4a2a0d8ec7be4d30fd42a9f68f26e1afc2be5ff3d98418dbd6666f6f35b62524a040c1aeb94ea11414d09f39644fdab56f9d27defac2d1b25654709ba9618230c368e1f440dd5353eca3b72a8cf2b907e7c0008900361ecc7a05301d6c99079e7e9bb5eaab08ac44a409d2665f6765d0dc5d6762d78aac361cbf3a8d5caafbe2a07d3d22fe14bb380747d9f290621c8c179919484793138d326f2435d9949054139fac2f8de68d85b511822dc86aab7e26df3a911c6bd1cca897a6ef36251004a26d4de0f9425f15c3bd3e1bf922b2953104aa74b7826a4d5a61c5553bbaba624d7fe121a20f1496dc3c52ef981cbd31ae1d4bdaeaf31d8bcc68f91abe2584cd9fe98004f0aeeccda04cf23948619bd54a3a524a1c5379f343a661efdc269ebde74398ab620b3a1d0e06d99164ca5d4590357abd8dc9b315305b79f73542226438a4a38202f4cf2d55c4d4eda144665f71338a0eab07e8adca6fc4f351eac75c0bc5a723ad5214a7a0b142779b2a2cc3435bb13f4177e59b82f9cdc7ebd8ab79d480df72be7219798704c98fd101264f95c423320483c1502493598ffca5ea0e96d7767f058cbb4aa7b6538c695a044131899bb958ccb1cdb85f134fef5047faf9613f43014e8d7290d88d37714df0694092795ee54f54e8dbb5a068bbffe3645b11269e1c5b23ac6f2819670ab85fb6624258140a3b84a5c6431f7a3fa9da3bf7f710cb8c20b5d5ff197cbd28aa78fdbe42fcd5a25196b6a750b89727e1d818e54fc32086c99e9ab5de8ca8e5118776a9275ca40f8e5c1a39646a484ac0f9a89769bfe5f158e8e78a01e2ba18b8e307b81c7db61c326952d96f9b62cb8500b561d455422b7120a288f0303d2a10684c6ef1cea76484d43601c2667bf7033c812a908aed05018ed21fd131448ee892b6c7e3c2d474dc4392940e49d66d6a5bdaa6a8835ba80908ca59509057b0336536307d6a83c6654c44323cc3edbb2adc1ee437aec509ed6471f5c0c1368175f6c8442a396d65bce07d66733f735fb5ff2f3ee638b53daeb0a9cff1744f071cd9d708da10926727021b7352a09ab56f494390858c90783c9e1ed8a5e42366d975b1eac6f26fd9d5a2949b26eba5e306441c1388cb688c1b4c46f8adf2db23ea285826b99bd22c63c2890a11ffe9a50554ba9dbcfeb5357cad7f195eb2fa10056f6cd9ca043410e641923a902f87659bd814f19deabe9c2f568043cdd2aeb8200e12daa077152d93a2e62fd848c7b7ed0eaaab0523c70011fba6c70b5bccc7003942d57c7ec57b64ba1d3fe1def16de0babee2d2a01667f0126f2d25f39ee0e2f53983c62ee5aa4ea17063844fc7a21ddf7d6d6ec25e9345f3182f415f8863541e823b10f69d83a87ee84c13b11019129c4a882b9be912cc7a569e45d08aa1e0d75916196e054a1345f49aa83f503a31f450cba3fcb5c2b038bb9dada7e68ad781adc61f7640ab851a133b63c20f56b2c51c5ace6450541af207d9ab74c69e0fe15efd06a4a1696f5cfd75d1a584250f55e9189b47a5d685c730da4ad030d37ab066d20846887e606a8545fcdcd1c1042e8af437bef5f7c53e72739543eb0bf24c3ef0040658e608f641067c5b559cf48b7110e184b514bac753ea36cef8ae84c2d84df0697a080508a6e70ffe6fcb42e1f275bdab71ec66b4896a73c56afef56ada7f352ab5215898456380bce36cba40618aca7fb0c14d040493cf2b629510c99f7c277a3727da85707efb859ad5d4b26c4a270a2620a39e577fe09205a2ffe82932b5c483533384267526f3ef469e4d82a2d82642f7b90c3fad74feb0bddd1b162a1f9696af939308501f13a89282a8c52e610c28a71e2d8287fe0c7c6605cab52287c08ccc026af5e0c1574e48dab382cc777aa6670c7f8750bd3b7fddc1437116d558c80811043c8dee31909d85bb2131022b5e6249a858d01286e7d44ae7d2dd24fa0e09eff7521df51578cf15057826cb4c05264a694934d5ffc1df0ebb9de7cd68296c14d824c6752135d43c255f10123b6c18fdf248572a66b3f85afbd168e85a2749647c15f11c16e1af864771406a0571b128865b84411b84048b154d8acc5bd90c0860ee57035640835f26372d2c0ba3d3a1443d5070cabdfd8b5129fff1a7e73732f6912d947ca1c1b1ddab096fa1f26d90f58c9fa3b1f159c72762a142b6a8f0edd694020c33eb04c1e158aff1303367816739741a8b506ce79d215fc254bde2a06785a3945512f4021c7ba44508e18602f7bf64761695477bc4bc40cdf0db2cca700327e3f8e2fcda1102e4b10cce7280466b44f399560da8b44355f26cb212dc175d1dde05cb25af653658c0e3864ee45751f788eedef03f183028edb53a862bfe527a928922d9ce65d529eb3dc9fce28f75d2292fb13d21a099119260107b686bd1d4457c8eb08ba26d0f7f3c710769f901a3f6c5c20e4ec97a7343a328d7d9602277a3c3683d978bdbe6397c8a648f9cbd29302a4da63a85fb58e65c724fa23ff144358cee24a24f98e84cc66f9a32ef9d5a873bb2583261df099ce4d3db3a1f8806840924ebd0352c879b38cab96522122f9ab07d3a638f732640c5c763a8484194d772af524bd7b46c4cdb530a0012e0d57e96c7cf270c3e6aeab3171b31358e32599af6e02da741f4aa76be19f0bcb920d2a5f01811751ba0778fae509870dc774f4d2ce22dcec2a1cb43706624303b1ee1106a74bb7d53483a9eb13149497b0057163a2610522ca123c99f2d519677c64e2b08367545c9f712eb28b03ffa0aaf05a19d0c9d8c352d571bc5a95f7a05b40f95ba0947636cf89d515adc947cefcdd64b06c5e0671a03a1cd3aa485acff5a65c9d01622c25bf65133e98aada16e5aa8eac9dfcbae17936835e2b2d48bcc1a018cb0d3fcf771d5b0a5ce535c07f3f30b8a491df450c49f186cfd342b7c1098293c003d2d67ff3fcfed71c5dbd97f4b439891525e40302e4e0986027819213e828106659bea58212df3387fdb7793899260a854ee88fdc47e8fcbdb1ec87eba504ca6de3f29239da68b937941e3f7063957b0f4fcd34e8bd623a7cf4493fea060e1432b19450e6668d2a36a0c349fa4672f0f6ce879c383cea2b85ac2ffe89121c8fe86baa62f1a2513b0ca236bb73c31ec7266b9f20f374c059d9a55cb57b2af2fe68639b141721fe93517b52d578fd3fe053b0e6fc9e39492902c2dad10e31c42c38f9d3c9aa6f27080c3c2896f0a5a7cc2602c8fab95160f2efa92ba6590a34269bd3b3947a764b550031f9bb3e77167052a6034a0d50663d5dd36b3034968ffeb24843c050203620b15e3936f7561bf88b88c0f3050eb1202222d883590652c44454b21bf8ddfc285b2b2909f20d42366a9dd548500857002b8590c93580f3fe40626f2bafa667122ca372a7efd387210e2695249aeda02890906ec7af9a85bec28676abd189178214ff9d1ce62e3d57aac95ea7a6b7cb8c1a5319fb2fdee98fb6dc6cdb59160b22595f204dc2a135b2f72ba64b77213b4fc004f7852384aa6382086acaac242e6c5078adba803c9eb4b3bf9716b71272f8ae69228fab09f6c41e28990b564b83645f6770ff348494e977d30788edc36fc76f03cfe6d9ca41e7ed4f68315741e1cdd07396393501ec31aefc2d3309759ce95a8129c17bfb1d165a530d8ac948bdbe1b59f6cbd55ccdcfea502a104c69d902610abc3b76f8481c77d1110d1ef66c2f49c169af7adad94cb28dd27087431532bda4a4cc0f77cfef83aedcec62ee8d7fc238342ae9eb5ac6982802161c1c4513a60f8eb692598c4f090db692dd8f5916196c0e0cadeadeef4fc1a3b275deb6296dbcdc26f34229cf20461c88be6dcea1feb58ce6f54c61a3abcb52e0a9b65a72003a725f38a380b1fa36bb28ada0b59ea62d464345874c25b6d6933784baaaa9db9a98884019690d04335f9dd340f97c121e4933e32b314b078413ab9f00f310e4f58bc3a710d3f0355c9c088870ab1f2b361227fdcb516c21895c71d8b77c33f12849bf531b19fd30648b7cf67fd70dfa9362a2aed39baf792467c1346bb6d4f8148a882abf88e9335720be5945d6957fa1b5fa4be33ec5a35325f1c4db973eb8873f9e14cea1229b3cef36d97d6b1fadbad350462ede4258aed93730810f46d8e08e8f9e6e0f4986219fc0a9c9273e590265dfcf99b1ca8975ac1241c35c973cd2d4a5d65d2ff2f5c5d9f5be248150c03f59bc3b4f8c964ac1f0368fde5ec391cdc43a8346d7d870db3259800d618956180912a043bf9e846f205fd2b468c66c046598af4836cdf99c05ce758083f5cd50db3a3a15afcee24447b6d488e31b8585c4e38e6ab8cface1e21738d96911e45df5575368e15acab03fcda7f080a8893a7bf19232729d5e01bcb3b3e258c168dd0052283140811afe388c6c0ecd5698b3cebdad3646a713484e541e35c12281ff1ab5daa0177b1814292f9c608232bb439a383144a2e5f0798d42d68e2caec1016d91ecd3a2c93a18ae8ed64bdd1b859b02af14a376eb217b2746530881398d56ff90bc9d0e35891310442fb1ba9a0f7abe54b717542b02c34ea90327f95a9e619936459ba82a3af1b8fa014091471d3f0c91e60129346b22864ba2ccd29f33fc922fb1defb786cfb7915c3b967da43ae6dd9b767aaa7de2475c2aa108c69c739449c3f70d0efbf66ff64045b96f0b058be18ad8d94a7895512f3e24b4e8e652d51159c5a1774a192f0d63af95dedbb86202a0ef4ae4880bd3a5539f167c5bf9c9407d1d29a4c406c7f71b937d12b8273ac1bd6bc9cb67f039ec459defeeec97084d139b7b127b97a32104fab8e8ddd083c67000d56a3a8180997cfa23601a1bae53b9916ab25dba2e275fb3736b407a24e826897f45566bef8df969aa66fd1a3c4a062727b7f9117d8611e9573ed1e65bc5705b13f1c8718ac7ebe22098be6017f8d6563fb23c82693c0c04a2a6d41c44c64d0315a101775bc4c1cd75b6c1a780de77c22a8b2906282b72cbdd68cdf4a01ab2d52aab240e13b64e37ec3eb6860792e63778189aab2e935dcd7497f7af2d8d26254684411122bfa1c103a758810d28f8f0853f4c700270d2098909e360e6363a9806855107fcbd09e08d1fd08e5e64d084532fe04adaea91d675c94e07ea6a34046477f97bc165a009fe1999e62dbf0f146254d459286fc4c5462c85e2a43ceb6382c3ae83e223222e567e246400d29baefe43d3bf3c31b5e335d1d445a781b7e459833752006808803a4ef766fca755567b52e73af1cb57fb05440838f123156a638d2d35baecb4bee18aa1608d1290ab52d4173da13742f221e39f68bec66e3b50e523676cbdce0b5008aca494a205ff5cb5fa8069d8dfdf80bfd2a71eb7da9bf00be7da8fff9ac5b7ee9600036258ca94cbd90268dda552898c6b438f8388d327e481c8072f6fd0571721f2f26e66d352e9a41ffccc1984239363ff5a6e783594038ab674b3a3058f66f389814df0de7d7af491f4aa4334351a4b7f3cbf61bc4b7ad76c7c577eefb92f92d02f12a95682d99085754e28599c4424e6358ec05df1b6821817b870cf004035e5c510b057935acc4ff2f91bc8a59490c8190cd551a7f30314504a71cbfb942669ec674987671e1b8c97039c8bd2f0271eb481cc880a4e86bd70016d5b447f2abc62e00f525ba6f7258937787946839c857620e90e64b846b483d232d0f4b6beda67d24f830859b69532aa509aa12b909d3e8c36613f7b797ef5bd4495dd40430bd574df6dacc3300b8d21e0ede88ad048fc2a4c8c25e7ee16b7b9c57a20b79ecc2051bdf74daaa6a457e416444a9122c1e2c005c8cfc79c7991f6b61b81849fe2a5eb8067993c4e89f78062ba49aa4d57af86d4fcaec164394f3ddffdf2e20483326b9441b709b6d2c93c0622bbce6599a627f7ed9bfebcf49d2050a6ff36164dc25f3133600a6046369bf21652a188cd23bcb0d2e59a01606ee6c736c84e12b1c657949a84866ea9e63f883445626991611bf8949fbb072d0c9fbff9d0653153494f924bdcb8bb5aa54040679c764eda5f4b4c84750452373ba6e89cb55297eb928e71f933c2a5d28c770c67fd8bf6721660b2f7b8e19b0b3e73bc2756e7fd9d1099052e81d3c9deb456c461c6d71a5d222af4877340aeb6e79692c1bb72f40e4f94b8ddb97e641f07217fb12ffb7312849b6f8785ecaf7a63c0286a548fc5c72f13219b03e0a50e1157f9ba891aa80e7427baa80eb80d51030fc98e49b8095639b9508416f983f817607e45c10fc3cf87cf56843f00369912d714ad126762d064dd8120d97663bf2fbfb34ec48a0893a043012245ca4ce280af67cc6587aac1643c6329a5fbef225bdd7ad28fcf6b1835256c87c758d97be3d7ee8fde70fc9e6f5775983fdd85b0ae62d8b17d47a531a91eecf5b2bc0d376d622d2cc1ba4abcab5aa6504b6bddf79eae2abc925158b9c07be023c5b7dccd3b3db773f69313bd45a631ca38b03ede2f3bea46c4f9c10fecbc808579ad9ba8b73024fbb0adedb4121fc6f32fa11122f2d347c8e2c015931b6a1f13d612d1d2bf6fe4869b16294d84d840eb6057508b4d5c4dc5ba80c5f6f21c7fc3f2e4009e11e9c70ebf5511376105ed8fa22055fe29d40570528f3492e5f1147b0507e045bf2483ed8593239fb64014f7f7e6c98c372189e8ea09aa79631d0a379fdf959bdd30923aa85379f9fbc6b8a4509a8d4aa5d248e272ad0798cdc2bccfaeaaccbd3d0cebefaf3dd43dc64463815152574cf3f3bdb98c4eb33f22ce4129e59f8fcdc6c417719a309374633b55ae73d6ff10bb03d3a065dbb48d14d466bcdb465ac22c98549c67d59811a6b1c4976bba62fbfc7ab24e622112548bea6e2b9a54941614408c2cbd694c6b01c08fefa47d690527290b94f08c34261f2fbfbd7fdb7328592038ff191c4f7a6302ff5c76f30830a61556cc052394e1b42e033412d8946b7dc16a9722c22288344eb4cface6f99948ccf38309e2f089741d475e7ca5aea098dd2982ebc8f677e15c0ebf01c8b78fc7c6f6f1dbeba91b5cbc6671c318ebd3f895a86668ee24c236094554460fb9fae386fb32f17512f5385f3056da14a2f49e45c53d0215c4f3e9d03baca0d0d96864f9fc127ca5aa9df0577aab04fbc2e55e6f61dbc2ebaec7b63355f75026f9f8b16aa4b689abd3f4e8b6423b78c88c414c12e6fe1d1d26d48a4e9e26c8d5431e5ffa4b366d9bd11060dcc009f4ccea14bd0609b0af406d13b59320213bbff99a0ce09a9129f1f43f0be8f28dae95326c32a7b62d29576abb68f05d201a3b9855a5885d45103c9ed6d5ecb0ce4fae2b329aed04fb54597bbb2700c20c0bb5c1cb67226083cfd3790d6ab8fd8b501848dfdee9bc2356d5601391eff4b10b85890350039c38e6f3a9942a7518084528951f78bcf4c1df1e561f86bf6b04e0c7c6948c8b8bda01df79a6fddb25b12a813823e6bbe7e5eb7070f22c11c672c2f820da7b564b1e22468ea56bd17f75cfbad18d40d036ab2aa21b15e247153f784b2cfa3c20670a143409333f4523ec3e40e06b2214d3762a17d8fea50a427333d77cd0e33536c83c437ce081c19a0c7d0fe4e7432ba72ae7d4fc8ec74178378ed1e8769305255983cf4eff87962e62282c4377a125c73aa959c4122eed79fa4e887dbf43f22ef36cf23876ab9c2bf87c2d2af816f52c2fb40b88abd570701072df03491813187452477e5c161b56756dd4ef3eb33cde464389f270bb55a838b89eb297e128c8e6952237d4b2e938b1aa975dffb515a8d192e48280ab90195c8de6fc4a4b80d8eb7c61c970b216d2fcecf429d389f5759e08a934ea65fe62b955a3802029c821ab0be0aecff12624c8ba8d4d38a75468fa1eab7f7c001ad301f74e81ea2e4a633d544161580133d9310a0d5a7f381ea040a4ed152819f51eed7e9bf0bfc9f63063dd80a6383b27769c04fd4be9cbca4252551fa7749b4afe832c4f0a6cfd34e2ca68618c47558f06d3814c2d5c846b94e98a7938356c0b393595db9e5da5753d935cf93d4dca98fd12f38bc7a2387b92c64080f1e84100da2318f8c08bf1a96b27d9866b086998ad9d8118ff93b62ea868d1eeb261726fc92c851fc8820d18afd4a5fa2c2bde9db7929bd998aa5379c265352c7a418e77a7c0e79fb119ea195a84af8192258da3e6287c19bfdcf39c8b3bff1851b7259a15b31a0d7a0529e3164d754ea68ddf97355cf68783f265ace58766bfff9a5e91958ec4680d28ec6abccc8249a6b0515c3bb5c6212198ae2164692172daf0ec10da80560908a127524c885f801087b8bb94a91b937098ca5933a738f2b60d56a0e27cb13f19ebc2e169bd89ad80c1d5d5fdf93d31e25835649e538aab51a4282f5e257161502ef2fae2762e4b4d94c744fd29830599e58d3aa7ac85a4ac7f28c5a1ca74630a858430a7465cb8a21c6ced2460096b27aa4f8720c55013cc3d1e4f7e44af58c3cc569e1adb88d6340351410cc074879ad7b3ba56d05e11ffd80569e888fa1c662cf7ee7a3c62d48f40fdf9900e0978970d3ad9238c72767430e1d420273dd71840447f2291052aeb99c2d75482e8ece1b0732969551d0c9cb9d85658bbec9900b8df636181f2fe6dc1a0da33ad76b3550b8dacfd40e10d693d3a2695eed3e5f2dad76cdb1bb969632a95dd2918ceb131c07858ce7d4d707ec50ac63707f56e9a92088b435faab5836112c33588b32a8651cc10d00e971dc29f6a5d931142cef4ce73ba6cb9c385ed785f53cfd189e3824d048b8d9e50299d8291b7170c51df3d8030da3d63d9c44ac26d724a3ca770aaa754c21872cab63984275ca97c1f87ca812dec17e3dd5c75da7f123112723cae5474cb28a362716b7a8680c184f56adbc7f5dd348c67acd50ce496b98b216076c5001d1f8cd23c51fe36b5d9802ee2722af35f7a7a4e5e255d13fa537c08e1716ef0342823066f9de935fd69605584c01e4f1eed4ca01775de6c4c72ed3d521364714fb009b5955c1be42387bc7da12e9cfde62c98f07b58d6f9e834da1c4ee19388c9bd21574efaf199e75b4b1ec1a2dee00de0bc27ee82f053bb046d2f98ac037b46d5053fd96497d0bdfaba12cfc00420c0985d23a41bd493377209b52d648711745c6aa505d9d3687c46e437c0ad9bd64f826bbb91c1003fd46da111f7a5cdfa519cb883f96589bc366d3844c02890f0b69b4df02c40ea474fe7ba5bb56943f737923fed760dd7bb499171150686fc051c9a8bd41074106ecab4ffbe5eb4e1f44a3b5a4afbfbbeaccf70e87c2d552f2bb50c48e5aa43fc23b227b6cde2627b477eee5708ffe4286a1e5b8e4b4ee4bb3444841e93124986cd2330c1914ff876b3c926c65bc0987222a300b75ad8cfbd73f0243728df0ea68c0d6d0c0e7538748cddc7690a60098e7c35ec2c26865e73794b8892a938aa6452b0a3fc88e49017f22714915303e3acbeeb8b887555c8853ea528e92de8f28321dcc4fdf514646d6070f424161548218a1d4f010c0ef0afef78ba4a23951544fcae40960372a5cb8499995ea31ae1bc525188717a96aa933682d84bd2b75ae9b6d36b840292b4ca73ce42eaadd0bfbc40a4c623f2f75ee1f5cb9b7942c2b08d2c9bd6dab2eaef30201061d4304553b6b07c7001a546f387eca82c4f928e3d107bb408272338ed92dadacfc108ea653a8304fdc3f06e5f75e9cf0c695875efa9856af8789a336d21b5f23206ee5f1af7e8b7a4d26624598bb6f0388d96b2ca911d882435cf36f5489bd527b673f68e1e50c058e44e987a13296ba1c72cd7f952f002f530e6c87580aadc421959f5cf583fed492be506b309055b33d4a289dc6b160e0764be60b5ae94f017261d8c34f11304f14eb95aef90064fd9f67675e1a04456c07c1da83b70784e0a1262fa5313c0fc192d8266f0cc78550bb586278348f6ba9add7147b895260128ffd1dc7c333879001835ea8e9df61761ef5eca866ab34f083dffc132fd73fa48c92900f019ed0a9668a51fcd4bd4117afa9f8a0df78b504292cf61c833003e4d30d8d94bfe4be5b3f53b76c6804a421f02516a4e692753eb80a23c64cc487ac36b3cc8a08309937978f0e1a0db6987b8dfe7f1763efc0b192340348c8885d4512b073c94ed660a25b01b400a1a62dd7ce54f300751070b3564fb318a9ffc6727f2b3f160fb8fa6a2f9d86114eeb2a32fb80914020d2d40435012f9baece51b5356b44da2ce4700e3e1c3d66a4f76cb61d5d4e5389034a804b7699ab127cc2613a92cbdd45b3560f870a46967046bb3a6385d5828d81b755fdf8fa3f265184a8f1026435c036974802b4dc8ce7a0de0d2bcb170cfe0317d276ef1e9d41dd8e01011574a1dc03a1245fe4109a8f0bd7a04f3c3bdec6b72206ed6f66a8ec0fd112827dde2619625b39bfc37597278ee2892dd075ce49c3d806309642a128f94ffaf86199a50c38a7f2b21940ced401f4161f9c0087e90c5b3dc1fd160051583ea8300b038014d238cc6be12b6d3c02421ca038e579497e0356c29634762adbc3cd46576bdc59938b60b210652ecd6ea44951100fcfa7ef4d11e2b918439cf8d198049ec7e9693ccaaf9a1aae55e259d428c1bc03f5aa8366804e86fff10934d364cf8d95e7f699349cd3f9d4c5761735af8c9e7471e78f2ec9662d8677da0f9377760fa600f382789dfd15ed710249330e8ec373efe05f4baf1fd3501ef7e083849b1a58fd9ca5208c0679ae1e75d0205066eb43c150b1f82a6c874d9b941a01e1d9ded979940e246c22fbcf04f9a6b96238ca8db7cdf9ec0951081f02a4020bceef404eaa55337c1d10b5bd3877e477481551dc3e809cc6c43373b694bb3cfcb50fa99fbb2d451434353fc17d26cd0fa3c5ccc396903783b600a4e3f9d3427b0aead88714c0e3e54d8d3d5171fa211a00b2b401e4cbc69ec406da3b135ee2cb69b5bf8cfea0ccdbd440ff21811b0c62a276806d932fc7014ca916cd46d3fff438382290ffbc92271c0f5307cbf36fdf25fa2f31958c3b66ff3ed5a25437c3e13e44505a4eb5e85e1d43f6345c322ef95976a0c22340e8278258a2d6c094e9593bf4d4b1127e7168cbdf7744f413e1c6901f4f167c42c4ea0d5de8f49e4b1a088646134402f3662ad3606431e34049266ae816c12fe4f82927a1333fbad92a390e1697a13dc5382dd8c8d757b6fd706783f115a0f4e9bab74735aac14131d769f52f7de0d79ae8e0f694335434ce3e1aaba5ae0b80ebf95b3d9e8d24af5c84f48078ca64c1ea21f36775edf0ed882d19f939b49d2f332a45a265d9043e61784e24381876b82f26c001abb3034b1252c98608f39310e281f3d95f8408612434a20657c7c85f06a0c06eaeb404e4a80dc3e63d12c55dcd2c85a61bcacf3b253044b1b2793d2904faa55d6abb99842db30c5e92af944485f4918f0c3b6ae87da801440f315a94fc7132d90d4f05b66e1bd8afd67dae57fc611bfcd176531509e229a385f24f0e1456f43c1be38b29054b512680de9ec8dab59291ef8d21843a61ef497a34859a55d3a9ea8ea47debb8b6b8f3da73aee78e25c53182ae274090562050f469b7d880116d465b173d40e1c4c34308700b5782d6de6c2f725c27145c4b9e46f1aba14d3c73f8fb879d957783b0ebe0ff0e6cc71749dd920456e5c7160ef9a8f0970eb650ff35c4f796c3def371b80d290c03a7e977a6f90f7b2874acfa6bdc92c1a89ee1eadd42e7d4d67d8776686bbae91e5801dd88a822d1b78dc52d37b15a1bace7b67a8249cbb69fd03f5b290bdbabb43616d46be563a4dc36bedd5f23f115637ccbdf102544d53d202eaa0c358ea3eda8d2e558862b3d9ed11caf72a6bf2f6c31cdb0c22b1b0687e63fee5a50b0f9c41020913422ccea27f0e85665a0c4cd51196cf688d034c416613ec22702f37bf1222d230515e3f1baaaee1190cbfeae93d6956db1812729f23a75b8bb5b602bb5e289a8215d0b38a7088a4d12bc6e7bdabdc4e1da06774c1cc28a12bc108413847a5b64a4b768d0a35455e4005169f117b4e8d058b67f27f66877c6acab2846eac36a553495728808d83490bc9100c8f4c137b5b184e7798b18b04b004fad25a992ea1873189f395257c5b5b1b7c52f312c90c938a28566824a294670165ed28235e3d68a99b00e09bacf5e46e8f749d88d1dfaa75abe47bc200887526b68578069500299ff1e6c6aa01b7b7413870e9d01475f72707a5a2c9b841ff488b4edb26087bf8667cf9ee601aa04d436b2ccb1e313cfe313bc09ce285a3d630a5bd861494a9f5e68bd819bb983f688242b481cbd52f0eebf22b001a665ca0dac4e7e915298e524b9e92c27c94d673949eee6c66745a1ff43c18c88632b5617bb1a451e10af3addebafba138a8b7e1ecdc44cf04001e2f73b2cf59342a6f00a2583f28fb1494647793dad804b2474e3c4c3868ace76e60c346be9d7b2480531351d2904f1e5db85e4c36fa6a8ac23922b04b0cca0c2e2151685305b155598e258a34d85ec0f960cce2abd6bb51bac134cab210f67aac2de30a33947eec294290523b83f83b1583d09e4e56f0a314bda11c19495552f55457a4f268fd07fce422e87920a450218a2caab63e0759c5d7e19f68bcff394fc3a345732031d781059067cc3271817f93f353111fd6f46943b1ba2bba32baac821e4c56dde46d9d6a90867c5ae1fec81e4d4433ad3498aa28a2538ae443afd054ed07b6e2a91542b1c0d930b5a3d0d83b2dfcf4b6508cc6d09127025fb91e38cb157124d98673de63de54f453cabe5ab73b495cef49af532d9668e7b4a8fd8ee2cc36cbf53b2b11e7b00d81dce57dcd4fa0c87b5a0946d192b112bd6447e3d3edf2035b7a361ba3c8c923770e7a2dd3116d834708b06bd33ca379d1212af446b79ddffdd25b1477b29b036b4a5ca60afb531335a921d111b0d4320a26561d464ecec3988f89f68318892d19cd812c6f531d1d70e0476701436d1c845adcc0ec265bd72827444856861c62ca051e03bb353eed058696666d9745f36b5a8dcc58263b172279379d17582e39dcfd88b63a78c4f28fee7cd4c2e3accb0982e19953b189f21d6e310bdd55656a75aa87b3f9f6be63a38cb67e3007c8ac80f0018c0687b8171b6f235a9354fb2f234c6af76a56c1dc47775f6d0aec2e771d5d9ae8e5aff7e885aed57e719d1610c5dd10d969372972a943859edd664fc83ce475e8d00fbe0c91aacebf1933959ee173fc66de3b182b53216a22d9cf45779bd49ec791a9e080a751494994cd31be0f235ce71758d33c20386cb5d19d73e7d4f68926ac35bd727dc2f464ab1010603c8d874a07aebbeb9750f757310d81101806d14dc93db9532544c134b57041fa25b16afb4ea6ccbaf45c78ebebb246300f67110c239b51519431e8c8605f3f9a0e94e50912e980d4ee35a4480f7b029dcd7827cc65d50de6adecb73feffd7ba9a97f5e7d8768d04e9cf6ebe1371e94cb5e9a1e1c1dc570830ff6a0c777aa31497b7cd6df36e0889acce209f3ea68c861be9d2654de1acb255c87bd0b4b401f661d5844399f68a9daac6963f661bded80dc0fda2d87e6aaa47900faa85fdfe8e01b5ffe1ff614ef0268b69d21f19695d97f9479c5fbae4c04a232c65020ddf95914b4cffa8a82dc20f0ef2a251360e8b70d10da3187f6734daef515375de1eae3ca87b5d278cc579dd626ebcf88225797c53dfd959a10383d680a12bc4d425c675e0d04ed7c989d01ac08ebbf7fb958c3b521b231d5f6f1770a25534ea41d189def92d9f87fff182d196d8f7010c7334da22ae56c7da80af11becb1985d408cd5749f95af385872c9ca318743a08cb83d8a1158fe4771acc7523830e99d91194864d68c6237d828db1a6cfa31c764bf3543acc60905f2eb71fa32b13469ea6aeadcd0b3d5b594608e727f56c8b41181c8ff887fab8e9197e0d682af8a65d47b42d1480a344a45750f2e16070d0343fb5a184795ea23119b1f413a18b80d2ddf7b838eebecd9ce0eb1ec3df96419f31391c9e464e5acffa4d59dd66389b8d5375af1c7e066a8c49be6b947dc6dcf93ee895500a178284daf1ee6a9f401b97c66af6f21e7bdf2f9f452ac41dc795cd2433446766f464c6931c6aab36c661d1ef5ee687941e4da6b18cecb5fa9cd4b95af9d0875cb97b3abefb65a8301a74df93e5a77810bea847a87d75b2460f181c30a1d6253567708330b922b13ae33128b8847c4042a0d8e4bf322e5abb6731d6eb0bbeda0f1a179dccc2c56926eee02fe995dcc72f6ccde00f89c010e92a3d7b9e0f90405f782f301221480184914042740955791586d40ae5a3ad29e1185ea12f6ac6c95ad31df6a0e7bf15d2da2bfc30fc169763eb9d6561bc0af93088e6c35877fd0ad3a44a0a5901cca7dad89a768a44c6ca81f420c5a31339d243d0fcd0b72e130fef0948a775a47520a10f7e0b450869821a7533006efb2da9795fc64001be28a794796eeac6a1ecb26051016336e80d451ea166a265aee987493417e1d79739dbbd563f3d6d85fc8899159ad21f1ab8001ee7c1cb9301c1cf36938e033169274250347c475fd1930157e0d104cbc441d29b02a9387ee521bea40da7acc98a8264477b593471f9d69aeaeda50c313c9d7217e573fe4e778f84650fe1521d46e46d1e922bb5ffa2519774d6c699c597f144a667709238d1fdf2e98989aebbc0adfa0bd4afc2aa37880089dbd0423c452fba12aa515ea02d31002d2ff237035bc3a407173e4f9671e39f7fa97b04ba6b744dc7f4ffef1cdce9f68055fe01277b6d2823b9a118879d58006bdffefe0235867d6abc1e3dcbbcc1c3a5d68e3af91bcf853f0a0e84fcfdd7750cb19d162326b698aed6168112438656f1111aeb4c985d8116f9809d5ec3a02c97c68ac212c84b39fec809ecc78fec18cdc5b5166df8d928d59ca8dfeb5592d5bd902e4216595a8e79a730de3e1a7cb022a4f6e2b9af9a20ee89039a6f9f82daf2b6c76a9fe3cdae5c68e97fea6d0b8981b9a984008fb352aae6cbbf035ee0e8d20f499ef3e20a98d6507b48a9244b023cfe7c844872455fd1f36a5ea5ac4d582ea5a99886be790e4b07b37b65f1a013d122f2c87cea5521e5dd7a37ad7777f416d636eed9228ad9e4e3714a4c167787d0baa68e3b936403931f06e0f1c7d9fcb407e51e3b487c018a215d68df69cfb5e13f9c6e95077c2a8f882eaac90500e3054b332a6db5b752a51bd0d74d0e5dd31dbe280a987982d3b237b7c3e22b42047d5ecd8788d5dc3d92c62e07f356c4ed9285ed4effb808bf47da6ab470f7a1baa5b5345d48fa0413b97b5b42b16534b7181c2941d98e48808ab1c1917097cacdec81ac54dcfcaa725a1f27418051f7f1e05d6bad2751a0c6a809eeca8ec1dace01c1e0411144d157b37aba75951a4026a76b7ab96dcd596cd884850f347b42e59d9863a1a4a43172148c6ffafbeecbb7f1ca611010c7ca286362cef83a8c67c4ecfdeb002b4b1054225d1f876e93ce767f53a19c33786c274f16334746ef2bb81e62d0b6bbe0170eb601261fdf7ef6b574d2dd999729392807a92dc9aebf5d8afb7cebdff7e3315675767919caba464f14f62cf393b422a9166a8045a5265858fd499be8dc89b953ec9d67d53a06d53e13bf4bc265601b0b5021b105427c86ab2245779c10f2f63690c163b04ad2e8064788bc7b0b443c0cab4a9a8e3821f3c62d10e2335c1529ba830372e101543c478003006f3efc1f035a006a66593092b24a07179b065b930eb8c2d118e12680ae8d711ae52601b8380e8ff7cd1520e6078a99c979c55a551534e737e601c3dc1104b139fe574f1b73d6f7af0a0a1e5c90299708fb30e0c1f58f404d12b2f7de5b6e29654a290569062b06c906a81c32339e0cd743958a991386d2178f7a1c14bbeb2fa869d2bd1d337d3e15fe4e16bd5f1cf7ec9983828238e8e47d35d4ecf94d9d914efd643d4570e8d4914e01f1108ea3d49dc8d4f20ac73caf10e124e98a15423c81866c815c1ac6126ca203b85a30e9e1c2824909aea51e16481c41063f5a9534d048638bcc81bb400a16278a2a0b75b1386154a18030ea6840f49ec8e7be9f2b0d931fe8006e12eadd08a2f80e2038185ceaa752827e736d66dd4e4921ae981146e587df1d8429e04ec6ef97447a89f2fbecd4eec26622642bde8d712e43132a11387703832e4312a420840cd5dc5cafda0871a7077f17fec2da83eae2823bd7bba4fc6b462bc6e8ddbbcdb651dca27f13386e4e23d2888431d851d9fd238a53e0efffdc632f0e85b1396e4ee979906a3baa43873baa1f015eef4782a8e684f4a475d735af2bf30c5ed373ce0dea7ffd5c1ded2feff27dad83d9fb9c59d765d9cff73ccb20840ba15b5bb4a0710a5cc34af7ee9e60d06e4ba642cf48acee351116dc8f9e7b0ba743f9d3e34f2b2e8f9360dd872cce3e5c08dd7d7bc48f91ab82fa16d9b72f2374e750dfbf3391967f4b29a565414b4aef06c81f0318bf87d0a3737612894e1caaff800d55534a2925959223b337b78727ed2eb99df42a49f5a4efe9957ce7c2695fc475a6159d1eb407986e98ae2f3e30850ce9c7af6148357d7c1ad4e3f4f163904148af4efcf129d02b1afe5595806e1d037197c58a16fc0c851f080606c67b519180d293f7ed15a6b5c2450caee0e5428a2c5cc4804a8d1b230cd1854e452d84f46ab3d41eda1aa05ee6b12b7e8cf1a21b326a7c8e32389447cf80525ba85ba50c372a1353119437f8bcc4931cc51ab2184c44d08fa1575e0c9df28f571344d58a5b468c5fc30a5d28f6977cade341fb2d46edb7320e3e9f0c9aa77dec620e3f95b3df2d02cba35daf7559f7cd7a59c18ab0583fbb330d0aa34412d4ab25bd924c7a25d4aba15ed164bf157bebaf8ff25aea6e07eb886214560bdf5f7d9c6fc8685eb0bcb9b345236e017a338853c8c1fa606f9a60711ca8fafca953091efdfc731ca5412a078a01fd3a49081eb9cefccf813e079a4146d57f1209651a91eb6453dcc69f4823d288be0b29c84abd01568de842e216b230a609d6471684c5f10cc9660afa654444ee0ea1f77dcaff324307b5cbeab096b5616e81b9d814ced8551abb8a5f4612ffd76f544783f2b352b656d48fd7b03a365a9c2caa6e9e4d68f240004b02105a1f59f5258c285b41551b9243eb8389d5913fac8dff105028a9ee5289eaffc92a6ae4ec06cb8b468bc3d2fb2eff083b7a14955c47c7cf11d2d0162e71e9485084032889249250a414a3285edbfb221758eabe0695a01b6a54a4bafd5fd6c26e0aedddc05a687c964bd168714e31292ef5ea8b470d614c504c8a4a6868a9fe718d0887049249a4904422974827f1a857fe9da296b8e514850e51f5d9aed1c01ca59c25d7e1947290729292b054ff1c2e396ba8f093a4a5faab8044852545f07c2a1c813f4969d44f8520a13286a89f0a4caa3fe4eea6f4aaa3d2d124d12445c0e2f1818cd09409821e1d557f1310b43f0bba09ca2260f1dc0069a1fe5bbf1ba068e52608888ed0e278ddad866e4a67a5571f8f9fcec875648aba295dd11947d5bfbba2c3d21d755b3aa54e4b97d4adc1e3870750afbe45e2718407929595f5c12f876a8857aa0b200502e8a618b98e0586cb9ca357a37e3740aaa5c5b901aadd0d90db780e520e524e18ab73bde79451bf1c35aa7f8e1bd57f0049ae73793993e37206902473900690c4e36775a5fac76365a50422fadd0005e93a2916e706a8fad3c49b25bdfa7290aabfc77823e43a51e80628282809939ba15e6d953392aaff8d949ba21b2848671ca17e375382b830a9dfcd15d53f42ce419a92d4abaf9322674baf9c4bf533ea97c3a53a77eddf49b13efa5d87c8fb5444c6b941b97704e1eebdd3293a3d57c51e4eebfa1859dcd9c568675c5d32c3e841114067aef3ce5b9bf8180a35fe7f2a38c0abc0f21575c8a1bb0580a1faf354c5f8390fb0c089c816c917a8ba93c839a7942425505f242551fd09a9a41ec858aadfabb290e142c6185950982c3128a0f48a99a128b0f4e00739dc2043053b9011740419239059638c2e4863248d81248612129be0c2defc04ae86a2549aaa612a326cbfe2adda91840ab2c03e6fe6bc96bf7358a82295493af7c805a84a515506d9b5eba5fb540cf9e4c5b64217da42f553d19c61da7e780d6457ad8f56a9d607e72c0b5b451b951045965d34eecf92e3a44b7777e8f5951d1889269440420dcd0e1ddc8c4c0e540ccc0b3d99362dc32e2b2873b76484de8c05dd2a4a54ea14c8cc6850f81a7c38beaff11f0e618c525ad68cde61d8160466de6d28ba6d30ae96cabf478bc3de9d608d468be31ddd7e709da21bed14c7d8931575cb30305b5ca3531ce3c608475caadea1221c4b6bc8c8b04c34d1c090aaf0bb7e34d4a854390685ca2133936366660615033333f3426766664e33a66d66668647569415654564d4a133776b9ea5f23b7c37d234186394cb32c6694dba250949cb16a423cdaa71210e55c3d1c599db8f49392e4a495bb824215d12c98f388b6bf123f83b6d8357c8996840356d630821b466f7261ad0ec74da7e9c7073da82d4544a5f5e208c65bd9cd3729898171a738a898989d161b2882a7bfbf55844957f7d2ceeb0d8d0b0a92b7c326c61a4cad0bbeb51f151b6ad85142cf5632b104b972c15be1652aea8504b857cd2ac1a54d3a2b17eabccb2eb6334d22968a46ab59f041cd0fe1df7ae47bb01764c248569da83216afd4853f00e0b752f2cf3a46c4ef7de6d7875a6b70fad97756dfcaf17b8b338ce3b92fc39f504ed0e3a916d2b05d5fd21eba31f09e9e86853efbbf94ec3ae3b1dd8a7bb8980f0ba2e081fc5d5b83b841d9c1a45719d2af20314d4ddddedadd085128d201be2fd1ca5bd0ec8da0f8b6cd8a96d43026a7fb6ccf36eb3f134647d7cfd10bef7478f08dca29af4cd6f4006ff874c0e62fc359aa6bd11aea653fb578775a86b3b6deefe296e9fba23f44f96b4b6ed647a7a3a9d4e262f4a6fedede26c90affd49dc8d7070572b4d4af94638baab5dedaa6657bb7190db6cc53eeb6a3a0ec86d16353d0574f9719bd52c050c61c96db6fe90eeeeeebe8981e9f42693a699b63f994c26d3a6f126612d9dc8be2ecc54b8a2421696f50484d0870d0d1976492965747f63eeee46096384bbd277678c10c2382d2bc618239c1e34e1a517bd0dd8ee0e638cd0b29cd961b37b847257eeceb676b73abcb453137ad553133aeba909b5eaa90995f5d484c67a6a4261c576008f6e60786a428d7485de873a3509e3d484723d3599d6ae84d02184106e53c4dd6708210a15c3ce652622deddddf56d082185d04444f923338410dbc10ddd0b21dc192f58f6bc306d43c1c49665f99c13c6ab2f5e798c715e168661bcb0535695d0aa5f0edb940cf59c6f52326e87515a3e0750e103a0c2e7183dba38ec0ea31c40f5ee030006bdf8b5e90619b7c3189d80515ab3afd4d3986158b62b108a8e6199b6994c9b966151a29c47b0df2a916437830c46141072b4809d22170cd0034a0f8815d92d80cf18ad091f32c4ba14f6ff82e7df4cf3972d6221c7a6aba81e141f1d691b8c18758246c8431b8450f552bb29f79847a1cc8dd50b9b2db3027959539beb36448e3d945ed0af1ff5c03529f662da5ee2126b0596f53e358312836aa0066aa0b5196275aeef56729dcb93a29fe827b5bf95a8fdb5926d4e8dd28ce3b0ff54aa8b8a7248879e6328c790cb0cc90cf9ccd0cc90e48674480d2c4e3f6ab381eb901c18d98a5cfd329bf482c2fa35f4962706062533c3ccd6cb88c3e5d85eb01e7a9f67bd6f2f58ef1f63713aa0d0b143e3f51103b383c687f5571cb5975850621dabc34735a80d062228543d3ae2233ee2a318189a9cba3675bbba3a5ed64717f5100ff1a0f005ab407c1a488d152554364b3d3e50420368ecf722b7e99f93528efb7840d840ac4d3f11568e304a01111940b06a790db438fda62628bfb43aac9e4c24943054c2a609e9e0c2b91d6a18b73a5b2f719dcdd3382ef396bcb1a9fe4f5bf552a7fa03bc3d29a2848204aaf762f3eaae375e7653567c775ecc04a39c0e7f6614107294811a90e00c3218a0478f05ecaca05245937b0e7de078a2f40ce92d40cc66f3366692de8fa9713b38bba4d4de0514e3f5c19b69da130728004e0048a8fb399887d5e9269640a20821503da8b6d25634b75c3adc1e954efeb19264124c8a499ec43f9b4953a56cbee374d017fef96175b6c7134f6ab312ab8487e0299372fff1142059ed256f9cb4c532ecc7ea1c44197028374884a85801839fca54883e1e82d26d851426b59f8384503f4dc78a535e6cb14f4e25dff297d929239dea1ff20dd860b0c00d0c22a810562243969108f9f102f5f7a7d12302238aca2fe8c4810b31c83083100ad49081213a330ff34203a8df60094850ffbe38d4db50a4ceb72e8bd6cbfb8ad4e911b12c9969b353f22dfeac270b908c80220d2b4eb478920495681a927e80040a78d89244139884cb4048130c80b1841115416812842b9848c186111b4cc470620d248400f1c30f49c8c0082455f400a280034656506182a48a173e1cb15c8424c087329a0042182be0d2c592951453b4e0063e98a20d1228a1a14f20e488244c1c218927547c88021a21640fe06408255c840832011727a0810a254a48280902154a92d0e08382259450e1811247803184225640858a234164e1228d2513085931983882951ec0a0c8054840228c28811069bc200b29b49c20484808beb8a2882094a44811c7103db898f1060cc4f8228917c012d0e030f021e288113801c3065a6c300409d68c234b5c2cfd18610b25480410051180a08549105b8821040deb861855780843491a4baa04e1038510823072620848984802e2075ab43cf94113406d4020217ea58ba0000c88a83082941f24e9e2071a14a0e58a22aaf841891668f8e0248c2cc61072038b10b0e841488e13252e58a208662019e3080c80e08511526c4982063be04188290926ac6cd10650145b4670811ed810838d2e9ab0d205dc81060ba4b1c6135ff8c0881fa8c01fa22e461881134320e9e2893884c098e10334803801104578720447a2a1afac68fcb3eee0185f79b71e6224b2f93dbdf90be713b11ec95f907de0e7e767874ae4890e6bb46d3f80c4cf3edbb2bf280a4533ce7ecba2f6946a262de63f1fa26ad2b22ccb36ea43547703c271d40bbec9a499506f4299502614b05098e798cff43d3e44557b98eeeb59a393e6c137bde94fdde743544d2693c98442994c7183db8f6c7e6f2f5c3fbd0ff3bef904f5bde9b0465920c102091643ff036a5d98c090de137ef893e910c2223103274b78320e628ae5c1cf1b423c996bddb11449d418e2c9a0d65d6a8b2d929278b2a875c72d40c114209e4c5291258927b364f00489279b5a775b032a4b4a3cd97584058987a734e1c50f0f0f366ef013c493695a7738f070838b1308f1e8908038cc38ea8e9f8881258927339d4c8b832f7820e2d181b977c0e84bc72d4effd545ab0747538cb24081511ed103a46959db46510fb8f8a08d6869584783802e2650e2b9bc09e795ede27095a507c4610996887870c05e07df020a1b3c51e0fbf064578775bbfd981f75a8564733c375611de6d1641e0eeb36d28767c76517df068e238e388e703d80abd2860d70557adf90ca9fe940c44696656da16ce4793d5c85cfb19bdaac38b8b04cdbb80cdb8d5e934dd08a69f909422b862855c2d1188d9829777636a209307c406e87bb351cb7cd284a77e510b2369d53cf27c0ddb9d07b77f952c521329afdadad4d33e807b50dc2fc24cae80e61949635e71251c66b892ccaa8699b69fb01a38c27ea5c92eacbf6831a71ff1d97f23dca0823e5bbbbbbbb7b8cd11d85ca914346ebfe1d87595125d21efb48ded4c8dca736f36c33582f3d1d98477a401a8ff5409007ce00792ccf071e4e3cf29ba781f8349093f43eb60ab28c8fae1d3fcb651f8ebafbbc6575a67553539deaa8b6c42955f68bafd18eb4ed7074c3772eb0692cda2923b79157d4cfd7a852ebdca8bd6f4a47a2eed15897e7be05cb3c47ea94fc1bb8d6b441e75faba3fa3ae9c9cf38e95272d771596ee4479d7224b791cf21a9dce1d4326b33de7eccedd4e579964ec95ff8e27ffde746973b12b770bd7c07c2fa08c23a19ab737a799aa926d4f99f09f573246b6de6639d6f2a85f9a66efed6cdf7aece6e4a78796f3ebb15ba508f5b0c40be5e97273d22716b92ed0973fef535de151a80427a75d0a7f267d923de7ed04c3a9b799957a8bb454f8bff71ce7da7187a962ba46cb12744b80ba1e57dbb5d5bf1ad8825763206be5eba862d6281851c1b165a25af5b1dae62bf7fdc3b6807a7606d3c28aa7f7b1f074d51fdd963a11a36be1eadca70e48fc215fa7d0bc9295da9bf05bef771e6284986e0a46e1535a0545ab78a1a4c5eb2f89d05b8003fa5eab112ae842b6f4cffad50a1013ad8a9f8a96eb34eeba096cd2abbef84785ddfd74551f583db298b467abb463bc8e0132d84736a1aa52814c7d5d460dd58ef1fc91ec3e6c4aecf300cc32e2fcace8d0efd33ef80c04e6d3d7ab45f17e4384a23a573660e5f5ee23fc7498ea3b4b720fd832bffc741b0ca3a3a05010f622d841042ee69f2f5b8c5e11c08dc8240198ef4d0c3dc82c4da417871f883481396b905497d2f7535de80f4e753e9acdd11b8de21b005b1b8a769ce2dc855fb3153ff4e7b7b971240b881b833936c4ff8a651f5cf948e3ac51c7fa7a7b84e521759b1626474c44895bd29de53b20d0d7a94051a2d8e3bacb0973ad549f58b493fd19b425f2b55579a464e036d42e6f8cdb8fbd1db6e7e3d1a0d4df4b679083bbdd33b3dd9602a4545281723d088a6a547bb01c36124dee0a75bf5964fbf3c73cd373779cd4d12ca35723a3d549af7a532effb79d32b203e72c3477ae5365faab9fe9fd863588f8afd54d82b20d6a67b4ca18222750411537ba6827e6cd4937246ee71b3d2d6170e58a866a7fe4ef592fe66357557e765b33b6108ca157ea7da85cdd3bc3dc23c2ee297f4d88345d5bb1b6095b280e9dfd3b9d62932856a118f6e117b42a4542a134a370935a035563e6eadad2facc88208566441844ca36e23ff8240e651b7e9a30da4a4f7a5eaf452bdc2e2cb6b9b677a1c68b68ff9e54169da9bbc1972d06c312f01fa315e0e5166403dcce710f3f42500f3322fe3e5807a985f9e1c9e0f8fe66da7ae98a72f04e5c1fc6e37cc80fa180f87f5d66964383d4d0496e7442301e679798e89f1a2d0c040247b6e794eb353dac34e693f517f42a7e44b47bb1adaa9fdad6114394459568484b32be8062ca22ae897196546eba3c622caa9fc1691aab25742d110e88742094515859fda99914594199550944384d3241455f959e6610f49d0506bb696eb4542cc0efbb30afdaca2a28f24549effc5ec10634d656f0711e703fd3a24a2da21d5b68a16a78b8aea0ea2da6f7953c3118a6f64b3571d8858ec7657594d8d3f75ec6a3e751dd3c7e7d6c7f571b5ab6353f2bba3914f5ffbaba36908d0d79e467a12a0dee9b73f79df1b10afd973eba3e31667ae0df53aac5e3c3bd565647bc2e715d57e5315ba81aca8ee9baad0f9d6cf494215da9fcd4e5567fcb2a2c742bf999f3ae7eff8667e80a8ea2ecef456e8421b7a9a57f403637352ca71524a2271c220fe2e6ccdb2ae571563c056f95f8f6aae7b1699940f218c0f25b718d9ecafc7874a85d8f33376b9e60e5fa08181c85a709df7e20e8292656957e6990cc228864dba3d3637aa8566270ec32e6cce180f9b260cbb4c461a5719c3b08c2b01eda10eea24319fa452a53ea69bd18b8f03443dcac321c687878862a1eb36fde3360d610e51e2e780fa98b760fee5ade7186f3d7a33c078ace4366dcd77c16d3a8b83f6924ed2a9140f34ebdf71ece32518a553634c4e314e38b909a184ded7d353e34b965f0fca22c24f1738d660d27d2c54668bd9b22ccbdac5014195bf559a208bb0fb8818e91a7f43f50589c0f7d8f1f4a0cd0ef4a31caaa657f1afddda5d407f865008cad579069902605a9096f5d49a939e60eabe331cebdefc62dded07dc3ee2bebbbbebbeebeebebbbb50b0771f27b793fb3fada2a7695996e57dfe2678fef5f87acbee28608c31c61d87be11c208b94f10638c31c228658c321ae90a1907bbbcbb93fdca843ce82ed1619cd082bfd1dd6165235b239f56a3437777778f38cc1e6ddfddb5bade164f365f814564c5ddb2dcdd1d721c990e441f1bb1d1044e4c9c9c9c864e43a7a1d3109448e9290514456914fa510d0a3d39a10068666666f61ac2ee7e6fc80c2184104208e1f880963d9aa1c70a2a4835666666786a41134db4a00902687035ef144e2d80210e5ededde55dde5ddee55ddedde5dddddddddde5dddd75dedde5dde5ddee6ee7dde50ea6bbbbbbbb97777977bb973b14773218972c3b31337b4b64d98932378cf0c402fad10a4fa7937682b0c722aa10fec909d5e80b84ef317a8f738c0e4ed9699fa0683b8cd29a4bd01f4160c5b25d6d777d77f74d272e49dc7af612b3eecfcccccc3da9ca71a9bb98de47eda336470ecec11c833ae590c9212323838a81919179a13232322719d3262323c3437323a3ccbbbbcc5d8cdaa44756c7219d9cd0130be8e7461857d709d5769999ffe4049ee052faf2b27099999919c2dd5e6f724c9685ee4b787b77bd440d24468a2aff62d9cebdedd416d9ae8f3e80648efc51c697525a9617bd49dc76fecf1dbe3ba451a331b22bf175f7ce3b0b08219cf3087a84705acfb1658430423884eb7a0b7e8414fa67fdfeb5c120fffab9bd505f700f5adb5a2b74a16b117123193b2f468eccf303b399888dead5cf777ef85da815df627e668bd9832e57c2e8459101e1654298aad0adf02792e81181449a83d07ab5b4535aa7acd59a356d6a9dd2fe8a103a84fe9de2b73a6a592f3d78059e93a8340eb814356468480000020a0315000020140a8744229138241c14a6513e14000e7492407e589b4a434910e4300a828c4186186400000018620cc94c4105008ad182d181edee2e2dd3951f3bf4454cc271643f3c40972cd965ea66b66760fcbc988141e1b781f113e2c03cac2c4921ce1ba18de22e60c78801539405e2b57d6b16230c9d9b65743ca926824d1adaecc05c5c3b984b26bf4e8017efbc865b3a42f5ce5cdf3ddb4260e34cbd638b4679d91ef77a42473a652aff32d44b1f952fadc455ee896a6a9f5fbbb6504bcb58374c8e4de4a1b4bc08f825b51f4056cec801d86f316c7effdc06cbcc03199d28a3cbc865a5450d11bc3742ff4a88521528b710b05c90281f3ea2941179d2c8421f7c070547a0da5a0dc11ab52a08a1ed118c01f5f23a16d763ed0ff460191d0d59b84897f5530a5022bd7290e4819f5a9ea18288463d31b813b62e94d84a4a7727cd449b94dd5f8a49003afb565f0cf83bc49d90abf94b2fe19bc11bda17bb636c88b80f70add6ed67a875dbde373ef42c9d419ae635b4b42dc963756b40864e38fdf4885b94adb326d2fe4aee04139c2a9a64e98472d292e381200d96c1c8359ff68a0e2b3da6da1c47dca39471b677061b122e7b06666cfaa2bf5a06801e3a47118d57e68c5da6b4a17e93348335cb3cf14488efc96d66d6e26f8eacc4217ca4398731fced0c9a564d4000dccee85bb9647833b382e3b10d2a6ce789c5a6b59abc478e482da6a963f877ed654398b0c57c9352e401093a3858fb35febdef3fa1364bd6458ca569e7342183b237e2904f263a0928fc1639bb4ac2a60726fa2e8dbd9dd2fe07d520218cc39d270f44886d02000804c3a306cd1a9603de69709af9c97b873babb868303c0afec4792975cc93fe91fbccb08ca8b5d837e4961d90613d92f2e260e840e489e956f0aef035c2a877306b631bdf2d8942ddf4cf14e63446f44832519108ba15574555738c36057be2155d604e7dc4390f4b034557f9b2509446037f1f4da53c6beec3a6d064ebf142bf44677b442048d77a7d0c280819271091e699781ec65c19d68436aad2bfc2852a0a5558ad78781a9953cc8a03c4aef758d5f4ac50e0e5aa232f4ab53c789ab2174aac53b1ab360452ddc00be5678de7965e3af453731bf871b21e602cfcf3eb790e41b87b87f632fe478e2d4b2af66fc63ea6472fe2da9eb6ba328b36a44f837bd8ba6911a9d6698672ddb8b9b0c4b1df8fd9deaa132081a6077f3d57387a03b0905c70c45150f9ccef30fe93becfc241f9ae279544ecaf467051846614e9ae51c90af5c46079fc5d08e39af54ae5f7c608aef7a7ccbf0f422246ed44bf17382bc984919fc9c21cb4ffe130d5c3c3938df47a3c6b13822061c1ce4e1e2add91719d587315301a7ce879d1daa8c5642871d1e2d2e17ed70acf62eab3543fc659950d697b4d3fb79e817848e28db61037e32e2dda1df5047421d56a0393fac786d4921e3af3e272016c991f922832b9587bbc3d790201b06962ecbbbce08b1c89155ecb2a9ff94c2159ef6925b914cd49d87a6a6892004c85c0cd2c7e054111a16133fa5b005b42bf811a314701d4b2f9d5116e68bf74ab9e788955e42370d1f2e8830092f75bb83c60e56c18f80f5e6e978526387ab96330cf53021f01570a4bc83967cf42998049eb7667281e1f5f04c6603b4eb04545bd6e8162791d9f13a0d87b19c2fba25b54b0946b55b4fca6581b0eccca530a7ae7c2a504514e4185b721ca2d7730ff087b1f9c9968c93c7e6f6e5627a0e1eb62607632eda69bfb14a4232a8a1cc29d753194adab8b8592c0a7c21d2504bd3bcb47c579adf1350d66a80a4faf84664e73c918e42b84a6ff50d01e50916acd3cd2c687d8f7e025b3c44d8defcfe667836e43768c564cb2cf6f19ac07aea6bb146895bac9937047f2620e516c66ed3b57ee903944e746f04cb8248c29fd98cc6a7ad2954cfbb3da2ff632a57850c807f3a24f19981cb0b8bb394fd5a3fa007c20886c8f966c57a50845dbe8e82257b704db32e00fd6bfe6ffee49755b472042c6dd1b9a0208996e78b556e8852b29763f13df539d6c3cb2025568bef09d958b3182aa0eaa2d94bc7bcbdd6def42d57b7ac4a1aa71dc012f799ad1e9c4e10a957b52f6593a5d09e4c430d32a76c42c9d89f21909ea47ac69b5c1259d7e6639c0831f5e11115a8623f81044be3d9fc895b998792a3d68e7d18d6ab6d14f022804a0cce90c07cc3c8687d184ae1e450df34b36df279f6ced6d20e8b7c10f13081963e02e643a19ee53730025018fc14101865057df44eb9d9fb6b00005fde24869bb31a61cd6bafee89919ad5ef6d65d6af1daaecc85a440a6145fd344dd2bb24fa6e60a9dc0e2f06c40a384d68264c6434be977f3644ad5b0efcb373a93f4a4fd5de22747cc3e06dc87515735c55bfce1d1d2ec6376f01abea914235bd250437dac42465399d42e68e4643a171388b83c56d0b308b09b3f8b2b555ca12b3a25671ff725677068d66d9d2c65ffc71ef5d0939b4f6beb87479c3acc047859f425897206989b907881729b3d011491a1991523b96f4cf322e1a43ba3394e4ee7ee54cfa523f20c9b1d61d8dd787eace2e974a1850b76486084ea60f1471e89da360f1823254f33624627762807a2be605695c5013a06572b94cf02193e25020d9c17a7a916b8b6755a661908829d4389a5f7e4aa8d63c6ed3a457de8331306ba749e899c130235e1025adb5a1e12f598aa0b0946842836d7c0609d96ebf308b6be3378335497cbfc4658e34151f1bb42219de5ef413f29d71b5ecc385ac5ce267af787eeef056dd3d8c3dae933f9f83986f6588a911aae490b645a2e64602e8708487e0828c7117505d80ff00e960fee27acbda12a241c437310e33ce88b0c681b6cdd460cb5933941a57da008580ff53b1e28ccee5dc38186c0f07028a9601310658fd5f9f87f1ed271d5d14764a3da3acbd296a2648d43df24f2b5bc20362e364324d329ce8893fefab210a698b35aefc77bc015349618578f8623bb95bb63d11be4c1a01c4d80f517546d5c523715ae0ae94f176ef2f4747a0a78ff244fa2f6bbce0c322ef0d806641b441b73f40f00859cfeb986daaad4215c09b4cec45110bf1e035e956209462c5e6392fc65af0d82ca0fb65d2b8cabddb2c6837cbb51e6299f42cab1239b1a0dabc46deed8eee90d2a5481c7006ba80732a2ca245b7ba3c93dfc826ddc3353100e4e6e238721d5c65e947902075778b78fdad8f37e4587af97306f107a2a2567a045aa3e7bc32656b04d37e93f081953c485d738fc2a38d96d64a95ba04a5b6ac3dbbf0a57848237374787e7abceced1ecee958b1457b8fdc1c66905c232d4670565108b96616086d948f2c70cdf7e8a56f5fe9275b077290bd64e653591f822cbefc7f733cc630ed637579a5a79494f97249bb99ac55134a4cc1ec378a2237b9b4ddfc0851c361827c0c8c563aea85cc3140a6bdae02f7d0e7538127b4720488ca3cb5ad21bf92791c9c074c4096dcafefa4e205d29a1644e98f89f2b8216e19ffa4cf2de71bbdcc69100ee38ebfd406143a4d12c00ec7a9cb40329cca9dd0cef9ba1afa9d83c68ea458e32c0dcfce6cd30ff49ccf6864adfa06868313d4f328a06d6544b4aa617e13464bc4ca0f9e0c1183c7373df31026552fb9a64dc9fd4990750e0e3da49e75835c55337e0a56444c6a77f7fddd3256a92498b14416550cd10a83c6ee169b97fc8f02354986280957434d079369ec04810b99be4a1323421c9dee16dc89a7b202a93343c023dcbfe438fa9862dc5485d4ff096a6bd5a3705afc806b9a704ec7136620282165598a355a050252985bfec1c107644fa247cfe5fe54043f3c06777666de446147ce1990b178f85c2260c564d6528a12cbe0906ac2217fb3876a547e9de672a0015ed970f210df2b3fdef192aa3e5082793982353862bc98531dbc9e5000c56a0f3bc21afb1a2aa04288904b7c055f8a3f38388e1ea4d57eff53e60ec60990d0a80ae5a79ccb5c971274334c2c43958994eb1bc505a2e558ea2957e006904124ec5abd3fa699978ccf1fab77e0e12fb48a4abaee46c58f570badbed7b5253341b3bc3ad8fc28489910cd06e913bcf2130d3418858612756b06053bad323946cced513642476512be3b79ac675c5f2fa894de9502b881ff85d0968a133013893436a34a37412f7ccdfd6b57d186229878af94b78537d8aab4ba1ea61c19ce850f3cfc4a378844ebeb0d44113470469a4cb4855f13617e0dff9a97c017ec6b0fc5f2d8ea67caa3142ef5745a85427bf53525e12e1d0e4c9d2ef1884f7414b15fcbdea7af906a706fde4a14c074191dbb4363fa5ea6d4903cb882485aeceba4ac0b468fea67fcd66b34d5e89a075c8f64df5142641dff3cebe2e4084c3f2fc9eb94b89f22075cbbc5787acebe34043f51b10cc2eb84dee01c0bddc1d5590cb45db47e5083e02fb62fe8226d97dbeea9d4cc7219cddde2876dac4de1f93f151d2f262b6898c0ec494fb60914fa7094823f35581488d5fd2f127682c777306342167aca46cb2ee96b9d35a03510ae43334887f8585187127dcd7050a015c3508acae0808dde29654afde864afa1e2700f7dfa7a0003e2ff0d969909739bb5b1466225be1ae0f3e9509f9c09a4d1ef081c86e7272faf13d023628f069b3040918773a38c9612f29216dec0b3ec95368ec2c8de5630bd71bca7d8b81eee32637d280ccd26e9def9645316ec8243928067e08495f4ee0833b06d97400461dedd5e189ba35ef906f44502982c3d4aabdacab63d1faf5de24c72f68ca9c9f7b4916decb2306e78571144e736a0cf3fd247775212310d51461e1213c8b0fda06b1f3162c4037b9237b1b2affa3e9d75dd384636584a1892ac9635dca371bab13491e8e4bb063194338494271241b0cef24c1df3eca04fa1582cccf8c2e1289958e78840a5d1701bc205db47888178984b152781053e83913645d6f66c4706ec398d655f366d117a5ec3a1ed745397ea1cfb531f093101e13c19cbece5aa16ef66343c1196844a78a389394b98b34df0fb852c4dd1339e605f0327e5ac7bd31d14d132230be76b44a9c4fc439880bfe95ae8c73ea251c62f01be6c5c23bd7529bf7e19b99984f796f8c104abe06d5983cb02d81ee865863198f6923e14400835b76170b1a798b86abef687e466f59bf4d405d1393b9590e8e3fcd823cbe0f163c11cd9440e02e68fa283f839253a7fc3b1c4152fd5669e0ba9921693c1636d48414268ce49105176a25442310ee47b94c8eb6082101cfe0ae5e5505a0edb62da237019ac1730af0c0b08f832b6ff516c6b781c36970b11f6f0f87069b481d31b7591c43cf653d8f5ebe5c0717d58902b2651d9292da7c9cdf625179604a3eb095598ff6d5e5c452d96f5bab8b4ded9fe8a26be766bd59a48455750d8e5d869d86e484ce283a9af54c5e786aea81093b7738d85e2f31bf7c1bf8e120980644f147d299b7cc44663da5b7589338c747f6f14ba736b08b96cc7a0417f8f86031d958060cb15f25d1c76f9bcc7a0d49d0885e3d2ca46422d4490cb2337569dab28c14412db02f0225fa8524fd611bfd35cbd350125580d8191899375f4767750b17403b8e591ea4d471ddb99291c1a0808564a4d495027ff435fd4ec91c14bf4c594e2875383201c6c9e0ea93c1a4fa8432554499da28dfb53ee50aa2a9453c71e6b3206c267ee9f9c1795c808611062bed8bd54e2f4b36acf1fc90f58199c8399fb5a18c33e1bb82c6ad194099ba990aa833e10d9f48e9604baa77ef903179d27d2cad3b73eab6a88e04c80d4b66feb8a242176e49e20cefb6c36350f438860f4c6647f43532009a16661b152b4dc20f54d644cb5a56b09e51d41844703165437884ccb1ac253eca6d3699726e63184f065673a5e2a2768494ac9a503d0984466f4feacd5ce0dd3396677ce03e88b16d41c0299a10c1505a2b791afcb2dab1b0c881b8b12138e587a275b25d5e3eaf4407b57d53f2696b2ba72154372feeb696d87b88dd9a554e2d45b6a0a8cd9263115dbefaa3244e0405a9abd68b7bd2aeecb04a0e1ef29b3ece76067de36082c784b518e72088b27e6bd599b0d23d02d71e1f6c0d1bd8f9becd8fa1b307467c043f08df085ca173b276c34d9a6f793cb814b2bc911789275a39011b917373b9e37ed3772e10516a8a722010710f51a7caf368d02664cb77bfe815adc9f83ba989a5c10c893a158b65663c26fe6ace34158b06d0efe391906fc283a7064f06bfebe5ae7e65181051620b02b2bff646068a016a0255407da004c89e9a5714d1ce0942c655132fad6b6058af31caa592dedb2f5ed53083ed68846b0a0a2973027ed3fd5d1955c24e7a03cca92dbe66f1b42c24c97b1b8e5a31bd92df3f5857671a8a7302435d456c29e8d23be26ab450c7a54e05982353406efabadc3891600bdfd5a40520ae66c4793c51b463038d1a20682d0fcc219df1a9a51f31ddd983e7f3670398137634a88039573f93caef8b3ba05b8d405b8e62d4da6c7cab57d426cc6b797df62bd1f003451e7bde4fb6b25b2dc3110f64a245c678a643f46b37895dc6c7b3a04007ae1357ac65e8444558ed73cd94f804aebf3cd5ece330813882f0a0136daf34549b94a336b2f0011fb4b735a82032382674c3d8632b447668c771279fe811636ed22193eb086b7c4a4d15e5872cb227e0b1352a9e088f0e5f3079952dc55cbf364cf58f57b79314c1a8b783e821938b64fddf0ac5c431d011d79715f8e58fea817bf6321b3a786b422e141dd1380b9550a5e63706518274641dd8a8fc40162260810926b4be265953467a4188fcbb85bc39e460cd4c8ccca607b3c1648217f287a3719c92eaafbf8b7cbfce126246f8f3535e8fe078ac99d590a6fe3a81221ae66943622c69c4c2cb2dad7dc936a405b71a963df02166c960567f7a0e426bb0c4571c681bfdd6d454bfa75153f8ad0aa523b1ae86603476dcb1e8983cced6715d059893170edaa0a99142c186266f32b7306b9701e381c6224615249ff23192e0273ba6d1645c387c5063c32a238913ea1ee0d048208641d69b902d0dc64a122f5630c8e0d6bc3d5f6a30fa28cc82f1d60fdb2889c1dfb696246937049e23a9eae52ad10a4c66a9b5f5cad7a05a1ed8135c5bda9af056d1c2f1cc900d04e21c7f3b8b62d4a4d56798c558663a2a3f469cd14cdbb667d33b0ece602a13edf10e045f5c8f8095cc9a3e5a849903b4c1e6c874c5496e7cbde8ac1cc138b76f78e9d0b5a0f28c5100e36b94006fa8a664d9324640ad1da77b8816eed475f2ab7196af676fa9f12a64980485c5957b943259feb53e41a429b44b88877f72ab4e77c4521f8adb3109fb012a3c6371b47cce50db6907920ea5e5c3fb189fb768dc5fadf3847c65f7e6fbc463573909971250547ec6d016f2889b6bba47ee1d96c9baa8e575f53a3c5f4d9b99d424fde5a257325858893873920cf732792dd4e926064ee467c6f509ba666a573cdd4acf720fd0eb99ea6be9bce9e2e1c965a2e05c0e5a53fcfdd7110ced3b1284882eefb30879ae0f346762a8f07dd428473ee065460fa5a074277a7b496f57501090d35a02ffb336a0dab849faa26f2d5fbc983cb3ca57015dbd26d4d1db2568f424f287e6e39efb878342f28674fb7e3406613f0b257fcb7f86a96e05ab6ab5ec936339d99fbf604045063f437a0f46dfd54d14b38bf51a789bab4bedef1b185ded9bdfe1377c1696f3ce71a21a13bb6c3110d38a159fafaf2d1def2a8ba050caef9011a4ad75c5cd8e6ee6cd907784ae6618e19a2b8e3d2ae64ce806517a1355738688eada081691e9e5902791a63bb37a14444db2909d5e852d23dbc6526e6ca2fd2eff651b131562d45eb878dd6078ac8b62cd9db95dc7e082d1376003d5ccb68b5ea29b6c5bcd74f916db2d9f072c65a2e438f91553de895ff6266ef7a34235442fca257c5b7fd1c68a759a968ff95c54438faf558f893e77ae9ef325cc6e21339d479e6e0668a829a9ca60704e9ff7bed2b9d61f1a2cf2a6cecbcb530e379ed840628e185f6c12f17debadab6d19c8d510fb41bf7dc4ad54c35e8ca9ba80345c27eeef8a83e3498beb738d09583871858dd099f7ef32428e4d78f4b65025e3da4d36ab717d5f623fd09bf2235d6875bf7cf49df7f7d814d36da7edbdcfc1da5a27311fa2524bc6ca4316542db07a5f028c1356c1471bd44995d68ea57433795f5a3dbaa4556bf3296bfbdc5441ae3288934cfba4065d2a6d8e6cfebe07f1a3b0f042ea5ef4d690267d489352835efa69eda77749933bf4812b652082f308eb004dd307b3f5f84152a47880eacb479a859a34ad3d9a76eb4563d507d68ddfa4a917d9ae34d236aa47b74eb3a69af8fe8888b2a7d1fa1bd8464d60ddf84d346eadbb854d93a7d3884e785aecbb5667a01b026805ffcd9582a5fa463157a77e337c02ef6b5d4dc0eb985555bde621a987a1e88e30d156fe619b2e7efcf79a0544bfd24a97318897af11d0d541d04ee362420f451a88fd93e1d3c6fd1fc2a918d1b88dfefa3930762f1efe7bd0dcce067d4fa6ee079803b38e864825a0e7ed7d46721abbebff742c31c63867d06319947cb84e81988c1f155d0168ed264cbda11d1d595692f1e83e3c83aeb8c203a7cea0d716aed1322c828e7db8d0e06e16c1e8b1c7386aef8aa30d266639601eb1ee7e4683abe91c401e94b51cde885278cb190d4a0fd2b78ab14f3c45b92ebf633ddee6ac4e92067d72cce9634b7ca07a4721283fb39059ad70aedf1f7dda2269d0e68a93768ffeffe7bd72446a3e0d5eeb336cced95b22e63ce78c88abb4673bc19cc6ac286a70f637ee1b1792ce74363f712a3fa5e7d6b17b3dcf524cd147e21d5183a7ab0694b4d12e2681d09f6cb89fd8bd96c19443f06cd64f103b843818dc8878b035eb0a0697202633f1f97ae5ee123586ce8b6591a4469b457aa9849812b348660f985bfbeb3ead2fba2c99dba9301a7b483ba00ba7c4fea84e244ef4e1bc01e03e15bc9552d3e891b2a5d8033a240f6d43417f1a61c9feceb39efb10f23030ed4848e372e92d1b19b04cae3df78840959fa2f5a8f6db77d6238b28d4c85fa843e381b1e19512aa1025cfc073b433c9f5b4d597ae7d00a2651bdccc2238847a29b5c4588e3bbf4b4be0a261f0137e3e2d1912162e80789fa969b856b43623f98d9bae13f0045e66e9cdbeb5693e4108c52dd21fdda6b032a39c46173f9f55d4076d4a0ebba2d70b1fed21bd37de07d223daf7a6738e6dac09ccda400ce04caae2e454bc37355fc4f2a98129ce418289c1dba4556eaf9445d8cb16d602f2e6fb4f174eaa2a0162af9955607521a95e557bbcd98d4804f7787dd5979714b132132934a42ffb6fa0dc8e30ab14441a4cd43e64a8ee7aec34e4a996488769301a7e24482690fa5aee429aac08c9d5c844a14b6ea1c5fed789bf8dd947e58c7c040991a3f16252ba0cd80ffd1cc4377c59ae544ca3e4f090e96621a01be4b41cf608490ccf39ae33ffad6d81b316a3e00cfce7604c41ae166da6f95cb56bec1b457ea79653508eb25960241d003f4fa058e6ff9444438ad100ae1b0a0ad332d0f591824d00881ff19049b35a8389cfe3212d8f3f93ea38c60879580188efe8513b00cbef33c79597fee92cd08ede6ec75172ac1a5fa22b0cf8b23b448aae5527ec3523e9475c3ba0264bf619065366130c7b9d5660d7271146cd600dca426453a2560a6ac80a132794fac1f1d3bcccb695dccf5308b3af7e817a3c312a9cfb29db03d8dc125b3b3ce1bced9eaf461fe5108adc3c4d6e3b5a11bec6c39a1bf9378f18c0d5cf9e41d3da0808094d92868270de04fedd8a3672d90377a7e79477753b082e7f37d22b0043aa09e03b991d4ed5dd3138dc240642b07268c00bbc75074064db595f3c58c2420c26e9f46edaec6e928d459d5f076d33a7d0101ec115300f39d058365d7e9aecf3a7c5c93e2dd31d21bfa1eda57016af26e7701df302509563447cb5151e116fe528b5f95d4b7cc87eb0847321fb40d03ad1ddd5c4ab904125229e1580e4bac44505800037676df85957f0518724c8b0a60d4d717d241378ecb0bec0b52d97d13ac2d4d186e9924e0272e44570df3cefced5f430e5e3f4bdc77ff450e638810816ef26b400aa17bf6db1893862f040cfabca950d2c958385956a4eb893844f2d82fddaf97fbdf623a3d90874e2363090c1546071f1daf971fcc7ab8ea9f42dea4db44a14800f8ee6d6f5c083d2878c8abfb4675ab450b7419370d10190e75912ac44d095465d692abcb5f626cf7513fece33f8ba51a7b5d113c1ac804d5b847ed6c01ea9bbab1b5685048ae9cce350ead3457ae18e6f2d92d689a8f48ddddc7e581a1dc75d6578343bb3a0a8f6379e5cd63ce74088d075860b11648d278edc131ca9c1408d5dacd1a0e310387a49e923a085461e6e95c0b3f51f47dd668f4ff639bd392ead3a02a12a32624bb9593a56b34569d00f70a49484a509894d52e44767e3498053309e379c50b4493cd9d282746184479d24ed716d773d1d54c9366bbd83a037c6d2a50678da522e63b7898722f2b15f6f3aa4464b652ebae08354b41e1ba45aaf66c78624b15d89d5506d4d009cbefae30219c0f9f7d78cfe90d53558d2fc700f94204673e0078712fa1dd65babcfc68ee93558a6dc896fe824eb99fa60d7b6b39d6b3017ef26c34f4984b34244d1349867d0d00d4f6029181342e19e64dc97027628f0be5fc1e425e69280a3fd06af328285b806d0fa054eabfb120184bf4df457c8ff87b77e3a8702f20c7801e3444fdc28db40d8366f54862141193add828a752ee29177107bc302fb8dcfd90d54ec36a0c75f6acbc4df56a306f51cdcd971becbdb69bd1efcb065648ebfe033dac5e1939c1ea25b3fc7bebdfed9fb7d1170e9b4d68c6c9325413690f20e9b93c9971c80ec59fe2004b5de19890e5ea59682cfbf5346f80f3340833d7754c672bf45dfed19f0dd4adbcd982aa33fa8103f4f0a5f4f74b9d3e619ccedd61dc75be68e81040cbbbd452d0284717c08579e8969e1655197282f400c7a5fecb20a0242353d88bc420d80992cfa8f9c127f4b5d20f806274f66931270eca82b2af51b57c71d155776d0c0fdee0a5cd266b675e30d0d6e4ec3ca5927499e68db4a4ad6614e032d483f4dbfee532befdc41f3a1bd4643347990e8dd2b4a16ee03a3b69ef1a47310b3e8429084cff33058c7aaf930e5985a721bc8af69b4e7cabace4e0a8f54f9e94193471f35e294a7b8c7dbe32ae74055a09a7304d9596bda4a02563f7852b10c540361b1f69a9ab45f62e7bbc834502d501e142e63ab8cd4d7d7a8f641b515092ddc5133b2a4eb5489f7ec79e3131d9552afc8c872a82fe915662de0c55cbe41c9df8bbc6838a3d7898949f1c507dda57a056686f3460ff265267b8e4f13b1606f66f6876e47cd4987fd695fd868ef7a28ac0eeaac1fb5cd1f7ab62817763ddab9680a9ca8eba04a71fde2d43a1d2f137b61cb5a82e8e0e3ce6b12c0440378ec9fc72a9a9c831e76ec8428230099149b61f9305b636db67a759c3b08566ac55c1a6c6913c015c963384925351c92f9ec97045e7f6c892a4bc15b2dd692a512d82e377c2ab594366f1728ec51c8a35cde9adc713b397c0ca3a76f70303162231f5d5faa482e25d1b8479c0ef9b07851dabf5e6d0c5a86b1df891c149a32926a12077f9efc0d4ddf2974ea77433d5562a28f78c8b422e62b7b49c841f036c570903077a404c3a3e70ca75530952255fca0af5ddfc35e1045cddab901bbd22647a8095e0ad63365b71a756bbbff11c6e1ee92137fa5456b0512e23eefa2d61936ce82f09cdbbae7c979067196b92baa6e6ba0b2342e9498ff19cba01f45d332552d926aa28a282b8a886186451f283aa4c8a0a20115095374838f8b8e480eb04341a92d70f9c221c82ff0041c44b0f53b0f028e51fb7cdeb029579323aa22b2557a4ec0edd05abe1a2c5cd2fa9bd763d57f89be99905171713d13b15f479a2e3d7892628a7309c69940dee6490b515e43553301dc13b3c76772cc48eef89069b85725415dd327bfdb99257196dcad0cc4b1ee341031200565d75bab200043300c343ab533d8f961e52d4cea035751fe4f2f071de894749cf89a6e7dc0c7f31475c0c918a0024b55a223389d9de66210673b89edb95c119d5c0323549294c50303ee42f368aecc6b729ea1b0a927c97c40ff7138d70388455aa0321624dd51fe19c23c006e15efb669b89f8c3887c7ad988797e09d6661b61018a1bf9c08cd8d84febad149e7e5cf8592fb9ca3d599a0ac13b278612371b0e451c1fd24f38823072e335159d65abf38a0d91d633a08201a3907ce0936f39662832ee2c0d7fff15f81c3e61fb814933f0f99216487368258829b1a9ccaadb6628a87bdd31bfd0016cf509c3bc701afc6fd858d8b5340ed4a547e81013b283ab93891a658499c1d3cdf66619afe730ee5948df8afeae8047fa48259ad5549df73439f080d6b8730e2de2c94598e351c55e4396d567bc1c8c2c1d38c245e8bd37300db2798bb2d7619f1296a46ab253ddc22829abcda146fba95800a0eb57af92b5fd8ec747e252e89de17b1959e698851c7e19fa195fbac3c8cfd8720e7ac2aed52dc613a9dc19e22bc0a172cb3a10aa900a34f61e1ff42ac907d230cd015001c5442898a59160b3c650ef5c0bb039c30a86833a0eb04c66131440919a44a1fd8c800ac25baaeebf12c8bdd29639e8ae2b09942707c69a57b2eb825ed5bc87ba302ebdce37046c900ad9dbfa44716ae6ff664792b5ebf298b419ca7ec9e855b8a46119aa7389cc4287c6209902d8739c3905a4f7138f3f1415935dc91845b2d233053a83df486b6e9a917e4a417decd2b72f2a9250cade6068c8d385d5a665526e3d3537a2d0aaf4bb1eb297a3d8ad74ad1b529bc16857cb244b4360a945c8352bbc65e2cc0874277801e99947cec315cd3ae49e0ecb293d562f2a38ae2352984d719989e018a8108d435b764bee7826df841270cd8bc2d749f85f151f700b046aa6d94e192edf9b9a5df94e223f9c879013436e7ab718223cf7a1394a0eb2f5248d0c8cc5ccc3879145dab8ad8fb4a5d63e9db52e0d750814ac5002daa489004705ed3689480c9867ea20ca9d66410f0e8ab4f7d82b1ca43cd6a4d71d3d2d33c92ed6de1e03206a02b757291674df38a37ab5750532fb1315bc4258991c37a328b2a4936f0b2f352805eaeee8abac767dfb601e30c1188b7470e0f552d68dd6edaa570153066d55b543d3d0a1639686c33c057738e8702009c7bdf9be3d22cd4eec2945dcbec8cd1b5b590d82372850089430238bcf6252d34f2610abbaf471b9ed29525ee27244424319606b2d4cdcd0d5437fde7d5fcbfd097fbd16b2048d700c0b281218c44790d0cd1d4219afc0c8cf3865f104672ba41d4a4afcb436a6fb73eb2eb8fa6f650caa0539d3213cf8ffb4a34af37338352d126898b57dd62d0de14b229b8c264f09f0800dc04422e5acffd4413b8719f544055b5b57c975bd3ad519e6eed4846ebb83fd24f5342461c0e027e48a947ab59cdd224b9c00ad4d81d442bf07efa6eaaa03edda115d11931194a4c94498aa916d3b430e6d3cc6a936870b1791bfc8227fcb4245e742971e944950db041c00d01e6260c0474ebd836ee89ed4d6f6cfd34b2f3abc754348d9dce14561905e56d9f258d1105cc3106e326e56702027b7bdc425c2d938c821409232b7a2e015812476e4d2c207670dffa238784e487a273f9b70a035b8412fdc1defad5d0b09df7cb08c64a3a0f9178eed485c2f52eae25b1e3c6ab0aaa406b10cc20d79a7ac305d3ec9a971f76e8a96e54e4d09d9a0f0a5260954e161c0fa6d975f7a523337c7cc5c7fd2e21aa1bbcb479deea0025d637252fe508fd8854de5e3ed76b08c85a96fbeebdb73e3329871812adb417e80b8d3120bf26730289eac6a50a97d3d619a1b423d3392c5781bf0754ac9d7f8855422bb4d20153a730499a01c466f67d174a622f2431a211fa2b4e3ad2e536d60dc4937ebce93a1ba2665b092502d79cef20faafa2e73211759589fe4511157989a4fe5098d0bc717c637e1e3bef49a900a6593b5163fc89efd04aedc57d9e22a48d8a2e3e1c0fdf978a3b216acf74ee23989f8e0e110d60faa913d5bbf310b3be4e09d5b4593d5b348289f0c06d654b4ccf907a119f0a175d7bc989d304669bc604e9299ccc1fb7f51292717156174a688b7b05aafcae0f66caf5233b4b0cab0809159cb68fab39ecb0cd8d3ee2bf73639fc8c2d3beb9dfc89c7691e73b64faaf53c2cc19374d31d8b61fa4f69e13d9495dae9efab5c196e6da1cddf226c70808b36258b900d5f582b0adeb294b0b265031cb16d3300d994d9c3c6b703ebcda1368dfc1696928eac8bc5359c7d870bdbc9db435527249e83d7f3b4a517da7547cf9025ccd7c6bcd4d30ccf0474428b8f787171efece3173b597d0a7cca4e6ed2a30f4c0438e4e64d8ebd9231a9c044b9a61626912cb38dae0ba3a0958047326ff2912bfddb5cb668b05af4a615cd7232abed9d44830a27dbdcba6cb006a6b65531b22921178b3c921fd425571a8d97b06c7c4f078c4ff096ddda38bf07aa45d5f89ba1c4c745f9f4781a0791dd072723264d99cc391271770f57e8cbbe53afbe870d20e6ee908ec27a799521cf188f644601abf0c354f3142fa082c1788782c64cf0da0e5d41e1c81fd4b895c31e50fe404fa2186b07d9b95dbeae32b2dec559d5497d9a9dd9752f896c296f4a41f54eeae925992b095f9fa3df0e834eea023c97c9e5f0e4db1b1e0713fb3c173e2b403393be8064b4d99abc122cb0cc283143e0d0ae2dceece0cbd8faabc95b3125b03238d20d39c55155d4a07957876e1c989431313bd5c902ceb9bf25625a1c96c9fc4c789470812a96368e705f8e5396267df3e584c83894b4fe1d05ab4cde675848c10f78eac7d25345860b029ad32570ab868e74804262ed83b2edacb6cee95c83562d373d84866c86a6ebf01e60515178c1426c991ba86b947267585363b3f4268bb4de418c979e50d6928c00774c60d449eac9c502bdd5d85cf84308a8399cf265ecd88a17342b2bee61b2039cad99a0cf9dacc18c423f5cbc14be024e079235e486ee4720b0811a79fd38207b5b48e06542b3e809442a3dc19e618e0541de1ab9a2b42e1eaf8c86a5885ca02742c5a355ac4fbbc3acca2848d22497920ca524caab274e25de10d2054b12b05931160017dd35b63d56d2b4a287af698df5e9c48b6e1c2377d9ef0c38e7ff1192a9624941db93d3ae733fc219276897c36fb04289f6162cc01389bb3fc6aeb76f5d81cd99ad476c56578ae9ee2e0197297e227bec505606f56cba9ae75cc2462dea125ffc2f13db1e86402ddcbe6ef91c92538f23a7a0d78715a8c5d879e35f812a232e38273d144e111e492046e521c145d9c6488ef582c331981d48e92c0112319ccecdd3212e77f762167e4fc1dd387ec20407267f24b26bc7c7f77b2043bf6fde4b972006b41dab6394326c746fc04727b147b525ec1206cb858f2ae94be85636d8a8320457ea012d6a613cded57e5311cc1be9aa0bfd9ecaa85245380ee8fa10347da0caa7271c0fe02b6629b35875769c1acd47e58cb80ba1221770688cea10f9d47e0b8b3eab87e80d1ca95147180208f54e8e7d33349720b994e0c97f6f51730cf7c4da61310adff11d26a2859e96ae32b1cdbd3e42a03f21aa296fa5e944c3d0a6a5db78fefd3c135efe3b40f851645e299e994231f256f98b51161da353e71b5c1fca222bebdfa7a796bd0b8c35d509b12e764d125ee3ef20ddbc36bcf2373ba9c3ce596d783c464e23cf67445217cffa4d3c87cae0a09d68f8962a1b0e79a6b7f70f3bdaec7a850aa1da51ed65a4ab81945d45de509bc045803586f3e576eb6f97771fc29d4fce9e14d5ac3fe7c7a7fb20128fd46d124c05d0808fa75d0ca9d867ac6ede4ed2c5a08e6795208d5c4a0d4a563d02994f219638466b41087dc8757f45c88438b48df0f35e37068a9eff866d1a1d5c43e322ecdc5e91177ee82f7d4966e30eb51f1ab92d14ce25061fa0f1ab5e770faafb421e091c5ee6b1b5e995c41d6b5f98e3cb48c957240307f3c034648410ad6496c15fc4ea547b94d166a87173b85c5727af04cea79040617a385eba39c2085aa92a623845483002b7f441918e40db6ce4165143001e8a6db8c680cf257315de0ffcd630184f4b2cef1379dbdede7e2d017ba0960542b64ee8dbb0845ed2a91445ce76e74952be58b73271787b4c84f70b5678f067fe33ea8669b9097fcabab60acf929e1bffa08fa69c77b2ad76b2f071b1cc46344f841cb80d238860465d502518b5d22557422d196875c05c6e70c765c1ab4cfebe21ccd1ae939df45a6af7402467ac2aa78672b15cd0b727c8410cece2a0268eed955f5aa547975a43bcf0b2f52782a9a70d5354be98328e16771358426bec41cbcb45a31d5c14b9ab87dae02c01129aaa815d796264f451cf24966a7724e9f05dbb2a5feaed51a3aaf3238bc7637115a4a6993ff04c54a9ef7dd8eeb636e2669ad19e867056905e17988100cf30811aac0ca5d1a33dd11cf088ab83b8610721f9a20505607ee77f736d255335445c40dca8997212533fe047b465c47b10bb33b18ebae47bd4cf6731f8d0ac88cdcd689001dcf39ab1842cc27f955a7ce0ade539e962bb3068f4c832d264c2cb925b94ff6adf5fc1faa1da5c0a8598116d80ac0f3a39d237108acd5394758c0be0522149533bdd14538131120d348cbcfa7a6e50608a32f40cd6e67d825cda34ee9387a13e1638f5d0a1c94244cedc9e484b933d206aef878386c96dfc5e16ffd7c4b1ceede9a83e7c78b111867da2ffbb600b5d9a7c2bc9620d1dc83762a24fcbd3ee3d57a05299019a183c311d3db81289087f4374ad0449b0ec3896587459a2266439aa8044e4dbcf502c69d25a677f549818c9936756ca8c669a8b11a07bcdc8851055a7e58760c4a7ebc812dd9a9dbc6af3e5c1e5c6b8bdfaed7ee3513af240db09319562fd7d06dc7333eb761f1004b253b88bb687b86db16f0c1f14bc5b4674767dba5821543db3e16bb2cc670b4b44a60b5bb23d38145ecfedd3834abbe508bdf055bcdb51bcff66556163e6ca7a7ef745637572076b79a43900db6e6d2d82ac73c918adac9185b5a96285379898aa750e3d06df60562d129e67c41ea30fc66cb66199a00c37b8d444ce0a1e6dccc7cc781b8c30272b13dcd64ebe0c09bc63e6979f08843fb6bd0bb831ef6e7a19b73750f175c3f07ad3b477e36c99a65b9ef3c65b225f19bb4f78ee63bf5be3a2b21c72c09272f0630a82ac740136a6bca21e1895eb5bb1b94411a86ca8b298d83f8b4cc4e2b630b4bb99d8816fac4e9899603a73ca5acba4669589a5c49ff49305bf8c067f9d61ae3d4f5b8b16034cb8bb6977dcf669342e734f83ce9c039441fc714c4e89a716418a833cf6a0800b149bca83a7a6140d19100ed72a06d06c747b9945d59894a08e24d8bfbc2dbde5c017735969b64bc6f521c700bc669665f95221766d81b7dd4425a1b27e55bcd5a01b1aa872f3b4688f609f1aa9cb3491c1adcdf9dea3ec416ab23f5422a7c903eb2f4f6863602f5164523a39dd1278ba7cd48ab7d0ac848a4ce3d2f15559c7350c0730e7f01cd2d0c9636628ba79a2c5d4648eab01053aadeb6fac84c2080ee96fbe3b177a9f3becff2d43584b9145b53453f7d37d30b998bbcd6e63ce0b1fa64976e6f2a954ef64b209f707b57b3c868b08d5f134029ffc8f475c2ba05b322693502badb856602f6192ceae9f5d7391a2042a2f156da08a05d3ccc18ae9e807667ae1b7df5416a3ca5bf9476a3764157911536c3d1454b5c8df5c787a6028a50a8e09a640f86a766fcc41319d2ce5cf663edd74155eb65c5c27efb26c72ccb9546285f8c9c8a70690962416e013859525bc6c2647c238772fdbc00f282a8d62001b32e7d9ee7abb28f6b68de4ec6dea82b084c3b9de111b9dcbf77d0c148b25786c77b1ad91f09bc669ea66a2282eecf7ae645ee98aab7145fa24604e9fc1538f807b63eb9d1b598acd4cd1adf7ab8c7397d283cf124b82ac1232a2e58c8513f86c80ad1394bfc0a4c3946f00ecdc0027df8a6eae86e4b7bb09a74a5a400911e2d8458be57d19b85a8745bb512a570849a2106c4e056d2daf808aa2573b0821de97a5912890506c2152a9a1ccb13745b0b29b796ebd6da7bbfb10c1c855de4bc63508af19d90456432978e15e2b5834d92dc8d282a24073cf5cb8673cc30215d8125f2af9213dc454076463dc6638466e5a36a81edf9a022e2202a6c61148bc6ea0b2ba944b3ea86b6bff0a3cae8f3f03ae4bb7d62ee5fa843cc001e76ce6c8a476e927f619fbdf6479edd70467867442f00842d32bd6a1b4958adae5d63ce298e84f22b9f032dc219b9e250dca5d0e64ac607b94d30a0fcbd8c44434ad171723adc01766e2cd99899ac4b47f99a6903029b420a069147942873df33faeafc50da8ec411d843e94f10eaca4ea6ca653cb82f0df6dab48ab3311f418c3ff02d3b78efa4279584767bbec2355e81ef2f318d0b35a47642320934ff9654adf4b1b828684c0aaefaa0d939e6d865f7a6c3f5ae656bcd946421b72d2c4832dac116cfd9362f83d76fd1c63df75485891786a8f1ea564035d666712f5b2463f5198a62337c3c0899ddfb828491f6f0d9ad2c95dd24a73b76ed08aa05da7cb058711bbb6c7a56a5b8db75b11e160759dba1b5213288ab8ae7372936cc6a9a268ac474877c944e880f1c74af37643be85e4a6ba836ee38a15df682e282bfbe03ee1c3ee8c85eb08d3c0fd38682d23d42b33dc688fc161f6e16f9728748fe19de4fc3a0be1f4a535cbfc72dc3780951ccb2cdb65633361781e9954102f03b42b8a86eb99c7b7cab8963b4a757b3dc8ede50f5afa4666c9272da34422b257cbedcf1307531a1142ad5e06dfdd757f655be405dce58401bc3eac8567b72710e1622d91b88b1c8e3252b9997ca62a4c79aff613e24fd2f2515c26bdc37f7fa78aee26bba19c0aaa0d8e25118566ced9126ebccbc24f541fc015a022d0400dea8a4f107fb81534b219f9c24d0753667106a64b9a84a4862d818875d57240757c04835e7916515125b54f87291f411eef73da17645817f45ed521dd95a6c89b9016aee3e194953cded56a7de7ea876833188f26116a6700fd1fc94658d4be5b1d12f492d00940966496a110a64497b6745028f12f95aa0fc8e9800bd953a390d48849aff826db51cf0c8a550dfa8c6a5704f80d86c24c0b9c8cb1e98b7e249f99ce6b2ecd7b76c68aaad1abe5ff74137a89bd2dc2a0bece4de43e454d9c84d9190690fddc10b3f5b7a4dfde21b2dfa2840ef418c08c153589509fdae339a61e4d6eada4b4518a7abe0a50e0e5c209a3f00e06b6833b020e14b0ee1909a8108f7d5c491b2bfcb8ccbd9514c3a41de40e35762a92e8ed2c992db9db4f6e24fd68862b79a0f252386cd458491c4683ec68c9c6d1e60dcf0e2ffca4f8e63fd70e0f98a26b660c3d1c2ca563cea0fb1adb3e56b2c087e4dc9be6f4e64ae805a821de95d2b891abf601ae52e80759397891412565263d81e3d0d09c4d8a62d7bd0ffeeaaf1ebfb3cd904d7bb80b461f6b796d2d16bf8ecd082348a9c951c952aec592cd0cb8858c38e8408c808f185da066f846ec351a90f18c8d9903cb1d6671baafbf80f79d6988e22bea664538fc4928909e7c55c1a88c734d35d43800d195b020fbe5a6b03530a7c5e7a40681d974a019a1bd2e1b5951fcbfda65502c772808923bcffd59f487b3bed8b1c8dcc6dd8ef2bec9c61dd21db6711a5d5001e199124b7073e4ccff57ed02191fed05d9f71edd0170c6157124ca15695a72a8dcbf1e0db5117cd69e0a6ae3714cba01c3447b3ad44169dfbdf43a4e0896c9fc313c8275be0577ecc3d634f5fe403ab3ba9e5dff9a057505d6219397ed084743936bfb056abf9609eb070c54481c0f495019602a084f69d6cf4b0655e634097e9b00d5faab2f95f3814b136733cc18b0d07901c1fa39fe562152a84f397f40849474b88dd752279c75a968ffceba79755822fba81c4b2b3fa557f19e9c6e801533661789d81e7722e5800a554d04b3197609c102e57d2ba110e163c35674292723145ea2201cebd5d99433fe4107ae96116c3c6aaa67f104383a7a66ae0c94e37e19aff0d01157e4b3c120c0e8f21417ef911940d14832fa52b862c158b09f3c0d91907d27fc5e81d9d750cb4292c95ac95110f0c72034ad71aea1bb96624b934107ecc4ee8c15294a00d9710b8e9bc118150fadf6cb94d913344c68e86e8ab2b42f55ca8889291d6996e99ea72f0b34e086844e2d8d48be56bbd2da9846316ebbddc8b26b65be040894cef9391fb4ba3a6901480b621b6ddd71f45e5535868c01a8e4aaf94231d4244915c0f9b22e0a6fb8bae3bfe12291849e08fa891925eeb9b1672520d4931dc89f43044df0240508cf5bd7e03c62cd847d9d2c9839caadaf02f3ea52db535b11057a89875f2f44db775165e358796786e646957cbcc133a47db777d9f5c37c18f52a8838bd84f4670b80321cea3948c5ff967c667ed899076484be64c64b2386d6a09b1bf02168933d6c014733886f250955046d52586eed53b0a9b05b04131405c1d513ea2a1651588b8b79ed3936b41b060d728227592a496fa0d2d5cdf28619adb69905e888996e1e30dc710ab201110ed35b7d0a06ba6ef27bb83c8f902a981a05cb552616ec8886aa4c18d074eb8dc13428eabd0dd33033f55c92df9f37f4679c093e3bc87ce46169172c8cdb3204dd4a445be613a6142155324e56c3fcdb7444b846bda146be77e3aa94dcaff9693cea22e8e72b36a6e102db8a9d9c56301b6997d31fc3372da20c60d887d2c78996ce02843065f0a95a392e609384ecc20f7e65e1213650cd43bb992d55cea151ea11a22ce849213013ea286c6fd0e4063361970c2e4a58c4d6f2c0ee7d3e15d942f496faa9dc3762ed302e984e56526cee4d4db2482c629cdf85db89311e9ef96747553a2d6a023f9de6c5c33d04f5d5103c3d18a3edee6398818496ba7a094dfcda7de3f1b19a93f90bcb7ea36791a076f683f77acf46eaf7f4edfeca70c04449f27fc55749151672788177d5294b0af77a4a1c10ec3e3ac83c5c4cd454d2f5be1a99ff47ccb5d91440b2370ac2edb81170ae946d2f43506b410aaa57f2c50acdadacfb21fe949c84d6643ed0c35b4fb1a0b090b8a7f2a226975ae233ac8552a5f5a8d559fca9f2f40770600cc27bb614efd19240d57cc1c805de6b52a769f9809112b2f29339b25d793ee6f21623dee3a95bb8b9e1e9dd87d07e640b66d3db683722c3fa4924d52cb09e15a14e518f339ed202a0f405e33c251c69147d9e296c605ff121b47922b838c807de58223210803cacf4f63ed6bc250c160ca8b0c829ac44f8ec3d46f7b49dbe7c3549954eabbcaf1bd09ea327b194231b3bb86e786d9252c2266cba36e0f3c9603fb26424f20a92606af85afd91807df12e5638460c0e32d7a14cbd803ee508c4bd8bacd98cbbacb1782b9746da1928e2beb1be9924682d97364b924198554107f416e51056ce0aaf68587d5e3299406c95943622b45838f6dbd83ee8ca2c5ff4e1590f6004df4894de68f4d925b586716bd0a883bc84fc5023766835a971719ac5f01332dc70b996df2b7452cc8bc005b5e7120ecdb3fad4b00c1011c62fbb8130b49b4d62b536826668eb0027f964ab0494062297bfa24c7416d8dff27ddac834973395df5c83e869b9d854d329c6b95c5e182fee2c0aa0160f99ba9462181342489a3002dc329bff9cb93dcdb52e8200638ef182284e52a27f8badab838f672f86406f3d570270924d30dad28950a5cc820a9f69d9d7358c8530e9641a847a9e24e1af304c5dba78e934707533497a290185c62cdd590488cec9f5b738095464f5280b78479dd9f700a35786f5f947c206ab38040060d3f5e32ce7aae9979ccc7103cda4ae7427a9290a93f924036df7237414619dba30fda24158b7d9eaf6c9d80bf5cc227de00f2b6f33a954ff662d2fcabe5a280c79393a84590ee517ce6cb8c71480d6b661303eeb7a3fc8cd8d535804ac104fcb278af55bc16a78a68a249df1dfbdbeae72f2f64ed89d3084d57f1d2314a8fbd882c4767e46793db05846b1062b4b6cad34602bd3684b80c95adc90e03403c6107c9da5800ba7755b669f6df84582a5d1d0e3eeb6b3939b800e6e7dc13183da185c1b6bbcbbf1b7540f8c287aa6218e3da4a02472cb4c753fa47422124ab067afc0c4c97af92f306d26cd1935bf01913b45c4ef8c811537283b0f6fdf46e6683419d194723a013f4bb8812342627ec84e6bf2d8bde46cdc598b3645d3b39f20be70b6d980ee0243265ee46e45cd7507396647fa8f4f4b078bb3ad098c409d9f844ea54dd3feac9a4c08132231894b94d84eb0f50988946998be46b0c1df81e0c711aeaa6d5f8e18ee153375928d1d61af449d5cde51c7eb4ba9a4da3b432fc4c9970cb81b6759f09f2c510fd18e766744bd2ee2201a24cc8752305f6e6f8dd81f0c4bcd06aa0933f98f1d3c7bc74049f07389d2c0ea9d4ec6a00e1f3eb85997862409b67682deaf6be268afed76d45e65e039b46a9e55a6e499889589a98c1d54fabda8cccd309e9b8c618ac454553714d2f6314a3e4e1f98cda1c52bd8e86ad9626a5146d0c99df4e93af485b106d808cc0419a27c3884c88dcde1271a10fb25a539357f12fb8b358eeb36ce11149192c2f27e5210f6c2da48dc66c0517ecc6d88be01a83913652af7808d9032748ae3f39336d382deafecd369867fef9d12914cc515d6f19d8c9fc2d985a9c49790a0415f3991da90ef4f6a59bde8825f9a86fbd85169f8f63b813673802b9b682347d5c441230b05990c2805c4be808fbe44574149f528433f9c765b72a11b522addfe8601f06bb31052d7a426950975026b90339bf1712876d35addd9d9a757f36368844bf6d30a287640e52bc299b8e12ee80c8fb7e0ad443298715534ee2368da5cd300dd0c91557d9fbd80e9ee90265d6f0ff3f18a7e18beaa49c06c8419f09f84848246a40a304554a8ef57cf455a06b8ef27035d811114f408d7d23083e0d5cf201416d809910ec02b909ec4dcaa2e36fb242980a0d66cc883a8c290da16fde8492797bf2e3d5859fde0ee264bcaae1a5f660455ab2dafb897f63069f240500db4b0f3641dcb19e4be9e16abf66e8d40c272d90707605cb647f8c8f9cb367b9ce5bfc451fc152f304ceb59dbbfac029009493460bb529ddd64312abf0e80b62a9c02657add86e53a10bc28faba79d465a52f95bcf2c84c1986134efe4c8def989c4cddcd3331cd517a9ac7daebfba26446d2ad7404b520db43f495e6d1e2bb134087b26501a735111eddac6eda5e311a37b1176a3000f2316f20f1bcdb466c285ff9d802c88452b260cc202d1c302822dc1c3018b5e61a0b542a15d6bc80f88c34f44e4af2e69471a571aa8a188393bbec2adaa01fca82959acf4a7bc79d77b4f2a70f12e85098c81020d4a4f7292d4afb9637b1a186d50a321747c5e43c2ca86fc5a354d7937df44ab6c7c5b311f2f9d325836be2698dc2c61b4448f3630221221cc150669fb1336e498e19bc75481b48bf1c010e59028aa7f18bda6503092ca8900aecd9f33416f24673289240abc068a93b41622798902212e14f347e7aab016379fed817ee6ec47836dc77b966299769668a93081901b678ac7c752655643780988eb9bbeb58ea5b2a9f57a44eb3e782c5f47387eadc1b305f8649c001310a068cda5b85e3d42a3b3a2057d732b6ec6f70201288909fdb904b7a6cd26cf7d3f19662a725adac0b6a885d31a4d07c4f04c3e21fc37de6a0f025cd7d67dd35e992fc45d8de9c39b647478bbbd2ccce5ac1f2f3eea89cbc0abc156d1980dd13e9b3a4bfd63375470b2c06586d4e83f3bb6c915c71a7e2b135c523c0ea1b199b0273b7a68f64638db81fe0f4fdad49410212d9946ba1ed13d188702d96dec41157055273c86cf7a638d8f3652d1fcb3a8bbcf468eac8460ee2e02a96d8af81ea07b39e4abd12e7541ab94ada3d6038f51c8c5a02378e9b02cc81093d2811f215534f60cc001009c7029017cbdf644802ffb18514ba7fde37650bb9c2867d9784815290626aed4fb3da6309eae9c9f05389a6d77c951e2f2afa90f437e7432d87a4c547a84f44bbb6d6e41f4e4021cca122023577930c5720be83334b8cef5106c98d05ca5048ff82a00a179686208a019e3c09715cafb3efa06880a218ec528ba999a3c987783b647fd49c24e3f92de33512821c844f653fea0e41f05666159d8d1c6e6be8c62b0af1759f2d59b3d051b4e331ba638e59ed81e84140d4e54c758ce24b6f8f9c4c043b891f9d53c0630742bd7b16d0d1d4b28849f92c193df6158b6c20f05d0b6f7f916785b33311988c8b13bd841a8ae47ed96f7381a97d4c115592407cbebe8718b2f1fd10eb5199e4a9f2b29f3630f85553b87c5cd9403b35bd556be29eb15eee596308995dbac1c700eed3b9019ba57278580d2ac0cd7d4d933712ae57f9f50fdf7cc9acff55338a4f9d7e692bbeb5b31fc5a3f4ff14af7f4a76581a5a4d81a6089b9321508b3c11d0c68815b5ef9a550049ea0eb5764b8eb5a18f19a20bd01d0df3c3cf31995e00b607e6f3f0c781f4c5c347cba5b1aa218df8f63704c0d78ae927e6ce25c0fcabd71c372e671c15010650780f15fa6165caeb4298882229365f21461d4a12042492688d2aa513435c124556d228bf380cb38cdefdf2cd76eca2fd7ea88b4eb7b83f066dc219340900103049486cb629fa6fc4b52aac615cdeab43dea128e287d83d79676471cef2dabca57e6d8c54f4fff3a40cb5d71ee87ee75311641d5658ae6401bcfba809ada21a947fac6d741bcd67ababce379503bbded614b4e5829d95cfaae7a667dddf4e840bb23abda50c529af1c507a21e031e14f1338a7fe29da19762042b0d383d8187bde1b7830db3abefd0413cfc7105152f848e7d9795b12a0dc9074ea9c4f76d0204938fd56d9f6eb3361579e962a8c5712e35d85c906a40e5200a5e44c705bebe694d82e2bc623af5b9287b3b9ccf9d8f99440a4a1a92b34fb13678790747ca82d95c1f1e7228fbc5b74c32a03fb143935fe538ed5064a0a790ec1ac95713a27098c1b33bb36b15f8cca3d398f7737d83e261bf2e81e52e2a99e8548ec5789b39f93780fe4f82dc916fd21bd285fc96005bf8cc1c0c6e262862354c7a7a2bea157cc1f20e0a83780dc172b7a43ba4683add0627004f9bafceec7d844b899af437dc0d9a390bed3c1653f42f4e38d029ad174c00214aeb79e9af18d64ea10afd03bddee1246115d4a16f6a84845d5ae8bdfee89640bc9eb988d09f751c1071d4fcd7a888257311159a56ad06f2378b1af325be2d8e28ea9e56c331d269a923eed55431ffca5eb4ea55b0be8ad4dbf63971a929ce5c0197dd3ec52f99c08d168b3f6aa03d300d8ec4943221ed80ca04cd3350d9314c08e95af9cc4c537b88e6257d8bbb4bf51cd622b6ee3a7c94bf1fbe84d2ccc306903b06eb510e80dc00209d161694384b5f8b0edea2374b880c6b9c62e8e0bc0cd182edc0184d6d717c81700b40258f88d5d3ba9ec7709652e570158758af6b47e1d3d4508b30afac98916495a94f64219dff302e8bc6bd40003ec7683d4200698f9608c482b678907209863959d80ffd8a5bba9521eeeb1c47dfa2ef0122533445f2a515333b8c005cc7227c34a9d6eb4c56518ff37688287b2dcbe2870c830fa37249080668f8efdc11dec255e61477b70c59c7c5e7d47595cf797a881033473d81e7b2d20d260b92042b2b9be066c7425a7b1a008c1b47e402153f52148ee5d9ad8ef3f2e4a11ee98622ca140e05c11ebe21004305fddeb78896bc8268169d9df760d38316a25f9ce36d408bfd02faed17e689020490e624516f7b032f812844833ceaa89104501f038cd0a9dd287d8aa85c3bdd1aae170bb0a4f6f160388a8418384d0557555079048f320ec7f353c0c08432a3c0fcabc3e794c5f16212a3e0882ddd63248335918ca4d22909408494436861c838005f9c857f3c75126abdb189cb89a0c7610335a6df99158b123870c26b65fb80604fd2429c17eb6821a755012d488c6cc2178fa62f065de993efcf229ddc47bf325267cb7e1e74c2e9c610e72f72c2c2634c760c89822b671b7263208db68840b180f82b975644ba5c01da7568566b21cfdc5d6f3a673109330608cf1eebcf638281af273dfdd82d81c60627d9d4eeaef9159259b21bd9b8f272f5aa3015d601068743bf5e186f7204be06194126093278d30a70b42cf7e6fe64c8056bfc64f0d9b78e906e5d9a8b9464adcbbab5ab015e3c03816969f13c9e495b1fac23390348f5c5e60a296b497f6a9a061e27bae8d1b4e884236a500d400817e40531e04a0420744a6ed5d7ead77e08e78813b4d1205c667cda20f5663db40e94abfefd9818d5e01f4e57dfd6429100a505ff7603e28c0e7194a92f1705a40407021298d3dc629a5c0296d59d5463ff5904075b15b5a94e2b1133fa8d63b896aaa56e08ebf6d3f14a047f3bb7b5abcf3cf600e861a27d10c3abcecb8a285a2524c952dcf831ee1f3731a5fa91ff5505cac795d34046291f39a4755b779f25cd19c99e442926d80147e248cc000d0f9173d5880ecb7039aadda8a5d3054aa9a0e39eae8db1c58faedb4c9a2944dd88bba7c339cec2a0bdd39357adab535cd3475c50858c67fe121be8ceefa67f4c101200cc1a030931f8f94673f85ad65e2ccf4fc99d55fb7522e8d26a3264256f626bbdb965b4a29a594016c085e089f0849808ef8501e2a0e41633407295494302845435cd49ad082924c2586c861caa484c185d0bcd921c3f22cad441e076ac8b4f46ede4dab9c42d4ab326fbdda49bf9b1a327dbd6aaf864c5b0fefa4dff551b9b0f60b6d2c04dd9c2ea5945206117fc2204339a594724a94adb6da6a2b6ada8c826e4e29ad95524a198e732221a79c524a29a594524a2967cca1105151ce2ae26dcb991b8d8868b36d86dd9d379b0a9434caf0d96aeb4d2965ce92e3388e931c2747a3914b131de9799e27f137d2f005a0a2cb7b251f6d2bfdbeeffbe6f749ef1ba1b4e7e27492a4fd5a56987efd860078dc755d87bdcdf33c6f2365128944ca1ff77ddfc79546a552a934f2485f5792a88c1a2fc079df94f493927ee3055eb29ea4e594f51b67f8ce4927a4350882a0c9743aa198dc202a968e68932daabd741549e732515121e9b85051666bb7bc8da20ac9bb378888d276afd83da2585a5a66cbca0a49a7935151ce39a7ed783a222fbd636161b12c2c95b24c16a9027f3af8a30b3f1d1c7160064190821304a5e91b479586c9960c6718e9ba4e863248100d809f873ba48d2833837a41d918f9e8f29392a6cc0cbbd1beb6be2ed9a06cfcf4e92f76d48d66e697645ceedba7f5bbea7d5b4a525ec9ac56ab6a3b6f7f9f7b4e228dbecf2379d94af932c87e43007d4152c2e51226978a8f96c6c70c6a550a2553f3f251ce5c3a7c94351c1fa5d00340ca702c6d1c6eda328eda307eda339eda25b86a93e0369b00aec27e790abbc6bf2df3347b87c67736e0394bb47c0339c7866dca9429394bc47887c34fbe79df7e7ddbf56d1da056a550279b528e6fdbbc6aa736ea6473f3ed9a24dfa62179dfce91526990a694436ffcb66ad27ce9c97dbffb3cd34692a373ea390080a3c3f5aab139a1522a0dd2a87c9e530f24a2bf5d02a85529d4c9a6e655caf16d1294501a6532f2714547df6ed1809a66671b981902dcc8e060e98dc35f5b05afd92938cdde726066760571b87619191cae82430607cdc697665b9a5d6936cdd1342a1ecd9e39ba047023b10949d84868c296cc9652c411d3c1883099667fb863030db09cf71c8eb3731c85adc37d6c97a3b6e8a7fde2a96dc355fb86dbec01b8ceaef113f6cbbf8de34d1b098d973a1d1b05776d1f5eb375fcb54ff02ec7b639a0a6c9b16b0ecdb961437c19400da8695e2e1d393bc79e393736121bb7b17fefecd8509a22a5862939cfbd3439e81d8aa403b234a84d343663fe6fc7a4bc6fc36c51c5fbf6cbeb1e0e6e243d425991e45879dfb6016a550a75b229e5a0d9020877960805e0e0aee17abbf8b75ddeb49104c0551b090f2f6d4f6d245d00022000010880c66b6c243401e0b16de1b76b805a95429d6c6a5eae168f66ef7095bd1ddcee7ad370d59ee1df0e4d30c13b193b159e9cb54f27d78ef24e85a4833ab99f50a770efd892c2f8de266c4961a40c3f6d96a3b4c8f2beed34a056a550279b9a974b074e29c7b769805a95429d6c6a5e2e1d380028e5f8f60c50ab52a8934dcdcb55caf16d19a056a550279b9a5729c7b759deb75b42efdb2c3b3389ebdb2ba056a550279b9a5729c7b757a056a550279b9a5729c7b75540ad4aa14e3635a51cdf56d16fa740ad4aa14e36352f970e1c009472885e6aaf5cef16576d16ff36ea4d1bc98d976e9c66ef2c2155360c268f21ff8a7713dc528a2d5bca9bcdb2a51457561b0609d000fb15d8dd20e9e8190d9a42295b30deb74f47a81234898ebd3395e870dde4db2650ab52a8934dcdcba50387f76d10d4a51c32deb7b5caa6d931d019b42a45c466cf214a396af64e4dcbfb76e969764782f7edef4d1b490ea7017529c7f785b93a0906a1d9e4a97341668ddeed56c7b7b20829819a1086dde30e705cf9481eeae77cd7aeddf38fabbf9de638aeba5d09b283da5a6d155ad46dff6e9eb3cd167b9306b49bed9d6ed6cd24161a08ea66d939aee8ed1d3a49515391ce5bb30943dfa0e77ce452a4a274e19022f5156925e220fa481d97f7f55b748b4620025947ad1137e8572b087c75ec5bd0df95c5944ae1de80af9b84913286ab82c770694c993245534b29120f8e428e73172df3b3066b7f3ebafef6b5f5c12da5a8b794a24c0d16fdce7aa50d3ae825d7da3952018480a12eed70e41c7f2e5b965a3df2edfa6857e7c6199246296985d20d506ca1679ec10fa62e43eda7af64a1afd3ea7307d97ee8ac3f7403230f35ea261539efb85dbf93ae917bde71241d2f8fbc119727e568a62e52371aedf9a4f99d97391f8d4252018478e17c4eca96fd4cd2c9d52d6a56bfd756bf75e6cd8b45cba3bc933cb69add6325fa39b4e2630daa351fab5000a2e011f39cd077c4667242077dacb49f3bd589958f15a865d623583e562364f8588dccf0b1f2f0b30ef994cf13cac78c698463ce33c2316b19e1a859e1e82da30cc7127ed47b6709165fb5b885d597856dfce1dec02a6789161b30748baf9c85a5c5b7169696705f2ba3442bdbca1cb53ecb0ef7ce069c8a9f558a9fbea21f6e29758c358a953dbf6537596d09d0007b16bc7dab9595bdfddeb2c2f816fab952d928576d10e52a3f9d508e512794caa65073ce5ec2671f4ba05276ac6c5bd4b63caabdf1c816f5524d9f7ce4da55bbf4a59af65cb5bf21d5264d1fd5f64aaedadd976a5abb6a8fbe54d31c0ae5dca7f60c7c542865eb1492744c0fee71de30de8cb268fa0cfd686523e5f9d1ca68ed472b1b6bec472bab437eb4b23a670d52eb569b41c124e6ccd58eecc8ea6cd46b0c76ba24d51983ed424afafa48d5abeef22498e95dd7715cf5387bbb3d7fb451a2b8c2a40b13d41f549f42e87461b3dca8d6d1b601438fae25e9546b7325e9e4cd574fafb5732564346b13d4e9f53a33f33fb2e67c64a9d71d64e6299db55297d433a5e10ab1945a5185c81e8f6f6e6626b76f66c4eb9b5b15340c06fbea37065c22615a5170efbd9925933d94c554a3561b49c7decb5ab95ce67799a463f1bd2e33cf6bb94c65cc408f284df2465c584b9e8f39b5e7750130894303665940d4f539901d20b12ef4085e8ce44716900fbe884091c431c928627a5106f62508888a99355342c5ac8700ca6ef70b93971a155f82a609b40609cc294fea661bcb0f6a5a083d5b31d9cad7c8671f6598f8ec323f12a7e4b97ef1d9471c433e3b8e9720120aa7b71e1793fd823e9f1d8ce5ac3518fbfc2371ac676fe5273fbe047df61a0122d9ca5ee301cd02fa5cba3c9fddd6a2cf4e825975405b002671509fe593d8670702ab2f2f94808ff9a35094adec3e64be0b91685fad3210580b120d240664878ad9b9503481a63eca27b0cf62f8fef93969edd2d0bb57caef748dba7413eac2ae8542c985269aab0e68af491c196f35ec4719a46b2b14155360f2ae9958f758bad039fc1116808a560c41aed5a868bd078d61811e75ed0691909016a2a2686b7756d3422950d7885ad528d130d8b7a9811e75ed758dba681321ea1ae282481751574b76a9c3d051b414eada8981398044a8ab858619861ba2f6cf36844eba8c3885062c199a6104a530a90bf428879c00d5d775eb154f5a6dfc54166c6061306d8f27ee172c7417bbf870f510be21004ae147f641e678605948c10623171df4d050ea88230c21c508b21614e8080c660323394a942980a8e8a14921449b22f6338ca1e6a8398aae3edaa774e6da4393a1e2a4da872972d0c57b5018617ae090184463c468cf1845f48cf1e3639a309f7246a240312265dbb65a5d8e76b312e38ab79c3d7b994b42898c849579f1001e97b79e33cd73c88caca3ecf23cdc4d78b3baf11b1716d0173af0424fdfd1037d23e56fea0d911fe5d0037886acec0575f630bc69cac712506ef269adb556c8cfce23a950f8198ed86d107afe7516aaa71c159a1c0c479db32679dde8ce23beee7146c1954adff77d9f4be944b87d2eeb27fe37f39f0340befc46e254ffbe70f65e5f330a2923a2620b2c461f8dd3891fa72c0b49212934655268a6863348b6642cd14e61283ab281f43cc6808b481996b33cfbbdf7e6ed426166643cc8ab11b39c4519dede8d47868a332ea27d7c2bbe5ba6529af66e4030f29efee87276fad09edbebd60b65877177bdb3ded5ce8679f331a33eefea522349113ae9327a3f4ff7a82720451b8e407eda10023f9d65bc40cfcf70bc1e0f99013eea4d8b8ffa9bafa8b8838a79344389e626f0027555ce473cdbd1ade8b8aa9eacf6dc1e777843deeb797a6b7dad1c50fd46215d0da0e20df23cdc36be2eeb75795b54a8d2a757172f267bead5e9cc93f13c750748570e4f9df65418eade10cfc7865eac03babacc192bd01385d3773e41e8e9fdf4208ff2cadd3956400a9a6c216a4f96313691057ab43e730bd427ce2eebd66ed800b36505ea5269189848936618d124a7422c781cc0618b0a9be32d9c91b1a102a8483db3f0420c32cc707d445d3988a2906ecbb0e70ea506e04299192ad217a888fa18a8489d148e36eea2383343579ef7824f5cdd862de3fc1670b8fa4aa46b8c0af4663da7eed239e7f9396fbbcc360a572e5b6419d1351c5939079bcbe86877e1a83f7fd9bd1e744b76219fa77b0f8a2190b59e1175fe1b11e6c2b9712a38ddb0531cb6ac601fbb7d210f012ad650afc857100a537b5498ea7327871419c6571ff7575fad665a82f2a5ab8336f46446ccd94ca6250d4e70eb5d9a2187839ecf717a055acfd5b1cb9ce7d7dfe6266eeef5f5f9b6cdfedbbabee8a8d63993bdee1c664c49928a75b31dbd5b0c75a1002032b5b6ce79b37a206ad7d61bdeeab476c1841e678c02a187c2a89446167ad4f9a6ded470bc32bd235759ce5acf9b5178c3d53c476fbb995144d76b6b18561b6445230b3d5154a47a643da5395018ea44b2124f3ba7e18f4ce90e0394e891c25ed6a0501814d99a5184164db913e622cecc69a79d734e3b67486f72a66e67c0328a65818a52ca0d15690bab65868a54868aa897249de9a3c698e27b696d0015a594dae251b1453e05ecb7c0b2b5d65af1756d7138bef0d78632f464d88045455ae2c17b817705e8814456981b9b1f6666f2df51ce7adcd8af7723a101454f1fbd6a217361ad347539cb586353d27eb5aa95cabe9b1e03688ca169bd4263cf9c5b89d3013d3f67ec37fba87be7abe7892d252560cb919788e7a33dbafcc833ad7586afe13892baab7872158bef6556ce39cb5e57e9717a0b4706702bd0f83cdae76e92eb5d49c6165a5b9bb714c6958f151569dddb66593fb79747515cb13c7bf7da6b9ddab0c5ba90b7e138df3b3a49d85a0fb27aec780bcc8191bfba4d72f32ba5dba90e6ca58f7c039ee339e7d9e366ef6da73aa8e72d52d2a60129a58e6ddb36dbc3b9f5550c3f6598e1e7a88a266a2fa57c87514574cb74213fc33a5da6e0c2ea5b18f9ecb69d5d4439a707597dc676b6e07085fbd8712843683991f54e27751f8d46239d11e79264395b6bd8b2c2d7b0a5ba90afbbbab0fa165cc84f928e6871d5a910f8b17aae2bdcbf5e656ca16b580303b6b926756e7bd0f32729d4ee7d4eb5cb3a371fd7b1b51686692dec57a4cc01d3003dafe3ddfd788c85e0961f9f2740e67ce1023a12f03811860ba604f9f1d5ed0ba1f428a6a244bd950c8f6778cca1787c68ec1dced8cacd51ef69cb71d9b7916fde71dce4726873761ac85aeebc6e3df30c6f5d661b8ed5e3809e3f6da8088476f96eb750dd664f7b3eaa3db76fa898431b20f4669f747663a75b5a5cfa89c2e6a30f7abea5347b78d21d89e34350f22b2fa11842edbb5257bdce07ddb2b9105761f39c47a10bd93939a704e8b7f8a0bff9f46da3203fe750029bf7a0dce69c67e7421736cfb3fe863d061bf4f3a5d002e8e5cbff36dcc216b6a890e557922771240ebd3985861a1efb06db300e7d8cf11666d9c2610bf6eb38f441bfe57bd02a849bb8882092b84189277c72f039228969b30a73ce39a712407829549084b6231bb447451f2aa5a4924a29670f6aed62bfdbbdb549257ae9ddd04b973f498ef8549e1aab39449102a5c8c950adc98c169356c3163f82b5b73f3f824ede3abe43d4356a276f475006ca6ccf8f1acadbda5d617dec8c7c916cd9aee8c149bb59978306916821b4a4d550647a3b3d84714574d2659453fc98bca3b45a15d89300fbae7ad776b7bb1dce495a3f54dcbc95a405c4b58e5071738ea4d3eaa1e2e65206913580b43540ad55cb578f56d35da411029d7295a75ce5636ba612e337cf019238d955846af5a58524cce659cfeaabf5536136f7d96fee22d0928a33fcf9cd5baad4e6d1fa69fdb492b480a86b3e178ffd6e215d1d913b2471a6e33be4b1df18dee2b1d7218c316ef5d0c7d85723c7ad1e10044150eb52a98be22f86dd172b9e48d74b8d8a5dd05fbf4412e7e457e5f75ecff2a5e2d7b57cadf8f5957cadfcba4b1c945fbf2ab0ef56f6aaebbc1a407ac32a7be6745aa804027dbd47c53b887642c5cdf5d0cae621b4e231f48347adc26c5e030b3d6a212da49d50d7f5cdfe06a640cc014c220ef9eba328fb2b12017d14637f9530f1d7c7979fbf261f7ef3f1c690bf37627f4f2a88e2af8cd8df564d060ccadfd68cf5e4af8733894322f91806fdf5914534b28afeb2a2fc1d5fb0300207e5a34acf4ce28cfeba0a4ce29c66cea8127b15d95f1e57fce6f2c5a3266136bfcec30af90a613c6ef05bab8704d8b76af5c5a316c2a8b88536f0a8d5d7cbacc2c0a8eba516da205d2f569c12407354c21a253dd465f2cd7934a12ece37e721445d9f6faeda353d15667b99d557cd4f85a951578d11f962c5cb2c063fbe60f19ba34e610d4f137af6236b36001e8973f22d843d5108ab303f3790e22624a130f29b84a2e7b7148f1026a46f707dbb3e86b1df54d753a71bb6ac70fd73f9e3f52f07dab455626cde715ef53c8fa403c3c96400fa5fd83aed96a9d5aa4296c085ad16c7b578d4b891470d4c02a6005462f2e1a482dfbcd3fbabb556b082fb734daa25926f3e4bbb72bb1b225d1d9198c499be1591e2e6e3a57542d827a31f89d379170427b898c4f11c86b98d8850576b46bb22d2d582428a9b6f4242b0ba208424239be4de9ede9a51d775428ad4376f41417bea1b756246a24f3421f49b133cfce6236bf632d9826186b0df7cb47974610e508b478d6bd57ef3564de28cba70d7d99ebff1a8fd9603245b3c6a728a1fa775d2655481bdf43ce79c73727189a848ef50e945dc13e223d7478c3b2228d455d423716e3c75bb710cc3a848f10e9611a12e9e39573daff34a05164a7b99ccf8ae7a757b4ffaeed23be5b577fb4adf594f9481b63708ec895d26a20934f6f18e37c862f008e8a3225241e99138369efa2dba44170a15e98dd213450a75c16ced41c31fb4246d6f3b2b34f5fa6108bb2de8a4cbc8c97ef38efaacd3bbce5a29dd1b8dba20b5a823d500d2a31a407a540388a3f7e691d700d2f5e515a2e2e63d68b7f968c31083fe6b856c6d7eab90256cb7091537ef6e6dab5981cedbacf5d624cee6c3f4351ed02ef8713ebdb5df9cdbf36f4de2c8d016c9d6e63da8177a7232166c339ef45df5b6fdbda42e4ba79576fb64e9bba96486bab69d5fd5b1a7f0a3bea8636781baea638c633807ea925117e7d8e5b18bee420389199b9b6d731b45e2987ef32b7487eacbfa567ff35b445d323fcbdb77299c40450cf6542374d265ce404faf08d8d3a3544a4aadddf38180248ec8427d65f1e666f6d8417dee21defa103b7a5071862d6e6d8f1d54b49eb3a651d1ea1915ad9e6dd4b1a6bdd54aa8781363023ddedc20d2e34d8f1e3da8687dc78df41bea926ebd9b5ee8ab84bae4df5498a0b75e43a4af8f3748b2e7f086ded0104167a0b71aa8be727e4a42e1024d49bf52169afa0d7b2cf5b028759a0859d2444ca0972496b59675efbc5ead9df3de39e7b41205d657180f741ea5b53ed14468e95d2973a30e44a1509e09852fcaa22a0a455112f59534a803a88309d4019592a9542aa59a2a954a05ea904ad5542a4553a914683aa15029d309a5e3e22a920ecacb2a2b928ece0a11baa58543a550a8546af5a994567a056431b578dae48126944a1311b2b0b4c898c162c9903163060d1aee7b872e2e366c541b366cd85099e1ad684c1a2b335868b4b0c2b086210d69d070df6158a3868b8b8db0c6de6158c3c5c5868d1b375e50da85a463a386f6668d39278eb93131362626a6d219131323b5e762e3c6bc71a3860d971b365e441b376ebcbc88220c4c4c0c8e97175184818989c1814346a6d59a9981918991a1b447a53bc8ac1d51022d4146a64542093339686a5a2df00812482001450209271248985a7475c5921b8599e2574cc2e33af17c9ca9abf5d24b34d0a3f778f33c54cc4b2f65a147ef615ede29710e3506093d9d5249a9b593526adfe210938279ca3129d0d3e7cbf1d6ec147009b8045c022e0197804bc025e0127009b8045c022e7119a518439fbb4e13a1e56868c6334273dec92a27b51daa1b57282ae691f579a583b276daaef31169ce21e6688fe0109f9d823c20112ae66d9b1b28a36257bdd89095e94214d2d592b910d4f2e1074c5a41b4be8012c5670f1224713a6fa19bbade2d3344725d3bf6c9499cd2c85d3fdb055461720c66ab4761864290c4e1748fb6ae3da081c0ea4b839e1d880c084f7d61cf0ea407880f9023244ec9553ebb7ea88eec738faec7aed967770db988240e97009a7343974f17a3cbe7330bc84628df40b38066ce02b0832e490bb8ae4320b00a93379018177ab41182d9d1b426a653e320d0238dd0a869846886240ee7d969b6a021a2117a4d13e573ac0646c525406012877e76203b4062f53573563e9f9ff47c06651f5be1a0877e94423d2ccc20d02371529f5d17a1473026d4f3d99bb3256bf9d00aa2f545093c9fbdeb76553d91a20a8a8a07d41315916ce5eaa14a862292ad4c841e411f950c0be952155940480586149da8c610a386b7ae520511b0f8ec15105a450983677a1ec3211a520f07b5561fc3211cfcc802226921f49db586c8916797632b87b736429dab8aa8cbe5e32a30a4cb9584146b62aa3146952a3e5c45d99f777be42e1ffa852b09e992327b36120e15491f3cf9ec634dec2514273efb6823f4d9bb1d64265be1d067a79d8faa0b411608975eaac46c369bcd463660dd0d2078a86b3639d09bcd66b3991f33b45eb1881ee76c3667e39cd55be78c48c6660729d3bc00f663cb406de2a0fab6511cf094af2c5a123c65a7ded0c0fc615d82f4286b3e4afcd40a546735a8767ddb3715e6ced46ab51f3f6ab5da8f5aad6623067a94b57183299164836d318965a8278808430748d43004255524d991c970f0811fc05c644044de41b762814568162145e2c85098e94ca2485ad10ebd7271522d68b531449430798583aeb25ddd206235f3b2d453425134ca62e2d244bc2e5acce06b500d0be1a14780c449c034410117dbb8218975055cbc31d16dd9c60f9973b9a2ba9593cb5b8ffc399c81e2ab8fa3afdbcc6846073dce24a1af4c61a60b499c4bb512baa25167223699b2254730961d6ab1da10ad8826efbda40991440f312f694514e1bf9734218858cd59f2016fdba684154f7801a5053b28b047922cb1c52049124c8bf848e004de36ea811f8024b020a02d5104a8512bda123b2cb123842488604aa9ecb408228a10302256312dd02d2ad4bf5efffa4b5297d94e7e10f2e8d159733ed193e4abcbd8d06a50f295851a821ce9e4abe33b89a86b643d1416754dec54ec428b3674e9ba58612250159da94a51b69eaead99587ab8526aa15aed05b1aed56ba591814f4a5949416e5d415494d6f33237adb56eb3b5b57a9d1c9138961ef4749c377c65b994e5a6b3c4a413e413c0887e6be5642bbf1fd1d3e5a8a2f26b94b77b3b95177e83a88bbddfa09776ce28829eae4aa154be5a71ec30a77857d65a6bb7cd5a6badb5d6da6db3d65a6b375aadb5379e24f1deda1d74b389adb5d65a5b023dbd5a6cadb5a1ddb69ced0564d5594b6ed1a433c8360c6a11d55e1e49ef62e47532d456926c3f241de9d3e58d277a7a35424f0f298a051de75ae498146cb55abc12434faf6f6f10e9e9d686d9621d536bdd6ab5f55bb026d0d3b537637c6252a0a71c8d70d0d5eebdf7deadd67aef95b5eed3dcc8fb54463737371955024f2bd807cb5cb0ac5e4c64fae0237888eae3444fc73f2ea752c9db6e42aff0055658eebdb3daf4e9aec0322c1b45f15864ad850424f8516bf78115cb8caa93681934ae0dadac0607f4bca34e46ad7546b5d3e7d22082e90db10c89de4dc8ae6602e3c75a5b6bb5d6d65aadb5b5d6206b6dad15ab489f6619c566ac002904e995d2ca744ab1e68e8422a104b1f505fada7c0b27eca3d4664f1627f4d882522d2c2d9935e56479e1cdca1732fc5e0c56c7b5ee16e819443448ab199128e813410b6b5794d632b64ea26984b67ab947073d3dac413896645b6bc3a01a33a8bee691da53a7b3270444417af610cd9e201c8b49a2a7d71f96111e6aadb549b40995f248a595110ba3142aad3e15d084a2e38e71077ee52c6b412fdc40dcf0e441b1cc3b4b2b2cd95a1d595b3cd6ddec66a9abce3bbbe2e62b8a36ee0a71dc09f488817e46b65992468081bc7ab305416669f1caf25975123d83628c5fa881839e343c4ab2f34ef7a4f796be369e0a134e124f7dd151166d3d4f638ee8224bb4f16c3c4f6d7885648b7a0c0af448c23611a529f1b5dbf542cf6ba7b51b90ccb1453788685aa21b5d4c496b2f32aa2882f8a921081f140e7af59216844cf501ad1a43a5862e4e33fdbda419114ad960040a941346904049a1512f694680d842ab4cc141a778a032a24959a828a14935930e3c4d20e139e2084f911df4e9258d67277584d62f6945c230d9a0f34b5a1128a92134e925ad080e4e5068d34b5a11295241d0f625ad48142a30cdbda4159915010222086f917fde04ea5975895a3b279e78de694bb84a6bb5c0a244d2b1762b7da47c6d8d173a3bf51a229d7ddbb6c69b639cf3e69174b0776f474b5c68ec5b69486387e162eb94c887c607dafa68899efa6671688946249d5bc3fca3da81aa5e2754a4524a2efe0ed5cac55b5aabb5d75dfc550d22a93073562578154ff40cc7eb040516e9001d4f8509a2abaee7a9d7201adff50ca1a78f34fc942d6a492898fefa9d628aa73e6b9d5a48e260a7ae876a78ea1a2671ae834d60f1d43f23e4cb3424f4f5481c7bdaa1168be20473224a13454c3c35117d479c783a6ab704dac18828a71b5a22d39b86eaab0ea93c98aa115335c2548f3855264e15e874b2618dd99b5d3a952670528ab512a1af63120aa6af1dcf5f0d93adeba415eac310437e2a5bd8b98a81b78e69e8e89743bcf1f840fdf51523cc24cee64dfcbdf7c4f3f77a1d225f7aa8c2dc98c4c17e77008a3101c28e88624411d1df1b7ac744134fb9213c5c6c8727516a38faa1b2657a3d545fb90a09431d0ba22d74d1932f60358b117bcac176789a6b348ab05556d9ba6eebbc2e554f0a05336fd84485be2f693ef0fc30e4071f32c0a40722fcaa7b49eb01ca0856da730f4e62767a3872f3d83f960f0f44243dbb19174a8ca10329b0c0c17e20075dc85e7870e2ed09a364f2980256c8a005444a04f520075b231e67f1c4e3d4638c317652e347d40e4fb0903df1968722defa88a4633d2928b5f1a3f798e8b193c73bd074b07705ad03481e7b56e8d083c78e7dab81623c85931f3c96d425a9f8d1a3e930440eb4b77e8160b1cde247d4631c36c0f2128a28441ebbee798c83111ec6075fa462f80630784d4cd2b9d972808a0f07adb58a75c316f74494baa10bcf24460d7543104620cfe90629273d849929b683572202181362f48151a443403a418c588909d03b41cacff7c4d70d9168ada323bd2a825011863c7d831117c6acdb410e026c140b02691b43c6c5a67c589675b89708b3ad8795fd42084f244cb3ca1c46940a335d49a62744b1b48e4e0e8c1cc020a2296d08151d1da230f23721105118f963d640cc346a755fd27e88c10ca1958c0a133ffcbcaba6d0345ed27ee00145d3fe9246e40b0fe8192f6944ac503142afbca4115141101a7c49237284ca0bf4f69246e403261d689597342238a8683e4881424739c187229a0f468c51c4db97341f88b0eed2445e4c0ce611d0e7fa084889ad2f3da4da42baba1ea468ddbebc75d51051d1dbee072ede8eaa216f5d159338d46d5774a4a794851ef510066354d4445b874145ebdcd651a8683d6f2d6583b00dee58d7439a0b2a5a900815ad9d13e40189c404e91194813250f67604c11fea1a3508545f930445600f75491fda5b0795a02e4abba109135008a4bd0da2faa8540d9755390a99a11100000014004315000020100c080543c15096258aaed90314800b76904c745a9c4ac3518ce3308aa21431c6106208210018446466868825013824cd887d3a488ce1d8aa9a05ef48260268019a05632603e8cbfe1872aba73e1d9457ec2903174da4f1dfea75ddd595e045f47008ba639b2870b30d4bcb179572e2cbeacad3695cf6219b58c5061caf25e8a5a4f2753568b455e45a9751d2ceaa224a12205747a39bc435735e0ac7793aa065d1c91fe7cd6da324da72a824943ee5dc0229071346821a4c483e1acbb91073d738313a28b72fbab0afa0852f8a392238448097a346b85d994db43526d4468f891bd698449668265141cdab1226752f5edca1cb9454de0f77323c902d3cd2a8906e180746e306e3bc96bc5f974857185e89bb45d10ca0fd03585c552f9d51efbf2722dce6c8cec469e83678ffc13a649bb537da0ff0a0a76d65f61369ee0da8c4fe04c216ae60a74db00e2637bdafec4247925eb4c8e585505c51734c7332a62132f5e434c9b87b578936976f23aaaecafc0d2c642943de99a5068f799e73e5bceecc1ce1a7616e886c1981a1f15b74483cb575e3ffd72986e5bc2ba8f850c2543237ac2f025df979055d98c5f45f08f6874f498b5ce8bd8eb40dfdd27caa2cf84cb461f64e1945854a8e0e60e389c13d8cf766e35507e2d2ac772dd4844fed77194ca6ce27942e1ea7aa9bd36e2c034ce0fc812853f546817736108f0de26042c196bdc6b574a5ab34588f6512c1cdfbb0dc762c0f25b4eb9f0520faaeaa579543fd749204d3ba2fe8db9f239bf0a032b83b95cd1b16ed5913944c577a20ea66c3aeaaeaae5d69bada6a5c884e5703eef3352c82a258e8c67b33f414dc9adb63a991cefbc05ccfb10af63ce9bc8521a4b2a734a039e8209f4d5cfa884473f694e14c3296e36e5c46d4f6fca3eeebcfe8d0cf1ec65459b00f49d85b3c2d50990bde83a70ddf86f601dcea1932db7e78b81a86902c47ca70a045e45ccb6ba2264c7a413b6af9218fa2b0b96ad0e98765aefab1904134201ab2fb0cdc0a7618dfb12de5affc2666889dd32f432f93e1b81b17583c4a5b70506843b7c27e505c73c323c35b602f412fc337012be82d98f0c8296f9f13e4a56c8fcf4081604571ee54bd23b3110034b65193ff6be0c9068c4ff75e495bd18eb9712f801acc9895eb780dfb67bb42f1d3490ed677791cd66298f3ed0f0ff1bf7f6e900c55f4f66a6798a3554f6b6694c7c173205720f0026fabafb279efb93cee194bc9f30165377c18964fa50d7bf709aa289a5a579c297ce3c0831e461bf240fdc04f0a1f755aac80d7506fccd7312bc549a18a0ea53059a81d70ef8a0c58ce3b7a3e80c854bf1afbc62847e3ea2220e5f6b08d7f8bff597c38c567e99c390d9cf74795d1d42803324d3c0261fd512e68a0ed0f399c60b7f09a6836aa6dd64be274a494ac0e7dfcbabb74685987997ab1056460d0ad6b49237f9c2ff21566e83e955f8e2aa0a4f4d956dc87187c5380e2652ba3c71388aa33fc3c304cd525fa958f13256bf8c7e8f1adef37004b2861b401be602f5c884027913655c42c09239c12273026366c03bc6f282e836ed4df60eb7d0145fac3bf8cc7940d4b9af4ad679bbdf703c9c770b36ef0aa84d1ce7d73684874f127af9c36e41b82320a8ac0669625c726ad42bb630124482211b5b951bc7af1607f5c876a78fb81316933fc60c384fa6cd708287911ea8d4e1541d209d954837868289a2208e6fb4970bcc13a5912d16fa34e1e3de292e175f30f2483c7ed0eeeca24dbf758e9c42f134030697a203ecadc552c2603f91b8d87eb40e9df15164024989164b6b409e125ecdf15e544523f7d4e044b15b252fb69254172b4957cbe9edf298ce9b66c0ce74a58923a7745adf951a08b1192ccd0c2be97cafc56ea0b91e84196423b209be0e716659b901b086175455b8ec4adfbbe2e8471d5e602cf5280d4fb2c67af5a9d6f22ead438830f7873d13503d9b58997e39a383ef6f524452dde549a97016947f5269fd29a8a563b3700850a3f5d8cd9987a0ebee3c94a21a4ef25a7655b601a1b951af45ffd6cffdec4f9571e28e04b5966ae1602cc448452e4fd7fea82f72f29ae42ccc03fff2a71a51c2f1183a9ec4f8ed3d5c6d36dc26c95ef3af884dea22ae758f6beed0b0593a8a05c97ef3ca19600047b71795e88a81d59e8ef52e4306e9acc04d5cbb8024bc1eab3c677ee20b0202b9e4e1bd6c73ae4639535ea72e6ceb7a947a348f2aeb36ab0d52382744d19198d4cdba874785c478463f5ac403e05d995a974f9844de0e4ffc9dfe13157d2add2442696d5c0d04a1375b966d951f1f7031fa783acbbd840773e843a0ff778401a0055d52af60e24511fd3b0af8bf478d2bdd82862595e2a83c3bc95e3233b814a9dfb5855150f4e24a8ddb60b5282134ff82ed4119c3c253a33ebeae42de8c30313319572a77afd58c346175461c48a417ffb6e09e3ca8571a7aa234317e2eaf7bdd972413ad2c76b64ca5bd8cf91e5a6b16f2291526bbfb211d2d870a00b888d7d17ba3102c0f4981b920e7021b408b3a6b290cc8a9cd468f885efa604c3a9050fc8cd8c5126dc5f11034ddb121492a19f8bb88ae2dc2d96571de348082a9604ac3cc76e1019f6b9d43c588bc7b01dd1cd15819ffe000bcf1631f9df3a7676f325ed2ac8fe85ee410b4c946f861faacda34828640adce391e6136d81d06f88f8b08064bd062c54552ccd7c6fde6ea1902c81150bf21c89d6e222a145fbe3f468a66f4c4e94d343ca609aae4519780221f1ce493953a8fd862a89f10b1bccac2ddfbecdffe1969a7652fe508285fb522637a49bef198ebf8eb66d2b211321c6620ecff184d54dd29fdb2131e14e66a2e4828fa59cab6ab95c7be80a0e28336d51e91f102168c0a27ba52e13c27c21ab7ec4ba75abee8d0e20ca6f481bb9a6c95ae0db66578653686a1d0183891b691aa4f3a36ede69278fdd596c11ce9d8cd494e01e47e198e789c689bd669f6016ff65d651098ce07693e1a7cd7e833cb6b09f91d6a68f2e6dc0f5be80211efcb6a2f645b2536d8c6cb7064410a980536796ba9cdf97f06ed8b28b2cdb8bf7db2a7e9a11112e7f3eb5e457a5b40a6ce66b634e38c1a46e35cb28409679cfe5a380186b9aa0b5869b28ee1c0b875d791366d3a3fa69512ae328c772f85293da41a41e393458287b5c68bc3f4a9aa929fa8334e19f3bc185567fed911491eeea0f0cfe663f55d58e5c52758ccfcff69959ac896589923114b87bda7dc6135b185edc87e46493d80cd0118be0f0038a0fe9934e047199ee5ab548c0e6159c882579281e33d9d10dd6452b166aaaee693a9eec4a527b69348dd1de1ef11c2e80679a07cfa722486a7a1f5e73a88a769c3107caebc1fc1087e16be94ffe5861ab8a9778cf3e1fab8f24dda207b6ab0f81fe1d1a79715355268b8ad79d32578a29a56b66c407b0f69985ccf6eef1d882a88759201673ce0af2023b33d45ef3f6131fa20bf18ce671082015752f82c4904980f8b36127067c4a7cffafd93cceb1a73a49632ca4bf11530da43f439a3162f9b079a90e0ce4839728a02bc85c80b3f1b70391ad44e9ac9484cef129480e0f7188d9dc9c233e1e72f7d2b6b739f23a2bfdb8345aa380177555d1268592f4dc6c3c267962eaaf219525503a0d53b70f155ed7c9068b5abff8a69ac66e8b45efb24135aa81e00416bda4abbf2342b7b7deaff147f4c702705c3ef0b96a5637eebb34fcc19da2e2335f99fbc8e92fb76c146b3bde7f51bae9bf19d0e222f8f13f6afbd65dee2790d94eee76f84ef9aee94fc59d955709c07d05b177f9eb412c5d3b99c83d2866efac02a277a3f8091377d5a13f25eefc6493b02710e69725bba1863f449a58969e31eda7dfac34a11481676f1825bc0470870849a67ac3f18bc45fc937c7e29f18023c82baed6401408f0f1588300ebd82fbdc15c94f18bd937425070ba7c0a3328281d2f020bfeb33015e7f414ff516e2a757202cc86a8a621602696c721774dedfdc945ebc0138918bc6b9fd5e5b8bfef1ca44fdc2d6420e1520aa2b9133cf340c9b9fe8a9d023961ead4666a662fd9f118ba81d5d7214bcf4c2224b7b32bd5941da1f46405efc84db0ebdf95af20b67be6cf5d448351375a5f6e30791b9b7e0317ca1e8fa11f2ac3040d399be5920a13b6e4f4c21d836d78f236921e8a2f4d8696c9f9f4099cf310fa694704f1ad78bb8abd31c01126490f10e7022f50c32e53e104931644f83582e33e4ac94c83ad3b2ad9862b4886526224838c80b906013818a2592c1c00735d62cb7a37b4c9630831b4b17274d64962bd1250a61fd0cb44eba6125c925cf5d6e79cb3b8915b47f14b91c73c576fbb38b626f147e58e7bf90500c9f5b5cab1c31ade839363c0596f603b32318d55ae88e31c3d4ea7c10796552a26b2fcb47e482012bb33575e3149fe7fe7082e2eec46cd338181b6f5ec557c705ddbaa65ca535d974ea8751f4e3a9fd3527a1f4e67172de2f9a2fc242c39698fe6a71e8f8d575c65a88bfbd0bdba39a48a978204dc496442dfc8910bbe3c2c39452a6b25a35eb1247ab1e9e2b94f1fee746c31df94ab0d6d2117986a2b833ab710855e55557735816b205ac77e5491fcd7ea6c02d75f56331f0b508c0af534df0ad542a9731e79d61efd1abece0ef8acae47669ba8b69a7faf684c3d737814a54219f1b44403001778d08877549b2b8028baf18f7878cf1fdec056b88049c6765c15b8d5418dc6ba600970358b4063a30dfbcd07c63591720af58f0a3b726c5ee3a0664cda590e7d2840d769e2b5372c97f7bee4a037947ee17e798aa3c72e04725464a6ff3ac8022e780cb08a20bef830babd669c94ef8f299e1e07019256366bf6b06413902ba0c0a25800538bbb1027687e9e3413a7ec347d30dd4ef207e82c3b7b593578312248beaabb99078f39ce14b07dcbe04f607533d4bf1bf807644e4a16ce1920873a67f71952af7f849c072e43092c3bf175d6192f19f1d5a2748e6c70e62e6b265a167f2272890a9476c0d194d0cfa4e809848b870164d9f2df0a7ae974d74b57f2b55a087eac569df4e81429345f9a299d92fb0d3b6968af57039d94ff2d3f8f1730abda0433c88f084accc56e739ac44c69f189f27160e653827546902279574540d831c8bd02873d20a950d20a1475bd06bf4c7fb802b88f573265047a9644f22a34c70dc52cc271fee2f5b30afabea8e025b48f7e01241c69be698871c2402b0186f8fe3f5cd733876efd9df7428bc4d0639f642063412c8a627ba37e61f8d3413b1ba233a04cb67b36db5f4e40813a18610e05bc7d4f03bdbd1cc100369e8a92a3a37dc93189bea806d46b5512c8291d41dd0ab6aaf5838e5509bae0a72e410f424de15a5febd4ecdfad4c2c7467a7adb8167d42cd0bfca47dbb75b47fe284f8e98d530f1509185fb49cae76a29640936242ddb1fc770bf2f264ebf475e75013b27555ed29c5193e5aa9f289394e9336a93e5be456f5c63289e1af4ca063a5de4be0a6dd91118a717ac1b8b4761d2170d600d3911d3619c8216729e503564d2d7ea17791896f46cd222576a379e0ec42aee2bddadc0ff9952e63867b60935567f4570d935fd84507e2e0825cde7e8dff4bf7df6c94097fe6a65f6ac3592357173520f05ea279f4509f62aeee64b008bbccdb30a7c47bafd2f95807e92de8799f8873d592fa664df8069c60e5d60e5712a8901f10db4d8cd9e63a6fa333af4deb15abde018f2fc21c6f33acdf8833c81bcef20839a1273bde82205fab7a3e4dca6ddf031824d73f7f01585fb891ecad443b7aa1b77f90e1d70a0ae365cfab4945e1171000005dcd78ad768de5539d3cb455625347828db0e6dc1f98614400e1196f3ec7cdae044687e80eb86b2a675c57563bcb3ac55dd6582648cbaa977015d3a078fa75546b9c2d92fbc04ae63cb0652d4dd8dc9b8245e12fa3cfdba31f475682e7e38c6a4c415e7cc9d3740649dc3de0ee63ce0511644f29da36cfb7031c852dd051f584283cf6f8b049612743bbac202cdc6d4c9e5000fb732ab8016f914a1154c1f4aaed248db69c5fc3e9404070bb4e32417879cc12a03e75558ac29ffbe2177f4d0bdb4b08b293a7f6c6ed962943178db2417975599bce10ac8cf442a2a9b7b3ed7265072f5402fcbd6a6de2dfc2337a0139f6ca761baaf57a8a94be9cb75c9dd89513fd9cf546f43f6109130ab928d29c048e5c16839e8351398f1bd86d12c31618947ae1cd9a40b939b05308d72de7d118a37e5d2791a92679b6d9081d825015ee4bf8cba0799c007f68444fcd11e744c2fa161442edaa49c59f4b8c2933332a99fd38c9660d309e04a276f238c9493dcd6770277633a900ad15d2042c13dcc42c7320b6556c6b30e88b7f7ee8cca84386de85413c88be640f94775f21d56027b36177b5b6169df0100235f790c8ee810d618137d2aea7fc6179a6a725d7ca12d3db01ba34bb29e177b50e8567a50f61af3ebca23ca87b95d2f285a18fa3c02515a2c30336a4149d07a7b8de84989f100751d3155f360601b1c25109cf38bf5b6a4a021abb0034f6af08043019865d82c0ef7cf56a5db4f9cf74a6b77cf1a7dfe76b70f162b082e491d820e5425d2141a222a20ed60e9aece2c2d3584547e71d186cb1bf21cff50af8ba5f3dd8b42fe7226cb0276b801125f25ce999ae3a38491228f5b99f79540bb011052ffee982ba792652eb482bc4dd9f0d80e857de39620da56d1d54234819e0cb3e3f6bea4f4c10daecf9d12e4740566859e5555173d20f2fdbb19988ac6413f129856b41200628bd67cca02a469c0c900a1035be2372644918b5999afaea6910aa6229990c96a91e6a856d5c53d6a3f3120373fe215aacb6414a6ef40227860cf1fddb0ae3896892aea3ed1a79480f64116e3b7b25645bf68dba8f41592d08c95db78b7a426f0b0f2cf7f3402a810d5130f5ba1310b9dfb0afd8a7ac86535670377718ccc367b82c8d580915c89e0a0772983df00dc6c804eecfcbebdd9e39640a617a108c49c2902f4fad0f29d0846d871ae98d594f600b2b7d2469ea36625e4968aa87022752134b52e7460b302c03d827af869a5036615c435262585442956a56615a410650c2e99332f9c5cec248961cacf4a37127894080ccb723fa138194130325512db5e1600139abda768568a5bd0a65a3b008e12c80435010484fe97b39c84da686105d1107b664d81dc50a3a03e2abd09f68eb4d8a4a77dc723c527fdb9f647c3d57e51e279597e1b306e3967a19464cfbb6a1b01bac404a2590614d7879a184f26c60a95f471a7c6f7f2fea9bfce08f93396b2f13585fb0502b235d257bdd95cbf3ed281013eb51e220d5f5a4743b6a953c422dcfce8682d39f9622355f47131be1acf51c634f4c1e25c750c5213317392fc0e71ed6538a9fd49af8d414a4d26f21a10750f42bddd1e74f0df8c4504adf7b883ba79ade285240db0d4026f3aa880b15a226acc08b7d109af1e81e73caf4d15fe8bea7abdce4bd5439233d34f69d1dc60245acf4031ec8aece4e00482e5083a860b90b01c81c6e02204cb09744c172471390685d9c5d5bf024094b45eee3546f0f50150fa2233de38753ac0aa211d99f260b48a08c53e9323f4dc93b7a0c42ff580aaf4f173d96c6417d0a9aaf5726e565e0972fc86131f9886b35c896d041029afa5699d06177fb30796236ca62f32aa5d8829d702318d9162fab34a25a865281b573008926ac145b257b20e76aa8c67c1f1d50d50ab952a356480d51662c5d80bc394dbc010f24f110cc43a7f3eaac94109bc42fa0695e5853d14e45e6260bc2f5d09f5d1a81a984167a9b3ed1a226894c9aa4c3124bfb0041e5527a8c662526580ea3cb38b9541ff0469b53bf25bc79f508e163114268f63e146067d27cfbb42efe65d69e259778fa1337cf40724039d57c9e8f13f8bb84ea3b49394d5f1849a549be894737df6fa8a9663b6812c13920cb7415e2974c72ae3ff81502a37c3c184d078e629cf8c001bb0e788c60365403713b8fd6bc98e71203e6e7ab2bb89e1a4286c5508eae757922482f1fa170b58a90fcf564b87e85b51ee1467ccfea595404280ff3d1e4e51869803d914164caa2fd66ecb29087257df7d1ea4c4d75d07d2a337338bfeb333e835c91f5a242179e12206bffc0dfe701a48fbe77568f4e7e36d94ae96db5011ccef54a130609d764ac501438d4bfc2bd1c62ed953764c6c0ce14bd3b7fc4e896b88b25ecdb3d5585edfc1c8eaef6395daf31c092292fcc306daf577ec9a5b2ee6ce1d1fa7462fbb7ee73b5a953eeb2e1775600c1112cb8c6d3e234e43c211e3bbac275979feb28cb6791e7136a694e0c697a2deecb1ec7301e26d222d3fa9c531a60539e61671d5b7f6838cfae22ec16a1b077bc207e0acecfab9212ba2a20bc435a0afe470a735ffcb24419cea202d904e0ff2a93f64eab59bdfca8f25ee1ac12761bbb525f500d0fbb2ad80c5f7f92b8f797dbd33ba7cd924c9de54dca174d389e80e0599492d82e968547ba7ee636d7c58a9a90f147ddda211dacd11033489ff0e0187f035816ea4f5f9c0a0bae4f8438195d395841e2183e5e667b6960d65543c221becd1c714a1dc8462b2e20b32569a9a2dcd57197d024c7187692ba606786008573c91fbb136bc318727599552990c83d187e91747820790bdae2a45987de783f9a16c800171fe8d7745989230efe4a0ceaebf30d7e38e285c8e1a4e305721be263af3d4e73ca7bcd498136fe5c8b8c75c99f07fa72c5c9bbb81ec8ef0ce6e2937e1856f1002b5a6594bf35fc3a693ccb560c51b696269433e1b117343143723f0258a55e4770b57541aa38ad2dba622804e0636518201e24776847d234163178da6eedc3763c66e67120915fba95e94f1655dec3744a2174bae3723b9ab7522c5ac64fe4a763770c92c273bcddd2f9dfd8acfc1286a70f798233ebc7f9b1554bdc1a7aeb41e78802576a81f41766f149896224e4d2209d404a5d07a568691e38dffcd2790e48798df488dee417a912438856eaacc2d448909365bb95084ba78a3264bf69056686427c490b67be0ffef9d7a4410b6bb8546da872a54ec3db823563886ad2870b1121c480c0b7850667dae4baa10ea88cbe5564958757104fb846bb1d1e9eaf77ab36fcaa4c522693882a9b0e73833932d3fc427b81cca021d4364e453604cd8f57e1dc5e98e8f95eb3795e67773ac77e23cc5d8e8f370aa46a490b0621861b2ffe940ed29000934b4c67366edba908448904d29c6b2db2d61e9d8f88d142d0cf40862146884fc0dc086187e3197104c89b0d6e2c6935d085c14566575d752dec1c1355231be0158388a97163d2a197b9c360b07a408d3722abbb9b5c44ccdf045f7892b5544ad93595d3321b5b6ef025bff16748968a676f5fbea0d57f125435d2d46689262e9d9b0e98ae1f34f97ca587145c5a4bf55dc0a8dfaa59d7d4262deaa45e15e9e3ed12140f1cc80db3f2ecd978bce982bebd3284ef52de150fce0136a0c9e393e4b9740502fe92703a32435451bfd9dfb75864742505ac9712656b94289df8d9651fff83bd2bf04332276b71905e0a6fd09ae1042767cd0a37ea8cadc915de814ce7dcbfa7a4ddf122be65bb212f3820f264d5d7d40b3125ccae2c551e21d5c8e28c55bd5db8b9d1a0ca206640bf03e5360c10998e02d20a1d2cfb26975faf78856381d029043199041ef3ce0c852557639300e0e3c764580f9d9d2495423e3975c7c0b0b42a006a511c7336176cc9d5ad00664075d8abd068474a19733e88daf8f2f1c96786e93028354c610bb5d06be669b48bdfac400c6b04272cf4705b765b8b7ef93957aa4702f418a412c93a06c39b15e0270f606a49332449f3391ac72e6628b84ae404d1ca6caa1c1dc70a1763c2195be47071217891263f6d19edcf8b5051e667817d493a92b9ca297cb23e6fb2184d3314129d430bfa9d86b09a567bf348656d0ecfd86bae211a7327626d3f6702a60828f7c0971b83d8e10ea2b310113a21ed19990ee8e3e2a624c302af2fd6f7cc56cf44efc8bcf0ad0c8d790bd2504b94616cc16ea06ad1e3efcee426a5d25274f8e6e81f06bbfc0d2a63673e18925d7fa81ac12222c340286e283f9ba3fa86065ce2b0500bf64cefbaf1f9baabe0b08215f7742525ff0a4d402634ea5f2b1dd8e2cc5124c96eb7056e9841279e298b0314c2fd638b7a52f274c88792f1ddc5331863c98701dc2841c388c8352128d45b90fcbcd67d6032a9f4faaf9a9ab1a094bcc8b74f20c5f7b21d922721c4519d56a1f895a89dd924f036ba810c20cba4c236d085bbf5f6d4bb844f4a8534b5db8f8ce4afaae03a4c9706e43c6245caf1b1e3d4701d6142bcd76ba2042ded8632e8f9afaaa2f413669b57629759e9260705dbaa0bf46627793de21a8af31812816fc3f7328adfc31f09d36635e786bf38f7f4bbf0f66d1b2e6710479f8572c8a323c57169da333ba2466f69b5df214abe46785fd1231390f978a4e2c87440dedb1862cf6eebe2814598a24061175cc13abb68795720d251f0f6be94d8e5cd833627bb524a97d60fd75a1408c5145773293c351437a8a5d6df59670301dfcce1a40c2c59f4c26d205e7622398ef0714d7522ae2489383d08298856348291b4bc949e27543907847ad1ba219364626990e919f57110f03bba99aa228376e288858d29a6724c0940540446b6605d940030681ffc31db2737f28d477abe0ba6628711815c4692187270ca2eaa994144652b578c914f18f673deeb76c97596739be7ff0ab65e96af0fb93500d6eb0a1271adaffb62b69c454e131c8e2875bea876745734d0b752734c78920ed15f2751103d167f18a59f4d5714718c7924d90338a290488a9b001a45e2c1cdd24ea01aa3db15f7209617ffc8103aa91b49db7005c96b179259c13f5f8ec629f7457fcecddd4ae40c715f11dc077f4d800e207aa2da01ff1a99725862bcf7880bc8f0c9c8e08a2a69b50dd58cbe400aecd63ae1ea4248cf88fd3b8c8864cc13552c2b555e02611eebcbc289efcbc1e57e7e11ef4b6dc22696531e6ac755416aafc049df63b35b0eea02ed133a1e099b6088251551f11b6c600527345ab5ca63e172e709de81e3c8ec4638b610fde84caa71973d540ebb492d120c31a991946d5ae02593cc996a6bf8a83b490bfb22453b9cedb0e14e4765b4ab7ab72b7bbceda82ee3ca7100c4b4779c079590992a22260bcb0f9e92e545915fb75594b7240a3bdfee17db6555f52a78a7cf3b895e3bb8d91135c6b29f58a86c90688ea78b95a065645dc6cc0ea80aebf4ea962b1967fde245d0e64e1f7172b80aa0a64aff09293dbe751205af7b477bad891ac02c27747b40a4ac35953249ff5de9747203536c2be1d032dbbcef7b993231bffd208e9e2a10b355fafc01c94772659d60bc9589445d8dcfb0cc0f2941a0b00434c2d59cdd3681f4cfca9201f30b4252e8fd296468541a7442bfc922ddeb9908b87d408b6d3ce2cead6430ea9d7178ac973c577d55a5376d11eab396e82854821e68b6600bb990686ecc0738fd53c57e1668e8bdc890ae981763d08ac47713ce158d67d8d0cf149a3c5f4031c9e134affdc0f2beb4645931bc5970000071ab8f13f8bd1ce75b6ce14451e408b31d381b382b5611d565c78f781089affcb6db60877e96d98b15e0878e25b106ad3c92aea498c8cc1c701dc3f94107e5ec5ccadc02d9dcc897d851e87e82d0aec2323d4451d467ddba793e0873dd08151783a1a54b5c64bca1cfc74e6e85e9fecb33ff039af99ffa3dabb7dc1e835e067d48146f481f204d3e3cbedd78f995c7ca41f96ca9250620ad4eb4149b3ec8e72525be81697f48f4aecbf4d83104a757c07377ad2dd9bbc6692cd0b34b40e0fb4c9fd376879bba779c594b8001a13b4ba3760e9899f88aa308d87940f1208e4b7494b7a6e4bac0a8dea3ddd420b8cc887476d260e1a07ba7acc6a0b7df3bbda6b8e92faf35d317305bb6439254f56e6e081463d9e8c35da51d34a39c7c9c90018f1d7a190a176e05f23ec66bdc2f1c4d8a053598d7fbc753a820187d9ea7412c2e1f389b140ab7a35f679cb7470011e773d99c507d0d64ee1f0d0a4de167144664fc9631b348e9d5811fefbc026af335c19cdb281c6fd9970427669ba88e891d31ee21e731e117f4d0c7ce4706aa97da03004b4f8138187c15fdc8cbbc1288296fbc08d03224de71a058bd59787b1c195547ec9714a8e59f2b98a6eb6f7bf4835a206f4dca676c072e125adcb9df871574d2bdbf35375af01f522730cee385eb9b8091d3452fa9c4ec443ddbd1297cc3a8bc1ea4aa14868026a0224663717a6c48d7f7c3a03cd280f196b9824f722ee4cb80998d5ad173c3d595bbb69e181e6bc4431a67805eb614c7e58182d90f50910fd56dc503a07429fdaced088a45aa3bbf9826e5bad7ab2ee9d5aa9c830c46b5f5621a8fb4c8ead9b1151da8d5530e23338e95c5c0fbecddad42a81a37287a53365a042cfbe52450adc09c44d5716c50dc2c40990af7f90818829e392042ec2b1a3f5328e27d4ffeeb71c47acc272d5a25fbd646354ecec45ee0f069525e0722967e2ae655820bf5615333e0812969a4c1ff2a98e572232395745efe73ba488108c336f328b1888c38cb430d30a11bfac5b451e8b4d12593a8fa240b5ac378be3c11c47a31af0c99ee3c4f22c63ed95d072a90b1a4d3e780774049dda26ccaf23557d962c5e44cbbfca849b55d48022879dab12c0b97916b2b4f548f9091ce4dc8f4cd4b0594409e14a26b1dcc2370a2f39c141f558a76d0ff2a8e7972707075cfd723383bb899cc072dcbf2b1da5f3ff9bb89441d05bb97407791220510a887d32264601a9ca9271b927080f4f7f2aa7b52a9355e0b5ec494e7472fe91d51c9e7656e15c4ba978b0e6e1bd0efe77e6fa2f8ad992a2c0e0d6852b99ddb40684117a24bc9b8485ecc39759a655ab7a5e81fd63daf29735fb23829205d76f45816ebc0fd7bf8929f44d9c1ca7b17b73c088d965564fb5f73839ce109d3f65a6d2c7f0fdcb4995223f8cbf64fe4fd23c0140a7d5163046cb83b2cf1edfbfddfc51a4a70d1beab44c107366f7eb0700daa2c5a1170de4339e31ee1a07d5c72e02ec80ff58e325a3ea7dc6d2bb743546e5f4a6171db79409908bc8fb8a8fd12d0e59798b563790ae1afa0b4ca7ea604d6b4b8b8ea1642641f0e06b8193bffc9bea1c2f0086a886e185e50dec8f9b1046b8940884570e4eb4f2cf00d03ac4049650882daa7a45de3e57acf0aa02808593335807db56180c3d484069d6b5ce027cbb75fc3588f80262543702ae4611587e12b05d435638dcac0dbf0d85848cc45081f8770e982e51d73a07d7b5308e3aaad86b2164f4f54e76784769eeb4acbcf136563bfcd3ffbd8decf0980d11ef083a1ce393d56a1c81703b819062c1284368c063d8d7378532f5ada88e00e2873010ec8d9fbdcd2ad37b215f79dbe005b57b78d60463b6279a0f66e283d5941808c1508bb3e078b24c314084ff59f086248dbf60ba1a3938a95574e69fdaad07a6535d0afde7b44dd5bf1abb4c81797f5189b1540f44b2e6957b968c7f5c55e25edb4a1eff1fef5995035196e786a99e720ac3a710bc3aab2ae444d08999006c89e9c466703ef7fde75cb981b7ca899660bebad881bb4eb3cbdedb7b4e1607e3675b234157751843298b93f3d7a52066f845633b89000c0d281dba52773427bbd10e0049625403d01310759919c6a9e57dc4d7244e932849155ed8642fc7c3f2f02c235708ad1d1824c32508910822826297bea11a64a932b24b75e64c91ad9560e252b2f7ccb86b46171a29dab05af0a8e2c534aa564018dae6b8742d96fb6d04b9fdf09dc42b90ff4beb09136df17345f373f3e954b2f1a636e46dd7fcc462d60e46f643eae9424a96d3b5acf57089f001ab580a8e64f4851ce6a990d4806f453231ce32b9e2be12edcb8a907118ff3a530e544a0e0d51454d20361618a8d2ffbeebb90d35406f797b86be02cdf966e8c210abdab1f7ec61771643ff31c2a6c392721fd8cf9a4caedea889a7ac0f88152d1b0d8ac955a6554513ac537d268370058f237ee58b2aa83e82133ac04356398dc64ed0548e239f2c8a28538ad93f30446651a20302eeae162ec2cc89931132f7619d6a577443e9f7ffbd248fbcaa041584dfdc877e1bf7ff2b6d64305c4f050b49eea2fe66c0c92e8ee3bdd1fe70f07afa9d91a39bf844b44d53c4f57fcb7800e144f88eebfc13fd230d4cf255822e0fee4338cfd6592164a4ff5ea4a706e7e0866e5a013130e30841fc705ee28334d6628f5e1768612b0b66e546dd5a1ea6da6852ecd17272d88de3483b5746dd292ac28796bfc0b3cbe39d86bae95daa47edb75a499e8930cf1b646b4968a2ecc298539568d581ad45aff5656d47096b750020a6498d2c401b933e0059ade4165a1ae18439c475958b714acdbe4c7b989ae5e179c2a62f2e3d42948cc35359e4bc5c3feaace86b5a2bce523c7e57b77e1b8a047c863e5bec00561c9e6cfa12e449e16aa51077a3e16e0122af82f9e62f095636768e168fe2f15e8092d1dab66befa31d0374864b474c4b6fa1ec64cba5e526f5e6222ea0fa68bf532da85f106bf6947d609c985991976c2837d2f031689fc47898eacbba86eab6fe8ccf84e98d664016fca696a81e7e5d5f46ffb30145ba30117b82a0b0aff4ca53cc0bd936fafd99a28ea1ffab005472881b852aee6475cfbaa69a9e0c5eaab80e94a1e380849d3993c12c39403d6ed63bb452c577b826fc562674307227d7376babdefde6cbc25eec37402990e5810edcdefd7040e01e17403775e1fe5d665d193665f9b3fb188bf0e0821b61f05eb7198615eeb21cf2b642b6f97cefbc1fc731a5bcbce91e2258f2d815f5d3a4ddc0971eadac28451c0247f5af320b0fac0de05b53c0484f135cc18683b729cf77b887bd86bb7d3accde0615c70204e5d37797b40187763340cc4c2f65afdac640fa6cb5faeb3da0069f19d955fd42cf5b991f030e1ad9bfd446ad543e905a08a1e543e81abe4c60ebc326b1a4ff6cb8ff9cbe3c78d0e2c9a78a6d9a9ee58571f69fc79a2b638104a9c379f7092a01fe785f970163029edfd2b01d549367ffff0371c79b648818c445941edc7c8ce26e87ce870f418ee4873e24cd48e638c6c6ade0649c2733ec480b201f0e4a25cbe846a99c62477b76f568e0d41771ae7870cfa3049cfe8e2695273a12344e2432296bcab77553110fae3a3baa1ef865d428afca18cbab3d9077423a5a504d80ff3d6add1053817aeb4ca455763aaa292b8f829f4abaa31dfdf46b418177fea2ba5b1bcd598d67b9026aa51f57e0f24e450ca7c615cc6ad2bd3caec0e5ddcc1af534850da76674495f053c1d8e547c4c3c3dc06acc68f19d5fb9c9cbbc4c9de245a9a9225b3bb0acd8751c1b34deb03f76d1daa31651e5c0da69d3d9688426174de54f0dad156066c58eb6666a3b95adf8dcc39648569b2745232634b434ceaba8514e2de9614bebd9b6869ccd05eb47ba425900dc4f207699159367904830bb2519e213d2d39e7d7b711d8d2ca0261dbbacda182f3c26982b94f17329da4ec4d777135fc058b17178a62e6247b79a0292987bcfc344307cc90dee5015557499874042b77525d31ee7540dcb7af895836d1e9ce6274f61b170713e4a5ef02658df9da0888654299a8f0d98b8f309b1635d11937eda385f61c5c4d33f3bb03e7379fbd285f2cdfdccff48b3266d59cd44674566bb70fa3730759f431e47849acfdb10bce5bc9bc6bc69604fefc2259a5914276830ededed172a0ef8cd5aae536842f46050fc43109efdf3f53c0798d98e21e4ac54d9ba36bcf48ca5a5c52ca255685c81c5f74b914a5a1a2ce1b129dd670a6f08d31edf3d52ca97a31ed58ed8e538a7af305f240fa7133fb48d9f721d69fdcd92f7f61ea424ca916f86fff172e68651fbad8fe59e47eac6750673b3351dd8010eca5bfa1541a5a5bd178e063d9229b553a164a840380fa35aa473cce71fa1e13047a91adab6ae43a48851b550235835efd01ea69e5ace4cf950b8cfbf0aa41fbcb28def5051a24c7049a86568c52d48cb4cf9464c259b691cc091f0ad01dc7e7463899f74c092ac2be50586223ca0dc77863d59b0e6dada709ca4573ce1e4d9de6a3dad427c7bbe7e59fbfa17937371e89b2b0c6c1213799f880eb95207cba6d61a07a9022e29fedf635b2992ab7e505a105e80dc27a0fec13747005be44aa2d161141f59ba4b68dce3c7da9555afe4e64d5b255a05ed375c46b68ae5dfe8c85e24a53e87a684e675a8b85019e8238be0a8ba2511efad911d47f512feadb36c5759a2edec55ec8f49a8e73a38d1e26a73716360d57fa44d8a8c8db7126ed53e6a8a95bacbc0d71650039af6f3aaecf0294f836d53d311d50387814dd2be81faf7652eabeaa04e4286084548e47fe5b1eab9ffa0ba66fe32a26649c5dbae48570a0642b6038410b60f66cc867b72484c32869228a6544aa21049b0d18596c2c3e2357e1aa5de944a137405b0f25765451cdf72c98495846884a3e1a4d8888078bb94daf333c6e9600210aaae5ba6f45eb9076ce7f4c93b8ba5188b7c368f230885be71ec95813a34b0fc6e1f79c5cacc0aeada2fb65e031709941e1b3df90bbe34dea1a2890cf0a760dbc7d92b361a70d2bfbd342f116fdb62c3f1fbd1c7071a393778c45d75ea92b278514c0b8c30af3c1399d7ab2c991b7f067b1a4b2d8ab57ad77a13820c8a59493ac877ff1bce95d5a9e7d49bf0b4e10ec61c67b4d0ab4acfa0ff347e9101de907cb23ba8c18bc8565cb1c914f9c44e35e8da2534226dd00b74a183326850403348132f88dde019b6253a623dc93b609222e6a4035aac9588f94f7a426acda425841266a94eeccc3cc58f3f9b3d438a2967adf4717020ce9b2c44608a530d0aa43e223d6683396a2cee54818b247c5e6c6c6080d131291654962a09b1e7d1f344deee4e3d16269e741dc80890bc0db3cee47f793cf85a87a7a8ea3c9832921547d60e5aac08a9de25880834fef62d0b854b2a23d6608a784bf4687ff10dc4fe281496519344b622538a19b8b1cfe48aec935f2604c1a0f057b284c16570f6753b19dae040e6bf34fc7fa7578cccee94107c09b3c22a9a083e9e670466b4d75f1c1644b6a31f7a7adbdec6a5528ce4f2ece89bb40c6d5a1692f058162b26311ecb73a2429c4ace72497c32a70eb5aee62034270fb2f47427b98ce925dc145c4a03271f29723d63d7f16c91bde029af86a4e9245d0488dc1b35f019b54d72d9998133967398fce682d333395dde680cc03b7f0026ef0d2b70eb3d30240f249b6a953b62931e80f56272a4a260865b1e1d8716ed4a2edc2256e7e906f777bd1c47a46cbd5a9bf973fb599c6c138747a4ae9d83d300d55d86cccc17a626aa45a4840591a0f589c249a8ff7ff459207c60c8e2ff67b26878f0a3f119e58e3979b305cf58e03880a4e0a427412713008b883a0b70ad4e1c07c15a65880b470f36feb57270db751cabc3545baeb7cb0a0b2dab24a22837f142ec12765a6ef8dc0cce062e6fd6b60eee612b368b25fecea071dc312716db327372702ebc73caea2a4e5bfa40a7123544539b23a2c41bbbcfd2059eada5f11221a0b01a5ab0390a205c08eea6f660bd05722604762ca20405470df0bc38ff43f206657a6d4265c249e7adbcf7caa0876fea413e40caece753f56ee67f7815b2afc1e2bb428c977b1bdd7361b30b032f0c4cdbcec2f63b001af6fd392cf464302f3efbc5f723579b1cbb86ca8b35a0129e876c08fee68ef03a6ea5666eb8993748610f4896ae3b73126ac7e7c293af490bf3341df880504da1b7cfe92810551c40e4bcc452d092c294ad01e4479379357a634344087a7824ad180803ecfefa57ee4ccf8b531cac7aa4c41de71cda659051b3cfca1e1a2acac9625521b09e2fe26c93d1cf3c48a48cfa8eb8e510b7675ae51d48d69b77265e396c2bfc2f97b4a98af578e6c17636892a3230222175aa9d483cfd6882a8a6e638fdbd4b480c9e78d6324fed0d268c1b785fe935788bd1b37669db2577e072ac13e5cc30fad9da3e7d690847ff6e48c1a18c0344d692dfc7d8df76c859ee63851e3c3d97ed861767dc16e89808b615e3d13760af29d1970b51e500ba9602d88a0938eb8e0ddca8ff6bb8d3456855a414badb80aa9564fac52be7f8379af419989423f6a9e4e948017507e1c0f5fdfe97ea7c5642d3e03a5c8745d4863c1ee00ed18d297a09f5ebe341b4264c6494aedea0b2c5349fcc6b61059df61b7f36b88c143c058f8baed19f648d64ea461b044a993b274913b7082445c1a6da2faef7c289600efc6c4d4ee7d2c2670d47c89a7b1362d94a1308b4c8403e28f902843f490ba547b3bbc376cf952193e231d120ad92cdded04161719b3aa1869bd22c4c3755e97c856911632d009d9c61ca0f04bc72982c31e5f6285dbc10e5971b43cef069b946190a8044fafbfe944044e01caae2ee7a4a2f8478e183d20f738d4929eccc22145b802e47f8e4b2ef866db64fc53feb05341fed9c18d666609bb5be730b8a72c28bc9d4ac91a58f2b55829b92819b7659b624a69d8ef4da16623220f5e695a64df258c413356a7f4c8c12563d4cfbf78946274001bd7c2444b6870fd7c3d3caecbd6305d83157eb43d7ae6328aba63ec4b9cf839e222144819b05710859997b37351370a813ef61025bb55f2b1799f8c7bb9753fa0161621f81e4ed4544ae0ccf3b52201728ee9961049adfc32d0296d8cc4d47af29ea9311a005a0ed9e5d39292811034a6172570eaf49d24a635d34032b38809e36096227eb60043676b50e08bfe2d342a7083613685582401cd57aa659f91f35ed7b524d262d548612552659ad8c38d6ca9c1481066e42cf8297261686553fcfce9ac7e1191719d618b23412cbd7b2755a58cdd74411e2649a1b64c39eccd649e23738b7ad5dc3908d2de96aa8264303c83549b58096b80f12ae595d0a89503267d15366d13a1b6474d2d7d26b0aeeb713c5aa1e2992c563e0789f385a3c3d7832be7b9035bd670f6f105c8c259ae6eafb27cccb760c3b512241b09072f50f02de56aeaca1d6e1efd4cac2292af17fcc2c7dffc0afc3fdfb57a98cd5384bd1e0eb60a60236db2729a8fc37ad3044e150cc1277a87327a4ea66a1810ff5a1db52505913fdae08a9026cec84b889b19a937995d33c4a167b77b8eda3a36f4150396d2cb65e5803b47b194a0bb2ffddcef220046f84a2a707c0d6e7a81d8b62c232d8a22b15e8ad1981a98ebd11fbcca3c3144fc90d66280814cc3c4e71093697035429cff647ee956c2a612890bff09edf64b92d93c713e8ec935031f1f7c3e4a31767bd70f25fcae41d5c5ee6638fb858ca88f59a23bb9f7fab04c14cb8933b8d8424e14013facd35d86632a7cc6494193d1e73657f4c3dd1c45236b33ca885c03b8071bc65ea6bff8faca0c358544f56f449cd02b3dc1de0b3505b5a3ac93adc898edea06b3b74918bd2aa9fbf045e836f4b286882c0aa135c6ab8384611b937531bc2a181289a873151cecbd4f36b5cff86126e79c008077f07138af9a28608e3dc44afaeabcf5e582145f5ea03a75a871368ae6e38611ca5749ca7c56efa0e9b323c013e0386c8588512a368b36797c67aa4d1543cac5d6dff9a85958eb14dc10852ab63896ed3b76ceba9bddb43f6b14feeb633a77f3f48aff1a99efc6c4bc50e6cc5d96e1adb76c4e087bcc1354593478760212fff688555085b07431d1d98ed6ccb994827c3a3bf7a11ccc0c7916df7a01c644437b339d0446b85defa82ab354356f4a49b52ce8df30ce223783244cc70950ce14150d6257b1002503c82078a71f9ec2bfb2f57025e82693e1c02460bd52b63239ae2baf156d92c23d601e9577c3da82a98f35b8c9c13a0264084b99f98914eb72d730c5be384809bbc8276674fde5552b04f948cd40ba0e1156dcdde0920596e96f9584188f5b2d0fda9f7dd8481dedacd72698e6196d76852895d16f54619af684c58f0ca7a3bf013d2689f8c332510c3e2dc7d19c4b40c671d7964d2061c67c6f96bc1a2527e3938623b181ac90c64a34a2bc3e116868b3e68cdb6e9ec7cfda1ad4f636c102a17ed25fc2825efd4394260b8231ac7a61d3750d784420c9ab889c269c4593f6a446294b6089d4d412dd830e5abb3d959047a04e9c88f3583e4b14696ab4c1597360c4ce590cc4f3b0404c5017a7574c32495053f02615aea866044a6203477af087d48878fca325ddf181154f53b526433fe7364e290fa5ad50941b358c7b769a49e6cd8f8bf6fe201fc75f62c198e45cfa239e187351cf4e5e1c64b079da78115c70c9cf77761ddcbc91c25c811f7727bec2dd3b31f2445dd28a25bbf69165fb20ab9af0269e639680b770d5fb997dc0ad7a34e3240271d12ede4790124f01858450dfd1128fb931fd6c4057f15f8b4e852677276689fd05ac2de8dc1e65e1aafa770e8a1898ac3aefd3a481130007cd7b732c761a88da87b62bf4ab69cdc390c323ad0c96e605e65c73159f8f3176ca96db233a06a70ac5a23e6e14021ae15b36ecbe17ac25b0cef019c655d87575dcf45ef6e00720b24840dd51876cd885771f827b01a303e2c543e71a08a727c7ff53fec4a51d0df6a45ac8e8117a800d57e6f3a47a380236ff4d02cc4a70db2b2170833c93ce0f35a00c9e2bacc3405b1c3fe4a03bfae6952285982ed61b0447740e305bf9b1a37cf4ae57d186b21857708d80e9350aa1b13952d6ce62bf52106462c9b9348ad254671c8d4d2ab419de0242b7d15360c1f9dcb2e4b61f603320876c74097def958c5167d03a524e01283588ebe799250123df9becbf0b106a7cd4bb89bd1012143efe50b32630eff173c0a1bdbc5456493358e7a17b66e6c45cb2ee14a2c91cce2f0d9681209f4cbc1aabab0bfb16a1277f52ea3c11043199fc5cf10e58c83335db9f8ff8ab198920f4c3a163e764bf3792b8a456386b1c260c8d5cd0cbfc1c5f599321cc4f4145ef1dda05f736dd5c73f082aa9ac03cd94f92ca47c12a438b64df5b80064d0c30e0524e00b63570ea4d32ad5d731305f287850268982f32af1f1d580b76a70b7ca1bc304aac52b63fd5b115e0f332a4bbc4c72b836f835474d9d3ee62c6e75b57cd8ce409b6819f6aa1d8aa72653b3062295d732a8d12ff7d5eeb234d8926e8dbb773f44f2b4906890507f9714ccbd31249401d30a6a1d5fbb33af8a66f92c7ad0ef71a247eac854bad05020578a7f109d5db38205c7b5e139c01fc5b7062462e520b5870ac4c5476dd59e31eb6a21484c513fc7be35c2f40dd220da1c1addbda0412c985828bcab2ce72ae8d03294b49f7ea4315f771d61769110b52761b8961b8905dd77919f61a7048aad67d7dcbaf7b51c68c706e501fd47c277827d35650b796138401d717b1787f4226f3c8092a5173baa1f0db353168ae08f894b64d12b6b23be567c3aeca06512f4389d3e08a8db662b6bb4c4a2cc407a81bea19e1bd37e44be156f23fe64d7d0101750cda517eeb15053588a8fcfadad8faff65570b2192dada52019280afd2c3693f814700d5393670b98ee980302f506ea9c660e6b5e675636c89bda6ca7dff3326f522b72e408416c5541ebb13021a641dbefe9616f33ec7a9870ff33060c64f38453ba470cb70181e49a0d75ad8a71db3f160903f410668f9e019c18541ee47c20002f6b7187e3f852075f1b65ceab9ab020cd27b275b997c140aee9330bc750b1aa48a4c166475b80adf109f547802e98384f32719f79c30a88ebeec27ab0a12cb549fa8a7731570d756692e97f665d8483e2e587cc102def0fe4e910e19c85885d26f3049b4b4d0f8646bfeca4b6487c922af310387cf0358601d863a5cb782272d864932dcd552768c64894a229975fa3b0af0ec655ef0b5ae65fdc5f7f84482e2ffede79e3078c23db30cc442da9548231fecf051c7a0659a3cea83dfbb0d8d7bf858b6e35187f2a74032e241b883adb1a3c04e6e5c67ab79caaa5d94f0a3948098458db087dc68eb950285ed08f2f4e54bc71c5ea74015449b19591d62d71665539281aba8e1a84bbd289c5477408d1122a9bb84cb36363d091512efa5956e35bc63bc5df43cf22404958d90aa2c2fea36f1e6ef0cdc82128eaf07ca79029dc0585719319cf77b8ac31fb823d233a4e07a9fd0521053ab78181ad4b786ae4c59f062c10497b28e5d83a3bf45bc8cf3c63dee70f1679bf88d543fd24ba8b015cb12532f146b488d93958b66b327740924aaaecf18eeed13b14853e0eabcf347dc8b4c07e43e3413c0ad7b7fefb484a69c195964933865fd6f485be71f209734f35b4d237ba2049aef2291a3b1f73e2a3956a77b6570abdbf1a85371dcac455086b35fe3062a5a0aa7a0a0fe36495462650455853f3b4afae57673e308f80ac82d60bc9aa84fd25145cedc1bf49d8c1d1e96a6496777d590380b3f1c1b24a3d7ccd21a02538d87014cb0c15acd5960bb69f5fb4289da56ab04d2e68107427511f73ff66e646acbe4f083291c75a4c1021aabc4fe0b9ee0decf2c24e26a227b5e96fe948a358c79137debe4072277d12f184afe48a0b73970a37fff79bb156a9c42082261d84154dfff41f6b00bccb7d9f70f0b90c8d30d5c91fa30021d8646d5b435fa40b150905424e50181b3caf1f94f7f0fd7e05a3329c2ed5ed093502ada249fc220f32d202f7d4a8daa0b75658c6a2ab42a591b984e3ea24fd4f52e5e2dc3ffece347d39c1e90e842f4f96951c922e9bec7aa719eb926a21738a007e6b00fa2d68df93bf6320fc2a92b5487a679e34bcf2cd8c46deaad6064c1022f1b451a56b9924c8c68cfa7f9057ae530892254169cc8e384a866c4ea3de60e4a4538c3e0cd6eaca0c66f145eb51b45099e1f50b38ed2273f20867e18dd075eff22f9d95c15f82434dcb0f01f6887a26ba816b1398fd7dc24f980bfb7318f7e73da2400fbf86270d6de08b1d7767878ff9284e3cab50b8859c423006feee1fc18c1eb7f0ba7f22c58d41601cbf2b86db2c9dd6cb0651ae8e2ca1dd98661a9986a347c38360d3e900a9977fda4835cd6b39839ff69a9c60a57e254f3028f765250a1ca3129c4598fa0961b4110145f56a5ec153be969f82912885862ada136f257b346b6a1e26561f46569384bb6ae6d022d5e90dc0b0e56011cdf05bf968c3301c7a7e11126c6ba20dfbe42526e441b4348fcb1076f101707554f63648fb240b7bcafa2cc7c38a40fb552a195be7da2c84a303af81d9b397f1b2b3f79257eeeb5b5db030c364b75fc01fad08beb08ad17eb0580b125f3c6c18ed40d2ca2c9e8b53a935ced0a357317543837f3012c24a85ae8185c112ac2902f79c906ecb83c4f21a8849c73e0cb7ab2351832191451f350b183bb4ebd3b9498d8afadda402a5450d6fd85546580be8a026874d8fb9757ecfd677a2ab11de0a9427221d08b5822951b8b2061d03a2525789d4ccbc55b9239924b70b0546527f583193b1462c5297e921713db5a2bf2624a02fd556de6b1a9814431a803f9e0221138207bb80f8607d6cdfb08de419803cff01a7461f3cc4b18c4891a1cc486d03e8a7d430c14560e70c794175a377b3410b61f40953207236ef353f09f730a5d60485dc55bf1efa94e9adc81682dacd954561f10c3d5d4608f535f9eec10a20b826051da22bf24dc231c9f877d9eb194d474937151622889352e332ea5b8707ab930ac9f2964515a1283a53662580ec30145deb735c246c719268b3e7ce2770de71da617b36fee062433d8f2acc106917f01516f55f1d5d039ffcd6a16722b2caf55a36f64edd01a1c656af7dd7bdfa596dd0d226083fc0da5b164a5f52c46b4c90237142104fef6497fe86acfab8338626d26ab18a2e17561760fd5723451d0b025005cee148025f863d9a6e717c7bbad4a81f4b3515f3fe14023070b139cfdb9be783d85074063e80492567fa8ad91b102140fa089986fdbf01739421ff6dbc83dc69e4501dbcbee780822c793b63a532172f7308306a6672c5f962c3743239e67a2ff740695b429c2ea09655252353118aca60ddbcf3abb7d5324fa495350a89e1fb4780fb7c67534cf18bc04c0d100c8059138d6434b7a61e138fed792264b65f2c1d617129a1ea577f6dc3533147d11ba6c7873279854c6824153537a9ce552a43c647a052c1f566d9fe03c601133bf387a15b960c1e4c964c1c2c55c4ba08bcb14d91a79b13652a590f04efe30b9fd2319166561d894437d13121316fdcd866bf5b4e0b84c22d975b5359aeeef1e5e52b788b7145502cbd68c65f7ca7dfc008706f056a213cdd88627803477bd9ca65d3a2ec500ba2ce346dff1b447a01de6efaa2bf58b27d86ce48bc197198850c2e8af6ed2eca56f70807b2a8bfa9ea94d8d6c0f4bb5c26f4611a785ac600fed436ba7ac9d4a12b3f338890e6e7a4f1d2425e44488118246b33bfa5edcc0f07b76e744fb672c03892ed9ec3c0a0715264c493dce20c972c6f5992faa3a2cf07dcea885aec3e2c12e6588a7fd2b551294a39e27a9a56f81bf4fb496e62228ba33a4ab3d9fd455ae65cc7000e30db1e363a60200db430fc5eb6cb0bc035315bce034f4b135dc94bf51140cf65ba995b9bde215227b5d27015f59be4fdf81cf9b86302c389e819eebbdbeedf4d0c7c42144b9659336c2400b111dbd80cb7ba1145d21206d9cb807696f49b1504014babdd46bee6508a8826b1f7494f92fd77f4c288698c23d26e5fb78d9cd9a6dd9790bb11aed3cd3eb3c7f444216eb0845b8a40408708080d45bfbffa9153074b06dc2bc26d658cd63971e28b4f690603f373c7340e681751f0a06f4667ac0a186250bc735b2ca013a19f13b3caa30b6c18a0f22e816f89d135087116e2a272986029c6fc3af8e2e22672c526b9d67ba2b9ea53b405ca8646965f8d755c31bfeba29d7663d59020cc22ab8bae9a93b20ac5f825a3b352e428f6eeee48ef46993ff1f76f08a20ca928132db2f6fcef00140478b5adafbee0f536484e4151545154fa94d829e1a285a0449e1a4b1724c321f9aa8c9c94bb7e0bef1463ba4f6ca615aaf8dc23a9fa4489a2ed319db3e07967f68aa93c1bfc0a5af3a984c0703fb53f0a3e01eb930d7d784db81b921e80f8e06d84ae364a9733ab9799c32e955abc1413ea0872239c314669f2a84b282742962f15030dd9068b6526921119abf60b5bc397ea8c2ead4a4dabf681ee16114dd8fd656c9100183df9c134d643ddf1cd62d1e387174a3a58e114ca405d38bdd8b681135ea3049bd23a0e46f405eaa831d5b1fa1cc6b1c7f4327ecc735a18469bcb54cd2f39a011aaf74c5589a44b2cb11e51215dd8054d75e0d125199a4ad122d97d4f93ba4442d3fa8ef8fc1aa55f7e0e29bbea7d20592aef01118e4c5391796a48b9658346702060ae917904accab3efcdfa586c920753be10c94053852bbac6dde80531cfb2764c8ca199ef7bfd6d5e5f4322ea62814283e2b56061c9e5342aa5a9f8c622d4bec3af760e2e2a4df5fb27bc08fb62f34d23a32dd9668873a1102b9a15c7f5a181bde932fa4741a1af5a277d4415e3b47c6c28ac3a68b4cf660a68ce3c4078afa9a2910dc2f52b22f189bc892f04c25bdf86aaa77b6ba8ad7d96df593a235fa6d5a91047530d462d2f90866a401ea1a4d707bf29f7ab57e7a71914e4549cce8cd66faa0fe4be2bf3beb33c2d28bf7e4e6cb60355612a389fbc4160e987d86746844f2080ebd3c1d2c454b1db8ab7557b277b39cf6c3fbd7e6bab1ac135de4fbec4e2f9c3e5d3e9d46a70f0a5ea0d42e7bc79af7ff10a8956f8acc5b9974ab98b199e9475e700b66725f9be549d64f28ae28bc98325e7be544247a34e9dacbc70df4671cf65ee74f1f1ce6d1883dda3682330eab8d04b454a4cb1ca2fb30446ef35839d4b08d6832c1c2f1e571c54096ac7bc02a3dd2f0e90fc17f72abf0e6a4cd37153904b75c66958e5ee8b22c13b7743514d475ea86f81b04cef782ed5d1b0817d9b0a43b9fb3f9e61dd3b7ceb6d687c1165c10ab25495b75bde17caa0490480f2338fffdd601dcb92e1fa851d5371177f54dd44a69aaee082d2f0915e884f847821884af5c90064a8d9475c514e414422ff051028f927e2f7a85451eb902a92011b727c88aaac3b4cac057854aa2e5add55c90beec8cd1528f32eb0a01e45a5e2ac2fa9721a40fd7d300ac741755932aca8a1d14a2ba9e42b3aec49b5b32855dd0f373ac0b981476afb82d51a42758684ab5206ded33667b68dc008db3adf934a1554a35093bc7c31c64d509e06f281b827d5e06966e50b5de128823083d0bbde4a49bc6381b85f3200944a493bc9ead2bf756dde4662c6151ad2796ed820054ad59a75d4a0bb1825263751f7201e14b7203d36acf1de8ea7702ce77d51816537a86994d15694ed74a251cdbc875457f9dde6ae6f52a90678b58f5473dfb23668253c28ae2fdd53c91c8e494984e426a9722ce2a7f69f611387d7aca563c7245269d20edc0595d1de462c914acf43d0e1097778965231ccd465a29df3261c98e22941dbd0394d55bac7586827750fa4ca85800e6f8454bec50bdf1efa7eb725cc171605a994a03c5d09e96c7be2b85ea7ed375112bfbfc2df47db0ee9746fc9f0750edfde1dcb9b85f4f3d573a3b0ec3c3be54439fa0841fdddab26f8f0500a3ab4ea36a10848597d9aa0bd8daf013d5c648c49da05f4618dad04d387fb75a3f25e750415efe29910e46775fd3207c10b67a950e320d48e93d6a49f7590f393ce3cc48692ac539df25bcbc77d9e3e2dfedfb0d3e47bb0c829472c85f34fb9f3979237a89c5c68f3e5a172697f82204cf9a6f10882f09d829f6b01d2f0433bcf03e6b2bf574a5dba1cbb2495681908610d82c4c6e34e47f9b79219641b5d355c8c124eccda6ba6379f3aa0f68b26d686aaf9d7a119a37ac57c10c6eaa1cb49864512ed63f1e18888390af7727c2efd1c716ef1e1e0133c11962e6511cb1761def06bcf2b9099d18d4392c10db0ee2964f40f93cf3eab70cb2eca0cf850fb4c632c7f44bd6a6f62ebfea06dd0aeab52c6acf348ed4fbb4ed480a061ac1304ceddbcf8a1583d9ebce8695ba44e2286e5dd310b6facd97b78c5f7ea4e44b210c60830e91ce06c7d7f02cf7a50b7fc211aaab2c0f94798073c08fa49664edfb817192bd7a1050de2d112403e950b4ff5d461ba2f6798f9ca7633704d6de214a664de1831d270b7efcd618703d41734f68aea253d7fea825316559ac3f16b511db7e9cf7d98c9d8241d8dd026b7fda9e41e60f180f32793bae38a9bd4284732b56291960c76b102b066dc9201c6d8abe4f6d4e0685d4523589ef1b4c3161b361db043d65d9124e9d6c2b6ef78b7688f0148c2ebd297f478aa7e74f04fe09c3e7ec14f43a3540dd8c1d2a506be43801d2703a12820535dccbc8e3b2f27d431ab2b6be5329767ad3c3531390e2cc044c63fc6a98ae037e0c884383d1110515ef7f2d252721194e49622d1dc8716398b8802b5219c84d91500ef8a90ed71084facdb025faca14d3b1b78f1f2a17a27425def4be7a08766a3d39c502e763ea4aef56e39faeadad1ed689e39a28266c3b893dfa5bef751fa81ce8be1dfd18d83dbb73bd947597d7b1aa266926ed80511f994db2f0431d54e21f772536da2810678175c6e8a4dae284230493a0ceaaaf81252830a95646b36e63e65b9b99f9ddaf8bf45303f630f9eb22aa375fa4084ef81dd9ca03a0fc4d8596aff3072e7ef23a116f0aa06033607451fd441ebb65b74dd7d14969522c520f4cd9836200a50c43d733c61437132ee674f06a429f8d68bf160ba680e4c40ed55216987f10832157ca427ffbbeb185e931b761ba45026e7e90bd94242cc7e79ddc6a8dac1bc7aac23c7ee2fe5243b76e555c14c5aeb85e48c2c50f02efa3c1890bc799030d9f055b83760507686033e713c48e2b7792345161798306d33675a584fa48ddb27b98fc030a280e6f8d416f10eb650da69d1129d1c88045cebd37c1baddeda554309d2780186814550aba76f4bb6e88532ef7c1ae677cae1da9b11536fb87dd33f02ed7002dbdb2542c53e72c45631eecea58e0f171dd125b5ebc5af392f26dcefea5bfe451ce57847e25e1e81d0398266e8c1c3a139fa22a2717e810519250a184024312ba77e2f0d15451b6ca7128be04011a19203cf3e7cacdb86058e3c523f71f7f00f07c866c260580eee6fae79f774a596b9e1073e48bc33d6db0922d3c2091e729a111f47e677994bd93c3ad80334ae03c99e51c16c61fbcae5c8b37fb71e229a974caebce3c04ca2d8bead6639f6af8e21607165dd813a9efc6c8b5c8f82e7cb5e41a72d0f735d5412e269ece67fbae62f44c677fcc7b9a8840ac5c47abaf34bd347fe409ab4ffae8a165b9a376258469c3411794c26d40d258f8cbc93acd116c7019bb90260a0f4d297b352cfd00f2cd8f19a1bdef5d0932248ba44a6906bcbfe355f2fa35f0947b3626ac4081f09999c4ca359ff1ac45334e24e7d31687ee3760c68ec21fec98d01d9603e0ed54f3d74b4856fd07218ff4b1fb4d8074485eb139181c9423f14c5e1b1bb3853a2eab2f82f41f797764fa9b0bff17c461d291e288a00aa94b1de77ee5830c08e65046cda67e96a9064aeb96e5416d6ea3c2719923dfbfb7bd2c345acefe77be2ea2120ea5a8b697cf93bb66277e67591d52a538a5a38342e269ca81a70869a8895d62a5c68e07bcb45cb5c5b95ba610f3f746ced01525cf6e632bdb78a492b3acec7aaadbd991aa370c51f8b329e8b6451fed908683d60fd24b2334f3ea14ee9fa68a379ebace8beff862fb4d0e9d05402741f1a55fff7d405e6fd57395bbe8f71912a69e18a7735e91ccf2a25242bf48d4745f75ba9e65156bf79814fe019eb9010e8303f465d242d8fc51611ac0e362608deaf579b621519e5e9ae21c54bb604b8ef04e06b8d095c47a192d6d3c3f512cff0822eae18172ae0fa7049ac5479c562f7aadc8a74f168bb99c29a38fd595999379bb79a6e9ae3d35fb459e93cf311abf6fd78256c841868bfa43e849ccca3eb31e8ee691ff9971a5c74e9894ce3c6446c2639703fec01a72a47c52dd3d4e4b9773ff191393ede27a12a9512d3f58399cb0ea92aa3c5f59308930a949f7014ed3fbd447ae8977c9f7ff68b185a5444ea1ee50969a3b5095cbd60a26743853bc5e6e7b45c6e4a9e663d4836b47282bcc6448263b04028f340fd175a08fe8df226bf6a30e816c866b646a256a2cc904e661d228747cb2fe7f79ea5cf0d243e57a86d32f14a5fbc48ef4e2f6a6799fee6dbf5d86c60c91115e28881d772490a8e5dc15e47d5e8d130f25e6cb45e26dc434fdefbab81d4c1f17b796362a3841fdb97103b9e50e04ef20ad3b159ef08fd458e8d05804f3a3c9124cdc6ee6143e467b30dd1eea7a016fe9ab2e31613034037ceb94f9a854e24003f6fbd22a208a02252f829b43a728632169614d1c51c5d2117b2e222b52b7c868b2e19db6d9171943fc6132dc1e48732de7d0c33408ba5eb3db651f813c77e96394ff624bfbe5e42d42085b807528fb3998b3f12940f32f74eafdb3cc0adf2b706b4677a071250a4c8a19f72270ab34e450b8cdea3d77d6087132a08674b9cf5b6813e48301587e86576e7b13d9c03d0b8209903d9203a6c0e86a1169451e61532df2342e73348e9993c408b4553c90ce7fba5923c4060ff215e6736e1e5ff411471f32af11c4b1a24cc3e30643a733250dc3f8784119ad96e39423340690f7e82986d87d28942d3acd12679d5de301f0e1a57de70cc14aef330bae114d83dd91c7feb682ee324b9e427005b24bc274d82fa00c7627892d545df3e3a1ded3d02a87347184564008c74b7304856a68c84dde73e31ff6607233241d36ca6ac623a3acc07c8f670a29dfb587a52823e45afacda8574d78c3bdb05736fd8d186d61c80cd4b6b851aa6f688db7c1be71844439a190dbe97f7e6f864d3e64abc28f24ea68021b710ae7c31136d8e2129d5c59989f4d83de4e41fea20c8774303af1901e5034472d5e2d0c83dc1bc06650426804c9cefdf272d77fca564bfa7d385361aebebf435fd72522f4a36b1a1b2c8f60a0087aaa35fab1c4395cea33308945d6dbc9075be4ff8ef5398656178f8d0355c8f9751580d8422ca59cf6f2735049f76f62703ec742b8a2d09a7dddbc4903a3e3182f15a80e17bfd96187699c65c247281f19f2aa893f5ae9b2eebe293c6e7c5df51c863c8322e929d24fe046fd41433bc2b2a094860fbeb06df79e8300cac82031aff96a0561040cba83a55b3f60885c93f0c0eed7728144479357d5ea524c76b1f189cfc7767c80656640a67c85c2ffc016ffc507cc7788dc335b63e91a590743ed40d799bc7babebfed448728d650603a16617dfb7bef2d659232cb09b7093a0aa6d35c32bf54475760deccea9634d121b33aca533e6bd2f469594d4fe957485695a5a3545398b80ac447be4e56206630df989fb9ddd88d89398fa03293bb31133883fa2b4c1b69d623a42fe2ad38f071c9d76b54f588014955d8a5a305508142430750dcd248e7da33a16802c31b358cc2e4eb3a6a68a992affbccaef3c03e2330b2ed275f35b45c916d2631348366904bbe36f9baf9a5d3bbcb5d1a1121b7cd532655ed11ba4d55df24451fad93df2e4a0973bb0d7118ce9accb56cef956ed4d005267627a488bd9e38e07762031337399da061888393464397393295529b94308b454e501216c14ee61007270430939c270c9936acadf0b66180d3a843edb220c79d159889f101295075776d72b42740919dd6daf3e3a869ca542a1689ee4e014a74d475314c4735d076beb70ee6b6d6c6b88ccf5ae65a995b7b3165636fde95e16b6dcdbbe16923bb5cc1d24c171dffac67c645773106e7232e8942b258b9a8d034f3b4c1cef50d298b569a278a9e124547159126d4cc5b5af32a2d893985d94857e634bce6b591288a33d6f733e711546816e9c644a51b133d7c7bd4a8e8326f813726baabb2306dcdeb2c9d1a756ac3d9d2a9cd1e35af53dbee8303ecd72b4e0512e2c8918d3a0393a89338628b4312638c313ea14ea63bb6f8c3c2b455a5264e7068e16ecc81d151af32cc2e7ba582e8d79784bef2fe950ad751d751ab25ed296f4fa1bc3fb51a62573de4ce35cffa64ddb4b2a85fd19a9cfa939037368ea393e3382ef50350b5559e2b15488ff125e3e831d6cb292b7b7acc39e588052c29cf29a3c778ccb9b253488ff1d295c478e9d6cbd24de711282ffd8894c738155aa5551ae94226f74148f4242697f14a237f6543f1f1753bfa92181f4b9fb2b27e7272247d65cf252727574b4ca3971e3669f49a8f4a994ee3451ac91c9fe904075817d32ee1c6bef1f4c2b491d97432994ce1189e629c42710ceb8be1189ec2303499c8314452c22449f813519556bda29c88166772515cb22651144511259e7cd6a9476f4c3c893f3ae42719555288e26a874e6d39513a15dd89288a4eae722a10ec3d918a6d5c83aeb49b1ac5167d1c451b8ea2b5d65a1339bbb124e26f0bd356954827b687a5c06fe391ab75b2c750c4573cf10dc3300c2120866118866118de300cc35b6b1886e10dc3300cafb592b3250cc3b09a35b0b1f8a7aded431e7def45f18dedb75d8cb1d96feb7edbd97eaf779871631c862a8c71ca92b5d62ae3ab2161ee6cb3ed6edcb8a63bb4d97c92c9917814c910e39014a71895204552d4824352f4c138144d61188a238947528b88b138aa522754883a85618847524b2862d28728866e7d14458c7b31682143d18ad5c76d8c2f2a954a6941f5687f42516549129bbc9ab498ee9dc274dada83f4daa9b5236922ad965f795b3181aa2d6bc971249d8a7932351371042385f41f2533a1cf9444da9bc4a65194d23f4bde8688b062c40c0d0d0d0e530d0e1c386a489b9a9a52e87a8dcd08e4e7748641ba2d51189d749af2480d367f6246d0261c49a0121026f707bb933842b03eda5137b614eab199316d6e526d8358af9e637db4db94a72ea6b58d31e511234423d8a63c82c53235044775c39a1c393939a669cae4e810d8fab9222a67b52a85aeaf72788ee4e8a4c79442e4352398235aa0121026f747a7140a5d07f713620e1a98b7a11f1b8488ae928df819c28ce04a2c533bb277d640d5963d9d7c7ca87c94581f3c2a1013f641dd945f549bbcb4a71c27ccf147c98f9b4e26b5477b4e04b0caa782c9adf2c138144d61188a2329a65cc4a4d731570fd12a2b3de5a9b73fa39b50579935e50d4d8e732d8fd86cc2e388c9f0da11859bc3f17451a9540ad5d8ea84aa712ccbf222a93d30bea7564ea8b23eda636262eec9aa625a47d91249984fa735dd30d74ec7fb4d7eae0c07950df242f5d10ee64b0cb547ab9894f5a6c33391a31a662ad7a3041449c03ce57a94802f9a480016383060a2723d4a00145e20c08d1b868b906caa54686e6c2fda83a2b11ee80f104818c1023cb0bdb5da4a9a9abc2d9d9a5a55280e4b9bb0b4d921c35a8e40011e3f3a35a912b5016167848b9a285a5610765ca9b6412eeaed8da226084e31144563288aa2288ac65014454f3b4328149338456ff7bdf7767fdb0ed2b6ad0f20b7bbfbec768c316e615b6bad75076eba4962d4c3c8e130d2b0c2175a39000da0e81581a1054680972d72b896a54e2ebd3e20975e966ec62097ae42000572e9a952a84c991ec8b69a8c729b4e463946a1c4b04046156991513938991d383ac017a3d7df987a163d2c8544ac864d87846c6be5300c0de0258757c8c8e151184f1c19c001686d830d9f955c2b068ef2bd0520cab7005df2fdf98e02a290410f55844d0220c55a607a0b3d224017310f605b6aa704b9fb80c900d34676196e9e74c070006a343281d143894824181a488543d4c08d6088c0848d4430945077080772211831207b08971c06630dd2f66eac92160c1d8c76c806743756c91218015326573470f0021c0fc041f0868b89238a9b304cf28b1a2f0e60330336c648820d33da6003a90b3692682c6086b9a2c18609c20cf12a901ef9bae8802ac3b502b15ec533ae0c9f22eff2b24cb6893af1567b884988b97cdd264e205ff109a51b233f60b66a0f914746ad402ca945a440be5eab88019c5cb38171d70a842369245a0ab1c9e40a240e29730f62981c656ffbf5244c8e6859cd102e62b755251caba8f55baa5069a2fb9c585f1226378b6bc7887163a3685f67d62a2e4c5bf5615e9882de9dd25316a0a41d35ba0db3b5b836b932945fc76eabb4ac06e764882b2375ac645c97c8216e05e223636fe217ad6d329d4750a1f98de19f1bc31ebececffe22a972a64d2763a70d0d997cf4a13209aa89931bc34b35e833d116ac8b697be8f69de9dd16bf9089f9c2b4b9f7f5108c3072b822532b31a705a699ab069220906da459d368e86648147aa852e510d71e1315480b96b12d8798eb75d520d8fa78a1c63026bdc54281796d1057f5611d33a93db093de33561e95dbccd3c99347efd3c6936d3d19db3bbe0f368b222e87843e3afe8d67abf630b9f83ba0590ccf95d73cf68ca70c3537d9c20cb1c34660dec61d8c37108357777317776f9987cf4f0fc9675b5abad5e54a00498676cc3a962ce3513e21c90beee9e9e96112e2766119afa0e36ed4e5ee0fae8773e2237f4ae34b099d160f9f9b298ee2cbca28ae61f200fad4245c35794f0fae07e7c331f9e9c1f5e04ce7ac692e1a38a9c1d07472c900672249d38904a2586f98368bc3e1aed5e150be1d2ef8a248690c920dd2b501df98d4325b5b0f26c797192ff99abaf0e0d23d4119bc7cb1ae25b28a97117849dc256ff95625f426b13d9267b4ebed90725677b3bb5bd12d4a50c9de942c4ee9f5c475e57018032f4a27e8d8608faa4b12b9438d590f3374bbdd5a1b10d5185117981b91e8a6ca5131a56a140a9268dcf57c54974c996a4fb3f2c2d3744c9ee6c2d3963cad85a729791a0b4f5be1692a3c2dc9d3903c2d85a7a1f0b4139e76e469263ccdc8d38a3c8dc8d3863c4de869c0129ef6a5f6b425dad3c0e4a705674f0b13f4b51bd0d78648f81a4ec8d7aa04f95a0ec8d7ac8cf035a29fafed645fbb927eade8c7d77cbe26c2d7b2d440f85acfd77c7ced83aff5f81a8faf79f0b5a4affd6a5dd0af297dcdcbd7805ffb82f2da16d5074d669d355dedd13e3a1fdd9501e1ed3ebb2b0bc2db7d8aae4c086ff7e15dd910deee6374654478bb4fefca8af0761fa42b33c2db7d7c577684b7fbfcae6c8ab7fb285d1912deee03bc322adeeeb37465f6e6ed3ec12bb34978fb8fdb955925bcfd07eecaec12defe23776576c8db7fe8aecc32e1ed3f7657669bf0f61f4557669df0f61fbc2bb3386fff617465f6096fffd1bb320b85b7ff40ba321b85b7fff05d99ade2ed3f7e5766a5f0f61f4a5766a7f0f61fc02bb35478fb8fa52bb3396fff11bc325b85b7a7b72bb356787b8abb327b85b7a7b92bb356bc3dd55d99c5c2dbd3dd95d92cbc3d2dba32ab85b7a7bc2bb33a6f4f8daecc6ee1ed69efca2c17de9e225d99edc2db53df9559226f4f7f5706c5db53a52be3c1db5360ba74653d787b1abc32eb85b7cb6eb22119aed3186f975591e53a35bd5d66a5d3196f97e93aa5f1761951a738bc5db6ebb4c6db65573ab5f1765951a737de2ec3d2298eb7cb789daabc5d96a5d31cde2e33ea34c7db655b3a5d79bbacd7a98eb7cb8e3addf1761952a73abc5dc6a5531e6f97f93a756f972575fa7eb24e18ef4fa4d377fe4ea513782e9d41d68d8563e5583ad68e55c4e2b18c583d1612cbc7fab1945840d6122bd8bab570ad5c4bd7dab58a5abc9651abd7426af95abf96520bd85a6a055d3717ce9573e95cbb1b7315bd8be7cae232726d71f55c472e24171797cf95e4fab9bab8945c5e5c40d717d7920b8c2be80af3bafdbfce7fe11aedcf5f1a6df7cfc0cf5e3e2b35dabef359e7f3ef7352f67de6f219e9f351eee52dd908c7e72c349f79194b2eca57f22e13659d179f7bf81ce5330f3f83f2b325a29f01bbf89912173ffb6df1339fee67485afcac97c5cf8cb0f819cfcacf8aaef8d9ce8a9fe9aaf8592ef7331c153fbb4df14141293e68a9ca0701a3f82025283ee8f7c407f9701f84e4c407f59af82023263e8837f441454b7cd04e890fd225f141b9db0751f9a0db8db523f140c11b6b9ff2404b473c90110f54c403fd6eac9d88071ae28184f8201ec8e8c6da817820de8dd94ea417aef6689894d7f9e1757c789d251d9c132937ea6d158d2e6cf5a84a6e5ddd0019b91d974370649d1b1ba60dc5d1a1583a15d12cb97919c592516a9837dbd0275f69b60cadbd3df927a8696c31c9b4644271dbcc0d6504e56bc11b43198563925971df1ea2f000a5872824699a3696cb1dab121b2a8562371876f027e74ac91387e250ce239e38151a0a2503f3ba2dcc3ebaa5dc23dc4258c3bcba5ab0d17ea2e4441b408d2768c66e5716c579701b0925768bdd723b8eeecaa29c403c902494b3de188eeec9891302b3661b10ef64eb8db9fee42b6d005f6f6c87bf9d6840bd1b6bdf6103d306c4d3f17208cef7547da5e5b0617ab60101f18078b9bd7220972b0b091f2a02688b85892d2280c32202b71b1398630e6a5e60d2b800278b0968b4004102ccd0c00e8d0c9811019c2cccc8f2060e53a11d5362dfd478917aa8caa3c7e601497a2b469a44277d7e5a3121f9761efd3a2a1586e7e96f88b66248b97b3c7889a493385f2c8aa4188aa493e469b30e797fac9bd0d345491425d1134aa2e809754249143d89cf45f7ed6edbb7bb7beceebef7e2bedd1de2ee16bbbb556278efbde7bdf7dedbdd8d31c67def256d775b7bd67b2f3633c656d58d529181326bad350c7dc2100dc3300cc330bcf8e2b36badd5bc169746b66bad8d313e4d6da046b1d61bab2e62aff5dedb395b6aad18bd5db5e0f0a7d452c530c27a5d1cc791c4a16872d3353d11c3dd0d9b419a4237e1130a87a2bde478c5d04f1895c2373c99c8513c99c8082f398ad88cf144a2ca187cc393491cc993898c511c45d28cf08e223623856f7832a5502994c760324e2632c28bcd2885ae97384606dff0642247f1642223bce42862336edbb16bac8c178ba38f6288b58c23b6a3967114435cc62886584b88c511876219210eb1a8650cb196712ca38c51cb388a212e631443ac8586ecbe58aca66a8a924d6edd92d6a656091b8661188a23dedda23801de420f49d34d527f3252887944e4c12d5ba2d16f3a266224a9e18e68b76bb7e34d779e36cbe5a61bfb87573e7a499390183efb2149d274b2e895a1ea58df7c31c8de9646fba8636d83582d36a93d02a1bd19895509c71226266dee5e2b4cabc504386f338ab2ad2e5589420b0bf218828c841c6a3980d10bb2898f8cb2602514d96918f6fcd45aabd7dbb5d65a2f5965a16991e86ea0ed994657d6b06dc4762c21a620b044fe00026464d114552574aa324d222a0550430104288095161c608886c3c8d8b7441b198f19638cb159834a23cdd0d6e8b478d826b2cd4748168faaad2675c9a2ad7a11c56cab6176b2cd0ed92a59244aca367ba504a2cdb2858c34b2e8a293a6cad11e0d64d17f66459234e0974567d20497457712c58a2cfa10658071cbe205ae0359749bebb41e3520298b6e75bb2c66144dd1693d72c00b8e1ed000133891ab00194319fb3dc11652982fde09b29408e851a2c7e822031c2306420d18030738483c2984b2f87859d00839e0c72bc24144034bbc08d810e1c2e3f96288a8d113d3448f125da3c50d2e380a3053b9a2e14609b0b8e10218b8d1460cdec0d96c79e30ad2cd68008f9e2e5cc0008c8a29af4c244fe3e9644f23ca628c718953e5a7ce23a8d0d0540ff949c4d08a946d6c74cba257201a8f898ebaaa5c356a57e3363e6b9b1a2e2db33aa44e431b1ab7755216ddd63c1c8e4275893dc6975cbf1e737d4accaa8db298a47737263a2dc727ba151d26fe8a16f3ab3e572a602f975ca739a7c4f8f5f29cb20025312739c5e4b29371d37b7765a6cbb8e8d674d124b31b5da765a58bbfdb04e8225bef804aa813a97772f289d41e459c5c9befdc8d895ef33afbbb16c76981b74645a779bb7463e250c7c4de89bb5def76bbdd99a4f49dd16e577465342e8ae24e9cf9de95464537df22dd984a0bb3a691b1f0aeecaec4f4fc0c21721bbb1c03555b49c2eadd4bb6edd0124a7765abd04fd694f192f1744e69d2a75c6b79289b1b9dbc32d26772a55bd28d2caf3c4fd569734515880fabc3b8c88de1176ded9e3111d2248abd8d2a9d69ad589d904e6d9a04f5b499cddd988e57a4845ea5f55d226380b4d4c668bac07c62721185aaf57c2225cca67285d3f904bb58b30a28af3586cd4c9a4227737857e189c946f38a00a60a1df2c6c175940e6e632489a1036126304d1e5e9fd9d2e9c9af8f7e3283b67eed82cffacbe219ac391f73d7ef7099cfc15feff2d3e6b37b1fa246db47d768db14103fb814b7440f848f1fe3f759445d70b185ce6d165858b9e2ad952bdc5691a3620a29aa4401c51338279a606268092592b8bd4dc2a9bcbd39124fc5a73c127ec44f7123fe082fe28d7022be081fe28970217e080fe28570ebc50711e57b407d0e3e3c147ffdcde70fbf80d1cf979f2e42516c2886934173507888d283f52208218620a208238e988204955b124a2c31c444134ee09e80228a2a524c4145ae0a2baeb08245165ae8ec16960bdb85250282157383a3ca91b3d2d9d1c1e36fced0e0a8b139e1d02a5337d8d0a4061a66902106185e60e2c2921694b0b0820a4990a480c209474c305284c810a1126ab43c0b02d281042141808cf0234b7ff8881002083d3e3ee8c1c3831d31179a43ada03a191919191919195c0eb937055e05f2b0eba31bfdba2d76eb9468e701d3762249f9c107202eb62472f3e9305ea73a9c3e4821b30ede8ee23a75a14ea4d377fe4ea513782e9d41d68d8563e5583ad68e55c4e2b18c583d1612cbc7fab1945840d6122bd8bab570ad5c4bd7dab58a5abc9651abd7426af95abf96520bd85a6a055d3717ce9573e95c3b57918be7cae232726d71f55c472e24171797cf95e4fab9bab8945c5e5c40d717d7920b8c2be80af3babd865eb8579557aed396b7bfacbc742fa2d7ee75e555f4c2f2e2bdb2bc8c5e5b5ebdd7d10be9c5e5e57b25bd7eaf2e2fa5979717f0f5e5b5f402f30abec2c06eb021180e560596835981e96044b01dec0aac08860596056604db02ebc18e6048302e301f2c09f683758129c1bcc080b02fb025181858101606bd75ba03add2690ede8e3abdcb73f8f3e8dc721a9d594ede89e52c3aaf9cbb93e8d49d56cedc59e5c49d43e7edc37cf0c1fcd27f792faff45dfef749ef7b2e8ff447dffb2d6ff4599ef758bee8affcee895ef7563ef7551ef7437ff3301e7430bee45f1ce85e5cc9bbf8cf93dce75c1cc98f1a6dd479f68220b38ccba13c100fc808a80784c48ab9c151e5c859e9ece8e0f137676870d4d89c706895a91b6c6852030d33c810030c2f307161490b4a5858418524485240e184232618294264885009355a9e05019120240890117e64e90f1f114200a1c7c7073d7878b023a60329038587283d582f82106208228a30e288294850b925a1c412434c34e104ee0928a2a822c51454e4aab0e20a2b5864a185ce6e61b9b05d58222058313738aa1c392b9d1d1d3cfebd23242ebe2494ccf9507ee7b683dbc9ede876763b453bbc1da39dde0ed28e6fe7b7a3b403dc59da09eab8e9c0e9c8e9d0e9d8e928d2c1d361a4a3a70349874fc74f87920ea08e251d419e1b0f8e27c7a3e3d9f114f1f0788c787a3c483c3e9e1f8f120f90e70bcf120f189e204f18bff990e3bc8ae7dc8aeb9cc877eeeeeebea5519b1a1c3433e63b8f8e1d9d554e0e15ce4d0c2be7322edf824b40a59f0fa967c42bdacd74b3dc0c37bb0597804a3f1f52cf8857b40bd205e5827041b7e01250e9e7450f517880b27373bdf71e78be4e50270c1ba6ed26884392a679f3bb01a38689baede6877ecd24ec9d08200022afd12b8d36bac317cd929b883f79d51426e929f02a9096df04637cf8010a0f517ab832ebc51d75c8e10647952367a5b3a383c7ffbcc1862635d030830c31c0f002131796b4a08485155448822405144e38628291224486089550a3e559101009428200495d29c2c2cb62b4a57784c4c597f4eba2e405f865094cb016a6d3d2db4bb85d99fdc2db4bc075eac445d8b9e5d6655bec7613bcb29b5fcc4df071f0201e0817e283f0215e0827e287f0229e0837e28bf023de089ff24738123fc5a93c126e6f4fc56d126f6f6e95789b84db25de2ae176e8ed126e99783be4b689b74cb875e26d136e716f9d70fbc45b9c5b28de3ee1368ab750b8adf2360ab752bcade2768ab752b8a5e2ed146e736fa9705bc5db9c5b2bde56e1f68ab756b8b5f2f60ab758bcb5e2368bb758b8d5e26d166e756fb570bbc55b9dcfbc8c9b1fe3968bb75b388e9f719a37dd76f1960bb7791c5ef3346e89de76e13a7c8de7f036fec35b226fbd67fbc50ffd100047fdf03a68289bfae1533454a77ef89286ba29279f8307e075f01f3e8656fa0f2f436beddc7670ad736768e7a6c3fb10f9ec7c78209cf537bf9ee5aec7c1d17f39ec5dbee3518f3dcc79fc0ef7e063fec1f3f01eef81f7fc07eee37b7808dfe320bc0ff7f9105c8407c1d3f7f11f2f82ff7ceab2ffe1a9fff1115ee6413ee5407e0427e183b89007e2414f8203bd10cf1fe4b307f2da67a7fdcc85bee6253ccd89bc900ff912dcc813f1223fc48fbc1137e18b380a7fc44f78131cc9a3e029fc09aec223f1249f82b3f02af80a9fc45b78165cc9afe02e7c0bbee495f80bef8233f9251ec3bfe0303c139fe16370191e06afe167701a5e06b7e16bf0264f839f6f83dff04ddcfff4ff1b5cc7bbf3fcbbf5c20797e23aafc3779ec77b78eb858f2fe3f7633ce7757cf53b1ee57b70d5e7788e5f390f1fc56f5ee5389fc3a13c0faec3df780e8fe33f3c146fbd8e2ce51770dd875fc0e82dafa5d04db0d1cec15b6ef3c1e5761f5ca7b69d5beb6f72789cdb1aa60d8867bb09e6f69ddb9549f1f61ddc95fde0ed3bb92bf3c1db77749ddeec63f4c5fb6ce998cf961b201e9011500f0809c807f403520202022d0105836e41b8a05c902e68175414c40b320aea052105f9827e414a41c0a0a5a0e0ec36c3cd7233dd6c372b9af16646b3de0c69e69bfd664a33e06c6916ccb78ccbb9accb447997afe4a28c25f372966c94b7e45e3eca48994bf6e5a4fccb5db252f69281f94ba7efed79a9d3d3db33984e6ff0f61cecd4066fcf613a6de2edb45ba735783b6da8531abc9d86eb74066fa755e954066fa7e53a8dc1db69563a85c1db69ba4e5ff0761a51a74cbc9db6ebd4056fa75de97489b7d38a3a6dc1db69583a55e2ed345ea72c783b2d4ba72b783bcda85315bc9db6a5d324de4eeb758ac4db69479da6e0ed34a44e51f0761a974e4ff0769aafd323de4e4bead4046fa7fd3a35e2edb42e9d16f1769a52a744bc9de6a5d321de4e03762ae4edb42f9d96e0edb4a54e6bde4e03d329cddb69c14eb3b7d3c2743af3f6daadd3206faf0d750ae4ed355ca724787bad4aa742bcbd96eb3488b7d7ac740ac4db6bba4e53a70f299742e696b7d7883a1dc1db6bbb4e7fbcbd76a55399b7d78a3a4dbdbd86a5d31fde5ee375eae3edb52c9d8ae0ed35a34e43f0f6da964e41f0f65aafd31e6faf1d75eac3db6b489d7ee0ed352e9df6f0f69aaf531ede5e4bead4036faffd3adde1edb52e9dc6bcbda6d429eaed352f9dc2bcbd06ecf4e5edb52f9dbabcbdb6d429ebf4410a9901e0ed35309de28074c3a55396b7dff89ae583b358529cf583b3582cc7a510eb3a14a74243995e2879429353a7a71c85c3a355d021f4060b030bc2c0c096605f604098179812ac0bec074b82f9605c6048b023580fb6056604cb02c3022b825d81ed6044301dcc0a2c07ab02c3c18660b7579857f005e6b5f4faf202bebcbc945e5d5ebf57d2cbf7e2f2427a1dbd7aaf2d2fa3579617ef85e555f4baf2dabd885eba979557ee55250832b31c008f02f140bcfc252fddf0198c0d9f834d3e87a9e169371a9e3634c3d370323cad4a0c4fcbc1102d1e3e3a29d38c9551f54ceb3eb51ef1284ba1dbe50caf8793fa970f76774020100804026160491b98dd864a09bf5000c75be4e58b0c8ede9e9e32635d8a095cd1e92cdc0d062b856e88d1d80e1e6314e190bca2490155d6bfdb00232323a3d6e97011c6dd58bbcff7f329555f0d8c3a07b993c8ddc0beb83bd4e8adc38069b41bd8bf0e7ac0c8c8c8a881fd821a6bef1a90f8b4750e4edb4da2752f07605c3b49e25a0d3c9f15dbe1c189757a909a91c204028140d3f589ad83256e14454545a48f29247864023353c3ec5031eee5009f78efc537bcff448db557416e6112a0dd4becb3c16030a8638787470ff287baff7ebf9fce6aa7c707a391ca34323232cac9b1fac0474a774fa713985667752a9c1c3e7c9442b807877788601008fc02042ee17c0906833635373d20609b1a1042b835384210a14f3868aa083e40201008bc54c4a0ca92ac91757097b851f4e07b8471388cc3388cc3380998d66d1897db7b4c193c83f23165706fc7ef34cae0e54b05834f6313781cc751748c4913198a360cad8874c5d026e57276290cc3dd6eb74385384e1af8400986ac69aa5413488561e80bc3b0c686e4f1783c5e2eac28390469471165f1bd38b4c10db10deebd27550e9c23e7e6acda5a7bb1782770c55bde1d5550a86aafbdf75e8c719d91c23e218569bbbfbe384ccd4c617690043d1e4033e0b5022a206608e48e5cfdb4b2eed9587bad80955df264d2a8725a053b6c08599563d25c19bc7c31dd325f60ca89f3f73b69489d8e92b9333435d7897b134d783c79ad7e97a8b2e6dd28aaac7339abdb755d0a2edddfaeee9478d6c8f690c2300cc3367d277597fe71e1f57c9deb5ce77ab31cbab54bf7daa5fabbb22649d34ce5eedd1d0ac7a9063c238589f67abd9ee9ee461caabc985d6e49f7e613e2ab32ebdba182a96186503b72b7bbbbbbcbede5bd5d95ca346f149840cafaac38d6d8903c1e8fc7b3b9196dcee66c0e554541c55c20a3884be292b884c249e19442a12a4c4359925eefa81b09550addb38f6a90e4ca54aaea3ba972e01c39b744a5ca142a67d5252a55a650b6caba5763eda4c99c51e56817a05165686b20777f207797600532b2d6a34b9af7a7ba682adf5fa36798d649922c756252a5c7e01c6c07a404ba842a306da6ab321986e10aa6c8e1151687a7901c3ad2081a10866118e2d8926933ddde1260dac8059c748c624cd5349d910629c6c94d7e3acf18c1cd08c692e90c1ad88838724826541aaf48e1215f2c8960ccdc5b2801de4cd7a84c60fcb0042870da49400332267cd1936181caf22093b0a6f1e4e3783221e00d187c79038c698e6fb8219ba18d37da09a5044d1426ce14384060d68481630db3d432930253260d1b9c190302dc0abcc840b7031cdc8c81c60e767081db133ab91e11ed4e5684e28ca22c2a7d69c2e638a30ad9822a090063add8a8ee711447711447517cb1cee4daee99244fa7cd34d954e4e983cdd8c684b1434d1ecf55e8351769d4b1c53f6bd4fa2a0b0986b20dcdf6de528834e5a831eb1568ba55ea15c0b4994a638fc70dd2961bcc600b0acec018638cdba95c7bb5705be4c6663756fdde7b6fa501699a8991314fa85439d3a649923f3d68358d782431eedd2494b7aff7839a33671153a59231cdb2534908a0c6da6d1e8ea5d0cb01b1af3566339d501002848c918447848865fc64f46b22c79a1375285b91aacf732d63647e2a5a512a552f1c23df588d85150424f97e8d856e4b23b15bc5afa13ea94a616306380ed6d962c583e09609b8910ff0c20adc58208c4e952b1a6f604100aab670ae1bc8d86da8b23aab406cc6b8be6004b503185891d5ad752657868337ab47110093b19fa06487216e5b863276acf4c61176b25a6fe103641b8f5cc7f04c32afcf3c453aea3367575651e7793a499e6a20e5f207b91e5d4029440ab9746a72f174f26757fc4cbae83632e48284147d3cc3598736294ad9567d87d097dbd76080e1408ee5b5147a39aa08de6539caa6be04a60d88c703cad229eb098ed78c82f2950d2e5870b319e7b4d98ce3e3a3d9477700d306c4ab2d552039bc97a868fbcecd76def8caf35205725de533b7a6c0cc128da3cbf1bd7c5539aba5dae3c64777e3b32e87e0c837e73d6da3b366bdf3b69b754e5bb87adba540cedbf00b681ce79fd0b8cd285bd2b861da4ce32c1ac772250d2f376543e33117f0e5181fca1ee47a9486d1116bb3ed072a3632e378af7972e3a3df388ea36edec6697c4ca3288ffebaa33470d906c403eab50319755a8fbee0027404b4c5888edf1c24a3df20c9e12abf719c9cd3488e53755ebff1eb382ad761392e4fd8f11cc7e3db6e5c75faac61de388edb382a4ce1fc7514aa9647747cf49c1cb7250a387ecf550eff550e57f9e839bfca71deb8ca55a7ad085e1efde6b415e1b39bf3b61c2ea3f25a16b179c767ad73faac618ee70ea1cfc66b8964c755e7931dcf399fbcef7077cedaa8e938e73bcb6bde86b3fb2d8fd0f8e85d1af913376a9ecee3e369c371e246fda2a0c3575e4b1474f8cadf86445e5d5b1ab979551eb1ddf8e8b5515b938c73b12d51b05dbf719b949b759cb629646af5bdf259df9c38a78dc7a5dcbc72db3da5dcd539e33c3eeb2e8d749ef1d5cf78ed74f4ebf7b4a178fe3a8fdf5f398f5f3f5f8acd2b2938afbc96444e5f996e98ab23396ecf95111a1f9dc6c7d376ca2a3fba402ff3c8f5e802569ea83cc6515d1a3921a1a92a8ff159d378cc91058239c65538a78d4ac61947bfb101f16e3ec7c7b7ed10fa703e085b1e31723de6b4a9fcda12499df58d799bcaafa3eee3380ac7c7cfe13737dea591d1739cb5d19bce376fbb71174e974690728e8f3938e71395d3bced4675c638cb6777f535c798d9f5361398592ef3b6227899e56429c4534b219b1d425f0ec2661ab7c934a7cfeef535b39ecc5082a85146af96acd38768a5b3c936a0dee83620de2fedec0c752aee54e914e38c421d7d9bcda8e3e070866ec234da289bbab53eb0f6689b0ac029001f9c1396dbc8ecc407283c44e9c17a1184104310518411474c4182ca2d0925961862a20927704f4011451529a6a0225785155758c1220b2d745b70d1852502e20647952367a5b3a383c7df9ca1c1516373c2a155a66eb0a1490d34cc20430c30bcc0c485252d28616105159220490185138e9860a40891214225d468791604a4030942820019e14796fef0112104107a7c7cd08387073b622ed44bc684ee4428ba3211884420caed2c1fdcc7f8cdb33cc7e3b8ea6f7cf5393ce755bef32bd7f91ce7f91dd7f13afecfe3fe3a7ce6dfcd771cdec64f5fe3e5e3e0ad3ff90d2d6ff237b80d9f721abe89d7f036b80c4f83cff035380c2f83c7f033389387c15ff8187cc9337117fe0557f24bbc8577c1577825cec2b7e0497e0557e159f0143e89237915fc844fc15178246ec29fe0471e052ff226b8913fe243be881379235ec20f71a127e2b42fc16b2fe4b3a779fe9a03fdcc833e3b090fe43a7c900779125cc8ebe0237c1007f2425cf623f8cf03f11f2ff3f47f5c84ffe13e9f3a082f8287f03eeee341f09e0fc17bbc0fffe07bdc83efe13cfe038fbd07bee379382aa0eba884cbd9ef814ea1190100020004200000e3160020200c0c068462591604719aa8f614800c59ae766c5898caa34910c4280c42c610638c010410008831608664883a0af8c533dda34588252028f4701dfd7f46fe33cd62e46fbb006eb15648ed2320748ebad76665ca8e0032e64b58e4d1e776e2af84324622a6f6a95c3a508c9c7831c4fa27f6d35e19abbdcfbfa6bf44611df52618109bb8691e3a5eb726d997b2acfadf19dfdc890f5a4556ceadeb45a878a858924d4aa530146828d394d96002b329a69c80bd66b467a6b6b0f26455ca4a40415f2d1f0551854e558e3b1df9eae8aa6699c8878e7fe6867770a9b90a5671031082bfba0fce3404cd68bf27f63f82ed055b46b74d6aa68615416c227e88b4e78673952548dcdd375d2e3389f1815d2e6b40b4b977f94369578121146652226ed52ab008d74ee334c71d96fe08f75820b43bfc20c209d6350e4376733b623e30e86bc49dfb61bc3d9f17f76ce21ed234008ab9376c8f889ab3c26749b377f625307a9fdd759100dfb0e058c7f4101ca30eab2247a534508b7d96a87dac465eeb270fd19e5b7c8a152ab61755307ed82338f1b8f41d69476025951b89315dcd0f1bd9b45f36f9ea50fc6f98dc2c114da1ba328bc62c52f34ed841a2e29e091f440687c2da04a64f50e51872330c0b853cf9e6674f279cac165f7b3a15dc0d2bcbac6cb4d27e8b129320611d411bf8efeaeba9e83353ab0b318e0e878d93fa9cd3ec603f59f81dee810a7a23daf565b6aec5b090da4e57dbb64b2c1144ff3ea1467bc8ac83f2e437df87b105d39b8edeed524461f680d3702a6265037ed6e8787d5abd761136eb6e16c0a93682a1921151be19e6189840c4eaf9d3e1e2b3cd9ac56b1435b72e414055dfe4d883c50e0325e7014cb12d738cfca779a61a007aba22dc6394d2b38c79c3e1fc6eb139203a2358e6f97a51c8fd90b7268a5d4b98e14f012a01a72cd700cedfcfce1bbe47fc420e7777df814d057737939ec8a5eb056603971d8f565830d0f8d08e5c03efe4df40e41bd34f349bcabbe7fcb2824506bb7510152d776de7fa0cdfb28f8d854bc1507121def452e3b83793878d08585ffbcfd7f40c6be01e966c08295e31e953da5d864e6d6c4d8100444caee3ccda26c4fbfda2fdba8c138bd16eb79c26939b9c725bcc7c7bc74c33c0b8300420b812e8162c581553484c71713d5b5d9bbdc47d5fb44571385cabb44798eff6d17b8ca9d916a68f0897e7d5a48c48b3a908a1ff8fac9f57301584bed1a01943c7f58f5eeb6c27f82ea6da12332052031324a6f1ff4e5dd9e1d535115658dc60ef6a22fe2cb56d6f73f8c79eb2cc57a2ff638718a75fafa92f73c1045e20c25447350c00a23a5b0079ecc8cc6032400a99d8b662f0bd87ba35d45e272d16b59dc79fb47bd00d51d50b3a1395c99588121b2bcaacb017a92df865639d12c5a34edb7fe248da6e785d56394ee11c47b90d47fb03a318fd11bafa3f624127b8b6534347a33baeffd7fa9f481ed5d1e219402adb697a114bb443359a013c239dfd1de14658476377cc34982ac8234f6750f408316073421b62358eccac67cc67701bfa24e5961df2ec69a6227bac59af9a5b29480c84bc7be682cb58442bc6c37a7999aad99e2077493015c2aacc5c79ae54563146550ea3df4fb9066539cc6cba3bb4a520fc6d0b375fd94b1f33e652e6196e041ef62f740f868c68512357a734c3aa35e24de245b6aee5af0d240d01faffcd7ac0bb490146e872d3550e914ffd6206dcd5c32ac4227b643def5af84d5bf277e3c118256ecb0d26c32f26c05d05ac429c62a55c2742fa44d9e7bf984452fdcfa7994a9a9d848f92e7d5401e7c57b241f3ccd7ebb0cd1bbf255c0e30e712c9ad00720189578b8779edc66a0f8bbda9dc26c21eec39530155862f11c504eecf42b5f58b5462d89934cb5322a52f82b3f4f1d4a391bf6041a5ffd6729375a200629508e81ca75aac78c6152b552dc41ed065b79084f5b4e768cae762b39c180f35a65b4ae23b76993e76a91e9d392535a99525d35c3ebbf8590cd84cbf69f56bf10235019cdcd7a40fce6bab58687e6b79b890cfd6016bfec9f575f0c0b616f93381c70cdb138d5a5e604f8390c27c318780eb4546b9d5c5a6c9f5d57db3d6bab7827257efa2e3debfbe692453e7485b0a83da8ccbd42d44c6836a7b09cabac88497fffefded47e04c7464812d61fac55412f6a8e394c3c51131a4fc29ece4b33a4a473ec809893a955ca655dbc8cfe48353717b251892a2e918048762261a318f86064b588e47e13ab936a535826a8b406a5e22075db72a87fb8f581f454c7723f75a5ea160f30f430416b67c7edece8208d4a1116a25c0c2442bf40b81399e60416e4e400273b4d716fe5e269168a0dbf5072c7a0718037b721c49f4942c82334e9381630f1e90b0153aa2cffcae445511c5bd098ec6f32556bbfcfe7ef5073874103b7ad325fc7406fc219d62cbb754383fdca4e74662ac06b90d2ce1333c093ed09cba08f77e42d474ae164f7734631e44ddd78c5229e25aaf870931553b78b2d52a6f788f73bba3973803af307c1d5068200c942d5ad65831b25a80b115657f4647a214f2d64b490f818824a2451c562493885b1bf03a96aac9a386fd0fba3a8fea1afd8d7fab265134f9417464ed2e1ec59ad1cfd276458371305bc3fe5c691366d0495724e03db7f61daf4c68731074cb6850bff7e2bd0b3bc1390e1ed66aae5043903f7962323c452f1ce63c0a4d8026878ab16d056f282a955bd578be9de434773f166276925b662a7d62daf0bc6134a63049382b074adeab0059bba20ae1348bac0a5753f0a436148be6e1c63d205a07111636e72bc5df53816d86803f6588306a514cffe14e095d7352e32681b161c2c0b6c6c462dab253bdd1f3f77c0be2946672fa5ac431f674e6922383ebb6a69c9c833584830cf12c88bec88112edee8a0624762590312655cc0fc3435117ac76e27d51123904779639be9b8f568821803d6cbba173dd3a249751add3fdb1d92d5668eb3eaf15b707e1022d4050f288698232136c85144bffbc9eb1004a83a0918449731b8a90fd60e4127c10a40f5de5904f0dfe88fede855fd74d63f39456392f87dd6cbbfddce843cdda6aed4095bd7545310107b2a78a4deea38f721ec3efc5fe8d03d20412e7a4fa8976d02082f6d97f5c608374ecf79b413cf39f6cc64a737b417ecc49e7b19bb1ee45ab5cbacdc877ed172d4890af276eb68ebb52bbd5167def5e6bb30164e6c030dfc9a29c157b373f8802205518d043e919938e01e9892493117d87b4b9f770091c7771ba365c29a3196bb535a2628f363de9b369126b07b5c2a96a9305103eb1633fc4ac73c5836df6920d51c725ce917e56a5f4d2a5cd43b4255fc2766d4ba335cbfba4f05cb2e29f6a09ff6bf3dac357c5830f058c8942ea382479ea1bb63a670b0e1eb950f59fdd60f609985cbb3557515ea8125ed1c8f95c91b6de42f80e55a2257bb2de4ce55ab267ce966c127cfb0af1b7703b605df1ad8b50f5f6c62f7e64846aa2af068a6eeeabd30a3cfd86ce86ddf0815125a6a42b429521846241fbe354135a6182746334b1a25b314f9396bba245d79f3a97e5a8afa2172f1a03d425e9d4badcdaa28c6cdf9c1d8ad790336064addac17c8426c5b5edf882daa71d32e10be9254560b6c9c5cde2d76a82fe15339b4bab4204291861b8c62dcc503ea93dd4dc2a4bf6a0841e8c776715eba5acc5369bf3f5e9f8775095419df8f0e2992036e5ed3f1eec49733038547319f52600d9b213e31e270115e1335079e4a01911ac1d2bfe0137b5a30f01c0379623eb44a08fb4143f80ada097fad928cfc28eac51802a4cb75e5dfab4902645d1a431597372ac38a7697ab2fbb1160f254945a27f3daaeecbf0b9d031204b314929a4bfa5ab7b44664f4286e81e5d7423ff27b106ae9e8c0092a93e22d71fd12826bac083deee4841bc7f455907757c47e7fc9b1a642d18a7bba4897c99df0995b7f600db4ed0c3aac3fe903d46a7981e81a19e8d089e4084cde9cc1db45d81af63e84f5acb32f9157448501338bd18b32cfe13208813ecb50563cebdc35e9822990d54ce2f4699961267df8566e8d0c239dc8e1ba61a67be68eb6689387f2de93d762aa0774750cee1db790657d56986af8c11a755c316bae44491949b72c2e7d4cce589b3aa91460f7b8285fe982bf103fa7d36eeb40c4c24cba8e1e147965b8678f0a34ff1d9b983f8e9d4d52f5775611e3bc301b7243d65884fff54a780d18e2c6f6e0f34519bfa064fac98322f276ceb62e7ac2d468044ef947faeeb92acecc3a4032813eac183c89a391b2c5e15361b347ec7b21dabb61444c215dd48a3a0ffa4967bc3ec03c68d834950e39ffb254dac3dcfbd2663e241c0706777211e8e986fcbd50f19070a1188c6aba52aa4b7f54224a2c5ecf7df8d6bd222bc82a332fb8a4878b91f9c285860a8a6c88a876a36884a70b32c89fe8ec45faa6a480ac005166fd5bfaa929cf88f36e2581ed4458452a2a440f2ace19488ae4868f1487be91cd7068c05d82915a81198cfd9860e0183025a06674f45f9d303d6fbe455095bd039df1bd2746583d5500023cd4cad00da0528af7ccc57ecf89892fc4e8e5ebea8d39c17403e049d9f3ea05f4c9d5202a09e96117ee17bc9418765dc1d1ff3c56042edd20bcfbf90486ad78423a3a0bfbc36eb3256d0226d4eeb7e5fa5b498c35f84836273555cf2cb588523fb744b25e7235858f39c9769b1cee5a055c1aeff6f1b77c6e83aca3c9ab835658736577a0ee264dea3c14d445e38474d9e146a4f4af0448238d996cf762d0e491e39b857fef315a728a1517b9916ecd0df03560e11f25fcfd84d65fa8e621802d46492809367105ed6ec90380c8cda4ad7f616c9d923d911639456feb551a213d4d45417410a023a4790fb7cca23fdefe574583168742d33fa975be095b7161deea2acd2abcc5119beb5282b07998c85e961216868126ed6dc2e94f4e7dd2a8ded6c7cc3a5db833a5ac447b611581c6d77eff5c39d54cbfee8190209a72c5c3419594719262f156b164588b0d7c7651202a69768b09b34588a32a9eccbe40d20dffc7c209b586b78619b536063146dc61239b69dc39e19b8a83d8e3c1f82b13fabf78210803ffd1a23592a1b6fabc5bb5411f2fbc6227985113c653ece97c93a8b6970f2a2d18b68478f8a46731439a79b5e2ba0a380090295fb8deda593056a554f2915bc32e2c5ca7b2fa88ea3f90ef1fd05b78c1ee5476ef677f632f7c2c48cc50b1b81190343afbc040aab88057fd79dfc58138f1a99909542f7699da788f858716880a996aae432a685f4c3857f78503051330b25f2b767601031e6d677eff8ab8aef271c2862fa92115979c5c02efdf4f9653dff4c7d9961e0e308bcbccae9758ea3ebcdaf09b76b05a3475579cb0600d2e3fc859f3ee9ba3c576af3f6822e812abdb012583b7629bf77f5128be2e03192f8ea6e680523865c11f634544269168fcd1cc39e09e003ec9001609335656a84e1b5633efb7532a766d6a73649f14f20fb1e6b7cf482cd759bde412fde50335add4623fd1629de45005ffa7e3a7b27d919faa8db39060b2c3d90bebdbfa2e29944311a0cfc36e34fbab17712575d5c7812b6f0869074fed006e53d149d12e57e77174d4ce9f66a712ec3171eb4854451417a4c98cfb9f7e2ee4d9c7798392cdc3416042f403a528ead350cd6b80d54aa152264591eb49cb697e3e64ca379d900f43feea10a97dfc7c1c9ecde08421b410b390407323ecb68a61f87be787e1b96ae45a1eedc5622d79c7e1277f449bf42abb9bb6d0e1472849d9ceb2b1bbf17d0669fd30d16dcc8a2a30ce090b00e11f9628798dcd7d398a6e7d77d5b49a6ad58076319eaa6507e3e1b9a02ea6a1b58a652373ca30748bedd8597acf7f198895b68e22a4b6a793258487a32fa1b019d51ca0f834e73343cbf472dda36b366d812c3e27647ccfa75b28c82901431c70951d0edab67bc817c1e44d660331ddb8544c8ef4b7c63cc8c2ff753b87748f8de1f430c00e5627babe7dac254a72c779cb6a85f089adbd4467da7ac861d099e10a4d9b7790a718d64bf7534dc71a6eadb217450492d4ee431ad099d0428ea6106e84c8476ff04031f47aef1113487f675896edfbf4f89a00173ef8aea1858c3782b5a9b96955a89dd9715a4c9a2f883ffcb16dad875d6cfb68d1f66930541e599c41b1eea1c0459074e7abede80a08158b984086e458c82849892f3f18f4c0db8cbe2934e3a75c24db8270451edace1ce538123167c86cf1c3fa078d07e226e0046fcff60f363f8b09a6379fb64ee2fc0e42d12c0e896dc9a124fa97fc28a1cf0e304bd8e60e2d6f179094aeb3169879ee39e849468501ccb15a64dcabe8ee1948bb63afa73560e57084640e6aa334db11e82fb404c67878fd0acb96c859c7a9e2a696d1b3fb845cc9888f0516094874f704346cd64800aae6710d91517389fd8327aaf24e8ed537bfb67eaedb9b0245a0265668e7f41ac73e8d5e13fb483a97b956e0ab5e4134459a41ab892e9050ceb7ee31e9f8a2eba32f24238701403ce7930a3a7400f5560ed9c9d0d8f7c6e811ba94d8f68d3b1711381b15da8852bd8cd57815b37f7e5f73ed072f2dba8d8b38e42a66134017a72e8ac1e4abb7b74ffa6a21d60c22cfea2cc703bd2fa848f3a6c42fdffc2f8666ee1950f16d45a5f8649f61b7c9cf402f792eb5d3c3350e29b055829264f347272f110b8ff1022b91a9704f0a35432aeab3f11daf8b814c23d63c58bac0fe28e818005a95b72198222eff7f3a5acd1b8440fe9a84745299925076c747fbaad71e3bd0ad8efa88138368f68453c5466145f658fce07afa737c0e704049a8f5cfc50fbfb2537e638a345ae0e6434e0ed1a06f3fba06ac47a2ab5934629459ecba22be07bf70dbdf43bbabf189c30bfd9546f7329a829b3ceed4851012e842b15ef4ed5d1efa65ca12dfe04bf76efff71083e179c04789fd930d332746ff4e224fff882dcb9be65e6887de3ef1928585811fe76e63a81063d5db02a1f2ce07e4b4ddafe5dfc31a71084921331b8fcc185e99061ea5f9b46a69db48124fa8712ad7c0b10541402f4458c424fea2c0641a3e0f66b211ad1d6e428fad92de1641705702530c285e096fdc16fcb6c59292a07769530a807bb0591d417b88a0cb583eca20ad4c7ba4ae4e2c15c290deac06e2504fd44ae2053e5015f29827651ab50a1fa30578b9938c06f3c9290fa7ae5963ed33f04aab3a0efa03605115e4385be7f5fc4446fb3431a0ee395229a51ad650ec719080d6106e5223a06e410e1881f344707d3b941a4d9a1c1113168f0b8a058f694001a111ce4032de1c06c3210020b40cb04ce8140231a300f0584cd01a04280e304a021fe9bd93f19f833970bf51bd442a259b4c4f4ac9708758b0950f152e2040eeaabcb65b3032436f65f6dabae37ebfb2e6f169bebbd3ac3555f7d1ae7d489b2fe90cb1fa7132deac4547622bc3b01bd3cab227d10fca6bdcd206ee252102e224b48f2c4ca788e4b3c1d0b4fece0394ad7093982815b50497f91a9c9507bc02eaa407dd42854aa3ec0d4ca241e6a165ad057e00a3ad51eb2b522681fb48a687612aa8719a54ad407985a99c443cd42cbe8137d7cc9299828a61bb362a73058f972a268b7f4bd89258a86b39ba4fe07c37c95a01397ce4abfc3f41e5071384df8f44600614666755dae902cb1000f1e2f43a2d3bb240d6dc75d908cfd64367b96e959edea281c56fc517a77bf150f40bdcb04e04819163d620f23ec836c39d51df765634a157d614e569e21c9077969e088081f710f1f5317236cc32cae85f1afb308a54ce54c9316b415ab96c5628b8c34eb76072c0d43a5842890fcc32e07d480b46f95e3b630d275b4c29450ce69d2202b01c6322ce21a1c53ff1f9b05f0059c7f99f5a81a9465caaafdc1dd24022d717fbe4b237c24aa9042ae04da8cbe2cc99b1675bd11070b6d2a228d1f6576212c27ba26405637544144942268e24392dd29ca85a089d3d335391050a3686278d062675272a764a284744d07e46428960ade44d891865c9130410aba250032fa81242f1775b211e748db43a471475912c284a2cb83ac5fa8e2445402344943923945cf10c49f9e5cc8914e8d6162bea08531294950229b10ef747c053218adadbdec0783189129d6588ba95dd48ad9b0e8cecac5a8e29914a583f29d535463a2704ace871475461411911b84e2e01f6aecf1483bb12387a26d7c204d34c9501930ee6e89305728578a3b9c446d12ea29e25220d1c083026f38c38c68baa01a2adc2d112521a89183033044de09d479e0ba0aa2f54d6cc5d32465e394d394b2d3b4c14c93e7eae04881779b69e4a431ddb846c199edb4a56c7a65d8d360993eb85232cd365939685c338f2931b63bac28bac69750badb7114eb1a0e9edbed863513626d30d609c9da6b211a2d901d605301111ab531056bc980719dcada21b2c6c0580f47acbd8635021eaec6e840bd98aba466b1a690fe34e30e81360fb34ea6230789f10729841662063194f1ef592fb585574aa90db1240604f1569404145740479d22aff88319ca0563ba175c7d4ff3ed15581ff96b956504a08da19a0c67914e836bb247ccbc4aabab8cd1665bc4e45e9688302cdb6d8d96d2267c19b3291b75c3c73324fda1d7f082fca2630597599b50a7fd1339b22ee9a05305111ad7d132d0dad74dfa4a1601533d11e2658fc1451eb6095cf69b59a02968294664b3c65bfeb59e577ba86afff4f6d128d835edfe430ad6a948722842d26f5d0d0545ff8522d22775f39bff8af70a3dfa9a535acd6c379fd069516629c283ae58b5c210cf3a3b6444b6de0a155e59e8e59fa0d9f36a7ff2c64368691ba1948dd541a69b85469bff506dd8049f7c97ef75722f0301751ae98434ee8174f973ce2151525d4326fbf143165326c3495314d826a3147a2bd96ca1243526a798ce4a6d54c425aa8609bc5399048b5600889c4a07c5437c5125e2624d2545f540aeae26f141b3a6827c22a3d2a970905b5541e413bfa353600718014d4d64e94146ad66ea416f41b944ecf25d1063a5b961ef74156a6f84b99512fc50f7cc69ce203007374b56058bfd1aac50a27c66d7646c572b3aae803af11a95ca562084dbe527a0326ca0096edd3ba0a12114a0fc0bf929c7f4897deab524c6630775e80b69d91de273a5bcf627916517a4130ff21ab86f30237097e2f29154f3a6d78df4e1cc4bedb92b2db44a548501413d8b2419e525c051224cd43d70cebab280ec08eb6bd57c5658a462a676a4097b4a71162860012990bc0777f1c2dba2f8cb8a0602ea19a1f7ea777ad0b787b759945510037ffb318caeb5e9bc5eabac2224aaf0887d2042b75df8f094b20886f11c104504e1e400ec021f9cc053033a4e80b309b61c80361bc8fa030613b854078b63a8ac002adb30cd01a4ce10c11c2075e1e1191cdba1c92830b386a521289005899a80f00a876d6064108a5901d10c064882a01604bc01600bfc07810f05744980130a360fd07e818c3b6089c0253d5826503d0f146f9872004932440600fa1a1ee6e0240b8d6c60bcc3f215148621c91210a1e03009c65f41422c0d7917cc753426ec9a7c6996ae966c5d902c5d72d534557733a5a14435e7e96e34e968a526887441469a00519b83eec41fedeae9889ddb1847036d7a16cd75c868d92fed6cb9905634474a7b9b5c4d126d24d20220d7b843036e34a78cebb8d0da146d28718520b48743c32fdcfe049d3f68dc0b2e7a40e706f4f76f43fbf9753f4bf33dbdc3d4baf29d73376e45dff9da9e7dd7bd4d43ddb3ba2d33ebd86ec359f73b61bb4dba85d22cc07455e500130580b79fdb005efbc97b8f7ba1200cf9aa2efb98d416eeeab2895a405f5c30470bc45f7aa15331c1777f7bc06b3f79ef712f7ae649effde2058fd2e26085201a9e150747c9c9ba302e5e1144db956009133d3b30ecc12fa2390e7e19fe1a654aea3c3b09fba611a914b25c380b896d216fd57ee54f10163dcf930bf83427f572a0704f6f19d878a0151d61ebb19f7527462b524024841a64e02282c525c88d0093cb27b4781ed2fd1c1c335ecbb833d117f39bd7c367f15f55ba43093b26871fbdaa213ef749341df9aeb159ccb366e12c94141f91ab0f636bfc93055b4724a3064946b014c13bfe07d29efeedeb241b3cee505013c7bcb488c6b91b7a7ee3f3ad0c94d1186d1235b99d85210dc664a79919cf008741c537449c2d5a4c0b595cc010155b402657583f3144bcb51cf3b83d5ca7abd18d4ee2ee9fa2ab5be42053afcf78870988b05c9a235c3fb2e0d57f6f323ad26c00abc85c1aa6849365ed0f8fb96778fdaae4256511afff1272876bc3bd9ff8873c683b081a8f2c2c0906dddfbfbff1f6ceedad5b6d6ed8dcbd75c3d6d69dadb7376eefdd6a73cb60f3dc0bdbe4a83a59ab79dab2ffcf5a057f165cb92ee798a76416c1676d2b1b1018f501e555b51dac505e6b03532a77d9905878e8f84f7fb090904c62d85799963527ae3f07ad82b92b2a674ad8c3cdf54fcd5725f21637564631e3664bf8db383141fc017f75c5a794fe72cb48fa0f6c13bea241c727cb2666b834d1052e39b64c755e1495f46cd116ca172167ed03f166e40fda9eb12303643ae527de33dd54967724660019541e1f0e9a04248970aadc60fdc672d1d4b8d0f562d8af10e3bbeb481d2164f38d28d57630d237069f3174375c0fb3f1378a3b6cdf722676b7c6a1712499d9bbc8fc5f9b567d9088dc75ff7a941553302e1add2898e1da6430285a1a3138187cc79e0b3c9b28082d096e48e3f9eb17d2a55e812d88e90365232dd35100f22ca008b303dc67e448602d839e86d4b34155c06fc5a90a853426626e46e7b64e51f34b14a937a61fe198a40b6ba364fb1dbb2563c47d14a190dab318a252071c1aeee6733113ef879911ba26529c87aff14490db6542b3dd1b4a5432812b47c8af978395ac92122a40057afe2757d67f857183ce91dc10cce4239249600116e10ffa5e60890b9a630342d03f065f0195128b22137b597aef57d8b6a293d6569c3f75477fb18ef9466f77f43552f8fc6fef020baa7ff1b4b61a32356076dd495ae48ca46114d1fe7e043cd2b716bd4ebc7b1ddbb98ddf9ee8dd340309cfc4c5cda78b773bc8c68d04f78052bc8827a88b73d36db32eaf409f92f1c2bea30d16e2331a31a9ed85b1eeaca3f5351bd69433e444602f1b5e4398ce7e68a520f0492dc4a7f51acec0de2bb7fdd57c5b0c8ab08542c0af5cbd63f63965487710fed9c16736b345a9f49f3476c7f40c519d73414b13333cd03b66600d031e3f813556ba45dde6a09f8d7eb785d0957c0bf08b21f9a9c8b80db4218e2eeeb6a27f62425117ad6574b971e4948ee63c623c29f1f7e367be0d9d4718aea324831dcfbe7a3494b1e3829cdb07bef22d1d44d8873234d5da2c80b5132b3a01aa3772d3f19ed4178f5eaca2c202575fd34c7ce472eb99e1721495ae80be874a454a2482504284e040ac81c490e1eef640f7fa855c93389023a2a3145e60d4eca128068025ea315d4feebdce80b6d933a6bdb8e28e23c9c965931d0831c8ce165846b05f57f1e72b448753c25fa08baa6b518a9fec91eca62277ee405c07631f750397ae499f822e5fb7627d11e0ae56ed5e15406b488440a20388092e13beb05b5d4823f79a86f1865b44a3879c023ad1ee845445f02657a509353ccd888e44894ab7062fc87f3276f4a3fa3c3787fe9fd0088411414b131f5c8f085ac8a9a5c8c76274158164f55f1b68b48a7bf9bfbeb7ece620f5d9351c3ae52fb5c4dfae3db33409210df62e52611b2a68d48b2845c2759183ca8fdf9c8044a0112f2329ee8501770fdf8e9cb9680e501924a9941bc7e0d647d2434c39c5219659b58a73f5dd722364349b937d8fa09682e4d492daea4011b63642f6f25091b38b94ab99b6de0ae22cdbbe245d4668b32795cea3b8ce60dd878da283281af52917773414d057d7f3bcd159c7d3fe7af0a5bde2119000c0e6cf62f65af7d7c095ed596da37b93b22b396429ccc0871b5f82fc6991490225dc74532fb874cba102e4528cd22b600756ad9625efab3a600f23073d5355e3dc1a1cd39d183d47d8cf22e1ff387622a976cb6f38ebdfacd5525e229c932742d6e0a36463c8418108e7619c1837d9644251627a6e24e7c68b704adab6f395dbecb5d25aff3dcec20d94a40518bda6c701ca10ba3c8060ce27409fc68cfbde211de594b954f09b33a1f56f61d5007161c516e6701d96178939e86bd0155e2b98e9cdf7d4dddad166ad7fcceb86120630ee9c01d056bb7d4fc19f134a2918d339503a38105acd75225a1d5f98d4085cc0370c2114a5274a5da886873ea8682af507fda575d92d7bba6a6349f191c702ad0a3af4faff1513acb150d8eaa91e82048f63b44f3608415f56b4d3c781babd71f811c2ea3dd0d61d5fb5e53d9be1b12c806c231d20f8a461c9f374e10036a036093ba816d3c4a19a55526ccbcb25adb06332f368ef3e9dbc6cc65b126cffc2f29bc6a68416680b30bb6145c8a09cc1e157bb78347e74be5a713f876941b1f818e2fd8931dfdc48ab2a62a23ababb0d6345b735d16fec688ce9a90a222731ca3e3df051a1de0c57958ef96a52e67a14524da8bc40ec1f35dc55ecd99477b7ae66c72183c0e05ba2469b694b0f80d436311e9372db0180a73774b26125e4772529d6a214af658cb6b531174a70069306547e575aa818d9a02802137eb78c0213b306ea395e21e3d0eddf24f8da0b86973e042d16951773d4a0ca6147905da023a6090ee5c26cc53282a26a9be8c5168099bc8026347d2a3aed78d605953abfc479f478343c93953e77d8204abf8b7cdc5724e2a027844004de6d9caa06928720239934c33693c8dc7db3c4786412b08df4cf24b66bc04d7b5c091405a65f64a441a3013e16d50d4d0e26764eba99be204cb354fb897c2cf248ed88d148e6425898975b4f8e3b347748b0e077b3f3571b78aaefe615c0ee566b92136aa44668e1f8c7b18aeb96492b4370ec625b2dd6ebdfd485ba9879a509f1a6d14058b090e1a026ce21cac340599bfd90fc74ef376b201ae61d87af4004618c48f893aa40508f25776429161607bad40ed683430c70251f0bb69f98e4c8016c446416dcfd3c32b71ee91ca2c69f95b43fe4fa43a4ca17b05e90f1cf0e53246d00bd4a55892470397cc506b835c1d61b5c6b8b1abd5ea467d6fe2dfdd4b5169408cf0244df55fbcbf430a17c6a3a52bd998298dc029e1dce645848284a7e80815f07b4915acd043efa201e1c681c0dbb0b72f59523ec7cebcbc3c061a9e5b6c43cf38cf4866a64a22fc26027c696e49aaba230768439abfd8443348a75919c6d0f8bd3d5cb058d2c2a9276a017dbe4647b4eb07ac8b95f0946de6bebaf9376f4e325b164bd84ba4c89fe2c95eeb9ecb14c51502a934f3e512e56090b7f8cc6a0f16b28715c74ab274c73c415e508c32e5fc0cff7042a03c91a233a59d7159cb062ad05f22fa918d8720c2a847d0a535eb950fbf21004b986f264db021def6d3046644f08954e649a336ea465928e2437b333b4c8ce9c11f750c8e5f99519006dfe6a8bcc0924fa60b24c91fe6fe3b87c5a08870cc713c3493407cab483516de188da2900be01e672da2f058c1d11abe0e3f8cad7d44e3000976d38a6140c4bbaf33520c3c0e393be06783392b20fb6d7e7d94e5579aeddd216f26f4b0c4811a9c339b8977cc21f3e928bb74841b40638153a7acccc48c2a8ec5100564ec3f6ac83a8faa113230bac6c31066b1ca1bc9cfd99482857607acbdbbfec5c93615ea33bd64fed3ff4abf31892019a7bff79d30f2c451afa94fadc2d7f4e3e8671f498188f0d4821c50cf466ea686bfd1dad111741c4e7d8455b24ab8061a6c9ab86a269ae8d920f8124b8dd8d89106eb7478d3abec16e2443a9dc6f4a8bf0ce7771973bc2462f1e0aad72268141947054036691dc284bd206e1e4b9a9259f3c31d1d81bb96b079ebd21735b49953b99307d552fa5d42262c4615d825f54190aa37a68d281fe5586c75ae492419b496584e0e330f0aef1a3c8d57e856ae88e64f1576cd9d8db7f60e25e4ec24bfbd7e2acd89aaaabb06d20b19176bc2d92cd2e18691a57628e4a5f209550a9571a140bbe37c1260ea944197b77bf27288468938aac0cb7cb9be43525af2b4e8f99573fbc91700b5d4e4244f453b4f4b730cd49a5f4e8ae07bc361a72e0db0edc7783c49b3e3e2e64b28793ecabeaf9b9a650b2ec21710cb6b859cadd782a495cdb6212415e4f848122788e819129820a5d390e13719cfdb62c921abcf9f731e3c13d7b58ee18bdfda984cb360bf51a3fc46233856ec4d5165f090f7736d65cae44a2133c33b2dc04ee39f2d304c38c30d5a829327d4087b4a6a2bdcd88dda2c63f556afe3a71f112a2acbc20cbf748f6da826cc50f59f77fc7635dc1d99a3232d105737c13531c9d728119ac4ee9d7cd683a89d5e428d1c89e1e19bd8e9aeb321c74c033aac247807834b7b4a40e27fe5f4c1c12febe03914e22b9984ff17edb236cec6aaa5f4e549fa4ee2b0737168d89e9587f3c00ad5de7108142c017b6abf058ee95958da3b7e36721c1e355b40e2fe9b2e8596d41d67dcaa53790ebbf78de66288328b2f3159f70a280c279a79ca573e15c129cd353b210bff2b0e141bccd94d5fb741183ccd461f4946a6ea74ff7ef38d5b36dacb3d6b93bc0ecd21889fdfec90ff4986df7e4a6d5b77cac0d391994657a857e99b7270fce3d2c25273a46a1d9d756b2b959aad0dcf0ed9868f29b9ec9d30a8c6808bb846cf9413d1600c41506b935aedde0bcb6cb1871463f4369d978fca18585a3a72a3a90e2ab60674e2dd353e05057babb6e9933f1ad625c6408eacdff554ded35e579dbde9a5e0b495ca70d344c46913a644eebd6d383d4ceb50c98e1c2be7e13319217961496b50a726353a7186d7b7dbfcaa9e526af1de878b99f41029c89b80d43b7f0d679d142a3ff39b7100810636d288cb750a425a7d64b1f2db8fa0728dfbb05b21e74514c636a4f6173eb436f645d004c578295abba4f60611d6b8ccd9305b4fe11e791c65562646f3896f35c9d4a1c055bcc24a25e297f1f7b514fb6b4936639cf376e2974a9e58a02e17392739ad97beed76289bf3767f8d9d3f333a46b090f8f941e0ae9543e1355e4d93edcb64fb802609c3789f24c198492dae7bc436d14d048175717cf98f21c320a0d39de6de6b87ed160113ca05d1e7f1ca9d0228a5ecfe3fc02166fcc8aa4e2fbd36758379631019410cfa581cf68e327b756099551bfeca6d9c4ce4843f2800448c3aec58873a573437cc89ba1269c534af995ac0186da331bb0581ae498a91f59c565c85299fd40a5a2b7c213e9307a320a51fe192455d5376a4c90c579ea62b849e68f688430b41af712cce30852fb7d060f3122cd83c38d04d9ce27ca8603474a86c7774928d2a96d22c8a6849339e7df0328fa12212ae2ec27dbb49bfdf3cd7aac0173a1ef3c250837da39a53de66aa5c6fb6ad00f5f612caf37b8a195452e67fd03fa37a76eefbb4599e2d77331668d0e35540890b2be78302de16068e5241e07d1e33fbfe39965e4f53e72e863d848e31dce7ceaf7a26853475ad13a170f1417090ecbc4152b912720bf5d32a823186dd618f5303074e2f40e7ac59c5832337afe20b2ef46d8633420624ae7b363d741d8870011905051093fa68614a2464c7098cf4a8111cc7d8330c7a1b423ca539861fd389fb9dcd4adb57b800f090b0c592eb085552a69a22f9465437c06ff6ef7c0f9266d4993aa5f38d9de8a2cbe2513f8d51400d501fa19532901e8ab082e2cc4c99b57884dc40007b2e7e6ab2b8a18ad2307e70bae09d8240412be8af4b4dcab72302338247d915c8e00f4a5762a0a06a63057a50f5d8558b01aa55fbb76115679b71061133c6a35e101d22554e0c0d444e434dd6375e84536b13cba52fb972f91bc6c8bca5a443fcd378154fee325351f429827c584923fb58e19e24c9bdb5e23f11913a627d7f8dd0dd745fa821487f2aaee861bff3d53f4b31a3de2b4d3996c4de98611cf6cf34c7943e3068192e4546090b365ad2e2b725f4b8ac05216410d180d0eedcb5a1218d424c4d92b04a6b15c842df4494976874c89dfd2db4cdc7a22942e70a9f8b9713893b23904ced07bc240b8b873521f6eb26ce3e1e210627dba353f2000beb976c180678771a6609395478da7ed6313eaf8e8360dbd823ddc5186e3f690399d0610fe4b6055faa0cba1e2018c5b8833e2812c17120069eada3827f903f1eebc08e4c763c439a63cf4fd800a654bfb4cd5fe40318687ee738b44d3955493282e8149dd80eafc7277f020026020b4d231f87e8567001a71663d2f23ce2d8b5e15ff96ef73c4c2fa8788ef0b940751411aaa43662cb94f27fefa9c6b602d7732211a8d9d4eb416a413d640328d5777cd241dad2e9996e204529546164510b1207f22b23534652e489ff6facbb10fac92f840c44bb7284be98652fb0a2a5fd9a109e9bd18d900f58c68eda36dae645c9f0be0be4bf3859ef64550ac6d072c3fa0ff35dcc33250e85b74e5526eaa7741f386a0354e9f2b94139fdbfe1ae7533849761f3baf263c52d802de1eb44059a4c7cf5f69fd8c524aabe1622f5b5042b22544ea14dc36f8528bd9253610c87bdefef3d99338d44a3cf68cb5fc5aa06c01ffd60b4e75dc06d3293c2a6dd8158d4da31542d90ca683139dad1a0dbbbb736d3947633dfe93c0930ccbcd800ded024854f04b0a782d1d755fe133c79951771b0c7052acfe1b8b90971c0b0bc1ac6cda3011bd4f72cf80ac575b7c6028408763735a201918611143204286380742791345ad19b6c078905acc90615587024dad1939c1e0da905a115b09ed841868980585aefb096688fd47afb3424a125afd0c3b1c2b769b358fcd4a7ae6b4e43893572f557a67e290ca09116cbade1a04a0f52105477cf5e53b3921050aabc7aaa79a84c1762d55072ede7ed51114684d9555e2d649a062f7408b4ba978482f32892effc24921100fd2aff3d7c0707221552e8ba0ab72e6451945c39ee467952e5dd8bffbe0df33d70774bfb94691304adb8b1d91d149a3d867482311e88f7639103362467cdffdc277a7c55d5c1376c6f515687ab552463c37de7290f36f246dbc7b77689fd2860088eb83e8cab4fece7d878b693470240c79ff45473214ed3590929a9931f104836215b47a3f8d1959254d13a78cf72abcb0428b141d0deed4004b3cc71859604dc0ee7b3a56817c23860e7126bfc71d1a1bd3f1c33793b62993283e0aba47b44cc8579388a4866d20200665ce9b60026935d150a8b287ea975df276a19621739e8c4f03053c59d8991743642ecae272eab2a5310b8a52ff7700695e34875727e0e495dad3ca5a2a7e93afd3ca70b00f5bbf3f87083c1eb03c878b677f4debd421fb0e83cf76b48ddc910e70c5519745ca6cc6accc6b89ecf0d9c6ca83f566b6a112b0c66a2d5f72ffa1b43375b7ad0f263b88edfadc848f84f2db505259784ca4cde6735dc03fc88b0359608cda697ab4072b52111b20018bd94a62ab1b490a1419bbdf42a6e099d8977f6fe9ed98ec98a464a00e547d9c6a371319a41b538051fd1875175d598f4154bb9560c6d99249926fdfd4f23c80c11fbc1ee8dd1f36a4784d33386a405b1ff43dadb54efb5400b32615819284e4aad6097cdcc4086f981b5e7449b25ba055faf00b8aeb8d3b03391b273f1c18a7c6fd689ab9417c8698ada61389e5f2d3ba8e67e6236ac5899d10bd02e6393594e21ea8995699b06ed6956745a2103e03f407b1c65acfe17b468ac9b071bdc007a3bfb0e6a11b439a45adacc1f5206d642fe57d8900912c40fc9063ab1834bc32bb3960e0c59d21c303764d44e3e2fefde563e4c9e90833d725d795313cb434aee3988a7f261b430b43626db807eea8fbde06b74cc9b1a28c71e6d7351ca8d1df888dbcead0f30d12599edfc32a88fc9617f63c3af436605a83822c224e8f1617b6a59388a9cd216504103f69f293ba3a8e4158625189596fa573b0f0db8a2e924340b0d49ab21df196197ab462d3ace340355fc73fd661af5f7341019a13f22ee91212399d9a16249971c323e9f2f4a0e9836706b1850ba467cbba1eebdfd654564482cbb875b8cc4929f6d77747ce9be48371047c34aa5c134dbaa0d3779159de43c876eaa3ab7018081deeea8ec02fbd56fc453fd1e2b707aa1f53f9d6cf6de17c4857abb25249eec4a0e788d0bb6e845b8871e3e8c70d6c6d1b20b11547273af0b36f14c7fc071da6576e35a5369e1e47404897fc4dd2cb502f0c11309d94d07c99613cf5b7a4e45310907018c4d65b342a1e1257453231f21a388b82381329d0034abcce6f1e61ae06252f3b90e3916eda6fa5a85b4885026a5010cf7316a961ef404fb988620467a1424670f84b624206018403ac6cdf62e3817595205ba6cc55662be65b0dfe32e851e439a49564ab8d44c4c8cdba16187aa15b1b69be120114c65c7bc1762be265df548988a9071747544df3186cb541bc6e1688e88b2a1141d20bee962f3e907879e4d7b401b9d20d6260035422167ffe50bab7ae126aad83fe8fa5083075844ce187d51c25e232d15f4dde7d04bed9714a8057ef2ef4859aa569843754f812b45137e17e0f8656857a28b45648157911a43e08aeb8437c01fb04b587883d25407f0b37f8375549bbbf5849660352962acd01dd345448a00fc4b44b8a1d68d76560e932f84ee3c17d19acb445d9010490a8454c81570785f735031d80ee1b0fac54079d8f4e73d0e880fa5307f1f34233d1b083887640e690c3797dd9df24bea8e72325622bb75be381f95f4eed8bcc969ec610e671205432a9704d003c5f03771713bd71d9d1cd5d4e10d8857450232bd4749a1cdd3432368a61a8ab9dcc03f8a6279fd00532c667e0a490616840937789e83ab6bfbbee43aeca11e991495491e17abb572375b8e98d2092e3c41340b6f44432a816d42e1f7303ede4817f042d3dcfd05382f4a8e75f060466c3f96ad7d77543fe71a774f9354439f1d5ee100cc9a98dda7867e028eed614d687b5489662ea7773e0247e2e556f15b501e1a38c2c8b419bccc358aa541393c29f2c2edf42a5a40488dc0c64c227509e2667ae1028a5193f30d3ec0833ab45e43ce7412e926b6584a005d1d21dedeaf1c3d413b75e9a7137185be258362e55ca5048881c9f3a7683e11b924133cf468925cdc0c28e7a7c4f4d090e46f7a4e3e256acd689f959c146cb99ef5831f72fc4ae8e25c5e1aa4db12f7b2130450552eac44a9e0c0093fdde2182d6445f380dd3ac1a7231da04d4b1816c987a9ab820277703476348da53dd08e0887319f711dc552caff8b8f3f060c055a402e407607ed03606e7bf461c6b570a60bf40948ba4bb7a44449d955b041f9340c3717e5d7d5d7ea2b12af16268e11b5e6a5ad51db6cc5f2a61acfd9065ea1f0264b6935d30c6b69722f8fb93109737f83242524f725c05b0513ed3eb4b4d1efe3081e426d274b62e5cea37cc164e2ec46fa62cd9b2f84e64c5ff1fef4dbb5dc35a27c35a39bb87c186a89ec0c42e47350e9dbdcde049cf3db8fae93289cbb56200c847d0e791cd5908a2d06d9fafe7d7ae1cefd3d54c3ffd17ea91371b67f378591df6d1b8abcf54cceb92f7b41df59266f4f919bf2143f876bdefc34fc78e66b39c934fe00ccc7ab1ae9404713f7aba840ece240eed159fa64cedb76213e3ef5edf65e35de512f61c746d05a8005b3eb61d333c196686d26c82337fd63033f4dcde2d0c710ed6e4a625829f295749d35e3c23d8dcb0c77b35ba6dd0ffa202f5317ab97f0a1b5df0a2226fbc399aa9a0bf46b95f9dc06403f511ab176f9ce4889de3461aa702a2d19f2489798fadc4ebaef01a6d488b4986bdc74bd7fda124d2bb0963fea47a460fd0dd4a58b772768fb2dcec032ebf8ef93dbed160e9b35834efaedb0f9a6aa4d1bea58cc5ef659087b33a68d223056c0e84f9a17fd56e996c7e87889c936020c80239ab86ae9447cae6aa9688ace516325e84dead915b98a8f15b357a8cad20e72279eb005af26562b8a027535d97d1bf013b5d4af552f667586e4344d355f76380a028acd08f51fad1995c71e7882c71ac01df744fc4b702428119dcd09db906caa3d9adf02a324bb6b0b63e99d4271ec5980bfba68867065eba32ae06241323afd217811a774ada887b9b822700916e1530df53626bfe84a8ad41b7e6c3ff40eb16b2bf8fb4ceca00fee9deb35343643bc21fe71fe8fe7dc8431284cad82dba817665b6d35c2c33bde8247609b0322dcdaae8e6b1d804c7b12b6a8f841e65dfc1d9ab046073c9953a6b97ed32ec1db415ad86797207e9b93c979c02aaacba2a3bbab249dbc1800e355a1e99dafcc439d38e235ee95b5aba3035aae5aa56b7b065f8821fb2c870379378e2914580ba60a28b84c7570d036c682643d32d0c92e1550e40a54529bbc55f6b04dc468cee2f20d9b40c40e02fccdd90328eade4dee64de4a99e0ea2deb8d6294ce0d490423fa0af32ed7ed7f4a2f68b7bb2962783e30ec67ff73e334160b812e65a1a7e4d611150b690b8b08ecd14f3ccb602d73c0156f2bcf619f0fed018df011215d416b202f838da6687b74445a8371d3b4d287b98f0b00a7a2a728d0294d2d2510b06d37bdce879963b4131e0f393721a72438d7b9cf0708d016944c35a5ecfc5482ef51100e3c14f000aea92a4f2c181d4e97f0ae9924054bafbbce4f0a350cead08e36348619126a6cc2b1247a4fdf84c9acd0f281bf38133607667f9dddc10327d62d898719c2731212ff53f93200d8a936b5fc3a2820088b7cef8471e99bd00e0e006a51217d04652cf25961c31f1915968ae88aef44ce160b9313bd990f11009904e9efe1b1d48893e46b6766e1b1b4980fa0e80cd40c5fc01dad9f7b0a215c94078464f69a23e0f39a493cb62156767d79bb55aaf38488ce49358a2f518465756cd700ea3039481cf4053bbb762cbab09b2a202e9efd3ae8039c759a33b726df5363d63f760c39b856ccb70171d439393e177f400b0c9afe1110e6e263bdfdf36e0285ebd63d5d16d38afa0e527fb425b5d36d38915a9ce0aed1f6376229b3830cdc1bd5bb07e43ca5af82b4b32bd46ba79066cbb022ad6a4d17cc3d51f16eb75a25daed55f0154d4d037a4fa0aa748d95d31343bfce71318a933fd8cd6834c48ca3a9527130ac205093ffdc3e86b04562e743f0f8b2f1d24a823f8913602f1393620baf51aeb2de3134fecc46a8ecd3ee067ac4fe6e04c4f9396e5c72ccb162cd45978d04fb7344462bab4d70da2e1e6b224d94f65bfc0b2ad403c6994738c43112da60c9ee15c16012bd4c10891cc05325381d7accf3f001a4425d79afd298907af24b005ead6f994f7ebc2b57409c5e15dba2ae58537239896f31710f0ca62ee2974a20fbc82e6c42d98fa92dc318b0558a4079487d3df067ac7625289c12455a7aa97f35e4c1e8b7c5721b3edd4548b2af079e1f1b50680aa322de550203ee4087fda5159e829108080aff8cb065928e97de2c13a00aaddf75812046aec819e3a2c7f2844c723e9544c88498ce2556ca6c4f0ec279f986ec4bb05e8715ef6b43d3f4058a5a26b09f811f2ed2f6004b4b98cf3c43e4a9c1d15ab27b5841aa6cf4e8def68a36210903b91a887a8d8b78c1b8b94475cdcf46acdcbc94f7cd7dc54cceed44e5d9a3d45a62b7ee983db16baa8ad2c4b4f1711d282c7848da815874bd08082ef849ebe17763f468b166147695d162bd05287a9e33a44b8cc29222125349c05fc57a2d326e0476bbba250315e655b08f0011d0c383614d1dbac4cb7be9350550c2462c1dbdb0ba6aaf0135655203dab8aa3fa8da93171028f84266d23a5697abd5005a09f19bc07839a0745c5b42e1b0bc1f0dbaddf49e37f6febefb4440ba2bdd69f7d4c0d45ac64c4647fcb04f6e7bd85d7bde2ca00a8ac1446da25087534224514f489d71485f98b2950a894c1c6bae7362e6904c8ea286bbdc853f5b75ce244bbad7cd417063bc7ed9fc4a8c80dda188aa27c4d695a473f287ef0fa9b4efaae486abb087eff2c2e257fbaa7afb16bfd401377e8f9523563e33d1d5caac071ce0157bd661b8422a3b7dad4d9040c299d1703d5e92ff0c3baffef04c03fc2b757181baa73bd3981681d00d4a4bf350c2e35ea275ab348b53c8175f8ce769c26e0b537f64539411ea47f79338abbacd386fc2a492d8644053dfae08e3af8aa0dad7cb211ac31f578d7c6b095d5b9f2a43513ab57321f1174f2bd2154d41004b58e8027ec23d0ab0a6b8d9f371d4d62c014e412957094c70dda95dfb3fcac82819da86530c177b8c93e420f7d636b13fe417b5dc1ee4ef72a8b2db2acbb5a429127240988041cb2e7b3870cf6d390308a74f2358901df4a70cd23b6a27c8a7b4a98882700cb330d12069d6b1be75ff598c3ec1bad30307e721c6486bb41e3dab09fbd43257b7564b4c1b1900ef992403bc409c42660cc6138fcef090109a00dd4896468265c5cd7f210770064dc4302c1e219cfafe3dca232d12630284c8854870bc316b45962348b5496b122aa0d865ff31148cde174e15c27e6b962ab090c1ce7a24d3e2ff81fcee6a42c7402fc0f1b7a040245938b64c1641d8114d26671334721ffc694c8d1a120725c4f14c8bc6daa4590d91dc67440d60de6bbb2f793ab570c0c807bb332e10d7429ccc080a8592b0dc5024972e679590b885fd0e7515b40f046f81ba9e076e5ac5ccb288158f3d96c17e6c42fb0a5d13fc8dc226e2d5cd3a754c14890bc372bd4b46593b6c6c0c0519579559401a4b3e040042123e804b52ee179a0803b5ebf01270223ec0284017380aa31b56657a62c4e955a6725e5feae7b6dd8c6698561f295b3e6325b13a477508e9b6db84b25528bf5fc6fbf9b4aee1280ed9137198c28f1ec6e97b1bf9495e15a1ef0377003cfe767c99ee512a5c9396d45b522e11b3782e7a1208f0155cc4f88420858597c200f85780cea623f210442c8caf022a22d8152969ffe66adac5a40f5e5d1cd775f1a237d6aa107bc7769e859513803d59a350ed6d85e5b3a2c0cadfe82c495a972084403e5a3dbfa80878cc0eadf650c44ebfd03d2a4c0a18308a78cac874a2893b04c42d2e47c9cef9b08c1a9b6f6ea44305886062ac220415f07a287e1491962d3b11049513acd83346d67717f6418e3fbadbc32b6d58c199a19e9b2ae170a38e73514a525ea0354a726050182481f1a8c7d2b35c411c0af4fbcdc74c8bc362480136cfed73a097ae86c356bff56428ca888ea9cdc1064792e7ea78201efa60a6543c8775ba1c80e27885c5e9074030e0575e10f83c985146315f79b55873a8ba05fdba78c08ea624331fbf9d296ae24aa1ff97f5f47093dd011014233ddf5c8fe8452c141311d2817648584cde558e67198247abb3686bcc0d1195e6a95377969833f4008003b07ab7dd67b06a3d27b0c22af38f7bd5184276227a396f910fff87e8bf2511638d381dc47119111441380800ecc54b1b8b293e1c0e98302c1f76ec752b378417c2576b8c1005615c87a8469fe5ad6ccc589ccb37656c4cf3b3fe6d6416b1ed466ab6283e9a12679c57ac8fdfce0c2c80d91b3c03a9d8f67723ed8e3958096d987081999d11a8001a973efed348ed914801048096e74d119c393fa280229de1ca35e6081eb8bc8c24b5b83e09728c58cad80251b4fb3b29ec3be16d8749d4a52980f637029432c9783e74dc976256523b039f6705116b388247cfb40eb07569ef48553c83df51f37ccf9b84f540b87d28880b77156970a19a490ac9adf38165d25f488d218f14c02d77a3bc3cb2910ddbb4dce317de04a0fd8270404202ce4d2820f8058cb2a7f427b230406499fef63285e84a4167d9bcbcf41f9d418cf453377c862dc0dfb90e8305635bd366c7a5de9c41f6b69c6f0128fdaf2f9c472c38c404928d90bdfde9e4ed7b4a522af57976f1862ceec8500c78a936b374e237b9e1ebb63b3bb5ef78459ce5b01b6c7a9af6b6f8b08a0aa4cf38dc18c4db137722d03ac9846c673e91e724203baf80aa3a8088d209f63e79ad2005c9e2d207958059e26d2406ac7324d0d31f245136fb583227c051e8394f0fe4885c974b47fd2779a5f5d6532a2d3c37a890d0e2e9b5cc425e75823e006a4245cc0f45b7fd1fa2fe69b605df52657c70d5f878c4cb8e0ba98e5fb37a85b03e80673867b7d0a9d748a43dc3b8f53bad7b212a0882ed925fd1ec5d18f4b71149e7438de403d8f2ca515c8056f31b3cd249eafd99ab3d1dac343e8d02ae4bf307588a7427b9c7320582b6d065735d07ab71288d2927d7645a8e0a7f1fe64da35be3af262e46fab4d28a5b9a5eb019fd3f087eed55a8a2826f25a9cb53229a6727c45d635dd72ca3ce24436ba80e4f0e054a864b92c8ca894750f327dbbd9c4aef741d65b01afae26caa6278447d452a705f334533fa6260857f71f5d676413e9ba7ea9cea27760426cd89aa2e28cea6cbaa6a41304413565fb506ae0d84dac030a25a0d70b2c5183113ae1059d1b89ff11708a801d18e3ebc6e1e1b6e04bf958ef853b397b8582d49334749e2208d7d44e27d01b46af09424f8025f4ee50d8b9dece83a166f72fb9f32dca7bba3a3180302dd16442253290ea56f8f9fb0aad48fbb8fa96f5bb8fe51131f3372450b812bbbbb1ca60b23c8d5161573485322b267129bb24717da221a4970707780a98709fbd81d42d6962f7924d641948f2e8b3b88d18589f6262f6f567b37fabe252f571a7f1d044f4005f64b413e1a86c31682ca7a462cc0586291c7abd47cc897753787b6737a95f397b645e7ee949288044cb90e8c90b08f43a4644357f528bc0dd7f4db4204347f410138181a30f93b1a18f47b3c714e7927e760c17f1032ee0782d716f5d17ffcbf8de92431208542ed503d25d7d6d9bcbb6142f055778f33e42dc3496306b460354f2f7248c6c517f9be48f349103dd44b0282655d9f1f5cd4926129710274fe72466492a0a6e60b1d5838b0530aa183f826e7a7b4bc6374e423865f6c489f83a284de3289aafb6761421973e74eaf5ef09975b8251a1b3c3b5516a213426efa949273d5e3c75a6b7c70a4dc40e620fc97016327f9cd823b18927daf0c3306abaf8a2272ede746128967ca2a134daa7bb326b92e192d5024897728af28a682bbd8f564ce62a31a67a35bba698323045af03c47f0cc979b452d60b9396bc35da4e21673bb2bb66095b9088adff676d2b052f58fcb5c81eb01c288f213a72e4049964a0d62ca4480e16da8d8362189b03fe4722791ad08b268516d013237590b747302163369a042ab48486423099dc0b74f01b44a61dce422316b7c3fa6caf9438c0c6dcba4a26bf4a6500b4a9296eb01bac46356ae2904e79e580448715a0ae024b328d21de5ed7caaad632c897f0d75118f40d0b5f15eb0f92e65486f9b76c34529de4a72d62a748a682aa426f82d506b6248e608fcee061c6722607b5eaac444483567b351f67e1dd9023c35f961e7f6afa1dc283a9fcc1e843c9206d1dab20d59e159f1cb1d16394b1c64f227928d866eba26d109e5350ba4df88e9891e135b7393ddd0706ae72fdd6e464d5051f68e3f842eb180f65bc97fbedbfde3564194361c9329fb6982807f9cd319152075fe7639fe1a88ea0b076eaaba0e23ff33353cfa1db34a8e7600cdceb7a69a59fd2b811fe26069920242761b234f853f6005c5231e8b2e1ef262fadc6f5379102493e89a34e89839108c0b6ddf734cbb0164f0a94f9bfabb9771365b2cf8a04d9d42c3ad1b268ccad26aef2e1faaed74ac4ff508bcf66a1850237b6f0a40a40570e4c0e7cf1023a64127859405c11b5c5099e61cbdb628ca874ee3b2f9d0021804e75ab41e7188dbfa44e74e25ab48a5959d1b07185f9cef80834ceb84ae09e802629e09fa816481bfb6354a122b1703a664bf0bfb5d65a6bd64d22dd44c8a8496477f70ead07ec06f906dbdc4a27398e928693a4e3debdd3df8d8dcfe9efc6d6be7ddced86f377efd12faeb857dbf87cd32d9f77ba45f4cd25656fd7f277b54bca1e1d131d3ecb1fdda1cf2c877891a6d234d2afdff4779d4a53087bb540b74016666dd7ae6561dfae81ac57cbc2be5dfd552b6ac73492a549afa45b99a4bfb82dec9aa63fed9af57aed1d4bb32ccc834bca21eeed716fc774f6a84935e2b1c76317965bae575b155f5dafe2abead7b58ce5efc6c630d1431fefdd39d2e15c21f9e86eec21ec1f8f34d25fb5b58b421aa4bfbba736bfb577fafb9e264d3b8ca9f510f7f6ed31cb21d6d85ee9effab665ed51071d608683dcb2dcdf964972c94bd9e458fe52367699912240f292e3207989c68ea374130d270a2637963fec94f68d148192931c4789ce4ea2e1cc8e23cb34dd727d987649d997ae0eefb6be78ba5169caee21ee78cf14c9cbc49d019416072e213365208a8c35660601ae449b24aa13a670da168e54a936bc68c719ad3d03eefa3a455cb82faeb8d166dfe0886fca2021572acc942a9694a1d99fb7bf1a53616a4ced316f2055a8ea43a2c8d71b0cd9d4b6748d296171a39e3fa449c64c991a53636acc4462cad4212a0db67cf5516362aa8f3a84cc72b02c15860b134103448a4ea4d589556ba2ad93da3a6977cd29278c89222f95c9415823e42c34ca3957e00c84f3a29d6ad98aa2208410da0025ad539f4813cb4d292c9f8e877ab4a31c05595cc535541d718991d2caea50d475ea17ce6450f6ea74aeed5876b3b6d2ac14521021420da968444a315bb54c46d5d49c9632419448222931b90490036cd320cc3039a594534a2925b5a494726a5a99e89c734e59e79c524a29a59496ce59e98c998c525e4ab95d0d00479861812816086e7cac4564add5ce7f544a29ad7862c38b92452184d7e29260abad2c96076e7cd531cbd359c44e9b3f6aaa15d658b4d65a6bada65a2b8410c239af33c1ce1aa94e763dd0a8c549cc0103188ac415598491235b3e5a788899385313675866ea67ae34cc9ca2cc7090bbc589004c1d9392072000998c590560b266e20c49a9240001c8646454250094015801863a6d94a9286c9379802462e488dcb40cab7414d2b148a392c994c99895a20c000c60a0959ab6aac20457644a0f204e9822ce54d6468a20333351c81344a111ca007af418a2c74c9c89343d7af4e8310002f070e261c890210420800d2e0a600518aa802b37ca703ac9534a4a4a0a4d0c2224ce8080c632c8d394075239618a539cd184a4a4a4a470b873a1849926e0cab53f4571e27137eca200f2628ec3f76e053040bc98e3f0dd346880165a460011504b2dcb4cc7619b85ada9a9e95c96966998eb4c8a5aab8a93a2156a5985598a555ba65de862e448c7e1bb6919564181da49516b2b4a591de63a0e5b15452d1460606060acb5c25a1163792e806f6eac155d61ada0585870a5f807986c856487b98ec32151ec30d771b885490d959471c239e794b3c25a6ba59585d65a0b2d6d028c120b713f294f29910c910c11453e1a89457c910c2a509a8af8221924cb4c8d1059c4773b356ad0353030b1c60250cb34cc75eec59cd5b9379381fd3a0c893b8fd6cb6ab2885944fc01e362e4088faaaaaaeaf19b26ab0cbb880c91429a6c3a5c2443a55445c9222ccc65d9900614026de69cf1860a2271881432da48984ed779313145c4c49a1824d6442131484c4c4c8ce7b94084b9c0cdcd8dc7f30185ce408632c0951c6a98b5a6a68685852547102318186992b1a6a6a686e5038a4d8835300ab8722f6735af9630110a5b6a18f943559d878342923bfc676dcce11b67145d2d34a8c9019ce153058d1328286957b81e1a8074617fbf02cd13441bd2d014e143a303cf0c0d0c3c40a031c11a9ac8a169820a277c40d3840b689a18014d12ce70d34f4c4f50bac9cdc4901d53c023095fd81f9ce1d183092b76cd99093151b3271cb2a709e865e247ca02688c189052210c9bfe05a59452ba51869c986698c0040d7e70bf0a08da831925543c4320e2238810acf0e9818f2a3e23f0a10614aca0065e16688400028d103334424841882ee80d6964c0c30a595114655534333ea86c531445519e11cc8ce023733b235d918bb7e099e26e5208b999123c47dcedc7678aabed00dbc8600417db90660915705358a204f7074b78a0082460f1e102128adc5014a28d945e934e1b63b596c22aeca4945927d65a4a279d934e4a77bc3a95856952432b488d3336ff4f6436e5949d8a08dc01db34e64a8e9923470ea982cd8b76aa652b8aca9123478e1cd026478e1c73259e76902c5acc24362fdaa996ad28aab256edd0783a4995910cee678aa71366c1a5ffa0cdf70252b123e4e608c69a2813a3c5bc1035679ea121868881b1870eb8a2c3c6e6064489bf66c7051ae14184081554402968686a65a950c856d576acadf50135ee69d10e8659703fba5f2cc006881233cf0f2aa9f24108e185109bf37a413b2795ca4ae5e88739a7ac1565294a4a295368bda31f7eb89146dd639352421c29a594724e26200c423529a10d1aca6942789339b10f5c9109087f30d9271b4608a1561285d8e32694e26095524a2985705a55052184b06282524a6b654355293be7b47eebfca413a3b4d65a2bca96b04a2184b02329dd590c3171e32bbc1056940ed95073060390832803e53f29e5254f05c6e3caef85ac7993b5c61e30a73a45181d1005cb1616c56c1e16d9a19968034ae8a68b12bae9424929c54c01f5587f224da549c3affa8bfb0381cf3b9f0eee58590e9ff7bb75f0bddf8ffe38b03baf1d7c6febe01beae8100dbd93431d8c753a577e8932ee75bcc425c5dd1d6bed143f1e6338b760b73acbbae5d1df8eedf9963f6fbb6c2f4369ba17a4bfcfbd7327c9587fd937782fdb7ff2e7e2edeed93ddd61675d6635ba5b5a6636f025842161c8ceca1ff6ee5797b7eb2feece3b973dc7f9f336fe35b31adc8c7bef30a4274369baf76897bd7aa7e15f4f9ab4cbac06d6740bf6f84cbb781bd3d6e1dd9d0cb765fa6c70f19e8694ea866e82372cfc695dff53a20e94d0b45b79584a4a4a55dd1b0afd26d38daaba3714fa4da61b37b6a6699af64dd39428955252525252526ea488e4d68394ac410c1eda67ebca326de92a23c125a76072a22109eff89ee792354f7f2ef9e62fdf7b37ed9e6701ce73bc6d9bf7f1dc73ee16b0804b76c92ed92527912df0833cff785c72cefbde9cb305f2ee21928185bb14655934ac65b12675651ba6b87b37d6b1ec2b8ac6b667e9c172255d43114a249a7651e896bc8dfbc682e58fa5aaee0d857e93e94bd9d837ed9db669f9a3b1b56dd3344dd36e77eedabbfcd1d8f85cfe686c4dd33496ad7d636121d134fd958ac04283f411c9a8141a7d42de87d35fcac64a9aa4bfeade1d0a792a534a09cbc52ef6ab3f968d612c2c2c3458760f91246f1014457514555514455114754e07fc946a5e1645519cd501eb4a7f378850425282c384e4961c47a984eb38229494e85092494a8ec344974870542bd55751940fbbc2909444407d957b40593aa7151742e7891eca30fc0bcb2eb71db3a1fd3ac6e91a9ac6d73df9d3b8edf2f8b0b94cd27e91345d03bb0f9f0ffb9efbb4ebdb367d2fddf239a75bb26fd73bce324f140a81402010feb6813076b77befbd5708dbc58e5b5ee6ee9e7d3b962974b1e94cee7bb9b97bf850aa60fdbd6cfc6dd31fb6756c2c4d9e50487f3e7836b0b1feb443fd611c06a8ee5a7e99bbea740f71df6983e330ce32ab81e163194317d3f32167bf48cdc7b5d50a16f755ef85523b6fb15ebd63d17a5b790012040cdcdd0389fba6834a930e1d2ff485bed0205c1d2b25d7b4920c25ca0bbd974ad3c9486a39b817666de8d8f49d0a9357a61245c7b65407953240e83cd041a37fee095d7469f73e64ba0d27a46ffa7bb9cd6cdc6b36b8e36b58a66b701a4a93b61d9f34fa757af3a71d6b1be93ab6f648babe1dff66d275eef7abb6a64997ae214ddc39fde9d8f758e3d161c84b7aa11b7f1b1deb96d12fdd12fa7da1fb7ecb9fa65fe81e69284da063a1c38b3c3704f25e7ff5f7f178b6cdf3bcedd82fccbbc7b4edf798b7fd6ad837acebba5bb9db8e6118866140d8b6c76fdbddb1b52d7f3af6c61dca95d2374e7fd5def4576d6c3bf652861205cba876efbd31b371efbdf858feeec6b6e37b377da589eac0d92f5324312287946541485114f568bf5bd082d0da6b66148c973a8df80f30d1ad5fa1771f91b66fa4d02d79a15bf23cd7b84d7f18f8ee61b435b06373dff7f5ae77bd7fa17dbd6ff7e4cf0b9d3bddf4bd201ef6764d933ed7ee1d67d2e7dc7155ddebd1a48ff6ce9dbbf7ed9c973f1eb6e685f0413af44db784bcfc52b17ea93ba42f6944ba44dd682412894458842f8661b732f683cdcbdfb7779c3f1e36d634fd9978aefe4c4a4a48484a251269341289422110e8f3f1783cafeb380ee37bb74dd3b20cc3aeabd3b1b666ed0b0269cf3f1942171e6a6b271dc21b3cffeabe87f006d04324fdd9ad5df45186d085487f76df7ff20de98f8b8ffee8d6eed91e95eef8d774f587dd3b8ce965789ced3becdcbd19ffdebbbd30c3fdcefefda5f280012a8b2cb218b25fa8287a485c17fcb03db353d65f76a5e6abaa3e236198f54b4369eae8faaa3f178c5e00db7145e9841e789ed043a483fe1971c738ce08514cc8741b4ebadfedf736b3819debc1646b60c7dece5d9b26696fb9be5ddbee3df77bee5dfe36d2af534ee3539c2ff62b7f2ffb7acc20807fcf1dcb24eddbb1aed3244d676fe9ae3debaebde3ae696efb76eed7b7fcbd6cecdcb771a40fc334e99c6e21fdd22da2732ed8e67e49587f2ed8e8c3853cafe7f407fa7ceef5783cf7f817f670c798e79ec32ef63cef56f630c618e31edcfbf87b394c7f3bf6cddfcbbedda15c21d9b044b9c712051fc6c439fbcd9f0b86711cc7c5cc013b38ee1797354e7f71cbdd6fc68ef3876dfced2df7d8b77becf7d8f64ee3c3985c961b7334e02671e7f40b335c520d7c69ba1a4a53f6ed627fc1e6486a4afcc2b6aaee85d332edaaca5f0a353b94d5425d86dc75e8b46957e7349c28f63a60fd941d43ec0fde25851a317f611e485ae32bd22dd0add1479f5f0f899edde3eb661abf6d17c5a854de9ed3bbd65dbbcd6ce05f12771d4a27b90e262ff9ddb00ddbb66a63a44e93381d8152e9e230d1251a4e947bed98973fee019f77a7dbb1d3ace5e039bea649deb57bef8eefdd6636bb7e948a927f212aa48af77a187393644f9a4c5e926d58402c21dd695a4369a44a3dfda1e90f9b908dcdd6b0624c9a4c3e2f65c5579a8cd8953a11fa27196594b19e524c320e8644b9626db8d1af2b961e8a0ab0af1fc11be2eb4950a5e4f5def039842b1444a1ffbc244f68a2b754a66f41afe963de948ef28b2bb06a0b17eef8afdaf59e7b432c928754a176098b3bcadfe8f631ff44f6ff7d69b60ad054ebc962c1625163ec186a0feb850a636d2c15accd9cf11d273d3dd1933455cda4f2a8a77a32d1f424552ad7596b6dceb726269d8c31d3921293d3d33c12c5904d4fd1dc42ffc3fb44a582009aea89ae238028f5b5ce40480b17ec7a0d0454403d4503d4fa01e8a2fea3a79ceb29ff947f9af1fdf9e75bcf534ff554795498121206e28eef816eea18d83645f2cfdad053ba87b8a3fea8164a533d8f4893896e21e2256e22f4a33e801017744bc4de48155a8412912bfaf57488543179bdcef18844a9cf59c23ccb1f386de6b4d0d33c224df52f712f409aea690c1575d208ab46ca416e7bba3f0e6c7b931c73a4a916b143ff648f07d0c80d88992b2c25313850dbd29354a15e874c94ca79ef4e87785e2d1db22be92479e64853bd287f718bb4b539e9983465d7628c7a3b44aa9088e07fd521f44ab9c65c61a4a9de66d6a622e9ca439aeaafffab279b852196f56863a9b043aceaa38fe00ee52f04ca3619a07606e8676d76e57206e8feeaa9ded09e3933c76443ca0b698a99fdabb98344e270020c8466094758021273881f368c30c60a5cddae8ea954f15edd57d52fbce1da5e65adaa30d6247c0b5d74b71a4217f53bf0ed67e05b9b71973fbaad4c46f65b5ce798d5d15fe71077b48bb7b7c353df0e2c2aa0631d4303d85bddb65ea637631993a68bc4e1cfe8748e35842e766c4ecfe83467659c3febf79dfc59df7e516c66d9ad4dcb740debd82dcc5ed7c0a52f74616f551d4daa619dfa0c974fdb938b96082550d43bd43b5455955e3239e92392977870f6ee7d8e2986b37798d5f0787b46cc0286d43c4f93aec601eb743a59d699d1fdbacc0e338ec3ba46f7ebc2f81a96b5c3ec0126bf8730a44723f1dc6610f08e35493bfe754f2669cfeef97c3449d3387cf4e7b23fbf6e9e91ffc34cffe69885cc388eeb50195bd632a6758dfc3f5f7fb65d039be7da0636d6b6e73b360c0963da8c46ec4ceee9b0c935dd62f2ab5b48aefd93e171fe4c3494a6d23d92c3979094489a56892a4d545556a55555a5dd4adbaa2a87b6ceb76adbeab75bd55659af947049baa5433b5a6ee99cc21475eaa0fc791bb4afb669282dfee15a68fabb4019fe667b2e7fd73d8ca953faeb1c77b25f33c3c1623393715d3867d9f1b5ccfdaad90c4dd3b44c46770dd4e91af7da2fe8da76af81ab2f74b15dd317ba007d3b36be76ee33b40d1f93977135e99e54c373d0677834842e3c0769085d7cbee31ef419f7a0efd8386f199461fe644f5663eeec986639c89d1d77d8bdfcc5cd715a93348035f64d5fe802fb0e528dedda676c9af6a409fb8e9d5ddac5db1d4d1d5e17eff2d384cbdb15867d07ecbbb53694edb083cb09139c105db79e8d8e91b48bf340c79fe3dbcc8676ce8667cbdfc9deee76b7bbdded7a74b7dd83f2c77557c3df0efb624dfa685a5571dff7257d740d284d9d477f3becb0f1efd50ed257b784bcdf934fd32e273ba43f9793ed1d9e8479d935c2b0ebba2e8c2f8c4522d1ad2cc218638c9de8ba73f93bd9dcb5fcedb035cfb7adb443a1cfdb5b086f2f6dd2f4d19e8cbd6bf05ab6271d8731779cbf938d398c4fa4e904d6ec70a285e1d757e95069cad6c34c874afbb027cad431e50e2aa3144d78b8870eca8e7dcbb28f3c9c1dd381e42512d638f486ef71d74e33bc6ddd7dd8d8a66599a4691c242f1d9f440192974e52d29f0f3b3bf6795d86b16ee1aee996d1b3c3905ec64d50361281409f4ef62bfb743ad9f60ccb7e338fc7f34ef664b1892c661b3ef796ef65669986f527377ef72d67f9d3b1b3ecde4b79f3aa9dc189a265f97b991dc74d4b7fd8ef75d8b652267996bf0a4729cb361c255d4d949779753c2be24a4b559eb43405a91c2edeb160e29add2dd145a167a393f0b70b8f3c6f6b9ee669f857f338ea69daf5cd0502daaf2679b64cfa7cfedd8d499e0a63ef1086bc3c6fc46d5844ca46a22e94655dd7751d0804ba95414c78f2dd9af67b3f1ffd55dbe3d15fb5bdcaabbc7d6fe5799e8bf7cc08f2f4aaa475482f0bd35fece4cf65776e595696edb0b35777d9d96dfe72ec4c7f9d4ea6c7ea61c88eb56ead199d8e86d2f4b964e94d21be84651db38eddfa05af1a7ae7f30e76d1b552f75f5da6bfbc7c6ff799d9d8deddcb9fb7bd636fc5fb85f1bef75dfebcec1e66333cef6ef700ec9e477cef189f846dd9732fdf5f57cc72c8eef9e6b2f1c5ddbfdb65ee9ebcfd66ee57cc6c74d9f34b625dc601f4ee73a901daf7ddbfeef6b56634e2c69ed9c03cbacb2edee6910e5fd230e9f3f59e54b19410832f7892a8b5561caa3899613ee10222b04ea70847d440668a9c2bae30023261880fea892136c8acb516aa010b3c0032040b42a018a2082eb0f1aaadb5d65a0db06badb55a237c14c10458b8e00920e88f285ca00436050f03ae80c40984c0852c9250820862c8c1c6c0e3b232325666a603850c14d6450426625114f5841980a89c40d5586b6d1533530083a946234af0c11019bcd6072c9861c21245a400033a842744300510a78eadd65a6b251434c081a20627ef0757a560ae4130285028d4b003cbb256a0004411225042073ea4a042a811e20315152c8aaa4da834c220636baab0b959f8c2166ba870045c6ba60843c707b6d65a6bad750342553110814a82add65a6badb5b6b5d65a21331305cab02c144b10622971c2a8b8e00a9ca9b5d65a6b707cec7aafd65a2dab02a1835dab5d6badb589d75a6dadb1d65a6badd501a79b4a91d8f589278860d7ea04cd15bbd65a6badb5d65a270ee584185e30ab509493da3645511445f3435a15ad81f921d20a563545ece01104115080530513b620031a04206e0084084fd8d44b278841452f1492265a0348952a904a71738aa22a65a98ca2288aa2a01344e46c8abad95306360821e8743a4720711db144a733bd20062c50278e08d282a2e13671822a18010866e80210bc708414530648a060902be0800640d8c00b37580105821554912c8b2c7410862bd46c610c4a50199a310011451034974651a5f0441451108288cb0bdd0a8510339ba29ec82267535b0c8aa22cab0c29a028abbd4b4f817c40497c20081e8842103378c20f702ca9126368d3b9124ff4e403f107980479061a21a01544b4b86850441a6080051e4960e183285aa0068c036b6da863adb5d65a082427886dad053204246c9b852394d442c904333e54007345112250d01e38210320aeaa2b7aa2320795462aeca00a4a4029ac400a2105554c122401041431684e0010312e1a7d1051862b62d00ca5b082185705724418ae8871d920552c4182181755038511a0887155d8124a50022fc4b82aec095ea0a9418caaab89224978c4a8fa42d881416cd03082183493f088214f70b684592665a48c49caec2923553e9344c2a43571e1ae7696134f5d4e9226ee355353e6fc32914c862365a08b8bd25353bb5c335bd3707262b00f4ae14af5acca3687b6ed6632ae17ca70329cccc8bd3bc6dbc7d7990578433dcdfe44a50c8b0c07a2c88b90d8f2d9157005a661cb0b6355de11e382b0c3322332321c69929911699239d22445335b8a7e4c14290118432463e51d31aabc23468b285cf9288d90583443a9a866ae509cd3e9e4811812fe38c211684442a40a6c020968b6bc888a1b212229b694324dd89f68c896571348a085fd898a6c793aa15491446c19b31152e587901ca96224c369018350394ec18e78479a2bc8c6313b1ef7d891085b24035176bca908fbfed8316e29d85b8f1de51504284a424684190e1916639c5526e1a874754d382dc5e0753319f156de11e3aabcd74c99802de265fd679232dcbe2456f2822dad389570a08ba94b62321cd1a435673822995db379452f886468d59fe80715553a69bed264e51d31ac8cea78aa320aee2792d92299792a45322219918c549993be6e22992de3cc9534551a0a8542f044ea96139c173ef855515a6f2805015cd1515113ecea66a2d47f15566f28d5f58666fa82dd469a6ee00d1507e254958e97ea34ebb8aeeb78912a9d15593351ea85d42336bb5e0ed97576b2aca98f3d42b246eb087291a8aa1b60494949c9bf90acf586c22e935b1ef5724bfb43576fba7fd647bda947e8c9a4a4a42b2929a937b58834d563265cec56c84d09f67929619c1a35b989b6a1e206a594d2d336518a6ddb5675eacd858d48bf218109dccb5ee579553e46af0c3bb6d241ed1b1995fd75d1134661e889f2a84760769521cbc24895aaded893e5b17fa041439a6a89ced0d35c993854d2d82e36b9c9adea0d8d0648157ab10e797139bbea982815dfe0be38e8a18f0631040f0a437bd020542c3d7dd447fe476f80e9899e280f0a53aaaaaaaa375555691a0df8ab0d9cf1bc1aa147e6cac4a954c895bf9a7c662bc1191a0d9838b108ce0b692a49192424cae79fac893ce26903d2543f71e68a0e9c8973845a559bebaa369d4eb5199d24c3a874bd19bdf47a5371aa11a912cfdd7ba3ea1452a6f466ae5477d69b91a6a07ff526f48f562b70b6edf1a616912a71c3a034559ffc85b697334075eacd953340f78dbb3253555555551549a2a00107a181c2080d143034408cb4e8711d9e5cb6094ad90ac4aef50451aacc645410c0159669822d55a8153d3ec289cdc0cf86251b7e72ff9828f59a3fe6b4992b5a573f6e333d3837d88ed8a6260537bce8fc11aabf72761c86b439f68854197d45dbb0cc15ad674ebca5a89116b788d670a2d45f9262a1d46966a14e718f9965015265e4b139fba34e3367527ca5c9645a1b6b05b8327326ca1450c5f31e6580a962576ab9c03d9311694c28e6c65c1846a54acc9465ae9c1cf4d0e1ec7e925b649d674e282794734393523008ed71df913de64bf477dfe914688aa743e6f7a2447f304808d274a01457a706369a0ab9b0cb86bae757a7ea6c34357408b5a142367a4389cc6c1aa5280f758fee81a236d4469a8448958fde73ee9745290ba3317345f69828f5554547b164c6581247d6a6478cdc47aff7646a049ae243393bbe56fd8560763c554f3b9e3e60c7fe28ea2070a4e96e10ba88fa733919bd07ba3dc7c0b63ddc698d34551b9e73ba87b847faa3a78a6c2ac4a3691069aacf0e4083fbd1991d8f6beeb56191d268a2815623e510bf0e6caa298e3479367e55c9212c39cdd44473bd0a931cd22c21c8476964101ffdb1eb47039c1fa8542bdb297b1822a21100000083160000200c0a068422d16096a6699674071480106c94465e58281488235112833808c21808320619420001c618820c423765037c90fd8947b8433f93f13c16c914f061c6d59377aba9945632e809821fc9d762c3c08ae08fdbbb58184b299feccd4c525c35cb8db6e365b572c54ad22a1f1ef6eb5c4b2a2b88d84bbcb2be958c288d6ccd56e1de6f2d62324004f5093e97a9c0bb1d788f5845909a75faf864c8444c2e7d5620e154361dc499b96c532897456c6c921113633fe6ac153c6fa5069c33c16248ba7c41a10eafdf0fe8385aca7570c172b79061d1b9f76d8803dd5d474efb7002144aacdcb322f3e36f756c1239a5632c8fa1b03f6f7fa414fab6e4b92545738c6fa653c62fec35c994e805c286e566a92ee1d4b4a1d1a3faa3b97b56ce84484686e2298650a7b78d3e900b0b069385bd77843cd5105399dde70764e698eee20328a9b6c0cf1304fec897d8a7cee758d4e07e45499ab4b9e54a7e01c8da0c99c10a184bfa8e175b2f55e97bbb4fdb347528c9c7678bfd7eea48586679715ef545c12940718420cf90a3d3b52d3d374f8c9986a7689c8350488f3a736bb9e12e255e11ca2535e7d7963e196fd311545e321694d7e1df967ad2f2bc946a322a5bc6c5246d532042f6a4aca6e1ecd7c74b0330798614ed245010dd2388d846bd350806d7f502204db46e1d340502e1ab6b8c111a58410520ea3ac27c33ef4b00e2891b2bd946d3c8d2d878ff35d5b8bfa63d061324b9bf43a07848625f595facbe8bd6bef68932022f02645de15414fb19dee4116d2d42a895805301912dae3f7dd87e8d2bc0c09d513dde0dfe4e4a62a0f2c95121f53aedc828753c9987ec3417a8ed2949eb599682d97fb91a4ada83b1c1d79a23fc2edc2d95ad389cd269d3aa9ceb23eec3cfb0e8da8b4586b802a26f450c6c2060a7d21eb9d7b0af8bf4b38f759d8b1d976f58ea727666285265f2d50378086d6bf89ee9fa49de6f1acf4a12fdcdd5ccd411fa4c0a1b5c3b2987ef5d8d710562cadb639b58cd7a6a133906fbe6e87db0833aa414a04fdb1cd440519a21632ec7d47a1335256595a18858013386aa967f1beb845df74cb49b903e1b3b6753c627f7943605b94129ed2c975a0d88090319e8014a362c611cfc0ae671fc04db72954cdc0008eec11dd0cc4f18cd4d94106fd0040110accf4fbd0c7f7a2341ab6e495906e91e02883f794860b76e29a8e06cbc716e15b92fdd8c0ed05bb020c7d9b198a1b49354f05bab9081bbef44424bb23ebffcb92ca1d082773e727bdffffbc743f098cd112abe9b79dca11216f1629a773bbc3d80915b117d758542e907c4d40c69efc170d6eb8bdcd17d4460491e00b7b854578d6c9030a12323d2ec5856ed088edb80a6bdd55fd559af7d4dc9a2443d1c6f740ddda86cc55d4ba5468925783a22b09a9de57a749030b152dc74008858a14e30dd3fba6ab83b44d90a895fdb8dca549bb289b36c3107fb188d605674845a10232bb18499225c9bebd088b0ceb09b23ee247b397dac9228a5d7f22a096fb142b1a883146c92083328f7a651b8d214a8e4f3b4224a1b248490bc5c563a4be99d89a6271cf05b7a47dacfd4254680c2429c8adaab0956d21fe8907fdb6cb626461cdd2d17c23c5753f5aaf151c19bee056b22f371033dde2031685dc5ff23eaaf0fd5b9822f3aafb254ad542bbf83b6d1be3f29f42a55d1bb5b130a1049630ac8d837f4b5fb818f02c405d375f6e558990dd9700d044c8745dc89f5028d9cbf1aaf07c5484e154e849886b694f16c92c0a2e6eb0bc52c4de1db1547496b081c115dbc744400227cca950c83b65d638e245065c995b79da8f4f0d2d3e3bcb43c2b83b079441256d6d29b135e647892bb9b340faee72573521e6cc5ff35cc0f811e20aaf24b8ffd663a529ab1debc4acaf0a4669146218125ed6741f9404ba2ca6aa82f3b2f693e5133a796d6632d8d207c3e416efe038cab12b92c15e7c0a4c1c1180a4ceb2440a5a1a1dcb020aaeb422a825f9f6e5d8f84ba243b9e91e9e06d8c6fec02e07affcb117263c7b6f5f6fb5e98bb96d6ac69433502f54d1d5933e46b47a5f6a2da643a8ea24d136a06198aa5e83683903fe6b46e0629b5b387d4a5496f865b450e7a93c664836313106041c9eb0c28e9dd9d643412f1fc083c7d8a2d7eb620f167fdc95bf9ea5dadabf0a62ad3d8ff3811e7df8369276ae1f32d9f152f4a9cae402d301a7d5fab71f081eb7732132867e8d3295ce47dedaf4ca1bbb55a988114bef568497dd666090bfb156dc7fabd639c2f1002c8c055adc1e60640d4a743b1e33783de3acd9a3f207b91b78385a8ac4cc3031a98b9f73fd05103533d29684e40fee98fcfac9e0167a627618f34f2083ca8c0838429ee72fc516d11214f1d92f46e4c1ca3aa7ea8c2cdf0543b6c11dc4c07b49255df233e0a8a2e72ce4cf18f7096798c91e17b4112e3c7bec3782f5097e44255d35c45df7e1c6e9b569c12e3e177e9110cc422e09f732dc5c03d7d052c8d92ab14cf7cb4b8f62c33edf635e5aa8c91b2a9700f68828ba5be3dfe8e7e8dac08140209664a318a99aaa79cc9edc2504fe3aad41579540363cfb40ab030245b40e8e1d9756e934127202afdefe99a48d5ec95eb062c6b79f4a494c840a81ac284bb163571c582389112f0c44829ed8b5348bf68e1ce251e0c1f243b0f88f0085aaf502d91fac24a2c7e77de1841d2b3c548a22b2ea148c199401d20d57184d07b91283b063a88b973c92387793198402e8aa8721d8efa268ea09e2b100170df23916e10d0f1179077ea032308e6a14380328460231e0add915d227aa6507b745b9fbdf4016c553be1210712b35bf092433b30b2bf63b06e5487b396180b716c41426ebac639f662a7fce5943e4b133efed1c922817f0724161a4e21a2769e6c7538f2b70ec45da7cbf0fcf46ee0741bd69ab2bfc14d39ecafca5abeb0449893b2726c7c5722002860a689ef4afa27e2c303b2c844d1d5fc8fa02904738810b98670aefd3de7bcf741218e7f51dae42755ecdbdece71c9d91d724246c470f2de0bfd8dff83f8ab780b8a098e3b0d1c0ed4647742e5c9cd59058d5e860d02880ae5ceb5c63441d3d437c7449516979d4676d7cc6864c72ae92069c5d00227cb37017264fcd16de6449a11a8fa71560c0211207761aa821985bb8b40cabcf172acbf0d31a682a6eac4fde42dfbc7f594e3569cc124e7771953b5e16bb2684a7a7a4952c66b7191dd8ad055448527b3cb69bde7c3acc3cd9c5590caa03a97121a34a2215c71a430b628b31b52fc8f0e21fce90c12f0114c71dd2146614b7ff8c989525a7ca6442cffb9160504c875b24dae8ff9559807e7cecbe2e831ad7265f31c875b4652b122a96aaabfa569dece2e9ff2ad255c3431d5f57c824616b6d90ea0b4a37ed48a2d14008f80743cd1df0e1cca6e935852f9c444cd4ea56a17ade5f5bfb3f19286008c16858c80cc7dcfc1b60072f7782f243ed2c8efd0037eca0934f9ae2178255ad2003feab7e7de1455939d4226cba877cf6d41003a95ec4b4115e6367989d900bf9682a21fe6ccc70be9575bf53525ff903a0bfc7514dccf7f5440c51f1370669130b95fc0927f60828acbe51963cb504ed2c4c86bbae71020829bec29ed589c6b4a0623fa2360679f84a8f4801e0326665bd3afc5a094d49d831fa1041f3510b0254cd0406c33ec53c9b09edf804df4a2b61f061c65560689742f2273dba6da32db379741e6d0f0580494b2020acd987cd7d56af790e605417aef86d46258d5eb14655b0d2cab676a56537f54d6bfea62b7430f36037b1b002e726031ae727c1278495ca9cda701d2067710d83570cbae441d7ddcedc531080509f7c7419b01b79204511e9aa67f88a4c56d6a85e99336ded6c4a29b03827ec77b42223236455e465e6e3b36f08437894671375324bb71f43fc071123686d874531e3b29f5b945b70a498c02c84f37fd5c960ae3f6af149db2a258b6a7d80f5bb5ca2051b12f6a51124fcaf258e15c5bc777662666a8c085b170f38456195b9b8a2ac02a6eb229f67e985d79502c1112c8b58ac52a7489de1b909fb88af54c762a172ffce3bc2041b3e1baee377fd7b28be2073d2d0aa08718f649f1b07b17a2fa720b8bf74421e50aebbc7022846ae7d3ec203203f5399fb0bbbd6ba414aebcdb1964c2275536ca2a1f6a7f0e92b041af244fd8676ca1619d901069db09eb66f3764a82a51a77eb30b0ccdcc84677c9c6d3c1622b890642be6a491d59545d07f6afd78a3a524b31becc9ad472a13c73d128bb82e9c4f59926d304d6ed1586d43bbcd1267c640ed403f60a3535d49280880d88417cdac8356d8452fb80ad0f965668f25c210e7eb2dad898ac665482d1166137abfea1fb0d7c0ddfb6df425cd79ef7b86e6d87f3b5b72855d6577d40393624c745734f4e2772685ee45671decd21ae774e1033887421b8c4fa592a611abda522baf497ee1ebd0b4797c3ea66a1da6d21627e812c4c38ddafc6998fd8c63efa54bdc7f54597ad04b058042dfef22518c845f1adbf038a6a69a4785ab3c1dc07342ea331e92e91a340209d5c530e834973985146c880a702c9e67ca9deff172d663f31f28e0914873705ce7e08c28b6fcd4ebae793680d604eb66603babb4a8341b84a0f2e5525610656d62725ae172716213b5a873bd38d2ddbde04f4cdfd3a705da8b53821a78e8b4b04b2cab88144aa5fd5ea6bab02b841f174a37033d05fe43c6780e810416f631010d44f2d65696d6edab7cc32d2608e868369be1a26356b20abf8803ad91d54892b8184da32e87c211d2af3b3d8b22647e095e1515efc93a67edb5ec2a62d1ac8aaf1f3a46a796df16c9e480b8f2b33fc268711361a6b12fd615bf5da791592b4ca3bc47c8054bdefc104aebcb7b643e85ee846435bfd619948848077298e4bb6a5af9e2019b737cd556aab81b707e75b1ac45526bfb5854be327c20de4737ba3d13fd916e5154c442dd6a137b5ae505e30a52d524523fc15ab6f08f19819629c1bdd15872f47a295f8974fb1f720cba5eeb3f7f757c284ba7464381a9996214a20acbae805d267a986c7622a9a43132a674349beb1b2f527264d8681653f4c7aa907cd70201ad8b3afd17b68b66a525058a7e66367abd1afcefb0493db89ad98e62b7ce4d2b0da075573a126aebf6dbb93e0f2005fa274b688496b849b2dc980e7796bfd9c4af672152b2c29d7241d8de1573d38296e47ab5b2966bd7e6abd892b39cfd4146444989f85fa11f749968342a51fd7d83b03a5d4506defce55a507018993f92f6e43e36b12d5c7b50178b76a5f4956bec36b5b4af397b4408433cd8b7831529c6ef75ad14ff7bba7409288bed3901c29225c05c7d9216fb5184be3828944531f9afd8f3d247f7689eb14b6f24104e49871ee598bc921cae637b2cf3439a05eb63fc0b9c10ac5352086a65fc3f6febb5c6eba6c67cf74411fd58f3460ee180d6c98f0159435fb7040f401b23b700a98c5168767217d67c6a1c7b3a30265b8ee35d40c16eb55e3ed1344aea8fa30eb8e0f01aef66bafc3dd16c246e94baab885a8a5dd7710fdbe13293c181af05b8c6aacbf39eb224174f9d22571e9dff3b4ae57e8c2d58faa8415507e4943fd3bb41ed5cfb85e1ae8995a467176160161225d26dafc1cc61331ec3e797a5a01bc11b221407d9d4761bf4ddda1f3e26d40b3bea26270a032ff4e2cd86e1ab359cf91781c8216a0064747be610ffb8946de621d153883017588d5cf88cf06bd49abdee3b262bb8684482f44ac00141d59c991bda3322fe0177b02369272cc60805c4c2f2a1671f4df7554e68f4ab64fe16bef057a731d56624a067522c81f81703dd76da00369c497be56cb26a80e843c2d3f0e58c0bd38c062769b73c01cb47917ce4a0f9a54fe2efa8b8756070ca6ac19c1dfc17bbef2af7ab9b2a788497f92915ac1ddcf4bbac0ec254d21ef9226f5e6028f0538af4ca3624afb158d562813302d6708e9e71ba5447df9b6ec15c05e7502edf45612ee0976078824e14c5de474a5253a9c37fd81c4e4e8a371a1d60b518509454fc0f73e61c507c2e295f1a10c2657fb15051e00bdd7c4a329b06a68fb3ddb099d34cb6bc6e91c46cbde47e5b838251e150fb58bbc0cfeb380580cb5b965f3ef416dc033dc7453482a1fd3fadf0de0197d24a30beac24c18555e53e1e8070d7b7f2c1d419dc0050e550195310d956dd1002067515893e5c1b458217b46f4ef9f86c8918e67883f67b2491e2a8990d6673c4aae3463daf355f524e3711b48a5066c9db49a9264115ecdbbb886a7d83bb3c48232d10c6a24be4d2913caaa198d1feca7a056e9aeb277d1b959b70664a492abe39cdc6f9881028ed85ccfd54194bc25384c9e86817a54d0a454f17b8d3f5437fae819be146b2b26be1098ccbe643f22315dbccac8ee7472251f9b2bf54830ea82502417d064ce662a1104531a9ff27e2237425822e0bcfe0e28ddb7e1f0f3f9ed1eca0649daa523253b75d3374a4e636f9f18e1c24fccf47d4e11b04dd90aa5b40ff5b8ca409c729df4ddda6838910656c122e8e365dbe9cc72fa6abd6499356a6db7f28e148f67b3bf49184f8dc96da21341cb5efe501dece976c932e5b27e11855ec829bb342b45a656fd66b3081aeaa1ac1fe988765bc8c67dbc92b010b73daeb135efdfd52db61576d74018f3e880832c6d9a3fab045479ba0b00d379a2a3db461d4e0150d4a88e16376480cf244d090996acdda822a1611234c54425b2428552670b001020f2809fc25fa927292f0bc95795fb86d5c8d05fc4f1bad2e725167eb84d38ab7c0088cf7ad8e5a72b531b26ee27a7655844eb50edddb7d0bc58ab7abd3a809b6be33826a13914c4947d0fe692a8c61be5d8a51b15144f0274246abddb2edd2ea14bbbe32ee71098ba040647612d9cef97659d0de9c359e8aa93fc232af5efc4d7270a797bdc4c560dfb19a77485f153a0d062df46db1a88292cd3424a4a0f95257ea2566917aaa4d3baae0b673ef4b90a078a6467a7035d113b4669745e2d92604fb11410853d937bd0d8dab1439baa9f4d44da0508a19d48ea8020366e8cd458f6670cb6733d2c5565acbd09414c3e0a37a658b6b24c483c8502f69fe077d26f1dc48b88ed5d5273e33f1c4e0cd6b0e1ac36a07f344f222c42cdbf9e3eae647b4b7e71bf727da7cd2504abd0c6c0202814e5665e501d407939a95a2f0435e721dd7432f50709a38674bb5e319624e61d2eada984c8376cca42baa99070182d5171053dfccc8033e305f59cc1052d827de34a48200d7d40c53e23407dce6595d845c3661fa76ded90ca4c4a1934cf3cecf8283539d2e524bfe1783379ad8766655658ee35275591066bc7647a31e725f59f10a77d09624958d059271940005c3c7572106fe92672ebe034fda7f7aa99885e9ddadb358f251d9ba9453d9fd0a4e4ab1490aa8709a639e8edb67583cc3e664abd1d12992f4b22099b843fd658df75ccc8180dc8d04c9adf297e881b49defecbb0fe70c2a2ff3eb1ebe6a8bef68740f06a01b96671a7b588a79a9d7ae753492d3298ddd43f079026e967591e188922550b9a2d4f0ec5bbed936591533c6b2351579ce778846b9e806dedcc8b083abbefdc89bb5969f557dac47848c16a2d58413b0609990cea2a03d2efc633fdb44508735518a2a740fdbec8469d3644fffe935ff221e62cdba5bf76ff40c591a78915ce9a542bd732ff537ce5e81cb7e1e4eaa66196027ca311e756ad179e00f6271c2facddad2ce9880d5d97b33d978539ef9597df84c90bacad7184b12a66f00125354da5f2dc32f701401d3a41ad261d1f2b54f781c14091c67032b4409c5a5e5601854c4c560dd5f474849ee48bb8af5704226a960d6210f42bb822da1aa50f4a0f719a42182621f2983ef4135339f1fe20f81d7b22240a7f26297e17cf7eaba8214fd2e73dc21819ed0be142693a4bd52fdc481e17041da1424dad90865b5aadcf4211f80278440b27fde00c55c56516109aba3f2841f52f8d26a904f5cd987540cd8afdad2644bb1bff0a63392311bbfbe6301089088e04ef1218f389721b4561399e9440f1f6127f89fb6c8517883933318bfc7576e8ae327d182268e3b14477a1e50ce6da322d0f348191ee9a9cafcbde08666fc41284adfb8e721b0094b24d55830515bd112e7b65f23a31de994f5b180da9597e93daf941cb7c232c0877bfb7a35680d10c2f3ffbb8e8c8cbeb8310d64fe09716011d3a070458d0a3741d34e38407512350c1a0a6d5bbcdefa465bf55d612f98deca5c38a736f575f68c9396ea143745bd0ecb1b9ffcd1472b38fe58b98c7df067e7f64d6429e950a898c6514abf0899ad1a798cdd069d0e67dfe68d5a732abf388cc590abd9360089aa8f6c9df9d64c935d3a25752a59b88b5c3386f1e9a625a3d822a78ed9f265e5c2bc74b188f0996f8d0eb340e3266901da533e1fd382c5b544f06156be724b9988b8c8e08ac316054d811e1233536416bea3662990eeb9186bad20b3d5636207956ada5ea560b12ed4a682475ed671eee73a35cbdfd1ad06babd82f343c39d505d804d9750714e57060b01e0ca5c931148b7d3ddef52cd0f63da9ed3ecf61ee88ea729cb7eb3c40178d41a0fa2967a78ee1cac608e01c1e8b8df242abb67e746288821197ad33455760011a69d58b7407bc04c5849d3a0d66aa47ea5080260ab7783841c187da37b94ae331d120aef2e52022ce7298ebcf6165903a2e680143f6f596aa363a826aba6ab62b1c4e6f508d1e8f9732297f98a996fcaf092ae64fa505e0b8ff00587c2a8da38a5d0303c047ce864e359818defcecaf5349cb5cc7d70c68a763e742821e60bfa05f79e9468b88e72dcfc6807e9561792768e5007da221b827a2b2a8695c7d02a9aa6225f0bedd93b944f7d21292184b87dfc9bde3d02a284bb28cd92cfab3c4beac6840e7666480d2e1c1ee71ecb76536ea5a402eae2588209642d71a63df8519d87f9b244607594ff7dcafc4208a5347e1f926d3b20e8e59d062b1d9a9b38afda3f8b0c5dbc3c460505f2ac2c3cb7e07245afef1a32255ea48e789b32a521f8ebdc26fda3cfdb63500ee2bca2504c75d3499a02259c43c85c1e05dc21a5c8d2347c64a4f7521d0bd6a035f08583d9e62e4a69ec8630ca56f7dc4f67652e92a43ddfc7220b4ec95b913acba001314f9091cec097a951322f2aac2ba9768a78675ae6bca039a20cdb908dc4654f7e5333d15d0d1f3eb9c507ca8c7b6272c2740ffe181afc3343a8bf8193a61477de50174285571aa5fa9bb3fe941d18f10e47a33ad3711bcf33ea98f03dfe5e0c74471517e682699ccf94441404a1c4d0411ebaee0cbc65a02d4448ced9c23093bfbe8cde50552b41723df72e11e52b85445a9b40dac61462c6b062e338a8437119f00d3152e82a08c0043475056897608eeabceb98c983f2bd6850ef381951f06d15b024202ffed639de06a4ed5e6248d2193b39610a6601455a2705a950017b184129a759a52a6380181b19b1351ab7b15d400392ea0c50fd12545c010138b91a90f7f81ce6d15a79181b859ea51562b31c9ce25a6148cb4c529e26bd847ac00bfc125e03648c114527193f27a1f56280d844863cc377d1e1c34581573783c909695bf3b35a8820f64e6d721b44d1c2054411a3d93c6b9f4e7608ed0157de20cba086c2d1a3cf615e18cb5036545016dd2304f6e7c26bb3833cb8083cc9e7643bcf0f155d16eae4d5157549c1eea296d900782850fbe710a958363ad209ac4eed59425c5605438871b546d349394dfe69e2cc759fb0470c50655fa6e00bc2f6b8900f09780a8a00b0bb9524c76a92519e6f5a22c2a294f82afcb19d72e3893c1a40fe63ebeb121bbf8b161f37b1bb976d61002a526e60c460b2416ad198641ebca090107a100db594095a25854e8e2b16773bb811c4f5ad074188802d679c13c897b317049cc54711d32190e04c3cc615f89c94ce07015e5a1a86b4f670275a662c26f3528907c69b6e9895932461db5060fedd3010ca2cea2caf8079c0543fc47c252d49d899c27469dac54156361a86342e1321aec856b743500de5fff0559c2108533cf325fccd6ec664d8a9bd83a38f3c7c466c6d6de8b1f9401a439980e022f3a0e794dbfdb05d2a2836f3909e898ca6ed39cd08ab0a49d9761868774a7356e02ab00594d14305b91bdf83f3d1a19226da1b46a4d9e428cd2cfe45f1033254f48aeba10d7ae3f4655d4c9ce970a51fa063156874e5e6d0213b4dfaeb0a2e704e99e8534b5f950f3b0143d7bad1523f2b258916b27d46e4a3288f4e7dc44e7caf3da7a91d48000c4d151c836943bf6fd93ce93b8b404b51085bdd2e1affdab559b3020c660f9244217659c4f1065d15442c60b079f6d3ce4687866ba94993351a1b1ba978ed2f800e114e6e63e2668b8793177b998218a830ead987ae63b4684fe1cdf7cf66d7e1b72a9d2546c80670a6d71920098149c9ae8a9d97082cb40250f4df591c0a84aaec5f55931c1a8ce4b87eadfc515e68edf5cd8609d7a59d1281777ef214ca9555a449d0db98b3b312759ddcabb1f205f5b1f98003892d3415bd486562b76277d24893dc8d802173fcec3fff7658a7e7977a1710629c34268ec17690236bbfc80dbbc83add9483a711ec02d3c14ac3446d42ea55ae244686815735751cb9a5c577ef5364c9949e097ac2d92fc86ac60b5ba37b1fb0fd79388013e0cfdcda6cdcf38f5effa85d37f667871d874e36728fe399851c52e22dd2d378649b1955f663d0da161ad5039ea5fd344bc5d7ac113574d660190efd44b3584e41c26f36bb4070db8fbeede596f996ba735866d06190b617530d5c79e0137400e4841d7293b9261563dedbe0720f36626352f9082542556f4423674a09d7281b0e6ccba1684e9d71567dc34353f34d5fe32fe71c0705fb95d9e2c73e79adbd508444aab7c91d386259b3329e388293f46e8dae9cd17c55a2c7ac47ccf6ac12ecdd6706b5513235cd005b69cf61a2983b3e3c3d39639f229068c1993f0baf3a1b59d1c79dee954532d43efcd08992333781ac4a6e5bc52ad2b078a2b1231df4a79c90803342d57d18e9bf314afb507abff5376842690edc4aed190684c1e0f31f7d084e681eada4044007b4ad7cf29bf247c9c04c1981d9ffcd38c4cb8125286ae448f4fdb6e1f041d0ef49c5ccf2d9c55868b8e93aff4209486832624a2772629c1c44fe3e43e87802589afc700198672c011c4927338ad23e2a071fa31e99a7c2c64fba18f61510a8592eba4e02554de3fc15a3f5fcba8413ebd33e2025ae82458aa3556c87be12b517210d504fe194cf1c4c2eebd02ed7d77c0dac09886d08e993470dfd8406e392e28584e80ff849c582204f1c7a3f6fcc3105f08693bb32fff78d454fc7f52589fabbf9473259e14785f8d1b4b25ffe8c7a6a9cfdcabc126c45b80e8c59b1665463db00280aa82c2f1988a76b187b70d15bea98e52990ab010eb62736a4820a6c48aa10176968202b800a2d88e0c072d2b05f53bde30212afe459202f1537c789e620b94f1429bfbe24f0b0b503017f7ad36969cc414f48c40b4b0c8867a2a9317eab2fefd085bd97682e02d4bd5c41fad825b011237037edd0b00b41594d49d00341b4119e7fb526eaa41652950e3fc62be0055408503370b426d67d84f19fc80a28a3eccfb59b018399946185bca420abb02be58bea607c2eba5f0d86a5d41769ed1387ae640259607ca560ef02536c37b13f8fed30425881bd76660ee798fa23d068c2e81fa291dc4e64a20604a77d2ec280100367b7847434f441d1f8ab6f38c82ecfde69c889780837ba4d38017588d3bf76f1cd8be40f97d73c7cbc6ee4f507a3cbfad359a0c06cfe052792ec1058c781298fece7c20fb47d56e9abb008ce9ab670ed06190b564706571aa47f004b7b66a3036dc50fa9388deabeee68bc125a436f410f71bfc762788abb1c0e3195c3f884bf4a10b5cdf53ec9e7365fb23a4a51ec55c8ab50657da42b8f52979bd32b82486b85eed5b6c1d2e59f3d0a90783cb0a13290089efbee6e18038e08e6851ec1250d7815ccaba2937db27ee2451941042a92d6414e5821219065352a5640bd1f1fbdfe65ad08ea81bbdd0fbfc2c203b078acea13a41347413671b23ec4c3850d402411bc141f0342378928ab5e941c4001390e6c26725b904dda62095d507ed890639ee4880129c4a97cb0125c76f8a02bce6ac9de7c9958cf93126d03c41610de46616930e4aed5cdd1316d9e758c3d5c9ba878894259c64a06057171ec44b60a14257470a8e7c5ec38b03e95a8d0974fac6862731f8266b5aa117a2a49104d176256c1c5645f586943735a9ab630170beff80449970162e6f941010ee410f896009071e7f65569a2ea4fbecd2ba0458d01fdfe8cec1b29789b0feda6c9a1bedb3c6378e46718b56afb424885791415ed2577085fec5a09fbb84c7f5fbb8bc1e82c179272e245cc7dd5615c70c5056e8a131e453de11d612d3b381b445c4e480363420f91c478853b48a4213c802c0b9da20b457c706e5f4442f014935248ce2fd054820cb89739c640985140e6364a666e57727407c261bf19eb6364391d4bd2f75643d747ffd8591e9726c4fbf9bb9185264851b62b50bac7ccfa72ffbf6c092e0df8b144ea164f4d8ea661434a8f06f120515187557c0119f4a79ab11d5567fb52e4e4f819d26fa3b84ec5890a4f36dd71888c15a1577a50c98580bbe891a0c61af6bb3ba2b01421b7443f5db0be300f13f876246b8e0200d9e2e956760e73e703712f0dff28ddd5c92175ec5a2ef48b990de6a385ed02ebaeab1d617b8883268e3706231f2e598d2d61bd9664e2797733d67e253a412515c102366e09d6c223a9331ba71068e3b1655d011edf3698fffe306179867e725b2fabe78263ec417521ea71ac4d8bc65a517c3a08305dbe1f1e4026a0d6f748a2d2240d8f12d9fe9673ac02440850aad4e490293eb84a94302815caf5cf4b8bb13ecca747a432c6369078e692dcecbf2416d107d134d6885fc0ebaf569ba3b2b5f80f25fe3dabc627aa8ef46394932142436bf5bcd480243d3d2cea49938710179b119dc00199e646f5165c42aa3666ded4bda742fb11583ccbf91e1130b74efd2319306a5f7c12d77ee05556402bc53d74840e0fc739a20f721303dd11a0606289b4592a14ca1bbfcdefd2b3b6821b39830d7b53c7cb0655ffa8e21104dd3d0e7983c1a3449a6402a79702c4c3c4d9bf3110e8b0026e22d6150152e12c6edc478a39ca4aa48c6895db542131f080322dfbf25e4e1c9d96121fd7ba23932c698df6363fbc4780f08ebb33fad1183bdbcf2a8b8406924ed8a14c143e642181c15a3251df4632b6982c77f4325f0456c2b3ef52e6af9346447dd1c2eca3111600465651cf4214bfe6c299f9a5a9ec232f3a9b28881d6f536264754b3eebe63be6b334ef08560079ed2be661eabef6f7cdad54a149693b38d9111a462ee99728fe566d934d7c48c336ad67f6a5ae249762a32882cc30709bbabd95fecd4f2d8eda08f12c172bda397ce1ceeaacde6221524dc957d74ffb11a77b5a5098c3bfeb0d00537e1ae807e3f65c2dbdf2669255362a497c933d24b2173c59e77575f5b08007500331caf099428f81d31293da052a16cdea7bae839a255973cac7858f670e961cd8395c7951e171e163c5b7ab8f46ce1c9cad345cf2b8f563cad3dc3c58396189800b97f95701117cb33aaa63c5da4b653f5471e34cc5e8483b144b011a23b7214704a3511f25e24a4fb95ae7d08fcc79190f48032bb71b8d248c50ad48fa8e01aad67e9495ce8e33d74c85c4812826ad8ebf021cbe297501404e3122c77fd366241cd1a9194ec627d101236de47b6bac0e2b0c1b6d5f9c605c96d358a4d6c979006a8d4f7f3082d17518172383d2b9365e5bf6b9510856dd440187d08d683430f13a04f2578f0fc932398c95bd76c969488de17206ee0915ca865cf4fbc53706c474199282d25070da852c1417a10f6b2d8e42f82deb0d180025c60ee58095db854f347a500607902ddde4ae13e8d3e8ffaa3ad9609aeb5a1e7b81a0da496cdcae384de07827d25c03fbd5d53f122de668e3cea2d69a60be6e870a0cc661c853f1e943ace7fd58c488abf549b6d6a0a2554d250debc1f88fd0589a89d3fe22ea87fc81243466c96d25107eb0a492c49d53cf846a563e11fede4d9c762a0a91da78ee584ba280a9397355f3f77a5c897ca8eaa1e10d5d120411527fb3766f74a48c18ce188cb0cfd8e1223d7b51b4b4e918e3d75c3b621f06c9bc26912c9925ce74acaed9d38f6c27c10a2081f80a5916037058542fdd60809c227bb3c0767a3eaf620e94029260af8994bc809a1db0dcd6bb73f2c6fdd9f9a958461e172bfdd6952fc0ff9674e8e95895188ec71f9627312fcd21a7402b030a94c3c607c3452c747807eaf5977991427f4d75eeefd6fe8d480685e0fd4bc9b6cb37d9c73d2ddd1a3530625488ac66759665b9dfa3b3392e3021b66a20336a93410237764eb881ca8a570adf16841d2152a4aa88c8e306ba6921de2af040afff8b22168b2083cab958189e20bb821d2bb23c74a810a8fbd52ea34962e99b717bf8c45229a0c0e65d26a1b6d681c5099bf79e76bf3a4dd159d2e9b84787845cae0059bdbcd90a5e2dcc2358af035d7a8baea1a4d1e8c526e44980b9d6ba421a3b796f283a5c8712f6b6ee72a42d7a88584c8e342884d5b2b51126808d573178244945432d7e943d69474e4aa208f1d82dda5090aa34a8a48dde9c6e1945722aba4e947a9e9cd77d658637e50a284b781e226a9508807ca74a022819485cbe66e7522a951a4116c1f1bf7618a535103d30d1e8df0feebde3602c461dc9b89e3006231b00b3749f42a3923371e1760a0cd024d39386cb3599685048f21250ae5f0cc428c65cc72a46d59dc488da6cc816260ddc322d3bbc8233b8b6c27c313f2214d8bbf16c13944a8f9f10dfd51f3653f9604cf024fcffebeccd167337011eb9858c0d21a4c569b870a90c042014492b29740a91e8afa3b01a76a76222aeb193413a569f79815818048a24699818cf3013f502085bb13352525e74f06443acafc42ca17f95f6e43cc32340017352092e0ac2044a1f3cd8d0d88543b8968569a4532cc471388247651308fa9cb329465655e8d379852dc2aaa34143584953267d6aea0d64f888a2c0a1d2a65b629ab2c77f8de6eafd212cab0f591a3d3d53bb1ccb90915988e68ecc68a7bbd991f6e8ba56f08ea08bfbeac4372cb5a56caa831c1750ac9696dde6ca445134a4c6bc8d895133d26501721d70552a00d509ff5867d91e53d547b0225677b4721315106cb058a8ca869a38d4da0cbf293869a4183b226108ae874876cc62e2878ce387661f312220a5e0f6a5cec489fba5781ef7002cdd61748b4c4db0e6d8c191c55413d51583dceaf4554c84c5b860224601f4ccd25dcacb03710dc48db93531f4438c10066e51afb90cc2ad456830d9d763f56b3d67feb6e3e15339a830e68e333e347f1d819c9dff720783db1cfc82d5771b47176dae254d8c59a69e136c7f05222839c8bf0e9bebdfda5fc0808d5b06501fcf1d79130d2b2fd4cf71beb601e3e4d70f2419f830b23d67a7d898156b1a073473033d7f7e1c046e1fc5c914405f6c1f0813ba45710e8c2483e39d7b5857b97d630669a8305db0ad0552828c398dd0b7e9944acb4bd21b8e4f32d8c59179430f3e00188f649136f07d2eac6ee911298cc6d8432311f837a61d2ab330963b6cb34b34d228acba683d0d25cfd60328f42209d2529bf7793a277aeda892bbd50fb2660cdd3d5344096d2094895455f211f73b023399e66920412d528337843357d9f9e00ecd5a678e573602c4b021f16144c9040e227a18a6594ec600b12173cc9d8d68b933de3513155506a2c6f7bc43bdf2c3c89d616ec6da0b7c1d4b3dcb070430fd998a6e13e423f196003cbd1313c6e9969d89a86d16e58dd2c741abbc3e272846afd78b9b506ba0ee6881a5a80d0a6e9825589a182df8f99d8cec7eabc943b1e3876a3bc768ff9f683a0bac17e396225477007c5f4d742b85dd56d172c2a9756cf01e181bb12eaf25e25ed5e9c006829975938626d1df80d4f7b2b904021868ab9218dcce22088198d46d030b312d0a323c8ed4b39ca2fd7bfd48628c2e299909a8f71163076650cc8d5ecee7e65d8ce0410406a48bddea4f6ae046845ddbe364c0d9de9864f954a93b0f5beb8b558a8c9eb75f5d48201db0c3c130469be0a3b4c312d6fbd62072456a4672212b5114ed758cd7f88dbab802b67a5ac6498336b47e021dab971b7f7f31da629462f5179fe5611f1f07e97df6abe3281206d66d1ff2adf961db1dda6b00d51c2758900ad2b4356abad1c492f0bfaef8455d4012cb97af93ab06b59cdb08243389d884d52ff9b12157160fac7bebc0f3b5148911da29b97601a0c44d0206aa95c91e1d9d63e437f6644a6a9d49e3ac38b6b9356e823c38e48f9be4028c5442460692eae35bd392c52fc50a3ead1611a2c20c2052a5654eb090f8e53de741d23a320c0f6d6e8a743add5583213d69c3e81bd138c5e10ce091e0ddb21360be0f6381ac6e1a88c25975dba2c9987aca3caa13e4c8c35f3b1bd1e60ddcff3fd2c4044a8aab070a97935726ad0931a376911db5e0573fa894711e6d87f6ba4898aebfb255bd7f777053a04351836e3dded174194c2037d43acd996074174bf1380c0a454bb1d8af2155456c58cea4283357b530a3c7de7817f6b6003593b819bf209d7e0dbea5d0551a0f89a808d3fd6f71304bf9fb0773cce52108f87fb036d3d9fc980843b5892937c2d8e1583efbe72f55530bd84af02a8efed061e7062ed5520aa2cfcf94326ef6d19499d06e4c3c90f3bd49cc3cb5a241c86de51023e2c42ce80573543bf304c125282d80af57fdbab2c77b9ee9a45f79060999362556a4a194dbf9036c6f9bf0eaee765ef05bab27a3e39f2af712d7da883649550cc47629c2684f015e0e7accc4548e182c2cc6fc1a7c3c14edd6012c6f70ff8c7386b00048c9eef376e544035277c8a946ed749db6d27c8571c8df192ca90accc417fb74128bffaf970011b2f782bc51786dc99e766c739afdb2a8d6c27356c26d3454cd534724d74adbf5dfa9785d7a85e93fc994d67295ff9761a91f3231debc8a52bc2c73d4d8ee43bba0ba07e574d373211b46b63e48c84f8f318245aae4e2920e38e5a1e50eda587cf6e4848a76b917b527bc2b0e690522b9c7e93844202b212fea6234466a5d65c8fa777b26c63d4d43442cb8c9b8c3e3800d77d8071a463ec400e39424e97201635d98d80a92d3fa976344a83ce530dcdaa233c5082fcc88650e0ef0d5cdb31bd308ee4c144d409f7cd2efc48e1d8cfc6f8f1af62d7ab3de8092c31cd0ba84ee7a87826a8fe23bc6067817728fe6132c027a74380280dc9c638ad7bb342da3f455608149e1681fd8818935d4814761162eeade419c7bf4e535472bcadac5b34248e39a783b93f1c889131915bed8dea997b9825c91f28a678eddc6131808eee37d13d25e5da8a9d026769267e0e8147076de0540cd218eaa3083a299ab18a3f15814a341b1943c841a69aa0431a25c8443b9bea529695ec89a8cd43b3e1a1cfdc2f2a2ea9f845cd2b35ce8f21b051fcee03604a9d33190542f5247438fa7273852c46246bccce090b3ed9d9123898d734c2542dc7a6fdf6ed635a7ece7b9a08901568af0eee93a667516f06d0e1fa80509794006629be9fd3441038d86676553731ac9b066b9c6de9ae591baf9ac1392216cdc55663b2b6ba493fe945f65978926a1cd791633c718fc3e8d17cd392ae8b7e3ec193fed8caec5760deff158023608142d570a392581b8c9ba1164e490c1680ab01f6494fc7ac579b5a82e393865d68f2c267637b2c6513c1432455180ea88aa552b9617d6f4569255a25d6c850fe3b843caea8aa7e11c689ab5a033478ea1235c3a5932b99a9b73980e0d637459c95c6ee970e564ce14c55f796a52aa0fe49c5169dc1e2100ea66746023ddf01b14d28e86ba7b68a35952c13b8a2f7f11abc2f5daa9635c2e02fd7ce37867c9026b50580682ba2e3094ea2c34243bf1f5321fbe5d69d406598a787030d5cdded9b92a0cb695b036b1f015b27859c15ebb92703388c8c969f40024aa11caf6c23389dce45e52b69b4bca4b877135b3e128251ea1e869f6f3631d12256297aefc96cd26a53c38a66da5c2c175c6f42572e64bc6d510d3157b155e183b48a3944a57b455fab07af064275c2b765a4666e817e98b4554e4e44f1a1ae769f0a5e5fb72fe0416bb98c086b9121be075106ae9d3bd490ff4f91cc91c4ac2d7a4119c2d2a2e0f4d243a72779e956660836c000cfa87f48d22dd3384047c7f6d62e6e179fa0cdbed2d75a317e466c8df100a3b314358c0d4521e81b6699e5021cc406ac5c659668df006c3f9923ac5953e52b1253023ef7b80b4b1c6f8860ae0d9630a62a2c02f80521fd9c9a2b7ee50a75dc9af0dabb6026e187aa328d8e5ab22525c9b026509204d937a9d680c45f1a3ce28ad18681708f01efe0116b4f6578db617886f5988efc7f0eadac51e019a20348f67160c7b9c83a2010485ea1de9d18d3ca2837c40ac6d9273171ccc5a81089c68526d9f90507b267b1dc5755d995e36590ad07b57f1d57b829fe0e6d4ebed22dfb177142d8b13084442a8950d3086256c3040d346c6acee5b6d46b35479c83432f19745df1475ffe2394d73193f28f2097fdea0902d56498220c164e8500d142a5d9c2ca075ceb6bc638a76d29d14bf2119c383fc187bc40ff89ffdedb9534c87351c01c2ea544dae76a85a270d89dddc0b80643d30a920d86347f1853a1a7583b8d90d147f36d325a4057c2b43c90969caa3b17e49b09be88af6f0214c91609392c74b4e8f5274fe8f1e3c816142f9d4621d69a9e00c55c0f05fc08f3c97eec4f92981c84afa7e29e00d898dc9f17e5783e0a0ce64d289e30101a9c441a36535ff84c69d78a955fa8556cb6a068e71134973c2aa952f9aa16e8f3a8565ac530695519c4108cd2eed574feeec6fd2e20f674520c61ff3b8dc2436d0fe2763c8621b8f2575f06ac51f4480c599d7b4ffa2fe7fbfc32b34147d4781c3c178465168e9606eaeb10ca0427b2c50791c07077403e1239a0f3b039746f9c77384a0cd802ed1324fb1b587f7bba8bec47fdc0db8b41b5c403c65fb56a91abf6926814b7e03309eb650af4ca8a25c03673716a42ca2a4d6c6080831c11d3c61109a014508ec6074043422cb69f41a6dbbd35bd30bee0072482487f8e47be5c59792576e2def6a475a8d7530ef4da8b4e89ba8a1eec340eb32f000195b00ae4c01e1eba8961e05fe17403bf1224c223fe8de23261462393f202b2e7ea189c308a4cdb7c6960f3fe72c8e3f0e93049057642336171e9ed9f815e891114981b4f22fcf54e31b746a968501088aeca261ec16ee5dea80cde0486cc5a27e39315b64de4b5b866bda216898ede07020566bb2b2827aa44645f6e4d5ab387c0c33a1c453c96142c59641985562d88e2d35e8add8944e77180806a9cafedb35671c700681ac90366c155fee9c7dacd984090b17696bf97076de23c032685125d05036ea002ba31ec0e5d2a002440843390004dcb2a8367632f7555055d2edc0dba24f3c814451c24cd550688f5d64b1927047a12182da4fb6f2e0e8a0b7e288655455c410a7c1576fc1270800068adf8e5c5a7c66381e66963c0e50e56fa01dc07bb18c62c286c4705b19454208edb0e4304f889c87f48c906a53b6555a8013542ef1f6d5a039013540810060dbc8b7e9d217fdfb73bcd3f296d3eba0c3408fc876caed952b5f25e7d599936c19875c104c845033b3dbd22f544c5d28eace860586008795d90a81035ed66c671dd4ce5113595d914b9196d6ae2a3299c16c429c1fb4de0f8d2d08a50917ed3b4922face40552627813c47d1b2daf0bae1c9ccf948ac1132ad3d2c95fcc2cdc3223cc8a0d0a03e4196e5cdaca3545d50321af1e0a84d256cd699f0104a136a0036eee3f58625fdc0cf8ce03c256a7a493eb017357985aa94f00f2d1530d07451e5d5d94deaf500cc803c9b96c9dc697d17d1429dc4645353dd68d33a103ad22098817f66e47616f9cfaec83f388f61a857789dd14ba26ecb39479007afa647dcfa5fd28cf9bf6127766186b528c2bec9b79c7312d086183e82b6f9220dc2a9ebbe092e80740b9056aa937618d28fdf3f04ec0139d0f463c6b8646d73c0a9f1ee1e3dc0041a165b05fc3c5449eb7694471efaa8249e6dc87e25417f4ca58dcdfdd1f095b8e545c297e923184869c26748ab53e4f68067237ca9e787a7c3252d1a15f9dc0c773c9d5e84472c0e10ccf947ae416403c7a9b11729d7a487ea7ddc208f25914405f40a0c473841f5fab99e0f3952849cf054be2d097a01fe661ae11e34befc5100ca5c0d5288687f241f89cbd031942706bf5d7c638f1203c52bbbb21eb979488e685b8e56b15b575376ae755202f6a215e0704640c38006369ee86f201f72016a67a05c4f470c4e27679993dc7c65988340b13605ba0164e44dfd7632c25588bfa9b5b5f5fefb3ec244c9fe77cf9d59883845f389f6647422a82a6c46b1427c1b9e356724c1133091d1209f7382050d22121e4cc009ae0b4604471c5807d81410a0399a48de7f02043ec2f12a78a752e98c40f60d9a3d757edf60f32538c0d61329150c7ad78ff4ce5e5844286f27f40498038845a2eb5691bdba66be9fad7d6388bf731ef6fbd5ff7bee65b19cbfbd6f4135efc8b42dca7b6d190a985cc9ff7f8ea2b96e43e94abb84213b8cd8c740ede041b1978ea0825f3dc802514d50a0ec780d7d924aa50ce93b2606ce937cc3a02aad5122029a5e390554bab2932a53eaef6e4d2bb29d446a73ae8e86fb2146b4f36ac16bd9bda8cda191eafb0f69fcdd7ca44d896f3d3b3da131a33c0f6a17a613f8652e2fbf49f040dd388708eb0df2fb0730e64600ad787b30b88f1e067cb8c412b8a69e3fe6d77761c9998d6f313aad94462ded0056bab7f27b0d91abc6c6fa7590a1918f004e89f9982c4f0201cdc4214863fa47335c02682885bd040bf5fd3cb33e6f31650aece983a8f133aa10218aebeb1da459fe0801f9f060e3e44b9649f48d45783014bd700446b7a845e4c52a0b99c7110223c53d203e1ffd2795e88085e6e410c187013ae5037501a5a4241c9b9702b887d9da298b0cd41607e3c41b553945cfea4f5726e9ed4c4a09435ca9ae762b7cda757d829448a3c88a52d449f0cc21d01886850b5d21e0346279afee6fafa6570bcbc82e63b6bfd87c695d8a9367605cbc59b79f1bb01b6b71df3525b003c9861144773246c47c206de23f4e29e9e7032ace13dee71ca0b17d8ca1ba8505b004aaef36079dc9ac94db01c29be4813106d47a466b6fb98fda13020b09e831216d73716d3e42397f49be71e3484cbb9fe2f405915acff400980abdc84d07035ba04d0236d8ee290bfb4db65e60db2de85489d15ad4ba76e20469172b51dbd456367c32c382c7eaf3c0acdd5e0fb809d314b47a807b252d1dc5d82878526c5f9b6ffe441f9948944e4778274483ae54965dde15142af7cd7ef008095f2a10dab12033991b7a0b86b2783fae0e82a9a24f827c866e96a3f79bcc5fd752b2ff43004ac78630f1e44dd105657b891b0328b0dd6869dac7897e409d64cf115c5f4141f3dc3acdeee88a71a515ce9c637cee13af46d47ee1591530ebd244a15afb0866eaee5c59486f9611b88f8602ffc11e5e6246433caaa95c0ffacdeeb52ddefccdbcf77eb6a54c843a566ba1ca34ec4a0526b4365ff7cc06c12875a01b172ac470c110eacf199c6e5845ce566422937682e8b3a0c9c4b4062ccabb4808d7201a05a2e1acc8fb3523c3125cd5e8e85b32a065d39179f8b097a595c34dab3c1822baddc5b83c474efe0065e8893fbd34e95c868065b7fcb3e5b41f53cdc9655d511190ed720525a286345f09becab3d7894095b4252214cce11be29a7f7e17a18810275a752bb4a8dd5d883f4902a7d5fc9147d448c292c5ee0f0f318f78a3fdcf7ac099037ff8a0277a08ae078c4a7b286c04ba1fc47ca5d1750e58312c226d5cdaea2dbb69c2e357f279b73711d843c4da2d1a934f9580cdc15b6b39f6080ed4038aa135687e67513560a33327ea58594b4a4348bae73a460311f15cece232f7515624107171429aa529cca99ac9958bfec5604ed3cf0a603ae07a6e7c2482d63e77b42a5a5816ccd6ca53b50ad26a4f17a7ebf058a4ab066581b10e5118e244194619729570fb5e7cb585eac51a168ba978e86d827ddb89845da8e9b83590ebd0b0eeb9119e898e0a1272a0d84d4ea515cc6ece97905be6a99d226e6197203ac2d915c6865eef54fe9608d5e6b67aa7a561733291d0a0f84993c329a98ff5848ac583b03852aa3f71565be1cda33403c9c1011a2d76ebea21eb4be711f5e82dae1f703aafc334dc18942f3a99de833b22b2b34c389628c8adf6590ff3052a2c378aba895f50021544f9360c9640b8042348d615d4664705e7f00e29827f648e697ebba1c15011053be5f4ee352346cc5fcf5db80849914e4258e621e4241fe205d282a0302518e46d440176128f6c39104a7f4691cb98828e6eefacc704356056990118a64a87342c4b4fa71746c685d6700da712a5f34a87b7fccb97dafcc7b2d3e083064ba1a6884dfac20d7eda28508c89f199ab6288540f001f284f2940b3aa29a1fae9b91608dd566439a8b4bfd4098a2065d1bd6e3464af1a40f0c3679634823fc921491f7308877d9784818b603e188ec3a3010b20489ba4fb7576d7335625b822de4ced7933614565ea9e0ea4b4738b21b624a30e4c661aa3bf9f915a3c8b9c1c0811c2b451593880b43eb352818b250eb4ddc3a27325e5e7862ac49b3b6bc97cd9af209a12bde26f218895f4cd82d76c15517fa39b066050fd84b54b8c04a9e2c523c6624a2a35b1e1aa30961cd883fe5ff60c979c44237282e6083c4ef77e9d503846c015a07fd355c708aa4a374eb06c858190d9c5b0c9402757b7b85befca1ddd200ea9458a4e13b81134ca0617a80712ed4adb95bcaf7488ea80906cb7224476a749adcc8fe690a107431ba3a292bfee51bd52cd9f4ece27f5fff97fcbd899469935812630349033d0333489ca6850cddcd56146e415aa87e484581b5d49435cadc212f689b4021406e569f25de6777777777f7f1e8c874f4718c3a561dafcc59d350eda865958e1ac040502e608708ce122217234158ef656c23196c809e16970e26285c8daae4f314167477777777d726dddddd583aa68d3c773ed0ee19d6962cc6a46a5509b7aa4c3283991e9fae1e17477235894b4404ae9e0d1e7362fd52f2d025aa7fbeff8fd51521e2ffffffd1fa5305079110c0c05af27302ab229918224300b0b2d8e06c7276027bcbb6adb56dadb53378a58dd4f1fd8041d63083c8cccc5c0322350c893fbbbb2b209dc21c0f224811694184b303aaab1e09af644c342e747747c97a19ab0364e90628986410b70b4ab8b14b2201522b8b99192583f6ff1f4c49a49a5ffa07fff7ff1855ad2cbd5fad1f285fafa2df20b50f98924202535258c447da2a2405e5791c1676ef112ea3a20b1a4a645654a3e09190646a42ae1f587785f28ab1558b5eec202439e99abde071d0ff7fe3e1fde891fe475941e9ff3bf992d0962e68e9ee06d2f9ef0c11261abb972844cba0fc3f3168dac8f3ffffa0ce944cc9859f9999cb476923a3aaaeec8d4beb82a93bf92f17df6f2ebf9f562b2f044bd8bd0d4cbb85417a6977f7bdbde46254efedeeee6eb4752a3fb976777788a8d246565dad84d0625beab8e14abbccd0dd374c0d6354812326929680266c744a284316321ae082cfccccccccbc52e4c748465a080b8e4e06958357a33ce59bbbbbdb360b226b67a4dd4dd26a05c1b066884dd2649a1a96b64a2193ce9942d4537432410a9bfdecb6cdcc2cb455dac8165c4ec76b161b3a18520a4a1be3362764e7799ea7f5299263e76148949f0accf15d1c6a24c7a8c849ed3b68659962c4b337231dda928e9613495dc920440b8c2da2d550c5a1a2ea2c6f38b68ab44cd4263181527be944886092411c343aefa49c145605f0bcf7c91763547292f9e9a9378e68d8e0d10d09f79a3282465a0e92708874df4b76198976f8947677f30693058d83f216256bd833a6c0071c0670299e642e18ea21ac444b564ed025231c66310d4944a61a411cf6fcffffffaf848007062f48104b87476b486b2bc6a403cacccc2f2c263a19131719bc26e0536c54011c3b9bffff312fa58d9c396968bb945e69e3989dd246a61bb89032dbb3da90a94846222a7477dfb6512323ca0b144c564e1542614c03bc1e4d846496546cf1928aaeeeae65c9642933b355da2a7260c491c4d5d0081404e426a6de84904045728b63cd77fecbff10d2ffff0f676fbf1ccd76c1ceff7fd9fb1f82c8a57fa7982633f34b94d246bedc3accd894da93ff3fcb8a166bffffff519356abc5d25adbdddd6d518e62666666d304e299c2242c5256a9f5713e12740fbaefedeebe4e4c8aa45c236965d9dddd35d345438375ac5199b229aafc35f0c286881f40386ce839f140711ef168e1e0d57370fe655fdddd44a069234f2df47cbd1eec68316de4096566939a344003500a0d51644900077681053024a029014d5a6d86967210425ef07741bc6829c69f45a4f498a8890561ffffffbfe57ff6e6eeeeeeeefe80e26929a783898dcd8f4a04c1da1900606b1720e4e2088383c106403e86ba13301332772909e85861070c27a8e1111715207a0c57d07a6a39b59299997d67bf6d9724aa4a1b79a5750d24a2bccc5c43099aff3c9e90d1034734e2e7a1f210aa5012babbfb393bab430818b9a1953513c2fc0ea435bbf7af455034832821c4d18c135431844b7ab569427974c334e1b4d65a6b81f84a1bf90306894523b300393233194d25592e76feffff41faffb7c04dd1a846854f8e0c329c98feff7f6b65847090629ab5a1a8d24656fd36749562ded01811a36946efed464f73f2bbbbbb59466cfd43de9db27b7347efed69eb002aa2ab1cdd9b1b4277b72e25917a7669b72e55726bb5a042abd957111e356630071198b53a52dcfc000a259a27333b0d98ea0e240c3aa8c3854279fb213564419b0afa656666c66d9536b20597d3590d1f78d6c63081b5c18d0a164a3ab2549c40435a0e8cc92a44a451e4d904e1a83978e60d89c50ba3221a0b943095d8a0c6dd00a9211fd659a3c866cce4a118989999a97aa58ddcf9193ab2155031591bd3395f2ff367dac893678247a29a280324f0104c6265638510a33a1ce26045d0cccc1c9cda4a998f1c3cee6243b666ff7f32d8b593fe5be96b9cd0491f800cffffcae4ff95621ca5bf950924fd2cbda945005bf23089483104d7b352225af275f5526ea6ffdff2ffffb8e2ff4f2bbbbb3b08f7a01cad435117d4cd930e1d4455d8631831be33255aa3a2ddddb1b6f97f962b9fb546df6f4601cf9fcccc1c6357dac8dececfd011d34a0c1f13a4dd40aefbff0fcc0c7b40d58d0f397c66666666e60a82f85a88d2909023119725304122c47b8f449523127957a6c4848648ba38360ad4dca2596bc684624cbabb293063fcedd9c559df8b41edbeb7bbbbfb1a903a1743242f4beafcff65af9c9c6923cf9247fdad6518c9d2a46359d48c49ff3ea94aebee4e5a02a6ddcccc5c7b72bebb3b87f89f5d4b61d6e1d51d8e142d2b70816bf3558728597a10c1ed465690ffe8859ffc7777d3f84a1bf90306998586c86cd1b2bbbbbbfb3de7049a72ff7f8ffebf8622a3b567676068171484a2ca048c0caf54e96d56d13febee9e8012e2dfe8bd38eebeb7fbb004944d29962e5499314221943fd13ced9e7a4f3b4f3f484d55b5999999744c3cd38c40c33171cc4ddae88c14fe1fc9e927fd5fbb979b99d9c93a5927eb649d2eec8c297a0014e7eb12ef43edff7ff3e8559c3021656666268539c0e28caa9b605d6b9dc8b86daa7841828292910a4614258d29a964c4291dc1c1fe7b9586b9cf6aad4636835e4a1b3943b3eb5596a09d5acb102e66f040f239f66670688044d0a68996720a38159c224e15a78c53c7a9aca9adcbb551983d6d3700285071b5bc4061b5e3a91a6b70349cf2778003bcca5f4a06857a2905e599c5c3ddf73ea025466ec6d08c1ea1a90e339a019820c816dc42076566ce9918b29aea4005e545955a013bffffff7f0ba88913a7ac1f256084fcd65a2dde0fd1d41007981a5513a55e52028e50ae1c19f9e8288b7ad690a10c06ffbd4c925c4f1af0b078572d764005955c61800024374bdba22f2edad8ffff3fadbebcbcccbcd0bcec5e7a35074cd676acbb9b2641dadddd37b8d246e6cedb8d8e9682567b6fadf7d67b2d3333372ae375c1ebc662ebf684d7e793cf5823bb371edc09badbbcf1951b2b38781826f8ff0adddd6dfec2e633333333330b69145366e6aa522b33339260692389457b4362b496d98c290b004522490d8b1e5d2df5a9433b99e169a1d1ca96943c3a1a2928cf1bc9ac629eec8810eaa9da90a183a4cd4a6a24dc3be10238554b1d7a41b476d462d5995084c1b89892419897f42ffe26ca0fee86323e379fcccccccc42b25ac8564bb5b414aea5722da56b295e4be974dba82d14feff4cb5e83233b38e2123398c9caf17a9a3336de4b943a32a33f32c56394459d214a883f32ffbeaeecef1336de4994378369de8bd12eecd110425408b99696d089869e3d3d6b6b5a610955a3d449536b2eaeae6414b652986d70e3e948c565ed0be1a357370339949c260c10d43bfde15591906e6d70f5d6923793a3f7c293f7e2a3f801730f42b0238a0a04e0b51cfc7fce1a18bdddaee6e18066666e69e636923b3ac869ead169cd5e093b3f6b4ddddc223c6b49b56bba0eb63f776570df8f5e31f358b3395cacccc3d684a1bb9ebd90a7aeca8d4fe7f2517a6f4f7006c732a094f55f280d4efee6e24163ed2ee77dfd8bd95c982056ca1e5ffff1f66edacbc4f1f640e7cba7db4277383120724e7ffff3f21fdffdf8e8cfffffff7fe68041a3cb82439e65f1aa1d59ba6601536314db8a810171fba7ab462e46a6010b4600e394b00cfee6ed408446142992a23333305704426995fca32392298b62720fd101d39cc0ecc0fcc90c94202c8cc6ca34c9373ae70dee7ffffffff49eb29cacb0c0b14cdd9ddddff4e2d570c4ff45478f98c9036a43592511e18ac6bffff1e59ffffffff6f9637ab1c33f34cd6dd4da45d960db2fade5a8d4db7099425872e346492ae1f56504c414e43ea919d5a2442d923d2ee59eca54446ca2e4f623ce7abb5166d6205feffffffb31b126e5041b4c9185c4c2e46777677c3acedee9e41a8017461c0ee6e09ddb3e361594d48c47c0881871bdcf145d3469ebd996c879ebd1c4a5329ff7f3853dac821cd496ba295e56b57f6684dc69a69ce5434e6ff4b046d09a22d51b45600162ce16155d4940286111b9a7397febf07a0701d1d77258466ac967431966c804000c164ad317b8f1401f0e36972f5feffffff6d82a58d24168d2b3647bef0ffefeeeeffbfee81ee678a07494757070e4050b50810efc2f8ffbfc9718ae7bbbb5d8e4a1bc9e4236ac5a5ea6c2011fefff7de1b1b01eb000c902769c3918e4f9530828cac3faed6326a4b242daf62b5f0336de4f9b6166d618aa8cb6a7088c9ee8d55d37766e699cf3c9eff9fb4734c7fa21dd2fcff5f436935b3870f74104c5a2c46f350b9f9004e79a81180316842779b26cebc974a46752376ed0e4b6b052bcacb4a9299fe7002435044c3890ef1e5d5b1b49159f576b5d5526f58b80b1b04c0866f4893666666a5aa999403928aa8344cf807c70014231a58040004611c8af34066600314000815daacdcac401a8bc3615130180e030381501810008081c12000c3500c80d1204836491f709e65abdfdc288489dcbe7d003a896a4b760d92cd0b885298e5954d7083f682485a120483783d47c82044f95e0e11f6ab9517db7a5d786f22911a289c89044067cdce2d4334db0ffe6f5d06d177548eda6f918c6a046f2f921bc2a2543d42cb55b6c421ec3eb877b74513fc91d94724d4fe23b2bc02df7c67fec3ce250dc854646c6f589b06013b26820d727a13de591292ceb597ae8c42a92b735bcb6ed239a37c125d5e8b7c7f64750113b49399de5440f4ffd480ce20603e4e29712bc80f36702fcb965a5b30eb61225f1f3bed86740478ef85a4775b7581016eb308602e20c320219bae1edc5ff1eaa35c5b80438163bf0f6bb2430e2d554318103714bef6b3dce2298587bc9aecf109624ed4f0677db751935222498044b406d1583ad4c0b0b3ab45279d928375065229e1b88c6e8d3b4cd0d8be975792d25b10bb6fd75316a06b9805c8cec6f9b4d11d800da40614ff1e3012df4a4e81928ccb9371002cd14411c83d17e9d1b60697423608a9c3aa92ff0e76303f3aa411ee346b8d5533887a000b0389b76f428d292f7fab5337e657ac21a084f8654c4666eb9e4a6f895b50363a7fd7cf9608ecb81dfcdff5cb41a36ee136e4c43b482b208fc52507b1b8d556d5080ad0001ec3431923dc1d1e605eeb1db7cf66637d618d9660f8789e027738c414f64f6d233c572844a1ec0c5b010cea0771e40f44ae2312afc9ec028affda73c01f270fc84399ef8d9a0eebfb43ab1872c0dbb6ca0057390390f35bdcc0100c5c04eafc51b649030d539b1e31424b3361e6c08accd32804adbb60dc769f38f054efeba04fdfaa12cb8fa026d63a0c960ebe89878625de9906f4d4a4a71419aab0262f8ae92b70b01ddc62a1d25cc5f8399ace806d887c02c4469a8796ac278ae9456f8860056158224fd216866d0f2c048a6869f580a003b43c75b09c48fd73fccd0e21849b6509d09165073996efe2f6890a1d797358c7549d228eeaa04911eb5c1b20f74e7c6216ced37f8ecf2813af0069abd2411fe4c5978d1cadf695c2d39197d5279ed16616ac0096ec9f87b0160b606b004df429b4dd62d4b8dca7264cdb11ab391f026ec206e247b991114e852413a8c54827a956fb053a23b7c3c1b127527b09cdd42e02a1ee81a681ac3c9955fbc506af6f5fcc34ba2cd9f90c426e86550bad6fd2c66700c73f99166e66166fa22f3515dbea11ae62ed3d1cb601e8bb36ae4b0fd5087d3d32d8e83a9f98c42a922d5306c651a822733d86974a00fc1aa408f8dbf437f28058aaca73380bd039b80519afacb27b0dee9988ba873f298da7254da6687269c51a95ab6b43e35b1af6404a46fcb72acd758ed7d8cd93e6325d8bdb42aec9e281ee22de93eb5896a6772c0d7d11ff60c0ed68480f740a7ab2f345f2c13e95b414d703d98f647d10f4d35df96ac5ff74652dcc30715ee9ec747190ed0f5ec9941658c5d95179afb324ae86c4003813ba7c6d0230f6436640f1349d8ee0e435b51ff8d078a620296a6f9effbb85c19008ac96d93168a0ff395bb0d1df3a939921f237824a8583301c00cfaa018f7d5e1c284c1eb17cb9212b40c6c24acd8ec5746e0632198a9e71254132dc27f6858388f76c4c8603c0975c7b8e3cf0fa95131fe8ac1575b204d0c98084cfc986da314acc7f8f0f25d111fd9f782d783d0ae022a34e5ca058d29a834b8956c157ebf4c4d86a1ec4c02515807161b2f578ca6068542051fe8a1596188d3328c62557ef78e6ddc77cda202fd7b56aae17793db2d7415063a98184fd7299fd1e42d4421c6af4753e3b60c874d2cb503234228cd34db9e5c009c00ae49a2e358611082e7301b9df7feecf12f7daec17f2302eafb2958f08c30d084c09818142a948326751042ccd210e5dcfb5c595b1527e13939f2104fc2482a49f22bc629ca501d52c67ba7162a08503433ec603a032bbbbcd8ca9111bcb4d2de6d48c1c67143036fdb31d0d84fa5fcca8e82c08595d094a2acb16bbbda46c2689028dec572ac34c9f60070fc75b3e691a07c01dff2cac4499674c5b07f4c161651b4c3557d8c6c2723cf88f49b449586ec012d1224f346cfe82eda26ba6c20bc0920adede6006d020048d24c316ebc76395f7a1473e904f3936b5899ed735c01121c0b701dc4e769ad6c3f5051e0504fdf11f2b82c4812c3aa5618c0757fb2567516e38cf363bf658a6881d75df546e0e17d2be1c3d74cf6a0b9d7d68435be7665f6e4ade12e4541c32c24c8994543c00b2528f0da325dd7ae304497cf44d0b0065e625900ff39fd277334749fbbcf14359ead9bd4a2ad64824f1b7a3e93206003d22f233f0f68c395c454d380091515e3e219c2eae660de2254c7cfc3cdc68690d325889064f3d6086df728c889cf452adf2622a43847cbe169b7d4f11c50ae1b6dde0c6646a317f28dbf56602984dee72660aea99049cac28f2f061f67295624cef91ee65a244570ebf319a7ad7d26cfe9b9a410552ef552027dde3df731caa9cd7341f0d9e28b3778be05989fcacab2b3ffcb5962337d5d7d8336935965ae78db9af0d626cdf1b895c230013efa510653240e6d21b430e6618025b1ef0d0b09e8fe4ab91a979a357b29950b1ff84941d7b2190dfb88c3c6cf8a2f6fb823b05880494a9886df38c64c58135d822f3db06050998866ced55a527bada21e2ad2d088a0bc412a105b89c8d9368f3c78e3154c436e407710a70f7c2328a050ad06518be20c86d21a230ba5d5080a68360fa9b594c756814c4519b2de7aa42f79382e6e5000deccb6669cf1e4e1bf865e2dd1a181b238eabd55342ed02e2535c827d8746b0d7f19643731650008387b9b04d6a54296d13ac79459752d34e5a54c656b49ccb539bc22a0cf8055fc3de246f9fb3d1a65f6a03647237851f9cf357893fd450d1880b9c643d42a7b82b8d370a01922bc643e35e588110ba7fba29935c77ce3750f01c13c1b5c4e42885a5916f58e235d430254ad9d48df02aa214daa8dade837cba0853b826fa99934fa9791be222bb30aa71c8b5d78be5211fb085ccb3cb874778eb6a00fe60835b45fd6f12a60b3e1ab25d42419cc2c41eb15e31054dfd523547117aefc302e3fe41862ef70822f5d557207f068d643b22e9eaedb571ed58fd46cdd16cbd7693cd57da719c9104e8bdb301035e50745662eb3fb9f3bc5c334502abd12656f5f3bed4b51019718636f1d6c06a713251cae4e80da211902542cb546370b2038bf816fd2882f835ac2ae9bd205752905355be88e180bc49a9dd20f5e9e83a8147d5bec1930361bbeed50f0cd25b3981fb4e3ada36e3a4377cdc93004dc18ba2d621389a4476a956fd44508ce60c6d4aa97ca229055338e1db5ddbff6d2ed02fbb8e44f1c03e679908d78891fb22c96cecb1c3828ca43e053a934737145c2b758077043cf4c1859f8521edaafb847ee37de103f8740633a68f48c503aea1b2bc0fc24f2aa4ba1e91361bb33261ee98a4b03142ae9f6b1235e88e372c043b0a4e57e0caf3d87180ad4eb7947e0d20fa6a01a69b6b5cfea8e29f56481990dafe1b56dc4a4228ec314e54f30a6302ce15a186d1fc26abbc9ace436b5ccef42edde5fe5999a054eadaa1cb586ceb2e0168b46582228b85b0086662b461ae25bdce8f44161669f99038d924cd2cbed00889635e5c02fc18a8674409e9ad419539616bc9b411b92ebde20e22dde3a7312efc79428a00586ef0462f635a78b1beb7966479a0c6ca9b7cf8b79d3c1454e51730291aca67a9cd9357cb29c905f0e45afdf436ab7c6f11822f527a5fb7679c939a37a2155d45f30bdd5a3f563d6d06bb96693dc14f8e8930e6f680d28fc1e8ecbeed56bb1e42c9dd67c09db842b78c93cd148ec9265bd9421a2323804b42e8e3b817f4d0137e11f54a998ed9e3a4e2eaf5f0585ecebab5100a71c2489d819ff61b5a813fb03312f354435dd764f81fd055df049ed366733299010817241c37d20223723d2636d6ef86dd65699500fcd00714a7402051d353b938c462e89369e75ff714b1de606a074a06894e3e4bb141f876701a7c805d8c974294cb8450b08883d9046f5f655d57e95174713f6a20c0c9445c9b54f6b2cb72adb335b5472f72845c78bf87e73f4a3c23092856b5c8417a87549aeb794b6f85594504467f3d84fefe4eedaa0df199fad87aab0604fb2ea147a444edbf7a60070a9f2d07e0db0a129e9ecb96c287d38640dc7f32dd6afe1f8783f30636fa47792b3f32b9250aa4ce1c04c084c286498515b7e0f721a118f68ee8e0822fe58365d5ae0daa648631c5e0de0b60c55e053a0d480d4a690c2cf54aa1bede6bc97c9dd8ec40998a03ee8fd548d8cbfc5ab4c34903d29a7d9d654ca29b619290b5e4fe23f3db805341eaa4b5fdb8d39a8dcaf91248fdf7296e36ef45204742f52bd337bc107f24bacbcf8e1fa7d6055191c138fd32ac2540e9ee4f7567021e7b77cf43aa7980f9a01bf9a0ef44380b41899fb25f7b39d2ee3d4bb6fbf72028dc727a2386c38eb767a19b679de8f7e6d7b741e0bf27d4b4c692a11d28349ff0760e3ff5aca5bd335f210565a13d95d63d3ff9c7cf5032be24d83185a69030cf215e70cb700913fb01695f9e448f67b9e36a5545d760576686219be6a79467be9c70019127f842a9086b3c376ddac1c1ae720f4626a21c8faa937bb40753bc8154e406dcdb6ef58aa4b2ac975cf4644f9058853455e2103341fded1ca889da8c6d889656a1c44ca5c04ec99b2f343cd2947a178ba8a8cd736725a06fcbdf07e8d6bab1390a781390dfdf9a7fcd58997f92811ab1af2b50389f3a9fdd9b6898a5c58d3e7b59dded3ce2a6ed26736a2687feeac17b07b5bd8333c04adfb4b38fa9199658c4bd274291a4892210782cd37338099e3d15b4cff4eace78980582134b84ba4a054f59ad0cb617a0edaceb9ae90962ec403eb1cc50c5e5ff0d4a9b0f5b868a71d329e04b9da9e222c95875a28223f8efb33d708c07064de80cbe24a40f3beb2499955b6acea68f174d2ed067892f60ed132402389ddb5ea0a35683e2b9f7293a38190a65ce371d6ab8d69e9ed1fa8a0286058b1c9caaf9975b51255d85119b8e4e91d384f769a393a587a56f6868f0fe802c216c6536591544a22a232c5066cfc6854d6404a09f6a988def85dca18ec6516f490053c1c81e6ee925745f666cab0b11b3d6203e9aec699b0008498709132181e99dd8f4a85faf20503caf20823108eacd55e251d292e54d4a970d49e5fa455168eb9be00a00ed774594930dff33021c80e3b41ec0d9652d101a40d74fbf52f8ab226ed52c4f6a43245f15e96a09503afebfe293588cdadfec432892eaab7934a246d40aa3c0a095d1d387ae4dce567686364cd6cd06fd27537064f273fe6200a000a03364b240be04b2ce07d5f71a15197c1625b5615cd5ced3f4f21aada64d03a09ebec97f94771c950192d3c4906c04692454465da07528019eeade26bd689a1a7d3937b58c03c06135ccc9b883f7c08ddf8c68470a610b3a62a499eaad6067868e95433c08c3c2eb1123697f7b6a63d5a23cd8640bc3c1d82242fa81563a0d6fb8d489f0d8eef403fbe09995b782a224f853e131eb1a498184aca46e417aeb0832ee84d6e90d0885cf44f30661abb9026d14f95c933b334e0780e0d3f407463fdacc92529461a782af5cdf2cae11b05425eb9f283eed8dc2ced0991e40b196ced92885ee643305dcd3495d772a6640169323aff4dd1f4297ec2d97fc1f07d528539332886c5b0f5041b3f837c772debd0ea53fd8bf2b14166abb63f0b219372ad59cc54dfd921b29bd702c6ce50b316c362a2690427267428e988a5fbdfe4d318fcef35d90c44799c621b39dfcf888d2aec3e49a42c203ca4d831a478cf4e9782e4da5607da92c5c2901097a4c80145573180ee7f849082a2c8c3eaafc6583f50a0da3193f21e5f675fd70624d8d1ee415919ed84bc36b1e024baf800c9a94782c08d4eb3285415b2cc800513434162d8db6c7549cbbc8121dbe2e888f50d9896a471333df4c981c849b12c209784f5e078d795f8aace9e2ded0846bb5b3170c608d01d0650077134252a303b050a52c4a0d1a462682431eaef5594ad0f6a7e8f37d7efb8154cb24740b7e280487d0b7302e097b69f403b4fd17cb4371c2a404c688bda509476d8c1d12a0520b8d01e19209c6cc1caf08d258a9b97bcc4fec39c2622f7e03a34f3f457729dfd95bd6dee0c39efb99ce2c6b4ba7a0d4f30ef5756447db9d6efb5f8d8aac8ab2530de595946fba57f7202bb8352ba795ec260bc619714a53e9bfa0c7010643be03c5f5d7a569183522ca52ca1e323fb49f9ea7c8fffd286077a47424462382c422290bd7d27ce03cc389d527bc86aeb670af3a63c958225eb1165525f5465ee00d4c8ca68b747309b4ea0b977a29aa216a52287b917440163e7dbcb6c71073f11c53d90f30a00c83a64c5cb7c2454d283b9848823afd8b7aa8c35db1c871771d4c3cb7257f91a93798a494dd80ea333f676a5be1f44e38ba7c8a59fdc4eb99ac8c0e5c5ba1a8ae3c76edc640abea8928d7023788aa3a28db7f592cc4490a006c273347fe13ceea6faf10120c89825be7e23600f078d87760b4c051625324824954003413290283fc1c10f8505562db0d3adebf575c3ad8fbb8a15e2e614cdcab5de03462c3b77ef8299cc9d7f018b281b6b01d979a342326893045596b4253c70e7026e9cd8a0d0c6c354cc0903f2ee7ce0c653a38fcd9536752e32778618494ff9a89e5eba7cf1606fa68ac6d00bd691bbf5cd1c7fe9d63d326f1eaa0c5aef60ba51b0a1ea11627d46fbaf67c2f536e9f2a95aa90616e6ea453f704bd3350cb58baedc4e8524df28c24484af65a8525f753e3d268fefbf0dc30c0a989babfd346345e2360d5a40e1ec4063d6fe59459c48cf5f693ca0a219881e6cf9d82220619a2a70565ba0bdbcf36784bf3007eaedcca8004c1426501f332f8e500b8498d1829d28b31fbb500e3298149ba7d189129943399253d4d73c16989f97be05d3954789bd824b3095932d0d372d13b80c7bae077963ae3f410f04a26c200c7c2828b53d3b949e8e012b1f5c02f062aca83c02f39054005cf419901fca667a738320d8de5b40df0b256a94cafa4c8e70742c7691897403e8ee14ebeaf0d6e8ae3d85227c5f83bfeccf3bda76a08462351c9387acb726d352808ac65b13d8adc8ca2c8e122d20a893227e30570911a98c4e454fe6133faa5c2c631ce7e4bc64177b99721c445f972eaaf31a6594b1c22474cd8a5da649604b149352961396106f9c5ccc5a7d48c54698171030726909af58eb2078f42713143e3e212e80132e95810f54cb82f458ce61ef09fd7b2130a8978dac6ad62ca021d6723799d4154aab7105c3fbcc6977ab3d60e11910285be865dc4da8a52232aa5012b42b032f460fe35183d17ca6236521af3365229f1c0b1e8193841cfe2402848599163782352bcbc82da809b3d9dcbb8073c400c104d69c3500d9997ef8e0d6197a343b5b50851ab67bd0a644dd413698eb8624aa82a41445929488f0d59132dafa0d2d6d737fdfd48bcea934892e770f781087a8f6e521fa320093a429220801fc5559885bc0644cdd1ccda66c377ca3b054e2236d021ac6d32c06bf08b067ff89b8d880fbce7430137a3cfcb5074e7b537e0789c4203282b5b91246d6c6d8195ba8bcef25cafdab54c432bf7b1c4c62bab1403267797662d98a86f6d7f765c902a498dae8e3582391f9990130549ca4c800259d38b78214a5d1996d21a61b8b5ef45887b43ed39064dcf04944b1974aa356aea28403afee8affa5dc66966d8388c153ee972c9cd6a1e9b0b0f167d02e1ee48c445cdacd50148b38a6226a52493d30d77279aca904628396b70e04a5e0c8d8dcf30bea32e49f8926fc183c6b47d2d2c23188f1eb094df3f4ea38a63da04d8afdc6415a34647084d3676e13c06c9b863467d913d1245315ab326dad6a0efe2432bc44da5366faf10dd1f2deb025227b4a9aba3b88b7a2f4b99b9272e11ab312fdaa16025e1f5419d1f9adf9d0c18362a9e38531d2e83aff4bf9d3a29d5575e14cfe1fe07cae722606ef543bfab562648e52b6f3c577380f3627500ac9ee36568994513cf2ec37c6283964928be9a94ba1e08666d710fd374f66e8202864f42493bff8962978b44bda146b83fd9351447b69f311345dec285ceed5f37f1af1ea4956781a39992c151a02ad8924da32b8cb46390cbd0230801bface01583507f2c59a5ddb801a267187e62809ed387a0af9b193830c5283422c0209287ed46e2dacb6d13fd30f9720a5126c37c316a58bef9d0ddd9c04257d0d369f2180de9c81eb14814406dce6d660b1b413be90f1c11a90e88f54a2832a5b108850cf14bf8aaf24bd38fbe852692e50d26274f6d445d8a4e194e724d087100fe104ff02aa0886c88a200e89ed80966bc0f2183d6f36584a059ab714a76b723180d603df27569bed2fd90de7af205ad9dcdcbb9e7cc688329fc60163fe9b78b4a61a680d7b2a992072c0b28655fd8681ff6692ea8d5ab8009faec32da426160bb3952616ca181cc1d810b5693ff70a33a2d876faad90a06c93239e2cdfac39a18772e8d91a9daa8f884671da474a7fffe999ab6e00c5c1af7e3bb9d998574c5d623856e77babc47bdfad6013e9d28fc0ec91b07a6b65f6c4f76145060f36de70c0d7775a4211c3a4b59b94e8a6a13d0111d508e84b8d3dc0ca43fdc626cee847162e45df392dfc107b2d1cb1e3016b92c1422dd32726e4fea3866c224102e9a4a161a871350f14c9898a4656b457dbc3f2a39958510f00dacd144d2e918d9aa6adfe12bdc928c2568c6b0029aa92014b9a2637bc56a7a8fb33485181459d3c3ec21a599da9e5efd365113bae657e5ba4778aefa2afe9e3b301e6b3c8fd5013a632bfb6f13083d73afe9ca221ae0f8aecf7db464055352b4b21c8486af322956c047e87a5b3b11b23157e830c6554634d8501a4e2357adbfb0180cd9ffd49ffe5eedd9e8d743b4ac7c13bf0fdde51e0970e0265884529065a005010eea4271224d9dc319e6b84bf388addcff9a04d00c20298304e564cd14f28e2495920673d42583ecb13b1c388883166093023b3bb99eee48f62c8c92a8653d3726e062db453fad503d3733fea2f8b84301445923152afb5f242417fc24b9cb736c875bb9081d3a9d8d769a776ea4ef1a8a988dc327d673894ca836ca9e9661e04987b1bc8f626ba11a35bbcadcf357e1ca10240aa263740083e73126d16c249f52833cd631d7ada8e3a774367694c74c02406a8a075469dd76d5e72b5a0929f19ff7ec61fd851097221ca565d151fcf3bd47fe34c852890a88feb37835a84bb83d604301b4008970c523484193bfa7fc7c05af756698f25d8e6e1ce58c30609950bfca0809202b9a504389dd25b01fa964c467a7a45d2884258e33e394c0f4f141e35d61072e5751b4c51f271660a4ddbdc053204150a75c2f38d09d9aa25682a3b92284c11aadddc80af77c819c190f2be0cc72279f0c3fd12d5866a917c993348528d3e484400719f6b4c456d09ff8a2518e0c11250ae7b74831002b7f48d161ec8024f0eceed0e7013e15387d5eb67fcebf68523626996bb67964bf94d6a6b365bb8d07f3c4b8889291696523710b5e9467f4d936452cb3060b4626b0e5360a84055c6d7c12b74e27f31ea4174dc3a685720d0bd0d6b3df27c323cf5f407936e19a331393c808b45d9f05282e61a268cf49a8909619062980b2f431f383e511a78a4328e493443a4e1d0145715a00552a262374069b5c01a99ece20e05b05536965794a27d665e427512ad98d44dd731af796c22769e622bc44f580619b38ac090826a6521de339e5c5441594e0e7849b68b49429dff7d2f8c995da544cdce15fee56ddef83068c4cce1c7f2bbdba3b132748c5dc728fb88367ba3c48da3323c1e526cc627e9adebb0fff2e47670467119281b56ebf5fc6b369dc4c99d582e68f2c6611e3dc814c4c3f04b4aee03e48b5724955380652445e997a056414442cb2ca4ae14502daf36cb61acfe1d47597ce5b11227818cd2a11bd293e7f7b12dbda506e7c05f27102240522119c527967df8af65e4103647cf9704935f1b920ffa034ae91bf1b9f26a26dea3178809c382a5b2b532d34894eee37ceecef332116fc1062da5a555fa431031820a30926781406559a12ecffedcc2caa35525308ccb94cd11560c29fb1f72b3c00ba9943949a9725514bd360567162c60010ff061a5cf9d8e9a251840405364e0de6498dc30f6e85d000cc2ae10de02fb4309caa78379dc481a76392eefdb977c408c83be076c2feebc243d3549d3e105220c37422971c6223e93b062d8ccdafc390e01bf87426f3df26fc885b01a56806dfdfc76a57dbe818c2bd337c706fb7229b11d78b6b1e6d51f873d00468a2cf328d25454dc1855033b84d49cad59cfaee83fd507d97754f5e406406fae5483c509aead95632fca230ba8e8fc65884cbbaa3b226760372c782844c6454fbcf9b31fb04c214ca96581d89361249a1836faffebf0fb092609c0a0918c1d8ec7f5f264cfc692e3d38601a661c9d2c15ff49960c693b1f79eeeab2c5e49e721eb31fe6d1b3ee5323cd4a961e8be2f13d07b8fa6b62861bc36045cd405061999359516cdee1f37e008886f91d003dd6564342408b89203d3067d513feaccac14f808787f81b920f430274dd84f9c8c8f617d3d72891bb2ebd2b4b4785a4fa918484ba6508bcba7b14925304b8a41785556c03b0dcbbf73e083935ccc9cbe67f7553e467103233731d0ce04e15af7437c4993e6471835773ff3739135808245c761ee00bce0210b121158385c48e660811aaeb5298de41cbdf83273772e11950424649cac99821ec1db144aa8b7855a570bb7c66514f8f47689d66ae45780bf891975831a8a9b927ddec31e59ba5781ff95ce3f5a751a7fb458b0f5867c64b88f5a5ef5643eec473dc12c42c52f361c413ed610c6186280110ab756092de551697b94222c95ca4d7c8d7026a8a694f724c6352d57142276dc845cdd091aea4cf74d01b4719a2ab249f4060f9b131448feac08d5de3d08f776838580d9b72245f33e0186d011381184b50fd4f8ba3d41db5a99361d22da5d47d790c480bf0af0cd6d9f4bf22039a619764012128dae543916fcb5573b60300dd4ec59d1d25b96dfab0b01140af3a2debe98afebf82bb56c739f5d4ac51845946d578ee28276cd6848831cbf2ef30b1ae14149697f318366a1cf65b08fe51558b71308623cd531fc3c855c71fa04a5cfd01eeea4dfc0ee9526e5a916e238d2107ceabcd8b6a6e435a0a37adac802caae2a76b40545cb135e7e039ca92ef0c874a8c505b25e2d1cd8c132433a27e810f002d9424036318fde1177345cb643290b157eb948bb93e21e8970475551cb9c6f5afcd2a892472bc5a2e2b0d544a011ad0ec295580d4d9340631841485da51184ed31f8c63938c71702ed1bd31219a1bbb9e077e4185fdfbb6c37ad2836524bc0aa5da2ee7749fc330dde7304cf7062fcca999e2864a66e12d1a5fbf3723a9096db2e54e29ed6dbb0396026d028c021fffffff7f1d2730f6e93c8d3aba974e950b4cd2c82aa10c72d3ce9a504985d5b381949e240d7c397e84c8e2240992a2a12a5359685ee664ddccd051c21225840d71875bd5a6a26021f4d0592285051e4f9e50a10243a9006d6062022227454d7e1891e38938f5b384266328468691cb2f66ca4d68db4d3d32fb0810b310889d49816ddb4d7ac0d807244444fba049cd533d288e735b9e52860951931facb2c989b06412cf8e560b466c5b1a3d30f6e9d08c3a362f9d1c1798f6ffdf1291d584076d6b6c3b061018fb64624cdbe317b302afe34f923fa963e5129cfd7feb4459f34a42f4892d72ce92a3d05ddbb66dd7e811773ed850c2ffffffffe912866d4b439a34a549bbb91466c2c78e0d1cf8ff73468dc921bd6e3b49923f0895ac27061035f53b02c0a31a3a5e3e164ab5f1c8482b213042f4e20408848791161708b4eddfb66ddbba6d2faec212d964ffe7f49c22404de59480e40710241e5c4af6d1c68db16d574cd5562d492aadbe5a0c1a1bf6cf39e7cc1318fb74552d667a34362d5ce43805397dbc94e2083543cc906263679343e3c1459dec3ee69c87b5ed5f761c7188706838351c269c262525c6711c479678438624faffffbfb28d9fa8c09c3722bb18c57aa9c288d6fa003f663f7a202862a347287324c7840792840072947f0980cc10de2c91a39f21b270e65649b224c922b0155a1d2b243017c6beb10487829831be00c09c38724e1b1049b4e612ae254b2a4e82570357ab8b20a440149698737a8e73ce71ce39a17060eccbedbc028a37537333a3cd552d567203700a9027aa1f7ade5b002a79614766db5631e566ce20981c6152828548d40b0f2c46c92c4edd30b6ad0e48d6be2ddbf2762f95c17273e201fc24f9ff2eb02b03d1e0036ca662d6956d9b75a52d8e7a6cdb0540cd3567026d4b46093b087a318494941f5148c42296e60fb0396d4200e048f1301d2d8aff9f45098c7d37a70fb23cf5ff4f84841fd95f1fb5bf094511a10b49ce5ce07092d1a38690290f6e45f69ad0c4e5c598140453dbb66d68db56cad9a5efcfd8a53873b59db375d9b66d5f08dc99f560feffc64d18fbc69224ffcf169ad30b85260ac6db6e5b1572c0d8c7f31334b442052353f089adc9bfbc0a82ff4f0357191ab9974d0092fc92ff3f49b3ba6bdbb66d1e3710594a810610db8d86a0206d5bbb846168a4078c7d4093851121975f99816166407f6830e118db76b62dacf589ac398fa2c257892116606e0208e0bb917d4932985ea0f810351b02440a3e62d58ed1ced18b6bdbb66ddb361d3f32fbd53253b6e6dbd9b69d9524c953825f924d524e10193ca072f038923144b5c464498a00a00e022693df054b8a8ea87c312f10d93091a7dcf1d5d8776ddbb65d043525c8920e9e928e1f47dbe681098c7d4db8dc6e050fbc99153dd4fcffffffc581e19cad6823432b8c187de22c0093a4d8f232e73fc20e524940d924e98031850825f28739ff396790b7c8f8e474909d849124e8dab66ddbb60a5d8d4776b710b27319d09c6daf1ac2d4a2185ee181b16fa6e6c6306d5bb17479069136a7e738e71ce79c73071218fb946e4e4fbaaa163b7aae972433a5eddeb66ddb86d30d779075690ad61a0204aa8832bb2bfc6ddbff415c4e0bb0d7b6a19cc0d8f7a4ab2a0ba81e8d4d59e472da55fbff770501ee08914a0420a984f3818e6effdfca1018fb8c909462acdc7888fc93a4ad851bb0634223714fcaff490f6469a58a6c312427b6adce4d18fb461424e9030819e268a38d04655cf4d8f6417f4948933435d57f8984e506140c5ab2d8fcc020730cc53143d494702ca0d6238622fa54ebe803d46fdbae2b0aee51a0fedf36dc3361db1d59f189c234b482c9a835d9b665b90279807ee62cd2b6513d1d8dce4697334e2d85ffff181cd4f258d0a4c3911d8b470d3b39beb7486004871828b88004d14ebcd25486668d33c5f87e7e45b32b0b1ade4ccdcdac2c4ae849f189759235890a1631c499e1f4d85a4a04204ba404a64ffc5190e411e3092113263e21d99764741322616fdbb66ddb0e591a92b5b8d992e45b6301a8c50148c5c7050770b88461dbfeece60f6fe29871695798734e202730f63de9aa2c03d4a3b1b18c0c3976052e706c2df44861851d11091968a20c48aea404be21478848cd2ce158e2730496a9b1425673aee69c9f493ab7deffffffff6f636224fbffff24f9ff2f243957bdffffffffbf6bcef29f588a73d66ddbb6edac6dbbeaf8975580aa085521aa42ab52abc254a5e9297a0ea0904041d93949da89bca432c9ffbd44b14c0500012ac12157762fd8cab68d2234f9dfa6315334d155ba34a265dcf8e3c8db03edffd7f8a9115463a886510d243fd0b635f8dbb6ffdf8596234263931cac2449dcad24c9974140de8d141668e0733ef54acc990312308a9454b1e5808411bacfa09a2dbb030e005b76b2af3634d3b2234d921f5b80b022ebd8b62407bba3a3aa14ff9c73063102631f92d2cde94945105db884841e306631a5c975d7d3b66ddf82c0d8771b1a6f46af1b920b0c0a17d088670707a10d1a3c25429b9ccdce86076346941a3f731d7770c145923b14ad4892e429592e1484258a7ec0d8175434f472c1b56d7b6ac0d87733eb69d10314a20043d5046ddb9b323734afb60da35cdbb62390d9b6ed1f44a11db0d3784a5653dbe63081b1af49260737e545440a2706309f06e0ece9e54928d5538eba26b049ed77fd7f05e0cd06113197305ce52881b1ef96e3f472e98b1a576024a9638922ef544d5438aa1cd58e8ae730c9a766851a62186ba821063bac6d674c61ec1b89c81d2900c8ef1387f82a660388d2ce4e91214f5c1c29e2c50b0b14167233cca023a486ce0c098024e980e9e398c8b5feffff8fe281366c1a02639f1192524cd3ad08eb33744d615b2f10fcffa25a551431fd3f19b2441d9d4e9548bab66ddbf68d9ecc86ddb56ddb360f986bdbb66dcb2cd99f6ddf76db429901635fcdcdac870514a011f6ff6f756809a2c93e0338543aa89c14f5f0f1216699c124b34892f3258d1c38e4b86b3d1b8dcdc69613da760e3d7e89184f317431aa462bbb296385375333658ab89973bc1dd278a0b5ed5c0f8c7d34363953460acf9ca193093ec80cc6b8c03445d1b673c0d8c7f313340fe0c1860913f480038ff84373b5c2dacc58b75528f46aa9296144d1b6734e60ec7bd24d15b92a98cf39e7931118fb90946e4e4f2c9e74ac5795587efd3f0ca4c2502a8c5b3b47f12c92d4fd460c20e384937295361a84cab67db5bce815d95e30a248149a247cfaff2772834ef63724c91209a7f0849a9394c8bb3dc40e0a47acab6d8be0c2d8373ae151966ecd29ce29cec9530265006507054932f158e42f86a1d015182ae47a3de078580ef244d615db16a23485dce6b4cb2892acd3d36d056bdb95eb04714e1d47acc4d7ac019525962d2db64d14c6beb1fc77843cdb9e210c1e90a4a4989d9e9f737ece399ff4c0d8476393c3f3b34288207be5c36584f682b5489224e727ceb99a73ce79d891b64dea05c95a23498cd0e9b09225484feceda4927654ad1639864428c510711b894e90a493b29544fc7c6a7c6e5eff719420c9c35e947cdc1091a3324866368c659122a8b4cddea6a6000c00131a680400034118c9e238b70e0614000613ca94e4b83c301a8983c2703824048542813020000003434100100c0e83404090259b261f85118b88c4dc9b1a38f506443975d953c75687b874e860078b11c0d4c3e627e8bf1e101b2d236374e9fde983dafe2199fe8a352b09ca34fcbd5d9d96abd4b6bda5d8c178d9e19d900d408f01e7a67edc7dadc12145933f8507ff8a2bc4a4b9b52bda2a800a5d52d2716c8b27ecf9e5d010e22ac031991d8d11a56e6554ff0dad9f400145ae9302b7bc45651dc2722cdbd4904031e2b179e3709ab9bea2d144555b2567920088aec13c1b885e29529704b049e3c3e07e89e12c9990448e017f2fb24d0a82a1efd0c45731ef4d50d0e9a665ac83f7d03a0be9ccc4c561a9012786b10742d046d5df4f456261054ce709f9169badc6678266beb3018e513fc5837ca856ed51f64f7607c3edd1c989a76dfddbc145d381685d5ed13767623ea711c191391d14e6c945a7627e0a366d0b425289b9027c11e05605b8effe8a147e15b9f11771e8d093c8dd19eeab05c05db5825876d53b10eb8ead725c82953ae7737224d9e1b8a6757ed2f9aa93d053bf92b721489e5676f9ee3ccaae5d761d4074c3a8b584d41932919bd0c15f980104bddb026ade1474c4ca89274282a1edb98308aec3c34b41b30e587f080965d029fd1f26dd1822197d621a2ac331e1c682a16786a3d580e268f62921dc5a5fd1d154ddb4547ee631b5d8d0b3809e85c5255a80f521136ccc33490d2981b9f3662da62e9aeecac08516e0202c366a1666dd56fb995a24b397458209085220df7e5d766e67ef27775a0ee229be9f07496006a147e740ff1bd0eb1a65e5fd7753a45a996922f2215ed7d7f0fdbd07e250b493cca41b8e77fb3eec263b4f95d4eddd90e52acef76c9e3e983e6d2d3ed7f7131ddadf04f49830d52b3f9c9aae21b258ca0dd44bc5d44905d482bbe183b3a5c5d8ada047eae23df0504c5604ad6a674fbf1d714a102da93a1946b22d86b0dbdcbfec987dfa3f0bdd041a938e5a104cba23c79541d48e583665876d4fe511fbca508cd729ec59abffe15c1d4e56564af3afcc5462f9689b625df524c11ca56313f994bdd9f1a02f7ec7641f2a801bd3b424cfe9c4e4780f1079ecf917e8c59ee81769ee4b5211ecfd0e5083de72e78e9527232398c64fba64b021a9095f0903a459dc7fb2a788dd73523d710c134f1405768bc606a2b4124dcb08c59d9490296fc4cce892eb6f483e402345f37712801abee46b75bc837453b939a2e2b8fabc210d0274bac1890629938473ba01a74560d9316e1e5bf79a320db6927168704edf0fa651e433f98951998c333553129a6081c51bae6a8c30ca035c3929197186ae3a0bf81bb68712faa886ec5e221f4acbd71cc0acad5beb436fcd79a5eaf1afb0565c74d15103890bcf1280dc20e885690046abc0965f417d41518347ff31ee8490b785fc410e14441a875ba2aa65d4c4f51cc3a960ae0d82ba1d3266d916eb82098769a92a1a4092cc4eaf1ff3ed924288542b6b9a083e449595d09c69b3f1c8dd457a9a8cad2f25dadcb0d938c8e7a342ede5cf248d0e32c6d0fbfe5b5fe40ad515a0271b78d205318ed9d4632ffda47c3f8112cabb62624dbc4dc06d35e1f120701d17a21fb65d8fc461c24b292876176602e0692894f6d656c982ca41d77dbf346204945502860daaf69dbd33120a3d0bface3c243f01e67a6621c49881888dd17a13ac9916e3ab026c5dcf515d7df47569fbc8c51c01c2c7b4ff98ec772ba6d80b291a87f6f3289e49ef05559ef24a4af4648c3535d4c0d19a2fb7e045b758a7491183ea9ae78179fa3e7ab582141f5165150832a00b79b47741b751cbb732afa0703ff8c2141a214dd9e474caa86094df0b24468c52f8f5962f69400ba6850ce775635f527c50724d0a12f8f04a76d8330c10754a7160391b0603985cd185879bd966c23de9ff2b084ea91af00601f4f7cfa39b3c01635ca27352b6249e3f3ae0918e71ac5a351f67e9154a352ad4a799ef8cc1cc09808f936fec571f60a85184221982905a205a3582a0958410fdd72995bb7df1c5b1ccbd24efd1d243fb0079626430173d320eacfc39bc0c1d473823a7e71e5f5d60e8ba02d2185e042e6e65e1b545e63cd0f849a0ae1280c0e6c1552ce057dc79184e7cfcd16c7ce69abc9ad4051d17b064158d2a5b6aedcf6d9e273b807df1e79164fa53cfa68a6f72c8871907487146d4f8ef84cb2f799ae4f3226cdf5eae8670007273c44a82648875f7668c5f51efeb746cfe634db28a1c23b1cf2551f17e91f698fe0c95fa4d20078d350ab12b913440d2a16e2704614b0d5c347461a852798da1b4217a6cd48e910e5ecbaf289dcf553d42b7bea298f743b55ba40c28e8f54c206e60584a7180e6556d1c57568824911ed7d35de7275e0ac7ed7c09593ab7f9deb352aae248c2f3b86d5d835927277d4018ea114abf468d94ce78c5a52077ad15b541551867d0acdfb226c997364d2b0b6e75f0381537d728c5029c0b0dca69aa06a7d751d329d08e3e1774c91aea1f1ddd6506a3e579334a31dbddc73d592324f892cc327d6b10dde6a40082beee347a608555253b4f2cdfc25549a54f42084597f826e98792058b51f03d129a5449d4b75c00e4fa3fcf401a2f125efa218ad7c65dad23bf809a5727084f87f3af282e1be4733218a5b1c97851db378c162dfe7e40b5d0b1f38838cb3acb5a0248ee412b812002367e449b905c8e11e4a8a1765233c548d2d4a29fb0736b62d5a4384b0e62ed20832bd5c9aecdb30c173f413af92e90852d664d885f471a7c53488a608d67d403dc4f91740333af7cca2e005398256d0dd091bc88b26104c556911db5aa71aa0d42010908c5b0caa613d0533e2f61d2523c3c09dd14dcc31d3f24abb4cf2b1e5bee535026622a4f56a3b8559cdc210f159e707274182cd2be40955ebe02ad7960a3f505801273799929666015989290f041bda2305fdf33eaf96a029b1f2a595479d15966c434603c76e72dcc33f1a9a168121211f18fc25e0c5b18a4e47bdf95f345516b19274440d52fbf004772c73532ef6201e6e25b69508d918e9227c201ca5d327fe1a75bbfd0006be4f54892a2689cf79253bf2bd1ff8bd83e548dc6a77e313b59b9324a023364625a7e8dbe5759549b06010e06cebe15c3a1c569e2959a031fc4e6fa76c9c8fee9930bd00b23a782022c6eb0e4ea230ce2447a399fe840e2b996fee4cad6ca1cd8e398beb8b1bedc9e314e18eb1a30f9de91208665041ef9d31d658a0bc066c2ae1255f0e7d5f09fd66c8838b1fc5a31e68b7989626f61b570610bb2e6922b74e06bba52499696f4c8bd0dc96b556f3da080dfe2d2cf4741443abf523c566f8be33981b56038ac98c2a18a14f422a2f204bf82481597246db7f3de0d00912717e888c6ab6c087a7caa96ddb8e0d18d05955fd261f4e3f2687ccdae60a98c7458b6699f6458fb5522d0a672907d17af718320fa944d510800ce6a9beee1f59fc1ef59bb4caa9d187ac6c199c14369ac2654fc5bc6685f7d96f11d52141f2065d2a52b53faf3f85c210888e33fd1af52ce22ad42561eafc8fe58de2c4772af1e573f73ae6800d374347adb7baa450f070744754b3dcfdc26cee505a015e17b313171e6e168146066d409e9e53322f2aa1d849cd9566f0bf3165376b010d4c69d15929041271e115afc8be1ba69628a0898f88e043971d15331477927dada39c3ae8e26bfa8d9ae888617407fb8e6e657bccc0d0c07bfc975f15b27a889315de18f06f035f13e5c9f61d39d8a2c5ca49a4d52b49cfd2b5d17c9265580627fc05e61b3b05cf7d762e78e2dcab9e8f107d02fd78876d4328507a44f3396155ca40a4a25ceb27e31f21e4843688b58c603d65b2133005405d63819ff231ca496ba1c4946be17f8145c6c84a727d1cf62869be8856a0891382f61b5d52fabe45f5a882e49ee7cd24aa4e96c0996f06f196be263504547719d3fbb1a13291968a14eeab24ab36ed8786189afb6a80027af2e0390735bf1ee7a1795f1013ff0b4d14944d87775623c88cd63d5078c09244010d7f17ce8ec5b2e5f668ee96bd40ba5c90537812c90d9a88731dde94e30e99e58013b80b18bb718111a7b6c21b4a1dd2765e4ec7920f8be11272eca8689619779c2a6d114344ed97a17de02ea9dc00ff74ea4b7335527bd9001c6812e67981747629cb81ac68364703ab211829575986ca002c9ad669f8572cccc1d417da610582ec638df25454215fc4dde0aff56dcd5663b09fa06af6c959e90d0b4bbb1ab5d30cc45d94a0c2ebb9fc440d18034bcc3c66fff0c7fadc3011ad180f7c051a366019787959bc5c2746828cc2b3ba5d619881438f696dfed0258cea256b21c739c2df8412655b00cccf35b4a84dc8f776dd9b8a2fdd31f98db85f6ad4c644b914d2ec0ad20da14c9a6055b7e8ef803a2e3dd8b53646a579b63c45e18e01e99a5fb883ded3939ca983061ad6c1475bac6b6150773a5a80d78803d767a3275ed0aa814448e8f73cbda8896e50b82d86cf000fe5708d008eac932d9448688216607b6092b798db7b66289022edd65d3a395ec7c3083099d58f3fa3f88b8ca083039487e5c9ba5e4488a06cd09a829de5dc09838b57f7a40e384c0fbfd082d871cfda1524bf5f771e8e3e12ab181e16c34a46404a816626196370b00d8b0586fc8fe6140bf664008f36305e5241a77540d5ffa554b54d8d76f3d3a9ff16300fdc29222c1c43f5a606f34099316d0d29e4fe34bd27057e53101df0e21b36b2ca8ecc244ab616fa843b0c3831b83c0ff998d16579e4cee9f4aa414708b228d3efcd85617976847ba69c4aeff04e80cb0a427bf65c583c6740ad1737242172dcb4b7fe574ab4a2895a9919f37512f1ca4cb9984d4b3b582097b83aae6f17d0483831294d819f3e5b4c44594174cd049e85f908827afdb3955922173e1e8dc9ce95ccf1547901025a2a49b60a1949d6b9fe5495c6d90c32a280074dc2150a7893811f45bd2093215c84c17b1695760d9c55bf356a4a5a78ff771f39e704ff83473ea01a050365813e2b7e31780594959c203639b101eac90ba806ea18e5d05529572fd97b108bc5392491b0d50e08241eb8a7d749421555631f83730060ba58c4721a82429f73b95578db0725901d554bd299ac7e123e3f91860eef3a1b016b380a9251f197b75fa88870dd7843e58b2f1fd7400ed86246d2409098133902ac1de8f0934c26d33b81c9cec6490581082c2b4bb54fe1d4661d759105a2a4fea0a9c7685ef9f5b0d885c5c86651d348b53be3e64b83288d9d51e69b8bf1cefe219ea0b27d24dd46d1f385eaf1d0b15739742fd913d4de2aeee7a863a7baaad1d046ea0934d7c249b4a5d6f98eae206cf8c8cdf65d4624e8276d4667ebce5b4a817111f9535dd19b526cad0028a13637e412e7d977be3c841295103c0ada4f264fac6929e5cfa8d32a8d14dcf43436dea37a861d720b0e45533655a772533d7b849e85c37e0a50170efc64221dfd2861c8339216c2a3c01a0de31ab304565db5e5423642c6a46389d6694ee0b584d561650dbef022543359985950ecfc6d88b68ae5d76002cbbd9d47172837025a514cd6fa7f4e49c4c38fa043d689d0e56e4e899d9f058da29ad12aaa5e89aa7d9f49b0fedb5545fb59dba6041e5dd141eca7b9b8ab5880574dba1d086f4a0aa646709da2abca320d1349e8c8afe2027531f60b0b9d3d21199f306db8141823956d6ca2ef1e148ce414e1eaee19f7f3eee6080a41068f1c0f511be81408500d9846b6f8225a80a43d1308159a3c0df63793dc636afcf458b97f380b8b2b0cf38361d09880987c9c4716e4bee138279c9b23aead9eb6807f4c4b921485161c8498444bab4b07ef126315629a88f8c8f8cab8f2c89552905062bee151887d121dde85aefa529accc530fe8b838fee766530b12af8413c6f447e044b56476bbaac93b8c64dc7cdf63f6bbd22ae8b961f6a8c93e210dc8a145278438fc8f20c1a5c693d50080eef8293db2708558e5128f7ab09b4e2cccd3376616d73360af7a6495fcab9747886fd9019617cbd41c762e4c4e36a227290b2d263dcbfb2fb90ed7fe9b0385a020bcdeaaa6a34200a193704135aa4222c06011346a960ab303d0ea3952888ba904d4515c1f9ac7eabd2c66a8d2fb5444aea4b325d5c9394b40cde9add7379c435f839a4ce45e45356304baf107929f485d33cb08ac2fbc38ebdab089e292778da7b6d190120f441f50265642dae5f0cf179cbcf40901e7c151acf74be6a5cb64c531af18b6e99271b81ba290b6c809d9826140b75009aff034a4ff3c4e7364a13f869facbf372189d9c24893b6890668b93084669ef85443f40907c5ea0753cd888ae1bba7680fa73e9f3165db565e3b96a60557e71d2ebb2c838201cca958c12ba87e44246d325bca9a694b56e919100e7274039369865534c6f295ca84370f8c4ccc456c70573e0a06d415a6c7d384e8e46baef1df782aa780b8013e94e7e460a1bd0eae65bc41412564fb793108747a0c56b98077bcfc347844cd2c72d5b0aab5e99a4a367f99d61624367db91a076a3a9aa0cb5ab2d9e6bb243a742e9b1f29f7cd0745a85fcd4c3676c60280a30a9d524ba1d2699d5ad9702b3b5ec99495893b6e2c210f70836c3520aab0d8548353e1bb26cbfa16c320775912d0c1e75f05382508493390ea15eec1f8bb2100a94e3476ad3e11033d04b6502e01a6a0690939398e2cb005db3785c87854a24c89971714e034b38d7f7f35f1c92b374c3b3a6d59ac96c49709ffa956c377c11d5d7528e28c704d7b751809912b1915ec4c5be9494ddbe64338e750cb93de88a9da7ca0b7c699c0ad004d1b91794c74ac19dfcda5900a20bb54531a9086d844c23e0d819c115ef5f13c1a26532a38661ed45e0abfb838005d54582601b601f4a12f48ad9380832ad7d219244f9f0b979503d7010c5530c9678dd63c5f733094f80976f9f0d685e304777e41698f7487c4bd2ea674554d7de49845d77d954abb4255cbcc13932f817ab257beac802ff7f3e1f1a438a268598c08bcc200eac912c4f5100ed4ca3da1c9d91dfc7431709a6f81940f193400b622cb8fae2739f80906c4ac4124531ce2c9b8548072f3b78b8d68c1b194416e26bb8de67e3997056fac1050ec28a46c9f917ba7de2bceeb70959ba38c6f6fd7d44c90498462d63017c68b26085d8ffc1bb0c9cf03f725d7e375760cc23d6bc9e3120ca58c297b0b606d43de78a5078729d62e8a84b6cdb03094961ef078b98ad187b810c509faab8a70be177acd69b0114c67433b907e390007157c5b2d844ea7658a2055cabe801112f941ebcccec71ea3db81cfc27201ef531bc62b2a8f2c4b5492bc36b5bf6dd95d940168cfa6cbfc36e491341a374f126c4a6c35d33f4aa5ea18a7b5a19e476eeeed9d9c300dea2933ab08fee46242ad231dd8480febeb9563fd64fd5fc10e2dd0033202382e804e43fa8a3547d6420904143597049f83cffc33f0a40c3a549980cf140d1d3092cac94e10932741e5922689eb0a02dfee6eb05c938d7403c69eaad00ff55efa566722debc14379ec1e08f3710f562cd19525b4f929486cde2af7be74c194e39b297ce2389f6f6eb5b3c85d58f9e11555e8e4989024d130cf64a7dd80e7fd3240e20854e3a2d16daa20362a9a43e3d1106e79aba601245144fef203df0b9fa698d5f5cc107220830e2e5303d020904a59c59f2396c146e13c251927d05370adb1da3dfc932aaf13aed68a66746cab0b7d7aa20de01807667eb3d6f05fd96394fc6194232227822122e597b32197d129f96cce055703618335b22a0b102b49727a018fe686ba980c18ccff3f3eb477a910050ec1d72188c03ad777778b47417885419a903c08b569c63b778241a4f123f68e34eb815f9a0dcc9e5877106397830588d6e89aa96698af80a43d6b65ccd27a740c580150f25926575e8b8ca906dc171ee150b6c44ffca541ba04ef1aeebc7a9bd9c4251013343df906d85006243029b0e96bd624c441669f9cc63614d1826a8506f7628d440a59a4197680ba1ba0be41d32af7401d04d17733931e13b1ba82e3d36329fab3daac8b21077dea4ed68511c3ce1b033a817a7754f970e94bd7f83a31dbc6c1ab4558779f91aaca36a8972604d261971f0200a40b719b58a3f08133a4d1f46597324e5592259137307407b5c8133b1c5763a53b98b5a0f216ae1d7185da5dedce036a05354a3436ea36cb4a92c995075b0923c098b202ffac23f75a39b854c7571ce828128b71f05eadca04071101c40cb371248bf2f86dec897ef6a72fe17d001a408b5070e7cad0d6e7b87b4a6c1f23192d25a603d4762a340cc6f8f9ef2fad5a6865c66ad69dd272cfa2e61bff43d3cf04b96648f0d6dbe4e327906fc8d2f7d38fd55cd81935384d2b8476bccd5f6661c4c75420cc331538f2ca6cb201ca29eb1435500975475c0b1bc1ae5ae7d8d783fdfb378fe0a6650efca09b4088160488573b6e1835d3c52bae84a517272877d167c5d5d045eee284663bc13fb2a69b6000f322b90550f64def3bf8d21159787c47d1c4098485d715984160a850532a7b0243be54cbe1ca898a0a52ccb0d6de2bae2cf9983092dee93e0e7ba9f2dbace6c3c1092b1986d0ee34c9b50e103161ef6f212328e0844a6c36e9ebb7cddabaaafa2412770a0e4b1fe3af65c631805a57950fc610f5834eff145ba32e1f588179cacda024168d42ccd01692d485022a70488e6671db3bec605af6e37872f6069359937eb427eaedb2644a578c83dcce150295bf5db8fef14e32da33ed6dbd5b715dfa315b1a78c42bd00f1b1d1f31c66cf033b361eef055be2ee27991262f8a1aa6223bc9e9fbc32d5b2637d45f313eca413f1e0d898a0d0e7c5b7f9c0c3fa3115f7778d3f67679ee3037074589b1b49219f40d2892592f29970f9cdc18e489908ace1ba64e93982320396008abff829a4de7c722795a1f896a64cd2c09c8969226ad7d043c13915b85bfa8de544268a5a00d81568acace382300fa5e1d0dd087802f4c3b702e949988cef86df2c8d5877dc74ce3128a9d4965143195400a03d5bbef3fcbbe75a869915fb2a687e1ebc2fc5cee12800ee3f76995981306f14952b429012675e773a36d154e6c73b03e128a8f47510f2b03bf52c872ea8e4050f9fc8f1a7ccbb629a94824c04783fe31d2dade5150b81cc4543bff4c725b3f1a3cb1d0d6f970b9e2901d82ca7f49668c63ee71a10e42a0671003e4d056444b076554db115cdff7f3c36aaa42c437f6675a25695ff33bbc816900103ce65de18a900b054113de134ef5225a4645be2ef44c005ee0caaae2e128204a0d761ee81228a972fcc713ad261e936594d06a0a32e18466c3b5f664b16ef13cd2225f68c6c405200ccb95f645f8b05dfc0470e9b99f26b8b5a3416a88c230d1470ebe41c757f2b7db1777b2e93fa8e71bb14b28b55e2aa8423e5269d780ae5303d1b4486d003c9afd254c812449a9a4e940f6fec40ed89c43c2701aee338b8408fe5c56b4585ab2ad8767ea184636c52eb6492c772fb593a06165c4b0eaacc18c48f6fdd23fa3e25f810607ce06f8aabccf41458a0bf27f44ce39a04fb0e58677bb3ce464d337c3367ed23aea1d5816f915a6ec037687010db548078b301030f9e1fbb6e2d4e7a11dcae8f12cbab6b1d2b3de7e1b9aa8a3c0b793468840b6c36e2576c3464f5a682e3841893b9855a5b32316ede0acec335805a54470fae065bd88f5005832ca9e962eb1196107c88532a1c40e98ace8239d5a823feaad46e09412e69353cf48d1f6a6b414f271573c6f41d0518b1a21fa3394ee80e539802499459f1922a4b5478b9ff47314a66889f81c2ad0847ee1b2d9d7e495724275569758376d5698f55635c16b991dbe83a557ccb5bd4a2010a19754270a121ebf5c75a2c763c71031d2f723192a89fa4945e653f1c9d266b419d5c0d57acbc87aa9e47772e966d9386fdfffca72c7e5a256664213e49ad4820fd66a0d0536283511a67265cd1a40b28944f48fccea43611299b029815f32a6175f601cc058633151c52d832167a20e9b5fdd6d519386dbe08266627037007d8a08623294f31b0cbe501ae282dcfd2db9b31f63828f664e94514fa345bfe8085cd6d73a47917c15e7014fb16c01ece549d98da21bf5b91014dcf7e3551cab25549d20779422c16da11ce641da186d8cfb4404683750f8ce97ff6c1dd88efd8cbdaa7d2f7454aae294ba2ab92e29621716dbd5573aed1d676fdcbc88760aee929b65e507ad64ad4ec156aac0954897168417f890a888b204b00baa2c862935ecb5a0a582e869dd828c0909bb6545bc045312cf209fa792c7d5e1feab835416b44b75c422ebf011e6ea049e3c41bfcf0fd5d95a886bc2138cd3fa5007407bd9217ea9a53871401011c6da126fcaccec262932e1100d4129ae46ef0292041513d711dc30ab5f206410975eb6849632527609ea63a8eac64560e50444ce795ef3bfa5d072c05827237c768c4d518eb07a3f9e9fb04ca2031c8d53d46f970218500e475dc68b911f44bc657aff064a708e58ba4268cef0ccd8f8b47c4addbd671cac95a64e39b814880ef9cf20eaa5074f704a758eb6472d5c730be57185ed470f1f3054ad8339592f7a519fc08bf0a61c70a126fa72770c0cccd4100f761d394620c8e8509d2743f51a79bcd52274ec4410c29e0945d4f4a8dfdd2b02b146b4025b0290e409cb535afb22dc51956726981dad7c609fe979636b9e38dbfe08c2f0a6212b3147022fb89795e88fe6a4cc5f89e9736ae09e9083d614e074c34359582fee1edb2f2325bb53027a5db007e2509126b428fc46146d63b42597f99292849b7e66957978d2eac507db310f97288350ec0ad639bb61a678600158807c6692b366986cce45a8986aac9a88a5a46dec6cf9a5c3f40a618b42883eca5958e534afbbf971bc7e281bc855c268227ffd810485603802456b95c91ba00505197a508b3b837a9b4db108b03bc076ca242ae3e272a9c9b72de1189ff3cc3f83a9005a4a1010a3075415e408a59451d8ae46e75253793177a0fb0e60eaa0e3b61fd8fbf69ceafd3316b6d147a51d6c132060eb1c5b41fc3d609b21459e186fb67d8fa67c03c51532cbc1b0905d7a7158ca3e44f30038b0713d4ff8d2981244fb8db965ba6b4b79432e003fc032204f75ed5bdf7ffffefbdf7fa8efbfeffffffff3fe567dbff58e54cb99ece47dcbd185f8c6dca4bbedf7bef4f9451f2de7b57586c6edee14c4c10265a0d1d3e3486c985d1ddb12ccb7bffff9f36adbcc4c8fbeeeeee5b725646756452deb936d8c2d3b11a6e794b16706494913135833663c855ee3e9ae012ffff7f666465f408af9821b9aa1c69e580c95b870c89cc88d48e86a6bbbb0e2b3cd1e338f228d3018f295a07280cbd1621821c43a4210e23445e03851e42dc6ceef7de7bcb0fb59b1b5cdb94e7a3b6ef93d40c1fbe334d5fa4b8093f8afcd089b8f7de17faff8f1afeb35675ca0c201dba39e446f08690ac3b7737c727373a0677d0f0b3f1c4057845072fa103bb95bbbb0ee39c8d3b8fed6e43869211aaa0fc4699dfff7fb6c92d8c552d263c64c762084fccc5bdfeffcd243127ddefbd17ca131428285da0444129f331bfd74b92244972fc5602a27820f1655094d17befbde2cac94b62126ba159191d222a323a42ba1b687972af6f999120499224cd1b281e212537e523f9efee6e925961cc83b2b180ba9133b4caf2a6fcb2aaf1a4c6528d5e8d27a9f2de7bafbbbb97008c7246e9a8c8eaefc5f83136f1e5a009e7eeee6e5225593054ad27fc82eeeee27330e6e7aa771b7777cc5322daeeeeee13b8dfbc8c2e32a2649431a3dca2e0a2e456620b04435cab55e236e50d751958d2af598269f915ddff7f4d9451f2df54b5802449d4e8b473777777f71c72a94b2fb74cd5ffff8f7852e5dc7b6f8e1e47ea34b3325ab361164e37fd3c2dbc6b04cab744011a3a29559024c1a062d5c8502b42f19010369ecd67fbd5116333094dfaffc7efffffffffffff1d30011ef5d821861c40c0f4c20e41efb5fd4d35166d81854725cb6cdfd1171367b8970a96a10d799dc464149c95d19c2e69c7f3f98a283f77771a54feeeeeeeee2e7483e1c99a192cb810e44e63fb65020263cff99274dddddd6dea8a303ba22d42d129353cb288929a6163a7a4dce8c14cc21380956224fce556007a18b708734b08cb7c6c8500ad0de34419d621a21e195cd4184132450489cf9892038c718bece71fcad0cae8ac66bbe1153bdccfffff04452ec65ab4686192a4cb962a4e82dc656c1fa53c30ae94a4a0c304ac1780258f87ad453e297a62e4eebe8627caa8bbbbbbfbf5fbf7de7befbdf7de7baf59ef04185ff387bc8fdfdd9d89cfcae80f181472c06448de7bef7577529768e8b1463ca972722c7861c2982366bc0835d9418546038e6dd1c391f7de3b418c75efbde4bdf7aee179076b7cf9279a6b6828efbdf79a94478a2dfc6cfedaf0f8a79419babbb7441b222e8aa68c6f8c9a1c5572c8e242437cec3ee8d0e948d2b1ab5f6656466bb61b2ec7c117ddbfca1403d401678e9ace20e99ace5429854d66724d66d6d89a1d42924f5b13810bf2a4f35164231acaa7b5dd8a4ec8a7bd599c35b238d7393df99a72c81dbd904f9b0321da6a40acce2229c9a7d549603b733b83eba5a1ed7a3b3cf5cc243d31924f9b7433030987dd120af9b4bb1ddd88c33233815882f07a37f2697938c05913941ab009ca145c130c3d3479e0c9f7b4423ead6fc44cb8e403cc0fcafe4a25a1a1126da644e3a1538251b340db05483e2dd0c429a599529aa2cd94863668a35ac8a70d662bd4a12b5a6285b68c2e9f56886765a68039704a55723c3bb4532ce4d30e5b3e265f528f684663924f716633ebf510673ddc975d8f28d8bbd58670f9146b5bcced5ac59d88c2051473f604d515213da14c568c76d5e2933d41eb4eecedba73b24ff1b7ab8ac94ad33ec52ebb8a274ef1b6abae5a34aae38da92716d913391626da44221bab5ab489c2d182f043456f3d1dbc518a1a23524803b8421315a53437d741034629038d1ada4e4192e4ee2e1bc9ff6f85a0b7231e69a3c2e2ab21ca66c9361d69c601520dc9669a2172a28c921240728403529aa899790e1b4cc0b1a188848966655e1aeeee3423da110d89f6e4b5fbbdf7de968fc5922465c64380a1a1c3cf890b471f701c44682c11197de4bdf74e5096ac2a062726274677dddd7d690c6fbb97ee119885e1528313af27b45da81f9e0d532037de15821befbdf762104bf275c7eeee3e13659474d1dd9de541801817b9e9822f0af98ac0fa136594fc559698bffbbdf742d1ac8c0e1115fd065046ff66129641c9e8d76ab9fec9134015d60cc11863dbcfca2830286c611bce5ae0a8d934d9d9dd6b626c8656320680b90075a5062632a890440b07702042ab0885367e73b10283db260763b25cf20255ab2d00b1bfbbbbbbbbbbbbbb5f53c9adf0feff6f7924851a13fcff13d130d1102622c2f8ffaff0babbbbbbbbfb942949dbbd1cffff7f8aefdfa468cc601b8c6518da58d9605cde8bf13503f5feffffffff4f24f7cfbaeeeeeeeeeeb7a7c254ca9aa41b159731470565f54efebdf7aa39b2328af4644903353dcb83ed5d0cbee05ed04153a6033453b4a10e8644e2ff7f30c9cae88ee7d320f87be1d08df0074e0684940cb141a50329bc70c27dffffffff7f31c90516042a3081993308e9580ae1b8bb7bcddddddddd7d8531c642495646773cdf0fc84228f843de7ffceeeec0232ba348e493bce4ea690d804fb11f1c42b01d87c42dfb7f2908b818a4848637a6ece0378c3ca0a32bafbb7b1877fcbb10f66877a3a2a2ca444d8da1bd979b4d120c6226ca28499a6569416d8621a9315690ceb80d2ebe1b2e37505077d8b069561966653992ff5bdc55830a1f881189b9db126e56467125e472093a5709493a86446cb90945b67bcbc97befa50d591925a215659a918b76a4630118ebf275bef0d50adc00fc1a37a527aaeade8bafc518c348d86863972c5760db82d41576df015b025f0e01041b7b0aa0b2a53bef2da82e40eb6e7b7de52c80eaabed1637d575a1b5c8a220eb4dbb76e1fad1df8ffee893132033e9cada15d33fb193fe5cdb4997b85db19f14bb2017a2cc6b5245b231f7aadb41a92cd5a25845b83a7ba98f8d94bbad082aab772e5541c79f1d1506297a72a44851c2c6442dee20adc3483071b974ada2a63b3090794ce33b419367c6cf54b756abd58223883c921764a1e2192256f285124c8d63034417c705d0dd111a951d4777f1f5d5e3aa85b8e25f8499b9917c7f775d5d8fb031cfb60833abcee2aaf506e9e2d994294b9453131be5a26375f56ccaf48463ab3665723aaa614090f1d6d387232e156246ab033fc66471a90f358c09c22015a2059a8b1802e248d1981a8ea3c5a9e4561492540820881c6344c8c0e1c106a956935de591c20764d061061d30b8dc6684e0f243889811d074c0018520cec36eaa338889579c6c61c149044c3c2d427ef2db6bef63c00a13e091932d46ce778019f34cc33425465afda123a937651ad2b0cb4d99864e3d4c48e9a0cea0c043cd0d4e24e28a88183eccea99f3ff341a4e4e2b0465a0e471d39e909097cb4d7b425fa0caa0ff7f9ca9ac72b2916d62c8424c562d9ca11124a11f1f72c879d39e908d9b2d59c499a2bc0ad3ea61061068b2b9692f46152659b5692f06940f56622cd935f76214f9ffbf1941c6d9b4176307ce0cf9c3a6bd18313e10c92336edc10803024c2637edc1b861486e6dda836105c8ce0b0c17707ee4109bf660fcc82183d8b407a3e694579bf6666954e4cccb76cf2dd7ddb437b361ab36edcd84fbccb80b8e1277554aaf383669551c0226f666eb3c14d92e261e4ed81f36651a4e0119c0ae4bf54b88c0e5715326a79cd1be7aa43bd5dd49c8763139fd18a3a23ba7285461edb0abdd2cf5c3723c781248fdfa45d08ba0174194053cf382f00c15d2bce5932477a370e8f7355ede91cfec33dc91d1878be22271944fd791d677090c3c62258ae433f673818d7cfe88baa232ba14182cc9678add52110e979481067427eeae8491da8105037ce493f21e0784f6331f4d1ce041927c529f69e341ca7d3dd87011011cf9a43f73f685c788f3723401109ae4930245a09021df01f1852042513e6970b4f5ae8079f25d012366d45df9e2bb026685113030249f540802d8c389e9eb61450324f0c8271d621d102747335a0af9acb35dab365156f52b483e6b0de7a404a6b915e9c867bd6d5cd539473e2beebe6ad2927c565de9b3e26568e5cb6c9c5991b2e1402b6088ac7869da2dd5c867ddb9f088102a95577b2ee4b3f2ae70490a52aaaf3ea990cfeaf3d901814ce176a4a9bf0a4594cffa1b7d3a7c4bb3a30aac5d82e4b302adb086d50eca0e3558a372f9acc15df5bfaab00e4559d5004092cf6aea844c3174538666b41cf9b433162e2fa0998d593d82c4aa3be2bc17c0b4d92e1a598295c3748ac85c4c55f8f8a5b63d2c9868a1c708053271a4ca30a08e910e4ea7e8a802e9f4d0f1417dac89a36d35099ddb0532746ef566d7d1bfd456972fa01ad06cb5e26140341bb3da02345907674f9c22b870bff6b148c7a88465ea33fe8bc40730c5176bdbbc8a84d0225e3d3851c6d2f7d573a33e2cfde1253442082d2af514f5f4285f3c41eac3d2e48b07487d585af5e2f9511f9616792c2da6e9c273c328c403b5599ac5b385c6581450a0354f18de6601f7e962b17488170c28ca581abf60c1a088021665652ccd8295d92c1ddc2cd8053b6431b7cf1e1f88574febd5b313653d467a9434d9560360b581b84d584f8efa584d42a87333e102508c6d49134f4c28a14040b35a81a52aaed6f5e4562b4658cfd11a133a13482692788254c58aaa30b5d53c61280a99122ab31a078e1e3d5658c18811254a4e18b0b6add6596d70b35759af5a58f2625dbe469f8effa84f8da95e24d4161a7a7a562baeeed13b2b0e6e760993e9a88fd50650924f133db96d4de8b6d5aa9e558bd7b85d6760abebdcc6d77d550d34ab40945670bad3aedaea2cbb02cdb6d52a20537c610c4cf145aa10608aa38d91660626a52a48641b16ceca5495d845def7b1848f02445b6f0850c14f010c6001095690f82a3790ed5fdcd80acc7a8a7872f0d4f0c173a547051e22e44ff953975cd5f2b0e20e1171dc9142da46df31e1c8c2ce1d7f9ea17717795d0198cae74f4d4441c5bfe1b6f8b9fd771c613bd4a76a9f0d1695a8eb5b7336069d7ad3acb2ab0f3ea041d1e5634e8bd7565b588ee82a7a1f5f94fad89d18446537dd39339a5cf5033004aefe0d604d6abf186b52e3fbcaf1e0f5d54f49187d18b9e3d5d61aaa0b98b3fdda6a324fa36b88576de2285aad45d8c823c937ebca85fed50758ffce3636f24a5899d5549f64b6252c8af176616709879d3bfb8c9d310c60e705268c840a4645d9b9032bb14fea13b259b366cd1a0bb888b9702ec69f28b33a29675cc1c69b28b31958555d7d403f5069ba72c04a8bb21c0f4c96365f04a94c6dc24830350b46ae3c6069158c04951e93b61d73db8a7abcad2050834a4d570fa8412b98f9caf180a4e28b20d52bc703ac4b55a9a2aff36f8ff3ddb6bcf235d217299264ce2e97d6b7dc3eeb2e28a2187da2d0c5738dbca01583db064514344814e6a09127ca7064bd479bf7b85ac0a8aba8735ab8aeda6139aefd624bf025eb8d4396cddbd1e7b8c26a2159dcf4ad4d2dc5082a847527c62f933f1764b53d365c883b412abe19f14316206b0b4a9d47dd93824e11fc3d91f9855001172e2aabfac737661cdf911a35e9e465d18256206a310565fa14b57ca6e07dcff7644f8c215015ad6aa3349a26ad403fdb3688c6f2b61a5b8d2db62a8d4992c4bae2913ca894ecdaec418a440a00082800e318000086201086711c48a224a8c07c1480062ec49cdcac401a0fc62151301c0c83c280301010004120084000846220060661aaa765035dcaef41911cd866e3f7b65662434bd9da4a1c08a3eec9beb15b442d71d6d0279ad114dbf4e4714f4328aef2f27f838196fd7584598910a60b7664596f47266a320edf80d82dc6ea4eeb783f16a9dea06fa1237f27a412b3465b449b62cf65cb09e6b8ddc8d2f95b6c6381711bd83965a9d230bdfd322a5409d249a054cf38d6bd3ec73278b6747c229b3722084c202791166fd79c10d8f573901d1c00728248fc9d6484a3a0233c0207b1f115b7101553c57b42f395dee96baf115ea2eaa35305836c7c479df160bcfc7511b15ce363a28cb8f64b0982383173dc6311065c9e16d9768e6ccf496cefd179180308337d3897eddd56675322dfcf63b897fe7d1413db1e28374fc4e2c464f978b48d02e00cfe1b0063db250520be23ad83a89aadd1fcac762e2d65ad91e7220a50ac719823726bfa992ccaa07ed914dbea5931edfde2999c2b430c121982eb26deef0410688cae482c07dd5014eb0610dcb766930cdf58dba7bf2e46945168aaaa8b3f6a19a065282685612c076484dba95ace8426b896e5c2575a672837461d2bb43ea8092b7a59bf70d9f5f3abc8473fc763eac9b98eeec20525a3d7b78d72bfd7cbbfca49728d0e03ce46175c5150909f9171e8b3dd095a2b67ff8e5a90cdc88f06c35afdc85172565d85fa62bccb041fcd4cfe6602598d46448fe891a1eb490c1aefa205fa0e5ade4dec86a362678344a8156a2a773f3c8516bc78705bcaa9eb7e71702337fd419fbc8e3bcd1271fa9566dfc86ac5588bb4c4b84c73af62e955d733d1496cbe4175ff928d0a207a2c5ecc202b5c60a524e40657c405a55330bb422b8184c8ed8dc579e12174f1a22eb0396d0f6810da817ab1ab1dcbd2a9f18f2ec2cd27dfdb7fa38faecde79493c2d2449fe928af099569d413fb01ce805ee46ca5328dd3cb6b6a6f8e92bc3ae6322836e19293b390aa67339fa9f10dffdcb691e01e7c77bdb0ff109b8b70cc4fb606fc9267caa4235eee74b55ed2df4a315af228b0ee33a264bb9813a7b681e218ce2d56b4fa1020090bb8dac519e41416625d946e85ccebc0b6a2d06885840b3ea70c030f3c801004586f0a8a880476ba5db675252efc2664c2dd1884352593585f8089d4c45cf27f01254b1b9050ed4d5e11d1ed56ab3d9705b83e00505b8c018b86de542c45341d738a9e11422b3842227a708fdbe61e037eaaa6bdba1fbd679c264bb308f9b44b1a4d8ca786d21e24ffb7379c01eb1fe447eef7224311277cd3d052133f355356069cb53fa4ae0c5fbf5f3da7d765abbffd9e5d6d5c1e9f90a093c17145d66761038d1fba75a668606070d03df3c32365337bf258833e67c2f771a5a0c1873d3644c186c23ed25c91535146d9240925ceaaccc0ec5fcb9f486cd221553157026a8296edbf914a911c0d76c56a277bbd5dd9342f965a9f1282accc6e2e9c09f5dae0328bad453880d64da2febe60d953a6750a7de78c19214cb65ad690b4a27138ea28a512a2e66d7c2b748ee56939b2343951910230523cb2bc2be0ed3fa4294c59898cb1a73a61373b661212a9702fa73b5a9c448fa975fdb5c8500f9075968ce88f90352dda0ebb3ce1a4666335d8ac9ab3a65190c75a5e2c4128a9e5db03b10f3abfee704b9acceabf7468d08a7f601b83e4b4cdd11bb138a120e1470baf71cbc31cc025e9bd4e97a157e7b7649e51ea25501a95997b617650e7713324cdeed271cc242412a17cb0d92e541bf31d72c73378484a02c9711ca7a31d728b81ed17aaf278980e4279f3271603964be4f55ade1649e852315348edf19fd9e45035098643d9980583c8fa500b1dc24173b8f0c98fcee3a6dfdc229db3bd8f9680338ccdfac74406a4f052fe39db90dbfe3d3ce54e7fb1cc53949aa7db42a8fe864329ee8b3f5f1747dba6f535d0f4e5fa519710dac4122d4c5f810ac25607047c9970ca4f07a149b1e9c0371846f221d9adf7a5eafad68022379561b4faf505a2abed96c271599b819eccbb10a480945ce1e3c280c74f1cf321c43a3cd39415fc9d80a51fabee0f65301ad797d8a46c4aed15e983c281a5cb9dc825d99e05cc44ac88222b15db22437251b08ca40f326aab7bc46beaf1f919d610d0cec4ceb5135315c8a269f900842ac41a0edfa097720170628b7073a192b0ae58b7000092cb7b1d540737ebbc2b25a1ee21be805daf0f56470a5535437d222bf39b5adf61820b207303b19d79d8734ca11a6ec59b1a11080521878cf22f141a64ed3d965050456b1e42ba5bed21c65c2169e50676a067797ae570b075961f1785d3852fc911260a87acde46449d098af16a4c911ca0288e8b12b621c07f78442a4c80654762e860144b131f4df8ae167370616803508358816416d5769e239a1973d0348891d0e880867f2f8474cb70f050d2ea054733218c6cd7cc9e43b3f04dee51dd3521b221373c3cf248bcdd7c507e0b24cd067bf2d8062160db7c3a85001cc612ab69525221568a404f32b398bc1f7bb16c2a95e8bf15c6092c855c9aa2438235ca83758e664d8ea98814eb7fef6e64ae453202ea7b20863a6a9914f9436003ae067f9b77a8f2a8ab9885173c7366104d28e6c8c8fd831b251a3f72449ea6265d5fc3bfe15249cbd3a8d204bf6af6edcc0b1a09e92cddc0b093e92d43540ac84a49a9c9022ebab632df0d07de6cf2dc8ad720a63a2c0dc574fdc8b81a2421ebca585171c66102ce3bbd8b304b3cd576e69308c08f630b8701541464c68d3ba43676d84e19347883f85aa4713d065015c1832ddf14255a13221cd49f54e52379cf288fc46382a8aa0e39ca0c39ab07fbb8650cd3b98cf1dfd127ba414622c8e4ec446f2846e12797e15a7e41089cfe9ff038d4fc867cee8b17325d51ae7497af257941202d926c40fc50c5bdc470a0e82e8012450b2ece9f07b25640cb28f9c724283c1399120413873c7b933bd5b3da01000574fb65f8edb16a5125d91cd95063bcee6d2035934bef252161bdd7aa9d33522c88bb901ce4913357033019fa4763c196db5666210e9bd44ede9820c9df363832a317629cd823e21c52f13e2427b3f47d200d912c0965969853268ad84427012daf9865bde04c8374b38f834dabacd9fcc31f4414b41f0f3697c3ebb3fecd08cefa623253a973b95a1e92ccaec406c2a137463bc5ca01b2d5b861f3b1ad32953832f21b1f59a2d3dd7ef45e7fec01dd63e7667c7b067530a050ddb3b80ff95acd383060c8ebfb6a302b6c289ffd7f340766bcdcb6b37c0c816564123be407468e0e90254d0644203cf291ff8c7eaf2e44e4f863ad998bd7a10cfd6a1a23707b3f39ab73ae53c7607745be1f635643ac2b4361828e93a2d503682050c85b73a52540f28342e8439a450f0a4f6d500c7666556f47f24b54d92fd5169209bcfdec976a23ad1b60199c99c2f40c31d1071f6dc9d5d7b6aec491c8123574761c4dd0b75b536a9a7db40d85a46fcc360a9931eee6733a1205cb401357fa4204ddba11f3f34ac9f56b9daecf5026ba41ce16873d43dc516f8de67ce19082ec2cb4cb3ae057435a1e36441b3c385469d02ef31d2682d55bcd6882c972b3bafbe582b379cf85b5994d4bf8023e7f35bbbc4c3363fbf66a00abab904c41e04a2cc1aa164d811bb200023f72e49a579b876e0a1872e0ce2b8a5040c5dce73916322cc4748446e85526e5d18dff5da8faf6fc053d5e94fa22907eaa1734c032de85aa49f8b3da76e0aab630168a44c65f7ac79b1d4b1af420b39835acf8049a0ba9830b1c9114e18bcc1fcc905fce2348e6a359d02e0fd20db8cd78bbc62ed72906e14add5e660e81af97b9ebb6d3a0c0876a8886190bcf92a0237c798d83cc97ec929fc4e29f87d957a4477c60e4cc88ac7c108ca997468cd1c42edd1d4ef1b30317a9446aa1b4406fca5b022ed8187d9ae7d45c430fc649bde2c94a0739047693b0160e4e25437b0c0b074fb87b5911d006d24071e8590f5309c861cd5b2552b2e0cd44a2f904fb2afc0c3a6fdfe8cb256ec397f5613d07f5418966f69f561ade10c06694fb33ffa110164604f2be979fb8caa240a799bbffc8bb025d0ba8831b9c429ff9d0159a0e9c41e0579b3ef26bb2064e813698219ff988ae79d28540faaff6273410d8dfd0b87dbe34236f2698417f3e9747f3ed6692fb23ef3e690d9c0f5670f2830806d7b10267dc3e3fd45d719669b4ddf1f8b38084a59e2b062bc342873b4b30077963c6e0fd7dc4cf7c296e520e2883d517fa63d0c9cef59861281ff992ab5c890b6af9ce1f3ae121a277336eff99afd27125b7df82fcfcafc5475a65fd7c37308c67b16772572930fb24f995b0af000461a6b71a1132e1eba710d74fb1138754252c3143473e54fa0872e5b6875730e34ae386ef305113fd87e80017fe05afc40fded7972997cb7011db7d96afaaf85cb48c240d1c21216c5c0861e29f35ecd7f73704d2d4cd8efd17a17fb6510d1e4970837c0c73a6951aac982ea9738ca0a094490a085dee605e34b0734442087c302fc3d481caa4a80038d1301b0de0c1c1168f8cad175655e7090fa387166349dc9f3c3aba8fae2f773ac3273e3cb6abc9a09f77a8b2fe9aa1a9d3ddef039790bf9aeb21d27fe2d50b0da4d15f0442347dc8ef5106867dea37bd64ecf027efbaedcb13d72dcf73c9c79250eed8708459550496dcc356312092ab1eb140198d22842e4386cbf040f883d7c9ecc02ae29ce06a5602c75e173d4b08d6113e64d8fbfc436f026212c0998a2e64b0e13c554a61fe2854864abf82f5351a0050e2714d88d7bce94dcf7beabd1e89e64588f3e0d4593ef48a950582d88d14a26db1e187901bf946c1b5adbbc114e931ac708afa75e78801befdc31e05631e9ab032597aded3ebdff94d7e0814c50aeacb90c0045e19d972ef79e65695bb3a7c460dc08544ded09a82f05a2ba21059a3984c44f189dcaafa296374c1836e063656c3a58b83827724b9987b60e45ef958d24ce479adf051c2085e57390867af7e606e8987459ef422cbd70a0f7c24d42faf5410d4f65b1d6220d97afc9fce741ab969c2d9df67ec6cac5b7de9514ad8591e1d1adb0b3f46aba7cd92bbc777293fd5fbf798eef4ec39ee2c2d38d1891de39427e6c8ee4bbd539e1360f8c1d5130bced59ef9cf828548050386cccd24e03490151be3502bbf33842c49cd1bb2b2fbb1e1e5dfbd87b435b24be61f405f5965fb5931492f850b3664f10a2e4f346ae6ba5ae4c2be86a71150ba31374ede981d8f6205ac63cf3bf6b1b4d21ba465737159c2e7fde5fada3348af348e023b2e42f1b711f9e8342e01559b75880c60b6c4234cba9f5f6598a1008aac3b2614bdd751af0208c0ba9f5e402f912d46926f684e8a6cfaf6a0322ad9d9a49f3b90a7988d7b4b0205a619995957149f6f6797f4245e5e0df89ab53ca6a2cf62d3eac7c5c559228a9d05ad1902630fa60c1306f9e183c1285fadcedaad9d6a15978eeb56e1c11eb31d3b6147417b99a7abcba82c88eb98823549aa5192b9a78132c50571cf1d5d258cb147dc410e5b10d8547b5665117d4bcc15ad664c138c7b671988ca82a2713bdcb7f57c53651a4eaf527a717144a77831b62865a106c10dd0a35ce27a69ae015ddc9105f1e517b104856fc9887a8a9d888096b959929b7606d39b14974131a285d6bbf44bab7162384ad8008636532ff148ce1dd2f5d5969714c0c47199993b05d52e63f12f96d3aac69822fd24a7bb5473ace810f70998b579c51debd1f587679a86298fa84ad15ac22a630f12da4ed9c986a9b75fb27c5def2c0be592eecf35240168e268ffc6bceb0ade694de6e7f56f3ced94f3637d426fd65ef542693a4e4d886357ff92b75cbeffde2b55d069d34820f4e92dcb024cd53724a33050fa232ee7746b6a8d4694fbe1c1540a8a3a3adb3412a4667d15c2837657085dde29f614d451032a5ace750633701ca6cfb0a36fc16a5d3054a1234fa0d5c827ddb6bbd4b26c2da7ae29469e64c5eb404be57aaa55bf86d75c016ed8f72d07d2219cb584ad3983d851fb20bc75c1a012b9d27dc3cfbc25456a273d9ecee32aa5536e4002311f8614bf4aca250b9bac9398a59fea92947d405cdac42a2caefb243fa95fecca2ab84af150e42170139bafd17ca55c744ed421dfee09a9aa4c6900ede48a73a77d5ae6a509e715f7d5f13c76b7e40ee93cc23a87969846a1d7d901b98bd6ea2c9cca7e85fbb612ff881ca57d7af06c18efbad585d7cb915d21e35758a28484dc0552a9c1324c3a0905d9954c32fa3326cd49d0294060012e72ce8d0e16cdd425c3a4e77a855167a3429ee141ca30694b0a6e1e66141053340f264840aaa069ecc57ee3dc157a813bcf53886098e28c6b4b98f68d56986b090a34881b2453e77e1f0611a36da4b03bc6d7c0582d59606d98fde0cbb5fe1d5b268b43c830d5483f74581866b0bd1834aef89427e9a3a3ed909e31107840836e252077f6c34f9d08d09acac6059005b700850044b9171334a278aef02e13c18491299b31b92440ab2f063dfb86c71860f56e26a6959fac8d1f757009744716ab4264aba3061e2880a98c9f680eca3b3729f82e54ba15004a9c3be65b26002a11843482c07cfbae2c94f6119fc065108610b0a2c81031d4718a30c5a9ac9e12bb1e0ee019ee7f5450aa5b3e32dc0988d5b180e2811c87a66bf4ffcdf7acb4116b23e8d75131ac94b3cc297a3a31503a8b02a05b93cf5bbee87d85b953aa302dd74899c9678b016b46236999a4c5e216cc59ca8299d54a2418972329997ca53a783f7eb97670b734800fa9d0c311a9e55ca077ef057be69d755f0a51ffd0ef00e54d98be913609d9ae7e194f2110d884bf6946f4b80e8bb3abb033356f324b4a9f8e17bfbcb10b525368183ee35e8810431cfc9c139c39d4d53bfe98d829c82dbeeb99bd07d94a0730dfb438f602bee19b33f4db8088fdc1bb710310be3f208f99bcab20568e05342cb6e7210ae9a36a69895296c344e9d131818b03636677985b0af03042f81d3bac0b0ce89d9202fc626e8a24ca8d6e610e01d5b5bd987a60bfc3f70ab51802e044976de6b09808234e9a0c82685c7d673af1a2249c8a4b515528266bd5296675021c310a4ecf73a04b7c78ff01ff20403e42a1a631bf061589fde19a56b6fcf58f06747c8408ca85c7c94dbb589442b1c708d0cc73439286a11b7e7f9740412332b112a76f168399eb7919916cff84b89e71cf97fb94b0695980eb7cd03867205e3999f2f87f83dbb63cd8617e3b877180503262a06324c909982406cab9fc6e37536a7afcba7edcc5947a625ab3a43865ab2705ee0bc59cc18bcd94763c02329e0b0a286901ec17e6e3a708106dfecd885756043fdec4e1db5802b956000715f58161e6a123ef54bc668dfb15cc10ed4f76133f94f05698dd955d40c3a821169d64252aa032310c6eac00847890e9c1e84ebd0c3b22a1a34040cb4419db11717c1f296313bfd76490b17bdf8c5606fa900b5f0511105d37270916c004f00df41c82d6b99f837ccec71cf89494aba8c603a0b40747e83719eb8ceaef2e17b6ffc3af5cec38ac9241ebc9435e37d0761bf90b54b956542cd253d7081ab0b11f126dc885d0114db905337f57320b56f61d927a6858c72d5488fe2b110b8d37da07fc90dcf68f069ee7ebcc31a8e3b7ae0abb27adff6c9597c7e9b7f932106718f05357deb22a16d78270f37c73b38487843d509aeb0c8d8dde96e2d18bf615d2bcf1869dff3aa824faa621c33bad8260c1cf1fe5311cdc66304a945640037c098741580b6405805067afd27743a957f3cf3e9567348de97e7453ac79f9a68490cd6447e35600a5f838778fe286a08446ed62f36babae4d6617e2633cef833ad9fee7ee7b229adfd939ac29a2320d2ec0b0d3f054069ed9ce2b03b456d75838dff67112ec3b4fdd736eec4e4808125a32a1b4a0568ba353f58de455c5abadfeaa74ca0ea9980a349119627db4ac393120051c15f236f7bee14626aa7e7cde61d6b3b592b98ed287a1a378ab82ceb82afdc440ea25d57c17888cc7eeee9a22a2c6d35ccc5c2541d411e2a913591c208c0cc0bdcca8eab7b088cbcb5c3866d9e73f1e54e55fee21bed5cd0892cd9466bce47a6ac5841a93ea916c1cc9d5d81da9aa210becadd325775481326350e4a93117447578207a84b8d444fe676054019cc199281b03d44a35fa6958a80908c96adf5272c973d094309153333503ffdb4b08c0c42e4e3f0f7505b6a98f456c0c5e85e9ca965ab2b0ca00c8086552aab371530d787a7c5e09c1f053c44cf5f125dc0138c89c86e6dfa9bfc9b00c860a227869f2fbca3306de64098bed71dc15b6be18cf125fe41334423b2903ac26e5c31892cb64d32e7a88f474448e28236f74df2bacefbd585a06d41389abbc541ec54c1745fd9d4cd5fc6c4561f048377055de56340bd5af104ce331c126cb6a05e394faa4d91a3b26596f5739f406ae129e7e271b84bd74f30bf4fa74bd1d04f3d167960b47668142232cbb9569f30d60942b99cddc1b906c37c8bc883c0b37f0480589aa22ec4e381727360e819a36b5943804ecbd6d6648dd14ae908e10556ac4d5a7e71922f1bdac47753edaddf1c3aa9d0f66152a3ec60d89361fce0fce12ee94f59e6edae1b3b399b101d0eda0a4e5298f29cb94fa2cc665e2db8b5ed78251167e42a4ceeb27fedc22ec390f9c80c3fbf838d61bacbcff2bb03a06a9c17cad4bd87cd500f2e3013b450535f95e90e521ef174c7571bddb1ad2d47bc9adf2cc09da90b49d83620c1d6e4a3fa421f8fc9689527a503ded912de684f26001ba970f590f85e8f9c007224069bc2d6a692ee5ac6a162360ba818287ee85cbc4a7620dca72a288f8a883b81232a294deda21873bd210860b4a55c0ff168ae901ec24bc957bb97a091c56cbffe26d6bd02b9f56e99951a55f019739632ed6df467fc7e5d8ee34e253e383df569436983b05c148762d64713726202813a74a63a282b7474cc42b6bc9a3a5abe6874d38155115abb4fd7dca8215843f6e508e90a71a959ee308b6571dc47c36c1389023f56dd97fbb0700ddc1a3f3e5d3aff3a8a15e62e97c1016a060429d79304bbced7ba80542e652089f73d1e4d190244eba33c7800222013dc6348b4758b09220e6adb7fff3ac291a464dd5352118e3110e4ae3a294ac5b8b58e7f2cbfe1bcff865f95b421a63d764e1d4edf08e04bc24fc3a83478ce01628729307ebcedfc56fbece3d62fb9cbe952bcb5ebfcc08a3063865b601f047b18dbed042ba200505c1875639891b3145355b28fdcf93aa4ab71018486ffdcf6c4a967a780436b4d0f08ba907866bfcc44b1a618f131dbede4cf21bf023d437b72b88b3889b12784335628901f174117659a075b2bc04850a6dc8beac8910a02670d0a913e21313cd00164a249f3a676d7c9fa34678ea4f0bd0c175a8f6e8cd5d1e70004e8c27c56f7d26ced41160f8241b5881921734f5a9c3cf2f0115b37fdbfa38caf760080008e91ebd008db8009644f70eb5f59612487232ee621059801696cdbefb0126652794c02b640e2627de0b88c2c0a609ff3a23693567c4877b44b9dbb1f50c311802209569eb9a0ad236a6c2c49af8a8a91a04934f66609c4ab66271998888edf01b9621d78b176a068e70e1286925a2d0adaa9b9a7e125c209fb0c3c0117f2b0e8230e759608a50fb4a6fc7cc2ad5ec283a994234c1498ce4f62d180d4db116e27030f98580316191b29bd920baf42f2599f49df2213d462a63970a07a5196a5977bb204c4d6751f49e7c107c9728343b4085d6693ad470a35b2f529e0c6e67c0cfb263820d15ee50ea29364f3e1b0f700ae1c75bafae4440774e850ca5c7afadb38d652a48bbd9a6ba17bc8b01711be063ae1ee84469153e65ab149645e634ffe9711c0fe8bcecc80b556989de96ce04d1018a03060c1cddd06cbcac002c60d308d36c46dc5f2cc24bc1b29092ba283189c2b621c6d9bee715d33671a1bd61808a9eed32579725a08db5394cc8eb6a1580077a794c21cacb174bde2ab2b897c1854949d69b077f3c1b2ea265a51d4ccd3ad6833749014026fe92ae18189ffa48ed8e4d52ce983370c99e48069e86f3771d17d5d1223b70b93ef5b59c3277a8df58a79e442f5a9da5867580cdebd76bb6cc9e8e6fbca06d08f87a71c004449187a466055e254d89f1b4747d392883eee99f22319cc56229e22fc9814ff0be88f4a1a1eee88f57417869718189605e2b945735eb274e42ad15708403b0091e5bac93dfff545365f7c6fee56ac2d3587f9bd79c39e8726ffe6799e08541731d4c856bf08f62248806947cc4a4aa953db27c6572af45a854ef374fb1afffd630bd881b2a099d25177b682d9ca12ee62e847143c83dc467b75b1d5c3047db034a8f5970d79259ab05cf26edd8f394de89b6129c2ca2a5095756c3674991264df4d6bd0a20b046632f098691a2ae6ac39d74b958767e17bd853495aed6fec17f1327bf002730ff1bbb0da0160fa2f9cfde1ca068cc70dd9b9908f0d330667a8fb538c802d8ddec8bcd342ce11827c8d43723617764296fbee61353f77d5a9da5419a15710bf820a50856dfdda49d082743142ad9f8d881a0f76d42578cfcbd58752f2e8a327edf6ed16fac59024f14c37c3dc80427261447ae2850ab45121feb91ee8399d5835d9145a90ff081f1c2f07a5382e4399fd144ede122baf4c68650c74c21748c2352a22e2299a6458be912f3f1c10a81e4a20c913c6e4f5654f393afc026189004497436fcbeb66095a6118d003831e58e00f10d6b5842bd2ebc736224113d9bc6a494a6571be6a89a67d01bb3a5c478e4fd533913146690860ec2869a84ea29199a0d288558a9dc8867988445bad6da28386eba43662717e174b8e9575aabcbec8ec9dfe1acedb4f4b200de5559dba7be27ac5b1c5cd0ae014efe5d19526592c09fd3b2a3e8310339493a7ced27d494b2dc1633d36de6aeff35f32794f800bba3fd4b87a9612986d9837315330decff9ad169970be87aebb4d94352f626dffda9d936bb4d9fe7083e41dde0d5f9980b3930aeaee584475c3b90ad44e3bb5aa895b5f6ddf289ec8a3dc75cb405f03dbcff82f590635ead3c84fe00b63ba77165b1fa41b0259d0a0ba9c5f1e76c71fa5509b06b8bd5ccac04bd20cd2d3d5233513393da03cf253fc5ca3d510f5c51250fbaf29eb2ee9cf9bcd4584a45997fe4dc4ac791650b4ff49ef0a4163964a3d13a709665876fbf0422e0baa85e39c10b2a84b2109fba88cffa6bc07a125a6b52b04ac68596af2099e84f119a2e2ca0cd0dbcb52da455965b12e325ea50131e295617cefc671e152780c3a8cc96bdf607b2c6f5184eea003445d013fd2cd6886f7dbb552a36bd37e3e8d5dc0fdb13f0fd1eafaa6c0b37611cab8a9cc4eb1076fe8ee594243123266e6eec3709e81c6f07c245c09ba572d5072d16d98394addc09c85d09f18b4902c47c8728bed90f8c4c41772e7d13505f2102496c0c43166f0e8c90c87c044652b75e9ced6f558ec1662258090be57b18fa10c716ca4e6654d9d5c88e21a7a272b1481c2f6d64576d06a8a3297676845b89ea895a8e691e562fd22989e74c723972b0888ca1bbceffff7c19137f943a28f8241b68c99db2a723c494c00fd7b3fa6eb029c725a4918039e32a89235e2ee5ea5813293ebe98be91ee97dd8bfeef4c66041028b114e6578b9c058c9d1328ea4b4aaa16e0d3018e0da263cf348d8b0580a86910bc2d3289012108b6bfba3252d02d6dbf00f4b552d044a743054c2ba6113f835ca1b7fe149e1618a4894114c5bfb518a86d9f00859dfbaf685f88d10955d673572bdb824fa204b47cb31e0895ffc0dc5e42f892a5f49019378b941824d07f1099835cbf1da09daf4932aa3099fd5ca1d6aea52af7c5c09202322a81fa4bfc87611bed546bbfa67522ceeae5023f22af2a923149aa5ade0d5adbc365bb16e13b534d7151bd1c869241e5fab4c111df0a3a3fbbab8a8a7ad35cc35e53b4935f8a3292b43cd384c586d133b2ca7818c228ed04b433af5b0feaee785a5e5aadd540fb142e785518f9f5cd5cdd0ee3165aafccaed106f0fb187262d2a96c8e572dc45a790a45aeac3c9ecc876f6658986f9977d7f8a0856ef5d8fdcc2ce85a49eecb6032a21ba655edb0230681e7274e6bffdfd0a99bfc7b0e9b819959187c62a5c6bbad72386172e0d1a3c01ca024a19b9197bd045512ec297c343e505e3465a9445ce5c149469ea6097b5c5d23d19a7a94b19e561856244c05f61cedd2cf9c1642b0bef9d2a089039a6c54b2aa95e1c481e7057bfa114074b001bad110d90057ba64559f2e1395b69c902d7c932c0b6f07d33f7fb7399a9bc72cb89696e3311282c39c76796e3ac1cc42c92c22fcb8c8d8878829f400d0aaac36bf84bc614281f7cfd6616884d174eeae97c503d2d2bf8e4e5954bc962866b6896389c6632b5a4197e984f1dc82fd739f03267b10bbd5d756f7e084da5aa2e9076c93fb3525bd6ebd6178331f434238ccda3161f5677c3896591aeff45cf4d0712a485eceea53cc1401ad7b3127efa8890c3dbd58a487da74e816d606030300aa3138f8b349af15c273ce5358a343a86693cf91dfcf467a1c14ae25df7ab5b0888be7ae3ea82b72fa273edf5767f0b45e7ef5da6fa05b5e7da4de77e0485bf9ab7fb3efddb783c414dd22530854895495909f9b2f115bb4428015df85b1089e14711aa071cd16817d058e1391aeb06388d313b3fdf6887aea6acc9c993d1e358193bf30d349e55239e4dd94b5f080611ec07db1e388c81e547fff912a645e03ba9d26855b301a12a7037d80cc12b952f777b4f6005764d25bb7b8c90b4b32c30969fcecb471787784242c19008d92a243175d9f7b01101ef478e1201ed781820daa21fc03cffe2a22d1067fb16fb27b4a346a2224a4edbdf796524a9992940134080108980836ae0ac2c2c969b9563e602ee8d6c630d8e66f3a1a9c4ee4f9d289397f077f41f939e79c27f3ce6c9264ce86bdd25feeb3ee49a13779a23ac2a499f97366fee4396958c0cfa05c357fce393fd4e49c7eafa37cce594923d4e9b5bf29fdf6fda5ea37439a6fe34d0f6435e7b7f8cbe827e969b4277d3fe9ab49d2a5dfbee6f4da0359c178a9feadbf6f5b9fc7cbda9e73ce39e79c73bead1d95a860489e4f3349af3d0de9b5af26497ff6d5989e94fdf6d58cfede2433f84fde8f243f4c5faaff4bde8fd197ba81f4dba7faa349d1d16b9fa219bdf6a3d44afb6dce39e79c73dec913b4459550ce4819e3e1038c67c70e367d8ee1168a67ffc4bb7ff2b4377999c8dbbee4859ee47528272fae4ca41d3b3c403b8078f4a783fc68623a480c81bb9be9118bf9e8c123f6ffffee233549a31efda2d7d130dadbbfc9fe9246ef2f83bf8cbe54e327d91f3d8dfdd1777f34f77e8d16c69e9a2434a3bf5f8dfdd1d7dc277dd892dd5f6e92f403715749e6c7dc9ecdb24bcd984ca7af5fcde84ddffdd2d3dc2f697f9f7e34f64b5ffa6ab2b7f56b485f5ad194be66f4a5d575d5cd2c50813fbb1288641b1285c4cc0de99b718545dfcd4df6f67dc858383921e9e201aac45eceee50e0cafe50e0e27e154349e55031c0fd5bdc2e073e81c3916be43f4242be57060efa74b2ffa7d32ef567f4bf9dcc28270658e6be30577f239c764931c0b3ff286754241c1d19c5efdf6827d3bf75ed5ae54fd912066583b5450eed4e86fec8e5231efff9e1c895bda5416ff95c350723147640abfcdb93239e179280474ab2ff4849bb8477e52e1a5ce51fca568843d4a3c0649467fa93edb1a73d8ad73dc9e3bee489fea6e6ea04c58b2b921757a58d0677ff95fb0e57f937eaf94650e800d3ec63e6151d60cf3e66fe86c0f3b76f081cfeccc75820b2b0106117902a9f5ed8e29d0522cb0ce0102c9f05b00996f7628c42fd83608ef7461fa979b31efd3efae987435ce16f971e38c4d5cdcdf6a6bfd1fef4fda5e63773b3f707fb4f6054ee97ea2f7583e9519ec6f4281f82a6d3bbfbcdee2d20ac1d1063fafd1f105f2c1055fe1ec1e872631836fde96364a9f667838df238c415ca9fd4d464bf7dcc3417c5ab4942b53f7d3f487fbf667bd3f72309fd1fa5cfde7a377b9ad297c51f89bcb81a79127f3737a2bfe9fe66fbec6f427fc3eda0794136222fae7660aec117b818bd688b30f82877f724fca594536c29f93177cbcf31764048306e4c810a1255c21858b9ff37299b9e00fba49d0c366db3da9f6c5ff7de2ccb3a99ed374ddbb6ad9309fdc6715c37c3cc5c27d3fdd67534c45d91a89311fd8639adcb7e1bc58e3e2389567b338db4c92e24c2a31a9ecc5bddae27f3b679326feec9bc759eccdb860a02cbdf70d763fb01ac49ee21beccb1b301a544ac058617f1c55b41c0c28ae8e23c36fe72faf8fe135f503ebec3fc3546eef84fc4ff894518892fdee32aaf421122d1c5834810c1fed087e4f84e44749936393146fbf88e136358316683b5a0fce52689e84b2f55c6188cbd7872ca5d88c35c89db4edc14794850dfd5faa11354adb5d6aff56badb5d6596bfd5aeb11b87a32c7486bc7b92769e84e4fe6cc93f97a32c72ce2dcf32cf290a07ce72139a17c0770b9ea004e1c008a00e4ec3bc04e8345c035c8e5571b5650c7b20ccafd1ba5eecdfc84c5d13ef3e2cd4460e0f08335cd28debf5c65bdef892cfdde32a2bf5fc94bf138cffa204fe48978211fc4f378632318a7c8911557832b3ad76e1911445270b89f0fd62e7549bb50cdc30d5aee87736fcbaec8d23a42a78808f62481139e48e1a15e0a4e4eca91065d4838b15d13c7659187c4f4d6fb576469d363f15644b01f871559da4811224388e8796289238a40a5e0b82a2527e5488a8ef67dceb581e2c1b2e63273b2732128b2687f3d17629145fb4d73610a51a5bd5646d65e8b4204b5d77e9240bbda4a4ed6342259fbf0354dc35a0b41b2a6fd89f7c55ca57de67d410d6a1ff23e260d6adf693fbd6f4983da53ed4ba355da6b2ddc644dc3c9e10aece7e5aa959d15d7ca122b45ace844f09f5869d93bb56ca5853fd807b3bf698f845252ac41d7d9389ddc1365144f15530145963ea9986415183bae566469d5122a245447a8745445a8605af45891a5503db1d25a71b9aaa5d3e04e834e4091d3200ae58fb32f9192d2c9681ff2621679325311121c5f874715ce7aff13592c17af85fdeb7d4e64b1d6ab60df83ee17be91dc6feddb6bb115d911c95afb27d65a6b6d4e0e577e5e3d2bad1557645939225c69adb446797a1f5083b006bfaf93b1ff7533cc8a0a82c30f66a39b010a4edbd9b2ed53b90a70448ccac882b96c8be107fae39449b6b95ee0dc2b699e8ac753ed984c27aa27544ea896f05ceb17aa8ec8fd1dfa18b743fff195d70a142b4a4ebc959d159e15241a5cd14941f15272525288443008d125a548988293c2adb0602f66557d1118385c69ddb021f2909c5ef3bee53d4e64e9d3ff91dcdf6f2482fdfd45727f3f9108bec690e29ff05079899fdc47f49fbc941c2f05c74b6935d8293a2948e4ee97f7fb1c1c40b9dba5d2899352c40068581c402cd443727aec49d40c89bc0170eea5f28683738f85dc79484e4882fceaf4e116cebd1ad93d94bc595bb5149c06bb56510df0fceaf1605d29395927b3d2120981a75d260d69cc5d7370ee99f2e6b2a2d3c9acb45e38035061d47533dc6cbf149c34fc05078fabfa7bee0bffa070c0542ed511f1e58da4e0acb452708c182962fb565a292bad149c2572b8d2729e1caeb452705c3a069032fdde0055c0e1015c26efba4ac632fdc20fca1d1a9130ca4fc9e4a820987edf12fb3931867effffc498edfbdf487c71018c0f46848891d117ba9122445fe845f811394cc149c1f960a78e01a34c3f1780ee4e4a2bbb28a0ecd6ec06ef3de20b0c8f6f7581f34404911b6604b9633f0462e8228bdc601072b7f8e47e8f8710264a20bc78f8b024f7d7dc4856bb441f2cca90ad500ec9cd630412f44f8f9fc2a696a15da80b7213c9618422f70378d824a7801b45ee2a72d3910f54f5eb2a97cd93bb7164712fc6bc68bf7a299a3bc31e3cbb7f37f48a7e51a52710ef0c678bb33d7ab48aeccdb48d83c9f136a2080887332463f4281767f449b9e6388ee324c7452d1402b9c1a974608d287038a7b748241289a44814b95027133fd387bb50cbd0ed62d7755d160a8542a04824f270c418e30d638cb778ba27addb4222aec3200871d10b678ea4de7e46245249498e7f8a27289d0c55d2205843a24e80e36fa24e66633512a9a3b754766da5a9d44ca5bc53299296912e89446a9224915a9e9821860e175028725f46557c7947344a00c9f265430427ef953166342023cb2f6cb1f111157998d8a24a0b5b609752c618d365df9d8cb689b6c0f363143dd9fef41ae708042e569053fd72069c53fd22e9f9985fbf451eca06e7a9802d0dc22cc033f570b38f1e7046892cfea788a33fec0181c3f8731b741ea411617e0513213ab1a5a7edda747a393cf0071c02c9314629bbbdf8c993eb1b32626afc4d1b12856a5109017f0e5bc0f9a9b47ce73a05cfbdf7deeb82699dc04830130cc6c2c92179f74b788e304ccbc503c3b303762fc628d43f08ca7b3146a1fe41b0f14847db8a47a39208bfb55b4bebd1c0006fd2a76eb09fbd0ef5473fbb99fb239ad8e06cb0bb991bef07fefa76f436cbe6d78c68ee93be1afca91b466f9f668479b8299ca08c4a279c73ffc4b36ff2b41f79d99f36ec752858de3bbf1fc0b7000c760118ef29d32355e7df92e8fd55447844123dcd7dd1f78bece31a2d6cc9d77ee439bdf54c5fc292256d44f74f4f73ff24fafbf5a3b1d4e380ccf3b76b5333a298bf991b7b336de3ba101544a352a896e82dc947f588c57cf8e8667f539f34baef6fe3fe083dfed17d1afbf7fb6ffdd25743fa7e84fe7efd9aecbb3f92d0841eff8fd28f42a9fed0df4fd184be547fa4cf3e84b1a3547f8a26f43990bef4fd48723ff435f6b3cf21f4a42755dca39087a9d4526062c5a8efe04c1b1c0ff298c3fce7b6e7b88be6e484de5deef21de7f11e11ceb4f1ce01ea1d9c1c7705951193990d2ce8878707c68f7097bb5c07ef47b8cb5d2d030cc86d2f71ee45792e775bef47bbd0973b274b58b7fcc7b57d992757eef217978ba777bc7b7678329999ecdde53d2597bb7cc75fb0c3fc853bc8551d7395bf07e93c67b9e7c4c28e0dc1a1bbda89767128da256c26d97dc7795c9a9badea9730a098bf6c5d1096309bc3de79dff197ff38cc63ee0255dcf526d6254d21b9f60b265ffdd3b8258be5f4db3acff4cd62b16ee3e4e46e752f67f9eba759f3eb207dd3acee59fe127219720f23cc65638f8e26a259cd6a157413d1ac662520c360f6ce47aab91ef47dd02fd5fff317e0c2a3877e691b57f96b5fba614c83067fd9be739427fa1bed4d2c7f912eff66b82fd5cf3d9015f734d973a22ffbb4cf3e99e32a7fed932c57f90359b58dbf34ebf3eae148f24ced598bc46a568cc1f117ce5d9afcf18ff225815ce5dffe2114113d24a75dc216d2acc611d22efdc5eadd06b7b9922cedb7f7972ec9d3923b2bc9f297b8c2c969b97678b2ecc12ffad077dc6f1ad8389dd3ad7635abe546b3401207a436e907d23aa055f27452328d48a1fa274f54f2ae16f2381b6a29edf7039672b6fb7ffd9bfba2917d7f8b55da258a3e34aa6f9fe6ade8f157735ff435d9d7eaee363baa05837836e800194307b15f9fc67ec5f5455f4d68e3b4ec6ffd5bedcdb48debba1db0d166c6e661a061a20ee904ca6be6e434329170f628de3de97ee4718f3dd193bc908e3e99482dd70e1a16d0cda02c00835d0085fa07c1961618180bf4b7c3dddd1d266e5f6b45655ffa9bfbdab6997c7b9a6c337d35daf783f4a5ef47921fa5df3e73984c7ad3cf493d0cc8183a884cea869393d29f7c4d7a53ca7ee9b72779a9fa299b22d5ef86d26fafc3f6251db427452e763b264cf274566c2d3aaf191d787274e0bc462f9417f79241248bf428aff4289ee947dee94fbadf3cfc5a67231d38393d3a7a66ea0793e3d5326f78bd708891c5e73713593cd6bf2fbb1ef3ef8743d7a37ea9d6486ffa9bd29f7e7ea9fe66b46c8b9d3d0de9b3efcf4abf7d35a72f7d8de9493897bc548bbcee39fb45107960f097aabfbda67daa6acf7df69bac1ff252f7b70f7de7a56ed83ef4a9fbe5b07de839eebb1eb27eedc5e9f5884d9a34b9420f99ebd7a3e7899784fe69fbb8e2e8671f57242f45bf1bfc37a28f3ab8ca5f84232c76219b99972b1483538c491b96c421ddd08967ffe4656f1279da87bcee6f8aeae8fb7a548f58cc470f1e31169c86eacf39ede3bfc97ef4fda5e83713fa9881ac5afca5fe0d859ec67ee8fb43a4af66f4c51afc4d9128f43737f7ebdb9b719dabf2b8d3fb0ec1077f912bd4ab6786a7461029e824082568245d353ced515ef6271ef7246ffb91172a79f84f9e4847d7f0e20a85729ae1e9f999f9f141870c527e20b23808a4caefc51885fa07c10f4416f721b2380f0d0dde8b310af50f8227d3d7f7fa5d3f1ce26af4cfbde6af72b36f31bdff562ac90d66e6100646c6d0413c8499210c0e7175fa526dfa52ad75b6594e76a999d39bde763d6c90b93e0e7155b2dd4ce9e4994cdf6fca1ee5abd93efb1a6ea634a1c8dea3034ddca6ff8a0e70e71bb4377d0ed9973e7633a7df7638954c2cbc300f1e82ec871168a6664e6f7a9ad39ba2d39cbce94d5f0d7655bdfe72faaa79da5b943f3d0dca9fbe9a2416659a5e7b1a9356ca3eae48a3efe6066b2f032db4a0a2e2820b2b2da87c5f0b2da8b8e0c2caca0b2f80a617e40b2fa8acb8f0c20aa85a79e1051054a96080410002b801822a150c30084000376eb0b00c60000420000c2c0260c151a3a7468f9693e76ff25e8c51a87f20f9a97670d839332701d15561e3e4c9ca01bab873624c97a78c0105e12012c855f3bfc0a1c772ee117098056553d04610b520b246c4eb68e97596f36774682451357f0757cdaf2247f103d444b58cc13f02c918fcf398c411468ca19f931159266a07587e8c3174e0664a190000207038f3e790d7ab1ea81160ff6e24f145eee420cf5622cffe4114797e8c5504b96afe9dd89b2d57cd17793bf32738479f7d3b3b248c472814fe9f2fbf99e3aaf92a25b2a019b3207f91afa8920f82934effb95f88b3a03c652ccbd772b2e6b2d8a28a5b2ae5c493ab3de4f9218df172f929dd414404153ffa9c4e3bc617fbd24a4ae7f6826d8bedb243b6b36b9d3de4f81dbdd0c403ca7efd707eaa866963635383ac61a2b20eb75e48bf739d1fbd90b3f6a7fdbcde0097e9c7039763e5c210dbe9b472d7bd9066ff9a85598e1be71e17ef45c2de3ba91137f39ff3a9b5d66d0edb19a16d3e3062e39c78ae9b761ad1c5d0e47e9b775223f0a51f3711be5c0d6e04d68488d727757b39d734ad9dbaeb7455fa5baf5f75ae93a9b4e6d0e9424174422219bc88e3ac0d1d7c4df273f9cd06fdbb9bc7ae069dd2964575794f94ca401d26ce8f76d607d4b78fd2d91082706ac0fda4b3a13efd5357833ff55396c33fdce2b306816f8091ea584083fd1a12acd260ffd33a4da5926603b555eba4d16833e1e881ec5e3aa77d5167837ffd5057c3ecac06e887efcdece5b64e0801f8bd576be2038511c07097a30f933272f83a58cbd18749189995d7874917f9de7b3f150ffcfb8141ae1ac5b028471f262f921018e7e8c384e7c11d1f262c7a0339e59453727e31973b19ff9ef3eb4254844be7bc9dccac33bb54073fe79cd3dd7dce39dddddda7bbcb3967cf39e79c73ced931aac06f81093ae79c3572fca2ec24a2e6452cf2c81de99238b1514be025b094ddb2c5007bcaef066292fb652c94414ff821876d933bec1b2161b31aa791b81fca1d27a0d882093b20920407645481d22087b2078c17e450be2416b95fae922dbb6710cbc8dd5d6389110ca115860e84b033831c4a1c2032e4911ce5fbfb0b9022c408e2ea990117341062d54680503a021a3f000da1e70541abcec9ed0421b99b0a1b3672a802c60c60004349182e08e38b1860c85a460151c1c5cbd9d74e269301461a8143540442128ae410e52fa817902bdfbfffe17c1bd3fbf9edf2a18d8c72c214d95a2ba9c0739d800343ce010efb0543d9d8d8fcc033bdeb2f0d837df19373642f02870d6b01bb2470d8b07eb54b88026a17984de774ac3b43f6c401c3e1cc12c716389caea385b858c27cd1836f540284ce0ca02081094c000595c11639424416be2f8e64fa35eeb715b46082236be67cbe5041139ac8f4513a198aad00bb61813c29c212a04c2f6a01333126a200d0032904210518558861831874e143ba1165941e02514896ac20d39fa64e865e2dd349354a29c5f43113204760106362843544092d56f4c176a13078d9104129a55d17b4d4c9d018d3d58569235441400e6fe49021cf5884f95d782105ec064a86c0aaf082065e9491e7b7c02c21499eafc3c687094590992fd809a40b9e5dec20d3177d2f6b73e4d046a892c31b5901408079fec538c8330966e4f9df2ef3c1227c9450469e1f8201819860490c709e604209a610062d82e30a1c8259ce49844c9f3e77051738b091e76f375e100182c3f7e20a042430c9f4332af8200106795631554e406325cfb74c2802abc6d71648327dfaf96c51822fcf9fb8c69e3c9550818416d7a0bcd83b993a804cb3c85415051c5e99a58f163088c17702e19b6519125c8c47244386ab8a92cc1838be8414c7a8584eac2513c3289f2c9ab09065592685205260f96d9f1f226d7ac944e107a39804f17902d0c6d336bb61fa028f72f4f149721a820f72f82ccce5e8e323c54914f096a38f8f114e6660941c7d7c60251de09ba38f0f1a274ff8f83459819214d8f004d8ccd1e70942e68ec81f097d24b19f6b1da404da66153f9bf93107d964453f9bf9a57290abf97235bf9a2438c4d50f11c425abf9f2e7671397ace48c7c1d2dcff0c9e2488e3e590cc9e1f693e5dfc8d1e708401996e54cf5648242617cf1ebe38b167ba130d7115f6e8fab821a8cb916bb5f88e947b33324c83f119420cf0f3b48a69167125804e11338fda24bca5d2fe52f73dda6d3f03ae8076f5244957c2bb27cf941307f892ba058d0ed6101c8512adf2008b6f427034c83292883e336d696d32ea1ecd95afe226de0da5838ed127d8e6093e56f45daa5cbf2b723ede292bf39d12e3d9247656365f9d3658fcecbf583046c092026624dc820276cda256c34821ca15d08c9f2e58d2247116394c850f6482e96a8e450f6c832641a32e450f6081992e50b72d845b21422877d44d36e4f4cfe2c60d321a45d2eebe2b02eebb22eebb22ece65b55c7747f63840eb64faa3413eedc2dad8201406a3e9fe66e89176a12deaa22ddaa22dda5ab976787ae88b02ae43a311635034c0a9d82090d58cf61b0c64789ad75f486318b03bc2e6c9af1e540f2aa87964f764d93f59364f960d94e5ed7195e4a1ee00d4048f1e6a7c8841597e3311633610999037e044a6f02fb524b3206d84cc2041b2fc5e22c6f86fa087376992e9f7f02669641ee2923c95b0b0c851891058c0725432042cdf9e76894a583c59fe55d22ea32cff6649ae91a39212d864d93da07f03feb3b34136c9f4db807f5f5c92fdfbfddb40c74c9764eac92192d520ab484b1c1c24728ec8d6912dd972b99048c99d2c9d6850beec2fc45942d1a0ecc952bef40dd6c208c4e022fa94313e0a85845676cebd236e245bfca812a51370e4d1a28c2c6d5ee40b6bc0e50872fca2d781698328c7772f041213ce4fd5a0e5f9f510e5f8318bdc92c3d82487ee456906962f73ec037cc6d93f544fd1af12c2941246061eed22bfc6b118a3cafd32c75f02f4094d343144674e5b6913d356da44134d58269af34999a095896a55d0b2fe76de6c3abdb63241fdda3a99b8191399c684e6ee844d25c66ced5af499514e25c6dc7b6fc41a63cad755ade2f7460e15d972a3c11cad6a15883f01a880d55616960055688e7f3146bd806064e9075562ccb5a1979537091e483484b4c8a0848b215c750613f2fc7ef101164b204308364f4c81b6e08c2372016ecb6c023d2305a106a0e08c1764b0338e740d38410f2aa725bf5541cb0f9dc5eb0b9c21812406d696984af0120345c8ab0a5a5801841578053e6814f1b1020b208831c4106230f1a080232063f422463072c728e408349d07b87d06d924f7904dea47f933c42cbf1c67e01e68ae5fe8df0109244fcf871c45557f7bb93ff7c79f8c0738bcd2864a37e30a1f3382aa30238c1214b028471f339030c40c2b8030438a520ece72f43183c78c9dead4453e41903a2a0b6cff3668b9ae2fd836aa321f6c30be777bf7f53e7747396580e9eeee48626d9bb79207906b0ec92ab68dba7b0ebbbbbba7bb7bbb7b4bd9dbcc9893a5255ca7fbe7bcfdb43d1bda861064f5bbda10823cbfce6ffadd52526b6dca0697f336604b95393c6ab080aa612325c523c6420d1b29264a472878c789a97402400038ef03f02f773a9391eecce89b7ec9853c5f46faa4918d0f7a11475b837dff908d264983c0fd5c64b1aec8aef6d5ec4a176649b9d55aab37a25ab665dab671d9368a24af0293ba1054ca17965e04a90be1046aadd35a21aa945a04433e32af4ead0393d21054dafd6c914670ce7e294390fba5ec50bc41843d0fd85377b4743bb8adc5bb858af0886ad99669dbc669d9e8dbc5f41f29904c65665e9d67a8c0fecd193bda2b3f4af9ee734e539537d32ea519fd4b6bc86e8c209d757eb68a9a7875e7f83065c420bfc096021172f790ee2c328d22d3f70bfacb05a560932311b263144c0e528c248870df7e4dfdfbd988a0ade004ab7e36f5ebd72f4a4474afc778e79c73ce9f5faabf98b37cc69311fb269d14f5938a6a4251940f0e5b5a6614a64800192820c38b3c555a482a6354c8f4afa4942e6922d32122aa4113a03097040e6fa663dc7b6d663ddc2012946709528e0f0a15364bb853f4f8c4c2c05400a1ccc8b98112041548a2686294061552e4107f11541801e5092a9840041560bc7652b0459530e565abfdb820af5f875794c09d235324cfef6e0699b9185706176423166360c8f3dd430d01b7abc1f93b5f45a2c1f9e00a3017a322cf3f0217a4041bee8b3c3fec82e43945649950607f2e274f8e08871359e68736eacb5f2a4eedf197fa135571fca5bae61492431b39e4625cd0e562ae1beeaa7e5cec052638ac3d5cec85d32e292f3ac5e6eeeed4ddbf071b7f5a1d8c78e5bbc7d9e4b9a591e71694a706b6ffc04f762fdc1ea07de16fdef6851ff0a8170291a9af79e1077e72f685fd77a6728a098cfa4c2f4001ab484a44c7566badb5def55afb5bd7b6f6adb5d65aebf6abfdb6f6adfd6badb5ed5598b56dbf2631dfda6b6dfca5da6aabfd18d69cfbf27e3344b7d7deedd65baba9ad0df7fbbd543f90dcf75a6badb59dfd81f676151f250466c51818b2ff060e016b2f9f3af52fac40e47e100c2c1d75b7dfb6ede3d578989a77eb7bc4c1aeb8d73c2d6f99f694524a35fa012c32fdc02bd31e6c32e56cec476b3f7a616db9aa6b4bebee06bba25a2d40aedfa5d85f708cc365a498c028138602963fbd5a82c8d236360537c66364b9771471dc8fafd3dc8a12586a2fedcbf6d1b99038bccaa455f7371ad30901c3eedf5b61d9cfd065edabb0c872df7ad2ab4bb89031542f228efb970991e5be7f615542beff92e19434787b22cbb55e5dd2e0aa06b9eabe8e8602fbd7564d22c6c87ce9dfaa936f75e55b6d3e078573bb06b93ec043a022df8ff2c3ca837c81f4b041e6fe66885963c0ccbc78e3c5f9c64df3c29b6d28b3b5efdd0c5df61525b0e710dba0bdbff6aec974eade1acb97fbb0c2b4abbd8eaeb00681ea9206efbf10052cef4795fb69a0cbc1c2dd7d6a9df9ad5c8cd165e853b7d5ceeb998a3bf5d9da94eeee1e5ac256b37b83fe32c6183bc6d831c6ee6e4f66dadd928b5c2836186b185c6fd2a5b774a44b6fe94897aed54047baf4968e74e92d1de9d25b3a9352a1a533694b47baf4960e778472759b5eed9dad9bcd56a66d736a27c073b6366eb6b82e345b21d19cb3150a8946a4e9530042cbbd1de66ce1d19cad1149141263a31cf5a93980861e9af1d05badcdd030c2de9aa3a5856af34683a0b6f9cbdcdf381bfd9271f7e39bb26fc795c14015f08e2aeb8777701b28142777d881817bb96ed3b2cbc53415cc215e34cc7740bb8cf2fc4d443faad8f88b800448fcbd9349cd4457651adcdffd7e1ce2aabf1cfc43b9aafefd5233f55338c455fd6a92d0d4bf6f5faeeedf97ab7bf32309cdfdfe1f3feed72833ec07e3aaf939c6c071b5f26f7ff939f447e7e209436c7c07db165bca96b9bb5b4623883e3aa3286eed327ac87e9e8154d92cd86e8c313611b6a8b28afe42eb8dfdf71fa6857a34e8d061c6552d575d0eb06c19d4252363503106114c381be3892c479f3176e68f87aa3a994c629357d4a05e6184c43e6318c938479f3188a049bce56fed75a06b9836365b64f1cf220eb7c9f3f351bf879bb3c8323d99feb2083a951df564626eaf039deb6fb63b084cdad243f3ebe1b97e37b24c4f265503cdf3a994a139bcd9b9dc576c5165c331057e4924ba4490fe266d247d19440ac18931b67f071d19885f477ffbc21b37b22816a3aff96b9aa6b956230f5c9e3372b35b02c0042819c3fc78481cf4f311599ae3e19e1c12a4419690eb2e8b642211a44f631227b250151378aa7a503f56fa6940e65c1ba082d488a24614a820a820ff9ef7059f17a8848c785541eb18a21100000000c315002028100c870382d16014864980423f14800c6f944a6c5a4295c6a320876118c520630c328410400021c6101a1a1a0e089da9041ef4f0b11c378a2eba35e8a898251c3387f9a0d07894f8585cde44c5b1be07af24067f6ce8160385cc199e2b92f045a2eea1d788a571472de67038f2e244516cb0ca1a8c0075ef7fa121af0f4686461978883f334ec869c4ce40ba22f401891c11316f1de8cde3481d38b010a7410b1486fa2704cd6196445c3c911f8e4e8cff3067a33e2178360ee3450bb68c1b8727903c0f99262f9390a02ec42dbc327a6e55ec74264e9edbcc9db2c4a45d2d83534c59e3843076fb2b2d6f7720dc2d63fc8da0f4994afa1c4a7ba2d6d08f0212e5112e5c5240df66fec702e6ec603c27f04cd10218ee85fba7567f8e026880ddf55e21734ea8779e102f12d394177b66aa73293b9b9700dbf5882703f2f78155716fa40a776da20d5b1e964849e1ab198b1d4517c5ed7273b65f8e0baae20275d1505bf7af636e25c793c3852e2f42e2e54851a62a597860b3d72d0c7912f8a9f1beddb81d6516cb184d0afd8fe47fffc2c59a18bf153fd00b019b7dffc175e40e62f13767c48cab2330827a911eb8d31814a6409bf7813b7a3f139f31398cbdb7f30bdee053c6186f4200c11d55a86b9a0565b54190b8d2c5088deb1993a0a5b08692ff78cc854c999f996446c132b684cce061d43e59b5491389f1c7c78b476485eb0f4c0db619d238f406d1a3821c81fff40f207478738bf73fcad83554cb5bdd83d28ba4dec86e6939169c836ec0cd8e6036071e1e5eea37851724a68332441c7584579ca627da682eafe79088004eb68d5bec6502f5af0fd9555284ba8a449a00a45b4550197a56afbb0b7e6575c2f25cb1892b5a04bd0e593764b9c3e7909d63c2295f2c40d27a03699104fb1e4b14b3d7f7985f9d10e6c0912b98f3e4c1e670c35ae8980f9eb0a3871942d32cd0682a59c2100b174129d80229106ee91a07db508ac07a94159bb951eb0ae932a29c0b4cb0ba533a5bba363ab30cbddce518d56a3ef9e7e77eea491958fb6095f9380c118733687f4ffab536a2c37a8768aae265e74e8d5421a49d0dfee4a524cb8038cfc23b723e62f8b9a0329dcebed36743d48f81b8672b3254c3e504457327f199e3476fd3314dd6fcca7032262729704974998a82e84837621c8534a2a6fe8bc06f4bd8e206bbcddf8bdab6a3b1d3a3b1d7e70e42dae8c5175d732325e40d265469f242d1c8edf34472e70cc278f9882c8ed8c62c335d092f270abeb6d5355cbb7229b021e84f4b577f87eb7e67c8c85ed0fc938122ae1e0265929d077466ab9e04831ebf953fe3b744feb9f4808d298a65cc04fdc92fed08a0f1e1d2c28d75d6679bf361a9db44459a452982e49310010f890438049ae8ea17dafcf2c85371f8c38b1838fcd48b66832f3858cf24e51614caae0e3204bc0f407604e8bea90ba34a17728c062412b66384464dc43d094655764127228ff2f86995c6d3f269b066f129c688012092a366c4fa0892ab321291bc1d99322a648337fd80db081c2e30de314c9b89f58093d0f676f9034f2286e9832149aa91c053192be6625b650aed1e8934452787538114331b568184d6de30a7796b98fd9ee71293494210ddbb471030301197cae9ca59c91982b999f157705c125bfa66922bff9b85e8cf89bc0e66462a36941aceca474f3c131d86fcf8d855e315764c114264b65e135cef488ef16b8e4e014ebd024529c8577c55b1fa4db94be3d39291ea91140d65c34e817349a6c65d7bc70f247c9751a1f8c529db6d06b2df2b5fa0dbb0db69b013b7e135239336adaeee97d98e099c14767481b76f9bf5bfe5c21ab8359d6e6dfd73cf6a341e4030bda34d05e734d24965dd57e1fe0e17278f98e0d9feb2088364d15207d00d3d709a28fec0c60b105a347e9ceff83dfcc83466f14c617db964bb7889450abfb41742bf4d3b479aa7a08718676a05ea2fdd9a49aa0b6c866974aeb922c220121b3b20ff2bdae0ef6c512cc20bdae1c7f49dd488c1cd1b1acf4c3843550cc503f739df9bc7709ab3d017477c92543bb4743e262da49064b9f53fb069a373dde459428e5f92444f1859109e489fe2f7de61525272663aaf94e96fc21bcd094a25ea25c2a8b8e41ead722a1a1089d7519724821b2439fcb266c11bac063a2a4560c06e0bdcac83a78947cf1c7e3b4fc671f32d33ccd8a3f4e4608f9213bf6f55c642060512d2edb0859f40c29c8f74e202b18b52aa62099466005c339069d3a9b00ca7572471ef4f63b5e7f4593e091658b5d2306f07fd8183360d63b6e1aab7e4ddc1b06e25d950e0d3e0104b08cbdd2db6845c28d78751778c70f7844ab178f9141ba7fc8d3c1fd51feaa2c55bec2547f8d8171868fc4b60a03140974ffa38868ce2a380126a02f107108414fdeb81fcc562674260a9af9f0c11792f6688347132b0309e7a4418d59240d776630cd10b93a0c92e0d32c463ad63951ef6db0432397483e1c1278d46537c92129eb5cddf9ba1e1d23a278646eaa4e86a33564ed3c6d448e48ed5f8fb176e50fff872a7c2bbbbcf630c01a8c4ad209536ac957b7114b4eee78ba30f4b4c6efdfbf90142242c9d13d0c6bf8f468b93790766bdd38c99b37ad40e8dd74617fca4098dada6832d288f2509ba935f5aa809cd482461b6e673d40ad09c4463df96338193f2c68ff60eb0eaf8399db5be120674b5b4306f665fd0ac4c6cf023c5523e18a0125f1bbc36d195b8e298c0105f2fa61ef719091b3f2495ac6440350270c078568b8da7069a263a4d8df446dcf54dac9fc73328f9a2ed805d3bb7e8913cd6fb0a320b85d52c4526e8faeb940090f4c68856021eb113fbb3759c8312b35a770d771e447d9cf587ac27cbda1038ae5677e4618942294ffc80d5b4002aa08e9474787d8b4cbe05f3d76068f9276a3996d98d927c98af16117bc5be108102b4026022348787d7357b1e71989f2a781fe72b42590280b95e64ff95fe1083ae765698b1fd0a653708ee6b124f1d42f27faa1ed033ba9c6d949a6acbb93e280dc48e1841a07cc2eae74ff9eed0634e0fabd354f78e64b242f316b832625dedef8d0a0edd258c7596a3b3c3dbe91d2566008f02e11d0f085104526e793315b0e38d4fb1fa4aaf2369e66795b558ee3b37b67112361a502fa5a58dba178b2a25dd84c366b1ae51e73339986e21a0e6ec2d24c1e98c813745bc9c0e6bcd641d7f32003b2fed6693dce51205e0b003376c5b2b7d3e558bfeb2115c9be88cb08aa98df22baf49321cc1f8425611a662b811f609da247d37945c99e12c25850429575617dfc82fcba03e7e0639b95e5b7566cfb4ebb794b9323b537bcaabc80b693083a683f59f3f23f8d67a44d100c661513fceb43148d14922e180148366696049dd66a32d05523ee6511a07cb544143c33d5c2d2aecc7a14b21cc600af41d9e708e070011276ab4fb063a8a8628198760110269d52ca7a60f45482a59cffd7176816fee2a08c12fbb1ebba949d06dd3522fb3181a3c60947bd7f20748240bf3fe859183f931d71dbafb43b60316c9af05ccbcba94ee00a492d36c3991e8142d2818335bddb0c14349dd9cf47b2e7f7fd4458e0ffeee60c568febaef4f46fc374181473102d15f49462f81c85f39b558b74e945841a349c8188d7243e9d1d7a6a54391db6ff25e3dcd0be8a5b36889f91db4ec9d99622ca1a7b930f4a5d0bbeecc5450269e0672a5b0874fbab9e31cce7f9b2e9e1482acb20d42f6f447228f55e2f3357eeadf21a615d1741c5595109c393645411e6ce39473eb3c9aee4b01dfb09d2f31693d452ee7d1f84b65fb5751749026d29ab26316363c5d4a724c5ab399fbc25b6e4f1968a1481fdf5a0b7ff436b537d61aaec2dd8689a7163716b15e89b80c55a75aecec97aba070c31b5499f3ad802a559b1f4065953f91d5b5c34d19ca553c392ad5b1ad24ead5a7b817da86a48df6804145f1d4f4cee1248403528a62e13e097c0b5e46dc823770ba57b407dc8808af8e5f854b1de4d249e20b057f60d6152789f09abf239ae8f52c788725574e3de420d0f8397c7bbfdc7823f08c229897a9ab0f5af4e08a4ff4ebd3becbf08f2326bc55107e0181d427d65f73f4946700e8bc5addd54f5292eab1d1eb2b0040b2f94aede97855d4ca27e46aacad632baeb61edd52c3d96d1ea222c7890a0ead58554b3694a4cca8a08f6769a5011237b4260971e7a9ec8bcf287194db9834baa2899024d14167912e9538b164fb32029908026e400c418cdaae8d2e91bc5936c6c304604a5aaf24395145e1ffa49a2e1367dbf734347a2934ac6911ba8b4f13638ce130ce35b92b4d4ed34b80fee7d0326985d9ebf95e7ee52319e1c4216e1c73b8f6ca237108118ed764001976c3fba972f6592d2b5920603fbe93eba0ea9795304ccca5766ab1db4f829a9100b80fb23d8d9b37230ab66dd6b6bc2f05dc89f4b0ffc6854542d624c973e55a6a755a5ce73344969a0244c16d2eca67702017608f5e2671d7bf4b699fa6cd0aae9e4e9bb4b558f60a96f8f111b4f15a6b47a950f74b827abf3062fa544a3068b83092e07ef9fbd0a3a842cf9f18dc730b69cbd19060a9a965575554a988f997b2bc14a646510b200c70342cf6a7dca9e7fef81b2acb8e15e15c1f4fa0c783be3d88229a266530d78052bb1bf463c894cb75fb8e6c1e633e8d61924ac6a73d6521514595ba9a20a07714bad6c915ca5506740dd821fefc484e100d44d05d74b6aeb06f7ed7bd22c68d4cc3cf86b49f9b845d713c22e4e842b78d25645a0fb231c7cd8b5f7cd8127a7edfaa4f7c53049e4a4f5e50bff9dd57dcb62b809fd19945b0a59b26e04f4b6e126b1fd8e8f8b4261be4a503ddc47ff4cede3701225b687d4eb5c24bf34549d40c0d08d3cec7fb7ca80f1feeb2deffd0ad47404cddab88dc2b57d166fe091d9bee02be11a5e44155e94a269057ee844c1f2e5fe1cd2b809be16e0373839b6cbd9f040e11b2a5f8a3190b01cb1d55a08f2b0b01051ca7118310cfa249b7f8309fe566b8937f54768b392eb18479d00ad1dce146aa35862bc146fbeecf1945448df253102d7589c343f1cca74c9f553a41cc054aa6ad5d677d1782a84552e14cb383a9d17782f28dd31129d342e4ba60eebc88f8943364375fa2b2489a803608cf6f1fdda644cbd0bd88b979231e481c8680640e856b039de7b0ff2249caeed5ee0e676858568a4a9784ca5963b108d84680c83c433d6a42811aa6295bbaab2b930d2ba3a40cc8f7fb8a9bb0e69dd4f2abb3c4d21f1e83d4098338ddbab0ca37aba89dbfae3492956a10942b076d7c9f06c4ca834a330e41e1d1edb5887268b1646e256633b776edb009e974aaf663d60b33d5ad49695abaf24056192192cdb24396c41f6a4d9d44b9f5f8b3d60878c873e4abbe1f92d692067c348b8289ff44cc37614dd1ef362154bbc7ab3021042a1b519fb2a0304357baf02dc67aaa3acba45f0dc521dd743760f430e76e077f8c56c5ca3521357e06849ac58932c9cc598f593f5e63bbe59e459545abd004e804e1430c81902790f088709392a3204ac54b12a949debc04a50bc6767146466086559f41742fd9634719fdbda8b3cee71d2c1434e77864100a2a9e84778e37c01648031ace131d82893cd346c4791aa6a13a76e09c424d970ace2ad0212e7137996e85c5c53564268b1564929cd047a062179713f460a0c79a7d2eef958e23d0a6e0ed543c1e0232c70cc9ed39be036c334fc2288acc535a702bae717a8abec87f0112d23f4fb12d14a877b526be2150d49e4e09d73b4f1f03bc31902bb34600405d6db2c22c258033953d8381d2e4bc656002fae6d0cba800d9305b86266ac0ee419471d0d269e9744ef04bac18766b4b2d1781bfaac8e92ca4aff2a1e384de8485d10eb07916b5be409b034df714400b526fd27ff76eed7fcc66bd9039cfcac6a104e6bc2bf37023b1861f9944fe076b08551bb88e14eb63a9f2e5928ab8d250944743f2f322b97d15a9e849a63fc17b0e2f15ad175f97c5b746cdb39164c812ef6240a602edba2387afffb8c3009568d0375d6b2c68766f0c592b5682611b8c50eaee35e5a43a40a770ad6bee8d3508280bc948e886b8c9dac93e9656cd80dab46d0efc2d458a21dc5f9b966d68f4c362e273f91660209b54ee8a65003e32d2f3155e1268b8da5395bcb43b0bd17dba0c3cb3da1d36ccb70d3e125f2d462744c9cf12416506e4c676f99b4f3a06efef16498962b5f486350600a4cd2a062b95c072dac6b32120efe5e3638055dfbb5cd3e8f2e2f7fff39839a2bdd7b062d2cbe8f77e39ec14a2df892b9d8952bf6f7e6a36e83a77b3321344cdfb1485d403deed6e2035f5b9dc7345203050370bbc9e7126bdc66e38603edbc57fd1590d47633c11ab466e8d192af259a313839253b0f08500aaa965b8c46df54fbb81ce6164e5daacaedd3053716a5407cc8123770ba6e415ecc90f94268afedf6fd6b574ce58cc6196babf0d9d6d74aef7785e08d9605eb794ab15c143e8bdb2602b4dd9fbf4de450974148d6e378e4aafd0f64ecc0fcea23a3d13937687a17364777fae2e870e033302ebb2a5278898a74fc9e2e4e29598381acd98e57311a3aa56350394c2a881194d0d43cce2c376fbdfc046175dcca98a018c5bf62a19a5e80ff63f36854b649873a7464c3763da9fb860026b89bd0db5e16a60e30ce83819a4a5aab43ae53bdf4d0273d5f9f4e6cfcbaf9e722137b9ea9287f020b36c0f2563700c9f4b82bedec2238b2aea0681ecd4584170895f4c8cd67a05cfcbafaf7e50576bef2004e3ecaa08cf12397c611806b4b139fdde225c129de369cbc3e72228cdc89d3829004f0d06c7c9138b84df496c40e9f6d910c654c9ab464661b19cc807bff09b0ea2e5f986ab06d8924a075358ea9791d5897ef2045857f9234bd4ad5e36d35132e51ac61d4757bebd54d8263848b24eeb02efaecbfbff72cfcaf80c7c3f2852e5f1fb6acfa93526d39bf0c0310deeaafc49e50eb36db714ec1a1886e70a331417106b18a8405b8fb82b2a1fa0dbc369caca8934b41eefc3dfee01c2a827bde2a3e9120a21d38ce39af42fb38b1a331e01e29dfab3006d03100716a69cdfc3ef0afbeb70e5d94970aa8f0b876ab487dab83d22387e762aaa091f125bc4a2b89c7753553d41cc281559a447c3c4715397044c560035f277402eb425b748f2c85eee310aac9e604db7cea08bd4b3f73ee426bcb0608994bf069dcd83da7f8a3a2395eb11bfef7b6d138b1f8a798eae0cfb75ed43e01c6adde41f7a0be6bf135bc11b704f9a1ffa948e6ed4144ce8a66a4d843b1334f2fbab9af63dff7d855d2b2d7861602d9320259da865163ec675bde9202395961524dc1dd18fe90a53e2683bb74e07f5c3b557f1958ab2b4454e906446f4047fcad0fb80f0b8050ad312f4bad16b80698eb097f8a019d8f49337f89fc46b1337b4853bf668f1e36c8059b131c143c9ee6759040df5890c259e5a477a5c1b83e7f28f8cc8970757da6ebb1216b7a681793ce6e2d2fa6e760e428f8b27faed2895d427a9f162cea3dc0dfaff481ed7f323c250a06ecc861a33eb34000512c03c95b447269161995475ea9064149f25466d189ae0fe53d203dacec4ef5158f72c922ccc84a66af5127db30874dafc6d7f7a4b557aadace77808519db1a2f1520fc7f4499db4c5cc923ef3c6d9c0f5a7053fafeb074fa75f485b2160b49cf930515c7c00947b441ce1c8d32878f1d087b40748788cf32bd8221470eb9e4205c09285b18b49f223f10f7a8ac3a44c331fdd591c01d21aa28d1be1052698a84714b701f093d81e18744538b64c34dd335be27b520242e5e38bafbe4b7f62533c595adbd35fec3927386745a6b1788dca53f5ca64b206d77e252a3147bfaaa962cb349b1ac9c3d165613c656179216db8860c17d295f8731febe316ae58e71f4e4b5ee88396bb45dc8713b2560d4553ef917311c49726e13dafc2b2960a5624410d2911b7b7a597a58f5ad2774d771fec7f639c2c52634c920d61e01036f076739ddd8d078d548ed87a68ab741f9e53e517773f4de821516477369f32ae2876432fde88e2d7083dda3cb08462578c99a7f2a46b0e2d2970c063f9f7a334e9e106a43c406c0a8115afef6c51b5db3327ef756fdd54c7ea900e298a141e64cfeff24ccf6752d21f9ebc9dc619a3690e8f1494b05e40c3fd07b7d08af6319358695c14f62cf450328610c73eecf72860fe08b102757f70c73e75bd59a4b701bb31aa060cf964f2341c8c4f20ed4d5ac4feda2191a151ddcbad56a199fca0c8523da5a74b373bbb465451c49c61266d423309ae5ae78e60983ea3470b2f081b33cc843e3427e65a230382f94b7828fab481e4993bab2cd2cd6e0f3e921bfe56e27534623b38bcaee77e7b33e7c51b92670d6686663f11f749a64f727e82f34dc926ebefab5a7f189ba06b0eb227f549c953e643b1a7d25d6932bc032a8b4d69293afe12020017b2f6ee8401efe6dffbb7d4bdae39a9f7e95ee5756831bbcb037c58d2ff69af1325bd2ec0b0a99454ac243361e824bd92f7b36f090fc5a1ffa739bd418361194134f869585eef780b4e142d58b78b0c4186a0c57ad32d74fac9cba35adcabe0e3133d5f6fc640308e268c6264b091afae7220a51ff57139acec8964c105872855e6024f2fa36eaf41c19c853f2adcf2ceb9142c1967f21b7fc3f4316e42040029e27220569307c0633522cd372ceafd1cfd04f2014979e60183f81c4d0ab75c94952f4ba4b98215ae0864e1ba134db8f3daa074a788f6ce23fea4fc4b434e26f691622d48371937322ca9756ced6d746f7ef179f5a6030654e0d36f6c8f614e528edc68cc383645e513494361a798fe88f912b795490886670f9bc039ddadee0b231bd8069163d8a0ba810c948cde316277e80aa8c590d37f33bcb612795735e81cc66093110be62ac07643d37ed2b153b01bbfd59343867c58db253bb7dbf320b79d8eb333280752be3be0e8127d5c7bf34f61b9271d218a2095ff9aa6ad6bb135817a51acf69c0e352dae4f7271ddc974c90491b31a82d13ecf4ecf35b299898ed291f9af45e57ff25631398e8de5b8d38280fda25874b6cb55d0be224fd655694f40b6656378ae54b17668f10786c1ebb5e53c56f3731dabe9fbdbf297ac492c347316a764386e16e7e37ba2651c6cb93c810c2c3c1c3639ccc05f44dad9920297be32d686e0b346eef8ad1f858b891b1547ac5e69d3bf95f83f354f76886122551bee4d93e6de782ad190398e516a9daaf4bb52c4a7f95771f9fc05c72cea445588182970a5a5d0120c2e11a457cbe60f4235806a561575ed0084a0391d7a904274fb6f9b3d9de4456470a19a637d7ff47a2d221843544a538c395cf00628bf703504fef7096135ae0a22fcdb03c9496063fec5be03d5c3d885ec32af7f0825baf9cb2696b1005194ef1d786f51884fb03d7ab7078c36136045451d167246bdc406b35c1f29a14ae052e42c925a3083a2b1edc41d6bdb598ead0c2affbb16e61fbec844ecfe509d4e07cc313b434355479574e28660b432748146128d5dfa8fcd4500e4fc860e723d83d3efad806788c30d10a9229006b985d936920784806db2f89a03b0bde20782ed49257896a919629d8e9cca7570edbb3b01792f303035e124a6d5533b3d36ce925b33034b28b38a5ec373813c7ab2777d5f5c4ee39106ddbff221b69ada283625c029f9acea285c1d8696886941398805e34724e39032c813580763a921f64b51075b7a0e7cc8476b1fcc9d437e6baeaf5d213b57509fcc0882766318970f75174b99d26642ec2ec28b1c0e11fe9f3316bdde194ee12fbc72726325a1a76c70220730c9fc487c9b1b56df410ea5fa82647c24664dabc854ae69198f95f25c899f0826511251034b1ea4f886ccf317a8736553c4b8b0b0c4c5c069158f81e1739f6403d97ec029669c4119739b7e542407417ed6406140ea913684ba02922472c1645b3f08b82e380dbda77293a21e353aef67bfbf40b0298951eede4010314d31c5177151ae14a7c774c29dedc5ce41ab78ef91bb705aa1cf1380c05efdcd91752b599c5a3dc51e65984f649366f217d494d9a15d9e768245e7bc874aa7c3a184ae3a4caa11086c0a4e79bd41f96fa3bfb5e93b38314a3e6566157c17b0fd64208b02e32bbd0972d1aca2f2d334c163bb4b2f83a52b6950396ca9f27d36069d1e05c639b6aba73e7f2b155700af1cba73a7c400fb205c23690614d53fa818ffe435cda30489f64054a1835cc330699269d8b40182d1fe66de7e89476515a92b0ebe47bede836fd0e0013f4a9e709251fd141b33e2bb498e8f4792437d14b33b0272768f835eaf8c31488ea17b7b040dfcc483c72d28938c48597a7c393b2de90c070c1d871d08ef4c7b68438cb652550d4ed55a0949553943d8e0f8e0ae51559ed7b3f754d28729606812ed739da85e08ba3950fdc64d741f486703a5b06599e87023e5726b6ed80658b75a29665a103c4afcfe57b222637bb1f10f36a331f282b8dc9f3357828e45e3a3b4feb389a6e834ba487b348e5c2439198349a5fc1504c0aeabf4079d6ed2642be611dd1397aff84b6575fff2bb4584fab9e25549c6e3bc217fa2073a27ed342053bb84f36c02c776d501cd8d76d3e827260a547af6aeb4ddcecc03bfb7e16a6bf6252753447757ea44e08368ec90720dacf685ab335eb6875f64c8cffd392e0c2f7542e005ce50f40957fac43667f9bbe4fdc6368a68a86612a5473ab4bb1c78b586359fc297a40828681209c1c13a6ae8a18bda3a79f389066fbb5402ed027cb5336b937d6c4a174774ed4052b94254df859e42dfc9b1d7147533fd5c3a18245bcd539253458f48be05149ffe7a394d0ce6ea20cc9360427196f60b523e3cdd1f86fd50216c03a6c3511468558cd5375d089ab53191145a82584af0250cd643ea5a786c12581928ecc5191b1889210c75c82445b61644f549f9a82bc1c0a846390d62ecd31fd59cd1c9d6d7d74a4eec47c2e19168027c5d3740c80b9af648bd269770a24f33ae0b208cd8e32c054c42fb7d1efad27cc7804512a2054a3b56ea1763bf840bbfa1b1c6ee2f4a9adf1ce96228d9734ffce18fcca96fa62c229a9fff26b015fbcda589b437126d3ab324a6d023198b4a55fcd6546c6a3a7d0a56cfe8e79b7b1d2920a47947c0912e1c62665ffbe07856a9bb83b1fc057bf11ca8a4808300fb03c72406a2c72715dfe850e0d39b84f054b831a770b2f334a81aa3a308807f19b1497a819532d0ac1f2c01e28e3c959b4c2aa8c513e898bab069cccebcb51a631a45714c52a638eab5d894a52da290a784ed4d20943a7c1745d440ce42696a10f57e7664699d17e61c81ba4992531793638587b93ed58e8d3b3b0cad3bbc8c31feb2d0b3066e63547ed042e440007a10ae07144aa0e2460331941bd60ea6c06258e99030db27eec35caad4177950e761ceb062d9684869c9bc51a931ab0fdc0c5d4cef18a9ab58759bd31ab9e5a3f304f1ff3ea122c86920fb88660f6fd20fdc5d11ba986173f904b78acd7010703e80269dddc576347c4e25c6190512c84403cafb52518703ba2307a15d1230b5c9e9837cbb52f99944cb3a26ceab649813021a48d60b23b5fc4d7e18cee11e008fe3f485d9d3cc0d8e871541382829f7e6733b064ad07b5da4ff8f11f43b46f4f8d5ba895ef26127c05358a037060516f1e0124b99b3028bd8e40e0f066a62d9246850f0a74395dddaa9dad769848fb04e41c2ce94f759800156a34d29fe73f3e1013e18b1da3848f3c64c9710de305e6341b735e450a74b9ea8d840681fec408e83e1c43fec99a9b64cbd244929cdeefe1bd9f884fd76f2b6da8636e150207be0a86742f19b8a3daf198913052c7ad6365490495b990678951f3f269d68c7b4b152c43864748f118763706f578bc31e56548a31e69d31aa4c89975fce79bca05a79e3477a73898ae475dffc503852f0c19f9e1c83505ef900948290b343b27c0779ef11d608df20e3577c380fe9a865dc72e08389698655357df490509a8cc184f5caa493abe039afb57c35e82030838b417f56abf4932fe1250027800407184da5229d65cfe617a27544af41145491c288f8f15f437d0b5c6a16276640ba84b505c705a4599a915e2468a78eab760664e5986008c49a7954bc341f219d02de98ceaeb2710a1004e7b877b88d2a0d8d614a30696e8e8753498bf1aa763ca3478d9941d94b860a95a408a3f244481a70f526a89b0787891444ecf6dee0c5ea1392282008107f116b8aba8a4a8f5099f0649376b46349831a5ab9fd813c3ef8ca27d47302db4da6094ed7e1107f6f4afb2724e1c424347916b0e93a357359bfbf6411e8eec81295a47c21198e3d319b4e43a97ad5e6033cdf667d0558aa1772b0f57b915f38a531a8e59b010526103461b15f6e8826073a30dc3c8e1c9a3634188104bb40724645ac125d88fef091080a91dfd67c46a6efa6c10743d8ff22104da195f8b96a7a66c8c87f854592271c90a729c92dda5d07c70211346c329cf7293304f03b7ddb9ea9dd94025a33dc59a67298ef05a4eccbe73da2af544404788612efca1c8c78ac11d32e6212be4470b4b96e193ce90756c29108a8b739456c4a82f83640331ad4f9daacde31c884815cd0be88115fe5c5da25b2f2e4b01d36f351644f2038916654c4474651c025bfc4b7d4962098091bcd64cbd52e394cfd6e5c32c34879df39538808266aaf0de8559ec0362d47383c4003ceca68c9730f9773aabd7a5a7b365a273566689c441c92fd0757befb09e80be057132bd1af0884d1765c8a262aa603a158849a3d88546387733391a36a19cc892ad0f962dae290bf970e74f7a31dc3ec9ac0f8ccb5928e904ea86449f5b67d3e098f8b1bff741734937916a6252de992205cc3717896ba67362e604613a58a03c935e82f87b04f7ff07722a84ff29c8d8eb61e7f7443385940980e0d683bbe097880047cac981bc0619889a6a2d5add62112a4e739b02aad14477046e870b09ecafb7b7b96a9132408cf9741f7ad8b95aebab8199a37a33f1482d6bcf02bdd0841acbc79b3e9be5d04c5650ecb50276a8f3a34c787166a022b6327bc4f76c4a87a7c10d7ccdcd292cab63366262941e70fe719673c6a2e0499300b2c9c9f519c3aaa56966cab0c0e53b0491329cfb21a23d965a81ae20023e1394466cc5ec6c14ab38897068ca89b3d6027388dc57f216c2644dd1c14275c49b28a02e7482a8e9e14500ffb60cb7d4ba802a0f24b7b5e451444d4bc95ec332e8812daf699bae6d6c0f531d5a204c42aee95c66d5ed62445d370a9c9f4a704d2d410a2e487ca8afd345219408550018c60a3c0a9e2118bced5feeedbf958d7ff674ac479ecefffedaf2a63dcbf2194c480ddf8d73cabcb0b8175368bf26300e08ceb223927e70951868ef1cf2cf7b67be3d9aed6222d7302a89089d19153307c72340e9bec6649f01f7b655536a1448e834438d347d399b3bd63afc0ac184e556591612a1662b7f7026d15a92692d2d1ef2bcdd198de13a50450be52f9ae3131b3a44052457b35cac0778020e4bb6fbec0277930de5e3bdfbee1d1d16437734879e4bdf2f7ee0d63d67a5faec62049fa17daa6e1eb2f83ef410280eee61a8b4414b8df636347cd8b6a0d28b7a163ee1dd601d24d2aa5d1588532a876ecde0c9efc52610acb197e5786351ce14c3117d8ec3e04a6e24bc91ea47d7241a7ae4ba4fb7a520988944ef2b628372bd52550913d6c016e12896766c3216b991945dba0737e0e29e0b8ef0ad25804928e6dd0cb6baa97b4ee1c35a8ad620a236a50db6ecc1491e3f1b7b3ccbb7ad38de70fbb9a35549667e19d13e4053f27f352cdf562435a7f8122a27f0a398d0467288f092e04b991b645525ad3c9f4bce3c2d3098689fe657c7a03050c3d8d3a3ce913356437313e3c47a5804460665120e5ce7a737812af0b5228b55fdb09272feaa2672b3aecde0fa24dd2796781bb51787d8dc28b508a436c56365cde8f26479c6746e69d2ccae69210604660f70d4dc39ac375b91b0c43a5e9832ce5bec7c59eea51b75131a20ee26792b959017563586f5725f2b1fe9b032e15939c1db83852d7ef7d17e939b71251942fa620978a75746f310d263c24c7924514072550fab25237b0c250d86ffd0dc064329dbc09d3c09bb896a0a4b85119b7cb9fb93fedacff5c6c402627643a2ff36562a1569363a7953ea5d3fffefd5f9775c56904e2a6a2fa08be84e3a0f82c1e263c2ce5a7e29b6ad16b939f7208ffd3819d26a7db1b8c60e36c9d6ddcc43e920b1065f062646e07fd59587759cafc7d644b42ab69d6993a24d858a02801d07741ec5435108e9cd3eb0f3a6e2ac633fd7181cfc08d036482c2d5dc835e431aedf2555773972459ead947b2f245c2e138514e3f41d8aa6619c00214a04dc7c1726e8d919c265f12e7541b0eb5f83460931f71aa6f70f9b83c8db467cb409d5b8715ba658d6a7c11fa7005f7f23c10439ec18e394b792c4a3b0d150e1ddb0b2d44eea0c0d91fe97d2e410268c39aef7a7f31d4d4b41b4233105bdff45b6f97d2fade2f8033e5fe263d5651898d656cdc1f6f729dbf5b912787444acfe10b1d8d88049ad68382fb97da08daccea79a488be49f6ff95595fab8c248556fdf000d6c03fc732fd9a2ffc658b4937238370f56b36098d3723d3bd636d040ec31e6524f45524e0f020c2aef211f68305ac637febd04841961a60e0e035472ca522b963881b17db0faecb554673047eed31d668afd105333cded3f88a6a2af86eb7f8d7312a9992d923c72c2ff331cc9e4201ee4edf9222932c206819ead6811051a9a346ac10bf7bf04ef8072afb128d069e5c37d71f1e994e7fe59d83c4c7c420cc775f6f18bed1a299985d5cddf20a5e81ad59f3248e466aef4baae16909f60e1d2f958e2f10b7dfae187cd02bb0a2bfd5230b007c76a283a3500309889ee3df3150711083f7bcee501dfe291308224b3ceb1b1d8cd3313d0b66f1a4a01c98f2974318e680ae75b93ad6faeb16b0423d4b3604e145fb48ab9f1c01a7a210a54e758adb5ff2a5381aeeb16f9c238e8bcc92769861d2833532a3292161a4d99e008eab9d9dc2106e39c98ac27f80c341d5b293947f2edcca2bccf169c96f2e713812684874a16c32a21b84d9d7412f48d72c848ad66473e2d897e9d1a79a350f667193fc7f3127f63fc4d85750c6be3ba7b5b54535c1f765ad2603987436f66591f4e54089e59e35a2072ed44c4cd2bab8a3cba19a6df2d80f8a68e414c289a8e5fc9b9f4261d4983f9a8a5962e874cafead89d1f0478911c2baef1bd3ec6dc4e2e03387079537f2e382367733d9210b963abd50ffd27eab206b536f9300ecc4fb73c656b210cc4f0450e8d260baaa741d7e1e28dedde594431f5da8873919ead0c259d1f15d7c19652b7c9dc1e6eb2f1b015a38aee53ef3159518f94b240dc571a11bb36fe18a75201b1b4ea05cceab58b6d12c30709a14db6741c10b1ae003a33be1ec3da0e661c00901edf2706dd2c322bf42e821230e905a250c414e469d6f55ecc5f5783c3a18b3ca211ff421fd31162d8c60441eb3b0207a8a5e77031c55bcb0de79182f41d95d6b6400d400fc9dd60b3384ec8c12eda34c8c9398e77103547558bf41c416142371a71163331e2c3c3593503d17935e44580a1634da91e476d80c09195c021fa3113d773918ba0702239e146383967984dde4071d1af012639a8302e86f49c6722fd3b9aab4c6f990c867198d44ceffcdaaad3a49d7b4baaf6d2af02a7f8fec352ea7fe2cd31f2b2f732993983b26c69a7fd03d069231cbfee0eadc464fce983bc50a463c00770423bb15c5fbd8233fe9c26643376a788a111980f99f54b53e58c9df8b7ddd8c18c1e405511c0fe0d420c6b790bb6529fbcc081b4ee1468ca3568d9d8d18a5ffb71f9730365961ebfaa3914050e693b12f46b346cfac90df8c02115cb038c6dd789f47ece15a731d89cdfff20028065ae6647d90a5286ab9b7caa18d401ce39abf8f7f1991c1e84e05b2ee3b565be456ea0666b740a94d26835e35269a40f8cfcb902bdbf697283f3e6b22a37c3abd9e65ec8f3f6eb6d05acd050740eedfe225d0977cdea2946948cd0cb1cc70b34a615249184cb4b92a80e4b913180fa2bda189d0c5246700ef9e191eda54d5b5b314eb712f46c19e9e814631323f124b426b0c4de08a2d57a3b60cd8cf1b61366f2c7a16ea71f96f4390f3f6644882c25ddc6a9e118106ec21df209ec293824da6ad793ce6aee2c4cf4334d9a3f520dc406c72f98132e1d541dfb478dc0d9d604bf53573eba9b383c80d12f85a63059ed1798b022af919d2d8023826e71d912d963ba5cca42c22f870409f78372901298baca1a264bd88a768379edf3643379c8aea1a8390bff55a361ca88c17c2388916265e6f01f4df6b02556906f357a0e3ae0b4490b108f8389c9cf505022efb954d0da3e254f5950f1396b2f1aa5c89cdad36e7ca42d23cdede0c3ed1bbd19284cb6a33474b89a7278453c4f4f3a1b6073d26ade3ea08a00eae0939bb9ac04168bde1c1ee1e8010504b7cabfe8ae4fa77a70258c4343009010316d13da369b8e4b2b238439a8ad735558524b1eb0490593cf6bee68c8bdd9bb98e81feb0800f2048f5e513636b8a31ff4ebac889b20cf33a730d86473ec0cfb50f2dc3052396d206fc268a759d0d2597e52ef493fe26133872e8b6d5b00e8387d535ac46f5deb03224c7fd473c7cf13a9c7aa78b30410c0fa52d89155301afd1022eca4a321517a56e8c237b57b5c1af495f316dbce19e3a8a47780204774545459fca08a2e57ea0847b2e9791a16c9058afd2d3191f09cce2fb51ac5b766f0f61468393a6e5cb93cecc69734a8c189a6a58030d468760c1e5f1bea878006e22054bc03c82698790dc18257fe3c674246b3bf585e03f0d9e7e82768ce0c1268fad46e09b0b16aea4d3335d960aa813eb3b6e2b48906fa20f07fdd408c467d03ddd4debf838a4c5d06f7fd0fdbf755042fdcc5a31fa52a4acbd6feb99604a5e4201f3962a8dfeb6752e391f2d2ba8e462ad244dba0294a6c28aa06c3cd91a87ba43b20ee4c04a339fd257d2dbe00bea8c181cf69f478bf26ad702c3f6d3fca8a41711bda64eaefb902b563260a86579be435bc32e2ea535eab0f1c7a4a33d9c4f555a46f1d6f1c63dbc9d4f58995e5315b4b22678d03ba7ee0d1b2ddfd7a350e471662f9f8dd7a594733b22ec036fb45216194017f8f751ea551760d4fbd12d1caf37b76c0309e054aa94d035706e8e982248abfd70b56529f4babb08fa9249b1e3f132c635032ec2c1d7d9e24c7d574210d148c5ba32a4f48b8904c517b6cc0f8124e432771ef3c40860accddb796f10f97d468e18125dcbe7f3ecba9e76282ed1586d0982f4ede78cd0ab83e1338193df32aa9a333dfb54249b64c35b0b39afcc6ffdb505925100783729efa1dac0b5e903d4b7a59cca00bd55001c95a4afc36245f6e92d861b39def13d0030ca032ead10eec4bfaac815fcc94aeb44537ef50cd5f795009ac4fdd3cee521f666e936ee5168a781065efa9bea30e88d561ddbffeff1ee24d4571b2605f00d2ecb86a113602129f51a62a425ad7dfdc577d295bab27aa324bd105e16d0bc5d7558c2aae7dab2ab7a39c13857b78aa986ec9fe0c38894ec6c179ab844c135677f93d9b8b4b73105116168d74312ba0c3d1144c1bcdd44e00545bf1e3312a5ae6ad220d9a49f373c04e19e81bf3c0359442182c99319a9a787ec831e651a609a0238999b6460bd639588ae9f0e76f0d64a74e037a5b24d9f3485243055651897b09652eefd4ff19a16fc627022f5eac252cd8b572c153127df7637053e5f356d0a36419afb7775996b8a17f2fcc4993b755d07413aea42dae61dd0187794215e86d1957a63a174c93b6a4acf54207b0556e1d9579a3d1c409e1097664352500240a270de9445dad7cb34ba99a998400055844f1afbb113139cd96b2f0339a639a5576897ad564f92ca46b297b38d82f44d817ae884ab7124ecb1b80b3259416299907c4272cd80b2fde86ae41fe4456a8ed1748bc160dbc3efd4f61847d773b8d742417916e507b5f8ac4744b6f4b35dc1e9f1ac83dceffd0cf1c4ca350914690a2000c9ad370a2ed808b3dcae83b0391013f743004ea88439bdec91ce010e630a93b3d5a9a336e7da4981559c2bf413c3acaa66f2e27812e8ef4bfb8f10b2b83dccd6f37e1f92812cd8bd79c611d7bef41b9c2b9facb3dd58ef5218d46aae2e5ec73f453bd7792f43e686ca6e3291c5f9bf9d18f36c93ec40a449010079f84ec2e73768c12e326b1da0f5a6c351d2229b248c8a2cc8b562ab6dad8ce1cea8d8ba38381ff0c834c87a193fb0305cb260ba81c633e42a7d2c5fb2260071be43a8192c9ed3f4e10be73072c6367cc3fe885dfc320161af37fe1065862f2096f548b8b679c82865eec6abbb54a287d307acf6f503be36c31290bc3acb655687bcf0a8918623ca52b515d0a6467fc5063d739d6579155d3ae858c8947dd96ad78ce315b78c215b42deac2e732c4be07c56cc8cae7c00f370b8c7ceef83caec8ce6027440c905425bd1ab8a2d207e5719b484dce8c2b44c8677a0538a6004d0e908140cd7a47cdc8f8d9a91f5f75d6682c81b9adfb3469ca80e72720b3a1d6322dcdb51071d6752f2195de74ce06ce94827858081dce6be6c64e3ccc310f7e32f49a678c01d8d226b3e525a6d569ea90ca056645a1c8878fa52ce9b4c0122e2bdd6c8c38171a2c051b652f27b7c4d65963a25bcdb9234d068bef87d16ae7f68f6cc813826cdd44be891f033f216926db4ad5ceb3633c38dd9b4a57cb45b3635a5807fa352e698471f79650768bae049d4bdd3f8b1296d0bdbec2237c28f534157a586c864655e6248e1ce9cd8d060bb6317a7a3a1ded7f543532d3c457cf400e99ac0f3b5e5e166fc4069c2d5649f65ef9337e83b8f1591a8b036209bba92884513a84cc023af3c11eeda13c98435be8bf1857a7122e4bc8cb0ec1534f1e9816b2c0ef8e3711d04b9b085b283c4ac11780eb4ef6cfaca2cd391d9d9be45f83164391c1e6da6cfdef34dbdf945ac27dea4f10b687d87f097162b01ede586b3ff2bcbf93d68e620955ebac1a500e394492a62ddd96aa16e9a1d701de31dbf956c3a233cfb25c2380bae40e7e0d4704ac497cfe7225e7ef884958ac8a0d9ba609bdf341255c18f2b840b636c23afc3d2f19e6d0fcbd3e464b43590af6b5d6ee94bf41ff2edccdf69f22e692b4fc885e0fc0a254237000a8cb4f8d92b447ae4b8baf2f4b1b80fbc985698f581a6b6ae491f5d2d797cf273e5375330a1ce3477be79a7df3181f9392c1064350d72ba350a02bbd21a59d3d43f018a70eee7dd78703cc45d306367e3dc9425fef38d5abc025f147643879eac46331362a3b9e6a36297f1526949aa02495333f75423fde89407b60c17aed57088fdc29a875ba76c61887fc9791af39c4d18f4469bb002014b5d5180f41751bfd886a53a1977cb1b40a26f29c87928bf4cd8c988465e3a8e8a515ec4db7a5dd08a5b9bc6a3ead499e48341343ae28d42b9d7bf3cf5513f9da6695348913eff7d3516afc3661563a4a24fe8323d95ede896001b0dbdbfc2fa7112d099dab7b8549ca1c2450e05785a314c756537f312c655ec3f1d575bb69e9ba9844c2ee30ceb1b724265a6892c19e47c13899336a52456457f8d4dad69d7cdc5b5ada9906be07a3413dca0447fbb6493c136196d65dbdf30b19f5d40764504399d8f54bb347617bce72836905e58fa2981f730b550a211fa9ce1e3dd8eb47efcc73bd436454e381c6fa097f43101e39fcd92b91b39b49910d2d207652c3420142870a82c10d6ce5a127dac7261ae54027a441887a0075e96f82cfbace1163821df8e8b5eaa3e7266f5299b09821952e4570e4577c286269b38753d9244d95fd40e64c47a6172b0fb6511cee9116b51eb4aca7563160737f8563d56591c8c1f91629868d92c45dde476bb96e60d8add6c7c9cd15df670dc47558866dd46133855eba7bb99d90ff3f7776173e6280ffd8f88ffebc50715aa90730b3039c8a99c89ca3dee05dab8b48cf86e0bd89b30d18fe3dd6358396c5640088ae9128d0797ab8ccc7854725d46369c153d6bbcc146f8a0679a333335bb2e1cce19b4aeb0edebf711d3a9dbc05a09fb599b9562bece478de097eecd14e568b11c70b3f1a6595b724fc95ecb3d4b7005335ba2b067545601a24b81a6b7b541357f9387f4531a2238bd29f98c1614be86e86d1662624c6ee9f9c7673f3ceb4d87146dbdf91d4c5e2db31e98c0b20d57b0669904368adcaf62cebc2438741d89a1ecc9950fd99dd791d86bcbf28557f815b548a6ab8e7fc95820a5b554988b402e528a692aaaa2dd6f6049fe114731e1cadff0716295affccfe5bc8c18524bd515e53992781804b4ecd661d7304f4655af54d7930366526e5d30ea08d924c8100c1df40d911c04fc68b7b520772ba491169caa1f9609b1beabb3d98dc4126b616ed51007fcdfbe363177d96f756ec6b27a83539ca5d6049709365847d42629e72ee60b7a4ccae7b553e76725240c9d4a5d769b1ea727eaae94d96d83a7a3f9b77d465abc21d04b03325a65823aa2cc7856664517dd515cbd72853606a1649e56ce98e09ac648b91ad72e61acf10e5871090fa70f9021cb77724b30d3e8bc91e4c344973e1ef44ef1ae2a4fb65e9a5f19adb0add50eb4f9e254e759b1f578eb15a04a28cfda74b98c2593d49a7d1675a90d60e9bae756004b7687780509c2e63588d33eb3abcc25697c4a6a08399067a4039f2226a6df633543ca290b4bc202d10f7624ee5717c9a59f14f89075f421c8129576f545f85101de9aa524ce730c88750d4e54182b14dfd4d4394554208c60089d21b746f02b41378aa3f4933f2aadf5b9daf8b60ac10fae00942e686b9fce2bb00940ee7f169fa3901c5882e99bb1a7a62c5321606b22d06ba925f718c7ebae05c9c606c20a95fda4535ea0aa30a6f600445718ce10be9a1ee1d8889d2d864266398ed39d5c65cfb9e038bdb8923ef541c95694481af959d184d10d1483124b40fc9adab5a5288b8862aee63273222734134de05cb6905558dd9d74a394df74422be362c096d8250987ce374aaf177357f2f017d6ca5f3898f980a834a4d31bee386a1f9548fefec8ccbb19f0d704422a5bc9bd60381c640a151c34442ca6cc58a110147db112605a359b4319480e9df5110c5beb3bf4b138c53e1c6a120713fc689b453fbb0308a66d0afeb5b6b60b10cb1246990151e8ca0b6b96faae4eb41e70b198b0ba312277f45879ffd51c8ac6ef2d0c8b9fb032139c3b2b270444327969df16d78d7b24ccc5f650c9b1d2cdf42234a938d76295781a22e3ab768ea94e415cf27d798e3af991aa1e245f6185b94d4e80060fe354955ea5ff77bd77b1436b38f31a15f2c6272ddcefaf71f4bafe961c1c788bb7bbb34875c1775b9ae8e69cc2a19ff4662fae178a73465c273078e850b50f8e369c717088ab46a25117bb693944fa17128e1a5aed369350418bc93bb974e950ef1f014a77e0c3ff503a5886e27641004d27c542e377c9a7413dbaefc3aeec962f4f54800f511429babd13b31b22e6c1a2c83d2f910aefd5df179b7888350de9c58d226e237cc242f81e1aa3c11817fee35df6e19bb361169ae5ed7c49c806915afc21ac1a7172ef034569391f7016e636021614c3e9facb7b0c3f21726b2cb0b74a508d11787ddc722c425c5298a425859dbdbc23ab115466a3ac7a0afb42a0c446c35eed15614565b7229257f0155ead30ae131a47e5158e002eaaebfa0a029b0dda8d71deb0066902440fcbb6b2932660ff4da6287f912ad2904384fae22e49c445fbc6fd6655752a711a735be46062171c20be66dea5f994cd1b508d857a99f8dcc4d17e36bf790de6d8709f3c1e6f89286e827e2952ebc29b6222b007e82ef684de6023c94a21e23695f0286aeab7c4c01b670de896fe163c381a5b887558c8af2fe00ba400847867386130124000d882327eb76311a3e246231fd9a81b40fa8e5cd16eb888fb22a622bb904c5560c8c3efb1a9895a7969b2bebf9c3f9c6ee2b177408fff7141c860025b4862e8d3057d16092ad3a2e45fe03773bb36f5d1af5844619a5e4cc216ab37352923c922563d657527813698e485fe4437b206a4af7679bbf8683183a8457b46123cc422a92f8ca726200f501f91b272f24171eab29ca786f6389b012c2a97e1f76cabc566cfc93ce93543a73c2b964ff594aa879b948a21f1914cf130e68950be724475beb044c1d175445c437b6f4358e47fffb7aed7bb0fb4bbe8fcd009cff13bac21f04e4285d718fa96d025ac0ffda03eced6a6142ed9e8e3ebde0a5eb39046174348fcaff2abfa5c2e3baf5935a6bb246bce39d73d0e5ea60eff2a05c950e1dc0653cd443ed030c38842a70106448241881f3fdcb68cb8f66c1736acad101f3154075a768860bd3e80e16b8ecab0a1f3bf16f6ba305237833012123943f212f71061a6e21a4e4b64aaadbb2769f707f0d814cc148aeae2a675d3eeb0a770878761b678c6d84dfa3918458d42bae5600456b6286cc52ad3d6fda5c37318ed4c79c2a59958e01162195693eb11437993d1f68f40cbec0bfbdfd41c32109a98f4cc8f3f8c0b569e81004fba41f13f8c43a6edcd08279811994c20c6ccc2b225310983262303bd72bbd91b3325f67c99a836fd6b04d19b30812bdc460c81c8e4d09d4428e0f26deac4ed6dbc4608ee82fb54d8ae0300dcff5a0b2d17510c949e5635db2ac70678fb0d3e1cd9eeada85eac96107a8548c941a1328860ca17865dcba235be17e57325d379de6ab3ca8c7c7e1346cd2b2601a442f3624c056ed1cd0c9d546459ec10f1f7a77be18c57d4a3fee194d29d015b64a482ff30b3f46e63feae5f3b888e6e06e38072295ef4d78cfb143b170253a5cd3cb9b2dbcf5837a6bf9fe34eb5c122be5f475f3e7b1d6c1bf80869b1a8eef1ee8c668fd5bfc898755597da184cdb1500d4253b4a14bd9355f14db5109365cf72aaa0e4531bb1524fa3eaa5700b6b4d9c49aea640a9d036657b4e19f04e1e8278790ad0ad8e705052c6f67f6b79789d7206152ec1eabb05f26487ca211e0d7b86c94e43a82b25fee06a40796ddb27fd82b5964d1525dda5d781666bc9c4931f361707f72000089dbaf449317b718fe1ed007ae18d3fde70e095848c1d9d1577264a7a477de0a124a93a27ee1d1b87f50cf577f985e18482ccbb076d958517bd3a7d7e54419d41ec76b263751007d1195367d508dfd1ff938ab47d0d7c8d03bd598d992da41ae6bba5b3b8cfcd75338ee0b48fe1733739c32ec1acda35f27c60ef78eda46f0e7dad8642dad48bf382b644bdd5f9c16f4c7eaf79bfa43a8555e0ae9bbf4b8252bc25b011ded2fcf2c1c3a8042349675cbc4a39ca44653c72d2c1087a92ae6006d9a92699aca5249965b2f62dbc0220b24527fcfeeabdbb109d8be8540ce4cd18efba5cd895731ec2ed4ed382a29191150314005e74fb78960ac70beaebc11541ceee63fe1e51c4343216d9efc3bdd5a76a64867c78d6f6670e98c7ca951d81c7db4419cb6367eb9067a6180376e178ba107267ffb2b9a143ec27750ad1012d53d4a9a508bce113b26c0eb9ffba174359be1bddd7d6a6b64cd9a8326734530898766fcf7d68895a28acc43dff330664e5dd1d97d9949b728f23a1f25bcaf595bdd0e128c29becde7cfce32a71c5e9b52c5ad9fbdd300526533f4ed39ef3ebdad3ba0e1c5b72f59f9bed0888fe3ec56e859bb333e7dfd33169f8d2e9d5c75d1bf3608851966771fa1f51b6e5e4ed5377c66af00b792909720ddf6376e4ba020179997a2037edf6bbf653c3765baf861664b8e94d5384a8ee43ff79ab350f231ccba63d5b390795f79c64c5a949dc9b9037c7b86d8bc8763642efe8f05e12e5d0ab5f7d51c3e1087ccded00d5bd15feed1e02e403d686f4fe0cad64bfeda97808d112cf656c23c5f83dead09860fcd6fb8006fe3a463438aa730adbbf68f55f645d318f649bba8f1428ab5db26be321e399aa4c37f7cb5279ecd94083d07c899ac27161fbe4a2b7c4a87d9bc44747eedb185bcd9379140f6594a4b32f890cabdb536170096ef777aa95ab9f94ac203cc95940414dd74585bf91cac2bb2fd42d748434c59bd199ec20a7b8ebf7ff61c47d899dab68bf7a5175bf0576eba37dadec663c768c0b048ad133f454df0304ecf16d9330fc1d75a09d77636dd08f3efacaa09c9fc7185a789d034990273c16dc3084184be9a69eb811cc4c20ee7050715ff805f3c776577a0fec8414d37a753b916757b5f1ee407e41b5f05837402b2854d95e96e6fed7094ec6c4ee6f86b26683e95742c767de469cf8ede0e17d281b93916e6ed2aec540763726ddb01fde656a203789726bf8603be63845a2997ac54832a58af8a01fc44f43c21cc2b386db919aec78820498fb98db41de0f8b562dfd475fd95e68c6f0bbac9d2c12b4aadb77628b210d614e4b0597f753e5fdd8cca8af31fc001f0379a70d61c7c8fca60b80e63eb27a50fa2294a0632ccbcec42682361875c4583f4b65c7e5e22d7476b956dc5b044a041167b224d6b8ce002e9bf6cd1f24ba8b98af274d07cbf3ce493fab7716351e930687227e9a17dc5f33803d0025787567b6a3520a790ba90d0741912052e4400e30e3a1ac6855f3c01204f5fac95e0aba22ac50eb2954a0ad4248e5f5f159a89675d78891416302daffc05d54895365889d8e74124952c71002f8a77360fac6e21e0d21cf0bd618628a190f715e3d8d882bc068c8986165f84105f35e2a298649b564dc0c0ad269ba38c7430cbfb536d28a9b1811859071a05a22be344bd0d8ebc22791f0c350a264219bf2fa13c34858b2640ff016222de6bc17be88026de7f06fc9671f19f2fec9a393b10af110c14d2d10574170da6d3f418e8284e07a55ab20d0ac3f1cc750503bdb6cb35ba20c82826e9d9f59a42e54a8b74319c571d301b6f010e026d606c320bbd47d4fb3dcc760af0cee314451377d037f2c7c1eb798304fc441ad19b8e1524ed93578aa966ffb81801141b11fe9f6cabd3785b4669df936c7ec13cb5b360e848f3a06518aedd9d59c0e5ea4c7ab18f95b412bd17f6825750db904d7f2b4efce7341d2b7d01fd345bf486b5fa1a01fc9748198f8ecf81026c35dffc8a98489d7856a7ed326054e913d57bfaf07a91c0ec29c66d58898d6f338490c6307071c718f62ac26567853b41bd242a8a3a6134840d16548040064344e7ab4b7a3265fc74e6c7b00c36162f2620773e00d310ac87f0081cec714d0f40604a2e29f18a2ff8b75a8ad7816d738b2986d1344c93758fd8999e95c2137ae30ae2b7b667c3240d636aa48136a4cf76c00d5fe00eeb96ac19c7c0411bdcd64a3c2ede577069b5eb2070bbf330117cee305c9e422ec45e8e12f14c41bf7cd89cce91ac5c316b5c00769694cd96a352bfe37f858d7abe4b766a014770b665eac59c9df487075827b51df49a86acc269c847206edc63f3476704dc59831f94069382b0baed52f6e6440853a38db85fc0177ed438876054f7c553fac5c57b72e2f17043b5138635ece2fd9d1a901894719ef013afc8685d647665191462727081ea38d9e939d7348415bf1a3e59e8268f8bbd07d0bd92d4b2eb1390f7b2628f5007a07e3765948906620219ea24780743046b5b67673d3f608c55bceeac031c2cbe26722eada9662b63ccac2549534beac0ca33b9d35d0f57f45de70ec84f86fc88399395eab1d11b13959c6ed1c5fa5c59dcfeaf42cc35c5312b7266bf9aca76912edaa76aeafcfcb00a994ee50ad828a900cf13b43e0e3d60111a4586654256c9a87538fc761c53901c12850ae605b4ec7a7d42064fb1151279927b5d27325d87a2528ae6ea952fdf98232688c19f5b07d32479a0ca4807ac647337090384a7f16004007699183784ff61ea3e0bb5547b085ba382aa28c35c585de0959a4a020b083249edd8f311be06478e213ecb12a814415be8f8979337e06012019c800e720890cf91ac2c79ffe447d0909173e5e8f3de1e8ad8c4bb9416eaefb6b1eca381744e6beae23a92587e8d65aa8256bf1ff76838e73a32874f13e260eab3ef3d64d38779c01470a8218f977c44233fdf5ca3565a6f54abe3278eb634413dc77fcd7b10ff01acce0b7814f70733c721c7577c916449804a63f4d6a08a1ec87d93a3cd650f24da47bd5813d0b24c62852648b0efdf211abfa463c2ddc892affa9bfcb103e24217a180e317081d1afe2b9cc315a954abd7cc3316a55bb4fa00a08395078a80fb57c4d79b9542d766d9df3ffb45db61004184b5d3521f0290aacf87459256184fd42b8f411251a1cddeaeb40cba622723db394b63180f431673f4f361a7b166ffa20358fa980426801ba7bfbb84f8fddbf005170addceb364bcf5bc581d9631e001f7ff8926e8670fa34f16dc61e08a2f405336ba1cdbc4c330cf6ffb154145c9fa060851dc0d7db7bb44b4835668ab5a8b725c353ef40bdb1dd1baf820c933dca1ff3f3856255962aa9faa83258e4127972b0c1028dd9c831b9a7c6e56fb6009abfbff3d8dca4a317acb0d19de36ce50245d208b1f6bfd9dfff026b965c5319c06ac1ff5fa86e28027bedb41ccb08b97701c0c39d9b29443d844b7852e436f00351d6d9981f7e10abe7d9688029372108d4e6029a13e26a5f50d0596d0e569c311c39bbf2cedd40ce551ab32d77c5bf03bb73a7487194449621e55daf0fc471919049fe6ca41a1f1664f51912d4d1ef7610870000339180dcc6413341eae32f2fee011a6cf066b08085a7f79f6de9a525a3255bca94640ac906d1065206f672a48e86a5cb75dfe5ba8e86dee5bc8e86dde5be8e86dce54a1d0d49dbe54c1d0db5ec722b3a1a629763e968582f87ea68482fd7d2d1d02fe7d2819ceae5e5a322c631ea57095c6f06c0cbe55e39cce5ef2c0b4bafc69fd17aa592c140c0f10c896298760a02b1d846e23aef2b59d30966050c8ceaa57f52ede324e8a54b754ff3bcbca860c00eb2b3a3728969e91fcd8a618162920e76be9a73582ea78e58d26b611ec5253797d7ca3071ba523bccf292a622eda2c21180e2a9c869e6d0504b0be5259946d3e845d5c204033363a7d12d2710ebe9c06b314d7b4df0cd66e43d717b33f29bcdd59baba9f488830cc6f3e10befe876abceadecb137d746a585c2ee85a9889f4c274e8afc6e2ef105eebce8b270facc1e54909764fecc9f7105b3c807b76576e0c66dfe6005d971e14d1e9e1168af7a44e08500175e50008f1efb693c07605bb9551941871672809e0a7e5d0461782da24b6e2e8c8769fa5b546e3f7603df34111690bb14853515b118354aa87ba857a8211414134ac824c564b4c28d12ba12bc1af54b4e58195d12b79fd44c454cd32618f00d4a08254483a1dbc91811ea5515319e1ea1a0867a85f56042309eea9eabc7f65a4702f05a34647429239634155d981acd92e3351505612af29bcde534fddacb566eae544d8d4dc380a08d099305e60476e695e7f25ade0c192cb9b93cd746652b7b6532eab1bfa8c7965283a9e86adc091d4c0ccc872a9a9142c502fd4a5d54b1e05877adc9e860493bb313e4b2129789ac10b92a167af00a50c0038be41edcf90c98482c39d638cd7c2a4661c91a97279860e9b057e3a50a1247f4e0ca25d906485c1a482cc9423ee0445baa711ae65ec202f38765c4112f606942dc59c462b12c80baf359fabbeb0e037718d767a070bde42413b570e723b771eaec9ec425a790ebb3c8f5cfff08299e5c7feaaf59b1e1fa5bd08a91ebff56caeb3f4e01c4f5af615d7f1c1a4b5c3fe28a0fae7f0b2ed870fd1590e4fa114378e2fa47a0c88ecf752aaa5c3fa28a237ac5442c1eae371d82144002e06a429412e050430fac08a1884ead21b835886f1c6744cb47f440032cc880055c2538524150835b7f0607a6ddc00da590ea7335cefacdd4d1f1702e878564fb5c8d9c4357d34680c2762117aab0f7a1430a559898e387d63e745e40ccd71ec690651dd2bfd3680f040b4d127b58b1c57ecd0882659d6ed65161624d8fdae3388df62f48d89f52831b1bd05ed39e9bddd7ea633615e2a6d16d274ea36a4cec41fafa3d36f0b5b048d53dd869eab3a0d5dfc41e55ece143c7870e8fa921969a7458c7abd3e4588e318fac1b31b589cd39bbc3b0aa516cf6a0699ac69aa7829661b31379acf488ab3a4c8465079ca6e9e8a75fe9f7d3e76cb0f352cadb3cba140a97404f0f625832c59223288610c2a9d061a6a088e50a02941b7210d4811cbe882386308a12850c920882954313408a62c06148088810614237a003152e242e22a080c10f20a8067420e189097c50d0b2c10d2a00a23c3abc943c31030e76406589202cd981884c153fa8a1881834c182223f104141c90e0f21986283264f9414d1840a70706da086a09f1e420406296a30031139c400073824a141872ec9eb060b8cc0074536507da0472e1151b460062aa07243901e0eb0040f452368c24813508abcc8b2a7c8872952fc005291501bea1386520443a142e7a4623f4f9ea5d9820f07cb1f5aee776f85b9b538a390f656f74dcbb25a297d99ea2d61999aae0be14090b14969e52aaa5c9cb75e191a63e6ad6bff99b3e9ec9e33098e9999ce593a0cc700a5b0fcf3c197e56f7628649d14fbea74d65aa9b7426d1c685ad6cd23b4f95aff0b1496bfbb2790bda924b388d5165e94c0f23b539fec3d9de0381a2a2bb6bffe24b5dbddb49b07d6ded26e302d63a7e96c104add3dc35a785102eb357c61ec1c6cbb7059172edbef94369d1d03ed28ad9492a2f0d33aa90fdf3f318c39cbb2ccdd356650a32d036ea36269785dfe1728ec7c77c77e700cfe972017b8fc93b3596562581531ec5db859ad3d6a5e318f210809e7349217188c3614d6929e2fb73d8dd63edb5efbd2f527597bb7b777631628d6b2fc60e77710a9db054c299d31a899de24649b96a15eb65e3ee24544124f40a2252682ce6908aaf3f5f3cd99f1ef8f1a10a2f65f44bc39807ff170334f43f9e36ba7d26892135a4b4224acb7ac44033ba1a0305e127df1a08b507fcaa8992102ce0bfa99e7d520514aa2353d8383c20b1758d55edcb0aabda6897eeb6b615f0f6738c6c4b0ad56c7b47fd0b962240ce76bc019cea998c6b15cf7fa5cd7859d9d2ce341e7971370dc14b18a651aa92b96691b57a39f76b786d2b61a52c75e3f8deee87318d5364e64f176d78fb1ac63f06b3a60b35217dcdd9f597376779f71f7ce5d9c09a5163c763ef56ccc2bf2d882c7d6ffe63b3731f6ceea9c222d4d4d386691a43eb4c8959791942641cee341a8b886a020699f665d9962e4a4c9143279ca1798e8462a05967b9c49819d3cfd9b9f29615d7e22213c97451e2770a5cb443c47a92be3d57a34c2cde7995006375cee91aba98372b9478ed1a19d21388db04c74812997ec529cc39cf3c3df3b5070ce1a9028728265840f11200c01ced9243812c4142340011538d426496234bf4cc4a2413fe1b4bb2f57e2019ddecc4c359fb4d21a962c59b264c9923997f8f7f420ce811cc8811cc8811cc81d68083a63d6d4b4cff83f8cf187d63a35ad62d5378d096666666666666666666666f688d05aa3736366c698703c43d664d4b9a971a40e7bc658fcf04c72f7204d9a803170dcacb7332fd1ac96b04cdb485ce77d256b3aad6041b5b8801d92598764f38beaeba7dd4dbb9b8a80ab1bd7c4cea7a7d30a4e816085d784a4accbdf2af09a3033339d9599a9b85a55c6035d4bc7792ea5cfd2ccd57297bb7b3530bf9e611876d462f0645af13e30a04da1585a68ad152c5d2515173fe69462efdacdea6dcd8e323f3fa7eabc2653c60376ae260b19a694763d924e3577161dfd98a65e93b013bd1afc220d9962b5babb7badeed5eb57af492a0476bed7c4858b172fc65005763e1dddbf1b47198410828d701cb2bb978cd85483eed2724949d8f9252316c5b2e274a7c996ee3462bfae64c47a2523b62b19b1dc9dcfd9ae64c4924e2b58502d2e1dd8a5ba97d4e75fe79c3cbbbe5ca7945228cd32ddf9e5faf7251370b3273866e61766eb0d91a29d0692cccc586dc982c8829e1da5f58a99d91b62b28c58f6177577ea0d5133a321e60924bdc5914c1d57ddddadbbf7ecf4f3acbb3b35fa403a44844805f586606676238eb4794490cccc1e11cccc1d7dd2dde751c55c8468a1f5ab4b27f3013bdf9d7a49692ac8cea7ef0dc1ccac39f7926ad5cb8481f20cd3e248997ac1b24c158405a960302c080626860525423b10c34206c3e64422f31836279217d50c966541574022984b104c8c08732221e27285858c8b39911071b922828b393be07225145bd0502444e88fcb951633349408fd71b932d2605916e442046b09a219a1c80824381d7f7cfca1e30f8c1873fec4704ac28f93f04349f8294106c57668c102254a680009ad700368a89258fde3c811a08a55ac32611d19385a022021cc390f5317bc2ed4b3e9eeeeeeeeeeeeeeeeeea9ce290068bfdce9d3a74fdfa600ca66285b5cc08a6129269549eaa556262f2f2a183fe243625a5aad1816a82254510a8885cc570cab4b5ac2138029b16218132b2d472a8ac98baa85bb9223282b303133ee4a8ea0acb498711f82b222f3e10b5772c481505642f1851f71209415172f2a8631411da92c4c5e8c3e230d751740d40590bb001a8104772012a8d300511a20a7018211c36b919a6202fa0f3062c8a8517264480925c8a8f1233e44464dc5a8634ca8bffcd414931a9b5add87d89850abfb10134ea8d57dc809302d8660b35bc73d1bd3c99cf3faf0f98011fc9419c65a0644cdc2d14b023a0dc9067666d909c68663f4cf1a147070328cdea021006eb980805ead5ae830070b43b62bfad1b2324312d46307b55aad5609a65ea64b355b41b3355fb32c5530d3a8080626860518c342a645e651747b6d574a55b60f4bf155563124e48350eb47aca2f5d252cdb4ca1f9f209fd64bbc023a71f192cca01904132382c9e572b190710184b519b55eafe9bdaebc4468b988a2dc86a6680df9cdf67a852cb1058d57529657a5c5da5edb158c75bb484f4f8b191ada9a42787e785ee21570c7d2741170800b91d74e8b9764b282583423bc84aa6809b98ab446206133400b21025c68bd307a0a8011230717c3338a020e8e0068ac408277e367a80419747b6d2f2235e3482fedc1cee74af04a1936266c2f20aced8a0c7cd341411a68b58e90985ae56d1697a9155453e3953626e8a0d504df78a5e98997c722e2957eb3bdbcf24a076c5a26386d592d486bf9cdf6f2168561f160fb31950927b48fcf0933260fcf0c006c556caf6d0abec130d1d422c9704d2dd351075a0080916151241ab9f85e0d1d96d4cc18cab22ccb9717150cd83f483ca41d52909e8f45daa9c14959962faa16a6d7ebb5b98c5ce507e4637d327c1a2849413ed6f7a324f1dc26ed908294a49db22c493ba41aca57d9ba246907e792763e1669e7639182709b4ca62a290f94e5ab6cb98eca96a98ae9d58aa20402dfa05aad29483b1e4febc855fddbf958df8f8f5596a5df9076483b2569a724f19076ca2ae5b7f3b1be1fa49d8f75fb493ba420249e5e995e6595b21401098e63f621666e00adadc3cd4ec53aadd3fd31c7701daf00c735b30d983b93e0a64c67344c5df0ba5c2dcc4a3bb0968bba5a98314d2dedd058ee8acb651d0d4fa1e9725b47c33d9fb85d8b5c5a85dc5983dc597f5cb9f36995bec95893962417b919cb6f307ae5923363fdc878825c72b6b29e5b85643eb79fca7d00bded22d6c281edbaebe98af48d57763dbdf2daf5905dcf9d474dd3efe26cf5cf39e7537e9712df644ce3cc32fca7875e8d493f961604c7dff77ddf77fabed3f77da7ff4eff9dfe5bf1df77fabe53e9fbbeeffbbe2f6479f00be735994ca6effbbeeffbbed3f79dbeeff47d9ffd4ea7ef4f2751b5e2fbd34954ad389dbe5f21aa4ea73f9dec77fabeef7bceeb68c7629d9f25449d4e618ed2f77ddff77ddff79ca9337ddff77ddfe9fb4edff77d3adfe7a7ef4d7f12c9ef4fdf9b9efb4e9f29a47f0aabe9e9f72cd21ebf678bedd43001e78310c6186318e6363fb6fbfb27e738fdbaa374b1a759386f7d77fffa343aa358e5771b2476b6ba6bfdb01820e61011902977e632111027f7e7f2e59bc5d86049f03ac730b13a78cc146ad81cb1a4a6691ca91bafbbffd8ab98202c096a8fc1d4eab50ed2f398f785f166e3a671d9fb7359b8a374b7d7b22c7bec92886098d7bd7e2cd4918dbdf287d12b1da49a199cb3bd3f0038a7fe8e924780fad8479141d1c54861a8c1306f267a355ad53ce3d90f389eb9323df31bcddbc5d92251b16119fd0f368c19c52c2710f86646a330c5edef9a234bceb24aafe6a522cc6cf51892e36cb9c63dd997d4a39f4c19fda45238b349e562df937d7f3fd9bec5ed39f149cee8de28e0d62e499ce591255ba8dce62c2b393d154a978ab3d5e39c356405aefbfc6f6e2bc8d8f4a7d0f4dce9f9f434da743a997efe3c85a629b28c7e434a8ffe26530f413ef8f05cfb2725822ecb9f8880f45cadd398ddb19deb7c534f61ad524b8ee19f6a59dbeab1c854cb546972e7f19abe1a79053889758a49ac45363e581d94d5a33f8f79ddc607abc3f43ce63dbd29ac42f5498f363d749867a33e7df0f4a727ece90696fce94eeb773a89154a8fa44be9f124922fccf29e9ea55727310462abab477f2bd6a3ec8f3217e798fe664e984495b51e014e6f32892a9bb57aa532954ca637994ee20a4cd8d393b56c953707e798de9f3bcd1b02b12bfe6b955e9ddebfbe38c7ce5ba5e09c1db3bccc31dc8a2a96cf23c0e9ad15552c2b957df247c7717a1651654515c66bdf8a37deabf4e87f0a6198d78a2bf4e8d74f7f0aeb0f3846d9b24440762edfd3735d9732ddd39f4e3fd9abd1f77b2fc5fe4eb58aebcf3066eee9f42c936f6ac934fea7d373b4fb42d4d3b0f4d33bbd97154aaf4cef5f87fca6e5f976f6fd5349589fcba3afc9f464fd5ab68443d77b1ba24272deef0404bec9e134ce758aebd935895da4477f1b10905e7ad92b969bacb502d33ecb5ace22d6326b6d2bc4d6f58789c29a441da737bd15c9d3db37bd3d994a22d95dea71514856290f9877ab240bcb253d7c00bda497d7f90698952b1a2543abc50292302e20cb0726a9e41cd57c4981b4b62881a194c8fce537fe624a596ee4a5cbd563939ef0a1cc27fcb95990d3905cbdca7278825759111efb3b7382dd9214fb007a95f000134aae1342dc7625320f6439d02c872877663e7ed3ee95c732e14cd4fa53baea5115bde54224d30c2ebf1b293195f0b9dec2a61875489caebf69883a0559b40fc46e70f9bde54cd46f8a391318f653bab0238c0449cd1bf2219edfef454f7a945b7f4e0e894b663d3e94ed70a360459c63bafd251fa2df9f9a92f55cfa2c9837d9f417dc7629b5dbc0ac47339d30cb393c8f5c6cdff8d033975a96b43fbf9404e792f5908c52532c699d825e547ff3a1194f42fa8d94f5643d43604ffbbcb0b83584b9a5242c3933349b9d52f16aa1e634f3b954bfcccc874b495832eb21b90facc3245c8a0696f41d5b54fabc8e23715234b0bec3395bf77c6a84812244301084ca108d9cde255c8e351853863f7dd23592488297866f21a9699b48f6b89bcfec993c93f5ba52bae6aa0eb1100e59336180a24679559f9276478f3bdfef9c22e9a3bd1a949643d3e77569d18e086497acc08eec62e274188dd559b4bd88b890167af467a14711dcc192204f76f7a396db300330a790dba17bf6eeee2e64237d5cf76a90486fa33ee97ddc0e494af6084921f7bc33e4d15713dd8629a38fb869f89d7907ca4c293333a594524a29a53578f2a208ebcf0c85871b482492486e9b6fef9ba8815e8599293353caccd5293f531f9792c4882c8888e8c91320209e2620892e5044f3d666ea0238193004cc55c8e5e79f9385660d7a7459a847fe394b336a38468d9138975fd3b46d6a35ecf69aa6699567d4cca03fc31a6ed65ab5eb4fe6b8e4e370359cd12316d6d72a7dccbbc1778a3ab2d744b2e662f4d65083808fbbcdc7b82cace9117b2da4218e5723cbb41b35a81f62f0c39ddb7b4d8f367c78385c84808f3b7f7baefee4481ef3eaa83524ab8b4f7a2f85f9cdfe2471468fd877a86313716e468363609fda81ad338460fd79064eaf3631023d622f80a6c1beeb5858606066662e86f1e811fb1e31a1d905a9dff77da22ac725bbaedbb6edbb19a45c6ec165a2197e5c6e7a36e8e51eb7a73f9df6f5fb5adfabdfd5efbcefbeaff5bdfade0c47d7deba52f1cdb22c237d5c98540b8a85e5643275dea7be5aeac21da5efbdb0d4755dd795baaf863abcefabc7bd74dff7799ef779def542ad476f10c6eaf0c0947dcf6a9aa7f1f53cd1865aa8c3fb707862ad9ee751805251eb42be29607707d6b7af0a87dfee5538fc9213b89da85defc95e43bf34d4583c0c70e66ecf240ccbddbe23fdb6711bdd1edb8ebaee3faf4607f6e85fd4527a356f2bd17d7dafa2cafb4980ee6b17f58a3b0f47f7de78eb5771ac30cc5b4518507a457a5f7ad7e1bd50afbaf7e7489e0af3eb7721dfcdc3e1755fb9f6f1ed5177bbdb8b7059745ed8b7f354f86ef570946ed5de6b8de318dbdb20b13d58afabef792a9442b2bb5d065a979fb84c344395cbb1e86c7cb06451f79d4d0fdf3d8f7975743d390cebb76ddefd692249014dab21ed0cabb5725c4fe6e857eafeebde1335a7d15c48824797046f8e4b6a2246bb345958e17886d4ca5b27063439f630da63594cd449d73daa7bd40a14ed01fb0ca8e766dffd70312e8f4ab9b87c0bf65a14269c5cedb7204097a35c26ca40793917efa5707aeec5382386e18a4fa150dfc9782accae63e1e1407d978dd04dce992d0fb6bc80dff212ce2b534217feb3888979a137cb7e6659968529d04be1f4df732374b4c741d05ab055c4694eb840af00f4b377696947bd72b9ac78919c974cfd8ad7a4f48aec9ef4f535a35ee9e8b4a25ea154985198f841857c593c1c9d0bed11f59c16e437d315e437aca3d9cee4a5d0d705057e0bf864f62f5f1f0b72f9fad80fe7a8be3ea68473505f9f43a1fee5b3909c1765f208807a179747a19e69b40a25aa60a017fc1da50b3e0af520f83e39674e1b24b67449ecc766074b623da8e76a6c8c30d613a4c8a50f7e7b38e883220cf3eac868b44fafb217914ca54a9f872325a242be2d21eadb7b09f9bab763de9696a73ca93fa1be7a3674b43c8f795d3e25da18b124d673eb633ebd6a117b7a058a644b915eb9883a5adee53d0f478b8b4d373927051da7671b2396a4d84f0ab38acb8325e90e16947ddd1ea8573abccec3b189d96b22c7b988a4868599a8f588ba98082c221983ba99cbbbd967d9779cd37d567a2e0b390cd8cc468d48a3310cfbae57a4c7f89ec81917e74e52a301a33c748706e915ed9d0fb5201ca37eeff88d4603d3d4efe15623b70eb9b572e20fd0ad4f0a31a01eeb4f8f35c88956b52641f5a913bf611dcdc25065aa4c5461bce3a539aeb63dbd59d6f2c1588f5af842cdcd70aecb148d50b9acdb710e110c28b79f9c81b94e9bdc50a0a6e99a193853ae752a97753b51fba4d879c916ba4365d3f0bd43af1ef9f6df201e9ee6199b758ba0e078a686d9dd9dc3176a68ccd0346b41f071346fd21934a8a6590b82ff22066ce0f44873dc176aae7fd3b8417bd29eb427ed497b522a52f7afb5bbdb51e8b1e6523a03c7983b1c837e6a071ab69aac8e7ed7b1b0c0c0ccccc02061f4e8913e8d4e393179362a8a20ca810ebe5bd61dff1fd0c7f6f0689ab520f8cfe180de518752446424a535e5fae63aba2ecebb0df98da6590b825ce3809ae62ea6d0f5ff12ed04b9fe7f857e08b92c24834b48862757ca944b3b231c34b7b27a4c41b1e4f612c118232c09825504b7571121d2d10e501157a5e749d7faf1c339449cd39f10cee914146d06102c2d2f7f9afdb1bdb69ebed9341ffb0306f536e4e4f626f439714db3f673e2c488be7ac5d4881ad9f897df68df1fda34b14eebf3cdc414fa0a4da1ad64cfc5edd563cc092c3dea1509ba7a456eaf6bd4ab6e6d2f1005cee9fec139cd39449cd3429cf414169221a7c2439b704edf1acef1677a59d3ac058dc6ed4a8ffd45db6b9be284cb8fd33724960d0760938265b48ba9234b7697ea80cea0b78d8fd26a7239ca1246245c8eb204d0f52addd1e392dc751b84eb6e83c43ae62a156e1575f8578dc7bc241fbf611d5a3aedc9f35b2d527c3d7d707bf69c73ce96cf26029bf9a8a4ec596a620ad58c0040000000f315000020140807c442814814a5a922a13d14800d7388446c623695c7a21cc8811005310619420c00c018638c31cad4cc1001508209d9025c4c5698bc193121fba57cdd2c8c82d0a064948c33526d6e914b03a00d0c461b52b30fa9aa9efb6c0c76b92aa861d2d14744e35deb3e4e142a8abd14b9595eca9bf938e33313393c8d2e6ea22bfa5342fb14f5f14cca73a8a8260dcf366d3df30cf71ad4602ac46d5af8757b32c3912a0de47eaabd87a4c6f66ed34a1102999e835f4d11a91497311cc94ffbddb11ffc14e4d2e7c299e9c001bc049beb090736612e4e9307cf0d5d9b36025ae33160f572cadc4bdf85226f1381021e154b2aef5dd4cf6629abbb0a6fbb19e804de7fe73bc7ac7a00f68fc884cbb186a390e56b6c747ba2dea9b75f3bc0b3205133456f5d0cbc06aa6a3a2f7ca10922b9d32e91ace469d40bef612f316cb3d29982a5ea56bf210b77ae3a44ee47d368876dc371b262cb130d6fe61f644c9dbf5422fc850ba0860633542392192c58e56f97b51d1eadec7ea2105bb05abede55aca0e3b3b4c8666c4bd8093740b79f9765d956e827ebe8309afa8a1619c9757b793a52ac8466ea82cd9fe41354a6ebbd6f8a5725cf9a769ab08ed48dc75536eddbccc9d493b173fcbf2656361e00d410aebb00561eff334dc038c63191b40eeb214c4a241b2861193d8a2360d0e4f331ba42985e6ef7990f01e3d8e510ebbc9fd341131777005bc1f15ee4fcfbb7eb5923186a8ba5dd04020d7d86428d45c7743fbfd26857f5a012df7b1868b368826041e1ca55d5da18673e02c4f7ace32092afc7cc694b01529f1813af893342304ec6921c9f2025e595dfb56f767a2fab5e3183458df4e5b18c241714d3204aa1ca9bbc8a64a5b9befa229b6b6dcbdabd2cf73828aa668a1cef9804da0aeabe967ba7adf49153db5674808d16f413c1d9026d7652733388ecb021fed9d7ed12fb2b374bea5db2740404d4f21ed2139df3f039fb92a23112418b3968e6394200e6bc4f101b85d536843312dd4fab54d375e41956a342ee665ae11c0dd7241d3ef42b86482dbd508f6f240550991936fac5aa6381133caf0368b5c43dc8d6dbb9be0cae8bb7c952e3c5904cf18bece405ede463fcef77a34d145edfcf76d68849b85861de981d78d83a9b32e091e54fa527850d62a10129d1702b2387a40bfc882cdf142dd14fe964053822e2eed57fca503bcdff8d2112f9140e5e9b65811485b039aeb582819a21e002cc0f46ad826ca062ec45a0aa58fc3b527c02d4048061cdd85104ae83142b775c4fb72f729e4f1a5034ba5b20c83daafa3047523c6546c0ac5667d3054659e2377298b27d98f50d8e227fa446115cd6eff6eaf56845c541f9b27dc01a3ea64a9752c2ef4b7fd7055a04f06290a486aa66b14f3153f0467b8dd9c3b5de4cebe7f4396ec19e67e077eb74363201ada92c64b5313b972f20aef2d4408df13d05cb37768a2e5204a11cf7b58f42c9cf0470dc841cd29d462a5da291df5f4950eb042909985f0e63a2a6d7196abc5eb081530f595b31278239cc90bb375957734f70ba687ea04bd1387b07ccbae13bcfbd595f57b296f7d0731881abceeeffe67178604d2358ae0109d84803662093e3c4dbe34c420c3ca1c6aadcb928bde51afc18776b3b0269a68c79f021b3af2347f5d3ca8ebf8123a96752ed4374479ede6efd252270eb97a4ed57cf38fc82c859ae4adac0addf26dcfc58ee2cfc30d0c92d5cb3cb29f9b719e05899960b1343f0d5953f074364c5bcd936b29b7696266d83bdeb2446c4f82723ccc06256db1b3e1daac59c7085ddc406c9ca81cc17f4b5d7d1c3500d89fc872a8e235a7aaf296cbb304ada7fecacf481936f9ef7c734f0956460e353497e6d7dcd44c04507d46a3cfbd8d2155e92e4fdcf68c98698624938f2713e359349b0bc04cc387f3c133c12d082e8a56b193e61581a1d70c802d31db9ce5d5cd66a6ed27c5b217561b97e6bf68fc9ab5d64d3a265650d4caf719a69763838999fef033694e72048334c563ee01a8f0952f99ff3a7c7793827b03b59fa295c836e9dba6a19e4d6c3af946f180a31e960c8fcfbadd58d4444584afba3164a130458570d3f8008abff676f9506a62048aac68d06b2606b6ca7ae71d091cdf2f636255d9d41653ddb6c5f5690f44c530826738d319df2d5257c74dd1e953b72169e935546922cd142b355f1657e2c500daaa809b04aaf04133b2e6e12f6c59f58282dca0df89fd0859f0ad7bd5013a74df96e1049057e96f8e1fce2195cae95ccf94d8222369e8c5c7e8354184e86b120de6018e9de6a1507235aaba807a7c0190da540d7296eef982cf23ea018f18774ad7f88c4f266c43f44e6b385c61507464c22b01e28984e593201a70a0ed4a6dc5711be08fbdfcb8a504cff3055fcc116b5f8b67b01f04476879ffa3488ba2d007256117411f36762bf02aeaa3bff2e2dcf19a7fbd188dcc223680cae48ff04acb853de24f1bda9c1ff3e5bad4d34f62295916429942943dda452ed3e457daddb22d4b230a9232265f11cec758df2900cc70eddf9acd0e2fb336522e118298b053c22ea5e076cc8dd2881e1214706852dd78d40594b2f0a8c292953112238745b59e93c4cc4b527f6acb526c7909e0721efb8b194b30c8e13d8d5f02920a9eeb59c3f4a46a9562d36051f5db4aba3f6138e50048c7ba58618412d1ab725d529a76f49fd7edded3b5d89418f52c66a34751bd238479e2e6b941b04f7cf2bb68f3442d832559380ae00baf29f7f233417b8286e0522961dd76bb38041442ab877134ebd1687ce9d9159528925f1f519ff6dd654e9270782f00d8444af3a46f481aefc2c04b5df27464b41eee3596063407f823dfdc38e8b337a8983c08b43c8c9b94b1ceb1486f44776ccd30fb25e804adce7cf0464648ed8c6bf2c98059629c20bdcbee7ba74ee840dd980ffa4bd74ff3d88214cca1708733becaa5521b31368b6f266cda49b4b7d73687fbd652f724d45d3d8d448711e9c9f1ec486837cf61cf2cb514e71e47850ce3b153929bdc519e2753c894a1525a34ddb733662237636fa9e11a24fed1365f03fda89de0874a735858e653a286bf180e189b48323931f63e409e7a9dd8d71379a2d2477ac318df113fc2681b6c47d237b444663c311c9ec49a16d12fa54babb8405b343b3a74ba9c2e1e96b65c492197ae666b995d093370431c4f873f0946ec813c82104dea1d7a5c37d9fa394315e9f842fa7bcefd4af8331b0b2ab789e932d29bbe9e96da97e9918a174550f76b585471be759d6afdc42e05b2a9b6a7e6f86738d97c70dcdfac47c033d10ee82930b4376c5b9ac292511776c653871784545330aeb66e54ba3c3fc57e911178bbd931a18b18f2aaae27205938c16d33f6bf94fb4664305b068a9de1f1e482ecfc112dc5c089c28c5b3118034cef6ffde90944a72dd9d36d7788e0e11b670991a4eabcb512f8851a65f129c59b5603738a134f675a2761b2fbae599637fef991fbe4063fe2ed4805589987577f16cf81369ea8a88b0f669e7c70513e9f99b872da1c8e118786bfbda2051f29f16bfe4f620eac1c8422721664710e967345b336da01d667c3e825d2bbbec5af0e048a61e3ed9a5db5da43be9b5d1180c869853699ae5a286f69ca047a4f63bbe1b3caf291efb3f7747d282dc82df41244daf9ac417e601599bfd8bdf4f32fc8ff0f4bbf17a35c68c1dcaa596892a9c26e54dbf18c05980c67dd487d7441b5f3f23e43bd34dba7e015afbedc39ac7585fa1398805444a65ae6b14e8e882b899abc5e9314660ed1879adc3767ff7e38b511ec1ebe03a83ab97523daafae1185a3b6862114ad6ec0d8d9c0c414b68c91bcd40bb068e429447ed58ecb22ce0afc0de637c34ed454e9e7caf02456673be78aa820f386e7a92aeb4995268b57b2ec26524a38f4c1b9cc6e89cc8aeb6b8c21ffc865fde9ea58fac31f3eba98616da7d4dfc656b8a9fcc3b0187cd4b63470973719a97ac96239227ab6a7bd9ff4d1b9809d875639141619e1a9e1f527809b3af6a4661306c72ff836f004ddc290cc6311c3f1378e5f029beb1ff8ac7a24f7ca1617ffe36d6efb58df84f7a629506e32929cdef4e52da24a311dc68b38e817f287d3aa5c2b48eda2279619c87947dd3169416cf2a19b677be61d84249e1330f7e5565c61fb201b2b78882c5f916894be61604fc389c9a4dd7089aad466cf75c77d94a3a66ce74e3472da3198698c622ce3e88182ce30041a6c2728a4fe8247222f29b74a8075338804c6ad9c85410e3a1a460e22de7ee42fe4bc7f5f6de4a52b756f89111c538e6a81abaafe5cb7d96c99db5a5618a5ecfc1b84794eecb2ab879def07784f6ce8dc25e05adfa2a5051df167831ea2477d630ec931a1cf3cb286a9557cc9cd3b9a204f10059920ef58f7a48be58e3205d0ad41e06d4387ea176b5ecf770fa5e66c44d091e82fefeb104141e506028cf2698b17c7493482971834b94e84a137442006785c5b5bc97b219ea8f06412addf7fca972e891f1b25574778142c2f616763742e61df600a08a35285f66841c1edfc74c9fd0e19fb61ea2dbe59dfeff0127f40df742aba9278155f6260e83114df06397fca9cf0fac06d19978daa5cb38f09f3aa946727f125b22343f91162980aaee8bae90a6907f8cccca98276482717b5cf05128c8e0516966c135954832b821f78f614633204277e833db60bcd185632544c78f6cd1d71b1c3990628535122a4801a8f100a7412e1fd5a1725ec85921402908da368ca2f4d2e538879e4a6cd3f670afb77e26a3746c43d405dcc3564d22cc7f8ef4f02e41cbc61b6995853c800049a01916599d4b440a1cac0df2804848aafb676af76a4847f033c3dbb2604cb12cd40f78c151f6cab27d221fa59794e6441f1e945c0ca84d142206653f3da50a3b6dc70bb408ddbb43c265b73b68cb062b98c5621bd1a0b8f45b42c25c379f8f2a22fa1c44da9254c7ff1e55841dadd451c2206683ff15258b10e9180a64e4db9fb81a116af398d3aeddd95109908c9742c1dbabb0b9f692d40849adcca7218790ec9c5a380b708a5ba490bda5d1e0ca041c12afd4e607455c1095b7978742619fa3bba10c25b5272dbd9ae43f0872824ae97eaf44a68d21ecc63000fb19cde2e64ae4f5fb5336460553c359be2026b76e3e25620579fea81654419643eebbea2a2bd08f21ff2fb575795f931fcbde138a4e57a81874179a90a54f600a83946710fa745a092f319a7cc4c0f76570ab96e40d6886b3103c60863c9d8e10e8f8f674e65fb0bd41a2da2165cd120b595fa94e5a34174e8a39cade89cf01dedb8029e2bf0abe683c94561c46a8e2cda0096cee283d21456764d0483a1d5a3be4d3e360be005bddb020fde14ad02cab1a748361199c921440e951e7371ed66bbd0f4b1106c2bd9e3d66ff23e59ebf679ec29d6bd4f840dc2fa27e72d9ba8039cad2a4e5eddb1f4c7c44542d5605a56addf84ee65a04cd4015a3757829cba240517033051c3c4e662cd8b9e193c3ec176a8738ed2aa7c87e127826ae25ac69f2aff7a7ca8f5f50d0093ba4645425708e7e32e4ee9a7fd245d06ab58551802ad894a245c327197e619623d73872a79f58adadeebc7b5d5bcf78b617e6579ceeb5fbb75b67386362d15c6a5d721b37128d2050326d42bca078ae89a06b016428282acdec6f75705915715ebcf268fd2ab72e4a576f2c2076d9957768659522d61ab686d01d605045067dce7b7485456596eb7af39ff31fdbba03e000389ed27696e1a3bb2c77a824f8f7768901cbafa23958cf38cea71c87defb93a7d041cf152c5bb97890a72b0529d86bf1f95295a64ad0dcf7b42fab835442fda19c42445ed2a59e32a2d1233cf073ecff305b9ad13908d9cddf352d781a49366f2bf0be2970b2442598ae94783e6fe229e123b9e6e61f26f7b0319a2b2637c53b5bd4483b41420c9ab01e3c1e5171c967bbc6643a9a2a2eacf4ae2123160f979704cb7a1b1e7193ddd2d14780640ce1dce9ff229faa7d2ea07d845b4284165d539990395eb75525a4c5933bb410b0a7c751f0ccf18c132fdafcf5f6a37db9eff12038b6ea88a277b82b932ef6220646115ac8cc703dc08dd92d094d6fa478b162e8fbe67d07dc2a8be8b1073ebc6a2aac32011cf6d5192c402c7378f46bcab46d7708289218b80872bab1014c873f797112b448e775aac7d07c92272ffac9e237461e8eb5b9136bd73ff36d542eca654e9277c3186c90ebb81c1eeaa1b3d67a6086da77ba7ad174ec33899c5e76cd85bb42ccd787f60703ad576f88099e8895c76d81e244a4d69111ea5212d2aa17e8127904f8df8eb12441bebbe2989cc78bb5d16a6aa3a7366b54547ebbd1904c6114f89a4622cf7e845546d4cea91dee0b520664e8114e8e2f688f102d6e4532ef77e08727feae41d945714e43968437238b4318f270686f9debb00cdbbe7cde61841ca604a52b84c8bdc2d34dec367f0114db72f009bd398dd26de2489f7974dc3e1fe5aff269e050c4c326762439f0ab109bfecf53d1b3c3d2889eec3ff547058436248db994ff98f33eb1cbcdd92ad2d103143fac089ed104619ed3ba0bea80b87717dd1cf0057fc582e0d4341648f3c720ab8a1d92eed5a9657de3cf2656b76ff57d06d91558f38424d78ef7887dc6b20ec8f5b22c88e3e442bf643b745320f81930f853df9ba3a74d99555855c1ca182c6bfee7d82d6040f9e7453e7f444dcaf8f9d829b86e9ed09d48b7f87a9b4322373e2bee92c2f3a3282cd6c268dd3bd4d934bc17eec0c61f65f7a04ce21d4a0f90a68d3a603f47534a8beefae20c785b05f39bcc52e3fcf1ee3843bc92ae6e50336ab891f036082a274ea2a3b5ee2c3485df7d18591c00e5859b5cda238bf097309c42c25d758744800f6f02a7b109c56788514f99cbf4ab47db906925ff638a2525bbf5c72a3e861e2e0624e9f7b938c19c9a043e261d194b748c685c57bed28ca995b05524c3111a7bd8ce9ec6aadc36dad9041d3b54fbf4871aa2a6893281d62ab99158483b6e3d178c9c8f8917488f2595a5dcbd6152ae8aeaaacdc627e27e0fd9e0c16fc94cb6e089cf682e242d3c4625e31952995018253e416bcfe4245eb4297f2508a2ab14138a7bc980235c0c6f3fe9a53257d26771911223c362a9ba0db9eb923e9fc6382e971a5d9026c5b38b6ae3f40d7daab97fb5a99ca1feb275d0f594cdfc15b74a4c9a87ad81481d5af101e412aa356740faea982f96574d77141d17d4831c5651e30aaa7ee99addbe319251da8c55c44a941f7fdee151cb73025b6de97462216bfe6b610d272b3f36376a515b207b521d60f2f38fbe5676540e967f70719f326da19768897673a13723e39698717fe34f1d12525799e7d3333ba00f3f35e081fb209271f299ef985047dd4199bf369a929537e042d29daf3ef846e2d5efed50f7f31f61b6265d249b185a49103c2361ddafa0c0055f991d21870da6c9ee93ebcf6661f508eb519c207f4135ae3b7fd84322cdacf742b8ddf3368469eb7655ea61cb67eea9128793ce6ed51a734162665f6c2418a9f923424ef3eae3efdc6d3727515720f75a94a5e8d7f01ad6fd1e37702a0dfd7db5bd79dd9f07f6023989fa2fa82240f2861a1ff1afb5cc85540ede15973b9ffa3544e3ec019fd97962eee446293387d417952a109c5077025e08a1faa40366c926515a07503656eca611e2f7b63d82b3a82eb28a4c143857bf94611010e08da37b8156a1bc586735229c25725836f0652b7576559cf62a24aea7f84a4d65bd1a0942bb0759e69df030fb4ebdf72d58e7a240706512f578c5af38848f79122dee85dbcf20545c565c46d9778446dc15b23bae2f81d21eff886002b5c06ae0188bc8a2ac1e4ae6ec770a9ddac2861259107b16f24a39b9a42ac4c5e9b044535dabd0969dc80a675041bb2182083827054638645281a7b7c2904c63c17c5217c11938d819b26e18a2be85022563070d4964f4a7dbd6debc1d46a8d6a662a914f2f5b9de01502e61dfa59e1fcfdabdf55d472f14f14e2662a5a004209aae45e920f7aa475927f54586145e7b89883090068020a35826268755f7668b9008a34c9ec9d9e5dbce140bc82e36b8e873d837495f004f5bfef69c76bc35b97a9890f44d331dcadcbf4e57213f5366f6e9704c1f0f73d9182919afb801df882be97adf78f085d89208f3dc5e526adab2dac9279ebfeb3eb5c5260a93f2a22f537c828f67c12d190feaba0a8c157a1f105b89e607ea1c03b498cf6c92f97538e0ea1ad8f4d61906f6b924e67520d4c68da4fa8aa81765b56a1e35ea5c1805f7d077b38ebd23f3fad7890f72af959a09f64e6db28fd85f6ed8d95e0497c4108a071999d8566ce052af344ca3d5b252e23fe5bad38f3a4793bf6680ee4c70d1712682a374404cdbc240fc97516967f364a089f525636d8740774f2a3205399de3fe93488940f44f6c135a37a07d4b9db62c663361ba3b4a46a2496ba896a0b4b2fdeab94c56beff142d3aebe638dcd334d23f3b7f8a51594404ec5c38c29632eedbbfe942297edb2c89d8b8727e02472e2003ab89b542c5036165055f8576d4909eea09b8fe0c6a09b8be580a49d8a4e4c806f1e828a37a660e0a46d165d9b2812eae83d92f046e572d7034d38bef5955a1d6ec9d03bb117869801ccab2f54229ce952cfa289c5dc51454876c7c46012cd09ba7267bd7e75422612a035ba2430ab24cfb52c03f3266db072af440186a6edba896496b82e083c615ca8f13e3cf6898685e44db9238248f03c39f0ac93a732e59d5e92824194e357122b8b1e6ba3f4e2a38e5cb8a49a195541ce415efc1284a004506e8b47bcfe2549ffd0491133297d4c768d180be428eda78204559710fcfca2de482e534426033f82541b3269e05024434125fe901c684a2126d0bcb3a4a483865f374c556ad472bf0e0e6adb49f325f1d6dc8ccd428fa97f51caa171bf0d45bd36659304d0422c0e1d3fb4234f23c5f63198add59b93722fb6acc13fa5252e813a98b7546c41c4a260f69ab418436a42383765356e6515eb3d9e7afca5960b44392e07ab2fd112ad5daacf358225243196989d9b1ac0c443f21f8723f0839d33787707fd522c0785900df801d855a98d2d40105f9b3ccf12b327d2937b589d2c387dec7c8d67aa48e15f259f1b064f9e0fca48a0e2a89778e56d37ddf725c4c946e5dbb67771d931231f645aca0d46cfaeee9512b584f720811db193cfe294248f5c0828bf47b40fe272ef56cdb437ea59ff3768e958bd03a2cf90881d962348fa08d67b3e28f5c417efef628a169fb13c500fdd34fabc36fedf44d636f88c8745ce69a2b1acf54403f5c294de9ad1a63dd941b1e2fd68a7c51a8682561f2ca65c70d407a6c7cd596b5a9fa57db1de5803db3723982561b903b86b512ce628c2be29e0b4a7a8a593388ab28a4853d8aaf003650469053555c000c013b42704781444447c88953502708d5035a2cc6163a5bec15f8e5904caba380602694d53074a30e0818c69ac5075bca34289e26141064bf80561c4dc357cac3ab6eb33885609eae658becde2d96ab5780a6bf6466a57540235928e835280782dec1f8f2543f92784ef54ec009ca4e514a24b03799c964184df1230cac048ff969442e0aa77f44e025938a48b66d18d9fd224fa3a06c002bbc1bef1325033ee1a032d4e57fb108720a09680189d4453ed0f19221cc08e864d96065b03bfc38bf04e1b0deddae2e15695f8f4db2f10c6ef61a038addf55d5181d8fda97bd8ca81b178b6a91afdb2135cbe7c33362491728233a05350c2bacfea541005a4c55a71c3ad6b79a56660e53d4dc8e8f39581d5ad6f2919b8308e68c5a1e72a525b50afc818fcc1004a30502ef626d95193cbba12d0e0cbba8215cfc256e3951df9e2918c1d03d42e9911a4627a94563a47ecc33b2f135f09cefa3b1c39b162c344694ead9503c8d5a314637d31b30f9de991ac59974798a5997e5d356217e6127b12c83845e04aace6b8e8331bae661b7121872e44aab692d1ef942300c55777c3ba0ef5806eb1edaa5a6e6d94fd444d0275a2d09035b199d288f23a8b2b3a1602a117cc047787b47a205ecab058fe6d0801ac9d5282a93f3e7b785e14d78d77f2c2add082abb815dc6c4a5e54979d88263f4edb87e29b752106bb675501c3a2653a19918ee6a3d0fef167837dfba853376862e6f46b745b66faf10db849f0ee3dc02f54c3743ed8b337a0933789e390819c9646952828dc07164322ad70a355c786dfb3b55641cdf7b700c28e4d0c561f3bfa3f744a22b765db51474fce12c6f928717206440f04a89045862f3c9f26f390f6f2e925b3e9b6579cb8ecdd763623ba7706d744b0171bcd18436f248c411516c644ac872a585bc310653461841375298265faa5aae40d2f4e6a572578bf1e5888d3fa69dab4e00807d563a6a69be523904ff3f536fcb3cad9ed175e0541b65cadcf29ec478fc76976458cedbf646fbbf4d4a5ddf6ed1fecd61784801741a4b667c8ff7e739afc40b6e22788d0fbb7f6e1ac7eaf94c53c0420da13a462576e5c2901c13dc0586f4911977616212f5bf7a73bddcbba684e1fbc5055d0469368eb590830a9f8334d181a06275aa0300e05acb90ee30f0242908190df7f6596c5b04c44d60ccd99ef238bf4f6fb2b4ed6d7afe1446c6922837a0d3bcda454319904e1567fe2e4baa6e9912c36025da0a752d943e5018d82c2c0a720a224a8447918d5956c6296f27f6f9c954ebc2dd6a0ed20ec6f649f70368a0f61cb59b01f02a4d75952eab65e2e2878abc5912704f790775f3f34a7494c336498efbeb7114bb5b6f0784b75df659abca7b387b97309abb81892e721c0b47606c6637252294b35d50ef902927d56a8a1c910a77a97a3dcd16535411e7f3102fd5b1ad7acc79dfd71678dae69d1f8671dd7d9c6570bef7ad6c79dc5f8c0ac7ceab2d8968d87b4a4eb3a6df3317b15eadf455cb26ede4588756488c714036ef0e11207db5c0f82f496277d13aed9a12008e3eeebc023ca3fb786192182229b75f3be885da94798fa3f9f772fe183b711d5387547442ae6c0d34f813d0adc888992d797c2f09ab44ed8a6cb303c5451256dbdea20bf987564311121b28c6965276796e5d3eb973026fb847f00213611c08dddb887e6b27c9f6c5e162fc65cdcd83a0ec39755f70a2e5128355281444e25254844a5597a1d446499fdcfaddcdff295d347ce082efb7d0a73adbde756ee4bb0cbe399eed3c3aa504888f66fc786e96eec0ebd1b7d3bbb7d037bfee229e4985e1a03a11d1b34bb5e45049e6d9cae0986730f456314ee12b0d0bf3c36424d5977778aa52add53b2cb472a6af92548aa02377e828e159540ec42318b01db136ff89330324d0ebe67410b6c0599e4bf0accf2f5e3b91dd4f4ef1f07deca4c201c70ed752015f8033edf4acb85522e073fb0e43fae4ed95aa8cbe4a726a25f196b3625801a937f0e7bfdbb7d59e8f0c3ff295c4f62fdc9df495ed69bc0467638e4937c9380fbe1a3d5a46b30522fe628677e67c520135f42b0fdac9cbf77ab3b0784a667b8017d05742dc723396b2198e170faf36d535bbfa360068f1390e3e0171519cf7d3692d1cd47d868e3b688ef6bc52467ab151486a4ff9faa08051e2bd683477fdf4d0ee02cbfa788051c1b69a68be03e0296c871e6303c740f800a2138d17e5cbde2df58071b61c6f131df604543a41f8b37bf09cd13ad240ddac0c446613009010c069729839201c81dc7ace2917f91510c9385de3c1e9ce5a6fb671ec204169437f238f0b32b0a15a7c1c3bd7de706c2e1ac8c4a643d96cdc969d3aadc1f1e8bddd825f4539ed101eb2f4a1fcb5e7c8622df7d17aaa65729eb980a0666f8b3ed5c58a7fcd41621401573de1f1b810553017017bad57ebc88b045a5c207250281580fafc6348156677bdee47e42be213d7fece7572f00d94b1cc1bbd9a5582be2a6bdf8dbd983ff859a09f73cded1cc301c63266cf3782100d693beec511d19182497645abd85a804ee5feb2303352cc82277b74625b3d395a19e6786fc16163afe48d1e73db6d978d2ab35a603c7bab041733394aba536c0d0a86a9524e7553ede63e26c38a7cdab6fe30928a74b96e0daad3b635d9d6f4e3b89f086c4ace98d110240984e1de3bf10262b2e0c10eeaa586c7a13f85dfaa22bcfaeac7fb151ddafe28dd150c7ef6e4c8bf37776004ad50f68a5d68b6f7091451c0165197f537c9f8d6a4f2e990bad009b3219c59468831e1904888ba885fc23faa5e0b4be5df7b894ba3610d19b293971facfbeb6d2f942fc3cbb91e7d84127e5a54c334c350fb519c4e3c8d4755c428e7986cf04dcab25570bb20ce3c934a728dbd283ab8ad6dd739013c2d0b1ffc070beeba833d22a01e470bd55cb20cc7ef893cdce22ca60f303a260ff781b83a70750537ada57a1085da2f2dd5a3f059413022e05c65165c2626035cd54db691ed654c40c5e734ebeebda3ca7c889d24edc5341fd16ef86fccc682e43606a5f5367d44ff89bc5de23c7405128e588dbe7bacd6c97435b1b50fa372389f5aecd3a2904190ee4165bfcd74509def0f8e53a7c161f35242a96e4a262af031b78e866b643c88a2664d9b85a23eded4c17c5cd553eb03cb086da0b07406590263e0039092dc087d3045f9e535533d6c0230bdf413f0f100d0a8cb2bb03966c9163831e70168f9e9ebe359241b5d0ce0fdd9eac73f5c482595602e10e4915db45f63bc1d6137e71d74d03e86efc2ff1132419b5bb236339c79c0eb68a739459f4c288f55353709686bc21dfa91f11828c83beb169e5175787fe44d114e64d904dd364273005350feade79b1617b87625af7f60ac71eb03718301e66856659e65560e4ce60c4d981bb023b01ae260ded66e75ad1e4f3f6e59a0e43d57e9bec7407003e7b1d684c29b99cd2dff7629e84b0f993c2a648e18b1029942b3f567d75063d040dac96407c0aaeeff38098ac2fd3f5e66647f40f6b718c837c49d59c610c8eea404c56d652eeb29ef700a31eac782bbdf7ba10f9badbb0bcefc9d8ccce1132ec99aaf29da9b61f2a2cc1d597268b02584020a68574ee4dfb39373027b185dc87d0372acb63999f103f153b93a329b2b2b6074154a828946124cae892e14e0aa9e40ee5d87a50f05dce3034cf5bc0ad926eb5c387383042f116440bee4b782da895365c102b6ec3f71baa89c06f582a2e5350eb06760b951822ffd6cb160884702ba8d2720ba204dd027e1b0760f5cc0066dc6511ee239bb190a790b3ef33bb17b1f1218540c0fdb6169d06d2b992960d772c2c17b87084fc11b65765c3f41d47c84bf53947431a6d6e3a79ca0fb5b951d8c20f05529571d2b888fc9c6f8a35d28f0ec6c985f478879a2363434e5dee47ca075d791a753387df850e6944dd39858127a15c635004aa3645afc056f05f11350f31bb2534276bee9aaca2fb72d4c3f6518a260dc6b4524fba79a826df7758dc4685f0e1d36a8cb7b02b2c5fceaaa41da703879a18a9818064fb799e565d67dd07fc8e1ae55819a1e46b59e987d1a53bff77f4c31ed7fe72488655af39717854941dc4b56f01446b1a53fa180b4385ca79000108bb888b55a50ff7f8e77d11c49f7641baf862d3c4d85bf3f0f1c0cc041f5a8104d9d061337f48c9afb0acb4b224dadf1d7724700c406d071fd98df60a196717fedcd2f2dcde95e0d6873b74c22700ad9b40435e232a22d7022ec58d311f9f82c151055f28167180cc8c38821ddb33c1cfa459af750537ae48db7c9efecb2bde274635cb9881292edba2ce971392f8d9d0ffc20f01fba7f1d2f82a112e318eb2a32f297014e012635d28069e651bd21111c4c66c12696b362fbbbe037c0e3d41a9cfd746a016ded1455b9ac2f805869fc272a7ee4093080b343e143dc34c751af394cd6aa64d43508d1cb5b2725c2aafaee6ed11a7df28a3a099bad448e80ec6768247b2453241b3f98048d44503dc2529b7bc6c6acdf0e0f92d319097c18ecda824f06a2c0cdd8069a48ebfd5e14681cacd5a50d60eced8cba3c475344b8d70788f5685998f58ed1b5eb05298f7cd75f8a5167e8b168c9a7849a12a07fd9689e020d648502a68c64e28a6a96b959978316a106e09a8f192edd08bbff74f8cb0f49c798183343cbe27a6bfbd0ecb065fa53d36897413da0079bd7edc4bdf028e25e7c372b20fdb0d55b16a54e84b5ccfb672cadeebeeda191c481db73f669900c11e43c40ff5bcdc4e4a75e47a39e2b4022b999ce6d338684af995078bf8e97898aeff6023cfd3af3d6392397e5e8b2289f1baefb93ac6203c1369f1d8490ca79f2eafb09911e2b042fc7e371d93b1ca56b7b4df4887a911fc3f2b36e9845bf4773324e6368f8fddcceba7c9e664418642ec1fac797ed62d3872b6c1c55bb6d174fe4f747854fec37f6ec4dfeb6d0121a1f720db3a1b3144749ea28f748cf9e28e12c974f9c3fbfb751309faed0c1847a23ff974a2ae57cf0160cd68f973cb42499fcc9ab7f47524f6a64acfa935c3b79303237c6d804748d4890bd3dffa069e181b8d1dd34b44a8de2f7d17b5b66538ac9c34ef20f9bae2f8709b2f05dcbe8390adca0d015850b6d707ee0a1d0730ae5b1f69b20257283f0c9bea1978ffa317a18af1834552fc670d0a7074f77dffdb9e2563790277df62e04d8fd6dc3189f4b2286ccb91ffc6e153b4022498793a4eb0032b18543c7586c830b940cc50e6c79ec0a0289cb6b5b47da2716f02404025d6520040bce595e428f231c7ff54188afe498b875c9323aed566bd090e0d2738ce010c6d570a7cc411ec4d9cfebbd660613dc814ee633a7b9d3dfbaee1e6573371e2dff550a37935a89a786febdacba34d8263021187d5c47536e9d10a9b292d162b88e5f4d3329b566145cbbbedb4090581fef162179ea405773f4da3dcc4f0f04dbd4d3a6c4af6a428883ae1b2d36559a2237b7b8cb0a25ba9e0de54b74bcd27f8fb4abc8cdeb451026c03b7a9392a987e7b1d001e19643dcb28993d345e077726df4b1a4cfdc0d35a6cbd1a85d316c8981b4e927b694eba2b0b02f8d379985bcf305e66e7e1447dc34d566a1d760d231cb812d55a89a00a6b5b7f97d873687cbea538c081210d4a25a6160195d676f57c9f2d18646bf67d3dca577349a4156710c92039ed653d8b662bc147454fbfe005ec9e7796e1e0b9df9688c5b8713d29a122e20b38659bea54ab51005fca173b2c9893ff3e765f8576b8feb894efa7cae98ebb4ef3b693689a4dfdad176ef9cf9bda1013073a58d9a6a58c87eef9c1af76b202b7dfb4acb41d715563a4453c2680baad2b3ed0de2a966c1439f1b6b94f7075eabf607ee9b27bb400a6324023b7a105715adc0c142a9db2a085da3bea318a544e1a3e48bf6dea35d044ac269086a28f788777fa10a45010e8cb25da38e53ead8e13130ed8d73fe307ce244bfb6cd23bad6981b3dbc22925da5ee5db64d151c428adeaa234a11b2f8e387a620747f381119f0a48b993457125a6c2bfd1490a6c1f6443e9f92848087dca2637f32af6018677a7c15d7302548778941465966dd6653a4d73171046fb8187b621c47f1291e81cbb37d207b536cfe102200211f68ffbca31f4c2e9c0fca07a3a06b6b3302afe7055eabab53043ca47aa6bd5507ac559ded51c7d126bca9fb3a9a329d93424ddb5ec9a897fb81f922319c76a2a2d1b467c271f00f83763b0699079443c8fc0951a4799c35292862459533f65549a4e56fdf0e6c4ac9eac1b8b2b69e980aae7af4e3a39dfb24372f1aa1b0cda15f9dd716bb4a0bc3663b67a25295338e73c267943f2e53631846a92f4a30f9e35059dbd101a6095636317032188a43994344180304e4f2a09d2cd48bc5e9c0c3aeb70ea87221178fb367da1053a953b03d784425e23c7e0a3fed84cd9c95334295b092f91cfd6bbfeb409d554a8b055a86bfcdc5199d4016b8724c8861abd36754a0c88e507bf871cd8d06707222326098eb0b2875e895a5b382a10784898be57ccb53bb8cc0602e2f8981e7800574c1a07ae65c030839cccaeff259f0f6249f3defe312a6fa04e2901d75d5e1ad164465f6ab1856fd34d949bbe08830d17e461d96f512ba5c0a14a4666c460867708c6a3471c6538e217d9e1756442b4c44b53622b144d0a01b32d13e64b2f7834e4b9aeeda17a66fc334c0a5760c6bdb65220208630c2269c8608850d1ee953b91c7ec60e2d946da2d3f5060ef4e25534b877f1206d0082ab4e11f1e36b371e2cfe7200a2aaa311f6d5c8e5c597574d31bedc78412735c33fb6faa539e62faa3d085f6177949cef3074fadac5358716ed1c3f0710f5af177abed93cdffc136a5833c1f50b1bf74d3c53ebd81984ca4a172d2deb4a8378cfd18b2b8f7d13ebbbf4c19ef5b0c44d8b9520ad340924b12bf1563dd811d0828239ad6ba0cf2732c024a583fa5b79944f050ccd9b61c839b9e584d686539674cfe877b396f7746ac389790ea3484efbea64cda6a816bfa3853f3ee1573f0c08fd2848a4bee377129a2a3f296ccc2f408995fca8773e83472d3b61317694c5fb97892df9b6ec38704f08e349322da4350250f6db5cec58d372f3d0d5ea3eeb08778251e2a9f185dce7dd833f1788f14d2408a73f1d517761e2fe167aacc8c8613bb27dc53310e2527e2231ccb4344ac7fef1a41166bb6b895466db8c57d40a873d535dcaa0771d4564aa000d574b377a377bdeb926f5a59619f68215fa0bb6b1dce4072f43d559885677945dcc524f044db944a448d7fae81e29505997cec33edcd7642e65b9ba4162693fc856b83ecebf065f9863f432725e65bfdef58c40c0bd21dc2b88cb765978e5c04d1eda15ae0f96c543972bf15498212ae10a05f02a600eb2319c33cb7807a7015f8ac2e2cebea9641862024b4072a74b633fda39ae150abca13e8d0d9b75ebe70828994ac68a89ef1cc8f0828b67906d2039578a17726f4e5e0e66e842a8901efb4742649df65b1fe648ba09e2eda96323072c3fc662a85e8f2306f4ed13071bd0c6b17c75b7684e4929685c5220bd9b1ec9557f8a11421685200f0195d28bdc72d34f9e2d62dcfe86170f49833ee9fdb7d7461afe14e8462eda91f833c34ed7d5fd651dc5a6e54c80271dcac260193342d8452e031cd831a39cc80fecd804499b8e5221d9100ba2c331e179a21babc77b0b177584584d1d56fdfc2abbeaa97e186b36fe109677cf5faabe3e1269101118a8f31dbcef562c150d6af53608e8ed0db7c9b749170b4c11df94dc002430c0e6594fbd07743615cac42d1769ca51ed6b8a2dfd069c58240abdb106836fc56ff860fecf80139406d9260212e630564013ff73dc34fc77f4660a138f67d5e5d467e8b3a48afb21798283849b244b67ce814217dd3db093ec8c7afde582eac6e7c2d4826bac75659c9bed2369435bce314dbc9356f49633a26d36145cfcb1c96a9e885fc0b90cc5f9b73519fa1bff1afe85da2417c024039dd55a49f2bcc833fe2868a8af31db2a8188297df6e1d52a6b93896ea2e419d2db4c28ae68122c751bab947ffd6c042c0a9f64b897d180b77d4311280ddcceb7beb73f25489222bb29011fa06c2221a5ad944e53519c402ef0bc818ffe434196a37787d645e5a57428e468de33bf72f3a461b7b62d272a73190e741da4f3ca2cf963138e16e4116d24b477c0888bb74cc74bb2ea40b4046a7fc1c10677389f41726d6d755bc5fa9405dd438ce6909f14c3d8da7a7db14e09633b3804b422080f901e2560e43ddb6d70031f0e1f0e8261ccbd2d23ebf4bba10148e46a62ea26af66acc100788769586b016375fd27795234b4aba2e70aff8917c10f91546feb5c6741479dc49a6d5385945e5801f9f4ecdaf67f811b26eb4cc1c1a1fd04d958f4730ed0d1deaa05acaafaf6021d1b5b18db2bddcf0ecdb682f9b49e50c5938b08a89f2c954cf1ea3b2ce7a442e53308eb0c0d3b1a2a82fa24e4541bf8f9c45684f3d4fde638b214b69c3c840fd465189e2ad2ca1138eb78e2146225efc1d8b8c07b4b0021f82d70a2e09147301d66821089749417785e10575e780d04329680c4c5978ececec8a3e500ee47d89727fc27dc35f81d3edcad67954105640b86aea0310d18cd878efe0b3d5ad933a3b050e3519e40b482f06d6f07e9ca968a6049985c061e81c54bc9fbd6c08f7b7627a4eda6e6d03091b61c119420fbd99e0ec415bf223f4580073ad76413ede89f27d9054446c560fea1de4a4b924afbdf384df32270d0dedde8c5e7fc03666ccf5777a4937b1ed2072f51f39ae9384509b71f73f4a454551af5310b9d6c29a271ec25bf3a07ca374b0aca5ec9e89cdea154849b4cae77fe1687efd24ec8fbe5a9b2545ed74baf0b9b203fde32df4dbb65703acd297998b67a4dea9f2de1aeec6151dbbb80845441365803a14a008e9ec59ea75f10b4f9dba6fdc924c61b4e2be57872cd17e1336ba90461100ca1cdad0d429248a81af7c5d484110078a27bfa5148229cfd560e72435ffabab9aada1591e0fc59e9944870c5c9d34ab7fb292ae4d2a2eb72462bc140c1b96221f4d5494846451f07410573d1bad355f11a42a36a379c3ce17a28c720d13d8275f63ae438295fdcdcbdc863a879158b25f49418f2cbae986d8decaf8c6046cdba51a3c7527a3471ddd8065857b822d50df545537abaebc58296f554e4d180b6dc3bdf7327586ec580f65b6abace08f5a14308abd461d20dc6cd636a28d68e063d669c1fdca0d1ddd2cbbb4e2bec0feb405119de424b911e7e1abe12f5e0181a00479f16d8deb8c1cb0e419a1b428641f169c5840d477c97b40187538d132412a888ada2cdd8b0e12ca8961d0e946551afb0c61ff31b2ade835b083559d79762e30632769a3946077321bff867bdf7464ec72cd14dec2a0231876e686e1103eef2c01e2dde2e6a04f2815b2ea43bd6b558def3fd91fb1e11829b67dc371b72a32c0269b4343cc4c637ff6f6ac4da4ab68ad6a16fee515530631f0c682b669272e3227a5557b36c0ff5fe646c06e77235f1aff5d7d16ddc3c2203941c34164dbbdec0b49d19cdeb57ae8687b06599b2e722469b30b29d90f25f218b767a7b0887d146c65e57f24ec38ecd1acbf806e9d6e1d386e603ed37255c960a783dc745865e776d0c331aa551575d8e36a25b11e9923afc72b41194eaefcd4f7d75dea3d99edb82d37e13f6bfa8fcc9d272bed36ca89f68113a3b5864d39db8c00e5e50891e98ea40b2251e9cb688802b13ccdf32eeed201eb030d731ef9610c85c42e334f09bf54adb10fda05d6211f05755d3350ee2c334660963d294d3d947a8a623b6f64d07d4ec313141a170f527df04235405c6dc8243b555627e5f8877a2c4666c2d7374b9b2c3fc6619e6a9248ddf71c959a42135091796b29802d00587a83d5c35325973c16773cd53f3a0b45f1a5dfc95aaaada7068100c826aba8630755274355ee45ac31150b44cdfe036975eaa9458bee4722c01076215bce1ab3b54f98a8f4490c65d2f815bd33997e9c158a23c942a6623fe8c3fc1e27796c0eac5afde7fe990d392b306af98635e44c431fb689f417c0d40062bdc7b3e3bf25498105da50231b8c80b64e0c63173667625c44c0c2e02e717079258630de0e9f6a82e631ff6bec98eda69532bf09b170b52943554eb14fee6865daa75226d1a84a0987525ab09b5490db6e59b08f0c17c4528f55d35cfb4fb01abb9c45c57266972a907eca09e1e9a542b0829b6610ffbb6120ca83ff4b2aef68e6d547007570aac31396bdc00568113ac1b263623b9a8361122f9bc5f060047d5f99c30e2ed6df63d69dc0d748ec260ccf26acb74282528d12680b6c6690cd21f7f0b787e69c210258b0443eb850aad11cc29d4145856fb261dea94b3a394ccecb5cf46a26a8bff883ed0c37089aa557e7c4729f93ec182dc3ed54764f23f144313015c9861488fc0538445f66e345e4c09d9c8df8827400bb1746fb6325e2603a71df5259f07da82660afdebb3094f433b44a8c9d116a5b907f57643b8f4f654a32260307676e6f6047e178715943828ccf556fb8d273970e03cfb72e0f0366acc96ccb79af81b9fefcae8cc9ba67f9814078c31ec01fbdd270d2b2e7f0ee6530fe44bfbdde341fe89ffed5ae6c6db25e923f8087bcf282712e26363b1ba0b7f10b2bc772b152b379cc26d6a9388644126815c977fe4ff7067c39e70a92954eb5e46206f670b6267a39f811d20f54322037b1ac5495d04a2f361f8499bd7a09f893c9f694051440087ae64d38aa948e72b34e067d3af101e53b271c56a748d670d37b49d824b1c5dbcb2e701781448030826c1a045010e3996dd956bd19cdda501418d3ecf6f3d2137a26b8caef1d68043e7a8b7cec7866e2572a6014f89d3bb964f92805c62acdc66712466fb738e868f57cfd12408b777de3e1bdb57ae45efd6809f4d47b2f7cec7866e25722d149efac5ee0a129a758e0e237a697ce5b4a0b1e92b59d9b2e02adf24adacd22fb4e2b6c7d98b819bb4a89f237f82fe22fa131a8a2a6eb7da0be1b64c3bba5d2a111ae5e3216aa5f0676a4a458b4049dd6f0252c5567f79aed3118ca14407bb24c59f9d9035288efff570b35b48fdee074b334d2bb1e6cf74c42a7e407c340e4a84fd84d028142deb20bed0a558fb122abe8c4c41912407a8db2209ea1f385ed0055b818e02f8c68c72aa9ea2a424a14bf51b21acb373f6dd67255550a845077a8f5a8b174273e077f3ac43eba2c39d23ea3082f34d96f7d7e6701e1c1423f4894b12900f4ee57813ce3a30cbcd84bda3a0a8bd00bd0a4046659d8645de69ce33e7b3721a818ff491cd8b34ba0b1a99b43445447da7c092a37866ae75ec41166da19bb05d7312b6b72b02ce0ff935bdcca34656300615ad1c7580e3c987c0a5f052cb7a278bfe174622aafa236e3b8dd50c4ccfd8199247e19ba6558ae8dcab1b2fda41d6c95f8d27ffbee93a2381228b56855ee497bc50a6f19cecce8275d5582a0ef18857de1c3aa7d1d7568c900a6c805412905a2101e2c3e039e34383e62d0939e9208980b6847408278560a54c98943633688b94743f0d391a2b3db723f29a5464bb6edc4f33cc6fe2db240dc7ebff91ff75a7b11d88ff947164b84ed1806d27fb909ac2c2110d18d370f3dac1d12dfc1b21e26657d98e832d3bd6f82481c43c898636c680c0af6c98728d73e521d4d76ca72834e2f2d97cf209f13596e92736cd3b9bbe7edec0b844fb0237cd9d2346d986ec402081cc171e3aac678d16cee43b512a2963ec732a4c55769c3516c034f91b262f57b4ff11b21d9f042c39fa262c730074e879ed83cc3fe7380cdfeed3b730f4d9efc1cdb47f585b84065c9626726529276afd0e8be5810382e0a8d23ec8232e1c8f8a9c1283381d803aea5f850533530e2994fc3d69173a55e59bdf16c2218a8f1199f8b9c94d3e8e5992a04c4161ffaa80783ffd8e47a700a65586530d445864558e25f81c34eb303d25d21ca99aee91cbae21937bbbed72117ef8b9a612c6458fcc0603c28aa9da51a15eae11a158eca4b8ccef52208c4c2710caf54a9d64b3285299001376dd668f811766a6d669ed13a5c4f37c7e403a7684be8e8d0394df045c4e0f2c1be501911102b4180e097ec19c372dd2473b2b373cb52589fc6b5c1aeddd55c49413315333e493623840c90470984ec31ce9ce800c85c2f2c13d5588510360daad7c394876f11cafa245b51f76f998b7c92c91d1c1fe4e8567ce45efcf777cf294b82a5d841a9c53cc42e365d896c088c9ca56c0a681aa5c934fb2d48801847605a7d11d450545d29af49eed4bea9531ced2b1c9c4895d3229b3199f3321517bf2899eb4efdb12a61aacf8a27e1ff4341bca2fed094071fec450a2b526d27ea8ee3a291ab425024bb64019a3d19c4c90fd0a8598c7a977461d7a05b5ba508e1c9a18f6bf3a8aeb6e800238a0d3133d88855bed8763d9e035f34a0e6cf4fd7b80cc2b28c2a1aca7a31ea73f84c8e98350bff8487e52762d7313f510caf2f24481c1d9e2d2ce3232432c27b151b417a5b5221ae53b5cae4a1ccaac7c67aa34af97c495537368ebfb52d24427018701eaebaea355818d1f6506353d45689ba4a22d42d6371a739792b000e45a47f62837c2c848763131728673ca6db8b492573174ed3fa159c4b469acc203548d115a7886297362b6446c88207fda653430394cfdd31b3519ee5685c7d54bf7b392448f623a8babe3878bbea00100f463cc152d27284777a5adaf94d0813470af7f2963645f289742ad20ccc925f16e504e76ce61582546457f9115ab4e837d4ead8332ce6b199cb2f06e0ed17f2e910662266ae957716f795786056cfaee284f65250dee81b192f4e6e0642c9c91475bcd1871725bd4d136c92d191a38c9c811007c1372f770055da7c57179e488add301518f806f8c944e5f4df539fa345265ec29c29d52458066044d5add20003d88c8b3a9305c7c9460ccf6dc423c76bb9c588b1a269ba636894f5c565da2bcd50d88b218fa49c47cc2737746ac3326d2a95595ca3b7b08407b48bafd83f107519d06ea6dd472e5f591219abfaeceb23a4177c656cd1e21140cafb59b3cca9bba5435ba3a9f8241fb48cc9300f1b822909d41ba2c18f9e7a69e923ecdeae1789e49519f25b86499649243db84b888b1af2febc5e59a0eecc99a15009f7b74e05ea11567d3b9592be80534292e4ac1e0a928520f3cb2d77c9642b93b9467fb83a6156211adc7794a13cda3139f01299aca735a80d2154175c542d610ae96cabede82a159c151b1bfec0b044851241dd311389860fe338f1a82a948e49926f300699a259322bc02c40ffde72e1dd3763e6605041c0364fb36f60b004f1c98779a999bc07fe666b2d204f05711624f3d3d3741cda437e9b052265b71b3a48653cc3f53fa45319add308c0e00fd1a25a77526ecf2ca561be2314f72bdc7319759350ef7a066bce665660626a15c94ca202870ce8db66cddec8d30aed6319b08393eb7ccd76cc0ebc12f6d2c973b28407e5dd198840a14dfe414d20a8e78bd13188ea99e0d6c27872bcb58145d035150e33fdb287e686bebd7d46a47906141c4650b0f09c559c2b6f1815e0729cfdd3c583f9944fa3c98fc015f4b0073978fb92d5b06cc602a430249864ac2ed047b883ca0c47b4203972dd27cca9c89e237cbb07703a21bd64193384a398ba9d51e1b9b667c9b92448d45a68f986ace783ef6e8f1b7af34ed64b96ef55a4dacf21a257e5e4e8a8a8274e185a4321481fc7e1552ef25fca2be31de2ba80d989786cb13e418750017687ff963940474d6fdf5414eab240e50f11cbd1163fc67ab0d0384e90f9a1f87a5362e7fb4678b90a8101238628d07b54645149d6d362725294d835203f303cc8b3ffbca959b84fa49bcf2ab47377701902fa72bd0b50aecdf19209ae6b5e2f01d1846627a7fdcf15f044396797d06de1c2b5707e28e902972c0edf1179b589b8f74b47806a4d36df6af257fae79e77c42691126731a1df86ca112ab041607740163810b09032dec6ec1eeda687b2f45aac27d8ca5261010ac608861a957483e0aae52cb308b1b885ad183dd820b22c276f1171bdf95b971465932fb6712b9717e291b6092c159d018a0c47eebeedabc9280a68538a4cab5fa69c77bd38f2d7d113181959c52421cd53c447d59c15ddca3c00dd352cdebe190c4e687be9c920715e0246f848781a1b95bcc34dea884aca8cd753cd0471b22391cbe8b510b0601ef4906801e4b8d73ec2003ca4fee775a20d0ba4a07d56730c0295a541a78d2dffaf992f0f858021a96c9ccf1b1d563edefb7bfae26568582da180a85e400db8c030a14152c81ecc2f97eb2fd8bb21ad0ceb896c83216d2e656d6383f0c37fb9290bdf7de524a2965923258070f08150740fa860afa86d10c7278539453474bdb1f47913f70ba31f2570c3d5e06bc751ee8fd6fa2dc1cf90b85f79c6378051556f888807c37462a7c442ca1fd2abc0a1f51d077b33fd6108701b17ec8dde37b498594269874810e48b026309f0f6f8a7a1e059dc2eb68023c9076cfebd19382c6048871767103690be36807a4b1b81061fb75f40c78f7a8b2b7efa1ef1807fd734cd3d10378204d07d0e9e8131e484f588e4d9d9ba2d84d514c5f14348ef5d0729fc0fa5b46e4e17f53147adb6f8aa68c3db46fce566a67eaf6d071ef9f3b64b2adeceffa68568fdf3dbe1888c01ee889fcf8fd40ff73b43f2269e43d8d1173fc1fcc87d2a829e5fdf0dde37b27ee890c801e3d400823af78bec7bd31228f4ecd87fffa7025800ff35ef9f810efd547a92357ef3df9ab84f7f7b6f88b84f7f7983c59e4e931c2989c0f573b3ecc5b25c2475785f0d17fe64dd01ffc859a3a9b08ec55f8e8454fe8e755f8887e3e7a3e54f20fb94b78a0b87974de84dff9509aa049d071f77813f4009ec8f68e965ba7be6c9a3a3603b2554ff861d7afe161d77f7de14acaae2f802fcc36875ddfc75782ae24e8eac5c1fa3d746d8a39f55d3a3ac8e30babd3ae954cd1d4a960bc8a3ca172893cea085f88778ddaf573be702566d7dfd1c5ae2f42fd10aab4be1027dd0ec717ca8de3a7dedfe3716e1dff2fbd166e5ec7177fe4f8894323d1f172f74c10e741d052d664e7d038efa359f581dee77f34ab7e31a420851360c0887c9e49ec82c4171896f9fb7f7e264b0e6f1e27e4f1b4fdb3931cde14dd14f92b00dfcd43b931fa74dc5ab3e6e3f8e8fddc14bd66cdc77bfe8d66cd2f4fc9484749e41b1cdf0d0836c8f6c39b2222b0df3f3d223f1ee8a727f4f3fb237227b080fdfcf4847c1ee8238a617628e202f3f99e7943c76da39b6c960eef0eb67f8d0edf47ffe88903980d1dda1a563af4c8d8fe00d0a127dbfe1f5ca8f0860fa127e668fbcfd0a1ad614687954bbe50a1370b6f86a84e3bfcc0a972d9e1073745e10eed426d0f6f8aa41744fe94591cf41ba9a4dae1cd4dd176b9e4a0bfd0c4010ca602ecf7fffce8088bde0b03a0811549b01f1fd0fff879a0efc7cfcfff7c373e3ac2b65601f6c5f6ef2fce567883b4fda688476e92828fd00317716cac6431d1f30fe3c8418eafcb649b1f7274c9a2595942dc7befd14eb384baf7dea3de4c74103f8bc86411710fa96c470f083f27830719818b9de3afd203c263d7c79a0564845d7f845d916c2dd4d74219bcbef6f591dc67fd88331ca26650b4cca068d9568fb0ab1e1a21bb175ca0ba1471d9324553d2145550b139d58d309c12352a68421be130c2a1435e01084664a6003c71f3458d142024d5fca039a0ade0e84096924319e35270c3839a296aace450c66a9870228732a67918228732e6c12000b21a261a9df7835340e3a0fcb28379ffac62395c6551e1a43e0539fc1d0eca0f44814b4c0e5d5c113838c011c1a1e0a0fc769cc8e18e8f8352c6c24f1016238791498f206fe186c83be450c66a64399431ef4bd6b49083425e90c3d8b4805ac4881c462707e032f543182efd7b63f493454dc45beb17c2b0daf439237abfcb3038288fa82087f149866f88c8411c441f26c0b3e52ec17037344c707fe1be38049d2b3cdda5bb1bc1b9aeb2c8c56da53ee79cd6ab53970e8626d3d0903e271929e5087c62f71070bf315ead7cce29e58e313a148e46ed318756393dcf618294d2e952d2a0c9be9f5e22cb3293d259eb153a9bf83011a51336f5df2014d8f6ab0eb55d91d8874d1d6130987d6a6ba530f905a5955a5bb5d44042837b985768937dedcf79ef4dcafea22fe586e264cca1f4e38c545280ab818426040cd0ef361005c1c5a87d19bf48132c25118104c4919445f2e70d51c7a76c16459efaf2f3a50d88ed9a932f5a2ce630c51cff38c60461247981a4c5d117a3c9a5885a8f79d11737d2c28fc2f0243cc64d20ab1f66a2414313b34ac898536598fa3182af13a8f65add6ba9fdb8cdb0e9cbabc596fd1376f0bbded686a5b77e72dbf66dc86d1975c24d1c3d904d31dacc74f583d098f6d2d8f46942fab180d06cfb34db7631c7df77f4d78cfbeb7684c2518aa30c572328eb279d7c4bba770eee5896ef0fb65ad60bf2920c77026ee5af991a48b6bfa4339b4a60bb64229b4c9732ea2f3a299d744e9f5346ea2f4e16799ea8175eec38c39f6462b8bb47d083b86fffac945b20a941e3d2734069ddf16df6970ce23f9af89e76732abbb928bdee5c29b5bb4995b28b2c6adbff8db7c7dbe38dbb553b92273938bd4a1726c741da4f5ba3715652ca3d7d2a5ff829fd4ebef00fc5d4fbb938c9d0c6ba40f06f1febdebff2c3f57328de9a56bab7dd53a52c4b4a44b81fcd169d44441e7c97f6f6d7afbc5b15b2f86e784a2cebb828ad7aeed3fbf43efd41b6752b0f486bf73838a3a4f8bbe8fd580bc4359d64a800f42fab7eb802e7df1e07e7db38883f1fecfdf04d3fc95f48ec570de3d730c658f32287e2460e26e5d091bc8abfc28ae454220ffd591f2751ed3abd0250baaba6f40669168438e845a96ecace692a6ce251a283d4c75f3b1ca41f047e8d986c3f646df6636d5f1070e3a76f3f8c4cda074e1b35c8feae4404171f1196fb6d9f7a22ee553fac21f5f3bf503f7facfb1196ff05faacfb5999f1113623911cb4331acf96ada122cb2459e541c822fb0f89b027f5e91f993af2ed0f4d1d7ffbdc9194b5bfd9e7b4d7d4753ac2906a589087609060b2bf7cda3195972a5962b12f4568359051a54a1553a28e88f0242b0e0e687d7a9271f5d473647ec673a544119f39849834c7183ad80b185aa154ab63087b755809a3972d2b2770c7355bb7bb16c4adfbad44d8ecca09e7c8225e1bdd622d4c0ee3936c764c458cc81c351a50f840e609281a06fc3583dd487470d15ffe313a18e260e0c1a9f3120a0a6ac805656428cf1d27a81d27398c503b3638dd0aca678e134c5bb1ad1fe592c308b5d3b4736cc9df5941714f5cc429e1412331673e9627d0af0142b8c072074d384a06ba7ab0fed9f22179c8e7a48e2a39944550514d63c870794beecc204fa0d7c68982ec83240f10270a287f8571cc9efeca1eb5125344c9d479700cd63b240fd095718ea61ccaa230568cbf28a64d36fd524c7011875bf5d44499a4814cee641356e09e1fce7030c716d92ef5ccffa14c88018334860f5b94b8b2031fb25ca184c35089b5208a16f71a05dd0e48312c54d215f72caec5b7cca02cb6af693a68079c2d293d0c1dd05b524c94d92e2c983d5d4f4684303131050531313105313131e15c617464a1ac18374671b41845998d3c12aa090b557338c96164b248b12afe0aafd1926c33ede93756647474ddf3172b29afb557bcb832a50c2ec8e04204b50534903233cdaab5e34c690c1519ec30ff9c33c618e9a454ce38e76a85f1b437703430d0e0ea73d63a67d2956a639501455283a6fa9c1a6e22a9dacd0b426b144fad3ee7acd46da4c8baadadf4082d43a0a04ab64a3424c85c0966d8c0c30734f8800886c801cb1035cc38ca790c1a50e4ad06337cc833bee0004b152a80a246174ce880e5881c6051024112150a2956d8808b1557886145d38c125b5811441065b8dcc780a95145d6769c51e952459e3bcea8343591eb8e332a5348917dc7199528c0a0a24437a332858a110d737271426bd0f7de7ba30ebc2043839325ac7892d5a6307ed8a008432985293b7680d8714bb7e36c662485e9e79cc1e28b1438f8e204155f528077a22c6e997d5172d52f1bd8a91d675f7ca0b1195f84a161860eb2108363c71909a0d8a08abd4734903a60bb16bc8c29c24b0e88e80109d875242152ab19165b36de718685162f1ab621cfec38ebe2c5125f6a9091f38eb32e5114d165099a159207e6938b06874113345c4c91c5035668ae646fc7d91421c0618a22251d8091533bcea41003b280256926450d5c331fbcccae2c5126562f11ecbd345f1c418307447c0025db1d67450821dd034104b17a1c7056c406f6dd7156c4931dbe6b0a41b5a0549461730463ab33d0688e57685d361a1f66b315dc7c675380d9da8eb39916ec2504e794fff2a3275d5a26180dac64bd54de9757fbe4cfb668c19eb1e36c0b1993cb5d5a9a957462022eb04cd1728321bcd42a88c86293b0a1daa670e181c6a4e5d9ec86396546258b2b67c7191737ecbce38c8b2b365ec9644e3d681a889af1a0341bc10b8cb090b2450ec28012c368ea4c6fc20b17495ec3126520c15cc7261bc05c071c288112cc75944115586ce2a18c0ce61adc01c6512745642089060fb8400673ddb20116653928e104734d0306c6ddcef58a07d8d444520b324a14c198d0a5a89101824b12cc35eb0a18139712065cc491524e298fc2e0f95197184b59dd9d20529f7efd228dea220a2ee280619409a153168bc59cc0bcecf92cf86bee392b42cc9e0f83bf629eb322c8ec30427550b32c537618c9f86b52b127951da200c33ac6965bb9ff53e77ed7e688a8c904c7224224bf5082d5a72f83dc2fb0e48f0005231a010ab6230eaea1927fee4805164e88c8123d8994c3d5f6d60e5e36b8e36c872b7b87293bce768092b3e34cca15db5f471dd7d2d1105088c0881644c1fca517e4c62e366725c7991417380bea8c8e30ae6a224d160412302634669e04158331412188255200ab4f3551fc02c6243e01ab1f166e46e941128c898b0627bea03b3cb10463e28ac284122cba00c2aeea15910441164e3026948b1671ca0657684982513dc1804d2db7cb28a534a45acc89b24ccc899f0e4fb6c401c9284aa378db718a315c9c2776b1a74b200c481d647e5322c1a16966849759099ad84f78400931000b4082603eeff311fdf09950386004f3f9160a10640515f6fffccf4704f4fb6309fdfcfef91da9d8428ace7109720b3a9331802e80fe11e4a33ca5668bfcfbe668ba8ac82ce89d20bae7034074d06cf9afa08554f12287f968eadcdc105bfe3a208120e9868ad2f6205dc4c8189806d23870f0432bd13256254379f9413d99f66d342b88cdcfb7f9b8a707c4b7cd5bcdfa9e3e0b88a5af03f0fffff36fbeea24f2e67b7c4ed08b6cf5cdb31037f8d1fb40901e8ed6571d1a098e8f9be351ebd61776438772df784e7a2e4c015565dfbc0ecef5d1fb21a1c7a743b9bf2118b64df8ad2fd46c794c3fde161dcfe9d03a3ece764a0eda5c71506e1bba43709130e34d98e1d92475343efcc069c88565a52d9905447e8f97df647b5025bccb5b396e4beef1e14d519633e5c9628effcc4fefc673ab2272983abaa234b797d9253c473bef2988dc257c1e948362805639dcc86eb6f84bc7fbf3288a3c216cff9b2b91a7c7fbdf60893c25441ed7f930efd4ce8778a76c7c0836de65e349b0f1267c21de9c8de73e4a1dd5d36cf98fd426810416090fe483fccf03bdcfbb5c3f3f71eff7f91e07d22c123e2b401ff42bbcea6947f5b4755441b40a0fa483fc0a1ae87be81b25e81b5462cefbc7586305adc207d1378eb4b7837c376201485c13ea5b076ca26ca26ca26ca26ca26ca26ca26ca26ca26ca26ca26ca2ec1539bd9a2f68cd17957a40f567ad355b1aededaca7694c5c09777f2fb86860cab6b123170dc436dd623bea568fed6f23b6fc71a61fbd55cd530e72d05712a421536da57ef4eab78486faf6892e9125f7cb1f96f9114675933d350b774322ac7e58480469f62cf32118f7b5f75e6b05639bb899f80fcb2262467a76727c216373e6a8410e650c6a9389c58694ec4c9d9dd8922f6bdac1b6066520f6db996206a63b51399431277f8538ccd38338d20d06dab431e9fc6ee3987f4bf9411e676fae60a5f4e4aa656665346185073ce06207d58b1639a8b5d65aed4f4cf34a4e6cfa518904ab954480ac04318879c1841307ba642186dbb1e547a52216072ab595d24a6d0d7270f6f89c20d75a638ebb17c4e280b4eb3783effa5ebf18aa940fce197cd7f002d4a79c29fb49e6aee1a435061c9702bdd50ae3ff9b7777ef1e74e5158e8f2f37e7dc7634ecee776746197d4e4a69a574ce49a7fbea6ba5945297ee5fe82bfb34523ae7b51edd5529119e585acad65a6badb553d6903eaade95524a7d66ce39e79c732e654a29a594d29d2b96da2aad903ac31886d2ea4e69a49f9cf3a35422f11fa504672bfed571b6e20ea515ef5029addce675aa942a7502af3b81578a04b7799dbd5e77ed8ea6699af6d16b91881dcd093eadb5fe578ac4e5b69b638e953f786853fad18b04addce6755e57e966adfd68a54854ec3492a4e4de6ed453a5663e36cd644c842a450487b724b88e4e4f9552a5ee7778c7b3bf712f7f4b6dcf6dcfd9e7acfdf247b53be5224dc153f32698fa548a4a227212a92ef2c89752aa542aa09924549256db7932552a672a89e8e60c8e269127be9bd768b2b5765efe18b32e325bde34e43f4e919062d2e9a4dd1b79ba5aeca867ce4e736c656767dbb6cf6e6fb78f3a68efb5b8ded8d19c298f3af5d9c9ee2d102a672757fb7a1a634edf2f5e6a2b520c2956198646175a412e7e54a5bc8ee3aadc6989b053ef4a6923b325dfeb34c7596b6db5f8716c2ef539af946ff1b472b313c4e0066210dc408c6373e9ac9adff0a269cba28749198408a4a4526e404e499d367562533a690b5c190859e704c40e9bfe0bacdd3285f8c40e71765881412eda7416e569d35912149bce80c0a17381471183d7a656746865911f079cfd70040e9692437c82cf2ced6a3671cba578d3be2e180ba85995327296d80d357e8c30e2010045b31218f1930569754529d3ac8458f252802404161a5142f4a0450828335090bb1d6742c460e0c39420c6cc82f82106008a3c7794ee4fa47e4bb0e05f525f7b7f22737aac21f5b517aaaf3dfe0cd3340b7f4222ac8605323bdd97a494524a2929a5320b230ec058064abb56b96125b989fb95d1d4267d6a634e7dd76cd5971a48cca9cfd5d734aef5ddce6fc585c947fc551f865cdf7e334a6462944b47124a3ecd392b76bd9a2d8c37acb9660b1b8939f85d58fbc21436f662632c53ddd28c117e691479e2c65347f5aba68d31d65e7bed356ee33e591473f090bc21e6e0576923b3853fa595cc167e4fe3c7ef3a68b6f05bad0817034c71888ffc85795461dab2f1e3f9f8e5149f200ddbafdec6bfe21179eee3c7536706ea8ac155dd751c273fc238effd89a45e7e4be4a7debf255884b68fb0d42fd19e88bfb6691f6b2ee9de3fd6942f243f222cf31392af7d44dacb27dadebf092cc2aa0fbde3607d4fc376cca9b3a68a89690ca98fe107bf90d46bd67c6fde6af5136b883ff7449c7bf94bbaf72722e5d7345d5bbdd6ea3ac2e46ab6aa924f0607abfc1aa35e5db0c9ea165cc98b377ead67b6667c0a6315f356eede2b5593dedadd7be5e6f7effdab618de28edb54b74ac78200e10a4ce1caeac1a943dfda27400a740e51fdf62f74cf7da725d28f9e209f261f117d15fe0d7f422ad6557dacfb09e19ac038aafafc58b427aaafca4f443f7f2cfc5999f908cb7b26ab84789d44ea7258814c42feea22e89f7f67eaf404b120c45f3882fe11f40f87b6c49b7ea4dd0d70e8c71146c038e755186521c85728c1f93d1f0462cefc283904f7fcac5cff6c1cc478d58a3cbe6928d634cfd4a71531aea64e06679451cea4c2a19d6468cbcf748afdf9218dd559edfb31fefd86628efd957d1b03c1becc9b64428a3cd1896d677e15263b591b25cf9f60993c7fbe9431098298637f460fc9222b5464b6ec3d325b57e4d09524bbd202a52e599c96bcc82a4245ee173475543aa9b7efda71e1005c4ab3eefd88b020ddbb829ae910e54a16299b4927bb655b251258a40e2c615fc3b70351b67d18ecfde836ec8f08cbad7f3fa2ede9739b0ddb8b3657bbabf1e66c7735b6617397569cc2777358dfe7a4c71ae2bdea5ff05ef52dc1a2fd92faaa7fa1fb25f4537f75ddaa17aaaf7aed13a29f4ae24284e5e809c98fb5dd5822d9d7744d537e9b999ffb17667e7bacef965aa44c4b247b778c22e6d85fd55491df06142387f1c913aa9f1f2fa1bf3da79760c19f105db2bd90eab7bfbf05d391a217558a433f72281e4556e1fad1c2c8a1c73ce64f701c69064b20bd7df9b149337235ed6af8f6f8cc704b29873b56b69fdf0e07e7f6ad5620ce2ad374e1c2704c1c93bfb8267f859c1337e3bcecb063903b0f91434ec6c9b82d930b3365e0ee3f32e6f86c620738716cc99749946cbf9d15b4525a6912a2ed7e9dc8a169c85f4b4b32262d5b8664701d39c55bf3c98420d491831e16e090bfbc8946014209e78d441e07778e50e8015d3e82848ccc9cba38f21752d2942a461a105ff28608ce07bf10dc1fcba134028da218893c4e2550dc3656ba8f8016255f562a6b95954a772bd6dda70f5a195a29ad146b390a3290a8d4564a2bb5b5565bad0f73ce4a71b0716a2bb596da4ae9779d8e2e74b095da5a6da596fedc3c40c3644fefc7525db7d517d8b54e4701b5955a4b6da5b28923891a61a1825a5d413daba54c8f9094937a6e97eed25d1eb7874d248d33253f2bf7ce6d94c50e7087bfa2533d522b0c43458c3840c913a348a6a8621ac48297e7bc7b7f55eae375e78c0efec8982ef73d77ebeee5b8efb9dbbdf78ba1e37ebecfdd6ee7fdf6be2473771ff7f5f834d95788833e98052c99c9bf1579ec10dab215734220b2652b82fe39e101b604638eb712b0254e04bdc8961f415fc02c2257abef49bd4cedf057ec9038dfc0c892929496ae640163c720b72be6bc0872289190aac828ae14638a2bc558f750a97532486d9bc1a7e64d604e4a29fdb046818d956cff0976fe8a49e0a9033a8dad452de4bda347eb01289d5a1e9a9696644c5ab6c4fbb966abeb22875249290bcef5a6f99484d20e7027f2f893127f853b44fef22343334a28f2c8561c9d50d8b21379b4889f53fd912fdc91527e2eadc9d6461c9c3e1c9c4516e094635a00767d1d79e02c604fed65c05bc79cfaaefbdb0cb5cb112a60dfed2bf7f14b32b7f6bddebe9001fbfe3ce2aada93cbc1a1cfc71107270c525c4f34e6f2e1f2e1f2e1f2e1ea7ebe0f9c6e27f278a7f906452db596daa2683fd0ba9d63b2fddfd6a974a3a8d56bd36c6437ad6f3a2bad7bd334e2d845b03eaef6abb5d93446829fbed4be7a40f053ac6f945847ea7e4220e17c93117f852e071c11526401434445513226a1d4393fce383f6c99b0a38e6beaa83324da6d3f960d39045dbf7a8a9445471e1ae4638e7bf744b603ecf157b75d3d3e41ae1d14842c207a89616ca549526966a1df451760c07899edd0357397cb5dee7297bbdce5e33a32786bbeabdbf41b4a59c9a144429255706eb7652a88d4566a2db595baffe32023d3106c40c927cc50930c4a8c1c9150e4917c228d6ca54592842218acb167827ce70c2353100bfe02c11d1f8fa35406dcf15718c7a0d003ba7c0409199939d94a95f88b483e0141100441d74b91c308352403055304837347486c7d74d2d9d1bed085bf10bc5fb8f2d8fafd23366d1a0ad9f2e39764684b8eb323c821d7ea3e67a595d62c9e488ec65fb1da5aabbdd5e29e201f320d12bbf099983323022ea9ff4ce4e14f03c39cc1377dae76354224f52ba55f9e91c0d631c618e629df3f2b57de57b43dd86aadad34ae936d1cc75f1f41f932846153ea2e3b7a450ad5eecdbe92da4bada5f6520e83e08e9fdf0e9f5a03469e3576edbdd77a4026981a4471b2fd8b1194fb097bb1bdd75e6c2bbe73df19461ed602db7f5afbd4ce98436be7f69b11947f2bb0bd05baed0c3e3d4eec3147fe0eff195dc60dfacb9da6342d8c5686ad4bb97a059865cac808ca97bbcea90537a78ed1dddddddddddddd6710b9e387f37d98e3c647e4c8abc732c638271494524a3f5b6fc5b5627729719c734edb0395abcf4e3de79cf1bd54ba7cd0a4c900fd1e9018d09732ca29675294934bb63ea79c3b2e1f2e6336ac66350322a52e270838aa5dfbdabe0f32b27d0cc4d1f88b629b9d69fcb5bdf4f0471d94d4570e4a7cebdb2f49ae3f344d36e95d80605cd97106c6942dc28eb31b406ddc41c5f6ef380323cce6bcfafd8aefb7763540dcfed5fbe1405a9b6559407cfbcff080b049dc3dde824562c15dbfb904184d7b66c719185af687376aea60a42c7a358bbe7d16c534e6783fa4f61cd5f7dfe9e3a758b3f047af85fbdda71f7e70d31d59f8031f3ba824168b39d936c8486612273196aae75e68e6f3cb40cc079a366b48eabd2792fa88e4da71afe9d44fddfda6f3e3ed3d8d9f13b284fb7a5263709f9f0b336424f5ddfb7b8fbfd3dca774e74284cd788e939e0b4bb0d4df7e6a4eab3ccd3dd64b3c214f2f899e9019df93fc797409f63146de3e45dfc718f8b3ad017f35e46fe6fbd9dabc0f7f77cfbdf6f3b76ffbd487a3e6b87bcef512edfd37d5e30dbf6a9b7a091621edf35fc2aa2aed23c2dbfc9eb9e908c3b1a2e95f81199bef5cf458d7d3450ef33ea59f64800030ef8b1c96faab85786fb50b11e67dd544262cf59a5ea27aed3b4d8445283ff7113dc9400ab0ee8b248cfbfcb12a51918475cfaa7a091621d5c7aaaf5ac2aa1f6b48d68452af7aa2eebd9e298d668bbee7438c9c24f6604fa0c9b6c27221c236ed2bb049a3fd64fb649cbba8b40c19aa72d02945030000000401c3150000201008060422a150200e14558d3e14800d66963e72522e9807034114c3280a8220886118648c21c618640c420a95a01d51849649122bb982f0a6cbfbc2fed9fac3d3c417bfe4543cecda8b843639bd02352156ce20c40b70d27543d45da3c57d4d74f348c09af6f4259b3f8da7b636b1516555e5ab36ef2b614ef18ed637fa0fb374c1f03469d6d82af49436e6d64abc6fa2911a39c54a4742568d5158941d2d2d044323b0dc39bb7ba738277fa1a290b634eaf5fcd65fc0c2c0d61b12665d5866b55dc65af96553a0a9542338e8b201b117e1a0652bb22a6dbb501552d8fa93d84f075d8cd70d7cd85e1a373cdaf6224d64249bc5706444d4843a29374db7fbd5c28ad03dfda13bc425de76fcaebd68ea2fe9ce23f78757e74361c5bee656e603582aad03f2a5f419eb86957986b9804ce729aaa7ceb6665e878ca8a2aa596f5ffb7f1195be1e22e83cebf8fc0fba53d7cfd306e43a232ada03c37a40d353d5f0f23d82a431b5390767f055a2a9bbdcf39129c9195bc227b2f77d945f78f2db38b0f9ffaf1e230a685ab85bca963441cc37133fd65bc91faa438a95d3a2b0161aa5a2ca0df10e0a38da5ab7ec4c6148ba7bdd029840f85dcb35a5b288a7e5bdc89a9d25330ff04b284bb39006220b01346aeca82c45d01ae0e38e8db6bbca6edaacbb7b1c755aec1232ec5150841ecac2179bb35bdca266e66cd9401d47adc0edfc345fd14da1086ab2da244b67cad42de4d43f98fe95424473ffc19d8c0d832213b1e764d5eaf5d7ea54c2d75fc06d96f30b9ac4d7cb52fe391bb35ad095b860a3f384221d14016ecec61046228a3049dc39f179b79e47736ece043d8f25c6d55ba9d0fd0753c6eb219567e9943821ed78576a05f011ac743f35e935ca89de82daab38f4769fdea7af617657fcea6075f60ed56bc0c382b06dcb05d01428df9aa87e3be1021a07355067c43c64c6ab347cc1ed4f1c705af8f643816f2c535fe0d371a6de1ed5389b1790e3a7a9b7cf34e902af3b01d6fbeba3908660e2e9e5f136599c5eaf3e6a5799ee8409d0da1a32b596510fc5d586f7f5282841c1f1b790efc9f96c996fa70dca326a3ecdf1d29542e71ba5c2f5c8e23f29fcb5a8a15afc22bb83c94a7534429c38a750621f8a0a5613a291684379b871d59485e5def477160b3e3e595b68b25fa13106618df46875c83e3b3a346168410dbd8a8cb66f968d359027ebeb52a0b92786a5251c5c91982622fbae146d175f5cc75d76d88dd05814f9fedfcda65151ccef11dfdfb548ca25dcefdfdb6b9a5118fc0549b57ffbfc26c45103db3c5adb8aaef6e59ef2fb8675152ef31b7eccf52ebbc2aad3b31997ae0982b8245cbfba160a9c754172f8f5c81707da8ad5872e8d3b4c5ceb747783a2955e8f935741f42527e2baad5f7c5d273ad900ceddcc89479d8648309d28445c89842e05949bec09d4b314ec9adfd9ee0474ddd640cc1b3bf99311142bd35745497162884122f49852cd71d1bef39db70c938636b375644c1121c816944d7b7039c43eb13a4aaa1974ff4131769841674794da9c495f91b3b354191b18a3cbd6069a8640d1dca6fb0419ce1f84e5f83869ecd021af9ee5244a8bc50efa656fa6698d96d99c642261a114c1584cc2106351ee1412eca0b0a4301b63681b289b373eed33add7693fc6386fe8a92a7d6a37a318a1628c45ba3a3a0cff9e493038d600633b955000c19b4b503500f8aaae4e2279437c6e79c4a6a60bd9454635f16ea5a57d4614ecae8027354fc1fe6cf32ab128c2bea170362cd111b455a4ad0d329e087e7261cd4a65199330d8208cf2688e6cd5008c631e281652a34a12860a3576d679716a8a63b3c4ab3880a77bfd3e0a689291c131b89fcf35b13566cb14fbdab4933e1c216c23e5182a5d8bb846b6f53466a8fa09a1b5c57a2d4d03f6aa394c69aecc576948f69fa220c78837d1c2d760297738008c63648bbd56c81a20868119daa74f125caa7bf6364e2ddcc6bcb1950a0d71fbedd5f5a1ebb301c4c43501761fa3e0b879b6f8d351c83e138476672b8e16528bb426012984f16874045a2b164648460f3591b859d69903475d78a0ce635f6d6eca7285119f3feb2c6e21bea5114f8762f3cc36f51bd251293553d278167250c222b01709454e3214d4a3582ffe6e581a83deefedcd7102c38bcc5cea2506074244071472acd43b95125b9f10c7d115146808f0281515093ff71d1ef318c024ed2592bfb674184e4fad4bd751e0c9ed9c201e2dcbf76b77a49464d33af2c3dd6dcc066e75ec6cf610bc133ffd0fa295e0ca1fad4f67f65e12fbca1070c73d49ae65ce226aabf1c9d3fd3bd9a9469242dde0d40a77a4b4d4569e7902fa7366f7ba96e34edceb84df7a9894b274c9043be164a27d239739c499a57c93ae1838b1ba532046be863c4397b235a6be4e7f57c9620aba1eb288110e822ee0cb406616ab80136f701487126d211b472d66d26a2f22213821a010d7b4f917526a545a1f22e2bbac7087cae46970002ae4f7c3b636949a7b2c7033a519718e594b3d774f0958e8ff34f0c939c17297f7858000838dccf3f121891d94cd0d43750eb1f57eb7a4a8546331bebf28b30babbdaf24ff6660efd5f3af4e45a293dadac2ae2d4572b92e035716a3fc7b05f76fa407c32bff272e2c7b08fd74af6e3799634ca3e7fdfc3d6a968c49e8af2c07e6f708ebcfeab5c895acd475fc91614d25cf24c8f306c567e6e55cda5157901f3acd87c93be3bb28c80cf5f82e6d5ed5302991e101c013e5bec2f4ba5eda07659d3390f3d9dba259a9e218acc6458c2b616d3066827c85e8dc6093f673145dcd491d245405510bc5917643992ac6f57946a8e0877198366ebaa15518f8f80588f226ffa24e5edc00006fca955ae669b832c7ff5a419ad0a4321513fdc2d12214685045a40f918262f468128915749030b93ddc82a7119acb8444fc13fe9971f312d03f51ac15ca73b3a9204d175dd20aef0af215b82f4a9ced665937fc25a285ccc2cb128d52828e25e83d57155a86f77b96a5181abde4ea9d34b384018871faa794ecc3c2c7b88bc5f9ea07edbe8ce3ea86f85e2a33bf2f95c23dd2fb71e1592ad991357ed5067fa840c4f428e68e5669986297a2bd1ecb6f0452a8983d3d26958b232cec74eea2ca0b5522d1fea9d860208e19470b068fb8aedbb51d31a19d86fa7ce84367a440c4dd3f6c5829d1f188f3bd2ec8022dd138838ed0444340d226c6191579ed962e7beaf2a2959f960bea7ba6f03db3ac7410870f15a39375dde383331000d58c52a9d19af29a8bd128e5e8e4017bfb9b116c13d200fb32cb50abd929affc1868d2a815b6f4500cb7e80de0274300b28d6b122c591858f51c2704ec86ce08401a62b2c39efc897f6a471516bfdfa7666fdb5670c0ce7ec5d2f0869a4ccfa733929289adaf90922291b995e1e80bb7dfd47a1e8bda7315bd7c3f7c3ba31a90d5a6f28ca8e0ae1050222079852ceeeb9c8f3515b4cb8b0b4dcb439da602103ea7aa1d7ab029c286117b11ebfd3b9fa7af8c1d13a513af551d4342eefef84d03b54c632046dc17b2ac9773b454983f1fe14a758a1a95d30abb78796f206a497ae8430598b31b22c340a73d84766e28319f7456b9c04802f8e0ba0d65fd07e56e61e3baa8b88cc008ff6f28dc560c5f4c5734977b47dcd9f176a16e12d990816647c3940665ff4a526489ebb827772f99f57ef516290e1b502ca31a832b637c5260c101e10670b2ea21422645bc9a8700032fee6be9489e6ac0a8e90832ea02ef3f11673a7500a1778189ccb68d3237f70de7df210b024d87541a3321c63af6b5b7415da2a3c0851d2d81fce162ce43d01781ddca3eb1363d6c2bf25e60f1c0dac3c4815749a5655a71bd69bc220b90021b86bab4cf7a3fc946b29b5e8ac4aae036886865e67386af0947dd734e2b67bfac410b871dcdf94bf3b412d586f99d1979040eca0e87c135d0a88fc3f75112515209090731a2a1ec5c8a2c61381c1c803054a469eaf55803bee18801de6f9ad9f32149ce5890cd99cfc4d46e005ced8bb432232f5381ee33dddfff3c627caed6ab449fdf2a2a2b875b790928d6ee19a161846ee236324017adb89be34056a515033f6ef53784247bc68181032e66d06c4e1926a59900bc2d594a93e56d7b1aa5cac9e21e9477b9e622c82b113ab142212a5d4d224ddf5bb858823c240768f943ab3aa768dbf25ffbe51e1e8862c72b2f200c257cdef042ab790a3dd29aa01f61d69db524feea4e1fdfee0c019ec9959704ddbfd8b22ae1d3ab828d228c3a19c2cfb784edae74b2e695762d8a48535d815e8f924a372a72623e3d9192d8ebff6eb487e07fb24fa2ab21374b7c127ff3c737c4263de5405e2eddd117a8b17863bb35faab31a58b86ef2f00a44ff5720796a7cc2abfc4fc0f7c0d8775927dfac9eb59a8abb5abeb5dc5539f1cb88cc7a8af951832dca34116eb4879602d1d322b3227d563cdbe4d45562ddce6f9c9799a88b44c09df5a7a6fac4233893c1f5c89242fd61cc4242ed896b8c92509eb99ca4956df89bea55c4fca671b97a9e30f8cdb2154a1e1f75e5e12e45e5b76ef92d75fa40221fe716996f55efea9e047c9379404adfd96d731526c2ac6ee672f5db8fe6315280be87d2ffd2efaa626bf6bbbb0bc9d4b938ee2624118176f1ffbfb94cf7c87b538fa5f64a1e7afb48b12030a1411028efc73021654088a48dca2db914f1c126909fcd1abeae259a746cb9ad6086922a2e350533becb54846bf4739343af04cf6f6d11cecc11a3afd18f73e0b429457325c9017084537e04211764f2758513bd1f152eff6e2c308f52f9dcfed0fcfa26acb2b0d3b9e3f6cba6704cc83fb98db007c6e3951b34b956ad83c3d376bc603e8c406892e915166d82f9df428e2f035e10364665026c45fd8706c350c3866d53594d9bfcd2b1e5d9f3b08a4ded4b3c20ea024bbc03692d11c81d46f05ff0e3c7c7de162511b2e51a64ff6f0eeabf020d636d24b7463e58df79101dc25c978048c3640303211d01d4afe485847e269630de60dd9ffb90050b241d766339b9b9db996701d0d642d37c35a312119da20753154c1087d6b524a99676aecd0a909aaa386bf06d9ec7443918c72e53197c639c8e8b2c962a01d8a5080884f9abf8d44f5f10bfc2a5130bf10fb0ab9dc4446a82c4bc2f94ca9b2ace815f3ee83d97a8af9bcdae7b9922843b50727504f2f7bffdbc322657c94a06032544db5ac86682f0f71f36565d778b2183ef7be71990747df1a5c78ab33738aa137883f2afd22cb367e4323961ed68d34be8a58ea60f68ee2bcfad49561311eb8ddaaa0d9f1d7cc1d98cd112f4ddf7408ac04372a9ea956a4d3e701c20150beb4f18c6934d5478f2f35b0e7f71794f2ce9e460cd849c1edc4c41be0d28dd5e6bdf16d455cb42a6d624c5e1fa802aa59ee405c8a35993839b57945dfb148e5b32a6ea4ac1c8a1e1106d618b5e0dbf73c3dab299dffe558cf6aa2d1785490738b7a3a85b7fd2fd29bfa85d86d180c1116f1dc7d8d7df9382631a3444523db4aad781871fbe1ffb67edc88ff1f8e6bfd77bbc91f26a0286fd7188b041c9344f1ec1d63b504ce54e4f1e7f6bb0423f587ed66077f279a7ec7a5bb1b29c797871017bc74b53fd3095fd40f6976b3d8eaa4356aa2ddb7eafdc173fafe5c5616d45d2613d3c61288e4440bab52480d5a24e3a5d511b1710c74124e1ad6781297a972123b53b6e4c62527f423f24a494969b5ff4bb6a28853044a878422de62093005dbdd934211d0d24d869125d47ae1ab2b890016b87d7f0600cc9d59e34350125bf95d8b8951421de477299bdcb8c5a48b21066a9321bf5719dec3e61ee3924e51402520bbf1ae62a876b5f74264a9d52f1d1e74823858d99a2b69518b76f8b5b4e048a8eb094b77098b30402986dc3c62f913bbc2f3b4bc2fe0b5cc621a42e7585cde2b5b6fc2c41ba02c6fd23436984b17a966a35edf6a2e3ebe0298d68829c023f9f38e4b9d84ac1ce2ad2746b71242c49192a4f1e1f0b779632fa0b6c6948a91a3b34b256d08d73bb77443930988592d39ad1196fc71688f77e9ced8550ea32537877016ac9fd74ed1490ebbc8b617bcd8a2063ee04108b2b312759ae27db35df104bad97c0689b6cd55a923fcf07bb23df2357e399caf49468c7b4023ac0c918f18c6aa4d91e15298a8b0446ac3ee855d9e53f6300b149d94b0e09d944898679ee949cc7f3bbd731dee05e9c5a8fc970ece0dcbe23d4db1a747fe4d3105d702f6882bce88a9a921055464893a4ffd042f584da745ef374cb7a020933baac16bd1c410ae920021cd0d3f5f51bf7181703b6803b6b866dc0577ba21f11168699ef25a96df95e22d0e52adbb40cc1c94e3ccb3dd001c01a34e11327872db2069c130574cbb18b46689070228f57fa082a1408a348811e00753a6c931db643f35c4732e74fc2a1a329f93b5e64b74e70cdc9e68e69bcb55e2988436fa8889e0b7e8a0743e9940d3093649d979273143691600ca436d098cc968bafc1ccbdd7adffd46dbd27ff6147db6ab2a6d4d36c70ac6bc84648b77c7edddea48d022bddea2c4367718eac6e29a66c325423d4fe9ef7969447d4cad0131546b0c53cef29e50b0512c35d8d61ba35224cbd0346e7407938760d8f217c0b7577a86fe5c1f183cc00077016b6902effc18e633a447c51dda5300c59d95a9a685941d832c593e002c0cd31eb4241bc8b9b6232846482c4e8025f86a5fed3a444da5bf4dbe414ab502d833e1782a3b1e5f4e54decdaf47f023f171a45ae83952001f740eea71fb493c7c97039ec2250077ccf942d4853f940c23832218a3dc6cf9a3d2114009ad73b8be77ee0134925e63b8a300a5f0c95ba5ebc365442e5a1c5e051b23b232dba520f221531c696a3dbfb6f909dffe405e68f1a94a90360739bb109bee46419374349bceb24e6f7f3d52735fb19052ebe4139a2008a212e85b201ef120e28691c7e2f53739b6ccba46187b8d5d75aa247a16c2280ff0e129ac6f1dad14ba749d49f5e94c0a112bc245d88e4c0d738a87fb64055b645610ae3bcc22e35610686bfcc05a957251947439bc41142d39b1d843699b1412e0b473c36640e0b952684354a08414a50a49726db031366f249026ad9b9b61c7d729042cbe204430b00d5aeff3dffecb067175eaf54c712eac252eacfb861bbc60f574d77987005058fa6eeaa307c3c55cdd91f6fa3ecac4f0e031de8599a9ba7cf101d8170229075c67d02a60f559e8d0e7c6c020e4673e5b9787a6c6a35a5162d8c88a7901ded1844e3cdc73a630e6df1f0f1b30e6c7fcb323f081af316b3f7b809767177710df5a2130c02fe2eb10dae596a3324546b2ae4b952a0c77e6bd53390bc2c31e9bffcdc0a10409d008d9551f48dc9266b4680908c8e9bca79c0c20eaaa147f6711816121b333913abf67dbabe8294502a47ab825eafd12ee9b0ccdaf051c2ca7f3a2ef3c67208623a4ef675bc9feeb7cfb5b0c8dd4a45b93d7006780557961555f00cbff85f54f52c9bda863e8c5dc4e92dccc028b9c10b56207ecc3159e569e0a2dd5455e7e240947839b48f1459570e644173d55d55ebd0871d993a158c0939c54af8810abf0cad938e102faa09c8b9fc2e7a81ee9f8b4c532b8130723a113a4b563a743e8c220ef24733b87229b16b3f1c592d69d8a4b947f410ed8e24d951ea4d208e24c42c33db745284c20a4a7d31e733182d4e91cf2fa892b38de9695d6ba4a4b8082c95012e51c0eb37682c7c4e3c4f359e6e6f1fb71471ab9b2ddc1011ca053b10eb2a274ff97c0a08439250fac34e25ce7098965bc8ce8a3127d1d18db474d51944fb7f8a4b0a6a8a93b913628b66e7c9dc20f4f2460a92c0f9eb19587d3ec95f97a9e8c71a7d0dc138912fc86e2046b8365ccb38fdf1fb9770ccbafc1e972640953c3fcc0e2c7f8aa1458af68a1d03d58ff0760240c81be8631c5c96ae14e4e6b3f12c0e2c5caf56d2f14fd9c284c0395828a031dc5909437775b71468571999003e4a735367dfe2b33b0e1896fb360882b675c0893b93d56817188e5b7ad6ade2d1d3706ffbcae3e72da0231c902ac0c03c9b58c2001b5e6b1625346f7f2ddb5c5ccb402b84f10b2c20423ccb78385f9f6c1011ec74686ecf989bfe705857ff260f36e372139755d751169dad01d3f474d60bfe0d006bae3b232e919ad97877f1288534d96012032be306a82f814513f18fc81836129a42b7db8c9a3180fc2f7a032dc48df7bae000d308fffb6184a6aa11faad3a3e1340da3f4473294250729cfa69e43dd115e6851cfb07c607694e6c353783c9f67f59b916489624af380c928657f7c534382a17a214565fbac284af416c1c583e158084acde6453ef13e82766253276a4446688fe615618cc3b748471b6d3bd3bc8882cd5639e98e8b814ad748e162fefbf5b1b655ade304d89022d78e8532388bbb192f76c80a2022c575596894e98edd573d7a79001f783118b1460362a9cb32fee1a31b26d9f3a83d832ea1a6584bf05e0f1d82276a754821217ac5ba9c9d17b2e8615345ec7ba00e01f1025962d14576f2c5233c593965824fa9d64a2e752f39b7f9350037933b0046ddb0423aca1d666621b714d2678533f222cd19fa9a4158f10a24006ce667972ce9804d3421e289e9112c2c2c4517cad25070dd7ef17d64e01b55aba2020b6da66453b546408b3fc87db840beaa03d8124cbcef56fc74be183d24955fa3efbd0f51b2fd593d65ea89508ce1a6d29d3f7ea96e739d2c208b545ba7a45fc085d0380756c441399e232cfe047649bf9bb033e3bf41d2f6897017048348b3e510b04a747a932735aa9e94477f6a6c79bf83062fb53a08cc5581824b498847ae24d23e4a072c9b956af0f6171016671b52a58e61749ac3d499c0986970d9b3827982b8669c3dfb91b00298317bf172faf6a62248d5b71e8c18c6643e5f36643c9cf60f9fd01d19046274931cc21eb6af73c30c1318660dd46c30bf2c7cc0657530d136a0fb1b93640400047f67412a8e6add5899d61b10adf2436bd80976a80e66b9e5f018db1d4684b02ebf9808bf8855f28c43172206604f236c659a7cdcea9d9bac1f226e81e81e98acdf46b050846683f682872e6e3492e62cb39035627f0034793915409e5488636afaa6e58f88af8c48b9163261f08d60721565480f2ece7801474eb4cd252442080391659e965ee9e9936377d09d4814f05c8da756f7a401aba995892ae073b753caad43a6b4c26ea14db35e8e44701591ad1047e2b9b65fa1423d802d266e9833c4c6155258739721673668ba3d3675954dab56054a50ff147e7a045160a51eea7a34110557406923231f2625b83aa5a594abc8e72b0b381311f0d76a1919230343c11e3c2484dd56cf66c130555317d21dd14bbcc055d9e60f8a0015e1ca39293f47bc8ec6c0c0ba77b42c2ad6a4519a7e205be67c29478f322493c8d2074585982c9c6be4d036bd63593ad06701a7a3ef854cefb7b5ccfe98fbd4f6e6a62e4b7d9116db086fa188e44548b1c1fa2c7a65598fb0c022013801ea456d5ad44c33f24013594eece71b75505e489e90488cbd54cc9ddbc75f423699ee3fa442fb252c513dc1396e7394dec4402e9b5b49a7d4655184b0c11b1752c91608cefb8f97142aa82cd7a993452c4845492a7b0256e885e194e2db093929aa3fff8fc6bf6633c96821990f91b2857d9002d16b015199dbc1d0a0f21caade5ffd402a74081bfc80d14c0170fda471f9b710f74c743e22de594bd9e74dd4257770e63e07811dc930f48a05ce807cf60eaacbdecb1265d6e9a069961c943492b7e79e0782e1fb07037dfb358144ddf9ab3cee2d2e35c22ba6c1c6fdcd39999ffd35c6b92428d0b908afaadde4cc0bcd39fc2b7739bc13b335697fc2c3258856e86fca427255feb56dbeea933d240f42d10cf93794c4057c1898abeefdd413f516c20a81c1da423638b85eee8032784d5dcebb1f2780b0470582192b1c9502138621ac56ff04514e8a7411bb8314d063089f1186bcb2f8511b9c96ae483a80fea0284b705c76836d6d64083358594bab0cb22bd288473338bbef6737b84c92d57d3b89675d58d4675189f6d736f53576b58c0ad2837792012c11437ff513d86294ab2c1d847a2858f88c15ae08eebd2c185c6205435f6e984e7cc8d9ac8ae041df963b4bf8a12cf40d520b41dfa4ecfd7d4c14cfecfb7f9afa9a190c79b56cbe0667b1c1bcc513dc335c0642ee54f61f0e0d68f0563daa06e85a9a199951ed455e1c367e40f305c35c7f01670bc695d6c14b3913293ceb60e36d4b4db0f767431e859d208161d7616768af7b15ae050de66ed36cdbf3dab09fb9aa2c50c8e9b6b711c677d64f643ebc8d4362c5ce2e98f76db8e592259093ff194c950d75040e9095602ba633504f94172d70ca5d4bb189fbf5d09da5f85ef02ff82436eb7b2c935803caeae8d24e65ef215829fd522df7d7d06d7f11496fb87b3ce6856e01d6d9da0cf2011a1243b74c2d5e437d6334747d8ad3b8ba4ab656959cdd0387d06858e7732582204c2dd93dedbf0a6a13c0cc260cb94726265d8fa56e7869907aa391af98e4ce726ebbf26b5ce2d376bb53dc97e33ae722762c57e867bd5a929443dbdd681b39d8c632454e6689c05ce6e9967c2f192c30f75889c0ad9b51dcfd338695a71ead44e9110ba945ab0769cbf282ffbd6bab2e7a813b97ce7db68f08fb6bd1646c3021670539a72c37fb22d0d6ae701e452f9f133fc8336b8c6a501c6087bb66dc22391bc56ab1abb30000b2d47fe0b0a1a95ffcb6a14267e50b954192ca59dc5f96a504a24ae29ba3d2120ff6587ebad14c941ac74bc33430bc2e615da78e6b172b215ee0921c9a270c912c73e43b975fd5e6293806ab06a88b7d815af1a44bc30e825c3776264fc36e8a94d0de944b4a344c1635f0b08e0eed209758db07225a695b3d6f9405bf96c057d949dc131419f6913b8495d7e2438006d302f18fe5b39f1c6a2be0698ea65163a14928f3f53a10fa17f674d6bc434a7588f20e075923062a5c734596b70ac88ea4b798462dee5723da373ebc6efbdeda8782b9c15ce606db43745a2fad06a281925d9eea8114a23bb4b45a1565861b98d246731c54ea9f9dc505c16527c3655551196e6088450b325fc75971abf4a0cd645809b7e702e2bc4334de8ac9e1aa7f9204d01ee60366300024ce30872a6d33d0fdb8bd7e71d5d8e9a00cbdf408d47e1c765911d6e2f2391e6d92d443f4b81767b1662b758d0520a02d5ded5aa4007178741d7f4f958da72102ca1b8d9b8ed65d4057cb76f621083d5d56f090f1fa8cf67adb07b024dd27491bd8023080e698353b33100fd9c661831466db2d7e35d920094549554e0ab54da1244d834a2ddfb5cd876bd030b65cdc6d21c10247335f4a13c556140dee1fdad48bf21f55d0be3203bc03f78a09f0ebbddd4822f43f612d1035024ae5a001155a4625b7b16eac33545da1f49b40396ea50700c4465eb46b873f3fda0e02d79765b1625fa64c862b321c013752cd7c782c252b6f843d303d6e2ab2f99cea3b1f777f56505476c13c05f443eadc5aa976e70cce32c0bcb5e4436e1abbbd469a522e26864411f5c6a46aeb800f111693ae85c083db23d94da041ee3d7ecb05ca8f79a4c0b60f832346020d0cd706b453ed3c9751a12e0279f0f7cbe66a32ad606f9ae68f653ab97066f149e19b838433543925b54be09e10e0b4d282c2561825aa4871986a6ddea15a212a9cd6cbabdb1fcda2051bdf3d28c5aa29d96906993c49ef1c598df066e904fbe61e1394f03065787429698d5f8cd2a8983662dc984c735529620a517540e29e0ce0231f64e1888325e7dad545f83baf70bbe2a8443319feede9e9a23a3855c90be74839101d8053ff4f4c4fd854bbc3745b89d3aaeb074da4204423523b5f485114c1d0cd4d6af4f99c3b69406ee3c758a37456338a76562cd5a189ac8b42c1c86673f722101134619a9868847996176e33129da99da2e81c34cddf91a6530edb029c41b03a79d16e1d106bcbe5b75b3402060434255bd4712f378081ab0f918819e5d963f871621cb7f92d02a3628bf84f0471751bf1de4a0b1ed7f5834354008eb95b8d2098009d916a341a1ce459192104f2a957cefb8eba30573cf711e6342aa6ad8df09377e0676004615c90917513843552844d7209cc3a60f6e69fab31645f51765117f59814acd667587ba7d96ec75e8e378fab14053b54b72bf6357034d01c260b56f965caee5f366744163ff671ed791ef458b0f99490f8a0aa025156e549f4fcf5481bc6facdf0f0dcc1c3744b298d9b15c5aafe6114bfb04e6dfda461a70c18e6a51fd40c054d6f1a87a9ea5de23cc943b1da2e7aa66a484a22e6a6657a0065a101b30118e5606b34c2b74c47f3c8b75b5309af2e1b133f60e58b1171579c31f3a6fd48ec8b144d2d1bbcd1ffbde768bb7e1dba51a069a75856b596ece4c9eda407a8aadf0b4d06ea302ae1a9d4edab900a57050b8a51a6194611f702d61d3fef8701f18a8d5162ee9ce4114112cbe40eaa5a6107efd204d264de9ccd15c4f8d979cf1415f570371d59c7aea5757a441651338feb1a16ab3a0a460c06be9c3e8625b7c44f42754d9cf2c13221286793dd5ac40c12301ae7edb4ea89c3e475e43408a2e6f0afdb3c8061b2f7f30c13084d032070c29889490848c5e299bd7e9fe00d2cdc5cd26e4302d5233434093f5a6c5248c12f46ab92d8a899692432aae5f215142fac40368e2a6a31eaa277f122e0fa45b463136f66ce16c0ed8260ca3060682ebc1850160ca150b520209009bace91cc2730ef8428b5ec0a637a2b6ae5c77fab836850255a6b09512a35f0e7b6640d26fd22c03b3faa5a8a867a691527270e9ee8ed9a1602c28833531d3fa029cfa7e2f642ae4e2e12122c3fe79fef7b824f98dfc89f13aca3370e0bf3e62d3af066708e1652fea2eec13e78d7f1ef8809b2a7a956f3e5d2f7f093c815ba2077aaac5406fad390a7f82e65d2ca4e2665f3996a1b781bbbbdc71f023795fed97193abf6203f9643d3b6afd099bef22e448f8ad327532a0b211ebff9b02b7d5edbc1cf4e7c2f0a20fc44fdbc375b935290fc0592259ecf7feda369317371430c826c3d571698c163fa6c5bc35740e2030fef24a16cd8e4a83b11413f8869e862d7c9aa2f8919eb5b03cad4f811fef42327c7aad5cf8502229bf136100a61e300aabc2dd51bcd9b1a1be80605df1a1f45505aee97da95c2736ee358049ee475c21888873df29deb7bbe5672bd6ed979e09ef54525bd7bd037face6073329a28114cfb83fbc91bd74a3899606f70e09945fbfbf4eb18b770e17ee86954a6abff8246800f3a52010c133c1c475cbf7aaa040bc4b8bb76c985640bd577c8d5ef4269ee1d9e73c58b10b062cc743601bf0367bfa1676d01af92224907c6ce12290b8aa77871709ce1807c859b3e3cb1efc81620b22a7a40e1d581a07328e825300e16549e234d7c8baffa6e79849bca852988929f87884400330509e5e4e92682165359818cfe6d8992c70e7073bbde1ceefec5dd4a6fcc7347a0433248f469c08cf874773add1542ea21ba3e9e2902a9a0d8565d00d0a4235304d6b5d15d47ef6c0259aea556f280977cf453a24bda528c5c21a4652d1eebaf9d7b45c0277313d7a209f6dc82040edd77db0925a404cef493da27599ce5ca87f10a13b3851ca157c3fa6b040f5775dcbdc3a92f6b664c101922a5a85f99f5a58f56f0705e0b9fbbb27fd7c1c11cd977b00f3086701fd5a7140a413aa20f11cfcc04ee37ac9d3502ed5efc4bd11858c905650bc22a932f1a2890fb89a481d95044a8e6660ade2ed11dc51f600c18500e3b913ddd3a3bfa217c561ac7f97991424861caf976a694c3910a8a961227149b55541853edf22fb3ee0f0050f296bb406c8035830ec932786e052a63a22d88cbdbb85117900623a10b314d8f546e8fbc02d16069d053f44fb885019fbc1e05944ad55b22523a269105c51197077b07648b2d53cb8dc21ece92fe3904041659b7a73195da2f9ebaf2b1a876c54e35eacdfbd6b6dc69706391d00775fbd0f6b5b1ff87da95eb969241e5b4ebe18e8e08977af772c2baae10aa849f3ea224c4a7ffd05baab397cfc37e4c11474868fafb6d97bec99e083b7a81d258301df520ae2350bba78057697f2b6c8f09878aad2084c10a0c874bb0e38227f60b0077e3d9867a7c6718ea2f64dacf8dd564155efe736cff8036bf33596019d09d107d5cd7103de138190f00e95b10de95b0d024b4311ab98956c917e791aff49a0d704a3cd07f091e51c482116bb1c095f27ce78604fa71c74a2922138f320ee4a7b50fe4c698c973a01b42d8c44c68b7c1422be0252cdfe99ee38cc0b04ce3fa9b4b5730c3cc686ce20e86594407f14c8402048acc06418d14b474d4d00761df816fbcdca0bf25cafc1df5f4f9eaa61a5b0b69fbc2f3e648260639c8aeed59389fc425e390a394048555c72f9e5b2ef86c6479af5d0e388da6c4ce6717402980ea5f50ab835f0266f22ff7957a5674ae31ade2961573710b75278a5e792729e81be5ebd10570d305b770f6af19f31889b10fb95b15576c1282ff7c72759b8eb7d42c1967d3452878e93ba02851db93c271146bee10f5c3f743ab8d8b94f680b9a875ecb0c895839064db72b813b18aa3d06cbc49a707a51d17193d0bae71d5f3b382e8859c023863b7d1c5b4f387de68229bbba5594507d82cdbede66d0ec81350f9becd96f20b147c0138cfae2f366f621fd1e3f550572cb97ff7a1c055f91b25606038bcb94875c7dce46a5977859120d29075d48282ed3f20e6c2ceb3c7c0718dea1b7be1c90fabaa0ad3cbbec6fc093d2e386b522393631453b89f22106c45bdeab146c5af3fa8cfb44e20da29eae95d0494c32d66b881fb3d8d56fec8650601881dd9cd7462bc800ebd35957859c67d68a0e62507702d1bd8bc0b1eb6cee2693c5340a7dc10e3cf0ffb48a7402322a3da7e0238bebb1f29e98a608cd2a90e474baf1f164bf9b1a652146235fe2701f05ab03e4d6f9937104a1a8c7f9443ab2ff10e17cdcfe2235dbce7f29702fc33f82958e74ae651fcd9e815176edccbded0ae1042aefb1f84c26fccbbe384baf325198c18c2c9e9aa38b8ff77ef8ee717863f3f9987efce405c9849c3d27377f9330c3cb87361e0d67fa44a2b9b98019c1996d3400704952fc1ce0565320199e873d08382e91f022bbc56c75832b7a36f776496b6df692e608ebe7346a2b2c81851a86f1b4afeae66529c6767daaa1eb176a7938ef3aaa325c5525d0d8e40261f931160ac53add06c35b2d1413c3ee0fe60f66fce1cc5e48ba855fbaa076bd16ed82c5e6b9190d44b0aae81af47a13ff59ad34ce9af81c122e94fde5f62620c2aeac4b72a2bd609be7f2e993678d495685ada8ee79be24c1b2422d336f31517b59c9c18202df64146bda277aa421c70c201bbb0ecf506ffcccd13b0e53b49ecb03e1ef08508ea4dc00dd71243e48c7dff3a65df993f56acc53e6d42894ad1cf2182a8019db2b91e46117ba8a0d3604a0a2176e10024acdc0a8d09544464d5acbb15363910a9340e6dc56fde4247d8876623ae6e2f5ccedd1221a79de46f175972ddbcb4b0b07ea6938435d191e0b683e7548dd6b3ca21a165e680714800284458bd84b4562bc11398de06101a6610e06502e17561aef547e3c5ea15f2087fd1459d7e9523aa3ece8f1e6d5bb4312766bc4531d8a60956afe37b18e73a1d722d0730135ae6b16376f32aeaebbf0879d5b0983fac4692f1086dffcc83c13532d659eb527e904a565c5449bcf027bb892b49705d1868bb87ce6e31aa7b4a0812a0d202ca935c22dca9709667337c2e6113f9ba9de5ac3586d0d1d6213e2986d6e9f16ca95d884fc3f54b3f78c682141c45b79b416a3611ba0e6776103a9c6f4dcbcb1c313f1dfbc48554e604c490a1b695211368b7eb5a91c99506579e84fdb5e7d61811ad4c4be4703e9d64e3e3f5a6457fb6f0164c552359d710387f5fecc32288f5f9ae7f54357d52b363f5565ddc81650b2fb57aa0b07cd836c6806980942b08a8a846f8a88ccded450168f5a8a090095168bbc9b7901c7dea1e5863450a797b6b72d5fafe6fdf77cc7ca25c2744e4dabf7a66fd6009c8c15388181bdd48268260c3338c9fe802c8740657bc664a04f7687c40f8574dbcde1133bc05339655eeb910a15b5e2e60f966f2207a156ee4edf0d47f9187502c67292921a472cd7f73e4d5485a73081681fc4ae76e429e32d189e75c0bb48028e45290b3735173a9a56e04c603654cf2fce060b815cd7952330281c74f83ebd57e8211c4f91642a53bb8a0dd168e4e46f20fb60b29a598f9faa41117d0ec6d0a0f3e485e53477fa1151520fb4b748fa141e8f3b5a394d6e08e9c74635f44d0ba08553c9696855f43c32c8d09528cb7624d5b61a11173b3c51173fd4fd7988cf4f0903408d9dd929934f2885adf8db7cc18a3ff3192e126508de67420b7d513fa1c79fcea43cb6f405f48ee930dc0f80ba8670df6f4a8744e9e9587421d3c7d1003a68f1e39c74e3944e6e20b4d5a28d1876f9c309d8cb297783886520e34b4eaac1328451957bc6ca65ea0e5c8c9b733322cfe229c35633ed69156bc5102e0b3330e55128d8ea1e7a65fb6d761ed18aa4a7f55563c738d960f3c7a1f669e6da08e6708133506fb6a7b2cf66cb669571761be9af4b900d43f25ab6a173acb5aa6498d6a4e9ab0325073983eda9e3a15950ed82963f650c51a2374d12413e15bdc06837ff90261b677909a288264e17c26e524420eaa20f8ec3314a9f5aeef55a30c76ebe8a387919cae579beee42590702e58d9fbe2adea6d87f61cdb3fbc4ad160990e8567875744bd051eb28dea97840a5a692dd51c6a11a53bab7e9a4f0c12be2cfbde340baa818bbc5c3f4c311abe94ea43181424386c4cacc94ba78ac669715170917e797f293258cb5382eba6d442a3c2b1b6c5bb034679121ee7e6ec5ae82cce9055f58a46a753cf169f715542c93f744bb563e4255c165f6a66c12912227283abc50d0c36343fe249db20acce81ed99f58f81cc3f694c135ce3367b8b8098cc0de9b196c1e08b98c8848547991d3e0a8d1088e2430b919df65cdba4ea21ff09631a2a77bd886220a6f6e8959ea4df3d42ae511982464bbb2557ebd253298403151e66d66000ab88ddfe254d9980f99fe917b2061955cdae91345ff90caec5fe0a80a50d25aab11bf239512446088c2fb5f0138f49ec246b08ca349ad5e463d03e183e488ea621639ea260797a82130263f88f0e440a6a8d8ab576b7f18398292d7a9060f615c7047d27466f536e71f9a045321b23cfceb4eb00b58f82c6e4c13c514a263b68876461561a7101337821215d39f602a5c770ab408a2b64d01526d5b8e9f1cf7261b11ca3003d29eabd7c90809e14f622027b855afed059bc4b149e279b288591ff3270bc26de46d5e0733ff39f2ea9641c9c4687038c7f248bed577a0c0f56795868751de0fe64f7a43942e3536e769cc875e9a233621c4ee964987841b983f846079f73f1eaa9f3facfea74ebdeaa9cf8ea8040cab6254d5ede9272cdc01515ea3ebb7783e3ad13cbd4697cd01c6b8453e42c821f1f3f2d137d3bea0c0648cd686cb946f293069bac368d24c9c2931c2d6a32ddb82ae193e8bbe0bb361e69fad50c09ccab6619fe1acf9ea1fc356c64e9c273b50a0997820c5694383d60f9222377a32b95da09d95c4ce5976364e401fd7cbf39583d8d6a8f37c9c8ef6abb47ffeebe034ca287cd993577a52d91190044c0f931e488ebc433621d2fb99d04e55bc211ae41e5cca4d33fe80ebc64fff6503ee02b6a21b18f0f3439fd6e8fa8c4e329f5d74ed666bf99858c0d999dd48aabfa94a5a743ebca01f2047afd8bf575ff9f707aff7de2f557e1af5e47dc468edaf5c3c41ad0460315452dd6a4082f4ecdd9d0f73ad452a0fb8f08895e8082c114828138043bb538785998754d29c956b73fab6da6872fd67ddc7f157ceff009466f646b5b40fa8fbf9a60ab0e42ea918d5da9f77ebc5c38633e052c7024659e698f05cfe18376c06a1386a27422745345a8cfeb03247ac43af36e761b44136bfe17bc408e186c09dc0c54ae037d0996635a2532e9f9eb66eac85150330dc1e9f805333c946d4cb6800ebdc30540bc839059d28e40cd633e184474c5c13d805a93391c4971e4e19875548748d47a541264b65c1ba3c312349ed3725b6cd1440b4d6c9c8ade7f3ce01c2156d467691a41457b503ed2eff51e1276de5077f4acf11e5b2fb39820a1ff6e8069da65ddf82276d07784ae8386b468a1c4e04346aeb1f498444b48e64712baa16e6ec88c05f9ab6d3feca3f8b0f7341b2a1a3a65f85ef2680fb91de216a80a790c87e1a5a43bed7518410e2475619ce91a026a7a932f67b6dc82001fef64279cddf48e8830d449e626bc06de8abf6923d07ba1d71916873f260a6995aea0ff7413a136f3b105d1ab31455ea8f400275b514f7f82961dc56227fd8af4f967d7783c81122abb794a7767dea2858d25ee55f6ea4b91f124cfe5ecce270dfa0bdcc447121ebb4b8768683d153f569e1e81f773aaa3b24ed55f9fc124e3f6143c926fedb05eeb144f17a4465bdb8b9deed684b08e3c9f5a7572e5abc4fe98f30fe9e5c477872ca274eb4338a5178b791e8b61f80a921021ad1af1c2300c3aad98d10c5ce18cc0431e2129a66ddb8d6badb5d8dc053a40367c83da37e219a584dde9ae40e20fd687495dec48d82079fabddceafcfe130a7bfffb2b83b70db3da1ddd568b1e304e2e6a7f6185b5e7183783f5ef35d32fa04d0f40764f34082e2a1906a43fbee9e8c01c91bfa1d8884113eac463c1570a7454ffde1b8c26b92c4b125efa8eee32bb65fddfe8be7a4cd9cb8a4da1a287afbeed2034c9971cea43748db73d2bf41e85285f71cf79072dffa5e2a2301c09c0b987e37560c691115a947b1549a22eb27c41211567447a6ab62fbac4f74a747e9be8a924e628724dac1eaffe2f33634424f8866f428281b9d338fa15449d6b56da2b41f241ed8d186b55cfbe949a866fb9049ad244f6bc82e4822bc701d6292f4c4205e52779f1fd3ea8575f72f7857e63a1f9c1f62b2272b035cb136e6fbbfa8254e491cddd3327273b93d2ba94bdd8ca3617d546e4afff8156b7e84608c48a5b6940774768fd1c370164512d20cf00a810de139bf0251848d5418278ba5763e0100dba8a06f996e3f723f7c016180aa5e61376957b0fdd9315e92cd504e3bddc51911e9831a1a817e736e5ec87c1bf2b0afe8dfbaa4897f6160877cac631af59ea7b5e54b47607d11f5ae2a05d48e33edec68d76e2a572cecbe7a233379de56d6a1710bacbe6da4060e09d0a0b05050754588ba94fe69164f9163c2157df3f48604f71e4808be9eb6f74752d982def3061ec139b89fb16d7940a42a6f4199c14d96d362060b561c54281de8c7f8e5daa5297afa2ffd1c1b02c50d6b59588fb987831182cc7e26293397c77bb83bd930f32fc6d8bcea98d00e0255c1b09440404429dc165c844fd71ec460386996fe7f96654f3589b5b4389898ba79de0f452c4729fdf4cf246de6e405bc642fb4e05cfd34cd2feed393832a10bfaf618e2c1d5cb5d561cf265043c6724acf5676f160590fe4968c2b612d3dd7dfeb56d00babfea4f7ac5a7fc8b0a0c8c9e0f1db6cb1c1ced93afa1fa9d8a18043e38849ded45492112c530183f7822bd33b720d5de5042ef1ff2b22663dfe70e0188d132da84533dee0e2c7ead8fc60d921e8f30e40f82fff6b5a06728de8a09dfa3fb8441e0ffc8e5cae7877889fc69a09f716d62d42555cc0d6f5d9d1a5b656ed1821cf676c82da4c020bbc1d9768ba5dfe55b273f9da6ac394cb8cc3ebc84226e64fe1cfd8c10142ee80a41ac3f5465067e324b107d81c7a5152d79b1f4fddb09efb068ebe6046a2b303c7dd0e7924c8d2230d0d774a38e06dc650d0ca6a8721df40b1cee77e1564d750e6bdaec8f4aabf3ef00ecc8a457214cea33c4a1b7cee0812ce3b2a842b030b7ef83b562e19bdb226efc37bd126f9800b42340dd7be09f50b2061f68c271a304e6d1ff9d6b718f07e6a624a18d2053258a93a045a4d215a0db03c199fdaba9d7f4155fb47eb9f36a3bdec28ecb270e758cf5ace1b6c51dc7ae7886bcced1d30f0179a27734a93346ff325512211ff4177229b8acbbaf8380a6fe4faccd3c611bbc8c5e52edd3c092de978299f63b9b8ad3e4d9cce27c5ca1fa1391a200ee8f4b577bdf16312bd89f7ae915be6480ef4d9d56d07c8688e7f217676a93b6306744fb52164e8466c5b361622384e0eb5ff0ce7dcb89f1350b319f8836e3ee608d3f6a896efaa4491f2ed0c3659f14fb620a0ef2530e2e1c439000876d647106e8b441aa7728e6cdb2388172d4c1082b92f6b39695f904f53feb9df6e4f19e94c8edcce964a6e0f029ac6436d2aeff96d64d3c3ecfe673f378b1b154af04fa39832361c78740b04ae9ed945ed637a0f382763fe0e0d84007ace72fda58ccd29c2cc3fdea9fd46c06f0ac6e0f15134a3b042cc1474bc83262514b4111d837077bffb61ed4730fd933a6f670d211071998e3af12c57a177ca32f7d78d871aa786c1d9746a6e6f1980ffd62286c3bd07168a5fd1f76bd72e3bda71eec0f0c748b85fd6aa1be3713a0c40a4a867c60e8fff3f9d0e10bcd1951eb959e5ff9d9368cff13ef55d7babd7c39637b214e697f19fc2d58627852ca4d6a5caf7e45e0936749b57288f5a621a08cf83aca0c031212b3b0ddbee364090435dd97dad7ea587780566397491a1498915c5941dc0290d1b444ecbb02c80caf35e2b07713d3f5bedfcaf8f44ef24a086781aec81919b8d66bdb61e541ee9a0b81d608ec00fd0114be82c295c6d5df4402bd5d922e3179daf0f015f91a558056a888d255353ad2596c1c3d7a68a8ee9f49579f66159b700446832fb76c1c2a3b505c9002257697c479ce2cb85e1aa66008a67a49f9074ed496cc632d9f6ddde9bdedf45d4e9f5dadbc4a58eeaf492e22efccdd3fbdaddd4abfb8bca7b989bc3377fde0b27629f94eee397f06933d7fbc5a774d697228735878e2d7c42cc8f2f4a50b3793f364f25bcc4be666aea80e334ca8082cebb3e314f3b49cda85932478040211509191b28651b936ddfa607c33291ad09bbe95ffc42c3b15d6c652924d20e88969883c9978897dc9d890d5e03d37841b3f4a6efb3bf059dba911cb3e15fef73b34be985779ccb3d597f2eecc99bfa1b9c9efdd3f193a0557cf850681e73b05a8a8c8b8848661764eb5e1e2787c113c4d851c9be187e6365643508281260612d250000eafad78264d628e1ae0ccdafd8bea7a35da3caa9dacdebbfb3de3337d9494a4caba9d7eeeff59d4496585299027a88d6560ec24d659608b85a594f0656e2aa5ac308aeb7b426931f1cee6f00ecf0d1576b5eb83d500589a6720027909c0bf0094c30f2fef37e4f373c742f219e4659180305d2633ac67612b55846f155d98ccaa0aa89f08991d5f025d240f63989a8cf3d1fe6cc4d5b26d2dc9faebc1bafec14bf35e283e35f078968d98c5e69dba30522d4db602a898438146290995dd5b6b27ba179473b9e9d93025e425777919d9347a3a668522ec60565ff58d190fe67a46f17d9f6a5d07a1c566a8d13e07bdbd39fda63ea40d203b0480c943de305dc78246858c2873ca0fac5b8879bebcc2a3f389ac8a8767f9c8edff7385eeb664af256486a3cc96fb82c213ca75662e94defcaf7891ec3594187001677436bb36841950f3088c0c76b5581f753003dbd2d4442acdf217c7959a50dcd02488e2fa100a13e60b21f231c83ec1d204ff559405c7594d0061e5f8e3fc29ea8fa1a60cad0bfa7269226b91316cfca8e1f060159ceebbcaccd1181502fb504888f720ff8f2572cdda0fed33f5114ded705e2c81d00f59a9a7dd16385860220ea95870cf29f336641dffac553eeddd6ba6b69d20b97f1347692926cb165eaa592ba3aef6e92aa4296736af65a7418696265ab008e6abbfe0fbd8479735418191f05745dc4bfcae61ee75f2b01df091871dbf0f447180f768c22bafd8c460064de319627d8e13ced97e20f56753d2de2a4b354cd6673d9b645a04207cfd9f7efe7cd91fc0b1b48062904cc40ebd33d0de2d0ad2f4cd40c38cb14d765752dd9d8c76a768d59b40063a757d70a9eaf1f9657df2c7b3b0037f7dab525e7ecc6d608351bdad158de0ca248449bc09bba7c463ffb42b887afc71dc893dda0d3939b01cfe17be1331f9505075c687bbc954189bc4c9125b132572cd57c0dbf736c586951a2106234cf6e4a8f8ed092cc296db6bae5c3aa7759a3b62875de8f330f49b4e5544bd852a0807183a0615542f34830b58744a6ae5b1bb380bb925c7676346effd536571610008118955284bcb7ff38c45a1b95e4b5595e7ea15c7a728945f1ff2db8ad7f73d790d78a70a3b862a45fc4d3f8a051a9efac14023a257ddc0ea789a349f4c3b2c6e1dfb7ad2397029ea4fe28e6c76801086fbbef32792bcec723f92941fb12ab14f01efbde56ce42580cb7caf94c6f65e9625883ae30fd6e2200ddc008c2ecb61d0a95eb44b050f1ca95b4190495b8fc218d54400c869013ce905a1f2d759e3c84b192020d895f3005276d89499bf9643eb6a0fb03fd36b622ca6d098d3ab8bb81b217c499b2a315583618283df4474c2c81a82981dda9bb6f5e6e562d78829d90e9d69ec9f1b1a8aeb4f7f027a9617309850f42392a0f9e78469aa21ce173568bd1b63fbee8f3e2d4ea38ad507a3c633bf6fc9e40bb0df9797bc25b03f703041c8cb187301e7a4606597e53a0a701424bf9304bc6b53beea2a46d166ac6042f968704edb4a9a4016081fa03b5aee872aa0b8ba25fd1f8da4356d4bb3f68a1fed16cb335f8492033862a391913ad51a8dbf05accd33fdd2e52eb8a83b090eeb456d8c1f2787d35b4544e0b066fb9ca7ff8c73649f424c2a56aebc941dbb7dce98231b8c4637e2beaa7537ae6a7331c0a04fbe83371b8bb2a99616520233abd97f12adaab5a92a05a09f9eebeb094ee5c7915049546dec62c84041204ac9a148a911ed532c53369a7b4762d9708dbc3b0e14aa5f0fdeeaa095a9d16faed2e584670aefe05b5efa482d0ba1cd1b66062f7c04dd580dcdb0fdeeb958bd90cf7ff879d3b10b66de162cccedb591573c1c532c1e95a6c4b93965826b8989a4a17caf7cb7b68203b21add69fdd0578cf7f1aa5a3ea4a7049fd541a33418255d22f1d57911b4c710c4c29b7c5dc3b95a73bf0223c58355d42b782b0950c5ecc2d5b25f2119f834c196a74c023de3f512f5abe715bd622d5777a887b954cd43127de2caf428289b3ffb453f5cf57fadfeae263357f3b5b1c7986ec1364c1d6b457c76d0bab181d39465c70a0c15fdde4a3d64ca0bd1a426610d4f74b34883c8875dafd7d56d4b5d66be0a933c323e15ed9589fdc335653e59459350a152f32864573c1ab04ff011c7c5877e35ad9baf4df438d14f92be1c178fc0c107ac29870a15717c78eac9a7dec62ba7947f059b518fa0343ad29715ef5db95961765b5972e05ed6a9ea75e49f8d5eca17bb61a216d5da82294fc0d5f66bb6efa10caf387d894be538b42d9c06581943a9983d8803f50e71020a25c80d478b97de13f634e418f389c319d75674f5e5d3eb0021e8884ba950594a2eb9b51d690ce867a05b3c6c6055fe39ccf3481a2f311122f946c598fae0f71c534e07aa148408c4bd9df3b4444de7dfe8910d370c137401ee4a7a2920d1f67229f1ba8c872108a0cf520d314558945487a632164812043008c8d4bcc2e42872965c5f1d1e986d45278bee430e8986bfd61594c84e99fb6836bb58fb037ebc18ddea90c4fcea5aea5a7bfc3aceb02aa95ebb9a15649b9e87c418ad64634b59b965d85a32b0430babfe5dc603a0601c97af56772549707a909ff58549e742f75590f55c35d622d5fb1bfb1aa303a8aa48a362e3eda8e16b9ede20beb01783a63733406766d0a2cccf8e2aff8562d2002b3ed5b6c469cddb55fd000a48a79662519e96c08e11b0be44d79c65bc86c143b57eb8a7c5075b4a416778c4100355c959c81255a38c0e74190b6a42bf238058cd9a6c317c6ae0be172fc02c08073ce4997acfc8b023a6ab95633ee45168f768257a39585066911ea59a017e323c2a52979dec2b960eeb4bbfcfb97eb531244693799fd63d2216739810099de83f952eaf56844229e58bc7901ed6dc0fc6fc645aecdbce2cd69ad62a4dd62539cc5e981d18d9f54f04c370690e340050cb3782a801aa46d6ded4603215c616c6375cf86106d0f03964428393ddbb949ac08a1f3bee0938490bff07bd2fb4876d7ec0a84c9a22d3fd0f3f196d2d4b1f9324fc4bf0b45285f6f3525e583893768021c15bbd4f170e6e92eed52ee1a1c0690d92dfa6aaaf79ce29ae2ac35c4cd22bd406679f92f52da22766055d416d9ab93050dddbfac1dbf8cc494f39510bb8664bbfab02e17b467c27973fc12bd6789c6d40e5785bcfe081eafd573fe10f425c1af984ac12c23c83f3b6aad93e52fb58086eac902572d8d63408fce47bc06412b0acdec165dab2222b9488c5276282a6599dcb0fd402e2a8a3314ada0d4573ec0ca98d4c4a70023b8bf9b209537e2d4afc6d7f48a19efcf7cb69cc7656cb60bcb66a4c876500112d5bea0f2b5fd17972d9119317c28ae82222be376899604071efd5b55cdfb19cf57f6c76d3b75c95bdfdc465573d946300b1f69e0a2a714dd713a0966fe3ca38a66b4768176e126445165c4d9285cc6a742b52ad10f5b6a65b6a618aaaec7cabd4596424c001e8570f0208e40800bfb5fe111493551bbbeb922466f68864ac556b696ce82840ca8121a1f2e557bb1b87d51a99ab79bb9364c9427da2dc8462d42a9c622cfa6b6af18ba218cd041a05fa980ab10ced2a316f7082a0d3703e80e968ad6a95e88c9de0a260139b7117c4110aa15848ac379f818f37735307e3d8b78205cc09e0061c8d4390c1e0fd0292ecd120ea907be83502e9697f0fc06a409c1d584239a04b35cdb764e54ff0750569161405ab009865f9d5c5a8f8425593de84c0249259703aacf5d5b861c7a32511bc915447eab1c1538ece539bdbc749969543ecbb6f169dd7a9f4bc9d0a6423036a135a6b5a3054b24a0795b6ac745ada4e0696bee6fd9a7763755bc5fd0027d25dc080f49f08a1c7a29e94bfa4367570e91015fb727b00084bde0d88f1346dd3a677141bec5b9df89b6978f97d9fe6411311bf0fd9c8de7b6f29a59432a5148c06b40696063b58ce2e2e75c30a3b54c43da155920efea188e7f4c321cfb1b7994ad26d7f499fa55851c1854fdfbbb887025349ba10c8b5b8e74fee3511f663ee67bb1fe7079be2c166190b37e39cf954e6caeff1b969edecf8ec18c3d2a712b22073fd21c322ea580cfabd1e02837bede45ee7d1dda3cbdc983577ede45e3fed2c493825d1a4c4122592300c9028595097a910ea72ad125694a83274bf061a1262d516f6834e01208175e1057ba11965f04dff57952c2b21e1743fc8c549cbede78b4490e77411199230ba1fe412855a2989a258240631d36eaad1ae87b70308963e7d661bb689373db0b0fb58be8f4327cff1865a3c47a65ac0180c90285ad8ec533ed8187660fd6307a107dca9fbb0afee1403d18da80bb9b04dbf8ac94224a8a3b94b970064699d23c1953bf60c61278463c19d9e84b8c7ef5ce50903248a52cbcafd1aa81ba8853c87955a551a09e49241ee8b81e842a7eb34720cea6be132dbb804fc47861bb5b0e04e97dd835c66e0557f0da92339dcee679882706b2020d8c5bd964195451c92b9c21c63f4628c31c618bd8b49ab14a318e3ac325e935609af4a2794464edc21b3c3d7d1b974b09ace07d6ae4535e03cfd4a875c542fb6a581161abce0caff915ccce08c005cf9582a255ff603d1051031c6f84204fc686a8c30ba524a2392ae9c010bae9c410eae4ca55c061f68f0e10ad214579a98c82b6e2044ddb99f86e4c314482929a39452084d37b06125850cb8c0e0c6972929a39452ca2dccb8b248cc21832231b86205b6634746d9c1084836216ed02b4a0c88a2c860f6105a8a82c46614e5891a80a274a1f5a821297504516a86e6b28230686c59c1183d74a95901d97e1f3f5470cff9e9bee028fc8a751cd02b7fdfc27b5580eb41d828a18fd80de95e70f7cb85e05a09416c20003610f22a56ac5c38bbcf87bc13c282530eeb5c9d1749c27361c7befa1c4910d673b8f0a58f287f3a5cfd784fe924f78a5a4b8a8a5a469eb3815ad4b9d0976d6091917b4b260a9c10a38b0bbd95d453dcf382b05fbd492ea060effce84255983aa9a7b850edafdeafdea6ec0766a7ed943a6ddf81f088a594d249299d74d25aa78cf14865f8353edbe7f4d93e6b9d4376c250f93054ca48ab651bf9f203979c72c6c8e48a570d0d3d5dc745c4ea7f1886613d17fb88e1440cd6b8e717c33862d8cb8eb14e4e1abbda718c5132e15164ccecad9921dbdc49b870157f18a14452c58925cb9879a68cccd0995bf64bb5b28bde3133d3e8439285efef459c918a8a85954208a323391457ca20842e650fb9c336fe3b52a9393bc8524a873588dd33b38ecb2fa5f4991f587feeaf9ed3eeb1920f4d29699c5838a564cc39cc36de53be67e54359e305dc73f715d8867fe60731887bfc38386c239f5049b1f03d03d9ac5fe3d39f4fdfc79b63b6a9b5a652f3651763a4b0729f7ce6f8317e8d2c703742140c014b5f1f2a02361cf20bc0d68f2a300b970e723804893ce7c3814860123f621d9c77212f62af8175b07725435007eb7c077c755b2e7c2884a20b6110131f419a3c4b2a0cfbc122b048ec985017621ebf1a3c8a4ccc3963a4efe329789ade0e538f8f87b3c30790159498b030794e9393152c2b50b91f77d1c55c41b15f2f2d55994f70cb0c06e038699c334e1a29e723d906ca3a645e2098518aa0d67a021ae68173ce197f849608cc03037c03ff026cb36242290df3c01883c070e434c173ea5fd9d61ba7f5d599c09ecf94daf5b0d72ddaedf4cc6e077b1dbdde81632d865197376889fa60f0712c8e7098273ad521b1d65865acdc446710ce912b304ff6fc04fb20221e6b7b22cfa2a1cf603da9414229d6f6443c16ea9995722e8ce961e61cbaa36f6068c08b71ded36cd3c3e4e569700078f9180e7b1427e4b150bf714144522cd40b79ac97dfe9a93e7047f358a16b58112c80495e618fe234adfb3a806197086e7d883dbcd7fb160178b39d6ec7bddaf5086101eabce02b340f0ef00d2c12d4a503ecc5850f37c036f0677a60bf1df98920eef4ec6c0fbf67c3619e88397624e39431ca38e50f56629973ce89434b7ef38dd401a994b1367bf2258434d2eb7dfc6af664c3115c8f73ca0ec3a8192ce39431ca3865431c11986c0b899085f55430a185988779071c1c085a69414fa432521aa98c383b3d3e0f75b0f7987d76787f027a4ca830013c27540534a046fa7427013dae3fdcf187298e95e906ae97ba7df5780edf585bd026784ef7105111242fb8a77d61bf06c2010aaa31a108568883e379a91ddae1a08840568c5a34f55052d212935295a13ec2396d442369243d84544292fdbad59ad2439106086b7bfd2d08b10159ccf0f234647bc045c22046cb4530b2eda6938cd0ca0f547a208478830e438cb10409a9046260f1148327a41a0c8a1f40b1a4b140f536fe5197a4485a90ba349207e5f8af9d46f8afb3a8feeb27a52fc0b8fc8d8579707c512f795133799108fc5f27b5129425a03071f9bbc53c33cf42355cf61c2ebf0b318f8c10274f5098839a07fe4ec23c359e997a28863b88b39871f987308ff69ec5325b69ca02085f91f41da0a700971f08f36c9ffdb763e74a1c4b636552fc1af3441c9cc4c14530a4c0c1c19bd4a505861439b8113815d7485a94bef0a0a42e170c1c3938d5ab3ec78f30824aa57a1a81eb8b83eb9b230b16b6e9e7e40c1739783b8a081c8d8d2e5a82d24b1c31b55133e13f230267c3c6ab9ee6336dfba1b161a3fbce707d5549ad994c86ebdb4250a786dbed9ec315629be66484b7895ce7fa6fc07042fc3ba8ff88db50b8b412ff1a9c5f1898b7f1376efcdc5430ba5cdf1a5c5f1bdc924e5283e32bc3c9a871f03699c1831814178485329ac8a9879accc8e217158342757d1b68082723072f6361050b10bee2f66bc9e23657f17bfd4c766d3f2f136505c8d538191de7000bd8217bb8d55380dbcf4bd70f70fb3f97dff7dac1c92227bd26f532a9add171fbe5f6c7cad19f9194529a9a288d6dfaef0b0e3a47af8ee8186d751b61750a67563dffac53147e170e5cdd14cd4553b31cf16736a195b1598e2abda662c7b6da1659c69325849d4786eeeeee32d66861bbe7b0fb0dcc31c61861d33899e3cc113260668e51c2cd889923a4cb186334e2aa4ca29551327394daa4f5c2a494936accccb55da6b294325e1aac106e48f51a820f9a7437e96e624493ee26dd4d8630a24977131f0c71ed3069dc12a511ad57b7d185751b61599c41ac4699d6bda46d51051fdb16512fb0d229a310b2d21967003123d44bb7d18bdd920961b52d052832ae91d70b42f889561636e0442bd5c00a3c898888882e1084e9c224901f4b581655f07155c6c9ee10b8f28b793492bef1e770e0ca1fe3987817d7454b3a0722273b202122f269313f375127c9aeab28652b4c85f90a8fae6bca0d47a69441d4dddd8493bea1bb858a0cd5000c2278741ba98f6835a142c59126508e1c49eaa2e5494acee40414f7f3a61b589f63f12cfee45cbadc7e08d450e809a66e4124b73f98e4360e34ba7d3fe8e4f6d378994be3d90697c6d3a0f1d689332e8d8fd97e68a4b80574631e66fb898119e3c2bcdd7e60acfd97edc7be2435715f1eb5fdbca0be6edb0fca6e54bcb89bd6d9f319377b6cfbc9b0c7be59c7c615216808e3ba6adc785d3508ba570daa6822899c1c528a6e553571e4e5a8e9ce97dbcf642ca6fc70a5ec02e846c874746934ea0a2692a0d19d5e9c71e7cb9d73cec9040a78dcb8e2a20301cc20258a3b7f3eecf93be69c734e1c293c1499472aced61ac28621692890b98a322557ed118613e65cc550ea214695cb2e5104a683d6124a684b4081ea914568092d7600b44418da0f0e8096d8a2ede048ca0c343a70fa1817e090c06bd8a0e486272f58c11035bc509204174fdc388112a61e9438f9a0840c121495008c228a904d9e03d3c43c3b57bec7033b73ea129626f7a41396eff76cb05f7b50c7b693e77057e19aa9889defb55090213ea00e74ba5246cfebe6c7ec27eb98083b2f7fdf6ed2b60e06116c700192222096d6f5635cca3d297fc716cb2c663df196945fc47e7f651f9162c41745402cf8251061e7f5bd954004fd19ae7735fa37dfdc9816755a021a2068b0d060a1c142838506081a2002e0cd961180cd006c6ebea0f9e23abaf1a51f350fd9c5221f827960175be07ef96354576c0a8a85ffcdd65c629bf8f67a178a57378d6edcb858ff967b514a396badb5ca39c5bdf87326a576b0df8c3f5b916f5471fcb874e36cd5eb88ee9045666666e618619451461923c72e424f0ceb4576ba1deef58685e5ff70ba17260ba564fc2a2dac8abb533ed81ab2a4fa8b2e5a056672b0fc195786fdbc057520f310d9f8b18b215e31c8bfeac74e0524ce8fb0db8248fab053018157e7e39e4f951cb322c72ce84bdee40e8d5c0daf2798077643dceedc919a07eca2517711066ce3efb0998705ba9cc5d2f599b4b4742dbd6c37a5c47e33e9aaf53a4aba5a6c23019f593c275a714f0bd6b996e0ca5bcde34aea9b55fc0fe7098c8b496ce37f3d712d419de88433c13a9108be929a473cea1b7fc7b9c1f5d5fda2d1b5944ad1b81d8d86b0f0bfeb287561b15f2aca993497a692878455f97c479a40404242435fea1a7da9ebd168769308aedc063ba39193e5723dd526b4c0a2db3bb0830ebd9919d248238d94be8fd3aec7bdf669829922f74321ae0c1fd75ff5abf603ead44e05a47e565f06d6f632b0faa1002c5cf58c0e7605f702fd343ef40e401d7dc30f2134424a18e39c11c2cd0bcd3d5869dce3bfee0b72be0a3e7757556b08e7c78fdef8f369a4f4c29f55c66cfbc9324ce30dc2aec73d7e290429b6d5dae3393568c8d40827e5ab5e9f37e2e3238626664ba5ec0bcc0d1b37522ad4163f95b20ee535eb7602cdb2b4eeab5947bb18182b63376bb56c4b518a638606b5d108af5951db0948a8c1f6ad9c099c11ebdf7cfaf467c21b6e3ef93306e8b703f25511d2fcc0c278038cde99bfbc4046196bad15d3a217e49251ab1a6734a95476cd28378a097976cdcebeb3cfae7b6919cd361be79c51522cd384483062fb334cb2775df3d9bbbeaf8975cc36578cb20552c66e9fd88226db10d07b52a99a1d1fd9c33ebb2676711c7db847934a5d3deff9681ef02f88b363a7c7c7840ba32bfc9532d6464c7bd5151b3fd36874340ff82c6ba6fde6e3478e1a3a128ad8fe2c93ec61cfbda110bb0b7b9fb9dd78d426943d9f1fc9503e5058df52883e4ce01ecf99b04973549394c5e92108a9500205dc34ab7082561cdd8f9f8c54b1e5522c325528a1e2099dc89ec3b56a74fce0c2b7704905172e597121154a94a6488223c5b034512cd93627845ea210e2456b620815458b46e604574ca3b0027514451799511459aa5147a1a536d72696e08a6b145d68f87082498a14d4f041858b144b62928ea48802e3e3092729a8c0f8a80193144c2f3ea02892020ceb2389c986d0961dc8a8e182244462ccb0f5b252121c9892f4001f72d6d27cf71119d77ca9a25b9f256fdd83af658fdbcfcb67bc3446521640542ca8403965d3681659ed39c676288d698b72298379b047daa50251336e7d95106c8dd0a136a5c6cf66d5e05440e6d7f81a1d0cbc84136b762af8365e22c1557d1958945301a9a1a26fa393477055bf4607431bb9c117519e644d78c07a023518d8091964518505c308628cb09ec0fc082e2725603db96c9060bddcc73e7a6aa8d6cf7a60b1e85028a39aea8aa5dafd5e75c56e936356dc7e7ea868c894115b81601d6a74a3c53c3a6efd5ae439dad7af4b6aad48b756216e954cd408ead09677469348728175166553e8d6ff3ca64cd6a55bffa353dceac4ad343a590457f52b16f6934545d207206cf759216ecd3a236e5db24a161a41212d1546fa3409ae680baeeac325b8c43c978663c8d2f80fb6986462bacf56d595ec8bef318fe950791785ac6b019e0151546c7fec926efdcf9b726bcbbdfa00a0c266db09a823b40e89cb6e16b577e2d6ff5697e9d2ad74095ed5ffb86b83def9b88dc2d20e86271d0c235c61440c202016edfa3fda623274f8820678c6192760b737762fd62eeed5974cee55d9d4d5cf7080531ff5319c0b7d85fc803a7009aeea5754f7d50e8663b804759a57f22fa65b65ad7c9f79b61a2c3aa4155f552e78482df8a6fec74179bf8ff77bbe00a0c2f6a52dda621bc90457f553296b5f70afc27443dcabff827bd508fbf1537d2155c6edc776cc36b2c5abfa290d95c4b2521424b8f52593b422b1340ffffa2f9cb4e25e7d54dd3ac9c436f55566d80f2edd5a9fb630dabaf5e1525d4a8171702fbf2dc054d1e639baf4d6f3e7c7973102800adbcf27d48e5e1cd60d81be3b80302f6f2a34f7d5c738f9337eb1639c10ce54cad6cadd43efc185d886bf6f0d2efc8264fbbda111f3673172e652ceae26ca08f99faa84f40832c8980922830c32c820030a2775a31c7ca15c8481b938ca197b2fbceaee980b058a42fd0ec8872f32ed51d2fa0054265325a068d7dab2e569cb962d5bb6f460f49caa34dfedeeab7f4d7aa950982f7fc2cf3cca0807c0016012d8bbe195ff5f1d38021de17f7e9425a0ba6b698c31709431c618638c31461218f81057f88c5db3d2ccbe68a80d468646cccfe0a8f1ed71564c43390fcc63e34dc9cc28902017f01cfb30cf0f83e76ccfa88f79a6f12cf3fcddea299e83e3f93bc973669ebf95fc46aa97601e8da56ffc6dfcd74ce8f4111f0d21b182cbdf46ae33c403f368277de3ef24c8756a918be4f287807974912eb282eb38f153200b25771d2515fac6df4be9f0ba9d30ef0c8e242c4937ba4eb2d17d4e6bf411d46927a73e3ac1efcc0c4f34b8ce3204755a8b93e6d1610415790fc918aeb3b496224fef1f61eccb0f16505bf77517a8558aa3c2ebc0b37ee2b47887e141b78b0ed8c6bb9dd8c65f6502db4eee79969685efe44397499001475b271cf7d86507730c14a4f4fa747499a1cec951964f5636b5c9495646b5c9455656b5c9415676699373568669936b56a53005ff00cc857f45da1549c918ea4cca51d66449566441562635c939ab59946b5ec59fd151a0238032406f2aa96e2bf409073e66682ae28a185e3c3591014896958498b2044add5abbb4e6344d732242511048d73d88a4eb6ec3511057e8d0843614e3d7f680694391f65973d9fb1681ece1c7ed04d87dfddafb40be71fbc93e721dd45d0b7531425d4c509cacc4fa5b21cb4a3ca8d2466c5111f3f434f3c0d760add76380504aac908c7d1102756280600cd09310286c0f797f736fddf7c2bcd85721ccd3832fecdbef25a1f154eafa6b37d8cf63803c4776355fc41428432c290289d4e4f6d35d4cd00f5d00a1a3bdac6ba1bf87dfee5044f38a153b1a07e77afca6d61c1397a5a7242748f4fc909b1f5b60c0196774b9197c8d6b01deac6b6104ac7be1078b1172cf638054f4fbd22e26c83d57edd03d3b48ecb7a34aa23b137c62806eb3146e24e4a435a4c434a5294ad1f58fb063803cc73f26c873e416d77f082d295cc073543e5428dac20c20459fc4f6fb78cc908a862c4a1983802c175e26a5f3c8f6ec25f5498f6681a06261031eae5003f5d3746b321fcd165a2e845fb9a072e1db1e2e54b2b2e5421d023cb17cfb3952b7c0f2737631c59d35b44d1137c62b33d05073e3915ab9a8324d58785989072bae52510e6abe3974bde592ac22992417d9c573b4ef9f409eb37dff0c9a25601efaad4566b1e23a0bb0e2f64b2a98474fdf10619d6a0487dbb387dbdd8f7cb8f590709b0065f9f0a0e3b737c4a20f13dc53c1577eb1ec5861af010b809dcf64a8633938b59e40e02225fd4a5bf3069be538250b2a080090223e89314218a1c1e005f1486a713f5e62923089bc6493d216407105f974afcb4a5a80711d91f552ad955bdb346d7bda59faf3b1afd8d72e5e98c7f004c60ec8f559e7b0a670ba0e7e0cf569fdf940eacf0ec8f5b4b370c5f163988fbdfc873a31d0cf5412988f3d90f958e701a19f750f57d9c5a9fa1faebc3b1726c0ac148401eb18ae64607d8f754ffa9935ffb33b7a609985b1266fdd35db54dee413a9ee2928c582c6cab67bda226ae145151096000921e44043910c864881640a424241659452ba515a939e98544a332daaa8e20b4a25d502884ae6b1db996de0acf9940e6fd6ce10a2bc6b615cb6e5a995b106ffbaaeebaab5c2bfb816209313a624383eacc484e43a4fe9f9895c92f5e7ac58ec790bc25d6131ec7a11c3da1edb097d02fc7a75f063e1c2a732d73baf7b15948f5b9388ae4d5b529710c218a54be85d8c526a1c690c62437798926908a1162b841036cde060fbe73f84104aae910fa53b7c7b196ab142082f29aa1f0f57fcd7eddb1edd3dba7b8cd148538411465abd6c679380b3b20e87088e093e538589651b8cb4fe080244cad28f2040a42c19fda09ea66d8e9259b21baabb2be438887bd89642066e8c3f8048f1c69e1839317262e4a4e69d3cd4e1791896b991132389035e829059cae78a71cddab8e615cd3e83dcd76b593fc6f9ecc0c1813d3b3b15d6895dd7a7f040f7ea68b69a873a3c0fc3328f38646f658c38c8a56b8aaa1359590405ed64a1c3fdb8c5c449f17760b154b12a150b338620458ad7a6e8be02881bab131ac07a7dad971541b0f40437a60f2a79442a56c10163657b71adb2835bebcbb8cba4ba4990aab5562e86bcd62b74b8b5d64a6997ad2035431fe1c186ab030f3e2ce121063fe0810918786d6a23d9f7fff00175504322e7f077bbcee314e99b2ccb28a793f3eddc0d09026854b023ce944cd21020aa51ee00502867004ca35c017ac80720fa48e7e0031099363926f6eb27cbe484048fe06a89e41ac990237cb3a482365235cadd08808800ab259ec557fe9f7729677325295a42391d17e3825e3e287b22cf7a4287b2ca8299c6c1a72cca49ce6e3335b69fa9820c5dca2794314a9871cc7ae19cd52c9a2a4056071089e0fd1e6a22a873759211d4615a5a4afab8c5fcf5d011fbfe52c55a16c06a860b8f8b62bf1aa3512e0030664cae0e003608cad5d020e4e5b32c6bbf2e395f38220f25c7e4a5bbae6943a31c099fd46d5943e17a89b7135fc19a238da88b8caef7587ce52b5f591c1ae546c84139115462d0cc689453516e0601341ae570684a502e95230ccaddd07ae834cad9782e4f246894ab7127c7c47ee69b2dc37e9572328f71414fe61379ce059ab5cc3886f5e282883c8bb9005fcde5873a06306947c38a23c01fc52df129bef28f61824b70e5444986409d6ed198c07eb5048d72303fb05fb577724cb26747e2498ab6c83119e2393de5e5625c852bcaa1eee4985c8f713d045744ee258957d7453dd4443d012ed8e1793bbe2795b2b6b9b0cef9e81b48e3395e73bd4b33b97fe1c2c21ca711b2ee4e4696c1b93939843da7f49feeb15bc121849d4ad5faee7fa1b3bfdfcf85ebdd06d8861f6688ee6ed8e1e8801042088bd8d9d1e3a36fbc081a23369e02a8c44d1825b5646a48020004058315000018100e8604227138200d345d15fa14000d6c94406c6034960863418ca3288a6218630c00c61862883104208354d15100cf6d212c741c9762cd2af6710da9a047220ba34a2a266b870434a88864d82015e63d5ec866a7a81e0732b49520a06dd85b492f7dfc4fb81cada71f7910579e36ca0d019e26ae24cb596810517a1fcc1d32a2e72627a8f4274fe0a8604aee7b3e6da7a2104b78aa2079b4c9f15580e60f842085cb633d2d709ca0f017a8510b4ca65224f225d4d13f66b52a2b8df981fe78fc79ae399a8b2994e90345ed488f23c32f2980aaae9e73c554b74a4dbda8972df2d5a616c3c6cc0e9215081ee71a3d31728b3e365da296c39ffb25175a3d8cd54021a3f860cba248b30c95a9c569bd0004211643330639fb978de2847e41518d76c48624de52d7af9c64b9a21233f2a3ab15c6b87a8990086e4c525a7a3697640153766f1088416200401bb7bb1ed40c4f68e6e7360898a62571777013318dfd77fcb2db2856f2ee0db54c4fcdd5429acedc02096603c4ed45cfec9781077e8c5d1765495622c8b486f39e86c1f0a70fdc7d02d7d98314f1949c345f8eade16092b2d2e021d54ee44e9064e3df7120db8652f3a4d383a98fba6a3992d1729cd0beb8b2536774178f7eb8592a3564339105c1d400eae2e0652936ae717bd3f20ff6a3d7349c0955b40678e2c60e51343f5b7cfbc9a15d9405c805c91d5e1bdd94e546b5771b1e4715d3e3efacc6f5c8bcd070ae2e8a1666464a6db08d93a8d76b2abe5af07a744fe630058801d916d9237e3e4caa0bb0ec1c6cc671c27f7e67ece190a8f473c8213b507c52fdf84d27e95e961a0c47b3f3790b5a07519b1d25893cf322dc364ed60fe170fc70b0ecf4237a0ea2a7d4207d0dcec034f3ec8c6bc8eaf1444a6e4ea2daef13d4a4696048b12fde22a0c2ff1d6582eb294c888320841b59e21cc5c1c8af6e20ee2cea07ef1006e2171c2b00df10723a50c093da32de32c79f7cacfdc38b88ae90349540268994efce5e9acf7e0bfabfbbba652663dce0abed22b6226b168e284a68dc24a14962d41f9367e8563ee5154a9101abba1adfa32c19f4ab11d5f524fed4c11e9c6001153ed0c507d8c781f9616eb0e37d5eb9185589c17e64b3d3507289f8915060a2def8c51848f44ee421afbb49fc26c0ebeac682885a49da702fbd9597ed2b285d66c7e9e5cfeed3a97ff89921732aeca9a76ade3f85995b99cd30a456110154f9b137bf97053a0e40492140301a0138d2f396b2688d262591609aea69d489f794eb4fba46cb6df23eee4b2a0bd19a391cc1649de293329ea62ec6be0b5e79673727b7c6dccf3b70986fa36ecd11546d5dd6ee3555f6a80862c94155bda3a11e93e26c90eb1b21437588e7e1d9586948806ca9ac7965bf84576877bcb3758e28446813b0c442ee2d40be8012b978c2e8aeb05bb3438c01188d703831e6d160fcf9c954d48f0b5c4859b2a7643117003115cb3633dca25d556f7284d13247618d64994b321ca202b875426704d1c59ac573e6ef7b7f7860187cac070f457caa2f7debf79ed89fe5ab2ed1d2b4fa1c7d00a0429dfad5d459f607b3dc30964d91504cac406b86940bd2ae7215041dbade70e73225552836b298290a6074010ab4b68de6d7117c0923a0006405ed235b862064f4f5c91052ddcdac073e64768c2af66e41d882a04d98a15ade024fa77fa738c07ceb3eb63a958fb8822266756d5fdec53d59d3878b48895486926c9cf2e0f6e98f01f507ce994025221d61fc58a30bc45933e8268f3803658536fa649cc02975ead46412ba41824566f46b8bc9fc4fefb9d85b92812799ed68a37dd5f7a84b78fb4ba0f619084023bb93e17ab46ddc82b07373de6cdf69eb0661b2050e84d390f162c285b3cbfd87f19000748661bcfa12fb9c927f1118ec1e6b7be016e952b827cb563d6f9834a7e480b5e25928db1844b4f06fc60962ab56b1a9024c2943923f61d8d4a4da0d6b194c523d6f6d0e65c471dbba7578235d9bbc91861c76e5194fbd1c777e72909b633b76e133284e60c743df1dcac530f4b01dd9510cfa916b2b431bf32abe17eb420d09e6e40d3fda3faa7384e14299221bf07405bc385c9ee13b17ba673c4d07875fe0cc9246964edc0ff759240622ea97002d9e8230d42d280802161a9b64a4d3db0f925c7a4315a64cda588218484cbf9020933badc965545a4f38bf7d9b33c9db5dd0737530fdc722079b1a19ba51d815c965052c32a26ef606f327ba8a82d2a7b81b03deaf12a0f21f5937039a88f10a998af99eaae17d09bd483ffba9851eee0393ead4abddaf7ce3a4b42d6342fe4139633d07ec2dd6738881e29853904a4990b3b9d4cff659eba43f400fadabfa7085d8a7cb37edcad7bc4f15b34a2a1f4098a4fcc6d2d39838a5bc1b34c7e470d0a4b77629131770329859fef5ff461ccb0ea2523b851e3a69e5a824a629330595070dea89916f13505794619ab8d6f38f3e5655a8e0a09209ebb43f044209bc0ca30a983f9e185c23c852aa009ecf722b9fa7d6b544876504e2b36a17d369cafd91052df18dd6e7277ababe51c63941561bc7d826f33b8398422eeaebb93d95403181bf8a565bafc282c5f8c6e61c0674b5be4ce5711bbda28b6b6889b08e8d0db36abc5cbae371a6ffdee1abfc9ac61c35e9a73ccd54e61514baafb1a53e2586e2094d745672fe584c9ce040c6f081790b5b8a3284459e7a51b15ed971db254d56c71a262b69f0f3fcedbf93390546645ba76a4ea12757ff6ce43ceb313fb929d14807a83ff9e94b239dce37ff5398a17e5823ba0fb30f93b76c8aa103960750f9b8d7c0c22ec949f3542210b540e14af344dd25ae0e8e0385fda11fda5aa270c6a4dcbceb8fb99cec33a5ac765cee72462d7be468f3596ef22b2330c644242d410693d553c656325ec91b85c15e0bb875e457816e0300446b4cbcc55d00bbbd22ef6330350802ff090c130086c0e217f35706f2bae3ea816047d24ddb59e0f3c76237503afeb7fd599ad12668043de3d5b77692554fb1ab82374b913eeb1678691d7b2ab6b19209dfcbefec7c9e40644b8448f6219afe645377b10c6972f225e68c2f6e93e7b8aadbbe29f40eaef477fc7656caf9961cf0b2de61b24bbee42c9919c61433aa0a094fe7439a0546d68c45099d20842cd0d761a548bea3c545931b1060be5f7a5a13ae65f849036639a2c0478cb45ea4c04c0f80790c0a90be4a089110844a995bcd036abe1bea060ba339086da5e6e2067a62a8f8fccc81e6f6375cb0c69a838f532864ea2763dcd3a03651d7bf3ac84c55d60de16f0a6930aae7667285dfe4814b47e6c10a337deba6f99ff923f682d8265a324b841f7283631a376ed282df3c087bff6c4c7a84ff8bd5c7825fc85179b17f156af734ad67c97919417805cee242f205d0b270e2acd09929492e5359caeb1f5e8007cda7a82467e18fd719de6abf7661b92bf3239ea664d18d9e359b8be09b5b810f1ec53e49f645cfc1adc02450131fe257b792213902753a96a4661b34499cf032fbfa1d841e78b32c2f6ce9e5dabc189098e4499feeddd5d665a5f2c9c1a7a7500f6e546489a659e373e183bed1c9280c58195be8d5a677052b5f490fa9d4f18e510d7232be23995c71482e947e045a3ea3580659916c0aa7b2b1eb093903503fc3600f282684f7682c2cd80a91a971f51bc64bbdb3515ba870cc45794d24df967a80a1d9800b73b28b97cd62bcb94531f8cc2839e6e4d70082074c9ee9e044aa8f55523c95c8700a24542582f253e10638b1b95763d632307fcee006b377a0e0b2f3e4acdef43a8a97ff8907405de31b6f6e00a5d1cc14f6d37ed2048f22e76332395bf8554a1c84af09df5d4247b0f5af3a06fedca401134e86c6275f58c236aa28f49a9f7b329a6e7316746754421e54ac04e7631bef27a5bae0ba7c810b25e8b816c9bf0fd9dea3f0d6e20dc7719ccc35202a0a0a9fec8a541be38721b1e48543c2c5ea963ee48a377ed25ea5339d397814501e17339f3e7c09233426c7e8ab0c82d768e8c3d5b5cbd4d8b7a89ccdd39172df7aed44f2fbd9e7f99f549b25ffc17e905abf45301be893b7de0daa959536aad466b7abfb84a59e1589aef38593a9dbc2d5a6008fbc4f77566a439aba00960b7a45b2f29746a2d19a1e51042dedf488627cffe5eee11dd94b1cbca173f0ee70c72021ce4910fb754a1e220d58f696c406f18d7be1a28d9bbc4b8e81ba0a00930d2aef7d9043bb541fc3684a8edc3f5e909eb8045a70910cc9acf03139c6d46e7c54ddaf6700dbd06d5d1848d770e6c05e07ddcb7626bd89e46b2db180a37f8c3cf8b64120cd5088220b8ea147c73e1892f05ff622c86f05ba9a93fdf89902bda9b5c6ed485e3710fbc89293656c6ea2aadf22bf0b5dbdfdc8f05b65e2a1cf0c45b8b3a515c412b983a66a84b3ec7d6736fa676b22f66968be2273b9004b3134ae0ed2ac2a07229658dffff9115f6d27da1d8da6ab3197fd00a56c4ff4e0a98d492cebb7a68c90464ab51b73a9d168d999cb2106ae14abc9403348f699cb2554970a1eebbf5de525ca79fdcb0502504fd4e4cfb66916e95b73c140b8102a48abcb7b806fd8449bdbd911f9b4bd03bbca4797ae69b12a6dca17e8b50a386e4133fffe50979ea635eed5064d0e7bdd4c4899c25720d71be8536cdb128c9cf77b709c50510f6b9812c34254be0139bddf73af6bd7368722f845306b5937571925393f5b374fc39d23b8a7d2db1db2c29874cc1967c380741a0644b4d282f0ca19cc4ae0c9018367b2090b4f71f454f6dc1453dacceed45375d77f468c4acd72fc96831899356897838251464be7e5a1928f829e562c810e58321eda8d572cbf9b0c4f7478a370507293c6b828be83d9a74099d60d6ef5ff0cc6abc9c9e8d875c23da133fb06170f760df160be6f7fbe9de2ee4fb017b69b21ea1c2521ae88180ab6c036e21d4c86f04b9cf5db88062e674faa204132cbc60298650083a8308b5516975d0af48423ad9bc89e84528c6ccf6b131ea38d4a09f0b6177346e774258de6197be8c579c075e4e9b0c90cc68f4a263615b597e2a97d835dee306d725967886a0f760a8defa8580d9b509372b4b684bd66d7312e06748276e1c18ad3f19f1056323c743c2b3cd88edb92e541b4273cd805fe6cb03e6657d17bccc3223bcee7d929b8987873ac61051eac2ef5087bba5597612a0bb24b3bcd64e082bc2b785522434e5c90dccf227fdd883e05403c169024e0c9958dad6da792c88d30e7928dc61ca2e0a8e85ff8251f9c424b0026a984957510f35440d6f5580693df6a31501ea541e791496940dab62d7fecfc38f056d978a0b5f15cb4b36019622795996facb2d12b72e14324681a1c1718b5352d78187abbdd559d63a9ed7319e58004ff0d7927d1dc1e52a3012e2de768d9726a009eee2f1968fc611a4748935b5cd7848feaa1d2b6cdfe489a78a409f5e62db5856f1fc4dc14166179abefd2c3450927d5e18d380fd8a88d5d94041a5559a304736393b602ffac860d1d47cc96eff83e58c0800743d8b5f87274484e0849cef47b4092d46e8680e4a0123b80a5f7c19c15a673094c7f465c4ac0274a821b81094342b24ed3b44b4e2dd0d595c064b80c179a745edf1ece05e075599affb68af0fd883dfcb3c2d95252a06089eebf51f73b8ee99b485dea7e70b6dde3f47066131cf5d35da76c82b13ddd33938fc9ade782ccf0ee70cc4df54e3055dbf27049153bda9ac2daf912f2a2a208c779ee6783f35f3b3a051d39614ea7aa4f369572293677acfb12cec122df20d01821cd4e36f9d27bb8255811224afee4c4978f7e9c1e246fb26083c94985a3e000742b6d00e18319339d14b1e7ceb280c0ec0489edf2f9e58a6a1fbe644cfa208318ea6dacaac7cf6199416d1b5483a215b8d5e3b10de017e651ae779c30f095888d830c21c2a2320d8ac30444a8dcc8240b37fa05061ffd2935b43d5cee535b984d0466b9b629df22ac0fc9add865f74e6220d7f155be5e45cb1eda5fea6d523b14c5ce326049d38b1bfedeb104764f32bcbc320c67c030466a5f1ecd520133b1e7802ced36944d03b42ff18710f2ffcf50d0048314fe6473df804af84a510cbee83ed974a8668c9033a1bafb41c91db9060019be45b9c8c17bc42b04b49350369b6a1fa18f58ef2d9afdfbc4fe3d22795d90a53d70858012495ccffe27a809d90cb31882976251a1b5f831a65fcc89bd6f4aee938d0e7cf3fc6741438f7335bb2e343998e35f734eb0b0cbb2a9cf54965191e07083df7f8d2e6c42fec4429b6875b31ac0099b23978f249b979ed7327dc6cdf47258c55089ca8ee46e9d6167c5fc1aae281b4a06225fdeab50c1c216ec87033b3f6eb75abe540e32ab252ac1df4215c5a4752b58f1811818db17653da4f7c5d508b66bdd035b74706471303ab048b78437bcb91b8c2f8dceecb329c1f716ccea21539064c01f1a06d82b38b873b108fc031f1e2e76ecbc677ec617d270f1dfc8c6818745f7e77c7be229418151fb6aea6edc0025611968a44551cc114880c01f4c6fb9f342bf7879d03c8c8057ca661786b7288abf0ddc8eaacfb4810ff06cfc65ed847266ecb7942fb2fb2d56b638ea9087556b5df94426b1437eebaaea9948f616283429feb54f2137fd964e3f21a297a4b8327ac94c23d0420df3983ed523a42539baa50106146d86313876b6c92916b931bbbc6ed3e9f63d1a978304c776eb043049ba389bdd9f7551e3d8289e575d92a781b27ac3d2e52b557470629b11063fd1d172def6288d631933bc5eaaf9146470084ab1721fe14a75b6d0292412bb64204ded719f58e725449046a3cb2997bf4ce14ae4e20b8c52e50723c2aa5fe24e4b7e80be434136653d8e61333ab402893406292a64b1ac275641a4f97c920c3d071044e9dfefbf471bfec77d8e2b853817ddf5bb547755d450e881cf6a7c07aa60a26445fb072b4fee9b6f63328d1f59b5e0fe60c445b6133169b786d6e4b6fe545785d4b9108ed8b95a514c7338e41218efea00823d656c096b9003a40bfd746b588a31544063c0fa23df7d34b512f54c16c8cfbcd50f55060f404220439648cd2572e987c7980c200fc4f2a04a8766a605f635958d40cb331b15803acc0124d1c54a78d7f0e81489fb23454554a4f1dc81ed3e40097fc3138139392be645a7ff9dfe0a22fe30ab5f209a647464c53754f730200dfb304266416199428a346b7a248b26af5b27b0f82c23e1da3cdc93291b3f290010377038e078532fe6e8bf7067a14482bda3a519a726c5bb76771910fc5bc8bac4115525b42fc58001b393508977462ac7ed70fc0e912fe65d50a91bdf56de87e44be40f750903c25d0b756c10004b6a1f24d4eb32688e88c9546613e18cfd91c4b1c31efb47c93ac6eb6118f2d1f607c10dedbe8999a41b19341cfb2b7090d5bb02ebe4af23177483308a385cd9e12dbc63f12e56051c43a26936e01d6ec966470c06c839452485721c11287b94401ba3ad04d5ac128791143a7b145fc7ab9d28ff56b7816610364df2df509f1aa8bd3dcb0dcba5d1ef38983d88012ef8dcf5d560898e517a7c398966607874476ead4d0bcfd09b8b0ca4a121187aecc09f3138c520d57ebd66c1f595d99905624f4618b07aaee2256766946adade6989aedc0dceb4aea10ac93ccbfb89345db65f27f9495b18889040d653a0c1c0e0f4a65fc35539211c0f8fefd176fd03a829a07e455a713058b5c622d61229343265156a1ab1b8b088a310ef0a7be2a78a92bbe7f5f14d71698ecbc08b71f4ae1d3995ed95d17532b64074eb529e72094cb9039023219371c75035dc67eddcf0da246e41eeba8b21c26d6e560bb07e63073bc61f10544152d9ce28d4a20a9ed33ac952d68ffbdb610935ad065083cc5ea47dfab7c0d5114b1e4202d641e0d2fcd45bbe82abc02d7874b38172293e676ea2397eaebd64b310ca632fcfb8e2bf90cf19d2dd9e754396ccfb8cc690361b92fe37172ea23aaacb9737cdba4fb2b07880d0da410d342025a768df6053a2e5577c46c7a9177980e8028a682e30705c61572a91a1cebe09a1c8cc58a1118a0b9a88ed256a50b37e1d5f473424268d50ed1a587cb41240ffcd4fdaa7359b194b95271190863b96c10f7ffd7988d89c56ccf8ceec91f1afb51af4b4726d712b2605a545b109dd63dcd694e0e8355c3baf0c23c695a50cdca08e952bc22ed83db8cb57e541711ca3b5d9e8209ea08bb64b4c7ab378c53c5f1e4f571a9b0aa934f6fe49696c5b6b5d2130757bd5b611d960ada6e9132e8de03234dcbcb1a4039255cbe0a5f789b190bbe06d5c4a3246b6b23ebf40a2dfbbf396c12448dc882b31996abbe89fa2432ae604c0a4b6c2f4b4b22a9f5450acaa601a75d81227450b21a1573b6c01ca3bf0753f7c5708aafad5800a043245bfbcd565ec351d8cb028469a0235ef88eaf71a32c522ca9d532de7befa5422b3259be8bf657918042aa733e88b5298ab2001bd42926a26df3ba6e5f569d06a4bcca592340e6010d6f148383c0282c81c31f4ff281fdca00411863ea034226134ef5004bb978a5e4291305013b26e172cbfff24755397d88cff05021b5f82180da7214dbb4c523e444f22bf4558dc8cf3103fc4581d1688b94c877e9b10f0dc3e74211a1807a460f81fb8a477ea049c7d5959819c5ac5df48c730972929417104303ee5050b82adde3897a70bb4bb8119a00084288476227c746d73295047d561d8fc2d85a7791052b6e298670ea848bdb883e0e9e873b8b5fdaad4e030b35e0b47abb8f62634443dc6f67cad0fa33eea3bee20327a4a7b6b9470259b9808e4e0652e055951e06fd9ab662de9eb87555497d471638095c2721de0e603c6d5a8bc1aa8ac7ef778c6bb8a5628490869a0793a2589684a9e622e3d7d318ac77e5eb1780c57f264963cb99ea46b010b042ca16fa71c1b1b07f6566d1b78225b65472a49fa3a9a86bc44f42a78d897d377c2770c343e0b2b31bda94ab5961fbaa09e4544fdc7427f5a8d05efab2268071d6fb325d4f279cbc3bfe249be83b1a2a5852cc2a166c788d71bc57c94fa873634229a12f70a7ff52811cfe4090115d76bbc2068076074a459c675c1cccd4fb81f2f2005b9bd602bffb3c625493361b6c5a776bc0aed5bdc4828c45bf25d1c6f90261bf97b3466d2708258c7c1cff0d540ab4fdb64b67729132c2f0f12627519263ef2e02a02e70997364e3981c3ef461c9dec7d32921a04f4f32c789ce90ca62af7057cd3e37a3395e6e51ab0de94a0e567ce0bcd087a548d3943c5402d8c82e2841ab34be661066905a55129711cf3587ce0700d0d9448d3133ed5f9fb4039af6c7a20fedee9052ad081f676ccf2ce8b935101d32ac29293a46056d7c208427151a50791a4962590d90fd72482f83d99af01284d6bf708747307dd014727b8752fdd0036f57076b859774740723e496273f55d5a4716a2ff210e59d04c8c33aa68dd815abc3baa3255823b75f903e15c5da68110a76ecef9d027c7eae026efbdc5409ad3ea9ade720486ebddb639899b9d68eb76778372e5181660e908a22b1c9683ce27611fd8397165f2f1e3f8db1b9d6a203bd1b4cfe93cdc135a5ebeee48089f4ed0d2edc30d097b9c770b905954de742fcd7dfc7fa042f4404d6fdda0c6c13ff7aa47acdcac7b13a5b45e9e829a2506ffef0c3526295dd295c1f8d7db17bce1a450ec3116215e4b5b13b19cce8fd83382a82ca66f2cbcf89067a307b9e1520d3bfcf43f4d8fd62db5e567d57a2ea4f41330844536dd22371da9e7082e484a6de144da05d107e98d1fa14de75f4b8b192c4c1b12cd3902dd1cec97c30fb425dba506d7b0eca643d7b1995dfd7c58b5f0b68a10756e8d3056834722b607680c2d4a871a449701eef5db60366535fbaed88aba51a920ebccafff24fb4ddb03ecfad695b245f2eb61ac1a7b0f572f8301ece160f016e92928a6cdba1f200c9bcedcd7217f3157a39d1b9f81f7bc7f4d2e5593644d7c448b7d32ee71603f0f8cfec05c23d8a8f05b135d618fe6d388df83a6fbdd0d8e12f1a398c9c8d6e31e10a93dd7ee6b9e4bdf48ea16b9f43473ee9fc12148585ebbd641eedcc7940b59d7a57273c7615f5233ecf9bdde30c7e1fedc50467237971977b52d6d7eb47ceb62e033c3e018b91521f63a6232f07499ba4e24ec9dfa7cb3567b61b662d92c45774954916b84d794a60b8e3645c7e24b929541e88a0b7f5a1853755fcdbaf48b1dde695784974b6ed5217fb82cc605ee343e9b2b72ba84037ed25a48ff2b5a052a2500e83646766e49ddaca05f34ca15b3ddfc1d626e67cab0c7446069e6470ae4bd88958077c2ae27567eb89019ea0a0f243327ab3761ab2026d6c42d29d2d49802649738f11a558213db3038b459ee2e749e1f6b1d7ca80adb5b059e81e63dec17be2ada9f6d7ccfd8d1d4b3be7dfaced0e79f53fbe066c689efa178dd51757d4e3e4dcd30e8fde325e3fb01fa1b89d032df1384d11d5d456857a45b4337fc760d014dabcc4ee5cf8e82b36211f0f730d56fca141710bccebb8a2d41eb4fdebb67f231132ce2ea19027c5669582d406dc2ec65d6c8c02d939ef5f5466384ecc6b29c558297a16aa4f47446c96baf91da2e9f5f4d73bc9582de5aebde77e3494c4b814bf1e248b597e1937b1cdd48997392c73e994d46ef5e66ef415e04d2b69f352215d6894a516589675a6b1f12a6a159c233a5ae33d8b9421954ce3754e0d0d0660336f959ab75adc1cbfd2ad98f93fbdf05eaf2274cdf8dd79cf9885f783873ba845805ab4db2325d2e2e4259dc79b5a3629a318e9de32935b703edaf6e92f58ac429743d8616034e1e79cc438d4aa496e5b3dc584b12ad427c0ce692fb0f2bc03bfb2d08eb3739427c7ef3c5070d31b7b86927242a31930ab13a09a5bbd65a5bd7fac6af6a2f6d41ced85b1ea80c8d1cc0f86cebcceacddd4631d38847c4c0d2960a3ad2c607d8e95b9f578b7ebab70669e91f4d82ae4904444cae0114e9d822e84cff02dd1f7d2ba422a5c64867d089a93b14615bf4ed57f8080e46d7037b88dabc0be6bba631932bd36b4c30dfc220dae37c151d7a79624b095ea4be7d14d9e7e4481def907c2ecbdde4a4bcedd2d4e0bc4c88d167d515173a27b887d1b6752016731dad641b5adfaa0456ccdea9a62a516449d00edd6cc3bbd62ef8368b889a6508a01bfd033eb5d95a3e101cbf929fca475ff2979d939fa30780132e2043b0c0582a717062f86231d116a324f59f98c61219cfbbbbdf8d28071b1884f0f14cc92f63af2a4d8a8220774c83b503b6d9e379fb1950f76e97114b371a606d97cf2fec038b570785db494584431d85af6384be8ead4c55dbf4beb3106b64d5ea7d30e12f826ac87841f4dceffff414352f78bfc51c4c47ee36b56196ec9c47b213655628afeb8baf597b0da1ff9ed0dc3c914509ac458e93797672dc901a3901808310674ce12441d430c243b8af25c4bfdbed13385904fa0c362b7f4ce6777699cd867d20fe8ebfee60ffda60344958be6d5b4360f0a637e2c13ca5eb513417e3b35a729b10edc3c368879e86b5db5a7c72ca8e7f76321fe34d5c79709daf13659e518e315a4164ded3b1aa1874c4791e735e16a1af9fb755fb865c0fe5fbbb96343b8f3a4ac09b37e51098def7efd594a6f18ec81a58bfece9a1547bf199207fdec6cfc1255e82630f36b0df0573d7b874cbbc2951e79945426ed9d9c97dfcade279b765c4742855742ce9c29ed24161deeb95d8375531af325d2c5bbec7024875c04bdb280f56e4a5382e757ca72791c249c0a6231eea3bcc20de2a5d017e2411c7a9482eb24c8aa0c7cd526e91b3f8e9406b83c3c7d008158b6187f6ae3e93302ae485ddc0c4ec69ec4518e564b871470c76115858cd5c6dbd834baa54bcef0e7435ccc924a5d000a2163ba4c50f917bad39cd63a222f6791d1541708a92d682c9e49896ec20b8c408e179b4abcf57dbaa4c43aecd217685015b4248de217c735f548906959c7a07ac894480a1f15398f7bd527eb38a7996ebb1ccf23b16417a02fd347daaa333655f35247788b23229d1d6cee16a5eb1176bd871144326404c42193615dcf673c2588b3dc88ddeccc9f2f60fbb4b05b6b641aba330a23cceb15a9a0f4028b10d000552533736b80d8f94adc98e29546fda618fccce6ff34f1e550475ea367396a4112465e93d9e37469041ce179fcc9a4c438a646281a046664efde3b5ad7fb41ea45d47a0f695f86ba8bbe6b865777c0962bfeaaade1977460b7e69b05943ccdee0a92cbb5f4163f1df6ed690105a8baf48248c0a99503ee6fd9f06f0a8e14a1028f632443f3e871a2a68737a24f745cb42b97813a63d5e896d6315fb2ed76259e55b6c82f606b421937e1684db84fd8dab4deeaef7c6092c4fbad240dbb978460e23f3d014a73c79b8c31558e144d1eaceec96f861649ee3b511ef1c15a0631870d3077bb68f3f4540814ea3e10608f36058c3045f009f07198d5e61bc2e73de833fae0bc87113aefee46b1c1fc74494b9ca1bb285b859c8b3f8b69f27f4158445f354e33aaa794f918ec0542d0b7275afd38eb4b299abd9da95aa4e19ba5265f72160f7a57bc1b1534dac21c206555ab79d14ee5c3bce98e12a0938352b4468cb52d1ad5879c0c360238ad68e3ac4e4cb3ec996e1d1ee0bca6d703f61e49b86797da89d7bbec410f78bfb451ec4de2c1548c358764e0e8ea1d593577ec0213c6b97f247968f2c57218cc609ebab4416238848dfb666820ab980ae2cd7c1cadd07f05f1981c227897db51440f6099e1e8d2be958e1383a8dfbe69a7a0230f7a34fb5294ddcc6083e34e361582d55733dd50938906ae9dc91eeebdb6523e13c6f17c12c689d12bb5b69892ec10ef0044332f86e614e40e1e31d87896add1e4487228a601339131da32a092b036f0ca99ec18ed611d0f73b83479a1a3963a330ee29f15aaf78b42f97a39c0a2b1e17f35689be66bb7b404c59e6636eb764c6965d19dc924a3de913dcfb1a3f1b9f00ed2fdde5553613ef39889882d86eb361f76d5062906b667a002f9ca03c2682024a4b9685b6bbed4a2b2a9f570897a53b94499f6e6d21dd61eca6445f549a096761d3dacee2d67ed851762cbbb81a6ef82904da65b5da50033a329d62652eb4679e0f8dc18ea7c5a50404574319ef9f87a79a67f25f881302cf6c520e4c26f6e45292e3121c24e79b5b40176d3daf63e0a4c15c5ff49a4cbb22739e0ae86a9616d3cf851bc90ed928941a433215ca09fcd092367428e9e3b5c4daec9915a5185fd1f88578936f86d294772a7f5551fd3b39e3b9017f3571f6d2619b03261a77710591a3d0c77607533e66ba5d26b709aae481fbc92a05e4af1c841273f04def50377ac8a925f7f405595cd23d3ad2893fa6a2141b9fae0636447f2754a1be329b32ce607a758a0367564643e9c617fe453331e36abfba59fede9978e4a90f6e29c633f8854001101bce20083cc3807f3caeca63714db9541ee99ef1ff0c336909209acd2ed0f232dc29f78f7764bb491b84c53d3455a1da37a97cbdf7166fbcc73b829302be1f9bf234481339cf833cfc8e994c39510a637e626f1b72b8f8b5cf313e95be5d10300901e1feb09206e51faa94227bea497913e15a8fdd018fc2582f07e2878c4d24e29b9ecd72097dc4cd574328aca1bd5201ae4ceb25b972c10e3d6ad9714d5690c20c03c2ce204de3ab2b949f2ffbbf7e5811d21b597d4278e87c2f671ed3b15ecef9f63bf7b2c32c2eb4d129189b964afac06d557886406d364f9224f6252a3562b60b960b437af4a3fbdece3e9919d748a35890081d8f7c359e7985522790bc2e8fbd6081560d37c69e9dec93258e434de1c0262c7e4e41de2151cf2ba2b7af765b8065db15f5d6c0bd3ccbad46a9efb13d06a97334c5a7d262309e8096f1b0d882d5cc8cf64d3e1d0e84308c872a407f3c617e2e96c5f0e6cb8944f51fb8c3380ef6a1d2732a3f5c595f2f86e594ecab7f502479624f79b300f878a82e4de557713f53b69f8225aed45836b46cbbe410fec329910690eb5c3b75d8747281bac443c7dae474ac18bc25fd36c2591dc51f8c7d185555ce7dfa77757f96b9eed06c967669491784d11f4c710e5918433342d06acabf617fb861196947c0f3002d5d039d15910456b16752e388f258168b44c0331996a7ab9c7f737df9e3f250f3b7ea095fa827c606b0ba46ea1a6dfab96a10db8e00249b91ec76087d0319e738f0241816e053e98974111621cc785217fdb1d167c4a164fa3cd3b2d2d7fc81096bcfab24978b3ec6b9be8381a2fb8fb76c0ca860960157bcaa442208e4b9d8892e50d66e94dc18bb64ba1e20ac1256de5e8ce44f2f173997c60e96094df086030cb445a8e4b99eba757285548e42af772a930eaf6abd7111379a2c2846c451143072de2a14d1035400385c477f38ab6da8689adddf966de5a9409df7abc293845fac56f570819c043950691c97bbde06a1c6e95c15d8531276c20c5c795f2265623bd25a80e0e439c116294539d80669058fdf6a57843ee874624b5a461ce0cc77c2859e19a201b5c2e147ee496e7ae474f1186004881ad88d6914aed9b60052be310a1d0061d7a462872f7218f5c25c1296a93b60c35cfc09e2df6e02a30c81900456e8fb9672c2661e03422db14bcdb0ac182ccc9d28aa7a9003c5af4c61df340299b348745b3860d08e462215d29e24d5d219663184f58b64182e0512802e324c2c508eb14bd5735d3ca44fc542df8a08abe2ece29cb269142fc20ad33ebda3aefdba484357ba095650b30d28e8caa673a0b46256361d9aa922bd78e8ab2cd62fbdff9492c1e967b2a14810b6d44a24f76030bbb73ef327ed9478b243af939c091881d44ca283f892051ddda5ecf10ca0598f425efc068d0f1f330b8190368888aec5edc69970f824e7bcf24926cb309dddaa5dfdae70e607b41f5d4c21cd84e5d2b5d3a3e6ffb6ea1a41db07730170f810158e2a51ddfe8a43750433c48a7638ff1021ea825095afe23ddd368e86b51215830bedf1e0461b073bf18288df60a6b24605db162f434b9d9f92ded6f69d1443ee41bcc04df4fbb85e019221923aefa352420494481805f230b3a8d81630c5e9911d4a8cf270ea312bee388242bb822e5016f2debfd895d4a0bdba686e83ebc98232fb904f600404ac350d49c24396501dfbec5d16d4b91329ad0489d7635ee7f031aa8197629cac67ee5183e66bc2dbb0afc2ae46eceb5f695724168b91dfcdaddc9d72e4df13cffc444be3413c0048174635d68621b22924b1a7d4635a83dc874c070273607b8b81cedf72429a5aec84aec9f9a344209f1050daf71a89f96e3ad64ff8140705ea6c994b6e029263a999359e16b5647b0269be3e6540f9c9925ef05c3aee9f9511eb21727f19dc48298363e0fa93d6d8074c01c86b73d7f1318a1012c3ec25093305b6e1e69f01f0276d70bcb67eef9aaf73f98e6bfdf6f70fa0a9f0d59254c2bae83b21c7af2bedc7c936c779412efb2a3453a4bd4d7231cf5af93d58e84f22e5f1cde58ed8b3f6bdc730bc9e97cfda8bc348a5fffffad125063d25f946b6dd4a480fd34bee1bb6a6bd3fe1ffb8d4fda10e0a7e4073fda4d615f55bf336ae82600c2fd3be1d66ddd014f43f8ac7393d5aaf424fb743363d68b2985533254752eb836c2492b1840442dbccce87b7fd128eaac5c59c002d03032e5348257cf27a3a7505becb2f6398f5142fc0ee6a5582cb5533d31d4f06a46136688ddacdc387ab9c92cd75e0c824e00ae68683c60c8bb1e6e6a101bd34358fdf30bce5043995d08e3597c7554946ae3dc3245bafff848eb8a5c97762afb21c4c8b2f17f7551923c742e8cf45bb327f635f1dedf5a636355ec161bdd4391e57f1d3f10a84ea05bcc116a7e32b88993a1ada1234180e70557bbf953d592734e5882c3c6591feed67aa3b74ce78077901ab8d23b701f580afadff24a019e4b66af37f6a3e2d9c0520291f5d9c8736c191469c45321556ff788b6e4d3e9fe1815027a72c9bd54329c53cc12c1fb98abcecf6893690467d8269d9a33c06762f6d2d530898c1147972262b055904cac1477a6088b09dccd8674ecf64edaba1ebce01bc8e3c0f1d9bb54db42ed6cacfb29fe2f867716d85afbe39fd26be51f5f12e0b1e7f177947cf59d583a8742dd44c0ed099f42c84326868a18c2c2f4c457ad70ce2581f9b6707b88dea581a88cc8cf4736a665465ceb00beab21bbdd8e989f3ac3f11f9728ace30ed3ddc69fcfacf3a734ad735b43929cb686213fe0856d480cd496ac91a15d273d716a8135369b36e09fc4b12cc0d118fdb5e90e20f85a9a5f721966aaf98819a4cd1728e765e5f8f690e1ba4dfccec852525359ad8c9006fc2472a060a1b259155f448e4365682d7cfb1f715cc29b56d28d251a610576f5865f69a97b880b0fc58da3a6603dfc823cd70b68eafd2d5cf4c7ca675690387db2a5a54c1f928b432190a0f9fa6255d47bab9a0ea9fe188b7fea5d434e6bd913dfefd837341245b557353fc8b4aa6f5a38b46717c64088b84276f157217c26c63b4c104952ffa52d25830baebdcf77182de59b42831c7595baedb5511b760b7f08193f1a66b485c30a33e63c8bfc54f047025ebc0f116aded89f72d72c35857ce4188c0f1a5160e46f655679cad619d9d96275b14a77ab75df83dd92265c005e19ba1daf5edaa8eb788d6b4077cf70987b5b367ec44cc2d86b06c6d21412d8650c03e56e9d94ee9977295617d626ad2b54500d6f5d614d77190012bd2574951c9e57581ec0ce44d5492c9e4143c2863379bb37d891ff1ad81505015af1e75b7fe672e75b531a757b696b60e2185b5570835b0031e235650a5e4752f70013f63d453c83536e28aa2fa868f93604ed700457de85cf21c167a2b843491735b8488f26f37ea43e3390ef0655dabbe534567c79af9ed0d298080ea36d84d03f5616b6339b741e4d4ced952730b4b560843515f0d6161355341fda31f2509ccb65744cef6a60200128cd8f3ac3aeb9ed6b0ebfb0e4ef3ccabbf3087fccaa24ee19ff97550eace56b267164c56e131427457504827004c7d2d7282765d278d27c62f4aaf2735c1335f2c5f28db0a3dc101e04df874c9a503a1c12c20c66b169582ab4320a8ff996d300da75885128beedab12192e402cdbd73001d3c3578a056bfd913770815c6bc82ed58b815c4bc1e771a0c9e8a296ebc14e8ced4663e9473b51a891b91e0430bc866302d429834aa8793645a504d44c80cf601fc7c85ad55dcddc94555a7ac9fecc65e13a134df7284dc90e481cafc3a916682c67bef851ce722dd11098abd9c40e51abf4aa7f8d7f50db4fb94551fe0a4057e89715395b752843b5dd526473e9347469d8b7168946d422e3900973236db063323d97c8ae324cc8482302ee572ee8d9509c3d1c8571d1293230e7c5110128dacda0c09ec7f8f23dc1d21797244e78b332f5f203126aeaac911328b9085f640e43510d03dde52c9c886513100139cbafb92342c5a37124ab585a2c06694efccb9a2a6f1bde8243c94b17b1c97e2645bd62b76e1e9fcfb6d2d5d482ade7f74e3fc74e4be15d10dae18731f51cbb4af6865dfbc1e219db225edf05455223928dd8ab174b21b9914a9be14942b78d15777551d254e09f48f829515ff75c498d0866ad1ede5619125fe74bad8465aa1cbb7be9251fc21c52a5e2f60313af9c63a4aa472c0da4806891b8b63eb92999cfe2ac4c542231a2a01b8fae3e0e8f78694b0a1b1879bb0e2cda575d4e2c8369ac5d32d4307c6256e0dddcf658fcc471afd79026b68d773e8ee04805274b118c37114245668c91ae619ac9262b0475ad4369916d4fcacd2e8f77d4676f2415271190e54c548260e685d3c8331d828248dc537fd067abbeb5319b6ccdbad92e3d2d3146a5f590c937d5f2f84de2be76e31fde1cc56a4a19216b8d303a3d8bb4afb309841f1f947081f98a6c17545a8df6603ad01433f02597267faadda3bd81b60704f5aae3447ba776954b975235cd8c97b8809b5591938ec19a602075249ebba544d5c45df192a8ff046fb4bf21226bd335a9db9cf0dd02df302f60ed86c690e5ed5da4525adb1a890cec13e719ca499f76f1dc344f7d6075f33a9dc087239f218a1ccce7091fe75c9cdcfd820b5483e74c40050f3eb3d55604fdb5c252dfe921a0cd0283a9b0141781152faddd45ad8e2e3f1e8f9349f863f95e217a0a340a11e2d3d49f538fbce15a366df79b4fde42c98b248f6dccdc520e652c0fa0fa6f5b96c2053f79cfc2aa2d38cee04f8e6ccad801edd291dc8f29bef0998032cd1c07b027892ae41e486e422e250b34e02f5256c9483f8124a8b2e4953a398c7d202b3069457ae31ec635c81fcf024b666555679071ba79a4f5fcf42df24791ec26ff9aab06e7d5359873764d243048962c1e364f42691632492b740aac65337e87c1bdb9fe1fea05e4b7b8ac8439ebf15a39934f1f22f283777f8aca70f996c18c951cddfd51be2473eec4bd48fa4a732f2e8276018c31059aeebb2164dc3a9aec42d8154aea1fefaafbe7a583c258b95c8732394452fbc93b73ec7b59c63653863b15350a8fb049dd3aa68688ecfd5a7c27fc01c69611e5cb895adce0d375e26bd039d677b7aa054fe18f9e45185e93bbb4c0536999a4a21744d8fb13f316bded31696730afc55e5c98cff29b4cc68f3082d8e38cdefda347e07e2acf88726cbb436c6052d828340d3b7872b5b8102023750ba83413cb947c34b288c0a61f94117cc713d1cb2781a3933b4c5028deb200d8f0b0b605e4ae523c8d1c2ac3644e63ba0a68d2892b7dd2c4abb25ce38cc28d6b6a5fbadeef95b94235e324506d349bf5efac2150a979c9671f348420d861583a9ff31fa436dc3a2141a734827d1d64116a3ea42a3150f61aa89bbb58ee1f4cb6da250777d6bdd674d4f7e30ccf5d0299ab2cee7bb6db69c2c898849c420a9c009761681dff518ab32f6e2d64ad6f7d3c242cd7ff358f448d4cd3a1f8e6d254d46e58d3571d2314b2f0af97ae148609889ef8859976a9d12660af65ac550d520e97cceb166787c3e7b442a8b9825e339794ca06999fba317bee5314e263987ca42e312db1a28b4823f48f38b58e65b682274d4bdaabf6a85da15910959c8e86074c382b6e30f1b355553016a3adb77f05cfaf2cf9232bf16c56f31046e22cb6eb78029cd289a1cf9258bd75948f26d914adc574ea85b433775db4c62a4746ea8fc5e5c76e584bf39cc6c80d81c488f99a97c7c5f8aab21f1d2097c81290cc504d4a2f55f6189e8502920b7b2a2ab60c08c1531940e087495b0ef8aa02f8fd5206e5132401742355c8fb4f8487da320e89dc8568e6410c26bf0b1c0c595350edafb2c1836c9a63499aa2634509ef596b2f08aa625847809814cee05d6472493a14956cc1be03eac6a9ff553e18d4d6e53e662de2db05c869486a5ba74227c61bda889e8c1fb9e731af34dbc93edfc00ed4aeba9143fb703239f93ce117928fddcd755306a5853e944d3532d98d358bfd9a7f74ff751902047e126d4e4e8c7d2565d4509c14283d6ffe76577a1cb8a56c364ee0932af8d3617ed166a014bf77e417069a180d884f53871ce2b4ced4b8614f00745cea925cc7fb03117badbbfbdc4cc8f00519219bdabc12f5261bbef76fa3d56e486499b1f84b5be92fd0e3a2e32ea026ec387057402ceb0ad6193911cb6b785485db1c3f6b854877bab67fc15b1480b0f3d6e682ef20cab351c417d207aa6503047a576af542762a97009afd58fd1ba5389dbaf25e5e93df9a2f0d3b3527e5e634de03a42592905e92287a6306a4c8a15565a6ca6860cf6c62f58aedf5b575920d5aa3d79bfb4bc8f3e3bc02ca254ef2c25f2babaf09d44b7cfb4a320c102f4472c70612f0892430c3782d303b50cc12304632e324409143c543bbb6b764eef42bff5d7980d6950ba035441bd13d1798a4b4a1ce8c802667bb9bf3521a0ddfaed2f9f7a55ba912e21694dc0529dae3405c0883429d8e161e5a82507abb7d205ed2d4cf007ec47576ec9584add448cd30834d4f4c9dd5fc30fa503bde9234512d0764b840998c723101c4dc34a07071d4dc84542b97aff5935aa2c110f9116a8a9680d6ae53843f4cb109612159c02e335e60201fcca643f249f6d62e7fb03e8c621beb6264e38da93c1dd1606c428b6cd7d4ee03c49455fa0ddd1741c93b16ca1b8dfcf0e64495630156d84079bd07a27bfb1062cf04e99664960c374fd79371191edcc739adb38cc5b10ecc91c0f8c5fda056602c56aaa1ae4a6b8528e071ea8f164caa3ade3ec71f60f97bc05e2dcb00a2939cb89aa694869d51d0cb63c9936b80f20864ae14e5d6b84201c51ce805e9b67961254c3886c24bbb708fb8bd22f5f8dabe19035fc0712c8b92e72277116af69d621583e019ebab6ed405f5ba032c55ff1a1ef4d70f28e5b1c0806624e010a2201d33bad5e483a8e7568ad78a5af43a27f324bde78a46d099cb862260ef725a188083ed897b84fb2fe3841d3bf48548b028c5679fb2f0b02b56d281322c4ddb62557e3b00dc0977b5d1f0aa8891bd759b57a26bc0b1ba811e927fb632653761012117e35fbd91bf0498a214785df1e2b7bb008223b6f632f54e862c402dabb91634618f876f1e791fecec8407d369a687c7c77a5bb7c1ab7a48abe59e75bfb8dfade31e95a684b0c860705a5f088c6cbd0f86e57a7cf881c02195471e518f29b06b841631729d94c09994fd4f9fc1536c1ad1bd5bd3a95829642d9e6fe7702c4c66f0eeded75af7248b92e0841057242cd3be9f37eaa49a5259b78c8202598fdc66b88b778752a3f9c0b30d98fc602853fae349c1562221bef3cfa999926002cff9e522194ee57e625c25114d60f8ab25b51d46f8cfda39caafedad683d998f456b32a0e0237510b741554c8ae4ff5f096f5f5ca30f2028bbd48b310fae55485fa136c0636abb57a67ef8552c055421257e50487eaad5633490fdcefb5076535fe97c5ffe28a9edf409bbda3b90a50104e3ccfbafdb8fa4f4df37500d7422db75c1153ddf6a7e5a2d9e8bd00e112339eb768c61f8f024340fc43aea90f014b3bc8e599016215b8ea541ef859206b17d8520698eb0353ce827874b1821952efb0b6080538a944abaa8a8b3da3c0709a04ad0a6623699f32406a6977683cc9f22d278755d05d5dfd90d5b423088f362c6ffa88c37e9ed22c60dd154c430e4b14bbebd09007e051fa247aac7f1fbddc420134f86b78d8bd2b876028a2c8c6a74de9cec85c689588bb497a786aaccc73568a06c65b537c41458d243ae2b04a234f9d0a86164b784b0ac28b1ceec05ad6f67e5a568e9b07369507769bb30c1ef11e8a1d3ee2deca22c4d1acada9a6b059e37b6c435858bc20ca066693e388d71832312e2d6853109090be3a03e1d719f3e584e58b7c0d745538474ba761545d01f6f34ba08dda0a7d23aa7af9b97dafbee1dbd67da071b7f96cb510bd4797ab7b3130ca3646ecb1b1c91fb6c007b9c7550afa7f3f94bd36ab177a73b377f20102f1b93fa86a5aa59e75e7e5ecb7836d0d7f05bced9b0c366dc36aec2f6cb35802cf386c4bbfb191a614d8957409e98daa11db7c3a775fcc8e630c3bbe880c98cc78d54620f4e0fd9bacfd114f9b9076f0a1688efc3fac5bfcd647869fe9602ab7db18f95e9bf6614a6a58574f0d53aca826be59e60144ef46b53fcbdcc7d7bf7e3f12412b06506fbf5315a14abca76e1f87826eee07fc94efc7492052059ee3d662531d8af5a9a0cdc50e932e98b0c051859d79b1191125358d81202e3dcb1140db9bf0f1c303a6574c6f2e39094ff8c9468802a629243ff76a410482cd9940977c3c59f400f78c6fe5231a6466719156abe50c09fb01d4409a40c69b812b8d9a0aaf9c0fed499ab1b59929c1919a48856f7d682cea55528d824d22f4d8906048612478dce5a42eaee527a5d3896bc88d7558e58b74d2b47ac6113379ed910e6931c06855c0a3020ee8a0e2b566a58349cff32643128fc92647c8c5f0fb8878cf4d141d38492c494db9a51db82f2408650d7832b21f47718ffd60f8cd6d63ea094b35aeb904a7cfa8e2386849fe3afccff87d062dd8a4ab13a426dd232eda7a6d9438bd1575515f07654e58e0791e20773b8624c3f96c84c7b6b07fad10c84d767431ed667fb119d6c41aab4ef171753f16d115a84e49699bb767e98f6e95d9a32a4e1e7676c46ca64cb94bce994a19b54dd57d3e4e76a6631938f9a1c628731ad5b86cb076e99144b2470c19eddd8873444b20fd8c0d3310a702c2517b59de72014c8e4a15767aeea223e0617f672dd99cd91bee65651cb2978ac3eb6ac71cc327d599c1fa5511ad6d5121b2ed0478e5b8f50dd2b48721ef71821cb1f0728272803a07b04b8d479efa1e944d1112361a98944193c4137e879edea7d17739e591e3a7e9eca4c87542ac7e1cc67f9e8cc8c66486aaf3accbd3622ece5c3e4b377a76fdbc622f3d16d630ce356c1359ce488b151127df26e9708d700598adece7176e7625c271b8569e9b3b93caa7bb91382381ab0ca73e5bfd4914d76d1f22ed4fc43089fd26d1d630c32c82d5bedab185700d64c75a7c7547643e89087a148ffd27c889b279b2716b3d6a82ed48ac03e8a5056bab174b27d261b263b55f4016fe171de7460a6b43248b77c2b2a793a8f7542a1c076d93673c36ebe7638248dfcd69e7fb51166c0ca0894c9584b89ac63eb5d3bdea7e271a3bf5bfce0e9dc435c61e83aa3122c81d765795e1febb02a16717585f9f9cd61b8c96c09871e1a41ba9de29911bb1c4f4590f58af46fed6288bdc9cb919115916f7d5dfab91b481ae912d9fb8f4e257be884706809c9606a0caf85aad20e3584bad61f47761af30ac329ae468ec9482e65078be10fa4c024533b58c652f94fff73625755626dd2d20dcac14d665c9527b9f4cfa839727d9f61de3d740bb6a84191f8d31193b4ba53b2dbd922dc607e9e382051f5c08c58b5a10f626f3d6b675a1a7f25d3c358e1a0164384e127d288004e8e823f9b187fe551210a7da5af89ba12fe471774a82e211d58ca1eada6fc99b9dc6e81c446604a21dd5077ed811e6e8e07159cc5805c09c224fcf114a36485391bdaf20ac454fe40d591486c796d205ff8bcbe04bf758c6a5ec372c38fbe4220d64ccf497403088755eca3e2a393226dc58a6637638a81f19e3812101385d1fc294cab1c15e7ba508f8ff1aa76774ba0fd09d4b9dc9f8b116ab5247898d23c2cf508327c100a07e2e0f4fe57fdc0f3eb9f5ca2d8385597b322514e95d7f290936ce0f5b6f0bf8435a1cc87c7ca2048f8d304356fd41d2448ce52fd9d0142074492454cddca8ba20547b0722348edaaff46c04d0217083e235f4862df7b4043d1035c083b61ec044c372d81d352e7f88328640067a33a6028775211be46d620832cae8bf4ffb6aa91f0d044aa658291134cd5d4e3d78f0d9430074f408c0d1a419c8560cabae86b050735637fef79b86cda7587e467af8e4426d4bc25940f3d94297fa088af15b16f06a559bf074e3bc68d20ec7cfa6cc577ad35b90d2ac007d6c6fcf86aaf41ea8880f1da17c6a5a469fbbac3963809753c05b61a5078e7f54199e9cd38b5a744a6268007f3927d6080336b3696ec30299c54476b1876a3123d0c7e7f7cef7084898607ce1e4c1b75cc63b07ed904f1c50fec07a6280bdd30821ab16f8ffd72ec20eb04ed256b49a490965cbf64608aa65a6c5630e6821fce3d3ce5fac8f92e8c638550bf07a052e4a0e4d944a380d0e68da285380a08d1e97528cc7c77fa25169808c2d332105769c0d5963520f98d637d5f13e1fb90d3878ab5613d53abe6eebead158166044d61871603f9499f5bfbde7f71683098d0142df4f7fc8252f414d52c8b615775b7a092b76bed446f01a28ed0312d3df1550a278fb20da2f4e80c8665c007f63ff00647149528084465e22ee314ee2ac053d36ec21ddcb387a7259ca6158a92fe3cd342e7c7ce5006436649fe04023b1d3af5c735740b57f00acf5da354053a56d1088549897bfbd9a26b5decdc98e8fca2459ab7f4fc649ac59bd44191c22fcb547d0145d4f4246d7921fb31ae0a7ad15b803a342fd07d4b5f499f05465d6f9b5c2f352a9c935cc74e45e431bad8afac200b5310a63be3532a8566735f24546e8f7364501d053f77328a5b1f8c28e91017852b7fa7fb51e6b697970d10a4e5960d7f93bbb24acb894d2307a0473000ae658c2aa78caa5fd855e8db51a1e0dd9348713d42dadd394bcef5dbdf7f8257125134d17745a8bdd7760fc1b31563b166d7efe3fcd6d445fed59abda9c3c1fece7638682e62c96a1f65dd27613b11085afb4d472fa1fa46f6c95f036721e8d2897a47bb6c8114bc38c414ea110fa30546636084b1ea94b8d1d0a724b9c05533c034ab5f41d3cb05253b3d7738e43bf659ca32fbf678eeb769086c78a8ccfe505c600150e9febf0362249e783ce6ef5a1f1ea203528a5f351747728197df7130a3591ff4b2992d6f75a4a601d0325bd1d6fd07be04bcf59f0dd8943361c76a431696211de445fb1f5f2b2fdf7958e99662f422e06f7c7af628a5c8b6761181e6696c288b075fb21271cda0249dc901631702a71d22c99d16a786078df2fbc80298a6574bf7b6455f7258f198f24017b4bbfa7a447fe14cc401082ef04d88bdc0ea071bd979600c85ac5f8293c7061bd4c78583ee58dad82d6ebbdf5ef1b19d15ec11f25de95032ae4b0065a31aa88afc338c95b74858ff5848814616f0c5a5c8a6936f516702acd089444b743d05b48320e926eaa9254f4a2d92d49fdbd2bad72963575eaf83d01886d307327b350fb242a5d5cf026392d82fa485524dcba26727834be78d809d31f3f537b4ab676c06ecb4b96917825374e702456e1588fcfbf1ad70505314f99941da5eb24b179370948aba864d4bfe789d5ed25ed4510e4445a4744ac86672b30a82aa7b21028cdea8d461834ee2a160533144dbe6cc7b8c31f34a1801096e9f5e8ce741c434212c0fbc91a7f5b29df742c98e3f617e289563ccbdeeb37f96f3814cf6af82b97008e2725f64d28b12525765924568c8c3a5196513d574e55c635fa0956a6c9cad3104ecc9551c24fc6f22cf56515e2f31fa4092033542551b78d11cb7cef4f2a3370d6fe86b63999a0b4fd65f59d2259620b93a06f0f1f66d965848b4064bc5409e60ccf568e9d05497166647e46976a45dec829bf87625f794972e1b89fbd700eed5db584d09cc3f149749c81e3fc669f48f9e1940f643d7b770a88448824753238ced3edc866c665ae1d370deaf23cff4149526e9d100d39682e1e2c81125b4c8e819b65eabae35c433b98d22eca3e85426965b75a9c95626f37870370148895ad66d466c048fe0eb7c434b6e3a22f1112fa8c1ea06b774b8fb7a1cfac8d282b865f4fdf23525f544365f99acf496b7b16080649d702151675061dcdcc271edc20eef8ad67d18785eca0b95a61857b1b6e76c783d887e5f3f92bd7d7decac6cf8d180d4bde532594e1cb2446571f84adecb53d15a40ab6872f625e2fce2b84c3d42197f3240fb6872f28431e8289cfd2c04f1fdb62df276b8a6756c8ea2f3e3034e64dc76d6100dd7fba6193e6b6cb742cf39faa3d0d8210e96095b2179d785ea0fe60564d7d634c3edce2518b991efc865ae2426d4a95bbccc4618015c3090e5d150757c6386cdc453b38bbe666003db1ddfd13cb93693f354ddfe3dfaa52e3166107060550d77073458ffe84c0280565b312c106c78ddc64be7ad369b14c89dda05e121a35e422ddcf7be50d7dc4640147e4040ed5bd7a36a40c885808633a63c2b3e8ea0daa5e5337cd70ffe144456f8b9a3badb0fb631acb6080354f2311ad24f83790eea6b122d7cd232fd1a1af0d8351a3776da017703b1b0aa81514c9c9001c1b894082daf94d04863614a091a9beb4466b24cec750052889ecacae4ff83b11503ef1d1e86a18478a802efa724048f6767cea55f7c0688702d05024587c8daaa5cca0e6a1946875aad3b6c7a026af3deb956a8f58609ef478836a725490e83dd47aa6424d3e9b871da6d01e3e1869e7fb3f3ed3f21ec4e0c2878b3f49ed48a3d2170b8e7a43898d43e9e9f5245f7bfe92dd6121a0d4eeb90684c3116b7d65ab7d242b43707208e714232305a9df460edc4705cbcedaf8b5f90a4c6f32fc28a85fd3d75b3f798433b033d88f072760bafecd86933d0266c6d50d1754cdbf19928f5208bea9d22eb053b68cef6bace70aff58ec9745eda50271d638c8d0dca1d7cbb1aeb1f7ed1161b57019e910da92e9358eb5a665339bccc5515874a8395024355063cd8fc7a5ea2adcd19bc300b86a09f8dbcff355ca5868d158eec275307f9db2cd01b7c65b2d5b36ba481bab052c88f9df8f41fba68ea26f7d224e78c0585f8c28035a6b367a59305c554d074bc7280b4c942c30a37cb82611ecf8bd9e6aa2a9b4cee55f51ecf855aab947a7fdcb74120040601881a9eb1142c31003a03a056555934073d16a3a606d3ceab3fdb55c5d536349266c15edde7b6fb9b74c3249199e07a807bf0723164caa049ef379549cfd2bdeb0fc8ab39088fb2ec1b385d582ac7273e36c9298f2a64bf1ae25e574e95dbd6bed01a9cedbdc56e9cdf49f148aeca3cf51aa3af1324fabbfa55c3fdd17369b24bcf34237d4c17ee6a90bea827e82e6a947cc33dab74bda0afb322bd88af34c44c1da34d361775f8827ada3a2fb8ef81dbce7d767f6e1a0db8dd366286d0755461e726605d395cb121eedac083f9146eb1feaa260b83df1e96b9e3a162889d85e888ab211e251364258943c41a47143bfbc6902f477ac7992e958f3d431e104ee145c3fa7742d3a6fa60a076ee6a9230c876fdad431a4df0f0249c40a703fa7f1a06e55fb6ef8b0f132d4fc6287f465c0206f7e54b0bc9924d29c48b464d7ee78ed6ac4c5ae82ddcb7a66cfbcf407a461c993ae7b7e37a74bdc33465c626977f4e60fa49493462923a64a8830f16bd5e69c26aa8418c6f739e79cd415e90721c61823bdf4a79b73d200b32d1c3c40a81308b6dbdddda953f993aae03929757f971f2bdfe577237e744e882ccbb296a448a83b0ea44482e360ce9f1309d4a79c72ca297780039fa22391ac95ad098595fd44d7f249df12b9cc4b59962807098b822c96c7c25eb2603948582e698c0e5922225b946a53d7d361c7ea8e2081c5e39212e12bb41569a89665d40993854bd29719ddc0c8301cf58e47891a4f1c2bd82e612975cf529488de40049b473744b87f7433c219a9dad4fd74ad0ec90fba169eb259b868107c66d3a7ec02494aafd105477a02c94fd53296193ce183b35d6184277e80f35bd2bf845aa802e777e14c12e14c2e0123f566500f8258a17e18e2a915a05a7e88afa00b28a7419400f5c3109904d5f243e41228bf496c2358a43100ce91b048c302672f97449a988b8cf5719e5afce77d2806dbdb673cdcaf7cf69d334f2b3ef36e6e15ceba9b5b2885f30c0695d8d2bb7c0b28594d80ecbb500912d6bdcb102cfd2c6155fab4a9fbec46470455025bbef43cb47ce9bf2fdd1f92f82e0fa577b93f94eccf2f7deff21155bab5642f0e8953debff4954a2e3704a49f5f22b168b960146c676f9b205b5a6ee9fb96effa70f9d24794ec7129b5b4aef4913dd2d561f62e4049cabe050b304e98286c96b03cd303eb9b799a5fd795ba8fd87b14980766dfd20425f481e7dd679fc34c2cc664a32b6113a6ebb2b9712f2cb02d5e586059bcb0c07638bf0ae8bf02ce670123aac69e09937d36f2b159e507672fb1f651d3fee769bef6f4bba149d87761de856db08fb34924bc791ace2f5b3ee111b67f72601f6916d83c392b6049fb952782520147aff23c8c54545ee5f6cd0ffea31b124191e68944ca6870c2662f7b64add6baecc1190b673effc60fb00e3319934333389330ffd1f3e03fba25ed471f513ccc2f69d7c7caab7c44e5d0332b443968b70621db9f675c5daf98429765dbe49eabd98f6cd2928e88e9e78898de1a63b040402112635acaae37a6b77693d007367e03755d8c9af69ae6ee9a77bbf69996d51c59f6f389607feaefde4ef8a328a1e2c607352423c20950f687021c41fd30242241d99b04f743a216502319ec0e900390a7182d4b26d2c4c0fe3422cd7c9214560bd10a4bf08f92063b8a566ad9995b03c565b6ff8ff1b47df7833ef7db67106fdc53ee39fa28dfa9c04a4c3d20a0f6dc671077f1bbd1691e05b9ff1843efa370dfe74ac2ddf61af883fef61dd552b494cf1171caad3106fb19088835cc3de5eabfb0c052f9718f6267759ff4e42c2d4d421fd84ca9073af423ec8fc01a7b3f3061dc07f73009db9f2d87052db0fc522a85a14e086796267076c101e797570a6678f564a108f6ff2c8421ec4114ba396badd333d9ed630b53ca6e4a63b5b2561289524a29d53229a34f31dddddd1d06d3a759f6d65a3a279d54051186be068e3a60b325d460ba534a04771b4acae7c5143bfa3c3bf254ac4c66059979a936e2d1011ad8adb5b6e354be1bfe926befbba1dd2c714caf017660645736d6748f72769d89f14453ec8cd6ba8c34d1a5897a0692a665d9b16e3d60b519bc7482c78d7894fc506646099c634b05d1e5041346b84c71e109268e40851c8a9a45224cd4a946224cc4a82678224cecf984133a32124915809a8815492842892260d0ba401e13ac4e97a14537f1aab309984a3741869174a2042859144c6430c516667013c58a29ccc048b78cef53ec191de94df0e008840521ec5fc211080b45c013085c6b873d022a6577d38fdf0e1d7da0b87f76115d7cc9be826c5bc6bb6976b7c4b25bc6882ec653d5dcbd6a7e3bc6f8ec2b741ed580d77d1fdf031f9827cfa6e438830b0b96c13daeb1bf09a0bc778794202051de4b94776f3c14cadf38cac67b212a4a461ada7fa044f5c783a350ae10352928ef1b159dfbccbb31d2d034ee06a704e67e09224425647f3b624bc73c75739f653a72cd452e7271fb4cfbece7b7237a77b31bbe06eba494de5f8e260aa906e6e9262ef1e2c5382377b8ede577ea953d53a8408c21e0904387336390d184d9b1707fadad301801c54b05354968420f8a0a6122475400841096e00008503041d12b60fa3237c0d4aba494fb41c26652c746c91cc0344553916605f7cf6fcc12c30e9080527c9be6f793e63cd9cb22e3c5402e11c3d9e2974f826129250f945c22d8ed095150e17e949e9637a46ac49262a558f10814300c36cb95943288b21f850636aba4c0276c962a99d326794b3b3213acb07c942b5313a66555a7a6523555753c0d05c5f3501a6b2fada8d35e99f67a617ab5224c53b2ea36782a37d227ad3561e8879f9da775a4e513e3fdfd05d1862654c3678f01f6779d4843c3fb7baa4f9aeac6559e53d4a619aa8836341e8da70c4491e6df7f0af5298bc5a692d890ff0c6a138cf79f5388363426b0ff4c1269ee9f3297abe5fa71b98eb409dc41b4910d0df58015695cde7f16e953c6624d1d9611160ff6d2fb4f13441b1910d02402fb4f9c480380589f3215916aa6e6cd509b5e2c8936321e1e2ec0220d09a84f34164b12138a29c1dee2fdfb08d1468693e17412b07f2389342defdf3b7da22e96ab7bda489b5cbc7fcb20daa0434334c0cef2fe6dd327ca62350eab8bb074dab4d224daa040404f8a220d8b579fa84a0553c58884dab4a20ad106e5e1b94250a451f1e993c7624862406edf5ffa20daa03872060502f6974722cde8fd654e9fdce5922bd78e4c01f69745441b3e2467f4c95945405fc0fe1b88361c08e5fd3dd0a6fafe4022cd84b98f3eb96ac250ff1dd186f3c819f42392180e323aa5df730d1867ba260c7df721396386ef86b7700cada509b55a1fde1a2e0d2933682a8d4753bd68aa189a8afecba3c0886938b1988683e9c7b81f98b9b2a1ccf5990b46e6ca8630fdebe295325606c462b180307d1000dd8b4cc593a95c325529e3c1f49fc4b58865383152ac450c46cbe6c235e422b95ab886307d00b0d415ca02a22c16ca5aa12c204cff050b6d0555511ea72a16aa5aa12a1795cc7a8ce2782c4c63b118c5c1f44b232a5d432492bdae214cbf05c5abb31ce83acb59ce72204cdf0519ecc45963792a1ed23b4f8a8b9654a2a4c0c633e638247b638ef3ad4859566ffe98641f930e5df354577348daa83e72062591567408d3ef3cd02bb29a272fa7fa481b5e1171067dca22797585a9a7dac1d44b418ca1d50747e3ee7aaa1843dfa50adb9fd29aa7ef4b6979afeed5b83f98d6431b6b472648fa3e267d9aaaf90df509e68b30fd2e7cc1f3593e2af2079b131661e84b6d861485bb01a05f77b0766d87f461409686a12e9d2da6f4e3027d9abf29843aa44f83075e913e75afe9a9bcd7077b61fa0d350cfd8fc9f7c1708aeba533f092a163ca65deb2a7c4df0ef2b39bbf9ff6c7c385a32452d83eb8dd79bacf806242b833d96a93966dae3ea17cbc9dbc597ab8733caedf0e2f1d567fee861dd62484f49082e57b592662191c74f050810fd8a874768eb480821adb24d15cdf7c71d7edf092596e6893b0f346901c11d7cf21b1378e7146cee4c7a379840067b3bbb971f63cb8fbd2a11089b51810eed7b41de46b5b4823c64cedf62071e626ad674f4951fc33b04687133ba582cd325d6f3988d13cc2f133253828d061053aa4dbc8c586a2c377434787b46ffe2e0373cc06229afce9e8733dd98ff100292f5f0dd375f7eaeed52fcc7d37501cec234f7415b840e7d9cb11010ab468ebe6fe566e56ae41240706a1b8823ed0f8398a0e1dd28fc03cc50a78e01056760fa63fe79c73a210c1e2e8282b760f22f5bcef730f616b1f6da21d7dedea90813ea0b0d99743878fa628f7deb669dc391dd2edfba66fb83b67cbdcccdb365c916e558794b6c0f3e3b7a3056e9c0e69df7c6010cf9dc282f5cffdcd8bc24414ae10fb37f50a4003a66e14949f77601f6918fa3e983e7773df20c1f4b79b2d10a62f84e9673787b86f2be990fe045d2cb13906a522a0810f44e183f30b7d1d3ebc4f2a4f0f30fafc6227d847e649a58e9e6449f4bf667548df8513967ecf8e71bd4204ddc7979750c618196372f4fc1ba7ec28b84cf181143b4fa40923b3af1346d28f37c4cf88e01c3f1220e61f708ecfc3e2bed903b83f82dc6b5bfd1ca6060ed10215c4900fe207ed397777777f2166507e8704410495dd20b61ee1a637461afd3c24ee2fcd213168a7491049944cc14912a81ffc83181253842d20b1c417a030832014fd2ccbb22ccb5e881954768710398209119001072eb801105140d950fa36355a512a85ba21a1e8d7a410a55229d44d45d1af284a4251fa42cca0e80dc26f127d4bf3f218a211e9fe98d0752d35a6b03ec88f299589cd3ecc2b7798f7c7fdd12f7f871ff37acd496125112a654e6c3ac127909cb1b1eac6636402491b9b0c361a4c20dc2fe5dd56bded74b8eab0372354d83c5daea029d426e9452f0bdd94b181b0b449569c1b7c6cd798c2d2188de1f8c5700e6d01f91d677dfd038618c2c11168894dbbbb74c146a0252f1c819604e1fea45469c152469a2dc238f58f3f35c6f8bfb8b13211a8416fae3860a7ff5a752e52d8fe88e5e7d2b9bbc796b7a44f644ae9836e90caffe1024547f739a54ea909eba293b27b4e774a336de36cbaeb92dcf0d15946bb33d04390c274cef76e1970a0eb62a86c04f34b8e8981ae4b9133a6c824d3bf1ca1d710f22f87fc70c8c864a3c83bf39a3f5dad249286bf0510c1fe4d644a394414425208e1b8048e4052e4e0ce050aab9a27e9aa2cdccfd9344cbf1779918d0dcea52e50d8ecb27bcebe54d5219772b1059b670fb5a18e7b25815ca7c7e5d3a7b9d3d326a73daab9334fb4888a1669987e1ddc5f0e0dfba52aaae2582e56b0d9c31dbaaa58c10d4eb8c1093182513881111ca370821cfcb20bdd0d504882951f5326919e50d84c4fc56b4c61690b36a30e5ab0eb5bc0c11148c90ad3c011e8892ee0160e8f689022d6018183228d7c0ecbec7307c42827c6cc568ce96ec8b1f55904e530eb28d60505df0d226f78d785046406c7fb1dd20f7dec80c132f5c2c73e7d3984c4d19e667904d3c63298847d883dc4c03ec8fb12e7d0f208e8319b36f1d0210dff49a496215b3f976e69bb3c42dcfd9364644ec1b227cea04f036406cb77e9903e2175345836cb1e4c89c81ee9d3217dd9f3c98be33a0e225882b2e726082ccdaf2fbf7d74485f4687b48261877d131b463e0bb8024a0d49d47e6a607626b4880c4dd3344dd3ba6ccace3ccbb40ffda9bbcb2051d09b1f8196c42240e9a4112ccd8f38e23932411144782fbd9745accc3aebacb3ceb2cfe368564279f9db9750ee97655e165ee9b1ab3f9f93d13470764bdd3bc8f7babbfb1df2bd5bea2e8f1077df75dd95d1615f30bc4e03094bdf9ff21441c408ba9fddcfeea7f65d112b5f87d6536bada736356f44c306367bf92f4e60e97f9747d81fce937773ad7782a1cb3a7ae9b06abfc968d37ced6522cd7cee330dac7dd8a61de6775c89fbf9dd2d71dc7353060d245eb42168966519754a9f669452fa2f82b094524a334ab38cce4004353535353535353535353535353535353535353535353535353535353535353522883159672cd5457fe9c213e6c21108f683bbf664d46d89aee2609190805e25b4d5a0a20c2d2658fe7c4271e608c6f0e9407368915887d95b325c7287e3b3b88d2a67ccea49884c88a1742458fa13228c1412039ece842301cb6f014abcf582789a79c067af81033e7b120df8ec3ef0f1d9fb6715f154811e9f1d08067cf62176f8ec45e8f0d97370af4c219ee40d8fcf130b0bf83cb7b0e3f3ec828eb63b8827c9caf1790601c7e7a9c48dcf53090a187dff34413cc9a0047c9e46a03ecf17e0cfb3063934ca9278ea14023e37180ef0b9ab30c0e709040e9d728478ea1d1b9fbb09349f7b0a05f8dc5898e96f242d83786ad7e97323d13be820d4e8af493c35d10d9f3b88017c6e13d0f8dc46c8bc2c9255983fe3b3cc82003ecb25319f2518b08cefc2cf3209cc67d984007c9647903e982f9f254b2241c36729831a5e16114f3356bfb7189f25cefd2c83884da21870ff06e2c97326cc74f91c612f621560f4ef88276f01d11169b42fbd010810c3f2d10566f179847bc3fd33c493bf449ad83d334ff501375707dc06ccf898d95a3d6eae0cb8d9ee7075b81906de7278dc5c1770b3ddb1e5e0f93a6e86816b2cc7cd15c7cdf6062b86e72be06618b8fe24e0e68abad9cec7324806e1595708b8b91ee0666b80d47c1c521a918d9b2bcdcdb6003b4478fecccd30a6e63add5c4d375b02b86a10cd93b63361e6d71b6eae96c69d2fd344786aa919375701dc6c6356326e8681b3a08c3561e6db70730dc0cd16e666521684e787f3a7d67073b534dc6c67b82f37c3c86ebc354ff415e36618f7661298f339306eae00b8d9bef0968bb7f07cefd960f3444b37c320b5dc4cb23d783ec96b8b9bab6571b35d713355e1f92c9b4a0d4fb2aee33a3146b69c0872fd1011ad808d55c0c69ddd7280c0dad9564992c41819db88d86eb614118b156364630b36ee50ea4f8da2be6a50754d9c89535745e861edc06031a6318a4754575055f5a6a61a4923f95c5f80bd827ac5693b5a12b496d6a3b18a8a628c96d262a0a9b49ba020b0311744450cf63a7264633de1a795f5c418896b76931d91ad32d5109018a3bd9e10c560f147478c918db31e265c3fadfc825f628c0437588c99db920833b91b1d7040ec5481e77c30e039ff0510b6dfc1c6a70de6356e75776bdad85a72c66c529b48d9bbbb5b2ac17e7b7877ccb2dffd45caf6cf0db6b52697c2737e8ce1b9844c8ce9152f708c30f25bca4ec24ace85630c220d9014a04293a2264f9e3c79f2e4c993274f9e3c79f2a44993264e9c3871e2c48913274e9c3871e2a44993264451c0b09d15c774b9482499d94df0ef68c0ae7d75ffd95ec79f31a4f185bed39417afbd700437a94d2429a9b05266cf5d9ab0d971df0ca447dfd8616947fdbe35b609cf2884dde90d5d88b2ca9bf6d73dbf5ba519d1c5979ce2c294fb7ab26146883650e3305f4e73d5d369a6d58debbc0f25c57a0b772a2b2c2b58b420b96829b9bc00000cefbd6fa843fae0b70306a364e017fb603388f48a88333e263d4945b20aebd5d5ea5e2f03bd9e0ee9db007aaca92ad2303c3d4630d5d9e994f429004fbf7bf16c00bd0ef4541f6cce191c141b4c5f2b6aa181cdb305d39f8f80f9fe60f366942298fe6ccd16a6dfd140836703e865a077d3bd56f3d4296918eaa9264c57446262bba155add69248ff61d742039bebaaae8cf4c906af7b459a89557d809b4705e0e9d3b881dd3e1f1180df3e88fb60fa5243d76ab55a3474424260b75aad56dff9f8c4e852a9dbe9bce05eafd7cb852b2a82c1b55aad160038212117dc6ab55a91381f9f12974ab5703a2bb6d7ebf562d98a8a5a6cad56abc56213125ad956abd5b61a6d3e7206f551d952a9de74eaebf57a15d522dbc956ab555b13860a55a1542a5553d9e7bd5e455507457ba56845f5fd57bc68da87b72534710dd753b994c1a6d0d0126acdd07a690961fa2b20ca77abce67d5adbe5b753e98be4a8cef76a94ea74b815deabb54a783e98f5e782eafa2d7cbe55584e95b181d005a42ad172d979610a69fe28223712bce67b55af960fa28a5ad854b713a9c0b8ec471df8acab2bdb6a2d7eb5584e97b2d586cad4d686badd85a2c5b6b13c2f4bb95328cb6d5e6b3ad46db6aa55286de529b4e6fa9d196da529b0ea6bf59efeaab16f57dbdeaab1661fa5596c1ce56156aad5ab55585b2fab9a702d592388e7bcfe3c2240f44e21cc67de45e469f9c0b31f798fbd8957b8ef3607c3d388ff3fef36e0be5bff73efb90e7f2edf8ae1089512ee7ad18a3bd073a920e35ff91365c8938e34522e52c863523fcf06eab70babbbbbbbbbbdbc577a33bb06f5a481f8efadd77366c60c4b3bb7fceeed9f2f5e8d93dfba66a2eacbd83d990276ff8af80192c8b75a84d269a4cf8f2b3bea9a46f87bcad9ae12d23b25463ed339bedb5cf76daa47dad46483a59910ca7fbfa39e3c992f44d87da93be1d9d87e3476d150e78d3a5adb46dbf6d2a77d53c85ef4262b15eabceb47e5cd28f60ed3bfa910eb518afab68b80aa4a0e7386bc2f474e8298831da7b117186eb348cf6cee346b00b16353c094e7ca32a02dfb4695e128eaac5c783cb605a770e5de5aa064d9ee7f8ca77bc47c67309442006c5a0181483b0f62dc4717f0f29b8bfc1d883fb7aabca55dd0006f0ed80c1eee3f2b25cb33ad47e05ec9d567588d33760101f2cee12c418ed7b8838a38b744822699a466fc356be1df376ac87baeb4d9214900c5cab4ae82689960222ca62192c858ab5665f3fabb566302efb79b321ad08ce2a178470f7f53b0278b5fe0a18513db1671cb7e1bccd6d0b127225f9016a61ed250e27c96abb451b1177785bb16ddbc66a93f7f3f3f659b2b4e7c09f57e5dbe15d21127f377b2c5f8fedbb2b44e24c2bd2a1562b0d9ded7afc0946bcdd0ce405978438cebe87149c7dfd4e071d2acb0b901f1da8b0f3b98fdc0d5f1f4472d919ed678d97417ab12e323561b4f92e4d70a3af47fc9efbef721707eec60e5f7400511d0992452b240dc3612599448d33d07e3deaeda1dcdfb114cd69530fe13f892453a9ccfda4399922c1da4fedbf6f47bdd93379138cf8c50ab4cf5a914662eda3f7b995c0285f8feedb7bad1569787442e667b553ecab8023b063608ddee1f46019ac4d3d48dcbd1686ff24cbfd771be6ddac15d9d15788c4dd73df55d0450c36d31cac7ddfd01c1dedbbccf3b457614dd334ad15bfd270419ef148b3a118a3fd0433980a64165e91a6bef62e5c8c46dacffecabd10c7f57b48c1b5c35c6fbd4224e6be1df5fb47cd51ef8feeb60f1273b7871a86b58705712f526a1facbdf61d6b93ab1aa6df56927dd2f70dd65c85036e24168894e443edbb0178158cd841fa3309ff0cec41e2fa3ed430da33c1da67456f83350d076b9f65b0f6346e60f37c2e83614d2b82b5dfc088fddb31af1089fb3a2871a428d56b0d3c810437522cb991a24da529a50c7a7fb4cb36e59614c4ae295558f9f1613061e37b956694deb62ae8e24ba62cec35982fbf08cb7769c206719cc3149f0ebb0dbf28819dad4ed2a7ce69266d4a79ff266a13f7a421ab447b940eec56fb744853347565b6077239143aeb9f3695e837a593bed4efd0e3a97fa2cb3ecefdd3ad2e5a399974e845b07f17e9d063fdf2f2908de9165d3591e0f96d8356bfecae8201073f28a4e21ea69314ee6490eee76fa00f126f2fc1fc82b7597fd69ff3764fed01e2fa344572d16a71f119069e2df04cc1f36734d59f8d67123923b6b4802d2df7b17fcbf730f174210f8a31fece7a7f4fb2f2eebd8b9b49b7c55d79163749bfc42c1e345948449a565151f9c91acda19f0e3d0fc1f5238e5f37996160951b84f60a18447a60ab4df95b543e05767bd963fb1ff2bbe773627befa494ad5691eda4d76095ddeaee26d2f2f6e9d0a3fc70f4d7a760b73af448333189241b880a83099bbb47da903df3649fb01167c742642682bbff31df07b9f2b4f3cacd32f7fbaa43f76e0f12cf1722b1efb80a7844e93b90a6606213cf98af46d78db4a9de3b5b1f63526483432af23ad879709e31961f699309473963930bb68463e21ee4137c44a974676c28d3146eb589aa6eda148162469cd5a61ea72916cfbec23b91c626285b7f1a31b0d957be9abef32205d66152a66e549ccfa10e3d886369d36153231d3a4dc518ff1728b03ffa83e6c95941d87fc88fb589beff6c1269b6f7adbb59ce0a4aecdf0ef22998e54fd6b7437eff9cfe2e4336cf1decee9f9dd5a692fc205e92d27fb63ab4e9536c9a7a6a043bd5695a043bc5c1de30fe4e5453f686a4c2fe3307fb7bdd6a93c43d6df269939473a74df965f2b4c9a505e51130056c14b0c106ead0bf73fc3bc7bfe3660b499bf2f6ddbbf7fe53894823dfb7af9d047b48e97e03b394f2ebe3adb47d2e6df54acf217eb54b5687cea44ddc95d13ff3345be13f89d4b2fe2d43767e2e6ddf4f7fc6d90aefcbd7ddfdd9fd0ebf430fe2acf006cb1dfaa7fc1fa51d137714cca14330d85c03d778a1ac161d8385ceda5a47d0a5a8e853ab3d29d234cede25c6ab43877750072dd18be3bac41407114c7dea6893bf74cbe81aadb583efd795605a64cbd1253b8b5986b26deeeeee9e7d1db174b87d049242479bb2ec4974031d2c69d63fdbb6dfb6eaae4d2184ee6a472cf3abd7d1e8d26f048210a4b0790965dce677a3fbae931d6e22b83390193c9f0333f807f7de7753079e2f7d64231069b4a72a1f47366505968f1261e47fefcd9f20f72a60ae017f3f02f36352ae389c8aa8448d15564550aa19120000001000f314000030140e88442291502c8c5351d63d14000c78984a765e1f0ac33808829431c6200508000001000000010001510007131e162ebc46ecfd76ae778dc01a3fbf3751f47778dcaa5589f54100951aa95ef4fac7887dd8c4a2f2651a356b58d37f8119ebf3922c0cb2a555d1a406d42c07a050f707d4600d0726a1304041cd03e9b1d8881a8ff2d95d48af19513d2bd4352159c43825f683c86dcb88dd6cccb01f7e710ec638e609b8e01252e5e13d2dbd5203c4616527d6d9df59d87315a8578c4cff07133f13b6908a976ebda584c00ce3543ed2708b4ef08111bdf9c2a7b466f3a3e63337b32cc5b2ded0d8b0cc0d9b336ed6958ab33e1ab45e943f271ee1e9ae9f7b909494937a1110fd528a314082131d9a76bdf718486563c9ffa49eafdacfb2e9755074829c25f1d0e9c7b8ba563668bf46b6bf293460b9f90a8656d9494741f3942fb5c707a724efdd306303ac9b546d949c7879ad4d107ab378d0a962c9725e996825e7cc033a26263b1e4b25c6fde747bcd5e436474f1efdd5aad10531a8aaa880abadbd02a35e509b69c5887ec21d6ee7a8f652f100fd64eb3e791e14688f70b14c8106a2eac5469c85cfa2797742614e9c4b1f092904db215f26308cac62473357a18c32d07d4167d5985f79a93f575a671a62128487bbdcfcbc7b104d68ec2f0c17959e42b19bf705d7ddd94861a3bfd6fcab1c2807eb713b5c00e5dd5d903055f1af0cf1c7ac5f75b57b888d6ca83eae56e496ab715f68121b7feab1dfc07c153e1785df985f5fd9a9c9533b441d2b1148290df16bc1433105779bf215d62dc47b123f270175e51e89d1cbccaed7997d56eddbacde8f4a39298a49ce74929f86b0b48467e0dec11066b9c969bbb14c4b7605bfca0f8399e3e409d6e17e48ff0cc08aeb0d808d0974758b92e082ebc890eb09189b218699e9421472205450e591a3ffe91c745c78625bed1d6a683f0dab80a356e040e8521c0f379a8cbae94e752cb280945ae02df90ed39afe62620b421fc55d6023a26d0189f7325d39f23365ee6077d4dab3ce415c112a23c187b901bf5040d4f250b6d1b2b08f43b7225906aee00038f8adfc41930a1cd3ed2d1d0739b7b4e97744ee9f258dc00bda05bb6f7580dc0614420d661a2aee9747398f3ea2efc4e58a8077836aa93aee5371b81cd892c575586a617621578f2313eb4b64b8f57f2b734e4ba0f99280899373729140cf5f645c72adc1379ade1c923a283a5dbbe73ea0aa98f54aeb62b6bcf6cfddbb5c4ccd79a350d47badcc97062942c8a81c3c369735853a43525c3dfb6483bfd6ca85614674913166fc6b626299e9c7be68fd9a7133809bc1d1d6b812a2b9e266a20c1c31a53d4e1b085ec74e9da08f4b98d20dd07ba25bd1747c8ac30ac77c427c279cf5a84c063de77c12ce183cf353f7dea94b20a8af1d0e307d5e94c9162b250650f2fdd7e85ed72553c35c8dcb8821834c7234e0dd2c4650468282d04910d63a188e8e8db08dec06f1103b1b5df140640e0659f07cf87bc71d7c7e806497dc1c12f80a0b98437a71f2cc996950e3f86f316aefb5e0b537527a70a7f08027af1e8af54896f2bd69664d3328fbbe6d88de73694e1d4eaa97515d7104c87070fd2aa0d8a69c127c1f1018173b1876bafc88aaf9477f36af1edcea857762f5683bc83ecd69dfa0d0236841b4428faf9a3d4eecc37364c6c11db51a026a3a63f22411667fac2068de81cae1d86b21797d0e805624ee0fae0eac3511e24ee3228bfc4509f44d0f972888fda0f71b6c319761799a528c981240d91978c5d15547a7cbcdd0c59d2c88bb2d5bb2672d6d29c9815b6236d6acf560d19a4675fe69f94b9acc8b8a80eb348a9b9e143c8243d29d3f7666c72e75de053d9d136c4df712b0e463e27a97d6c3d22aa13cf3ca7d20ab8f59f85323e653fdee30e0d75eb84b22815ecb266186b6511446eb495a6c7c1b594ae8bf15fd4f69c507974fbc109d882aa5ddb3aa84ab0d96d2330da69944c2b1a869301e11db2a9f85e02664b9911f4e3fea185fe82f7be0a4524760939692613504973dd2cbd0a8ba0a0ff3892734c1e9dde991b140a9ef23791fafc10f33dd89c153d286e6019a1c85582f2506c41d65e6a8712894916c844cf08f3c8d911431ccc26420a44d029503c79664a071aa021e5a37537a1fd8ffde6f05a980cc5f5df7563349a4b69987d65cc8aa021a83b2a39b809f35ba4abed57dd018454f0af91a579b5799f66af8cd538aa6d3f8ebf8bc63302c2ce395c8b4f4e65e65ce89e9f1725f62df52fc3065bf03fcae2d2dfd7ebc031552caa22220d24da54871b8fc0669957899ca077b9cd282e755acf1a0fb31e66a78c6985a970c85bbee6f1fc551e230e056dc0fc4e59e026a7a3703b48e7c774f5d2f2eee9195c583311c2091e1712ed5db6eb85dfdf3b69f2ffe560a12642f4ac01a4f9686acbf498b5208ac16b6b4749b63d45222013a3fb45926815a293ef0aca2403f3ad82b69dca4b3bc595fff59306ff20ebfe71b1f1043ebcb7ae9a3e157a96e5188d347a2a82deffc79751b49ea2f81a446f7086998c4161c9c35fcd30e1c8ecc4b2b848b1b231c3bc4fc31e9402f414776c2de4b7388a286c9ca817d12061a0d12c06d0306eeb0cb70def72172cd7ee3d4365ddcb877af4a0986b66df88ee5aa171db95f07eea5b797287bcde6de37a34ef19d5c7623d21920bdef70fd2ed719f259703209972dce640265c3884fd4d8918932f1f3e872b7f19c67f2cf95c7cdf4cfc6b5f0392f6ed9131186640c4f2406cc8f10d5cf6d7d313c5a4ad9bbe9ff491a5d9d87fd86a64209c9fb781352123d53b581b13859fac2194d92e25b7878d980edbfb27d7e8b5f6469211e2801aae77bb7195884311b4bef4277bd816ae1d602c4b2d27408831af3a040c384fb3cfb4c679be2d962334cbf52561492c900816f0714452421dca08bc43fa38d0c261c1b12083f2cbab632873d532d3ffebeb3961ed6021472a15a2aecaa838f5ee82d741aec297998ddc11eaec3c750e93459505c002d2b04c45181a6630c32e8ce7cc4cbf15eff8f90620b1345660c156cce4cf88863d27b051f747afdeecfc7cd4c6ab667c74c41e87ed59ca73baa4e5f76570b334fbb11eb4d30932bdf2e62e646ed8be00907f98dbc40ffc484b82e62ea3491672cd3416e45fb666bd22953c48bc64e2c3e1829b020729309c39ccecb9c7969ba79a75eee4427e8755e0d32378d6a72b04f43028aa742f4404ab09f2b682fe8b31375abd6bc2082082d5398bdaaac9dd4684d40d1fb579c2801872657468c1b87268b45fc8f0624a1ae7c5ff098d7e9e96a3892df2d1104b1661f0be40dc31839700df8d6bfe12339094a81b43cd5cde838c03749465007e5bd0f44695c2308d985c9262be3b2733b1d60a05bb86bd634f13133eca82437a1081023da4282d36940d200402a22fb1593839fc8526f145b1873862b5cc52f0b5c5a30959029baa3c651403a56bf7bed6b2d67d17b1332e179960caf0f5b2c52ad205788065565cfdabaf6e4080090f8ce22c0879d8b35705d2a7a51d85435c7a82601699ef71ca826f4aa65a9bec42744990fed4956b2bb056c16f4282588ed93fff462db4b3caa7eca69088456562446925aa8b5e10320889ea955ce66a581928540419499fe019d540c6974b8587d865e654c0b965880893b510b0955be690a92a0a29cd20325361b81ba70d96fc379007d50f62fc9da6169da0dbcff601887c32b6c88726474c6fd76c752484cb7d2b3b882d221b17b2ec37639818930f124575cf264e19f1d7ced71d8566b8cb81a997bb8bcad4e615bc38c7c0ce9f9333c160979aa3cd07eb2b582032bc0f89b4dc06ccefaee0d13ea8d0f2a861584158b2b55d4be747308fb827af16780d3210460250a57fa78fa476892dcd1dccd05ae23d087b65cd83b21ca5a9ae70bc20fe6e3357084b25f28c7f76f3b0609bc34c726fffd49167a405a006a6b791c373e780c813810233202f41a357370739d790018338bfc6448fcd324314dcbe70477d05ff647f67c17b99e294e1448130fbb6596c0198102a9975f74ff3ef87c8127c379b4728f77277b05947aa5332b996f454c04278e6324002babbb2939f0a90a7f3c6152ccd2c30015eb00e8822308173586d90d9df08b8f36b90867fcd0f482cff29cac9b10e625581fcb89782550e20b2179baf39e4aa8e80de23de6df8ef8eab8708bd23917548cb78cd512935b1646b0cc2863a597cc488a036a419ffb71c914ae776c60229e1b867ce24ac52e71100f96a2f5255239e0a81a91e189ac359eec77991e660631a58028774606336009f1a66fb30bd4108b446804bc7a080be80aad446986dafcd91a60ffb9b26b9f0c46ce444d7520d2b4195f874b0dec6211531c8268509a99ca049b44429f47d0cc7a69b07a2b5a5acecfd5d35c85728e3f6484de11e52679468328b39f27da71e1f7bb9fec356a4e2499e7d288adc6a506a081f0231c7ca84ff62b618c46cb8622362151cc1c154bb6c8650db7359d04c4b50ccf0bf1daa9a41e7dd7fe0c10490b283a806264618dcf4fae3a491ad7350cb115f06b0a59c1f14a97d95d0440845b62ed9a6f604b7d9529676c6ee3741f407241f710f994cc87c341cfa0ac344120e80ead9f0a95fd52b294c0daec59bc24acd3c86a45cd07173d075e4747bfb54131eef9740ce5c39d39c389f64eb27a8afcb1dca3a4f19101cc2e08c19e3f106146553d5a37e9868165e0a0da6c6777d343039fa586a361e1d40817b58e49cb42da61d367127b6e086dc4077415a147ed8306282b6b5dd7a10987bc19292a8e4f42bac01747de553098c9526b8ccc055aa3907c0b2e3a93b226fb255443449c443bbb33a56e8a8ea8efbeaad15166747def67006188edf277c5d7d230706f882b92a8e660d1359e9432912197f33f8f1b9b73407f72ef776815c256ced8a30812c8a78d71e5aaa1ef22ec538b8c53061d6f43591b703312e7ffd05ca25796e436180d78371b730d89994a4ba407d0db6e816d1057f4f8bff090f0b52e18363cfe93b9d028ccc1e0357d2135a3ea09746520df6a8cc38b896f35960fd7d3b775427378588032efa1c47bbf624763d4478ec28cbec600b9bcc4afa0a5f3b78646ce8b5cb1e5edf4193b8c609d9931099de9c4276005ec820fa5dba4b0287dd1ab5de9f1ba2adc8b80ddef73ef20649aac23faaa17f355b689c943d466c2e1327d963308dc5cac734a6b5540b152ff3dea7b72d5dcd3782c24cd720cb33b6e4842cdca79e843e2f6952ede7b80ee523dfb581edc43b4ca727acd821de7d15ca7d262e812e33700e457af396fdb808af10692cef0c7c95a4c6d6b2b2fd6fc88c1c17b87b5e226d7159102eef4297051aa56b796e9aaca57488cb044d968846c78a166e2304b8cfe22cb05a506a1410caad4daeb866784676630351128246c012678c09a50a7105cfe22e6d27319f4bad41e1577c441afdf9712465bc9561a37e97a58fdc938088b4694f4026484dbce640dd934c155338c564b32f1c0966889512c10e3b6db9e9d01823bd2e43c7e766642ed9b51042417d149f9ff1b5913dafaaf8a8f15ba9a00d1c4a1babdda522b883d3c86ed5a293a1aee7a3386de37e5c17e139b8363410f2d4138719d530f9735bb14445de679688af75a377e6a441beff5c0d67ebc4f989be5023852d8f5d4ed477870b401d54c4af296b7cecd449a2769aa04dfc89939598d90bb280294c0bcc667878a2fcbf3923c0f2de0fae2a14fbf6b99fc53cc10b3ccb920288992dee330e22e4d12f344386716f0aedd58d79167e02155c66396f07d9607a0ca22f48ed8799dc8125d912db0b2beb4254cf6063c337568ce54fd2ed034e114293ccf562ea4cd0219905c9de74505fe0985e67f3ceaa260c84003509138d522985bdd11e43204c0d130a3221f9a0ad70c9339df585c0632816bc75e62d4ce0d5be6c152822602af4873750caeca0abfdc3d76058dba1686df11027295ea88073efc91d3e4ed9dcf805ccd6eaa6b8596129c09c99991b415d31f7afb298ac961dae38db948cbb2e723b79caca33de16d41ee7080058780533e1b514ac2aa753e261ba04bbc1da88b490e99a170698c9bc0004f66cb54135da05c28d3252c4633e040e3229209dc451cb79cb51c5c9f8f7d3462ce20193f15597a6826ebfe804900bb5a0b4564cc08381afff0ffd9be6111de6fd07641e299be12b9e5228caf2c1d67e07065d350568870e46dc8a9887be4818562db26ce74f1ed169f4a1d5a4bd9a8a892e8c79b2538ccf0b3284d70c83af316f75681aa580ebd1170ebae87884b457267675be453e442538f2056134693a74de8003745040afc0182ecc655237ac8159d0ff0eff5ac319bd8a69a9e63041d805bf4c9229729ba02d9a7200cde8fcd9f4900bea5ae26a4c5607d3584b8e73b805dcbac088c8986e00a630060ad06a66846d5de2c1b9406d0bc611905bbedafd2f931b7eb8e0709f3ba54588f2c3bba8db9191b87b0d6dc60d726af42c696954b445deb60f81354da53993943e5d00bf4b71b005585244c1ae4aff2da446fc18545903ce558466be1b9cd42d21de098050e08dc2b44a1e0d4e857b672532e99b66dee00010b060747cc5e1249acfc8d632158c4aafcd531e2c624d6df5a69ed6dcd567106e149e7ab27ac658e22f59044b095c952063422ee54f36e9d7c7b361773ef88a319cabb7701d11f7033e1ffe4bb2ec1f3587fb7adff26a085b7eef976a40cc39c51a887851ade541af6298b309bc78a417540a81b1d56ff7f9020a22c9bcaee885addf167e0c6b3b1e8e4dc2dc0dd5897069ec8bb690adde5b2bbeb0bb73a2e1932dbdb642b922ba0b073637ef885896bcb4ef7a4b648fc925ff5b0eaa378a3b1f5305c0679d8d4c6640306a13acd98f7462b2b2f72eb5ee65f95ebf3b3dce4a059d8b62bcbba53bedfc978a90d62dee9d91a1e19dd67f8261a19963e1274e6247950223f76f7f9eca3bdec15ce0425f55d59c6ef9e5626abc010d0bc554dd95392843586429412c572f65f27846cf0bc31f9f830895fa4785cd29825f729bdf89f9d472bbf24643c2a6fd4a5df13c8f08600b8da0cb0ee043ff35f6c18509640dd1230285f96df59eba61238df3e63ffba9a0df363dc61350b8954588f1486b638054ef53e00ff8d720b055150b3417f2ade2c9d7cfa7d8e8b7cc1439645fe5b94b5ecc121814b6fe50d2ec1697de87dffd315d4cec1125998b2b2b3b01607841920a08e174e3c0192fa0a7d015326de9b5a136ab3f066c086cdecc2a8f3b3fc2ce9678f5c18740db05d39e8174dc929536b8ecd4413266f8c4d9e1f2385034236704cfbb3a27b87a7ca6fefaa8cd09c698f1d0bb22eb7d5e323434ee546dfbbc1346aa3562abf24f9de22d5f860b8ffdbb37e11099027b65803bf22c4b3ee9f6dafdbd93e6ac866d9922b9cf5bc8fb8175dd3dee5eeb3f2eaee5f7175ff5d71bbfbae7475ff57baba7d57b6ddbc2b55dd74daedafeebe2b6ef7df155777ff4a57f7ef4ab7db77e5abdbbf92d59d7765eda67ed78b61f7cbbdcbace213704a08980cef696aa7183dfbb4d003d9e73fadf1b06cd7b87da1c2b82661b94232dd246633c11f081e7731854ccf71f08a7f7a75c532a777da20f174e2c26a27b41a4ea58e1aad66bc5beae445ad5ba7452c418b5da6a847a056df5578900356eaeb932be1bd081625bd7414aa3207a664cf4e86b54b0af93c227b2629868dff78069e95fd1f4c3fdd0105820e8b4f6a1d76d9b0f0837cc78b0267523e740713776e2639d1735164d162d6d3ed9343b983e04d1aab2cff955fe2e2bea54a2894831e82a468f27cb8d494af41e0902d989bda8325b12b97e1997d3bcd4b9b6e83c25f86d0475297b385046ab10c3e51a1d0681ca05f199d541b5ce36784f0c6724c2b2ac3f345c2ef89b85ca4268bd06d9b509a56c830a3b4238bedad563a4b388bcbc93e011236de7715d3e30fa399b214e8628de30f3e1a3d1a49af95dfe09d2b3b3cce66d3963a983ea8284180fca11999aec6d38d980977d166fc13199f5e0fa9816a68820e35201e2fa0d4604e6450a04ed197c59c45b9157392f120b0eae181753b3d0cdf2cd989c749af2494e5f7642611b8d07fbe9525c15036d38a3be775784d4b1ebf335cc3de3a78cb39756309388b1239595d46ed88e4603e9ae3e6290242a5ddc290cd7577d546da1acedf9087472a57c45c372c952e3e558e379530a4f76234d9b736fe9be6a0c1593095245db2c059308e5a674b2ab9146fba6aa5eacd3675be25420e5308f4b3ad650409b31ade7d9355e074ccd013f4334f288e2102772bdfee3d10c174abc3aaadbedbd9517fdaf17e6d4674babce9c0d5605408b2d4dae71ff7be0bde4ebbadd938421dd355c25a1f4281ecc31ba2aa6c8339ec6bbdbdcbd426931f64717e157fcae5032af1087e71157a1c1baeeccda469f78a0e5f74cd59730f40e4823bb70fba8217ed36108cfa2e9e288ad39e16c58555397285a6d1d2dac6dcc4c959c7a982376598348a99004202fa130d5cbf47e3ebee8d30362fec15e1720a161aceee6938db95a3721d0d63f52e8bdb766200dc13c72b950d5ee56e27de36796e97edf082fea537b52354b3cce15468736c5cb197491f98908a78d12e206f66c1a1ac87e1af09905e442e0392bc0c6c628923ce54850ba4e0833a20457ead1055effeface2c913cc12eb605eda9c070a0c4637324eaee356de4a47020b38168d1bf50b38244d536d0aefab3bea492d7cd988efb86180ac272829f94ec08089cd980fe398ba7aef9d51484be89b2b7d523e8f05f0f8666f5607463d920e8fbb8f5b87977027e45dc6b4eea7de42676bfe1b9758a9aa1b6d5268642df381154a764f5cb3ed9388f0bac365b0088f644fd06701a3b058c0d45b17eba705b885e0f99750b2b58af1654690ff4d618d078dab19851d8d0a3e04be486b126533d97485716cd455123a3277150c61a4b6ce816fec2b2ca41007eea52fcc520949a6bdbc140b012fa1866e8d63c09d386e3886b394be52f0a80b54c0b4065a8e16041566444877b4c9a1e5e5456ba17496aa4131f0aff9366618922337eeb11a6775e9b67d4a9ab30054db88444a5eed3e5173c5a701d7525b0e19da1f9ba15e5dbb33e22ee7aba9648c3e202be9d373778dea8170c2d54a05c09a099f97719ce4b6b11b62480819830bac3d71a376deb5047f77f6d6c06f1b16087ba876efec0e7fea1e51cdb6cfd506cb00e62187832150a7e8271f3a0d549778502706401025a2b1476c22dc0ee82000f6bc085a841a23c3b47d50779bbdabf2e55003dcb70de2dbc6a07f08affeeeb4623ca39c040882e7c3533a49e102e0205817a00ef691f8f0b3b11b46b950977617adaf49d0a6053d30c91fa0560e30dff3532bfc71633b84643a2897e35f20c9c0527181963589e8fc39b8577ba33a800275028225ddf716a7d025f034c0b07dace09fd6a30eb3bd86277aa3aa2fc96ed6b7e1cdbc3bcfc34b0b1a554911de819ea81c6fc3afa2bd3c5ee281cf5443ebd931203a00516d06ba95ec1fe9ef2b89fc0da8f7cab803e921e856f22e815d213f96601bd487a0bdf88a09ba437807bb9d3cbcd7ae593db47dc28441759af829b48740beb15715b884e643d05b722d14dd633ef4684f51ab97ae1aa99ffc9852129d62855975086c7210a926a1e8f5575d0c7a0e3537c5d379e4c8fa28d8c12628b819b5c937ae7ad501b591e3a2f54529334a86cf837e8593ad1064ec1688849bfc1f9fe1bcf5024209d6f2beab20e19539084e80a1fa95c443f05aef8939d761616f3d71865ab892dd93d6c8fe5d464195f379b7569a2b4f60293455284b3010b0d8c4c7fe4d509a97f947c53879d297d85194d9d67a47a2f615fc52a179a49b3fb4cc23f9d794cc059611d5411848a4350b538fd8da67f5a1e9ed4808d1838021ad88636850e8aa20355027be0c5c98ae86c2ee699b8cf6cae0354c3f5fa423590bea300918146361fd82442e00b93a758fb0ad69465733183228ea964ead9c4d8a13a283db1d3eba7929d103144997394fc0a721a2a6edeebc2b288e6aa1539a2d8f6429dac95bb38b77968e6834a286040d42e3f618c1bca98c3adb2d69e28dc4e9dc1badb4296f189f691d0be56c694979b22b3c230c9f897fb20e31377d85703a50ec85a9d9149ca822c73491cc10811e7358fbaf07eb6a154d490d38604dd1ba2eb2e7a1865646c512303b1f0305152fe4a41ab467fae0abc0aa1744cb29d1de03328f0e4751f9d47b26974a39cc1de5d4935adc7da1c459ab3ffbe61ebf518dbd8684efa41b9828b9c68d6e5e83af8cf85aaa086391305b03ace5891b8ee3cadca9b87c1b41deb485bb596f55658656d2bacb2b6352b592daba2def98b6e582632cc28e3993295714619cb3c13c984d5cc839086fb9c11463d9cb6375f9afd97ae3f7c71b540974ab5c117f125beacc4265a93bf7c02041253606336547a9a54b4cd70f50a4e2178d1251c510baba164f1418470b3971873a439c2f8a5867ead0baaea13bd05dc6d45989737c1e19852f3a9844e95ec1ffb6189c5022f36ced8ecd1abe4447ffaacfa0fe15458e032bdf057694631fe94910f9fa339f22861c893b040d0ff6bebf736c51ba4bda8b5c62e97f1caea34d604d6a4419bf037d9c1b2196e854ca28899a6e286c1b03e507026d256396c472497136d3c738702fb33f70b4d62dba134b6d95b75185da8ab25ec0f91e8c86aec966c7c17b870c2f583304dfae429b2403fb059bce4c3dfca47972bf231f1174cf5ced3b4895988b5ef42f75dabf62584017fe0747e76b67e9e27534241c04f73b8251470fffc94fd7af2fdaa13594241e6a73d33436c2f4e2a23029e818adf8f19b3f2ffeb22690905223f3e9f9f3ca39fe4214a2860fd734f5c0905999f3cc11f9eb4dfddd99750b0f2ebcf430905af5f9ecddf9d85dff3c8945020fe51777cf859b9cc934e1774807541775fde20972019564ac85bfdf622fc8d9dfde9b75042b5ebefb5d24bc80bfeaa02fcc1a6ffddeebe84eab5dfae3e4ac87bfd4669feddb15ce02c218c7336ecfe9d1c1bf5f5b8ac884c440fa5a2a451b4c2bd957d45ff49e18ef3e0e0c339306304675342efb2e567ab65a780c58a4ac8c3033edbd743879afcde21a881f5d920d8fd466876ae1a7edab57fb1f04290428d36175a50c554cdd4b55e554a2c21915c8596b74e9ac8e225ae5ce0dedc0ed271542f9cee7f42d869edab194ce196c4cac89e2211debb24f94169ddeeaba4731f92577e209cfd73b13e52892d40472c0d158b45066e461730f9d1e487b20a2c582163590684228fee87ac271823028a342e2a714d266242a0a60e3f22235ca0302c03b456433af4a1eb79b4809dc32674ef6f516faf4a917920bb931931adade54c096d3c086394a448fe095593eab6c37774e92544981fc70f6d4df82a5d1507c37c4bfc544608217409a80397ca1f2134c791a901731b6a39fcdd337a1054c83ad97be8b04a24bf983ff0fda93aec1d0aed35d8eb410cb2c7b6c211424c2910b99dc7b097b45c30017307c5b36901348c7ff15cceb14212c01d582b84f547fe4be28448664b9a63d14a5850a344bc0e39ecb5a190107a37945088ac11a510d1c664aeff0f8a929986fbd690f45d83eb0ffcb8a371e3fa14e449bde841a241dad3f5b70e59f30e46de40840179a3d6505d31c7ed5dd5db7f42f3a584c078f8e647ca285353fd5105593b48790dffb8fddb2eac1ccb47a9fa4f5d9958a7b8e4c15d896b2115cc60ba11e4739e43d90c019fdc642064c5af93fc2f9bdd4d2fe3bd366329d3379bb4a4089c783b641acfddfdd82e68342d891523ddf956c6af11c4c38aa824620cf21fcd0864326047a63183ede239354ffa8fe1bc56578ab8552adcd75619a325f1fe4c8eb37c04ff12e60071f40a6512f5c765ce68fd30520eab78949fb26adba11e92c0be70b39ce217f760a3e8de40d87f8e45c79fbe289190038ffc0c82ab30d71cf44431aabc8afa32a8c1dd6ea9241f5ac7a37d81dbd5a14713ad43e7d7311ce55a2ecf013db806c0928401344c5348a63a5edf5c767e009ff19e516d8c2b0ea3cdd3bdfe75b6e28a3d1528df817fe184c9185ddb7ddc6ceff64891f796ccc834a02eeb84197f3281e056257396e2e2fc6ddf7a49416ec0fc886f8af6fa5e501119deb9ac2172df70cde43eb1aec6b39bc587418567679c2068e24442b82078318542f74669d7b08a259539aa63d55be5424f13a5476a956b080d708350924764b5034ab70555e55d9b92fc53b6b35f92563624c8760ba9b2ab46d86066f991cf3b17fd9b05e7c68cccbfc2ee9dc238c510fe79d6f426cd9fc1674e3cd790ba0adfb1a439550b57f19f4bf0e43a561a33e4bc28bba6766939ec79b96e5d98e43f89ee3a5d26676782de95b6aa9056af8f00b55add6546d6e065f711847478577a475555cca5a1462527daf69dd86a116f4b3eb8fe9a18355870602502f25d194dcf063110b583edc081f4f3d1d9c175e4e537d7a210c6f93659fdb24a25368d33e476d985a740a55eff3e6505c30519fc861ff0697193436cdd7fa8fa8f0c18f2af2b608de141f65b01d4198f0da6d61439bbc6cea73a108746a2a6ac7685a16bd3c181db6bcac5579b98c808546dab4cc90fdfbb2c445d6d8bf182c8141401d098b02c47e62b53f97bd9fe541c571c06f22db58c0c68b5602b110d660d9810740289896c17bccf920df37e1fec3553a68a0c57d69c8ccf34461fc4bc91960a945e0e50ab29f5cc34392c3908668cf2949ad690b793610d41471a00be632c817c8900bf6aea7d6d57ad3d0333377b281d805f17cb9ebba1ccc93e48f00b12787bcad954599b14b5fe8b72fe54ec9d9614b4175e44d530673f972ca17b009db380b382bc26e25371ce7ad553b8943fddf1cda9613eee4ea92e45a7911b8df23b3afeb4a27755c0f76f409903ba1495bb14125394c4bbcde68fa29481e0d619ddf29aed6520869550841b0c596104aa809d0ed701a52baf14296a1420b1669ce01a571a1e4282c8f039c5d46ef9cab468d1fe9f4ab483cf73f1c7002c5ee5300b9d967c9623ec1e2233d79bc2fe17d0f73268f7b2efe8c39497b22c55c41a71ccdbcd87bb1fef1505d50781f95966ecbe5f9f26e895b6d952f0925a9ffa9c968e1d4edc8994388bd6a8f5731c339eb44e29d985e127764345952e985e23a6f374352a2fd7134faa96cd541a1a508fa6b023b78b9e056aedca5f46320a913112980471de7e0338d3148a0262ae7a7a29015c6b7fe947f4ab2a0a2476c2bd9310fe812de304af77a1f67b1be288bd0bba127288fa2bbcd3cc7536ebb290588000235f5e9ae61dd2da4836355c0980717c79a5bbebe188ca5e546f5c43f350499f5dab40f2681ea086a50de91240d2a29e9cb02b8be510057c8ef57fd42a97a7e56ac98f6e06fb1904ae32ee815b1551090f40658d6c432bed57546a19e38182fceb653a13aa7582a2730e287d94dea4f9288327a3a0a1c12ed4f4138054f51bbcdeb950d4d4d270f1477ea3d412b375c01c53f8616e88671476c363ea461c7cfb499a13766420c129e2a85ad5bc705f1be190b7b6f6b48cf74a2ca8bdd879cf4035fdb470835b4e70e1225dd9e879b7e399d51da32003ce81d0b00a2cc0d1602ef110eab711867eff73f41006d15534a037a5442232c87f5e11c8572c59b6f35fa5bb39b1d7148ce71f84677e4776c3b2106db591e951143ec2cad498282e8e74de54162aefccdf038a69c4fbd18dcbe38d58f15a0de06b84eb2da31cb7671c1c6034a4ecc7e04d4ac23f33b0910781531a9428cd44ef091f21e1fa72d3d4b62fd9ed9e6efaa09884fdf4e6a84721bb9b4ea33b6aa3693115659c33751decdbff545515ae67c05f4669fcaf25b920ad32bf3baca712e9d1906ddd41871e7092e040c54ccb6684e6f00b2b890b560eb4b9e3eda0e362e8bd8d8a87a88333f03b9f63c7d66190a38b6cc6508eae2c9fd8ef9d640762f8f24b3647eb1fac1f5a628feebcab59190767acbec9b34322ec37f325ee1279229a4584877d4704d967a8b897a210aa9a38cbe89993998aca7b31eab6929b361a8d3083686cd8267a89c8b5189250bd578b7d99a23f7a9f97551c19f175183bf349e42809999937a9a1bde3edc67fc88534b31dad837a1dd5d1f3265c21b3ef3366b8b68294f55db5462d64becc065caf4c385d1b9bc856f6789be68d7d2d40c7155620ed40d57629a8716ece260b08875c7fcb90f14038298f5e9a743eb69b730c6d1d648107293a6a32e43d5103ec040135a367b74a76287d284e439e4f17fc3924bb9f2f745bcfa1f27741c07de4e926905215966429eafb5b3de44aafd3917afd04e971697f16d3a6dc19cc0cf98d21ae00320c9139299287a37955afa7d060e273d8dedabffc04d83c2879fce0229d0a6a28bc809051da198118d26558d7821c2af465550429a1dbc75f899ea0e0cc9cd714a73fbc86c8d3527c7894e14ee1c34bf6bdb6be9b281aa8fee1a6adb29a8e002d97ac84fce063f90cdc084d99e3cebc18d3ec37705f9dc738d05c36b4074f8146ccafe48eb661a31d61079e32c9876fea271d93cc12bec6b2a9fd5f1172706cc66d24ba0f6ac9ad4484a1f3862eb91c2036edac8855f845348309a356dbd1d6e65ba77334c764dcef54ef2307ba6e1e7dcca1175865528c21813134cbfbf3e58b9608e7ed810fec92ed96075cef555defd07b3c199b900686df425d32614ee03aeb8bcd976c78f638f259024a3bdeb96026a33dec93c17a30efd78787d3dc62783010a4ac43f4eddfd3a7ee58fbcbe60b0c55344ac8298bf148ab5cd06ae82672902529ceb0b4e2c6997649331d92705c0be368c08396a54eb2b6e62b2c46aa9694964db751fd1a4fd2172624a7cc5e117f76c32fa1a62faa25a550b953a7e67df57a9b4f65809b21457012a6a8ed6eb89125ef47da878135f2a1b1f3ac6691665728e158dd94230b31b7f118250ae235f871ed7f8868188e37629c8a8f93034d91abf26d5788fc8a22c91dc0e2ab829d800b0915da43bfb512956bed8cb34191acfa4d68c2ef7543a6ab19beef1082fd606924a58f16a2a9f0106c39c903e1bf4c521f85aad9e6af7f0749514a7da18729a52694ffdf391da3594d61dd1979a25616bc3b24e9a64a95954ecc03ac8a66f00e1279cd29970061c66b99bbcc449b1694c32d45f7e32d1dceb8d7f520445a1c25e28ec577ff83ef0c99871cc18d948b1789bf534012fd1a27458ae0892b1c8cb2ae06abe28e94f74e133585947c57abc25072173e7990612151ca123d854fd42cd3e0719491371cb5255396805a83079dc41dbc280e1c3080ed8f886fcb323c0c3befa15227142a24c466e458085f5a97328fd89a3bf70b2f0795d57bde9cba34cc5ccbd4aa31181b862842627c6cf9eb3e22b8b6f99584517e1a0912de8b2013adc46aee57483f5b85cfdd5931aac32240378eca54fd29951031c46dc3fb84f4778d5f2e06c0fae481b2ebdf9e82d710cbf9f11c35153dfde31317aaa07af4b7a677d0c6f6508c01ad907612c8df81d21fc74989726a13acc24f0019bb412f81d15bf8dddf17d58f8371a2a32ec60fa10055b9f7f943b63398d08459da2070294770b328efbe2ff0035b0b36c84b176c54f7a7fa333a17349ddba2e28ba5be27d29569d9c3c22b599873939a193891f59ce45d632e820ed9be51c97af46d8b8134b7f3979b87f448abb981e56e3ea187d69556a4e58780bb30c435c01b2b2286f7a0d7008b7f3854fc587459a9ea3806b394e194bee3fc4223893cbd8a328d79845aeadedb4cfec415c6a682c1f6e07c7e94fed20f396414544e2c0a6aa9d41552d1d9b3920b9ab41ffae4e17a97a8010989a3149c67bd2cdf55428ae8d135efb49e7350d1381a4fe65a6998efd4f0af86e49e3b7fbf178582c1e8c8fc480ee630850944aee2c0021d12829d0830970b9a95a7f2c2aa616db962dc75f0ef0dbe8f8a8ff5bfb929d5f443f5624afdc0b945ebb1038c1a8c71f1ede7ec009f5cb54e3b7c52b4ed25530b436c71a95e86c3a5207ab3b0f2af3c1bcf8670ec29b6bae89dc5e4d6a97edca3e1dd8b06acaf0aece863cb426cffc41cbf02dcd16960ae97a6a50174aee4ecc5387b097cd739ee37062f3558d1433a9d3c4876096435c26e79f5ad1eb4b0c827a9c152956a80a3b0d1468ae63d503d069e5259ff799789dc285bef19c24d3e6ab0506584be0ac9846a87913acc21e023b3346a2419787f14fad1b400bc9c7742031000291f9a0d6dc8acbd0653bbdb6b16616466e88fe1de0070179dd852ffc9cf4a0e95837c3354a898148ac20a3d650ae9db1a0a59c2cdafdcb583c19e038abafd5308cb26fd58af42801b21bbba0862028aa2497a670a693eeaae5f3eb85e2ad81f5abe110fea073a255735ad4cf5fabc880e6c5ca8e99ab7730661ed0930cff057db39bee3b3579ad62ba72991010f07781b9c7c6a517e7426257b4caa2982a2ab9199ff5068fc48060d45eb27f40c3438d543c3ace01fa041287a18a619819d31a356b3d5d6195b5d01fe615902eb421d1bb1c71676e01712d8833aae320a0d8823c0d7d661c60297b64501e6a1275de9f2680fe77e223f5cbe609bdc019f826012da4bfd6b05bb2a131c63b883bfec81cbe23c6bbc853238bcdf77a6c4b018cc849cb4241c40aae2c439fa31e819834a7c56e733c087d279c9ce9c83baef9d518b32cea75e2f457efe81bf9fe02e372d45aab35e80f6f17e52d70577f7d35d749f74962110eb01cbfbbab444f0d7836524cc609fcc022f1a301e995e44031d4f195b84ebe6faef584cb9bad61a85cdfeb5253aa3930856bba5fc27eceb5658880a93cc35f81727e740aa128971771ccc64bd8071cfe65025a05121432b4c6060ad7034b61f1be3cc1acca3529b15275103eb33cb4dc99c8db3a86babd56a5e9ee0b9f4543dfc8e42b1207a2873e06d5d125154091aa595b6a0732cea38469c28d2423c73e8ad2c9c0b740aa0259009bb8387ba9417b0e5e49b29c4c6105596e1d2ddb2e37775686666d09cf630057eb1a8fd2ee80cd91c47d85c772915a86e0c6cb1969e3f2ce4455afc8a2ef8c2506916e8b888919bffbf9288d5b45e746134093098069663278622b1e42f17f98a063b5d0b8a1b5f205e4a7628666c07214937f0af9e0d9a86a98c113fe8583d9878b6247dd31f72c889f77a61a64393ae9f56398a667cb958e64ba59e190f901bf102fd79693367d8caceeb7238e4fe57e4f88508b67268750c856ae53c1202b0992e526b83423fb88a25707e729f604f8fda3cec651c4bbef4f6674b2754b6280913ee0d8ea26d4e79a331ec73d88912253fe7324bb77c59259ce7865b6d11c503291944b405d9233e9f6c57c2d7aeab6b4df9bf48d8628541120eff36c246894da0be3a3cd0ea00ff58910b49ca715c97e0cbbb131ba718bc6fc6e8149a1bd8886f048a41d61039b94526104d1e2bca32d0f2e4095b2de1695b4f9b4ea1a221ff80794f8f7c64e3ad420c44ad527a2bfc072bcc83168ac6416f2c5a7d4f3d11bb644bdde523e54feabee3688683c1be9c2333be8711e6136608a2f1e4c2e5c5b1c2cc80b3b0d111fc1921d828a196fd2df2d0b8c2c5a2e272e7b074f20c4a6015ab8000f8cf5e5e5285842dd001eb7c9132e4ae10f7b81fdc62eab2c731c3610e3ed29601531c58aaff8f85fd71df491970805b2723a36a966a047c27e1df10ec820700c48766eb4555408da69009bb946a98e44422024e8696f856e4d8aab374ea65f9ad3e82e00e3d5b549cc5993992f8788117abcf9a433f7601f97a0dac4b3440b90574b9c8de9365b77e7d3478c34edb81d6b4eb34b90907a31428415d932c5df717dc6905e3456680e8adcc1e8cb012320b210ce589692be72db18e24fb16dd7f4dc649352b2da79118392f5e2df25cb8a9046b803cc96c3145b38ccb784e4aa6f1a82805d0df2bf2af94447c12a6585e04c695efa21e2581d24e0d98bee684d314f2e06c3636e98fd90758e69bf852b550ca39d7bf8d4997238b6c022db6453507f00f0d92cae5059dcf301b4a8dbc8e4ce31b08bd63e0539010d2e5d1c55a34bfd252f9c83de6dfe823546b8be01be6e4ace2a8875da6107074e58e522c3e2f8d16c556aa1b6cf2b80757e44c1003dd6fea80eb13c98ebfe3c4f55ead7839ee8b1d0e683a133e8f52c70fe25b0be600f54a094a81601049f1b3b1f316cc42b5e9f6e82cdace0956e8ff9b08eac14d88e46e02e96cab0b2cb21fb8572d77748e9f35cab2c6b1151c797335090e9f56dc43a9827e148ad4c0d5b45bbdac023d027141fc10fafd864a9b62b424ea1cb7cc1b668759cee599b9b43ebdf48723cda1b55a85a3706a4f291313cf22b9c38f6488016e8eeaf7889f2caff51d86a029c7bb5287fe2306d2e02b4ac3b6169dd303d767720f658e93e72089db5c971d58fa2e27b661f5c091456cb90481bc8a3922734bf7eed769a8e1a25d916fb55c434cebf1a4c59924425b33c0bfa310bcdb987f9788473fec1c4b04aa865728b931b69222073824a14c100ed89c28364ca635941bc5a5bdd8a20d3387e631c4ae30d99d0c4c2715eb3510d03078572d6b9fc9d39249c83fa7ed4f3404997cddb4d3fc01b45d05d4ab88772dd0b5a2fc20b69838515fa09900f6f905a788c7f70c54d1fdebebcec163eb3ac9848150b003c340a2016a62c83375bd855bd6a234384db59480b07109a9e492219695400ed02fa319d98eee8e5944dfb438f25e1728f08b896a52276ba92d1ab897450db0bd342a65df5e936c434e19684246e64dd30a49bf503d90f895a53218dc99b66520637f32ca1e81741d864ffd837b55a0fc4b0081a43433be1f86edc676ad70844026130d766a5c41c94f7b5a763f3fed4440bec61a031eadc125fc44f19a728c6b246ddaa6812309183a54827f6619210229ef73ee8ec1e3e43a644ccbdc219ad4fce0e577830311b5d64e16ba2df96089f6e4f5b1957d829cd90432558b20ad538b03bf1232b426443da65da61f9e943e95f7719dfc674dc9529a8b4eda0a8d15a95f4e5929922084d8a64f0e76e790fbd85dfcc9d8c3276bb60ed9435b52f5be9334b060d23fcd55ccf111cee78868d6fc3327d891f13c34e69b408b9ee4489a1d11cfdbb709280277a12d5bddad8e7e1d744120c47d0b3dac9b20bb237e869ed35df5fade30beb61fba29dd43679d78eb79aa6832b10a9f0ff37ca76b62f862ad3f202cdcf61bd8aa9429af8aefb7f9b33f27a07cc0023b673e163e87ab894c81f9382cdbbb27d5504702e413531e526c6030260e13a45389fb083b8b3322a933beae339b28136ca6a818658b1c408175924706d5ad47ba3d72a9cd775fade5855af91063ce482d41bb5000a4df9f3805ab8b32bce9e96c237f9952851ce0139f46167925cd119bc9dac716c43e5d401c8ad8facbc5b896e11e0d2b27ec9c0c8832e211083bd8c359e574cb830430bfdf510ba0aadb3b88351a1a96f758103f019c432fc55beb883b44da7a80aa63fbbb95fd5959779707451ddba06852a14945469a86eca8dc82e00be451777a2128e0acbf19f087b93cfe68451ef041512562a35d0b2d0f9b4fb62d223d94272150689610039c320f64eff4b4c60d3376a5d6cf77e7e3d91553c198075893eb8133d44375077497363f14bf8cd1c09b98f1888ce654235bdcac830b03ddc57f560344eb3c802130fcc70a18b9da61b727ee31bfb379257fa1cfd51eb878e0d54e3aabed595f4261712ec460dffcc56238185a686f95062758b621c4085a923f7518f1e49d508de6113e5c37898230db1100a948b905253cb07706d0fb6ddd4061a40cc60b69ee01cddd2952f04d0b97dc24ee20d1467d32dd20b4863e3283a66c80c8a4ee0df07606429914801836d36daa145009519798ae5de4ea109886016a4b16f45cb997cbc306bda313752f66dfc4d3dd6ac8cd5b799a2ae8f87d4912cf02c3da68187f9752e5d3b3277e3c12a75ed65b34da5afcf8dba7e2a1b8c75f282568842a79bbd2ea410f977cf7e32a48c04a77755b2e1f0fab75d8aa8a20bc25e434b80dacabc4e141627083b02fa183b6c09061bce41ce95e6ec27d1628bf0c490b2cc3a50f2009b12a46531d0277d545b4fa8fb7183a6997d3b9867f040b2d20da3b7a5deb80e686119e203e39a5e2190bb9ceea9a7b53340994c93f9ed743785ed1d494efbc9744b9409cc02a3f1f5ed1f016046080b6d06d1a80641cea62609755140e00b23e18ea61b43cc30429d6ba80bf170c2e345f97824225846f51186fc785affc0e2f754a6c34d660e406e0171cedd786a9f070ac9832fe969b459f5e8c2f7b8f11c315a1a049e3cafd51a7ef19e6d96212b6048e59799ca900b29e204643eb185d9cfc5c86a085473dec63c7db524414656eec6f4445de78a032e6a28b6bca1ac3c7f563a4addf966c34d4602faacbd6167cb6fad1ead3c212801ba6c169c541b8eb05a8aee2631ab11c6aca5bc8729287b9367bf0b979feaf76e4824e3e268a19295765af9beda50b4a3f9cfac87c26a153788f64d0117c2b2120e4eddf567071438218485c4c968a40c31f740563ded7c86b6221f3463ff69a268ae5e8832d8537fd2279cf1676e9a79c1df8dea8a29a679795a9e18f7ce4467b52469b545d276c4555977fc7910cc3f96215c595ed27de3fb1ffb929124a5612b0150ffb911c2a2d59a87ecd7af4d5dbbd63fb0897b9c107b82847d23f7653ecb38b54717444344e874f752805803ed43016d3ac6c9505986f6d39fbd392e0009158b69b3ec0a1bf20b7611fb8a42b2efb53a8d6c04169e5846a082be4d8cbe2024ef24db998ee0c923df1f5e8b712970f0bd108ad5a7ebd1ea757c301f760d94ab83e99975f793d3b89e09c6e9024d36752520dff8a04ad0954a3e2929fabb52596d9a3cc6b837ce0859a1f67f43420dc22a99843288fffd7c20cb20e2df83e0e709cd9bf9338c0e703e8cd4a5f7a400f59d072a85b810bc8e161fa30b054e3d86ed18bddb0fb26e6248ffcf31b520627b56344557c81cb9fdd85ca19a85fe8da47623e30965d1ec61faf7968df8d9ed7ed1d2d5718d99cd52738c60a52f06f6b21d1018034ffe5d111f04266f08be1d1c3559e818edd76ae855fb5a516e28c95a23764df4456676cd45e2f00b041fadf1eb1257ef97f2bfc147908fb2ce1849e58fedb60877394301defa14b90e3c9f32ec47d2f718ac1ebaa3d1fe241eb4e6f29eb9b74fe9d0e95119580082906851e4fd085a957f42ba365a20e8ecc2c623fb614a2b51cc2efe6e15cf8afd6fedb396e8fc012e7c2e4102afdbfbeb6e737fec0c97ba5e38a9e5889d2f106a04f8e4db4d6a004df15f25410006c19665e0d004a9776373d39799799be3621eda1188d84cb0a64b96bda01578e89d27899d1278c273aeea961de0e0c954600ea4203509edb65843afbe8f2b44d501779942b7cfe92292b20875b9648e3d7f070435fa7687bc8fc12c3577bf5bd4b49d6fb6f1b7a5787aa136239f6d8e690b12f6cc7b5122bebd81778ef044b507ba1c4bafbe81dea8246dd378b831933477a3821ec32030239ec5b025203371777274d2fec9d1a874b1371be02975f2abe745ef43c183b958154a00fbd55ec25e0799c68aec1f2b0dbd10520594e45545cca10e78c49deb689e79aefcaaed2c0ef3fb7e20e465d79a481d516f721dce63828c49cfb7c6921231e60f4385d63bd826e05257d19470ad7ccb528ec34b40ea23a8cfaee9063cf3c17a366bdb4a0351ef6e0ae174411cb88f95cbd4b4fde120da60af6d925f4fe5c3d8579ba604834654c2030adc2c959a4ffce2bb75077845828982b13408ecbff8640c695dad50607f92f5a0ee437c1a350d1e7bf21319f03c1351c070dbc433fe4f1b125bc5299d0e11f9412e6654e16efced0a13e008d721cf8c179ae3584cad401bcf01bd31f622303d84f9d019ef17c0300108b645d7df9ceaf7b69eaef274a890e770f1e0f094d6efe8f6c701375b40e16946c1d9819d7fe8115aebeea28b2d3b6b92a5df216df6af713f0387c763ec5066344ecd2056ae13366b1c7cd5880473aa5465d1349eb511fb9311516af31f034f990d44d78749cd291fe30fd0a4d9c8defbfc90b89e5c02875073a08054dac1979412a8d81ddf87fa6b6ffe8a9bca206fe47c12d2bfef8240c81ffe18c79eec2b63be1d032b2e463891b0948052cf99dd2301548f43a844e363ea304442216d2a5755f5d44cf2f6c405aaede6402236f1849f864b02a5c34e34b5533910748ccb02c45b6df8a5e1b1df661f7ebd11a7b0e3976e5b0d1718364bab483095e09c7a75a39207920f8b731569bf3faa8edcf27c33f82d1f78d7f88ba01321301d7e4b9df6a020db483a927464efbd507167a1ca5c5b18a45f82082afef46a39b54dc303b84c5dae04388e29c987a5198a1380034b8b97b0c2c5ff61fa0d7422055799cf97bc86479a11530ded45de412e1501f586c79b66b4336c1fd9225f4654cc3f3e56f58827568c1a0a1a10537a167b1bfba15be1faa113da569f444d381bdb04d12a1ada1ebd9c4466f5e28670244062742b7da674e5851d42498e0a8b0d8ede709118ae3f51807ab4ce2e6331149144c8ade919234cffe9805b5ef2145fc2cf1eccfd4fc73a6517f2eca3cad8b9f5120d387c5ae1fe6b86fb4aab2153ab461e6f6fa8a0ed787b9433cc05b90e353ca737a31cd23cce1546b273606f3c49c00c503d81d91ea11d5e3a34fc8d7b1a4848b98f25091068f000dc2e2907fd5e77c06b6e6db715dc2498c72d77ad288a2412c458b97b37cbb3e3212177595f2ce0f2e40176e6a02b7b1dc830a940d136406cfdac8715d4be2d634a156e75df4d1bebb53983aca2144733ba57a427415c2570b38a0f8ae1d6fd7be4cb1b8345cbc5e6d215b229f169f66f17abde4820bd3ebc1e88dfcfe9dd84caf1ba010269d3f7b936e49387f6048e6374b371dd0f9c5182302e04097911e3190dc5a304196033a57cba231e2aba392a68fbf126d9f0810559d95c26cd40b11b290fa2357f94fa7121b58059349848cc8d52953ae33e3896f050fa061f6a3f6ac7a1e43b422d547695951326cf4cb86a78cc4d34d3f62dbeedce8dd7501a75fd15895c83b603abce832cad2860beb6d1e9d184fb131856c7fcc898a24d548136dc4de858a2b16ce3932978224f937dafa8fada5fb48e3d9aa31b0ca76979cfd9dfc406732e982adc412b5d359a7aecfa731ca676b00ced9545331ea112652062078a45a38b839ee0e853ded2cee97b5ee78d517375d6171c11c1906143dc952d20854acd50a9791269ac83ddd2dc635e00eeaaee7d141ffc825801be563c1dccc403a3acd89698650aaefc13c356a328a73462eefae96463515df5122e43eab1a996d668bcbccf6b1639991a50e4d9bbd6c73bf3bf08523b925cc0b0d25f7d066e627b0b621fcd3c62451d66403cebc5c6dbca1de056f8539b5811d95125cad9ca3449a910909cba5d48cd371fb479ea7fff3b93ea5bcd560c11537743f5bad597896f20c11bdab130755bf801badb3c382d4a2a8c3671ae4d855d12cd7bf6134a99eaef3e50dddb65ea8d37eed5028b1ec46f29c3200dd10e67ae8d3adedb841d8d4c740429c77dc92efc8e71d4ccd073de843e8be861d5d2fa147fef563a6765cfa385ca8bf3f3e1f2164a661b771c68e680d18bef4b6ef1e92f2d9bb91d0976e43ca9aedb2004ae7801139f5f152484edcae4523cf1c1c8ce92843ae7f96c4f70deaed26593463152e2d5ad12118d32a5b6a475456e352bc3c8b44e693e9bb253d2ccecf3d4cb1ce60483511119887149358a4a7bec9bb2de08a4ce652b63d820673ecc1cf98ef5d628b4edf8aafdb4fad7b9ba4b03a914ef1e9fd90645a4d3e890a96c2995d01da175d4a1da8b80842d2ca931e257d8392ecfe591615f3dde1968d3d01c3d06f2e0873a072d058fffe4494a88fd1f70a98c94edfe5122238645b574c594f2c1f8d88e85baf14a7f6a711010b5f685560e10a30005ebbf481903a1efe3f552558897478ef1354c9085d335070504b4e66d2c1a4bf38073be72d6fb2e428f4a8df60879d1bda5edd78b89c26fbdf08cf02d0fc35b0a5a236839e8e9527f123f992575ed364befdc76062493c1caa5c4e0fe1c280e00bd6e1fa799874463834b8c038896bab91250847c4d9ad36e70d442c7492a4181152ecf805f60259db84380602f71d03947b7f2c21ea272d9afdb3028d7b125b8a691488ca2540b5d45b7fbde5f03b570474f89d7b52fd460eae48abd451cb6f759c88ef457a5120e725e61fd6f3208ce4441ab680d1e47250ea4eb3bf210fa486e227598d7fe3be338a9423f47c55baa326b052c71ee21ae828b7f9f8baa1f01c6171bb0fc42504ac9c0dc23a0074d095a1ba2f18d50191acf9e77e1e1baa1cc641bfd3bccff8b3f449f83fd975e0843e9f24e23abe8bb2fbf1c6aa7eff6beab79c284e06ce0d53baeb30c6a1dcfd26a42f92d098f2aded751cd072b1023f5f9c9eb6e7ccfaba941098ee8acd48e991d1420508619297290f8c7576107e9b9093ace3ffa50a3bb4d292e3ac34722db535e0385a9ad462d680b35d42deb68ce1b869b3d982317f0292d4d4362759284a0d9a1c043e3142e2980784edbfa340e85bffe84850aa7cf2be0a60dc20398119fae38353e0a7c5eec67b49110a7e8b87c53d0c5bc82614409edfe4242ca2c870f7293f582d355b62505cb96199d6c7b1d77f84d912e415490e32261b7eb6e1a892ef717cb5fc4f289165ae56ffe9dee8f36d1214502e5c96bbb1658fd8f884f3d0d811a14857e2120ab39aa3b68390fd059bb0edcbf7a01b9f084291ba3e961d55cd20fb421bdec242316a9ec7c041c46e85c678731bb424a0efb514f4b764cc91e8fbc67296949df7d1a3957dedd352b0212a08d10cc6dd7a44abb8c5b089c87888a1da58b697c52c98175c68ce30687fbc7e7f9e9d91692e245e2ea8188cbaba8be43fefef561257dfd484c6dd9d24c7b9427cbbbb83ad1eefd922d8eaff057f6cef3e29a02be442fab43e85c6f1b0340284e0329a863606567de6931eb923cce907cc74b4bee190a9f1676d13c94d02d694cac3e6b4ea1a8f77e4a451a752c96e4e12c0ffc6a1159a5074f584945a9d16dddd4b6f2a826397452527860646bc6a81cce465625870db8ab111b3852b603502d76589e164036dffd306033df4ae64eed04de8c1aee4a79aa81c09fad769509f46447d1d002945001968cc64b073c8eacd0e2bc10f22b8275651f5d6359e1398f605a0ef1b1b897ce4020a0c4e665b409a19a5034d59dd6458527f4240037f4ed679117702530b59e1e8b635366b2bcb59b932ec3a046f0b0f14d099636ca57f00340a56056306e451d70528068cd838258e817330feabed18e8993154f2892464efbda5dc324999920cf4054906720695e7d4f3409ed3a8275ea451eee4fa3f0d17d40e82e082e0d920bdbff3748a4b429dc2debf9ff44cdcb4f7937be5ba19593c95a78b9e13052617759f4f1488dcecf57b1c479dcd2df190ebb003d7bf8358d0a5af8a4e44b587503b53497541cac747ddaca565d30173b187428d7222d74bffde49642696c52b246ae93b1872acc73041ed9a95e3acfafecdd339882190e296e0cd401e11bc54e9fd630edf805178867fd00b80925c8f2728954a3b4a25f9504a19ad4bfcea421f3e6d5dff242f1eb6a050cfbc788f444edfc5212e1c4e69f12f385e41c89dfe5d9c4e2d957b368090fec70e77e53b9667b1cc047edd704087d605ae227cd4353d8b89e3a00b24527f3191ebea5f41e2253d6c75eabd5a7f6e35f1eb8ef8f5fa4e87ed83f075a941c128f1dd6f84a49fdf62a301fe0b3067ce5a4fa717aefcee85d2475bbf64bbf8f379cb11856ee57cbcdc78499674391e7931f34997644745a34bb21b94ed92acb6ca201192c586b04bb2f409bd243b7b675e92bdaa5c97646590bc24eb4cfc926ce49c17334916fa7831f3695a01f09246dccb8b9924cbef3e18a60c1d0c371239cef5a2eb71da8ebe78128e3a21d7816a3320a8e8879b1b3febeeeedbb07bf4cc0ef1f9a608861415720d371eb061ef504f537b33c80ef2e1778f283e221ea29a448af091f8d446f7eff19112749f8f14d6edb8c8478a1217770e6a546c54648e7153bba4354afef53df8c778ba6c2e7cc85299a3c492c7df214ade9c7b57e9d4a8bfc1a0a0a363026fc928c426c339a94308b31688cdc813d3c12686611886d12ca39eb5406c46d6c9e8a418866118866118866134cbf846d7b1bee3a461aecffc3b17fee89c0b474a1d7a7ce8303eecb2c7300cc330182ddde86f19a57446ead4dd9dd277ff4c669aa6654c336ccee9590bc426ebcc39e79c93fec428a594523ae9a4934e4a297d4da394524a29a593524a29e528a594528a61d0764b36278c11ce19638c10e280df924df6f8943a16194e233a73ce39b1efe86318c5303ae79c139bdc92511c9c172d3814677bcddddd378d528a19c128c530b7af8527b35377d82ce83a104277994e3d1beefed3dd25cda8a4f0a785d78730e8eeeeeefffc9fbbbbbbbbbb3333b34ca77809eb65f07c6676eaee4ea5a5dc5bd99ef2f0971c0f7c15b1cd6a142f41fdd73a31ea5c9c4be6a4f4a19c2c2f52ce39e79c73ce39e73f29b597afc9292784104208218410420821a434dbb894c6d69b65fa956dd3b44d63693122d5d2b760d14cd64df062ae5fb16ee4cdb83eb3ced3629de55aa992469953d799b66f5b37e24e3ad5f98e63462846b50c8bd675e2b7bc2ae6740c574d8d291263171df6fa93ba0245703df9ebcfda7277bfc7652e14e0a7c6b7816f4c9ee11389eea94627135131c617a30fb24b3e84307e3f9473bea2c890878432c89f1c0c174a79737d07f54c174198eb6fba757d0ed1bd747ace5a7da8003fb58bb108cec4d3a95618f49df3e05ed7fb8f1d2e7ddf786035d84318a3e41e0fa69f28422de280b048e75e2c7bfce1b7b73a0581c41b2fad3f5e3d3bfae39d5c37617853a8f4a3bb00726a37efc5dc933dd2e788dac5a27b3d5fcf4573d6a29bf99d73e0c6a24e4db8f198efb18adfb8cf6bb555915d9fe5d0faa0ebaaa8372f1e6a8d9ea932de8c8906c25c3fbfe3cd5e4308b3cf1e914d07cc6d268dba7a9baf8de6a45946a11218a5975cd79487551a1587dcebe10a063deaf4f574fd9b8f5db153dd5af67c7a26e634ccf51135e5a59c846a947be540c1fefaa96dff8374b7ef9f73d639e79c1315e5a19c7c6a17cd229939e79c754e1e738e5efbd19cdaec22d2e8b5efe66b5d0491dcebb7ed35ae87361d238e07be24aee36b8b18cfe93a5546f2770e73ad7d13815bcc69cb43d4c9be8b46eec6637ee7742afb94ceb3f16a26f64d29a5947e67acf8d763d86b9b149d5b80fb7ca020e5469161d8a49fd1a7afc1c766867d470174636830b61c3db996827143f4350e482422a56b9cc66956b3582fcd9221d330fd3570b2026898bd7cae23579186b99ee9145924ab482af77a69856be7ba813d289e679d564a646c39fa8ec0755cc78ff8ce4f0a2ffdd933fc6eb80ecfb89e3ecdfe07e966df5a17269616ff665ab81e0f6652eec5be379fc5be37df64a137bb8f38e7b28ffe9c3fe9b6eda02fe7d3ebe5f5b386a59913861974c38b992e3eaecb03bcf32167713b665a92d1a8ecb908c5553a25a3511c783618f062f8f9fbe6d9c0508dca5eb319f7d22848c43358fbaeafdff06c60cf4c04795648c437501ad735fbe4c43759206cb8f45f0f977234cd621b7b9a3659204cef838dcaf4d1a2f8c68e37833e0b01b335d8ac6ee7e3015d1d78803749064cffae0d26cef4d4d237d9693b0ddcdc294d6d72dba917eddc411574ee7b4e548327092592484289249248428925963882895cd7724889af473de26354ebbda2ea435f79a9faefab7f56228de0bf775f96456dbe9b8d6caf1aa883e0cda4563fd68ff5e368347af8a487107e56aad0a704a7700f0365dff1102eb6f1a81f9ff674caa75340cfc6aba9a52f719d15cf060ff536e1dd80df43ddea94d0b331fad34d8bd5e22a8dbac9361e190b4b8b51513f1b0f12d59b13ce8aed555f120ff38ea240e10cc6481a71dc53b22d841f27ddab1c7c4b5c0e0de33d238e83d1aa55a664f6075f0bd8dcd11327d1050db1e8a4c9e0c5f8370cde0c7f1a9f2797931a4c9620e9d17a7ae06844da788c1e8e46d0057835cb51c2d1cccab331aa902b71ffa2041ffe0fd285dc23c11ead55166fec14c985665be8c5555cc199de0182458f3ad5d364d1328ba33e0a753aadde1d8d3e6ba1ad581eaa9689b251bc1c8417e3cf3f78337c9a85a254086368a4cf602ca15ed988d42d4863a77671d5f29fb3f6c4d588ebe6aae7c5c0d034cbb75a9843b22dd4432fc61f3614ae7f3b01166918afddea56b7bae539fc96b86e1da9cfb928de11acd2333d65a84f4e3e314a50219093be8343b824aeb4f1203dec89af86c445d2cca16785fa3f9d48248e7be6acf574facf2c5f2d87465514843d5e8c731178b3ab678f3a21b772d0e7c5f8471be304fdbdc358bc999567a37e577af8d9af587e30a379a3ff8bd1c8ebe8eb88877847ff8374475cbc71e39171d5a10e73c4e90081a00f0d24aaf70089cda4513e10a853bd6a540f9ce90161fceb3cd5560ff7709186f13fe2bf72b2615f4befabeee473d67a3a954ad5c29e2dc7f53490a82e9ffec5e9503f089c7fb94aa734ce45fbec5db4294022f4a91cec59b1249bc3838213aeff68e5ef9ce38f4df19daaa407f6f470c3d7e223ad2f3f7936302e07e582f075a11523cfd6395070a98028052da80222f997fceb1b42785d8df39119621b8f0b5e3fafebc2998139443a4a70e6ac15faf78b387db318479be5cb03e49d44e2fa0efee5f3fa6de3316be6ec29a7f63a65a3948657c311943cefcaa45f128e60248a5286762a8c92e52f0ed4a38bae366d3be475c938d4364a71a03874fd19c77182dc8f70813ac543fe0504a553d7252f79f525af4b5e3b2848e288248e2069da43ad519ce630a4a8fd1947fa9006d7674986273e5060805947618aa5c17538e9f32b8adc19f7f944e1c99df3c97881c1c2abae51cae954ca4536ab59dc6ae5399ed3ac2874fd631ca97c3be7098272f2b9ce4eae738b899e572eb4f39c8bcb2971bc1a7dc75ab8df71d1139d8650f5742a7d27bff4956312739e541f28ff357a3aa8864e7531c6af357a8f9e6956ec624e8cb1c61aebc3ca755c3fd63867adab6f168444a56a732711d18a6805677aac5e110ff7e4f01a55a5e69587556d3bf9d2bb8e916763f4fe1c03a601cbe0f18a85c03e6027aed7d48f75c6382f97d2cf2fbdb42ea5ac645d485f7a2844428dbe34fad2e84b2f638ebb4ba16115547af991d5a937b23f769072db51fa916f3a4a3f922f390e62229d8cba8967a3f42efdb352e9474f2da9f4b2b552e9dfa6a3442ac11ceb52a281a4f210ef0f26ba948692faee0c8219ed8d074b4e963e22b9ef8eae182265e89652d0caa656b2a7f7928b96162f4e25928b96162f4a3f4877f43f76b872351b87949c36a575912ef2495f928fbad963322b914af4bd9812d7456af98e46a552e9e35f5b4de963e9e3c71f8d1e48844572e407e11fd9cee646cec5ffdae24b3b2f1b214fbff3f44387fefcb716f7c518c08d1793e61b58e22ad7fd7de1767ca3c538529b05e3d9a8ef7f8a11396888573c74005d4c17c34bfd57573458c040fc4731c61823498b31c61829b5236a376a356a336a316a29b593da8b5a49ad53ea83ae68320670dfc768e1af4b5b8ecca4994cd98b574686273e50a2387922c3131f28ec45361d308f3a7dfb4cb36a04f5f9705b536e6d6e385a795ee17ab8f26df0c1375e2ad79d8032f89a95d128f8823da4fde28f1755681708532b7dbcb8d4d47b7dbd5784957d0b3054a1c22bf3713b7f1fa77ba19cd3040082ca4f03a9505404afc722d3e21fd7f9a8f1c23e080361b477615b6cc696f4d994db05e96a3b9b5bf9d9f499c9f434bc6be24cdfbd273d6f39deaf3c0d6c2bee0b5205cf203d0dc4b8d8282cebeecebefbeb6920666ba3de531969b3d211882c768a5fcc230095dacddb0f8483781513ef9fbd9e9d5cf7e23607e46aea855f85dcdc20b6f3f17261b4dd04ee6cd4cbf9a16228422142125aaad703233ea0040545c512842ae3250881c552bd298a30c2c34714a1e22364a08affa0b0019222d583d2841f0ca9daba448ea1509db050bd34154b3481a56acb5258a28a2f78e28996aa6dafa05065700452081189c06941410520f84cc1042b5499944214ab4b8396462bc80652a8da9688f8a8b2a9417b802738a10211a8da969280a37a58b07670546d4d2dd50fa028e951b57d3c40a2ca320dda4e41950fb0546d61f44095695944c1d934686954a9c233d2a07d403e54e051b5a5f14495552df280c91538aab632eca8b2aa41cb508298028eaaeda362a5ca4a1ab42f8a245000a46a2be307aacc6432f11344aad051b5e5265350d9b090ee68640a43f7f9fc1421c07d3e5338723392fcf7d2f12a7bdb1d24ce588b9e8f4631777777bff71e510d1068147f9146e9740cfcd712b56324fc1f84cacf05e1238deae722aca476fc7a8ae8c0cf726eb4409887c8916b1fcf10532eb48f87089ffb36fe9510d90bba4597b920f0d29036e6062ae8c30322ca41162a21b38505d5eb183a99981ba824f7b2a70a3920422584de14a172215081c5119510fa022454cebd8ec14695b5262f1c98c85a70850b0ed2ffe2e49c10460cdd09da53853037b8733e3c1e0c9962a5127205a1f2b751bdbc5b54c7d03f9ba8cc690d3bba207bab1b04dea722000e90e4b066abb9c08d2f754c9b28849b85c81e9c6102080e9c85aa3f421df3422e3e954d0be15e408c8fb18c0daec33690716e817810befdd95f4f2f19ccd579aad7757d76d1eb9a7c71965ffc4ecdd9f5cf397598d376fc5d3f7ff78c173f38baba311ab0a7e9b62fbbcccd819e7c83af9414ce4bc699b2a4c4538c31c61863fc1a32a90b5b0f3579b8043d746a32a5efff2edebce67abfe28e2b8b2cb2a872bbb5c0d7f7d029f93054a1fa90d73fbbb4882309ceabb06f9b04e3849c544ccc975b8ef9d7bf4d87bcd3a5c6afcbe9d7dfaf733d9800062de8c192ae385870f02fbc448918e343c93833bf26744f09447caa08badd1b8a30f80d8d8a50e6c5c47e9cc3044e5077f0a1030028f06ec0e8716a4ee65f1e8c08801b9d6511249040e295ffe4cb95cf921ff57f3abdf737dcf0626c6894fc1846aa7cff299fe533ce032600676420e08077a3069e215f7e0324cbd420f99f9437c439ff05cd6b5eee5e0511420811841042c8a772c6a7f1a9fc39ffc27e1524d4a817853b5f13ee7c4ab8f36f7851dcf92d2c8b8d3144ed9a4872d1bc6a3c3d0bf5d0a39c0d2f23f328d7443634ea79bec97597e47a086e351bc8a40cc2d77fda8c5aa7b487b0a7b435371e16cec4d669a8532bdf05e0521797be14d6f50d1fc233f8e95f6f26b6200c7f0bf51043ee07bc47628bdf87bce715ae3b95b82b37e4cae74b72c2b321038045769c112c824494cea773235920ee3a3a439d1241c57ef2c05e87f9ddda96c0e373cea12af5b25dbc7a8852fa367c5ddf428de2abf5344d27fdf897eda12da31f3f3b9d78b2e4160fdfe016bfd6e12f2e026d3be847fa114807cd38ea5a0c8262ff6a7ca3127c3120a0e993031a7191630967e24edc893b7167e7c812264ee24e2bae5a4271272ae1b81363488544494c4470c6c43537f7749eaba4535d04ba0ce43dfc114aa7523bec3871c771b63f7d3d1111350c3b0e914e25313dbfe7f40c2ccfc2c19c1d252b169ca203e6c6238d021219497c32e3ce8b611d2863cfef477ca7534fe4734fa77c9e8d9f67437b7ecd6df7b88eb71df365363fb6b26881308645248de2211b851a8501e9dbc51d2596e019e7f1c8821883cbff38d6e0f6cd0667dc8812977fae00069d07b9ce8be18fd1813afff13073175b77c89f3ce11e9fcbf3b76f75ca651249ce65c623979f29078976903033d46918fe9dcb4aeaeac4facb33d4ad9e74729c13743dd39f3490d429e5b6f1882f391ed8a5e652ceb9c8457e1751d445d2bf2c5ffeec09b32e33cf0d3f27a53ddee349ffc190a2528ebe89524a5bdec49cf366024310419313ea8bfb7ea0c02102117df91a22563218cb20c8fcd0a613d495fb7e866025a9f5be9f2194a086ac64c8f02441660b75def7d364ca4f932b6eea75df4f132b6ef795e97d3f4d9ef84c0e6032b882e8a14e94644d103913284a0c21767e6c80850b354000b50864d75307ae0eb4ba3a31c2523364e58409538d1044bf517131e9cf4debd4281c3b083aed00827559aed0046645185d4888538cb22044e13d4349b6eca789144d7e9a5439028588915abaefe7064911eae9be9f9b199882aae9be9f9b9f9f0eb4229d3f38442b3c3f9c72a82067c7103b3f1e58c272fdbe1f204a70bd65d241d5eef3a1b2c3022a4b9250b1c14a0ea83c310541258a95105069b530525beef3a152c513a8546981a2ce205c34514b1f58a9a26e2730d5a0624b58a8d45393962bea8b276260c5128aa8aceca046214c59c08208222c8cd8f1c3d0cf0f3a5918f2f34311598213b30c2264c1c10920d01942931538d981131941c53d5596d38f1150eebcefc7081f262cb2ce285010f2bf8ccc0d5e3f8124faa0dfd79780bcae4b8eb21dba02794991fde418e127c70637bbef27677583824bc8b48cc02a0b247870108509ace052384129b2d0441658d79fe524441656b7abffd3c5e62b46d93931eb77eef10cf86c9bc8e5eefe00cfe0f77f8f3ba7510c6d3a27c2eef7de0d41903803d00c706ee7e355b9f2639c314e8f9eb53c8bd252f08c8cf92767ad46b547661af860610a137426158d75f3bb49ef94f25949e94beadcf33bad5f1a8875d83f9b510c7b8a4d9f925e382f74f7c97537ab1f6cee32823de137a67cca97b92c5613264d5856d40fb056d50824446978b9b3fcf2bf6c3fb47e3db32e0f4926177e3ba469ffaefe78107ef775b1dc2fbfdcfd62f9e5b289bce405e1e53e9360a520486cc25a6de19be0d77258e4c22730e7be6e4977e93146180258c4611197ec1c80455a485e5a3004459e04d59b560d32d950da0640c8e3baded03694cf42b78b78ebc2bf1ad028d81b733e361cf0bb877bb564a1ebba5a08c2776602cf80ef300a11b35077c36f94cd643985507c5e1ba544ed60fc0b8a45860c8ccac24ce01b4237dac753536ce3c442edfe9b00b28951845a43019d8a7dfb9f0c9953a39a7f7311238a0a43bf8c8b67e3dd8e409097df81de0c36a57a4fe7c37beef5419815f987865aadeb3d70c67b200c73d7dff982f8f140a101a229024d104d50ec5e5ef8dddda5fb0c40b70bf2349df10c7fb633f85c9633f4ccf004cfe0bfdec61f25dc5c03dcf79304a19bb52667f8725d0f6e330350a368e08f8b2777fc18391708b91cd08166632bd677cfddb9c9753eb42bdfe7a473da4ec87dd73f8e2fb9c733e6bb8d3fede3217886672fbef7314e26f2fc5d1046e843ca0211b636f7f1eb7763dcc18d926b80fb7480051ffeb6f29f75e967229d45c6b9ef25ec2b371c4f4a1b1af8e3bab21372fb89dc2ec8103ce37dcc9a3febe1f6b32502dfa6e3baf26d2e9146d9fcb86e3f7fb4ce77b68089dc8cf46eeaf462612c51653ac52f43f08d47e4463b97d4eef57011b543a1ba575530773c1951e3d920fd93e1d14ce0a556fe7d0e3301f441d1bebb310323460468bf82a397ef76f4d9bf8d06865b8e15ae470214d0a8d830f16550a8ffd3a9d69eb3d62ccbfe75b6814b7a9aee61ce5a4fa77f92a57931f1b3a1195426e278fb17a0d4943f77d30289b79b2f075dec5e91941bfff29dfb046043e28b05fe5a9d99ff547c1a323f8857d58e21b218e1e92f1972b5756447872e1b0ec9b938e73136ecee281f76d3050839990d75dd857559f9d33f558b6df9178f838b3fb9f8171cb78116aed616fb542eec53b1fc83332e2b9ccb025adec5dfd0f22e381f9858791f5ab87943cbbb78179c0fdc9b5d0fb7bab03640181f452253677349cf1b0ed267b0458b164fc3bb2ddee6b2d8ce7613b82bf54db6e36bfa1696a5f46f5ba169163b7a93b58957f08cd1d7d1d340920c9c991a9771a8dbc9d096540189d2c89525abd83fdc74d0a891c1ac3fadc1e6cafbf852991bb3e7eb326e386810fb78461753fcbce1c076b894934f03697a72b5515c27e5e8247b2fda65c8c2b69e1a15e3cb28b9d928f9129334337377777737337337337377676db76d8bdaf320aded7b120df1d95ab7daa897e4b1b45d61666666eed7eff57b8ff9f57bfd5ebfe7e285113f735698628850c61d5d483e962d9f5a151a6ab5a2045d304e2cefb59314a2a121a1d67b0188419530882194fb8026009678af7e00625049a45a5a79c19c0b1ec4e089e1440c27e4bf972c8bc123a59452b3325acb3ece2ab7effa460d03d786520c27029b09a8c4adf68439ea14cd0000000000f314000028140a870342d168308e5359121f14800c83924a6c549c8ab3208951144719630c41c0104080012300032333540188e931ea869d68e4e4eb6a1e8035a90ff560a5bf1a2cde0231a78a85f6e8f8dc5e5a3df4906e97c6600278c9cf76eb4ea57d70c331f1fcbe08bdf6bf6f4e07084d97c2f6666e4302e64b740d1b42a5d15f8351373a0b0aeae718f1ec8f38cc8777a925ddc1e0f503583ba82c4f05bf40531a46f2eae6fe0a4078a99e1c15c163d8a28e2875a5811a686fd2e2b7c780350d85231bc66c8e9207098db025ed6d12f313e1ee839429709fc820a11ef4addc06055bb00e41989b64a350ed7b5183d5139427b66a3fd87508c564d87abf2099e40984a1cb761614c619e381f0b718b0b869939fafd9389664481a40d4be5361c779e604ad1f64ad6c5d2e3f48487e974e0c2c9835f61bb85f881aeada007dcc710413549b3ac7acd9579240e51704bc1341e460cad6715c5e461245d0d6ab78a86a5910650f45404272fbe6142d821f0b8754f69622c59a54606eea00878ab49a80717ac39de066a368f0523fdc2e7e963cc84a950388d43a01a36d1f0e308f063b451e8d37849adb8aed5a07df6cfd1d245aacc31f10e21c9ce531840a1eca1c84d753d44d6c62b57c7ee37304f82de7adcee473de08384a4fb8a4dcf5457ea941df7517613b6ed38a2a5b77e1d54dcbbff1dd1e8e02d69343aaec1ea5a14761620b93c89e6fda4804dc19360c26f0bb2b9331e7bc64c5de28a02c8a276593b84be673fd023ce1e7e9682d4916f76b3dd7efc04fea6efa5c7750d5db697e3fc4e22c7508e97247b18dbfdeee5da330e7bd9d39bb4f227186eff03b0593d2473a7906c919402b0b071f826f044576900ad254b632d67c1c71f0de3ba4a28e79458e87d4d8c1ded71a30d65c0743d038c8d44033a4c48df684d5ca69e7c8279d2def8a7d767302290a0609a334add61ae655d1b5ffc8a1f324c3c22c97192b966fb18de1f69d391b700b208c07a37044639df72a38f7f07e8e69e6ce6dfba1e0cf46caa1e573ce5e0a5bf1f2d1e13c6d81c0c19448375e82ea653f22d74a3ecb839fdf99d85b5509883fef65f7d8141becd71af1d0fe62146b628917cd16ba1672a413eb5877e766ea202e02dd9984e50050ba7af10147f683c323d4bae929bddcb3eefffbd7d4b89372eb7deed8df1df6afc072badf42bb574fbffecf8ca854faeb55cacc892da4efb22475fb4c18ee0fd8fec3b04349d4675dc0cc396ab0e23903ed829fd1d15ae8c11ac1226e789160063de7f5e45be7bd5bc8dc5044228344770073b1056dd27779de7a48e3c0facb979d5db45485584e41b60e743af1dce856bb113bb3ad01f12191cd9af0b5ba74fd7966cd83ed7d4526d75f4b57f65a8e07558682564260b7d9f01e5f306671b717e2a1248a9fa1c4798e36698c1b8aaeda2a261b50a3484b3dd7b894c170983bf7dfd8684250be4b1a76575cd660d8f280bbc01805fef0c4007565bd990e6255f84f489e281c709a83fd20950693d0ec216c182f6b93fc3d701b02bc6a6bd004021e88a280a62584596ade2d1ee8ee0c28d7caf9686eebe6b21ba83f7c71dcd779c941029a86b7970dd6529dc9b61545ed528f5a04b58e48cc039c328fc25f141adc218f2eb7431488d8b4da523978cfd4ee29a5c64bd7d7650aabb1c3711ced29dcd49ec7d21799c786d5efaee28e1de8ed8a53fdc7c3b610f2c0c06be7368199d3e9c6533be51e87676b910a4483d730dfbd5d9b4e4c7d83aa53c95232002eff4cba69dc176e75b04555b81da4e51cbe0a045d6a6dc8af4bae3967949cda374d4f1e370226a45a477835567ca55aac771bb1e2f3664769ed045cc58abd25d78977578435fbb61b5f359d75efbef1bf96b591884dd9b306ab8064801daa150c017b1dd6f82ceac12d3a2d138f6d178f76bb62c8b0d7b4e2a0388a720bcfbf42af9664b6358289a27c60fff7c229d6647fc3c4c34605d16ca82ab26ea2422b220438c1df43d4a42b48352e621890cb747f0968ab705a39b0044f2679569c64b69c84c47060f6b98f981cec6a5d55552c27ba4263c0cc7065313569eee3ffca89556eab6955dc59635ff48f80ab625fc0f696f2add54dd56fd604bba13971a3a0971ebceaf4e084524798139394f5acf53a4d9dff7d25ed57428a51674d86deb9b73be2b41a249b69e98eac28bd7b6abe875477296286401c62944190325455359b4452b44b6bf1de7f4b7f7183de4ccc02648c70c56a50443e3103032b72891d9539ad9c904f2c7aad35ea368e6d34115acd211ac3020ccd94132fb860f394ec4ecc120eb900654738d48f41e98641bd64e154e7bf393160c88c42cd33003a4ca49b1fc68abeff3cdc22237724f722e774cbf544921db8a7bbd5165529ce6fb4e8f9c122c277a1c130fc418ff7ffe4c992baf9b91322048cedf1b2b9b49b4914b0fd876226aa6625ce2ba1fd432e216c8eb3f8fed178bddad9f9dc96018b82fc291a1da48ea1ced451cc1adbef978b45948ec912ca2e9214d22750bae1963b8a721819c49ed72645fad23b2b41958c5dc080e31d1b436a202f030f2a140ea23d3016394db45faa4c21b03845a01a3d76719a9759383c42cf02a150dc1951f567d42f30fdfb53a9d73d5f6434458ebc1c6a2f0208a849581feb38840860b5458c61e1ac241f8a645c9e53f4c5446971be747099e669b737d38950233861ac61895142e7e84d8c3ac6d0e438713f4d75a1152936905de6d866da0bfa7ae94e21a28609e6525ef65121a48196e3b49bb628f1d01f14fbf7003469d0a060b7d9eda01315cd6e70c7080985c90c94fd7a7d8cf430141cb8daff3e6c9bbd9928760184c9d7191367ffdc9ff9a69fde7cfdbbd93510eeb7ccd7166f1ad217f13db6a6e6338b2c221e9426ad5b7b015ab3df931b0ab09582cc53bc6af35755e7a3f4bdd85c9b736988a95ec003b4abb692e6d8c601522c7e4ec755fccdc93afd1c81b95d60d47bb24da327cb316920a2c301a9325588c0d25ccf52f4324987c2fa2ea236ba6b148d3ad1c8383a0d5ba4e9ada51b4dab882b57af46ca8b314121acf23729441b3346491fa164971f2cf925962bb408165f1f64a13915d470e8b6ad5c2b26012f1a58f0c96b6ab6d7a8b27aff5c80eee490e47a747af3f27e2b0f9722a3dd177e35beee8255d26f256646145a036cb24056fe8c720e4f9d253d3f5eb6b6f8f08fd8473443692468708d77d5e4d58e52907b8144313e53a4599f6b39962db4e0e482fb375783114285979765b14f991eee4e13d975ad636334b7e6c8dd930ab97a29f750ea82a1d606643612a27a7fc831412c901237e89f1bbf2d5cedab721d631ac656f232262bf42c46563d03fd8067861ef9a9d1b0e7c4590e89e73d83cc37c86546ffd2c0530c2d7fa72294c45ca1a14a19fad137067aaf387b45211268a37e4fbfa0a88cb3a1e275b219ff45296d829d3cb95848c13b40cc5d9e843c6553edcd9cd1d2262b59d0c4757f08a75fbc662fb03f968e3e8233fefb704316f65f85c3c08a2eccd71ad34da0018f9f2d4b99d2aea11402d54be48b460d0465e2ab8820f035f2d15d6ebe0cb1d2d2c6285fd8f5d22f76b63a35ed154d58a0647d4c01124136f24ab8dc546886538260a75154455a0519d8a2812212411419881a02d57bd4acbff716180a8954d80c3c350953f08606cdc778d4d1bf978e1dae3a0155a708f8003eedb826d62dc9aacce9e5383739610ee6acedc39e3305e16e0d0d3bc87753780d5e9ae4e6256f19cd394d6c1bdff76c7e811ec17b65565ddb4bea954cfa774c764f3054388d0511fb49925ac1b86d5b9606ac6487d8955ed630e1eef5547ccd1144aaa4a0f83a1bcaa629a210b496cf859615349a70923d3484e8897b4c43fc316190e65daf29329ff5ab143a6be38435c02af7c5c4555872a1231688ee186040190781cc99eeebb218017a1efc71153774f7c483f0134c7f45a11cb1a0b9189fe1737bc40236e2271f1d270b35408db4c792edec99da5f0431ba9011f126322bd661c54600756c06f5cf2419c938a3f5443248c3a8debe6df0b04b3f249a0e3040f45237abc3d091904d049223f394eb8235cd7001779fa718fb558c14ca0e43b678895e6872f4b7a3db8b290ca4951787fbce1ec7966b29a170f1f6495e7812832c1fe4565ad759b951b35ec06a626fafb0035864bf647bb5262d33aa8b6beff6a9ea06edecb80b3051616c25e988bf5cb5d7e0f88602b34f8bd78f55e667282eda99aecb2a4269a13dbc58853d8fdbb0831b35f32972309e84d598ac1acb59a5cbbe88a54b8ea5e5b48f37331fd7a0524e125463729a317701effb71c07eb402ccf3c78dc2e81f647d491e1d13b686d751184a80801c84b757e31028100279485ac4de3e1cb2548e5739f5a527a5b11e0c09387e7be174a8366635d8079e588649bb23e22d65c2a10eef301ca73d7734a405be5732db480bc1f823fdafcebd4c47ef94793364240a994af808447ccfed0172f6b23a8b72b8bf59cd591092c2df2efe8deef2e243a4fab5e7e6f253a592c3597529f76d6a1b9e86eca2b754512bf60642ce5e1c4e20c28abd4795197e126e205cc6f42cb9377c8db4bee7ac86e701e712c2e46d441c3a354d473dae6485f37137d8004e4622408722cb3b11c0614da922f5adf3275e5f2f133e0d9a17cebc22ed1ad38ce229b71df733821db95677ca3d891be934f9c8e1bcaa2331edd54d0cced21086a39091b61904c447089245d56bee25e0de5e4825a9df35b1c2b55ad94034609d8818fc4b962891ce8ed5a0c8b472457d1b9470157c9f4dcf195147e9f3d6bdf914e4d33d77bea5d6645940cacc6c10a754becad4cc03ee8261b0db667b84fb0b07113f23b244460f25bfe3fe04ce5c464aaa887952b5ebe0c879e2c6f518f9fa7f2156f2c33c79cc525cc5087c83391a1631a47e967238bde541fe65b51b304f1ff682966c3de0919649adf7112d4522df576d596ea86b843cd89f59dd27e9f67202ab956014ff1fd51ff384f15f5496d3bb9a539ea408d915f1ecde80a35f0cba74a50725100ced8053da45615f727da082d480db65ac65ac2ceb092ac2e0520f4212bb147b98d96d39f3625ffd7ea9e94b99a52558f9da3b57c15a59b887d54b886d3c1a0ac6e6589c64ecdb6891495f12c2c315a3f11466bd844f79507ebda8904ac1edd3e90e5cffcc4da8b312673f03d07e2b3cfdd7223c4168848a36318dcfad86dd652495fd25825f958333ef3d1144b575613815bfbca316f2f2851df143406d096c63a2356761bae10a97087fe7e7609b42cd7b112a62f5b49870cdd24b2e5de277e61ad4614d3881dae3a3ca3883e9442740085990bbe3d1d5b313dfe5536d38617ffd24956938c91f2484b5a2d4f414683ddfaf1123333a073bbd724bb8332ab9b5fc17adf22d6bb59e1bfbb92dbd75f9ed9b6bcebf607c750725f153bfd68a3660d31dd8990fa4c4cf122cd76c228f1b59b3f47f22e6918c3f6c1ff2c78960f7c000d71025cc599a795cea9db2e9faacdcedf91f5bef6b23a3c6e76ee3e50184001483700cc39ab5433564aaf7df60979b88bac229ed1b147f1f44be3e44324e27b2744875d2b35c81814c74d9d780753bb97ffd34f42f53feaabfc76aab7c925e1c27dcc9acd5693ad72a727b04987f00f0ebd9a21bdc1fb50de085a67afd1002f3b98de38bf3c3611267afdcc69b5a6fc5155beb46b71e3335ed8962b7960e87d3f75bf64d7d45e76cd75ab116cfacdc5ba05ea1aff0e9ba4398411dd76977cdef3ee4a4a019b6903ebd106309ad3beb79647870f741ac5bfe973830c5575ec30dcd546ae2357070a82b5299e84db16c0e6962d2ebaab19fbae4a60bacaf445d9a0c2e01314f7f502b1ace2d322391671aaa4a1dd4242339df4c58f1e3642ab4e2aa5151f2fc0669ebf87d8991adf5ab2cce0980d5f3fd226b3f7b75013d111eb8f4a0c0554e6d5f5f2e7060bd4012052ed0b76e8cdb9864f28d0251ff99577f985dd0e2a0fb6b9673aef9d38e98f3012962dd91ffb640e2393d51d7f3f04a9abf2f731ed9bd0ec9ec1a1701a9d2c14a4ea5dd686275ca46c0011a8fa9954b94894db4e5b8a64348cd17c6e0cb675348944ca3f9b6501f06c7fdf37e940a7c84df8de3c85866c434cf55b7a2b74cb74d4db000d0a4183a13cc9aa5539771502d8eb411411bb0b1c41af4edf39c8d07bea1be5ba2efe7a3116849f43686eb36771b376d919d532fc491ab67a141fe3e40bf5d7b640768d266436eb6feef4b6b0f4a8a0506ef10dc94f19883a0240bdeded9b7cb60e80ca26e4544f3366cf9f6159762c46f8fa527b2f0c32b3a761efcc6540f9f1e4cd91bafd8d098be048aa8e2a7131dd24409ff0f9fdd2d197c4e53a058ed2b6d81cf8a6f690e1ceb7c4e2b50acec234d81674df8f2d31e7856defe7b56752bb82d0f01d3a5ca9326983f37b74c4b39abd5a9922c0ac97453004adccfaab9eba52c2bd00ea04df2d432c3028035ed037165f6e398c4d2bee5652edb517d64852eb711150ef108ae4bbeb40f10409425c6be2b8ba91bbb99c711f5ed96781bb4ee4248bcf897a811a71c580bc1e9d51d0da142cdfaad7c0b702f6d9e768d87c79adae98490e9e207b9f250b146ed354f3625f0bf48c9c3806710591729777d68aba49dc1d97f8bc906221ad535c0b62fc2427a8ffebfd550b27b8b5569c030a16c2ee5afeba61f62b924813165b8024c94c0b6c1ae7a561fd30c851099461f6404421efe6d2181b48b486ad54cedc0a7326d83cdfe6163bbb45ae1d9e43723d330209a840b9b640c1abb24d147806a9f6794cde2629c5f53fb1ed131209502c99e4ff77783e07e2acf6c043634e00de7ba89131ea1bba006350ca4f3144d33686bf740c3cff7e9d4a3bc016c1c47edb98a040b0cc11cd72552f6de1190e2ad529f17fb0363a7f8e65880aa45048a4586d8b600f0d68b8c418d14fc8b4257b457d26dfc6abaeb3960220838f2ce15d8bb4ef01b88b7b70a0b90961b918aafac21be85bbfa3b0c2f8632978cbef205e83d66f9ec6a431a212c986be9658457bb88e71396090a86dac67f0ec7ce7125403e82846572298bec8d079052f28939be0b3c6591cfe7e732578faa8cce673debc5012b29e1e7b40ddd94d3526893b3d8caf6f5271bb378c29a33f2aa95e0d0119297580bf8004a5ed7a2ee70d340993c34e4710ee9abfbc1def8b202147bc31b1b18f8f9750f6aab45faa7c6b6536613bdb767bb6ddc885b3b06f22ca126abea6f99ab27a9e8a1126f8129e0d089807bebe130050b9256dec545a1a87c3e2d869c6e749be4fda0d76a23af7bc531182558ff271d8ce7f18de3dc2017c487aeeac21dfc03bdf5d4d53bfa03b75d15d76199a8012d55249f02c55aa400bbea27e2444868f503d61c67ab0b80f44e8c9d542f6573a3a92006de0fbfca86abbaab4b57cbae1ebcff2a71045f9bb0c7f37259fada0b337cf6a2a7585ac88a3ec1227affb3cea0597fcf4225f60d446bf23784f24cfb5ba3b34891ea902a4d0b7c7b59ea5b0d3b3faff04267435a48f74632dca775a4ce4028238411a06cf3482c8b5b5a1292841692756407bb8419dc8e4d80b526f9cbab7b2b5cd9053d80de06434d9cf47c4c131e553dcfb476bc0a43e963711c5d6e0874aa64bdcd01d629d55318bf64bce3054a3290b384daca772e55bcd67a5a1e488ddd3ebee84cff76cf7e7b55bffdc2e51d60c6b58e4f83f6f922f8e2c38afc6e3a1282a6bf086a0ad570488bd51e8a5aeef112db582ca1e3dc96448d8a6830f45903f59943ec94ea0a605ba74e64343388e2192b39d9a29fd625047b9a1daea4ce4951516953cae1e1a15525485cdfb5d0877ab2f7e60c5f3282582549ac5a73a9b4debd0fcdae885073bdc90b88c624f2fc1fa8f60fa6f925f906bb7504780648a3a3a90dd2a82c660eaed5791a181ec14f21de52fcbb830cf91e01bdc347304db30353ce0e8c1f745e94ff23ed2069df8386b3a3427857f06a3a3010bb42b607891f5f02141df40d07abad9388e0a773831d6cbd6997d2c5db3b74b5c12e2bdb1ea10de65c5fae7e536477c75064afe6734da0fc4d2b8c72c661becbff162d00126a7b0bf2246dac89a7742c7a535ec9c06c0a86186fc1c21b2a99afe717a599169a04a703aeb499a05d0a5c0b9d21a7e77d9003da593764617e8f62d06cb7ede8270c537c7fcd8a412f15bcd2e0969a625b6799fb2f47602e194b5abfd06eea1e953c627a5612cfd9cf6690f49cd579ee0f68414d3ada59d86b170bca3ad128d0073b09fc17f1ddba6719823081cab3bb205056c619fa829e91bcaf6a0f2fd357ce87f54e49999a8f8b5d7f65b3e4ad6e8381afbe0101b9df8475a7b95624a7e517f5ebc5ca92a8d5d81c232ad1371720a3810b88a1a21995bccd0992c912a03e4b1acf1e667b6402456fdab1f29090c5b57700cd936618fd01cb82c488b5bc4112324762cda6bc2ef3113966b16f3024a0fdce6ebc5c7e8910dfa513b14cb02d87b8f3d29a6f2de60bf7a5391311365f72cef24e4dc7bb7cfe2d3767a9c7302feb4389c9bf7775d25703d322856a9f401424018e9cfe7a94808f49af2f88be1139c459ddedbb6a7ac795b176ef71923108afcd5bf1bc3592611fc8eac80135bb2b2521f55dc5b83122c8d9a95d44e1b033372d4b6b423b958b8c61fc4c5329334b3253ecb4251ca49bf56928a474dd048a8a911977dd13df2c09f6c952726e5ed716546e53159e5aef6f14e986d76806cdd180879e1e5797bcab4209da92da58b350554ad2f7c7baa28bdb47e8d09e580aecfdaa8aade747aa4ab06d9787049a07a1272eb76a62cec496f9335fe7424e494aa73be0e6f05eb11a25bf1cd8ddb1cc7c6f1b3b6e705d3dcea6ab7806f948d53c04808b6c3e96c099cefb539088e80d0e0710d36039b5405d1bec7f03e89cb07ed041b0bda7c0b2a2744d1c2b77437557e7d4ca2b2415ea0c32495665cf4d65f062087772f99070071dc2ecca4c40ba619914d37a8201c547a738b78b81e900c4e394f4c67716db908d8f935099599bf0d11b9c3f693b6af315d1e7cb80be13d3a1591ee3204887f3770320e2fc7bd59128fd586f0bb03fe16707da9e4311fac984f12eb347ce4163b5813deeffe83695a5234564735c433231ed67247229ab3dfe942831cabf72cd0a8bb50901d0fc141b9cc65396a88a8f234ee514a399b8dd14edac4c642a5278a3490795127d702cdaa4ea1e3cfaf2fc52534e8f098fac0e1c958e5871c738f562ba223cb009a3c9892831b614c0a652a93862a06cd8133c0184a534daa51c89e6bffa9c244a6a492053da8e987788434b752cf646f3ddb7c1679705c718b5902995d4e5340a0123be504a2980e2b245ca778780ea2c1a2354141b33861753e45ea325991ad08e58ac6ac16247f0169bfeff7ad87522ebe21dca8717b027379eafc34e6c16c7b23e003bf6579ea3588ec711f4ab02e47d06fc43bf420f2b473d20ba89cd3e895af57050a265b9998448c671eee7f554a54dee0f7151fe92336c706f648563527b34d1ae3453bb2af805fff38aae233fb17bd68f23b759afb2b4410476452318dd89418d51f30ed655f2221dac5a9eb1ae794be10ed4f9ad9dd1d035d2960d16a562634a9ca634762caad70fc39997dd2a2604e346f4d16036fe69babd2ab6085fd6e9b40a5306e83059709a72ca9a875eeadb119c20052214374634be1ada52396425bf30a2b27be5366027f596d47e11cd0933facade34b39eae1a74bb950cca5e80f17e44b97f4cf8ebb997cb8a1f678a38ac8690418239edab4fdbbcf6d7f6ec22f4520e979d27003b535cbd78a93041adbb1d3913f89ace4c31f4da9f7a5561d6c02b7275ae2c60256e436b4cea9f0dbd2db344d71bc1f97563862df02bd091e837572cc880edab337d4cc8e70cd6a0a0dafecb185ef4643267b0615d3eab273480f325b5937fc59b887ebc9d1d7f164b46e9c47e266e3fae5404d5e9ae5c614ec3bf0437aa098f0c930788c0f59f972ab60350afdc194d9c240af66abd494b0c142503918d803fd774d96862d891fc9d5885e6c3285264b1acc6107a9d5006fa110b84214eae437b8bd4f4892a5eabe1560a79465061116b89c36c0ceef46797935a32714407fd1be3bf417c2b46520a84ce59602e0ca9f3fb5e0b0f152d0da4e9860f3fa09a2fa62f352945692c851a07b2d055604b79d0dd2897464503e5cb9e5a527a97a72cfab06b1339d1fea5ecfd4300b756cdabff36b33698864a8a04aec80268beaf56e12b297d11300a7e456b4e4f2ecbb1106ee7e454e74e8ff9170bddaebc013acc370e11c48667fd0116dc4e2f4843a3a28d94c34a6b39be6d86a8dbfc74a879fe898a76518e1c150d4c5fcf38878ab67e5fcfa0a95f5d7cfa04b96ca506381c49c318c570eb234225ebd0aaf6bf5697c2c3518ad6fe611df96f38986820106da1e2600d5aa6bcd1eafa7a8aa61c742293730abef56621f73b9ac637c673ef6a3b754182c5856b8eb0b51113ed2b95534b8f334ff704e938b55775b90b5308f37c51dd1dbb0d342a22c52c662fc3e899d0cfed8aff3143841b87bc3cd208c532ee0fd3a2d089263044bbd7616d94ba398322dbfd807442814f947833322768a3ed96cf23292b47cee2d25211b788a36e10c6af25f4247f4d3636cddbd4fe828a15dd0bd7c82c128b3eb995fe5da6f9ec23910a92c7e668ee44fb2241f720599fb4872f8c4eb4780f212ef5f95b16923834ae5f15c2155326301764b225953e91a0499d6808c30b199e2fb94a0785cfcca0713422beda0a2d55c26b9b70bda209a56539be8552a4745f4d32b8698f3a0bad4dd1421233b73f6174aa8ce814bc760eae4af7187e06931a930f5a47f9fb06b644df044a13e53c921357eabc57d748c3694bdc222fb309283162f42c3e8e816d675af364fd8339b7d9f6d327229476dc9e40ec603fcc652a0605504b354b55de1c1c886a16fffe243cf7f821c72a372d6197d05036fc85160eb2a46b67561ff6547556bc836251f25faf141622e2af3c301ecfc05bea4c8ee2248b351b31fb462d084a785f5f4966f97864d7e580ca588874a8549be0292808a6cbf2fb601d3b30556792f9e874bf19e21ba254f8adcf13f73c695c4752ccc65a097820356349800fa9e64b01a6ea8ca3b30209a201b62acfd55abb087afb2679576dea89bb38b2f5cc416d466d5514c1edbbc3bd74460ffeab217b3754c3c015e9ebf37327ac3da4239fd8c453f23439ab17101cf2fa226bb7930d7f9ed87b20ef9102f6f3350a8b64f04ce5ad726d1885029c9edd7ed6f77a0bad75dbdb590447ff5fe88fec418e190658da7856570b5385ca93eef7594a96141ccaf385405cb26656e0e59b7146349878fd93974c32dbc7f4af60b94fef9d714d7f9b651af8d4a8e9a742f7f52c272159b9ee65d8aa95941acc4fdf67fd6c113cf4e9a58f4f8b8e8d897e7b0282439a800e5beee7daac01a5677e29082836bf0cfa93427ef54f54b7a4658c75e4aa909bca20634c78aacad22cf92671a6f109e05bd96f601eecab1f19975556b63a6426900275845f62fa556059c7d2cdbe376acbb5c896137c49a338a4d519c3a6c7ac66402f1139b08996b9409a3df85e8abea5dc8c7c2ff57adb1a3fdba7e1c563ad3b632d61e9c7313c58bb2e5792902cbbdba80d794fbbc17a221e2ded42e6892039da358627e295fe15ed2490b951a0cb6423226253f294f81706c7171b4e008f304da091ed322fb06c3b89c46cc275ed0cb5c3206c72d71843cc521cfe566c1cd0fe5301c3efa7cd3ab19db9b085d9f982cf948a9da7844908e5069633a97b416418f14f9a3f30288492686665ade51f2be45caf449c8f95b96fcef891a202707ff99920c941575802b6c99b6b6645623e96420bf6ee87af92d2c8b1b7f2b9b1a5ded0248f3c09dcca821f9e5b8f83cf7fcfc262cff49118393ade3c4bb5947552b10e7122c06b18069721a11cd92a6ed092cb4c4df33492206c55263a1eb33495afc1e8c0840570e5c7241fb79007c98aa156896ca365208bb2c63fb5a08d5b8a43b4fd10710b21bd52acc9b388903583ba40941e9406938bbacf13d728ba669ff7b418e2fe166ebc9805c9a4afec45ae400f72dcde954d8ebba85282ef4a571cfd001349f6b0d518db8500e943b7f9100dc78ef2ed71218032a34c0cece0f6f94680468a4ea0c23d4dd35f21612a6e8d8c1757597eee472d4bb0adb069d080f74e2dfde7d8a253068a4a2c4b7712d5c76389337adda6482316460a07c3e27b34e9f1bf8414f95ccb0d2f2778c8e5a611377dbc64ce53cb20f2f3b5050586ae2f41072bba48c42904c93f5851c021343e14b1b70f3fd9843242580a7ce4d88e7fda91152e9159d4a13138d51219ab2877de12590c28d774895c4a95125e7ff614fd4af02b193c9818857fb03d22a8de2293a0c8edadb6affdf26a4f9100f05c2d191508db2b8c25374b156a7132f5be5fb1a10cb3e3c7a78e0e59a8a0fde3bcc715e8eb2e23aada3f3d0f4968204f7fefda0c46078a447213108b95e17aea59c9b7c632603a75d49f040e1134d966d529f2072d005b632d21f5282af9afdd07d51a75bcb2ac82bf71f0896f485e373f3ef07abe0fcd4bd63ddaec0662b484ce82d2595da19745d91740d9f75db35705dc3749f897de4b99f0a7acfb0e1b7f8af00ba3ece7ddc8a2bdc636adac9cfb6a8f0579f90cb3bc08fb67ded1d1725f254aef1a346740dc368592b60f89401dae66bc04bd7a304ff7a637c827d5b69cbfdb36e6380a6d30d39ca15e1a6dc80bea695a3bfe66868c170a1a6b4311499d107b6cd90588a7a7f3989890ed3247f1a141897136f4ca0823c594a9c0686e777da4f06e20e559c5d22cd4f0866eb939e26579180dbea224b0e4bf30cbc9208b4c292702a7f98df102a3894e2c1354415165c2111076d8161d67355e36f7017261ff1f3282ddc8a89e2548b02c36862620bac24e920596b98fc6bcc5eb11da66817ed6c4b25d07699682fd5aa11afd25649b25b971b47943a2d70d893812e06cacffa0f55a2b60d229633426412dd53cef6aa40ebb2ff75f38ee88c82255a9452a8dc3c35fb5b3d1e095238b8b9779b21238fdfc01a5f84031f4b9073e67e4d0379f973015fd8611d0620ae5e396bb3f78bc36afab0eed303befeb9ffca86e992c4b8cc9b995d36c5769f4c0dae4510a299dcf7de345c3adf25aa13a526508c435abb62c613c945bd00996310d072c174bb6f611ad2b1c0f198bca788fd9d62a843210d503ecf5234983af47ad92e7547114cb4341d019004859265614af4cd798e5dfcaa948798964ab97bbbcc679ea6a792c97d7519bdd351c644478c4dd08d3501e9c4a47a133bdc6e20052afb0d3928df1dd4388d0f27a8df4ef1a62709e635259e845e618cf65d2a34c6672fc62003956c11e8d23cb5df4fb394b08e8cb3a1d6191e4518ae81901c507999ab1300e1587bf7bc83a78a8c828b388bf61444f0155235322637a0a9240740f10de6a5ff9a4906d84db0d18f3675ce93a50e996a36cef3431fedaa648f54cbfdc4ec1f8ebe1aa8db76c5de9988bc2a7799cf0549482234f5aa446c8902dcf5224f5986015753837c6aa44011b0d812779b7d569b3fa3681c5695394166b2f23b1735a969be85f74ba5a76aa799601d14d4c2f5866d26c1b6b82632f7b0cd977aa148f8c039a75cd69dd447259bf00422d5065ee3f04111327c9c318000bea99c17d27c229601b3ce110aa082e4221b962b4e9cfa3ce852a4272bb16706177bbb29a910c7824e1b6dd28fcb8903619606460afde95a4b27ee307ac05710ed077b4f5a45c5280655179fedc049a3532f419e516f9bbb79818a3939d7c0bcb1904aeb95247aff557e1edfb9b18ab0d1fc7400da64f874722442c15c8f42947e65f261dda6c47fed1d05bcd7cc09027bf1a41e373eb455dfe203d5289afcc8f3f6ff9da79774a9ac1ee19a2dbe6676f339b84463be5a113075ab5f722a3c3b07dddd26008aa8cff95b4bc6be5cdde4d2d1348b0c5a21d7021423f26210743a911a89ec70b82de727b83e0caf63b1c747d58dcbb381fa03930646ae9e2f481a48017ef93a9ce7a065e0dd4059e3e1666c011d84ae1e1b97c74003c4dd43397401ff8671204792d5ae517972ea6ec4b02b8239ca27f182bb53b764bd1b5db8c3c5a02a2f51377849493ccf324e37854f8ec6c63173ed831a8a8dcdce843f29cf38c98e0f4404c9f0aa3df058603a6697caefa10c7d04388b20e51c29c27cbe63df16b75316b82cce048238e5a0e292a0bb3cc98d0df7c458a3878b0596481000b563b8cb22f8ceb42c91bbaa0216a9b88cd89ec6baa2d36e9980be06e2a8592c581877ecaedaf249b174781e3977dbc60c2e5a8583747db501a9d4ee9434952a6a27f00a9426fe1c38fbbb694445ecdd63b37cb0679b06ea7f71550c82a5dc9ca1ae457a53b455303fd4337807802b2eba342493812f447f82d78abba97426509fdf8375646b9f8488261763826b19b57fc3859b726e0e60f08448c06c087f5f142fa6a45fc4c5c5e9ed8ccf90f6fb08c47fe8df0c0ec0d1704a2ef2561ce548b5750091697215422db8dbd6b9c276237dbab2248b92b07935493d3a2dfde9aabff942f30465583327e6018d78529212e646f9b0bedc183c1b33c9fa16286d104afe7bc8818f570cfb14af44a97e9de1b8a0c466024c6dd14ba390f50f834f9736b6308e1e35064a582831d9a5709f494d9599a093da5c8703ac20645feb3ee08c9a1145d5a42d9669081cf1d0f6056098f02d358839714752e625c1bc397b9440ecfb3e83ba59b4dd896f1b1d990d1efe819de0c8018269766aed6c9b6aadfe0fc32fba05995200e5f75241891ac5c2edfeddd17d2a7e3101fc64cb739e4e92e89187c9ee4cb23f0ba62bc2743c5ff8f032db6feed263d3af1b224f56f291064f9d27d28b17323f56dedef85e8f95a05f7845114977e758414161b24c2472a489edb09f365f75b0496c1ec64e54f21146141755e56256043b0fd52cc31da306f55be07418b323f3f81bcac7ffab05a1969a485ea544820f3871c879369b0790cd766a94ebaf09d6236ce215d5c5528c5549525819c862f4a4b7227a3e582c6dc8153febba01efcf59b8eaba04b440a548a03702078c046d68a4c5482ca150ab21a351846c2a3a4ee9786154a2f4969616219892b6f7e02160a5a2acfdb6ac5a7a21e31885e9e8753e99852355bafc85c37ef2ec2cf1942d96153f63a671899aff395b1b9197a7508bcb796bedf3224b669b2750f3ed9d80aaf41ea801a141c619d154ca19065f4d63282ac0230f4dc1a1b3aa488a64371a36af0173af037e815b2b8941256c8e8c6ec8879d9b4ed5fdfb6f1577303f3e47a876ce37a9acd885e88b29824db07b004c90f2be662e733caf5221877016d0b8a41dbec3cc5e819a5cc188dbaaa86f498f6af838598640d0e1abe29a68194511b17064e46c1bd8cb2ccd4101b3b818826ca629858e9f0ea1f1b0cb45fb38115f047cba8737aa84cc73d618aa4e8719368d21064e4fbc6ed4c2b55c615a506f015d077ba1c7e9a5fcf528720cbab5ec3ba4e7c9612073fd7244ad5cf71158a8d6daaf3623712f8d40a1b3c88107a1af9babc6822c4d330af910a005042980508002de5b57e3fc57aeb3b17b7e40701a4a4188927b97b288ce72cf158b20ccd2764d6a015a3166d27cda788a8d47bc84b425aeda3da8a0cd1328ac915e047efbd851d00fefde036cddfe146a0a06c48df7a2ba5ac251fda2e3f44373ca3580230e4703ac08292b034bcde614f6891772896d022d42b853dd02e4a27301a1024ee412bcd2c2ff5562045b1dfb8f6e81e8052eead4f2d2cf0dffcdfb46031059b222b851da30a011df208a11d99c47ae1915f97d021addd0c85fc758e9cc70c1506a4ff7d38e71cec42a7284db68ada6770c802ea4c7c0006116d228aa36fddeea050f30c1ec3cfd5ca7eeb94a4824ee0e09d165fd9fd9834a55182f5e9c2b33aa6ac98f4566386c7f5be62c0bea86c3fd8a97df3efadbc23d9d7a5fbebe757673d0f1dbe7df585f20a93a50cf897d8d7ee55264286c8be21eb413fa4b0044e63feb5a77f45ac102b30793c002dee86b7cd2dceccbb7806620e899aa0e7ca38ac66edb779eb6c876a067aeaf53cbc9a3f96adcdac313aa148ec2a0d0263dc73b7b5eab20ced3019fd9798fc1ec79ba63b486ba2dd527375e72be64117cd05a3f9103e548cc258c3d586d06a732cd16a139643c6601e1945a02c0de83c71a1831a1cdc1483ea3dabe2fbe80ca63d4edb3e3d9d24184e6b9839e3ad1ddfa58619c44722a00b1d03cfeb27b9d9c095dc0e5152646a54fa93c31bde53e4d7dc4c496e70ed35ebb6a2d5e40384d57abccffb9955ee25c3b5dc9c6596e7a8646caa6209396783caaaa547043ef5a73b59c1138869344b2c4bb1dcf253c78b3a573161eb024dd1b014152909a02b843e7ddaad17b1ebaa8a9ef19b372c6c25906098e10149dcce30b255f16b7a839793a694d86cd73c604b369e33c4a2cba63e80cec7d68ded68ac9da782572515e7f16049feb44741655de98e6f4d958f19bcfd84643b73060d825b2219c9aefa94d5680a3ac8dadd87d73c5fd6dd9d142ec2dd720a8d628853908066c0b87c575c657ae9abfb21220b0bd7b04c7ba5104f1005a50984451ff29333ebdf7a2b3dbef5a4d3af0c5989514626dc7c70850b41aa613cc2c4e54e467583cbf34b959454213886acdff891f36a16009d18e0f6a2fe38782960f7f8b0b770ecaee0a28093d1684d3a306aca01da703511a8b603d26f7c74881d58e65bd7daad7dd02420948f363b6335bda44cccb58f6993e46efdb8eb2b2d3501469cbdb04fcf055b8490f19ba1b576864d76c54d8edd1072572b4f1972bd58b765883fc21b38628482867a89742ed708641a4e130bb02e36a6695db1b7ab8a1b835108e6480cfe4037d5872e84fb9a5ed044d6eb91eaeb1412c93a02f65e27ce40ad556f47deda3c7ea1ce237ad039acc2b8122c9494980c7180a159f4cc5166ff55973763214875bb952309c67508c14120b301fca33c28ccd70f63711461c44bc14fddec17d13e23cb11860585e0ad5086b71c712cceaf6a9748c67a754283c5cde87941040a8fb919eff85dfd32b58d598dafd5cc09aed6a535299964bad4c75429d35a3eb03844893f6ebe1fbfe761ca53d96c890b818628cb6dcc6f66d02f28dd2313af9d4a606ca8ac8b338f096dc35cad4ae0678861a8ed3b3c682782b7cb1115d819e43df31d29ca7d3b8ea1f6c9a657676769840c15b9d12fb0063ff9100ff5f6657c39a264bd22a168f1a5f8a4424ec2c8a0ff3b43255a2628d11880ade64ad74eaed6d2c2277c02e9f00cfaac3ec7fc7a1acd62d3c2403e7a640fd44b6757649cf912d628200175e9de12dd448c6658f29410c6e15442f15f60d73a796171fc39cc0de84f10fb656bae30490d238b88a3057949d9113820f359da38f457b0cb22598519b13f1744cfa60a675a91b95c23e7a5684f16e6f613709ae10ecbc214edd2208f39359c1966b6af3e16e6a6f4f56ac9dce36f6a020138c1c23445b15a8ea7851071eaff936b4ec5e1b082f1fc9073e7203a70b89e0cf5c438e56f68c773e3c71097cf912b608bb933f376202668b6a2276430c436eac5659e66c823ce188d9f87636c92d4bdda638db373f9ec8b94daa672390caa915c6bc3e3d2d8f2af95b5ec8ab1ca3b666226d824e58345881ead41cf82e9161a5455897bcbbb67baa84375c36eb82249e914b50ddbc3fda30c3d204b010c86ae6a0406334405d1a12ddf3f966a203a2c270ae805f5fd3a3f41f63aadaf00b8782350d7b304035031206f9c83711768f201478fe301f630ac7d01fc975f5ae9375e45123619dd1a6fa9f743796341dd9d5e602e2bc8e06557cd2301e7519a7687553c4cce66f72d7f3d8eff8436ae255c066c5e0e64769adbfff546c0dd86ae423e900433daf42cb6deb8e0b12ec97027e0b9cf9c50cf485ea1145ee5a6e3b1645d2aee5372ec12a8e79102632157ddc6198a4ce24e56553ff84b7df0fa7d7e5bc369694468b2ff79aa3de3d7d23c548a111eba20726e9bc4ac80079c71c952afbf2d54e40ac19469020068e9f1311d7cf90e615837f914cc13ca383171dfb119d90e128d6078439615450729b1822b9f9637f98ba84fe68b3a2b7aafc5820e6f0d1d2794f0446deea48415a4753e669c51c29afcb4e1f59a267f53eefde249441e5b6c4d5e2972906160de45c4534cb327043d53db08e6ed625c2b05d0aa75f9e024c4e693760e989c0bdff68fa86a7a2e7c5b67a73353eb74a87ea77655ebbe175697d499ab1b9a03ac011cbdac1bda97d672eddcd6a7e61ed751c7d5f5e8b75d05affb37f475b853172bce6b616f48ece833eb314d33cd3dd046b9ab482eaef31563939e6c8c0cac2a43476c0c795c92b24729b4082e093a596854413dc59be4738ca611aecf3ae3c007ed380c3e24759b5ce1af675b7c0f26341d5994f5c83775c4fbc89c61558b4767a390302df00b5cb0c0052ea8374aa31474505df09e81526b3c0ecc6b23f916452da80c5362e5c19bb2e9e78345f2af3248f20cd67b4caccf47e7c6579eb306179c810c46f0f79ef250a5913ac7b065a61bc2d52a942ecbc69e14eea42ad229fbf3b66237dcd97d8cac6453169c3e2abf288e3bd95ae3ca17cdad1057c67a15105a0ebc1bb57a76f7f65032e64e856a25751d82c9c70848853190e76cb6ed01ecc2be86488d167d95ef7865330490ffb1d7db7f0b6b62116ae1932d3a54df5167b7a2f5d860c96caa02846e8cd6eb674aeae71985f40e6d02131a3dd289fe9e85ba6ccd96ac6b3818c91b9625a52a2720e41f223afde8e9fbe5b485208713ab43a866923d7e8d1523208774632a7a54587903fabba39888fbf905b5967df61cfa07b833ceb0e3b7b6c897a87812eab8d37c1364d0511feb9abc35c6cf44b86a694bcd17041111960e9f2e149d5017deabb070992f04d4f713b20958db8e90780757833f09db25b4222dda08dd9dd8d8579ed63eb960aa1a64d4dc70b10305ebbc0b1d47932a386f6bbbc9b4a7296bde916596f09bcc30cd3b42b493a16c762cbc8f952976861647c32ae95d0c6617b82eabcd4eb6564df7e915a89ca8db07638fcdcdf0f719307e9ec4358ed3d7a18e1984498b881ecd48b035cbc1aa46db03aba5c8908c3b1f820140caa6a570828fa9c6d05ee16324df2fc2df285cfa58859ac37c5841541be49fde694c162b612d7a213e336165b8aa2062cf40b99751e1c56157320a69f12716fb697ef3b576188872f3a23ed969ed86c1a890d18d6f5f275cc31d3b81fa9021fe26c38bac4686649975f2283ead3e99e0c81bf369d39862c37505e08859967a98b45087ea40d7c503777543226d59362ea83591ea95223588a75724a42b22fbd5593fdca555e8a3b48a64420f1d05affe4641791518a82d27c26d36054fe1e479555560d98c68b81ec7e9dc188a393714f5f895568cc54acb510a02108df81c64a1b0d00d662674a7f13d137617c98465c1252dd553eb6c9fdc1bcafcb06e7ce9478775fa9dd102d50df7e0e13334a8c01c91baa5ae8d4d734d9635b4d8175150cfcb80f0970b8e5c94bd80a93faad2e25fd8f47d01c95e78ba589853da8befab70556c62dc3a452361ed74b113ddfbd420184fa29f91a7371246dc31e8e6ec098aabba4342a57c1eb298f945dded189e91b3c75d8f481eecdc8718a36b80962281e20bf8a1d65e6ec1a5ed451309e74a7a164cf7b9042926d3ba75039cf2cacbf2303adbfd9cf474e99ac4dacc7f05c863ca84ab39d70b06e15847d69d74b12a381242b879e74fc3aa0c4e5783178693ee54a4cad10c5f4849c242ecb5873447790ac1508611844f68836d02a24404609942ce29d0f2cda87a867d6c7d781ca1f0370f615dec39399eab919cbd7c743eb55793e66e9b656537c36e735327252491fbebe8f7a2c8e532b8be583215ba4bc1549ad674ff56d27eae49ad3ab99b29efdfce743dd440b2ce31dacec72d96c1e9aaa57454c1431ddf97dd40dda43c7c56d3299232fdd8599021dacaccae87d168d463b4818445a17054f134baacd1b50bfa72be3ec67d64055aee3ec8c822e28762052b70ff41ac2a0cb8c4862bac675d562d80b45411c1d566936e1c749a9ae4cefa4f7804fddce9a50845d4feee357b1c9491664440f19f4ea5430631c55914a674ece593ef2b273d1ded14eae04318762a7fe7fda2c6d04b30b708a962d302975a0a8b1b2cc21b17fe925b47cc0bfe556cd4ea95e55e235ce75934b799f1eee7ce35c8f09f9d71e4ef47814aee0daa33c59723ec3570c0a2279eff46865ed23d5ea1d7f575a84a78db7cf09c15adb94cacb5b486f23ea002a2d323a1d22208e6b998453a9d060f195cd7dc37f4d1a643978a3a8db4b1a2d8455ef923cd83c89b3c02ad7b4816feaa4dff0ca506d6d2dc49216335e35f81248f12657fb65646d6a6cc04fd51accafdcd95d5e44e3ef7b75886bca08de66f9b0f6e1b5e3170c64f627750c68210810a6d15c8d8fb95a0af0f6132f423d9dc82a1d3ddf577548cb45864a58aa204a82f6ef8c5b69e73fd127ff00775eaba9ae00e6d569eb6889f22a490a3740d33b5e96e4cf5847404fa7d6d7fd5f9a1e93ea3b7cfc45fee3d2cffde987394a49c8005b5342fac1d7f8af286b2a0bdcf319f35113b3f49566e0ca8168b5c9c7bf53baf707636bdae01bfdf94ca42cb512fab01717f61b372701f6a01895e2665b13f7bff2388914e8813897bc41bdf8fbee1c47a4ea4da127bffa5845e19766ab758137397219ca434c77d899ff3313ab3f7f5aa072df7c0fc76fbcefa54d36d88b990ed03adac1225cea0b109748ae4aebad56acc83168a2365ef97aa36844bb8bbe2ec0ef0d6519604afab83b2f7e19a882e9dbefe93d30d2b231f4383f2990947c159b01cc16f36a955fdf0d2511aff0ba038f6ee9db8a5a83a137687c28348a0691bb2b4e8fbac450b8e025faeb03f2b5c4f7f6131cb01dd9ee4512ffe1585ef2628bf2b7b1911a5e30a8338add4f04602bce6949db257dc6efe7ba11887aa2b321b3c355b35b8dbc954322e25f3b87a2ea5ee8ee8a13077f662a14505ae6f707ea0bd08962fb8065f7003298ef5b3b0f0080d2bc515a6cf7ed47948c61cfda85fff4d3bf4f9baf3f04c8fcba03d6f01049481e81e049cf6816b97052165a90d42750e5a6ff2d8f3221963744e7f95b281acbbbcbcaf2db0ca1716c08e4a43f338d3d61248901574cfcd37dde0f8cb17d3daf638a8cf0419a0dae251d70af90eac2eca9b4eadba6bd58d5793129ff305bc491e41085be47aedcdc01623497e72b967fd3d35a9c7b5e267e4fc488fda1f986ee5ec29eff5d9c804594af82ff3cf900046c06fe099ce8ff1bf9fd48019154bd79c3c4cc88650475bb76c85b94733d2015e43e1b3e14898a7d7202d6f047d1d9ea3592b12044df3a160a2abb7a6a5336fc6ef4be79383e082095c11ce3f83983ebc846e738573d5cee53d1bc905647aded8ed4ec38384e70d71bf1950be84abb6378aff14ce38cb6baae4bd8770e494f71e829143cb879901173b0aaf18180093524c6e88b07153c2c72a72a56624754ba33d270f204a3b19c67c1395365931038fa8915f282529b671fb40942a5c3b66508222a89c48ade79e5052dcbcf72ba6cf5e68af6aa76725f7e007409c2a7fd0c766decff13db474cfd3743356fa67453f41510f9935eaba64d2727b7bb6f3c699c156ba45df4f1485d1153ac49f5d130df31ab82608e8b07af0a647924a36f88249010a35c921709c9cbdd9e8d992b96e8d161ebd1c9920a7b0832a00cf26578a6a8c926b6976f97a289dd9adbf05ee0d496e8b177984554c2b4b0115087d69227966587c086b5178bedbf9b055001429ab80fbd17b00d40a2044a9040416bb065b868cab16ea3929655a61a0c6669990cdbec401a88f35647813eb9b2ff7fa488680936008344f0b8cc2dc14bde9cd26c4e6fedb4741bb8e637ce28d679b19f232dbe35b757cfaaa4982d13a03b51947e19d7cce1da778cb71b759173f0f74fcd4a18a0b84549dcf6329d131bd7cf84fb8a559e6e203807eeb680058dc3c8ec600b19d122605168929cd63f59ecab7b90af3f78c42a2bbf965afe9eec6eab0f856149d9d756443552363a326832d97c05e6f5ee80188f5f8c64637f822ea0f19b4b977d62f22db6959b7f98dbfc16d9559adc67c4e2df1993ee2e031d17b6f05f2bfc1ea6c8ac1e184528112316ecce59508c4f2c61911df06162c76d64eb66182f500e91f2df3941c18b52a4d645578ad6356478ad41dd7594bbaab73d5a0e3e8719e29b8df2c5759abc6b18bfaa3f91646983ea4822f86640abb2ac22022943b2837f7135b6d4c0d6794c96547fbf52573f2497f0d6f6285f2510c46a9f47baff5875d092f51727e7722a07d06040921d813b28c791a336b65100442583a1a0cb4e57dcbc2c9ed2061a3aacc251c1e324df52c8e357a986400c736f871d91c335a03dd336c2b77ebb9782e96fe4ba5491ae81fbd3f698302844b179a1ff984f46c00c99caf3ae9aef84c7c1ec54159bae578c989cf9e7d5dc610f0a97bd6819bf3284981f21c378a81a27994690cc67fff019be1417d5fa451a7a3075a980c94d612930303b145c2be66466d6bed4d75f0480ef5e9a15c9165bd3b4ef1d9382d65f3563315c6b4d77984531398d276619d43a61d88f0a0839b0fbb90ea4bf72638d31665659e03e46ba765645a893f204f0632f421376e37b2403e2ab1777141ee832759326ebb7704d89581ebaff79759c45f8afee0215fb9a90942816225f04b54e056e1b98542872e79b2522e2362b06aae888128d8b796e2bf9017b7919dd3f4e2f6dbbd1632b601c6fff06d74db2ba99d687376904331908e61f7d4cf59b8a48d5e844913bdce6b6c0f64718c32df5e6d9411c005a0f010596e6754a1e1a83acb23f2c578a8130089d5684cb94cf0c2c01db7195ea1a3213528f9f15947f791b18ca999e5634a1c81324b63f1a0d3b26817a4278118e39e7a6b66875803ba2a564e1224b921cb0cda181f643117028d8f98ca6797b9a86786ab099baffd5d06479c6e9996eba125c6500b6f4693d8a0488518b586558448e4df067840d902a85467e963b8dee44e4f8a1654a845cb7c5464c996a2f9357f0911b3db4dbee59997f52637b2645467e7eb4e1169ec84623a9f1e53abb51116d763e68c37ec4a309362973185d19d7c1c7d89147713bbd6b0f767d284a499bacdc01ccc42c5d898241836d5489ac90273e12e99f1b658ee4898028d098d4561a3adce816352dd95b9e438ba2b58240079897f138b1c2abcbc48887df935e6dc51174f675fcac87068141bb80197b7b810125a8a8939c64bae0cd1d1360f54510c2413d0c2fa2805e6ee78d9e3b8cd0bf4756e70941de742111ff194618f0858ba5a7a3cdf1ce3f958d5ce848b8e6df7962d8b52d658b721b26527cb6d4b8f24eed8b34f072aee97d766e2e9f8e824dc86353108d9b480802e18d48ed9c0dc64c5a9af8d5c3245d184ed0e18991a110c1ed082fd817773d921a62cea898e91dbe54605bd96f819f960f74bd46b081edb9f61f8a371befbeaaa9488e0a1beef96b427e3066c449af573d1c149407b3957d57980d35c2da1e7222cae4df0d554d3781d6b9ed91e9d6e61e7974b149b0e37b2e51b61d099620d9f2c02bad8bfb442449186583ad1afc0505314699f50d678613d30459301ec058a270cc556eaae885b5b691555755546b51f089cc36f79fa3addb115b749109f45a046bb1a324298b6a00209583995028940d4c3077801607433958d654b1036f0c6902e1a62d6b9f85fe78f7515786f7a1837754b70b722f9eec283685a2856da9e42ba473b12d0625acde91595e039450141e9f1edbf4513998c8ed9fae32384a951bf276140b459219e9d02b2e2094991c4f7ed4b96c85dcb8d67cab33dd28430e93a60a49eae9a7a9e766fd1f84216cb0df544e49103ba7dbe825e3090325207a6a24652417fcd0bdca9bec93430bda6fcad498a95c43dcbbc0a0fd89e74733c9d5cb22f15c6c6982ac67a72d300a000142e1735488958b40211c06825e041a65262ebb63aa97ba91166e007d96682d0de25646f29a54c52064c081108d8077b5c99c63feda6b48ef4237d69244f10767d5c49b1f1c3a036f706d4c4306c7f10c7b0b5d4a0e2f01fb263b69fb67cb11d1fb9bd3097974482240f6a529b4a7b7e57b93c571a8b51653407db200483c0b158412860f5cfd3a63cf02ef5a03615046ad63f1de786dad4a736758cbc7128c9048e50e8f3c6f667a1def0d8183bdcf72975a9329ae3670dd7cea175872d4d28a20a35e92e4d28424771d4bce9e8e3eea88d75db2d080bae3238e8fae16c845af481dbaebb7e0322d082c7288c5eb9bca20fe8f6497b62e0b608758775b3d6bfa904a83bfc75ecaac33e79b0b4e08c5238fd445e5c79adaed8a0a4264442a9e5604267ab89ff05fb1b7f029ce0cfe3160de9f065ec419ed8fe29918470dff2ddf4d50c8fbe2bda45187aad6f7fa4945aafd4ab0d63c9b52c27babe0f31445d4285506b2db6d66f89abdb8bb383f7bec0debf165f9bb35cbb04e76fc9672d687fc80292ea679be50816f3fdaac520b697c907929890ee2d7d2068fb6ca55aeb83a34e25754114d25aedbd388b3380d0f4431024914aa53014c5eabb4d5f87b3179f5629d5b55e55aad2bd943c456b71a8f32ad43a60e6ebbc91b1eaaaac4ae52aaaaae38ba65a6b133551eb7276c0641a75660ca14f133563087d0386c2c0c0c0d030a6c6c4c4c4d4d710fac2c058181818878181a9a2cb0964fb6985d271c34cdda854ee30ee241a850725cf94088e2eab1479c266e8cccccc0c955165c89021a3badce572b97c869d3163c60cfbf7ffff6697b3037b66a6541aa96ad4a115c66fa85ae9a8461f1f8c18b3929161f9cb8a45b6c8197a4d335ee260f6cbf7cbab9446a9c494bbb5a5c5511b7f77ff36c6511bd52802f5188d67445194e11281309ea1f56b4d6aecff8202ed7e31d662b97293fa4dcd0042fb7b097e9906beadd6a8d33275648832a4d02b33f4bdf56a65ddc1ab2f91cb05d2195eaf0c19325c2ed78c193344d8a6ff224992ba2c4b1a33d05e9ee27677777777777777771f2f5a682fff9f24c9b2a44183c6ebf5a2a1a1a941966559d21069d000af9b26b5a1ef618d1a35461fce81e86e6e1a2f7dc3aca9a9b161c3067843dbd8d88036c4b083d7cc4a87eda0031b1b1c38ac55a1e66ed8cc2042530b7358c536a2cda81302407477778b2fc6e5b538448a85f61206f3c0830f3ec89163d4d1513f9713c8c601136710a14f7106113870e048e1c081c281a3d6b8d703e042fabaf9d20bfb1078e41e945a9c989ad93d7a7ca26b076d41cff7566bb38335901e842b509cbc57a038d8afb5575ce1650acd8205e80a14a76efab2c7d08648491791e756f29b19ec87e9c5d67e26596c2fb6a10bb4f65e4b7ad0da7b2d75d1b40de92dc1a0c610e471ce39e79cad2cb1b3077272ce3967725f18746016fb36d9fe05a843636cd954894d89c608628775ac3fc293ca43d5a1f793a54ad95e81e041099595deff8795795d4223a043969f2d665650d96eeb96318711fcde4a2291de07dda407b241f2044f1776fdc00d3e251a43c90ebddaff4820bb922dd89d4bd2d2107a0b56d4fd383ab272f4e3c84a787361dbc12a04115784186765d4967ecb1dc541854014022e84dc1e004f08b810725a84a7bbfbf8cc67db6f3ec3e23e2b10bce5a9c24ad4798f78245a118fc4a33d16f11ef1483cea85baa07d9f0a3580e238a906d24842241e484224a14daa02d2484224a12d5b2c5fae93d76d5865fbe77bedcd38b48e6dc5d65a7cafbbfd4bdaf71beab638db3dfcdeebd7babbf5fb5b803183decd06af28427d73baa934a8fe6e6b60efcbe8621a61e499987f6e3209f479737343733621c0154a26c855f2551dc829148b24d3d0ded708a65439bab22f7ef180b30cfaa43c5cee13f2f17c783cde0a2bf078bc15783ccaa38992898c743bde1e81e2dc374352a906084ae12b0495640a451a1d51eecaee416beaa766ec0b8b2394d1e50584cb0b182298e299e979fd8b7ead404740023f4c3f78d046b113b4a6117e50268c4e344dcedcdbf67b27f1cbb9f7e51a641087aa21a44fca03792bbc7ea6f6f07837b0d30d19c4187a010d6100218613274e98cc006ed9228444122d608f9f9081b60657bbd85efb5fce47b6ffc4b65fcb5353d9f63f2bb6f5c7b6acf78215db0fafadb5da1e4617d7ea98bc98de8c367dd2b373a18f3a18e7efe13d8cdcc4375faf0f03671a046b78e0c87d1bf4b706594bb3b53d73bbdd7af4b865ac833ee94d54c110438020d86c3ef8005ad95de9ea10f8a221bd221e2e76f8425f21b5a2bfcdc28d1de5e8d57516aafb5f6da2b21fa4dda9b7b5a2417d437114406decdb0781ca16506bacd12a06262bdbdefcd8e9b74de89ad81ba4a9b5d5bfd2f010210b94da501cf57d5ffcdd7bedfdcf664a29a5f4fb9efa83abfa325d21d0f4e9574102bd5842a74b9b160531028c20434f90c092141530619a42bf34a9a2439b298bd634220dfea09ad098062854d0a353410c4e74b869910d8c15cc000b2c5222d0a44d8b6c556488a288cd09120816d99e8020088260945dea0b5b939b2d88cf67a5c56c070fc81045ab362d8a414b119af72fd3c8100cb722b078a1c74d8b8ab0925ab1983c014448089cc0010a1323447cd1c5c5810950b36ad841c70cf1c8d400c40b4c13deaa86279f6ac92ca606dc4b6ac9104c0d2f784135c9a96ac8023cd15235ccc03484aa618c926692238189d149cb30be18b133d9607219c26924a393dccb902822f1e312050c91301a894a4e78a20d2f20dd76a10dbb11ecb125d0062f4a240ced6c18233412c1cd4809502434b119f1c144c2113dd508122f55af8ca0d91a0a1a69e28504247a46a800493062c89fc616983b131e488193a01e0d8844c9c938cbce5ac8200f4581d16c5a34a4c376d9b468688814580c8e7a4214c68f8f8eb1f184b144aa041a65c5a9091da3a50623805d25ab9b4d4f1e38d7ac4d896cc06475bb10e3a4050b235014edb4c94911142768d5342eb67515a18b68db9a76e3116f7ff461ee5bb2744db40c28763efca1101efb4a24316f7f67c863f8f54602f3f677a2784cc411796c8ad14e959d238fbd94ad5c2251bd69cbb6262da62ba69d86d27353205dc22807a567db3f9d4e32466474d8aa950c1214cac7ddd547476decb3743413cf6336b67d931697fd6cb64fd34e8f40e7ee2ea5c56322500f684b108f893d1e43f90415f158ebbabc7d14c9ba42b2766e6692a5c3892daebcfd1d4fbd7cfe4bb2aeb84b79a6b68e9cdea1edd8766eb40ada4ecd63635964db11f2187e9dda2dd4ad43ab70922acf479527ad22cab6b40a229d9b47be03aa0d519cf05f6e7eee86a9230b16b8312db29d7458494223090df3aa87a9ca560a2b0ec4c43c909c160aaa1760ca560a3039aa0792533face30fd5c33c1d538829612b185245a2523d46f09f9f23457ae07682dcb49f1a7576686edaaf752768a7564375c20f2b53fdbd2a84b7a05d51b55d1f45dbf57768e09f50beada928c4fcea57e50b314072bc3ccd5d8d807427b8401f99766eb2748eb15f9e1327648832029d157c751e3b4d3bd691dd77876669152a11f498b9ebc3a0626274453d6bd479793cbe0e2bdac5013d4a651fa5b2d4532a437d0f1b92e70c914c9127b83315dbb67f479b0a98a7e32f6405cae91daec7c4542b99ac8362fede181246755b5826b35aa206d2e737dbef7f8eb85d7b76f51608d1366d010f5b726cda02255cec96cb895777779ffcd547977d8e761728670434a526f9a93b41be43a338778badd6a85381726ec2ec2b3490d18c0c17a35422bc5dfd0e9161ba600e50457363fc7ad4d1f185c69f7ff481ff8e08b82302549f69beb9e69cb3cfb89c19d8f791f8f5814bd0e59a31ea549fa34a6be92efe30a2d7cac2dda68e2fb48f5b0aed0a642351fd2d479a9bf57a8def66cca8a26b059aa50382018bb869bf1c757ee6a67dcbdabdecd3b483c2f3b2edafc0b34290157a3c56b75de99e9599edac4c66666b8cac2ce6fdb1ceca60de63b0248ced7f7b56f5eee16db3745cfcd4da92182cd9b7acb521d79096f9f198cdb62f73c4653e576482f4fbf06c8daaac3e47acddf6f24909f4b665ab1779ac1dc9523d65e9dcdddd64ed606aec67152659476eda5b56a0dc0b133fdbf675fe6e569f231f9db93d86d4b666f5332bbbff32ab950ccda883eae12558a08eb131a0b7e9471f16b0638ce152471f2ee37f8fdf5317144da97d5fbb99ff561f9f23232239a3497124dde555c5d0ce6300d8f659575c5681722c2ddb7ee82c13e8730c725e298e636dd731688fb497e8b16fd7d187eba64fea05affa1c79ac05d3e126c5a1b00aa4dbf6665b4abd78d1d4802107438ea686b57687666531a5e6c0b54427f9a138f78774b6c6c6053db24dbb9ae4869a8496a4c685ffcecb33bc61fbdb325f9c0bc1cfb67fc99fb08612a23895daf89be208c3aea30cbb8e43760d43ecedfa1efb0e745c48e821385d8ec7c04d6d9ee65a9dc45fef0a8b3ee917b896082708f4501bfb4e923b4709f4597d74748b0c43a46cfb75088ee2a09e6ed9f58e5c566118b235f6fd537f561f2d5b7c172b300c09b1eb5b27bb7e856168db28bb7eaa3c2b0cb86d5fbc4186dc3e3fba65f7d9dcaca832ef4c6f5fe000e0291713fd73e4a99fed22151a55c962ca92025551953ebd231f9d9b51b4ea61fea43a05f0e814b0ab3e3a2a3cdb7a0c45a4a6e348d37d56a0dccc0cf449a19881b6068ad9a61e336531262c541653eed46c8d7d180d4659056d8cbd43731d51056ddbaf40398a53daf6593a2bb32cddc9d26d7b7eb65d7535d8a8036e0f461d5005f31468074356209dadb1af222b9091ad09758875f4714bd2ebb0a20d300eb9597f8ce272c273cf9efd753c8fa11e488722e97607ca798ca43b65e4b17ce4b1d42e4556a02915a84a053272b3021db909b4ab40392374287ecc9b5ebf3ca53bf2bb602cdb7ffc2c83ed8fb7d8fe2bd23f86f4579152b67feec9318af06d5c7a79defffe449a484dbe90e7bf6a6cb13cb1947dd21b797eb35dbf449ea34d05186edd5284d3cfae7f7eb60fe70f24956e28da132aa5828959c9b05a33325c3ee3c992c6abd2d4e0c0acb1d1c10d1b00e0a03097eaa3e379ecac40b9ead3b3329c1c0f0f0f0c393a8f519f1def8ac77cb478acf2788c16f9f47c66f8c9d519826c5b99d8679d61761b4114fbac331c01ca55a0291ec334d836c9adeed083db1e39cfb847ae87d12d9321e042c0b90bee1fc618e310723433095d4e1c6377d1ddd5449fe0adb55a960e2bf468dbf541f7b18416ea46cdca121cda758cb2ab639142eb3ff104152a565881719887b5948ca2e06f7b89711f2851caaf6714073fede5f2d443ea8cdad8c721c647251cc509c1a31d8643614df3c208e9ebec80f7a5be32ac798dfd1ad6852ecad8f69411d65ccf20659ffa4a6ddb9f8135748f54b92bdb3e26551f28a17d66877514e1b46970d4c6fe074968fc1dcc284efd2a6ed867abb76d07b31c3768fff375dbbeee8f8e6870a72ab7ed5f5ca35663a8466d9f76073c2342facae16404fa5cadcc5babfd99bf01bbe9418013506800dd3a9cd8429f58f7bd153f92cb7f9f49f19263a923a74facd3f1324f12fac4ba7de4b173b5f3d889ab605dfeb03c53fbc4f74be589b76c5beac0f2cc41b6bd4fb3cfaee2b1bc9aa147fbbe0db77d5616faad9518569754d3a7ded80aa694527aadb5368f3a3f56617f83e06a754b6a2d058f7c5fad3b28c1f6fb6add41c957bf9c960f2495beaf5622b552582285e2f7d54a44a463257fcc35067b7b14b9d8922f2ac325cc7564f15bc96890447172988619643842a44ffdb9e62a4042ced5ae2420f409427112f8f47b509cefe97300236548f84fbb71f9043f89026e6144a5ecd8f403fa6d4bfffdd2bb607affd6014e9ffa1450af7a973f6badd6ac4c55ba606bfc05f5e189a4392892e684d44dba65229d3ed326937ea70909e1fe1ec5df8982581b9091c7288dc811dbdf44d24ea4c8f6f7223a2dfd29fda8079243a2b9e93d5880d91aff5379636b9ee6b8cdd69c4aaf7900fc698e07f9ac65446b5304fa96a4b3e2ced53e6bed0422c476e114a24aa7d1d2850843e3a7a412ac3f2a50a29dcb96ad5585dcf4afb9e9df22a27fbb0e689f8e829f404aa24f9a43f168474511d2d7699aedfce1ab47bff46854fed37a4071e4a77f002aa3d403aa6d1427c7ce0fa338dfce5bec6c4596429fec0c820e180758e8aa4d8f9d2f2f8ff9b77250b8dfb2e50924d88dadc9ff91a69bf96df982ed5c29ceca34ab6ebf08a995511dd2d7295231fc7b2ffda1046b4235212374064171832dec1509e2b641890d4a7c28c424fda19d795def58c20536e94be4296ed204483a0545bb0945838e25a4b5572a8160f84273e8695eb7d8332ebdf657fa1cbe25bf27e5b7af4915d8df7f4fbf1c20b903d79d0a2c802001cf56adb5466efaea4d0a8651e821fa869d4f907bac0c2c29ada9df43734f8e9babdc137ea41697d0b5d481e0bfce02f65cf9675bf630f36cbfb55ef086a05b6b85f6796357938308f8724029f4898d76a5426bfcadd05be02c8e04bf91956110d47a6574710e0137079799c7e7f236486fbd29c8a59b8dbf9473d2dbdfdd0cea0eba2f6f9b86f4a94baf3d76a341ed02d32fa4cb0dcbb735a5c73aa5af2adc94a00122415278360645b86fa94862206f5c4e80de1ec2eb24d7421281f4296eb7a4ff4ccda2eea0bb6ad92626fad4f63589a400b9bf3fbf92247e6f2959cbf3fb48ba417b4378effdb1a31ad421e076efbd9706489fd4685b5dad43b3baa3967509c561df96b5d684c8b665fdf15aa321aab56a493bbf3563af3f75472d25406fc581a6cf7afb7eecedc24c2bab3ce2036ccd7d0daef4fd616b2e127dd69bf81db9ac005e73ff0a4db9b2effd6c56763fda0f9937186edf7b8df6fd74fbdef26e7a795084a16b075da76c9af320ac958ae605c2d0e5449fafdd44535ae3729212ddf4ead45a7a03d7cd7573dd5c37d7cd7573dd5c37d7cd7573dd5cb75a6badd5d64a29a594d22db0b5d65a6b79b8895ff6cdc620d01a7c436d30286abb9a7159e9f1bf3cf672932491da9ae48d9a73ced9b1cdb95cb98979dcc42d58ab4278cb3d41dcc49927f3f06c8ccbb3bb8290de5b5f98980185a443e88aa26de88f97b4802dbd14e1b4ef4e8490bece1dba9ddf52faf7de7befa5f75a6badb556948562898dc2325b71df7baf8cb6f39f62ecc80b89648add406b72ecc7c6121a3605666465e1e787e960472e133f3fec8ac76057dccc9f5f2461470e33b2301e4c8b9bd9ddddc28cdca4f77bf88b97309d9b39079b4213c5cd7b2f0d8e268a0d9c3e69686e345168701eab3b976ef75e7ac5bf7ffff64a7fef2892d4d6dc0fcfd26de79df3b3c2d83983ac25370c550e1b5959ebc76bf267ddfad91983276bc9ce5fa545fa73c60d6cd4a84da6b955a1b9b999ff0325f41780216a93ff8324740732ece0a138fef93bd8edfca5f2bc5bc8b0f3d9c1cecb1d3cdf9fb0dcce0fcbc1a61cedd2e70fc010c509dd468de2903e3fcd6de70773ceb1dacea53f633f3b3fa9a64f706752499ef647eed0b9d4245fca479d93b5a3cec9ea6c636d3a9260238b46bdb88ce127863d4f9de088fa10f51fea4554b699743da5dc7e58ddb5027d350e1ae6635a5bf5f8ed8ec446fe3b34f7ac22c19bf3a8137e7e9713adbbcfc318638ceff75fc7ffc8f39ba98079df726c42909b26d0dcb4e3f89574d1a7a70a6d89d51369e1cbe8f2e358ed98e3891669a8f2dcc06ca3dee544f34ecc73cf99971245318bd9e78acf4e4b6fac3e5674f8a748bb2a609e094126d45e483dbad0b67d138a6aae1d4c3b6a639d6c3bb4833e65662e9cfe175a29ac1ee6614a0eb4fc572fa38279a195828a032d2f91b05ef531af7a9a03132343561f5600ec48cb912d122a4fab84cab3ebf64d8389d620a857abbf30d712da59e44ecd03605f06019e0312d93ce642780cb37431774b2e05985795d567676b5250fd4a668689d8dc2422849b7687a6f397d3a3f18ce2d8f699b209c9cca2f85cb93243ac90294263d5e7889ad6b44b1b27d0a76977ff44e1b6adba7dae76aa4a714e3bdf6d6efa334d80bbf25c4ae893a533ed4c57bcc65691e6af446a14e712a1819a7686db35ad847e1a646617d50ac2d2b1742c1d4b7753e4897395c8d0d90ab22d912495c86ddbcf97b563f13c76595a58bd168ffd5690564fcba755c463a9d64feb482bc863ad0b34c528572357a3e745eba4300a65f2a026a630c6d5839a139d3d7b8c7bbe37ef644f619407b50e8cb67d2b1af515a84671dc666530f5e680cb419777ed3b9d469d70d314119bf8397b2944a7a2ecd03c669becd0eae803b5eb101c0e27ce199db48a28588aeec4ba7dd61c74dbfaad5e6f6da5b0a71c3b908eee29a870d433d38ec76ddc322e2bd07467fba8f70f51a4cc8f4c111f7ddf6566324e43cfe104ef78a9ef85413c36f6f814f1d828ce70a4ce10e4b116b62f923b42e10e6d0b21f1ad7e115d523f86a7173f45ee0851374deebb02f14e1eb76dab780cf5faa53c531be5529e15e8ca589e1f966705d2b22deac5f2ac405b76683a8fe1a73976a7e6260a45ea94ae55a01c2ebf7c79dc62bc1dc3736269c1c2c20223f5a851c725f595ece1ae24b4c8da91992d61e4b2edcbccec0b11975478dbe3a7bec8ec963e5a6c5bdaeea3a33e57509909342f0a2609f6d65a4b223d1541f08a9f982deee25ad15a2bfe0d631194d2b1883a2e6175a1eb5f4ae9856daa0a816f5adaf4b429fd7b161dd1760b5e37def553332f11db9b022fc662fe28a55483a452fe446b2dd5f7de7b29a525916492a9e24dcdbc4471d4b1a538d6b3e5dbb431c6263b0ec15834d028d4ad621436a1eb62124f17e78c743be76961c686132aa542046abb940a673b83c003dccc329b59933ea954aa0b133313f6ac4f1d7fe07030312f2753cc0aa7050e8763c1aec7c5155340d1dbf576bd5deded7a4c7a2b1956bed8f6f008d95e47b83d749904559c8705d68709ae35c3e3a6763323a3148a25ec908f33aae0bd6196a830758c302a1ce7b89309e59ae1eeee331e870587c3e15890693d2eae98028a1ead47ebd17ab45e96de93382c381c0ec702177a5c5c310514bd5eafd7ebf57abd2c3db2bcaad42d69dc1c0e87f3b7e16b4fe335e3e6eaf51a75f0bf2e4d8d8b2d09fc72cffa806455812672d3eb14eb537754291487ff51af8af3b0c00ac165c1d1a06054b61942c4009d9af87e16ebae5589c537e7312ece17831fbe994422914afc138a2fb52ec9f87edff75d9c67172fa95549ce39bf2a0d35714d02b625a9896918e09273ce58b060b93f4a6a557231be24f0cb405c4c02bf8cc51583e501bfa7914c43325e646c923e74ecb482d605dca41301dcecb19b796d93ced6b80c18aeb33a5bea74b92496461f4ef57612f62a206ef2138a2ff7852b8e45bc0f954251f4a1b0e4b8c9e822be8438f86b3561fa65e0aac260bbc93496f0b2337611e238afa24d251d3098e9f494b44d6248e280db67a85949b83f3fff807d7fa778b5d69a52adc0e3f178aa4a65f50709f3c995cc4f0946b0b25259714d6a105549051a419cf342181a53a9b5d6fa1bd1a6d15c89322f33eadc9761b5eabd31715ea5e2700d82e09120f72a7547bdc1c96ae76060cbfac363a54e28175bf2cc48023e785715d40f9ae00b15374522080e810f92273e478624a98190d9597b4aa8d842ac500a6ae881061f94e8b959a15ddfdaecd00f43f6696f3e39a725b239d59fd6c856b1bb1f8ef66979560bcc9f760bcfaeb7c767d7bfb3fb6343c13e2fed06ed7a6dbbfe7987ae931fb06c19995f8d3a32ab8f197556313fe4b0631e66d4898179d5a803a37adac4567d6ad451a502804a027563a3503d106d940f306c540f347a5424d4e4eee0c2dce21e537bfc3bf240c51e950c51c4434f2d2a4ad283a6425bf0da2412922c9b54630249d103a008090df9f1a893414841131b833665b36d5017db16a1e0886d534064dba743a60c10998a5202567fa56f502d9739e713740e62e7ff2bb918ac288981a5282915fbbcb18307f6dde1887d5f03fdecfb2b5a04c464dfa22023fbbe1e756e4d13b5169d60c9cef95f469d6c83c7459a92293b3f38c6ceef32ea647d7327dca4d061071bfca04382022fa4f0417c6ab3d2f2620d6b9f33263042071af62dd261b72f6dc8be3ffee78c23d65214260822072af6fdf0899b5338fb9cf162e7bff908d1ceaf8b7200ca363e40a194000436250822afdeabc430836cdf079dc00104fbfea7a5100125ba81c9be354536b6cfd4ce57ecfc9906538a6e08b273cd39d32e827a18e20a1229441ec8c95a76ce39e79c4fd03f3becfb64df209a0a233bbf8e1abb023082059844550be9ebb4bc7d29ed41da2fb2d4295596b4573b6b0de751fce6de13aad9845a506a96c74dbd7516da77cbfff42dfba60792d3f24f7dcb5a5ba2907a54a557d8aa10116183219838caf1717bc9810bf459bfd63c768a4236213c565b0748fde95348952d7cfa145ab83c41f5a8a739a9ff6a2ad267a489f4590b7fea5bf951f84fdfca65eb00a7d39b3ee712f5a97fc1f4a70792c3cae96aab42291245ae48176c0ff3655359a5425db0b25aa34fa49c7a97c25383a0d6ab1eee51b9350995599bfeb03ca9ae549e940aa93ce913607952295f79d227b93c41c0e56992a67dd69a267d466deefb136bb3321f4229d5bd94a74542c5a53ccd27a4ac2c08a5929aa4f2b42f205fc667a0977ed3592fed919bf73998a2dd2d6f7497293ed2a90225a2a34e09044190047e89f414a0447b02e067cb83c7a9e3f59a9949a5ec0d2abb215299486b6cac7eacf4f4edae64b616342905a969bf528b6db9ba7ff72d530fbbe16aadb5d65a6badd569a55ea97984aeef454cf19283299a7e85e930cd92a83dba4475944af8277d824aa14fc03f41d8d4b4260fa8ac46c0ca3ca0356e75d58819d25dc97720d4256876d7126654a7703025a713ebc5e1bfbc9a85e26831f1ae6d5fa17d6bd4e6f6dcbc5fb5f02a8fd6a35b73754a3562dd3fe17f95737feb5af9ea802598e4235397777179ea7b7f750a6236bb623bd9b687db3e31c563f67ba88ddfa6e4a0e89aab46358a9bb87a9381ca60b63a44717a288e13f4041476c3109e5fddea2d9f35f7e59acbb99c29477172d41faa4d9bb8e2886dcbb3eab6bfa88d3fcccaea13b4c6ff660beac4f69a0b43fbb06f98e6c90656e1dac6c35f64f7493a85126d37b9834768ff13f6a6997bdd622544a3e0af6f1427c7be4f00978132d267bdaf066d7d7f5ab250f7b9da970023b8a957d4e65af308ad539746c46c467368289eed5f7fb6a5449652fc97b4807df5e850f451b70e5d54b47ddfe1d75d77585b7555dcf47a6b6e9ade82a26911ff99a2ed5723d2fb579dc7783cd622a1fe07bb3e90edef413c26b3fdbde747a592e35bb4b8d1151ca6a8b95fd57ce631d1548260ca4d56165a0a116cfb33bf4aa257429c4671824e348a93633bca96a7dbb6dfab89f4e97ced42edb1b686f6c22dbd664b0fa2bdf0491880eff8fda3d9da4aeb4b89f6579e404012cdca44afb1f6dddaf0f16842f8b9f4211dc7f20422c41ec956a16aa8644df74539eb188900004010003315002028140c078482d1683049e33c997b14800c74923e7a5a361546931cc7711c0421638c21861843083088c80c0d1151100010d037a6ac0e00ce389c80d8af25de48408dd6d93cca2448dc2f1af12387edc5ef618981a2d6c2ae7dfeea3b32496cb4635c554306c80819734bc9114c4aadcc3a6c24c1008e25b40785d895674c135a1c721d41311a4623f008f6fc4155bf74663de7436d0a91e398501378dd30a857cebd422cb5631338cd198812e6326737afbbd611a9ea2266258209893be30815bb9b2569d87e03236a9508254824964f4092a4aa9bb83aaac4bace1fc8855338374392035617ad1c125d1be2fb6a62b956ab668b0285e43602dcd45e557030620fcdc0b7cc86402dee2d2fc7d92f0f92698929aedcd96cdaea79de14311d3a88e1d0db86160e560a6492882bda1bf0fb6e2880b8a14e754c84d4a3ba5bfcc83984c3e64224be8009c3b865d14b18c6a75c03c0e851f811597cf4e2f387d6cede61d3c97c656ed5c92dfb65da67b56ced785ae4f49ef60f9195ddbb09626bce2c44047f613c544c83f9227b9202e8ed3eef8d9b84464786aa458372b90c44a8749d7a70463f4f5918e04b839d6a7fca8f214315c001ab8c5d47928fe090049ab9e32977f11244b097da229e629f930342bfe6aab6b87c591205b8ecfa5b8b1954865a52a66c907ebc0430c3fa2fe4f21e9ac0428f0d66604f2bc6880162e3cdbff6d3de962db99ccf8af4de40ff5899a1d35f1ff0d022abfe780ff99b23bc748d46ffccfb99d27345debe6811cc2c65de1022ff995bb509233e5904692f1cf7863bdbb5bddd703c75196cd3c89e132b15938a90b1b2029b32b5315f8cf196c5547da0cd25671b860a220fe5e7e725a79c681b3fdfba832669f61b15f3b7a4f2d089f52d62dbc9bc78707727456e0393b71cca52620d77b5a8e4eabc7dec75481ce3afc88568f7c965dd6b1ad5496bfe3d5d06b93bc425a2cbb3a939bb965f38d0bca3d113f8c76bed7b5015fedd5e5f25955520a749e58a7c92410205f961f8c4e588fb32e530dbc3958533cee19d8440e77c6c0ae7cecb9a72618040ca0daa5b1aa0966066ec981b229a0ccefc1447e7a2be327abf7c1f3c06e0ae223be7162d2c4c9c00a27c2c5a41e73669c6df74a14be2c0e8f054a35b0ced6c8c7430db7524290ae7e6181d4b2544109219b8bac805bc91e180630f47744983b36c2e22b7af12a68802451675175cd30be47c11284c21fe8270e9e4682b9ebe373d30b872ec1777ee09e497fe4d11344e658444a33365b9a95cee3783fd6e39b7e80507192d6c975c7139b347a3a519c72ceb9a62da3edbe35843c0a0b96158b495b1e30e52b5457d5593a120d2fbee612a4606226b8dc7d4b511cc9894bdc51359a15bf5c55ab71d29b51bc90f2270d7722ccec8158797d4af5e267a00d08b894465caa8ce236d718b2c32dc6e65e78302bc7ac7e3302e5ad813ae34e55d7a816b95e2820942a9178e7d50d66f648c2c2ca34cdf259aa7067820b27f008812b2f2a90313da47c35cd576a77e52bcd52d6be80f0e8e59dfe4250d714f1e3c5b65333ef842b5884379d9c5837bced99a2db8e404436e11338cb6fa5edc1291b272cc38d7de4241461db4e3b8eb1365c5b404093de955ec78c064d25ab6f19d7dc061d3c8931cf7c08cc62ded67d2967bfdf9f3045cd49c7cdaaee063bd9d8e71b61de80077e11bb615a7bd7b5ed74ee9b43386b2aa739d73097e1f1a3e576358e962996fe3a0a6c8bba58ebd1f326b0a7a13b8fec19b4d9957e6557f082aa66ff1eca1b15e1dc2153f83650fced798a7909e4cc9facbea05e10abfebe400bec413ccc8c80e9e43825486472433f8ea263ed66cf4b2a75e7ab057cc3f0dcb5db360763b817acce92c7dd3426270872f04b0f3cf1b8ca5117dad0e4e1c4420aac73dee668574cd24acd7008f0b0a7087e6fa389ef5e190ebd2aea9c3a6de2669b35908f6266420cb215bb09e93c035e13b0306b63d796a9ab6d2f469760dee09eb369f75783ecca669b8470270f2f18101fa928905957abb426bd50eee187f7e09966b066d92ee144a2be71813ab5017b0e2040b97942cb9d92de224e71a862f320f5f4b3e65e882815b0588d58232ea226598bac54fc93938e3bb52cffb6250069e4b69adc1181a31125b6af522db12f94baeb3aee75cb20860b29d96bdf4d006931bc02a77312449230d8fdc4254fbfe9050e9aa4412d62b477f2e2fdadf79b821b90041c6250af362f82654e7c54f97475665e4a1aa6ff82fc314ea34ec9a10b814b125a445a0dc7645b16dbe36ba443c67b90637217ac5d488ae721b0c1a0e351a8c561ca701a97f936111614c5357e4cc0479e8d11f33fbf107666eec7ad63dfbc4ca611aa3c46e8be2f58bb2bef6fa63db3618225cd57a443c4225dc7b3b7dfe1eb494ed6d9bc892ff659174d8caa7ba42d9a649093bc1a76c4f6affffc2bd0c0f08664ceb057ce3331d5776b47d06f95682cfe3b8828510865d18ffefeff5766c19f0ef31808efef1d69a3bff0991f416aaadd8b8118a64b48038af5e5dd0550c6d493456b905091252d7dba3d350dcf8574a1fb068578e106f054690e92c127417162e50c9e554b1c9b55161c9afafd195d549b74b38ee72ad0bb46a72b43855d374ed4e5520492f20fa7234d44768429f2c94a8311accae148b8e9fd766d08745880a65265616dc41b14c341223aad687361f5f8fc17aaf86304e7807ac7d4c40bff94d3ef0685f77e9122b13bffbe5af2344c48c84e8cf4d668fb6c9c234343706be9754a407ecd9688b8d17d751a3a283e59b98cd5daf7f165546daa782c1e1d19641cbb95136833613431e33d0fc935814b5387c29efec15fe716da82736622efd9993cadfb0074333f28de6681adcdf557ed848a9b3d6546f4174f8df82ed90c1272211f589d3b95b4059ceb4e8b90184aa78bbb46b045f36db0300e676726959688ba66c05ea2b099b110ddf58590c4ab3c46e2324d91853ce4ec62d9b438f09470b0b6a502d40d6ae1b68346463daec9853ad54e3c2648374c4795af419db1c4ab2bd25a18eaa8464ad87f8a08443a21956caca5fe29e7122b308404802c9d1894a02adbd99baed4b9372ddcbe35c2da4fbf502b12602f6b8911476917a06de620af90970b9dbdfa38f374f100d64980c04dac4238e57bfeb0a270ee9565c15a09d5b2704b32c27b6cd3c1825669eda2f53c47a14d9344dee4e10ca2bb654089870d8159ad029f7964a428bbeba3d54fc423749864d3c977b884932e8f06f957b01018584ffdacab36f4e721073ef90b953dcad569ded1ee3d35ed856e8adb35b774c22b66d941c99ee80a46d4dc87f22aadceb7197079872efa3e9bd2d0b63ea575e5903a008186e41c8301ff79a11a7ce0d3f59871f8dfa832865d496b012bd56cb5aad80d88d82cf6aa848d1afc1e570cb2b7672a93dd5db0ec383f58d909ff6bb9622a6a2314a7130e24c57f825fff479f2fb688518a15a710d7d5b7a8174b0309c2f71eabdb5befc7301d6aece70fd2824f5c60b3a699e3e4bea2d1d37d3878442760a61ea1d560a2bf4d45b039b1804056cfa9bb4727424c79b669cf91ca4a8b7c00897a95c3c8320102455ea4504fccc9d7a634bebdd33f1e5a1a9775457baa8f9b5e3931fa6a12a364dbd07ab064472f85e9b7ad983ae23cd1e927b264adeea247f6e3618ed0d294cf8587266e5957eedcd66cc6b9de6d7957fa02aa2de90eb51f552706c211b2db861380986f12f5b07c9f7b1dc4aedfa22f5cc52db43e1765d2012301769286660d121565633830f859e46e9236b893ba33c38ed3757a9c0d133486e6db94fc569398825655b95e5357a014c6f31060c496a085631f25515c313f8749464478ed01f3d9910b5c6fde6659ddea8f802f77558f0346d12a43820cb03558dd240dde74bb542c14824b571206f58e93fc5ae32553e0eb13cb7fda62ebc54adecabbbc6ca76be5a387a4d109f250403056e615dd37a261d01af8c75b89e3c75f29bbcb84e720ea7676085a74fd893afe3debca9c8001d7ee7d298c64ccd1ab025fc5f090921d346e184f3cd94846412325a6adaa81767d305a81eb21448565a43ad97fd6021457db65f0e67d6016af7803c66cbd29a7bdc60db0bb2c87be0fe43f773a23debc5d2c920b3e9a2a2662f9c50c57acd48773ab6709582376cb29414cb2b8001164359d92d4ed28870bbeca4889046c95868763902b9dd4183f85833e67432e9e78ed044a995b52cc896d676c75244f24cf5cb06b4f92f271e8996f1db9b059017dd5d1ddda74da1864d67a17c400eee201b16e71bc6db76ac9ecbfaab5f9fa1356a35ee92710ebfd96aa51244435f002116a3860fc53a3beb339cb167fad2fdfb217fad647c54d3b5aac8d4b2bd3738e94852052d190046f3b5f094adcd61d32d2179e1049c1750f1427fd1787b9adeddfa94b9dfe1336be44788ac58b7a976c38b4ec0f5afd540acc55c84e51c77dc314f9e96809d6182ac7be7c0201696f0670e291c317d60df321b027e299591c7f590428887f85d7be7645a1116d480be7cb8edfa4aac5417c896cc54737e52d6d913574acc2a2359d1269e848fd298b639184e5ab319d6c67e7864e17acbb735ec15618e6a906d4195c00b477f51a1bc9229f8b59c36b454e29d84e76cae2dec6249710d00299b594b05b4d1677cbdb79164ffb9c3e24f888e465c4e7e62916b9d1efdd66ddd14c4f0ac5787e1bd7eeea8060693e39b716d2e6bc2812619acc763a94ed33d9a3107ffdf847e8ac58eab3a8343e460b2a54a6d5df95f6c4ca02e457e719829d238042d954b9d1d1247bcbf3e2beec99603521f73ca0aea7044bdd202722a7a65032830ad778c043aee467ddfe3b765eef871027994aea99b669d81211f0eadbea0c2400810d43d8fd57c4ad63360f7c62db28746c63b8288f091d18c76cd6ab85b0304f96e81171a6bdd6817977b3db48b2c810264c230dd8f30fb9d7ad0e731a7bbe7617a04f626656743a2b3d283c196a5df70f978521a1d72596fe2ee2e4143ade15e14967aef70a89e81cce482b1aee93ec6bca3c4cbb4fe8303a7191440f706a011c59042dd3072f34ab81b8a17ad9e40bb7d172c4a8478b87014c6efacafbef5cb19684381c689b60e8173222c0facd4ce259ae1cb416398360117c0e2f1449f86e0e30b60f64707ea35d580c7237d52bf79a0ea420f4240b75a3aa6843ad5eeec18f8fce043a6b253b1292094e208d9ebe6ec5889290aec4f284c360a1481afa25c38c58d02ffe191ade37c8aea6a83508506e92d9c043911cfdf898b63200348d9e34baae168a824e0fe9788f31236d2967e230e49b9a2218c98862e527291f70ffd1995aa1ae76661147c53864383eaab46385368c22a4469b81e304ec7155fb9c6f1c93f404d8eba3175c7885ef8832e81fe8ac27a8d2d791f72b3a8cf020d2d7e8198b1e0c77a5c6193aaf48fa68393b156f279f90289c5ae367bc350a0e936218fe5a90eb99646301e0d71afba7fe92dc6d1066e5054dcaaa48a79a6eacfd09d50c43154dddcd9d035374a06c91e7813f4b4da2deaa604c2d1170df6b8921206136bf6c16de81442beb668b0fd68625a97f37e24ab12a7f2a1af300e6c67a74620221020c95ba203efef5e68567e44bdcad23c4af504027c150033b9340bac1499346c9b847bbc61ee967b536c21701e239d296cc74dbede856d4e0f052d8bad18cdc2a83d1cffc326d1467bcb2d64c7b81e37b66c47d52f5261795f8aa9389279f514fde1ffaeb7f20fa06c72f293e5e1438ce088f9254fd104517eb448dbc22a4d73e1e2ab1405d386a941e82100f2a23ead060ec09dc44721df2ebd6c040818c65154e9c909197e00f6967b90a5f3f732990ba2d6952a4da27ba7c8a38bafcd0a914a721ea80dd33c07a128c2de5d4137633d4833ab927bd65fcaa9701eb420395f44b196886e5a9f4c408674c93854cc0fd2fa8ade5f27d501aaeb5c0e5a328dd30d199a820935e624a03a1fd38d72954a4c8573d7bd10818c54b9429b778bc1ee5ba634e26e04d7da1015cdc4e1da57e7eb7e06a3835d62df48359465f927c02083e6e422d369a29457ab2ae5fdf0efbfeb7e95024b18729f5e006c43c84d76a1710746fe8b75b28dc44c0793b288cb1b3e81e577801260b51f9edcd937ac073c85cbb87fa158791c038fa2f39720fd914f7e87414ed151272760f9e523c1171c0dfc50737dff0777369e3e4f77561f90ca97132cb7777c30198514fcd734018e8cf7442f012591803baad182770530ddd3bf1d0f9a2acd006323da393837524a5e02cf5ad952236cc825c5f458096ca8e6a6c2cf87990422dad39fa11fc1c76d205a293c8b064aa3e987668a3ac8c47069be50982741ce6f6a2683dba550c5e0b1886a11099f82c8cde1d3a4a17d080b401cb224334ae64f11bd0199c27806ab52d62d51de2f0cedac8863d1cd406c1a6f6a82fa10f64846dbec71117cc4513d0ce0c78c1a45bcd0b93b788355b030b9b56bbcb82b69041f73b9000151afa625f1902ffeb7e2e23274c1c4d2295c328d56e589536b54802e629026bb3b499df2e6fe958ab4e4ac012660c11225ef031b7f40befe3b13d5479aec02cf863d56182a182c7345e50803b05e840dcf03b8d135469f43c7b7014000083aa7de9ea236135fd9c46930953017cfcdc44efd1948ec1ea7741ca50cff6ac3384a16236a1af41527f26a3ae7631b44eab84b0191e14c8b49e8f34f15b8b828de7a097d567b98a602082cbacc22fd90a0619d46ddabbe33fc59da01a02eb12a0ae102e0a20ba993905c8444049e6ddd2d1e6c8d147e35d8f8e33ed87489b5b5ed280732a139b3cb7932bc32e07dcf8306383af8cf1747c55db0a90575d34b14325f8099220b2a9e43e11464230dc8fbb079730a35160b7b82e4199e158f6c09e27f82685e52788256c3e67bd2be185e56c60c83195dcd180620c5a7b7c94e521d2907e8fed840ccb695512dee894ce49f7bebaac2dece3e08ae80420205655bcc62fe5c68620c438b7225bba9b284b769180565a93c7389140257b5f7658338f17e85bfeb0805313c86a76c0023edfeb5874752834f04c022e188dfdc2604d76ca164b518eb4a09be17af9025a998190c37326b363c76d14902472515536b0d70b2bfa58832bb496dadb97b7f6c991d0e2ae049bfe52b37e3a4dc0ac8cd17b93eab5ddc65b1fcc092845bc8d41ccc59168aa03eaff56fa2e5e2b90cb488e9f8bed64b85225996242929c4d8b02fc515b69c776d2062049447d7c42cc9f0f3b28c46c6120d4c18a21945f2a7b21f9291802aa535a9b314715d0b189151a02353ee2bc78dd13a09a803d71bb2b6c1fbb0c422f4bdec3ace652709f4b52f6c2d804fe9934ec4caea0b88222ac1d1d9349a6ffa2b11a9c7ee92d5ed10a3f508317f335d58391a4827b13c475c87a91ff1004820d3b759d8f776de43c72e66899fd8919f9fdc0ac3e64fac13cf611b8c611d08051a74517b33649189af3bcb86884e0d20eee99e4fc78fa0b13c9e1db90e611cdf9dd99fa0281dc2a8a42cc248fe2a6c81c7b7830fb175c4ec004bd86b04719e3e28a7c651a870b975266c04156052aa78a8768d5d91dff5a3056f0eeb4e76a84e12bc729368c3793c924c99e7c422d59ae6f657a36671d41bb13a1e1c5dbb973e431afbd33a67932aa086b699f25ed4f3d2960586353e16dcde82caacb4ab2bb122dc1056c04ad9fa9afd6100b54a2a70799fd1ef5541679453115427d98e2e4151260c0d4f8f8b78e3971b61c8df8705c4a96ca57c329ed79878cfe21a8d2e78094d7c7e904b18dd6a0ec7dcec2972ff00b5117590c6d8fd1b6503e03208149397326fa0945127b7ed08412eb47926a75f40919650c1396464a0d833028ff4a73cc7b831a03dc6cf15b7872acde609284b88105b0f4ff732aa5b2997936e70fe37887139d9f94969a5afa865f12a26ddea14790f6c553cee62bcef16820dc0b08492b828c5f8d70db4a924a82922a4a902f0d3ecc6aa76634993d6bc900bb7081bd182d30250bdaec8314f5b489cc2c03372413d5ce9c1156299be30440795a25bd01873af6b72c2cb418ed29e2b9a62d2d1818dbaa2fb54346a074056d7b9aa3138f4acae5f199461e6bc2b1852168759fc408c206a99391aad25b4a0d1d89f0b884c9075a3ae44de0dee09cb5ef73755d67ba58b0e28564ee529cf2fcccae9ca84ea7ab5ba2d25d0f55c1e570f19b58ae2922e139713549a6f4b12091340905b69d603f515a3331855faf770e306ad31b456814bc4aa2fb7fc86f3fdc63590d334f3678cf45cd7dbff02ed842b38941f851525aa99713c4139b313850860cb96c29e60f5e253cf8080c019b0af96a16daaa8ccae962b566e1fb6506589c11edc4c440defb45d8c50a9506c46a61dbf3b000cbf9fe528d62d1fa2cb35d6845fc394406436066240afc735698fc1f27804ad041eae32a5f68e1ce7be0bac91d197b8179a87a354eb927ede33a8f1fb1ae93108cf203fd1d75e43e3983a96000a4e6d12cda967f14cf84138569f3fcdbdfb810d00e252054d368eb379afb51e670d3c0861c6fb53288384f0013da5156d1644ca3e4caf76e4d9fd27f44724128fd510dd771aed377b8cd24076d797d6977d7069f490c62a4fe283507dfe7e60ef47caa000fd9745af63f978490445353ac96ebbbad35e914d5c470ef61e1574730d5f0738808215ff48792735735527aee428ed9110bccdb9241831cd7180f7fa3d26cf3f58e4a3114e0e04ffc4ec8fac616e1582c99681795969f856a9605d632d2aceae08de95b446ad59acd1d0462968a1996c4849bc0244d9bd237f22792529d738cdb442c3d2bdc9033e7d397669225680d06a5c2e159358e7a3e5ac0dc10f6c3136d4af970fadb216bd620b9053a51365f9bc41fc1a96ca3ea37a67e36bd641c334c11c1384fbab00bbd0f120b8d64c629a203157922eb556ed753a4bc928d90c07579c1777b6ed1ff476fda84703d6151e64cb12e0908987376b29a6a36c59e9ee88d7dbf387ccf61c189929362c425a39663b9938044077648f8e8704e26e3ec3d82301dd964c3a072fbbee894108f97af6fb50c4aa923be449930bbc59aa044e5d6b8495ea36d8bca57a38197cf673b520559a5b55a1ae2fc6486c3105371c418a1311f092531b6a937f2ab26758a0359493c80642159e7e0576f64722d26d404ced0583e05939f026b15f5a32c5eab22078318b0af4695f46d6887994e31e340dd04beec9b3bacda4826135460e251aa2c3c49d592419a21f540e4922b273d3b0c4f803e6aec24a1fc3627cf6a8e286077bb5e8b4d6ed25b051009f4a21118796880f44c37ea364453a674473621bf9e4f6857ba044cf10c135a9da9e5b432983b791f7c43f6062ddc6e897c3d650f05028ccaa2a2107559e97317e55b4024f351e6ab555c3ece381ea3fcaef3f5637f77f90e54e6e8f6d93a17c3d79696a353a37d10bc4c4b0b9b08bc75ed142cceebbdd521ee1e9aea446fe27f6d62084f326aca89915727c40f931a91b5e49b54e070b50274f4e53725b69bd45486eaad11c93247642bf692bfc46ea164ecb3d23ecea38a529e5fbb7bfc8ad39b4111466a20304eb19c281e4be3e4bdc1bb7eb3f21e9d162cc1281954a48478d6ad61c5a20d3cdfa4f82f29de474eb07c82f446aee9c7ebe9bd9e5d2c57403d6228a0d5061e017202af5ff3f095e6d669d7bb875270426c2547a7369e70a20464eacb3749fdde8f06b8f079c4d64b23cff407d7f7dc3ff586f8bdd1dfd9922a1e400b297ef4ff607fb9f73a2ddf3e73714908add0c1dc4ee499776f95796aa374ccd5cdaaf070166510b9f958cb67b47fd20516ae95c23bd1e9fc52720094193abce9a663b3da97230d9f33289680e894fbc3d2751d4cac9f9a9cab52b4fd46f060d1fd248193c8c8e7f26ab96470370b7229b2d1b18ba765803dc19d765d86487a1c3003661cb17934d741bab1cef40832c8753b2547c4734c54ceed936060a6524a7c17e9202b4de48827b69c97c9a88901587853e725bd2392dcf4e2e8721e9e2e6d0df7199f4b829fe84ed0bdc0fc795c8b6dea5c98c9af43f774e5459218e7d8202e7f17bb83f5a3cfbdd01843c0a25756c3a3a5ab57d96c9812919f41ddda118a8d4beeed19142194bcaa87718b5582f4aed01dd00dc5c5c56c6f5fcca3cd27c7c1849d26e0279d975a8ddb2531ab8e315dec10543b3b8f8fa279ea545a45f38195ec399ef5d0530877279c917cc545411faad101641b8ce2bc391f6c0813122673a638b5684631c75f3f76daa12608f7dfb849df4a577ac1c6ea8d79f3aac83e9bb34c3b32273a3f7f8981fb773915b8288e2c448eab506f99299daa60054f3a9365e35ff0a37019021c7f57ed06a772671c1236b5cfae8ff4fe5338f24403a6ba844e6ec283ddb65d32eca7e9684d8d7aa522c7280c079380e6979b44272b3d28239795d07c07609fd0b03acc1e2a7d48903b8a19057de59957d3288ab8535646d72f7303c68f31da44ae6ed11a7bb348800a93e9dd72b34231a9df56059db0971bd44fa596709660e5b105f20c8bb5469cb48c6693319e4946bd9d3800bb585288b2da760fc6f913e8d36b22883c05ca5ac4988e089e56f2096e6fd5144905ee5802b3fdbeb45169052b93e95c01d30652f12fa0e2213c32bbde7cb96238e806dd8042e0d73a628389e807380ee4ae88d053341df2f5ee08bb13e23d5cf7311b7e254147dd0eec4200cf5e8d821d38b5100da8fabd0a2883f0f1ed69f9c54bbc358c93691b17f7828db86a47f9361bb10188f0a4b6688119b6f182e619d90dd608d3cc0656ae4911da7afff72d6c4f436d1e8445d839222efbfa99952e6ad2cab7c8a14a63f2e9b574f8795d103df16bfbf9ac979780c107ee956a075c7d700b143cb988916b53941a40b34c9a985876bc086cbdfaf2b14f670be0f0a53b01da7517b28831a86c4a844cca4cd5342dc1ad83399800bfeeb953c58d86f9b81e7f1ddd7b60b184176efc80dbff3f2202ee322db26032189238eead7c109a69793500768fb25d5d215e01800bed0a2e84b5f2bf4730452309b950464af5c039c77bf0f47e4ea01cb5c1f425acdb3e7df81b623bcd3ffd11425d85a9ab5ea6bd8ee16b3a046b9a2771bd01d730af7c567df57a4826493c84a20dd0f8c60f0a0d718e049bc066440095f5a20c4af7bb63d69b10d0299c382872254b5a9c600510850e0febc49c64336e8adbbbbca7fe845128ae8531cc911f19baf1e6bb23c762603a4bc2c4118e62d77c19cd03ce16de80d6c1883deb6340d6882db232f73b7bb2a9481487b8145f8709cecd0ae3ddffbdb4071a459b61b7dbe6f49f2d0805c583c11d696bff0758f0fe657dec7b09fd4e9a749e72f00826ce5dba309295e484bd99dc9442ba9a004f744f0bc321d3ca0901c917fa92e44f71e1ec1db3617920ec929aa49b716a8c951f43e3f1b6e2677eaec0927d702af1c898ec84b470921f0bc238dd5c61e88fd123a22355bec4afb3999314e8eba8ac5e21059f6ef3dd4d21ee87322b94d18be1a048302d59f9de5e490c0063b742b8f5e6e358fe106077efad85f07ff9dece0c8124f06e0086162f843a7b8e26a38acc6fb7f55477587355871174792b32c2101a5b83f441abb011eb0f5b6fd5156981123e91b979847e2396403b93186cddcc7f76fdade62c172ab9af09d38c1c4eb1487082a1e83234557a1a4433739efd3dc5fc2ac8648c8818435fcef26ffab9125e53305222f1216730aaf2dad5ffc16188ba6f0d418660f7ca24217ac6c45216d70fbe20aaab0ec64139d64afaaf1a0cbb629db610bc2e149bd57fb87ae2956ae737dcf1409b8c71e631cb6c8893f03a6403064c04f309b1436d75ae2932124d8c89b1496656acf03a9bcd7bd70c143c732adedcd87b1818e0f267cb04fe0c08d361b0bdf2a1f57c488fd21ee15394c2d76e9f08521b25600a32d69107fa579ec10476ad9e243c971a9325b23074bc9b5f2d5102c12563540458dfed40571c6d78d8b133f3ace04410bd2f641389c0942b890c616acf95f5336a96349e2a8e078e1dbe7be2bb6cba9c089abc044cb7d14b9e63748b3aa1ab6e91c834ce98d3b20c26fa557b4264cb046f0ffe76a1286f27c53ef5c664158cf02d0f2268487c224b250a6a4c58973dff16e64a404708dd6fe5cc06e21ae6495631cd4af389868c866e85cee848e04119011e9bbfd99ebe73554475d016368d3da67cef1bb1b5bb2cbaa9be4f8377d8cc236a1b5ea107eb990be70ea0d59c7def9ec84a8234a2a25cfcd90c4c36512bf8f748f5f3bd5dbbdb0a5a9945e087a133009fc0affa1233795773396866d0265dcb1606c7d4e42be3eb4e07df5d3a85b3ad6cb8efa68d8ed8ebbb31e105e5c428d0f342a719b080dee305a424b03b9cb9a244f371a2a0161ed1268b90dd24753a1095f70e9625c37737f4a3c03d02036633237e4ce3613cbf9d4797b5388a6e66f57323b6c0060bb963ae97264801d4cc35b411da9a7cb9a7a116b4ee2313c7386dc1c0d1e04f7bc14b3fe8522d7cdf797e678e5e5c97f72ab54b8c97a94d6f75c006e5eccc823ab6ccd2b8f4af211b2f586d56f6cac23ad47aa29b5e9b08f63b4535b1b377f568c612987fdea1a903ee4aba892a1b84aaa6b959401e56262a6b00bb3520404ab4a99f61337ba87744b5e4443ca76896c6180a7af9fede99101005d2017e420f341f11e61b462ef6c65fda1d1eecce3f0974ae57b3968ecd3cfe84b0857a8f84dae16af48b9057e506853fba7aa1fd98c66edae9ecf15fa6e5c88c13884374064b9e0e27ce68c0c35776cdf1382d43bceab37721fc846217910d6a2f4d1aa8f26171104590cfdd1c95d58c0ad652f73a02caf408ca33233156d9b49648a211167a55cba1f31d7374d551ce4ed56077fc0c51c5397ba89255741e2b143a618187989d47721ed99dd8178a9bb8faf1212260a706e3527029714824f451b42dd1625d3968e4794a7002808e3a67088f89a41980fcd7d7359724099cedfb2041fecdd45821083761c40db1a952dfbc92faba7287570e1e1c75df5641475054b15831df6e76eff74b509f74e04bb3a0a44fd61123047a275b34408c47ab8a977dbec524aa52a13d396ed5f6b2122f4ac5e592652b17883f543ea85b28deaa8b983ab5f77ce8b702c83226f8ff847653e00da4eddc03b935982b2c3690076c14f8e2dc086af2e6450080a80afbcb86b152bacd50702d2671684b45cdc442d5346105dde075558570efee1568a8ca6c39e72a62a8221e15ecf6257f357df51da5b83760b527b62db679968412b9902cf06830c697ce264c70e703a58bc34a08d8256b0808b90583b35c8d9e151b8a68169fb5f56de332117f2a8316e42d72aeb337d541294252c404ed039fe056eb904aff1260c01838c92e43ef5690012a0de8741d11973761809e8603f506b55d492abfa1071d4c0dea998d48e495841dc2bfa7854373053ece00a8fa780385cf69e07cba348dd835522206765fe2f8ea96c80367a0a27da7171990efb325b96984525f1d5ebc49aaccba347ebc5276d56b3955c780c3fe71a8d19a30c138d2f3de6f2be0db027d1a644af1d7952d4ee4ea270be3c20640300bc43154e303ba30df8fe60bc62f7f896ef69b5e364ad347346557cfced9dbae56c11d28edd67f49a557c99d8cf5ced7459e1c9bc805f07b1c722b3fd0284d30de49331120d9bd8ba857d4c5c6510476f672be32fd7e0198fdc2591041618e6220996924233bb54f9725eff8e4716ee8a3483829b9331e13ac6685c5587ed84e86c5107532a75693d0e1b60f3f1efbf1d88718722edc41039b09804512acc14aade699b874bad683e13d6dd9e8ffb9452020d3632d19742017bb6ba53c60144716ced0f487165ef1a2e97714f564f974e60c504ba8487d826be1caa2b853913b307bde83b31276a32bfa0438ad91b53bf6d89fc086c63ccc19599250f5bad9d65b838f71bc02725dc8b49c1331f87fe427e1321d96efe12ba28cc554da25c620cff19b48b712606847203433a141aeb15fe8ea1480481bbbe995ae81e3e6cf823f3b3190502b429902d5f8c619073a7c4e1cac71a0b3812984c6863175cdeac518e4c802b49939724cdd0e4b14c92d85571de24d76a35f1d49c54157589fb91c731d5125a8aa266aa5604464d363aff4bb3dcc03ada0eb38ed21ff60a15dadf01108d71469f51d987ce70a6667676724e20ebcafbc845a477b2c31b15dc102f615341d35d626b2f4d5a24d85f89f0df513c7492231939ed80a34264b335deb8a71997896395deb44d6d93feb9283e9260b2ec5d9ffedf76cac58d2633e9ee91bcfac793ca490ab129ab147ad244013f79f365daf410b096d35d5dac774a9b5aef832bd685c7e622b9d0251498c0366145cb692788d02615f7a01ab02492945dff2440451ad8385c08e33ec203b266677791b2317abfb4d2c4058f784d29d53a03c93a859b33809e453d888d907d10601432063a72d7c985f687518e8508cc2d55f67282db973d36c9f12082b3e0c80bfae1222768ed2a74f7e345e82e088400c917ad89a47322294e25de2f0066992a5e019954a55140ef6a622879a3916abe9b0746e5ed6a5c26e97fde25f5839b8063c0577990574d884718f3b214a03be692c147d748fe24831d509db26d41aeab244904d8da6a3ff8440df432e44bb2920465e0b5db645307e505005712ae492d5d76e8e7aeb7338684413e48df45449634970a0df47a081609f03385fa824a37e5b122db11bffa8ad08ed2faf42b566e8e356241dd24b7125236a435f893100e2fc066b3bb8da93511783b88c31a1437cc8c9f5e668f1c78b1eb1cf1d53889d15e450280a902c762626d8b4aa632f96d97d47bfe3e7929b346dc5cd5a3aac43a97be618b08c97801698bc9f018230a8dbd57a2211adedf465488c5116137551367b213ef3233a2eaca7bcc187aec439eb9448c9b3111f28a008769a6e722ea1773cc11e76c0f7e54550456a371d43a78768425a4459356ae4684c2be0441a99dc6ee30e0b9723ee68bf897d14d5eda2cc114b462ac9b8741aabd59ebcf0414fbcdedc94a5850328bf704854068f2cd9dba5b3329363e0bcb85ed2ad67677ea43d0a1c3bd241769595dbccf060a83f3b1f515d3d7c6e1e25459267537cac6b55cc775cba92e7aadac3f072593ae45edcbece9e0c63cf68b8ee150f96573c2af8ffffac8c5eb104aeabab64842d384fb95b38dae2b04e6c9a3107d19db06bc9220fbbff184eb657efc1eff379d43c4a3708d8260455e518e68fc9d0857454fecd162ed284460715ca9e590c9627ed23641497f63c82ae506500d4fd486000e28a507966f06f5d985da858e3863930d7ce8e73b0fb9e79789967f57b16917ee23da526c2e70dfefa69aee321b03c212586baa62130b849bb4ca38a81449859453390df5f92ecd6773524e26253862a0242262ace9243d4ada7449e7e1a1cf18a9f26cc5de3d8268a6cb58b559de77c9aa9e46311bbc6be679ce51d481051c6f0c0aa58e3b15815154c0cc3b49b93f68d5a6c0969af80e5c4479275228a97e1c5cc2faf3ec994d831ba99edbb071ad54ac39346f99dfc59b04cc97fe82eeb042ba744463a8db9e5d462a02dd61fe700384987bea0ea0922510094f7d2307c5a689dd876765279a572ab58273d25dc1a237c447142cbfb01551af5166d711b503509be46c46de6b94bea343507d338b35b39edeab7b8b6f21a055b1bf37c9b233cd44c76baf83c20b50168ce1011f784050413f64b34eb616d042ee217e40475f61605774565668d87f67fa105e92b0164c3e7085d9b4e48209ce3228f05f47910581bd9fa7b0f426f37afa8ad74df725ef6b4f87d89f36fe57c878394c0892c5eb2fa601996da653c4a008a9093180810af86f22cc10b827fa222041f139b510580a3156a1e9ab3c1b1b7271a0d8480ac9d4d0a65db9ee1fdf37661cdeb254fe2bbaf72d60ac65fe2f30370933c54352f7094ebb4cfb13ddea5ba152d6c37c411a4f730b9ef046211647cf17137ef9c1073a8f74d25581b18016497c3d6f1032be1b875b8f0524c0b098c004133d141958086799aa268a74582fb841bee43a0e5353975c885ded95bd88c7dfbc085c74264012a1b48f5ec0dd7490c2f5615f1a6f16ab229dce19da67132cdeec0c745a9e485a7cc35c1c8181e105875a818fc3c2329ea53099d09a2662b4bcb67f5f63e2951c5aa55d080652945f41f19fa51f3f588d0346bc0b89d782ff1c62eeec54cd4511cac8c2a2384db4d9d628ee1291d5b168e0820e239edd1f1a7dbe6001b318c380e497d1d1e2629922154cf9b250ded8386eb87622b0400d7a0fa08735fed450302bc7d8674790387b6e8b7dd00eca1809d92719dc1118256f7e80cf817e964989619ad55f0fc522861b07b4a3f23c28749e6b8bb892768a847991d401696170145a320a630d581ab72f941dee5cd48541a175411669eacfce6718c033269aa4e7eb16b2b4999864cfba1c0cd080f088cc1492841354308a8301254cc713d3b93e132be78f2037c451de7c81071a3f058eb8b02eeac4d632eb27348a7f08a3d37376172d6180c05fa0dc14e7be00ca844cc16bd92e25e722596fd21522aa2d2e86dab00ea875e48bddeba71b81619ef1dc2ba0c1b7cde2c852e77a13ae32422e632baba4f534af475dd5041994d76ec59fb44c82557678b546f0a3ad1f2ff462a9a34c6350b3fd7fc3e4a64aadfbb9478dd65c51b3c27e790db601c527ea28ec9d3bb8b9c7ba48bf1cb32e8eb08c5c5701855cd0edb5f65a68613b0a6b56bac8cce7018323ecedc9e98f33e69fa63c50a538659dfc975cb1cd69bdc9f8b6123501f1f3aa22cb1c600bac5383ed76f9b20cf8358a373e8c256607fd2c4ddb68d216792ecddae4285cfb5ca679918bddfeb6fa1493cfb66a8f82c231da0a30fc2ecb0c3fec5ac6df142545f4f55d9c6099df0516db6427791b4f5fdbbde60ab64c857a2e665707e976b3e8ccea5afcdafaf19ca524fa171dd4981904632cec4f4a934afe9c0429ebd43810031c69e18419158f82035d33b0b8b866df413a60750e36ecf351268948cb5369fe4a442553bb7f1c1ec162ea827272e824f25ffa9b1bdba4d4334030d0a78526b0275c9caa4daed40c2609700ce149b2f3b802e00a4aa63aae702702fe467be59fe0d8154cc6310d834de88157602cdaa051c245c59d401e583be6cf87c863c335a2abff43979cc88abfc3fc2ca5808157b623f502b4c5bc94f383001a7156458d560987625230350c158a1ff21470abef168a8e4918057723c1e8b87c563dd253032f5345ef2e614e9653cb849a66346952980dae17b152a42e62430c007cb34c5a5b06da66c0fcdd334806664f1bf08bbe910d7caf869a81ee4674d8e817062f6b89219968f9a00a5e070b8018bc163ca4d3a545ff203cf6cfda32c3f032b056a44c18a9424cc93c6946a581abde9066a5c4ded284b78bd564e13119867f996886714c8fe852240577066e76cd2718244b7187a98ca1d098fe2bcc66f0b9b53690716e58ac5f3172f41385f985d60ed868b597ef64edac924819d0e5c1251561965fe1c2fbfb241c8eda51043b310060f8ae2038affdbbbd63f78168d1f13a13041e8018c0a944b27bee5cbbdc29c617617adf8c5bf70846cd205f0b0f84bc10682c90d08f9ccb2db11535838377cea78ea0cfce3f75b22cbfe3ae490d584649c74f25a891c817b282be2e5cf567932415b982b2c37177bf7b3022869eb2e385d18a14293d5a4dc400d90560ac1be4b916aec2bde5077f0c60d04049e5bdf84c6b5a19b5034d23b66de0c5b2d5b66bddbd40548f3ea8659582f0288bfaa9e96cb91343188856ce29b2dba1682612a4cd198d5faeb8926c54d90b2ae84b78d86d520567aede7d0cda6b853b9a52abf2e7b21632952b1f669eaa8899bb0f2bce553710d8862d4d12132872fa15515b77c582826e4e80833dd38fef5ce4a1d6edee992ba973ac7da2ec57ed207eabe04e2c1fbcbc8d4620e522a97a829788fa7a3fb31f156fdd08e637915539d5e964c6f01d2701285701e84a2978a95979561f7cd8fc01262ff3a79dac992cd454d10a346827a72e5804fdb9a4f3c31abb961a4093f106ee6286e0ce88b0a30b9e5276af6b0d83e9a1e9f3d3f45e2f814b4e553730969f93867dcb14771acb32b5efcb0f71dc6083d215ebc2c5d1c8ac7d841a09d91b29d7592a075b05bbf3a0c851139209aa78ea8be2ee70135f95fc22bd13c11e19fe6f9d16cc3a7aa4c9e3ea48fda6e180c8f32ee065677639652a4914ccc31b7b33175121ac7a2ddd9b3315a40377f665b6737f91ed2dccb7325be3b6802c15dc6dc21fba49407068fee94c404dbc8d689dc697441d05627bc77d577787b4d9d0d8d8b9bd6eb47afdb12dd0b0696e1612d3080ee2b39fb66ac63bc7aa686805028ad9aaa158105175304b25167107958eaca96e31d6968ff2c89828e8639404d43fa49cf312c579299b984b2f859ec1cfa6bf40146b574d82034f830ba0933171bc48b18711315881288f7cd7715abdb352693c1216bdcc897055a8dc13152d4976c5de708d00c4960b348287aac807cc397711e9b8955206a90b5c3465885e18867c16e6dd629eca45f6ea13bdf6c667ccc823da54721e54a5d0b35fab9e354c0774f7b294f95873b5e22c0aec0a4bd328dfcacfa2801cdcccabcf6b9a9627d5085b6a81598ab0eb29ca94285f140f98bc8b51f0ad921021447d0616f90cd7e3a9d4a584b3e9408173bca5f987aa5b9c470e02709b5c462753f61d8ad7f76f20aa41660ff96a91681da68c22c77adf62d259160de286583a4ad5f12c3e53340ac96449091b356f5f33bffe8ebee028cd206cba365463d687be9b7255713845a9c190c842142a5da14634e2680359283029954b0abfbcb70e7aaf20a241cbb954ca4d9a0b389a5d36a9235405462e8956b56559a60ff4d38e98164dc0a9abdc3a0eb2937ce72275b810e99b289bde270e4374aee9946e8f3fd5d25f7237e46540e69e90fe9a2f6532f889d8f6bd2f416f3e05dfb86303f38c95013717aeee39a0bb54e37a4ead8e32e85a7bb5d14d4b2b893b9d6e15cfa6a7151beceeba0c6e0da8dccbb5ea8289f31cb1c33c712a2842d9ee64a6738d19c1f5607d23595d751e64873da754199d32f8a03276aeb00702b94a651c4766bfcfab73f9a8f56b3a21cf529ee4fc74c5d741ac856cd76cc8ee23b88385e1c2c3be4cc8fa9b10602b1f8f7d11cc0a4a498d2495bebb6d1cddb36ce2a3d90834481ca6542c9c888e74ded6550dd97aaf4db0c25b8f4f5ed80b701c5f777301239bc8aa1487eccf28392d8d5ea656d8b94d616bd0d87b6cd620cf03ea059504b1c3693c952a2fcb13339f73a69bf57fc8e6e67e9ed464831d7834c832e57390fafca28b336f46198c7233401753ebd7d147484e9255014e6e0b0d59df348926b67f1135d2190d482d8a6da0bf75b297c6602c1c2e18577b63226b4511298642a820b4a9aaeb0bd8af105ddd614358bfc36e0f78d66bc3ca28942052ae6608c9f4842df9deeb28b084cfb30670ee240bb99c56a9c8a4281d275ec5b3e636759c04f330db911ac15d43b8268d77fa396322396a6951064620bdb6774646f6c2d148b6e105c8c12fae432dfea290c31b5754a0371baef69d1fb0a1a179f801f232e67789e67965cd13aa590f44608d7c5b026d2cfe4fc2e33fb841060bf1de10432518c883343bdc256215e1ee02850a191621d8977bfbb57d5400f0d1de80d54330209e0b4cafbdd2da57c365ab2e98a7d1d97d39aa599b180f25e6010a131be53131f6fc706225f1a849e0fc3bd174615b94fbf0161d640be5ca351d0f2f94b44622075276d090c8fe7bd74764035d6f6d2cc9b480da52c2c4a5a5ebc34924619e59bb16530e3c28c0824fc7d740044c10dce255e0d9ac93b7a0fda1b4fd0730bc0ab04baca02af6db62eaefc7f858cb9ead4c018f3e45ba1cd11858795d676ce20544cc32f29b645cbe5bdc48b1561d7e22a6263a90ff94123dc6923ec2054d0846d934c8b15e5cf0524106647cd193dab797cfbabb8e2837f2b22198c6f73021e15bfa5b0ad0eae1c34aa6f33c2509ca5deaba5acbcb924a22dad7b893daf674b17fbb12ba9e595c412d5564e31ab8cd527a8ff893f082b48cca289ba53e606bed66382a4745a18fa1bc4b1118e2635344251179b10e9613264a8c19f45ecbad0d7d34203a2d5ee66a3b6801a97f22d01e253a8629960987e952f3669a666546cfa69a2822b82b2e6663705a90cc1fc0a1cb533724e90c6c388286410fb81c4a5e45e2d702f12c4f2342cb33fe3d9dea08403cde6d51da68044d9b0dda937287f611569d9012f4fca6218029a696ef1231bba668785f02ca00fd3885ff26338ffb8d0c2d99f5d362fba7652fea528a3c4ab91d4b873563cc68a37a034804fdca7fa44d8becae7e0d4c4625d9286ea561670e2043913a2b0f28cb1750bcab412e555a369bdd157c4503ba16ce2f5d009427c496e558c3a2d5e6e1d0fd489094518bc849055f4c295706efe1a84626eacba30adadaf30ddbb10178987ad742e7322a69e5acfa867bf8fef2aa7fbc6f71618e56558656413a6c93e771083279f0a9dda78c2110844833e3c8ed02d5d0ce3d93a4021f50f193857871512cd163540a12781114cced56ee3da179978a3365abf48013f3bf340e4e1b408c5c826dc1a4d359289931b47303499eae96062fa5a575e4129a37b0ff4aab43311b80d585b42e8d94d60800d226977322c51fd9a53da0314e726ce2068658c12d720ddd6892dec6c18ee7604973da54ecea4504572d72c105c7eb43204143253684aa831204bd1abc1d4cef47f0eb78279a4fde9e56d66e973febfd58b98e4c46a64a80646480e3552a0a21c5c1014fbed605620fc91a64f2ec8754ebdd82d4a093dc122d3a4a53ed9632efc467dff3ef32b264f26411f22b0909242eb352daf6a30cc630dde6573f42d4a991b7cf90a76ef0653590947e47fea4a2b04810cfcb07102e4a449326f4540b6e59643041ea5e7100130ad9aff8ca072a0e543a99c1ac08fb6e26681f6bc028c01161764519197172ce9a5e196bcb9e0af77401305c1fb55b6e5b74abe148801d020a8ab9958e6987d84612417fe655e61c4d9f6c04f24e063f1912d5c0230ee52a80df1038700239249deecdfbac879932038dbbd241a96a41217b4f202bbd2e700ebd77a840397ba91c7c573b1c10c6b0790c47e212c19ae3e0ecb17af9a075d2cd0590514b4c02bbd0629231ca50d45988407a8bd68a932158d749cf4c673c01093db5617dd2afced9ee58d81882adf4e513cf10336ca649e72bff5575d6f046895703113d984567c02e75185cca6e3bc503a16b6174668bdcfe3568ac4b0ae9397313a1920e6dc99e75cec9f20a491cc890c6404124efea49ba22744c6ccf4e51a7546ce54af33703411f4843be6045b2bcc2e64e7a86df96af5c8f7c994de24261d6222ac8539143d362257e28eedab6e08abef35ab1a263ced76a1adb876e9b06e0a7df9888e9aaf385e351c8a2c7823d8cd000ee65c618cfe12b2d59f8f06c3eac78f0091539e377e21d434594c258a562c1dd925824ef1de055f316d86a4bf3d8376a919809a9b401ff79479839e8fa8688dab95123c66f908fc892e6138ad8208ece36819c9902c7d85237cb67034e0486ff5612bb136514432b2c6d8a195a2c4df4c3a084936c05b88f126476dae7e903a4c6c08b5a09bac13fc6bc3757dda0b7e006d3b314a19a6b984389f04afc66d4b16889af5def11d921124b24a1a0efae012f138b5574ad9b6c705ddf497b4596fc8ad284080f8b300d1305cfd218c6f5611ceeee06324fc3fa65cc0762911c9912f340acaab8f8a5338f39fe1e50d618dc532ec2f668180b4905427c84eb942de4a5e6727ea313e2485d087c1c0e04c807cc0a7fe6abb25d8a6e4dee4997f313ba3603e42138c9d8ec06c934a419fd2cd86073b0c34b38afc166f94a6771e46813b5618faca44f51bafe00044b8232a1da2969eae191703518174078c06fb2044728834b8af887292e6eb2457b3df8c23ce5c677f511996cc429a464004801f243d0700094cb07e36c17222a33d1ee0d18978a65f1f95471fe94a3d9794e834b68af589109f87eaefe18352052453c0f80548a7154383a97b51a40977d584c1f62aaf4200d4d12425b7535e7c38dc05e434a38a083b5bcd93ac4e802c870f7c489aecee45dac98f02df5a00a1f13361aae376de32691a06720415431977943eca2d6874bf5a082f1b0d3e53d87f60ddfc3ad6d95345642c26d415ad3923001847135e855b28aa860d4b23ea0cae42e98769519b02a64a1d9a694fa1ac5aa7aa1eb87b8324182a0f2f582670e23d1fd8774473f42c577abd000f680e878195c1d53f84f1aeeaa608fb3529f6c675de30df2fda5c690b8288a4c2b44d98d10fd856bc82081dab489950364d4d63907b30f4af70f6058372f8ee272d4cc685bc989cec3f672c563d3d3cf6881c01cd4304972573bda15f835e5fb20d0f062994881e7b86da93ed45d5624ab9fbae09868642338a02281701323111c4a014769054286d0c1e2b314b11d1337968cee3ea247b1ec968897c970f9918efa491a9d2aa03f00d02ec471f8709f3ece0c8ff22bd2c9b84ea17806fd62404deee0243a30c506a1259da16d7c17c5af6199e20b23ecc316df39d8935c54167df1ef8efef66f163565e00c666a1ef774b5d77014e584127cd38b35617d0599d7732d3fcab41d062643ae35ebc7ac4a82dc774c22ba9c52e6e0690d7b60b7855a73970520818a24c1f66cd062c65fdeccd3e0320995518136e21147898bd9df4414581bf3ed39c78e2b8c8a5cdd7ded5f1fd42dc18079f6b95d9651d8a43aa56572a19508baf50327504f08f3f13fd3486c803010e3a67bc35aeed449025412a4d2f1e6f703d912c7ca4a4a3cfd9f56e58fc69b38cd4062d58f7f61f58de5fa4017848a9d174348a09f909ea07797eeb7bf6b2d36c1c148503cb1bbde01e381cbd3c4ca167da02f1f6704ba2c3d39a068161305dd10c8460ca703fdcb8b2412df484669025ecc79a1793b692f4e73d5355199cb595db9c6c4d23d5bff0ce5221af6e5674d26393292c378b9301a0dfa60fd5a6e7201eb74020be793cf308512a230c9e8af20c856beee1496190227a721ab7807e0e7960191cb20e929af9e991c96b59024eb502b1c255e571e91ab5c26e7a0460bfa2dae7862e510f9939f459566d6717cb8e42e92108ff802ffba91324283808112425b3f909ad27a4e7489a7fd054eb32f02379080588df8f8b20855054b820ab54ee8c1351ce104c137f159fab498a2942065b608350c6ede141e557d89481143b07b8387d5d04451783073ee667f217ad2dc4ef74a7b0301e48e80ce3374dad30e402f5169ffa301e604ed447ba0ab80de780bd441b294813a131c5284bd49087987bfb09a5c49a5ad415e3b53d337574f47a0216668d3672f3c94c045f66b3211f4a96ce94a4dbb6b9236274b2b2e7ceefc267c9ea8c9fa74e3ecc291a8bacd025a283a4554a22b0643d1a349a7f719bc1c4827c8c68beac99841353b621d3f32316ab2750fdfc245f61b194ce2869dd8025cee7a6e30525613ac6c74860e50eda16c29a707563c90c16ef9437d88e70c29c029e94244e5ce4f058a7ad36a4c8795bfb0b24177fac6fc8df09e81ec0438b379c45150493a9aa8e984c6433234026345edea322e492fb930ef026a5da2cab09d0271e2d6a42398a62509700cada3acd7a7c7c3a04272513fc04bef2023018e0f54ab8c0eb59c0608b224b56a4e95828b664aac458023f29a8f84593c09e78900d7ce0766c6d66704a0dbcc40a14a20c8134b0584d04ce47bc275cab8f0ba99bfec3ff1a188716162ac58d49233794387492166cacea258fec2d67842bff219bdebb86318ac3e14629db6912cf29a7e5d4a9678e53aa53b8fad206868c0cdaf25ad5d01a746258a004da63945a007054727ebd225229f391f43e445eec5203f5c58f850da432a9a57ccdd123d4905e71cd12b72e30b1e4edc246d50b937bd7bee33b93716ed5b2f648ae86ee64a1f7b93aef7ea1c77e8ac83627849f38dd7a87d6d8553c555e1c70c266b575aa06f457f2f23794ab688f720314e5d0de17a426941ce10b2fe3bb0d06bda90d0aa61a2cd08d8e6efcd1c2ac4f49cba6647189be3011c445db79607d8bab40c24baa4f505c84f15e684286e8b90ea72f18e7df8ae42bf96c4e2d29afa72e6e199a6a57eee5ebccb5d3762dc6bf82fab978e027be9948046f3e95b234fae02999f0cb296eb52682d143ad0c6257d09822a85c98ed0d087791123148afa1409c219e2119527033116240b6056817218aa74dfa085656459f2f9671d12afb845139b3d24eec5ad0e4d8a70cee6ed9c93cd81c9093a3d705713ef0e67941959c65e6d0c0e146fe814a3c2224cc69e4d3992f5da104b7210dcc833536f9f7e8381bdfeb318e1b4fd04a7c45cbcf160ac83c507b7791e8e4c7935644af6e20f83c9cc57237ac5c78ade58a93baff5faa7bc83cfc1930a75a78c930b6c1447cac203acebf1bbe6777fe67a07032ca0561a50995e21a9ae2373813d974b8f3cce4d8963b33f07153c0f0dd88edf14587e378d103a9179e9f961bf414590ee97c0b3d05db7df968a6ab5e34798b59049cfe32bfbbe7cc8320dc72c43aaeac93889b3ecb3ef4123cb48cbed96c9f89c05e94883f4d29762b2fe73b7054205641d64999fc9106120894d52481d8593aeb78f4d6b252920c8329e838df2e3f1166b6ce5482ad75ea7a131d8aa9af90ca55a58334c94ca0fddbc9db8e5179f8aad20042aad9917a5f203d72625f9a6318ded4d1cc074aff937ac3a065371e95e295817851b99de213e0ad356c4ab3863e6e1519f03a26c28dc8747d2b65c54acc6436b184cc23605777c6133c15790dad22e0d275c80a7730879278eae9d5164d8567599ae933a6c73636e37a04a0ca66f3f39566d8e90bafc9afcc5d48bea1d93016a0e08a03a7e84a8c7f43d26cec9385622e2db9288802f1688b4e1cab76713314a36d021ded9dba0fb83441cc07e1606a58c27bb107d172d7014bf1e092d47811bb4ecfbfe80523a97f8eafe4240c9ee9a9574768d4e5e88794ef24cb4690e16ff0cf6d5032be47b6c68bcbe24b542db7b7f25fbaae571ffcfc69824e8c5851439a7def005a047990459b7f990d3a73f8b5f63a308ee7f6e8e45b9da4d981382b935a51eb9c892a91708e5220776686b24089afdff1901d8e1241cf3cd42a97773fe7a5939287763e3a82666d93da59a99e7c0d029ccc6a4cb5d54dad99d8183b4faa60505ce956a8f51437f580e4d1d2d86e13785ee192104e1bd8c6214a7b462fd49b943ad6fa619e470d5033ff3402f801685921975605f290e3a97ae44ce0daaa3d1ea59deda43539c455a5620b444593b750d9676859e50a0c44f80a65fa8eef391b4f12bbec4b1cbcac57e6feb976694f64b299c2d131baea0745bbc8b3cb08ade2d595775d412cd2707b74498b02e25e23178c4f65c500507f384549a7e48098c8373c3113fcdc0dfbd92c79511a33b7cdae74766815e049a707f81d07719d82b99b949b110d8590c06cbc0dd80c2eb5117416fd20cb90b3c6219c58247bb5070ca906daa369f8a39f05083ed09174052787fa0d44b4621c449dc20af140f4a1115ecdc9c12fb093db15bb248c0828a661adda3c6d2c95ef15fe9cd3a470931bc2d6eb1ab03c7fc7de418e885e00ef307c4d9c76256770b21b69bf0a548156fec0a680ce83f2a5a1a23b7e781aa7fa0c9057bbf4c35b50fb92f5df0d0e14acae136cd2482b68e7c5cd8d7edfed09e73f762a5b71cdd6efcf74681be173d8a057ecbb32dde80b983fea34e4babdc9ea47f89fbe430bd2ea4c70c1f51fdead807005db848fe4de08b049091de44729484281b1e7d2e1cc0e761925aa6e6869d65e838b684b1299568204e6c5ddff626dddf4fe8aff234e50c637de6695c2bde20e29b75ececb6a38d379c0477bd740c0078f2da24729cddb0c9953e1b0b7a2547f4722ba366804b08b6faf76c8d54c627b006a69d605ea96b2e435eedc2957190cb3a2ccd867ab26c83741611ebda338c63430e35b535b196c5332c8b467e4679f2909ced1996a85ec43a8e9e016d00a8a8abd5fbc8ed11fdb17a27dcabd8f462e406de6da81b7ce9fe1b9ba7d67ba71176a1d1159607a2c7bb0de3827a5fbf9414be212363ee95be0ad0077f547f71da29a6d077d877434c4ba11472e39318ec5262b0b35c16cd217517279e9c8dc76c64a5624ffc05bfcc4eb4c00e38f5d00053e7fee404d62603c43cb9e322537c11d465175eafeef52832c9f5fb304dc13fc2a58d678a0fea8864fff1d0ec90dea9032b6908100bbd8ca9ab86b17a0ad7c8f5580d2a4ed1eae876d4df11f9c4fb69de9112b960f2f8c0e1db99ac143895baf0c87296298891cbcab66cd2533ed4035717fe5172f0b7ab630abda706df54155fe518b8fe940be99706fce1e8159eeb7a479f2749832d2ae9539bc6c5a5027db3d597992f5742a860c7c0889b9c474c1241d711be491e0ea86b7cfb16957fda1eba9561399a61898961f4051e1699688d98dab25d33f0d6a6001e1f8fc52f0c8c338ce8503e5dd534841ddc40c98c7c6c54a8b75af1289324daf2e125488876ecb82d9be2a100156e4749d524caed8323e1b38e9e6e6c09a329c73a06265aa46290ff274de865d133bc54db5ca87855a11011ef5ea7e692876e9211ea76480fd53b98d2dd21faede9faa204403fc301d53080a27c28c80c0ed2c377a0b16ad9726332cce43a067f546dbc872cee691a284dff404d8d57ac114d3685ddc686ef8ae6e59050787ed4e4b55aa8ad5d76818dd2d5007016e569c9bd63d13e2f8c26b0a51d8b81ac6300af1c923ed77d0a3c0b5386ac1e623adce4b7e76b89adf0fc5f6fdfc6dd160de4ae64f9aa2284e57f42dea1a4b2919da27f4ede2b29806f1a17f2917618b64e0c29c8c74022ca695c82dd55385df21d7f6fff9bec5a5972ddca4264e83586e3d50c8f89c4fdab7070f63c3672bcbeea8d1a96e6b539fb2faf6ecb27a130911b8f21b070d873255a61c26dbe40e32fe49a8105544c1379d8f80060d7e8b303bf8cb02122fa1e012edc8bdd284fa156424e11ded89980d2b0d1d2bc2c281db22c2fbeabd5b096f8692c8760536032e0531537008dbc7747d3abecbd0cd01c65f95cb49fb62cd9eac3bd7f58510e5b6abe4115336b6768617a7ae96dcc6f1e3673752f6b08c1e8e3f0fdcfa4bf00a1d6d1bf66c7036d24e92e33343d49a0cde8ced0949e526b0fef90483c22014b25b256014f0678fb914cdddc19a102c4f31c990bcdf5eb3233408b73dd267a3e3e5a4ea19999ede77f3b99ab9e0689051a26d7daa29c7ae97c47b99a6a32d0f756270778b242346539408e12351c13950fcf56b202ff4145a8e18e5c9e9c72ea2547cd147e9a124cd4d68f17010ce79714240da70f99ca15ebcddec894953380a401d5bc0f4fd9e90a57bdf89f1f1678f6306825ca1c6e3161d2736bf3e69efb821cf786061092468ff9419d477d5ac2f8b87be35f71fd7ca77f05e14b15876230d6ae02e344fe075987f34d76bd1ee5b51be370d7aa4b61a4a1cd6e5fdbf03097770c5b03b83af981e25b161533ef4d0cc4d49f3a04aa8cf226727d2c43f96d96f8742f13e3165149491d63eaa7ba301268c2010e2ab804373d546d8e2b74f4c563759922b5a0afd55b8de29afa1d72c04b55e6c665e86d36d1eb647db6b2928957fea192428908ad4ce5a47503a061b8a1b66ae70c02cbd85c97a8ba464f348b05f66489e4111c57c7e6fad1ded7a3499080951535c10389c1a3346282c1bb50e04cc636af09acea77efb0e9ee23c25d0d13d0ada90c6c258b8520c9ad4b689d9e86689de9435418e759971813aebfc4f56e16a3718a1002647a737d5f230095b58fa4e9e5e4485e03fc6c965a659a31cef392caaaebec1769777d2569e33b2adb4ce9b473af0881b1fd697c5584c0b0e8973fb0eda6ec793f29b656ba279e992e16d1d1d0217cf3de6da1434c809c7b5a3f304bf29e82dd4d1b2447415147df7647528023408943b8e1d960fd6b6265725265f6f02512fa1ad8cf0e01cbf5b06cdea7f1126a0507eaf27b2b1da4d3325ae3a1f71cdc537362cfff98d7ddac0f0ef001c309df45363759034632cdd43e895ae20819293e6ffd9abca5ea7492e59d792ff791bf8d63bd27d828cadb13eb1fcd688419b7981fb86683232c7dda84c1690fea5dbdb47bc58f975efb15c13b8c74bbbee2420214c3e61a8ebe8f8185604dac1b915b9b9359500f0442a74e39e67c9944746702b2f0415006e1684fabf7073855cb9eeb4515cd39e8cccc8ed559c5600bba447c44c34380d3ec80738f3941aea05bc871e4e2e88435bbeac66b1e84e81d1916e1afc6df7cb8fe5ed54875434d92c05e70240867c33210e6daa2ab012cf3820598cb34ff8dbd568740da6fa77bebc276609c2170763aa425994d4e5951602e8fede8ceef815bf8008ec5f3364074724418a948fb05d3a93945443d7f9b56a1ccf4d77799e33ec6d0e9e326a21648aebdbcf7e4918b86f4bfec62a4f5690808fb05a8a1b185a446dc7261d07345db22d097798b90927bc7597637ba681e59c14dceb9ca286312dae9cc53e5fcca52a02ed06aac527bbbc66949da0f503d81dd7f68dda3d3c6de0356842d28e23c60c09750f66e16eebefe22e09ac2e5df7f77370f6ebffe07b946b8b479835174c70012f10538fff5783dbec819e39332bd0bf9b439aefefe27e09ab8046f1d751fea8916a2c4bc10e725949bcdc392bd5baae303316ef6df6640ed6bff5c270b48d76b8cbcba5db2e4add8ba5326e0aa8922938de414571b5037ec807ce94dec34bf24b75e1c0a6c610b1b396180a8ebbc635c8295263eacf9fec0cdb2e62da81ad4426fefd5637c4ce4b3afb4e6bdc1791a6afa70ae991d5a8d7d6c2c683e4f33bb9fe867282a48b630c48a2ad330123b2d4861869cdcf9e0d9b9501ce86b4be5147814d4b8db5dbd584e96ed5dd4c2e56e262b0b332b7f01bf9bfa589ad8cca29196f141c54ba95e00dc7d3f67273a88e98062a8bcb13e35c4f1231d2ff46ea78087ffb425b44dbb247b3115a0626f532479eb2b60dac73a35e9fb8a78a7c6dff76ca69bdcd0a3d88089347ffd3df79887bab261e321c737a8f4ab98a9b413cd820c7aea22324ff5df314c2c4e032a6c3a7b5c40d6554d0711fcc346bdc4713b161159e7a6b8a5fa598445e4161dc45fd859e20ca8ca8ad371b66a0bcfc5c6b51924f3e17697cc9f70157f5db9f4210a9c53d880a8a843710286fcf55e4e8d6e7258c69290b6b7dc5bee2d654a52067207d007b4075877365885099279f508a3758d6daed678db34dece169e05c33a252e36e36df6bdadde5bcddf6e2b6fad8cb75c8cb79dcadb51ea2d09f5f69ede964e60bc758112448f1aeaf78b14176f619cbc3595de9eee5b546af456a57b1b837b2bc3be5dd9debaf6d6ab6fbfeced0cec2d0b7ddbc27c4ba3dfd6e0aa176b5a5a6099f179be2223864a0d101cf56743a70f55c9ca4d8c71e566ce39e70c829571852b7dd66a32757777c71863ec4e39806bd8f9c30bbc12fc79b7cc1bb0bdac621df2bbdf0274581f38a289ce8b08ff70030806ae9a16b453e7b29a850d0cb2cc998f5d227040859a4cce8407df2e4c10bc6204df9f8d7e79397d8c820912df38664c1c1a42c4ce6b32010a4358d56800013430c8ae0db9dd0e721a3162675543c3611c92acaa451142457a7472806680e4a3049ac19197472411381f20a2d32ae295f33c4318943b95996bd739eb17f698a6265cff6f16818956756b316f8f99bf90ef63618fd7811dab1ef6782cbc35ecefc7fccca7fc640f77859baac1ceefb2f7318a2b52907d383794ad50eaf8b0ca4812a9a4474baff55e93c941f0e545b6a4a78690291fa3586203c92231f7fe1c07d9c32dd28d5ce1fa5721f42e0cc94a0d980116554cf1a174d9787989628915cf383a26e625cac7288c3085e72f94ae97927396703df6e6198a49c5f44d1424ae8991e0f666ed417635501fba8d60f667c9d2d4dd3cb99b99b9ab2042a25a9dde3d23297609d022e68c912eab66ac613af7121b59421742323c8647ff42706378ee32bc159f5ef700240788102038dfb2f53c2211ed6ea29e40247a1dda8986106aaebd144ff9c248e4a249eda38eb3dc578e3b17edd33ddd8a2e2f220c829c7222528f93f02bce60ce2d9711067da20b53027c04d2217a49f4c40d99e739860fd9e387fc22629c17cfc9899ff889334ff7aa61b886dd0681e12607f229aee25c8aab788a7bf31b274cf10ee2d520dde436c56b1c0639c553bc570ce2b02ae50beb370e0c0e867398d2213fc5e5a7c0e09e711a26d6a4beb0a2be9ad3279958286e8adb140f25e5260fdc5ec599f9ec1dc4c52ace1400c584eb9ec23cd6acf8f555c3489cfac28479eea1a2e0172aa69e9bf26c6471613eac41e238e6a82faca7cfc430523e9c17de4aba422039cf2312bde6d3f3314f3c9c50d2e0d9af471ffb62b825cb9cf4852f59f7caec16a7f6c99cea491c21435c9c15ab82ec60551494104514353f744f68420819022d764417f630be64b87b31ba2a64102723e84a5ec9a8e24a0f7b85b1ac783e44209f1918a9f9a173021357d4acb87b3e6448a8f901258b9a95af053d08f03a3e1d280258d55f0ab4d1ab9797985e35f38a4176195ec7f0d8011085eb4228c52f8050244816ecf26ca2533a27952e0633a2181dc3b2f8ac0786cb2ba66e515e8a682f3e37e416ab324beb6c346db3611d06b31c065f64a079d5e6f74bcff590b58fbb0c623bc830437c0ec260e6d24b7d8cc399cf28724399b3da22ce48cf5c6241dea0ee99534f5a41be90b9123e9439b20a34a0b899470f658e4a9c89d46d56b3ea9ad65266f6c57a9a7d59966519ed1cef5a69dce04e6f2256853c4c8f79165d4489bc332f1b6d9efc1c588539b5d686f262d66d12aeb5425885b9f858080e83b4f3182d87f285a6b71ea365211ca457d94a931bca9d971c5c2bb4b3135da874f185f5e42be23e1a4fdce9493e73e93df02a88c46155a9e4617e3dcc2d4dc6651fe7596fde6d372ffce1edc88bdf1a91916799c7ceba676d3bf745f902e699d73c94c5605683b602f942e69cb59e659846c45aa761cb4d197bb47110ae82d49b7ec30e56c9288a143d7500ece0869eb13a2330f11de2a7cd98418c31c658a34bfbe0f2cb0e1b5610e4c398444a89850c923ce923508f928f273108224929a369123ddfc04c240dd285e3c5063fbb4945d2e7af072e7e2976e9b4bf503a05410c8a7a703e36f908f4022ba4902785393ce6a5d8f9a87e9d86bfeac99f0c92dc9ebcb4ee0c2732500f49fe83ffe43bf94292f330fd896324acb339f9522492934895fbc2ea9541ac7e5c0c225792bcd4f513275da75f9d8b453fdf665e09a569a552e9d3ee17190c7330015d21f411a80843fff211a8083eafb99d5fbf30870fabf330a5ee97baceddf5ea695eaa7aaa7ac94bd52f32f82f349ab82f6e3d32684b3d30687d32683d16bd759217ceb7d5eb4783096e187f5cb7d6174cbee6d2eb749257e56bb1e3b8b7118383559d634e9383062bf496b9b9ebc5cf2407e68f0f35e7c1a5342ae5119075b157631e7b7c3ce668581549384fe29ee493ed1279433e8924f4b253b9288eb98a631867b9ebdc77de9d4de7a552c967885ffaaa9f7c36b4b8a91abeead681bcf624bf5e28fffa8917ced7b491c78e743d204ff278703ffaec12f902c7394d77dcb7c9fa95a070548cdd125dd4ed9ea0e4274f98036f9ddae96595cd4dada49dea248d380eb453769ce19eda26a5e458b8acc3469c09857226eaa1b6d5e832bd2e9d8026eea5f9b96aa59327dd2424f2641466668e99b9995544c0cccccc551f418cf6a5bbb7a9628f71ce3999f9d36a77fb0a337b77f7e77577c6dd188d7132778c51d62cbbcccccdcc5c1ac928f2812b8213968fb264aac2954ebd62d987359d31a28c90e6a8c338cbd5ba7d32210f94bf4e7208581a1a8607c3c41c5230b8e1a55efda5d4c4bd4e7289c3aa50065d8f0647901b4a1cc9d36255c55c4758b5fdb00ae3161d1bb8853a2b88bff36292a28f493e022519f20a9045e40b9b635ef50d8b5d44be80611886d5ce26f32dbe0e4f87ea9bdb51167e64bf940eeb9dcf60475f589d07f9d6a5770e84dd7adc59dfba4e8e6aa5e91db2efc21f6cba8fe37cb39d5beb9bd7b9ddbcd66edbdcbaa675da56adc76d5836855018c612a5d7774c28571c3df5e046a0243a2f7b080999dc8e6eefa161366f97cdfa6e4abd7ea1c9b483622877f34cde2ded5e1237f55aad6a52f4db617df39622c914475635612492ab1eacfaf1236e1e4d0a888b7d2ed0eeee116726519cb9b179b146dbd1303b00f1edb4bba687152978fad9a02f5c7a691d88e1fcf881221a57dcfae1ec06c16e11d2125d220c00644b7b83a6996f027c8343de4e8404d2c8535df48bb2f97ccc7abbc0303b76340c4deda8b83207ca0ab354ba248f66e4e5405981d33a148df330be388b6d2915682c58a5d1e2155f2f365e3a26234189c615b73dbaceaa55856e883342d2b9a728d83604faa9d01cac9a3a70f8fae9c7ee0606d925145f2ceef2451017ba1de4cf9982eb1f46a22c12e1108a8971654b08893136edd9b47b369ddddd4d9b677737266b90e5ce04916f40ea396786d1994d6cd239e79c73f69c33c6922d8a2bbf18527c4bae9c734e4c04a42c93d2099e0c8b4ef01c4199c680d26e1e224d69370f91124c2ce46cb57e10358661584f9517d11461b2ecebf163c62a3e7c217b1824c1954ec5cb5056e4760b8f2ef4ac53807df991a8b85be95e6feb6a9122b548d7917cf3a694914fae5b9745e48dd2471deb746c7f3f5944be706511dad94867f140cc4954dc39fae1e737a7e92e7c09e74b288df8c8223d457c863c70a58f8fcf0bd6b291a509939ef78f4e5c61e89dd862c8bf7c74a249162f8d3c928db3dc8f3cfa6455e7dbb6792994bfd9dfbe6edbb65169e4d11ab8af7aa1fcb0faf5b98d4a23d7bc76de756e4b5f28bf73eab5d3b17df7490ec8173acfdc8e46596733ba0cb6293aa9f3d1486e4ec3cd79f169ced6d9480ec817d8abc4913738206f4837f2992785b85288ec223684f8b2d1352312134bb651b947b7b0e76c31df8e36e9304cac59f1c0cdfc32282529942693119434a7924d4c564d2b9d4e326bb922c4cd7c32485d3b4ca68f7a74c6c99bac2fd395f75693c9f42263e48b8c91720aa13abf39e79cd3f473ce39e754c5cf8114afa75318f1d43d938c424c94e28c14483c75af4c363082152a5e489eba8c49e7f43b953cf1d3883813430aa0be4ae0031d3c7595eb7c450a4e90c231a9571f74200a183c75d49572c9145e0021e174e9ece9ea3999f073ce39e7ec8914888900983a1bfa49d13780808898d3617436f38a110891022259c2537fd1d9d0976682446783c51b0440b2c416d434e79c32a0f859020ea2a8a20a426a26b5c2cf2ba8f8797fce39678ca0bb9201985843690f58f0748aa794523fe96ce817c59c53658b9f5e2f0f6af0d34d3d28e2a797bc8717fcf05c71032c24f143ab5085d007eef47a4d0eee20063f9df44294b1bc980429819120b0d084162db1410828a57484a2c2e2828d1d9ce0a75f93832f3f3f3fbd8b11c226b04411254fd8200b45f081cc001186885c153f08c0872e0c20074fd0010f7e48f0d3630e9af8e9f61221a9001036504412176cf18226a8b0c48a4b29a55b504aa58ad086154f34399204e8c813a0e801500e52f084097ec6cc698413fc1c41cdc4829e7a76e534c21474648c132c1b7aa76a461c10f1810f84200525f458a166522d7ece58e777b11e7cc8a1a86c3cf5faf3d4af13453cf5a8f3d44469ac4203251593162a32547eb2fcbc01133fa75717113ffd9a5cad9fee38d8c0cf792fc5c29863479c91341448e20849d00008269658ed20c80b120caec9231113220e8cb87e12e1a70d4cd0c4104d58e1c55f48a20822f618a32bcedc78f61a09e39146dee00c988670a7909507cf8e2161303e090637bc4ba6f091ddc42adb3ca3bc3181a81e8dabb2b0506a57e2a241c8b337c00676083410133a39e898a06666dd25406291412c01210f1c3f9d7a3af4e44102f9f9f25383e64fa4383c2df78e4d6f5abfd40cd2eb87e3e9174ee74102791aa63e3b26070270a486b851881b4244051b59427ef5949f8c52c628a394198d599665998cd1492ab8d23329a5945fc6838772c821071c72e0d1e3870f72655d7e42da06ee8c4292c91397913078844166c23f5dd095af206c84e18e742efb94524ad9dd7252395b474a29a5f464a473d2d943e8eeb6f173cec80db149113f6f2fcc7c6642318b3176e5c145983b85eb5efc11084266f2e10ffe41350d88b6452d522c3a0d5baa39e651d7bcb0731ef38835114e69eb304831ed33af6258584db531297f3e0f969f1f05c16c4a69467d524a9d524a290a8db3490d4923c6163e8c31469619f4c3bccceb8a26638b6155b854878a913b7131c678638c296177b70b1414172f4e524a30ae89741aa1ba14a762636c32b495ea99877d74c664e91698868cb106575915638c2f96486e539d14cbaab659ae1b912e2d9d742e505ea4c0309d502995183256dcfb66b0b44083d6a0b10638a2489a6259d54c179c9d12def2cd72dd88744b272e505ea4c03055179d12f1844aa9c490b1e2de3783a5051a3540fab60500017001a4e1028ee68779e6933ec59a5f329daacdad0516df96d09640a95e103d686ca1dd530a646e5fb4d06f69d4e03459a35946101c519701603de96c64580cd61f214989ab394d6f1e0feb9219c26075199ee802853bd936d7344dd334ffcde301be16831006abbbe8620882728bc2a5743631e09c3ca5b38921088315060c0943d3be18560cd6172f525260c8ec30581d466723a3637a613299505c9c984ca56b329948a6516732994632a7201f66d0e9d5309967df09480d119608b2aaa1a5540c1547e5a462c4480180e58120527a40480c9594cdca2608ab9ac857140e2aa75b2a6a06106ee7216a353d19db52762c8dc20afc96e3c52a205f3b1d39861a26c7ab5baa57ed3b322403f8339a7063121f22df82ab851f974d9c915f4f420c5697974c4e9cd9bcbafcb8fc30585f7efb6274d1dcc58875bdf6b15717d711171f979f9310ab402ed954db54cab6202404923985c0e2aba386304c7cb58c544fb53df1892ed55f787c9a851d56c13882c5aad210191e5699bcba0c1156d12b2480069417c680f3d54f5e188390af6ef2c2e882e1859ee2851851d89d5e3e642793139a3e94c9f1314f30161192382384b9fad4c3aac8028bf80836f4b5263cf9c47c88b14a5ff8e26267a8faa955dd858b55b1614cab868935b59a563e2d1ed64e4e673899ebabbb502b4ef2d58550e862e863c3d458d731edf4b9401a207afaa14b0f0a6708b5c3aa3042b142ad5041305604ca000f0a8bfb10b542ad5047c8d037bad48f32368f34ed73f1d9b46d4bad705e9ba68531e8ab46ddbaa961a6dbd900fa758bec18206b6ab758b732ca185dec170a60353fd7e76a18abe8b2853056bfc9b88d421edebaa2cbe69b735dad34f03fece69dd83b2561b00a0179c06539674fb317c03cf30b509f8e7d323bdd523dfb6a0eeac9e4748bddaca5443a7322d9a247081c702b245bd8cdc5e7f46a613e2f2160618b0f31235fb71567cfc527c76b3bbd4e49647264765c7a826c5639867260e1f26a9815afee12d43032bc721de79ad6302ba189e5a32380c1ea9ab783370d63f1b6f1690544444f3750075a466c1ac7209eea3506ab1891af58cf571a10e03b58a579f59886c1bc7a7553ada6e92b1e3b075097e1415a6037efbcc8a08d754ddb5c5342068d8f1e3bee9bb5ba0cacf5d56578950d80b1baa5fa0c205c8d89cb8b4b9a87a8d5574fd9cd5132399ab59aa3761a6612a156384274502c22289f8a1af215d5fa8a32f2d5eda6c9e4b02a7479c9e854cd637779b18aa57e3a4428725258c7ca4696d0ba1ef31514dc58e3ccea5aef8d96e5552e494f55cb92f273614e83581585769860c1aa7ecc6911ab2290105a8f3916845539b4c9632e6c7b0109c1071f623a8f79cd24cbb6588539e67608ab1a8bc7dcf2d0261f5a228f5de143db93807efa9a4571c63e26ab750d5936715f6c6258b3c4aad4b1558bcc1c134622c923c43bfc6a1c2cab59181389248f10eff06b522cc332c923c43b14a31891e4110a632291e4991d89983423882b993932616704b9d1a3478f1e7d46902b3d0eb9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b9b999455861754fac6edc68c57bae66af66af66af66af66af66af66af4e961bdd1314f756b357b357b357b357b357b357a5139414937b3d57b357b357b357b357b35729304e29f756b357b357b357b357a8540cf77aae66af66af66af62c8f0d5ecd5ecba5af19eabd975e5deca7b80925e2941b34b4959469ebd7d26e579764a87d09e67c7682b9b59d62cb33aa2acee3944833c3ba52bac6862319b3c7b9d45dad4b466a9d51186cd169267af3f9a6b9b3dd6c54d8e6b965c1dcdd6b6b17333a79bab671f3599429e9d3471eebcb759de3aea221247f25c4a9ebd14c427affe7976174718945ed8499e1de5155fcc172f9ae58b3a6a5717b5e6102b891bd2a09413c52ab771a3eba15cfce22016e221a2a21d168bc562b158ad56abd56ab5bac8d5382e97cbe5ea9cde6956f3742b6bb55aad56cbe572b95c2e17d68db81394941997c5d3629f17fe119aad2ee25751511107b1d0105111465bad56abd56a75511775511775910b142ec5842209c921a2225e310f8bc562b158ac39d4e29d56abd56ab598c53cdc621f76318bc562b158ad56abd56ab54c33463c2de9e3923ff2258beeca84e3392c168bc562b1e650b7f0d0d0d01051d18a71b8592c164bcea139343487e6d01c5259e1be195d8f9c1d1a1414c4ea167e7901417793e95ed9923e2fc92c168bc562ad7031ae9f57500e0deaf1f20282ee26d3bdb5de950907cbc1723c07fbc17e82328c06d19e346825896b430ab7719e736298ebcf86e5340cf6f385f53af8f2f25b2783f6d84f74619f01c495df666aede30c2066689ab6b24148374e9c2975af1cd15430e208ab9ada0a4a92c4b5ac587b5e3a7751a83012c3aac82c2fa910920f5f5e76395458e19b3f242e459d3cc66cb186e0d75a49a44bba298c56ea3182ee4a90faa1dc1ca8171902849e6ff90804041c182ebf10c7e3b4576b6a0ef4c50908378c4c9e0ed9cd4b194953361a8d3ccb3239e2348d1b657284034da778a13ce2a9c3f0421e389e5da23c53b7503f714d6eaafafdfaede8d12d94e4a51410a88f512879e24d9f0b201d2af90bef272eab36afb5f60e2e78e29b57bd131577b4d5140c6f4f71cc4bc170cce5dbcd73e1560a41f1eac99742341d6b25a7715e08e4b9ce6df7a56ae8dcfa0cdd1799c9776e3f205fbd70be6f9ec963374305f2f507bfe96b22e40b1ae7d9c0e00eab464e2180f22387e1f9b0c15c477bcaf782e498c4092511ace4055ec8dfae6d317cec74d01962f8faed10c357b7a43b99f69c230af0f3807ded1440bd75d0af2ed166881f37cd6a289e0b71dd59c096e81f15368b73d9679c71466666ae19465f5e90a1464bae74ed069a86a93d42904ed93e7c04400f6e185d327e93c1cceb57b35a6bf6cd38fb8729e769e04ef7a8c519cca975ce721c67bf19b5e842a5163ded547167015be20c8d53faf6d5c852f08e9f0b71db2f8ae91467a6b7f48804762fd292471a6abe7d9be6ccdcd96cccf2b50f036d9bb3d3650de2c8ceb76fded35d886b4da738437d7636b20b35cf3cdcdc7eb586cdbbb3b1d2862eedd48b2c5328892450a0c4e676f350db3c2cc5399934f2a87df1934a18dcae17c4f5d29cd7e35f67bedff5e961745ef7eb450f07831b8e6df31c70e06de33cc6d64d2c18dc987d73698455da27b1602b5370abbbc9d4981c8a2e9bc7e0b9da90f5f67c4892e76352cfe7070f83db907539c4aa9077e6b639290b77733944bdd66ba29bd3dcb065db6603ab56ac0aaf0cf2b279cce63836c7619320f84d0af19bc4c06f4b8a3e8c4a7edbc298c56fb1c96fb7b75aef8ddfb67964d93e1d98350222892450a044751bfb0bef77e6567ae6913e1e65252f88929f747f251a3ef17c0052532a7d256f4ffbc26a81d54fade4691c8395cbbe8a753634c6060d394858dccd630cb9d5889ccca32ccbbc7ed208833205d75deb64989f7d3206cf959ef9dcaf5aa79e0f49f2707cc81f7921cb871227fcd10383d54b30b8a1f491edc995e7c316378c455f5dfa745f9377f5fb422bcb4bafaf2186beba8d18a315bc83083f1fba8063ab6eb529893a2374d0f398a77474754d0bf9a57718b5af5300e6d2695a92b0af9224904cca1a7848a9531a63acb5b29452bae0959bb0914552aa03f5385907f66fbee1e307a5b19b61fef0213d1525a9e886fed2317721ce54c7dc06ab76f8c159c09638b339e62e7146e3c7dc6a9efcd11d3dfbe8d957de4a9af83ad494f6e980e1d97d0059c1f0ec303c3b901b2ce179ec629815461e731556a9a480e4314c36f7c81584658f2c45b8f2b321853b833ffb8c65b7ecc920b9e711ed721f58c5517189ace57f79c84c7eeb15e7d968b76ddb6c6224195af18b8318949c6432f86a23ddc3afa1f621eac1d5158103ab78f4f8c1fc6952323333cb1a63cc01476baf9e2c62b0331965267d62d8c42636b139e7f40cfb64163ab81dc622250c7613e1047541b8612c22eac10716aa43eccc91c71a1c24b8f386ee4ad3305ff8126bb8da1ede516c6f723e399f219037e473ed3bef0d3372ea37b6b05f1c31b161a673ce713ef236c7dcee605dd7f90cf1bbcf8616d7813cb779e1fdadf342ceb1cab9c601f9cde3d76bde0ffaf59b21902f604ed319b6faa994528af90f264cae10b2e10a269c0429520f63a4cf4c29cfa904d2343118e3fdf6d9320b31c638638c31c619638c33c6202cf3170f50270eb6882b25bfabfbce87147a1eb8d17c8ee877c89e87938e1bf180bd7476529cf12157bf430c5fc35dbde462f74b8f919958b122d1903ac75cbb999be3ecf8c5193d4e6f6fb207e40bb38799d3ec1debb7126420a1f312070543598c59943aabef26df1e89621673884990927e217179f374f423186c97395287c18ef1631c3b51e6979a9e654dbf30353f1df419a72807e5bb9916395015449e6b045ea4d84193e6e7d82f063e44f91799015b800db4c8e04af78fdd123fba2871f14697241faa005571e443930aab4a71463e73973f60a35771e3143c3d8f7d9c8287c8c729788e78ede3143c391fa7e059d18f53285185ef3e4ea104162f2510e38d08937d3cf89312866a1ed698760d0ab57ea69e9612984f0f837d03046c6857c9be986ee91af361cdc08f86560b73f5c8a0cb1f8186309282074bd0e9ed9252a64c9952dbdea418c528a594524a29a518a54e2da5946e4fed10a088b1051a18ac8e801d3578bed618f235580c0e610de171d971d1d9f91aba1cf1d58891777d95916955999c5ac38741191d5669ceae7d355a3243be7a28c3f3b546cfc73e05819aa6bd200489f60ad2444e2fd48ac1edd36e45e130586d34b7d3bb770437f472c2940c27bf79d6faeea73ecbe4ec6c1ecae4b47519d6d6bdb90c4fc3b808c9e4b02ae6fcfcea3243645a3246647e647a645c3248be7adbcd258855a19723c4aab0864b125661417cbdf9d045890b931aadf7e18015d48ac71aeaa51c2003c5d32dd56578aca9e1734399d7cf5797f16f089720978f03377479b9c49ae9d4e994e1a1d6c6153794c9a9d152c00ff92e393e5f6b1cf9ea05d8892ed5674071a7d3eaa816fd520ec02e9039e6b12643f9f830bf9403a6bbfb77811aea80ec43b5baa53af6a158dd828a6985a855113d383118104508a18d45932b681bffa41a907d98ab81d44c0f1bea964a3d4ce8d4a379dec987c17a32d227576fdec97584c1eaa71f065352dcf2e6d9530fc3c8d5c908abe2a9c73bf91861994ca3939313172ed83b15b95e2eece714d42dd5e987fdc860555c44243162d3f884ad4654dcd0c5e7abdb225659af7e7ac596a7578ea16ea9cc4a74ab3b1b192271d22328866559ad9ab66d49b06c648eb0323b1cd775a3910c8fcc1019567421ed903a1b199d9d23602082018b38145d2a0c433630dc6e86f93008953819485c142f7536323c334d668a389f63a8144d782541ea10cd8c000000a315002028100a87842281301246a226fa0e14000c78984a6c529f0a64491224290c33881844082180000000901291191900a02ab68c34f051546ae191d4ac74ca3ecd8520a365f6a62b7d137999282253049c7ee8ddfba6396d7e7016a4a76075b875cde1bcb431e4156674ae0c44c7455f25e1c284cc5b2545f96d2cf9894f0eedb95cefe7cb8c945b3181d0de9f1c9e2f1dd52ec8a1c2a39b4664ae2eb5a21255f37ee59daab9a9436a74a00a1bfbe9a6bde8abf62a6dbd6d8f88ff81cb00fe60dfb2a5502dac0d349c2d5238b82ed47dd775c55897ab15a78b0dadac3d2d11eb30832a497f31b55d6ff4896dca4b6958a28d92e732153d1b367471ad0e91c40a6987e0264acf61fe2fc56caff0b53b5141f7d3ecbd0696639696e22046a00f2b700017833a8e62a819600165ef05b961cd37c1aa1ac9394d44a3813b43a2461783871c990517e0a1ded749bc40810b736814cf37c1c0c05afa5e7174398304554ac5f20a51345afe8793018ec98ec1f13e9b6b276a56113c23ccd463117694805e52a0b36f6a47bc1e74494ed2135bb61d58bce607094a9c63b5ed9439c4c2a9120162cf554d5c3019aa27686a6f0fee4ec6bd897903e0b788a30b1555669cf49a23189f1ed987cb717bd429dd30d019112d9b80228b5f12b9eebd66e9ffaeeb36b40e2b29b5acc6d8cbc8c4adef042b9264955c9673720eba326ed54d57c137b0e846177733b2be5d2591cba7456fec94ddc24b71f31d6138e35d00f50fdfb9509ebac78053a769ae4891bdcfac21cfb300810b117b7fba34d1cac924e0794f3e345af73d3eb9140ad7306642330c8867f8041757d010b78c0bcfda81c622e7732cb223e2ad0d25783d307a9b1406494396795c7eaee29529374998ab76f40d44636730f3c4d67d68147460bfc053f835554ac209e006479593f82b6b5a0b5eb03ad2ef58c6975adfd5b2492c3f4db33d5d53802363e49a78425019840f93902ede148be7b2a495ebf02c88dbc3d0d061ca2ca5c8db11f0ff33d7a447d64ce9c43b63ff40c8320deb37e90114a5862087d79505c12f1d3ec21c81907f371f9daa0764081f1a5c572f20c86fcf033f4578b6e0a4ffa36885fe208842b741acc0c8a419217019742ee656fe9dbbbe619d1441560b4fd7ca78766ae8737ce446508118da11cca7dc23b5ab50f82e1232d9674fecd24f426d02ab01b2a50035ecdd0eeba70caf884e82dce01983a1eaa777aa3d743d93110cb46b4d0d71359b43d1dd5e12e7fbc4864b3b5ba86d7e7fb3cb2d05d9b3e0b36fd9b0de36c0302a8a79f6677974718de505ebedd49449a2cf4a1c31b0c14a023accc0867d96c507c5d4a153aacfd6c4300cf7562a9e95811f5917055029d3281f52b8b7af1677876885d83d5ada05e5a8d620858bf96ca1a328a6bada37fa39012a050bc1593212fdd94d5e89f8c9a875824534ce3aac620cde669307ba8ff023354c87909a702c4696a8c1c8b6252483ae586162208d90a8f693c498233576c46a5e82afb3459759661f6ba7b4268c27afda0be3fa5b46516d39dc7552755fb306012ae07e4af8c6ea4ca13ac68bc635e2231a7d458dcaeb03f0b35811d01499b9bddc4020686f88eb512c7646260788a95f66466d1111db53081a200008b1ef30f5836fe48b0d1cfa73c240d4e2af6b085bc4c5c1d3f59a0d098843b1baf70ea60309d2009436e088e224a088fbf38c7793c01558162216372b9e5dacb7db97a8a3875b75351c97808afbe3af78bae042ae1c9bc5f83707b6bdea65786fcd39e5838d128851ebfec72297c58e1c215716264ff29fa0879fdf2c78e4aa45e7955d67075882df348544534742410df717ce299d1bff487885d219fe8d3d3ecd63f1a059552c6479cd6f9b01760745514e03a4ae4e58044586355334a69164a71fc29c4fca5a4dcec168d2b56a3b2c48a0abc29d044b11c8702bde87ded7e722dbb09bdb344413f61635e0fbc8c7964ca67cf00396398383973123f3d2198c10ec12071488cb8b06a00cb62a20f07db3886ac3b2f81981c03d2705485ed3f17859b6bb22f305d56e72a3c22afdadc5105fa516b17fd0f749db599d10541628841cc6ce126b2b14d42ea19c9cd3007f0eae54e1c52669b1300c77041d52a7f15479a5f106cf21b132bbf424e7b86d8294d318beb44ad6f9008bbf1592df9ee6fd6c6da292b8a1bc2ed7d4a325b7eeb446313fc88e9abd42c81b093c27e73edc52d45caf0d758f578962544550bd7088e9a92e85ad43a02053822e8f19b392bb46305318c890100a78e996fbb89c0740302c4b40825bac576339f393f2ea2b85a070c89dd1f37c4804a4fd8b3641ddd59c3cc8abeaf7b39c832ac7b86185654bb848d2e875cd636a0149df08dd33091463df823db92e1eb5160e0455db54f0ca0dacdc182cbd79d2cd5e589ee609bf470b65c46a9ed0820d45d51773efc1bbb76bec52d8c4639ac5f96ed22f878833ad1ce8154c6d6fc234b4bd05a0bace7412ce27c511bd90439474963520a94b291149a89a5ea78099f8a7978b0126562ad5b14aea2362e05ae9c57eb480b7cc6cd3bda482efd45b4d52141008a1b2d0bbcf46a1ca36a8f4b0ec78191cc2ea5d10b85b99bbe91bb33d1025b5cf741f40ea1bc6fcbad87aa3e2954f61c8ac0c3acb56193443d37d0bc4a32688f42ea6d95fba2b040f2a902c7fc3919eec351d53c6ea4a7f55a3c61f50d4a59bc34433b8ab705eed2cbd8eabc68a52645ea88f23720506341c8416f76f59d83e67279b246b44c5562326569922d183da3f8d145639c34101b264ac5c475232037845a301a34ec14e128518f2926a29a741c1930f5adc6e3b7bd5c40b6b1ac92d69d3f2cc4ba57676c2976ec140f6bf45057272a771911fcf132f7aa7dadf67282a37820b592b76bb6f2f2bf42b58d296a908b0c5808665521a78a952b0dbfd9a6aedd162a0cf18a8d613353f944b8f4d8fe7f5cd70798d7d8a026b00bd559bf3c0b71681dc40b66bb499a4fc3e298cc673cc24dec01bf929985dae750d7feb9ae5cb733d69a6804dcf20b2cc79884201e9b4cff033d2db0173748df05e0b507724dd2a2029c4af7a349339a83bf4c62e5a546c995f888676f0f1fff5c223f666403e6fc60c5d5d43b3ca37ee0ba0888af01b3315542c890198a2d0194903fb98b95c37aa9e93001ce0f902f24466bbc494f285fcd52813b704a47b812665cf52964f422387146627c8ba3bf0aa26f630edeaf54065bb428f730277556f969c3b71795478d298e363c64e72066ed26f093e4f43dc967d2d1c38d075e975b71e5b1cdacf52c19e226cbe0448801ed1f5eac69cc5948ed482c4ecd48c8a045e66d640af3d967bdbd9762830b276321f510aa1842f507d09a6a1fbd81c4b5e1186e285c3f0b3c6bee7ff00c56667ce04a3eb545eef0054f481de27c475eed38ad1112f041cd08d658af266b19a04ceada1207813c81549c8c0d6dd32de2cf732a859ee55fee41fabc4418144686e054e5d3f1f805c1be1064a9f29bf42ad911e140b039539f9ba61bcd7c1980a6f528055a0717ef89b7b89edcd36566f5a44ca051ca02e7ee85a01ef9eebaf4ea8932ff5dbfe1342296820144278a2f660ab9072b89e535e0dab4a048f61a339c48bfd71bab9f462872fc48e8d3ce07557e52950f5e9c800ccf174ba06010bfc2b3e00fec0c7c7816c72725f1278e61d7adc09b11a64c300595383ba28574a5c548d77cfa5857c0c7ad9799966035e19c56fafc6960845000f24da4f43842a30957ae0902de087707a2ad0c6597944ae1f63f727386c6f58781bee83da8e3abb798f06863d0695f1ab8bd5235dbbe2b26eeb0952b35c5d53f4d3acee4fb3e8f67aa52d0b52e7091f192ef3266b5c7ed9e2ae689b1ba89d76ad29a7604151ccd0d15d3d5004c898d24dbcfa306de73b41c511cf1a9f142d58d68da0ea983819a8584d7d9d0d2f1904a50a4fc049bb289974717bb45056960f9b4bfcaeb4ba86017f382e9bbbbedaa6b26b256e66141533cdfedcedb4b6fbeb582b959595868524aebf593d9d24b7446cc327976ee720809e91321ca4244cd809c60a9962153cbd729f248e00999324c03007ee9b25332281636935e366fbe743cdadfa14c672ed7d6ce5a93ff131e30d20b9ea1c98b7478ba553c0e6d8c12bf308516cf98fd16415fcf80accd3b775467cffe0a413c1902ecaff809a97e509b48751fb8cf8835f8f58f1ecc5e0b212a81ff62f5f0eeb6b7b700ab3ec412879c75e890c1fe9517f7f8715b345c0dd57ed988ce3f99597890dab9ca188788246a1e2c846aaa7a99543a0270bf10ea391d82217a720af000e4daee166e077a2a9519e94fe96c0891d09b51388b782ed378a4227a1c1edcefc7666517e06513ccafc0c7d5bc1a8d12218d397fb31c420c968da717fec86e42490f4c7e346249ea3eb2276d33c293713fd361af50ff784ec4e271bc83899834db37056da7e193fb29da4a2c9810198966645a1470e4a2f8340cc12a8965514dea0b149730df3b61950a8d722220dc431b8af150e0d5270387b82c4121521e1d57523c62bf692e7849a2f0b70e438df0ec981bb5fdf7f249b0245fb80e4db5ddf2c0b816fadbca72b293cca5b03498e91046039e7c00e6bcd91baa27b17cf2e15d7ba247fbfec7f069d1184d223133f11b86d9e1258f0d38167fcf87a29ec46803c1b843f51276052c05ecb0cf8ec1253c31e8a662b3202b1f5a9fd74d237214f96294bedf7f0ffadc51349e3a1c58a0e1f7f3c90fa94f2262037ae329a00670a0bb5fd3cecb12c5ae37b96c9061697ae469c51329ae29d02a7e352c2198e009c6c8c0dbd4a891ffc17c4fd38bcd84072fcf684b3ee41582535e1e60c1ace5879dd47339844f98a300fe389f38c7912976c464f62f12757ee1036432e7c9b94a450b3cc0ff467cc6c7b49fccdc0584d2836564ec0ca5ee636c40f08b3e57e333ab319fa30732a8bce0366a8d6376b5761955b16b29b697803d976e32d9048b3d16abb52de7d694e2af773f3ea4d0db4db3afdd503ffe1cead5250be5229ae68f98f8d44fff31268bc72eace5cc970f0eb2d7312f6ad8817dd68f477334a35f104016f7b64550ecc77a05ea5bdf8de39177544c5265c8a5660b0f03182c7bb2285d7de42d5f240faf6a4b4b8ae95f23897030348d45d0c6eec3dc5a9f2dea2b0a7112a8a0991b0048f890b91d807c0a4cc0d6bf478904f55852534f98c7e5e46ce19e5490e89f033045f36b2b7efa4796ba624da3b3d0d9ea7748487c5e8aa19ef1ece077b616b54e8b023ac52c380c04284fdcc1a179abd5624371e43625dc229c9edb135f68e6a28ceab31e5ea334971b96502cf54c3eddfca26af813906e81aa65174979f45454d7552a11fe599984bb6c2db77d40d34c401e70969f264db7eda4a27e4cd276b1d6c65f0a4e09e653d34bea0dfe743c729690a4a4a02dcc94d18d9401951b9e78aface00aa3922c77b092e0e8c3476c07652120c5160a315410c4391686273afcd755421db70edb9392aa303eed8bcb25b1b64184ec8cc54c37a54b0f4b1a6bae109722b148fe54697b1acc47ca528fa7a84878824c1ce8e92a9b22e286b83ec670c117a662627369e424cabf484b009bc7087bc3b12ffb1d3c007130f3b99c0dc10fa04b94af287b2e92fec7cb63aca49e90baae29436c6d2462852ff47f2d0fc1445538e2d5c2970c9c5c34ed179508f5054460895752eec34ce3be86beaa3fe471b41018ad7204605f24b2e6a68d2e4d2cbc48ef4ed3bed4d39386ae45aaa79bdc019b458ff472ee4e0090e82840c5e90d44f00114667e6e8c21d405638c9ae88dbccc138465f0de479a12a47bc893ca1def00bf22300e52f28aaf1c271d35831090a0b2a07383316675856cdd5c9a1ac7460982d3f05a88a6fceb2cb5c671335e62c30eec0fedfa03a856c9564f1551c216a18cfaf0162af7425cd606b1931b61c540642bec39ab89de170434523d8bfbe914db2a212a95a9c515dc99a357c69f208e22a5583cfab1dd6a1f731369342f1f1ce0f35b59f6a2ba96bfeca4fba53e8b2723569f2f0a31810f4fb7ce071ec410281a7bb462fa3ec036a54e2580f39709cb7cd3c98d1f9adbcb5c2b3f7e31f4b37c34eb19fcfa17e3141c0355abbb6b2ea03f70721c4f9c8a41f7a46c8fccae640d7e598eb622f31d3affcaf40690949f684483187dca59c786c77b63105c6001eb2f5b6c6598c8e7af4a1b253b377c3dc2af29313aecb80d8eb9efeb8900f1331440c42259fef29991c177c2bd5b498915940e399df8555c7826368867c600b1c071ea1c8aedc9fa164c0f22ab404d66be7ebedcf8c9b1734dbb555e3400748afd29d2691788bca989f66b711b3e28a834e60dfdc3a60ff921a8fd7dae8516f19c1a09cced1438524a537a138843d5a5a9b536d9b05084c13693b55a0d9def9e62750b49645607fc2ce3c6333a881029dd65c68d423ce9d291411e03442aef2db6517c059a71efc452383e21e1c27d0b8ede31e50895dc927ffa9cfedafef1db758014250befc62c009b7cb26100e14777f45500673f73b27e09ad7d0ed4defba1c36e02b7a6cc29b527eea12f436cf7e7ad6b92d8374dec5e1c737e81e320f5dc429550695f5d37a0155fff2604fe0851abd0bea2a80bbe88e482e5576977c42653f588cd45b147fb40a8c6d8ee36419d1c868ab6137856e76c77b533cb2c2c26181872a2d206f5bef4ed9b94e582d91ba7789ec1265483e2d4ec9606e3027b085fea5984e159bf8977b965e026cfd1dc5a80b89854e6769c1680a568e1812c3df4eceb6a337d1b82a2dd30ab2c1f490280cde47fa0980d7fd211ecdd20fc894eb27224325e6f6068390163a21b7fce587e85d2624eb3c97495a9d12d465444dc1e2f8091f1bc682b6463529befe197750740142ac01f2e0ffc87d26578887ff9e7ca0608e43b7bf00b65285c7a72b842446f58214545aa94d5ab9e8554ee35e9bd449ed8980301fbcc2b608e02bd8063f2626242adda5582a06b7fc72944af3df4f50cd70c48919eb7417d32d228d02b50a5d8a44f65efa03a13337aab7eaf8879a7f57e3735718bdaf86b66edbbf73859cec474e0778f63202b0d5fe2b29da7077b1c90421d82713cbc1b76fa12562005fbd361669532cb57bbd5a4aea1963d99cdf92228ab71c496d2905d87be9a5eaa0de2c405b6acf982915c6fe2aa6f1c77247ec8c5f1bbc60ddfd45d00a2f6dd6d0eea939fdcdceeba9d1fdbf2a9171ab1a61366fe08599343ac2799c66442ceb4ac1161f3892e6f7f6af3d5020c07f896fbcae068acaafee5732b80db325160a2868b5bfa6822b9450d17353612bca3480197874b657da52565ec06852975a7edb43e63fa90ade386eb1cd154d416cc5b74a1ed62ef5d7cb198e7d7af79cad2828e30c15ac8c09cb6c08430b2e7110ff95e620d211ca7051dca54e1561984dd98998eee98a5e4bf79aee8d228d10ec2d6f6fba93662943c92729342121d60e8b1d00175837a9408292bf17c2c0519ae3f288d606880598277653655f1b666a5035654ca7b03a5e5c98ec5ab3b8fdf2b61ca7b0b2c114d51368d15b0179bed4b2196e60fcb67efe80c174d963bcdfbbbf08d1b0d8385b6d93a0000466925fe60f55b50fcb51b13410362326fceb163cced4c3792ba59ead0f436a4faf24acff650a3d3edefb391718253a7caf98ece0f3c659c4ad897264b69148b848cc34fefab6bd6a238c8ce1d623f018b911422e88477155457b8a629c111e07bba3019d0b10e507034d118a7d09d68d1796340a49afbc61569be1d5a889c1bae696b56b3092dda91121b5d126c4488518b344e3c4c83a63a77406de7869b3aa5326e71ffbdabea3722c389a449f7c045fa128f01c94ccefc8f9b4607351fdcc0506d623ec3dda6f2c9752a5767b6437c0af1b16ead053647964cabbfa5cfdc052125485bfeb9139c3e749f477fbd42b81ecea1261f13e6fa5bcd8be0a9e5e0a99726bb82c9463f38b080a7d1fecd3d157faedcda800f26f405a7277ad2a324a321d8209df5f58657c2945153a05e9f8535f610f14bbfe2723f9b8592c86809c46ec32b112f993e01b3f0e4c24f7e08ee2990dba846b5f9407b33ed4553d119860c042f2d9f947040bb9bf2164c88d901fc71358643eefddafa0d4aa4aceedfa2711f3b2fdf4efea48cd2dba0836b740bd6ff448812cfa4a79a5e952432049e84b7ab6e74fa0faa07861b6a94f2f31ae165010605b42f1b1036a2bdc8b145feb8d0075e8504d76c5763629f5e49e77f6aa301770bdcbbb15549dfc25a8d68face7e8094f928b5fe56682a1418ed3eef083ec0dbf809a35f009d4d02994a020d947fb379f9104017cfc781c109bc0243b45254bc19f2ea43e6959c38b35acc718458e3c509f84e004f618c329ae542029bb6085b143548e68bd6ea3583a1180a3bbdd47a2006785b95ce528bec3cae19f40b1380891e33b109395e3f719b425863ac0f68127a8fedd8c6080799e9de3e834db296e972788e0552e791a4a4c1fe0bfe1b9d0fe1d05b7a671bebea9e86eae05b849e36dccab19248534aa48fca4a06d1d7abc303b423d9c725d6f6c8b581f371b1e46e9cf83eaeca82eb311afaa9749e1d5e914f6c94b9cc51b6054b4d67efa007789ca93abadf387a5cdc99d20ab2ed02dce3f1279d351464b5a65229b10222c7bf47dedde058599efce02b5e7d9ee9c52a704ba8a7ebb3e52eca39efc45f4cc819d3f5637f7ec208c3798cb963724004b40b5577e81c20ceebb538b7cfd8c095c6875ab8bda08cb713aa79b6db61833161e1283954beef93b5e4a0ce08f1b3cc8da8a254bdc2e3ef7335428565b3630ca10b2c2adf99b46a7b95bcb48a798582b51d08d906964d3eaa3dca448627fe6e3c8e4282e55e5f2d0fe8ec7c2371deb168a54598b99f0e808b9749e9c08c7223f410e54777e56342594c96457d6988819792ba658d8efb751526e61df36acd9a805398eb1ac67a82290d7719c31738479d44dd825e91de36ba7dddc2cb5d53f74bd843ab6cf86b0a8cbc0e6a96114891963deb156514c5c14ee177e3e698702f5c6f4046e74f508f4d6bd13d057eec510d811d2d77127d4d7d183f08644ef4fdf1ba127bcd1a147d8dbc88d0edf13a0a37b23a09fb8d712dc1bd3217013d67b36f888f43a6ee6f58cf49ebc1bd918bf7bc158826fe5869fbe521ad34c79a454852a85674c6238f41f1cd520f5f03006df1c83ec227880e2919e46e237b73b036e3c7e8f60d28edba018f4cc1907bc894c0f32eb1f630aa8873a1fccac3f8a09502f75664639003e260e412fb7befe3e833613e380b791f981ccea63cc01f552c70798f5c73103ea499d2d8057de87dba764d38d7bb82b48a2ef0ea81c0de6e301dc962907f89ac79b01e8b90e6cc0d42fe7c5ddc3de67d066621cf036323f90597d8c39a05eeaa8623fcfefe24ee9261af80b9268bb019aa387f958003765467e16f470afe0e7e861f25e003765e1cf6446106848fae992862433f589727b4310fb18147581acecc7cb41adf279a733d9a38d4c0dcff38d643993487ddeca96a75e13fe3dcd3bf38a0394ef17b6269c40f0cb5b0f4e6c438f47caf2b24ad9ff059c6ed0ee868fe438679dd5748e0e20f16e103adcc992ba47732f4df339ae78ddf7a4551beec64dd9d1c147b6cc5d49a03cfbb2fc3121949b50ea75ab3a41e9969abcfe4602057f4324a8f43552de5bd4a743d86cf7c2190ddf7eebf00fa6205efd22f625c674629f5705f08412d3cd6dd66718c306b98341b7e2abad6b744600f1c76c8bc3cb4e2bcaf03149262027599c73588c59151c795143580e444e857caf278e2dcb5d78ba3f4012bd46254675aba42bf24667a352e8381226d411acad468f10e1eaabc2a540b59c444789a308cafa2bce07559cd159712e4e662be5808521a1b2143e4cf3ce5fd74f02089f193ff7071aa5fca48a290d04408a19714a0cd79e024519dcbe99712005eae5befaa33e19d425834b12866931cd4775fcac8b21f6078a88f51e78feacf0591e8391dd87ffd797c9ba32503d0beaa77f22a3cd606401cd8e6ac897a4efe5c9aeb4e176545712f99a49d0175dc2c984dc2c5248713dcc813d6af12a2a4445380de1a1ca465b739b9e4aa496a82918e7eefc1cafb5faa3c0b644dbda57c5ebc8082614122df3ce0e01f5f815f0d5a74f439c26b3a0331a0c805c3bc00a23bcb71e5abb643919d7c08d6c1b5e6c0dfe4427ce108c3b5b6408ce9463c6e51e6e4fb1dfdb1375fe6d3897382f85b68c4bd873eb660cd03ca19b6ad1d3bea43292892400c6d372ac5d721c33360a567ee83bf8beef8dde749509cb22aeaac9e800a7df2aa1fc0b7ea3d4aed5120b497ce3a06479dcbca1a22ebe0a7e356be924563d7a4a1da34031456aa3354be818f4357a151bb1acf1505165969ba7864088effeab407ef4dba4c20ac3b40e9ba2cdea650cd89b1f2134a97bc066e86d9ffc575706eaca809794a11a4bb322b67f0ae322feb323a642e3ec77fc63c277a4f6cdfff5cbb4ae0ca8d69ad2166c353599fa2968da65386253d948f2445725ce891878c70c19f80410fac046f939e1c809d61f8d42394c15aaad7bdd834682a0a4288c1d73ecf30cc355a45c431596283a119d28d9498ec6c59f97920811bb59bb5a4d06d1a8d1004cc52e585eb5e3d06109491828ece1b76874a5315edcc2bd7cbee72567919c40eb3d7f8b968019931e46d0a849a16e6b176e685fe9d5d888b8f5e46905b0cfd0e2d6f5c63241ed49921ed9ace540185945b1ea36034b54a62c09e43338808b7581366ba9e948c401274e14394cb5ffcb890b97b4fcd82ce6c49d5cc8585d24b35252422069f7a790e76b015ac794f0958f2c3e49eaeabed0fa6d16b68b553236111805e9a94c14f1e303c1fe3cef18298a73b8e89bf00f7df127f91cfd14513db5aed4f1c6f1d30de60c25c8b3c880784c41448306b4b7e987f072841e5b77724b0cbf30605eeb501d8d2e73d6fb569c2f0dff974cb5461dffaf2bc3ba4f86cc45fa6885f37488ad6175aa143f7eab8c2efc98c4e7c83e21db1618c67ba4b6076b367d525d9fceb351afc55ec565956f8090a9090d4d152c41ec3b6ed8321bd788e3b6ab075003740a043f124ff118527430cf0539aa229255e51cb8fb2fb68cc88f7ee7d99f7d92674c60d7219f39a87cc0c9b50612e0294a940c7b5054f78c70cfb4e426db918176010a006556d70d75677adc0b74cf0b9451285f5d66d7cd50c060e7073f66176e079e898380ab50782d6f6fa720bbf19137b01eb06ce4532985a06e20f9dc294bceaafe0c4fd9cb6e9553359444944b0926297d0c61c5ee9a28ec03ca6fd6f9240cfd82aa1f48004d0e583c4d6ea12c379124a01ef5d564d6743fe5c9e664379f2dbbc64a50aef67ba19f38ffa93d2b52153927ded45970fef7295c1c448e817645e60000c1c2084b11ff636d83e8fae08bd40da260448bb9ca6cbe5cb62d9045254330d4a082a811489220034b04a41545064e7182d31abfe2364da80e31c606304683b4853c3c5b860686e26e6df509e00e313092e78090efa12c73a0f95772bebb6099c4afe1aa4dbcce82c933a5c584a457b4bc5dd10cb98c4007a535b17e504ad6d724a68f171407bdc55848d0e8a1fe8e8a14a527e0b22ab091bee35d64e9e86931e70c585a5d238418f7a085387f2792d4c1519ac7192acf8f474b063d1e0d004835ac3f0f7e806dfc6855d263399246f52686100e93d00f155f851890a6d6b5e5a85a0ae11ba27e83f197717fed9a09d7073bdcfe006d1625d0b331125eac04d3a1cf9e1a250594e382834ede15cbb0ca3fb98e3f0bbe4ffce48ba10130437920e964054535b5d0c3d6230dc8b944854b887278b3e529a0510dd17842c65fde963060dd22095eb77d3f20c90098f7fe4340d5977fa11accd78105b9aea5945ba4549d135bd82b7b0c41d4909b465d0986697f05510a8428941bc1eb5795d78182d76e1f31d30eebeaba92e3c971830b055596a832713a84522bc2ae886ab62e2a3eed5a575876fdac3e7078d3115282037f35fd878202fbe6add0e2532eccb61a033e8b3db914524565cab5705ff1eda7ac2e23c4c3aa224ea1f9c4a47944d954c072011e41b41d58c823c5d6f0efb07cc0436444cd08da6865e5e1ce63a0bd22cfac8d81c603f5f8d22fc6b2a5411d6bd161f2c04410e42d7a3852f3f0bdd21fa63102cd3cd1ce22268a081a629fdc4cfab9392d675f4c19eb1b4e32b0e51035ed471548454374a06747c962dfa66a26d0fdad6708c72db3e500933b15f1d85badda750a3cbfb9f7e4d6b2ba8f6b2b6dd037de1e6cf1b23f00d3ff15c9d18c457684dec6a22dcbb18e91d278ba17723632779d87b68a98faf4d7adc5ab6abd6aa52960b8810dce184c6428c9fa7e6d5efac402c958c517c5466b33e8558b6f7978e5ec1bbc2040e19a3a61f25ba16d4e618293a8d8ea48cb3046fddec41cffbadf148614db1c96f50f24a62c5e1079c6848297d7c6dca1cb234475e40ed4627e6765004fdeca9f0ad487d792a03a15a6972f03eaedfbe7570cacda5d24a60749668a081efd65e4dc38157e2b5f3dc2c93b3d2727ea0c0df722fde204950451abe3d0c6df508d87fc62a124436f55590412d4754339015a95444001a9f6965fd2f51f43dd6308057abac751ae261e0b0d1ea60b7557b47bbab10f0e85d2a27485cc5bc19c93048268272c96d82ac177fb72be601ab5b67a914d9020e13d5e55f84c87c77a59ffe47c28324e155e319fdefa9e402c3de15a044468299e541564b70160332813212faf6778aa3afb7e96ab632272224991f70ea62f4106fd815052420a891967987a022e368c70f4bc478eb92ccf46f518489f5dfd5297cf62417048e9e42b99f85b4308cd7df446207ddd22501494d83c4590f6c422dd41a114dab53a382fcaf02a03a69893217e48ae612f9984a511b4bcf0987c6d36a54002c17e49782362ea71a731705b4d7054a1cfc89a21c2abb98eb26512a89d702a6c391a0f6700da08c9ef2b885b84b4d331c544f00654c86305284e2474a07e69b9dc5514aa98d66672cf9e3f9fed19c479cacd0e3c4516ec68a90ba496a5d612b09a0a1b3134e15654df49528cbb2030454949159a26ab6070b1eec627e7daf469ee262c072d0efa0128312890d2dad16b264b4a715c1485db2fcf41b676b072bf961a0b99f761050efcef75b9b8d4038ee31b411875b7ffe7e3c91b0a8d5453eff15f4e8a6daefb45ecaa1c2c645cd324c621827a343000c1709e0afe1d41036e5039421270994f18167bf2faae6c8bfbb4b2cf35fdb6a880451e467a4327f8858253c700dd69273b7b87948ba3d38838377e676db9248fb541535c3c8c892fa5746af716a4f1d294740b6078c25b26849e593a62c8de4ed32221096e566be0a77a1b8d0d679b458b3d0e2d1ad0d648794d5202c1f2480072f562910dc0aa81a25a0ac75c222172a941275826d0aa534a02e38efd52104572202f6cf760e1bafc102d13d53b2da1f2d98730381d955255d3fe1e33c9ea9d9d54d78c37d56703d5317beb1ba8010f4859e010d5395137eab349fe2ad63362ac8d3be3c4379d65c4960ef9662f6b499bfa38ef9db88c7a6e659c522ab41a28b814a4c32f4076929bde014090a93b7088237315add7567052c69a83372a24b19855431ed214a2f6e588b9e74b9610e8a2668e9781db29ca5a866d886b8f07a4ff9f84eac2730e67e5ca22b35ef237d37c891ca25d133c8d3ab828efdf921505e8eaad03dcda97e5003443135d960c4a8897eaaba8960d6036aa1393ba040b51a66ffbc0e915ce2c67117bd5c8589145345be93efc3a172c15c78e5b0cea83e76f61e246e29b2541b4a8d7a904f57e10f63d6a43f74e363540f3b45d8d1ddbd46307e3fdc4b2b1736c8d9468e34083d8156458a1578351279d472c25d834bc1e99a7b9a27de19a9e36e6a929e6e46d69faf147c23232f5ed47362fa6e12c54c3720b5fbfa32337a8575736fa04874d03a8fe28ddaf76bf85f9c5821fd19ac663beae487eeded11cea8a5887547c06b2abb5ffc79abd9c4e3e7cf80aee549bff034940f9c7ea6096954f24700bfffed9d1364befa7485a085c6c5c2cd3486dfc6fa652c2c7fa14d61aa0cfad64c73a32c9dbda72e8d775361f3b4682cc566c7e3d77187c3d11990530c3edcac28dd82d19b4ee3b765ece5c17c5422612ab8cc40c8e6c272e21488da3c9a497abf15b1e6eb26fa1df191b7dcd1dc9a2a92b8dfbd2ec935bedbcbaa2245ef5a09394842863cb3402c64540a856c0dcf444baf52850504884e02be5b075bf72dd586a017cf62196b1b6e40bcf037782e14f3327a6b5414ab48c61b54977169fa54ef82b244d3ca02ff93fe3818f9730234f6edcab36d8c87cbda2e3f620fd658bb8f8941067df6077ad9696c0b203300184f011be2388b5699f0ee8983011c95488b3af7ed85a521211713d2520b04acdc9c24e27a14109f16491831bf24b537f0dbafc56f90b220e725317dd4bed2046b3605025619a7152d524b082475877a002150cb88d73a7194ee7ccd8fd70ee3205e6ed9d5b12db943f2573a3ebde88d0f5f68a1c6b08a8f9941f3dd810f30bcffcd6c7b87911d4cacfc2907221e258e1cac079df6408f38e7f1f4c70d640dbba159d685bbed319a007581a74080f342e262629bc4d407c9b07f1ad9039f250e3b3a7caff7420d1ca365410d4474b53f282bcc957fe17df2c889dcc15f97e1877ccbcfc30a8ec656fd182b6a5bdab34dc2f6ad033a7e40894541c2ef1cb901f4498be03598b6dc4506660eb7a3468e5d1915c1c6051180bc1bbb988722b6c28364bc15a416eb0d7f315e4ff1482826dc5383d47cac656ad366c40b773ed5a7d1add6f2310830926c2ad6489927a8ac35085942eca4b5369d7400d72b6e8eab7652872643423a68ad466cad66566acf65a1da7052240d49577bc1c83501b8af838d26f43e01bd5be3876139fc030a4c7270715c6a89fe9e51b56a106c7e50a3b5a4faa2a707225399aab3b6638beef0921ca68232a80fe551e124eda5c394fbcfaf41050c2908d9b604dae9a5ffd24de1e9a6137ba9b07d7eae76df041ca0c01cbed2bf8e8331a540cca03d9e1d51bc9eb715be3f7de584b68c78549e16bd398420a68e20e1e7e3157eb7cc8a7a53952ba69d6dab9a28429709b272d77d1ff2b3a283f04d79456c8c554256daaf1d86c8d7dba4f791aedc4a521cdfd1461d7cef51a56d2c0ded5aa19f14c77ca248405fae9fdb4ea5e0e48b58df1997affdfd4ce0a14fdf055302bc60fdf9e6e93357174a90a4f9d5eb2a29743296d022381e597edff5b66f21ae117602f0a53a943212c89d7104d3a8bd97a8743fe7a6e76b259a18459b3f1cbcc7a1c71848c774a9a87c4c8e54af68435d84ba94465d021a61fd305b988a8e3c0091d7cf15f0bd1bdd0b6a91d2777ce22213f404de3183b0135121baa8265998d383762c54c57e4d24d2da96e9aa778bc41b648cf79e5ee8005569b7820e05362ca8976d531b8342f048e4a29913a94e8c711b9fcc8165b049c0627f2cc4945c0125f4ceb75693e3fdce0d56d1b4389851110978bd15edcf3611e6a09a9ef40d2579def28513f19cdbed1464d26b819c66b517404cc1956114be8989f19fb461b40bcf8034ab21bf6a743550f3d06b976dc177d42701178cf0fd49144150b747c297da677dddc3a34446280da3d904839f82cba2c7b014f25a63421afdcd86ac812932c10d36479f74bb3a01c3ad121d629d16386909e2c051279f0c14482ce7aaab6ae46683df64b547fecc026bd2643628aacfb5b758c479e39eba1d9a409ae8166100b4f53d13710296865b5bc6b11e34cc5c71daeff22bd9c07ecca20a744f6574103b7bb92392b2938f14299e330a16c3a1910bb52321160f96e1534954ab018b51807cd860c6b81d6f5303ecdd152550715e18788a40effe4e6e76610c96a1cfcb5c924ace0e8b1ce8ed7a3a06b0fa7ba07081c489758489b078e317cf1e7b95517fab6cfe5262bd3aca7636ae3b205739b7ee808ea71398f663dbda3e72b5262e93b6373e268984db9de77bfacac9700e31e093c0dc522c3b284f028004ff4f45ff63c5cbfb4cd4b2803a2f635f7fbd7fa5c0375c589683503a435eb3c585b4e4d69014e545a7876dbf900297e177f4a573b895c614fc02d7fd21fec683075fa7026a5fad333130351e077e59ab06a3b5553402babc1dacf00de5f0bf31161ec06fb496f5a5ccaf1496e62f3b06ea879a822285d16851cc8044306af9e21deeb663d3203d395329927e151d11d7b168e5d940f7a247b49c833810a57052ea5e81144f0e7d42b36092bf50ef154169e314ef5072fe4d33ae4da98dad290bd1e924596e4aa215e563663473c4240b70c209f776ed4348fe050f0f2b32f92213ab02fefc600401c0d881d8f1a4dd58a8ba79cc4a8395a8fb0a0cee3c2862e10f4398a18c577a79386d83cbfa221922f3d6c94d71f60ea6e1bf14ddc8a3b07d6b3a4cea92054ffa2c98b6dada072d020e3d0991f00a64437bbdd05a3f56b049a88da1f31bef1a4a1de18d8690efb053e247381c5cb50b76c5ddbe0f506ab5c0ac6910c61d222db47c60d63569e3a77bbdbef4ddebd4afd916ada6f85bf082400254a78454c97b05939a0db68ef0cdc2320552df468b4db3a98c1b805747b4405592f1212b0818ddd82d584c9566edf6b46dba602680bc18d07c14644c2bb274a00410f773635d16ca7e308042901a944aa9b3438fff707720d3f84b34cb2a239930c695368989b827814070b1c83e0a0d012e36fe0aea734f672719d7307866eb3cde91525aa8b89088a721b586d93a10baeeca2b503b4aa9afa753976e7c4a2d4a63c92c67d867eb25b461505b0c5e34ba7b9ce08646c0e3de5845e3207b8fb8f10b2253eb7687166afb8e24dc7725003d6518493f695d5ae553903e847ac8b3ed84cf09f5f764089a740e86db5b2c45dc1638c9022bb1d70e79a1b15486cc97d127a9a05af2c9cf9b938daa25237d56dd10945d9f8809dfca6f898ae086c34d130654c6e2fe4a2786dc79abc9c5cf2365cf6a3101b8cc07c2facee84af121af0842cafdbab316b35ee169a1641b39d79d084eb2989116741e0fc7c49607c76453be314de7f594cc38de136ccd293325098debc61c3ecdec93b6181267feff5eff1b22e150c3f2b75dcf8ed99099ac011ede4e135934c45ae73253a98aa4a109ec092e0e84574c92217f2d9e6d7593065f1a32b643f4df7310fb11e70006e008d4a16727701e718b99d82d3925e82997ac7bea4986d7f82718615818b0a171862ffb488d1e4cf3018c05ae7979eefd9031c3f23422958c0f902ffda4271010039978a5962f554b2807e0902b409af9ba7c0d1a997a19dd7a4176d8deec6f73f066253a87121c902f5d7dcb9177e42b3ea66fed0dc073a649eddc83cc6fe7aa9a83766ccdbaa35d79329f208d4455c80c4a74bf5be85f216386c5ec56c8c468c32708d45332a8e8d5124dbf73105c628b84b8403b07b994f49b682d0fea9005ad91147f9622aaf5f929ab2eb88028056aa1dd6f91f4f5547998b33cad5ea8d2c829d19b94c5edcd78cd76cdf2a733e833360be1d6d4d7f95824a492e49255d1209e679126633142048927287e3ede7f4d934df54f492a5cff0251e0cf9a8d0d3fb864c4b7d8e60149980adc89dc5ff2c86d6fec4c43435b8addefdd0824637b81c9e5693c9bbc95bf7aa160361fdbd9ed0166756e25d5d133cb0f629d11eb74b91d1d0362dfc8c74bd6d9962551032ed6c03e014d7a7e78a25cd048371b6950ec64ea94b966b63a0775c3fdecf04c5f199a5704b8cf855008e442818eafc2bb25698bd6f39a6bd34f74c195a5a2f7e9fcb712860d250a279393564de2be7e7ab48af5daed2d2ba7c4b27319994ced4be98a23101fabd10aaa0109cc0b85b2849ed09dac4c023856b4ea3f3dffed681dadd6898d3150fd6486f5f40726686c96309102d3d10b7c11b0120805960ecd72c71384c852c1b83b7f2c8f2763c4afc30fc6a741c070ec0ccc8ac4fcb29d09cd3389b005e7c2c4422d27f06876942c34beef9c71d78ee388ebac411d0d19ae27a47ea12b1901017ed9001bfa2c8c6d03658b10b22426f357f275386a7559911630f2c4324966987d3886da7c9873acfc6b7583d07749f387ccf23f579bd9259fd9b7626ea6975b6dc7d83271366b1e8d747c41af227384d62355f1b6ef19788a7b5ccf82a22db6314f148dedd43bf614030259d211221d34d619e4fe9283743a52cd0a1a3b34fd04aa01108ea3aff5c0da0757a7e293334163b32a212d0a68e4c85b132924f9a96794288d53a1aabe90ae44725b8061686f5c09e1aa2f1e5ede616db5fb614c900dcc701b4cca2a0e4456878c16f68831a465f01a676f08690806d1dc468ae446811d9a19ce802d307ff33fed9a91a88eb65c995497e56e783c8b4ea7f753348a8e06de4fec885861c4ef990a3b854ce07d2cb51fbcda373fac1363d56cee50f216627539adcae54b28b79f40d5bcf9d36518181ae135c3e0585e48e0737e7a2a20332414b85a31251231b972a127e827d7324033be7bdffd409246540f3609b9e34930cec9e3aa0a149a15554ce0fb0d012e882b14ebad95dffce7eaf6ab1762b5b5898c31b02dc701999eb4d35b69d5e7796517334baebbaf009426c929765f890def634799b5d82cf7b5c9e4995b84c1bbd396ad7b345c1054f355343d2629e1ddaa2790ac0a08bcbc40f24b449625f53e5855c1aa4b0ef0ce44a7ef7db02a19061e0f9014052d588536981f2e8c202e4798084225b0e0d35e2036776f90232cc446bde23f192e55b9fe469a8a989e854b22aace8f386dbf0938d533c681edd300a95cb17d21b966a43d539dac267a22bac4bfa536b2954791803c9335ce93a2eb84c1997ac1db82821aa2137911674b1f54c5ecaa94485160c4c9c4b9ec5aa78d6cdd6b19b812cd73538caece124c7d6e8ae4b8acbe81efa25982ce8c1ce8b20494c56b302befd2d27ba0e441bca040c3abfa6d5c7e1b6843bd094a9d5011960068b4c072e8d2fb357d6bd66def86e0c555ad0d6da4b9cd34db90469bd36c330d6da4b9cd34db9046dd03cafaa546a03262048ad59252a0adff77a08d7cc410a525f4beadc73220221a6cc5ce309c4dd49f9f946399d6e13b02432c55791f88474a1b8d4e3f7e1b9b28991b782d14af42e05508bc0bc14ba1782b14af42e05d28bc0bc14ba1782b14af42e05d28bc0bc14ba1782b14af42e05d28bc0ba13cc2c6b843e117e0389b12720808280f66a8cda021befdfdf2d5aed4926b27d89b2a54c22d055f434b02eede1b33bd564c323ae8ad43919af508a186575085843316a1d5d0d7d1d5d0ead1ebd06ae8ebe85ead0b8ddbfc6ebb7a01b9c06fcf17d97dd5676c30ddcead0a13270ecb8ece7dcc8accfeacf8e229f0400bd2ab07f0406e6a9983cc7713b7886720cd09445e4e879a63560846b097f35cce061d5fe9efc4831d54d4f5e02cb003ca9818733cc89f4bf58982212cb479b2b156d50dff8e6788b1327f5aa8371a48938f2bccabe0f45d756dd99a19faff0c9ce1317786941f81dd8243e0bc64e69b4fc6bc1ef44ac02ba12e9ae23e3b41b9bc3e88675864216b318d89685cc98ad39278d9aa9c28428b2fb70b57e29dadf580dbba22f48431f6283cb16708679cf09d024822cb309e38e51b8d1144d46cd5f834c2dbed0d3579142695fd9814553a8fa31aa2510bd003ee4f74a43f69f0ebc933c8049444c108c718ece809fbfb1fdee443a95f06fece22a5a07293e1930231410744b2d62315471eb1a6903ed440200424741f9deeded4abb5d7eea9a2f068ecfa5f211ac5e991001adfb9ce59e615a18feef4eb36b93974844b059447068a7de85c41ab7c4be0a7821d3a548a9a70efee927504951fc9cb163f1239f1b9eb8028f1a1f4b5eb3bd2d4c2faeaa8d7e5c5cd0bed8804cb13bd7acdea44aa9c6fbdfc33c0925da78260e88a1b7f529d2d2af9239b95e0e6432bdf556ae2e1391deb2c0aada7f85f49740a7b3c3b3f47b8a69f778c2c503a58e8e8326bd779b91bd2dcba0b44cb4844e5ce7941d6658ff6318456283881ed53c77abed86dcf7229b38dcbf373d804d351a5561a693bfc3b7b7aeb405eb5e1abe3049ddb12017bd4849d9c4fea297cb2e746470e1340ca0f2531179f15eb0b638679303acb65aca912b42e5cba87cf255650100b7a42adf92ad268c5fb2bd06e06d9b87919bc5466d9cae21b4278f4ddd667442357dbc7a9fe648ab222ee0f1dc05dd8d5670d5ba4d223880305ffa2e4322e29b4463c2f00c485f82ec06f50718866b3b27c36e1a56fdc4d902497bc5038993e2d6948e794109541f3664a43042af643abe100ce2d236b454a131beeba492c8f77052c26e71ca94b0e820c986ec5af9612548ab91b8fa488ae50e2e565c22c28967bd7d71d034990888d391fb139850989cf6c0812eaee9ede1f28a9ee655784ae7cab4a95e7f7bf86a3d778e96f649e25be68fed9a96cd299457b5e56664c8c6560a1651957282befcd751a307ca53a9b034d0aa65ccfcd56d08e6d040258e03ea25ca7cc95f0d84b203cbc6aac8d3508f94b9c3cf905cf602f75627efeb66c11c61e795a1f3c40116a71ba18d328188b1525f816af930100115942820dbe14e44d45114e6808518eeb9459cef6932b1adc3917ca85b533532668ee0073ce4a5e954012880053fd77bf062c6fb66c49cf30bbe76c1bcca4be23e967199a898db7fffd8f0667c3b73337d825a9993a42d9274d7df083f60a778d63e9d5996b2f4b429b6c6049af189b29dc1998bc515c9d968a57d7445d5e0fc32a9fa50561dcf8163cdfe839aceb3a85e185b2d69de7712f82e58b8c9472c2d92a932b7226096888bc6587d0df41f93986645f981d1f981c307c0cf3eaaeb2d738d6536a2abf5d2fef582a77ac1a60e635fc162031672c9942be78ea7ce519d3f4a46e3374d2773f809b582786ecf6fcf06cc12bd0ce048030c959b2f9830e4c8673f76a9f969feb9c649c9e83852ff064ffe63f6589baa6a28ff150b92727b3f387f3a96b3be7cce9beec68b05645598dc3451ea06201bd7ff9ef84286eacebcc577cff0a2c152f4375e7bc51d253bcab90f045435c9da3e80cc3b996fe82822ac959c623453e71ca3352f303485f2d1d3aa5f0574f3e882be8072eced7b3160662399555ab5f10544ee31c6a7fe3b30f5a0aeb8ddb7938bb5bdbc93776e1d8dfbe41a96008ea2268d6564f338484f19113b15d26810f21ffef21f305f75e61b36470089fd4c75a551668849245548de0cdc93f88af28e8b7e060b816dc5d44688dabf98ebd46fce2bc413b4b0564a0b846e8fadb635038e81f97255795830fbd643b1b18af927b96e6382117bbfdbb653d01b93d32e74586cefaffe9fbe635f35dd70e03e4558205115ec93ae1033724dfe2d7e5393c43c800cdc18e20ce0946aa3676d8a4a8938802cefca41bd4c2d5f5f4c9a3bc52c93419ef254b8c5cd09dbda75ac6dda2b28c9850d57f52a81efbcd111c95361a7304563e9ae92a0366cc2555818e6c9a3ed003f5eee3cec341f050e3923dd90739311df92a6f929585b4c7b3c43c2f57ae516aaf6e2ea09bfd20bce08a42487780274d6f37bb22aed59c77f9c91c59ea6829a0cac126c42c358a102137aae97ad09982d9b456476a20550131821e9345c784f03c9fa8b34d8fa1b60f6bae096279c9f3a308c26768b4ccf5cf6fb7e7876a37f7b90143283fcc869abe12fc8aecfeb3b414b699aca45947947b0c32330b661b0767e1d941bc82a421af08563c64e0412d65a9bd1c347a305ffb5244ea305f64e67af2e6cab886a4c09a5e8dccc769f47889cf3171a3a71eeff546f541e6296e468e14ea5214e1ada44092c046d1cb7b17c1ffbe1058147aba8239a6537c28dcb07b193d0b3ecca35be6886dda6d0222d4abfcb610e2b984aa9e6910897b49d33a23dfcc56fcdac3030ffa7f3635051ad7868f835c8814ab1517a7286425e15f6ba8e552b5d12528149333019cb0d4f4d08608b105eded8c0839597c77b6a7af4cb052d970ca4aefd02812340126f336e04fd5db44d233e47a79c5ca4528ad21649d6fa73066e0544c5ba14d79467cd5d24434c6fbfc706639ac847dc54413e6ac955f6284fd04494302f12cbbccb41cec829f220568847cb27b62fe342d7200e336a4c0edbfa9ea0e9738aefce37d6a26b923657433321a8b21fe84f9792a26111fa7424ae713ea5ad106e87a46441f58fec640abfbd18b7df0c1f5f10032a0531f89e1032a53052ea98455d0ea901bac35b23b0957d5bf1cf814af6cc3c076fb2430a4391d35223961784bf71d0f523c54452f3083eb29baaff8b89dbaa803e29abbe668e4643dd8fcb8c8f5d7bf00a2a7dad7a2814b3df0087b7d129e8da5bd07c19e4a6265e257785806604b1b3d67f1940d4b62108cb25e92eb16cd1455c636626a23942215d6d394d512a21fb34477a054111993f4bbe050f85db058077a0dea8b956d330e5968004b75ce527770a9cb0a137afd56bae7d9031a05e55616a41b08ff5fde29c2a5aa5d415fe6749964b8d5db7e9ee363730ebb3bd7d56d9ec5669043e1a9a0b80ee079320c8e69b2403cb152f808196281aff8179eaa9daed92910d82f826f5e629853af0b2075203aa8f0c1920cdad08c526716957a4c5c4805ee733b7c725138835bd28c6614e1187302ad7664f9811b3abd3d385b81159d692d695c7e439616dec04a39bf3809e9864718e3be0b650aae57c04cc113eea2757d4c910beae2c65ce4a6757fb731a568c6a0dc067287435acc5733ab5628e5b9da83731b7f35c814b2877f699a05fe51e1fcc2989979cb0a1364b3a491512a2d82f28918e035f80c7f2647259e247df5cc252e333570fd920559a3980743419cb841b91c6a815e01c5f82c91b8075b53267e39f273946894a38b468406ba2011821e4a2c41ee42e88b89e792d1970c3cacc9e5206885a3762ee3d0a643aaac675426464b08f2376f06d45cd07cf09e121115a9dd92176254cd1ba46042bf5b131630460a930774e292e5bd5cac7d2ca83590291fae32700f0367a5076ca3214ce328fa99059cf7d281a0ea86a124281092210eae7ddd416059c8770455fad9d70759ca7559cbbb9dcc21b84395075e06367ca3d0bccc9a5003297018eb394bf16aeb96f910ed68c1c2ec314f529c3916c67f0f7c2026595d327db4acc01bdc1fa63042353de807c0ba533f9bc67f72a92cf867a196085c97c17304d2a8f010d3243cc0c3e7dcefffb7bdba959015efb6ac52ff1383f907b5d4857170b6bc82a415b7460df1180c1c7ac21c224d8a84ab3f60cf1dd9ee982c1be0dbbb2f352c0c71e99879056550bd882409696d2430c45eb48a566d32d2dd80e25becbef5ed1e712f76e477371830cc4365174c7d9f1ce5d1894c9da3446cfaa3e34b3598e362fe845d91d17150a6004c21c1ccd136626097006d4f8c58632ee2707b8370df420f404136898b8bf3829d3a15590609a7a02dcc0312a06343eefc6cedca6df040b07c9d1ff14f59e27c84c3d6584ab2626cb40b86a0d8f573619beec9b9ecf926b39d830793b69b4aa2841e0c02ae75b7d6a9d77a36d9c6f7f1c2e35926839d46f6e7a58fd85058dc760d7faafbb541197146e1f8f44a8b42ef1fc6a5a96545f1d387c72776c5e53d1df7fc24829c543ab8070982c2cfb55201c2838dcf2a843031a2000a5629ba884da7289897037a77b20c0a60ad95a2e0562fc2916710da767411b1688ed2bd97fd40fa07fdf9dd7252228f316ab54ee606de5bcc0996932e8d540b58f7c8e824173032c853681e45b506ce6bdd47bdf46bbc303d5734895698f487ffd90faee54eb870ca551a7fbb665165350216c387eb8be38a1931c94de3bfb3a3af9d462dab19270ddf4f293f49ae239afaeccdfaf66bb392d454f74282965a63e027f2f09c006a50abdba135041a1478758a77bf93fab3b1d1715b2c88da512e7993de07e5ae5c8f52925906fc9c2a66a7527669951ec08728e914862a00acecb4a74462c1e26b46c3f7cc89b5d75dd287b91906fa9bab83615bf2967c67bc3326e0625253f0fad52672586b7460e0614f1969bafb875b02fc488b1a9a4af06821d0a7c4836569f7ebba8c84068f8545fd366bfaed659fc0c0618ebd73df14eb0256c86b9a07d615bbd4b0c3053a8e6adbdf3da4bd1115ed9d54fcbedfe179f82c3a9e49df11587c3cba4c4de8c5dea644b4de0b81573887ba626805e5f53a3bffe6989dc26b60403f3cae4aaa8eb89bb63358c5b492d8fe4cc4b802cc96338c3bd5d7b25a01498ff023743737a7e0aed96ff6454e05c71de29f632cc9b1e3bedc4522932ec3f065139d007dd303e3dc4f6b57ea1543da812f0d4fc70353e552268fd1f5d8168b30b2098580c54d0ecbd316cc192837918ecbcf8d5ca74ffbeeffb8002312d74b31d5510326d86ebf05613b67e196f66571616dac28701bc5a6595d34bc2988e0692d9f0302606c5250a0dbe41d81716f59fe15f1ef9c7e508f8827d2b78a48c02192983801123df532694bd15280a24bad11f125f916cf72c250f9de6b8a72180eab7d175d3b992e4c766df65c1d692067a199c1bad94e80b0cc047881ad250025477065016c2a029c4012b29418a61d2266c4db64a926c0cbfb7beb997f771d28c44a460c422356cc1be1caee45c75149c2de7e37afa9af75cad523f09eda555eefe306ba1bba3b5afd8639e13cb5785ca24c095300cb8d44ce86edf70a9d25654b5acd4862552cbb357f4b657b47d11fc1a69713220fe4e33f7922796d1da9035630198fff0f8b481f8c559d26c6b41dc05abaedd268dbfb48eecfcd5be8108d997c348ac0cee5ea01b3681853270987ffce9d63a22e8dc32a46e91a51a761215512dcfc2a66f6e61b1ca3a2b93152c42fc9420b1fb43bd3e22e17ce30caa2ef16e980cf586111332e9451db943c4e0b4ca6be331511d4bbed4424b849605ea40f6bd277d2fc2abe738c85a670956c8e06dd3260f7e6ac1a2104759c7406b4f7a005f7828279a531f691f1750fbb3439d5ae05cc3d685c3acd1e77483c5ac433d4c604624a126f8d14bbcd8c623458da7a8d06b44a3805e4ad8a4767318630f33b5fc8b7b1278e7457f3aac0e15a98de384a7bcac29ec080aa82f54b56c14f292995c1787c39571fe6f8136a1e3baf2cb95254eec0819d50e1f26aae24f18d7e56327a5767f32450e69f55e84c4918a47e829c2b4c241d5591d9e164ed94c5ad5df4a8cbd8e638c5db635adc489b63996e57d52a546eff848b60c4f2f209a5e00c004083cd63aee427fdaac72907e4adbbcd5553646e33a08a3981aecda72cca8ab09c242f84c0b0ac5407411b753352f592561d8fa66f3e90033ea05acbf9c96fd22126ccaa326da1a46260fef69846b12c3fb269f0bb1275f67ad95b9a4ee49ce560691225126f10b81e7c37cb10dcbdb38418480701370028cbf9d3f3539da51462d599641c197583a80a66492dbfd90a7ce88b0da4d99fc775a5d4aba304ec2a4506928ca2832822c8b66da4c7733746ee71be829746d040e7c447da48f5202ba8fe1e13f3c1525b6501d281fc1ff349dfc238f4a3b05557ce5a44fad74bfb508af5f8f621ca8694811227ef7ccfa96a041e88a509e41be55d224878f986daae1fcea8816c1236c3db2516e12c998a1a8a049717300390804b2664e554b3ff828fce8c0a55749065f7c7a9915952feb6679a8e43606fa45986b1bbe70f4b9594d0a7c94d16b04dda83cfeb352864653ed657f3039a63814fa2e34f2d61abd62f1a79708937baa453a5016831aaeb9106de1eeff673bb3ecf7988be82235d7a60ff7d40c3e32f5e16b7a806174a4bbd11b914520fbd71681838c9ddc0634171de08939306d6ce3e2dbef7e8aafc7db286d225d0f0d23ed830222125d981c7feb01706423729d06cc75bd839c0f1504c8fb20cf87d5854b260c45fb174222ca056d5782216a8a0f02b22b7aa32ab1195dcb353738d86415b46e6ed349c71cf27f1f9306c3d69cbe41b94e76bde659a55e6d1d18a980b5a10264b286cf1dfd02f0fa8ee7c4f72d89e761c7402c848f2f9b8bcbfe7835a97caa24ad5e53ef2c80f5499540f9e8abe89ed7b872ed53de87bd18fcbd86da992c11795fa74df6cf0e9a30648bb9b2f4b6dd5a12ca7aa9ca7c6a7f60e4373b6c8400376948508f2d11ca210f833bf912c7f2af027338e33c27f70e2dda11648d2aa1ca147d3598d57dd44cbc89ca2ae329e5176d356bd94970357cdd0ea5e9f88d3133d888e8921244a7b666a7fdd4327f23d0d1938a572fb52238e821b0a21bb5159ed28151218766888727094290af00ea8b509155e80639186776c58fce36da8c27354bdd2324bfaea27274c70890de349de0bcfdde41f795488cbe24e07c61eace6a434f72b002249469aa43a60affe18b522819fa2c5e3ce0782bbcf7b72af6035a7870c1b3fde2a9e27e2e26567454e1aa7b00a47cf4729dc8004e14a01749fb8de1b8a50edb2305bd3189f6ce5ac0a51142b7cbf7f657dba11a1cc607bcad9e67e256750a755f345d289124c78b1d2addccad99d24568a414c880d4dcbd5e1a495120f11b449803d6d9ed6ca1b9d077b331c4ec8be734a4359f53a98e82add50b5710854838083427614c74ac4d5674080b6f709a3e0d4b6082885fb991cf74212ec2d5d736d4aeffb365b59fec1a1e779cba30b6348afa3e7ec40933ec7ac20865494795e8d1a3386b38f0a2d94651a820aad2159b0c419cff855c9f4e2c906f2979e232c6495acba4d7a103295661dd8200743ece1e34323f9974b50911f0cdf48550e3699e03b175087107584e2adfd0f157d2462f7fffd69fb2d3f0f6e1e6a378d1e12fc903da48cf902149403dab65e915e3050b2a20e5664526d51adccade55c2b1319e7f0893857117715777d46c307fb75de6d7166cff9ba6119f7d3f0b8c51a62c8905ccceb0028481582d2625da9c35642ce826692bf041fed3cd62d619486fc4ab68590b45d42f696524a29654d099409b108ae1abb971a3b971abb568d1dabc66e5563471324fb579936d8e0273bf1945e76794b477120d83ff711427d0b6f3eec6542a5f711aa2aacb0bbe9af8d211b24b04164230c1bbf9c9f9c175c2f2e39b71347a7b3a10a52e56c69554c608e5262fe1d099ce061e26683e8d2788ed05918bf9c17a064c9f9b13939b0d052d7edbbc893d64f1b442f36886c10b56c109d368836f82ad206d1ceb9759c125ecbdd19175bbc61c34f3ca9af55cc248fed867daa72ad9e2a0713443b58a283240e2dda756bbaba12af29b165ce964c782d68c806eed26a3e40c13db181b3f9a5c46cf31b6557f6d9ea3dc7369b79b9aaa338d07fe62aa43ee66f54e121d19a9aedcea874fb96270a90f3e0680dcf0f8f8dd6f024a1b7cee3029df9741e22b4dc652009227e42ec76aee7b5a01a8ad792500eec8bbca6a496e33526e4d853640d5744ab478d20d2ed9f37aae8faf91f10f5da500d57b35d5a4d499193cda4dbafa44dd535c963bbb9c125e3fbceeae59538124c76ca240f27648c322ef64993d8d99c32d9b232338aa278c79103bdc4d3eb82a0aea268429d7878ecc4a21c320f241e4e9c52478043e0e8c148e90d1b5c2b9902388a9c76d4aa8f1c08f69903c5fe04fb17f779cd7c8c2779e86c99a3d01c1e5168997797a6cacd4069384bd0997d1cdd9de56c8e0d9c2d750e34c7060d349b77b9767997c46e86b7b2a01dd88fc15b31a037f649deb282dab0cfb9cb0f6ff9862ead75c5ceecdb566f08345f64ab473bb07ff3038da77767f62d2d7ad2ed9fb408876b34d8af25a11f84e05339a037f2db7fce23e33cbb3bb32fc379743c3ca218018587cee6f010414b9bc3974395bb341e2274b6c40f47776939b4a03668a050ac746b737258414b2388683d74fb28f2b469b2a47f12d7916d840bddaa72dd82248fed06887db67a1d17e3f77767f561d896394f8f4a083e97895edf5e715d2bbda648237add36d1eb112d7490123d48dda46c8c284e478e6db67eb9ee9903bd5ed46513fbdc45dd1675dea5a96e7786d4cf5d4444afdbdf452f994c260435eb433046cb4a8c134fab479e7854b9564f95d37a830d66d0a031ab1979d49c7878ec6c699fe6c4d3ba49da272dc225b1ebf66b4136e76cf53aaea6c4e6e46cce09e4fa3aae3169f5e88d172c26237b4e7e50c61ce5c41e977d40119b6d9ea10289ca5f1d57b8e3d2f4d71d5624fa89cd8152b5940e7e8e1d36c735b3c53d8267ec9067acd04eec93f2887af078bc1f3f7edc6c8bf649793f2ead96782d3ccba3a1e6b96ca83a0c41ddc2dbb9d329603001c40e3b9d02461260cee28a9e9f4e0183046004a9885832ac4050b5d657f0c00219a4a0c483ce54830115921d16b564d8e20fac2d78086221d9a84e8f78f840cb0b78e8c00f0f2db4422084c5e547e80a212856332d4e6c9611ab853d06c11a7212c366e15ab010da8017424458495bf4a22505bb14831094a26b95a9f5ab6badb516819328436cdce9911324946c53a7474e4eb04312985ea07012f47264a73a3d7292c48991136bb584139bca04fbc4450cf6a6a2c5c57e91d2aa32c46a053b385165b1c74e8f76c8fd6cb1d3a31d62580db1c3cf3ed2a10633e57f79a40316473a201de93084ea881d767aa44310ab2c9bd5e9910e39f02083900e2e5cd92e9d1e35e14285c56e757ad4a4055aec53a7474d9828f1142f847ea63c6a324493235252700a7238515314e9e9ed941e13d3ab64532af5b8341fcbebf12a11dbf776dd3e0d4e0efb052881019636befd52ce39698f5797e4f184fc34c3680d1d4b2f8a2f9ac8d267ccc1a08833ec63af390be9c3bd27345072ad08238a0f2630628a5503453e9f79f9cdcf14ad144a8088a03c618a378928769706a1dd8f7979ddc104a69855c0015d75a1071c7ecccb6f80302b100f5408e1c4bc9c42e1421366453274c39997df2062622233f0e0635edee2c1608aa611f35b832b547ce6e52c1e0c4c511c31a7489c00cacebcdc3502531c47cc2d0d4f7420c8bc9c364103533c8d985315cc00c6cfbc9ceb4cebc2079af099975323b030c5d48879cdc0932a38f372d28916949882c8bc3c0610a6d8d2d2c2020b54a698434c23e8d13832875017d0b89425e6220c3e493ee0cccb73ce033098e26ab5aa1e0022d28539a41482256e64c01c526198f967b158170344507c30879c6e22a833109943701043b0800a98cbd56ab584e80106e6908c448710b0f6767139f1d42398b001c91c92bf78c2c47f7910b088d9423631f9aa248ea9636dd87f29d95fe586fe72e2a9a517f6cb88793d32849420f3721823537441316241cebc1c048129c23041451343e6e52924a618f343aee835624e8d28e261c8bc1cac628ab011737a451630d899976f254c312663841760e8cccb4322a688812930f099f2021d5410040bbcf8e2c9163bd4f48b0779b2ba25bb26779039caa14d5fb4b27bbe96c52afbdc2f9b8e0344cd9d1e557141cf242dbdb0cf906e7b6ba034db1b58c4fed89feb638d6820ae316974c198246e80de7bf1d5faefbd1483af391057bf14882defeb1f3a0977fd3e76dcae05608363cb19178cde9308b96b92daf2a76b0c669167d242c604b115e01ef6f59cc35670cfe79301f6f9f00de33ad66124f88665c0388c8b3d4129fdb969239dbefe1981b83870cb48d82bfd32b149f68a7d81bd62af5cae2fd63cccdd8b43ad31c6d98728f2fd3bdce28b3aac467a7dd26907c1f075883558d2213e3de694625c2a954a295dd2214ad4e187a1118db5d618c573224371e42790b1638c492a7bc13ec3aeb54b7fc84b8fb9c62188bf34c2dea4c326f204824326fd86a10e35c518c40f6270c418c43804b5c6d85acc73c6d7d61513d9570a07604ba5c230fcbc7314b68439655323df39dad1ed1ced1c95f889b3ae8f799b9e5e9aea53a9d487a9177fe7a87eb0531fa6c89da3ca4198dad1ed1ca5322f27d9f2e61e15550ba777fcda3dd521cccf1d2776763d7cf0c4b37354e23bdcd1ed48d9d1d99ca3f077766138005b07793852d93bbb9dddce2efb50562b713f98328222f37e8f49f1bdb7c5757998b4b1c581881fb0d613026a4fa470c308a3d76a6b36ac3d218a63f67513aa0424fbfae951baf644aeed6a4f9cc43a9eb8b6e99f15689bcf3e155a84547b625773a2b6ab3d514322e26705bb86544f19890b6a73e8d39568eff70c71317fe557a1d6eff7f4572645d839e15e9997e9f6650680a9ac8b54a463ceb786d12d3f8710e9f6a9e5b1f4a2f2d3146a18affbbab4a8531287d1453cd257fe5b3fb044ea0755cc633d62876fb22999ac87640ab21ec2fe9e42e5f5f0da93cea8911a20bfe92f2fbd29f50b406fd06c6d5cfdde1d0670e896c4b43e3e25a0c7dc7756974801443f7710fb74f518b494b2c882427de0b735f0724af24b2d618d34a07e6889b8dc498916930d44a34f0a98d2078a44df15563803cff4c30271bc921295aea128e421dbf00312f1c2364d65df106ab0e5295ac46c084a76d805976a69212409ba331a6e71c3e8facde0e7a50444aa0c494e84d8ab205a599450c1b077a75392a4a862b7743a25098b1a2465e940151c6021b14b9d4ea962c90c6c36a7f6e09af20219bc102212e94cb5b54b3b7dc9243e7d1f2530e04703e88d9a7a940eb5b4920638ea77045d7fb5640168cd9d4282ce77d0528acea1232af11a2da1f43c69e1365b07d3a25a8a3fa774466f8faebfbe0defa51ff2f8ae5922ecdacbb949887148770d6200b7c051b492502ca2246a40839bde32c824977cf8e1449fb047748009ed433fdf099d2afda4452745a2d273165ae4fc6116473de79e077080620520302ada99f96d76fca0bfb1d3295914d14d9d4ec98228050ef5fb62135b67f54daa983a23066b332682e8d6b7997510803b53032e012fcb6842cf5401ce0841c8a434930422b139679de1870975067aa3d29ad09402f0e6baa20c3046a3f034530525a388d48c942d038a1619455451ac6680800584092d242e4db8c0858399f9c5d020be4a29f65971158783e966c0bc180e57713d70976653711557d6b0cf8a2b713b2c65c0a105f4b208dabde0eba1e22ca8bee551d0985352c5b556fd7d1505c57c023f06d582ad5ff53f4ee38b6f2d4f7502b101ffcf1994f8a9d7bf5f4c71d4264f143aea00b4a7481aa4a3481b5bda04509346946dbf64378bafb8fe919734b6d4333d544f59507d901e53f520a852bde65485f1d7aee62adef2f43402355b9e96427eca65d5d1e9b5da1251288ea2b85930cac2ef1e359d2f69707214a004066c16cdccef0ddbe51f199ab139d60194278e7467124593b525d89cb306f5f0c1001a53586fb60afc3001880f05e2801f26fca03c072411e90c8e4d17c73789a2c9dad1447f333425bb430fec9c33fcd163878d8e0072f8608009404aa61c8e024091212bd4b1b1c1a9361d86322ffb76375bfe655eee652bf98a8f119c12a4a09d135476bdcccbbc9c9479bc6c25f3f490de8dda76669f1ed2398dd336fda38d68249a8976a289f493fcb339a7d6655fbe12fab44da7fc70b3603438b51cdaa67d5863a86dda88b6edf034ea1d0e220e8be29813c5caa35ee46c361b912119243d81529da854ac58da68aabf9a63b24406437d04c1134fce75ebb2e59f60faf14f536ec95f876c9e5e19b2d96c44cc5cb73fb26668682753e60970929479e33dc1684dfd717cf04b7c2481506f32e54ce23a048e234986f464b168c39b941b197b5ef6e0d395a3b8611863b9a24591103d345992c4151363b6180bb19fd812315d0c2926458cf7c2440ed6d3b1d3a1134f327e3268b0faed20f6908e221d46e2c9030ff8ce6d478913263b38df9b7c3edfcf0b3728763c2a44b1675b6529217941fc116f2214e24ea442e4dd6eb7db11f1762322dec45ebf81ab9f083fab9f89b36c4b5e411b8a0d8a6de440a489fc1c2554dd40f23d54fa580a89bd93cbe6ef86a1958fd6209104b7d3953e68c807284f56bf17dd1e32994693698cb110b3c57e624bc474312962482fb11f97d84f2bf6c38afdac623faad84fec27c6db297a61c29583f5603d584f04dccdcdcd88f260e7e6c1ceedd26e70c464d040c6efb7faed2054ab9fb8faddd5efb4faad7eabdfea87514aac0d32e8e6860c1ac5d96ed6c40c89c60d3470348268ec00e37a7169b1563486664dcc7633241a37d0c0d1d8814610cc0c698634439a21cd90f66a86446348f6af1267d866d8c69103c93ce73e422397f9176f6e6e6e6e7070703cb8b9b9d9447b68fbb0734b9970e86e8ca4dc0cd91c1c49e0f8317d37449786c3851b23dbcdefd24c2c38455cc1f9b139a6164645374437443744374437443744dd8573eb37243bb83bdb59fd4c269369347f7b68f58bf11722230f70dc68cd8eae5bf116ed20b125ce964a76724137b84bdb71f2a4dbbfc1f970b37303c0cf0480d58f8f1c888f5c278a000c21e1d881f03372020713386e386ce60fc711a31f1c448ac8626231d21911612484510f463bb91d18768e500eec9bf80e921ddc8e125b06ddd9cead8822b04401eedc86ba7d92ef04ed80f0bbb41d243b4c40f8ed2801e167fe7ef5d3210509dd2acb7832a14ca0d6a249870e48c74e8794532a0913a77d3c712019d9a36464fcccc7d051a4c3680f79e0c1cdcd78da43434348e864f05518337c5583187c8585e42c1ccbc6f259fd56bfd58f86837767f6777cd8b9eddc3ade81c1f4f6778ed00f7064a91c708ee3390e1d8e221c443a8c8c9ee828b2393884307b266f0f5d9a89c583227c1ee82ecdb4e2a688a41b9ccd31ab180ded21a326ddfeb8878c9674fba73d6494e488cb856e7fc888c850d8776e7766fe563b37990c0e1e8e2c463c6a0486c9349a6264486e994cb8cab2faeda1d56f0fe93da437d860060d1a331d5274e856bf9d1b12ba6e7f276773ced56fe7b6836407b7a364f5937273590754218a2919173687231f3a7db1079a63a2e5504b149dbe09896a9f4eb3d0f2d09d45a1a5a59f93a0395967cb9f4be34f3fef284de6a9ece967dea5fdd3cfbd4b7b3dc52d4f75068a84f8f5f04d3aed2dd949fa914e3fbb40697947d333d099fd3363a1347dcbd139d0d2fe89af74fa580b9aa3739f297deca337f6531df4a88f795ac5f124d6310b161d1142f4d0c4d5317669e9d71847e12192058b8e08217ae82d49b253a10cb795c7ce9697a35e56929191f9c9869011c964349bf25db21d0c11313719241e48a329d5c718dec29baf0d7c1b70d1eaa98054391e3a1e5154a63b729c1d8eee7453bad4127255bffcf617afe1ee8c890f4e38e898ace53ce0edd3f433fd2ecd9a7ea6df29f572e249bd8b7813611071e2142292d813b1a0d9946fdaed441f8ab77cad5ecb4a49e38741bc8938710a1149c42227871544224ec489381127e28e88381127fa0c70a31dd84ff156afe5b325c86baf30624771a0d76b97041142f4d0e4b58ff839e2c7358e280ec4f9e74cd4439325207ff17d67b483d728e362c38c979f64dc47a824934921b185f06518ce96b1d88927f5b1ddeaa9722d1ead4162490e6957920d113d9102a5d583d96518aca48239da325cc8f8647eb2216444b22832dd8bcccf45e6d792f9b1647e2b999f4ae627f393edea2984221822626e3248324832484606c885e376c56cc44e3c36703670a74ce248f9a9e1c2cedb808b0d7c375baba70252b57aa656efb67a63ab576af55abd560f9f98581b33391b386b63cfe4646464546faaa1ab11a5c68e66069a1b4d8e260718d78b4b8bb5a209aa11a586aec68e66069a1b4d0e3439981abb4f3e96fda979fdfe6e167aa3f6cd93917b479242e1f397fd5991bae0a48cb48f9385ec637f569e6b8d68fbb3fa53f374ee16754b94806a6adc9dd97fedb071ab8e1fc6a9f9859420748b17a61c79813924e65f3c0875c28a2dcc2043681256cc185228f543f41353686b2b3627f5b47e0c16be7ecdbbb4578f9a31af44e76c89ffd4387be316676f5398a05f27da0619c1f361a8f4827d5cb3fcaaec13e3facb8beff22cee12f3addfbdd56addfa8c4e8f8ef4562ad5d7138feac51b7e4fd7fca2f45fcd5717cb5039583d6a8571fcfcce22b112dc0dc3bd47f27475d6e358bcf42dbe42eeb917ae9528b14f88c4f0ea174a13a1505a473484cb31b11a8ade692750a0c64598083fc150c0d7fcc5639f7e7f4f8fbdbec784c13ee67dc062c82042f7d5638a5a6cf0c7e69cdefabc7e5146e66bbdc6a16443ec987ffda97938bc32851c740206e5ac0dd0d6f2324eef8c359224c8c496fa66cb3f413ff8a77efd436a9b3662cbd2b684da77c3f047af803ea0111ca8c4d2ec87faaa3e276b24b4c66a2be0af3e048a797d498d5b6926e08f911f1f5bda0ba23e2435cf96200efce9b69cc5962bcdd33edd7e875d52276923dd3e50cc2f0c7f47fbcc484a6c8c53338b9447e1462aa8e8a7f651b14896f5ab8721e8c803523a4da7471ec8650901459edf516f7a590bb6ed380cc3184cf4d2dfd3f56ffe027ff53d3dc65ff7c9ef3163df633eb1a52502398ffdabbffcd57a97675db1f55f8dbb3d186c4ca4ffd264debe0ebab4d77d5d921422fbd7c34821423e64ff7a21fc74c54c20eaff3da12dbfdf85b7f808b08f91418458a48fd8936490d7c73e48ccc3f48b9aaf584f4fabaf591e3a06520051a5b35e3ceda07d4502d11141c7400a20789db5fa9a9551e207019bce224fb8bf7addc32095f2867d62a22ea3629f190923dd997dce91c2bee236e988adffea2b3647a549fdd3be2bb6cc3f235bdb72961ab6fe33ff30efca48d9a7e6d99c7305ddc1bf3a495bb139f755affb212fe96583aac9d62426b2258804deeeac8ce1f63482355f2f00ed4b54bb304ecd18a7e6e69477b3681c6f858c6d48f67991a06cccc3a7b661eb3fb1cde6bc6caf5b2bb6bcb498b7afed6bdfa5c15efff6f7132351e9a19fb867af6024ebb33e0cec33ffba7d187fdd187f5df235c2eb634fc2eb63a410d8c77c8c1422e403f6312f24a65d78f40608dde278f4064cb7b0b7f6d262248cc44831fe7a188ff95d650711f201fbd8c7c8202cd4304ca1f3be081e9010a049f72f090448c8fdf9fb30d2fe344612baa4267ad89ff6d7edf3dd3e35ae0b187971f7872699af8ff9f3e2b8e66531f2c51d633868e3daf7d2bf6e635ee67dbc3e3642ccc39e84d7cb9edc629f184933d14e90b8ee813fbc1b88cbe5609c9a324e4d194e4d8dbb346aeadcd5bc18a7e62b081c6f0a1cfa25714e7037e806757bc518fe0a3fe6a9f9e2afb08e1746dea117bf4477663f86d33b8b39da2179ed974fb67eded5d9528aee913ac996e5d0d6e439a4e3efe92f0e2e61c6f0141348884c7a4240265ff7595d9ff0ba5fbbced952e3ba56624bab79b86eb5efbe9c7a5c76041e4404bcecb3fafd135ef62b77c1f6eff1093dd9fd9bdeb045505a9f84540b8aec0b7afe800da5ed3b4b002df7133aa0b84967f94f5c099c021efaf580cef28b377ace3d28e8f7c90ec20e1050e2d1d27304364d0f71ce0274fc3374163e1533a5e3d39f6140e79bcec21739f8b4c4c110d434867dc12f300e41cdea6d53a753c0e02289ad3b9d02460c68d197345a886054115e01ea29301331b439344a29cdb69ef8335581d29a15c0ae83da4eeb0d828188b10b3f78f553d509816ebfd219be0ff6fb345f3da4db6aad68ddd35dd79236027c47a0637a63026007ffc60becb8da08a8fa7daabf6332051a4607c90cea53083df6569bb18db5a20d184f5491d2c2c32e5359f659761a9b5e576044bf23d8a2c33ecb977dfab21f76fbe55b0e1b62ff254f1d2ce88cfd241b18b9295538a1bbd253898463d9e7bf6051c5665a53079f5f31ae2ec8d500a9000eb0256ca05b9fcc43d24f7aa5dbc7b7becd3961274eb7337a493f69ba8eeef4a84a129d9baba8353371b5c82a858191e0619c652041e9490cbab1944a9b6e238cb802046bd01458174485cb15e02e0b17fda4483d9f948a142db224f5b0d32959a88056723ec3fa38ccb5aaa05e4b4ff544804a9e3dc013884dd7e409294ce07620d3d39e59287d901e0b94ce90ecd14235c71f4b2c041912c610536431031ad4200cb3f4457acc125944fc912453a8b57bf6e820072abdc887545b51eef6e3830122260b340cf307c62c912e94be88e94d25d2059759a4f426d285d02c52a448890c62ba61c90f496218224ace347d91d217319145481a5e6bffb3d5f7d63074b972cef973ce600eee37df4b69ced55eab6d98f1b5610529a873381ab9a62cf2199de5121806a0e7af3afafdaa7fcf14d06f2d55162d7535a1e3afa6f08a9b96fab106229a3089af0c3b801a0c7358c225d315f158c3d0c8a46393ef8b90ea15bb5c76e312a718ffb8592e5db5016e0f5dd66a5083610e4bb864ba6158813ad631bc4920587ad165a3ff815c369982af1f93b38e592b30b439f5f3d3310cf36b1dbaa84e2244a0e707b9cb89c7ee36b6b42c9717ddb25f5c79a75c3038858289b9a853684fe349040a94c217ac8e22ac249a443090b06cf162c46b1559a2c8a7040b5850c002272e25c0f281d56fa33a9d82c54816586cbfdfef278910e30a7eccbd976e7d33c6f9deabf5bdb7569c73c618638c31c694524a2b0a075fb72fba6250d9985a7a44dff7edd72394566d746675b74435033deecc3ac05a79a1dbafb88aa48462f7b8b32d65d7760a34a702b6b4f7eda3406fe49c0bd0242d2e5055a85d85dacf7da335f66bb6daaa8fd6e43626eb0f597d6cb94195edaba1d65b4317494688e878a1a49fb4880a85e2eb39ef5ed405931bac7a5d08bb4062c5d7b59e55b9d2442cd4fa9601ac499b1b05098ae8fac992e1e2ca0b5ce45c41642bbb0d6cfaf5bf30f2c5db529f46a077764dfdf6f3d70f648874fa7885dbefb714b12f5992332cecd3c6e67366fd9acdb9dfebefb0393ad703e46b04d6c33c09ac97777921ad673d092e1ff331a490176a9eb8a9d3d3ab8710211ff7615e08fe1821f761fec4c54e4faf11f0c77c09f863c812eec39028f4133775f104e62e7ecdba4e277eea1f5fc459417713a963cbfc3f6caecdd5c926f06ad09de51fb426ff0baf2ef95b9cd5003acb9f73cef96b3b72f08a5a8a461a8874e654d55a7945d55ac54ae61051dd5ab12d3c7a03845a7aebd36de9dbf8636fbf56a1b46d6f44be165994785bbe66c5bf5c3c81d074d48b2f3e15519f0a41fcfb280e019a7e82f8973c5d5ef5a2c84f9a7e45f2f4d1697845a8b7a7cbbb3c5e8abf2def8c2dafea45eef2f4c4a32273d480d0f44beeb0a5e98dbaa022fefd62bfaa7691771fb52f109a2e7e8e1ef3c55f5714f90e196cd1ee18e1f5b0f7f17a98ccbffe923e602f2383c81ef695776928b4b49c7829d2260cffefbfcebde45991d852f5a20bafb8a4edf23aea0da616ddd273d957db23c6cc53f345924f02ec674821ffb027ff612f84575c4f77797bff847ad4734eefec511f83d33b8341bd88fa9ab32ef20c519f3abd783ff5b57b5d3ec52bae1615dd6a2fc99627f5f162bc6d9167c8bfc7a5c520c93024557f67aa97e1b50b8edfc2cf31459e3d3d6563d3514f2f8d7c99ffd3c5ff2cc913f5e3939c3e97e1a83f7ded02b1e9a6f18e5c7c7aba624b0715ecb083f64b02b1e9a6afa6cf271e130914fb127ff108613d245fb5a8a757deee23af667f478c5bcd6d8ec7ed8ddb2a7466ed0fcd12792cddfe5956265757a432f5c0d89724f1626a41d863ea7da84e281218b5d5c707fe7dd5df4f81d2b6fe17a6e1ea11225830f91a617fcbfbd8dff218933e5a5a3e08ebf7f7983d7e5743a1964417def7b459e9d89cd4e315740f77e448dae1eba8bd788c0feb23f9c25ffbf83d1d85eb43912eeaf3d1c396d72792f8f165ad380abddae4dc4b5fb325d58b96935d14dff4a51cba87e40e5bde47c196b5dda555db0e1f2f1b760556b0a5af120da1406bee4d01473b523e961e25f8f0895ab4a4f002426f5522a7dbb2534f2fad75bff4ed9366d6df1f81d2e8ed416fe817ea619e9a97522dfaa556f44ba3e81745be4650bdeb49583d0c29c4e557ef228508f970f9d50b699d3abd644bddbe8fa3138621892befe77f0cce957dd6fa09f8c37fe1f4ce5cc2af27fce3d7ae89f6d3c85ff8c38e5f3485266ec35f98c4e93ab6bc3e6c79bf6cb22beece6e0f5a736d77767ffa0874767f1b68f156b8aaf41b45d6b1e5fdfb3643291b7a21863687525aebb539579a3319d2b7328894525abf874dc98a54beab524a29ad94524a2ba594528a84989f925be3a73595f2c4d0e6d02320b07409d89e5a0f40774a6beaca61eedd9b750f8aa90629c6a4c6187ffd5bd21f525af89a3cf5d35c0a7b7c6e531b5ceb0fc618e31aadc902b0657d94129bead812638c31c6c118638c6d6c5969ce5f59bdd28c290d290dc4efb57c97b53ab6a46fafb5381893a10d168c41f0c41dd30feb83245de176fa18ef5a2b10561316cb035bc6c8ae7f66a49a7777469f030a809a4ee9a5a93568684a29bd3add2cdc8194527a75ba32dc85ba0d4ca531440dedfa7906a622901103df7b0a22083a33ba5898bc176bdbdb18df7cefbdb6c374206cc17f6f4c0c324a2fa594ae5431d60ac67afdf0c3bdf7de7dfa01e605bb5c2f94c6c0b8bc80624e3cf469abdffb62512a632406d3ad1e8684d52038e45385329996976d929f78e8d37befc53bd446ba3024f2bd79f743d67a469441e98c52df20a4b41c4f4164308bb3f3de4bffefcdbac65317a6b426d7b83a3cde10d360fa36d7d77ffaa1e61e006a2c07f4e978439ba7e30da90d0700a8a1a94163566e30c306752303183fd0bef4c463efa5a40af86903684da5f407ade951005b564a29a53b6c699329a59f29a5f45e4a3b7dcd6b7756750bd7855936a494d2ebc22c3bda3253f22ce1a1283bfd8aefbdf7de9818767d9be9d39bc12ca6fe76c6648737dca32dede69d61587bfd4d6baa290691d20f6888f1dfcac107a4ed39db4ae2ca01c538ecc9d556c92bf44aff71d8f8e90319f18f58e7acf3ae4f6cbcfbb5bbf273df5ba235a64e33b63f854867ce3a9364d7ce33fe7ae41d975671396c4ef8465d85da55a83d684dae486aeec5c3ae41646562cb526857199dbe78e2e9f1a7f0ac75b0764de42bfc7005dd4baff92bccaf1ebe6cd825903eca26dbdebb6388de00a1dfbf36f486abdf4bd23babe4ae62bfe5568d443a73be14c5fceb5f6410d8c77c8ff98a91416274e89636d427bb23187cb483b7077d4025dd7ea82f0f1be3d48471b10b5f9129d29dcdc3a2268bd70ee341ea16474d6ea6ec633c48fd91c184b99e9e60be66657c083d22890c6830633c8c0f7975c08cf1e2dbb04fcd8b7d18de9167adea33af2ee9168fae17592fced05c45a41147448f8e48e9b548f3bace41e7d0350ef4e8880fdd3e4ea2b9e8d2725078ea3988d6d8ff1b76ec614839e8e5e9a59954d9e5138ba15e0745a11e4502792021ae3bb3e1fe191ffb339c5196b187bdcad5ad4af5337dced970f927dbaaecd8c3304886e42533d2b75ac814198b910ffb18a903837d4ce7529012c1f8e63a1d884e87f9b7617f6cc6c3c820423e663ef6f4ce2e8c0c0223492125c47e86dc41bef05cc81829e47ab2655ef6327ee6f9ff9f54f7a4097796053549f23ff624bc901924ece99d3d09b1279f2485c06030d8c3c82142c8873d272bf0339eff497f33487a3959717796c5937f565c87218162fcf320ff3366f0e742e0c7e0e08fb561dfc5c119ae793238bdb39f0c4ecd194e4d1917e8634b1b721fb28f91b618394448257ad8cbc81a1465631e89a99cf8c4483052e87e50d555dd9d759c2cfe6562706a8a9703d98420701fbf1004eeebaf2711f05f1202af3bc65ddac5b8f0225d1aa662ad74fba88b84af74fb20c63f181bc11823612a78c74a31126f8c7f7c4c7cb3d812fb74fb9c0711f2c19f2483089d40b18fbdec39596de41021b517a046a814cdd5273d29119140000000288315000020100a06c482c16824cd0261ef1d14800b7298406e4a369648433916042908828c31c818020c3004194368686aa84100c4d8acf42f9546577efab151c12dba7a9595314b88a3456add2127ddd3fedc814158a428819b68e59b39e4d8d283d9fa6dec0a6f5795e6b08bfa55fc3e524b1ba48e21aa5a6b0c501f13a3ec451054096ff22201ac994f17dcf854a5a0ff1543978d88f6315f8ce51edf1c4d045b251e351c36c0269d61c4e036e81f9bce6e16d961a373c91027ab34c6af1d6172631645d2f4632d72518298958cdbb345061a5060eb506bc8014dcbcfa17a1b95a1cf7774ed259a90ad4a62251daf4db6048993d29a92b46781300782a8e74cf3340ad33ab255d9350222174d20563cb74628c524abc9ea8051b4deaf67b1d3e2a2e4f10b1b8cf3d5b8bdf225243b2eb6e2989d218aa50b5ed2c40676198001d0229c2fe3ab99fac6238381a13863ae5a63115171bc85d6d93f6ae12c5dc360151fbcb318417d8e4b193ce4043097ca12c066bbe6156ea0404e580ee0b1ecb463b8c9e092a098612aec14b57b294aa9d3f63275ced834c8373e498e390f9052e8f99af19f98c533b93600d244d0ecfe9804bca3e08ad4ae0110b151c9cb06a8a3f9bcade3d4ed3e1c192e404a0745d785f3d2af90e6e860b88e08d485651c53229299e66ca8464c0029ed94e97d3831f95c481e6ad477f76a389c1563a5702aa6a6a79b32108ed127e3605a7921bdffe179afae7d93566656ddf04391f35f2e766e411258548dd90e98d38af942d1ec5902057bf4649aa9a6f7a8f74454a49791bd75adec28994477ae1079766a208f8362dcc41b972ab76b6745b42aa152872c00e3b923c630c4b51207d44a2933bf754c4a6c8c394a7b2d15ed54fe434e2c81dce39881baf28de5f01fd89bf394c12823d99baf8d770cf903b7c13fb061b7d61f52d2428913ddcde1b55b56a0f0d960c002ef0f34f924931f14f041c4147d454cfa4d73f465e9410cf928c3a0d0009ff3d326d7841792e17ee91ce5c5b51fb079888636f33f6e0e89a19e812e2f7045f00d7072c5c4e3edad87e94b65a61b2366718547c94f4a9e9175fea9287e6ad1ff31e7b197eb0419636c3e32798fec457298538a40e01f1b3fa245c5b2014cab0ffc86a8589722aa308f543d899bc80411ce57cfabf8b97b4013da89cbd2cfaa47208c7d1eea334542be9085f24fce97a70d8d400f4f72928fb97a15102cb5448444f9b8597a96b37feb76389b4c405422c879b7310c97de13b125f1c63db13c55b1f6d7c7c64013ad31687cf14be3787aab896a97293fc24d23c65e63382b721da13329d0a463ee483e4a5580524ff2a1f4b169b1f53f967479cc2f4583de8f8d5c420b2d9d481bade0066522dd11cdd907df6d33301a9b5ea5da521e2f5737ee64275baceef3ff997ddbfc021fd9a7f7e14528e029dbf1ea8a34bd6d3c1b590162255cf5ca312f13a4c4fbf7f1694bf9ef8c13c4228835e5064ac358197a0f492cfc03d02693391cc9bedc64c8ae5e8a5cf7bd2bc5ff21dcca1f0f1c2e3816c4aa479420569987d81095c32be595299a31bb47b2214eeb4de71ee569198e6324ee1d0a61263290a11ec09d53e95a01ba88e19b77c87ca4af9c7ab79b34129754a73f21d95bf27b49f9092f68e4006652a51bd17a70eb2e782616a4c7b68b21ca51b733cc83aba8c008e2e3e76a0a5930d3b06737f198f7e35c649cd3a808f6b5c46c221a972ff8bae73dff587e2e4c7bffd294fce7c7c7a1d393dbfac1208a2da43bac3b693929209adacb052130ed0571031a2daa7c49320c15cb2369a109b188b002bfbc685b28f23252f0c7c8e78772c64ae4f431a1744f260fe77143566a3b28b19d58229b88207e1cd6807c4b532e3afddc871604f4634d1aa8a34c56987facef8f0542b009e809d1cc3b1183ab7901901a2d243ad1a2e99ae3510be3e1f9e18ee350a71efd5d0188a27981935568dab49d8052bd9897f1250b3c00a06e5297e6110a8fa4ee4f5125cd984f89e88cbfc56596d4356f0dde74b53b47821867ffb2eb40f5c08ed3ea13a93c67c38a353593ad266b62fa30459e2af4bee5110a9c52513688429ba0311130d54146b896755768765f298d12527243b56e7b93f2892a14c3b7acefa08dcae079337bb942716afa804418bcecbd7da80e7df72827c21f80f5e12d1e7889ab58dce64a619b2b2fcc43316c28080766a9077a1c06679dba388d0f40bf6178a51383857884c345be6b76cae76d96695cec6271c4ba2eefb70cbb087b630776e1e6377bb7fce16eb74a725ce4dce4d345fcf6819a4d95877ba831e551e22655f06893c9cf3e6c70c4dd8503b752bf4b0d382c48957e012cdc4d4842b1b60e87f849bd343b81376506d09a428792b505e16a143f735dc1c41525d65d815517507021d177dab3d071dd606983ff5279a6c535d097bd5c9a04f3387341e1acface2eb8e14ec5e09484ccfc619114ef13aa34782a869868f26910a5409a8e4599c8d51eb95a9829c749cb0acbeffec372d96efb3fe6e9bcc4a3d1fe934c5bb1cc9ba29412b4c7d74212aadc7d22293ebd9d9a29bf436b29f4a0f909e827ab27dea4c2e96ac84c693d124c7ad2a06e7f1574146e03b25ebbcd16977414a0d6c9f776bffe9ef55388332c68ec52501cd8cbe43168681adab60271d8ff5dbb2143a9e9c3151310922cbadec2763a2d4ba728e036443d3e09d34ec25559c8222d62ad4c7953bcea2714541db0282dbe7a517f5b4448c819281cb349fa247f626d06325b1ba4968c6ef7e25cf728a7f0e8ccf3d7ce85a0b3905ca652b2d47d7bcb88a4708dc9e999861bb9105f2a21b7db7ae45be48a5d6a5ce2d529002f86ad4acf60b005a1edf19bdf3810344898bb0d2f778d0d28e1ad83c2a567482f8d34f449c3b1f7f77f9c7cece69d9d2835feb328c76b653e2dbf3591b7a345fe20d08939c44e35814c315c126589f4d9af5799224508f398b948471a28964759b21711304383250caa0599f6eded1306c9f796bbde4d2762113acfb509f6309d9a993066937aa40d454d3b50aa554fcf7d5f06eea7f3f38f3604e57a5af50391d70efc77d7820ed9a2daa32f784f5f1a042aef8ca3eec91978c71564527c0c476c7800eb37015e54c86e250c6067010ab900a807b4e53082120444c921a303f88fff58d81799d06806f70b8d701ad48f540a1a15b355114e445a9016eaa5f5f44aa4e72350976bacf73d9bad4dff0bb7eaee1d10f6048900dc170cafd3a6aa86113388dce252692de91e2ceb87f0bbcd89745faf8a841bafbc4bcf4d7e4fe6a9584b6e38c61eff3cdc7960bab9c76bf7f80c99ee7a01c4ed4a7a281b94ea4cd15de36bb5537c0bd95939e8312723ae140345afffb9a0010e3947724eed2b73de3d7387bbb9aa6c0242757b05c86a28907db40f6f248896caa189cdc9f8fca08b4838f62eccd42985b6f756d6a07036588ea2ac5f74351b62217d271a56b13223aa9dee9dd0aab65c13671581bfed8be412c01967ef430614f48d2bce5ab556e1fea9b791d4a00f181a883340c8d69c345bbfe2e5df739361d3ed19a5644b41192a26edc2bf414e39e3efef9f2f667cef7041c23b65c05be1036c65d540ed750afa63ef0e743edf1d8e5432f4cb3678dcf5e2ddf02031d740b11bb8d1ef840280c281966f73a07384f6bb040a214ce517063502f450cda6d23577a0579b36857521c6929c07adda577ee5b419cfbba5bd916b6fdcd9ab8cdc5bc20606570cd621ee0ddd5b6149e8e059b5d0122ef65af24b960059e6e33ef1e5a449748769f37520d35182526e7b27773839ee796db9ef18516a2f2f73fb83bced84a4eeadb8dec82644229c70ffcc36c8430f7aa03c0af5e7d15ebcc5f117f72c2475f5c7633f0a35e1c62d0001443a71d988569b736deb9586a651bdf5524c0a723ce82b390dd117d569605891e97a69fbb7adb411c39ef0587bdb4c7141f428a8c5b0903610fae8426d5519df1805d676882ecd6dc7d8f70108b52025d141085962cd2e3064535dbdb92021d0c806702e94d473bbb9f25f9a14ba68a8bfe0b29c58ed572905072274d4b4ac0b3cd6073ea655c23920d87aab321697cbc238996c14830262318050bf7cebbfe369ddc4ac5febe9c5b30103df4dda35e182469ced96ea05a8599f4bef21c82eefbc21e9038f9b31c45aff77b8a1c6df1d0a7c990e2b8f99fe4c626c8e87b98163a5df172ae906d6fa07547a07244fc837b0f5be861050cc0421bb7e24c192218621e8464d622a02aca5d3ca307524f938210c0e1d990408b100f4e7207c076db46f00c3f05c555f857d1c80be2d100d9152a7898cb0c60d171250312ce10c91fc6af1dea34b4f474d71157169470bcc13034d1e1905244acea926ae5c96df2cc97bb3712be994597480ba8715507afa9a595b7d4547f274bbae436ca13336cd99709fea3bfcb148e8e935cf7db6e2c9a2d4728c7c69b14fe8355ba57ba5a5b9122a59d7d6c248ddae10fab1a580ac75c94c23ec41cb17a2711d1ac328de7cebe6322664dc5b3e20355a4026aff461c0c93ca162bb42c16f85ca8d753fae9fb3f610e686f3b2ef9369a421bbd586ecdc1c6584a11b5ba026ef3d56a0b5442ba3e438b924e78c7b6c16fbad3143c4a036922d139ce5f7ea899f352049d7aaf61c290ddc6e922dbd6eeff8df24337a6c5cc25ea0f86d9615512e80aa2e9eeb0401bf5a6d5acdfb828b1ae6039315a829a3f255114ffa9f66445086b67cefca5171a90be4d5a36cbfac7d16a7cc77483064ab7a71e1b6c28505514561164e583c9ebb61e4fa9c34126269871f39216bf2a600296c57b4314a3eda5c5f73ce37c25a7aa78cad16ee1841b24a88bf7611358d41f72943e14ce4b27126cf0cd4fefdda530546b13da96066dfefe6b76d83555f586100abb7509a724813eb854283247f6e8be3b24636f201f22d54bf31697212f2ddefc36f598da8734840195158f455c7fdddc7b0a61d87373a61f9d919eb6ce4592a7fcc1e5221b5b0240eba510fb601a8c4447300564000a3589ae18118a4f4c8084e480df4b7bf036ef43c30ba88d3ca2806b332f480144fba241516d4c8bfbdd243d1c22e01f567815754041bd587037c5b3a307574836b751de4f43b030184bf340ba3a1099763b1a8e35721f5ba9756c12b1f4df66a1b275ee934b6729567ad008bd93123f05d9cc55c69779cc934e1e313831ba78b4ce01c4dbb8fd82f710900a1c98b66b1d8fa85c9d10a87f7d892ba683d2e6c94672c1eeed84d20143c2cd9fe434311c6774eb49869c75b93024989fbfb10148c5afb8cf24cdeca25e21ec33118e347608a38158a104e3261a32e4b70f724222f981dd38fd9f231f99537618d08d4fccaf8d0ad03f4b01fa9aa31c09446aced4b5d1be9ebae2171998d83accb3700ba8c3698c19966698087917abe5628a33e3357b5e07b9ea97f58980261b1efa36f62df257778bdde035fdbe8eee37f0fd4aa0e3c9d81037de8e56b49ddaee1427868a8bb5c849ed0bcf394839f0423508c60c5e0b0d0b2362be3bd06b65bfd3900d99bef5bdc6496d78d2ff072098ec66a664629420172669558ef4b075a680ba805a08e52ede9dda4d50d260e870eb1ac3433a26af158bc511f0a648f0e8e571180213d87266a5b70d820cec0267c937a7a3b3af515a575c4a1533b2985894a5e091e4b68024735dc820629cb40b4dba834bac47990de18b7a03d5a5d548e9abab3eadbfb0908802c0ce366b5b4a7b2a9a770192d29f64ba5f5a9cad9d8104b3353afa2144d17a2cc00ac6d657c866a675c3ec0e34d12a67fd9e360ce9ac91d75a73a90b79775271150c9717489664b1e5306943b311f2038bde865a7934c229f0d301b382d11e143f71141cb98b9ac71938a1a5bbd6c98e073d1f613674a41206bf49017a76183d980364140acd3e81d770b971782762707a0039c13ef8f2a5d8ac8e1c187db596c7981eab0a60c23ca4fcee9212c984ec09324d046eb9f5719749f48910887fc2a1c502afc38ad23a924d206f3f93067704d58349b67dca85ea3a0645b4b5c8644ddce37717fabb1b84af9b02e852b12326e92f4a668118248d874f04b373c648c577dd9c8176fb19c7ed7d32e26bc698d6ae04b488831ca01eeaddb880daa75d831d2f0925781bece3692a6cfa01d2ce3656364a131a3f17578a4c06ab5092e489162cc7c9d0c2b0e7affc917a5823a77b9250818367c21944a484269e90024143950dacb993dee27a12ad43c4ddc06824bbe56e833755743c7c0ce7a92139a1c16b02f38ca8c808f00bb10f8bc687ca465e3c6978176353d9c1dea2ff74b5e938a42c57055c8d8530c5fa25c865b505a215e7033c6267b86fc9d025254f94d88cecee6f8f242d15eb43a89f3325bf3ba5d20ee21b7bc1c14940bc6c5e82db2e526b98156ad5e05a6555d73b209fd80840add2a140a706927957818c23d2871d6e7ddb8067a8d2fdd8d01cd051f797a11343575a6e5932ac61a31a27698d94a46b45c435561f419e73f9d26786c880044493bb8f222b17fb2ad6780a910d6a1129ae5beb150491c55b6adab285d042155e1375263478ff0cec03812cf74833f873b9729a093bef595fff81a785166aa92656abee23ce186aba8452d1375651632d543d2e6e054a757a91d20ccd569e82c054a995ec1f9d309ece72e59a4187ca6ac3dcb6c47d989f28c659842215bc30018f261e6ed14716d18b94b74aebe507feed8f14c9a7994e9af02a2eca36420a5eecdfa4848bdbe36c06709182c9ff146ff326f645d33d196a5fa0fbfe2a140ad8e9c620337b66db9cb2e9b1abc1a59f5ec68b46e666a4a5745b831daccfafa7d9c4e5c0f7277e545b412c0e6915012c9ccada4da157cbc4e8826884f18a2b2eb3ff86652d32ab34ac399c11e365903d1dc734772fb6b18dc91d670b70c49fb3651f3e7eaab36280d4f086d9bb3ff2b717df002f2408d56c4d21291515ad3e314b255d536121c35a928af171273cbd5ff0a12d27b78b539a2f60c69321e19c2bb6d8bf7089be4bb39b758bfbf4702951337a2af1d45c929771996b1b9dcc621541fffbdf90033bfc86eceed4b46cd9a78d31a9c088303172008937aa8471021827081053f8656fff33b4a377c863a721b57c303b491e666288705532f7cbaed8dd644ba487bf45447f9a535f150ff78d5ccb0a45bbc2386ce70391d91cfb7f3e97288adf0ac404caf1ae627d880a9c624774326486457ab897b9ad6927c2c2961408ae9a30100687442f2d4cfaf09fe430599e556cb44aa3c0c771f5e968cba1ac4d553079941ebc11502d843bc06ac43e063718884dd8a9ce6f9d40ee087a822851227e0b857693497c433ae9d409b18a93e96808b8d6a8191e5293d1952cc37e0a9ecc40a093618a277c0a378b527f2762bb352a71504191bdea4f44bfa3b897d4956610c8bb59c83284400e851223ce60b68569b8ad1e83718f6d2cde0465282fcf346324ebe80796320f14b101c5cba79d8d73fb94f95d36b3da6a4a3cfa85e917422bb6c3173cf0ac9543f104ddc67d3a0326f1a90076de8102e4e658af89c550c4b8add691ecb04a732c0412908dec7cbfd848a101d1d229f0191831fa6d3edb330b4540e8ec60825ed43dabfd9c3bc2c9babb057b664e3a00ae306cc50aa4075995833331bfcc0d9160756345e1c62460dc59ec97c6544b4170c900e868c76186ba870048b360463a922f59e49cc3a7fd190732d365c8762076fc223c2892063720c14e0c6cdbec265ed05e08644a5e85a43c509a368aae733afb8aae68752b91985a291dd248557f1464146390215dab77ad21ee8089d117a6ba5e9e81922d9d2f81c65db12297373f43ebd7559b30e2d734363afc1442e301a87c838fa1d084ceeb471ba357c2b2e38346c3e9420ea18f68ff6470f66cb9c8345a02ac58ba34fc0aa09d548de014e4633bf77a9d9fcd81b1e2f0965a6882782aa35a2c5cc30bf85e3b6b39856b753071479ab86c5daaa60e6ea5aaa775ea35824deb9b6cd76c027b2ed89df9545b219cd79531577f77fe98007b2a2161419d805b431d7aa69e97807c495a9ffdf486e00082f1a99246c87255ee3149f26c4e2c81f1090646e328ee7035e211f34715814649a7405214ba5d92278aeda159cca751e228bea3d2c7759fffe19868c8ebdfb2a9ce91bf32ac19d1040cab1d8dbbfbad12deab7d87015919a31399f29516642b2de76a85450fc1d91242b0f6564bd691ca57988aa787ea7b8c78b7bebc561b40f91a129179979516315d9f8d6bcc1b4d5c709a9d37893ced037978e7bd39a663056ebdf941d78b63f040f57c3deaa2077bc15706abc967f9412af350607db02ea7dfc2262d5cb7f6a002dfff4b3a79180c81006238d5b391414e11621096884dd291e24d597f82b44f095a699dcf1825348278da04529516df60f9dbe1c1903d9b2f18cf764c57646419825302c17978fec68ab50c422302b870a7a411804b2bfb40109095ef0e78b6fd6a866004e3322f6d9a8807991284040ef1ed3104732bdd86ee34d45cabce1baca9622887c30d13f3e5894d9101e13cfca10bc2540997f1567c518e004987e64152dbcd3961ffd06690d22715b11358053dce38e32e622b5b913dcf3f6534388a19d18747f8ef006ec9aca2b754c59bbfec5042abdb607b6666f83793372df67d53ea2fb57898e41296c2eb4d4dc9de0a72bb1c06552e886a4fa5fba480bbe17cae6e9707ed52b6151ccbbc310bc5e8e80925121e32c4e13755b6260ba44c227b8a88cd8a207e3649f210533ba17357db55fbaa8c3b88e99c6c0098b77b6a52f0fe9558971969a50279e44bec34c7ad28ce6b81682243a62316796a10842a3ec054853b72ee04b1e9b0effcf960563e7539d0e29fb7ac5392abf51c4eaac56c46d5bdb70bf7151a93769dcb812434c686f4a5d4b6a64081ab5a6aa61094a08716f3dd6d4001766567415828b41b6afc46cb94c231136b263e03751ea43937881e56aa28d143cf8e0dc65a0dfc915843d0fa42b2eb6daabae8363bd70d7747deb008bcd56de107c105675dbf28ddb530499442c84ceac40826818bce3882463c39979b5bf01e65d31b0439b0d31be3d5b9d112b54ce30f880ad5fd288598e460e7b8fc62df876af8de7f06eac9defcaecbe681dd52369313267eb6ee1a987bbd2aaba9bbf9f399bb353d49deaf292c4df297616aa342ad78a26cee176b296ba4c5bab4c92edeac26cbf745b9586004c248f55e9999c0965146ae7fcae8ee3fc0163321d46212bc2b1bfd2c6f32731c86a4b3f5e34f762e4c0a37f00622e9b81af524be4dc672a0eaa0792386e50ab66e5a1aed4ef211c17bb0ebdd3988ea8ec8b05031df5d4c65d21206a0507b42165a0e147658ae17f5a7722525c020c621a6fb877de5579379e656b452284764530c446e032f35b905fb9f6fcd1856e2de2630ab8d968a1ec28be7492a2dae28cb724de4d0dac37228543deb570c78f4ac1aaec143ecee27ef85c74b394c97ac605ef8b24344853d4caa133b8e79c1214b3df5fa0361b654e74cfee4691ef4e74117c01448fed076ffaa1d5e45e269dc90853bb24724dce02b080b263187212126210c616844cd6ec5d794b435f43237c6049caaf915f5e6e8fe23c767aca733c2d78c79ed123601435c0b8c5f68fc78f85d24e78f483f111bcdca874523a096a03e88c1042389ce6d5b02351bd73781776393a708017533355b9447b5072e8154df16ad1266e2fa83a046c49f62af85c41a9fdc90b89e5bc89b71fdacd654ae46cf9cae8d58000b0edbbc723f7a8222bbce33478e3ef0da2e414e7baec45564df80921e2a85a9e045422df10aaa0c31d1950040bf6d85f15562559a1dee440e01e1f69c42142c966a44863ea0836a554a3daa541ee357e365ab0c29372cdc3041b0299edf003ba6ffb8118dd608ff157d36d7bb4634267cd7af6a74a677eded4cbaf26e8fc1ff80c54b20d39dd4c1faca6454c83e49d438a872062972892cf02f20b5f4512bd67a1d44b1a8895bc60442a0e61312a722c5790e4f89b4216390055091d1d116d7963869162c1d852c14338e3e19adf855107180eac2dbf24360f4747431c8116c0623f17f8790871ec71aa911e5e7f8352c8e193a76b34b686e7f3d5ea7aa6582040947b6fb84d7959a1fedc4590a60f0500d61581a4132e87e0837147d0d9187e39da23057730d9a41a1650116d7310e64b73d0ee878a210aafffed2bb9585b669a56cc5a44ece08caa4443f826e822d2c7dea38aef89ca8ee2b00b50b76f7917936e0b09fc908eab406a4fd69e4e93005e98de52f266f3d3a5329931aa3abb87035c4293e3dc4752b4c0b100c95f4654837a5d7debc2f2e42e96c85a3da9c77c8f6cc6f1b68a7afddd550b7c6cd80da8cabefe04dbc270d7eb6f6aa0a8d768e79fb1d73c04e33fb2d016881dbdc2fe60c1738e36cf28556e6741dd30de5fd474ea5753c80ace298467691e9de6f43dc4186d1cc3827fd90248aaa79804fa5c444c2869cc03ea7d4fa75fe5f774027ddbe0f0f8bbab5e29d24bf734c66ea6981609ec86fd467359176a021c0c65800152d3a092c77eb9a83c8ac2fc30ec3fc20ded9a1a9e6d2479a5f6efb1243bf7af510d702c0322cf7a59080fb85f4a7c015530963aa099275464a8728c295ad25252f9db9366334b4217114d67341ec9857b3cb4a73397ad4cc6ea3182c617b00071d439259bfd5d1788035aa645b8d13ae0d4be81dd42941b70a4c91d89660249342b4934e1749324214e38857d509ec2a232949cd853a87418094c6fe858d670e2b6230387f3c1feb8b51dcf06a47ec23256e8e6fda49c77096df3bfc85a8bfb0b6789ca65b85c04a7364eeb2d0fbb56bf02605e0c7562414890188bbfc3232e64253359b204a36d142d15af7a4a6cc3bb895620285a820afd11e0ac471012aba16ea670903e55510bd37b4c2909803909120f5b35bc53dbac584b0c7bec6ddcd21727f8f04a5583fc392f23c4a99d9ced8a746d5e0a6d5b98f91b74748eb1d59d0c3b978ffa7ac9a69e5a2677c4325c22fff18dd170ad5d84e6a70395d29d889c44c98d5a218cc303ac4b024f1ed659ddfb81b1d8a3d283a5a878bb45d82a5a5bd885345d961644147db21bbddd00d789d36aa6fafbf20609dbec76dbca458cd246a616bd4b12063f5422bcd10a0bf3bb4a4fbc0e0f6dc47846740a2c627b52afab408f2d5beeff3822ff793caf5110234d5561c47667e52936dc2bcec3400d33fb7cd20b3f6cb9ac37d17ff5cc8de5c94d17acbef488fa3752c24dcd778c05c9cc0310ad7c9157b741cc7e56038c5096c9e117e26b40a33b6849f8feb5a4daba80813c1507acacae7e264893fdd00bf6982f961adc38f700b47ad6564b8fe8ea8b633322dfffcbf6efd1f0608d8d588f079a9439a284a44aec1b36ff9346c95b7cab9a52da9a329fa19ad04cbb7b40387d9915317ba0d4613ac664d82f3917e369da0852505d70bab36c24771908fe6f0bdf9753fff5ba75852499558cc7448b0fa5073396553bca5a815654fb9086462676a40d886f642e3b2802f760e6e2c824d71941b6efa8c9c4df4ae8268c8996d04a84c8c8b4a5ab9ced081647f4e932c4bb80391ec0724b9ff74c5001213de7aa064bdca90700c5b4f1c149044f76aa95da84b36ff83a2988515e3409ebdb511c1e3380a72b23abb92b62176525ebf2b8878a63a18f332a4b7614cb87be4f188c2c3c6d3cf1ef793c7ed61508f759750a61c6c17e020f6f64705cadb71a7d00f1b6ccbaa5f6c7e8e3109d5ebbd04c0538734632d3ecace32a949d293b8fe36692428f3e837029292ec67c9917a6e0e6cbbcc9c9aa55361295797bdcc51c3b37d5bda704a51b4d300085d8ff3942777ca9c88d5776293eef4911acf5db72e084cdbcfdfc527026f37e635b2f9f7f222c8c8b24df4100c9f06ee103a2b67784f3dcc3abfc9d89e6abc7843c735002b668537907a298115c0f24bee9e056b89a1c295a823d8cd478cb625c78ffcbdc30a12889c8ac64b3712005b777260695d49536aef739576cca2a15ee577ad245c1d2ca1ac79e245bc37c17f537d4ff8162100915e53a3dd6cee8d92e016e362960dfc03fc7cd1366a3b53e51b10b568af5be3a4c4ed5096a1d56ada53d2b432aa93d0862faa4bfb61db3e14718cbffc21737530bb0c9fa3b3e699915ac92c42f0a3c1bcab0751b843bd2b4d5b8a93c24882860dfbcb05182d2d41b5191032096ac10b9b06fea905ef74f6a5238433a09e11749c34fdbe7055b47acf25d4bc0632f7ef6bdb454201ba17e900d8164d6bb41780eddf6e62c8459a3bd4184b93c6aa163996194398807cb9901a7666d483c8305d0e81056f1c0633a0a9c670373e33a800b4705bc0bf0d67f0e26bb29b188c020ce1e6555b58bc1e5e7ac9e4f9211607009df6f9c5d5f1e437c709c54e49321527c349edeaa6dcb782adf8e5dd92f9d6636bacef11e3c9cafa431e2bb6d694fd78e3e93dd7a1e0851b8539514510384bbee2da79986d2a1e22e28203120f65c11aee85fa8d1873bb19b593fcd0a027b7a457dde59092347550e44e2330b4b7ae5bd7c2d5b538ed7786df9dc0b32a982b55d2dd3c3a384af83b51025914c8c4233964ed4bbb8dd06333e5cb7530824e9571999223d6b13b8bd0811b5022d61c5cace6cf953a2cbc43115d61868b0c67d55698415796a016376e6347c596c7ef8e9bcca89b766c0229bedc5e1fd6821cdd78784902f3adbfbf82589bcda7bf2b5f7ace5685b1944c41a4d494a09a64fe469db2850175cc24aa01f326ccb8fe1ff19dac27df20838c95b1940c5baa309f0cc498b5f8580fb0273249e4af015897d38d591698e41888d3d6b2209060fb1979b2de3e05a9790f274e3c1f778fb7bf06a73875e86c9f1900d97795ef2b39a10ef1ea3c71aeb006885973b6d06acb83785494184bd04255707d7bf80b2bafe33e59d2139324f15cacec9994457b0d19da85f620c122da72fc4b692a7bb9a791f3a4522d4d8539be865d38985c5853d0bf8206b782b5401b72835ec13358ae5a7dc7c8ef76c08400662ac4f48bcdcc0fd3c50fb866b58754cd31d5c609cf49f0e9deee2887413f5882230ec11aced0e3b90a5ee99438f78222a53c7b5e548acd3183f25d6f16ce2470e13d3a78f8532645cfa5b09f4ff196d36e9fa0cadac0d4ba8e8447a023784cbbee9a09c395a70b66ac57a0938765509938e76c6f809ba607feb96592c1a686efb4837597f355c6d5af55997246a06976e013e6207b8ebf5dd8b49ccb1cbd6eee7df6606c524876a200538a0fe7e8a1c011e1f545f2f96ecb8e214fe57efdd46ee70c0ec1797d4428aa17fd0ec6a338a7e45115f49d9f4cb82a186e721bf0caa4cfadc53782400b6ef89062989aa9b1fbeba070af7e4f336b4c28ea209840d86d9ecf2e48f814d5f0262256abeacc626882c6111c6ac2d2736a224908a976c010b6ec11b895be086e216b8211dd73c42cc1510bbf863db08b2c6a7c7dea3efe9bb6bf78b42c9c25c421f4b2b2d530903a903f6badaee7cc7c8625e0b15332590067c7fb219b862af4f715e92b01f5157b096e2666c68078ef94090fdefe2e858cb7bd22674a8a408f1d63044513852057f17c66fe722fafc2c2055e5854b49b5b8120b1b15f38e175a6ec80063ad5a955df6560e8e92603d085bf4e795a57304ae993a367e1b6507d675d42b64233d6e1a7298ece807f1668bf32e6cd64e5ff396fd30232422ee16b36582119d6650d38db611042afbf8103f1f02e9ae04b503369ce16c578aa21513734359952005d5a483ef5353b6c0400d66443de006ea0eb3083212b3c29e49256a247dabc8b0d2189fbcdb8813ebec13ccfcbed01056541746537a55d42c5531342be268f549f760900f67338615d6f54a9e2e4debf32fa5e4bda44c6f2ef5feccf99d88abb2473fe8363732837f420b560f798f4279966fb4d7117f37f50e778a1185b6ca3dcc83e93cbcbfef400c6660005a1e7d370fa522256aaa88c9a23b3a6ffa95c674431c87027f97799d91b4f51d485ce18e343c1e7fb85a7ded7d129f5792b4489774ef8c59b8135cd1dc28c4a49687956bf8b5e628846e15a0d7f95c735c6c304cec2a5810c812f6cb42bf31413e4c7c030077f25da5614908969584e9737f4ea25d2a3db0fecb14531c76c06dab8f21b4c14d14ecaa3586432f1bf1ff167436c635883481a2ddfa1561640d37a3754808ae7fb3acec8508a05f4eeb7b79ceb6d1da4118913403abc1951b415ed8b66b6b2674649adf8441bc0aa4ecfab21f42480d0b26fa064187cad6758aba1172461dc7d374bf0f854d3fb6996a8a8166404304f47be74700cc9d116a330c5eec2295e9841121a26f8584a52912880674103122b6b833a453dcaecfb7696966069c2415a22d68ff4308393fe041b6f2ec31a50bb70c5b886234f810e19c803e70b68099046106b6a96332264027f832cbc23dca1afb104dd722cf07ffdb0f77535473aff06bbc35d7d84710511c16cdf5d118e4751186353cefa84328168d43fc42207c8eb2c63cda73e17ac2ba426b9ba854fc52293d6e3f76bdd5186b50824fa5dd4d8b081a66608f82277e7d4fecb0a2bed0f67b50b3d8ac99c6d45260660cf6dd20a2ef64c127f8f7434a30ad4fd3280a2b2f811676d28ed71bf0d6ce901cc3b1d18caf39b5ce74fa42266583df5e40af01d218b4f13db45cb993196ce8ace465c44258f41d2e9e7d6bfd5b03bf8412e7d32a06b9bfdf7bfa568b9ce6ed8fd0eff44fab30da2b457d7faf186979f4d5bcc33d32d5b5c0ae1ca61b787cb62d64223e981037c852f3c9c369e3421e81e00ac787fb3bd61aafbc660b4734c56dfccb02787779ee6d885abc639a5c206db8a3c11ae5764ba5a16d7476a91d5154898b9e88e8fd193a111493e0f7bee3518dd4e29b6ea48e6513570c1bba0ae92da1179bf8d7e5c4e90345701f315eb9a5f8c5cd21cf63ece55849b74c2b7857925d374ddf931e07abd2b45ed335e8da30b4213d2ef33074b3d3a663e0234cd7a2403e03a129fd79dec686595b8046ecfccdb6d8f09d83a5e7ae29bdd1c880a32019744a4137608e97c61aa2440a322b10edf02b49ee84812dddb9a4db20ea386b466d51fae5ef489a6a5eb85b052ae746d2d8b2f9191213d5646c59e153a3ca7a9112eeaa85b8907a1c560b957d66e1ac5a982ba9c765bd10b27d29386b16f65a6ac1b5beb0b2be18ae9585b8907a1cd60b23db7929a0ecfc17f843ee08d20e2a833223f108042ade7601eacbf9b22f7959165f3cc41051bd0e4b9beaa0d9f0362ebac29e9d35d3f574155cd08a490f85eac1bcd40a39a1d66c5eeafe088a6d11a553c4a2b2362f8020f4a08c791b65764f74f86d03e3db4d3adc9ed35fe4e146f9b1b4d4885f2a1d8733a8a3fa520931c66e0b9714628914a8365bf710a831b42d5817c4030b909ec31e6a8d6fc1cdce6d6f0fa441ddf9b12a95cc1d1635cc637b12e4f598205a9f7c17380203009622d2aaabf6c2e355182b6798b35033a78ba4dc313895e71da8684353eebb36b8fbb39d438f0d0544f4977a74bafcd6fad2cb5e119f62163cfa0b0ff68a1d2c009c87560fc9fc89d4dca8f6a8e032cbeb0e54dd06254a00eb0967ec1eba8cc5210095485e37d233f7af504d9124b09b5102e71ac42832a4def21e1e3c46fb57e986b222842c293aa085adad5048ba071c33c5edd6abb3ee4a0f69d18cf0d94eaa84979b326ee699c97f09f0a0b51a27e4a982d36dc6336149be246c7490e4fb8fff1339c34cb1efc42995b3e03f189c2c595017899358f45073cb6cfa02a65d25a7d7b470ca78afc11f564046d647f3ad940ad5cdbf21195091576cb33f2a57396d72b990aff4df2423ad688c1f12dc7c62a3940c985a81230b337d06294b1526c60f59892f8dab86cd0b85451e0936f548705d291eef230310071e70bd391444f037b0021be8491bb577da60d128f7205ce2f9e2928dc7a92c8c45518c59a80d68be9bffab20afb88908ee57a1de08d3d5bd6037d77d7431fd32a8e8747fac6526f30a13ea745158aa1e954805b24efed1a9270e3e73948a44f4643449387cd94429ba17a5ac3b83b39884d5d375ec74ff9a1a32274819b5c4b50e23c7ccfe7361e068c2f74e418962d5284a6e40e7f39430ffd41309604f64cffe9d10024a60af05b1ed59c23d2b09afcd234fc7802fc1f57b3aeb6ace0b9af421aa87438efd45a2f86357b75ca79de0ea1552fa8642e9e9634b937a7523692ab60c54a4588d2f811e496c5f86decf442961e9d71c3032dff9e6ee8e9a36dc6f0626251dc0849e3535aa09486f0d5ced83768b9d5bd812749975c4afe70874e376e42fb03acfd7cda47ed0a0aca1ff53c9c4886e6bc2137dcc714057745193b583cb194258e08cf745f48ed7e5e31e00442f03afc22604641de23e7e3f2bd3808f04feeed115da40854d6371a87444088efdc5ea853db9f582bda0034afff27b50718dfbe5f480d02ee85ea097673a54f01ee9881bab7a8d7483124c029fb0a5ad86ded146431fcb3e2d9ca4f6b00ec3f5fc7911ff6a5ad9d8211449c54985d723f9194b01f52c1a9f96b7030e436f3f0873af3bf84bef554dd63fa9e007b37b5f0eea43ad53aed56fa39fa44f0e4f7f099b0d7684ae3d0863ff33d6b78eced436707f0e38666ce8ca1559a6facd49110c385ffd70fa9600ba013a3a5ad9546a170bfacd087406112e33f00b218e7d456da6c9381948eb7ea14cdc2f3c555d8ff4a95969e6b53085b3e3aaa18a0a259e4155c0fdeb1814b17f0312964668bee5b52bf3049b8a073a8209d2414f5bd869500837ecb572fa98c9e731243b84269d491696ecb4ee8195e5a84453f6cccd494000b79c364e706962baecfe18c550c1297fbd0e8485679bb2a889c5fda3b39bcef2fe9102a731e6ac48203f59f3cfc71b24a75f5a3f7d6e2b40e809313f3c1dfe012d31de556b5014532e3e4f0d8a8e050bf618cf08d73fb2335d8af203530938538369f90b40d6e82d6868566dbc70098b0f5a4a219bb9a607813f71011842d1260a09a3585fc8666b84ace44d8c1f2528e8a84621924e0d4d90a7301eb942eb285326835b5d57a8f794c4b75cf705759bbd49fc8331c31a3f3c55494eb547a50264b6f0d1b9412d629a6283fe6f1bf3cb043f1e5c541f0b6d18f425d59c3bdb7edae877c99306c21971512a5331b9419951b2358cde25af6117b1647bc15525566b9e4941352ac9b8349d56821e01b77d506f94a8245ee70a21e805f6a0d03acbebf8fb4485e1f55b1fb08a411c67153e0a36ae43b5c780b0f280e77a5ce5b5b353c52bb5de85a70db836c85365deb155610cb917e7dbae97446ac59e04cfa396841771edeff76082b0d9d762b99687c0b6acaf2f0780ab7666d20d30a003c0c78a1d524dbaf1d8595cd84b34956f94588a3a9c72b048d18443164ae0096c45ff3083b148d2d76c2f0fdf4f67b8410f3000c11a6637da338d7cd0244fa6ddedf9c3112ae93c80d265666f993f118dbceebdfed4877952e520ca51d0334b263b4e5825a481f4647d40e83c2bc357ed564cbbcbf3b6017a0072ea601ba322f4daf41ba26a6d9541c2064e0deaf2eaf5e1a854d0a787374ad4c6cde2ffd7911a62a6ee5e2938c473dfb51e2cabd40e76ff60f8fb3bc693514e658cd562a9fe6ae01fe6ba94b80c5b87686df9f828fd0a670cad106894b533627a7c305c5b7394ba658f6b84655c892db0726419334c5708ccd718e3b10b5f2fa2eb7e1aa5185e6377f16c9f84c611e2cbab9e01b1fd22994c19ac0fee771df7c4fba8853d9491a2cbcab09c7e44cf4ccf217b066b20205aba01c53cc5e0103668e2228c3942166ca38f54e5e7532f038d96c880f28c8889701291f26de9e32a9d3eb56685e3051a3548a1e73a050e0c341a0cc23871bc949869b5afc16b887e5da59bbc1c781e0aeb20a6972b0016e4c0f8be96cb93df0d72ab5b4a8ec9b29d5d1d8b98f4911c0330df20144e7ad23c0523b11bfc51ba23f23a9cf92607425d01a1baca13ee7df606a229bb23ead2403c542d10890b6725176d7d5c582fdeb349aaab5b85046d51714d1fbfc1d12a2150303a78fa7a309ef0420fd767209a7fbd3402213c4dab3d767142ba465b5ce07ab98b7287b30664833e8585c3413af436fdfdc2bd41000333b1864205784a7d0668c49afd2da160c3cf733cfa1123dfafe21ba6fdf334fb25719e070865bb1e7733cee739dd9e474e91272e82c5b255454b08a571e98bc495d43cefe6e1533acf4f9c376e403878fd29e2b3180d546278cca8960867b5a1fdee0dce086107def211447479200da5de78f9faac793b41b94f1d0a746d4c68fb30451e952abf2a9ef041afa0b033385032e5391cdd3c71c24b1ba9317e8e4af3622b6a8e2400245eb5cfd1252b74840926c28ceeba386939e65fa99f8ad53e17301f25ef737efc9fbc1bbd64e03aeffaf01c2829b5e567d5b5450b204a5661448423e1187d26c6b077ece24cb91dd6e120544909d87d5d387dd930354bfd6f51b606e44670d425bbf75cce1cd1cb0b6886c6a309bfc7cbcb7b32e77c5c148eb10a116d509dba6bc369a3bd29019fa07ca867fc7c72de0951d1251fd16fba8df66c9a5696a2ebe4c11cda610ec46315e6f98d2fe850cd064100e30554eda64e76d64162c4d9439d2c457c4e60c4dc0eafc0008f2ebd58c05d8c2baa2ff7189881e23175025cfdcac0ea82463bb1d2cef9629a13ac9b011637bffa039996744e42221a46d282cac5c628578d6f2f005dd7f8c2739c13df3fc831efa2cb1e1eb140ba08789370c713c585b7cc8344b2067d3489caf9b8632ef4374979d75a256082e6088c8ead392c2bee3658a973da906bf3666c298b03e764a4968680b7e56a9fc56706e8e44d5e446ead3bd70fbbd18809178fbecbc31b90fb5ff13eb18e4ed842f095d1002b8030da6fb1c70b5f5550b54b01c095d484a5327097bf125622044da3cee761ac96c9e6da0582ee4c562ae984b85fb3219d6c607aa3d2891f39bb964e30d60bd0017b3bcd2f9d2cffcb03481b493c3acbc79eb5c23c42ad64320a9bd41433e5e34fe840a18be4c40a3c922cf26c5985be25d4fbcb8831399b7baa967ff7e77d760e140d2c9e24b6c227f8b9aec3c9964f37bab4469bbe773b9ca4dc3288be9f26ed13f1f795111e194ef86da4c7fae8b6d93093fb65155edee7a6191920dba9d3dfd9853fd06ca7cf2858d75c7c6ab99c002cbe4df00407663c39331338230cb24ecf292310ae5836d60c9d9c60ea34267774d8391f02481d76e12d366f13ff03360df59d3bebb5fdfce643242ad3be4ef637b049dfa8dccfd33586a3931649b14951969f847fb800926c22f3cb932cad2bbcb3925f2ae0569928699502784486246d922a94efa50a77a729b49055cbda24b0bf6a45c69b5a23456f6d12dac21837b053ab92f373117da7c21d717bc50f4fa12c4af78c026b09ab949681f891af89118b79d2b000001d288ac33437c015bfb4414cbf9b4dfcc53771387bc5144f86fe5aee7e30101a7ab6aa37419b20cedd566a130941770bdd77ce35440dbc6fb2c66a0ff1e5b66623be579f393b7d8c4a40a159e8ba88cfa6d9b43d6d84ad22b6c16cc61e1f089b349ae19ba0a2da0d2d0b293ddf4222f4500dbc484025c6efca20af84dd88e67704dc1b256c22ecf553e324f8de6b1bcdb50e72d3a7f2f140f33a752d2f685476825ba4b6dd2fdbb74e4efb7236eaf84d7bcc0e1b5d339f10f07a52ba34635479e8ba52fe071d6602aa3c8f78446e0b852ae1bd3721d73857d8f86b93e305dfa5996a6bdd4d8b31d8f47d4d98fd95ae8a0ab48de85c08c0d730b8aa8e7e889787e762411e1c3661557f69c22644197f4eb0c90e098d84b00979c44f006cb2cbf76d20854d7c8df6e688e8a4232c35ff73fd4f8990dd13bc65c854acd9b2d4441615b7f34ee691d23e81b36d2876493521836a75853f69758b5ee65a5d92e94fa418ad2664102827cdec579327c4440e09646c1e41fb2ce1d59af4dbea5d153448fed226849e1b46aa1dce3b53cf512b4b7d4bbf7ba9ca499954b7a9c2d175275c131cb25a93f3a84624d6ea9b6393e73690e7c0a2ade4262ba686269f249a80c88afad0e5beb8c726bfa6653d72e25201a3cb3da26513d2553c53a2b991ff29b57c568223668166c90e26c2cd486593f8496d9bd18bfed14d6d926eae82f5eb07b671854289e88a78f01525eb61f205ae9d7d7cb60935a3f056f573e29954a650b7a6db246e42c13fd5c80738b139300237510b5a3c743ffa2e9adfeb9b3d27584a6d79bf8df1b650fe1702bee978489f46b0879c3588bffcbf3c868c3f1a6c3fa3824f53cb4d30b50182d088958ff13cd86ec2b8e64de2af9dda0c81de043f5ed8db9b04427152c3630e2fbf893bb2fb42411ba935e92dd0e6bbccdb64fb73f9bcde84b725e76baec7dea6fcfb94c2cb00c92504ec1b20769d7b30a7d9844b643ff55a0b894021a0144a9bfc2d148ab0336e1f84e2de5ca96489b3b27dda84236e12daa5a8cd26293214b788145b3091bfcda3c63ef28fe527b0d61f0a5fd3d89db5131b30018449937e621adaea3132eedbe3be5022ebc91e5b9ad54798d7c3d045a7571d88f54eb596e94f94833aa0f95d9dd875e99f545ba61ef060b5f7cf2c5b6a73cf921e4d88537e3748cac49da708f419e087879bfbb73d8ef4426ca37ecfc11cbe984c8aff03fbe09ad6feb47b7dc07427b791a0bf28a6496c1e171c88829b4eea2a5c3c883c1f6529ecba9dbe8822b6b98d1f9abe982dc7650fd324649792b998dec355fffe356d4997ea898be81be3080150c263ce1ecf9a64f9473a1545d178de451bc572f60ecf400dce58e879b9972cedf8e852855e29f360ed73a5488349f4ab048f47ef8b3a61467c3d8ea66a4ecff06987134b9bd3328f923fd2068eb1856397a32f3e60385786bc59dbda42e13c12a3ed43a80e216d445924b4825f5cd4983ddee18c6225c9831fc004eb095a7000479608ff1e1d5cb62692015677c1ea21b7d05e5bda411d79e2026914677b00d0dbe5897d267fb17d3818a8c2052ee38e12bba64110f3310c45e0882362b872e522fb8646aee8038d381ad609486e8f916e497738d1096a54096926aeab72b315e97b8f170266733cadba871a01bd6de19f48f7412d79d175fbac70787b44bcc0cd8d43268f018ddabbd9caf017c10d43e8689e4772fb8a7e61326d97098629fe97c9f62f6979b0ed7f5305ff5a51af01444a2e800896416818a045710b64643df4ae37a27ddd1077f0d163bc914d36d52f4c2d2e5c33ebc490441cda9b4e18415a8d79738e656b3d64589d3c7f18f06c1674f9728e514bc510c14350940852ea65ba9600ba957e88cfee6512c7b09d6a3addfa2ef5a22c7b4481e4ac5c8edaaf9d67a0fa58ed7643f9a667454df6148f8c3a7f2e11314f3c19617934bd84fa5669a2a6bd8c29c5b94511e08829ea8152fa37b03a420974d4e272e67691aae7913dc05d1c6a364f1253cf937a49e749388884f03c6520688cefdd2497cd271afaae01b20c276412aca9ebdc02ba308d6d819c59625b837bc0eb67d6f035facc57b7ad668f6b19db6da294ddbc7a8bbf005da635d03db064061b39a74b6dff59dd91fd6cc056b940fb401f63292c3aad3b141521e4c9f6bf709a13a5210ed3f5323fa3abbfe853ad66368a0585c53b8b65d5d17510a83f7ed46aee4ea4629a0fff6b7b345dbe22cb633bbfec960e4ce1fcec2864615b98066f9035eed1520daab8032600b2e1afd821d15c0bbf323a31fc9840bc618e601274ba063b0aa49c4f78d031956a1c26747420395b73d99bca00a8f3a7fb4a55a712821f11c24e228b792a12fc74319d95a08b48c0d69c21aea155fa23f2680a302d55c1a1b835a4be9e93b7044b67a4c74313143eb38b779640fcf81b5c32ff271f1f47774f0ee607d7fc3e15cd741e5f2b70588d35ae721bafc4ef1f8757e47c2329331f2319cb1a6d8bcb99ab687742ec0cb5d57e1f0a24a23d6d11500b3072c89af622bf44daa902b024272abd5c3cf891cb5f2c7d293b7e85b379ad3320d4ec63481ccc449de642e961c40ba5f6fb72f6993202708f2889e9494740cb139bf00e4e9ba3b64f4bd7070e61cde0c2d03326aae0a100e3100a8d2bb82ce58195190c3af3957f1f7e9c6080da58aa750bb435fab3b230b77b9a05f53fff31761f01e3f76562539a4c41529350d59652f9dc50a91230271051ec449b2129fafc9f6527539f9424aad5f6db5959046b3d17f6db4a23a75606eff5576c3cd75e929a6b161a915d708629ab6575b7a08df687bf5059b20ec9a6618485068aeb16cc7b5562e06e1d9960116dbd1319559f1ef68c3670a4dedaa5c8c9da0d65339154b86e6d948e83503ed6349c689330561dcea7082de56d1a859660675ecdcfed0f65b0393db6a46943cccbc963104ce6cb001822014100909411a90a4f3f6ec6b2d80285cc1729334488ee740c7b5739494d312a40627a4e1c16825b42e0762e8f9ae1992e2d8382cbe5f5fa5b1ce988ebf0201f3cde0df8ecc5edc76606c6ad7a7b4b272ed4673be3f64f6a8a1948fbfd7d84a1be4ffb15068356dbad7aa8ea69cf8e3dd5598def36795749db05391d7ed4fd13efaa27c70054c201e3d332066e32f75861ab18da27ff4d8f7032b7fc80c6ca0af4c2320d0595e2d87255cd953f82453afb6326965fa20f7e0c5168d18d51d31f1590f98219509e33244db27cc32546b5796e6edae0292894acc39f8f44eff0c90965df14304668559b37e8f5859a994d04673e63611af205ce0d2534ce4bf7b64682865ce9bb271b87c54ed2f7f54dfb525e3292ba8677c0521ccc79e014f40b722b096b5f05204f045c2bdde5b7825cc0901bc60223b316c28d12cf179978d6650bb5486b9345a7ca6b38f28bae9847ecf45a310cbc414f845a8e0fbb338fb518da3afcc3d1d68e4376649c16aa64547b7d8927496560eb7cab24c40e1117114958732252b06ea106f4c2674988b0455e7d82b922b9f868227cd2a4602ac2cca332799ba1439d7fc7b4e12b310d3aef9bd9b11d25ef7adeb726dfc9db1855f70c1ccf0ba65734868019c05a5f357e412f87f006979e96eb6d54a77d607f73395b79857a21aae424c2a90ec200528fe75e278efbd0c1da0abcb04a45ef4e508a975d4025e32d8fe0070407d86539b694e052e3f98ff950506a5e6462dad15c633af7062443a720ec69f6812981ba486a2f457be5d594ad9b0e41f0f83d47c0ed9b8bf8737da07e4179f35e669624fb5be049834ed3e7deffa30a13b68e7962d79beeb87cf7dad27c2c4ef1e92d64ce8be418013587dada99d03c3fcb4590e9e65d790894fbad2bb9ab410071211ca2545c6658e87fdd6981b696acb1b0903186953af1f1e4c01f74988e53114ce4e27ae5e3949bd3d9d6ed63c25d90e3ae88cb3b504d77207aaba517c73be69e1af572eb6f3400eaeedc83a9dd24f62e90c24de3e4ea5b063d0923524b4f393cd71eb0c75e0b88ed49443e7d7f866cbb68afb2393fe90fc7b4b00a7bc5d48f8b26a68c864dd5a1b3f3936ded29078add292af578716200a294c59d98fc174b9860e9e7e283b9067aa6c9eac319ad211c80b3500efc4d4e3b83531160c7cb2a8e36cf10f594314d46e0153b8332f7442f76966c76502ba2c3c29bd02658ceddd418939774a51a8d173b83abe106d317abb3fe933e8f95b8915ff6072cf519892a2d4a82654b4bed432019a263f486f52f134f46f613b6a2ac14704d9c489144f86817ca8a0fe3aa5779f1458e09de5acd3139fd8baaffd077eb389ce9ea306f2e0a4465f54b3207ba21abe6661d3caa61b6b1b518ebb571af980fb447ba339065bbef73f82bc6853edc47580fb1e916dec4224ab64b866f26a787a0b157632b120a043d25e18348ad53810324ac47fdb554f42f9d6d947c15b4be95012f67d6006406d6fd2184adf581b90bb881469e8f6e52a3bec3a46ec7c791641cf44e43c3773f93269612263c4ab3a69576ba7580bbc3bf821921196f85770939a95e62dd84c61cb52be9f59b841e22a871ba5b95006328b49583f763442b984ef5611089486adc4bfd7206653e995e65f4f925949e2371fee12b105cf27d4c69a7825e5762653d53ba4a7a4c692aeb9d2931cb2a371fbbb553041f0c3226426a0d64ac884c78866c5011d907de8bed9c5d7e1eae9e5b3442599233c3a06a7f80b7cc85ca404654744a2fcb40365ad5622a634b0d544a61cb40d23fb8f21e8fd41f042405418eb8f5b63194cacfc0102d6afae8ffca838e9076624c541a53902468f58162db8247672fe151ac030955ea2ca8a39163131f384d0bf6d683d89536fb83d7ff2875bc8da15df6cafb0d620223a07bcdde3e3efa41c281a965f7272d95c8271aada0fb1f887e75dd218c948790c06c10927e72dd322ce4603f6884b76a3423f9701c22c2243fd1cd70a0107976efbf44a24439fd37315f945aaf48fe7f527fee6d1a0a4ab237bb7f1612e7085f95cbbb23217acebdd3dfb90ade699c9e5cb7509c7f709362569dada839f94c07c81bf21166cccdcc67ea219f6d1d89e70a405a4dd148831b2ee3b93b53e12ab965b8ce3ca6fbaa714dc2b19a7dddcef08506545cba8848210ea2f85b4cce486ebfa340b8bcbb2b3fbf746a3b614c6132b6f1f0cde3738bd3320631c1cd241bedae3f955499c39fbf234ea69695acc544a0135ef0290e2023e105e27a20dc7d21bc907c14b7095defbb087cf0e39d863b261ddb8220f804e18099906779d6a991c00bfa955dedada414a40dc82a7b30f8a44b1233ce338505a1a51edeb9e820ae2ffcb3cdb1a6a95ec408010f072722412684d6bb041047901e896576a30cd0a046538a7ceca1813fe1e0d911acc519a45d70ddd196a708bb059faeb757726faadca98b4dec44d73794f7b29b8bf8150f4ffafa0a83ed26566ab60906d45e226c9d210e82aa130dc2e668f4b16060a015d799a4b612246d4dacc568b51bc6734e1bffd176584b3c7b0f9c60168ac3b6e8b5aa80a386fecdaa2f3e10922eec657e3818b4dc0f6738f080f43aaaf642597fddea63bd0004f2fdcc05cf6176f4e28a7119fb93cf0f42bba540ceadf730166e6b62718e4626b9dd7b73aa731886e04c569f65bb811b479b85458d0462f460f24ef8266970340fec3b63fcbe0a71e50eb6e97b912dd903339ce77fa470eafe3dc1a5fd7768823bd31f21b842f75da531c67ff9fdb89516ba88a252edeadbc12257472c9e20248c754d85b07829bda15a10e8ea6d9e6f9c552273287d2952b6eb94ee6f7c8adc3593d479024b1ddc37a9a0ef645e71dfa695581084cc25a3cfe8c034a495bf449e69293e185c2ca5fc3d7ea1f65f7ba1dfb409b351aad879df61e678a2f5592546c59cbdbfca05bd3e6491b51166c7abf9b4f1e164b6dffdc30a63d3ed37053d0b19597bf3c3de32caaefca583cef4205ba96848626b444f54372b11d85b7d3722264a5f7e4b756a82134aa003035c055663e2479091be677f333db48103ed850e007be221c4dfdc2773f0b7818c7ecfacf76d12bd1e8a7fd68a069676f4ac413f4b334e2b0c5f806aeb44f1e6e61e7b01593dd9c35d3a30a2be53b795e88481c91d2db28f55a3a0be2f678df83e3a8f6a063430fa4a3bc6db11be7a3d7665ca6114225fbe6d327f94f4837460ef2e5e5f01226e94945054eb3ac2325c35606fe78797c40a96715d8b50b07792f55a2ee202ed6e67c8e1b4ba38532cdd03ac972e5c6344263bf22ade864cac784a910fef5f1be6ea3827be8146948b70474862db3b513704c0d5582d32815dddbcb6b47880b370e0d46ece38da3a8369f8c58d57265ae86165c9b75e36825b1760bb4488240792252ba56e5e97dd85503df374b0344f3b43dcefd84946c068dfae84ffd81493c77e94df775573cfc340d329bb430bd4423b2cba0f94047727e78d80d4721e048f14fa72366ba96219a886c7f34ab7bfc12e0397db83d6792f5888ff0217a634d137215a3a334af13425b631293027646cb81e8a16c5510302048a57bc3ba3186fc4e9743a8ca6647b40d1db8dcd43cd9d017e2ad8c1a61f2728feb516b160ee8cde3d3227884c6d6638fd31dfc29b88483ee74d3b13b59cfe4d5df445f1125be8323e145ce51588140a1a4dbaabcb3be492718c3d0bfeca0cd135e396d90a91c43dd96447216bd2ead4aaeb24de2c8ab7e84e5f67d9bb7232c18d63508777dc0a68aa9bfb0cabd03dfebf3c522c988b9cc13ebe6a1a6f73bf9734905d5a66ad2d4c81bdcf6346e69266b200219b15c654548210441b1d805a0277117226c2b820deaabca2f995cc17adb96a4b91a5f2136dbcf57810a74371cb224af2426293a54221df5bd5acc8a19d89dbc15b54854fe702fa19d7415ddf211368d4ac38ffc17463f5e789b301ac389cefbab170f1b5a3d74fb54723aecbd1508f65323f7a6ebe73ade7b3bcf486bb0c426aa60e15ee79c5591cc37b1732d0f256edd0fd7e66ea0c3ac154b28a2f2b1cefba7d0ba0dbc1b42f7d76aec00ba09a1bb67129b81208394433334444c5be959c55adc352d9429f5fc252f6514fc16082ccb94fac6a0bb335f52709c4c760d44bb54a0b96f4fe98eb40cc957e8b8fb1c7bf6481cfa85283c14329ebcba8dcd71929286fd8d6b09ddc19440d699ff4ad0aae0f6641ed81407a51da5877369896deb40d79c187223ec675325df222d3c7b775c9dbb658ccf41d9c02614e2bb770e4967c867d81ccb173ef1e7727443b126f73ec15ff7da3680aa853307aac76fd6b369021e4ac3351e1acad70652c80f37df61c765ea96b3c4b0c535cc18e57a7643627116f7ccf203db3fb5b04c24fa4fc22807c9342816c2efc476506a278578551d9298695e0002bf29a0a5d6f8de2be77f8e648fc1290c487d74f82fbc5643100cc481cbcd6fa04ab646087e449586551c64cac953ea75dc4070a08045bd3492e6f52f8e3bd517fa9f87fcb932c60aaf72c930171242a8c7779d8fcfd525aba4d7767872f922c4a0626dc8af7bb5d42e672cd22148468b8acf18f9a7fedaf2b798e0b6fa5ec6af6c6f2a56438c39b60c611694152b713e3989dbca233ed0f9d8ba484e23c06f9abca1070c670d774574a6a092bdd40fd6dec194e3b65858580208b9015823a0b670c3c64da483b5d8ef3e59941e19740291caa24ec8b8cd76e5a95e6038c9e94624396e6262e5cbe8446d4808745cc40a50f8a4c797066facbdef5891e5c8669a82b3882d0d778c7cb0fd78d9446169ffbcd967c4fc3792ac95ac448cb340dc415a5e662b82cf5be24be628fcc9b07519b504cb066a563ece2490d61bfbf7a9a262e95137bb365f0242e89300ea5a2e66831d7940c05a66a1840201dd06bed5fab80a8b4e4809c43934ab1441a5807a4ebcad2139aa7e118482f738e298c3c3ca8e042aa40cb613fc568543092b3b9e93b0ea39d5d2f1daa4590536c08302f427b408f600cbcec7031aaf2629d33963df4ab5c058ae6a119c5962d01ea32752a1dde3bb3ce1ce58da8d1c0dfd0dc2c7fcc4543dbe10449a160dc4996d5f32f7327f7a9b36298f4b50137c94884c550867fb71de61298e9a9cc9ec50cc5a3ff6f16736f87e2f3f8bd49f95f56358cd7a8940dc11aae327f8462baf8b61e848b71072e3648d998c5707ed86e18352b6204486e1a330ddd05367db3861933d6b5c6b756817a801d1b63b61cf487b8878f75c8a7ce87cb9146206fba0731c90d944140803a68df2d132b5aa5c11e2e7945ce6d99854d78e79158353671593a03add9e1a95d6e343a0936ecee4947661e8632eeef0445d81744bd0ca1a25937741c1e071fc1265916d3ac5195f81f099a4b9fdce199acd1c1b3c66bd1136b1df854ecb29dbb303b7708a559bf29c29ef4ff4f665343304931922a96fed792be1e1418c3b27e708065c0d4974d54201a051317c433de4306033577920e0a117e28931542ef7e7409b4efd3935c832617f116bb86948cfd53032fe8c00337c8c7652bd0e014254656a8276cde7452e9f077305ca2db601569e9d6bd499ff5709385e75db001922083eca87faf375c7c024ac745615f304ebaa20e85de0bc83478002d8e49d7269f3a0e12bc913e269f913ed7e1717bcdc7565ddc9a799ca2756a130d6010ea7c9fc690700890e1751eefc757faff1687a1bc5a6a00140e1ee307f27b793c09d8aa64ab58b27793bdb79452262903b105e705ef0541b07460082c493881658a1821b05021244bd08f2c457ac84244cc922c4ac092d0aa4da1647182892c52c44065f98284162018d0e2434c07f46c53285a9850d1428216b45411fb41c73685b2a5855706b6048961419b9b42d992a4b5023db329942d4e314f34b929942dbd146ce9f29b42a10203df9616dc5104373f519541db0f4b194cbb80a23e08ca64a20c05db3e23d0434542d9000f55e679c1c7dfdfef9fdaa736bfb5a60106e03522ae796a90f2ae20f2afd749f0cf8fcb06b87ff44f15f0feccd35f02f85de6b50a9b11a986785d3282346c151c1f40aee896a6174cf558a02e2144c5123d32154a0b3d16a05b96f48076c76477fefe607977f8b60c436b2d498e96fc91b44f5af3d40fdad0d45f63d2bb915ced76abd19a67f8e0f3589356a21063db12e422b573dbda07f0ecfc20251f409776689e3c3b9b0d003f04433287a64eb9a5d7d3983cb8d21f6bac3bf0695e7bf04f992c84eff54a85bccf08f4d8e087b0879900c2626060313130fce4941312da1172cae57cf0f1bdc005db3f060696abb948266242ad39ab73020cec049ae3b4a7f95af50aa6e660660856c7e65d349954a6b53a36a69bd4525ba3b16c230dd74b85bcedcb5a96f4115f7f2eb5f69ab3ec67c3b3b6676f4f1adbeeacc09d7d50979f59e62e34f5a6a4092a0b82e569c90755c87b7cf27579dac66795acbd2a67961473336b561f35a82aa1605395e0119907ebeeab3b7f3c559f62515c72413e76b9b0078cc79ad40550d87632547d4d4adf0550fc95b280a2805ea943e238aa5e2c55a24a14c5185bf56118be18aac227c557813fbe18c6d8aa07e9122f47087efb1e0445f3548d260dafa110638baa92eeefbfd187f5aadff19b93a0f885bf637c1a63b933baff68eeecd00e9aa44967a41896678f99fd98d7c75ec7eb63b08fc19e47ec5f4f7b666870d1630ccf1ecd93678f6fcbefc5936787cfe3e4839bfc1d4b33f20406f0a854da415f3dab3c57e21833c646d928338ee629feea676464b11895f8a1aafc5efc5e5c8de3f7f47bd600c6b72faa90b76d87a438b68c6831b596cc1c2d1ad2f03e581e817998b2ee58c08bedeefe14ac3c21e4a03ee8a796b7a644f0b32e22dffcb6d58baf63f5e2f82b5307c9437cd2b4f5781e4b117c31ccb3fce02ceb6ccae883eeb8a9e906076d235721680e7e976a7bd5a943541cb2b65c87631b7fed599d29ab133ec678f7343e515acaf884aa34cbf8eb12f8aad711be68f2b01fbeca0ccdd9f3b80faa9ef6804f7bde82d6299b2f83d64d38e3e7f1918bbe95e7d8318f2797b5c7b3381957265e3ba2e45a026f10e9cdf912daf645afd57d5810876dffc4b17f96ad4db2348e0bdadf7e8d4983d96f678fbd0a45976b5fbcc46399da0c9fea24d68a0c475c85684c462b2c0b2658750ff03261e60c1fa065ceb0396bb5fe65ea68fdeb5f2d738778c7a736c83fcbed44e80781615edfb976deb3cee440501badb7d6a59ce7511bfbee699f7409d4d785b675211088dff26759a3885765153d5bc13c0cf63c5e3a603c5a0f3365b26d9d069a63bffa4805ec61c2ebbbd7b73ec677424a34c7c698beb338f6791840cc1780f6c47cec797c87d58999b33bbb2e57ec5d3173470cbef869cf7df11b61a68ed6c3a81e97a1a92a0780bbd54790d76c0ab1a73db08f791d301f3379b41ee6634c1e3efcb41ec6e4f17a98d973cd934e6dfba95ecc63ab7cb95ab32aa4fa1992f6a56ded8b570c792c2ee9fe32e8bbd91daa42aee44b84a62cce7d6b1f02754773b854a13a04c4772fa195c75e58daf81b31c9a3363c98c0b87afc27bcad85c9af49e9a2efb1ecf12f80eea036c0c7313fcb22c62d13e663ff03f3b1cfd9fc89fd8c5901fbb1afc07d98efd1c3d3fa1a93b6b5ccfa3247316bf1b4edfc35ab633ff7ac3eab9055e54e49a38a56fd69db18bf6a2642fed5afcc93b6b339cb5ff7ea7bec1d2df304b5796aca324f590e1e24fda4993fe3fc6375884a869a7021e28a9e99d721f33393870f332f63ceb20e159eac804086a55ccf8c39cb013332b2180cccca284efd97aba5592b167d9105da10fc32be22a5a911778f5ac71088e28de9d65ad3dd4b9a1deddb50f79a8de6765de635987ba5d9a84e73aa0d9f596bed4cb5d6cac8ac75777777678ad57c84e26de8d3a6d67eaed9189bb596b604c30473f7d7bdd7f5656b6dcbda18f75e3db2dc57d6da7b2f2903f5bd7794895fb636747777771fab053f77cfee8eadbdd67ed9cabefce56bedb5eeeefee52fd79ab5eeee5e9dbab75821e087531293ad826f4ab3a949734491c65a4bbbf7de599db1b6ca582b73f7588cbbc3506aad09b316eb5b6b7dc9ac7551dad2ee2cf71549e948a9e86e434569d5eed8417777cf14bbfbb5eeee260a2aa56e4ae9fafe35373158a7f6d27cf9e26450e377601b3ccb74e6fb4099dff1a02c54c56c085e9d8209aaf0e62fdcc9f777fc6bc572fd8e67e9964e41eb96d6296898f177bc8dc942990cf83b5e66e6b37646c3b6c6aac05015821804adadc28260a812c751548560160c66b7f1526b54ae1d17814ab1ab59d21c9c00a7f6d2d05a9b9638f7de2644e04f15821f9ee53cf37da00c280b5531558c08b3a44f9b2a039aa34100b6fd9a33108009a408a36d672c4b9c9d8100c8fb3b3b3b3b2f96ab54c0b3744bb774cbf582c18ca21893853219907e33b36a565ac3f8d27035696a2cd93a6572ce20d51145130026169b0fc5f714aa79da6abf9df5a766697025d3e4cbc69a94b9e571e8349e15a2a1aa86ce6cfade44548daa52e04843bf862d994d7132d8b40c7bd994225733a6a5a525d58a3553775557752a9696d1b2962b1603d3ab30d4a0155985789a82ba4e402a05291d6a0f09107bca615e3131574cd6da116500999087459fa419b5ad667178361f7e0996e677f3268b3cc301194c5790e610e96ac3b0e543328e03cd593db9b00252a13a61683a91933ee4352d2f46aa436d359c2f6532ba5579b45aa13ae1fb87d53c7d86ed2f5cb1822a869e9e7ce869c8f4217ff221930a6d5353bed0e7341e7680dae2c856d035f3b6cbb59dd8f5611548901a72406ab8620393251b9ef65981d4a91b98ecd37344370c65d180921a70b02287204f8a8282dca5f33271606a9ff7e9bc5266febc5378b33f6fef6ad9f5afd479a7884640a4e7c43e687fe21fbb9e5888e64f3c8489767d5c84753920b14fbcc34b76c54c3053138e1212649f9887abfc9f580b962affc453f84b2e4888957d66a1330331ffcc44452cfeccbafa7997958628ed339fd9889b3f73536ef167ce538470d92d5afc0d896b7173f32c48dccd8dcd420892cde24d12c7c234bf2471669943d42eff495cf9ff3624ee6dba6c9baf2171363a35354f43e26a688868d8344f237134b49f9138da8c0344f6ec6748dc8cee99972171333253e8380599a13239e160c4d7d360bf5c50b7eb5b24ceb5e33414846ffd2c12a79370ba018a46da5065933f9238727c91c48da2ea4312a75ac2a90620e07f240e84da1f1025fba369c12c4055e6ed1675e608ed2434450ab4368a212a5a8c19b6bc7012227177943203122c76fe191c16cc70819dffe69721715926ebf1c3112710ecd0eb028407f20e50106195517a8606159a326dba3340c5be7f7198c8c0518d9d3f865325033be89d3f667c92626718129775ad16830a589061890f508030c2e9092a240c0345fb3ac15dadf152ced90590ba748c1f5cf6fd96d639e75ccbb59c3315172edf1f1fd0c129c18f0decfcf957242ec37841cb8b9d9f247159d76a16a7be25a226c02148882553485094816ce469051a6e33358b4afbde5791b86b850c2d70b0f387384bc4d0030823286a672576fe6f043014ed64138918362a1eec4c9b4ae0040317469010e0528f205057d8908314278c88c12e01b1f3d3a8a306417dbb578052325d1c0c69e1134c8b9c14b19bdc970d582829cd159e5a942b7499bdd0dbec6e6a589032a361a18a1934211bcc5a8072cdb4404503991a5a321e58b12399181f4e0fe3838a18cc8517d1cba5a4f524a65d7882e50297d7ea851829841c8d56a2102e55123021ef053a5d20053041ac1b583c90418c44cce82636083ecceb05a9767a2105e3d4d79c1cf554f0c39517aa287add7065882a579868b1e0ca151db0b4d084254832a35cda766854f3867c6b7118a9dffce36b7d0751a4aa3fc53fbf3fc13fc33feb9ff94fab12c7f1c5d154e17d9de27d7d7f785fcf75f3585c9e28e0eabd42556d13a2f8edbc4596fff84ce8fa15dbaca0df45eceed85a96bb4dba3a2bab57f7e5cf22e5d65b3fa0efd55155f8ddefdae46a3b046fbdf5d65b9174f9cbaab0085b6db5c1c8565b6d30b24f8750c2565b6f30c214c8f90a5b6db5d556208c84a8ae61dada2ad3309db69a3e1133646cfa4ccee3b5275ddde684c0938385255b63eceb3c16a7049fb2bd56f3825bdb3076b53b361ad57ea53d91e51997570259adb423acac20c48620a4200181acd61e4345400085032c6409a2973f60c3119d989efc377ebb5b66377542c2c472c05c9a819faa0ca61817c3c8385fed097c21155299ac058d1a59f068aecd9237bed6ff36ad1a26f1459bc964333232a6d892b8c42a429f3131c86f270ab9988f1f7eabd85e07884bbb01f757456807884b6f9fa608ed2fe6a7196371e99bf4cc19cb6dfb4efad836a539d6acefd89a30fd920279352d1bad75a9b579e3e2c5e83527f619cbc9a23c09b687b6ac3030665261dea6258e022996dab86773648ce38cd15c82096aba41465c2046d03675daa1862dd2ea4453271e7a9b3aedb0649fe1a64e3c48d9508660026588255da6ecdfb4898b121ee435c71663d1628b71b55f6fcd35d75c83b6f51f58e8de9bffeb8f1db49d4743d3a3d172b31992d0cc4c4f46e6484a26432a8ac58e783131433030484130d8d1d4eb95d473b9845a2d9dd64a3916eb69b54ae291a4d2d0384689a2944a953415864544fb472968ff28f9d8946ad127c8c4b4db2d5162224a9db42d9bd959fe7157134b3da0621b28a4e427156ca0d0f7792cc6d8cc4d56be1f9fd07e02f7bc86795ec3554411e7dcf62f2a6c4be3a4c72fdbad35f194d7fc53fa983e1dfe927cba6fe7b7acc464bb9b99c9f765fb94d76cbee6cb16fe4bf34ce19f96ff39bff98fcb6a7e69bf46cac6ec6dc7d5981f17af65d12728e54869834a7e3bc1a5ed4bfc767ad1ce1f5cf2db0932193165fb834c7e3b715193dfce9b039ffc767eba2da576c02588da28a236a86ea764979ea0e49eebd01e4abf2d8eabd0dc0473d99e996cff1fa4297ecb4c36dd34e50fd2141e0fa98adfb20736ddb4f207a94aaf77f4c56f79884df7acfc39fa92cb21fdf05b1662d33d53fe20fd40124212aae2b7acb4e996297f8eaaf47a475afc969336ddb2f2e748cb91d491141291df32d2a63b56fe2011211521151d4df15b3eda74c7943f47538e78473c207ecb4b36dd30e50f1290a121241f7ecb416cba61e50f920fa420a4a0232e7ecb1dd874bfca9f232e475347534955fc9681d874bbca9fa42a49bda4de0fbfe5dda6bb55fe28fd10125232e2b7ac64d3adcb1f25233add17bfe51f36ddacf227e98b524e29d7e4b7ecc3a67b55fe24353d3d254df15b4eb2e926cb9fa42949bc249e1210bf65a34df758fe2801511a521a92e2b7dcc3a65b2c7f92a4444569f15b46b2e956953f495aa4a492b8f82deb36dd61f993c425692a698ac86ff9c8a61b2c7f94888a8a7e947cf82df3b0e9feca1f251f4a41d406f9a3144473e8e6f19bb33a18bc39bbb43dca90d43e6fd135cdcf9ae3cdf9c10f084f0b94be6691433d2ea216cc391d01b2a9ce6fd4e9c80fbc647b1227244976cea64eba2f5b14790c6d7b499fba7f539a63edbb1df2e13592097dd6a2dcd4e9455e8138112e2a22a33edda7a3b745bd06fb74413b07dd1f3767f382ce591ddbcecd6dafbd7bf854e3db09b1babd58ccbf03804d6db8560b02414b1e2195685ac3d85e5ac3b9de25ecb6033125a71d64f080667dd9c108952c469a5e4118f100cc041a665327233c4c3162c3124658700d29a2020de89a4d9d8a44c1bae8d8a64e459abc94288204064b111d5050448658124556102262450a112897155a6fea44a489ab49b7367522b283c140a408ec039a66532722178029818e917a8140bf9e30810e48c472d0b34d9d74501283a203912974086a41a163ace8e99829311c215574340222311ed0af4d9d8a66800dd1339b3a0de912c4102b303a2ddbd4694894100c1101c802b166535aff746d3ae314a73e0dad460d997216385381cad849c6385fa88caf132c5260236c6454ab1136c24646419c4e70028b83e7daa76b8bc0c1d3e3d953ec9e6b6f24b93792d81b493ec8616d921cd7ee247727b13b09cec70eb1e25a7bedb5d75e9b8da6c65e7b614fd4d87c942f4d6c817856c7a45ea4bc4cb160711345a6e9a6451499a6162ea2c834b9781145a6e945add62864acc88c6a383de0c0c0e00a06f67e20a8d3815decd7654617230689b339f65eb13324ee76c9912145c60c15ccb0cdb860cba056241968502b120d36a011c3ef6870b0e3800312676ff646b1486e3a3bb546b153a3d6286ad8e8a0de5b9f68d1d401009ab4b84283831b38ec8d228abd4d6e3a3878ec8d228abd4d7676481c588367839f4b88bd3e600f89bd4d6c74b07bec8d228abd4d7878eeeeb1378abd4d76cf959e0fac7c90c35e1e9de5d15d1e1dcea7569dcf07d37df00108b61046f7d203d222c416c38cbb830f082390606f14512c121144207176047b71148b6d24ee2211618412665cb8315cdb0866ec48280149092620312100480260b31b68d004ce38e38c33c65ff014a81c1bdbc7804a022a09a824a092a8958a144020c192f1c5d7c8e8a200a3201b619cb1e08c0225a6bed46a54ab519012251867289c8d6aad526b956a6d9d9284132c385381cad809ce54a03276722fced7286327385381cad849c6385fa88caf132c5260236c6454ab1136c24646390411813315289c8d30ce50381b7d09825252ab51ad46b51ad98ca837ecb4687befc518e79cbf8f04010c55e248ae58bae57ac1606262329999196d060dbd4ac3ad34f42a0d4f2048034900eff129728236d6c8f811f423a8c6c6f63148712447f16d2506d584512fb054734ff5024bb92793054e97a12a745d86aad0b1b8a95de15191bac2a32275d3e28509744c26d06d978a52dafeb1162e9e1c018b501d03e88cb61740c9e2f8833225985e5633804eb781aa9c60892ad1dbfeb1022881a0d632d97f8d2aa1b4fd63481fd506e2f9e0e20585028a1705c5dbfe3eab243738569a72b91e8eeaca6a36dc09bd14ac00652a30812918303028793c19cb0a0c462f55e8d04748cc223e11f229f29a2db44275c6779c94d5194d9f229bed6505d44b1524083a712285b37d74325e72642ce996742404d11c192b64cca0199a61e3095a0ab2cd32b0693003d483a1dc51431e31006875c4720021306db0010d99e5e016333575d3c1d1c3d1834227f429f2394275703c99f449d213253578389e703c6d07e2599c7742aa06efdb35a78352c306095c348464ec6845f064ecb63f0b5e4f830d00e0414b46512d4a46918ca25a140d0e3cb8a16b4c2c7235a61a138b5c8d89db782371dfebd86ed870b06abd7df97411b568072ea23ac39d1480ef6a529a038220500f079a403cafb98bc82564a7c658c4454471fc5d43a88e2965a9ebc8761a9d1155a6f09870a2892853caea00f14ca9ed40556874376e90b8fb373e4a8cf7e4888b68caa68fa5e0a78fa1680735297586dbb01fe3512f5a88b694da01501474860351a139fed93c6b4c00f16845e0e0b1322af146a5a2eff1ec118b204f5959ca2ea6d8634f48c3b67383e4f9c09623462dc7a8354db69388389fb1f26440bcd18bd4f69701434e0edc28c2d844ab3b2f55f6139d4f918f9113b83609414f3e453e3a1f232090385b08f7da1e4c4f403ca09ed770d604aa42d3030821d84610c9f17d74524f945ebb0793156409d41b2d10af06069f22ab03c4d3da0b4f06e9336303f5a278c9114518c18b8a4620a10a099130526ac35602f56205884781a8501d918aa64f91a55eb26c1f233de8126c19d86e087df79428512285aee51017df0fcc16dc7dbb6c776ead381691de9c620ee7a2a80d9bc10a9bdea75fae599c573581a5aea9723185b8b3ee4458b1e2c214537c0c5835aea6606910ca1a195c5cf156758de56b74184c0c88849689aab1baccdddd1d464873e8db1f66344a478bcf7bef95619a255f3b4f5b738dcd78f58ff7daace053db2eaee87ada7dbf82704d93050b72890b1c1c18f4a580512383468276848eea798e59b2b81965b4b87151abbdc0a11d5131186d313070ede20aed084d2ed175c915ed1b8c2eaed0e0e0a6c3018ddb4d6a67ac5163c7868b2b16db185d5cc11d8c2eae5000b8a6365db224ea97d0f5c9251e7870e3060e1a1238706020c312d50f46a10b92a87a08bbe88a8e74bdf75a37227fdf2eef32ae545c8c955c25f503193bfd10e874ba10e842a0d325499244974497244986c1c0317cf052afb9dfe8a6385075c39defeb67d2bc1698dda8d46b9c4b52c76406000400f316000020100c084422599224692027f71400105a8a3e666c2c1dc802410e232906622886812084186388410619649462ec0c00e80a9d4c5d7393b66f9d243fa9ed11979069361d90e289cf09e1c16ddcd98491ba0a9b22aa9b2ef640aecb510853fc7dae7b909ed2e70b6c7aea3e38ee872b6f1c0025bf87c8cbc05770fa8eefba052a69eecd44d2d5c178d7e876ae59bbec2766926fc6d1cdab51cf60ba9b79286f14fd2a6f107d19153c383d7289dc5824e851c8734f48338241976be37a049eaa560664a91507225916b608c0660d1661f0a35b4a4a76f6b3c7499f68379d57cb621c4c27c226f4d5884432079a2c794c654dcd951b050642567eb1360c1fe615ec7140727304f191a21aab93a52bca493d59baaa1cd490a52bea492db332bd41a362b9b2a5ad1d201f477e6831bdd34d9545692d02eb11bc302688145de835b1fa38b77814063c3bb5a215b04cb0bc18a4f0057c2559f609e639e31fe49f17a76a956f5c119f9ee86c221df0bd2ad722c8ea42b0d5e06fca9303ca83679dc3fce2c529acf8cb4724e26a9b34674217d2d3430edca21cdae07685587567d651220a0e6917748b428c7707ab28881ae799150b23ed4286553329f483ac6e2970d2eea9b9099c29648430ba020243167cbb0faae409362a8b42ac770b5651108574de0a3c354262fdb628bd96709b3f3a0e40e9df21e64a702bb8aee077ed824992f94f8972f56854346abbb9b702bbe3e79244375bb6135c2068209ac35f871d29d4e5f5790a3d98034b4a4117d795a13b465f554bccbb969b6f5a39d16155d9f9d82b9339ca40df986b158ae3cf70d368230f0935c6290aa1860755d5d0be90e80b086e0d450502b7d3808f4c98960f86f95edc0669f99ec91a31281bc0e12d3e45dfe14da5003d7ec5b3a8165e081539728c2a197483e532aa05b64b01fd734bf0dfef75082cb844da701c08445830c1324d2d1d0e9b724c95290f22bd1499443f45f5b3d874f515ddadad48575b998e1e68424f3c4043e86674c82d150a69ff86e20e26db6ef1267bc8bb94128f3179f59a33e875f067b86208d0040247f4fc031da66181653189c49ea8a70b0282e0c0270a1f5be39977674a07a0979b281f9fa2077ad79ca5e7fd035579b89d1285e3a4b9cffba1905049878d90929bf47d7fa42a2eb750527798f417493f2d6d888b98a99b1b906284cf0934e06db9dbcc445525364555b4af14d746f5bdb0f5111a3ea1416f50eac15caa4ef328fdd19e8f094cdbed4f98ef012fc2416001d7a1eff06ba8f0945308e293f72d709f5dc2ad8e23716187d367b68e8a9d6e8a840539133f2d900052dc7d33432b2747f6143dc5ed397c2e5d69ab02df972c4ea5d15d44aecde9289a6e34ccd627bbb04f183760e2bb58b7c82a4a0ec1a1fee942b61deb4910c233be42d7c264eace640598f5ae65f06b0751e40c444f06e565f0fce868406e61df3c842fcb0775016f9cc6db8eff602d0dac70e68fdde09a3b758a777880c365d46457e8bf818a55bf4dfbe0cff3e23092ab07a8cad94076aca4daa03bd3a59601c26c43157218a92e1f6a3360ac76714879cd1036ab31843b2ec73c1a0dea497aba8c595eb0bb32c2c102318226a8e67c19a7804d43e3e30a347083816e4271850a1fcd628c0d44b12277071714a3b87927a19ab28dfece4e530050b1892603fab9f567dc1b60312ae12bc0d251bcd3e2bc441d0b7b68d68f1132420a7e618c95276cff57cfab6fc94424948948c89f9350d4b2a1245775005ae32ec7eee64d4a76b8536c7928649838e41209a2f7ce2d5669e830425790cc4523fa8d8e85b0e677b985599a44bd1d074fe4ccfd33320024d810e62761b170682c7b7b09b482ea7528db3cb0b2b0e164686e778cbeba80ffb3e658efe5329527b818daf2092fef595bf7c2063e3ee29fd0f3e62ae56275e77fb60a168cdb40427ca033fc0204e0376edd31974ea67484298764894243eb2f6ef0b2a520af5f4735f03211d6347b3a4b0f63fe0f56e7a422add7f398885c993061ed2cbe820983d2116bfbf9c5aeef2bf62c60e7603c038083ea8dd0dd62be34c44609456253e275ead6fc3cccf4ffe724b776de4b8d084e932c222062ff52d425107ec5cc90701f74c283c72c88e21b236c2cfc380962c3252e488938ac951d4a9e7986ef83a7e1ee381c5c7d6c823de980d5d9943a48f28e00380d4a7a96e834e7b0950be15b3a77d1b0a947aedc2b09657a600d25dbe4d79d951008a19d12593151a0eda43d463660eeb75f47a3ec5525c08b5d29475fcc013a601547878b26c1558711f9c2a6f18573594305b9b4deceb9367e6331abb7b615bfe8c80e105978e4c319235261ce42b133007b479df3760b4d56995980cb170a658745164ffc1203d2e2c37be579747cc77a53758a78cc05c1e8bc8060cf6deb489085ae85600108b6321051016406bab21163bee8a6ad0c910290a7f605b28155d1c096dea99979ca426d68958542cd0003da7d3df43a8196bcac305326dcbf990570eadce6f427bd3dace8d8ecd453fe3ced03a436f90eaa95a6651a64ad46f1b4f501711bbd839fad6c5e1e37c01c2b0038662301caedb322cb3e2180462a24b2df737bee0813a3db9ac2d03dd0231c949f149bbc3ed7dafc16f24eef32035a68e066b963bba979a670e6099d63f02a7b58fb11ad5705e03811a7c154730d55a4090e28502c73b88825ea71ce14e27e7d75846e6814a793254b08451ae96faf7a21b9aaa953dd39d626b7380d397c15749991a6c98e1fdb654e23c42a6d2f6cf66883bb8c258c0c6515ba009e652e9e6cc8fa04095be71749ef2040138611dbf7b4b3141d2867742e75856dca5f0634183b9c18a12c17e3bcde5214228e2746ff1808ed3f2615f2c29d9e30e3c70768c13c0366d3c4cdffd872eb01d742cd2c35b8c496b824626d5ccb4aec62c1c3275cc089ad7f0dc166874a1312af172578d5d60e720c46e098436c6e4263eaa619f339a2dbed55a0c741bb7d8c3920b868a9be798f426e70115d56e1fa61fba6a49117ab5dcee656fe7048d4f45781abc1e8dc3d9c3cb1895a9a997dea9d9d05ced46d55a52764ee41e8ca5ea06d759d8b2ed02b5f652466e7c24cab85d3bf0c0ffcbe6c632c94b6a885c8b9d3d5b3f7b9be0c34f0dac7d3253576301cbd2bbb10e73fea36a212f10c6c3ac3261613e375d483b615c7e4b21712b9059fd9494fe1520a6bad1b7c2b9cc9458b36a4a75f2ca3dfc4bb0309e4f8347dd435c1d3490ebd2a885e4eaf82fa8c03324379e235345673d81fdc92e31a3d2a0f891131735dfaa446a7c7099841d701f00444239fb0c99262a6ab946ac7344ee34f97dff388c7aa4b7d6f1ecd824e1843dcda6411ad051b651a6fda8448edac28c4e66dcf0c607edb6a433007ad4843e6e67b5666f8de4c838feb45e4cfa4ef4b90f1e27909659075c3496fdd375cf9310e5550aa67b0294410bcd5342bd3bd2f4076358d318fc94d2ffaad1e04314f146a1f8b23d322c37cf653adddcca16a576f3897b786912fe6b976601dd4f634f19939ab59e977fb0d084fe1007d776b20c0aa60225c9856af05157e5838a1b767c215a0b9d6c9e7305e1c0c364c1c233867f31423c233bba271aa890c44fcc9a1dd1a9835daa3d41501ab7d0c543b36bc06d821e7fe17d0ec2ebe2d171c0be4e2f593b499669d4ac5986c0a20aef2e24574a933d30d88e43950b32b8b88876bbf8505701de84a34f75f27c47a826a8f68bcb61cb3aed97afee882afdbb9d723129965ef35536940b06bf317143db7614b29fcd424d4d1ee0be682212f58af56ca2afa565f82796fd462b894fb7b4f7c044e4f8d8ff5fa430d64ed287914a965ce0f9820d5ad7e160081908e65b9a06e48e4dbcd381ccf8ed6a0dc2c1c7be701275eb2a4611f5403ea9a48db5828a35d786cf07ca958e099b0c9403e8d5259e924985056e2188e4d0b758b0975ed396a87a6086b5c8f276eb2ce6d25134d69ee8d6a6337ed3366941412b3b11c3dca4061707b413225523d4e5cd6c2b0a9626209d0356e2e341886214a0dbe36cb60cddf9164f1e338f662c367cf50684105c5159cbaca7ed8e80efd03cf29a49ade1438095bc3408ec0d5a6fc07f1a7d25935282ef4a7785236112d8c95ad288490b3c1f76b6eaa8acdccbc902eea5f041e94ae8a96f6e389770a00631c4bb09f410e7f2e6b8abda6532e93bae66cbc690aecbd1c2c70131fc4549eccaa3a021cfebc5adf4efb3e2d9dc6f73673c44e11f50e3a0dc336cd4b3fdacae6efe64c2d715a42b21d8b50051ca0360e2cf861bd4507d08bf060ced288d2ce70bdc838c228ab049c0597b51e790a5a610cc637d6e8cc6b9e6dc37761eae4ed45c6e7740d845135483aa1ab68dcf37fcb032a9bb7e46967db97030466531dd543110d82b1789e1f4ec474965b84375c6926f585bb8f69ef498ddad83d5153689d36e843d81986f4cdd502600e0a5341cbab30d1f2343ef61cbe1253c001a13868c265199fb1f361c0fcbdcc9a8cacd62b1b01ba158f7664eef600647c0bc711f2e9e18c972c33243e85f211652abba016da861aafc22ee1eb9169dca8326d33f6e3ee76733de0ceeeef27dbaf6527545841304cbc22aa2ce5e87a81935ba51e409dd222ec3365eec4c93387557dea9b45dabc2ab9d5b52062b91a9017a2c91753de685f23c17cb897069c455f2f15411807f7aae60597a6118d0f5d70d04504b438f71f072ceb619d8a79264d909dc4c301f41930b539b314ccdfeaec4d837baf7d0fa5fdb046d2e39af8c534aaeca6c18538bf8f72f0211d4f0e9aec0cce4c6badd07cc548306ed9dac3937ea83a2eb972654c8f493e195a4f9e2ce5e1570e3d8f9db03a2ed2c02b4042927ece35ddc861a5f1d09d7f1c6ae34e34c98e9eda26b8e411528d05018ef8b821e9d86ae19727d79935ba611ab49e704e20e5dd7e9e8288ca8c1a5798d56ef951050a079937acada4aeaf3b353a6369d7b5b3cdd92c64364373f35c77992d67664fa699f51cf93778e2c4037ce1bb78eb2d8d547469e62b0badb807399df26e6e646e0991d587489ca8299a0bdbf8ae009e34634b54b6709712a7cc766a84ad02e551e907ac9541adcd74d37fa41e95e6622205bc22d538293e53df7eb5282c37107c231058f6ae35095923ece458be1427079fa8b4fd3e28ae4dda78c9e8e88ce11432937d200bfe26a7688babc42c4d1b079cfe7b36338641525da40987dd0f232d69665e4e093d2e1aab418ede0ed2743054859f265d92a9d4eb44f0a9fb6ad4aa1d141c8fc528ae21449d4998a3bc74a04241b224dfa55463ebc0565287c0e59ceffa84fa2e3ab425b837055971249e86b82854d1c3518dce420b1f79676b40369005d15e47967401d131e09051019c9f5ab04307464848baa309422b3467b0f14cbaee2d69c6f141a79b434b412909c05f0969a747e7f35bad5342b51b15d89d1cef4bc006569364c81cf0dc9414ec61abe4868f76c3a830e3edde400a76fc3a4a74ebc8d83e2c7a27cf7423b10947c8deefc717bb00f45be567897c0574a39f201f2355b97bdabc19a697572b233cb7c45c596f8c65f06018c40d1d63228f8a6cbf6636d3dcbe3ca227d77e2a5316211566aeb4e1b1068331ea7395d647509763587b19503be1cd31a766e0a52e002a306bd6f970aa9c3165243f1f93941d0530d1d969052ba5ce8150c3c6d839125499dd2a124362087c63d75176d40f26b0569a632f1cd7e456d487180d1744c680f6473f3997c2d9926a33c3468373131af1f398dd6c71fa468683cdfa6b1bc209fb57da903c980d516eb8d48ab4f56ac149426225f485869be5fc5f91ed4d848110096869ae9a75c1c5fb5f430ef46b41aae28cdefac4cd78dd30143687f36e076f78b221c43071dccb8060dd34b266720e68bb51789bbc2c965818da85735e24a7af759322a35291c8bd0a109b9c6dbcb27a2692bf6fd5e9592110e7770f34fd3469c431b139ff98977b6bbe130991cad5c9ec463172aa1ee329d692f4d6d1763ecc912d4bcef5c840c75ca5c1eeccf20fd64008c70240b81a1d76b0a29826466ede93148b2793fae035903082bdbc3aa7d7b34e70a11d49f79e14b1f225ce36b9c12e9eb1a2a7b0e1ef9f9e348dfbd4412318dde7a32e941a06540e9564738d752a0f82e841fa851d3972234dd45ec86617b2194b4674a404735c3b857d00a4a36a013a87b156d97416ad9e1acef6cd0d3473c218ecf3451e0e3052e4b58b2e90bd0dae83a90690452987257a6421481965f147cfa1ba2b93908dfc0ef643d0bc9e059c207b2650af013db97cbc077b0d485bfe378dd91fd76c684fc3bb1ad01bb89aca5458cd443f2acf31989ad59602e890f45808822e0ef214228dd3a593cea34d1837097b43b091005c780f8ba66af182b18851d923e96f93f8f2f9cae31b0ae392c062b6ac2aaee6c429f678728676b1998f2ce62a18fca4e8dba39ee1abfb8da0d1a5729507cbe1edff76a7fd9bec06b1cdf85486728405c3208c211b06c12bddb0985c9f8e32e7303b1a28797a128fecc81cd60241279c8085968ed3845f9785aa7f8172ad545139c412f0e3a0f24709717d430014f705be38a27c8a0caad117a48a196d8e60ad4d66f62778c5afd6417945285869e600e0334c014505e4fc6f3500d363526d29310960751f1e3feade778ba7b771dbfa05051458ac993abb3a8d3bcef8f84cd7c528567c3fbc06a32fe168d86f4bcf4ecfe366313639118b1dd4c133908487c9a354da435d47b35e78ec3c0283a43c454c6ab390a9c2d6b335362db04a1df758ffbaa69bd22887753effb0282e008c3fd789ea2a8039c69e780409cba6f651c3bae383697d3b90663555248637ac1141a5235687a2dbb2a85ed49e36f2a5259221240a5f0d1c1b54d696600093a1fc84ee203e6e8b1fede3d73975ee4463f58d1f275f257a934f7c310dd97b405d21356da0021a9fd1e29cd00eceaf597c2312adc969fda52b9cd69419d801521ddd4155338d2367bca06fc89d24a23daa597236ab939e502c8d61ad6ebdc768b0cf7665a1ea9d0c1f13ca34b2bf798087a7560316b0a838a01adca626b7d7d442f091d2773b3cbc1ad17bdc356f96c600771f318f5f0a9525add56b321bf9c7b73f620e15409d7eafaaa762b811a554f3c6cd018f58d0e4a5cb03f29938634f49f85423efa4d0833fa70f74df29e466f5239a2d974fec261e1fd6f62e22cfed859b3380d27f2b0e686c7906a3d1342596a944983c3fe25e836c9c7e062845fc8a75308ce4187f30765641e6f77e05c4810e929790f1ffe4f011afb6cbfad7522cdda15196b2adcb006531e8ef19448ee0af5c665d95c879dded8d6b6b592f6894b43042aa571da6d345b8f3a1480160891cf1f340f1c47db0e41d37c5a6cd284ac205453ac2578f5d4239828fc26b17a9c75ff62e21dd3159e6d189e66a9de1fa39fa44ae9dad81e3020c053b18f948e6c059a244824ce8dd0f5d1a3aa5d16c81a5ecc13f65175edcfde882a4eabd63758c5f84cd39032ffc9512855bf634226680d4af30063efc5c1f7f9b840e930b65b45aa689a71f6806a8f8949d13a24c27a107144a24cd65f802b50d434d71f5d0960eaf0b7350af6c1ae7fe8c6d92fc8cb92f58a8e99dd1c4ba0227e11b0c9bccf94f3581a3e3d69f2be126d637aba8c2f107a287e1e78e75fe6346e9eb7de799fafe35445c3b7bd61e260a71bba6ae1394ec066cfcd6420a2ac1eb7daf585d1c67133373e5f9273ff4c906af0797b1ef69034ed8999a388562926ab3da12c250f0b859ab20c06d5eae74f563835fde0686cd47c22f0d12eb411285b1616d1d26022147d5a3544a84c868e5acffd40d9f0189d18f5eaac2de35012d053ffdb48df0421dd6123b7694a38465a55b02e34c98571da61800206b80213df7011000184e28794bcdab0b226d4aed6791babcc48ea3c4192ab2034da4634ad71c0727be91c9b4ebc2e67ada27eb697d8924ed78306aa21256cff99700559e1f5e6872a1d41f1a0689c82cf2cdaddab29fca015982bb982b1b77618bb1ebad5d75606a90ebd2a3cf75770a73475e5cbfcceb7758cc701625fb98f5b1e4fd38f9c2f9a4f7839a9618440b0a490fdd9aebfdb99f3b1890e0b895c9289a0dca1ff299449ebd41d7a46b4b40c6ec28f477ba82039078d5c573a07f805701e60751f51a59150b55a6afb601febb952f9add88c134099f04c49518a5e09c2458f91b754061cd6f1d9c0beea17f530a58edb9f57dfe2f4ea690c30a9934e5dfc2826062f2a701e74e0fc707cca27dfad07d11c17b44645beada2956f73d8da206aab868f5fea250a3a7286b3512754cd4a22eea2ceab5bd44417faf28e7022e9a2dd65d0699de13262fa2bdaea2d8d550d493f14503445cb47a7f518cedd02235dbe31d392915ca1f3e3c3361827f5582faa0232e563dadd926a87e0a8bc0773ef428a5c9a2d1949f3fe688baa0afac6763a71e311a237f25bfbef0135625f04e5dc9234fcb653cbfd0cc6b432d4e397a7205c614407068b077680b112d84ed390ffe01abcb8b8d56a92ba56f0f3850ff852702955e674b26f7a9fafe79189b84e26ae611785a82b60d6c588de54950191902c5ab94edc31cdfd1ca4683504ea7f1c06ad855460ed00c0049d0775102cb8da76a23cad14a3da899d1069e420af8f93337a0886cb69a9e93b7e0fe1b8c25c2809984b48ac4af8e138be917ea7f1b88330cb0ebb22d93165fb444f042b8ba666d86f7c850c68ccb23e9d02c54b6653762938795e8384519590513dc2775ae0f8817b64f0d5f846411cbd58e415ad054e75abd13d30b9d230fbc037da77332e19c2f893cfc374bbbabde73af1099cab26066dcdb3373d12d0134d4fba5a10b2656166ef9a93c9d5d1c72a039d1320276c3af85edf1ce14517b78327d97575d236409cacbbc259143bc5e6977934a801b96199a72f91fbf323d170f29b89a48c970252bc909a0b9d8a1945b1c346f38e6754a425efa0162cde7940f93e43251e9d4138e899aff6833abed2992fb5afcb30daba8892721c3920ba5bfac3dd7be430e5cf390435b77f6ff393a7cf531999c3da6867a364ec072719b5f5b0b3020db6cb1f0b2fb2471deb7cdc53e396e41f6ac55ecf9f4221ab04a668e50318c1fe3ac7bac53176c9d3228e63899162cfd12bdc7115e0dd946ba7c4422cd6858650ae30bdbb4c8c4e82bc06eddc47a28bf98a2e69dd5e49e3b9fe197760f6beafaa3cf446e4ce9650e25e3ebb6397113aafb2641a23abd7d01a42c8c3f6644a7ff1f4546941c60b9cc5f9d0e49e8e95bc61b7076d819cd7447a0ee3d761adc628bb44f9c001269339052a78fea3d8e59f24ce1cae6c46f6ca4b54f5c7d420cb889179534062024dbcfb2e6c32b392061641e92ab033199026162cfd152c469d6b3d3221156b6baff91da4080974398ccb49283fd642e19ac0174a47d47e6558b46ebf3dfc5f588b6aaaf34d773b8f998502802b452114cd4517ca1a1b58af7f96c879e8bbad42afca2d45320e7fa862702cb255c0b035c157959820388e4c3180dd4309d0bc8925a6616900841708d96ab958ddf7089eb1490ec11c84559e9916bcb75860809b8765eb20c33936c5f13711d6baa37d89096852559fa036f309d38ab659beec32472eb052d696d27928dcc852153002bb7d02c2dc561e303a2228dcba888a89a3fa3faaa3fa759484a0d7bf4291c11f697a54b22edfba63d63a240fb56c914617f14bf76f97c6c0d241ddef82c6f4609218fc3d2cf780b5af2b0fabda7c88bcab43250fac8fd68f8cce78b3e6973057137f9de2bf3cbdb7fe686bcdd309dc77d237fa74d95311ce23c206d3745fb271f17c32c8796669c1c30e1f4351de3502756bb0ee5eb7663fc27c77212b349d44b90f13d7b81ee088e093aef84c41f283c9a432ba5f38bf20fadfd72140a831fd098811c71c18de419e9a22d35e38a73aae52a9fd21ff7e052e9edb2e5985951ecb5a45de73b0a5fd3532a00254ac7e9e3798162b64b84837ef0e6be28cecc2825716158a1210c3f3675e81649e2fb7b9f6af39b42d3255bf0a14a17d4d439c6abaf662883da564c945cc5e058489fda17fa54d928f70907150b2e0cc31c542fe846042126185c81403acf4d938d83bdf955dcef179d157d4ccfe604e467e353f8ff3d49bc5ef4e350a7e8f41a166459336c117b9b896482a1f9074c64c936c21f1b16a226f6ad77f31ad40f1fce5920937b863571accf0b0331c70cc6f9c3edc9ea09e73a0bdb39ea7b17bbff6082dec17041c474fba2a82797d778090aeb4cdad8c12d45f3ef21f3f4a6b8d853c9fc50fc3c1c3540f22b877f19cf2b07e36fe4ce0e11b3d13c24180979a352653767b8f557ad6f8c753ccd4cfc392b87bb03385fddf169c1bf2c751d7f0bf8f9be1a1a1f896a10738238a8c1cde8f5893a28f1c71417b6eef0537cfa636b9980d71bbba7321bda6656fb57de4dd0f12415ef9826c50ad716eebfdbae49228fd00b5fe63fdbc80e1ffb2e0669763a0a31c5370975f926abf6580dc9ffe619a0a771ea8d3b7b5f64188b44fa7c1caceff8aa1349ea5f922a8df4eb43b8c5fc262cb04818f349ac212f50b4d3f2ce617e3854ce6af7135bf5c81cc623f80022f9a07a4228f433919d9616e1ef02012516d48496b33106dc0cb009e3e23f541ba9457a3352aacb81dddcf0aa8cad7d313ab6858f5d2fc5812e42781e5280fa07bb0664446bc3c76feb8d31e06680f5a9a0562852e2ab610bb40efc9edcc4ab017d3df7d2d66e164452d4141835f46811c576eb00eef90b053d1a213f64572d541e965751c314e9ab36d55425fea7327ca16c5241ee25e87d90fa625ef5c897b729cc2cc5c7ab480d747591c6d946e72661d1f67516afb816bb9bf351e27e7d070b62eeb0bed9454216864101a1b12e53277c17359be8c63dac04fc07301b657d9bba88bcc0be8d5f2adabbc45c2b41559ddd4b3502d9105114113a9930117ba890900afccca891dda1e7368799b95ac3dc77012394356d25ca2662ecc78f01c5a9d7911c70f10f4ce4bf77e752761b4d87f9ae7d60e0dca69c5d864b354e88fe3f6d9844f70b8d8d29dd766a703db69b09ec01c904b83e2c88b18ad238f51b66cd3ab29a5267016d49418562ad865c718ed0fbb9db3085613ec23451f250eb28db7674d78ab660527ab822ce092772048bc68ca5a86e3353fa0f74ccee949eb297c1cb2c7649eae827fe3470bec168130120fef1b84d82676160b411084ebab2fd6b1fe96a51f68c950ebe20fb421c5150ec24a0fd26b6798af33dc305f06c4b31a168224051f44fe1443397faf79cd44d1b6a1ac5e5d2c6e1bcd6297263fc0ba3ba07073d877cf2280f76b0d618fa41b287e80e83c94ff406bdfd8df3bcbbe157447004553018b6dc7d3c2ea02adcf2586961993d77d5304fb2844143fcfc8d010893a530008b672c3852a14399dd1e7c798e84cccda61a1147a062e50d3af918db1ac203160654646658741c07f3a175c42fae12bdf85ce3b95ba6bbd88ab12cdcb8da4ac0ca59745883235fb477d5b78c5c2672309aa8ac3274a54bdd75e15cb4b9d2e84fc78f2484ebf9e9a25e4b99dd459dee6b5171f45558c9ad5ba9c361c24f6b31ecfad04c6e9644e5340612f1a9f3fd6772ecbed23e97e4f0de99aba83fe8fbb75102a07dea708f36c49e67a5dc07e7543538cd5fce7da5d6d6fce95059c67216810df34bf06d9bb046afc581f171ecc83d7c5acd63d054c6f0c2020aeef8e0cd857042e03f1675480a126984244d8a8ad22997ae1d2181332fdb6f50044afea67add110c2865c004c56b4cf51d0c9938eebcfa2647c986846301f3f67595d41c574bb4e614daefcfca03cef4c729aad64f921d074a76b8c12002af65df1f4f1ca52f57da33f619558b0316e5ffd22d7247368537e4c05217150e0a5fcfc4596f3dd5ee879ce9e080cd5061819a04bc929e508658db256cff99c7a88032404b45d9cc849952830467c6d5a0f78e375ded639ace1847c61599bec927fa58aa0a953d00c7e817417f8323a7f7fa81b0f1a9e425b05960b1730fc0d85bcbbee80c8fa8e1f658ca63af4a46f8d066e62b73bcb8c036582af50805f6deb658816718ea22b0fa614daa0d59c2b3df01289b5d2af46c0a64345dc7d9fe9822a28ade4291c485b0804e660f0eea275282cfbb476de71b182fec6f85cda6c5a3aaca3b808cb3afd36d01cee5b540ad80f4f4088d3130d7582262cf352136df591669d5faadc1fd0208fc554bd553b4e9ea295b080a8121d8be18595c2aa13abd805cbb8a904d1af6a00dc717bc45a0a72f88980250895f70b4d9e60c5542c372b65376cecdd66b8301606edbd29bc959e606b13d860f760ada227b82f0b9c5ad7261c860c5f564e0559cd6c3229a88c1b96290165dec6e245c706c4cd216d7e62e0777f2b462b52f647a3beeee4980049f12d979003e85d6c4664df73feac5e89de8e7a4122ccff6e58d6d5fd0b2f62e73f8d3833db9914154ec16880431f06506121e126e7472f7f61521a356cd88b765c9b652cfe1e5910c1460cd62c48a12e889e2398021863383b081340d901d49159f77c6acd6a6cc05ba4516ba2f2a18544d5bbae4bc2e7e623350195199b594b185d8a43355e7df216fa48205d088f94125b65c2149be3df9978b1e768ebd6f80d1229893d5726e1f874423fb9ed184184d7c74615bdbb19a786454767a5d860fffb28278742578fce0de6a6f47085e5f8bd5986e81cd94c9ce9298bb58ff50cd22467b6575397fb042a25c5238acc4a4f883c0549152deda938c295de3957db3ae4abe44c778aa82d7aaf933b47417592badd298b9d4f0bdc971385ff08a44c597e85812c816d32c7ab00d44aa57b86eb307f2f2d8824aafcabf303ec62dbc922a146b61170a9bc0ae7263d7f21a6bb3d8f46b8f96d1710378840ae0fdc4450d3a1862834bdc32822ec51047d4e31108bc7aceecb758e151320454df8d044ea56dd8157cecea47e0bc797eec781079d1927ed2f2843c08fc2eab27fdbf23fd101952c4505fdde57067e4a94d0a58ffa02245841996313f8a1219c683b5b4f839c28805cd1443a00081052176788d182e3a581d865df3717e04c2eea583bedb28e29ba1bb617a59c27ee2f9de884f73ff6dfffd865ffb5ee04b3cb08da790e0840029fabbefb43f1acbe06001600b3a115263a4497fc20972f78a6f62c58e96fa8c8eae44b017d69a8761141ed63d91c45c93e37cf94c20c210efbb2ef467b7dec62d529c12141273ef746c8177c6fc00d4ddeb60eeb25a744d01b8070436413726bf266ffc34b1256a99ba1f6ad514adcf69ab3068e0f6c659a36b42d17562e55f9190dcfc01e88f06aea6e30785fbc0555bfa08268d94a545550bf49438cb6535f20c13023c9b324aa28ef7d50480b63a8771becc59abf7bf4c7d6b36914caf49192d245d9d5ed11b9283be7d0ec981be08496bf32151bddc2bdba9d1c40e410e5d42c47eb1d51a45dadb739d09b9c36fcd36a25e13b85d80a236a04d56a043147b3872efef9a4de468af49591a0938e6e1ea66cf20e6e3721e2f4493ba05b0b28a7885fbe1053cb3d24c5aa72c6528ba51e3a033382b702254637d83e02ca12d49d31f55e352adc409e666065135cbb7153b4f6ff5dadf29d75bdff36965c11618c10db1087801a0ce1cb452774c0f39d9c841357d9b868c4f28c7ac48d1a6885780faf98b37dc3499026141984707de8190935b4b4b05868174969c70a35a225d8edba5f4d66f2c68a696e07d7f0211ae42d97e9b5d6c393f6054a8e69e9d16bc20276f2818300f5764d9cf29724bcef62c7c0e88536e899c9ded6b4cc7616ec999dd7a37307289075ee8f0a472c0924b3e1b7687ff25263c482e2133d18bd3d6d11df9e5ccb1e818370508f7be35da2e2f9117c9cf1afa9590ff95932c4f8e12a144869ce34000acfb737abe7acf59adddb242ecc4800ebabb505231e52cfa255555750d14dbdfb62081b3392281871f5c14d572c47e696470033708887de8333e85b03df929e5a0484851cbc4bb84fc20202333a1d084f2599c6478d67073eabed0d4e21413c5aec246fd683218f403855ba5494f1d2305a4a9f508d0eea8a4277ffb590c64ec9b192976133478abccb3fd119e6a76d5b9e36e8b87434a973e14b705bcb3b4c13d267f51199f00b9309d23a410501f705c5c1b049a2521e8907d91f2d161a99b90a36f96863c0c9cb51165a862046031620f782cab6152559168d0d84092534e3e6e856a3d249a7c40584abed906e3a415381bb94e41962bc9451558ceae7ea20fe1af12399b17f180d430875151b85f37cc9b9d1b9670f02ce2fe18d3580a4c89a6581c26df960926166c9018559f9034082121beb07922c1fe5f08c90c7e44aa504535407b9cdb6257165262e748b4251c8ca2b222c6c020313a3716a4e8287d4f06ef9dd7f8f207748293c2b4d56546dac8897c1a894dfd4c1da31e025351f8a652590b873846502b88fd014e2ef4d73e8e066349d7d68f6e5ca981bc79c120b62ee2da1fe993d340afc0f53552a26e952c25f511019724af90ebcdcfc16383677309104609fd37b4f38188ef10058f24f9d6ef28781e7b7f700179c32f16ad141717a7502b12bbc25572d5008853c7bebe2aa7cca447ef9bfd28093906f5b86bd32c13388ae1fcc21457452ae2f112d39e08527dd88173d891f4e0d7008fd2200818d62f71378a548fc1cf8e2d5168f1df0fc9e147c9c703896d4f4c4a1d485c7b4248a90346aced107a40aa54d76e35cd702948971a9e13e9b00abe593d057a4dc1f3033ab9a6685657815c23c5a51eca25d56c7bafd468549e991434f20e9a5951bb0fd14d47ae8c68ada8d761174bd7eeb14d546ce93eaf84b24307de5ed29735101e9fe8e5965f3fbd17cc7f895f2114966ec4a9434da8ba93701050f483851ee05bf57423ebe9eb3e7790b2b87d9c73d9605b3f22c12033e1f7106de9c60dd3b406f04c31b25de06f54ed42fc8dca4632bf9e8a0bb0a19fcfee3b2e10c38b0c61c898a68f6eee892dceb94aca32df9dfc49011f99574e45780497d3c441fbcf612fcedd5dd2d36368c1339818088fab8089c3acef97bafc45c802b868e0cddc4d0280fe0ce216a77e81ea18803dcbf2b9e45fb23d9c8f7ac61c13b2faca5fb8c7dcc44080bd3f11f256988ea72e646fb187acaa835a96e9426ce3defa3ceff8e18bb0722a81c3d0bacaaff4d5f87eaf2c9a370ff5b70f626d49d7e69b6600fa4cbd0841a95ace8cbb734bb1e996245e9da4fad2cf2029e359a24147c8227b47e869cbe7ec0cf43ee2a8de8f3842751707f5c3679a100ba866e0aa5396a3ffc293d82bebc2c2826df7d88582b23503a62d4361f483f7d1abec09fe5031aa75e857b79c31a61cf27d3eec387cfa336c9dbc75498cc9ddab6797300110b89c7a31b8d0012fc0ee14d813491f0089bb9aefe96c816bc241f4e7dfb1138e8947c96e88d420bb984c273d1c9a559fef39fb36d96d93c81df867e0dc126f8a4318c9a293f1280c422fb18936a9e4ee0402226405ec895cffce8d2d0409b73e1edca7fd8e118128cf72318a2927de71a477f5343cec1834fe91f6920e3d45c8d238026b78c242c382e0d7ad914041d69d2d9c5eabe978eea3472a549d568b7ad994aafcef1b88d4cfdb31e78162ae2e123b817f60aeafd39b694a355865e0825d6d4b50c6cab954f24209dde3691d2ecba45988aadc1e3dce338fce751e968f2597f95f35e4daed6d8f98f601fe037cf14e618f153107165901afab610bfd76c6339779ab2c5eceb8618d192f18c62e60ddb73649cb181a46bd0eecd5cecfb5a5804bef79c311eda4f7e0bcbb5a5701d8299c40be21988f317f988858d42019716274d7a475a7700251c7da6c989ad63dac284d5f2543066a8daab87ae4e24196a2d8a63ecc4cb1d6cac748c7f9602bb88a867c862196c524999fa120b2b826b5884cba251dc4d1c4249d24647322c4a55d8a0154005db032b15d24d63005a1c45ae1f3ab226deff795fbcb7a10d91edbd7b5cc24bb138ab9951f8c1ab66845fa58e2ccef253a54ea8a748e188fac90417d6e66a248d38a5650ae6c1cd3c4d08184401c831190ce3f78f2127ad4babb86f747a17beb3619eb985c90898e32a802e35efbe9ebdc05710ba74be152d8da5689f2ee7274cfa4ab8c5aae812e4124706814b44820f6580ac825c1c3baebaa928753ae8802523e8820158e125a09b9932aa1521d98738f0f617d9d6c7d401cc619ca852cbfe8fb6d7fcf5824e6e67f2047289f139220898e282905b5ed263223979de7b3e46883674f4877318cc922b1e49a92c7e9cb09e05b4ba9fa38c4051c5b0f668224f982882f1bba102ffd4ba15cf0b6b8bc94b73d96724f0ae4a7cd9dd8e669a31462b1cde9a252390cd7a139c7f54599bd9fc36869d20449b9786f2ae4f34ed5bcdb012b0179f7974c7975141013ffc9312a6abc4cfadb777b43a9b365ffd130cd5884df16beef330e44b6d7f877b6ccf92c0989b8ca42521cc2e1aa8b82910706d99d1cb22f1b95e955cd73462ebc2a708d87b208786ba8469f179bd8be6a63536cf773220411138c32dd48e7f9e49e824db974459bdc909e688fe6a3044ca2462145394fc681e7417f1a9fa55bc1b0627b858969b54f21af424f8574b48142f28f7d1a08fe58da523854ce182c358215126dcbfa3ad639465ff66568c3a42c2393b07f74902949d5a392f82d965968acb5109660d04b8101846de7d2ec2666f1e6b259a59580d9d56a05de2af525f77303fc6079306a1a4e8c04869e0c47a3758ee9f2559c61ee703aa095f7862615303df19ac822303b64db11601b32933c47b16071b2bf2dc45e0d2de1f61c128ab7320fc462def4acc6283426f50cc8c6e4f6de145703d500289c625c206943714143f42bd8b0cf3494a478b9dec60f31e2ce937df8d06b363d4f8964ccb93fd959bc472c6a11786acd2e820c8f5610d66bced6668cc1aea3244871e277e564120e80a3530fc459d3dd4b57196c48ad0d4ab65e23bd99c27bbc410fbeb3f5fe3018543ba1be6ab138ac25ce38a81fa98392fa1340350ff6113f513221011ccd9587009d952b91c433f2d3ceeb5f03ddf30b879847c91231fa1b6d36df6eb224a641cf826c17419a355ed72bdfd658ed43151e7eb5137a72ad970f8f5eb6dd7b9c4bee6fa2c3b59098a047929eff3fb4ca3ecf3199eb1724ea501d870c6e6c70d3c61a1bdcf46d646960370ba142fea5abe2b94021ef48c4e47474d7d641647e5bbe6201d3a2dea692971d5c55224fc85f6ebfe7389e9fff21c13213fcd984f660ae113f6eeafc62396e33c45262e27c04873ed8fe1a814930573490ebe359ea9670752cbc0a7e0bcabad382286fcea65be03cede99665c8b203c34d854f20226ea5ffabc1a383b6ea90bc2bf7c0f18dd8dfcc7c0495521e4f6819c95c2aa70c26f5ce42192b10d70099c958230f2fc9a406f860ac3b823f629f360c506a112846597746adf8a4af269784d9807e08ab4046a34bbb71fa973c01ea0daf5c70b22a16412e56ee07f2231da6dd118150be21be1a7309bc00b2c97418e9389f5aa1ea0443350a0e32838308e1b0bf5855244c76401a2e8ddc8ac47fa7676dee5e2d0105e0e271c7b69b8ea406e4b21ec2971e0d8398d78e98579616c57cc6590a17243e67543e1cc4e61562c9440a0819b80382c8886351369551ba5b2833634c4c0ecf42ca3e02102f6be79ff4d12200c55028f4b447a085af6bc4cab5f1987f43fa150ea7a3516f05b95a4e718f048bea7c13668e88552fb9775e4237227cc55325e5cb9e4c5f6f1afd72f4e0e976008fad620907999501b4f4448f0a26efe19bbaf1781489019b0e89619622fd2b3aa543c687836f0a8f471315a38b4df176e99276e125672ee6df35933e08d49f8c2b55f61d14657d53a54f262467cf6cf7e7178cb71877b3f40be96464599575e8a8cf6d4d8e7adb038a7174c2f8eb25be115922f4bed3886fd349b2f0f9946f6dfd1f81607d7129972477001878e8d6c0fe5a469a6894f00607e344fea53aca44e61da7da27f24d58f89c40aa8487c80e5607d7323e5ba6b409f8074b7c86c1358707f08bd1413f3495519444eed89bdbafce794a43448cd8ae97f1acb6454dd4749b3ec1612058c86802271681af9169f73d01f7ae001efce11b99ac550db45d7b34ece0a17399ca2723833001b8950707511b0a7492c237b94544142588c1b2b2abcd5334bac46ebc2e70693ec7f8d58dd5fcdfcee96acb1348033f2698fb90eb71692d71c127d8bd788bc9417cf17aa7b1732f1a04a7c47829e36baaf79c907b2f90270d22074688fc271e8feac17c0fd00556ed4dcf683eb427845232e6470d9057852cc26cbaf32671785547d93961394dc5ab3d6c55eaa5301a061454d1d3fd40eeda9a2aea6765c44bdbc2f410b255e08887cd6ff519662906a6f5c382116bf989964364868c2498bae45718ca5f655fe8cf8f08990f8468b3b389cd4af28926f52dd8747e06f5f12a4e1a73eea871d634f0c607ca9e677ca7257dee1b5ecb46bf3af6cde1231f64abc223c6e6d5d86263fd280e1b79cd256e6c764ea686fb8f1dca2eaa2907726c7e71beb31b0482659af68e32b365c94ec022087ead0035e1eba95d50ac003637c7859da5ef370d9c3fc2c2a2a1fb7cfe372218047e0c7dc5fdf26d7e9accd2cfd334c44d2def8981e34e7536f927c6ec7e380e532d5f10a6cf5109348403064ddd00773946c805e6e87161fa0faa5d368942a165527321cc4f69c7ddb0ebb2b6a578147cb6896ff3b34ba94da6fe0e72110c0c7d56b6d9f84ba1e45623c37ed0782c3bec056daf7f67ae8146c6bb61156d5b9415f485abc676cb79540ea3a87360e1743a7d4dee7241da04376af536fb3164ce3e1dce9202b56cc3552d555ce9dcc9d5bc287a954f1ccc65b3d9e8dc951280ce95afb8bff120ffccc7e13f9f7ceb3f6e88a52f523ebc8cb13016156270f3361c3bbe8df617e9d8bf83b262c54d42770ff032cb3c55d6641207dd95a5e7c21539131f0a8a1fe89c890115794b561f56abca77f8de7b0fa751a742fa8afa9156c42c240f1e80884071497feac6e564281688e7437f6d9fd0094dce765529128a0b01dfc415fc757426b7bcb88328285ba689344ed9c22d9fbce078b21aff4182e3bef5dc2c58a0dc0cc37254b2f4fe8b18859ef4f2f1d5c4948a07c0e9fc0991cb242795233e5e907b57571d61521d2402d5034e8f2cf25cf51148e3c19991650673f0c69c797c8a1e022406c4a78a181caa3547338195553c9869a8a44fa612014aaf9a41e78042574f6aae96e675d433ee9962e767214e0ea89564218def113df2256f3c01e3682643b7a1af75ef176ef9b7a41f8ce8766a0820cf4dbbb76295e85bfaac3495c64cd905cb5a23c28ad9d2945605372a1f347a4b789ac80b5c0c9393ce21bbec3522c5f24cb470fb6c4f199db0af83fe4cd2bd028833320c49e116c6bf743492e48f7635b6ff3d84505eb5002c85b4f872de22d3d4c519a862fb3fedcd4d61d32f2fb26de423e914af0b922b45cd0d6fc72d1aa6169274cf4fb90b954268b7637987cc2e728ff425886969bd7918a00a1b0a5fc6754a0ff99ab6e1e411e85da2ea362ae5a571f45d5868e3b03b951dd8eb38e109db608d176841d4e9a72534dd824e1149ec5b766727d2af12f70f5b718b3676a4b2b16f997ddc32ea671bc5e1c8f709910a5e01bc356cf224a64b1670f9dea094e41db9cfc58b495b3af2a5d40c0a96cce12ddb6f8d17ef00f07196c24c3efd4955ac1ab750400afb07bce102fa262a705d30145a4d079a8c88e83637831b07f3672b657b24443126477cb9b98fa65ae68766230b77e56851166564904700066a7cba6e8ec1afefeeeb104dd3d87140481344141606b8a05665701eed55d061d370586b216d6fb9a5dc524a29650a4707b4061407f22b3884fd39e90b32152d38c35dc18c97a90732af5cd9f68911937c99e892dcac02444cb476ec21be5c41d148eb67871168472660f1e9d4896e65c78f3ec30b5267c61df952b64f418320f89114d2210ecf0a423acc113d22a81870220e8d2e70ae4e882a42491d0900218aecb4351f7346abc994ab2e2a0072a2dc8689c661226513f944429192f28833294e121d7a114e90102c262ba23c77e33813dc45551c22706a777ff6fc5cebab404a54fc62c66309344c308180aecfae312b56a3def6e092a91ab8567aeb62bba2cfc6245f492b007ceeebd22e11f9da7eda8fc155f167b4da666998ab1cae8a2f43c56834ec84c78eecc7420e148e4ec538947e49116556a44e28891c499d30d6680aa796a376dda3cc1dc86de64759dc916044e2377303e32b26b9c19e60ad8baf1045434887f2b5d5a40e9d39f6ec66ac4087f2e54929254e84c9d7fc1985647cc940bbba902b4de5ed41ae5257c89edbd19e97c88cd45e9739d2bd7124a728d20042eadc1973b0c08ac99f11a4432f7a4d532f85b64c1a4634ceab394d695021c56ef5ef96830596c7ea0c22dcc8315716fc1cfb4874e8452fd3f5a750110b3e2ab524188d469b006151542445105c94a2942c45d0733bd11239c0b440cbed444bb480b0c377726587291b414ce00a811059e420ca0e7688591039f9e267a4ec762227473bfc0dcb32552e1247385e502309aed55062050717354db4bf6f33cae4818ca848153edcc0071d8cc462b119ad5612435c232e889c0811c176e0e3840870078830c490104d781091506d47f08679237f3be8f839bf0f5abefc98fd09e0bbbeade00f20fe61c4eff7dbc07d1c82ecb88f3f0bdc70cbf7ded721c88e0beac07d064120fef1ad05b90bee705f078cef7dec3a5cbfaf834f950ed7b77b13f54d7a844c3fdf48bfcaa694d2b7d17a068816f45183063a74188fb6d47acd0aeaf7fafd5c9a0518f5ebe7fa180cef9db57e4f095c68fa52f666639d4bd8d294749b89bb6443e29420dd26477a71902ec89d71d8e1fd04041941900684008226888062073bdc55f9ef1776e17dce74b7e9aba1db1a4c7124d01d2967efa39eca9f27b507c815d8d73d95b9f78cc3cf00c1bf2956211819c59f331e08b22de90c3923e3ce80f606672d6e4d763157a353792952975ff92703dd2c948372916c647b95e5a05c241bd9f16505a78f9ef967cb4aa7e94473b7197abf7e66f17aea9728b5f6292d792f04d91beba327afa7ea39e9bd2468a1c5aded180335130343660645450c125e7420c34507ffb16a2e1f4965e72155e68d5f6547d7d1deb4d26295e94aab32dd6194cd3e994867c15b8b26576b34a6199a46e94db2c5f4a8ad45d38aa64c31b3bfc92953d066534ca1cdee1417ac45f6abe9aa945a94e58c7147d971fb4eaaaa940d5699dbc46dbff0ee57d508800d1b01588aa255a99f03e0f5d4af11e3d472957d7c505f65463ad412277623fb1461fba579519ded2ce387d92773df97830e2b6d479971c89c489dee1bd926716cd1c5cf89d277fa9c999d6ef9d8bd10246edbd295a0d05d890a1d76fbd2296b0fa9474d9f73fa9c734ea794bafff6f22375af9452bacd3969add3b70d0cfdedf449a9bbd30a527ac5cc7741e937a508f4e88476266a506acd0109b63fc92e35a6b1882a370241e3b8a216e50bdaf6285f146dff6e7b942fa06c8ff245936db747f90289cd6d8ff245921d2110e58b56142fac6c8fe285d1de1ec58b29b647f102ca0e511ec50b27dbb43d8a174148c8664ff27311e5a4d56e1773b9f348a5f8e16048369d4c272640272fc6786219ca376059416cbb4cb57894d21617292e2f2e2f46198917981718fbf280605030a8129044808a41c5582156f08118183130962059d2721918325296c41cb64468c912dd755d2733b3e4e846961c944aa51824b82e1324c82041861151fc641bdc99edcf8406fdd497cb78196f0413239e50972916161696ff8c38a22d2f171717970f3cc2957d8081818101696e4c4c4c0ccd8ccc858c8c8ccc0c1a2f8a81a0448a5d03801a1698a6810c193264d4a4822e9d414a9552fd405b231a3f25476c97291a1a1a9a1a01c8403468d0a011001bd369b454242ae9c146003602087a05f57097b88ea8bd006e0470e332828a27841b9c1b1c2a445b817303e7c6e682d94e9404ca0d1c377004d5b610d021947e40e3c8c19173684e093939d78acc65eae6e6e6a684128c70dd1b376edc18c0009648b9317bb1cab15a61216b04921316394cc891036f276222c4526402cb0413ba289d76728df86162448c12ac1d9607245e899fbf7bd56d3eea36481041e207294c8e98d0a8d8f2bbb86f0b764ed8d959429bdb8996185121df22c409289c70c2931d764ec4a4042f6b4441b03dc2a6c420caedb0294a6c8f28a0e0f554b7911f3fea36414cb6ed444834718414ae4911124a9038120bbadb898e00bad559aea3dbd9d9d9d1a1e3450828a080020a29a49082d7e39e827f0a29783d3ecac3f35db7213ae2b5b1b3aebf3094715881a38ad98cee1d93da6052277b07951d2af840851526e5019a3c409407a887478c403c26550168aa00445500eac1021d810dacb08226b155061feef5f0e0b6bd11e436d4468e115de381a739cb7efe9916a592675a7b3eb511b311dbb3c6037bfa6087b18a3dbfe6cd7d54d6c75fdc97dbb97a94247adab97ed25af9f4ab301c332e88ec33429bd9b65baec255f63dd3b65aaaa844d2315aa9a2b9ca339b96b37c896d4b60989ad1f86cfb34adb932c9402aae63828134b036572e6f695c4c5439a2cde62a1f1dc15814ca249bab18ada982d9b76f62c275b2159eb224b0757708f38ad1cab36c450cd706e699dbd8e7915f90f65dedc768996047dbda9f6939eb641f8cc1fc6cfb2148dbf63555b3271ccab63299b21f8243db82b26dc1a26d33cc4b2687f61350f6fc10e6c7656e608d95a8d0d2be9e9984a206b6fd7cc4d148a1e37629342e49a1596e8914a345fae2de481fb7b76dfb68db3ff54a60eb4ba24397f5207df44aa0f7e1663f6f5f85edec7d71e7166d7b1fb7ad11fb7296fbceade94dafa7f4a4e79173db487303c3d875b5966777da5f121dc690711fbd1e20cc6dec038957f6294710e6acc86dfbb9144388d4b11fa3f5d19c95b77d10567ae9f59474696b1e27ef3b6dfde58935f73e60903a2c6fbf0a398b7bfbd6c759a7b79f4f9fe9b92f6f968fca94feaca5d7a39ffbb87dfae2ced3a32499befb1c232679de3695bcd269e7e9f500e171da1308b2f5f715a14b3f3f7a3e8494beca76e963cc6d4aa08c0ba4dfbe44fa6d9e3697c1d3766f1372d669dbc781c5e22e49fad2cbd325fd491f4797a40b56d806dacc5a64506b3b9790689b8be03a15e85aceb27bfb7ce4aceeb7ef5c73359f52c93fdbe6e32cefb7df80d8f26f6f5f9084ef582656c859f7b7cf69a2ad90fccd752ee0a9edab93ede5d7589d94e62cfc1107516cefe533a01a4530b42cd8dbd3e83b9649bcd9ee4b59db3fef7dd213234db64fd9ce44173cd850acd4aed268a4d0272018d861901d83bb4d9512a434ebf188bb3ea9c2dca6fa10427f7e05a40e4aa6b6dffe02ae23e3a9ad03bab2b730e2606fbfb7ed2d93b87343bcd97edb2c93bd7d9e5f61fe64cbdf36a0bd3dfe288dfeb0438f62cbedbd2f0606b88dfc1be46f5cecaddb9d0ce684e8edf1b69d5a7bdb3609a44f3e780a3a032120f8f8ecf084ed2f3329c542da7f66bb4730b4b13d469739d8cf381e9b725bf9b67bfb11887b8bdf5e6db3f7e18f9ff7751fbffc99c61c8790ef7dbe237ebea3fb689812981588ade89a2a1e33360d32652538c354d9e7913252e033a7f681b3d9ff7ca03c1b8fa56ead76957debdb22d1416cab8b6cfb38b12444ad21c36aa041a6ec4bafa774024d3fd2cf063a83d4a96fdf0172158984dd0e7db6ad8f4cd9af21840e71b68d2d5a93123fc9d274fa7cb3f47a6cbf43dadbe1e73ecc71bf398ec31cae25ee2f7703ddd9c8ce8f89aee861cb8e87851572ec13a4ce0ffcf939fc3da5207d697d1efdf787fefcf5b395c0f6fef4e170fa8b83e9f554d5c73e3e1468ee6516204d320b88183855aa9f67f2481ec97df7f527e302f7a304badbd0e7f6be2173ef32dcd3cddd2ac361289a850baea084b6cf33bbda495b0383367dbfdbd75b6bddfe6e1b4a0b1a29cf54d1fc42ccd3bf34c618b7fa77bbf7e3ad5b84c9146d2253d4242b424730a5848e015f90292a34573e8436cd31c61823aedbf615a7e0aceee9f3e41fdbdb87a2a54367da6d5b8e2186fb2d1f76bbbabc7c8df9fa39e68b81cb42c7f4880163e87a5efefb70883bbe2fa29bbe6946f33ccf3315c914952253f467288940d3003a40a6e8a792d0a6d854a54043e1d34dafa7660b1d9a6273f599a4489d97a76f2a923af7e99b4c3253d15c91f0f44db3b99af134c6d33751148a448a9e0d2d6cb4dd73658ad2fcccc7007e8ce79130fcd3781a200eff4176c0b8628ad02eb57cedc87626d92773963bd954b7f68cfe7dd3fb903a3028fa303ee6eb17836a89e981f18537d00de363debd1e9998f7981818312f03038c41a6680c08038a05df15bc816e98ef3c1752b872e5ca8679f70ac0f2f87924eaa31be69371017ffd0afe60c95bf71f0eff5186838c8f455315631f0a74cbdfa73f039459c094410228b30089810f630c6f20f1caeefe6532a143930b38c4ccd337c968c9e7906e013b99aa7f5fd7bf3205b27cbe4ddf2c3459094d479bbef785349f4cfd99a7add60e90848ff1347e8a88b576548fc79782dbd0ff3e1ecf3e6088811e60bb72e58a16fbbe8541eaf80b32453f19f8a300dd17fcb181fb3ba2c314367d12be18dfcc178ba68abe8c064c331b3c731565be83c6d8545114e8df94075c6c93ac04936c531cfa2938cbc120948c0902870c1021dfbeece6bd990bdda2e56621eec41914a61945221728715a951d826843e87ea8bc64d1ac76d5eb9109903b5898aaf80e90a9e821a16f90d1b0c37e6cb57648503e0cf32b41083a3a68cf4705610626d8a63d67b42dd75cdde46089d68ef8f16dab892f5c0040d3616ac768e38498cae1ac1f67e51dbf1e599f697fea151f6a1b7bd9d61a2e6aacc41bfc55bc9e9a591242fbf3ccf9d55491295c53a39142571a29b44bf1218a612251d064a822573a5e53069ad491df85d4f1c27acb4f865aaed1eb24c9404381d66f7a3dd4026ca26a68cd550dad8626436daab00cb4a9c26f818c36fe3c3f1d2f699a2f876073d29a3369626352ec0c5b281bdba28dadd1c672b600933a3a7488166a0d4dea843534f929d0f4654d15a9231f7f4d4dead4d0b2fcea4bdad570d1914cbe430bf433de0697c2b9eb704ac039e1e6c841ea3e2637575b63e5c7518d15b9f511142f3505b45e687b3e1424500009a8b48145d103197a700511f40144507a29256dfa8413d1c9219329fca10988dd997c76d7799d09411b7728c08a76f7c48e0e22f1067f98a3ec2ecc5276d7752623bbc36208dd8f2174b2bbdcbd57c7eb96ee8fa33bef8f2eeebda4bfb7e648c7ebc751098ba38d9ff4a38b0962d1c5c6764617d0064657112d882e25b6f599e38a45367e3b6d9138735c76f2ac007be1c6369ea669b2c222434dea303d7e198ea40efdf865e842eac028940532e42a6cd28fdfc2e6ea743f4a91294154b1b116b48d6d8876a886168f6d5b10880dacc769c70ec4aab4a20f3eceb21f7d00c2bd38e2a20f3e36b01c663107a3d88198b3b20ffbc12081e812da3885dfc68077e8efcead52af470ae1400a3bf4bbe28ed38755e63cecb6ef3b5c7187feb016f90e20311deb5a80b5f044ae74e8435036e63a4eaef4fb53ff166073a541cfd9bd3002068ee20db6b9731dfa2618b1b19b50f30612aff4c8ab9adbe0c71668e7b468ce8a1b7332f984094128c0367e1d44e28e3d125d07918d3fc7508e580e282b2d36fe552d6796e3b3f1e7f490c343ce0e39ae9922fdb9bcf13b429b10f48020138298d8c1c091df3cd933c7f6fc3043d953ef09c2b2ec48681ac18e027efc067f682ab2a5e9b5e503826a8e6c62354799669ae3ca6196e3d22d3357069a0c55e4ca024d15f628ac6c6c03db605a36b0dc753534f9713fbd1edeb479225336b0cf2a69b2f1538e49d0c68f81a3b8337f8296082f8e360669a4d0dbb68166afaba1e59a2e640a1fc9148632b3658e2bc7e52a2c7de8c9c69fc383af6c60dac39c211b87261f13111b6427bb7abb6ebb7ef51d05fccc5d3fface0382e20d7efdf5b5af4c5f9f3a8bfb5ac19ff9a55a32851f684fd3a73f1b3455f815f013772c114558287eac12b7c1ff8020bf22685b24be02527315febb64639bc4c6130c5144d81ebf057216cbd5e2cac6e0161dd8f83b20e647293225882a906cdc056dfc7668e3e73258338e26e6abd0721bfc3864a075bcf253a008036530742ea6e430db1868e38f1428721bfc394574e85574bc367e1df87518913a2ab43e631857e8c7be456b63fcf4814a54e8d006b671866d204e5c67060904e62caad3004fe10f5d8b271b53a94980e62cef23ac4ad1c6555a1b7f0d42a44e7efc356cc0a6606bc077ec911a5aceaa8fbfe6a8e66863207377ff63c3b643f067638bdf1e893b406af1c8c66f4a62e30f615c1bc7c299a24c731bfc0df02a1b3ffe19a44ea6e219082ad9c76df0d6da180fc136ee409390dbe007721bfc1903a9c51d8bc46ff0639bc46df073ae456de3ff54a06b6881367e181e056de38f3ed414d0448c3b9778b8a77a9410e0ae1aea31f5ac4375a847ada430880f21453aa8e6db4de3f3c8d1bf24d29bc00483cc287b907f36bedf551830609c5a736585a60a3fc618bfcf55c52d2c27530e4c773bb84e053afc9dcb59a4c7dfbde66a3ec6ef02865d0f1bb78061f7833fcc4b36fe1ce4acfcf84f5fb89d60e3d2e3df827056f7f84d9f85626920877cc732b1316771d8466f5abb6ddbb66ddbe6f91062bfbe15fa0bb88e7d81a7f0638c31c68f31ce0f7b8115c2f833fe5287dfe3307ebaf3d76dd217b7f7c9e477a3ceeb11a23f2d5318639f8df1e3e7306a630cfe38fd64135cadb50b7ea284e5a4511e90b5dc274ac498622c4f8a340f47a727462e259789e849115e482efd01ce6b958a6c9d46c148f2476e9c37c618bfdf1d663bd03c0d038ee341c64351a76780c22b68519477d05d578cc3a2b4f1f021868964600492fdc1088f1f8e4895eff06ad7a98797084e2b583952f275e0a806d9033e452e3dc0889a0082c9f583728268e8270692eb02832484a22401c5e5073db39d28092317225cd082f941b36c27720110302970c111030cbd889428c1a6db89624f7ca0194233846608cd109a2134436886d00ca1194233846608cd101c19886dbfc6b8a0ece32cf9d686ed436ec9b61fd6a16dc1d8c94fe64ba243d48e3bcf48e5ce3ece9a9bf4de877793402e066a1a29f49cd6f706e270ddf837829c55bf036dc49c155690d2665a16ac45dffc192152e769932baaad2136e732654f2ee62273d6fc962367c99f1f94962270310e8acb59f8a70d3c4e5bfe4c0b8a8d98107b7e969f0d675a5b7ea79d73b05def174c3c2672529111848e4f63a561d7f7696d0da82fb494d63829a594d64a6d37e3c4e6d7091d7f36b1fdd7c446e7479a6f139f52ecbd28148db4ba748f7368fbbcab39e4aabb4d3a873ce51fa3f4928853e6f341f7396339ce39698d324ea0e34f986f915f07355aa0e37ba76b1ca12ba5d469d8f3e9a76242c739a7a6b37633b118a7533a736c4f299533b1f9bd7dafb5de946a09cd554c5b6dad758b8d368f361aa5c9a7b51e3141b96d5b653cc2759526a573d66081f68f4e7dce39e79cd3e99cee74565aaba74f94d2165b27a594548a95e230b99ae9123de77cd9269df7e79ca6d3ddb66dabf408a7e484a2937a3db52675e8dbed524a7be85dc186b91cf3bd945297171899198f72b8cb9e472279b3cb54da225e28a53030c598e370ac5c6581576aeba535a078092e72db44514a6360ccc4c894c339775d96c2898ecf614a29788ba001cb3e7548fe0481a27f7c9f13e4d68cfe138c2fe3760739201f68ffb0c2a6efdc727941db4629a594524ab78d524a29a5944e3a299d75d64a29a5342aa1b5ae45e665ce19834e1718940d994a7154e8bab95c73158d6c3e3228460ca554069d2d77935e01b6bfef7263fc89af888da065baafc947e90f507cd5eab22d266fe736ef9c26af67ce2e8b8f71674480a7181ff63c12c9d3dda7a70a45027832e93927e8460ec5a1bc205572855f4a0ac617feb0c58b627c51115f9dacba3caf67c657278b2f17f855d0b698544a6957b425ed64f369279b2b1ba593c557378baf8ed66531413b2342c7a019aff8922a295779a318dc5ae291ce85935d9beb5342470c825287e4b91001dde3755e1028257b8c52c618638c3f48a178b7c79c258b606c6f4f780aca5c6db039e43a9b4ca6e496c546842db7a2a97ac275362c3c25933a1bad286f59f8bcb2d1e66a1eb9ca67ae8ee0ab0bded654499f2d6f6b035b0a914573453fe034045b1679a944bd169eb66d1badd2ea4b8e0234a923ad05e89685afeecb8d08d71b0285371295bbde19e7d1d6721bb00847f7e707b6e3c7bac48e763ad9e6922da59c451c882d7feee0ab0d3661d3057afcb02ee1538aecf81b6caee4bd3ad428d46330fc3075b381e1cd0e3798148a9f4b54484ea690602e54c41220243f41bc8af84c205cb14403400fec69cbd65c917670557c205c457c827821f9994b80762409edf8a12c62c787214192cb6de2cb2881f60f73cb410ddba4649f119e3ccecf73ce18e79c9f0bf49c36ba47f7e81edde3a45fb72040e6a6dbf4362e56d39d9f6c4569ceeba99d476dadb5d2d7546dafb9ea6437c3358c98f376fa5125bda56647eb8c5aa06f92af89ce94bed04ddbb2ab7533cab22d688ad540a2bead5bbd15d708346b2c0285dd2c0285dd2c0aed2dcec0e57437054f40459c6a8b4b0c0cd2cc9c47c35c9148a5128d44a7f769575115f7c916282594508254c9152739b09b7957e957b076b317964d4153ac0612d7d65ed7d6e62adeab2d18a264ec30b66af284c455f297b8923839897533a06d57f054bf6d7733201e77ebef05b3e9931406d9d105bac2a0dc8fba99ec66dd4c7646b2825bf761cb0ebb99ecaaa05edbeb4b42c75a6badaa201dff24a3f941091abc360d7c889cf8994862b15a2d66bba7343be79d776eb3f37a2c8d3bc707bae6d074fd1035bd1e3abf2b6ddd365b67690a1d5e19bbcddebbf5d86ed62ae6324ee5e06098ca791fc71034a51fad291c4a0351790712c79cb5d10fad6bc2dc866e5e4fa5291893568b35925c65db69ad35d4daaea06f51f2214adfa2bb59ed26c41542f7f4a05ceebc1e3a49240ee8f8a5e82db3646269617169697171892e2c2d2517af87beb8bcbcbcb4b09c5e5e4cfae5e5a5f442f25e5e5e6a66ec8a907d46886a6deb20cebd28773c3f9c69a4d0fa43a2ebc3cc4e545cc7d264cac2788b859baac4d82cb6bdbf3591c376297d9692c459fe1d12dfe98ec41bdb01c1cce62a4778edbd30be3b55dcc77c3e559666b1b0596c4dd8ef8e7447e6bd2ea5161cda2bb6edb6cb4777cb27734bf6ea1698dab6335ba25097a67a3cf25f3007a63b2067a1de7ea744eab8bc7dafe52c98b7ef69c0592d6fdfe4e32cfcf64d3467bdbc7d53cd5976db371dcd55cc9fde5dc7054fd96731751ea9f4ee3b5648bcb15f7af77c08890179240b52c7baa40bae6379f0947d7b7ad960db0e2805db5a1f2671c71ac51bfbd6484885f1605df3ede90533bf654efb2e1ffdac51b4d808f5f98679f9e276319af1a9cf864c5918abe7cfe8e1d8be3f59be70ee13189aa884a756a8ed973e1ba24761515c784117e66f9f434a4aefa59352548b459e44f0f667c797383b6c7f227d0a144a0e71996d4d557c1d57eacc0f9a4fa8d4ec17363d0a11d160000048007315002028100c08c562a14816e560b4eb1d14000d7290466e503897489320076218c5186288510419020031041965680a9b00b0e30920ead46a4fc1bd10d979d7a81b6111e9aa6f9276cb4de45906adcee10aea430bfe8e784f774cdc9bb1c3b33019c09bea1f0f9f72f6ae6aeea51bba295f0e84ba10fb0c8bfb61d01d94a79369ce89b0a60637c7e911827e9086884670cbdc13ff1c808e5de11fd6bc8eacf2e303fe70559e7063526cc3194b300f58f238327a06919bf4732d6c4ce80e4e0fa0f24a216b65ece2b6eb01b34a56f8899f8ed4eb4902d29a63a065b99b4ecd7b2bb22c26ec11c95e18500157a418b4d0b9d112739041d55c0f7b5053440bd2d562e2664be9aeb3a899e9e26e1591c6fc8d1fadd43d65627d8e6d81b44566aac8d1ae620a48d1071ee4eba5964c4c6b57ec3dc3f027be5a70c79c720abf7b1c2c74582dcdf088c95d10d96358500b2ee4f5c06400794a9760a36577b266fe5d9aa9bfa59f2d287c9d6eda5f46f80c25d294dc49ba7044db838d5c28480f9505841126695df130b40b36fd363f7313b048261081946aa4e7852887fcbac057fbe319ff9bbf8ae038214cb1e0e235a46f3876e95b101a554c59f2b8d603cd30148f17a4ebe9166be0a804a8e61b0879ea1c0330f9be94df0cdae58a01dd24a4fe2e4dfeb80e8c03bdf640271a51f46d9ff2157e241a0fc663d1249d896db0b909b8c6175d9bf616d6cb0b47cb341134f85f0aa72054bb9511d07fcbb111f682b80bf16e67a840134889a5d63ac2f1de9349e20634eb710edf1fc8dc28079bed2254f42bb037ee8cde755d2bbb5c912752c7ae4fe30d151c555f1bd0492fbab901e2358691c1451c902a65f728bc48624169e4bc9a0a85aea5afedeecd0bbf44d0af24a5de460441e1880a494e38911aa762ee81873b7fd5a0ec980d2df235035192cbb04bcc7bd880b6f82cca22f466edc78eea42b287e0a93339f0998d6b17933965e32e2208189feda0d61b0f1d46c40a49a528cc9521ae57f7dea2925e45f4a440cf298a082ab966a18d602d432b2dbd4e95b9830947fb458d2b2d0ac09b7ef222a1c28dd24883050541036db1c6dd0e351413b1825c4d1e14ac50f4d91cc916929e43e74f09c34c79e1a7635cbe07e5da3124c0e6c8a19802f119c6c2d0db40d0a36dc2b90a3ebb9b2fffdf0c649e57670c3ddf6edbf9baa8139a67a96efde84c77c22ec3bc6fef8fa85ffd994ed65287cccac5c3f2eb37cef97ed99bd8582831619d262a9dfaaa9fbe956a8a767f6b7ef56a95ca708ab61d9b5c77aa4ed18d4ddfc1125ee60a2cee4bdceac4d1e4c2b60929294108cc5d2ba48eeaf0e4238c47342b740df21d8df7351772836369dbfdc28a4e44132cb56929edc79446147eed57819c1e0897d92cdadf70c0b17c42f60338d64a8f085f4538656918f1539be89a20a2126a94a4f4c4dca48dfc44183278e63e4b00860184edefa7b983c1efcc11c20c526b831524814cf9d90d4260b288ec9bbcf311b9a3725502bf0fa54a51a1118a91fe5880d71532c748b97823b35333004b3d226851ab6caf3f56f41e1f5803f7d6105b882341a58c8180eff351cdfc76f36f686e91bb94071f26e8087e62fde841eabb85a53492efd8c38b1b080ea9c6881f8904fcedf328348435c12c26ec6640026651e07435494ad7d106b6913d36000bf25c83a16a526a8afd622b0ef5a4946579bc122065bd936b7b69e708a76fded6b661ae9a490407b67493cb9afb921be40648d57f1f4c151ee9e8e26a8cd20ef80cbeccb549dea5919cb51d1b6aa02d1ba0345398241205d3abc2f079eddf5f4f416f70169c350693083c252d443ba9fa80fb62a3338531fbf3092f8020fe263460af7add2f94352381c855771205668f195736ba844aeaae95b7419e3043c65c44a7cbd56a0e90df059b0c8c834b0fc20db8f00e8b8f75f698379b5df1ec65a813a1ddd18788b4e88db55c8cab2b76c15ad828ec983d862470171eef6d2af999da5bbd210b721379b7014d78a8172ef806bee95a999233eb6baf021b7bce3ce5ac50880ea0e59054595f48eb56fe28f604eb0b1e4de8bea42e9ff9c64dae4b7a00024d79d1d6970ac550877117367330a1df1e6203affdbd87f077f7aa6a4c38fa309b77ec23d2010cabc17350165a3dd45c7b8d00f86184474e37d8397d015f390f8a15a161598fa9c79e5b6f902e60d57c0d8f60819cae1abef6b304aad6737285205755cfabc63ef3eab44e4829495d66b792e1dafc4438526a91819012198a8c08c18b1ca6b66f03c69d82084932c8082c13862740bbb1a0348b5f0ffab93b2355638944cd6297433fee66b08408fb9a342aad83a70b77520648c18d688b8a6482013fb00fb95a060c60abca70190106874b0801255d74ba84139834f5c03ae8a543d9905cfa2fd6f00980feeffd8a41809f28e7ad26ba0ff28bd8513ae7cf457b8f57d0a8331024e5e1f82f6ffa2f53fa75eb187a062ff3224a6dc294dcab181040e6ad62271e009e2f8180590ee113b184cc5b9097cedafb16a0d04e3bb8aba4b8268d7be7d2be6a4c2aedf9fb1191c56f4b7c558238f4bdf97f1511ad5f076bf908653c84a646b3dfb7dd55bf65f35100d64f3cd5535ab4c6b7e1e78501e94f33cc24c0bed2f521bc593fb48cc3a08a479521a2b9f594c71e1875ad02aec5d04ee0bedbf3da233a3fbddff0fc1ef78e51fa6fa6b81c1c5348e20fe8d83d26c3109f43211619ba836b3da19fe1753e27279e74734e5bf022de85f77fbf948a0833f195dbd73ed902814d4eef8b2dac1b9d727d969cf6d6688d96a58f171baf51de716569101645bff42e707f4886728b2b42103bf97668e1b9361246e4b2b48bf4044bdadab58eae408ef756cc9ae76140df054b0c0774aa457c4eb4397e3fb907775689eea47c8d875edf337ce6759005d77a05addf1b24334dcb619dee6c451dc0d513b39491e2fa66369e3ff9fa3cc0f034c5568d39fbe24b87253d696093a553dda91fa6559abbfc866319ebea494a23d60d90e3a4c3f04f8adf555adeffa486aed2757e9246cd3249850069f9dcb980c895f6e9e71f2d68b9f775a7e02dd42acea0577bd4537687d58e10d7b6ff1aad61caecea8369afcf908922d6ca9c28793db8983600c2b2258845cf9cc9c10dc80617ea328a530ff61ebabb03f236a0f30804c379b46c2a28aeda70a9973e24a87d327add42ca5215eb7998d41f74af81d646d01f28f88948ea9df08320a267155ae2058d4a0394e4436643ca8e96ceb7a03d1d20fd0ddc461f169fca26b05ea6ea158e042c7a479cc03e1ca83f0daaab74d86908465ee4492aad09e3a7329d6726c54083621862fba0903bc97ac5cd4d899d82727c7d3a0cc845f36fe9c06e182d94582b7956127c86c49ea4d3468da05feff6541921fbe033003324a4dedcb024876e2040dfcb0d6b23c71d743d4761a35c48762f158ceec283a8756a4e7a3799870c0a1127380067ea4521b14497469e58d5aecca547ad9163b29e12b18b33248b0113c03c31083040ae9104325f928574e976822b3928de659b46c640bb638596e9c24ef0d7a445a636231682c77711e745a64e8b3b1568d12f5b51c2e231774d9a683c07f702fdfe381ca7eb3cf3089d0861032b4e3920cade81fa8ea3884864ff93c24aa57a58df384296deafa96ad4d6cd0644d94d6df0d6343a97133cfe63b8600b231523534b659baa5ad8092f24b8d3793f1b5b4909c6ad54ab2c73f807e9ce6965cd9d5271328c8e7c0e3c89eb6a0ef3fcd23f5b699e8c07f250b522a501aebe0fb2ccaeaf416b5165f057fda5c7f0e87679e0e9318e4e750142f42ee7acfbafae5e645f00437246013effde999bcb9271819083a2635c9a64dc542246d85b69f73567c991275debd7a888bd5f005040f28a928947aad8aedbe6b523c9a8a4e4f957c5d1f734bf857795e9ab3a66a98d11d54d470578b0af8be296c9ce49e77d1491cc24c720950d34df3cda03444c4e9b7fcfd362722610198a2fbb882af3798ce5f02873d6355b6f126eb1460d19da781d22f0aee0a72a627466e46781c1b1b2dfa76d4900e8df54eb859e799f1508c88bd427860fc6e94a68071a9dea94b15367cf7908674ea02803470f3d112cf409beb9ad5097a488f1a2a3e8ca83745048401e81c3832f4c5d467c8911f1f60155ea83ccb3bb8932195f182be668c93c1ada6f6b6687bde739de7528e80b1efa5ecf5a6d44b63ce5358119384a003172d21aea235299696971526376ea9ea97941b320dc56a26e240b8aa2a138c228fc2c5ddbbbe174f05a589854681e9dc57f51cfef743b6ffae078df97c69c1fa9e7ffc2bcdda5acd009efc65afc63057a6a7d3e47966166880a8c78ff9dffb490100bdd5ae8402001868708db3d16990f7f350a349a5ac3a5e16ceedfbe5df11a24ca65d04803fe33a3f4078f93ba4ba7952c5b67577268ec626e6c44d6efac38881343d6f78050f7e5c8ec4cc90f87fc9eb72083e04fcc8f75bf826df32c36cdec618a4e47e70ff781907e880437681132793e92cf3a56b45039e8f7ef15fff0f80a9591f7bd44b12e4dd6571a6f401b85576ad2ddfa492b8939b42d7051e61299a6bf0060210f44937a958d24644c6ebb4fc648abbca85b1d124a6f103a557597733b202fb2e4798e02ab97603d74540756bf2c820b0354afdba3a45a6a2b751c292bf7efd94972de934d5683778aeddd632313458ba9fdbdafe155fe8a7748c2b49052411fc95f2c8ce7919c48bcd2b2ae1d7538047bb912b45cbef2fc3988b976da5f20406ea912e1ed4c04ad5b470bae4c15707ed067dea1da92a18cb5c3511f4fd8b1c8ab434165e0cf102cc60fe696dcea4f7ce188328608fab61c23b71a7a3adcd62f75e89b3c0ae38abc469ba03310e71de8c5a96d4bf7fca5eca177b620d25089fce433c54460086b1e00985f95570976868ac44122524dde8212a29274d766a7cc78c2b82b1957dc15b4978c68865dfba9ee9c421ed8599046b06568ce25a25129cbcfe3cb4657793d4ec9f847778c3359011939e43dc8c44abf973f77aa0c9fb61abd2652246a2b5927873bb81dc96750bb526886080c6fe8799bb7c0d59a6db92fe5eb01066bd041b5dff1fcbb7a8e95c56f1cf3c6274091a30dac9d18e18fbf1bb619df6a6e886d4ae923875dd9206d39904cc82b8454380f2f372173b1c8b53ea3847a0712505fb46de88500d639aac0dea8150ad3839e351ee36f49678890fb41f0002f642e4d3262304699fc52710000211f09686b40397a620aa12f53938a4e27041eeec5f60ade6416c1d64bd1c1cf70e3a1e9541c680386fb30fd735e874e03d7e4615a9feeca5eed20f0f419c8c242e78b4eecb3cd872ffa4313a9e48878ef473604ec30a930ee4403cf4495100b573d3b115efe3dcbaf5d07e54cdbe5417547931969348c606a3a4811f8a59a3d1e44b938752af022f0910ce4e4347db32223f9d06675eb723a07b73dba905e3b16dee9f0acd83622a0d0539bae5e012413c2b08788302d58b6c0aaa32f1b1681da1cf629c38ed23346feca9e7e706dd50068792822777edb094c19c131ed621bf54620e16e4836e1cd7e7507228edc5e66c28ced37d186edcef1ea2657fcdc51f0761a21e053f8c9d37a9645ad2563d9bc3a62e47285f3de95dd6930827574d35e70f2e086668e212ad5b7f85b63387ea62cb79c73d47d2e8558ff7affdebc2831fd8d6cc69f30f214f7c662776401ba46f32379ca16ec550287998ae490e57f262618363b6479237f6d67462d361e87c04d17f66182fa8a09cc5d2661f4dda2c8b2c18e2c4f3315c5edc1ad029cd1dc46fe4a9d5c34ef3ef9a472ed27bd8e6f9d753c73a8e426cd6bbc71184ae12ff9f2150249ad443572dfc85031c35fa49794fbbc9ef518eb5f4ced129254bd051f605bc0e72c5cb204d5d2f3c55b88dac16c36fb588fb070585dd233b463de36ddf8265c0034aac653412b53fbca4aef8d433e106bb211e2ae19dd599a976f968c3681cd3fd58a00ce345d5a3c1a7fdb8b332eb816a44ff99ea47da0910c4cd10ac471fca6f2c2ff0f04db013e15815738d193142f6353b60489ef1c08711c4d4f46926337cca5bd27942d68159efc55eed31e93819ada225e8bc0bdcb28b6f9325359a0becfa1d56bdbf7bc48db9cc054a0efd03ed40945b8134a2af0eb6319c12bb39a83da3e666363e6e6ff3f5434c39bfa7087d3328881d7673ee988bc558289fe36392351d0ff702c94f3d705798f5a1ff5317691a5269a4c6ab40b3eec7d519da6e725f74f6c4d7d2d9ff189d6c7b21660c932f66e14a543de11b3830423583af10ec5ed0b1e0b79cb7df5b59df9a3b0571b5df49926a7603d86cafcf26b8834c86c513d1a369b6ce32ecc47046636c5507cef0f99789ebd36bdad72bda98a4508b91bd5f709558d57c0669a2e43082f4b2435f4b99de7f473a31678adfd1e813ff14cf80b07707ef5db97579b388cd60f9a58dec891d8a33bf1532938b3cb54d1fc24a35d33cb17725db11fa78de04020a379e76bbaf110b8e58c3032738a5c057ef71883b8ddb0ec82a1dda826ea762e61574ce53d8e157fa90d7ade4a60de1cce188309cedd550cfa84fa74aab3ee10c14b6f3eb7d50a6315e16ce339b0f2aa5644801c48a9564758438ad67942ab1a255685f4f340799d42690abc5451b8c387a6ca833b89844157c15e85d508a5c80a8cb200d7a9d830f48fb3dded7edb33400736eb9107e7e1cc690b3ee1e871d1b32e4cc69e6de71103ad6738855ff07b69c0145407f628186a51c3ccf9938afe6bd56efc93a9d3e0c7445f6ac1a78143cc7ed58c9a33f716e34bbe803918be3b5c0c952408cb0db7e5eb7e1f9c0ea80cf4eafa22139cd7cc3a1c260bec2b4a72ff3c165cca7847944abc5b0965eb903b41ebfc687f9003ff792965a18e02d79e07e040b30ff402bd24bf7286af767600c57e928bb379cc14a09d4f6624e5b96efa093996e2b6038b09bf3c8d7ee6c10d32c75226ea014049dd53da8bceacc8f30e94d28ca6035de9d2c1cacd36bd423e58f95c27cf3bead7e3d1ab1129ff2ccc2dc7f67e9620ee6251172b6f76cfb86e8dab5656fc1169b0a19c9bda8a3c53aada03d05cda8f4b0546bad1607de6e85ffe07bbfc11086c515f0e36e724d15fc6f4ed6b9c222458c538e3f9996009e4bbd9dfeb299067d846c2f62f66020c91595e06e180b6da1990e30721fc7de1c227ca9e649cb7a17b3fb2a49e7f4228a118623a71c9cde7342fad7e689604cbb83138903ac0a4e65454de2d9d41151cc08144f71ee96da3c4338f14c74765070057cb5672559ecc6c24033365d6a899b531ba22140952a97b74dfc35fdd0022bc8094277898d502cf56264d7088bea391afd293c587b14c4e356bfcf78485ce3494ad246355e07995549aff49b47d6ce9cd5a3986f60429550e927a79a31d15bf1b52512cbd477eca7621bc9a71e9246710a9557ca8c4fd79a545d79819fbdcf9ef6a66633eae02c0b2af4aa0653de00064ecee1883cc27aaaa0286ddc2e780870216ccc6c8ff40d1f28f9d3700d94966e779fe88722971811ce4ade4da2edfac898a486b0874ef7347d7e331e89a4123988cff4d323ade34455aa905803cb0ee6b2df05cda4dfcce2be317422e80dd819d6e9b0a8e94ac434d639f7b49078216945f6df8b28f2540e008e4ee68b12ea15f26f54f5a60e32c93b6c1a237991670f1b4fcd492760727642ca8daae0b31db1ece190140296b2c0c2cfa77cbdae9a1ac3a02f4503b1999b799ae9a1707877049d6291fda3f848593c67ad10b53863940d2aaaffcd4a1bba450c1e7eaae5b0ab0d94a5514dc11681758dadba9c10ef5a1f3c62ade31707d01606e7ff64bde50b80499db1fcdff5f29ab81e5361e6262f6c098d93335987d3727fc4b307347499392395214e9cfd4a0d27b568a17db43dba67a59c952942ef66386af0d10a8430f7823c84f8c34008d3669cb8540071e85711ae4b21a735719d1654b9788b0b7c81d353340c4bf06bd550a3c85d3951b01642afe4482554fb1b8d11264df68277a1fa5e26dfec175858c0839188f2b0d7458a866b6d2f4b4a2012e458d9570772d75d5085695c9ca76a2fc1bd5d84dd1ab600fd7f531c8412b5581504064a8292c6f2dbb95934faee0228395d203d1beb4e6186da4346228a2b3afaa8ffd5cbf106a714569c80f74ee2e442f7cb09bf643ea285be6cc36b7c798f376dbc4c96fa188e5fb3ed19870cd067729200efe8ab622a681e0a4957c02d24e10d82055dd6d0899bf958bd436f601e5dc3ec86f53e906b3e6271e1122a28b374761f0a25a661b3db35ae6b7786f32fb59972989b5f247faae71709127d2ce781083c7a0947a3f78d0068d7ff24a704fb94b9204ec0845d007c95c4558be372f1793f99ce43e47aa71e3bb14208313056a5d1295ff0be680283347b42e307f45ab0e4538a97e1e12de536d04649b7c1ed8b02edf95109b0c787efef2678b478a16d734a1d9aee5d75c43e25b811084ac187ed26b5c9a02f845bb308f84ff16b0d4805065f8afc9425be446ece71515be4de6b682442210efa9677ff8973720fc5e717aec5c06981e1e58840172d5743692b545f6cd61d75390b8e719baf501b1b5b6daef62225ca3ac5e2520060ee435bdf743f67607a2345d51af85b69a527ad4446a49e931a6de11d47cbe1a66816eb0e665a44299e7eb4a9823d73b66d43fbe0880f81f2e3af9b680cbc2cd6f300c6e40d36ca623b854ee669e3e91a5f5c6236b6efcce4345dc2cf347c7e34af2580bc21b3b3b5035bc743f9af531330546d71a5bea9eb1c924c1c45bc469fb69b2f1ba00d1eae2a800c76bbf7c87d9e86b9c749c6354b2e68db5e2d0cdae3d111b39103809decc245ce4d3b0b4420efb095564fde87965901b919f97bf2b76285cbe748809d68206f07222ff38cc99b41079c0d7e87076b1d15298900a5bc79a6cac73a611306cce42360ad35b8a6bcc06b3b694c6608ce1cea28d53e84a713887a535e3b9247c7dc7eb5d3e4b016183ede005a60fc62fa4d51cd51e13f2cb0ef1864e4a21e63afad17b5e5a136795b00580f79d1a5fefd5308e188f6ebe23851b51ac15ec687a7aeacf65f265e43fd58cfc6fd312804dca03e84541d3511e22686961a60be3688643f7d17ca40bf8dcb5285580c791426403d9f08a38ee2efde5470695a85791ae16a91de66ebf2eca448a71410bf3bf8c4624a2ee72cf8526991a6c4ddfa5973fad5b7a797fc95ba95a7a4b592b052ff2cd465445c3899003476e6449a40564dda34d756959960ccd7c7fb6932133548d76c6fb9878790cda9fb49230d03868b48b798eeb17fe1ef58f8dffa84aa1e0fcefe8fbebed4316781bc06493d812f6096d6c7f02c3621f62ecf87d7ffa4eb03a976aca2249bce0387226a04d722dd137dd8c4408c0e3d81c6898e44573d5b4a86dd43b1a59aa4d3e285147e88802fe7105d89fc9a0a486b1507b58d722cd98f23635a78cc14b4d035bd4e2414e9d785e6dc751b9b43a45fca4c238c122b7b80b08fc24459cd47b9e0d052b939e20d741e1853f02810f987d0a8d047ac76b0df89629f15b1f4d6b7cfe60778c90074579a3b6ee0555a2848176b00be406c8710ce8902148e26b9aeb168a7fc91a11ffe100e2cd9ad0a1ed18a843ea037f008ad7423df3e116843dee2f75d85d3ea29f935cf0bcac71bd1cb52c383c04b541ec4c1c96721cc4e4ee079e14a001e95c59ebb3d9c195354bedb3d46215e87afb367d5c6b8330aac29bb5281bfb59d3583a04cc205709795ecea1f80dafe66ed94e2c49c236efc12fa483f61c6c9c0be3fff2197c6c6e6b4f303c16802b53dfe3821e8e68e9dbe4b06274cf7a53a4abc0884e027196fb6e8a2a6c0b18034c3d314269967a028753388e2c3986c67e88c9a4841932b2d48885e20fe25f6c69fc42e5df7f73a67196842544efe7d6d97d228e8468fdbc37f36b17cc08a58ffe3497db05ac24b7f889228af085ca6092d244dcfca71d2b1bfb546e0a66b74188a974cd4d40f5068e3009c8297196ed83366e6b08ed336c6def9578b9b39aa6d91d73228af59df3f20697a9d9225451fba68de11df49a92ca219269efd29cabc9b5eb8a82f42fec14b9719dcaf4cbb07b0d156c15eecf8c81f158c0cbc31afb9b16d392ffff47b8dda06e598ea30add74b8b3d2fc78ab829534241307115cf663e11cb1950708ef9544177ff8d4ef12f3eadf81e3add26a15439499cc507e2bdc3ad99e97b84b36c16e6715c0ecef1b22c11a92936d393083857767826d6ca26b4b1fd8826b6c9a06f646df5047160939e6cfdcbe661e06992fb2344de9d2cc2d145e10208de99ea15bb115bffb0db62f058863cb281c5401ef1661b135eb828cbbed9a9c300e7fed62a5cafd7570a43e2018f9e9360e97c207c4eced3168c6063f33f8082c56fb939b3934ad5956eca0178d5c99fa429a0300b2fd0384eca2a001fb85b2395382960485f34d838f6a30a60b41444b18adefb903565a45674f811cf82eeee3ecb39e8f199b0b2c2c3bec1f570bc61d74298091aed58a8a7965f6435f31fb322d5808adedd50813481d84900364fedd7036dba89f1748cb846c888346e9790cffd8b61a915f6fd0a5e2b21f0e393f8d1ce125d29769943da9331b6c509d32a4a6f78667a83fe3da963e16c904209772acfb3152749f90438bd38a19b43c1103ce7631d41546d4f422df0c22e8ed6fc55df695c2a2065d9f4f84a9004d94e20df304f448208d0319402b0b75f56179a2004a5636055859edb22b7d463ea82fef683fc4a07233b451d5ac85bfcad80987a82f752a9542ad648317ec01dfb7029cd5049417b86212503f27e8f7adb82477f3e4afeea294e84526550743e8597aa17578e215bd59a7885d76ac1897438e4185681f999a8c891bad805a46590f509f90ca4e9410e0e21c2a3f6ff2e960f42d0470dcd502059183e13128905615b14156addaaee57d4472818a68bd8b11616565ea1228cd9a39d12687fbc3c0d5e655368b00f27b91231b069ce1c0442d8b711de355254ec22e19091201d6b899d220e890fb75e402fe8d49e3fb09c5a5ec648c80378bb893ed198d16a201fec3b2b5473941657521ef65f3ab8bfb44d99c0ec15a6eb9f6a92fb3020afb8b8b2b5da32afb8a68fc0b87109e8f5399d96dce8642e2d2bf2084f8e9c890430a79dc628a8fbe340a31f44ece59a4b47063fd3603eabc6b1efdb5000c6e2d442c0f3477823fe2a11d7da89eb510757dbf4d012d24bae88d5b3b4f1e09f070c13ffed90c35d61641d403a2b2d4b90cdcf4a3fe76758ecca3377fac8d9982909c5b3f1da031c0d39632bcdc6f912c035b963cd1fb417ffdd6303448a4ee8ad2e750b87a75110eb9044f710149fe0a945e2395b9594850e8be6b84e390044a2ab2205804c46c060aa6c6af01b514df56e12895f64345b30514c92104c80b723f9d4fd7e924abb6ede401ce4340e0f2ba276f94bb24af13d6416431124c0f6fd282503609f804d2a5f268c33483d5d39690158496837d55f1a9f35b056dd75919dadc03e65486f1d7ebe3443e9a2882d63f8752485b02ab4f8b6c31b9368a403400e8d903ab2b0702c37225b6fb7d229089100cd0ba89017524c7359733b0e5fbb7892e2a5e53b5e43bae145db23cf7dd7979a3a1b8bfcb7834e674bed50e2b45561497c4b1043f230299e52f032a1c698447ca2b025074fa99fa55c256324d456810f82c88c6e71ac58b6de14107c51f6c4c91aa0dd7a4af15b753f258ab634718b1d620942ac3af7a8ad4bcba46b99b5eacd0a8ce0ff873c239dc12b17c38c889bf3e6c8096e9ec6e57095464f6aa0cac8bfa55c6112f84b70d42411bbc8457dd9ff7c129b66efe44854fe99e012017807201fc5f9eddd3d51b2546410b3a00c93450566d1b64445b10646b1acbe4080d24a69906e823d210c6e383cad9988cb16908b7bdc455e071a74e10aa45fe935e4a7c8385061fce51c638f43bc1f4fbfb0a1142ad29ed25174e8a1a459c7cceaba5dd2ef5633b5bb903809a8411ccd08368ab7968057c23d2feed64c7deddb410dabb103eaea116a7a21b02da2b8f93a323d4bc2204a04a50d46f1b16d583cfad11948a51d0013b88af21d232cbcf91af6af62d76b4531f378621bce1c4b1e12eeacfe4c258ed509216b56849985815ae590652da61b6241592db43ca56191e468e84c4adc2d1c0825d3e3543f7e132ce4b71590ff14010d785304f1173084457f5e0edf7424d715eebe9b7b688caa1591d196ed3c8ba511142d44363a3e3b1ae663374c59964ab6c335763116f6ad0b2304eeef01c1ef76817e323491bb3f240b92f39d9a3b94099f11029a8b067d6b31a350b4c6a24880bac21c711013099b39cb74c9dbe330dc2d1854cce2d57436cee3fb1b595ef4d94402a5075ca09822129f753bd2023fe2cd7e9f22e22bc93328bc36fcd1962d7c4817c37fd8ed34869ad281c4e198c4a28ffef41a4be41d7172729fa81f229c709abb0415af2a81ae71048e934fc58b12e10c7fc97d1f48f46486f1ff473826272368325405e4211864cc570ceef80f3cc865a690e6ccc16ae6fd1598222b6f5f99bdacfbaa1a8b900078ebbd74bf47910dcccac56a934a9c6a70bc90ae606709f4dd027571fa7a9b79007aeaf8fcce97c9b7cb91bf898ef7ab054efd25f88792652ecd2e668fa61127ece4861a3e4fc8ba36c95056a1fa334e8f56da65bd959825b9daeede984cf7559263b8d0f5470c211f09644ea10e38b6c332f950cf0e2bcdcf9fd43c8624adbb9a8e330af58a668c8e3644d8a6db3e633394d411a1390ffa48cab53027ffe178f20d1a11f4652cae2ae72ce7d9f9ae26d209cab88a360e33ed691e80949b28da20663b79f5fdf8c505b37b1c539ae3983da62d8fcad5c84cf0c23c2c384cf507b26f937103ae17b641af08b458ddb65832deb037b421232a2a4510ca1c08e6c280ee78386019d51928c1d325ceef327b3001d5a0f9fa9518c404a9454de3aad08dd39507b09ec85c630ecc180a371e0ffc6607fdb80b15e33674e18c6f4603445e3ee7dd9209f90ee630b4a31681fb8f448b47092a5d3893936557e68034b65c82a3d6b4b7f5aacad3e556e6c21b2c0e3280ce1e18a22dd6012a96bbe7e6ea27bcf5bd60f11031ee4353729466825e5202a18144db71e5e12cdbde5728fd86a861a5627ab73bb70ac11b90cb5e0f713ef7e65529d7318f91900fe0d1ca23a9ea47616f3262d14aa9856cf92c9ea06ca98fffdc6ec90716e975c6d38b9eb07070cfbdf98698b4aa97862665c2e9eeffda40c85b1c0936a68e85d77be707c0c21ac51cd3a3d2dc1ba88a908423014a73d41929f25cd0a441ca8aa00ec404eb3eb95621135cb1b45989de1bc16a87517a49fb7077e80c2ac2823baca99994f6701ac086a65445164c840e0d17d17aa47b69141010152e6b1485931160619ce146e11d76cc731a37778e03d8d7c3772b4ab964348dcc3832f895e79560de06dce2c5a6e7ba81c463952fffcdb491a76f7ced2330779f4273a9390aa34d0b73b336e3a752b518c834be26d3f94d13d0e585a81ef094034e1e08fdb739fdc94fe322abacc0daf2cc10cbdcc5999d3ab0fbe8ef149ba719ea876cf8e05ed5e9f682dc1247854643a711f7fa6866b94a49dd38b49804a79f4043e251b06e39193c92eba5b6306962844bfcf63f68dc4aeea0b3e754d11f7fec77757c3fab3ee495b6eee12864e05a1465e86275cea94d00de3fd725066d06568714516d7613b07321495af3bb793ddf9ca0d589d4c1a9c874b8c22da3516672ca7b1f089658df37855bada1ad1ed74a3628dab16af5899b371a8146a10abe079f7e18062fa142196100abbc69ccf51b633e576e78cba8144ed23823def94b9975549c74288afb74ac68cff05d5f84c047bb71b970d9e87b868504d34a223dc03509fa64153cacaa1073269730fc534cff8f8ee0362134b3955c82169624fa7c94f16b1d89996b4c4b1fb14300ddba720a57cc19718ff89a388dbf3457283dc5edacdf30adcc9aafb73b48ba597a5de3ba27c815678dd8db4baccd4ea480a70e651d864c03466f0bac1d65275406c44848d0d855904cd4b808b873565c28571c3b7bfccdd13a87919f75722f2384b8dae6f52366286a52ff33831e5ee282d4d72a1230e8475a61eb4035609e78de4dd58c8d337b3a840a0de95bdd7c58d5a45c43fd018512a702bb991ca0a24641af3c88951eae006871ed721c9ef57d77b5ca360a379e5419bae1354a14755dc9d665aed99b11e74ecd5aa793cce7934d9312c36581e86634289925bf5e245f9a68b04990b2b0738be99d79ae51cc08d0d48251990b82405fede79e7672b918145570514e8a3b928404496791e81c13566c6c49e9eb7a131d04dd1018ece041efa09205eac0f8f5e0394524d9784d033a82cc1da4f16162274571935ac18ecd70316290a418f311d6e022ca39fb720b943751faadff411e6875ba93d2c903a880573a10289c4d9b2538ab37485e4f0a4ff059624fad0aaf8acfde43c43c150ea27faa8d8d54729b46a61f387d10d62cfbfe0877e7f61d59b3f5c9a6fadd8e08e197ba9fdbb2c4709aa1266af152aedaa9c7e093f547b57f613ccb417118b1647fd67300910ba3d33cdc82b80e7aca6497dd7ac91be9bad6de4a23ddb7279832561998439a6aea6ed5836be440e0dd05e8b94adae50929c895ec224e2bec20692077d654ef3f4d0d4c0c09645ba12328348ee486321869cb2b887ea2135a710662b3e7679a7cebf9116f963603bb14c06c558c889479e0259f2c71ef8def1bd17cb869881b1f490d67da45cff2bb4402b0d1b378c21641a85888c5642a36558efe145e92e01fc9946cfc37f61fbcccc297b6c43694f5a71013bf0d1f83278fea0ccdad2722ac6955beb0fe0bfc80b227c08b0b571ed8cc7acbb83e15870d531a7be33437f63049b7a8001326959b046b7b89520d710b62dea0bdb27bc667b974d2e6c0387a6aff8ef06727ac1a56124f5c0b3f44e6ff789dfe5a39f8712964379a41e1f72a723112d6dc69faf32860d3fc8b5a051fbf98509a2f2c417dbc8b03e9c17502a37a2bd4bd04a6ff9e8ce85fb48271e381d0059dfb4a15e25bb2e594c8ba562769a2acdf98b5f28a0f2cf947dacd7e1d2c2c93a6f431abf6ad4fd5f4f29aeb0a857eb23b9fe0ece64199fee34766a902c329a5c38d8c2bb1645f73bc93d864aa044823d59a7baca97d6d81dbe394b03962ee19f28f70bd2ca451d1f35aaa06d9ccd52f37fe1f6802d639cd3564bb4bb362cd517a95d644ff2a067c49ae3dd35030dcada3edf98c169f8f0cd4a45e2c030ce9dd4a41326381f0fb3264993efc3ca19f5fbf924b8715b86ed5183c3dfec841ee34c381bc39e6be587c74015fa1f91ad0373285c774766c5cc92f921263b3678a8c00c865153b3b60705a4bcb49ed53786e0ac0a6e7a377a3750b7e3dfb75fbf85d45196296db26948b5921617596e58edb6737173f75933f1a2a11177b7e0c7a27716c96c7ea6d38ad5829844b45897defafca5a6cc59c513fa9752d284754111b06cba4cfdeff60d5495b5dae0ac79618db8e5e8754c4844145b07cb5be4d536124f76799b19589909e722196e1966e1dc11d74ff3e60894d0ea5bd25989e7edaa555004ea93aac79b47cbef3ae0a868f29b6af2cc801ca9b91147dc2ee4eebffbdca48dbb552c109074d3517493d7cb43c3b48033f0d899515959977cec58b3be70c28c3138672fda06a25609b106f5c1aebc0dd404976f51a0b5ff690e341855aa39ce9c3c9fcba748636a8a6edc8fc6a6f546a26449ba02f573b72bd0343236c64971a867e30b502b6ebaa1770c6cc515436503a4d87b4c81fd098925ed8bfbd3f29160066c247c7e0d88d4aea5fbe7e74f7fed5f7225980b9344bd3b834bda8edbb7d99c93ab8cadd7283c79954da69cb44c0345b10ba9770dd8cad740593233a40b1a869c3de4dc454a5aa2919696e9cf94ee35a08e5eccc79c1496f3f11d16873cd822c2b3de198427e9555008fa8d1ed36dd640f3fadd963f3c190c4fb7581e31734533ee88143d8069faf930fe5470e3b60cd1488a56968c3da12ce18999123bf7831eea4af817d60a91b83b109b6a6529c0d6a80e66b7c5ccea55e78d89d9196fec77c7d3fd73e6c3856e8a24cb69c74342f169f51816d571e0945b36b9eae4663a74b175e73e0c14ce336995c52cdd406286d7d00fab8c191a29183090d54d21f9635b41b11a759d67eacf442a00f9a4faf943cdc33493a588849962abca4feaedf9d1a5083a94b854392a7c5df53a767b854583a21058d0157186750490feffd49b1606e6d4cc5c13349d0ddcd0c5ae9cf2f46d54a3ce41f6ee33e2cb0e774e86d37933ab1258430d46398993fbfe94e537d0098d4d4728960958aa3386a112b632f4830279b82b60264b9bbaadd6bfe5dc3a2b0eea4d914c124ecee8d5d5b9664c6ec9ddee077db9bb5a59d1760ec900a6de3f4889136426e3affcb838dd503fac072a026a5362ea5b7d8177aeec41bc5b9e226e6e3e10c4790c5deb80458be8468ec5734ce1da09cc72dc830d950954ddbf2d17d44b2fcca514c3a781e73cfe401c823f985d2a007a5c267389b2d070747107b20cf8f4f0632f212eb4f16336d507f62c201237f1b7962e3090f818b356dfdd01717e528badfe642b67a907edd47e80c888a63cc14624d0851298b03b199148900d00ec583e34dad105fb4094f3aab7f17f5c4c7b2fa401e6be153499e2b39bc8d8ce0feeab413b4bafcf39df0197b7b1c1447e320ba5c07d7de66297febb52dd63168cf9d853379c8e9ed8fe09def45f59c6f45bbb748b043c15eff14c4d6f48ae0b95a3e6ea8654ce7e2d13efccb741f6a28fd7fb36a9a61d8cffe6fd2a6acccec1c886e62625d4b75e7acc64238225eeeb8c3ce1271450782a59da15b9dc7ef244273a5a25e08077ff8fcbbafdfcab3697900148857de84997a9b846827d3d05b700c6bc7c7724055f4695a037dcfeb8e0e1b25afa920022357df1d399e2fa7ee605d28feadd3ecc66676eb56751ffab642c5eeaa40eef00ea760c7ead962be9f152afa6964785ed5e53a3945505297cdd7266d73f23cb18cc1dcc7cecedee1a1331d7b2c9dfeaab2cabed47fa4569f92001ea89d289d3f71b25aab9b793e4feed6251a72bd623495899e567f6ba10936d1b58a09cc7b8ea8f2801a620a4fa4b626d86b2d7e3c74bb07dca418aa7ffbd68b6e6582760c00112441bec497757e4b13413cb3869b0492a2673ba1deb29db4c2d59463ed32f65120006ddfbf02dbe545e5867abc8a60a11699991a0745ce815175ae124e90f326656991106f2f4718284066e2497778a3263a411e0e3df6c927fdfcd4b70d1fd6f2dfd312f87660fd52ae5bd0a03dcdfba281dc86db44dfef46949d55f7fe278f1cbfc59666eafe6431e4197d238a1e6f93fb3a7bbb04a52f99d062dffc4140c479960409cc9f6755b1ce9d443882719d84f12a60c1bbe3c57249b1655a2f504d0df618c6b45404e8cf17a80a1e7341727d8ac134719ea76c4261e03870bb49e1e7b22a4a91921982861839726628a9c2419cf9478f05a5766eb9d0a29bbf97242941787f69e5e91baf89c63a8e8cb071ab8104b8c4ed29b0f19c3fb07439819d1a515244240cfb07e269a1d1b84328b791727c68682a18ae7f5c44df990fabbbac151dc7d82db4ac545425913a5e911106356306d03358bc71e5553d252d8b0b0e17eba7ac90111ce11f28e532bdf203db2c5333594a47471ad669dd73da526931cd3ed118b2e1f2019060dcb05b1b444c0757f7eace65e266d56d06367f602fa2dc5c6b072c52ea24fb3101b9ff259cb6889f7e12aa3144911d14417e086724ea7a46f7acebb6a40f862bd7645ae0a15b34342c78fe93f8f3441c9570764ea1faaca90984d972f034737888b98cdf56ecfd0be5a4b0b5b3c45a160248865d85980a1ccdd8fa230a41121db1f8d25416b13dbbc23668898e5a12a66550494e431daa173362962366d276b397298bd366799852e9c91d9f89b4216d3caeef0783a3bb9f5d05435757e4cf6e0308b90ac3bf4f230bcf9f71b1326bc68383733c3921d4e49f5460e2c477699b9afa4c90d40110e4c872fa78dea557a55860b53c0114c243333a88b6f186e461253727a7cb37f5a7066009b99983d071f757b03d3b12625571f8a0339fb5af030ca3a5c9a31931ee393f2590ae66741e93a952e703a0656997563c539ab0a89336ddad951f7ef2a2ec29b978218ae19d7673dc37042fb4cf1cc573b91f99daa6ef141daf9245c4c8ee8871736a87044d75d61a6097740cb8c98c317323b73529756d427f797f2f4e9897a43f35b7c442ad0c31cda368939b6de27e7b4557c6a3132cb30d66ee345356549800c0dacb305828e4f8edfaeff32a960760fe8d03bcec2d14bf511229284954b71a3202c9db8628a4afb819ef6c977a399e82c6cd32a9e7cf1c8017f48527c24162582b79bac004837e668eb9884853ec819c4c0c04cf26675de0df3f334ac794ee1524e664b72236eecf290832c5138acc953da0903a8aabf9f24ad01eef9e33da5e2b080e2e5f6325baed5e050e6854beee1201a1572a90c3834055c8e805755ddbe98770b91791f6e5b5ae26f2ab74fe8c4b1a269123216a7b4f3625e794f523884e04162e91709e674809ed95225288f8e18c27521c5093d3be4f06a3deae70a1061dd02f0f14cb7ea816473158a282bfe895e7e65eed8c12542fbff3fa890ac8be3eeac4b416d84e0460ee144695270afc16675d61eed887203b300a250353a7994a885b2209e62502739c67e5931523a2fbeb506f9f95cdc0dbf691a23d7d63703bd93e47244c7c8cad2ef4684a19660036b27ad2e6d66edd21a77db1e10dea5714f19dbc1cf8438e90b8aa1788923daafe5f55d3916cede3bd1185d792b02aac28fcf78909544728e99a37c544667c51b7c191e5b6aa6d128e5527154bcc97221baa8071089b4d1d5ea4a8cce75c668b8cb3b5fb1b62622d6e4a38e0caa211447ee07d9852c6a165fd1523289fd734968e7dc49789c8a5e84538555ea38a6a3abb41b0d9433318c387d386c3be0d04ddc66bdb1499c722e8b01187a40ec0a615e4fd692f985a590c7fda0537f905fae6a33bae253f41f2ce11e7c02fe7df85c025bfc742ed0a48b9e9e4b2119c8131828ee419bb31a67cecf3e9f3f2a91160a16caa6fea02740bea39e286db039b12ef17780f7509b5268db4fc8b5bea3c3c0777eda3ad3bacd153dcfaf59f7fa120a4e0ff6afc7fa94f84630113d05b3d5d30d148cba3fa2b92a70820879611cbde02ae9fd5f326d30b007796774a67b04f15cfb9508148bc02ae404155eb4b15561f0e85bf8cf5ff262fa90f8c8c2b083d516f341c422fb9325f0df6cf0b31946473fd91acf252e7088dc2563ea08d5fca42e6718b89c59fbf6000c3dc9f82ff824a2acf3b56a72ffb6b345bec4af58c54b1603da4450bcd0b4f747d942bf9b5ac3024517c53658f72282cd93dc7dc79fa036610ecd5a2bc2cc2d141ba356006f72b7e2ee8f71b08131511ee85edaa201f3357a9db380a11a62522303866af0ce9043d8b00f933ade00f3e8e9a61176a3906551199bcd40bd3379c7f606e2691838f909a6041d6ece9bfe575a0f14433dce468482d25b993e800aed9e0ac14cc80c6c0b9a64d3e0c9cd2427356980d32bc9471053a4f4a8483e53166e82ceca018243df62be19d0a81e9cbc132615b9eb75dc3478c7264f4ade5004a356a11c62603978a2fca16a1b5622333ed1a716c26b5239da7885f645ddcf1f365c0094cef297564893004729437233a7f781f3ca31053f369cf8460af6538f07b89bf5a52d1c8d2532e330525b8f0fceff7fe6ec85b5fce72757ec9202d6db36af3528f72a45361eda4d2e15502a6c3708c5713e95e6cba7794d2857d035c01967808932839c67213f514914762a592dd247f1c66699b9dbfa1d89cb8707e0bbd9c430799f3c1ecda4738de9831885dd9e6b95ae68787c7f84528a1b6dd32914c1bc1da210bf246758f9e392a7504752a90ddeac1310279ddfc8426e44eb6afb1e574e684d4aae02dd45dfc77de894e043e7720ae910046c7e3f7d91717b44fb3396440d2f88682b3d05682b4aece5e10951127da7c1382fdbba2fd8a2914b5f58a80fcc1ad882c4d608862847502eadb0de3ef2233d6111c91ccd4cb8bb22dd8fd491ff6d33c7cd7892f771cd832507c076572ee3921da4bcbf30a022a13a861d7171d0eb42fab8d5fcd6595d4f8682d43c566ad8ec439c3b1a5e7a048848d3b3f75c411d2564326bb42b90ae981f6de448410ac74a2334ad6708ef1bae0bf545ba8200952c90b7c80159a0c05fb4f2d6934fe89e959d8d828f61d83539b2d22d381022e937cb1dea19e39e36ab8384dd81eba988f939dc44e7a8ef15313902573c63c8caf14103b88b97549af438728863f41ad01cc54460f3ff97232805b4c7fe67adc74969dcae26ea9e729dcef11a16ec04dc27a0f4a4a069fe2d3de12d2efa4a393fad6ec63f2c4b4a6988a4f650231c1ea87493ad7ee5a490286b800f2a2d414f35b3362fbdb2f461db8492a207beb1ba90ce494590f48710dfe5c0950bc606b6d60867702bbe2c0fef75655cb4b6dda5a3ca9d73544d2b0644d0fb7af24c0b9a12a8cbe36fbf8e3cac99431333770233d29ac169258aa76efa3c980eebb0652c60db126471af888c4a3068c3bad874c95833de74601e7be830c004ad09a2664d9cfa98fa5ae74736530f2707e1f2b217cf1a6434c2b471095d1c753a4800390857e165a4dd16b2d9790d0b82d7d8d8534595229ceb435955a41ac38b3ef77397ed7a09b0cdb38ced310d448583257720d1965009d6a775ab92e25b6cffe5671a249e358b81c4cf46a7c8e472fe368eb2c06dd0d8a7badab3788980f5adcdf6a3710d7791f4a79a6f9755cf0f0c47c898f381e8471371e6030c341467122c69f7f568f2219ab33534949ad1d14f16affd985dd9f5421f3bf4195116be594f2f0db08b929e0e9db7b472443e5e48d59c3f31e130d17f3ef02b48c4d95356eaad838c6bc858ba0412a974fd4df71b578a6af360f26cac7020d1992a08d5f1f78b3f9e2b51ff22575b555d1507d5a6d34fc1157c7d3316ba2477ab707631c5e188b6b5824b561405e21c4bf51cc6383bf1f3f361dd628c035e6130da504e1961f311b7fcde18a81673e3d98dd654023dd127b25db7dcf3e8159559ae427e8b8e4bdf4cdcba0d976b367ccfb721bd2aac85a196d966e6b5b2d535bcf7b7450ade6d5dd6e8b048a0eb45c469c4f7de46289d8aacc31c99dbcb699d079301143ee59945b501eae8e2244ddaabbccacf57557ca80258da77f3e6bc1ec1e4d035661f36b2afd510c1b3d681df5a490c7a5259c8181b0d3e5725f43d234f0913b39b55da897f5a92e30c7d0f4a3127e68fdb0460deb0b7d06f705626ee4fc89659fd89ecc50d1674458a39ca81e38e3ae658c78e71e4b8030e39921d3e82b3ca9c2ee85f64b4962303975fc44223449b2b0dc67a3e170b9ca56ca45498f45ec1290f1601819b5edf62aaaf2d26a4ab97e8460901d6d6b432e9b3175febf9059b967bd2408cfbe6b9f4522beed109b6847f4705519777cdcbe801383d1126b70e75dd74d8440e71ae12be2db718120d11d555d0c9abbad2e2f3e8243b4f266c091698a4f4dc14bc656c895b82512b7f01af1fc799e2c6f17cbe855b14a00d154172f6bae19d0e3817d5a138e5ff663dbfc40590e7a1ea2165addb434a241da9ad961904ac881905f4b73ff745a4aa2b61c3a5bf8f1ff0439e227698b1d6c4002782a3fee77a815460bc7e6d5a9a96b925582182a7e41292ab0303af66e26009f12633aa72b90993ef0e2d07f64c8b198d48395518f4c34186c03c044a2314f5f84d4a235110bc53b281ad948e9859ffdf4a934aa7f0d6e23bda342a472badd20e3b0dca283ff33d0f2f3f970ba414250fc1b642c72e60828e0ab654b558aff761e2d084c7e42f0258b184f51ba957d6e98775e8bfb8f8b0c401d22da712a53fd5e38fd1807496cad6f0b2572809d840c7e2cd00e78cc798d71fb7f2a487729940ff8e918705784452454520d11c79c312de29d146e03812620279694540c2d81ca2b025adea7d8dd41089a2b84e2b8cb8e70f8721e167e4292069d7f2a60d0ee1fad91ae458841490561c95700fe8ff68950204507fb5162ddc966dc68ef37732216bdd01539338528eec511ddb62d9e0c2efc34f948cd41d459446a0aaea70150006e0eb96948f28229c8bab6fd5ff400c65f41cf193c55f9f9bb4ce4003050fd3a0407ef64bfe4849a4e8cc4e9a1c091086d5960df734d1fe9a12f9a7efdfaf918b9f2a2aacab6080f9c91ce8d0297a589cef64d6c4d486028196ed022001b6371631b3febf2898c04dc116280d405d40dc9551fd7f2f88009c035631bc7e0801f851b821db0ba2270e44316188a37e301be0b6730e8295246f89719432c4a80cad0eefb6d1929047218a1059e96de56d17dbe297f440dff43c8f05b88bd277f0448bb3ec724bfca3755c3521dadc0c3a76a8f3c75cb1895d7a7bb0f242fc1a1a202d119e94231e9f1e13e5b33757a241ac7f5ae9e34159c24d1f6c6c71eeaa396fa3fcbd257f4c1f90ba0b91d964b5f04673037e32ceeca694c1a8295bd7557b94f7496798a2b6e82a8d1c49a2593eba043619a74423ea34e08fed0cbc1f69ac5be79e0203cde9981cdbfec4e200e89e6a799d6645aa3692e2b9652d3fae0a3e81d2c4a7d17fd5ac9a172a57582751b080fe31a75b91af4b1d2abecf5a19e768472d73240a331ede7d6f15d2d7311f607a0016eaf5f92cd23371e22a552ef33f5bef05de767938067b1e2ebceca711943c1ec8a52154a199cac7c654382af4e80b1cbd2cc7146dfadfd973a502e7fbcb8400f9e7d4f3d1c9fbda7419860f7e9200313d52fe1b5ff7d53c40f46284ec23a09db8121200a612ad18967bbfc3d73fe0f5f64c54643f970df682c335a54ae10784d3035a6fa27d5c30145a4d07eabf72d4a3f6e798c0cb5e8ee442992e861768ca2eee8d950c28abb7e5e89833d77d5a94191d5a1d2079b3fefe1cdeb71f780cc487d7ba8cfb453ec78c5128b15129ce8b612fe14af47adf7e0be94d9b6cb9b79429c914a5070f08a7072eb8dc1a32df8463837cbf336a5176442dca7a3b28403a22ede7ca154dfa807e40031acc66b0ef3f23fd9dec765a8ca145e9210dbe91ae0ac23a7b0da82db66845047714613e6b1d053e5c51edcc9f218f5710257aa04323fee7606144594d96d6431a34dbd190835eedb5378d3ceef6d91314993e506d931aa53e297d8d52b9a3925faf0c79a4ac4044444040ec31501d868a10410945942821fa21d3b5ef725c1af86f98ea0bb77e35c1d8af0e0321f367a8ea81be7cf9c8d77cf1d67a3f6789b6431af185588146b7da12db4ad528576a810375ea56e298a02f9979bcaaf637f0784d1e2f9503b4df9e47fb4de399aeead07edddefd446ae6f31b9c5fc11a5cda68517e8fa9f32f079b82b10c1572242061b247e7789f04225208913fca1f3acef2eea9a3ce953a2c744d843582eddef4a384d9973694d23b07a5d142275950f1603d943fa1ecd1a2144f602b4b1e4a2e0dfe2343699003c10a963a2857cea8555653899bafcdd0d3a20d0984036ba566a5ee800879bc512a95baf4dea8243653b27c1b9e6185143dd1c434838978eedc83dd73dc3783dc570dc4b1c2f237c7ce1d779d77210da5ef3801b6f93b0673f48581a9f840a50634f896c2cdabc1547ca0e2c39ddf75a6efbef4f561287df7a81f4696c1b44a2fffdbc57935baa7dfd13047794b5daac1bbee1b04553598060d54c1d01caa6a30b7a83d18aaeaf3e54055fd796b4803be112b59b622324b597cf818c2c794294c5f7b19dc7976f06b09a0bf343cc747be3c54f530c506fa9cc53f33c4ca8f2bc35187e68d483065ca122500aa2155fe434325105262c4f27b3c47c5a1ca01f4fd79de439f231cf2b8872af6f1a7a18ac31e361eaf5aaffd7ca688431b51899d6d058868f643ec4f986d0bad9599640c345f5ca12563c81f4b97ab5c415e0e4791e60adb922bb4206380628b3edf31389a98afd63d6de188c0feadc2c31dd9c8b9e7155be534da3e7927061a75a3b75f15ecd7c6cc3c04a41f3cdfdf24432e073bddf403ab5249d159fc6c6589db21f3ebe460471da49ecd78dc91c7eda7c1124390566ac1734706f15072dbb9411a6edc6028ef3468ef3f27cbefb417ba6bb841196ff05072b577a971f272203f572ba7f1c673a6e016418723d006fbd70a234d202bdf86b595c888a7dc1868b49c259de52ce9605f8e9fc8880004d3c07e5150c321e8c5de8c1d1e98fd6b65cbcc3403924e8401dcfec4d7284f8b5d7fab3ba25cc146c6e86f118765a890427d097ab79361e9ce392703660d55f4fbebfd61e9108f0f0f1e1f1e2ce350064747474fa5147bdad552ba74dfb12161a67db1458322c28eada9f3618fa4279802c611d3a75dfdeaa7e1d5dacf65f3fd3ddce13fc3ca346cf9d67ef66d8bb7ad4e0bceea249630374b2a94054b8b2c65b73ba55a0ba9a6ca71a552673261612727d4a8a37d6a93bd253d718bd3d4dded799ee749cfe3928d77e2d3e9748aa1459a7b02b1b0a3086261c739bdadb5d64a6bd974ea1086bd16cdb744f13ca94204f00d7f77a79853a954ca84d2c1f9558a65aaccf881db9d4c286f86e4e64e49eed3f8813b1b1c57f765f4b9dd918bcbcbcbcb0b0c4c4c8c8c8c378312bd30814bec0b282dce9868c02bacaabbdbb9ba6974b59aab95f74aaeba5f4adc4b7d7979e917f9f2d2fd6456c527c41156181d51c5cfed50b266f9609b784283211d127cc8d96ed2624c615d0b904d6301e1332331c201274ee4079901d3a2b42a80e239888569f9a3a9881afc80b1fd7f5998e28e361dc4f22fa148112040ba9fcee128af3797c43695a7b452cdb9b06ee1f851cdfdf9e953f6196487262614597e13a2c8c76997f738ef0dd31277fc9f20a6b8a6cdb0bb755a13d4c94171bacb7203174225887b046064f9c7561146d0030f741072049059111749749c5872c7dfe108894ca725be35d2455afdddb64a202731605e7d30d65485b4dae483fe16e0a0d6ef6b39ebeeeeee39a1a89cd8b1f5467ee8b11fa745d9dd7af9958891181021621bcb07905cfa0db28cb0fd2d7883d4e8cee94b1871c70fd244a0e28a377f5fad63ebb6747058e0f07bbb5c944410b7e1ffe572911339dcf19b5ad8f3c14a1b3a9e73ba7426cf172d6aba951d826e4a44e7bb06755a7cc3afd3aefebe5a67a8d3d26917db3ed241255b8243ec73119470a77c2d320de3d0a472ff5475a872007df9ef98efe10e9f233284f4c52f2682ac9a1341765691ec815c167484d85167c6c50c160b8429b200e12886831aba7e7eb9aac35791fd1594af5bca40dace91b02682c916214eb1d8abaa1baa28315160479024899124a80ef6f355616129a0a2648a8fdb39d1c847a614cc56abbddb42b29e6cb10cef4a5186624b476cbdf8bdfd68883457fee5fa8b4fd3d2a17101cfe11711114cc469b1ff47152344ab14d8f15b92a4595114451851ec5f28b5bf934623cf68115b25135ba56d1982df2214d6ffbfb636aa1324610453c48e16090845bba75d3d9207ecf9d90019e6e89e16fb775a6c1b3b261eec583f96e1ddf6b934c8cfd1dc5f3fcd90a577fcc0edbbeab9e02f9fbeccd17fd9a3e18a0bfb37b6831c8e768003100f4c14e121882b3e9ae8626803f2f6334d277e2ce0549bf934f25b94a3d832a20118f8cafd82b02f59f605613f4b1227ec81c0245768188d107bd4a3e3a195367cca3b1ba459c9daa2bbfb7f34262061477b5b7dfdc28c34d7674f8f6fda67539adae77b43edf4474a3994d6ad73013c69edbf242b7f6b554aa99f6cd8b3db943addde73d1043fa909e512f30285100027ef84429dbc95caab5101f0690230bdbb5b85a2c2ca6432198c6f060514bec60233ccbe1b9f7781181601d47480ab210058d3b5241c3978768c5ed8e8115938f825611ef8bd60b106301d09c7025a908941c6a3310002bc902180cdf4da4141624f90e7d4c059dcfe1a4a1bc2519a20c6f3c49918141414e4927a6199bc19388cec29aca08e01a450b5af0f5371509abf49a1fe58fb750d8ce3fb6030180c75b229c4e0e1c18b849442c2c11be01cef5b1a4917a8e0b61cc16d2d6c90511b054923272d0ae00729a490428c02a8305dcb819423690b8e991120e1d091c308a4180e1d2d760e236cac00a794ed1a859fa9cdf9a14e9e9c93563129163299acd326d5aad0624293c68c5e603a6d52ad0a2d2634694cc8a24e3228541d754e1a139a3426548da4ecf1a4c56167b3d9cc9b952d4ad326d77a674c489e7e06c92e838282824c53fa9cd26342b3d9a4dacc8f16139a34267402149d536d337137853a796005de5d3cce9195c6e8f687736d24c18442b252266560773aa1604c31d020ea7876eacb942acf566d83051dd3024c5e0bf6bf6b5127af9399cf5d2ac32f6a8f8d27e2b4beaac33d9c937a9e716a264187ebf57b4c61171e71141e360059779e6050dabdcb5b9dd3b30b980251ad9d93d7cdcc7833e88333683d9ec7a8671d1478023d8d074c60080a32a978a584db473d4c01cef15c36eca27f74d43f92ade02338e1cdbac76c369bdd96d12a9537c3f42a6f46f77c029b4cbc7a1ad186391b6d039cf1956b85ed99c146cf0c2db60d169c4e38a14e0cd4f5ca08520894524a29a594524ae74f26246fa1441276f0018840092c27f87358a6e339b2072888420a293dd8010e5a127d9c76c94801185d801f0683258de2287f664752ea5cd9ba96524a29a53e5ed464c72aa7d8b1baa59256b9c1d135c9920e6aad2e46419dcd629632aa8187ca02c81a0da54e4b204219fd00c6db4276325ae2332d01886774054cb70424531413a04a5b7eba2840525c1740a528462a1758b82844a0b06d91d5284cc8685c0c6d328a9694a45cb9007216d7d768e21b388ee3c2508a10006861248588992d6052aa7c49529260d22256f4832018527c3663ab72dbb69538192a96bb5cc4e4478c0dace97211931760616244ca923bfe0b22a46861bfcb454c8c70830862d0848718217bba5cd444080689264b6290b000b85cd4a4072e576c77b9a889145fd4448bcbb66d2f8c0c418218205ada90090332ad877a41a1508cfa176f86bdde285ecff3426b81dcd942eef4d1e2fc6971f6b043494237d0f6d12ed44f47421a3be6b98fd80c621ef3596c168b79cc6731a11667436924313f8ab951cc89621e8b792c16f359cc6744311bed8a0dc5260ca6061dd3747cc3746bed944cd3a39e86a5e738ee51262b7722c176505a9c4e5a9c3624b047c42e171d01643e012e171565b91d6d2b22ea8e6f53021b011eafefa59376a13caf93c63ed2d169b5c2d6d2e27c1b22166807a7d5ae173f79805aad56ab35da5c9904f2241015efebe0c2b81464a32998b8edb9f0726b905ddc023cb17c611b96d7177e3fca98e9e565a32c59ae9402cbeda3767194235cb9f32594cee99f96b9246a1795b176bd789e32321476c2028ecd0bb065c0b6f2e339bda567ce9a3f25261c7b084a23b9d3c94b38768c8553387e38b67f3e5438da5428eba4024cb17c6d38eab8f63d7094d7ab264781630ea7a005733805479e4bc3715eefeda3c0f12fca07f516b8f4471d17d573e98f382eca8235b8f728907b6f343580a606bf5e38ca8b01d32d99a4d70041175d74a1e5764f410c986e178e396e29bc61e2409a16e71709516d6860b9c52d043f708e8da58feefccf6530ed42452793406400c5e49054f27dbeaa52269f744ce3ad0d54f5509f5f9bffcc102b35fa2a2d74a1af46c3733808339da1bb807631b7abc559aebf36a704a1d6e834d3ecc13afdfafe12c4a1a00efdbabdea7b15e035338465dd92b0288ee5029ea3fd940c658ad8ad497383355d198ecfc51d6914a0fd235d99359149252d893c76fb639e435d8807cd6cd78a058bace9611d08fdb1b15c200b76f90e3fe40fa943c3c0863910dfb40311b21ee4e2107b83dd1eb0853ef2a5c970477b1f1e2f2d5455ebac7e04bc60b771b86303adbe1eac54ed684fb5779638c3fe09bb2609e6dd23c94b4abcc0ae14a59c4d91755010aefc0e6aa73abcee48f33c8d8acc0848cbeb34a9cd40455df690464190715110863c879180c160301b828eb8a2048797fc8672d4563a875f3ef5366a97d4947079868ce4d76aedf7fd8b6258c48e9c74e5ebf09c1e2a3d1ced4deaf19cd0daf0481ec9172a57ea13ca24436371d64e8bb2915a0bd85b1c06fa8ffb00fd8703f12027e2428e64821ba8013984851d5b3bedf2c642f3cf25eda0af839865047be2b6ef5059294020c15e9fb37e664811a2b81d76d0ff0b0cb582243976a689182cc96939ab69c4d0623f138d3a34765a3a1bc82ce0b46810b578c6e7188f1053e434bd0f76ac62cb73b69f95c0b902614ccca2efdf95d21da7c57e1af6b0793328b5c131c347cbd7021d923042072222202011d0964effd6ddf45bdb5ae2c04af66654b1fcf43b9955f5cbdb214e184e159237d98a83d9c4e7088ff6f37dfc55ac119124c9d050922494abb4886b4af8b563c7911ddb6b13d8c209d0f07504546586d26247d419753f7347c420f7bd7544ad8ea8336a17c304a95b07e1421ab15d74bc418db090020bbae2e5285df0703b6fb95c0a846ff8795c0e476a0297583ae31bf99d6c7a0c60272eedb46f7094d70053c8ee660024bb21f18d7ce69b4e33712fb804e1b8859a9daf466a754f31791615c010920c45f6220429248bfd107b010f41a40d4047ba27404395a13cf1c412a413f7c5e5a227a028b1eeee2aa72ffa3bb4a7e10e9f235aa87295873d6ccf2f3a41a63345bdfa5c49e72eab57087104c9c70a60f00217bc5c8bebde5d7777f749352f081b83168e624be7ca3c480d414b8888404bd10c765842152552b3caa7147ca1fb9b611fd1b1e7b06d754741626e1ddd7c102589ca7d15a0ad4a095a454749b75e2e3a42da7658a2a6a8382a82722493f1c110b3736653492423c0c8da31f14c7316929bc02318e61c66ead8b058f8089042df9992b6817c4b1447c78675e5b7f8044841748699d765386f86bbc3d07895e9cee499c08ee8fbfad378db7aba7f5f7f933bf9d3e79ceef3e3efebc994eb172bbabbe59c736a74dadbdddd6ea5eb6abbb87a77f74c13679e462744f19e692251cc3349d73177658c6466c6016f8b73ce49d3dddded32d3bd6f879d46690fa9b96bb4dbb4dfdaa5e5b37c57ac740c9d536330a57e6e9c66e16a5a58bc420e19a35f8747c6ec6737ad27471f3d08b4d450c7120fb40f1d9ef25cc97b6cebdb39c108d6a37be40a1ee018fd3fac0f10f04d1fe9589254ea244cfe48203a742c49ea4cb46ed2c84b019dd27d52267432a1d3e53b9d2ee964c284d6283a0a82d92ff4ddde4d1a8e7649ef7906c68263d37a16bbee58bcc202648cfeed2b9d349659dd9fc02e75a5da598be3d697359c9ab800d109bd5b4823073b3c30708c7e2dac61a5d1fa3a656f9d6f1934daf52cd6671a1b97be3cd1767b923378a69d185ae46956c3c0628dca46262c3468681bd52852a93b1dd82a76002a3a1ac1cbea4e1943186adb44f886106b599f73ce404410369041d20a7630f49a5a16779e60be696190607251b8f42b12495cfacdea02891baeebb343ec58a70fbb21899949621a151e7c7e44a92cc13510b149058a1889e4c70b4884348bef582d152d4a2f24393a1a92f10177a5480e64781882c86888257418228b133b2f170d81858c0f40ee96acdb6fb1d8bfade3ada07eaeb92f7d73618ffaa54d5eaec4f597b66799c08a4bd8d15a6f7fab792e789bbf149f05b0a5734321ecc65bfdea5b78c3370b23d640816d36744c0343e7210eec3ff74d58b1431a7c336158d500c1ce1fc07c1544f9f30910438a5294cd2dfd34ebfc17ce317145b14bfb39ef58efa43eb52d2c7d7aa53763a747890b776ac8be85cc3493f6803b05d89f2750d51ee8fde979bc3f7dff29f439d221cf497b55bfcbe97d545aed50e500998f791960de25dce1f2302ea16519ef79bc5243ed927979ed9f66fad013763e9216fd67d8432d3a187ad882b35e5eb5f3f2aa0768ef12f32a19644ca8d242d5045e3ee66548bd4bb8e308cfcbc77c2a5469ffe261bc0c319ffa54b8e3253cb285aa1adbcfdf7e86aa1aa5ef2f7d7f4ca872c08b87f12a2d94e1c5c350c9f0bdeabf573dbf644295f632cfe30513cafc11d3fba8eaabe87c95f631a690e7a5861ae07179fb0fe0978c8c0d7d5acef2770977a4c21d98975035430df0bc7cea1fc02fd1592a07b87ccccbe0f231e18e233c316f7f472af4790965b031e10cfce2178fd7c8b2981f5babedfb5f9e5fa99f797ebd8447545a4843e9fbf98e3ce37e034b1f03a6fe0544bd0ce8f230a07d69c27fcf9e4a7b2ddc81f13d268447fa55ff7dcfe3f5c284ff429fef4d088f6cdff730bf43e6777c210e987ff1263cce1526c060ce8af9d52861321ff31380f955a8da898979f97a7979f97a610d06a8d242d5ce8be7abfa55e803e355e1915283aafe17a0cf119e170f03c65b97df01239440f65fbcea71ae5085ecac17210e1536f533eff2f6550e70f9d44fc0fe4ca8da49bdea01fc4aa964902fd4cb174abe3e50be34502a6911498b489ad0a1a113d8481a16248404c9ab85644e64348432898882a67732319b09e6c553df227dfaf686d8b54d27b06120ca0776d656da35368cc8352750da281cfbe7fa7750bb54d2484e69a3ebd24a8b7e34033bcaa3d9cfdeacdacfa9fdac77761c17802decbc63f560d07e0b6b8b9ee2516a18a3ed3beebe54006e6ddbf6168bcdc18eadbbd168b10cbedbb75adb66a5b0daed96e7fc0d9408020f3308c2132c5e5b873d5826b0e212a5df9e46ac0560d6e6dd1ab6f866fbb175b79209b7bed401393086169cb57d83dc071899c814802d6c5ffe7e7fee03803acedab45008fbdf0cf7da56d24a30a040a1e4d677aff5abf5ade7dc106ffd597ac2d6df6aa86edd7edc4c01d8c25209db68b66fa1552cb03a214e8b3550ac94a92955a6dc000b2141a6dc90b2625faa90c2c2a88285109ec4000981082242c0410f21c44420c48a10806280d0e2b265cb86f9de66762e524af62ad7939b1dea74bd17cab980303ccd416bb4cd8a4d2e5a4ab35a57a2b9a9fad1eea695fbaaf566cc9294eda892a9647489ae28d28fc5d03be5cda05ef7d4d169b54466c9709c342245f56c1bd1f3aa53aa75aa9bc6eb0b33d49aa53965e5eac7d56bdd98d99968a3946a5a9d93526d727577e7f8519073cef8b0f29db92b71b53bc95031670e7aa7006e3fc87933e6871500d79bc60667a57548a78ff3697f93ce979f69be9c55243645b9336c75bf7477d1f31f03706942f04e13cccac4cc1faabb4d5ee99d3de79c34a82928a5d269e5c2fa7d755f5153fbec64c94bee3e39f75ab2a54a29a5eeee5ba9a4f151a7532de5dc1f85aef4ec73d378d35834b97ba51bd50292b953cbd30e5cb7cf3ac30c078c1c741d05b97bbbd62ebd1ded3453355116747470e8e872984adb0e4320870b71382b213b410849e3362afe3b0b88ad760838ae9492dfea69606ec0c0a03430c37f7bcfe1a84f0efc6c47e926af4f36f25af2bcd59b316136f68eca86e1afdf2ea5853c2708496f9ed33bcec430931575abdbb65d717451a8f93f770235b5ced2aa276d9e96849d77f4244fdab624b4eb96644e975b6c490c4a776f1464209de8f2b3a42f7d1c74e2cc39302236856c1a0a78dc294b52b6a073ce3989ec514c48e6eeeeee32a888296768215dba74e99c16427388910b0bfc879a80737c871d82041932c4889124d20e5d4b43070203ff61192e6ee436e898663c2941b22119e29ba65f74372450c82424b74f611656d250f24d7bbb8495924a5b4660479f25ddbe82a55d5fbbd4c1fd249b27e8eee611c2cafd3bad7684a0dbdbd4269d25e89866046777aab6a8925a9ceeaedac2302097a7f4dd77a57005e4acb9fae9401eaf127834c1292d4e155d0139f255d0fcd50fd00f21f3573125ed92cd95d09daba13b574eee940e1eb56b04a78056a683e094768daaa43b7faa92545b56b0e9ee3248ee746d9a3a9041792757661f773e9529ed3bd975cf5f9389813ed8d5cfcac7233d48d435d8ddfa5dfd9e0d96ae06caabb96bcf5ae85fc3c31a66f4bf202f7dba03bb6a8b1d8bf3c7eec79d632907777e6988dbe1cee7805806f70db47d273d2980e87ce7dcda30abdcf9234b713ae29b395f8518586a547d0b8a62f5b3026a71ae7cf4cac7eaa7c569a4c2153bae7eeefc2ed601e94e48bbb427c29d10b00bea80b4385f052c169c792ae89866bc4172fda9e98737e306a845977206f6e6c70d90169dab31bafeb546e62cbf09ba11526df26b8cda35de04d51cf9d7c888fc6ba0b44b4e9965a949f29a2bd76b90aed76cb9fe9beca0768d37429788cbbf01bafe3742ed1a6f806e806e7edc00e159d9e592b77bec9cdceea0804672fa0bd8eda4c9dee6e1fa0d10b39cd638b9393741412eb8fe2a34b909bafe35b21a27eddadebf86a85d9b6c9992dfc92e7b20e883b589b10c1779036319d2bb6b9cb46b768dcc26e67f03f36ead93ed936a9ba9c1aecaedb1f08d0b2e5b1576d199cc91132cfa8f3256aecbc06455ae3fcc8625f5e33609ecb22dba7c7f9be4395c0ae885b5a46030bad8f5adf322643da9135e04d321f8cc90eb417cd32aa2185899019060fb674b70bb2b24aba1956c65845742d7df57aa1f5cff0d5411f18dcc6cbbb3bf01dad56ee7a611460a88ff0d50bfb0eb46052c7a0d91b3bce6c85940edaa29e24605ecaa71c2a2bfd7c8b0b8a05d19d6a8a0c48e9a4cdb8176e439ec5a11e138634473a2c95886f6fe3ecec86a905d4850bb1cc87d057bfc7c7d1b23d77fdcb05caf91d9a46a64d76566d77f4c01b9fe1a3843c119a11924334a9ae55f679a5c7f19ae627c630099d90d92172994935dca0df10dd5d222456a91d624bea1dd10dfd03779e11576529b1691a816fada0f4a472c034937c43234570c16e98f9d12aac2a574d49e5c4a9f5a41a7a049e015681619837e497e0c1bf954854b7f9327b8f43bf955d2e76816b982641ee914f36ea0e99654dbf3159816e94f6abf558b94fea8817cdd9b21c398ed66f4084546406204d95d828683c580bf0c2a2276c7ef569e6db577125d2dfd7ceeeb3f7dede79c6108b69fdfb5d61056bf8621a83f9f0bdfe7c8d883f632c8298278128219ee6839f734b45c586d0d671f40fe16721f40fee35c41c796cf119ef9dafbb0b354ae853e139c62c10bcc14f9e868b19f93361edd1388aeb68b591a75b55dcca1457da3800761065a960a2566adb26ab5d2b0bebf576df2d86417752edeeaf45fa46da76b1e82d972d66d5a6591e6d8c2c922ad3a44aa492903e21288ca154809c42510dd56e0359092ca8c50050977754a8a28a6f8b1b2e4499128981089a20648a208e283287c0ce93477b96dd6ddfbcbd9b639e7f48298734e71ce13b4a0eeae31bbb3c9864ed6e9bc84d28d6af487ed078f4a2973a2b68ddbea86844352a7cb57bfc035661b6cf22565506c69d313286630aef69365e588abfd7403c82087121411458721b2d8604d60c15d214522e8d74385141a5831410e58d0e0a575a13d4eed2376fce4b73c674221f4528792d4c9ad9ed8f203501114376c4786fa7b2263c40db768074193522ab7a94d9f735229a5fc396da46ccc256be3d5f06b69c972d6528d6a9aa6c58498744c33b6844a9bd0dbb8ea587aeeb30ca0affdd4c051bc5ae9fb5a7bee3bad01a5a74f4be0487335fa96b369176dc0560304579b2eebfcc19a6a77cb9e75b27dc2b08ce6baa2611186bb5a981b2616298eeb5b3b955f8b6d695f772933fa8b74ed86ce222800e8c0320a004b24b3ff0c5d38f5957704548692996557fe9785cd9764f9432a965f8ef365f74b7672fb4fd031f3ced1a66928b8f6351cb7af736edbcf163864668d8673bad61e45c73c27759fde3016e645fe95f27bcec9f4085426c39062c5545bde0e12b1638caee53934ac4eab059a198365b084390f8c0418d4e0090e8ac0e087255c34414d44d68617cced974878118e4ad6060e33b8d2050f702022096d8b2230812945b1fd7ce7ed31df07217448820492143d18d1b65c0b85f58860b95be8af5dfd493e298990037aa23f2ed09e14fd9cd808160011450f4014c10225d38a2686684f9ae8e1448dc0c4116ca054d89958a2c8896b0da46aa8346e3725b16468060200004102d314000020100a8784229158309c478234fc14000b708c3c805e341588635190e3388c428618630021841030666486863803b48943e97f7b2c8a3a158d34ba0fcb0fc8b6c62eb0733e0274565b359e90d8c22ec8de0f5afac0defe6518e667a2afbb601c38909c021bcfcb05f934230b9886f370490746e4d242d323fb5f62ce503940829f4f82397098b651590d3c2a5324ac5238d14aecbf2c1289625c3069251fc1a77a42ff6fb7f3ca95d4ad75f06cf4b188513796d26687849fe28cfadfcf1e63a00c50c38c3f4695cc8d35d6eff3282d6b50337eb3aa9ae2b8a3e28a6c4736760b2df4b8aacad4f0a96aef5d456d7a31709d25823b513239a60e629b3a36364fb8c235faa060202e9ba69b19cb4206dbda2f0284714eb72bf37a8d7537d35790d33d276924ae7b2c2dc5e9e20b5f85300b652061e9472d3d384317749c0264e40e741d6c3fb5388ae39d6c2c2f0576e2cde4d6f098a48803f138ce6e9b86160c7ea24c456466c6dbcd39686466fce1b4cc0ad1d07021573c071c2b6125dbc55fc91f97d81ee1f8cee886f6e298ae5f08a7261d4852dd429a62612d975805ff5d19fc65d8ede745e1071be8b789cefcde807ade3c7e1069d6877888d1144f586539bc858992968f218466ec701fbc4f76a7062c598f38362ef41095e31af463395eaade87142d8b5664cf78bb113fd83344f6990174e356722be958239d933e11e41bd9053da9bf79a598838060939ad11dd6cdb4d8906722dd609a10c876ff3a3d65f448246d6a86ae7aec47c090cdf0a5ff187ddc75af21b555c89e410e7d8ae2ec701129809e71b0152d2201512f35f7e89e1365dc89a4af160acfe8d4468d4277c6e0ea02c56e36cfffea1b07013c643e4e35a0c17e4f428cbd6822b6ed091c04143f91bc8402bbacbf27c27c7fcf0a5d5571404e742fc1af3290772995c0fd21bc11eff4d02ecb697e4d5d4160853826324f98e621f5e6415951fb118b77c0ff52ae45ed01032c7e7995ae5e29930a17659d7fa552a840a928ea6a2bfe1732ad4cfd10b4f2705f94e7b074016ff515d04dbb305681aed96738d37c19043dbbbc9c765d4e36ae9558ec3e59b0e4c1e18a8b19d8b7d51b29e33b8725afb9d32df72a98a36eda3459703bb8726f9acb09517dc266475d6f478e2ae8ca78058ee72fbef6ecb2995605e0d98c97fad1c64f9c05a1540afe7016913e9a4f650b15fde963bba190704acdedb4ccf4e1ed89b7b900e6a67da3dd040d220583c9e7f4a35c9752adc91ac8323c431476f465d22bc84ed9f3c1cc3ea5ed8bb6a7aefbc417629e0510723802e5c3b78bf8250c005eba58ab8a8bd7a53a0edcfb24b3cdbc88c296b07e53313be5963d68c5cb608e5d26749edb3230ea01f5bdaabcb988822a32c86f3e1881d4fdae94923a01e771b33fbd3046e80cb3339df7081e374da3a6a79e8b4eb97b1048d23ce9e7a6c9dfb099cf593582817874be6b6e85f65a381cb6ad7b217d4839b0deef73228cc12c604d1ba3b02a745abef0617520e99c5def392ba0e7d8313b1558ad3fa844c5f29b7ea5c268144e030cd96f778b871ae36095d6013d2e861253c1501e9ec1fd476c25ea29ed8a6444db939e9e7c5ac2c46a254f16983c34a24cb86bb229706592f7d5e40984cfcc29a943e479c5396f86a9bf08a6d7a9dfb64239ddf9b94bec84ac25dfc808f42091e1ad9831955ff96d2c72896bd394d257670bfe2282180b478476face32945d48194d0ca0db1a07214676c688ab6bd7e0263dcb8b48d14ca1548867981276d3852c292233f2e01add41bb2dc2e4de7f94a760cc0481a41255958e5a936c9be74f4ccc6a1568394d4056db74068a5e9d9644f8c4d54fd3ce3a6f08feb2eb5995e39321e82c4e844e67138a75715634078ed0b336a9b4c371109915e22336ef054d59cebcf22b8f0014552f2579d63c0705f2b429bc2963ecb32aa4ab5ef7acf38c62ee41e6f5481349fc88076007b336090934bc0901b692563b37d902dc23f8453199246f978af0ac2a8c56ae4c8471c747c2e7727b0caf1115c9aa5fb26dadae59a5c99898278f506906840c874aad3bcc3f42e106c1b931e54ffbe749ad6f2a1e962a6a1b139b6c10f5a6a5b18e54ae9858e97cdb668e434b93081e328b276fa4841361d4046e57f8a6d26ba487af8c79908e5595c1016c9394f97080c9d42a8501f47d85b080862103616631b0e5d139804c3e747c3c763642b773a72c0dedbedf97af548947a60b3e40c93e74fd7c3960863891b0d341f076327c48031abb4322a81928709f08d68bf12868a98dd2ca8e818c8a14c08dbd8c8002ab4097840f8d89d26fa525725a15565f8dda569764020c9988aa969218253fa2d45715a0ad3d5452b1402bba7baac99d80e49a71d942c85f4d407aa7aeb978987036bdf8a21ec60524b69c9f39c400341cd442b8033cb9cc46f094cdf76fdbe243515236e9b569bf4ffab90019b5979dce3ee2881c19002a6042f6e820e7bc83f0e35bec10503b8194bec8ab347fe30c28abd7364e86749c79246bf8ec6cf309bb53ec315745aced21c764f4406340e83ceebe0488e2852f99e27b00c3a9cbb4524871e30bc78dd7aa480c25b174230b73dc48220e51e4b861d255bc86bea62620dbf68c5c99396e902273663f0b4a787a0ae5b8f94f77a5082f3c20448e9b716cee0c08312d48fbb1ec827ec687ca2f1445dd056a173e35dbeefafbdab0366a7f1dbd7c87841f1fa7e71a1000676e698e8dbaeeacbdeb0fa9cc14324e81fcfa714015ff350a6c8ccd39126a5e0d6d2c1be9d7cb2993f0cacd0e0985d7e4119caac9ce029d3f27d26a48a075c6a114cdcb15b46b8460587e64a1808ea4ac075aa4e2cfdd90216b76b809100286f373154a4fc653ddb90e3f10e22cf916c2e36082faa6c7ac3bd7abaa26ca01162bb47bb33da3e8c12ad6bc053716f9a7970a3f3a95fa56c725087c1fa9cd082bbcb25800b67607c7c7c15321c91c17498d794ab02b0e17ae64e31563232d4a3b29c88dd5b3db5ba60fbd884d9f07748e93120a89c3413413f38d340f96c515beb7d06c955f1e0378e6664c4b188409bbe05254389640cce502f0e5a6cd7c3588df478023c6663e59fc45379dadfb9d05c37d389dd147ec45c6491c87e933ca684d7f636a712e48ac5bc6bf12634215b54a784990661d5f91496a0513f83555a361f29a734404ad100ad13b1815ab5ab331713060eb9638c4dfe72c0466af621193dca3fa2ceea0fd176446b2cbcc4633a8e1327a0ad6533e7d85a1e33d97363ce2e3eabf91b590ab6fbc653a12b20a5ddd9eb82699018e0f46fd761db87f53a7ecb8a8d235305b8d12bb90b06b85c0909f4669236f12b72101150b3d1c9cdbe9c3bd928aee94baa4a6f90fc45cbd162a52b0a156918432d013c1d76c56efdc7d372e512b400bc9c9dff7e6053a46cb1fe8b6f915c254b67b547b0458c54c10f9147ab029aa8e697e96ed9a21304db4eb4f33655f245332b60ed843fe7523e6c292f30c6cf69b9bb42446ca57a4793611065ea9ffda028c404ed4d8ba7bf17a0752efe9b3b0b2b680016b8509537df7626dd14e4c1e640d6776089dc2b2048a382674e0d6e7b6a43384079e2771ecd125a616d8d6d2081143df0886156df2438bc4192a958ae372662a2194351ae40b91449df8bc4d9a2280373718823175dcb23dcb38a7967a5672badaa925c4d9445df09992d02679a8518146ae7c64984a032b81d7f4117027f1b7eef8060833938d8b68ce45826e04714227bda15ee94d85f7d6c0273f7830fce43691982194f4aaa5487f4d0196baabc976f94d2e238d0b2c00a26686a6ca1fddd3b05afffffd901594d34996e7d45373af44cf950d916ef87b84b757bab770b3e73f4c9dc6f14366cb4bd5559a0abb4d391e9b3a62146cff6039c08a1aad927fa4a295466d69d009d6a7574ba0b5714a77fc50d0f4cad66ad68ae235b98837827066665f73e119a0f50df25d43d06850ec5abe33e644cd0808640062b71e97bab7d663def78c2a183c19e936f8a8e516e16866d1a278a923061235af158c162753ec0e1c4a40a445e52ec78ba99cb0d8c533ba12a28e3d9e44558a2f99d3b2ba4d1160bd059151d9e87b7feb86732e60429c52358c1a4c68019d3a29b806ea44b325fe27617fd5c22299509e6a235581bc48c8891bd84e017b03bbf38f26113cfda67982c40eb8e9b5c222d95b8652da2bdc845ec7a4ad35cebaa8eb34be44da5d7eb6d7667a2070de2df31ce3ed602512d69b8451ef6a38d3e1a94c0043c2c25d0531231bdc4e67b25a945553aac59bcf1a40a0262d8d42c2c997c632b31c3e3f29211407a7e4598065b529810ac35fcbcb348bbe77f9d8308ff80620067bb13444bab38f68169cc775417a77158f980c7e70cda8f10189d908dbf9875236f2d04ed2403c496d19a7e9ab648224b8e6dfb4e454ea009680a6ed0c920a2d60f6786d9ee493c74caa38a806b0ab303e86b51d7251ea56f339e27eb3a80761b952a4ac19e6540298a71de9c5a61cc325f1f920837c87bb446308b8c40f1ac90f9292b68558b1f1e33fdeeb35f1c6171c3f805b4a6db1477cc36be9fa0bc47aba7612278fde1514ca02e0dcb3a417ab7c886d0c0f0e63933133ff1bcc9e621cace03a083299671509e22be4fe1c86bfae58d1f03b68de714681deff29bad3abb067ed18053639cb493117af1e26b8ff4a475916007fb7a1dd1ebcf408d0cd5b64dee7d88c1eeb607cf11fbe70e4a00f4306096d8a24db201fd79b8279414129778fc38b966f18b114539ac162b260137e3ccb86f7cd45047de6921466d63fcbb5ea94481c584be3d5c29eabedbfe51bdff24c40e0a11e696621d8f7b01df50bb97bb810bb1242006d286e1664fafa0647ba34b76ea4cfc18936d44b6aa01e6891ab005f08e8f0f79e0c3c3e7d37190500f1ee96c90c4d1b81f1aa4c7246f8132a9a04cc6862b0c99caf2745b6202d437ffdfcccb7d7eb8fd1e33e73876214f4ee7f97acaf760e68997d958493c031b2921f29ba3c3b23c8d0f4f2c402746c7f408544ba5c96fef8edbdba1c1be4b62faa401a9424d70582f53e50f1161753a1b68587a5448713d398205df98f12d3ac40568381816f50eda9b32d8ceff643a16db3397685fb2f18ae88420989e1e0d7ee2c8212bcbc3dae8e000958058a310084361182ad0e4e2ab98d359c6ea565804a5613a1386d45116409f8fd1b4a48a84634fb29cdbe59855fd5e16055d95f4d0580e6d8ae976e4f5952894a547886566ab83314c1b015974276020e656217f091ad1db7f09dc8ea23d1fedbdbf2f8939b56d1e8ab026b2adbe3446c0c649e57a8b964641c086741f66aaca672638dc004b2fee97394d5ff1aba8040895b590bfcf6ca52d33e56d9b26f0902c86c9c2d3ec8f45c232a2843792548964486a00c1591edd7538af48b25cc4cb3aaaeca27cebe2a597d6be839505089f452e3812dbfbbb20148160d51f525a58231131db51197562fba38c641b87eb2d958c7b7a43ad7fbfa5fd6aac4f68b1c5f6eb180dced4323261c6fbcb5c9fcaf368fd374656630f2933d4409cb84fc64179e37874a03d4710ffd23c760b1caa50affca946b6cd3e05bf522c7518e9f304b35aa4c822a07b45a8f7dbbcb016d51c4836a31f02818049c2b47a61d0f0146006754ca1c930bd4043ea938c662033fa58fd741a20f6d0485c0f9deb401c4c57f8490c04dc720f0117c2390b064aca047a45f78ef60a348b8380ad39023fd1945c7be66043fac6e22c13dcf92326041b19772d66bb1a726882b520aa36a099cc556f21f5b595c5ac4d8664af1862521c46ce30c0ff1fad35dc2aaba59bb525b3682e87651f19dabd04e490aedfb548118a97123d39c569209ec548bca8d23a0aa2187ca0b7dc6caa9fa714c4389b2ef3b4013b4fa2608d195a0ae442ea28280d8590e3f5baa2207aadaaed5f1b79d04b6876829fb9ea44445315fa01274beffd0e6dac0fed1cf7f04c86106902f5bb9557311eef5432539b111c13e827e23436d5c28594e620a3a638de2b17b46e229342751cfe75ae6bc7cffe2fe10f43bcbdaec76d97f34e3f80f715a689fc9adcc46fe2e3403f8b17e5c402aa114f034497dd288446a633abbad7ecdc607ce7c12d05388445c0bced704b3c423eaebfefcf11a22af6e42d32598a72162f868aab559e7ba6e563807f1dca36d7166224927bbb0e82ee006f6824534e4fe283203bb8e9319cd214fd44993990c7040bce00ea08d928902cf7230338520012869207b22b94f20b4ad40c967c179d8db85121f313199a3dd40232a413593e0a884baa6d7920c1ced84447fcd28f6c5fdca071d02be3b0c5cda8108a1b067bab9816535fd99d9add384af7f20408bf58a4076fc2a8fe8bf3050ef08fc5c9a342e7e8d446322aeab7a7be009f1e86787223684afcec4b40e3a2f0606c6b275444de91aa4e6f092ae499b686f17dc4a4139cdf2e2b0b2b80fcca0dd179497a7375bce21c66bf2361d7fec218f3e64e0d9cef5d9e0b2dc88315b8abbfae1ca6c3420b0027279b82085aae7df188176f8e3a58615203cb8af615faa059b9a9d4bf5c0673f7efa07f3bf53573889ee29a37185a99218c577e4eade37d065603714eed5a8ab4a6e70e4e151e4509c6c36ce1bff6bdcef5ba502d44aee91e8f254f3c345c4dcc9fae3ef159d21e1ac56a1446301dc78f84eabcfe7483f42f37db9b9bbf7b8673f1b08a75069444edbae22e8802795305073b07ca196d1dd71821d8147a9bf7ddddc22a7451df183e37a2b1ed0d867da9ee4953664bc5407d248b8484c5f810d692a2d7c9d6890109d8ada334a6e3feb16abf2c727e04af42a695680087105cc74d6ccb70e95b7a0b1689ee9142ade9419da716718f0ffc6a0d1483210e16a8930a058e085cb20342ef953687560158297b27e4f9546caaa307ce0d1c0c304e42ac1e0c3d9768d77e69cc2c0c5911076b4cbce28567984a61a7041b47009c191e9d6d8845caf61c8a55448a0807197b71d3e53027a7037abc49ebc371326253091bffcab0db5764e3c77ab015305b1b88fc0449681a6104ed5670182213fc151c7a6720703a0887959a798b4898a496056087e7aebd9503161a06aa0e0931aeec8d575a002630fdac2bb00e7d37067c66aa1c7f3e30fb47d5f4f0a2d08b2c199090e43e7537258aab840018bc3fc87a7f812f38c4f0452aefbf3f01f855b8a742eb43b20d9294128c5cee41aa048450d1e52ff7aa4c7abfa662c0167aa01a6793d32585de5571f7e2e7b46b22beb912703b19cb91a28d9698ce34c49d729fe0d85c03f27559e9256cdaa1117bcadf0900ea738428c0c418845636ef141131fdd52776fd76887c4dfa67d3a9932638f8f7a40465ef4bd9bb54a7928165f330734770dd2f11f457bda7a82cb703a3d850501da5ac5717c775d57ecd67ecf4045e8185ec561d1f972c2d65c604a6aa4e4be63d0ec767b68d38614b96a0b3f92386265e604150bb1e7c8a481adfcc4aa0081bf0bf84e3b209a5e795737cbb30411bfa76b67ee8a47b31c027a4c0fcd3dc2592e772902718bc345dbe915307cc47c7f5b59d6a84bc7e043a0c04d7b288dd62a190ecfe343cde8a6d45873061c222f0c1035d5666357226dfe1d8cf961bf7b932994b062bc9a2a9d9338d29551ba6b2c06797849a0ffa5537f25f79537637e1e67255e185a984b218b03a2783d4cd494ad413147bad06a40a1613932f628d8ab3e79d003c417e0ae0373dd66e197256946259dc4b35cd1e44b9edc979b1fe90b906ffa7567d7da9a4c6504dde5b1c6cb09981a46b4fc7d42a8a7f7e1819d04df2e4f0940749d00364c791643673fce561e410e7504698ddea0741fd51204da3fe8909c02f54445a065da6b9257ff1ee927b50a95d1f5dd11d716a532ca6b48a45e4a4734bf171053a2478cc54601ae2416c95d41b54505d4632c777d7d028c51d702122f88bcb4fe28393eba6653fcea3df8551d085fa9b21a9e3cd766dedbb1f5a56969cf8aa4e7b34516635ff1dc710259013c0f36a1948dbd9d471396559b1e35a84627da45c4344c0ed83637e0f5b75b9932973bffd432e10a248e64bf7b7d68a96c7b21020899b66afb245de2d4d097a9214905418c33afc421f43203c080d6a33c465fe47d304a0826369649ef0b816c6a0fe3e868de6105dd96d6e59c21863846be420c6605dd9f1de6b2414b904a99aa90ed141a7f86f7e5dac9e34475694462faa3340cb2af8809a5aff450ab70a0de761e018e363c65ae8fc2c380f49e6615b0f9ab8f82591711b790802d1fb56a16eec7f54453ae8fc030721cc257c82e49bd3e9ac53fe620c2d18f57c09df22d4599e23c8f78ad5302c656acf3d27d599935485b7f70e8ba681236751a5dcb466ad0d4ddd6eb6ea427bd29379384bc87cb285208d3c9a94fae0923d27d500022305c5710484b0ebf9d8300d985ba194245ff606afd9429580846cb84b00f0b8dabe8e4199a8abf1bd1509b1d6cb94150c73572b6732b8e90a0f197587aa858d7554b449d79b4788d5998aafad373dd06755681a243ac1b345bc0a621b2f80f92045829201ab2954ecdcc35a9c297f4051c962ffb08074f2246902ba8064c991fc61561a427762a52d2bc78bba73381f39b72f1ae6342860074533367b35741650ccd5bc6e5369b731af8f0d716d5ee93d7d523225646dd52dc630eb601347107a212073ed5831696dbb180624e863530fb37dce0e605a8d0c116b7becdd5645726fa522e509dea16ad9c58f3b1c5865100dd481d797d29475ac10f182f6fc673a747e889eb08723c44c92516ec4f167f7e2cc471b0db84ab5813a3114cf4859bfdf318e2ea8ac5d10e7b5bd66ffb9f0b026830d23086b8cd5bce5c3483dae9576f91220c492627416c7673e3472f10d214f90e20dfe695b7a269233036ff533f8c84dd9630e7e3e3de5d95ddaf40d07b3cc085653a64ccd887820a59dc2dbdfa2270e2385888877436724a38db3e770d43ef316bfbac4c457825e66c9f45d14c5d4f028750c73e3fae890162dfd0b2c696fd2451673f7132eccc22e75a2cfe3640b3a802bda6fb400494110d6277d872cc00467494c201fb0ec2236e876a3326f2b1f97022446e24d01ccbcf8064a77fc557c8863aef3f425302bf4144746ba4107eb42b454b1cff6cc310573fe688e1f08b6ef4f6fb75e2495722c16e4b30c6cd5ed44d836c620722182a0bdbc0f683b8eca12c6b35fd5e09c95655597d0508ca5eeab455eb8cccdbf061c1a339d08f5c86d4af9b015db243303a9511055b7f668713b841f2e1f11255589f3a8bcea9aca3b8729d18fb8d5fb0a493364ff7d5ec7c9d2044c754cff6f65457db7439bd75a44aca1be874a34ecd128e6ea1bb3cfbcf1a6847f051b581563305835e8c5f75ed4eaed1102b3d60e8415d60187c7e0bbf161810747fcd19dca08655c5af8f4d9201ff8573674530221132abdb103d3e46a48711c9fd32e5244f49cc111849a33e424ff25cbd0bbcc81eb445e0855c23c21d8597fe24707533f89ea5cacd67bf175502fa78ca634bc9e9344db4d0bc9c0cea89f1418f1c66d9aa45bea6145cccc70b75694fd645db42b74e1fd4ef359c73aea34f01f2e0a66b83341ed0aa7197ac126b3575ad3da296f3275331730eb9adc31781e1eafb55cb219ae51e4c8ee381cb77bbf1f28ca6f60d32a9e0eb0568b6b91dddaeff66f9c387d87d3d8905fc53ebb33b023ccee8490b961c98cdb4a97a7019fd61496e0c69ad9cfafd8f745e91c43ad01c3ab4b8c6b88968da97bc0534a93a11c774482aff9d389c8a0dc16386c051dae59855b05555a1eb00c6c98b80380c55fe6f8cea3133195bedaffe15a88e96d11e9e07f160cfeac99bb014d190a7589cd75809071115c817e72975a4132cff0e0b1d44b3eec429a432d99ab194942fc80ad13243a4046e13e54b355d80613931057a1cbb76f00845d63fad951ee34ebba7fae91558873bc426b0a509e0b8e0e57d314272061454b714780423e30a2c464eb13a4d1d02b71bbf401146db3385a37e5ca222d6dff0452268be95299ee3e18855e9b4e146f801abb703f165032bab4ba522dfa0528832c3769c8c607f02dcc8259dc08799e688081928feab8f8bd21d927e4d134cbc27b019781041d10c85e4649f17ea67a81d518d7b143dcce862fb8728c5e6318e9f7272131e82f25135ae806b7bd79af33205db395110ec9b9449ea0789b5ce64d83b8edf7d53acadae6bd24181ab29b9582cf4f29f6c9e054b2c567c60ff7c67a4f30167219ebc7daf0a40752396be643124d9744f3574f19947995ee73c0caaee0be2bafa1bf1947d86e8978d45ea0b87d894fa51cbd24f5f138d4665078fca272fbc44b09d124d57c8f73ccb8fbb310174d0a063d44c79f2dd92841feee02d4a455ac4b12a72a2f95c70660cd14ce555797c7a186253d04f777906f733c1a74ce37fd6b5c8695c51b4620018512bfbd426b955ccf40c150ea69a2ee812cb58a2eb8267bc98b9ec3b5a47608ab298b8654516a293be1d9e21d59e15ea488b717cad68df3c2158f95ec1c9e50b9b1c34c47f2251f0500a948710a552bdf2da674d2884a681a48e34ed6dc29307c119eac05e83fe0a45a2435360dfa5e7e28140054fe44ffd164585e1394812724445b1788bd10e3c5ace038467ee8cff069457ad868c0ae3ec52406ca0dd6a9a6e4091ff6a7d4168777c2403e31bba15d877af17b1483f62932221e7c26184cb248983bbd4232c6f3fe23575c40861872b1afedab7110a1899e58e42ee23572657ffa7a4a90a7b6200b0588339676141b1dd06c08544ea0614e775d062c29b9d0a65d841d03dfbffb83d614278aacbc3c70dde66a417a1652606cc4362b61d18c728810efdd495158d807790e02867907d7abb3ecc69cf71cd26f483925685c03e014b8927c31a969543d7a5d9b218deccf07025ddf6241bd92191992e19882a6debb3f91495ce6080c66965c106132ab35ac182613133d0b34db71d3611c30d9ca1f6064b56943ac8eb9b6fbdd6e3556700522c03da6ae20c23ebcdc3a500e0a064beed0cea2ff8100d26ed2b686340132a00199de25d51061bcc7c42478d2799d0dc145221556372c99b9e784646bdb2026c012c837613c981cf8df35aa6b0bc3397bebf61b2f47839600f6fb416afbe1952429c72869660ebf5750e43071a568f70144b48d5190ca95466a6e944340b133ff862471799e0af6833e2286dcec2ece919e19b5a842d2ef164e2d4a0d32c7c21878aa7e4c9ebfd05cc28080ad6c2b38ae3c21b392af87cb0fe88a0f3072389c8cbc53b3ca93c04acd81fc1a62db60fd822317c74782061baf83bd24b2fbf53b6f4abf8d719f333006f102c591a447241e005cb69e7718f73f4c6ce037fd3890d04a24c9de4f2309de8251d225d80780d7285b550636dccecf3cc0cd78f7d6b16e467208802742eaa71d76d16cd8b6b97046f6caa8d9fa9b7c9665085b6d1d0cee6c0e7a0edca0687b1217641d8b9aca329c9e5ad3852a36a54a22564c492404705566dfbdc23aa9b90fb09d37ac45b7ad94fff5c2efb660e35b7508b2f8d70119372cf9e0547acdc4f9d220d6ce7d60ba0bea70eec6f5b4d67d928358d68d7741b172b491f72ee5997619aa7537c55b48ba00d455a55b4a22ab8d6842718ca37036e5f4218e2d1897dd1ee53c8fb28f5885c41afd6b2d2a5e6d5364621a837924b999bf23779d2ff1070960d262578623f8c4cf87720041ea0203b9ede56b2034e5431158cf10410adaa0b59cc660ec2455a794cb346d4d1e2865bfa264c13ff308bdadf8067e21ba1b692001c365b0ca9f4399ad4c8c32b4588f2ab781ec72fec8f27e8c5f70fef4b7c7e5c5c8ff8708bb0daa3af142a86b3faeb7582498da09cf11bbdd74e9e0eb76e2eb43671d79f7265f0486a9be226c9ed0945eef34314e044c1d135ac1d398e34b3f5aba3b0876259a3d984b5fb2349c0439bb2de7783881029885cbc219551381ab326a5b4676ce98f063a450110074574592590ba2f57dfceafdbe0ee69d4894cfad645ebbf1b2ab294559bd9269362606967a495732eb5fa8b37ebb146427c33488aa19acd7d4d66872ad68be2a8aa97b25dd2687b827caaf15e97bcb5eb10187942b9c5d36682e4e441137f0b29414a60947ec91d4e1ae04f7ebd7a4186887d560028c43f22044b920c5d5890657cfa2c9aa08ac722454236e78623250459ddf78b493ebfa54dab0ea90a6b42a4e929443635548d93c803a89e05f5cc3aee24b22fc520544303ab7f8cbc19603d4487a1088f089341c69aca5443d010625831a912a5543da24174cca9290cd786f63fd74dba5a6618a7243a6fbe86b20f30f885b8bd7e35bcb04db546c175e5625099830a184354d0d1b73b638f64603ebaf97d8768b5ebf12da2b4a4b98cac957b8315af0dd9289fd2d9c8fea7b738192520e2eb66e3c48c697d277db360be5ebb36ec3fdae436dddab2e73af5728c3aec64f334119341e39a83a468f5135c5bd95e8a91673f39c31ddc99c869dbf3a787be5710a61b86aeda52e50b4adfa930897e68d0c6ad63233c886d093a724219f2908b889461043b0b58bcd8ca28b8ff79d9ffcc4ea41d0539adea024c7f99150892a3d73a68fe1f4af5dca6a1f2e185686d9a9f66e1ccc5560da9643b4a2874774b801e914d6b166d99914362f59ec99a8cb521a7d06958a24fff58ac0523c18bd09603ad9970e4d530e841af0ed6aead9be70c647197271db50718663fad7cb91202030e54971b73d8d406b2ed8c541bd2a079a96a037bfc7bfae0768f283a113164851959f8a0004e6f9a00a20cbb0ea837a33f0a053c7e2e4ce2b57a26fb8e74f489c81891a6e2429476441a599e0007e4f82adb459b31043d74d06cd3f93e1746f0006beaaa8db0b2df18e36f8ad079d4bff8bf7ef7f861a48b5c32826e11a0a675b68ea79c381ff370df4435d8deb5afd78d95f7a2ac7d19ac88d4e4e1f7be543c1aa0f08589fa82f5d2139c9bd54c81f6b3cf6b2c0e9a6dc1e140f68e89b89ac9b64298861bb5b949fff9098c996194714f621cb8460afdd5461b9f1154f9c19a99454aa27a675e8d39ebf5c49677cc52116009c2b54569209eb4983ca7fa20b13df81976a01de6a8c8b020e33b6349ea4062eef8ceb8c30cec1a68983a2f1c61be33d43fb2c1c08822c0735de4e88429533135dc2044646327ad43ce9030d733b5687106cbe7e9fd24490c8694fbf04b935ffcaf8ea4d72bba3d74e0c0fd3d625e8c08e3d80095355dc1138142afe4fd31057ec6d75b23544a35511900186e4e0938300429d8960a61e1a0315e27e067e689c2612bf49956459b4853953d75e154ab5168e820876c005f5203e2009c3b5db7f2721c30ed94319a67c262204b79140908fd9aee51f467b85d3ca25782063c29a21a15471ff8014a594ff70d9ad7c80e27f72ffc9e23c693bd1d18875a4eb62062ca4593882cc8c56ed4479b6e4808dae88855660065ffd2c5e80d7191eb178e1a1f7663358e216819b33c86afc9fc21c7886dadf1109b8e6c7ce8b27d0d05c3333cdb824c5a94396b03b4afdf2825a0c4fbe060078100bc5a5273a800a1b894cc022a43b21ef78c0cab2cfc16f709e998113d84460b9b38b119c550a773131de70f49181bb3306deab97527e3e9d573b5250efc41ea70e5f4681f4b117fdbf81fa8a00f925ae8adad8237a87424ed5f644c9432d632848e8cc17c3058789fcb3e4a23d75f204c3faaa1beb1bbc2af0cae36aecaa4f452d9cb7e221165eb148f4411caa21b124d6e29e35c435f551418c7b2ad62620cabca80987a191d43259101c331bb9813cd6f8fc3bb58189de246e35e902dff9f634ce207abf41afebddba88442e0e894abcc7c2bbc2a12dba5c56435db7199ab99909cb511f867af9c27d8fdc18918267b9aac80fb8284f4bb46805e101e745a3ddbaa0fbc49347403ac64e534c1499dd051462ac1fcb9410da01556061ae5dc306257acf10f31d24db734f6935c657945fff63d4435ae9d20c8edabb38d8102af46e6ca4113158925167b9ac009396d17338d0722e8579f8611232b86256877f9382807da8b9ee827b9bc92f9ce4194c07c5023e260081792b704f15368a153e47d241e50a8895ccf5b0909c34ec27b589577123b249b2da53532eae293bc9b4201217493c13b7e3f31df70a272e58b76ccaae530b9127ff790601a4158649e8d3fa2129674178ba383da657daa3f3a48683bd382bf3a1ba145adbb083de39a3c9db13efd0e0b293d6cb878cdc953e1f61302ffe6f4819d7c52f676f84814f94f7cafbd1c4f00e91725ad4e67456fe06afa3e403440e80161371ad52c57f4c234f24e6db5ba8c33f32d95fa0bf929d816bb6059c62f5a4b8d25cb2392bbcc2572ff4b924403fb08305999c07d234f344d967d92ad34b66300bcc884b56e08b2795a0d3cf44a29d5d2af983a8a05de70a1e7f878414ddecaa24e1f5058d58cb85c644b00e8b015a8b6c1a451a032af074389abc0668348e5d5339125e6fc3dc0b1ffaad7e40e11dac6757cbead47b857d825388a5be086e312ce17c0dabd80f0e678d839906c6349f33d561e047c2bd0443e23c26a60efc98b26e1d7361c86dc227923980466405c8debb3d72eb1d5421cd1e762d76a8344a4a03d545c0bfccb06bfc9fad3189e8c6fdc3cd567b718b1b07bb82b034def18c7517dd53b357631edc134fa908b9997d146895c9fd770625f8b477254d0f5bf01e709369a416baf99a5c8008a0401eb9ad41faf83a56f51c23352019dce45ab3e0b504c8608c80734040b4721a2454a941cf83c244f90bbac71680816c383b47bf36adcfe6481b63111b0e0412d3c77ced5727102193666a979aedc80cb9784d4b57b38fe1ed031cfd3703600a8f4a02be2a44d33a485928f2dfab4ab28864ab3696dba517dccccd6a411bf6571f4e439b4d45c3ac902a1cfb9f87eca236c6d46bf111cd99aef6cbd739fa7dcfd23c1feef8727b9aee1e81f0cf2d299a7b6e0585ba316a43fe11419615d2610ef269d0ac48ef9a42fec6f7a4566d02aca771584852a4874aa5dbc3443e24546024c844c879e05417a16113a7903e0e798e24827033b9d1fa65568c29c00d1ac01f0f43a7a060e96d6b9a0d00a8f84463502a9ab64ef986d5e672fba51e12fd97a931bf33548d69e6d78621e58dd261d3ef41a7f25da2aedf0a99ebcd64e7f371ca4a8a22309393b6b2c37c20f1c2c4c257e023c73bec1df641a284a714ee8883f1b496fe0e6109312a78be20430026e5e0568ee8a8005d28cc598d161ba27b7550c4ea3768288dd082730109e8fe0afce1960be8d4106a87a4a18028450b51854472c02ea8090eb6cadb82246dce424273e737911355452ba131283ed433e4db22d26e961e192bb5254c432712bcd71208a6f957c8b22e3f66030f9c136811806742c0fb0828a36cb0cfdc0c4b5e434f01b3a01fe5d52c85bb9c0c49df4fdd072741b4451b24ebf918302874285d5a7cc508e31ecc1a9144c429f1033901f0dda9a9a6d8e8c29465049b201b53be90df39dba9eb056abab0654570dd246d314dd76fd5327842ae253e021a9f570053ad81e148531f76598ba239393269d517f6dc4e58a1640ea2cc240fc07a627039a239a41630204d51c5a45aca1f0e5b972961e55d4eb8a79c246adb3c1688f60fc043070b6b783bb146ea9c1c56e384cf8f18d037b69c639e9fd60355b686ece993a9f6a1a30d50890db4d78c11f41b240a1a1f66eaa3ffa94e97702252945f6357a8bbcecfa5dab40d3821a8882491226add84ad560b6e024cd57d31d4ceff376d52b7fce696b231a318397126a3bf915711262b2d3600257f4c9d8c4309273f662ccaada63a25df89003ab5fcc7c0e5ecb814ff17be073edd1860f962fc07ff791293064a89bda58f061519dfc56812d41a01692bc26ad53af4d8c58691c7921ed291843342038a4b5e978d82633afc54f84c590f82ac045a96759d890e5c22a401d3d357322f12675a915ec83fbec00be57134d1f7a4657bc0e08026347490df7b6393da4e5a6482e26fd7af6ad8a08037b894c92a5387decb5482d048397bfa2da05e19fe4535a674e970de3d1244ef7f3944e577eded179276673661c6ae9b5cc07feea3999d84c6612084e77eeef5908938242b8dfdfba21653f21ddcd10bea15b8bba422fd38dcd48e02f310a6f2db3de0a2f69f85a0783650d36313063bdff6ce266d445957df90f61c0e41e91ac92cb79469a80ff48bec1175c0bc6b652dab82deb2fd561b294dcf314479bcb110c56bc40dece7470fa28dc0ec9a77590c842e061427e02f6b570a515859ef701852a5f8c734fb6bde82d445d059830e27edf735a9388e799ec1320174ac3d7309ad442b5bae8dee5b0689eb15588167557722765f75870d51969547342801c33d1b79931aa92dac0d67d6f5847725483bf1c07e1b23a9239dfddea06913ec13aa59204042309d58d90683e22048ce95948f7d880283cdfdcb8e40c1ba3d3347df8c3bb8e242a1f3d83c965d1825abf139c31ac1e81ad46da129ceb9bf718e0514600bfeba3b3ab4df0a40651ec1ba88688107a489ec349697fa586f9f3b2181b9a25525b2c20ef95b5770717ae9044000eaadea174a8edf7ea3f3071a2d3269a6fe548d67fce5189de05f7918536fda1556567e5f903a7ae95814c1bdad1379a333742b7a67c8686355191319b770f82722d7a761745c0f79a6f0b66e340cb261ac683d93eb4e9a521dcaf43bd30f4f50728c78abb4460739e0fcc5021e58ee1c3e2ebbfaaefc57b4c34667082d3145123d0c45cb0a51808a588aa6e4ff57b198207f822b57e5df91159809cde795aa167587d51cb564454beef65b5eddc5acd787ed561a69d1f592ae33ff0b471cff5e6e1485409ebce8aa6b234f6ef176a8a4ea0c76eb7aea2abbda177c096b93100446c90afdda984edda4d35a4f871d93306f25aacdb505a69518a9017bf2cfac9fab6b7952504b01006af9a4f92969a655ddb5611b86f0c22e89be942afce431d2b5135466b4709eb5dee9fb3fe334a640467d0fa75f29a1f84a546242bb42820cd0e9ce5833fb0b15a91dfab91e640ef85494a80ca52556fc701b88128790979f2b482472328d7e689835b27059ab35ca6f910fdec0259b76fc0257f73381f1445fe48c5b3b36e898572470f3a9fcc5ed0288fc7b533c08fae74f9b1366b200d85635b4210ca9d74bdea67df68e1ccb50617a44496cbef69e1fc357ff7bc37d71af81ab73444345118eeb3f1a6149bfb29eb7c1140394fc83c921e91de6cf6e48fd43158d24333cc23dc35d5f00dd42d4ba574f2e89fa0a0c8aaa180ed50510d4ef2b8527756172eef93f34689ea297e430ff5a44032678b000199dd0954b457d15d1784af6a6520f0dc4053dd451b3d1a4f9a1ed33c1554cf01713c4e057653c2a9f256728e6740b46a2021dd8c1d15881e59f0859d74d0fc48c448666eb061ded3d99bb7a14a644dde6ac1e6c464a7976be0db2a8f210c021b307189cd30c34686aa4214c01cf924cbf776c802de761442491051536ccc56c7e5bbf11c8ae05276092ee3e775066a60dcd612f0a9f66124ca6db7ada5a0502cd826a2e417bc65f156e43ecd0f55e3668cad2b80836343b3db8f01147fac342bb9f293c8984b01f0332a02404eeeb36802b8d718e44cc0a4e2757a54a860c5f4254ce786d01cc8ba1de28cf472b909bfc7e048a07579e43cfd6e391e8828825654aa4881790db89d207bbfa3acad8fec3daac23da5cc09a957c7217e6be3f5a0da7cb7365e47e5597f5fb39a25c43fd8f569c06c868381a3afdc1202941a8621b139f563c71d75d108bbd791da53f25332a0373936181e0a109612c8d0be159f2fe47594d718c202c29c3f9ba5160004d8d87856c71013d0acaa6ba879fe1bd21c03236f39dfce92b9598a49ba56a3ba6397aa8bd6b200413c07f2919d8af0f8e9358519736be8e13ecb5fcfc29ce72cf166a4d3469ec175f1ecc4070a49f10df3d70a7506ef257b19fe7c8f290379fa9c6cfcf2a032a74623dc84c04aa60d36d231b54d8805e5035a91ede627e6ab617c142d7a05566a4cb8de41ae57d73a55393ee8e1c90d18560cdcda15f4c939f74a5ddf5dc6f67d6f915aac5a636eac27a598e015f20a5e1f2d8b6d665a832771b6e840d68f4a4f85c398cb01b0eb888ed964e8000fc5abb731248b96813ca62d4df5ec1ad2f5d0310377c14f348c0be0f8b1d8a52d34888802f32853cd4fdd839fd3d079a5df76337bdf2dd334951409c969327767ee2f31728e371cff10c1b69072a0c33125bae882f0d00e7bc180c50a7444a1350a248455331245b85b4eb2f592cf8261da42d9ea6261402635f6b0c9a1e79f7525cba38b4eb5aa0be61db1c23752dff494521cc4036f122bd6771e6a02374320b6e03c7a7c09bba4cb1b1fb022c85c9b32568de294c392755040ee9b11f452cd4c5d0f638b8158f4044ddc912ee2d9626bb4334a7498485f5a4ef70506c3323909a81db2f79bd070d932abba3dfc24ea7a89a98bbb78ce2a166d2394b56d237549dc5989ba8d96225f7236b0263adaa8057e3db13de179e4d7608d0582046348c93516ac2372dc1f59ac4140cca7591e45d1ad766a314ea3ec670e6291643ed4a058f5be3c12d7d59f74aea76551b3b25b6d89f57d5c0c3d182f052e195fad17277125cc9eb1f15cba0932f034cbd327a57ed9ab08f13bc1d66b1857bfb3cdb711b0180c19af7de412b44851a41f4e33d47736b03c8026db19413b2aadb595302523995c0cc69395cee07509e7cb4e27fc4128a74d1315d2656e4c67ef55d1c18f16aac5648725f439764ceebb89aa48c0145370d6ab3b13a5439a9892624f0c6f120b80792d24fb6d20a888107ffd5d309ce125e15e004f1379357924b8a93de3b2c784f35c4c4cb609605142b3c577f07ec61bd2793530e6f3da891204dc9f82362f049739bc355d4221f3e42f8d8f4c3e64186de32999d6896cf06c2297b688c9e7f9409a27d21ad5af323cd87b5b9ee207cdcc7f95ee332fbd36dde91827d47cbe50663ac118f228fa83539976bb8f06a53b11d14ae684fd98aa09f0acf32ab6feaa1bda6a89f21708436a271ccee40f33cf52046124ffe8aa5daa989d557839a5a135228245b6aaea02640886ea1467e1a5e07d73c152f80c9e7aa1e12ae041fe57829db63a1212df056d4ca05ccab095bbfbe4be2b469d22ead342549443d819dc34c364454bf45d7c3d2b05d85aa16457844eafaf5cb3e45842bf29c1f58043d8a7180137e78a13ddb304601f6ce24386be61f4fbb082568b0c8f6124a612f63fe342335b36ea9daa84b70493ad4feceaa7fefabe19826970beb913b1881ecb56cbad832344d5bbae83a800cf08529c33ba8ec30d0d0ccd4d43fcb8646c0778a040a54aa163983cd4a63f41ab354c0dc3899b4867cebba1f916f388c96a9c7df28a88d3df47d56576e04c429299c6dcf89f6c8d4763d6d8c008464c3d9cc50fd97ea330a078da74bdedfc937a5530e25df46b0cf450b8973eb5cfdd8bab51e366af05d09a654c41c1af2fa8541327076c7f8ad6725404c938bd5905db8beb9be6bb59895da37c9429a0cbe3bc9f5cec944f9a0ab7c1c1869695adfe71d904d01186d21821b8ec2b71d9b05b38f9294afc64584a8f49549f0d73d8badcb9398712c56c1466364c82c84cb02102eb2d434eecc16ac0caea9f8c5ab39cb7640614b74c5ed3b5122a2c692f0017aeb2ea653b0c42cbb91cd98c0f14cf71cb5acbaf3500f6f27582f6edfff3e802ed6a34fffa13f757f290c1078e21950019b64ed451b8ab05bd1ec3fe2e530e19617247bf45153a04f8ce08735243c191b2932667409ecf2949add4ce8b23a229cc590aba89ac8c39237e5b0322ff8efbeeea1052872ae2acd865e56e88a4a99f48b4a4677bd16e920bc28faedd6a112573e0b668aa63f41ff1e2cf5c6bd9e6001ff08c9fc867805a8decd5f8c61aa5ac71a43550c530eac1dbdcad3be4bb4f0d11d9809dab99d147cf2fd3a63c21c137ec0a4b26e0a6bac6a1e3480b724fb4cbc754d0b9a759190102020b4708add8d74de71c21fccb2128c23a2b2a3b1882c5b0068398184beda1f77436a6df1205f47c4b3250ca8b101dfd712b65da1f92512810231886d48e0016ee131023613345c2a91a97e3dba5a5b4b7eadd499f06a4a058e05f77d59ef31e41481bfb43498293206d446ff7c1746ef15db769128a82b859010e640d4715bfd2d9eec5bc7dc150bd84ab1868a8bf0d7f645ca938a4a10b28def7f25926b8b179f51e237696014206cf2f014e308fa821ef582cd22e267175622961a3960b05729bd734bb36670a337fd088243f0bcdef3e2edca73ad992c202383516b876f9a345463c0719dd03bcbaa1fb737df02a75e2de45889669dbfe8c045d99f1545df04c4ad8801aaa12088faffd4b184b93503909d97c5f2c3152918eea23caa3775a736b70fb36a8c902ec2fb2f18f28d71d4a5fba57db128dbd270491e27c3427d0d6bd89e60acaab3cd0d23f9970ed67f385b91966601f4b09936836b5080403aa657d71cf6804b5a547a22aa50da397cf3d2ab5ec65790bf07201c57c67cf8ccd88a66e101c64efc48d265177da4ff041c43e1590020e6a303007d2e588e4e4ee39a6997cc48f2fc22a9557e697481a3ff9284ba1efae2ea2c59446141f73262e2f4d0243e0f2bbe69b227b4784bbbc0074edd43dc819e166dbb4c180bdc7bd1739e945a5f6a258def9f71809e6bafd9d7a25c8a59b8d0dcde4a352fbd6693426bab8be1646fee6bbf1b92bec97da802e853e37763ebcbab1d97a66664e716330c16c871cb2557b5beee55c6a747e8f43de39d2f9d57ef7b97ecfb7cae50a64733473ba3381fbaee69e6866440d4152b5a00ee5870f51b7884c918a8b2618bc3040285bd780f4ac41e059bc1524971ff8e20917e3c5152744900190bbe27f869124cabb908c4cee7e7f56279f0b56ba8ad8c25a6324a30810a8210816391ea92f9665cf2482e3d5cc62d1c1e227367db2cd8910324425027062b17448400a200a1a0d62a89a8433263648444d1e0f622719f581baccac9722d08913c973a27069695708cadbc021eb30de2a1452d105bd4dea9169971b8445d1b6339cef73d6c6abe3d51c89dc33bc944a4296e2a23825d6a363ae6c768a37404736db15f8c3abcc3759a5639f5daea2a7073b0f3648eaa40f46d4b3c5e36697709fa3e094a22fce8da1467c5be5273c6ad7b01289a17f41b587463bb703123e135a99811f617cdc92c962800434b5c043e0dea0d40cbf8824225205c13a51430f2ecf5f480b1f708f290024f36d4d28f653d95a0f6bd09e04573fd1584b345f0057e9806f58d6457e997c035321e97f0831300f89bfa209a4dac1fd238c3212e203c549e86e3f0f05c587661fd65b9ea6a95b1964a0bc27713644e0d79897d63bf4ef9437e89e8030a859edd6c1ef5037fb2823f899cad9cff57038fca6a4b423a3e140341e0345e74a2f7d984179dc9f9c75b3cd09ee01aba7469e354677f35cb0a90cc10197ecef886f33a6e50efd606576e3ae16e1d590fdfde4b944091b83735913fe1ad1c476edad99301b595abfa355e28de13b6e12229e4b4a7e1376834bc89f832ea7ba79590ee3228d68152c5d232defbca89ebab83f87623ac139e22068888813c56eb686a0713397ba1b5708fefc5441cfb7ab898a73c9ada08505bb202ea082a7085a2a8bf20c584b4874dc6a23418326127aa42a4cf821b8e12dcb6b124ce8e81e68990f42b66589d57a98adee14f576c0540b06795822f679428d643423454024641da9b430fdf89b1c28be8042203ed47806fcc761430c4cf5427daa9539afc3d3f2c4dd2449d42aa9a13973a23e76995110d9605b293d91ed7f254771f65ac365dd6f92c80e5af3ea2014e2fa099b0cad333c70f982ceffcbdc8db52bc10d07e41240d205d0b748006fae5943821eab802db9ed396339bf8d01e709adecdddbcfc2b6f19f142fe2c3c82e49b915f54072828e150d86d26d9c8bc5aabfc34e669873d1a939f0d6c74313c4d455006384bfefd4fe5a4288c7169dad54b4df08c11c1b5200a5621f21f145dd214cd56025e85f63eb675e6a8ec25077181f5a5d6aec3e7cdbcab2083b765e69bdb301781fabb563e37702effbf28d0fd6bc45f588f50396f7ead1cf21337ded70022d835f690b830848bfbdb4cc9e4d150b30925a57912d04e4f103ae4108cc1ce96cad9dbcc1a4d426a109aaa3181cff1245f9334e98547a3f1b66a8420a490313cbc4df5d43ab2fbb811d95c1769cb4b3117eaee02d6622c265d5fc6c579d827f5c8fc61c651b1223e62f81c0fe586fdcac906ee784087f444f1350d71cd327e2d46798f370b63d4efc86bbbd3c0640ef5bf0a3cd14cca9eeab4714b30c2e97f67e5148a8c9c04ab51320a03854465d280ee3e168fe9d657989a4d481583706f6e49a275c700b0ba3cd7a5dea7e66f2e7b340322a5d17091c8752920e6794a26c46498a01203fae00c80d0d0e0f192a004b51130911732e9d8e802d9e54a8ac3c3da5e5ef01831a3bf0626de16d6ed8e63ad7ce0cc9c6fe0e7e748a72b6ff18597e060684f7952b1317c2f59903d2dfc0301c628a6e5540212b1c4855c3ad77a222f780bb9fa21c7aee4bef1a2427030501c1cc9f5e751b40e5fe8ae37fc5a10ecb71815282112c50f15e01354e1b8feb025a3815f74f0be65486bb7e903f52bbfb444f5e4fe1dfee2b7cb4a3af5f00de0299c856329b01e888238e292d5497a13950d1a5b7a5676c01ffd0ebcdf6d82e202d9508a3f1b10cd39dda3938790247caa0429aaaf7a62dcf34d4db8153720fba38197ad9a17e936b55ecc48d0f2e54c264bacc67a489346e2606cbca5937c5abc14d042ff670626d3602d7ffd163b12abcd296ef8faba71f5471cb1e39e76c261459844cfdb8a41ab3154b760ac86151d612c84d037708bbc3989c8fc239253254c813404e75b147c7e0b0b2be5cc1cbd6f27560ceeb0417e7311259e14b4df74c1ddfcbec237ab6f4287cf60108dd666a193dde9a7bb6441eba5aa47c03be76ca6097a38eb0af46882800495194d5d380e2275963ea5c6e6aabf5a304104b04968183d1d88988e9b31557be19d5dac0283a3467f41834b1e32dc6e3808d593ddb7db3a5f1437b53cbd8a7aa78eca0dd527f3b3640dd8f24bdcce7b6a4bd3df4316fd423c1214310c06aca7dc6e7047d9031ba25daf62f07e23b25ba31403d5bc1262c90d5b1db772dba3adf73496cb5e07a746328605dbb8816f5f3b62ed85ba02bd9d9eac0ca6311a70557817da1e8d92b1fa986de0d352c7b29473a8b7404bff011ab4a706655fe29cce655d92ec4b6a3f1ee2119b771d9a1c4428e660f64fcfb4bdf7b9f3613fdb239827477bec69df75295b0984c661c393a8dd71115edc47310310c430a48b4907f1d41a4ecc7a0b1604b9b21dfca9612793a9189177b9f226776792789760a1f7042937e7fb18634b29124da5c7e4abd5ecc8040637364193fc92836b6c8f6d9d546ce77b3897d6c18bd72142d375a2208e783cd0182f2831c6fe90f10395388ca61c7b7b2e84200e204a21f26c166c2e37dfc1058ce2f39b1e25e6c0791ba879150fbf2ff6fe27f8707538815bc5a453ee9918f057e41412fb6823c5c1e1b9adbca62edbee735bfa9a3e600e81513786559d1086e33a5a358ac834ff7f2f8d4d3d25cd56ac02febd4a43196adeea8a9bb53678c6ad38078f47e1217090ab8ba42b0a084a654c7fe7139d2fc66b3aa98deeeed0c3f798fd39daeff78f16952e50e8b68f65ea84594da1fce734089146dd62f0cfa0613fb07053abbffecf0a8bff76cec4208094feed04704be96da8796e60e9b0d4fb28ad6d6f09c434769f4ed30fcc4a2c80dd39d69d603023e00b93c7fc6abaa0d5bbed4d84307d332c6a9dc217f61995c66c6d4597339b1d07178828608d2518d4170d9ac098cddaebb7b0d72da7661fe0821b62f545eab9cf91e3add1b0c382f0003e950c012e4283b735698b46d5c95c09d6f55446bffaec00b93a15c205ceab8df8b5aa6f4306d16a5ad3ea0a0659f809e1f45fa519f1703f665c8f1b8fdfb221bf546313709460663ccf21da287bc68abb78324902ce57bbb5e40a97d70f2cf4391a5ddf8ba9c652b30646a25a05289c5b242ad63063468d36ddf82415fbc2396240e61aaae249711f3310fae08958181fd73bf6600d1c5a3e4266f10d89e1ad948b755f0d2e695f770e343316e0f85364f0fb250b29977313707cfe0c01e7fa6cac61378df044d8750a6b9d5339fd4118fb16e21a404eee90f0c1236e8193fed2af6f5c65d8bea6d79ecbeb602fd0bc8ad59147dfac2d4ecd0c3e73a4cce62c589f3f7d682a871932ccdca631ad0e9334f87ee491bb2ec09d60a78c4d4036c30c97d4dda79be5de426750c4dc17e37be64f7ef15db45d2a93106f97c598b77ab321eeda01a5978e8b47d868b197e860a5a7d49eedec9b2f78680e1c93cfd6af3e08f4360d90d2d0b946dc955a8e8801094ff737ab6776d9206a1d070a684d3d6e5471db15a47c6c9b989c79a378077bb96d9e2e16ab32a714263d36f57f0822b131a565fcbbd92689795485bbfeb92301f7dbd71bfcc337bacb8faa5103084c2344bbd42ff9a7a9d7752ee08eac01b07124616312fc49072f3cbbf0d98ccbe694bf42a07323e16fb4782c0653824852cd2cabc93b8908744e111e93f73a166ae2d79241041f3d9d724d4b6c57a5c1f55a01793a76f1bf3a2612f1bb66f5777cbce26580b9025307d049d56a4ca45b40a148a863565670174fc2e46f01865a1bb1c1efc5c4ee6f3636f0f04d310beca36861267017b5815fa185cdc0210b34dd6dcb38036112cc341bda1e4c82e707715c594f4a3593cc520fc30a0b691c144d5cd4868e31ea834957ffa58cc3b9289f24097ef3db99a4074ccd8de0c5cfe8105abc5c83fd3f529f54ba82110c85d6fe4f7c0cf396d0c0505eae54fae778dc1e0255ff768face03c5c5439152b79509f416961d30ef5390da12ba2a2b9f406f3f5171b2354592036291007768eb1d1e617a611ff79c42b9e9a73ef103b0df66c84fd3643c90732dd21932d04ab29e1af0bee56bfe8ceb993771ccbb1c040fafba882dfb30a0db4e0128e9fee852cd3578833d77a923b21bea87c7ed9cccfc34a7caac753dbd89e7dbdf26fa5795263b227187301e21ff4cb7eb52c8cb7fa0428538f4cc838d116b0293e1665ba76990e3d58f0aa600e25e8f6200dddadf1c1a886a7dc75fd62bb48ad4e4bb47aef57b950a6d391ae7990fcd250b76229293ae8a9233842dc059a837710bcc93592f7bc95429a033324ac7c8e2e087a2f3633390ae6af12536a73df3eeb30027bedd95384d10920b404371c214802acb7eb84b94488c18698756e43902f0d4738357d8d8b0f32bc744a0a4a234b044fc036821e495fc17027bd6680d454f68de1d191dcca80b4290f0a2fa6ea0bd8dffb88646ecd4b8d39c47072694c6b05d05327d995a62ed5a61f5755a6051273b69c20cd4fae447be0f5337c2927db694427b28fbfaa5f4a3155e742d728a26071abc4030d864a1b522b0d0e463b40e5cf404d86041d342af6beb57dc38d4e7ccb3662c5f52d03ef24769e0aea68632b68d335ebaadf362db4d1bc01189147bceffc9237848f1b1e3543eca0269e9adb3ec7e8042ae540995722d946968e3715d67ba93c0d4e0bba8961e547af914c338fbedc3320e185b8aa5b401236260cd7b32ba7ad2b88fd437ac2c712fb5ae3d956b8b3950175b6a3098b97dfe75312cdd723fa30055579717aad30a05fd6556901cfad59d37cb093363f8c69705c8870ec79339c8f82525bcdcb75b383b54c3fa1f3ca504100a3c3b15e9a0f014b93e4a87db4b510c84a3dbbaec04449f117e6bb37200d0056376e89b3797d81bd3337485a5d59c4c8457d9ca7d366866e44911ac5dbe7ec5ac58c776244ab9071c88e5fb6f390bc644987b61c537631085f10039635d0be8d4a1f88e826d555b64ae1c7c23691aa338f96070e5fe297d7117eb8a44b44e7cc3da060f810828126ff3f6e0a4643f80eae3c97278414268241ca2b42c95a57acc68884b21eb72d15db3943770e500974ee787874bba52dfa8ef7695756d5a89dcc3b2e11bb974601078d8cf731cc0e08364ac3d2196037c1dd817704b5fc9590b23210b07ba259ea0b40c7326d69c90f8c93e698c7ccf0fae5e40acdf0fdb3b30918e250a230343ae04960fed7b8feee06f69add5f28ab2f123d7800ed3f89677e13d4fd067c82469f9f1c9c0e3044dc6ed58a0c121f20e82ec0c1805554091f63df2f34242bf031809f2be308b04d15cf1512068a3ecd108c6b489ea6a93528c7df765cea5cca263a02fa57a9e5fc6080476d7f21591e123f8d4edfd1ca52f1ffd91c51a10e3135cdbc3f72d27a87500301629c2dea22edaca1da655fe56d5418593519b32084f53c090b503fe87438ed4710dad429d55805cd6ed8fd3ecc9e3423bdbf4df06d8ab6fc5c4547df9f0050f3c52111f48bebf3a3835ae962f5395a3814eec10f9d0d1a2841ff83e9b7e5ccc28a9944e40773deed13564e434b12f5ece04c7e7416327f82f0c984707d8371b2f6a84e8aa95e2c33e75f41103167d93a03d07c94ab37c7bf92911e406166b7cd0756caa1b8f59a0a32ad956468b8535ee0fdd594b91d0e8f97a898fe4e98a3ea5f6835be92b49b51a93cb9861d24f0367d66f7f72ad418122621f0e6e59995827b74a2dddd563db1e2e3a15ea94304baf832d5e8c2f78f364abca40395a4f02b9c9067d3eadd950f4e574256a679d7df62b1a5dd3a10fccfdf76937dae903ca255cced5814d0e57a64b5455890aad1faa255a537c427b3a379b3f75b1ed11866514148c4754ba3e3ce6f3f25cd37cfe49e1c513023fa490812b691885b457e5a9f19039e0d9cbd4252760ad7ec42ee88e3cc11202f1302e0f689b403448ea8942328b888738b13d5869158abf07e07a2782ce5be339582558611653d8c367a54b531d4ef8416171d02502fec0a1016d5d7c28c5babd4f383a2f30f5ed8d54cad9877aba420a6f1e85ceea3b9456970f0012ca2ed4d26dfaffc33967b15f4ec9e242f071f4e832c4955dda1e259129f1fc86c5375280e0c990521a0f0d3fdc4ad20c5f93528d9dbd602777e8060ec0833754334e5553803bfd400fe1df8b87fa99f4b6bc9d64e6945792a7445ef0b95ba813d78d5b6000bda1d44c79b9a788012880aa1f50debd13ee99a133861e02cbc3a0e23f3b3d3199f7f01b2c9802a31b4e702e357d834fcaa34f3cd5be0e461d8d4c4c77f5e0288f272ffd0aaf91bb9049d0b4dbae5c9c9f16fd68125d52d073023a4a5225d14724c091abf101a02c748fcfbbb0045ec32c1aa07528b6e92e5dfb474f8f8471a5f9afae34e44b4650853d56df1660bbaf478c97d9753e27d730f0b34c1ee651ad0f3e75f6a88a92595a4a4ac8887248f1222b228f49d89a56335a649fdba38dff095e676af22bb01c2507ad9400c80c9bb901d4df1a1cf3cb58f71985252988f0ed6ccf54e135dd00653a2092c34d43b08029222c8979561f0aacd873505716635969db7744be8ece43dd34345dc432c628310f8edaa62bc98fb666e3d789ea461d53009b94ab2a1c0caa8f3991b25cf66a8a0b673d80dfcc2aca2efb6eef3456e2b37b0dccf0242b0842d5fc8f150a937ae2385f1f6916f984c250100368f4ab0496effeb587458ff8a72e942e689c917f3d65366846fa4effa4f0b207680fb4f596faa511c6414eeca1303f92d873fca2168dc7da8d0bc5dcd4741b5afce451c907e3b9f2ea13e53e85b9c0e994bbddb7de06a912fe19432463804d520b967b86e516a19c155b5118664c793a85e1a6916dd31ed93b2c99a55f281a88b35f26ab613252b73806d882ac68b7b1d12dd6e644111e4da590a59e64a86f54d0f92fd06065de8c3104c3874d135cfb2c2358c82ed28eecdaaa2b4a903bffc95e7c90cb4cde8def3ac4f4d638ae2e635da49bb25ce660c50f980090ab854c7823971f60714fd5c8e8f2b5ce11e09507083e3110fa513f0a3aca5d1453fc22331a09cb09a17d57011fcd6269f58e53a5046c30977e21e99d031ae10700d01c301d93aca405f98356823b19ab9301c2ad0a7845ff95ecae6d944dabbf200f3fa7fc61f46384ccc620449bd2d48fcc740a4dd6546b668818a2ef05939d6f7bc2770559df9a33e9c3f4971888992309639115f638b2bc5c421dcfd03fbe07553a39fdf48690c6d15ce19fe0978ac1ad1012db8d1330ecb10931b7dadcad963afee1c74f64cbc6b639f763ac357af0c53565affa8f3518b4f2e340cd1a886af4f49a2dd6da50b0923a3ba83d89e3cf7f8c458b9d79ac2f034c712bc9583afdc760d54586dbf17520f1fe55e8017385d9dc3e9b888248a048b01bc4049b8fbeb239e6186b5086134f7f9e1ffce755ee5aa2909a632c3af63eb8246aeb996065ed3cd313de9f46a4cdf1d0e29b95be827a3027ee0431ddfbb787f34197348af03d365cfe356cdace8136de85efbba0e322f6d2171f33b07f1a1abb7a9fa4678bdb7df2dff50050d85c13da2749671f51c97ecfd6bdddb0d7f5c816bf531126071c1e16481961e8e312bc31f1f43fe170919008939ac269722f3a5be4bb083cd772eaa2706ebbf6448ad55d4701aa64169a1f14ad8024b9d0dcc63906e4c08e7df444f93077ff4b3cfcdfd685e193dfbd22c40c4dfa9ea58d6e3eaf8cf6c76b9d197d8a8b16d5ba638b642f52bfa620b6d2bb07363bb04ac3f61afad99a04631b8c9240e49e5b5f267642bd3ea3306521b48d988ffa4924da59f443cc1a033cd735756daf8b2b6e3d765b91405f17a25ca1ca0f61a3a5afce53c4b04884766a0df4ae36febec89e60886ef3ba2ceb89fad233376934840fab44d2d1b7a46b17db056ed6da7932a8058da838936bf462cfd8fe430eb890e58b2e3028d74ea68639f22bbc9541ffc7a2f1d0c5106e2f6666f8c9a78134085dbdeace37a799447f2c5068d785d6c1640fc2628b0697bdb3f9a75bf8fd66deab9538bb6e222423db7db4a941e8cc4ea8c22e09e213f9b8bf6a32e504a6c84d9fde3c8e86b6aa3e7d80a8bfa60e6ec80064bb669d78111010ab5e55a5f3a6803c03229f3f5059a636e8572ba2dbb0d3b3454ea035dd4393b6dcde8ae5ab3ad189e311bce02d8c36ccd71c4c1313adb16b62bce5a5ca0677da411d2e0c92ba3215294db15b8f9231f636f38e134e517b0a75ee4d48b5b693f606266db2998e28c4b86f8a2bb0831d3493a846ee31944adfc2b80af2a69bce00126b4f48aa2976eb55f4038087461730b685be3904af2e42c1004470974b914020e0450e30ef298cfb3dff44bf4f7633ac2370966cfc626a8c400aa28bb24536ffbb51c028ab2b9bde92267b3a285c8fe712f3e292bc8cb8505fdaa674b43924c3921105b8949a642cf9ddd2a3b3a4c7cbd1a87fa47f87dc297a710d55269153bd6622810027c95e5fe8d4c9194e44f10c305b8bbfd4ad8ff02cf890228937cc9705ba528c4b229154021c88892f769f2a6f3202d1eabf32c12eaf6f413e63fcd557dcf699b5466dad9eac334d0d11008ab24de9d31bbd7f247a70e21018cdc081e697ae603875928df7cc01bb73892897e7cbfc32bd4c2ed3cff475364c086eb747566eef6d7964b8500954d3634007e9fdc9e3490add81c56738ae9ffa4605fd1e2bcb7b018d2860c84dcda005435fa3411a6aa1ffe4331223d835e6b83883a844f50fb29ddf989a47ce30b4c9b56a056e038eb27701db65c9daa9babd86233e7b88e817d9334f419bdd8439ecba71861049c4dd769a56349e883e0810cebf6236361faae9682f528bff3494a9cb47c78fab112db0ba207e9004838a57191a75457ef66630678fa39256567b35cad3e608b07a86208ded8a21de342f1cd31b17c4f5b9a7f659cb8feb7eddca5a64d66312bcc9b60b30a3e0e8679c10b2d3f380285c9b63610be3cd26924f1a159cd5852269962126ffd56aa3d6e01c08dbc5dbee58ee467e672639e053397dd9778881dad5ce756bcb8ed905e703fe6f6b4c5d186bf7f3772c584499b7ea4716c82b1a8a413a30d0306927eda90d350cc7beac0cd49baf1c2dcba141d19d198a322a7d5b50d0920848fcae76c4589745723d7f01f9bb18d752462eff3025d38367a30c06c63ba81aabde36d5d261523b67d4824c80faa5ac92a32c0eb421f6782d60e8330dee0d60a19740ebe046ca21a8fe589ebd0de67a20f3e678744154b62e21b011c6876010efd3e956fe3d769c2aa17d76c73c240ca1d74344bbcc0351836b16dd08f69c9ba05e828140613dd5f89011b60c10ff0f42941cdf1820cacd508229954fc9f1a51520846edfca91acf41aba84d24d88285cd8c2b771a8326e61ce534d1a1d8f3e75431141b421407f730eddc63699f44cc3e6c507673e6ed6df0f2553e81784b136dd4de3977e4250889e5d87992e81983f2fb359e743c32891771109a7871184f3051c01f9cf1b6ff8355311fb30f03efa5ed4a0f8caa058e030cc7d45c83b63b2e865e81082de18f0ff39e0e3070dbfd9c8e1e26b68d51fe739e6af96564e7d8810ad81de21e53c27f2d5d4a766cafc3bf6af114a20c138b6bc1cf6d6b11d0d0ca80bd152e04f9534bd2075b5a95789af8be1e70ea3dfadd91f5b1527e399f3fd798691454fc3a85a6190391d041dc7c1d6a08d1c569854dd447596d753fe904162237ecffc7d9db997d8829da6c56ab8054f198c921c5744c586a0d4fd45a53f46c5ccba1262fcadaed8c07d22183b9809a205005f54f285f281c251e500c08169e8cea853546243994e9168745905465f939466073a3eb27f78f2cd7ba2dd5a1f8817c094991d7fa419aee7dd1a386fe09ede6824d0befe0dda93350c44eaba78d904bd7ef4d200bc393c4cf470073f47f15cf2a7a05c38d4238e8535131a25e1bd9ee5b4941113943c1b28c45d38dd3e7d04103d9c01584479deb58475b5b879cf69e3bc305c97c8c4584b83b6bc517fbc1be0d6d7f68b9edb3f0c9bca0237146d49b90caa8205d09a09e4891e2a66a5186781750c3b5e001fc751103c57485cc6788b51459d06a454b8e15a447701ea19c7604da250fc4175a321ad3c74cacd7bfc22ab1410106edc73ccba0fec2b626bd4f76a375c86beb81a4df4d2bc35c5b24481c1e8249b7723912985602b9a7c26d927dc1f5233e3ef52712126dcbdcd088d0c5e24e70c26c9fb36aca0dac8a3cb2359701a9888b143b587d70671ed17f643cfc60c763f48171d6b4074afbf2c97cd74ad8e2f7729dd1091c76133b00aaae3b95fc9054ad89d7f7c11e6683bba9fac4869970a90717f7f1d59d0eaf487b1a91758c6d128ab93b69a1f202d57251a40482e7dfc00f3670b5004f5f152d2ba0844595b8fe289c9c156371c0ac1e53b24bdbdd3aea767d57faf5e4d9d9e3eeebd9ddeb659fecf397cdf63fa6f051da07edb9af96ee6042b9e3f49fee26fe9089f226ca872a85df374a4f96ab077dbb862b587d3830cb84913483ce1b275c0eab718141cf516ed5a5d8af9d19f30ed3b8ebd2d236a292b0f7bfa50cd02816089a4210bbeaa58e9216d56feda74b63d9c11f1c3bf1b42f34fcbd774e393615170c0c0d72fcfc2e99302f706287e9d769ddc78fdfa5e7e8d8c242274bc64094352d1082ff7d2d428f4ade5e39f870f5e6a2a3bf1f88722f8ce6bd867707a0b6a1ba6f653f6ed0a324f2aaf1d45e8e0ea76773a4db6624fdf310d7cf0a4c8b1757e0133e390d87fcf0a034db6a1aa943ae0ef4c16d201fabdc5991c8c0451ab1745a605088f48369892c6e892e092ffc38f4d7c00a5e53456e5bf2ce5697efd6557fd2689b7c622703db4c17f724fecdf504c603ed83a125687216c00c91cf7b975ee24d2124f87a8a7a4401ddf8a114b2358dbdfdca32e9bdc56bbfd9c836116a4ee567b83eb3a91e88dd224e11180ed4a3b95e3912a13112c2c2c979ae8f3299ec0cfeff74a6c0ff82972624f22832aab47ad6e12c69d0cf019e788cdaa659f4f71560f10eb8739ad99d0271496841d8625d548f14195e23d6571a11eff5233a4d0ecc2f2840a7c493e11f73e6d974dcf0c1c170eb62c85f6b529e1f3321ab53cc29891c194e7ab7140b8a5eabe40ef9195e72819490fe0834013f14b59f87ca29d5040a21bddb2b36797538335255a635f2bd70c75357300ba137b1d358f06de8b4546dcb1a6d13f758ca802872a02c9ac4106f492ffd5056e96810c48335350948ea4ff7ccd724810038b992d2674e62c779526f2b8e9924dc8fffa7501bb0113cfea5f626f62d79cef4826464ac75ee310130cd32ca0e6c76f06940d7fcf9073ffd9d3b4e9405abdb8bcc8539d05434aa16c7401b1dde881e17b9a4a6b9490d05984a480444289397ca95fe65762a4ca71711065477d4b38ae885df7090a234d87c838cdedafd1eefe38c8ad13fd7b5471d0b47dbc2513fcd55d0a6d19de69f0421282e46d91830423d843258602221a7bc990dc21c0afc7e9c7d5b156714399d583e843611bed90cd9388fa1592e4b06e503d437b185b9eb91ba07b0da972887999191cc515625357b969f5ef6c11451d8c9f0191c63604a74408c8635b9b6fa6a095332ec9c307b3aee8d84002819fbcffbd8ec0f3a3b1a585c7abaf8329132bc43c031a1dce6b5c40d619a7031ae9ba9584b180e3d43e9aa9daa9259fb1537f7500a1c50eb45647d4227e41093298248b4d7937b7a8e6c8ea26402ade1dc8322e5c83a42b1e6e18e70f42ec2cab31a608849b9487075cd1ff908ff66e6b5b4a29534a29a5068406d2062bd404294ed4156a0207ead37810074a6afc21a12035763fba5ac054bf1c542a55fd61aafc99c3672e9e34feacfbe24fd8d500bfabfce2cfae144fa620a8fc1ae07bad213e6daf69c9948328b502b4de10232b923273c47abbc315f84a801df4b1385db9c301d67e1f8bc3fb4209b4476a2b0dda4ef5d30bfefd3735dd51a9bc20d0d845610b7733775d36026d5bf6068fe6dbf0fcf907cc20853226baf9d836f8a3bd6ea13f4606627c805251a4c48de7b96abf0d8ffd7bd783300a5a53430593dacf428e18806a5ad5b11ac3233e64e678dae1281befa86f814c9ff72a1b315f92c14caf170f514665b2d7e643bf8e998ca05ff692e2ea171ea03136c5dee887bd3ae6369bd3450e54fbb397abbd8ef54008842655b551fdef02fac2088daff6fabbe88b302a82d4fe48859a686884794ef471d48a13bd49d515b428cbb205e2673ff59001fdb22ccb3a3ee24de1b26162a06ea15fc60594b6d71fbbcd03f225de67324df1a4f6cb6c5632b18a08ff20bc7258908463648101fd4c3afbf0ebd77e198f3d66f6161e3278d2ded6cf3b6d7d08618d4ce4b6b3213fd352fe7e53a9159b5fa23f968403fa799f6782416c740bed7538c4fa0356e7ca2799af3b0424469084a552a97872f69740d73d473d909b18f0f5df649a8248ed973141eff36a4a330b29b1d8d73197e4591f1f2515090f333333474752fba1bbf7b4d0afcd28519b67d01c1304fa491e67b9cde83be3894531b71975f18a45f5673c643ed4ee18ac7ececa62108d0c502881606728e86156ffc498f4780a3ca0419deae9e9496dc73ccda4dba75b88a104fdba87c787bb87081f9f2449f6467ff7b40f13e8c72c1e47e24978078b9e1aa309fdb2ca5bdc81168488f8423e47fed2f432b30cdcedc378b2d15e0bfd2b4828771f83c7746b362304fd32ce90d4fecdab79c18bf1a29fc946a33af3528842bd854b54a61c62abdaaf434665b222e8eeeeee2e6594534e39a5ec5ac8628040bf8c157fd0e42fcd1763158b46f5772efad11817fdba483e6c1a3f4a63b5bfd445aeea22aa04fa511811733a5beca0a0fad16eb5b6fbdaf5d17efd54193c4776d187b3827ef1058bd91663b188a5261aea2d53b7d0ef271b51d02aa323d08fb6dac5ccbb0b574ad57fe1c2850b77f9b41dbb584a0fe87ed659900d3904d918840fa5b417859f6347689cb3343ca01faf6a671faf3af5f5aa573eb5bf89f0918f573180687fabea97092d4952f79b6709acdabfc325b48455ed578014b1ad4e1eeab7d56f838210017f693c9aa70a68c1ff5b40bd5b274138529bc74fab5342555af547d70ac025b5f038e2090c5c79fcf0f881d2a97e3da95b1b9084a853fbc4aaa76ee5f103c53b1e3f50eacb552043e774e4a9fd3a58b86fe02daa14acba42523ca95d5d212978eafb7ba5271b48183c2cf2b4c34ef5d130ff8d5de02a85b6345ffb50f195a9a84d398a0a2380b07360f77599a6a25b778875148c13b40f4b880945425578a828aa3fbc328e10139650b9a38b732363b5698b166baf990a5369afbb8fe4deb12059d5151f1eabfc2bdc0c6b39ed9801d603604261651569716b396d6605d52187f67a4256b53c3fffb04d36c52b6679e69567e67fc088190ae3e867af5545d4b567e11f56cd918a3609a5bbe7ba72da2a7782758bf15f2209d01084f6f0d19ecc7e3ca7c39d0d3dd4a51035f9e7c0a792d396538e18b6f6805a4e353735b1e5f45e1534fbbee5444f5d159ab50775e04629635a16413d40b480851e9e26c9373314c700f30b1da752ef4909522a7d5132d1f6bc06331de8b6b79b3bd315b44d45b4db01eddfcfc73ea9fccb1d034dfb34d8c1f660d0b653be4c9490a402a0ae10159eaaf906659ceeeefe0169edd5f0f3b15848c838e0bb26c5b52938f4bb0e1d3a40267b0ca6f061717777a2042cbeb0a9c31a4570125eeabc356040f7d979b8f5f1139597872a0efcc4880ae7013200a21acc08a1fbe2b4e464c5c98a931533436686cc0c392d991932336466c8cc90992133436ecc0ca133436686cc0ce92428733b8cb061f66d3b22c10d723f1b4b34264e0984f0bf08a10c84bd10b6172becf666162f96bc904136777ba13b12f1dd612cd1d28bbb9bb818bce8e9fe820997dcb923361bad1cc218e5fc9973ce39e74c325b30a39c997b0bb29111941767495a8bccd3947c94342d6e72ba60dbe27331c6281d4ea6a166194b295bb022bf8eb815e75ac0390bb5410e03ac8d06a36bdd0e275a3b8ea0271e33c06ec78b18b0d016172dc7015800e4f2a26ddbe94f26d38817a704d3c2324fe3a2c425a0865229beb8c07038e8c0cf5c6899a37c482f9c8e9760005261882e5c7038e8907e3e0acc4cca46bf52d251c682c82c2b2898023b8c3eeb76188d3efb11933efbacdb217b5297e570769a3c8e82117ecc94d127cab0204481940a218410420863f4e9d43777a212c8e336f3c55dc1aaf1556002212c45c89179e23ef7b096f1f39c3c0a621672420b0820216c424e78c294bc62229df22435f6f0d1369ea3e2c7d9718fe74cea4d8d7c2a558db712236954641e940be8b7b0ba5a5521b2f2866d91e4eebbf95f2039b2fbbc5a74c5e2641b6fedc58cca5349a6877e266f5681c994325312d90728cbba793ac5489887c7477bf149a76d4f87c60df6ca03c9994fdda6df9fc7f62b5bb77d777467d8a6813bf968542c6d5b0f168a40791607d6f86c02b95a1cad46988c391c15238da320743bcd00e315e3100a902451c240ae18b3d903283ea1c61efa67709b1c918791fc80cadfda833ea2142a232b264a1644434686927c4046ab56bff8db24b30e3a116d693e173fadd60f27f133c5c54776b8d545ba2b9fb8ca31be8291c04f88626f14316c72943d7bd5457ed885d9fcd3737677d74f2c8993e09f2ed22b153cb1389c0079cce1ca8bb6d93f4a10b994601b9e42c46dac04a37e1ce53f3f4727e9d9f6240cb2e00760517e88ac16a77bb5518e20aaf3c4d53ef58e0f7bc31f7e80551bc62e740816476b400824b870331428cd2e7477302fcd1b02fde20e10bb8a6875d077c353d9f4038daaf62ec09595fd50e61fa2267a66c2ddfcd35de4796714d8c55362d72fc6e1bde2296cd345fac5adf6691314611b968251de2f7681b9f3ee05eb805b3c855d2c45f710b10b5d449fa07988aab34f9b6aa8b71304e3b8df7e8795df9e3bed40048f95df7e07fadc5328e856dae940040ffa2bbf8efa1220806a838f58e553a0d40db1c30c4fbb183e8fc51cfa2b14d6c351eedc6f6f8591bd718ae997bf3dcb6b311b0c02b24e9504d5d726ee2cca27cfd9b399f908c8c3e2581fd8826eb3c5a008f40361ad56dc813147451a5ad00fb24e35f1f7bbb58991048cf2872c762176ec621c91046cd32ae85ca338522d0f12ff98a453fba4055bf1a753db648958f514e47c9149750ea8099007ea17a354ff519408e04de3ed5e91444e50b537a6d81bfead02b6e91a308a875d58f1b0386ae04ec54bad5068cb73425944a9bdadb39c623229f468931e236dda12344302e57e2371326cceece9ff53760e83a169d080c29a44a553f23dc6186565e35f2052ca51dd4e496e5b696d651bb5701d8cb90cf4577e3bc574c783762ca718aee395a3fcb72ee318d0fd5b10746609fac59d2b903822c51026980115b11cff1616d02fee4c98a2ea0fd41e93f63ceeb038a140b33dd6a9c8d35e77f1487b4271e5282134eef84ec3fc9b012ee507da27d9ef2e97d2b086cd6e8f79915781ac956e968d85858bb15bbe703a7860b676d95892a05fdcf9b8b3f259dcf104700dfcac83b7a8f2e7ebd83abbd87d3c79886db575fc03c42409fd1af6936594966225181898394dd4fd575c7030707576bdda1b5dbf96e11452a8d3b4ad3a65849da572c15775872c93c95b79eeba8e7b2a7392179cce660465985701a4c56c0cfb8a7c18f3c4352626663482397bb672ba711450cf2e9078ea267bc35f9ebab537fc6370307095bb9e6182fa6f140ae57e2332c304855ab731a7b32308d71541d410540ff4832cd8aace5a9c9644160b0eb284b8034f2b573127a0fc6229ed793c5018338c61fc72ce39b713d7cddc276ece395b1e832ab8aa35b6e560e851d598999909cab0c589626d620816e571071677fc351a9ba90885d9591c0284606d1ae6a89dc55953117405d56126a8fede10e807599d5af947238b13739bed1d16557fd8729be601822cb771138cbe5ca6200a047fb28c527fcf087187e280729d0e44f0e07efb4e8714369002233caa1caef31cc5792a3ae3847ed163f548a7686441f9487bfe9fc7aaf3b41327963bff66625336d8147ce7b66b1d78b07d45f27877dfc83483d3c1d968341a693468ccb9ed7147831b7d8e962d3c7e90b4b735ee74cab5a8a30e1aa12f759b28515473f476da798a2dba379877dcc5a86f20e20a25452e1a10315a2d8535353554ca1af8bf6eddfa71178380affe5ffdbdcf9fe6dff3241138304509d5ff3b03dae26c7b91d3d10fbd9b49a3ac6a71837b636794559b9b8cb06aa32df3aa91b611247130f46bdba679154f5bad376776667698c321cc6090f6f537a94d6a3f437767ee06aa5946a9c95473e3bb40b50b506be85eef8e3b5d3fae1910259c6c139e92f7a626001e0ac812dba442af4022c3226d71b65dd69ab40b4731eb960da1cf6e2967dccde06e6a73e84f6b37a977b7dd34d2881bf50085f20f0b7585ac084251500d31fe4f5d3733434323a39b399dba6e868646868c193368946864c498799a938c6ec60c0d1a4f06aa86ac116bd480356ad4f0e61a356ad0749b8c193c63c68c0c9a193268783266cca041c3f350a81a3500408386e7a150356a000000366cdcb8515383b251c3c60a33b6c0d13858e7469be8e6834f2c45a0dce108800006c09365c616f4dbea6e775a6185154a2bacf0b2824b0b6559e15660147f573f1c2a770b28c22c3a68208c4900fcf8b0abfca3b35ee65f1d8054f8637db979797777797bb9096d6978593008c469d4e51640aa0e0408cbb5cc41f8a3a8ee10eba7f6d3ba43ac9eda8115730bd84e2f7f9c863800ab3b04844fed37d51d0262a76abe3990dd16537b0c0357d8de165b6ca14585694bf3693b840cb73b84314a39797797b30c05231249d3b68d721cefee32333333cb364304ba6ba29477b7e4dab6b8b8703a2c49a0dbbdbc30f3be944accbba50cc60c142acc807e5f1f88a651df25efbebd9921d0d75e9c15434576726024074aa648d162033e8b031f3050377630f0bb5453c5a28956eb89234fb89e2092056188c891a12a2d2028988c860cfd70e489941756423ca7879943440554402a95cac8aa871c26409165d9cb4e072ffb6872547fd49976aa06ff258a0bd85a3caee99bfe03ece8176a43e76f2b75a7f85f1e1b52a2a2a9455092453104130b9d82b1ff866136f3d1546f6668e876bcd07d1b94104228b76d93f0470521847ee3363d6e76fcec684f3819628f133b3b3b719b1b0ba451cb51fef05bda7273cf7519d78d62ab8753cdb647f7b0a908ac47437bcc517ab8cddc591a3a357d3a029b9a464c097394119b9a4eac4fa76650cf1fb7c968fc19c5d1488e648c31caade31d194992441a914692a5cb27ed0575ca6bcf894d01603dff1b00701218ac60e76327d597546771117bc3b78e5923093392c6d9c62cb7e91c7fde619fda7504b3f79faf4934619d224999653de3a2bd83bb981250cd03a2df6c91be7fbebe49c49c650d5f3b494ea749249e93946ddb0aa733fa154e076e23e95d41bfa982d5a922d2a52aab4898fb943059549d7758a250fe245168771fa744f571fca46f1d52b71ca0464e2d1bb376717ac645bbb2a8d08c456216d7d3a9d1fb73499af3e9d454c2fd744a32e954a6699a16a3b427e164173e01708dbadfb1ffba0db31645331436fe6f466aaaba29c47a2f363585d85412aaaa53cc9a3b9d92305879aaaac3d98210ced66c2dce1328b416676d50fd4f46a0f0358f5d276cb6a66b4a99af4934b1c856fe3336ab6444aa7f84cc21a07b8310460e7e9f461576afb0d3b801fd260f87a45badeab3355b527234f9e408c91fc9f9a39306c483c463f4b2d38136377334961372c36de918788c3f473f3b1d4c2308903eeb7420653ca0fdf1e443038a35e4f433159bc150f1c1032f47d0adee900f3e9480404775877c50b9f0404975875656b81882caba43abd80a36a4480592a38f7e14b398c52c8b5954e1e56b26d3322595f9169a17faa202dc381d27458f32a6cc24c3351e7d5181c6d73229b7e55e007d94754308bb4db0b965842d235c39121942243cdbcd3a2abbf528258491635cba05316a79c1efd598a0c6ed304618638c1146eeae8c468a114a0dc038e79c114a23249c31c2491a65d188481a6549c0d99364ce39238449c09e241d0989495a919bb6c242a96c29024ab8694b29f5961728bd085f229d8c7a5c5e9a347a29415fe29d8f6cc421601b81b634de0e258cd2e3588eea9b216a58b1636924f9d16dd429437acd0509300eee64a2271b7cb4f74302ecc204281638357444b283d106e7c9d188c4fd2daca6470f580f79b2c18724c922285313c7624441e520de46cd4d910eb0824c4184779a601e8410e15765863195a10cf4a06aa4511638918782f0a46e78d6d5830c6a74294930e25d154d54c7404ff558d5785681819d2868edca5b85bf525408877800823584c0a1df42055b6c51a14ac84404fdb2f52caae840d112ca228b519190511010241ea84508361da2222d52b0e910413564484811c907f25f561003a2ca154950af2b54050684a822033e5441440374a5ae50152060809098a3b64a99021b434290d4f825212b9aa8b0ee90101e3fe249164f78b63f73709e34fe09bbcfbb1afcbb6e9d1f7cd94106c4ae067fca9ec9642a519d8c0844602145284a952150a915c8ea0d2c503c8148090e4451451b6d8c0cfea37780ff68877eefb6d57599a3a0c951f0d751922787dbec301a7539dea30f134cac179be0fd191ed01313778b9ac5e280bf36daf36a58680fce1df842c31bd2fa937c9465dde6c44e3557b3a753df644d9fb9337926929964fecc27b33599cca01965b254d3480260e5f1d3ea9f2ab7711b7fe8362a47c5262a6c62bd6fb22adcc95a0ffeeeee439811eab74d1c204710ba63b4a3cfde01a4d7ba1d48d101592dacc55928db633210f31988c5e16ae4c43b36a0dfc27ac060310405052dd9dd65d7b28cb3cc336766661bbc3397b2bbbb2c087b98b5738407099320201f25307377c8ccec5b87c3a87eeac4f76f4e04d066a6d0aef26f16a7250d356b276abb0d8e624d9fafb16294a05a11582c2b4f1a3d64e916b5283fe1c0f563d68f285f9df22c8a4b0a8b592d2e4bc060e6c5bdb51c6e2db7913d8eea973ddae439e184bcccfda27ad97951bdac5e88748a5f8cbc2479f979f1a9fd2f3c2f485e98d47e3921f4d82bccbc0265cfb62d6154ff7ff419f56d4e6a3fcfb73551fb373f6d48ec8dfe1815a2f6fae5e2d22208c7c9558c314e4163fdb61f96db6cad4d7befa7b68fc9a3bf9d5c5e2fac172416c76bff8cca5d5eaef65c622e456eb3f2106eff723110d07e7b1e1c3fd731e7689dcbcb25c6ffa27a59b98d0bcce5e552c5a58809f465f512c4850afab9bc629dfab6567db90d7581b94d0c0ca19717941715cd0b41bfdf7e16a767a6a05cbfeda7f68baabd9755f712a43def3e17d78bea45556b5c5a2e515c5c52ea2781aad4feedd5441b6ca3b2c55a9bcb6db427914a32df9e04721b9aecaba99f0745aa64909554d517d58b6ae6f5a2c788d8942a4208a2aae2eeebcbd9ba5a31781683db984c93e7cc66c63964cfcaa153cb417845a479a7f948ed9e5967a700ec0d0138a9fd9f8753fb67aaa0b07e3592a8464e277b679408e83aac878f4ecdcfc13d670a92456d0c3dfaedbbbdeb23313268cf3d0fed398d87f65ba783d6658eca6a16a76b3f89442291d89d3392f62412493b6d165bf1451461d195833b90cb94a521a4882841a03245a542020a2d67f7f5656687ac2a2269b037e827aedae036391cd540312d7a8c30425f761923e55c154d559023b3a727ab8dd49e496abf8c904340d7de19f6463f606f408051fdbd4858287eb477030c5c6d7055a782acc0456c71f606b5b5ef9374b1f7cc1414560ec618a59472ce39b32ccbb26c34ca36180cb220cb7ffce7f5ea55afd8a7ca192ae8486619a723eb3a08e3747015d0919d8e8e675b4f26399df87277ff18fd34aacc5b7b406802fda4eb93546a53696f0a17275f19902ed96aaf67a6a07ce23ac3e2349fba36474d4f3f49e5870eaeded9d02f87d0de239d6a4637b24856a1b10595b1202836fcc8325794025f32266328b46060b1015f7776defd5e277d5fbb9d9dbacd8c19b5770674e6fa517a1377636c8d76b8cd683b428770e35e61777767e760406b40bf9b1a638b6f5cb24200593b332fca0a8af02bd5ec456110cb526508a11654825a104786abb5641640adba4340403ba83bb4048c248df0b0b058c255774889201cd41d9a4151e557102f168f91b6570292b5c71e93edd37ebd8d4e919e5d8d5a79ef56ba18ad6b2aedb9b46d65723a2b5d96555ad23a19cd64aa0c60150f7592b64a09c30a493367f76fc33382cb8e055953c3258055aa0a6bcfbba9ac80612ff40d0c27d0af4018525d07c8416309dda00d0aaa0e397207218410c629e3438eecec7294bb8a6b96209ab0ea0e3551d5187587805eb5656df867513280d5159281aa7e10261483188cd57ef7d22ee9d403e9357792467a2949a4f75324ed109ff4fca4784476ccaa3ca01fffb08b7ff8879930506bc889ea27a849516b29f0801a09e2279d6ab70932632e172a954ac5c390132652886255da86f4fd113aac535348edf7550e279d82b0b494705579fa688dd2de11fa39cc8be04a83a9604ec5635d1739163b5ba833935e2267a8094fed8fd07596f3207ba355d445d9e4e6234390be8bdc8679b8a7e8874a1df6afba4d444054bfaf30b7e9a02cc86141b015acba7c04f4f323f4a342d02eeb52972a2b6e313640f983fcbb3da8363442d3ac542a2181c2ff361d3cec07614b7777773f08a37417c6b286b1c15011980e9aeed39622841d3301fb04a14a9b32c2d94124dad2e4684bc3b4531984409ac602b91d46199a22745f6319692d25e8cda5126973612ec95882252f95bab425e6563e202b2ceb748616b4443338438b12ccc2c0c0c0b860172e5cb898a105dd2a848171181898868181e16da57361c186f772532a31c330bbbc04bd7043f3a52d43c1c4e964fc82c60a1a1333e2b8c4e9304c43938e8e59b3bd4161381d5cb3bd616a183060c080c1304627146a4a6b8a81199581dddd2d633784f206afdeddc0255ada5e369a2294d9c5763452baa180e504f4839496a8c321342098436bfaa460fa6ced906ecccc5c629a2294b91be9c0f062389d1844f4e3d1dccc0c21f5ee8592a590ad0acd504626851454508165fb3f755250eebe8dc6091d0d8d0c192e43860c19289862708c172ac0881123d375de75ddc5089f0ba834ceb6253185684600102020001315000020100a074442d148288e64514f1f14000b74904078523a9408a324c781180521638c3106014080310464a486843a00040afdac54170534a324e7c89d94092059114f62642105869278d1e0b6f7cfcbc2a60dbc09f51c2e0a51668d5cdbdd6cd686d16ec10b97dee541dae91929160c9f1210ba93343d5002b5b9c63a1c88e7d6e54016d153585dd539f4ec6f729345d565384827f6aba6947622e03e96e8cc669e08f4e0337ae3ad5a9b47c3fb1ef024d3315ebfc68d9266162b8f328ed9e0085db81f4f23da9cccae34627d485b1bcec836cca6d6b52b14e3a731ce16c5e690ff2c58ca1bbde99afd3eeb2672e86c5db342e479c88e8a38b8640401ca3ecc055e44c575af83eddcae1147fe59901e0c8aee9c10e34b5f7bd90d8637fbc140d4c87bec85daafa7634fbcc1b1931d87d1a94bfd317fed474d2db2d1ebc72d5ac0d776b44c420e4bf744a8e374310ea73ddd83b7f03c4dd7679fec024bf89ada9deb0e84b9835332f24b243c4da99196ec1f63c08f6089fbf862cc8faa2a3804521684657c40b1a48ab1c9fbe6a1b107808bbcf43ca3dcade7796bbe80fd632f30919cfe4acb020516cc3ece3a10a5768e06f8e107e8074f39ccd8a54121bcc1811cfd793843249d0d9735a24c7806c320d343c701493e724d7bb6ef2148336140ac78f5d929a1b0dab8fe97d145b7ba8a271a871b299f48de5d7b4614ae087d43c48ab1d05b239c2218c9291288d2340df259a2b81b81f55de187f98a06a79f5ef034b283304acbde6160ed634b98919973248d70cf78d75519409e04cbef42158a39854f84ca8c01afd92e38dcfad415eb91a99fb702e8eb995a9303ee601042a531a6169ca7f80e96409014f477c25f38c4117166db0c624bbaafe07e8e77531396f7059cb00623848c71e24ab76af775e0cc49e31f518c3ada3297780c5bb67b1b48cd1e2aa332f6ee1003b60db61369a457f93a2bc4b21187cc473500c81d4f0b3547f157acbf047adc800993d6e6eb6e838d9d8485c27b51d5bc9aa093b46ea923134a981c67f79167b3d195a8620841c30051591973d75a498a3f72b555eb38f88f20f0060d51aaf7264ebbed4c151eb3a03f2ac51a2eec5be1766a1c28505da13d6612a05f5b9a21324ef3c38ccf9021f84c570c1abfcde0bdf63cc6e0fe2a406afb8102f27295c7a5a2480c659849697632162f5006d9f566bd35fcc3eed820044ec2691a4709b687c966e5121f25a37a92129fb96273877477fff51118aec4519f85ab01ee665c17790ec6320b32ad460f761496b91dbba07903cdfc08b4c313c4eb433d519270228e36e4565336c0ae74db81da7159b69265e5b378334a327872b97a9f31047455de1a34e4637db3dd61c5183002eac693d81882a63b577360a83e336298a02c680656df1aaf62c598e591c96ac98fe53546a92f4fa1a9ee9837b08add7280bc8ee6c02eb40c6673ca96e79dfc992142c04d45a09c5f041884c22460225aeca934b2616715926f95f374c0d86207aab0da1cab067e41ff2cfd941f2dc06934ed6a10d043e22af3f8a10c9ade322b810491a14950933d98af908e8a3bff87fd92e193cb223e0bed8d6fb22fbe0c42942a3a5e40228f6070369399e4c23a3d088621ec59f192f51f4284f4c9c9886f5c7e8a177bcb990a2ce86a36393aeb6acc81d67b953b6d4f63b705a38fa7c94d35f67e6fabcdecd64e0663cb064f395abe8c7c53f692f0ae26f151a7b5a9bea64b07a1aef89d89165b2e619c8ef4dc0fba25098be6a1309af3b5d6cc9f47c1a184e923a35a4d2c238f2ecedd5fac1bb97563076fdd6b8dd460e14a6f3475b4b366f926afbfd6e6e66cbcd434552529993e441aababe8dbc125dde62abb474cf09bddeee732356e1d5df883f3310e86b04c8d800b35ce86d7bf732b7035dd10d0c95bc6fae1dd53f240ac40b48a9a1a11a091280414e33765fb7699498352543e6fbc1eeebd9066af74c39088b175c96b337a1ddb87c4ae92acdc62365e493ea92b44b05a569a3687b9f064462518e48fcb91fd38f8c0d97d44787a2f1a7626e2583d0883d0f2c502770859660c4fa1eaf62d1dede1edab2f51b4a0dbe356d13d5a903d354b0d808b2abf09c3b6b86e5e308ba37cf45986323c864c994562c7391ae87b0227b75538bf7139aa7e7fa0a1eafe3ae5bea51a42f48708f304ed1b25d789bde75620285fede2e2fa510497633253104bd85465080ad82084176ef2edc0a78d551ce85fd9795350f25279c3fd6846872cfb403c1f2137ee9b12e50b3ed691605a51b1e195437f98594726de5123cf8a20c4e788a694d0de22a78c5ec72a50f8fa81c383163c8b40a0f1a484e2be22e2c754924901e28ec07d8b69d90ecf0bcad41d6f3efb106299eb361e3af21a03b259397b6706d9373b869cd282c2a756f20d138ec6c4c9f59608a770a0c9f300194c707e41265419f7b8787f741b54de3cd60faeadcc29b3239330f04a357129792e746240e47d3f50b50bc810f3b63ed358b83e8a872191706ddd75f205b32c6283dc4a8a1cb987ce1a6a2053565085121cfa287b7096af437e3c802648162b274e82728d6216bc27bce48766344b73f77540362d586962b60235a51f39f17f612e5f814a202a0ac22992ba6c15b5b49f07f68463994e87306132980343d91922a2c3670b4964eb9223804bf8f4b21ac507fd3965e334232ea31b8a7200bbccc7942ce55177c3b6c744adfaead494a78571c7cb17b5278465dfbbc1edcdf2590ae56c6235850d58781125920b0035c733dfa83ea47571723473680547bc66d2cb7d6303a7f4453c439a3bae1c03f0413ac6eb252b5871f05bfd15173f57ae67c720d3fb47bde4dc687fbc07a64690c7c6e66f715496316e42cf1d0a0aa63aabedc5a6b95898d1bae844439c63304ce50a9ba171870d8a2f325cb1591860aa846083d80e238e0cd38d4f74b6e0d04bb22a7b798b9be4a4a69811fbfa99f984c3fa046a47afdd62c9714226d6c3180c8166e2192b51cc0026c0579ad2f871d656770453b87ca02683027bbccb38e306cc040410b096d36bfb2717703e1983bd42d668788f20f6172bee11a4b30ca4ea6cb63820e417e1290af6fe1eff5de8bf78fe26bc65989b5673f5b18d067235cec5d1ae479357603b360261912ce11ee6b0edc15a51d546d982df22ded11990e0f6f27e9a377dda413d55417b97d9d76a31a0ace1ee9cda8cfed7d3cc5bd530eb7c40d14d84e0fe2b03700e04a46a05ae02c445856a9c7e78036631588c926da0633e180c7d80e142ab9a1dc952e42d9068697df384e0da365a933ea35872cf2fe726a8801a4e8a2186caf9a0f83fb78e0ac75aab439ca72bad0e041a0192d543640f01efc7f5ff110d2d7dc61bf4c3b2d4ab71f1783e36dcfde380bf50f0f6caeb2f942d1b4069a396097e32d4301f955eb7d83e78fe26c8782a73a8186ece21f4a25e035ef5f8097dc151d82a7dd18508a2b7493840d8367a2b77f63ba27ffa0a021221957b92249f0813c2f5cddf42b7cc5b2b660a734b829d9a2b47388589a3464cd58a9f1df6c628f4bc034c875e6017aedc514f730d2d504ec974a7dc7de278802c625ffb38c9f6591896ab42f50e3a55d0663b714a4d8a8707e07b8c7e2964ee3eb81ff4fa555bc37ff2cd9fdf461714e032fc84021c49f0c1829442f07683b53888c56c27596e8c500ab767d33e19082242a73e0adc741129c0bfc79556c7c88f85b6ca9384ff73f1174ca9503ab33ad963d132641454240cf54d0c01d01bada072269711059f701a1cf1ace92160992bf112b1612c7b9752417b1e3c3e9dfb3a4fe580d0470632097bde3f5bb663938817f55a475668fc817af40d59c2ccb702d306ac9f97c87f4925878185e519d4ececc2f5a1b53e0dd973c0bf5b2ac555423f72657d656a82801fd05db00b3274f038b83a5d578b98247609e243ef7146148475722197b5f9e5ff666239ab6b899be0aafa82eba59eba7efc2cd8c7f15662b073adeb9102b8b87bc8b31ea60c8983dad9e5237c0998ac97b5078a472cb8cf93dae98449ba686f58e68db72eaabbd599045d53d76a6e1b73a12aace26448cf459914d2614b3395dae6a31ff0be80300adb7814be3c5beb2d0a452f67cab49eac3bb516ec7beacc869d9675a61070650237aa6b202bc475e59848b95bf84665ba68c36aa52c600a5b025234cc91316e80b7d91682db288558ebb282bb8c4ff3e9c1f39e163e3c1228717b0fa486344443fb32c511b91a028a1cf366c340a152ee63455503583e52e13684d7886a2917ccf2438b83bbd2683af19c541fb8338c56ffcb44bd29a0908f7e2c05bf4da5cdea4a647cc338278a3aad7e695edb1d195d8d24d220bc8507f7c86f27b921ba71951900833a369317cf2ba9dee474d07268bbc5580930f9d285bf30089c05289c8c5e8d089a0f32692ddcbcad447c194d5993ef23da8d19f463930eb98da9610ab940dcd642b4173a934108a3286d4c9b2c0017438a75d912cd9ace38f86914ad55cae34edc2d5867d6bfb74bbcba5919638c6eb719507099c220b47cf8177b62172e7e64c5c0405fefb897d051f206102e41c487f43d98b251e7ab122611561f5d00264dab8f8b67755e6edcdaabe6c2c6f62abdc9017cc7223dc68378074a2c764c89b07d817cb9ec15191af536e46cf95f1b934fbc11ac9d308ae1a949e5f6b74ad598b27c48c253dabd43421d3702869702aa2f0afcb9295ce8cd47a9ef5cc2dbd2b4204558336e6962bc80658871c559116083738adc2184c1968e6b7b5eb228273a239ee22cc905f27fa1132557293c8c993935107f5cdd189f4979b9a8e550b0fb58549f72d84a94b630381a62ab926003bffbff46fdeff9e9808fe2433cd50b0f7ba541af943b906adcce9691831c7223d61d861b4180183ba1183d157e4af15bb209ea37b38d8d6ea8b240ef12810bd1bdaf36b75fa9d9f69216c6916f8ecd941acaf40ff7a95a02340740f21b4f4170870d84d27f5b7438465b42926751b6c924af442223d929a46ad757c22d447101a7cd6879411ce9972c5bc5a23444c426df080011b8e1b230ba119b91be1331872cb9084be0a32d948c217d47767849f3419d051b9b5d64a4c2bdadde775275afef044faa6ff3369347e37967c3f14b5d8b8c1706873aecfb2a073926a72dea25566bff213d03a2046e8e2b234d9943d2990206210b0d0bc141be07c52f53c933cf76ced65cc94fda0e7676f220948b90855094a3694f90a969d6c0cc9929c8e7d316d7f0cf0f14a433d7b150c85286d4667554eda151f76909889dbfd10a1407a9b635724160d864284ba2424ab2d47f6a05639dedf50b0a59654b7fbb02bfddce02aac5b8c26c39f944412fc69cd67468c33d0d05f577e07075b9c26abde9850e4e81f68dc835b734e120005f50b4ccae28945c3b640b81d27fd327532050122163c60243feb5c62ab822a3dbdb5e4aef2b2fa00cbc99b5d044fb5cc93dabf50a6df9c9429ef0f71ea04479c3975762354512c634ce314e16cd96883304bfa19f94fe387844b4fec6a5511cf889fb60937c476a15b6f7ba0e5d54a91bae05121f678f0e46dd3113392de8a66cf13f5fc91d1e150c52be389f8c6892f1cb546f0e72ccab6490be1a9c2dfa1fd18422dd7ffd75ff8e057716e71fe9b6ab2ddd3232cbe49bed402c0e58c23f95c0fe783c9a5310815f4a84e23f4b4a45acfddadbcd1f735a9c83e1f1cf059ac81c7c76603270871f9141eee5902a6bc26b3e7e7fa4278e1968fd1f434b70bd6fd0919c95e498d9006d8404f02dfe4f2cc92a993afe2b2c0249b6b07b89a5dbe54be2ee399ecc8c55540124dd60d1daa182c8e5338ad3fb58a7219899f93511d4865a6204d9c068669f91ad1513fb3145a499e52d609073b627486bf5bf67897b4b16c58499fcc98c21da317e93e1f048d9fb830e8a5956fc9abcbf6f74cd305a70b2dfaf42c0a34b7da2b288e26bf6e3268872ae470cae9d9be1b83d2d74cfcccefedc4c2311c17cbbf501e6b8b7b4cc8e262f6d8a915f88b056614d00675e3529c3ad008a1ac9a6f865a57bf8d49f99a0d064429122328daf280b743a3997bfd9c8b168ce7ec1e6b91199309d2850269062d5f32a328b52a7cd3960de3be88086eddb975fd1b8cf7a6c7d02120c2b3257e22e90b8b4c73789e2a7ccdc00a09bf0a5d044a05492f6eafa8aa3cc147b44ea87f84ad3f607187c056c8818745b3074ee4f09c47a22783910d57effec7e5528ad54326bcb8ed322602c33cc9a3099bcaab6e9d363cef81765a767e9834888c9a0479974b145664802da68a667df84d324c8236b3edee161b7c8b44a87eef2370eef8f0276320bee18027dc1ec6a28a49111f85473074bbc635448bb505a463429d08e93c31bd461933afc54a5218da048a0bde0e862837f914c5768a661c5759ee27361b78c8a83e450e0cce66e72d210ead649ab5d22d3b0b41585a41f9568bf3a57118094bf827c02c4ad1ba594b02f09a757d81f326e738f0d765deb10ba676890e16cdf12ccc4460ecbd31993b9a95e8809436492171c873df7a01cb49b30be301a6dd38388df2f17ee42d17b111edbe73d590c7ffacac4660ed341a60024d55b072c6058f98564ab279550faf862f8e594abe42f7649c0fc0515f76d00842ab9171ab9635ed3c76bcd34b01ad0b60064ed9879897b0fcfc86bc34214de0201badfb30283c2d06bc792605fca7478d881e11f33b7de9f4193ad656ddddf22f442df1d56ffbbc52537cf3e1141b9b2ac0a8ce8886cfc080bc0baefc82be6fc077650090b9c0dc4e755dd89891fd65413e75c655f5116be35cef216fab32ba6a18b2055c42db431b30c28715df4e165f4b40d06d43575871f8918fa7e345815fd58f50cf4d820df6bdfbb56bf9dcc785dae20f0051f11aa89d7e31a97e52f14ae5d2fa280303b053fd6b346e5b77172ff2870ce4ff20a36456649e1a7d3f417fcfe744cd2b6b0c3066f8d9b85988bd98fcd3413bcfcb94ffb090a831c564ee300b13673a51cc9c3dc585219ba42ccb46df0bfbebcf90062648224338462c13100c96afeb8b5372f04c9e3007f0be09960eaaac9c814b8ad6ad27092649a529691f94b72acf33f96e92373fe7478635356014c3ed81d0a4ca3083d1e0831ec2e2884478e09b9c508458ceb72bfbae645e5a8253c5233a67ce3abe34de9caf2021ad25ad63099788435e2c275900813a777e5f90198a848d66ab834ccc574d52939faa181b29893731e3de7763c1069ae700a0510cff70e21236301522549feea4552adeb6fd3e7ff9140530b5b33079efdef16e6149b238008988afee4e827474726ba12418d768bdc86d5c819a0cf345d07bf637bb29944e913a721d0ed072c18622a7d84e392705b0d9773e0a74b940e39c402c1720ecbc4780a8de27e6b4cafb21c23c007632a1ed892689ee138c2b7812fdb537e3d7b2a2eac3cc240ac8fa2a52b01c1008d6fca0f385cc4a96c3cb73736eb7e7f29e764aa4eb6f73a996e03f92e968c5c04f574a84c1ea552eda5c4b17c2c10a42a53e4cb0df46cf57b9ba0af98e2c7ac0a3c568737bcb30bb2e5b1db0a9dbe29259de841586019c4b475172eeba792ea7122529aa6b3770050c15ffbe7dd74d06b8443e72be3b0ca6c49c335b46ddf49cb1b173513085e56c9a9d8f2db5b125053795b1b0d4fe298e543e6aa54f58a49e5ced4ac3236be4bbce1692c932496c3bb4b7060a64c5d825a7a41a7f5ffe28a0a5c3b95d30373f7dc48a9629068bd3ad89f424e79a665c0983ba6582057cb9035239e1a8e31748d0eb7d0afa9486424d7390ce773196e83a772ab75c6bbedcf3129b5a85f6319aa5942885467b7db36770044925b2aded5a3f3ec6086680e201f7f97509fb606b7f3b8d0b44b6f448095731373d9db6255ca9f26cb444311f9678acd641d89c85e05095bccaf7dff19b0613303cf6f7394700f995f46ac416ea3a1a5543bba8a41d7339789250a279764fd9135c87fc7eb5a3c14206d0641ba6b0fbb506298751f2f909552cb2f8b51e91d4d664e65d017eba69d5c786758db2c0410b596822637507b3c40e7dc75cf7aeb670e94fc5a002a70a49f61768d11465fa13932c7f02e4c44e15ab24dc1b2bb9cddb0ccb32e5dcddb44defdca44c13f47ee362a05f75a820b77afce4dd13a0c9c75cf2e2c7edba935c4ed2ffd8a194927fd5f5f7557786077a7c49096c0130ab36c2532969c8b6a79e11e3f01c5f62742aeb431af8f1e2c1c1f17ab22ef2a5bf615d8b6553d23dccb5297c0cb3a58559000f220ed2442104e2c0e94f2b71a4504ef8505bd02f84589ced263d945ff13778aaad4d07c1d7efaaf7ac3b2d364fc4d4451e31bb1647c8bcf99718c32696b25ac55eacff52f4eca46ee6361185e479eea64c375367945cb2a8a630c64bff3f8e249cd3e0af3f8b4c2db4a26c3e381936fcd35d7ee7e63ee69e367084f93d1c6cc770963e81b2311923788d9f02911f754dd501831667d97d9782578d8b0e77e6142c60cf8ca5a17ba209425e0ee2741d569b10b72a3bc3a86f94de3490e74c5b701449b2e7f565ddc73baabf3b156a9bf855da03b9aec964cdcaedd056cd488f011bb77cf810932218a3027297e73498ec8a8bad769f9515aafffec009680b6202a2c410bd7f88594abc09d34831aaebd8d40c3f428335f8e14479d5aebee570d77aa3f4ead68bcb6948d446c22dc63161a84c0bdcd29370d2f40e4bbd5b17e1a9193262aaf1d4e1de2058c7c15a11f42da2b11c71161adb1d35f12c006c433c7039495531d2c02c8938a19c8fe147b4def99b5702353de61b5378fd0cbdc86d544cf38250f760713b2a22610020b294ef1cbe4daad9067318f36215d48ee9bd7ee59a5cfd7855c7d3ffddb51ebb0204bebf8e60e0be6ae80fa4d5ca7f3d70e397132e0ef848ebfbf9e1ffade25ff8109a291600c37ff2c44d7572c7fda71c76586fe502304c53cd9d6d931d6a2271326c55b1b31a44976d8dac42c4c1266f0bed1ac4b3259189fdec3d30a739300a1651d6d3d470a121d5347cfd88e8507e26e82307e333b384f12dace0017854bab2927896e573f9727af0cf816e928127f986ac9b98d84b051259ef212003998f15e76a5c205de1352b4ee369ac69eefd228480b824a16b74d1850dc83fe662b6dd502b0d297c93cd9e97fc59f8a14d0f17359abd34cf5d3e0cd329dd4da25497e7bae126fad8912864bc384e8bddd3dc7f2bfabd68bd61539f59100343033ce6d72f863796240fb281435220423e56428905743fa53a58ccbdc6798a22118007a4a12b55d942f00c0f12f6e4b4ab0893d09a20a7a309541154c3a74785fe5e68b610cc72487ac0f2f4809080bb976c18046b8712384183419bbbff6f28fe5cad134f31c9065cc1371261a8e276a26f356122a93d263b0946006776da6e912455d02a5289fa5a242f2980c489b825b6bebc832cbeb2eb09620870e8df464ae5a68c99f590e8d3ace01f5950ffe1e9c8dba1edd32881b63b0129e557df3feb93f1a288ef2dcb8a7f0243a7edb99757a64edd3ba5695f4e49ca31f36178721722b2760654200bba3b0a31d6ca9c24e735eff21ae89d6dc32e7494ee4f1ec1e74d705266423d87c3ff0ea97cf90658648b042abc5d8bca686ed4589f455b7fa355781413f1ef641e28afe01db0bf32cfb8d7fa72f8525e29ea84192a75b7bd25652e9652eff0d9ab878460911da73a0b5821f763bbd73f08d5de78d0a3fecbdcb692310981b52a24ca51c67833e98fd771ccff9fc603d402b6c04bfeaeb3626e6fdd978f9860ed5a1e4ce06a5069eddc868102bfe472340f4d284e56fe56de1bf12c42ed0670c5e3432dd3b758f9897e70fed9d799eb016d4c0c353e7c1ac79bc923a60b71b948038cd11e824a48df9bae43a150d0a06a6e80573f0a2f08b3b8cd4bd16fdb052087c8709289c716a83a9e6b43bc9912b6da1b1784c689c4a3c7398bc4826b1cf3168ae7cc742b329bea3b55d6a7350ed4306cc8e0cd8b31d674552ec852f99eed00d559e8879e73f58ae6c9d4c2e25b54ed417ec279e2c3f745433354d62b346883158c4cb2f270b561a923ae97d588b2c2590dbd81ad77abbcec803bea9a7144da5132934734db713ac8ccd2e378da5ccfb1110d05233b46df2b5d3a1253b0e91a7e80d340172213365d91b8eb93dbdbdd723da82daca5bb402d68943823e7edd2e1d618ef1dbf3afe2c38b74166b6f4cee880f1d5bcf5100dfaa25331afa33a35cc68cba5e202bcfcee4a8e46510190a28783a6efb9807dabd8d6476e278385ef5af83ad4a13374da341c2c1e221d50c584e0a805b80c232137f37b510484d946047d87cd250c0147aaee9f0d050e1e00551ffdda55f59ea36128a5f22af4a739b936de6f85873e15e9804482ede298cf528359af930899e3be98a3433bec60997a329e3c6a49a8931765d657b37af856a62ae838da0fabc7b4164a9997cae3b2f00d89a205341fc0ab48eafa6619c9666feaf688c2738d68759f61e5f967ce563aa121beab8c2d2d99b3417ebbe202796e1e61569450a5e1b207ef2c8ed2c274c7e3e195d03e802118a9bb944c605350e57f13568719de9016caf9d4a42a0ddc826b6dec0e54918c5caad89b57e9482e38dd8c3d1cee3b2b5f494ffb45a48a7a7bbd773168929295a078337d2588d18e6874cf7fea3b5671f807ae20a441191ed69e571566721692eedf4d06a8ee69769c58856d394bbfd77d9456137a32e1765f5c26a69e2cdf1470ab193de8c9fded0e8aeef5cf9dba12d5f83628930eacec627230712e1ae7cc527b3c102ea608a86d8a1a7fa3f9b46ad9cb1bc6dee60993b32cdc59bbcfb453f94c1d56a19fe781fbefb138a7ab111c2e1a85e9433650f1cc53f58dc26cc6992cafdf69125dc48370f98f3927f04ab64f8109ed61cc5c22b288dfd4b0ea9e286191eaee0d8f0617a403dfd8c990ec1fe70e51b9b95f47034732c46661dd8a49a75c2e62beb1357f3f4f14d9b866a4764046ee9a6f2f8e9d094c12b6768bad500288b2f73ed49de11ec3d1b5a75b2ef8dcc5a9c8f796b3352ff13b9b881ec1dd8c58ba49dfd758c9e0308cefbf10e6ac7cd134e677bd1fb6f13ec7c0e582c7ba0ae4c199fe11849648d96b056aec7129bbcba0739aea501e5b40837daa2b829d86630e8af45f49ae8c85b43b9429d5ef12b4d89231be19b59176ec59fd0c48aa3c44e7a58c278aee344aba9fa6ceef47aada7ebcd40fbe743ac6b996adf9cf2d5199957adde8ecc129140223f06147b1942f6a6000ff944e3bf023c00ba7e546e714557ae2f2f2a9fac71349df63494aefc9a7fd771117f21c1cfc672b065efb0e1672e7d476f02492d4477643ee102c48c44ecee97652a68d451c2d2ad98624f5d455ad862615b88b624d1034016384c0f615bed8a7aea39fdd36da9717d716af82511a8871245ec64a13281834d364c5412331ef497d5eb2f54027262029518e82a2e10c00dd26461528f3c84f593846fc3fe54d777e3f4b0335b0cd7221ad20ffcc5535c934849f5ad2ebb5c746e42c02ac88ba202b5e6c915f23baa2b4aa04ea7b42cc803a53a5c5e6a8af8828d02a91bb2a7cdcc049c57495013a87f3ea0d29ba0f696dcb939294319f5841bd0caa9e9ae5432e7f91cf7785ca685010f046204e2d80c98f12b59100255c7b981b7b32af4167806276f647f3f013847622206a5b35a6c3916468a7d41571ea1bf6acfdbc859f3f358fb23ab1841c6f661647bf7a6741ed110e63347b8427f3fb0b28920c77c0cd97b25ce2073ead6c0cdbdc9b91804a742f357f385d0db3ceeee9bc032e6ab2c00d7b7eb3d16c804ca82830177e68602f621daabc419741a81a853ea5b297c5e4f010920653a713821c4259515423220ac5a16b6a044092feb23a793fd8b59f202203cc179586408629935a5877e2f24c0a12363a49685fc3e46fbc61194f0fb39dafc85fb0014737f8b6c2adc9e199acf1a1c6224b87ad7d855d73f46dda07d3985358a768f0398517b8096c705090ce7e51a140622baf3ba23514873019c93a4c17fe990b3e4651faebea640c391978fe378ba296c8432b1e4058388a618010d07c87464274109f3a02f52f1d87afc8c8465282c9811c1b0c0458c1ae55f7c39bd0c8c5af6a30efdf576d72c8e463bdfbd93237d3de0f53b7015a7e6f65997b67bc5dce25ab2cea57ed42b2452332a33c4f637981e8f1be056dd5e0451b3b3bd428622e5fd0a75a88652d00d54653f83e1bd43287e0f97c853e263a2a20e2320dc9da918ba1e7ced39f1af51db5637b32a812fb4adab7dd122205066770d61e845fa18594ce64e3840c950eaaa46f83ec510e5446cb34557e578787c6197e13ff3010294b240a984df33a167381e156609492be8602a26b0ec683ed8d5d8ef9cb4b5df25a83540df2e8bfcfe29bd882bcafdf0c709e836b34e7ea3b7391cf7c49696b234517f9f6b0729f9d6310bce32c850b575ca7b51d09738051f16862af47eb0ca4a60039d843ee02a31b2b081ba764725607a7b23360caa1fcc58029e86936e075214b19f41540a118e7d32379b8a69a3108b7b928341fd8efb88333cd1aaaa179a0067c151375df007582582f36715c1535fed59aac9e4518200072817fdab7004703e7ff32b50dd7c276e94c680500ba9435eb2b8902cf0f6ca93a4c95a91d3213f00cba7c4e3c126f771179b711a3a67fe800d99c3341c1e27c2f07bb4d14b7a3b04c3b06331db5a95862f19feb95836bef556aa9849de698d9e03d2f8b4d0cb98ccdab819ef69553552b2daf3c52c4564a8c644dfe816ebcf7f0279fe166bf2ba98a8034e97889ce9522635ab91a906108016ef8ce492f14b7992f92a2766f7a45aea42f2c464a1a641966cf8691a75c11a727be30e091db8efc3054ff01675f494061bed2fea539845175a4d45c12aeb1690d0b7744f1dd0cb122e062a2afb754734b24475916479b47c588afd43006ae5db95c1b45b1141c2689fa160db00fca2145850e3d75f846e692e3056862ba5cbe8ce5c20c1d85bd05c1c23f9942a61a3ee73440b762411d11da68e5c4877b92606a462c2edc8d9e70e369e59ba55e7028120c8f5d422b44fa975f058aa23d5697c9a2b20e3dab32df3fea1aa2693d1c860a5ad4fa922d5b1546f333ff469f740b5a338c79574e956c778500f8ccbf6c6ad966a54d571ddf0b2e85bbef39d78e356472aba5044cf46d92e6e95ca594669dc610cd499fb2656465d3ed633ae4488043d10a764a7e65d7cc66b87146b7dea97d3a498ce65f96f841e3bb67aa2a88af5305addfbf0a4d26ae8c4d01ce910fffdf52590f705541afcc0782e2d6a74328866ebd6ca2bbaebdeddc3697f476690ceb4e248c7fd01af622c91932c00bc6a2611678f41b65185f9691044e5df659023750ebd1271ed3cb7a99074a929c81d4104a4e03f91042cb426c2ea8fc6a0923e774dfba15e3fc50103b12aa2272bd3124b15fca036866c24543ea25f030ae52ca5ace476a478ff91b7d2ae007e6010d10274e2467131f32a5a29caf14506f0f9c809fac5ab9ef7829084552531ce8f088d129d2f73425ea01cc35bab08f2687a16078128c24411995c7829880fe15f4087b3d1298ebcb128a7c21b30dccf59fe0668eac79ff6b15e8522c41c1edebac8b6561592fd74619a4d56c8a9d8f1fc8221c9cc1edcfdc6eb40023971336497b56d9555adf285a03cb7ceff2ed290899417cda0dabbe5c329c3aacb5969f2a2f8b879e88b4ae654cddc044724faeaed8647379c6e65e1ce0f0aba0074a02c408a9134e53386a8823156006d1cd4d38e77b92687bb9ff868e004f1fc1010552fc09c19cd789b2f696521159cfd6bd630381be4070eb1dd01af99580a30fdee3492f187ad02348fadc397dbdb28e63a09e7eed4323b3b3377238ae44b4486880040ee151d356aa8d77c82a6dcbb1d2167170d2c4a8e0e5dae5ca0fe5fe25ebc61281200823a429fecb1c2b9ba3e3f6f94569f25bdee2fb27b3fd23c722ecb269561d065984f142ad15e4dccebaa72fed07ebf29ce66509ef9ccf0ba606e6a9bd0c8cdbfc1459b5a8dd374725c48d3e8fff300ddb45095b808194200fe88d52ae372d7984d549c647b32d8dd528a33ce9dbac8a7b4b06e704a7b39dc03e03ce152f5403e510c6420d629af6a4f33eb8a96ae5c90ec2c75527763f8ed0cf93e3f6de048908d2bab4bf1af871355cd478e44e2770df2985502721fcc5f3a485a12f4ac64ab46284caf8e642309da8849b43637ed8c94462b838b3970865400683b23cabd47f69b15a0c308140785851b4d69fb55fdc51cd7553cbf1045d9bd1249df10d14ebabed65667ffd9519aea62ef76090644fad4d9b2b508b7ce6f86615d8d36d03c0a2b758ddf3980c9e3ba1b86beddc2da67817b0b4b5ea8649ceb65162ab32e92a3f57174dbb21ebcd085be156f2cec0a9ae4060a9449c8ae231c29c7c318ca79c6076f3e53827ea75a9be6e0d2c5ee5b6e59f0341096b9638d1205ea95bb8d184780e3ed933adf4c074ea2f4a4027a9003fee876c9d67399d2e329025a97aa751acf7ee11d03f0b99724fa7ab9fa00fa4d81ec87f67b2695e54c6193845f7b5e9cf1378331a33fb44f287e93497eac4db7def34b6603c95ecfe435c5685491d74274cdbcf51d7f989817e839a7981683e4737a68680d78a5dcedd054c181a691806db9b808f4431f96c3474c864a8627944daecc114805b51a49e0774894854324f413b9dd04909238a394b471e8ad2bfb262580551e7848ae0ee7ae2d50ed5fa2665aa5c92d7a5f869a892a2109437ca49f14c7165209cd9edaaac9c40e6d0d47ee1503403fa2f3c12116522ad0c4abf1be45224ddcbcc02a8bc5737b7eb425bae8c726de42c1672ec39e2b3533e8009f2a0ee27101dd72a7c17f5d9a04d5c556ca800d42044551609ea01f462e57fde253ab4171e6e8bdb68ea3dcfd125f0559e8862594810e0e171ccd8597e61410fca5f98af15389cd29baf328bc24c89e5209c9c6435220019936340c67330f4d1fed3576d301de141983e2d7cc456c7887a762f201ff26f7542e10a9d269663a1e8398ed0aebb123f7c41b7f39e17095d0b3a14c1510b3937e5a3902520e78630bd5ab280e3c54d04236b0f8cafacc5614192c5c990e4e032058209355296e9e7e0299faeeba3060186b95c0893b0d9ab0e16ee364a9ad9a1e3ad6261184435cf738da069fa3508d48722ecf611977c45c8aa0f9c398c02403f71c871c1a54bb3f62e275635e49438087d8b8f843fb7f36761b877bd741fe4f2ce4689ca7782ac581874bf810b91d888a301fa26f8608c5960279dcfc0b08f3445c3b24eaf9377440737eb687b9b0709005681355e7822f8c4a7c64eb17014a39b862a1136a190c2e71bbb30685a96898b733b04db3b083e18746e2d09331877831049ddd65a5abec4eeb07fa02eda5553cdf108feccd33aa00460fd04f71f83767ecc77c250a511d6ef75fcef601a0af25d934277ef5f725f6147d5b80fe049aa2fdf4510f3a20bc2b3607e2a97ca656e22756dcd241333b91e14260af8a499f84d27abbed0967d4035149075f15a30c005c165406fe5382c12d18622c936ba609390ad403bb63a84480248d1a23402a81a41362bb9820168bf7e648cb0deec66f245644e932a8413a263bddc35a7ba1b1f94ae6f3e9495a6736f5e89616fa96628d5cd94064b0c1c810beaa405db00d7ecf2507a89cb49ee4c424a09f3e4db83295b034a8ede3ec96bc7fcb71ca0e2e16ccbbc1fa513ea80cf5403efc588f4fdb3d8518ed9e8189401ab3fdfd45f698075b95fab91fe0feda25951fd872edd8dd073d974b99302089278f28e59180e9a61bbd0ad61cdaaacb46d0ca34204fcb716a7cad284fe18c4f36f2b923aaa5fdf6c811d27bfce19511949be4cfdb86ce8e75c04ccfe93fc8b9bf05facb01c605a51a41bd90cead5cc77935b69f9b8cac92fe2297ebf702d43d768fce4c66e0fbae931a8382fce08feb32216a37409fbce426422a0cc311f67b5aff203f0281a7811d00931f14dcdccc064025c1e0733c381da58a78fa5e6d66986dd2f98b9fa29d7ef5d6cd59225784b4aab796c40498c30ac81c6037cf5bb5aa3ebe9ba257a4f175bebe30295eba640795cd714f9c5b60c2277d195f2dd9904d3d305cf8932684c9f643caaef4706c86216ae629a4b4c973706f167a988c7db68d8d58e3626349348533c3aee9223743099c1ae16d97bfad6aaebd8bd9df371caf9ef8b1864682c1f748b9a7c589aa7e26bbf30eaa0238f7c889f49cc5c7e73d4fac65a1ee2bf8cb5919e8cbe67bfa1c3aa1189b38f778c3b13f50d92bb11a721ecbb9899a5cf1d5cf44491bf9e0c99189a506947df5d1b08f44e3f7ca2f13e25b0800227284036e6cdeb5fa84a09f309a9314d3a52649bc20945c7f10f847da09de20338f03040f1f2e064ca55c66f43bec2d066eed696d720cc1eeeae983709a3c6439932e34fcd8fd7e0c3192bb9bd05fed0c10f107b14f6b1bcce71f60882fc45e01b09c6a75cd1cab89817ce83c49c9b9f04cf1acbde57ec47ee30c29986f86ed3c469b749f454eb036bf043f70bf5d0f73e467dc598ffa0b6189b7b517e02c1185bed29c727715810457835cc175799846feae085e549aa67342f1f4de7a578226fd18e7d039000b046216a3e2205c39b4f5c67916c53e3e7351e651b5d44ca0434b8ccc8fc981bd0e5565ca35919dcbb60ed6ed420904f35b854001658ace4981ed38e7485728e7640e727dd39ec14232e2678f08d57d52fba621ab28b82962d45fe107cf58a85a80aa5f539b5f9533acc2e5aa2a0c8c04cf017be17c92b50a9d4b82d6fb6f37cbbaa7abef1fe8828c20b9320f3b5146127838979f04b719f448fce30fea2d9f5c6f419715b29c9b327b1b04462163c093b17088df87580eeb3aebeceedacb8b2d813def8022b94431421e74a7958eed59a3e1946d6aaa7e24738cc167c4dc5331c016bb1f50de7501ca1dd029047614eb8af55226ac2043308df3d52e5d1f587973a72e6b1f5b86096152509f2832a7a91e0d58b094a188a200039ab558243f929df45a7e1474b2858b433bc14fcb9a021cf0c7b23620ab4f8ac559184335554c175334cb12e6dde29cd02769a6ca8f5b6736a33dec472575e88e1cfe325b98d60a2cd3e2cee813807635acb04185031f501ca32de963de285f6beaec564ebc969ac1f9bb43ee4f844a2e61054c550f09e268761922f19b951dbadb12dc0d9229a95733bcc51126e512bd2737264f3f4e7b484cf5b08422902d5146aca8e49d6dcabf20808bf1049a751d0146c54484ba1e1a6a6af852f6e1d4e91c6094101241e62149048cb435221057fe8811b7547193afc44124bd66659a30ccbb12816116e2e07eea10e92c70091f91e041ed2c484a9b2f6cda02f3124448c5d9d426173c7b5a2ef424cbf0c1385732be804096cd1bfcbcdcdd0f1963851a9bf1e1e3c8b7393904fc7271a1225284f039488527c95de9470fd8818ed1f153bcf7e2810ff9f43ef66753a694b3db7aba2557dd75f5a2451c9a893811b9c850f2aacdd5c9e4a49e26240788e44dd429a88541260da7eb1c41ad37b44f4af75c356092c8599f7162d152e00f8dc9743f4aa89cfe18eb82fba7c24901e29b9d2c55161bed464c40bbce8414c57b21bbd126a4e58abceab16bb1bcf93add517126a7eadb9b917453d83988c13f225d48c07d658a98aa1f74bf8b05b705502b455a3bd58ba392756fc3fd9900d1a29417f5c2e78c83130628c05a00a7c3306bd4a06b8cc5c037a8766a0134b970d409aa459ac8e285af3cdee06b2f6aeadc5a0c092e57bae7a3217cf3e90ae599e0b421b4d62a149ed21548b34ed9188ede9c5519d961ab8812ea4af9b5cdc9115b3e2d3e748d0abd9cc32b54e8f2b06e7ac4fdff76f0856fcb1666e2b29461cb59bd70d9ae7f9e5bc3578e1aadf87895fa4399fdd3968c50a35878660d488fef37a635d5c2cfa536520b513ae7202d01c086c476ba41079705a92dedebb872919f1165a917b219ef4303776185dc8e9ef83036dd856fbc79785284aec71954cfc8b3423c1579d73bb3b45fba608bc72443adc0392f8c8f1faabe7bd845144ba81521c9b929a6036420941944468c21c5bd6f57f0105b2b9d8d0fbcbcce721915018b0d034f04a4ba57c5d01a3d3127b3fe90e6935c99a050801b3e06b332e89f2160cb5fa0217a853fe0c0b22aa6ba01845c5d9a06d8900f4275264228b23917c6b2f028504d2444e37e883d98500ce6b110ff46e052695aa45cbbfb1e20944d2f29e0a60748a7d00c77b5d587a2cb7b0c53891a9287d8fd27a00b97218bdbe337ee586edbedf678a9f534ffc020a56b1cc0ca0f525df73a8b4a04622c8ab97c0867db474d2057adbf32692ea7b499d420155fc4026cd258617a3f13ce6175758c1e228bb31588250d38215b72ab37cddb6a04e889af95ce394db2f03c003d0fd3af1129cdba71972e4a6b5a6e1e8eb2b331a5947de44378c59fb68345835c6704fbe25abfc892a549cc681e879f8d1fb054f405c6cb9744c785052d74045f4831b439d68079e14584a1544d50f49658c22cc2e0f92363b94b9f73d167439c0cdc0b4ad0b273d0c6c323db7b7be3c16697d80ef67497ebb86b7062e745d7963bb6e49fe51c9acac2b103e324d3b3227da01b1abebef2c0ba698aeac767e75119bece5d5690182a7b9872ce6775829a6158d826d6f4b58d16d10c2f515f7e5a2e73f74ff52219d2031741e243ce918210b58dd17b7d50603600e15664dbefd305043f43db4485f0065b5961a6d3a627295e584c8d23a6e7d4d2a06b24e573009dbb588310d2c23877913ead4e0714a46c47fb179733a0d6ac87f8101209e93963eeac846230bd25100970222055301d680be6701cc4a082209e2dff4f270d62964ed198b6b839c3d23a70cbc8ceb802b7e0a0c3ff1e1080ef4d5bd6eb276b2aa3aefa42fb0cc83eab4b212af163d563225ed17dd10934564abef77bbac8548399f0c95acb747e40255250662b2248d402facde02586f88dbf2f381454ab76dc73c50c8c8504ac9b03f33b1e14d508c247d473360bc2fc678a827fbecade1d16213e021ba779a03762b3b634c927a70f59e0c5cf636aa069a3cecf9de7515437969ed2d264465eca3372dc9fac7738013cb22fed2c25a6b9de009a29819fc0ce8d35a8c6fbf4d92706d23b4e7aca9417c6d67eab728f5065cdf863e5e0ffb7cb68ecc368dbb771b3a52baf63449921d905e830981edd6c370556a20925b76f372d9192579404cb3308af8ac1559bb5c434700f8a26b22cc6325a72421b58f4814015787e8c1dba6f617d7de66e478097f84230b8fbff1a1a6e071834d5547ee77e4afb60f3002a04284d6b2323a88e56e92f0ae6e45ba17df71c3502b6536a09ca90b9ae5a6a9e5c99c0dfc4e4f9d17d85443423f8f42a59256a9d30045212d4182e30c66e2a1f617dab7e2a1a441edb55d5eff71443514cb1f63440d823ea6fb229cd4d56d424533a7fd19a9778fd5363a010b8eba56f38fd451387267eb4b8614ae7781032207e6d5c3d4d59649c3d10dad1ed83c6ed1606a14f8b800f248d904c626c8abe384c4a89803df0f3a775ee2e839b2ea2af01bed5ae4603958cd33803645a72ef6a3a63e636006aa6915ba82c727ce11f787e8150145c405f029bfad3848522d8d3c39948942043ce35adc49004c37efaec13e0e464b4ae888b8147bd7853cbe3a4a47e084c3d45f373a7a1fca923b2224a606c33916fc90c3f358de2a86070caf7701c64087bec5ccae9fef7d9f89f5a404e53d52478a4c8a59b27c6da9f0dbd8951bafe4ae53f5bed89cd0d7d2c0c849afe75f4ae35a3cd02469b4d63b094e475d0b5d14a2284761485571e2856600b36922929a7013514272811f9010b97f7d337aeacb136ae0e1762d08d9cb896d79e168b2195f397d7bdfe8512b1b82da24de5870843cc4315b060220f24fcc8eef763f5f26e0f5d5795da85c47377d50da26006ef56f8c043c2ce43177816fc2a3748f91028fc046d482dd3642382b954208d4d0a6dbc6c7b3cb7a60e394a3fa013b868b0b7d2c48af22f55923af1b85c6f760767bea30493c9d1a35051d951d8233d870d43537d6855da588cb800abd6c5e1418c9f379d4ba58cc01d3b21bdc4644571e26c4e1bd8c0c7da41e72ea7132f0142e884ac9c7ed20704430e087166448e3736992300a2eff75ca2d53623289f5a1de94a7d15ec232cbb9c39141e367302e1ff01b25beb5e6008e52a76d682394cc2a040683f2fb6f5d39eb9b5361f25868967dc8fb01c7e1f0adfe1fbc67971286bbf53678875ecbbe59845420019b3416d100d34b6ee9534a483077faf32973876a73126d5cb45d1adc39ccb8793f3d3fc08f0ffc679b76e781e21b087f490601507de9b56c7bc2c0e5ea702ac9ef97a87ed5004965a0758c1d132c5bb669a614e794b35003ac60ace978bcc1f6bcb02571c28e6b0a69e86c7a905584de421ca73a61a9588f957de0f4be3c75b75f616060c7b2406d237a74e1991515e28f12fb17f1f02e01d68b47d516cdab9ee146a01988323c87e0b1e1285cf0543c9cedf6803243bf939e6bda5bce01b083b69ad34811ff41b49db5ecb4796459eecf3089525e35abcafbbd6ab74f81a74fc64083c05521c5318a279acd85aace2c0a31043dbed638b55956f56bd18fabf1da0c64ba51fc68aca6893188b9c8eb1b6db2a928dc6c4bdbdc7a0e0930a32b9d02c15df48138d3f50d4f7ef32c42d4b625d4bee629f21205dc5424311e42e93b5295672e43c8c74dea2622f0cae280344fed0bb6275a04cec7649f5f3fa00749134e5abc899a2136ccab350a7353e0f65dfcd563a5ff9fb9a207e6be70146e39ccf84b4d11f68cb68517928a6fdb4bb171c26894c5fe45feab5fb974abc7c899dddfb2431abc522fd34a7043ed281b84d1942cb024f19f052e41cc889b794a6b66cfd1265a85d9f298ec598cad171671f46d1cf68be47e5425febb1a625089f0552290dcfde4b0123730168fed4abadae510f0c92ce825160e0c85459d2b6b60ec8e8d751f63a3b890eb5e319839c211947ebb9463c85b0038d08b0fcf30802e95c5da796512879012dadb493e7c0a33bdd14bb3f2bdd0dc33bff4265c7b820904e6d4fc2f7c2c2913ec1bc62c0e1666d579c8e60908c1683cedf62c37c68ec965806ca346393b820bcc547828142852ba2fd926146b5d9ab78a6bc2e156bcebcb24d9ca9ac5bbaf2e2ac6d39f5bd2e93861b4df3abc0157246dc0ac7058ba9d6eeef78e468045ce257ac3bcc7e9d292f4cb843f47e284817dc4423bb22a5d04921ab62a935063cc6aac86009c6d28053141ab539a292fb20f50b8e710bfeccbcace41ea1c332fc799504e93387f482f805e8b200ba387bc70feb18ff0c8674463c141d4c46d0985c6e4f472023c948926984bb877d46f4628c9bde865a4b25c9324f48f4a70c46e9d56ed4f947a03e937f5398b2b883c42d9f0e0a4262e74ed04a29a2b9be5310e93a5306a5d274a46bb64295f21cfe94d822a3aa367b5982f6f45a10113ff3ab82414a09a6ebf3cb357d248ecd1de29508c76cc30a9d3b8d3e1ebdb5daecfba51ebcb0c04c6d43ece8ceab792a6fa6c8d0e440af16681c21e858556fae70880580b95657a00c016c559ef7a194ea83e78c2dabc448fb053f6691df403894ceadd1bb8505ee373968b23f720dd81c338b818eab7764e08a4b10a9315f8d33aa01764e6f181bd0b83aa627e3c995bcf7ba6cecb669fe1289ddb1d32d221b2e22b7e8af251261069d0347543ad08c54d5d044163337d93b03b8046888f88074e36f9b31ec1f8946d52335bb5de2b66f18160ef5831ebb645c311f228cb3a1d91949e2e8e99fa3c92272667bab87a0f47ee90641367c491a7a5a72b90214d9047d9ae66dc5345ede4ea29387c270c8b14cfb7a97d902cff328f480714b8451d0030798c180b3f786ddd6016afed99992a13a99428e10f9ffe94fef4e129b7ad077abca286ed5ed75799378106526f749d2aa693c0f0bb7693de3bf106b5263f95b3c82685f7e88e8b5011765ee3807fc5a19cc032168487cac351d53a4c8a4011a5222737f1df00491152dd1c4682f145fbf13b201738d9319a0f9b9d915d6ec21848d11a01b058a9b67f7a2ddba64c9f91103c98c29ef32a8fb1a18fb170d1e44bfcc29fc2f4ea2477508251901b4545a8c4848b3841d060b20574363400283246c0edceafde0e72e976b32eba1844f02148d9187ba3840a206b568521a7928e88f7c3c6a0177e644345ce964cd2a663110c97ea38095dded8cd04e01e858d403062be55a5c18ed38533b547531665994e24ac76774a248f4a34c97aac73b8ee7fc8d3e5cefa9ed10cdece8819876a718a6324bc5f919843cf76f162d02d390e3d75bb64c811a9834ae9a42c3e102afc4924f9759011081f1e8cbcee34293c3e1fac522a134fc418429d47e15dbc96cf6afe7119ae313713695d5775171764509717558cdaa77138bb5c7917c37374e8e650960e1d9c4812564fba0f083b60667756233a45099e4d30a1b85764cc27e07b9b1b4c7875fffd9385e603256af0c153c8707e7ef3e9e8120bc9d73b30a870c9b8fe3dd9e3456d1d272616d6dcc1eae4be736c0e26b71ec4f5377b0679b2675575cdb08d902e75a69381cf086dbc7ca2fa51e5a5a08bc65c919bb642ed2bd3fae77adfdf34947647f1cb834c6f2684d7bd3c2ac37e3ebb43f54df2900261ce8a0ddfb4afc58ed4e2ee65ab50ba5150a9e95d17157cea6570d41b1310afc1c1edad0b35ceeb5962e2e3ec662d7d0bbd64f51991787651adcd458b481d9864f656c1f5eb06775402617141d807706ea94c84fb8bc0eda73c0d583f1cca2d5fc2b6d1829d4fd58d9720c2c6af1faa8b468d08085c9726fad1e4c8357aa37fe02126162dba5b503061c453369608408b550c892cc14ad1e72578e4b5b71b25fc959dd420169d55ac9fc7ab110489b17dabdfdaff0c3de57417f1d86bef39781cf7e2b609326dd6b5cb3dc2f388a9a63e5155f6c7a23f7bc8efaea60b791ce2888aebf2e1d18925b1b252b53c08c32166cc0eb3dab09e8590faa9fa67cfcf988c6a02d7a8a8796199a631f916f5b5b694a0f4dcdc44ded7096b6cf7bf82ad132365e73821509d2010b4a1220dc0f592eadb1d2432e3015c357b9b3d149283e643f948ed76b192501b687c09652026a82c8de409dc8bcb72de254339b3c4e9e56ca314c726609d1c58a1ab79211aa48c04350ff10d153a2b89361cea6c77820481d5fd5aee81485fa50ec2a2f7ae7e34ddae46f5fa4c6ce76a60a9f60836c4285f2877d3bb8d80c68f33067536771a3982968489b077f3110fd5e3ac7f42c15c9d89ada3cbeb3bea71d72ec1e1ab27084b8a765188956f1ff2532caaf3c36d4eef3f7f20c2ffb43521e20363e59a5af9a0dd2dcdd7e094e23fd3ae07fcac2e4f7d7cc6c96d843f9c3bbffa2cc0127852ef21073b4898d2406457165a5756003eeea3c98c619a5e7d54c001134819d48ac2c63a93c0c5b870011af3c14bbdfac539561e710b74d9bffe5482450637ffceca2e093203ba201cd426d1c736828767da4fda558138a7d163327e5ac3e50159dac28c72129a29a827a76709f96af4218f11cbf833ecaca12e4866b59ece11e8f39a46f12f7f00ecce40054df6d2bef92b9bd56ebe26d82b5aad7bfcb1912c4de542cf65c12199e6f91839b3c1dabef16d9c106e5bfb86069cbb575703e7183f92e9928beac1c1a8c1c926b671b1ededc1e9a487e4577fe951f1e567686944e175be0748ec2564c8bfa73eb212a7d1ab9728c57f014d3e8d6c91401ea3d97c93fe09cf4fc0d113c34d82a1ee74dbdf6380a5b491cf7a887c61d73a648b28017cedda2e23ddd70de04ad5e2870bf48105b3cea2b41e9cd6236d1c9b529f5cc0bb0b05ac68025eb415542a4f648b7aef9258070b3b24dd19e13daaa56e10d444227440ef16547b87b7fd4e2ea849c4d11c17d83415ab7180f1c81b02ee0598c369bcdbfbbf04b5ea7eb75a20293963d5865ec5587fa355f6a285bc764b47080a5c66aa616b2329ae26ab020ccaaa59163ca5fe5b8ec8d5e14dc928db0cb2d32b3a41522710eb655ac222f4d494b0942ccb980a78147a803402312897c19ac35d7468a1638c5d9bb801a3495dd4808a8f297af55cf6fdab2596a979d74a164780b140b827d87fc645a6fc632f0494c5487ba7c457bfa6978c92ba29e0d1c5e3cefd757b7279d11afd70d87ba11e21d92909c299885a0dbadee58a8f91f312c9a7f691bcdf2346c0e8687eeb433ad18c6bb8ca44b51209034da45e09e75c4d902a3cad098aa5be2eb5b18588fc47f0caeb35a6b2da3f3886eafe007eef120423ea4de1b5f9d65199ab66b7d53d87e7855b2d77cefa3335de6f8c40e73632c20c05813e01ac5df7d420384daa1e3d122095f36287b31fab2bb079132123c629d530f867d8a907bd87a14f06fedb600f1c1928e9dc7a2ebb37877a30a0d552626bdf7719e53809926e26c6e2a674781ff1b70c412948bd8661317a91b88cf798699172d81434ba2cf2444ec6e43cf7ba998fea31909a9721993dbf3c9dd08c311eee5acc276499adb323bc9227bf3e2180ce87e7a8a22605e0a7a50ef579a7a57e08570b545a6ea1dee311800e3394208ac2a9f3b448e1dcfedc61d31fe0e3629cf3863bd280802a95b24f538ecfe597df6b05bc4789fdfd01ec9f38f68e714ebb547382b5e30b72382547500d61281c22d059cf009bef3010b48f227a1aceb397210b09a10eea7486a128ab00c0d4b1e5e1821d384ee6719efba264c3fd41e9218d1e31b12f106d54b88abaf02146a375349d9a31f4e9b0f4b55f2feb7a4d70ed64374a8a57289a0f7ac422e063d0603ef15fd180a4dffc7050dd36cabd7572f4ab54d9b55a8fbb3ea254a0e6bbbc02e8ad7e81f807a693347188badab0f39ab6a13bfd6a79b367fc8c0aafda6da36c76304e300ac4d1f39aa798f5d903210266a077f29c6fe9125921943c8eaae81d74dbde24c16724cd70195946ddfa2bec38bbb3fb72f8646048d62c7ee8fcc440f045e426ccd257edddf53a1343a5cf7374835e62d94a79974288f9f33ca2c181feaefabcffab6c030136dd15da07cc595af2afe4d17b31d580321390f012fbf2d55b99f88e88947c56c8e0c2c155fcdd011a6b965dafc24b86f6cd95dc7bd1d09386f4e75873102ba709f698c831e83d45b9f5994c15b96e6d72cb009511b76a01d0a4f729798f0afc92c03a2a1b83394987a01b090650dae54eefefc94a4b8d453cfd3c2861df2d63578271c2753c9019ac31f82e9d7c24b6b381dd143875610c88bf9707ee580d8c612572546bc2cb165ee3946617ef2f20aa61285fc13a3b446926b3c824fca0bf6bb9776159566074544fd1785398805d8f0dbd7b4a1d6b202c4cf1200fbbfb9e604f73ef0b5b88ca794056ec7920441d054066f3c50eec85b15b72943b4c55fbaf2e6fe639238e2fb51b6a06864ec5cba18c31b9ccefea50173813e872a10104df600ea3ad2470751c3ee0e42967d3bf0765a724c4a494b85c28f33f9062493f8bdda52e93c4e65ec94f5e13e027974004002a45e428b60a59623e5681340f440de83ce8281d2bb5080dc3f27f44f7d7504fdf3cd2b6ab7ad52b7901dd4857918e64192ed19f4195a42ad55608ad09d76e09400d39b4b6754dc68bdcbb62af2eea5201a0c50695fc264008e048c39866eb79d0e379620a6620a26df4171faa2e34eb65b5c0e94168aebd14d259b719acb470ad9e14fa8c1f354b3d940c6e5a1abe2421b2adec70dd40307c11d61a078807e590aed3283880d984a893b95b7ac009737eba9ad8111511f320fcd70d56f4bd4f0f608c928021ed5211217cbb251571453cc0ad0ccd7a9475a94ad2760f77c3216f50bcf86d9a64a5ca3362f4e592a7fa64bdf7bd397b0ddc4a8a1539c96d84af9ab98c21fa482915bc9ce48cf7dc200e4e66a729cdb42c1445778b4035a66c4fe9dea4b07615e3b935de33224b085daa7e98b77fc4c3d39806b38ed7974adaaa1e7786632dd5d377eec86173acd6e28ac779e86804f2f6c95cb8d3cc9632b167927a7b1a716fdda9427383961268f0f4f07a53391fba0f0fb2324b13c7e834bc966f9a3f15718c636c5973229121d95f2eb5d6378d608dc982eac3c1289475158a57f33c6f045af37de76fb28e0d73c53c73599a6b8d8f8c179717b7a47e90d6acf1447c4372e7a2961bce4657d6b576ee064b067ec5118ae40d92ae0eae1e2a4df95dbf412003c331d26c81e469a53a56775bc661cf17bc786935d12183cb4f64423333de82e02156f5129bfe3e8c6f3c408e6206fe5b6109d80f46fc509fa32438bb7c23d499d299de4c40dd94036216f54e13aa90be4631ed56de54c51554ee447e64efca8f5e1a7c4233cdc2cd8e471aa24085c772a528be3e12f00a091e35c254b4537f8cec1096d6ae6350b4716dc417b24664eb198cefd9fdd12a823be4d479f6a1a88aff6bc1e4c75897b43e9c43ca4b39129e53ca1090770362eacb121fd903e357bfbf5abde60f7232f2f7fc50165b6201bdd6d59a4eb5c8183b15febf2e3293fb09374a3acad4ce93e19252b28e9a8cb6bfef2e541bd3422e30925f5d80d060c516b9d4c9477f4de7d89c22eabbb3c9dea6b18a2579b11a999834b88e4129419067ef52078170a29294661e28814cd6f7daf9322c37e10da81e7631bc8abfc7e14fbdf38b971abbe0b2eae59827861413eee78b630e70508b90124a49e5353a0153cc663a3be2c71d4c3fd44c430e7360b0f3c10a92ad3887059bb31a0865c54d7d71ef314e522a2655e66c57f2ef0354022edca9e7fb81b63c805ad43ed823448042c0f8f7e8549f47ddfbdd96be81defddbd6873871dce7bf3c6a95c9950d91eeb02f11eca04b5d3a75bf5bf3b993db9cc0ee1bf9dc2b75c1a6a964824c71515e35453a11ed9131c22191bf0283b431e5c56680a0b6289bcd177ce83892f89aa07a3bdede52d09a9e7fe9f4121155271932c6764fd6900d76ad617e462e519ad9e83b2984970a98df3653d85e3d1c5464b86fc2d4fc85e62000f74bef6d0981b05800aaa72790d019e62f097072c577621931fe412c92e8e8e046a9dcda99619afa2392101bee5b21d94d79a968873a3720b6100a5dec140025417f75cfff4382bc1cfe205b06f0b5471eaf1c7cbbbc5fd3406176cd8e5f24fc925425ff966288bc7baee0c8107a2a1e7b8486ad2784e1b31d58d91a9f05fdc08f714261022f45de89df6d5ce580d760b92a5eab5e99794d1776b98c34bc423207410838758d2861612318cbdc80bc22425d0f39130bad243c50815f10030572d82ed8d5339615ed82bc0e0616bf96cc3be48b2ced61090cf054bfa52a5413c023125108194e54625db61897cb18c688d743407f4cbd2e848d46fd6744e1faf7541a008a5e37c56ecc5e21bb930ac5d276785a23b14e72b7d801a7a435d9ed630722177d317ed028c3d136213ac4d5d7a3b343473034a2f926f59309cd5360799c21191915a8ed40367a9600f18f21a71e5f415bc511dfedca2940e508ed675aac19da260c85ca71f0b92362858188c8111df35800bdc5d89c64d7681d23bebb075f0a62f3ee2a75164f2b6bb7be59652ca9464d206e506ec0658e2af7e3eb96aebe753eb64912d6309a3427393931c24734b8f71d18db88c5dc118f39b92c3bfb97b6e3f43ed059873b05fdb3401874e0aa5dbd010618414ed90f90618b03ba1010ff4d862031d08a5166895b571f817623ee7cc2ec949194b0dab894685d8dddddddddd2db36e92ac558767599665b5134529f28ec90f0773d8a47b3f62f6d5cf63e11629c7c93ed20c75d35ef75eae072e67dfb2932d97c5fe5281439965f7d6cc9bdff325c74316c241767e0dd39eafebeb20a446b568b10536c0a30c17c080044d0b43ea700ef9b908902109b2198c1c7e3a7bdd9683b2c75c14352da359c9961a5614d962493648e61d2574162381124702248ded1d9e672e5b0ddbe059bbbbb117d873f6d87bf68160c061d7796ab01f7e2c01aab5f75a7b2ff65d77ef952de35d194949818814a26811a58b5a5db000b4f09ee052cc0427961d053ec1fd791ed007c8fd433c2484a45fa9d68a6158fdd1c2308c31962d3660b06329f25a6fb70d187c7388b13c2363d87769de5baa4fabb59452b0b117c241f77acc5a1b30d873883b0b71bf9602f3908083e01378be046e804188aabdbdbd3f27b27799a1eceb52e996542531e0cfa154e1074816ff2ca820802b70d8a54aada559b53a5bc0048972e3838b7c26b468686e688634116db4d0820c3500206157c4b01b739138a8e59295a7fcce6497e3c8a379ccd0bd18775d18ca31c641ab04c9c9411b48e0ee5bd302cfb728977e5357d5895224b1c82eb34572b515aa7431be6c8ac6602c8a38c93387778883ae14146972914c20029c05e4c8a61cdc3ab1bb9d28625f2bfbe524fb37c9ac158d8332a94b49f65f925dd57d8278333bb7a35fcbc4f0f387e970cdfb513dd7b776499eebbb412e8c83d9f3027cef905b063cbfd02ab9a5ab74ca37267bfd6caf4b0c12844571453b44e7d0da5c51b63886baa56bba4344eee568fbe6b6bee215efc5d8316b635ba155d9a866d56463583786bde5ba43d589b3607f4f486a0c14282c3c32a07df6dd1329bdd0be10a3c215afa86975a4010089062da8906aadb556e9241792fdd6d1efa67948654b71f96ea9cec1e91c7c4b71d725b55ac30a786e9ba721d1543c4d89b368473428648b6b2a1ec97fbbc9fe9cced1cde97befbdad9d9080c3cd146aacbe39cdd0469a2c359039b2f7df9a90229314a16668cb917dda17640e4c4890a6b5b26fa6ec9f1606d9e29a93feb49646050eb794e6573c593968871d8d33e461470c5a129ed4141df9d7951469bfa99ce42d3559327729647f2d26e675f8a5b166c82b4b4b4d2da5890eba3239dc524db20f21823e43e6fc8f9bb9f70cfb6dd338af5f83e13927250ccf8e38de0ec9f1b487e125b1801ca97cb83d21fb6f29c9e5704bd10d8637475beade3ade91a3554ee7535121c1cd9a13197f214beeaf31dbfb177279fb90f3edab31b56e9b86c7554da5538584bd9bc16206311b12a0e0848d37e594d8ec24169023abaa3464834601b4181bc6e1c6c072cba051007aadfde46439d1406936dab8d26a1cc4d29a38288a83607c6bb90625fb2624bbf57b31eeba7f10a4a7c9e2f4444fffb547ee0b1cdabf21e822f0bf703e141831ea7cca77de1f04800cd124a436e989f3299ff2bda43ce77b916148311901994ea410865192f9468c421a93fdf339719ca29a725fe07e1db3072f043d6610f463afc9ef1c587d7bc47a413c76afc7decbcb0b8c2431c2df9e366aa367942202644f79e97cca1b31ba9c057ca4f21ccfe303e7637c8cef2586d6f1e66ab2268b7b607c73e5e1bef3303e86e7f141c6c77819df8cf179b887f1510a33597ae4e1be0af848e555bed9fae6cdbda2235a7ac2b080436d5c390c29fb5f1a1cd213f7ae8da78be381e3388759e51db2ffc6bdff0e99fba46cb95f0804c5f1e4087b25a0811550a3144f8e360782ea31d3cfbc2176480bf888333b1b47e0f053e873ee17448f793e7dc2137976d3ebdecb7923389e367ada4ab6d8b741c2f6436dccdaa88ddd34509bc78099c1f248694d3384912ca9be15e2a0d2102431d58ad51ac9d5cae44a6bd59c522e774a1cf13348522455b9bead9609bb44ae36897af355285f60d2bdf3ebd141df0d2f604bb2a45a6b0bca0c561758f0a054e91634996ec1cab47e7394565a29ad1f082aa5b47a75f7fa75b4e794d94917a49444cc10460a42881f424c25521079f57fffbdae43f6d7a6c02e0efa3ba07dccd0553d1676b918779d8fd768b80105ffa8b201d8f6d06fc31f35f73bc005f7d62803eebfaeebbaaeeb9ab4ce99ad7d1f33f4cf7ad6d57fbdd694863f6ad79fd6b3d77e3d1c741e0ece10f6c183c29f65ab3f2780fccaa486bc4182e65f1fe290b5c99db41e01e5a04b8983ac64f9a8238d6c49714e5c2ce10a57d8620915e3381a41deb4800062da7477840e8819923636b23b80a83b42f731c016b873f8f9e494522adff35c445ed94a61a3b047e86cdd52ad5d6b0d2aa07301d77badb556e778b0b7d65a6badb5deaeebbe76bc2c19608cde9a302d07c9280eea19641299a729ce21b4a594628b2db6886188f886c11a2a2ca5942dce106e8cc18f67fa794ba52545fe05e92d29822245349c41bc0cd9afcf5bde4ac9309d846471d319b2033159fc5e81436f410187d2060829929c8be174c44ca2255bbc0a54e4cd845d54aae676504a5b2d7b8d2b1823e7468c114f48504a96acc6d44c8d28a6523a744c7c350ed8041cb678bfc5f97d5d0e6f8b0e7644294240284b514acfb44cde78a87df9748e13db37f7438fb6ef05c8a84f93454712ece2001ce0b049a4f9f7da80c15d44b6f8d3245c7687985dbfca4ed35f8fb2650b8cc93b39fd3a640b1170d8a20d18cc65e2146d1b393988c459fc9bc8168db459f2a9dae214f0f53f330cc3340d9b5e2747fb4ab33e225bb4720a29454791fd4f4630956cc1be4e8972c4de3ff34203e4c66480419336c2a692d2260581116204753211514284bcb84dcbb0cbdd8bff41f0fe0f708f6699735e57dbb06ca35a7683dddc9beb86dedc544ba9b5b49bb397bd21c25a50b6b46e52d5351b60a48fa571623f1f97443e1f299ad943d9ff1311204524c0069039fa23c1cd9fec946ab4bd99a513f73f3593e56252b22a6b7dcc10462201e1c4aa5cec9d728a2bb41047403a2bb4469977c375b0d65a2b37046cd61b030ea52a880b40c0250292021234df09981f612a29542b2dc6184b95cbbd5dca908045cc8c9dc2904c594832b5d5869dadfdaaccd7e5c5b0eef46906017d4cbb4f82c39416fe20d191dd52ca29e56782e07fee618309ecc9eea3cf07046b3029606b4a29a5df430819e7ba7ed2c3f140a574efde8c506bad986e56f835e24894843e3ca4a807077c7c0088203eb339ce5f1e77bb65578e87fb1a67e79433e38446e494cb041ccab1d29107c692decd411ca3a361e3268a838dcab28611298d5d21411de0e7068e04f8e0b2a4c965adadb5624b2291bc2f30c8e206d8e1eacb929ad2a95d5b4f8dd65a06ecd9088e6b02bb026949279029b0a6cbbe204f5ac17a53a38c20786f0d0b3504dadedd9a95c07b7fb45a4a29a594526abfbbed7811a1d4dd2a548c9db13536a3a59173ce19ca462ea5943e9234642ffb7b12448304fd3d09a26c14528a3e9fc24a919d63aaa8cdd2bf7fad0782f97d068a61d88f7afdb42707dd8a0e1ab1289b9a1fcde69c73ce3967f6b94c161f1b85a2b4b552d26e300355abd629d12619a314d590bb8e933ac83e0d1670ffcc611db9aeb5d65aeb65ebaa66066a6cf15a5d97102d831021a3740a674bb997f3bfdaa2c072947dadf5baaeeb7ac971385fb77d3e1a0862d7b7eab81eaeebeab6ab1edde3e559172b2419569421c3c6482428254b569ef2195b93fd65c897a2152bce10a5488ef312e20be8deff8271d0ac6faf530a4a76e996c6419c83b6f7b7350ed2ec85a1d279c7ef6fad2d2243f546b2b8146196c898cf909d0c32de6f96f1d913c8902143860c192f63e5ca7478733cd49b1919efaf4171500cb165ed8ad779860cafb6bc0ac5c17e189e55f13a9e15823d5b4af1ec108e674df664915894556263ac8c8376891d2d8d83ab9aceb3acc9b2429ae1da139a94f22f8ed1d1b8a103c441d2eeee6f09ecd65eddf6765ffb841def782f103c7a70c0c7071c24859050aeec5eebc8cd7b91a9130c61b4fd7db961aa220565c4fde60149400f46db6bde17389423105a032b8a8cb62f097881e3a770ffedec002ba1eaa96553d3a6015a3b30bd964d94b43116e5d76bab84c86851d7375e2f0a7befbdd7f3e6c991777c3828b4a80f38c83f630f182c928859be60544ae283110a28e4eb5a24d9dfed4cf66fa2c4ca7013904d6eb857b5e9b62e471e18766da5938b82c3f4dc78581e1b8f134fcb038586101a261aa5ec4f834423080d22d97fd2eef6f4c0659595ca8a524ab3189c554be7b43694b0a104a63f3595558d83d86a295c835931d8d3dfd9a3529355aecacac3b2a13543786583170460d96023451aeb26dbe024fbd360c312d9df86920d2fb2bf0d1b1a8e647f4ff44eb7dfc39222abb2c24770d8417945246806114f94220d34a30909fa879e911938789514613f630cd9bf06f1f2567855e398e520ec2096821469b28589cc816d5709cf35838883f0e8339a9020ec04b9c24138630ca1caaa48f6d74607d529700715aaac623096bf8a55f61a8ae41a44d9e25965fb7d1346c8d70fd12af5ef45e382ec160a2fb29fa0cf30dabeaa9a2c4f68b25f28326d714d70e86179284f8983aa8795ddc3f2b0aeab8e34ab1a1c5299ab3ce9b4a7bf1e3cf70c61241d3c244d4f29a5fc70b27cd0736c9d83bf740a94cc400823200b78e28a2546f6e5773201d7cb1cd6b000983bba1400038eaeef455221ca08480c28236b0019fa48d1cd5ebd5d072aa5fcce81c98d33bf4f16708803e10f0238823832810282a446402612e348fb10c4c84e685f11e0675fc79a488ca3edb30fbb2f6c1f0e50014152a310878b0f168b75af96b19aa47441a51de6a6a39082384812f1218ea4d5fa7ca4fc7c66e85e8cff3598ad9f24459fcf07c49f564b8a14d02d29eabccd10f6a8ab5533cf6b82b5cf435f7b2346d8e7a1df4b12fa2f9f0fdfcbf59a80a980c36e81335d051c76ab4739539a80ed78bf1aeca473f8118d85ce5a8daa548ec91e762bf493765d970d396c980ecb36f6b3ec2792c59dc465fa35be02639f7debf321fba64891c133f4f9b45a9f96c719c1b6cb58163cb20f5abd72d83040d8de41926e853d041cb658adb558ab85a9cc8042b6b7cd1e7df92104101fc8fd1cc8dd238738cca697278259af9417b59eb416ebc66e173dc28830a52126904c9d1ac98ced39b91d5886653158ce093361661892ee013abf7e941da49459645c0536e97777c334cca8448886c30cbd9752ef0a38ac37550c5d49f5c669e4b0de5821ee94524aa96a098d1c5e27f9de7b1365b44f9053ca963de79c73ce8997e0ebed7be370992c3ee5a8d26cfb938087095c3f1d3730c1dd9c5dbb7badb5ef123458564359aba9503132a91907491b6dd468ecb5620aad983acb564cd55ed7755dd775594c5e97b517263d113466ad7d6dc534439a632d9b568438e82b301d530f300d2612549d388d95146d5fb940a326bb178eb08249f6a7612545f79b742f883da80658bdd0246b859f9e64eddfbe7f0d6b49a19f2c13d99f90382d94671f36ad98f25d3149917dcd9b292fec1deecefd9da1ba72967e775c6d349486cab2282d2837acce18348c1dd1b574f9dca0fdb97241490edfdd5b60d35c976865d511d3d9aa574c3967cf9652823c12ecfecaef2cfe3924e8efd393f28a2364c8c7f45e02f31a66b294268b49b6489983481109fa33f1b17c7c2916edeeb6d6d660611103278ec50b887c9aa2b070c1921c766d9f216cacd7c899b22c935266db0d1c09a874be1cb74f866d5e678dd5c4411b270eda15146089aea0285d51ada05662566456522b332b352b362be30acd8a93ec4f653f953fc34ba5a4df1cb52ba81b505224adb85096446f20453d041f406171b08262e2834c3f547d02afa0b207801480209f15d40a6a05552b789f90a4846271ccd0d75abb7348fcb2c5bf4f0a2c6a000e0648d07c77c781e3de550d8b7484a3503132a9991a1bd6484329a56d62c2d42813110785397c7ef0017dce6951286bc59b0512255b04d902091743f2086e16410a1660b46aadf3344bd3241a9151a5504a6477a752a9542a954aa552a9542a4529ad61054cbbbb692a954aa52edc7d7f9aa73ef7f8b7ef6586152eb819bd00e9846194a41f48e7c0c888f3468cb6dfbe174ffdf0d726f17ba932182151479c2f49f61b0f3cb8af4bb3244537260ce42ccd215254bf4bfdac01f7cdd22cdd590a53ea9198d9441415f0d1f65d2649f412b299901f7691691ae2a0142607c9b771046b3f4b59be5660c9dd00c3a563fea839fb3cf5656e95a77e3dd329227846e1d7755dd72ccdd236c268552332576982985aa5544b461a2737ad6ed251ac0c19365253c84e4a8a1090b38e49f6ce02cb37505e130017d87eddaeedbab87edaa27813e5e6eb966cb1e9616620b6c0f60b716ca9d40ea9190fdd6f92aba8998226a59a29428f34d52a07adc5816388ce9d4a7d4960fa49755bc0ad9aa11f76ea934aa5ae4b45e41cb1f6e686084cc4cd0dd58270100807afcbfba2f378248a29e01b60c0df441491de51784ec0a1bcc9420421de6b735d17c773b1aa1d13cb3ec4f2e7574f6bafb6d6aa6999e7190807613f5ffb9ae4a0e739bf17793321c0a409064170ebde7018b4948a555575ec2c6daa131b24fbcb7afd5e8cbb4ed69e73085b6b0ff103cf1cfe942d32077ffbf980a0ead61a83331654154b15787e1738acaa6c22525555652dcd4a4b6930f8481db113d3113ba70e920eaaa3a4b3a4b3eaa43a331dd9ddf7fe8bdefe077c51fcacfd6ffb9a94a0f9d6ca1cea1756265382e8dbd7eebdd65a6badd73967b78e583ba227de4b841475eec81bfb756274287e1c52b4ea14e91ce944d191a2237644dbc3e22fc4aab5b62376c47bc71ad647ea1861914c24d931621b6999963c00c9e22f3d94019912c2fe89112f1cf31e415624921471242942003502ce02befe76866dabd58fead2d669fb37f5fa354c101c5e143cbfada8944422594bd361652556bd61d938694111622ad92044288b45b529606d955d1bbbbb291d6f0aa594d2ebba2e4d0ac9e2af4d917d458b912d2ee894fc3ba5393ba552a794afe6703ce72e8e674797b783c3f1e8c5f126beb81e500ea232154a1ee2c7cdd80ca5bc7f75d2baafa578ceba977559d995e2d951e6ed90f9ba3e85f3a17685ecaf8d1f5e6d4cf128eb5ed66565578a3747da384395c5d2469636b2ee655d5676711cceb579aaab5d557655acca929365db3cc60f023a5c8753bf6eb5f3eba1ff47cdf62da97ed6b6c5beb0b644d18a1100b313d937d7ddf56686b2b75fa94693712fa89dbbd7140048167fb05b823b1b27d6b69ed083121746b858821d516bad18638c31feb07aefe2038a5a73286df00cb9a8546088391e6456d9c002dbdc9f8bd8a8d911efcb7967db4c2a9eda8883d7e8e0ca9304ee1cf41ba280c58e9400909eacd6da7b5d775543c23226128c90d210243128d188eceea6739bdacfd3fc7c48fff98060d78d4b6652314a8894864c9214f59ca7498a7992f64dd264710293425826fb28f499eca12fa1c9ee2e47dafbfbea03761dc6ac21f39174e7229324769db366a84f2767b1ba50abf5d74ea0f31c6130e44e2d87b215812c4fc0497946e006987b4f3bbabb3112864dd30cdd28cd298339e79c734eecc298266992eed532182c1a3a50a15c26355363c31a69e6ecbaaeebba4e08324cef5e8c5fe331433e6aa1545051c850c35c6215a69214d95f747053027e37e2e0c9c11923850cb50d97f2e02a262605d9bfcb025791dda7709b6ba3027fedefcecf515d7f7efd1a06316405b84b327228736d7f391e64fe7cc091e6f985af69da1d713994ad1eeadf77d40c8517fb3a3b884ffd0bfb897533d43029301f783df6bd45cd100c47c150b9b7f3c1a474606e92a33ac6ac724e4e9f4e319e73bc8e9921771358fa09d3f94d4c0ab2e8aac053647feef3d93d0d07611a06069b73d63a6796655cad2854ad1c8c92e6346064649a00c5093353e8eb72919a8d2560bb044cb32f016399c506787e8f015fcac202ec722f0b1358ca16ebc28345091c76ddddb4d69a612ed91d3bb3c2c7ebbae425ad94224f14dcdaded6ed5d472ae5ee1a462261586d92b1d4b09c6844a4838610011052be6f839b5b6f9ac712011052ce3e1fa683649e12f489657f1e39647f2226f71bfe784568dc73fede7b2fa7719ed37ed37efb1fb4ff817bcea7e33341113c52be16e0508e4524f121e5f117b13de78b4879fcdeef03fe946fced35f0538bffd0f9cef07fcf5e0bcc5e30bec9ae6c9d16734d27ae33c6d7b1e2f2f9c4f4ecb7d2fdba77d52057b60c815099a0fd6b899cc7a726aa67159a67d9635b1c19cb3eaaeebbaaeebbaaeebba6a5d11a58800b9ffcac15fa2838c4891e52e71b2f4775f5f98d22d7186fa090f728ba1c7c9d25cae9798fbfb6f1538ecf1fa1abbc44bc456351d095b410cd4d44e736aa9134a498c0c0d6ba55aa235c9fe73da50825edc9a21ecdef93e3d58e672783930f7875b0eea0087dac90b09aa4db08d145d5d60275a58223b3d42f6e7d848517ded2445ddc50d5a906638420ebcc8fefe35dc0cd7d47278ed12377861068ecd2907da49b6e0126e9d2a6e65a772a6b43e75df550cd4f6841a65d0c10633947a5fe08edee6fcae6aeebd3455758955ca5a2baeb5ce690306cf1cdeecbb977559978b7a3591fd9b90a2be86789709e3aeab4faf52bd5080c3b7cf9af7e5a41685aa7d6b7713d623ce623161924516554c11d69b1d76c861bdb99f65b19c47576a2dcd25a6246195e0a41933e718939a512d69d2b2e9965a86dd6bb3dff6f968f7661877ddff030137785b44dccc39e7b457a6dd3b53a9eeaef762dc75ff20f8f9e08889d1e1225b6cc949d4b72c5aa792898883da1a9151a5504a2ced6ed949d72417b8d65a2a49915d4286bac922457d44da22d9df3291bdbba96d6bb243ba52f655a9b3255b9ad1ba2d908fe3411c3e3724d063a6e6fef7bd6fa5bc32dbda3694b8f884ed875f6f5837f7afffc7301a3a6f5f5365d55a6b12f0bcf4c75565d12e82e8aa177631404abb1b0b29e595b7a90c692f676e7707388bb3847249ce3207ff06d0c84e8313300fdb474313b0067a50e186d5c9ff63f7de7bddb32cab61c996263662b68227352c6b2dca241a3921991955313256b6c9d47d279eddfd09eac8ff9a739bdd3ab4398b7f0c67714c5ae193f39d211f71f41b691cce73388f9ffb5e6a7061062f462f40649860d4263f028911d7f9cef7e2e9c75f07e918d7c11f891fb58ceb00802c8c90b0468c7878fae39ef3f569b2705f8b5b0ec236e998213992f9a5cf200499278c5e8070aa3832e2fc0b9001b860b4b90efe40fa8b168cb84ff15e3e47462f405ac988f349d70148e760c47d49b4a74f606a32b5c973dbedeba1ffba3f6ad63e8f7d99b1e7b1df764d264b0486dd6037d8a4947226d3e55635ac6669de6141a7d3e94818a2105775462e22d7900c254aeeae201dec5eb6d2140eb76959a7d3e1a4743a9d4ea7f31d189ab67158e8607f185e6795daaa5082c008290d319d90a094c4c8a432223768a2058d16363065289d4a2622a21119550aa564892d95582c97169c7ccc900b769122ae740f90fd491f14f009f0fdb04956fbac8390db07434042d83db0c97cb048ed7f79f5eb92e57670d925067749bec7072524d1050c48a491f679eac723cbbe97249e1f5efb5e9204a21ba8f40d7805b5642302000800000243150000200c0886c301b16840ae4b82ac0f14000d637c4a7a5640190944498cc3408aa214520611028c014300406086862600dca65ffa758d088e0c9fbb2a80223a854921dc88db41a38e19c71f2e639496a7f744dc657595975523788ce5a50ba1cf153e2dd552211c9e8bc5d80139f76d0c3e85146879db58f205aced32b4176d5bec0f53ca2e22834ca258ecdaafd24c10685e9ec86e1ed6d9e0008126fe3cf031cfce5c5d169523feabebd20e83ac87a92f953b9624ff57b6ca76a2438acb79f3b1fe96b20f5f6f1852f058bb1d9bc9fdaa614087ad612060e5c03733a5b038b1285a4564be626c8be87a89d94226cf7d2044cf5f9d66456fd05671fbc39b42e5f5e63eabc96d6f790938bff207bf97cf44a2aea26140a70d9f5d5e835580a85454019daa30a0cae0e2933ad423f897084c82f159f28844e09bf65ec415650a9d66c9c427207227f18de17dd4c24ac5a363f0c10c9e1d11845cf8618ff211116793ee4a6e27c7eec081c303b885b9459f5b7739aac05252485fb85f7bb52df86713599cabd5380d6eaa498a483c1ec3ace36930c8477924e2db8c9af4f6435178c5e0ee2d748feb9bec0a0ba42344cfd679604f3df7302488bc4b5552151b37fc8a50d5e9134145e439e6a472766e232c34e08b7a665d3171b582a4003dc6b3a69aa6b568f0430dda12d426c0119563a6846bf4da3c8d07495df82068dc87bf01029b123f8f5b1759afda636fea2f9f779e383506588cf88a2cb373b213e39ab97b7cd54c73700d11db4735437e444b95bc8228c4d7cee30d8e86c93c33f0a0a83d1d422463be6bc7458b48cac2cb1d1816054cf77c899fe3d25e251e7e7065a238bb477cbf1e2a1beadb856a4f542c7882bca20d9994d6dfce419c8164fe5b1a015a4ec39652e722804927eaa3742f24d7d9e159e4e102e263baf083ea3b7114d86197e454b7925e2e7e6ef1f9a159ab1d82f60f09f415b7f8cab9a3b0a7b2c53a95307116028b74b9a0603806115df12ab9f38e8be0a963080b5d848692c00e1a2f6cdabf922224b40bef60245afa354b958637c12cabd2205935d436b14ffcd727fb6762e3966993dc7747879ad4f8530cab94e7f0c2bdd86ce70f18be2f372da00fc5f4f314501cf851e897740e258dab24e1342231feb32478435ac707ae266404e513434b04a869a56922cca61eaa75c509f9a31e74345d81454d8a1cbb081ada731cdbb7d44f713f2391e3954aa386fd0048799eee1f41e403eb24546b78d05ba47656a447916807c372e413c2cd7800f878812309501c19597344adf556ceb5013eb46db954f5ed95429f072f5612ae78424b8c8fb08abce15e71c53ac698e65048d165bf16c097b30d47137b4bfa3caf8ed3f27021764caa4134c153c5533fc2df91043078433b635fc57f15301ec9693f567132862509e779b5399affa58001cb2ff4c371a259ac8aa3501a6b6a4db9c685fbbb337d8e8d014fb366d263b7ac644e50f73dcca9950b976f306a9a471ea58f9c66bc88d314150c3e55435794f18482e7cb367ea1001f220e7f96d591a2f71ebf00445fb9226f80a124c2f88f6419059732a1efb886fbfeb809b4936f70a98bb6e0aecc8ad095c093efdefbb857998afb56a3f4db20759d032ed6f3593d3c148d7663156ad9361f6411285d269b383cb258b5f4d23408f240347c6ef8422f4a60ee6759efcfaf45ecdbce013636b3d3924f66be24ed4efcb47ad48a5608ebd5cfcdac2fe9e8cd536261e21ebba14a827a4b49ce5412fbc77a63f661ad31950404e623c6f065ade08683c42a8b9b22a0d61dea5e2d945ca20a97f46b762e48ddd274c325bde1372ef9dd2cdc6277a892d79c9d0be94c44ac4adfc55cc8c600a1cba04f217989472d92c29364d7ffd6489be6d3f7629138a77a2d2431e931c4ac1f04ad56846f8f2639ddaf2b93746fbd3c4ef401222e153527cfca03b211a01a2a66759b6f929aec638d95011d567318ea67ec855e6784a80813c378295c48f8978aeb1e59d0472629d1c680d188f66f0a1a0ef320bc080546746602fd037a4dbfb87603dc0ec9c4e219a25e0e7d2708ce5adbdc12f912543eaad33921bc2ff158d7cbe9ca3d5894d5205442a6942b6996fca768dc09ae7856c02014577938adde74164ad09012d22ddce24110453ef2624a02df1e9a3e3b2abe3e99d6440556456c4eeb4364e4588a6a22e67f29f2c75c2522fe02a3f5e31fe5557f4700509afc185566af0cd0e26a2c86b7b745c5294c922949156fa354802f8d5a5732530344c3ea36af71e1decfcac3274ce67b1a92b8e2a609e93c83ca5cb208d27b90c0be62138e6466130223993f29436551884f9a2e3488d79b6e2ed1e90eda73d2899587094409d8b7d85f4338d286723395811cb14b0212ee4753d6b85beb09eae7625681bc50eae7e459784af48844cd13d661f588a0b3a5b8cbab5f9c9933b367a3d2fc14f57418b81a8926017dc693d3526d1cd8b1a01c6693138a98d8a78572c152eeb0455bcafb5136a3f6e5ee8730371b612533ff28fe3b62387b48f7d6b5d728ca4077bc44086bb052d6418fc1c17a3d042b71f54b1017b6e25e61940cde3992ec4273a808556e072c2cff02eabe4d01aa7b9bd683fafdf963d447c5c266b5efa0f6764e7627db79d2bd64bffa67069fefbee62c754c0d3921492457b3856cda2ed310777b26d8190a3eaf569ba6ac559d08b9956f9c898e0d4961ecc3ba5793f36caa0ba866c0b773d05f35d87fe5470ea4cbe25121e649b035dd0377a545fc775b507e1a2fe8fea1eee09c20b45d227cc0e2d735f239bab14a719dc93c3c1578024f2b469223e1171a5e2534202fed853be3c860cf879e7c6fce99e184899c61753b2ae5a4e8c9e2f7f634d17b3d5dbd6697210c5653d3d59bc59f9678ca31c04c0c558832a6c68d842d5d6439667625dced9c32e719c4021d6147f6797646bab974e70846b07041fc99f052b1081dcf8cc66c34729d0bee04a8c08b15007aaffacfd237f4c6edd418fe038be65b6f238ad40abdd791ab5a218494184291e2afddf65530f8fb4e341b104263177892bfd5703fc4ad75b595e0698bc161787549da67a43e360bd12f5a85b5c27989954dc45df959980ec6820c85a94fac897ee793bcd8ac0af34187ed1321f9fa308297e8b6f3a3680aeda7cef416bd52a5bba20c8be3ed0cd2b2a818e95e6fc74577ae1e64d27129f866d813cfa6e50b4c26469b7acc61b32e6d95d854660f124f0fc410a36737927221ddb78c6d7fe67ee789655a3f047b8fc565b246d8b22d66d80b70e8c0d00ff61013c8784ba7af8dec22e8b6aec01e5896410c2636db3d2e6059bf1c9f03eedc48f920a5bc9b3110bc1011ae47b90a853eaf3233d832948cbd12b38a2492ff088b8c0608b2671dcae971724df25607de094727c9a8fa01d0c1e9924f4c7c947e284161aa94ee1e510f4661cd7870fc271f7490d8f531cb87d22d075a481755b4d3836433b0814083a00e8da726ef12483ef84578debc5b52667da3af290666687731de0a5a01d8ca3376e290e485dc899514a6ba077709fd23e1bccc66a55e6d95ad0295076942ff35af2efca1bc4e2e1bd188cced66041a5b75a1448e40322c89ee08efdc8a47f252618c41aef71398f4ac34e83f182eb139b1b14dc62014412f429d30a4602c19da5dd8104800a4733bcbcc0e90e42826c0021e7099287b348da5b05765f1b12460daab3472bfa4cf145a17ad339bfc8a868d630cd7b898b3d512bc5fd1f418b5160502e98e37ce8d98489c87e58040dc82eea49133f5ffa317f88bb8e74683d25ead83604dd46d9c5122b831768908cf0881ec369ca3041196b53324e7bd917e39731ab62af37ab3e7d83a43ab6fd48cdc2cef381944fde011b05e667423f353067c3739f64f5be0bcd320045a16919e7186e418085350c66b8411d4f7289b8beeec6b2b37e55b0e855d7bdec2629576ffa6453ab8a6a933cd195bb78a5202cc5abc26e0285aa55097392afe92009d8c9b2283d7d913ec740c9bbb98906469bbf3ac36d051bef3582b0422633790c6888117da680900db163eed28462c0c0a57763174b560560e3f9bee6e8b6df1d572a6fba1bbb673a0cc55d27eb9ac002c67d0179359dbc5cf102d0d8e411287b6aaaf2345c028e11efba3aabce7722f4f91191248f472df8370e7fdd8b122369ca2a39ae310e2c020b967c9a9aded74ab0ea4d032da1408f987a3364bb6a99590c60fb615093a29f342741b935e82b0325bed9582a1f9bf1f9e02b47060707b03ab3fa7833f60e878560871ac603fca29143b443dd88af70b661e7d46bb35aa6af3d45efb3816150237b5f6404954950ba686712464374c505aa4490a6edd3c022f46d6876ee1c36ac98663000730e3b4d412cc1f4e4a3461ff5fc88f2d46267a643e60a00aabe54eac9eec1e3c41656de54d4cb8a528eff3cfb14c64018a3d89a39252af20b55cd335db7290da8ace21ebcab9ba2599542a5f7e4ef3d7a2805cf728e612b1f2f2ca7d335402a29d4f476d1898d5d1e4e420c343d6d880a7fdcab37410d4f7d1eb7df8cab26e60ab802a3414a5b4fdd11b9da55c13b4292d1e9f00749c73e27a3041663263e4f2409acb2bb7cfb0acf359c23b06d6f5e9fe5c0f54e9fa8fb4d058bddebaa05d6e226aec3affb3fbb7e65434477b5b335373fd4446bdbded304b764c5648c2c21ecf93b707c6dd96b3bc7bffdc6d057e53707e52186ac9e6d3db40ce74720e7c0d5579edd483f531c4e2c73863c63c152bc6c72bb54b9efd72a47db6d758c34bc053b1e254d56cd97ae2c54daf01c599c58de1522dcced93e78c9b7835e9b3404cd2c60941bd990ffc9e4e4ecd5234ac2e942faf11e3fec7187fdff51ba722e0465d3715ed34311f90b50b348aba084f1c367dfe2f24158c8e3a6b2ef382acace0d62f936ab42b1caf4381eff65d2b3048088af15adeeda97c06fa7cabdce6802ebd07794e23052f4bf6049f5d86fa5b10f58b5c4093c0ec420222890acb675ba14c9c647db86006f28ff875bc0c263257eabc4538b1498f909dc8223cd90bfcef706069ffef871119b96c6f5d5021290aba2f81da860a507527525baf514389fd3e1a6d8a0634c7da9d5139428e0bfa6bd07a0bdd2ed4c7fa34dc9a72c08100ea06bc5392e744965c66bf8e8d87a9e2f3f65073d7ed77305b7d402a6040a361b23fe1fa3779c425ebd2f947bd4c32576c983ff5a069756907df4246d66ba5858d32905b47e0d320a81c5d92e08d5c26c7d806cb11f4fe7781545a715187649ae8e6f7dbb8c0d4ed0b2a4831425f3183de507f8f339e97ba7e2dc6a9ee732fc2bc6c095d9ebb9219e48acf28ad323176cd0036bc7566133e3e181eced13f7e08c17c07939c2ee903017be4d6e65eb81d8a664cde9e1359738734ca22f4beed2d42d512ce3854f6c2916405b329d7635b33017edfebe362cb28bd47f6015d37059fc02a873833b6974deff97e7d97fd4233e1aae5a5a43ed1fa7024a8dbeaf1b972b9df3dac6eac548106026bc49e2607877460f03a89d0f486a3986ac406478737e30006fac5a03065c9ce86c1d6fb40fb15d679f15aa4e1afac2363f3916a7c57fb2ba997caaf5b4d74db016b0c3499a25957dcf2234225681320972b08fffee2e07caa71fefcd81679ab92f13d752833e412229bbd61696bcd7b2e124f0c025098129744000111f2fd7534be547cd1c466de31fd9c019b3992cab9ea1d5eb8c58767a65ec83ad64d29a76978d77ae013ec4576e8655341483eb70527d078c36577985422be8e3204052bf33424cb9816ea134beff251b89d04fa7b5c019b2dea3c7177bbf82bcb983eb1ab8e9ad8c26b53e0dcf38960c53a8f244cf162ab14162eac46987606c1269e477c80af59f1f387e9bc5fb8326a3f8a77425e2d0c41c7c13438d14a36550c4b6e9e99e0296790a4886fdea0605710290d93584ae999490fbabbe9d18ac2b7f4bcbca066dbf6f1cec0ea70b229036f8d0fd968765f3412c347fd23b83de97d08e7532004ca689eed308314864c334d16de4258c7006e7361c5683f7b6b4c01b48e923365140e90b195f21ca00c40608c5898aa06045b05c70d329c931068c1db40f70947efa93c8bf41ad16c85e5342f44643eb916072e6ba84b545c88ae80d9f7b2861a15a85cf8952e338ed2b772632b8a7e13b253c187b0484ff90d91027871936d1b99393489130e5379f220917b7a2e1917a315d42379454212e7b01a877725040f88a3d685485ab138609ec0c74318071dc15ea384e427e2ff4a1c273789b1977568a6c8662d121c2c622aceb5306a2ad509b760f57f98de39ae1f851b5fb3ce84f60f82751536a8a341950e2ddad0c5bdbcd1634b72614b66cff13aca80b4d70399c28828b34ed69acde3386e1a56d87ee30c354c7a4c53658d6f457d44e1d2b2fb2dc90a1c102a274afbf0d32363bd702fb722fa3a3ff2c5a011e04a0e65b17415086b0302b6824c70fab9f7007442813ffe630be123f19fd7041d1f9885f9df266157acda0ce1b00208171c0bf2ff16c374465707aef66c9a8cbe63d0701cc618264c0e279b154d3d0fa8b2645c8bc9f8bcc7f16b4b18acc1312c0e540bfa37610d2d8bdde03e41994bd98db3835de83b47388652039b940d45c0d775812e16461c2c9dc74fb249a5932851dd4b29420faf416f77937f297970f5067ed0eaf02dc15982f9ad12c2559fe597bc05cfec648e25ef4dd4b39a0b7ca8088c2c93d2994d364e2a3afd9a9e1a4e18d08425191484e6ac2fe4b400b844795fb01a6c88eb122acefa225b1a771f1babe0bd6e31dd8ea8a4eb365ab8d60c4b136e02eec49b21a324421b76c2940ad32e177f5a7c6c8671ce3a7483c268e1497d002483503316f2b12516a44df873098384413d6d41369ab7af21a886808235217a5adad1e0e6e5fc08c851b695870c3f76676a978b981610b072f691c5e1c4e58659deb01d3452e72ea29620831e152bc805db5c94c184e785cc05d9d31435e1f11d86c629d126c440e05c10ca69924043e1a0a3b18620ce7ffe6e6bb10a8da1c64733c1ffa13504b52bc7c4562b5a2306f22c9dcde131d04546c15c4e0ebd90bbfc03dc7d07e49240974e6d09ee023208ae7d272f57f29389770e5f10e29f044b583588b7e43c364aabf9e81d013f5cb82c38a08574a3963c404a8975c57c8ff992211f919ebf61e7938a2e43c306e2e71ef378967954a4cc1109ac28fe20bb1ca1accb194d44511c5313ef3950953c3b2360cf49514b144cb413ac4966338761b4d8455f696108ceeedcd911a8841bfbcb47dadb18f08249c2149270f2ecb4cd9e4b6933b80cc7039c670453423922a79ae7efddbffb5da7e158a206c259ca883c4a5576934f4b8d21ccafe2235b1e3d11b1f57d31339a204dd57cee0df44a86cf26522d50ea5bf440838265fece68130c7a551fc6131d404aca620403887df098915b6cd8a0aef16f93ae0383adc37eb640fa8b6b2f9cb9ce64b2ec43664a49c114ddab5807e67f4dc4bcfcaec131b2d04ce73996e0e4a16e396581e4dfe561b82278817c46366623a16e85f0d3ea00fdeb12fc22528e330978888a84bf6c9841a26ae1bec9bad7352d1ce392d6d348f307896e2d01cf7a4744b97729972ea6d6f0a23fe51b0fbccfaf7df5e2ea803e4b067f12030b502e368ee0e306c022c35b315677961858a42009bcdd172a222d8a1166f066dbbc45e0b2909a4ee9140162ea67c3847873b15c78b9f32bf32acbec64488ffe6a4ae4dbbc20e7319b345902ec07f0dc23ba292cc9c4190550abc3419b00f2210232ae6a7856583f8cfe692cb5049a8495c91a109f4394f0d557685d6e0a58a3965f21253bf954277576697410d2f12d5594731d30e9dfd5ea14d8063cef82149ff2a3f1cafba9cd26e869a7212c8031fc5d0114c0448146ffea766c61eec9684e3b3f887a27e58668cc45d71ff4a80a5ad0239a126abdb3dbe73623c95b374d2837c18386418046b507cd24a641d44f9b6a733deca929687c1c03af9da869fb0f3dfe75861951bb9b48894693c482c743c1a3e8eaafcb527208229ffc0562e8c7b3d4588f0d799fbb440af6388d289101d756516d7abd923fe82b2f8ad290bdc29dc9b5f5fe377e2b9312a5c0972abea62589b651b02f5a3a6af577e97e0917f8372c81eda93868926a9f94faabbeec74a902e901942873ccdd12305ccd8cd36cdb6381e450e84073840a6056e8cb3866ab64137e63e8d9ab2e155185b84daab0cb0a7b124900f31d2cc40724d9176d6eb0eca4c7680716d32e6d25745c235b1097df9822d50317c5bb866927415cb6f93fb7b244fde84cfb312e3d7147cd202d7d320e023c51e4c54131209b718884231ab409349e4fd6b84b00d3f71251438e458d3a7b441397a2f54230387d6e0cc1d49f980313d93fb03e18969451684d0375bf026426813ec447da0da7db103653494ae00bbdbcdd2d39c70d35040a23c7ed2eebb4e07872619789d032bf9bcc9a57e0e585852e07b7c464bff65e5a517475f8b5a1719f83769868bbbf8a3c9996aebb049a7a5bae9b5c32d21b4df4ee45afa3b178b863c4f568705b2a4698045c824bb5086725a09555e17f6886a46144ba6585bec824d05a30b0e9e428a35ff3632e6e2ac3c8f6fc85d1d1482ac83930f0e05e31971d965079e971ed5429b149e34d0341250a564d24349c0e157a45fdd71ea7ee0608647d75fc7e563a71279db41127fd7d77d52e86df4ea9f721fe9576b6b38c93a90c489960b8f0ae6b30ec7a2afd60ca3d5264cb5d300c1eea8f8e098b4a0a6f4c7eecab23708b92db7cd928120c49d5aadb47bb7113e3e30fc89971c4d48482bf38e73ab635724bb3828c815ecb9130da3384adc3b03207123a8e4fce8ed5c58abbba9bb1373adcddb19e16b6af963d83a8277c7d84e6657459cf21acb8a6807341113d56918f310209e990185f2944a238735c80c0ccf6d52d1a94137a06b8e4c342f7e7405f104cdf8d14477b9f0c82bd03a5056edc4702a5a11d41b50ad7cbe5426eae6311b9b993514d918e445568966191e26b3f561f821b8c408d8938454eb267d9d42112260cde13ce2f9899b65dbdede9b8539f971275f47cadf3501604e12c08b353ad1e3ab52a58147102eb55ea495031d7ebea8bbc4807409748c67a69545a40d4d4c14572986d7321034cfd14065d2590cfcbb701aac6fc28caeee8ccbcd1ead3af68d6ef0ff97b7f85e9a28a96b71653b947631d8bcce23bb020a1d574e6449c24ccd1d719907c034ded12d921ad74167c2a032497daaae46518ec17129ad7096fc50c91c667a3d30a97f6a856b8d390c3a280908f5575754ed4399b4bf8e33d9e764e9dab3bf0c95a5fdd319ea045ef1718c692d3009686f43222f41dad53c48d33f5ff5230b41ac9e75fe51f91fc5e85bccbb9676cf96a93150935f75e7eef55a7d1e0e3a613380ffcb1ca2b614821eebdaa6f832b842ebb91567741a8f9e5d84f189e41d4cb65eeb94100c8f7093d295b0daa1283608027f55f7474041c9a5a1cfb6805b65d50f019ce5a99232e2e3140e31d5d57edcd03432f70ff48d77759ef8bea1cb01d099be4a889bca5939c33fd3e0daa6ed8dc4768b0668ae6f3db3e6fd1cbb6a85da56340094e7d1ffc325670f84a5ee4939923ee192ca40e79971bb8625f57fd5aca4dd229b6e6d454c6f974cbda939aa74bb681ea6d293f1f5df2a254303b7f2e2f40924121125e431605dbbbda93bc410073b86d62bac7d3f53c8ae615b2c2363c02475b861d192b2eb4b442d11cca5bf2bb6c940842f3b0394cc3d411cc44eb5c3de4a16ef4aa0e707d0f3a0b4859f992eef6a36c8154428de03e35c08353d4998a327117fa90faf926508d3ef5507421d22c49c9430e382d266116ad88426a0a5b8da7f4550da15793560a5d3c9bfa1fdc808b778d396d5da9467b8d523dec1b638a206bd7d8884c1b535bbeaec642ea6ead7d3b9f167c13711999166d5f05ed61e8ee4cd4f033705008988638f212ee2b3e8ed5ff4396f7fc8ca4dabaa1ac050c08131520b613c31dd9834052860b3dd4669230546b030b3eab684e9a68a10f8363acba1a7f965d89761272efd0cb4f0f5a1f6efecb2d8a80ea985f1d18f39ed333b420e9cfcf37eca297b804abd844194bae15dd07d802835f43c714674bb15d2fd8dea5cb80a974a0b5ba029483595970d8e1f29022b5686930b1532c97514ee8c1e3d750ac8830b55403f39dfb40ddc8de23d000cb311bd73d5ffeb4a43efc2681296d11279832c626bd84de5a0c515040aa587c46db2cd3d38f66bd1e9752ae2e2d6a4f0d1f0068a03675e5c0474d87f3a40631bc348cce9ec056349d83dff1fb7912792ab4bd8970216b593a0e3753a1f07dd457b84315836334c43f725aaa2d97398d9922d55981c1100686b8812a529d5e56a4d11b3ebaa48d820f5dd509a195f645cab2ab92061fa3601a4494aeb442010f76d41da9155b55d9b714aa089f00cf7247fa4a542a76374f888f1c6c8b69a010dbee65c865bf30229eab1008d4a522a2fe399648728359af7813874ea790e8af9638dd98b8bd6f33104db55108820cb9f9ac6f6e6c3de30a66fe8c7efce94ead3ca9382172e4b8a5b96fd349e0f3099908f32ea59d821c6ac463dc6a6744da57d97baf7892d5e29873b7d9528f94a76ffa8e8c1d930045f45c426af0cc6d4bb7057da85a4fcadb2d38b2b42e49b25eae4ccb394a3d8eec78e1c2ad068240fbd1208dc8ed7ac96d19d4667ff89ca46f97688558c8ca9f1194e098d436c8569962633eeb0154cc38cbd13479305c2e64be2483022cb54e7c79e96a6e32369a02ccdd69007ae7f647f0b7cda410315302bb08f774e6a2f63b07c6739f936d32bf41aa50e4096f925e9d3b7622268747a585c5e268147084729f879863b597b32cb26dc712570f64b1f19ce649b6c82f9f260e19dbba5a636fc4503cd68bc54b1574c304cc56537e65d9feda0a9e93ef4d385d9907d54e0794a261a999b4bea271e8ed3568c2f7b0c097d5e3487f374b1421d6fcb5f1089cc4003fab1de4b035776545b50c8d7c2bd3f4b10990a629c028e0d5596adaec7b5e2d8d9d2e3c9530c20081337a559fec08b1185d515287e3397c72bf4a94f852b7e1b6c64b6b3d0d598246ae273045373fd8c0825e91a67592c92c5b8065c49cab3e0703e430fae4b3c14d55bfaae26516f6bc715057071a972a01f43198d4d37a64981bbd19c77662b502ea6d30694368d41dcc72e6ba3af115a127d947a2b8bd2f3d8de49af3ae85262dd7d26ff4f1703d50649301e7850add0a194c12c6781fbba0eaccb698a2d4aa766c588f183f0b4b0ba52b008210e5165579f47bc064acbf9208a6907bfd4d4d411792e888620e6fdf2a708ac9d1730e1bef4ee2290935b94a3c097b6756b5b70c4e6d1734abf96005b90cc641f07904f2b994d718b0ed473eba06098110694b552e9b99696771333f65586e70adf7e20c5a45316ce6179ed2e77cbc6f28a78ac01fe0ef14e2d18260b50309745686da85871dc194f00c116e9402b320acbf6070db0cdd54f9c4c7798f355b2a4dadbff1193bd803ccd0e64d06461737987e0a393b2d302808c01971565fd2fcca723a5e5fc3710c068cefda4df6a2497c4a059bf031102c58f64b519c71c0e17d1ce432d523892541d4f0ff2dba07383dfe11b300a785b0481797d595ae2f396b20303299a82a47e6f670b0e7771d69ed98283d498284b1d540b42f0dfe1223b54e632a24a643a40a886070f45f660cde023fff568554a05b823c7717a34a4aa9a91669cfa92860905ecc1c5443b3a354269933f2701e95f284ae026366dcf192a5802b9840d7148f72e3f344279418333f250a339155b1faaa8ac140ad732c0c6244c19f028d6a75cd919f1f2e8969da6f28556b751c0496f89da49529ddf6c9e8397ed50587198a494007ccf6f69187c5699d47d40e3f18b4bfe4328aa65d48ff086bf90d0b538b0624e248c39d6a28ddb4466b35358e33337bf83d841c12de1fd4973f8914023d4d72c4230fdbc1704be31c781f5670ecd7793736d598568e9627f7f84d6414bd04ebf7cf2e7b686bcadb4f4d7f90f958e2184f56dddbf225536d11026865137c20bd6ceaeb6718084205f53e75611c1a56eca30a2b4129d8a1ed0722712ec85e9d33533d79c0b9cde5abbf42e831c33a6bef3c08fa8b524766ac0b40050405a000b89550f26bb141f5a319646109a94df15967c4200d7bdd714d3b871dbca281af1ce3ed4b3a78739979a5ffbe786235409f2507f22d80aeb4432c3fc8ae704f93f449303f58ccde1cf5f2cb0fdf7cf0461f86e89993cb91e729b4146c5ab15cd3d99b4bb101d3c6cff0944e22a0fb452e3e146051e1f0d8544730eb96e959a1489f88966da159dcb9c498a2bb6d879a203be30e35e385365bf848ec3932984c9f7f90a8abd8d160bb362dfdaf3d48ffc2ca7c3df8017f1f42db336a32c9322ea909b3aa7d69951e51034a3163e5f8838e368b5b061da8f42a29cc29fad8c94412ff64d815bb6260c47cd0b9cd41ca1a8970211a6557980f1a1454a63d3581a4ada8994646b056d38a834dcae9767b2dcf37320944992b1bc3696eeb22d280b81fe4cbd2135ee3047b3a18724c8d32670d652615857030e35f5acff416c2eddd42b1e3c0edafcb03078f8c16619787d446f8cecc7472b6471b6edc7b8ba37be28e91ea429b8761fd8a7256cee8c42590f70032c781162b62c86c65e5be9f2de9e9d49be23ded5a1b1901bdc5913e0ca4a138a38403d9dc0ed1ebbf155910ee3a8a3b9659232568896bdad6154e6b328d4bf42bf97b69e0fa24f2f2ecb993618a1cab429b6d2eb5c68ae6776baaf9a3803dbbaab0a79eb021c3469715ed315fdae932cf5c8e95e0238aad7114b4d3b6d1daab75bbbf2e579cb26bb53f5bb35377cded2a960b5b0e5b54f4ece9235172f67eb6e9a7a473cc8e3e45ff7904529b370f7302368cf72da7071379b5c00692407945acca2ce40f5f81e4c0e1712b1471923e8ffc1067eb70c5a54a7b50402f2e7522a333e6a428ac8be5263cb2bf88d2dd8aeccbbf3cead12f2d1ed4f56bff673d3940ee2fbe9e2cb955bb1027abc00c4153f8774b531ab953c70c964ae668af1dfcf70f3875d80f72deed8e0fad791a9ca641eaa2aa428483c1d1324ef87f13a9e73da80a4e47ddf00dfcd6ff5430592f1807b89f8373eb65760f0e85c0ad6db55396c5e2f6ac6878108f26813a225d205695f843e046bd5bd9bb1a9c21e2c5d28dd65f8141e9b9b358990e212fe144ab700656078a66c2a8a347b0d3684fd2348e7e775b9250414d3442e6dde27bbeb99de7fdcd8a9ad6517f5c6b3861997d4a30a5b6b4fc3aa2a4ef6b9195c2aee81fd42bc696a929dc805644c01348118e171e747723440144d41da33b8871f872ace86689bf8fdf3b670238d00d03f2243565f80a720a8d2b741a7a00eae1b5401963a1715d1a982bdd4482bd0a6afad0c47a837c4f1c94f8c0009357ca3f498438627fae4b9823e79d8cab766db3380c0dc8455e31421852c744a91e9be55d3dc4ec45704d0b5523f3689fb2cf7992231f9eeb0a7d7ce6809f08971bd367b3747f3e830a4ebb4a747b2001fcaa2621d8f37bd31fec0f2c7c2dfec095c67c360d4c055e7a1695533573a7aaa252e4ed0a46f2eef2ca01eb339f169db4ed3ae2b02557705fe60710bcce6ebbfa631b08d254d5daa6b037bcbeafb03bad064c36e5fb1210d0c4240d7e69b47977d6dd5d6e83bdb6e9d848da16512fadb481a0193af8835ffa5df26b6427aaeb0ab4f79aa7b569b038c2a59784c3ef6b7cae86e70c7e098745ac71d72b549965c312fc7242ce7627da43ca919a6b925fda1c8b623be8588f95d00c64797d9e52d660b5a98959f84343dfe8cd3dfa58389d4289710199f61b7d59c9e384ecc69805cc6310210472914dd9ca19d59f950da534c0e246832444b6004de9cc58e13453448ec0bb27f90e3201cfeb26f801959d62134034edd83c397858bdecf4bbc99728535c31a863b78c48363e06647506d9f9483fc3e52f03c8d4e787f9d8945eecf3416caba8f441c7d7c7864f39b40d472bde37fb1ab68c5648bb30f08f8f239b93852e398c9870644962fc9cf019211aa00517787057e588d3c5978116292213503a0644e28cd0b14f5386c85acab1210b6655689484f4f1c9c4d8a39900f59aea30395a4e24c57b546015cda90c2d565e00a095212de82efbb23c26d316310de31f96bf6d5d9c3c2806c9245e532c52b668eb4ee762304b0068dd1399417951439697d3534d6c5c842b9ba11612471b5475790a7a9737a4415ca3c502c0434827b7bca3d64ee153f204b79724c12bc629b415830977018f59e7e0b403148879b7ae35a7164460cb529ab990305c46814e85b2d16b5401a2824b2ab036541d6f8f003ba46ec400374a84891fc4855f4b34b10d2762cfda505e6cb3bcaad2a75d479e1cdfdbe32e9465a577475006079bc9b9a786bef35be808342bd0723596bc49d30a301085c91bb74e80064999741b8419fe606c23c9f16706f670b37564754e8b1b03446544eff1a400e9758f8a9f1d8de9d3627042207f053bb3e402469f43c40b22ec73915f09c64195b81b3da2f1f9782606b290a5323e89549fbee62eb7171bd4500019c633cbdae56139867d1b6b85351bdbc31a6b90c58bd609cc1d6d273e30ebf2fe5606ef3f2df7dcc340f3a6064e9003cb30db3f9e9cd9eab2a104f342ff4386fe3737b86ddd48fbb0b060f80e69d7122b80ed4392b5667e33a51f96e377bde5ca9665ffaf472cf8d657c0692b360941f542f6a35caaefd452f314345ba69ba0a867055c9238b097b5a61aa6a3329ebbaba8799e276c9e184a73a7cf76e18cac99019269df4e78daba679dd6a757b8021c818c9467ad034d78583173aaf87b9b8e5ee8827168626cda2d3c60bf6c7ba7c8624c1a05c6e58c47d28d095b9b7771ad1456ab5dadb768404a0fdf049c4a1c19125cc3f145d6197d96a7c8a332245c2b3d428d21392ae007c398419ea5a6686dcd1cb8670422899d2abe00243fbaa84e5fc60ac9d30c743de3b6eb023e913227cde93e2693fe8351191b448180ec062a301ace4f38cc8bd3944844b10f213bddf6155d19ad9088f711e7a8876e35fd2e29a2990a6690dadc6a1e3ac1149cb78507cd851662ffbfb1c08403b588a2d6f44d99c8566621ad6b2bdad5564f0313d4e2eebd90f592e7860f1335b59a23eb5dbd9d8baa581decb225b1cc63f08593f50a10f600d3bcb50eb2e9504e1edabe1d08d0687082ab9900ae5c8a1e160398610526f363df5dab7189950a39e92d2531f45b0ed37dd828db740a4f14d22cbf4dda5cb7cafd87e18000ced6e5562ed9e1532ec6a1a8670891a9021e95fac2446b42e8da22ec492d7182182081837e4c57122c5c1d388f003ee3793027538212ce4a2b82013759a0b337ee2a54c8c0c7afa19efe9e155884aa8bd898609ce41f080be3b3dfbf36b369bb5b2b2c51f320050590d626efb7e3312621f92bd59f0c6e9912550b4d128d071690b0e4ea6eb8f11bbc1803ef6aa48724261666f45e89d7431eeed48b73a07231d6847383742263bcaccc8a6ed6c6ba037712b43fed3b1d05072b046b8e16f32148cbc465f6b2d588dd0d8dbd48aa07c0d19feaab7447ece5b47d9a7ed55f96709a7e33eeca4b3aed342659a62fb13d535f0b666440f04406e5acb3fb311c90662b2741e0d0f4e2897a71d45131f1677a40a6ddb1583b4d3f919b894225927c3a14892c4f4e5093100d42c4f4a0ab5ca55d3245a7a3470ce655cada6e929a55be9933312b4f2fb62af4c4c8c37b4288f11dec9a4de80c9cd5021e70f187414ebd179dc06571c240d04340a7bfc3a781ca49130925ed2902b39e89fc6705b6ebb04b6fef51903cb3c62207988b286086ad4e6187a31da1469dba416f0b8fdc4d4f36695ba52fc60e24aa8bd7856b500667b46c1a69d61511b0c4048e82b5a18370376a7c573045e7e816866784c3353a9019e3f291cd5f249c8847d884777ff01c96faea944c2263438d1215146044abd21b14e40d0dfd812d327c97f0eed4ab931a463fcfe9585416bd406251b9edafe8c8be67d64580d431391f1d44d5a086620f931412b72b1bf53f843cea451f77728a757bf134e2a45e4b1f2391bdbdc23f84392c42cefe190e2d5713d26e096e4bc9dd347e84340f9cc49c7a2b625ec8e9b1bc2680bcb33233eb61d123964981d4c368ecec267f5e071fdfff94d6470857c5ca3b8cbc66144fcf50a50272cc80ee5e64dcef06f66693987a8d8e0d0295952e0f0f5b28e7a4e7a0b6a0a967193b4fad4ba2cf99d8fc95f7b4c3b312afea2208dbc9e16dc09bd12399c826c4ae6edc740b0ef41139a4e84f418c323d7a7a8ad6b60dc2fd72583e0a6e8cb01dce1b4efc106344e35c31cf26303057461ac79c7020dea46608fae1ac7c983fc38cb2a578efceb4d2b0d15030328a5ac7b38e160aec93214be35d118792cd334cc8393268268db4e105868bb08dfb1b0011eeaebc73e265346f927933fb875944d927d33d529af404aca0c059d8e63a60b303f8048500dfd2553cd34f66601b3472378769ef5b1ebb21f5a38e76ec1f50ede56f900dada0cca4801cddf592b219ea283c29b3af4fea1a079d55597316f32a86a25cbc4635d011a5d64b6f23ea8f5409eca7b58ef0707def68a00d20758c91d1b94fc55cd5d71acad08ca7dec4353179b3e8b11764ce486735fd7651345e3320fd18e92397bcbc31bdffd19e61a328187962b930075a354d3b158d1766ebe1b43bed533830405376a9b22b1efcfa018a9277005c8749e17bb721ca564dc04c6eec9f0be40d42d860d3933baeea0b61831b22ade9e0d1446aabd7510b1ab97def9074996a14cc9ea791034c7918e3a481df819ecc9ed63d0e07e8eaa7af495ef3e1625694d2e5be05a8fc56e80f6b01ae17643e5d0fb3126eacf284ab2670e42a34ed7fe2418362b5b5038e968a45232fa2c1ad1e6843bd9a7e5bfa3955428e39ea4a4c5723d49862ef5f1744e0beda5c3b80a1f6852ca1d7d3a8ee504a88d382e6ea73905ec86ef2e982dbb6007f776a47b5a2cb85270686e541dd961212fa92d2bcef44033583cea38e8e29e696925a22ebe632a74baa25ce11013667de65fe1e7103f8b12c3e91a7cad7a5db7ca482567d44efd5fc78d60c5f9b0bdd07f34192a77c615e67934642d9b4e38310b0c8e1d093e4f326b40e294279721d62e04a84a04c7ff53ac998230c4218982282a97c75206d906612534ac0fd131ec70451cf8b783d67096979efe8e44902f708781c99516e0bbbd11a30bb22f4d11f227c4c0df30af8bc00276f7f9549d1604e98c96831b17849507618ce77051efe4c24e786f0a81e5e7d4f0898eb7601eb0a14ff70f55f05f36955bfdf43ccbd7fc89fae38bfff9461422439750fc3b7234209146d3d3987b38c7d10e3b7daa0c784740b7e2278796f4c601484c371cce4c80f68c2256bf023fd8e6ed1989b7f1d73d2a57fb068147af27e734a6a88125aa443dc72fac578bf5260508632d98573cf59c5b3ed8f4dbe92813b74b8bc9325c1b482a91d476d19b10dab7e3b6465888d0430312d29d835ab00e27fa04d0208f115be953a34866a3f240bd66cc2c1c81e50a40c92968cff5717eb60794263ea168ea22e389b9e5389d536316138eb9c02531367d7600df68f1632e19a11f227a58e57bf2d84e8b569c61c59990396506b90084f9c06260af5eecb49c63acb5f7dc0e4444528c2d9bddd3d1e33f124158e1cd2c58b96777f8c1168f0b85b9febc1702605e3a499baf2cf351cbd07e1c5f94275c051cfab8a3e3232ad7ecd86646de5754a5a3a5f2e86e058abd5162662d3b4bee23e2c2004c041a1eb39cd8806efdc9201a43f548c992576aa54f5d90f9e66a9aa17e247e2679882ea0798ff380572c00fec21af7c0a0e8daf2c7d4839091076a371d8d24c5ffd582407e6df38beab4ece57fdbfb31500a962a00f01ef5cc0ca27994d9c923e900dc3dc2b5c9f3188d97dc3a9926bbdaf85d2c24a3bdf7e5d695289c97fd8168dae9e3166e93bfb02a004832e278276d4b26df81c1016208dbd8ad55d733b687099f1f347ad743f6aff180955d2bb299456b7dd43d5593f22fb4919a7274079fcff230dcca1e4e015e5867e7f5f1bb9c6bfc918141d9d02e45f58c227e8774e42a7a6d96a60e71e733529c8c1e58f17b738e44877cc5c6437ee471cd2c181c9c238723cce548e4b849275c0253553cc914ed761dcbe39ce211d62664240a267a239a349e6c99facd774d7fdf3ac5a0016574d6976f3b5323de118bdb174df7437b3586195438afd67fa865f434aec9e4563dbec25104933d9655c48e342a95445fed0ecb538bc727bb24bb927eb003a8075d99dc18125235620ee2519023522e492dc5af265b4ce5ffe5acbbb1e0ee76cee130714cf61456150749460efd59746f91b2e543c61e9ec6120680f5895f35b57fb7adceddec3545e83123c3018fa694c7d26bb9e13cbae2bf43a2e3b999f19d91ca9204c19c6c3025d56233ec6624696a3836789cd0759d578c4544e08f2a77248d589b22f4ae62f3843d53f4af18179b0bd56161e2f98e77874c9814b45f2ee30971e973ad0ba71275b50ab2538ee64db95b397bc4bf705ab860d1e6138468c6ae9c67be20f4c6db1d33916ab0a56a4136f8366cd1a0f169cf867fb5ee96c4ec2d658d71c100e827aa1d09c0f6adb8ac9c3ba80808a484e6c8581c1b2d4a271edd1bffc49d5c874aec714b2ae82f1a3e6d681c48f6eb7c7597d79b4f19bbedc13d2c6ddaa5465ae3743005091769e29e249418b7dfcd0058495cd9befbcd32376efc456aed878179bc5d8d912aa05ba8d300bc8fd24a968948d3b63cbd2f46046b21ca5d36298c91b066596e46520a492cca33f7acb0d6407dbdf9cd4dc24e00efa41d6432a92edd42b2be6ef9e7a34a80909098c16b410a62c486107257604c8759edff6b84c55d66b4d03048127aae9c76791223be2ad699ea4acfa4a7cc951badea33bde8afcce650d5270c2ccd99042fb9084faf246a5fe899363d122b194cfa1282ee5a49ec6df333bf245eb0609cf3ae7f90a23b79b6a67f4df342d276a980a8b377afd33464b1281e52868a96369994b0c8e09bdd1681e2f31d68b4a2aa460dbc3bc085f7dc48df5da360816b183a4d0646ee439701a1131303cd8c773ea5ce1ccd04d36bdbde8e1788d1e9685244b72582d026b39618095126c19080be15fce0f094b4c08e8f0aa0e0576836a2bfee4a312a124885a9c69f4e393f07a1f3d37f78e3a1922bd9c33dca5d11297349e2e9f471d681a1518339e47a35015d1da764e504229224ce19debdcee14c4afa5044e4a702329e760cc849294e1d50ff8d1cd80d68d49b5285545ac57a5aa75022c07e5512a4b680faf4e1ee34f78ab7cd6cae8eaee3037ae42402b1551a8abab14c2a82eb5377a3077ba9cf2ecb0ac3ac124ca53ffa53d66a719107eb71b681e179ba95d7796b85c6d2576d744eccbe48f809453a1fe64d496f2119cec4ed8e306bf8ebc2025a1f84f909b9f280a52eff136b01706119446017fa495cf8645f08c20a380d8a8c3515714bf3afd08d883d2ca2a67c4bc373a2ea0377b0451f7a0c019e1b84deb4f1f226836899b0ee6a87d5bede116df6b6fc9dc3f99ca8e8a5475f1f8dec1d4b148030c6100954dd6927f3d8c5730c2e10e240a82733efe0ac2639afe2e2d254f777c61941504fd6b79e26663bbb440ab499e7215215353af2c37eda74e7b46b407f4ad0fa4fc4e590d77887a0a4c04b5f71ec01bed301201df72da641231537f840bca6115fffb40d6336c58583889f7b4ccae6b524fb04c40a17d49d8218a7ea3fb903efddb36ea5eecaa5f66da07b1f63b702100fa830f89be71b031caac7e2a4b473b740d8ee5d27dbeb984dfce78b9969aeedf1782ff530aff7646c8c49d72223ed325a15a5e51363c9e8977c5f10ae14c199e04c7ba799a6d23041ddfae3d51b2de48495511ef6af825a887650b5f95b3bf684a91902171ffaf77a1049ca794a9c800c3779bc775a130605902f233606e5b0b47dbb324f5fc25402dd1d61588ceb27aecd9e63edfb48bfde2ef6d4a85d832d307999115c90c5e644864420374dbd52718fad91418d8fa87d64b8a22732c1d9c64dc21a659b07cfc971d4b193185c5b8c7f638f3d0d2d1f672ad63f9f83d776731b7dd2c8cb5cccbe1d38d236d423c4de166f895d6531f698f5eeaeface4efda182b7e78d23ba768fdac92f75a41420ca774c40631b5908663bb8d579415111933f9305bc4b4a52a0de507de82aecaeb1b950498b3285172fcf5890bab185636227dfacd9d7f154ea7d633d7d91f40a799e73f9975a8ecca980c0cc3631438f3e0cdc7931b1923a418aa984636026f7d7c4f97d1de6dee28ef98fcbbdc0241816d5f58cb053ca1116c71724c9f923b69e6a7ab97db61300b4ee15cc1090e4836357993f23ea6ef6cd280bc7f44ca05f8d2b566672aac59097bf0e3f56b519a6eca4269b0e8992383c9b585f308d0cb45248f96644abfdc141d7888d4cd6b1a3a26565034213e2214a28d8e5bcbb14ffffb4cae91fd1877a74eb9e9140ea6a35fc7669111c3cdd7a136d4f57d56d3696bdd8253f8d3c1e72139fc2823af8ec19b658056ad977d69dc3f6bf754ef30ac06bb8d5d32ca5f13e3206b147cf93639b19cbef251e962cef17df524ceb13945665dfe25f8f6a88c461041c7775ed2a01c2b61a07665607c031e628a1227d83b7a051d9de66bca220ee85ff587e201b72b92e4d5c47f9d1a8dcb84d276b7f4deb7475301cb2304c7d053753773d411e5702757c30a8939520723b46eebc140017b3544240498fa884f108baccda198c470d0f650d1bb0908b283bf90342dc4ae9fc4dbde918e80547fae1be892f85d27887e3bcb368ee9a94acc076183bfa405c1f156391a984d9c10183f9f83183c7cd024f00aa9ef079ed1a163705cff162ea1e7f937f610b058c405df6e7d5ee4a6fa3390d14840986e134b1553b4af2f9dd166fd319858d06927ffa7a9588d59a1aea55a312dff8186fbf448c14b380917bf14fb03adbb5038d8cf71dc13c12221ed5b430013d57598a0317e4f22793f88ead921b842ba3e60c72e18ace1ece83d4dd7c7ae953ae93ee77e75b53d530e8ac30f0d9e051a463ff12a2fdd5aa4daf8647fea2c09962af57751ac2f90f8cd774dd392c6b21924f7876b6e2975bbad1075e145ede5667ee9c75d0e1008a7840fe87a1deb2e1be3eaa64d6eb0198ab9faba9abe275efb4e278289b222edd1475cdee46beefa5ab5a2b1d710fbeed944e9a5d0a11db7672b48b09b5612e9e603c0b3baf0ec9e8568046e2cfa15c73b0e2de271c82340abbb6ae3cba3e01e63cfe6b56a589d027dd669cb0be8c4750627378cf8220a4968383e368cbb12574a51860668eeb6384a4de77a8f97771475f6acedd6cb1086e51efda7227f5a80fe04d286fd7ff76d1ccf050aecae032c92409124423f060c74fc0692bd35d52da49b57550f0b7c7e64baff873e1a8caa4f81268fafbf9d8df631bee48d6c51954735b9da8fada40807550a4b43ac0b6e1c3c0edb25fd5571407da8203b39848d2d4a2c265bd977234843dc161219ed9ffaf7a863668e42679c3de73e7c670475a350a658a03873d009ecd94bd784de56295c5ac56820eab984a7b19011ec0ab2d92c4d0f42476ca542e0a591e9b678b8a9396894c0acf4a063be5e30dba0678f5839e481c21d4e627e17731eaf1e6efa4cddaa39bb751928ee183df79c1f70f4d890067ca68f2751411b0c08cf782c4170efbdbae06438e86589ed728c7801ad9161f488101d4d9ed0c89adf43f9fb1ac2f53c330880f253a7474d410275db58195b2550f73904a3cb6147c40b6a8c4919fe24efa5ed65ec972079c16885b781488ee4a6c60b059e6632d669b6fe37bb466ef6736a2a21c3b988254140f76f2451f1ef4cbdf4c8162b822d6ea902dd90309c504b061d1c288be83740cc0ca3be395ef500fe64bb743917840b0c3c25af12ebf138ff1ceaf07127ce54bc7cfce7bbb412ffdffe07e9aa7ae74ecb259dead979a31c584fd85c630d3ee18af68d0666e7be58601edfded8567e913b67c2d6c636ac56101d20811198c72498f3b30b0d7201da1cb1bd74e12e3ada36d2b295dc3f95a31fffacb3485eb16b6b19570254a027d65652144471e17329503a65c54394925c8d13c3759c89299d5cb2ebd05e4a0d89de502a2d7283a7f5e66ba07d36d895f5829176dda34c0a50b0c608fd009b9a05a99abb7423a73afb1240b9c18ecfecccd43b7cda689b8773e6ba175af5870d3d681d77a391894cc3990708c667c12e5e05b79fa8397f787400545422725682a5bc596633e512e629a48a59c262cc8a650e0695bc415e100bfdd153028c9ae5028346563703008d480963c4bebe6f33d2b41d6a91ff196f631a3aa63f39e583f2b9b92f6c681b7d690563f05c9a16500ea1e420d130a2553fa188cae5a9d8799f7f7ccc2d124215f6904feb436f4f982dfa026da733a0a5e82be81c0e2af7af66ead25d607fb7e62c2508f9a1476689b2707879e3ad950ab54a15afd2a81026f5fc5f8cbf3106203938775f3ada0fbb37d30094a38f1523ef583da1a00d70533e1dd5c466d9ebe15435befd67aafe1eba8cf83d673822c0bb62e9a050b717d6495103c62175a7740bcdcc48347a2d26bce2a81aa97144f154fbe944409236c4311ba0893f360758cdc7fcc131aa7a9b1b9df86ef067035101e06ad31083583709cd2c058fe1248dd11b90830c1e63c7aac12e52b35ce27e9af98115b46acdec3058b668e08466038a873e27a4ef2108b73ecaa82393d9f75c49dc32f62a87c75c43de943209045abb8f856e1db1847840c651ddbbe6e0388421990f12a84ab19642bf50f1356dd507ed4bde651c94b855b8cde74c83e643cacc64ecf650de1a2eaffc4da1f21cea508cb58ceed0dc0a3e907f2aaa42677120655f0b341827b13820c29358344a883718f308733380a66a80ae5a6b341ec72ed5f40fcb557081163cf9b13c1b315aa55880836f4b99812f0e68fea9367b46bd07b999f3b912daf7a339801c305775562829ab9dfe75405d3a6797416603ee871c06084c37c2e00ad23ee22bf88c2b18e8e84fb6de444ef126b52e9d1bf31ad0c9448d1202d12bdabb3037e7898a831134b69e71521ab6f4661648228f7487cf590e7c382a8050088c956a6459532f25ffcac2ed4e95c8d25ab29745d31e07c7ed658bd7730de92baee1a60a096e2348c025f7600495f912418f63a07da8fb70beb82cada9dbd349c5984ac7625faed4838895e2d41808e28caff7ef4cf86345e62e1bc603dfdbdec209db8368fcaef1d40b16a6465c7e899d7af5a82195eb6e75c827c8ad82ef025d60d42ac0750a292548c60a5d20b5bfaeb242c77cd51f627dd97386672a244814cc740b63fc2b0e3a8b09f552c361765b667f29ce57cd6871298271bc963a7316b5c9c61cbc42675349b1dd4e41361b787136841985877972a07a114578defe7f7308c400c98d9f168afe00248f994ce0de1a69ea1dc796c65207999ed5e5e2f46809b5cce14eb8225e0392fc9ac56c59df046e9ee43bb48475eda52b1d4d50457d42e74a21f1cb5e44f00b79f302bae404e4511c8645af627de7a4e1657276dbda0c08ee8a5b271e2c8915dca7928da052b8adb91601a447f62498778ac0d8a4d2e340dae7b3d42af6621b522c254b5168fc40d70d18ac7842588cfa4e154a14d4161156289c42e07908fd27a41947ebecdd699cce403124a217f2889b617ee13e5f1f322e75dd5ce9c8844ea54c62a7fcf2085dc036a86e022fdfd99f737f9e189397defda94f314321778b57702514a0c311059a2e562d4603bafee8b175ed2e8b0505a63c8057e15aa88178901b8f1108ba14d616092f216e16a3981d860ae2cadb02c059149d28f6606ab7260aedeef4ba6f22277f5d15732072230494da8128e9391452aac2a1532fe1a8a50d0c6f507b8d4c738dd21417f031e4834dd90d4e70cbc7bc5339ddebbec1896521b93e61b13f761ba6c5a4209907259a53cc6a52cd9427030e0b5392d1a3fb8e51fa417d1d36c81a441923d2d75319fa4688976d9be2c2da5152928718889851c69044b39023cdc24af22747886f3cdd53a18c4bff3f93a41400bc1124224147ccbdc5536f91ed871d01e441f37a804bd5db8c8343acd0dd13619f35852e85c3c164b53065c4c0295f184919596a29420e1ed1bc1c70d60cd219a17a10f790d60c12c011caf98fd19d414cd03689461244fd708004119235f1b00f55f82e15aaf9f54d6bfa3e94640b8aa6048224498cd44115edd09f843956ec687e9fedd40961defc72e69bac3fd721a00f25278466e33e0a6f8d81b18989e1691c1cbc9ff94fc1297cdfdbddc3235a1e778f82e16835f897f92468149e0c279e95366879c905ccee73832077975d2ed4bce8fc78f929d24c2b3264a83ba6608e9b43aa767988bd9766eace2532bd6953b50d03b304a81fd37ea46e61b2b946acf425dca5df0a5e0de216c1faac1a111af6c415879c2e9117b2b2bbaab9834efc2e91221ab8bec3b6661cdd80da292586f6bf96fccd737388b5e9f22d07fe93a0d3ba39db2ccc3fddc32eccf25841fbf7c72d9f98147321aa88d756841ebb94a10f9dada772733dd9e6d1d83c95cc938f48fb6d4e24a786c242a362a86b328bf38336f6874a88dc3080ccd709d35f73c424d1e93e45ef1da936aab38fabc24378995d5020073aa15c7aa8d8ad9eb809a9c86e75b6e8de6c404bed909bad8f02644b600c6191730d8c66736c8d33a5f7b212794595645add1d6cfd097d5f24ac338523d82c63bd8f4075836729c30a56d2435d9be74e1148d3173e5d8365fbd78ee860b9654b139868f3b1ff46e82c36f64268b7c262a82657ab9bb7d1c94098ee4a594620d78e1bb8d5442465ccb084c6267b029ddd622603fb384207cb2fc9ebe4c9bf50240958b18d66a74ea7cca8655db2b42d278c487a5f6057d188c7acd288bea742ebc5d8803eb55a291b82057045fa8ab0dcd069076eb8299a13c183828e41e11f802c0f4dd30d8ee05f4051bf9d7ef18572714ecba122779b5dc9f31d3f453150fffe19d75873457346bb74c2b5ca9e582ba15b772c540c40cd6c706dd27823c65471115dc4263710876ac54e33b98adca2b041d405604c12bc5332d6be844ae18b283aec4e566dbd78be5c6ab3b141027dcb50ecd0af2c8c8817ed0eb78a828f09893000b36b1d93f68104d65ccbcb53a7c6d668d3081d23f43ebea64cba4c21b3cd358f5ee611a5bfd9a6ead29794c610abbb3ab7f289b679697b04524042e7532bb5bb95a8371a0ba5e620380eb14b44840234cd23f53c244e347e67e82c9ef54da146f45ce0b66d70871f004868a2c8b37a4a1385b7bd00aecc13f80f6e6943efa7f70c42e0473730ea9451ba4aaaed6abd77606d23978cdb29dfd0e21a40fa225144a281c617eb944fddffd253b6b9d0379f30d00c022e79dea43b80b9347874fdcec6dd52dad7238488fa2e69cc800018439f2e982a96d94333a50f0d6be0400aa67364b7153d7ef075c1920076aab8add39cf984327e6690168a093c1db58905bea15250c4ace254c4b1e98c0f827b79a02f165230354238f8d8d8aa0d2e12526134fc7281344052a3d6a2add14b0fdcf01d74233f6806bf74631a0623a4a0327265099232816a48b384d824660a8e5938c17296744eba66dc3d0a1dc5c8e9a4ab84226b0d77547548b5ca2ae8ccb53d045f36b1685b5a3bcaa3511a4586f5121469ec3ee4ef0965eb5f4ce2dfd41e4d37ae40ac33c22589ea7e978d99bf7d9d65497bb6b3a94778086e948e698ca3ca26cfe8f55f5e5ef5f5aac00e246b7674af354d909300a949e2ac19811a08c4f30ebb689fa45572691b07ed980c7ab8a94d541f1dc831aecc4df51c7ff2878efc291eabd08c3ab23783f584a822bd00dd46936b19029784cb23ed372cab7869fc3970b0a74700fde5b01523f0838bdcf5618f25f5d1e1caba30226d8dd16b885a19000db54287da200487eff2d0a6fe3582cc692d3834d75fecd02837dbfae21bd426d1398508f0b253e3f4b00564de73099edb942595b6284915dfe3748ace0c95dc050937ece727b55f94320897e98237eb859452c3f4180b63f4c96b3f02a3db082f63d34c9800a11226924f4eabe04f08fd4c8c856502f2f15d2a71a22e31ae3d91e29242d525d9043f34d1dac6b4e1baa136876751bcb9cf7c316aeb1c6d0fe3a12db82af6fe0f8679b62eae890f57c60e90f8a0974ecb5c7bc3788aa680bdcab6d41a1806feb1665b5ed556a993c2568952991e51049d017b841a85e655b83ecec949f78aaa30a693dece33fe390df3d8a7625b691a3f5d702d08ccd2ff54250487f32fe60a812c5aed811b70c891b1bb1b555d590eac65ce4f89ef2a26c525a35189b298c0e5c23b294ad6c7ec0c7b959abd2389c5d4674af11400f3a5c25aad03a7944430cf2f6e82b8b53eed795cdffb353491e8be71d4c26458b2a1080b461ab2ac0615b00a65faaeea89c46e20e24a1db39bc0b2ef10e88d79db614b2c9654d67a9b43a8d16b774075b85b96e52b564173a99c4e408db680c7c133065be5e3c3be3482cd16bd0210beccc2d5eeb53f2cf0d6dc4114d833345d8405ed33a874ad01cb90d100842f7766bec799dc69d85e56b32a448bbc7161d227a54fe7fa512ebab42aeb8a73d4ac7c2af94092043e2d10cacdf870c122873465e34582f1bd72e62fa5a5434464cef01d9e9b8116c09e7cf4642e1640793fc6da6f5ad3c2fb4baf8abed5c41d6976c9938e0d15c8e0d0a65eb6f596c6c7f2d0d95cfe18af3cda40e2c10c062fa969ed71a6b7e801edc0ff7fce7a8d184ec19765c6811aa94819c12ca9d4a678b891c028d1317e21384b613694c301bbcfa1d2a013aea974ecda0c0ae5956a9deb3e48aa17483aabf32032a476a3442f3476846bfc33a91780abcb703b2309a49fe88f8797437b622bdad31c2ed303444020533c081cbdd5545f113c3b4ae519e7946658afd4873c877593ae323b27cb0618514a53ffcb10ad92045c6269eb287303176b09759cbb9aa4f98668c5195f1e49d5ebe7b7c4eb9915518c3fa8bdb162f9b16d66dc86af472e821656c14f01bbb2961d2e4431cb8cb2c779ac78830bfa41f433468216a5433a93eb056eb8bdffbc27fb583b998ca2f6329405057cfd8c1edbf8891ec7a46d13fb960b69a4b59b29badd2d16061d398b61b4771fca8e4faf18c47b75f9be986a8061868823ea913b6caed3ec07f43e40a644139fa6b83a6a26e61319857eaa9a00517b94c3d2a6236b7137bbeab012d0242ee2f45f24b43a9aa3368b43116925e48e54ef996c06447ad5ba0ae5543d4d8cdac86465d05f10ef55e3e99072c8b0f78c6429b742fdfab56dcb6c1ba5000b0a605a562674d988bdd04e90d708afb8845a594a68405850a1e27303a58d91e74b19494a8f6694f1429587a9e0d61c643126aa9b4d825d1e5a1ec9c6f164a95950cc0a6e53a7206647c8aeb721d742971a220fdd91260d4777f607a572ea2a16f9f3c597892dddf8695fcbb846605d297171640e4ac1e19177c27746f8902a76dd83d3d1eccb7715bb3a2846dde6ae88c03e6643c743e0bf890dfade86e829f4d9c04922ffd911c2aa8f971de5ada151a3def8253668752ce44868367c3520102bf03c6a1a941500e50f351ce754efbba0905c8f42801dc01a2283db4158658ca9a2fd2108cf0c945854a434e024830967aba3afc5d29770413a0650b2e32889522d4b6ab679c5e83b5b47dcfd9a3f7d7ea856102616352e1ac18be68c92ee91a19c7118aa7f9c026ba883f863585ad7be4deb5c6487812aafd39f041cd70135ea81174ea7ece05848b63098923cfe8a7b0992c3421cfa36b81828a9626e935c390ae242d2ff9ddb82ce8febee8d19ac5807d9315a6a978c72c803b76cc2dad40165c838a3a5b03e4367f2607d3919f9b4c27e1d2be02d55e2cfdd778012cc155ff48110d2bd37e79d57df4e761a526d90ac8b5e413c3db1d8b97d9a1681e77f31087d7417d447b2aa67a79081892556495b60cf1acafd31ac4a774451053140847893a03dd11d9991a09372753b9dcb9f6e17783385d24ef4f76bb9591fcf6a67b80c760cec09eecf1297486d1ca741379f6f2ea7ff53780636a25c93b786587f33e9f956bd150c7b84fbdf3f67ba8bb320e63c79de91f659528e1215a82b88ff32e0558dd169800acdb42b5bc93b7f228e59239617bff0cfe8f58344e258b5da193fe01057c2409ff20c16bb976a3cf61176fa9726b1d3b18bf9ad13f49c493ab1d9bd6c7be9690b9b181afa2c9efa0980b2b9516ec5043a345743d421c50e30e120c29699ee1bbf3b7184714affe8c587a9c9bfee8ce071b2e4ec810701d87a0e2ce8128ed8b7b887db9526c2462afd01ed304b2bf9c8139b99352fe083f875a6551f8bb12c4f3f4fa27ab49a08f3117eac2b59b5233a0c09761fb9bd76b73701ac3d7bdbf074b403d720f647f1e3db74f7834fea207a7a504415c1c71af272ecd3997203c389a61d79e21908a48b2d863eb9befb7e69efeb87433e2400a58bee8bebb8e58ca233fb0148143b6677d2999e5929ef14051d1e1e7c443b67ac67baadd824caecab060c1b4e3c8b1db00c0228442d54fe0d10254503a8af353311d3db2618d95fe80b49f6497094830823c2971829adef49ac5899b9c4eec0c078fc1042b69122d9c41fd2641a846c6db07fca5adfcc831b22e3321ef23481f7ca719abc4007f864c7e456e349f9f2aa6cd6bf526e6c81a8315e19285e138133ce95c6f9be713cb1de9954ea7d1d1ed17dad741a3ab1e4ce4c494a5a46bc49b338eb8cd515f647e8b45b38d2f4b21857aa49c3422ac9f9fd45daaed04a5a643da5ff6daa089415088127c103ecbe2d2500645b659f00f67faf3915a284b790ba1fc9e94ca8b871a9d908dc4b064401b3989abed899a923af85f7f1b6996b16069c7cdf6323cd423652536fbf1fc3276ed536fdee007efa68b2f1f993888289f456f019fc4ec0e212f646db5a0a30dc333144081729a84014ebab2487b59b39ae2d0db916f52289a9a2830a9d7fe7e3390b549f4569242347047457ca11d962b843ab8b667d8fa1687c3d6076e3d55bb4688ea4e08631eab2b8811de89537a2a1c8ad20b4e0db06002ad69405dd9f841a2e988c2a845f55861d818751bfd29a6be6c781f28b448944e0883934a7cc94f21dd66be0a32228e06e426578d155c28f05b86b99f07abfb1e27203152eddee0111a8831bd4d48b036e67efb6825ce574711abf852a79779d2a0ee66c184c71dc8d47cc97947bdcc6e171fb39ddc6d1edd595d0006e14d819d52056015d6297d01e72974a99d745931e2f77c97c074d6fecd37a86c90840d09f2f689f6466a239df97f0e91f0fdf0ca5d280fbd467946e0e53eb022894123778d445b1573cdd8347f79f84ba589ab572ec48ce8813fcc96b642ced14c5dec72478bb5ada5ea0c0146e5ab6dcb8c52f25a7f3a4f2815e92c30ae261cde4ce5bb8c2e34447a201d4fe02eae2ed605c409ad5a80391287bc7c5798d7714e401deb06797db6e7925d587080e40d663e75ca95fa03fb112be10fa0015383c701e80ca6a561b4c0f18276af321426904bd10b9e1eb051c0bd8d8884dd7b21ab7ad1b0afa7befbda59452ca9452e305ea051006a51864fe731c7ac6f41c95bd44038c85e5015aeeffb47f77d75a25fb1d1f489794eeeefeffff2fdd9dcaf8546e0fe4bf972ff50aec1555def43db40c39ae050c0aa4bc892e1c874be154c000c9c6a08d2c5d86c749f4f7ce928ec3857b129ffede657e97ab4731cb8a45ae6b72b9b35a49fe5be17757af48e1c66843f67277f74829a574d24a5d2e4a2baba3426175f7f49769c39c37ce39e7b451058ce161261f89c8708cd1d6ea6ad9d82550faa1b91f1b204b97bbe7cb7394329e4cac514a29a59452fbb1d6c6750383ed943a854350324958ab192435dae9f47af9f0f17af978bd4e70a799fc04725493a6cc1da849dd699ec7f8a7699aa6d96072291317f453c560bfeca95d068a1d04011baad0349c1be5fc34edd33e2d6e1cd79d949aa66958761cd7b5eacdab575cdb7346acb56ddb79bc906580dadd3d5bdbb96240d3344d8705798e316a37af50436559966daf18ea8675ac962a9d124722381b5db19565ff5b94b46656a3275b29fd416128901896099918b5d65a6badb5d65abdbb31fe27b1b07c48598531d231606a101da230981eb990bf2e5191c8f588fc55487e901317ac3035c56d35aa28cc5a0af328ac1ae9950f22085889e4c715a657de460ffcc4af307e7a3589c1c0cc891303d69594006126bb90dc4a8e009244c5e292b8deaa69ff884da50a503c737f8288b05185abc2750cb2b0587294910583d85d473e2aaf2ef7d8b7746d526115e7c7a9645f1ebb6c1ae2e4344cca584819066b70336eada2e197420c866022ede032c0990214a42dcb32cfc551499076682594206d5e55bd011e8334726b9ec8eb4a84525d315cd654b518da70784fd956575dd5550c6dde0cc75d65c550e6a1e00d5f20c39da78386b3ccc33a702758f36644970e67de8ca8e11d19d670c49ae752247a3b34bc792e1cd6b0cb8633cc038907128a37c33d22527093507093e49c33a6802ce882a407de7ca3baa487f10f68d00322b2baeaebe63f5786357cd2552c8aa1ba44ab7c6d227f6b06c77f10b1adab276e75497aefe703e22cab0b042c59d60a035557758962a84ad12a8f63855920f55561b6d43d0358b7c85fb5901f572dda00df43ab7cfe01b4252547bce41ab995ccd4e4ef26ae38eed180be28acbaae0056d72bf3aa6b555934d5555759c6f8d9638cd1bb1a220e1dcb32e2665557757d54f5655411058d124ab477d7dafd9193d65a6badda56419f5e61722d0cad1be6c032418d62cafaa2e223274bad2c95a5feff9665b6daf48aeba657b61c2931b6165b6cb1f50c5a1b8638b6c8089190d2211485c31035048761ab421820380c8f28f19ac2860a940f374a9e9327750a87a0643c493c2ccf8c0789a766c6621fb3d6da4a842049721321c8910cda88e5c703c84d84172df24639dba30f7986bcca873c31aff223cf0c7aa5717882b40c8f97dc5a73f4cad3bcfe7b1ae6033e77af484cbf75f3ef69f21e9cfc9e9c5674d17ce84f9e9afcd8e341f22daae841c5d0a3b60c7a5627d5c92373be257bd77f364e5ce93decbf26fffd0b497b4ce096c13fc26342797e6471c14e2c3f36e5ff1e4aaa10c6f47202d235bd9c203bee1021484d7ecf4b7e3caf70c18fc970c17e3dcdab3cf6965cf02fcb649a3039a6794ef13da799e839615392fbb1ee870b624f901eb95083ab0265f796f64f08c7417a947be5f1c7885b44a87d881b4b2ecda05d72a58c1f4f72c462b177f7e1810e79847095c7465e45e47fcf1057642f7dce7f9f1117c4d373724fe88961e2e6c83df204e995c71e213ccf05173a1dcf096c95c704cf29c733c413e339b9e0829e537ecfc913c4c6a9479e538fa2c788878847088ed55aab794e9e53ad1aab6573a334438ab0a55ad1b06a9ac460ae9b1ca9524999a954488098206a62091aa9e4688852dd56a9181071740aa243c218c965a0263920e28a23c8e20f48ec33c6383d50464c2d06d95cad4767a28aaa5c45a9ca3d15953193524a29a59459a4319f4a35274e8cf0a4071307f7e8df5e8c414cdcd851344522514d0b268b3044c9a48ed0b458aa2436340c3ffe89a1fe7fb9d26fbcdc9a29a594de78b99fff692afa904c49a4a14c0e0c55dc99f31f8abaa4332d28e7a2a78473896ea1418366a301040dd3b7747fd9a3f2636b61f6644ff64461fe63d22df405f4b45141611c10eca99322c31486be809e605a06e387fc2ea480b970d9130c130d130d53b455740846134d609cb11229214e0961bc8070412c6154d1a1b9048cd74b0a29329812b66c22bb30bd186385e178ac172bd8b6fff7d831c6f8b2a59432ce3925e794d2a6542aa1d1755dd7755dd7755dd779776771412ec6487e5a840a1da2e911fd69595c777794d89568c86e725c57a251ea5177c487f010d7f21e3ac4a56257ead1d6d17c97a443928dc8ad2238969055105510f9b919eef4046a0a7ac2c5c48e6b3dee4aa518e9293b0dd77b4a111ca4b4aa9b870d12b8a173e89b969ee3c6b0aa1cdf42f3e3067c8baa472277c7d9bf195c10638cb12a07a529e1cab562d5b46cbea54b316f22bdbb29f0a4cac2f27d9f8fc55f8cd1629b95af56d25733477ce856ab54d68e44b77ceabfe741df3c91fe7870c36435235c7554891e635cad8ca8950a57fe67040d624ed4b4629c38f243ad75d6e926937b2a954aa552296befc5988525155958be0fe3d4939ce4e22b204750915c7658c0165168cd10840d664845b6090312624effcd4f93d9c15f7a618a16d7dad343d049f2071c73b806addc4a6ab09221b7921a9cb2b5f7726ebad6e4a6525391a7e76f8a214fb9d46341ee973c4789c77a74d25590bdd703efc77aa03df170afc0bcc3388cc37420b046ae1ef844f28314c7a961cf167c21bbbb3b92ec33d9793892dd9b38c75710183b498ed55f872213ebd0c12eb00e2fb007e31c3dea0fc201261d5ce00ed6c1835fe0f9c1ee4937c3f1053a9e0e76817774b00bece10149ececcb3caa5768c82235c387fb30ffe33f1894dc09ea8040d04972054012a2482249c57ffa10f4a68e2ad905a8495af664dec7e4c080849baa6f7a98ffe1c3ed615b079d73ce393bd8dacedfaf910333e79c73ce39e79c73ce39e79ccf596b571f25a66bfb2d8a1e5edfa2a3b3a524570c3dfd21db25b407568eb4ae578f2c7d9939f959516536a1ae2ab6d56a654fe6a65d8a330f6f17f45df70551d206d2d1d95e942edbb89b65d9666f96659b3d4141b15dc6655ba6651967d3bd0b2ba09cca289d3aa595b39cecba1a6426a3a454461965cc3ccf6f79bc69b6d26cca8c3b792fa3f9b06285c56882c572bec5ed4b0c2ab440dabf0fe38f606461f9be0f0c6691fc27a1f9c4fce748b63243f2e37a4587aaaa613ddaa0901f8683f8fcd08316f9714da27bcb60555d257ca0a854e4ff64e79ccbe1cab8ef362c419ad6d530abd5b68d8b31c69883d66a6d9669dac6495047931fab5a824eba1a8ee4c71b4aa52853a2c4f7d6522742dccbfd17da568f3a57d573a8c8faacae2acf54550ca56c9e8af3a4a7bab6c634b9d103fd64edbd61a636c200c5052b0cd65e8555588555558515d1be448c31aeac75555401f2211af32a4ee9902119ec9a0c76120e6f58c399c58f5d66a6c22aacebf08d185868102120073db1b897c10407ee2d1bd74d8d8216396fc3e67a7e520e1fae8fc8c272ef09c340c53d593f9de20fee639b75a7b48c06e9a0a753d7823967d3b4fb9453e2195544356070c1cfe7856f89f88bb9d323a27e1fcc56515da6ee0f1fc829e6886ae6653e8994524a6982910136b1c820b7c96472af2e2ca6097e9c604a2f31db83a0031cd040cbf0787ac516474c452c5fd5e6cc660575c0c77f593b915939eb55077ab4ab35d0a31e5adc2bacbb7b945276d5d5a3d6964d97046ff7e8b1c583526ed3325b7970adada5b5b2966dd5d61137c618638c91566db3d9e65acc6ca51edd7d4e96f683e973f66cb7271db769993de9b84dcb5e10c29552246adf5858b48ea974304d7386261820a79823aa19948cc964ed49c76d5a66bd136b3b8c33185ed7d4d1bf552aaa39e79c36e33a6deb26b7b1b068df77a7e7eed564aa157fb11cfa9b8d1530345840028bfdffffff7bc360dddac6757d3d1c442d82c144b0cd419d7190917110c5fe9e845940162a8289449d5807e7bf4eeb820eeb74581d5686c17a1471e87fc07a94809b57f1265c1160b01ae68c891130b1e72ab13bcc6f608e82cdf8018ac91019f419972e4460060cfca1ad154d510b8d23c38fb72db6184c0c6d2f1ac79179db924d8b6ed99a606d31cddb9ca06185ac502b820a331b86a1a665295a66b5101566360c434d0b516166c330d4ee546d35ecb84e66f1862bde3aa0efba29a903e9e8ac69b893828241ae07e1d6852761270cc36ddb3aad1386e1b66d9d7653523494ed64eb366edbb8cbba41f9185488a172c54af5a5c6b85cf7ef73a30ed14cca596b5dc9d4d52329e8148ec3f127e602a93f3e39f25b7241ea72ff78d405801ca04008352512484bf9fd6689bfa547153beeafa019eb15ea955425e97add30814e5829c6e93e61f2c18f0a68e203cc405dd445a965d5c4506db149f77e0755e1e65602848c77fe2f254b4896854b9c90a01e4dd3b26ccecfb2ac9b73ce8802a594524a29a594521a05c6185b6be3ba098388e67491c867d3b40086b4c3c78a16c95a908b39e79c2e3e29d77385b8e027dddd638cb5bab5020bcbf76994524a7b74448f34e040f0d0063a0a44f0530cb567d067eac94fd64f613f9103948ab4c3dbe044e2020023f950499c5764d340af9c52d75a95975c72b0d6665996596b391b970b8548775b20b6c5c35a6bade536cb6d2ec28015d65afbff9af6da7c983da74fef6e97cbe572b97a587befe783b1ab86ce5f9adf5dbb8eb2ab41e32ae5a6e468ed342eeb38ae7324aebc759d0f59e55b12d29dd2f7f0f38d3c914f9020fa7ff09aff58f6f6e851e89384fee6c6c6a6a6a62412cd30838a0a082443464aca975c1cece57a0ce7bf6745168ba5815679ac458fafac5a43a98b276cb03a308fa903ebc43a39a5971854e801e291c98f3bf354eabf868ab7cd41d535adab61c32c317cbd9272bd6ecd5a5ff3ae7ffdcd7fefbad9b7707ff5dfe79f0a1553f16752d69220e425c7a023f1a9f4ca13493082c46087c37466d05313df5872bf5eb19e735ffe7f58714187a93d6a87a9c25eb00303bde4840f10b7478f3a57f4ca77aae02a8f3d3f74fa8a0e16f26398910bdad99e1f9e92e7e5bf0eecbf87fd0783fb319cff9e89fb4d020e7751037560a04eac7345cb7073078b5e89118b335a8a2bf6eca91e7927d4e1418f3a58f48a91c6d1b962c9fd5c33e8a90e163dba42874ef0e3ce16ed514177608e23039d2af4e87107c77164512346ab38ae6165cbe018058452419166c719701cd6b55e69427edf263701991fd60f6b0922c167951e695ee70a0178a85305cf051732f875605f07f675703a3938fffdd781753ab02b3c588f9ad5a3f7fce0e901ec68018316d3d66ddbb6cd5adb6db8b70eac03dbb613d6ab890dd60b37e8b8344dd3acbdf7f3c13c5a9aa6699a76afe6c385b52d59dc7998ff297ff5a8bc799ee6756682992bc863ffe1641874771696efc3b154d2bcd27c964c8824fbccc7f103da0929b9decc43aba2a57911c6435d50a4f1f01e5e06442277918bdc9fc3cd719cebe6d564c23a2e4cd435670f5b39d305e94c3ea243076895c75996922223837426c690ad57c7733fdc1e94d650971059b8456ccd49fff80aa89fc36c4d5e37ab24aa5ee9d063bc9493c30e110d2c9c6cefffc67b1da17051aa61ad33214a267584a6a58a496abbfb0b181ddcb859429dcfd3dd5340b1d3700714676080e20b5044c55044a26758a1b8842a03750ae8492f404eea809a0403e4f230d8820e4ca417f1061660e01778071416c9c71be0f10b90cb0e36a6802115e9f00e3300e9418b54648b61d8a3517e1c676228c78b16ebae60e0d0c2c02f6a9d318ca838e4bf0a03e4526447a7835d606097223b5270c773e9e014ece2c12ea6cfca034c76ec020324ddd213dc241797223b60e0179ecb0b0cc39b7171935c60e019f784e2813c9c7814701e6672581e33a44781268da0b7d8628b9c3c6199e3f004e208629e2358c08bce8b2517205c45876610410409224408112246de4806db15c176bd0a620ceb5727aa578470a7c712438f988a333104793ad18b47aae7a30331549122904347b7c46e5525e4971d509362982dceb7d018c6d0b3ad69d9b85c9450beb384ad855b33b2c0a3631400d3b194d6daca2376d227e062860f6030a5b473809c4ff87caa9452bae7b0e69c724a1a261aa6a8d252ba4b29a59c5c8c938689862985868986292716c3042272414ac3e42dd12d9b8db7ea91b631f168321557e4c7200e08f931eea4c89f120ef9b7d3c459d74e2ce349c76d5ad6d1987befbdf75e7c3fcb59adb675271725a74cd8c47931fdf88fc25020f44451ff51199a3af2df0c45922d41e288443870173541dd18f19df4fa4116eb1e17763b5c08002365d87aa817911222c224fd5d059a94ddc830ee550ca2f082df4b494596d52afd0bb66ddb1a65c39d941c409f6fa6857474cee1757408a55572f8985c102be0e2c7fec31360c2a48b1fe3ad2fc861502aed260bf4f71600dd84d2211d3de330203f59e086ff3ee5445ae506cf4d442ed831892356000a7eec030438f0c8c0b764d8dd4f4e4eba462225c3e232279dbb735bcb663689331655bc7b141f32d8d6dd9b7718c69045d117db2b61586d507133bc75ffb66ddb3663ffd50cda8a84d87f3144adbdb6f4aaa62aedacad18dc16485f435235a96fd93e40dcc7dd920bce588fbc16691cb6082d432b864e3cbb7920e64cd4143d1832584dad4fff978426a12bd046018b17156205155e322f4ac321282328a1e98a896e51d22ddb7cbada3ca7445f38bc75772eb76ddbb0ec1c531997436232883305921d8314480671c6417b3069a171cc285a86b0d26dc6be25eb91c51405b11888f364c22483f884c32010eeba136eaba1a55ea12fd494c15c1c910a430ee5e4661b877272b3edc3facc0f2b440d298561a994dd6fea045f578b463ec11b8e2ab1c329c01b08056ba00da3e01468f804770a6e0a4e41e635de40dbf599a8820262798b74414d82c9e1e7e39a8677b4867768de8eccc39d03522853605971efbdf7de1b4336aceeeaaeeeeaaeeeeadad0c3c7bdabbbba723543c8103d9a09c81f3dc42fa5b4a1c74bf34ad7f34a429cc8c78ff8ee677e1e4e0fc4dc05028643690d07436348622d9daf7923b10a0b6c11a8026cadd67533a3055a433f1f9a59aa02e4469cd9ccba7b65cdc0355973be589efb4bb0f7ee4641f16e607d3edddd0dfb4c69eb87f5911fd6677e58ac564d8ac54aa5f2636b62f5e885d08643194e61f528013e035a650f0bd30cf696c9c4fa7c33d670088b4569e704236ad5b60fe682dd02bbc6a53606e2dc413f2f29fe18638b07922ccb32f9f1474e6e245cc1e685dc4a7eb8b267d1ce2ccbb2ec435958be2fdb3c1512bd822b076a5f82f5ff2a9528d689258edcb7840bba4a1543495cb05b436a7338e293d4910f10f78487b59be7abce5528b899e7aa7b842b8a11b839e20c1463c40fe8ff5e3ace6bba6d5bddb66de35e4dc2ce135c7bb76ce92259a9ac2a33b82045a150288aaab4ea406d392d8a12cd89c3952a0aa6d0821b3137625a50239c73da5aabb5d65a6beda4b57a5e9e57ac717a5e73ce686df79cb456d096b7c95598e7e57979683cabaef5d508d22d2f53e3d4a3ee775023e679000024f26300a87ab4c515d008921fd338f588934664b43e19a91e51567e58545941d14764b464189131234395bfc25a32a04039a1de043faddcdda52e798d2be8e697f9164feb77d02d1e289e07710532a4110ea4614fcb03c5e72519ada9a24cd88c6ca95e91d1aab0b8795ccbf3f2bc7a6437ab7d7ae479f5ca7b1ed87f238f15321a87a70acfcbf32a40102e58619e572775fc9fd8b7f68405c389d5085f4c215b4a97dedd52c6d55dddd55dddd55d5d1b6e7885423a726e84c80d5d4af9dd5ad792e26d7254c272aadfd0ffacfdf74a334a69c66aa504210a1a259488eed1c6d8c518630cbd23f214f4597673ce3931adc074af2c568e8c9465d99765d996b5b12e7b6373325306b338d90f8b42a15028c70e62b1a80d2a6eb5d65acbc4b23e2cbba4439bade991638b417b93bb9918da3ccbf26672c88a212ba4553ec5241361d95254f92e674fb8a54759255ccbb22cdb4487442226485145b530f72c8e116ec4e08765a39852f462a2e5a49f39a7665996a569325227476af55a6bad3146ef6131cdacc536c70d3cecacd4c6b549213485d7129278e2464c0b605274959a41a24a62f372b16a9ac4544a561b57dc984a7de8cc07a6e80c3d4265a84c4ccdd023ff518f86ff792a445f5ec531fd91c14f8e8125cb5c7c364dd5af120e39b898fc870fa12f1f4eddde18425c1e380360097c7c4004af2cbc7c4c91e2aab5869b9612d630ace1c97759966d5a4a4daf3c4e01b56896384153840b6e31705335578a019e7ea8f4ac5aab59a84600000000a00013160000200c080583c160403020cbb3181f14800c5e704a745c3a1c0a64490ee4408ca228a48802042002083040880cd99005710c50afadba54a793bff4224110d47c37802b676114503015164c626d49ef0b8a42f8a0ae940875055936c6554393d739505d6f4fd4200c644e253a9c591aa496d4a668f60fa6be33e32a443895ea699d53192d362cb68133b928ae3209fd469fd8fcbd2430ad4c28696c1ed7e808b30bec7486d1e559474badd87df8889c2c600adabf8163f258e3331f8b07999bfcde2dd12b15c20df6e02393c227b048a02ed75f2b0b29b285502005b6be878f7c09eb4ac552a2b4bddf1ff5d3e3c12f6e61602da9959c661f11f4bf21672920605af1708b2952b380e7adf7b7b4b1fabdb0c88bcdab748422a683e3d46a4d0a7330c2b17906eecf425368ce52bd930895fcafb78028a59fe10f93ba21a907ca26de99bb62661d0223408f912b1333eda2a27cbe7eb2a19e5315f02f3b8bdc30fbb95dd9d0178756dd7f0ad4a24faf119f143e8c381135175d8079d6453ba9f38a722462180bb15954e0c254be729c47842dd74eafad183ce02bec0a9f61148735b91b64dda2a00f94efa12953750b72af0bff5264a9d1f0f03158d080bdb26fda469797fd842035f7fde94bcf379f83ac5fc1a98eca56fe523daae79aa851c58974b034c0988d45b1de4f4281f60841b49a8906c77805737d11c7b74424b084c87c8e8dcf80ab75a63a083d703dd5d5c0dee1e2284e1f3e29fd6f93293107e7c76ce862f56d400d7a8c56a9920f22f72e1d1e9f788cce8e09d5666c14b3acc7531d439942ceb814ba5f62f9d70309ab10d26fb4e2001eacb7aadf921f69c442a5cfc4d2cce1684cf0039f395206f665d38221a535b5159c96e07ac21d58a15bc2ecfc150cc150ba286841361088c2b3b40d4f3a22a251a00d2a986880d341369e662db504e9b856ac1c2df858bdcf0071e6181b9b323cd97a5082f438761f3af5e4049ac5895d1404774495c223968a1f025a7cd4f79d7f7fa91e1e5d246d5715ecfc9d4897803043c06240ded8d06a1a764b3af3fb6de5745072782f858010efc2e382f3415ba9da2b2420afa4dd03970a637a1a3b175042d8c26c08e2488d1bf8891d4b5a5570a4deec2e7cdad62c6d72c1d2db05af1d49183646ab28151b3ceda8872aed1a3dc6565ca6c8dbfe8ff948c4b8a8440e54aa800948a3c0f95bcc07805366447a19a1b4c8d5687931a95972913c8843e7a606f8ce902bab01e898034b759f588807484c8b8ec45de6de8417e86a520dc433b5935af717d28476d077444ca96ff7d75da52925e68b4c99da2d0c457f8f909f4127c12dcd255bcfb1f730baf4741be051001beadccdbdfecf58e92ade37379f95cb31f34ddbe5efdfac8557db78242cebb9472a74894679ac6703f6b2973ad95f88486a7838aeab5658007e4211da81022edd814b018ee634460020113d1f91f8e88f6f9b29c9880f6d0216671b0ab297e2fa3de003d408401fdb5b820958d0f99d2a24921a5f8d3e3a654b5ec50c6f4973c8a36f299d2f4db31056746b7c1dc2859b53512a8424c2b13100d01fbce04ba3f21ae31f7d509e0c90469881f00767be316019682df79cac4c4d289ed7f7198c35775f2ce9d1c09067286cab93651927c2f5c81a93af3e6a7c8fe49925910d18745ee23931a1dbd5dc50d2d21df5d31907ce3207882a3f80043f1e78e867167b07aa9b0c044915a3f142c7767f465c315fd33fc76bd6921f8e0092360b824d892e94f5080c45345ded4a335144ec31a38da359c5438a47dcac6d39c5467647dd3c6283fe0357b43d39119f204f63c4c93c2d6f00f53f6bd01d3dd615cd17434123f25139ef844807daa67c156b7e64bbb1862822f3ee1105c78ebf6f41530fda9eab8ec645b2f395333e3d3e92c6efc4e75b069e57c380deabcb5c14799e6770c7da63a76f48e38c2e5c8fa23a6e85a540642fc7da361a4ba43574dd519595e39d9c5f51114cbd5e785222c98a36c4e01529b9d9466f269b803cb8487fd4766147bcfe5463576bc61639449858b66ca794168205036d09a0654434f6ccbe47aa3f6b74800e24ab683ec57b110ba18f4d3e1e70e5a84de5bd1119c450f23071401e810cc8610362159a6ee6eb98e49baf063f910c20c13f60f48af623b20f0c88b361280d9365a8ea192b4c5f8792e824d68f8a4dee9e097bc6f445b2e5f164b10c83d1a7e14a87fc99959085b0003659ed59614806bd4adfca316a8bcb09a9df388868ed80163160e223f5dcf7fdaf9fa2a7570d7d005ddbf5c4a8076498960082e3262e7315a30977b9c4bfe3ce2c20a4a77221995b4e4e72958af2664b291f6a848d1fe98e7081f6719718d137230540d894f0b54743fe1e338f04080e7d25517c79be125b1c8be401c6044332c916991d23d229c0e90924b2024258b6b973be3d8e4b446252ad878fe844d5b53ea3bc45ac1caaee894c6c13fe47c4f425977a5a725ee1f8c9cf7c093d3b152b8277174624d196c223a615eefde46046d5e226899d4e775c5621e5ab5a8c75d997d9b2c1a0e241d0761470b2794f6acc0e93d662bb3e0e16783f1de41475a10df3fc235a0047856ce698abadfaacd0734f9fd692f9a433e020ee71198725503a5d4a09c2d79c18718f18c255801250603465802c54a7a96b71d46ce597a453ae38f1a9bd354f775c77d191294d1f5ba647b43eb47a21042cf4ee89143181a7c8afa71671fea7a4b56a412acc29df6aaf6efe48bd52dc0d73db5243370ccf996e3c9cbbb5b15fbed2f761601d2350e5d01d2b725ba2cf1b7cdc581e7c702920285c0cc9eda56509c1fb871d29bf30bb071449352cfc242f3dac8cd9207b8771ac34cda3abc490f3abd71bdbc9d74af31ebeb6ecc69c07972d74da455396e25e2ada1dc388cc66727be204eed5740c798a812ea210ce6a5e7957d39f57c97c0ecb639b6e41f933be6c91aab1952c461acd1047c26083955bc68654704dcb7396b898e155fb195e73c4940154d30f4495316992763c3a4e1c1939ebe26fe67565e761eda2cc7b45f95a64cd4fa82396a07fe0009f316241179fcbbb3c11666fcb639587fd74e39e90af7d17ee0ce2d7900c28a0178825f1c122e8f0e12e41ec4d0462dfb55dd6cfdf323f7cc3e31e642bf96546dcf6b2ef3249f96294645cf1bc9f53432ee78c77b9fdae14dede4e5e17bd822006c07163d3234b296d4d26da0d3f4a82ac41a08786be13bafaf2c4ede2ade1ad56d3af25c499a7adf1a855884f0c5864ba5afbe96b4e36fad6b64108a0c285d94b5a8e17d83a272f7f5c5b6977820da3d3c16838fc94bbb36619b4fb1247c8099c7e95c77086bdf7d110450af8dc6d3c49a9b6ef36f4643d0b3e10c196132021fd7c065d5c9f6a138c01b04de01e7d6add1c23f89faf4e0a95ee2495f6b6a77b109047586549f924a602085b8f37cf693f9b9c8ad0ee454a297aa1aafcc2f49a46f5b9d826d8246654b468bfa539785973a950d81509db711d95e24e0595574a2efb4bae34dcaba2ad6569fc2bcd3629178a2e9967c2104f01a4a49967adeb563869ab4412935ff9203a125ba95e1ad9d02b2aed40d1dd7e2318685b2dc2279a34169b9ec40d9153f5ed1c44048e508cf5aa12eef596e1fd0abaf9dffa33a9001edf60b16ea8086548da8e4d72792059b8df80205bd112b2b3c68feb350d081b32fe584edd45f8bcb9975baf24803079560a1cb1642db584a191f0c3200c2ce444a8d84d5c545c871fd708b3914c509a8b010d10171ca17ce7da28de3ae7f0202788860b36d217cc5f9a03391c0fbe4a6e5c5165dde212a3624a4199630956f7b1d733c045c0daf5c4a43bbabfa55d012496be59e94aee42593e35eac53944a851db0ec531c8868a0bce17cb6a1ca5a4e1e483912ae7fec0ad2394fa81c0fa6112dcc1d6ea065f29bb87221326d9381389f549d656185885d00067b306ee71149ab12179267c46d69279d915f945e8342ccd6842acc71a616ea8b30d75ca8f4daeabd7c8b6ce2b5877d66b05eb5129d9d0b53475879899abf41c43bdedfdeab800384e118ef71f5c1bb8fd6fc3bd5a31a35bd948669925eeb0afa5d7a60706c3525cfba17c347309cfce7df3a060e68146de48b5a9a93c5490414966e0900dc7d5f34b9fee90791104c72be8bb3083706e3da38c6846b2c111d472a101c0ae8ba4382183232cb29a16a8049984c3252c223c2fd66323e92df0d1a806481708df022e6119dc24b3c3abdfbb6a4c41275e94980c9c680715b5444644273d2d4198675b56acea6c100ec6bb19052b5a68690fb5f5dbd16b8850e886ff43a223763455066e03bc6d030a6ee807ba4fea4ad7203a37caef401b1e94ad31535956eae6dc8cea13c86326399589d2bc24253c52c39fc81a6cce2dcca3513a35a518889190c5a202723475e752041ed2921a5bce62418246d9e6224e4841ca9a3806e7a92974e4606923afdfd12d4890f615c7ad9a4377e52dba7eb0dd4434b7059c5eb7f524f4ccea1e98312d509d253081e7632e48527de0cb06c2ece9573359736606d069ac8c16e7cf40306e36d950569e74988dcdfd6ac59a9147a0ef0456fbbdd793773b4a7732ca1ed6ab051b2c507613ddd82b41ca3e22366354a60047364dd4d74db1b8fca38127dbc593236839e3556b59842c0215e3aa79a08faf63501345cae2d1278783396d5079607c772cc79e1dbac35789e08f169c7e11832daa9a80b3c0d9a9c9cc039a87d8239e133851ed24fc314a56434a4f4dfd58320183cf1326e033408b6f5052caa6fdea3ffd5cdab824a74628b924cf546e60112125bb41596310ed93c318300ba287a7d5b46cd61d893c7fc1c7b46c216900c3ac553519420176aee60d1a705e2ec9da4e890d8ad16c2e6e49469d7cdcb04d329ba1eb3b4dcab8b6c37355c039a9c39f80462b02a2d569b262cc768fe70250be6491333ea8dc1b02261046bcf61e056b97ef7e3e12d7aa343703b4c6d3baf63b8ab7840748b2ec8317811f8551541fb2526be7191703ff08b926757636469299158c981aafa972e8068a75d5091aaa02849b82dd525771b7d4da7f616dfb13a3fd85c6070f3d6c10ad2a2588b47d79a55ef7c36dc8795d853721674525356253061ab33fdc775c814a3946700c1c722f21530b339fa129617d13eccc654f25639361a6bad595b34f6fd54bac6bd5d50424c13e78f1a13227512cad9dd9d0138dbc4422da5d25a59f91a0968f2f851e2d9fcf542170dd99d63fc70afa89661f5db105a56e822ebe6bec665cb679c4a2b14bedae03d30ba8fa9c6da4dd2c3e66aa079c91c37398f8385b7fc704e75020d63ff7e38c175fb563367741a15d6998de5738e9402333de19f2fc824e9976fd12f7ef9fd8de11733f207d1af48dee90cc5c701b78d309d073b73660d63652137fe35fcd47492fe6349ee6497436377a7f50705b9b05d42e5e79914e2e14d67af488b188da123fc8a63f4155c96a426307278c1badab1ed9b84462541979919c35aaa973093ad46bb13cb8d8c9de0f712c0b4ab222284669e6c10b4558d2bb9d70406e91b8a56a0be63b498fa4e2a812a429c6f2c572a3c9f3dba16578f28a455d1b6bad6b8a1be7228324840d5b5f5ee6175daaf7f81657c678ed00b9fd350231b43485de3a04589aa300361a5ce919a74ad38f67fae81a32e4d715fc74854a85fd21bf4dc8c2a34d2d20fe9b9932d8cbbd30a7192afeed60021fdd8f3d0f4bd3c20610d5be43d6c44257de6c1af328d44c09cab2d96586c1f7084b812dbd4dce3389802f4185e6842907f886a068450a408c34f7390435781319cd5bf350e4e2c4b57f440ab68c70aebce6bde8a66c119642cbbd093757f88cea8f62417d1c78cded8c7e362dafe92a733453bc6f2b9472c2d0013a655d1325c00549410281e1503a100d9edc7ceee3dd2c7df426bb8ac9d027355a015a14a91d11497aecdab18ffd47c9cc92b155353bc6873101fffc5f7b6547deb7c0aabc17a1ee7ffa6afdab99488997136f2f81ba3b1c8919222a987b614b76da680ba1d27c0da49cc4a586a1ba8ab58df561e88dd4030f7adb7e8654b394153de34e8d15ed6c6352c1da00bbf0b5f83c00d401ac33cbb35b2103b0677dcd1004a2399a65fd619c6cd1c30768539390cdffaa585f34a5b8340324660039ebe6dda7ebbe2d327a3d2c8ec5d3560f9878c07a986ec42e4c15831498227f1ff6b510097012e84a3c8c772116705ef4ba98106bfb2ef05697de2242af0bb1ee16e13e698e9f960212257bf01bb65d000ea281a0f92fbf8929bb73acff43e7adaeef7654ac8af42eb15ebb1085a9e58e1a2f86c449b1f2568c2f4f14e8217f8f57d25dae78736d87afbe0c93c9db462c6f7e4f9dc60eb9ccbf5214bc2b66429db8aad7861906ac943563d2931ef54551a7ad9a223e3dd66cc4921ae5f13200cc05b930e7c099ac0064049b0ad75fb91e512230fa5a9e0aa359428cedb57eeaca885b2e5b5ceefe51761e78df1bcba06f8d53fc83cc42035a312b50407fb2ff93537cfe605ac72ec65dd94cd1fcd78bf6ab15ca4883d4d42f4ecd69ee4095235026ea66f83ad2a2eb61221c36352e963183931ab1aef7e0dc1fb76b442be80ccc2ac320e52062340a246f84e2c686319852304e4f28d6fd3adfb2c4d215bc224012e77a04d4308ff30ee9401be4d33148b60d8b32f9be86c231cbd13075c5ebadb8366546ab630215c8053bc3541d0eaaf2bae9c7fddf5e8bc49aac0b3925a9a2225a7ce2fa512bca0589e8b91ed7a7c9758b7c337ae22e0961da9ee61dda6e37e2872a8779d83f30402e4f97822f6ced7ecdf433829bfce82fa466bf5cb2f3c96b07edad4530bfa553e256fb90afc248322431a3860b92e59eabb74fb504824d8c12162ed1af9a06a598ce9b2ce793293bd0f0d000273aaf5c808904a9c2d3325b462f914de793a53e5622f744f119525dfdb71b43e3eb4a5dc1d7d40f5c37e3625c6feed1ea7475d1acb96c77086a2961ef7bf98738f28cf6c5c7ace506fa7968223f2156fa81e65eeada629bec55ace80fca090e06529fbc814d4a5af65c5f090225c7e528c3f8c26f880d94924d0a35af82066d46f7152d877bca44433610f35f8bc3e04963bba3955a18d753ca4097896db6f6072ba6e55e4172f8f90d09d1ba5771e9ff1097f42b795b17bc7903656f8ebce9bee3e2022ad8931d2a7854c424842df996cabbb80a48f3a17a623fc038475aa312ab242ebe153b3731db78848b2e6fa05a1463fe928762a3d84ab3e1c5c97a10ba2a24a503311a262daf3a75e90dfed96838bbbf76173877d0e7a7fa35b192735b0b1b859d783ff488075b6cdb305c2743f25b2e079da35f6c4222ed38246dd1f63d81ae87142f75ea7a1a29b5de95e48d8d766d60db29b82bf3bc85f018ad7412f7b3325efb6feb89e166819c78183a0965ef24a4df11a73aec03389803e36ac07c33585e77933f8ff713e6fd6233ded2f0b0da6966c3d7eb72f2002ac091764445eec84cf79973e9a5a447db4e5f1e295a2d73b48b87bac00b8e808fdf2ab989ce19e949c5c0d617e6bae4881fe83035eae69660dc3514757a613d1e90b5f84d92cef51b3ea18ce3241506b8f860adb6e24c665123e86d8e0d3763a2c9a4609725f66ec9351d9a67f56fae7362910af8da1d43ea4be0b1524e69231b4aace1e23b29f7533a60c69852a250268cbf2ef40e51a2a20bc5b8748365a97372117b0b96247ce50b01962f302cff1e83acc4c842dbbe28230f7f61dc1733b840ceec507396dc89b02604b3a96996a1a36a845fcd27252a6183549e4a9782c566645265b08f0a6890a51ce4e563b6f27826d469fbe7aec3d795db28e1aea1145ff44020f7cacd49bca6391f2c20331b48b79a839efd0522b3e7bc41100ead9853524bba47cf1788aae25b5493cd87e3b9263d8092ef8d677f9622f4cd62d3bf35fb36f8465f6f62dd11e212566fc23374db95b07d842911e9b5571ed137dfce1cea316a0e69a1e95bf51d9877dbcf632d95db265ae964aa6ba2293750449f4c28fec58a888991513408f5cda1291da9483740ac595f701d6c1b6d1ca480c5b79e4f3c5342a9a549b3070b5ed3181231a1b6c5f3f541a113608adf9dc25e11ce7277e824f705f631e19b47635a8e6a4f533874cb6ec0c81a0132d01a17f512c756b5880fc7646eaa319bee7480948c139c52688c14ee53bcf93b6dcbd186e008b266e1fccc5fa3117758de641aa064ac56eda9c606e092368eae072f5e133abd5ce198cac8badcf42c76d1e2a78c453b096017922aa22fa08766d347215dc7148b3c09e64063d9f490cafc8851b0050cb8491cc3b0b1da7eb889050b0efd38385ab122033c9b84a5db0216ed9d384b831185d93efa68d0b889b764817b005af73c560d8ed9774cd1a6d801d10b08c239e12e19922847009ec43473451e06f0f5630996afadcbf32c087efaa0e69d549ea7f21ca960518012946381965057beae3d0fcdae339af29e3649f96814226c1a1c661e22ddf1a53d912993efc2a9a41c372bf6cd160f074efb0037511c9e8f656bf457ec60834a1179b2e6248997b4f62767bb65368685fe9c6d85ceee2c4e677f63abe96ce20f081898ce36e7747625d4d959b01f4732e3b367d0638efce3855420d3d19bdadf67e129fa5f71c217b2708ac45885bd44e385eaec8c2a6164b3930d8708dd13832e2c4164f553c995cd5c130a62bd0e893f411804b1e8320e9145c139e4a33ff93e87c449e3df5a735a21a902f626c3b6670b22e652047c76f3124d2bbf930af38db5f6e6c186e6ccc299c53bdade7794c13190226d77cce2ecc56f0eba60384f9d11f913a2b10d706ce2afefc8f5273804cedbf15f95ec0c83734be13df371f84485cbae813783ea67109c29e14ac1dba88d74274a42770586552383591426368de78bdebabeb8d0460811908e7056fd50391fb4e3afdd30c221c0674ea34e6ebe345ec5a0f7123b7bef4915d8088ebaa498500c6a524bc659564bd2f1a1256aa12eadd55cf5df9bff5c6a7814fb34c898ebe5efbd2bd06fd40a84c01d73dc054963da1d8e2e1db757e5da7e0f25c6598b3b4658d8801f990c20a9d222ac4f86bc3a6c2f117ca9f482e31a95501785ac0fe97edf0d1e2330136cc9589139b82a669810101f8044d8c2628b8835c7f416118b716f15c5cb33afbb87c40a726a161054898912152ee98594b9b017331b6a073236382d7f8d79785dc3d63bde95c5260dea8737036a0b5e7b994bb4f451b11f0592f8ef83610a4127e1d4938e6058d448918c367c73e11a37d65074e2ba6adee416554f717a6502314294e03d818e601096b870921e98335f6c866af4baa738410f266a45cf785e64d6360fe60a54e819b9de8052cf8e04d7d69dd76350a5d9777731dd872a3fcd97dc1e31ec687707f984b3db5a4832ac042cd41a3607e493426248652b7f77b2b0bf192349802db94f7f5744974e9957083d26ea73c3a21ea6897d4ac6a48de572f1d987e316db756f751892a1201a94eb40f50eb23e5e11fb098083ee01b79207830998b4b05675e2a1b9856ffb309331f164d6ed181060dfac3addef8a8e6b72c91dcb4daf165cfdf8a9da9e7459ec9cc72cfcaa88272ec0101299813016ab60925e33040aebe6697d96839689df6f2fc52f0a80f82594bb006ac3f522070e0789d71b3d8e7543aa6b79cacce1001ce524de81306c1c1afdfd361ce933b15b18361c79f8bc4d67038892b8ca2f90e8a44a3d9d199e8d3069f612c6dc5e5ac49ed50575b03403ee12cb20d1a0bbe931f094c8ea1242f0c4b3a25d0286dd68f9a568a50dd985a3f4ca34090c15c223d86edc29f82d8cf3d483f6cf7b09d720198b146f0046417306723e05c3087bb5cc05c0abf6afd59b76eddbbb8e50859f3a84d440c174477916664208e300e2ab031a1a0acb2847575767a669a7f7cbc72868a6f79fb416e4c7fd35b1ea049d3a59e5b71834b082cb5c77f8db021f87cd1334e3be8f93c5a22791d841ba3d50a0d1fd6b65280dc34bf6ade47d6206d6c5b5ae60682829f1e132b37548861984adb21a620affd04fd64f3110871a9f0558402cdfd1eab5899a8d37ee6ff92bba501ce6cf5c5113e350ec2e00f46042cf3ac7e79bde55fc8daccacb8d3cfbb9841df3d31dd3b38f2ad85b073e88ad16fb40125a3da9d760904e4f0cd846440fcd844ac5168316beaf7ca98426486928fbe57f68d050c99c0d3823adfd235ceb3e670b884e583256eb293c6d0b89e3e9271753c59de8f39a0b320b93ff0a868f6e09e03cf5573af4c0baf719ef60ccec223c06a340fe21fb678d808f965d1b5a7fb12231ddfd02903a37faf159a34ef11a510ad07e7e885caa547c715db98d8f92d492f9cc1fefdc32f75143076210e986f0cbfa312acb2b77864b72a61c59ab5e1a62aaab5e34b5679f47b87efe53ca000ab52fbe90ff9eca21588e4eee6cfb7cdd998c21c6ea54c9c28d177b27a44466522034ade6157b959490f300eedc3f7e51091ff98a65fe6b84bf02fa2e2706d90232b4caa49062c01015831197608d7edbea2c57f91a836bfbb0a7738acf282bcb37ace928954932074d8908e0fc8ed9de6632cd4ad56fce4399ccef0a631a4bb4c56494588e43cad0eb88d4528de5cc1e0336ecfacb4467ee5a70eee2b6a4846f3460a0cc04fcdc0ddb934edf40552799a5dd22ee7e839241522322444a898c927854a3a168c374382dfbb08ef656b658621788b8d4e1eb16a47ac47c547a9cb20111a384d81e0b935c89378e42de9d195ca2b48f4f7da632e1b692bab94b3a0e7d9cdafde1050f908f263f727a3815c969d6d1e4bff411f1638c5ee01007b98d54069f9f0d43d3cdb2248b01ecaa869c40322211c6d2519d9929c1f9c80f71d99f26a9aad238325bbd929692151ee46e24f00f348f1189231f333fbc9b0ae2846b8a63c6b6df962400dda72681c887794246a1d0e04c4453baa720d9ed4f5e1df8c1741a782f80ba96560caa3f6965ff7764e4e16acf6c12851e9644be66af16a2a5ca5dcddca2f602f7afdba5658129fecec4a5171a0a3a330922d2e96c3edd7442a2264a9088dabcb4bc8126be74172f2b3c682864c664eb235b280f958f236967695e852c4cc9af59318c6692023af89a84513011290788d06fc2a9242c0d3cf32ea9f49a794f6fec1dacaaf91f0e357eb6cc65f05e58885401d0a35aeaebb5f866cf4c9bc757314f7bec63295da4b4abccb72c3e64f86fad2c2286d9b186c73d80c84953febed36c6f5583f0132d941a9fab58287d4bc7fe29a6cf5be69eaa81e486f1078d7811a8757eb6a4ca2389696cf9e30c3bb045892f32c87ae95b30024bc51315f0248f34fd0a57a44e8658492f3f3ce8511f498defcca377009c169cb44dac59ed7583c2d3cf6b86a2395730ad77666d18a382d83e63405da61605cccb14e5c80ef2a2f96f893a13da6e47a556aff9d5ab87941aaaa8be3bdd7aee9bb07fb14faefe8c85b1f84ee66ab16e9ba6c7efe03b573c9e12b8bc0f235f98cfbc4c372e482d26a3506578cb2f0062a02aa3390dc38c195aa06293590afa4e294124224fbd0b4d31dabf649b6fceaf19b3b0d92b972b4f9660a0b8e6fb9d460461e6168e4f2f6c78bcf67a671c770f50b9e93247bec4b190ee70e638cf10f172d8a7e5d56c4b06d1ad18fc94861e5438001cfcb60b994a59701f1ff9e8d0bf9de4a401d7fa422d4dc9bfdd36a0c4c4fb038674510944d54967323eb93e2a534b5f183c6bd586273fa3b05980b6eb4336a3ea3a0ffdfdee600ea588c7d66a564bbf996de762758562a3ce6f790ec5eeabf20191fd5b1ee73ed7ee0d454a68cfbafaa642718b2a1a3c77f085aaee63a105732a50ec4efb454e44bb04b9cc793e30b1003dead1639a85b56264ad40d0535f02aaf14bc8c1de4db1ad16feb018a5d17699069fa2c8e691072f7ca34c3cf379c0ccf119263a9abb863f29ce89c8fbbfe76cd9ccb9f1ad7345f6b00a173476364ca690ab09e69b2aebe1130bdd54504515f0dac7fae4dfba47c2772030e2e636d9be40c8a1172ed111d55c3e029b7e7f0a440922a2f040a4068a6e1d470a6644bb8e8080564fb4c0a6247ea46a15e48a120d5c36e23486128772128699a791bce121a45713a19144ab0051bb0ceaa70811c9e607651bab6b3ef011cc67f74c9b2abfee58a24ba6df6f980d1178eaf0405df75db163d306b2a6f4772f7f5e803a243fc58f49cd0f561fabc94c214ecce76ae4680266336355340f89d5f93e37b9e53ea9f96c2f827d029624a25a58ad65aa255a8204ad455f09c1bf7e11ac5731f479651005adec48fa2095a4b27dacab03e99c39416603092fcae89c0baf23b4a2ba4616a71500dc8503a54f7d559d598cfa70437ddd202f31f04cebe9ce259333124afd6a11bc8d251866b9b6428f9fb2301499a360c0ddb0420e1665c77ee82fbb48ef966d51649d1df559a4ae7ab21372f334f65663b1771705d97832566603c89f81b06448f971f292e761a524ae1b8b931655ea0e74ff5351d53f1117dc2edc4eb344c7afdf3d4a8de9f843b6db2ecdd0d8661e24bb856f87850b8fb08dfbd5d0c0b1e38613acb121730a60fa282cc644cdd89a6fe62bf43c1e5888138663170c3da4d50b89d4b9c3033fb324f09477037b23c8b673a469f5efdf1acd1fc9837245b52bb92c0a34a113bdeebd90c34f14668843e912aa459908e331ed2464b35a711342952f0107f3337473df6ac510fb1b00bc586848a15554c02e32810ae919160d4b4ce622f5b5d3d176afe3fa9fbca210d6a52f5358a2fe8fa1ac14e651c7bf1c5d988b6fa8686915127d4217ed8884f93bb65547168e9e90d8ccd0f27277c2c210f93419e8ac621ac4db9d918552282dc759ee78c0be86fe4594324d14f744aec2ebb8334c50c261bc11711af63a9f36d8e88975be559d0e69894acb0c7d95abe604dd0f1adedd66059595f1258b3433a515566828a061913d696149755da52fbbeea6508cdd764e52ad3d3774c63e38cf190387034112750b33424106ddb202cad7e8cb54042112aad2e9d63f3c85c6a8d004d25e58bd7bb7eb41e56d60f0b880f3e094d60a3e8c33b134309a0be3179e6821c1a19c561133ada2bc74a3dea2e36cc2570df3119fe4dd6c565aa9a4ed2d73ccb9b77e6b6797d3bdc95b0667c1bbd1ade4927db884f20e05b25832ff037c2553b9f69da766e35909e48e4bb6959451d5b9221e20e12a2990afa529d26ac7f05b460d57c12c61a53b712f72f00c612bf95980d9c35a697e25c2ea2722dcc188e041a7413235e35125051e41a027e219c9b9deafbda94866ab291f2f8bc9d18caf500d087448301deda4a3078b4ea23bbd28ae4d2fa2aec9d64c139de7173002a6970e83f42498f52254b74055944b0b0d3b64f9e6a7d29b6fbdf1b70075021dd25fa2d3aff23d39894e89c95541d7667a001a244c1969a97141441960ebf9198ea9c5f2f723bbf21d5dfb606c7e18f7d7e04c5192329c43857d5ff5cb87b97c59472b7cf381ea13d06645e7aaca6b4b6e56b178df8a67fac0114535ac9d79f42ce4a4469097fe956745af2257511865418db9121382b070811ce37023706fe10b439352d0b5b8ac4251c8b23a87a13bafaca4c95bcbbb1931e518a80c7638c8e85c517dd18bcc5a019f3ab68faa03f6be324a56c1c9d62e1c4243498c1ac2ae81c917ea5c0f58605f9f8a541f501b9f6e8acf84342d8e293627ef8d58e1402a50d71a7592f10dba73740d9f616af5b3e38bbf46dd966f842b47d53b4eb8658dd27bc5ef8eb9e4ed2bb070d300ceb4b449cfc8e6071c5f1b79641276b653588bdd3af4add8b148116a04277c2d0fe6f2e2997529adb780711030d5d50db3feb83cd6052ad16edaf3c7a7d72b0a3793d0e82f176800def0fcd6b2011483ebaba8ed2294a30c4bdecf03c07d1f377ce4b58f37c60fd0c9f1bb68992fa70faf9ad9898095e3f4a0bbace604e1ed510423bbc61b2d1b9abb126cfff89b887d012aaa4a62d4269e4eb6f19ae6f487f29eb0c23b6cafd9312b09fcd70e14205f81c279cc765a513b5258846a3be5c9523ffe4ede858c143a0db805aaccc85d975b35798b0ade787d3c0c906486ec5b4558b34d9d211ab9e33b74846251103b854e3d9d42370a7863aba077382e4cb7709ce2976f9b9f1b2e76c1554b0b87940990ecb51932c87b1d88bdc11cfa2f8560963c72154a06529b5f1ad16d2b452c5aae34024db9bb27cd27814430ed764192f9a056948bfc22519425835b480cd4b3cabad1782dad6cdaa40b7ca8a3163b4a1aaf2dbae6badc61c58bf445ef47b218b025264a4a5ff2fa8f449fcd9a4cc7f07debf7de3064cb5cce0b12e2318d50cbda16376ab94a8b93c6bf242558d262f3011761e12669c543b025699377eaf2bade680f2ebb9c1a0508dcbd1c254938b11b980c71c610d92a098c149aa43a6141c44a0a8332719fb23d7afc4978840b05b2e8db0714b92d53d8a8d4c854c30d39a5c4e11f72c69bb749c99f2b41d289d5919dfd108934e37987794a84e2fafafc8b7740ae771cdbed52a207db1c13874060d2e1b2572f7418c83581ffc7730e48400c7ea8d41f1e98ec2700feb471bff116346b3cc4caadb8425e5965d581c6ee86e49d67a81573a37760d19a7f07e30943e0a7f19b6c5d651a12c6818b77e8404c9cc0b8b8447b17e0a535f13b9bf2a97264599d7343121371feb6b57364f1170300a2df919a8b2d8b9e17820b3b19373f9b5b61ee19e5203b64b0cb6cc86f9a861201a4f613510835ea15b1c6a3c49ce1db1de5d27923fb9e8c594186db008de34befaabed880c7d760ad0cefa2962fc30a69c96fdff858ccf1c714241ff728c93aa2f088368163d47b06f4c425966b166a0a2342982eb67e27d623c65419dc8e76b5050eb770e782d4fa33151c622b371071a2289e11b08133fab2d6a6b6cea8b23f89832af8fea866319794816af1963e1221f698dbc78066a991ad68c0e44aff8a41f14bd75eca4f63d876c7bb227a15cb34a812237a04b6f8b51ed6a4509bf907f150f32afac028e83d2280e3d46a4bc1c7c98f275a58970ae08f01710e683ef1837377ede3245a661e2f0b08c5054b57a36492acdd2281a0ffa8f411ea7915286685be37bb3549863185f9189fa5035f4264970cb18f7e7bf0acaea32fc757c2e5919d525e1991997f6e9c361d4e344e9058edc640267b22eb6604f395d098b49e46e4296c45eb921cfe39249cfe20a867d302d82c4451d9240be9f8b6857a62a0487d4128a5db31f6b05ee7d994331ca923f68b1a08c17da59d8107eae9551bea61eaeb13f121af4aebdf015e09ac46ee36b0a5e93641582c057d0f90b4dc439b99d94ea8667692130f6c544d0cd5ed388707a7a410f2af8c6dc11dd7ddb6751f833812080e9accc0c4893a9518013ab2f25992faf467eafd1c66cfc654488aaababe94ed6dba3bcd0753a242cbdc2bc0b2c366f4ec00a07874a8d344c93f22d85b358851d1ac8c62ed30a154689dd5ce25241e3d8e45d98e67f3c1afb9d0e8ef8cda44f19d91b38c06fd9aa430cbab24972554ce1cc6f42f03a71d2fc1ab1bb48e1390bf3c2c5a6c7c7c6d9344b91380fbb9627b29b4c5c0a2646bf4fb9ddea4ddd746e9fce20fe1f61b39139a6c93a8aa1fa2c3a4486e820ff1b185c2796c2910c0daa091ae816db6f02ae12f829e693343f8c6fa8754cb88e8c5db37dbe5be8a05875afde147695c9557d5228a5a26524055398531946d94336eec05391cef1dcd9e0d25280fdc3a6b311defda3ceca67e5e6bacb027e6b3c449915b39d1fbfd48dc9f71b157d620936010a92631b6fea4aa891497b4f2794c1e01ba4a030f8c05cedfee1e392a507a23df5a2a5da0d1334c8fc59e0672566160309a191c0569f1eac485feb3c88be53c454b9d70161196da33e5152cb0c498a90c1a0de1ee0d0907bb59bc5e4e238ca6b951e2d85fe9e8b659a9007ea38114043c22cd266baccfab959540f6f3cf1f1dca41714b225305cac9a36ab036f9317ae2243f1540c499fb1a7426f460922a38814bd98350c48cdbb9a980fed1a210503e707b6f8e91237de99979eb917343d6e86497105695d2b1818a5fa4cc2c050746017900d413b0a8f6066eb3503ee617ed8c22e156339255cf4682f382db89c214320f06ff6461a4888ff8778c2b1fc3358507884d835091c16be8362723e34ae132719059a82306080404fbdc5885e410d574b44e9ad0f09691ebd416e5a100b2576523717ebccde4027326f8a61cad7315698f8f29cc92ded25fb7adab4693fd3a92655b8afbabada203d508433ec14953451b200948b5af90cca6a5b4c67c9cd15f11bb4374545a2f6cc0d7106bd48b249d7c8bc5442781eb7dcb0a3af49ba92d55dbe67f627bd9567b7ceaab6ce8ec9d7c15821462454f61d1923dbd9a22c6a42302372e7da567a521d024cc9341e986314992ce8c77dbec7e26127fb88c1a252497dedc381dbd0370c606da9477350a57cc6593e553434d144731792ca85fc6f3847d69398a5c863dc6277e5dd0e3d806c42937eab831b6d52161a50d8c6cc28757eb130d8183c5ac82abd7b0d6a2f2e4d4aaa6b764b9ff3aefb756546cdd7bd36917b3151c537ffea4362b364a4aa51f5b8b979d535244392071ebf0436edafeda4d83d2da78dc66917627be390b055ebada94b4b3230ea86f8c1336a2b1942a725fe329809d99fc082eb9a3d52c76b069d1bb00d69d3b5db92c719992a1e19da15a50c4951125cad77a35ed82111102d3c8219b6bd25c3c7f34a687fc28db9db134b6494541122227c5378db0ff9bcda20876a2b42299eadb419b23913cc03ec634662e903f7eef181833f254e0a4169cfd315364b4eb0f4c0c20df3e024ac1b05577913fb59a4954ce599dae58f43e6869c12f89ffdc2548cca0e33ebbcb955933067000922e957a008a2711d7d26c0bf629636410a088152a6cef1320e43f55fc98b61080dab58540062bfc66046aeeae4397b8f02cb2d84bd2d56328398cc2e7c8099e77f426ceff975c2163089077e477b618690ca7802fb51474474c4cb839ea60f7a260b7bc991223afb9180195617c28c8fd40ff5eb8cc2671da47e22b61934d1e3b140a82d92bb0019b86612339d9acab061d56f534975a6d7634b276b16975706bae27f9cd6076a7a09d4fe5251d169cd65e6fdaccab1fd20e8a93837cf57a7c4416937d04a185c0795473b0c96fd88a353be1cf29f6ba32a9e4638b09db19a2e68299ab482f0cde05453b30724b5c40c11af5b59386ffad499d29ab7082f684cdfaa6cc5886120cc18f0ae325d0bd81d787373f5fa3ddd60369292bb25b03df03fff58882e960e3e268d049ebf3cb296fdf16042be79a56424f9f570320512bb72e3ccf8613b3861f30d5a0e3a2ebb4a08b36f37024f342c7ed4badae2e063c38193e18eba7f26b0af73ca393aee46bfb08de37373cdb2eead92284b904d72aa50515bcc2b47eeda30f04375767782ad5921e771f54d9a77922f767ca549e3f0e8d3862241ed0153473ba94c59168990599777b8f1fb8012171131ba3aa77aa7f074e7c308104394874c54801fd5274f2136ea4cd9130a68702ec43710da214753fdff7d6c14f8fdb17f5001e81d7c08d741579f093123c3370d87c6b534c7c2f725ae159380a80f4663867c2ebd0849f3a06cd9160aed228120b4c3e75e6ff07b89375bbcd6bca5d89bd0872337c5a1062f7332afbc388d5dfb32a8a2f4810324346179b3bfd3691c9b56bb1cb26ba530456c9f3ca892189c4d42eab61c80fbb4decf5008bf574e7de674fa5cef6bf696c3e9ecda2846839b2485b27ddc58a1601ad94e313c272e1f873aa6b9d9a7096ac58b99f4db12cde69a92597584e0195264422674391cf3bfd009ebc466610925ce897e254e233fbd8c316f75b4bd34df45d3c84898857c74a3b079a1bf2db64c435a98e2621265e90865f57865f35ae87292a033ee0658c43e490020efcde1ef711d645c59cf944b629c47d44bcd03bde8e837afa1e40f3d2ca28d92440fe7f4efec2948f143e31dc40974889986a87c26e81486dad2d310b4c706ce4a25d701a93899c6bc217a9f1336608485ddd9a0cbe3d4fae30c5c3b95d673cfdaea07a12dfdbadd893bef9d788eed5fdfd91271af267939dcc4195b6cd0c9ae67ccd4a0df4961dfb765472a3f52b02a97276633902990d22741b4dd48bfbf7fde272b61143d4ed1d9bc2bd6304ec88fea4532f4c3245f1e9161a2700828c0cb8697ae65bf8d89663d2668e49e6e5b4598804a2964b6d9ab2a6a50bf3f8d7d01503218c22b950859b65415bdc94ce3b9cfdee87a9241c6f7285489ec7bd8d8b378ea25dc75db5dcbaffecce2a6f7cb2c945d9a4b26c4a4ecc6674789a9d67ebfb23a0e9a9e2ca5a25ff1e814fea26968ca46a67f91fdbc38c31124edc0cde1506faa4c0deba542eeb6b6e795faf4b6e86b3d54da127be3049153e8f3537637a2b7b318bf62ede46c0427661b66abb84b39cc32e64f762301e8ac592ef36bac96d7c509b50f8ce69d798f3bcf90821ba0ee3418f96609b624c71ae460563e9009ac5dadbd2f6c5b3d0f17ddec161bc51d1c7afc0132e9b68ff3e05b819cc96eda1fdb792959340a14941dc9cbffa74c2ee4678183e082df67122b04bce625c4401599c435345942e1052c619683783656aaf65b5287d0ed6c86814a4219db492502f89b05d071505ec53a83c75e47fb6149322f653622eadb376dd79ed1524c51c9c17f92533b6db0a99f252da56c3c0bc21b699db1a4124e8c50f549eba0c4c0f97b9b039ba532e356081bf6bd0f70b771f9c79a5144d6af7f70c4efaa60dcd9ffa89be605aaaca702f13d1cd5f858246b40f9a5c59c8d8f35e7e13ebe302de6d3278bd912c13b6dda42990c8e699ad7f659332a62551643571bf741b37d8e8f5b329eca71efea4248eb024d3d97cc52eb738e65c50b488a0b9ea4af0ceb9959ab3d2a2f9a29509873c546d74655f93b25c2e8dc63a1593b1252709b6e0a131fc7c254e0f99995872d1c31b0a83f882f783a1e00867ddcc11ca1ac30136b5a796826988316b3fb45b3ba28e34e5105c089845079309e094700dcf849b115649da0e60c5b0f395b0454314d54b6efb6cf4566120cfd791316cbb846e839218a850afdef466599d777e6ea80d6b8cd78912e47a2c4c3c581808af7d3d76b61a3d89ad9c3f93b9b3a615b4ea3c114d52a52ea6795ced145191328283979d6fa614a70d052e2eaeb03c045850bf78239aa118ebd592705e951bb00ed87c70decd44cc03930651960306f6fb0f649f038171ec4487e044a554bea14473249320e3e3128105ce560cf94bffb0b5f8e8aa8c366b24507f0783d690e60742cde0808735df28c1259e6db0353b92847f777435a44bcee4e5601499896b2216aa903745ba590258dcdd6768948d768429493754dfbad1e0868b9505f1b880a7263ecd5827b55093343472ea935f89fcaef8d8791c4abde7fa1248c845cb4f1689d298a583a568e08d75c92a1260be803180332d6ae529a529fbf650c9c79573ec48231cf89b87ceace64e5308d26733076ea1b2b1711f71a7ff8f5662fa5992d06a26de29022153e216bdf70fee8e943ed3d04b24e24086a13d9861bdc99dfc3f30140584bea798f76dd09b99f88178d500edbdd048332eaa8b682a68c30e5a1a5f7093af6dc20e2848d494097fa1545fcbb047eab9f5b88e3cbf86de390f47db23d727b890e358ce249dba81eb9bce387d27e22fe68d59c31c23adb98c996b17204ae7e61e6e39895a407ce9509eb64988c51de1dba6f5ff1c41ef5b2f4c3830a6110922a4d85085688d2492137b5e426c170bdf6ebaf775017f5f1ed3d0c6fbdf7ba1868b85c08012a573866aebc9721107e30cd8903c61658e6574db4cbc7cd2d187c1cc778e4693e2535521d7d48a1a5a3f8b654d27a3b6c706e081a3f7a2ea292fa7afd5f8295292eca5d91f1df692245597b2c864c327dc809fb717ca9092246bcd094d2eb4b4d18024f29488c25fa00a1bb7c79c756a53f8b3e511af1ec51f0460a3f9f79854dc5d302ed6c1b5e1e85c25ff9110c087bc1e9461071a13e4f9cac5e4ad01ab08edc0ee963022d37dd8acf8a3a4076675579fb87d8d80ace0ac73dcd5b04ef206eeb647101f15709adf0c5e4ed6ac39ef489382dc50910e7376470f2f73d7cb33f880fc2faeb730d6b215af73be5bd727b5cef78f3a184812327e27e447bbcc9db5871646f5096f02761accf32a68abb5c5b5f952a43626ed345b7d059846dd90aa1de2ca1b249cd01e2aa2aefe2388bfd632d47858058950eb9876e4151313ba62956f6a61f846b6f4b7e3cba1f61fb492cc519607d72537f362b53a3530933d36c6c1248b1dfe9c759c01075330a80c0cee24e0427cdbdf2d0f9137d2c28c3421c9fe396c15d7c83137e81cf63a487acbb1223f4348c28b860bddf185b7c3f1794f52c807e5bb91c93325176c13c596df8a806bbf7ea5488d3d4aefbceafece0c363dca0d40087c465aa0f693729ddf685a9b467052f47b57a38a368e3d5e038b6bcab2b0a68289a5347cc4c1b058bedf9179bda137024fdcadd11cc8fe606c1bb71569577e90acd1b249a28306e1e752888092a04a5bbd808ca72565f5da57ff94c061bb3a4d6549f532142f990d8a010b99e11b8fda01ad8892efcde4e549cf70a4571260499747a3405d7152e2764d4c60f4f017825b478d02079c55c3a0673329412dcc8c34f6d6d3bce9d06a95f1930c73ed39dc7a7e47d11b090208194712103d78e5aa56f4f3965c12f3eccd7deb06e6ef83a588d2a7ec2e5bdd70f002d70c93700dd835cb01562e7d6abb3162398345a17081ea6bf3e17bef103158e28b8e9236daba441e22255544b95f6c7999c029520cf9ea6edff56ecc83688e1eb57b7b82aa6775d3e8075039be72e9bb7b3b648c790c9862197dc349d9a89d405a4c9e8f9623a76164c61116b0e9628b94c9e186d4b8a9add3d599ec5c31cd7f594fa26e5a67865a2b469dd8966832744f0b51e857f5283925aa3dfd19cd271948b45ce7d109193593339aa3cd278684918bf8fd65b4afc7d55d2a6b8d5d24fee7cd75f8df7ee7464e9df2a8e3003f161124b566eb32cf3f49e4f22ffe1bb7b96147d163b691ad92ac46173931cf377f8f9efcc2c3ea02108bf1fcc76b6266722e5ff7f14c4a00df3719aca5c7f35c15a71d5fa342f3c8bbdf745680d22ae68b976b4461d858a135d4c62552ade874993d967661b4d8f51a70e149fc20d3c69cee3a7346600b85a82fd8061c4d7936137e9a47a05d2f12c1ccb7451d0176c2cb4de135a6d8eef2fb06cdb22c9ee75d31da50338c7353762b420714fa5c2aa2d4883c0b2d08fdf07cc7a9af17747440465bf156520f6e09ce8ab23e30bc595144f37c8526518666c4a4131a18eb21d7f0ca1b2bc75a1f5aa139c5d444f46e2f8df03a6d4b38bbb54180424059c09c2cb429c7263fe7460027687ef0b3fe41d0e5bcd19f24eb636b4ab8fbe7eb223e239f06fc990484f63d4e9304b6a2f017657eaf24c94ad67d461ec1ced3bbc130f00c1c3682552850f27a4fedc016f3d87a95e75aa8e5456036db2a4206f2cde7af62bc3cbd1c62c4eadfc0da8949e50e5cde7ea62ba8a8263616476e5b27441b294a54090c08f394216720a75de59fd29262ba053256c8f1792f7320c4727c928bcaf935c606608aebe7ebaedaac2175725e51d281a1d660f29b55c0507d5e889d89624706242ef6b1be66d98e61ccc1d6f27cd1765b65c807ed8850433871c5c2c9e18be78f5a82f728139b3781b4604c6de01af5884c2256815fe9958e355ffb70fca1be02c95e2b459aa14d471476e399b9e7ccfdadc4f040b219cf14c7253323098b3b8db38c8e3d40c4bb1b80e8b3d64fea22d2cdc0dfb4238145895945b5f78c9d6e38766043f37d04493986a58210ff3eaefae807244a10d23f0429e82f883e3f42fa34156ddb40f239a6996a6f9033bb68129444a5d622e05d79b2761a11181c5d05da813ff2295c6bd510fb9742f530f2a1ab6a4169c4733b04080194224762d9ae3d4a352c8a369aaa79800acc6acee7c5d6f9c960ab0824f6a8973427928132a0bae6c612b2f3589aab16cbee2e2cd5cd1d612de59ba281a15f11383b506dc50c7e272215349d7b746fd6553ab07e4b6c6b5a4d5e0cc4722e90f9a319ded7bc9e3d740b699c3aaf13478e43dd081c5ed47681fa208ee97bf236be4a0790c67f4b72c88eec604045964741322116c0508436356293d7a557f4a99e8d772982a6f8ed0ce1dc3952889fa82691d371c4c6822812b6d3228dd33f18d794b9afd1c2e4e6acd8b6eb199e2de2ce8c8d8c7cfe985078aed16517ef12dc395f0287484724a50faea3a7f4d871fa4e299fbe724bdf5314a6149fc7f555d5689536a977453ec7842c82de8ce7f2b78d3ef9513ff55b50c65cce2f3f63ed4a937c24b88d52b18e744eecaabb611dad36ea883ffc0150dfb950080371f1e51ab683e188f11d499578b9c0a899f92b678a78ba8f0d571d0a3fe19c99709ae0dd4045ceb6f972fecbab4f9941febbd4b15aff29489fe91d2b0db30dd346e0f0727abdf210644c4c949b16187130ed3867d23601cf8b4eaa2a05c08b893be3e4c4ec583c9916740bb9b2fc99a293d477f2540b488003c53474d5981a8af6eae08327d9d0dfaf212a37a1592281bf2794ba4b69232442ca0770fdb1dc6e93f706d026c3b43fd976aa235ff1705b92dbf6df484cd541fcaeb6815ea2df5405b40a559041f31c387495a6cd54ef5871858afdea7a81f329a2a528c88687ba40b1ca3de39a445bc91eac690b06ebd5efcebf31c2519ced40df28efa291646814495254a0079ab656c82bc7a05fdc0dd72612671bb40f0b818b5bfc2874f9f278c6c555da1c62dd09b660f93c589915bc1eed0e238e800202e4aaf0802d9567e3398508956bb8de09418585bbd14f3b76ece5cd85f325ff8b36797b46671a105e5b81053392bd4bddf282ef09184a9647f71953ddeb1a4a638d57883e58dce6edc62dafb0d7d923e97dce51b81acd30b48c3814b2ec3cc3e1a43c29ba2e3eb92645dde22a2ffa6bd7407b36ba0d523d8b4a9bd75e25688de3b016011704b46e1deba98a5720c9a22c947313eada43f2dd73a80119f1cf621fb1ded41ef89e5ed64630ac86ac10007e0b23a121445b666194c0a2681192ab97fbd6dc943b78e19aa6e0006b74104a251a8a90341f8b9cc9b91eb4f656c0092d1160f7c7c23fcbc8d3045ee79cb95a18347a66579d9030ccf0b553b237bc1955b2a8f1bae320dbb8d7e3f2f6be12e282937a78b7340060a5d1d544744b2260612221b83c8d5be6ad1ead1079de949d02209abbab4182cd9cbae6a3e570599a78babad662d5964091c52429c39d1f2100f352003d7907179d57798b9f997c66b12c106c3877c5c5592fe24b896f379fe8bd00f314cf62fe7c21adc1a5df867ae3e11045dac60f99b7f28fdd330501f61a2f5c734f0e1af49eddd39032f4f56b4b1a62625566b0152456723d001c53646f49b5e4c98702e35e90e3a2fe6aa348c11ece3e0310e4d68a30ddac5237b13c91e37119297ecace329395063f859ae1463a07de15e85b30bb330c98569102b6db3afe991cd8a72c6da839529d3828e942d7445d079589d4403fcb3beb1831e15fb2163a9bdde62126633beb7e087f8fefa69043b64045680ac64cf55952f565ee41ddf0d87fa4f0fdc7f68712ac382b0a51125c37c0d2f7f86621879eeed0e63f0adce3433538d5902d36084a3c0ac048696a47f009ba9044c488e9d952fa1e86db9212066428e25bcd8b760c09bfda529a4984bbf66bc6a5a6770c07befca249231d2e7f69233ee6a4e5778c438d55e91496b3858ae72f1f8c2d4601e342b22d29ec775496f2ea955d88ac0f4825ae2fcc936c72d097a8c85545209e39a9a094e612e1bd505937237e4c3a2a863b377a1e9082ed3c0f7a0357f28f44db626067cb4a4827c4c34a242a78e5ec1eb60371f10246605456d99aec16acf75ca374728691d8fcc874e042273b3d53f6ee3003843efecb9a6f01e524042d7e4f25839c8ef3fcc3f3cb21fb2de22df010734f613fbf463a62d79fe6d107d077426203313f1f12a072bee71a9184747316903631c6f7f299c80eac75caaaf0da2875e4076d5983b62b197d4dfe7ebea0116fd2f144fa08299610e0352bc0c3ed78a20ec021faa4a0549420f56246202bcf341a42ddadc00c1a286750301146c0b5f7e708aec91ec402106eeb205d3cf114fdab314ccdfbd9c6560c8cfe6b4374f637fe68c33bb183c2d3593866ed6c29bdbdad8cc409bb194f6843e6e35caae360e2a80073f8d8f881b2a7c316e8f39f6db93bda6aae3a41a2e34c4692b6334ed40130803894b82a685f18ccb90e1fd5f7ccbf4d6209103bff8a1067fabc442352428717b16ae7a8c28bef54dd787b3ba60c8eda01883f5351d681f60fbb50c029562347b7e3aeb8113c6fcbf560c2e419c022c30fa49714351c37a0712ac8204dae60349934346efcccd831712f9384b2022bdb5dc7a8df559f7d15aa783578b6e9bccb4be41a231a84303e3a6dcea1b4b19f0abcdaaa43f01e64d5c9f00cd90bf486880a175eaa6c8022bad765bd2a5cdd7ec5ac678a68f7a3823d1aee529202456edd399d1f8a7aee96c4a49e1a0d50a781a9a3c2dad4cde8ffd6581ef79a18e7f651e42e3817d2b171fb53b8e949b85af34ea85522f1bd1bb640712d928ebc26861bd9dc8daaba0b3fb106ba0f7a8a31893ae7792579b4cb30bbba6e1dcbeb19ce06da08db1f13239de11bc14786d1800e58a87f1cee28330b745995d163783cf5c661c631d1b62faa5d7987577adaa93430b58565a04f3c0fc6956d256a3a3223b11ef5d4485eb1f00e5d8a86b096d87d17bec505d99a5ab92180b2ad76bf5be8942fea82b625f72727f7aca6a0531b95396279b91e99fa53c1bc478a87fe3769ab558c5bd40baca8da13f538f07a07172545f861f4f0969785188db3c629a2ea58c27b78109eababac31e20e55d8c0ad887b695b0a19ec0e7f17c4ef7ddca7aaa09c82374a1396ccadccc75f124e379b1029876e29890576b20a10bccf4e56ccb0762befe35673a3c062cd82b4eee3d882bb76f54ba27f728c451b64e62c0385e7311c5d6d0dbdea74f0a0662e08b89cb648cd2230d1a7f6c1c2b30c801343d8280e95bd992b00d302f5c99a9b2db1dcc899cb3f28aefec201b416acf7afa397246cdb0a0c2130b94f36320264c75e03099d5ace837e96992c6d338f270aa98e2504762ceea9d2b64e7abb1fdcc93f284bc997db4d5c2f1656e717853e7ae383741d8cb9280fc71a10f898c3779c3e3fcb631bf9dd4c3a22bf974a8ac177ac678e5dfafa6cccf9a027a999b6a3c50025e31274831ad3fcb04b0755c3c71a950d33b6a84857a3bae540ed14623ecc50914c0f17bfe6c0ac555224ad1a945fd6cc94786e35b2424c322a4bc8c1bd2475d881ae982d6bfe3237b91ef2c478fe9e57b21bec347d3e3b304dcc77309d4be104493d4e4a4100bce1e2a02ef45c5953b3c9c55b88137913d1ea2249627a33e6ad171cd56e4df72475c4bc614ceda39ad241e397be51114f740153439ec3e56171d0717e0c62c6b5f65687c236bec48cdd09bd0a286aa57289fef2438f2626ba431e21e0d2d361de8d398a330d4a686342019d48cd9c9b0eefae4308f36f0b0cb7027aacd5565b3056d2380779eec5907c1818a100aca65161108ddaf14a3a4e933049304d51af8b7743b1f20a91eef72d5f74c67bac04c171267d2ddaaf85205c38be3539585cb9cd2f08cb073f043d766222a00e556b95efa5252bb79452ca940265053b054a051a88337999a13a7dc3108b70132d3fb9688a8c644f28f6d4214d16712b36433a6691450ca32f9f8a41a43c38ed691270dad33c70da7bcd10073fbdc64d40e1b8e5694e9380c3e93432f536ca0e1cfd691e3ccd69da7acd1951d7987a218d2ce216279b2b4068407cacdf514b2db516040204b6c7f6bf1c318806e2f5edb491f81edde846b70d084dbffe7f7006a922de317b3e27db53cf91e328daa22f0efcf102107480042c85fab4a745eaa22ead4d61b8855245c6f8c8d87a34ca68b95228477560148f6bad1cc65c4c86e74916738b4e5373866558866558ce5aaefe1fadd65a2235a6be48f48f33ebbfc7ff31990d6a9db4e28ac39b6c6fb60d639cf2846c9bbb97f9deba6ddbb6959973838bcc32e223bbbcf6ca111d394497b98f4ffd98a169a3c2068a57b56ddb4629f5ba9999e37558260ec36b9b22ecd7e2ca9e331f5de7c53e95f78412d65aac59ac19c11ad6b0b8f7061d7d9ba72613a554bbf4b54b29a594524a29d52693b529965bab0877fc432399eaff7f4a45ffae19b9113b295527e5c9ece42625c488abb52ac2e2617c32002e6e795557e5273f2955a10ae67c52da185fe60a159d9427e58d984bfbbc8edbb0f6791db7612ab23b8e24ba5a6b2591c2506b91a8d62397441289364fbd714676aaf24fac30bad789219f265eca2c57f1d36b2668f6d448a8569db192b57e663573e53f364358ab9410fa2fa29a8e26ea47bf475ed290b8cad4538c3f3acdd1dce887a73fce913d5b339c1d9b4e796de7dfda1f55507c8e1b756a66383b35b643d3c9b10c93337641e6b03831d89efa65bbc0ca8f84fce00b4758454043f604a9b448192015869bd3628e1c9d26f935d5dc28d3111959e4bcf6d4a39a5cc13a57c46f96b348a475b84116e9438a99652486ed5d6031b333eb53d3c22206dcde0516af2cc8046cef220b969d59b4f43982963e35d8267f6e4420211ed9875ce59111320b5258b4b060bdddce45cc11bbcbacd60cefb39046d63350938901358c24097e4a083c0e2f493ee48b91fd216929dff933bbc071cc9d91c822a753d25256519554cd5cb91c52cc1c3f3dc1e9aca22550297e48d59e427c66eda93f1c92aaccff90a0162d795c2e0ee971713e1c727f62f6fc8c1fd2f52197c81e182d811f0f6c4fddc515375d5c612386cc67662a3172648b1fb27c01cd5c71254bb8ec8fcdfed4ec596464d93c6ee4a8b93f96eff5d57ce5d7fa663e72b70046163fe47fc8cffecccc157a673e369766d3bcadd55c7bea1680a0b1f6e4bc484ecb0c392e1e97c7f5b9581e339c31241071814f0f1c724fcd79d007db2302283fcc70ea0f2946135bfc9420a041943b464b1fcbe48cb4f4d54c95a95b62db66dc53735eb4f4b5e88c963e17e7f5cd60b195ec9b7d64ebe3142108e40fd287cfe8473faabb4929a54f5f1aec437ec857ca8867019250ba028c23aad7faf53d0cdddddd6ba5ee37aac8625869185eab821622b3eab57e7def6047b2885fbb0b958e66701c0b7095a961b0102971614f4d4da1c9052532d5d5da2b5bbddc41b01728c9f8224343c6626a6e599665f9f7de7bef3883c9baf54a1f663831583bfc3221a9ba7d2c9016a11a2442a62e7a6445554266087e91459ac22f303129afd2db13f4a22ab3445fafd764ed57895972cbce25123fd597498607e100899f64701c538bb4d81964051526c731f50b63fa32c93a2de68a98a305e45202e16089154ba85082f1b6d51245b2955ec395a057e7bf028f633bd753964049402d67f8099403335c070849cb0c5ff44524635d47e4dcd80721ec16c28e402d180411a0080b6a79815e16bfac0ba8658626930c7e0221397d098403d0cb047a99402f13e8b52d087a31c9227dd116500bc865833e243208049a01e88524e5f5f25375d012072901e5ef867b2f06bd402f4e867ca10558ce398a4f29a5f6f1638cb1c74cae6666bbcc9e4206490c147bea7ffd5e5985a8efb4566befd534bc8d4452e7353c1f89a21a58bf8e4d745dbaaeeb30de368eeb3acffb3e9a3b23238bed0bf323f1c66cb8301967ed665b339def94b65a7ef29b3939422667c8e414d9537f35737e367beacf8a2f79e5c7d2628b5f4b234104c62e83506ab1a7fe9af0920733ebd7f2d316e3d5289999420689077b02d51a899d828f880b4bc3f3ca0b9ba157def29b5feb6b519ae38c7c5df6eba903e031a25271f0c675dec7019f440a439128fb10b2e823adaf7a17a6a1d9d8dd0ba7b556ac52398ec964be2466b9668f8eade299e067a75ff33f8ec972b9a4069aa6b9bbe6afa5e8d77cacc14f754360d2c0699aa669f4ab7a0cfdbf7efd7adbb62e4643553cdbcb0a183c70c2beb56efdffadb5d65a6bffc5691adafcdfb2a31aa2bf3fadb59ac6c96438315fa34547d9088389c5d4cc15a7249da1377beafaee5e966559063183c05854ead04b92e88534b226d261b48890a81f409c49122a54a781789ab8a0a278111d473f9de8cb5ca97aac4bc8221dbdf43163f0f4a7354dbb24bc777854a7a96bae3c286a4e7fa33c72aabbea91e828a263572bdd72b0faea1bb84dd3176b3bc2fa699d49c14b76c9588ee5588ee55896231d9fbad096d80e3ad2f1de2e26abd1027db1c2e8791e9e792251ce9e96ba11bd91f636cea6c22489243516914861a8f58b65864766385945667833439cdaa5cdc643b08a24fde45ce0265e1239e9a727b54ae4028a165338202635970a1741b1da511cd55c71f1d363167c646323b8050fd9538b78e5a2253c52951b572812e52ce492b31c67644f6bfaea6d24de1bfad9702110647bbfb59196b2a4a7454c7a9ee77977c438671762a5debcb79eb55bd9822276f3a83712ed3bd284e410de807736c3a922a2a944dbddbffb2bc7f1f7fefd91786b465736c3a92f79afa7f18c9658689145ca1a9b6d086b4b6d4236237beaeb6dd57a9ee779f7de52d52b149398fcbe185b335566372b8c2e348131c698a34698000d14a080d080031f2c81f2baae037ffccb0505a4860dca037358fbdc28870901241c002264fab1d9e6944a29e5386e75466c1cb5ab6d625405f64d64987838f52cc7c850b85c238bebc5525de3cbf53243d80ce7e86242c6781c43d8057f74bda816dff56fffdfde1fc7ffdbd99076ddc2431c7e0b87759fa267a8886cad25020eaef25b3ccc3b9de44918580c061683cd994913a65c7ca41e06854c3ef9240e3b60b0184c1886a1adf7de1be394f1d6be497bc959b0b4c613d9c978023b8139899dc4d49c9027332737e3388e528ca39f3c0b8c731689b4beaaabbae3c9a83a194729c65591718b4ec693f1466babf58e1cd5cd397f743d40017112759d2863c68202e295b104ea8251bc76cd92454c29a5ff943efe4f62679a87891bb3d6da0f634f6b1caec0dd5dc39db7715e3792c6d0ea1a8b91611a26b23b8ecae2f26a1d49b243b4227a41954f6aadb5d21d33b4a51f42b4ab27127d2b8b98b0e3a6f2a7aaee81c70d3b6a0553b474e7a7489f721cff2a7afa12ece03a7e0887616a7c83d0c167b6ad3ba8d9818d9228d060d75137bd8abd25b2e8b11d287657d06ad50c49249216f3e430aad7fa43623b16f0c38e1dfc6439adff154d694e73465e6badd54f56019be4e1d49f005fc171b040800c4e50a499c84e553e30b6e3631c08927625ed06dcfb3ff7dd71af0ecbd9cb9b1c93566bce17bec86fadfd9525238b44ad2b32a986886650ddddebffcf696d0d3dca94d24ebf005e5db0a0c8bfbf787dd5a6ffff5aa7b624402358b002c618638c31c618e377f7711c473ad29196c2700c473ad2d182323051c0503f808c60258a0b7e51d3eb6097875d1b76d1d295c95c57e692c9b82bdb4621c07bb3aff98113bb5c1cd799a117c3f9262806ee4e6f8a50590f947e9b524a2945e1228f9a527aad2d5bb8e55e93c9f42693d6a2feae78974fc09efa79e02b3ef015daf93ee6bfc59edac5a7a2a38d6050fc97873188316e61930f2f9d0c4e6bd1c1f0303aa3939f3ad072a011ec688bac812823ba5b59fc978d9648a4699a0d283b78baa379f07407e4e172b4e6e97befbd9ac67932dcdd037bb924195f6468c83db527e6f1682feb6432ade7d64c720064f2d5280e78aded4e466fea6f4a36bf93690f76f30ad1b4a847daee3a994ca652ddeddf25e24b9e9f86f0d3a659e86e8abba9cdc471a454b4e4811d18df85549d6c453b59a893859cd85377b24e766d5cd10baa1cdaa2ee3cafadb970fabb581733c369928086e2c0ab52832c76b2ebe9c2edf1bc1ec7845e1d1fb87b859909c306842d08ad9eaffdff058212597499761a31acb5d6432c3f4d6abb970df2a73bdad31c7dbbadc3197d2a9cb5568b2a9653d9366b3537a278db364e4b7957f7ba562c23b3e5f77db5baa115e075d8445526e09d8947bea98a385ff5de7be76b3459d3e53866cb2e4d17aa329178484db434278b024826d18d4d0913dbe38b6cb592d1c9f2d3dd534f5cd219a31346e73857ab0d4ef0894c4b288e5ae53c8bf015dac46ab69a5d90b656abfba3f5375e5db06074afd0398eb3655ed99dc15cb14e14b1c51f42edad9fa669ddf5b4bb6ddbc68d1cc56d73b5c25886fc94789eac205563dcc86d9b913435b4a436754865d55415528deca9374fa552a9542a15629cc29fc29faab672dbeaeb6ddb46494a929892b8db0f761103d5e63e5b0c331288684183fa01c4c15032437d1a08fee285f230c624e9a7530fd24f7666ae3c4992248d4692aa1943f7b445078eba9035d9f7a33055f16cfc5118912c52f2653f4d6775cb816aab6fa89db6230ee4607cbf984c52951d83fc14f929f253e4a7c84f7d4a084ad2380e19d4bae9fae9f0aadf36942259531289fc280df44802101b700ef80a753f635f0d634c494a3a8e1ae30a49b8fbf52b7251242275c04f762fd9b4e6601ec264aaeb259972542d9262643255fc3f7d4a67b3d96ce683e6599ee5599ee5991036bcd6741691527e5a000e5746feccc6fa4ce9b797f3c66739f4b648baf973ce34e794bbebbfde0ff05c55dae9ee526bb10eb2b9113753a9d4ec1fbf4d49531e9d3fd0d2b4992a37e512b6388bd853e538668aaa505a6a565bebc54050e081abc94b13996a23c2335ebff7ef7b0f77158ebe28cee8afeea0bceee820288eee803f16206444713807d8537768d8537790c8fed970238d5cfaa02aa9206c8f2a68213047fac9b3c14bdf8ae8ed21dc4fbe57301421802164baffdefbb523832c44b56186b9f23e38e96d11ef1e76d82066b8810c380c53e3510f1f7e98e1d4af821cbae47aada6c964c68fa67aad5fdf29c65a93682591c250fbbc8edbb0f6791db7612764954aa552a9fea5b857fb8f8b913aa54f7fe6c3c76c369bcd6639c83215fab3d96c46673f9bf9d061397645689fd7711bd63eafe336bc5b4cbcc86c98b0c866f607f0f640079338a5b9346d74bd5a5c92c4903218cc9cd9536b8ee273ce395d296264aa6971ba346be7fb00334b17984b5c07052cb0c55c22c3757aea09f313b65a9cb23da71754574d5b5937f28fe3445752030f172d4d146abe6889d3d365a2be110a0f4e7ffa037f789a037f6c52d8a04401d14e808ac2ea20a8eeda2d4e9700bcc875739fae9f04268a645242d674cac0e29aaf29b2385dd3453946b2385d0ec305117beae99a2c2dafe32fe03317650a5a079fe1a66bba389cc2b3f345098c2bc0382200a9964bcbc5f26a8191c5c6179956eb26f94330ce596bd38e1ea689884fffd5eade7baf89961c35bf1b68e4ad744d93c96c47d8846c301cf2dc3e6ffbbc2fb268fab4e8b03dc3ace1f31e80c30472d0e123b11781cea969fdbae9755da76138745dcd8a8bf177efbcfb9f94765dd7755d87d3753822c8f1251841101faf23c7c33057fb7da65734ad15b08063c0576ec944793c0c539b409ebe1af6baf905f9be2fc7ebb8267018ded6f0781db6c97751b07fb462c4c864c8191a2177c8878212af3076ad58465a4792c0c6578b538c473ce2118f18e39568b5b5e896a298a47473d1edb5b9a8b6d5c6c234b9c433a7988897341a3f512d13c5607885823d57222872132ed058da0b195ed25c67bca6b03597126c23f6d4d30b43ad7df3f1ce3dbfacb75187f3be4eae9e9898b2668694c5c862621a6ce627d768a8cac4a30e4b11cd9eb2130dd21a288a6ae80a8a1900347bfefc9391bbca06da3008c338670de6d1354c8f3743bc5ad1155e518d35c3a9f7a7842ce2d5c8d2e23257aaa620ac459f797287f3791db751bcc2abcf47bf101a8094638633cc90d5e292647c81511551e999a29b6ea5fd749f848097ddbec495c880bbc82269e49b644615342f6314db5fc62af2f6e02868210027c09a80c6f5ebf1d3fb4bb51bd66564771c55e77d6dd63499cc85010b4d021b5f2d3074c78eb9030d6b77f8e9b5552c365a42c69a049ade56ab1f383d6697d72567a8ad8e909d46037d1493dd8a2314d93c5c10a5f280524574440d80afc69ac7d518fc1185d53fb4a3785cad813f2e8952f70a022df9d25c91456da5ad4ad666aabc686f68b495b6aab54e4f894ecbbeb56ebdfeb6d5c3a2254d7b73be2c883189961c6502c00f882821128331954265141398fd20c9561211582f56eb888be5250673491273592c99ac872c73f73277f6bdf7deab4244169fe5c282519649459545930c3f05bcc4e9aa652cf05de0affa23be871e1798e16d1911dd7befa494e24d2643929c19acfcddb72964fbc722b6d6e62d621bd664f161586b1ac8d818c3fe5dc70c0f43e74ff330d809c84d0340f4d80007c833b66ddbf40ca490863dbb8e93c6d37f6aa294524a2955d140c8a2c964da4c26c7b194463ac10a27dec791ecf1771cc7f18e75bcfa628c2dc6f85aaced16c3aeebee486faa040134f6ba0955283172341016b270417d35503b8e305bc7f11b51d68ce1f33c9db5fb2e188dcd91680361f5d59ded3a1025874d537d83d7511d646f5c11bb819c5148e3273b06499224b991d4da160f0e88f507f2708a7fe831c3897d2047ab7d3299d0104232140a8542a15028140a854236252525e5ff3f8555adfdb7d6da14560a8b9642ad2dd44a61a5b0c01b70d682ca2b5921135ff184e132db23c6107bea30e527ac3f5430d9530f40e6274d839aec9992a252c098c5500b4c61b5905ac26bb6c79a636c1ba58d99a7f296e8ccd8de2a85d5bd1eb36852eda9535429ab92e292c3e2b038ac14d50c3d61f84a8a112c313e546c0ea8c9a653542946785ec9298285262c705818b4a14a61a5b0f6d4292c0c5e6d24babab233c30b2f9830b9aed0755d17adb56aac6d5cb57eba251b63b8ca0c9976c8b443a69029640ad9b8ae900d1b347e7a1bb2211b6a89d60bab61dc855aa116c69dcd2c245948a3734eeabaee1d5daf16972431a40c06e3b9d3e968d185edd83555a676fc92e282f467e4d1777447c791478fea5c1fdc2af6d4770a8f616a5c037c03bf3726c59e8fba4f807467a4c228d0447946283c320d672051a2b20e82eaf2e8aaee8aaa8c50787874d61ef0aa501ef4d120f07e4094d71ed0a5a33be06d81d7b529218bd825fb669f675f2e5aea60ec3257b09e1ab7cd70381c0ee79bcd70622fb00bbf66d8825df092d70b7631f192c54df2187b9ab1a7fec6c816a6c5a7a7b6b0d7f175e156085eba4580dd8e6eb95a5cd719cdcf039b57a8a48dd9d516aa190000414001e3150000200c0a05830171382c1ec9faec1d14800c5b72467460421ac963911c889114864190320a10600c30841062c0504dd906bc98d5cb5b609f949b011c6076ad5833f00bc586f9ea663073bc5bfbff7f24d12abfb30f28ac60a73d00822f7ca4fbf64bee1769fec658e61f3a04a5118dc61f0ea5ada1c76ec0d0d99366329662a2adc109573b89dbd66948b4a09b6af9c87abdea60ca317baa810187f15a91c2f0cbc8d9370d7abe0906a2d5b3e5bf8872e3fba166c00eb4490147b69304c1129626d7a61701283320ad915e065f1f6855151f8a150463125261c4b6f5211f96427b848bbeee7b19ed2766cf175f4781f587a6d38e8cb910f863e1c1c1df23c45d7ddb640141f6142f43cca1dc8e2fc2e10a2226571f4927c6a7be8a63fc7b6133697211cdc309fdd2824c4ff73826e97745c78cb9e95fd5a33f2e481356a310168d6c665ca9c45d2a20b6322074aad3da9c25187d552cb97a24602cdd8b499d91c709c40050620e96a10a1bcc9565fa6a549714a6bdfee1e72fa9f98836a084aaad9a720028a41bef533b6e109d209b346e5468d8c7118ad1df1d0c516a6dbaa62709e06b79d359dee8260050d6980e6f425fe0de04fe6d5307d99a5acc15561a93d44363313ab90a94c3ab0fd7083fe4fcc20b24cdcd70e36f38caa4161d3b5b785549edc8c1604c4cf18381c1d6b3a192c65d2e6648e4bf382c6afde827ed0c2c06b858f84959057c6c23289ff0790e3ceb47fa14a84170eb234abb1b16270a2b6312c27925c214ede8b51ec4ddc8e2103be3ee90f578eb605051f48638937bed0ec92f9da956797539ae9cbf400358ec657f730545e42223c0f96fc03638cd71b4513948755e125aa10cba39e480ece9cb707335f55b20754fe7f9a337d8e0d18e3ac8f9fc71ad44ea597dbcc02e4a9b423cff7e8e8ca9ee2e95561029d859944169d66b41077880fc816001c03b6d3ca9662f9cdf477eca7f943cc306ec54c920143b19acee627507da4c416cd7676df1482a1e599e807a2b9dd027e7acd5f0e3419940cfd78839041ae6f881d2b6da0d966b123145dbd54cd744c9af6b5c7dbee9bc6044e99d9fd3080e311d4bcc868123460cef12c91d2542887234bb723e32bb4b4e9189c3e3244d9ce19088c85b43336bf2e098601c2bc75fad08500bb60857a5e22bc62c1b049113590a9472f3ba8ebc744b83c839916014254b839d38f64dc8539d15b7ea34c119261bc449ec440b2891b037644d9fac44dcea2282a871710ecd4add277e0401577f43af10a6c1d79a9a9cbd763025e14f69b6557448c42b32d54e3454338a88b79146656ab90e7a16dbb376fa6ce6f5aeef8e56c2d3157477597e24b34e47f04386adc55cef967b9827cc057dc31f194375b8e7b5b32be48511ff47788d02888332642704d0ed71daccbb0a1859a214a103b72b734e7f022c1eb14e5bfcbb009e7f821c60ffa4166267b3fdc66613beeedf6431c316423e56f150c9a11d0958b6110404b1923217e37e8ac92eccb141e23ab75a951d434133ee528b1c33c6a127393523c9cc8c9cdea971ad57218fe8ee74656e0d07d889ce49fe7cae08f124aa1d011818d39f4dee0b0632ddd72b6a4f408c2c7226dc02dfd82272e806eae7ff9a682a3241ab86f8086dbaed533703db5f5ad4b032a5ec0938301884e5582aa600337cea89bcf570ec7800ee941efe175460abacff7e5f2227fe3d450d754c39d60734876134ce5bda63606dec0c5acb69ff10486e359694ec6183bb81a2d115589c58092b82a015d8c57587e98d8495d6e8a89298a8fc489d974ef2cf721991e82d7ccdbcca2003b3158a18c1de1344b198f184494a728bea5540b4632851025151218afd8177ecffec40f56004f3252acf20b0dc89eb0573f305e03f3925ad60bee111239593b0d4c8619ef3f0ae68b48e70f4beef14031584eea19b07a1dbceee46fcecbd5fc672671064ceb0965c01946942d2e20df939a96d9774f0c9ff5eefc689f1d3ed70648c826d00f7453a43cc2a90bb8c5a47db03aba2c7bade9d8a80c53f41c1397abc1da4797a67cc56eb6f0397cd3c2f6914a86a26c02ea90c0020223fee46ebd63e1b3f0d25d9605c885a8c7e47a014f01274ed6f810747c906f5fa73ede6a04704388b9e2e6c43d970b6a6a5dfed83020ed93e80954b696921cee3ac2db731591c56cd1de81e7b00852e55e460320e8b6fe86892e0092fefb0545f0e0f31732a8ed60c1e4a2af8cf1ad865c3dce69190a3657b257202432a39d4f6099d63bf3ad538743a9967745d8cf58a8a216785942cfece01413407a79bb2aef8cb225ddbfc8cacf2d35fba6e994889a2bacd48a1fa27e52f0eba3329787b237568709fce80bb18862cd6a04e19a3e11fd11c24818d0b0eabf45e3cd6fd38007781f9a3c1339046cb00407b8bb023cde55390826fb411f9dd65600166d58d821415719d44f2553377cb47a96a3e4a996f943594763cd48ab0bc69167d5c892298104c9aec1a5637a8da874033b8b4c2fae73203ef03358ea4c262ffa77696ebf4156fc0f4bce9751a4057a6566663f6fe8436df60dcfb2818bd1dea04681a11d9049c7998c752eea521fdc75118e8908c65269174d5d465411b054c33256ff572d04f8798b10a10095ded0917bebaae80cc178f8ef2a77afc101d4563cf5ee1b20b2f5ebc5b1396845e9c6aad6953e06033ded1bb01a27b713901cb762e2872a08920166eb7a0c168d6ed2e4e2d05cb5b99b97de329ac2f6cf89b7fa7ca5b2c37a46e59b7943c4c6c32c1865dfec57ad392d76d42658491b7694bdb504e704b2feab5d70cb0409a45b0c3eabbf68c4a17b5a59335d586922914766a183f53fdc0dc1ead793e1652ea593cacba02d90a01478a53fd1e54be7b0b686a14d54a3316fb43f92329bb92f4e79190ac0b8357d5dc3580ee58928f66c41f07343717f6c6a4342e08cd0706d6993710208aa8f00bdf9b8fb9cfb377a3f6eab86dbe58c1dc61c4f068341b2b69f81fb73dadb566f6088ea9d12c27bb75338b8395827d3c206b8b3f52a19aac382dc5ca52c04a8ab6a5c61025211e36d4ada797051488f254c7b8315c2e5a26156d127ebbba649abde3409b21a4a94aa806e30023b027c1aa7eb6f17671180fa26be4250d4e37a9307071093944cbde5562adf020107b801685c55f7eaa81c7b8549420d574448897b1a7612718c950e529d458ac42976c134c1a0ac8162ccdefe5eb17b8dd35a9382bc35aebbd8e74ae1318b7228027bb192e7a2f4778f42f79c263a765f37c18e60f0c76541d985d5a5ff0fdcb4dd54051601236826bd121ae94eddc317b8b712006a8bac5297e1017fbf0a02b2bed198d6c87b1b3967d9e4fea004d520ebfbe682c87549a8d8573e67c31f5731cc63d365f2c01d40d9c5336e4c0d72df595f23c05871776b6533a18d1a95f5a652314e1601ca0d92a27e596a5dd0cdfd380de60ad4eb8a5f1af29563e99afa4bc406e6ddff03a5e5088d09332a8ea6ccd44a754c257a19b64e212b5681ae0f74c12697bd94317a407103902b09afc04273dc4ce4d9f00babc02775bb7778a052a270d50326cafbda2081527a6bb7d73d93921e7222e6218c77aac6fb790c989da0bf341dde0d59e9b9e4d543cda0bbadc57ec05801eb2f52bdf0933091a139e149b44723a3f9539aafcd73129e4c14a41fb6b257775b9131925a775babb762d464360a4cecc8a9c931ae92ab0c45603a331b894fce96e7440b3ccddd64ac3e95815076b175324576ce42c10a2189e4db0ef1527adeb6fa38fb972933b009034a31ee36ea176c43873f0a438289e893c41d4a79d33882b9a2acdb6f1248f9044fb9361c2c307514b22814a8e240a251aca632711a6bb890455686275fa37eb86399df2372898515b84cd14a138e1a5306d5c9bd713fff9b78eb4be1ac27a7b0d6a8b40dec98b4a286bc9a947f323de256e58719d539233cacf08f0aa525a78949d68ef7d10afb0aa5f99c38dab2e11fd97f993554a773957cde3662a6deee531c577a008d012e3a07e4da56be60ffc52b9bd28a568991aa58ac2adec26dc569630f6ade58877454d512cec8592abcc02c2ca359027d4758751aa66006637e59229f18b2a82f3f730f846d38779bb0802065d05e02d568fc8d98e01b2d34482b0d3f808fa392c8f57f3d0100d67e01d07259803b2bb4b6ab240ae66e07457bf8ffcb029e45f11c7705ba6f04af3ace89f9f9d0c0b38eb3e864177fcf9e38fff021863e0941cb36bcffc6a303e33b6972c39d66346169f5d5fadf3e0e76245093ae9b3a9bdee8d1f9b3b41ee92a6ecb579d10ba6a969e16bb1d35dc89c9e0eb79a0abe87dc58fe33e3efa7e486b38088183815564953991c150af7bb7918abaa54d23c40a854d62c8782d77da0391b04988703118cd839f497bba55daef8e3726630cce79a7825b2e00751e9897c7f0fce17a46df15c77ea5f027ca2353a90250ec07e39268e543584d18818ca09ac1955c4f0c1c0e9ade9169f09bf28bc1ca5d33a565edbb5444014095c5d4845b04f97aed6fe0a9637db967d54e7cebe3688fb8bff4e276a30aa6a81abd9ec8f9b5f301f0fd50f4f6e055e152a124f1621b78819d52480422b0bd90aa0f7a06249d802b43d4e11deb6ec472a40711120983f128dd61485901351dc84612225106feffb2777f63bf4b099d78e4f104a91cf489789a664d15fd1ea0dc247d9a5684bcb5f0d63b8862bdd400720203247dbc5f09de416e2b97ab08ccbeea19e58299d141cc3c7f16a1d949232e8fe431aad494484bb76c17fec7f607bc73146b326f25497f7544df9f42ba9aee7c94ac14959427f0a784297b1e53a9c4b6b0c3e267cb65c82cc7d78127239c3369cd28fd1e9e3332a487671f48f0fe99473921540a6701f088fc00f1700700954850ba0c2e5a2c0c70064796eca273dd53e7656a98b568c0293acd43548ef4e51c15bdf0be323b0c002314862bc54ea82219aea1ed142d79202a646e2f6a2a480cde576b75a46d96a198fcda52e9b3acb339b8c0a356928241d7d930381c068d46e93baa4308d3c4c063a8384a28f97ba7efad485ea54578432653c197516dab2de81b24994b5be1f393854e59af5a458fd53b9f8a584d61195cbec04b51596a75c1c9afbd4f778a59059627eca353f77cae57306abef19922d5a9563304835e53a9e870b8cb6a0bb68fcb3a433b88a9daaa2275ac7d6d7bdc340cb1fad6d77814e6dda74f77dd7786474da3dde021277b9b386e1cc8cc18f2d408b36d466dacdbd83e4b5f1b30515eb3b01058c027720b5c5c3eafc61a084ee7af874d02c012d73ab3814be79066370f294642091405342130d41ab0740f71d5b3d4ea9d5c471dac37e9b5c01d21e3235110fca3266bc8239ae1b1fd36bef2af6491b7b0c3affcf826c79ae710b8aff28b85fc58b49ebb3f7953a24909a1e551cdd5c4141e0215b216baf0690ad6c02a03f3be67f1e1c6be119d11b831504f51751ed7df9c3175b79fb7fa2c8ef6a2b3c93f8a9b0e92f4aea5742b7fef268ac40af079e5aa6a499a46aef74d920b5593add4a0ec605b91b3d87205f6fce04849ab556f0c5463a9f3199ececaa75fa5126c18931e9af20c4853ccf719361cf1b90e948cf855c6868ab118e333268944a8ac56c0e852c2eed4edba8a97e0ba7a8d86b39d9438b33df070f9913da612af4e591a08642e9aaf12f8faaa13946a8103b255c72a5a9e3419acf7362eccd123bf7c0b33d81100ce994448a3b0c19124284889cd3f87de511540010e23825a64605448b851d483d2d72c95b26b731302d9eeb57db1ea0971b135de6e63155284ef595198d97c642f7c6e0876cf120976f4557d6691cf16a6b1c402390f514f3462b62cbe46eb58450f438614549a2e75c09f172269e7198774d5278429c5f870a583997dbd3c293b3aae73178f1bfa51a31956cf784cf7ecaee8a507f7ec7085ebf5aba5a95a149a9739b45caf632cea3a9bdcd375d39c19d0fe2c0ef12eefd605c0e51dfa572e7bb213fa062daec725de339d11722317690066f9ab13fdea08f93df06886530150def14c666dc6ff33927076c03f097fb80cce60ce7df23e5c800313871aab2318df4631bd2901cfa22d7ae21b4e3aa71f47ad95a9f1aad067dd1f7cc764b227d944f94af679219d70fbb8b88c6c5dafa2848002c27f1e8881a9357dc7c8c95e1a7202708a2781ca9a9c66832bc5fe35abea4992d51a200bb2d27addf761519ae231d3a92db32987c24d275b98c27b0400e8a4a097fb7126477c1b87b739956c369fcc03859637a3b0b74b75438bd9e95304e5e9d11a3f55ef5865842de4696b98ad636894fdd0adacabf451889d042eeead190c3d71ac58022d8b87424a060e81426090c8575b210f1222b63c75405c13f41a8b4541540b35425582c6f0348960512554ef4d7e411cc04aa77d9dd9a41db95620dab0465623bc3b690016c1915745ab4a3e2400560c4b874447ff237bd069da9b9823cef992bb288f5b44a879ea00bda01383734654004e6852e1ece9be038beaae7e1b5829421dbd356b00b2600afedaa42351240d7c50328c235d3f88bfbed409f134ff0a095cb32742d5bae169a72a6b32bdbf0d4d1d66600a27de038b3b111e809d20c10f5ef0db7e946daf2a04b8b7887ff3552ae93bb9c145c217c5a8c3fb821591ebc0508d21c0ebf1ee5a308479adac428fba10ca9f225108a123f5da6f05324146644ca7cf0f05df3f702b751cfd2bd8cd9cc1d0c3a84ec2b0143c52f064a5caafeca851d7cb235202bda63f09c8a1093e45708aede1072f6cdbe574c2ec2f6b4f254918380778c9aceea6405d654c195a315ccef28dc8d372aaa6701e51816de77ad28970351137395d8930d20161512738959ea03ded4753de3dfe873a280402048ace6514740b15461922e338f8c261c4a35f13da2b532fbd2a23d4b23bcd809a4ee5ea78e5b4b52090c1359fa1bb3884795ca262f24c94f2eeccfb2c7fc458b0e8552b9be888dfc0ecb7ec6352b182fa13066012c773f6a4ec9422751694ea48b6f6a364e9ac062b25ae2b8364a068d284ea8b2ec770f3657a1dae0892e80985969d07166327e06b1b96f9f5c5b8f30d548ce0ca62855a97142c53cfed36f329d85b9acc9c18cf91c25053b190f4b70b7ec4467d9bdb1671f0ef84c98b04bb9bf116442b502c8c5978867c4234d5407ab04519c63e947331c9944bbc000200a90ed2766f8982cf84b6bb79b825c194e137ccfd55e47f1dd9249629a1bb6ab118ba97a292ff734324a1cf5cb05d73e335c9fc72bc7edf7294fa2dadfd00f30737a17284354065cc06bc32860bd366f36afbc9b453fde52905e583df7d262752873c3a033d6875a0ce558f1348c7ad646bb52ffa2d41ad513584396ef3d5285d8182f33002eed807fabe94b9e6b05a50bd14cc6639bdd3c5c1d0846c362d9750a04410f22bbdabe1345dbe610defb399df51a92f8029b39b1a972a06d4b8591cac1f8ab1cacac132a077a0c0286c36c851871c265f2d3c7751bafdb16e4e71de012effb0fb854cff6c9801bad112d92028f95e81eb40278c828347d7a5007ee6b6b14e2d05554cf73db9e2f625ed4e65d9e51203e6cb99e6955894e24b62cb2640282088d95af6c1117f1c138f9c13fa35f320a97f2fa88d1da05b2cec389d1079351e8b53d5a77d1b3cdd8243910bd91864d555009e7f6ec32408a46508a4f14e01442c3826946f0f5a70db88c26a2728d02f7aeca769d89bfbf0c68e031ab2b8334470d66d065ac6ae61e7a26eea7e72156c5c5c0afff987363cb45ecc99adae6d4010108c02661bf65bf9eda27af9eb4ae2f53316072329791a963136e621c52238b09cd0c43eb772e140a4bf43cb123b6b1493cdae136e456e83a3d724469a9b286a817cb2b6a1d893c447657a2fa02d3c964c1cf78fc098a0eb6497d4b5686f5315c4fb2799a5368ff521caee1f09229a720aac07130a122a78d1a470edf0506b1230e4f96e84d95c7b1b9de934f43c49ca35892bd6ccb976cd055755ac823acaab6a61fc617f6cec10d1cd4c4b1efe05cf4dc974e85490b3c5d4880100b71a7856b69a4e68efb6091ae43d28b3cb96f410a5a29f114bfb92d815707bd3bd891158d6581015bd149864bd29999afac00c7a62ccdf16a75e448702e7db7c2ac2935294659022260f83623bb31132e787128530b1e4060d64fb62b5a468592cfa4a705174570faad5a8386b05ed1043382995f947753c8c9c44b0bb2437ebb81840c85e0e14053f021149204deb03c8551c935878169b253a136a28eb5d04557516ea56c87f5712c35035c283342be91de122e837b7bf204e39bd0682623a23d6adff2e123c0d0768bb58a0d03e3af5f1055c1fd14662d3883c05c96507bd7e70e21175b368ef5ad5bec9d283dcedacd63f76bd6a2014d6c0131a3b0aaebec9014dc02b4ccc10680320f8fc07791a2c2ccb209963c70d2549013e39b37aa75f3f4e5cfbde4d0687e0020b2fc958d48bbb3a9fccb759bf73fe6fee2370841009c46cea4aedba42dd31a9d013a21a5b862ed125c7d4670b1c032ae654af4dc62fdab3da01c1f7c6e47cf8df7da4d69846d6adbd6703b7238967fc9021d1eaffa5655ea0e22c285bce9690d86b1ac368813254a0ce1bf26fcd9b03b9740c5edf31de8abbcc8b7044cca9d07130c2825c4217d58d7240d93fc35e61b4826fa6c248fe03d474a1df307162f52cef34b3318629706aa8428079796534f8f45d898b7bbd93eddd2390932548e46f58b9a0a076278bd7a168bdd7f544522fedb9d3194537c2bb135413189fa32b20ee7a147b44e7472534ebb06eb0985509b627c6297ab519ef49291e8eddd167c5ecf7f86b753b0eccda23804c4a50ccc47b68b42359a78f0360f9829c6abd0e6c7156db7beffdc4692444aad3ca8db46927da9ce58329b6e710211d9e6ca34dcb55c466ad8ae9d0bf24b734a608aad3de0b0dd2b21c4dbdac1b6cd3c0b17d72dfe24055ce9eeee1ffa5c0ba017bbe114b6a9e3b8b19fe3ce4ac94b9df094e8e6fd5535df01203dd2774fb8decb9ba18e34e734dc610c15b3250492370513a7ccedaa2a68a9d5cdb634730f44d94f6e4b991141b06430c2ede4edf18e6af0a78c15703f8038cc04770fe6ba28442efdf81a556bd895bde355936b7e33c1fba917740657881c18df196f2b67bec3a51b5b78557f4703c42e9e5f0740423623b42b3e879f0f2158d6c6091a152bc5c6326beb3d1eaa4c2068fd6aca76eedc8db9ff1b68020a483902585e838907ee7241249778fd0bb91fd649294c4568e6c249fe68ec92c9abfaca3ff6db1aea59bd152b00e50116ce3e5072b16256081a51ae1e49699ecd5d6033414c21f13260d7b02b7178106ce2768705b19fc82e086461370106f6512f2d155e3cbd649a2c78391ed9425bb157c5ba743456e76bb88ddc761f40909efe08eab1428c36e1c60b38afbf528189e475f7a72155c416e3c8b262b369f0cea21da73bc24f604cf8860692180185a5d909e8ad6de1458727e1610403a6eaa1721501727e58c0d1adc0527aef901f1639f5096f64700703b7d0cd091c91749f983851900ce9da082a235fba2d6690b855dd95b4fd89314a85a7068fde46250a7bb57b2a0ce98c1cbc12d9c1b0695b694c0d94aaf23f4cd857001f81cc2b26f85c6b80c432c1cd1eb2dd9609f86635cc6c59cbdd48727d5377e1f10f33ddf7e3ad493ab7b51303aa67e601350f84f30a4ae581176f6861ed93635098a31b4b036966142e885157a365469ed8b2c4dc1934dcf94e36afb56075b72ee4a5bf452654806bd6a4b069d5340c6035dc38791a4384f4f368e5f1fcdbf3d6c5b69e081cd50abc407e02a6ce07e82facb172607222e6b2410fc6e39156c12a879a0d07291f240bc50eb355d6f2eb55f1f6ba396ac12b1bcee7308de1edf56200975310fae9fa60b8223cd8f2105b400cfe7f5f7806e05254356fb082f6419615be40c77199d6658e73490e7c85d1c9f3d40fbfa65c679c220f2d7b8ac0166a556091ac10d78d2ce2a7809b25212ee3ae1a3448bb2398910ecad76c5532a4038dfd7b2e1c020730cd9653981d836284816620a7a64774ba214841529cf0444b0bb5b5c27a393b430220e6e57dd553b5a3da9fe9a30ea22e8376c00cf4d0010cb037593023c41e35d90501bd5770fe1e0b45797b9eb83f59b52bb10900af2ea0b5f41c9b633a8893a35488b3f5ba19d285257dae252f5b587c15006a9c19f4d04f41e7126e2db64eb09ebe897ba820fe896fab0da8f4366df2c7939662a515b47d2b3f0937f3bda81ee43b6768349720ce869c205616e156fa3d8adf8bb3d7578e263a61ffc37428d5fd80967486f905bc7289c4a6ed5e812d30969cad55d87709f51cea6eaf0c9d94a6a3925b0ba85d5490b7c796aec1679bd68f392b6515604edf9180f8b621d44327785f0b4f5277c03e6248186b3d67fdaa1bd0b19b1c1d60342759eda57336ea6248d97007868a1a14ad23d6147cf22b47f05d198dcad357ca3e6b8422e52d5fc811bbf62e7a4b2394be3f3c84526c1043e58747fba55bb242900bbde58b04375a87b4cf549e64a07d136bf09d2412aee9cd58157ed921c44f7efd0b3c4316b9b2fd281e1406db3cce3f0c58b69cb33f2129bb9fec633a03ae6c25567fa16c6045657c389e3c4ed08651c8c97af2c81a76e764b51418a97b1071d0eed5aef5a018a8787eb351e3b4bc47a8d73b1684da85f4de7f3a66bca3ea6653966c3459281f013852f7f5b47b1ef95cec9852a3681fac0a30b16052fc581b85ccbb3430465f831112c9278329c095d915108702d19b5998a844e276b4b7f767244f3a13d53e06abac8cc9a500326578d4351888f86b474b413a7eb6b954734a888afaf0e5dbfede083d6b4d1d9cb7aada06d92901deb463adc2d3b081aca68fe9542320666569c8363b9eacc555b2df717796cdf028854f40c6a489d25b563d87ccaa2973d4226851fa9007867558f50bb16282f864656e7c896ea6325133189dd0e038675d8fd16a0bd900a21d06b59d68201c0991e00b1c6e3dff7ae3090c4e615eea17ce5d56e2cda9f0481f50fc6c88621353b9bdc2e03c2ec1e04c3216d415b9294d35152bcfa6ab4e8dd4c98fe7c8041a6fd5892190a94505e19203e314d29ddef5df8b49c077086054c0f3f5a1017fd635a63802d3a6ccde1f5630be2e269810b5c6e7c08d33dc960f4503ff0c9cd1c22b1ccd6ea8a3c18f2b00c49f6c97d250b35a6935845004a8222ef8d5b7a5ad2bf4d4627175745212c01b15166c4dcdb0ba00c39e328d4326fc6866f0d72c58afb61cf755a22c09a1d7ca17022e214d47a9c3daa1c584fd5ffe79d349e9a69f83e1ece8467aacf49503806536313160b80b41a4aeff4a575b4119c55272eb9d478f956326f2c31ac2a433062e822291594f4a30c7e78e9aec2c5eaf79e4c24cb347864157fe908ff4c735feaaeb2a5d1555753ec5249696627dee01cbc58f02655a0af4b29b5cd5014976b3210207c3147d9e7bff70b54103b5220df527b6a6fdceba1838cced1947c613c9668638108a8fed9e2be357f786855b628e9d1300728957dd848505bf119389e0bcaf340d73e5151483c1e9917d41a677bc8a731efb27b3db8b12a9d12790853ab329629f4cfa3f1485840be61d9900de46a14fca0faf65a43e52536d62f54a35e521fe1450eae60f5c5bb6a84a6ab4fda47f8a2f14fc6180dfc4d0711ad7463c3c8e1e227bb19b372bec3513a8d9017679f38d45c1ee51cdf8b52a5d1475647032fc9714a91c909ae7214e52579f533491eb9e4bf97560486415b0e43840a2a2e07d599519b04e276d47e5370148eef929ae4dfcfef5d03b2c8cb68c1d8d55766f78c7dfb1cafa15e9d6bc483149e25bae7befa822b2a86059465f44909c277876921ea9606b07eaa74b3723d501ab5e7da2e9e293f8a35350622d02d00651acdab35fc05d2ea661af6445f98865473e9ae0ef0fc4fdc5c092a9a21aca678f9510c96f38c400b4b245d230b4a397baaee9011055359d12e462d0e313caba8b8effa3cf33a724db5b1798f49a71fbca004fe3afef67a526064fdb7cd64ba283af7ff888f7e67aa0ca78b1c2125c535b0ceb6100cf6e48e0147f5ea45a11c138160078e843fbf717b7b4b63964dfbc677674116f990d9a01ae8132827b811f3782e45067261e5ad16d3a0201438cd36a6cc26a230ba01e531d383ae57d6df64d41fc17d6f3b8da51c573486200297c775bcfd34330c00cd0c1e50b08a66771799253f682f7cbb691067ae2cc7dffa7397385a46b3ab61aa21ce09561b289be5f50689ea12d3291060350ee53a5685789bd0c0db292f57c7e92cacc28a84bb4074e33999f58be52c0bad47cd25a69f4340ad8532ae647900d8b077f2f6e73bf186283970b6638c364b180b8f80a942763bf766585afa19c4748208b7ff81bd593c9ca13a9909feec9014fcf1ed909b17545686cb8c3f4a9cf0968ce14e4fb0a9d60dbe6d2b1c80df24519a986e0b21f75b6308008bdbc0df0cdd3d7bc2e0acaa5cc26e095e3191f3a979fd85a07584650d011c5f5cc8ad97afb375f3353433de46d602500d75398cd26699155da8477d7024140afc3fd9766bb36a720c6e678859dbe1ff4e868c8048a52de29abffd48ebec4f523508e9364f85517210f7084ff6d7ca333196eceea55b38fb92231febe1359a0c591137a32b23c40598da61773879e674f137eceb24ed68facec2860ad0165ddccc57e172bc940aeb5089ef568738ea173dc11b4b3a9e28765ff76703d340d25e971e0a27ba4100b5657f87abc5b5caa4196aa090832e63beccf6d38b1772177f8d18897cc0a472e13f898bd7261e780ce3fb6bdb931c9ede6750ae8f3a018a05670b80bfb8acb2245a2c741d17cf0bcf32bd6e0a73ac34728f2cc2b08d1b574a6d8ba650872a8ac752c49f1116d177805d4960003661eb5cae022ca53d2e887e42db5ec7814bd8cc1c3107309fd32fa515b931979965cb701c34730b80879be72bf8af4d9aedba3a7dd909e0e0454fd62113eea8071272a1adccf1cb5e51738eb1fe322198dcadaf396c2d3ceaf5886909d443eb4732931452ea8d607cda2e69cce1ad7fc1a0777bfb651bbee0dc467cbe07dd819b4ea9740741848ce5dde99b1377c09129cf72f9648816179595e72c71d978fe11b60e8503c2e9db5c3cc72c0c2345e587624dc1b2f529eea70b7f192d7f7598ea5b85a3ea51ac280d73db0412ea15587dc295da6ba4994686b12bab0db27130684d6a67a8130819b23171927c18123e346ab7f182d132cf446e609956e953031963f74c56527fd8a3b96df94b104d2fc8f12ce6e094b7710eba0380eb8454d7e07d41ca29e09471c9c617ad2b0807c1c16489d289cb8157ded63a3b84c12452a1d2050a6201c7c847b60a1c38544eebaec1073b4c03d325e0799b553a1ef894933d33f6b087ef0a506c8dd3914b61fc0b58e1c936e3fb8752ed0202d0be04818d00590033f330513a19048f629561927419b53702acc132e407c4a55e0c68cf0ed50f47b8dde244c3cc673989b19f500f0f358cf3bd27253dbec84933268535fb064090f34cddf29ec249c99477a89324567c3c49b9d0a153bf12e6fcc1b290c44d0334c84b26c280f147c1ae5351644d08b522188b468f3c31e2bec5bb790e114f2448d785ee0866540e0a7bdd8dbcdc5a27a9527e84aa83d36411a295813f9f1f6537e92e396a8f2b0a970cb77e02b0471f296fff99a401fc1f789cd7f2196f7362c4e9ac234fcca094880471bb6d06dac45fa77e7200432013d6f98e839fe02bb7d1f203a6bf6a09ba1e4d50e0cb84b742fa5d89346d0544c4e1b21ded624ad69f9ef0821494986179161186fa5aa3696658f4a661c87dd691c3c1114df879b6c54eba0cd629e3831dadd823162fdc906dca71139535ca9cd9df709171106cdaeaf2d3518b6dcdd30e755621f12c2078a11bb9694cc1fd616e85a6b59023dfb1474a1928750a0498d36408ca3bf909d54ae219f00245101eaa90e55faa2671ecf9a2090f35a41ab042b56f01c4d5b44520163274d3205f879e533e8f0e78b27753518aedaf1e0853c625d6b82a9d7fb5398248bde30ceb21ae4520cd5e73bc2ebeba11ef1691619eadb1b6f7b86f71f8110700f1078908cbe2bff9cadd6185f9150be12b05199e44424f3a540145aecd243d0f12aaa6b543d3eb32ef05a25b6e6346f66ad1c49de04f6856046a0baef60115b9178e876851966128e12e7b0b37db5d75b2e883fc51873204071d4ca7e30927119768dc460b7380176c3c3df66067a2eb26379825801c5eb43a415bf1e769fdcd60a0107207c0847fcc428cb0ad811927ed211c8ecdc10702cd2d992c903e9af1b436714f2f6e6421ec14e080478c32061ed9d4f9caab3476027df4b189526fa960f4dd360002303ab51e36e7e9da7fc6d896f4ff351fb4240220450ff402d80ad0851d669fcba1fb59cb06af7cf97a6b6fdee91f67edea7954fb673a600711c96bf211d3e2bfc7372789add31234261cd4eac39183205bfa527a5074e42d1630ac7391b44cd795efe6d68cf0ccdafa36f21f402a50bc2e6529eda5aecc8fe9cc506bd5bd2676706a1baf500d84f6909613234fad0e1ee6c5fb2b4299cae7743de96d5b7af1f05e4b358e3658e4f1b9821b77876722a56364a2e535ffec1782fedad54c5f51f551aeba91a3ec28a330255cb31a7a5a8380b089b2f1e776114cfa6a91be6d8f2f3e4fc9b87bc56e8b3408ba07031cbb7832ed8dedb1558482002764cb2623deff30a84f575498f0b72866ed1012ca1c3385dc7a913b1a2938e9a2db6de22ede0d565efd0858b56d970d286578ee7e3ea7db8c12062591a7c9e727f8ce06b28bb04f53458dacbeaa3c4d97b8af4a334094849ab235b629d031582d204ec08fcf611dd40dc73d4876dd80a4e414080d2fe4614085ae70b8797bcba3153551e59e6e0d68b21b243de186581c86f02e15e81d5f4700021089319a169579345952a9f078e5147fd98e36b10c4a9336b9ba0678d1231134642112c44b4f60d3f0372082461e2797dfeb50a3c0aea6888c00da9de93fc542bc744bad6a46bda1cd35f955f8ab985e906a18e62e24c5da219e618ea9b394b7461377b3affab718b9c26a5f1542ca518d6bbdf4a8022e5b66bb85b1ce3792b7533b859d0378311456b00855d9e12f991e8b6d46599e4c66b641cc4e60d14e6c879bfc7e704503d5d0e7b57d8add59b5a888bd3070f1aa1dd08a6be9fc939c5c1ea745ac6afbf125c55bc000e3326a921f5ee7700b42a30994750de3f4c27c3f35f10cc322437017dd8f80b3e472cd224342a7ed7d9507ec41257823434bde31339189bdb64b9bd5d37ea3df6075e14376ce80e8cbe6ed71764c48c056055b24d1749cdf49ea44254cc5ef17f50483f920806448709f2ca8d864d763123619b1bbb1196124fce72ddf103743e9cf9c07ec402f2aec3f66fe868813fc84067f7a0e9352f7f1ddfc96638d068b4c0080c3130ea6a9abc02d036301c9805a4f3a8749bcbdbc0c497da262b6d6d05afedd0a8a7ab150caabd4880d702202e3bf498190c69c20e5614ecac663895e75a133c8f263a3b734e64b8959b07a162001a4f89a6fbcf0aadbb3eac050a9377e699d128026405280b046730f6790574356e49678688275ba67bd81e5e4f473f138c663b77d384c9b683a0742b2ac0e0f2705610c680033c01e2110a8026570e3f7ba7bfa7aad18d0bb5c1d458c956a70d85417f80857e42510e43d6a451ab266511461aa367ca32aecfc0d21df73d4d77fbc10ba559e8cb1713611a63eda01ba9bb1d768722ced8aa2d7bdb85cf7d804087cae9e7d5bf79ffb90b0a598f39f10879be2bb9f9503f0abe5ff686bc7159e82ee081eb4f1efd6825cd2aeea97de41a364cc31e63b798ff44638904b73b379755e87cfd244548aaee71d96e948c4166a0c692bd35098b4c1fc47ec3d226c2a86767f8d0587192271ad0bee2f5d642db4534f62348ca7d8813de2981c284a7f4a6f2ff0e3d800ebc8268bdb73508e3c674dac89e1b36691387809185adfe82b7d966664d32e178505dd87f0b08ccf59b688681a55722ff65afba1b8002d1914f07ccb4ff7680c35c7425aa5f4e442fee18abedd72c3e3f67d35b77a01d615d0ddc2ddc67835377ca24aa100f885f6384780e0658e1e8ab2093d2d5dc7198ce99ce26de9de3580fc5f286d76180890cdae7e0719e59b0ad274b12eb91dd39e0ec1df031800eece9060f18da8f1898354d4ab12f319e25353c425c6491f0dbcfc401424d1a69ee7b82803b94d313b63ae9dbe370dcb07576edd9d5d2e2ac02ef2d69dc26b5c3a837f01c5a38439ed182fdada8009c2313f4b64b42c329f7322dcbaab40afe7c20151476d06299969bc7af04698b5991666dbc33fe713e637c8393aa90d63ad1cd2a19eaa002159114538186b317636e1dde637184e78e603eab984dba85041649ec4f9d8d89ded29230f2b63d09af9eb53b8be4f579e7fda1aa3c34a4d21a1cc3146d8b4701e14e7fa49fbdcad1dbf1c39eaa858fdd05ace6fc21568cb9cfb6cb2675692a1a43b370d8c47da97d050c48bdee9ae72ef7a532e2dec92ce54cf7214d38e2a6223f82813102387fa4afaa884c28a26e64f58c4d00f04730592b46bc3512059a1d8d4ff1bbd18c449e92b5e5b4ecdfadbdeb22098497d43c386756f7dd1fba56149d8bc1194673d9d1797111b00758ee3d453987a37132992abac6c3cbed352c2e0f0af9364098d8fb24b327a4f402ace06473aa1b38305f51d8de756e163fc13bc6465e0339fe516adbb570a819e86dae42f95701aa406b772b869647b2775158ea0187f3f15d7c3449dd8daa9821a0f6c7713cf399d1c5c331eb088ca34d0a64eabf141ee0fc34f893ec3519dd961e1c764d78e6b4eb5e1ba9092c72aed28f266425f376705306e22390b5a7654494af0a4bd28b076805856de9c9592ec2aca3056e07c628ab0b9339466941dfe10356fd7efbff052bbafcf0d8778f918e2572b0846d3e59c0eb8157748cb188015580b39094ac9d692d1a3643c570d6eed5fe2cfa530137caabffc6baf51c5f9b699901ed0be32e83cb27b030a40baedbc3f225986371c82bff8dc94eee6c4e357e1e9339530e6c24844d0e7b216e192ca94047b22c9aa866ec7d1e249b96ff6b3dbd142fb604dc2f94029620eca50ec791d6e496c1d4ad2d9e86a20f11b170a9883032e82547b22b97fdbb0057454c61d4fb39b1d031dc0379fb9865366b276696ece1d389d8100c4b7b45231008ff2576ec47a4480ce0989a81f9111ef30caf15c8f4550772c9101750fb820ab923b033eb9e61821261bf7c3928bedd80413ebc0439d1610f5f8bf686940ee96e90e91bbc23f34106f487b922df2ed1dc8616f23e490a5a010e416d734fb4ba52f0b3dbb173eeb86cc841fae741c7144bb433612d44cece2e8d2d30bc84f94733eccb7b59213889f424e21f1b5c865a56d1c2a9cf6b79f799613850f7d6b626825a450f29556f801972169832988045929269c86966da19a13ead6bee2d2a67ba7af81e3fb86acae8ebbe2dfe963bda9139950e375129209be2d8f591947c803ce1df7be4d12eddf690f0ab63c59bd24f626f54495dce730d0850eacefec4f6477c8b6fcbb2a07681bb15a85a6ace074f8646ca3d8772a84ad35e1872147a733483ce05ec431ba3f272fa8fa89dc99ab2505e91f3b0cd2054af0ad0b9db298970b20f377b976046d996647c1e608da886a5d7fb9d21d5fba59979af1bb822a950929c5fdafa0d37fc9d1b73a817432f5b762a938c36de844fe9433d373cb0b841a14728f350f6b0f223ea7c024613e09a4af5bb8d78d567b1b206d998bc40c554146f75b51b33b17fc252aff12225cfc8f6f7b366c72f392710a2700661acd883e1cf8b9b0f19eba0e34bb6ef8045191ad2f4decdc1efa489fdc419a86102fd64066b180bbaedea2c717c554cbdb9a0daba6a34327e81a9790a92b12f87253c4ab74f9ccc9c7a2504a904a113a82ed6921774fa0a39019fe706b27244f4412cec89d88d2b00b5252e65a961809405de3fda2c05614cb85d8c054aa8a96a4971fcf1529256c083a64162730d67116a9bb1c8a638e3c674cd30cfa6cc62116a98a1ceded358a65474df10c1d3d8563a661f85d0b6ef84132c943dace18b9011ded24f68693250cde3a0719673e278ec4923251c844e6a032d303339ffc1a9cd06abfd16f95ec22b7378147f880d4ada6807b36e73179e6c951289ac77d70fdc8a2c162fd35cc9e1db60c954ffd70880fc1ee929457356d0880547503ca30b6940f31495990fbf1e19ef53991acc38885ea4162063640f81e03c85570b9c0ce9770f30c221630279c8bcb56b6be63bb460e2b65cf1f289a49a72af320e842971effb29c0f32399c3b78e56269c6ecd0e11ce76885648b6de8d11f8d131227ed45845ff7af33a209b231aa2001736122f74c8bb28e220b74aa12851bbe8050554145f6632ff6cdb21cc30ab9c93b9fe27e0aa24315b6e59f43dec198e83c9d837e66d310b3ae0edb1cd15801b0ec6099a6646ccbc84fa13a182b091618125a368eab6c6e5f2a18886d4829941dd936482ccd538d87fc38596a3694880025afcb7885f11d87275abce3ff33a670068d2c1a710f090a5ea41d954f040d603d62afbd0d2a4290c035a6221f91e3339bb5fe10da5d6f18c24893c1137dcbe957c12cc8f6f3efa8d2d7c99900e04e968f0181204150c033d14d600c2b310e92b5514691eb166ba4a9b7401d888ccf73780c698797e8eb9964884821995cd4d34f1f8d9ba7457fd9d4e6c414199093b961acf76c4492be47752ea61aff341e4eac1ba46a87d3a87bbc96cd1a4c46c0268dcf0160d91a8167f0a91d7153ab243f52e9db3efb7202ba2f8aca91e3fbf2de22421aeba5d63aaa844c34fceb8d12e512c5eb55cc0948cf0cbb5efe18d4a6e4b283e1b89cb6b7bd2d87803ef8a3ea5e3975831480aded3a447a8c09e52727b3fe508560f6ab067c6042ec4df6664130bdc765303408e269c95d8a9f11be9ae23db579f64593f3599a732f4a0ab7aa3e887083a7c81930685fc374a61e63f1f02c49017c1df540fc73607590963a74c16777290fcab4aa32dcd68941cabad0edc919ce625d1226afa414d380d4a3d8401a4d902a91cfbe7d6d5b2f929704999769f16f151703287e6087e6d1199e19fe428e6086d21a11df1681fd6971de30c611ef07a4ee7d7a9a29cf83fe668ede46b4ac149608416577aecb0bb5f30087deb6011af6afa651c29aee212ddaa4a0905e9fad8f5cf36db2f971f8192ee4bc778a813db7712270bcb598600dd9eb2660a77420bfd95947cf64c46f494d7d06c12607abf3864fe74844c96e99084921467b581108fd5419033049838502538e832d9a096cb7fe70de1efbf58e3ce6e2f761d8a5ceade56f3e67450ebb70045214d1f5912e02af256c841286f8a5de18aebbfea3a9105f7b9000cf044fffabf4fa042042428289ac053e60c5c1a1594feb3347bf413b8709298cc648f83b22c8b457befd294070f98e462cbccbc3c33cd2013f52327d5a76b6b81062aaadea6bc8cac96e433ac28bd4229fc2d9ada29d891664e3bb33bd81dc764bcd31433ecd97c3d5e5fff6ff803ebf5e33f81bd630dc2c75e8cc88c9efb29b37201d3dc7276225f7194eeec3e6343da9b10190d9ef23c01cefbaa3971ce939bd2c25d912b73947b1966ff2ebc693703eb7cdcc46037e8696a1f33d1d197970609cae5352498aa46ad81a5159951afab1b91b648c3bc95365ce0f7d4be88c8facd55b97b93a12e574e919e676accd9239fdcb0579028cf1aa85813d1b3e4abcfe24aa87a8d3608fae0b027f32da46be89a43744c6aad2ea18443bb3c91b7921c46113183068983116b913bb0a5ff594210a08ad17e338a93d8c93e5b7a206363123cac129b53f4629bd1ee5fa37bc6607474a2d207f7e000893785dc57b380bd6e9a35b0b21eb219c398182ad16c940ea371f5235c249c86b978bdc068d7b14f915d8a16077433f860931dfac83ea22a5f3abbe38a348ed2741cad6733ebe846d262942d733c793d6775c65e9cfcaf3b0f51084d9e70dc63181618f209f279e291c5b61dafe46206bc0a888a5acfa723e4da47e5402c0bf732e2b9c27515eec7737464306acffad663b5be9bf8346932e3aa865534834dcf3857a02c9530c6182f6f967f27a92548e9a8a64b5ae7c894f83af21490e7c2101602798d14e78a22b0defe7d0086182385e998eb44c437c3244e96196bcdae007bd250372d209acd339dee6475e89ef69ea6b1815cf92a428d5407998a5ffd977d7e5aa42ab06c9e2059be2d35978d9277887edad7c5f83c4791449e291c52de803e4d727fbc1d7dc12a7f27e6f4e45f40af7578830c6e923c42f8890b629a7c33e47e68440833c98ee6428d354e6fce4134465e58869b4489915186238366d8716128d8ac3f7fbc0490c7e8408d522018fd08d702333fb53a5952518531f2207ca7a858a0d03ec97c3d919be401061bc1c7efe8d55325fa1216df0d8d873da29c05388220eab62e6803e205dd8d183632bbd90eed51dc959a1b2c26d870f98a89b944cd488b2c902e7e906987adc900129c4ffd65c3b48498be2000242707fa36cc2d1098407f99d9bd166b4564a85ec04fe7b8c4989e1769967f351db5bb42fcf7a4ce29f7424879899a8cf759337b9bf9e123d5a5b9c81de68d03417bb3f66cc66dc25dce4d41ac1a404bd621b4245b71e886f0c2127d722d5bdf08114aa16ca15fe38ee47b9246903893dd433247ce513e893da22a9f9a81ce5ab2d42735b9b4f07786c7d4245b87f7365ce51ed517619a4e0a09d395a6db103cff67eb14c342729506296e71482e9d21a719dedf8b4e59a0590ff88896fac76868c929dee4565c0bc524b3d186864ef674a062ad8bcb41ae9f3aed936afa46ca7fe460ee54bb0567929f8da9b95375c2b0cbac8a245077325e178b6088fd6bf1d5dc9b22a3406e8a2c3da6bba87575356c2681605c7e58a96b9ac6037e08ebed0d034c2069f4e084dda3932a5fa73664830d6c5231d0149b47d1fabd7eea694e4a24a33a2d625c758f7c4f2cddf14586c91dd5b5b51dc11358ca62ce807a5ba9172adc1641776a8a3cf251fb690dc7431e812c2a1ac0d304f28848dea19f620fd5298c0179247b40c6208fe4bcf6d159fd67271a2b9f0837cd0f17e49188b3011037a0649d05c823a84c4a68c503b21216ea13239a019ebcd5880a428b1b98d0731543a603e8c4a5db71dc0fe2032d15549b95e5320823bc174a87ec4b7c20ed99bfb4a0eee4a248f6cf38489b718a06f120b45599cb6326e5843231fc3169b5110a3677f2403133d15d9605801809113551eaa74f8ba1a156297062d02785b0b315c9052d3544444df2a2fe029cbdf9681a366c5c75f2976f301e34954825d155681199fb7e7aa422e2716e5393a1c022482e51e4fe8c922962960a678415bfb0932546cfc2262fc48634427523f09116f59c6062b5a2f05370765b7b65831f5d032689d756a4dd9010d554c7986e565d0f2dd004dda1d3193d47a70c51e37a562be89e4d654facc25f2e0ce99ac2f16fab9a2b80868156e43cc744b6910fbeb6bd2a808108817d84d5a225a7c0f3400071d37068b8d9bdb7f02ecae0aa8a4ddf4e001ed991d4112682d14b78bad2149dd20513a6d80187fd50d2e55b6caebe6a64a8ac72067da9064bf02e07b1a1d49b60aad548f1fde8758ffd256dc7d4aedfc560ae00f78198993e0b1cfa6f80e909236c02713aa473930ba100e484231615589f48ea8a394300a6d3fb2949e76e003ca69774ade9f9a934b8ed973c315aee66d8c7cff94382a923804bfa6d1a92b3d0233f45e56ae142113baac40413a19ea714664c6fa1a600e22c9a05162af81e6dc4f2a3ba7b2a36b3584e814dbe0a302da29ad8b5fa7427d0e1165cbfa44021f703e7a7e93193a76f3dc187620134cd7a35a4f9774a81cdfb67e546a2c3e134e9ac100ded2c010509ecdd211ca7ce8e739e17b29837ae20e288d1b3917bfe1d536cd326cc2eb1e7c9fd2ab58b0e17dc06c3cc72482b497d018b8a8b40d5736dcb578e7c6109a4238999e7cad5524385dc8cff864da9e293ff38d86e9c03575d1794a8c638c2252becc1c787040ae2761d7c2af560a6385b2114048e9642cec5354be8ce441288a5e66a4c1dceec91547204e6153a77ffd50e4e10fc755cc92ce0d91101ba377ffbaa29a1ae9f73518579d8e1d62e57e9c249e07e2861f5f3651425cc181ac86640a18bab383471b314a4d9798617ee4eac0a95b4d76559ccedfaf42bbd17c7b9e7ce7472059fdaaa43f26e9264130f5ae8f121cd6eea29b5d0b9a4353e4162fa13730e87d1adaa9f0001fcee55a0bd99cc0ff4cdab34c02b1c9913af7fcd4f51b9e2b47e6de8577de4571382d3ee943b471e1a5f7a199cca411d206e4359fbdc6d7fba86defda78ee0195d8f5552d402840cf10101816801c31b804b947ff40fb83b8814b02dba42546078ba36dd9be12761ef46c45162f006efaba328c635d5f788edaf28d6b9d899b80087cf94b5d23d93bf65fab4e00387dcd779a14ceeae1300cd828bb163d3879dc6cb1b88f633f5b5c04961ccd4f31267c58bce14f8aa45bc50922ba74493513a7889255424fa4a84e526f1191108ebcefddfd42d9f30538fa55effea1434bc47e6cd9591d9b1c6055e4c0279699daddbdfd027f0ca2e576a4ace6f36585f65f02bcd9bbab3652ec63e43109c9945f6cb49e1b1a66d008be0be3b41b66180c8a966454358e8a762b40b20ec1c7e5e3e1611cc37c3465ca82356d77ffa3014bbd51d977a01a9e8738cb15bc6f44c0e85ac34316afa44cae26999a2f42ae3d7fa7faf409fb7a081163a2b9952e57282d6c910e1004c22d003bd3fc80c32828449583dfc41b0a87fa341e85b8d0ace1aed36e6d08c6c2785428f4b0618ceb9d63ec02914cd09560c0e6e5dedd5ccf49df9cf8234133a5629c96a5f0900eb79e922205d394cc70204a0f1868199f8b9c6ae28c089e1035478be68388a4ab3ae64387d3229ca9c9bbb06ea7e4d36ee6289947495a4fbb0d895a26c11f0671bb0f42ed78c293dcabb47f9a55081f4df62c27f0c3e4dc1bfc4e4663efd9cfa3c536cec50820983d527c00ff4c804da01af8b411e9aeb708d309018b0ed2251a3a6c028c44743a70e8f0c8e3eebc78562628b39c6321da49129ed143bf054d3c896b01b69efda754378745f164081ecaf5102de75a94b868036d533a73baa871607ce309c67b72315d9b3ab4beb9d49c677696ba9c0b71a17053d7841cb216425234eb648cd9f24992b3065ea80b6cdd81eb8de9efabd6847a443705322fed1f5ee3096521acfdd89d8c30384ea029ab579575a30dad9a603cd126ed211f06f6af6db6806e6fa5c9a65c4af566ca09702775c035dfae43ade01a790b259f1e8dcaebe94530ff2f3950f380962e8205117712d2cd24d6e462a784eebf7719c3e4fd3ffd2dadeddb69452ca94920c4b056f0542056c42b1a9c44118580394734e29594c5b163f880299688102ecd22a98269c9327c3fa19f64dfa64537e3efb88b029e287c4bec378416cd6dab43256524aaccbe130371c065e97e1c676dc66c8821de9107539b00c10d5e8afd945c77e2f22243d25a5ac28b3b12c6848126b6d02c330ec8a4c87c374f75700aceb1e204c3f47cce36135441150480ba08cb30d340ce11bf7086305e8cc816934df6d7b1a58940307a161d459b67d306db5a100ba1197cb5a6badb5368628d84929a5945a91a453d24fd36f57bb3e9212e1a23ac00ff08db45be84908ba8361d8a725e50451d334a8c19d1ca21c3b3b3b56278532d25f33db30738e666d3d596ba9c41acb423d6dff3577aafabbbd45e0bb33e7f63601983b8f4ccd5a6badd5a27faaa6450f02fca06e04f26a4f3b97cff3fd1829cd14c8bbfa61d0f44af374d2b4d04392d534106807b4b3699aa669ee9bf3c42972422267ce39439f8302db83405f8258804020bb694eb82f9cd9dd7d341af96834f2d16814fa1887e338b90038f3a9a5118c39f557a4bf8f578297c3178457b8fdfcee310e8ce1170fc7b994125ba96e9069b3e6abc958cbdebfc4fde5a691052f14e90710ce63a40b5213d997ec83b2d5ac79bd5e2f51263596353e536b4dada9ffc54cd3361c4af886c36fa05fbde9deb9ecc836fbd6fb615f7b20a46cebbcd5b9cb8ed06f1ff2bce582790ff6212fde98300fe4f96bf3624d458245f90b2bcd1a0cc3b07e53eb83c129788565f1e7dc7cb8017e3a2d7b67c632e66cce3967ad3563b9aab173560d853557844cc5cccebe5ac998b56c9bfe5c2d8761f914de82005f702e7777b73ec353ec10809d53d9ad07b8fd1ffcfa4b36c482d249a97438a54ab11ce92fd51f74c23d256ecd8eb71bb6015c6dc5f2998f671384c32035213f0595d4b382363ed3b3b919913e9f824aa22bfb9ad8d7a73f85f504c2c01a4a2c0598801267715b0125c6e2ae522a9dfe4457e42e72918bdc5d4af728a5bb9472155acd0089254859c2ab0b287465b8c50f9f1789466e6f5a4f93651feca3d3caeea31d40a1ea7382a58fc17a21d0922c537a9a1f3cbc7cf4d72a1d3bf827c62f25571d4c43e3a4947e56aa101553e798708a9c90d0d951a18e546bedb576421b9bc9e5331a8b09daa08151ecf21c955fde35ceccf57061e397e0ce8dc364056862ebb5d0069d619d86693ed2306d837b0063a00dfc04a4d1dff34bf616b93ea08df92618d3a61e538fa9c7d46382a1d4a695eaf51228d812a3765093513b45ea1fa11ea3604cbb6cc196da6432b9641a357b66e618e3f6f97c3e1a56b396949e29f69503293a442e4ca2238403dccfe2e18bea0995cb353333bcb21f80fffd7da10a8acde56d021cc4859fc99fdd4d25cd97dd8eae21cd875d09880b4910af7c171cf874a5e75e09084b2ac977c1e173a5d743e8462f89f9b00957046beeeca0c340e6c2f2f3857fed758ff396bb3a559392e15ed2525c3d1da5a9dc7e0a9d2dec228dd343faa3f2ba3d537c46cbaaf63e035b652dcdb28d9f24127d299ad252b3476058be3a704287b8fddd3ad824e09836c2b49714d8cf76c13269b5a57cf5be7a2656fb52a7529de2b1bde6f556011748da1e6688542ab5558049dab78e2ac364955e29f35aa5f3a9ea71ab2855512ae34a3ac5a74eb9af9cf5d9c06a5ff295e79204a8bf5396739856e9f4474f3948c0d7fd01be384c4a29e5e7f3d9a4263729652a85615a4b8abbb8d04e51a8c4d128954afd7fbc726a446eacd43e8b544a23f22e559338c0f94eb63435530ace149c29f8806677107dee666aeeccd48dfe25edf297341b6c604883e98f6aad72cab490c9d765f97a0219c23959074f9ea3dbcfc32f8f704657da550d8013aa7780950e234061c23979328430fce18fcfd4a0fa78a218d8af58cc96159ff67d6badb5d28b5521387467222c1e3c582c168b050467e7ff5c3c50b55ead9e292e2a43704c3536455aad2cca90afd88fa2b3765e318d28a278611a5f56bbb64db778d552a59cf4b76afbb04928aafa4bd2308c04721430660b60f89086b11dbb5e0ef0c9303f1d5faa7d34073d48e36cdb5a2d6715a7689679acc3ad14b75a8474ac23596e89534eeab34ecfb8d473e1d56527fdf5ca354348b31c86b70879dc6a61f5f369b5a40cad58545bb7d8780d86611083281464ac66a3d369d6671650c8aeb0ac84fe411c4739cb50a896949e292eaff9b406855d39aa1cd491948e93168b97400cd3364cdb72dc733cc7dd861a4c43d6b45c0cf468fa178036ec601a5d9fd5a26d9af7fd386a8c76d450e5e46499bb9ce84009259e88c2891b181663d47070b49012eeeeee57c0acb0c46879d84192735c8b8cc1c2cdf02b5d93583a26b5703046f7835f8005234ca41507a3e5573e03a20623b4483c56ba1f407c4552a426b014f4909aa0225ef951cba9e557be24923246a72d2bde8f9695175e00809471e55da2acf968d9b6f22dde8f965f59d9b4ec1363ad2f23d6d24152dbc87e0267a41227b83ac1d509ae4e707582ab135cf9c94f30c624a3e73844fa13b18e2efea43f70924d2d6e38f85b3a48925ddf18bc45cf8f54b1d2e3b1c33929a5c42157fe46afac625831038508505e8ab48089107a4840704e4e45a07650291d5592283dad95932928d49c73ce15146301f2137066244407c15f705b582cf8094f0191e0acac8c45a146422c0f9f097923ea9544ee8d3c8ee10aebb7bf4635aa5152fe0d9f3b7c49dc3c69c585df851f5ec983511e1f49c520a29e3881abb5b2d0b9b21285155a402985144e1a6e7715495061450262860d70fe6d344c7fe45899d8d21fe0d9db229c4fa79328ad2a059caa39279cd0c79cb007be3edcc03ac0981d600c08204c7f152e7cc0981deb03df883f2f84e9c7b0158ebb01633421ac8cfcd5548591c197d20b73ce39e79c73ce39638c1c03164c27252f98e4cb9b35269fc1e12fcd1a13f7166318767aa669c29944bc3065ad96992fa7a49415e505d787084f4829250e0edcb4ec8381e0e83382624b8c339a2e87c1c171f9cc7431f45899b3a3d1880726564627d404050fbc3a4c7c4a29a5d86703651a886eac651f8c320f664cca478bc8cc31debf30b7b905d450bb7bce1a2131468e2c1e3c6acb3a8bc562b1582c1e735296ec0f159c625ea5544976964871f5d028ec10420fad7cc99f22996207fb8470b89ce7727cc605fe60f6b813576b475332521409a4296843724dac92af8985a4124dad7834b5d35fa7acdd70c0cb344553ce94a6344d2372228283835f96cf6611aceb250ee7e2345704ae68125b2b0b4bc7c2e170278f2b42c5a66692fefa59ba0e5e08636744ad8127415bd0d59afe6c86982a4e7f452a12fde5a0ea91aaa3eaaf698a77e62af5ecf2d5ca67688aa65634b5a2a9cfc634e5332ddfdcd2f50063e7da9991545236d3666727958a71a55d5420a594524a598428887cd658eb4dd337b9350c9b1e3f1faf9a0be8571e08c9762edbb33c1052e67cb38ce3be6f47e857b66e87b59fadf4b4e8afb8ec6879966ff1bc877e0cba4229a5b304a7b8e1b683242d7ea883a4acf3215e49435fd26c0749a10e85680d8528dd1a861a30c7c8cc912311b63462661e8de6ac02123d3e7e064ece39e76ccda003478f08e01516c2982a128944af39e784ac5231ffcf69844aadff1b0ec86197310ce341c352b9562a79b19b9a17145be29fe1842df12932e5affc212756764fece779d3f1f17e3009f39a5593c7e796e04b02b6e64620d65cd549759a939979524a55aa5a57ac28ad15209898d9d95128f7effbeffeba119be81dbc3c5eeeeeee2c11eb73160adad86a651484e97720976f5d0569b1963f3d7c589691af8c42dee6714a470623649ed6b10ea738c5aa98691a9bda1b14a9c73ad80d08231fb64c8a7138ceda1254dda83fbc5e4ff32c8d81f131bec73ae9639ec861fa716855346a118d6ab415382d512702be336ce167e8c2da16aee3b02c782c7481800b0d2c730b215ba369190ebd0e9e0fae72dd79caf5872de5ba6ba08fdd0c18d3cf2aff50e7c367f8b4713d22e9f5e8c027f6d8090c3d16862996bd53dcb9f09f5a86fb48d75cff1e52e35dd332d57d8799542dbb531da08d7ed9a1eae1836de00be1f736fff3777fcc9d5906296c7cf692bdaddaf8f3c5122538f821b320e4ae72ed9538f7dcdddd5dfe8be35e2f9fe127208c7c2452a2608c3c4215f2a1fcf9fdaa96e31bc992df5de9f3812ffd493d7ec118f93f54b9d2004aec9c06e0b15d9f30213934a912871c201e1b5f96aa49fe0f3c7c52d2ff7c868390df9531e0b806c6c8ef2b8314164544065f82607b3ac3fe18e7ce27d2df8400df0d0adb9f49f718234b1941b00996bf07dfc9dfe353c5f2df40d3dff4214b3e7a1461815cc8249a1e74cba14bdf9c6b3334b165b8dd636cade39dfee6d3cc2f72670f12771ab9f331afc4bddcf9d42b7df33bd19d3c5a66fe7cd10d1d251177dfbfb4cc7c9e256ceeb78ccff9bcc2a074cb74777777b7b7a33fd92c39763bfa93abee563afa9352d2f80c24f1c05d6e0d919fe386942fa57cf9523ee6554fcaa79e0c4f8f06c64819a4b0fd1ca5b4be566b77e82f3e2a4694cfe80061e2c71863dcf10163e2ef58cb990860041b1f55abb51cf71f9f238c10f66899f8f1695a264619637c0328b1d0c68de616852a666666f6e2064a82bda251cbb40c44c215af5baa01554c050b7994a072e33f840e1dbecf889c45567a37fc8d1e1c5d7f910c41b01fbf880a2b84d33967fd7cd605c95e73cf1edeec3f9f2c7bef3a93b2b3af9f75fd4c6c563a5b6a64e94e64f04594b2efeeee0e6d4ba10350430b5126b20379af45505878a794d86b5d10edb718b5879bf618a6691fbb8f97657fc363f7d1b28ebe562fdd814b42377a4d8610e4c20d87d07557065f644062fd678ce4c0489428515c3561e4c0bf1f068f80069cb909d409f544ca932a44c018428321519a70c3842541ec4b47e1297262c2102618c16c7cc301fd088c0fa30ff1c2cb176e35baf81dc46089ce0a0bcd245d36a4ec962e9f81d2957d01615a0be2f687b297105b9aa75286037efed6e8fc89641eb992c59a37aced4a29a564f98c74b1b61b8a70a9c070a467a4ca61fae433da14538733e9f299793a4917e6d5cfb6ae3708efa6fd3c61f1b5754e9a279fc95ea71796bdaaa6c1a0c2324cf5c15418a669a690165fa57df3f5d7b7f4dd0fdad864f81150e3d59bb7c437d0d1ae28acbb8cd3094b08ee04a90a42c8d4099d425fb445a5d02ade6ad1700e9803423f996aa89643dbe04e0eaaa22a6803ca50411b09f809b74e49cf94e6aa8dad42454555717a543593a85034458ff4e7de0aaa5a86777cf6f8ec998e831ed172401b4ea750eda8a88a1ee98ffe8c0fc751144d5115add57237373737ade8c34b7f257ae32faf7bf0d2235e838a18301a39c188eb5e11bfa1c4e6a203de1d2f3ce189205bd4d490848030fd44d880d6d09b1d7b5385d5810d50155551ca9d5a56549090a194550ea0654b9005dd59ce6245ac624f695d614bc418679c52c618a58c31ce39e78c5dcecb4fa553cb60224ca41146868c8ad55ab3958bca0b6444ab325696b9582d293d536c4c375c258b3c3b31dce05006aff526c51b1f43c88620a9dbc29698f5b133cf2cd1c96178e5331b54aa9d125495988b84ba1c3a78d0f805e0d72a6f89543b20949af2838140a0ad0126179402ab50ec93655996b158cc76d556885a7304af152c04dabe0bc99de88a382b272d761ec855ddd326bbf3c1aa17c3c6a990abb66278d16a68c1c69eabfa5c8eebc958b70402a94005f8c41e6883050615007aa8e4fe7ca66df4257bf06e58c5a2470dc3b28ee1575fde6a821b9ffdd3d53d96fe6e05b312b208ed102502c059f81373317902154970c26689677104215b7881080e5a6f4b81909a2350aa2304598111448e308408975a12a57472355cc84345183774210f15ae990422a0b0a8028522f50a52caf7cbff99bcb070bba15b2c1186eca0a08a0bbd9041163524febf3ccc60934b3bf1f04391cbffa365986d580709991c1e82546222d726aa76c01a4a2709b7c4463e1bf0f79771e15291465ce09881e438c133852b58c03385174e056f00055d64d1c51298d859024906615b38015cc8f3832eeee7429e1f4cb925fb7c67a98aeedc41746327e446a7fcd42bc58efe4026f71319446c8c0b79a09822041456c86062e9853c5038e9d931badc703473b3cb18239785600c3f44811059732fcb1f5f42c97ceb0d1e8764ae3907bdb588f1815c201973bbdfd06a6d19658d860c108946114689d576ef9594c225953e290ca4c380099754faa43060e2c6291d064c307b0c63e447204b74bb7b4b9cc1cdc6a08cc3f8f0236429e5d75fcb979c778f61cc7c384b40dcf83ea11c02074592ae22b52b6fa9e54a1fa8e0ca2757faa005051229a5940808c2054a8af0c1102c8860c8164e7edff7f2418921820bc24d3158389081611813381108006062c57384275b024705140a1704a7be48d2056b2f64f2051300f0035b2f64f285162d1c012e64f285155a0bc9ceeeeee2ee5177772641bdd4dd5dbadb3954a365e3852e1f4a794466f6cc27aec4aac4ea64f54c2a4889559fd19f0cadb87b3eba4ba2feecbc1d1a462ac1c2d287e5c0e1775a0718235f3e0c9c29318c7ce9f570ee952018d02bc12c5cf9256c65f30163e40fc009fbfd651f4c7e9552d69bb9f40fed41a06f4bd7953f821f7c23a194b279860ef99205a35c39e35fa62be341fc2cd36e13dd7299bb8b71993b17eece856977f67277322ed3e72e77d3cfa676b93b96cb2e527e772eb2bbcfe5eee667a01a4aeccc89311ab931c6085b9ae6fd68a2ff87f69b57778040dee4819d46be2495dc1823ebc6a7e9817de42d2eb75c78e1477b600c8d5b00ca60007e3caa97d128c140dcb4f69460b5da32d78640366ea01e1ed82e4f8f1f802516f4a1d77e7b6863031d9ce1c0280b3433fa0b81340bb9160f2fa81e10b9f17768996e1e82c3366c2ac2cd78a1819b31579a0bc2ab839bb106841bc3082efdb04911d3854d6600e572e7d21e8d2b638b9081839b4d86d3413df8addc951d5c7901844b450a91db03bd2288b77e36a1dc291dcb951d6c72c4eb6698263b17fab0490d86b8f45beeec3ef7889b659afc687003f5e30063e2d32e87fe62931ae4dc8c03d0e4fbc48ff14b5005e5c6520d57c62d551d182e7661124ab810c6c41f8013f6a53f8d651d263ed625517b0624967be030eacdb8fb48e3339e2947e4e1c1941b7f47e441dddcf82368191861105b4744134a27be6c7ea1a1659c75e3935baa2181d8ff62a253d4be95a133338d11f0652eb50df29b7b308d18e51320464a3d87704794d1d11f47af5d0e876187371a1e9141772f557f1f84da371cd1637f5dbd3d463abdc6dbd793641e4262bdcec82fd5fedcfd2574cb4e4a1c8450c6fecbdd0fb8118f1cc1440a29455481b38517435880a321c172830d164e2f6025710330a89015bc6a8de605175808480d1856c851b2049221568843a0e2b5828d0a2fba10a2f5a11221f810266402141e0223a45085138410851148bcd545930f155d5881799121b30a16b860ba9220388f13a9c9e34412975ec8e3c4e996ec574121744a3dea0e7bc94b7cf9eec3da6a2d46770f82796c37edd03321b8c121173e43bbc212c36fece0918196d11e7e8ece4147cb54aff4239a9c89a8748ed87c14aa990180000053150000200c080583e170402c1a525565f80114800c697c4a7a583a9ac843490ec3388aa290410611020c0180008091291aea0460d9467983b2904febfc26e0196c04c2d3bde7a334ee9b17a1e4fad0c985fbdbb80112816a1c8284e5c272ce3ec588f2b1b647c89dba1c176c444d079b79a97b67683c7c65d55da40e883675fa027517956b48b588b0c5523285b770eb13c832cc4e22593fa600061b5dfa219c5d3f0cbb997db857de44ef595df6ac32edf67aad28c3113bf25b89463f8ffe221890fb8ea3cf2c5008fd0cf2d80ead7e786c71ef48248cab6915c6216ab8706af5cc6dbf53dbc6382f3026dd8e3a62a883c459d65b5682c6e708411cf893958a1e82b4dfd72b84a0201ed998e76cf724ad8b154056c8bbbe7ea3706351cae3e31777f11af98a6accd858c9c40e879afa0ec586733b89267a06513f724e5b1d1d39184d54794b009e2d46c1c5f9580edf329a6ae247e8ec25496e2a16a384ac83413e514a0e47419224b39650d9dc5c552e30954ace86418b52f3b68b1d58761acee079276ffc6425be9ce10cbe0d4870306e9d4929fe90ca3def4463d23fbaed1317c59c340d2e858d633f135752aa24e10ca33caf0dba93d9efbdeb35f4ba42847146ac504aad116e9d4ad2780fc48b56b378a1a35661f9dcd09aa632ccddedd554dce4ca8672b09098fc10db86d3a1a10845f03dde5a59a0cb68c3f1681fd128c0d27ceb0939262e54fc4f48c9e53099b909dc04527c8440983afe68d93b847d3cb9417cbb37dd812d69a59590aaf9f1e450bad37a5fd0321b12079339eebca96e19312b21fcb552a606caaa3d59c72e36b6404b44dd8997457118c41e3fa3f01e4f0cd425926f0676170f421884b74db60cb37b08ccdf0cc0d0f25230bbe37d0bffacd668c140745fd905861a66562af4dc8d643f264aedb184dff11ff084c35351625bd5b383d472c3b990b4f01894980171558df57ce9049999fd84d08bfb54f183fb43a5d77e9226e0d5932d59fc2dca3aaf24ee4264bbf486a9b91a69de9a772ba1d63ce554af9a8174b0f558bc508ec38af46d6442cc85de4bc26c93284ad84fd539cd6996c573b373a7100506d4f15a68a742289f0591e83234b228a585f66c72916dde5d8a6636fc430c240c0517e170494a3c1cca873e605fdd423d53b17a0a6fe43959b8d51ae2595d182e5cdb171c08b03681b7d0f6057f49557c5ff028860309a6289b26ff324eabd4c886f42680234c56828a7657af4a078d8cbf64c8f525fd4398ef7286527136cef7e12c5da58faede6a534c48eb9978515090019f61fdd0248969dd82177885085dcdd91a55058309cfddf61a57513b581bfc0e09b8da92a3be153495f4f47151c1f5ebfea5daaeddc173b31150ae4d4f5598056382f77d8d9ac3ff4bd27c62e2b457f8b235371db07a8565615d174406323b62e46d051d107f200f6859c770e545ca73d8067e946531d0c928fb9398da1758e6ca8c5a8f0dea581b3aa3b44701f36e46abc08df8a430437e0912428406586dbaf7fc09f5929a9883f31c6128912a3410987cf9624fc9cb9b9f5002f1bfd71acadf44b4a687fe781df0d0aca6aaf8bf688ddf0f96563c03f97aa7f0270b1ed53d044f1aa6e012897f9f888fc7db6f64eeaaa4d2874b38069ab503c31d5175069004d748c8fa487e0c658a3d81537207e69d56a7a53cfdaece35b43c9f92bcb7e3eab7f60db0a61ab7a03d4399ca259eda45bcaa3d0a10373609843f5fade1f4d590266381070551a36efd141a487cf0589e2be1c70a815b74ed22436f338bcd025b6498f1eb3599e4f194cf5f9fd569c385b7e483b3de783837d060e358bdd58c5673a41fee383ffde288b9a50da285eded9b00383337920aa7bf29908ae6733e1f781bca788c845aff3767de3242cc5dd06a5e6d1cecf5c19843b7352c2c9ee9eeb6d629d88f424ca858025fc72a41a9d0ccd21168b54c383ea1c8e112fc10da9d8a9faea2e0eae41a76efc015aa5cc3fc422ab1da0b1cf7cb30fc362dbb8997a63080258e9c8146a1533e9ba3285fcce0b8c298c1da11eea464bda9dace4b18cb895e00fdec1246bbc7e6669b64a938d85e420c764029bb8de2123070c3bcb5daf8aef89ef8236019751be14f1cc5555873ce60ae9186e051a8b876d3bc4a5f98704e665526b68508d3573d72073dafcae94325930199060390030aad844dec86353fd5b6c03658463e5cc42eb18dbaab39afbb317ba2b2cb833e618c06e402a1622f731eb93d5189b56289974a532801cafde3c25067102e5d96bb01b4cd505453d8c5862baeee4deb438b9a26babb443910e0660325eed7a588e6e2d372a22859e45aea1ec88d28c90af9d8eeeb29e88659489737fa42372d378a18cf538482c03fb7cc0de9eefef62ed2dc60a3020ca3dde611cf320903000d2c890c8c3293d18d39e90ae19fd60ac8a1f9fecf1a64a29f97ef0b1823ae871557f1cdef3a894d025194c038608a185f41df53a240f82b71c8197644b5e3515612f9ebc9a83bf64796934aca3f3aad4b70d38ab9c9c729d87ce1ca163276768d8e7fca9dd094e40114d74a61a2120e9f704198d03daed37e4590318844ae1e71820212b8b9cdc1ead1ba3375470ac2708e8d06908e201cf81c5fab0a12abf96845160b4714d74c4b9f6fb7dca39f9b06400b5b115c1fddd1067de2b5ca15c232b6436b794eaf1453a86cb4a24b00cfcbe238103ad504ec78175e6fbc33c2a68e31b1883b0094905e7a364eff0f2de780ab7f2823e540614039025d7ec44760d745528e0924cbda89730016c751c01a2c68c8e226ffab9124d74e7a236b593e3371c078e41dbf0736b7c8b592259c69ccc5ead354cc5dd21da664546012e8a25d58c6fdf98af91f489cf147d01eb410f3b25452e1ca5693008f251317a6466244179466f1c835335e17746bec73c2b532c41f0862b6a2e5f6cd6538fd82ea08d835603a8cb93ce1bd757cd3c74795e6545dcad2d0dab93c67c709218b02fcd05330e1608838f69485764f20e56feed82c960c3d8ce7da3be4f825622f2b3c53e136e5c717d96ab87847eecf087fc0530d32f411b55a4405b5bb593a4350bb31dce52072fb930f4411fdb3495051c23d731c7b0a4dc83a380e7f89bd7426c6d0c3dbff3668736d2772c07adc07123f153999127bbc9ef61c55e390a14b3f818ccaea2afc7d2dd2cf309fe360fe6611ee91a503a01dfd1346bdf963af21b26928e1dc6bac37a2c13404c860c5a2254bc77892c318db091071a7e0ff182987d12c8a45a2e882ea2c32315011c881d1053d2371ddbeac650a27a93275a814e17a2b748ce36a41e2455be5ce520f2d528e61346a85b711fa1d9efddf2060f8537c7e8bcdb2b82b1a5f00a756bff4dac59197f378ba4c355e28529fbd519dabaff78bca9da0518a1cabcd5453b63cd872d6619b1cec14f8b5c78b6d0ca68f9255319c3651068ddf0e355f6942ff94b58b7cb341f24fc193b52e1e9b3eeb004d536af679e06f4364c7b0e3a2bb71cdb6e62824f05a50575055911445d60759c60b2e772ac43a3ba359018eeedd89a1e813f4115a06ebd7e271250a3a42486cfd5a38f787aee887e2836108cb386ffec969d18b05dd54b313c498366d842baa95ef14fcff7f9a7078b06fb04d046b4b097e2a9cdec85eaf90f18888ea54c190755b368af87dde37511d196e26de56778d8f14478f25edf4c90743885846cdc681a21611882684803ded7d6de906e5a7114ed4f3a99b3c7f77fbbc3156ab8f47435dc811200b09bda1ee4031558f42d86f77e0f74d60679191cbd709f52385e4b737d4def9c8c6daa858efdc93582171b1353ead3c7555237c56bd116afa105d63023ad6e217eb27f1640b24d966d82d4f38594c0dd54ad10ccc44e7511cb8947cbc84dcd74a7de36b88b964f6aab5110730d919f1b2d3ebc7f7d15c14e47414ec691a8140061d106fe85c73014bfe2768b4920ad03d70eea09101c2a1ee66d474185868fd0c556334c9ea8076ee5f089955367aa38d45a4a7ef3969a4e1aa4c14b1831eb58920508752b231daa40a0ce4dacecabbcfbf1812bfffe4966d0ec8489b675b36f20df56198d4f49299676e814d5a63e0a8b6d40284a9c721d587dacc9131d8e69cd8fc04f05eec7784f3589a56ba740f196d6ae47d546c3449e400338a69f97d2b3e1eccdd2fc900aca6d4ab2da52f40410ebfdd10d843effb23bdc6aafe2b24aa02d7de4c910d49469e333a039cac2e4f10baea7951003d3e49368614b004ab626b269175e4923d420d8450f8ff7c4beda89c34e6d613b27a096e899e0a0ccdb31e96e18b30882f4ce572b7de49ee2a7ca467dd80ea4962b284e250cc3ae8d30d1e9ecb81ad203dad7b5d42ac025a6a740d518c0eec89e0b4677a7de84e0aaa0c961f09f01ad3d971cc3d6f12176f02feb1987890230762709043befc0398122e92002e25c042b893b24ae81b38db9fa8fec5b7b499af1e7682142d691254176d859a4cc11c392cb03f347c70969a8952c063e70d5532d7ce648eb9f6143bf44fcef943408f8be3582baa47a49884db00ecd430c5608cf5f49dbd04c78aba6296f95aff815f1a9cd8278ea6db9b30c339c78e24a38887dfa1dfa441bb5551fd1a98a1300006a31c1c6ace295a0b1a23541f985e72b1e17b069f2d12b98ea137018704025fc0fa2b049c083c16801088553322c6f5d5daef10a95ea205b3601d093b8bb36843eb34dbbcaa962ae1435801bf7d2e6f758740cc83a1416f7ebe0efd73dd1050d09cea76c4a32b662328f0df4d3f983ead9a91c64651e6301dab8d5eb4ad0125641ef182493727887c7bbaaa0ad1e61b0fec6ef191093e0d25fcb76b25e463e7231103bce89d4def3b9639e3184a0704f7c8b5041ec52a226cd9e0ca3d6c18ff791e6814bf145d8119e50e2e93de4011c7505eb5ee6c00ae008827ad3f039a79da37e881e6cd36b33a3188ae4621fd2b9370cccbc8585d1a415b096c58f220360ff1eafc3d2967e0fc67ddeb1a41f97b1c5aca11017a46a5bebac4a664e1872cc7e012214c0f639b417116a2032d4d3b1765b5677bfbcd01be2d07f0f35b55d430875181d7fd86642c31a14d641026e6bdc69791b456196622e483b9375d6120c7497b77924a643644d01c361b6f4d6a8ea4cfdf80f470e6aa542e2a8b3b7aa09468cc6b913feba8061f6fc046ce0fff5b49ed1bd76018a57a425ac080a671a94af5d3609d1712b5c5bdacd309ba649577c13b2fce06c5ecc99ab1be85eeb99dadb576cc2f111da3dc50fd0fa3a2698bd5146c7212f28990d48a0d09793c74cf4617c2a38d97c1316cc14e1fc1797e810f3e6469f02100364619ae7ccf9ff572133139cc7d0185ae931777736d7885ba3727f2d20a40ee70a913a146b624641803a09e04d0ab22f6094ee646ea79429dc3a979ddda6e37d3da1e5b3bd924a38b143c4de5b031dcb96f4c20610cf2238de50b0fb193bfe711350e3b7f3bf9db20364f40dd5e9a84fd1d701663a27643b479bfefaf8d8125a81a223bcbbc4a5bb0d28e33a220b933022be68ab607202faaf4a92cb2016030fd9bd7edab42a96b65b69abf1bfb094385deada4e3e9cf1d95b888020fd431c8697a0554d06383400862d6c1dfe131de36b023b100f4276550d0435c6b8a79715c2988e52bb044f610a38bbb523ef45814f6532aafb8c6476142e022d2f624aaca5dd1f894e0803a0b917ca3a0a2f0417a4546cdcd4db3e7e85c4250c8ae19628e477778c3d8a69ac1e364b344e4d9601ed11479be4660b9d7410f63269b098dcdc6ce8bab58b885b53a73f5416abb1a3a939395f5e509374c5aa48c8a8b082b37b010098ed5014145e9ac20a45c8b054a312247885b688bbcbd1e0d719fc5dcfeb0cb70f83604b93679b30020536de60349883b00e603a68c111962b0fe2ea7e5cc304c46917f1d429b56f9fa56963d4ccd32787d5b7501070c30d55a6c45aa86ce00deef09027fcac06dbee815ce3a9e99b1ae1a7694bab888a4e387cb8cf690c5bf9e68107aa463df89140d3c52621d65d287bcd8f165d248bbc7cf88979944e18055006d1c5a6032642ad21bb2082669231f402cefedaa2fa482dde545b9d05c9ab574fe0b91ef175a1debcf91e505750be46b73cb1cf092d76cd4a348d32d862f4b7957c8500fb62f9c0e3788b701e72783e385726fe64ecfef89bec4e9ce6da343adae7494f5b64e3ba7686d3f19405a386aab2f532eba69f2cb64a7d4e4eb2efee7701a2628ca6afdf731b57c34433c4c245ea00e35f94a6e2a45c64d4f19095a28549003c23c93cc9dd07826622220b3861122869fd2d35ce509f173f531b4b2951d0762e835acf3b29b65026cd262a2e6ff7504c36877f8b6328c21e7db205b7be32845b1bdae3b5608662b30f2f460e2f592fa65e2cd1de704a620541e5b6f07752f56c926b1acda80324dbf4a49bc00e586a825b611637f84a02ca54b1050a0850ea1844bbba2a91b5a2fd38f41acc096a0a01f1a2d23431d3672a5f081089d85bda617b88edaf19f1917463f70a17609c411b984695a6a8fa0bb40ec7a4066a53a244a97f7601a67878532529bee72f4303722b93abbd9668c166b74301dfc0f6ac7a34ec9fb0d91189d443ead0a1e70a3cd4a3be90f3e5fee90fa44aa8a428d9b54ded9c3aeeeed32529fc10257a7e3c07d8124406c84c7b4ecd35b17f059c71dd043bc51b52f709ab62cb8be78809b9b9160f288de673a6852c45ede1cb4f8c486e69a03ed8ed78b351da63fb6198cdbd2e4e8d7bd2c16d7273787407f3de3c7b0de6bc79c076727040b0de739ce0ceda12c606fc6acffc3c99150cf1f638c997fd814551cd1bd9ae88e36df04a41f84624d84bd585032e0c1653c12e40826e98914246c35fce0c3c5a39208dd6a3ff2154bf6f50f0b1461045fb9ddb2ffd0e2d6d71cbae17a66b6f9cbf13ef8e4fda225b397ec00fdb1d5a7f9a020b612f909c956bf0af0c00a843fefe71b3b7c991c1c8249ff3be931aaa0e23064dc9aac8581ed2c4283b120c19930ea06af212b0bbd976c8b342f376cba5183c646168f30f3652ecead575f240a9843e50e65e8d9adb255c559dbda1e1f823e2d2de9af96517b45183e2b365caab7ee19a3e3b89c650c0a9f09cd036347e162972238681a199f09c70684f85850ac46811e2957c68eab3cfdcb52ec261457f4c9a0215bb389484df6d4996691109c39e97095f405a446cd904501c9f9bcdb2fcbea1d5d9b33e8c6d370a29fc88da9f5081ba23817f0b5063d0da6ebe85e3b87918de4509489d5cc744014dd46c69cbb2eafc26f38168bb9514ea01b41917e7c159cbbf8821d66a43c8eb2829e81ec129c78eafb29c9e1e26396f5ddc7a1b90519d3398687483d3c8d927fa743ca46021ff8148e49432564be8982559ada0a6a09465d78f38adec0151ada6b940cf559a1c8616239c0ee25e504e01800b867da3cda9ec44b48def3cf690b0f12a4a73e5b30b775ecde047c8c45c7227e623c127747af099827ac0727aef7cafbfa09c4f98522e4cd77308032bda3fa7924e78adb19c84a050a2f5f81b6cbaf7f2110d02de469878bc7e338d7209f517eab3c640484b02ca687de4c26f59e106ae10cee5df03ba63f7902026a85fb0989ebc6afd2603a038b9eef6d4e74ba328f436bdfc863091dd96f31840f6011b66f818b8e557c4b59f4148d2199465aadb2f27c8462b4de7f18425e0236edbdb5d47bf0c9a05db33c3e446082d79cb099643ff25424a281fe68b80c0e5dd92f8b092598ff64dcb11278e8695fd00bc87c0b92191c8af78ed456161f5eded78e3cb7cdd5aaa44c430da9609054e6b440758ee049272b7546eead3cbd3f0395d65f308374091b04b93f33540f618ca38daacdf28c2ddfc4670d67176d401187329472ed1086095813e1877c2b7ad884172ff861362f543a5fdc9a3c9b1bacd0b3e1646d9c7085d3262a23ba5735e7937816b09348e541412d96140b6260963935d208ec44d9cb9797cfff370a8110665adffafe2ca8db374d05807384988edec03ca7fa46dbb35ad224e807e23290a45b66e89d5b591ed85612c930cc20ab2ad91f8b59503a171e1d7a39124d3b416e879eb678c67ed30a3e36bdd361f69773d4e266a97e37bea83515f01382b3c7c706f57f1e55ca86fe0e02b644baeaaad8231f3c01c388962faa70075827b803120d740b8840cad7c912a716bf7bac0f8c24ca910e983350db503ef9513cf54722bf7ddd14a9370a3db626b526745cbd043cf584f202b23c74f429eaa6787c87a5a0a159a00f42d7e860e3262e9c7738c3f50161f71fd9ea217b1ca22179642c4449794d1455cd63d7265625572d9fe9038df1800c508a7f905714745a8873e62d486eb734c483ad87bcb260b31ef910cdf8ea8011514932bc88f557140bd192b90200f73a009f2c61a79c8e38cb2c4f9ebbb3d46177a2eac525c6ad938d2a520155e58b70070fb48ed59c0261dbe0ca20201015098c381d927eac439b89024207b1a51e19608b25949b26053ed935d310b11d498a9b5226ad64ebb25f501ba9112df865d2b1d85d730c7fa29a859420ba89241a190f75078470f97c301f89f3149c758665b1643fd63661261b32af24c881ee3b207d4e622ffdcbe3cd92069aa498c5d03c4ed63e3e08c2e173a4a7237da87006a0944af621682aaecd4aac43586341cf7f32d7620b84675da492ab221bbb4daeaa15c9360ea120f4dd62a56c455cd936f7486c6b5c6d26ddbc28207e9f968db6fc5300ac6aaaa9409f225b8883eee31a5abc8ca56f76ec105be873dea6d63c107dc9d49deac8acef754bd34e1261d638a1969a00bee0b12ae61927c3abfb835b40cc1a0a1ea313d9e5f222714d4dfa590996635e474175982f9a8951e16068e169b631d39f4b8b32834daa313e98b5b8d8c9fd02bdf94f18bffa1fc4bc805e3e497f4fe9948458ec8f6928d301a6420ef2df53526a9953531cac72724d9bbf58309577fa00232cc3a0bb32c68247d90aedf36433ee1dbfbfc6cb07d8bdb13fd73c6a83798822c749b703870913cde7c52215a386ee54c3bc7d8463328fd85e228e806fb9fe444b2e07ed22de1668c2e2ca15e45a3bd292d00e1162906286551f614c4a0aa3fafffe2b8cf6c7c52db5edd91d1f44aa84c90047abf6073f752086321f076af54d1e74254437abcf6ba29a353bf5899cda58217132239603f25e386a8458b8351901f5a235d4089a33f49feaa1624508c70832831bc7a1136c85a7b920d71ca2e83c37a2aff6ee3c2043f5069c55deddbd7a05d663f6931f96bdeab204f2dc7a03b2ec7045a991455a0fe9763165225e9fc9457783d76de1fc8370451124ed1cf17dcc1536e3d024bc8e00a8d28ff4b86bb9e091c7becff6461e852427d70fcae017d04c477befe635d211760b7f3f15c696ce5f3b62f35771e447f4bfff255564e3d188e2067c78d58a9e59ebcea6b88d120809e1632ad65879aac6a32bb4ab82183055df7a705923650f74b9733d7add2e83ab3f49878c95d16d9d399d8d4a1171c3c347441b5f87fbeff8e30fa8f661545ebde4d3bdf26e74977293ca7a40839b70d218ab6b186c807eabccc9adca2b2f32a92cc4f874add26b1590a651653fc11b7b2b2356d9a6dd605890329f9fdb53dd10e8705a898897c111e854959776fa40f5e4404d23317a0c3cc98aa5da7085f0b98e429c8bc71cc27e3d65a19d02b45d1c02a0dd75dd41519b3ee9e7c2786782cc5aca08f7bcad5fdeb3d5358b0937a009a8968bb8f356a3099d617a297345051d2862bdd2e3e50ea49b35c704826e6df66b01340bc7daa063d9419f2678f76aefc49fb5a9acfe726a9033fe4ab60387ca6ccafef057e2424a9369fda8f37bf39ef2f4bc052a210dde98984854ac537aa1af90dcae8901cd5b449b5c512643cb5b5b571abaa0faf1f0b0a0d33221e6848250501cc76f781487c53792a1b5fd842ad1854a20ad23f6d14a3e4350d90ead50549a6f810951436e8e0e2ee768469d6fc755028981a8028515f4e04fd151113c374dc0fc05ee8c0c069d718823d697b217b6c43d3049827073cfaacaa0e4e4ea352417387898b67f119d39d55f112d2a0099d27177dde4a4a82a2f1104f21f876ba5fa4ec3e7134121949f20ccd275e2073ba9716fcba86e730413add6521056ca1d75ab591c2ccdd8544dcf935775f0c543fafb9d743eb9fec265198308947bc086e26322ee1da8a326ef1fdc06e473ea474b99dd0c4fe6acc1f6e0bdd7f6c3a4c995274bdf14042ea6cf183b2868196941b5604df3ce0074683a028bc2f5cb10f82c2ffede9199e19ef0a5a859268c669d382b56957b5a7bcd4166e5a33fd23838d5bdf69e92381917907a086dee012d7b33a438acc5d028e20a9ea7dfac8330f8d583542f1d71becdc435f1e09cc368de6627c9e15de108f62a7a935433f4fd3f48275bd76da9ccd9b038ce0c92c9bedbc5d045e0fb68d1adead55528d5945d67cc055408e6e655cc5eda2a90ffd9bdf8a274b2926783a144b35a11c79b550f7fd764c871bb9758fb952d432ad3cb303475ee23b639d414326d7e52c23191413137211af8f2aba06a6d23624673c588d0b1efab1a57551e25084331467a9b597bcbd3144317da2f494c65be74ed8a421514a14533bcd064373fc98e32176112681e56d3c4339f1ca2d6dff41741369fea9f3295048331589acc73c0d80e1519d5f762c9bcae6636d83ca6b73a11312e9bc39a79e5e13b012fe098d23f158429e6ca075bde090999b17426bfc7dd3877f6caa5749987a51b34c3ff158c4cba94d40c023daa43a85993da192e3462197ba98d031a69d6a4d25e7cc17c2864aae8f8d6b70d932bd17e473a1db8d2ff69d061b9e414bbbd948916d6b99446416095818d777251e9278fc2f2b945c53137353451ae228fbaee994cf9a765788fff8372c4e31051017fc9f2e242627ad151f5a3b7bd6fdd9ef92eed2803e29e07c9a48ad750b665a13105948040af283e02ec2008685a4d6dd1c31fa6b192c0e60d9c94ed0dfda036e8b51210ecd3430fb9ee4c7dafd43bea7d9c9166e2eacd3fa9ad208b3d07443428104e8fde038b6eb73feca40591344d680482c394f13d2866c26869e5ad0d3c35e2678bb0b0d3d179de48e467d1a5f8a447616368f578ec173855f66c0602d809ead27679e92da1bd551d34a8f6e244515aa9ff75283ddfe548a523d1d2d152fd8eb8edc5282985f79d83597e90691c181fa9da520b45d1b60e828d27afb825346e782f7a505ddd8ae1edf9cf01dd6cb00248bb017cf925d5077ed09aa2d3629bd036f8e59f6265a9388f9c7b2922719704aaf7a7c1e0514e9616cc1726131d2b35bb169bf1faebbc2beaeaf00332ef87a6be7df61badd220a6eb57d0b83d2e1055d6b427715fa9fc2bb010e46555ac0621ee40a68812744bb55491f8ab83761ea262ef95c11351c21804f985b9378ce47362165c08252485f0c6760e53a93d7e1d25a8e47cf980a740529c2ea2db5085b47a76480a0a3561975b09cd4d880543956d8b858a8fd94542e8987c2c2910e95cd5934cef28c45afaf77c4c72cc279156c843ba7e04b39867e5d2879fb6e5cde933049bf8e816c59199869ee9095eeae0d16e5bd1cab271f5e36b0ca68ecf8a537562176496431ea679919177d0567d572ce24956d1ecd46494e103098793931a04230c1e7369bd59e6188d8ce885d228f6f34b1ed0cf89a18170f10410ebc23204ba2f85a5a95b88afb43bcf97906a590fd90fbb563433fee7e00091a4739c3ae9f62a582da1f13569367403f1b4ba9c8221f062d82258b16cf17f220f5f7a1285a2efe10e66dc95d63dc1078937fbcf6f3027b8248a8d6b285d3bf8ea73abd7346b304a8448c7ad630b53f74a64b18157d25e11e2f568012d7c971681f0a6f00fd3c0389f58087e435cb306775dea67c76e1c1c15563194a058c9bf3973dd61eec114d6052da2af87dc51dfef0c133e031800feead75736aba37e5c903c56580ede5819d3c209ee3cd9258d603aeff445840edef7d39e1c6ca8dc3c0408369b033e628fb2c98d8da70f0642190c76dd52ea0c6eefcea4f13010ec565bdb9a9e326175da610711f020d0e1dc8c02c51f815349de92dfcc6d6dec147a3093a5276ace1c202532dcf2d4f660e36cd0dd330c02b0f21457870fc21a8ecd1f809dcfa9b5003f80e2d46b3fcefdfaf33e0013b6c645ce3d0923511fc474349143c63948d9d034d0d2770493899c0c8b047efa770da5da65201e8f0a79c774fc8cb16c86e6363b7da1d8c2a71639b6a217ac1839c9c1200e620d0cd098de15310778fd4a464c3d337772b90fd0aa01d3d607489bde3eed4a985309e800d9d667d04e5fa131f0860ec83fbe06fa690c58f47027e50ff93331f8e1ace764cb251367d8749f7e1a44716cc00a814456812a4636055ff5dd314dec09877241e3b18051f3d3df6782239b425a67c794d692d2455bfda9806906d1294d235b780325381181a62a800e8a2e0587dc045c01ea55d7ffa5fc0250cfc98c022d4b683c403d71c831185e6b3dd1b5af5e3ff5d39ffaa409b741554791a29fbe9c8fd177b1563fbd6dc054e250ffeeec40dd4a9cc6030518fe233298de21830be25c3a06f500f55ccda2bb8d6d6a02a81f2b656f7260302f1a855e7ef9497f784b93960415b6bfb5a4fef2cb621777fdc98667d11aaf8501019b0f286ac2445f68d269d1194568b04739803b95748ccd12a419fe8f758619c37245191c7dbe3fda95898e95db0f48ba9b2ab5610c06fc93ca9f3113f9a7bba82b66135fa68583dd4612c2b9f5b940846026f960708bce2ffdd8379b9f8807166190dbb7660b190dcc40f48c88d692232a34587c7b3628d96d11b76a28206a5e5bb83bf92e78132e6c0638e75634037c68fbb6efd401c1e9640f59fa3b0b2a20dd9b5bd49d7a49183301e5398b5054097fb77e76bccd90500194e115fdab6149d042ff9e39f226a07f915dcb102ad71302708b9d7c93d004278ae7710dc8edb87553449417f91177fc87e2ddbc8dc0c57b621ac8ca090346dc1b82b55b2651f8f9c58403b8ac565cfddb251bbec03ca29219656a4fd71cf69cd3566b1849132f3ef3bee694e35afe1954865d7668d32d6156cff02f1b27bd66f6808e62c047f46644967163146b2f1f0bcf37d4150588cdccdeb1812ff84616c66fd8ad959205c5355588aa9ca9f858260dc97486ba49c5d3a7f037b26fcbeacaea0da50e3df6ec22343927a59b33244f742674216b6d07b3a566e89f464e0bdb09969a61d57d45d4360c19a3584d1af9966e9ec998795869fa3d8b3649c6eef73a993a427c3e73d6781408eb9b0350f8ae5f7b128b65aba9f919ce663febee32662c54ac456ad39f33f490253b7d51fc07fb12db9175845672e28b56000019a2d5073f788a81199d2a14568209bbf7cae0acd453424d8e9ccb86d6c48c7d9367da22ab919af568d9652b86f6364aa5b17afdd25e89f8148b4b0317a43d9bac8eac7ac692d92c106414edb54e2849a94ee84699847447501e9bc85f1929f713bed11b631a371ab617856eaaf956bfb7849e813a387356ab04c8337d9e308cc1028c61f247ca4978e765a4d45f1a9d5bbd1ef4732078896181eef3aa228e75158a16458f94fa01e898ca2acf863fea4b04d72dc1bf7307dd44a45f15448e88dd4d345de3faa407a4aa345ad4f03029a100fc57449e79563f251f31ae8d6eea9e8288079355cab8be1636ac18100a0a481f131a7a6a3b604e7e1d5f9771a79fc74d51307b9fa54917b73d5ab51b3f341fdbde7fde3c84ddb91a01d984770e2535b088035726ff9f343b9bee032611b71fe37aa49f93d72d8aacfb2033ae4f7ade07e123fbd5936bcd7a334242cf367ea0856db0da45cb10cc9f45a58921365f9984a8f40dba3eab6bfc6088c4fac9633956103db49b4460a41a74f4931dd49e11427a748dc19124008d1fa8e5c11942a8fae3d8c60f9e4f49cee087da91ac0493866fd9dcb831512d67341228e2c3c05c3f4b7474bbc5b8a23868e093842b0957dd9bc76f725ff3c404f62192c836d2ba399114b03066c2c4f1836555a026aa036ab52668878d4c098970725bf9cd6b426be688f8413a4179040e8acd55adc6f7315b68620bfd5d71517f708a1b008813371fc071fe8a602a2a4fe742052f408aee6040fc447e1d88782560b3e519e61c654ff3134f421b2d4d010eb3ba80bd3dd09b738441c0c8058e8334f821934c493ea082e93465191d4a3364441866d3caccf7e18e8d646f1a74bbfdca0d73bcf3bdea4055f348741eb99f0e2034e256a17341c532cc9f8e57118e9c699c8cb7986f2e87c643aa77476a1dc42ee11ccc4161974e92abf8ce43a0b93712db546c8abc6905cbda8a5cb3e457e554dec432862da0b93de90147cbb081d16cae4587dee40fed602107cea1fbae76ec068ac5392a639cb483a4741f34a1f65386d48f21f3d5287e6bdd614c02a336c779209104919915ecb905cf97932baf98d70581ac956a56dd12cea85176235d398b3766cabe42528d04e12c46a58fb6523a4c9154873b79269264e8e4d0ab1609d0129a793fd5bc0c77963ddc7bf452dee269b4c7cf1885cc344c83810e4e27e4175212f440e34aedf45432d2d7798d6d18b653dd2418182c927be309ac794a0133d0476530379531421ebc846cbac95a8bd4422ccc0f903cd71331e9e09b1e1c0889a759c6e39ee57940e66f855acc3b1a3bb9520045bb166bb235756035e2f978865daf5e97ce98838d3b9a4d9e266eefd1c3c92705a2f71d17f51a9ae210f4d4297fae7914ec2b02cd7b8b77daac58f612d8745fc33a4ea88525e282c7972018bbf846ddc0e85a76ccf2ca35395010cfeee605aeffdf5ae6ce013aa29fddca07e5a956ba36eed58fa09291f8e824b932c6bd63460ebc6d3dfbb015f3468707bfdf93592d486ed8410b67259e4411ffe64122441cc2453c587ecdbbb2a45ec938d94373f4944cbb10e2f83682ceab71587fbfc9e028825eb254fa9c4eb91359e2eb440144d095475038dbc5179d788a773b9090f88f73cae85a72d4b95e251f13d001d6beefaebe1afa8030902ae32164d076382b61da7b45bd58b1b559281105ee8fee296af109d03e0fb2af88b2883cb32c65803c61fe421fc3953c663db0004f06db246502ebb20787325e96fefa153573a10d71e418b291ed60ed2da2e210f1e0926ad31613db1327b29d119d800b8b3e0e1b167d893b4ad0d1b61576e94d1dfd82a76c9313dfe6a5754db4150c96786b6f68a245d03e2f2ff010805eb34069c33fa440a31255f1349036950491f2cc9405f66545a61e550c53589fc95d821bc7ec0ef18bc0b19e2a89937dab7fd485d9d5e2459bba856f55adc1a95d2e2501f64dacd735886c9f49b3d162a9e54a69edc9a830c90978a7bcaa4fc4bd64acdf2ec27bf609e477a436c3860e8864dcde6b4083561a34c4c3a1c15e84150e47296c759a98c22c9bb3afbd4959e8a0971cda3a292b0978e27708f4fabf39474f1f23ff8334db61b75615e7d064f5330bf116af75aeb36400ad9c90258f35b19bd3886b758995bbd13bda2adca09a2cb49c0c94039bb78c6c9e80098dcfcb51cfb9637abfaf8264ab1e6084c478b078516a1cd206d4624d635dd7681011dffdd56ad744c681524b4fca122f67e3c6a835b96c45b68ed2b1829dea19f06516c53a656eab42fefb519e678c3914ae1d2a182dfb64a6d68849885e7a868c1767fccab746ac59984944df2a2bf150a11813faab51449353c40aab8dfbbb60fd9c2b25f0151f30e8126c457af624231237e6eadef4899be0c83d607344565071ae07f10ad71fb7522a913010b5326e8c63a916ee56f33615752e28e0d838b5348a7f783be6fe5d0aae1934f5ee9d350c20ccfe01186218ad32c79fdb104816c3bc316b6207747dc0509c7a125f79e3ab0340a8f39fab610eb4b7dc8eb1e5c8e590c05b45977d60be6e16e6b02e44b08a08caadadbee1ad5f5bfb756b8e414a3b226c1443c4a5182e81cf70be106db15fdd7cc31d382aa6373159500b1c6725634016fe64aedd105f28d0fcce808f2d85d3cb2e55a057adc8a2f472617aabfb2ed0cb04c4431c6a5d6eb5d1aeec0614225c98d2fcefe2a6f9570cde76038d9c603aad60caf84fbc8d09e1d8cb865b386acd5f782832389cf0c3a58049862a6ad064397425affac3df2dfd563d49a20e62632b9cbbbdd03265870a308e1eb18c3ba8492e96ea7918352191dad6756034232496fc7b983a51d3d66cc194c5670eca390650db172792a93fd38358d805218fc25331baec9cefccd05dcf0497e73bb35fc88c5946f2681de1147901b68ddbb9ea47002e91dd07a4519ec80b4dc9ef4d1a5108c7167367d16ca4e85f5ad60ce91190f2f1151c83a594b5a05cc61ff6654e3bddc64b61ba04f4883096085a879b26fb029017675c85c5fd73e193976c2f767fd8bec2666843ed8285f7f2fe1878f70fcc58c679d49629ba64046d2e8276b79cc604ddd66acb5b93787a3bc7deb86eeb753d208e46cc477c8e1f3ce9122926c1e512c8a36f2f6360014219296c4d4abf14856990aa6445ca46cdd64a167647657af9c1ca16d7d07fb8697f91f601bd58c66e2a89d188d10937b3995b30a405a29a9d3e26723fd2008241cc977d2f623025207fa72bc1cadcb6ada1c27494677a4501c1806e42f5ee1a4406f262aeff523200e9545b57832a4528c3f1d6ce71b1ea938e3312eca0b8a7e081ed627e51151ef63fb15b3f53b23cb4084e195c208a6d3dc574c09676539c88bbb1a260d8878c8e27cb07d6720dc00f278d28573b06c0c87a2c7091b20e846404416b7b5754fc9040d6d2ecc5f86a2342d0d0e9d2c41350dae9ae1279835c0a144f44cba8131ee08b781e93b7853a39c1b37bf1ef51ffee02f19a21dac073c13380cbce2cfe66c6d674af93f09ccf9380c467dcd51802b81b1a216e4900326ffe626315e033f84f53c1dba5a9ee7c04cf520f9c1d2b16585ff6dddfdf20eaed901446c9beaea917abca42667e466f0ed856c2e34bd29e29bb46fda244d2fca896b93854be58932e511581549bcfbe21df3f477b6c4ef0519d826a19193c2fb5b19b77788f6ec429ef255417ba1914847d9caab1be74d3d641c8b4a5395772aff88dd03baa7a5166c1ee50597ac9a26a82303d91fe36084241f189db20cc8476887118a4366f0b048a8aba5d90b4fbcc2c4b2b187d9cbbe2e0df5f38d7c6b8714541d629f054cb16b3a14e6de8583fe0f1bc3ad61e669c1ca70612c1fd22a9644c3bee1d8520cc75b92637f248438ecb50c175dd2ff2c9eda23d3d5fcb8790bd16521aea907e6c5a3d76f152585da2d0c175f7fa34bf0bd9a73b3277fb6fd852dae2c91468d494799275d8f950d2913ff5c387b3937d2bb31e4efd357cf8793fa9c7aa36b83d58381935ff83d84089fef8e9cd1e0cb1f129576b22e35b8c61f10bb5b08fd57066d05e654d1a6e97a8e17a2aaa1bea3a175540d904060a5ad7f5561c23a061fdd1ecf6e864a7e170ca91fc2f23a7a8213969deb8457193be875e399bd2196ad57ca158172d1714f2de6857280e5e2ea8ab3a46cab7300817ff0803602a5fce825ce88b2f7c51a64a8b0e1c87d504c741f8f599d51d474baed1bfc994501504a2548655dd652aceb38858bdffff7499d53cc08d3baad9061010cb8375fb52d411d9e9f36f26bfaca262a5bc99e8a2cf59c6e49931b028afb33d9af0c6983f69b176d92f0f7fe650ea7713177828808caa4ced5ad7f0f023c893e5251ec56005b6bc720380ac92fb795a1da4e267e66129dc9a6ad8ef2a856f6ff3d3591fc0b60efa6963992dae56edf98934211af1d28f10914841f7fce410655a971b2e014cb032a1ef7f9122c596c80d3d8ad41f727484fcafb2c37ad0c8d0eb41cbe25255b735b031b801a2a70949f507630debf3b2688b5a5207fdbc9296130966d79a1a8642fac4aebf62521b108b0c2fbe8288d4de7cca371003ac5e7772a63a46321fb51606bafdd985ca10eea50cc2434f943fd3223ccd3fe24c915d498258d354961c40f3d85c6c5a7d1c6a826d4d3e7c6e140d730a573fc43ff662bd68c6a24a35756db863b73716ac0b5a519d66e84c0ff0691132d6e6d8adf84c5006f23ab9bbf9e1f433fc44f7139a0e7c57dbe014d364b7f6995fbf7a1a377f7318f46ccc64bcc5939e306e81a9ae4bf1a73d70b7ec231e8983b41471928940f9fec354d548d027473678d64fd51dece837dd49a6a62685169d85282381e4ff41fa425bdc5cb88783971de8f24b0dcc6fbc7b0cf5ae3e8d112622db115d96bc9b314be339c7a9011c662b8ed8400957cb59165598505b8e3d4b55fd3bb1976a10d69f2f004fda5932ca6cfef43004a86299892f752a0c40484729f3ffdaf8b84cae8550ec956a3149de8eb3f674d6f1f472639b328e75144cddfe886a3a96fd5a7ab1f1189831c88ec39540a68955a46cb99ec9823c9b2b141b3589767c39029ffc4823cfd6ffd500ccb4b4721a9cb06727aea65b34f345815b784939fe23b2ccc6c36e2cb17cc111294b68318eb8650376b9363159157e3d73e2a46de325d0d13b985fea4e5674ec02efdaf7428dee194a9a703a8ed163358aaeb17d9788c21622c93144d6667d4388a5a6ff288c9cc483214dd802c4a315ceb73a27a690d81afd7db60fe70aefa8f96653ae1ffe0cc73031e6327a07bd8e09e473abc09dc388b70222e7af4e06cf440aa8cfb262ff90c745a271beea641b2cd7a57f1b543c4ac766d308dc0b3e2d174e47ff42db46fcacc2499a8d21806e77a9cfb7b90345f241a54c2f898cf1d3a188f55665d3e089d8c0fa1f3b962ee994cd241bdf235ac7daa614f2dc6cb428f17c97702610e2af59ded60bc089278b4720b3bd8d4847e553fa2de9b124469d0eaa88e49e923f9f4d8262475977ecb2054dd35eedb552060aebe19ee4be12a809cd55ce280e5f43dc8533e2fb0dcd16d7a54ef7780d58840bc220dd737f27e6a213627edf4b499e824ee85d0bc52ef56b3cd309e14e5384d5b49da8e652ebd33784d9722c5097a0b653fde25120e99ca815a873e2e4b41d92905deb2d6a6211bf6b629f0dd390dcf7f91ca2e43f2f238a98e509a799ac1e2e0f55d5d4baac134ac1db24a22e714ab123695124cf9806dc292a3d9f0b288e2bbf6e8940d0b064ad9a1c0127a072392603a2d3f054f7c1e1208a4c582dc3fcd456638764258772c8b3462f503b72609ac42aa57617e3c7d895f8ab7645aad245d94953b25f2095cb15d19ac79b551b2c0a978a2a4bf7074333812b805f5403a811af77263550d972329d35ab9faed86e4843507256d0a8c49b73a0dc41dccfa5a3f6c3274da498f9ad00a5b143f5ac0810a581a9f2286e47ec270e7d95fcfb3b2e4d9ef83a98c872e90df2dcce17b8c5f71da7e6ed3a2983d497593181bb4ae1c3fbd837246cd2e84ac10ad7e5e369b0c11832c9829f9d1f2b21794cfecf594455a991e18586f9f812ce0486187954aa5838d3ce184ce20ce3c5b1cb9455eb4d3ce02927ed9e2dfde6c87c05956866c8d8662c945350ba49f6bb60042a2737e9fa02ba6e2944f575b69be2a22e6b513ce7cdfa83832b5e1a7ca6dc7378a02c1ec9cad8bdb7378c07facaeb1949fde3bfc7f2ec1266d689a9c8d85caacc07803b59e8ca26a16a24b6d0316914d7df6c47357c9849ad85b273349edaef7eeb213694ff4fc7699aa44c0e2fe92878de6091edc231c38ab63b2ff0862e878645a94204995d67613f3314a7e46c39eac9bef846ac851f6015fe2ad39afa0da0c133a300b90978f24cf3ae8f8366f229a8b1dbe088685bb4d402520e2efc6d683c0fee9f0eebbc55e3ca65665b862b621457e45b7e87745c91940a0fc78c1d4605ee17dcb0888e1e17505b3b0f522cf2927e16ec1b291a707764b44d99c7b7761b0d75e222369c84e15f26a31eb27a94125364147ced76feb598c363f50cc92ebecd2d770e732da33dec36315d3734748c6848f4db5ff3edfe7ab869298209197930179e48f0fde8c7b7317b4894ac8eeea3e98885b0a4091c15093c4842471833a6d65fcdf3a1d823305b48b6ae6c30558d86e36c4d547f81027ea45a3ebc4fb0517fd1471756fb56f716b96d811dd8166da3b64cc52b81ddf467e7e17136043f4a895d5248bd0a3d8d7213a143846905d276477611b0d4813340514b3bde63ab5f0199af4df84c16744b7de46cdd1575ab4cfcca92d95763834270c98a25a46381f1aa5f1fcb4ec2b798d1bd1e9d2284a80a427894ed67c59d988ac013300f80c4ebbbda1974b880ef09a383bb9bf46bca6e844f73a17bab1500244f48df1fb745d6bcbb240932ae7577ee4125ab9d0aa1096ff42e98538d12b94739586793c2adee9788c4ff88a0e0865074c13d50b4f32b8c98cc4fdec480d31106c1e900e2d79fb1692877b48dcdef850885d4c2ffc510b7779c73721f1c3b6b01c6a698304a468bb9905d1e127bb4118895bc6bfede14535243eee4f1e09b86fd9e9d2f7ee17dde9525f6b61716e88162312f40f1028bb6282c826480aa0af810c24094070ed62f5199f91082d3e0760d77113d2b7ee5d7fea62b22958e4e5c8858d3156ed397b8e4a326d20f3db61bf9c73f4d7e29aa741322ee16df10dfc355309ad0e812c3ae4d0ba489dc8da1cd4ec861105fdc7ab91e99745c01126944b6395158649d602db5df585b2f2b3b33718b42f4a926400a0b60da1685e72824cca089dd9f17852cb884b7f4589887d02afb0dfbb8e7e75f82f11bcafb2f3c2f610583f8717b485971c90cff2bef34bb7e216b26fde0701ea2a69c21bb0f102abd20421a1eb8d6039e46cedc9258c3e90409ab40edf8cdb987ec2257a26f4c99f31d298e691eb47f41c9e3512f7e4de772da6e77b90f0bf28e0dc7485e676bb846333f71baefe7524d20976db311d80ef54f11500b45a5d09973da35ee0c7d9ebd960fdc7ebd64c3015c14eba28a09d6798a9cc62464aa1b7037f4956f27e5b38c5782ed0e75d839c82d05dc5b4b9e7383e661d3932318a08be41b84a657b3943b926dab5c8f3ad87407399f534a06452f1e83c11038573cb0540737e22c4ca090e29945457ab4ebbd23e8bd2b2cb15941c6c10208413713119bdf624c0152f519875892c32e481c68fec37c0804c46f82aada58d9531a3462ae971b9570be6b6b4c030453472e7d2e6a68f744653f820936748982835a74c19f9e2683047f28bd07e6ddc1582650c6b7ec4e1ce249aa530bf6504ac793e849cf93547b28a6133495c78a27f6e4769dc8204faed5485bb9c3a7577ebfa2be6301d506b14c42f3ebc253b6a025f0cad15f31369ab4ee36c0750bc4af811793b240bc94986a8e16ff83c62809a9bfe6b829f702349d95c8c8973787f95772ba51c464457dd6430e31a4dec9198d9f0aac8cc24b38114c3af86736a9d830b9524cf0fb45a402266f9ce405394cb47453e1861131ccc3f4f44ad4ca1cb27f7249f67286d5b361ed23c27dc96e354e94142e7af52140b1dab6f8ac3c9c28e981293702f24729299f8912d860fcfb87998802d7b22546a22b4f0de11b12b3e5f48e1b72302aef06931af187e8f4d43f51ea8d825a9ac3dcd79104eab58636dc2d24efd16620f45fb1a85eb94ffa94c6853b1647f111f90eb9e17a824a61207db7e0117f5de945a5f594dbef322a4f84b5a6fb13a0725ecc248a39040d4230bfe2d8582f65891d5a121be5f5206b1312b71b5705e8a71cfa3b036a3f67401a5de1ac388128f2ade146a853d28033cdab43bdc56d6bd80d65bb3adc222c872f991f97ea70b008fee43b9bedd0324c871ce370f27359af6d15878961877c27a8555b10ccab6187741c49d30a0ef1218b0d2bf90aab71fddc85c160302b7658ad16ceac86ea4adde756fa2210e9dd1a1ec70ea3505ec8dc77e6a89381751ea1f06285ca29649d346d9514975eb3cf68baf8a1f451544b123f99073b439011dd217875045ecf6e495b3d4c3ced355982765b8902cf8fa945c67cf8ba09c82688d476cc0b44e5a871c005cc6905dbc0aac9877e85050100ddc0de6c92ffb452d4086c6cbf06bc34a5e0b54d79901cc3a4cdc05e8a712600716f0fc03de244eb08887514e8a7034cd52182cce839a766d8eb4f6fb6f567c8f54b569b3957ceb594304229107f055979bd314a80bf1de0d9f519397d0e3a53a44149d5bf94c01362c4b8c487c3d70599f786717b1d8c38aa3d17a18e053227f1d82c6dd285f4eb610249aca841a2e27c9ae7004fa9977bfeda234e4191c17a76e2a8dabe93e8137de6b15a7b60057cbb32287ff5584be0c9a76a5b5b17aef573a88074cc933d06c2fd64cbef4968e2f31758dd14dc4e80d8bcb38d0376bfef41f8628cdfd6719268a4c655b55deaf417f1e54cf0a1d816d7591249dbe60172f79f8275a1a7719d69f16b4e3252747d3e00873ad5fdf422b41dbcf50464b1e13f07bf9245673017a2a25cd543de7d83d7796e1f9ea0b76eb87a6c0936ae3fe160c29771e23b47678382d87f22e786f74683348e04cdb6293c224bee4a5f9d1bb4e31f41e9ad33b71d6f8870e1cb112e340fc80123070c3c127edb89d630f3722a7ee277bad4b7c29de6bdb11a434cfa7b6e3e058b0bb357dfecd6db174d71701052935a8a8352acced9fd5e8ef5951046f182dd9dc69018adfa7b020a413cd529cf106b58d98ec318521ce197fb2b7239320ccb288e0e45513fc8ab83c6112e0edf7165aafce28b4fa701732e32ff359764b23fbf688e731d2f1ad29942da75a4260120f96b2fe6048a3a1972b74e818656e6d9acefec258f279cf0a82c57f5514ff784f790ab074b83d960701731c1628618552cf1fb7d2a756dd4fe00e9400ddef9a75820c7f1be8587830da18fbfbf50bca17d251a30374193953c0a98a4162979e995bbc3e13b6fa297f3dff977a5766e84a08fb04b4a1c0358dbc8e13353a191204f1e06d0040f8cd9d07cd61d58cd96255a723874e074e9691ac9e70c036be578940baf1714b00f37466ee73e8743df1cb9e014eac3391c3507a17fe82147d7929b2d97a6dc4d03717b5160ed0ffa9182d492ad9c45ff1b608cc4c080b4bb918661da9b05080a7422b5b493e23bcd158c61831756980b20a936b7f4879d80c6bb48d8c82b4e076edc5ee197612f750b3db9cf4d3cfa6c96de127a0cd1c88167f182a1d2e9fc29eb0ba13416c4785332b250778555e7eb9364fc6e383b5298d5121d3a5e167f68d38e3dc98060f5a176d1169034c1c61f44e8ec782a24bf0626c1175451768d89d9a4402e5c83b1bbb44f9eec017ba9d99a60f10a9da0f4f905462da7dea16cf36da53b0082bc56c173bf2baa55c43f85fa9c2e9ce72865ab6968d5244058c5b69fa17e0127218b642017a61a2049d27093799d09653d69541e824ac485d626d4788df1837de1cca46f38058f5c95ea6044e894acc58cf8bdfbdc0c88653fa426a80610181831da732575ee12d1eaba4de1d244b90bd5c00ace45007571fae7209c589b6c360cc2eb0e308d157a263fb0e8a15aa8f84fc7903b74768fadfa6e1044cd512806c83c41616985e859a2369c40a8168e83f6be7d4020480e699ad6422d8ff1b8f456fc12e3fb34635d62d14470eadca1a7b086a7b907bb3bc9dc0232ac3e6ba5e30e8d7060698f6f163e521239be0408d7f247305695200b99c0cbb12dd0d7ff77a78046a55065fc27607c018fbbb33f585688c012de733622cb5364478e29ba3b3e3094fce05a2c1458a3e281d65cb3ae055c408b404f261e751e4d994823d02d3639388e0afdeffe10f0de77f00653732ff6b2c1babfb2e164a088d161e8d78e757f74d7385fee1a96a15891e36494b73e21c0fb4317ab1c348e1558adefe3c3fcb3ac5148941f6e8a20c4fe5b105e7e298dec87eaf5df29166cf50bd2df0a2011d62ddca0405faed3e79055ee576f0cd2166e3a6b5c3f374717092277c10aa77e4a0a5f94f96e74cd270bdbf0111a2f0259e34308948862c1ee3a9f4518297aed3c1a3bb2e9d60582c928982bcb2060c0414dc1a99ba0a399b04339c5c8c5c8bc6123ac4a89a917bb44b94b56fc16a2da2085dffeeee694abab49d63c61fb33149c58a855d2e7ed9a5b37c41a177e6d158b64698a7cd1daf5fd165855e2bcab641a5b1dd03baa42f19546eb5838083106fd76762fa4125b1662e14a2d571b3232469b2996761bfa84e306adef592188f088134c5d4d8a429d69a068aad1204334df458bbf4a8b71d13d3496d000f2ced5c2c039bf29e2c575e42a998a8b13c0a61a8bc5957f486ea7ab380f9af19fe01ed769418a95e0833852a7358ca908d08c1c6c11f926796da0600cab55286f0e20b099ffbcc167056b3907216b7cf062d9b2441da372024a0e272a6572e25a2ec1e9068a7819362340d30736d756b68679f0dd2cf4c2d935fcd0e2d68651397f68340a62bb5926a9dcd93af953f1b3daaa14546a9f02310097b42e0805027b68d1f2e8a6385c025910d4785d3369ac271bd608d28c2c00e0e61a87de56019cf0ff2c80f2b4b05406f99c65d2afe0752fb7fc460532e0681e506802198fc7801bbf823222995d6708b71f7d52884e4e7eb92c069f11a740c500b03ff809f1e7aad8be2a64ba74d86d8296423d639698e447e396fcf5f24f07e8c3810918c660d4a298735a58f4a04fa4bd6664295594b275cd905c2e29b4fb560f8e60c13db37500ec789c073696aa606506bda6c696af75c6fd907c522ace95059b3d0c7c996e8ede17bab0684a02cbf8d57f93c57fe553e6f45e619743d0fca17172ec54b89d4a95fc900a29f37f5dc7c6cd53f84afdda0cfe7557dbdef9e29242975d374b1a4d6321c07dc1d23bb76bdd042afcdda0689117bab106df4a2be0afd0ef6b6cddc1c0fd8805df6e265ee1245a716677bb6b8060ff9971d9cc1456dbc794d64e67b5e23ef20114a6e101b004afbd8c245493ef3230fbe34971b261279a0c867b4b58d0e20850a651e93db758ac880490e424780718341742db64e433d6e12de79a67b57f032bfa1a53ecff1b7f50acbb18a045b9b72a2526dd68c5a43700defc0495b554c8b785a59903ea2c0ae9aca8bcd1a815ee2779090e19a600be9a225c37a44232640511eec98a0bd5e34033d7fa8e3849c61a37bfd71df046bd579ad93b06ebb8ff5b6760d00ffc5d9269560de3a01ae08814e23f02f9aa05d7ea0d230047a5cbf9924912e00371903249ae8e46a046cf6884f5c747ddc2b8a97850a305c294c2c28524bcfac2ab9d594a103e782f1ea42670cda30d0d910f151e997d220f892474cb51cb357a1e3d5f94318a8849f0d04465ce891a18d88bed119e1ca1f49e6d04b19d72ab6329ea11e88da85ab4619ff6dc121922c5c051dbeccb45e01d9b60e350182508e3be1fcafae77236708d26e7989027075414ea41bb856fb161ac0b08f678593bea548411100b73fb8b34b0700d6672f3d690f8b43ec33c9a303ead9fb2cb8da22c66f3416b7c56da0493f50ad1f938499a7a3c2c69ca0b149ed422763a21b90e59c9b2832de220cbfe5b32b6929a3120a78c62d5eccdb28442b3290ff4ca01ee2cabceb41503437caeadec9f7d4e2e72321f1cb67b850985776f7b7b3439e3516124c0c0ccab4f7034171bee1f2669215c8850a478040cf810b48c94ef031564bbf6aba85fd5369c017cdb03cac97a8e5d3052b29a0e696555f0305b32b4f52691980897b46141d51a45760e3e10cae6154bd9b5a2cac821475f2cfb57ae1b80aabbfaa13f7cec79c000e400122a3943edcf04b210c7fae1fae50fe571ba7595b76e24a4edbdf7967b6f995292018c084f099208394db3fd8fa639fdf63860be69b6edc1704689da7fe228a5de77dfe964fabe5308da919ac06a0abbafdd473dda7174eb81438df551a697a993298469917b54f82d7229eeb913c7a1b8130ac5711cc7711cc771dc09c571a71c0ef51cc771dc736fa369b4e7fe14ce2c51274a8542a950a84fa150a9140c678930900604154de69cb3893addddbd2710e6cf4ccace3219b20da5275b641873b67d3327a82307c5e48dc562b18cd2582cc6414149b1296cf988a3b43863da1f713f39cb4751184637af47b01929339f8428b5fbb13279cd67a2a62962a32ec4f1c0afa9e65b5359250705057d4ee96c710631b9a143a5b32fa78d728ad2fc2ca8c5f93332d4ecc5d9560a91322aa8bd94450a26d4cbdcb58d8e54478b994d9665df94716c32cf085f36808cc9fe892a45674f0214a25432c29322e0a0ce16d99b9c1449bce73c0ddcc139e7d1908570488b243c2119e42c6b7b6eb39b7da145ee678bdc7b34e4c1da9e089f304665f9e0d79e5ffbee69d8214e8bf3b9108516e777dbb659f9fdfcd0a6c5e9e1f65e486dbca6d6ba5a6d36f365082ba89c4dd370cfdc093ba8dcfb3373ec04e42fb2badb0e7ed0d6fbb538c9c0e2a11d4149e5bed69cde64e335272b647a2cccb8ea68c36fa30307050e75b43861733e51b3d186d3c29bc1a1b6f7b469668b9e15a18bfab5d83d4ecf2451bb5f358de73df5dedf39f6b8309bd4f33a4abd90b323edc291c7e776dcd785da7f9c46e736b56c4e275f380f79e5b30860d47ee6e1fddce375e6593777cf649ee2b95059dd8cb3af63401260f37dce6c06893a3fcb6466cee69c261ce6dca4c652667ad291942b918ea0cc5c8974c481fbc32dcd6971ca186b3522ebfc6071cef92e7a0d9d1e23654ae055db49bf1da29499fe3d7f0879c1edd7223da2faffe0d1ff9a863bd597efd57bc7d3a35e526a454b59a6fa3f9a66b4e1b81abce8e3f4885dfa2ae40849baf48bbc304a2b54ee171b0ae38e928c4bdf5439978ea6d96eb874ee1061897a5da1405b72280e986f794abf86334475b6485f7e733f9344e5acf4baaefbcff4a83006d4a7382e857a930965b9f0840a6b7d1ce7c213aa86dfa34e9fa903bbcfebfa8431aa9f60a825c2401b0d9e14e18bca3a40a5d2fd6b6f2f2f33114db32c1f623f4c28f6f713d1acd8a2bbd83430aeb9e6da08d3a2cf145c7befff6e5bcf6cf096aebf37e78c4999d9465ee32da09ede4f072af298f913c99ddbec3066eccea43bada549455e43953cc65fe993c655ece88e1a75ed9574fd1dc8bd025d9f5a542c2d6e626c4b49d719f037b35a10e77a5df76063b8cfd7809a6649d3f052a685d434ed5d93493276b10f19622c699a268fb4af41fd89649aa5a1154dd37ec3f1188d66210de5bb94526696dae958a078b2a70d92d820e888db4856946edb237d2bb0ba4756d702f4bab0650982cfe5ef6136e24918f9ec65eea00ff96d89c8efe7e73259e4bbf2a5fcf1412b462df2ddcfc3f1b32c1ffcdff37f7c3beeb5af208f6e7eff5e64ecefacf735941dc3d9b1c887e35dc877a35efdecf83df738fd8563fff7331c3b06c8ff5e7acd7bf5e7bfa95ae9e1f8f71de9c2229f7b362cf25dce12912f6ff611e997cf3d87a3e9f90bc779bf09fe7c3094ee85da7fe1f83d5b2e2cf211f15e5e3bee5cefbb9fe158bfef7ef333fde904ce3b43fa9371bc0b89b8b0c38523b5458edcb6459c48bfbc0dce67d0877759d3fecae5f3cb8be2d56fcacf9146d0e2c703000ffed932dbdd76e882e4246f7e4b8bcb7eb411b76db56423058725773e0a2ae830b9f3596861d2d01a2595a21feef792924313f3372d93527258725facecbbef69d1909569596824873e6777a86dc0cb560f8f99385d80f952ea98b1385a143b50c51667bb1849e416820df5b3ff11731e595bcd4879caf1439ad0dba665a9fc6ce9008a6a8ffc7e4c05f0cf9f96e7375bd6f37bae420f6bda14acd822a5b5665996655996653fb3969c79a6993e1c8093ca3f6157bb47e6f395a00b31f7c8b4ac14aeb4e3062edbb97471da1b4093eac4455bdcc7c0595a1c8ea6c1d1a2f81ffba2ce1f59dbcbdf6c0ae2952217ca8e61249e4905523afb161ed20a7e70258d2b6158145d7eefc44935bb1286c51197bf45d1bd464a97270bcc3bad942ebfa58c1c8015e1c8502e8ef312356e65688199c2e9caf92c1fdd9dd685d39532cb1dfdbdee02bcc0e31a547e89f208a003ff1175b419eb6d86224a93db5d337dfa4f903a8809d5d4f09c3394ef196dd1e77e0b5db4b5cd60d3da32fb637b79736a8b6a5a669d50f9388ed314693681b4022c3063b774fde5f3ebc81bd5f37351bbe44deaf95b49dea09edf8de44d7dfe09246f4e9f492baebfc6435cff4d86eb4f9bcaf5e7dc88ebdf79556e87dd35bafe4aa415da163cc4c6840c4b9a0af5c28df03cf63ad336ca75de37ff149abe86a74785150c53cf57662fade0215a86a6e246cc0ecc2ad708b5fda829c92afd51db423b713f6e4c6caab0e54ddd8f74c9f681a1ea4d21e8fd48bda09f29fcbe1f39286e4bd86f0abd0bfed885e0fa7bd4d412aa5221a3c2ae612b4919fe53e846520648ca30cb2b98080e8c5dc595b8fc5a384e1c2e7fc6ddd217833781ae7cf9f2e59b86300d2184a76d94a3492317b54bc98d26d09599691bdd6851bb94dc6802cd4cdbb44d0b472e6a97921b69d995492317b54b299b59bb7c94495ce4dfdd2d934c26a8fc2ca5eca14ca110010043210cd918620dd518a231b41a9a3104800f87564333865e865c865a865443a11d520da586504375e8346447183a0d9986c0a16f680498a16fc81bea86600230d40d7143012061881b22e1c6d00dd13fef25888888888888888888888888888888888888888888888888888888888888888888888888a823a1a39bc6511730b87d66da46b9cefb40d3a9a252aa169797192b1a35583642f86040338c5e945e5c2e452d492d3e2aa115d0ed67cd30bafd355e946e3f8d17d7ed5fb914ddfe192d49b7ffa5c5e7f6bb84200200502008f419794a9eab2b02816ebf4b8bca73ddfe140a005fb924ce870a6d310d4bdb6b76e7494e4c10eea9dd0941881ac080f9b4b4274c5168b163346cfd47594446117190d8b4e86c9d68df822f2dad64942509eb21c123ae9ad9991b55c4b6284b6200410e3f3191524a6770c0449b1e2a34b189f90471e70cfc9bc6943a9fa5d2ea13dd6da48cc348191b794323c0f206bc1ee435fe2f788d6cf71fe235eebeb10b7ccadafc6736cb7a80e94fdba3ecce93cd9aecca63fcabed696d566e1b0eb14517025aeb3eae9383541b464894978ea0136eb76f7f0aa91542453b31fdcadf4dab51bcfee09116fda765ff1b2da8ec750784283df9b3710b27534a659531ad49b9692af45cb7367382a08e3296f33f94c664c8e256f1e60a89ba6aa1f5252083298319130329a5ec7e0923a594b2816c49d9154961dca00a3fcd342c32987f84b128da5c2e97cbe5126f5b230c050d8eb85ba6e4359b92d256a4c45b726666f6ca2b3781a41e338f8eb2f67939d1640c674a734e6627aa441a8305575bd27ee049a5cb73ced95736cdf48a8a9a467e933be2b8382ed32beae8195d7e0f055e1425640e294d683ba22e94a681358d91bce9f7a6d8b8fca3270564c011bef38bb89452caefb04a19edc32c09256bb8ff30a4410375476a823bc65cafc1066c2dcd528fe14c29aba294f9e072160677a5949af594944a8095002b015602ac045809b0126025c04a8095002b015602ace872f794379037f3399437bb226f62f3c6133583654af28659f3a5150cc89bece5cdbcfc994bde7cdc4f0d6bd8d2227386e58a5744028f364d330c03414a29e9caddc5517eb7c4c16366e613e55876b7381d6c40b79666ba11c0850bb8d062298b152c4d2c5c6860689b155458ca0c443e448981061aa2643c60917151830c05294afc64b1e1099629b61fc0c8015b8161a67b0241348bbc10d0ca51281728e0b86842b9a022e3c205d904620a2f03238bacc7072a84e8c187255b94e0078c1780714402a21b5e89e4450c5f40b9235c89d485d2f5a427bf0ec7225286805d035c296588d805c375c766728f9a6674fe024b37cc873b7efc05933b764c227961749979b26250c786b11b79a89c13486951def148f60b909f3d4ef791ed28fd92d6c9f6fc30189ec8df6c1fb5d8a48e0d9332c607bd5d83145d54b93b6e9196a5e6247e6666666666a0531395a54f663908a845661f76f11266a6462c5df963bd31a08944c611920e5c62f0e0eba4fbda71d45d3dce097b22f3717a34a59e1743c18790cb8f8342d3643fada6254952471413cd159381883b7a4ab11d2a8a49d3d49846b29f5eccab42de645eac1ac91b0fe66778e3893a6a2ecd47deb0955b45de682ffed4513c6166f49474d8bc6ac09a90bbcb9226540474998db68841390abaacc431b4464dcae5514bba9cf9209561c41d352b97df5bce1a55d4ece7084e540d887ffc64365b727243893a7ee69232ccd90c7dae7c8f3127d1361de8f6ca61cab04de182d88c0b4658024aa24d42d0fce901481a35d84de21b6b19ac83cd0074a0ced8cc88ab246f6a9908e31c6d63a409310140d163d4714614ff57ee3e33c3592ecb47dff963cf9d7dbb3d32ba4872b44317afc3360108b2f93a2b77004eb069dd577f98293e8b1894a6991ff39a2ce44df699f7a836d3e60b9ba7f2666a3f330f649a09d8b92b4bda0665a32428610eb5695a4c73c157ee45f03e59c6f08b9b56df6b366b6359a0d96e881abbfedefe028355cd6c9d3c50b905c60aeeca842b91c008baa92b916e40a573609feb9dbea6b1c0bc449ede163c861f8b902b3f92268ec7c026900a8401c30e57109145113ab4a69df118e6e1e274875b0f777bdd51e4e18e3624b8a33c1ac11d651213de81936210424a1639b8a2a4498b45706f96aa6831e4f304890c5a38308ac20758f8200822b0d2f3430310c6e8418a222a825a3540e8e0c312448ca00620b4d80544f460081c7491430c2d1e1e48c28a2cb2c8e1035fb4f85bf09a1e117c11461838c4e0c39418b4d8e7e23416660eaaf54aa432c240c5a07a572295b185d8930407604e74d0440992e8c295946103a432762863063bcc74e300b5655d1481bad9af6698f0608c20cd23011745145b4c27537ea1c4fc8292583167d18a789144166464b1810729e60f3c7cd151bc4860daba48c15d39b9de95485dc0600539405899428626578881d4e2312e7ff58107083e8872858907964832b7608018485481c48b1b7c00c591bb945284c868e04d989322274ba8c0490a7838e226ec8e6fc512139933b919d252d1cdb2226c4005131a6a40c611adec67b2a3ef4aa4a5264b474820dd400ad2952529406471747158ba6c8f385f0348199628782d75500450085a1dae646851a1c40743ad0e3b0aa5961541908249ab4376c152cbf3af435ea2c9cf1358aeb436508b5ab20b40020d5a1eee300c576479b5367012620c201808b53a9c01446b03cd6c82132ca1e2c7a88a131c5d015c59051244978a250deed06f024b13455538e1bab20a2682aecd955540c1e232b86366a8ca9bcd7c331ca8db8fd46bb865a30b3d8ea8c5ce858d294b37d519ee7774cfdd739ded01ce04519d1a99775621c6954852acc828ae4492c2e44a49c20232ae4462c1177dc395485250b99e0477cc40a9fdde69a689da75ced9699ad6bd2ad5696057b3fa2daaaf55a57a0f532a29532a55980a478a7a9cf66056363efaa9906ede076a4534efc1702c02a6de6b2a1c850c69e108fb7012a0251cc4fc532487ca979542a6d36251aabb2872f8ab73ce79026f064a33b3c7c07d4729f7b2e33ecb38cb3d0d353bd28d861ab785fe9ce6194ff6e624bc19266650506597235b6c4e8bf26788a8fd3c54b03dcccc842745d0810861d4e92981dffc0354291e7f2a306c26fd6ab149d841dbd3efd7b0784dc3aaf7ab617efa694fdda7fa0edcb1316f73e3d197b31d2465b2ffe9ccce1ff2e02c8b7b22db77cf6ddffdf6ddd3f7a647b790a1340db749b6e95d9e939a86ba84e1d7623682925adfcb3edab0d477100ad6341ceab90c76b3902f43a9b54deed1902f2fc9763014207f5508a2c21a8e4ce56656282739a8c5cc04f2e0db826753eb66d362466d084cd4b183725acc42a634a34e5ee817595d16f7dc73f68516a975e2fad6cb27e298f1605acc1e4a07bd843fb20b30c3b79ed868315bb560d36266bfc5ec475052b95932d0f6933385a2cc32abe306794cf6dd423763207eef9b3d6f3c2343c561cb4b8042067a42136b076d38743ddcecb9ece9c60348ca64afd909a4846a5519de8c13352505914d85a2fafbbbbc19a3cac476327d05bf10a6c5ed4fe1b7b8d5ed37703b3deb034fdf77dab66ddbb66ddbb6ed3ff0b4fdb66d2730ab90c52f2f7ffa20ab47adfeb47a9e4a43b666cc9841e34f34683c8fe967bc6c799d52a95429d4a764ab2564b14bc862cb02e2f2ac0fd2f2362ccfe95b4edff23ca677f9907d50c0d1d182108fe917e93d4ee01c0648ca71822a94ad2d94ab1b2fa8f251f764fabafbc2e88858d2142c4c84bc49bd3f17c139c0778f364dd734b1a671124208efcf5ee39dc63beb475eea0ebaaa1f394a507dffba45ed6be35bde6f7051abcbcb4bc832bde812b24cd6488ecbff8e8b8be862a9a35a6ccc085f9e46e8f2441d442394ad55285b3542d9ea58a16cc1b82a0d9db43c2bfc99e15b362c8bbe8d57bdeb5b2d9605c4c6ab9e456d101baf62056979976f7997ef42560addd3ef2ccb47f7a63719dd9e4b717ea8356e6c51472eba2fcff25821abfb212f21abb346725e9e25846577745e965e5eb65a7e94575e4c2f5b367e9449acb0c74bccc6b7fc288f4cdf461ed3451ee3cf7a7779f76f096d84a0f7acb0a3b490aaa51439717d4bf50d3a51bd296ca316ed5b888bba49d37cefdfaf3692d234dc8ff4c3958ca99f0aa59441594a654c7d11c2da12c2015a7eec28d755ef36ac9431d5ce163b6422a48c3f1872116c82bf6774c4523816f29490b1b884dc3e2dbb002fe18c306807b3528b8b5eebb3d3a2c4724d3db54f3b70f6d9fbece7f83dbbf3036f77774f73017592768e51a9dd1d79495e11a244856349ce31777f77f9ab973ad6492d1eb54b08c6519aa69fd25add4fd841eda7e08e6cb372bd8f8c64cbbbe70fe2fd6779b8f79e2d0fd746a0e5e96cae5552c75e6a2cdec3ee3cc9e19e5f3a933181cc3ae159794babe341674551c8636ed02e9fefdf3790342c8514fddd5d43a40c1fb15e55c80a37aa74ec7ba2f1a6c50f3c1f9238cd39e79cf3e8ce282db67d20ead84b37f673ce296fb23bbfb178cdf6f35570e5256f64d08e5ed2e2fc49a5fabc0c760fefe00e233c7be9cecf93a86310b285669dabf9dd83a4612412266fac3ce8236fd847cacc67bdea9c7fa30accf4a63a14f143b34d6ebe369f88e69d77de69d4bfb3237dee3dcacddf348e736d9b76a66956f8a1538f70bca6dfc1243e7dba0e758927618e60362c2f78bd86e7c97c1ecec66b78bc0f61441610eefba7cdf1de0b59f3bb972d1610ef3b4bedcc7c66ce2ccbb22ccbb22ccb7cce647f42c6d3bdf75cc803fe479ffb0774effd939ceebdff421ed6fc69698b598d16549816b31b59d47ee7e8953099f71c438beaaf428fccdd516851d2d954a06c3ff841a98a0a68baabefc842ddee736699b685a30325b9feee3ed71fa7b15c31c6c6d2626c5a97cdeea86645de4b2b170315751552ca5491ec1b7415d957b8bf94adc47f714786b5507604ee68262dfa1220d74fffb4e89b06661374f14b289cd72fc3e48d56b342c73c216b68c8187f2b56a8aca4701c0a45834618c6c40c6000e34aa7693ed64fcde60d3af943c1d0366508e726b1169a869bbc421c3a2e73fdf98885fcd583bce99e3e4c863d5aded01aee814be2170f8f8569b1beea7950aae767d11cd5d7e71086456d9094951e1344657ba0705afea007b125ce3c5cfff1c6a51f00998347204df0a7e34af5f461e44df7f45545445ac45362f3d79b9fd6c2d28b72a6f79c9a787c3ebea7f65b3cbd04a965f9009f3e0ff0a9859132a7efb912b445c46beab938eddab3f660f8d96f7189bce9ac28657c690b75844eeaab5a2d04fcba22e563b221238a2266afd1f7329a651cc759aa6594665ae6a2ce4f0c062486aa0d9e8499daa5b4d6d5ea5ff41feecc92a28efc4d69f5157d4eecaafca1e3306d8a07f21898166db21f436090aa18eac069d13950ad402da960d77778567ca989d0cb7b93e7fd0e683d0b5af93dde33f83dad530d3949c9037fdbde6b3ee2d52fced1aeeb5d17f27b5e6beaab8ab658548bdda996c110279c091964010141d483bfd99c54eaf9c1fa29bb63faca33a8b1ded4839f937ab07ecaeea41eb4f5bf4d9f837a93dd79b2fd4e0eea4f76c7f428fb1ec3dab655157faba95052ef5bca781eb70311e07abb907b90a854abf5ff5a3b284d9345c4122e757de576cfc99a56f218ff4ede7cdf7545dea3211bd550998f78710e4a656b1857f52c5f5cfeee3ded634679a067e977b2c5629b7a9efae00749f198648b819c1ef5414e8fb23c4f78507ffa93e551594edade3385dbb631802d43798ffaa99e4e15e5a9dec4d3f2a8f74c9fa37a53cba36cf518efa9d7981ef55efdd37b3f519f637a54fd93951ee3f13cd99e07f5e0bfd7a42ccfe953ef7d05423df839a8072dcf137e1ef0eb6f36e7f429cb63fa14f8f573c0917d4e21ea73c04759b639a64fd9f718efbd1ab2f85bf018676b2407f5a76f806c9dec0ecabae947b984b231709616f83f4ec84195adae05bee94719e3e6d3d0e5665fd84ade67f1de73cffbcefb2c2b85efb7e7f159d962b779e1f30ecebae9d2bb85b2452e4a6a291da5a1f8b7d27ca9605db35c40af777677679feeee3c9e70426c86a00edca014f36122880e06c311d2a2afa0e3ff4410584148d36cefcfc20b3b0e903974b0bce14f7dcbfb0bc3aefbeabacc780f47caf8f70eed99f954bfbea70a55cf852c660159bdea59281b64f52a56101a9f7a1a9f7a062dcb073fea5176cc21e8a29ebfe7ae6aa85266a49799023b5ce0cb5246cab40f19f20225713d514769d447b4e87f638af3d1e52b5ec3029ff5b2e5bf62a12c0b088d5f7d101abfb23c4f725a7ef53c2e4f23c8ea693c0dcbd3f22bb98341cb02b2fa1a1f64f52f4fe36b589e27392f4fc3f295190fbe6c8176646e29c376a4389cc3c60e75e4239d99f1a59429d2627ffbc8733f6b3373615cf573f04f21117ef03d3ea14e28cb4a01f5dff3409d4ebffd47ff14d2e721fc853352a677c217581012aed0a2ff4bc82d433cc67f86bf4b58398703a409feb09922ff71866cb97054e7867ae305b54f6dd24288263118bc18c2c51d3a3a4d6384190552c69f9558c1dfbf5126b90ef30b6af6938d96660b5e63a32365fc47c8a2b28ebc01e9bbf3704fbfc3c072117025d292d26590e973cfa3fdf63cf4b9ef6701d17efb20f4b92001a384dd7e1d262220a020825857c67e902855b941889b99dd9dbd9999dbfd496841f59f25eef8fe2e0591183538199a82450888b06202c14f9325ae2412883354fe04c06e58622951295dad64d822882bbc0cd78fae2d65d4199d76eff6f6aad3fd23479bfe43c7997f056666f618121c6a600195296a0002193310d5d0edaef9412f17d0962d61116349de689b65659ce536cbf324a70bb2bdf79ee5e9a86565cfb1329bc35956f6decb1665e55856667f80ff8daca4d44b617b3e4a4da5452a494b49fd5aeaeb53823eb25fb58fff118d2529293119e231a1062eb7b88dc14b536249536249928695827c66cbe230a6d4e26aa9086777e728eeeebc5df95ef6bd206ffce2c001245c0efd9c0d86c120ff424d42637ee4622cb1184bbce49237d9f5d5aa56d91b57f1b92357f14972446b6cc112c67603b19bc69dabd95f016db16d99c675db779a16ea6cdd6f1fa4fbadfb20db77dd735d8ba34fd449b7c93eec3ae60f691a239dbdf6b4e648c7789126e2b9834fb4f7b7e95ea7ed90ee79e87bcfc37df71e6d30fb2dd4e9bca7cfd379f48374cfb18050cf769d97fdb86a75d96756c80956477965c80a4220206f7248193780b491328e139bcc3ed916937a30467582e11435fb17e44d90ed6d3c66be2065e6bbad5f92981520ce01720ef0ca222264cc9d4adcc0a24eff1584d8101ac97e7b4fd3b42903d01290367907bb8ab8082907b1eb2faf44c201952baea435f294eb0c03c281137766cd6dd23eca0a2d8485169d7b2eac1163202b5a0b90f01c9d12aeff4a0595f2ed10fc3c87ca98f71dca98cd0f9da661e6246a77dff7fbbef73ccff3bccffba2d4e64cba49c6187cede50f863f3e9d2ee70747639bb5d144f4002de26000ebd1afee41c6f8cf24a1d7bccede9f8a94ee782a52ba1c0da9ff40bf5fac201f820d402c79f9b4a865fdd879eae2167d11a078b5a8e5119f7b8900c56b65397776b9f6ce9b1d5989eb594bc9d334f25ccdb251df1f2ab4a809e9f0699ad1a67718d9a48f8f90ebc448e40df85e6146e3ca6a6fd3344636f797f72aa5aa5e5ef5d27d5e54fff2309c93a3fa97974152dfb2f324a7e55dfe5f5a2e2d76e5314eedd88de2453dea74f2b28a329950a8aaf929ecba37852e70960b5ad1498b9123552729298701a4092d214e0f1d4da3230457a0944e4d8100e595ba32f653451295cff5a69f0d42af6e4fc6c0c8582d168fe0446d2eec578d586d20a75247bec27c83862aa5c0c8e8a67179554a8d995248316ae980a235aeae94af57d3b8cdbb13d5356f2151499d9ef68daff99ccd9a8915a05db6fcca6cc3663618781e9dd763c1dd9d5d73ce8c0577f7eefe4b57dd3dca2a57feecec93215f498548991f493833994d77264a29a50784840c3948c256ae58e1176df0d89b241851fb4a0de4a1dd2b4930a24ecf8604232a07a6c064b4e8bf428bce821029557a8cc38e623e499e1375082bdd982b1ec73ee897ee5a665150a765a0249ca4ba0d7c7c5c41948088263e33c84149cc87491042fcc466f8b9f42a55b9e14a0c53402005cb0f42323683d01311c911ccd9a7bb3bf38f33bf83a3e006a30dc9932ae98d2054f92a48d85512848249f9d36b3679074b37a2044e83d9b325c559cd397facf3ebcc7efefcf7328d7fa56573f66987098392a3eeee963ddbbbe5d10a5f8194661340421f9062e5bb32f613851b2d39fb747767a551c68ac086e0f44072b4431de95dfa6187236275d582b35ab5d4bafaff5fd5dab2aaab1e28bca0ca8fc9e0630369a565cb997ed952f64b29abacb2ca2a5b66724a9b99aab3423777650c86cf0475a4374b42c58f0c3d7032650909194e6000f4ac2376b7fceeffa1c3cdddf3b39c1659fe8f9ca6c97eb6c5d122c7729891d0d0dc6c547190446b0112f4d332ee02dba4fe993481be30411f9ed14ccb00e538eb825f6a67b665feb20c4fc27c5fe62d3ad40c5c0048810bfaf0cfc7bcdc7bd3c7f4f1f1ae3f274df88e6499c6fd117a3929e34fe7530bf4e5beeb2c9532dc77811b4e39a7f7907af5cc8c28babb3b9617b214502a28b5848b52400d545ff0712354910b469886896024c6b82db0119246caa671418afed3d259b5d44b0732901071c715123478384d5b9ad34da31f13c240d2888129973110bb7cc4e7f2e7b3f5b66dbf6d46476020e9aebca8a356d461114de8d22fc9f5ff9268459a116d752d7e1eeec8755c96db356e50f9f329a209f977e1d692600a1c63f1cbbb62f1d396ba04181f100562b2eb675a16a3c57ea2e9e235a7ffe1927d8c9895947e64185f39c9eb12cad610d49bc2963fa97e86ac14e67f3f2dcbc77cee3996ecb91f98d3352c202e9f7ad3b3829cdef4a737d99f27dbcff02d132bb3415c3ef52e9fb23fae6f997e866f81ed436f6c51976eeaf4f3673ca6bfa7950a59f3c5eebced699d52dccb964bcae54f3f4f3aef65c677eafb888b58889bb4f8422271fcb4e86225a32493e5241dd3ff635ab75e8fd75c19454c61aefde5cf77cbec2f2d0eb1456efcabe110c5995fad347b24b3d1e2af7cc55d7f5173eb813a6d5d8dae69dad3a6d934d99aa8d54f6bbad5761b30a8f6678c3bda8844318ec9eb7a1f4569299cf5e6e01d1936bfbb7dfc328271afd7ebf57abd8296bc5c89a90051f92565fc25f48225358c5ee77ae98af6ede2f73e08fdcf7aeddaac6c6d766c2013f8e3071603d62fb86701e1a79fc3d47a51e9f3e750b6d2637260c0145aa8411fdd84b5b2d1a2376cf5633fe5fa7a776498a68494f16fb19590341c0229ca9b2d49fba057cdec0c0e9b2ae22b6b2f0cd4a99a5a5c5ebe47f53d206873523ba84ff57cafb23b4f3e9ba3fa17bb737ad58ee9533d52f5a770c6d730f5a8f0e55b5a5a567fb23b3bf55bbea7c7f4392e6f7a7e138d3fd99d1dd3bb7c8f29b4f1a790f5350ce151e18c9f31e37b5a21a45ef53929d5cbcfb03ba87ff99dca0a59acfa19cf6c73663ccbee9c7ec6ef985ef53d335e5c5a54a9daf3b53783275fc2e0589b9ba3bf87015a587dd3f8962f32dcbc915950d4b16d1a9593c494798748994ef21ad6f722bc6cf5cb8ef5416cb09ea7be8d20acb7c1b2b2d5b9c0b7fb2c0b08eb43f820365e041b1f820dcb491ed3cf637a96952dee25e8a3f90a12d5b35c242d13dd98d2df492965b6a298a8c42d56653d0b15918c04000000b314002020100c860322a1603020d8f3bcfb14800b82a64a744a9909a420c86118a59031c410600000000066604884a615c25470e8f010f189065bf4445d10b8b739b138fbc05893b52c3095e2ea83e6a8647a56bd77012bd51e3b6c9f3887742f8b20ee74a76fde9910832823f95d470083776d74d1bd868c54c9c931ef85466134463bcc318b876871ccd7dea7e1ac032819e1b65b597bafe9c1d59d278e2a2d962bdb15a405530967cf3788e411c9780ff2ea057afad6521295ea5e62226e38d4634c9ce8f25a5cf3ade504689716624e9772a0676b9c0254a8c6db688f243670f17d23c5e006bd59cb78d6aa32c1d8d7f28308082f6cb598bd412f270ae9ee8d8e7a3601dd6d39c61ce0fc640777b7fc3d6096209dc61c80a4bd13f86b5a6ebd987cd1b3bd8d7d18bb2fe8f56fa2f100f29f911bb2b7890035a7540654e02615f73010fe565000ea747ba3c6bdabd1af619f341064f88b497dac6a530156e1cc6534bc108dd2eda7788727cbeca858af8d6c3966aacbff080424d8e00850b128dd61a37933eb3d51f210b08ce82b80ae80016c850721253bf628f95803f48933683fa1fc9900b81b074db6b0b176751690b1b8ba6cafa7193b6dc39c86468d4cce4186041cb5cdf928754d5eb216483e0ac7b95c90b0a5d00095bea17056a4b211666bd557d56ff501fc7be05d8a62b6a69697a595c08b65bd3cb0044f378c103c4bc3ffe38cc5dc561796bd6db78d860d958192d0fd0af14302ec503b9dece29b40976330544f683355a455240d850342bd0457e6e538534ed3847c015ba04279aae07215ea38b21ef56cbedc145432396aec996804b067a49ebd213d5caff4593cb47fcb90821aa03f70d640e49b49ffffb09c40bc4f98844882bfc1218b12652d06f314d962aace0abc10cdf4c7b2ab6fdda9d783b6c0f87cc4ff7b8c111d7c3ab0c749ace3439196178b4f6c53cac2c01e1a6e08892a2acc6e9f025e0b4e21c29037f9ca3f186af1ae9ad928e321dd99bd7e725207062995f4c83a19ee95b2b5f9e8f16e4199700408646e3261482c4a73632330f60e79a39b2701c9c7c0cb7a0a70fa2e7ab31f7bd461083991336428b388419f1b4c21afa2775a3e3699a79ff8c2d4a305b83222d77af91e09de4c2a8b3886985c8be4d4a9a17146c2396f1a930cdd24324e9f5ea19d5f83f3f480f32888066e592d3ac26a40ff2852b61b29b6643f15ef007388c703abc909389937e78a1743539db5848079b8f91554d6918454d911687b50dc4a3e0fc4f1800e285d35fdf80dfa0c8b5c59aac688b3d22b4545c028dcd4ccae874f91b1e3ae2e053f2250520dd8860ae4bbefa2f6506b56bf913ed293b6df6be9ae642c66c9525c1ea8679fe54f421e88e0d5cfcead63c83b00d615d2f7403e42c04a268a8f7f54d138422c6864506ea23f235601e69060eb09ef68314ec109fc0f55db122f023902932e23eb01f67c357ea380ee81ecbbd5bdfaf0a0b21f582108050c5456e894a862d53c380f6f74b5309e6da2f4f455783c9134a77acbb79b078fa1bd2165cb7165bf579996a8d86179330ded40142e9c57f20acf645a16e44dd5714284cf8967b55f2cd974b3c70dfce67b0581b1f79c89c204977da5fd025fa51a360be647336b9aaf3be20bc45712fd5feeb3f629358e96c78feaee0decb519b509ad53c8aae236751276eda5292d4b2a8a2b4960a790c6af067158226678789f94ebb00eb96963cb4404f1c94f640406d8db0b13bd75bcd4e6707adbd44a3da9fb430218747d045266a214a4f460687fff26e435273598468b5dc1a2afdc829622bae443ee2a39c8b6c65fb3b8b866317299f487e2dd9a7114f0c74ea2c70e1252751b4ca6478dabf167427e6a6f69cd66cd34bba19680604b962633aeb6791506c6749be6216ba7cfc690dc180383d8c00358b17bd542a40fc33529b08c21c15a0304af3641f487e339b397bf7ba8d9251c870fb4c8670d29d2e6ba05916fbb6dee3eb468794245767e9c2da92ba0288c2b4ddd7d91d0b466fadde43b577a06a937a8ae88ae55ffd5b74cba41aa30ca83a1b4f6d35cbb4cc3b0cf146a36d8a5dea9ef6a3805c23e51e8ba8ec03fdab878df20e5e9327b1321f805b5483468c997f19f2ec7906a553ef16b877710d588f982c4e2bb6d6b45737b37e2bb74fa75ac7bcf78a2ce301be4de6ca4c3c346549f8d8d4e3b49b3dccd8d57879cc57dc52d26d5739785fda8baef19064bb06f6404050183f64d425a1d8ad4e1a1f6433fdcdb669b754ff2325f23e3a8930abbef90c004680037b5004304fa85f9fe87e92374fd246b70ca76365024553f9f050ff9d4cef2b1f40e664cd1f84174cafcd457e5303a543005559a1866690800c8b00dff3f9935652ef9c607b059d2bbc07a108df83fb911a19c2c7648c3b27a048b9c468154789f8fa8fbf55fbaa1e2ac3f7c608eb7b5de4e9e555308f1e1a28f5c0225c6ee0e536df9fb1d59df865e8b0b951dc36880503444fb56b709776e493b8bee32c9159ba0f02f7a9f5c9c5b5907b0fdaf6afa911b44c1244b07a85f6f645fa4abf00fb2e3941f01ff19e82de520ca473ff215d694167fcb3f8d816bde32ae356b487526310db9f820e04bd94c380de06fd19d9bca85eb55dae48751d34ab6491bbd19d67feee46210248036cb1360627fcf03f541f345f0cbf8165646eb9862255751985c43241598853450860ffb255a4d55dbd519bcd9921bb8a434e7e6dabe30d75ba1c2c09dd1820bf64d18dd7c68d09fb0867df2d50514c671b030829848c1b00fe7a04592c8a36124868f28f0cfb05a9f25c2efb0001ada901e309e14ac063d209a7c51759cd01fa0c0e370b39652a3318f2482a2ab7c9b17b098fb453557c69a94e9605cd1e3727f4609aba3305e4e40504ea09e94d048f4cc81bd69a26673212a190463e659a28b5ee763be46fc1a9b8ccdeaaac917766460bdac12570734aba76d591c386523ffc21085c1bc4330265cd1829ca3c9a999cf1c382c4500c6e57ea54bd0d490aea47e4f5390cb753eda2564fef53368229c82bc12027fb95a190d8bb50a1d1ef897b587a3486f46322019fd719c0237cccc8c669d8b911df5e6552e0f2f6bea79cf9a4d4165af648c4cc7ff453126f2256721f612dcb4c74e9b96b089d98559673259f94319193bdc21599affc7085ce20bab22d473ccdda37e1671910dbcf228be46605aa8f3f9895c29a39554db93d17f0d0a9ee816100d156ba574c92378f146d21a18c2dd2fb2081c183c02c62047b8c446b10243d67366270445bd5c313c7713e9cf279d25fa53b39d5492c435c36d9c9d7e1900b245f222a4275b14e8c89b026c34ea479e33ac0a675f70c26737f21b540d72acc898a1c73f742cce5974322315693f09691986be793ac27b5237de69e678bcc4df255c1e9f4fadac31dad044dc1e6e8ed364874bf25d437e4293ab291a6d8c312692c8e464468d5b7574c8a3f4cf01811db3521b080bbb510b0666ab16bee1880ebae585366287f9751be12bb0828fdf88c2a3cef2564131f5c6cebc51a03d79cdf6dec8bad1370a71e16de23ccd4d79a040cb86bc2c98eecf84b3ed3f109eaf9a9bf73dba4e5a6e9be3ba2f3ee86eef5ce021a4a32258b7ccc55137ec469fa43c8ef77f914c04aeb9271b1a9b58479406d42225ba015b1b0a5bf6569345431a3a570ee6df5a602a2883f378bd8c7bf163c6f45d4ab0be84f08101634db614b01117f7de32f562b7cb49e0dfcece4c20cdb69f1e434784a911812cc32d51945f903b9e1cf9f4032cec695ebadcf52550ee3f17a8592a4fd80091faaa187631b5abe3e4b2a95377d6010613bb571f2a9603b9f4a28885b109b30a9034ea36f62c0b0d19b92440513f8a8c2c6f630c7d8a2b5c59dcfcd4693a9ad8cd6d0855ad055da9df449f0bb2079d3663bd7f987562481203c1cbf4242bf745ab03a6c7026abb975f5d9778352188c012a85124b79c825993be82ee3af20b45482bc58a53bf8865c4c2901dc24083d8b0771818b57ff9d79214303aa8b1b86276229f97430ef1503b23725f5747aa0f24328d429c462385e8ba9424030dd05e4b51fba5dfd9450d3f5ea1d1b4098cf5ddea2f9e4cf00112f16aad5a1088f47a7fc95a39c65601ee623f68c6b28e19b9ffcbd6224abbf1cd42c33e5fc0211213f24317ade6b58cb40aee648d2b6d78f99cffd4f2d0a64383937f9bc8e08e3acdd11f6b420d16aafc215df4c1efda4489b0d3451b4e8669685cc633902525950c41b17404966c7174d7f9b34bff0bdd0182e591549c54fb70c98a154dadfcbeb24cb9cde24a34b9f1dae3df224cdb52b497ec4183e4f6725b6a4c43eb30d7580f6df2aea44eb14b951f4179cc7d2a992fa0cfabcd43a5ef1698d09afccf8b3fd19a2173f974463ca2e5f301e6cdd43dc6166931b319a17881f2a1e2fc27aefab11a3e0e5822d4f6168d5c9c89b52ce5b20dc43aa05b9a82a932a2c14c6680d8b760c61e4038fca1eef5887c5fa87b839b59617a343b60965850a43f67b53d79d378fcb4504162b0fd21f49f2b704776c9992227cf2f4cb0263d746c8a9117b63cf3abed6f6853f2e476a6cdabbccf24b0aee4361faba33b0dea4825527b251880a86c56699337145af63b88e70be9b19a3593b077215b8b99f0954f5e952017bfb42b12167a9712ec1123a768cf5489ffb39056610b75e2e9bd691a985c0b64d21b3fdef558a6cc1b83092058b623151629f0bcb995a18617aa56cf565ce9c421251e74e33bc28d27e85f1202144f0b7704eb545bbd7798a67d55031cbcc69afc90c194c80a8476265dc4568c709a9e326506f1d331f47b3e68c256e58a8433d5c11bbe32ff94e6a435334bbd876b6e33db9f3535276881f33196a272d854cd6b39439cab92ad04f01e6c747ef05cc7c515525a046254a84f332a9ed9f5539879c973680d86716a591c318d488ba38999b37c4637a81f757a5791687013911ecdc6b6f903b7bbe91df051484c34db22d9623d12aab375a95408fbdf459bdbcb17f028eaffb195c94d01de648a20be754124c0a5aa9f0fbc6996a3ff8a13ede018504d314337d7ce2a6497d2006b8c5920e10a52350824b6b8ec0e8c9e45346bd92060778f3574eb374554ea6ed79f6ef0df1099a36b3943e2d06941fb37e6d003b7c1e0d18ee9a5369906128f5760f17ce937015378fe15a6d547eb7998afdf79e8ce150285829c3ad010b3070f2fd4a01b8068397bd7956bef95943d99fcdd409b4fd0443c70b72e519e9959a43590b460c78a91f37f73e646f4120138cd01cc3d469291f667a906b6f1f64858814d71b3f14792db19e889db92a897083d9e9d577ba47fede66627cf2de46e01090b4af61ad6a55e7a0a2048e0a4a3a8341373205d02b90562eb6c1bd4184d67c4a5aa49cffcfaf4586fb4db9c16489d52f690362cc8af6877e3deaed0c590d0b9e76b281eef705c0f72c919be21ba755865f0ed47f5aaceef36eb8d000b405d410442654f1d2437d6e9081f5e54d6c125d39aa9e10803533c77ee1770dc38901defbc22a06420bd8f4515957f48205afe87e27033d3005b3b14e5f8081fde43c65ecc8514d30db1ebf0901898a1f886fbc8d58e17c831f2193a04e04e19019c7d2318ca41655b9b0ea7df2f4e5db2af3215bcabdc6c9409de5d26826eb6d73ac7207a70ef32182a41c0e3cb7f4133ad1c4d3aa7749a862a8988771218afac0621d1e3058ef1d271b4a9fc6f753dad9a2850c24863eacdae7c4e394b58d1b04fae0df7e144cd5d62419e2f0fe5529e38c9a65387d5aa5fcac01fd106298c82929dc0fd4c2a61bb055b3d1a453131d319388a462e51dd0d3800e0d96cb02dbaa21757fc39eea523d0df6438ce452a857c24be0d80b0df3647d6a38e76590a443e499c0e5ad11c469cca746da94f565d3c70c360eacc2afaa6e3c22670e34d5ad0c952e6d39d0fec0b2d2e7e28759cb71e85a6f050c50f91a70047afbf29a90c79cbca52c91f4c59d9edd687d0e374097c8cba579e07cb0a36b0290a763b08ca44e691909fa51138b1b5f0bff86c41c5d779ba824fd3a84e0f83b398f3ec2d87485373e9ec1570c3bb514a26631e27af642911dc80b1683658904d9b3a4090996ac30ddc42d316969c136b6f9160e150c4fe318b2ecdd353e0f02cebab7773407838ddcf8afd6f0f15c5fccae512d52963f5abba67e37a4affc98c10288ed26531ca2cf2df5d89df502bac042864cf0901e40e6bed50c85737dbc6edc592715d49535d0e09723eb8e330a5d1c7021f5407cd1c80a9f3fe059fc5792ae356660b74c0d9353d2d19f656605090634bcd49ea6354313f139f78721d8c2c31eec67d4a6a74be565cbf4c6df453c007d9fd9031f235b8709bcf72c7fd0f8a96734b4eb158115c9c4921540f982c41b6eece4522b7a5d07321b4f27ad65723274ee683ac495e6417adfcd05c9eb1668c0a6ed8f161eb70dd72c5b31cdf5773098554625b9af73fd36b6d7626595b373845cca7230aa441750fad3aa32ea9544d594bbf065e88f99dee28ea47f94eb0478e859e51357a2ae856e67f94cd99fdecfa0b34005002d0c6d25034e8a6db1e2536cca82d702469f5d88615fa4c598afd6aee5e1c0c51c9403191d989d8d819977f80c07633cc3015190800d23fb7961730c3662f9587da0e0e31b3d12e9907dfe7142b3e018fd160d9b64f067ef3a9e8a6b1ed10c4e2dec3f718d12898c7522c2de7695d2e44a0e55553cdc7df246b3aaef804440ca2c051cf287a85ee215a0f40a5860f8cf59fd24a46415038ce27deae40b8bb456cfe13b023213d4906a13fef4ea1d1519a0d90f3937cc820922a99a4932101c2de1dbc0901ecf2d5390fc4010fdfbf1ccc00fd0caddceba8175c43483167a3614b195a49a1c33a379ddf541f190177d5627cf426346f4558a5a7281b8fe2c7b88ac8fe793f7137c5f77ea1839f3d26a29888bc0fa7aa306c830f4d50f0816a00031284344f661b57341da69844abc798341203c20b73ec748a3dc83960c180a9117dff5559914ed85398104bc999a159b15ea734d09c2f2520cebb86fc6442cea436faf7e3a1348cc4ffe4484e1a139883071a1a8928a7bf0b27798600b7bc443e91ba3dff4d29919d93827224d2b5d617f1828b8eb1f284350fa583af5843a45db5195c7f9dec48323c2069a56c055ff66381de4d11acd0ae1cb83d66ed76b380912ae67c6041f730f8adf3479e4bea81d2963e0299b9b4b8aa022605b3ec3d5d1e0fb94109c4bc8273b06fd3c1cc9a830e202f2d3421fd5d975a59d74211d8ab3cdca04e3b99b381b9d0a1c46f71e581353db7e08c74fbb9727e7060555f9a485b3032160cb4d20fc7362cd825455bb2b0bc5852c829fa181e1781340981c562b060c16ae2836def6d2f6f2b1efe0a4e696aef6da160cfac75b77b627f5762d4fab23a3474f45dbac2b83c59df83e38f10eecf33728d4b6dbcfeb255a24861be3c941de4527fabb6e92a1945268287c55e643bff0213f660da8e1a616b0865a38998b6fb5ffc804d5b2e4cdeaf2ebc7528037cee50b4eef9aecdef707ae338b14f37cd2b523e9ddd6aef2bb098757340cb44d982a9376fd9cf6a72432835d9ea32fcd2bdb71e3ad7dc68918d1f5b77c13abfe04bf0b05747dbc0c3baed999fec41fcf4f12379566d364d902469515f4a7bcc063cfe92286d33d326acc081582168a3133cbebda683e852c5631d1affcda2752f862cc8bea5130b109163b4b54e06b2881b12c776b86525316b08ceb2fbc5b9b55ac3f709653484c521a053a1bb61bc1c43ff14b18b06c1ff2968e45df8e9618c732a238dc8b088fdebafe3aaa54c1b84ba17e87dc9911268352778efad21813662b7020810b7233dec45adc329e20b6a8cdb37aef6811932c5587f66888e81035c4ba6e4f38cac75f85015deeba9263b1d6d57c7ff0e04d688c74424e3701e030d2b798cae91ea5ad6a0d090d489e815da06f3563bb06f9e184c24b1c9c7a55e5da386953b28675ff97a6e80b390474f5eee72d4dd7d48752033b1a86ae561d08ba82622e4f47ee14e0e1951055d7055e8d777691e86a50f0b92dc7bc36de40c144564913b7a299b2e40cc2f3ffcf483bd208fc41729cc247d46fe08527e1a5e57ca736eb57a82a41707b652ff3425db8273ac8e8645a17e344cf552375b6f552fd8c5c1d16fd26fa9549c0ba39d4a4f1edd872d1f92c41f086aad8ecc54f2831eefde8c6590e013b87c96e52e1705ddbb9b590607eee4646cb6e1726e0b612e660dafe21b7ddd62811362e3d4c258f5c069b69964daabb3fe3fb725cef93303367b14aa48b5eec1d2a51fdd273964630929ad69374fc19d354123fb14665b4c15fd9abee282b77903662b8be7072864816170549aa0546ecb4e9a1e5344fe80561b9634af4c36406e0e06aa0b727a8067b488f47d6e4e72e5ea2e9c34b4ed01ab366d0dd2fa2695a4665bd14bb3b54d1831fc96cd7e6b8a0e4895372851f1ad13382dfdd27f8c8ae6d4821382b1ed103780bf30d6b08ff5a1b2201d5096f6a667e394305bccefd66170b051aab657d89d944ad6d3626eed17148d0e24dcd47c79e6af54d09b2281d6d74e16867eb0d76d81236f0c08d5dd0775ebc032e79dc458bd2f2bf5d6267dc53d37b539876a97216b5529732bc2f5e158614b367d04edb22aee61e7fbaf61043171587ea5adb32e9d88b332ae53097b4f408f14d32d84c0da94a9b30fb167d40d05eb54305c24d227453ba685b16288279e3fe180a61b900842a934f9d6705de6a4633181d02f12d057071678ffef94564a69042a54924b007c182b7c6b83292d74de3a32900ea89205c0b649ba2754dc82b1834fd51c99e1ae0ee3dde76707a856dc420957e283e4a900c12b49b83d230bde5b45e9f86d4ff55500fde9325b1c99b21b863c02a40a7ed64c8a0a42969bc6fdbeb43924ecec841b81b22d55b7518d27b6c3580f9ce6d33e186ccad382bf0fcd085d2f5514e8af0df92ef77c294728724f480cf0c19019c98e5455366af8aaa57989344d80c38b68303374265aa89df26efb895c1b3987a534c47caa935180e9ed207e65e0f9a30b685dba085c4d416a1878e33f735a5ad818ea6a2b630f9854f2bced44f6d1eb53530ebaadc0dd4b930a956a0010cf303b7caf12236f4833430f838b5a946d9d891bb09aca41c50d38148000d370ee83c00ba11d1b638d9e0b38941f1bcc4889bbc3f41de2c06bf4f98a4307e47edb04b48b5764477eae845e732b4beda13e7bbf154c7a2eb17a37a03140361824cbf39760deadc17244c92b8cef03566889026f3c05173d0393c830eadb1dd41ea4942164c02f26851998c1f208ef49280a60fc55b1cdef521a0d4b9b946aab05d01ef262fd9411a1558d87ca8e0d630581462ce587fce772743825745d3511c58d441dee81426519bc7937a4d148741032b478c3a720462545fd2816d035de52d7344a255d035816d612bd8ddcbdb3cf670a8931212a0d541a11c802167d1b902f0ead26de517a54962915c7057d57605ffa50fa599c9835d38f48499b53788e22aa59520fe5f44e45de3fa91f4111516177327b005da94f0bc8ae7aa6a6489dceb47887e83e4596a89080b476747b0d65150d03cf3265ea0a356b3fe34833a8dd8f7470c9d04da10ef83663a1b9f5903ed85219ac39b802a4d8b0627a75f2225e531f2679eefbd7293d973ab80787f9172353389a71aff7bf96b356b755a19c52f14878c094db47ce835f128046dd6af6033b504eb579fd53e68be769974032a2221e2bda37570aad997d6cc8c972a2080b7ae73620302b6982243aa2d445f72fdb765769a8ba263eab59acb940d348ca906c2301115ed53b79397d6411958328be5ed434cb3b40843a2831401406104c39b32c018ed3000b783185a67db5fc20b8983f0c2f7d6ac591685479364bfdc13dadd87eed6729d738bd5be4c5b35b642e8b6a24d767777015adaa73c9032a5514b11fc980e4e3515ef3019cd4b92bd71c1e273e0f73c5494c032c21a974471e14dc05e44053f590bf0e3e6442918030ffc081cba6aac622221eb6fab267b65673fc0d6b211b9a0d78987d940855ba492a199ca17200001de358474767946063e85a64d3f8578b64c5fac7331288abd6b164ff274412b00ec6f4a041598f3358014dd0b410f127620641d9d2c4146b93e6cfec8c4cf75c573425cfc7ccb41710d8af2cc469b181480a48ae977663762977049722d114650d41804640d27221fea5c7884eacfd5a42b190b3eda3c9410a791808a21767ea2382b82afd803a0b8b249b4c5640c26dc162d2eacf7f600eb839fff66e9a0d593dc2700de487f9c6545dec3765867b76f53833b83d40f06d9d9c2f42e8a88b6d1da413e59230318511c183d65216f3e2fd9cb0f23a410051bf16f693d766a46b630507d41fdab98014903627d8c1c49008280f0856064e12032446d90b554e6339d4f30bdfb3b8e6669d67cfbcc3886c0b2aa08674ca1bef0a14e8c80e0e6a3145a261d171ea23b1d3e134b4d0ed3ada74708b991c664caa82d0b12c4260eb4a12ea9772cc7914983b89606abca426761498ab76269925ff80c04558470b78787a245575025c691378cc99fcfd9eb70a3ba147681cdebb25148e80001cf910c937d0200e464a068a9e7d27bc7727806326e4be673f523e551266e0fc5d89c43aee38127722b72538f09413d03e0e3237c6188846adf51361df9b179a53e513c2b2b0bc518cb702b932915a71bd0ab077aa574b593d1b246293e362723308722701b99152056c3658b3ae704e2721ee30d95d42598c7859e7680a428aa5933fbfc7cd14c25e686a9fc558413ecc33cbfd9469dc78463e961499b4ae9ada0fb7b514020f1e07199aa13ec8b5b8682e2f7115daf72a032581def76a10987affebcf3ff96dfe5ac7a3d9f933727fbbbdf6e6e449e26f75716f6c1e9ba1cf90b507758e482320e4ebe1036c1def09e2837a9cb6a3c8a69ff729e9adf9721978953175d513c8ef9016d00ba0bb37c34e4cbb8e4b0b6ff8f33d6c554054a193a743c807048fc9c3c6195af627d0e8742eed4198117a7f1a0b1d480bc89816cd074772c38a5d09127c4d40d59f9e4cd7bd6469030244863eaec99cf25c1a6eadfe660618d6467ba6607545b6b4290a21874f6e4a3c9eb051e7c2a6478b66125d4d2bd7955534bd0ca6127d2c45accfad5e0e902090f3d7edd767dd43e99173605e6fc99920d1d8cc9200bb9972ac61c458403bb7190a5d1ccdf0c0e1e2f265ce946dab7c56ab746ab9512d6c275bb74ae58f0aac695600ac19e531c070192d675318116c378d3c8aa14b1aff8c0805125f86ba48256977b3ea8684f22dbab88355b9ec53b949ab7663fe5afc1d2ee6db1bad66b7b3c20d8eda8c88189a812386415042ed0d6b0270a5868c301cb952ba414b645c47eb714a08e8b776e7fe74fe0889839ffb13cd4442e2ca7509d2bc41e2a49bd678339dc0b33f0ea45ce7046896e684dded8799ab7c1a3c574102a501d0c5914b813eb06cb448c5300299a666181ef31713aea4f9875c50181975335958c378662ba73fa64618beb9b48a9843742010f8d10527ee64717f1c7e8de0c844ace8c854444978d84f54c46b24d472603f234927226e799d81861125109d97056bb303326239844d7b2f6742c96ecfdc7b362cd8eff33c7e787093489443c4ebd36b9add4a2b1feec5fea93b56a4cd9b453d8ecc91215ab3d41eecc276f4ff0e2798b10b79e054cf8d024e2a7583d5bb5791c5a053a9b38f7f539bc835e81eccc5281748e783720f9e866867cf2c005462ff22f9a5f8b20f5df0754437024a262ead2123af76cf986f947deb80d5fd15550bc0c9d7e716c8e6a68dc303226aec08a5d67390648f21b7c3be82c3bc75a796f08b310196d2edf093d80346822135cbe061e4b36fbe4dfbdcd128603af261f78d0895905e3a5e0a757ae22b87e77b6acd1378507e90ef814d151dc846d0a328d6c1769ef95c30e318584ae82910ae9d7ff728ee630ff9042c32b16216e04b2080c5e8648413016b87d638113658bb2fa81829d2a3c420184791c93707a5d463b295a77d8e396b49f1d884633c3c6d070130a6a9d569fb6ece01143f08308e7ec89391806533b001f900b2687c0550e19c0effc753ee2b09bbfcd7f222b658e069ac5a6a70b82c5767168ba6285f6dbc3bc4c9b3fb8639687d5ce457e0883153d810f087ae32dc35d5cc21e0237092f64b64254422d44242fa00e398d7f8a7f76c634da595025aeeb88203ac16b3135f13bd28151f9cbc7ae32263b3cb13ea3ab9d40d8918d47022d75946a06e379cc182ef068c5696ef4e93e5d369a1b788c075460f8acbbf08f40aaf56341e0d94b0d7117f2378f24df90ab100eabf811bcbae470fd4e4270531c3a36d67d7b4480314ed10e0fd401edc8fecdeddf04f854d0bf9abcc37a96a86291cd1c5924ad228676895c139674aaacbb3aa842bea158b6da699d176572515f8d754de50580dda02cbc520867c73d0021e4584f5eedaea7b917ba6f810be67204bafa080a9f7aa04401811f761e9554ee506a183cb013d426b22d6190250855073457b5a5f02875c8927e92cf3e1011f22961089887f0f1d4ee31f77e5da9fb34590a4dcd57890b2bf6f40aaafbe7ce7cebb515fc54ce9c3b2717a650bf33359d39a3b3912b53eb417835a96bce2eca281ef9103b7d694df7598d21188c56f5d84354ad5460b0e32bd797af662ce91fe8369cc3fdd9a4578408a440dedf00413552b7e6e615398a8ef7d247186d02d24faf6476f4ada3fb6a1bba98731876a632ead0ce13b2c5d0dba38d54a2746a3e8064dd2c41cba47509bf32b737ea98bdb81386ebfb780a004b16215e7c8697cc023b4971ebd910d437f1719e4f46f26ae27cfa9bd96f0721fada7666dbb022acbe4d710a80f2d67d5e4062927c10bc712559f2d7cc77e5c65849313ff06cb31241b7f8812d44a4436468933473da363d2f6e6bb4a087728bf5c3490aff63000f92ab7abac2ee10942f21b5ad961e4a17518f1b8c204e0005bbba5d925e0ec0161f5d5b0bfb67d3888fe00f3c22eaecf7d78b3d588a9210db8a38f1dcdd2748254beb9434315c726e454b3e7b46d1fbef875dab146b7c59a611491001ce6180a3f4f537764c57d75a55b90f6f97cdbf6b53345fbd87df2a29dc845a47b38cdfb26ef8d0d150520d6e5e51dca56666af7a0a358e4c0cd22e43a3fd294d50beae511180d09c18a916e701dd985ac9979747658153c16f5f7d0af46a919796892bb74c28bcdd8531266a62b0affd6a59d8979d26b9ef4f178974156a59717f6096064884e57718f65356012e43afde88069a837fcdd57a4637012c7abd9e2388df7d0d9e1066364d64f2c20ea277a0a064fed89978c56089e901f08b3af108d80a73ea8ddb0a52aa3ff2a927888628487b3f4f7652ee1c9df615a0ebeed3eaf9104c315f61fea1d763843c9a36288acaf424467e8c476049ed9f25e278d6b4fead7a567299d433494e8323aed8057a55d6bd7d3ab46267af9de5a9dbc1451bb35d3f9aeff1deb4c4058595de659796a012444bc9ed3c8a773c1be573bd8800528d799aa142f1e02b53371115a223b40d7d7efacf65ddf8b6e640157eec123390d9cfa35026d631445b77f0df22a0bb6da74c787bfa8bf2c0fb8174891e58a86274cb4dd5e3003e9b9e5925ccdda27fd98e5cde02c713d12adc04a1de07d6bc261bd90797c74e15ff4c66504f9d8f5f29bdb967b12edcd3f2a29fcbef64686b881fd275c6cc554789b5dd353824c24353516254602b346667b92fd3e59e1400482bac299fa36100ae167fa46ee6c955cdef16ff0ce4d4e39e13970ff7b298dab6ad6d84313309eb2b3fc4e75a8d8962825aec8b5c928453cec18ad603c8b1d0c39d5fd972453d6987e8f3522c23f6bad8006409cc5897b5e39da544138163d1b658e99ebfad2a12299711722d95c0bec67cdf2888bb00606d6068f7e940d3353d3ddfb9905ce18563d3dacae1661ab817135545c2511bfaf12260d0c72eab671c5b32a0e340e14c9554b071a149e3505d5520252b834b116d18692ae0b186948f37a3733827f86c366401322974012c33ff1687b856886ea1f744512673cf5c71c53292f0c1607fccfad329bad53c4e9059201db48a8197077bc44bfdadda7b20d129bb17080771466100e4f249b7b4f45d60199ed03ab5328a85baac9ad6b8101f1cc0cc7370b8390a87a01fbabfb660ffaf3b915b4963195e48f0d41c1258d2b82768b349025fd6b93545d647b744cc81d9f6b6b257651cdbc54655e61c8ac8ca8f3f9bdb3c1ea4ffb564aaf646485f33fc39096b861f668ff57e9a552f63380aeb147626c06f4a0decb1bb394b0080658764e69195ea6e7626debfb2d23433b933b5456754a0e2867972f9da15dfcc307187f7e645cb1b40008ad74dc70c47802af57f06550651e8d63fab6731c8a09301ff3ae68c8346ba1686819658d8c1cb3aa24aef9a5a01bb8d3c863f24797273e3af16736bdc0ce5ede1362c9570b5e87b89ac00ca69e48715af1c096db8a6ed2d61aa5e2a48bde01a5f170da0878b2bf161071b52ae2a289c3ea91621cd08b9c4458a0f1d96d7e4a5c4a2b9f05c4317828f329be12fc23cf4ff766b6720586170588d54d1ef560d6d3f2987ba38e840b6e7e75d521149d7f7ece6b7f1e169a0569246d12cc88c1183c94ea3bedf849c73aa110a76845944e7de13bc8ab62a429071a65349db979e67af230a9010b9cc4a7d7df04217ab609278fa983673978d0c242d883c913cb634df08327ae728e9120f4e06f6054a49a806df159d4487d8cd0a3c0d8ae5ad2294d3390724960e95637aeb45dcc8b6014f4fb1e44fc522c5c8348c591905d7a6d925830a3152a5f105071db42d4e350f1ab9f0f7f009b62b0eff2468a7ad03e5d5cdb7a17a58d8cc61b72f461080359391d6ddc1614ab4a879466f2bda90aeaeeb83a3ae2a396633e9e5176446563eae6445260ca33815b72be8120653d9bfb7aa84a6d5436b15818041aa67ad8533488d24772ff8ca3890f824922176af9a2e16e2236e6e75be085a62417812d870d5961b9e8137a285695b589aab5710678b0fa8ba6c51ab9952029308c8c3eb9fe1a31ca68cd7bf4874b382a9953c23adced8bbd06a6e4ee81f1dd0881edcba498c357da4bed07d6c50104c1325c8c5b46513598ae8c1900fc1aeefaa32c81bd9088cc44154c023608089aad020ffd404fd7757adfe7dae61fa81626a72252864e83f853cf052b4748c72a6aa13a8ea75971c69d997ef15f9ad48b0cbefca29a6ab28162c5aed25ae36b88ca56c00458cc49860a3b1ccc32e9d7dc7d4c88212e0784d108d172e1a5c19dc4db3e2953840481068044b5ddc9467492c4f000e9e27396421de5ea8cc2ceb5f7982c793dfc889765e016f1c233f5470bcab715f6aad7493f031a09898501403af3161c24efc8d1206c52c1b05cd62c2f24da06c329019d77e870a753f1b8855f5701b8d207f946e14c8d5b1316454323d4144ff4b32caf40e93477365168bee40c8288b11558f880620cb47c66f2bb1ca3efd8794bfd4eaecf67bd335592128acfdf9a9908a96b465485735e31e3841e0740780e9b0f718822b623f660e7a1492755738590781213664098e89630b5ef8e6fb8eee9bada6c433b64f95c27324534de524b0140bf6da4aeea7230cb6eb0c4c04000d1e79f8c733e67f83f366cb2ff2598b284310b3cd2531192f5e49b59a714e49b008eac3c15c29caaf0fd0304f59c69d34c477b6b19953dc84c0076bf9386f0a632c2bb7766fab10fd19f89ba8632542d20e1b355d9df0b9e0093b25fc674b8ac45a0192c15d579352bd0618913b0d0f03673b8471b6a2dd92adf6858c4bd212c24d2929cb91dbc5b098034bd5ea54af54a70ce72b1316a835d2881310f442db3440ec3d3b35050e142e0f597c4a5ba095a05b073014b6f628e4911fd94a45e19a2b70746136ed9c5e2889482d9fcc79bb1f0d5975902680b3169c94bec1b5081f75d7de1a4d9614d0407c108c9a7c5eb264e365e7efafaaf0fd4c3e57dbf2ac1a100f755b7a40b53a7a5cc309e5335fedb34836cd4a1106510d92ce3aeee3898f0af0cacc37e58a7501dea84fb07a40bc27c0963152cb67287a45e6908a518fa428d46b2c8ee7a9e653910a21814d56bbcb871ea7aa46af808829adb801947353eb27c0e9cacbc97033dc29da0be2d10f02df1eb1db68cc8253fbae8692d1c0fcb72eb879be86127ca35c771fdd7bcd97f193111b41bfba00515d15d4e3399684628d191e268b92a9acfda878610c62996f9e2ad9c934f04783c75642eb3691d4abf907fa9ac179d32e008008198485e24689581b30dcf53f8ab955fd1be885ddcbe9aa5b22b567ed7f0188b7640152fbb03c1d0444204d5513494cbf0f18769af0496155672c74ca48affe63c2190e86ac952118d1a7ad152dab61c485320c2d8754f479b413e815a99729d33413d91bf3f380bfe9507ec3b1f48bc0df5a3886553234413e4b49900a9c2f83dae330f6a26338b53f0e44fc3ad2a1e77c7a55d3d95cbca02e9a207716ffa419c0acaf92e08e2c40ae93e25b05c30ebfa8080028816959cbf2755abcd65dbf68099ef77ce40e0312d44d5091ab326e13c500cb22d1f213067343c2ead63852da80eaa53f5aea3b385adf31149e6eb9e7d49f342b8951206a830fee4917bf4fad94a9bfa809104dea15871b1cc480571cef8b50d86226212448ff9444e6e500230675ae47861f1d0705356756b53cd7a81b055b92d88953c060cb359c439c1f539e3859be135ba95f51f34ef5beff7707e1dc648e6207cc6ccb32f9d450bcf77fa29391a7dfc9e6bc4a80b63f7168ab86283a8c888314a6d55742fe5618a3a731013df5f4bacc15a3834967b7824f74c407b9addee292fbf0953c423239608e7cc11e039cef68018079c6294c745026e33acc0cf40325a9611af285ee51df75b265300ec547b12a4fec47772996faf1c6121dffecc12678fb5f2ff65f451591cf4526bd87fe340de1ed063c8569baac935663ad2cb50ed95d7077d31fd4c21864cd04bcbb6e955c1b12198669fc19ffb729508f019b3036489abd4f54f6da3d7d98dd669c148040064696bbee654e86cbe7eb4741f63ac28c0d4c154d6c3459cf088adaafafa629f05141a00423401014c26f6374d5d52de1a4403a305880693be4fb27f49865c3da4d23adb94685346468629003a38496454907396ddbecc4c35984898d61484800ae11b6e3ed3cd2d4031f82fee88c4905889ecb93ed9a0ebb786934c878f4114417e824dfc30a0211d0d9f4d6566380ebee97f62480d51b73b977e7dbb5f6f733804061c262a50920a167d61306ed0f937ae71c85538e4230a4bba36e486324a8903eb7160b6492ab39b26f052fe47ac213a2e4183a6b52f8884a1eb0d8e870d96d33168ce392736711cdac9e9fef8bd40cc9d613d858be4d8a141af61bd8e603f84dc401a8d5e94888a04dd2c53999073b48fce56b1822030565430153ef6a045abef5f099c34127383c2a98c725ef4a6068106fced04c80269926c22962c8a52aa22c6cc1611ca867f16458b7556ea40147a804a81203d0cc186110004aaf88a393e5dc8b3bf42477d4d5c001b2560be0e3772109bcc3955d0b8bc87dfd0413e1afab71963c70e58ca37cc48e0a33870e7f60d3d1d61584c73a1c9874673ae5806a6e30449fc62d985603a3624ab5eb05549216a2ebc21fbe978caf7a0a90406c71545c649a640b8e3a8202897d2f87802c5b48bb3a25315022b0a0239e2b3bbfa5e9ecb2aa8ca05af415329a9115573c3502ee470a0d251df3ee47a887825ab4a20c6a7a6707cd447904f20f6cbc9f78150fd3bd9b89ec1ad8fc8ba7b94f7ac2c04f7ea132880a5305a54b158bee46cf947febe08f6db01f5a7511d725b9c43ce63735d02a8d51499f67ad0cc2dc1b585a7b12736eb65050023189a9ed6f4a219aecf2118038255d2a0903312238a296929245369eb38844434e73e631b80dd10ec71746791e4a551b7174b82e5b3ea1ed4f22d97a2e68609537051b17c2068c65e114c4cab7953ab6a32e30748a8fc1b9d036dbe52b620ba0a8e7aacad8aaafdd5515a4ee04ab8aad94648638ac30488819c74ce4a6cad15dbba3aa919cc923d5f28b62cc3add3fd9e128d410d7da17784b972e5c73be36a2bc084ec346350b9a8e8d2f1d26fa54f6fc1a407091a55448f0628cd37f1c8d810134be218db72c43853386577b783016089481503e8b63958574da675bcc18c6b5c62513c9e47ad4174e606e7f663757564221c5e0a6ee1fd8128fc325452cfbffe691c0503441b97a28ca4aba56437e29683e3e6a94e0763a1d1a25e88717625781cd3f006fd7926e99da7f767f2deb12fd70f7cc855111d8c798e56060f745ed1b23a642e9cbd558ec77dcca789079da96a59ea75928f67cd8de5c3f1a996678f15d583ef608b2f2325f4c0d32c41ef7b113e8a510c682489125b35167d986c3357576dbad7e4d9d03c4afb2732ff6071069e5ef182b2664db68899e644231301f0460fb6fc54b4032ba0f5d2972f9e55de0c1b98c834845960839f1dfaef5d59233b9ec2a26c02022237de1a249d26488a1200830925dcc9dac3a49e9da1e7ca001c589396e4025d4089d56a1c30606f05178a9b1b5ecf90abb4906020996fce009eff1d0a3a6744ffff8c6caf73205498914e554b976fc3f6626c741de68085f5fd364610578a002067a034e1f8bfafe8df7f5e949690dced44b511c90d1959a607859f9500f7960621ecdd5de66e1e062096f71eafc025dda5196076426b3bb70953ddf4daf49d950e4a01ebcea44b3a1fcca397364716cf6b686648ca5f65b1dc1431a8b6c82fb8c8a41d730f1ad43be872ac064159ccef49a09ce04af7c07b589f80b0cb97b1c587cf44959137db774b826724522bca81d3e178d6421fcf6f222310ee2fcfebd382b8451a71bd5f848fa6365dd84035739d2a134a3fee466b19d36d1292f867fa93dd1b04dec7ddc79086fbbcc4e1e86b11e37570e65c010985727a5c3527453a07842d6c786eb8b333c7eb9239f2bbd731fd9c3ef9db9b98a627d3ba42577e7998522ed8188ac5fdaf7ef3b8d3b90662a008c464d1b3192bed24d57f1fe59339ed129c0531cd6c685002a903d7c107eeefba6a9524770441376b94c5b40fecbf932f86695d8bf0a4dc7350e24b0b5950c3fb71909f8e77938e4893827aeefa4384b4f98e236b22c17dfbf5e948e225ebd342b202b660d1d5138455c3658fd6d1f942b556fddd23b44f1264d84c0505efad12f961d9d876864baac1235c05eb022eb0033060048522217cdc5a1237bd0c2741cb7549109752232b783c7cf6a9f9a7eb3cd71eababf761f97b48428adf476ed82c29168430bc92528400fbecab03580aec4756e730922864f2cb34b0918990ca7c3fac94802c63736a0fc8bf0f3452810695581bd5245e5c08dd794c34016f217627c170f68130eb5dc142c9d6266847e058473fd74d7c84624b377ed153d1a0bd7e827b365ea9045fb00d6160b1c7e3a8a71606b1730e55665ef3046ca61c651d5189715a0d2a2202ed75ae2ae7d44d04f45e8e215ca021d4c0d5783f636e9659a1dbf5424d0f6b7f1abc999714e79b39040bd89f251ec3c9449d78659d408f1a5fc339f8e80b777e872626d018803697089c1659c832c90b7b10423c87be402882cc4068e57ef566e2b9e2deeb448769b31c8a4ca9ba9b708d2f69c7e581add40926bed6885800ac145853f8a3d275d480e69c246f0178d1d4333e60ce259c601e9131c4d86c3413c5a76aa5b9afb36694d5ac5c22bef1f9320fe2ca89075ee7ab605f26d499e13bd10a2dc863ee453245b14f91ee87183bd159667293dcf9a66f23bb82a0cb253300cabe507c445f5dc7b3822c551c36f9e8e3a539998c4005487082d4f57bfc4348242e9e1d576d7dc280cab50176314fa1d3d707a896d864050a185241645690499826c965bb62fc7323fd12af1f6202e34ccd44d836be93cf1656021c57b71db3f9987be3b0294284eadfa29755c02064de85c272063b720ac267fdc72143df5960098884634ef5f991aa674bbc092262fd850371727416539533e8e8c24138f1104c7d89d1aa5d2f114ad391daf342eed5d3f3e6e59d49038e78572421bfe1f77291b1e3f5043c1e9cd154522c1da14ce424a9ac20ee2b0d964d16bbeeab0c85c6f7415aac39e869800eafd19901431ebb8651947881e8b64270a9cee6f2558c4b1cea437cbc9703d7dc090f7f298c84107ddf3ece85740bf91360767da4a321a3553a3e3e062988f344d50d28980458f402833e9d73d1971bf879ecedb6dc7da85432bfbb30813944e594a9d8b1070935ba5860342dfd3974d9edf1e9e2c3974ad642972283fedfc430854fc49865fe62efb1eb02d9efcbf2c9a30c698a3ffc7dcd1e24c01a0168d719d6038a19909a2e38d60968e0dbfe8c8882bed66b59cda6b9376696e9a64c96ec13dc5fc10eecb45d6c1b38f3afd3b64f26e22d60eff0f57b09c1b593618a7a4be0ff30107256548a77061d556d271a0b8ba58ed055c76eed12f5a21e151bf4cae9a3cfaa84b39d76562e053aba7617548165d044158ba0b423f035523451f8266c4fa8f6f0715337b11af7f57ca253e7f6b163f9e287652e2b0cd8896dcecf4b69964f50d4295e13fc181b19423c89cbdf8d0bd36385624f0ea4098b039c1ced49282ddf20fb5cd6a85f0d1005030a340691c73dd2100c4d9a8700aae98d16b9388a64c6eedc926e013ffd40e2be52c652b38c526903954902f3d004d45d31cd27bc9e4a33d2abb5bf83f2459e43c596a0e4c365dbb35b6e7974c9b7017a1a9f48e192a706e8ad5d12a6d422a3e7b7c053a60946eae3c31d59ff3c0dc51a0606a7b99c04869f0fe9c1eec341238ba605e244678054226a1972a27f6222d9505c6ea3fa9b8578e06b71819c6f5ced8d06d91ce026cbfb4944cd02a83321951494857bdc1db7f3e4d4378ef98927c1a40ad95d411264534b7d3c78350a8a3e6905f834980ac5124d2d080b4fd5ecea30ec52ca65e177bb7283f89c929ba2a91f1c73121ce72460ec422e28102a99173b7caca3a2269453b7f7eb482a7750799459720d30004c40b33a394b6767ec35a83495da8604e4a2d2acaa81a1c27bd3f4eea7821d5a452443fa93064d10a3fe10ecc222bc50e6318a616495d7214da6df3b9b74bfb194c396ab5a48b6b4895eb52ce0e851ffae63e14932cbd7e1d1e6c8a61ce6db2181a19961d3158030b7fd0eb55e97bd85c441fc6fe8badb23d60f08a0ef9534e46409a59a19a859acd8bb395ea9578d6fb3ce97a536e0023e9a25168611fba276034c57bf81fb64639b0ce6c5febaea97f0c6bfbd202f457e9ab5af9f59125dee72805e38587b8eb67a3df5c3a5e8e3d75d5ea62beca0222d796509b9fe812dcd54457c68402cf3a255358ea9e23573c8edf7b4616ee146e42ba717299d504ca0ccf26ac56d7e239db9c00dd96e295735a6b0919b4ce6a9d3b89fe25d1e7e56e6b89bd5d55642ce11a85e9b8debf7bb782e969d604259bc1cbe3e24182a34432807660f951ea023c8f2925fd6ae9b00b80f1b4ad49a180e33e641e4100b3b2ff75b5643894225886362a91fcd3a9d5cbc1332991ccbd73f76847625da3f848f1c9d90cbe78248a83cd1bf280e63ae0811aec9b3dcf2aee6bc3b35ce0186d222807bb811ba6438f1ce3268e640dd540c9962b545da443cd48e8ac887e5ae4dbb9ae7ac22636ab7a94076f1a5cd145a310e6ef5a4df8788cfa4206954714517ae9554615cf840b229fb0167dcfb4ee877906b5f57dea253275c71d9d6d7a59bb182fd492fc3814c50b0b5addce4604e2c18d1968de82d5bca7c366aa66f050ef760c7f8ed8285890bdf9ca9c6de78fb26c41f74713e9a01ec9debf1bb42faac41b6dac762b66dd3c0c028e8e4009d7e91f16ca77e2452b045ba8fcd30328ddb2a38848b0fe3065be2271291967990621898fda99f73db0d59cc3da722a91330b841405fa827558d4cde5912a892b6d0170cfc50a4b917fff10d9f769dfaae263e144936f1fec1d6ffb88e72df1cdd019d4ef2cfba49c688541a9c67839d682e9241981e46602a23dce25b16ba883f4686a2fc8418b3021e047514e0e3d21b00408e34c327054187ffd5abcdaa350fd2ac3c7e31e8c059a84215a190f1b22c7a26f5de481f084ad3f8777ce016632d8827865d2c888646e5585e349f10cacc706d654540b90548e1a04bfcaa4b5318684ea0f5ea5c8f55789a42a2cb4f64654e460cdd91dd5ec9615912dde9fde34bb50df329c390305b8557e2d7f4ed311450f6d226c1dbbf2e16e38c3fb0bc9d4c517eaeb1c889522d798963c038df42fc6f77cddf8887721332b4a7735ed2fd2f2d1a9a50be27bba4af8898e16f4248a5892a43acbd16ea85f456faba1b7e659fde74d55c5592e7666f162ec2e8b6fd26b4348312348f446ba28f4b94060d303d403bdd31e371f55dbace651f4f7c34e7224efd263b50eee80cacf73cb5d69c13a0e0d9a98593167b9c9ac68a1bc48e3cc5c02d780c3ce0c9978dbe8f55904796b019cb958062717379ba3c5a761fdc3f27ee86d9dfdff0e8c3d24d84eac00af6dd2f74d048e9af496b0aa4a6876b29a7493069df08558e9414bb2231126d2aa73289b39a4579aeac0362b983a38ae2e98081161f6cfa0d1eab44419a6a878f11fe5e3ca5d4f3693f8411638c6aa66520f6e2e441a66070302dc6c39c653d0f391e72518f07f5798955ccb322daaa433742548c28c297ab40bfa28fe2b66ff74a79e6c98cd29cfd65744a0c57d7a32ba4d44df7ab60d8cdba3772d56d7ccab5d2b8da0f228c2e6d6e5a2171879e2e5001bfedc1dbdaae2e057f4e1a0ae3024dc5309958f58178914299baadf7e06fa7fbeffbca11de7718a53b8c3bde4da4f6dd4fb8f9315e94e4dea31471e4cc5f312323a44a0e9bd375b7657ae4683dd936925003c8130aa65a0e5747e06f7cb1a82f92634166d873e692c5dde791b91d603e961dc3f80164234d27de92ad1576f05a971b1bbd51224c492f240ac882b34101a468179827c2023881457334d191883886cc6e44caaa7a5e9ae014a09bd45a40ede122442588939d7555e1c4e32c6b5b7ac4a386440ea4d2395b1a4144f88bd7d59fe9d18ccb44a97eaa81a8bb2df7230cd888d97147cbaadaceed15bc04be58fa84361e3e9b807b6597c700ff99a58f5ddc6a016f3df8f8caf0b551d64ec993cb86d515970a511b22520e4be725d16cb747416abfe4a0675b368569db78a3f412ba3aaf098a5367543ccd335a71e0618636f6d910dc7d0e14c72e9178fb93c63b24a88e978c086baca9e80002b0191b5fe763e6c597b9526a4904ed80bc658d5b7dec2f28a777be78f1fef8d6f291c287cf78c18702776ffa6f8ad52a389fd085f6d3d591e8c1a5464e6982cd881c1d002956a9f8dcf655515d9f80021f16eba98432cf0d57fae331b42f006e07b4680bfc572778f26c02e76813df5c8a0e9f6e6d5948d7a0525b6d63f7e99a964c46195fc0480bc5415d6d0bb8f4930b69269c4070261d97b8cfa74775dac36d006211e134b44c5e8eb464830922315339593e31b2f6a8dcbdf92c3ec6999ee91fc2169324667ca72a7ef1fc59ed238a89945a0c35a6ef0077787c783fa2839c71e698bdff419cd05a65388138fdb6afe3b9ce39a9748957053b2c93ee487976ba3c84813a08a4f227d01db6400198599c5ef6a20080b255a0dfa993f852a1791029aa6aee9a6ddf36e4de87920ca457bc6e1433a2f0c8fca39d12fa16d690a2c0785158ffe80ac9321764e621a3f3f91f658119818110b8e3d08d45d75793da55c721d4e603d5ba3be6d2a3f2853f0d9ba282bf1c9ea9dc9c2c46350d4c9145c43a6fe1d75184b15a782bfab590c155f34f87a4b4968da0386f9d3e132d28f9411bf087764b9e858d79e11a0d582ba1763d88d93aa6e08dfd6d1bf43aee2a8f7026a1bd47a6f192bb09616a4cdad9224eb2d0b95459618fc8880369299522ea84c93b9ce9d782ffca24a609a34b6ac368926d0198dd820367eef3c266a07123c08764ba1060cbb016e7264fe1579331909247630480eb8b6e8e1753e6e72c73d6646dc6fde6dcbb1063a588c9e45356f338a00aa12513829095c833ad5eac9a30ead198537e20cfdcff4a8d854a95540d8f358aca745da52c7d7a5307d8a5c385b08e5729fe966e08c5f185b03dfa36074c69b01f909381b24047f818893aa3f2c7726e04e93ba15de26c222fcf4d7e36f428200373697ec12e60808da3c34c5e800c140011aca0140605832a805e4fc750f3cfd0006e801403e885aac7c43a8035ad0074e74158523a56b5c79b352579b30af2454034aa5a4565accb9c6846277dbe79e10387b04183e9334df460a544737c6afd51e6f69d55f947243cbfbe54cd6a1992aa4cb2019e9de65ad0d6d6a3aca30e9d17b64e789c728e333e5ea9af06c1bec91928f1e2b55c93f2ca0be70226cc96499109afd10e0016e75555b21b55564f05dc9b6f3b7f70078ca5f60eab8f81093abe01c561ba724387e13f2e6633b0e130b89604ab996f951dacbb62c32cbd39c745546122517a17517d0bc9db71ca5276b664acf62eeb7e9b8c6e579dff9794572ef335a32268c3835ca5d733bb4d039d0257d259c91a45b8fdbd30a4f05d3242c792ac5c540ac9dc1e55a6e1b16caf5839ca246bd8c7ec6a36cc597d2b07382b22feabbcb729d47fb3000c2ec9eebaccc86f00694a28d4674f94ee8fea2387a442ec5f133872ad05f58aa058ab47473a23ba14152ddca028a9d7d422f509135c11e1e1f024c9e309f0b879168e7e3e4566b404939631775c12053402afe5dae5ef9c145c7888d9589a5b832d191f9c903f1e788935a78b3db231b61b9cc77163950f18de70ce430c1d52b2265ff304b9c344b00f1e1e4a6971be8a7147b0353b3d906795921a7b96dc6a6bdb7ecb248f341a7e8383a00ea9e55fd129bae9c043553abc7804fd37f11da45359762a0227cf5f6d5a472b46aaafe299c1bd7b4316f0138b99b46a5d6627dcec9559db5c863ef0dd122f2f161c20c343ec42ab761d96e08eee462d082576acb4a629b1e34c2ee250eb9059dc3e5952ff9825f53a737cacb65eb3ff7a6c4af2289ebe4327679ed145bbdc1fc0bcd2bc50a459839ba1c0f846182684ef355ff2a231fac6d58ff3b7d1954e5e76c5d95281480c1d698f8de5119b120f05dd592b32c47640dcf8dc16ae587c6d7066f1acd830f12b9f053789e23d9e35bca066be77cab46ab767818d33fb5ccdf0da4fcb0aa6323f6778f2d2c130cc14228d38988fd1d2622749d6391c92c5e635ccc026882fee75f7e8029495e7bee9d702895f8d01d78ed41735d7a26ffcc1341f2f8252218099370f7fe5008b647757477a332560c26444f3d8e86a7651446d10f80a73d8b3ecbb8c59e09d9ded62c3fd2cebd45f2465d3026002e7b3f0da52527b8d701837b63f44499f4bde2184c0b50349d9f969454f122a8f1faa82ac8e9463c4c388b853952efe74422dc2322a4123b04f7c1981eecea0e7634a3049255c819f8c37c67843b1c5aa621e12c9a795b8d2772702c6861c8867f62b3bd7943f1616b654ad8c755ee2a6fe71bb9c0958016392f9dabffc41a57ba9063c0cb2287470c4807aa751ac2efcb4f73287a6a435a6f5c2962eeb75634265ed64960f4d55364950fdc4c896179c1feb7588236f51684b4e45ec26ea84285a2cbe3682fa2acfb13995d5c457442c2cb629fd35137d6fe25585a65e475a63dbe9e49e102ca38fbf066d921f55afccf8c017965a0afe59061103601411cb249e7316d0fa126400e6c8201b81b4c9275f96b27a580dc3b28e5cfca28f81986118d57d0c054734158361dac72425612831a6072a9e604cb5bdc051c520d294740679bbd15a6930a3d9efa02049663c8338ad743a4b1b16d051283ef9ab22d5c9456a0bf608491a4a37d35d461309406cc324ef5b9e9816a4cf0d0722dfb44a5d965996447c59f275d943672145a9ed5a224a2369b0b19a555cc8f9546b50f0bc54cd7edff8aff96caf052705526142d7c168b64e385cc9639021d6863c9c344957c33304062c5a6b3944ead6096ba019a4533c96d9cda99a0992c49797ef373d97a09b43b0f5af0aa165a1ade6dbb4254b0f986eb606db5828dc6fbbed0670f0947466bd3373c0406d58cd8030cb29950743b0603084e3dc82de3c6275befea09063b4379cc68497174f0b4d63bc8de6b0a650389761ee28a1ac6658ccebbcbe33695100203cef747afdb5636fecbf09c6465c3efc0bf71b7b64d8e214220ad21b1630d7c4884870a25b4adb24434a50aedc8e012feacc4d2b122ab3163ddf44a4f6d8e742db741aa879fe36d2f302dae923d600cb82917fbbf1e42b4ee1803688d72cbec56628366b7b8d548c2e42800f956696824af215a287be10260a82b605ee241e164c760e5c713266d05a3b3a145d38c9c7eb7466a2e191dc003669509bd110ef1a586a6aa26c4b40a0f3602f84ecb360d215a74483eec44f3cb43936d13298b6f00fa9df7809e94350a525ef1d1018bfa094737ae13cf474a0dad5ca903903391bdcc2539376d5351f46bbaf4def712ddda897a60a201293329c35ed95227e7d0ed758d1ce4675d091a1fa9b10b2fc7f9bcf11830733bc11f159109a0ab42a687959f05fbdd403d277f14de9c3c1bdb862a1e10555dc3a44718451fba468aac3fffd5a0532d0d13431848d46bd97aa184abd5f5cb5aa51ede27b0cad16110cae6c3a5272da2d73628c84845de06c11a8b4d696f94aa90f7c17bc7b1c65e1e1c9660d2ab05fdd2b87ddc2574ee1cf0a50935d1824fc06f689d53293a532e4e06d94ec2ba034e6f4a4065f693c19650b4d0f53a8ee303b634d4acc1610e86fd6b644b54593a1fd8befbc682d6b41eadc017885b199cfa447240b1c9b2c4080f1a8040d31d002b5c54d41e3dcb50c397a070f495dddfc3e11d17d292e85c5d24341a209a8cefe4f5073a045c7e6bee62e50ee57de597cc15ee59c1f15700ca672de03c88b6fbc680f6baaaf5aa10c51675913b48b4e5e6f963bb39fa7af33f7b1383cae97467077d6e7c6bc52e1eab0ee43316615ac4c7c87a0ae61cebac20c9c8af501f169c712a00edd16e5d628574bee056c394e77d43c89819195789b4c050a890148c5acc4ccff6f942a7fadb3bfb0c6751883173cb8e6113ae4448085ad6e4151f38d294a00d857b59d0fdb1ca06c420226d85a489eae68f9c90362d29bfcda784725a0c31e0b032fd77cfef4f1c2df19f5e89b90cd86ada1c7c94ef66ec97bb0a8343dce6bbed489738cf0b103300d57533392f52d609d48e2dfb24dc74feab723082415528e9eb9e689f860039b8c0beb068561c20f0fdf886303f77e1f7606952531ba8b2a34a9cdf29a21b040ce0d53d565ae69e5515fbf95a684a3e229a3db52ef1232dafd0301679790ee07b2953b8fe300015a470016e374b9278242f39cb18c495f6572bd3b73924e1f60ff03a3c3217f68bad4a15e17400032f03843736baa8d3f09fe19cf1c3ff5cd77cc739abb1d608a63fef7145411e6624c894d829a4dad2d10c58c5ae1ccc3fadaf376943682700cccb576120a4b85fe049b04a719d6dfae9fba036db4f82a7d8b62dc63a5a639d84a643d0394cd5b08bac01a5e0c1f4300b2ce2bb8548dcdce7b6daa4aeb37cfec8c09389293e386d91eb2668a54e16c1d7dc7c1b1809e2c0cc8b2e9d0f1f7bea4fc49b4082a426e8ad75a7c9bfa8434455556cdb5a71b3c0773cf89cefb5910aa73003888bf258437d9d4b38279499d7e066395a37f7526c4cd8bc5b78201228fbc40b854ec52f9e26791016363639e1ca8188e3685657ce6da55e0de70b2303bffbaa313230b4193b98bd8943d56f887a58e6bb59bca997783ac30f39035f0108c7e8eac66c6853d06b5f506b5a17490cc091060bbbe7f2225083a0b4ff0fc88be91bcc8bb1413339b4aa46a1596455fbc5888c98fa982e22b581c564269824555258f95d12806ad1ec1d69558ba2e58c4f552afe4440f1cee1313f3e1bbcc602f6d0501699294a7a97f5e4842091cf5343d0be14dc609a61f357ce4f656751057301ae899f10ca301d38f32a2143280269874e9e837f3d51a3646d83570774b7001ce8e1a714bec50ed97b2a887399ac4f8507170a2acbad935b56cc5104d507810e905bcf99fc08270d29bc8ac7f9b7201d77efc21c6945525ad9a5de007f5972c9afa83a12a013e4040550eaea15c628e94f8124c8f75004ca411845b42d60e40e968fd044d96bb658aa2d2d5905d75669d244f78787d5b14a2ed93f8ca9c60038c0ed6d7e230c0905e5afba66b0057dfa2f1bd72a0b1f5a7a72bd8c53a9acc6494fecf02c5c29193d9dff781a54fa174cf4a13afe8a99141e9a74061b019e2554375717dbd39125ffba1921c939cfceb7d8f13cc78d92ed84f32469da601f15ea83f5ef1f965816386948f266bc67cd73f1f818a3bf504c17596be81819b494e85d8a56cef71069cbd4a2c5f03d404a5df76253b20948a11c014093fc30bf3d6c7e243ba048ba8d1e692e595517109a5fc365be5666aec36f09faa915187558d784b56147bdb0e2d1c9f12f49eb57878d064de61304b4a7184da8d0fc13fc53f1ecb205c1acd35e9faa57026602fe8195c4ff991b62002b06f9110218d0b98abdd14a58d0183eeea041902066fbd2ff1aeaae7ad2b98301f05e4daed2a32e3387054983a669a3c98e39c802647227634f5f949451b7da599925efa851b5555cd09f083daaf5eed4fcc2431b78de1d6955e1bde2622d24766feb788f3478e82996dd851160beb214b4b24a1051b3dfe817588da275121286662fee488cd3e27d20273411d097983333c64af05fee29e61ac59750c0a9cd50e2be0be8e5eab966ec67a4d279e8b522d541e2ef4918801fc6279fe1d00a573e7a985624829486f383ef16dcf7ff191119f8c7b5b0adc42b832890df019fc5e180e24b386290e94bc5633a49d9611bf2960ea0245ff64eb6bc5c24cf2557dbfb7b7bad3689d8b1ee236a59a0b4b9a867f263d1226b6210cfacf662695aa2f2cc5b4130acd0efc1399295ae1c52b2684d107695baeeb94463fdb442aeb4f67d2fa533bca4a957e25d0cc8fc4884471ddfdd4dfa4fa1691b21aab68e67c825d0a044a51207da8a329c95f77146bd5d7e010148c1a509c158851bc73b1bda92696e6a032c766ea33e0f0c6b9825ab3012fa54395b2cebb684cee2d305eb7bae1c9865d1e6d50257adf1986d074479e9653ca99b3beca8e4e13eba39a12bebce2642e96d97d6b42636de2a4bf006c627f3fde03e4fc4e9a8cbfeeac14ecdd4eb7dadbdfc6404f031b2f247f75667eecaac9da189aba8c3ab2929908c5635e88593a8bf01d0ebaa5bb2676d4c1e359f0b6a2a119327c39ae2e7030499625c446e312787f3808075c254b18b0c79ce95925e7ee8c395db2093975d1c985c48cab7bf43871736bba6783d52e12c8b2fb3739347fed73d74709ee11d18cf909a29eb7fb7fa33a81b64fb2f0201fe4c73215c262cca24e05d6026bae3a3ddb74536e6316d78d080baea1d0dc6c6578d8bdc9a477058f5258380bcad25163aea436917a5c6aa25d6563ed87b2b2246b20a7a83b484e43cadcadc037f69fc555b1571588fbe9799a43d2ff3a27be6621dd9a9c36be28ba4535ae26033f01caaf699a669541047f5b800d3f466c6894dc0a30ff214cb269079b795db8e10d9f6400ed09f58baca8e133e9597596f2b63358b03517c5731fcdf4847fac2b81bae52702dc0cd528cb952cd27543366979a728708f221e9aa29890815018ed7c54ca30c86b58ceafd4271f90c55960c64860ca6f32ea8b54e3c668955228f0d351eb899a388e69100a9c594e4c3b304a38f2c915e16d8a69c1b8820bb8d010df28c6b454288da0e00951354ab75d896769a41c6df4390918472c8f15207a8b8f34419d03161a7f339ec7d8ad5620806eae40d17ece0deee4ae0560de0ad9af58f222999a3c2394434c6bbf8437fabae965434589b04d9f4fa303533c7218b2c5906f79b63957f5334f635157472d5a3c5e866966e52ea0e7461a034693589a1121e486354c59d06d435e2bf3cca32a3054ee435f800a7bc5f91680e5099f911be875224b9b4d3ac5b580bc7b5a00e2d5c77c6a3d256e9905702d7b7535b0d0f50af053f478ab6e062262ad80221cfa8c976eebdc80fb0174c4be0efc22fffe2a8002e47592f854404d50f389bcbf4a275b34269fb1a7ae191b68d59fa2d6b0067b48e27653af8cd51140a93af60d2dbc6f7c681e02a183677246b338b4dd16e31e12f431e34ddb229debbb28401336ea4ba56576c0def1143375f9d8ac2c81d4da3f157c2c67c1cbf5b59c15b17c5971a381e0cf2bcbdd72d0211a55d7ffe4e5b5cfde58330f97099b7851c897c3d241a2f0b4a7d68335d0b960cc8659592ff36404ec68e8a335aa1f648baacdfc799a81291713e025b7ad708b40e192ab6d12fdc7da4bfd17c5b707f8f67e352280995a332a6a8a1908d952f2c6e5394f2c48c9d4139f8b43d174f1ea331055ef6e8abfce304d26a0849ddd782cc69bbae8fb75c8a677cf06bc33ab4881ab7e101bbd198525112f796e070d9e453c645509bf2e5f44b385a471f79150fdb08007801c7e71ef66573694173b4f8e6a8a4f1670059a868566f2a2aca415e4b5d47751d89434593a5ca6e46ca02ba80934ff63d1ebc089dd4c5c235861b95ff3266622e986621d92f86211ad1bcac0b492757037db44c7eddb4fd2dbc56e98cd2c8263b042b7c7f89d73166912ced14431dde74333e03a716bbdc77b7dcc19ea1165ebed23e609f52548589176cab224ed31853a73a6b59299259612735f4c2593a9e02b2e32edbd5da1997ff10fd5b853d2f0c8dea91e91dd45c46af85358273e064ab53ad1ec761d42388fb2f86f0d22ec3530b70993839af385c1d253303ddd55cf4f6ee8b32b923b91b11129bbc8db88dc9d486c047146dc4b418f2d91ed8bc4dec86c4462532477223862b440dfbe48ec8dcc4624364572278223c601f4ed8bc4dec86c44625390274610376c896c5f24f6463862342b84e9141c0e21ee49ec7d94c594048c277381af9d3347db3d0b9d3bdfe2dcb54f9e0e12dc5cc283febfb1833960077a400773400e28a1af833b30077a600e74000bfa76b007763007e74006fa3ab88372b00719a0d7813978074edde8400e7a568258d64c5e9ba778dca03313a4dfb379eb616c8325a50edb0583846c2f87d25747cc023643889c08cf0e8c57c0750fc621e4b9c3e088f1f621f80630ce7ee77c93c5f160c08a9e796ec9a910a37ec9e1ddba9037c3dbdc91d9083f41c4aa9fcf5576633efbfd1098363215e12340530e4b61e0ad9cf543047d37a95e3ee4e9b640f6100752e34c669d43a5b0fb5552f99ef040882e980df59bac7809ff17f3f2b20a4000c2018a640dfa958e845cded8c5484ea4f3021aee11655c304ec29a0ebb16ff5f56e63fc39299a387f274685b5747e9998670c5a3e276d1fab7b438334b7c4703219e76fa2972d77d0210200ee70f898e8ccda6e20660d00fe4e1921dac7c74bddad86b88785eaa4f5eba91ddd6e49652262953210632061d06fdb5824e443a4be0fe19c5d3b7ef6f41f9f641e8e7427026d64c5915aa75babbbbbbbb573add4be0bedf8225acfeda97ef00eefbadf4e0be4359fd0ec225d897a1acd250c7095c448f6ab05539c1734fdf8932eb57e1f8df77efeab93036c56fc4ab9a1b08f797d5ad56ab5508649432b34020abefdcc9955c8140ee4b5b1d774199989ab1c0c8d9144539251debd397b4863fa7fc7ffa1e5271aab1d17777f76a694c61cfe397a9f59772eff7b9cffc09e23e3502edf341134df588b56654da6c4b3a09030ca6ef9f05729f2ba1f55d09567ab4e6dbb00f07129a370cfe04b542d990f7b21edd8f9fc8854084d451def139e867fd0565b3c0b148e67c3c608a9e573be8fbbe1e677486c0fde3e598743df33b438771963b1d7d45f002350359bdf70d5ea06618abd56370c5fa156bf5304020dd7b1f7b0cc6bef3c0f1b3f7de77fbc87ec4d9c30165600cb09b56d37ff32f4020fd37df7dffe23be9dd84ae1f85d4fc8563ff05647ef117dc802f2e009bc78bf1337e11874d387e1f2c1c31ae09c79bb1ab7b0c76ff02473a0aa9b9c18ed5a2114cbd876f5e14a664528434abe0227f162893c910842e6ca7c958a1e8404e9b5ebde16adff5458256a1acb6be7b1908adefbe15cab81f321bca3e60b158cf0a89b4c22061b5d5864fb97a4340402ec1b1a546cb932b8f8c7bef57cf02651f78bf7a191782e085b2fbdebf0c84ee59df85b21bbee779def845825814e0c9351c87b20d45e0c9228e2c6528abe1883f4976b32002e8c071939249344cc2b7055644206bb596569f866ec36e77177ba7c7eaf5ed7b7d20d6ddbd3a457266caab4ba8211f3f3f4046902ebd0dc3a55488236817da210a09edb00109dd7bafd00e1bd00e2120a1eeee242f58c1e433fac83cfaf8740d8a2874ce393fdb6f5b5c619e206990d2d5246a02b289071ca8a5943ba6d8dddddd504050edfdeeeebedd7d47af88fb5a6badd5defbddffa5e0829ce5ac550117128b7c09b0c0e8a0f9a0061410e13142aa68020cf8cc10170ca25104a40481254788eeeef6ee6e77f791b63de2414a51d7334969eca3b4f114297d9cd94341b109cb6fe2ab8f4c04b68d93674875825841fbd0fd72c9a5c2fe4da5ec6944e2f81eb07c9d234ab822822daa18d992e52badb51e81fb2796fecce04b7590c04d44e50ac745342cd16cafce83bd78f1e2851be39aae69ef1d9c9828048e6151083c5251082c25acc5a3c39faec50ee7474a29a5a45f8cf360bc3e0ca326064c668363e3c168b61737341b0d990c47c60c0c30902163c68c7f100c431a22558d1a39399e939393e3c178f93166c81e07031a349c068d49e31f04431a3466b31a357268ccc290068d598d1a39393a3a62ac06cf94ed60071383355a1dcc3047cea494f646bd7183deb871c3a7bc71e346e3d0abd1ea60e6e8481d9d594e0d9d1c9196a3a3238a349a0d1b376ee010451acd868d1b3770e0d8d901000032c8c0c6ce8d9d1c36b4ec0020c7cb86961c4a3aa9a79c12f468bf77777bd7eec7eedf7d710b4dd1774cd11fc70a1e3f1ee9ffe1cc1ed95234894e7217934b9dc0020c467460a2a10586063d1c85fb21e04df40d86c0f3e3470837a21f1863fc23841b51083f887ecc9e66ba7ed16c9266833de549e94b29a5940f8a2066e9af19ad5556f938536caf75d2ee0027cb6e3a83e8054b3af1121eb10d2db80d8b378c2de30177e8ddddddddedfdb53bc9086c69cea8702b8e4dd1bbbdc391e6af1d6856b025164f54ae3ced18b66fc3d8873f8c71ff4432d583ce2f46ef14bf89c7eaa4a1d71d5ef095058fd78176080109f51fe0bbbbdb027173ad82fdbf3c5ffe6cf759ff87aac5b556efea6e6b777717b100a54badb6fa6be97d3a1660a48c94785a2109c7ca51982954981005942da630924221469a16858476d88084eebd5768870d6887107520a109f401fa839254ea26f5bb25d0cc81c2f5c11607388f3064d22a26e48a850b172cb9b61034e2c8d589cb0f4ea79c5f99fc487daad5567f0050d299a59a2c49b43815b8a5726729a2514a89347be495575e79a54492455622cd1f21c8a22237534d801483852468985c292b30c6104043dc0619e22e648a1e44c810ea4186b8d00e3342660f111ff22165c449c6f18f05588468e244c9e8367ddec250d5b085094f5c61540413275390202751c28aaf50e555070f0108103bdcd356c01308eac77d866a4b280c76fdaba97e701f1e09711f36c141dca73ef7370c57c04342ee3384b6824c917bcc0496794afbf87bd8c7a1ec3efe0eea515f76c32198100c0ce234ee6bc0214ee38aa4f0d001a6c87d60dff53d2a0f29f82f781d3b8d8b31041e22648a9c58059ebfe2be1f7c47f17efb0469eeeffde9da7c8ebb1cc75323c54dd6130732cf8ff104cbef07f70659f68c2c7bf68c6f83194d93dfed44963f6e90e543a06bfd72ce88cdc05e249554d22cc0fc955d8c4081a4592bc953840450595258e2141585094e06314078d4bedd71cbf6df4f2055d443cfca4a1d5c8b07f582c06d63e20825d6978a3411c116685c50c10e25d8d2c59115a44a290dc2be8ea420fbd65a2bf327f34783a0ec7ff6dfbe689f67f6d8d79124c8fed0d753467145e9b88c6b92ba86c30f60afc918f88d20bb502186d35698b1691c6545172e58129b1a28a46c9ce03045302baf9798a41a2d2f179924ece5a635e6e97585181b6f0c92eb0a2c621d99aa169217ac32549e139b555177a575a1686295c0c53de1b40a22669f90ba58dc62950c1487c5153774cccd6691e4290b279803314f59cce0e220cc2d0b2d2f0eca30cd4f8baadb6a6200c40565badc682153042c0c8ee58e2223c6460c66e58e2293854d127615d92029b3248fdf4213581afcca1d55e626aa4c962db8953baa0c0f0aa89a8a82428a4c73475521ddba9e8db1a24cdf8adb99a63af3823d562b77ad0e5ab9d2f7419f3235149906711afd5995499054363953d75699be3fe9da741ff9f46d98b04c9152ea32b468ebc73623ab4cd1690c6ef916d7d377a6d9e33d7d9761f6744fdf9bbc8a3bcd9efb3d4fb3474bd7ea535738360a32fdd17768f16875156cbd7eb4effa1638b36c79e0fa5728bb9e025ca1ccf532bf7e456bec6b8b070e31c699227dd710497cc93574261a055faf08ecdffa7e31e378e56e555776a74ce9b77e74261ead90678a94e7a70309000a4db17b504aa933fda44f6301fb8f35565455574c30fd4b4bc29d3fcf7fa069c6044dbbbd776ae5b8afbbbb1ba445b84fee971183ec496a31c1239d03c8fd382dd474e47efdead6802f9c3bf157980aa006c07a8f712d83dc995ad0677e2981c75bd37a8141b599f362d430c197e72747eeb7a184c7bb0280545203058f94e5e53081479abd761f2bf2478c6d1d4ffd70bc8bb10de74e7a5caea037c58f61d3e261a5add4e7bd1c05a5b042efc3f9cea0eb1b3c53a2d4437d7865bb562b48ddab38e63e533c52e2aa77e44d2c765b3cb42617cd4c0c255e228803f67478300d3fcbdd49fc55eb2b4903eaa1ee9dc9301ca915305a3cec8d95b1b7bfc40c2578a417cca07219b99141a3083cde4f26050c9c1832590ca88f0c1e29ce0e18d8644421c3a7d837b9df7332e382aab0cbbfb85d2df078410f074beb1ea59e5d795ac71ec655826ebf135b3cac8ecc553695df8fc1340e0ab3160ba60f8e4d1c1f1df933028f14e7e878f7a382c72bff66670e285dcf3ab1084cbf1371665e3027a5a6f8611fc0fdd03a806bb464fb355a66cfd8531c4880295aeac376dc6cc602671df8ac27016709fee1535deb2ed9be8faedd6cbf015df3b74fe43ef3edca5acb711c178eadef3c90678af65b208e29da6795c1dedb560b87177ae0f8dc772f5b1c70bffa9ee20a01afdcadc04f47efd8e74057feecdf6bad7d4772c3b1afa36b9ded2c5b1cd9e61ebd23e3de87ec09afc2074cd1fe184c6301d35f5dd0b3f6db3e6641d9a8776df5db3b14e72a0a961a884695024d10b7f80929e92a2ad0b515641acefe7d483ab26285d3f7c3f37d3c53098f7b0f1ebe20e87a36f23c0e79a9f07c3aa77fbb3f9593674ad7b38a9bd601ce230ca154babbbbbbbbbbb432a89dba9e59e1218562a77d614c8c925302f3bb0c756de9b2a5ec9772ce3927889faca0136554a5db8a9c53b44148760bd97010217f0b22640811a21fb5217fbb8eff830c2142747bbf17e31ebfdfe50bf962379c4f804965180c6a8a22ccd6b6de99b2e9367b7e721709eaefef88b0586191a0fa78093ce4b409b3c16cf3615d60505316c9a4ee596fbb26fba07bd63ba09b72c02abc321d30172ac1fd30295a465e1d2646a925cb975a88aa508ff68d52e98214d18fa62c5f8420529a90252d925d56c93286ec923b8f5f500c293c431cea2cd144670199e5fb038846e9344a2c988a87bd45a069d95b8bae24049dc25286919b88148a322bf77ff77f5283190ac5e89a0c0bd03bf6795a5ec7fab1c922a7d9ffdcc7150a39cd7eeb5e0fe3eefb58ff571ce2b41b6834493fa3f0060dae4fbfefd6d0ea8126d4945c41c1ce4202cf4f9ab3ae309f070df6cfc0044c9f47eda7997e064630a5e32a8ff4b9ef2194722b37114260c9ddf2401ee5910b6f08444a06e4b72d5b2ab03cca9d474b8403948a0b087808b2325fe60107448a0656dfb68c6465840df23c6265840d3210db6abe4c9a513aa2ae5dcae3fa86c2fff2f6f68f492a257db2085751f1c4f4118a406b2082cc31bf9fb4585f14e244318a12f289132f8da04ee06286d0627d21f7f92102c7fd388d389026fbff943724ea304562c0e050d3eabfc01b135075f2409ece9b567f05e2cc3394bdd65b6fbd40da0c375758b80f51011257a2a882c31332222042cc8c0823b81fbe1c79713e1cc9c2b91fbc24558ec3b1af05ab1bcf8d2ca7e538cebeb801b77247a191420ab6b9a3d08c3902051f65e74cfc8163715c149a1d32c771ff22cc72fff3fc4c44e38446863c3e8f1da29b5f6df26787328ae3be738e9364acb5b68694caa4200fffae88a554a1ff8c42e6596daca632583ea534744a8be6135dcf469c1a27a755fa3defed6468a1f4468937457a832443870b769e7386183c7a33c8e011c674a364d2ea0d52538e655bd435efe5df7a1b6bb8186529eb012332e5ea6b56f87bd83cfed5e390889555d823167b22f86dbe48100f1d2e78ac5531d0a65275d03df54e7d1ca8dea9d289dcfab1860b158c0916cae6c32c58e3c424d7af374bb93ae92083bd06a96b36d79aa45c6b9472fdee032b8df550cd77ee811f68d68432fb355f240887321b92d0037f4d4864011d84a798eb4fd60dd2cdd20d53aedd7c85ed3457f8f3b312aa2ba895b52d1eb1bc92b4fa2216fc7afc63dfa4b81e68d6204eabb5e82629bc5132c5fa3a69f008638255aec563b5d2b161a8fa2d50c715ab1f6914ac5bd7bcaf6766fdcafa3ac6bceae9b80253a9ec81b4de604ceef37a18d38fb8b6c2f1aeeabf403a69ab67812eb0274d226120068f37483748374a4db3e78657c94dd2ecb15fff6669f67061ff0dd3ecc191aa0983041212f6fda8bba2fa5f13665d628a53764f9d0e5deadb6807667641284004cf9cdf7b57359403637605c2a8f1dbbf9a61eaad6bf567d5a2aea07ba7bb7148d729a5f3522a52496938524a29edd0ddddddb1b73bab899fe2ccd205319c5972b3846bbab0e9016315c09e60ccc4054bb8fbf28209e6bcbca0c285963c3e0e5c4c31c245930b60e022c9962dd26cd9a28a980ff82677d416535b486971ab3648d60adf4c692ce00a5e51fc3e2e3bcdd64cb3e79959d445f9129499b614d762f7fddd06f27d7929d735ef1bc759ef85dd3b9dfc428a2d9d5f087027e71173d23bc5216c135de31f5d9896701043a5c2132e4a0f5b64b036179896cb67f852c52a8ceb451caddc512de0825bc1a50c296d496f76eece72f066775f557cafbbc597e33e6a67ef5e79c4eade94bae72e4a9cdd59eee0e790bae751ea20cd4e5f18507173c0d29dfa4ade1c28bd96bbfd37b42f7bc7d25aa975f73b7bbabb67c8a6f329a554babbd329d650e93a968d0b04966f03064e70e6e41a34da0beb74e22c6ec9e596d80021fbcb6a5917a81503f93b8f95ebac74d2d0655dafda985a6b2d95d2056bad4d656a09e1b4721cf7cda8322a0843c6f7618023e39190d4e0601c03862cf4f9cc20914c303eac21536b0499ef977bc18b9b9607b7cebceeda4dcb0015c8dc537084e10b5e70356ccca6468e0d2ca77560353aa2012000be284532350d0a1559ab5757ebd5d1a8ab65a3d5f2c06d301dba9ec9e994e59c73ab6e258b6451d1e5ecb5dff7896828c1f281265157dae2519dfa886f77ada5cbba5ef5258b50dc2dfd65ac7d2e6e9fffafbf1bba6d4793edbd893cf8dc361e6575abcb627acd303fe89a9414cd9163d6dc2f5f4a4a2a478e1c510ac831cbf1350af949f9573e6e22cb8f19790b52801dc9ad674f1731c3480b8cc8624c76e95d6bcfc314f7347382db95b2a86bb3264f100da6b42fbdad402a734f47e81d3f40ffe859ce0b86e5f682f1cc0bb6c16648903e4292c6d1f79ef53d44e6be00dcfd92b907adc0417e84e76a58c2ebeb73df289d32c75dc994b9fa25b8defe0d5f60307d1268c6a06d05e9af63f6c88290376401c58b0a98e0808511b7650b4a299d35c44fe029c28f189d21e4516a8291b28bd8d00416252598a0c1862a9c16333539299522a529f4d0201a50e6c9920782f8018a114e0730486ac794e757af95d65a6bad7506db122c52bc888952c491400814bce0f1dedb359f1d1310beb425a97aadb4d65a71fdaffebb8f90322cf0d242540c532a04d516eaeba8b5d62a1681e737a5b3870d8e4441454c982853a0802a19f9000af385a6468c388cb32dc1379329689c48d1720407ea42ecdb028f78ce12d0a3aa15edd50d27a5ee5c3e50624c135694f8210b961c8e52d04a236563b13226092d43144181807b3a9a42626138c20914942788d8a18923598e8058d23f9c50454a133f68591286c301012778628317148e2091e486231eb83be77416b220426904383c8581da01cbd10d4be45c6240942566682285872e5f381b8e4290445671d40489cb3997649723252a6028466792a6df8c6e2cc870b4830a421871c28dab41894ca5f0fcce34538a5120657b80871b94a8f041062348dc09a32290ea527f99e1a6e4d445143180e04a16166a027ae8e19682d118295c7032bad277ce2065b75aa8baf2a54b1157aaa081ca11cbd30dc4962aca18d18509a6aa66304281f55f1778fc66de0563498d8ce6910ec250e4921d8eb8f03486872c56ae50a11ac10023b468e14a125888c181ab4266059f2f340c283b48e164459507ce70549448a1bec0802f634830834d8b12482300c37d56d8c42224a5b4851e8c985088b203143e4cd972730cf7ce5799e1407907ee7dfa22a54beec4194b87a98085165a349159418ba629763ebd8d93a04b2a5818fc87521b2ab08862429417262704514e4aa6f43b8347ecbee5de49291da294524aa3d29cc994524abf6978e752f7edeed99d64b9bbea56dd2ead948dcd64297f1505ee977fbb1b8fc9dd8d6f539449a4d0f0ed0fa4c672dc722481479adb7d46bbbb1be3e9454a0fa9aaaf80664a29a519cf26f8b6cc54724ed91fc6153cdf45e09ce4b79cc0d3bbd46ddd9d3dddeeb676944a5a43e7503954d56784c0e51c533b68469908985f9fcaa0f92fd95200ce40a8ffcf105f196f07b0ec3650b3a7f5f2ab4d4ae9c94eb2e44a5ec949a9775bd790ba465f862d90e61691dc7ad9aa505db3f58bff142e2cb40f27e5b4d9b5a4ae512ecd13577585333385a37ac2dd9ae4f1662e1cb9a9f15e2dbe1061a748812ea5de99df5069b854e560e60ad5949b3f99921ebeaa2c08cb9c94fbb89e4c4db95da1cac14c767181caf3bb241aba6aad5277a995c3f8ad2cdd1ad029491042b39422190832a8be0caa219217541f3bad5fe62fb3cc8357f67f65291aa8e1759acb2eec77924ed124683ae04a253598a14d9239a4e89a0c6d077ac77ad666ed5b9b8dc2daaccddafef59eccf274ca69f6efc55315a93a55a47aa55255a48a54912a5245c2e12bac5495aaab542bfbce7d46707d5a5555659f4a75cd7bfbd43e25824eb94f4d6893feb6c211076d8add6b8331ecfba6b2f542fbd43bf66fd2b892d3ec5ffcbd9243b952b6b2869aba5199e91a0c2ce108f6364fc42694815053820d11d8dbccd610ac4850b7780c5991ddce5e7daa4ff69f9ee613959ca23253e5458ee44953724a4e0d994833692a4da6d9349d6cc75a5d4e3675956c924ef249da2494ccf7fb3ee71002dba46c85867c10fd08122bcafd9385041eeb53b64f3649c97d8a04d9b74c6e9b6890b5666c741073f7b2d65a7befbd2ff070420d5f96cfdda52a5d966f6190557821220b2bcbaf4e82784286c690e5e329a8307579010657963fef9d0ff6958f7990d292e5bd5c18764db0cc958655640f6738fe6cf6c0b2fcbed27544f7381fc3b4bd98bbb54e4da1ac937db2360b65a5ec1485a250148a42d1af555da35fb5e81e3ba5c526f76185b6e8f32e8e2c656b53a63a60ced626a90117da229963045db3a13dea1d1adaa6dea16f04fb4f5ae53e4ee534faf762fc7dff5499fe2852aa44e548548932d126ea449fa8cd91901cc9911cc99f1c6936398dbacda15ccaa76e4db3694ecd260935a76e5466aabc484249a82a09f5a36a164da4994483ee0adf8fab52a9a994649374924fd2d635792fa54fc59c43083cfe645a846493ac9265b24d3f40ee532488da867cb80f0da2376f05b61fa082c6ee5275e0b8312972e1c863c3706c9b9017d2905a4f03ffcefd3fa4061e4a249d63d6642893f4ce7c28b0ec10ca7fb88f7ce2fd709f0e325385249fb0c823dc999b95a92361049926f70f719f19c4c1cf108fd3ec73d0e180fb0853a0597992bcd3209cada8a46d98052d54686800000000c3150000200c0a0704e29040280c346d0b3f14000d6590467c5636974723498ce3300c8218638c318600408031842084c8a8380a20a93ae0ef8e5b8d2b012d9687ec3d9145f2a3d70cdf0904b3335447e2ef2dd46f5ea2d6a06ab4793a636febde89617ff85ee29ca08cf731d4731a85b16434c5381f629f34d68985826ede26e7f649f4abc3f6c357c947c3f51bcd6630ebc3fe927b066cc6b9f1840e3e007099c2bea75ec762734322a7e01e20d3129ba6199e5431b84298cf6972ad21d2ed5a88aad7b5370d489dcb5777b6bc48fba388d74821dce2f905107e6b672cd9615052ee230f3d0e3a63152427e0fa0a346c0a688207f25c6ac528c153a5fc6c01ed2ac4d88f71d4b1c0d5cb46446938589de7fa15f64c21cda0164eac2c0281609d96ca199199071cc7ce20173b22ade8a627b5c8fcf5c023a37508b197198856ee74b8c60da8d37592dd4f01cefd105f468c8beea50d7d117033795c6f4d708c94b91fa723aa22fb8b314cb904ae006ba2665e440f21953b84baea1e47635659b6a394788c033364cb8614569dd18ec72f0a930bc686a0b05b8f9047da7442c89aef91a96c91797536906f38267c6239fd1eb8329579ecee0e924897aaa05a2bb4e0e225101c0cff461244ab9af151dc1c25a1326137859be91e9625e08e7178b2168b72526d7bc41868fc05176947f392646278829122274c66f95104c3098e1981226fbabce17a2631783ff2995d6e2f522e47e448efba56555c909b79413566399b7549c3d7c5a8d419c58a1d627e0c09bef9ebfb627ca92f844a61ee65218d24028fded8676118f3458af1b86bb1d4e2f49b7eff5fa310a095324a24c0dc309092a5a8178e190102cd148478c1f843382547f95889397492f1f1fd4ae8b59a7a1acb004b0ecf790aa8a8ce4b3f678544a194c383c260ba140bc417f7e580a62d1bd822de074b8d9723f23925518ccba28671c3f14780311335d1e7443d4dc9c2c988fe997cd82a5fe9a9db6e329a8598001e0b58cbd64e00575c62abcbe8b6bd4d87f666505d7fc2aadd810610771914f0d228e9cf4d8cc2924aa54a96493d3932a41112ca7219af7046e4117f50468749ba233047b04d8a15ab1b8d4bdd1949a3574703701d08147172461659501cfcf4593c2250229acfc300f93507632f88e5eb4d2bf90f84870538107b5054770f466b3fa9642c59ed90a41d251cd1f0c97294842d752deb416da859dd690c801031cca2f6e97f9a4ce49c443ccd05a07c56187d4c54ce9bf2d81378be6c8f50e7a863093b03e47d428f1086a24eea20cb8b146c795a7adff31822bbcceb8fd65806f147481f63e24fe82a120085331a78ba01342a37831226e64e48be0a22211bb57b293ac3f2d278efe05be6713565699b9a2f057797799446c0ee4012b8c00c604b2ea31082997549d582e8c40ae7f5916e0e9b4aa1cb4dabf9f9970001e6e1b715d5d8ed35bde3ed0d0f7c578235cfe407ede500c88e609b7e239b825544d0a098331b36fae22591b06f27350d75e8e6b11280403d621814f2e1c1db4bac01f2760511f9af68fbee09db11634efcf046c214c589626df939930a96e89b39d1084c56e8388a3136095390dcb46842a3791a2f9c464efab6e291234fd97b4e7b0a3b35b717a4ea47f5ec9178f80b0ed285ba2f5aa9875e0c8093561eb1cea0039de6873b268572ea3a49cbf9d4e00588156578e3a8b065567ae06876020e2686398d4461cb1dfff025a06bcec7cbb5bbc9ec36847408d85374be9e20c8168f9222176acafd830107e0e56be01e8e0a061c965ecf987de7df5b59c309f1a471fb3bca4fc09caf03a67bfd397edbee2c70999875af04b6d0e5a0b445fd7467560a56ed6b87af8a4ecddde252b1026463050a226e17916bc202c4073cfd2a1adc14b849a87cdff2c86132a7ae1a398e3e1a0670f187dd577a11581afc642c743cd669fbd0c20e2349047da8d7dd9b94cefe2f2aecc3b44b6972a496179862cab939217999213a999b4465782d86fb6d678103d1b114d38d8029696038564f4777b25b71f308e574a881bbbcf90742cc37830545c884ddd209d88b344fd339e5a4d43bf149d78661d5a3fc135eecbcbf98d69fd280676426cebc26e1cd85e00bbc15b9661aa4d25f3e45c1ca049bbce8292884a66473e629fb3c6dbb330c3ea06122a67c11457d8a160cae7882e7b620653e9a92024ccc299304025b152f8c277efae720b1275bd7e7a092bbf901444ce3bf6404b4cc5251ab3fa4d3fbdae1aa87e42286997ead6da8cafa5188835f626e6a065d1c3a320c66846afec000cda43768813e617cf6fe0e98c6bf9e58504ff6547c1f96936c88aca48c3a6f03028dada5dc633b21fe8a4696dbd958170acadf12e95dc1863913b150f5a1dadc27a42be6aae1fc2397654430ac9703edaf0e023a6a32b2f7fd5393e219731b40f2a04905b709804accd02f044b1bbe7ebff2f3879709c2980a54b7448042fe68d732ebdca18c0c3a12a20641bca3327a4c99920f98678974be4acdc3f193416acca76a536a2c66d41fe7de3c25788442062fe0b9b83f6b7419162ce584d082a73a584e3ecd25e4b3a4b02f67fceea2a1fd0ed959a6528e4d08d52c90c25ee502a919561b393f63882009039b48c24907ffc55faec6c388f25f49590094fdd680b436105464fc4d216decf73fbc44de9258ec94e2bf6da862b74008bdcde10024ad4dd125123f9375ba7026de6da164aa2498cca06535fb10108b88506b2a054072d75a352713e7688cf021ed8ab26059e2b70f47835f5748a23e9c079d963e960060ad0e2341094073e345746b2b04f2b7c896054f7fa34c38959a98bc2e5ae4318bf67318ca4ad0bc980e545bf39778b2fe9a35c6fee0951cbd7a1e36b0ab2870ea7cba53fd2d99845934720c1309e9197049e6789aa305176006009abc2754470cef553e848881e0a017f3a8ede61de60d174d6783a6801403c6a2de6956284eb4146b51dd430c45e5ed25ee4ac73ae563873ebece0421d39aad9701c0c9182fe5d1f67fadb4b08c5820b2b8f2fcb6eafb82c014dac21cc5449bf3e5df428794cfb8772e3888bf74668dd000e9f35d63b85adcf6bfe0ba2eb094c003653d70a1170b253c7a0ae0b25af56f83bc8434f42f63455daa498080076c762ccdd1a9f08cacf4344b7b26e85ae6460b5d346d9561a7d8c3a94c535293643cabd64bf19221a15cdf9baa3653b8479938536bae7501645de9c91a432497ccb464774d7030e944cd158a0e006e256b593ba209ca772445ff506a3f2e898d1b6bb867262a33f7a88240f360e32722ea3011a411d72cc4b0663526eed1a428436794d9f4fa410689adc9ff81eb71df79b313eacac0cf4d3c1e86b090060a07d52efc18e37ba172ef0794c05feb585bc74fe8e1bd969eb60a07b9c81de99d7cfe8e0800552ff58120b2094590b01a025fb584262e9f6630d25731f1fd43054e089dfc7a2bfa8f49e4cf280c219fdc3239fbf97891590b0a6ad961c1d222e3fadaf78c567929c6643daf4223878a14d1736f6a98487bc510d328ad894272beb7b4495811fb33f04761d6c944194359e7c3f3e4e67408493ec4984c64119b1e6b0f1055022371bf4849dad9cf82b1b292823d4eca58d0474b3ae1d08e1abd63e04c71241abf27b85767441240ec12c83ac2c79c203fd0c8a2ce05974a878a84ff1bd9e113f256f16f699804e64135baacf61499491b015606a40f9e48f21efd58a6ceb860f9898ee4a0cb2f8ddc2cd841cba83b39235ff8c7f5ea5fcb1dcd7bcb056d848d8d1d126505e2eeb799746b0a0e6aea530ed594a62ee5d0153b1b4d317368b5ae9508681142a95359c02d3892421000b09296c3334b033e343451b54b2a4c41b9005abcc996ff1607a71171e00902302fa5c16a78ce8ad8b61ed696d8856a22180379c7580eaa395c654074b2b8081db12cd3729f1afbeafa89dc74083317949794c8405faf2c68447424fb7741b0a14609793a21a992c022cfe4fcb871977437bc248a3e71561c3811c514b2f41b17c2047075fcf233c5fbc78d76fdc33e05a239714abdf9335499ff86af6ac02c3c3962a105f69ba903fb94840904cef8b4243f5f7e701bebd59095254ab81b2c70c4b844f71dc8f1a468f5093fadc570a30e4e814c8af48e391db00e46a9ca5dfcbf7d82e2ea58965daefef9594b9cd2ea73118fbe0406f7c5972fdcca3156b8f5d2763bd532ca76d2f2b85d1fbc13a5aa2d374af2789e5ba992712a9632c0f00d8d0f0bd091b91310139e4873f4a699c7d1374ca2f57cc77ee7e5e3490ccf7ae62f6796afd86def0d38de992f69603d28bafe4fb997d7e79240bcf1aeac6c3b59e9a28fff7d33d4221b5550556e565fee29d524ac2a6e298026b84eba309644995db12f7128b1017683900bfd6e44ca924c72145d53e83168fbdcb2c4490e859fb4478687c8bdef4612d41406912b5b972cbd5671db17678de8a278edf6825814e3e973ad6ef03a34b76633666a3112d4e46e8e6a46f10977dee66361f12ad4a25697f8cdf0bc056bad29757439d0838cbde43b30f7526564d00a90503a08caef1271c7efbd03dede3da83193bf996a39ebc5a5b35ce6996d994a236d519d8d3b5c61b590aa723da532b8951dd6e4b6a19de192eb76134d13fde0cbd63d5c289a2381a0a957b942fc08ba99e249441f0cdcc031072312f08f3b29170cc12bd1c2ce514120adef3c8c668343c0d090121845e9a928857b78b89adbd50b57d94a2ade94f5a34d4d5de12cb252980dfd948e343b6eedfbf727369224bc52ce66f1d392c2fe4ddc9ee9b84fcb0a73189f1bcf1af1d7de8cc70bf252042d846350eb3c2c6d00fd8dedb3a83da8f161ea67eda9a43a023cfdcadc6166efa5b2dcdaf57c123f501673d70904311ecc72765c529de80e135e0a1f816a25ad1f4a81573a90be298e78c72e5f7c71d67ea8c954545529096635fcc2ca063968dc292b9ef63ac29d8794ae2b3fcfbc68160edee7b37fb30cd84afc6d3ad4a45d6c090e7070194f052c37072086404c02b7179dd1091ad5ba6d104134591f6ad1f3faf918647f4b9c6e9f7102a13ecca24b69c4b366f18a0d683397f41c0524b20a22ae3c26ad17d0594848f7402c429759bc8988a7613a6b84cf55efd333ba2e222595a391c8a7fd2c53980e2adec814f695e1f97909ede7ba1dbb26e55ef4816e4a5e4f1cf043cd442450462cd6de269d7e3d4e899f9f9dabeb418e5693fcb0a22ae300112265470ba6df440af6c0e91eec389d6d0dcd170dcd1a22c496e80c31c18754134114be7ca856eaf6f62fa55e1c08cfe915765458c922d30a4cce248f575dbf349abd72be75492fdb387ecd35bf6af1bf6d5ced3ab46b08a9d75e084b3efb75c0f4b1d92f114111d6771d3b58573d490e7c7bfe05c4b4d2956c08a08848223ff92ccee8a2099b29c889f46c8f317d9c870637748719a2b430c0e09a1832fe668c1cb3668b634ead7e6f83e0ab6badb3b24de76a07ab1c8e7009c09c14eda3caaeb87b42cf78e40903326a4ac8c4e2f729044ee294bd5886bf21c39ca4a8e8b7b6cdca24faf9775f1d693f54c1c7816474c01402202c995865f40f80ea8f45c9526e84b0dac772d7d8e653e0c3c6222e3fb896bf76fcd32dd6e270a883d601d81d5e13df9532edb7924c004a98d924d31cc52bb4b495e92823c9d8957a3f9d65acaa50bd5d4c9b6e6c813858ee67b1dd181611ce61511368d3ff7497ca0fe9e9393559ef298b323d2509d36b785b5191c048754e7086f5143a9357fa5e0e4a0e43d16a837a7d97d3b87207d9033f05ce051bb6cc2ad010784b9aaa4879ef8ebaab26cfb3ea38f1a952c9c51fb6c54acfd2debc133de04d6ac5152945ac95ef458b948735d041e4c6b2643c6987c834d74336a2db4bea697c48889108842c2bc9e286bb8acc9b1f70bae800055655c392a7b35f893d4081250934c436454387cc242d293679cf1e4d5881346fa668f93e874630a5581344144963ae46b4801810e159f85a37417d193d2c2532371297e965efbfefa5b474ae24c75f359470e5ea98fe51c5e6e3a4aedbcb4d90472ca60210fd4bc2ab3940114526dc824f0b859898e28bd91d8622d9bd4dd23d94c4948bd3a41c1fa48a3ba05c2f5828dce05ac6fc00a39bcad625f6014d48a46f5e16bcfe383141b890a081278f1fe3350b4a64586b816599c78e2653fb61c3f9768e1586587c30634312e3ebfa659a78aa16a96b72c8970f551b8991b73f9927982e5466b0ded608b57dd578383357ce5307c84350b101e4ef9ec7e2a5a9052ab435bbea04e9345a8f4df1a1913237b1270e5f73c810da4cd037cabf50588022268ad294a621982b8b4ed8bd8870c2e8786904c0ff942b6a4aef9668438683d5ea27f15d9c3a274c7f1758c50ca3e9a350431bdc2c8d23893922576b43f0fa4b13953c7259fa4e2e70c0122966d436b5ea0f92c8f3f55b8d217a6b6771398d6f7eb5c95edfdbb23bd0c54c5abcafd07ef48523cfc8883897101839ca1c488365ae4be3ea4c98d8526aaa14f225e53819f145d20d7a53c70fa67e4b4fc31cc9ab12d269889524ff740f21a04dc6f1bf507256f0f1697e594fc36520a7c385c1528fed2bca1885d11a32c8e5e3ab7889c1e70d9d337289a75f2201ef3a0785551f3c6dfa984c438b841b9420d2a7a5c0fae5f7b8af278ca764e3ba02b80fe10db8a3aecd3f158817c829b47f2e2a8184ba5ea54b8210c1dee3ed86c70f14ec785e6d8a2f0ef934e2c0bd1a96d7d54ac209325aadecf1d41718e39f15c637d8cced5b0e8b2b44ce1f70042f6c8726d876e01aafbe4440c19fc18d8148cb1ec1ff1b4080ed87ecb5089cefecff4aa6cb4bf81ca3405578ef696af4400cb0024ce33b17dafaf701543d7614e22ee753f8371266c6c0c0be770457745addd675673c182ad8da4abae242b9a55a3032963420152cfd867c025fe449e0149c971eb94a55ac29003a5c33f839af7e5e969720738701cd7fac31c2bb7c355333575214f13139a011dc56aa2103ade595bf0d4c67a7a886acfe563226cd82ff85171801293e940a280d38b61070155b286876ac6632dc0cd92af6924f3f7d50d47309ee59697a0b09de8bd4f542a35999cd950cbf646008984d4069cea52c4df1ca6c6b93c99b0e5223ec90046eb4caafbf9f9c89a14ead1059260ea474c9e653774f4d506c0a157e00acce56561cd030db015e38eecb57cff97fc25eb5c1f6ff9d235be93e477b43b2524642cbc05b83fd3b53fc21c0102cec2ea8baf2f739edf825e2671a3653d40b7059ae8ce86174bc04b9f30119e71d5211ad7240dc3f65ef110ab2a4c625880937b9d1c75709b922bbd5c4fc673414f7b708e446f9a8aed8c412f91752d51932a24953448bdd864372b81b9e4dda8b44a98d97a50295f3926d671778a420a7d8a8f0b9e52a227d7d357ae924157de236a2e6d1b68c6f6cd9eb1c002bc0a30e01a837954eec654501023c49d8862c91a91f7d74b3825848a952785aab21ff1e9caf04b61f77e5656f18c0210ea7d673b2f9b39fd478cdd619aa09c9fb0f5f31532562d26fa96e8385144da4d3bb00093c0744adf907f2fe8b7500baafccdf3f38b527685e359e6ee2b0d7843dc211438b21294e1b3efac816baff6d2625dfe13011420eb40f4751e34bddbf6c91e2c0a42f6f22972597bec6873a0dd0b6bf98a7ee9feb587f2785f0d054d0bb525ae0f4e969c44bd5237e4e714b76b85b576ec6db78824129e884c070a6c5e4f2358181a0c17f38c9fa616db7201a63ff0c85cbf108644b542314453536211b24b9f521c064deb8d842eaad568cbc6c81bbe340c927b36ddf269fdb807f92f8ecdb7cdf932c0954b56525dd7a1916e69f8403991ab92c5e18853aee004a874bd9bab2e08cff14932feb4d26caaa7fa451caa4a85677a9163905e39a0d03b2db3345429278500df0ca909455dc53bc76566f4d0aad091626a2756dce78db39aef127592c79c4a50d3c50e33cf4832b97c793863ef21006d29847c61be5ef6137d68e622ef92ef7491216cd21956f2080e3fc7aab2208df42495865c582cae268d38065123a34a56f09fa41ba66d18ac8018fcc8d0e1c40f2371ba19bd22f86ead13de1d9d9cf29ec8ba681ccd02491f05b36351b37fa988fb1dbed74b9f13cc26dc861f69e7a3dd46f04cbc6b985247335918b4f063f660fb40d3d7cce79b2350076b810543a3aa1318553b62a956ae11c2d9ec34d3d4d9434e1bdf939d49d7c83679efce02b95de7ed1fe5376257573c76d3b1c58f45835769875b61dba5e50110ae0884908d58f1260defa5f05443c03ec865ebcd9ae52606c6580bd7a835a92f875c3cfaabe9ff1c166ecab77847e76f2598847c46d78e4687e4a62847e62cefb2aedfee29bfc956b09ec502bef2b12708c98f4cc20a8b31e955659276b3eb156c6153bf95be1ff43efc842fd1c195c70c3293a50d9cd29ca59aa2d781d52e9a266dfcb9b05e2a62833781e2b5646054c0f99362ae32e8ee1125c0d39b8cc51d600d14373ef6e6ceaf9238125af38417fc4f9975a891661919373705c1ad0850cc35a60526b5b3d598b45bd8b0bfdedb548e667d2a2e127f680468a0d238f418fedb5d27f86f654961084677279e6ff139359dbb6988c35b79efa66751a91d0225004c81828ccf283f2d4f8230a77141951f41b6d9bbde162ddc8cc38ed43369e235d8514b99d58552bf71f788132cb58bef9bb64476cf0f59952d41c4110bc6e7616bcc2a787369e590156b5365dcae5e43004bf2c440b60ab6309c58f688dc47d57eb7e586c6b23377a6f949a61ea9b98da90c55543608ceb5cc5ec5db3869d4162820b8cf6879e3c9503fdceb99af4de4a83b9385fa317f5eb0cd99e7290c5f5271c89665ea398c9d7d4a3a19bf235ac79507c6a26ad2e8d8cf388dfbcf304450a4712a9ab98378fe4a650f847f69853dd517aa1c0bd71df3fdc8ab48f3749690f7fbfb9dd0f134e6c7dff1d5e304e03542101788ad506b7ded09358e70e6b478c6e23b89b088c5d16c9ed9d4201c2460cdd0677ee1d672357334343b81b56e1fdd697199ffd8c245b5b370155760015c6098f434021a887855533c5a2a4a9f4230c38e738836089c240182ba0477c791b32e21f9942a10eb8b39225322ba82ab38cced79cc8fed51db71ac2cd67e0a4e256f97028099794ee772e18d8031c0705710dff2005ca711a00b52579667dacc4c98ad6683c998e9c09376571de6ffee5018d37e49a3db4b87606a09bb7842a81f2bc8b2d4ce841c60bce77c26809ccee9080fba76907e8f7948d96f52d30e98c4975a173f471382c5ab7860b774a5c7321a37305dd6fa3dc9b0ef41a2becd189dd99fe3c7e8843d8c03bebb1b2b69855565e2a4fd9c82fba57b9dfe8c938d8c742319f8ed24dc95ab1f560a2f6196ad254a2821dec00b6a623c8e61d56504ecbc7066c2f452a4115ea0ac31ef7436eff5e84ed7dfd1db40e3bbdfe1e9d1396efd295d512605a805138c60a063086d6d16d428bbe37fa7ab72d9760bfc79492d786751fa794859a4abce9b1080721dd681416d5b3ec819b0ad4d80866387aa28241387594073793dcc03f4a86af7c295ec1aa2eb0594f95efa2a386c77b8edc7cfd5f00d58ea838e7043404a84a5206a17c706a2887a04de17246d6dfd076927b29ae8a45c3999c533174b709e5d35a7760a664bcff6314de60561370ee79e9f655a7c6c32a044850f13229a0a596c1b05e23e2a6e80348b76d90812a2a0b6654490027aa845780158a02438ca84fc46413aaa4ffe6faa35280f75b1ca0da972491a5c530164168bd024319d792c561b23102ec96abb47847ddfc74df996c6243f8996351dc0f6c77ea977db026e28502f2e2b4b4d9f10382fb5f6457189976d52657029ddcb012127112f7ca8193ec5910b62326bb691d4053cd1c1722d22e1bd6a6c210a4667a1758ea42c2601aedff68e01fe8c4d254ad808eee1e6b423bace8b20a05b1863a171cec882d07cfe08fc872573748c66330b765979a2489f40ee28002a6b7957cbbdad79075eda207d0f77e0db3616525f16fde91d0700d51c25916c6342d5a24df00487e3065beb6201407a5581467b4ff341466ff22c8abaf51b642d5c032962586ea44c5a397a89c544dcae96fa03804a8f913065f67c406048622d0b7ac66449a02259c592e4114a1334aa9837e0a29b1542efdb6bb1e4f30f614c5ffe657f79ab2dc32149831aeed1a00e0d05ee23f7700c96b53e997fd1a2b85f711e361196ace96e6186047cdf3883a5bcf95a29c00fadfe252085f998e910cbb5d61dd989877c44a98b9ac22aa26a8165e2144804baf040e7084ae7ec46d2636f0367622256d8f73d81e53c820b259b1396e30138ff10740c55b08a129db116548036e9e1473b5515c1bdbc74aa70bc105f71385afbf1f70ada200ac3099084b60affc1116631f0cb145e17a0f8dec078d27fcd2a0fef12a289ec254e923458f32c4f12835334d5e1f89d728aa4b53f657bacf93730cd649d20b1a087e5730f8231722d29d0777d2dcf25a477c6d21da0230fd4e4a173a05c5c15acc19b28f5639ca2f8c2a20401e45f374c7be06a9abec57021d20eeb481ca66a7122c18c2ec23ecfad52ed1ea2dfd7b47ca78391ed89ed73ad002399332e9a064d4620982294da7e2de1bd47e078408992c62299b1f60dd5f076b302caf451da1fd844614fec971854664f030273d5b65f1390722e8502d65a1109b392dbfd442fcfdb7ef3cf7c2c92fa8258da6592faa7d0627f5c3d77aab735d57588c27384fabc9792d31a28b04ee5bd510528baf1ba116e2b51ea59275bbab111379aaf6686918e2f879571ff57a106c5c50d730dd7d4809317e0a6ddf062cabeff6ab0ceb2ad41659e020b1709f9fb83641a93ef5dc8335628a53915880d13e868e68ec0e8d28e0df01476a0870d155d8f12fbd86471606c74c2472479229733f3bbf85a2271770aca9789cf5778b91dbb87c44f56ceb952543140ae58fd7f8a79cce8bf229ef81730722149e7dece1224c407cc0d0ad83c3db19d33996b6ed3ab876a5aa1c2e6e2b4eaf9ae5d64b7ead3076cc6e7457f7fcd5a671177077d7963e3eac8bcb46c02e1ed3d8b784049a9a9351a384926c87973a477841c3de49401717a24575e3cc755a3380c62d95b07945f3f845ed7076ddc4a11a7b94db242ad04f178352ed1b2a44854e5a798b5e47f90b435195fb2f5b35e8436f05d517c6b37ace30ea1d59050dc4c001af7787bbbedf11a3b320a46b4c8f44f15354ddd92967dc23a6fdafb7f63b32ed3f42385eb64ec9af8da248caadc81848e7a4f42d1db5d49a7232fd7bab957fe6febb4832624383a3047877a028c43807a1b26489f5ef2d3f665feb636cb0542532bb84753d8068c60677b73791e3a908c9b062d6776f392de7dcc071e5a183b20443faa15c57799c14eac6e9450eeb2f7ea12d16ba3a0d0e982cb427bb3d18cc81eea3965704f05362651bc7c98021b5aa05c776d2e4d44c868c3f39f622d8ad54c49f818ba6f86cbad2c3f6e0626db89420f5457d75b5eb14560862879da0bd4eb9a612d66e7377fd27dabc62253c9a6d9bca19857450524a8d1ecf6eef3c5e4f20dd0149b51fd6b349e489e7a4913cafd9b6b04ccb640eaacd8a96e3d64986a389b3f53fa334be4e08c51f2a52662d141ba57df268b9f15b30ef7536bc69dc5e544e4bf09c62f3fc7836750a6f86d31f0a1b726bcb877f90b5cefe61cdc60fd3727dffce0b0b0285b0b054e69f0001c3b4e20ee04d207cc5ae9302b322d9a8dd93a219f1b48b626fb3050dce014a8de755f7907e1188ff58fb514776324c81cafa19f00d76888ade31efda917bf4c8e1564f2d30935886a755250342901fe07b5285d3391ece0f7acb94bde2283c9d1230127f8b97a3429955a3622219ab4553f321323fdb951bfea1d82485ccf17a5ec9c739af20d5b9b3a351eacfde7a141ae1fecca970b5c07740f8ede3a4ec50a4120fb48d9c0f33dfd50d4a037e069b99a1d1a39f3f7a12d3cc7990a41751118379967131e800e23ee9dc224a7d7ab6a4bfe5f639c6e7daccdd2300842854bba580ab7a21b695301c26ac90371452bb11d56cb0549f1c88cad19c4a345d9b32b3b92826bbf40a3ba9f02ab4d9ae0e4af5425a00ae91260d1c902c2930c56d4f69a0233189869eb8d6ee1e4f035467a407abed5003553d0adead603d27692145440d56f1176068cc59090b0e3176a94dea55a61769703fb347aa35d3564a4513cf1b72525fa72959e7edda665a8585a7af3db44e8e7fb69bbe0c8a8d8dad3da53dba70d43687dedfd0cad61f2b17e50a88ac1b8911af7abf47d2ae54f56aed18c2fe35e155ac5e519aca9468b0029fbeb6561d60e2f3953b97b493a9577d39c9238b5bc9694105b1b4d025b95578553769e16c2f615873e5a21ad65da76cd13757f14b3b08b70525722f87d3efe5c4a386c919d859cb845057110dc00a0df142607872e428371d7adb3584ce3b69a723a2064dcaafaea67a84bc90d140bb526410047d6ce09bce6550290df654b238146132719c3e06f5c2b6ce6d88d9b3184b83d676db57cec33c92f31287b82043111496699f50cd1d765eee12c772dfd82c21efe9fd0b4935967ddcab85615822dc632c97d3e31417c079413708cb2e2cbe311e9557b246f92f8aeae209cd515864dd3964812d43b489c2bf608971aaa421a432c4af2faf8ab92b8ca09ba78e71cee6e295a1fc3a8b1a06ddc0c6186c16b2c470030302c42995b9a70ca9a1f3fb08ed9ae8f412eb3e313750b93cb7f2694f943925b55ff9c326df8b72c9b123da875bccf4bbad51644e1a19c488746016538c7a52082c7bbe07a45f9ee4a82ff94bcb34b477e7e1678f1cd7f971e59ec8f20664aceafe9bf8a4463fdb580d4b5a7289b3d7340d06c7c609588da46d2c51b25558d1c3b271561b2e2ff6c4962896f4341c190e64183c9e3a1a4498a93c6238c8c078f454e04204b3a6460e5f24580c9c1a5f6430903c72b40861423242dc88b05661a4f89061bc68aae011875553230213114c474e052c8830d77104d5651a4a2540b9d75109e439f4121f92941afc904d704c6a36fe9394374afb165d76d21867a97eef11a1164fcf70d8832857c872306ba92edd27204387f8c2045ac48a5706a6a00b3520f210743bd5a191ef6a5688cdaf98c13ea1a3230c382dcde37e6524f7b5b42254b8944ebd0117691dca91b7b5d79bdb69a574a1de5bb529e74a8652038aaa89f669da02b83ab0475025d07fb0ec77a0518a7bda4547bacd1c535c9fe211054fd80b847ce525bcb56b3b4c8ba437f12c163827f1828290eb51970656058e5986f2ea1167cfe916404d1543bdd58e810933f4a3a66fa46e67d743ecd56877b77fbbd881bdf79b60f4eb8690daa24ebdaa4e0a4ddb858801fcfe25ae2de80f153c06d8ce6ada437ecfc08a4c86438f31e040facf8a54ea6bf9d355f4932376193de526be487eda0a175f4e14f8a4d0303141f6823669bde509ab64c511200569f08c88baddce0dc51610b7c2da8a512c3b329b2812aef74158fc989860d3170b4804f11d777843f563284c18942d13a6b703727e944e4fb6e7887a2b4d88fc5f0967ef76a2afd4f3b49a2fbaeec843a83220adb7da6c2bc373edd695fc3a7b1e07f9821a525c7aa82d24747e6ead965ffb851db305b3d2fdf92eb5f06d4175df2e634c6c50e41b602363ecf21b9c424aea3b4a9112a800a4f4b6acdf345e2515de8f1c4aaa97b38f45e6b97fcf07c67e1972f616c5bf879a8f2efe14fba4d54b08e49fdd60db344fc7068f94b86bb71ab4b531d72c30de2c0b2d48d97eb3005b44b34d80a4cdc8cc44fb887145a7e603710fce60aaa8d58f57dc6b58d1879ce34ee3a2fa39e561dbddd61a619ee78a1ae8e7b12c362346c2331157a844456850cfed8ea7b6a78ad5b8528799ad7a8bb621d28f5ddf6ebc57b0eb377b1f8feb6d9727c43ce14921afcf9634ebfeec0e2f926e378029a96bc88685c91e514a7e4200af9fb1a0303a0e5ab86e8a66f65c6b4649f7f50719e3944cd1c00ceb51152b5ca2472b192807914d5161786d7a67e48ab4327b0a15610fd86d04cb0fd8946356c7bd92614603ed431a00c629349b207612263023af88655ea445bdcfbc5d6a19802bcecd4350d163063e8b832aadc8499898fb99915be0bb210b2e312b72ba60700e30e04d6e6895d4a958cb4ef08ac1a497ed90b8e18f3ccc7b040c52c711936e70683184c6b9c3c15b46d6ea98a817ad491e50bd218089adedc60866598f981b28afe57adb8aacfc031bd533eaa2804ca3678198ac40c8d9ed5400eada248fb85046002fa362579103fb5ab54369ca31dec7f8dfe396133f6aa9d7feee051fc2a3b2406e8d64fe47785274a2d7f688b316c7614878961762835cba94ad675b4fade5be3ae03d575b037433f12139a73c58edf68994c3e4e7116a43fd3628c6c853a4ddccc89f084d15725fa3e096c1cc040b3f80078420ab9934c55bc3c332958f3ff5464412206206e3637932c8a2e0f94157e01c651851eb67361fade91e1674730b51d33dad704dfc1bf0dc45dd2ad6ca3f59bb2cc1209d795ada04050d62dbc122939b351b3b8de0b3d7765a9ce8c6eed940df135684ee58673e4e4ed28159ab46d0bbaee8d66139ddc155f5f9cf4bc3dd72d51e9318c7fdfda86c584132cb5ea2b866fe21a847534b9bc8f82f352ebb2876c9ae3d151501d61affbbf3fe0997ced4435d21d774f8248540770b679aad2335595925c964aadf51af48a59d5b1cfae4acd69744541e9a131274c2d1940f6b4a7491513ef51613550456ff41fff7d05d0566c48b1941ad76e40c7430fcc60ac1058f4cc6aed7a2c84a7df0729a38ff3961801eb6ecac5f2b6e73042e64cfc62b69c217b997a293d4e0234b604528b174431b08208b21653a96c682e7ee5a3aabde63eb8c40ddce7b9ad2788f7c06f2c62e091fada48ed64e495edf60471a5b68bc52cd48f0b1943de790448b65e96109590397acc36a3995fdb7ede3720b696d75b9cd48c56bec9329b7264d371acbaa80c556278a564609cd16b8c451519ce383ee28a2c9ea8cede91919f57c5d39f520e0b4aca382f1cf0d2ba3adf4d486b543ba96a60666feb8022e3612ea174193d6785e6e768604ca5e7344c0a8c42996aa540545b37c0b1d83181e38744682e072beaa6a2d1684c5930cb843e261b9c6003acc9f169320917ff4b81924c90ce91ac85acf64246d2d59203ec9537e333d882ba0c43821faced582293575e9d2e46ee8acab793c0141b824b1aaeb85a783f9bf173cf4c8a246ad01d81ad86f8ba1c3ca877001958fb99b2c3681588271ba036e28cf408f4d28a679abed1aee08dad5a99136ac067af6c389cae391b369f82e0527e01d6107d91b3318c192e1e7f012bbbd0a89dde4ba407071dcc99ac936614d660b0f4048c0ab2c17067319133c205a0d3e4c408a05b8ccf24cb489b68a0c14f0cccde967bdd6bf2c6045d5e94e472797669e3e2f35e1aab3bb9311d9fccacbc2e32b7a195b2828dd0a751b4ebf7602db2715a8a498db98adf685d11d20e049103dfc791560f21acacbae869b4dcf0d20555bf53fc0ca67b0a0bd7762c3775b79a9d51dcf8d3bcba984d8a6bf6a6b839ae181d262b1ff5457a081303f509e8eda3d784cb08d282177cdd4d5770f5fb8f69756d68a5ea0b15267f65aa22f3dbd961b3caf9f019d09436d0c924edbdff40141b2f6011a6e3d10739209b3c44a807e785ecdd3ba5c6026982e57fdb4703a46803cc30fa7bf11041828b37cf391d812a9c49e036bc192e68b3453ff067331f0df02ced36b34125d6fbb31ec0961c2eeba9e0ef96976d53130e600a213b15b36f2b7073e18bebdfe69055a95546f5a2f7a33c1281984eefcbcb4e9831fc1a44f6d68c65958608ee2142bc373de9d4715b50cc174ae3c68f86bff958bf511f3765c8f5528a67b9c93945813f2f97e1bcf76cf757d81e2603f9d200266572539c09db84050a5bff088568f396e2677713d3d802ed439b8b033c19bbdbe1357e1dcac7127ebb6e7a18d1ca757fab8aa717aabb2d6132d71b5504ddcb4ea8b758080ea9e39e52e7a979bb819f13d97a0a617329bf3d49956b551fe1980872550b748dfe1d97ec9c78fb4e75ff2b8c5ae171b411efb74c0e6801381da941be1ec15859d88e0b655abe4292ea80805291d77d493ed6defa7434fe02369bc42f362f9b42d8e8b344ce1460c94fd16266dcc8ab4804d7a8a1d1ef1b1beb50fb3a74c3f73ae899c4fb0da3913aba43eb14be3f9879ef3d64b691f839b0cafac3a3ac76c5e0ae58b0503c77b1be9d461cd815dfe1dad0eb6684e95ab79dc28cf59a0b01211b970f29b508d79756be6ccb6d7fccbd4693b6bbded06ce5127a8dce11552d855391b9228a1ed44bf0dfbd12c8a7ded358ce59151f97445ed18be79602dc65c9b2487529e4589db98100c1d9b110643cc0ad35e9513e31eec541be12162516856c6549998701669a2a2395910b038f55090d05795ab4bcbe171eefa6aeb0121c1e9450e36bc7f3b03919c8d8d8013ac498ce3e07ff44b245c6f444f2fc66298a31e8c338c79d05e3641d4b79fe2d8293fa25a9611f8df630f1bd2ca2206659070e0f33ce7f1d31cf4e88429e6938a95fa911488dce92622967e3b4ca2bb49b94964d6fc0dce90100b54f544d0a5b317844a7393b8ea5939e3b18e9c7da6aa5ce96c96830bf11ea2a94f40a1e0f3babdc15c637cf8685b656c3b20155b4aa74b746fc9b15d10325760b641703be9861a504da88dd4353e9afc9b2418cdc0523111eef268e5706c0e2fc3b4ea323171654e09d7c19c8bd9cb33aee24c0a50ac0dae10f291b66ef541dc0a4461a0b61bcfdda2d067f9e0082222f906dc10f5b82cbc8e19910a875855dd926ac101fc9673b5c6777d4e4cd402598003bb51504ab5fa15d0fa141283be5732a5c1743549a3dc206692b88295dadc4a24f81f1638abb21a66f66d909cee1cc4828ea80f048449b75e35f5641a93589e1bf20b9f02b20bfdbe59c5e745dcc27ae8fdab378bcb8e8f043b79c055f5a52d591b625a6e432de19dfb7e61c5dd5f55387c4b70cc6d2dbc349f979abd7a541c23163a1a5222e4d9a0c0a99445bac3d62b66b496be9a70453396a11e58158840acf8387eca6d8a053e90c7466ab87c13dd91b0f1665dedf7f1b18b768b87f8b29e45e6ad6e4f573672dd321232effbf48d5c105429099937787356d022114522384ea9f2440acdd551f979fcff753a2064dac216f78066d4731ac9381a64cbcb79ae14d59cda7ce9da6f61fd2daeab5bd20f2d0d3c2f43bcdf88442ff76139598eb0c7f63e5459e5e211445b2bf6c88ce5c58c36d10c86ef71a3db7260bc716614c44b19060850fa40bd53f234163d27647d20853bf85ff89750800b369e0153982d15645b694a34382753014d9a017b447db1fdbb4645f038d7e4ca7b733024a4a920be09e0a308de9697e425c1a9a04671007bf736f57dadda6122447bc6ed3722706c289c2270bc6171cbad4b776ceba6aa16e742771d7c8add1aa704ff01434f22a2a41fdebe71acec4cd381111c5abaccd74d98c3da13e355dfbf067e28a7339d6120680a5146d9ac2cc79e21a4e8470c0ab091312c2b03e08b99f3dcd5d549c4658d230520c0fdc1d4546df63334a8132481eaf4bdf3bd114fcecbaf2e398703b09682c146e3b050b605954a03678943b300e19c104c381d04722553ce7883a400d830264628b1d165c638f712403d4ec4d09f255ead0d9408650f0d23ba6981d2a365b184bbc367081d4779583c5323d4f133bb0fbed6585e314229947c4973cf1d85402c16a721cb6d26a9518e20e4f9d38af1c34464c560bfe9c884c1c2496fcf73f53fcd86b8cd8c8320887f1beb559454c5963343196b6e12e8a98aa5c9c0e16a64d4c4aeee78c0fdacc7a7f9d0a922c0ea7dfe1cc4c31a60d175def842cc05ff3a5d331e2725a3af1d61d7368643c39b1a078d5f995efed49a05bf45901cd56506b80206b5ad99886a3d2bf0afd5a9bcf08de5d87c5fba188509ddb8419e252a3768d53924753a70d5206a18cb2e7c64c8733dead2856b428c09e0710cd7d80cbcf473ec2e0775c1c84d06e03e3b9b7bfa3477e65662cef0aa8063f37c85588f2c97ae4d11b5dfa68852115403df78e0cf1cc881992c440c32af2fc859b4090e02156c744ff571c542e1accb1ef34f3e73d734c6ee01d5fb2289fa8fd7041b100ba4cbe48720df3bdfb8e87f0999cf1fd30f77372611dbe0f9eba4f71a0b37dea2c094b89045cb0825b03c0927f08c3c261bdddb0749d527f63eaf9a51f246dd98878be8e759ebb4e8954ae1e668eceacce388bcbf80f4f0832ed11ba447f2a308d2d9f471dfc37c46d71e29b3af7c0e7178e82218f89d8da3d4142280f858e65e12343e058d4fd6b45290b5db94c3491cc5ef35dcc733923f34dfcab5f3f6c9e0e9a9ffdd280c6ba25a07f55ac1107cd511b900bf8aed60cc86d364b9f8d916253add859740862eabb29c05f5b0c6733e3ff8dfab118a544dd1efd0b673ffd4ff4d61781443fe3efa3b8e06dffd43d831abfe8293837c2bf2529e87aea1a4b06410caf7076db752f8e39efd699bdf86ef5ea608472c7a677a232b64af55b7e1f0bc0fd5de926d3d4466cea71f8f40d05f94b8927ba4c134b298c485c3f73cba19f48a43c610bada53ffaf6ef61d0c4f9420e9b7dc5758d4e93b9d99591783c50a13cb039375488bff8387a8c6198b638994eb18b6031cd779cb0e88131da011b40f9f49a0d80741f08aab3455aad927dd4f6def4a40c53c9d3dac7e69b1fba826b604f95979f224cb0ad96dda7726f2582004c88f93d467993e16b179af09981c8c39faab34257a513047288745b32da046fd38f121b507ba3d03ad9493491b9e497850a75db96590c3602772f92a06ee363221e15b934fef2e1757b2ac4bd380e6f1d3a3d53f41d179b2430cd044e8fbacf31ceef7defc8657034370257290693d497ee3641e52c941d510c19dc80c65fd00eac2154c02ff2046ed9d8868a1bbd4a4110307546570d63422b055c92f41ec4adec2c484ef51fa91fdc1625295ebef2fd4a4bb3e4d5bbfb995398775405d717abc24808806cbe5f57bb8bf6617db6a10b4cff0c01b1431a672ad28a9e67d8c4f441c5c11d1d17775b7a5455297cf20a97524d5c34964b9d05e45d492afb8f38b96752852d0b6a4db0ea57c0d47bcebc415e8fbb261625f169bd9ba720525cca672e99876eec5607f46cd765c098f879c75abaa2307e582adfd21ab1a891459dbdca18bd5f1411e9fb5a9061747108503798b9ed5a2cd37e9bcb5fb3cf77282c9737bc2dd2d31f502deab54415e9c86643932a875dad0a86e61b4e8313d283bdd07dcc3f6560040546430943ede923f5931b0d1a3e91e04f538bc854618e6a12b9a566764e348ecb9b07ff1adbd7f937d9d48929046dd54b098a0614e9611cd4518cc41c360fce1a68420c047df0793f53250e887c43f7da4e2088797cc1021997f3a7970bac911022157a441510e65d78a241256cfe4581f6be94982b2e43f4cd2d827861c5ac54aa81b9069f53162aaedcd78240f17e14e518ee5cba01a5720b9529212456f260d6e71b60179fd0bf1c392bcf385a34054d1506decbb5814a519416fdb74e2e95d884bb85bd0ffb61ffa38e83283d633b4b701b047bb541d8a53106cb802f61c1d79db5777ec8deb7457b57b4d99de083333a2b13d3276db6efb7f44603891492bd69da790e2f29e142c243915c362e7c8e5f8f1ad932cb993444873adfe1649e9c62e32ea2fbee660baf29434caf33b1293675e0a48314ae5bab8a23c128ff21002abec96f804a2c797d33730e9ca23473d5e4752cab6829ae95fe04de1190b219b476c09b27ca1a7ce0e4edeadd3c8928c9b48f4813276a05d06f977dbc31ecb7fd38632e0490834759bf75373d962e9ba21ec1c8b1ab0ad4e73f5ba3e6181f46a17345759857b7e85b35c7941fcb1596bb019bb7b1196784b62040c12542a8ec4a85a896068d86faca8536d200543e2011905bd471950d31608266578786039d8878a2662c03bd0f7db563fcb90bd909d38249c4e04d29d3a22f3cb3ec6be2c5854ae1208e47a6e4d201e4c82db1c84afda7f1cab2b0c2e0b62289cda5dd16b365d168a23564534d9090d688acac5cc458e8c9eb95c61924c332e38154b3a78968bbe6c90a8d5eb5f7ebac63fa8beb393b5a847e9dfe8fc1dd30a60afbc8817c9968f3707b15e9309081173d724fbbd189dc839b91539071dba6b2c3b3e80a770c002f3d4f40eb8c71d442cca089dbdbe6582b79e2889f2a03ff080f39fb002c3d7831110b83775aae725310407a57ca36b584e62f4da09d11bc2bb5acb017c234b721333aa0230c1a863bdc2ef719b27e5435f9c30f3adacb0209f2ce55e4e9d00bcdb214ea4ae68a07181fb4d55fec23859de387d851c27c1d4849d5bbc4140a8047741303d3c927e03ba411544b8ac9702466f61d51d905bdb0fb0b7e3f0f1040e30be6ba756a1dbdfbe38fa008eeccd9e39471feab5051b1ff24d27c70baa4179e2537e6204a8617b645eb0a71846481a8cd9a9fedaf323ca5450a5c2481b247f61cb1da17c5010899289ae7aff01d9093c578db06ac6de76150e348a688b4c14a85b2e36b2d30d95efa453b343fbcf06c3a93dfe57fd848bf685161e5fe775324b6b01d852ac6c0e9a1a75f9c491614e6e8ab0f7912f879ee38af4818fc2cff77697ad0e12572b388a52b56acd14ccd8eb5272262a5e2f1bf1aa98949caf0cb53555fe9fff0e4c54c598483477910cfa76334b7b10af22f7a2559009d43dd07d8a4ffeb64e3e2fd0b3f5a34fcdac0ffee322b002baf916fe0daba2983fa126f28de1085c9c12048a616ca5016001c8542eb0028a9f98156061fa449bbbb938fcfbe1455b812caf2588060b740935e5edbcf601197a18502c421b9c48dcfbb014e65c2555dfc02b4b98bb3f8fa578cf9844ec7b3c49cace7cda0997919352e896ce828c05e20452446ad86634de98a027375d27ac967e5e5fba3163d350fef956cd70afcc5e0016aaba2a24ad1b6b4256d18b0a7ae1ff9d17dbc36ea0bfdc00bc26eddb238a5da9bc5782fc55ad9fd1b39e0f29f6a720dbde71cfd4b25cd0f38bbb154b66956ad86fe8aa7c75f4f475236c60fa48abbac8df366d94299b9ca0f165914c2eafe6ce60c443ff56e1cbd5f7fc5191a46140a315f0c2c1099d16c35fa2f592be61fb76e609acaf835dedeffc5cff8f5036d9b30a4f4a51783e6dfb2a936924492fd6142efda6dd0a5fa4963518d373cceaa8def3bb983eb81c00469d558f0604cc981e218adde2b2da63f4d73cb531293565c660a02b9ab86e25fd66680792b00669beef5684d0c65a13b70c4970dfa537722d9b7f248135a643bef2df14d0313d8a96882396a0c2743de67e3e2ec9ddcad7aa1239bee6d6682f37e10ac7b3b20654189811ec08c58b602c7b9fb1c5cf7cfc210e866bf247aab78fe94d360439e54dbfe96e119ff515814c6fdbe832befc25081ba69483d3f7fb4c844b647abf1a466e782567ee97b24c4baabdd59894d40bf40f217f008d0deea5a3a077b728fa1a4fde4af155a986e0faf288b2bb62d77fa501b3b2060b0f8d998384efe010407593b4ece6760b43a3514def765321539e6e94ea79df3271ffd89f9634db06f6638f2f0571a5e1afd8ef4dbe6fe4fff6efef8503379ce3a3dd2df7c4efb2e0e10ce2c20d179a4adf3a66cb0a65b141aef7430739ccc4e726e748d0cbb1964196c98fb95b8c0552b55b5b060227f91375712781c9832cb930f9225c1af92153a80e61d912c191ee1947802a4354f1c4963613a93fad655c838d11091a59ec2fe8f098ba8358e9d6211236d779f6a78bc87f9ed572d3c5defc7190b81feadb4e2e3b7b7aa3e79bf8bcb1ff9b7d3d14d9aa5d65aa1e1fba9774de01de244a0e2ff99eec2c85eed6775a3b543e716ee51a54bba5dcfc001266f6e2540c04a5847ac31915944c1449b854bb25e197dc2de5fac2922e8042bd4d58dc26f5f5bdcda64c18e276d76791ec063e15eae5ae856ab7380198e7f551bb0509548b4a26813cfa0a3939985829515f70991b6acea419016a4d580eeaa6369e5666f6096349f66db83d879cf96293918e9f79058f9182033b143ce21cef8e7bd0b0b6c67937e944b07e5d5eafb129774832c49a64c5699c2b501f7e9aa9c81c00dfe5ba4d43a8cfb356801fbcdfd21a171476517ae10d8443bd9db785bc39f1a1bac62217093012582cf1b256c18398bb405617967a56f181a8c0d148cfbaf274601ab45f6547a1e8b1c68318cd0f4b4c036c4f0b6b9041971562a5ff151acf301b89a664f851b47ea896661be994076b6371258e015202c8717c74ffec2c86af482a8b861ee64bb9fe5ba5ed4d98267e021397bd95ff0b418e199e98695a1143d1726cb786f3c1682490f0b839a6af519da86d0126be97c0397cfb712123353ae6406adbb57c9cd39576c66ef30766fe4b4c381902c89a8fe631a0193247b98d22470878ebdbd867cba4cbc8e1b41f11089c40c2079ca8bace9abd176d04ef0e766f18c2adc5f47aae1ed4a8ebbe6ba90eb2eea8cfbf71e6f438ba1bc7d8b8cf8338c2e83862906e28585538f3eb2bb087ed883e20f02f20d0dd1c0c72cfd5ba23215c7faee64a22d02369a03ff9b714ce538a515859f52ac0bfc67093a20e3cb6ad879220a5ae942e6260ce52c83bd7822681c84a016c0f319af889b6452df83024e6a1dd7c842c2f837410c0c7f54892694dcbd3c1509d08a859d131be3b7aef4ac54e59030ac57f0ce7bb267bf0d184cd7cb61ebb742e6cdb292a7f24d6bcfe5faf9d3a0df6ced2106678b868fa9670d208d9d4733d2e6dd9bbc45ea4ac7f164a5e106d893d17f86342d6eafe875e3014f84ab03c2e6f00e75992a0fbc1bd171895cabb112bdf31e2a132edc138123f1bba1ecad2f9717a16fd18cbd457dd2d20d3bb7b66ba892891097dcd90f2883b6e089738db10a2467418ad9a9704da1b71ce957eb416c70746542c9655d026dd0080619ec447ea59f4254d5ad035b8f759ce9b8ccd1d6ce383b15fc8030199e823490d7b6a21e4bb122a6b06cd395467b32c6074adfc904df5ac38c75d56cbcf76eb1eeb9d4190af7b9479480561a525ad30a60493afa1865d609218fa665da3be00e58d08e59d3e4819e7f627b2fe6ef832776b1a91cb2475a0d2350aa36f35d42c8d36f5b9149894041f10e769cec50bcd070dd7e76640b280749ecde97645b736cd2b3849d4aae72a05d261296963cc16e6a81b00328891d77b8bf5cb63abf20a5de34e81c59388d27deb94f2818ef22524d324f5f0b947b7bfee0fcf17e3124a58e2794d83277ff2c5cbc0a49595837133c2ebea9e4b92474ace5785b4c2326114b27f4712244a44674cb8e932a0b7cf7b11a10fac62eaa1a636e13a3f9876c76fce9704d320731b5908e6e8237102268d9f93a66b52af022c181340407f15a065fffcc20288bbc82a9ba71cc099bd9cad45643b494e5a01562805122f116486b9648b611ed644edf66c97e88117739c95217119d20b4b7f14def1715010aa3d941d85fdeec9860940513b6c38b7c70f449155dc8469c59444647a288156b8b53e369358ba1d71a743d86c77e6ea9dc415a97ecfa0a0c57b03314054bb97d193c9876edd318de8574a30e0f16638d318a28a1e555c22071fe9964b8f15a9936d3044437dc2cbf672d1a2e988bdb2ed6a512cb62a47a96d2a3b96c6e28d43f284b0c842d110ab3c43e0199be6a6a2e1b23a0d3009c1566f59e8e59bc52dd01923c76d485df09b148997349e54f4096cfe3654b021401d447c96b2e5762a74d1c6dcb28534b22e719667e3a2028b3e5bd7b1a567691c4643550eb9f66d6912099b1a6b74981303b3492f8a4d6d89a0d12e7714f7bc2d8d61da7d3549dc90ecc093665a8faafa56e41b63ba69f8937709810ac10199297e49d2523b42204523cf0e51283f1727a530c2a2c68ece0f7f39481f3a6fcd376daa1e4f2347522687997af12bcc90f846c4583882d6f0bdc3669031858028403859012443139f0cf3747d776dff0100dc2daa110ed4c9a21e0196eb20a0c75416d238c35beb42ece6a24fd2e6d5eb809dc8eac25e694d874d642a67347057669a19c12875420574ec3e00260444a70445741cbff49a6d8a7e107c73b1335cce83241388953773f8d69a30f075d312f6a994c97ce5da9bcc8b88982244af500344cb0b726925258109d78da0744d0b43d2344030c7e8b18959e305eaa893ff1bdae1042d4857adab6d2021e95260ca3769981c4f464ac3619e594139e095a9e088f73cc4229f950f668492a83c3279e59a381008811244c4b66b9833aa4b17b73ead8ffd36aa7bfa1b74e195576fa69f413cb70ae00b3913f1d29104f54873ecfa41ed0a41c1d3443abd3f02af50c4978b5bf0992e9755dcc59b0b7c46a39d1db6a7d617d5efd7f76533aa28a0a87e6d1450c3963bae1bae4d64c408af0b432dab0c62ee204447959d294504556ad14258fb82f0ebeeae0e1111ac2d8b3642e6c05c31c6258b0747f13b390aa23f4418c5c27c711679c8711c00577de477ddf460c343557bceecf56a4869e2d8014460b8245a43c7b7bf702125fb95c6f38ff38b7562e8f4f3172d1247f6604ea6242eaed3258afdf6bb8d581eb1324179b2fa00eef5f453665f6a40209496fb845eebfa2b18b38879ffdfa8f104d1a906625b8eb2a91072e6ef5d07d4187e2272b3b2b6dddd4e3755a038b489cf2960e5158af1c394caab9118d7da5ccbf4a1049aa12fb4582056229ae6c9f36df7c86fe447f6a4cca632d38e1e3e83c5c589c0775dbc3a8fe3c07f4060af27f94de839041c186f6266a000003ad84f8778810a42649c13fde33caa10e76884dea90dace60d5ffbdbd6cd7a696969c90e000c1a0bcb0a489020419db1d5679dd8a9eca2cb54866f648bf0915c18c9cb175b7e486418db7bf68145ecb9f8e2a8e2d7e343d5d987c52a0d44194b6d9449f946d2aa480317ce2a12c6f91867f8e58bfe900340d5f554ceebeb7f39bb4e4c5dc7b092926c7b8b95a4bc969e4c1f2f666cd9d2b4c0c01c02b67af92cbed2cf4baf92f4b5d6cf9b4d5debe5cd8939e00540d50b1236f847b10fdca388bc5a400f99400f17200263b0c7fba0236a812e513569105973485d7374639b3108d915d37951318629c460f6c2aedb4b8453974ad57285cdf5df02635ad22b2b9c873a378fbe197630b3801c86c896d8cac0c51699605406ef29ef79d2291f1d83198c2ec6511dddc978a44b4d7a843abbd6ea81d22987c7aecc69415c50933036ba299f81085bfc0c7de92f48d8a6ca3311538feede43ccb41bcd46e529388fae824fef5abb3508ecfa19b4d84c580e0d8031ef777ddbd1ad563352099a892e34bba01918039b5eaf42e6afbbecda4738bf5679bdb99e44537026d2295779d5849448a3bba75c9c112662eafa8d74f35eaf72479f89db25624a550c24bbe628a5946237c363375d1c78ed51bbd1b255171b3d0e7cca337ce92aa7a3d367a5a7a8dcd7231a47a347ad6644ba279d0a99278dee28b330667439baab236ca3c711bce9065ec3eab59bb2d19e72ed29c752ee76cc69254cbaf935e9a33fad46e52937ab6020cec190ee1ccce829d84b7723e1940de9d94978f46c84eb67a276332cbad775a67ed9f43e94ed00a37fad631485cd3de565f4666cde955eaad9e1844d70ba103b34a763323bb571e9ec18cca2c83b98d153a494f6f80d3ba84a91f08daa4927610755239c3dc339b41a27e77a9e21385ddbe1844fe7d9a139611ed34b18e75cae4550e57ebad2ab24e5a6145cc4e93c43f0ece0d3b1979d4aa7d3e9743a9d4ea7d38906a7884fa552a9542a954aa652a9542a954a25d3e9743a9d4ea793cae9743a9d4ea753e9743a9d4ea7d3e9439c524ea7d3e9743a9d4a2a7773a5eb48616045d4f3a0bce8772a6badb5d65a6bad143b6743765df47b65e1c2068bc841c3234495524a29a5a43c223802105ce0032964e1053f34f4f33c43f07ce049164cb6f082224ce1051af9212ae689afb5d65aeb89f04153318fbc115220821327b238430b1a787a5d9e0b8010c61152e001138aa0717952184182911874e1c6145a340f6648b00d357f5d171bda65d44a4f0de3e460cff641c45622ef5a648278217668e2e50d41bc39c3b6bec618cfb34313310f7dc53807bbcd76e80bf350324f272e229e475ec678c913cf13cfc383a2434c3865707951ee09607c7144002c80470c31a187465e478512d060309b34891870c1a299f2a6789842382a948066e2ed32630b832544834d12ac61c60f8dbc3a9ad060f4b1ceb0e2098dbc2b3aa0c1aa1bc21631f8a191d7c4031accda9006144d68b02b23001334584283890c60840a1f1a791d0ddc82ec22e479a69c8f2e56b51551cf432b9d527edae8f268002fb1fb93ce3cfaa56c5015dd4bc9a02a62c90390108c49bdc3bb96413088043dfd1e0f404250f5042c1e730f0b52226ba873f04eda885d34b3076f82f4daec49306c706ef3da7dac2b6ad71d26332c4889761dc3e84932d8b2bc754def6c32c998734e693fab06852d4f343a7ea20163aa13a8ea3c9d900155b1c924633a994da02ade075b9e42b30b28a361231a340084da75da67a084314e4203c027f291d12ecab776257429cda11d606e2a31cd39599cabd45a9cb1b14197771607b1a18e77b6cb42066d34065f74508e6c64cbebb298c248c3cf808227bff0220c2fa2f086105250450e80f04518c260038c2fc048e30649e841167e5cc08413be70b2050c844cf084282678c2462b88179c7801cf099cc0841cb4be908420196401544517d81002184940c20e0d9e388941194e40c1144690e08514710b2bc878d7eb556945a2247281848a57bd38e381e11305266c6109475690059ad905155c1598644109464c7146133d4d5ab66bad400cc9c64b10e083f70809b1ccf1c6069dcd724e9d202c480916448bb44832d8dce1100d1f25110fbb0dee401506c4dc63428af20f477eca48b9a45fbf6e2101d49462ec5a482069d7aeb1071d1cb140d613c308d81101732e92983823c618638c313e9fedcd26d80c725498f1c8733070ee603e392f65159040820926a080620633b002e27844523a8384e2d08b2fbef8e2b3a28af8e2ab22a6007bf1c5175f2c0293175f7cf14526b1e7bd161d7c497452cab7087d49c4820b1f787185b04565e902da76b56de328db3b8ccf0ba716989a606be9078b2336d8aeb5821ea8b0d60a6cf016556c97843790741645e7af50805d8ab8f2b3c198f7de7b5328b14115a2fc8a15f5076e37a8411357178c20c9271f154be493cf074b62845e5c6f04e1c5177d98bcf822132650bcf7ae80d1c5878a0f49840e660726c15c4b8e41ed2409d2166173e2848b2dc71ff85862f43b8c5a7849d04c64d5f6945a4ab948024ad3da94524a5b2be869ad5d6b052690620913212c59a28a254c7ef8707f2d28b3659603d9125514d1b39ddab58e4883045b8e76ad2396d0e4073896d874b46b1d1105185baa5deb8823113e075ddb57f36c946951f5aac70dedeae78cb1c72ac649a92a5e96a47a38a85e0d10ce581b804f2144cd9583f0d52660ae25c618a57cd2242995946e34c618a194324a09e393524a29a59492ca29a99c524a29a5943225b1237181b560f162242c42699d4a43f8f79b21743d72c4d12795a82a75fc848fb55aa1213c6ca904172bb677e79c7356cea037238c8931c61823bc7373cee943ce1e7435e79c2adaf26c9d73cee9361b6354cd29e79c536253ca299dfc4abaf8bcd311dfe06052c210b52b7b290e063bc6014d8475a0ed1f268a4b54b144154cc428a3fc99f1e7bd1fbbcd14777ae754e2c706306aa26cd456bb6ff3a1b1db67d73998eb58cc5c30d1455d584295c5760993254c2250768394f19a9eb66d13bbe95834d40d5b8db514b55189d5cd876a2bfdaa6e3e587bfdff6fc3021b5cb355d770b930f7986817895c2ccbe8ee2eeec773f39be8d9a576637f3d6a40765afb7573098876fb4cbbd374b0d73e03a1e8daf529bafd965d8772713fa0b58f5a8d48037259212c11827c01566bad22ad54b234d351842da96c24155366a3cc35fbf4b4f91cac13c8564faa4ef624dd91a6dd742065b357ad5ca6e8c2726439b49af81bdc8fbabac1fd806d37d40d2f3d1be61e1cd90a0495dd804d3a6d151dc184dadeb8641a0e094e2c115ec006dca2e709ad5648187c515b2af81992dbb6bd960a7c7e1843135c00e30c217cf10415345556a16b75f39a563363e4ad11553082082d9f6db5a7d60d094eba4802d36ae646ea9c5923749082282d2fdae8ec31c1135deb46b34081155270050a5095c78a5a6ba5938b1f8e8b22a228228d234d5a5ea266ad07e8baaab1d65aff2acfe85a6b6d3abd9051b5bc15a365eb08142d99a09c183161b109aa1e4aa8adb5d65a6b9d1089e002e4696badb5d65adb3a4113a6bac311acd72bec68a30a513aff0484273d496c3ada25e1c913377802a422b4a5da25e1c9182ced92608534d8c8dc0f99b91fb233f74374f76cddb197455f170210b280316636faf0b15aa9fa7297bbdc9545dbbf20f47441d2ba8c0af836d9441c0cfc0b0ab0e8eadd37d37bb8408e36b61e340055176863ab372e48b606a00ae20bdcb8a08e58d2e7e0e83998181ffc99497af0410994e00530a620c24fe582881e1ce104114c8e98492494324a29a54443005f202149122d48411657d862899137937ce00751a421060ba8006485231081a4e74d27464fa842108cc0a0091c2c518588286205701bc1125098c119342043091618810823b3065f5081856000821378c0840e946c81452d028f168ecc241b28028a9227a038410fbca844e039e332a1da403d210c28a8420a4f40030634528b963c5068f9e3411e2ca2a089d142f88239e79c730708356002c5185ff0f014413387d09307899eef39e79cd64208b769e77c63670927a070a18c263040a227073cac1f6fc623a80183286800041f0cc1092c08c10b221b7461056d2a91f27561640a4424e90207375090861016b67044145718f98205d3da678408cf08fb8023509c51022aa8f1c4106a1602184211a070a10443692c215e61a4072970e2ef46490cca98021a502c6982094c4637d89424e08d318a30e3491b2ae8420d3222c591f916242638438d1514810a2d8260248925e900d50359c344115588a28329442106409001ef4874a18f98aa8ed05b0bf14f6bebcb012ebac078a156032f92905231a08a4aaa7aaa1f85da286c423f039f5cb767628d76ed9a30a244bf7e4d34b9b2a11ea8a2a72d3a0455cf09ebc991a1079964e80397ac11259e3d43434e24946d43a14eff03216c88e5151248064927d327bad05bbb6d28d4904f9489d66e1b0ad58450135734e6a066335b447d4ca5676b2f8490fa2861b993165329a08a9e056f258ce7f4994b68ebc696e8c621a862b1a08f9125d6a6e4b30c822a9a836b275a401595912449775a8eb3c730092f82385b29a594524a295feba528a776bde8a6e2aae565c4d892563d3a69bb3cbcf22fad2dbe2ee5803dc4d8739d83998139b273b2a409297a748995a42e5938ee4e26151d3af00a46e11c38e29a61a20b4aabe92794d68f426d5bd6c25aa2d6d592cf92d503aa608eaf568eba34b2826f64a3b0f491ac887a8c28887afd02c696e3e6150eccfa09b34c38a354b0c592257d90b0499c4987cf32e8a5c32e7d26e6217af4ed6622af4706614c745589519f28e362a5b1561fea83895e2f718562c0d64bcbda6dc324928a049b34267d6a3abc1ebd456f6d8b0ec921ea438386a24ceaf4d327caa4306da952cf8fe1799b01cb6063c01bf691412f5f6c118a839141138a0c92413228684299502694d9c646c2f20a09045530ca0b8b4350f5c59602f2ba8c6d28650f1f218e43500555f4beb45af1ca2ba04a0241158d33f0c50a5ba6ad4c44d5ad1fdae407c65c0163808ec5cb868e41c51848747cb6baa1635031464f474c44d5b6e17d0163cb92155959d52da8a2b165b70df5d3167d4903129b427130f42f626c99fac8256c9245cf84194c88d14e2e617b4fc14331a08a3e4339a4e1168c710dc58031f414368932911597f8449947434f4f2914da01fac4255186465694a1ade8428fe4f408abe9af257643a15aaca67f4163a3495a3e43589e7abcb948c7223cb0e0db2c5f6be2a1b5f17abd3adacdf50ba76caac2242f616cf2b1fa9ca00d0bb007b51af9dc838752c611d56aacb32f7334f444411c5a5e0d538926f6beb6946acfecb5147bb63938efaef3d46ab26b74d26de0d98298a171eaafc666e6688abb4826054dcf71b7b91fa33b2e7a0e618543cf02f47c7130f33947bba39ccc8e93c341f56a98771912245dfabb34ddf4f284cdb5c438efa5e7cde6e1acf2f8d2a756737ac1c2e65a2eb8a2b36b29f1a4b36a088cc979511ac7df952bd781840c244048d0e8edda95704309347064272d086cfac3c8069e2de7f58d178828bfe1edf586b5bf679a0eda71de731cd3b2a705c9f177a769aee5b581677bade3dac0b38d9ee3e6edefe58c6e03cf06248eee6bd2d51e2feaf2be769a96326aabdd6cd7fe92856dfbfb4b16b60de368d72eb536fdd26cb4c715d35117c50255a6cb438e3b64bb1320dd1e46f7021870468cf04616902c29ddc1942d25e538704af4d3e5a174310cc36e10b1b1d1e8969422d9a8e00c5be7b5f6d2312d887c0907115f4a4bcacd46236d34c2b41aedd87d3dd248c7aed6a41129c33692a6d56c3845229d848d320cbb4e6ced3ab4b553ad06c3292c858d3ebaa88c85de0b40150f43265055f32d0f070bb81ae6a7aa636b661a7afeba39d7f34ebd40d4e7e0e4bc286d03cf966303cf763d621b78b69a06a29edede6cff4e6fd274b0c739654f0b22faa3da49d7069e2d88da398fd53979a7817030da2576edf6c2a61a906703cf56e31c8c869bf0a0d560f8e11d5e9cb005f19266e39a34c5f587da15a7e8ed757bb5beae046648fda4f1006952c77064f66a2dfa0c4c5d175d52d7553c1a3b65f662ed341d74ecdfdd25c52b36c048f0bd47e4f5386adf7b1778475633f6a031461a532f42083175f91d81ef214fe56c94f3bdf7de7b6fbe98531bae20c4a9d703aadced1ba900732dac0821c4f3bd236f0894f7c284ed5de57ec0c776d9a5df9dbb734310bb2c98a04517a05d164db068cc61328310ce534a2785134a6b53f3f06e62f99958af74d3cd4478b34e6c688bf8a081eea1694e918eb5f34ee7d48e973705dd9f13a84a417c2359edfecc802ad77365e14374496208663046f6e0c162b38d3e1da882458460f5e0b1b5d868e419a1848e6c07e483047a60b15824a011a207168b958a485211bb24384a40302f676afa703ce1bb83aa09af73db0ffb90c4e6831baab3d550cb8e91feb4e867af503d26a3f7a4d89edf92244f8967041a9b19f619c9618dd27befd8bb1b5a22d4eff9bc306193b88883799f81f5a75fbf255a74dbd3aedd12aca1967f2d310d88caaf3f20e9849abc5cf5fc8c340df243dc8f00dce5d999d102b83c0f3b3c3578b45cf57071316fe88731bf7c0b8c99e1f5f2165ede4b9cdf152f656c3248916cf3965150ea4d551a6e4afe35eceb66555f12aaaeeba91da7e142eca02100371580d354fd7f2f6f4afee52ec79c445600ee8d6c0a03f096a0a6e1005003220191702a085bae034301c057b00c180addc8fee5bd91fd724c74a516413e5208094133220baa5a4bb47a47bb56125a3446ca524ed26c66975ee4bd9f9ec792965aa0cf4001bc5c24ba530ba1b86da2190455528844d3a78aea14593ca5b84a603e7d2efa0e931cea210905c6c03e06a383f633103ac9366f3a9e512bcf1646218a5774dc45998de52eca581906804ac144e91387a6cf713cdbc6f1bc358e6754e3787ee358e15903b603d881f3b6c1e0098587c89e5e8894a5d3e7e5caa02ba5b8528b2b7d7e4ea701e7ad4f0f00feb1f4f24248fc9040950a064596b760190e009c6de37b6517573e9168e4384b8ecb70c578ba0c38db18300b4e611d18f5158cba628e9b65eb744d37dbbee8556e26a26a792a7a15dd69d6a61e9184072d3f5fcf4d3d220950cb6358f62c0dfc10ed64191b0f7b7b0955f22911fe0c9a3e4bf868998331b51d1c69015de0a714d005be8b86c762e80a2653c8c134d9b2f49152c0181f18e32292282387a20bfc634f1c8a32d267480e5df5a25fb25550256dc471238d542a95ea511d5e4b3c671054f9c820290455f0191a5d40177869068a8c86528c866f63cbd3a7a1cff4b19812d6c61e9115c51e2530775a8de5f18aa312b127cac060e963371f1eb1a707c62801654ea58f74d2ef6e064223bad4ae054e9ef6e60b84f9a2d5d8cbde180ddd8b640983ce8e8dcef1c9186bf40fb2d7884f94b039694da1283324ba40685b5b13949309855e28c8c76adb50a80bbdb762c5ff2d61b57a06be84b164be36b92e7a185b840255f092258d4823640f92244ba0aa88fb2149a30c13b5a878b8919661a267d4338bad53ba63eea56e62bfc30885641aa13496ec588b4875f1b0393ea15234bc5cd29249c735b6e806cac88d904a409988067481af88fb21e393880654c1b8460a360755f10954c1172774f4b21d6050155d74d1bd7cb1cd2ae5759dd835259f574ae8295622fac43da00ab25847e44dcf6402c3de734e4ae1002beb32103bfd9e1d273bceef22d838f3aeb13c444fd8f63575d88a5e2f4e4e10af87682cc341bc862dea1e6acc79382fa454427b9d78bd6225da9f5643e7219650751b5fccb0120651359cac56abd56a05b4eae1b15643dc0f1c7b98821fd00a08a8070828e2d4c3ceabd58156404db00002c20268b5025a01ad8080562d69e06a02ad7a4581269004025aedd063b55af5c01e7bac5651a6842f57d646fb4c04b36bb18b44bfc16e8fc95b9c12457964b55a39e7a4879593d50e3d56ab1eab1e9cac9cac9cac56d689939f56125758fb9ae2d47b3d266f754ef48b5e27f6fc4c14dd7cd9f0a9977a18ebc47e4fd1679b9297f8e5c9e6681e4e517c8355666cd9b692b8e2a9d48dfcab02e65af26ba9e261bf23ef012579489044991246a1e20b9234d007b4633c62980fd9842d5e3e657ffd00162b49b992d27dd821a5a75c74739116fdbab9080f25d1b1a75ccf445727e6229dcaae019dd82251ea7ac4a9eb50f544d72ffc88401544f202d7e59f2604bbc479a405a937fdbaa6a75c1cec2927dd8c9df452e92a37b572d7396e6fafeb948ea76eea06bb8e63d781532b18c84eab3c7583fa76d4b76f34479cb321c7d3b37c065e475d95e7b82ad74f1727e5aeaf9b70c67ea9dc126e81aa79968b737dcbd7e945d7078e86310c48fd6bc2e65a3538d2f556ca2945a2d30c88384540bc1c665a2463bd3fe8649da3807e0ee6ba0eed1c0534bc3933b40f1d7b9039381e1393188837af9be12a0b2ee82a428a43c046bab5f51785b28e24891463e48161d9ad177d0e06bbf43aeaaf63f6c2ee348bd1b6b2eb8dd9858d5d59b3ec3e25b2ed9ae88e24b62c9d8389dfd1822db3d8d3d76aafa8136f1c6c9119a43eb84cafafeb05ca7699a08a458d2d9b7e3dfe5d9fc0101618f31270e434f0f11626003e7e93f733ef34c5521280d7fe00780defdcfcaecf5b775a745d93f9299160cedd19634bc76a614b4f0955ce478f39a7291fe628209350cae7e668b620c618638c2f4241674e18638cd147bf97a43842123ee2832fc618231154bfc7f85ec6a0420944500d71eae6e15746ec42ebc4fe7cac27c06350c6bb86f24f9367b158fdee7466478912055ed94e7b4bdef4b11f9a29f459bdfba13dc618638c38575c9fa605c15ea34c6328d36a2a8d2fd60baf63250580fb96006096524a2f47a2d45afc474918c7fe223d1a849070867d9d86cba10e00ce35385131010038ff8b23ac601db8d68ae5faaacdb4668fb0fe4558ffa274a333bc0ec4217d88ae37c78e9732beca353ac347fa283bbcfe8eb31c5ab9dc9594e7deaa29c6a9bfb00eece844c5847e6fbe996fe86743bfe7d5bbd36eea49d9aee870bd479c5f63ac03db3e1803ce56069cb7197046cdf05f7ccf82730df115679bf82a24bebe4b341e0ef50fd35a33cb6146c2a9277da409c96286fac5c733e018700ad7c3f7940e161c23ce926acf5e44c5e788c7718a37d5a7b0caab10d36a329faed5d65aedadf522c12ad1b8f13351c7d5be72b78f6e86ba392e8e7bba2a178bd8c7dce6243dd7d0b3a6682935a8fe87df7befbd12f6529ec538a2d352f6b10509c52c8b19ae447af4f54ca44776e1ec5746717e19b63425ebc84ec1a9cb87386ba841a5fa51a86d1ba2af447ab47d26d2a349cf2ce9ef5976f68d5e0b628f43c2f9554d48d676092e71aecc8933911e4deb651a0f074baf65d9a95693615734a94824ba4874592cd3783554d9ae65e2f713503183245408f5aa5d8b0a17f40c8c58c2fae9aceb27943640bb5615415d454fabdab5a87852854f633dbd517a49c744daab37a29edaacaeebe57b7f2f5e5733b1c3e6bd8e611367af2fd6381fab0ecc2ecee843f4ac77b2dfb3fa0cbb385562f670b08f4a42329c618f5ec219365652c15907c688d517310ef6d1f3c35b4fba23c410457ad63c829ef925e94c1f2f4ef621fadd1c1b1b32ff94c8899d9fd48464d8d95368c49e45af15e7d75331ac037b8475608b70adaf669fa3f78a53a3c7531de61fce367f1f0263e2ebdf9187c37bc5d9cb269216a4fe619cf7eca31a4fb309ebc0ae5789b33e159c1d1941e671aa90ac27ce4060418d61b347d735ed77df6fbf6741e6afd3fb0eefe925fa942b722492ebeb3dd29b7fa43ba87a3ab031fb397176644c9dd8189e3ad4673af1914838553f3a9da787fdb263239c75a2e8279da85d981d1941de71329ca72624ebf7307691e80f050f871a11eee96bd2ae19046dedadc516aade6ba7d9605df143c1ab81e221dece064b5460ae25cf1fb8240b23f4b4cb82085a3827edb228c24fc31ff723b21ccc7bfc892e536cef130c507c583bacf1c617ba60d10c2d1915018c2d7a58342f88c5a2798fe6c52fc400430b16cdfb6b459ae7b26d92234b0351263e89ef1de941f27c222b2ef969f22e9db060cc9128f372b004254d68de63cf9328f3583322914f2290174e1e2b47d6eb7150dc50e7c89a3f53805acdcbd09417b5c3f2898309723013c6480cebe96610845a8d7330eec151c4d81107031f8c72d22964b108eb99420b2d25432d25463ab6a670a3638cd3612640ecb40766d076781a0376ee2e10eff03c843647e368f2a60dd140093df0410f0b471e08893d3043bfbfc72022a608eafaf9b4d71c043bceaf539e3de74501723de745d12e0e9b67a3f2d1a12644e5a3e3b859fb083bf70308e760f2e8a59b701efd3445ab553e3265edeea61bc4b5caa126e45dc3a56ba18afe90fd071ddb29d87fd0d97ac33aa826e11e6a6cf3f2a35ab59a511dddd71903766e44b0491260f76dab234bacdd36157c67a2e966d40a4edd6432994c2693c964fa4c5cb9ae53a99b50a9cfc0959cdaa9a36eca944ae194093f23864419c8b276dba4bc9442b08d26b28ced88ca4d972af85015b35bc2b61d715a38a13b24ba4819a28c0f29ef1502aaa056633a16c37d7d2f9462884549f89e3af6948aca8acaca0c2a4fcd709500e018b28ebc1e18039bc57590d82cb80854c9cb70874095bc0f89da3619fe341d64c7d03ffc0024b21c889d269b8497af69387d0b0068c0ef2700b8050300a36ec2d9b669055fe5d8caad57b9efc749f9644a65e5a6547ebdaa5cf6ca532a2b40503867d771d353d78ec52c7551299c5fa78ebae9aadc5ca455be72b3ed95151d57b9d957ee53a6ff4095e9eab8d36c503865ba6c95a7307e2da8d2896dba8e9b4a7d45a54645c58453297c95e7f7b372b796ca3599ae12fc2dca40564a0e1cd7b2bfa38ea95ce80355f228b972d38ac9f4b74cada470ca741d5809009e32e11bd92a57c14af051181a81ac2803004c9f03e70d077ec31f168c912669ba092bc15779259ced26af6d0555f2450af93fc8f7233f9bc01f19208e3330f7b2d8e4b3cd328a261d37803918859054bd9e10c279da36cad8973336788853f04d349cdf36caac6e871421694fc56738557b40558438452aa2dd2150d58a42a8e3b33facde1da276a8414822e114fc082b49814ab66731050f892ef1a8269bddb44a9fc562a7ab3cb594d214ad975a5aa356f32e9cdfa534452d4ed1533a2f29a50fe3541d1ec4a094d74919af79bdcaebd6779892d784549c92975d7f4959e994b71207111b07fec51e88de82c248bb96cf1a2d1fa1262d28802eb2b69f26fa9c50d8e8aa13bbf65063bb2610fb9c17254a94283ab1a7b42f880ce41dd73350084dd14f2a7bb6c42730983ded61ecd2dbebec9d84297bf7947d3db5972d3a661d144ba050220640bdd2ae15835fd86fb66d2f6fd681ef98944113070d206f139fbd39a509697b516aba5f3735ebbc9d38832a2a6c3307718d43244ecc81f8e7ec240206663ce146bb5292264eb49e40a371bbd61342d87b59f639b3939e32afbd482265d9356d5e9d78937dda64d933fc94360327e998bc339523d2ae133f9a4a4a774a52b0783d01d4f3f40542499e34163317658ea4e4e3274ec9f80285edc967f2da67d7dec9cb2b95a47c5e2525550a4aa5baee3aabaee866a913dbe21cc475c5390a0882b6c4f23ab4656ab6aa110763648e76323b38b1c4b9b89f0cc814c4d2ce09a116fa8384aa203247439c39982780762d2766d039a55f6f0ee65d07986bcce97899c2a61303e61e9fc3f03949066a8331442691d7231f4edd3c89755e47ec5e19d8df17c03052d46a20561d849ac4f27d0756e72e937ace64589e4d3231c8c8f0dc320396e18f018562d9280fda049496144f1aa65d2b8a274e2069ecd189235395831850354b10e6f86a2b9d32c29a53a465adb4d504d329237cee87abaee6d486540c15cd01aae87940d5d4d1438d0d0b823de2a8ddcc4b9cbac1c1ae99558ea3f2d2e361c4593ec3f210f5ddd6ec22d7b1a48283788d7d06ca8cd9c0c6306d0255f4d9a5622861b3d9cb32dc3ca08a069a86af8b4404c86cd0b64b18534f22d926dda4d590ecfdafeb1d34c8a91a10f7468b8e1120c3a08c3146e75a46335cd732dc6671ade3ba5e719de3bac6e14e373f349ade7473ecfc847e78a39bb34eb9ae371863ff9e89ee6b94fb8172302a59767770b1b9d612dce80cdb689173428b4eba26d915bbf93a96baeac334d1b13f2dab4f89ea7befb2b1bfaed798bdd783d85f43ed264763387fa045b8fec212b654b0adc05c4b86ce88255b7e46748cf1a93484f22f2573228422794c74494a61a539d1065ede07aa648e64b192689df7d751bbc9d10fe70f48d681b0c1c7c8ca1bf4e1c1dc5554e802e6acf377f460cb3646333ac630dca865848184be92ae4b0963b22b5e87bfb0186874fcc888784c8e91a4b5e708e308184d9a8c8e87a4d7560c3762bc133b66b3a96936f394be6234d36cea29ce152b41d18c3ed36eaeae38ef002325d59348a40b938e592dc8f669bb6a3538727c069e6c725c3b1151cb67b7c9b012d4e5536e96b21175f69467c7b420ae951c390d0a0f21a2a1813ca6dd5c9de1bcc3619436d7754c94892ea601797d3d47a0530e7f69589672a7d9c4a788349b948f7e6937da239cb738a2c7bfeda6432dc876a3c51c18c7cd298d1371e63bd026524a93bedddc81c6b99ef280efc0891a28610a3f28820f8d732e2aa79acdcd22959ad29d76337af61948f3759c78fbd1cdd7ed556e26e17c1dd60b873e06e1ea7c1dcb325f27dd6937f96ad16386457726a65cde0de3cc7fa02f9cf8149caf6943af1d4bb9d7b1a7a95cbbf3d7c5849a3e75739dfec299f4790ad4d8b7250d0692c67e1d9b5453c9d771483bd767f61a955fb774ede294aed97cbd9e6a42e8f305e4faf64c03723dcb6eea86f4eba45f38a5f294203b7d3def744ae5291bd24915e7f9d15140761a754b77231d3b09af7c08a178621c0dcfd742f408aedb3b445f3b2d3520d7a308cbaa106d4fca46903343037682d0d8106d45f8ea3ae7e7ea6561bbdab5c258d211978880b9df1d5f00b56bb5c8689976ad9614edb49a77c5d66a19d1d9a222744f89886d4decf7841d38882fbe2f76e060cb5b103334103b30093ff38f5229a994d339e9e8740e4239674e7ccf4518618592dea5e884ce1d42483521f2b46259e6dce7a4873745a79b333ed7d465639dce51eaea81d8e94921bd91b821760e067e62f7b015e4f76a9e76130fa9009d73ce39e79c736e0bcc3d1c5e3b8845c8057c38e447af233b8beea2f40312fb3aa4f633ba78ed9d68de622a6f6f314e4b1833678e9f120ad1f13455aff9949d534609e7d38220c108f8092ff1cef56a0bb6186a92764dbb2e91e8128944a24b04b52014622dfabb44221186c9ec61f83932600dd1ca6c8aa896b5e38cb22cbb62cb0e8f338212c6d4bc4929bd70c38b96ec6da8736a07514a5121a574bc0853c94437b399765f6fee8776edc20eea20688a7663a7044137271d4456723f7e78acce51406b8fda81d809c20fabb563a751c763550be2b460b176b0e86175dec182c56a20e20fab359c258bd53f08b15a5e47d462b56613a1b03a274689d2d905b15ae2d2abe1d560832eb408b64c713026a88a5664a2bbe367cb1b8c3175bc2eed46f42ba54597f9a59e7689b49b656b97bf6e0a1b6529aa5dd793971469371ab6412210abe50fab25ce895058ad61d1ab21da5743bc0db6cdc99e9e2cf3f13961d9493b9d4ea313e9743a6da7d229e5a4c2c32853a03b63f4242e58741b91b2ec66521bb5e899949a26354d8eb4ec224cab79686c5ad67e65d7afebb2234c864cb6e8754aaad9d04b9cb58f5e47c74677470e36eda36bc7196937f4a447cd066731f2a4163d6a35a2eb5a4eec0b6721fa3a76b3cca3e7ec993ebf8f328a3df53029af9b23444bd273624b1210dc23327af4ac5d8e8e8d70d6b01459edb2c545ab479a3c863bdbd6def5011663c4291bfbe0912d9ccccf3713affa4c7f6120a28e0e068828553110513f07f3449d1bf66bd733314788867f1d637ced3488611298abceb423073bae80ad230cfdaee5eec002de20e30e1785783b2fee3896957b64401cac118d903e930905aa42d6c975c51aa223f75df192b8160ebab892c9830274d182723484aa9193326eb07107a93e61cb811b94a7e16d4a06a10e90b4ad6434bc75972d61676073e6bcd39c0a88769038bf08c43d7e2606a1a78e1d18b483424fec1c4c970007422d1d8f79f778238e774224ce423410f79e8151cb2962a4042460b5732fe21c213ac7cf1793b87e54e943c6478d5c8f5447f67b662fac0365539900d1ef8e27a08d2e6f070bb61eec7b37c196ddd0456402f026541bdb565fb17df1c2e6fad12877304fc6045ddedd0e24b64ddab9fd3161e3b157ab68065acc5a6b31767314509fdd4c04c94b0255f4bd676f76d6fdc1f719089934b43b9cd8ec77f86ceef92141122dee61f1f5bac97e74c7130f66f8c9b650de2880078ca1421cde42551686cd459787418cc75139d4d3c79dba9737b634e4d04462480f9327b3a75fd0903969cf9c53f648d9337bdc4e4329a3504a295db9a6d9a41367694538675b86b3a9294b531e4d6feed1f42f6b6c19d57905af5f9f5826baeb1e2debd3a8a59158f28009943c6f0f3924d39068508903a01b30692887a41c822fefc0fa640e7ade6c227befbd1546c1b0ed105731fae8f99a58ad56ab15b518cedb8631bd832b1a7d94a438020b3ae294ec88239e40948da5a609ec4d343667a10aca0763e8f5ec03221f3f2f8ec4d004b75625c4bc2d2f6e3c7a4d79e59a05aa5050b56389ed3d23dc2049cb6330ef36eec7c8fd78fde8c5a93174e6cddb632a979658608cede9f9d40dfdbce9b3747b337db61fd53b072333cdf4a73f4d88b5c2a7b1d3d2e9ddce42bafd83c954c83c4d390b76fb472f2f6faa74156afae9669bbac14c38557aea4645bb8623e5a50b64a753308ebb5d1c12c6fae10cbfdbccfd7899fb01c46b9ab291b2633bcd66634156c35a6f14e243577b1ddad70fd775680f5d3f03ebaf9ba3807e1a0376ba62e778441caae530d3cef1c8f45103426fa8cc1cccbb7c97aec6c6c1bc0ff1764cd02221af0cc3eeebecf55d594a99331de982a402b007bd184d39927213498947a367972da311698ee61ccd3b98f7ccb6686a35d76b3c91968e8f05680b6674fc23d272c302b48529741cbda8851b18d1f0703b69a765479116634c8c98747ba8b1917e3d4ed2ef34d296836b129624b95dadc652f8aa331bce2967b03ba417cfc038d0d238f5d8e9c528cd2d4def30118661d7755d38c7451189e867a2c44e27075a1a877eca3baca55ba0dd01838d3eabae430c7bde5a24822ed8457f5acd756d3fc80b677762fbd8d86102ab82f0999d4d5b3de9e5c0aa27c6915862fbc8b8623d07508de3e24587d7b2b1d9bb862221417000d515e3c8536cdb694024ce6013d8bb6bc1de9fe66060e660e0dd7d429b6bd50049d776ad9f1974b6ed7eb89476ad1f24eda0aaf5f3d30eaa8c60d2416094892ce569ece130677e8e7446758c4d3a1e6503289114a585f8cdbfac13b31d48b68c42b1dc492436106ad7aa01194d8076ad1a50d13b4c0b7740b1dd770963eae5cb173679b9c305db53e2217938bc9edf215abb6d28d4af52f97859a8c19296d702e6e056ddddf1c4066fab0e543df97ee0a2488a309c571c5875dea17144c7a9b717e1bcea7c817e4474fa15e9775474817777c7131bfc75120f4c33b5c3201ce12c7a466d8de19c75d6992de54a0e01732d311ede69c162f50f396e88c56a9d528bfe438e023a3a98ebf5f1420c75e373dcf8d295d7d4a4eb5337f255c76bcdcae3757d3ae6346b45a42c7f41e9fa4883278d5d1eab19d56a562e2f8ec73b3ae63420cec10809a8fea8946d3595412d95aa19090000007314002028100c088542b1603ca04892e87a14000b96a04c705a97685112c328858c21c418020c0100801190119969053fce25c4d67c696e42f064afcddd82630290084fe9f6fe1907fdc6de405449b7eac93309e0efcd5409c3bb086052a7502ef3c405de888854c4f1402053bd0fdd73983f31f8510895ed3b74d4d62063f0af7fb92638804a34ed455e8adad1f42e62f2509f83fb4ebafa34a145e534e6f630ccd0d50c454f561bf3c6be0272d22eb3a3a56719a2de225601ba184418326490ff2c5cd48412b5b81731314dbf194a0ff39ce45b89f6dffb3ac68b383dd040189937fb22709881d88bcd925561a040415286c72beac136199884c6169178fcc140bfe4f55e2e6eb08d222ca2bcd04de3ccd0a565075b107dbfa62f3be99fdd841bebd3ee37da03bd6e4af0d751ed09f24910b23c69dfb89983c4e122571fa0a4ff5cf74bc114ea1c2b1f5e41fb3020d515410495994b8da542796ce3cf26e0f4cc9d89d0e30326d2d5907a3d5e447a2ff8729a5c728d8c932666e214acb20871b250f3c85f6a603dc8ec8e1142de291f17f2d73db1d63de59c39b38761716a22092fec8f58d38e0874a73f5e6611923450320187eb4d2937181aaa7b68452410737ad446a8a498a865c1b39e6e549f63a33cc282ecd447a65afae6d229ae687493e77245983b936ea9279a1b6767cb7b0e09ca2001d153722a53ed17f7e80c967e66d377a219c6ccd3234967d2d9831cb6f06ff2d42c59699fef3faf8c60a1f954cc0cb8437f475c5db8a59c2f89e00f9e0fda5f646b3dd7ece111ce13741df070994bba98703b1deae87aa97e8a99abc4181a718dcb8dfa7a3e1af02b2d632d0707304b1b899682c67cfa37e8c27a249a3ec5a862be4aa9f8251efcfda6e03532f8385b9d570f673f596ab2e769213a6b334ce3ccae710603637a20003613ce4735657139049cf61e69fedf067d387341534b72dc3414cb3392182b4859667a213297277a293856423eae55378512a1417ff6d79206272bb9153ba96c2d6c9078cd9157653025f1a86941cf08707d9743067a2484a4ddb7fb526940311eea8e90dcb74828264e016ec2c173ae4af42930768e113b5fbe0ebec912194a3b6bc0d6b18195aad101895050ea84d9670a73a49da37d53e33fe7222024617505f196a36618e341937546f7cd227b2910a8edbbdbdec3d705e9c3dd0245dcf8886b03330109e856d262bbf018eed402ac06ca58c1786bbc1f0f888487b5ef21baeb59914acec5e5ba80d0932a7c21403e2b2f3044e7cd01a81560eb1fc729d98faca61fcc68fe6b6f1b10389da2ec8d3f5e2de38d1b02dae8be3ed4d03c525f535656308a5465b7dd88ec9228380b1dad21161aecf31c8492d388d03e158d05accc23aba4167e55adeae94a6df3f672f40fef9b175be2eb6dce4863bd1e1e83044164cad974237140ffa5d85bfb31983742516481b8d0958b323fd80ba83a8e998b67462581870b3b557bb25a2576e557719499fef73a4ad4d773632ca0e71434d5c3790c5c4fc3b290b3ae9bf1889bd688b6e4c9660bf8d816872c61c59c0e5b13019f3a94618565bbb24ea691dee98b730ac085887ea57ab0a5096c476f0cb13b47c4b6895d355c810fde0212f065c024e640fd06f5c6aa160b8f8a8bcdcfa5692fd2a5315298bd474c37d612364f52bc936f1eac7273bf64aa414629f537456d1f5615e22d0e4889def116675da811f3a66e3535655ca8967f4b11aceb8a9d81ce9c7bdde2fb84974eb0a46ae0e68ec209814738b22e6211da0482abb8eeba221063ed1f589ddb57656956f2d59c53ebd690816b36732f412967f3796121fe19167da8560d92515cc4c6003343520dcf3fd88b53959c1d96b9740e88375e9ef38cad4eb1a600033d5d5474afad541d12d105b6602e7698c20ae443d57a995149c390c62aecae237ce5d448ed0028f0b5e53496f5376b8499840aef25eabe40fe99825c7156c706735d277500f7eb1daecb9aa34808a9b1c5cb206c805cdd311826b5a736d93f4a667015a898b662ebd149749e4f7b28dab34b33fcbca9d28cc8596d93db951633f6ea7505776699d7d88210c153a3a46859cd0dd795e00aa2905626c52fced99d797594772a9c4baefa10247e83073597f906357979c7283a4f2b777d98368f8bb9c8b3758c8810d75b3d31f49aaa683a4c6c7ac333c246513353b54a9965aba88a47f39daacc190b14e946fadfd18f1c0e2d722a805a8569c811e48dc2fc4b53a69fe7102eec9d74a409655f613c36438d4cde2a6d08c09d3c43d35bb1bdc81c065b6ad0cc657aada0d2887ea55964ae5a8786bf4eaa6d4400fd820fda2d4bf864f230de2f0e34ddbd49a57919f9e8b8a5e6c6084aff16c0768c1cde968705ac2cb8255a4ff9a2a6970c28917f11c6f22fb10e476a59cb2b5c349722c349f9d57320f797819bc53a5d68f3199df8627c02a46a59afeee0687ed27645e54a6de83719a6b18505b4ed16f2459f82f9e81958a6ba10f95ffe1c87103844892c2f0b403cdbf5772410a38426ecac0be8daf541e316d037dc5ab6a540bd61d0300e2e8741c10bc42db43c21b2546a3ab049e9ba81c246a29257759b12d1f284dbdf7dac4eec1a5ce267574b32ffebc05493ed0aa3532810751a5d730f8f50ac4d33b94c65268d1d5734ab5fc7f73999f20fcf6ab1978151e175766ed1c6de99f5a2a7afe00df11798b587a761748a9502f7b93ab5500563e9ba068df5c0c32f53cc32bd163da377b0829d1838fd53c5cf945058302d845ea0ce29550a9bdc66d24264c06a51ba47456c50e85d02403d2267a17ec9010a7ccbd68ff0793b91c715198d1f9114120066487c779945bd7377c7324aeb50e77362218034d55a087146d4f31369b24269e10e26531497dde49e6a97a0d3a767502d451a8822d4c867d4e2e31a6ba4b98e116de9e65e9448d2ff0904a4c3f8f6b5e6ebff5df3fd7df2fefdf2fefe79fa3d7996dfda07066683d505131491222195936b3127f5f830b3effb21cdeca2659cf9e69d1e8daae8fd74b66d8732cd4f84d6185860cd17b5156343617e5509ebebe98b039f9b7a15dc618d745c16e3621a31a0405c7e3c467095badf50f181f310270ba645622dc574afb7e6eed4f6771c5476624e4103809b304e2f4e636879f11a42d742c0e9dd4e6ad849a368ed8b9d3f2d5b5306a20605df450b1a479cb0d38e8893d75629b924afc343758fad90300ad8fa7bef7274522715f9c027e26811506ac5e751a5313e7a781c8ee692a8cba5af471b6e967ceeb72df7ce5527f99201255d5af246febdf7b505b16d2091cdb306162b7a2f897c5f17c389edda6308a2fc0e6f1830a95c11a5eae680a1c46a0d539b4cff584fff10b597789951cb5b86002317c0c53831d9d8f96f410b4ba8ea150319c37bcae5bf3c314bf6c25740b45ca697fd99295b6ab3e08190fc8c2a9362a1d28de336985a3e42a6b65102c4377321597b13ab33e214c47a8e0f83ff52f1e602d5aee8b3cf1219802d8c323aa6a45ec7f514f84a92f5c84d5b2a742bc90e8bef63b1d80786572ee6ea54fdb56f34b3efad13cc1d9f258f95ab13afb59e8e159926d191b455b6fcad4b1145f9e562f53527bcd3ca08597754fa5c6a0bfaaa08c868ec0dd24d04f2428b35797080a39724f31413b58891be3dd63a1ad0ef31181d2dac1818df623b56625258f7fd7fb2bebfaa4e6b1bd663f39965eaf523444fe8050323289080312dc5ce32b3f9a3a94f54a980e66037d573fe7e6fae42372ac0ee4a8970b75d96c2b0bfd2a2595ca8be35c64154150855e8350309ec537c474266687772066233bff7f151b5ac55d8f2f212b7c335a02923feb09c7fb5c4dc7416ceda71347dc9427fba27af6a5e0c7a18315e6b0985557a033ec061c6c747af6e0ba9055db9a678f22c5516ba919e6308416dfb95a466fd5701057f0894c1737a5ba772935d66082cb603db10138c6dcc2afd63d4cc04a27d087fba2a30ab94a2d9e040cc2ced5511378e76ba88a3f902a3d0b34dd792ce0f5405cc5df3207e4d4ffe85e7082d0a4dd690fb5ce55e57716e73b48486b0a024367a55bab9637daebd4aa291497decf0f4465eebc01ff94ce8ce0df4dc8295c98bce9e9f3e57f9e735003f295a1635eeeecc0694b65c5680068b6503920fb431c0e43b900605bf82edcc5fd9713dbd46b1df79852b1dd10ec2758a0af4095935627762f3ff272eb0273e709b40603fb1c03ee1c06c2202ff44efbf279c02bb4c34207829aff4ddf8aab72b14d6c773ed7b4f6839ef85e0d039d2a521d8c77c6a9cd09c09bbbc860d35d06b141dca20974c68822e3a67457a139ba2739829f1c6b1cdfcf63da596915ec19262a6617d982d234e70419ef5c26a0a5b04813fd4d31594e6b27c1244fa0cfa1ee2b06031610b8a8e672f7a7f5600d7a0a92119fdaa9f650bcb6e55652d34a5aadb8d55f4753ce84f5e442c231f37f06c729fadad008e7e705f10b0ed4d6ba1db935e498b80e4b42b3d1e48871b37a193645754227d777ac0f05a506cd18ba9b2b16cd4bbf9cdd5ecca9a45221e9d243832650a48362f4721afeefaaed2fdf9f28da151820bbf54847ec27637ac257c1e087d1d366eb28264bc707498548e6def1bd650a751ee7e7cae25f285563e853f6b87859503d228b6e20315dd254eb840a33bbb483b9d7b33b2b33af60ccc26715100e9aa005f754a41870aac9ba6d2d6d6c3ffe010dfc554d1c6a0cf706124a58156a52f27baed0ade1c7c4c18cf3a3a7d136f5b37fce544405a41451bfa248bce823330f7a2fdb5c4419b242a8053b96a1205719333034f6f92a3cb081a5a88a71d6f475474205257f124a3b4f89a3958e6e279b464eb23f80b3f4ce9f1da13bdc81661bf2ee3bffaea23162b9306c6900e917e07222011421cf8fdcfdfd5043caf7e1c98b4b29caebdbbce45b3a9140d9d24ab2348dc0b1870085b6f96a39d58379c454e00e54cc6676c0b053e7f4e9f00327185fe9064b26a23df490760e624aabe16a82668893b3db2c7e310c8921f02fd3226c55decc336b60b5379f83aca5a9145dccec3c1eb7189def9762dcea975f40adf9a21c79ba9a382e0acc9180b407ae07877a03cdb5d48715a1faba5a15cc16db4aab83c503eb238bd84514f89647824b79dcff8fb04e793784c8508f54f31ca4fb0f6bd2e3f12f75a97828a5c453a9f375078818e7c8261a4ccaab5a93cc9bc81cf9f84e3d554b30e60c4d73627446482f0a7f5ffda821b1d08c9d95a2397cf990aa3b6d739711583c09cad8a1c0ecc8f1fd2b555322d41ed80a716bab5a6410a07f2f84e5146075993b0d469bd69b7d6141574583d0b21d903613c2fff59bba33ea476bab62f1cc99edf477da5875559f5b0f4e86bf65e49638ee1c69a0c419362060fc3c72b89acce8e9c6b7cc5f80ca034c0de6b71efffb105dbeaf2bcd1cde4592dcafbe7787a3bbc8e8450dad94f7fc3f0854c535f967aa5974bbbf0f786c8a720aa2615c784fa21b9cd7585fe0097d71392859d99bd8c8f38de0d20d8b597cf95c9b91abed27d7653764323346d8bd4c71cb1c52f507c84de450a46bb30b1f474566d83c7e6224f74673f8b3d8aae64f019e2ec768e6ff6ec726db607ccc0f553c30d2ce58f1aa860b112e059d1a8846caa9184c21f3c0e4491aaac79c6f40a86c4392b11618a5f365b781229edd98c4a4e5bec22a401224ce70558da296ffb4f8420d67ee0fb327cbb84cc10630edc1f5c5e153fe888a47f471f2476c0fd030905c66e74a76de30f19d0bbedefff00bbaffc383de0f3ae9680919e31ae40e7ed172fefc2489c7ce07bf67fd9007bfbf45254ebed67992f71f3070925d84dc0e4a05287f3cb2c48b30899b9ea1b7d8c861370440a893cc00600b964514c8830a9bc7a4abcc29dd1149c1f4f78bbc2e0e5efeafefc4cdc5d6c2784377636cd93e8e73bdc537ada8bffe2679a1f6b552c8d9afc8273e307a631ff6a9356c8a24cbef119082c0c12accd42d760fe392fb42cd1782eaeacd042c803b95956c532a2ba6025838520a4d4dff7d235a406668a5e05d0ab548ad4835d6e9774e8709e9a8cd0c24f71f1f4c5a33e1a97b5c44c9abcd82a95093a4006b0105ffe430e47e28229cc2452e981b598c6426c6d7bbbda7c5fd1476b0f5ae602e2265835d2123c2b5449ac3bfb78c039a4b918a83ba808000ba1c3971644105c7f9113071b183a24aac97eb8189424b95d4f8359812b25d6dfff2b1a30bfe95a9ebc5ca3ed3fd4196759fc601a1f94f5d61b5bf4a4a36e6e0ebf64d12d8202de4946e8e7b968793c533fb7ffc0987fd3f5c4562e9595162ba0309d77c6f0617d14ac61a79d23bcdcaac193853050bef87a329ac9fc3976f8bc12836e51a3709b968dff14478ee6b7520d91612c56fe162755dd99cb699e7cad65a0504cba23164128bd93b32ad9881d7632a10ae0875166dcbd9ab2f6963432582d5f8abb3282c53dd71aa38874b8978e31b1fd2bfc7fb6aa00adf0118c62d573380e37ff9a07028d0352276e3899e70cf12567581230bd20c474ec6b062c33758a60f278d772ddf1608e17992b863d48fec2f3b6341dd553d43bdf2fa0481876f4a87f55f0ad03535c52a87906551b2a4f515ed39065ea031ce5f17ff820480dacc2e810c998a2a0363fec47979ee624544b17c29f398335aed828c122e5bec01e7c3968f6729810b3216305569d9c00679a144f11e522a25ce06792ef6c74b726662ad8e900dfd4e077224f44c4207a099cb6fcfb1622fefe750a4537d13a2ba335b1ee13c67bd0eb9c519a0c2a36edf84f7770bc4da6ade954cd498d4d755868ccaf5bb0bd60f39b686076b015077b57646c6be5cbc24cfb7bbc94eb9a0281c7ea4118df0e9a40c6a2cb35eee7bb783bc0e7e43be24046fa79ea027dc21b51d80acaa93e78a7c292eba828177641d7538cd91791f8c1102707514f059bd9522120d8cad12f2d50dd474dd4e435ebc5e8a6b265504f16c16054ca13b3d701bbd5293f11ee1dc02adf67ab6a6e93f5c55cc2f3a78abecae6e6c7b86e696cd0163378e814ba348a5b577537097fc84ffef286563073b578e0a49477f529c4236ba729c18a792db7e490654ffb96969cd357ddbe1f03ea69f7e2c353f7fc18d7a4879264113907b035b9f4b5a5b28fb578c68637d45fc3e83a6e17f9a86db694d5a40d4dd31a306db0264e622e2988f6e41ca7a59fb9703c2a6a7b6cce753b7d0104c12934dcdebf570d5ec96a055b91f9a7c3bb6c5950351eb8564e8301a87725fdcf3e7ae03c0572983531063fb2d23b8f718669d026e5b75acf96cc9b8867c7fcd5e51c3fbec5b634e94ced42b2244fd0fad1d5cfd319df189724fa6c0c3df052cb3c528af3d945f845d228aee948a5917ff41c29a21cb903de3b71aaab49a40fba115b72d023995273572e1b18698ed532f4317e099632712992a550149df06b060095d534049bbd73b378b1bcce360ab11cc72f67e0f0becd51ed3c5a076cf08da15122c4fb52efe25db73ab6e3e09474c5eee11357b601b54c31ee9ba7a7204d37edccaba760388ba304f6dc36d4f7cb681b8e0a830dfe4533cd334fef2ae234cea2d4a3df7e72682c50249f851755475de3699d69d719b5ba934711d0c3fbc0593211014cae81ecafead337a0132f2ebdd1d02683bc259bbf052e4cfae16371061a48262e65342a2c18e575d9a08a0cb3a6743bc9a33b663ee40cb4ed6537051540144d85fc7503611b584330ad171c1829980c21ab10126a798d89897a94e181a008b9d6a8cd64071430e20db00e5b18d7974489cb021b86845bd401f808121fc4c9436b1e8dd3895c1b699cef7bf3d68ef5256149b5f26c86199c535e4105c0b5b1c611878da3d7546a76f31f74733c2f32c72dedc99d018392b5fd65b5fa0acd5cb2bc556196152ec4e90e8ba06020ccc4ea915c1e326d0c7c4aa748171b1e2b4c84f98fbecf91f2301cdb29f6d4c66a5ff92ab5e4bde2cdc71ba414a6f4ae9a6424b3e3fef1451c9538f349a5f51d587864ebb2a5d002c854ecb522076d01d588ac3e7db1dd3cddb61b16a56e89ac801a1978a7ce6b9861f4b180452625108a0a64a15486974aad85d6932b374512748088f54204e5d34ef3ded8a39a9af74e1f93e44a1f88c32daa527b512cbba53baa50a7bba02c35ba192c749f63bd4b0d62a9c01f552c7fac7f75bd2b74f74d7bbd97d053d0706461794425aab0f677908531c975161e07dc969bf43762035c538584cc97226787aa192d80c0393cbc4dbdc2c39091c226ecf4b11fddbe076a0cb3eb94d2655e9297dd74bd05d299a44d1c9a6a57a15a9907c62d7c843f170480c860a198c5a6c195386343f4d52afd38a65a8a4c4284320cbd67c5a7af762c7b705cb4a64844cac87f0518ecbbba4797482762d9ff767d6fd172ac3238e0af2e2b27b289991898b5bb7d7f07e7887e1d6b5bee4260ec9b9e3cc9eb95b23c7744020d156b37c53d726c074d800aec303bf2976702d1f479e06b6e381f0ef99bb382b59801b0ac16f442fe89469cf3a717b35f0126f97c3d0bf90aab4ecfe3f74d1c6be0373bd4dbd5f272b744f5c3aaf7aaa48b77b67df489db601f280c6e53b498c052ca8a6dc478113317562c04049ecef91cbf287e245aca0e30b4d4238dd4de60b7c6d8d325f63ae1639f31ea9e6df7751c200d9230b9712a179cd2c9c0693fd003f1501db3a9dd1d4835f0c3d29331747fa463e6f0943bbd09a57038d909901d472a2d6315dfa68373561fd31db4681eeb062dce3980994b5babeb26132ebd10282225fa2c165f90b297c090d95237db8cf017853ce7226aab7d6e8fbc6e47b3ecc0c22a98603143ec1623aba5cc5dc253076bd80589721d6d189645e0e577180994b71d4046283a011b4308a9ac07773c98a01ad018dd3cd920cbb8d511439c794e216ea236b01455ea7c045f93743e2b4bb775900a8a8733bb0e5030391b08c538f433b80203a57472ca01752387520de00d11f237a5bf9540ad2b0e69ecd69d6910be90cfda09bd6780660ced560856e6990ba265a3bd4f89601c24f30130d5152b9e04ee366c0be52083ab7854112f65527ac679da3598e59e2e4f45d7e1e34b5e0cc6ee688abb0dc2ff79e897ee2e9422f4aad02da1456787b288a3914f1e0028ba6cc22ed2c3bdbb7abaa2392216d1cafc77206484ebb8d98e52dfc901fa7fa651989e695e5e110f508eb740288414804ba262e8aa192a6894d7c6d70ac6bb94c109b02772eba61080d065275715d7fab4629c6d47d71fc4e4ac714ca0099e60754346c0bd5113c4302e2e03d38ea97375cc22e2fe7ae8e06c84b1a24817244123101390cbbab48884d6f84d95acdb8348d33bf76cf464b0de1b37668e51897e6840e9320d698e71a282d4bcc9ca102ee7b8516f3e5b749363e18eaee040af43b4ad31f4434d0223aaa1b845ec1bc388361a1e26906b5bb06173ca6102f329c6a61b246ebfcf4e90f801afa09c7057842a02b90fe415f4b6681e7ff79a68c5fe9c1602e2bf4b488cf5ad9547d12a4a5c9388a0f67f724f95db4a11e1c9b40446fdee12b52350df6a91238834626aced96f5bf8ecbb740fd0c497029e14ce163677ce3a135415c91ba9ff9f91f3b466af99259ccb3eb8c090672bbb4017c4acd0b3370f0fc4b4ad34460829a349061f40005d6b69de758ba46dfa81dfea33840ccd8708f0500c635587f165912ea8840fab7f0900ef4c8c38aaf238572dbcc7650db07bf8b8df38273147565204910b3c9d4311f1c6d943083907359370736370e18768835a00b2360346bd777769b06115b41ba6354f2581d579d35f1a7aa0be7db747da11d5dd9a3283dc5d7265bdd3b91a2c08df78dbd3b0d67b9cb8c27a4f2c3209382cfd5fd2b81114cb489d41c82809d9df2d6222d9b26cb7b963dcec1c77f413a69955b61412744029cb5d8429488b9029cf84755fd47cc53185b26f4b8a514eb8ddaa893167d3671e34c5844dd9c79c00d410837cd80ee18f2b8f96a03f485db70f0cb075ce9ce010f2bdf82121094d523fbbd725ed16179423945c14434fd228c3f3bd9d21ec0cb39ecbfd112120248ad1a60953d6588a6716473cbe8e9bb535e546b9c511498e71b20edb2116b5ed4bb234b7d3cf615d031368a2bacfb425323562b62a8a65b3d3c1235cda8767e6e788afa40fb317f3e914fc5935d7b078cf0d8c8910d0a0309765afa07e96c36596d314b68ecb9451b07864da61c20418fef35a2048a340b0cd646dfc44d981a09444e5b0571942a05921a580fddb951736a39c334552ffe6b67737f6e2b58c1d974e49f02e01a6200c61c521ace76778a24f538bccc3767e9a3db4084f80f554b6e1eb445446c90cefcf5206187bc114c5c8566fdd57d3af1ff383494029edf0d00f6fe2153c4e1e820297a93f18cb72742a7fbc942368175376a093f80b2b07a63c9fc428bfe812949db85346b3f4008eeb296db959d21b7ec7d20c5073740a4e40cfc5e5370d554e9cce070806123e4c69d93b11f8c370d2812cd99315e1ca71d6e7d467ef48e4ea8465d1d943b4c5f345b8733fea9f80c2c0d2d07b547cfda7d7442332f0f6f36b2ab2b89bcb484e1a886dccc1ebff08d519db20f7704f1a86bb1d2c235ab2bff8dcada97d52d8e78e58a41daddb4af0f752db14c163687d6b945ec7d4782d7821b89285c532348ced412e8ec51051db18d75fd544b58332c7abbe4dbdfe780f89767038f696ae4dce6ac2919a4d331512028e3f5533c628fd805c21fd4060ce9050dbfdc1f913d764e94389add33a2049bca12c393d9d1d8b63d8df84e256f443dfc640a72c54a276e7032fc6f4b66fd2a50f0b0917022c16ce8d01056811ce4b86d041ccc68918e6976321d0d8a8ec7b212b48cc924eb08dbd755a343cd1242b82d99b13d2186a8fcd6690a7cd621fbbef99685dbef0c65a6d3611b88573e1ce4537032ca59a3e7d48465f31d020b08834a2c0b8083f3b673a69e891c161f5686f7ef9bcf982f10bf69270a2971e449b5af9d825d7acd99eada65bdba34c9dda18913c5a5b5a6980d08131f0e5a12fd40b955227ed274af03db2d8be35f0c1f3a52e93fc8a92f0116683d818a64c5c1551664e35b5caf25e1d6416a4a228a74c3f7e6a88074e43913bee8a5f1280bf69320b76690e48c8d20bccf5503f2346dd8faf6875bb44b9fedca1ee7a30e0d26797d79a8b28922b4e4328d1cbe58ef96c09aa9d0b559d10749e6d5b99f8fdcc8183d0ce4fd431ff77260f894c5a8ef9d512224222cbcca1cdc2cf0f588cbd6b99c054b80100848c03bfac4b6c24e51db09c98853c916051d6d7a20ecdfc4e16a71805163889bd69de704eb06813bcbfcb4b2c8ac58987991bf06c531f57bb10cc8fceb4e10f2d93feb69b72976638ecae5376014f6ad545266b11f3357751a0cb826167591b2eebb81c049c1a34ac7402ca0e91f3fe49efabf5fdf6ef4bec84f1d7d3f184654816b54a58f7d24986a0d5c9d64b1862177b81e84fd445864a108b545b4d63bf715a7d49bf1712580d2aa77bebafcdee8d5f00ae25b3b486ab5e7b0a3a42d9826f7b592e626e786bb6837c53e80bd6358530adaab1fab3f26c34315b8282a03de141b067ae6c9672cfae24f0b7264730516964f063554e67d739989523cff198d14c945083a1bd37a32a529e3d3df5e27fc08221aa0a98adf3257789d28e88cc149a5308af02bc7596f2b629cf82dc430503978ccee887b96d3c2fddec5695620897c7cfcee43bbe4a8e564c94f10b3b4e4848e461a0c742301a0d66e3642d9cbfb22883a3a2f03ee7382e6ba5750e066121b9cab395c378cc628dba2f921cdd64a90745f2acc9313127ee0f2886df2c9de205f4da2e48e83f43b49b009df288b38acfd7ad23dabc600d481defe1998744df12a8311c6e20f5cdb3e26db78aa4e9ff7249571a7be01b8939e559f2dce25bf84e0e7154d61cc7bfaad8271328db2f1a2b35c2c9d06adb88e51dcfabec66e36d4fb790e2bf3c964c695f0da0dc0486c6473451326567775a3a774418790ebd9a9630e32c601716c90376aa4260549492c89068ca50336ff6734cb354a491c895f3a3d8926309d43797ef3d7305b37b9383efd1ca9048b6705dc878797d8f974a6f38d67743eec121212d61931a4f1ebca494b8bbcde4d519fcf9c1a4eeb9cee61b4b1c638a53d2cbd3d879de805bb90ef1265742e6adbe3470cce0d884bdf9527e53828ac7e8e2d5a4a43ed82550da3d259154a817630be14f00bca2814f355bd8a801ac32b82de68342c17fb9dde9e90845c06f5a3f99575135249dc239f9b306b868d761b7f8c59add675be57f4256759a7d810d84f838844d97e2a24d4e37a0758b0225126fd6c5e51261f37b178ff60b033193d13df9c2b92fdf733edfc98f6f3e7400ca4fbda5f4fa24cf1f75ff9e9cb778d5c0295dd3e5bb93e31732aeef9e9d76e29aa4de23e3585e436eb7d4e2673aa18e639972a6edbd6d0edef651fc3cf014d1a5cb8af89004e9954f8948cabbd045a6cb4d5a0fd189e082697f240476ae4e8bee37f9b7026ce3923367be67c518fb1b2686af2f174290f9b213408bc3fe5abd4f99fef473228864ddd130e978409a824476c95741f644f84ffae3cea6bf22b06c7ea4e724609fb8c6d70fcbf49ffddf4413307813183e23c2d2a9faef3edb655628e8c6c20cc11aa98d092f25f1937cb244956a55a1413e72315d20dc0e3da98bfb9631906bb6bbe8d1e561466bcfc154fdb7b30f4b632646a469bea7bff3003a6659626afaa48043942e4b885c7df4024074ab9775556fc367ba7573bf4a409287354c4e14bc1d6e3f4dde9b32731a9f7410822bb4ac403af540603b52daa7569b027e74412086ce82d36dac96819b090b5fb58fc179ab2795f7a033d97dc9a0616ceb3e77e512242643df0b8f74b44bb6a5515784675289de1b488901df9997d98fe7b70ba7ffffe488f3b06c26ff8abc6d03dc6232f02ce2b88be0381f29da92c6f28641d13d654d975055195302089ecdb4d2920518c2cb3bf4db918f97b2ddc735f4a6779c691a84f12f34fbbc486137ca2726f150538ff0d5bf9e6ffd455b16c23848c9a783c1c373d824b143f334b74102de839668df23b3d605c054bd32636133d3764f654f65e65f4d42d12138fd03695d3fe6eeff837d70a7b1b3e772f8ba5a7933c918e26f2d11500bdb08d95ed5b908b125171eb8d25571a676512826b54a046978a9b08b4694d7c23ce090bc08f1654313d009e84177f0cb1f110206d1f20bbb7128d362d007754991dff5599dfcc29d46ebe49f08264cdb8d799a226d2356996a53649c24b65b9cb066a02babc68e80e3b244bceaf7ffd79a6252be984977c378553c8be33a6a328bedfde55d4d6a5b253e8e02070f3257eda6ebf84042dba49e1a7b3f2c48995b53317e7418c5b59d63c56a838f07e0b74e4292c476b7260a5608e6848853ce434d9f460823a5fd8c7c716267cc284abac8469d39d3d24fe2d7e7f5005b8ae15b29e53997d2c9fa619be632c15c3cf60b5e0a82587ddda7f54da029a5a1d350a2943e77db1a9209abf95940d28e407ab02b5842dcb5c560435289874df76bd7a800a8f7759d17b6c06b130d8a025edba97fb4dfcc50d9de2da7ba201a091ff1e10fcea83688cd2089f0001a1bf00d1a9bf32ca310c3a7d35c60a4fe2637ceb23a67e417d04af65f655406c82024132aa9cd431ccd5f03d3f312fe17216985d9288d3c41940f2bdcefac0c4b56ed38a92f357fac24b47ac6b8813d157854c080877208f12ace980211651a2cc1c6eda7e7937067c6c5f37b4ea85bb991de013c156b531293092b2e15f2d386ad3170b6a9ee53b82f9dad1a70204ec2ad488311dd1a3a563d8e0208f61afcaa9ac893ce4d477496da042ee27dec38ffe64cf83191cddefc511dc9d2abb6a693464025fa20a644f08de16e55d0e7839cac3e6811d6ec4746087719c58c4790d5f9f9be72f43c3966c4ebd0effacbac5feac369c025406b426b41517e6d0185cc94247242f31024a2ff04942adb5a20407118982e0549805584012ec41f6584d38b1aafccb2f0f9e559a7397928f1ed0fa0a2f3af0d29b261943dfa3eb707231b26e7c8b9ed2cf0ba71e33243ce6145792359156f5d8acbb9585845936dc60ea00908074d5127f8f360ba9a0648e4173c943363294604cdb9eddbaa9a457f13daca518615d7f91ffda400100f85682de2ef98e0255a155f57331a98e67e6f6cd008fdee8e25f7bcd37965374f2b151bdb3b3f5f1b08363734845a34f2d326109359315c2cd303166e8906647e01ae9163e8990fe84185cb41e9ef72b05ba596c4ddcb5ee0f09d7cecb6677df5a06128e75547ba170cea2252da7723560167a8407f717c549a5d5a27953e68f06704887e1c6f5eca11c02d7278410c29ec4fd9b910b908e816736203968803c8c19c9b00dcc5aa6d3425711db0369647771fd148b06283be19bd3acffdff32e384c56832610f5d913edd745254d3afaebd102acbba7270880c7ef6b109ded8474c4ac9fa583164a93785c06370b33b9b20cd1f32f20a2a08f80001c0cb96d68b549fe9f60de95f153506539244b000e15178bd35c4709dbf9a0d880a1a3138bc94a85150bd3d068e84c9d937bac3e1bd4b17b45b3f83b29bd91c8c8f9163193b7f86e01dc14863a32ea00ae0542d1795e9cc9319350d1288e493aba318bcf346c2f58ae39e6cd8dcb5de2032e0e1d1bb5eb891545640df9e4d3b86e104fd7f483fecc6be8460393f02000008d8a3887f84806d2676b0f4e96175b3d0321120195af59bde2893f278e2469eafdaf065168004bf3cbd2b993c27e1472ae8368a42ecb724e8da65c1d6e493d7a116516f1076b6b8249e94bbc0ac40624293635329faace5aa7874e627f586b2b1414dde529e20c624cc49a9e9de4c275d4ff558acc9367a47e2330453c1a0f18dfff2eef9c923ab607aa312e2ac3243e0d073398eb9982281e4284e12d80d29023de142a1c3106c36aaad7d18f6a78c9d8b0c38937075533f02fe8aab97ff157f7c56d1b0e495558e6c5daf121e2cc9bf55e206b5499b7937a8c432f64f251bdbd0f5df16ef151cc067ee5207497abd5615fc0c59b308e4859a274783e20b06865a3490055d8f208be262661f0aa518e27a6195d883a1d3026b9588281853dd5343266e544e006365ca67052e38a7af85ff8f7d7b9fdca392c7c5c5bb5aad22a1b8d7ca83468ea2916753f0d00a0f9d7755fd392cba3c5fe216a5276fb07b3faeb90387b1bd2d600dd151979f03bc06f4d8354b581b869765f0361b0460a2a9510ab1f6c6efa264265157d20aef3fd5f74d4b68bf9f2f705c90a9b42343d7c833db74adbeb00ecc90deebcebb1d42e34af786971f06c305a684cf0c16aa6a24ada4917253945a227d5faaf7bbe902c9f4e489babf8e84dc8ac626468072b771f815e0e3255b532909964082a94b46aa4cdcdd9b50bc783503a3021c1c8a83bbee317bfdcfe20ee684c573a82ce0712b1cb889b5a949b30a350b5895c778c022f990003fc480d5620191b0fa6b367b959b1820409955020423f0a8a22036e7eddd3ecb2df520f7a97924dd17b88c0024059ef0accb9f90d04460eed262066aac843eb85e733c3d5864db386c89453c974938e23fae5ce21efba117f1dfb2812b0114c054fb7c477c654734a8fd9f3d11663f75ad3efb1e3b286cea85b90ff8dc8b494c358373e5c5862d3c565d6f79d7d5e20da49f0d343025103e3acbed3761d2b65bb102d48e78aeb001ca9b120658f997a2e59ba9507dc4e6b0764090410857883f2a2cbfa3ba87365b5469740e504835773d9819e3f6d0dba35aa77688e89c81aebd4ffe427bbf1cb603fc76b59f6dd4ef6c053634bdd6ba945097ace2ce36eb8a3ad239e55e7938e2f7bd3852450bb24fb24366cf4f9900826c9f38d944d94c4b05827007b2215848a6de0f14608faf597befd6a261fd5f426c1b7762dc37b5bb5222de0315573765703dcdcba31e2a1119c92717302e970cf1df23318619bc2adcf19552381b5bf4d4a3e436d8cc060bff03b31240f0e5ac99e3bf74fa393c504c05c1cb0814cdb3310fcd12ef4c40b1f9770896022f4e260cccefa55e14f61daa7dbdfc335ff3a9c7549ff7b930bde298893346476a76f0b6b8c9c2d8a29f8efc73d67bf299c96eec0edd2f498c65865d267c75e868bf0ec48e7de46f2849350318fa94a2a68e54027df45b76d1f4a59226e1474e005b0da668d481f3bc164c1c68fa96b963419772267a313ea1b108d53c39d157fd713a683299436894dd006014af337d8fc4096cca75a82f56ef89a12dddeaddec68c736bd9e6a3f9b585b68d071a33c0f44fac018dbbae3de226d92db0741dbfb1c73687a80a1428c4491a4ffbc19c18894e5d8c3a7c3e4545c3225b0f1656cd6677ea791fdac5e93e95f9431298c1d60d4eb7a1ae92e03a36ddf8f02d46c7442d2556f2b29255002fb1716442b2f6be3da44c05d367d7f1c6ec927208955df1738e9a665fbe8a46868a51e87f82abd4c8072d8bf2e975cd0f8c1a1be00831312950b005522a2b182297dc2c6045c2dd2d794795d32cd4b01b62d5146247d33ecfc31afa722151fc1d6365b806eb55ca9df8781ce1fbd32f3b57a45fd04c70e864f344738efc1376424d7c15fc0a8372460bb9fd3b9505ba8d568100a7368bb14f3827f832fae400d68685a7be90b6ff25b413b19944c68e3d4744eaeb674ceecd7c411f321d8c784785009b578cbf4c84861307075086d9a7f47b39a9fcf58239b54fbbd8072756c6f7ef20287ed3fa6183cb3756d1d4867041a99d65bdf59c54fa5774b0d79ecb01776755db9ca23c31c7156bd7ac1666de5afc620622cdeb2f6cf65ff35d657c24132bbbd208df562d495c6fb4e8190ea2252cb7f9459436dc016d92e632c505e27f89952063a41099503e0a045d6f842b1406e04cf473a3efd76eafc96a0b88bdc16ff23cd01e659d26166936e36ca626ebfe34a2caa7c484534bdf299053b5947f11e96619e8c247fa9dcd4e2950a8203a836cfdeb3cfd7712900014108d7a58bde2ec00feacd4b4095df07a27f45b5e771c355234309f4e6a6468d7b46f21ef38ef542a9449d3ac14da82556148395c0a2d3f0f4ab1da7335f98025bc4b5f16b7c728f1682772938551e005c89a85d54450a367e249d0c6dd86e3527b02ec8b22791f5783082c2f13d421b3501ee7531d2bb478a7bcd0e004dfb0a79a1fce908bf05d844e25559de1f314c9be482fdc88609b00f2b886f06ed9459b88dd672da0a18eb395bf47175548556a9729e32e2213ca6a6120be6eb293908d0f030908eeab2e2b988893b8689c7ca933d55369e984ad865a5d12fc12911aeecfd28c4476a7ecf0108472b459f7c8f6de11155c11e71c528090047310c2a7a9db9c88eb90e550b2db1cf0b9805a8aaefe3d524e00cec18adb50c09cfa63b48413caee25ca38f692f3086c1e2b68eda3048a5379dcfc9b9c54a0bdbcb9697c10683652f07882dabcf11376430b793ed5e6c611ac789faee2fa93c4434e15d8abb482cf3d587849af51a27c8ed49ae4452e0b9c2688b54a766058510ba49012ba901c62f14730808fb41c418869208a2c1fc78faac7d8d7d96c17ead828c023c08100dc0a37a764a123c941fa378af926f422208368dd02058e7fd4b271b61ad26a74fe11c352ef199ddc615cc44ef00b574db5c792da084b764c446e5d6759755b34b5eecf60cf410628e2b90884b748985c5ada2184fb1dd03fad8255a28b366489fcb4937bfb8d8f7152ff0f755f2505c4c943e24b6f6afc90f16959b03139ca3b5078134dbb26563112ceba5d7a2c6e62323ca0b77313af3942e77cc1ec4767dec711cfe777829ec6a59508b463178e9110f5b00f98af42eb584cdcd9f32b15a8df6d0478232ccd70ce2979e47dedfe3349a9b55c36d431a9fff42a1fd03164a8e4641bb9d17bb8651069aef1619cffb4ffc5d584d4c994d4eb533d0a1e3b80a487e7191f7d576959fd67c1ae002975532df9104ef88e7f7cba4b41126f85a9fcf9b336006a4465efe1bd4709445f509247acdf812f8fda7bec7374ce2f1a9437ff150a921048ebab9ce6ecff01888fe3f8a14b0e54eb973f6fa2f00f2f4c10ecc475004723c1eb7fbfd072c01ce64ba0646fcd825c590cd309d94c3d8e0ee791c00d3d7b291b458ee6a374eebd78f659407d27f3bb765c5d956afb62edb1ccfec52717dc0d9043edfb315078051cdddbfcbe1577443fb99c53c93460083dc58285113e8001b16dd1ff50b421495a2ed6ee97902449f62642c736c5c310c6cbca86a532c36853892d353e60f1ac5ceceadd87410c6d3aebb66ec2be500df7e88d0b9a1fb3058848c1e0ddf5de77e7fc108f137ba6a85600cadc38ff3f80107120e0f217524a1d4dde1d7fea1ea9112f8fdddaa780ab15801e40d2e6339dbbe90153c2d0fe0da84256b123616fc9889bfbe06130166fd34755338111a77d06dcfaf27b070fe2f25773f14f008a41301e1a1c71c390107189082135e0c53cfdb3b833350bfd4fcf80740e4b57bf7ce1374078aae2781b4c6bc6cd9196a030a59816baed8d5bdb73f0b70d1a9c3de24d8b84da673b89d861967ed019baecd57816dfa9756b100ea1da9c0bb7d27888b6334f4a1d871dbc55e55b0f958a9f63420b78144f2a637c573be23c03a7dd2b9ae3297767f295d989734ebea6a3145a7b798761fc5b8110169e2a2b032a38f7053e80ccad07cad3788e9d31f18013fbdf153bf313ce9a75275a84d25c01a780aaac99a93679127c5b5cc6cc9f2932d3fac8a770d6b655f7d03db3b50649181488747a5c199008a57547a4bbf25eb06b8fa0c1ae602f651a5025b5ddc1ea9521a7a36bc19156ca143f18a9f80673ae1b28a95e5fc29b4beefaf563255b08d5af78e898f522db22aa1de41b89505fe71e7ff04a20c9d5d390163fef92001145517e607bb0175b7ae0095b27a96ea1f15bb88f0d91f255b08531da1ee47a495719e74e9944f1a4b2d473d8670ced96f70e0055d25ad17d1e793c7f512089b704b46b2b5e3a967e1fae488fc2a5fdafa5404410b8ceec9d69f06af0e74678c7030a78583e9c99e69fde34c2e515c5f88c744fd1fe93ea2cb0db1803b63735b31631b2c1016f5a52804511b8696db5cac0ddd7b6f24e0d305ce170f396b3d6c05721260e7ee8e120b8e4aad2124bc66303a6e18776f132e0003015b0a4065c0b603fe1381bdff6cb694427b4551c83281c5664b43fc8970e21f51014879b43662b740f4d47f123f0cbd4538c0c26aa97ba6bf5765ff6a84a9a675be388d7522c174af1b6872c47fd6942823d29ea300ed02a4e93d2778ed5aa2ee2d48d05eb7a72565c4318571b5f83f3b0c7285c81c3ab17c3e888a18655040469bc091b9b3c147689f145c2b0948967d2477816c86645d0972c96b412b9cc1acac769985d744af35b93a030d4bace6b41cfb502a517bbff04c252e744c3de7eac4101f337431e0fe30cb158dfe531c5792e8ca3f9d415109c73e1daa6fdf8330d88859673ee53f96d3de8c7c4a1956f4d845e2bb5e63abf1fd9eb87f0f31c757099a23158e1e30e733934c8d8ed5659bda6f9caa92b0da8e92995ccd6701bc4e5aa08741286e4606a6b3aff8142ddaa7beb8da3aded62cfa675455d4e79799ac873219355b03d0ba5d093191d86624c06d52a2dea2e94c2a15ec1c60f3a10632707dca9ba3af4dce69f56297e7fcca43b481f96d041ecca7e5f914cfc168cd6f721ac7ee8001452fb26296fd50f8063fc8a6f4643a80eb11109eda7334d9be47c64eefb633cffb784f3b14a7517560f1c05c64d77789017a1151f9149a8ccd7db68dcc718dbbe276acf66de8dc215200272b5b750afe63e1d1e091946d14662c61aae4eda2c1e66f7f0c2e127f59f95156ba08c08451e03a603dba14a5f271e0adcbb5887ddf22bc8aad71a94fc21ec081c7d27a5134827da6bc7ec3402e4c0bcd2d59709cb24b46948376a21ebf167fcabc1d88279858d51a46391025b86a6815eb5e27c2833279585b65abcae63554d185df06ea3fda2fbd97ee271ab2a800983ef9c94d06d1eeb11ec0ccf4d20d5ed1ca894aaf4f5d64cd45e9d77cf0498cc5f54993bad293df8ecd6edb8e2b0c2d2ab3c60c7becde67bca6eaa6792a4e9cba207e17833e55c614d1ec31625dddb5fd4fc7212a11aac37195d7827bf1b51fd9075690a83eb34f6c1b850cb75a7bd2fe22202647228f757fc5bface0dd8531a35497f6a8aef5a998419e855458d30b97dc8e3b93f68e27fcddd2cd3fa6059238ef12abb970eeab7eef00397631729d0917d5d411055c8448442e67763821bd853acd8f8b503de14057f1c18fc52a2720bba84445483a3d16cc71d72f44b104b68213790332a1645a8caadb6b8e849cc3026660c269ab2e0c35d150317345a64b5dc3f9d6f8923c4dd2260e59564078fed18abca7725adbe754e0ef9142e2e8e94a18beaaa49b0c4ce2becd86900ce481a4ef74e7222b42589c1957b297ce8e4c27f26578fa213261fda2871b093e5b2911f990a1421c47b04870fd0129067cacf2e0b8fb02648477f9ea85042a16575c913caae6f9d15be21d45a781af7db62c845fba2f62fd15245152f9c9aa4f5b5c38b90a65861ea810efa78fbe2f1ece40476a84947e437789cf3e11e733c42da3114cfdcaa4689eace9a64693b16d55f055a223f486d5c332a529e93736d4f7e1f3470e9e40d50b3cdd5c1174e30e2e2ac7fdb9a2f352e1e51a4e408c6b4d63c9985d88a5c9b538c18aae3363ea77b5b41015fa5fed8cdfe8297cf606947f56b32ad66fabd5a43d824255175b3c694e71ad616f7a49b21bcd6e718d8c7795c584be29d0ff05e796b182095ded9dd0f2dced84e0794505677fb2d5feead15964acd0cf5a085646c4a2f5c4d31f58d250002efe649d301a9e9d5d0ca0d30aa06daddba46e5005f5a2cbda94bd4da91a8a7262db713492c0ab3f6cea3c77881889a5d6b614e45684d4d7b9228b9b1d18d54692bd0f1230e1d00268383c0b19461b070327c48c2da1c3af4e51bc42a05e9903b41173543b4cc9ce41a243104d7cfc841018adde1c9d036866284ade62d9054070fca43887a9b001e7360c2d763b73512d6163e8bc67ca09829c5275524ac0983c6abf8e577cbc13f3fcb40d0f3287b9c14bd3f939d45b61c46f46e9a07465b2f82753440a47c6a94a53b6029999be5146abafdf05c774c929d30190ffe1469862eb1d0adba4c657eb5976ac1893533c206a58a462fb41b50f17b826c36f299a45aff759c3f34da79fa9748a8dc0e6d69be2128b1f2e574c6bde83f3dc4cdd7586b4441ab06c58f550055725c1edb96a973529d3f6f1d8d3ff770e3b305cf5c385460c7a200b2e0d51902a9705191d142dacbe12ea7389cdab39ee26d3bc27e34ec8008dc7abfc0efd8292ec8c34a174641939cccddf470567b97bc7627091fcf3f37a9a3da1d17efa6d2dd4b9ff590dd613ba36063ca8ba9ad575414c539715f5b0ac082e1f323d08363a2932869c2b00fc0f20893430f7de0b21d0f8a41cef409afa6db2b79adeec71dc594afd077ec2473d73327de5d6317e8c509e5fa6e4fefc4b3a3c8cfe05be4dd44838607b21e4f4fc7d3d0601e738a325ee266dc58dedf64feeaf23391bfcb0947c9921c34e539e689f410c33a4737bccfaeb10250013e82f2d2fb2c4580f1bbe6d455238ae9964140e63a5df7900a243fa0f718f9134cc381d835340507d2e8216450fbfe760dd468d0b7abdee149b41dc1691305df8bc0579303146c0141eea908ce4e736e9f98b855467cc9212077ce30d42142e3bfc2981c5cc6601091d3d412fed617d7e4a1623a2b75f2e89d5403e60e00661eba1ca24cec59270ed8da12475918bcee6491331927f06f0225031dcdb345360725f25fe843249f46c07a61dbcf6445542b5e99cc6302ec70c692ee9e2f538dfe02e0af02704d273f2807e73d13926b15a30d26a003ab663d8f0f9e6e69e1e33406e0ab87e81e654d24d9a876d31467f7ec9f0acbc567da4b99f5e56917ba4601228b0ea54df07d7598ec552131f33c0a24789fb9a5465933221e4aebada5d4900c02c811b5b289b0dac02f00be12f2c1559e48ee4d6787a75f67f4a50fa293584eb205a1ae2eb2de80b9ab4420831baccb04cc8dbcacef237116780b505f23c8d9e91e0c40709643d1b5a5fbde88b7b2d40e373092c9d7010053971db0babb569c7484fd00e0f99588b6d42d965d657eb9f512cacee16c253f8b782b8605956aca56c5d6a35625c0d07bb1a291ca10fd178e641150b481b6c9185daec36339b204f69f664862dea0f17844082f51bc59dfe458b971bb40c836aeab6f64e70ab622c92d3b4f391653dc704fa078bcf20db5ae18c837678ab17f4d6c5cc8474c23617c53f339348ce2f133d3007878dac8308cca11e350d6f1dd4ca693e24fa738ccc0cca7379c42abebb6e86c48b4b2953305c550e7f13c22d3d7d4f7966aa40282a56a89fecb5a3cbef8bcc9ac72c42a354fa56d7ab152c7bfe122b8320cccb1363246350c88e9941b668aae649f66b3e686e4b02215b3cd4ff7109cd91850d617e5b2209414252e3bc1638bc4c92f15551ac101d63026b77ce4b7a0c8ea3234500b3aa8a5770dedf61d93752e45cbdbdcd2c71adcc6452b668c257f790396be557f86df57d57f718c0db22fc4a2511b2a1adee4126033c449f6598b52122f888290de4a6b1c8379d22d1b2c68420b5ef43b82d5cfe4d0566080010a7856235697939b3a27ccf139594705107be6f7697919b9a99621e7680e990cbb65590f4ac8ad683739c8ed27c0fe4ca011e8a981a84ee8f2160e4e0781ad8c6130fe6e978777a5808fd8b1dc5c3e4632ff2bb4ee600375a0afa29cdaebaf74de2737a12738103e6f731444a91e0a83508e0d5fe7127d41c82259ab723fe8d68c1af39cec96a57917d3c84760051813d0f7003a36b4a39471d1a958957f7e4b5a4162fc2592b0cf699de98050327f87f2b54d078307ffd26137bc67fca320cf464e4c87ed24f353f87bf81ba0d3fd3236b8551e5fb730a0849d7b55451c1e751875b5c5c96c6a17fe55203a4158f75d4b6dd1a11b350109d1040ee60f486ee8eac7f9dd81b4d672e7f2621a5c636e70f98f1883c0e213c325329a590584c04255768c3f34959b186b9183fc1f8ebf9cc34219068c23886d5d70bea3370ae75375002505f3f7b737e31712a10c03f82dc81c8e512ba952056319f9de70d9fb03164fdfca37174d0b0006345810c9986270b2ab29859f2cd7a785c9a6de4101948cbd55215c5e2e21cef59e5457b2dbc4635091448184fc8b7b71bc0eae6ce67d7e793a58d1e041197b624fd1141dc347b2e2a03498ee76b7eb789aeefad5e01103ce5791793540b7f5479c6d5b99a13803380bebf8824fe9bd774f1ee90623981a2239067809a919bdcfae4137a6149803194774ecadf164fbfbd8d977f87e8393c043ff6474a989b63c6355624b692b9f4cb3ad132e02b052acca4a95af452e2f8309720455683cd283e4b028f608e944f52d47be01cb693b943b42f67d3b1c302845538b811067e7c3585c38c035516c5270050b7a2d81e0a7c6243585012ed4c159626f1c608e51611d0d8bab06a2f1c7062455246ce48fa554f26bde2af8648b68fb48741e87f60018eaee12a28b0d5b5aa9e4319243a3e600e5c34500652b8b05d133fa6716c681c79aedf8c93b4dd775375d65c63b0ec16361eeffca9fb93cc0a15dd20aa33add6a0e6044993b5e5c29127193cf3b0354547f53fb30a172b601808f3fbddae17d718abf258702a2c3044f3b103cebe427878d990c687a78056eaf8b2c50c4369359b62a4487286421ca244b6700f9296ce61135ace648d82164b7ca105b43773120e8fd75005935d797b5d4a08a354a5f543453a55383153a35fc57afaa859c4c3d1eb7db5ea14a47af26b25e75413238b6701e838c5e25db4ca5e4e099a549637af72061c843412e6be1da1b2c3c801d2a13e7bf6a2d2e412072a4f801095f7bce61d6238f9f80a6a946aa4a665b5f33e21d92341e1020bfc05c63cef1848c4f484c9d5473a54852315f029c2ccd91db5d7e6a275719c8da05f3ae430607fa50c7bbe66806e36ab0f6287c88aa3dcbae4a25524baf893c64e054e50816704c112d940a4ddf5d00d62fc76288455289e559c44ad55b32aa76c905fcae24ddf503801198ac3386243069ceb3cc6c745766bf14445a3018669a005f73f56d4a49feaad21db69f93abd9974d6dafcd4db389f2ee21a62624184d27a7e937208001480c4d5deac75e304697c1a150b8088d427b5ad6c367923b21c5a3d8a1c2c017e331edd9d92226d5f12a9c828bb9fa760298b9bd8d2320d8fbb5633699aaa186044c19de1f138e0c79d8a100a7eb76fd56c1d01891c561cc7597bbcff16f28ed27a7d9c585f43542c9f08ace66ba3ec8d688b962b2a1aa505fb2a3f2ffa0d9a97338ba46a35627be1431551929eab500ec3b260ea8e2cbb628d2e8b7688a0f9dc0822b9a232d99f03cbb411f9665edd895afc1b4bad1e34eee13d9abb9c8a7d58b9d48c863afa0d813586025cb42a21631f39d92ec402222dc64cdd84d4af47d6000b326b6a9517dab8b39f0c1feb063466a8a422e1fafa2b22b770e618c63ed30877c16604a9a63a326285b4238f7d89567d923039e27d9ac2f1c33162aee3a59e327e9533589425317c42cb55e3a073d7b1d6f3fc6aedbe7075679b2065a46ec910987d631d3c811df880caa814747c5bd46f2470dba8edca1db4ca44a7b064914f09f0f8147ae114bfc88d533e7549e0f7991ff03413c4fae9c94e304e26037170d36414ee73496819e8c6f49adf59c9726d0e65a25b3b31aa8a6e63e228d5c1b83e34bf99cf6f4e0652a9584d028515a2e7d3fd9fe4cb12781f1e2b5a14c6bcd43b190c851ad05fab27ea18a4eb0308cff4cdb6a1acdf413901f9d936f7658bdb2c9321583696577913562c54f2d3e775cba7177b22f802aeb4523033c6f97b45a55e58f5072f996235121aa0e2791f5f40c5e6c15dd192492c8b54a160c0672e739307b837cb8ab73121a3bf5921f6df57927707baadde4084f170d89a85f85357c56381b7b8a8a25730a75a3bf56cb618c341a41076d42503dc0fb07a568da281b060945e27685912cbc72bba989f0ea4f4b84e3099cca41945d25b4d5661abb46267c8ba36373d4122a6837ac37e1053183701fc46715876863af610ce00fc16d2307389df6a8ce411445ce1d3fe7c7f93b5ba20683516fa2af2ec3fcb9d90762353e423f450e060684d230b84c3efa1e1ea7f87ec67b08f2a342a455a5c5bd1e1971901f84c96fcae809669c958c6d09c413358fecd679f28a700071af0fd00d83265714843f87890f79c7e42a1377928ed3ca1577a190bd4da45e1b5d9c00903e49619f801800a508986c651b6f0d42dd4cbfb75c4d33d0122e715f64f6a832688f07b430cbcd000a42dfd2425800a6966b79ba4b1f130b788221fd7dbc71db4df7304426f47e3d4b831cb1cc5a1779aec1114ee8a53fee04031a62d8a895655cc394d87e21704c092ee4325b488c24a6d60cf927c608f161fc13e6a1c796bdd148e6de1fff16ce1974e6c1028224a4fdcf63f30ef0b1651380948c96b836490570f765cf80d614a41389359035d44ec0acca9971d0ef00d60bccbfd5443e1bb9064bd500f715d46f7e8323cd633c5b763a5ee867127a017c9c4398de46d4938c0025c0b602232882e7d6f483ae65d6dd5240030a5824cbb50ce2a1541ec1d33546d16b4af597377d052f40b6f4e6c582fef7e8e53226467755594451ff58805c26929788634f484502024a5abd6476ef46104dcc8d1bc82b5e3225331c420471c377839e1086400e1839e388f476617ad110e830ebacab5d21e56b5be2e0b3e9dbafe49ed283bbeb30fb00dc31b0296200436e9359621d015c5152cf1fcad5b9bcb9706095a559bdb83df2db5a52a6e65be0b416d8ad02bb1b09093fb57eed467850a61a7822fcb0b078644e8aee64f9ab3e79ecbfc19eff71fa83d2acd94d423224c3a132a876c134aa710ab52e8b4209d509efa1f3250b75757f533074431e594a3b0e23c651d75d9b13d5465d0b9d00ca95445b1f34a5322e48828e40e1854552a50fe69bc92bf3c2eea4d07997e1417bcf12d0ede228ab494b46ad960bd600ebf75fee6efa74abcd1af58ffd53f977d70d832a1a73a433fa5fc3f1940276baebeadc51c8728cd4f68b56356e3c5a7b7977888b42f092dc76864f901ec0cfd0980ed77050fbc5229d1ee16b534f9d0821cc7fdb82b69c8685512bd1bc6217ff42546781de01ac44d43ff4f0c92e842d92f694f17b97a886495962ec89b0ce4a4cba436b69a5b15c5e492bb9cc2f20423578a61ec4e400d1830c2879aa8eecac86b7fb1c03a4915b679226afe7aa3fd4d5ef90124438ed34e2bda671a2fb871d5f52f560a8a5614f4ad3e88ae0c2118ba0a7ef094a8f4d11d0ceb4664770691b7a4578134ff7ce047b2b081a5ebf39c3a6d90e8cf59c0d1f8710af0723e6356e559609e1e6f82adbaddb7d991c146823ffbcd48f062c98d29772b8c8892fbad70fa3f5c484948b9d288ef9904e4d353421ceb2cf7eadf1dca232bf19270b25a60a8dbf568552c7eaa778d91a473444e6ede05a3f33b30472785927368225c5e50850ceba9de58af73521de51c8a8bf36a2ab99e24a09f9be7e140727bad82e50b965326361e49909a1f73b5875019d73db6f579a2e1c818233e186540c573a220889188c79b17573279ddd4311df743e591af648934dd5825c65b43d1be2298b93de6488ddd0c0ba9b4b67ca8259db49742aeac19405041f8491724dfb955a6cf9dafb388eb172ea0db142f6db423cd01ae5932005eeb4ef06eb6a918e31d06db4acfb91f8cf32d78cbcc33982761778e074264f323d30bfb2cf4f87fc816544426bf1fa8e601a559243910e5958981d906b79416a0b204202c8f639890e50c54ecf06089a825eac2ad7095b97a052aef32380952126368def568d0f42a7b7b727c33c46c039ef219a796f26cc845a8f7884639a2023dcde7825b326a23fc21dc8b11b9a411dadefcdd82c3ac1c89775c0f79a6d0cf9436afc1b2e34a764484536c59474092f35827a186d17f66cd27c136919689b53437321bd1991412155b493eae55817a8b0dfb63798160e322db82db9e1a71b44812ad2d3c828f9d8419f9a105ca7f83ff653366103c8d2b9ec56c08f18ee64aa5099d9840b677bd9aa402fe39a2e1648e5fca9a0116708ebc15bb018b6e4fb98f56c89c499d3da4a19fcdf747184d21dbc86534834779f02d2af386d6d469f25010c50e943dd8d225da9d5001f58415e9299984e533820c734a3c28d6c42036685acd689f0120acc457625864e5b5bd0c10eb02568bf477febe3e40251159701d404062786262aba2b7c8ecd0a5b46c40800c10779d887ec64aa488bdf02e429fd3aebc425fbc2276d42adc331fdaa02b211f8667d60db09916c6331764fcce652fa4fbd160c829d4007cf8ee1cabcaa0659d8124db0f5134db87267c5bf6cdd1a12355f6aca267dea715ac34077675bf4ffa1cef2d2cd1bac4dce410dd30bdb3cde935e1baa2f1c4630422cd82cd5180151598a03249ab7a16c805b2214cc050d801adb50d0abd9ace9417522b0da70da15de037cde06791a04bc70b6a58817aadabc100752ee738d37ace0c4766d470deb80eba6643a2beec2a73a53df7cf52e921f73befe00dcc1914021bcb4194193c37d60803633a4745f3affd70e35d84a05d76836dd18b827c3ae30884f5c1c432d744aec0eb13b9fa4ecdc8e42d1cfa3fb75258d7d10680425b098a52107a40bbe2a208a99edca09f8380eb5548e09534800c66a6c85b8ba724a6618fdab1f4b322b5412e3b59bedd09253abf2c278e5bf1f93b832e7db2056983a1e95b3f11ae9f25c45b6fd5c15e97b4f76aad15f06f39595aecd139f4160371cd5c19e12538e17e6b403bb44f4c4af689ebdb544c133c68f04c00426e8e7e3628a6d8f06760fb4b4d7057ca99c62194e7fb338ca0d21b6669aaef6ce86567ed7b31a9cd98fbafeb2ab1d29c21a96d3ee94fb7bb7ec9af9bae59844bc022b3d750b0448134d88b91a50858eabd136ffe9127f5a16bad38d8fe52222970fb8981be0f49c57e30ac889bda119a0f62195c6e2fef82f3e804f7897572d8c0ff09455bc6bb4b70f2db7f10427e17c67ea3bf960b412cbb41d81ddabde94f0caf7aa70f2f4e642008c3dfd9eda8443a692fb160d30860aa817aa27be288b88309ac0fa6203b3c580ae4dc12645adc32a5c0dc0301909a9734542e42c94ddf8ddd34d1d200273be31d2b99150a0a990d66bc1b4b125bf7803cfbcb77ab7b4c91464b4483ba009644d9bad063453021e5805e8ef30bb6be50cf3e1ae18127c96a56cebf8f176f8342a06e1ae2ffaa235bac5289354c9d7b3750c3ce5a353d7e20997e04d9180b0be50a921e12beb6e04b3f71489f323d99ccf3de4f621ff4a4d30e8c20e174ff65df7d97a1a456548c92e9b94eb937ac759b9bbe7aa3e2077f948ebb9ec129c24325add580fabf983d559746d28e92da88365e9bb1670fad1621affabb5f9743ea790a62837b2edae8f5ed3c1104d9a72f7b363ea66509a2f68dfc9dfbb56dae06dbdfba92d121402003986fed280774700ff1e4ed8bccdcf2276c401b8ea36b0c572a8cfabd5702e3c7e21d62f1f0ae3a7753d8fe668576a6bacd06b0d17e755f06c1bbe94938fbf1b22f20a4690ea8a38b31c0c4109596d6db6875c0c5aea62150c28aeb3e791e4f7a09478b733832e80a452821502e5ef64ce06a45fa76e7c3b2439b282710baa91917fdd6079e6863eb4ac04cad80e04277aab247e9535756cf0ddbd8578fd1dc7f6dd1db5e26503bdfc92046afa4cf0a0522bef3558b7a08196ff27c982c8f56bc0ca63b2a8b833776096ae46ef622d08da6137ca3aa4f15d0e3c809363dbbb2410788701d93627ec13b02035eaa81cc54871850e8b78b9e66de5838c16482482d4521220646af7341fea11afc9984cc8ce8c33654496f0c0bb25483b0ab4381ef12a4de6add7e4b03721326e8d206c37455e0ec3012842bb99c7fa81c4344910136041e4e3b27420adabe875641e4c6e1d1ac97b0cba8e90209776ef61eea2693d9aef4f20cea412124701652d9c2c58d6dd687d05f62f0f1b25f3878a9066097fa24c2053affb9346904a04bc70939775c008b2a789e83b7dfc09529d9629d35db51d4297a7be7e0dd5dcb97175feb42b73091a6dcd54dd7b839f8e1842d830294c483c99b83177c1ae874d259f363fa6e9c925dabc8705a2e54ce54b3235e7cc214924f19de2df278722c20e9a655d59cdbb5fb2afc63ef727654580a8d234c1c190f1241d2bc3627f5de463d951818501031778a3c22fd414539a500fcaaabcc142dff0fa4e915a1ebe47d067655f31e41fe4fad752299d1a47376b9bb4736619d715f44fe4f45bd24d23c32e226122f7800b3aeca40a49f510f9fffa774d99583d217b54c8b43e6ed414b1660df3219fae2219ea13506550a2808b4748dd77340cdab1af972199c8636a909df6cd779c8b2ba13bb9be5570f2a9181745abb7860489de64b52cbd47ab9ee751ef4c2935553664c3bdb790948ef5bd8175c742df56eec52c2a3f6bf14d46561d1beafba55bea040b9c18b124e8daa7024e711cc2e98e34a41a8d3a37089b2c55ec7654ae5a92b725beb408c477b7e51f2ee0e20bb6031827752d3967c02ecbfc12bb636b9d58aceb6db88ed1f329448594b6b51d8fe9e3849fdf3feedad93a43753486b700b0f3068580a13cf69a1df1b82416c9b97b98679d2c98f7fdff3cbb868cf6628413da24231799bc78025799be6419c2cac6683017324c8541474dc61515f131f35740b7b944feea4005a2b3acac92d229de764cc39fa9e0e0b3feecac61c2700dfa6f579040b64708d321678d8679aff79ac95b23919213119dcf6e440825a17199419052faeb46b2bdfc4f0d4f427e3301367e5ead6077780f2309f61fbf3afc7a53c670cf257f5cc177d9a6b1e0a6145ab34d8c253d467014060c887e3974ac0789b46f45cc3f3858379b4547641073cd96674e50675174d7f68e16313aa8bc4dc8c54f6096a1ea604273d42574b48eed31c076fccf4a2f05b831678e0dc69aee6e9d7424f9ad1442bd6e67c9ab8f2b3a88bbb682950a5ba056ae724eaaa19ae8240144f2c27253d079848d562322504483b6930d97e5c97998921a41dedecce411b5c704b367683237e4b7aa0ca1ef3b1794e79f593c6f07d5646dab9a98dd1c06d22ebc4de5fd7426d3bd271e9038f27abf7c1872fdd1f3816f01eb7e68eef7da2c4ac591b2402e885e4e1b91c32d3fe7db1502583b0220f7aa73ebb566b56d37b116b3feb6c51450b046d52e4ad540d34b3e7816b96047f55c163bc4a928c17543ba3f68f0a29c8bb01bd5491b949a580810558aed5247994ae55723ac3c584e892c4bdb75cdb50a4c9ff2ac32d0684dbd6c5b8ae29d15203e8bf497eabfef4076565821ec525c4278ccc9b30e483d09eec06229f55481787d0a01815f76805eed433cf8eda44f68245f99434cbcb3c59954344eecb1dde8e753340a29ce9797bc00d3a398432f33671ab68f4e83cae38881f00ce8bfdf0737f063456125fa734478c250ef40bc099b1cbf7b43d832fc3377a517c64dc8f7bf30a449fd4f343e81985f1a07d3ca513f97070ae38e382f63d29207eed3cfa9158c9a91b769fc28ea831237276204bddcce5f59f4617e85abc82f8ef31c9bb26bb22298c93cc42bfcb316daee3ce719cd77b8f5c95c06f002a20a22e3bc4549d9d631b005d4edb7deaf93e2654ce442eaae8fb39f991af9f6c72a9240c0977f2e963246c25749bfbe0acbe1c20786befbff15eff5e306b53a220ae2e533e93941b082446549fd1707ca27f611924b67c0bc7967a0fcb8519a3c19f874bb36691178121614700940ac9a74e45d6230e89149faae63cb4e1dcd8721b809b758e8d0f25efc536529f7c323df535989fe9a2b491c83773b5ad7b834b8d35ecfbd7266283742f9ba0422612f04e2c981d1b4c6523a5350487eab8c06b85e82d11d5840630aa0c82cac2dd4a991c0bc1a895ecd825424673efddb0be1648cd66aaf5fd9c5e40c8773306fc2ef9619e5025c9b5df41bc8f7d4af546292db9c9f291c346ccafcb17c3cfe5b4ca3481584901e881103f28956a9a1ab6b08d13100c4b52080b1cb1d655959c34109674a847932e83eeee195e756f669055ba47e3d877fcb35ffcfcb3df3f67267d6d01a94b0969db6f00d8f3b06f1a250ac2c808e1a3717135673027aa080177764a95f1d9820779e58cb4da31e92eb958cc81e710198a9e9f1528f3ad096115cd8f55d0423caca09f7c27a63b488c199894fccc013b4a3f0b4771238fc44e68590e3667dc39eaec7b38671c9c7208d68b68d81edd2986c8a7b4d0233de3968ada2ff61edd49d374d73a17fd20412fda0e0dfb0e75ab1a6fa3095ee38116979c5025d7c8e42cf73eecec2546c14af509273adb530511f6cc1003c48808a1139b076ae9fb222bdbb9af4e2f0abad94985dfa725f08d45b3253fb11fdc174887921a646e9560256975f7168901111d9de5988adeeb3ad0846f1a15eecb2d383103cdb72c28bd7c065b9ed2a8ba9be83882fe48436bdadc9b8a86e51417c71301de1ed99e30761bf9c4ceaac432d17600925bf580fd72105db10be880a38d7f2b29a5297f1fc4bdee2c3f1a4ff4ce1fa861cb5c60b1ec856b93c68c3d7f0c560e26c88875d2d3bff0fe18a5c50b3453b7fec6790f3748374b66ea7c162d2219772bb242e5a904b14b6255e81e224ffb42e871cbd827b88489a847d70b6fbd78ec1e7c08e70388b8ea01787bf28108411f743c0c8d12b73686a7b167d2365e22c24f40e2d65c6e964a521649b3c77efaebf9a14c6c3fd2e6316ab718bcba6c1ee197928fdc229f8a31dd729cd54adb95d8daf317b0390474efc74d7afb03ed65288aa522c1b8fc19c30efd7917b2fef4384642a648d2ce5cdc8376c8e002f775f4f3c639d8d9b889f0ee29de35d15282a96bb002fa62636f05161bad1b65d107a44e6f2dd1837372cd6754f25e4f01d7b47ce2f75890250c7aee2c091bf1987eedfb6dcb8fa1a8c00aa5d81f8ee641e702fd9e26c16774bee67b1392e9a3f2fbc7d4007d14da9f2bd6088c5c35568f0be16c314519472b59d070d196bc12620706f9dc46d9b47061ae627da3bd3ec9e1ea6de95e57a503f2ee9eda5b354c2fe4795c66aa4c7e5d3d775b69eb6a03227004d29f1f3058c665fd3bcc8d8687e95083230cef45e2c1e5ceffcbfa8c8ad38b5cc35104086f3a411eb052beeaa613e109a74ecb366223592a58ea02368b67648877cd09e83f62a56dbcac11a00a90f6a101a36a36b873219aabbc3706b6ebb1d4f4dad594de6c3290ddb30d01018d910e692ac3bfb03b0d90ee472b63895580cc05edca70d6ec1707875daadce8fb1dd759dd9cfbb7a98b9762df815b609facc6f5e41f4068dbc4cb80a4b3809d0cdf14bc4d94087575990069190e79c03ffc387eca7976e81d5877bf635da5d5be6874c6776fbffef1d255ce0490c419ca47694b534230c401380c5e3a1384920893f57882c2dcaa89673bd2335ecd86f9e74e67f00cb2017f0b450b7918acbdd0eaf3af69b16042736bae8ecf591e76de9a784cda7997c00a0bf61f93c4d037a488839c70902f110e4aba4701bda173eaa8844cb2084c3b6e1a1e0a94c29d26d20e66d233ace66e58042aa69b2754f617caa2401639659adf65d61b1fbed5c6d0c7dbf2a2261e4a715be1e7b64ee3775022e8b2d226118d4b3bee40e0e0fd33896ce0d9384ab0e22f7a1ba6ae467343ae281ad9bd384ba7e7c97c9586128e22691243445ffbe9fa0851a5a3e6f731795e5373afad96ef94d9ea565bb48fce853f40e82da66ba4d253dccbe9965cf05ff86f002e8120cb461993fca9bbfb43d6e8789b614502623d9caf371947f4307a7060101fa71fe4bc80c893ed7b194cf499b92979de0066fdbbe5123419b290795c74a3ed6603c156a8b02e9af0ab3769aec6958b9d7d84533a28c2915010470218ac679e874d19e690ab05d16bee3316dfee717d35eaccc92cd09907499d9b340bfa6d4557ef5a71766320778b9cc982dd080c9f9a5be1c496813a3ab7f468d80aec23933cbe6d4282c89c9b8e6df208d78a0a1400e0664291783c5a32f6b662d967744c68b6580d4993d1a9ab470037d2dc9aff5dff15ceb572e797629a0306a6cc7f8c08cbcddd8d2f4c81c192170dffa0d189e97541b20bc7c5a5a0cd070a8aa1a218d0def3fda61d382652af6d1bae174147b7e8861ddb2369a0840c0c67a9087820dbeefafe96fa188a2080acf2c70c4eb6bf99e853fa93adae45c8bdac3554507b9b5a9ac023c057376602b8be405b27a7962bf163c7971f40b4da961273803ff4a115d2b888a5ce7b11871e98bf00ded33f9d2004a62cad884069627cb9ab8d3bc9548286aba93fd38efe483585f412c280d2953e9ad0ba29e6ec2822f14209ff97cc43c2e2e2e6f6c609ee08716f6f89a2e627f1bd09599eb6fd464378f7af8934683c4a78ce14a1a5b963811d81f6928778f047cdfabfbb0a81497e00f682c85887132a0fb4e9b253a30adb69d7682f2795f50c496120a12e1fb15456a3f5a9da6c38bc8529dd7d9b2be23ad1f6069313006e6d4c998a0397d7fa9392e9a2fb92814ca62bf13c28706946b325788bdf4d34665813031cde4a2f20b36d0d5c5e2974f3bf363c00be7750edf9ca18faee7a84b82de02cfa47db24ed5be0c1146857470df00172e82ba16461622faf5b0382ef295915f94868a0cbf8310c5d2400f825f813776b719b9f3ca27655c14e56b0e9cbc7bddd929e56b880a7e66c443dc5c1f4a42b564806e7c8f5cd947a127069a862cfbfd9e84185d670341c7dcf78763e860a8b46a95d36d18655f6d13f1cc0fbc71b434afda4cdc4ad87a7134a65b5d1316e74feb1d9ba701d73e1c0d1046e0db71c1036f121d82875cde2b1eee4f51dc3b2ba1c1d8f63a27018b8b5e71755b8c396ef615096aa0067392f20f25806f40c321ca94a41ffb60f4dadacfd1d4dba8916d756327c3207bc870bb181d21bbc0f1bbf47df0bf2f34e6836981431eac3b82a16d76bbdb22122325805d32f44a8fb810a75493b582f8ff4ef27d047e0a23fd0cc6b915f7a50f0cab0b14e0c3039769abfef8a7e0645122152b2cebecf5f0807e603c39c3c26a5e18ab1702f1a8cb8576c9a8b6c2c58d555baf9f04f36e225adaeac15ab78837456ea44b785f0210a684331b118df62b17e804b6430b30399f216b0d29864c6062a82d84dc496d3ad2d871187c6dd0348c5c5da89f290b4dc736882535fda7e200bff079878632b78a9899755f53e1ef263a88e809a1e0b825624f8f4c7bc31071f25aaaa1093b3f55f1759d3f767a7b8d59bcef24b14025034c09088296be31d91e546b0c54a79e328fa9229a2a12ebde204f977141dbc20b2d9ed4f1c92acb808d1ea93bcd3c5e9ec8516e5b633f6bbfdf31ffeeec4809779a87b1ee19f392f82cc37979850331bfb90494222b364430813912d5cc6a0380b5e3d8dbc8eac08f8a95aa218bb28f7f5a0ffb10bfb34ee5add1bf5d59a45ec4a03abd181f68905f95bbabbb4c2b2ccc344af5632354145b96d6b9681e1dd3fb77cd4278e448e3697ba130b784f66f3a18bc24d6f922a31f67f145606f5af772097e13e9b7111de0695041840de24bc275699f0347b4982e08b04d026f23d02c165473a49d623f902e11378040e5b3d6ea2528b0d3d5949315216c1cd976a9a91548cf5cfbc94e752e72b7795fe7edb1d49734695c8144c2996b8cbcaaded87c78203f9ff3a7d6c6b4b784aecb708c54d1dbc3fed63bddd7631325514bbb36110973150290ea7ce40b7761fbcd47666ecf09aaa5d7e5d4ec86f93c28804d5c3c5a506eef7c701b44874ea73017387d70f867d8a270d5ed911e91837c4a52804435cbae8509d1ff08bd9db29b63e3ba9d435d8e2baceafdf28202e57bd5cb07f4c897f6f612bc6deb059c4d1ce42870a26ff5a0f775453aa940847552bfea0323a94ac5e2e5d4f30c26a452560c55f2ca9fbb02d5fe0b16f0c5fc2d0b19648106a7decfb7f60cfef0699c5447f9c337a90698c189b8c4338ec464c00b93c488f0e76f4ed1168ad32b4432d8afee85f0e97e30fa6e36b0b3784743c6f65503747362a49379c871ec2dcb80a4b2f68dfb8fdb193ec142f8dd634ff9efffdab1ede72552b63bddb5606dca66fc07d38e4051a05ed481dab9ab13b7d96c3cc872df0215e2daf6907200f8ae94b242a5d98c4bf92a50ae01e682d273a743b10735d5308f2b2da7402d4120a0baf21ee3dd5e658fec7fda21b1a559c0e625e56682e421bbb2fb6d5ad5fb9a764228fa88bec93be5f5c58aa8bd6609a84dfc221d654682226cf4853d3a05653697c591e3e2ea71e60edee124a0bc3c9f81dc09e7a90d8a3b479589764aee2592a9798c912ad9b1b7318a42b69ab6543d520c5a30aeba36723eb2bd7e77296e85d759d8652c88e9d5118f268addad9e6686118add0a64d3293aab04caa540ed0a98baf7a8e4dff0fee755169cca885de9288b1499d1dc7a45d5cfbef21f9251b0ebb9e16504919dd192ab4707c95452cc6b59ac447ddeaabc2e7801aa879badeff7177893d813e9dfddca53ef4cad8db703e28d1dda30c2ffb1b419a71b1d2022d2a502f8a6774ab191cdaf804221544620246952a15be0aa200b3728a9acfa0ac9827eaba05d1ef170072d7951044f0488d54aea2ca40dc0d5bec8e57d2b58745267362457236e3e23638159933c770359b120556dc25239d9eba82f375265f67e85278e816a93aeaed2e61696f74b1325a4f5067369d1dc086b005d114a2748b3cdfb3161568ebc33f17e8093f0affb3f7fd08815af0a6b0426b908d64607fc81d0c4ae5fcb56584ad535bce8d0da7e1fd9f54e5db24c17af186624312989c555c6846823f3e5959c143414a99ac584fe96d63b38b5903a0ab875ed4f019951148661348beb24e35d5863b266b2821be78ace129a658ce73e1a38920df9c087d080a551d3a7c5b637ce5d3b877f7022a05dd6b46a3941b15e5f5fab98e59538f7bc48f255400f095de18911147b26f512242b82ebb7940cc834576eea198e0f65ab2c4922b1c1d6d73693bc86c273bb71ffa21763c79683f2b5b6f691e60d8ba344bb63ffd2c8c651b0000f30dee6f5a74d8fee928812204c815427ea5376fc3c4b22f1c11a85514bf75fd9ba208a9af20ad1abbd3180f7c9207c924a6df203b8c6b2bda29fe8a7efd55c4a1fabefcd9d16bdaccec0f6f602540efa1b9e9c5fc77aec0801d81288473c43cb08989fddbf32501e759dcb72ed622207eb8deaa063e198cf4755db5cd59f70fd6b7950294f2ccd0dc3d2baed7e39f63a561423208195e87d31a4b061dd9f6841ecd8a277fb6fcd64615495e6af0f1efb1526048f2581f7b0a267ef352665ede5670fcdd98d6f331a2887818bdb5bb695c2eb31adc3bbc49bb2a64480588b5d6390da0a480ffb8b6d0fbbbb05119b39832006b4689666bab9f9c662ce021877ca019a3f4f85b5f1154fe172d2f2069a79038eea284729bc68f90fe6847608d6c3aa7d95c05894b81e1584b7919f4df0e14783a5e55e01b55a558b945aeb448d533a55a1b8b3b319970e2a2d6a741c66094fbb755e306c001d5256929e718999a08e1ab7dcc231e2e2927caa8416339b386d3556746095529c776a2bfbaa4de90684dd3f719be1e94aecd17f7326819629f44edc76e0457ad40fe97c350204fa884fa813e4a6f649def117c2be4c06f9df26d9d69f4672c825e473f6a84e26c204ff652e6d74ead69428a1c1790d1452f639d3e8b01f15a00981c863f4beadf8e03b9249ecc020f9c80e34285b9ab15113167c739b7563ad57372650f8e7e1c4cda384607d7dcd1ddf520c18c9485910550665362ebc928f647a94c1cad6a71965a4c017cd303aa794bb254205e19a7319ddc8c95f9419b561f7ed2fbde615c06819b50bb15d13f2c378d74e91df559ad81ae1e0d60c00cc03f108359664f7f859d5a30d0e566f01fe8de8d6d49ffbb898888086d5ad2b6947b6f29f79652cac80a850bf40a964a6b6dfd9d41d0aed56ee3b8099381519b00683e7cc8ee44cb94d3f2a769acf22b4d8ee3388ea31ca59e8abbd4d1eb41a5b970f3e8c9d1a8e7a7d5f1e238298d91a3dd765abb8d528e7b766ea3996e418a169ce818de2d77dc2bc78538fa06af479c4247e26dc31977ca498e93f24537ee357f1316b92d8c32c2eed19b61cc10be2a4bdff3df84c5982d3e1a6486388463a27c5184b5dca55adcefa763078f68c510ac8610e02a180c775443838915a23588b2086999d371c668be1c128211083e9a334d6f1a6eb9d18dce8cfb8db676555659ab942ea1cda3b90fe82db4394896d119c658e15311e1c639b528352d462dc68cda5ac3cda3ada4b4a374b335a31cd7719cf6bbe12df372e696ac6c6a91c56db64e4dcb322d6351aad19c9d522d9c915bc194a732cb68cd42fb53b5bc94544e6ece4d6dd5e8dc366edb4422f998493927a5d9a434d36496519ad14c4fe7a400086d9ec849cd49f6f8ba3266f32b6599cc7f3d46ca171f01eddacc59b680adb347de885bd4536e0867b90013462b306ddce7024c180a0410be528c0e74542f088a1313854feae869bf9a5665ad73ebb8cd4ed60fedf59a96639c5c980c4cf3885668a01ac02a201846a6548b47b4a2b4865216a419e37476cfcbc9e464f245ecf188556c958c9e3c1ef5db1a328f47bdb5dc8643fb01993635a9516dcba13d4407da66d7a2156f0a3d31bbbca419cd92db2c3dabd22c9b19cd364a9fd07ee39c74679bad99d428ed414e0f08bd9459949282e1d6c757cc14bef83be58673a29eda6518731766e76c8f193de7e30c6398f8e2e5e9d5661d421f6766d159298dda66b58cce99f5c07940e8b3df897fd8b419d61edc18274061c6e4136885427ddcf5da0f35dcf4104d4a272e8e37a313247e0994f0049e38d133c0101c2f04017ec4d131af97f171221466eecd290a43af9dd6bc61263ccd09a19cef095fd41127e25c5169c228d6214563a8142ec6d2eb51af1d6721d943d0e1e0f7a00911bd02600f48a7b7b072386f20aa7dd5b2d7ac76dcdcb20a8489f92138777a3ca2153c221478ccfcb0a1ad357a0b6de8e1486d509c98695ab555cbe6f7c6eab8b9d95b9969cfb6d95911e3467560cdcf001f3f6699649265cf3c9d659565332944db67b9b26ec765676d9994b6ca5aabdcbc0974f1e8496088951007de31ce39f08e1f69cc8e3e4688a28ba0ba0271220743c020450d25384394243bf0a727802112883bf08f59045dc320f53b35dc80eb097a2173889d191637e609df766dd3340f5c1ffb8a62a0f0b1767c64af1740dc3162032e5dc320578ba1c2a71d88edb6fb109dbebf9ad783ed310622282145df0e48a7b9b07b05b2bd7b3dad5d67e50d432e66e7cebab59317a2ef6be6b84cdb58b67695b3db76ab01d9aad443acb4466e7cdd18287c1cc519d6b4c73081b9d0b95b59920bdd9ab7c8b244d49b38e4d1b12c17aa211dcafa11f27e74dfc22df516ddfe561c03e50b127a86639ef0713da2159a88ebef5c0e22baa8fbc2500e79bf1be67290ee99c7a3fbf6e8edd0b11012c82e7098b80f418743bf43b7ef41d33ab20b215452c6400425a48c81084ae0d0d0d438efa98374b71a08cd59f969515c51f7db65cec397bbcf3c20d9b9700fd137dca2d31865847eb328dc5edef752d31d0e390f7b1e1743e766a4214fe370d6a1de0fbaef2b8ffb799cb96f61cc41b490d5c3104f6a52e8694923d2883cd2fab1a9c1f8b3008599a37f52e40d5944d254d6aa152299b124d4466e61f9c87537ee1c9051e87a79857823f4ba8228030a283e1b3d22b8eb6cef11a5d4d6723da2fe5aee7939517fd6da9097f31d67fbfb7d4f647af06d974f74fd26ba21fcd55b60d6399fd870ab2111411649fd5eb9d73b7a6dbdba5e2fa3cc18cbabe8f5120a190bbd5e6241de6879bd9442def8ebe516e28df77af9835f2f93983672bdfd72d41fceb9bfad2c939041413d73d3d1738553dcb6649984a4912e9034d588a4a1e28ea8a6a3d7914194650b224d7d28cb174423f22b7d224925de445d6da6ef966db8fef56e74a9bb9128d4594f52a14245d6cbc1a9d6fd4e755714baddbda19c32613588e66deacb619cb7e065e9824d1a99f2659944fde580a0e8a8bf4f4e915324956dc341d4863462a4ca200db471a36769c495e5112af16bc9d2084b9647be502661ad0fec48fb01492229aab9a4437db894a56e65d9822823060ca8cebb1f36ddd612f8d2cbc12ca6d605f1491ea130222eb44ebf2ca4908c4194512f939034f572ca141d65c88ce07a3a2d52be16cbe2944c6295a50ba4118b33b65b96262f475671799605db166c5dd80245a12f0cbe59064d989515c49b30e822f2869c1269bc9c2d9455a29793ba8ee9c2f6d8666c5f602ba3c4a7e9e803f4a3334f476a14f5724e9f5e8ee9b2c485a48b4d00389b17d866297b4ef0c923b287c7d4d2884c023c0cfbaf860657f382631623b31f65980c078bf1a22e815f8e7a24ab7c9fe8c789de928bafc59a33a05c20268c5ea607df0e9b96055a51f48e41f2490c924dd8c0e61829df9e4966126a639b30188f1e6f23e688c11ebf63daa079bc0f330663c6e5f5f808cc58cce32d3063311e3fc48cc978bc068a983199c70b9137f2e33d206fbc3cde087903e6f133622c7ca4a9df252c74fd66f188326a8dbcb1e330a20d1f2f62ecc513406170165d3369fb6bad7c27b7ac43b74d863c2c7584e86db46d579371bb611b6ac38823882011b940020a298ade65723efd1c3baeeb421ebe9dd4f7fb7dcebcefcf3da21e1d636ba5e4381d2aa5873b4e8674a80e6d21ee232c121d94a1efe5f0b5d65abb75dfb0deb687b20ebd307d7f6ff8dd93f9f7e8e5c85cc7bc243c12fd96b00d8f7823a31065ec9037241449e383da904d906812e5c88f28e60adf967794a31ec55cc7bcf4f6dc2b92260a51284c9541d20a85a9bf2243268a4b26627b11dfbd3158c81d9e3b9030752ee1881174ad130848705f08a304f998416f5ec85307b10a41898c83b65e78e5890309abbf73de20260c79de20d2d4cf11c81b369c398834f53224f8b6ef99045fdbf0dee21784c49ba86d78b31a7fc6ec72f859aa95e2f0a5e6bc1e501a87b77a42a28ceae5088932ea1774f78b313ed6f82b31f16dbf56cb6296c5f80a712f792a0172c36cc2b6db709bb02d3462c2ea331833144633f5959c8c34b405983c95489afacbe1c0c6501193a79238719699c1177d7eace8e83345121df568142e21c3678490a7e2cdc6e1735786cc1766818e62728c4c2586ac0f3e2ce28430c25ff7c48884c16e891226855460c2a5d01a50f8b4a1218551def7292e38450b6691592267f4bb22e153961a66758f5db0df124665a969602dd46fc6f7be8b328c5f134e65a967f25c426160708125f5cb72c2aa2cf50bcb6d41e1950b095aae2b855959ead93363af70de20c2ea49e0ba57e15696fa4513284da0fce61566c95287792ab1728574d245906fc8c22d596aaeb4956ca9749c655014d3eba3e96720bdc6298348af290d82df2a781b4619323de17d816f0bbb70206992489a7a1d33a68aef85270a63acf0c98b3c1e52c396788e9ae5e57cbf2cd8c5138531517cf2f74545df9999a4d68daa97475cd151ac4822f286bcd2243a9137249448531f65949964a79258e1a289ae975096a0ebc8f529d0d032c51a583a12b4423afa00a940b35050f47de99ed0170627047d61d607532cd1370616651925be4e471fa025b4e6e948bd7aa63af572529f5e0eead2cb393d7a3923bb71ddc51e0e4d11c8c032a0161daca1518345b527caa89741e29b4b609288f28c01c217ba9cf9b4e82498cc9dddaf0cce66ec6add37c9eaec153db344210fdbdb75ac1f5c97b70c26540ab562ad5a2c2dd75d39cc2f97d70b8c179818303132313334333539d4cca031a346ac868d0e3637376e70ec8023878e1c383ce0ecc8d9d1c3478f1f02f8e1c30f3e0c00c800821022081f7c08912d5010d94e01225b2a40b20d1a40b6552890ad952cfb461f48024d27132a855ab1562d9696ebae1ce697cbeb05c60b4c0c98189998199a999a1c6a66d098512356c346079b9b1b373876c09143470e1c1e7076e4ece8e1a3c70f01fcf0e1071f42fa6a9af68d3e90049a4e26540ab562ad5a2c2dd75d39cc2f97d70b8c179818303132313334333539d4cca031a346ac868d0e3637376e70ec8023878e1c383ce0ecc8d9d1c3478f1f02f8e1c30f3e00fdfc90ad131fb27dd243b65004906d941fd94a9940b63e59f68d3e90049a4e26540ab562ad5a2c2dd75d39cc2f97d70b8c179818303132313334333539d4cca031a346ac868d0e3637376e70ec8023878e1c383ce0ecc8d9d1c3470f4f5f4dd3bed1079240d3c9844aa156ac558ba5e5ba2b87f9e5f27a81f1021303264626668666a626879a193466d488d5b0d1c1e6e6c60d8e1d70e4d0910387079c1d393b7af8e8e1a449134def235b243db24dc2235b2539d92ed9912d130964db44967da30f2481a693099542ad58ab164bcb75570ef3cbe5f502e30526064c8c4ccc0ccd4c4d0e353368cca811ab61a383cdcd8d1b1c3be0c8a123070e0f38585f4dd3bed1079240d3c9844aa156ac558ba5e5ba2b87f9e5f27a81f1021303264626668666a626879a193466d488d5b0d1c1e6e6c60d8e1d70e4d0910387071c8bc41ee121d72b38d9f6e86c89e8c816891cd9168940b646b2ec1b7d2009349d4ca8146ac55ab5585aaebb72985f2eaf17182f3031606264626668666a72a8994163468d580d1b1d6c6e6edce0d801c7d557d334d3c9844aa156ac558ba5e5ba2b87f9e5f27a81f1021303264626668666a626879a193466d488d5b0d1c1e6e6c60d8e1d705ce162875c8170e43a2500b952b9916bd04dae552090ab952c339d4ca8146ac55ab5585aaebb72985f2eaf17182f3031606264626668666a72a8994163468d580d1b1d6c3a7d354dfb461f48024d27132a855ab1562d9696ebae1ce697cbeb05c60b4c0c98189998199a999a1c6a66d098512356c346071ba09f1f4daf43ae4e6c727d62235728b15ca3d4c855ca0372f5f9461f48024d27132a855ab1562d9696ebae1ce697cbeb05c60b4c0c98189998199a999a1c6a66d098c1994e26540ab562ad5a2c2dd75d39cc2f97d70b8c179818303132313334333539d4cca031a33aa94d68e48a6446ae490090ab921c725d52932b1307e4da44967da30f2481a693099542ad58ab164bcb75570ef3cbe5f502e30526064c8c4ccc0ccdcca6afa669a693099542ad58ab164bcb75570ef3cbe5f502e30526064c8c4ccc0ccd4c45528fd4239a9e266b576672ed91912b11995c9188c9b548037235623a995029d48ab56ab1b45c77e530bf5c5e2f305e6062c0587d354dfb461f48024d27132a855ab1562d9696ebae1ce697cbeb05c60b4c0c982b5c70a1e963640d08266b536059a302236b412f59abc280ac59c9b26ff48124d07432a152a8156bd56269b9eeca617eb9bcea37fa4012683a995029d48ab56ab1b45c77e530bf5c5e403f2e5973f2cada9317598312662d4ace9a940564cd27cb4c27132a855ab1562d9696ebaeab69da37fa4012683a995029d48ab56ab1b45c773969d24443e2ca5a9296ac2961c9da9256d69828206b4d64d937fa4012683a995029d48ab5cabed1079240d3c9844aa156ac9586443ba21dd1f4ac9c5d5965ad47953522a9ac2181ca5a910464cd48965d4dd3bed1079240d3c974850b2e322053cea69472468594b3203067551090332b59f68d3e4dd3bed107f49333275fce9e8872062594b3285ecea41c20673e5976cdcbe19c34419225c994644b322613466f809c354161a81698e86db9d0f4356748268c6647268c4af1edcc88912c89782335a749a96d59966d9bbcdbc5d76e526e55ab75b3df3ace4eb9f5e8f82204a221424c494408a7940b52288e5a906ebd664c432dabd16620e2eccf0ff73a859393a354f6abfbf53ccff33c11ea3a26927993b05e7f99ac5e8fba3e507ca54d8787d42391089cdfc7dd2be5fdee95525ef9c9cecb893aeaebe588cee54e74974c58b5d65abb5dcede7bae095d3b8cbd9ca845dd4507d2f6fbe5c48c79afbf4dae93cefecc588efafa783f57c9b4215a42618e4c1ba2239789ae76c99249f5a53f16c852118553ec8fe808b56181a8e82a3a42edcf84dd28ba729b2622b340574abc895eb0a2398edbb6bb7ddb76ef76efbddb76efa3cc98fccd6ece19fd3ed15bb85b7afba51854fd7462ad7e3a9d4eabd36a754a653bc502752254b63f76ca84d5d00281f85aa0d249cf7a66acca177a6909933d81f85eef5ed053e286be16ec6516f8a84ffbfae89f093341d1a4548bd4ca768a677f2c906994a5deec14fb6381be1fd6c7f27d2d9b7595ec4ff6e58dc1aca7bbf2637d210f4b294512e39f227a8b9adc29a2273362d868501928151d2fad5079edd8fa6152669865a5fae9f1972513618204b48b2e7098107139060a9c39a93271971db6215128c47dbbf7de13c7711b66c9445a67b9f438ee75ba8ed9ca11c77e955f9baa958f384eeb5762aa5f3fa7fc92d15347cf1ee325295d732867b4e24dd42c39e2e0253a6a8d71d789ce12853cdc755db75d4ff3385546dd94bbaf72eaa395b5d6da4d75d36632dd9a54269589cb47bdbaf0cde33aef940f7b9deb94b898cb324a34c1e517c6a42cb52887973468f82c57825198fa0b62ae0b44d097b40457c2dc7d821e853ccff3501e17f2686cf15d1d7da88883e67e4d98ebc2135a85b7cc1a3da774eec354ac4173bf28ee9d97c36d96cb7fa53c4ef378485d3ae8d1d8828a2b34f7abc25c139a749247630b2acea0b9df9256a225592a952217da20d0298fc7d47f8157e5709b65719ce7b570c671c75e0eb7598f850452b9711ee642215128d455d1dba66b596b4df385055b0bf6da5e63905252b971872fdbb6eaf9107e5b666bad5ae6e530f178c8a825a192cd18cee49e34de9820dec87803347f26d0fc018a3730f8e20d8e322618e48d7803839b4a7dca1bd58b13cb296f78515f8aebe4017ef5a2969141d6d383836f3da9698e71f2451f2b51280faec4b44a95372895b24a9a5197d0dc94e8244dac42de983179e3eb622093f5f5643c588c021303eb81dd2364a954b6852bf393663945df7acd180d63ae90da98a099966d4400aa893a72b1d684dd30dc6ea822cdfc9eac5494315372cada59b1d656cd22b15a566badb53ef3ecb32cbba6691aa55956b56ab50c084a6e7ccdf975e1a35a46a595536652a35ca6ddbbc9aadd16d02c35a53636ea446f189d3234f84651c6fc94f8aa566badb6d63a7b4c1d81f0c9cff750a978a02317b63967cce60e2c0c466d6c35f5db75a32569ea378b8e3b20719c158a0bb57eab4a45b36b2a954af54ea5e2ee8c1e6ec91b8f34d7aa77f85b56e10e8a1e18257fb7cedb260c1f775dd71de71ceba1a697c38978b0da72a7ca30ee2e3bd5b76c8f6f459907abdb51acca5147adca52a3b24c0d0d224cd81f719cd5af9c1a2606bbca34660da9af8489e1db8f1165745df0606051c6946f5fd7bfe8ad9e0d63a2f84c79bb2aa7fe9954a66f4b95b8eda87cfa284b4d824dd8a9cad830a60956b8d0f29bbd22d3d1c70b26d0233ae7fc80a822071116f5bd4b601006f665de1d61db852ef497a5e6381c76d7416db8090144154b4498a86384b1bf20b65946892e5cd1f78457990813acef519b7ec47198289dcbacd1a32681d731319e54479f2a4a38c7de4d86682814ea640d6d10e89a09ab37793ca6169df0b66d291c338c55b8e255d4a028dbb61f247adb8c6a69de0835b3f4953e2d7cd93fe79cb3d3c2a5db763fef754cefb788be9ea7e19bbd67debdb6594a29a534ab1dadd5669bb576089877ef621926d55229a946e595d2daad6a34bbf55e8df531a3746b54669de5364aa564fdc8ae5dd3b2ccda70f36413d62d81b32c7cf4fbcb3e65a64996a5d67634cb6c4ec6c447bf37a9359ae3b6859b476aeb6551c6f472b228439e521b465ab23c229f4041c31528d0a277afb1c9a29006e17eaff480e07fcb98bbf7cdbdfbe532ecb5af6990fbf920dcbfed791c0af5e8429752f8e443efb40c83602fdcf71b6569aceb6f9e35bd29b795b55a314a8b091bce628ce223db708c3e566e7ce10953af6083a7e78c5ae893d6344d0bb709abf531168bcf3e3a410a3e241da3380312ad9dc313567378f2c53a61b4d78a832ffb84c30d8310a3e83ba3d0516a31587c5167b01e64c29a49c326081f4c66da885170e86134e24da471859e73c6b66b6a3a69d431afb7ec52aa88e8e883061ae878e98541b267fdd9c315a38c4c079541c26725abe28909a6e8193a3e39e2476b3e6440320625b741e4880d87c8111fce7d7722f8f138b7660dfa9b593107fa2eabe20c1cb6262c4cc51ca84c922f7bfdf6cc6ebdaddde2cb1eed10556f2107a8b6a756d79a0d417546a17043795b1d32031257946dd143f33e4e1d002498d0b1d1f5dfb7c7831edb68cbd24e8f6062fe7badd1307d9ddaf48d3236f86eac5a164517bde678510e82bfbd8b9c68abadbeb532d155577d370d89d6b4a66f970515d199cef4c5f48aa69aea1bc2a1bca70ebd8a9e7a6a32009141ca957a45dba02a7a3e5486227a3ef45ac2d2ebe1dd72797ee3b887a27ad1d4389c77dabc6f70e819468f87f719ee7afc2f13f97e04133a3b687b1db4dee8216eb4a8070e2db3eed10d3d3acafb905106becd33eaadfb766897ea9dd166fa50ae791bb183b541701f793fd8207ea8ba3ee405c1ddf37ed0743df682f821d3f59d17c40f34881fa6ae37a1423c42d74e43eaf5d0815090d01ea2a9a6e70ee43e742e874e73d5d63ed32c90b63a984152b411fa8aeab94c84d5dc3594c6af9ad33497698e6a6e6a8e08a9b92d3f1c6eeef21bcfc6c308cdfd867abb13ad834906263aea0868af87eedc2fcd60102e574b0b0b4babc562ad562a550aa350a793c9542a914820381a7ddfaedafb1565334604fe9557243a854226cf2b614cba17ecba11a7f1f77ddf475f48e4880f07f55148e48813cee9448e68e1984ee48810a7742247c070482772c40c0e782247dce07c3f8206cee85eb8ed33bcadbe97285d5f4f5533695929a3243ac1b9a510b20b9c510d427681f38531e620f7aa87792edcd711d03b027ab33d9e98c00a0f1cba86e11e423177bd85ac1e66106d33be9ddef4d5620dd51f51062eca604547118c01c92b2a201b61fbf9c871f1b1e372e4388ef3b82d9cdff6ecb7fbd6ae8771eee1715e163204c7e3c15dbb1089c384f6aa3d71e33d20ff9287c54c47bca19f88063149f8a68d9e3f9f7cbc8e496d643d2c98328f6d6a68a46e85931a194d53f1f4df13175c29736483e919db44a07dac40b25f7ace0c88618a8e3e62201206296170a2461fa17265eb21b2c5166ac86294182546f141a6061fbd94a9c1b7c52d6e78c26c67f8e8131b44644f768c4ba556ebf592913ee8888834d2d3d3d383c344fdbdb908a9ef0ff9fbee4478bf2177435c880b7161aeb7b9c7d4971b72435bd68c6f90fb2a7bc2cdfab1f5f4f4e8ed556b40b21e20b38703227bb670d773c06a7cb9a50ec249afc73d7743ee756aced69e4d0c576a99881791551f2db5ccb44c84d4dab32aa98c326ca5f264ffd0d91ed2034dbd1f9d06426735abd96d909a5d0b2fb369658b2dd410062434747cc2c4c8a6397de59d150c4434ab0b2daf694e679a1b585473fa56cdd2adda6ae9b4d266d6227165bc614da1e5afc61aeb6e3efa4cc182142266318429a46c51052b5041034e8c30f2379b4f68b9690a45cb571d3d1a5b4c61a2e1404b1a5b4c498196544b8fc6165344a0258d2daae042579106ceeb21a39441c748d375b5eb3a3a37d9755d174fa11d023def83332df28832e2e38f8e13568626516ac81320661326335a436eaa699a166ec0d399cc0f3efa3d040dd236344ec3700d201fe7a4f177e63db3d32ccb14201fa9f62054a335a2569fd5ec52fb90a38ba8b370039ea6a1119226a2200a1d234d0cd2b302f28610492313306d6c14081f3aa80d9b800923659435d346cd84913fe243c5121d7dbee00343479f2f2cd1d233c0109ccda7863c0286e0d850880e1650408108477a70843081011c449e84c006e108098196831e1c267b031b987a885b049d3d88165e2052d1d9b70eed427320ab2187a04686e041c1153406a8111d840a2047173a0b37e0692d01df8cd1ef22a4a6c7f9cbe2a3dfac78e2c89228b8218a295ac0891166d355d7304a9aeca587804df94f9bda1aae9454522c73d41246332aa5a6d51c69ea0c36fbfda5365bf38da228433e95fa9cb854ca20f9401d7dbe10037de7e3378d71df59cc7ee31b1b8d36c2e46dbe11268a30f2d325af85d9a63f24df75f401da42cbf0e0dba94f0da567047aa65a6688c38df3a4a3cf142a604841a7a30f156008f99078203a814f1554cc2b62ae00b53cb5498318a280346816a94214681015576459a6e91f99f4026c081850cc1543600d7f21ca1bf76e758b1ab61cf8f4f67b9b6f0c14b7d3aee8de6dbb9cc5f7de9b53bd9c7bab2365ec4f908e3a7a31244d85d9eb2d813b9167bbd0a341bce5223e1df2eee6c5f06092a63ea60ad0fba4628a1e15c424fc95be7b7f4d5ed7dd7befe96eddfd45e12d7357c7ec3a0f08f7ed33240a85ec0f136b8a22d41d39d26d9ba4eb64667f31c638077bb7db11eae5589b61cf7bc8e362a088ba0b12711cf77d36e788ee6dbf39dcf1b76dbbf6dabb1d91f7c3a0177597eda7f7ddd2f5becb1c07246f74afefa6cc18678f58241cdebe2990452269aad497fbb62ddcf7576aea85ec916d93e87a6b91849cb04cacb536d40410bc714b88ae284b0d7e346167f8ece53f3cc2f8b333903447244d1547ac47638b2aa8a0b9df0d57f184e63e8fc791aaa38f17a0e8fb7d3afa78c1097d51f866e9e408053a3261f51b5d44b2775e7a43db03da263982c4bb694371dcf7619ce262562bf72ee8eb8236d6a2efc31d1780f40e81a1272fb6b8648b8fdb930e5fce860146af9067e300a7ef0098bee39358e9510a02be8a3b4e497d472a31e83b56f977d4c2f57845f6c45adf92880d057c4b245cbea591d7b74ce245dc12c90222131d9d80f12d9bbc7c4b270cf8964f68be6594996f2945c6b7f491f99652c47ccb9f067c4b201adf92ca8c6f1904806f5925876f69a5e65b6ae1806fc9850edfb3c7e67b12b1f13d91887dcf2235bea791077ccf2476f89e48707ccf2401f89e4a6e7ccf2537df930904be67133c7ccf2638dfd389fe9e4f747c4f2839be6794ef29c5c7f794a2c7f7fce1f13d8172bee794d88eef49c58604be67d00fdfd38a0fdf538b1ebe271702f89e57623fbe698f8d097c5322427cd322417c5323407cd324800ce09b22a1408ce243cb2940017ea5b76545b2d49ab6655bb66d314a4e561feb0b6003f651025ccbd90b40ad644528a53e7c00a20033866f04a80d038c4e3f8406664c74fa226e849069e300a7a79b4e2f8bcc58e994747a99c48c81a70838659d5e2a519da64e8f3a4dc0e94f5da72da75266ac75059cbafc75fa17a7e1e965d08ce5d3059cc638853985c188bd30e094e674e6f4f3c88cc94032633231a70d38fd643263343ee3f4d3891903c0e9679319cbe1b4e609b5e1001d6c4e6d9c7e4a3163b1d31aa70f98b11d4e3f83660cc7e96795190bc08ddf40e0f4b467c678383d25326338a7a748cc983e3d2d32633a4e739c46e0f414c98cf938ed717aaa64c6789c9e2e99b19cd3532633b6e3f4b4096a4302a7a74d66ec87d3532733e6c3e9e99319ebe1f414ca8c09e0f434ca8cfd383d95326d4ce0f4548a191322881810a7a753660cc8e92995191bc029054e9f1120476d63aa20b313204b18b5e1c3a4339665198e1d70e4d0910387079c1d393b7af8e8f143003f7cf8c1870100194010420471354dfb461f48024da753043e5948a177e6930514594441c709f3c9828a16c18df3c63450d1231d7dacbc41476f099a7d7d8d55b220c128892c9bb07a1e6c5bf8c52df8a4410bec93450fd5649c686129e593c510b238a2f7f737d4d4c828a146260935324918a2a6660bceca109cc7f523d453e7722784085048431618639797be45c7dfa1d0795a74e8bbfb0e29f5101bc6200536cc799e959edf3b22f033dcf3afefd2bfef0b7da75027853aa8a73c80832674e10c2008eed449dfe0572d1629dc1d28c11bba70834824129dbe51ff482492eaf57ab1421d978fc680840812a8ebeca0be8fbe4ba7d3e93ba78cd8c10dac24813a08b27c9356ab55eaac5027f569a1008328608142a14a47859bf5f9edf2531886df2ddfa893e8a9542a15ee196eab640925b8c2057ca9d46ab5be3f0a75beda042454a10610045d0e86fb74d177eaa1cbe57a3112855b035243125770717129dd25dca256ce190441d65f843aac8fb22dc22005155a0fbf4b77b1b0b0bc98673d8a15258850fa9e0a37ebe1b7ebb9a5a5e5f417a1cee993eac0096d28c2f77da57fe1ce0fbfbfb3fc7f21fae9f3891b5c21a5543acf4a97be773ed74be13e3dfc66798b482412b5fe22d4695d14ee30dc320d5ff841e27bf1edfa4ba5d28b704b2bbc600431f8beef9324f0c1132b269369489226672801089ca05244c90b0445aae0a384eaf57a09498210729083954aa512e20220c4c00a69b55a79e04919a454014522913c00832ea2b8818742a13af082285d2801e8799e112b48810b80e00282a025c212182461e4e2e25297f4600c52bcbe53a3d1485362ca0c5ef0b3be5954df33954a7def8459198430c515d6f3b76bf55d9a738639dcd34a152734c93f7db748dfa252a9f4a904201894e0f4966f16d4b7492412b158ffc350e7ffc2dd126e790325582003cb7576585afe7dbb8e5faff388f4cb64327def84d7d9c967fdff633d8842142c6f85fbfbbf5b0fa954aa6ff03b843166b158dfd1393d3f0c75f259e17eb841c0440d70605ab55c670937cb59dfff6bb55a7dbb7cbf0885422d3f3de77c9e169d851ca147066af87576fed65de17e9df5edba8a44227d8fbec3172f5e9c4ea7efe87c6f69f929dcac707fc00d5674218556abf5bde3bace8eeb7f2bdcad9fbe595fa1503b8761c8f2ef2d2d2de769d12de13e85fb033d45ae10e5bfeb0ff7eaaaefd3499e771e91f69ef379543a7f7fdfd1799de52fa10ecbbf70abc2ed812648a1081a5c2e972bdcaa7fdfa4a34010fc2e7dbbfeb7fe3acb5958ced3a2593ad08436e0000ba87fdfac7b2e2e2edfa2ef1697eb3c2aedfa7ebdced3a25fdfd159bdf5975027dcf6086368811758d7d9617da783a3d1e8dbf4cdd2d2721e956ef9def1aeb3a3faeaadb75ae769d12dcd0a276401e8749d9dd35927851bfceb9b7497542af58dbf5b2c2ce7516996efd5ea3c2d7af51d1dd455f7421dd557e17e853bcb02164ee003d68a749d1dd24f6785dbfbeafbf5d19ce711e9c96ab55adf5157a954aa8985921f2c4ea713e9a7708feef2bd7aaa542a7dbff856b158e75169d637ea3b3ae0bfbf843a2ee19e4986f00429241289146e97a3be539f2291e83bfc3ea954aaef9d97ebec7807ff7ddf795af417e3704412a2b05f2f994ca6effc4d3a9dcea3d2271004bfa333baf79750c73b088736b0e088176a7511c6f8fbdf1f89741e95267def94aeb3e3f2d1bd7bde795ab417022568c84213567f3d156ed1c1efd44de1dee140e7e9d079443af4edfaf6bec301aabfeff084f4771e95febe47a3f3b4e891cb4ba18ecb47e106c32da4081661d0e2f53acf4abf525ffd35c20178b9b8b89ca745bb74800a2b2ae0c16ab54a7d650a8542a15068f33285db8823dc208638a452a9d40b1a16066d78428bb0668910def004d453592412894299d5bc411452b004d16111265732091518410425d70b05822735d0c211fcd4f7e82d2693e945a4b4297c844991e8a8af421dd4a7077ed2a0042d989efa0e9da5b5bace4ee9a2a350a8f3b46854075a10871228994f7d836fa544dfd1c12f7d15eaa4c2ad5d014a0ed2008abefa1e9d8542a1506129759d1dd3f14b2f95ced3a24be15e859b52610a22538caeb3333af850b8bba3be55a552e945a47429fc220ce3f3b468fc1d9d79d353a18ee938dca87087c00a5c38820e4010fcde095d6727f4d1c170732f7d9ff0f70e789d1dd1e74d3799ced3a24d1f000209d87085d168f4bd53431f7d9330c62f22a571b8ed39cfd3a2a7e860a823fa0cf728dcf6045f38410b7428749e950e7def68d7d9a9d71eda5f1661125f67a71389ced3a245e10e853b4b62491547f4f6422288e0404acfebd038f4680e6c7a9e081d7ba5a74717b1e9f922527a8631c2e4f74e769dfa1dedd9f70e8fd5f53b3c265dcfb3dae1e974f71d1e92eeced3a2bbefe874dfbe83439deedc75b86f2fa2ce2ce3af946abd640e1ddca8e3bcf2e71b5d1f2b49d027cc26f8186189119ab0d2038a854fea2dd2540473e34b6a1d20f3f23f58d9a3c613f64d6a29655cf0451f2b4e68798fce2237be76cd9c73ce99852529c99065d88a0c196aee174425a4a1868f0e4183b44022cc8864419911265f9d73ce39ed36f3376b6b8cb0929c97303d674c1abe999ab01d1386fe567c74c3f1c6472b818dcf0cf790cfe24dbce93e4f332dde789faff1262442dd7c9fefe2cde80624953e1f8a37a71b796308506b37fd9a72b6860f752a91c0d1270a79f876dc66ab954c5ad133b6994c9ff166bbe934de70373d8b37dd4dd7e2cdbde935dee09b6ee38d77d3b77853bae95cbc09ddf42ede886ebe9b8ee3cde8a67bf106bce9a17843bae9a278a3ddf44fdec86e329532099445f4fc284ba1e745bb027a1efbd0f397899eefb2167a7efbd1f3d649123db78ca2850002e80b1126ea3b473afa0059a1b35fd985b611da24371788620576b0b78c0d12ebe8e5ecaf24b3032851bb94524aa96956d3e88f8ea752e8781aa4239da223d08435e184922534097a8422418b7071a58a15a0293e523c81e24413254b8e4c18922257aa589942c5470a28519c68c2048991a80595024cd84f149b00c8b4e2e317691046c1bd77c618638cf2f69b599685f3dbc4818ee4b4709369c1a763c2c01014869e982deed4318a32e6e39c35dfb7e957cc2eedf14d985445199f8ea4322df83eda8515229a1e3c818f8f3fe56bca13bc919a07780df53bc14fa9994ca720a683d761ca3a5060b8c19f4ca69f4e5253d32fc5a8a34c2695eaa9d42965ca44a454a6eb982ad353275318a3cc29dce0b36bca41c0fb61e9f1000f9e9a320551a79f72d4d20b4519f2a75fea81f1069fe973f41bca1a0ff0bb8e796fcde04719fc6bc6c0832129343dab07f0f4604855f7504f1dccaa704b0dfe9453394619a7a3b229ef21da0462a185a637614145db83a483997b6bc642a1343dfbefa70784de14eed041d44f596a530e02c6509707b38ed788745428c19fc08c7fca2cd2eba38e9e29f4d24f79977e6fcaac1eecc183619052c822853c8668117e3d3ebe059f7d454560b8bb8744a2fccaadbc05c137c8165a1f3b6a68cc21a9d9bde10c47f4f6499f59ad93f1c1177b241124e40da9553afa5ca183efa359035ef420df65786ff47e5979fe4a6fe6a8bd20bd9d35c36db656f84a2ccf8084de2eb9d0a44f2fa7f5994ff7f236dde38efaf4806c97e1f680903ec33d445fefc7c17b199ca9cc6a3d6ad604f326ddbb9c390878d54399d53aebdb3bcb4379b31c7599593d905842562be43144a7ceea8175d559575d75d2a36e61e55410552893a4b274e294839842b96486bb14ee219a144a29a164326114cc5fb8e5bfd3af68a801e79d2fdb8c320df9efbb260c027e3ec8b7c177bf7487a47c2824a5943754e57d0d11515f440512012d6b482614ee794e0699b76239bfcdeba0a32ca7c62d9701a372a8944a1ca89a9e546a666400080002b316002018100a874422a1280a14496fd90114002481883a523a9688a260855118a49831c61862080001180119188cc6019d72fb003b9febfea45f2f352d55753a194ff4888c3c3e8397d9c9b0471493ad88bab2f77996c51f1100dc067d6d89f8373bf7f9960cfb507b068dd9b4ad0d083b27a1776eee21dfcf3384a8be9be67496a7bfdddd6404270d761e3e946f4e27480e2334fbbab4f8c0dbfebc245362cc4e6be2522d0fa8b1b9abb3cc99072046bc15f2c8a415b5014aae993ee7024d43bbce7ee4442ad1341b218acc74ac24f4f2f235f0bd23977af7838a3e1111b8396530f3ad8b85f2f1368edbe266a4f76affa7071789c60cd0a658eb6620f9b3f8c1b48e3e1bfea545ecf1324886308125255132ab81c5b450eb5c1da3fc0118dbc0c16f7c239c0b09fd5ee1014187c438674897e61aa826a47081df4865d92350d4c5aa5c7f408ef004de0342907b616686cf5f5086d0a3a11ad39e1db8159d62652f4b0fb4da74bdbb787feac7d8c577622afa1797d880f1b7e8269319735e64f87652051806861604adbbe78a001376736fbd2b55c3b7a2c467c999f397638339bcaabbe7a31c54993ab6a7b79a18b025783002d764af337c35c971983c832636174e53c5e61668f9b93d1aac7bfd94d10c059b496da11cfe326da8f133a414950a9747ebdddcd78dd387f7bcdc0c43c845fd625da32ebdf70465cd0fffe07e78979ac03263ccd115f3a2ec16f977789f9b6abebdd8fa1af06eb9886cfd1787d9c134fc34a116aa9ebff73c2acf54b44df2520c1fba6b5ab78bf185c11bbec928b312e54cf582c21d8249e5aa5d38e8870fea4335f16b727df10e650156dc279aa11d7ee6689491bfa141e6f1c40bed3bef5ffe7a68c2b3b8ccad9b78004df8c3f55c267f2934e16daee6d626fe4113fef03297c1df074df82bd5b9b589cfd0847fb9ce65e46f8126fcc5f5dcfac45368c25bae389759177f1f34101e7e8e355175756237470f5c78f35d3f0c4b1dfe1feddd0329fc783f06ce37d5611e9fdc3d38e1b7f70bed0bdb5267eaa47afd400a7ffe8ff135a16e261f68928bbe528d5469ddec551c4b7053c7d64aef5d9c69d65d48cc2411e3fb1e4776a2252b32b0cf29eb5e4863a1fb223210a1a8f7d809defe9487ad8b33d2787aa6bd55a034f30c84eb59d56dcf6af89c7c6fab8239e5d94fd4fdec910cbb697b41c887ea209f230e18cbd3db37af052d70ae0ebed0e59e19efaf637df0aedaf127d03862aefb50414473f7ea15b63f84bbe3e80ab6dc8365497baf7df00d3cdb4f0366d88d2e441c44057bdd1d566becc5e2eebe116f4f9b3e45eb5fb235677b256a1c33c3fbb2c0e47116e3c53a2be184d6f339273c62c2770633837da1ce7c52bfd7f549b393c72a543828a1ff02553cfeac02ac8ee326cecc61c816c0d9f77f5fc1903d2884eefa5cc2d9c43f073f8e808156f113541b5e55af42d904a14c2974cadf563899ce0e723c30cfdc41c905531c50becc649def40da082074ee47e93f33b9c919397c69d1df4e910f5ed7ab896e2a7df49b1ef7eeda4075dd30d57d017ea3c110ae754e0540547a82fdef37e695e7e2a0367d906ecba080fc5d48025f4db40e3820b052177ba0c3b0dbf8c6dac7ef98b26922236d23f34bc006e26148e1e05a6fff7599d0fbc73be29850a72f63c11a8939e068ad8d3c1dcad5aa5ee0c2ea16d058149300422d1c7dc37467ea0fa907529422a3b8e2a3924d3715bd10bb6da909c482e220bcd9afe32963a530fbf4ca0563d74e3da5832155e1a88d4ce9594b4945983aa21dfaa9195214c1af800235d1bcb74279aef9154d350ae3321054b14917defe7ed678e3bfaa191dbe936c5610f7e9e84313fa6debc4f5e6e9fb0c9fa28c395027233283c878b1b7e731081ee2d0b80e28f058c5830bc305ca9f9b7d39b3482c122208f93f88ad9ba58e50b2bc74a414843546aaf56d6d2c266504a30c80029b2f39ad227ac3db304f80f4ac4a97591838c1b39217daf4b55a026cff47633eec66f41932b9c09cc49a6731441b17cbe885963de6230504c24a81de3d82768e44fa1ef4d5329a0e657b6ed2cfdf3c9cb99ce67200967d4320db32e580117413a11b06426f75e3e3a030aa756595496c985eba0ebc456338b0e89f00e4ba0802f1a25438b410644216bc67193c5357cdcf26e664e87fda243244523224b657afa0f894132d8a7b347122aa637e33a8a1fb297441f749767968a0ccc66707b386b110982c05467723921d288d258833c9c1e8b99c91b01ac193c4cd2ef559b1d32c0bfd056cf32118c2bfd9b58f42d147a960da499d826bb579cb8762258efebdc550952c7e3228d9a4792beaa2b23798fb5ed2057113a4ae1fe6976038475644b073e012d39ced3a194b716e5751dfbad7724438d9d472399350a82bd5944ceeb3e1b3212e0722dffbe43e6dec9a58807202d463b5af1821d7f076f114710a34b17d3148ecb77b68f5588bc2bce8cc4a3defb0d8110aa780202bc8503f06c693aba5e3d825366595f22266bfd657bc47a4ba193b50d1afcecdd445d9e0da6d4c6dd4a0764f4cfdaf5e3871e5488496cd08c5c459bec1174e80e4f6893ba29f51af8206f65c01631373b43f4b18f08803f9d442186319620878d92712404940f6f91760e2f4547a3543f377e9bbf70fdd23184e98608d0adb714ab6b7a10fdbd782851d92cd3d6fef3b26241d14d17311ac65b08ed62ff6992c03c696e388204dd9d544073983659f0c1798b7cd81e5049080921a2f3afb932c2237eed6a73987a79933fcc857e3b18d3d6a8fc72469d146072e6facb901839057900f5989b0480099ed00141fcb3b3e573582d7ad1c9a335093d8ac318ac3c8289edd0f658b862d5d70d4e4e1a241591475c282d24016929fd972910b00d5142c5c2a3c2eacc05c78f8b963c60fa763d1cf733c262c45000dd5406e02df50a6e9adf989d0058f3d0258d73b2a7f9d2029fa6849acf1ae0f9e805653153fc2460104d9e2eec6d4afd6b2227d14bbc1a09af77834735c9a3dd0521fecb6563d79bad8a526e86bb120794e6a5a06fc1e687c7981683338eb47e8818aff25b0226b47e802f1f1923d74f8e4ed2139f54e58a39ba3e4f2a9c88b803bcfd4ac76043d50715f025464ad182d25e33670e81564523d98fcc617d3f1e3e56ccbda459f45922253d4832f1c62ddd7fd7f74af0ba6015fb60ebe90504bcb14a450fe8f77204249e9bef3da0cccc9a06eb5235d6ec95b99231920ebc0f5321227ea3ee1392d89fa546861a14f2b2515bfee546b140d9b4e541775aa9aeac48ad91d7dcc682261a973c6f0cc87bb8f2aacec39abcb71f77b73abe1c6ab9b5206026bc37efb7b4e14c47d58f9e29e364d2421b245762dd497209cfb51ce5212b0e6a2abe7fb0f9222c72a4186a0d203c3b72a501b8c134c0e3a72ae087779dcca1a992ee828b401fcc9737b3ace8265222495fed264b5b3c127cd0ab51ac9ff081f2d62da108a60b18185cc11865e58b2f255659616691de138303d1f3cb01cd09fae7f3853128a611edd7ae15db417aacd9783120c04668219991c6e70f9a5dc01a049f460b730c90f44aeb08694d21486777f999c9fc632575e59f4295ab0b1b9687c68f36a30034642700b643c760cde0f3f63e4d41b2fbdf503f8cbd973098ae70ab44fb96f73c973c2e2f4ceb61c6cde6b3231736ef073d2ace04fc15c320b368b4beac7291ec7f2f891df4bd1feee065fc9a7ad85a9a0f059ff359decd159ea6700008343234a26297f3821573fc2289b9ad5fd3154bf01bf4aa255941ae5ed5ed679076c48b8b77b7b478662dde4cd4199849a1adbbc0f12bea8ce9538720fd0d361ab9aad4a49d46b3e1ba9378dedeca7d61178be49b6e3ba489f08df60840f151c7b07a6f83bc6b0647b72c74ae108fa2f60578de3d58db44ef233b9d1af4e3ec972974e98afff82975c67a98d3308be68fe606094816ed8a1191a5a49302ad2e47b432833a10be9cff3719778a1d2a9446b51503ed02b13f6858199557afa62fb77b890c43821f6891ef42be1b84e66c6f959082bdb3af2868527a4c994773d660b5b618453ade7ff5b602f1eefbdb07dea9b024b9420fb4d4eb6df813963e520605c62f4fc049c584a853889d082a6afbf70c341fe24e1e0d92ee650ec2fa07dd5757cd53eaee4431bc150d5e950627952d3880dff88320499b2263d3cf5550cf912c6ed0501682fbb306e2e767f02a8c2ebfcafe3d12b07617bab449f57e0aea549ea7205fedccb0d5fdcd429ebf3998b54df319595ccf1f544625efc4df389476c74d3a8a50804e532505b622c96d2a1344e810b2d90e44be01421cbe81a849dfb35486c04daea00c47b1dbf435a00289107924c1dd8bddd2703c651880de6a50215dd5dc02d688dff7491dbe9d8fa3e1303dac5737d624e1f8a7f9e208bb8b0cdc56b25da04355c7a408194f4378c6922dc486e356d3c1dc94d2bbfa9527e399d64c9095cfad3d5e5c1a4db7367d5fea46bc7332ffd0f9288ed707dc13402130d4822d6e32769cd1a7a949b3151eafbecded222c479c00db33599c11c34871b0ff1e42e884282c066da8a0a197934c935af5b864bc0645a538a9bc37ff11085755cb3c03a9611522e8e5d2aec9152e2511ca290d275b74af46d18ff57731fe6d48804f164d6509d7d995b7d7f28a49381fea8f8d5627e18b103fc1a97ab90710ee154bee6884af64c400e72e908eaf4eade9996aadb268153e07cea0ac48a17b1fd515f5d7e7de702ad4ff76c0852d82813a2c0098fb9c4fcff9d88ec9b24e9a3a282f2c1abf5c9d894005b04fb9e9d72d818eb80c64b80d1cf49d39322a57b104eb499a49c0a5acc2ee29a93eb8ee934a8f3bcfac1711fad08cac008a776203ec521608ae9d827e082a371786ac290ffbd5c3267f0e20c07998c731ae6b233ae1f258bb4c8ae42da7f5e0cf8a9626ad3e86e561a67f187d53eef036f1e001daf56835c64c27e26d63936f71065393794e0017f0719bacbf76e458e9be9ad5da4bf865469417a7942d233f4b72b36331ef62130143e65a51885a7bee8a61b4930a4021dcd49f7ce99598a2f57c442e9a7e0f015985e817479f94c499ec897d205c90fec4631d398b72208b001957cf6c7e759c5ef91279ff85fe5be4422cac318ea7cf5c22b7d650e888fe5f15a0cf18e089bcc60817d0eb8601e83d688053b86b41fcc4f2b28c2cc76e2c62d4dd35412bb983483c37b1ef08d75473a251b29c531a3d6657e1d513dc0cbb4d8806754c81ac924e4db85db1e81ab77c0c488e00c2015449ee42f1710722360482e14e06eed6b987eef988d23be5a924b68a98c27460fba0988c06638c34588229444544f11dbb8f68db0a1562172709ee07906fdee47fe9d44930b430cfc2e625f12a400d641a3d5dde21f1b986c915f7f09403acc33c5080ad801df2396c5f5b0e3ebc94e9a7dbd13899cd4a98781155220487892349ac1d768936c83307eee4fba1d6770df62b25c3b243f62a100677a2af34cf4d4768f0c407b2725c7c048e214a581270581b6a4ff6629b8c73f0585e1f2437c8fcc0668b93ed8f1289a6b1065e62f2b78692bf9dc37953314651587866ab8c7ab2c44d19582b4873e61973146c1350b2323647ef47713608695a6c9995f2337a35aded9f8feb42ff03b04726a6f312f7a187511b57e6b50ba2da20f0c020f4604d42348c6860a3117c06c0da1137cacd76dec2625043ef334e4c08a2d63a0208273a9c01380267a3b743c33c48a1f900be6adb25368b581888ef1edb939bea4dff75e7695288ed789ee844f93c4bab7f8952bea88d619a78e63cae836943e1e7c61c12df3f331765bed2e879d8d070661d9029e95ce2811276121965dd21be8a08e2ad9ec40263640380c1208b12b8808025457ec6de3f69bcf677429aadde16c62b84c09b82840f316f00ddfe9cec8d88b6cd6202af60dd6c5b2081c52487e577d8b41020930371a7d49dbbcfc5b1c73e4db660dacb904242dd4622f06e7c6ec11cf42e27f728b951df3f05eab3b8578fdc52073e87e02a6057bffc40407dfdc7ea2653b56ccf26d446dd124e454261a09a0996c5d0e0829088af592cb9cb1bb1cc3a7e25cd1dfaaa52b5b695c05a9d01859348c9d53d8589a12ac3b64577c504d01f11a37087b924ada58b0ad019dfd8da5dc5829300602fdadbe8f776ab34ddf6604ad99529b330cc8b33a144223727ad7840b106d0d6f5ab38a0a41b0bcbb407ef7ba2b74de97ae2bc111156fa575981fd8c564605ba0a93c2aa53f7f288c2dc5f2df81b193fba2ac7782ee91bf4dae9728f467c259564f2d491ea0864e5b2e4065f13c19827ae4cbf200eac4c8ead10195e415fc0aef95348f4c5604dc70a9ca963c417f2827102bb9d703ca80e6fa4d5dfa494f3c9d08085350cea656e333019a38d97b40f80b22cad9efc0149948615dd11a2d9a22176b57e50171f894416bdaef7fce142c11b7c630aaf78f5c0baea06b816bec2ad2ae253be1439cee9a15666ee43d88ae62d7a22be89ae81abb922bf50270e6f0bfcc968cd2a69fcdb541b055ca5727bb3b2b6683138db28150edc056b470122e8f8bab8ed6945715d35db54e3187b6e4cb341e4f233ef3349ee951f934cac42bb5f54f11a0b36146237454d7bd7c9111e22bca256f627ad5f100cb266944a0095abe9751ad303db4d676a21bf1ae8c03c3516137f29445acf34dc6aea405e43f78e00f6561488f5db844cda305b08a5832ec74445541c11d9378f871185db83ebea313d004f183478c41d08dc0e7c95be702f372e27c16be2c57c2ecce98bea2ad3297a5caec7be0192180c5ee90735387f60c9f32fdc90c48ad68bdf59295179ab941225e7a8a06708c92ab246e8367397064d749a30045bc313bcba6e008b23852cd5f50dc1b35a05615b6242942450b3ab149e81121128c7f8d0d5ee921b1f696d5935555b71ce1760e5fce97dca40534e62e00102b24f0f116fc6b65c1cd949e01f0d5d2c3016915042003319b971dae7dfa082477cd9671f99c9ee5753cccda043f14461d7e253e9faae12268408c7b03b15efe0b7b767ec452cee89e8f6ad1b6003b1b0cdfde0e7ec8566f56552812889e430ef3d511338000322eeef4047588b26b5b664d82e966dcb3a32c0951f0e6a4ab6881bba5196a9642e4e9bb19ad35f8120d2b58b04623593f52ba2b75c1bc57c91f1c00a3527d705ea6061849a4aaa2ea838d1bae82ad0d9497d40a6f37ea65e03e03154b13b706da7d4aca91b6fefce50f35a818f44c8095333780ff26e6c5fab7022c43acba4df20aa353ca82d34956d7f04fe2464fa9a8b90f6211dc3926d1ebbb4514c4ac3355312813e2e2d06487c33d4b90f03db7d1694fc2a5f3d963edd01bb179bd715ca40ebe54cf2c33a246c6f2a9ebd85b14ea20fe16861c2c14ad40266e06b90f938acb83bf919a40836bd216d32975da167e1c94d45d28a7e561b8495a2a3a993c742440944dd8edbf3a0e5af93a051bf315fe13a7f09b21efa34ba30f96143b21ae3348b908e4d667c4931556a4c109dbba3ffced658f4e79d4ae67953777d4b9c66717000b8eb31382a72a9bdef6f1d6dcfee4e5ed07d066123e5f5b8f123103a63e816bb4981f80c684f68d825b3e00370e7738d9f03558f2b86ca20d1f93b4a023e807340b9d19f2ea73d6b1c54e4589ee149d247544bce747394175dfdfd829bc35de13e7d46e6ce447f78eef1864f17fb2bb803941396d6c82594495c9644ae900f2edc956709a05689d707e638cf5a17d354d160e17a7107b08122711d918612f0024ce3b20820c05f45571a84e1a20cce4e9379622140d88662ff4d5d3a53022728e9959a6c770de403101bd442c59c2d93baa055bd05e7c2ff0873a04cafb2f7901d5c63a0f5638687a0826946220b70736083032885591c7edf103774378f1872b81a71178eb979e91e4a196f0b283e813c7fc40993bf795489b2f8a8fbffd20690762c6d784ff16270eaf2bb3f308a200bd53ce5299ab0b52ed2ad4f8aadf2bf2c77dfd5d027b0769a86b7884eb301ecfccc573705262e89aa39f8514eb9463b68f66e03da3a0db948a9e66bb32ef66da4b3954e22dc110e6ded43245f2bcdce01e0d374ab9a6a6062224a36988a6f16595e856cac428d35699e8ea7f934ca913b53c57fad4bd9cf8f289c6c12a1afcc6181411e1ab1fe7c32265f6a524edfcadaeec45ade63df2cf30532520d7af140b180c8d6928ff82b4d8b640a341916c53aafe986c53277aa9a04b0849c3bc6dc3601f5805e884c0f085a0e9b08a02e015817b2a633fff6350617de93af83394a2f7ed5a42c252438e0a845ba69e01f4fcb8a9ee8a7abf1feb23ac69b52f69602eb0eaf6811c14995a830817cd20def4986e8cdf95c9539561af1034e48761dab2afbc33d9557da5eaff164020a82529fbda17616b33b2becd7bea187f596a365281d05e2e625f97b6e37f53584cd9311940de6f99cccdcd94f2cde2bfb023c2d9d9f958bf9853566d64124b63a23a6e0f61747a73693c2ef8deca29696b1c9b18b7fd377178f93a3fd8a6a89867963d1f7ac53bcd71958129a047aaa4a0af20f17930459b6a3d5884ebc35768ff0ef514cbf52fb432452deb87c5e8ef6f56e4dadaa51d6c3aceaef30e19b71ca78c96b4e1fd23258aa8f9883c81e7fff9967e62d988e3626a8295eac126d19e122284249d533f35ca9961bcc38d9f857d493802e57ce45a0baf3364c980cc438398db47ca1c5d131bb471d06bcfec8e1fa065c2fee2f9e48f4547d5db83874f8df00a0889fc31cf5b6b4690b778589064d2a5abc4b83e531b72bdf9618f0a4d7ec4329f1982a47f9da9742a764e4db1cbcf21bd0cc5d367724ecba91dacee70c114f4118343528e021e337f63f3f3c1d8da6bc0bc46617d2bd1221beca7789384a5d2d28acefb87beeee32367377dbd932385e2774b79a376b2585cd2bc770f7474ab511cf7b8133b986e802ae5487ed59548473d6e112b69e37cde37ec9ddae070bf5d92c8a72c0fac7ed26f07db1f597f0d6c3e1cd939a2469f8bbd17cc4acfb9a37c9a08c003ef841fb5f1feaf38d036d1d660e8c81ce76c0b1e363c37807f3974d8c4b8b861e6e929885409d3cb013e2adc7f7c43fcff201e27255031934893e8687e4fc0b3c12615d4d0dd3470562328a71bd277cad54de438a2f36f2ae8c5e2855e8405df4f5cb438bfb5da99d3845b45db036fbc4b676e7634b90e78ded75c57b68093eb70b0dc019ec9a776e59534f368ffc8d91b0b1062cb19f65e8f961ccb2a1f992a218f2a51247c6390c13e034c163d9e53d429045c475d74edaeb09e056130b4affc7622db2fe601692970a4c811b8645ae27ae4b11f19f891d21fe4eea113d939f99d1612a05e6c7f946c7478b78e52116a6079f67932a57a6189f30b789f731ddf7c7c62bd94632bde2df6c4cebf11b52c12b856131fdfe58377ebca1fc9235e1b5b7df2f110cb2519ad98b5bf12e8a5cc5acfb7072f528f6d4fc6744ba84596dd4042fe34182717274857307016f2ef1216e159358d2189f8085ee47f17db3d67ba5fd7d83118b2bf1e5c13ca4f00f6981575cac43dcd3400ff5ac58b8246e60ead28c9fc04543220df1d66a7a7cb908d88343f63c5c62bfad1f8188db64a3ca32404d43a28e3a7d4503992e5e7b95df159281494f622cf1c7d19457cdbf95b81ae49ddefd15da5e0e4d66caac3fbbcac3964f3accd38ff7acbeb19607839b293df5771caf3cbea6b1874d32a63ae642294d3b131ab97b657801e2e7599f14387d0b9b3e35bf8fc70977c7e086fbdd973fa17f69e7b06b718eb42b1251729632413f6cb864e3824d05260f8d582835d67e3b10304b96b9acb3f9fe4ec3f7a1e4183022894e77c5a47a7eb7d0a1378578d3a09f76e95c918882183c629241adede01839f571e2543ab6579cd48b3bf08cdf824199260d164d58f8b9e384990fa556138542ca5a9ff2dedbd2a214ef226bce141388123179e75eaf35255a5a45e05b8f0862b469bf08a7074fde261a6b23c59461a956b2a5d4b33375330c35c7bae16dd18d649b7b38fa3d3c0a3d29d82fca5066a67b52203187f8fc893551d6a638fceac8a1032749e2308bcbb234154eacfc66bcd556ce0787f28e84b4b74e5d511e600f82ad86700c67650e51044506e40291f2a3447f818cc15e6883ede1e9cd073f0cdb895e2c0403c57b9b10801534e83b589465b90baee44e16da9e4f0405f6387f9ebfdde4d27105ba0f03bb0212617f360b7944f4d62b7cce79105a88296ad4a74b58a5f9953033271e38a4a9cb72e63f194d21ac2e4a284dc16b4d927c84ef68e50d942c30be4d9158b621e594a6e0b47def803058ddfc9632ec9ec5550642406ff8e7edf2534a165126bbc90907414f8455920cbe50a8eb19b3c4cf596134e2fe2d2c39b2586070422733b5e206006358f89ac7a616600c6af1626bd45260ff71721843367a9051c2ffe7ed47064bd849db6d059e4411a81726173942ba88c75b69ddca9b38c4949e0edd8c6739b1362f465fc16edcca2ccb02bc67070209b4f3e03be1f0f916ecdf0057548b070676bb9d180f93247e47c9eb540d1665f2bbbd1c662e4587b3696f61c0cb1202ecd986fd6b52b711d15e8fe1e0fce50b7b398196b120bb706eb21ed0de41f898b056e0805528e9d1bd0836e1378b79e28989a34f60e15dbcba0dd7217e172709b1f06ffe76d78fafdda12e1dca2d3b511a2749d7b17e53bb7491097ec75bddf454634377e80bd356f29114dc82d4f4926759cefb0e89d32415a566d86700088b44fc610aeb9bf398a81a7159813cefa856b07b57b0e27e16e601be13f0e7c80dfa02cb495b2f0a91f2e8ff3e3ae7d0a8883d07b9f6fa939a85ee28c388ca6375dc6472031ada6430c0aca050ccd3f88cdcc612b6b7bd2343ceb2254f9c2ae711c9e22bf515d18ccf048238872b67f2d297adf789c5cc2ede4b7bc6262ada16bed652ea0b4ad18e5cc2eb4435195610b8a03fe6359d4a8953dcf5bbc05a7d781a7444a75754bce2402d1f3ca32a4acf3dbd97712806b3b108a4fdbce590ef0e892c18a4180b7e1a0f3d948a83bfc18b50632e01d6a662e1f43ce364d6540322f19a3334ac26b26691d9ae07dd3a90c4af7162314e3527cd5288dd193bf4c4c019556914f4518e980c8d111a77461d0472aa080ddf7ecb30f8c6c9cbbda54be3507a81614f3226e137a470d601f00d338c01ea1b45674163efe3c04467b5fd08e383aae74de0f41d0702c06b74a8076593fdcd409f8016625089e0eeba47bd8af5f7d94cce27998f59dbd595f8c79b0607b961d563c5664c80e444c9176c962b71fc90e85b0c9fd3d98b1c321e8649d6ba62c9a1e3b4e29c6402e520f2a60655abf316074bcb8e9d4326f50431b58012098d439642248c18842329a63150940ec29f1605ecea8a7145ceb33f1fe9e5c9b568ad71d4b45b54acdb4ce4a57fe29dc52603302254967a3f5805eb842864f404978c74680fd63c1d28c7820ac3f7a9cb82cc04fd1ed16f411470ceacb05cc58171461a11e592820513361b4bbb99db1510800611bcdad59cf94875babffab615a34a591509f72c0cc92c01775a6975486e2543b3ad9c71887f748a490241102dbc6a94543415fb774a5cce8a95a8a55f203ced63023bd8fb8272a3712950cc16a472f7270692dee2096de6863671957f82afd504c09172fca3ecff087432406e8cf05304fb22e18a14a02c1eabe09ea8a29dd41f99a681f664f6aa2f688b84f00ceb6b624afc4de011220aafa8175a347484e0eb5c32a97d2b120295a4591202d41d24d5a842f8e7e122cea4bfa27c47cad54859529d05188b9ed6fea6b6f5d184b66a50b5499910ac2d82f5fd33e7beb92b1b42355c2da085424c47ef935fd5b4b37cd851969055697a71af3a82d78b98229c18e9ff34b28e70dca98d0a2e1baf08fbae977705959382caf4ca9919e821a04f1d33f7dfb9bbd8769796b5a09d424d430883dfd6bcbbbed1d0c4b47d34a444db102a1c7676de4830969197f1935dd955e6165045a1063b7f9bafeac559fc6c22ca90a2b53a0a310b9dbdfd4675bfa3496cc4a17503523158498dbfea67df6d68571bb47ea6b8b6a3d1f4a826beb3ed8c75021dfa71fa1e677a40b509b818ee2d82f5fd33e7beb92b1b42355c2da085424c47ef935fd5b4b37cd85196905d64644453172abbfabfa3551f97fa0ba9df3717d7625ad06da106b1089befeb4d5dfd60e268bab2955a0a7508151fcd4a75bfeb6f6605a1e49552335841a88e2a77dfa76636ab2cf185e6afe4df547d8559a5e9bb907fda1625d06d42a1665652b2812a2244c4100ee95c7d6be527548285b4123212d402a8450a77cacf6955687d294a176f03f607eb5702efe06ddfa1db0ad0c0ecb47d36a444fa186b1f8ab4fdbbc6defd0b0bc9252467a081514c45ffdb4ed2f6b17a7791faaa80992eb370f8c946d2de677815c00980bf6cff5f3d19422d243ace1287eead32d7f5b7b302d8fa4aa911a420d44f1d33e7d7bcbbac76171544a8d542112918b05f2326877124009681080bdea73ddaf950e4a0aab5114a04b408121ec548f537eadf4a0541a894a21c55150ea7dce3f285e7c289b543a82bc91a02a2b721e04942115c2b053be5cf9baaa435969053a01b4085310863ded67dad76b3d8854a6c6876793d945c979801796e67181bd31c958da912a616d042a1262bffc9afeada59be6c28cb4026b23a2a2186c2e32933b800cae5d98e7f944f56f3bdfc80c1fead52cb52a23d380a3d5b85db854461aca6bd248540f501523bc9ae30e7ed19cec6580d23158285b45a32125613a8450a77cacf695568752d148a436f102f54b5024946f2e55760abb350f5cd4f78f6f519355d014a409480700ea6b8fa93ea5f54152d88a22605a022a8024debbce07005269cf394858426a0c61b7fab8fa6b5597a5d28e6802ea02a66288b46fdeac0a0b9d4b7aebc4fd7b5d510f27acc4087ef6cf36dfd63b6928af4991d12bfe62e4e18474bb3e424c774917581b1115c5c85bfeae3eebd687b1b046ba822667e148ff47e492e0b58f250b01ea05fd486db68356416a861414c25efd32edaf4a176765349f594901de726285b09dde7af5969572e12e7a152b115403d4e5db9210ca2c9db7e52f6d8c1bb890ee07806af666f3b1af3d7cf5b4da0f9295df2f170cdb69a98371573f454d9641d39012611d00e8ab7cbe7a624cfb1faf79e93c29c1cedf3393ffd9a535b92ede851ed8189f035b85c55119cd67362fe097948d1475b7578edac2a1e511aad209347fe65c37bb0706a95f1537435b3f9e2cf34968f3dc07fcd16abd2e0453ed8dae46d0fdb22165f3de5adf6455d9f4729c07ddf382885582db641de6824bf4815fdcb6474db7373514bb04ac02569dfe2cb1b6afb5a91f29617c5844a48ddd04e346e7483f24006b8899c8cc4eb985f5d08d58cfe53c0e08e19b6273698f4b8ea0c994012a5d0e482bb62e5a1c5a9b0d8438c59f4c41ddf7126e889c7be6f8922f5f913e54ef9dca1fdc83778087d0005f543a26d026d474c7db0e6672a7c2ccace018c8f7472dbf7558c5962c5c4c5d2195003351e6616950108e0d95c6b349941db7e2036510aef718183ae30bcacde0adb783587c1107b842bf4def4dd806776e26843ae3c821459c78d042d51b66d2de036a7a3d9a92080d935b4d5a025bb52c8ba5452f5b1205645ea5f2a1719b90441f973e1dbf63179e5df50694c78ee932e9d26e2b01ed5d2356c4a2364a5c9fa7ad53d531674931dc6b0a4dfc67fa53e15224f3a693d8e68448d542cb29d89a3082075d786f29f4737bc6c0daf6cfd1e0b85fc6878bdc99dacfb56ffede8df354b0e25939b0a5e72a12a007261d62278f5049d720ba6b800f90aaa92d3cb26bb4d52a2a895b687995ea8eda06b0122c00aced692f4bd12f16498b5eaed334756f0d97731690dce80de89bc0bba1e8d9855d9f39fca6ea5ac85b1596a1731937b548c025979b5de92958b4176c9238cbe39998ba6303c9b76af7c39246f42e5863a1e190c629d4717228ff931fc480a516b1a177a92b2fbb967e33d9b25cde7f3871130202889a6d8a32b110a36a4f72c0a249e33e1c906b518024b19564947968522ded686771d8f981dc40bbccc8b2a94dc0ec8f0eae1c7a5bee5f94fdc8433a7ce4f59a2e21b184d1794682626201a8349cc76e074905d8bea094a4cb3b610a4ad0dfa3987a4e7a65690f7f170915997f60581a9fbf821db52b760f88b7a3be7bfe6f15de365ed5d3b3912c9e768ef633d80dd1c83e2e213cb1b71ec9ebfe3cb6194e020991369079502520b9282d950e2dca63a7c30e9a6b3662ec9ec18769e4661010af47a30e564697e8ba6253f67cc83694d1bceea31527b349e304fa58ec26a68ee169c9a1c647550afdc9afa8c05e5e91dd343dcac74e81b1f707ec2501b781cb4ffc3db6d5fac009868fe47acc438f3a55d635b3acc4e345e9c80b16f71025f4dcd1cbb727b96e75b2401b73fb8407015f427fe6c45740493cb6e1e87a83064c543a0dfb62ec2ce28e9c2d6bf13b437399a130048e31da791119a93e6643d33b91329a5a79aa351699accd9143f0353d38560f1e62398c274869c9eb100f4394a622c63d67addf3cd174d741f6742f8b860fd308a18da29d81000c2d71c1de9f0ae88db8e7ec31638a47b80a8281fb410cda77466a90cb3a11453c89211e09aa3154037190f926fc061ed0519e5bcc0ae22d009f458fffabebf1e08ed64467ccfc1206373f5386a83d4105e37d316cda3b491d5522fac18c78b453debdf87afbf1010c9c4d8a34a49d6e8853440d64450d6b5df534c3895c054797b7ad76eb6fdf61c80d093d4fde7a709961747d9b0cefcc71122112bd6f06f3bd1191db62f1d783eeb2615becda17ace71c8e93dc672ae41d401d1ffb0d2c963d2f50b680de883db2750f47def3b74d5dcf4feeb685bf8c302b6f1b7f77db7875dbaea48e06e012648adb8961b3226d27508a395c9e1bf5f87a95336e85c90ddbca7102073ed62e1f110dc6bb02c48dd4764efe94dddd8adc2a3b540030ff94cfa73c5c7333af1cc6cdbabdea61f7f180abc32beeb38a0330e7d2bed8887c6bf05f8fb5513314a815008fac23f14abb42d68f217eb06e3b63d56f123f445a43acc0f73f95298a6d344b0b0674effdd4eff902ab15c71ed4db4f8f5de79c77bd75c105d17dab516ea68d9f9dda3d79d94707bc14697ed5e92782b921350d5b3bfc603fcdba3c344625cdd1d4e84b29d30dce3167f1957be3b7fa774b67ef183dd57a88549d9e32a56d374ca1e9c7be6e9047bc0d581af85ebc8a3c06b8641fb750559cab815b0a2ecc76945fa47a87a5907a014213d09cf563f7ffa00162330a69d6fea86ff3f7b6beff04cbc50b246215184b58bb77dd67c8ebbb6e0ba8207e3329fd109d51aeea87a30930fb69dbb341e47bb06cc912acbcfc49d1742a2eb0d4365cc9269afd4ed6e3130d49f1a076a20e9e22b563656492997c2424a8a5012eee067c2d7ccd3a81bfba8c1e8e9f41c0ed456fb4e58cc05474f950d6527f7e832e2578e70f4b289e59be276731f690782f74d7571b49d7f18a5591da3b5604cf26ff5cdedec78f2b6dd20b1e954645927f8a8336a22e8ab744885ab4add013da939d3e17a1c6c2d6112d332eca09d58e381ee121a137332e4aebf0c09bd780f10e27a3f4a109bd7666524aeb11fe56daa71bd071ac3bad003e22a6152e82aef5a887869152d254d73dd41c525da477ea8e274e99f16cee57683b9cf6700ac4aef1a7f031ee07b47b8554730705dfddeef16e6de5fe24664db93f95b6ed2b3a2cb056ee1fe230b03d840d9bc3c9e8d65095eea05ecd29cd613be566b3500ebaca751dc716e72fa0d98f54786fd2773a3db50e36a62ed78beb41958fdd51dab3c38c9602fa585130bd64272a0175574d092f5877025fb4ec184e58adad193a87d3de6b26e6f59d1cfc042cb0ad33f2334be8e74add8a345da6c09d7dc4aef2b05d4992a9254d0027b901e7b454e2068bd03c552c9e571ed84bacf1bb98206a181cf89e9be3fef1e8adc876484684c6ca9c760abeb0a18bb9b973d22493b7a69e9089c8b9c2375f793c3757a55a81aff30c9f73d0dc42460d289631d7725eee8ae69e4d48ae9a5b0ea30c4ba2bf227d706175268e2fb1b19130fc736ce8c541f45acecc9abc300c1a10730b835f5a3029b398acec6edc737b9b63c18a4ea5076ea3be96ec4643e40203b305042d1f98b6410dae842785d3617d98b1eee96d91547bb2877b649c58aaac6ff3fa60e3ec47a9cc9bd50cb53fbfdb8fdbd6fd99b679c2f29ef9f84a7db2bdbdb8fd47d97f69138db1dc14fdf3885de807d13950fa7ec54a94fa6ea5a8914f8bbfcbff07bc1b18c9093af1c7448e21b3914eaa63a2b0f2dc005ac15eaeb8edbcefb1654c14e46162947ca6429b40adf3b57fe77d407ec660f14300a358868caf52f97463c9fd48963d1e45fbd2147cf145c4224677062ea450accb2c3265232e479aa21c481d24e21ba944096d75dd909bef8ff9d3118d39e3ea20a7f2469911965893decc24f5764cf28f7f5d9df1a25782584c32565ddfbfc6c9c4819c0623b10a71a399f82fef097888fd6932708d08f49081356b420f3510a27c72522330503dd2c4d258ebacc70dac88e689e20e5fa36e72faa8aa4db4094ea88dfc481d24206c32a020ea9a878d405361883ad287f8ede212bc60d8425bcd961710ab0af01f53007d953d27666721d85ff9f0d83df2d8e152a72abaa2dcc82551fb334f9207c9a6284f3f5206fd9042a1a6cba9b7cdc68278cfec8acb4e18272b63e2e9efb8cd5c29f7a36eea5b618d7ebb9503cb854434b5381d3ae7acf4998284a3d4914f0173016849666c8916610b0a74c9bd937bad76604f04d15e049bc9a8e4dac54b77a857fc474e33f3c4afe027ede2f68e8a23b3fd9585239d0828675b0448c84620e0d62270c009455818af31e1605444c4aeab2aafe069982b412ae14c7b486f66b76331fae69e8f7392b4579620a1d9affa0c5467a9212fe2b852340e7980becd2a00de709728ca8b3345de2a0c1f919d6b77707eb6352628ddb626168f275bb82d378c16b154e68036d2a23c3460239643c814487b15c95b75447546bb1dd9155c7c46f8ad90018ba826ad98217f8764dcab4f7f74af48f94922aba743a21d2dbb1430d29f7bea1aa0e1b805e4a70329668d744ad3de70c19e44bea61e5e194ea7f442eb7a3db8bc6e75ec3f66ac71dd0a93aa364289de1fc4be0dd77d9bd6950cc5fef7e5090126f368e162220d1d22466a5e696b7936901edb4b380dbcd8cecaa68ec42c3a7e8538bb06c512b1f6b0411e63e4bd00916c6ea46c52f309b6f7145e36cf0709f64f2cff840dc921b65920f3c840094f0f03928b858f3e74cedc7c11a442f9cb7f2be761451be409e9ffbcad50a7305055d82be53c6187806edd83a624cd3e8ba8b82e92dc9cfca52097f461b882734110fb8acc368118768cbee84228806b0af42b68caeea38f1f0d193ae0051d11a6327f9db131ee899b0b6f3bf40d0133130941dec0cc575da708e334b7410e507d100a54444acdaf7cd6478fd0339d03c1967443438e13b3882a5f58e5219ad47e7261c36e57c965a4bf300f0066c67a3020668f3c1343384f02e94daf87945aec81cc3690f88e031412b4a295f7b4fbb374c45e001ae40f784641a798b55c7492f07652d5a8dbd199f798cbc47282eb936772bb1a8bd4ef1df1f45e63e197333f7f12cee4d5fc079b2b3efe95595d85537893b78fab9b31f9997e212090a0bd373254d8cf7256b18036f8c51c772380c325fa4e10d492a6e82a43daf8306afe78da7bfc0b7c07ac86913dd1b289a8cef772500275e243df9fd67b4425c64efcc7b77a6e40d7df05f054ea8808081411cf5f8fa10f93914bac002df9398c2772a8472fd7d890ec4c7f8f6622596e00fd53c19f04f1175f3f75c3ccf45bf7fe6c1feff78230962273a529e1b7649fcf1fdc53575ff4f97a85fc2321b60d2ddafd4d262cf29678eef141abdb516d215b7dd6cf35127ac536f902890aed620e5107d454bcc5d291fd3e5bced9bd01f4b2ebb87030e12bb0aa5830da4dfcc1d25e600640fe4b1bdc815ff5ad579566bea1f3ef18f23e2c7779b8789f13d872b3b0ab771fa53e0f41e4adf80ec99f0954d1a809015096e76aecc1a3d01f771b3f36144864a50bee6022b6c8d0b67401531924ff488248dbe708a9db730d40dd2beec1b9d5cbe5d65b764e08bd07ea61360144ab1778b56e8992091c37a22e768bbd809a5d7e76f42ee8e97034c6a19830ad4161f011a47c3d2ce2cba2a0c15d8509b2a0710684636706736ec908a338f107f3c1ed8f12ddf2595cb9d85395f41efc60a54e0efca333852d47f59fef7bb461a785f82faf4842d9e4d9119b9a394d5d92b886b79e1abc66435274e5fffc45224d2d1143f8856bd7e3f2f70708ed9e5a728fab7a50b3d2a89a80f95351441abad3bf8555a317340ca97d376cac51ab56f123a65d018347a479eae83d98bb71e743b94aaa1170152fecdfbd6341a8b404a497021ae99e53553bbfdf5342a1bb95993daaf38deb3c3c11ded2127cd12307b6c72928d9f556751b98027d160fe59b911f797b530589b3dc0a93a2ed50cedf7b73550ca65819e4ea69595402053eaccb4d91fafd49894b3cf82fd805b2bd38f0b613f5c4f38acfaab9bedfc12583425c85a1311598badf8385a708a1cb983b75e1248448d91072047c0c517579d82111e16ab1a576825ea82233670ae99c24132bc2b7f7054b0e4b5450d8cbae7faa69e418e31c13fa1ce14f29f627c2002a76dbf1150bf3da1a9d2d9091cec4450bfaa0762541a77a010dd116744f030e15a234e8110d4db23c80f47f27776f0e37ac1ea056914c3f8685b2ebbf3cf59365f47663d5ed24616cc57604e009ff724f3ddc772b3d527a577f9a51a2b3ad8d88baa6d4a2826694009b0f34457122f8ba9f603c9137c9a9c42fdcf2ec3f54a80a52d438fb6c87524859f8ddbbb81717bf5c72c21ff743978d843fa7b1219fd446469ce4bfd3884e39b925dd4ec8d83ea773b9659733495553592060060b17c05c68a75dcc4a7e584647b79294ad30b87dfe56fe77286b71cc68fc9d6dbc46231c3b7c37807363456dc3660445b50de697e8f97ed2bc13524f03037c6e39f9601e6be75d52ae4161b20278eb2d491104a0159360e3581acc9cb9340e554ce4b75d90033ce72717535f688c75add24f947b521272f4eef19de4404dbb90faeadcf097c426e783016028dbda537f6a20398390e00b9a207ae23704fc3c420a4b1a09c8a9220c72e864766673fa3cb9972a5859f19d805c2d63b027055a6771b37e7f4e4c1058bc5558c30fd012d5f1206c12182fd1bb6a733ebac5f789f4a69f352c2aa15f683af65237195facf67ee6e8bb0364455fadfffeb1ed4f9a2852fa50242eefa35302a7b681a181c5972b3aa1418c0d739a275dbb6da95049569a4b9a13577965c788a226b54bf21b704f3e22c77bc71d8da9b5101e04ea955d77eecb9d12779877af9511e9ee93def6b48d0a3f2e6aab5bffb40a94f84e0980e9bbaddd4653b1b1ced6205a1e01118fd27c1caac8f2a808f6c6d6030e4131cb27f4d95db37153e3848b6132feb919fe711304d1aafd661470acb61ee4dae9beb74a0fb9f89167166dd47065647bcc4f3ad6989bdbbae46d6644682d5cf7209163c6051fc089f349656f535f30e69fe5edb8d15b7634b41c325e407a5fe99f2073ec0f8e15a9d17fd47a8e704c241fbafafeeb045fac40eff909efae779886e71976a66cab3a1729f86212afd3ce7ae62806eb257af9199d3b22fb86603112cd10040c0240c7ef7b6013fa11c3007cbdf0ffa7ef4f9e4a4b4a0e7d2155306ea8e64025b7ab2e7bf4b3ce66246157441e82f0777ef649494c26eaa21fe4bae19a25be9d93cc1232eb471078450d89f7bba342a409af112fcf1b06df8fe45be350175cfc00c18ca6dcfcaf0f114d3421770dc4276b144dae442effd77e7970e3f310ae1cfda4ae17c92cb53a55036fe6e3ac79bd3615f43e1269f85627f3b37203ccddf97c68ea301e77188f1475718cb8c44dbc821ba53cec691f4a782e0925b7fd31ae3f5377ee873b6a97f3e68f16d6591618bb0d7899cafba2e5f92321ecfe8924d579d37c139628ef38c0e2b3b973ab935c67e28b0ccbd83231aa1441ebb33d4af4f2bd72b60cd4b785742a7cd1c05bcf9ca7d6d03de1eb00b6849d35e1594864a4c3819cb72f94b48855e52d7ad145f44c72745397cd200d171ec06a8fed26991badd3da363df402b17cf44f419e8b0f5b8c352c9dd4a865451a5100b0328e0ac5eabaeb77079aeae1e654f37ccc1565729da2c532e64ad6989312d4aed31f8701eca911ee4667f91bc8302649eedb07089d8926d89f7fd742ae3502a0cfa071bf7d5cb5844bd01f6223d41e5b01b2a817eb32d3a59288d960b7dadc6594070e806e9e15641217772229db4bf73991e8c44a0017da303264f49b0bb04236503686b4f7a4597856243c930b5587de80bf9348b6bb917759694da61448ae58473c76015cbe144a75397e47e97c6a7f658e7fcff2422585dfce2b99e4aaa0eb680c6e08354170e27233bf80f86adeb9a7ea79390e12764b16499c2689cabfb00f30911581c893b4a34e31ed05c1d31311fd7c316be76e34cbdfcae1707446f603f1685fadafe3fd93ed88aa2f7fd71d42aa94b32a64a50873d222870cfff86da7c5bc5ba89061b69fc2ee413e4f698f303d0600cfbcbdfe596e098e8bc8c5e370e65041436206c49cd13ebd38914dd95945ffdb1bddbf120b64f857d5736d43f5f1e10f1964618da4f79874668d4f58881f129791d8bb0808396f6905137df5648dc5702d86e890625b633352f632f543fa6595548f0b42d44d97795b328f2d332630dd7d35414826ebcdc42992ed233bdd5775deeef948ebf255bcddef4314bfb2918640753d120e414efbbaad8f5e676a84eb7f177b624e627c14a13dc736c219022c2b581a9e1f0ad0c0753d11f737d1d9ceef90c6bd963ab4aba407a4c48586ef38c089b4331b33cd1d1a62a2a51d5926e3046634834cf5cbcc5551ff8390effa38cbb7f1adde344a9e358c964290e4a07dc238eed504e960dadea3ce55862d4fe64e405091b6f0a7a6fd1d27d081f5baffd6982c77c612fa05b2631c78a44b7223608392aa6de8e3d33d9f1701a951689a22d542ef1ccd540e0c022e9d6d1ab01a72c6a1a1ed5f06bb05d4b76f2b681ce05412ee104b9c39db2b1b831fa148bf9acd966109adac5d6c448019ee7c6b4d651a36de8136b389811cd179538efad2846b035a2cddc84e0444f9e267ae904aa5cf2b30125aee6745e7f969ea77d887162e5cb19fa4f20f9dabe1c3393c1a2c67524960f9e7961d1f346102ee3719337feed1b29f00a5c68f0ccabf320a476ca5cc0296ca31fb5dd1d86230d5a82157eacf7e234e8cbd4d674aa6483ca07ed8dea5b70c2c66bf2aed9e18a39a78c1b8f0cf1137dc994194fc361f7fd494a4908cc59792c0866993c56fe8a2ecb00efaea4a637ebe4a690bc351ee74bc33ed34fa1204c837e0b8bf3b14a33d1e35a3be077c276615ed24c5848640204f76e56634d5063ca704a90a45900d8649e00690a349cb819bc21ecd8a0a123efea47458965f3391921004b09aab5e21d600b75e08db15aaa9e5873c40920aea3b0004daa0b1bd5ebdfdaf59efd65db01504e84b97c622b5a7bd414ffacd0bc54ba14f2120d56808d7ddfa8337b64da75ef44044feac99d6d647bcdd29377fc5a82dc95a15de65610862609c661a7b1cac331ca6e5fce9e4a47dd5cadab961fc5d4061141e0836dec1d9f9e221aa797f64e751c2f443a2ad41efd12a2bf08ae8d39e21c0e1ad155e8225b0bd2fd05edae544c05bc2251852cb65d6b4116133a7990f8983c193200c330e52d151f4cb64e280f2e59986e8e29e2aca9960d0a4a175b50a34f85b22f888d7e67b237ea3145c08c6b0fa6e436bd0b39bb4a05964deb512ba39ec81d5706272e81ea7e614f20ba2576702a952f026325f325c5c19b5b658e5526426ee258c5c42ad8771ff2b3b53330d4abfb77001f3cde68cf9e9d0d1ab91241756bf305b8914e4feb75cbc0c77811d3f1ced2a425301eefaa9821451d7b3a8dfee5fafc2006efb9a8afa00302869c92d8ba03c1072424f0e3f7ece8e2555749eb510ba8907bc57f6ac103f06ead1c8d293044bcbc616ddf1118c1dfcce3a646e1f3a8b1a328304c0db970e57c379811cc1d3120c6d5af82bb2e7585967b81c11330e3098a16aabca271adb4031280f36bb381670045cf4176230561ca85ea3389f6945f131e17619d1b23d3173462cbed3605776b321129d2ff0b159a938a9bf379a90aef04a60509041b9a68107e422079d824d56ebe6092bcd0ccb10e60c6abe8dde00d01da8bd1565bcd348f896f37514469457db80e284d249b7c08d0a2e1dbe8181dfbd67a11941f4c05dd5ede5d9b67a5033792bc1000eaad9432595722497c32ea07477779a4896aa242033daf3791bffaae28e2112e45a49b87904e774b7d444f112958bc02baa4fbcd0a0eb3a73b09bfbf477af542171c8c0859fe30232cbbff358dfc7123edc7fe63c4176460d3b7f1b5d1603a990c1366b6da42093c36faf154261b41b9abdadf5c7caf1a0c086e770318184953d377528ffa75e63dbc66e8761d611bf65741cc62244491852eb90ea56454166a9a629192243da8f2e18e958b2f74445f8b163b1955eb2b2109a4e13c3027e9c7b7756cd2e5007e43989e2eccf7f5ac2545277e494b01531a1265fe0ffbd8498fb09eecebd704ce64f6e343941be247030668461bf23dd9c9d6653dc9952257101b028211642eeff438933ed7711b8e17c480fd2b33821168a7679b0403ff24204898305c6cfdfbb16e932c67cd398e0460e508cedb7fbf66277b67fbf28e52124cfe8b4811eaead268c3a452fa234d78bc6ee7798f02e0b17673e9ed0ec584bc69e373bfc29bc5013e708f6a4a3cdc9b67d7d8e29700973a8b11d2bcdd35223d024c1c14142f7d6326cfbe28a8d6aa7e09b5324ce8aff5f66549803f51e3f7902da9390550a0a95cbb7238b90489b8c6ea2bf38f217d5c407dcfddb90f1c80c516dc89b9695724df8f05b060e6c6bcdf710d26bddeab57283abcde270b8e49a576f4d3551f13de77c5e5dc9af6d4ba93c1353580898fd4b783439d6b0f0b5d095b897c153b05c45fd7d6d44a18a28ed7488d69adfad3ef03eebdf796d33ed1869beb215fb88df4438e314dc22f66bed8cc97d0e3327c0c828d100a2535696810c2d81ea08e3e214ade9dbeb82b612e2be0a5026a477c805c43731638c1f4934616ba170360be81d558c22e40b0ddc51b1ccd283d6cb7acd59b116b3cc810236d63a96ff132e6334eb2b11577afe41bfcbe77c6206912cd0c16d9e64000c86613cb43064ab94030fed5b36cfaa7d39acd196c0cd99052086132a5e0e8a959d6ec6b3ba20b442f79f904d148da7275d562b467c04d1f3b0dff5a09e593dda03862fe29e5a299f4d4917bd81473953bec0bc1018d645c84a9fcfb59e97e60d24385f00bd94446d39aa001aa41678fd591a752a5a5f913fd55ccc7e7f11f0820fbd8df11473311fd6a63016d1f02d8f75e136065012b394e7c3dda7439a5119eec65ce821b13745604429dece2ddf1c25398ca85562b773d6dbf523bfbdaff1f255f6436fb83448d9557ae290cb51f113b860c6af8ed4a1f93a4038e85afeab32be5ae1c7530fc0f905741f8757efd5e5d478e4f06a13bcf44d1c7e21ae11b5c056d90a37bf07af9145856da072cbe5e39ed19fe12be7e7a5de79bae2d7df4937d887d8d4382aa3afa55e4ca746a904946a7d072b0ac5bc0cec9b028f37f7044b96022f699cc35314847f4c416238759182fb0a90367411df06c5bdd82267a7cfbc89289e872efc324e8fe9baf0e16e7b5b2977286a0e2c45112209b2c7f3f1a955b04a7a98890e88ae841e40067a66a88fd998d1954d153f2b38cc150d7f167da0fb98b3a70c55fe4eb10a596bbbf7bf8441f363992f8e424a5cfb6657b126b0fde67a452f76ff2a23447d5fa0e67f30a8805156e35eedb1462a67f7419d8c9dbeb3a6dd077c512865b1020ba88966359a4f805cabda24812fe6f27040c9dd680659f89339225dbf7e8007d298477d1007a91701214871b48a3513c8123a37a75dad5bbe77222069aa17a18920b99e636d69179bb47a729752d45ad829f4b58c359a191303418e17f06d3ad27e00fea8952352b1e3dd3c78ead0ca479372beed7ca8e32b412063a68d4d1f2265ef73c35e7fb649db674a31c44775a8955d290f0d7326e5ca31c491fbfbf4ff6cf0ef402aeb8ee5ef475c165b0afccc03e5dd4fd407cc460d4f3af07c15dbb806a9e31bbe3c6572676e64ec75caf3ee31e1ddd3dde0378d27e86090e95646de971c8d539455ab74f5cf70a617a800bb4a644a3436d302f91af434be1e76d1ab47bec02776e994f69b907f23b5c9d297c0c56faa6b63f01be9350014e52c87b74b3a1b5c171a505bf2a22cbc9d3e3d410359230b63ce8d71c6ee628d4717b01813491f112a78208d872b20d327fd69e572d5640a11483b5d2492d5b8ae3d9314835d67f39491457ae56df8546d9e75ef2b54d8c4282a32a9153a13cbb5650030ae001f7a9139c0723454ceb066d29067c91233669865b37cc2a3674d691bcaee626827f6dadeea36486cff0b926f98a8916423598614160d05c5c8fc425f0c8502bffc8173ca957ee4f47612fc0842966c9665718bf54ca80ce1e9fd9910e51871729608138a2bff5a7ea11d3caf453909c204f260adb1b3214c8c4e482a00082449ba842f85cf71047430cca97109f8178431e84cd9d0b2095e69f5cf51290d4cd56218710210e29799825cb514f6e50b282bb0c70de38668906623849103c014841e9d653249ab4ac8d03c34385a7a0f6b8ebd722e3818ea0a13435a35b4bbf8e49e23918653f1f5215120cc109492d250f0c90b85f7dee80b86fbc7e73e88a4c014847f6f8f61e39f1de5398261b40c08c1e1c4244bc0f2aeb77b2a7ccb80bafea163db415391250cadc211c9dc703777c38f07cf28b0ddfdc7414375f2255cb88595dfe2c72f2154df0e1db743e605334b4cc22af08f31186345add94a26e51a5e92d593f386e96084eed89ebfdf41d79392589ed642a23019514e4237fb6e443a39690fc36fbe8d03074f5ab7cdbb5c033150d5a5e928d11e2d2587cfc39678957191ce0d61177fd84a619e073129ebc76a0e84daea9da1e45582d59826e2f7b6b62ab1339c38819b01f3baa7259eb12732c0f77264bd3f09c0e853ebd9555d1e7de96d8be9720041fa5eb0a91ead424900646d8377511d7148fcd128252f9f12a3b3265538a5068f53cbf03ecfc62992bc36d781060e9a811bbca1256b6d1784d497921e0b9e7ffd19b5be079d7f3e8320351521af25a471cce18f0d28a277c0a5a64bc82fa50fd846f2c4da31e14d6709344b8810cb5ddf2a215e8b6a09e997d7f3471544941015c4c5f5e2dc5525c4e7803d41fdf903b3f34a08031026f8f8f5fd6bcc28f912124ae90c91a59a08c9aa08db24523f013bb0c8bba7ce44b962ec498c91bfd670205b04bb0c3958740d72436ac50ec887a28ef648814565e17595890311574f23e8fdd590f31d3dba7ed79759adaf06421f07ace63889553578afd9142c2e82faca0f6982fda4110a0da63312836768320956bcba0158fcac671b3f7f9d91acf58c8db68031ca7e68f0f452a9d38e142df1d98fb06bc6e60aa1e00aafc9d88a64966f03b93450eec0e8bc74b58411cb9f6ecba8a4c422c64003b830091eb99a268f7e9920678a69ac8798c4beb108fd6c2c7b872c437b0f2a1b4d83a75235d8eb4d08ebfd952fd551d4d81cca15fba87e014cef4ad5d05bbd8eed0c2cfda4a4b87c897726d00bf99439480cf8efe8feec8c08d86f19b566cf5314fb7497017888090bc5696649cbd13353acbbeb819aabfa8f9eb64f90c5056b0fc39fa8730cbc5fd7515d32f4ac70244181075bab8b0aaac42bf094e7c8726f69604eb1ae784aa6962bc9aa06562bfbaaece53a406700b268fe64843949cd66642b55cb0e6cda29552df35361751e6411084b6ec05fbb212034c8a19c885cf2f4a01d5cad02ccbeca2cf04f48cc0ed2f2f01b528526d711de82a2e59d4e92b976fc02775ae5b4a09377ef2d45a862521876851c19f845e4aba3161075527052e063f44552e555d3bf67c5c4ab604590950923f4b28cdf5d6aee7836026641894f7619b2ebcfd13319e937ef347b4901e2c11fb35cc2a9b03b232338c073304edd30acaeeb3db5a39f43255d6f73ba7e32097456fcb42958590177206a17376405c742536c2d3e67bf0c7d388c021c3dfad68415f3db4305b2837ddd32e253e1f8a571a385207aeac822454be6813ec69bc4dbcf1b94dfc6172eec201082d3e5dfcb53facef34067f8e22f5e463ec113e5e4a56f95739d04422899937f9a694acf8b60076bf504dd8bb612480734826e6306bb48994bf106e6ad98ae0f2d8df13e5a87c5d8414bc21d3944404800218871dcd5357b741349dc74715ed0c26995c7a2fc1af14a38d5deaeea19ad4666962c3b484468da771b3f0dc15230dd185c94f7841702cf3d1aa95764ac544d1141dd9ac9e8e30a021f75e749cb9bb6c8d71ca23812f72d5cb8705a3a9e85e8d6b1f6db567ae2e765e176138354c832ba0002367525ecd359d9ae294f72d8cef0d7e99652ccde4956ac69cf79f213b380998800a5f1a05bf79b43b8f1b68dc4f6671e5225ad9615681d327beb59a3974908d8020dcabc9aabd42a22708d1323003b40918cc0c0b60eadd2f93e27ce89d853f49c33276d4b3726050a7dc1d5ceb9ba69f269cede4110d216daedc7f13bd7422e17fe128f3e13d9f3dd4ed958249de5e75068faa4b62345e160e3f7c708f009ff1aa70ed9dfa2ca6a0abd8e0008ff020bf1fd8d24d47924f96333e5418f0a285044f00c69eb5156d3ede4643a5a1ce6947ef1252464515989ee1611c2c13e66f61feb5465ea62d696db8858746ef52b971d1b84f1aa2d446e7e5c91178185a8d7148a498c672ad5131e224447b4dc3e69cdc44408ef1520da77486427880c3ae456004da59a3c7fbe81753d658924f0bda449d4c8005a290a6ceeb048cff2315f4845daf7ca3ec41edae14403e9bc0f49ae1d916e7621ed9f2adf82a0c3b040b7bc29efd53bf652ab11e8c0c8503c236dd942d6bac59e9e40c00b52cb98de178c31b8270bf67611555654ff50697a51115d6c910a00f0e0df56b277f9e27565ad917600a2b2bf7cb9f024e0ad4d0bba68da5226ffb88316f075b32615c917c262edc12b16c43a9af03af3e96c8d4e5383825ef3582faf2eb90f7f94c5d73e550bd918b83bab8d860c757cf018504b820f6e3b23f43723b3240c04dd60f5527d9e137223b28ee1725dcfb1c812fd7c8bfea284dd72041e3dca6ee421dc360be9c3a90fbe02f15c39d6aca0dbd70c86d5874fb4e414eb8a3894253377eff1b25b6d287504d8f0dae6021e417affe671358beb29940e35b8a565ef0e19dd58e99246f82f84f306ace64f94c64d32d52819663d36bba18de728457ebd73d9973de25475f72241ace99df84a7e617936265b05f2b416e4db1f7c08d03d9490231d05c4a4ff083e65daf295b5e88827a53fc8a80e97cb186384babea888395b4a1ab4dc93950f2818c55f56963e4536fa04205b1fa703ced792d08f1101e9e07d759ba64ca00112cfaa0faf7ebad65b48f2b105d63e492f76a7799b6590775d1658c5ffd8230431a1a452735bb0d5f326e3456c1eeb1e6ace4158d49a565ab066617dad28cf58c8d5afebc55fd63b6008827f0b56d82166bb62cb1b8ffc64bb010a5241485b404a71950e110035b5253ac87902ebcf10b9247621e9946b425e361d2102fc5794dbf53b616b30066c57acf37b5ba0f609ef14b54fe07e9bc3dd6da680b8b7f3452985ec37f6a0d8b064aa1a81a8175f12b2f7267b6f29b794490652048204d504533146d85a887f47f336485dbdc05a1ad6678a8dbfbb7f3f14e2ae4f02c9f6456b6d77bb4a150280824c9fe2ae8afb7e47bbaf42bcd90c28d707c2fad333d247bedacd8494cca1c25ddc148787cda6000869932a44bd4d7178bab66ff24647dac8e77f05718ab50c2a884fc8763c5b1fb6fbe7a8b65a12f9fcf6e82784b482b00fb4b5e9ee682aac67800a9df77188470edcbfd2e8be230722a8255460af3dc20932ed4209325254b53fd3a03efd2f44f6aa67d97d0161874c3b5c8530b99439e4fb4851b6fb201c810d3f5948a69df85327dcc0b78f864c3b59a5891ee2e4f3f7b543f7effd0faa1a7c7ffffe540a6e4f468af29ee2bc770aa27befb3f774b41ac8beefbb49d44b25cc61468a72dde28968bd208358d0f78d1fc4ef9bcdc21361ad1a963f8496822af4c183d0907c0a6d2ba282a09f90909087da27fe80796a86f0f3325214fd161f0b71a6dda5f4fba8ee3252d4d52d9e68562fb04f06c9ef51a87d110dc8404409f914f10011222cae4a8513403972da34778463979be2f270ed7c01f247defafc20503520b2b83c5fb61592f4449dae68c20d7c7ba65eb8816f9aed2345f9062ccbf5ec4613dd637dd6ef3e9ce0c39a5c9fd65bd469a75285c4ef485b6bd177b91163d330190fd4a6784ad8ae1d50ad3f1d290e4f131c1eab6aad876f67eb8bb8afafaea3f449482bd1f9f92819f9723383c7982f2c5a65038d2d842b3482382919f9bea27c69e9fa52e18b052ee7d96de5ce03994cfd8b808ad1dd8530595034f019a0531ab0f433fd1909219b811ddace5a10b682a0ee1474445741e03b459e5d53d45dad8b1c245ccd8062024244090d26cf4bc7996fc6178f0f0be7e562875f9b765f495e3fcea8d8612689150b1b42b864f9647c09f9b0ee98b903074c101830779ad8b9fa72ef58d92e77ae76b729ee4e96305eb870a953a78e8c2b7a268c549124584e40a3c217c34e922c6707ca4e905acd83969ab5d65a5bc3d55a6bedf37007823eefc11345d08679a892bddb6db003411beba2ee2f15cf7231c6574bacfecc3e4df1ddfa7c0749f134c674ba36068930c61ea4d439dab24d7174dc745facde583a31c258bdb174420873db5409d26ccf8c169cac37715cb8f77a70d2ec7bef9d32c1a6b839521bdc1437276b9fb986f142fb34a4b2af34a3a8136218d60f1fd427626fb591ff7f909375ce76c87b99ee1ea6ed87b476780e1bbec36f78a3a8133f5fdf7bf7effe847fa74fcc665a02da28ea0487776d54b343de73f80e9a8336226187bc77edfd066d54337a3be4fd8fa676c87b58ec479aa88da2c037121ff6a03e21fb983672253be45ad4461eb043551bf991527dd746d40ed5378a3ae15fb591f7fe465127c477efc5371a3fd77a703e1a4e3ff2f1276165344af4d1bcf7a119459d085fc3cbbe87f681f8b0cfb40fa24e88da99ec90f7b0ff40fc0f7232feab5dca0e79ff818da83f308aa20025ba8f9f12c1f407f729d1e952db43aa71b8e41cd59890fb246a082401fc14a044f82be04497c8a76cf58858ff202a2a0f544142015e98ecf34b2ad9db34f702c93e6bdbadb5b91750db3ea5bbe60758626ab952ab4da9cd6626d5cc94720484879b17759ae01942f4c2a64fc202388b2aa4bbea0ebe6ed7fdd5a8448a81ff68f728c6b6388a31997f3dca34498e058e2d21adef349794862b97d4b514d2201fd185b0e993388052f6afa8d62f4fe048f229944bb2aab92429bbfe870c6a965dffcb5842dfb40b69f5c4f0c5574104347e6c466b16a2a410253d08e67d104c9f411474dd5bba67f85d44216efc332a9d2ec25ac87fce4cb3adf2ea1c089d366521b5a94e5137a5a405a193c9fe5482ce5a6badbf75514ccd7c320b329ffc58d0c3eacc8f275d1bbdb2f4790c4538a8b9ddb805b9c57c0c01bad81972e1c746bb846c2c6e386e0bb06dc1d160b372f32d5d6e329b14bf5561193e366ca3573c6e7ebe50472a4aeae88c0c2b271a8070dd467cdd445b190bc2732b14e0002fdcb428b0f99c01381da53817d828941f373c37096c5868dc8b0c32d80c60a35a406e42a26e5d026c48fe854ef130736eb48c97b4e60b4a8b8d16553254d980053173bb549848e5b9a9b949296d3a6e706a369a25cf08b72d606e15d8a200d136de25c226c26d861bed841ba51a6d5ed3cd83cdcf04c096bb65604b61cad1548f17395c8c0d1a83c738c385857eb9e2201cf17273a26e60b6bff7fed47befbdf7dedbe1ef4050771d967d2f6edf1ea27bafee3a6b44031b036c4e82921bc5c2c65ff81d30dce87661854e97394b92cc5c595104080b540c981b026c44fc0e7de34be80cb82a5637676a710bcd0db63a6b6e74ebea465b1873cbb6109ccb0436109c9224a94b19178c10b1c26285062028dca81600d8dc0b89b7a154fc4d8ddb9ddbcc36a4aa40e10593386cd27041caaab2224b0e42bfa8a8153cb7a91b94db071ba502c2180f086883f1044a4b09ee8486f1245e0ab07996aa1b999b9120afd3012b848d665991a92adce8381b4bb182c0a85ae1e3898b98292ec2201447d76060a371b3c0f6e44d565018ccb88b1b4de371aadc4cd0369f71fb2c6093a34b0e6bc6095e4458cac6b91523c88008c0e65951281a1f6e5ae8b1b91d2e1dc5d8fbd3b11c134ac6efdd97677ff56746c97477dfd55be89fc7caa6b819acec0f9be266a8b1e96802ef0f182093a8348892088772ee488907a102e4fb3ad4a07bfa3202645be2c3dfd3ff9e5cca03c8de23455dfc415f0582eebdf756a1fb42f75e21210f5503fcf7137f3aac2bc849e106be3590f779def53ccf0b21901ff6de7dcff33c4dcb4851e08bb05004f183e2857560ad6e09bf93d95915f220876cca6e7305c14177fa0657e7ae0a055535b5fab6b5d65db5938992e4689fb5ed210295464132c2ae75cd3ec53d7380896cdf73f73abfc561b267c7772bcc041fe0e4eea9bf3b0924d33fc57dba08b6d37abdee9e776375dd87b733414fdb8d1c8c26ec77fab45a771403da5192be0aeb554778558c1d789eedba1b49a75c7d4408aa1649a7dc759762f05d9a898e52a113bfc701feebe10fbcb6731bbb1fb0fbeb3c13f848288000f80a42dd5b2bc556daa66f39a86fbbfad6fe1d39c0c0a94dd0765167ab876d8d90d6dc11128a29209f72a5800602dff895f05f1a0de2e34f8f28e8d7c61fd3678db1bdfbaee45e7d7af745323ec245329d48c64d4fdfa34d2b50f1e312f0c84e60d882c689d49029a24b66dfcf5dfbcaf0a639a71556745eddad57aad7e45abdce4ed1458b474bf3af846f5b4151a442fe4ec712755bbb37cd3905d921ac84ddf64318f56abb98fd129da61ac3083fda7df5da4e3c2db5feee7e3ac555f8de8bef7592b1ef7735840444f7de7bef65e394549ac9dda63927a4224d5ee68eec66036d9a6bea62652525c459f0bd7111bbdb18d1d2ac46b4f33581e0c136cd35257d6b98cc8462a64c6d6061f7d1cc7407849548792a9767f2968c33e50a8968b6b1e9e9c8abec7f7980506e36f116757f24099784feb5c33026339b32b5d994e9ceb6d6daee2a8d2c30c6de0705638289200882630998a5b94d8aad49ce5b7a422e0da2e76c6c6432992ce7247cf403f270dacdb66915faa1be7fe8fffc6c7080d53724b95bd4180b77389037e5e598aa8850346c9a63ba9a137af6a12128ed6c0532c5e0152bc4546046f1daf8a8ba7bfe99e17484d45e8c493ab96b9b37c6ec3333fd00b3cf876d9afb81dbb14d733fa2ecf3eb9890d6d0ad773926a9ed3df1d8e7c7a4b4698ee9689f22cdfda8b3c34d733fd4e80d34e16802a22f6e6f03514d2949734c5536cd314d6d5ad6a80534c4d711dbdfd4ca676dd744e44098da3d9be68ebe6c334cfe20c2dada878e2622b71465db4d734b4ff6591b93c23e6ba50b51241c275369a93cb251fa5812815461e988d4a2b3628d89646684ac29576613962f282b28216730426e0952ca20610b0769c5078cd28e8f1574ec984bdae4ce94296895362ee4152bd306d58c15c82f2798524f4e3f56d061b6e82143861e48240b5e77de7489c169ab4ce3a58e0b3fa6b46862321d29899032d84c183c4a1d3c6894e1d1c254010fb9240e0e34e6129912c90b28e04c5b951c2c702529a7a0c434c9f1b165470d373b9258c893664ef9430dd9c5455a6195d162beb852f29812a24217134a284048184620f3dc90d2f182850e104aab3a251936e4160c5461ca365b483455a69228e5d212b28e14d982103725984772bcc911a33421070b2b774c14dcc8e92a6f88c1d3858ca185f2494af98589c9040592c910274c268e1d371c33ca30384228997c9941e098486428978cf1fa62829085cc428584e2c4ec81448522a49a5bd9a44799e58692c9c20d176495196cc8295da429d59431858021ab72275879f2e48514ca1d51e597d39c25d28c0df28e0d11cc281b4733e8705953c660864c92651e5d6561e901254a49a9c5480924488e0433090e924a0d1864931a2bca397694b431879c99a24562e1024306adca2419a4ea62428e39224291184d65094b259c1b6794609060945890595e24c0315da0298f94298d6099265c91295479a132733029571ca12a426a6902b36416b951bea001837c81060b928c5719048ed9044d9c32a4142c12cf159a2a9304aa2a264e8ecc50847cd3540e59227203c80c186594192ccc1a5e70b8d094b83224169619e3aacc5345b6a1225d60b2e6c80845b09a4aa525ad1b3a64c0a8228345925719070e149aa73222609946ae4c165562a8b83021a31c295b28d2d4443e592aa5dc0021060c33068b2baf320438a5171ad24e19320c560a57258d2a2254260f26e6ed08a9a44899d46496b074c20d2c3060983460b0286be4cfab84ca1f1c724d9a15cc9457b44c1f5e9ce04a36574c1951c8d569de6bf0a48abd712cd123f6783e6389dbf3b0d10371dfaf751a6a3d4d7618866158efcb1ed332cd7b12f6c23eec447fe1234e1fc71fc31fc30dd986d8a03f36fef9b98745e0df0fff1477a83d16d340df7b5fab8d2ffea8c35a376a5177de532fc686811f088220f86920831dcc02a1f2c7043ef959b3f6f50ab54122dec704551d7bae38f0eb6310e75eb25561f43d7da0124b2b92c5f0c7043e59292f268b11f9916d88e9b36ed96fd067dd9ebb7fb2ef44e1b9ecaba0e739e8814af6097e0acfbf27506a9fe007ec2ef2bafaa0f7b1cf8b55106a834f69088f0edd9fc086c7daa7a2006b8e79cdb2c7dafee76920d9d707df713644752f3444757fcf0326f0097f1ff82708b52b8e7d0c579c86afb8cbc17bf0ebf76007fce3e84195298c93f2be03f81cb4f71504b3766865d65a59ccfb276137d0c6ffa17d3eb41b7e4cd5a321eafb58a28e76cad62a3484ad4287ee5fdf6ad343933dfe936e6dc97a6894881561ef81188b187cf14317f5f93981443ea79d65a76c151aa2031dbab87d1d57f1d6d6e7b4c51f6912a0e4f79fccebf04d800e7d7fbf2d9cd3068980443e26900806a140226e7a1627010b3f58540ab7ba29ffb690ecfb6475c5a3d3502bdcaa20aac5153f61daf86118b3d6da18ad48a4326b63b16a7d422d1f61bfca3cf6a2fbe81f12d56967b8b56b78ba571fb7ba28f6e2fb9f28541855cfbf8669f6e9fd472bb23f82ed5dacd630cb47e090ab8e1ee0702bd4fac2adaa1d57ff9827553f633e1d883ad4d234209109639c55ad0db7be1fc71ffbb1d10331d4eaa4367807fc45f50777d0a727ddd727077d76f8e7fb44a51dc6449146772c1613ad0eb5f08faf747d74a5792fa37d4fc2e2f0ac4c4fe3579932ce18c7c41d6a79d228ee31e6622cf6e1fcb0ac81ec1393689fac7d4ada9fc77e8c899ec414fb1bd363a825ea50cb4d4fcb47d0fd855a9d8f7728f2b47fd31c902c213c766dd35c90ae0dc4ca690ac7163787af238e2159fb824d73439c9ee26ca14d734f563b370469875be4daf7a826895b22d7fd3ec735574c4124896444ae0a0263dd55215b69f9c3b0eaf1b17ff8abff61b1cd5869a7ddf87d3aff11ff583f1f3d7afdd13ffaacdb8a5c5557abc2c82b3e3fd8629b7d7a9fa2168dd5fef6fe7b71cb47d4ffc42e3c7ef5bef7f429726def53d4ed58636c71d5e0b52ab15ffdb91dc4fcb1fddeabb5be7f4d12b7fc47914be4dadf679a6c2cc1e15dc7387c3d5d5707afb30fc54cac5f84bf3aebc21d74d785f58962c6a1ebaafafc461c3bb4d6dd833bd6cf1afedba07feebbb8e58f69defbd0c427616bf81a68e76cf7d0724daa429fecfbb3c648da559f286418e3d9768f4c0c2ff6f5c5afb31dd345f8bf1f451f47519f28661beb8a612f7b1f4db84c03813fbef7301c756671f773a65f7fd6187bd41fe064142866019055a8146b9a705ae7100d0d004000f316000028100a85c4226192c240a0caea0114800970aa3a5a4224a24a22811043411083200c80300882000001800000c2300c4619d1fbdff62ae26eba29ee702381748989b6e8528981b1b920e4fdc20cfcc94279561952ed9a8b581f2526ddd88277cba8518421c1260eb455e3c559a51523db249f352906f7c5b422b74e87c930919632c0162367ef7e7145a0f7ae1a3cc91840dc9489239750a51130de45d9250d23e5da6b781ef1668649095d7b377e1b215e46ed86ec550aaf72a94b4dee326d83aea7fbfa14676077cf42e99281b261a08224df6aa74235512d979242f504b158675df0547f09315a3843ab988fce8cbf5db3de06bfa1628541675ee73e68e0edd81f63a24950d11cc4799479dc4dc79cd30c4b18569849871e2665a0a6d09cbc8c8b13fa19695f912ca19a21dce5b38ad6d7f5abe5ad84c8f95cb5c7b32fdaee1ba2ef7c47cd0b0dde3a750d62c254c58cedfe61a2f7d96b5a4278617a0a6037e1a96b3b863bc2a18cf27f30628d708dde6b1346a0d4150ab1dc930aeb109bcd084342d26533c2800f6485014745255291008f2d9a049749b4d44af1fe00fee2ca940ff4e9d7516f981fda1feb767f14f8de20e1bbc89b4df3dab6110c3d8babc157e97cc083e6598914fbfec8645f677f08a7e44d28debc96e7bc3235c61f5c97776c749ea840cbc98519aef79d46d7196f1cf6cd4e5b894b96a997c63b20066b9a06506bdda7b8d5661eda011295184c2f7071dfc62b218a63308a65f58cb57bc8a1ca70e187e0dabed83f46b5721db96fe62270cc8a3d2d4dc11aa5788a59c39d5cfb5840f66126b4e85d9c5ee739ed2f05a10810243605e5771f577733d978b56cd60e9682f9369a4301513539077443307dff7bea88ff48a27ab8a3ab59aadcda09380a333b107e40051fe0f789ffd13423d210e11a4a4b7423507f2321600f1feec02be989db3bd2ff1f048036f785ae61f1f98d0c5f3c2349c1ad3710a2620cc4415922f7e2f16d8b216b297b1ff8ce424ee312f8ac276604888e2329dad0b74ed5c29342871e06ec4aac9108660dfb04468acf7c89fbc3e793f407a75581b130ca4d08c8f5d996edf352ece51c08c0f80e02c2927b31ed1ed9030908c8de44e69baed2a202a888d5ab7fb3567f9ac28fd244388c049e1d4e2accabef86d9aa5f6195663850debde5867ce4771d0db47a84c2ce22831944cafd633fc9ce843ecc3579905c2f46af28176f9bd540a68b47ae960a0f1a101058b7088fc9bf44700a89ef75f6b44e7f2a027d894c346e1b6d27189bca1732ae83ce46dffc6af3771cb44238982f308184cb6e0701dc7cc49a66b3d339a38418725f42ecbae343023b5dcd73488655d11b0afb32127bf157f5629eebc31cc71647a05bd39b17346a5d789ca9d46c096202d43d47af9b6ee2d3d105cda34704019504d10561a8d264e8ce3082d01825f8c26dbc08d9fbb1eb161609e2cdb4cd47cb36756b2fdd86a8b967cc2b30b674f1e44ae65cd7e9cd1b291837c1cd77ac4b65ab0a136870a1096e3c784f7a67524282c44c3415de80021a14ea7044ed3333e8bbc44b594679d42e406864996b389ae01e8b0405e50812db9747b7ae3c3638a164923bcfda630188688df39bf0d4f50d5cf3050fb0f37adbb5be3d39f4f7d293b3fc645c19f81217bb7e1f18855213eb523af511e1ee8a9121983342d122df4233fe229fd12e410c8e4faa3924e41c29a453612dc1bf0d7f4244c6a3aef5e3d1f7da4c477cbca1e266930cb6e9350b9f8d1262236265cb49669281da9ab2457b30b0c6ecbeddcb9e3187d28a6ecca6133b155b356f0960e05e11d11c1a2f9aba1052814515b45acfc01032635dc14b76d93cbac96408f1d6ba722b67650af6403564ae3dd77799449f5dabe840f0b6b5e6ee8deca026a306ae4cd91f04b406a28a9bfa8607c148d5cccfe63a23bf17c7c939ea9a24950e58b29e34176de9f7a25850b30e42421d94ef5d6e7bbbd16f403d6f779f2a243276fee9887d485bebe850516f2c6993f901a2250ca9ea9a5eef88334652ccc86df76e93090949589d715449e0dbec9d23121776964c4220ad43788826125666eba6405d0c21d7294581d54f1182fe04cd24cb65a5d376aca18b2e910b5cdba857554d5d5a5b604b56d6ca8cd452b44466cf1f70983abad5d907160a66723106c7125ea55c674b8567ddd20c3fc24153b7463c5338d26493c168526006f03525e37363a3420dca529cf3c1843c8b3f5736a96f3155e145530382464110713186dcb3214e65d74b897c10f13a8683a7a12b5f2926cb8e9f1c8b987b89a4c7599634ac87ce3e4f6de26b24741771b0db7126ea55bc2d3d2eba3c76329e2aba37630c44843a857a65a43ba02cf392ade3c23ad1a58e5d6084747ca98bc5f491236d383a5c037b299fa36f026404241c7aa4b4f0aad32dd9878c49a8cee330090f1fe5ffcbe646cef5d8f462f65b244792ac7c3ab1c19116093e5ea4e950335776ab1c49962e1bd99c0c8044531bcda81d19c551a34c4dd12dc5c93a19aee6da8fb43d8110d3f6c25d4500c96427cb126b50248de696eaa25c58fe58e1686f1b0e8e309e373ba9ad5462a57cdfee5837406ed592779754184c8ebb1a08cdce8bf2f2151eaf45b3195bdae9c07da04c50cd70f2fc8e85003167a82e190bb987d1741e5d81b069876adf895e2e8de6c2c555acf52aaeacff569567da41e4228e3865533d349a59feb027f6876701660fbd06ff5c5d98a42f01a7c362425756147612b2394fd2652eba445416283d628ee3b5a4551136f195476d700502edd824079af50c4034b01c73626c64dcc57a820cd33f0067fc3c575a6f6805dd58ca48149f5ccbf2c850e8e90cb04b4efa2a624ee7e98079b3c968664f21b284d30d12ee0e013e331abf305aa7b8ef480dfaf209a68c5413da982f85fa3daea17024e9d30a16a5ecd00597442d66453b9cb31de220e9ce5d0bcb80f67c0e8537c62d69042eb61ed8993bd9219578e4f572db5ee309d0c33bda6487c0cc18321cc9762ed8f30c6a08a239c6ccb55aa4fd0a9f0ea25933eb32d096e01655450d11a80822bdc3b0fadec7978f3f688a7265b2bce8a31638a2a1b696476adf504d5b0926cd943dc7f22f15444f03819c02c68cde10911566c1a7eb068c560db4e731e1303b81b060203f15dc93804e0ce110d98c158f0586aa243a8d5c07a02081de0ad3fcb7b68e793cabb71eecf8ce181a0b4516902f41b4a5278d3d1f0b652e6ebb99b944c8e66b3716553c14c2cd9ad30838bacb80d88e4007de2984bc4a8e147ac854186787c2b775b22731b9426357d6a15563c2e3fd6f148f873810e54122897d83619c27807839c3e3737b8062d32835f8b3126c1fb1f474ea08891f198d74ca86eb8cfdf0e32675e6d50e334810cd90c45334ed3f2b95a89f79102b0fb43935a5b8c148b276a4a03dd17687909d49f8198047074e2cfed12ecaff662e91562c6937b9c89308b605d75cb7c355b1be2ba1fa893ae1156f0d23f78069e2dcde014745802b8d460c949a034f0366c57fcd25b7bd30a0a6e77363769cc68bc126bdf56fbaab6740be2d4d9f4fb32ff2d4392a52cc9bc7f2620cb8e8677250d453e98643db7c4ea0eadd13ad0f4889948d01a76f0293e7268c36ac6e127b30abecbfb99a1889858e0bef4f1bc4d22dd1b3e5fa6f85cf44e530682b642f0448582edfe70ed51199fdd348445b43448541a58ac1ecf650206192116686cfdf798ec42fc1c948e75b0ea23756baf5e6d271fa04c071a5a53bac3ed848981c27f95123bcff1f2bd70888805a30efd16d3cf8a85151d87bf2775fac6921f33f1eec9bcada6e57e1367bfcbd3d1af2df6571008440315ea834742f5b591f6e97a9b519e668a45dc1a2504b5492b08a223bb24eb6d914d09f5d31246695e8298341e53a04412e7049ec24d1ad411d67701a14e2b80de8a918b3ee9860b74cea09cac2deffad266ca87c68c134312e6097444d454da20f13464f2cba1c504d15433e14b822665a5d7dfcecf79693c40c8313637283f07ef83b4fe013c9a0ed039a883ed84e66ba6bcaa14d7d77e77e78b6a5fb15908e2553722ad17a8fbe3a31c5500f00133e381b5b8de3ecb9a09148751fb57d5323c2270418b42a0e6d0b2eb8d208b272030dfd29bf32b9b2bf31a23041a4005d80c2e28649bbd221ef390951190a1972ae58c827df44829640025b056f1f6ef19f3b16d34b976fb6e55fd9c8f89f284616d00fdd00218eaf619affc54db2038df7dd851789a057d1065697e18b6116f4e420a09e01ec6c98ae191a4c275cde6b6af56941bf3dbf2a0294db8a06fb66532cf0e41eb21d31846d365ff9d92a7fc43a4b859b43a9a0d1d116166f20eb8d83b9d1e8710f40576f658e661d65af0ae9f35ff2c72f7ca901c318af9ae133e445fbfc8097eafe7e14f6078414f82bdc903aa6c28afb06c395abb50c92e466929c46d426d63e848b8ea5b48d7d15a037519d6d74cc9b9984b89006a8156307001433fa0bafa03756bc6a560469b38f0cbd0b3ec385c7cfb85e1ed0dd91d9c266d2886cb80ac04b7b64d9e1964ad40b818e3ec891692bcc852a8c0492a6416a796693b815ac4388efc39d12217109c856f0208de63c8cdd963cc27571130891a17f6134f0dd6365bd8b64346239dcbdbb96787913f2770a8f35e5a1bca175a502bb48e5247085ff7bf597351f97d9752cd423dee3451a50e5ffe2a9b834fedebc03e12bf9874a9d4a8ac5aa142b94d6e4e721ffbb3020758fc89c31acd6e4e291bfbf180c9b776a297f63dbb2f603e9f0771cfe467d7ad9fdf90489b8d085f60468f79baefc8d4f6560e93916f3274591258c0569a84c24aad2ece174cb736f0ce0778921e8c12cc48d2637e7880461e21723d90680896b014647f5b509b6e5db84de4185a1ab2331ec1452b8b1d3519f3f7c3cd669fc8c0f713c7bb47b685afd8aace4f1573fcdc426f0605ddfb79daf603274ebc0900c6004fa8d7904079efbaa5672ad3f85cd28df862f6372edb1a8f300e3fcf9be74e70934a952ef93537d867c568cefb699fab94380241e7b637fcb539c378c725d6177e55b357636e84587f6b629bb9e38afdd2520d679625b8173b1e3118dee08b0f9b02eae6f9ca96df2901db47cc835ea0469585f51984bbf6ab3cb625a2b7d46fc42accb63c5cf33f3a064d163b63610eb9ac582f403f05cbc832981f95eb7d52a4d6eeeaaaad8c9827a2999b58ff1a3c6a10aaac370cd0582f21ab3dbd03655d74a32f66ff4f92b3185f4778e5953c17bb26648170e87cc75bfe68b190f6ca2899e19adec5becbaabf1e96a074072f986ce9bc4c7f4d7edbdbf85abcb055f58c2a1a02c31e1938ca815d5c94897971aeeeb4bf1dc562cfee18c5d33b2b5e1111270ba37da4392cb7c5339c22bef5f598d947a7777319827120e65e37d7c1be4406a6e95ce6a0d66f65d989292c311b40cb8241bb9bd65278a878ea1d91f54e859b497a79ac812ea1861867d93c69879ecd4146d0013d3b30c610f78817301e14f28d8f86150d3a44987e9ac2bd120ff3544612137c4e4f0b78723dc930610c35d848bd804ecec936fa29ec2feb019023c055e286ea9ac139c2c21f327ca86e23d18e5ed93b0b3a7fc9c62a98d300619ccc020867d08224a7fb2b202700db2a4e323251a6c455452ce55e5c1ec1e88c1310e3b785273aa8b814641e46a2b8c179bcf2084229b8c0490cba7b5def40c8121e1d9c928ce6eb5d070031cdf85d77256b0bcf99010fa5033917f6592891d106c077811d248bb59cd31f334323dfc00bdcc6112442e79ca0a82583f47a6b34dc0863e0b3e2434134d822fc53d1af0a1e71350f57e300b253d6f799dfee9e100e69ae822e029e8a5624bc959ce0f1107c20ea29a264d5a4c615da92602ce1cadc59f0487022e8f9180cd76883974322c38c0b55370e0829205dbe0e14eb4c3498c33afeb366e0bd8916777515e5b4035d2f421091e36548a0ef0510b82b7c2f1a8fd050395d409d571face8eacf52e10c64e3fc571ea66aa7a2002bce16b109e69408cfa686280308daf9cc3a99bc07e405dab5b937bee7438ad72975e822d81104b247ae38c1cfc8ff69edfeb51924c73b4812d3ba775110b4749d6c654497b1f489e64acf7c811c28d45177726f87900f5bedc33093cc48d5dfde98c857b70255b111adc8ac7e04adee0c481ae88e6e81fa2439d9c0474c0a5e20ad9497e7142f985003858ec6cccd644d77b871555d8e993ee8613c24ed32cee3d6fa54ff5495f30e3e24a26f444ae7120e1235cedfdaf8191ff568f8fe7b8d67e89dd39d7a3ba82a1c3a0619dc2f886d0f8bf920843fe9bd905b09acf91424c687ab16c5341e0b562e1f5158105260bfe4aa4a10e09b8f555ebce7fdf3afa11e8c4f4a6ce709effc6c75fd00121327cd8fb6ecb6f485ae46fdbd16fd67e67f865f237b07e73f88ed1001f6a8fd80672134d3baf34b40317e7093062c3654360339ab117b0658e3a0ffe4acd816330fd818b7b35b162695a8d9c166f1ec435e291a4cce24173b4742a35980c5dd66fc81765daa655b6e851154547526f0b97a65de898ba3f78b98fdec317c50a2e461082118d93bcb5bc327ad2f19ba3eaa891a7bc100a7b8b340bdcbc34ff8beec2ac7dfdf483a5c58435c8f8149ed02c5d428f6cea6756b5105c9250f05784e9d108fee4c7694a4fa5fadc25949969b68394b9faccbf8445451cb9206e3a4b3f2ca412754684e7e720dee50ae53f9efe4fb1073c25fde01fb48882601716e146187b15543c22b60faff4a049beba848afbd5be11427db143772c4385b604e3056dbffa94366846af32a61832784a6c21821449307508a7ee378e3d5889d45aa8fce97a8d8e46a1b05b9a3f914538dc0c0e59ac41c74dfec298d45ad7c429b344be6e1bbd510438e0708ae4304654c879001dfb9bcfe74cd45c928a802cc0cc464f06009805750be47c5de5b05ff5249d5c2f19797bb5cb63856f94dd2ac78a1fd784fed274944f065b5ea2a3f0fa186632745297f9cf663de9c9f1ef18c38a873300521c0ce4d97185bfc96b90117650004f6ce2c671edab8a04123fd2bd31403bc6843c890c1c5b6719f98712e8947e720f42f8d0ffe183ac065182c007792d8a14c1a6247fa9e6dc56a9e6ec03aa8aa7d43e88207754b449f4c18348f843092a254572e8875b5d8ae8f3a42055c359dbc85432a489466a3d3310897e487296b2c13c11996a24a30771ab904aaf34614ff5cefd4344d1904041bc40ba7acce8470264dd0f6ad5a4bcb2b27122334d826a402770a962d7528ca46c20270aa90f19ec2e042115fb38e05691a96dcfa90432c594bb03a18d90f2af34694fe59dfb07f143ef870fb2e243a9a2954000211237659c42f911093b25ab3c7d787c50409440d8ff90849a1a53ee0f886df9d4c8b3486429029626fea9e6dc40087ddc01f1a9f4a70a22f30fb7881439c9f3204b3429fb4c0a4824da521989e60f13dfd47e2a7e90f391e8035e90935aeaa364fd6013903e036232969c1924f42de9f4316b8a2c5f0e6755b09d689037c85f414e34a9b24fb755a9c9a4f08712dd94d7a76d8494e10cf48792444a793907392dc812e7947eee02529b8c9437a4c9265535731f903fcc7d48f7916d68cafaf3c4666aeaa924977220b70a12493b954186e9a3ed7f6a2f690151223be54d99649b5a490c08453f949c523d9c8e8c94328634d9a656e71c04027206e1838040a641183e4420383e9c3fd681e8407c108a1f0a207f3e282050822ce94bed4ce58f9c55952d0c794d33a50693a906df315e1b4b0e627ac922704f123d0941b268046c4ba29993ac55d75a2d5d91cdd7f9eec326345158061cfd09c83c785537f111a037ab7318e616c148c687e17c130ce84ae8ee788600028922488342212cb02b3540935b478697d334a0b7519445a434d1494d27952ca464942790f3a7f8ef58e9720184ff23fc8102d1064800c4524ca945e27c274315fe2f2b3d893ea603224948ca7ad2c4766a35733f207d98fbe0f9080249811c03e4442cd558d20e425b21e553b63ee0fc61d2913a4cad802c69a7b24b3a03b12d7fc92d4725482e020140276546695291aaf57af27dc4f547259a945d56350bb1bd4a35d9c9762d35f574db4ded96b47e2891d9b46dc49291ac206f1f743ff47f78419892dc36aa86392d51a43c2b4d4e5269b3f001bb754ea4afd38bc5929458f5b20361e5e35a10483ea57c37dd2a539899e669d999a957b2c9ced44e86c5e8ace7b10a0e240d9a09f15fb2b4264d3aa9dd5482c84022411e907a1ffaaa7a12a18f1ee4401f92a4a4244a0e819cec53559ffc2f4c0d67e403c187eb630dc4ba0f25ff947d72013203417ebc204c93f00fb862cd958f9c7fccdd8907fa88d5f4c0fe6bf92c8c52498545e6f943c92f55bae97629555869724ed5e7421f7c90f01f2810f663c64296e452c9ca591fd08290e491ba33b77c28399b6a28a39968bbd5974a952e1091c4ce183aba60e55d7fbae1ca9b915a18a9442985513681f87c544194407e2abab9d5879233a9e6720751e2d589fe9451ec4f1422eb518553ec28e5e96044a37cfa0c6b6ebb4adb2132f840bc9770efb35b05d60e749409b116595551707811a6a5b3ff6ccd00ec0dabd46de5e7332bbae7e4a1359aa6582c9e1953cc6b5e53209bc30cdeb0ce12b5077a9c086ffe402f144effff06feb2c734a1fffc66995375eb2f93bb59412b03f3eb552d8b9db4a3745a4fa1b18e738528d632dc241d31f687c79b92fd63769928addf4328454130d3bdd4943316c4d5ef6d30b6dca0a160d0e29539fe28e2e10bbb38348af741eb782af73ef057afc4c1501a1a0cf2e24fa6e6d8dc83e74d45fb747c78cf8f19bcb6b3bb796a8fb163967a6f9dc22ba4fa66d2c722f6b3b2b649528a824f26b1926c953e24959d92cd6db11c761748b3cb8cacc5c2f0745f060874e9f36489a2480c69cd324c5e0d207d8c1bf3b16caa36dcc31eece5adcb9317b479437c329017315d5b68b516950373e6136303c5ec88eebf3046fed5addb6e0d20dd941c77816fbcde72ef7749e098330a075da48373d8cebbd399f63592f55961b231c28a8e5906664347c413b8f50ce1256b02bbad5107116a3cefe7d0496351b7464c0a81dac64d9340fe1ecf960b09a89d0eb9cdb119f41c3bde1afc191ddb482598309ddbc607b2b1493dc0e167d064a4cd487e05d9f368eeff592199a938c1b2172f4c47ae2ba6b9e0c15721ae893318253518d7e453407e8b5b711bfb92d5546410b41cf3ac36b935ed1df55204925feba0a182baa0c66611e53cf08aed907be83c228aba8d3985c746210c339ab26585c10e2aac8045c4ecb349b4160dfb984eb37276c36bc845400c2a2be5a2760f133d106f49d5c093c22a4850e7589b7e1fc74e2a0e71ec330faf108d294aa0e0b488775f2b62678b8bdb80b13ede9ac4a211d5c77cc9a3767f924dbb9ae3b01417480b72ce41e54dfbf844ad7d1c2f32e7c51db70b7482b2862cae3521434f13805dd2c38e3d37ab05948d736faac815d24de8ad37b376eea788a1ea610413737f8ce012ae62c871ce58dfd4cd1d029f8c2199a1c0f2f6017b8a284d5c006ff92b452325750727a7fd2de402226f0f88a67e7802e784fc68875c22862677c5d0a720f6d6e21d7056abf27f184a03fac2b7e1f107c8b01ff4ba66523b7000705aa6c20112d75789445775a7709392b3571d22d2d761191db00201b5205fab57f65aa0cf68475e968eacbc58b431e999628f327244b6f2e6fe7a7947d1e8645975c11f8ea562419ad2403c5eb015cc3c13657b44f4a56184bb93265780d28ff787b2971affdfec1bb36677317bb9cbfe4cc5e345aa0a59657b914cffbc07c16686174995a9f64abe27197544a94b7a874a45249da1c5e59f4314b349362146c09bff1a2fc48a6921e1d0a369b4138476fc7d2dd3f11be4e56bc4bd591acb7e4e2bf3066ea3801e0f40bef97211dc5958cbcd3ec150d639cbd30dc0b4c8891668ee8cf4da232eaa65409e41fb8467d43a0d20483576ef198fae60489b12f4a9ded2b9120429470f18bac2d300234403f45bb409d26b11bc394140f97e35fc044dfcc871e0dcf939d248af949b2576966f0b02d6804b02f3bcbe706a84a775d6426d9711de303d8016011325b026cf9d6ce7c0122c42b256eb55ce800a0115e4ca1d33158d7fcb3544fba901c99bcb5a2279dbc902a7b8e5bf62012c69c0073f8540088f8d29e8ceb16f83ced84b744f1124f62eb3ee09022d5d1d3a7e84403633acad0f49b16555b8681983d67d58a07fc081fe23f3eeea945b72cc99f26ecd747ed123d3837058be6c2690321458a1250a0f5f73f9a34b4926a459fc400ea4f7548d76fbbb7d70fe16f2deeb3e6abb35d408c73900313cc60e303cb442d48c4d7b5ba50658766d794db0cb16b08488a4361015f16b09131f0088973ea1a4169b4ce9946553e54056db4ac2794b482240fcb466d7bb58bcb391d8089c2737f5400ac98ce05a12ad6be593d6798b2ed6f4cd6a87a2d1930405f83358f98bf5ac6cd5775f545ec02b7d312fb3cae37c82f56c69f1885b47c619a97abfe660dce49801be970159831f12c143e72c85c46653a667a3f6a085574ac66fbd9d3265e7ff6123099cd43faab91ba80ca5b78652254fef78f01d3c322f5d350efbcb4e39b43b65809b66480baa7a2b34c46a9bc0bc8f481d2018914023a95adec65af4d6091e92d96d276d9a02fe0711a7691a032c5f5c434b529305a3ac7b3f98bd3b330a6cedfe3beefc6fa18281c1fbcd314df905bd8023eac39e001c6314c499a89729ed2f149bc2746f10ad4f14dee12f92c331886cec8aed2b50a2818a288e49a5e1743571ec19574358e227d67fefac9157ce8b26a59ddb341ace6645e7268235b9fe0528fcfb65d9f2c15073069037051e65023beaf3de991f7ca012219ba83f9e37d6173cefffb1752d490a1f144cb4d3af1565d6abd4f6ee2d70f85fcb26bb4da4e1c677238b8ab1cfeb6f2ea0c0ca3502c4680dc5b7048d87c6da1f4831c08cfa48c298091a164858ceeec15f16fe44c230a8faf7370225086dc92c365c8a80d123fc13399053c1a642b7e17305179dcda8e3e51105094fc2ba18629fea54518d33052d097868c245890dcc35084a58d64d584d1daca380ea53a08b26417091dcb069f856081a301bc7c308c5f3d3d954383612d213328e370fd99c2461add455057f407c101bcc4285cbb48d44622ed80e6950f2b88362068f4686df927839d53daa8f7b1325aa4c5e19569f130cbdc372eaa6648a1528b78124d292c85e17771cc2c8c81dacfabb86ae5e69f81583fb482a23f7b7d205ccaa243fc0699df099bc17d4cd68ad86e339276f060c8b41be1884804d349561ac5f76dd20d2a9bf1de6bb987df3dbf27602969f00aedc1b859bbf73bafef0e8a71f30239775290cdc50c7f8eeb44bb1d0ea95256b6bc2253a89005dd3f18c30dc5f0c0653d1b5f338adf83d3474392c390e6dbf386f4eb90d5335bbb208418393adf9c6a653d1d62a4041526a0e0398b304ab9b3a33e28eb77130582348fa7a902dabd9d82a05fbb95a6161f552b9f8ae08faf159316344691818729cde7aa5c48d2177d28a0ec756c12e33f77bd2a4ad09f10230a4957b4049292b1fa07425af440b5158eea239d83aab14e26774bf5390f093607f46a75720ccbbb3d2f740bd1f34b3550a2c346956b6aa4e27eeab0b1c8f0b9d1a0b9322c4a0880f5bf606465af65aeb435a35d9a3c4563c11bf3fd684e773e7cbf8ede342f1430449ab2ad1700be0cb43f58199351a7d67174fc72338c0149873ac4c4a3aa979c48e64914c729cc1455ef2ea64313dbb1edd4b7f22c15c74a75222b0389ae68e439c8c03eb0fe87bbc5774f642c5bc22a9a5f34e0ad702cc05f8c93ebb6cb263b3dc7ed4ac750d6ee31257a409c082951283a21611e13a906e44c1d6945bf48a925d1862d5486192e5f848174beb60cb3002a531b2c70cc4b43494614ed307b726f8322876c0b47089a0c4e69a8084d0c6a405e69bbec501a663744ef70d79c47ef98ac0354fd5c3aa26fc7645c7a1724936b3c2465c8430a77fa60c39ea3c006e7cc7e8f529c05db57be86597f66281bf9894c34369d3d8a3d31e75dc98937868f10aaecc3fb1200545873fc4756663ea6d91079963f7bcfb2112cde4ac13130e88b192cb7c24cd52de49549a11cb8f61a46f6cd463394e60250bbf12a92de13277a944c8a7d9d30cb41a86ad6e2f3493fcce771ca4035ccea9c51e8db9cb578f1246cc7d0106c1465d01ab3187e2ae2b901d0908c4f5a9220ba709e3039a9f8f2167b8b512bd429f0b217ec536cea26317949ec1f4df6b4d28227900b45f4d308637671aaa206280ebdd66a9e760761dc185df31022526ec3c31df6c3e564379d65ae824343c50404722d76f73cb0f544ca85aee12545ab4471f074df58bebab687f2b103ae1d602ee54150d7616aa6e3730c634da4ab771f72e9026d6c9324095d906697b195f5d7c9efe0f34f86ffa7a13ad108d5c9504d6b42799a4793c30c5ef2ad6fa5fc80580dce4c775f6521552b01954932b3833703b0f396a758c77ef90040a62abadc751d4bddd90a7e3d8bd49072cc1650436f9881fce405d4a314e83bc38191981a2cca1f91c627341b6b19025d831d9ed71c36995c9dd0548b0a34ee4fb33060623afc08ea30a00349748222849002e8f1f22514f4d13021f106bf7d4a8e0e404f508100d8dd17867e667f4d32ba5fffd719b8146acc83898600468fddd052fc84f8a71a7ea51486ff99bdd96075b86508ed53ba741249d19bc0a5870f1ef09191fc5620f1c3b64a7e6ce13162561000f105b5e307288cb8d4db34fd47e07bb331ef260da8af14d51a895122891af145649cf1dda55b1b73bdcdc6e990015a6b2415013be9132a02c159a8230c4ccdf5ac31b8d420f15817c17b4f28984a72cab308c03db405348c59dbe924923186d9e4ed4c9470577d879607cf8770a7d2f161444a00acf0f188672ef670b01c4129d70897c0ebdf74eb54fd735c2d13157eb9e8d35e52141613fcbce95df44519f11f8c6701967185156072f72ddfb1a05d9ae1eb34ae926ce0db58afd88eac2522907ae4231bb8f00cd309a013c64f423fcf189d9b323f1169372d3edf97e59fe5f621de5452cc183f5c633b605db9227482634f1558258c2ba149634ee756060c729c172c8cf1d11a01c30a62961b976bc61050874a010bf887322f8e41275da43c6ce7aac0aef27e447106e65426e37de5ee8bf4bfd77ae4fc68f54ecd14b6680f8056611e29d8060ebce0bff989069c8128ab3af50c2ce522941da09b80834317c937a6e3e43517cf525ea4cdf975b4902f3390f244d04fa0aab975a30145ea503898cd4157575661ff874046611eebba07dc17a584c619312e8f24f8fedb2c74ba844ff50c24c36770f919500000603c0c72a01da49b62670632ced47531668811208c8bb06e028025a9384ec2dc996524a29934c01f7087d095609b32a3d76c43e1f7b6fa0b7c0ef18df77520e568d5ed5a4687adc77135de68c87e79c73b24738233f671e86571520ac180185e5439212248a84746d893280f2cffb493b41aac11ab5cd7bee64502426b10089d79e5b4d92f017eead8b6057a55e411c86fac8a88b88ece670f8e7d77bee7098d775c518bbbb9b5ef83007972cb1bb359a60b71adf7ee3637856b6d50d676a37395a0d343fafed529b89c106ebbc6768dbb88e46ec42ca0bbb4187f0badb7555a74aba56f7d565d12fac731cba9d7194d22ccbb2ab3a3950e6d24f994bcfaec45ee8eb49cbead057ed8aaee351ade116d3b986d29bfac2ba3333bc1ccec7fd5eadd57df8eb55610e541d3a86cd4bdb84fec234ece97453a012f50755e2e4a10658a7af2614e972f2d7158aa445e55417f5913e825dd4477d94a532b6e569d25b83fd6a2c7afdb27608e723228d7278f188d743cea8570da1430db61067e46342edb9e377ea7e39852af9c14a513e6fed39ec7739a957ba2a1901bd9bd8e9fc5ee34de7f4193a27bd349ecb71cd9c0114242d28465efa55c417d267c47898d2ecbaac858e41cd426a7bf52e7a39d62b79812ea0c9e392d74ef7d9f1532a00e9a6294925c5aa747d37a0ab85831e7060f44de70a8ee353d67939d45fe700e91f630cd66127f4542a59c943ae4b21743880b87f3e97cfabe2e168bf29e331fb4d990f4e9a1e1fbe734f2f642cf6c1312a115e5cb78a30f2dd2d22bc7c67007befbdf7f85df8584796d66021b1acb7797c37faee7dd2eba7843a81dae79066b1c03233d7105d2ae566caa584bce0871f6a2a8ddc184b89dd688adf41c8d687fcc0e2b7eb2d60b36b1181e4b9458491d60dac706911116542383d4ef9450cd792c75b6362ba44b09d2164478f1f2f03c1431fa6e3d15a6bbdcfaaea5cb11add550d23e20822947c7d18cb539df3d06fbe8d5802074ede653c2eeb61f9e0532fa535ba4ca9a49b1c624fbdcafb139f5e7702eb2ed62bca9c34c4932f1e014f95fc16b71facc45f855c4e64cb6e37433adcae260f2912bcb0594da336c37f5d95b26eb0d406a6e35d5abb09b6bbcc3d75da194fbdb45716c3c13e9f52a39a499b81ac1bece65eb3faa2cb0ed7420460d6027f741adda5e33ec5d77d9a3668f21b7d6803a487b0061896bacf6cd7a7e77e32d52836c3fa525fbfe76f1b72daf69edba2bbe74585cddecfde10f59e1d52d5e517f5e8f446e9432ff4d7d4a04f6de8b187b757cf0897932b08aa440c7a5dd7e590fdf9d0476dc35ee8e7065d470ffdd47e60da1b92cef5511fa98cb5465dc3e6acc7fdc9abb6a5be6eeea4f9f09f30e74c02109e1c566f8fa7abdac17f721ffed355991ce8e4d24d27977eba43afa35dd6534d589bb4a1bfb41fa9a76d87aaeb2f4a481c6c78b12d4a494a6df42e9fab4b9bf16e36a58429615aac5ca9d2ab37630cdb8e69a149615ac6b0ed1b7d8a592509840a1dbc2ca42af56a56a55ecd76adad9004e14bfae76f6a7783f07aad52db1c7ccffbb92633a2d0c46957c7db861e12d98662142cd7bce8e40ed529a4935e4b2f469d60a25b22bdb05761d8ed11d147bd349173469d63ba62920893c024b007eaa88ee76a1775e1b2c4a5cbd297265c969cd0843ac1b644242a8c5fa270f11205d342ab605cde39299d036b547a47afa84b1375d1237a444dd44498962e7a44f611d147f4cf44bdd6fb4cd41d9777535c302d483f54ae714547ef6e0d2faceb7a67ac05c3e4b04bdd0c28d0e774734bfc4ee39ff1040b243dfee6964cbed439d14593912e3269f479795ede176a7211be745b9c92e3e2943aa73ae4d239b3dd0c3090943ce78fd64ab11962d8e94a339e601d0fec37f79b9352b7bcf31c576175da6a52e4943ae76d4ea9971e5113a2cff9c7cf2d265bb87ba9627133dc853e622c724acfe6f512bddc64baee06af7b69cb1bb58ac5cd80174222ec686808d72bb5cbac5be2d010ae576a175b6d557245d615b511fbd0c238778e9d3198751818eb10ab582a968aa562a921e95d0a9e5b4e9c40e78d63bd31ee9cad6e9dad6e9dad6e9dad6e6334a04d3a75225fb5cdda59312ed45e26192f9fa7ead5c6c4d8ea313176c396ac85f16aad75547541ef635c47db18944555192fb7d0ce18eb28945d81c2d81973b7d45bb74ea1751868aba3a0c9a79f34db2bacbaeca7bee96ed22f553b8d4828477f387aefbd57b10c0de17a25ab57aa922bb2ae28dbe0cbd3093b393f1bde1856b1542c15cb51d2105ffe00cfad21a67c57dfa6c31951d15174831f7b384c8b5f99b6d91f1a82ca50a80c5bca1cd3804cce59965d8e658e05bd47399107726e42619e619751f4ccbf3354b6c2cc4e1996a132cf9c76e6a7ec5ecf2ba6c5cdb82ebddb94373ea4a9e5cde7f3ca9c13daa1215cafcc39e774589526ca51d651be65339bd89c73ca786731307ef218cd5e94cc29c326160c52283cf9de7bb41f103f0f3d85f0baaeeb3964d375410883de76f92585a8f20a786e0981e4a487cb8991105baeabc617f6429d321fa036a15e8682586318867577a350a84679ff48bda933acdebb0d615a3078fde0c94f6ccbd5187d9709dbb2657e22b2afa73ba2e997a3ee66ef96ea9ed6678c5f77b397c7d8bf629cba8cc66d735d1ea3c168b2a7464f1976b7367a5a0d2f6cbd3f523f7dfb917aa6ab8d1ca605d382697972eaa94443de9edb7e8c3aa714887f832fe49b7b1e04d22fa08b10474170f9fa321a849537c0732b882719cf66837627395d0d09c3eee68a1e528c37a56eb1b7308e5dcc63346e1b98bb9deeac7eba9b2bba346c1b02aad7e445d815fd5d20e7d365c5022192ed5902007427f5915236638c40588d5176ec3e2263ac11f31afdc8f6c9e78c7da48f3a27cef8a4b776720680d78acd69341bd3802ca3e6c4bc8d1c8e76ac91c0789c306e8f381cd323aa1bb3307df4115a6b61dcc278b4f1aa6cdf6dc7b3c18a9189de4611c3fa6866d353f019e735f3d99c69472ae6ce5a3be92b8c8581b12e3acd7cda4f2e3587d2eaa98f6018969218bf53c13de5a9674385cb7a36d037c7cf46eaf2b381329c4db9cbb33ee2666457e5d7d426b6633a8c7da48f1ca9b58ffa88924ccf1401131d12ce089a9aceeeeea90165b57b4ed995a3518dcee8a50b75edda0d5fcef639dbc18f77fb8135ea94f9a03ce36e6e7759d450ce305e6bcdb22c4b126d8cb5baa39ad90b546366a3118c7337fb8161edf1ea083818de77a73c8bf18cd3b097e855920f76734fbe8f784283599c037abb067012cc4cfc14d54f01fe39d073033cdf9c096a7e23c1b3b4703f06f8779940da6d80b75183592c166b26c8e9d9237476091d8ef7fcf4f1b946dbf8b0ba5827b7b0343737ac1bea5c8d5512b6d53b521f168f398599d79252f4efb925a5c8db991458c7cb9392e4b5a434814a9e5b528e78ee95eac73d91863b54eee7ee2beabd0a1fab098e19a705fa76a894759e7befdab987f5db01067d36763c9b764aad4da5dc5755d816289714541c503e3b0b0e07ec611d77736300033c5319947d3ae757238467c72ec6c1020b0e07bb661cc6a1c3cdd831b5c082c331019ed1befa2e5bae44f1fdeebac0384eb03bac03ef89711c8e773137a37d5e15d68771da29d63737353534345a0ce1db7d5887bf6511165487a2ac43db7da87ee283ce419fc82fa8b1a1a5716aae4eece686860683fe9c5e48efabd141372534411ecedd499f3b87b20e75de3c543f973b771768fae518d59e3feaef6ecf29ed95733c2ea7ee8371d683e5f3f971a946bf71dfff34f7f5619075e24daffa86d539daa6bd8a94d412df4f28f9e8ee5dd6e33eb2be57af8a754fa1e4111dde4df5231dbad4d8b9a4d3475e967cbf06427ececf356e6e6868ea66bfb8d1e585b6690c9b99a1a1a9a9a9f135301616964974e7309ca3681513bd7d2605c67141df56786865f45c30de16daa65d473317166699f929e9673242e8da6736cc32c97ca3364ebe6118b551678e5e69346df35ceac5e9be031d6fb0c2b858ca73152b4f87b6a385b6791eb517a0b6810ef4a672ce9f03eb951776a3dfeea2770f98f168973d3eedf002fdd0cf9e2668c52720eab9e58416614427b2fcf5dc72620a2d4a4a92a770a249133e605da184e9597164892278b0420826ba5042b970e208ea44d26359d16ac2054d20f942590aed2281309e8041161cc0f0c1922450b185143468000695294e3041110db660c208efb9c5849518b4a21cd11ac2132c2ae48c503071838c091f34db18c8c4345154831622e0400834a05a30b102c8c40e93091c30cb50c0bc3c9692058c9767194bc60241d21721c2e0a18b264b6e8828515e5e2f5b515a2fb31d29254b0a26a06022c9f4046b8a2a0f4b15568bab38426b0a15620821094528972c5a439082c310969001b30e2fe1e555786e05e1e82baa3e8cd584554d1ffa7937f73ca6d34aa97f80fae5ecdce5aa9ea63efc575ff37e603a7598f14c37aff5898cc38c23bda544510b085e9e7f7a049ab7330c639cb80d4d37c673e76c446af88c47134dca38f2aa644fbfea753467f1f6bcdf66524397933160b1584eb0b2b8801090048106ac160f598ea0c28b227470240c2c84a0e4634b08358061456929a1e4b1e7961249b8c896deb225ab89272c3ba462a1b3c46620847eb10e75e819eba4ea9402f876202e806f17c0b74300487acbabe2f1806f7fc0b7f318ef3360b186d044600de10ac38ede5f5028b00dff869a40125dd8861f5218042be339e3e18177cef0b595f18e88255c77bb873977d9152c203ca2a4f7d8a912a42b082d25d439eccf3d7eddcc19df1f33b08e0b8ce3fcddb8c162ad56eea99465e560553fd706eff09667a5c884aeac1658d84aea81d5c446e7dfbc267ef419176b780121843df08a73269024035ebd26fad2e5b9f5832b9fc473eb0749da9dfbc23aeecbb3fb892eb08e73e1a56a562cc8ee188cdace353f28f27223e3c1584158fecd39b10eddf9cc53411dbdb2c2495b2055543b2b08eb9eb5c46e944b2f412fbdfac221b92a3a58d60c2cf59ec238ce5930b05b2f29bd235272de4abda5b9b81ace88719ceb68d60bec466974b7fa25586c05d6c0318ced9c3879f7e4194a94efdd19158cdf77834fc34a62b7d5dfb0a08ecee91cd593d25b7af7c41b83956725bee2e21534b29c58e933c61863355d945e946237b0584258e8b1abbbc93337b594f2bdf7e6c44cfe4cf5eacc744d3925c558b0086531b1b19d95643b27730ce5186ae8b38c526a4fa813e653ab0e9b7113add8c649d7a374c6388d312f866504ebd4f105dddd43356abc722f1900a1c3f75846ac7d6e2519fdebe78818c73e265c91c262629d6584657fd199c5d7a5c418a31c621db3dd3dfb3a0c73acdb08e6d1e4f2849a8e9a7eba54e8313799b40d6658a60d3d56a5b774a963c6da3108483841220927cfad247a7898440e53648c43a6f3b371f4d918e276e8a4fd3c7af4785946ecf386168acafdde7befb9ee14cb06421d85fa08f6092490683b6357bde2b8810ec8d0edeaeb7ec1b133b9ac2fbd4cd774e1e63429bf61d570edd15be81c55eb884b5d743065073bfd390b0893c9e43b5ae89c1adda4b1cae4d4647293c9ab5f15c61c2689ed60e705027fb33645b3ba91fc91956aa870e907db5e6b8e1bd7f5c21249508e383969d2031286e4c848513644c88f193ab081176290c242b2db0d5575b657f0aa60df9ff8427af5ee7b3046879452eef2ed593bd650a65c268c9a62a41efd01c518238dafbaf0ea2643ea3355bc2e44aacd44d3b52a558c4f48af5e0cecc653a2bbf06cdc804078e255457a553106178a58940bd0dbb15c60a748b690cea47c8a6d52a12f21e1e49d14cfad1d5491e145872f6a2cf4ca29714dac3b2377044324242446031b98261676fc48fd7348e89081dd5827d8ab273dc27e8fafc0588d87d5abf6877a44bc9550bff3cede953a9e8d0c29d5bb271b9c694b2e435dab1a2a7ae90c878c5ef670af9243462fe361f5609feb00c2339d075ed5bba798f16051857220f64b88bfbc74ef04871e3e23f4b2d02b365dde7787e9ba2cb078b01ba5efa99eeaf9bcf227fe73eed142afd8553f38ec2f09068a7ab9a5eb727e39a1bedbe53fdc74d7c5d5b9eb72ce5dbe5155cd9c5d7557df4d06f64bc5f727bef38bdd694234560cec7683fd07f6f38673376e08e9d5e599b6b197bfbc52beaa5aab576c8a941a4cb05b8de8ce8abbb2c571715d3aa73ae611bb5b1b7db6a35731fa8dac3e0cc31c5e8e616e673aa7cece4e284ae3301efda6734e6e3d7a7487d26ee4c83416b41d5a0b33bd8a34ac2236bb3cd3aa9fb2edc64716b0a9e3ba91a373a8c74b734b58120b88d66870cfeac10afd46dd9f8d5751dfe0537704945e459fd185f524f72449896ba223ca3c9d1cf6c9273d9d4ed529c738dde4d167dc344d1505331ecc5a6baf0ac681ac4f87b13eddae60dce4432ff4274b515ae69c9d86fe9455a7af5e93269d26cd79a7c8287640a484a250633db1d219ca4de8e5ddd87fa4dea9a47314529bf62aca88823835d5ece9f1a553d5bc3f5e1e3e4b7937197e93fe23d5ce3ee34b17d2ab087df0fa51a4c7d2c13e67cdc05225979399849d9192f4d68aba28c360eac9e77ca1fce2d831330f8c3117be868e5f3ca97efaa3c3df489084a2014d3282e439c9062e493620f14a509e5b59ac6471f2b136811f0b929d1a75a83133c768f267baefe6d97dafa4ab22fb5312a5e6064dcfd088511f9347f50df35eb5a5339b4379b949d6ae41ee65673eb00ba566aa9555303ae6f1084a1ef32d424d08fecb7a609a0a8b4097531fd7954ea7bc3b1e3afecd23e4cc87cd9584ca5db9a61ae1c973ab0a295520cd2b50ae20a1516294da48a0a76dbe7f561f3bfaeae35df9a0d249275d318e36bd4ee72a4a7df5d3e15dad9a4c9728e734944ca7d1c43ad6b91cbac92f2d08f5d7ed21dd69419c5be7a58a5705d1d2f3d32b465e00cf2d236c7121f7986f73464de526c5347855c7a24f6cce395d4e6cd23ae1bb5e10f83eafca5d7737eaaa9fd54fa7dadb543fabffc2026decf4fecce77cb35ad150628350fca41394a624d5e78e679cb3ab9c5707dd95d593a33e087cea42f063ca4493aaaec7f9dc867eea00619cecaa82dc6fb087e906b9aa78f9457d3349870c21ecb9facb4df0a23ade948e7db6a008448faa379dc32ae8cf55eead6ee6049aee683b5fa04dd270553fab0be4bcefcd3b41f5e17f4b7fa504811f848a0e7d73734eaa92ae921e574ffd5a3dbdcef99cda5df5ca65cf0a12aff3dcb24264842a4648fa9b1b20ce99719e6fcfa3bfb85af58a37e9d16b843484b0eed9fd455fbd2aded5c31b2318e53369e5ac553cd89dfbf0952677f70d3dcc7c44e71b24f3d1ee6e90c7f4f2980e7d5e2009f4ba8b607e4de9346060653ba5cc99ab4cd59dc66ebab0497fa473865593cfcd6d2c542c7d75da23e4de390d18d8e61ed5a50a029f3d08f57c1d73a76d41ae07bd41dccb7c40e7db9aeaf2e9f05dd5757f567fe9bde99563b6ddaaec80589a820aa26f7f416ec9f973ae21c63aad138b487ae840cfe10b42bdbc9bd3dcf773d841dcf7a5d1391bd497c5932c90b6e84078d8a7a1fdf27979cf2dba90bbfcf975812e22cfda76f9d6d267952b1fdd1dfd733805118a3e9f4b1504bef320d4bb5b1d663ea4cf1bc4fd75b7e7d0df0de29eeff63c62a8a7c19f8f772f8f19cff30883b820eedf4d7f2ee45e02b50bb9a0200787a85c037d2a2d814ea5587aeab252ec3dd63fd6d3df582bd028c20639960d7e39febd25febd57e5e6af8c47469eee033af5b8412c36bc4e331e68f3ee0249a7d0695010f7f36e1dc47d74a07885dcc30b63132a774e90a31184dda09362cab7c320eedfa5d7020e2d952e4c134bbc02359f5b518441e54b154b10438927b66802094a0883ca0fa83091429778439c337ec9c10536c021090facc0c1135cf06060a292826bc9f342a5064ac5045da02065db04a52d680bd5c47442521338a844bc5613538b887782a2b2c064744d305a44b155821963442c81a371248a214c3844d124d2c0013bc1952850294252d1817ca1091cb8d0011268608316ec48da84975498bca43284b4cfad28aeb4a62cb5a614d140419c1206508890a0a50ad1168e424b06b2b5e4d1286a45b1c3c7e756144c6441c3043472c8a1832e96ae2822092642d02f2ec04b0401081dd8c0881e6831861731d0b881c60d3528c649614e8f1418dab1c0d08ea351c43e2ff21d080309f86530b0d3ab0a7243e20ebd444c3b942774aabe3d45430441e01dd7f882ccce906b872f730ef668dae19dd4438f9abf38e30c2a0d2226ecef3d9fee8eceaeef074c8e093bc347a4e711f1961de9ecd2731431edf02532ed48cf410222a61d79894c3bd2397fcff9134208111111116b4ca6576d73346ab040ecfcc3eeef3c887321444497c9f49d197dca77de5caa739f26226afb42f6c7608d6183d067f913f4c69037e88d21ed3b0fd24444cfcf43e6e2fbc341197368e29a3caa56ce7cd8317c3cecad6d447ae0079e3b9fc1f4ef7ec0f47c5d03fd30bdbb1f306dd7733ce83b7f4054764b8e9052c5eee3887ee8ebf640ececaeaaef0fd3b7cadd1eface8304cd77ee889e61673c64f8fe30b94be7a652a9e67ec997eccb677ef94c9794f2f0dd86743411a23cb7887004118c3ef5a2e678c2bca41947188cd22e16e88a99c38cc7e5993fc7dc72848117a38f067a79aba9dcf47ad280e80de2de3d7e0e9e9c71a0a3247dea0f4518cf2d289ef0530b9240cee596f9561de87220ec98cbab6a4c0bc2dcf954fae95572795784375755b657f06a5a080610e47a5809f88bf5cc381bb788f0c54fa1003cbd29e9d5b1ce36fd4d7ff33ac6a1ce7aa96dfdf4b974fa5c4a8df5d24d6fabd5a77651c66116bbc92f4dd5f3a8781bcf198f947703f24bb7eb37f609441427cfe911ef8afc83c2cabb224f5ddbdcbb9e04fcf377753493a21b9e3bf282c4911a76a8b6cddfd8a94f0d883ab5e1a9f497fdb822ff1cf607b9ff7135fcbb5b739fe665e9eeb7fe5a44e586d2b7985edddcd4d4d0cc0839e860d63dd7670b0b2f13797fb44dcbd0850b928c32c545d85f5fc7148dd7ac6454331a1315bc35979201801434198d4991efa870abec1818548655135d4105df6eccd38533291aaf59ddb06adcc8d1368f8719277aabe0af7d827d1ea3cd70afec45655a8df67c058d9f8d0a5a149b8585ae1db69417004d6d601d19bfd9678c5a4abf58e72789b9195158daabd7a39f16e81e7410fa36a7f4aa610a6a356ee4c01e17167a8d5e65f7a1d7681b3fdaa61dc3666668686a6a64807e4306be5b06ef8f5ef5095e26468670ce2e39c71041ecbb1b34c78802d1439d20f4211c220e01dd183b43d894af66601cbaf22fac67d34ea9b53fd88b6f0fe219edac87648ad1b7912f98bb33b40ccfa1b3c0f800e3f48c2e2c16847e52c3ded01aac668cc3b719cf06781e80a35006dba02896e11ce9ec881eea845df4ce9add953b8d8933d239d0953ce9a10912236df37c73475fe3630d66da303b933dd394f578377b6d8e9528cfe669f6ab56bd5ea0be2fc41077f4ea59495123179143e2a2bce749513ef5ac1267a457cf0888907d14ccd89c0effa0609958dfd899807314ad72b0ac733abdd381715cff5c05ffdc1d391cedcf3757f40e05ff1cb6bdee4bdbbc775e94609ff7601c41a80fd609926ac177740e7f773fc1ba90ef758c0fc618639c8cd353d2e0a02bc1c310a45e8871dae7f7641d8e3570d4394727947452eadbfcb7848594524a21f5e997976a37ab24ac20dfbc4789c074ca39676668686a6a6e6ea4bb7f34328e203448ea7b76774369e49dc683bd101111fd26641b4264c3bb8cc78b02d16f448a107dd010d1d7f0cf9971a6f4ab8a753a1e0d43440f31ca3ad70f95e0215116811bcc08d525f10009e672f876ac7344e0a6d01aaf734c36cc67671bd13714f44dc6db6a16c036ed323c1bf1062af062082fb2f000073e1801c798f30a82d238da01084964f9a2872876e0620a2eaaf8186394589ed8e006258200e34a0b7a8a844cc3c9154bbae841912ca25425f10757781104175d40e1810b1804262439bd2e7c403929e495514a29a594f1524a29a7904ba28c31c618638c312a200bd2143824f9a1c8869dc8828f31c698821d24490104125e7085879d88e4638c53a6d0c373920221972065289670905c04d14a381c283881d28322508650836b055c3cf125f052c1125d3229a46ce7a4434829a59452be270cfb09c94209484a40c5073fa002b96042146c490d4be9a46059e2030a7ec842c90ae00d98500506886631b208c1845296294d205d2958020c66249c730e197971421282580297221cd999473fc5f8e9347342e841380714a1d583122c960811c68502a47e02051d4ec0e58b134a5ec4600212d8bb8e70a1c5bc7410860e3ca082094ce8200ce8840b889631c618638c3e43a90f766084092b56e0c4071dba8831b9307201c19d20a907151479d1654b13700829158878197181022aa7fbb1431849b088228b0c50709d400946d95bc20d4eeecac1cb5ce2be18239b37ce39e79c73ce39e79c473fe79c7342cb54889448a2c484872c4a98400948af077372204a148460e2e4053960e22ad2a15b4ae0614ed8c48b49e2c3020b0f5ebca088154780c14ebc70f8b804167cec12c65b82004abe780214528c5064041cc2124e10598a2594402e60060fd832258b1bccc0052f58c27503174ee0027c983405c5ca1caad52df591239471da82be75a478e619fd6a6413aa8addb9ea67e69f6b36bb0d4980dff10d3f86c282ca3595461276e3281bab039db341cc7994e8402664557951ad07cf803e351f8c03634dafa0af9e0d179e0df4db02e3d058c2f2cbf00352cc5757097849f6435261787ef9e5a8633914db11515f0482dde89692f9e83431d6f42afa665987756aa2437a3d9895d150efe31e225919cfbd86728ff6545025ccab780626d52bea97cc5fcf1c4ff79d648c33bd8ad137cbce631ed32c8ca98961d42c9c60a1e4ab6b86723a3b16dfe560f603e36de87a50b9878ce00844434c3ee4f9f421d28710c9b1139d08cdd10e11ae9174131e15f088e8789050d8a95e2766d286f0959daa0d79c04ef57a61266dc80076aa571366d206b0c5171467d88989f3a90d79410721ec0cb99c04d48710692e76a617350c75c7dd21433bedb56a2a0af04e11deb2e35ce5429177b4e39c6627008ee6d288ded1ce743aef90a11de79cbd802262c73991e87a4b8d2ef32a1e5dd965bed9a32683dcfbb8375d6ed2a46f431118faeb3abe618fd765bee107e98534c8d1e03e48c697e969a8a9e28d13f6933cde1495552f38972e03838bcf190c923ef3b3611c1a4ad84dc6a7bee10ade9a5ea99ea75ec6cb609d777b64a45e06ebf0f7fba1665ebd66dac92bd38862b9a58595dfb0afce2d2e5f3cff49e64f5ab55a1376c62f9fd647e6ed8549c5d0ab6c870a523873a15799cfaaba8cb6811a60d818156e0c315703bdaa6eb5d3af9e8d15bcba651debd5bd7360bc7a4de7a4bc3aab7332af9eaa0ea339a25e555f417345cfa6b258abd5e68fa1300c25c3b7f900c06438009c8fac3603e354a751c55287bd3a798d984f0a2e5d8606c4d53c0034200ed3b69a4f41db84de8cb699dc871fcb7a367f94fb43b98ce642af4e1ea36d5ce54f3231f48a3a4fb1306e7b95b973594ff7c052d8ab8cf62af31bbd3addcc73f48a3a74186d73184d3de2e9b31fd8cd52f8a45893f3603374cec9ab0b750e0a8579a6c9e7ec745fe623d4abea427a554fd7097bf9c64b5f6778367408c9d86b0cbda28e754e4dbd406e0aaaaf2795bb95051251509723a17ba70571969fcd0d61b76b08e1eb796daa5791610a2f7da3691bf6d93c775a0dc6790c609b4763807f29fcc672168b06115838d06763731f6160ec13f6e49e550135a71185c5b1390c32764c37ba78a55348af9b1830f990a19d5a4d9a6a06d50cefe8dd6592790076687602b0c38f6647003b4930d2c2cbd776335376aabf8c02bc63030282d8a9f706ccdd8e491bc04ea60da94e84b7ec989c3318b01dcc5f10da1942c30e36d9198854c14063a7dee77c664766c73de71cfcaa0db99c8869c7a40d994ec4b4f3aefb1887deee213f82d113ddb1ece9a54e9999d5c538cf694c616f40937b93f35fd5b9faf55ed5dc158b715efbabd1abe7a9f784cb7c4a9528525239dec60dbd3618e8367a667a05fdd910f284b5da3c25c32d097936dc176d03ddb5d8063a84f56e2bd3dddc684b1979f885bd3c4cc9b0e2a1c7223cf4e79e3c945ead2015fbac67416f2847c022acfbe1616ae9d950eaa2ce39ea248d44a10962ad84a708cbfacd9a2e3aa58f150d7353180742b7ec60b85734bd7aae65f91d5227998830c73a0c59a7bd572fbe08b5d47bbed9c785cb96e756165e7eb35948f98a8a1e35550f3b7bbbccbbec879df966b0f92de3c019281646d50426a0c592cc671f359987466a7b6b33af0afa19db2bd3d7e740a4f3043920337bee32d7effe5d3d09b0771b7adb3e9b7f769971aa8ac70a26375dc7a3e3c168db73eb98565b53fd54a0ccabff5caffdb2ce998f2bbbfc00ef9867aea39fc60f0b2448e8f240975787994f021e663eaaf7dd68e849408fcb6ba31c080f75eb5705621d26e3c96c90731608ca79a8c7ec07029fddcd61ed7e520c529b796730bba8be8044a0ffe49cfd3831fe721807c2d39ed5ed6514460302e33ced10801d258c590f083c0c6d1f0e8ba1251fe4b23c7548b354e0e9c5fc3db7aa1f7f529068cc2701ef18077bfe7c0ef0df627ce5a90186bddce458f5761e331fd79aab48b8f2dc4282932d4dbe5e58f304654e0366f2b7b55f5e5dc7ebcd8388fce5970bb9affe7afc2f9e93b3d363413d416e8c9ef75f1fa4347e538c03bd351518073a8d2058fed54de7dce815643d8b71e04625e3b09d396d519fcb7cad3e323c93ba1cba9bf9777d60a94fec3e1d8eac057196da5562efb937e77b734ef80870da888cf1d8b58cc3506cf7b0db51af66418ed1071f2885bd6a1fd6cf40b11d539642b18cf490f20e2a77dd1283ecb9b525055b8c54c0ad2bbeb4ae4042c38854eb8a1bbe9f5b5718c9010e206ba992a587e72c46cf4b3cbbfce2a13b2d5bea0555f06a9cb13b6d3a6b3fee1d730fe7ec33eb31ef830f9d7b9cd139a772d2b932eb4c9fd37b6c43aa79f31c8fbc2a77552ae7b247e5a6bb79f69ba731cb7c387f310ad7e5eebadc4514d74c715538894e79cdcb65c6c349bf544e4aaf9c77f6f3e87549bd724e9d508f494b7c941f931e464881a2531ef142534f77bf944da4d3244d963c8f513e3a75129dfa7369c5112f7dca3837232313735d7509299df30090f5b8a48f77c3e85348ddc5c04887b1190faaaa4e9dbfde9efee8ae5fc78b58aa869563c70b1be89c1662e4a887269d034546ccb026d845531803518fec12cb7e5ccb0a2bff3c46cf7850ca6306a7d3a854ce548541eb14d100000008004314000028100a07c462b168402468b1ba3d14800b9ba84c724e1767518cc328648c31060042080102004400046668aa00c6255612461a7dc0acbe7c44027a4be74a4819804f658b63dc64f4376e5d1ddd68e405d132a732980a0493a6d2affc3f714254a5a4d0d34476d94b1ffbc7145345ea428f282b2e293d76f7530c545636e54f596ede60e6d45fd2cef45a8ff12a0f15a8e158996ee29c251a0986dffa313bad8a7cceffeb06d87ffde495ddaa5354b02a0d00d7058b228b6f81c90329c55945383111af13fadde1f81d7df60db2ff7c62e79292937560fde55c6ccc1a3be65d5992b24c5ca57c60590db3926222931e974fdec8dc065a59c66826ac7737bd7041161209c5eea261f547b9dbba66f4723741788a0103385bab344e3f10d1e58e35e7a7c835ac6b521527d54ad49466d3230109184b537e4a71fbad60270654dadef8a1b2f407288e40f7925deab1c172fd1efcf450d4218272d7cc436818c6d8c5fba384c058812cfde2dd2217719549df7b4395130c1edc64d1d3f4d1d99313cba15d4e600047924a8c742592ed7fa888319b69f3addee99b6157965036c94afd2977ab22aa949cb3b6831b319aafd8b734afdc0756da3da2728faf850f0f0d3c1d512c80200bb44d2ae0373db76f220158cca760e05c43495791c4d30e8d78739af1c614554652c4e25872888c0da5b37f5bcbdc30aabdb825403b7be3ad2de6a0c95fd50a93f2872c6e18b5602f51136ee95125265dc995f640f4275e1b199305e256d91b219019eed0be770633bad2dff19ae5b6c68086135a40614975a5445018ba34b175f7ff5fc15b1dd291c1c48a4b4a1f6f95534723a36cf54ede74bcf51276f462332fc5a2ac76dec45a350c67d21aa05deb0d95b592b367926537010f54e9407d2be803394cefc86685a19600557aedd201ea4d7d5c215c09fd2ed79739deb663b3246cb19a7561aa8eac2171f7dc7b02736ef127d43e89ef014f8c865dcc16489e55b7151ed7de5c878c29a1f2d781f407aa27b219ab243ffe4ba530b292e6d3c535023119574a16573b31df3eae897429d4aeabef95460d9d68c8fcef1e5e6a78899c26b497389c2e3d5a20d694efb073ab18ee7198a4410485a37cb2b46ae78dc48e57734f38f18d0be53a6c6a95b21ff236b093853809d55507d247090a4c5432a3d904445744ea391730f56d0ca18158243c2106f4ec0638adca0b528cba0b96ba82270cbd350bb96972225c2eaa381d3510c544a1f1e54a5fd77c38e460bfc05508b2c035223cb9493df8558e3a6841c968c075d44961780093c72f5e791964edcfebfba6ce6ca768b5850947688d966ecd8dfa96554d401f7a269c6763f4c2e1c774b4dfaf197d48a4bcf1bae6404b4924e76298e5e7e8aab5c3ae8945d9ed82392e4e7ea7f39d1593ea2733b99e32eb999a1e293a45072c7ae2f710aca922a0021f82a50ee577f94d0834698ebdfed13a9180fca2a8f8a618f52c7171d9722f5e33eacaef83303c087e675b066fe00aeba36785ea913e42a40c203e80bae6261aa58f6e7468ec36b126c0ed0f8907afa7f596f40db5b2aa1db5b83ba53a1084914e3ed2d1803d6747c6193495e2afcf4b439bfd0752d492bc821638b96f7cc529adbd4186da01511a2ec18d6a3fbd47ceb56fdeca7721eda59c805da5a617fe29ca682046da2999545ad961192f7ff6710214a5218a7ea7c6a61b4a09e0fd0c5f4fb8061f5d67ca91995820250101afff82703304991246b19488e650437c317fdc360c25d2fca4c6015dcacc827d8c2700146314f7a39b9617c9b68159f057fc61bb89fb601780fa0dbc283050068c48e2d1c89d6f0bc4d8b56c905f85e8480fccc651c300e9990d648997eca7fd6113913c57be224855845728ae9b0ad10e4eaae3af52e2c074f5e4d22043200e902d6900738363564797b5531497771a81e71725e7f1082bb2be0bcb9a461026d06862c8e0e26e32132fb570f08d0b49c04ef7760ae1297034837bdc9f6984e65983b1eba4ca61f6b4a2c656359b92d838de20d2a0cd1feffcba50157903bbcd3d967b3c0393100b21fcfb6288c2385146078724962be732d2a115d7491bc487fb76497c6fc4992b240bba2469cf0cee004a29db1504afb655024a1f25f8dda9e582d5ac16e17deb01f3239d4dd595482cc3e0bcaba37cb2438c2bf5f688fea76aa6a810a5c6819794c16d4501fc3cc6aa58915f3624919ea0ef63be1fb8baf1c8ac1aaa57d27efc1de80fbe5f90b5681aa84235590bc8e87db57d99ef358f528c56e213ef556d91de37edf1b05cfdb5411422f7f8d45751f8746d0bc23ebee6c5fd63d7d1c04749676bb68619c177d8420d8e07dc57b033ecd27df91b90bec47efc7d1c0cf8cf8430eeed5c71990a857b538199111eea2d4fbce7c99df447fd82cb3cc9b79c26fec4b1ac67c30a39fb3c0fa1e0d39cefda08c2bede4a6e91bd2fabd6190f90224f677c71d955f173d3ef7e0921757b20475ffae529c8ae18c395fc8d3aba92654fa00e7d3c23ecd012de398243cf4228d8b2b0e182377741d4b45ec71760e4d7bde61fe02525ac380e83ba07b6be54aa94fc76e466d19c425b9eddaf94a50d0ba185ad2a42822ef0c3ca84cd9e34b8396cee8928479b32380ade642b99b508049c79cc7e4e957a8e5013995700fcbe83bd99d2113790b427cbff1b002d253dad7c073ef6764efd1eb4a9e9a09bdb776928aaaee170dfa2e507ed083d011868b720739cccc52eedd91dc11ca80144f364a775fcba7ba966b9d047da2be7a788a38225ff190356f7aa16d8d1f24b1c8c3b74c89db4da0af1e8e34eb5819808fdc8579169ec420cfdb610c62dd8246d0c7be8336a7321b35e9df85e18c6bdfd302b2505a7fd9b003a235773cb50f73c716f88e32920bc7c1569cfa5c564dbfe9a2e6451a0ae7aacdf7b218150995602d20c84537d381c38be2a0783d1727ee57f1e6e6897e3bc2a717f1966961a826827eac675c80a218cc2ca412b08cdedcbfd6e55f86bf06d738a0c7d84c47e0d3af1e9a1fcb7f1cf2bedffd05df6d02a578af5959a5ebdb9f9b66ef218992ea3a66e6eda59cd62e2ae490714122ff15c440528923ff1f3768fa769e8d2cc963414bd7420091cd5324427f95e579d1208f52e8fb30e7a5b6e8a0c4161b9f157851e465670d933af57a1630b296656293bd40997edf7e55fca53044ac5c360977fe797aa110ff254af609d24dd62b98babc8932aff151820e384aa389485aa13a25828a1c21fe87cfdd1c1a58a838a0d3831e61bda606b232d668baebf66e3371080a19901f38642dbf18a5809fc8cd50406ea7eedb17832b99e5df0b92a370d0d3ee4b7a9f6e465e171fc632d34f3fac3c7bc5e034e64301471f8230f7dc8fed1d02739a12cb86991cc9d912a77ecf8dba50dbf90cd2d783ca06775da4cbf0b09160198580aefa677df4908a7ac084f5629945f219f596d0f9e9731001a9eb59ea65428d19b69e4a51d57991ba0e28831bdc6914d9066b0d2b2008c0ed26b52977369b77ccc3610a3888996c5559d415703b9ef3ba718cc920535f56ca336759dda5a911f3788cf702d6d0dfd6bc0ca283e51ecfb7e276cf1397823f6df4a4b3352c7e2060104a7efa28bbe2c1ed1ff6fa48b26b3d7743bec4860213d5812dfefab456226508c91d9a82599f39c693e897f8e5f1c03134b9717ce4f61d9b907657eb2011121b6fa2dc92ea404e47d623d7cc2bf0677a5c10799bae48757b41983799bb8321582d16dfd2741d0e4466e0d8b7d26fa9c8bdefb481405c84cb1bac3ecba886e78e5abb183db7f616adf3300086adf0cb3be1ca314c9e19e43f9557063111fe42cfad2e195457109cd0a380fd954824728445b8a108dcc5349ffa1bf6753698303ee90cf8dc735ee8bf6e6f0eaa9ae40c4a7da8b1b1031f2b2421bd934603749a705765a16878b4201d355ec9ff854af51fedb0f2b574b0f3b624c13adfbcd62bc43637b9de45ae49c2a460f32902d4005b476260dc7b66d8980e5c5279a9cc67cb174915f663fe1ebde50fc3ec47ee4de87a600a6637edaafc7a541db05716d7d9a11cfa8a09251b0309686785e4bd42887070ba13d6f907c324f1f3344f656cd8bed3bf57f16e9548acb6e05a7c5c7ad09d51295ab27d9dd983286466db9ccd0d0f3b0325329c4feebf85523ace3b30a8a21508a866656cba77d1ae32221ba703fcea524b978fd06054add51900b3d663a7606a0575f00743d3ffae14614f1593b9ec0273f43ca0448aaa60251f00b76abfc54268d77fe4cf72924bbe62d5285039c8e572a4b2b4b344e94064afaf179a64caca2eead42c57d711c742a33e00309c9f67cdfabe81e71d37b7782e2bfe89c064409d04567a447dc033e0e0726528cd9ca51aeb65ca9d80ac1d08d2ac0b8ad35eaaa8e2c14191682c9c950c1f1ef4e04e6a0ea5c8641f998f8da6f1babfbf9e3da6e90f9a548c86b038a7a8c3f3f6387d8d1a82baa3fe11c2c1f10c14d66e260f27640cd50448e109983d5f96817345670f097cb60f3721049a83841d6fe491750e6ea7d70f69642b3389b0f662eefbb41f83edca3926033eccb0c18a8376e3aaa13b13cd5979013e73e7f7ec4c2e977247d562656ce3c1d2c2aa4cd7d00c43486b2a27389a7c12908d2da672044cac4b73c610840bb2487dddc56d10446df0e8fd7b80f08ec798bd1d9a90daa9e1fafa396b24ffa47b7dc7418f3084c14eafda0bd9e4016be5a7966da536f97649aacc7c1e9273e019111a70da1637449522b6c4de9c4122a3251d397f36f12adce14deab02f484aa1668449316c9b4f681835ebc64381834d18515a449de3d39a199c965092b8293c30c38dfc36c33a2e193f8088e1f900f00ce797ceda90824c195241573d6e61704d384261de05c1a53144ee017d70f00315e31d774c08018009a9b4012cca0078c8a6bc0ca0f904a9ba6187bf52926b5adafd459ac9acf91b99e6500ce5b89f4f2a19f844ac8219523f196c0aaf3dda2fef62bdd6a89147676081507655c48398dcb476b5e43bd87116a627df0eec2e746b81507b7e5beae7e6888f3558dc55ebb5e217c2226edaf518d1be19d9b71bc90896a363a4285ca551524b649d07091e2b1a6d3cdb0c80db80519d3165bf7b10debb1307d3fc4e14213036c92d24b2b4bf0e3b9de5d388c811b2169d81943edbdea2a5d0b64d907e50d9c9aeda5ef52065b5a94c322589a3703a0c99b45811e5bb796a48c195e7dccadf401d18bb4c3d08f8058d0e891ae3ba06be86274ec62e9f4a9fdfaf0890b8f261efdb529a1116383501f1a64867c8fa28909c0730ab19b2c5f2a4dc55685d6cd9c17261baefe4200a58f2a2021c8a9101fcf024a57680570980b098c4b75e437cbb0c4abec3c804d0ed1004809bcca761cb684478dba8c69e0fa36f5fd703e7783531b61ba4b3ecc8315b752271e8bcd7dea93cb2c8d2c718d1646b4795012859b19a0507d81a57f9d43f77f3f24cf7944cba12a7eeb34f067373b39ed5b73b8cdfb6907d44bf64c1c0c022a243fba8313eff32d5e05c8c14d5c87233fb9bf079ef774849a129ca2d9e0a0bea94f206cfcd8ce5280187231d6a0bc3e4ff42a0e614f76e204e96d7a826c08536b68aa6555d7f24eb51a40bdc093536a97830e6be4cf765a52d8cbde874310de75789cb900974ddf8dd6f4b88f356d69b1787bbc5f6a9870383c2979ff1af8099a243d6942e38fbea5e86bbf03441ed7c57d5fab5bfd1819e46a1089b5d66214dc68f18e4c63301088bc1755deb593d5eb8cf0155c2b7f114455ec175874d5be0f7c2ea616b6e2f37babb5b17be2daf00518669fcb880550d74741b0d49d733c3d7ad5c3ba3f35a0ed4fb8db2b560b1b38d274c6cecfbae6115c913207d0e8ec121c54b81ab2e8fe78d7b275eedd6894dd1f9f7f9d86fa5c5d3ed5fdcadfdd3f509dbae16748a105ca239cab180424df742e8b1f9f3f4cd7486286d7c1dfef7a1fce814c9540e8ca7c4f0ab8a727f31b3f715fd5b80161f8624e264e33ecdad74c17665b1e10a28a3e3a40e1e443c61b5086a307949c7648996f3046d6174b305537b48d45626dd474606da3b65d2420fca99cc7f06af17e1bb58e11456f1e11ace0ffb9683ab4a79ece997a4d65436fd396ea141ad9124bc5a754aac90f90b346d75285691e908f85368a631f8cc800571372314d17ed71ded552e8b9d82b5c80766fb5656232aca07e29ef13d3e68d4a2f69a1858a9f88d650bf83288c8c916cb5f044b5ea1c18e11775e06d50a59d2df9f090412c92638197d0a84f036b7aec4dcb6c7d97b0475494666c78daa292d6f1396e46810c5fd296d02c5213602818efff2e5194cc8d933c03eed3517079073ad1039f872a892b712ce063f5270f2153fc734c47551988ac829e8a57a3330909d709e1166e772034e37b5f347ebef1b1fed867de0ad42a908af3dc6753abff8d1f025dd39f4341189a12668909b1d39872b0f662911954d07a4aa553655bc3953204718fdb07d54abc0c4491e78041a1e7355217066db5ca029766406ac24d0b516ee71925860da5cadc529df17901959944a35067752907432f0089c80da1498391b8a579cbb5990e885bf209a020c8ff2e38f86c71339dcf342a0e0f415ce5a9438495f33cb169a865d919e415cbb0851cf7562845b9ed553d013c12b7a19d36a85f51358da8c0b2cd86b2c0ccce3561a26453a4808877e4c520a5f684e2f41a13492ed5694f3c999ee1f1a1316470fb81103d990e3c439fabc3ddf5dfd996449b6a39734ac47363cad8f8239fba30831a0504493b5348e9e4b47feb9d0df312adc2b71dc3f32a54c1c98e97afe0bcf83a4f5a529524ca8bcfbb7bd13e12eef2faa3b493e0b2a8116652e68a2bd74ed03e824509dfd991ddd2adf3f7e8a4c471b256dd59b6e4f3a2c0f8ea410a3670d14730dabf6557bafd8689839c2417227822c0ac21ed2412149c0976c703014a04e958383806714b9f222d67baa3282d0002148dc6ee7a0a860252397ada61c098f345748877613a7d1be255ea03b6502b4d0b68a8a5cffe3ceb485fd251c37800eecf65ee298e4bf0a5f4e165d5a0949910fca60e37f582ebe28753afb85884c0a0e514a645dcf2e45f07407525f5102c9e2d49170a25f20b28a626369fcd8a106fb317f8927f797f36b5bb21e1ef0e8676ca93608b3bb240a117244f7f8c0e691a740c5d2b5f9672afb3167adb02efe2ac5c48014d4f9bac241d474694949c86dd2d223cd95f3fd3287c3ddb82d0f988ebb9c544c7437398221a014234add0ffca736612a120134d511981fad484afd69a6d6579c34c30c585de8cd6f86da32db820127a0cfb03f714d026e72dff1a1c806165eabca8378763afe4f7c9edb023a7cb890bf82d442f41c40be3534413d43a843b357f5a5609e93fd90cd15800762c5c6c44da83d180a5f361cee80f4b3dab723647fd2836d75d22b080503091e889287e63197ff3ae890c94186d824c8cbbb3ba12e52eedf230a8a45f74bee22c4ad7c0afa00099a6c007cba5cfb1be0ac0ec0680913dc016bac69624c8838c1ee45f9970b9fd64369f6f54e891bd46418ba0ac4a5fc3519c9dcba492edb01133990f18435af02717a00cacb35da33285be942df6810072d441be04ed40a13f1c96212884e50324015e80fe4f5d7252ecff0a1f17fcdc93e65c447bf997a30cb8b7b082ff17e66a7e106878f6106bcd83484db1d24f47f27fd0f557a29d9ef51125902bc5e97931821985a84b9c8e6b4a51abef13d96df888a649010635e7ff3b1bcceff4c0e806457ae008d1551737d0ed1b7d18a9810080b62366dbeecbbb207d8062fd68a60ac290378a2e38ee578df2fec56a41b3890e563ad941b918390cd340bef8570f90e47a82766b113b097d3f5d32e7ad92485886c5654f70fd6de3e164a740ea8acef9d2bdafcb8e74c19aae11ac5e788829090c45b08b879e9840381b362a54d1f19f73ad6ebcca6538c1155aba8915c96f6651b3a15c6437f241292ed2035946896c72d68d9bea45c1583b2f6af7ac244b0241254730f349fe32cf4df2b11b0b9feab8495febfad6c003737671ecc64fd4f701dbecc6b3b02d402bda6453995a43cd1559d9448d3fc19cc0bc8de12c9d931a31f82bcc480f5281ab8ffad0b7ee9b62b092d5177dce5fdafb27d40445ea79baa12d4e3adccf2d40ccfdf3860bdba866ff554c2b4afbe1755b755932ad3804dfeca885c92bdbb37bb176565dbeaacee4550ebe3f425e0af62c83b220ff5f87371065678372530f0ebdb627433e958b67da81b7fb12b770d75eb292dcf62a0bfe0260bf97bc2c2fdcf61d34172e0c9a673a6ec7641f0e359ca9123cf227b4bbb5ef975bd33e15f6bcbf528167417e0ac3b3ba0640562cd4f1e2cc0b34aac83c5807f487b2bc3310dbd9888f8aee2fbf98737291872fe3dbc086575ae42a1bc16c9647ef7fc9f9decdf53f53565e2599f9c9a375b239ebf467eaa73e9c7f652eb08cfa9c746c247f1c817c917a676495c04399094101fcf84c50c415cf2772a38cfc0d79c8ae89d822ae924acc4692cc0535e5cdf55b444c0a38e394fcc2d313c9983637c0071bb4793659f18394e39329f83df0d6755aeae844b899e8b04631030b84b4c980a7848165f4232335f08400d558f1ae032f718afa9334e4ad8310de388878f89e9eaaca8c71ce093887e06e24021ce27eb7343e327e5f232ee7747ff28b9bf5a85829517491ef2663201b5ca4f6d0393e5c33db19f099c7f95ddd008f86777178282ad7b29cdcf71efd8d5d0ae61715ed2e5efe40df2baf8918ee5127c062673d9baf82228f08241d6c87fd9f791b4cd565846e744f02cb64070898f0f5b1b7f09a8a36ff9eefd52b8290d9cfe375e422858a68944650784f663e0c60357f795190eaaf771beaf774144533b0d380eb3a089f39a38e7bf9692187602ac20f3f24b9de1eabdb3c1833b8a5ac53935c6d701b9e198261314a62739a421c671eabd3c6c2f85205cfd9dabd3faad926ba32d1ca2b0d462ada9ff40eedf4f5090405be35b70a0a6b7d05977206e20f0a4be7a953ffef688b922744aa6f94606e3651afd5ec2b9062220a6b4e54c97e2572383b7f1de0b178dc28e586c0ce60df73aa3926447daded09404189bc9b1e05fde9b2c63f1aa7f24d98dd74b9574ae49a3193e859ba2e764450cc774c470f1dee3c2bc514adc3d2f936e4ea748d2dc2d854a4ddbd14bd508dc04eeacb38cccf9c360854c193acef029163b13305f1a8bb43c5ca37a088a0192896ac0f157a54d66115794d2328e966065098ec2ff631b65ebc1959e7dd8b008968c6fa632f9c9dc05b40dd587867160da337b814b920454edd39cc214effcf3b664202542f9c81458c7adfba2ac6707da1ed449118f4adf34b6a6deba8c6007db77abaf966454dd2785e16002a3bbf27301f926156c60afab3747edafc1e8d4c7dc9355d95a503dc0e4e20018cf1bce2b9abd5cd25ea45b9365afa6dee217a74725b8486859f275740b8dc51b22d8f6fd1fd15f51fb30cab3fe70423ac50c398b8d90617eab94981e9d83b55412837651ad56fdb88ea0d6404cdc3e195545a790fbae7f1f197be26a0dd246f3b51934eecf9092da790247525d7822e15038d1bb63d215f31034b5745128a54ecdd79870b2e9a7542a9128107f40eba2074e601e48a95b3cdfcb8be94e326aca768e960858ab7a532c146bb264922219a9e2388adf560ed0c7ba4640d473802dd9eb43ee1352cc4051b8eb3b481ec80daaa9514426c3e69641707de8af1c2567354571e28d990a68c1a8522d5ee162313b411dd31596fc528db38282d4dc57e5549890fc92a672bbb468498453c16b09adfc1c9f099b33d1d3870269a3ead0ff9dfa3c71a1b0337201ab7be1a53bf2a65f3dedccaca59555790af303d2283eab8e907d8da1d8c1b8f04e0d8ac080f3579c129150de299f3af8972b88e9dbb73ea5e461503047ef26f8c38e523d29eb6d559bca1afd62fc580eda39160ddb51a47c1e677fee6103e3bfebd58aff6e1ff9d3dfd1a4372498098b721a362a6aeb1bfa36c37a6383ef146be2a2bc463ac4011597faabfd94f140f7abacd37b14de497db592e4e4a91c4cd0173621158ce45b5f23f9885a3a4954afe45611851dab5fdaadcf5a484fa60c1604c32da42c257b50b6e9a6fb54090f1cebc92e6a2b1292ce3123749684b8c3066524cdc582c72006f219d09595ab34e7537c9d0afc40cd835bbcee3aa4d0bd061f4e7256e35d4af61b1a2d819f26a398e1e8015b5bd913110455b8bf52195a831929ecac2c6fc2db553b8cd86bea934c9445a2a3341f7ed33e23a575e19ed4140c5366116ee0fa349e6c11a942e08360839bbb8e65e16bdcba9079a9e86571eae22d0098796e7a7976eda55d08d938baa6c64d80982b7bcaa0562d9e221dd5034a0574c291e16eb59266778d32e23c2f46c0d03dd4425950a306fea5809ec11aff58ef80d780039e89c694bb8e5a55961492cdaff847ea4955cd5b4ce20121ee9986b1523a58e063e3383850954f78d125b4818630e7ab9e7a3c8d8eb4513aae00bc85d6e57c7cebae51d96a7fedfa21422c64664559ef6f5417778e2ddf9f8633862fb3e9098c172a3571ff1468b4751c28a2a397769ebe26d3f90ed23f45439bc799c969f6f3fb3759bf0bd524459c2058e2039f4883a940ac4fd1a5d626c7bab6a10044af4e15ea86bcfa81bfbcd365dcab19d8e4758aba2505a0ff71ba776d01ea5da9f2099da7bb8dfa1a0d826631c8da7b77a64620e3ccadf05c796b5c4008774b9de889d741dfad6f5fb28fd720d83ca55ec644b24a55b012e7de263638855770f3b9b050578f8851f11e3f6c9e5097de9630dc9764d7637512dff9eb0e3b119382d39e0300406ffa10bf5db564367a71192bfdbcdf5475fbc0f153ddfe5c10e523f2005b7418b0a3eda560ba5fcb8022576efb3a7c81cefe721d745a87b2695299d43d01b063dfb02242913aa965bfa35988fcf8a35c5d259889be6f363b9b25def62dc7c782e654f2f3691867857a0c683647a8fcdcaeda6762b9a35dbd491eead5b7121a7ef4e1a7d2a678fd8e1fc5e7c4565c932be7e622eb761085385433fc840b81683d4c7c5627a3fa2d990ec7f1a6398711583ef69e5a319cfe719f73f40e5e5298d266e5c40fe21ee2805910daaeba6f03f37d6a4577046e588478beedaf086a93258f1b4eaa157342e46400fefee950be723e82186639e86e1731359b0969ccc70e04d338c6a78332a14985d452dbba682ef7851a1c9df8d40450fd6a3cbe7482511e8358523fc6a9ae71611d865f293ccb1ac46f6ae44df549bfb976dcc61bff4bc8122e9d7d8fe9dcac45a5a7979feb6dcb60cf821b381809ade5db236e1c32194ed6e816fa81e337715b2a5af146e7370cb94af138efce231444c78ceff36e3d7b8f1619c90dd4cf9a0680a7838296fda3cc89224ca29366a7d32faf93f9e6a09ab342f21708399d0f57e18b9df805910413a36f4c09ae4ad5cdbb265a4ed99b4a6f96ce5a3a36d7c2d7362f0a362cc5b89e44c23de15c47a8260ab66e0523702d9e2892aea7376f9501cd30a1d5c41ddcafba7674b164063680c449843a167939038d42b02b3df94e5b758503c1eed8e8fc045c8094ed9a594bf5ce40e1caf40a796d46196903f4ded04cd1715b9d876a110fd674677d1b1eb1ea8d2355b1386b73cd758fc993c680d491b22133c91d0e38df86d3828cb01f09b5fb967399a1be408289780f38d2f9ab76f81989dcf7400cefb2184da73eb2b1a57e897cee4c8c039d5a6c513ef35f02c10a1a2458c74584270cf9d7c85df51fbda4dc4982bc6129614d3d5c9b88ecdc4f4d38d9526518fa116145c9940ae99a5eee69e921aca0b58d1af9ad68ca1911570a3e1fb1b979a85db518e9f414c69c0cb5756eb9e2415390637ab791ff2318ae33548d1bcc229d35a209287ce5b39e45ef5ca4bf446fbd18c5e915541922d5acad3c23c48f3e0ef41ab0922d78aa569020992879137087509cfd509be7cb7d59e706cbcd99c0e61cd3addec0b4ffebdeed5855a6b893e4db3206057e4fe07ede42cfa5cff6ab636155572120920b09c5e7222186100536f9d9a0c1f5bda1600965c892e5fabb719b38550ae38403c254869d7bb60023c12d2350adfc709f05f1d2680086db3cb30af03e6ed126bd444c4fe595cf4acb87b1cf5b91b0ebb4b463c2482ae2e1bfb9f5d82410eb61ff0fbcf74a53007ea0f24bc83b3a0b3b567b2a77aab909d81d19ccab4b61fb1fcb797b943985bce52c10598ba1c828478d6d68e554d15d479e9c23bbf5e32dcfe0fbd752f7d1ac0630b7e9772aa95d8825eee7f1afbdd831b1fe5c366bd82aca9974f7e6208c1ef572b6cfa281ba1fe02a1b6e65eac02be5a72539c6ea11cbae442c997029c09e18002f7d5a29aa9acfc3d131e216be661ad7526d7d6b974d982c82315740c54d53a0288f66fa4a64a1e3acbaaaca6732281b3564783f43ec955a2d93d7a028ad3a33936b65c53d4e3e6251f6bfa2699132f772ac02234cbcd62c6760fb18c87f9162f73db1d11bed96c6953c9fa6fd20aea39aaf21877a2bc8eb8d50f3c5379a8f9fbfe52dcf7e264e6f35334708dcb875cefc8c66058db842dd669c54f7523ce68c2c77c400de1d1b653605280d1578a5d5d2682b5f6ae6881f4ae4b391c0c88b05aa99787aa47d724305a267198a475cb3f7b719d6c38eaa8bc2b327abc1cd9c7bb18205c7f81cc2a25eb063751f249fce2a1b1d076c65878e8b7fed4db00da7a1755926b5ee55640145a1200ce013c7feeb24f00ed7966ef7f347b1f6759796febca3b70cd9287cdee9af9973ab196ce3a237b9e129c3053fd7f5204177536d9100cb221f05fc5caa72f34bdc364c8ac2660da2cc3b774afd1553183a89e3e77266b3014a70efb924899e64e7ce7ed7545ae8117c89c989ce4351efafcca381fba08b1d865331ce76d571ebf85f56bcae702bee5782fcdf1ea633190cb62a0328b810b2245cfc464981bf7378547e46c783347c53b59efda35e6f1b2156b5d8a59e04df271ff944ba7b3faa45704c6fc66e572d87284ad97b79807024b4b64e3afdd7ade6523de351b490b14591f97f61d55d9a63419786477521f857a10d680e771b4cc559669ca74d431d1e3e2ff51d6e347f570d92191647209573983beaa659be343940dd8eac76e1da216da62e97fd0632eb07c85da757984145fb96ca8c7be1aabf20ae645bc3b703a69e8f45c3b5d9e17bd521fda82974d978a943faf5c2bdade15bb535ea1c05611a303cd61a13f9bd5cb9ec3da8e7f2b04de1b4c94588923062536cc091a20ff27281f97a518c353614df2c647711d6dac9bcd38c0e02b1f1f48788052206e6cd40dcc980b7c985857ae9bf0a7a4a97ef600a5de9c5b5f0e46d643ad0f37c907e706c59545b4c9b7879d9b64bbea58b59ac5dc7d6d01171d02512984cf5f6dd33518917f255a1a835c7188531957a1751823fc1f14a75178395b7e03b7c6010a8f42e20a464b5e251c534769f45c5577921253b484ba73731daf71592f622ecf95c9b993266234968779b26aaf74b4fa53628e7f54a880a2402b9aa05b41a47fbec05e3e6a1c1494aa33f9baa5f32f38039a930e9b84f557962281118ed0393ebcb6d6b1468cedc603aef686c40c66262632b3eef26b1c1a578524542c978fc5189fef443e91098a0027be2d7341a1f8cc4e1ec0b9c19d1d9119fa5dcc6c84a17a6ec7c3cccc1fd6aa96d5aaf257bb34bf4c619b1ae686d5a15f1dd232b9dfc112c24a0e67c7c192b207bcb1c5282353a3b1084b2763ca3ba095e03280b7b78b7f2cb1c7789e12571d2edab78f1c3eba8c1f271defb4fdc3feb546a561b5ce4ba32fbe541d32a749223329962f7217cae724ac3c4f595d6200aa889cb50ac10eb76b7d82a685cc580b1be5a76051af513388dd7dd3314b450f37ae0918d3d616b18a0a1da48672943b5e27e392568f8a27bfbe738df59dccd89e1e50878e0033efe1d4930293d4b01049864e170d4f95053c5dd1bdd2b709111817259d07df8073d54b3a413ed5bfe39decd9033460bfaf4a221b3a4997f626b195d0c399d4a4c829cfa81b37ba6ede833a439d01bc9ae62f6838eafe8e86335fd5a18ec803a64de6cd145e787f1f66d5e837caaa2f8b59528ae7d81f95d57252a752a383b33b888bf4f76d2fad626049902133989610391e92b04d86a72800e16886a955a0b1e759de581422ed79fdcf1d701244ed72a9a85db4d75b4e199d1cd0c96276fb9c7b27f5fa38b7a8e167ad9059ac076fbc84420623c3abb95f85d230b9c44f8b5eb9faac295eb25cb7421c4a3fcb19914f1f063575fd663b7bc7620ba9929afcc0e266261925a3a1ad0d6bc1b1e5f84665ab7ebce689b321a72cb785bbd60ab79ad40ed0f8b143dd8a29080dd38f5f7a57c8ba35f4a961d13ab25583eee7c4d8f3ab3ed236d1dc311d2c636651d351a2e7933cb324fe8c2b924e9b1fe913e3b41cd74c692fa35d168b9ec719a176337cd0a560dfac6ada83aaa1b96a60675593f8556bf923a05fb7bba4d85064ef31b62acc34350e64e26fd61d90a39f291fe514b9a9d68f64e148c3f94af2950f632ac508d2b76be85c9855a2ac5f7dfd3e88ab07ae1c26dd5aff5971ac991336144080737382d191898d98b18e42ef992896a37423a60a53123ea90ee3b21422c1cf4ac965f2299238de9d00c8ba205a2ad9b963815b7cc860cbbfc33b58d5dbc3711f3aea10fae2ceba35fe0b075b03a93123e333ca60499a94b8534d575bdf734ef3d457529c2550d4edc46c0942f1bb6d0c6268c9cce82a70fc9815593e5ef9b019df4e61f1f46418c771ed134bc956370656f632cc690fae42c197dc22c7abcaad034b7df3d7072607102ab63f4a92e4b57a6754c04387e86b1ab77fdeaf7356b3c60c803b28fd42a8607bc7a688188844115f2d5987d54cf4f7e2e2f47c30c3b39a1b1c171fe330ccc0de19306be822654b88ed52657eed4bbe3b4bc2246f4a5e9b9014af5394b0949ac8734bebe7006e239896a1be3aa08dc9550e6303c2baccccd898c3c82582382a544a1064f162a0bb61ad97d393029d63baf3bed4b12e9afb390cbf874a327108f3bf13c38660d0893992c5bd3b2b7582e2dafe1e75f9224d077158ac3285df917393ad6fba725be4a1f1373e49a7983b619d0f8588830187c3c7e25ad5358a2c7195e2e83210ed0be0a942f0f900728c0f55bba9140b83b4f8184cf196daea4c00ea8781dde2cfe5cef13af746c86a9a3d56924c133b02c9f03e4d07cfcf4d2e856b841deefdc8af9e7f0d0458c80c86f7ca0dea27410a3719e5c627526ea3f2e8074ed4077d1f7722eab8f87614b8db90abf7ac11ea675b9bf0f04e16ab6132841951c95bd3ffc743e31942e1675dbce1e7786aa8fff2ab9b2e2eaf781175740c4edc0aeb205eb8439fb06ab6d5471d6be661a87d0f213573263a1e62c82293f82e0c3b48a2933865fc253d2f2acbe031d7c510ebdc4f3f292ef1873ab305e28eda18e1106ea911bc00dfb0490af9ee0d50b31607a996d19aa0798ed66052185be345ad73bb3d6b8a34045c1234990e5397026006e394e1490c8807feb7754e644e490971103d08a69cd6f646aa7975484e40da129058c5c3d5b45258e70ef9f19a5291c0c7e3b216182c748b537b28f8237f9fa588a1e7da816eb735e81c5ba20596b8dcc253857010fce01a640d49ec2bdacb426418433571bf5478ffb9899097e3e8a9a25f01a9849017c2db2f7c70ca2569334ba8b004380f17dbaf4f7b8e7c3a88aac9033420709d1440219cc3e200e745070755b1433f55e9e2293d851e2d96a8968f6abc497fdd5296774658b5f39a1be767050635a437cc5f3490fee24709d48ec64ca7915d35d8ddb9e60ada2bb5447c841ded5eb1415f8cf6f58e561c2dde6f512b7bfd44769967ddeba3c808c88e0a4c8c50c4bb187947d31687adb849861ddb7038c69bf69e591aa7e07567edcf2d8e9a3f95c4d5abd25ace0d3467e2396a90a28754dfcb1260c904f2d01fe48ff1208af8616506a3591d5e24fe1e700b92999f3393d21e5ee5437217557cc911020ecb68283ef9abc43972b393d55adc0617b7e6f1b539f11170815accd437d761db05eee1985fd869d288e4d0cdf82a0ea7ce3e784c5f5c8b422c9d16d126e803cacb37e54b9ad245dfba9820f12b6aeece035c827622b21411847dbe9886a4d1e20118f0d1375a6034e1c225f0316f825d5b4369d64658f988336464100544e9a0ddc5b9f0bf467dee609fba1a938903c6cfaac593c1666d6c91e0c2e3d84c256307db2a97f805b8931f7ecb8ef9e7e4cffe1c8b0247fff0620592c66b1317b221016955b254921837fc55c402e12e11a385b99977ccf7978de95d1042a62d29140e41a8a0a689523b95c8a12ce3ea69735420ea85699d95ed40eaae0a4b4a73be9ddc80ee857d2aadb6eb4ac98680dd566ac4f6cd5ca080bd8b8c32121744bfc8e000176585deb4ebb5e6f9751ce45c8df2f3d28c49f851918e213926a94b7fe39b27d229216059b8f73b107a83fb68c81d47e082b31e44b130f4b46e90802eb3c2bf44da28b3ba942c4a94db215173fa85a63820269b11b9dee8b9c507e8f0324a00f023e70eefb78084aaa0cee69ba2a921063378b517d280eae69786891da11db91182d07c81be5e6828efc33e625668dfda9ab52c84f5baf446966f2016080f293bd2c04f5a90b1974ba3bca10514ee843d68bd0b9a2680702b365af90a00a1244672b1911448057a32d3d5c816fa92bc02e420077617d32bced81d69d42a15ee03c4a5289b581996605e0825b3121ea0586b683c795b587fe6d279e8ddac3f96659584eb709a984be94d9a45c17df5b6dd745670dd09b8541bf8d0259566c014e96b92acf2bc2e9ff915e0ceb70c8b36dda7507bad43a9b762f05533f721282792b3bd44c3112d4139c3d67bb634cc4732bcf2c67728388cef1924d81a571eb8dc42e4c470f4df7db5cd49b34c0b8f1bbadc86719db1a23bd80e1dce52ee9da247b85e5398906f4c8d8d95c152861a7a42b55580c9dd2178f5d61c67f5397522a862d335533300ad05ffe9cdaa260329c29391e4457e1290480ce91fc8fd5778adc69fa5d7d967c0308ca56c9915df7add5dc3fa1d5462bf506bbe904985a34305ae41862507372bd6f6d09a22d6f537e4b22ec41422d0ed528582fb5caba0a7480c38a66339cb126d266784b94f682acf13cb17f14a516299a8ed024f3171add24159d40fb67465ee57f531fa8364ab69c7125f9511330b5799f20d96fe3e87eb4a775743e753203ae6911331f4b5a12200a0140dd635d160045eece9bc2e934d7c4993b30f9b433820eb6af5df7438f5b5770fe0102f244ae1cf6054194b407c8986db699863b5f3509e32e1fe1ab6cafe5c45c6eafecda43b72f6903c3401c32e6d49cc84ad91d0f498ffec5672481c578713b4b78bea0462b3194facea771ebabbacbfce29616b8c0a59397cbbb9b11f0b9fdcc32436bade5735876e1ad76f75299cd67ebcb8afef88786c9aad447554a363e1214961ae99d5ff33959f929c33bbfbc8777560104cbacef0cab1c7c4e2c075ab6c5da162fc1c729c2d6312846af9da556fc4a6f15e31c1edf45a35a7c57170b761c02a1a731bee45cd8de3e82b18c24a47d690ee059a63ff267e0dc3e7447fbd316215e9c2d10fcdfcfc4e34803de1769109fd493fe9dbb4ac0cad6e605a2f8bdfefc808f71fbcd7a788df2f734c849ec4b7b9aad740486b1f6e6881b1ed842beeb28158d7ba29ef8775c44a4b595a6be544774b6aba2cb4895813e43652afbb26a094af8f0e4aa04c25dac4f6e1cc8fc05ddebf99418ee8516683898f5666871d5875d034d5f99242b4655e8bda64c284feebb171965470200204dfb792c482b2b0306df0c44293afb15b62f78b8526a1d7f85bfbc4e0b52219c8db36ee83c8ac404d83d286a31be243c513f689707694aeb0e0c026edc2524165e2ec895dd3c7761deb190441085bb21cb6e465f150d7fa0395aaff83a1d4bda17b5bbfe49a416360d8c046ac9d2100254b1e27ed77088e23d14e986fb4c4ec27a6c9310bd10e0970cac008fbf2b21cc5d85ec756f8bc00da39605fc688e970060974a1716be228e1e6c8cdde4d56130995c2641f3653335d7e1828f86f6f6f82b75dc15a42cb5cd9c38d7a904b15d1b91127250e0ca9d7543e2908d061fccf779650fb6fc6cd9cb13bdf4197d0908fb875edb9da576546063f926b4722c02371a962194c60eab80fb8ad25d92b7eaa710e8e7d3a6941b47c5364d5922a7cf1d5f114cc2b0a22537a6c0599c8813cc6255ea25f46b324e476315ef4c6dba2313d4d4ee24424dfc8eb58d8573e291241e4f168c8cd548a4e256005e133775a91c60bee038695511e84b970b472207edc206d65f4f43bb0502f473c4c69368a1509066984e7194a10940a08406ead2c71dac23eb3c4741edc05aa8c3cfb28df1202708a12cd206882b0c86a6a1b4d49d8a5458eff072ed1b683bffef9fddcc01c22743f2853051a66a341d01b6bbc1e3640842f1e66ef286370e51dfdff2f3fc172c55a5fc8708f54b88478cf32f46ba24f82ad441100380c685ac802f4a499eeab2a3ae80ae8eb807c604057d039a894d083851c2854f710ae1400e9a886de43e79366c159fa3a0d15a6f119aed810ee593542dd109798bee64adcaaf3764bad343aecc0d5c07002170c37857ec05d5258d465080048107c1d96339f681ae1f195d753165009ee6f443466c74892176aae7a7a857a06560c18e44a331c5fcf8d7522d57f31f5e99a8d14ad48a1af5fb9f184c719a82a92510d6324365274d743352c13ae57209d639eba16f411b5fe367acde723a4bdf8a2c16d7040ce7fdc3e0b7b291e3288d984d55b6e0fe89f0a62088f1d66da94b512cbf44b7d9cb1aa1707ec19e882d3b9a94606851953ad6a381d6205e27d96931dc6ec096a084e05d791b307e5082d21928b0cb8459495d6d84dbe2b0e74cc0544082047eb338850705a6f8e6470b1eba4d3d0269c1ee9d0dd30ca7c09470a0daece49ed54dceffa485654ca3e524fbf03cddbf21786c9921c0b3633c33e67a57bedc91727074787808f4d11861eb692accc7142b8a72752195bab98715c590fa78802e7760aabedd4900bd566262556ac44a63f9ac54483981fbefa2be1b5b1cd4d598f4cf60498265199de42fb0928a8f5c95a49ebaeef399190b95cb5f3305a79501a26d3356aba3af79890f7eaf595fa14e1927bbfeeb2187af7963da045a022f008aec1bef797aa43319ce791664af3864512c5b7984ba4ba0d7bec6af87612cb44fe320c09a904792fcb501cbb2886250c54198796d21f8c5e79c5f4e618885465ff3bcc65ce837e63d6b9ecba684e8a4d2bac2fcabaf3ae5f2aee34cfd9dec97d987df33d309564eb8750165938354614b1b038bafe64d3edb9bc9793a6dd60825e160fa44e048244d3cf549c0aed862572d8f7e9b11a5dc097df44ed4eb36b60a5369642c23744fdb3803714692cfa85e109823a7c13c8a53ae7462ca9b2aebb9ddeedc82541515a1eb96a7cf5284044b7d3308779df8d8350a88e8f2f46f487fae05f9ae0f4f71abd4b9753667828fb905bc1e1758bd1d9e981ebc6a831816a26e9cd99cbb0dcb5c6a7dc4cdb8bc4fb1a63ea18a9609746c8591f4f107e1de1cda511bc9aad465bae66a51b67a101d7e8db9ca5610c3cd92d716b00d2484f8f08e0d94fe12328f1376e21ebdfa63e712562aaeccfeaf90b3145c69424017b82999a76555b3eb38c6b87ed17582451ef49230ecb9f4eb31623677db9dd9fa415e60f123a8e6af3c716749a006756c4adb79965e69e54499812064284100998e6aa97b9127f4ef4fef23764db8b8e46f62a2e8c6178ae38a0602d9433f5ca84b6db458d0a9258f953d17816be5964f0e940824215b2ae7e78d6ea06d22d956949f1572c19c0c92b3737dce47cd098b3e3d243f8954c7f55db8a1f328fdf9f33812ffcd390f32299ec26fd4a05e6cc1a10cc9ef01b1c64b7317ed38741e893319e434f81e9f794297bef855b236f311f69845206c875e13e9d67715d0ee9db308c0a436715747d0cf284449c2ef0484e3ad709dfa2104f18096194e13123bad521084eec82bdddebac3cbab594b28c491e6a657d2b3bc87fee9565d3d912ba03a0f5231861393caebac5ece20d6790b7c060f4d07e140a6402e6c08e54b1e7f8494ab99828c90f212d94510d40758cfe1e76659448f3ce9b2f3b6ea5c8e468216457e3bd7a90fb6c21e585a08b08329e20b7612f2ebfb3bb2d62b050e9dd288da9e43d4814eb362fb8be3012e724abdd0fa4a40528345b5412fb664c1f24f856d3fe418281cd4cc2d173250066e463436fcc11123fb02ebf002fd148ed7fc70919deafe7d9365bcafe617caac4d447656df3e6b68a83680ac01617e6421a8a22b90be3ebe022605cc3636d5465daff4cec9d76b3fc6a85fe0472e2ea0fc0c90ad0e102f405b52090c754c033b83e3c81c9d80711c4d67a227b91c7ffe8a1f2cb0f240d88428cd3ee3c4a47aeb149e901c5421d32f90651b6e5b9b53da25556dc8357636a58da87c7be7bc8e64854724bcdf6ae620f70f95d92118d55fa3fb117ff3a106695673dd1352111e8be3f275cc4226aa09b587a40b214e5fa1c01c2b47e9bac47ac2451eb11b6f84fad5d9c518c70ea72b6816e0910ca3d72a050036472b4f31adfccd3d9d3811017110f880ee00cb4e7b3b3918728d9c6b9f8ba6e1c8839ea0657d6a7506d75530407c615af46ea4c3eda1a5b8eab68f2a07448226045e63a5219a43c623dcfe0883462415be4567bb6524bbc2a536e4594b7c228e325a901d5f206c31fbb6a663c04c658dec2f13483aa6f291b0ab6f0f3b6cdc22c249b24b819a6f84119b68f1dd3f17326fda1aac9474675edcd5587c16904cac1e710ca2d9756c229493933fc06cdda6c91aa26fc45ba91715d59d762a76239f3b7522a02086b122898b04db36469bb7badecffc34c31d3cd5651fca187ed61a761fdf9b44da3a2269fbb441e0a14b5980062663365b6bb8257f468b743ac3218ca2b75229bd6dbe30008656da54e2bea45adb4ef4d68820dc8de90737170179297c3221f520166b5094190c74e7859269753fcb36d9b4dcb01263c45c3138411e8ba7e06eda0b2b7ea1cabba616d4c2fde639475b285cf46c0c7006f22560b564b15f4a79644ddf52c5504fac81e76c3f52a5eb6dae0fdf2246458e7ba4f1dcab2e9e9c0d56bcab7828f1f616d66cf903807791408af94303ea6e2b1467fcc76bfa0e1152471009dbea6b09f76c6f7d42a232fdacdfc514d1dd5e4b2c7b11bcb9ab2fe9160d5350e3bf9a04c81b5890a9d07f618e46b64298522f7f31ad12f13502032bbe65fc98047f89d98848aeadb0d133d2d6fe629c14e45419ef51703d140b949a0281a5598f3ebc59fd04b4accc7207b89048043a3e5557f75b071199b0657e4f1132a642f18479f26f936741670fc183d71dd706d39243447fe1f496958466c56c1dca0ff10bfe62eb4d7844e1895b3a3584f25413b4558c180ca633107514c09da5d745ae64c45449698ce079d240ec7afea7c6a3ea1f39d4a32e6560b330ac656bf5ebf216cf479088e7a93fbfa96fd242bd873f0f8a4372698562b098958b3bfad58f5822e0fca379c1cf645e3578bf02ce7ebdee8e03c4729fea2f70a17b6bdd139237b8549d46abaca188772337f387f78338bcbf81386304605e4ea0a7af5a845acb4c8fb2bd52632635d775fa14f0e642d40cfe983882b05b08c0297bf05cb35c3fbdacc87136778ea561fd42c48fbe197671dc26fe8169a299398e053a68e37ed4c631bcadc3ec2eb46c9cb8af04e3398323ca6c28b3b4121aa06aa4a4b4352a1b965b4642c07a162782d546e2845f13520507bb69fdc11e645ef486bc011da011a03ff553c84046d0cf42b608f96420d3665bc4d7cdffd09648e7ac18b30aa723962efec4fdcb051b88c358da653baaaf5eb4029e48e1620adb086a58c43fdd852609fa434957065bc2a9f147076fe93c4e2e81cd6382e3803c5f085b054a8cbbd9c233495eb8d0308b9d5489297f3f271ef5e386134539f00e6aa9e5a5123864290759eafb6e49018950f7ccfee78ff5b56e4da0e46211e1675a6883135bee3107b742f93af106eeb022f2cb3b2c92804ddbe32e2dbcc061b90641774e7b351454330b1cb120639caf18fd8cd577356b8ef0fd84c09971dd727a0d55f21867f8c2895b023d71a3f15761a311e8a37abb3eb93b2fcd1793306f9e151805dce92fbcf63133411f3b33cbcca0f20bf2b8a68de82e42df33833b9acc2b80caa2203f6a95754280c2a1e05f69319301aba76835d4ad01668d9dd94e9eb648b25829e3dff78b6c13ec70510281caecaaf007ff266a7fb451819276972d4290ef93aa7ab11934d8c63be0e604d1e8e3aef101ce1041c07c1d139f7f753687c9ddad4d8ae5997db77fdc41fa8167b682ec3821bf33d405c779332fe814c864a621d247b90e347bf48fb87eb872c7c191146410429f018d694afae731c8c58c7ece6cb73bea1a94933720d2d3bc3ab7d4b754520ce4cd46ab0865c66d57275f8c61a3a3ab5a36529c40430e779c337015aa10d8273703431439a7c3bea12b5e78138f4b83fed35b4b25f6859c6eaa2c03c1d9a3decf053228b876cba541751b504e765512a3d874be6ccfa2f1ba807d4e15e8483ab3041110f99213fcce3a0ff919a99d2e86815333c9d61d2925984a2e89021a3b29e0e7c110082f2ae29666efcd8ea5ca11e27105889fed945fb6e65a18694efa8d6e98027998c66cfcb82dff84dfb000598e3c7724bcbdb0494c8d8b097d19b44d5556a892a6d7ad89c8ee0e7de702ffe9befe6a948357aac14aceb0aae4dc8d8eb87a47731b2f5c765244c54a981f62b9f892e09145dc57d0307bd307a600ae79744fdf3dd092e51564304398ede22f7c20817ee2c4071eb0884917db27a8d2a9f9cc6f0f2e82e6900e1b6c74ecd26140d765841fa34d6d87cf44a7ec40baf48bf31f44c99c9904470ceb05474e23ae4bc0a28ad060fafdcfa504b3373d992cf6f00e78dab93558ed75db20e2ae1f5e26e8c46692be5db179f021eb21335e77c95ee5f66d763a0aa385b96d675f612563837e5211f2a7ae36851396850967f368d64051eeeac5f24fba2c31b3588d31d2da37f73b1ba7470048ba366e0b86c09dcbd108d95cefd4749e5c99250ac8d85ab8a99641ac59c23bbe85cc4cf53c963d73588e78460ff8f217ed133a7d5bba49cf88c4e1529e38231e4173f13065f5863ae0069daeb3378160c7ca2bb47d4d0fcc9c1a1e199af0ff56dfe6843f7f1ead4758096c51af8df81c013655e3e30312f671aacdfb2068538a90164271188000f65fe0211a0677aef458a89aa578632ed925aede5f4f56447bcfc5ad69f0f8e676d0f9fa31c0d7844ca41086b9ddfeba44bf77ae8c7da0fd8df7cc80b7ac69fe3f495a2c454c429a5689491fb473c4e519ad20b8bab134ce819f3130b948087fa68b1764b4875546fa0fbbeda101497eb4f92d0a70e034102e62a9cceb1562474ac3e0c66b7111d797f48299a5112da4208d64c4c7d115b536a73cbf76f3cfce21c623db2ecef2084fb4d1bfd637a302dea994d6a2326a28ebb433f1ad34cbb9c4bebc1a31498ef6929ddd6a4a3d4af14fadd93c4207b77b273fc7d423bff3785153eaafb94666cbb0b24f9aeb50bfaed7bbb4f34ab0b56707e650d231ad8b9c47ce91e600e2403f11aef3a89a9f0a42cef3d0075b95ccd6e8d5b5bac367a57b59e7c09ceff359435105935065f30a63e8f00c448a92d01df58e5051aefaa50f192890eae4792d71c6211d603e44ba67380a57feb424810879a25250f2b81f62c028a124db1a9437776cc941e152f80f39f315f0c22b9ce998e73807e68eccd8c1258d3818586f0c0c223dd82934355208aadd96804102f9c261630ea5fc8666a23fda2618fd85f182c0b35d230309c48f01c6ab63874263c6b4764b7c38cdacf7672ad6e892f08fc118292a785418abf6e5c339b12e83624bafb424e4708720bcfb29d96dcd82a5fd4f21bbd070abf014df914ca09ae00b299c1bb552e0440a6f0cbb64852ca014b1a340e1f2372b817e5372509fc645b7e3d9caf8ae5c2e3b8a4520b78ca313c8f6e4a5c6c8e98a14fc7a844a051fd5185385dd8ef86288a496c2a07703519b1349d967507818a78c72cf37edb9678086a8becbdb9ec5e4c9920950e9433c6a03ce643ef9fde93df34d5dac45c61860454a8a2a44d3a5b81364d21495dea93cc9c382d3cb2a5011a48e27bb7742efa12628f2ef70a8daf58ae8fd5f67fe40982c9df97aca360f6380b8264582daaa222bc7d12c24dba7528546a656eba88d72c641bfe5dc0ddd225aecbf2b51a138bf5eba703ff28ec19f23ab4cefa7429cb29ec91ec52403a0f3cf76a0cbb0bf0d5cfe5a8abcd79e3f421c21f1f0d2f8a8f2bd5303833788256b45cfb6e75f473cc12a61a9e6cd919900e056b3dc09bade3c6bc18ff8be58870e030b8e8da66bd7fbb5d27723041df6f1c4086aef30b4fe4191f270ccd6dd2a95e3dc4081f807364ad1c153d542cf8ce3d479113b8a1d07de93fb60e81411ee11043680b0781dbbc85859cf2ed8dffbae908e803b57fc63af13b2b511a7874e5445f8f5e5af844147393a2063de4f1b4704aadbdc94fef5192b0a877c8e124f397f75f16995d63ea8f0896b439321b5e3684c60bc2dc0f33f139ef2f680294c0e19ea2826296c46b1829e4ae423e072b66af08dbe029d5f2ee7b1a70fd2585d68f528638078d03517dd6084899ab9e4cb7ae6800da54f21ba9ed3e842fbc090508c7d623469e2aeb418869b406fa7270d1d66336f793b83024f0e46b9c557b97107a0da1b071714d3953689a3e0dccdc9ac2a52baaccbdf634c697019b635ec0c48614f141c12ff89324f3310b284c94788a20ec31ba371d01cb68dc8324accd779659c0b7526e453386155e2ef31d10b3f25fad0c20e692eb71096f1ff0dc5f34c649110bfd8de66b3833329b359d92320181b4db5915800a92e9dd6f3ef7dc4de363af968c9bfb2e22662ab84d42ef34b0672ed0caeee45ceae6462ce2ba206ecb46073242c10b5d896b1f83d81734e8401183ee06e0ab668fccd96802611a330525868d5c4cac6b1b9c84de135ccfbac0ffbbd3b9d4768f588b7b1061e098c7846fc1980eec9624fcd8cd6a8ebaac1d8b6f2fc1ee32c8f5359f920fd9dd628083f2c99040823b5945e9606fa6611ff40fb95b32f7584c808fd055f40cc42a47c2c4c05af8b34eb2b61166577ee7ec3d5c5964118c95824b5b51b1340945874d5496501453a7dd9272b7b54ed6d2afe60458c5b019efba0856b1a907bf640b6cace27ae7a25bb56d1e7f32189527b08a3dd38b371a793bcf9e86a40787bd96c19e2f0d4693f48f5c654f85aafa5d9a3fc8da48fa07392e684688477a8bf28e068a3d58276dd767b731189b43fd59f9f60b39eb97619ef3918146fa8492640ece28f1f1dd3bdd117abe7f17179f2f5ec6e8ff6087b3991ba1ff828631bcfb5513c6db209107a20228d0136a8aaa97410a1b6f5e0da323a41e7562b6863ceb73b1e749889933a2abfbc9e05cdd1b647d07271789d36ec29021d7afa523214920ab20b0b3f62947fe113dfcc6c838ec3659218b211945033a24170c55d1f138c075f4fa791f0cfd5233d12240f707b5d2473543d3eb5f2e00a1e2a1ac44404e882101f47e901fc3313ac0ac9cb70358e5cde3fcee4abf6ecf9ae64a1c1357d29f7bc34fd5c0ca71f0636064c344199825468d0f5cf1528a27c0462003428c94736111a192fbfcb3e888c5d6eb8aeb035494c946e739e8fe4b8fee0619dd5ef1b8c605605c90eedf181bee0978361a233e13208391d829d57cea0e22b97c341a18285444a796facb826f4048a82dd28b2f6a5f0aeee0c05d62e580902d51449ad9f7409aeef849b16be8ec204b811e09a2a8b6fe17912620834729d08d9e1121050e0549104dd4310a873b885344f17be77d261e85613a005170158ff206065ac6143d8889634b3794da690955d5e97ad0cec49f7efd8f53ac2ebe45fc9341477b5c0c4c60d21aaf5668a141c6de44f68071c7b768bce754d102de797aab42664bc742d4cdd788ef7a655be947fa7168b473bea5d3a0592523b039b66c014922a266c7f051087047fb68c62bed676de7e4e3e88b6aea96dfd97c31d46da3004604499cd528ae57e4207b2f1df70680d85a2f510f8580bf85c4f713271febdc96d1aa27091b2d3948b42041af4437f42e8bc0b72b2219fc26d1abddbb7207c7ac18caaf16aa8bf854ddf05837d8521850f97113a9661556bf59ad46309e13e4eeaff4302a868475f209218bb03c121646150bfc4016670333b14d98e1cd02ce9d80d25d0db37201a5cbe5c8caeeb1c53b592ae4a5bbfd3a485541e1bcf93c220f4ff335376766dca91673245346e07a2d6a5c27c85cab9c05dfd43489e2123365d11c1b27222c538084e8e7c1d1c923667b3c4c48b2b4b732595f4ca13e23194f5ea9e0fed6d6a0222b25065bb906e6c5c9cf233e5191ed15ebe80a1df4efaae9211fb612ba59d328e71ea38fa81a703c5cc34412c2ebb4aa100b4e0408e6ff13f6e1b68c233800d1781a03dabc694fcd3a9b1dc64cb086d438b17e59b50fba17608ece1af052488d85ce062057815abd1713fb9b8eeb7b75cea8483f861174fff66d24467ac34c5967afb4ec9a8ff6ca09c9fe22bb058d7366d02a4ea98ccf44883b7a684d6ea9de5caa267001f9f2864606dd61fafd2a1cefda5c32e5fab9c7619d9441361f1f84aaec038aabdb8294707ff66af245a698e8f92ebd56461ef04a2f304956add347cce0588014e4084640f70edf3fd673da188700043461339433ab9c22d8e24e300969f1042f9974a97040a3aa35da1a6cec2a42d6ab8ec51d51b9a5645aa654ae43f9fff634752885e05fa2b905dfa5924a5c7e5fec08000db5107862b1dbb89260cb793221b4b1c43e902f2ac434f2f2436e1f578aa21a683bf07e7bb6f06ff42282b4ee5f14eaf24cc7998c24d1a34636a2a8c162340356ff849798a0c1c0c942645fd98d00d5fb6044cfa91611d43b84b83e295d57b3e6fa05fb9c357a31c1adfb1f15f985ad3df73be056ef6a6f2abee02ec541ac160b63cf52fc0bda7f034412cbf731f7c0a86a4b45a83c5f45f5f4aef407c2573b686e3ab4287c9e7e48c147b73c670e3c42a0e2170dbcf33d957f3e137e8f31e1954fee7221b0258fb8278f3bfa8bca69726fad3a10539eaa9982b36ee32b66affad9053abcecb25488fc294c26c01a69a6277830cbf12a5fa88a3804ae6c726d1f0ce4ba8b557631a72f0de95401b2eec8d95b155ee8f45b0e2ccfc19ebab673802b0f75069cbed0e55139f1ff4ff6ccb1028b359c462f608a0ccb25bfd22d287944e8fd9dcfbaaf7097ca9406eacf81751148d1c8b8d4fa16d2f706a0122785fe0a40d5aacde1015cd4b10828738f0711708c8e537c8eb2c981ec090a8d98e53dba5946d2404688da265474ffa0d6a76fccc540ad51a81b67bcf338e737630d12e0d11b081e5ff886823bc26180bbfc1822390149b988ab7166e5de0c31f6d903d98d2833ceb294b64e0eb63795b18a1a9aa330e7ac5cd61db778948bd64378c7da8d76ba5034ee0ec73aca6583ef0a015acd4a637828488f52a48e0928d49d85b07739be114193388334385aa3f5653a16e39ab31d97a7898024023aab3a450e7edaf37119389e5a4c1fb88c00c5199430260ab8df08ec1989b5bb2546d46cdc5ab7c0448ec24b3a5115b45bcfc930e34008c3b5a719f010e2cab2d17a0baa66aefe49cc2478da17567a17483c816027198849b3478720faa3c5377156e11113d0fa1af23cdbd04317033cb653a756ac848780eed288592c9cc96a100fc24d560ca71940ecdf4ed17aa2640f63915b4a80ab3f46177a04cdfda01748f171af39502770eed2ec78f1efe9eb598f15e5f2a57382c52cac5348d454e1bce11e5f760b54e7fd3d3221a2fbe5d13f01c1be7a40a88788a09294376fd402d1ed83214a603813369a00591e45886e0431425f9633567b2572df86ae4050c3ebdb7fdb4cf88076c234ca487f95970e04b18b2824525142815a3b1408d0a22c7c80c6ee840486dfbe79eb9acb3976cb853bce845a1991b20a50f83b8bf0165b462c2237343080fb3ed19ef55001cb1877de2fe4c44f39f45d9df42c7d08d54ab7a5cece833cc60735db33425303cb500a6a2c5e9b398aaa84af8f19bebccaa4abc3f5139c00e222e3c0e9a26b2c085f5b7fdae1c5359b0b68cad053e1c29380bdbfde4c48cd12a8351ee482bd3b871ac88387c5575a9039497584cdee070a702c925fb531fa0f7ad90c0c76fc12e9e476f4751dec502d68c5d01acf61c24aef693413659148a1841636429d91194466cc8dad0990d0214348eb8e1999ceeb3189222f793aa0c1efe8403ca2f58226a03c92b5ea95e37b8325c00925015c86389e8ccfeb24844887206319c35c379093d0915fa6146ba153f4a6d13bc986f5a0a864b8f42c87ab3355c462e8568e40aae7b7e485e5a33f43e9d703982673f65ea9694e65a4d9295796f80d19e8db4a8f452a9504918b3ce718be16e88050e2ecd43108e8899c6574347d5b886e8a3c00c0ffeec3da05a130e330072e2615b117710c87c9174a6e7762901dc7fc5292003f6df4984b5f618dc51b4146fe1d058109a4be390647ff8c806c23be839263687cde5590c509d00bbe8ff3fab43759137c8e636dfe91fb2c99b25bdb269bdaccfffefcd1a46557291ac86bc95449cca94110d3fd9e76fbf4b6111ed28307abbd4b4dc8cae4d286f2bda4023b4795a8d24dc29e5cb572e51040406fde2ce31355b32040f216057ae93e6fa9769ffc959b7ff78931de61bf911a585a4fd4fb24c05f631162f0ecd78b91c4c298c592df46fdf34bec623ed009803cc3ad40b91369f0306e514a82cc93ab0473c184e1ad2e60b1d52b3938b0332340b62e778d4920926ecfe74057659103721eee3ad6963c480c15d7f3ffb6447dec2cdfe8992c1913744bd4f1fb89232bd7f777943661bd113eb744abc7aa009fb1ba256af33e73ad3c76d81265d096601151cf2ed5129d7757f33dd90fb40e93a99a04be9827c081ec144a3e2aec0303d93a6d1fe4c78d5c3613b86ebad3d4eb0b8d70dfb0af0acbb2c629b359c01745d9f1592c13b27b472e774311df0108130ca3df81249eb36dd2b83ee21cbe7c7183409e5f6e0211a667172558b8d373525cd502e398fb433db5be15543fe043ee3c0dd045c62d9563aa7241ae770a09e0e4f7453cf1fb175af06030c0b36029cbf140ad0effa78fdf58c6b2db34e4e394331b9b0acf5fb8e36b6681abeec871ebf9653ab23775f41b4125357b43613a29654607a95c5696176e2e256a16265dfadbfbb50a5ea7b8f4c044f7520dddc6613f03fa16bb448848ae7f87a4664dbc19488b29ca5807223218d3dcfe84129491bcedb626542de7602e90e891a1d69c70c0885b0802660df92efabdd8061452a98127743882dafe46e65ec0c0a4eb75df314104f878316846f89f7f4003ca36d1a518b22ad590d12def2667819a7d3c984572afcccd7d4f284b394c15f895da30da3d86028d3e32732a28867eb2fef2540eae45090f1c34e0d1423fdce7fb3b6efd8487ed01502ed0c159a23294168a1da0b7040aee044da21fddec8a236a51bd1be83aa7df6a44c48d9c34be4df13bd9d737e97a30102ef913afa684d52274fc46dd1b78fd2357c4306fbae047f98f090eb7d2995788cc80f05b3168bc89b1ace9fc9c409d131b0b1562583cea177cfa3c8b7b475a6e8e7b377e0c82b08c9bab3d0c981ec6289671b31f41e375fd758d2fc983d9731194aaf1b034e93c0406bb2aee29c22a5c0ea849acd34a468787c56d3380100ced2f427dc4cc403864593044fab9e6468366c3a05f8e6f4219a23e95ae2f30c156325117e8d4c04526a5222a5cb11a78513bedf21cf35f275d8407142c81d7e1922eca26786723e00274f074f318312807e2e81845f4ac88ee3b00ef4233389e2d3f419a87ef6cd2b2d150e9df85fb2e248078927d76bcc31f612fad331519451daa8070d261163fefd4f0f375e4e4f24cf703e9d9531fd90e0b5e385af6ff6931533761b8b4d6828878c15f4b33ac21724e32b2002486b2cd988f6cac379507911ebad42404bc466322e64b9d92ada2380e779a63570b84d9ab4a804037e6ff3001a83d112be89eb401fefd530965343eec29fe29c03dadd27e68dc49c59d13efe0494942c16891f5a3d93913652a9f16b60bb81590a49e1f59f43f4ed8df0e194d680d4f34abdbedbd431cd2ceb68d3c2309cb2da15d5ba36147e46c91544258f6a99b8a4ea436b11e0a4f9da8620a0b9139e0104425c7d9530a5cd0e6ede9f633e2b3f727c081fea75214f7058ab32c11f8364681215eb076b42893945e6857b78767ca1da076e414125408c097a29d70c37bd1d47572fdc6c2114a4a0a155c39c3c532312038ec2e539c8295eb83390f2e607d02987e657269a311f2d49f9b576c73f93214b9e0149e1cbb368c368699a53cc2f836c20e85b4d151ce27f37b5faab87e88205e2dc112eb719b95ff22d751c364bda029170e39ca46d51f67150dedd1f4c00205eabde3621e407df05d75ff2ac545ca23a6ad6abe0aa367412fe48aead62ebd33345f8117a6289139ab1454887873be900dc5deba28443d02e0909747635e83298ddd05e6dcdf0c1a7b0c60028cb897c8b3109214a420491ed37bbcd735bad9e36d81b21081971109cec4b19a3b7b6387e2d789afc586c9c1cbf4e9b7eb7c0a121096bf00f8a98463e8ab90229edfab7da07e4cf1c946aaea0765d7b4cd4210f6c630f2da21f91673efc33aaffc4b2b2224f779e0deccaa6f15abd942039829e73dae5dd86c6990ff05fe5fbc81d220522445a4d01f577e680325c40e216959ef24a1550209717e1e8207aff841233b70de39d1de173e074f502b1f0d3c5683d3ef28cdb1d0ad1a72649bdc63be4b163ced72c7725aed4dc2edadf4af29876d1b5459ecbd07e4804399f2375db78f010ca6b33e82f82435000c98eb60c64fd3bd643d8753d420a32d605651e6bf0d349d15884553ccd9c603acab12b1ebf8519b61ebc43478680e73fbb7e32f13c574b9f25917630ca8d0a6c0c6709e32ef6d3214e521d7bcd2ef9180e2a615bbd453c190d965d33e93422908c9fcb9e14133c203f71503ffd3c18a62cec69570f68330a9fbb5bee229f15948c508d9e26055448a61fcab54b0b1e2736fa90135dc5544451ca7c58c080620018c15e705ec7620295e12ba10d11b7146ae2d74aebc149fc6a8438e49bc12583f2ee5bb5b90e89790a2bef639ff72c4140ccd1823f0768aa1e3ca457a83415bb848889626ba17e710dc3b8ad214cec7d786b936119790e29909312fb8610f28deb7f69edb54a504378a5598e4b7f1327348a452d6b76da4b7a8e90dfd9dea445ae25f110223b64a97174a17da47179e01861bdb6ed9177e18a5c73f82802bd3faef09fbc6b94adcc00189617f3a068a7d25e7c6ac066402585066d67e693712e049cf96e82dc30f0f9971c78d872700de4410594790dab8399c03d7407b63f94cb3026844038f46c06fd10e54286cf5b4110acbf83f7f8ef033b2f2547faf2d331ea9e77702e71d3e8cecddf148a5a492f5584b5c2889666fa29f815c90045fa57de148ceb8bebd54e994eac00364c881a20b1854be4eaaac7d819df6b0ab117ad054da003f3a637ac790c6bf17cae08e363c580da52ff7580eb4c0462d8d36b2f8f2636fdd23b5457b37f2aab393e7d72ea5f1d881e6867304c1e1ac06dfbf5f002427ebf9df3639f8744b72ece811e3c869f07567051bc8041c5380e94f30300ace17cec8bd3034c270fa9ff54f0f1707fd607897b09f159c685d97427123327b8da5cd25b2f0773985b3121e14babdc2d1364ce1f960c00119d6de2c2e05ac6313289b000472b2fadd898f4b59dc1bc46abea4b994f0963d55b4a60db298688271df115533a3a80021e2de8e1108700794d23ca610fc0459760d4ac359f626f2a9d6b7f64d59366815907016d5353eb67ed3a07c1762066bcb8751f26177af193910e9ae85156f2f024dc8025a588e0d3b631a36f0d9804a76ac7880283cbed449740247f958df590120a62706d9e3be3a32ee5600c8f205c25ddc8b8102e2abbf1f8703aa99207aff03702bdf5ba3eda738bf16ab60049a9b750cd1b98400d318619451d28b5ebcd212cfda41e7b740cbf277448ebf7a52fa440062a54bc8a356b002e49574fbd928d470f5b1b7d9cb3aec6d8326fe64d085576c32103828a5d223a8407172b67bf3f0176bf126f767fb4140b95ca15e78401c4ddbf1406db3083367bd6fdb060bfb268ab91338ef4ad842c2e797549e4e95105fa62ef2230a6229baafc64c135801743b1fdf64c2703bca467bacdae03d805d93b2d607dbd3d3ffe06396a32bed86e84bb06cca07afbbf434093853aa50e88419f93cf0ab5dc7ee7f6254f0d80d439666b16d0afeb3f40a19f8a9ab7563ae842ee7d1e1ed0ff13dc9912332f4373435219dffa6e0c378ee54b9584bb16b70a16a753c6e05ff24d01177a2a84777ff5bcd346e1a3a289395fef86369e662832b9980c9804cc5f1719fef52ff6eff5c446025efc3693d127eb1ee6f055d111a20cab233f43a0fa87cb88bb5971708e27d3a7176d2cc44a07ad2f9d86a94f9b0a20de214ef4d1f630c9e1b731ee7639f6d9f53e3ef714880bbb50d59be00779baea1530d48dd8a3f161a5b0461caa3cc02f9585bd0b69009aca34bf8f9f8c349a2e8d74803fc7d139254023839fb1deead10822a95a919f4c1a41de18f29208d0768bc39678a96bc89871c64a262546283b79032c84ea0d879389960ad13dbe138c86897c0364623b356bed52fecf23df6823177dd5b000b57f53aff88e00b4b261a8ef8425621a9e489340e80a23255f9681d4f6284ff24d1cf962c9236203a72adb0745e825448bb7ebf7d62699ce3861b43866aaff90d058a1bd14f3750130426c89152c74aae0ba2e49b8a3d79c7d318beb234321b2b5008068b191596a24b32ec7385fe9147e8434b7009c0826b2231e2a1404e1c8590bf8254f47fe1f506a18b6e5ca912808759883de3bc51cc8211b982d5f78e0c117fa4d80f08664404fd1df6b840d41298ab0fdb807c3d8d592a3a111a74ea9a96e4ef5a9956c82bc95c94604c332db1d810f7b2f36a278bc89f052d705513c9ba052d6d07393a782274776a79df730ce804b4069bf174341529b25820dac0a27e919055bb6b7af74363964508d3769f9e49dbd90d42184a679d7899c2f9c7eb0a1bb62df46f995f1cdccdbb5f8beaed780d5a03e9ba618b0554d2c9f06f3b400367400bd46f1dd951b90d64b7ad970a76cef21c8381eec4abaa2557c64eccfe012e68944044109443eae364c720652281170157103e4aad40fa62ee0d8d83fa03299b915a3cc47c12f30a9e0ea27c992234773b0e77381bbaef066c31c789f3cedda8bcb85d1ccfae635aa8536ae0c41081c8708e848d6560b577e8190d7be124c43be19a70a39a15f5012b35cefd1d786acee1ba6fbb15a83a05649d2b66470455377f3e24d10bfea9def41492352f8de019f070f5013e9dbd561cee1c1f672b1778825922d50425003c9b73d078416f7e9dc53afd820d93aca071d16dbe47b410134636352faa14ab5251752afba521af55348fe48cd4f0c7670704ced4175e001cf1256c7c348038a0a54bed0103ea16fb0a1010547185d66f53a2a760b8faddee8245e9896dca9080dc949dea0c9f578f43c508e22e557cba25968178a4fa89a020b989d0b8c1e8988e08e8334858e03f7c768c875a008dc1e159e18649d2a9acf83d73b6375e86a53109dfdb6ad023847cad77f6d594f8b306c2e5891dd506c958d23e57a7b3d36b4cd38fb1d4a69c6d205b79cc4d9093a0c1d9cea3a0cf20c87b0647b020debd423cd4fcad782d3afeaf36999adf8865be45514fe4af006bee5ccd26f6470afbaeb977bc37976716855d5e317e96c4e9a42e0ea292f45fb141d00308c708bc7b1e7a8da9ed6e58e9f799c1391b70ba140c23fd59eb0be547ff998343598167a5d7a984569a6ca25a455f71a27b7149fc539ee791060b37a61539a0aead5ec5a284c2540b6cf173eadefbea40202959b804e3d725a21273052105608fe772fb5d1c4d40e865db1d72eb0fe578ff5325011b66a67048eb3e4627724bb961afe010dc39bbf1a89eb41f286e1d7bbb6a418129cc41b560a01478468303fd0123849b5935c591c1648632a098dd1818e880439c9e54dbad95c127913a375749691fc49454b3d9aa34992c9f8899c816c1cee114abc0d22c846b2562e4e03f6fd347109219520f1349786838855fcc592dc57be637c815e0486f806ce67dc5d5c70d314f577549e4de8bb9771328ad9f8a8e614de4849c0242519315b4de94295d83958da82ead4f39ee063bcd6c4acb828e11de7408e71c0c03f4d68870ff3d9f85032065158e3a8408811417e3ae20f6ca933559dd3b9ab1bcb5bbb83b81df00263ebfb0b5d29241bd32ce9b74f72985f032e59841b25b8a7a1c0e0faecd08711dc0033de1d28814426c3968f36cfb4a11c5f2a656e7f7c79f2015308eeb916035bc5a33de0909e301140390470643ce275c40c0d2c226bbad08b8c0df8a8937fcf03089a95aecc0279a2f5d2aaf3e5c7ca21e3bab20b413e0a74552973212083708423b79a90b02a697b44b5d96c0a92a4ff2c423d032c74c4fe2314e46786b03d520ceb4227fb1910d9510864383c2cc5169a0db874de643985dc9be30cd9fe6756c21ba351994abd152e802ac55e07d9a15c64884528a6a57e5688e2ec1918926c8670af155e611f199d26d85030cd1c530dc6c057cf855fd3acb429f943c9bf4ea458dba5792442b666f3196bd38c3e2110c72b19d16e4c22671dcbdd9bd98cd5b54aa560cc14a8e4e8b915c5edefdae963a546275268fd50a19628ca8f4102cf60e79cb4183d6001d083e976ecd3348955c358346b06fd9b571b210f757bc89374e8b20fb37dfeb00955063bdb1c7b205fe560b67a67f135fc849644ee8bc767f0d9c88058239641ebc26c2050a793a6ba7dab40370b428ceff451d0895e6533c791882f9362957ce83191483b4df352e2519e9afbf3231213d8b68c341d11263cf73e417db176237e6a46bb0d8946c6d7b4c0309d4a2432796e1e43e7813e00d6fb2952b9713dd91028a821794733ca01f04b5d459fee94204b2c3f7f972708ea2244b9bd211ac1488275a2d370c780e3e41c0299db388d07476ca9f6ac308ba6f9d7a369ea5c02f3e28d46125085364e1862a20e82da591831c9343f1fcdc2e7685f21602691fffe6e3b0491258e5f931b212128fdc346d3c1d461293c3980e31e4b0c9d8c62675490d1d956a78dd05718580c3424267d5420ca63f62441cfcc69379a79066c53053747b936e1d19cb3ada8f1a47cc453a63d28e670b1d507532d497d968b441d19766641af17fb5d82fe437774bc2ff62e551f2917e307961306f89ba5bd2a474c3cf7eb04f1e4b6e8fa073af4e7e5e7d90a666687bbbd8b61d26eb46b7e1f10e3b4c58527081c162e3346d5c56b16d9232742ca116763e75a249f2505b72fe99bea1e3d5c39c247b6a4b0801ab24f4ce6652abeb7b0ed2e06526d2d2272c70e388766a89ba8c83e3b65859b551bfbcd8af177c62020ab8195b5f400e3aa2363acc36a04fd57e7d32e4cda7fe012ca22243e04e95bd63d3cdc99e9176ad479150d869db800e34949386d508288bcd44d863c01e513bdbfc51750e32351cb91cd483866e111b220bf65914a08aaaa2fadd9f484a22422bbbbb77bd059105a905a48f41c4bf87bfc1077c1adc1a74000779ba4e7abde5de7bef4973487a9e9c79bc4e7afdc9f9e52f243d0f09858261033f61845fa72e1f1447af8f73a45da6ba4c975148b8339923dfa67fb4fa50922e9954a9f5175d32a9b2435761da7c747e0d8306de23462677f2d36edef2d3a0bac7557295e0d725f9f4fa3a46a6a4df99dca111e18a9e5123fe4947a05f27bd644ec77f640e0c171f49396d83b1c91dfb9546f548b91ea5676b51e345d2a9a4ab3ee2a719217ff71d2af77184ee848cea50772673286ab4516e32c7766ae1a251086efd797625bb616153433323738a817931f151086efd79b9c3d7762d9712898f4270ebcfcb1dbe964602980db2b21acaf521c964080a58511c044a61d870eea40ce1af22a48fa97e2f932a3a984e1d58d1435c1ff957251c6061768217e0480243419095d6501313b7f967ae735a40d3f0c83e5134543e953f512bf415e654807ed942ea7029658c0380e2620fb22a06265e4c91c18b5b133938e92327410dfaf8bc88137de42a1da0e8e3bfb04205caa562032b7164ffaa2367ff38292a38e8e383900229415414c1b0d92c714430b4d0a30a0f1b58f65685945cc590e753c50d3aa6307ca8b0a0835d32a112c319bbda43eae41f1cc8c9e787c86a845696dca94ca40e8d999319945994d99419160a4481489d0d5417f4b30f1fd2658e1a1902c94f974f5d138acc51f894d3c44271c85a8ac92b3921e143f49b912e7f0e913af33581cc1f3d5e13c70b4b977f01a9435fb014e40efe5b0aca3b3931e14a6a1284948b174f3205903ab45667c89d6fd69f1ad4e9d76ab3f46f5d529f74fab8d63adb7595ce6e15c415438bd51343abeb56413a7dcff5b57a62e8f4770bb42058e9046747f696d9269d7e688146b34e9f1fb14b3a7d92fd29d952a9d259ea59716e713e3afd12cb6565739dbec90ef574fa2f2b180b0353e98451c5f8786d2eb0249dfecb0c461643e448a77f92c95819994aa74c0eafa4d506d9fbfdcb7e51a1a8d38fc9d1d4e9de3a7d991b347371349686a6d24993c30b13f3f5daaaae5a61355665466017109dbe4cacce489126480d4cda581b9b4aa74d0eefcbc8ec2aea5eabb5d65b2d4d15777f7d3628369bcd66b35d9c6d6c7615c17ebbd55a2bb641b1d96c36dbcd9e066f6e7615c33e6b6ec5621dfe705370381c0e87dbe088e4228abb8aa3be815cadb5e60ee3a6e070381c0e271b422693c9643252c904739a69d162d3a8124aa7514e58e8cab341b1d96c365b8771381c0e271b422693c96477b55aad62b62ac64f33352c1e55597287d4e5eb0e368dd96c369b61d72c2773326793b9582c168bc52eec769bb7799b37dcbcbd5eafd7eb25a34f6bb4466bb4466bb4263fa0b50f3e48a5fe254a8559b19391d7fa41aff3c78e85816017c7ae117685d80562d7c6ae4e1f6bdbb25d28369bcd66f3aaecc2ee900b0b2f0cbcb07d61f5c2f48575fa36531b4eee745370381c0ed751964ceee021ac6c5b59b5326d659e956559a74ff18cd995c5615776655776655776d5e9cfefa72a913a9889e791483232a28851a30b17285c7f3afd8bc24032873e0c183806aaf5c942b2b0d6eb6c2ab0408bf4442bc716e34a27cea19c31d07befbdf75e8131c638a6780207951a3cf91162120afaf2bd42b57e6f9d3dee65752eb82c10b1ad198e4801c50e285a86783af854fc830cb3258c9032031aac0aa8f4ece0c305fac2840531248722846a0092831c42d2c5f7de7befbdf7de7befbdf7de7b73ec761d2ee2e3853c7d64de01638cb15015b288b613988d80065a90c82141c207470500f7defba4881e6428024bcc85a2d78d0e433730810e970e4670a040b845654d31a2c1d9cd129a7001c105b9820a223754d144b883bd3eb84a708354cf05530bb9f75e57096e902a23b7849327a09019b1c5864e888824eba402c348c8f4865e40b0008a233a58b04fa6108522e08a807030a2090d4282803b4c9c690d51f04089a8fd48020742d050c4051a703168c143dd928b541b5346b0f2210724c5112038d41cba1ecf624a6b9d7203d2f45c35f3525c2103a94bda6a04300840875d57acfae3a4ae80e18a1c54052a2195904a4825a4125209a98454422a2195904a4825445f535145850d0b835dd00676b9462a2a5454d86c2a2af87c19ce8ac23768d5147cacac3e7fac2c0e0434a22e576803270cb6254ecf504dc1274bee7894dada0d7034edcdb99d0e389d6a6ae8b4ff54d23f9dea8dc963677571295649e1a305631071fa7482372447719a659e5e485f8237e8ad41df9d6b703cb8f69a4cdd62caa5951cb4614938289c71e733ef8aeab9779dda4ead2a083e9a28efeefc401bd79b99f39b394dcd1bb34b2f13dd3a05b2702b4ae059f12a62c5ca8a96152558a263ec1232220b21d14de101dc12bbac221e84eb1f2778140e856f1162b7c0b66459910f7bce49695193a29f6dadbd570b5017d4755dce454545547cdfa7f59618b6f8d822e45a1cd28c908a80a7021657d1c08b64e8fe715245418ad0a1a900ba89e6f941975ba7ebd1a123058d25a593c284f5a7b85d7890ea13e499e9f5ad0d1841f44f35230a15473737652ac3459733bd3eed6305684d81ae3fbffa9842fa7cfaa2d5555b7dbef55183ccca0aaaf6f2e04ecc9ef5bc4aa787bb9c79bcaf27059933dffbf44c6f906783e1c88e4695ced106c35c380279469c34e2a418a904f2945c4c2ff6e5a5d2f9526ae222a5cf37dd4c2fb6975a9f0fd38406f54989d02014081aa32f1a84ba86828a72b85b10ce7664124932935d22b2099b3d7268fa98ac3b345729c82521f43979e60c399a68a5f6d26a2fce36e74a67c65deebc6fdbbd2b9dfbd35b8321b79c573a7938e22352c9c5644da64aa7a9e46272016fbcc0bcec98933d9d2a9da793cc8c89a6a6664bd1868565c1a2d2c92266cfd3498a3433bb66da11faa8815eff06a9575465c99c89925f717dbe5c814e0fa0d0923ee1b8e3d50c2184bc291f6b3e95677dac89f5d1834eb500f531460cb44c9973525ab42a6a15c9c0aae96060b3e44780e0839b55107c82d00f828f057e9810c283680a1035e07e589c0c8fb54ca1c596811800ce8b20fee20a8e85e5752f86f8f76388ac870be12ff0f179e023e05998b051bcb0823be155701ccff97073974c7227a85274351d1114d9c3419889ac741d5151c65c5ee9c118638cf17d01e32b2e8c2556e9e02357cdc0af00d91cae34210114443820b2425474a5e7852baeaeebba9caff858bdbfeffbf49e576657ae00f56b833b79d09ee44674be813fc1380a7c6dceb7bb75d674ba42b8da9cffe6db59b1925526e03870a225fa111e11baeed701a100f28574517564a5d300f82477f28b3de0b3f035f416a524407e1bdf5711772bf6003e1669d49829fa5984c08de98282a80840d1cd4ee8febbece3f762ef3ed1eeba45164e358a74afbbdc6d2b765de76d45dcb5a73fad77b73f4b00bd7b15af157bd0bf4556aea151b68ab56fb1487dfdb5d35e6df8f54e9c32677e30d23eff5a992382b56b2dd2aef79474dbf0eb355717f3b5076ab1d6a035343825056d286bd8af461567bf913a269d8e83afeeb0f75db765215aa95dd9985ca1c124eda0b256b9ea57707db45d5e91526b9ea1827b07d9dd60af0fdc07ad5222f79313a2cbe572433d17d447de65ae868535ab944cb000d1b108e9e3ec124b0d587e506bedbd32c996755dd7656f6209c2d264ef3a5f649620d575b25886c02c447026598884b44b26392859585956598cd82008826128afc0b2c0a04b2e845625c143b562c4ca9224f9087e82a3e8acdca6152b5d7e44af4ed422d2617ab1b2854f264444888e7821ed7c6895923985d635c308c2354bfe885f16bfbe17775d061f80739639f6011ae36ee77c0abd53d855bb311883889bfaf8ba0379be4e92e996f49aa668dd1ff880f7be5a90c7e5492ea50f4be209e4b9bb2381361ad0b3c3c58eeae276d2eb6e7b600ca26eff9339f63b900777d2a77936c8f35912ca6a9047cad4484aed973c29495aa17f200fe732477e06797227bdf6b6f6ba7df03d30dfa36472e7da005d1896dcb9fffa5ea30d500d965e3fc3b0be16ad16b4c4214d4e47365b0d6d16bcbb8f59a26e5f97163784a4534aa004292550820941144065d0904996dce69c94de4063068e07314268592b015bc2bd26cc0021f5b92be172e6c4ab8fbc011228c184d7732ac344fff435381ec408a1cb3c9bcc81da3e288450967e42f23063529c4e481a8fa22e615debbac139c50bcc2b6c54148de5a38f1288c6f2a101228bc8a8080d630629b30d788d07142a38155d97341e55aae83d5535e078c269386e70efbd57f8d7250d47154370e47a7a6839608081875dd272dc1005d75dd2721ce1b41c48f413379d6a3d7749cb01d4475e93f81a45ba2f927fe435553fefe5d5013f8c51855bfb9a76b9e46a2cd2fe89d6f614d9fab5deb6c87ebd2b9d72dfd0a22c4d59097e9f20d675ca5a4710f48c02517c4e1a11bedbff6ea97431ff2bcd54f176620f59ec0144698dc5daf5873f794f32a77f9ecda0cc5ce6cc0f3fbde5e7856308ec0c9a4ad2abc5a36b3b4db19525a56e6f40884899e3d292cac88ae3482edbe0c216a155b3190557b98096238a576badd6ea806d17191826113a19e9b0810e21928e5c09e7700959263dc38bc987d61344cccfa9a7894c4f142693e9e52507119a1ea29a30870d6f4d91b6e375b3e307da0e22586c1b1b1b1b162c762441ed29d276d85aec20c22989a083910d1d6c1c1c1c9c0e3ad8014424002092d46809179e1302d853a4b120c500f614692c6891e992c683c7a94b1a8fd707341e47a442ce43c3e85167954285444a49dab2721f7dacc992247766a75ceec82e69aba21f5ad502559239e79c271a8e20b921eaa382e9d0c20b1c782ff49831f954bd56b9a1de755d772f6d55a59fb8e9445be5a005dddd1c514801a0e5c8e2020017ba0e5e48e1a52e693a62e8329716498428279f6883fd3a84bc1b1db98f450e6cc30a6b66d0343ea699205c647ef6a9491863b3e43d5160988e31c69888db2e693d547a88b629874b8b975e21e987cd89f84649bc70870d06dc41f4b24550d2368c3e222f1e12302f31ea9cf0300b52bc5c16b46ccb43062787d7a9064e0e32b0064e0e341e47641a0f5abf5dd278281180135824a49c53be9c524e29a79453ca26526e41b65a93201b50b5f65e8c3bf917e3aecbd9933fa3cbd9f33e229f96ef7d9fd65b3e18da30ac7486f2e5ef0dca1fc9971fde42f95ccaa744a82ba8283784bb050de16cf2ad9413269fd22adfce9e1442e8523ea556ce9093d25aadbd93d66aedbdd85e8cbb2e67efb3df57e9fcbaec79dfa7f5fef4de20188623301c8d3827914a2ed6c5a5d2e9523a995e5e60668c9ce268a7cb34cd1798181b1353e98c914fe5d79f9c202fb4b8607232318e91e34a897fba7c185170f9ac2e512345a1e2ec103da87cb8bce0abddeb47561d5056dbbdb5ab44ca58004dd127e12e99e00248d0e0cffd913b4055677fad559b4c80ac430c911d02c03f00eb95e8f225f8638d419714eb2df61451e1fbfa53752aad35dcc12f4615febd7dcfbbf35617b4deac7c516b076b0c608c55893e5f94a0282501c6aaa4cf261708c1aa7ba85c6badcf8caa3f57acdf89a48e71c5355b6c9339148a74413f013f66f4e8527fc2457dac414b6cf549add114b52855ad5596cce1b9dd4b04aa1c53c0c44c2d29a3ca54ad534a1954071229834e2a842c660436e4e5e3ea75d256af94555d5454a95f262e1086c15c489709208a6118f643ab94719398f1304c8794d1252153dd0f4dd59c393f9d3acaea28eb8af6ef0f957191c8541235499f1589ec806ace9c8f1d507d3976401d506601f510c37a0884c5d01536e9211359651f18490d799f131050af75949339f561e0c05d9cb8d4e40e7e0ec4813810079a363f86551d1727d245fd1f1908f7390df96a248561bd568c72a9c99cfa2a237cd42da05edf4619e5e44ef7f55f7dac3f3f21132943ca10480814bae40efefa61930f6429233fe4e4a35bac3eab2c99339339b5c758917455143e1fbfa47356538bd4e4450e023435a68081dabf1f2d4ded1f7da8d25f5004da4bff8925d438fbf7a3b7dfe847e2e8b38881d9bf9fa08d11066cfffe7b184338cd087df41b75827eefc19fa008fa3d148d08f3f77f9d268255f7a04b5a0b4dfa095a848f84f97aee30dfa3f59075ef75288e9bf43b7c92187e057f8b5e06661f3d0545004798ef8dde438dfa4792061d8d5e8fc47084da22fffd1c355e5cdf4fa38ec6fddd4ff006a51d86101cfcf1e23a18a24ed0bf51285c5cd7288ff74e1c2fae03ad0bdab0285c5c7fb9b8d3dc2fa893da60bc9ea8b5d65aeba42c9f1d9de2aa0d858bfb81871f17f7040e8c17081feb3416b324e627c94c064492596cc6dca894524ecf9b95fee852ff0fdf0e8f3ea13505b80513a0746b636e3d9d3e5e75313152628a62723445636e174f29a5b416e3d3eb48a79f65d4ab30211688184dd1d3ab3bd5783efc9aee16119890972b0621af969452ce9c4fb5d350a7ff9d70fa643b3d394939dd4e350fbc21f10431add6f35a443a7d0da3bbba62b0425e3445655a92567b35c8f3e1d7323f419d3e68a3b50a24b384a6a8cc13991a4d5912a9d249caa1cc8fccf9fa7d708378886d81ef2aea2b93c96432994cd669cd73ce392d1e72711777711777711777719dce2e7b9fb65a573af528ffe888c0ddcc3f3a7dd9dde4704744a7dfadba55b7ea56ddaa5b75ab6ed5e9fcf406491f132f8ba743ccece69c73a23e26d8cbe2e9906ff9966ff9966ff9966ff9d64b60141086bd021d749ade37c1289d7ef7d90e05c2d0e9e35df5d7ed15e8200dd3300dd3300dd3300dd330301c8d3827914aa65d45de494e4839b809464dc22dbada7cfd9b73ce69494e48393a7dca4d70474dc22de1ac16cec259380b67e12c9c85b39915692688abe432c39ae931cdc430d39a59c91cfa2fbb8aa40ee3d329a594ba1cd1292d55d129a5d95e0de3a3ad1ebdc0a0d3af5f08ee6ab5a94aa78f3d53904edf5a6badb5d6e5086badb5d696aa28c54ab152ac142bc54ab152ac1463a144364165fe6053c38e9a24f4490e6a0a686668a1cf1923fa9c28987d9233fba686e0efc94249a77f351f8537a8cd7fb4a1d2e9dbeffbbe7e3f0abf07f787aadca686d9b1e77dde8f42707ba871dafafc5a93a4d3af5dd7fd07ee0ef5853439e8f4b1875fa330ea43d1ccd0e9cf7c7393cb37b97c93cb37b97c93cb37b98cbac9757a336344a7a81b158d8f30b957a73fb3b276666567567666656756766665516365cdac7eaa0e05aad96eb85c5dfd74fa32adaa435d2f0a8bc9b46432ad994ceb47a6d5e99f6af36f19c767eeb47a56aa55ebf44faff9b12ce3b3d3cf03a5e6e9d5e9c7dce6cb5c5e71d6a9f5aed4add38f89cd9fe51f0e74aafd0d265773e6fc74fa4fa560722693b5a8fa7d6c85209c57bc75c38ed7097200228407ff818fdf02a7b188d05843d05831599b3116b555d9685b34227cddde467be3000b7c6d0dc70edce8cfcb361c3df0243e0ac11b8e1f1071388200f5372c6c6a6866ba700c410b540ec71550f68613ceef2bcc697f48b7c9e9a886360b8e38230c2f29d1923b2c847499dcb171d1426d72a7067cca923b342f4362726766f426c81d1912511f4b4af4fa2c44db6221dab7114d2525ba8b905edf46b45f23be08e1fb45fb3422ccc7b868197513add7a711edc7986861f832e2e9675e8680a397d9b18d5db9c32edd4fa20932677e8c386332673e8c485f446a12ab4ce6cc27d5b7a26e42af648e6bc75cbada8eb9945ca37632da284646da63a450c63a449fff891eedb06bc7482eb993776cc77a65c09db4da6d456b77eeb2175a94b5303573dba025ce7867b32aebdba6ee33245dbeb53ffa7c91743715f9a8db540ca908ee6ea3d432a577e6b568f5fafa3e38e78320ea36350976a3c6d9b792db8d702cfe237368e0230cab57f0eb6b50bcf93e0d0a8a5db701aa99993640bd7e99dca8240ed7f6247443230040100033180000100a45722048821ccae298e20314000d54823c4e62442a974643a1389ea4208601210622c800028031c0188310537a5200e05270fa7e8e80c2677fbec62ebe59675e27d2c9060ee777b6f49d655d5a2cd80a322181af14d483c53151183a276e9abe88cea310c155a6098925c60217b470c0b446f60be0af1c1ead5aa7a972c434bceacb302993df01b483b915f4048f032d9db42cbf18280b2ff8d2cb460ee7f7835fbd1c6454f2c58ee1755bf328fa91f5746a5abb5d740121c18926fdd4a006d28d17c0eec4a02244658db1eaf73e2fd2b5d80a018c39f990957e649db5b676c5c981f5d1b00f756356cbfaf2d2ff3f42ba53c57a9a29889876e58a3b7a9a06fb08e077e8664fdba7f648ec12ac309a6d23207ee548f9b74ab93620c987df5e66fd637eb6819e69db20be571ed92b15baa62e511f627b37a314076cca2db00185901192a8faa347c7186ec8fe8f15be1b189c8c6b766abd79f95c4ad81a9c721e0a6aff6739c1f2d55ff5aee404a470d6f6f3212079655ae6afa39bc574e14abfedbce12fba7d0726f4c68ee6ed3c2ab55a91d1050f567cf21f3042dc4fd30c612d3a49330fbc61b5fd3ebb6a7d7c4ec0f70e247c646fe81ddafd3a463d6194b178bd417a2fefe359000ffd1f8c054cb4205a7919995c2a897e569836746d2979738915be6c4835cffe63761b3618dc3893e199703c96027e80ae0dd0eeb30683ab199cd124d358feaee0f19cd238b5c4b4b18e8958aef1db0f5fc3600fbee39e046e983a8daa5996661428aeb5c2b9b162c9a30d8bc8200f03f0e25abf6799ebb4707ff77ee2ca3af143e944daf4ef5783a2d35a7ed3f1355a1ed0a34fd5723d7c0e2dd8503506b64894b25903c25a9ac0604351c400108aaf40343307e594b8c5153527146dd9c09fb8331fe91f1ffa3b37519f75f330f5a4716bffa9eb6b571c55d06cf94151d8e73424c26796659caffbe0c97182e6742bf874c4a21fcab206063f42547ad86d6c48c2d986c741680555185fa94a3d068095ef5a221f116905a006b58d37686e066a8105c03e6b349594aff96e00cd2947b9fe9d26aa0993e4c902d0ce40a7361d5f88ef3e8c572ad89877326db663cdecb52e5b2dbf5b8bc7af895cf1309a8b66360068cc8c7220bb12271090106c1896a084ea5784703a2bfba4be658b8d4ec637354c8cb40a0a3b352cea3da2087884eed210783ed16a391161c5f11d3bbbd12ebf3b0ae0034b05376391dd4082a1711792f9099fac7131e1d449d21645a5caa5483fd45b06c827cee0009096f7767cab145a6777f2b59d426ff76e038df045eb7a0407372e8e5108aa5e7215fe4d6979ff67bdd8877e010a85b366b69e8d61254708f6d39bbd189f5f1c4c85836ac57384131c43f4e54e2e198490d85567ebd022cb1622a70312e059f7285aa2bd33132e3ac1b9e431286813893f6819721c9e4810966f0f00b57180ad39e673c0422f1d44f5a35cce9da9a439e43155a0916c5225047eb971897d4df1d5e97f1f668fdc6f8aafcef6db6f608c6998413202f24724852b9772816a17e90c44762083861f120a71e9095490c03da1d9199b428d79c7a5e939c4822248eea4896bb229dcca0700d030173b1aff33f6b779d903ef820d1aa07eba872dd1d5726e00a420bafdf7bfe21c8c7d7a326054b1bff78af3508c69ca0240cdffdf0be4e3301324ca7fd91f7549b06d4262e299e039c23712581b1be92596ff91addd88cecd6c932e5798ef6c54b320aef2e381e695360631857c121bbd0c6fb616857e1417a5e09822c8029d23f3f78273d53c325f13a7abad283287f5e8fef739c39351c564515de9e5f12c1d94a12ceb1b1117808f09c4c7c75a50e3b26c329a3ffbc343cda84f5ce0cc90f65197046f12c947dacdb4ff1eaf3d5980ea0a68d6376062b13394f870e1489c58b3039016dbfc75739a285cecf71d682c1f82b220649d88b743d4a9ce6ed46ea41fe441a6b23199f4ddbbea2fb76fe45164880a05f42e380fc49de82191255b58a8cdfa5892959db13c9db5a020ab8bd2fba5dcec90ce7bc7fae3a1b6837abc6134e73b16178ff41dc9149d4de385f5835bca2f1052476706796893ae3ce0e3a5624974e51e60db91c0a4ed97f596beb188b15732310bb709e0842eaa3c468ad93cf328fc5e8f162cd124e7e736a88867384461b11adcf72a3ec76e09a385c0100fa80f02395d6dc6dbe9c2b7cdc0a5cec0efcfc0afd0c02f47239a465f5b2f8d172e2d1cc5afc358b8fde10a14ffc4cc994b33eb6d9782a8b770693586150b60c715b32be1a8cea4d6ba05c418fd50af16cbd50b38beabd6c66bb85f061a9739237bb6affea971b7f99f46ab714a233597343a331e0dd85cd1e8cd101aa06aa0d975f13c930bcdeb339ae27c221792ef2e33fdb924f9eef21ff73e98693efe5e6f1aef6f1f2cf0fdef7bdb781efbc73fe43428f32cd1ed2457108ce02a7eaa01da9c2de3c573138637b74dc622588a95e2355b649df0e06dc2d6463cd8206c43957d2063b09c910884818f3b30b9d48369dbc018153f1f8b849a1a2360d8398fa0c5f2293a2a9c49a4ff5e0356811828af26f1a1c2288575aa71aa973a9e688255a6928cb6c3fe2b86c1af6e1b5d8a00a39b02c0cd1700b0c6a28c9aacd7fb7a1bfe7fbb58067de9051835ed9bf310e90662cd118893f2526baddcaf942a9e967884b8b71d53460afcb871bd7baecf14ed30f3191d1724cdc840edf0851c09853c1383b57eb6c00981aa6f62f5c99ba00fec83dc0fbaa97c2c932a870b385f819e09b00ad3c1d25d78c006abf70b16861c0decaf73f01c04bb06fdcdfb33a280de04a506a038ee1853a66c89b3e990c088ff53ec58461455edbc96380e29ce74f1ec1b2e9869e36c5d56141af1bfa407e1aa3f14270a90ee6c9ca27eff252c292171faaf302930c031ffdbeed2b49633be8a41701ee1314770c089b5fa2374d8c9692e083b27eff208018edfc3854051c3d6998b12462376203fe8edd121b767f561681ad7f4c4090de88c24c25994dc0edd9e53edc48de104efce122dcecc00907a32cccddfc2edcfeba92157e42a44fa304eaeea01261747fcacb40d0e42b328ece91e1a760048adfff5160db0449a3ebaa1554c39a63ef03d1355b25b786c91f5c2446ff556a033a3793ff64c03cf0fc870cc1ab3d00ac8acb836693206e110b71e2566fe53e8aad1646a1b411aafd7ec9db776c13e54e0225c2f2018f504e658bdf609cb4c83473269825efe402b62f2690c7effbdc8ddccc7c48dc825892c7e5b3a51bdce92a3780986455fdecd0df37a3fef9c301875de23c9db84b908bf51503477c20f394332fdd01048160c57bf7029cd22c40e6c1f01cef3fbff41f9c2cf9b973a5d085c5213990c4be4b3c2755ed9a076f11061929cdcdcac8f50908302d20ffd45c85961aabbe4673f82bd87e517de8f3035a017218f8d2179f6084ef12fa52121e7859364d4a98df8c1361fe8383558f61034861177b7977993115b7c04f883554d411972fcbde89eae165158203c7ea201cbe8ae2453f250ad30eded01e01902111f58ace513a875f4e1cd0eb6f91d2c30cb0deb1e374e9687eb0b0f83a3490949039744f3f60390c76ac26acb190b1b295cf220f7099eae56200ea95b72eae3085ae27e8c8d7f0b05bca2da86550d7fc8807afea00aaad9d7d1f6964a146dabb0fe7d1eacc90d7d6bcb7001e7628346d39677343b485e29c84f10098fc7937df8a27a4c013d00d7e0b033de1458f197fe3cdafc214a066d84d0e2c6c7cd2fd4cbd81c7e818159b17ff4d484c4171ce7aacb5a33a08cac5f4d674b6bf1b8a081825c4af590e24aa2df4917a3cb5634288fa904fd6a2c0b40b1556a75749457fdebbf90a0b25899a2538af0d76486f09b812b89972d077c4d263230418f40d9628962bad6c83c237f65fe330fb3f6352bea3e9189d638891c2391add22a78a5378bb9a0ed084bbe3ad91a402969a5e29ec5aa173054c37ab01d945a9d30c5a6506bf47623977f003d22e2b7a3c64ec0c6e3353366107659c962c0be82ca998500374c5eb4f48fa226585810a7373738cc6ba8528142af23c702d77029018ca2010ebbcf4be8db0886705c40d06ee340b02cc8d4cf9eaef81890202dc0d263952c3d467242ffc758931870ec74285bae146ca585dc74b08c2b907c20811f6c94dc6bf8b8d831f34d7a07ac2a82c90ac5ac44502d43a2b26bc4b79272b63fbc6a89a04b24abb6227a1bfbd8253e734f234920107449b7df7bc7b28c548dd886ce503998c892e0ee61adff93bbc8fb830774d93dd5eebd19e44c0f729991b4c0a3ee171e912a036895fdd572d6f68957d1c17b7f17ad56e52774fe257fd7f19379643294340454685abc14d7e77e00a3529cf248132be184fbf5ab1dcd7e12519e0ed5d460bc370af095b62e802a4854ebb3ddd3884b87f9eb96f2a1f31cfa15d682ccdf54f5f79aaef81402f28344ec8b1f5cfa6de6456be8c4afa28afa64949e0e329af46173a52eb3cf6f54c48c867a99e4041ee10866b6f543b6e90c5c914d694c3547a72ab13552c6d76878954dedaf147d2efb0022e7e9b32de3d57d1725594a2562d69a18c74c52f0fbb701ce03b400f111061faf0584929db9fb1e2f4f56822d050d6d591f94e799b79128a4637c925d8b0821950a23124ea4bf876304a9b602d09fb50735928bbe769ec1b33418d3cea80dbacd3a7b9438ba3b5895de09cd3fb97a8cc8ba3f7b3647e38d7f8f45bca24017ea9a46a4b5b3dcc423c42f961cb607034544a935a98f801f2a712b400ea3cb8f0d0e0fccd4e874e4688931225962dfd637d7c8cb6cbacc2476ae4ba73d3a13e00bb283795778d7b35da275f1cf4d6a73a21ac452d9ba48e2da8cbfc7379f4283bb02e5224bd4cd71284c43a9eddaf894a96aac4ad0a29dac503c1f70994e2594fa2e1853873938486332370c88291607e0c3422d1b95d117ce933ae32c7133c42e753f77e4e77ea5396a172dbaa4ec840bb2a042e62ea4e99925f9462c23515acddb481b5a8b9194cdfba16d8db2455992e7b694e60ce1a52b277c6402b3a15746ade52ef0c3a31a1ab6b0ed1a9600deb8973dd3901f59c1a259e20031f0f49cc00c8230f127a4e3857cdb9b5583b4d5aa56e0ce96b1ad81f479ece93ebcd0b2015d57ef7007b49daa55119433e03ba1b77cb9580c2bf3c0d32adef9430d4062ba5c18ba60f6440633c11059682369d1a18c0d7a8760f2979975ee44f00fd92642d5e6f8e94ce0f6ef7abc97decf2a4e207046bf815770a725fd204caf1ec6741f71a2782eb8029aff7ffabf1020fac1726b778622c9ebb7cbdb3fbf2a9ca3cbf2d06d8fd5e314cc9d9e4500276dcb1009645830d4cc494235bdbed99ab5e959ead620924f3ebf02ba95e0a6cf9f77885e7fcb42b926ef91f9769c9245f98317f0770033408ad75d8c3ebb14a83a33546774fd0c511cfc352837287931d68f6b510d8cbc5c37ddcce7445c66e2b3fe09f3c85de509eb811728051ea5cbfe7a46e64808db20d24f0ee070e511fb660c41d4df95914f5c5086b1a4beead8cf682caee7245f30725b08f133a29b7ce89e9427812499e9e676b00cad5359a6266522d29807df87817237a722f309b4f478841ce682245ea6b36243512e47380fc437c59b6bac530b7b215e4190f63a434629637b0ca57ce0fd14899d10a7aa196c13d2aa7a9dcabbde602454669cf1086723d7841a3195cc4e35c261c0f619cd032fbe5c0f61c3eb37c677fadf7e46db61d3f4d87b20441d5d6db8db82ac432f7430a212c4bf1a8e3563173d867f389cba63a41530033f6b8046fe8b15236c63bada713b83d3b6a7e75e0fdd7a6afb217caf66ff39acb4f30087690bcfb676af68506bc7c0060f32ef43e849980bc8721b4238cc80e947374b4f49c6d703798d3f38cc46b0a53a45cc7da06706155c53c64b5fd0e08e3e85ca3838ef984adbeaeefb83907dfcd004e214650d44bf02726dcc69c5439e3ae2063e86ca8f4d1c88539035e8c58e2f73f20d4eae11b0d914b4f9eb53a5d59f872d08a240ce5ce39a51d2b677de4e1dff1f34563426d6378f550b5d3f4363978fbaf042b17107c531e4149d21777722405d4fe3763328b6c1c6d661099913312bd4817da3ffa58b0db09553df6021cdc385c1149104d8db5735664930e227960f47c42ab91e624c90ab94361a7aa6ab876a902cb3c7fbca79f835f0b9b0d0839ab33a76c386d5f2b3d596d01e145bdbe5c2814c0ef7bdf3f2fbb95787ccf8de2cdb3fad189ee16b7b94a4f42218ea3c4cc50f843df01a37e664de5573044199afcf6e51113c70f5224604e9118e5e553845fa08222ee793fd90610b978cb66621bcc266339252d426abc753ee185dba7494d2140d2c9ba3dea184d991ff3f4b99e02cc0201d71022a0c0bf613d260980400e7963e92c7955de9fe09fcc3c47a7fa9d5e5e9386ae776ce90a19ff5d13074ed40361f36af3f1eee88fcdee02d393fd73a17c6b7340ba6219e341d7522080e767bd09c26ac3ca67d37b6d05b40264dc047932d710072691e778d55538e0d0f0438348da347649edcb6093c8ac6581867680d958d564a11ffd05343edc85fe72ad62e92f5e9c38de1f939705ad8dbfc43b22eb259d0e423d5325a761635e8e058a7673c9129da4d40b971609fbcc09028f322b6461ec925a8a19f385f221b5d37479b394eaa4d6814745f156e6737fd32373338a263d9ccea1b2dc9916601a0a36ab2c1bc4dca45fb3420b53418066326ae879a822815fddeb5bc8b6961a58f4ae7b98fe30476497903390d4dd8743efa440227653b61a5187d23c8cfb849e1f9d1fc9bf710f86c04f89b6f293e339a2ff326851323f9356f53787eb457b5db7b6502fea06650eb269b0feab7809745278d30ac8188ce3067d87296b0da48788f1812457ebc6d0fb64ae54be18d7bcd0c9b30fbc6bd016747f86dde9bf8e408eb366d97f0e9687e0d36133b1fcddf6c37e1f911fe1ab7263a3fea3acd9b257e3a42a63260ec1a02367ef5b394b6b98559308c93877ebf7b196c48f6422d1f70eddf426ee7f73217c1788a53fe890231f4e247698589711c8b09aaf1dbf411bd2bd84063c6c41367a4d0a8e1404e4cf1a27115344d6f8bea47668d50cf7634cdad52bbeadc950a59bb79a67f0974281f393a880f8a5ab2bf29cb0195864512bf619b05946c86059a405de35a8eab4cf13f5bfc689e5bdcdfbec5ed447a8d859b178fbbad5e57c17cb13b343a68e28d787fc3b181bcee88e3de501c46be487f32db0d4ddf01af8206d23ae9b6d590251ad8426ee4ca0689c5a174ece3e01af22a483d4251bd4a1596e932643ac510553aefab9e6d261a58f50e8f8388576589b9dad35e887c5e55ef8bdad5675778adbbc35d3c43481d3eeaf4a3f0bbabee9b4622a6708bf23d3f9d12e4be2d39327e7e6c44cd9a174ed154db57357a5b07a1e1252628dc5c8a9ced72827b645f8e9f5d1da4c5254c0794003fd81d0057052aac5a5a8d0a70be94d15c379fc30ba02abc9592e60b371f96395cab82de984194ea79e646f5d955eb1854de2f52690e219ca1dea377298058de9969b6e0d276c9c095c3599571cd0a5fa7e666b3b62e10c7832d04d93df8d03663ef241376c1a1543298298773cc63e5056de40ac1710c22b9a41674266817e19a344872857af6eac0398e197bd4db3e315c5619c4076ae5237c9f190c4349fe1e97c57a45bf34b9139c6a366236d2f8e7b1108b4c2ddc82b83a5197bd48828eb7d36edc96a9e22d2b8c1262c6e635224254412a82694a47c8e310942a8c913dc6ed421eac6e34336c3a18c111d51437123345ac7a9f2a6b70d2e896226a2b2268703335579eed396935d7c8ad5cc969ab4a540aa0182216d41a0b9171640e4173db8c87b191d8758ac1a91eb2663b9a643ec6bc9411f33d083415f03b3ed9967e55b94edde2319f580e831b5825449c42dbc3991882b20f79b81ffe545116338b064cd1ffeadc022a26354f75d0687ae530cb86ee49dfc3fb54898d55bb20e007d44be5f9cae43ab7db2f65ba3b8fc7334c3314c4801a7a02ca75592c602207d4387c8f7e8c6e98bc8ab0a17fde046c0022fe21baf48744e074b2a749e8dc27538270cc79a9a098050a56284399e597e735dc11e02bda1c2800242edfe5bcae1552636885ca095af48c1c617d9c4772398c0afd2f315d690cb6d21288a48c0407ca485ec284ccf69dad973098fc72f314b3738637cf0770972c0bd53fa88c178c229c0a805c32f8c95cf0f11405c11ab6d1f4f83e711878dceb5c9af4f9f5a41c88e74d162ce3e98d0bfac5884d1941d72cc1f0e8dfe247a93679f0939e6a11a1f6b2cdbe7660032407137397209a0ad2147171dab3fa012a7107f9de6b07c031cc663c0caabfa337ced3498580b4603afdf71bf3f70c048236f981d85ee9d383a449e6887f76c52f92ab7c30ffe9aa8727129b9b1e2ea46b8b410b6d0dc8b2e9870c98f1c860ed5cb753ad71f9a81d546effc9c3a549670f0e95b63280273db04c6c1a00f39251126479c90385462374c70e32aeb3a0d6e021d444208379c21c0c1ca5af16cad828c0b1afe6e3fac92991b7da667689df78687ee1fe869cb2566bb4d36f5498f5f98921ea76b1c6e232e87a6ba5c761e8b658e47095fa3e82e1ae4a764b4eb99b3bf2b6b326c3e9c946dd8e75d129f231baf965a8c6988784c54f647a1744b409c174dfdd13203df429173636143ec96d60e4d582611734da1de7472368709516357c9bb3c8c0d77f405458ed6ce5c5ff2f91e1810a55f6534853f1973d00bba6bc70d5759959dcdd40686a84e38bb2d7a85b3f316b8e96e1a92ca76d5815abbe2bb7c92ce65356830ac69383358e3fc9f4278a326416bdf0c7d623085e8361d29f3df274bb1e694e05b0b58547f78cc573010fcd0fe14732280cf525138c3bafc1327b081c5abae6c61059c2761aaba30ccbe355cc8cce21937998056ed2221ee3dea395ee1d297c692338cc1b8185af9b8647205b46665524ee2ce0e04ca32997e1e7b45c1663a0cf4bb716cb39d6d162466f045b6450272de81136a29e653d5d92e597d0884ebb18a48402a4427fa7c82d09767ef42e1ab893db00015340af28ca17c40ad047ef549a6087053811cb5f709426545277efaee8e3c1e5c750f00b12ea9e0b8e953633511523965e98ec4390839fe0f4d856f95028223948e68e1d0a0988f3391e451b6e72dffc698692c6b86b219333d615d985e58cb3c99b1d438fad26b81f0967942bf466efd37a30150fb908bb0aa8ab6b9fc63dfc0bc66e4b9b3fd88bb5c9224efb6aabfcf63ba9a10356559e5c0c82c595404e475a767ae7df76a5b39ae0f6d8a721b3f0bde1ddbdf11867034ba415cbd7c140d61e54c04b11c1ed8abb4930b092c228900ecc43dad0c984e385a708fd316af6b193c797ab9200b88fd3ed906857282ee16768707f49ad68b698b37afa56e8533c399771a97f8a3d9f28b580317586d5ed1a991d29dd56506b69f0e8554950236a2989e97596149d5d833468b364bad02b47fcdce20d73d82cc93bf1441e0e7ef008980f2cf051421e082773584735650714ce910e8dcedfdea858438d2c4003b323362bd360a8371a889dde3996c2ff233d1c1838dc7f6fa8fda393f1cf15498bcd74409371df30bb2e706bd860d755391c44a98ceff2ee21e690553006d810b2bfec68e80c52e09b6dfc46bb9f80cbf09bad22434d7c6fbdedbabcfce95d91036374218301b2c07f9cf3150a97f364c1a2ba3ef7daabb0338181ee681d2c0f3055c15ee45d8a25fc2051daf65fb00037eeb2b9be4ab68803c7716e9a47c3601d02bb67661e0c3961454f8d37e9650c6b2b4c68c0e3a8b9a2ffd8e6b40a04c3d0a3661121f0e6c438867ea834089b6ef49d70d45aa984203c35c84f48020077ba56ca048a829150d3bf160915cad7fe67c79356481393313bc82b88bcc11552bc3446689927f6a6801710112a6bd91cee9af927bc89863b7ec7b619457d65db2e3a31b0a63b3b4be715cce72a8b4b18bc34869f5dac1ecb2e04942bbf48db9d3387c71adbfb08b243dde20b524618850d0a9d17a32eb3af16b76215ffb72d92b10fc2630346a6eba116071f6e9ce600347f1d045f7e7a8b0c1073f6a346868823bf0e491d3c07dfdbe68c380fd9026119850e3f876d04495ba325c586e3d359fbf5aa34ee71b2ba2fde80cd3498270c7a1dac554638931f332778d240ddda4ad16c9b1f96671f22fbc8235e1313d056eaec5368ea1ec0783e867a17705cdaf35b79515a92a693a28976f0191f17d709e0ecfc761e439ceafc57658ddaf7ce58539d479f48a6d23a5e77a671d9cb0d2dc2b05599d152b2f72d1c7ac35e678b07afc2127aa35c512cb840e7ae127eadaeac5ad4a9554816390ac37d40ed790d61765c12a80ae1ee3f62291174d251a10232f566beb6fc7dcb481f8862c5b080384a31948fbeda400ff8583dfa54b887d1706d871ee552b29029c30e09efb42bfd2871104e545be2362693e64a4e8f8a07f5166d566b688ef12db3a81559ad12122d6b1ccdf7338ae056a9a90956edcd017d73048a8a1b64b069c7e1bf3f3695119e0e8d0623a7007bc539909d70b2edb4fc93e4ad5776bce1e74bfc5dbdd90dd599ff7d76bdb5a7b728e049da33f4191e88f6a3604cf9c2b0c83a6e5e4a026d97983e8b285d0d44c999a5d22a81d2de5b0fbda4c19d934e0c9925efba567bf4a2d461f08e9efb89140a9d9e38e0578b817b82f6c772d1cb0faec87d7248d2f7fad843e4ca194115ba8aea328a46ccab7270385793f3a63475e82aa1ebea6c966b127173e4d0e0566f8059cd78ada63247d9e1f5c2a8d806405c24f21b505504a5e89f4f8b2529c16eb31acc68a944e92e4366353d14643bf300b191537445183000d2050c7db6d9ba9be181c4311813d03c236af9f890e7bd41f2a7280e1ae5e1b2f025798bd66e86d26ab91a375a5cbf17ed8adc575db3fbb15bf728ca10f451652d6628dde5681c7dc54530efa3264d0b028ec0a34ec5c0bf54fec73c3fae13d8a0b93964b4a5bb6bdbef548d2a2f4f1e41ae9bd1282baf829e60889b83aca3213b2f0a61b6fe86be87f53f788219be715e7905c014ebc1ee4067519101b01848770991bda4527ea5d2c989af00b55b292578565aa6ba2be08a4fc827adbec51e5b78597ab6605fd64efecbce6299aa19d7177b5e07122e666cb5d2fc07a04189a1ec8093dcd5436be325904d534b3e1b5ef31b636b347864aeb6edeb124712d90af504e2e3dc3ce2fa1d085e0ad4b3c82cad8ab2164b2ce8d3bb2a853ae880eeae13bd62e54f43b6de67e7feef030ff6fe21c9c4568927ef53127e8e632002a42a99b3fecde1ac2272e9bfe0daba8d6fb98dd3ee4481cb386aa5ade7b84a93138ad2cb666b55a7a6c93daeea4b086aef010edf41153ef5fd6f167d4f7b437a0c075811f82ec02df3b77cb3b3e7ddb9d055cd4b898153090893bf578a4c89a71d9d31e61ebb3a2c54489793d4330da9e4f87645be036026990485961a4b23c388e491b9747094527812719ec47eb5c1a04a5445d94e2be444ac5b1d932de78287924f990a6d17257550032dd21062c63d3dade0ab97f4e12fb34958178c827aac2e9ba41826760ca2624f38b19f5c035de48559f3c73b1aa13ef27c3c050046c5f6a76917649d3e47372b93d3ca9bb03daac4696da74b0bfa62070e888b6aaf3917cc47a7fb01e522136f79d7bab484f5e1023408460c2c924acf78f64e6ae06150648042cc146d3864189ce7523882c0083111ef5f317aa56e0fbacfbbd6d212dce3a13e45945e482f57528d5d8a1934bce78019e33845f0c0b98f98ea06b8ad7ec4915376cca0cd08a8281c365e41a8fe293cd32cdcc126d13eac2734a567df0faa8479b74030779fb77719ce3b6730c488213ab4adb059a807c8c076ba56be0b29f6d55186135e8773e3d6161d0542ee1ef3734f9a7160a4c3292e58f80bc6a507154e1daa4b62487b18029177acb388e2a5c74970e721c8650e2b6760211ec0c8be602b6ccb01cab26fe00ce6413c44b87186cf3158c50c0ff2472a3421919d67c3329dd92e1ffbed465209df0200c0311126e18790070ed44e35f8d7f79f069e1fbf585f5cd7ffbb4a0ac75261074eaf2d4ca0e03132b511da69ecd049af5c9ebb1b22783afe1d2b4761d753f4f2d701574bf4ede3e3db0e6ffe493c135b4186637e27497ecf119fd4701047162f3e33142d64bc27e40d004b4296baa84c57650f18c833c49ec3d8b56b21064fc1251b369c27143dc174ff1cc94b4e109bd01aba1610a2280f321c931e7b6cc281fc34145df213015d5defc20a51d292a712410d5b6fd75d44f862a17fe3d0eed57b7d3967dd58fb248d800342efc150e38995f7c07416fe43ef41ba485a860b91d2753e1b6cbd671a76cbd6eb42804eec9f1212ac780803ce7cd1ec03d1374772e77e559c2d1efd4640b61fff68eb1a360af8bc8d028a070bfa080feb5b9205f0f4b85260a832a560ae181ff26cd50b584c1923720b5c425d8d21a1e0e04e43916a917f25048d2b140d6da0e74e74bcd3530c7acf707d75f4ee7cdbbb45ac8b40646779d16c8aa4b32fad7d1f748c5f623ec9641f7d835554845c011340cbdc16d6a99a683e2b4f93e828fbe0b92d7bc661b5b5512bce099afe29410582a8ea692c5aa45bb2ea6ff51cad86f4d8382be85c8442c028ca5b2285d47dc9f06f91aee102a4866b49899a234607c49f4386627d708ecfda3cde32762ab3caecc72d81791634d26c53e8fb2e025813ed3af28135207ba5b67da9f15f4a04f02133ccb3dd2143c5f10cdf44f61248551f2416b79ec5a0a79d03dbd268d6ac9dceab75e8f3c9aa550f726756eeff692dc16df757130493dbc5787b0f448315cff5fb3dac75b211e7967d6f94d596ba9f4d2842be55899019949cce4091c9153e4f451fd7be2bc41a35b3b5da9bd01da9ab17dbd081fea7329c5fb06127a507dcf790d06eb1ba9bd80f32e995238287cb353c957fe34e5e293880373d8565655d18e8ef47fa197211915cd43ca402ab115a4332723f3c00e55ec8196662c438b3618b3116ea674fce011cafd3187f64536c152675132ba954c0427a5cb20e6d382f088b13c8c39aa1da0d43aad8f100ae1b1efb9d11b5c72cc1357501c035c65a8c6ad62fa9cdb03e4816c2d80a0e39b07ef558d133215ec8d701787d54a8e9818eb8896541600859b6bebb7ee0925e6038f124546e848e1b3c1c706bad63c1299161f59958f2193ccc8334f239598db629a3476a3f34cf20e373380e6d871e12f29d76c746a0a56269da715b835b6c903a44812703a739633541e2b809c327283ef1944e2e91be6cff93138277a3a8eb5a4a272296b08e2c538eb7f6223fc85f261162ef20673dcf0e66f439b37cc77300b7c77a0f85acd3a96621137c0bd8888a823aab48327a0dd2612742732e4c7644987a048405a779c3e45c9c1fc93f4c4a84d4c926164b07e7d087ad9e6417e13961998c9d2ef612a54f620b28e96b4bc79088f4b4244426d330f0ce4c66e0f3758f517913de4820e671f7648d7730909d774276ff2250ba3db6b74150ed51b393ae91d1799adb04c9c98098d3f003e46b71d1e7c1dd21045c8351f564db58802cf40969b6c2528b7b38c4ecadf8aaa43e2cb05193f1a47c76855a8f544c42389c54df6cd9be3575a0654c27c727846adf29969a7e18da2d4503af279fda66eac08ff4a8293b42f740cda53fd063bc061706ca2e23a9432ec500fffa326d24aebdf1624d0b58ccbe6155dad4de6bdd505cfcb8a8abe3337216572b615bda9131da239540c80f047e88b20c7c3289c804a0d70b9846048b0a583af7e6349e846b95316b5c8b6e93811b803fcc5c0935593a162d39006924516cf4251a5ceb7815d19b90db71ec7ad00e1e1af711f3b458829d5e340dfaf218d9c9934cf764f7517ef53c556b2e83ed005724ce29ec43b05a055ef1e3767ce1c4c6316d84e0047d3b0abd489817e606c124b777fe7036ce1f0877e1c24e5bcae70def3a028a140615988749dfd71b0522eaae66e1a17660e362d196aba3cd2e38e589483528e0e35ee83295637752a79a2888e614c21fb4d33224d169880cf34e4d8f8c5d9da900d823d81505acfb5e7274310487b1b04f2b668c7782d5bce4ac8cb6073a21850ad74efc9f4403cd903f738bce5cf53a4c538410f332cd20141bad2c7a63a436ed00ffeadb56baebe926d9c7755e4fd0e6118ad09dbc7ef86d89ee6a525009534e09234ea184e30ed97664b828b4a11248be0b7b4e17fc1a1d116a280a0f98780b2205b9d057d8e61a7a40e484607058e8beb2be6bd59eb3843df39e22982450d0aab1d9a6b3ae619a55a1ad181ef625ef7fc6d17f1dc5c032a028a4e81be9416fb3d0e97891a75b36d7fcf16700a6bd0b516b3f47aa59d1e98c916fd66e3ba234dc8870da67cdf09f5a69cb97061dfbbdee276a6bd3686721f63c1ef3840dbee8fb742a5f1297b9e84d95b1a40a3bb87d45c6e467f226a373a1750a6e1de029f7263b67750b46e6e480dc4d75502ca053b142c8987ee4fb4dce60276606cad44973626904686a320e60c1dbb0a90a9c65ff7b565b73e37d0a9572f7399b236633e56422be904998be621058a17b54372c9f456b525b142a3ec1e64e6376f5ba2f0dec28c6eaee2d37a62741cc279402a3def544acf836feb9bf3c9656a22fd31a943b336e4a94978688224595343a64e4835ce596a6d4702609cedc5eef877aa2d85ca49790c4cc017713c2c8cda9a46d52283964ea55083066a0401dec7844bc710faa1d6494f68ff5560fff51918013bda585a2c37850f29455e4aae86d677f56fab13248eecb81f3f34d1ef08b1a18fc0ae623387f4b78afb9080a418806f0aa180cce1967a0869471d9bc08289469220586b558c533c765d15566a463e051e0c13bc72978aa8027414d40eebf458afa280e7263282f39784b79dbbfacc889d0e663117364f131138e123d024b1873dc33a65b48ea8c94a3119313f61eec03d43bdda613cab46a96e850b8bc0777c7cccd7b94f2c04779286321a8885668f61d12c50127a6a4302fec3bf2ec2972967d0f424fed0504720a56d791e6e44a0a1fb3533ce6997b627b90b0a4e848390d871e5fba07cbae02d38e19a922705a6aa99ab36d9788601126a3e0e104507aaae74ba747fbb7101c1a88e9ef00de3438f65459da316355188648257e588d79701187b030cd608b2a980d484d7ea89af9405ae2f37c7a9e9e63f2700f51c2406ee955a61ac66725795ccd302435f9e16a868312131eacaa62ff5b32c7dd69390a708c1333da325d1a8620551d38d0b9fe0debd22a0ce2bcc4d22f491cbf6a6b3bfbdad2062db60049b39dd9c62f91dd757c77d00651a90e38ed1ab2aefad080ec87b431be9b5b6d09a40680fba4133423498eb2fc530a24a1e828e7f8c3856d0c1cb07e2737fdd2ab5550a293ab374ca41d64826bc0c8b4b477276658636dadafd9a73b4cd095b3b5214706ad97e1c4d8350cdbf7acfc8dfc2d4c70906828de26d85bbd32e4a99040bfc4aea816f6222c6cd0dea0007b5a91820061d3d44bf051a3bc03c56e3ebb50846e29adcb3114b2fdb5f6a3a4000316508719091dea7b366de2e1a2cf543906da74e51c7c8c14c9a81ae1e556a24a656e2b6a3d0d4374ff511d4ec436b5a9b1251b8e53e9ac39646c3aac5fc27f6e3dbc99bcd413d556c5ba54e406565d7512679e3aebe4870431c8e6ca09653580c587afd96956e7acbcfac96778e7f183a507426ab5fc5f35f42a638a012b15ef76940410662a4fbdfa6ed81247a15849ca2d1741362db8ad900617cf63581fa173160a0c78f1ff10a856dd4affeb9b99eb66e3bd378dfe0825ab38093886d274965dcb4d94399ee899ec23e206f8723f68c38d264a069a5cc4ab6d3a8d47daa636289f03cda5477a98651bf6bf689df5c11b958350ff3761257c447b789e5c9e5dc29503890d6e7ec895ed1e6101b7c1d2043012e4c209ef01547dd4751ac3293fa75d2be32b7670c90c42e822184a4b91f2cb74564b8a86ffdb91455fe27eecf9c6ee20b435354824336072324b15e9ba6220133cd98f7bb81e09175bc3adb702908f7c3f43727e8f5e96e8eb891d292787efb0b92150d91b65453badd30d4916da3c5478d7487de8a549fee67fbd8f4f7c68fa38e2b378b7af023b4c93bc704da9901ff5eb6b9f087f7e9bba9d6e9defd5b7d72097eeb19fe6555cdbc21b4348de6b46d535ef429450555fa596f8ac4e162da4067a5e46fb071519ad062eae111cacf8840c3241fa2d218dd0e0b6432399aa35bebc815a427523b19f8be802463b9e3c1b5c0b12ce88ca8198774b3915d917c1c35f1e1cebb69d574bc928733e5efa15d77904d028923d2ce30eb3e98ed9d847e550b5189c2e26f47da8775e55bd478d5e1d919389a2a3c4458e1682971cfaf88167cb45b98d8ae0bb1d8fdabebbabade5b08705f1c20932c997497239270fd927068852c9a5541f9cea7ece8d5beb66f907f24c815a70ce90fa17e61dde439b148a4b0beb7299bc5ad31e5d2c89c526261b6285a765c97979484a421229eb423ac6d9c8406f121e0806332241aaa1490591b9d86210095d1d3058ec098985b4ded20d909f025f59187dc22a96b15d230cb950b9486e013ba82f086b8f2841c21d1540af33b05817b8f7342f089622430f20abd5052726e482f73207e9c935c4018318890a50bb86992b03a87b11bcbdafe0f74603d3f71ef46146b7df1b79bfa1f769e39d79158fc779e014d5fce8a71fc495751180d6fc3c1a315376c603301b08cd7a25305368c0c08baccf039fc322b1140b7970e944439ba4a3bd2cab682cb6e18c85c14413020860f2c8f663e87b6909703605ff65014d9e5d4c031815819285a43026bd22a219dc5fa9947e10abac4495b6bef289c32c5a03c71d657f337e079a7c381b171cdbf7f5ded08aadd9b5f11df14b7dc9fb9a532b3e7152e058df74fde3a7265ab8a04e6a5ff702af2871098220f5e4236dce5e30960cc58642a4cee43f6ed32dedaec4b698d0c8a50f6e976c017b43c041d35f37f37e736f7b8bbabcb57415127ba8c94b34b513a55c5015377ac5155d7347010c17f648414c8751d0647c5ed76862af0bd27316db494be875ff84ca881a16d0279fe82736d158342a1b0b9562d2cbde0a795d43055088d293cce97376d1a068249bb13908c391260490c1e717128e338dbfdfe89d8dde8aad40e6410d401fa6734f12c4107dae874457dc502b6ea88a1bbae28a1a10df21074533074735ee7d3d14ed7dac7d72cf9088bcc0dd16325f981d9def8bea198576324c8ee50e2bbd9403864804603846c35d8e8e00431a50624063250399be19df34a4610c992e40d8e8cdcd9e6d88a5e90006704ddea5170c30bd9889c9842e9953c4cddcd26c89444701df9b36f076536f68feb2706418b982f591d45fd200a070d38cbd7c0c9a718f611980636826730c1e06e405aead04ff6db104552e59727880bf0e59da419e0d9a9b36d9c3011b4f18e46027255c9340d857b00b18310f0f8e9ead89325f55d0b13664d28c7d26919d11fb63d8f1056e5754aea4dc9e112c9bea1f3981a4c1c2b3e5241c4d3bfb2e711228868f269f99f4e7addfb645b308a73e9e527702c423acb6aa7b2742e821bacf9262dedb7bf29e3b2f2210c52d16939c196d4261f295cabba915330dbc2ad6f1f0b2810e11e7848fc26c61e76492ca99a7cf89304543f87ea347866e6290d4f8609c7beb8abc1df2622934e3222df96b5f5ce3c4440f01b259ad798b86406d171337ac85d8da428c6f11735c89856eb2c50e363d791359782f6e60125f547a0e50ab529473c974019ccbfcc0b858ce85ca9a2582502361f9e4a08f340c5012b6e9012a037906ba2fdbd69b3f91514bf2b2338da2b4945ccc0f291557eb4f7010986a075a939aaf599800de2159c58a14e70889d7dc36e083db0bef1e132f24fbd84503e24d618507026b55e443e0438dc77ff8f23f70446f0099b5e6ef45674acd309a3b513d0ee2db26bd8ba1915e26d7f504e7884eaf2f1d04e383a473693b2823ad03e66d3e3b5e8bc7378710dfd933a7aa60caed7264aedf60b6e506658feb8977a9a78423bc5ef2091496f2fb86d2343f8cf448be53f9e1475e539e331a129e32f68ad30e1b2d76c3508f5a1e9056d064a307bc1d64a22ab8806559a63272c7b3df9aead48dbbbe1c7200e67750ce550b392d1dab0f40c2246417e6ebc2ea115db3f17cab9899d96e100478e073adb98442c1bc5ff4ac59645e6d1fa508ef836fd1d31bb51c52df427a8eacb4270feb3b697dc5b165e697396ba1b1f12422bb6c9261298e004c12bf11f35b575b41abd59b049954ec55fc35c318ce519a570729a30306faabc0efb848e8735b0e581a85e82316d70e014e39727322f3188f5992f70049a4b037e78018c6eb97a201e515818d41cdf0218abc7b9399d8744c01e791b954b374adeee97f14c0a61203d2ef22ef0bf11691bee9c278361dba8073a4a6d2414052076dbbdb55b72b840f1766a775cbe4e36e9192bdc46a844b1710b008d1adb0104100bfa90dbeee87c91e3e51a2a57b1bb5127c2c341cc62cd2a2b976578c882b09d64ed311e155b8198a03a6042fa423a1814d92a57a9b813c64fa918cc7a4508ff9854e3613d55b57021213bc933b01e036c2840c98c02b7fa712d7415a5b1cbe5778f3329d4b2ce2effb04d35da7f74d98d3214b949ed4e8c0546fd925439dd5d76ae89101f1aa8843fa5f29a0b7f82aeead46d24a9a8f579e1d67f1c7d833792b5ec7f661a3476739c924b664930c566d3a7ca31ad0426e0df52859010744b2180e51b2e8190c6fa5c302b7fe3519a3b5b2cffb51d691cb3cc5b26abee13648e2a2c07a43aa82894a1be5b27e6ff0a3d18e1b945e9cc9762dd56b094a62efc45b233585f6b71f282788dc8a238b3de37474aa632ed9017aa8155eb7b7b006d8dc9715ba47cb735649a8b3787243ff8b03990831572de84d199505a0d82059635952065fbfb21b23b32be4f935252d7313502788c57080588749ab69a2d103de449fc0167facd6462c4d7ca3da2fe4574a4361d9042516e3f3be1ed3ef675ee34b0add99b35de0ce9dd28bff02431391667211626cee007a0f2b319d3fb06e000c11be064cf413f37123b90b543603ba4552735f50d415a70d9ebe079c5a6104e73715ea0d31fbf21551660ec8548f2dfb44f41f523b7b03af2bdfb224581f3674b75f23b48c01cdfc9c028843c8c477fbb8bf6c40cdb82d858a7bf11ddf8b8e2999310f44ff81ff641342441289eceeeede01ee0667070b072e2e534697faf5009ca0d4afeb14adaadc87f03d0e37a5904e0e425e618c73c618e3ced426f23823a516c298fbf53be159c494d29bc54a29a594520c537c2ddc74c7bae74a8a665a7884c0e1d317b419b3a84c8abc8adf09cfab49a37b248c7c95fdae7934cfec21858b77cc798648918c5b05a5f2e2f24352044117f54fd01101f91e5cb6e868b141c70c2a5370f92a2f95262a417266ce93a63fa88231f6cbabdcca489048515e4991443da1bbe659758be21922677a94903393c2161e0b45a63fbfd687f1619cd364b549a6878e7352200ef50c32b44be2803f7735c0090345f1050861b4ac79fefc39fbfcdbd378f182f1c780ed5e5b1c7bbceb6ac2c4d7368d1778f7bae2d8cb2c633a1a7318e72723849e3228d0113f7d22b45d8df0a8334977c8f47307655aa03be5b10ab8a5ac16c36e2874afd6186b5dabbdb86e31aeb5d7f3f03fc6a110868542d65efc22299d1836b3ff39af054aa58c4f9eb1b86709befcbbdd40fae2c27dfbd8de34f8dd4a554aec665abcd9502c66114a4e4729e54ba96d2fdcb7da568a2fdcb7bf6d31aeddb61207398ee3387a334b557018588b50e3667c19280d2a3757ce9d84905adec3a5cc709452ce394b94524a4bb5d65aabb5d65a6b3f191f7d1fcb4cf484e9e0952b48e6dc1f91a75b958c8fb62f318d01909f6419b12ccb1e7f19bfb695b6d77edb6e361e2e5f4bc138c176cf5a5cbe95bb572d623a9029225344a6884c1199223245648ac8149129225344a6884c919a4ac1988774cb4d81ec1289658c753569583e8ae200e257d78e31c68bb30f9a30d2de807514b2b5b96a4a6584d094051594636f08e2f259744091486e2fb0e88030f23d2010c67ed76d360da851430db9463929ac5ca7315fe521cbd378b1f2adac3ccbcfb963b07c68c7c082fa18569ec60b7be23228d05585e58482031effc453793f3c9d49c3824deda17e08579e6bb1fce82d425d73bb3b8b8ec9bbdd3d201e0f97bff5dce2b2874587cbcf64d41b9a34d505616e350245f7e50fe07e05e2f7b17d9fe5ab2b08737fe58310a6d7168c7b1f7f5047db1520a73104424e7c7e8d6449a5c8bc9a31c616a4985432431a8d06fb6a0492321864df2b131cbe4e4d1a6d5bd49c3474796a58964d93064e178f8f73adf57294eea9c2708cf023cc529e708b9a34d9944d16954d16954d16d5b389fe73fa4bb8fc0e08c508a499364018157471bfa70709248a28a2eb542aa6ba4e69225a2567e8cbbb4dd807972a8516643d30867e0a2cd0ab8e69930b50d733708ad2323db4fc5e338a53d70df2c4b0b885423286dc4af196e863ac936181fcc86970b8d9507843f7960f63b094732bddd01d238cb1f6ebd7805f5cb05f3f460c7ce956aa75bb897b471853b7128d17b7b4602211f69f65ffb686423066beb0c5b0bb6e3c61f02619e0ed2c140a85e69c76cead358c912a601b86c52df7508680dc5e8861f796f2b7127df9545aa094712c3f5cba899b6e5a05c736f913df4c56c165a034a6d48a9021c5ba7b590172f825c8e1ae468cc07039fd1b18e20bf0458d1ab6d7be861717b6d73ec6b6e1848991e184e975c5e19732ada2b4e3cbf4e858cafc4a8ddf39377e9f52caf1fbb5d65aeaf8b5d65aabf9bdf7de7b3d7e81805cae2dd3837dce60d40dc3bfb080e52e727c83ed9ab2008c137e1fc609afbb7462afb5158695755795e532501a586a1c48c92264a4df097fc0a4a9a99aaa295861abceff9a7a3e67cc262e65ca347364cdf3f79abf5f0e3ff4a4fcf0b394f4636093c9f48206a9e7cb0f7f0d0efc0bec7fd284a6c4f7cbde7e34c42a5ee4fc8b0cdb0fbeb8df1130c42a78c5f329ceb0141e1fdfcae3476880f839c66b69504981c81187e68de5144f9a126442007cce8fd25048ce4428b2f67274c31e32943882e0407120c039610eefb4aa388430de00e48a31ca69ba2188524aab459de6bdf762cc068f0d1d1b296ca305618c4f96e18112b6c478c3468c514a1baa1b3eec9e3f9867d60d1f1cb26ecc1069b0e2908523a571c8c23184c6142971b470004108278430cec8c21184a306eb46931b4260e18882952387432992540659c68812ae048060da811033f8c00635bcb047b89d411022431dadb5f6b26c0481671cb26ef4e08615270087ac1b7ee4fb4197d2b554beb0fdbb7b064edcbedd5d467dcdf8515f5c9a86743245e81844dedc70627980081f00872c1b82588891cf98289fadb4bccaa7847038fd99fbe8c7d780b8f62c2d960d2a3e35109f8f5d7cce9bedbbdd6574cf9562153c3ff7ba7bad8096df9ee63d1a82e65e6bfaeb587079edb7ede3b600fddd7ef1fec5fbfcda6fdf8bb7b9cf9f5f7baef3deaee0b24743d0df3d7db8b5d0f279e3b4fcb67158bc4f467d712d1b4ad1caebcdf2f0435fa73ce5a7dc6e583695a295fd2a4fa528a55b40b77be4280b40f9ecbf2e03cfbad73e6b5fdc58e8dc6b11e07972b8808eb2bb0678f6f59359f7dc86013c4c5aad7d7c6f96dd7bef66331fcb54a1e709ba38e132091affbd89319372ce99514a69dd4ab56ad9576badd55a6bad1dd5d0d8e7d4cae648d4ca555151c7c5ef77f8ea5487dfef2af03baaa1f1df2cd33ed33e7b6d0735e1f77a99a66dd96b5ba97beeb7eda6e3360c8f6f5bcbf76d10bf8fefeef6e9d74cdb6338719a5790b1943182eebaa7c44d6500fa365313890bb10d537aaee03c4d1a6f6b2ded07331a9014c9eff28030305a6bd268af0923ef49fb019cb93940917c4d7bd1e40067e4dfd3cb47c0499ee4cc54f1b8a7fc826df7c9356d4f26204df7dcf78ef7a9babce37464bbe31e404245dcdd1f8999040abc4f55a7e172aaa05d2141f77b52a94e1ba5a1c98b63c481b98a14e0596a3247d01d6fb5d6aaa363adb5dafceebdd706638cf1c8a4b1cdc8a4b1444994cb1575a24e87afae823647260dbf632cf2189fee9f70ce6fa6541680e1c1f038fd1374413f4747ef18638cf9c95c651d507e8c292912631a714a8adc5218a27b09a110c55ff1df8f81afad290c913fea400a437496278c15893ce79c73ce39e79c2ff37120fc39e7bcf7debb827d1cf9f5adfcfa726f2a322af2a442a3948aadd65aa9b0d65a4b4577efbd970a3d7366ce6ae5725181f105f005e6f43ccfdb3367e6dc3b2f11329cff0245332129c22d4018bcc20b9c61018a54e02ac0ae8b2bd8155690332f45b205482380de596efe77bc3f97d7e240170db4540e7471d2d363a6a08b499f48f4b8dd38f3fff9dcdd86febc336c80861a3570a003393678e0061b3770e438e9c021871d3aecc003d583871e7cf800636ce2be3fc018f936f1020163e44308a1350261e43711fa2522146010cd11223345442545560563e435a54c5aaa8a5511298cf0b1e2410f1e3ac82800893bace5c1035408a640a1652343f8b4aa7e42aeb52ba303149490f2638c31c64f8608471ce28c3088a616862e68a4946e284530063e8d1b88774dcb216887069038a2891c3a3491c28919602ddd35399233d7629c0311ac1c8450fc1cb272e0000e53f0008f44223b72505a76f4704352ec0862c710941d4576bc60870c76ec60071174a041871c3af4d021081d7a7480810e37d021083bd0b0c369072076d071d94105de0e31d881098f1a3c74f0f881470f171b30275420011048f40882144c1081bfa4acfa74b57c9faaeffb5cfdf7d922eddfbfbf4f26bcef9348ac0145a117970964c5f7d983c5f7a9434f1b54becfd4cac79487bac5ee98c58a90cb7ed95db4fb09bc6b970fbd3c354d1a7aa2a696cff2fdf2a1970fbdcb67bcd9b8bcfc7d71790955fef221974d4f2d1f85059893bb7c10c66cc977e42f1f72093dff3ea462f9527ee5f3b8cb68083ae5571e05c56e0aa0c15352565698186b1d0afd4f91a84315f63dd6a0281fdd91f251d4ca77514b34ca67af572b5f27512b1fe75af9b6a8b3f269f0339759a2f55f6de5cbb8cc128da5a4fcca8771952fc563ca86300665f7996373329ff6d801153a7ce96c5f8f2e14eff42457bcd3539f39bc53dae949c67cc538bfcef96241e600a971572ca40702160e4c1ec3e0044299a92a2ec63d6aa2ab0a05ce0c008ab82f241a0087bdbdf5f1379fce18eb172705f2001e1f7e5dd4934a7159ff670a37d013982f8e133f052974b72696947d4c055d602e3b4c631996310cab95889933dd653bce0a5471506bc515b2a4788dac912f0dcbb00c85b117f6c25ed80b7b71d9b9b2a8a2a3d2b9b87c95159798eac5ab5429a2258521a1f328b751ba944e775ed7755b101d39f7f2a1a73f653f0aa677b7639c38e0713ac1e36c82473da70abad03eee2dc5f5a4a1799b3486776fc1bba63aa6ddebab8471eda90963ad672814bfb7982adeef0b736de79ed10cba6797a7035d68f5b3fa58c5d9a5edda8287bc2272125c6e305619096254c54f46266484c80c4163082a8a2682e4e081c707b0d60328091c4095c09f1466c8c112968e286a60022374b7440a3ab45d25a13360b14e4860e9b8f1034b8710968e23583a44f00111a48a3031031d301102eb44847bbac1ce930e2c8770ea71fa619e80dc1cad3973d00093399ed0399a58d6a906cf3864e538421426160e271464cab4d1a428521ac38c0ac0cd26de3bb92e83099cb64891a44ba94042157c06fdc8b58451b1765acad5b71b0c13b0895970ecb38f371826ccb4e0d08597c0ed87a548ca1821e59660ed8c9052ae6e4af74b0dc110da07ad3964e550b920c78f1c3d72f4e4e0a11c1a9981a6417f40b372e44c8e43568e1b261338682dd1c10e392a1b8ef0a2c280d710d41ad22921201aa780062db8ae185300a55ce11614b931438e377c58cbbab1538401e46b6fd050e8cc21eb46101858ecd251e81d687b4ac106dd5f069a65a3b571c8b2818323c810f912bc81a528a59c136e8ce9949352aed38849c48f30c64eca52fc8da58852190472096da5776671ce1dd1081ebfea483b513b309d2718a3a4c98b09904b0913a0d614176758a659ad668ddb38dde9134dc9f6e24eead72513201944891ea92383c81f4c640dd314412f26530401c521475444523d4388a422cf1471473c45d314b412e131be6cc949895018a39473d21aa59c93d25a69add6de8bf1c518c3b22ce72c674ddb368edb38aeebb4f6bc9315149694904a8bcb4a271fb717450f171d7e208e492aad8828604e3a56b8483f2b9e5811b4820969b5a2c90ad70aa0152fd2cf4a6b3f5a69d89d3481c2133fd4a388a01c1813dc60a0f0887d0ff5e0f4e327fa917d0f0571fa281f0074260cfd939326b60ac650faa313e8bec2c56987e900e0e1f46b6c2300e81101a96163878f20750a9389b422fdb45c15a8be6a10cc496322778f217e8c34a26d816e3677771a31c6d6c9dfef0463286722feacbdc97ce6ab71b18b3a7af144d56433c99a7ca29a345a6bc2d0c75aefaeb91315a76f020fa742e4f0160e594130e1396a231c340efeca592061012467e8b3502267ec7e82c54aceb4e44cdccfc215474c68ed474ce8ae6d27af88e545419dbcd69a34211dd25a9cb66c264e4fb609393086ba30ecbd904b875c2117a790dbbd2587d3dfbe00a0600cfd93176d915fee17def0c5290b1414a73f428166b1e2541bc2e9e7af07a007a79fb15875136ce0f4b52158094e35229c868e708a84d3df215fd07d532d1a304c1ef78bd2386d4eb7274edf049e8e08dec7fc1d0648473aafebae4742d2455b493fdaf3482ad2118fa44477279ef75d975f3f497b9ea75f67fb15fb9bf2dafb3e83ea4fd18fa2b3edddf5b4eeb5de3df2a8483fa41fd20fe98776f242f183471d1cb280d8d04f73cef952fb5d773bec7367abf71da51da5ddd7cc4ee2680fbb93efbafc5ef75ea6271df5aebe54ebbe7bdd7d4d929a249b5e0f6c73cec955595f6a3549382931c9699da669cf4929b93b37f9aa495293a42689351284126e0287ac207878eee69c73ab5c7d4e23adb0a9719cc6715beb24253899d671dc6bdaa6fd06c3d4b8dfb6cbddbcdf38cd7bfdcde832348db3415cfbed493fa41fbbf90d0176edbd1789fc9a296526e52bb9f7ca7baf9d32ebf5759368d406616ef7dabbdd4be9b66ddbc5b68d8464dbec8f213c050e593f4c3fa6d87e30f94164fbb87d18e3ed5decf61de76298d2fbdabbf92fb7656c9a96afcef60ccab1dd69907e348a6778b56eadb3de7b31986addfbb3be789751b32d275693a426494d120a056925354c52918e9056d97c1397e190f5c311fe6201a16201d1c302e274f2f8f17679ec317efc27dccba8aaaee3f6e6e9bb67d01954dbd93ed667d0c7b0f9a41fd20fe967564eda1ae8675dbf2e917333a7d5ace40c126d638c366a5bc95afb35ad6cb3b1f7839b4d8ddad7570bcbab3c8aa7dd5aabf633244e0b2d6bd5fe5e0d86c971f48a9a243f43f2165f35a0100eaa13b5d61670bb7b01f65b7c8db4f565777d79417663695fd6ac6a90d89730f504ef336c8b8f8f6b666db171f06f2d3e493c80fcbaf51992b378fb706ba1c5ee926fcf6277c97145b12a96c5b658cbe2ed96b8b2b0d5fdb2fd7dede377debf7c0939dbdd6fecd7d4af41120f60bf5a6bfb0cd961aac56f6df1796b418bd725f470ae94f029f6b5ba95eae3c8dd23b6b5a0f1fadeaa8487b3d3aafdabd9e4257278c610726c9f72fcd916da23e1f6b3d7587cdcaff8eed797cffb3a2f6a59cd2abb260c7d97dd716877bdb255760fa5ec16f473974feac652a4396f3561e8631dca2e2dc7a2f4c9f782837d7cef7b7d69bd5fbcd5a41161911685bc15a75dcba91bf755d57d9c8d6e2d68fcbe68d2221da69ca04dd09a24f100b72649cdaa26496ed52489275896944b0e3e72f873c8e241891e82380a1cb27a50711e8ce8f1a309d7b8ca27c0c4fa4109d60f35583e2061f96004cb8727482bbef283dbcf9af69d446d0faeb177c136de3616321e33a62415e987b49233d70663797becef971f7fdb5723d39b1d8d18a47cd21192922ea2dc4b52ddefb04449f9fb3592f48372bbe7ae4a27ad62dc9cec33628c72e374cf7dc7bbd38d460c77c37f494a78df7e86c4912f03c75f9f9c77db634f3a120f101feb2424bc6f5d72f911fb8db4e2db7fd767d07bbb8d837bf7f8debdb1805fc62e4b2d729bf2d17277f74fee17eeb1dfbe1ac1f8f1c7ad85f8f8492ad2910da548e23e4376d28a637a2ba93cde38f8e5772a18feedb14d65e5c3f06fcf61bfddefda897bf1ab6cf8b14ddb6cb4049838c61dc61fe55d0b95634e6e4f3ac2f1dbedd33fbf95a71fcba37c1d496575f849e6e917b7d68b7fefee914f9af9f7efce946fdfb1cba8c9b671b2c7fe6e9fe302228fccfe7a1f79b8b75fb0ef1e77ed04b75ecfeb3e9e4129ef1ebfd63da73dcbb7f2cda0b8ebeceb4fc52f0fc7dfbd1e6e9ff4f3da9354a41fd2eade8b6dd24f3c016fca577eb040a1030aa8004940314135b13808c5e22819a088409d503da05640634708261822d5840a7c967802d553001ba04e3da0842880121e52a4c0033503ca068b0b70031e47701e48f0e8e1e1c383053c5ec063c5c3091c2a282c21fa90d671c2c13b5401c7505c7e570484918f449744b74467a46ba27342fb81da9d0e8c91df01fde0f227105e4d3855788610e79c14e3aa6975e25882904268eb163195a28961b7d2c7d9d956923f6f64b6dd4488d9aff8da9a6da50a21c459d3b8ad2423c4302207273737cc029fcdf9c9be6b9ec9c76a92c413e4e318e3d31f5dd38a5bb5a721e41e7a3ea004d3ae9ba19d7cdeb7b590e5531e58236fb68d7d3d72fa699a57ad3febbaac3b1632de6dd24f3ca1fbfbb9b389d5b5eb13dc578160cddc78286945fa8927c8c73eade1fb610d069ab48a9b758135f1c91d5142c999765f5d4d98fbbaf5ca085f585326a70d5d0906a05b1e2ee1ae694911fd1630463eac2320746ff143ca6f015dc011101da18de59627eea0663bef90a29a77f0fab907a5c36beee1f5332acb76c7f55f6458956559ce913336f229d7548540f56baa1251d5232fe0a4a19ffdccb2f9d96f99f6d90db2afeb8a7dd4da682df44c23383171f999c949f7cce4f432bdf2ab498c3ce6246076c99927e4cc04923395899c81f1656e82cb6c84cbbc047d595df3d3ea073f7e9657d7a4d176f800d24e5204444711942a1925eee15df34f7351eb33137b6018064421fc081379d031da6b5fec438ae3cb97f8109bb2063ef8c721cb4710cf5183d92659a0d10fdd3b3e516a9e73b31905a17b5565f831bba19422962045128e7ce85e0297df5f464c198140f77ff92324babf94524a694f39b9aae6969147252749455af10498482ad2aa074204c999ebaaa605532d0e97c8a959b500f5221d21a95845b8583a44482b999f14c826a0b576c9c7af2be8e2aa302ab9d98a917b8d949664c593e769758972fa54b318579eb1ede6d29112dae69e9d5f2f6c05ba5b1faf15e85e83b0ba031f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f9f97b59f5fe4a8d91719c629392574968231928e88689749c41d11d1ddd6e051fe8888be1f794c60813827a554ca9c9c5a6bb5d65a9b7befbd49e8245c2e9c848e0fb1943674ca7bc4fee6027ca1652d6f302014895c8010a61a813036d95218e5dc463d5adb2f302ccf90524a6b0bbaa81f5ffbb4cf9af69ca669f961a75139635fcb2db87c5f1326efab7d1486e5f9219c587dcd9c737e2c3fcebfe5a77246cb30ece74da528ef16747179fcaa11dcaa4ed89833282f619443f78ae467554918f8b3ea28bfbecbac91d7a75f452245308910b85a18ebb46c0bb75c745aab56cececececececececececececececececececececececececececececececececececece8eeb08ef44e555429ff2282bcfa25786847409293da44409292ddf6dad96af6fa16f09b5b46c5bcbf7b26d182d3a3a2d5fdf42ad50e85b36cecaf6c9a8ad904a8baf3c7c16941595931414949393939313cff33efbbc23b8d7b4e75aad566b6444abd5d269b99a20c2082153dc919018c569f401931026427e0c81c4233204912143040942248812593ff79ede23219ed69ee7e5fc3aef9110adb3d63a7b5a8f3e204489109e2176d88c848c848c84f8182131e2a9f6c505edf3c7c81e7b0d6ea52869494eac637c0a83da0827ad95abb50443ace2c6ce682995b6d63aadb5d6527befbdb75a8c31c678a4a3e54f6ca483b18d91ee4b6f34fb1f8431727e969b9c93525a6badd65a6befbdf7de6d4484963f5d2e22b42d5120a02e03a571a57e8d8204c9e9604dab669544a6a32126a5b5566badbdf7de3b3f77a321b0d110754e696da5745259ba479039674dcbb2f679cf7d0dfac505efb98fe1ed187a6794ca8aa7d554d3bed2b602e45a66bfbeb66db88d82d0d9fc6aa48cc97d32501a999c3ac8ab56a49262bf492bd26a3e7d6cf7a8a2a423a49f4c24ab724a8a6eeee1b302fd50817ec854562168a717dd35ad9a168529243549b41a5a8e663a8d7cb0a714bd473e68a71a2453a29a349cc63a72d47e35d24177ab9a34564747a5a3d251e5a071b04e7e1d395333a644ea5a200b6499dc8c9154bb2a69d9daaa4ab2acae918e0a5499acea1426a0576d528350da3ecc2187f4b38384c44766e3246732d48fda9233ddaaf884765595e8f0a8543a3aa49f8ca4cafa0c7a9fdb28b6553c2a4dfab1af9ccab5098f71635ae39063bea31db40dc236c8b66cd06887c6c1bef26e5bd9b65c3963f29233f05ed4455d1fab9a24dbdab039f6942367f049ce58694f231c503ee48c11728647ce5c949cc13f82c81939737fc443db87a31d3af26e7b2c113913b792237206df950f20de6d2b0848ce749b6383e44c8e9c997f4d366c8be583880db241b6655b17bbf4504d92fb838a39e7bd72bb97a4e27ece8aabed70d755aed639e7dc9efbb63be7bcb3d65aeba4f776f5f34887c6891bd75a2baeb76e926abba94b90f05a13c6be46e1f875108cc99bcda847c7bfc1126ebd25dc662878a74b9644140eb712a63a08bac04d348482c7c74eb413b7f1cb19fe88f1f79316b72db9c1c0b494c2f1074117293408c65829b8fdecc34e73dffad5af6fadb5b66e96d42635cfc5ede73cc241e3dcafdcde544ee5ccdf1d53ec579a638c5f8c3546daa23f77af9446fd9ad5adad8afdfcb078ef7d4c29fd9a16bd15c74823addb9a60568d4aa0ad35f1f833065e9f7edb523a6ba546d096b4f23c2f9254a41fd24afffd344905492b928af4336950029f3fa8b4ed95663dead4d000104000a315000028140e074442814814a6a2a0d80714000c858c466a4c9ac8e349962331884296006388218400800101919119221000605dee90e8f9ad514d2c92ec833f110d71489e850eca1a59de7780c6b88d73e870adac8d3a44c1a5bd43534ce9f228628adbd1f6f88f8121969d5b119b0af3be70166f8b0c3ede2413a1a49b40919060c4f102387f87bfa5916d89c6506b07566d24ec043d0d08c8537cc0952b7909d2166444067b51e2da4f0a1b310097efafba828ff8ef4d85b7af8385915dc4b1b775ce74a2b745350183ea658966a1e3e63555388c0d6ebab7b74f34ae8919b52c40b5fe07ad0f637393a1a5f48bd9c5e0ad5da52c9abcf50ad87dac33a7578de6ff838d3db5659b713fd14a509ab733541d2b59052505555eb3fc327e21549c426c151a3e30015919c609369db17d2102c4a776d681b856a9f0a5dc6c107ba6ef817aab0ecc10a4ac0a2b1829a9a13b1864ad944bae777b3800a481aae4a974620f73d8d835972adb3581a74e7cd9c91e488d457b026beef6adaf7ddf2148a8fb7e17e1d36f9049ae44b13a08296990de84ccfa37262b5411cba5d44dce2f8832e11325f6ed6b475f020c26abc9989dd027885e69626bb4637ef6770d867fa7efdd7ee61afb73d75776a238594ce6959264ab533439fb8825263211e1a026d9ec9501c625673712e331f1735b877c57379354f62c2703827be314d3262d263fd2ca8f74a1a6a59124659e408d936c9b750655e388fac3456bd65dd5ebde28b3474ab37f89903a06489b2ec7332c2d6ca70469cf35735c5a0f87814d423b84e82fddf52277d2f8728bafa5cc29b93c6eda15381d32cd6fb950921a3a0f8092ae5ca242dc5f77d2072cace5760aa4e8a4b391441bd46b48a6dea8bff5f2127b49146f78eb374e1f7c3a75409f0c89a811e7b709213d8017bb85df47a9db3e09ff8064831de2c810f1b56a495e8f0cf9b90432408298e8183c32d2209efd0b9d80c21f10fa2712fbe629400bdddb57c3fc6159ff27fa8f362a8a94bb40c1ad648acf6c4c861d244d2a19c9106b9ec40cfc05ba6934b8f86b918be8b5577080b9fbb0465634d9b2e20cf66e4708ebd16b536f07d5b38e0e6ef4e20b75c448967650745302b6d119a3b27e9e1f4283a05e31a8247c3abc1770249231af666a912693fe3a7a3a1b6e7c50f3ad50535ba9bd97f6aed5341f5c77ee7d7357af13a0cf79ddc8f417e86e0fdfc036ecf1b1447a9fc5cfaa440874f30de9fd5838588fd585811750be3b6d5d134ebd3981ab052139c84ee6b81eaa02b8c10e78da99491e16308e065e0f2091021a1cd1251e706b697f26dccc399a2d045955b20e7359578bb9e330a191b7b8c5c5261a23d30b6fb32b907cd1de2230041eedea0e6b2cb9ec49e1ef38f3be73e2c98c2c8acce4e455590285fe46ba090ca6d97dff53b493f6a1b562c39b72d358a1d800f41e1040668282c3ad57785b2b414fd34675e92cf486eff9bdf4a465ccba4a31c26506a929244bec7ffdc6a8829ffcddd6a755a46cda019ffcb0657d4b5af670e692842e005ef87c1220c146cbecd03bf41a501fca6403e556150b218e45ed6fdb2c7a8ff7bb6e100f0e25248c3b35d9b3184357d8ece078c633609a948f67e2eaead27ef605f241dbeb59a2cf44ecee31ec70dde518ac903e58980561191aaea3967a92e452282f3d1a88c913c0f39a056f6ea1cab0ee0c848f260bf529eccd4a22c06f866668dadbaaf2cc435d85928b52f0fcb180e3a7f4c11d48d33c78453e7719180ad204567ac1f67bd932e72dd1fec29a5c04b480780df968c584b42ce5981ef3f5229cc2429524e9d7e7e052230fd9f6956ef33587c112a6cbb3f7b19bffabd3199905e3fcd3ac08dced0497b936e915c91e26c81633f95458ab62905595e0a89c355285294ec36e833f266f81fc9d9920350429bdf1b2d8b0ced0abb7846e1b93a84734a5269aee05bd9d11950e26af929632aa6506941c0a20f5132f5a371cd45fc56f8ae2efe41d501e455a115e4c512e135f3c77a1fff44e48916faa1a0dad93e861a25d6e83a484936ea8d212e3a883f18dd0528fd2e5af9bfe1fe0dea984e079a8b27a35bc97f1b52a21eac7669e03de4b56d7bfdff544181e5b38c07a9fdfbd4ac8cd78edb44e007e59f5b461a2b207b47a012bd415047bfa20bdd93f7bf2a6d610425d58f932c5fcb63afe4d14fb38c2bc6a6e41ed8ee4750068f346ed13359af1d2eded57864f02e0b3fe26c2bce04796bfb26c66180ce8ddcb9c5ba317f4d66dd6d708f33a32beadb2f4be615461f0e7ea251f1361b6dcb73e17837cb9e573b47baca07dce84161755a384524a978b48639fe8f299aae5648456faacf8b272f1c00d934329d56c295cf3882bbd7cb6c98d6736e269d2a9cf747817bf440cdf6cf7eea2e1c2883f9a2411f9165a3ba786f3a8ce0b8719ab2ff742533b05a25368307f5eb3dc3409e092538d28ac12677b448828b63575f80d3ac9442fa2766d6904a300d2d4df1032a34a70747a06897b44455e0adecf92965ae866dbc0578163f2ad6b9b799d000b6ad704bd9ae2665b42288951b17c690b5a223c34fea3c103f7a153a204d4cbebe8e82037b746335fcd95d7c74e835d6afa91a2b3c64e0b97cd4653ea44b30feeb2c9a8543935f6825d6a1a9929c5e5de6a93704ede887e6f15499bab77de406ca1610d8dd3de4282ecc53b0ad72075507556bf7b5501a3e915c42d9ace84eb7ab5d5c2e6246ef021aabe24faf37553d520efcc514c625084591bc521dcb70561022ccce7cf4ad1355590ad0b3b8c7d41d80ffc26ab172970dbdd4092c9e7e33def8252468670bcd441036ff034f0679a8a289ec4e2f27b6e3adb8641c3d7643852f364abf817cdaa6e9cfbd6dffe0e3e590cef9a2c1ea6e8d98e66a73974c1c9846299bcb37363051b784caab43525e02cb3464d6435eb993df99d1dd13757353fd27b4892ef54d3b32eabac514c103f5dc7cc4e379ca6680b76f4bb5cbac7360563185c22a2b275515ba614f5b574f6b4a3e0df34ebbc1f99366ea60a32ca344934534d2ca37653d60c3052062fa09af61e1aa1968d8a794631e778fde0e557b2784c6326540170e2a444639bcb24ccbe56330530ae1999c43c37b7212c125c5489189d52c74ac4e7bb9586cfa586435c11d306d9cc83be5db837b1ecb72c12508e9608eb31fb98ee8435ac98bf44eb156d8def8f03d7ea65f27b49f97b224fac074b6f22270c66e1d6a1b2c9c501b0d1ee1e569aa9199699851fba3e8b9700a262420d83308c49c6ccf87b7a329577031c47e324bdfd345a985b8d1202af9f0b6df998e03470fddf0f2e1027efa4d252a0f4b885728b9aaab40e71e878e1c737f79fd0b52c21083c0ab5d5cdc5d9e4d4306dd2f8458300c68b029c80bab53ee2cfe955c0ebd030237c8c267900d2a64a92e3e729134ce42dbc1a25742ad8418db507cec53b4a1ad1813a204f7efea447fce8011556bddcc8d83506641a3aa2ed47cbe59bd353143cf2f913997a97888c4438d6dcf247a40c12d27d551dcdb323e4a8118a2c95febecb531eb559a74a39dc232b59180a86e61293a559ea640e87bf87694e3c9dc0617585920d93e0a5c5051599e4be5e84a721f5ca6217c961dc60e806b5b6b1ab0e19eb8ce07243f3f001bbcaeb7541a7b231bc210ba3971e6e1835c089bb5c53955c17aced173312f299955259dbd5c29697fe784fbe5a4f198cd14a22d84201fccd65daf9f7955eed2552c217936d26d21c4ff3e585e353412f35fd4ad15173afc5458d51523a44ef0abaa03952cabc5a7b412e348e3a0a611947af4a52a44bac788d12f84250ba9be6e06f71c6487b746fc4343a84683de86caaf403bcd83ae6c4055eec6e1d06feadabfb2d3a7a9125f062bb08cd17235e6ca5cce568494969ee02bc8ce11b3c05e8bd1aa41ef54774e065e06a742777fd0f10fbfdb3ba9410c07313c4cc1ebc5876e74be79bc083087c5dec9bc7be3426168b6259f6a1884a025c3b175b27da0289acb2a73beeb19a596c00f1f8817c7f85574cbd6d1d28453510ff066b1c271855eebf7ecff3be5125837ad24c29a6bf78c23873b70c286fae56794d792af5e15ec41c84b3e7cc8d6a5d7c81aa91ca8b24f53342aab9d54394b02f96ab5f00a46f514a21d5afff54c9c5bcdd328372200de6cd45699d7a21f22045118fba0d399454f72615a54aa55a96aaed7784a9be91ea7faf6a69de97d3e2cf948d4339b7a2080c787b71c1ec0be94f7047da450ff1b7ade1c1b36c852d0bdf8215fe54dc72c49f967c13b54bc7379be1e4a0830703adb1118adbd250cb59c4250d5808121e500c5ebfbec4f63141dfacdf9c57685e4640c3a311e7be0ecb36de558dfefdbe242e1b5f29766ba5397ca2287154e3632b2d9f1f380731365cf8fa7c3ab258cbdf675902e7e9e3c8fa2b1ab527c912fec03e472c5f1ad5e68613feaf58bc9fe7ac9732443c1452506ada72f043e979704b8ca3140585b7597f5cfc806593e57049a7a99924aec49e1f41f85d37a8181f432b1bd228fd9f270558ffd381f1565a30f19b28f647982123603edf7ebffd7cf2d1bd14098775b0178a29f4e2e043e3f6a616f48bb67da79c452a615a563c01fede9a37e167b2dd096d1559363b3ce17cd8519a5db560040715fdd50c88afd13e5981e658bd92aaf417a21d9fd69e3c79006f37af668710dae40993263b598858494a53b0c13b21335ee46511177a3ecbd5dbb572b46f10b045fe0482dc7cc9c5ae7cf226a90e0c8c2af0f262924cb33f8d0d97ccae30893f06b7d23d540a8025531a0404759470b7ee15ab35840ce7a9775f41707edf88ca8adcc51ccf78c327c0fbf0a4ab2fe269d3fc8eb6b92feb0325732713453c953542b8fead89783a95a2cbcc748f51f0d8219e340072cf3668d2abf7b6f3519a13e01b10b54d4a66f04d32300e4d03367cab850d2fca4d614f7cabf59efd45954b55cbf8166c60f20515678d5de1f1edc9ce2f5962411d052c63267ea5b7111c86d5f927f98641485aa88600c98cbcde3a549257be21300a1154f936ceb67dc162415906882837a6ba46bf9bf2c7a122315049622c669e4dbea9cc5f2d183aac924082268f9cf9861796240520157d2306f6729c349cf92ecd8f5a1125ed3b894af94e87856c60b4beca5fc4509b1c650ca53939f38e77839c2c0e41dbb47c7e6ba6f53984b929b629667226fb31a81a8eccb2555352e0fb5c88603680723a5a76209776901a1362bcb420ab6decb071012ec6404867747f3da1b008c5777d23539a1d9485b77f7639e1aeaf146631e6a112e78daa3c168506bb5657d36db89f58fa21999fb43dfe9bf9c812770d2bed492dfdce7433ba8608176152d8d5e80df9ef631d86475b2f7751e540ad11f22f52905bc29c6d9d3fc36efd5e94fd459530e65de6dc5683c9699353065bb060a2dce21c0762bab88bf0a438ae79249bb1d6119c28bebbda6a08ac2fb4ea54b8504af40351d2b2e7898df88bc9a5244b20aab8a561c98e34abf691a343df6ca19dd4e14814916a89c1c09e63b33a8e9e0d6ffa7d4d9de3fe2447a8b5ee0b05bcbd2fab1e006ca071ea3b8f3194e2f035c2024ab75740edf61bc6d2f15a4ce1ff6a16ca41dd50e624cd3f4259794a395740852db43af43c5b104d139f6a969af514c00d55c633c1d1d6f236945cd7feef28f63821456b26c5a554129b91958ae31eab65e830be3e05260530b1bc92a8119040bd0595606a0d92f4dec46c670626057726de0c051cc95d2691e19f6b7eee2a10f7b6a5be24b0c49e87a4d2d6621aebeed8fc1706a35d6d037c7d935585e22a25e0d5137c69d54b9d65c52d113701d492e45ddeaa861a7016c068e4bbaf9fea9884c830e1f15cc10b27f016f1e6f67de7abf072d5cddbf55b55f7ad75eb9da02ebc21df37f5d90094a1ddb8de8053893e2ccd0bc0d25bb61546bf9d3e045cc293d59a59118ff6c60610da252db76f7a8fc22f170dd2b74452c8770fc2113101872f3ea51fc383780e34da8b4d82cbca2b2f70a1ed6ed3152d286cec603b5a19674c807bbc2e52e9a1d89091704d23e2bc7317480fb598f73b8e4066585101a779eddc455f9f6407dde799145f76d1439967fa3fbcc0ccadef006227ecca4392071dd6a3518f1eaaf687ed3a8455f08637543814def186c2d5f22f2958f4a2b3c3b232cf210c334dc4fee0c286688046d062d7be4736755f9c0d6026df00212365b126380e4e467c9d87da1f5510341b63b69d0cb17906d784527a6c87b0627c63a925c0f4a51eb4ed479b31ce0817c73152189acace97f91fed0164c7f882f1d8dec4519b53cf5e6ed5a277d0c0b9930214dc499f37f1a9873a1fda503fba6a49b64f8bb3978a8e6d190d8af5b567291a9fcdc4ac5cd4ccd80933fdee6622c2bd774cdc48048306be0485d63c6766f46b12a23807d1fbed144a545af392399af5b10b79ee48c0f1479d7efb32f8884cd8a8f3774f5384aaabf03ff3c186d3c399584703fdb5c3c7bfa0d755c10af0f089a89f6392f249d93374fb5201af5243a0105fc4eb252bd8736a7be87884038b815f91ba48c34f3e9a5c674f4deb1fefc1cb856d305ba8d394fc94456da1521245fa1d242135cdbdf5b886cd117c69b88690e71fed273273810ea289046070f14430c0decd96d6599a7f6c43d93eaecb882af82e77aa47e911039e8ee75a1a00a41628ed3016de530213e6e532faf8d74936724722ca7f2e5017d5a54096536575578aae90d05ef675fa50cdff8fae22b0d02d3a467a19a8d0249908af1101954b970541401da3fbd0f8abb8d33ad136341ce745c2740f6cc57ab461a50e26ad7a03aa1660507b9cb5f934ea1716d98cce73868e773e2c008807b8155d3a4d0370e74e355376031737cab58e1abab2011290763d0d87e1ab5cce13d9265f1de153137e238be68f39d6f527143f8752e28114bd6eb0e40ed2adbed96918f1f10b545e50231b3760bc6b0fc38cbcf825c488f8d6560e6bbbbb7f1a51a42e36232d81339e5254cbd3711bb3d69109f9d3fd4836ea7ad449df1db4da3f0fa19dd78caf934be4d3c2c95e83500cce27e3e4032e8bf1d308926413da51f521d251a59a78375123c256b0aa55947d9a3c4b172dbfa92d4f1db0ba3a51f8b6c96d157acb4b942fe19724e4c366e4312038574d4e2daa4ff0069a7998fa9407e4b407545e8a014acf90c20607453a3d52c49faa0d7840ea23af9ce4d4acb1856483fd2960813a1117c2264b5d7f98d654874fa370fc6427e198d1239c48be0ba1c8d3d310fafc811e06a9cb0a872e9556739bd871521d0d217228e8f63909435747da0c8376e74631acba1308affd7f345bab7f917a234225f833cc3420f2e86ab2f22a7ea33cea073b06f16312da04a470eb67eb51e4a24fd15da92ffcd1d7f43e8b026a7f3765e001e8e5d7398eeb4db32a349ccb7a9126b479c43966027309e6383154525367daf211a9a7f355ff76a67527c108f3dade88ab0130ad229ad66ce5d48ef5caaf4450f3694537a046eda25d30a0bed3d9a9f5c868b79a804e4fdda8192d2bb2d49ed2bff74d8df61c962b9f025a0a8e6a37ffebc50610307fda1fabc88181938f31a2e3afbd981a46906fc10988cbaa915594fff41a3d1aabbeabe66bba34c83e602dd10770b0467b233557751470eac4c773fb8d364a61421048288ce41c174f8a0cdd5f606fc7dee5348604e8b8f7ce19940fe53f06cd45dceaf6f2bcf800a60bec12e874f5544defce212a7c73bcb04d58a5ddfc82c0e43a00452f5c12d733f7ebea608ef41f41065ed2fe5cd6c13f6d96d572fc2be089381fc9d9c4e185fe9bc83418e4e9c8eda78c5fd4251d085ee377bdf98cd9baca41be4f6e8a6453a321da594dd94ca78124da55a837e93d69d6c8d6019f4ad1419c995ecfc9d1132ce849222a251c7671044598a74ea18309b537bcb7d02e05c15f6c7a1e56c0176c0d89e89bf344ec061c7dd68da24089d994a8ace9434652756392176716b931a82b5f7c90c9496bca6301034d6d1df89e72409e1c8a9e7f7d1553db9b7ffb41cea1b2d407fb6dfdc08d3ae540bc711ca21a7de9ed486baa076ad4cfb4eeab7f8a09bd9ea41e4cab053660acadb1e970f93ba97d644c347fc850e1119608a2f0bceafc32be4a366b3ab82d4bc152b1fe12e9b05cf93245a6fed95ca05f4e2f05717d322bcee810f1d36d9dc36fa6cede552d1c755e1c1316d152d215cd050cad38106e4dff8ebb64c30ffd92a22732c5d0485406f282d0a6b862a5aa1a9b0cbe7d248513fd8c9115240929bc280adacf727ff656560d29b5a9ecb3a8acea1589fc1853f27c7a8db9323b21066ffab42e984ce0cb6c76530d192a0a6c5c63e2a6ce3a259cc5932bf5b59924abb109771c0daeddc42f336999e763a84169cd2e787d593e36a214ca06839077bac950dc924b2a41a19d3ff41e649a251dcc90dd83f3601e11c1661f8b61c5c5dfa6a9810fe794144b16dc5ea4a952a6979d32440a7c19deea5d223da2bae62d16c12d0a3831d0e58f7bcdb7356c1270e5dea02ca84eea3b8e07f8afd41bf6880bec20875b25809b23d12f58ae964d42e225afd4b08ef9c84ca5cb7a9cda5b3e8d1601caa97669a56e899774fc84bde6073e8c122f61df14b3f1a7b0d205643ef65f641393aca8d81cfca50bbec23f01550b38a38defbd232f5148c1cc43f13342f26f4ace03a856af2ecf022769df79550d7da1f55f532f2ac9a9ce6b4011e0f625bd696f166c5725748bbdfbbcf5a746549ba100437a854c8def2276a1d6518944843d519d70445918ed456ad003426c8c7c639c2b4a12693b10902c337d37f19095aa61cacbad55300f130bc52e76d9a8b35b3c297853707ad2543f27405692333c06ea519d2f29ad696612411387630e5ffc63a543acb9cebcd955c74a59479c01489a0c93dff82474c255add26a77ed58928c600e26018c8212578b7f8da27c1bef3c72ceca35d65fb5d7002373dc58e9f6d96ab6383b37422bf799a5f21a63a107a945c4e1f8b4e9c30a779cb3fa6677c2cd540e15fb498c522b4f0aa3b9901fcdcc9ad5f443677c89d52647492ba4ece4993bbd0e02fc8d8c2ad38df4f6aae916cf9037f0679af1d096de4b92dcdeddc31712e5d55f1dc90961f29d97844e2c6e9e2ac8102b45bb54bf4945772f255e153e075242b95401d35fca734e6d9182672ddc9358110b1424d20a53cec918f1ad2662c61d27dbed524b46295df79d3812772dc8b1cbe2ab915ab15136b22e394362c8f6bd3362aff3d1c07e25b3ca3c63b403c7b0c4f94991f587ae5840d74c5e23d03029a940f9ada1ccab865210cb876e76c85ce3fa4873e1024fc49c6654204800a7c7af892790fa3889ffd58624d5f9365ca45bb2ea7615474cd8e3ad4be5451ee4f2eb450c4b80f881dc5ef87413a31896aad14c996373919ed9118257dbe26e4085837b1256f61fb86aab1f7e818c38d354a00da8a52a90512be2b29bbad1101eadc2c9ff0f160de43b26f58eaa0778124bc0a458f149a2a027bcf13129041797b853e5ce58226aa4ada45e805ccecdfe2c753c653ff6c5ee324e49932c0e5321eca6f9bebae6fa1e3bd85cf4fd1013e1e323a11f9789c033fb47437e7c0d8769c4a94c6fa4dee6d09f22c8caca7b11fc3ea03f516dfb125754174004bde5a201da174c245cf7579ac294af8a951e9e865bcf66dcba220afcb60d1ea61447a4f0157949d7b6d17ea8dede633414b1a5da021265a08d9ad4114bdc7a0101835193b5d33ed61f5a7add9c7a94ecef8308fc7ea70b2bca55eb781b57ae49b354edc88eba5bb1eb0b21e45a3a82e234dc2f98797241593f6f51009f1c7ec122b99c7705acc4cd57b786cab35c4b454801ebaa0a312c1d49eb33c39ea81862c411aa17d8d3647e61d92833700c3b99da1fb2e55782648e19d0d9233d9d00e18f37ea8446d9c16f564cd372b70c50918a3154c00356c37bc4cb06ac77c575dc4a1abb62aed76fd9aa981a2e15024504c964ea842123739232d4c7121bff44b3d902dc505233ece4de5292e4966f434a0f7bf19d6a36a0a5a4c3b10c9027a844ea0003b5371a08a8c8deb9c3e3ee8fc05fbd2927c85e13f2e252713120d4743910ebc08d53c290a652ea8e1bb34302f3b9c72eab388ce85e005ef424aa2bdfd3c05aa2c1571a6f5a505f19a9d832f2bd2b3f4ecf2801873dc85ba73c16211d9008d13e18ad982a6d041a33c9a6f95d89a5276c812e0fbe444f05b99df6a7a2255ddaf6272a003076bc76aac1df4b49ae504e1dde0b5f8a3d131da4f3fc96bf97b6245f5ffd098a2a6044fdc8093f30c8510835fda741f180f88012288bdbe2f66a3d447596027fe9fda290b9bb33ac3ee8c631b94415081668ed54e201c643c0c7d00cf87993a5a90560d2e38d9308703a8f09fa587a9dfa767a4976220d3f4297a17b413c65c8cc7118b6ccc9248089c5a743fc600869678a514769104155d4ce3e349f01cba5b1069fac9045bbe6bee7d3f9f0653d6a74bb750b411d930faf7908ef37a6a1699a315de9979e82c5911092c5e7e509816208cbd58a05733825f157980ee8248d2c2d80efb92685314d71fd8d34abb93669945eae8c58adfad31dc094b853114ed3a860cb94ea7fb424ce844e4ba738b8cfb228fc7ba16504ec97f44e78605122346a625db8a805c3057b17f3ae2acaae29b33411a0b0dadad5c9dfac332ecde2f71bc2ee076965c2bd3c1a51510319dc1dc444a2d6d8b24283067d510f9a327ba6775494d66ee223fe4ce9435a7a94ddd7c3b40308b7d218404bc52c19005a32c8f34b108aca047d318a705221ec616f63ef2295bbfd23d30cdca705c845566b2900d39649f4430f242efd60a3091790a6a87d831f54047a450a4af442e7e1394e5a4df7a508af04ba4a7c862992b69e9c9528c68327b260ab54bc79480d19b5a8f4776b1b6f3c9a3d77f628a758c7b7a173b85007efb671a07671f5f3f81cbf4a74351038f9d1f9150aa542c4e0269e72c1dfa91ffbc4975b8665c5ded9df88744b00486416051a93f23a8d27f12870858192c99e4f69bd279abfc548ff91c20462a1d800564267e50bbcb42e39c8caf173e447f80394b516e61e9ffb40591b20f641c15bd343209faabb5398cf79674f1eddccd18d716839c874f3c68c3c99c49ff3cbb8f0d0bab82b819daeb1ff92bf7a6b81da1826e5187fc104e0075ce6323565d6f38d17fd8f5f966fd90c9b95356b0a62f95f73d7e5713302598977ffb4829bba4b8199810c44d9954af1ff7cc0931f018441d6f518cf94ae0d198c7a137b2eb31c822cecb0a2299f8a1e5e98a018e350a950f1945ddcc490f5437f27ab89eb8a51f4f06d68d5df247487310ca75790192c149f936b8e51d92d68d705d928537877880019976ba3df5144e23454a236a9a0d5ce7e82793acc6b6df18ad3afa3a0daf8eddbb09021098555f54c23d736a6825cbef50337021d4d70f0c05cce0bf50c73fcaf76d7cc1fe6f37814a2d10f222b0ca0ba30636eac8527383c5473f1b325659079f4d66ea4086fb383302bf3d0664c4121ba01b54b4f0e486bd8e879f4eee53394bf191c6d5718a82a4455025add591a6809aad2b713cf7b77fd66a3c3867074c4ef323d7ba92600992208638ae280dfd076126c219eaefda6aab417056a4abde7be17d634a425f4234d9b4afd1e1eebec684500d2525af0f3a6dd7b8541a9453c76044ef1c83b5a7753df5f10295e6a79654b64c6d1cc44eb0d3f8993543a1767deaa70e49366b85ba3499c3154fe0ae1e789b20032b0f3873c75204e0a8dfca23388a38437a183b6c2a62aa25df6f6055b9c886873aaf6cd59e8f603b33ca164cad24aac3961b529c6666d2afc7e40526c63c7b72b901247a87063a8b4490f26ea4568799e5fb569617953c4eb08c032f53a261155507468e99f1ecb071e46221a10b839c62b6d6633e95d774c4d713a39012728d2449b1e5c5fa2047bbd63f11bc4d9a10c3549dd305c39e103e52c8e6dee126d52cd846d915e28a4a04d58a2c3bdffd29d27c773faa57bd48ce3c3c36e4cd0fd3d351558f3d2ca76cea88c5a1978836b87ad4838d441d3927de4d64fbb7deddd186bfd24928e3893bb40b91b2ae15570e2116f26f547511fa059d6e79b4be85440b2273fe3138a13c5153adcb34e20e28f8036800c2ae03d0080b224ed081b5a38b896fe81e2341542261e5700177a667ef45efeb4de6c9313c541c7eb4bb5d3e3df42004ce5174e98270660f2bac400f5687a745a7074af294de4092e382437cb562cc549fb3f198970226dd26eeccc3b59b4ed991fa0cb958cfc37434d5ab8c14a9b320d588c322866d24c3f47b5ea62b39536e136ef990530f5d1a917e7d28b3982ba5d29ea7b2c3349ab61cfbcb43f2a15358a57ebfe2a51d84a2049c00cd4caea96be7fcd4f224caa6b67799b2626b2c952d4c0ea52b453f56eb7b7c4d7cd829581c10408683706d9ca517fb51b1971ff6b109810cdcd9164bc957fdfb5640c95b649ddd7ae6d384f23952506e2060330d74d6aa533d298c1e2ea72c7649defc1629b1d60b342967f49561b1ad76e3fd2fd5392c05d7ab671533479043a0a364bbb6f36d4c77e63f3041a3768ea4625d5c1094a762c6d5cde8e5a1245a4ecd1c4935c00756245ae17fa456aa8280827ea733d8507146e8c5f1ae8e0365e92d6670b3aad70538d6739fba09308fe70b655139aa79b5ded8290b0a080f20eb316ce0cd27130907aba67b83254eff9d89a877041fb01289acbce1d376833271352325b2fca2eab83563e1ef0239e59c32e252719888b54148936d6ea5737288a325c8340c2efdc70215825e65f1eb02a314465cb9df05b390018593fb959a20477d66d5b1dbbfa67af75744ba5d3913237d439bb1171b48df027937fb4fc581796c4f138f3e1d6432516d562952d6df1b035cef651f329802b95e9236157b36d0975abd396803f5273fa2bdbe8facaf7963875081285db04ef5f2eb3c6dc9cbde724b57e97d0da1723156763af6701845828360f19416927abaff0f6703691e01b798bc16339735235956b4284f8305a126e7059aca6ffee332ab50a9f8660de482fcbb28656a21cef937b32292f011960e9013c88157f70d7616afd26a15900f2d3fbc06117a24b944fe0db94c338c29e56d25bbd2a7dba3357324e6d8074e409a276a2d33159b633ed094c69250e3a34b6712185eb29b10c394d2f21fb8a1489e87f491a1f21fa085a02cfeccbbee4b2212fb54e54cdc209e1d6b5ded13c8464279a9d89c22e26c90f8996cffc63481bd22ca662a9514102d048d690683dc3d99d62a36347c91cf3d0083769a5febc877a68af7fc26c7df6f76a8af134f770ab61943ab56316548646cf10a4dedee7a41ffe8a7e0f23a4ef69bc3582c0d50d7d8c9e0dc106f22b39a297a6ce83d8b2ef372c7c3f3802eee1f3b1d84a254edb74db390eb3bb6b6eff569305011a4d4f74448ad8c151be2771f6be83340a4848675b82580309646482718a2721d2c4ed28826c838639959630f09d7352bf9fd51ba8b49517b15a82b21f1dd497aebc7b766383c1be8cd16df6bbd7982ac3edb49bb62f4c0ab096e6dc23bba51e9f029c46f014d1d41ca8e46fd82bc17b72fbee3fb5a9c0d0f6ab64a21c7efdcd337a9054a514e21318bfae7ffe42e3757bd9467bf9d6038d9e2adb9ec4125739ca5b5cb5674c5a5da9b2255050389880882658d0f397ba16bc624e0fdf176cd82caaec496a2dbc8ca04d904ddcc017eccb3fd769feafda6f280fbb80da0b9a4d6e9815791fa86de1494c15e29d19a628b1feaea01682cd2289ea9b52c742710032a2cc661c9d29471e555d124964ad04ad7ca3ea80aca611283aa2a253d4299f6ada3ff1dd61a130bfdf67a40ef0919c7b2cb825abd67286dacc280b136342b6ad38d493aa2552e7100a3c70c512a795d6e0e22cef1132cc14283e3c7c2745f6f6024ef51019c4d27b7e7772b6fcbb0d640f907e826d0c4dad45dbb0c2105f57cbcf989a15b1d2e6f11cefbe898c1c45ff9ecb651edaefb77b25fc1fc0862907cfc37bb96cc123420978c71d802acc43dab425771300ad54684c4b4520b3395453209532d256744fe33ada93c1f13061c34da0caf82bba0601d80862e5af73f197db5503c720c53cd946a88ef5d63aa93c1b247cac5932a0dcfe2a926f1c8c6a3c561445d43bf74e58bf298fdec8cf573ebb3ea8bb66b27696c846fbc477e311aadf56d6f3ac083a122e5d51eebed6e81b894aaefd58a6d88c8c885d3cab491bc5ccb7efc680f74ba38c684121861275463348aa5f2b4603761bd0d0da2f82e68ab35d801436bff812c8d34964585021a29e08e42a1996c063b8fcdac660ae7ffb905866c536ad2389991c2bfd5e110fe2f1117fb727a27591c2f4faa8c75270144fd4706329548a719c4abfa8eb8184f4fbcbcffccb41fb536a3e933492c53de5505b472ea30ae81d7725a51bdefcea3034f2175fc5c9d9c8e824ebe26277ce999fbb984427b564519b3dfb4a105070462b7ad18076bf21f9ebc1cc20847f866ade603e437ca33744a41d512e17e18d09e0b6810e56fd13bbf92851e4d9a9849baa3640e1a9ff8f3e036a1a16b4ddc5962b0a25c81be070fbdabab3a126147164c85ac859803cb9ce61f8988f54d791a5d856cef2acb73f7f3f5bff3edc227cf6699fb37aad4ae6b227f4d826201eaf892574b4c8e2bb938901ac30608104bda126ffc1f147f720a2ef9420a36905d0ddd17f98d452dd1ad4ce75b9d87b39d90f63792a037573b7b3e6dd474b3ffeb5e36f5951a338da5bf980cec22e3e55bd2969012d6991b4335b8e1b2c84b868cee82068970030de789a1fb89be23276a641247f627d8568c0424d02a10b035c994dc0add902ae671a7073f680bb0920770418372ce3df8b28594576fd526171126e895a59536acf630084554775447a0939852758ff9d564bbc472e4219ffdb036fc27ec40f4f3e69a5178684bf22caa8ac20defc06d4a6a27fdee6417f63f4a2d5211c6e6dddecedcd728624d09cad89da2b50c877e7bd64e469735d2c3a83bb14037e8396464ab06b28dd272c0796f6d59868d1c987cf3bd2bfd356b39985fba507854ab1138a2e0733ae9c16a3e375580e5c34e4e89bc39fce2a9aa7adb7dbe139f53ebec16ef24d61e173026771dc1e9d347d326d70c404e9828713370d46acfb9abf8d72a5dcc91d847a60d4d904b02556f6a8691b1fc1924a6aeedd90c23bec865337824deb38e1dcd5160c577c8dd045b3c92f8c007d64abda620d546460bc16bea30c6660e4fdc9c4e2df68e0b44d179cfed32e56038361f98a8c1d1c6ad8ff45f87ff4980abe40993d6a29c70d0c1e511c5a771f54e3c1628603870f5f0b6789a1df9cbf4b9dfed1bc3f1a909a9996e30e28f36327d44332f2484f00d09100840f18782fc04de004c881f7a585fa8cf632ca9bc081b7a62da3004e430c1e4b5ec5b6227e1f2eb25a32885d1b761e3461bc2b082eb0ed9a207fed83017c184ab09ba891e7439ff5ca51ecce239e39accd4d25ba1ec4a02090dc0df364121c8aba2a5d08922a04c29822e16defd1b4f0687a1d9c644ac3caf6c28cf29cbbd8f034cf1c18c5205d1cd6efe2a038d522ed5351cffd39552fdbd86bc8f1417e1022151060488bf3b459ffe32e121412af8940ebc5b4b6b5121e5b969f2cf793f2c6d1bb1a6108867a8bb047b5b569263bc20e053a6e434358e282abc20a182031734530bcbb840b5b6bd3a0064bb7411ae8588cbb48ac9a95236f62cb17305ccf0ac0aa31c58c152b568c183163c588292696981862638a892136a698b162c58a9161588e6d688008b108e87fbc2c3ff96cb9bb805bcdfe8948c0e33800bfb061132e4092cf59eeab1d52427fecb17846831aecaf6fef089af527801aec8baa91b458a9dcee3156146e8e8104398b3b9ffc3c58b675e0bf8b322706692d2693152fd8dfdca442db73ed8882fa9327064fa8905a510428a0b2c7d1d586ce4b568eca629a5015eebfc62cec4c88fd545bbd6f70f0a9d608a05bcb9563882dcdc95c9ca2043dac48dc479f9d96d63c86ba2e7947e1f875b031ca36eb3b724d26a219fe1f287d6ee314a66f3180d83c2cb6721528d23e917cf00b569eafe7367db7c512055470ccbe1fa2c7a96fb0084247a18cc95ea625c92d4dba92fe705ce1386418e008d95bff4583e81b747911d510d4ab37fff052412b1003aef4a053bd7a8aa48c18b8027bd6eaba80989891f21a4c16fe923533a889ae94588c1cf3c8c4212392016d9a4e3affdc2106ed380cfcb2eea1c48cfd861c906570598270a94a2d9a655f04286797d529f712219d68fdf8a19c50d1478b878ff4fefa6b2fcce8a5207a3409818a2aae8f49fc99e80875e4e31485c8acb393f6ce16afb9d37343162173c39d39bf3a6af0ca9e4115c1e362512b75afda4d6dd571736aff156593149e3bca5cc61c8dcaa016eea0d02713c3eac8783bed1756463a47397e7434a86d71c9192aeffea81c8142c78db59a8ab60c0c16dae9c361c140743b12f5d47d238a81c45ec9f47aa3658461c1eb8d02a4e410588c82586af77de8ac63f3715f4de72736b5fb3e74d6b1f9b8afa6f3139bda7d1f3aeb04197007082e7d9f58d46ebad095761cf45750ba88a32febb5fcd5a1cdc97d65c22296a37221275055d6e1f2096c042401688a480dbb09ca6a51f8d63625718ffd077ed8c898a593537526b6a278bad299080e92845226b580030cb372cb878de44072f8a503d73679fbed198ced53b49ab94e7ec0fb1e5f699f26397ee6cd60b370fcdf94a497de463a1184bee4d2e0fa76327558bb54bee1b0f146bd525b742afa458bf295e33aec0625638907dd1b1db5401633deaac52f920fa8732019b1c0c482265b9984d58c3402604d5f8f2bacc9173cf99d6ed959cbb5c16b5fac0db8bae785be0dcdd40957a798d836730fc5355f3a8cca7d8133b200f61df80c50f71ca5b6d56189fd3e0f4b6dbd65d75829ad4ee8b3dee82a47a5bff336de9d9ca316d75b807658361e25906ff0ceb7bdaa0f9d0ba2003f6062d56092c619ced13c18c8a11bcf36b7ba5120a3441859ec021b3fb9a4e67de984a65559d080c5376adb6939668a7a39270f01a4cb820c8281a417032191e22c46a80504af76f47f2eb41212a3e595669d57a14f95b1bb4e9dcf2d53f51d64f9fc37f0b955519fc03a3678ebe0eb67fd476f0c76dcdb3c36241629b4b89c995c5e12c74d195894029a302c3084dc8c272bb05f53e79a821574a563426515b665e67277a081d9536b832bd81bf3adc43688464c352f872a6c1f8934fc376f0bba829bfadc94af27b6e90773862374ee902975039d74997887ac8a8a0f085de40f9a44078b1afb130f68f05e0bda1467c99813668ae84e7e8a99658c36111d51aa4f367761fa895cc84dd291dee442a383f360ee1313168e9c4c562afb08f747c85b416f2265f3e4d71aa7fde0b2e64eb6c4cabfd2865706102b22f1ffc421acefc441bcc9fc8af9c2656bab812d5ae6d58cf0c389a4a03df7f50186de7e2d9d1afb9afee3bb335813865cfc859010cb30462156eb35889c6151a9ed2173e0bec55818b98c89a559723a6dac20624ed500e52cd09e74d8a2f4ad31197a8ad3471fec0f793320ab4adb3373fe2ef70afcb78d07f5c67a02673d8c196a2dff9cbc3829f19ad53f3b639e5b9ae35fa0a9364ec9e418503066360154bd5b5ba909330ec96cd0f3a86cd0da1b40e7d324bcc0edfe00fb46fc2510f33fb8c707e4d5773cfda14301f12802391da996055909e92249bec10cf86b15fd7f516ee59f7dfa50b3c79c29f803d546cece5ca93cc6a82c15f75b2109be02865ad1ef18272264a3034fc068ef9c03307fbb531a6889ffd350d2e6d2337a33409f2bea025ef69022626ca4188ec1036907a66408ea506c3e7102d4e92f25f8819d55246d8ab1c80d3772e9dbc94c143a9f8ba1734bcc48d6a0767292b35df342226a51f78708da20fa7a708c889dc5dc80a72903a1a4552d65c261110b156d1cc42d9adaa36e2f00cb2fd48104fe04cabe231129f1e87d72128d700815cdb95baf28d1342e48409f73054928a613721ca0d3010558fed8d07e02fba96cdcf8c41cf4c49c5578e2fe72064e40a7a4adacd6ceed5f6ca5989036655a15d6a8c919e2053eba13d14fad9ff2e0932a0088676e0c5635c0c431cb41608d96e3c63f2e1b9cecdfffd5296ce1f037e377b087dc7a396ba5717e94cf7a0b145b65e7f45e287043f5295ff626abb6df7e0749191d04807b65f29399a8c9c7b243b780226dee2b80a843035cf9ac71dbf67b97d45d664e4856a3cbaf3484b2b4a36e67e5f1d4db7c424900c283682c4ffc967ed7d96d330b5853cb5fcee478f071034a4292e64070ab538120cd9f16f26b44342deb9e2ef3d503761d8c4188c9940cbd7df10db32d32c11971d534c35e2f03d21e3449f7f9901bf620a54157f6075d885ebcb0fc11e593649ea21d1e8fefd6ff92ab9e1ad9f766501d1286e4baef840edab928475e841c7466a2e7f72cd046979efc2bc01ac46f2ed027bffbc5a11fc19003de67ab6111cd821031d0c4b3329961109b82afd6157d11856bdb6e91508d4e26c6c5fbe069b568051b9cb55e03f78289bd179705e81dc4558c9b7b712d40cf415cc5b8d98bfb02f448e23ea3381d5c16b1b4c03ff02e2911610d6eb3c6c16fd1658031988f94dd4aa60bf7218563f26eac08e9792541ac0895ea71e7033d88dc9b724b4ef500a4034523817367db63ba4bf4cbfd360f99d004bcb7503a580c246c1f55be45be15d782cc23a01cb737d3aae65d6287b7bfc3f0e848e216de472e6721849361fa826b6d2c38cfe750802170d7db926b963705031a2226e7a4f796ecd6daeb796d2c8c96fadc5fe4cb334978716ea948d9dcbf5b4e2c0e967db2be42b45e32890b8cc60ee6cc0aa4445f6c92712595089bb1927e8221fc26669ca9b72a976f2b02363a4caf1c9123114869c0dbab1e90142d17b59845006b787ff35254cf664acb83513ab777cda397a0fc5852e56747fb442261f7108c8a680db2bae66ccb6af1e54cf48a757c82ea101cdbc67ccc4eddae547ddead67468f520d5a1f7ec4ce2d95e3c401cd9dd1aa16c4cb42e8aaeaf23227100f6e0f04f5302697b120285025985a1a91abd5598b65db31185583517450bfe2493ead06ac1eae64d6379132c6fe3ab7051871076c3b0509ce4b683ffa2c86acc843573da5594b4f01e79a5ef9f5a1efde93f594fd37ee1eba18d32c21b6ba318921e1307ecaf3cb7e6f6185ea4cb7e56e875e9bd0bf2aff4aef39535008a81a89735c65c1bef113ebb4f3698e4e37e79cb094feb4fda73b13b021e9f8af229fff4c1bd049c99785efbf97e0f8fb5b77cbef7aa2febfcf0a433836c99ea4cf1eb210d3603775038ac90f00601d3635b67fbeece9df095d49e02bc663e642758c28d62639cbdf7e5c122812353dffddcc90c4ded4acfd9115cd6033b6569d74da7109941aa6b9d841ec11b8d7ffe40f98462dfa66a35e029da5d09951ef11f5ab016f7a5ae8d9e623bf0fe802412c274cd4695babee9e489bcaeddc7caec9aeb1f0acc2c055a7863f93cacda70848ac5f8ef62095f56d54211451b55beada8722d745752152b205b848141cc8230f3097cc769e84c88c66a3a120a0f924cf2dc0701ed0d4a060b9c839fcedb5e765c129dca04e747f0ff33e44842113ad8731d4136a53a887dcce0accb0a95815693c2a06947aa913202922eab237458320df8b84b1690daae4b05883e20de3c3ed8d7e000b9a8f46bdbb413bfa429d8d8d41dca95b626a58cf0e899699c605f7d926c3def34e4baa373f9c64697dd6d2334248057f62a280ff82cad72bae9046aa3c2cfda29daeeeaba0c66e866c0669f4b5a6d811442abc579719ff865b2bb11591b6a6547b3e932f4e8e094051cd91059f95bf9c043130e722c40e45f46758dafd38e845751b0420730b36ccce914f17b43320031f2ee2255af205215b8a01f8fdc306d829e0b7951fbc52ce79772ba147b2f0596831b21e5231f8fa67dfe224a05093f266ffecd7a503f844371648527364df30fdb0892e5da63e635ad11e3607518de18fa9ee317ac0fa62f2705cd7250c36742c51acaa5fe08a6e933957265f038df98fe8f66c001dd8594351c6e87ccbd7565030321a18035186260f10c8d436925ddd76656faa0def02e946fbe93ab516eb0f9ec8d7a262cb3c5384ab2ca22f55f9cda59510707223e3e318550ce1c208727861272eaa05628582712450aa85b471edf9059c7251bef6be235ae509de9868f2c92b43cde188b61de82a585826f6b2d23543fe2bd496da6efd9163875c49aee285d18729de1e0ecef36160291f2756bbfedf5d87d59f00daacea958c499c6698a7afd83b76403701942321eab9197ebe8fdcd17768f53ef0b28f9fbfc7d43e0cd06a496dde5fc5adb8357d021d2f77e60f93ab6df796f2eb32367fe0693d3c7ebff9560eb5e59bdc3fee7929da804291b1c326ce758611c7b9023f6511c3c6e95686ab62affea3e867ec8b42bb689d45d50d94146ad8ff9fcf1db6499b867e2549aa903e8c5122ed2255db027b84a18255a9eca07f09d03cb6344af1a511c0b9d4517265ef60f20140950ef150e659840b6eba4642f25c9640308706169081530bc2343f62ae5a0f86745be1cf8a536324c7a562873a587605537bfb2949e740955f832b8faf3ebead64734891bab3d47f9a075377bb248658c17a0102fc81fe7de86371a5c64dd01f066dfc0a7d0c9114dbdada67cb137062f5d088ebc3ddb90c794afaae316c51bd4ac3421a4841097575405867861a55ed0e443fe98acbe9327158c8d289c69a0c0ac8ec4795fb3cd09ec3511292729c6e624905a51776ef01a563a49a99e13c1631cddd7872502f2cb796d8d694e93c4543d63a0936713a246172380ce4b606295d5c7e19e4839096bc33bcb9c33dd1a159014c74b6a13cef532a7c06eebca6735ef5d46c43748a68fae31adb52485f33fe14a4b2958910b4a273079966426971051464c81ff43d3ab8b670abc75bc5d181fff626dd733914b515a2db416ffbf06d954c2a2e782c629efe81389c49a5bf029786347565d625783dc95833ac13302435612eba71c04fde9067553fd53af2722e5a619d2867e578da745e6a330789ff8aefa2521ab0324bfb6f3d2b8d841bc7e68e26bcfef7b8b5be68a15f857d0c6c09f4eefcc568fa68560746c1cc6cb445f22f6f4711a89dc0e229ed3fd189575c479f04539be8cc8ce4d4b9bce199e1a4f0aceec2954691cb816773453a722a9aac75081a1b64417049591c9a20bade1b51dac12557bc487081117840de0294b484379140276ba191a6c00af0d5abd127be7a79379bb80f2382eb83c705f1ac02124a75da8226961e3555f581e94c9dc9cf07dd720e7d4cecc60b158c91a3d8e1e0271caed73bf3560d07b2e4aec564e7eab7dfee158a77ef23f2ef7bac3e8a521acd65c2db712cf55c579157ca0d50ee51d822f0baa99bdbbd6359c0ffc49da000728dd49eebd2905c345bdb05d5a2d8903068e457308bbd95383d357195d98e712909c48086043f63df24e79128d8d631a6f5ada891cf125e6e402d41577a081d7641db81454f8268b03c70d2a38b6bd895c4c92a5cf45db6db25619880f387a52115148ba1c88722a76d11fa80665d65a905ad519bf69a749022ddcaa61a62ad623aac1548cee8f9e2daf5df3a3021ea52bfd234d50394738c521966d69e2e9da408aeeccfac7b00427b28ce2179b899cba7165e69156a0f801e89b2534ca5c95340b183c4f32176b77673b6a177b638d7e08f229c030a7f31817fd7414b34a093a4d7bd6c4cf8b05a9ed091e6129e66ea217a007378876faafb2adb2f78cbcdb144ff661aef32eb1400eb13f1499fd88b2ec35ee0f2ae5db114e9789056de4dd663af489cb6d921e5293a26f289f665d4609f3020bc2a040d034f495a12139d4e035bd33e051a7b544640528e5a548f2f52ca58e8801cbb1ddd510505677c524747f4d65c50a05250e3d69b49979a08a1b35f8a765247dbfdadd84339440f2178ae6dc46b9a089431d4013b9298c270a00c9a78599e97ad8ab1a39ba3e3c45d9631bc629cefba0df5d5f926113d30cb07b8112c854796a274b94d616e091acafb38f1ace20a9a3272d2019b1652f8e2065faca35bf88972f022e6d4cf04840f41ad7f80cd10a4b5365211edd61d5cbd34ce4079133c75c397ede2d43330454ecd6e526e301775792e8d6deffc59662352757f1d454d7223eec5fcb617cf523ea301f35c3694cd6d49e374e9eede901841ef8d87f118b57e2595d0a31fc40ac5c946a7314611fd65e3686450d8995a9bf32d6d2ad1bd18a15eeb89217d5ab0e81b484d15bd85d397f0b8b39474f54fb49deef37b389acfd984f60443100e4a1491135b1a7e5edd6f5845412d752535333d1536f42c0d578cfa53976bac433ae28c51d2a0f04c4b153ed763fd3e88fd1f1f27d2abe24a682d3b46151aace5cab83d996142d219fa5ff0699a5a137ca0088dd4c5f325c2eac80038590748924cd1d68befd8609625a8ca19a0ceb5680a37bc7f186b8a20f6d48088d1b53fd40554db9196db1aaf75b1530a1f90a1e0e3d2a15bfc11fd2c844d62b849e2c0da5cb09f4c970c98f4458cb57911c8c9268865a0da290e1cb075b8420e73a88a46a9157ef79a5957826c513ddaf57210ad0ccd0bac65dcc0ad39322cb62b065804d9c5f0c7426166d6c9a483f67f746162eb96f3c0ead5d492c51954a33ec5ab00699fd6260c2124f8276814555efe7e223849320e862862fa54776c2f8e7a130992f3943429964229aae23312b01a13aa90cbc30f621681ba912800d731b182ed234fba29897b8a4bdba2b2ac431c8a9d47b69c74c11b126d12abfec145e02d9cedb1559e87b0108010062fb7ad9a81ab92344d5d7defc194304d80680e19ea087cb29fa16ed7c94eb32cfb34d3661c025e5e3612c46f0458523a99ddc8ff888120b30624bb2f6811602ef08b759404e4139fe8ec5f11fae95e8eff74119a56b2e2580e23fc21ff980d3969356b22173806c462759194a11b3c3d874f50ad6c48d9ccd6cbea5290a48101b5227ad4701d50cb691e16a463fccf22561c87bde20ec2636b352f0883756a22087c18ce34fecceda57c4739e9ce9e9b3f822e8519e56a4d815aa05cef58e7ca018794e6fd5d187b9016c5f49b609b104e5bc8a403449cd6e81411f86dea49b2cb0f931dcd387097f96608ddb25e590884663195adfd2b896eabcfa24b44d34fd14c660a13dd9baad3f8975c8c0a3c60c0b9e2b2a0db570b1e8c3152e9710d268d2bd6012ce55ecc0bd5dc6b1247ac82043ef3dd9819f9ef6febd8f2a03d87b1640e977c27666e8bfd7246a7874c86d67328e4eae887ea474d7834f9605e257995760f382f21c3ed009550d088a67c2c55e6a64a76d2d9a06f5d7d0d6d0b7cefced1ef61bf1bd7b16366a32eee4609bbb1caaf9239ac971b227b49b698c9a16265d9137081ca13e93bf5cbed9570ace297e130a2d543f8fb5bace9000fd96a2fd1f2f67958fbf6fc1a47eb1430c034751f370dd3e90bea4825489572c64525dedcd8a3fb7fe2077e11bae4fb60b7f9c7a0a8bf4b22b59594c77932de0bac2381ec5666b6a6bcce582dd35361374a5effa88e43094f62eabea099315d248e02a6c30ecb454cf130f2e8e6080e6dc06bd396e3566742434f367a82e600a10e8200bc9f07d1f037ed09bea8ee76d912b3befc39c49898b53c4deed90f4c006b5aabc82781c39071bb6c9ec0dfc5754a41bed593e38b91c62a26f7d8ac17e8c0471f038ff6cd1e996c2ad78d58ee8b896f3133c1a4c778e56b7a28ea176856b739238f5ced894fa1339f6dba1a74375026ce78d8c5c83a8dfdd1399a7e35a0b9ade2b9b4e8fc080498f622ff26c98935bc5ead67681b7b0929c2977062b81ae00409db6dd4c3d0c71f15033fab7278a6935f25c67c42accc65c30dba1239fe012473b98b3f170509ccd8182ed4ee993085d91c1ebdd87b8d3674da7f016a40806bc2edafbfa7e248c4ed617f797a8cf88e2973cee970293e170974a5ebb18efd7c29d531c9fbd53d52635d5b50d18419cc82a9f6e3e1230bfbcc455128af81daba1f79b15f8d7e160d276670869b7d6f40f89f065d7a42d59b83e68432e220815647c68b86045ffc47fe4bbc48e97730a58fa1f02c8a5228a46c5d320bc2a5ea17db7fd8a8a8ead05c42ef5388d6aea5269521500d4ad5e143bb649ece3810c16d8bec78c903f4ca5f49475b0983acce0cd1fe762114973b038781a73257ab20ec10b3df8377711536d425244fc121f4fb0a5cefe4f907b8ba459a88521d2e4bc09e5d44a7c73c0a4e431f8ac230798f03a4e540f4f16320ce640ff5d8d9e31801514aaab50fc48081df01c71b89d72eddee7fcc3fcd61d6b3f7c3be1bab9f1e5d9ed45537601f57edc7088de8ed029eae5d89115b981d7ed5015a812e6e4db3e89289611beae7b91fb77b93b15429ab5eb6eb9fc1ce9f0ef28581a197ceac0b97b532b8a48f1814caef3603334d7ebc31da293d760c7c2438468274e8585bb86c42b1f64c7f829b6eb337cff3a016994f04145867261071ce89a2bbb4e88350cb0d8d7271e296e65e4c8adaf5764a580aaf36136f2e9fabffa39bf74cf568a083107b85a544ea04fb94c8106cd075bb0c5aca62fdacff9cf1b9d40b40817b3cf77ed157d6f66b866e3f471ccbaa141cc8bc2a0ea79167aca9180e8d0beabec4a6137ae78e3f622f088f451fd316aeb074234eb567a560ddce56083fb4365993de2174062394438edd07e1d5b4fe879b6bcda250ec90201930f567ab0a33957c6c4d50c169eb1e3ba75c4d2bd2ab37f698f97410a8440135025490c3ee7826ac269bd9dc1cb75d23ddf3b7ee967b6dc5a0d9c68c9340e74c1507c8a906a7008da0548d1fb8a48652e4df4084aa392b9038ae23a724741f2459c2f6e51c2eca3eb5fe07aef355666d37bdc8d752158ad4096a7c20c3fce9e2f8eaa0a4d4385adfa1e950014a75681144eca53202608baab1952070c378c6eb275c9a43d2232e3d58ed353391608df785f1c4c15492b24b57fecd362d03c749e3244d6721b4968c82fc32606d06b65a816192e13d49609973fb4fc9e21f2ec7e393438e8366108fa2842250d795f99b5e46bd1f28258b7b639cf95710fdbebe8cbedc15477d07090489fa3e3df4e1a21e1a3c6485b240c262023004d7a359bcb248eee241707a20cd6b7f29ce9cc2202b917a027e955dc3317857d53b54713943ec9936530a95087ab055f9f02c38a751acb3a26fd4c17ea0ec9be458bff5a5ef551673b154560e2c209d3fbdec25767a979a0b696dde502fa6ebaf87c91602664931ce87b2bd61d755b3226bcae888dcc92429837293ace95f6606295a1e713972fbe7c787e901cb4cd9e3f68114c4cb9fbb2ac16e729af48a8b00c508c7c8972b07832faba543c2f77b6299224106e12058a942128a39e8d395f990e1341ebe193fb11b014dc6d459090ad5bcd3320b3bd5def9abadef8430607f6bcb231d22ee6165026e4b6b9b2bd0574f06634c5c61e6d5662dcf3860c0bc5fa307a4916cc26ce8843aa423c386dd1a3f0087e385c9c46198f00dea0f1b18e8a85f5f75dadfe03e8aca7e13b094c2045e9ae3ec95297c072bc383d8c7e48d07485a5d07023a3d76b00960866f50fb80f1710b4fb6b02da84bca9811137cba5daf270a7b1ab528ff05212314ae6309bd3b090607cc3a99dd4e15cb096d9870304ee060b93bbe3f5d91c3de21a29515a64aa6309d5632d8fab9553b2d1e004b2abb134b53b4d166ac5bd32c321efaadf20c7edd2c2410a99cf6e36395731df9dab371386f460708f307544be5e996989d92aedff4d3f0b1fa81d25146ec45fcbe9fbfab023aade4c1bbcf39654a08d5720191c62c898b78ff54d64cda898f0d076c1cbdbc9b6f74ce178b8430cc0bfa00ee9b1a8af07e2174073016746b2b8407b193ec736b1eb36f5acacd74d10f2b45032a0dc2d59a75133c2b309868faa2fe41fa5f19b3a45f261044619a061830e579e3da649d913d64bf4ede981abab5e0abe7b8a11cfed72a69e9ee6b55e3ff8953c129270bebd46c13b32ade1e0319c9d88a37a3b3d613011241cd00e12878135a12decc8904c8d3c8a46371ad96c99d03773e9b228d569c144abcfc398708431a9242a0143be96d720e85980045ac85915a916040908a9be54b241359760420911a8e19df39e6285e4d0687cef18df1095c7d58b6e16a545b421ed1ffe8fd2041b33a9316d4a49c43b9e542bf392316e4f469c02ccf3325190da3a4975f22a452af7258c63361a05fecd94830f277897cb950c4cac5c4112f279f77234eb569e7977c0b7cce4ad498b53c1577e06059e7217ea200953a74d618bd2c51ff06fa1137cb1002cc36c72c0a9584d7e2f934df610c8a3986ca1cbb3caf4702d43c59c2bb1a3ce4feab069e9767ef8db50acab5b3d371f2e6af8637e37e48e25e4cd9ea044ce0052ea7064717591038c98e7355d63b46e97cbbbbddf79f5b82527ba9d50d898194ebffb16334fcd5524e99fe3f668d94ce6f7f22dad1420383b52fbb2bb722238ffe500d7a50d17ee4d95c7e505135f21951fa9a02eb297ca0dbdc5b371192029d18fc16a01758a72a514df1eeb80f59dd7b26650eb8e101c2444498c7061361fc03f73637a0071e7660800e1f9c3daabc5c75834d2e99151c60881608288a2438325f30c38d2656f0410a1c3a10992558c2c7860c31a0f003ce664a294030061d0f8eb2a48063de3004cd14434c1e9ec8e1619eb075c4e48192052851420e73c8830d26c10fc81637e40c93aa8a13062f688801460612c60402c3690315295081e7867499988a06579c385831c1f9c2849db04301ae98c0901832d8a25c8105884d4d0bb0e1e8b4a2a4e2050c4b4860c3028e88a628279003093eb0432245040ee0440943145024e0840b1aecdc9628457852c3881600a0d9419144094c027b0086102031de5044d9d4e40b2d6c6451838b22889287024e0cb1e3a540080ff850d6e0e288175c053c4900121ec82f6e8081085b1343287060037902132425e0020880d2932206520306f0e1039218b80025022d903c4cf9d91931e5004982a040ae60c2111356380283114430218b3ef0a6a0e1f8c44c710989240b1723007cf820ab844e1ad5c02a00012f5c5124b2923d4a40b620cd6048001590312b38a93838e0a8e2ea3f8b2292e8808191968e17feab90a081960264b0836ee2ff02514ca0855098d2430e35b000ad81678922bce8f1df2401503d3ab0f1420dd23fcb0408846cd821055bc2ff5d0a20f47800002a4032fc1792e282070080448c4604fe77882064c888a5051726ff660fc61518c1c30f26cb952b4bdc7066f1400c43bec812810f50c0c655143734ce16591080421049b8eca826495891058420a86849579865b08229b2a0600440025990f01b88e144161d0378f04080ca5708c112596a5320398bf0f103082c892c331020248a23a80020041e8a0803061d88c20195e6e199401061403180162cb00c214323cb87308aa8c0d1908514728041091ec2d0018b0e3d259af40f5ab0218c147080e20209f8818a1f5288218c0e050c8a3c8637205ca085308c1ce0a904145400a2032a14c210824410597c360cecec9884c141007ebdd7ba84c8094bc2904940023de4c0802c5b3f7430aed0220a2c2c9820032ee86005830905ec00bc8ae995808b140c209ae061e5f1c1ef062648c080c24256023c60010e9038000f8c128c78e2a8470621e800104d60643199b2041420384208902330a2202014f4c2c70f040a4004c6cf1536d9131b3e78a0c206060e282628d101178670610b1c307e04bd040e7030c24d033cbe40819303a8600b2d3d81caf1050788385b0072f2a22a68c6170f188148c631c9043c6005f3c5011a90844e8521c61d9bc5f505005410434d7183169cc0812fbec83268f190420f382021812dbe480a8114121b1eaa1748b0e20b1c0250d1a00727217c4134c5173321aa54b8029725021827bce0224bf1015f08118413504b78e1c40ec3306c68e0852336f0ff1f46125e60000519286106c900090a124822a9a535f8279962c4c0bc9032928cc13f0c30a0e4773c9d92eea8a7a4dbf5ae704e384811beb169f55a03bebfa3cdefe5c61b1bcd6edee36aedc6264d732fd559a816a2b5dee6e23a243e7f5bf53db1ef777011ee6d666b29776bed0e6637d5390675cf6ee73aa84e7bfa0a7cedbae839718507b66859e0c496131ae8822a089b2c340caeb0910415978266e1c520320a81111d348078f47004304a0710d06a99304015192588b4c32c13441d2183864e00aa902001ae102108c09127b525e4530a34e8c0c4c127841b4174204a098a7039d11202255e60c5067d0a1d191a4260985020c0a5668022f267a0cc140881c1162558800b2c688558900e81a501e96a410e8b3b52020f5788b08109596c0cfc177f5af8971510f1244fceff0e265b6fc60cf97fc2ff312aff3fa504f2c206fef58213c8180de971a6e453d38e17f4bc5760efa8a63aecdb6a77a33b384811ce7dd5a9fe67775a52cdba1bb866161fe9a08274fe9f68c7cea36eae3dea66d4c50555a3bd79a88b13eae1d13c1bfd7fce9adf8ee65477540f8d485d0daa47c73e649138eae290fa7eeafb88c7c3ffd74e1d3974a8a063ebffc6c6d52a50edad4e751dd5583fa81cfe9ff7a78e1c43b59d9c54dd399db77b8669cece1c2dc428ff6ffecbcf61138395ff4f9e38a8fc3f99f3ffb84ff2270e340325de84bdfedf84fdb801c6e7f4b4dda8e63eff557fde18f2acdad3751f737abb3db992463be40d1fffe40ed2858b2b78fc3f05aa66a466986694668efe9853f73de694bbc79c72dd1e73ca8f396d400f41fcf0ffb03f6d40e0b4e18006fc3f69a3871d6ce870dab0c10694ff77f56fbbfac7eadef97bd4ab2a448f6e7edda39bdf187efc3f0cff020194902ffbf4cd2bdfe3244d80f23246439e1c60ebfffff58213e0f87fa81abe219b3b37aaed8b3376374e350d2cfe5f07dbecdafc49438a10ed99c5eb4fe57c0ff16eff1fe74f1a286856ed386d33d63da758bf5a2a100b83ef6e35eebbc55ca5f99386efffb9ab3b9add2c44b18a79abe6de6e14d31802dc386d35d6eedb33abdde149c3e63fd8aaab9add7ed2f860abba9de35477770f872f6f5fec6e9ce61fe9c2ff17f973460bffafd38d62bdfeb210cdda6d5f9c6a57f756e3db3f80b6c4a75a732070bf84f8fffc27edf79a55bbee394db5cf7d8ff00fac5995a77b3ed244353ff97ee9cf03241e203152c5abe2453d453d352d190d1932e43ffd6d6d6d6df93cd8daf200c9d6d6d69691abadadabaaadad2aded6d6d6d654d4d656d4d3d6d6d6561391ad2d224b46465b5b4386fc3f07ff0f847940ce39fb7c3e5fafd7eb79e081071e2449922409122448901c3972e4881123468c142952a4c8d5d5d5959595955555555515151515158fc7e3ed76bbddd4d4d4949494945454545414141414d4d3d3d39393939353535353131313131311224488e8969696969494949492929292909090908e8e8e8e72b95ccec8c8c80887c3e1860c193244881021428a8a8a8a820409128488888808081020406eb7db6d686868484848482828282808080808e8e7e7e7c766b3d97c7c7c7c7a7a7a7a7876767676747474746ab55a2d27272707070707e7c78f1f3f7cf8f0e1e3e6e6e6c6c6c6c6a6478f1e3d3ae8a0830e38e080030e78f0e0c163830d36d8a0a6a6a68686868606638c35d040030d76ecd8b143870e1d3a72e4c89103070e1c3832c820830c6eccccccccd8b061c3468d1a356ad0a04183c68c193366d068349a0c193264cc66b3d9799ea78c8c8c4c8c183162c4c4c4c4c0c0c0c0bcbcbcbcc86432998b8b8b4b4b4b4b4b2c168bc1603098cbe5324d932449f263b8bcbcc85c6031302fa44c8c1853e622f3827991c562c4c0942e32315c4fe274bb9f7cb8dd6a1f422df97f0ffe6c9962cdadab7babea68a29a5b9c8eff0feaa0e5aacd283766160721c2adcae25baa573503797ef286ed9fe411fd93523bfe491194fe491386fe5f043dfe9f03ecff1ff933e67beed63477f350c7d653b3d5767276bb5d21cafbff9aa5ff96e8dfddf9ef3cfe776233ff4624270c4bccf5ff484e1896ff9adff3ff8ffa136680ffa9abbb7ff9ff69fcd3fe5ff7ef9ab94c19ffe7ffcbfc7f8c872163f8ffa93f5fc6f3d55f55f08da7edbb7f18b8717f37debdf7ff2f2effdcadd96a3c395cf7230e428483ad9a6eb44512f660ab9b5275cdc4564726fdbfd59f2e2c9ebb3577a70654e3c9e9a84f0fcf8e4e2d07e7878f1b9b1e1d70c063831a9a2c59821001b90d090501fdd88464c992a528cbffcbfe3f03ff4ff5a77902776b3adda84e796a3e3cb59d1c1b90e6f9d7df61abe256c53cab7d8528066eecea1ffbb6797dd135a3b8d5c222866a29a2f1a739f35d5d871cfb7af69da515ff6bdec1c69f250fff3fe3cff2c90771356fd5ff8ff97f8d50aedcada59bd54135f12ca36eff5fe3cf92c7ffb77ac54458ff70babe5bc8f10de6aa0d36eab455b1266e56bbbd612ef2ffeff927abfeffc69fa417dcada1ed915f60882bfe3fa6df3513b1cf75917cb8cdb8ddd8776333cc6eb17d6fb7bebe5c4ff970aad735a3ff8fffcbf4ff67fe245be06eadb340d8ff935fbebb5d57e336e354b7eedbb58bdbadf6dc5bcdea75c8aab8abd9ed38d8aabd67d6dd409ccb98ffcff127d9f2ffb33f491eff5fdc2bd0d5c48d8f5d45f330a3dcada19ad82b6617ddeb9a758e6e767feafaff2fd3f5ffa510264c4840024a2e36a4f07043012496443d21040fa8842d05302b920d0d0a84a8d08a2db5183cb1e26915800d2e42353709e0a142bb31809a39e4473c8460352941012d21f8ff2032fe5d02f8dfe0ffa9f87f237ffeebb95bebf1d1a8ba72a30ad4e9c0cd7b6a42b59d1caeb38e4f8f10aa877ee9d0500f108a6a9e1c76a3b5df2bec6a500775b9ced1ed5c0d6a576707dd8ceaac19ddab8ecb5f1d77bb2e920c1eacbcbc94ad2ddc9b332a204293264c9ab86a4cb0b160c912104a6c621048ecc24b7ae0c5300c2f61d2041bbd782ef8854168c132fc354b4cc046af9a2520c0b0aca6093632b14b0d082d354db011895f38560302ace65563e2c720c0c8f6c5b6029b0a5acca0d2a8a4318134814913135c353d6809c19958eba5e04c3ca4b49a39ff348362684afc429246256ea9d98281c12e3199eb63a44a170d131786c124cd072e982c5d625c2e3426083132b18c864913d2c8c426083172e18fc121e5c23198676218fc42f346313158f6829b9046241318fcf2462e4c60700c26693e9061d2a5e6831e78324ce20f5e30cc49f3660c8ea4188665c56a16702c31ac660147130bc1b970cb1bbdd03079c12d344c62c8989a2d139398ac61f201082696d56c994c9a60a3186cc61839b7b6b6b68c4c1c43f30108267ea9d932712965f2cc5246d384349261179a26a4910b7ea16162821023196e421abde097922606933430d88c41c221e5c231d8888961700b0d1317ab108c626a3ec01fc8683e78799199d08396101c0c9346b62c4eadcf8283d5642de058e2051ca34a172e31cfc4246e898972e18f8932318655c086c5e9c2e68b24895e6c5664bdb0aba6e63fe7433049a697d7325f6b0b9311442871e9ba911270b95c2e92245f5a704c6b09966b09b1ec267ee121e6cb7c2d211202ae2556e3a2c97ad5682dc1b9c060b8c6c42a3199d5f22e97699a2ed3e572b94cf33119c324832bf1932f1d5ebec426560b76c1258dd6bfce74bb0bc36a882586e1978b466b069755d6e064355a4b96485c0b960b93355a4b70257e99353093c426cd63578d964932c9e0caf2023c6038b178e04aec2263b8c7b5e87062c172206b604e264e2c1b1b1b1b9b7205371e3f0f54781802838087281bd8e0cc5859b3e34969033523868b0db21c42a3460d583983062b04a419356acc40a2316306b64143cea03133f39a79cdbc6a6668606862332bf8f5382458b32d70666963ceac5899612163cbf4c009c3002b48980e9c484833353b9ecca0e1a280b3059bf84507163388347ad998a979bd666ab462382c1d5f94363b7460296dcc2733d87489d1c53056d406ce9995b79283e6cd1b1a1e3566d0a8c1b29103cec42d3ab0cc60590c52895faff255beca1a1297361838b174e8c0b2a3490954c2502881604e9e9440387024568967de15a306eb0507ce81b17460b141c5061532070e1c8bc562317c63268a8e26240e5e0e8c0307ceb124c74c1417b691c3df4a33280736cf9336a3e1190e1b3ea470c2e01c58aee0cc1a2b2f5807161d5fecd8dac144c71553c795a5a62813470e1c2b5f346660367c2881b0de460e3678b011854a8d297f2bf1cccc4a0944039b7836c5eccacc0a1d4da8e04c1d58a2e0cc17cef1aac1218503e7c03c1c3847cd921e5e78490e8c4307c68171d42cc18175ccc4c0583a9ad498f239a60cc6d28165666536c5eccacc8ad2c62473d0e028310d0ea91c5807c651d6f072601d38783b300ebc03e3c0b01c76e868c1396870b4c468d0b841f3c21f43636666668606abc435ca174d08b8b246cd526963c66a9a4a1b73f6c3e7983807cd4c8a7963621c342f58ccaeccacd07096786605b703e3c0582d3930d6a90363cde0669833f00e1a1d34396870d0bcd9426306d3c0356ad480c16aa6d86a385fb865666686c6c68c0d1af84503c768e021660d1a2df83505eb85840c1aac971513cf66566656b0ce1831332b3813c3d060d9f00167e2171d58b09466d0927963c66a5c5f96ae182f3ca484bd6a1e06d764e20da4cc941b50215bb00c2e2106c7f8a00717fc010c96c1b0181c830783656a78b0d265e3c08905f3ba62d660b95e6ab46234582f34b61ace182eb18b0b86c94270e118cd10e7c2434c2a2ee58b07274ad0f47558dab866aecc6099a91244831f9f343430e50d9ed1e2fb293ba01c7162c988e22a3dae658a8c28244c4a0636b0c4b01e8b8c2836b0d8c062038b0d2c36b064009302a302b302930293022ba29402ab02a3c16a99a902f359318305aeac495d32d8158374e216fc5283f5c29886a4c1e60e12d3608c793418634c5343836bca16dccc0cfe18c63271376ee017cc0e5b0ae70ccc01e621036f1065c7941208268912c8dcb1044c0a0decc234a4c0f852210fec825bcc200eb04be9027381c12e2676c1641045535c300f2510d65bd90165870d3b72288134c0ae1a588794bf95d8069ea9e2c48c95294a20197661d9c08273c98802438343302a31a6c13c8c6938c09887310da6a959c203e39a108c4a171ab306c67a2c3630d68e293355669ca099b132334569e3d2c02461585c31514c38c03cf08b2d89130b86c57585f106f88a06d7e00f7ae0f1c035f8030e6a70c0f00618e30d70c9a306875923b501c61807ac86b741cd9b37650dc6c1c334b8a6c6c50586062b841d3430fc3130a669c2b00b4c29045ce92b6d5c349f4aa991414323c3898c03e70ccb706206d1601796d104e881c0b08e1c38f0bfe00c645830524c25702faf0ee05ecc580c528c4c5633c556c30953f2c3897389813fa6346942c09531602e5fdad4d8b6c022fb320696125c896964b00e9a1c3458305cac464a0b2e831a18293817be4183f542628606eb65c5060dd6598306cb95021a34583368b076f08075e264d0ec9802b3cac0b03869646860569436569436ae2ba58dabdc41c3dfb8f0073e4002123cd64c959a97e2c4825dc1b93079c50c72b9b0b02971623d96d74c951d505e62481951cc1b172ea7c88862069533333833ca0e1a607024ac660794cf71613d16b209f9c465741997905b2550894db3c646e505ebad6ce0c49866a6ca145c0bc61296362edc69687640316f5c18efc801e7d221e5735c184b46149c0b63cd54c1b9b00c2932a8e064447161988c28327c28817e905184d982b1ca9a9a9286fc9cb2243fa72c67905fbe2b06cc45fe93a42b060c0f2971b0b4c48287931c6507365879bdfc60de98980a15f385c5bfb09041f3a2f2f22f8f1413a324c330d7cb5e686fdef064d4bca89837af181aac107031250dd66b8aad86b3c452f203d6898b815d3432f86348d7fa9a021365888379c130342ea5cdeb3525c9e5c2e285e585050d678961388667e016192d334a9a970bd66b0acce9b22213616605c3c0349989802bf10c26075c8961606060609ac45eac3489c562313246fe808b61324a2cf6b8980cbf6cfd8dc42f58cb0f2f380423f205c3c060989a253038c63469609894d560bd88802b3199c313d813580c250c0acc86d206f6e4c5cacb95172c5eb0b85c2d2d2de40b16028e5c5f53702f9897590c062f36254ee112d64f2149db94130b0647da3470c29464165c0c8ad385cd58cdcbc5c56682d3854d18928d8813eba7cce420e2bc122e352d45bd13b8d28ab9530299402e6cda702517a50c8bf30733a8458a39055752c195af2955708f5bbe0687948bcbe3252dd8a5a685275be282652f2549e29224adf8500291b802641519125440a68a0c0b1916b22d6458645f9436322c70257e4d71b990284dd3e572bd5eaf170c0683c162b1582c167ba2e58996275a9e6879a2e58996275a9e284b927c79894992810be145b634b135616bc2d684ad095b13b6266c4dd89ab035616bc2d684ad09276c25287348c19919d89038b15eca17152a55aa60b560c161450a961a58660f38737072ea307bc08b14194ece1c6a60799152038bcc0ad992438a6d03270cc732784d318362b8acb169713ef91cf3c44283c60970a679629942c3cae7982f51b0e0cc269f43bea6d048295150749a3ef3a6c4c3d2a604e238570d960a33c4097124ce004b09141c240e01a723b19084808b2599b8184652da947846d66253e274f10dcb12f31617a2d266a6c49ac19ac19533a54d69be50c1b9d06011e14a01e060a04c8121ad98266ae217120fe43363b097f97ad1e0d099588f5f18c703a68993d284a18179a17982838162fa4a2018013c0a8302ae84c9410a9624982630293c4c1b959324732031162c8a8c0419254e13bfbc7c10322d3197982cf6125b8ac55e2038499c8cccd294b3e5a7c082804991029342e2c80c04018bb5b8c8605260b0874931cb9794571945091363c99861a97262c98072a5451614038633b822030a0cc7d4cc9c35301950aee06440c17a2161e34a066440c1cd389165a961e5c546e534c99a580c6b8509c149e2c812f6301bb67860cdcccccccccc5ca9f901576297ab86159c6b0b57621b95d35432f1abe61cc18935abb29543953305b812bfd88a38cd1a2c181cf99aa28a0f257ca08aed03568aa698b0a2294554801842c5344df2cd182e5c62184d0c9b34a5091b42e5158364e2b22c61b017ecf57a0da9f230292f2d4f92640cd410116899598688e0a5a605c360170d964b16c3e57cc578c5d40c11c1cb0bb605630213533e3cc53cd5c353cc533c3cc53c3554cc5342c53c1554cc5340c53cf553cc53b6629ef229e6a99e629ee229e6a99d629ed2ed7e320202fea0880f781f0ca5ab56938c70755f55602fdd1a98779d6bde921b485c0f8fddd56d11cae9d633eef179313602fdd87c7a8c3ca9ce4277936ef7134a6c778e7d533d35dc6e94920fe8beab769db76a0e1ebbea6eacece4eae1bbeb9b78bb1dd1eca29bd53cdd7715969e4a3571bbc3212ed57dcd7c0fb71b85aaed7e723590b7d9e5e9f5a706db37a9b79a8824d4c3f3a63a3da5d1763fe9a3d4e8f8b3ae3894ab4e5a27f15f8fb79b673d6594c138f31d741555d7a151ffcee37623880080210d510a776b443595d2c3e7f0e2c14182601edc014eb27324f79cd6466862faa74fcabd5111aee7cdf116df1c98bbb5636f379b41cf81bbb555c8ab8c200408781280a30dfe9f0655d7a1dad797c5c15675df14bb6e46717014b85b4333cb3520e2c9280de841071cc8ff305a48184c0c087caa87f4c1f25c6829a1c1210f2f0e9c439caeea984c8af030a3bda26e5f19f8219376cbff4b196287a7c693836ae2ba6696b72c7c6be31ca76dfe7f212ec0db31fd608f4e36879aab836e5e3157f10f6bfe6654ff70c6ec7681f8873b9addfe7b39eebb6d738ebbb5dfcbd72540b0eeff202cf0b9e7b4b72a555035fa7f99f5cf2c1bb85b6bd55eba59da99b5c3dd5a9a661dce0abbeaeee09a5d6e700962617917a816991809b1251f337e9982b28a124879d300114e55b6454a6d69d5d7760aad3c0dcc8e70c8298ce1ff69d4deea760f8d1a6ba2da7350b7dcad05d534a7b76a50fde5b06fcb12917a7edacad5bdab455eea6e94ab474ab99dd14637baa78e5dd5bd97856eb6e7a7ad767a702c11a9c727b7497a7c92ecf400090013fa7fa83f7511ffaf81d8c7dbb747ccf2a57afde55663fdc39dbf2c4edbacba39479d15c413dc6b9453c7d000bde3a3618ca450fe7d6a3d3cb59d9cb6b8dd9f906761ba6429ddafd9bdd3ed7ee26d60abeb3c37a95d6d754ad435b76a12d2711d0e719a676114ef762b4af570a8ba9ddf589e8b44a40fbf253aaa5d77a3b8ef96630c5431e62ad6421e06e2ebb9bbe74bb590abad0ac4ae4e57f535e26e8d7795555706fc3fa9c3546fc8359bdbdced46ecf1798b5c1dc57bae1e1a33ebaeb9efde3366b688c8985954e5adea76a3ee699e7f453d3e2f91aba38c99dd2cafcdc0ff541775830ded56b9ababb85b13f22c14f22cf431f9efee1672bc77c737cd5d4dd42efeb377c2d76ab51e5daf383d6b3788fb7ec2358ed72cec6a11075b356ddfdbaa897aeddd524d7435d7535abb492857d9db8db73a6bf7db8dbf3df79d46dbbc66a317a0d3032b403c18fe77aedb9eabbbda63b70beca17ac875504df4c04a4747c8b3b07d6fb7db4db36a6f5f37090e496c48e2e47df8e6c3bd0f5fd7ddb9ff8792800d6c754777f79cb6d969cd7a08b566d6dd4fb75b4fb3d905f2638ee2bac87e4a22c48f394a6b608f1ff390f3bce69018200618fe5bbdf65abdf674d7eb90dde89a51b5dd6d46dbfde494aa50ffff42aaee54850ab6aaab5317aaeb1d02581d35a5d3ed14ccbc60e6fdff13d305dd8fbfb6b8dbac962a9879ffaf822bd8aaad9a59edfeff013ecd3d1b4008d8a9fedf4930f7ff6f618576ab5de57bf8ff2cacf0ff69ab66b9db75917e3052442b51b7ab952851f98b79c65738a7db4d2b51b98a831061ad44e545bef87f15dafdf4ff4e52d0ecde9d45a228e204be19776c8299f784436a8282329a425242e23de974464a4fba29de0ef3907839a8a626aaa729245d928ef78474a4c3bc1c121123a8261cd2d411120f3785e444f5842494c4a342ca3d21514921ed78483b547535fb3e45e955f38cba6f9dfee0749a5579391e9566555eaa95a86b7675ab95d4aa2827a32b2bfeff087e82d2f170395d535414ee4acaff119cd325e9724851bb1cde1512ef092a7775430c57fd0a84f3eafd7f1b9ce40846e2e1aa74535137527f5e9dff59bb2458e5f0dcadb9baab6e11a8b693e3ead47cc209570cb11c9da7384810d4e55062a842f23c3807b86a8e8304c1c29a0ea6ba084f97c345e17298081b53951c9e541bd01966b655b330a70a5b9d90e7a5a222778968cdbddd4a54770b8b7ae9ee76d3aadb7b434e7473b12deea320676f3754136fb7b4f3de90e79ed39e7bbdd5bcc7e725ee61cf98d9dcf3519af5529ab570a9f7d4db5d55310db393d62ecfe869a98738cc3d1fb55bad4a75e72a7775d4babe49fbc9d5c20ec553bdcb82ea0d59d478cca84e7b6eafa7d9976b77b8ddb4c7e74dd72de4da15f65edaea217f795e8178de5ee7ae0a5cf7fe75ae1a758fafb90daabcbf50bdd5ea5045b55b7493a4872c7a85719eb2d0bd1ae59ed3dc35db635b9dabd3567329d7075b75fda9bbdb8d458dad1a545da828a9f2a5741ae20027d4063d0755626e75b476fb4f4787c7860a05f1fca43fb6ec030464f3b1a168f6497f867c8078843430037b343003519b4d07edfc75758ac4b7eba29b540753d5fde1304eaacb6e8eb221427515477bea4d8fcf8b717a7c5e1bad44e5bd54c8f3d2ed96662deca15a6c4f8b4d95384979a73f9d84ff4fc3a370ab75b1eb1fd63c6b176d557c5cf13781713661e0cf260dfcd9d401e0de6ed4516f35ffe18468cf2c579dfade69d65347b7b7486b4eb39eea7bdda5590b715b49753bd7533acd6e70489566bd846a3969b1475a89bad3af7ea58468cf43ae1eb5ba21eb2e71aedd20d2907597fe5feb87fb7fd56d9b85b921eb2ef1ce7bc7bc6af776ebf52de4c3dcee74cf7d176c5516dc407382092135a4800a24722001d1111ab0105a24209b49400915b0059494ad1613c85182143f702053421302689cc4f41840061f7cc8c962e600331e604287231040c01b401013c0c82a2711277f6cb390670d04aa29ce5813b310ef8e79aa758b85a82a2c629ca3bb59cde2dd5b35cd7b4d75475fb7f376a35cad650cdc42fee2e30fa71aeb5f9bd1ffcffa7facff77057544ce5f7f3fb5657bcf38f79c627eccc535e354735528e4598875ba5b9c7dfa5d73d5ed96eab608a5d38c62ed66acadfe5ffd7f6170886e66f866edbd9e4b53fce3dbccffdb6cf04fb3c43beaacde9ad1fd3bfc3f121c4ea5206c359e1a4f8ed6e9facbfd65956aa9ee5d65d98d9246b52c34ffb68bf50fb719c53d0b3bcebccd6ec7ad1e6ebc9fc0e8f16792137d6f17eb607677e730488232c4c7bcea292c49ebfff71e2dd73bc569d642dc5bede6289c13c9099d2c74afbf9eee99d59d1f794eab66219add3cd4e13a361ea0d640a90e0f104f06f204d97c6cc09e9e9fa19f9c5ad75cedaa51e3b5ddae4a6c917eff9f7dffdffb7f0fa07c16ff5a209da956dd8ee448aaf131cdda5df3bf12ad97e1ffd13f8f903c776b3d9d1f9bd0cf860ae9f0f4e49d201fd496faf8d8847674500ed7e1fddd3a5d0d06e04a097255da9f9eda4ece2fcdd1aadbaf72cf473a98a3f4ab5fadde904d7b5daf6b1ef6f8bbbeec8be69e8f822ab0dd390fcedc90e7c1563daeedc669d62bde1df3aefe706fb58aaa40e0cb71aa3b5015722ba3221ecd4376a33f359f1c54a71bcd3976d5cd43a03f8d98fca75a0b03d9f1f1a007b759d831e77a3db61973cdba99bf5b256a370aaab7ef467b45571377dabe3ba783392ad5fced756150d5566b7e5bb567cc2cae03ffcfdbfd8e876ae2edc6dbad7654afeb467ba9467555aad3f6dd463cb81e9c0fce86fbc101e1827042b821dc0e0f8e8707e7c3833bbabdd55d6de22fdbd5a6bed39cb5eea8cc66fe32e5350b99d857bb5ad8917255b9aa28275dd7bb9eb59b9b22e6156d9152bdbea916a2bb54ab4095f3bca22d95bb87acced56d50dda59aabee16f637b754ee765d2460df75ae5f2d3d05ecbbb4d53b9dee9c0eb67a5db33097b6ef4eeafbe9a89982ad9aae5a15ee5ccda934777394ab5395b8dda8dcf3d1b1eb356df352cf4f5be960e65c3be9964a0f8fbc60bb5dabdfbb6b83ea8e25221d81aaabab9238d76e9ae2582292ab891bd553479d75bb714d54bbcaaaeeb0a7dd8df677d85f28ae71499e909a7a48b927a8a75e1124a69ed293eea80949b7b4eb4d21eda83010a1f78846dbdda6391709bd44a8aeea46dd5d2d1caa286e5573d7473d3e2fcf7277b1eba760ab1a75daea76aeee9acd6e8e2ae211a1baea3150b493aeea1b54ebc9d9c056cdd16b46791b5439cfb8ef160b7b8e5a0aaa6bd64a54dedf7dcc4d4f3ef965599a8fc596ef636cec5def0a427e0c0a99be6664ec65c8570d3206d9d2e30a7ab305d78e58d9d2f2729924f936cada974f4ab984901f03f27a1918f92449be5eb6f2a57c9224cd72892463644992264c49499664acac3145923032f6251953334b18cc4686495759ba9c902e922c65e40b4ad2e55fa4ab34354962952459b65095384ab284b94a72052549c24a528ab4419266d9928197198b41d62049922c874a1a2d2629035992b092244bb347962e998bad84218b5ac8170f3308192b4b178f292b4bf246068804808be725c98c2adff5e50da4cbb471bd4ad2fc9701c828c82fff3ccf279f749125d902b2a5f44192e5ab2c4d92f42175c81612a62ccb1296237b94a5ab2477ccdacb8d960ec8181919942e90aff206e9224df2cba812cb48d7972aecb18f2480f93419207fc811a549c248578c8c21cd2765d972334921932c4b172e615c6cc81749c6765c3c1aec90a50cd652c648f246966513268c74295d2691ac295f923473489797926c8991e5907247b9a3d461bef9a40c092b4b32c6759aaef2465996248c740149171ee4cbab245f648c201ca52ef6e25292bf169858494a912d246996a48b5c22759443658ef2a58c952fd2244bb2246fa48ef2a58cb948f365926410699236a40b09439224107247293363648b8b2c49f24692b01ae50cd255922f173954be9066b98334cdb274952e72a82cc90c4a19e942b6b84a92246fa48e52163349578b8b24c9204ccef86821327fbc489374215d33a48bcbede5088e1de4062eb22c61654c93278d9d9725922cc99224c99c8ffdf08d84406251a4058f8bd070854a119a29acb0ca513480ea04569e475382dfd1f001bdcb4afc140d41c83045b3a1a468c0c2222adb8634d1b0438ba59c63678886199da11c6387a013c4f008a29191402720c100f26599010887f1359f173b727c3994385670d83878c7c70f28385768e0c3301a601b321e0a2dc9fb5e2a3c7691f06ffe23f98f128afa7fa5deeebde1db130276aaff4f1ad2ed7ed2ecdefd3f52ab63b79b7474793bb808bb5ad8f78e0d1361ded5a09afe764bf2700a19ff83ea4f877d5beed672cf690fc644c60107b6622e3117578c16a0988ba9038627c2534d8b0d4cd622e48291215d30b09eab68c698335715125ed882c528632e2e9c181d420dd89255c1a42e9c182294361c2f2a17092322622b48d3d18c84d2257335dd74b18582117c5011817cdd661a0c11a1746560e60c499aa2d70f26b660300da298d090312ac1541221005b375cac6066c0cc50924463d8c2655c6c11751725df0a67ccc58c7965b9048082082dae18301da48c8f0855d4e0e243044c5ab0b810a37c790d99c5865c4cfa489411682f1b5992ac031812978fd8d00815a041690406a450e3dc611283360da088ad9717080309313d3264550e1348ec069008b2d8104cd612c30cd229993a64495c6cc962ba985dcb120ea42499cb12cc2e25aac0960d170e900c76a3c5c0642f48351ee0b0256b59daf13b61eb659e2f12260593bd80c07c1419bdbc2e3d5a9660b2d88d9823171eae27171b33727490410d1f2d3f98a63a1041d6528bf9c4f804e9a26230ae5806af9b09e402e3ea8187c45c623297ce8bc92534847ce130695e35765c5a50730809c84c8bac864b9008b9a7175b8b8d8b878b078c015b3e62b88e5e3f5e3b5c78b872c8f0983a46d8aae99026d0cc71992f00d478c08210bb754003a84889e20006d83080103e30f22ca0467d1e2031725545b58b6a22b27413f2b1e980830db0eb0823b25c614515428a38a8c9e0460c4a079258000e5248426e3a68c0bcc08a0802881e7258800d0aa0018a0c54363d746a30bcc0db65f9628b9f912b5a30446fcf56484105160191c57250c149900b09aae890c36d08c8c68c17594b2c070e0a4c00894ea19e88e89290662880000220c3cacf051cd0000684605515043433c3f5012a5a541505017289c11e60021010a8e1002c9c008004cc30430a504f45407ac0905e983128b1b8b0c02d28afb8aa284ff0b24296a01c41290217266096283f502a5106513ec0744099e5c22279901ab4e878e588b951da2869903366b4978c72469eae18e5cb4b56bab4b494b1d8ab74912649c23e46c6c4610ab1e522c096ab09e60393b1178870e2b0f57ac1c43a2881082e1a785c36606cd80136802d57934b0d598e1912b65c8ab88c983b5e3a0ca625a94508263381ccc85e1cd86af9616b0612115c824c19b2808162eb9565eb05938108b6607a8d105b2e3331ca189097ceab8526345e6eb830820b852d191a191a3367a6d33233cb32c2eb478b508cc89431895a6cd8329d78746032180f5be60b2c0317ce0b8f49ba605c38b2a4d78f5811580630190c0b6606cc0c9719971ab122261823c03230a38cf09ab2c501968bc53d0e74b86e3032b0998b3473cc9c2b0ac606ac8c59c570c49e5e346290b017f3e582314db33463668be922cbe132635431193251b2590c1623cd98cbe6d271912d374c1c22b868f0da612b060f22cc602e1c2d4723b8a018a1a6873f4b185590a0ca07a82031e50dee54708264242448d06eaf68814e8f53a62576c408d453160f2c01020d52d879e2c29017a01881121a1882020e58804ead8303bc3d05eebec0055838c1c41246482004103c4062e40aea492949831d366a046181101cd000103ee8e9c8b15b0d624061022d762b4b185d7081e4888d1ab31168e0080c0c4181093c20850208a0e741112ba8279d121e38a2031778b2d4440994d08011430881830d279440820bb01801134308110404986ce95487348bc1828a29a040c2080b00e1830e2a1040a73d0f901cb1d22101b9ddcc6294f0001232c0a002139d7a4044978404e41623c6f5544ca184073670840580f0e1013aac5000264988e88200b905d9786e64cc5c9f858a294cb001248e30c202403c408715542800019844d14b8244884eed0b2f9a9864623c91c3090a275c59edd44080138ec61e5cb298619460bcbc2863d0b2838b01a50ee502c89e92c7dc216b2f39e60f13d7d0a0cc41e2886560de20675c36c81a2e341932ccd3250313e38cc900e6c64b0ed9cc25d6028b9530d8eb6592ff9fc1bfecec41e1d75feeadea0ed5f68df5f87a7ace1e1f678feb06db60ac5395b72ae62aee2a06727473e601018f0778a4fcfb70b0555175e537380811b6c169ab83fac7e3e4e429e1df87811babb81f7f496ab5da911485fdbcaa6a5e46278fab7bcf431db7bfcb2c9f3c752ea09343ab76ae53ac778a6f6c34eabe9b9f3a03d0e99fb6ba67cc4f1d5edaea53a746e7c42b066e8cdd0d7473e758ab668217a2ac66756e6c5c10e0b2025f3350bf3f405a9260bc1d9891da8cffdcff1b51f13fc4838bb0ebbea88fa121a1a1a021a0a19f21db90cf50cf10cfd04e901090d08f904dc847a847884768476828482828280828e827c816e413d413c413b4133404240414040404f4036403f201ea01e201da011afa11fa09fa01faf9f9b1fdf8fcf4fcf0fcecfc0cd9846c413620db8fcd66f3b1f5d8786c3bb6211f219f201f209f1f1f9b8f8f4f8f0f8fcf8ecf508f504f500f50cf4f8fadc7a7a7a787a767a76788478827880788e787c7c6e3c3d3c3c3c3b3c333b423b413b403b4f3b363dbf1d9e9d9e1d9d9d931eee8138704dcadb16fabd3596092ffc7c9706c64361fe00f300030c691e1c8008031c6f8ff87fcbf90ff2ffa7fd73f101c26f886e3f3e1dc4e9c1a4e0e1cf3fff1cde7c337198ecf876f1da8578c65be9b2e0caa2ec61da8571b5f90204168b8feff20401ec80f238ac0383d7ae09e71aab350b5cdc3188edc7eb83fa63057fbfbc398ab5ad8f1edfbebe46ed7452a6ed7dd463f70fe6bb51fb4f307cc8e2a376508ad5e8d997dca4fc056ddf99153abed0861e3ddcdc30bfcf740b2d303c90f17e1243b4780ef2f88d3070e144cff6508ca2090941d941b98e009d2891b9b631fb2bac7d51af0fdd1280d31fa276bc46c572bc1c75f5cadd5dc0cc49b631e6cd51de4baef2a5cebd1550c54310e0ed738effb29d8aa9cab4e7d57e9f7e8b8ba19696f1e574255028812365fbbb1c1a851e31b1b8c8bba095d4ae56ae25ed73dd55bf735c2055b35d51e80e18dd9a906545b8cb5b06fbe66cc553cbc21f1a09e708de7225b3bd20555ed6ae2e6987715075b95a8ba3ac5fcc538a98a7bb4ee6b541b62f7fd61ede69b639b3150b5d16e1ef260eead465d1d95b67ab8abf1541355ce358abe3cddaaaba38cd9894d71066ef665d55675c269bb8d6a188735ef3a88358a66dce2704da3791de2ddb1ab5377e356c5a94edbfde33ddc24dc24757331e3ae8fb8ef56a36adaaaf888e2dd716e55cc33d62ecf42ac853c5cd3696671abf50f0337c6dcd5469db67ccd6d76da9ba7b2ae8e6a757d3fe1e390639eb62f9055b15e7bd71cf71e6dbabe1bc53958b7ed465b5daaddacd71c12469c4820fdbbb8b8c8f811cceecedf9c8b8b8bec9cc282ffdaf1a7d75f76f510f736ab6e2662ddee1c6f5d73aec6f131cdda5d71a956a2f6fd94eaa754f7fd74822449563811f416fc8c41b295110298145fe58a0d9815481d05f8aff9687c699a7391b759b3f8b8aef9e52fe6aa9087695a15b7ef06e2fd44a488a5973262e8d3346b9df6bae6bddeb16bde3beaac1eefb99abf29ea6a62afa7d9cd73862c6a3c8be0a2882a67112e14e1e47d3e8c624ec43e1fce69a170ed46631f3e6c30c75cbb7945758a710a3f5c01e585281e8081f6ff73720a20c5f734aa125ddd6eb5c7a2c6e1fbff32529a3cf7f9b0167617eba09b8539dd5f541bb5ee9bd8e36aaaf95ed19e76873dbde6dcd33918b0f2ffd8dd40cce2dde2126288a248b3b9c52f9126e6e8e8bedbf675db778829a47e6d000729c2ad5e7b1da8d75eba6e2831a42044cb5fa0e84243c339791a4e004fc3f5a7e186348c38c09f4640f9d38807fc690406fe34e2037f1a51c59f4670f1e711af3f8f98f1e7113efe3ce2f6b31a670c023c2d060d4f8bd180a7c5c8000d195a789a0c323c4d061d9e2603104f9301044f9341054f9ba17cda0c369e3603ced366b83d6d86a4a7cda03e6d86feb4194c8099a1f142237af234a2284f23d2c0d3884ef034a22d9e16e4c5c67966e97f66f9e0cf2c05f833cb0d7f66a9c09f598ef8330b137f6691e2cf2c2cf8330b177f66c9f233998d18194d8b2c4fdbe27cda163b9eb685d0d3b6a0d29105eb67319a7e1623c9cf62b03f8bf1c1cf62b0f0b31835fc2cc6037e1643cacf6288e0673150f0b4208a785a1022785a1028385d9ca7b9414f73893ccd3df23437eb69ee92a7b905789a4b036d46a39d5a3c6dd6429bf168a16551c4d3b208c1d3b288e2695970f1342dfe695ac4789a16379ea685cdd3b4087a9a16b9ff9791c9d0e8e1693412f0341a283c8dc6d6d36639606831fcf0b41892785a0c533c4d867f9a0cb3a7c9c0e3ff67cc64517e2653e16732ad9fc9763f93dd7e26bbf999ace667b2979fb97cf133172cff6f03df88c96480e9cf0cac7f66a0c99f1948c09f1998c09f19e0c09f1990e2cf0c74f1671130ff4f6383060d5a273ead07e0691dcad33a104feb22785a4fc1d35ef3696f064f7b7b9ef6323ded053eed6d9ff63679daab80a7bd433ced05c1d3de269ef652f1b4f78ba711673c8d98f33422d1d388564f23eea71149781af185a71179781ad1034f235e791af105ff3fcbc0460b0e9a0d99191b3531b421304f1bc2e369437e9e36c4e9ff6766ce289ef8330a2afe8ce28a3fa380c19f52fc9f52c4fc29c58e3fa5b0f9530aa03fa508f2a7144c7f4a81e44f29b0fe94c2fd530a10febf25368b1123060c10c3a701717c1a10233c0d88129e06040a4f03c285ff7789d16456789a0c0e4f9381c0d36484789acc069e2653e56932579e26d3c5d3ced7ffcf808921e34ad4cbb872e4655c495fc695f6655c01c0cbb862c2cbb8c2824b4c0c11ec9f44b87f1251c29f4418e04f2210f0271150fe24620134578c0604cad380f0f0342016781a90229e068489a701b1f2342028781a902f9e46047b1a518ca71169f034229ca711013d8de8e96944c1a711159f461480a7119df0342215fe1fc6a586ecc6092322e74f2288fe2402f72711527f12e17bd166a8c0d36698f2b4199e78da0c563c6d062e68c8c248e2cf309af8338c2afe0c23067f6669f9330bedff65682f5296fe9462e44f29587f4a39febf0c2fd43fbd30fee9c5097f7a31c39f5ee0f0a7170ef8d30b20fef462ca9f5e2cf1a7174ffcbf0ccd25062da68ba7c5803d2d468da7c5d8f1ff322d510cf9330aa63fa3a8fa338afc6714587f46517c899151458f97510590975185ee655441f532aa405f46156a0c1954f4f032a810e2655081c4cba0828997418595974145172f838a2c2f0305312f2f2db075cdb954dd55ae5ed79cdbe99b856eb6b7a279f86e37d835e73ec3ed02bbb1b7b2ee4e8dba97dd37bb79035b75a8769678346e37a872de7b3b7d7759e876d22b9a5dad793a557746ed6acdcb5575aeaebaab42ab28275daecaed9a739c93abfbea33dcee11efea8845af722c7a3564d1ab5d54372265f7c565f7c50d5521c76d60abe34395c7b5dadb771be99c10d5bca12ae4aefeb1da6955d1f6ddb9a3a3fbf2ac5d5e14af8aeb606655ed4605554ee5f27630fc932f20e09f7cf24fbae0e400ffa4015668e19f64815ce1831ad81baa6052f582141d682ae1aa049c114490001d5c17c86046ce110230686880859f25489f170448424a141e42399c2000a310512c00d5689b5b5713b7d2aad136e7dafd746c717a78e41d8ddb8dfaffdf60caff937f36804703683f35c87681142ca8e0ffcd1c6ad0218a66812941ff4f0a59e108941301c810c412bf1e23901040eb89ff274f58810637ee8ba80949081527f438a18bff3769a440a5052e3c090204ff0f7bc10058c8256e610cf97f728a17a8b0e40b17a408fa7f170cbe1802841cc082e273f85133bd7e7a08e25d1d5205d2c9106b61ef7be518a759bbb8ef3627e39f9cfd93e7d943fa670ffd4f1eace46ebc765757320060927e82983194a0019f6a15a8a27ac85b2ae009725ad0820455d8060902433fb2ac28928480630cba0b6c1c48e0d16400218e2da8e19f943977b042c2b438b103d3e10ac0928d8a1af00e5dd0d444c00316af0e1899ec38a9120314bf9b222e00852b4b962c5f7c102888410f0bb03103132d2ab00122be403990ca92058c30b264c9e205b9433f42cb988510305040173644ad2b7022449618a0a081b285c914e30ce30de8ee298cd002616d6b7124860d403d55610091a11c3011b0450942023d968a4d3b82d6961b3660a0c6890565b8458c9f12448000188f0fac70e5860656290510c174c230870c804c350c718586155c9c12442002891a114b66012de3bc9d500ccd0987fda2868346a807056425ccb8b909c236804a31b10015948eb0f2a2c5104542d04014b3c7941c788e609f14494d1161d0a40448a2e8e00491ce84013929c0d93c606848420603321c7185104a264c01838a010cc0e0820e8313b0056c99e00161b84224e954301f6480074f0942c820e1085925c680273be4f0808915848c60431117cc5c305445c4911448a0891b2420c50b25b4828e54301a80ce937f201c5b43f0d9e22a4915e850a500640ed923cb4db6d9ae56d2e36aaddd2cbb511a122b01333c59e1831ca290323236e4b1950edcd0800412ec00c34b8882548ca9106e807ac18da8d5c2b540b0012560b9a02038810548f11ab000051ea003508895e5db45b5503adc101c60481e0338b98109776b3f3b3e3cc7750f753a7fdd1ef4b421893f6d90e24f1b60f0e70d327fdec0c19f37107d0d1b403f3d36211fa09f1ea09f1e9ea71dee08e94a97d4b309f9e096763d9b904f540fb73bca2199806403927f565dc910a2146065b8ffcc61c89f3dd810391530f34fbefe7fcd28ef659ea25ae7d7d35d7db94e104f0fd55ed4f0c373574df30e10b0e727a493837ec01d9dfef0e45f9a6621600e7fb73127d8aa6e0d3aa09f9ea51ace1aceffef19777aabb976a33adf3dc4db8d862b2857a074a048c8f352901ff3aad3b6b8d3756fb4f7ba42dd73aaa66d46d9b74ddd5c1cbec6cc1a33dbe3f326c0c7cfe0c2ef7e7b354a358b5ea53ab7ba67b4ddb95477bb65396a261ec65133a1eafa7ba364e07df1cfd372dceec3a39bfb7f2f6220c4d1ac2828314080bb3537b36c76833b4240b62014e8e6b07a0803a35c376a5eaa8b7b5d739af5d2fa63b376a38e6e6e371f66b445ea7c47fd3f962e7ed5bfdca487475e936fc2071550414601c39331bce0a4e4e0059c176c20808bc7418830bef97cb8f33cc41a6d3331cd28cadb3de47a8a45afd2152a6d71bceba09b79bdd5aefbe68e7c3fe99dd6c1dc7741d5c9dda89eea9bd5530320e14994273b2e5cf145443d3e2f77357f5d9d85ee7537a97d7d9d52edeab6b8857a78dedebe9bcd2e08fd7f162ec0e20029f054ed54058906f0a21b756f37da4389b75b0f2572f5c87591b2a090781c976a56771dabbb8e77d52aa77bab6b018a635757b5bf536e0b4e782ee275cd28c69d2dd44e16b0f82cc419b72aabbb8af51a6c558e71270b3ffc7f8df7f777b200c26ed31e9f9705dc0a567c6d5d7591678cd36aa21ac4c156c5c08d71edb8ae2f10a76a8ab9c6c06db4c208df53d5e278aa7aabd9631ef634cf7ad5532a34f15fd44fa70a4dffff2e2e2eb2daf1e7e2e222d3f84687e7701d1d8eee34a75533abb3d55a8da33bd5d1b9f161ef429b24bdd5447d046b21cfc5c545b6736202274efebfd6b90e065fce623d3cf6ac851d470b7babd937e866ac855da7bad5c36116629e855c735cd44f4e6e74292cc1ddda0a6cd51c2b56f0ff47485218c80ed63ca3b8737717f1e65cc56ed6c79eab873d176bb455790a2e373641bdeaa335abddcd51ebfa26e582ad4accba49afa94e13d58ee282ad9a953b7777cfeb9ab6ef9e2ac0acdd2a57896a7a450001f474aa169940210b776b6beead4e5b8d02d97e405bd6f1e90142857852a0a120609ada6cc09c541b5520920aaa104a81d0ff57f15950465a4c3f908c4b43584844a9223f4c44b2ae78d6aab418a50bda53bb8df21c199528098fdc23a31f2bde4f6755d223b3d22901852cea540564dca91af28252db8e13d5d28e4e109291d4484aeb047b743d58494c573b553b2325a5fc16c93c524742bca1560ba4ea191ad9c994907e4029498a7a83d07e253476f63db6bfaaa070f714a58b56513e5942d74a6d5aca45907e4c4a448044f62a8bd712b172fa51c448096b3b4efd901d89447a0f5aa4ab3b523b3d2ac77bd61e216d652372a5cb393d4ded1c7bac8238eb1a751e7a3abad255bba4aba3224b5a529deae887f087c8c42423ae423b1f1ff5c897b4d42723349813d2d9c919a37eb4711b91f229f2acfd54e4695d8beca2deaba9fd3444c488a7248575e56454e2647565a48749f64348a4eac78ac47942b8292555543a142aa8a7e5b1525521d610e849ab0d1a026a0d3db13e4061d40afc1d8b14d14db9565a4d436f9f2a0aedb7cbd49ff787ba4592389bb4faf02441e914291a9d7e98d4629ba97688c629261e5311dc7675ec0a04552449ab468454a774d5e259f540e5d2ac454426154ca78e767e96747e96a88c9a7e5352c59c1316d08ebac45605e923251dabd7ae7263d692d1548e67972544e4d8db6c340c1221b2a4cbfa2142441624452c1e556556a808906df7c386ea8cd0600ec9a709e98aad1a0abac575c986d4b259bc98a5c4883485453584823a02527982aeb29478a2a6a0baec87ab0a0a8f52ba9c1591a8223e197595807e1c57a56d44b5832464b58276c6dcb6fdc8c88ed13becf9c152b3785a4b2749872d2a193a5969f1a264c08e84748494797c7e8ed857e3928cf010ad7692947c9e7e448c601159f2211a891a72d2e964057fa68853c7148a35ca2a42c5b38363935a5c904e9d643a256c94ca63446b27379432baa94fdf198ae22959d72ab4c8102b1b6d514f483c234f39adbd23d2ea17686b81bf9facac937a2b5c3206814519554f96928e67aa2745117d8652a11414c7b136233498437512161450c9e875728d74aa8e574742ac2576955262cc4241449e7814d4502e454277643def1af5436724ca585cda5338ada19d2350cf91fbd3be4d3c6355c6ad451da4ada4c7e8c71194cf8fa5d60811eb48698869ef928286aaa47247563a59ab534478340a3955e98a7427292cb4db6c553c51ed9370aa871361736bd655be2a5aedac785a4621ca51232da9dd9beaa6948845234d3acd4599eed773aa54d0a9bf55382c95677584b6e8544fd413cee84705649a0a6afa813e65b93aaba21257a26a6a93a87634d165a2fab56ef1675562d42403ae6faeb823324c87a0766c15b10967b47315454544b88fa48a464c5246494223ddb3634caf947c7c8ce89e9d948e4fb1f3fcc0021af10cb948412d437faa88fc78817c86949472accbe3e394fab8563a9d2d298989889048de39ed7eaa88b83ccd7b126a22fea836bbfe289205ccb53f9c907ad36e2b914d5919c9ea91c20983b8a39fd028d483d5d55d55302b67156584e2452548c32724d6c9e604e411995272a453f5f1380c362d5541112952259565b4c4cab68ecd48fb8338d583733a168d57e8ae89c7d46231011d7154c024a2ce6a0a2a0a0a28e743c4eaca69a7aab843a4c996d361ed8c10714a3e4223a1212232ab5d56cac2f1743f74502851ca28a54aa9de2723e39071981e1987a270464b1bf7133416092a1e7f3a385c51abff18b9497ca82e116d4a433b2619ef4a2989fd2109157f928a14892fab7f8a5943a25567754343232d95ef824f3ea9d5d59092a8a8b42a2894a3c2d21111a64721d3ea93ba4ee92a2b4e158dc56ec4a3e345acde1e5d10d5f147ee68a4a4b36621ca85784eac2dabdd499b74d938b48bb44e47233e59b9482ea22a4d097d6c4aaa94644cee0fa7271ed3f1eab793aa1a62caa911526ea7dd1972b372db496987f8fe569695b2d9b81148a3ad8fae090b281bc18a3ad2495965a9ea902b53e223454c733f76da44a58f4cbfa19d1355aeb8ad863214911c7464d4a387591dcae51faf53136ec7ea68aacd0af658b1468ac5e0540e6b4aa795054969f178d4865aca1de18cfb4a873454fc2185a6e914b0cdfd7e4b47bbf8fe80aa1a2a411195e86c6544682777a435d4fe7250c39d54d14562656813baa4147564522ab2525a39be814632cb63b53475bcd22831352a5115854a6fd493522ab483f2c1fa6137d0b0458fde291f97c8b37f5345d91511223b41536a10f08d528dee6bac2a5e41fdd83c1da1223ac72c9f9cce7b6595d3e930719e9f8ea8a3a5b2cb85844dac100f8d92d21579bad292f9405d017545dc308aa8a35a26961f81a08ae8b44e424582908a4c69e5b27eec56289c52309893c96432994c26c3e18c74419bd2079d23f8f059e8667382b9cd6cceaae3aa021f3d21e5e4e4e4e4c88220e170464e41dc51ce253a51f9acee2bd47970edbb629250af7aab7cd89fdfd19030b52a32f58334a4dae1a1595b36f443eb25ae4256eece110d2a1275a56495443422621c32126c62a582d29c965b843d6222a205a41eb13f4c57534651413c362b61c602ea182d2d097964c6a6a5a66ca4247764c3a52a14562f423c068d1c73b89fcd0e51f9e8041d91b4dabbadf519420559573542fda82a92637a9aaacad94749cb4694e97029ad21d18829f83b6a796c43e2d295139392904a875b7aca4045a2b076dc258df534b4f9f0a7a528a59d1535c473d5e4e35e395171212875e3643e3ec657b87ff07646542af7a854cc42ee0e8772514a4a3d0b1d091df1764a4a56506abb59e00f26f44a2935eaa4a07eaa88e82ba7bcd3f947c6f38bda6d2256574a526cd39015d38fb5fab98a5a4a7a85522c1e2a54c4e85211c182e2057fa02894913b0554aa425ad249e2094aca2e931125463821599a66269d2238f587dd5985de8cc35d69e5dc6e29c80a086978e406d32c2b9ea353102a6a4bb545749e8c6d51f7eea45044a79faa9d0da8772419502898a58c4a3827ee021d611977766aba33ea449ca68ce4a88240afd390a929533511895cfde879b9111e21a1541e1ef91c89685db1534163d29651fd30028ba42fd027c875d71fbc5587090784432a82cb310555239dce092754049783c2e19290a470463bc52125354ba89515347ac2616d239dce49869f744a4846393939391fd385094b26f870f97f57084a7c1124268cdb4d154162c2395d931155940e6656e7ea60ab260d8351ab9a59a4bed5a99fb65a7350371df33ae4ea118f07f39ad45523a09f1ead5d212f2ba3a86602fae9c9c21dd04fcf2ef274565e8d7a7c8a798ac7a798a7767c8a796aa8a798a7847a8a792aa8a798a7807a8a79eaa7a798a76c3dc53ce5d353cc533d3dc53cb5d353cc53433cc53c25c453cc53413cc53c05c453cc533f3cc53c65e329e645ed622dec28d87cb05531113ee1cabfcff7ba2744a0e876e3dd6ebc138efeff87bbef0680c500aafcebac5ed7bcb6d337af2aabe606f0e43baab1067085850074f8ffd7cf046083004410c09210f76582d2e19acf8735578b9aa876df8d8def067504c573357f05f04d7cf8ffbda3a670c64d8c34b1118011bc6b266aa0fee1aee2dd8568c69aa8067d3edcaa6c8ab3ee3dd4ad7b47f5dc4b5bdd732fd59acd6dcfbd540720cae70600e79f022033818a094edc8ebb0e9ab084b909f8c9842fc1cabfd6492564a084265fc2550933ffdfaab9bb1ac5ee06ea2416bd6a5527264c9460e2e4bfc68f5a03d1146b2b265198ab4c6c1e33f9ff1a099c6f17256187e72ae615dd601cad814523a43042d3ff8ba082084e22bc6c51d972524b55cd665dc4c524eeaa7d7df1d6cc7fcd17420c5f0b2107082400e1c9bb1b88bbcaeaae6a0d5cb3ab899a7fd0c407367ce0fba0668909967460490b4ba496e896bc00600a008600000800205202c64cc9164a2af05f3baec254bb9aa8ae3ac5472dd4fcddee5ef170e7f01e5dc7374aecbbc5405d8dbb9a15f66d846badda7f5a037d35fd3b66b66b8e813a57ffd4ce75daabe99edd3cd49c670de45a45db0d6c754a9694d828b172f31d5ba8e393c71d858c5682ee3be41983301a3b9b8d422fe4159b2842799f0f639f7677ceb7665445aa197591977b4eb166379a9385c55445d577575474d44c479d75d44c3adda80de825de4044e1ff7d5df35e9b511ff6ed7cb91dfe8173c4a8ffdacdcf8839febfd6555667b999d5eed659d9e965c08be49df9ff9a765f14cd78f722e6fcc5bdef3de5626d857df8f0e1a3665481da1de635772cfe7f5d55a63ee5ff8399b3baab7ad7970cb31662de031fd71fab39c7fdb45530f721ae452e6703a5d38dfa803ac5fddd3a2deab68887aac3dd35cfc22112cf3da7403dc5b38b74bbf1843c0b7b43b5f3288eff7fe35c2c5c29eed67f8de374d2f65dddffdad0c55b2b8723d66a521baa5deb2aa8fef0de5dd532bfa6936a21cfc2221dc6e11101a186eb5067f8da56fe7fd7390eaee6bbdd78beecb465da30da08b448da196c95f74d61e0be618c71ed86f10fbc5b0ca4a85b5589c5ecccfa82358a75855548d0cfd6962c1ffe6bb71bef08e760ecbbe149756fa8f6be5b1bacab055b95738dfa34bbb92ee69e2f67a8663d9525839502ac24b056c05a82c581ca855a83daf43d7a60e0c6c1564ddb9c83710db87d5353439557d44f6aaa5d740b9ba6a87284729278543863ac7f40866a17f29c5313a259fb743f1ab93a2af77ce4bbd5b40662de23bfac9ad79c4e098310028532c11f66411e44f2ffb55b6da8f621700b78a353a059d040665a271d415a430a951aa12b4059408be806bf2e6a3e9e1c5d53f1babec03563a0aa62dc559f0fb7ea50edc52d5431cfc715d76e379e0f03f750a7b8e6f3e1e34f0f872f8a83a66dc618c7c7f3d57c3c5f11fb6e783650ddcdc2b6b8d3debe3b6bd7176c55bed71cef2a8b54f36d61d187811b673525aac1a1da7b43b5fb704eed48877dbe35bf1d6b61c7a96655ae0a7916621f77f570a8f27c19eb5f1b54f950edb8d5c39d93876ac7191f09795e728b6e706c88505dc5876a2fe26e512f5d776fa72fff09fdff1124b896b55f2c5cb6f25f4b812a4eb5ba0e872f4fd70d442df224485cbdd3971f4182b590977dc05ad877c66db4ae192de21101a18aba9de5209f95af1d75d64edf1b5f51b73e1b9cea703efeff4c3ff3d9d48e6efaee32af97c3ffd7fade533db587a477d3bbe1c111b51b1e1b289c93c31b6c50ebb1db4ddbbcd397f7560bfb8bd3da05b2db05f652b59785ee75f754c7f360e9bfc63dc09fea24556e764fc242922bbe57dcdf8ddb8cb7ab95e4b4bbc52599a9dda433245864e5e20c89947f7e84e4c9deaee6b819921789d07fed66771b5c44f2ff5df3361fb1f27fe4c96b0dc4797864f85f4b792e1ed9f9ff9b1caeb2fcc8f9ff46acbc1e1e3b0ef7ffbffdccc893df1d1bb9fa2c349d19b1d1991969f9dfbd0895ff2f02c548bbc05e51b75b58bcdd786951b71a75dfbdbbdd8a50b5e7de74bbf188525dee76e35dbd573e59583dc0eab5d259bd5425513580595553d5e944ed46bf6eb7c19aa8ae59f736bbf90dea15f7dc8fae266a7c638371b0557916a6adc6be75cd3d9cf5ba774ead5643bbc6383c988fedf137d4ee1027d5f9e5adc69b876bee0fe3dcf8f4eb769f0fb75aebf597b1ab819cd7a8865444ff9ffb190f06bc23663c26339eafe6eb7bbb3ecc55df06b6c3ac5d7717757be3d3adb96f9ecf867715d7dadcf56eb693c0ff0bf9d98eb82b9a026336356598b58b83535ddc6b0f9fd6493efd72aea65d57737dda4df261edab4de929a93066524f48c55073b7f6f9b2eebc557b9b8b9a5539181acac27db739c991bcfbff1f3f93b29172893a4114105102881a46e17ccdd5c036bb37dff0d86fbe54edf9704eadd7f79eea71b536c4f1f9d68e66235216ba9bb47681ad56b25da06ad4693b83aafa7fa8db8d45af845ea09f9ea554377b4a32f4b3271e4e4b38e1e0f4f5e59dab409c727c1224384d5234351de970cf6c51b7ee76ddbd719a8538262cf870f719d30e4c554cffb5feeededd8d71529c5515e37035d8662229911cff355d0c66ba2959e7ab71fe626e35d3bd2c4d60c9f74990e4d9128ed217ff4990e01c41d2a23ba5294a4b3e099260ab1ef3101f4152bbf1b1e855daea9d4f77e0b6695d5dc45ceb24a59e24276ac7956761520266494d707aab71b3a45d920f9213bbede9acdc7df89866a419905a248c7304490d557b66777f737b14c491048e7a4730b920726a6e063eaebffcdbeeb0d5c4205ef3db66275c4bf5510b3b7fb7ab836d4e6ffabb8fbbe3346bb7b5a91dd7a1d10766460330b2fd3fef2acb551557ebed16ea22e659a8c1cf702c98e124805b9320c1e9eb7bd3bafb6783bbca778b9be1cad9902a86b03084e89320c138ed1ebe79771c8ce372218feb9dce86d41062c527418235bbd1fe6e7c04490d49879910ded7d635a3792624561445110a4542ff9f04098e51a7ed112435df0dc649753e216e5f2006e25bd79cf3a59ac85d1ce4cabf42dcd5999f0591126458bb49dd5f576bc19ab60932b3ae6a4ff5cbf333a22affcf79332227ff1a4724accd8876bef6ebf133a2ff1a9029359ec5f22c0402a5b79a0359d2acbb53350f9b7e76c3a255815ac8bb8d6076dbfaff5cc4bbc5e1b9785bd23fde633684a596eaaedda16f3614c34eb95ac4c79b75cd36b82d6e57b7c521df50cbbf0602df1f9a85e615673ecc425747b52abe09b66a7fb7bb53bc7b4dd7541bcc336f2664e5bfc667424f5ec5fa87519f8f371312c277cfd16e5151d1bae65c113fee30cf9847544464dc4645453736442c7a55c4a2579debb4c7e7356ea319d00a404133a09a9f24663f5ab39fe0cf8c99cdcaffd77c6876731f40daadee7e5c792e1e7df4ddfae0aa707d81b8dda25ad8b9ba559f31b3fba9765cd7ec6e7c5cd320677d42a2c62dae86936a9c8c7daaf001c227fdff5ab0559d72573e9c53bb71ca5d09bb0d30ebdd8c0706339e15cc7806c033020fcece12b31d07ec20f9af699e85a8fb0e9d7257383939ef8ab66a16575ddcf3aa66a0daf361770385c1770f5f8c93bab9e86ea04e15339d07cc746ad089fa1af7e19c1a5781bb7d5976afebee46a459ed05b31a0eb3da0166b5a3af6957e53a4ddbecea60bb57bcdbbcbea89a673946cc72dc59ced4ff67fd0ca70a1c0fcc70569c1ab31f11f8a1ce7e10cd7c4c31f3e164e6a3a9868f7978cba91db313ae690ddcfd88f636a7d6e38371f8e63bba594555a06ef5cf87736a363c573eec6e8ce3bbb9a9d56c706c7aae7c58fff0baaa56b876f375ae531fcea939e5aed23663e01e669467dceaacdcd735e3a4ba7d5f614fb9d640ad813e1ff7a958eba41abbd7ccbb09c1ec06881b206ed6ffafdd7c7b4f39e19bcf87736a5d3d6657d5c2eee6a076317ff171779c7116c7f5776b56ed45ae565d96bb4537422fc6117a6d88d6bd7fbac7d52684d8b14381298656432c40e56a02606071e2c410253a4e5cc0a149495472d5a2009921300b58d99231e0877f591459b274810393058d2e5cb05226e603ff37ca1f70e0046459890580881a2f7c167083c219f2b922550496a52852644f1fe0534887b08598c592aaa389cc8785a5d4599ecca892fd701980bef2e20111cb3c70011b9a921eaa381585ffff30330770f25fe2fc973f4a9b190758cc386052faf82f6f4a9b07cc782c99f1c03074d001dec14168f06b03af0a74d0010e5284d356ffcbe95f425e375c4cb8967085c075c4ac8601dcad7577736376e2ae2eee771f8959c8b56acc4e3daceeba63ae4a97abcae56c3c57f5ee7255b9aa566553dc144f75cfc5bd26b99a08b5e3a0d6f12ed54455a37aaa1bf5d38c9fe930c14c47947f2cd469d6421c9cba59c85917a79a2969dd59e99e55b7e7a19e4aa9baee7c3775fe1ea5bab8d31649d7f2ec5463d5ce35eea9ae0db5ce62a7e05a8a136cd5de35dfb57b3daeede63e1fce986bfef2a39b75aaf260bbd78c7d3ecc713810b8759add8eb966378ab96a847dbe7717c13e9f0f7f8071511151eef908e3609c22a2db0db8ea1d11ab0a77c3ec848360edeebec318030063ac5775c86e74b73e9f0f096f806beeee7c1db2d9a831d6421e4e759a3bef9877b57df19afb3e7635555f2d75d54475c50db5660ab66a9ed9d0350dcc9cbb78b7b8eb23c6d128fba26fbadddeaa354ef0ffb5ce5fac34e3360bdd2bcf421cbe055bb575b315c659e86eca3d1f4d51dd10861112cb8d8326dc0c4c6924e554b694354ae40bcab214028354bb6f941090cfe713f2bce4c3386dc6d56e8078507578ec3638d53e9cf116b6c6ec94ea1ccdaa3d0ab86f6c6a3f708f8f2f2b779fbb3b9a9370f82be4e15a8e7687386b77b3d8e7c3c0dd79abe236a7ae1e6a9e8d6a479de5bbb1016ead44e53eccd556783462e0e6f9f7fbdacd5167d9e08ea387d909d7fa7ed2eb2fb754fb296da980abdeeda75eafd733f6e04c46e87543b3180a000943280b1803f0993dce8f2c47ff7cc855ae5bad94856a236f98dde22c26c830bb45bc7be6398a6b9d749c7273df49ad6a4e724ab5ca6b7556e6699ef656ef70b317a7992c89efa5fad8635123e7abee69abf9cca586ff3fa6594fb95a8d72f52b550037c5dacd1cd78e6dd6c1dc39e63d705653bd7bc63dbbbafb7a7c5e1fc6b5e26eb3b2d4b5887511d7801bb35ac5ba8879ab51ede2ce378b79c638dcc51c6b377776bbc0765d71b8c657ccdf5ddc6d16d6c5a68d72e076ca5d618d77c747b876b3ae19f7cc6383533d1c66b7bb6a15aeb96a1ee21b9f0fa71ac706e8a707f7e8acbcfeb01676265ce3c3ec76dc35e75a3dbafad5e2bae7d57d573527a5addeadaa5e7fb9a5d2eb4fd5f5fd74e46e21da663dd535e76936da4fa9fea92eca7fefaeb89bfa7eea5ceb244ed5ea58d4d84b5bcd67e6046666149cd90bc0dd40cc55a7dc154a448928b155d934f7bae6699af64c543b9a33eba6696675577b2831d52fe76a0f250acf2f663cd74475cdbda1d659eb9a7bed9b0030761e25f658b5c7bac60d06152c6accf9a28a2f90f8a2c9172218b71751e01bd7aa71bb43edbe43edbec356b3a946db77b76deec5c08a1838791162c0c3a873b2fb66187cc1f19a35c779eac5614dce663c41f9194f15f8194f5840cd7ec693909ff104fc194f5b41347ae024b2b072022c41905790607ce1450c608085ece29fe4e29f7cc13fb90599c53fe9827f128be13f79c5ffe772555df0f05d30f9ff2e7c5de02e82e07061c567b5eae4e2877f2e4ae8f1e16eadf78220fe89e368e70b6c5c075d4ddc6c8e0ef7f932d6bf76abdcadb55be5bd2d725bd83cbea5ad9a8571f0edd65d4d6c378a666da29efabdbb54a7292ed554abca94c50bb240e2ff6bdc989d6a1c075b15e3d482358d718046f6d8bb76876dc6d955a174745beb275c3bd2a9c7cc726376aaa938e7f772dce38335df42d5ed6dc6fae76a252a5f57b5c8eaaeea767734037f2fafe135a769c6ae1ea22acf46a976756e374e572d446ba9eaf3f18c62fdc36dbbd5be59bcaa68c6c75567a9ab0d6ed5a0ea6ebc669c5d21cf38a8fef0d1d544dc7510ef8e35e7eeeea8266295ab2a0ed77e2fc7bc876637ef19877735b87bcdefe5356bc66d8eab872f775f54fb7cd8005b516026a89c2b282b1eaeb5aa50fff011d8ea56edad0effde1d9b9ffa7e3af6218b94e6b44eda4fc72c54edbb561355253e956a56cd6beef7eefa7eeafbe9c8dd494456d081150c7bbd1a4ff5c65d65d595af6bcee5dfcb8fc335dd6c76bb11695d73eebddd82a96ea98ca0a1070af06e377ebbb9b55eabb3768febdb8d45532003c872b7a6fb30a7b977446f3757f758d428e47929d50dd5ce6bf510ea05295040f57097856a61ee890a9b2ab6aa305661a4631c7c63b70bec5521830217e09bfe1d335bcc6e2aec6ab177a2a001270a54d8c2627f77afd766f476e3dd6e2c6aa4220a0a9ca84040e73ac538dcadf1aeba3b87458d3a2c6ad4b1a13cbaa707d511d23d59dbf44f1ad40314f2d9b1e5f4dc8d6fdda871f0ad676111dfa6786200244c11c51229aa70732f4db3e66ed62bda43b54ed55eba6a15d8eb9aa72f566fb8ae6ad6efe5bd34cdbfb7fdbdbcd7d557abc7f3bae61ecf3d21daf3afa7517703855d2d9a22c43cf91dac16ffff2d3cb476b6ae644d7648faffbf31841119173af0ad709ea0857749c17902a1e76e0d26823fae2fc6f99f64e29f5ce29f0c01bb7769abe635aaebdd110a350bdd2ce6ea71d53daba96659550b3bd7425e11d7fd684464cc4eb21b4ef18d23e130bb417ca4c3f8078a5d7dd4591803d776e31b9fefb8daf02d2c628e8f5ccda98e2eaa8f8e707d3fb1aa1ba59bf6e665fddea8dd76b5912c74af46dacd75ae1a91fa7ed2c323af4a3982b30929fe3f67d722e95d9a8db2d1aae6a41d538d3be5aedaae8556b8a6b50b5cd54cccb81b75cbf5fa7b778a375f71b8e6f3b9d8d5448db9cf87b34ac4a96e337bd4c28e57b5b72f1e6616eb603ee6ce31d7c1f4fd616ccc2c4e35aab2b93da6586fb7b71b45798bc335b5b79a63fdc3bbabb9a85d15ebd7ed98ab78ddfb878f5d75b7ea0e7384415588334e5bcdf1ee580b3be62a06a2db8d7bc698637703718dfff8fcfc00ed1617b71bc45cb32fe7196b2b5cf3f9b02f0bca97b5cbd3b6d5c28e71d6fc6a69acddbc8b5d876b1b779e579cb61927d8aaec46f50f03774ebb5b9d54e7eaa1fee970cd0d0633c635ac622dec3b9cb67ab7792c7a9566a3341b7595559b5c4d8412410cff1867e8007368269948222072ca5de54439e5ae70b0dd6e5469a43cf93bd4b8a85b9c3190631fb23dda8c9a5330b1e4bf166cd5e170bb293eaebdd5abca5b1563ad64e39bd293ee690a6735640ad3654ac09c408f5288465b9d0153a3add668abcb154a1cbecce1c440d17faa31cff8d887ac6e7fc3cdc3454130c71bd8ea86ecceedfee2da6dc4b96e4adb6dc4dbadb6db2897ead2761b6d6054aa8f79d547ecee7d1b0d376ff37a3a4d719cbb5ac5f12c7437f11ec539170eb3ab74a4e33a2bf75dc771ddf3940eb659881a9172cf477a45f391ebbe87994d75aa7b5eb59b4b91d2a35d9543c6c0ddaad8dd2a310bf50febbe89b8af6f06eab4555957779d623e5cb550b3aa3bbc3962d9709ceaa540a8c46e0b07baa2059d5210cdc00c30000000331240304828180e870422c974dbbc320f14000476be7496441689a2284a29658c31c4100008000180011081a11901a50a9345c4818107274cda9a0c0c85ed8f84ec20174e38f0762d35f3e17834812a5a3de2b367b2c82645fb14053d27c3338bae65f2b3c1ba5ee8881cc5cbbcaa73fc18a1b49548bbcc7686a570487eec2d5d23152434d397e3063ff50ed5a5bbf9c2a6065d22e874146aeeaacc42dbe5e9ca27b8a8c8ad9d194ed8c500aa775c96b657f4b60b8751c68c7adc1745844210aa6474894cc82fc857689b6fee7f8b19cdfd42f9801cbc034be1ad1622208d6472e5c74c065a92df835781ed4a0355d081c692ce7b60af1105962d042f8beb3017bbd86fbdf08db41f9ce1de5b772c33634dcee83a179997defd9640b749d857a245d796aeaa51a0da09570f4b07fd75cd196e51f8bc7e29e65b3ee7f0556d2c8292af2ee9bdf6c50a5da03bcab507a1470a12db70b69b430351d03aa852d822a95e245bd92cb901d18b34fa7135df008d66f8e9e6041b886f0c80ece29ec93dbf0b524d4c02cf83fe975946d725059c46513a620f777d55f082f1ed460e70021823225241513942daf19a6b71dc21f0c82d92520b91df055d71b905d0d51c2bc18b0803849f54ceab59f68b5a8124baf729e2d055c10d63c1c8b17f7d7f92924b20627241d9111b2b36e78cc8ebfffcd95e7c574ee4784651098f40fbefa13800442748e042d32222f59642d14bfc88e1d3f3672a67d1b848592af3029dcec8b4b20d01f1d7c685451bec5f8ca459221500e94528ab762d659b22d38ab88a8ea616331031d0c822a86d8f9eadaa92102e8676b6cd899bfc3ea983182e881f5f35d61355b12512924a13f7aaab162df374a8f3a954710c9ad845a08bd20ed1c5c225003e844d88eb7ddde024967da3dce8192eda4c7377a59ef3473477d81f17f2c7a3b4db8fe38d3438047f7deffb21ddf7df614d857576841c57847705a82b2f05cb97b391e1d17a093255e34b7ef451414d3b3de4fa87b28f08cc364a591afe8184755f587556bff62ea5073738df1d29dd2aea4ff6bdf02e7396b1cd715179001b5fdac83cf28bdf71370fe27427b7a02ef9b386f4cd14d8b9e30a25f549afadb5ae16f949996283215d18128deaf10935295c8acfb210838c44db43f2a07134eb5a934e7057ba1fe97abc4c722f4dee10345e7d0f4a40e3c93067d27690ecf8c6d874c7bd346efa91f084e2d3ab2307a362ea8c3a4fa74a528b26d50f096b9d2d8ddd6a5efa4c01481818289d3748b52e02ab71b2049d6ce8eed078045b0e90b3c7d69f10bdf7b2ef28ea5920c94317475f1ce55e982d6146076f7cc12bb49e3168d9603b9375d2edf566eecf778e0cd4aa72efa214d066d4cc2878e5b1c0b5bffafdd0aa4923e8a0d47fb87e3739d2e3cfaac840f426af282f5d2d5daa3b099ce1e2d87ac5745fb993122909c9b8acfde6ea2a62c2dbe7dbd27c0ecb09f868c4c51cd6baa8538406d8c36851af7104786ed1781899252d191788380e5b5e723019cdc0c0bc9b7ee94a45da59da4fb0c7eda51e03339c336803401d9f7eee7af548a821c5e28b4a4fdcc3c840b1efd9f067ab31997a8e6fe16dd90c8bf05d0d7b765ca22c2b63fc5cd54dfdff1e933dcc74fc12c525bd3760c6d0604ed28fe5607cbb190b94d49ff29764c701337fca5f3faa63679f5120b6c45d313a8065af4aebca133da35c03b26de31dc507f4ad6dbd065efc681b2481004cb81c1f61a5436a2fbe1075260e1d605af0f078770cfb889e0477938e2b60655f2badfb2a7a9970834199be0f0f746bd8962ae4cb70805b2a527f0580cb751b7deb5654158aa12f79eed704ebd721bd3187fc5ce4a5203299a08390fd6bef056e317539be6f385b3a8a5a7b7432ccdc3bcdafd9539605031e6d4c56d24fed4d0c7f4ca4e4b7e14dfbbe9b0591a9ae4d5411c15573ce091e1129a3c4659cfc7c7f11831350e69027005e95d4e5362587be15a3e4c5d7b5e1caf7d8724606b1c61523b0aa084e4e4319b2c40aba75268b2b376a4582b2b068a0c310ea4c6b43defdc25b5586399423805154f9af4284f4af25b101af37af329be81a31a28feb8b1de7923701db3a28164c8819bc050bc4ec7e83a3a7f47abf16d952e1455a962392687b5fa648331839f6f8f3d3ebcbcdc27c04e5239198860b299965547b30ba4e41e9d3edf2507e8ace1d65f9104a896ea57ed04a0990e8d94d338d846bb6e1c2793f07ad7b68fae5b6eab5233197d0cf030f64cc480fedd8ab5d513ec54ef4bdb40f364237bbf0b39f99f7cfb6759777623ca6e701a4d761ec50d32631f5dab9c48ee187598ce16eb2d606486e0e4347a7e15dac9c9221970e3dbdb23da05cfc8c050c15b6e81e2599c79b2f3eda245fc79855aebc75495cf420a9470a155cc313bfde7ed99cd1e7664893394a47632728caec2c7e7dc2524884a87b45613ec3ada006a06e243b0734c7106cbd9309f583d70ac90d91f4ee04c7c4f05ef2d9b5d42b44897f522574157b13af9fa7017adf2e95061f0d26ac51a859b415d7d536a68e59a56bc9a2d008891cd69b74073432abe55c3006b84ab5e31cdcd8b5286586668291833e35deab95f3021077c40e38e29710ded4fa8f9130138350207f015e387cc7bfcf0a46713a42c01ce8dda4ca5e2c7f39c7072639eefe0712d024b0f1014d8441084be4ed32bf1238765da6c7c21bca8fe167dcbee4994549c18a9ce7b067dc8ed713c117f3d80887431a58988cf26bd77ee8a37e86f533cc1f58857e0faf920b5f31b4e8c0406419362703e3c49f6a82c5e7d0db0284988321be0276986315b1f1fd63a9907805d979839c281f39c4c9f1dab4e4f03e22b4bd7d11989116ec85fe49804544a7ead81078fe839407b2a679121ef8db4ed8507f1d38076f36fb33fb307d5683afc28e1d38d57f87f5246938256a0358f70b38d2e0e56b8a70f9227e3de11dbab463d3dba2d6421f127e3fd432065b3593d348584f1b08a40e39b15bb8db82f72a12a849a5556b68205bbb912d3a23219f482da9682625586ed6f969af45eebf82322a4f40d265919869f84ba9fb6938f3568762375658a6bbd1e82e552f78ea7deafde8287e21b36df2cfdb1adffd7bd669600991037efd0515fd02ddd7df16245fa0b41ddae6cf5d115dfa065eb811810a9998fc61d7745dfc1d67230f7756c33589f8666eb25aa1477e11409dd8bb8c0052e37c2760ef43bf00723996670a8db530368a1bf3935af1ff98bead1be563cebcad9907ffe3d2e9f2f263a87a769383e97d38c057e35ced9c1d3c847b9dffc0d8190772864f391adccb5ba522c376cce3d118befcad0b4c334c922b18755038ed43a278b048d1157ad029da898231b23e578fa387110d4c22d272834f926c046ecff4f6093f9a766688e89a0946a12f69172919d66b16618a2f46b4231bb8a8067e776ffb7c75f872d7d4f8393f675411af6a221787a03f5e36fb6b29ec067e8edecd1ad568594812164599b98527ed1ad6f4699e33864150995545d53b088d4f85d1e1b84dfc1b9b63f7239d11a2e80ba9d5093f9933e4b933282d4702d979d4da9471b6982c2b34190a83ac6fb78615bd9cba652ca4606c711936cdf9b3665202a32de1ba0547e0be854fc9cd7a83f61106c747bafe750e7d41c8b36d4e631afddce66874905ccc43528529e242fa4e473e28add15887b9731dd004055465b160e1f7cab382cadb3178d1a010e0a65237119232865c61de796caae51343d977c73c6a6379f5f911da408ea371fd0dbd694321e5adb2cdea5c7c22afbc27424b4b107db4ee344d542235458fa01b2037c94f7b2ea49bf03106ace60f577cf085888527a482255a8a40d67b4775b9b33df0fde2565532034c00ae0ad61811c9e87335d1ac81ec5183187aa9f7e11e2531eec967c51b05e7bf82a20edecc92b676383d7e082d5bd85411cebee9d6a5eb14e629f88c67692e3fb3148036af519b5957fb04c90b55d37b9e27cb11d220a9f22488d687efd681f884359b992e8bd3b3a3ca3e5499880504eb0a3a583111e6450ec5962e010f7795510ab0943fceffea964d8f00bb97d628fe598b86cec8101e47ebc3ed1729343ebf1d3efa3ccdcc13e7fc1b17680c494cc06cdde802162c2ff8d74933879f387f1969300b0012c7db0ce55989d9cf08e59aa0d06cec69f0f9969d46877efa4a08c463ee6afa4dae82de3fe4dcd329c2e83918234d4742c39b0bd976f59b1563b5fb217914faad3bc32f272aee26a3e79e98424b58995085da116daf46aaa6c35d6021a045b8df70b35786dd4a403a29e22ef03147f47140ce1d3294706b61dba7ead0f70a8f4981533a15e74a6d92e7d014e696c58282328793dbb7befd011e711800eb78e50a1174216d13c5ef616ebf334f054d1967799cb83d492c8d2db115a63c711f42b1db7b70a3aa914621a7a576f68c28a3bf29520e9c2ff41d41a024ee1db148e6c3936660221973e13b451200fafe4f20700c7ef2c6019d2376165feffeb3f38ccfad1b3548b3e927b9194c7c25f15d2e573c5efbe67a60f5a269a88ff9d717b861e8b9a38ed12f6593a905eed9fda18afb5740f1ec0aaf7a6ef91e3af158aebc65bf53b603b758e75f293d917d2f58fabefe8419dbfcf422d284bf5670aa27bee1b702c4408a92bd357a597fef0922e51f35297a6429a30b025a63f909269a5cf47c9780070e201053181c00b0700e336ede9402409f4a81ce4fe1a440ae58f9c26ab318fe09627460d01f09cfdf31d6c443d54e95028d69dffbf7e35380a35742e0494217090ca64ae4ecca20809c883e87da03e02bc14d3c89646c345541b5b45ea8f0483036038ce62f81e96c3db3d4886a8cf26181b1fe188628c90b03d58e2523b9d0b471d721f2adb319b143a7c1db0bc7f0dd73aa92d06cf0473bdfff6efce9c38f7f63ff84ef8b2d41ca5140cfe71fbb4b227e76088b25a42ba7b2046f8fff0fc9b6f5c0bd2293ab2adadb7824c78208e89c06015e80fa26c76973a97e0816884d2b936314c2443b0684abc6f5af3d6d1cb4a96ce1dd2d5cff3eec6fd66e6dff10cdfcc1acf65eda30f9b03a93ca470ea66b6dba71b93744d499478f13e55f59c2b9326b32a100b870b2fb2dea3b059c9af5af7f3d9cd72d591b4c5adb2887c7382091db9a51a45332017be229da0b93c384d491b51775697e13b4c20bd2b2f2da51711eec7f30ffd07319b3cf8768ccc23ea8f05db68dd96aa696b50ca13a7c7786d656258a3497080654ae54d8a4948090e2fd9c231e006a914466d67d2b277b832f5e617c1ad27d55f1e78b2f6aed97e52ef17932ca4d4ba4322326ce54fbbc041188883ec78fe40a7d962cbe97bf7bdc67c5940a232c1998c2fe41be4e9e58c8670ce3a084c5db52e40ea1201bcd593d4c5ef5d85a8d4d9095b7041d0fbcda42dbf1db73776794483eb1782403e3f2d33da557065c747a2ad72bcaa86d47639e076a583488954cb40060967179b740b4727f675ec85d6c9e6f0d0ea6403f8741983b234de2943c59d170330c8e356baf414e619e893effb768fe49f425386c765c5326f4d1fbaaa8a70a3994f558d2466e43c9ca823059240174cb2c3cae6c4b3551ec757140d63c51674ac5e423be53f31305aa9b6276065bf2791c7d3f443194308c3f20cd2c9b9d518d51a26d65e8e609939e981d300f1f07a0dfdc338f9140968e6772bb13d2b7e37d8a4fc15b3dfa9861b1f5e10092a6aa1162544016e02c80e7e676a3b7e5ffb99af8791b1732b5f82b96e0a61db42d7457deb41a9d3b92b3c96c121d6b372c2c799252630d0aba5fb4b3deb654238a01ae81dd15ae4b53886ec287ea4e98e865bcee711c6519872d821091ee85c1871e50649693987fc71bf12deea59eed94a6d4ccf907cc96c66d31bc83009aca743222d8e60d9e2da5dd2f48f5e6e36c62ee49d31897caad5c9edf3c8cc819c1a4771e0af9b8cb8013e414118a20f9a49b2bef4fe41a20fe56419f1a6240e87702d87fa6e6c3cee45f13b01ef285eba766de752421876eec4c7565dcee09ca450a0812bc0b3745239b4df3061fd1362208a77c00f2a019345c03fa4b565c988ca096d6c20062b814d8f8839893bbfbd0e4fa85fb89a036c2c00777887cc9092f4b90f7902e6ee5eafeabf0fcc90b0c161261b4620e5caca78a068787f8cc0bd7d9114ee2799d75f3a727ae4575045f33402bc86cd4ef8b9f730b348b52d429f48fc6a20619c4d5cfeb39245fc13be9481ebb244d463e70f758e3b4aae46bfee2d9e5315bb7755bd5cfc18e63566ad2875273a7ab9893a56619b6179e2df4830a17311bbb6a5ecdee0fa95f1bafbd4513672621c8ddcf57b56fe3c1947dfcab88d06e9923e38bf7c83ddedea50b5815caa954f413443ef45d789b52a3f5776614969930fcd117254796a3895482ab349577ba06110c69ea1ddef78035945cd283042000be00be9f8e41383ee9a4ed553a4abea022dee6dea1f6d2874d22c36bd1cf7d6d9d9de9e44b89b06f9213fdbe530865abcab96362b542572b6bd001b1127c8befb54a2a0eeeb4fc33e6aee6f1ced4072ea1c79dc4d6b3c1da8a1420f53de82a9b76245a0011f55018edf1c52e54482c3f8c9b9d74411486125cc455fbc0f4b60267dc9c43f01a0132672672120b27ab3cb45a7126df38e4831332a40e5fbe971bd877e984f6c75ee050697363a4c67ed0a0240fbd5a4dd2346d05cb946e09c109690818fa4bb2948de21d731cf6149a3884f6eb02be4408300ae04da8f593127aa20f5d25140bfe4defe3fd3671aef1e0c2e6af12eef23dd4318f4e5278bb1dbc8ac7c150a2f41f7f5f0dbe749d5efcc0f897f95f33c8459e9e59318f9de2a4d395c8f7ab4b8e1c9fdb84a265cacdce72f1048cdd90bcb18c43f63a5489a27b9e7cdeb1539dacdbc2e9737172fe2b5c6dd4c8425efbf6310f3da55380e6df4a8449d5cfd42c57cb1a10723aecb5dd8956e9822bd8732e9723e2b05edfe91eacb74cb1286d22569d8b134ded819a4315843c3510a1007863a6ab24ae418b4ec416b1bbf8c6ebd64ca15c0332c895fa3d49b1cabf91148a36574975aea5d0e0f0f25c83e1acee30f1582e67edbfec1436361609ccfd1f480a42b5c17443b26e18fd39000643797c79494dd3038e02e9574eeaf1233f718d3953ed7f570789bf29b1076b61dd4fbabf64b6c2cf3ea7da0530b4fa1aff887a7d8d709fad821af82c6e2ecb47b97eec7417585bf6908772b31299e2900a72734730b53121671b0554113f395c2e1d785f1362d5a55a735b8f1acfbdd747ec0248a8777da05c2a87937b718b1fbe49ec6d2e27a111163284f0c7c2a45e6c900d939e56000f91dff5f53b52cd664f9a54288e81660fc853209bbce147abbc28185456573ddca391919cba74dc4067a3c8b34dfb70708d55ca11a2b15809df80f8b54bc3607b5002a99e006fa1803e89750fed54b3350900d0cee9f8af7a6b21c28cef8e5667b2a31e4f8d05a507427358347d20125be02f77af3a40c9566e6503cd1d55d69609cea445c485aa5d44df7bb65ef75a0f0416b1e69858a758ed5b7c45e868582267505617f30c1da477234ff6821518e79759c9e50ab3ad1fa8048896aa6b44d8a3470adc50e0c135121e966e5a7f0f6b91335fc1dd59102a7f1d1652d1edc6566c39111d7b84f07e2e4cce417a7b587bb8ead28669b02bab063c4eec7be09e489fb58384b3d68281761602fb1f33088b875a53ae2ecd5b8e91ced7bd46ed1d822ad5c8ced8a611fe47eb88da88acdf9d82a21685cdccb4c9807dca51b1c77ae397dd24315e6ce3b5e4c5e0baa9d837e61c22cd97bcee03651c1aad360cda4f9a9d93605fd0046fc455b1e2279dd94fd3454ec8f82b413b793ec37d3a409955ae45070e4fd3e17ec15f24e613fcd9f376b81434d031ab13857a502dfccde407b0bf9f66a917627ed1f44679f14a283d56001279d58252b32a5dee877d6c1a62777dc5504ce02472258dd5caba27b917fa8c94d3efce5f5a013590a877958a8eab39deee216f87b17467399765d557b75c7a23d95f8b5931d1f38fbe4593885ee8be1ed1148627116f7118c3d3af01febcc8e9882fab7367df80117c26754cc15272d463d06aa3f751171137a4b79c395985060b3b5d38ee2e9c00609998f220d22724a698713876b2f30da071f7c41fabb44c7f60b4c8dfe5cee4efcaa3a24f5fbc356e4510b5bfafdaa9c8c37c6d2658adba20414cfea29beb70497e50936b0b1e5364c1df1fb99a50c831235dd4f2859900cffb59c10fad41e2e2d934e2225b3ca4f21a2245692960a1f8f27df35860030dc043ba742a06b0fed01569ee1cb07989e9c6b52b2a014025e220ddf52da63344fa7f5aff60b99245e1480a4441b72fdaeb372b9d374bba199083043965f6a43579eb01b94f310917d90001a47e5f01640f6a05572ea298a679bebfe3d785830e8fe866c733b5d4f497237ce66d9a4cbc31ff81a6e9b27b62855d3e3afaa923b74297086a2a39de74e1b4ecf6de2f7e379e6cb334b22e55d60e1603bea21105ddac7524330ff3b3837f7171c25c1512087df83f0f686a0654456b53adadc04aff9e30e1cfbdd2bfa681ca532f42fdfb76d7b86cc047e258331083b75b15ed3bfaef0b888dfa9805079e80abb4324870270e3773e5e5c527c7328c62e74809e5109a1db8a737643dce02063d4fe64a15060ea89f43e8e1c9c27ac825612a9e3795539f118400a3160af58dd9940989050ed32cc5b1460cfaae1671ed2023721f11c26d0bf116e8b825237d7a064d5300a3205ad82f7a1cccf05694edc140b8beaa50529752b222b77498445099024117ab7708a1531730161ffa972c76ed200e68d05a7c48b9b6ee03bc25af908c8ef839179cb5f318991c20ffe83d8710e8333cb013173116f376b088326980089816667317f13bae751e6cc2eaf3c747e8c96dcab912bb9e507a27e881e0248887321574f090dc6d8a7460a8e29e0956cdb160ea94e694997e0dd4c5113e5defea52e8256860f80bc513f53721400f07cb12c917250d7f09265930a542af4c00f90f2d32bf65f692084719cc97e36dd92032b8123e102d8700a7b8b0534b126695e0d83675e04eb04df6785877db1d3f88da9d35343e4b35fad9f61533bc8e36458c6082c9d4c06a3ea9e601f9ce020cd727b45b531e553f34f507f7862e61981dfc617f3c7b56a1e13c0cfdb366c8607bea03c4819889b2ae5bf721403d9dcab0fd327643c766b3713ab64fc3ef395a0a3ebdb20cb134599e60de0587f3fb131ae5f091104126100658223842b52357d90cc40157db2077318ace63bcb58202c3a8e31287a018d7dac4bbc979c34e10e2804a87112a81fef977d8f38797872c66761b3325dba4f2c9f843cfdeb977ec92d495941b0f11ee3e00a7b83229f17d5d80655813579c21d63d7f5f79e6de2dbfd726b66f51579ff6cc5bdff95f18a2dd2b0c10a85c1fa240ae262ee7cd4bac431434224f817ce00332db00526f7416e039632c0724913629f16c4dc74a023e5689755b5c9e4ddc750852a9c153f8a1c2de24ac10176b5a44af523e64c9a23511349fb1f445c897cf3e99fd32e80e0a715b3e87f813e52aec755a53d243b68fe2370d80f955eaaa7323cc120232f3c04e17cbfdeb5e65279f41f4a742d12e93d323a9559ca3ff82debb92f00175f5b0424b696fcaf415c4f6a77d4c604cfc831b168a30d888226829f05c89aa467fe242f234026060b4601247f9a598c974068c42079963a74c19aae4ea6bf04ccaa2a49f63fc1ab0e95a006a9ace602dee689bb43fa01df09bed338188960d65fca6431871ad27d0015c19f43dd72a4a8208ea9f3ff07e5ded5824f846d416d0025d9c5caa8a8a8d0b6ad7de671666d466f09d94d755e13031553c84efbf726eb0c7717b00ec0c5d3cc3a830e023ac09cb25a3ec41038081b12092eb514073975a4d0c69d9dda96bab5149ce164b8be9c8a44f0f98d0c026d005b9bc91ded31300b0c721a049bf250ad259ee5d42d30234cf034013e1e443991f18428f49d691290a5949b1d0f626156c51e6442788b1e89d8cf39d84cfed36554dc0624b06179283791cb3da12cb34f0c6c44a30dd54cad3eea39b0f3ab986650ccd914d5e92dab9c66abcccef4ef7da6bc6be08f53102a57ce938d004bea72822ad547cbd74f1bd44270c264f2b3145bb4926dda7ac3368eedf0238df3306010a390f41cabf27a5d9978b40897c2ba58313f0e5ec606690605bd18475462e03ac8ef02b695c0c15de972c26e8e3cae20433975f8c725eee89e4376ec1312aa7731147cc4ca5a9f0fee788fcd1b1fb408a3863fef273d09df63eef8f91a5c0831beef11cb6378bb12274da545c9a64ec6c8a43f304e821f65db1d46f0debf69ca302a44daf02e1241bd5ee94d2af0682266d14233ef269680a442e9b5e6219554bb90ae80d98a26c5f87538bdda6d5d8292644f3aaedda10114e8d5cdf0566389fd24b042ab714886875bc73af8368107969cd6baca4223cc9cd471dc69e2974983c043f17486829988ce5435252cf3aad808180b1bf17154792a085db2f032f961306c6b648dc6e8de430151e5d2da7882ac27c1d696b1b4a2d3afe431468870f88d48760b9142f06fa85cbb0420de48e2bcf292d9094cf08b90a1c4f0d94c6e0acaf8a76cb7db298b5104cb3ada1e5c00206daeac2996f17b6042eb20a432365db6a40fb57aa96034a0704c85acc929f84a30fa1752a00cb3322f9aadcd17e695a770f163822fc79c41068bc84c3f31aea6d3efb939ec513d9fa871fbb97682b81cf1b453b9b04a7b7520b3ccef76faff77c8041d6c313ccda95c3a01a8b76c447684c38665c1903bd803d32c399ddeb8e144a4f6847c73f0ee9314412cb16e03e14cf63d78e57c862ed687d9a56f0f431addbebce8e5d311fe48e28bc8373d1ba88a3ce2bc29bc1c76bee779a02a82bb43476ef410f3af49c9b9dab96bdeab629b7777a20fee300b6f2b6f39be881c88adf6c86e33187bf0090b33299a458ebf6b617c1bd217fbf5301dd1f04db19d226c502bb72ea297aa86b60f4cd4829a9a33d7d8e46beeba9f650c3b78cbf73a9f6c0aadce8cae864fbae84cb93820dd14ce6b094874022ec31b575fcdbf9ff380e8e519dda70b6cf4019c15932f2d8c8c06c86da35167f3c5989de819740133a7231889eaa18d4fb6727eccc8a5c294fe38ca150a79531c864532dc564163f460e6d945c9994d96cff704494a8aa335c4710eed5a7c82e006bb57101ac24af765a7d991fb827ab7ee5eef05181dff97cde6bfe3f20b7b1f80bf0878727c5bcfa2ddc3c7a4c402fea3bb71c7c54376eb1bf71f248e07f9fbaaa7f20941d0d7be3931d13a0de925bd018dfcdd74875d69e1f92bdd641065be5a0dbc380e9ebf27cb3f0eb3b15602e3b82ccb74ab7d73c3e1555f6f8ad23ebe08d7e146b173ffd6e0346b75433b994f2b6b1066c3fba74db88030360c6c0e15e33efa224ce3c0fbff5868c857fe414da5a1cf575cf4bcea4964a4fa24f05a068bfeeaa8750d7613a643bcc1f3610ba803875fa3a4576b4e5715e7c976de710e7917bc4210654dca2bc98ad05ec2b6761dff0c8ba5ec6db218afd249a60c62644b18f5567799fd0f05aa2e95e37aee81bef1c17e131eeb5d6cbeb625abb4c836112e2990a9655a1ca6885738283305fe0f84ee32098a5b750891c04951c1f1281374cda96806f96dd5aa641ed36621c567325430bec1957af926d68b32b99ea0ea09122dd0aa4a373d37589f274301b42bcaaa6fe59c7b9148189ba0a124ed65bd89f9714e2ae5cb2391113ab6c3eaa3cfafa7a05984df7a8d954b31be97fe6c86c682ccf5e484777b8221e76a782526365e1195b6fe434c6e4601604f3f29bd6005ca5b2083e45d4d9a910f54dd0e56def733b2efe59c676c0a998844dbfce1e2cf06d34e361ca35fd1a2af61a13de89c95bedf717aef2fb12f3b69fe3bbf31acc4fc85aef9fb478649256346021ed9929f339fe4849af9ec01a2d1732127c9e743d7e9f3d261240e0ddeb15c3048f973cb3b2036d43de1613a65f9cf83d4cfb3c5ecec22e3f6cb9245bcc5f258e62cd417fd1ebf4b6b7dd403fee68625c99ce97b4fc97a90f79e79d3b8ecea138b0c842167e5d85276263b1403d49c51ed2ce66d4b0acdfd4f49c87c525280e22d3b0a281bb95e3f81f61798b00608eaed0a0c891f7bfd3e73869e04e6dd117a872edc2c7d2d51a4f20ef05de36478fcff3c8857f763442e82db80514e7f8c1b35e33e36b511ade682702f8eeb0209f9cd6f47b368710fc1f3befe8c04bfd3a7b70a7af30d19091f52838ebc13dd84131ff7341f6da67fff9e461bfb19a3031f0d4c3a789b8942e209306833882893f8d239e5e53e6af7d11665544a3a076e0f60ae616842f4fdd7f204243cd28126e1f7271c144961e7c866e2f46dc0833c9c867ea27fa9433d822e17deff8ccfabefe78ec12e6e0cf71939e8abe47984ad771a95e5b5d939bebd4188042d7d816328773c78f28977dc9ff6226ccbf20a44920c2af09cbb5107892534051a165004fdf5786ceaca8b3099f3d409a18f5489efa23a2508490a4ac6884da222a02afd6438b55bd6d34643d9d2b5cb21b3c3993f09305784c2ce31c134a876a336b2426469cedfc4c743e1ebc9c2d4915f8a0712586005d1ad38769361f2922b7a1e46a46829b04255453cc8c484c87db4c12ed2907f0779044ce11106b3fd79c991280b08735b823a38b43ef3c7966c35889c589749a670d98b95364e5e97010e7c57275407b28b781bd4549f8ff07a4d256792c0a3929e76b17fe739f69eb15e0ef36c8e5208e9f046db97e85d2d226ab8fb651c2540055424f4aa52a53a7a07f18bf10dd3eb1c3022a285bb9a3489a1c85b4c04df62912d199d0010764060f26ddf985fb2406d5c25031239a6e93b75b19988e446b9f4ce0a024b9cb18db707edfbeb851680b181fc60c1278f6e673dce254a82322eaf56031f53f1f67c59b0cde78847f7de4c9429a83ae4e24e90011ba716b9b81dcf04bb2c487223148b4f2701cdc0fd7e50dc2d189d9280d9234a6ec15653237e511246cf10e582fa50dc35f39709e7151a0ea2fca0f7f543b07510d5c6c91ba06ca27c9e3c3721609e8649d096d846ce224a46f7c0ad5d62ab1731e607e6250ed427ba5cf44f7c172768c5aa0f679b7159bd91442658cc1daf3d018dab98bf8c6d120c3533885fae652525e00766bcfbfb4af3a6394f79faa3c996ec3646a33149fb85a4198d2b464f28f6cbca1dd8f57851ae99b7b24b2d4c5f2a1fc031daed6e383edf71e8a707f244bcead225c9f732d12715e96ec84f44f89947552bc5d9b8842f47b51b01d9f454321947d382880ee88f936903a05b373b442d114b25cdbd76283bee246f90d06ed46b094e2c586b3ec8298002dfb8326e211e61a1a599c3bf2b0fe0defff1deaa29be9ddaa3e6e87990a59741bbcc92b3945472a57196578a3dbea6830b8392f2ec642e86efe45b7cd0e4b10b633b62c4c95102a5aaa1d050bc5c5b35bf9bfab69cd57e5e25537c1ef410370320cafe1c1c4773063282df9d6090af018d7c156c4db86a4338258b70f753a2eecdcc636c759dae0ff3cadbfb23986a2bc89786b20b5869a4e7c77acb79aba3fad321b1b07b8959bff7944ca0941a41225d51c99bf3bdbc03f83323f8ed1f101145cb4a1d0dfedd89241e3f2dc44a8b6ba11449f63200e3a0341fe0d6f457da2c3c3fe046fc6e88e6fbbe521e18943d3407d8ae5e3bfe295b84c104eeb5f18fa0046d33643092fabd726653e9ecabb6820141e02bdbfd8a7f2a210c1c23a59f105ff8bf88222808dbb1e8e018c3168e845753fc52289be7b39988a26505e2d2490e0a31112213ff790a2351b0cbe7cb8fb096ddf0f356a0a412b4e91676a9d5b0a2915685669f82a79f58124fbcce3dd50d6305f95115b250b6fba57c58cf9fdb540e209f0103ed2e7a9211bc28e3167b6cc05bb781a7b8518920c34bff3f49d668a0b435ee6f381fe3e1f240d834d29b1e5a59ef71888a327a9e1f3a5c02eaf3781a0de303dfbe8fa12c3e953ad520789b079a59c207d3e4c9dd53b3ea4caa7f7a8dca6a99ceff796bcdd875c01790a0047487f041ac4eaa4ed0a598d8c4cf226659d327cfabfdd787e233e0044637ab027483755d98794f78c44c01b4c65b995806f009ab1d4a4cd862b4d8eaa4d010d1cb676787ac996b40d21ad75a2d50ff6d3ed4fd75902f2934b7a27c88b9d2a3558570646b0ba1109c998b9d0f17d314f635c060f1c0639c8c116dabea8dac1a216dbdd08700f236265506a91007170473e0fb89a93e2f0bc8a211270722eb7c33498a19c09d74e4d1c1275b82bebc34f9535ad53b33a74b80640d6cf966d56dd7190cac669fa365021dbefb5735e2b56b65a1b0126e286ecc53df0a9298770d5339967fc783276222f00844a386e3e1f2b8c9471062cb82602367a685dd5e6e444147dc422d48f1d640513b45316d109f6189079d12e3e6c1fa88798bbbfe1581ff682930b564de32d943a289eb17ff6a812d4c70e88494d6d56c2a342c8c59806f8d2356a1ffa4af14b3ab4808685f3074ff99380dd1c9085a5b566a165e3b71fcd2ffc5a1d6852855682f9c6f8c672b864121c08568b51e8df9a0b49ea81afa7f9edba3c10402987b0c91609e5dd2592b6daa52afdde2c8e8ccfbfb6e37c16bc9e1954127b5715b762f13a198158c475f619bf7c6840dec3d1f83938e16bdfe7421035d42fc57f4126efd2d08c8216068d2cd1942e3f4bac737ee910039b235d4798fb674e95e9f34e430c74a2919d9dfe939b572000b28234240a8ab88029d26c11e6bf84510bca9291a35ce73611c927911c3a0ae3c3996f0da6600ccd9e582c9854dddbca7a2dbe051df341e135e2c5267127d1fd6a3fe1f822575fa8ace147e38fe495f3d388763ed4d8c158ad80e482e302273508d9b7ada52fc160c7fe296ca61e3bbd9f5dde7b7a249ca0e054b0467391f16cb53f3e4c97cc47ea4f62f94ff7bfea0d02e40ad49d359d3d228464ccc79f875196d9f555f4a33f7257eba847a70ef321048483c0e2df27b07c77348cb12ba9025745febc4720c1c61159b77efd63aa4aa935cf81c5ed9baa13542dbc8ef9e0c247beddb86af5fefc5f426e089914523abc6d57821bd317a79cea958895045cc634d95091db6a3a1acd4dbeaef2225370f980f520e29951bbe713a21ffd87c3e38918cdd09c506ff27f34671cc8074902c8844be89007ff6666d8034114390744d2276a8ef26fd238eda115d363437e1efb03cfcee42aebba1e3cce7f3cf7645f4bbed35409460e5a83a557a7648d91f5a01051d409ca5afd23ff4e0a0773bbf5176c98e8edd0307f4a26435432f5752e6c795312cce0b970d87956b40e27993b3c03d596069eccfb051e77c670ab573ed614c1e7fb07163f97d4f4819f80013a10df26631bd5b8ea506ee6b9ce34a2a7af8969cbcd599a8a4100a95bbef1d26e9dc3ba9d6a40774b9d66df4697b3ac4beacda869bf4008b9b1070b21213ed94480b431024f5d0d78b7d43a55c6538ade3275f4fb7513713725f2397208ff9fca0027affc3feb4c8ed075eafd688b20385091c18fc6f4ffcc14617d944ac425b2589239661f70152c4b394814f746522ac99272a25b93b2fa55b02bc9c53e2e4e5582af547a001d53d8263434614a14a7a073f097788d67974d539a5a2c10fd0f876e885e02d719dea5f8cf97e41ad0eea5efdb21d1d0df1168de391fb2393039cc8478a50f177d82d993765c32b247370adc51f96a266cd24d5c7244c0963b480828219430eea197fe0dd11c6ea37cc94482cfeeb2a3bd84c071f569f97fd4d17901480510d2e7147e886fd7610cff636f380d2da48ddee70cacad885a9efa218a055812412243e8a2781d244d836a369d499c96d09903a1102cadb050ebe0cee4b9e5820eac53a2223e4c8da9fe6effc704e07626eb2b9080b171efa9f9b2a6d42bee4d102394db638ffdd6737bcd0307cb6e34fb579cf9a2d010b5de27fc8fc894590e7eb85264aee19e1c8f04fe81ab6e3af2a7f380d7d554de036a891b53f4f187867d9981276ad1d965ffa3794c1d069356c2796087fe670f9bf6dddfe8914fb10758e2f8e3704023b64df5befd3c08453ef6e4d2e267d9a006a0b4fdfa22b121f8cd49686f3c26c758b05d883d809bcb413ec90e909b644ffc08b5dcd60469abcbeaaaa1291788678f5af9d5084e78001aeb0b61220a090f12648329f446fc967417482c8c43fff0b2d4c477a315ceb10a58cbe31cb03101b8738cfa73be8b3fabd1c4bbff3d579c597f849a21593cc9138ece1c8b31448238b04a1644f3c9f5bd56f038a2e0bf3dc7cffb6801edbc10f53e1f6f10adb281b18d12879889f2039ccc74ac0c4ea11aa7b35d189b52bc497025f1f700c0120f45be717b78d7448f0cd19c4074eb7071d77681f830317c91d4a0b7a6c188244180e956a9c224f75504bc605da20c301c8624968418f0ffa9eb10ff2c153cb544b75e5bdf813e2d8819c87460904530b803e72240ca443f003914660694baca4e42a9ab3efd34212102ffd0830b6c0367b20bc8edb5a395636b7a612a5f17c3bad187ca1b6e8ca0bcba0e4626802172c3e085cf0b7a1f7efb901ef82b02120d0a4e4e2707a08b399bb20067132a5c1b5a547c1d247cdeb9f9f6b04bca6a2849b2a20f117e5673682d3be9f5a13c968b9d997ecf813309301c05d843c0063e976fa106381fe58e0f3226c3ebb93aec89bbaedc5642a726dceec16f73228dcee2301bd46b56f40101663a0ae8647d84275b75d77f8c9da97a64e7ddef3c6c566718b29d3f875f9d23db3ff29e460e343abe5883965fb123d1c9d3f9481848d1733d3f80f31b57e3d9c0b7e0ffc059ed9341cb1066e6f6cf990ad9a65dbaa4120efa62efec12ed5d66f3070f9dcb0ee1f7f0d9267bd81fd17e33594fbddbb92b624bc1f690681324de60a68ebdfc0dee1eadb5e7896156550adbc70e2c1156ee3762cbab739201de37914d6f657cf1c77088ebc760ae63c43334451e89b36ee19fb435b2796a31e7e165fb0227c31b032671b5f46f36f2232dfcc68d5b168fb3ba8b49451e146a7c70b3b0db70f6111bdd5bf5864cde54e619a9f8404d3f3feadb9ca3b55a585f5866beaf0de2c3789e33b670fa3371e32e261739ffc97517b8a7971e6e665ab703e9e5d73ea371aea9159fe4a35e9d56165f7cff09f3d62b6c747c95a76ee674c7f60f9af554a588fb411dfa66f0de1463abd20f16ef5801649c6c7610dda9809e822e7368bd58e7e84792782e075a7380c08a8a9707e7a20527c5a850707dedf0e6c3d5c591b7eb08595d1760f5c30f48e87ce2e5d69d7e6b6581e6f0f61b5d8b263819cf0450175d6e2b6a31d603284b5af06009a448026a9615a6bc82a0bd090bd67428aae8d7099d567a197ebc19a749d1ef026ecadf4f58a07012e374872f161677455518c18eea032dcb232f5a3afe9afff08177b5cb831f646a0fee56e117780a6783cbee1402e088760d58e7a9f8d8a07fc02902d052980d2f8faf00058d761d97efefe7251b189220cc913b3646dcf2cc42075092b2b97cd6a531ac1fa3f503b070e5e56adf5204b7fd68f0f072dc562471b0e02254259792fd88a9d86c1b3e3624aab631aaab0d28f8d04a1dbd95966937638dca04ccc8f30a4d10a6d0dff57f7522bdbf0433faf74b2debe6dfec2bae0718b0613f4b8cb15d5d730b484c18a55ed2206040207f7b538af4c8e0a41d897d93b0fbca83689014b7d9cfd2cb430b32171b30e9488d8512b882610d6b7a667210d6d0abd56304b114e45858227a1180e936a853f9ff127364b10ad2543eab60d7c5a80f258919c6691799b1e041ed3b45dffaa0cba5c2b9321b8d85ae7a05f0f99ae2bd2439a618495431302e3bd26e489142e7f3c429b725fc644a5bab7f9b4e0c97c7676431863c9610db991bc598235e18457f8d440dc12af331127d35777f539a0ec0ebe4fab50b94ef62ed35162093a607f5358f49f9a0bb36f8779ace6f1daf1f42c2d81bb17b0ce493f1eeb37c76dfc546010f877c9028d6d8078d01ff2e1d5ccd67e3644fb0f24fdab63efb899a692783303375e9737cee985cb20f8d6cdc435aaa2fcf0c528630d01647e7e1fb107f14f9bfd4b0d4013461e778dd72ad87dc5f6da15ac4877febb5f8a7173794a7218669b38366b5221f1667f0bae76521eaee06c9062a104ac0756d32958f16aef91e282ba01ea3fce2b1746ff198e9ff9b8efa43ec6d9867c863ab3f3310e0d2f1e5c3322bb649fcd0e593fdf227bb7baede71bce661089e5803ee3cf5406f0d4363b973a379c627bf3820aa950a06fabcd5de09e262772df9d809b588f4fc235c7b25d2eb66c6201e9907ab37fc8a54ae8a749c91de59a19e094893a984b9ed9ad043ecc66fe4e5388f4732a1f463189f9fb9d910abc781191f6479e0e1c828fad94e6f88decaa87ac25fbf54050532cf61e066b23ae8b6ea6ce664847200c8a49f563724641116a5662d1e6c179ae632d7730d40b904a963b96ed54875ce92329c3e36263462ff28959998d0785bcf526b4a454926ce637ba7c87179915b88c25ed6f4e95c068eb9f7c47c0347cec7762ac0ef097870d66a50661c15e35f0b7ca4aee092caef744bde889f6e0e0b8f4a3c94a5a21011aff573cd631b9747bc629c7a28f4ae71491f3345d6d02df98fda1061aa8d76cb1248bb81b5d07e74f3eea2f9ac761f6226f6cde18a02e6012cae0e0bdc6b6a9ae2d8a9411dc78d570f50c5797131556144262ca03fae4e1b03e1b0823a6375accf571aead0df86c72d858b9a932ec27b3cab269bd841af7d6b62f571f6dfa298634fa0b8d0193d63d74812aa1716da2e28c49c8919bc6f031b90027a04768f4d1c6f3daec28988120a7ea4f4807ddcd55346fb4feeef4e627f5eae173157c96dd8e779aba277dd2ac4d13269f8cdc2e7ac48472933d116f43f79c3b0aecbae44aa67bc6bd41de00dce85836ada79846adacbdee2872fdd238c3b7bde94780af09eb3dd64ea74e193c8414c5090ace00b25a3cfd2946f8d49d164ce3aae6ecba68d10d2c27749cebe587cd2ec29deba9f00bbe9e51b795f9b522d6bfba3f00d109e82d2c5fb9be2c3c6a36e2b688ede8d73e1cc803032f89ca3daa068953c30c622bef315fc4ba116b59dcff4693869f23200f5939c8b5327585eefc5b2e55418eee0dd9c62d08c58f1238e6af7803c7396c7ec10e522da288055a9b54f884a66103b93b0aa0fc33f14622ed1d2949ee071c595e5b98ab2eb4056213bbeac9820dee4e339d87a5052eb2318a55b0eaabd1f6dc37055fbeb4153ba22f25414c184fc3e87b1cf54a5eaad6cd0fb4040361a6e912f8ca3a09a90920c47fa359189b45efad82f36619e708bdba1c976fc6870626fccfd4783faffe10181de8447f168a2074fcb343b47b3347245cdc8cd1394a788911ae5ae9375740c50fcb53a5763a0c776593163784e8659b06f6fba991da2bbc4e822ee05caee027909feebe0f8e38a8da3caa807b8dd4cff9d0374bd60658733866207c1d547576270802e950c2ee85569c109ba553ab842a7940cced09b12833b74a8747081ee9b28a580dbc76f864a19bec7631e5bdb1185d7d1ae8ae319e0028b6a714a7975c780d7377ff8ddfd1ec8c0800b9dc563ea23b60c614828b01ac44284ebb20b29686c930252e743caa7bc67a0616bef84ff602afdaf2f4db861d0b48a50f8e5d3e70726ce8f2578306835e11fb58a3c026f809033203e0f82eb1e7d1dbbe3dceb2650762ea9bd7c0e5c5e723ed563fb6af0983fb811a22ce10596e9a6fc8a9db00cb0423c0ad5287ec3bb825b22f0f42de37391971a18abbf36ceae4aed90232311b01785d797cf5c3e552f7674320650d1ea3cd95d03a8f4073d53adbb3f509536d5cdf8b2d6e4aa76d1cc01e9da36e0439a1d08ba491a95d164a149bb0164ed7d2b62dfb0baf02a68d9ac66e7a1b6412f22666542d8133837f84712fc76344d0d7a4025b93315c1f858356ba8dd69f24f920c8a3c82f07df0d2a6accdf29369b62934516365865e3931f85d4651bec6c64f1d6c13cdeb3b1af541d38997a78c8d5b41ce59ce9d8e98b3b3bac7a3f690cf2b0aa5fe43c4a6f8efc4566a80c452ad6e96be5d71cb074163ec71eb7021a050b8191f341ef3cdf6438c0820a6cf1592807d975c64b42545b171b6e4cedf4b00f3ceaca55bcab965b60fd33b5f1003c9ee807143f3857edba16df311f707a4fb4fd0cc73fa244e4f6091ccfe9a93b792d1d611d203d6edfcf4287afae8d16f654975136a42c3d83fa9a520cf8e73a55d83f38e7432f6d6117885c8ed13d13b3c93ccb8bd69c05ad3337c989d59d9c3f73c15bd8f032d7e40ecff01e364084affc3719e1015fc798f4e5cf24b3b180476529399bf93b524cbbfabaaf6b5826b1dd8d1a32de079b64c49487d1805569d2d99f5544a5ecc67f8f4782b972c94f1a3327cd1a7afc5699e22ec83752ddf598303333cb70db629c006212f0272299a74c11384391ab979aed7c2ccc9603684b92743b0a3c40b3f6ba4aafe17e7feb4d2fae66ded3d7a8dc9715b0c5914d82cbc9fee59b9216759969c0db466f188f01c6cd43e334775d2cea49d38ba31032e3072ed324aa97878c183ed301ae9dc02664d03006381a1ca346ca1e3008918ba8492bda389708f83f3a2c06a03fe9051ac2faff447c34858d7df16e54300f09f417428bcfc309f6b2746597b1ea94746dc664942135fa3d1fa2d74494b53e1c3fb3aeb8a5c6bbbe7d046b6874d7da384a8341b9a79e65266fcef41c44fb2774ae8d632e5040c698ac50378e556533f74e3a7868602a39ed460a11fbcec75c62fd4934efa0445255be5da49301c8adc0d4b9972f5cf81ae632ee5c883c402aead6879326f2cfcc4e457647ca1875f67d8270878366960208a858e9b93ca878b9407885d4e9407608f78cd139b35f7f513a28ccc465cffe96c281daa47e49d7f5c9eef08687e25c12b338a290fc03b3f84b11442caf2fe483fba68f5b0bf48ccd3624283a5dfb5344e7e8852a8d578f603039a4a23b982fc12019b4df76099d03c95ee1ecf378ce7c80abd28cdbcfc94494e7d1eb2786c8ba4283ebff7b67b8c5876a18f947088e819bafe552d4066d7743e6cadce2eefe964442fc36c423f75ce3f06c43683b8fe0dba3cb8cf33606f9a08eb4d1b9e318542cd966999f469286a473b16e037c75dcb5f3fdb95a851db368d3d208962c4b7053ae25dafa7ae42a3b36e09228cfe84f3af1b4f06717ba34642fd1a42092c6cf877426659383843e986e5563139a099358d1de3f4609d4051e526fd0200af013a6dd32d391141295722804b28e3320d3d0383650c7fcb2467a7b05d8faa548e87b1734d247820f12a6d151e5a6e35a04084511f5a8a370dd806116e633c40176a519e75b030fd15a1654d5d8fdeb7972499c2d9a63b7b7c096d13b79539bdbe099c5ea80d9d4ccc6bc3323f62a0e862eb5c83adac0030a51114c16fef39f5f4179a2fcdbeb706e034e0d7360e61d63912d5a5edb3fec5f0f6044ba850d00a7e6a94bef265c3f1db0e25bec81d70adfa11a5402ea8f4996e4f3a0892c74bff4b7fcc47340be75c9e30bda20cf9cbfbbb7894b09921e06d06ee694f0574700d66acd2be58169daf04925374c664da7ad0fc7886ebd6600059fc31066e58f5f932f33d2453754c09ad56347fb8ae533d75212148e7195cdf6cdc91dc9756638cd99dc10aacdbd66fcd50a94ced6b32dc4725bc54c75432b5f9ceacfeb3227c14bea4a09a1736cc2f388adcb10ba84d071cfd76dabd367ab804d72fabc4a709e60e62af6fdaaa211843ca8b3cbcde59d59324fa2360a6de5816f3f3f117f789c7ed8830b0ec868d8e4b585dcb6be7ba2a42a6dea9ef46f1681b5f7aa7e467b9691d3161b01af98e5157fa4a989300a31211b8a940c5eed4b84d73e24266f13ed3e94b328f7421176086900c732537028dad17a49880b3d67cace1d09d2e1af58fa184911e16e0fdf821e92aa0bac0810fbdcf4d9f894dfb2789056366e074ebb87e5601f4635f0b8d4f3c04e640640d14eb3480a57fc2b8ca31d282ea19c36e589b3066b1c750ba0ec97f92ccf9f43cf2cea974b1b91e49c3bc72f92bffee82a632367337cba121db8c01a6feb8d32506fe616c281966f396faf9e036d46ccb752b7472e013f0b690cd6931d834824ec9c7f386342fb2e5036c45a73029156c4230941c10ae9a4600f4d26ecb310173fec1f8bc8ad21696cbce6ff4590e4bbb8a3b1d6ee0e02e1bc0c4488356b9e66dd20b5df687ba29cdcc5a7e24acfda7eecc236f6986138d069f0b65e9ca2e9d1b61dadac7d63f3d005cda2c6ea256d7fc0c2c59f8371cb6b94b4650b8011ee8c54d635fb7cc1da9564a0058c104cd0830a3ea54781e34251319ee6015835779b57da6962536576caeacd1075277f3fe609edd2bbd1d5d43d5b62fdd27fb8e39a91b0a0b172644eff13b6108cd3d02b0c845cdaf016296c3f3e63a63ff2446fb47c5ef400cb5697f2cce0d64c6c81e6dd8b688ad013f5f0c70bae312970463d303492166b5a772784db30965178f27ce1ecdd5118bf3a9f2544132458ad121af264eb1f34366768ff4ff613be9e65f3f96efefffb209fc82c52c81d2807c09177c1eadfa5f7d86f1ad371ce23e9781259586645f7353cd36e5c59b98a2e8c487d0178ea2fcc63afc5c08941d84f2848de98a1daa4510a1e160d9fdbaa577fb1f27c4071bd3083f4f3da4b649e8b3c77bc78e5752cf7f18e838f77bedd474a71cdcab23c856131dd4bc38bd44730f8d8b7b318de3a0043bcb9d95be5fd3fa04105aa28025511795edc27545055e5ac81dd2189ec9736f6c3f5235d894113be55a754e312fa31c28410ee44f92da13a452aa4bd27a9c4fbbcba9224b1f8a733701a4946732f0dd18771160562cbbe9e748b8021ad7755c030708dc5268dce751b5ed1044738c6806d190ae37f41f8c7f7e495c0adb476aca5d39410fa060821732172330b064c119c2a011a3283f58791592fc3e1ffa2221bb8977c240476c43a6e1f58d51b11be0d0a330d8a3def97dc78886ea62d582dbe13b2d6d660dbfb62973031081de82f4a11cb23353c0588356265a5d43931fc277c4272243ea9665417c6c8cfdd806a3696e08b6519975e0c5668b113264fc87958abf56113f61a767dd70dd8741077fc7c0bf5d0b7a6152b0fdea651ed91c0a3639fe7aa8c03948e2b70db40e1813deef26b138bc5a422646b2b05b6fa2c1d1ec170e0bd7dd02c1e5d6789d822e9d578af7e63166c789c5b9c9fc69d42f04e955bf13d69927460693a806fc2cdd9a47da9e7025fbc477f3f9c027888cb1ef2ff8795a9a1c65db077ee255856500b6e872634193c5760c78087cd2c5625ed15ed997ec9cb577a9fe539f1b58747b6d88dd678c27c0739ba9e009092a16853d189cd5261133eaed0a87ccb20406119a01c9a0fdf9ae148ec935b4de9b89f11c8fcb3f867c99be7519e5d9c1697fa9135d3fa4b083afdda0b52587808795317b2971b9f987b6f4ff30e8df9de189ec91fea6142510f3af772251be39878fa70abaa9dee9823a4d8b5153fd2fa03c38e477acc57461fd95dda27e68fc0ad1e45e8c18b831e23c248eff823ed31b8558dfe47ac70c69ef46eeceee866c36d0fe592879c0da29b064a4cb636f2fe18d41c3dafd27cbd133f682d991e1f0fb942f2aaa14240c2f534488dd7a5fbd7f1e5e77287335708c07048dc98307ee1983798f684067569ef189e17416c11f1a39b3285451b564de7e4def162be025353a57193ae85c5f8bbf83fc258fc65da55480fb4be2818bb1b77855b9a7d9b79eba8f067abaa29b718e4ea01382601b6c9dc6867601109569b2fc02a400cd6ec2fa871afec0f0cb7c13084b3f6bc03adf85b4591d34978f51a1149e3f06869ff51afcfd46f825ed16fe3894bc2d6e562e866ac713a179c4dcde18c51c32c150f0488b3695b8132aa50f9f935c9229c7b77073899bb8d2a0d781acd70cc1a7aa3c283ebe356ca625ca8966df5fbc5b2bbcbc51abb2ecc3914ea2643df5f5acd4118496131d9582b6022ca5c4f942ad78f8f2b56073899d98d2a1ef80acdf0cc1a7ae5c283abe750d45b4e92900fa8fb558d3893f81ca85ee41addabc25e2cc39a6459a425c269eb84629dfcc49fc40b52586ba5f094abe6dddff13f24d699e41342e1902c2a6492c0c3ad30c4cbd14abb3f002c6168d670a4357ee05f11c1e75d30f0830e0d08faa92d209c7b4a378224028f6cf29db6ff88aba33728309be3cfbd000a7289c4bbd9f9d6258978f2bb2ecbff1e9235879c15f17387d1842caf1765d252e33c6ce6c2a3e73999159f312627e192e4ca86f70233f5815d889bfaa3f322a8ee2824def4b495c53e13016ef0db4cdc23451d3215c8601de49049043e1fe5164ec8b9dfd5fc61d4d15813440c3dda3961c9588a609d84693f49cc9b5a8c01d201cf96b5e806cde32d1b244caafa498a3842764113d8c4f7196608d02802841e9427929376118724c2d275183926c7a2f2c5f857fd063be738f6c33a3be0446ee4fa73955b7a7fa86478da92154ecf921385e91e9de31ccf3959e5543217a82b24bec6c54f2a15080236d43fae3d63200d2fcb32b65cd70ee95876858b12d5cef697407384ab942c089cc4bffa92d781d1c17ad26720d3c727917e1ea89bff03948e882795e7f9ec5320745958471fe677cceca10c222fe22b96a06a82160260ad29124cd2047c72036a5a42eb80b549c9fe859f4bd6162d3db02b001b5c013cd5033dc82c47e3fe8faff99680d20bb03c69494441b6cbc2bfb700dd57d1b880fe2e93783048926e5379dfc0a7338e9cba79c995aef0b277996f656688769147b14f0ac6b71310ad42786b54f2b561781b0478f8e29baa5c4103a8c4b8910faa84ac43aa233aa14f18c7e5869ca77742a720380a1175129bd1f3f169b835b62f42c2aa5f7f143b17929b792c1336596f9587ac53a63a4f1b726bbe86490a13bf0771efe4e63f81b4cf0220ce9884c4bc9ead9a591f0761c87d2d3107914b1a3aa0f9bd3e9aec39f04385ae66590f6657bdf3b36c9c7d7c41820cdf13a05b4578834748e6806fb234f5b75bb4f51ba55d812600313d03594c1b35d5dde31ed6bd5d7f166929ea5a1c3204f6ac64c80a477cacb6f64a1d64ff2300408b73869689ac39f8d0853804f87e99c7ba3a2d7e1e9f0a80fdafe63baf808cffba266e750537b218e380d8bda0fd84bbdc9f84febda459b2c9d197ac6f1b4af2b66156d5468bd9f9c0c93711a61638da9078c12c57a625f0f834d2eae3e6ff1229f2ce1eef5c2459aef7830b6c2ba62db36f7cf8e25d56ddb8c575a9acc740bbecc2f96cccd530a793b191d04670de28f3a81052da25b7e5882fcd037337664eae6e07e0eecbb82774d60bfd5f4c0bd1c772cc82a2e01c24a8e2c65dadb8af3f84aeaaea0270589de088e405f4956bccf8a10317febf3570e4e9fd41b9a54f967aa030445a1b15458b7cd681a6ad7467f8d041c1a2573806a62f9a7f702f709fac236c5f281912850a7b78e40c866a2997029f0d42ef60444386c92a7a192d45c7600047eef4764a865d60e89cd6801723b0cde5ddadeb6db038d74647fa9312bc4065a47909592cb1e092eb8e3a08f3c63d4620a62489d908c0e362f10e3e8df844522089b51546bf32e6543074bb5f247e21ba631b15213acc13c086db654150bc0f90b89f3e9ed844cc74ba9faed215fbce5d714c411dc9fe12cb4c886de894b03d76b9dc3928c0c2cdc7f0f34a1c1cd12183be98395480244346169a0c32113d0421a39f7236d69733533ac5ede44f5f4530a0738680970c4d248f9de362ccd203c70290dc7f3cb37fa19062f7372de01ce955d575c85e5183d4935e483af2617574aac16baff1a247db14fd53c11241d6d745e10862b2db6a13b6fa6a57bea37ab39fb16f436467fb05044cb1aaaa5af75fe19c27857ca8c6387221bce1ce07c4f399fccfc925a18d851686feff548638906c351ae82b4f2355f2eb125b73fd162fecc0d0dca0daceb8a01e39c2237f4638043fcc63e02ea79281258fd6df3b38949afd011976a40a9430de8253b903ce4f8b3c1b456d749bc15ca1d0be60c480af91a1d039e2127b0fb10817241516cc8df6e5cf6e1a103ae4953ce38edd100bc475471ec85329b42e90fa0203e13deccfe1d0446cfaef0b2993f1cc2cd843adc879602ae4d5a4ccf8665abf02358df1d0fda6fbf5993008c44b577b5e94d8d34a60500a1b4213c7bd0ec486e86491df0a0deb5419c967b89626d31c4c926ba92f6b89fd1fcdd1367e25cf3d75e00098bd1c9fede14c4384377c5ea685f00343aeff115c4fbd6422e7df131d032bc03cb8f498016e6c13aa81cda7a88d4d714ca2bbd4988d4bf63bae4b2357cfaa7cba8aeb714916d8c7caa87826d05d24650df491cd48f30a546e4f886db2b9e26175435c92d269ae6d271ace372cb701d747f27faff394bacf9cd987267ad7dba2d557e72d4552914d750410cc775fc8f376f7d7039818f5ddced90d87b3ac61973b42d1b4f79baa6041c7b034227fcc6a18c7c1cca22b6ef286344d15bf40e522fa7d89d59a88891db1f24cede3f3621dde8ef30c3577f72c1492458353a0832e3bb60e968d11dc8436f6ba42f230504dd65aab41c2d43ca804f2b9967e57403b602f443805f4132f8cd9c1bc1fbc7b081230694c1b92cfbcf47d458d2d04d8c2bfd447a506522d02e26fdf7a8fff36ce33b574a7c60f818a03f36fa4caa082c812b6dbfe769310ad4a935bcd3da6e16cbcb0062d3020ec0f3332a77b5f26bc8d299c54d41b829f12e0c2c662e159dd23d79063046b8b7851451ecdaa16a5bd1ce96f068581066d0840ecc687c1b09745add195f5e7d9635a920051a521431825591fbab733182bbc192013617d75cd08ce06065146fe6e7708fa0697d1323b92b7a7ae452e56de8ffccb464d1931b258c4bb55c45736097c0620e5cb89f293d6627224cc75f57c3e23a4b0a43982ff0f274efab6936409fde7d9694dc12c6f3a129c042ac90506eb9b7f377101e8ddf1689c7231a39bd2098af991c9e924b8be14c8b2d1276adcabaa058382d775f814e480a43fdd847465e5a03ffe0663a143689585917baff47fe3cb8e691ba67d7d357e6ad91bfaf7a4d27426a7287f3d2433106c44712f7bc7e59cc7bf9bc9c4073e0b72708e55a512503485493e8bd2d8bc459564fd5adde647515d752d961441c2d804b451ecd96cf87a5703bb55f05be42539f2c778c08ca2090ea1a05f72c78310b4df46d07c823d510981dc2955889a21bbe3c0000e4ff76f08da6d84cce12403be75c3430868b042de31de66ae65e3a22e56a54060194918bbcbfb9a6b7f637c2b982f1221ab1ba2439acf6bbec9d5872cc5d80bdb412fb71333abac2acfa086d9792b28fa0d4e4570135160aff11f0b24681ecd710a5c24b20e76e1c55256c6b89ee5f298c43af027c890533b132855bc6317e647b30a3a8f803b74a21e890f1c4cc332443aadb0f2507f0593c3e243c7feeeb55e802aedbcdadda8d4da8105e7f848d042ea6182a162373adb886974218e0b54681aa2d4b1f148d79933dc31e68e43c26ecda1eaf01ab9626db462e84c847446615b437c7ac5b05c8a82c6949244d2a965550bc16ed726460a4348c6c92e212696ba86e0c2ee95543bc521f0ce2c4f931540ce74d4e5ce7593c504f6db9cd84cb98adb1a21a0ced2c6b029f13bc1826278cf321f7500bcdc5baca189654c0646e29cca24c91c8e60af9333de031518e7426276ed6e3c72baefa2523a66d232641f8692086425131449974607f085b91b98d7a710e355aa832cbe1a186e82182108d5983d7a9c301c844e6f9b658a6602cfc115418471ee37543ee8aa498897748c5c32e02b07f3da6dbe274863d12871a77d664910148dfb3b57f8338884822def359030e671f70e159d1e91349871a69d1cf408f399fdcce2afa277c8e96b907e0e79f80aeb7dd12bab8d8d168dacd21d4600feacee0ce48522682d8eea18b59e8f2e75af4ec7736831a5b30564cf5564fafd8d832e335d68167328e47d98be851c2798c7c43fb773b2785cee4ec1208b95d9ec6508eeb5db4ff79e8ef34009e43a707ccd2bded277154d02339fff87d0df17704feb8b54fad160504e3d1e7e7a406283ff2785f109bf4cf244a397c883813f37968915d6f4a42fac0925149eb3260893bc59b8f6029d3729a681a2c992045abbce6dbd4c1e8ed47656bba849dd4f104c30134277175b470a2d8da1c06a3006b464bb266183b14a8ba73adab39bed0e9881d6a72302209ed1320944dc241e429a98683e50c437485ad27515d2a9c2063417b9fb1b18a9a4838f1cb1becb112e2f79eef63eb53782908549c0b3f809af000d045040439e09ca60a8fbc83c30488f90bfeddd47369a299c6c3c034d3eafb64c36f2ae29120a976308c8069bca308bbb95164d513c0fd1230569e252a162bc0988c53cf84b536c9e7fb358dcfd1ec15b51aadee80e7c2aaf9f3584ecc873bf76921c43f82ee88aad635d3b9ff5269b45b32a714e31ab8354e917eb2687c3cabb90796a6fc950a19219749a0aeb739067d764badcaa43c6d69999cddc3686de35e4d445adc3a4c63c7b0cca56c01426c86a4f4d6649a580652f26fc02465e0a2f48a53a2bd25a4ad58b5eb4d0b8ae49cc9c20981b3be81763f09e9a56e325b7eda3f74f2c03d7914a06747310313855c90ed5dd243bcb2d75a34c496a04035ea96e0365acb8408aac5b6bf219b274c8f1a8802240c46e89d4eb3ddcfc502444bf4412701524fc2b854d97db9192360f0126c5233c194d0861bccccb33aa999d7179e3113b2b2f3a0a1f2125d1f533234e0c1da6e23705e7d433c408e43b482412f83e21bca79bb48dcd87e88b00ab89ed949e9a8cc2764f3f117d1ae42ccedf3593c3574a80ebe9b20a6c52da37290a3c46d1ba6ac7a1a568b126527ff33ff1cc4d7001a0ffdadb7a1351f401bf2f703f172002831cb82d00986a0fab290da8b60ea7efcfe2c0c474ea0e7d918c671350870ac77e063026bd27d8e23a6feb3d85df653e366d9ef9bb9a232eea7a134d7c1ec8add1b42bd5ea3fa4c0eb013cb384cd292ecdc7e1f80af0548273691cba8142f6e07b07ca1a8e8a15c75a36f292c65452cb0abaf11c914da57eefe65b8710289ae406946944b5955381d466fb18a5df2c85862e3d7d232c0c726caf10be6fc999358a441a585a53f4cbbe891910018f2af08b3c2497720aaf4e77ff2c90276e48f00a6a068da66385d835b3a2dd3e17cb61f67d477ceee308d2c64b8bdc1926f24d56186d4e54ca374fdb2ae83c8d617f264700535ff7b9c7c87bd8682392c553382af56914c3b109cf87e6f402a7e18c6535d798300f0eeb4381d99c3cd39b42e70216f538a44cea3c54c074e0a4c16758ac7c29048b5877ca94a51b16a201b566a3598a23e471c02d41090eab5c5dfe136e7965a79cc94cb541e0991c5cd6ec5dc5348277acf0b550f22a15e5bbe89bd5a23ac822a59ecc6b58fc875664ecc6799e7f30a2bf7dca98496d06a6949465dfd61593ae69902d749f2f2eaf61cf7ec940d4ec9f5f7d109ec11e198e66e5af669b1a8545f9b713ae3b192bbe7d265aa23c6201e000ad6f611a55c85549e70a655a092ec4cc096ec25752305439af53c46a500f59c3af79fe6085ac0425d9c50468e593013f4f66c321cf2b44d0647c38faca0372bf2f9623bc23db74e0fd5389be476c3aca2273a241930b85a13d9193336e306d0ce107d5ca9efe577acda71cf579a82bea38cdec4245cb2d1eec227092285bf5294f0ac5d8f9da2ca50c2374e98e28ba8129879520b074375dbef9076bff63f808e406cd1ae3aca0c35e5423f540e7e1e273dd37a8a97fd13b140a75bc0d944771e3d59210920b1ff2c0e6e455944e37f169388c60ce2c8c504fd8a96e5878bda594ff79f953bf0382cf833aeb584695300b1467e58a9230cfee72ec05529f4e5a9e7c1ed3c98498f6c5851dbc2934c1378b6e4cdb311f47d55e0a8bdf2f1b0d34aa911302d6217e25932eaa37f47fbcabbd8845642ba622e7ff40a78c8da56562a26ebd859989fb8b9754cf62d7413b76743fe9e1091a94584e9eb13abd788e024853621a2e6bc3621b14d7c3fa85d7f9262d9ebf4b5b760b140a75e8375485855b6aa44ce28f21b19528851329773f6729264e0baad321c1f8a4aa31a5a0ee19786323a3010bc6b671c8b62a826950c9bb1bbaf20c95d6ac1be01c54feafb0cb75a0e24e6a5f8deb2f4a7ec92e8e9a0d5ce65019fa46c5223405ec4923c4824f7105daa145229fa500a1adec9fdf3a49e684b413968d052e290ec8c7ada892cc57510cef535aa9c1ccf08cb8f11ee749ed3c502a17371cb9a08f73eba2d13f47cd494c807984a362d7a1f79b73985cf8cea3a5b8219bdb30f709279aea1ddbbaa6c1d01c691f9b2936a81bdbe135da2bd49187a38d40424f182f28fc3584ad705b4c3ebf0326ab2b2b36c39c4cb65067a59b6bd41ba541261d02e044fae35bef8fa55e09eac6ee4751472a3bcf82c36349d9fbb49b95805fa3f440f25e530f4e3dccd2e5694de53fe40255143ca537e38dd88c84c6c02d53c4801c7108f6eddd06a03ab1d42920fc8030c76a892e58023727c74bcb157d42cb95acd8a2d5687798abc149a88822fea899d4fe16ce48857433442a49efa5934eb304565490b0f743fb892ca7582db987fb2d7617bfd8683be6ffd69561ebb46444073c3110e320395025c9fc5fd096852883c02ae466067c53bac458976758b91cb69fc4095dd2f5d419df9387e0880fad33d7685ebfcd659244f438900432498d34ec4cd209d2551582422f9ba7f353fb7a542742b127a084ebc0a1cbf93f9f9d60cd8c474fe82948b6dbb5ac20b80964b8b8aa81ce72223849e796aa5904f71a1e8002a326c441921608f318970570373992edfb55064c36e979c1dc9e8704403f7e8628a8eb28a80efcd43c315ccaefcc42708d33f81076673da7f75a7e958daf68687fb823ecd34603af688daebffc8950ec55acb42e71e2cd71f9700444e5ef4f8fdcd4bd1fcd5cec2f8880ad7b28e380fba61ba052654dac8f24c00e854dc5984e1b5a849d7dac8fbb16e76afa8a1a315b9432feaac4583b4ca3a1adc03bd8a936f6254f3be40b96a8a67615da88880c1dcc6fa224a2f79f05bc84d1ecb323925f534cc342e54c18c83204f37a732c71aa1b300e82fc10f82d9e72dfce5ade38f0f305bef25dd74c6e1c5209a1ade1f5bac06cba047437abb087237691f956a6e3ba78cc7801e79c980ec06623a939a74f5a56ea6fd9b2b9729a8bfae9f9f588418b49cf288063e78a2f0a9efa120989f86380d16c0783954b677e4c975e26a79859de1d5906612e4d1db42afb1fa0cdb7bd256fc014ea6caf9b4eccd1c226c517ef9db55905a5901df8aca47316396ef26f2c9badd4d00ba545c54ef54e12833798681ee1c2a89b097d13c0323cf42d935b46787e7f04d0b54531dd899f7c243cb0debc97cdfdcf5042de9cf051a59dc4ff7fbc65f68e1af182093a17db41ff75356c80fd1f27c550d021259ed8bfb761fdf1ff7ad0d305314788678dcff3c18eed8a04e857b209032f160180c2d4f2c70bae09e94878c12a96834f06ce20d2dbdc6d227f5a8874a42a6b32715cfc8442ab6d71a243127033042a67bc162218f502f5ab21093f6d9930cd4e707f21b566647a10ac0828efe627400dd942dba0e3eb32645ddc13ec58380885a95c604c49e4bf3a7e0783c840a953e7f62c342d0a93e8566cf1fa0fc36a2ca9ad23a4db6faac5fe9df6b87c0e5c3c9fa407f4eb5f997bee159faeee40aacde00e857f29a09ef8ad49fb6932d0ab00e3a24b9e17a8b85b40bcf3782c420bb4c19fbf21112b47d614dcdcb1ef7cc5dcd04041daf93fc8235a39e71eae80514bd98ce86a77a9f05615ba80c9bc7ca3c91410f15532070a0f9f30a80bec8b494b5d5306c52d3f7f1e2d6bebcbdfbbfeb5fb48b041b4e0370183ccb749b65092e84cbdc38e16ba46102642d39614302693d0033ea4acf83f5a4ac09ce01eb87fb316bad172529dcdd6ab7e8ae6be2a29a4db292c22b5ffda2dc8bea4005af1693913a6533f734dd5e3dd1c83f5c0366c77284ee4592ad8140694970faab341116239023fc7c6b0524ef83563d893d8bdf926538dc742386d3d64279522c4c904a247204edbb297c961a9d2a79b5932cdc69617bbdba5a341897bb4d6818145834f4327441ff75d2ed6a85a7d1a269cfc3e2e3099fb24c4a0f7ffc7827a63352c435eee9daa2387d9c45389874968ab84429daea1ec982d2bdabe91e3bbdad42b6160ad51aa54c1c8a1d604b63771b36b42679c6d89440254aef07054da6e165a25a2c065c476f5d4e9fd1452ddc45cae3663812dc55e0843c461d086b40e8caa3155dabcadf5da9ad02d4d8935cd8a6df5f858d1c96f3018efdd6aa00a4b9fbe0b644bac9e72d888087ba78f5518d709fd36522ec1f6922480143e3fcd68c2d3b69e462eeaed11044fc58a8ee69f9bdadcf273fe8a61f5da19c0b76620f4e4d54cbd11a5b485d6f332a4fae0b49ea732b7b3cfb86e545ac0b7d5c348ad8fe3d218f5e9d3b4601a5aab55872e11d61b926f92fb93e3a74d1c2e734465263437e0abab23177e6c47c3243a22190233ef538e19589114135409d2aab662315d24818367a05c9ec10aa98c615fe3145fd0a58733a9e4d52c8e47214529e2d40af7605ac47175b6ead48cb4b57e5c2d6c9e60bb7659c0c61984f656f3250b07600c666e1aebcc6fcbea44eb69efb27949901263bb5e35e2b8f62925427084dd919e2e19a04b052d9c9945804e08163ea975867779a3232fd632bfd9fd388dd145a2d026a02739c0d7a3386e233accca2c55ac0b2e0d70a8fb2df273b6e9521c8cc40961bfd51ee16ab9688f5ca7d09dd5fc9331e1b1e5b4cf419d3fd99eb0f9a848d78e1585c5f7e8ccfb10442fe1899475b6ec36574d00ce63c1d56c4db545d55d717be8392853245a3667096555a515e08ff7ed4482c7bed76f57b7c9b0cb1a377d87d75d9deabcd2f163ee02e0f3f2ccefa1b1b18ded8feba61ee95d8207a470dd0602d73c875aba9e25d3d4810a01a458cf4231749f710fbf4cd31f61d118431a7c2df347b2b852fce13fae3777cd326334188905da6ecd93d3786af515e2ce1c7aceb95601d0fc598472fc23cb1bd1e8a28d2c52708ed680cd6c4570786780a82ad7053f70dcf9ce27aaf636a43a2b50b827cb592d4ca4d1a2f52f795cbb14fd55d628c7364b909a5d2bf762fc159b47e99a9ba5ecd7a8a60439918f4007c15fffc2f4276604ef96ce7e9ba331a61de275e672cd15bb8c886443afec7622cd88fa1762f1d16c9201d926391ad7d66073f058074b3cccf756afe83a085b85e624c7754c3716434d4e3c95f702870f49aa3bf79f961a57fea6a12c7c891d7e155887b79908b67e88189aceb4b270e7610b54aaaafe0fc8c245eebc77b03072f13142d1e80518acc2f4363ea9d6b64c5f24add9e5b4ed2806e17c5aac3dd81f53ff57aa4e5941943298deafff489d2e0411fef34f432a526cc0ca27fe146beb4cd9943d4ce2bd661f7555a6ddfd80d651b35ff33289a6a5e72c9ff2d4773da196721f893111a73c9854c6ee17d8c87e3dd4b44894858512005b6f95769c3df0e4f56d3ec1b119796ae48649ab2666eadca4b46f22c4a700e08b33639c963d28802b64370e429452a2c8e902321705bca24098cab9e34922f2c73e45168989867004017e31c2562c03e90bcdbd2b6ff6c256729c151c397921261bf51aa91dbffa7655ebed99b58b3fa0202a70b587cf6b1194859b587ceccb987782936fa674db0185951bef812ae6f897b823f56ab71bdef2cf07b52c83530091f10d01d29e195fc40230f900a6023122b1c950868a0e47461dcbe4440b148d43de31d364e4600c20d36b0cbd748bc538bb6a468bce4e5e5c2aa718f92296846e803a27d13754ba6a549a2aeb05252df0c142005da0b17a1521038ee361d7cc73e20f87928b4edcab0e339eac54473d522b2fbdf4684f5526bcaf5f26f22644bc9b1a5dc7d24bbed24bbc9274250da0dffbb102afeff1f6126aa41dc3142a15d5a557beca8856b53a91d29230a100acb090ea4065cd4e06b70430d36a0010d68e0001a20a0e2eec469d142c471dcbf376789b4ff354b248dd27414b178c27ba4f25da8976737953378330316cc808b19e01948f9c3057fac718e0806be0e3f7f7771c7692ed3fc47113feef0830e3fcef891851f5cfce840062670971c8b2bd53f9d6aa61fe9a45da33e55104b6ee824874842259de31a27a75da30e7f5b6e76533258f531833ef4e8a3893e6c1fda1b16b8cfc0d7232d654caaae51cbf7384eee68190d92a6d28e34ed60ef5a0dd2d7837b67dbd05aa9e05b20692ad14d7da4896eea437ab2b1cdd5f5b11d6f135fa0a5e1f3d166c58799994bf221ba4fcac790fbe443db3dea70277ba4d97b64bc0737f788e27b0c197ffc1203371e8335ee33969363606616032eee3e2b68bf4ec600003070e33ecaadc7d64cc140097787814cbdc08c17e81734719fa1a5a00b36e0822d5cd07247a15a9cb75372b600052d08a30540b48089fbec66e0159a9e9de551d3cf29d9e3a6036e8a70f3f9bca3979a0fe2dca558c0011614b15940030b06a0471c7aa071fff74c9c3ccd7fef3d4e9e662c63d3a892f3f56061057fac408f158c9921fe6274054756e0810ad850c118f789e28a320d7d54929327f95bf6930235526053a0e5e1461e378f13f090001e3ce0c1040f137cbaf455c959d459f163f5d297056d953f56bcb50b2e486deda51783a5595f8f2efa594872cabc308b9ce22867a0ad974a17f622b3a59ba6e4ec2c48436d4d7326ef956d479aee8879f28f956e196f77f8b8fb1d2314b42151c0030a48eefe76dcf1efd951666a1bca8c3d3b4477fbd9d1e4046ffc046cdce70998f0d4098698600226f8c1044d7c96b8ef2e69ce40b104af12e8d041822148b084044146f082115431822a75bca08e28eae0a2b3d539f975de7aa42225cb94731d2411fce1b3252444d433bf7389000d154116116ce033efd1254f33042bb801820b022774b4800e29e820820e1fe8d0a1438439c298439cc3670ea731cd9dee08d2ad479ffcaf97cdb1bfdbc528d4ea0ae93ddb5646b74bbb0d45344729bb33b0d2cff5a94a9ee6b3e4cb7af9edb6b9d37766fc81283e60c4079cb8fb7c010cf73751ff6dfdefdddf41dbd003683cc091ee782d951e48e2b33e973675b439b3f16dc3b581616a1a4b75008d0e8ceeb347cebfb86acd494d3f9d1dec5d150782e000b88121362024471f727821c7147210c941003958d0c01b1a8069a089064e19e04006c064e00806e4d0f9577e259cce1159da749b284ea7a4534d5309494b2a889328eef7e8b2796f2aeba5f8feebda57fe90d351e2a9de7ab6e2f08ea6bbb5a50fa39d936f6397aef7f3d6ffbdec27ffdbb1bf26fdca1f840119d8bc719f27ca46c546e7027b5ce08b0bc82ec04dcd65fd5dc6401718c1027358600d0b1459000a575f58380d93b34bafbcaecd2f568006eebe8342b59ec8308fa6dd63a900100a5060e75c1c93f552cddd2b145061027568d052cf0582f841fcc5c2106f3a012812c843024c4840058338d4c411441c49d69060cd02d66c6ba2709c04c1ff4a4313c7d94ba5e9eb7637b6e4d4e1d865db5d2b7be4e79e12a7290f0a45c5994b6a6dbf1649ce3de24cb5f554735fbab525498e93a8fd1bdfd9a51a0e06c0d1031ca437d8bcf1c31b436ef8e1461b93738d7ad3d4a4a3c4c810a764d3f4964051a412ef2a473cebe8ee463ca3aed3a59e4afea540339c5d5cc7d7a2ac7fdd783798f8d4b30d366d9c996d84a9df8696ba6a43aed8a0809e7d910c2dd6d2f4300bcebe0a1ba835d678638dbc860a6abca1c6125f74addcda47253fdb52a69b946ba552e7f8a8a47dd6ecaa915293023563a811424d14351d7889a4abdd665f05856a715c4949dddd7bde67bc6991de12efc9e9d91771126fa949b32fca6fbd4c3f561a63bad04deb16d9e5d3f4dbd265cb4da521e651b6d288e29e061134d6486d6aa1116682b2a231c54aca4263883ca3cdd5bf774afefeaf75c619972922f9baf60c999f3184576748efcca896f69861c694b9393304e0ba68db11b70ef669faedfcc6e3974da45dadbfc559eeee6f78dbfbc2ddb51d98c74de7a02d876051fe70eb31efcfdfb9babdd14c0617646821a383084020025cdc27486176fcf2a76d5d525bd4bf429cbd19f830ab47b0bbf837856daf6bb5fdb6c65f2c023a69a8c45b62af28e398fcdf36bf7ea7f9d2ac30061d630431c6149f5bf6b0575d919e4a940fda2ff6ed8c6392a39b566e6a4e6fdcc45b5edad11c7299e66d5d9952314c0076927277e4e4a5339082dd7d56131d25a31866ec46923a86af1861c4e0de93623471c722c69030c208e3ee9562704645d9d930be30a68441c4dd1d4d1da5d85f92fcb0a049e3180b1a3268acb897d090c068533299aaddee17e12d4dd56e261349ea16d04abeae05e30c18a2fbc679080c1218f87ec106afbe18e3eea1f551c92f3ecf5af9228a7bfd2f8638965eb4716df2228dbb1761bc88e2ae517b41c4dd939cb9c3ddffbd8b71eb8c1af77a26ccde673a1f155e89388bd56eb9daadcbbada1d087c01012c20c04120c904411b33a568d8059a2e74e8c2e43edf255573c430eac96b1f00c703c8b883d4545f6681e469560b9334c43d0f78e2ee332a72d107176cdc41ca05145c7071ff6d732ccb8abffda1a4526fe1664a2a7d54f25aaa7fb640620ba22d4c55ef67699187166db8cb921292507e2d645a28d122470b0038800c07c81cc913f982874da79d27f28539035fd7d60c669c9a3f197b3b4fbac74237fd1bc366d98ab2ff6bc1abaaffe0e751dce0b899299332f3f93433c5cd98662a0b3eb250e373cf5ca92cc6a4b298a273b2c082cdcc58d6c7428cfbc5789b40108b28ff7ad0c26c167f539076fa7bb0203293c9dad30010cc9d4fd32920487b50a85603805c71c615e0c59d150eb0a2b3a2481520a862892aa050f187fbe440fa594b7cf36bcac3490fcf8b37e53e7db1b51dd69a4ae9fa3ceed1937ffa3415312a60d0a47d3f9cdd1dca8465e4141a986288295653b8145f485184140490c248146da238f379e45adb963d274fb3a39c3c692a664e9ef0bbae9dc9f228cafa3746451d450e99109041e3f362cde527736200081870c68d18995b633065c4889cdffe8babf46c0c7fd2c47d1e454ece5c78de2b0c5802c51c505c008a1e7c7754f62c4e6acb3df1c513443cd1e4892563ea18c366cc6c4c0c633a70028d139c133f4e0869228d6b5348b5e962fb014d9c7f879a8493afc7ad472f239ec9cf39239edd2b7296380edb909ae8f8b2ae906a5b39799a2e2b7bd0c3fb61a28b9e26259af939e961fba0cb5efbe9cf9ea4f2c6a8c86a820626fa60e20c135a989895388ea4b32d55d2124e2cd15a42b504142550a0c40426274fb38886204a0fcd9bc73dfb4f86672e907e0641da29212a31c5dd5d0922f95d92d2a12460b0f3bb5e0a0c96841acf2be3b5a69210899288e24948ef11479ca5b09068816b29637f2b889130e38ec4870411316a4c263161b670911e1633e4de650b1789b7ecc2650b1731ee2f4df58f50e39e3a42d685cb162e52d38eca23a21c31c4ddb736624d12a92dc71911c69d869f8dd0e2d2080fae0dab1a2f020ce7d122a67454ee4b3b0a93f816e14ec4993b4a0f13f1799e720d8c999dff67242929284d128c0866c80f6c7e00e3e48715c62af5b6da0719f86086e34a3317a944f2819ba61998e9fb10c57d20e26db40288f1a939ed615a00d10b90441680480f6e8ad4a01e2af0ca5241a11dd5131ef0d078a88207301a0f3bf0008396d17068882434eb1b99d364daa1c90e426678ccc8cc7cb4999312c8889031d162297822e625464483b1a1c14477e794e4916322a140912514cac31b57e9678e5a98ecf9f6ebc81cf53df2207deda21a83336cb2b0198c738c0d188d18eb48726e8893279410b7a3d0f613fb47bf40b3d1f4121e4412a2f25f19b4b1f1cb6bd7c892131c3d6d6797d2d8a4337a6754dedc052cef642379852f2ff822c517f18b00be0cd1c435e2166d108fe00de028b0156ce286627f25b7e28256f2881c0a427ddbc6ba3bd3fb5ffbb50447ef33ed663735bbf4705b64b59d7d61903b71f2bf967c615bbd394efae8675dd92a583fb4db5ef93cf28af6cbb93cdc9d2b42c881508a50873a8401d0c0158010702f713a12ca8e8e92a1eefe087a976657d07b2792d4d7f2703a252543f2880c9247248f38ca3d8a32e312a7239f4892e4f4bcf7431c8442b562f816dd51d4a6084c11931a7861819729bc78f1628297c9edd75b7ef02fe6e417d18d4255ebc3490f93e4ecbee28e244325f254da7f853892e4662e89df75df74ad4de3d222f1f9abddbc6dcbfe7e7c6c7647d35691a6fb137d8220adf75fb1bf9fa5f60d7d49fcbb6f887b7da571ac5fa2bb6baf29547b79a0478f8d53d17a66dc7a9ad7c543bbadf7443b215d1569daf23c709fdfc6a9bc7fe6c1bc16f759bf05774db7a4329c5d7c533b3b9d9d6d8b42cdb60de9ca82b4a786f11a561edf609fc5fcb504efd6b8ef4e4cec6fa7c589b8d63599f5377eb9dc60d7197797a85d51e81b77ca66d232f4a353521ae5eee8bad79c199ff4e3c4c971ee3eb5dd5b1dbe95b91bd78df566bcb74fd36f1ba265bfd2c18dbb7fa5a10e6a7430e3ee3ac85c071d485a97377ea5f6c0d77fbbb4f12e69ba88e9c2b97731818b1a9f568fe122725971f1c067cdd8f3686b8b085a3b3b20487b3e4dbf1c9ac841082d8727390869e3ee9cce8ff7c3382c252c242c783f1698c45be2fd58b8fa5870b83844c1c1c344f2dbf66a0943d3f2458b122d1bf8ac1777a6cf2f2a3598a5882c4258fe70ff906a1bdb72d6dd97178bc5a2baf2c6a7a4d6753a497a450aed0a0e5760b8b281bbffb8d64a23c7b5d6189f5c890b494552dfd7924586454563915006d113d170430d6e68e30628b41b1070c3e98610863a303466680f5d195a3294047bbf31ccba42dc91a411230f03312cc4353f0c675a2fdd277c42d54b4d9e0a871cbdf4c16e4efa1b5b11cb9394bb1b75c62794a7127a2344874fad337db04713a222b4c26acd8a87150e2b9c2037829008ca62a50656b8702adf45ada65fc6401f7c80b7acaaa28e0281967a7fb19c81a7193803abd451058c2a3b548152050054c8a052a9b4a870a0ca808accfc4b5119739c947272f96f4c8863a9522a10a6d031458cfbdc188f5f2475725fe212e96774d376cacf942152ea904201f7c99594c817a47452a000a500e88c06f404908fbb732525f5f459a4f2f3eb5a13f79b7e9b33ed4ee1cf057ec8f8b13f3a3e73f898f1c9d5d77d97fcd05ef189e22344ebf1a3e7fbe14017dd5a8ff36cc1737970e011929223d5450a875400a294a14521e33e7f6398a61f952f0cca23b285614eb2a9fe6fcc5d53fefb0ac74aa318d16cd843b3410c1bae706e43a1765a4f50282de23cd4492852b4e1849d3740706dc78c9da29d1314174069830028493428459eb0717fd045a5bcf4db24c995c477dd94c7c399acf799eb2a95177baa59d451f0d230ff90131a388180e6240acd89e884086a0d4aa35ca8610635645183026a58a2d500a409174d7498725e2cabd43913a12657df25f5f8baf634631804b1a773f61d7f63d826d20a627d718764b62d4ce27b923263e9c49513dafdf9927e46f58cde3de29bc73d0341da9a8120dd6fda56a4ddce6f8cc5442aeab0951d5d67f462d8cfa8ded695b72d3f0c8320ce349cf1a9d100c669f8dc7dae341ae40c6e7c863433702088b3dcf4733243139f4cde684cd84c26665c6312041326f35aabaa765bc266c90fda921f9d18e854a1b3e76b2c8aff45270cc4711c57afa6a34447889233ee3ea5122f9a121692c8e13edf4a3a0e6949a448c221a10249d08c8136f5986c689301b3684a9f20955883d4347f5a532cc5a54842f2e49aa7119cc6fc08d22dd2d4658b3475d9c265ca2c5f3b79719fb1d85f4b875ad95271cb7ba7f5c485411e17065d5becef8774855974531f531af7a999c45cd36722d1d6113747cce0bbad76444bea08be462ae0ee931af162c44829052534d3c39f5fa4ff75f2ebc650d2fb60e9c70da98c4f5d2ffd70de5f442d156938bbb4520c8aa37c17c948ce0672d2b8e9756d7d994394f3022757a9292dc9fa0952b5e40f9150d04a48fafc58b192ea913f3cb225b4922dd69520c9524da93ba41d9d25e3ebdad7b5328899325cfc21d632c8f115831b8fc10cbea918448fa149ec3d1852308871c99d73ad74225b1a0cdcdc74d3aac1d0c467c652a70583b6a0f6c299fc3563ed05cee78cc6b41792f8d4a5cf3824c9d9a52eb4b156bb30c6852c34b6f3a44850911634290229463ef1d99303496882144645db9da854b520d35ac8d2420d1a0b24d058f082052e73069e2e96a6017cb0821a2b3c405b01b642111526a042102aeca8b08200dc4c0e0469779af7c1936ce790c3150241da238fc81ce411c99940cb150909c9c929e1e40cb47283e3cf2d53550a69b4143e051c525081881d44c620e21141c1041a0a7368281881c2160d0519b41332a09dd0c309f404124c30433301029a099cbb5771cd04134ae8400940b84b234740a9b7fd31fcad51a7df4a9448941012953ccd12eb8541921047925f97e96b39cbf45a0fcb2041cab166ae6e4a1649883399ae903c4d79baf80a8b1bf16cbbd882b45e9264a1b84dbb1249ead32963a9edc5527326905a8ed36f33de3825f2c4490f7bb6b35b5b9af1937d2d163fb4adfb5f6ce75a1a8edab83b086e28ae8dc4582ae6b836e28a341d25a159d3d86848d53436ae69466853b6a5a146c49dc73512522484e9522490d0c4672ecc2281c86caf48f049451ba2461b22f3d9a5720ee1b4214d7c081137ee3ec21a6d046d04313e82d81a61c87d6a2324f139c2100f001cee3e33f6aece63d761d7a87750a8d6932732cc2382024468a285604708526821642d049f2e5bf6885d5472f2b4b98935005c400340199f3e2a799a20e0a1814001106220d4a08190c2076bb40f44ed03e92ee5b7370ad5e26672729fa6208bab97d31e1440f3c0a475a087d6811c5a074db40e469a103c34215a68428870774e9eb69ed59ad238a043e380097727c1350e10a071e0d238b041e3e088b60107b40d88e08888e41199a3451e915af6b3b017fbbb3315adb60100b42027d08268a105e1e25c498b3c2259d8bb56e764fa608f86a387866326f6f9351c1f9fba560d881b9f290d48151a90253420d535ea39b56bd49a6fcd5f9aef68ee447311b48eb306ce1a6ae0a871c749c3090ed47002529b3796ae70d41bded76e350afd3acc54f515af787cdee696ac97a250ae70cc3d408478d0d23d86dbd614e41e96f331f7d968831574fe6df1181394830b599cb92410277082746014c4e4dbdbbc71d038c30c9c328078808c08a4c11903470c202a18e19ce03acf6fdb4c916811df2b2c5126a6f6b3f2b3f4088a796411cd469e2944eff3604fcf8b423db15f89619e92dfd3f75f078642859d2cecb4544b87fe81a6c6db8262483fc671550c47973461969c1a6b10b4ff498ee3a493d211e9e481a4117944fe7b1b1429dabded079d9444bb491d48bb0d8a9ced07251224d2b4f598398e93538ea04b9a4aa2dd24a94f33f08ef9b42fed389086595ffcfa2cafdd14a4db25da0d8e2137db36e36cb10ba2ddd9c25c909c05c56dc1314cc917aa8a350bc3d167f655364bd79ad2fc884275646a53f758a4e95e0141da5365604693814efea1700ced832eda00cb0426ffa4233fdf91094cee9ce4125891a654ce8b4d2ab9351dbaaa7f79103da81da18bc4da1813c528116adb56422a16d10f96ed474a8a48a8c5da36d4c7020f3a9e0b7ce4614316d3a886211fa2fa15f7ec61420c56c0ca38e79f18880141da33d46406166b5a6f2492335370bb00d46406ad5d6832038b255f724a912e9fa9e9a6f5d3748aac7fc72ccaadbf829834354acb20df98f262fb71d8bb1b5fe9595922c9d720059293d35932729cdcb4c571736b0cc6a467b99289c47defe1f7b4a579bfc0d3fe5e16944fc4cf241a24b99dd6132efc6f7bf8b4478caf0cf3c839cb38fcf074adc48287eaa8a768f291738a9cdc4cce528e1669489272ee512c917c54215d752fb2704bd7ff95eca7e7e215ce9fc3c5abfab1fa5fa4934fd36ff5471e7678a00379b491c718799cc9238b3c92d0f210a3e57184bb886529069244224b3148698a41a2442c4d314892942893965db6e441f39022512512450129e1e1c2c935147c71f72eec2861552bfd3cfba20d07d7ecb0c056353b82f04da4ff6dfbb59659eff35647565187835a3e61a7057b57b42d1bf4c292774c4f32060241dad3834281502dc68242a150db76bf56fb6d56a49ba851280f7cad83eba8b78e1271c4f9b58976eb28f94e65ca3b28140ab5238aa9160a0583d454d23ca69ed03cd22b2e1dba3458af2dcad80b05e94e499a74968c54c68ac38caf0db7df3477dbb561c6deb5fbd258c53dae190ddad64bded95fd4d95dafada2ff3d190d1f665f19dbad71b86d2aa3a14fd8c92eb63ab0b0d382422d81d59dd24eebc996499318f738be10f6713471c7370e0fdc676bdab8fb6f0a4a8cc7fff279cd185f2386b05f73ed1a2270bc81830d1c625c5b38b4c021251caf8b73ec0d36eefec6129ef3f3d37a5dfbb5bba3f7c6e7daeec0be84d50d99e64694531b6fa6ab8d34b30d31a936b44879536cbca19ab261c6ddd9d0f8638388cf35eec0ab35e25843e677d4d6854197b84613f789576ab4d9f60387d410c2ddef98c52b9f9a17a8d9424d4ccd15351ca47182f9a1694ad1ee34ba48438bbbcf99cbf4ef71a2dd9c894ba591c4ddd1d823bf1eef48f3eb71f64574d3fa8934bfb66ad190cd740e1a44dc5de733e438038933729802ca13fe62120a14694389244d1ef83aff8724b4dfe95d72b6cf2099f187bbbb7e33c6980184bb7bae99b1823b87b7ac20deb58c31cae8412b8308673f0c71a7c16eac9f4732b0e0e21a1954c89035c42f895d3468ce6838bb34024958d722504ad366062a49c979b1c559a45eeabf8e8a3a0d95343d5a1a20639c1903076d8c13c41881185e8821c6dd85248a9b331a72f23467607e116791861c87e28ae80eead1a2c538218c3bdc7dc4482651180a08a389735ac45b4b347ba0b9a247e7e41f421305cd0b683ed0c0b8c2b9c9f9e86fe7d11371ae5f673f0fe78709859d1630a23818448ab0d4be78f34519908efb8b6f6a4989a6c89bfa6248fb6288bb7bf1c60b365eef6b2fce54cae3c5e7ee45157777ca53553b321a5e1bea4d46c3edda4df61bdef5af756d2bfbadca2888b76d65af6534dcb6fc22fd6ab71dd2edda707b11efed5abcddf1da7093d170eba24ceee2eb22c97c180e1ff0e662d2ea016ca47c0018779ff582f87b40137fe3938b351f17613417a425a4d212494a6dc1c6dde7de628cfb1632ef6c91dc428b3a5c973e93e4901661a696fd48906e4f8b290e789372801af7300ed0e20069c68d9934939a0183bd9899e253e798f1c0bd278b2cb52cc2b867f169599ca06989542d1ddaf98cc39d7f5d4aa4f58358940141aa612152c182a4f5830df84003c8b8bb8cd66f80ea8a3793e2d7126351db2bc8f8d4207805d115292b5460451bd72779313559797a9615643ce3d00a2b7e8f1e27afcdd893f58be8d6559ca9e2ab8244051dee77d436d38cc5f05b549ca99552d1a2c27d72175b8e5a58993365a894c929e320c6e2cc75d22fe229ce80f89b4236a720798e499d31f6eac52e292620c5769fa58c39d2b6b297c214c51c5198e151acc8fc41c68ccf22acab7565ecc9a96ba631bc6517146a8ba4925a98e9c970d895b147260d03bedf746b060c71bf16f43cbce11757e9e759ece589a0b8e34128a4703fc16cf62ead77dcf01e69f758c49d9d27f5629e27fb59f5b57e508ff545bc772ebed86e5b7f0c839b8877cd34f6790341ebca3f74b1d57acb9807a41befa41e74d1fb5f0c57951de34e63523a61c6dd89cf0922bad5849a591347b8eceb6d62c8fd2b6e42ba37e1fbbe66828932eeee4dbc3d4abc352638cc8474778977de4b94bee2af385ce20525ec50c2082582945841536248c5a11289d23c3e5247be8b2b91f4b6f1ddc2ad9f35e57e97d4346fd9a366202c3f8c66597376f79e47efe8e18ac324aa48224c124490a800125e787d0e04e94a6a2b3d3b034ba68d74c70a6224c02001b4af0d75aadaac3731637c26a6062fd527a9f6b561052ddd5f8fa8e308315e0a65d68ef8dc9d86471c417277bd8ff0e0db4694c0dd6b0f16f95fab836dd96f5f3ef760d9f7f7c59d11548c40e1e7a7a5ff6b9df417d183af8aa0a10822ee2e2b116edc3d8d080710a125f657da4dc67e45c40cee6e2f0d65fe9d2dbfb855150ab5001a16408202ca50c057ede7adc730f6576251d64b53ef9a89f22fc62ce924534fe2fc5914e044012d0c51877b9214067a71882886b8610852ad29240fcbf9c3cc112601ee9e63b5528912f2116a6971d6daafb5a9aae8b56127d316a420768533ad411adaeeeb4a083c8448e342c87c0af1e3ee60be02e651f662cdbe8a6b1cfa8a83642fd6571c6eb2974e409a99806f2740babb6b41bc3185785ffa0d05b12647288830eed3d3d2490cc75a5f1043ee3e574124a914012140801a77af35c5e12f860016028e1ce00fbc451a63a150ad5a2907c3171f008c033c71009ffdfa894e9cd49a8af5d2fd40fce14044008825420b84101026770f5f1ba00c03fc60002aee735fba658ff7109ddcec8b441a63e1ab7f06060ff70fbe96175bd867d0454f1ef89a5a2b0b3795f9877490102886e0e2ee4576765a4f9e487cffe26d4645219bbec185106990ba0bb12326c2288c1761ca8cc280129593939323477cb1052d1e521551fd2ce9cad6a4b16c89b845f56bf0e5eb7a19ef975200e9e1cdc483f4bc1e99f14ac4ad6b7f563e193febaa7e563e3f2b9f9f95cfc536e37a318fd0c6abfd38782a212c4546838262187c9efd2ccdbaf46b6529cfb3f51854ef88893ea4ac9c05afae9da21f46df0a6ec57e563efa67e5b31f871f7d7efc72ffcbc26305b7b2d0a55f4bebb772319038dc7dc7473f14c0dd7f68f34319eefe1204e9f3d31aba424424d423759e3edb82228bf51bc3463edce145f66b59412c1b90c4146e7cc0800f66dc5f909b4a1d7b8a0304032737c907381d903e864a0823c36275159c0dba18130512fb3eab0711782862c4830e5dd14f5390c5f9ce1329d22079ed8b86569a34f54a2d2121a2d917cb425f37b2f02a7ca27b85350333fdb994a83e0c07dd2b9776140b5ee1915e6214dbee2e379d7d512c8abb974cb32f2205e962dbbfe95ae9b5d80bdef0eb330eb72de370ab17bbb47e70cb785bd94c360832d26bbc3217b9043b53116f8db1b86d1cb83b079e3384853ebe88220d5f70c719417ce3b26f218810bc47a20c328015dc83a8104400ee4152700f42c43d080aee414e700f62828f309a113653827b905110214148700f32c43dc808ee4102e0235c47584b1011dc838410240536ba6de83caf98b7797721fdec39ee5ec6e88e10040038110802827b900fdc8378104448284389a4f35c22dbc0301215a483204246452570f7d179eac05028144af4919736746012ef1767590c3b2d958243408122f3d805a152221561d9436993264d92a10769da66ae6d9b48f67dd650a7ad4f0a688889423d4b826327533ed2e6fc307cedeb5d31fac5f6cfaee44cc3fc9bc626d5535b18d5b25e17a6788718a43bbb689e9f0dff136d2829f715f7c47ec56dd156d78c6352df940dfbb3c8f936effd5d386e8b7c2ff6f7c16d250a9571b7895aa2a4feaecb8f8f500f0ab581e0c7b674d90256fa1f3de197c6babe05aba4aa2b339efa43a98d577895c2abfa3b8cba5871f72236ec4b610f3e11b7ed7f7d1971a18117a9344c2533c9495fccc572f9f1595fbe883b2daf65750fdb8ae8c78ae867b9a2c61603b87b9159def4cc072ed688e1051b3914e93cebfda28eba463910993afb78518618568c70c0e1f507205c7cb190bca9fab987055256fd8db15cd9783524a341b3becffcd7189497869e87418c7be6499eb6a6de7faf6be56b1d8e582668e9be2f75b65fb2c69ff1f72e0f4f1dfbfb5666a5202d91640c4fe9e8fdc93be777a9303075892439ce7b5157a62f4941ebb18086543263298663952b211615a9f5144e4ebc414a24e7b6a4fab32d86cfc993134ed32cadada9154e9e246a7f18c3f63f3b036734367b571639f5136048b0f70f506ae75c6db99f22eabaa3bc92165169faec519dfff5fbbe47595fac2527eeee1539b52c9162d8cae3d5a7e914bcaa8f7796bf7895f14ad73b02bdeec6abfdab2a8a5762d80a12578f4a8a9095036c514186229c04d11f0e0a1bf8e1c08f047ecef8a0e23225aa088fca8a44a94821fc08d1ea6806da98d0702589868370e8137633c0ce41ca0e5d94d9c6f0d2002046482132043eecad88b805f4c6258a488888e84716295204080ca02f46406380c478fe0d8944d547a237d33602022514285080a400e96c5b7f0b3bd94f113f3b8c7ef00f15f7d206d2dcaa5febc8e7012a8c7c4c40d552f15e8961104c01197284fbf551cf0f3d5b7aa6f8f774e0eea88de78c11cf14231e1d463c59dc2ff6d1295c4756b575648d526ea4c8b8c475d474c375c475d4a914dc61ee3e2bae236bdbafeb7dbde13aea6d739c276c984237add71260c9122556cff4088a1c3abb463b0770f700f86867dcf9d18e158992a64da274cf17d1160995a40639dcb15411c2894ad74aafe7e10dc4f9b76ddb9a7a783f6cd4dbb68114572740fc88112aeb3c434d631df52eaea18cbb447161c302e29e867b00dcb87584a00798291ea43103306548225c1b0124a044248a114479e195060c0e97115a308327c4c4c5cb05e2164d8e60f140140e7a464dd6709f1cb70110006cc0e9182dc161d46488cfd1920ddc5d0d8e1eee382b70c751813b4e0a2e752158e4084122244b8eb1e4d4ae51f3081163c5b1ebb0f4eaeb91056d83171499018ce7e18e8387071d0855ec8e73873b0e0a70ec70c7390108b8183121832f8dfdcaa760c25d0b5e8b858c11130fe68f5c522814ca1dc704ee382570770f70483002e1d321e30ea4482789ce13c91218922eeb116861ad8454ad9ed5152a9b9654d0e643b4219958f01048f3c7f6b639d36b254b0655d5fd22dc6a818621ee9981a238b2be8e3e510f167793bb91d154010dffeb344e1d227097784b3db5bc98e63125330692a0ab28e3584c8ffabe7e8d3f1ce4d4a348e5587116a5112334f63ca2cc860fe90a4868647ae2ddc5de6d26200eb2429c8170abfa6c4a74df8a8c0669190df2b08c069172a42e9124122c758efe11da78559faee8a63eafebc2204fec57f38e32bf38759694aa8a44f2f011c924ffa620d55ac41aa478157b208c5b5795f14a4683ea67bcdd9d0ad723fe8b73fe196f88612bbfa98f27b447a18ab1941bc3b70ad1c5767baa0f69f73caa07ad9747d38f4599865f779f474ed045a768f7ec5a199ddcb5321a14c320c57a4641918cf97594e74a0fd1030d81f6eba153aa8b0f2018185109248e510b2a1842c1a805342c2ea31664b35a97cd398c5a68b23166e1b4dc3dc7472cbcf1b1c20211231686dc670180a0613400f0b9aafa2aff4594db2ee6f6bfac1713bfa8ebacced1f5c7fd7536c417770fe6af77ead71ec6794885258557b3af52f5ecabe0d54800307077ba717e323424e7078829050d8c52c0c0288533661d7dbd15fd87153adc00848f0ed058137004f0434c0a03b887364c6ebb98db18d42314ea98fbbe54348f348f7484c216231486f03be62d4dfaedc6f977c0dac26ccd5f3da86fa5a8c356f6b38a3a6c8575f7b3f6d7d1095046269c3132e14817fac1b182de6713cebf83b6316c05e316de57a5adfcd440421cee3e82c947230ca12102199dd190d4161f0540c73790ee8c836294a8aa645f2f4d8d20f8e1fe6047b72d7ed7d1d936dbf5846524c20f1ca3102ae0786fb5d26da379a434236132839219089084229981490d4d7428131d1afe992ca161493d9d4ea7fb2246a146218c42d8608fa11d27f8c38f11089f3b4e087040e09e44a45b8776ebd917cdbe681b81c0c47d480942b4f1128498c34b10c2045e82103070ff40c81611a02204eeee44343f30e0ee5e421f4508d0640f0fb8bb8bc04116da86483a6dc39107643492affb2d3715a513efabe34051010ab86a8b22d7810ea30eea965fececfe7002da6abba9a63a7734944e38190d61904f6449464318907c2181f63b89447ee1d658030e09b8e3a889c31dc78d35ee3870a859c38d37dc71d4a4e1863b0e1b6d78af1fa8f433a7eba285884600000000831000203020160c872332a9743ea8f61400015bb4ac5cc9902e50f328879041c61803020000000000002006946aa5257dd9110cb67cd7cce717e4a4eb884d14df0b7ba26e8eeeceebe6315f23b0e6d6a4a11d168e2bab9a29134cd092cbcf1e3002f291f3dc9852d04fc3beb8a46812e698a658921bc6e23be2e9bf2591f75017a3c745e77a0ef09b3d22d8678c287b4d6265db6630223073c7975dc16b94f018a2950fe3767beacdea2c5726c0f155030d54683eebe2465d45ab8358f000fc148814c897c9d364915d22f371bdbe3add365062d8ea9021186f08c3b2a2da920d5c50e72746fcd6305bb4aaa8bfde33e162c4c5ed391242f81b7709c55627052af96e07a65cd7cf30d35f7fe6deef91bf770d980b6f04d52036b6ae2677fffbaf69eb9b91eecb2f36c2e43dc7e10ccd4100d428e2800af2a867b747c29feb684e29603ce257dd5a727fa8dd422cdaffa31f1a76c0bee7a7c3cec08568b58e3c0cf835c823656ebe02ead5b674eb61e8b30670f50f7be9948b7cad969688522c7fd03a49645a188cf0904f6648879fc144b0c029600745137fe58718ce449e66e680b35f187bba4ef0a13131aafc7a8230752256fcca748ace63493e87076b0d1ea040720457561fb633156a71c7a37a0045337b11a0db11af894c93dbcb5b1aeac4f440430432645f9c70075e5abe02aec3a1b2ad415d3cb989a006974ba63b044bc1ae40f5bdfccc9366165ca29699eb0d00803a7984dcda32051bc8339858efc7ca0d164fbf92bcb60a6b5d4f43815d554a596b90e14eb25ca061b98cd36ed33253d1ab4662b9726eb0c7ebca6ff0fab14d666969ce530ba4e34a17f3aa3434be6007426f5e9753c38be7597280c4eb90aca5ac16605c1aa2ba1db8f3f84539a32c153fef0b3e65d7c2ab3b8c6801deeabed348ecddf86f52be9233c89cb683aca155ec686bc00f795ef249d9c78548363100d9155c36e8d7a05ba7bf8383888b1983b4857ba1ce595f4577646cd66dca0f34dcb125b1d0a38f9633b4b1b5a679473f93350f656d12464b7c30547185ff37664bae833f474614d60132c6854af0c3c33242cc8bcfc1835841437c9966494a5b52ca3597669dd3c6a1e67a81f2eb0c8c61e70b3f05fc4d4b15a9c529d6f991b4c888416790e1f6f4cad6c3ce823686064404ec26add0f743fc6aa14fff83883dc0b465df2271f0899aa648149445a25904ada12bc9aa8ff00a4e8ba76c60a6bb127a44eefcd8e6de5bdd3927bd7dc4d50b6d604bad5f25db8476d68c412d235631b45e6570eac406051403925bb4ff943e2fec48d418485861fe4b6f47d9c1e448036f9fe9b0b0251205d0f9b91962a6964401747e8e061cd3525a00a8bf988ebf4c7a48ff41e7a7d9cb552ab3a65c315de23598cc4b80267cc4a670ce057b919eb6726c2c576d93963fc7732938da684e97b1f169cb2b70b5d1024a020e7d6d5f8f8f338bb3171eb1954fa8f002b3eb6009466a19a0912d8dc055574ee5eb79db310c29ff14cb97405e8f3704d1891519494100bd22bf2b235d2eed268140f3949712dd97c64dc26e1fd8df744b0ae10430740a1c7143550f7b88f56dc7586a910cddeee4134a096de403ab68f7e0fee76ef90ee329367dc693a10228e145a92e62ee2c543071a014120244ca7cd221d9489bd4b6ae91ff12db8e0786f1e6ae4a67c7c4a49c2321220889ba94f43a1c92f1436e644d546ce80c5852d35c8cc7061e6eb67b5467c8aa22f0430078d72b0140fcdd553ba1d9c771b8f5e62697c8268a5f252000dd91ba1387fcc53b7a31d9ef18395c23490ee2c7439ebdba070e0bd2b224878a261c899ca175c4999e39164ab5985684ddecbb494d3c51dfd0ea01acdde945b6768d5d6d0019d59f23e96a776e9f2ff9e18920b960c4bb0558cc11fb90b1c5aba7f68272b5a3f601c182cd6678fc49d31f110fc405d6c461e216d7a1a35dfcb83de41fae864cc4fe3d1bec6bf660cdfdc56c86d5521d8be56ffab9b8bd4974144d236fa62c9be4eebcbb80cac1ecf2e1a488818ad43116b6e6ecc065bdb54404d73f125f93404bcfbe60fe876fdea728c6ea8a064ed0f8708065b31b5fda9a6ca4ce92b56d435be111a97f3f416a3706cb6e02658a8a20e397f62dbf993137081285dcf060decad16e56d7141d2da6a73e834181822139a11fe4573422af1181824296e24489f22e63a4ca386e5babae677ab97424709101642d7b9cdf6183b7a0abb1de894f9b566507520f5f98a18a17a2e86d62be61d9c9a422b69ecf6a7f58648b1881794c8f6aaeea55a2ebe1bb3872073500435c2fb154170dda18d59b08e4ec795f93bf99d579e91bd839950e76f7cc86fafa461fb0f1e82ad39fd77bcc05282f9c2941c27f9dbf6753e8e283ed3d6afa6656d4d092aed7b9ed4cbc1292244c2ac76fd1f7e8d4d37ede2bc0dd8228b631c27df5dfe3b2d6c1fe33ef1e5705ba09ebad3bbb736757d417435f129b580dbf51a758be997222aa37516d0d4f255a1f8b82e6f869195b775a72c74af260f4792ec487bc86e0e2ccd8819ee85cc12015a2ed437e7c8f08bdb68756c3fcfd9508573036858e764b1d14a271da69c2ed56612b1080e15e6da8abf203b36c11ea35ac9f71e50bed4786973eeef86daa9915e93611cc7f9b73605c4c3c3de052b1f5cabb2a9a720688cd914a98553438a41aa929230d7a166f6cdd0e55c29fc4a85b9063e03f0f5197b3ef43b7080c1074c7d7ebb82100e63adc4e308884c2c692697b1b7aeca32d90d4da61fc3374a084c065dd2da1fa6badceb7ea9ac998992357dd2991bc4b2ba84c84996d4114b3ec87e2d3330d39a31dfcc214a1af1b3f11fc21cd84ed97989ce9ebbebe059b873c20880eb7ded5de4e83eb3151f52503eb246b577718525e3815eee25c1ad35329a0d81f66962d05cf3cd6e2fb461a118aaf9f3d44c75d917c85380a0cdc5c8314a3025061237eacad6912f462cd11bcdf30872f53f5d2322e4d1491a9da1425c39d639289fe661720c7a42f64b1422b614235ce3af0a4cab114e618a009040bc8e0240c13d1e482e693402d119bf243883b15518baf6f285b3dc73f27a93f3a4785447d0779ca5718bade7c60fbc8f9ac89dd8fd951909b279cdba5e4899912886d0ee4aeec0b3ad22b7d2b16ebba0da0b02b8406df25cccc315ce569c3320ec5eaab55a1090ad8dd895c1d45271a1010601b29bb272c832d4c54424ad870729a1009f200c232923543a26cf8ed998a8802d35996c499263beacd3872045e4fadd88950fb50e5243fc5591067d4ca355f2ecd52f9d5e0f51021ffa343f14f1038e191535e4a1c0fb86040ebdd0a055695cdfd72ec8708ee3273454f5cd0740302891a91c5f5ad1a269e7f79e0793259be4e4ee26c2fd680ae06f2963e34c184ab889f9fa965cb1dda87e56718430cb1cc4bab8c7d48307fe95fbb54b5796815e29a380a6d1161cb818d525a2f787341b24ac8921cf8bc3ff99885d82dee108659dd12a15dbd024a28461e4206e4085b449ea2c7a603cd0ae9cd793264d43fe5397d30ae621fa6d3922d5402e5068d6ff004f68af37e98e530bcbb0af5a25a635305a52337936b4f9634077a3c2cc7dcbb8efd1111ced26811f236c210c3bc959c98b7bda09280c3b257e29c173eb3bd13dc6f457f2296d6fa720da0d4738e6bac697e319f039bae3398626334d30763311dc83eeb4e741354e51a3320cf269e5acffc1b07a1c3e6e1eb803d4c03d14568ea501a7cfcc561c85cba6cb553d76cabe87fd5ddb18600cb6aea0141ed0f5ddcd789f2a219388f8ef2ee74eb84548fc4496738827f1c4d97965c94c274c18c7320677eeb033dc75545de45b7b1e2690719722603e2e9f0d44a85eb5cb85dc150a1980f803986a34a5095ef58cc1556a38e942e065e5bca5f06de368600ac01893dcef2ad92c4bf491ce9d01197116a3c464e250feef0d6561e8cb97b4c18e91aee65dc6e3f66cec844da0b39a89ba4d48d0207029abeeb186754a1c8aed7fcff288be3794fa20c8f5d7330ee0f3159b6a5bfd549fcc01857976f8ea2889676c7f1d74ba496a1ba4047e4e79ec9d88cfa239c0bcc86ceefbabfde74658a7fc77fbc908bd03d108dcb7d89180861df6c05102e01ac4e5c55d71b885646501a317f1c7ddb04f31d4406985076518a126cc874a1063898880bd729a366e767d0673a9d0eeba3ec55e5a12c2696620026cc0c5f2a3dbdedea58d5ffd7ace0551978b9d7f135ad834bd851c884fdafee3e0f08a7848194a21be2f9ec00d13d64a0d755ec23b3641213167315cd11a3901462fa1e0a6a4c59815927a94e3be31dcd5b9a6445bc73c05b928a8c8cef7d3f423fe6d2ccd1931aa91c7b1e491acaa63803b284a81304c4721423325d183ba8e874b847c30dcf066b395682a18b0c950ceead2f649b244a9e257252f93b5a02f704dbef00684a10b89be20399c814e99639799be0b62a12e540533101d159c7610ca63adbe5960036f93332d210548121a0ef19914b4391dfe38453f502439dd516c1ebe5b37fca9b0c63a6a820dde9e62e11639bbc670d3bf2c81a19dec173eb44a0f34468bc28ad7fbd9b5998887d3678792173bcaeb7b1a2207c6589e4d786ebabab804dcd22e4e47fc769e6c95efa5aa5ed943f1d424774d1a9159d4a38e75175b126848b9d72e662d41f3bfc49fd99fb3d811ac3b2db31423063050bd0f2c86a132409d28c7ad9f632603a1fed18dbe4c322bd6c174a6d1cdbb7e9652ec6993841b36e97f2193b41f22436edad46399bcab336487fd77c8e8029d2554028ee7571828a24c63d6f159936e532b55428d89dc0ee029a98d096e3c81d99152808e6d799fde3fac1029834f12e6ccad405fd49787d22782ca27733eaaf4189674112b3a640b678d84c004e553f068d5543b485a68420aad2f1f69a457636f11088051d1136614faf08a5b8113951a58e56bb1529d241bf0591e726bb1c10370e675b98a78128e7596d3cda66966c1e5d12bfa4418f09ebdf44e4af08f9b4594f8684120a9c2a6d2dbd4b000e2fdb64ca0aa1d529ce77f4bc443c82a1e92e45331b3d3d158e314409c619ef167ca3f823b60958ae29cb2cd5696ef0c5fe8cfb3388cf681e2bf8db4c5fcef3028fa0849df1e020b5f5a8cf3e5e82744d5bef054b5114c8a7d52055fb9332f03ac6eea6e9163fe4e2b844d8a2408ca616adce62912fd7013771df56c7f32e722c8b1149fdd03ad7d4dfa7d83acc4ac08e355baa3f420a8cd83bb76781f23d556c2a74e7baef3f9f796eea4fcc09bfaf8b33a22f793ca472f41c0ff7229e1f4d08823f577bb6d0c0c740370cee9aa372184845709d559163c69c799f97844a0f6e8c72ff089bc9b0b696a62e14080f8fa5baeef33a23614a11e6119e76612dd2799c71f54902a621601cea91737ac40f43cf15849b2a1a40ad949bae85c4456d484ed97e8802ec53bb4843b940146e312b026c06168abb9f7439d52539e77bcb1cd50e273378989d017ef2d3090c2f37c823d0cd62f45463260bf87ef02c7d0ea9e780492c56bcd2d5004353cd8e6a87cfdbf179a41bd0c870cb6d656164b5c74940c5178bcd4d33951111a35444478293b58a5348f085ab4b7ea9bc658385605e37f4815472bb542629088deb6271afa70c0c8dcbd59e856953986f4ab7265062808155c3e11da392f09e3b542f4248f7e77cc9fd51e7ee212b3698394904f8d06808cdf2eb692edb88d34d82e85076418e26bf099b0fabd194eed6a984182fe38a65920fc6a89189a4326af30e0cf700ce9b1510ebbe67b35cdbec5d0264a0c3fefe1e516dc58d3437492578671c230aade06cbf8c400cdcd7686f9bd1902d91a84387524bc183aecfd1705f35783eeec2d361ff71ceb8c003a63622fc26c7cf69e81811e056d98bf43c66ae9d6faa7a2f825c40dc4ddd6dc7f81ae4ec04b8c77463a34854a0f0abf5b0b0aab76a0d365bccd86554a58ec043fec41cef11c0537eec42142502c532c54ed5c7a0823890969c9c500210b83c9c27c2d2258c971115a8277bfa01346e0c04493b98213e771326735ecd08a2b747f584de21fc8be0f136c0bdee29472c6f0ef3c5a7e4d545b45b8279e46bb498e5a7a6d43fa213a56133d2c1a42158e108b85b6ed160d8ea4fc01ee399cd19ff1e861a1d012ee67faa68a2335acaf5724804d1e99917577d6aa6a1324be54680964c69a4d6887e7f445cf84b81ba13a88b41d1c5807521ae9595cf1613710a7ba3355bdb63dd89c46d4ca3874a88bde0c4174321cf81d8c5b372f647413f02d17b909fe3c551307eebaa5c3fa7914ea0d05c5b9cdefd82cfcbe872855bbc47bbb89559a9f974b0fab9227b7792ba5541990a6d738924dc901a86357b0d8a811dfe865ab5c621f6b02961728fb81c2f97cd20ca47afd844b28bb8aa230f18e8fe45a4939736ed845527e3d4892f65651770a6ad5b8bd2482b422144ebf9c5233e7ec5737da09fac05803ff42b91305c0f3fb8accd3ae6a398817d11321865172817edb5f09d36efc5ed75a37bb4f99c28861f6465ff117ed8a725e2d23591ad1d35c3d3b0f7d98f247337b2e8db6ff9ac42b013e52ecfaf4377626871ff9c20e0010cf9c58ab03a955c7fc12fc4f5c6486892e11fa1fc5d11fc8120dfdebb2a9ff3c12fc175fa4efb7bf1431bbab101b28e58293a9dc6cfcbf5b2ffd0d8bb78c3a6d141300a64457060665ed6c5698d9d09469727980237baa8e18787ed5a41466e0f6618f156ec6186e498bb44564c0c10f25bb2f7060b37b3b8f3824a2ef6426c6864950e676de88347b8298c0dca9df7b472e583c518a62ba65cb7d64019d5c9ca45ac96a2c1ac1ca37be831df6b3ee5d59d177225d8a355cf91e072b29e8e079d7b662e194dc57a6c1abbb6715bb2481b6f2a4146fd1da467431bfb0d26b445ba4eb2b120ebdf9ff3eab7996af8e0a45ca7acac428e6ea46d8e2b476d2b424b53e60c219344e0c744dc68382fb76dd4bc5e475f1cf17f1b8b62f4be3dbde1c5c18d36ae4a0303243808db00280c4091742942d9a60c950a00465c1dfb7d80b042c7016338d0f3107a949529d19cfedf9618ef53e6b44b58c4a4d901f0c95bb6038d37fbfb29f91f8269f1a3be987e8e41ecd3249314e8dfbcae277111ed6345c1a3a790ea8e69d099e630bc6d1c30213d3f264e4b795206d305328a5f82c3db9ac2c0eea173663ddabfcc2203f38096c578797f65b4c19fc621eb7b82e340921eaf4ddbab11fac8340443cf320a9d2db2a71f9b4e1f95bcc52cc516d4d5afb12b1577610e6af4843bf184f3d0cae0e98b037731c5f6f64746bfe79819de5fed38b6c82466bf2dd44fed69f86d127e1a72cc4340ef4fcddf53a96d06235f8766ff113c1e6630dc216722f1b476a0daa128ae2cd06da29bcf58a308e65e10af2485c7c0b9aa191516a7abe879011c40b606df72a1328785c154fd0126a5708298cb7f609dd4669b97beefccb496aadbe6f9e4b66df8283149355b1067a458954773328821f1868382cd7850bc8e498ec563e42608039cb0ba91cfbc0719e53180772ef84ba8074d619f25f2fb1f16f418389cdfa3a6cdd316614f13a18af57ea7c9a90fa3f613297fec408da8ff67c527133aba8f03349b55302a66515bb4c2c27c72b07ec27bac4c4760fd9857f68591e23e88c2521d98e17a2c8cb9e2479bb7e57b41649cfd81a8194b9bd1aab05d6ab884ed97bd27158765243d7a75386687eb2c8d7195b1517cd24832316fb7ae56ad34c4f8be589dab92354e48ca7896eebb254084161a89d282a575c4a233666f5d3f205cda45e06de5de10d9101342d5aaa0d5401083ce563217a9aa7124528fc6ccf56dc4c51cac76d43b97cd59f2a0951fca2cd207578ea32e3bf4bf02d5ab8eb885282e05d82ea2119b094325490871cd65d3ba01c5e2d3fdc203ada16cdaa4fec931f02bf4d02d8c38ecf33d6dcd7c4650d2fa99ed852406ea2a691aec109dfbb8d53c1444a04b4144480d3afd7a06ffc1982a1d7e879ad95da859d9de5065c2ac3ab6b7135880b0daaadde06542bfdaa3ecfd0f84a2a4b50d13a2f942bac55c772ec5bf884c10238c755fd2825e6bbca22d5b85024392190774c270e2b2dc53d0863e6914c06436a6c1451d076decee8ff4f1161ed0053b8e1889000a32df25fcbb5e5591442c7de51fd5730682edd1fa21b2f09215340bb13df46d56db7e8fda9e476acaffdc2654f630aa6c430d850696007f7c04b742709804d7583aabf71ec9d86f4d7627e568063e1ec4a4760fea98ca035e507ebe93f0657f8f6ec0d45265554935105ca67e51c82460dbbb532c9a1c63f77cc2f57bd93f01aa42397e9b2503788b68c48da96e47a7e28d09d76f9c0d303373b11e9ab3070d1816126898fe72cf4a2ca30d18210d02553d47f4b3dca6c151e378cfc59c0ae33692544629e4216710c4fca4a26cd8813ebd1d81e88ed14ff5c79ee4331f917e04303279f3a72774d7e3a82189c1172de5faaf51b4c112d103cdf72087a41628ff01636dc0819258be8749c8e3027e6de84e9f0b47f9a0f4871798da04a23f1cc60f410c60615a557524b0404f6e6808c3605cc8cbcf15845ab85ab398a34f40991439b3a56fe10476273f94a019ce1fb4154d835024ece284206ded36391318722ff58da58bf4d802328e04ec8e194cf19c110a1cffe179c6bb102667a09d3f21f0a4cc8760bdd6347e00ba0d99e4519f384ed6a0717f63472e5e9f83ede16265a5d3d1e9886677bd888a28697c24f0532f1240d8dd9ce5a29d88ebf017967fb2dc76547a2796733e677fbfd446a48cf853e144bf17848c4f4bb2c34d3625d25e90495b125d4cb9af4fb60bcc593c77c03993133bfa32200d00290f5ef15fc0092dcc9e907fb047ea39725ae04aab35afd048c9a36c3b7fde4e2dc1e9f5efeb0d46e568f8b2b51caca956c32184d4d47148e7565185d148f8a30827e298bdd69da203e34ae2828245b4f6bdc59a30274faa6d5639eca972db182da5a4b7e2c273302b757d5f5ee639e8f35d4177ebae775a2b3f18b2e5b41dbcd027890c314cd286df4f14269ae94e9f24e4d42c305fc231a43a123e95ebccc4abbd34b0863f79d2c59862f8cbd5dcf0012eaae3e8b393629a62cfe504a23b2350c28fcc1027ed30d4245f8b2a238d25b4fafd43e5ae2e09eb9efb47d8f6f31dfdc126bdfbe236cd3b01f333281f857ffa22b8083c14fe4506ad7c76f416cace6a6fc696a133c34a78edfc1d5c0b88b150c5ce3acb061f6ca8a1b72e8bff385638582033794a490e8ab17272e04d83c56c2f89de01d608659af490c2a29710a1e70ce12e621e6a9bdcc86940486c211afc60992bf148290bfb3d5d617a82f744bd993f2bb7307da7f38ee7621036780bb820310531b5db405283783c3a9cbed725da540de568336adfb0dac671f5cc813331bf586f13597807f883922b049f39bd6606c1ce46d35f7750198edfd6bf2143acbc9df53ec34f3e07fe314858a1669ecd1d87910fd225626198b179e8f9c664f13f75b86dd777d1f640d18a07752191a06de03821c021a3dc03193af567d113adf77f554e2cdc7c3920bb1f6ef955f7980e9794883fc01f449015891933bbcbdb279dd69c8e9353a5ce2fae71b69e8d5e57bb88b0d88fc58a0a633b1a4033d53ff7bda4a83257402dfe3d1c828769dbaa63958dbf43411092cf6d2a5c40ce11f9f81efa4d2d0c3980f2e74cbe887ccda3a270a32adbd4803251a50671b9f93f2be5fc1778fe3a02268665628a4d5b875c5e9917c3c1935b0e9ded8eca62f756d356653c2532e3a32080369c998409726ab8ec47130ea537a545053631dda4c17482e32c8fb6fe54dbda998fdd892650c83caecc61daa744bbe1ce830c4cb1ac37e743ba503a57cdd78017c83a41c37d425f65eb20bc1610aca8ef01f334a6517cf1036834ed65748a7ea8403772b0ec8530a9172fbcbc4fc873cb25327f8ef2eac42e82e125b0031367251c2dcb58fe1a64d9f416020d85903203f0d1f4860730b4c043bee9cfec7d8d5fdc21928f38c4578331cf500e23ec417ffc9a2dfd1ee0b8cb10b107fba3dbc46f8ab87aca8a4d6f6de0f648037f1fe42675fec7fa90fd6498617ea4a85e1529a2b230a719f6a0dc8db654b446ce8dbfff690131f15fb5fb9e6eced7a33543f111a953a1ffa2e9b64b5d1f2ec18410914d62b21ba21b3591be193f4df3bcbc87f103c0213e52b04da3d2c236273400f82f68078b7997642a22dcb4ba7c07776316d63661990a217ff9789941d0a87edbe462de4aaf40f402aa058f2fcab6f594fc8c0b7a63fb7ba7c1ba3008968d4381a385f19b142683b81b149ff3c309829228fd486352d35877d1f02dfb0e369c023f3ccb8efc624e513b248dbc6d5bb862df525f080f9155ffff8763159d5986f1fbdc9cd8d42aac8433e222ffdabe37dc902f63e28567e87f07697184f7ed349d02a79c70cc81e74e3fb92f8c0580e0be431be8faac079ae997b2a49f0ad6f44d1d14dd21b8af6fa868502ff07b9aa38839532f75f003eb37c513809d1598dd888be841a7f6621df05abd96bc4280be5d15de3954dffa2f71d5b68f7d00d0263215f33fb9636cd18e418a82a9627b2a555136d53d6bf688ebdd88514f7289861448a9b3b33fd59133b1059ec0357fbb54cff39c8ed36b46a5274043cb868f8737b0f21d7a2b8b6cb109ed53440a9431c689bddfb6df2cfeab850d3398bd1fa4b69433c3c9f2de2cddc7ab0494a39353b943792581f29bcb19a0e453e6c2a6a5e36a29ecadf62ea7c189cefb49b9eba0fe572c2bc6b4865d71011486fb60d520e8d59ff5ac955406b7aed1f364d5a0fc4db944684e9e2fcd55e5339f835e34697e259a23d2d1b52d32b709f117695036382828413f62b3d44aaf360e7690249b1a133946a08b4b96dbd9f7df0123b22c45cd1d62b849a0ee8db10d31b2df33404bea0c66c892d60e0392b4dee39ca462ef3d31eda790dffa62a7a404f8356e9cf3a7d6953be23ba91a0220a494314b4105ce1088016ff14f56c69178c69c0ba793d79fe0f50d902953af1cdc8d550e883b811ff3afddc16f13aed83e4708c897262ebc9b293d5680cfffc5743520fa35a089fa6c25b7911ea356153176433caf708a6dc491c2a0b13f181f3e17f27578b431123b313241e68c10dd1efee05baf00dce5452cf5474cce2ab4a53088c2f800d5d65e19793a48e2c9bddbc5d8191cbfce31293761f9430f54be9bb35e06c83bdf160d319ef986e154a260b535d526c8395961774642ed706a41c66f07e5012cf60d2f635a0dd26110d60d5982d6449a44a4cf00b8ce1aba2e70f20aecbc5ecf878b20f9815867fc24705a4d2e91300fdeb79bb83ac106294fdcdd182ecfeb4aac84117cc3277c76572c475845bbab430eea798784e072c6c506e3c417c27326aec3209605d99beed3eec5bde6dd452d889d4d874280dfdc7862461b86b6f9cf7509165d618298655be59b9d2c3b9f85bc96b9ef5b69b1b7ec7dd3b46430f5a43a8afd2a58c321ce79555831100bd55a5797ba08a31237520114678e22a60a9702a0ae263a7173a0c43954fefef0afbe8f97112ed1e81d944f47a2d9239af7bc847ae957c6366f2e76052b81100cf2c7710f4b068fff9058196a37b368a26110e6cb0c17e62a8b947e026defba89cc0c0e42c7145089d96ce830e671eb0e969ccaf719a0e50086defcee01562a97698db2e285a2349afcc53a183e8fc069142a0a7508ab9606c56d60cf1d2ff5a4db4e68d7a3276b7cc31c727b3a9431440fd49e161a171fc809b0c50e071422419f5387933e679d636aff641da810c1d9642127e7081ebf549a6e66c950b5a45143534039b557e0fcf896b5b7cd20773d9aa8af48dccf3f9ffb8b9344b20a74a6218364310e14b34cdac30023f823d4dfb4341364022b1d283ef7e99b61a08a3c362d471c08908aa8888a67dec8d3296eb90e4d325412f7dc0da0ca9d973b3b61e3bfa8fb4ce9eff99377cdb25a3bbb19a1fef0e2fa991785d721f186e672addf01e2666f5994566dc1eb168f4f2eb650c3eb8a2ef34eb230fdaeb8b8de90855ff5973b000c6453281a9c9c57303ccd3437c92c60d9086bbde6694e06892614b92e7b399b68d3d7f92ae06a38efcb41ab739923805d73fdef84be005b3acf6f3310ba5da149a0c4103ed474a357338a874d38a7a44f52dde7714984353a1142c100b75081e55dc3c5b9dd0e86a4d985c79e977deac877da904a027d4c6e014efb10f2304265819bc8a3b19c3a338604a15bad0bd95989650cf3ce44cb2d3951a68ddca08a1e32b7c84e857eb4c4ed01683b46d6f85064353b7be7d0454179c39f52bd8edeaeaef0717a5bf5cd84e92adefc2adab4b7c3df55586c05395930a45ce1c48a0fc3896eff381b5429143765d6ac2b51a2b47c995066c43bc319c8d8bb81bde06170f57d4610504420b6472562a63b55108951524c42b39ed936793b1450c3dc60713ae240ae0725a3afdadb23744c7c9254085a0262c230e0159f9e989f40b7a6c73c8826b25dfc98f34badd0a34c0cb600f4dbffbc0be750d7d17e1cbe1d7cf04cb6fe02d4efd60633fb5fcf36ea72da87bba2b51575df46c300c9e2dfe7ca0caeeae6b40ea6ed876991b4410e9daa4feec8ae035e2a257d47543cdca4afc2a5061cb21efd85dfbd31a7079cbf9d72a177c051b42a47dd885e528b4286dac2b49cee6b2a5dc6f7cf48e80130ee0cb4a4117680dd612981d9f19003b52fb219c52895ba03870e96c1aba3bbc773d32a016ed0f26f5d78940c7b3d159d9e283078d0eba81418dea8663d467799bec449a854376b903d33e17609b5ea04d321e94a7e4d3065c1c7b6a133489ad6c5d18f946fe033170d6a510c9a4ca765beda977f8c326ab9b8038d2e34e8c86ab050c3a1af288a09fec3828ac0d38b6e2036a01e78a12dd1f38d25c116f70f4018fe7c8a6585d744d7a1917c7ccbc0a5466348463583a8748df7458a013ad1975c5b7430f772408343ceafa75c75243c7032c2d37a8a9162b5ef54ac39051b536af5a6ae1501cfbd315e6e5ba85424bb9b8f704f5d61c83eaacb381bd7e1588252ffbb6297fe4423a8b0ed9c949efdf425653675d6c28c8033b6da0258f1d495b8739956fc764eb7c959594c5d608a5573534e6269ec1db3c7e24c195005c325ef7f95a35106f484c3307f76bf435ed8059722b447b294281fe92cd1c0498200770fb6a753badfcaca423b8d84512de993a280088841aac6eef0236c02fe3629c738b64c5edfc188d457f852722548fb0f02b8704f2968fc1888fc639e4b1dc1a9d60cc2d07ebf420fa2c0acf30ed26168a22ba8f1b7639a30f22dc3d0d7363fa485c7d540848af24e7a2459fabf8157dcba0df3903bfc45f34e37821115981fa6d510dd8364e67f519b08abcbb776cb3321e842708fef7d7f2ea60e7cb56f748d4f9bc4f315e07fbd1ab3a1f1c203dc74dfe4961d9c18a09ded1ca356c9658baa02e211f5cae2066f129b2e6647032842b3197f26f8e923fe429205c4437195b1fad0a240759a268166187118ca947022db983910fd7c272788a1bf3f2bf9cc943e4c4cfe73040c4be8f613c25e039dd7e19f05866984d0916deef5955949e618daba0698a68ef6bfcc03472ee2414ee8fdc421b517ca98ce0522e060dc4ad6604bc83f52d6130f719054e6b29072034ab2b26a64e0b4b4ea0ee0d70aeaa7de8d6515bcf960683f283ddd5a15ae0e689343002f730075dfaec0e7ea3666ecff35522026b9da06dcedff900631e03f4892cca6074cb4e50273ea8f6bed889c7384467234402c2a649d4a2b46108f4692f8b11eb482a41cef8ea882e1385f38ea3c8a9a113ef090ae830744709f2be8cdea7b53609071ad145449799be071118884e4b887cb5ff93d80480f123da86a38103125bd9b5ffc4f08e658a060e5debd73d226b44b36a78e0dd266248b8cebd21fc146efa8f28f48521b52ae2cc9a76a25de5a8baca24e8f22c125d31cd1f633e1fa571a260d485857e7612be12d23c64f4b988d575582066b6364bbc44c9618f799eff7017dd0443941a39bdaa9d6bc1f6752afe5a338bc5351b5be2cf996dded6dcf70cce2f75ee9cdeedaef0dc203d31e90e925145f4dd77e6effc74cb2e63a92623dde1d2146b9806f6d802823d02b4d5ae61c09cc258631d22e065b5f082c95c4cf04bd7093ce06659058dc291092eabda597533d2ac904a4c0f22ff82502e8d61ec3cc775d04fe31f25c7e5285c309b46eda8149dc2d35f7fd23c2c79f92287f541229e640dda27a79883bb1b1ab111d3c1e344ef294c41b83ed3f2463921609acb9ed649fc54a55509736d48d3b019e2eed28dd2aa16bb45c13fef52e0325023ebdb72068116f47a50c41e18a9ec9e87e842fbd02327a69d14befce18fd43b01e27c6651734efd3303cbbd069b7825125ca3c8b5ba26d66297b62295a2ea42f1c4fd8b4ff79512184b76a6d17450e72a5b7e06243b84009989eb857c8bc1bb5f0bc173375bc7a86228ef2b11082057e55f02ec23a46bc5cf7ae70d01669d36dc983bab75e2bdfdac419d9f598ca60bdf749e4175b3ed3d85ef5509debc9a3f08322a702464b507a5f675068d022feaa3ea10505fc0a77424b42bc4feade695a58aa7b8324017f4081eea4254975693c01b3896587a178b1494ae48488f88d492e4311a36dd12558e8c678d3554c75b9feb836677d5d53317bacaf4c96360c381414cc747da1962687b10832ed0e262431325c39688e1d6b9b04362a28461a58a5789b442f0d6f579d07dc12f8e702116287340a5859811069876e6d8b819d06bad99b0b65a57cc3597ee4120ebaecbd40f0fe9a9c76619ff2c9f59d03add0fa970a7b6173860a33bd41573281fcf7fc727041d357895e400094267e63e276eb1e1886d3fdfde07f1c7a7c197ae3807a29a9dfe08f0dee7596286b6a9782835ebe024f477ea77e1a9cd6047c64f22a8504f794ab8dd2d24df92c4a9c593229c3235f8c07ca4876af037e81534df944fe61d262bf0ef17dd7cc2b734ab8773c28bf336599a0ade5d932ad22a48683c5d5a178a2299c80b08314a01fb5bc8f0d5ca8075918b6acf6274d3b013f5bc4268ede47bcdfbec089beb97ad6401f4a1534fe618e5cbbdea12f9384992ea4763a76aec09e108cc7195f90820f236ac9e55b0e9557458d0d2ec594e34a8630919333531564939a91cf80a98acaf17e9fda36edbaf5846159d6d6e151deb731beb5d0f20598aaada1bbb408f0ea467069187b4ad7b16c71406b595115e91a3a300409fca9e03b66da819f32db23816a0e4577441a7e13e702b946085bcf8b2f804e68fb230408fa59e5293497f93cb121f615a7dcbdc734018c0b5fdddadb93a69176c828548743ebcb0b92025a1aac796979f9be68ef12e711b8b5100132d2c6d896b705380b39deb13a397bbd74a5c6378d7f86e52544bf63285c25fc3d41686f36fb7e15545788c632c3f2e8854f705f9a58508de26e18a61cefcd5540a56603033875f9122f6dd48a99a9690c5e0d771f18fe6a63e3bcc229451d0bbaf8606b982f4e64668e2cf209b201cb10dc85b6f71200dfd0f4be9d46150623d2df07961d45bc6e07e41915a8ddcce1f9b3b7e1689d590f60c3f2faf1de4669f037e70a9c249e7caf13f675f7884c35ad875f9d02280737a66b5c1536bfb6c2003d64b94e91788ecce029c6d43d11e8996e3ad27f65c7db055a7869550535511ba33c9ebb89ecd8f9aa834e2561efc5cefc2dd6f00401ae8c55bb5e55beec2c5ef44398ff25d9d861f1b947cc6f1d9063eb1d0add8d080574143dc113497118bda339d8d1fc34715637d192c1f8b6100e99f030af409be598ac6ed5d1bc17bc3c1bb25054a33b48de23e195a3ddd5d95a78d836292a291cb7924f9befc266eab3c38956716974314dd487ff96ab306253ffe2cd6786f1fa204490e7916862efb011b8c53afea81662143fefa724e35c71354df4c150f789096c9223e0926a2ed0790e74a38651eecd3f5277ce2afef9fee0f4c8a073238f7981d35a1e5caaa427a1af3d4ec5bdc14a882418982d054d2f919280846b6f32161908822f8dcfc2169b0642e888d4f7dd5c1afe236148834bc962ab6fa266f9b5949c79c4313ce49a202f8e456b748d56ebfc309f82e85d82f6dd877205d976bee7a07ed81794311cc00b2331488e271fc7456f2388a6a42b46c92205f40fd4bc1024b32a9e42af4f9b3eea2f36ca4100fc5100fd18b2a91442060a3f378efe871cd4210861cbf8b93f00c719478a25d3bdfcf75561ad8985203600fb8f7ae65a0cf99badd67b95243cc8dddf81b6ff787807bcf70331cac335ccbdda8516209e02068f51f67a302976e7a5f602fb3e51250d7b822204c561e161e5de3dec1c0c9e98a860dc5895854ac2a2df1668f62f0b55951b3765838230708c48b6aa0672411ce418c97cbd5932a34e119ce810fcf051d3d5077c9b49daf2e07cd25b0c070c7b81f0a6be875a9d215569e323c1ceff7705f037fc78b5f3b3f64a4bf6afacb35c19ae40c4bb1d27e3a3f93c81ff50949c5d38b783a9754ab48cd1960a7f8a77baa1711106db2209ddf505db45438dd9c091157e023df93069e5ea1b950fb5d6cb8932ae7f9b21ce068341638629dfdbebaa0d117a718c1d9c597c239f8ce7ef05c7cbb29e51a81f54d0f82db264230feccc1b80833df125a07a2f02a59bda437c1f2ceaa437a63ea0fd1c0d80bd21dc06c23726a4a0bc03971f7bc28af60fedb4ed0e35ac0baa1fc2737741f10a732ba56d7b3635c7f820f3cdd4bffb25104b9b6e232af6431117148c8392b343d3f470716253218e8695646df1b9f5e0526091f8b9752407def073a8f35e5181b90b706f6dd7dbfded8a5d6b7b1b781a5fad44bb8d3839d937b7ba00c8433efd604b85582530074fb2f1a82e159857d558d1a6c83d24e608258cdf12a8c773423d61ee91b84a34d79280ec1d288147f40278124787bc23af2cab7fe360e164beb9c6a03184d21eae897ec89dd65b99a7893810c1a04c6ef771395662b3fadafb11c06b746244f30eae5a6738cc84db55b377ab562a261650ea1de83f847021b93aa26070a133a4c50833924c395c08214b291a748b95c4f072fef9c08b3b074ef5ac00a578b702a9a1ab0da4f0aa4031ab99028aca517ddbbf1e70c5835280f722b3be2cf51905018b06ca44a01f02da5bd9ef7eb0db64cb552e04e67b4938ac7381d95cfb8793829326a89d7aedb6a2c467b4ccf118f2f7ee3a82d9ae04355df05b77ac6d1a6d30975078c428c8668666e2c37ca2f6ecd51faf7cc9712d335f39953a205a48f94beb944f56c8fbbc24278eec32cc6c2557a06078ca4beb12db3bd003317afeb9bd2bc7ca28cca4cb6c425f16be79f885993d45b961b423d3a144bca8e309234718c0bd73c73f1dcda74a465a3a505fb579bdaafffbeef30702114eaaba2b999fce119ffc68baca077bc952ec6567655d61f5b8b09ace068039ccf2a92f676c44bbf29a6f7dcba64bfd73206c23170e66c5e6f52b5f3f5bd6567ac0f3060ae0ecedcb0b37a0494e74c3c6ecf72255b29a7513d7f51d4a0c4cda4dc3d08f10716d23db0ce7840060463f00b3e3b8956f336b57a286c0763885cbb5cadc753a75d877f737a5756f62ad7dcf65dd17051857e883eca7a027075014dedabbe1bc401512f138e3ff1ce049ee5b6c2af1a9ef8da24b674182659420f502aa866118b36080f914511589339ff4f1a1ec40eff1aa64caead041d0420424a29a3839193cbf0d1025abf40fa9b5e83407affa6b785f9f3ac4e1e5933446087c739ea99073b2dd470167c5514ea999e2ffda2b8bd64001d6402b29869c28a464240a08a3812f043777516c98e60932c1ca2725250055bc12fb8aed07fc0a50b7cb2c28306bbf3abe840edf9866810cd4915b539cd4908684a6e351f561bed7235aec687a655aeaeb731553491ca307d1c90ac5daad7721bb5e1632d088df021fa0edb0b70bf9bdb25d403589038d84b6ac193a341d19a5b468f8a469a3e32fb7a2ff996c9ceddca2d241285590206d5e08d4e16ea5d484db222b05f45365172b23db323fa01a49ee9aeafa719bad6cbf0de16eddc3bded5891bc9bcb61bfb38038cef509a53119e8b1ddba87ceb5f3e0497d210260c852f67a32d09f5eeb3464ec18b303a5db0be3553ff1f3ce01e23f0b34edb1620297b687ecbce2cbea922578a079d83d90564ee82556f8032cb8f7cd5921726d8f2e607de92a393e59cf72870eaef49e11fcc76ccae47292ef09b3989fd86089e9467041dadeb24c2fc3b436e14407927f08d17146379b33c025a6f958d9c3788309ede2dcc270bced6b9809075fc0708469bee27761a1c0441a2a1a00e4be5259662e91398a802dbe68fb659e4c10e58d3d15544fd80f604a614c8456239410167677bb90abc8d0fa19ad65338359abcc9b1e583c0257f8b4fa1c521c5ce562c48f36f48591380dd15bc360f480f2e0e4298d7401fe1302c98d699f6b6c8a69ba856266038a90ab855c7d9a6db320f18288cd1d972b26803a786b3316d2d9633fcd694ce67754d07921edbcdee733759ce9aad051118518509ccba04abd9c0c5507d46ccd97f929b04567649142047f712ffa4446621948f1cc46d57a9e50acf501d08918d62c0a97cb9df0a79e21772f23174f06e9f92ccd122a412b3f307342c3f81b7b2b4200f14457692c39fa20ec87f869d313f9fd8a8a0cdf8fd1935bc83ad350f0a8e2d6afa3eda6c2d84c2c10fdfa87290c8d0ea13ddadd4fd8bf940f903a0a827cad945f9244e7a930e4d4b73bb5cf6aeb4b7d342d63be4aa7c66fc23333e705bc39eee9fe7487011352d4b9141b1fd5914673a5da1f904455d2911bec0573006b6cb1589e8a4288a56d63a0c264ceb194458eb38e4c0b3ae99edb52aa3aecc7d8be4fb22bf429a7668659a04476b9aa1be53b0d53c0cae13c25ad59dc114b4e74d9f1c8e9a64656b8aea66c45ff4d5d775bc748c0e42d10faf25bc476023cc55103eb1c8755ca7961d93209f051867ecb406365402e9d28f74a52a4e2648703f4229edde7eb83d7a46fe72262fc3f65073f5ff255027493a1966592ea3eed7caa9a690fc618c2982c26a30b3c136e0458294628eb060fc0dd6240d61f0a78aa5acecec4be43f1fb9e33312320ed7a04e3e0a2e4c1e80a5309c4aac3631003f662f972e2825234eb1f6555cce342b2801a90af4d7616613ed55e201b97df66abdc8351604c697337d854c9fd78732201242ba25ddd2007aeb6aba7068e2c078ba9e08511b006fa5ae0272d3a2f4d877a7cee46139be4ec5cba11aab5132dc5ff332e7329fe8dd826448fb8f337265d3abeecc362717e9559982ff8e68adaca3c07aa94e94b317ee281e76eaa57ca26b60661264ff1ec0b281c3e24e79f6b8c51db8a431351073bfe736554bb1e9c7e551a80543a59703037dd2787fc4b5ec5c851a3fb89024975bec5312e4f467073ff37f2662addb6adeced1c5467ecb5f004eaa96a186ef581efc405344706e444737fa50ec7ae88286805121c48c03ee57f00ff52b2111ddaed715c14eb81cbb09aae47b42201c4f5ccdaec883a39f8f50095a6c59f5cafef0a09c2078ae06d3b4313fe509e146b38670a2ef3b1d27e699ef680a23240920f83e306cadc1773209f253b39682778e49d5a2fc2afd78d06970f946b91f57ce9319475e0e3da4866c3909b4ccdb2891590f445ac6a0f721f98b6f798683aa250bdfb0b6627ec1a0ab04c32608593ab310766e854d16a1679aa7e54d11fb98f558c48c3543e7063453099ef138638b34a584b6642131f1798fc06478a62f3b808dfdba336447031d6671d0a5f33809b18af6d21c5d815b617a2aeb64813b2d948384883bd9844a13a09c6b4a73a584cbc9e821c2ac377b8c9d0c0a6e25bc40183072f4164747903d138fbb82889cd6ec1d01e3f244dc893058b75ca1185658d82bdd452bbda2c13938cfb87a40e46af7c0282f8e915c8853dfe2ac2d467ddee3aa9f78469e0d3182b05c3761281433a159010d67afc7f1d0f6c31605c2fa0bf15b889c79b01628c1ef0e80e0f3e8817de609528cd5810ccb40470fec8ee331f95cfd26e06d044c1778b339e5b19cc1f6c919a8c8943a5c346b17417c472bc0d829c9a95c03dee96850a8f38fd10b22888803457869cf7477a829758ebad3bd334f468e7aae3b59b5d1bb829a9dbbacc0a3cda9fe81a6089fccb892300ef826fc95e465bf4e381a17f2be20cf465ccb0d5163097aa0bf67f0cf27e326a861893f1038f82e6fc27341bd2d20fc2c352cf81011df95047fbf3baec61efb5581888a3273967e008f8c9065cb5c4b83e29dce98b5ce3522d0b688db31b99e64ced1362ff54fbea01e0349aca8e66c31c57bda596c60d6d9075cf65d87a7775616990261a85fe8e369254a654a9473c06a71254307a79668a7e47a6d79ecbab3e896a3da919200aefd4e6d9d5d407099393b9fbe6ecc5ee11949813f11eb42a98cd42f050f6cb79c33f931ad250473efa19319eded05bbc8041e72304c65cba45c7651b7203dbb4e37b1abc38b90173b86be784fe0550ce88bd67c9914bd87317ecb1e6c85a17554cb3f8b03c4621e022df685871cad3e492395085db32dd632cba53d28b2c6c71581e1683c0b522cff23a959cdc4662f9457a6def7fc2057e2c1854b0ca7cca0b7c454655d8740f222cc1e00ded8e114fa343dd841d47551529506cb01fd9eace13d3c2183a4572b4540289e09b0720966cffd9ff96b0d82d8aa1be38f099c646dcebde00727dd18f861bcc579a2bf16816831e42f286c286a0ec609adc39a9fe844005386df749bd65d11c3058d870e56dd9ce15e0e5df05c4614171a92eee524da5b8dbcb622e195ae05621bc3d97fd0b90e553ebce459a832f4030fe1ca84c24f375057246cda67fb971e77c5e26dff166b9535538fca4649332ba4c53b25412ba4d42e7bd7a40fe567d285f3f481787e41cc878491792ea8c92c06e81af73a35d9f4b51c7fcb0c9848ea2e6f4b31b233e894108fcdbd7a7fd000cd29c2ef29757ef4cd5982a82eaef3801e24a70f667c8a5d2284f85b813eaac30e20962054db9b3982f099231412290cd8c599b071afe33fbfc4df6555503265f5ab5925a03a6c2686772a55ab93d271b36d51350f9c6d92dfde07b04130e023ebe65e63e7f2c81530cbfd0e7d918608142039865c0c481edf45ec7d7208b9b0bdc18c8e2b97a48d331821144cd4d7b3b269ec2c26136c7634721e0b5139a88cf2b8c66f7f1207ae9f640f1014ce713d3631d9a8e4591e905d446d77206d1d3931de418ba440dc771929572e23d1c3b079b362ae44db5071fb84aaef0f287386a88996d022e92901e2498c71083e76a71e0119fe2d5519bebf2c435212dad6e3aa06ee0844a12c5f218e7440e783f25e6af97b75aaceb4027769e400404e4efe339110a1f274654e220d1b27aef06037f6079ce6e31a204bbeb4d582ed6c85c4b3a2b82874df21e9c01e3cfb970c66147f81386520c8dc28f6cc37aa1b481f1e605f0a224a07969ff2749cdf3e15bba94a2a64dbd36f6a5c0646fc47955533ae854648092cb15851959d970d800662b1a66093910cc0a15a241c1c631b54365d7b1a4f611f79813b17ce4d3e5be150ef1babb0d773a11783a89c6fff4812887a358ebabc76c6d89a1b4eaf0bf5d7f14068a520662670fc4e2a3b559c6a4587407cc8be59e40eb11fc7f1fecacc82df38d36c0c51c7b2ff3264b65044e8764690fe6aa14f3ed96ce6abd32fc2cdf4503a10f8fc45e425e304fc1e677511d202fd4eee4fec6e326fbcd1b5ca9dc8b813ffdda63603c0231071013565d486b8e747012cef37ef00d456d6d429ebb6a941a61e7ad37325dcbad6d20af2496e8b6fb597b068dc33e55b5b44a929448b6275075e3333afe51746125e9b7a62e3530f2659f0995cb2bce7c53ec4dd794a057f92bd6e8c874b0e6962c7aa66b55b743c924a1f88c794b66d20fa32a7bc497be07d0fe41f9dae7ca9584795684abe9c099510a631f4114a73786c5ed8ca2c8e77c0feec068727a046c0f0b77c623b34d53372beb390ac85728a3546702085db150f4d8f39720da72a79fdf8b8c8a43c3ad0394ac3ca6271faf35bfef42c2be0315af8a1d45614f0275d7f98514b1075f2574c20697cb298f2b9c1d7465a5107ccfdc5d2483de1386133b41612e024eab176a5f9dffdcdd5a3747612a53254c1b617a4321985b6bea6824a5d0a59a30bf484f59e407a200e0745ff30154ee9345a8467e065b1f457880dee4178cb9f242e7f0f9543e4f5a1cd82fe74eaf9fbdcfb13cff6902eabfad5aca0c820391614278ca6b835d38a77c11f22333812237aa98d52ed451d3c818b405ee20c212ab66332601d5a1fe133c6a6d5db7361fd23905d7de045afacab1205e6afc8b349a6668454b2283e410b886365772236660ed71532f3e5759855cf7a636ebd5127ec9ed1b57e8477ce56ca733c954e0319721e3b543ea8a5bda2225d626980f8c3be595f1ce4a6834aeb512807da2a71c0e8f3169b0d0687985819e2b0ad8c21a1d2a41a40d6350c3a7dcb6be152b8b5b033b84228fccf42cec640b1c8505da0e12eff2f88f2402ccfdd886d0b37173d98a8305cb61dbda57c93866d602ab4ef16825f6ef9a8be3aca3366563c7146d8ec90ca487c7cafc9c0a2ee5b4fb6f71c727c82dedb39b46d1eecec81900b44e1a9bbc6a3fbd3e1921c01b85b490a941231a1bea1ec9f7baf7f851835b7ccf135f62442de74fda417e59ae62a60f84106004c95406c3573e0bc31c20c24f1f3a7f81ac5ab57ef385e0468b8efaad8974632cb98933371bb3ef127c8759e75c7e6c3bac199b9a33bdf90efbce2371820fc1af81f1ac28346258d4e48177d7880d2faedc558c734c16608028b325730b6de3856bc91abc61c45347b7b8080c045de93ba3946db573bdbc2d40b7438709054ff59cf0498d506b81b4609fbc2d7443ca2508beeffd0b2e7b596117f866eaeb13b97bc7d329e5a04d40384fb22fc8890c91d2914f081272b5cc4403059c9a1b265764f8e1dc206e499159a7f05df4ec70cd45a475a65a64814705a6e24f53e52cfbd97dc93083f5befb4a8290943a6a6f015bc1d081a0975bad49b50405facff089c20bfa9475dab4d10432c6563450d45a3076bec6130872943eed85882fd57d3d435f02ab7e7ddb389bd55280e03dc5e3baebd6403c3db7672e1e8332698d9df66e186c047ae6b5272ae2e9291ad1e599d3fd3bf38e0994b7257de609f25b842e5d99c3129d7cc15fa931aa9062fe83ac7d47eca6016e294340043ea01e388261d204e7abe9659418ca558c262e4c3103cf82c4640967e8bccda78309fdc45a9e276e7c27300a687dea56f09b055061aa4dc838613851a88a44c9b4ad1412b9569e7e84554741b1dd7624f06544858ab27c0989174de78bf6fdf3e3ac1b50f3c84c7ae3ab58f4fb85dd9b5833f74cb18517d387d776e314b644b715dd291dd813b95a4c06ec251f9de62bb26a4e1ef9982f9ac4343be1b86b2a606e349f6b3f79aa18ddfa46eb0d0cb1d5aef0cf82815b00d9d8aab7c51f1b8ba76c6de82813e63bcbb004cd81c1dee0a6822fae485a6afe095948892b65620cf909811e4b19691ebc729f08ececbb6c20c68117ad3afffd8a23f5414041e8b580c8e6a223f6166272ae635dedfe036a99103c1ca7ba390b253d9ecb0b2454278d41dadfde0d6abec613c4bc603f5f30d4a64511b84210884ede27a5f12f2ed253d445929c8ed5dbb1bd26ee04b182fe50256553b2fea2da9868a0621bb5d15f9093fd267b1b24b728ec7e871ef1e85042605d4c3306d821deb07a70f4ac0e66c6759b14a527aba057acf6894f73109350daf380a2e8b47f95f5858063e88e114d9ee22fedf02da8a73ae79f0b7a31a34154cccffa0f81ac63265c22f32b0e9577c72df3603d151cadc1fb0050769077e35d3693cc40e7febb9cedc78889300a7273129ae5d61e898b86a45093ce4141515bb93f4b5dd6dc6997609747dbf2d3c0b53a806f2a6efe9d19c30b0aab76c96cebac48f0264372ec9130c2326687a87191db5c66dad774165aeb8b065d11f9ffdaec28e4e35ac8ab5526940a421b6dc717da4eadb7c590fe1d81b2a989c934c80f1fd471267f43db36f0d1e02f46f4a0f3f735cb8fecfc967de55bc1336e3c7b30116eb12019591292f2fadaa143132ce74ee9ba721825558e9a268a396439a88e84dcf5e32106b31618e153a1c83ea2f4badf60dd1c8fd3dd01cafa28a8a0cda5760485f3ad0d0b92f3348aa3179f44cb4e87dd302ba4c745eeae885490c22ed1e6986714c5299dbb916b548ac0a31e179a989319dc82eb270195447348fbd7d630beaf644a8d121c489adaef07ec3fc67ec77627e57d29014f69e0bc88bc37a6fddcb075d663d976c896a7f2c19e1b499deb1a3470ab786e66ce8aba5493fbbada2733f24c94e58dea2ba02b492a391c8652c0e46972156dea40fb2d25b0a20113904c6d6da8b63526dc9801ecf2c67cdfd35ac837a8c41d1a0972ba2e1603156007783c2143d64c9ba8fd3162e2d5a62a013cbbc98c453e1a902c08b568cfb4f6a22d4cef162db5609f88c512ee70753fc9ba7d33646126981c06565db69829802562ca706afd9b83203b5de6bb2be15a96223dfdec2abbeed839c779aa929f61fe2b0adbf158e793efe37a7fe6a305cbc10fbef01a72b27dd7e5be049a10618672760639b1dc52ad01b89ba403348e1a294e9f60a8c51bed08c48ef3b1d09f664727a4d25de8b38387e261439d3848743a9fe1994351fa1a0ec4e3ab691e9a31a398667d6630d03e55e7dfa0e5facfa7b4a16edfbab296a687ad840c1f36ed97c9bb4daa4a1db789562180f21657ab5698438b26a98dd91fdea6ef8e6ff37006e0822da0b14a95f7522e7102464f030e2772152d47d5d80b90bf3143f69807fa157dd508686cf412daf0b7e5ea69c1fdc3549446559e44f37170c43f2932f13e01a971d40b549d7b3dc261373b650888727e5c88fbb4a9493005dddb1c7cc2f9e2c9c606fbe4564d006e9cab9cce9c6ab570006981634d6ccf16cc2380b724f548bddbd992eeefeddac2472dee2e979a052add6e1b6975bd4127d6890d5a51e820c946b761295ae24e861b185f5a018c94e69c8b64905eba3dfe14a9305fa215ae94dbfdf8b5c18f3d9a82ce880db01a44c86e25c4a010a6eb7059f7ae90b2c207142a6b1f78649635bb080eb9efd1739fa67d089ce37f4f4e774db8f929f4dcee8c2eb5d6b5465e7759ada2ebfc5f3a1a5ef10999bd984c555182c81b45d75f5f817df0f94d339f853a00905310f75ce50ff0eea548e161d8173353c023907546448ab19de9099b8e08277177a8245a98322a3d5aa6d9b739263b1d19732ee81c4265a8de860a8b125ac8c66e508f8e9db854d2e148785c443b409786109a0902020f9e1ab6a9fa74a51e537e1f65ad18598e0ff4c98469a74ffaeb293c1afba12f617ce6bf9451acc07a651fe4bfb876e47239112e48b3a1efeb4a0f543a981c7282d63a76a755805ff7ce80befc9a838205577d3b50b6a2cfd7251959e0230d107a72eb0c9e8d3e9ddd771df70cb8434c5364ccc68c206a988895099ed2ce0a465d7f1854bfad4232cc9832236d9edc2a3fdc34121bf0d320904fcbf0793bfb45532ae312beed2fa8260899a7ee9f91c6ce6fae99981f01d03e556027f97b5a593c0e81a07db1c6777fd266e1af6bec63dc59eb3b4a4bef6345f0cbf26a07404998af0e3860030b1c1ef92c8ea08bfb99836d4ca6623ac2c89c6c20f6d988dee45db7c02367baf2b85cef672e7a7b73ebf3cc6e249cbd052fdfdf4e07be1f47f773f3aa3b0a3bd62c9f3e4ceb8ac63a727a740153451692b5ad548a1785ef69dd6bfe19dff096d49692289b79fe41f17b03ad5c4cd7d37159176218dd54ded3ef7ea409c94bb7c4717f730caf871143cfe9017c969f4d535e4b1c1f03a5e30862db47e2fccdca068798c7b8c44b565787dc379eed7d933a28db4505b1d6ee3675412d67b0ba1e30fb75953765341c11855e6aecb3332ddff7b7eb2d620ef1fae469f134abbe86b99e12d88516dc9f88cbb951779cf7e3b3f4c024a600706500ae3482f0c363433438e9ea966c9318f076e0cf9a79c737f52e3aef39a379484845fcbcb72ffcc2f9799f107077a5d6de7a8a5a6b5d2bdf6cb7e3cea66c2a5fbd99f027dd1cbaf24b4433938c3851ea27b8ac3c3c125827e67a2e8732517b30d65d6c58b80b508afdd43940b43264641d63c01b7f7d2b42f0109e6183e5d657e1762216c03a4c8e5339f596c0d967c156b9f2bf1fdf648f9e8aeb4f1adc7518323e0dec0ee66fba3ab8b017d0c02d13681522faa1e655a2438b90632a96e9e0a99dca88130c8d3ace4335b43ec302949803b8cd7cad1ce4c5fa28662327421e27f7d733bfbc9726a091cef6bcf55b240b25c2bbed5b91eb821dca08587feb514f29840f1ed0e8e5786b7866ffa1064c6c167e607e9a83cfaa03ed31bafedbd63b318bc1b586cdbb1ba3bceb8e902172769a679780decf1ce84cb4562975744cf7f23ff33e6006e1884685d4419658fba7df1c596294195c3ba08714e911321c16730c343a26725ab22e003fb47b30400be2e104276c3bee62fd44d8de6a28b2400062ced3d17a988a203347e83520fed7a3c42d0782e38385dfbbc5aa28bf37ab0239519236018efba7302c1384f66ad0a6d9de032ecc6ec3de07e28d0970bd14bb2486980080f804996d4baeb810d0475d74981d46c3ea4038bc6f9afa9665cf22e0f7807ac9544664bfb444c1cc8b9d74fd759b7d646349fbea78bab483991972e1347ae7adf6e5207ff7b3a3ea7aed9b16085a3150cebe188daf54725e2f712c496d280fa1ab12d5781c2e498c7dec0a2c11ec38e0fe00d0ecc01f534703785e8a4ae4aec12d6202a01bc12294f95fa7d2cdcfd86d6c3c186e4a6e1c5a4f3172bdb83609eb3bed076e66e970500ec6134edc071343dba60a360a2e461a7f1cc03ff7dab0f68b2a05ecd950718410afbae68c6335f0ffcb0798fa8fac5e687e63cdfa59e8ffb54ffbdf23665c33993ff936a1fe23ae36d7fea4007649429484943e9226bf4127e844fc170f274b82c70a89c1e2422477adc9b769d2d800022a743c9cc5c3b9c6c58bb719327f89f2abbf22eec2cb6acd1d2343aa3799f3215350243e5a10cf02948e67d05a2120abe3778c7b56b742e74ba7003144b7178ff5e0254e3b1ccf47a1becf78b92e3a87d4468efb209a319e7e19b6d6de2265bf2340e346fe06c8ee3502ce0222216fde923ae2d2dde3ada1aca54c935df5e01b9aac477d1f0e3532e0155c65f26eb31e4b642c7dd41f51b928f54310ce356603dbe41f7deca846d341a20ba05a90e10372174dbefab59a2c24c78cb7f626d00bf7927e85f01a96a6ba612608c679804a92ce4a6d1ed4b865ddb56dba0b9899fabe90ef57a8dee68b0742fe196c05e3c52e008f725dd22ed42e3a6073db4ef5ba8074ad282d3a02fd3da32534648618fe795a58390975602da2035ded81982f4db5802ec6d7ca005232aba2de8a4098df060caf44b58feee5d72f79719761be24048f0bf34f2a7acef6c5cf9c690386594cba1ab9a71dd8a14808873db1f4be2087c4a1a68e0b8a4e8b6277427bba5dabd71cbf3c40d40f27f7d07ae0bc431bb4e08ebb55a2709173091ecaab2243840cdd3ff1536955d6f9d84e4cb884c1371e77b0d2f2237f2c16bcfe087f97b490084d101288f1d698c17336555c8d5990e299efedbc45e6f71a12398ccf9638acb8ae08fd9a30dee2fb9624ab3d6a800e641ff9ca9c7df8109a56d8680df9fa35d5759d73b39a035c092e315c1ba7bc2f4fd5977d85322b384107d30f083730ff9583363ae84a11a2040a68f2eb9957e9e4b63f80a1dceb43f441b0f41bc8ee71388f14b46e088faf5e9b198575746503fb908f8ba5f351a233bd990a528409a344e2cf00d6a0454abf9d54dac045b1400ff60c8ef43cb8d9e64018c4e2a2a2e28970ec00444af9001d32f909839f079dcff06d4f2c7abcde2fbed1f4af9f8be1153bd8bfadc1d3facd30cca7fd1f81522ec27b16c4aba52d71a9042e8d99f425183b2441c93188adc8bdeaf8c1b60fa2f0a53a3932ed0eef6c647bb5ddc795b8b74f1c78b8c757cd8870137327eb91c960ad6ac10043f230d1fbcb3298c3f003961172d8352e1d81caa5f166db00d94968ca105259d2791472f44316b20624ef32403c4f60b84e2f20db905e79fcbfeca66c6661fc61e9f26fcfa4ad00c973e4ae95005882f8ba82d086d6330cfaeb3d2af3590660a59fe6e3ebf391425a4200b68606b72882d1d4e6bf90fd38f7dba3d3499d1b82eef21352f60f72dfc7442fd3e52d97d05c851eb64e69443adf2cbeb8b3201b160b078a1f569c7def7ed7adc3b22982f009c44be9ebc7cc9a23f145a2c607a34396e1919c84d5791b17f8c0283cad2b22ac60639de6361efd4a03447d05195f2d247f26b027453fcec13394f1bc0182a4c3c281efd1a7e8924f1f6162364657310951160d2ead9f12214f4c5c2102aa94b163acda3a49aeea6740bcd17df9273ce3810c404318011ae9fa1c3f595238f28cbbfa557f11ada54791a86ef030259133d8670dbf6319e29c99c25b79c6aaf76bd2482ba4c608a93e9caca020a7751f0322ef75dab1520f3523c837833c9e89cbcf6dd5acd8e9db24746b894d71d259fe84d378de2139b1f27a3817f6cee95275e6a7ae00ccd4f9149f9fb01cd37716e5e87749c935a89aef060af434927144511e5bbb2c94a44e8dab648f92974e1ad05bb56a873d8aecf8514c386044aa80b7b3fc0ad001517143d156bcd75cc076933ba7e135842d8356c0b7cf25cbafeeb8427ab6e6f1d0b37d4f8ed288e7538fdf5db59739fdfd02ebba65ad79c97190bbcb781bb31b478a14881a923198bfcd0be11ac0284b2906da70b14a026f8ac677dd547d41007446e9d023d4d10deb3c8e8607e713f37e6b2dec04a12584c75a68eec204a3e9dead37a979de6d74bb8966a00d82d79825470779939b62028cf3101dc5bf9ee3437a47e91c83dafcf73b3c5ae4e344d3a6be767d7246bdda2b4250434134454211e29a1eeb30bc833f3d914fedd1c83b6a4cec81804e14ab75da4fdbe9b8b11e10fd3a00b76e22772c936603989419a337ad72452bcf5309ab085737544b410eece6efe96563654d209cc9b34c0b02f1be3bede39803bb43d5fe8cfb83ff886116372272dec659ca6f1be2a758d1237a41daba0bbaf5b9421b5a4e24412ab547980343268f91cde747e755b6e83dc1b1315d8f96a05f90ea6f14e4923fec3fd5c59ff1591189946cbc3a1d6fc858cc30d43a0f23e73aeb62c50b5029aa0285694ec58be9a710ca910ef7fc94e1b1187a4287bfc025024e34ab6a111e0d0fe3c3645da55780344ffe8092e7c0b4252ced63a3a20ee8b2a119a8f4860755b674e8fefa0909fe7c74396fdb25df7bf8d60b87e6b7ebf2424cf3a6b201e315310106ac3f67726f9da8fa15c3b3b5f0921daeb7a5d1a4e38979b2a7d751b61070d281c6f3427f07d4c2eab3d8215984290eb8e5b7b06118c2978f17fe988b99120b2def3ee9c8df4faff46989774008da64cd81537f9b28680e5fc56b0f66b99476280cfd7a6aa5d0f140800f4347f3c8d3b87b30e67e1f5872fd5d050774c05d3111ac5d894c6297f710d29cfef458a3558491a057bc9df18bf820c0671edd50e7827dd5d2933d063ed9920773da1764232b36e38342364d566602a4d7c2431010be8d8c5ae70d538d522181c1c8d1069a02ee9adea580e3e753cca083e939e0c8f67395d68789f4bbc16f633b3938576246783fd6d812220f4648e0b077dc37341e1946d82e4d01e0262c458165908f1f1a704191e98378f9046cf9d11c3bf6130c017a440822113cdf8a1b8e85cfae02e9868cfcf6fd40da5a7065417fe4660bdde3885f88d1889ba9379dfcb53c61ca3ce7b5026e75acb1dda486b6696c837f8a86687b2ac383ba4fa833d596f42f2c1f1f5eaf9c9a1fa7d24161db590e8b409d426489171f16db7e4f8e8178e5ed399133ef4a6fe404f0fa1894cd1b37f1e510f1a48466270d8c01f08b66658ec4abd1cfcd7a48c80113f3fb01fed4a49f21858c53061a8d6ae41cb348f4c39e37253489873d7df1ac77753301b232e700142b0e441032644ef0a887d78382b0a87987eaff18f3c520e1b714e8e83d114345728a805eba4a6f5eaa8f0138789af8f5024da5a611ba93e014d202a031e9e02ee79dad46310d85224b2a1669553c8bd87869842da33e59e2eac16cc0a11be1fdb7b6a4a19a4ca3bbe5034f085f1192243f4c32f3c65ebc3d93c3ef81b5ec63857c631b27f97e348ff11db89b210cccefa6a1998882628145e13f14a65c09ff70fb2baddc52c0795398ddf6f5ef3d83df978e8d303b3a0dea0c27660be6cd4e1cb42bd97660dbfdbccd2234ff1ace5f8647816149e1f2640ec8193f83c0caeda29b60b0cd3dbbf0c4d05ffb8fc26a8d40333592d89a3512dad991fd0d931935579e4eaf013f356685f75815629726b02e1178c509c057a19d3113cac2d240fdf078dec407b2782d3fdef0ff412bfcc42e101cf70c934193d2cc68d692d189df601be2a1bda14888a1045818ccf81c4895de81502259911584cb0386e03d6ed075ce6002f6d2911d3a16d00d37f3f0950848553c7534e9cc6185f17a08089a313b93d087f529ad2bf9657ca788daada538c7db2733c1eb91f085614aadf2e172118ed26850685b93953555d261c9f6d09ae63b92cbe22e622d677a54534bfc8efccbb46aefb4812eb24b71580cebd3b4d7580c7d20f660782aae15f019bf176c344032666626cc6be66c322da7b18a83797eee77ba661b0445317715375b116149f64af360fd200ebad036897fde89234346b130cff79cf6f625d5e306267233803d23a198349a50562029d0da08159ed2a479374a43cb005c39640e34e34b9838508c2fe31570f37be984aadd20ccdba5e4078bbbdff1394f7e79431e9ed77963ef74987ed1018613c84a43f242534e42cd57456380023ef1e36d64fd728dd5ceface26b70fc1809756ac11541554bf341633f894f7c5dc1a16932c34817ef824c5da877df5e102697c82a42f5ac1a7fcfb5f39072cfa2dc6b750fbe1d4ddde250d334e3eeef2fcaa76166a43c4acf0639adada0b8e167094ee33ef519ced633c3ef9bb97b15c7b33cb8a2b902995d3b28ecd86db69c2e8d4c23170cef549b64b9fb307360c702e9a8b6f41f6bdd139cbf40864ea30cf1dff55227fb08aecc4de1dc4c32a6be8966bea3ce4be096eef4e45f949ab62dcb6949063a9e7d316d5684a307b67c18187afc1c0315f8d414022e19abc15d5a2b2e2a34449ac025b6580b4e5cdbb4ba1dde450fe97c1cf91baa625354b8df8449f364024d38aa48b0804d7d6c723dee8e7d0e002240a5fe8f04b84d7c413957b3dcf13e237982e75fb829c890ac53c9be006d75506eb336eab4805da660b1053964f4a0ec437c48ed87a1a0c30d98e8675e4205f57a0d2bda6b8a66c9609df64af3de7c95335d1bf096de6371fe819c7aa2b1cb067f4ee910dfc90bd734076867ec1b2adffb9fc6fb99236ae46f8cb06ec11e2ee3fee0928812e08511953f9d8a90fe7d3f04105edddbea229cbeb1110460e8bcd7b14ec1da70700e6c8a8a594c88a6719ac4900d0e83312fae5266e00adf35e9a4edd5870fb424138e2f7a387d85bc914a84523851c04962658d068af9d0f9809f8e696b4fcc03373d987e63463d52f42659153bcce50b010c2fb69d9b044f8596805440789015a215273af98808803a39a1df49b4f3105aebbf3d4dd0850067969a677c387a72bd0cb60a321cecb434fc03c1c0bb01e7aa66fda711075079428708d95ef1d5901ebc4bd630828a1da34997eac90eecfdb278c2299b23be2cdebb7a180ecd1c78fcbdb5d9d030ede7d89c2ce3b830d699e8c94ac9c58a9fa38e737396363f00a97a3f98425ace22e53c1a45d409bea352bda0eb45f671c33b531191071b069fb3ccbcfdd1bf10902d3774c72cdf9ff8d46c92257c225542f88d4f4cc1a286310a25fb2dff87d0cd8b6faed5077ea3423347c56d162423d443095f1911804680c24a1ce929396bb3eedfb4eb2bcd99277a45eb9ec7f40515090c26e313f2e8298b6dc8f0be8f068316d6a558e7affc3675cdc3457b7253695dfe70975f6050d254e690babaeb0141ca36d3ce9782f91a2c273de9753d3a91e795c9e3c6439af8569c3eec33d56b1e95c6517d4dcc4c0d15e6f7c3da57561315f4ae61809e2efcf058519b9d9047780119815af680c11e62337f9cdbf9c15038c3abe667d1883ab08b1bd42fd18ac2d1606ff9f0d2d1f48d6d020fb032997e54a378e34f93c8a2cf89eead1c03810163d5b146d6e87dad9d78735b70990dd51fe480e733006a9034377ca702e9a5e5fd5df9cb9bc79d199fa3378840a93b99e8f1e82605fb8b1c2cbe65aa36883211d3de58350393a7dde159489d6e37a194d5495018fa9c187b201adf658e1d7f1c3b96fbd10f1e0a94d764c4a9805ce9f0b4c161e16889e05397b23b5500b7a2edebacf64915e54727bb892742849973b487ee4f15dc5a149096713c95f517ab95d35fff8961b2c668854d40fa0e337ec26f960d1ec084dcf9184e91f809694e6c67936927e2f9f60587094a3dd03490ab000d9cbdb53665d0c4083d7715e902ff61398b696f53465f2192c4cc2bfbae35d6e29c24fb2ce69a69775b25a3100af7c36c2b11c65e425ae829f9684932eb9cae8dfd31c8ecdec6c08404881b6207c12a27aac91effb08f2505b55e5df40f796df51fc4edbf69e93cbff99c945b2a33a677799d545b8916d393a6e404e4c4995b536f7b31e1682fc75bc729c0d2e271f730dabf4c3d6707f7e356a8c0e8e8002741de7da93a0793a7fe4606156e5ca2646a093668f5535837f1990ae2faefcbb3d519f3f0a5f2a8145fb7d98a87f89b46fb10a3a16cc5869eab80b5914dbe5c339ef9d577b1a650f3dd626f109c4ca4e23fb79fd9df55395c850366ce20d73e454e7ce4cb3fd6e8272139eb4be346ea5c5ca8e1439a0ffb4140ff0b8becf83210bfda35791832a4233c019ea29e45c71674724c6e4f3d3decb02c91cb78bc39db0b15e68e20be075195c5608f96dd440df8e1c97fa3beb0fac0d3a5847476172be5c4d7001e64762cc39cb73fb9b10aadb9c4653ed671d9029e32f769291edf4c368059d507c6d7aba3ce2c8a27f574e1e357cbefbdbb17a6eac2adbc163263adabe86bf5be946b61a919299d49a146723be85a4e5a1859bd6b887e382f009ca7890e0b88f85afd1b54852d5f3f592fa5be7213afbcbae11b7ae1b0a6ffc2fc1cff7b9fec424bda1807dd3fb75a2b6b9c2da85c81e7e7e96846f3ebe610221695fd6b0f1cde6832f60f34190bad790eb68c76016579f8b59584e9eb36480f356141427c8c26cfddb5809c10cab71855c400080b205810980aa6d17bc8a092602ad44dd2bd17e4f8dec7954adc8cfe653dffe7eb100a20f0c6283e6b3cfe469671b08b53ddb39bf08d40a6b0566933bb0ed3a9488e7cd8309ea63c3c44f21f682e46ecfe30cb63636662f5e42face9cba6126d06d9aa755c7ee5295b5065edfe412246c239a70891013857731b85c09e385bdee428b6fd46ea180a964ce8284667c56eac5407c8a6eb8acdecdaa8c8957d19ef6b73ebd7c9a740a1f43e2e40ebbdf2dc6f3c2f3cacbfac1147401eb6ff93ce38657227ff0e0ece3a8dadf6d48ba6d548f50eba393c952bb56c6770734186d081bc937c95937ff01f148db231930404d9ac849076894cea120bb113c43f6512383d3c8913fb19564acee07372f4d640b12b5cdb466fe23fcafb6cba26596e4783333ab6d147e0e34610f0629dc0304956f127eadc55fefaaff31698c02cdc6e595f43a8ce3a32d4ea8768f384243f5266c8496db54053d64e0ae8aff016fa0d42ae9cd2505c7f7f127e917db5c50daa115525782035f18f605d2ce16ed7bf398198ffb412f367ba31d2bd7eef3644d1b202873650a57bbcb5b1b913079c8a363b4024f39105e0f940d5652499b2efe6ca168412daa2b0a78d12ca2fa96b42fe77d86ca8e79f4bc83395792c5e914115c76acc41aaabef63a661079794f3398e7006ce2cbdb86becdec37615657acff6b7ef738ef6d96cf16c9a7812d1d13750c0685c9a9bebcf1ca42a750ecc16d9e5b7099b71e90ab5350d3d7fa329b116d0852dc2793a898849680597df330a2d66f84a92fe217754207b1eebbd1d522480e9f6c43a06203544e598bf58812df48a78c0361c2b0454831c77cbd8745efefca93cdbee22d3d9d0e3a866b1dcce495d128d86c9f7f626148d157889903c6ee4d46b68a5045a3d625b6bcc878a8fcc10c6b2cc3e73080104ca3b9cbda6467316289352b4fad01dd3114cc3939908f40569eccafdd7d78008d80a92b168e91a953ae1b3ee934e7b9c7c26e5c039a5a54a3baf1389c6ac1cf40414b01b0887c0843853b17f00b5ce8cb20b0155c8bc269209775d94e159c41a4e8d17790152990f1a94481467301d9a04215fc14a4e151ad2edc3b27844ff25bcad691279f79777f31868aa8c14e28e4b363b1cb4505b69e73814727690f7f6c16d4b666c3dc30403817a04d928c55ded11e3e38a84d502d6f081a4d6bc619f504c242d832601782bb183bf2f8212a32753f3a7941d67ce20ef1d169d4addde193219d442bc3572eae83214ee956035936808efe6e869ea620c4fac710e1a20a847aa8c0b2a8d3125f8f6be3b5125f769840baf4e28c3b339399dc97e7ef43975e0f670ee0a75c9782adb8227cf38e21605a7a2807f609a028aa4f85f560501ec2838fe9b20e1d1d80bd54ba2d33e3fc80853817c31ce89fab8241087ba692b2645a8073da448d10b91fca3d936ad18929671849620269bf762bd7ac7da155fdf519249d42d5bbd8f4cc28145e40346bd0838dbb1a2afe1ceae2f84695833cda626a74a2b80add071ce8c040c35364984d3b690197f247683480822faad7617849938bf8e8a7767aa53c745270f5006f242917ff3fdde85363509aae78e7ff4f22875faa7ba441b17f9b5c86dd4b59c5f1b7b384a9dca6b93a1924814178c47b499d0495a3994d814cf1ba783776d594caf44e486d32b9a4aadec48eef161d8c38084d8ec71812f42b32a22da9e9c0d46a80b09d36d8c286b46121a77ae93581794eb2e7707acfad235d194143e1387935638dd75326e3d78f2bcdb7c1dc2e6b5e13de4ce5e459c8f96940b7e55b01d63867b97c6b6b84d6fafa2b9fccd26213517c96c185d89ec5133e0e082cf537d64b578c4a3011bc38898826504060766afdc3b1b3fdb9424855e1fa7ae1fe7433aa1aac0911bad07fe73ee5ad7f20fe3185d5c5c9990f37dd7d53afe4d3376c46627c7b130d006f477673696b83bb9c08617b2fedb51c3181afd6b2f76933bf45185d4d5c8f09bc6f334602ef124476423ea8c9b2613420c22eff0bc33bfd7fdbcd7bfae058157a757071c280f401510d4f8f430ec0e1775a13c9fefc3b49d6681585478e936111257b4cb4b61b131ba453ac615bb401393b95534aee4058e8827b120178b59464d11eeeb9220dfcbfc34e52d3c830883e862c078f0c0c2596dfb14f7a97c3a63895fd6b3676ca2a9550310da152f236e307e23ae52192a036ddf51fd44374ff5d85f631f1cff1ee039de2e58f4869a9154b31f6297e06091383b9eccde80ab05ed12cccd238d39cf441a44dd89c9b3826f282808deb15516d799445d26743096bb90050d9b9595794a218c7f072c49d3e7b1dd4a83cbbdeca942a27c2eaeb150bfa035988881cdc864d533a89a27b68078a53840c034eca9b4ed88394c9b8588e08c4aa1b78a0bd1615a7b0d8b3a4d75c507f14f767c086f0e7409c93c0f327b0cc42c1f6789daa1cfce4bf9629e9486cf488fd3c2472519b54411ad5f525afea4c19acd3341fd6a16dfc7330a2b53cca6b21ed0e4c09815363eb655c29183f3152f6fb45bd0a0359990982c1ad75aac826bcc481f22f96af8c0287a4be007cc22908dd37c5f39a3f5b147f32efb3c26801a9cc87dcadb9c7ef31593af0848464cd4a8ac42136ba754e41f368afc87cda3bd0c607aac2444c25968d610e018b5ad43534fede8e89bff0cb08d4469cb9ee5b988f9c91a845c11d332510dc99e7ce261059c693b55cc0b987365674dbce30ae0f9d49dec275e8a464d6d246209b05d58a49badcd02d93732e30635ec75d136643451c6b1a91031e83946563614fd68fb054f1114191a927e0bf7d3cc528c979b78e5fad1fc89e7eb70ffa289c9b39b793b5c7690c0849ad48b065f44327fc403e758ce34b36ac7db4af4f84a1b83c16bc5a6f1fc1e5dcc0b229c4753e3b841e91c6e56f45826111c8e31c01697c197a07c34d883e76838f3f49debe684b971a49971dcf3e40a718c1e8bcb6e0530041ab0458c13e5861b1fbd08115e7c7d68fff2558e13a9f97613116caaa81154ef6a41e09567864c6e0a5f0c1062888bab966a913a96039e3b3482a764a4a938e221581084067a4e271b995615cefbd6db2480548bda113520123a211e62443bbabce8354ec2e2854a5011a545f96dbaaefefed784aea02f3e23d648a4fa7a75cadff7fc4db9a11e85dd9e1c868e551e0535458c2a89ced8d4d2d7f59231db8dc51672e61ca8db4ae0e40d7235c08248919997b9905be327d467eb48c771646b0e04a2e039579e273de0c14db0eed93e3865a9589f8f480a3f1cec90156b3d5265a2bf57decc0bc4b80871dbb09f9d73269b096dc85b8f76fd7fc52b03568f1f34d891b91f8f70d153571190732bc995818b71adcb858ca8be3e6c3670467520cc0f7ddb6bca9d0d2f4ca45ead42e6493ae89abecbc176084cdff4a1fd0e54bfe79e629f886d3a9f7082037b8e5b1210c30950e74bb816e06962a16ed5ad0017c92e5a42888f29896f234875727556313d5b1ab83fd5dc3989d9833bab4b9a34dc2a614b9186adb8d88155007ef22ce979742abeef5f18504fcbce094f45a25209f1890531be7f21b04a9557494c2763564486a3c2a33461e68f3222d40e96dd2040d04370f4be97f9a3e4bb78bf87d8e12ac77a5ff5017bd356165bfc3bc901a1522c134c04af23b6460f1da8c218ef75485d53f45614225991a2d26b80cd52a11115f43352a925702a0390ffcc4a208fecf84d1a9519fe62dad6ccf64ccec5ed539f00b5f833ea0e5688adc95dcfa43940d99929e8316cb7f72d6b713e48e5028391cda0b70d0df1ed5bae60111d3186ba02a4d20d9990eeea0e43152a2c6c8c6dc80cd7ec791c4e9123e10e538cca7efcbbcce121f43296786f4bdeef1d29d213f0c3b729ddaf974f444db2eeaa2fdb8da0ca93244c6743696a70324eeb186c1d76c2487202c79d256a0a60210ab1d1d3472a6e7a644ad0186a207f5611949c05e055107be09f0728fe7932c830421260c8e89a65afec21ac2ff5f34a591534828ae08a2a723570b4bd6c9478d79d5f5d6e25b7e97dd0c475df1bc438a9f12ce97b414eb1d8e11ac58fd1162d66eb3c4808edd66ad5858695f0ed8e7967aa246f868e9b586a1ff06b90ac24f29d98e06cbdc455374d5818e7c6abbf95fbf593ae409df6f64a64687ad103a408b0785353fb89a538ee67b2a59f5b16ba7d561d0b146b4c6258eeefedeb7c17e05e36b326d769706a013992e792970346654dfb0a9855d5294be0700923b8866089082370a032cb610b345c8393e8dad181594e50f2eed3c325704f569503cc2dc3b5a7232362c3114db03c8e737981a90a9b02de86eb385adb5817e60a1bc2d36c3739347ee3dd727367b0eaaca247d83d5c6acc28037bf573bbf553e57d2d1c19bb8ab9a801719132b03805f58a0305859c428b2364df7b391cce6e0bf63c3c61bba9f56288f0026b474bb3ce581da17c8cccb2e7079be5775c7438f61bd8baf92319e21845daba227e56476819b95d76f2c07974df2b63f59643c96aff5df44c5b9bdd2f9095073a18dd1281d0d7461bf357d107fc3a334e987b68147a8eacc2169652b91294c28028fbe3186dc5433f9f39b4aa7e29d62d88a185ecc9cbe84a6bef80fcc201d42e6ce762565b6e5ab72931e44d938cc083970ca89518f874aacff837df968401c720fba0be4ec44bed963b5197f7fb014ef76d65b067562f2547ccc40c185272d27dd7a4221cd16394f57378b93128a9cfdb103af8caa746f3d2b750679297a852ba4bea3443d4fbd660f02c1daac3f064904491c92f094f9b7529c09e0635a2601cff99b12792ba141f3c86712fbb2b9a335acd7b7116d24c744cd39d25e7fcc81b14a3ad8244b8e4cdd64540fbc5c9b197ca6c8f8bebb5e5c6285d41ffc9a9bf77d13ae836e7a4780851fb0c7152d5deca049653001b10417d7db71d5f2df7c28db9fd11dcf93933e62b7e19279c1cebcd61ffdfde988b9e982d1227d0ae9b47e9fedd329d919677357eb708d149a39463b398b204b49363eb59ab151453e414dd63f30174c85e293d24f0ba64c04ca4a354faa714a6fec73a9990f036be4dfe169caa0262054f536554d653205b3fee32d71f7cd74fd2c0a3840554e36707c54aec2e9e91dc3897aee249a64ac81c0816a7f396b842a7e334e84ec7330fe0caf04e819c255bef892b90c6cbb6b1527b77e9ea4a5159eccf54916a30161d49ea4ae3a8c8c3b245a03eda2725be4e9d10b59b8278393bbc5bdbaa26d3c4cb7bc3812b4e847d274036db5838c72bf04b35803a99b622b4c42f1405e35afe4dbfe87b62e36b97791ec561f6110f14fb57402677dc9115ca387704e5bebd1db4547220e0e9e2233e6fcde37f03ffae46ad1a135b4eae688c73827d28115531bdda9e37637afea63abbf41160bc3ec35dba95ec5244a3bb0c421233eea3d57eca514ff3e4949d6788ff689d0ca7ca93a10586da02342feb7d6213dc1b8b4e3cf3a9580735f2dee0d2b73eb2451756b28a8f6fcf49fc44ab90b326f811be76b86e4c951e75c6e1c67e00e1a1af3563b59d4471fe9c2ddda9e75d00dce73e34319a2bb972db290219d14d7bd5c05d8c93cfc93838b8104ea94bf1baa809c7f985d563307631e96a173eb8a72fee6bc3a53ede915a2c41ce890ea5eceaf03ca2b6e165cfdc1fc4737c33ff86284204fda78fa80bcfd0071ecdc281b6b2199db5ae8edecaaab63ecc45e94ce51a424234f9593ca0a4b1482b150183c41faafb9f3586a11333b0fccffa47e61e66151b5122c3e1bf02fda762c383946c0b4121dedaa5ea8d41cd9979a6e30fd354064e5cc838e31da395d032634530ec795fc45fc28b283dfce1c5909ac3a846e6f32b98816588ee2d29e687645de4a3c0d9600acfb05beeb6134d8d968169648c88efc168dac9e27b6f09bb3fc88b1982b1710ce9d16c9444bac7c934a6704e04c9e9e861d0e45dc53aabeba1682cf48bd914d7ffe6a3fa54ff55300dd1f383210fe631896b06d6813be92a4f8d262d26a020678b9c3f488d68bd0bb65dc3d6456d89a7e3b8f329c22c74301ce68cd22decd89658ef9523eb2f2c2ce071972140797820b65944933207f2f4eee4d156dde39f452e36b0e8c207f6f2e94d05ca17b244508972cfb6f4188f8396e720cff8591f8ef31299ad95c913991e2bc89021b9b22344bbd657f5a7746afe51a6d935d016be54ad1feab372d05a2016f6b0b31b38af532d1eb6290b975331206401c9a01d65c90bfb94b98b7459a683132eff46ab8ef32e9e8413e3862b8851a5c8d0a2bca628e82b09ee04bf43bc2f88c8cbb7c2c5d0f38206fa4b4e5c3e5514508cc8f1f6d399bf52f01b9aefa029658b99e00b7d805889541f2f99dcbd9d938ee4ca25033b1e9a53f360498921e3857175feeba8adbf190ab89371f17f6660102c056fbd17ec65b6fbf82b41056df3ca448b0d0e7cdf6a87c4a00ab2a9a46d4cead80d4402ca705d9473b3f253c08f016685a001f6d17f3392015d199a3e0bad10debe94a8a102fc3a1af51f31da7038b53c56e0f2cb990b7f00515c2591e0784aa5cc77315481e04df81b3f546041093b333b97a3f5384674b419d0e01befe69dca8044e0e38f84b8e70e52c1c19dfcda9d0f0487aff43cc81a233d0d03442c64fa56b0ee9ba840df2f7a171c33f77e2720c0c0f9efbb37f1dd4f8d5785b5584b939e0d733898a7362b9720618567297bfe900544ad65657937d9c10fdddf6bb712b46d2c8efbfb40967ba01d5c18d3fd552d8e6d419e33ea96017c1528f3d953e0ef1260e3e00bccc25d439116c9b9b668b7011eec9fd774a24e8e0b48436efcb40b32f82be6bb1f546018f8376f302a3ed1d03f60f1c91130c217be0226a93d973accb576cfcad34526fccd8aefa1ab05a9503f8e5806638fe32e2ca67868b761156207188d6a673f562642f5f8c0bd729abd329f39660c3a113f610242a3880e3d4e7da64505f47f609de7ecf4d02c8238a8906f6af0ad7a7f2fb332872a795723b72d53d57543ccaa6e3530e7bda40e384b96be293c6163507566bce5381fb514fdef96ed16b741c02588c0399448cf877959441444523318e1f9bdd5f36013d9116197d781bceb1ccc8537a21dfdde85b2deee86cdfa79e0b89d130a32c1c523df4c085c2c33f1a6c501f8afd7664fdb9e1212cd6d438527ccec224037cd57ffdd16454636f258ef1de170b45bcfb30b6986b6b65c4b906e111aca090b581d6cba86c764fb54c2b7be5ef1af3f13de4cfcaf2d7e8cd83562aabfd1d90944b3bae6cb2fbe2198dd898ea93d93a4a3f95026a80324a780ed70de2ada5c86a1d19382ee6e1728895ffc138b421a1894817e50d02d3b23f4f5d9602092add60a749e283b36be262d17d8456e1ff169d6d9db3a47012009a8fd629d75bccdca033015da985e92fab278bc9f31a13e4e9caa260dc0525a69643cfed3b1f7deb09d9069757d3cb63b1061c419e6077476b48562f83858300443506d0e313d868d84079def8816bed8440b9f1cef62d7a8cc62cd83f4a6aef79c62eaa693fccc29973ac65d31eee2892b5f266408842976c884f601cad25fd65acbbb99fb7d0f67e6ab821659f8f3c4c8d5955b57e53b23ae65796f96a1b5df54564780288ea6ac3ddd0141b5fa19be7be364c27eb1624995cb0d9b0739e7140b04edecaba4f8855bc9f3af8318d90367a26451b7f5fb389b5ed48e7d0b2bbe9e3537db146d23e278cbd137cac1626e8cffc730abbb8f89cb5b3d88fc6452b9c4074956fb889cdba8bf1c565c12b4763da7ac3f152db8fc306373ee9df499ad4f0eac3e34c3bd3b725f3727c59b8d48afc95555478e32f1a0f62abc491084850acb6e0bc98b326dc97f7b2069130f00593ce49af941de7127f0453064b91a907bdf415a91ddcedce19a69bb30b177a6829151e548c56d341e7905d7bcf38d52bbc76a67868102a52124acc598fcf041dfceebdf6ade68fe46afdfa29a142198918247ca93cd710bc4f05178f03e8d104cf7a1d89dbe54bd7f224cb0c826d255cbf2c77e5da041382faba031ba8b2346586f858b77be356afd3b6d449d8da8da69fa8a98ea3ea48cbd3503ee57d7e21edb41e6011aefced544ba85575a02b666cdba370e99e82f61f25aacc6810f5c8efa1a34685960b80388fd0bf068b5110972b9eef5858317049eb38adfa539712cef31d4eb0d935d3df00799ee7570e574757258e4af79669abfa208d4aa4370bd65441a979b021e618c0c8fb388dbe1002445db8e2e5aff851810df66fb205d2e010a72cf84ddd2a3b8a1249b5c4a1be7a63e8dd83d9e2467c61736495d2822b701779407bdd4ab01fd8d9bd97d32845985db09aca618875ab0bac233a4c1a2225a9f9e0af9eb2f6e53d6c628b2d791d3c0df1db6fbe21786beeb8cdb491ec7a548904e41d96530d31616fe3fc7868a722f22c5d3c6a961c895aa9bde0f5b679f9c0894bcad7183bc887d54b31bb1b542031d31b8259f8edfa1e3f23df965cd856abd199bee9d8cdedd7fa4eca3b0fd8035a9e6bf68a16d195341df2b34eda95a89a1d8afd392e83560690f7be05cdacb1cb064c90dabd02b28a7154dd4f840256ffa9ebb1e5b0b351772a195e72ee9145601678b4acab5e78c2c91e46c0dbb18a59413d156e0a4932cc735ccbce4acfb4a033f712caec899da3b584c11b87309820ba17b40fa7a0da8835bb632130fa541c8ef7976fcb5993324466ac629a41a87867ed75b313fe96e669842c6f39c4c09b4c19f3ad476abc1cd915bb2f9ad659f1df9fc1b7cc4f32c632895eca2f874d81b0c3ac2d2e90937bb117d943cfdf63fef524d8c8da7ef3cd6334465c883f16d0e980759e262a2a3328d5b6abf006236955fd4b8910dda46ae9d376eac86ef4e210adc883c82c0e17a6354a85a83b367db71aaadf51cbd0c297dc2da8e9efa1fbd67aa226cd4a69c150d8c843ddadfa129bcc7ebbc9f9c6d81dcc9d8ed54c22d513f527f23ca4f215402136c9385f74f1361dc3086c4d8c0da537b63217c4660de9ea3e2230f2279d65c6653e02eaff23c8931cfeb8e5fffb1ce55ad6b410abe6f925089a103c04b867a5cf3fb1ad56dfbe9338829f8f9098fe6eeab88c2b87ac7ed654543fbc31280994026fb697c0134043a873ba073f097f835e1ffe5b3f4132c8c0eb3a4a885c2379489331bdd63fd063165dbb600e31770860d378f1d0bf1ff8b4c65cda6492a9bc407d3f7c91a916232672fa070e5a9cdc514d3a74866af2f11f83b898165c2d712681b7617d646d444030ceec0aa90dc695bb88ec1c9c8f1de8757d08c9ca63409b3a3dba33cd768463660c2e2c5855bd4d490eb334a2db335ee93f1912b1ec1a8fd41fb6858197f89996b9f9780065e4b94318c27d296e33f25a63e7b96bb412378656347c98199995732ae7cbc86f489bbfa780e4a8490a208098c0024330b032a23e606c78df1f837107dae01c7c28adf50d0e16e63a6aee804358f8aae14adc63d46a01c09c50f8df4a5a6812db936c11ff1a494785449319d191a45fa3ac90d7430d1117401ab94a569a4effcf519f74331d7da54a1720f7a2729c24062bc5246e8fb9f4eb4348a747120ec0410efe27ff7bf2f476accb111a75cf471296e4f89f44cdafb6a780f53fc1b7a52da161e79999b871fc00c7acd6ff2cf74ea5e34d3a306fa7ecc6af5afe93369e1cf03703e01b88d2cec4513040f08a31e9d6cc4202c3d75bc6967413357feed846cd65da97551c6b931f72e21a69fc50e0300a866934814ba06c3657d221141a1bf2e40761a73a0114bdc77a6260340ec96ca7f15525473bd0564a28e8003dfd10f1055861a01656f766bf5ec069fef58511e6fe799b8432980767088a532ce56e4c9714ad0c5d019a24ca60c2cad218bf7dcb54b15ecf23c8a2ec42f99da6c106baf600ee4d3c9da8345bdd8218e2baf28c5c3df99c1d0a953123c0f16ab8151a146c4a4f20e39c30f50e8ba34ac5d7e6a7b4ed84975ab5c02aa4cd9b0ac73178a92439b303f527f3a912512662708e8c73cc715fadb8fd92c81e3bde1de5348e9475fb02c3f70842d486df3a259fa6f423b16e375492fab6f816c4890815c7d6b84ec57885c271284437fc6aa6322aec978d0b9c476c7f3cadee1c0ab4010e81a7d37d045b1d4bf872174c8740962c798461fcecb63b41dae15406e36df46eca41132eca42393dc782b03bc63573e171cbf5fe9f126cb409fd8f05ce68322a52334e5f65144581d1f397fff56c4c901437fdd77f0a5417bc61038d3e30e37f29fea030f888958f12df1f51dd57f711631c50d140ba45e0673a002244c00d8287c8a0137ab94062197698d66daff4b1b96c71cc181c39c64e88c3033d2f167c39a60160271fe64c0a5241dff2b85810d8add7e5e9a46a34e501165243169781b1ae68370fcfe6117471fa0125010340f3c4ba6362e35512f4e126c6707e7af3a74d00ee7acea5e3e8bbbb0eeb25037a4afe9bfe9155af870bcca4534eb12b261d4db15c97bb3bf5b6adc087b1dea056a757d61be83a5163db3aee9a7b28160b1b108f8c50227c4e97c3f83964bf52cb512ddf268b99cc5bee096262435c9aeb84f4c8e44ce0da6ffb7387b8e8805b93543e290a23e18b4b4384cd2439753536ee62d5a2abe62ec0d0c0a4e23698c9002d6d6f2d7a89964a2f3b128f61463a61207f9c24e9efe87d58db84976d236e094a4e4e7332ee668f5248d7fe322d66dce600bec454aa7b8af972b453cea8dda0f1c640ca686be207db72a8bd49d9461d30e2e226ea09bfa55c941e8c800e8c6abc87f5caa1273d61c1dd7f67327f9800fa3181b67327543604711968e4874d6a7980e97c3fd6ba6afad9ebc1a399eb5e25c9265d71f34022a4f647651e3d1081fada8d2c5798d4c29eb3cda7525d223b19d405d8fb1fba22bef8f2ffaa32d104a796404c8d03b4c8079e9d4c832e3310a83a1d68369169c468d36666e68e6804b1594718a0c741765b73a106bf37f810702ab6ddd69bec9be2a0fa13d31afb52a6143230d22eb92e8bbcc704c5b7648192c711e0fa9c26c4ed1676f0a6db3f0b2fe67769b27b3143d2de4412324eaaeaf5715258e83b0e7efe841947dab58b7ad388820d41d424d7e20bf56de23e3f4fd1a385d879e4a44424dd0e5ae58849a6da0da3f4e34880e3d248147c6d0e71258265cfb7c76f0b6a3bf44e578cde4dde223f902aaa43c5d170459a7ee8be400f3e2b53a2cf124ac337f8a37dcd9191ef91c56bdbddb172b4ae14af5549fe410541be8f9cfb6e1f2aec4c70775e65c46f670adbf15956771e40243b3e07cf8ce6e4817534ab148d0094973ffb43474b41d472522a5c0f81c7c50b0dd2fa2d24543c1ab750bec4353da50c2836223382fce9c2ee439c1d723265d8f4042b86fe454bb2393479e04bcc3abd9c463a0fa8cb9fbe2aa0c3cf6468f52320759e8e8ad91d4f91f21883d39f42a4cb2f8138d448af460ecf387614c6d7c92898d276a10e7900035b760432ba27f402c63e740df99709271a40033e6e455485e793e4e086e650f8318b06fecd169ab6f2b17cc613355c519990bf24a3f115a394f2ad4b73da4bd1066fe1e5935e3410c3610ca1bc888ad5114dfd13c94c71c135820f4812eba7906dbe83053cce60c3d88fa161e84a14fdf1ea28b7d753618b6e2588792ea9167d78038fc3f1593e06b32da71363fb3f7004fda0269abccbede9882e98bf5179e36790aabc164e8c4eb60286260186c33bc449d3a5e6d0f02e0306b6de6ae60b2881a32f8d60e18f0cb1de1569c16321c62e461de2cdb9348f27ede112ca8838250c497f2f4f768c1f553ec4114d7bc58639bf204c018dc805f4135896faaf49095f03d0fc4e01ccbe831a453b010bf60f6c29a5919059e3814adc78328640be0d96306f027b7999a126fd0111d1459dd2fb0c360cbd0e43a95d26c68cbb81b1a70983a470398a8e4de773dee304f3979b3cb95e184b1e82518d5b5990dc5b512f57f482ca87ca11e66f1ad3b4d28c8243181bf551e8ce1c4c96762b6b929faada6e51cab9afe3b7e635c1d8e11ad28cbc1a4249aee1d024171b71e6a91c59f8e0e3953f6943e576d2eebc885d0c6731e9864789a33a1e47b0ef4126c0ae6bdf7c8db31acfc7f5e6a38ca5790bc1ce239552d36a4f50fd80c3f99d2036782a9b2c3916ab032e28fad4a7aea03c5f9425ba9306269ee490984fa728ba27446def8cfbf1588ebeecb6b3235fa5cf9cf75388876f8954c205ebda7c5b5c052f0504eb4730b6f14d6e70bf49ccc086dbb2e8211c31f82940f3573a017f9a2c081e42120a9fe5e1fef75abf5a81f53ad2da675117e9ff32205d13e2fa70fc1837f252dc4757fcf67366f51e56b08ec80a8c5f6a64fc4d0266f8960d969e898bbcffc25842439b1cfa1636c89d65a6d047df2d9aec4b953ba956ffe4cf9cf7d961e93283b2bcbc726d471d80be8e1ebaedd6727ac6005ac7e81a74327e66e83363d29ef9f8b5c958107a36cd4c9a072745374300eddd48d2f468277e3893bd280f612ee14506157a5a8d78bbd8c2f748506e1e83901ade062ad1e574b92ea4c7d5063509868bf5b0da8cd4297ec04c70832052e611caaa9b8a8711df56c5d157696bddc3999c5dd686c6df447a3f076cd47f756af33f3a18fa90067b5f07f0f19b40623788c216fee9cfea9b869c0bda7aa0332872f157245de9319d4ef89702875e75eac13b9afb348db6276f8a10166f5340cf957d335c089793cf8107903e1025ccaf1920a31f9d797c3e2c92ec28646bbeb052127f38187cc580033b41b48d079ae2d82708a904f90a44874f6f8d1fba39045e1f0e2c62441f9d0a950608e25f8d2d3bb6127c7a820691a11c74bc8c2ae315ca44de293b515d473ab06144c5874c13c430205f501536762170636410053619148710f9d368250ec107bed3204878723c51674a17c2460d0388a46199a0e1d222472f48c3065a82101c9d720c98d1498bdc0940877c5e2e5965caa420d2328805d984cb86493a54e828b2a549580995811f6d168870c2b5183df4280a10a2317916088638537442478090332a68e7091ebe390f8ec2535781a5e191113fd8390f600346ec4042b402f5a0119ab9eb43110f3e280cd2f972058a91811b2b127d4992c41031070fac214c13024e1e4c8afb4a380aace4e242a5c0163f80507020e140826492352489a6c86e880a24297502e4661f5e298380264b1326823d000ee5c6ceea1a68880b6441f561880a2a7b21aaca9c117b66fc9736254d7658460e12158b4b309d00948426dd5b30970021182a45833f0813b4c2fecc38318404ca6f5026342836020b07e0e032c6479e0e4193ac08b05cb63c411924f188e4510089aa32037e8299053d74f87190c1015009bc3f424a1b8c121da619b121e78a041b81964ea25d34cd3984c387271d0250419ac10894c972a00aeec1cc24c4eb14343820cdedab0702ad06666c06ec01f20ae1e2cdd9e3216205c63d803e520e3c7098bc59f606a1393558e1d3c9cb912862369865b535da530a4d089a0a615db6f34f1a01d8ef860538582099bd80a3f44346043371d21c12d1803236532aa85c458da8705277c7065b0e397204d60815c01c4e0a78baa354240f9a252d8ab4e1a323106f709af8d7a1f0b08a0b8fa348121c556131c7e96f47a9917e68f93485f850d1685194d628d29d0d531e55502b789a5a701305e5c1162d4677a23c09c1a0b9a511c29643114dca0cad1c896a105bb20981da13504b58bab5442a24b1a00b63000a9b3f8c5346f216ae1a649bc4845d98a3a34d6c48ba8941d3ab45dd224872e23439b2f685ae4db300e48ad4171b531d31fcf844e85b53e9c3609f2043162e0ea90b9a1aac645419a15c9a8e62e4c68da00fe58bb989c0118b136b36d438f0480e0c8c247a7d285075e19375aa8c34d92775dc03552128290901a092ad8f81b9d12050724c310eb87b6b54f561ace41568852606e08c986c2fce8084518522922e469cb092e4f6b48083d099e8c0aad2154f849e245e0031818b9a28703af038a40453983e404de7006e7148330056959a9c80c22b118f4ab049da12186a71be513766c8c3d42180416b2c1cb6a43b8892992aa0f4aca65a24c17181a3a4890d33c9839ba36c8d8c2763767c9a6128c69a8049ee33b5e4f44b9b8467819367bc88ea04022186e6ea1840c4c8ce92164158f26031c474c74f12085d2a909972f2ba90439b8520b1e4395817816b537283960b4242438d141e6e646c32839287c31125290abf44b8241f309c782097d7d51647adc995990c1635431d5b030b6b56cec418b88354e28cc85a6532c78513d304c1118e3e0d405c80486392468007248a3019c291e3e7881f3b2326ace25c95d243a4d1923f31c3b1193d0575c110d3a1d1e193f3cd0007532347ee9072d3047977a794a80b83b85499739cd364d31b2ac0116ffc6021149448ce445a3cb0420899178670dc70a22343888b2d01a1acbef0b1d2e7542135017f55084f16d9acfc1920d4028e0109ca812e1c0362dc1194b365518c574b16b237b0ae4943e09ce982818b151947402cd610c9788249c2e62e000711787ba2f864ad9053e7c6469ea311a968344a483529521d125b4ef4cc96b87802060d4e9b01502332102c28b9788a24fa08524de4004a511d0f25374c7d29c022edd3963f4b12ad9963186aca091b81a74ef199a9c38499a08806ace7fa546c73ccb25c31d1cb46a2e6b30d20b81eec19f3d620a22849866259dc9a32860a87485881b675556182537a4a4a49c98d13ee27c54c451e810d7273a781829f4eeb23410e7c004056e15a2334fd7003c6a39a511cf6d8289923c8e209b689eb04a61d44242fc2eac5a69d58f0c7b7298b26003e703c20f507c78a129ee6147271e120a61d88dbf31247f16a4af51284c18c2c1d0c477550e3276fc6beca5285a8b18fc68732bde78c4b830120fc820036d11107f1ab2b159d17155694206576001429312bbe4494005446e90009108751a32ebf844593232838285a73672ccfa11aeb823aa0ce09020b63be607adc4300e95b9af274644a44336b60a3b4b46bb30276d425698b03bd32f1123cae344cea74b9f18709d9290b8c51c004f23356a541c9d23a7646ee049935809348cc1903156342068c25589ec829b2d7a18728296d14c1719a68e10232931cc4605017018139e0b22c086184534251ce11420d2a645c0c323d2aa0549464a152a62bbb6898b608483789cef8f1b0d7a34e9e20af06080a318764141cf088a061831828a81cb35269a949d8e4068b541a042b3565ca28e0a02160ae858fb9d220750a1516d44bf1201d822404fa127587c4ca204570e093489c25738424aab4e7cc2227edcda32b30236ba628fca4d41b058a2470124e5a7c4d7880d28656a14080c32d3760c0698bc1e5ce8d36447af00462e858880b09b9cf48aeda931e2b08343e29c117c2d1bb868da8b5520a804811bca822d2e00d1001919c07af326e7a47e2c31409ec10081565012207157d31aa06d01987667dc64c286ef06120459cbf2ea7ab04ee90431d055100ad3164f1c805b603cf1050a51c3b1449f804e643446224350c22a550e469ce049f232506700dc91bb4f2d629169aa26a68e104c3125473e4545ea6335f486915c4a5658bccb1e62c8e05ddd23e2a1170405d1a99b97b15264bee7ee487fb54b01a5367bfb89af71753a762d44b9cdcab2c1df12946a45e49c0a43c4a93dc0fd30e2f806d114912a42d081790143ff4506528dcdf1f55666b5d78c93aa57f676dd52347ee558d182d72af5294c8bdaa0c221e5e8551b3766e3a5305019f766a6f6642f25f9f893f4679eee25e35888f3cc3fdfd09aa0a141584ba570192fbeeccd579fa8c4ad1aa4fb3aa3f32192fae1f87fb2cd9c278ca858fb7c9c2783a35c3c1e78e63843b95ea5ea5274f1ef7b9eeef4f504d4af7e78ca56ff29f746d76d13797e65e85c7ab5369a0741aad03ec8bffd0a474b39af42d309ed2cd80f1db5f3ca749dffc9ed2b6588ee3f78cab73542d77ec80e1a2e85ed5a50e35d48c37de96007c12ca6cc912ee5574dcbd6a4e151a390ef8b48bd6c9b37b559c2a38680454a19142a36c7f7f826fee556fa8556eaadab0a95a43278576a95293c6bd2a114d61551a438ea1b7150385fb0c1847dde599c2d9cfbdcaccbab685f388aa325564e26a2cbb578df1c279c45f5c25a63a8d03ddabc2b87b95dc3715fc8b73af02f3173ba57f21e6679fb4443cb755cddcabbe004aa3d1988f6e9e4add64b63e2f7e7d51a99b7b5597e660dfdedcf617f73dfb64411173da8a108567ee555caab6546571bfa954adf04c8ece55ea6c424efa943ea526dcabb46499a7523e6a8c16ed933eab7d67c9c1b1b12a2c56dcabae58d92aac5465658e8e3a4fe735d6aeb54bdab9764abba5dd6b07db8d8db583546adb2607c7c6dae4e0d8583b981c2c9342a36cd4b2e8dc4618bb4da5d13ec0b2b81acb6532dbd8d8740e14a4024aa3b30c288d52a954f9afcf4419d9732e210895f1e0ea348e2b94a567d5691c28b315cad2b3b2b23649d626ebaad4a814cd6b032c4806b7ac202c5838fbb94b41b9af4d0a960d1ad812bce35854fc0882116ce25e85634b908d7bd50d1beeae77159d98a097cbd1f92f7e5efd0e6065f50034886d5509bfad0a9051a38ac68cff4085f3576c0d13d44a9ec507be743ae75e2523461c6ddf9795db9683ecb6b3122a27671976c64a770e93ab546934accf2fd039b7a80a0646d9a07072b4598df609024e67ba3a1656d8b36a9f733d73f733193b660456caa4e9d0b369b3159866f5b725ade4ee01607cd0215dddaf04a46d45cba48aa601a680f302ca93263a0c0da765d53e3f9bced1689f5787d60bcc2b3affad5e1e24784567a5df10ea54ec390f82f2200a8c97b30beeae6488900f5b2bfe567c5fcc8716858b2b7c96399f74ce67350e2d3a48150c7c96399f743309b2e75cc20d6fc902e6e1b9ee527016a452fb96ace05e1f356fae4a0559f031d8da7351c8b7f60ef9d65e2277e72a5d09577757ab9a56b1d01056032abb85f0008de3d4a9d81654ceccbd2ad0bd4a1a75399ea7ee527086f376302f2f902a9b9233c98930788d6df0ca83ced30b742da45458f03997a0f6059a51fb02e3bd682060df73115d88b2ade707afce73b234ea54e3eece0bfe67b6623579d4ad58cb9d060c087a14dc3dc5ec4e9d8a51ab7195ea24c956528dce3e7250e7e9176ea9c822dd49e4c0b93a7bc974d2b79fa5d00d5757575703a2f1596175da27cbb3d9161e963fd94a11e75b28eca66cdd11027b71f7f5a52472e247ded609423b1c554f30bb2ac22d1389ee7d521305b64cf43921752af606b56212239a62c3060f651ebc3010ca7d17aad0b96d6caccab7d7273785ab42e7362f2f2faf688a175e84725f1e00e615000156a5f07820d3e0159dfff29c65565c5b738d3ecbd2e85ce296dc2997f3fb8be7b9791a27fb168934df2201e75b24ea7c8b44ec5b24007d8b44a16f91a851c0a56f735f9afc2728455373afb283ab8ae5a8557aee5ee55e95e7ee55ee552aeea2d2bec2b84dfae65e85e7ee555529aadcab5054ddb97b4c959fcd261a955a0d1414f49aa7f1dc27c77cd4e8ec423af5aa0a3abdbcfee2783ac7d3a652a9542a954ad7755dd7755dd74d2693c96432994c26dbb66ddbb66ddb9665599665599665d7766dd7766dd7766dd7766dd7766d994c2693c96432994c188661188661187edff77ddff77ddfe572b95c2e97cbe552a9542a954aa552a9745dd7755dd775dd6432994c2693c964b26ddbb66ddbb66d599665599665d7755dd7755dd7956532994c2693c9643261188661188661f87ddff77ddff77d97cbe572b95c2e974ba552a9542a954aa5d2755dd7755dd77593c96432994c2693c9b66ddbb66ddbb665599665599665579609bf4ba59b6c59374036f7135b693da63387675f955d9509f7aa12ee5575746969ee5573ee5572ee55247c2bcd018616d2c238f0afcf1947d9fabcf8aad33ea757ec15a58a39e33f2f6a2c2677288c6e7500e45b1d1af956874aee7ea0eacd0daf79c7369797d64c83437b6be6c9c1c9c935f39a6d7569706e7976666e63636eb06019e05c65237bce25a87bd2bf39dab7176da281e8c370e15dca5ff539cdf2c1ce9947a5c681551a3254a371b40fb06ffaa4f6c95e8082d119901ab7f092aeed45fbaa53a9d3cc4b6cf69a963572af4ab0e5ee52b6f6a27df21c576335f72aa496fb549ef15ce5d2f510a64dc654f93953abd23f6a55fa37e72e0523e50506c663634c56a96ca7543208ee3e954ea74d33e974be2b3c8b9a99a5fdfdc12d5b27c0c95813f7b1b1bc406a0700eebec50cd5e7845b368f3a9d03ff66bea4202c08b7ac20d3d0bd8a40d580bfe9b4f994fefd992d95ed9abb149ccece2df8870bba6f4f99b626afba72af125015c0bde2251b8d5f1cf354b0ea84c6f39f57da5bb3496786c6a6c64627d7444b936ba679490b673fafa7f4cf5d16ce2396585bca2c46a85faaaefc3e347fd4cd69b192b4034f97158fde16fd10c86120d28e44624188ec8502ab0b8c52890d9b65d6825412b4260bd6a10d611829a4a25224be03070e1d2d04aafc6c07f82d00c06b0f7cd67264080000b9ced569240033345d404055dd0670b51260e369fc87e61557a3f11c810201aaea3600ad758b9d4a03a752b40166687621e6bcfee66a942dabc0aeaadb00b8d62e56c955c56d42dde01715e47b46fbbc602f69d56b1d0364cb1bb1e6b2090ae8edd8a9122343047ec0742b1962abe5b4d52ebabb5e17f76a99fcd0d3db8bb6d256cfcc0ecb2929e68aad6184331f6ba40a4095957b55957bd5aed2eefbd17da772dfa84eb9fbaa440a8db2fdfd09aafdfd09aad985987bb4a84a942db14661847b374c39752a954609673ec299cf6b2fda179de569bc66626eaec66952bac0ba672015103a0716d2399f55996d7e068a8131fda8b1db93da07e8478ddda67374af8f3a05a456e53e593a0d1ca4f6399f55c2393a57e334b96f2b8c900d13e1cecf40aaf35925dca36e851170776aebc55715542b9c4af7e66afc8c0e52b736f04952d3d80ae5d8f9acced538cdf9ac3a9fd50d39dc9dea4607a983d43ef7ed8869439bbbb3ce27dd2e456a94a850a03e79eac46993a69dc9abe2aa324472f7dd8809cc7399f24c10335451a7625b1812f9df9f60b40fc31b0c62dcbdc6a27d3e1e3144e7bfc2699c37ed033020d5853b5b17fef6a56d69539a84e08081020406ec2b65ba421f3d835e510a9800a74ec5e4789e32c55631362c2aa5aec89be5e90cc97d3f72df8e1acd70b4f8c910d39d2833babc8840d6a59a9c317847521e0a4330cc381b2311b56148cd0c03191a44aa12324406213a480efc4c0fc998b2054e0408148f64002a1ab1c58b6c196f24b21d2ed844c676808aa9645498187c8cdc4981e1475b8c242d008491e061408ce7a11f9c2ec40e3138c475810ee9440a4629032512a9a0ecc0a033808be29c0c308c210278f8f5a173847102cb991a970e5130be0cf93c710120ef05273f02624cc16d2fd4a431baa10188da0bc33d49f3f000e8455c1f4cca3144442fba2635258c3aa1c28524c3aa061b42cc7021e65dc2a0f037bb78e1a3871eec45ea42edaa038d30133f5c64453905edd125b030916b67400b176e85578640e97078c743e1d4401b872c51d842b247c819d5a48dc2000dce1653887a42403094812254ec113a29b283cc96d50ae19e96310c749f1209db4c772460c929d522151b0897d293142dee8c2999d328234c8b22cead266dda0a2df2365d98a91b812d2ef419f4c1a1c9dde027431aa6fc889160f04d1c462b5cc46b0679ec49d274e78dc320dd1c04bb28653f06bdce6478694bc5287869a37ab495a79360993ea9de5a08802348e3ca4ce010704f500415418ce2c010104c00a76806f50d59d5510131ad9cc767954b881a97fe2479521504c4a7aeea82ab6a26c84291a83b5b55029b1c203171250795d10e129742b92aaa94466726d8a9faa1fe2d695335a5ab415d3e5afda018dc43d56f40a830f2084cf7887881c991e532458ce2e16104148fa9f34d002968a2044dc3524d9940e934c762542a390a180049c4624eeb2e724108128b2067d4a02272c28f85dd2107d66cab040b0a59c0e2b8a2acc04d542913d788a5c034182a2498b3852e108717a6b4263bd002e3baeafcc460f6096430870d8d3b296648233541b20750c1a2344c8c268b2f2d0f52174e016600dc30226d3618cbe0d764508af5c888263631364411554988e118338a5a694209d1548d19511622e4189dda5312edb00542326c2a240a408218393de41400fc532688b2039f40c026740ad61c429a0408d5c205cf191c26c05642893126540a7fa8348521a412df3f3bae25108aa514ff0e87a85902dbf9f38757066f4e900236fe2ccc91b124e041e9f749eb51219533f8b959b108d50c005bfc76d4cd605bbf357e732d9280ba5062f58b001627c7154e6d2b2a617ad810c880156436b9616f15012b6648f25a3b0180ba820d0783402efc84acd8eafb7276a74481be46743ef8a2a431f66df1626460cf58a7be2a252e42849c777d20121b75e9f206a66f409c3d175e4488e11bb46768aacda03b3e29119e3853a5adf1f9cd795e546306f89498046cc8638410dfef086442400be4dee201089db07c61da43c262cc5de3031e7b31a82060288810b4b7414d0c49566939a0a24c215e5ae10486ca7132d2b4264c292890158f91f26a08819ad8b178660c9f02d0098118490921e061d614c1cd9d0cc2784e643027604b559a6f84c08105ca4ee3fc5628d01b1d756be6f22258084d089a0ca9cda846cb024b190c921350368d4ac885cd2db9950ca19cf6a2553fc95194b136284761e083f234d4a884dd198854cf2a6408d598ecb5cf402145fcc018c6798ecd906b4a9ad2e769d566792f55b2e2f4136a2e6f93170e279309101e25b920e75c4692121caec5c9e2e170e1c775c4a6ee041a45c62d102b8ea16d10928231267247d9e6430cc238059587981e53a0e20a720481494fcdd1f3a41cc4710251ebb5996062ec4227277a3adeba88e9b020464fce4a091f50e440e93d1864a2a9074da63c4a8a18f95de575ca23b340d2a6313216f264fca0c28587699ab71668a330121b147948204ab138246054305aab5340c9c40c155a006c214ca3b5a8a26a5517a68788510588aab42f37185c545c450104f028d6c21344c3046ca64b102f0ab44b18de173a782b3c345a3b120910cf4de50660519c093cd713086804b530451e465d97b4e450a43832636d8d19221829e236ca113e6c6b0a0d38dc0990b2b8a110b553543ae3e501459c2622e6e84c4244f1230c2d4d87013f5094e8d1c408870e54504c101003cdee948dbbc6434e2b14614feed02468f0c2f8927167a3c55aa9f5417be7e6034b280677e0ee164c69a4b315e5c3894795daf46d61b078e28b1907048342bc9c2074c1ac909a763a8196e212a6b00b714e1c309545b866a6b42b74e790621437831d95408f601a18d6d849d3f481f2b33a76cc58c21057fad955b995a126f13699f0538621941c313f4c2841856093934a8d4cc879a8151664a2616295122a11f9052de1d884cd89c4310f25ea885843281a49558288800a500061e92981028487c51b6f5a25c016537950163a50d729508fac01f9a16e0d252e810821e4a9cb21b729911ed7943a12883e2920a356a9ce4111083d125950e92625e28a960295826e0c9b1730237362d0c538fb3245071431ba107c3ecc30dbc0e8b4d45ce1d9098430a74894a0b31099e71c16527895bca4809ba3862dc864224681b9b22f3fbe788abdcc09f041e5cd85b72fb940532ccd690a3d9083e287e00ac3093b727d13a6806abe30e5dc0641dc8875b223e70a08e8b18bf347020f1b465ce2285d903082c8a0849be10e093d42475973ea00496440f167acea5ec4854ab08454559a330ecea20445964e1ac4f538b5890427537971758860882c70981127a189e691e11b257098a6bcf133e81105dc99068b58b2942880ab0186b1f1c056165c5b9b0f245b1a33700aa84c11146484c8db230735462942c3f2d6c5b1efc7116681b716626a1815d9f1f076e6e30e974850e437035daa6a6349a0b8116a83e1cf1d170cdca6a05a7380692a845bd449079210525337186e5bfa502a94db9ef6e980c644dafa3c10b412d8a4415b92482a1e7c0a93a0ed69295224399fd2da38249212ad4a041c512a865b27277a2646d091cbee22b921470c89631fc579d032e28e81a5150c4e998df872a463c7161e3e223895f9fb53142d13a1464d88ad233156118631a04145a8e08820d1e0401d19f212110d54c81090674c089b24496c142977a86c6270582053264a0a361800ac34d22515d9d4fcd070424bf1842d6b93e2893e31dad6128d6114c575a770ed4a28423a8da2c46b533cc7b78b01a5350f4f0c9de6a4b0b216c0860e504d443c0801884fa00c0aa48809f16441229a89be0621f8b44af529f482142279164fedb8d9504be587c9a54230b6da9d2b9026a8e169a25624d18c19abe802b53c209b760808f3a586a1ec0433ca8f4b4d506c23c749842434e158a0c42182568d260f432c22a5c2a19b745e40a169b7b2e9e523c3860b9a48da2542b442434ecaa49909d3761c8910954683531f006e830ed3467445627cb4292e2d01980784667e8040736417498f880747e3120770efed8ea30dba260c0b457134b339856f231a8e56c02f5405d7fd8c910d1d0843dccfa6ac4d2481703f03ec3232e55d1bace4ee673a4a9ed79e3d3322211c4f2d3f66623868905a316787101027407c7de0b4640561c1386e5060ab8b93bbeb6d75f9d1f3aa46fba45d6fb6bacae8e979b97b00dfeaaae243dc0be3e934deea82e13e0bfef175c5b96f7581989f5b5dae07c6805b5d170005a3f356d7d61683532de6e8168348bec560d1dd9dc2748b81892d2e385c1f9c3a67b8b5a0d0d6022e3fb0b5e0cf3d0ee450584564885aa50aca7901eecd542fbe6a1ce8f5f7673636e6be0f19aa7a255891b1c2a260c79602015b5b6c7c6b4b438e2d248ebc2da49ebb4739b2a555893a15d322f42dad049b106356df1656a52d04a0dc1dce2005087c785e2120a0a0ec253048800f1472f778ea8e68eb00e1542947e329746c6c2f2a67c0548234aed2d75434d3cc5a76ab000ca74ec5a83c70c8357c8b009d3996ca04f4583157ac0d9bffaa4e41eacf1064a7d207fcf3db1af0e393a71a5b035cc90eb2d4a75f600bcbcc97c9a18dd7f5956556969fcbd79de7b0983a0d9356030ba75b5774edf6f23b0bba12342a10001016a82234b07268050ae01289948037889c5205c07277081b9884d87a13aff615b6027062c5d6b0aa543af5b3ba6ab44f706c0c50303a071656ab51b63a41eec9126d09c0a75dde1c58286f016823f6ecdb0b147c3ea56665651ec6c4bca8339fd786685f5c05fcfbe3f3021c9cfba6d5d80b2af592f3129b6e288456a95240af3e366a601f1d144e0a8db251a97f7f31da195b5c8dd3a86953a95ae13c826a36cf6a54ea5f0c859bce71d46a5fb40e0a4785c24da56a542934cae67cd2cd54eabc17c7510be71155c2f98dcd8c3a0785a342e14c40e1a84ec1392a353a0f36a1706e4540c0630c775753dc03fd9be7e46436ea54ec2f10706c2c2a057c12a6e273f75930909b330766357996aacd55698953924ecda70a885165681a5bdfdfdfec84ee3939144eab7d7fd579ae2a7de3a39e55a9d2aaf4add9944ae9e6594d9a36cff3ac46352b8ce3146cb0025bb5beeb979082e233a0746eaf6fc53cf5eab07a95797590a352e89c5648289d028e2ae43e2a898c14c5c95d506e6b42a374d3793abbc88b4aa348444191a2dabcc6bc7a0859820c052754157797202186fb441df2296e0e2cace6513f9da9ced4199ab727cf7f2da451bf67fc170fce090e6139f21005471e1a73e421b82db442df424be25b6886dc7d8147a8b080b965c6c6b7cc02f9d61903df3aa3f3ad33aa6f9d39f1adb339be75f6c8b7ce4af9169a976fa19dd0eadac2c2c24af4c3b1123971ac44651c2b511dc74a44c8b112353a96a22ac752e4c0b114c18ea5a88463290a742c453e1c4b1114c752c4c6b1142d3a9622488ea5e893632d42e0588b226461212800008947c691786f1c89e7c79178891c8957ca912a908e54c1c0912ac20200c06214e4588c901c8b319463319a722c420192605e0d922d02af0509ba68c43992c6ec481a301c49038c2369f871248d508eac81e5c81aab236bd039b286d491359e38b2c61f47d680e4481b091c69a382236d281d69e3cd9136a28eb461e8481b4d1c69038c236d283ad2862247da00e5c81b011c790382236f6870e40da6236f8c70e40d3d47dea03af2c60e47de4074e48d2b8ebc41c69137de38f2862147def894c541cf911cf81cc9c1852339f870f70309b220247a160445cf8260c8b3206cf2ac08559e1501e9591122785684d5b32298795684110bb6f69a6f6d14beb5a7beb50d7d6b2fb93265c0b54c35702d535fee8ec4c2ea93c5b1faac71ac3e7c1cabcf22c7ea33c9b1fa90722c3f568ee5078163f981e0587eb448b9ae458ae95aa446b81629a76b915a11608080ac2c11531c29628c2345c471a4883b8e1441c891222639528426478a7874e4882b478e50e0c811161c390276e48834478e6873e408138e1cc1e7c811828e1c31c391237a38720413478ef8e2c8116f1c39828f2347284262e509e25879a438561e348e95c78e63e509e4ee580d30383a32c300476690e0c80c648ecc40e7c80c808ecc60c391199608c0bab3c3b1ee1871ac3b4e1ceb8e15c7ba33c6b1eeb87177035ac80a551c59418c232bc0716485408eace0c891151a1d590194232b9c72a405048eb4f0e5480b4b475a08e1480b768eb4b0f7008081018d6b8ed5a8c2b11a5f3856e30dc76ae4e1588d888ed598c5b11acd3856a31cc76ae4e3588d8b1cabb1d1b11a4d3956262bc7ca84c0b1323170ac4c121c2b1386ac2b2f198ef4f2e1482f288ef4cae2482f338ef47ae3482f3d8ef432e4482f4b8ef40ae548098fe02ac182449eb50093672d10e5595c063c8b4b8167715df02cae0e9ec515c795604b18476ea9e3c82d8a1cb9659223b79802b0a08198231bbc8e6ce0c1910d4438b2c1d65656842c0ace95c5cb91593438320becc82c6c8ecc82e7c82c828eccc2c391599238328b16476679e3c82c8b1c99059423b55839528b03476af9e0482d271ca98505022472eb40294e8e2c85ca918e041ce908c1918e618e742473a4e308473a3a1de928e848471e8e747ce24847318e7474e348c745473a3672a42328479a22e04853101c694acc91a6c81ca9958585800001015908cc781602389e85408f672120e459081a7916024c9e85e093672130b580c116968216a71c4938c09184121c49a87424a1084712a2702461d591843b1c49d8c491845f1c4918c79184891c49d8e8c8c22b4716467077a41750f0a08096840453363872ca07474eb173e414d991530a1d39458823a72871e494288e9cd2c69153ec38720a23474ec1e4c8298e8ea422c09154ba1c49658323a9b839928a9d23a90022bb3095712d4c705c0b531fd7c2f4c8b5303dba56230377bf42ba01e54837a81cf9068023df2070e41b068e7c03e6ee08b4b01668a539526bce915a2a1ca915e848ad42476af170a4d613476a997177ab01050618b0d272028e3c6b4ff2acedc9b3f629cfaa42e059550d3cab0accb3aa58cfaa3af3acaa38cfaa42e15955b367554d3dab6a86675501f1acaa43cfaadae259556c3cab8a8e6755f5713771d868334aa4ce303a4b1231c2aae09e57a0546ae26a8018d56b80d8d41985ba7066f827f1635b1edc1d53a1412cdc770477c770610b125218c279c9610ce4a3369767af0dd237175ed2bda7a27004c7d32a396a73947484e57a7a5e651a319267b4b1b1b1b13c2d4fa480cbd98848dacce8dee0eed8e4e0d6bcdcdd30b2db478cb8bb9e5bdd5b5e3384d9dc5cd32d2f4dcecc445c5d9e9d999b1e75ce2233ae28d1fd5b456beebe33b1f012a3fe5d5d5d79d580aa2c79b693081946354434a38e4a8df9aa7a7a18cac8a2d2370b80523ab4aa342a7d438c0dd131540586bb1b920dc51952220d79799842918e14b2e2d5994a8dc3009f8448429408312214c7ffa675de84d0eaee48425b8402302e5207bd305c40321a22194d2019d3de9083189d701e1b9ba37d7ccfb96f5a1db4c1bd7611f77d3884c83b410a82119da9d439af0f52d0963bf52fee2b232b74ee0994657564a0c01581c800818aeeed400262e3fe5980e2a67f22fd29e37f92fca9bad7d806af316add87fd11d0b858298923fd10bad7989797971f10ee1ea38f20377df81ecca6e035364647750a5277813d4d9c149fd521eda1a3e3b7f6acb92772d7161659388dc57731831e52ee5e483d651cc305a49e2a860b7a5c77c770212f0f290c17f2349140ed1b9c652fb1b8cf6fae8a220fa1fb0522f39039860bd26ab4cffd7380921f069e2678c4fcfdc54d3c34dcbdcd0de1e172e49d1d82419076e0ec2176e8ecac0d1932048d73c1a008e7c30ff7177bd1be3c8a1bdc8dd4391318cf7c638e74a0b87b8d55e539b08faec5fc3607d59c18732aa0b189cd6ad4c03e3ab1c0beea2cdd0becabf6d1890dce7d7ccf981a9d03a5d30d5e627c16aa8082d139daa78741cad7c26b43544a8de7e9053d393076a306f6d17905f6d1a955a56f7f7dd5393d2fc0cd9fb701e5ee7c6d0cb8a0b3cd58d80b3559dcfd81ac69746f24c4451fb5b966cba9817d550e817dd5bc1673342fb0af1ad857cdfbaab55105330e7a703deff2bcfa9c50646056860b892054dcddb1e8b4e9e0efff32eabc110128837074223c70981f2aac3903ddddeff628589169f4dca5d0beb434177c9ea179d26a5e9a02ee4ecd9b676822a946d1c4454f2ce3ee7955e91f35d1883ba1530b67bf59cfa15069625ae206a7266239150d2977348d1c8d1f348968a2b87b150b3434dcc921120d9d570503a37d7ad15535867acd51e9d6823cd3b80d370e1b22dcd9ce8c98439ab11308f8248c01a3d4b84d06dbd8585e3496b350c6712a862ca33de6236c56f9f6fa4acc7bee9314bdad8d1a63e7f333da3737cb52b4ea3c27f7a5cd6dd4bf3fb32a356ea352e3366ae13cc24420e0748d4dfa37c7478dd70201a76bd4421269546a9bac332b8cffa885f30899fa479d65295a0be9db741eac52e3b6191acf5509e7b7ba5996baed093ae734bb107371354e9bce84b11b753a5709e737eafc17d837a593d2c95268946d2a559b06023eeda45d14b89788f6b989e9e979fdfd2111f36a31bf7989ed95a84adfaa662da46dd44218d428565a04f251e3b538bab5ea3c1717ad6b769856a56fd43f38a994ce8d706f4e9eabd2372a9bf44c9ec67c7353a99adbdfb42e966b4ee7b81aa7616974af31af3a2c77bcc6bcbcbcaad2a86f03db6bdfb041f5f24293a37585d3aad7181e8a2b46bc4ec7990d41c5182ba8532b6055ac20abac5551e0627a18f6a880152e8c8d4de7b1313a2f0d4f3bba36e8df3c2706fd9be7a46fd4782ded19ed934ed1aad237c0a75de1b44a9d9fd1795693a36653a9da336d9ed5a4d3f94e3a9defaad1796e2fda379f45a56f84b33ccf6a7dd01452e0c8526248c9930241ca82bc0748289f063d717c82840ed2c90668b42f8c0a8df641e973a7ce83f24c15ccab0a4ae3d9599d0ab2c8f31a239c67a8d704335d8b062e31401ab07b40e6091457aa2b616824c944aa8b871d0103f2fa2e84c09a2081cd9220411b80f0c04183ae2713ac2d68a16f57a97bfa1757a3718b79b06f8f05759ea1d5d9efd957a56a54ee1a8dfb0e9446dddf71068ca3ee3acd06fcd35e804f42f7edb7574cddbf9057779ffbdb79437cee7bcf7d43f7fc010c5148dcf767f6ec0b943efb026759ea469d67b5ea3c3757a56f7e4f695b752a0d246c52a7f3605fb46e1acf7f68d5673c153c4b6ad2cbdbb2fb7eba6fa7fb8eddb79efbce73df2adc379efb4ee1be51b8ef3bf77dc27ddbb96f13eebb84fbae73df74ee7bce7dcbb96f12ee3bce7dc3b9ef37f7ede6bedbdcf788cde6bed7dc7708f7ade6be9bee3bcd7da3b9ef33f76de6be4160b9ef0f5364912a2e261721743e6a9ce79e027948c7a57c70af52fbdae6e6b9ea3cd79ce7aaf3dc3ca726079ddb4644a57d53a95c1f61732a8d4afba0735bb58fb029fb3de3b36a7436314fa56ef22c4715d1627ef3abce4da55169dfec579d8bf6b9e9e945a56751bd0d182e38a7731c61ec568df6b91506bb09f37d151e8eec035d2ef8a3423f8e470b1f1fc3736477c002f338ba60e8047e401e90529696b006f74d209070265dccbecfa7ea8f6001c7c90ea17d15a6207e890c9433d182c7cb2076f9394190fe139244fd0586c3e310e8a04b2438f2932c3df8286ea5dc0feeca267242d1659888dd39c981274121c69d1090be070e4c4f3489cdf7109bf4555040784dc6d3f381f11e54232e1a91abc8affe68aff2c8e86a8258dfa388fd2b066f34e1190d6f94a5e5a30cc1782a778d368b54465ed111b798bc239fc1ae8e4f283bf08a2fc2e8b33168cee389270db7e2ea6ffc18e0a9d292786315bffd6618f1182c91e9980791bf838464571150f2ee9443b732e7e2cb008a6d2b5cfa7c6013cdb73133c8977d70da549cc1ffc0c6b9f51048e5cc491a3e0fc7d3b160fdd97b0cbdf93b191a6da7a308479e98dbed83c447fa18b3e167a84f9340822ee406e87f3a04ba9f287ebe06aae1a0b0f8b804011d7ed02946ee60caa19d2a4ae451fc1f5d768accd750a047ef351bb8559e385ff4b7c54785216a2bebfa27da70ed1e09da3703e6dc8424b5f93093c2b89b0e460e048a1e3f9340d087b090f10f1a71f91e04eb165440c7abe0516e0a4c347d1a0c1bba8b02d86fa93bdb895f777e644ee3c3b040c4e73462d110b8ec3c600af619bfea7c10260cafc840ff813e6d8e294ce14ba9b01f7888c5315c60a2c524d870409e16f998aa161e9d53b8094594f987b63bef9469cf6b9065d108c24cdfcd9947ff0a52f7f264625701b3c42f41c7b41fa586872286d2333a3ddd4c2c94ae0182ede19e6bbb0d0fccffe9141acd13bcef528acbcf7152391b37835fc2d19a6379d57422013d1c1004a55b5d92e403f83cd0cf00ef6a2428f8b317703f00cf89cd37a888c709d0b5c3bc017aaaac469bf932c415b87276942ea67e931a353bcd21430fea05a1ad084e93cfc007c363c9e97946217aec317564df318a933b5a32f861d0244fb646f9675c167772ce8f9b6541f072ee90382146739e76657b9b8da79dd0a0f11ca074d810b0b06f635b6d1e889c3896b9a8e71950e001953776916a1327d41adc7baa59fe1083553b205be37f1cabf69923207e869f7857b0d0c04169f0f3723dba7c520603ba0f2fd5578a22b6e3ec45fe088c800db7e67d23b1a47f9852c58d78e2a3057c59f22e073b9e8406322f0104fa982a283a8e4402769e46041e8f07235fd4a8cb63a069d22194765e841c88ffd0e4f533300e3b6f0f96f3bd685f472b922b7884a3db1e59ce62c5d35720defe46198f8f64aacf53f12ab427b548f4066830e85260969c07db49174a19e44ec422ded749911b26d9e42d8c4e3ea25485fdb7e8ed1780c8a31f5331f8b3be00de9e887b1579c2e820a94afd09ea9eed69ed84c791f2b7edf875792c3d56ec078192b862bb7ce6d89247e082eccf2c7dbc12290d7f54557408a0acf814eff47a16079da966c19944e49fc06094cf7bf8f81429557e42011fda530c2e4e2188dfb62106d4474855603bb56278254b5b2e6a6cf5346e607c1d4e7d9e12228c0f95cce2424c1c70185c725f8a8676357796788b3a221e020c959b5944e20fe2c347d30189df1144cac1d000f22f361878b7468834234759cfa451fb63cf170e07d2076fd00eba993467362738005ef681c76ebc10c2114920d1641ca5f9b3511276a22028beca11c957cd12b9a4b979bf00837c6722a01760c7e4cb0e21be0f3f341c4ecb802e6c62f7238a1c78421abcde1d24e1708ea878451d1c6948723a7e89a005ee044fe32fe93ba29b813c7cacc99a2f10c4c3ebe461d1625509bf0247154ffa40e069550c5e8e073bef43a1495fd110c52bb408f341585479a426af9de21225df44c6e773281e782f848c3ed40fb93741e6623b07c5b913530c1d6805cd73f17d780b871ebc906dc8a3d042e52c1d2c1a8006b8b75168937e13d7c325bdd1fb3ec449aea64b8adfb10a72ed49d1bd4a08710e2178b8933f886e274a2657f04080c363830ef815c8fd16cd7d96bfe4b78d513e11a893ff8288d32fdef9f1b81a513622ba48bf24c5d5863ed8f4163aa8704f889b3ccf004d3e0ad9e5fba4a2b393f0c9bf07cb127dc836e6ef2737da4ad2cbe302133f4285093ee90c875ef454c0bb242ebf8b908d23c16365b7a930e3c982b8e75933f839a250fa0866065d4c240b1b0ac3cb491878bd4e9e4c7f25c753cb9df8f8a33820f6a7429cfe0688f5031bd1b89954736e09cdf0b3d85a6c2f0326bc8aa34dfee7cdcc573a50c203959a5f529e44ce7cc1e702cad4d07a7c6dcff6624fd39154cacd9070d17185161d8d4cc463450d8e818f9ae75468eae3e0b9fb01382c7c032da79ead06886fe08175ea214eceac84f82c40e87851746cec3e875cbcc932d32f3135e82662119a8bcbc5ebf8c446cf42ea7c7bd6e6ed7402f0335e1cf84e63089dbdcba23738387436891bf698295f2fe5ca95e329c53cc099baa75313f267aeac1b48ddd4535589f2633e24fd8e1cbf4985faf18d2d006d2a0f049ec30f090f92f4e0463881d25604d4700a46f65c86060f4f46c6db1370e4c7c718aab3e1440d700e8b9a5bfaa7c787b2bb734670293ed018a01d23abfa6a3e85d806c4ecf01d7406f42e24c7c970f8f01b0edd27b4ffb6e08552be666ef9254e58f92b3962dc431f9d3f61e2e54b2405721b5c45b40bb6336e038dc7e6c232f075ce9e4b5160c4eba679fc0f104f8ea786090f3566f0094b58e9052c56f77029a96dbc307b4d04fe68326cb8b89d1c4e5f53b8c6070432a1bf3080f80d3448e81dd14b0f77644fcf95a2f3abe76921cf0c0ee744e44790f21fc0a3e3f17c29f304d8aed78a73e0af9430768d055cde8615f96f3c71f20f0e3f6c417c2278f5508a2ef49af91319670fc070e0efeaa6ce270115e7228867670150c7bf10a27a6512931fc852e7033892e497841479397730bd14b814cea5e5cbafc972e0842638dd45b4be930e63fe4e141b0d2302f1477e2c7227813e3d1e76c5be23d1c9179170a347e401f8071614ec2947a23c821d71db541c450f47d28f9fc4327e8e45129bcccedf8b1a93df4c0925af23d4fb6a8834791e9a8b6302337d371b7cf608463f5ea314eb5d746c7cb6c4f73aca72b88f13875e0a441aff429aedc41685af879185535824f820b4f4fe86630647f16cfcb9b6103ec8c981eff983e4192b65da888e74dc4dcdd16181617e0d2a1c7a0ecfd5df80cf5c0b5827af1335fbc02946dc87dd845fe2b4fb2182c8e82f8078b8de1e487bc6d60a6701e5f52409071c49c1856ffa43e68e1fa26c3f2d27af100a94a7faf2e8cd2aa069b3028dae0086d4750802782a7082f893bc3b1f6491896dc74beb5ca804d937fe7c7c965202b4a7b01cef08c3822e9cc3fe128b3d17a3b3c821e4a07bb94647fecf1d07bda046173f15494f23aac1f81cffe57b5c90712e4a555a51982abf488f906d15c8d22b4d3d7226ce078e22c9072d63859b731a42e9f3ca243c5817469bd11a316f8bd96c084c0c9e66c1025da6880f7f1a22d24c28a8fe6e02de46b2e2920f00c4676fb191f0115740788e48a85c87963e3f5525cfd7515834fbc6c3610c21d3606c05277129cf7653a5ee9b5984ef966890ab48d2d42a342ddfaf82bfd5263e4fa607d19eb6489e47218caf8b43e6d18459a0dd2e505e0f001897a275e1132f9d6953400af8272c0e5e452fcaaf61327047e9e33ba1b29fba20f14fe830b299114afd9c4d6bda4f9194072cd4a52915aa793274ab0ff253c22104a6f036383a388bbde2339101f02c2d54fb1382458ee146a407125b71b02b230ead64c08d6881a12b4128f49a18b83bc155d2af14b8f89953259fc2057631274e3ecb1a423fc585a307d363e095c8eafecf12f911308c1c858e0a5e057ce2a3d2405d8c27f73f7192eee4821d2f81006fdf88a3e739c591db031ce03d89479bb41e3803df45a7f0ed145af076e6333fd332c23d829d23b61973343faaecaf0663de0ec2aad1b450f003eea98de2c827e74a7ae9133780fc20d3f979b0f407aa32e6b96050bf854a999f93e1e6fb10a0e42d63a83794d4c51171fae45c3a7c788c64d523c0e1e5583ea9f1b646183687ede01ddd6dd27cc2c09e4d1c984f90d4e581a0126c413f9a7c8f27ae47e48b6e0312122fa5e3c05fc589e3c9c02e3762098ceba122c21d095af71940adcf73e6509b8123f076ae86b88cc61f5ee9c8a48dc090196fe047c0fbc139e181826c68e525f907e36cd98709327d84386ff405065d9e1082419f0b7cf4549438b800455a1e41058b5dc90d1797700650739d79fd2c916cdf998ab9221bf0d6fb73e5efee70da81d6310f81d1e3832902e13d4eecf1c53c344e00429dd73385e68599d6b82047541f49539c0d0671ceb7a47f088d40781669dfaf9864c945ec68d445fcf0f89a942b8fe64b1e8f334863231a25e95754d5b81cea98f74121a309cdfd6f5aa4751d1059fc44d7854ff405e8d72880ed1a02e07c469a53eea900b88e0043f4273d46dc4f5adc5fd2b2f4d694065fe4a91c1590eace3068e1f510c9f3eb2705ff4605a65734898c1b1ab1fc40692a9772e1c62e1386f79ab0ec71b91c3b9e84f24b1f9ecae1160655712b1ca87e6cc1182f93a3fb1aa9d6f80d008e6f2495a2c6c222f1283f43ffdd00f1364c19713a672abc243592f4f2ac912f151c3a1c1c77ce05559b23fae5f0196c8aec2ccec963e7f2f1e6f0f0050bae7635c99aff3a2cdf27848a37b1673c273c24bfa10313975047882761c4c4dbc8a21ec70b0f1b8ba02c1f65aac6a612f2b9221a22ffa14093370ca1df11d9f5792c217136879678231628fa0fbae9396919a40f3d85fa0378497c12df25dfea80e12b9088f47622d479398d5c9c0562a857f4e4f4421a5a6d3780fde809475d6aacce23986bf25a7b3e7c1447f32ab868f151c9453ec7708e6fe549db68d42471afaf099a8b431fa771a17043b211eaf71e723f2892e0790090b30fb0597b0f189cec0e04b23c98ac335da5d03fde8b2aba4a94377f894f89972e69732c094a7e9631f8d6991c9f18a7e511d320d90a5e34fa0196d1e65b5be0ad132c9a1089e12b651a1a4bd290679049c42ee160ea078c0cead500287f11a8f14f8400e0802e326946b6d20ba013f40c36c27ece1b313b838a427fa177410f3a6b3a04e9a50fa3e3d06d0c76d289558d1eae0c54c71183f33a3a553d154c39bcc5e127cff049f129a801dc398a44712b8386bb8c1ffa1f81568da7668f670951e119781ae4589cdc5e53043afe43868a672365e22bc630f8272f04df6489fd438078dccb2049df6805c9b55e588ff8a2e8851ca1afc8d08ba7024194969a9d7009f1aa8b58cdffe724ed05f1fab8973495fe4218169f17228be3b0e1e239ec68e1a502135f48b383233a43f43a5098bbc233954f92127fa340c04f54dc9ee44c881fa335e9d7d421f23000475c716fc0938113630f00f2e2d78049e238f688b70147753618e27e514801d7310fafa348c7334a88f44966006e402f9438a945403e2dedc61379c6700816c69ed12939e1a5062ec76d9487b102e6ef5e98d12dc45c701f8cd0ecc4c053dfa8d542e701b0c1536109f4568c5a9fe280a13f91a5c3bfb00ae2c925989c0bf4cc7f8ae3c3e51c3fdeef4b1bf73efe1a8ee17b6134e457f492fc150101bf74c3f3a368345f140a497e4a2ccc261208e9344c54b5a84d6c9ea2052afdc304de17421a744d18127dd598496ea094923f6b83491712e1e85814da575332f7775678d9a020f9f9a7091fb48e54c319932cbc4515267f2182a14baa74c44bfd88a295b4357e5c54d3ded085cda9202173008b5d5e91238b6d688e7f8590336fe88e82a7f8e3e90fd500f41ec844dfaf4725c75ba1e60a8894bd9933886b487ef268aa3cfe9664d46b397462d7ad611e4d140aef42cc1d5f1209f0bfa47df20c6e001d4d89771c9360cfa340e933ba38fa3c8a77dee6ac71f729b0f59e50f5e90e0aec7c08b7b9c71055fc1b964c6924a808bf921d994331533ea05bda7a3055f21a6c80b81b370e7c8906aec7d261c0c769d3da42c25cb9dc14b6dfb0a691dff9f3b8ff68f9e07ed6423f6ba13c193060de8d9086c72325a75d37951c314a533ba832f58b98003d6bc08a7759d468678979f83466a4ad8455f93f62c4e83f85be9e91a5485e4ad28da7ca51b793c8c173f21261abd940f57ee0390e8469c3c12e91da3feed8f9466f143c5f0633ae440dcfcbde347a2ec308fa4598326730a8cb37a824e1ebca4c9dcd40c85f2d28741c24f9fdd470e80d844c79951f5c1a0605217fa729d07661a2c543911be373d02cf9e067d03f2c51f93d070eedb03e0c9e054cd4b348e19e82920cfbf91a5f8bcc7b6b56c7cf1cfd5cebca9b571b42a6b53ca9fc952a60bbc15f78b94454b404c1097f4aaf8c47613cfab9b70e3bb0cfa9bfa2c4d4b62b60e0199b297ec31892aff0278716f5c28d0b22647b3625927c9c181eb6184913de43dd89dd272286cb5e4b362b8ac7379788f8437db6781b20a1361b84a80b804b7c03a72e79271a129e908ff90e191ce92ed2109ec0108866e2b6c79b36d61c4982352750c182fbf013f46100cd6d4d1cdc7e94992dbf02f0ca430d4ed99dce34ff4010e4e36984e2cf5838b439ec10e513da1ab810a5da2790e4e6d24a679e0a94c38793618113d892db63a6b47052cb2d6e044dcd1b50f2d97a77a478db1350eea0c2de87a509d2655c4f6f48059f7e6223ccb522479d47b8c507492f69168a14fca41d7a1b9e7178517ad434de9226cf2549ba137931e48a2b2cbd0d2519170268d16e31a1f99da1762bcabaf17e5e8ec682c6e892d8047d7446900fe439748446088fd7b7a7093548f421ba6c6cf6cfc45bc891479b49ff389616a24701a3e64955a5e80764419c4d5e151791a98797e8fae34fba1ebc159bbb91a879f84e2538bc03a9fc071c357c0d668c1f034888beb384f045252a7302636ccf0254831da12ac4f76df9db0358b71f4268e72d5dadf81475b8b8f28cd967e214e9abe025be975cd7b7e96bfa9a8585b33974e1d32cd136611d2c7e879475143302df9c90f68449145d05a83bdeb6457abd36595a30ec8acba0b4c897a9e02f03e3e0b1ea483d872cf16f3294e4c33a65d2d1082e3cc501ac0f0206fa987054da841cc95c8bc1ec730a2df1221558fd66dae7138c8570b9267a7e038902dbe9a7d0e344c1e10a5290f0256a36682d6c2efc064d530714b7713031c0761b85389e2691a10f11c0d145316afd23d5c55f5903b02f3d62f42dacd079870960fcc709acfdba579ec0400e8fa2c05dc473ebc72c29f2470ffef40ea5ec6a6880f132336d4e23714ee395aae1671c987a4a60007f0e13808793ca8b0fe280f178d5510f66e39176676c7212eb02b78408ca09b56578266e277cfdb26033f3e4714e55e07c8727749ed25edec6fbe95780f0f5b19ff983029b34199922ff221185dda1c08ebf54a8b80981647abf4f31361c3f26be8d04a0d7c21ae06b5c7c365b883e9fc74908dd2602a9bfc2428f236bd0b91b2c8eafc5503ddc9a307b531d17fe864f0b6db896e78c3434f938305478853959360530442ee6ccf0783ae8df13e99486d649e1132c59d166d841ff0240a0afdd20f56109a84f618d1b6fa1c5b959c337ce78294ccbc193e59f2f62ad45d8e8659b12f5802a991c4d1e2bbf2368fa5f0483ec1204a28e182b7eab4484fbedf8a4d3e8f571355d531c920ee97f1da2ade52f899bb5643bcc7bc991e8e8b1f1dc69e30d0ca59a2e4ea20f3046f8558e22b9e00a476d27eaf5647a14691705003c9a41773ed0db8aff537db3b5c0e9e3640866ec1c768b9c5586d10c14117dbca3d00ec4b2e0624a42380a0d21de499724ee034898cb78558772038737e3b0deb044ec9678383e10b9549f566776468098bc000e7c7461190a0f64cee0462334f6191a41d14d6615b74bc2c9f1e010731573d0f69c6011374360c70bb824c81b61d0f493e0d8f8932fa73cc221d6cfd2f469196761decd0fbbcdf794f35e448e36e3c4cf29316a7d2097a24701e849378092e4b95ca0b2d35021f0567c386a315aeebc1248a13411019f9c5408c26dca99c503d9309b86244dbe6054dade34c7c09b28b2e5a9b490388736d3ce90218777591dfd5ba2921b2181b55d306cfc479b9c0f4038bf1b901a9e600184d7f0e6c346d3b53dcf56e11eac4cfd1f1584b4a1415acf2547e0265cb3c39534eaedb72f13efc1498c5f2928795362a1362456c26b7d717cb7506c1fe20398edf858e837ed0adb4a6ae81a5220fe98277adc4408b477e16390c7a892c00f7972f2200e81bc9536d0a779b34f31498ae320206a4f1963c0f3fc977b3890f82e3aa5f9b7bc150ea7018ca6b26282431203e9458549f54724407235888bff6170b99d2c0274360024bd5c0535278b31e71098565c49a1a2fd27540277e33ad32bd01cf2462bbc0f7224e9d1c605be41ca2387f4667833a64a791615b5af0023d1cb20bed0634a88714a69379cd0a2ed727c287231117ef81c1d3436093d32dc8408e66b505ce109321c6ed60ffe3553bc9cb0cb977f21f5ed2822e55908c1f9294173df88ecce0b8c1979487c1f3c0ef0c163046e7a537d89beca220b9f2391cc9310b4434fe943f067e6003c87494b7c8d5487ed57447b6cc1d08e23f3e16490bc0f842bcb9b9140c47dbca1f8445cf43c93071efc440737175323f6463ad15b6049fc59a82a3d97c3c5af38a563133aa1c1598851e13ace78f13b322a9e8521fb5feeb82ed026d02f8953c4e534eaf103f0201d8cb2834fc9d2a69d1018f987444767e168877f16212d45ca1aefc306907b0214e772d6438f4181d23b3417f5a2bd39ee56b96c28a9ccfc943f17f4a6c53fafa68197979207e95c1602b9a71d1f7c0f68f3a452f8f961e38fcfa044ce8924091d11a7230f13a1b0ab685c71b2384bb495194ece04cb875e4e00f5700c68e9758f92ff92c8c2736220e80118d23a4f9306ef2a6bb2836405713066a89d285306b7732981767e39f0383a9dbe0702431f284804cda88de38fe9a249236a72e47f6812e17e7bdcb78a64f8769455bed207e4bef4e7c72321127b2e5e961e46a3e38710903daa4d697c6d8fec896562f492b9e68763267da35264be1193143b318de38b58b69aedd2c5e72d0b5bda78c0f338c2b16bac98fb266c6a789c40006f0826bf1f236cbcc49a32ce416eecc1a0443d930ce4cf2130e858bc28f044a54239cbb5c2c9d84a6c397f98ffa90d15df8a22f75e06912ec3408b2f93017c8d8d82e754e4d149a562e47d89437c501b46c7318ad55e965cbe12821efa8f8039770244d05361c8bd883c593b8c4f8097a2117c8bbe502e8a40d87e1120f1e90000db9dbaa0792083a43f8628751bd20b9a8727d88318fed1f195178f858f8d932520e16ab4fa68395150f899a7c0adb578eaeb623c9a80dd8a5f2f84d271beacbd1bb8259a8a99df3ba507362a1f9d7e0e1e1e07c204ea99da8cd25e1af09c4d6ccc231f5cf14356befe48d4ca3b0542f12f80597f636b8df6b4668937d13148e34daae27043f636a6066a5ee54348ff8084f6122ec5d96f68823c040b245c8a6781abc9e9f99ea5071fe729ec2b941ae5b05f90d762498fafcd917a2348badc0bf27dd9e2d0abada9f18f83863c1f21110de985e0df40dba4534842e0f108130f638c809f61d8c3d7f8487847aaa9dd672ae47f1026fa144becbecb4ec1568236f54140f297d140e625f979f99f37f2ef5081c31dc5417c48f09a1e20987c7d25e98252fa78914d6d9e1765cdc751e0f7680e89724c4e9e9a4d2436eec08ea4ef33a3c25f8170e01b9780dfa4c11b87e1e270161efaec0d2b4cbc2314499eeaa6ca2b1957788be9a66772c88cef188adf4998f5a92176ef040d8abde805c7ab98a4dc9070f43984057cb4140747fc048b3a6fc48021a7a204d1ad34b1f34f2f463b4a252caf64bad189422c71678b2e7a0e40e923b828e020e43c791d26a6de8658e429ac40f2d2345f5f97d7c4a357bc1ec13eb033f5b9f16e7ee27cae22fb0e241f9de069e63b66c839b58f00f763bba41f1b00721d24e2362a4d8e3c4682710a4ab05f94c17689b62c2fa6894127d3be3cc698113fc5a6e515b5b1d34b4ecd79983586fec48df380a236e81e21fe781d453c7a4ba74c7f6806e6ab7ce17d081851fe8e17bec7446aea3606d5382750f146ec41eab338a0ea0589009e463ce5035105b804081eb48d0e4e0730f76803b852f186822cd05ec2b6fc2d4296471054e56e384cfc23863c3f741148c711e2c24551c8e43e3e71f12620e2f6ea62860bc861e0b1dc0079c466dc6e9473f46bec7a389921316f7c8cf3352260f8b7916f5bb5f9f384145579112c0f57740749ef6062e7597489e343ee1adf0e1d281ae965edc3440769017598dcedcd8a07b541c53bb512f811b320df07ef877e1384e33a8cbcbb9322465ff302a82351507c2d6e263d264e823e2c0396bb88e3e0bfb406b58f4285ee47b187d7b9e1e863e00cec4a3aa42e3f0eea2d32427c1a8a171b3e62ea351560f46208317edbe3b78d1b10de5492d3c78880c71fa5a2f3013b4a7d280385362267872732e8e6bf3477bc07c33e9acaece8df19155c0883c067956cb62b23c1bf02441f6866e6d52480a453d9adaf256ad02990e5705651cb4971d170014d02ba8906074e224ea507934ad08f3a916a33d0a3f475310638d9629a673289cfcf9d79e0bf2863db11c2e3d18a04ea3c30861c8f9b231f424a252f65c8ce3f7ab0e6420264ed0e718cafe866c933c94ae06ca2573e408f381f69c604ff7136f72ea9bc3dc52b3cde8b13fbcbac4f2a91e97166165fc28205ee08cda11f5346c9bfa44074161bf1d71892f36d00757ab22dd6819412b51b59357f2a63c063e940f1510e7ded176d08dc0a1bc61d039dfa1e7e026db503a65ce6e3b2ab16647c57dda9bd20938137d1888b061501d157d899f0f28041ae34e5f87196615c9903d2261cc3c5492529b4a98a087c458a377b81dba24fa327cd8e71177b9050867bcc0fc3b7ea42f75c0257f8d894f8078071e48fb090da64d82e3d85188cfe70fef0ceb8df50d37f85736a0778f43f6b8ce3101ec73c21a9043f2664e95954c0afd10791ef71cfb41a221dbe4b15146e95f6c3e14e99fe509181674012419f612a7154f32187b258e3a012d76da08021ff70e5c1b6d1b6c883c071d027445c7da83dadaf0982e9a1877d76580645af00b0ca3b3492e23e662c5f4faa271ee8c811ad2760cabb2d62fb3fa0ba3c092a918ba1b1f85e9a963609311d7eb03085f66278e6cbd63a781d33b49f0a11dba1d800b98db9baa756a2e433224cd08d2064f9a330d0979128cf3939b9f340702afc44ecf406778c6f21c8144f31e9ca27841af45808c071366c7d76022421fc45dd97eb2108e0d22e81cfa502f00118eaf0852cccbc7dcba0e314307c4404c478209508aea7a3cc3bea43e44f1d3ad0aea08c233a64c54904b872ea140b9b80a3053e078f91bda74f8ca74055e82d2c42fb0f51e4b857569f8bb990eeeaa1156f487763bf793e3f552cf73facd87cdc5653a771d539dea102bbc80c2a0ee5c1a06744b8e93bfd11d47b5637bea4840a8da9d21c5f81466f0f0242f687f6261d0a6dcf7f11c55772c1ce2189c0da2a04213f45182b4e7738c767bc19e58d8726bd8543a3bc8c47fc4b2e95f60b42aea315d13af06bce9b4c2ae040a6303a8c3f3d3e43989647b5e6cf9e7b16782e6276f6893167ae580151e35882e5077040d38c0f0afc2541d8f50ec5783ec5163a912e94e3a8a2e50bd618f9213e1a7c85aa05ce0897c1d71814f8b523595a141aeb040e71ed65e2bf9f414a7e2e19bf04000cb6de6291fff49e399a8e28ae370ac4bf95f8e20e0a2cd868667aefc5e863b73d5bb722aa4bbf78f97e1e64e02bbd107c126efefc8245067e0f6ed00cc450f10a5c9aec478cf0dece121edaefc27f1955ed01f700f1259cb8f6012612dcc16ab72100e8e17fe876b8994230bf80058c2d6788910f95ea934ee515c0d506ddd76305cc8fc90cf616a200376128edf3b0ba5cbebae08422ec71343c045dcdb3f930c22e6dbe2da05c488a315a0b990e577315e9fd00f6f14c59fab4979ce0b798d83692271797a0354a3ff2f2e94165c2b01d95793e9840481e43cd815f82e9f02fd50fbfb50ce09600987a102e3639833b270efd1d1a9a8191a3702ef597972c0fa7e942e79053c017d930fe1629537f4890cced3c9ae45f1e8df82c8bfc5e861b0ffa8a9ef3b58be89fd319be0940845d45469a6fe06083c6eb80e84d1408d2f025107fc6a0aae56089f448116b342139083f38a3f9a416cdf0431b695a6dadf0b354cc3e98a8c3496dbaa48de419e26931aaec2f7183ee43d20dade8a8c62d15bed8480a0a7c0b8c11ada4afc9e7d98149934a02c29734e13463aa116e499153b7e5d0e33b2291d26bc6ce38dfd89dbe03628a37293df90d59e6b86154d0f6b319ff4cc0dcb6526de3ca192e7e4d13e31f9a3bf41fdfe663d0b0dafe98f68c19ae3c9eb61f0ee707853386707aa22d5874153b0efff525640bc1e29f20830d7fd48580039922443f21a5e7ad148b3c7ce2c797f851638f4931e7db7c7aa1238d18fb4e5632fc850b201ccb28a88ea2eae23e26b97361c0c7df40dc70414f10dd41aa1bef82d8e366aec47c010c49fe484652b34514798a1129b6da0e1e2f60eb8103b1c3f6275e68d1a2e2143d89148d34d548a877b312b5293d6af2885c38f4744e9e632854e8384428f02b9d1e3511349ffec0a36fb0b1a403daf2a8af5e38724a788a6c4109fc9f892679d58f245e56c643735540fe9aa140fb4b49a30f4289bc1933a87c461d467e49c1a277f106da34b0f8b85d243c5de1c2de4340d5c7b368e1f3217a26e8401c8efe608e056de3adeb3fac09f48c4663bf2541aa9ff101cb53b1668f06cb0c5f539cd96932f4b8a5ab42baedc2c0116d19f58adadcfc172f2eaf06ce9957f621e0899a0c7c90dde6501cf9700e08ce9c8108f6cd3847fc952b57ae8943133fe2603f28a5ce0bfd7ef86751712750eefc20d6728b5214e59d064fe86f20e53f199aa10518a8f19fb638edac248907510646b760d5b88ee923adb7c2c6ebb8139b50d385733173b22d817971015f34e8502ec2bc310948abc5ad1e05d2047fb681f3175eb42fe0aafa513df72d5959fe08a0df807160789f0251aec3f18c9b8263648f19b3e7235cc2e44dde94bc1e98a1272097f13893deb88beae253edbabc0ba84fffe94ec09eeb93caff16d57930ac131f292bd01d04067a3e5944e90d070ef90dc0264f3846ce6900c1b0099fecf9270820391424579c0f82189e47a3cd9bc053b17b3456782983b468035df2dc520c33df6000a9efa3e3cda96e0e7c1201937f13e789abf0d1dc25e21a1f10cba26d58a7920f5ac99a9140a0e74244c8a51424fc1f363174941d7bdec08cd8d7d5b87a4044fca90840f5035c3b3d7680e977341e3f0ba5274f4088a007a462f66808597ca4066cfea0cf8a2e40e0879fc81143233231c5d7bc8cfa29965c3c1e4189be004e043733c5f546da54f8e15aeb47b688fc825273769c1c9a1b70c2c019dc41e46a9e2c6cbf52da07a5f1fb29563a9c80a0431fb3c1df00cc8e7d2569d35742604123495672044cd8b80547c4f7a157b67134487edca0a3fdc60388d3f5c8b2170019e23bc0747a5289487c14bf2aba858c445eb508cabe22e6cf01112aeab00af0e31852689b71f2e1133e14d18972c0dc1977e7cda2f87946554abfa66a3a1f2c237a04169fd339d149277660f474c85ce8aa813b6e66c8810e4414c13f1de8a0d3a49af24cfec8d8703414dd0f2cc73fca4bbc4fa013dda8d18ea7318ace4b39f4703410633f47278f8fe083e505a7607c91cff91294b0f8b74306de1110213ee50699cd3806f085f0b9f28e1a20f8c1d8c88f71c0e23f28b5e900d049af23adb92fada5f1072a2aec1d9772b8d46dc41653e4f2b6af537ee2c4984f04e87a0304072e222f94378271f659500dd24a72c43d804d57af8283fec0549e1c5948c92579bae439bef43caaa6ca07b05be06055b2b8b30ed87b1168e4ce688b433924c7b713126ff6648c3eb3a8c8dd9c5afe2316323ee941d409953971190db4f6d608056ee789c8197171390d188bcf8c64c20d74ade922201638820a88b6102c336e3508c5696c593d961d0c8fa4cfd617d668d4247aa0b9272e43bb87171bef238acfd1ccd2bc230037a7e168edd7baa0d970ec80f0432768fc520f4c1e038efbe1981afe68538b0d050e08978924fa52881b5e4fc19ad381c1c3d98c30b5dbea7079355bb27c0b1f08fcc1a42ecd408b0aff04a6eb378488e133c4b0714a9cda1eb0060b0fc1a9fdaa41019a0e212aee80cd7a388a1efd174903761900991e07a0489a8496a3675b83ea104c2be7a4e6b9536502f3106466fc2736556e85422abd681495d7f3a78d1f4ad5c7ab9062e193e84c793b737dae95ec72549738bc04b3229e2503803ec1a4c1c3152ed15a18acfd1512533622437e2f38a975086b0e5fc6a8521ea7b1ea73103a37020e125ef1818a3d572586db5511b796e7256711e58b1b1e49f17427e86c048f983ec9abf70a4c59f20e3504e93e7cde3c0c0241ba849c30fea283e0b78562f1860240d18b0435f1c1ad014eadf2742a09e238211ad9fb7931e841420e78175ad587e9b0e70785087464181d2e46401e8d176783c760d049e771baf1364b8d3c003ce0c3fc117214af8587335d3e2a2f245e03832fbe72f8e46566113ea64dce4719fd3d050033deaa08950d415089a742c8b919b4153e81a6e84eb326fed79de6532accf052bc04beded89fe649a9a7128380c351f2d90b9850701871287ca734185c8f5bbcdfeac839168232ef27cfded771e1bcb1edf30090a2789e049c7ece20636bb1b5afe8f14d872dcef845b1f911049e70357f35be0281f5f580ba7b3b1d81de81f1c4bf78c4e7af1e39fa4b8d626ec3e9c2a13caaba5da0460f642de05d44fd793c714d0fa00bfc0a3112ee0a46f005f299b7d392a213bdb9f27c0eed5e845e128761c0c166b105df1012051d03aaf515704cd028fa08f2ae1c4cfc420e41d730755fcb8b0b6fe289496358763d74c399579ac0e5eda804d04d4c88bd05429a7615247a9e102530bbeed38f27b0e56b1bf2787b2a02cc7c813135c741e04d633adce4a368ac711859467d61f4cd193482734e0e76fc2ca9e9a1f2a03c1135b09f13c2cbcb3071fc098065beb7df49fc4970014c6287629569134144fc41059e7e23d0eb7f89f6369719bc9701a13c93c7c65f6040c3debab1c1b76810e223de4cf20a7d2f761208ad637870c171ac18f4315c8d3ec03194bb18a3e4c3d040e57267a05a47a5f59bd461b04b7c29e16008e1f8afb30c6e858451af6dcae3433654d17a47bcfc4986509e630a225fdb9bf093c27cf95c8d33fb6a45e25721abcec192a26fda9be05ad686bc133239bde6907347bff64514287d4900485e8409f5473d951ed32044efc287115f9420ed632ebaef826afda8325e5f7741d07fb579f22e270bfee4c3172de8d2257f938e6e78a6cceb6501f2280c28f84421963c8253225e0f23a88e2408e87249460fd4f6e4871cf0a05901485f53c384ae9602e4827a50794a3a4c79192b0a9a1363237fb3c2d3180ab53c314411d7f3058f3bd29147bb5dd32109b0db839ea8f1238516740c46341f2a4297de608a8703d8f2da1c0e747a2b0dd58dd8c092eb71f0c80f559ae14f48387208825c6eaa091cf7c487e73f6a7471c64e057ed013b2dff325f1012dbae46d00f41c102ab7d9f0e0f16389a8b8d62944ef66938e6f1688fb0e7a652e48c1cfa378d1f27296071e81c403475373e3191c107aa0a2969fb326c5cb39ba5e8423a53f9c50fd4a74615a09023a1e48419e43897c79009a246c3415fef8998c46b78321c40118626ed883a57744616b2f6b2870c04114bc8b14a7ff02a065739191f032bd357d202ccd67f801e43a3ca9f92c207e7ad3228f2f81e1f0534148fb2a45911e9a8acb4b7053e2fb83991790e0e65d0430bfca13c51d27841b2f8385857fbd35fd43872ae704417b860d4aafba1be58f7ed05d0524c40319f5b58b50c0e46d6a68d057be583d2040441b89292d37e0084233d7c7bf50277e3e93ae1e4fba64c7f852e6659dc0f429486edfc611cebd0aff7c54d60e7f6b34f60e7cccf930266ddf6523c54f7821c9195d71792984431741889476ac0af26706dded4aa7fc38114f86dec245041f5425cd26100088afe999b50744aea7a281615342da78276a7ef89cb4bc7714808f2e7b3af146321c775b1e0b6fca861ced44ce883b1edc4381c1f84978e47956051adec586884de591dcff0843b64f304e7806771e6c0e2a22f9d51a75ef5d51be883d97de2f919b4f5912e70cf48c6fc3f8c037c501f58bc808df0c450f97e17844cf1123e49317fe9e148601cfa9c317bde8e0c4c340734557491ae55766c4f91f2f861e0ca3d953af023c9122767c0080b4c762a3b7d9ac91703f33405fe9c2e760d33afe350ff1448a9cb8a022896f94a3aa5598097a37358e38a0ba20bed76748338884e81b3019d1601629f2377b0e5ed2283b17a3c6d162619eb861251f2fe60ffc867282bacc63255fe4086c2b3a22c71770dae44d25a197126ad45cf6f6fc011c8dad04c5e0e7b1a2a04fc9642f474f20cda231d1e5065075960f75dccd90d49742e1e1632484974bb9f00a6f207c00a525ffc770c91b8e12e47cf0a46ea5718eafa180448b0af0e6670042a43d3151b897b412a4527cc6f34e2fd9c4e6352a84d2926cc8bd1907246ea642ecd41539b6e06ac19938aaa3b5848df1c738cece42b0f796da9cd23c1eacf027871a351f10721eb1c7ea45d61ab9a33c3476d88ce4e522186e3f7e539ccca3229ac703150f804dbb7b70a1e248da286a2c698daf00cb8d0780e8e5899a3dfe052820cfc7cdd16b8ce0e33e144d680092503c5caa51bb122414ee46cddb05a2d47766e96d0e7c107d0c2e2ce701c4d2cfc17bf2881a35f04532944d26c814d701e09136d0627e1aadda77d031c3e1aabc67c1c2e78b39acfc1533196fe68efc0412c0ef37056b4f79b0c5e534373c401adc9370fad18f2475bd25383ebbca1f236fb1e58f2e134d73473295dc05a7c08f1077e51720f37ccb11c2ed07cbc62b9850e803c5ecf8649da1dea2f1f7a8a26f1c121f012e6817a689cc796f03f5b4eff885f02d1b7ffbd0c99d9be882e1072d3278248a0a1dc61909cff661895b25c1e05f8f78b4a41c217c0906335f88c9cb352435ef9253fb1d738fdf0eae79022df27893cb861f9522d5eb4df11ccc01043feb93e6675e95b42919529cc799295a0fa51dfe0611cc2ea227c8cf687ed04782a8f13622d2fcd8904adee46d82bf21de78fc078b0e3528f9615a3bdbc0f7793aad2cfa10fef066d430b90608943ef9b5f83fc612bc2181405b46358e5bf2e344571b0d701189589c0b4a904f3659f8220657dc51013b1ac41e483ee982241d6b92ba1b680eef4267e743685874c5538ade4b12036d89029c2f634950afa570fe973db1cdc18dde6bb9c3f86a9d16ff4d0ee02bea44e49d2e746c45c2b827b007b52842110e7b21f52320f170502716f7a3b54f7f653a7ddfb0fa17490136929ae69f7482bd11d0967f1c5b3f08073e232e575382e4c9272de1748ab6f82fedfd88a0a870c6bb4e1acb92e027c30675132f36be1d9b7d075dddc37194faa54e80de52a8315bc122c53f8502d14f7a5af21755509d4654a80f74b1e59f3c1c3edc25407de28da8cff3b347e7b57e5ccf981a8e2213227fe452f673c024cf906ac57e40763e131abbc7d422c367a892428f61177f0d8d05ba94133bae620ad803c659e0ab8cbc7d5884c9c37e82348b400d5c8bc0019d4959ea116c85d88a41163d9c36598fa34d29672b82f5441cbc871d6a7c328e325c4784b88ff2e67826d332ade88b952f51036a0759c4e1814cbf5e0a989cc7b8f2b23f51fe4a44be6c4a72897ff268f5215cdf9785fd361485d15d9c716d6413329f685090ad8825c54fa296ed176a2cb855112f2f6480c2ffbce9a1ab3018e209e6087f2249fb7d3e95f92096a2381998470f0a8a8c1ff4c8e969e880714d3b407c1b0b0d5e83bbcfc8081797a103c4178a25fd9429979e888c24ef094e277f4357e7ac9f54fe810d820f34458d5ec0278acb4c74bc2208081ec1b1875643a2f56a2043fe43419f87b4a68d8711bbf12588e8d07ea518bd12da0c8ddc00e9876071f3114c01723505426cbc4732b7d023ee05ade17b29727efb0334ea4ae464e8b7541ebec89bb8eba8607425704b7c0102185f274bd0e50008f1fbb1f0079f7e9c82a0a6132e01f2820a0ebd11a701be5806f815f6d0b828394b9bcf32ecb9dac45b5211531ec769c63d70c14e6701153feca0e8583195dd28a8cbdb3ccc3bc424bd2f9b936403704ef83484741c0cc993ff9322e6800679f14d6cd5db5810e10beac85ecb274bef4600f45929290773c2ea3444047ce0e304ad648dd607c0b063e7f1f1fa2128bce83c35c0874b4bf32cd1057e20cc51af2987388b4f695ea646d3fb1c72d2553860f95084b81e91cb764298aefc9310fd2834850d6191d7bfc9e8a217a786dcc69430bf6410f1c9a47dd15c1b40f9190b632e67410c07b529f06546575cc6a5353bcdccec4be565d18eeabc7e42318ff7496c9f14128223eee5f12ae80f3f33f53a12add823ed30f443bd204f3dc3e3e13b28b40a3a346e67c522efb3258907faa4c843d0d5f84618493da30f0dcf5b326b226fff60fa96ff04051337c3c1c837b864c7371401e43864087a11a39e5a4f0d348e618f80eb0989e0a81e61d15da908f9a13a510fa0e78a5bea1bb52f8091bf9ad6ba9a022edf4747e10b89a3e802fe4cbd05138e7c46da8b2f8003cbff38c3f7568a787220ac002d83d9c82fbdb1d895dc123cdca2a46d688897676b24e605114138a440af1d78b7c7236089d382a6f4702d6466bc7ae7e1470478b50f2a11fc9193124f6140119ff3674b67d213e7ffda8c7da018485f4654bb0d4518f177ea0f7042c0b7b084e2274917f9a4007c5e520fb857d11cf429eac03ea9a9661b72e987ae19fc464d587e27638e9f29a17b33c43aff030a20bf90a9866eb73c7d1a1736365b90038fa6a6e6d318833e28ae4bb315e478344ad3f5bae879004abc68bcabe5a7e50dd034f2f878c518855e1f4a7a9e1f150d855afd4993346d2786f01e510d3eeec3c6803f60a3cf9e42067d138c9f5e0c993bff42674ad3907bf16a4ecc781c2e32792a2f905e09880c5fd6b8b782505f7c041c14bfe02ffa5a6e33362230e8ff0509a319d1d8fb173fa4fb10601397a3e56aabc9b2e299de1afaf14819cfc3a1cc4f5119bd0eb31f1b0f9b282fe5c2a07d47ad8893fd20d448c8887a115d6cfa31038b9f31f5f3692b82fca3bb022f02ce0807d48bf30d8994933a92c00f046af91431a18ec9a0f89d8aaa55d419f12fa0346e2037bcfc0d9996ef5480eba3301d68af4b979e4da1b29d5626e8472af1d848d66cf928e9008d7460f55c68a070cf02e58f18e1b32d3dd07b2352dcdf3ef17db8e5d02d7bc8d3ec45fa2548007f019f212ff3a578bf36160fb663d0571ad4a41dc9dabc0aba249f408387cf9116ea039478fe2702759a0623245fa588f8194a17aea18f00373206c4e731c0659fb9507d1f63f2ec2d2db4788dad33baaff0f538500239a23a40dc4525e61e62d87143a1a2ae412ec3cb955975a449097e0ba7e71f61f2701d4a121b81fce89fc820f0453eca3e0e86285befb1ca5f34959ec0579ab77840634f9aef5c0b8a802f1430c6934018fc235a86f85308ab07259d791205135e0b2ae11bfc71f9900f3ddec417475d2ac5bfa70a6e7a6f09ea37813ad36f4014bfef0518a7b10c7b14806e7c83d6cc4f0853fcd5c7a5575048e879c2d6f758cf9b843438aa3e1db49541807f0180eb6f413479550d3cbf271de0472e35f2e999384f8447722f8ac9f40bb88cf2086e819f84abd456d008cd275852653bc964e0037582a1f1bc9ab81d80429774f7c5b1eef83d282782ffe829e3f99254f92753aabb2aecbc183d63db7503e4d7f440b5f10c157a7976d46c789e3e56b4c82134b9e28122486d345cbe381394a06fc1f0b8bc414f27011be4a206fdd0a9a88cf82288487c8a116d6fea13557b9023f913366b731d91f41ec650d28610007c1f95146f6586833f3234e029420d7235c23b3731ca91ab919ab1479102f584e4fe68c43a69bf47c006cdf868e0cb163cd24847fb351c7e78422c447c9fc0badd2a487c3f4810e8c1039fde0b0250eee00f2a3fa374e88b7080e47b42e8f0690fad0f03b1f427d43a27c1e535a8440d2ec3cf28a7d3e4cbeb394ae006eef8710c1116bd919e9d677348697782d1f86dce023a8c1a42e7f41b9c0e182a0e1dc1674379f1eea66585263cf5e5762a1ceac03a763e050142da83a103bee489412bb55678201b0474042b5f6f2a012aff26ae4b0b38d802a8ff807c0cda8f8e16fd0c261e3ea20c8de33571a13dcf8f97d191c72f5169e048b85cd25e2df178e82c5fedeb8863e0e37ac6d389eb706cb2e9b6087da52d81f45bc690bb82ae74813f4dfccb69e7e34446be8fb1d5cc02792e1728421baa44f89b941cd88a578f9e1258a1f729c1e851c470a39f54f7bc1dac4e1e4a0d997bf28467bb251c5fe5c5fba5ab4dbf6786ebb7e4de7c15a813df04e7c5d75052a05150c1e14e66c3db3e051ee1ed8ea3e2d13e92315f7b019a1bdf86439d764149cb330045e3df0001f40524b56e0a551b977448cce556b667e3a3906e6306936fcab166933882c5c7f270d87b0c2e7c131d09f4802c86be9695e88fde174e08c9f1a3c449e152489cf9124a6cbcc59b2e1d48839d83aae3fa1b4c7afe6d0fb5073d5d9e049dd3956d90de910d1c076561fa8dde26ba11888707f4d8eeb21a327e9291272f4153de1bc01aa4e5c8f8bc4ac88c473366c917c66d6d3e2a4e8fbee9d881101472148040bc421f297e4596f58d44f5f9b533709a4f9b4aef46c38bffa352f83a16e578029998cfc493fe2821389c0a058abeb3b55c8e1c383d82c10587fd4c72505164bf5afbb13bc801e3711d42fc1209025e902010bb149a3c9ed6a9eeb336b43d233a664ee7c5db9b2862f60d14393a0308495724c9895772af6c0d2b30f9e3ff79161266bc9c3b093e051af797a1a0ec278c42fe491b541f3db1f762a74a69137dc6381f331f5b0099b22fbc63e8edb869f41610c97d170a479fa9cde93a24cdf1447bb468010d34791e9aab1f4328d4077618a547d8a1f32e74516d0311f045e460b7842f835e8ed3db663dd87ec01e00f75304d0e7f2f43dcc9ce0004045d930880d3f8a81872d234da30b19b5a6797c09f3825741f4281764ef836ec7a329a5e69d2085f929240b5f446fcbde5368cc2f318ed017e69ef89468745b41d3e472fc582d1865832f5218e2a69a307f0db080e7e80bf02e16de9fb570be2ed06daf557178adc43dde46a7fcc1d9c96f1222e8874cc01d86c0a6679054f039065c70af0537b6e097073ee606ce2e3c20e71c9434be8aa5f5a544139f0dd21c8724804d9f2a83c8e55030a1491512f8180ca5ddc520ef2f65eaf08b9ea8b90d34751fd3c9f2710088e82a4fe03794f3c3d526e5fd56a23f0ffc4a0f5514a119f009781b61dabc9d3609de01079fd6f350c945a429d14ac640fa2c3a98b820542afe25c4e5ff6034f00a70d6378123e83ff65c6dbb00085e489c357fc551d20d5d31f4d1b43f9ee349a31f7149ced95491b13f8c35722d59a29a8b48a3c3a12672291eb21e01104c3a0ccf813b2ab2e26ba4b9f1b0c6561342b0c1032c1978812280be875bc9afa0a5f93e191276a565f5fbf40af58af5e8634ee8f9a09836af44c08f1dc8458d57a4e3b4cd1c1a7caee68a2d45502257d0b7c4436511734659f4681e8730ded044a6f7f060930bb03ae02504afbc281b90361f214a6fa68aea4b3112f18808296a1535a81fc5501dcf2168cf532d4cbd40179c6f2249caa6d4b2780a832df6567ce7f1141d6d3a4304af494e8c9e0b3b74e5a1960ed1c2f9291f906c214ea92302b346ffd0b0e82ab4847c0f44657c479b38ed2eee71b705785f7883f14795616d3e20f2fc008e409e890723bfb3e4932644a9ce1b70e040d3a148f01544cc4e268df40abcdee8042ee363e1436a6b80c3e0061ac93da842b4c7db9bdb2c28e823b9a8ed4aa3189d8a234fae200f7e237948b958214c5eca081a0d412ac4f99c7f3cc9b2c5e72119a2013813b80c39ec5b58b9f26bf070f89889c62db510b277c878e369041bf787bcea2b09cdfc9bd50d47115bf2ac295a7e8668a59551e63c0e1ee9466c91e323c18cef8d825e8b1b49cbfd39f1882ae89e0c15320f80c3a3e3982ef2528b4ee901615afe1114271dc12bbc8130a6f428489d1e4206413a909308ae820910ad20079a37c124692d23ac4b68d3b41189a1723a511ebe518d1a5e4869c3b6e312e298b6c06db7beab5f244dea375cd2fcc3994afa5dd4e6230835d8c83f188ecaca86fed100870ba24368ef801bf4260e08d1523423bed205a55e1482cb3f310a7e843b707cca26403e22fee072f00cf84fa9b8fe468f207e29a9e913e12ddb5413317750c951e7e8e3e40dd93df123074afd06d569bf80987c99d9e32f796a3d98671c07038a75c4463fb6201e4e8f63006b97c942f40cdee26c2c5aeafe071cf74878b0f14c437aec4785723c1d9fa79d2551d3c77c3c760c1d387c4d9610fbc67cc327046ad0a74cd5f944362d1bd24ac0e5c8557a4648ee2f2698b0955f763ca651a3de9da4c8bfe441703e01ee8a298a3c1736751eca858adfb04386a33171f553fcd49f08e47ab412f3f530b9a1af84bd8fe2eacec62be3bafba494177a91c661a921ade845cdfb8823b8e59e5587c00583937d89e08e01bef85d9948fe66c34fb77ff4b8a9015e7ec8851f0e0ac5927793137de718d9474812f00a8f58fea688983b1173f81a0a7878151d0437dc338ebf1021c5175595380035300d82470f2f85a7c7076b00fe91e6e37335f8e208be18f5a911db237481a17db809fc2245c8bc981637ef014ca71dc78ac0d37df1bd9f436f9f22079806b416e93fec701f50e1813ffad8e35c5d391c4d3ef41658893734c5c81695698593993143efc1b372a753f50150407d154f625e911e0c8f488ade0b6841f6155ab4d1156688fa0b47e2ec293b8dab6520fbbf87f0843ccc712c4ba973b162bc4c032dbea7d4b13fe500e17640003c7219e85ff099f9de87ea23d946b61db51cbe26ab70ab898af378d244782e93583c74cfa69d82c3fc014809700a65103da60d699a6b9027477109d22e3124c64b456870515fd09ec219996d05c6224f1b44be882e139e40a9349da2039c63e26bdcf160f4c344617d921ac56fccaaf3a5b4e65efee3e5130c60f9102d845cae8ba08f81404e63b883e349fe4878212f187c89f9d0c55c22f24b343ab49e3042bcd49b1b7a81609817d143e3651863b859d552435341f90230c46c51332957b0e54fdb29a2c423e139fa3c7bc1335a00c7b7ac9972bc28df0e3450e97fb868e315d450722f0c10683e4078aee6304d3f3a9c731480c4e8528a966f052b929711611dc5f6a3978001be1151755a465fe15b5152730d8e867c0d25a6f4a42d34fe9085988f0441817f3260c2f50cc1e2647433360aeaa08bb22275075e3c7ca1b0ab7d8285956f5dd627f4a9e47d30d1e931cb1e5e8091deae21394e4903915d05ccd8dbd16447fbd1d35e4310a6dd86829863d25abe824a4a3fc521c5d76982f4206e2c5e7007f16934ccab0961ea2b3540b015ff0abc10e8041d76818457c9517af4cd1e2f847ad90050e1f147728e6c2f67795ec7a13b3d004b0af72a4017c1be79367a60ee4ffeb7043d5e258f8cffbb60480f5970f65516e1b49f1a89dc95b1dc7d82647a19690a5dda88e5d5c82e68659343eee9cd6b0068bcbccbd2950685947af592d956d478f7d12b14f682a8946fec33a167207af47f5aa4f94058826fc12fc59e22a3fc290e5e6f8084db272a62625bc01bfb4a49f0b612c9a73794dbf080b3094755e4d003f51878258918be902935b764d6e17d6872e296dcd636a8422d5e0aa4abad4c3c7c0607c4f80f432a6fc866f7f0173ecfa805eb577cf8f01cb8f0d874e6bc0741206937df08fa376844fd88c052cefcd463176004c005b90cfe2eab93438a46fa2f2b575ee5079c9e2b30c2db39669b8760a817c06866eb2d40fb0c80a8e82913c0dc910640bb6a24cdafed9636d087cb81f4e9fa147d06fc8029163a7e93e24d98bdff38c9ce87d940a4052431e4583c38f03c06ccfca191be0dc391c96732b5be932b06fc50811f1b060c3cae8b84edf955f82719ae26a0a9ef9f2ae8b89e5d17373483d22aacc0710b67226d1e800a7e14182d2ee092869fb22973400f9e2eb682d136d1547454605268bc2a971c8cccde4cbe387edc99fb9824d078b6bcc7dd4850895f26863d09074b1c441b3e1b552334bf0182ce47a9a1fa6b81732f6102fc7039c90f4fe0fd13361adbee899507e184c3c7b0947d744628fd68ef824f3759e9798585278467c2eef303e659b0ec74e004039ed5a7cd7b0c32bf5012d996ff4c39252b32bcababc12b32af78862c58ee864ad8db41d4e2bf7c75fd98012ebeccd200efdab4e0e54292dccf02b387c422cef60c761e2ae2caa35813f3c5c707da4e95036e250b1c4d652dcf930824661bc27172b61a56b6211b264ec80496bd45203e9b26497e854fa09f9b917b2b6b8723b209a4273cd6f8367152e8418e7a3c059c985dbd81c7d7f8e26c1603a63892410134d9ded4a96ce0e111788c3d2435d21d6a9082af4159351014673e8968c7e70821e7db7059bff08f80af0333b801fdc0f0d58da12b5a39f39118a1daa52cc8f1042d0efc994552feecef81af5541bee70a1777e3c2d33bff78d93a0a277d0e3270eea070ccb36030b2b73635f9b72d58f45484d693654df0307fa27cc71e890d08cecc6f29fa690a6c87ae2863fac349f0e32162e88b8430bc29438a3b5e23fb45b7e8aed1d8f6803625d83556319f09ac71115b223e0d96d67ff521fc2a46a09a83dbe89318e5fd9d26a66721707b3925a9de83e3f44f587dfa1784fb918c39f1cb2a57aff013e1569c2b3ed43bbe0601af0b21ccfa498c8dfe4a0a2a0f629df481a4e0df1264a255c478e46a3204ec471420f83214a6ff3388eb673d599cc489012e2746111da8410f0713f145fb01d3c5fb8068f3c4b2c4af0306d6623752b91bdd95edec70c007a1d0f4633b3cb8a8b9efe34095bd1a9ce3677003f436da1c77ec698cf36df9c5bcf8f470fc185d508f15471ad07b5114b6bc9528871c41d61a47b5c5c84ea322f683a4acd892d4a07d972182baeb6b85830d19b13fd5e0fab91f627485c5099e47a482a33075e3352491b78e425c1c4d8ce03bb47cfcc6978eaf31a1ca456939ee25d49103d854a60b110971c345a9f432111ab7f044f397ac10f92c6e706c1e79763c52754ff74870c10780597cb819523e305083fe2461c277b0e4e758da18fa034aec741737224e860cd62b3208f4732d9c7a06a448afe08f9847c67afecd04a75de4418d37d183c2c612f8e26330387d27430fdf14cac83e1464cfd110dff660a73c8fe748a1bd2845ec7c8e39b6191f721c480176afe1a09d131f18af72c183776011e3319d017a0102739ccc0a327721aa9223739c7a37304c2f37a3f90699d278a34e2d1cce9911ef3577e88062de4b70d1bda899ec41085af394ee0ce805111279a6b5245ec790a00feae068fb9101ea3321f91c4a232bffd6458f26a2a1854f414242df4935c7f3b07ce8248553be831e91ed44418cf7d465cd5dab390faf65f1c1b98f339922dd801009722a9d34681269aacec6a984bf4120e5092dbadb326854bda6540efecf0b42e7c320c89b0a73f4bb53db8e7e3979b3b40f8e0517f7ad60007fd307f60542543d89e9a3bfa2b0c12d493d39daa708da109854ae01cec7bb7b32bd0e35607484a6a4277569d4038a24c421a8d9da6e0d14ce688f89bde0ce97a70a10c4134719f0035ab49b1285e389f829f3229df2fc953a795e61cefa1cf3e23e73e5740d3ffa6c3eb916bee248232fd323c49f9569f144f4b0781793686c342406fc20141dbc8021238e4253d7c3f110c82ddcc97a2824f8fc941db56d67499a8fd2804d1f8211c1e39a763421d884f72288c8dd54f17b2814331712a4f1218832e439f87878608c3dcee601d44f90d2e2251958f260700e2db6e5e24bb1317a0a8910bc8e03285b2b062517b0376a5359541dc426b0cd14ebf8802f777ea052a0977754d15c3c50f81c4db65e128279d5aef39f124972c1b14adb509a9b6fb3a26fb719f2389536195cd01f1fbf20092147fbd37b0c2e7468699454ae748ce39530e479984decde8a62f9404a2e69aed7f34d833ebc2a49150f43a5c1f10ee9bd9e15635c871a02dfe9caa73db840d0d5505568310686b893bca52b91d039ecd6435f60d67803743bdc511aeb0efa40791747371c48900c7b4f029daf821742cb0974e8016811e46a56183d5496123b0606068e0136b03d10bafdd246091d85898b1fd226b9ab0ccd712b77507d032ce8bb41f8b61964a53fbc12c7593880be2800e25edce2e633a1a0e45e4fc03c50e7f8067be63ec0a92e0fa543cda39a2460979a63e0ad187af3094b4ef8af617b158b895cd3f4e97e70875764577c004886de54221abf67b7e860a412b9daa916ef5528667f95fd7d0f3c1b7a459831af4489d363189ae359658dbe5058dccf4990b4ffb490a710a3cc0721eaf23c9e5aec313ee07c00e2d52e43e4fa5158a4369e9c2d0e83a949e310f4fa234d7f361f3724de6741c7ef4805c9bf60c9d15e8eded90042da5cb6c67e2bd1aa1bb9549f068b1e9a121b1faf28cf86bb3b145f52202a6ef725d1abb81bf004c844712d3710341248547c9022b00d17e9d1ffa575d184b4143d98548b8e8203095f2348851baab1f1042a10d288aad25fc0a4cb3671e8c129418ab3cdcedc9e2d928a4fd034e1a0e23a7d96051aff9652f29faacd8f12f86a477898dc1916c3171068f23ca041f657e2144794e6e517c1d9fb528f22f9e1591007a02474b596cbf1bc539d4a451ae7b263f37d0af37c4091307f08a1cb3bc4a3b69670f5691dba74003b316fcac09707dcebf26f1024eaabe7f311b508da9dbe852f802d791691498ee3d1c79332d3e8ddf94efb81b87a033fe02fb506e6cb3e50b4eb31f55c6641ede24af63e89717e24959e7fb466f55806bc788a3f287cc99b27dfa6cce06261087f0eb6a0950091e32d029fbacb134e7ea0d490176217e8970440bf8bdac6292c42fa02869afcc8911a9fa7657e5286426c212620f993d3994d4542f83f3816fb528be593871e6d02815cffc7494d936023e45fbca8b2cbec18f32f3e64fd55d48d6b5129e3876429ff05c8e41b0df4f94c2674bc2527957e865b9f5613e2f0dd1c3eee362adcfe8796425b5397d8079813e217a200baa54038af9af1fa0f30659a505694dfd20842434043e55f1e663c8038005e4e9ea2a95fa03c4e410a6fa5ad1f924adb2312dd390b31695e908e525fa96ec67f2260e845e0af6d7596c77f4cbf7ccd942adfa92befb9e2b479d8d7defb99f19e840d369f07907e3b3b2ffe50a54e5e48663b843f578fc04cd3f73c6d7851336eec360110bc801481e339abfcd8143f0e33bae06bcca6ce0306ccbb80f268cbf174e001dbaf0ffb04c42978c1a217ff8d6fbae06a1e29acfc9b387bb4238d31afa46d7c130ed29c2f4cd71f2174e8000235e83451c43cc35a9cd67094bc01031bf48f0f769e6d490b974a9f788b1cce27d38480cfa9b1d15a4932cf4527c1950eecbe830622fa5311a63b9a69d036eaeefc8824d82efc84e4c1ec2cf1c410713e076d94062409e908f630d06cb836fc86275397a052e75fd610bc9b45a79ec80113ef81060197f334b65c5481b7d939f56006c4b99b8020ae06e7f124a850bc893b5dae810ec55620a5f0a72c65f92c85de77c6429c0395118ef6c6826bca22e635c8f87c4725498ea910168d62cf79031dee776070c99f648a74113bf2ef8d35fae709bd1f9472677381adf06de78db69b32fa410a9bf6112a88dc9388d8afdc129fd19496fde41126dfb0fcfa205a85bf229e0df8783cc5a6bd4fb4e4c14b6202633f782b6e372580fe2e787c89a2b30d1887cf29ec35d97467b05e0318d6e964387996899bc6c1c6e2bb383bf8a4b04eafe544dbe70865e04bd531d886f2d4f84026167f063bfd6a933fcde194a9ff43771b11a081bf1317c391fcb8e29f06b89b90110d0f5c2ca4b59c637e6f4a8f2d24548467e364b8f1529970423337dc4d1a2b0f38d8e547a908f576ecc0bca0d9d103a193fb416e723e068cba4b448173530fc06c46896ebf264c6ff725950ee89114af1ac4c7878cb9e113da7039963e2b5eccc2d7cfdde1a20fd491f32774a6f6a80263fe4b92264f632c869bea71b61b3cd73cfc25d0537581f05154bcd8657ea4de448d0d2d615ee457c492bac726b04fc542ca3e806ad3bb652c39dfd894ef02e4ce37b681f1335256fc1018776e890b0f774263c5e7259b0fc58ae32b82bdff9b01e8dbaa815afec3c71028ec113c29e183ef433f19dcf22d4a086d3a39d4fc0dbc46fb1329536ff8a6429baa23c7b910416e3a2cef6e9e891eccf0f0d54cd0f32a75947c1f162bb744063f1e9de83739c3c8f118527d00940a2ef76787263344d0ff10b97e5003280ea90dcacf75cdf922a4127bc881128f00859833114cf57f82eda6e3067abb44be8fa44194e309f4e35ed6283f8863e296c026eeaf045ad93c36697d3253a84da62ad137093a77282d526f2a0b8fde9435e461eade453809720f951838610b463e800bebf11cf20f84caf015a224de5198eaa69087cbf38124c98104f9e406ca2ce84288d8fc1619526e456f834b3992c603eb98703b27105d8c73ea8d81873fa8a882bf6084e1678b3c5c00cba6b1d87d7243312ae93420327c114c1e7cca012f9f27559b57094cbe9902983c6c089f07014ca1b1746adf0c9d073f8da0e7f3562fa7d4e192f3908ef8b0107abeca82f3135c91f8198a21be44a9292e6350e0d7795df845a044b991187d9cc800c72750a1f0170b60f02450310e58b6b6b7944b5cafddf0ad0c2c3eefd39127ecd4742b22835c881ed73b9954ddef09388fb3a1e311f9d0bc86a90f2f62d4016fb1a8a89580a6eee8a5db4748117a872f4336a645243e42aee3186818f0cd7c6a2c6dfe4de181f41096f8790a5f75ba141ed9f1fc54d27f5b14fd0388db86105171036ba27be9b2f0775f7bafb69b9f458190e6101f713dca3aad63939837cbe064ef212edd0298c6470b4bf29b28fc78ba16cb479cfbe1815bc8f88d56856730b0c8990c32a52b114af9312645f612c33e7fe8678b572931f84c9ab4ed35646ef8873220ba0a08d253718978e89d407f2d29769655f015e980d972d25a7ca000289e03cba3af706443d3a19671b60450bc114c920794f7b81bbd91398d48737ea822d45f50f0f8ee25a6bf6d98f06f38ccf021cd39aee10bd94b2980e35f5c19e3656469bc8d11077f93b1789e0e397ec29fed39ea18f80f4fe92d6049baa2aa0b0ff3b5e51aee3eed337b9a1ee50f9e23512ce12c1bfc6e826c78a6ba3bfd9661f1813ab4d82d4a957da93db1d641b6e2f5c41e9f5321972f20e36ab72ca8bc4ea23e37dc5bfdd4b2d0bfe558e204109df89d38293f650045173e797c0c0420e841358eb882c5305a8c1e05ee69bfb6e2619363e023f4711033bd963841b60d3c597c53971e1acb69e8673c0369275a811e0f5da33621e19367fa13f6290445f14defb81dc3be1cd5870a7a1204c467a037c26d0cca7109f09dfda9b0826ba226b71e1c4067c6b8e39fa2a8fc141e057b4ca101df65cb99ee7445bf873585fec8abef352da9f15e566cefd828b6a91df8b8d24398b6a242cf2bf380381a9b41ff12616a37b194e824fc98fa5000184e870c9776d024f324a9bd3f42e8e047f9b0f2b196519ee61092374329c183c2c2c5fd44a8e34e0283b661591a6764f8e5f90808f3f7b2e683641e1fc009777b1941cadf1cadf496ade9a77094a7b11e99f925907efc9a133ea7e4edc75926793283567a1021b637f3a7d1eef086c253e281b73920c6f99a2e7abe09dbdd4322437c18ecc7e3199243939973f177181dea4f5ec987d6c0e11d94685e93a72bdfd08cf30538ac681c4e167f41cd8b1d29470dbf504480eb5104f43f140c34d655d50760b1491748c3f402512aed492e083d04a7867f1006c6975132612349c5c8978825701498c0feca8be83a8630fe1d2ba3740c12403c4d4f915d2307d2fbdee2bc10034abef89566cf10bcf3576d5ce81d6b2ab8b6c4e71f7d613a9b428fdb93d876536053fe018500be434f8a3fa0b3e46d14a0a28f586df9193128b8dd02075f38c6e82bcda5f9412c25fa4b93394f53c8c43d9d69e28cfc6ced00578a388cc3315e2785cecf40b073a68a24cfc1099d6da7e0cae55cfc784d54c83ca0a618de01039ebf63e8419329a6704191523c222410fc8a4fd8a01e8d381f3543b481af54ce45f1f787b2156ec311a437f142d059386d9f27a987cfd994c0478081e4182628d991ec70711280e6cd75a189e78d08f14f38097a3313c4b6193e4c7f2471669b795af37e7f8f76e3172c1e2745a29b9ca27ef668aaf5ce78f8602431dbad8ec25f4801e233588d712b3896ec278032f81bd7c2be1403c667c96969b6e9260f559db39548983890aa241f238e85ef90f3e633ed40fc2e4366fff246e77fd4e6f764e4edfd8435fa9e26a5fe8c021e3b4982456fa291915d43abce2f4300ee0e67e0277353c6b3fc86781799da43c932f5276c1cf9ad15bc981d63f692c5ad5fa1d51dc0bde414385972c516837e920e2d1b500dc1b7e446c7d1ccb0f0568e6cf0210a8e3c2257c16e1233f118594a3d5c8521ce48a782c6f22875423a98e8e7a6f5ebf6a6f8d68ba9c353e4bec581446f22eaa41f319ae46c9c7cd1678dba388d1d37362ce0cc29fd85f9a5c2c4975f48d13518173fd2a34cfb11989c0341eb7b293586fc290038be3a28e84e25f30ffe743825110fb4280bee8016f7ec026832b98a4560fbfdb0e0976f9ab61528a71c4b1a005d278989e7a14486f311a2e9ad9e14d17e758af89a12445fa0c3752ca6107d0d1f9397d445c3eba0da792b3fa7349b1f5bbe230da9c97c7aff45d7104fa285fd3ab4375b0d80e6ab6074e7a5306ae16628a4da26bcbcb82133584fa44fa06772e78813320ae27c84886c0b07ba7ce21c913d8a6df357429978316e34f95874bac5a46df20740266802a5bef884226a5bcad8865fa395e067d4d0f172e2266d1a774a4f6c456c5389407c8c0d647f0381a3b3b1d26427fa245eed14f60812057d4319d3e3bf4c7f0783d4adaeea3ecf17047d8848097fd363d459171a7cd24aaa0da30815f77026cfddc4413e1c184876983c8dce7282f10db29e78f202943d8246215f72d7e5c5a069ee44ce5e31cb8ec72b84c545cc307bc54557eec59091ff091b1f67c08d975af3490b6a23e1c95c82f494caf89e4ba444daccb0cc1f0070e72b6958f104c6d2e83a49232f878ddf020f3f57c221d11fd823ea9f7956ec54859efe485602ade840ed4fd821d09118acf9368a02f7e3967b76caabbb4cc8f364989df4639911df655ad588fc26f80c254eefc38485c71034f9a51eafef3161c519a83df94125577b521bc257dc1df1a473ccbb68b1c171a409fa1e5d95def08aea675837fe0cc0e7b3a8106a42721439a017669bd3e3a92f541ce09ca25dfed49dda962245ba95bb44276a92cadb9058340b670cdfe2c5f59e924cf9ea54c96694c5915711cce40b08507227766ab6073721dc0b090bda0e5387176c3ef78607381cab0e22bfa208c183a892f7583e02781c0e98dc4b90297f864b229d5492e6d554c1a04350fae349a0247a1b99e0afd13bf053a87e4fa58f9d9f2266c71539c9a2ab1832e493b232e939761c3d28071334256923575e20e22ab69070d90fa07719d1de9484067b69c7e7799451a4993b06bd19238a7e5341f51de417defedd792f311eec47868c6f47838eeb6913e49b6c92e15e22209d3019c00564f2f273a0505b8cebc22f9035f5912118fc50c6b217ef143e9f215bbe869be767fee8700233dafc1e19bd3dea080baf4b14a695893c7d8e4a69bb0bc40a8fb4ad6812090a5d54744c7b20c5e8721ec76c0f4312fd8a52c1335735c7343841a780c1e83f069cda93dc48fd11c1584703a4702c15695e02d1c16b51ba4f8963f29cdc8e6cb844a44311d5a7612451971369876725593c9727a42768e9f27b6d5fdec91d407f5141cafeb29bf32ca71e5a141e02dfa40cf552e8d4f13e4d8eb897884f9e064ecdc50896793f6a499c8e5415df0b15a6db408a1e24d38bad284b863772c5dc8028f579f28e0a4d68a380d77024c5e9dc2073c61c5c768bc3121e02d3809d64539ed72952e0dd60ddf919272474211dae17b324cc05bdb9f95457d169eeecbd230e65be088116de82b06b67457ae1747cb83c53e379078daee8ea283cbf226a8ade9bf4e57930ce5b0291add728ce769540b66f81b7c4175df2e3fa51d44dc87ddfe149e3eb01a1f07314fd762305755e539b737bc973e1b72425fa46b835a7e04843f301b2f65c4e5dd95b04277857a546ef838cd61b3ad9dacb08301e91aef53868988f70c0c1af1123e88937b4166424d28bf963c8c9ccd8784a52e2e8b92d22ae8005803de3aff5b268d59660a3d19d666eff28470cb7a264b89188427a9ff981e6aa427a25c9075e640da817f4e4b283586578317b1a6d37794efecb87b07d06c401cff444f0cf5c3d1f14c3a87f50b97c50948ee65261812f3aa2c8f3186db81d4613af78638ffba912e01faf12dd4723e7564b41ea5dd308bd3210f4a2789cd207d464f9991901b6163c329f812775b34186deab84826772c7e74867dfed81a3035220b229a19a7d929b45da8699ffe31b301b4097c6efe44af14133a47788f1b5efa0d073ba448e6f2236e153540cbccb1734af52754313e1e0e2cfe0489f2565ce0789c2ed2de18e873933f5a28e7470382919f6030a487f429e9c5215f5adf0d0de6480d89b3ad4fbb9cb18af2a7bdb7552edfdbce8649392d3e4e701fac990cafa4514fa6c42403cbe0503612f9ab3baa4114a379103957b2ab2e4c91e993c9798437a1d1bf265fea61a5523450fb1e0507749f1e70dfc60db6255189fe649725b9991f02b310a2fa0cd98eb50b46e40a3a8eec1c81ddff44c7ecc550287c3c4c4f3b0e873681e4e1f498ed6774294f8cb5056be4604833732c98613a1c3e198581bfa05d282f7f86be1a624157ab240d6e30094be8616364f6851f40d49b86d23f315ef24e0cc932a13e38a70038d07a2c047e852d309ba40f90d988078a2347c1c110b51e3399f0ea8c01b1da1cecf332192691ff9dbf044c87e04919a6dd2a97cdf56f9949dedfa8c213597cfa4d2652adf67bc74954cb9e57b59e6d7d08f9af1ba7432d9f585e524ecb66beb423b6a3295ebcb84c23374f932932cec521b359fedd2c932df240c2b9fafeb54326acef1bc269fd0f8b9cacab5852e1735a1cfa7527ea1719229cf4a992dd48a9a4bf975996fdc3e994ab99d9f2ddfcb265fe844cd560985ceabfbb6abdbc2f29a44d49c9f6fab9c676552b9be70f2b9e47b59b695ea43cda553e9b233b48561385ebeb1cbf7b2869acf6452764295acb2755bf6752ef95e56d95275a1269c84b66d0c9d656772e98459a99450534eae70eb945f1766199b4ce8ba542a99f0d309f3bd2c13500f6ab6c95719b7f3ca4cb250a87209ef096a325de80cb7cb79665d280bc33375b942076aaecc784ec24e7796e5767699cf794a95f2bdac9f06d46c6367fb649fac0c4d269db0f3e57bd9d6655d964dbedde54df59fe6525e425726dcb2cef995e1d74f53c9c6701bcb4b767db239749fa6fc945b16fa64e5e73cbbac1286dbc01830c9e4d36c63d84daeac9c845fe61376957c2f3bf785f655e20bed6b041f355edbd36c5f585ecaadccaecf57096526d7967ab1b97ca1cee7938d974a3829c34a177d2ccb32b51ef87d95abcb64e164f25dba2ebb4c2a99f0f39d974ae7d3855f36562e09759e26db2ad9d9f926e7651b2f9df2cbf7b24af67dbbcb5b359e66127e9f70d285e1e72ac32fec7c5d12fa4e937d5765d219bf6ff2c926675639f33d63b4b96f4a272686f64517a2701ec1478dd7d0beced2dcc4c49c72de175ddb57538ece7d533a313131a532b973683b4d66ab7c59d85d934e5776bec9f72b4eeeebec0bedcbec2baeed6b29a815934fa7dccaf3bb7cce4fbe975dcad237e6ca92e93acd24f3959dee0cb7c924bb2ea1cf3f2af339356bd967b490ea8e693a4da5ebc6affc5c9d2e7485ceac734e73998ca150a5bbbe33ab8c97cb98ef9d65e99b0b6b39eb13eabe4fd9a97465d6292f4de8384d66f2098599ced9e964a1ee7285f95e76c98a8945a57d55a752704d5ad1709af0facaf2cac2cc64abdf34e165f27d425b2754f98cdb55a974e3bacb956de195d9ae2bdb325dd65dd96792292b5f67bb26dd7895d758b7692ad7d8c96c97efd3096567d6f964d374bacba453c9c64ee6cbcab1dbb27d7a4d13ea3a93cea7eb4cb2f072a99c63be975d2adb353131ea1c5858cd13139b6569544ccc7dffb49ae60b5db270cc2661799964c6719eb4aa646759fab6b2b32c8dba4ef97dbbcb33d3699a49f75d9f72729e5fe733995c977c2f2bff49a1685f81b19b9858600cd8f2d089cd16eacaecd39597701286e5e8fcc7649530fb7697a7a6d134db569974cacf1686a1cb3699649e692adba5727e97ec3c2f9fee0bc7669a72bc742a956fbc842a59b695cb349f4927fc643ae3e5ebb6ac33d992693e6739a94cca6b32996c5ba8eb8e692a57d98d5fa5bc949ff07209af7c2f1bedcb63c528e176994cbeaf9b6cdfb76d97ca242bcbaf92855d79853add9985571da609859932ec4c2a6765725e26d795a94483693e65766526a1cf64927561263c8b89c1796c9edbbedde53d12fda5f94c3267270c5d269f71ac9ca5d5a698d87396a1b39c9858f529269697b77a693299710bb7c9768e5ddda5a99c5959669d2bfc8499cf2574e57bd9a4527369aecfd519c749966de11596e1765a0d2cd35b9acca4537eb2b1729697cf64bb84b534df767db6eefa6ce3b59d59e79ca539b370fcc22d94955d386e934a2ccc65cbb2c93509b34a97659930ab7ce5f9a94cae2ff45d67f9b926d72b4da50b2b972f9c4cb2f19365dbd94a937d425b671256c24ba5fc42d9e59c989898d8cae6b12ccb2ca3ab3493af3cb349160a7d59e833c97493682acd986599ae9b5c42dd997ddf77161363e6b1cc277a4a338e59b74dcaf2ab9c67367697ee102da529bf4c28dbcac97676c2d0d8094d0ad1519a7192855ba813fa5c59a533f92697701eab3cd4509aedeabacae4f2e95c99f10a65a14f9af2bc64caf2eb7c2695ca3529ad46d9d0bee89e73e05cd28b76d294d71976be5066dc32633629cf4d9a4c68f29ddfe43b27573676e5964e49433369c2b0d385df64ec4cb6b3724ece626285b39f98189cc716ce7e49e50fbda4e984592774a984b6c955a97c9931dfcb4aa151e6dbd7da0a7e7d39937360219d9858f2a5fe0502f6c9d04a9ab0ab74ba73ab6c57e5aa642ee124cde553c97c3aa16bdc2e97f0daba27346213ca642a9def537edda4d219275b24cd16ba64a1ed0b653a65d975dfb54d1f61994b787d95701266beed32c9aeacfc3a67660b7559f965b24911da48135e5ba6cc74ceeccaaeb2fc428b345717ea3a9770bb84aece27bb420f9bcc182a3b6339865b662b2bdd9948f385ae4f1786db785d93cba72c0f69b6ac72c974b64ae79c5cdfd7290b6942e57809c74978755bf7996ce320cde7131acbb3cbb66ccb42974f2690e6bb7c32dda53cc331bc946119fed14cb24be533096de7f709cfcbf7f5d164df182a2bd9e5520985e5d67d7b3495cab69d95ebfbb249b65d42953c9a4c18ba6497f1d2957734636772f98426952b0cbb6efbd4d17c26e3e792a974c6cf363927e33647f319aff3ba8c9dd0394ebeb3cb1a365d38393b97cb7556ceecdcbe389ac9769d9771ec54b2af0b6dd995ef65d5bee8dabea8255e5699b7b26f777969fa46d339b772ebcab0bc7cddd63933994e6da3c9cacc978d97cae50c75c2f332aed16cd9a7fb74d975e93e994ef7a974976f7779789a4613daaeb0d28593f30c85be6d3b3fcece5609659fcf187eb2739caa19c7ef7385d9350965b22bd38d512967679c9457e61bbff1bc264f35e7f66d59a5eb94e3e4927565534d560985e125d3095d4293301bb38ecd95f99499cf997dce4ee7ec3e5d65a9a6ecb64fa532099593f2eaaeab529054b395a1ae735e673686dba7fb7cf68d6ac24f6592553eddb75d2aa1b0fbb2f18b829d4f9819cb32fbb67c2feb2edf50cd395e5717ca2e6736e9329db30bf711ce6bd3b985b40d54d3e98c9dcaf6e9ba4aa60b752e577709fdd4949f731cb749e63cbfab92853eebc4a2d2a8a766ec743ed997b964dff5753a9f5040c1a898d83c7ddfeef29edfa9b97459a5f38d99f2ec6c99f1d355726a3e5979e95c3a9d6bbc6499f0d3c9dcd46c675766caccd7856726bc849954bfa8201f1a0cadd68989bdb0a6e69b4c42994af9955f3839b3ca27dfcbb698a3c9e93c37adceb1ec3bad5293999a4ae5735de37576954ce62c33a9f0b44a4d7e6353192b93b0cc26e5a5f27d3a63372f3affbd6c61a0f4a96465a5e017dd975329f8c56ce7c7d47ca15078969dcb27bc9c65b67d95687fa91933e7a712fac2f2bcb62bcc425f4bcde7cb2edbe40c852e6168dbae6c1eb352d36d632833c96497d067ab84bec965da536ac6cf25bc746118aa5c9db052e94e6acace799dd7f9e9b6712caf4f36e693d49c9fca566695ee3b2b9f3133f9f2bd79d4be9665466a2e5737f9bab3ec32974a586605fda4d2bfa59c174ff7946ccbbe221e5293b98432673996ddd6e92695ec92faa89964b26bf2c9c66bcc84db58d9fe49a54e47cd765d95d0e4ea3e59e752d9326165a36652194393ca79752e619751f3852a974957c9ba2e93c96c996f7251f365c6d0e52b3359f87d2aa14bb8552a6aba4af68565259c5cceed73f9aed444cde713eac6eecabeccd59d95c9bcbc658ba8b9b6f2fb54c24ee6dace70cc2a97ecb44a3dd49459e52c27d927dcba71eb4259b635d464dba4ec3ea1b2ac7c9d33bcc68149b12fd484a14919eaca6beb74329fec1af3bd6c4ba829bfee724d3adbd8b964b2ef3a8df99e7952d96595d32a35c9d864593876c62f1caf4b66525e95839af1bc5ce7a7bb426567ec642adb17d47497b2eb74c6f3baba5068bc5c3f50b375ddf71927e136092bd978b9026a2e65770975ae2e2b2799adf2e934f1a7d9c2ccd8c96461e713bac26b52396dfdc0f11296e7f9294399ab1ccb7c2f0bb7ac605e15100af7619f337365ba32bc8465e8ccf7b2ee52d0edf934e3d829b72bd319cbd07856bac936b9f67b9aacd2552a9f4ae63bc7333c53abd338302f4f8e0399ec74aeaa31ab80d0e4875f6c26e77655269fabbb42994999f91703f6315ba59e969d59d68de1558693b0b2705a8dcecae91c8db2d17d9e662b4399309b4cb24ce6eac6ee3bff050ad2ad789ace9595e7e73bc330ec746358c9f7b2ca399685ab80d03bcd245486994c68ccae4c28eb7cc67f719ecb2eed349dd0f8097d996ff25dc2cf64bb9ac9719c9818155e2a365ba6d2d93a5f99e9745de59b84936c9de62b2f67787ea14ae6fc84dde56ac654bedde5d93d1d577e2e59385e3e59190a5de1394be8fb2ee324946561568663be974dbebd9c26547ecaaf92a994677765ce32cb64e33465f829cbec72e9329f50e5d35dfec169b6f3cc7ce5f8192b57277486d9374de5325e5726d37d5728bcc6efeba6c9742a6516dac65067f25d2a616620605fb24d93b942d9d8e94293f2fc5c2e67289b26749e5f78095db64e68eb7c99f29aa653293357a7bc3a9d4ae8121ac7526894edd9178de3c4c492d057d37c9df3fb8c59d7c98495cff975ae9fa6093fd7766e95cfe50bb7ce64db26d965fb894dd96d9dc9967d26e179e954b66f72f9769797e2d134ddf8d9c22eac54c64b263baf62627f7ff3534c6c9e9e69c2c976b994d935b97c3a936e32e67b59b92f6f0e2cacbe513331b1c2689c7cbbcb6bbe99e6caceefd385c2ebcaae6deb32f95e5659058456f832f1ec84a16e129a9c57a52b43dbe42af8649a2d136e9f4fe5f25dce4c1886420965b62fbab62ff61dd3846558866198959fadbcc672fcb21d5e4c13facead52293fe1797eb6cb42374c13ca8c5bf7f926e1d9192b95ca2598e60cb3f1fb2a57e713ba64b2c9a5f2bf349531949599acab6493c997653e99ac20b56fbaa76c7b2fcdb9752a5718babef3ac8c9d4eb6edc2949ff3db2ad9a7f37dc68473abf65c9aed12ea542ea1d076c94cc64c2893ef65951b7e4b737661f95d9f50d729afabcc4cf2bd6c0b6b69cef17275c6ca27cb7ce1565e59be97ad52e73c25cbf6599ab39c84df59b9c2f0ea6c9532ccf7b2797f82802bbbba846d72afef0c5e020a46c91e4b938563a6bc64c26c0c3be5950ab37bd13e3fe695e6fc3257369edf95f9ca2ff409e57bd9e8fc97179df3dc5669a509af4f27ccb2ae7209afccb85df2bdeccab69006e6fd09023297f00b2818add2b6ca37e9aeef0bbbcbd76d5f7655b64fe8f3b96c6328335e426798ef65a773950d4d4c4c9efb02a54f31316abbb2d36a2095e6ea7c93ef726e5726fb8c954b25dfcbcecfa894393fa37051b5254c0d948efd94a63b339f2dbcbe2bec64c272e1b40a539f533397fa9cc62e530a93d92a93eb9b5432972dbb6c93c965fbbacce7ca26e3a5bc74ba73cbbaeede4769c66ef27d995025d375ca4ba8bcaecbb7bbbc130fa5098d9fcf76293be1f775dba792e57b5935b9afb79d56033dfc93e63c33db647255c6f2ea3e63e5b2fa9c5738e1c4c4e43e695e951a952e976f777979efe45dbef09275956e9b74954c26734dbe33d49563a8528e63e7fab2abd3757e9326cc26d9e4ca649d32d48d57e5cca4f96c99f39c8432a1cfd639b7d0245debbedde585f8254d26ccc2cff55d26936f2c3f994c184e4c4c4c6c5a9d03944ed5aa5465fb655e4953094d425739b97c5b96e98442f7a27d6d3b03f6d125693ed75556b2b3cc4299f3bc7c57be97cd935659c4e632292fdb671c2bdd250c3be33ca7207565c5c4a27b7d54aa9953704e4c6c06eca31313131363d9b7bbbc77f2489a4e67d26559e7ccc64c37b954aecaa8f40f4d4c4c960399cbb7bb3cf98f34db67fb4c26a130f37dca30935df2bd2c605f95d9cabd91e6ca32e5797d63769de1f565ae807dd522cdd765ba4b987d6566fcbaed0a3d6ccaaccc745b97c97c6365dcc2f00b856ba94c224d661bcbee0c9d97ac93d9b6d0794833e95ce517fac2ccb985b2cc25cbf7b25ff22bf8354f05a9d2bfe797f315fcca835bbd17d284972cec7c329feeaa5c2e5f19caf7b27979d2ea07692ae116fa7c5ba812665b376e997c2f4b2d8c65177edd07d25c2a9df3ca544259b765ca3354fed1642e677985aeb0f3e9ba4b27fcfa683e93b2ccb632f465d9d975cece1e4d9929af331386ce4a66eb7c3a997c2f2bf827e88287121abbb23ccbc995094dcece67759ea3521733605fd56d55fc8ee6ec5c61e5caca2e13ca429dadcbe175345916ea6c67d9659973bc7cc62bdb9a7d8e66fc5cca4c175e57b985ae4e96dd8b4afff2a6d2b57dc3e63ccbcb270cbf4a28ac8c954ea6df3cb748321f475366db577ee5b86d6736669f4fbe975da7523e6a0bff8de60c33992db47532a1c9977542932bbb7c1baceb26d7b75d5d25ac5c57e5eac22c3be26bb062539e46d3e974e3a532c9645f39f9ca30131e31a3f984dbe4cc6457d79567b655ca6842dff94d4263189a8ce7249375c668c6d016ba6c994c59562a5f2654c9f7b2d499ef598d0101fba6d597655b11309aabab9ce5a592f92e6196856599ef650363c0e4175d88bff8296d5bdb17cda51bc7f152862e93c957998c5931313131a50a17cd2553765dd6c9c230f4652ed9d9afef6faec6693088c2a6b35d67a7cbae712bbbf333093b05a9844db865e7b97db2ecea3edfa42cc7301ba245738d9fcb6792193f6165d27dbeccf9d9be0d369f4aa8f36d675696a1cb77f9b2728b4229b54f9926d894974b99f95c9d4ee70abf2d33509a272d6459e65c8d6557596d2a67270c43932c737657a6d385f95ed6ed406dc6f3dccecf996595ce767e42937c2fcb4c2693cab1699365c62b3309753a99f1fc2697d1f849755916cdd6b92ea1acbccef3fbce4f2593ef65db166e9fd0695e921d0b6ccaaf5276e3a732764299cc765d4a01a7b3539954c6499b6d7219cbd0e71acf315329bb4c6a612ce7010a460b6339a9948b36a1b12c2b5f788edb16ba7ca14ebe974d9e01365d2534e9749db07366932beccad2681c75d1fe9aef2b3f93cfd875d956e942db27dfcb7ef06bbecc5519bb6e1b3f95ed33997cf2bdec5c158cf601ae68c2ed92c954325d56b9742ed979c9f7f635d7e70b75b2ef0b3fdff95d6199ef655b762a285dd90f7c4d6532f964aeb1127e26e1e77385f2bd6c612c77d86bb22dfbc2b2ac64b2b21cbb7f31b4f95cc6ce7966d977c92ad73829f3bdacdf3cb79052fb58107313eabeb0527e2a5717969570db4a01e526bc7cc62e936565260c65325b99ef65a382d43cbf796ef1aa3d9b4bf75d5de7727d3e9dabdcb24cbe97e5ab46a5801e9ccd5929bbc9f5994cb64a78e92a5de65adc5c57262bbfeb53c9543a97b15266aee9355fa7f29d9d2d74666527bc845b29b58f0591d764aed055a964cab15286952e1ccb524015cd18dac24fa7b375c6cad7f964a17c2fbbae6cde609f9cb7649b78cdf9995cdb778597c9e733f9649f7c2f5b9da9b0bc9467ea340e5c2edfeef2c8523461a7cccab09b64c2f1ea84955f7421bee6bd682c272636ef5103a555ea175ddbd7b3ef6f4e4c4c4c8c6597d32af5a3a0682ae17676dda4929de525cb5c97d059ee5dee093a67595905842603ef9a71ecbeb13319aff2fa64beca98ef65ab53a933a670825dd7e70a5d325b66bc7cb233ccb2d9395ba7bc6cdd670c6d6766bcba7c2f9b1c61c25d974a7639439fcf25749d6168cb8a28113b6767eb5c5d985d59e5f36d992c893a26db269fccb68559a8d2753a63be9795a57374c924d45d3e93afbbc24c99293365592a37f74cbaecd24dc231b47dd757b9aead9c5cb36de5776699b133998c933053e67bd9bf40c0bebce73c384f633e2accbe2496d0249465ceefbabeccd8c954b62d5d5c53f93a99afdcc26e0b9d5de8f2c9477dd925dcf21943d7a5d36597edeb6ce1e83c38e74901a5d340b69d7b6b325d964dae6bf219c74c794e3af95e36503a95d9d5adc99cd7956d9fca769d9fc9a5cce47ba5d524dbd654265966bcb6492553e92add6554faf7cca3b62c3ba2d9be4b16fab2adcb6c61a8337ef2bd2ca0740a75bfe8427cc1826262cf7910503a5513133b131363bedde5b910d14ccaeebc9ce33986cab1b28d61be9785cfd8e0e4ba3297f3f2c984b6ed9399141313134b2aa7d5c0c0b5b67dd71666b2ebba6499ca95655f1776cab133292ba1f19b9467e52c857eadade05f20605fab04148c26ab80d016219af2cc74c2cee5fc8495f39a7cce7c2f5b259ca3ee175d882f3131b1e95ced9ba37d6d62625331b1ea137ebbcb1b546bba30fb8c5fa8ec32d9e713863aa15029b3add99c593839c3ccd785d9e4fa6ca1694d26bc3e9d4be7fab2b1dc26d7e4920dada95c9fcc9639b74b670c7d2e9f32dfcbe62559965db2cf79300977d69c93c924bb9463e8f3d92695f1caf7b28554aaf44fc92e9764664d9865b6f1ea9cd764cb26e7d6c9f7b2792dbbac0242934b10cd3639b32d3386d7799e9f32abe47bd9ea1406f1a1999ce1a5bc54b6cf24ab5c63e6cbf7b2792f5cd67457786d5937392f9f4a25bcca7c2f0bb3af8726ebb62edb266578863ee7b78d5bb2e61b27619829b350a5129a7c3a67be979d56a91d9af00cc3cf96c9b64f1676aeedcbac1b6bb62ebcbaf20aaff3cbce4e65cb6cba3133f99c95ced6653a63a6d2c9f7b27c54c1c0bca914ba64dbe0e6dc2ad765f27d9d4a799ee118caf7b2d3be698b959dab7d531326aa82805f6a725fabf2ed2ecfc40b335f97b9b2ebab5cd75509afcc65fc26d9a4cbbeadebce4fa553c9f7bae8a30a7ed1b57de545fbf202a55379d94eab81c9792a7dba6f777974cb26bbb2ca179a9c97d0e5d319bf2edfcb4ee7a8b2e92a5d59a9846326738dd7559e676a9e3c13ee29d92fba10737262628052391a4fc55ce6db5ddeef709bcff6392fddd6e954c6b2fc7442f95ed665269b505719c350269c9c93cb151ab77c2f4b8d020afe11b2ecb26dbe32fc84baeeab7cd77976be4fbe97750a529949b6c9caf2d2f93a6196193f992f14e67bd96a9f136ed7a63bbb2fb49597b1ecba4bd89df95e76a96ca973cc64e12a544a75a9739c7cbbcb2be3d07c5bb785caeb2acbf0ba26932edfcb5601a1c92fba105f62625540a898d83c953ea9f1d7da0a7e8989c9f11c7fd1b57de5c1eedb5d9e9b5893e94ccaeebb4293ceb54dc22ccbf7b279723496ca6c3b7dbfe8427c997da17dd17dd1b57d3de7122d6239e6893026cb8457985ddbd785936e927593ad18e0e0df9f5833b974ce4ce8fa94a16c9229cf50be976d219deeb1ecab7cbbcb1bb121ef49159ce6e5756960e7c1f92f8f30068c90614ffa97b717eddbdb93fe6148e6fdf5f9ad172c58b850e102d8862f0b14324c582560f0f28a30010220840753693576e0206fa96c3334b0d04565c0a0957671655820c7f3b44b018404d5798fc1164b7580ec0304945ed06a276435c07280c05d3e3840286d518560c02f7e3e67bf024b65eb45a065f3dad92ff61ac04eb862616855f08100366fe6cb7310c002809682551e5815a0f4f9ee362f2f8ff55b2adbc0e74cdda0f32e3c9b9737e7e55509670aee921253f4e867c0385a25b6288903022e28f18e24b43333101fca3c90751863c2ef52e9265b76e52016b601c3850a605f142648f08a00e18183065d0cb8162870afca8a0ad2e3514948a2a69da75b0d506f1823bb2e0363d6c26071d1d8c860c5621d0cd4c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7c7548f8f8f8feeeeeeee8f8f8f8f8b7fee6cd1e46ce01e1f1f1fdddd1f1f1f1f1f1f1f1fd94c72f48265c1131c9fd2e49173ea0946e2d3047168ac2f6e2f00c4bcc3caf03ce359e0eef2a88db348936a2bd243c69df898b4fd9851f00708295d0210ab27a4498a7355ed1f43e270bf0d88be04ba3bba0b4e0dff1413d13ffa10781d3baafc0cad452e6946a317a1d27b4f82fc68061406381f1d470ea48a9227e374769f4b511e4c13937d280ae217a8e26d26a7d61e8ba4a40f61b4e4839c8aea2019fefc825a8a2d4685d0af9e78e93f83da8d7c9af22718d4b997137c7acd2730de8668cd675a61e0e1bc2dea196f8a38900f79bad195fa5a06e907b88af101d404d0a9b4f8f8202f9336a2169a7cdd9145fb0c401fc5804e6b9863f41804e4f01d8b87be2bc7042fe002d05f1959e0469858b91e76c6ebf1b0e44e2f50d74e52f82650551fdb62c681603ae2a6eef6fc88c4340faca0e119e11c7e0eb4b5bf62e4a2c1d40a7da84716f619ece3035d001b71cf941366d5783a92463d1c0913348d0c8bbe8fd51d971046c0eb0d52b1656ce179417d146c1fa9107c13e6a39b8d187f8c43246f8269f016beef2378c1f3443c197a4855d45711264d3b6d0078345ab4276500fe8e3c1adb090dd901512ab3612f841e1393f1194482e4394299d14ace0adf0590475e88ccfc4a1e69d1599e1cfe928cc6578c52cab72ecd692f451bbc85974f7ad1400c873184f2222a564734f54f2433c505d7083e2049ac9f42b6f90e16f038830d633f8686a12b51f4c7aba3dc5e4f852dba9520e6b9a45af4e10d3c0ec767f918ccb69c4e8cedffc011f4839a68f22eb7a723ba60fe46e5cd1fd8524a2321b3c60395b8f1640c817c1b2c61de04f6f232434dfa0322a28b3aa5f7196c187a1d8652bb4c8c197703634f130649e172141d9bcee7bcc709e62f377972bd30963c04a31ab7b220b9b7a25eaee805950f9523ccdf34a669a51905873036eaa3d09d39982ced56d6249f2fca12dd4903134f7248cca75314dd13a2b677c6fd782c475f76dbd991afd267cefb29c4c3b7442ae182756dbe2dae82970282f52318dbf82637b8df246660c36d59f4108e18fc14a0f92b9d803f4d16040f210985cff270ff7bad5fadc07a1d69edb3a88bf47f1990ae09717d36cd4c9a072745374300eddd48d2f468277e3893bd280f612ee14506157a5a8d78bbd8c2f748506e1e83901ade062ad1e574b92ea4c7d5063509868bf5b0da8cd4297ec04c70832052e611caaa9b8a8711df56c5d157696bddc3999c5dd686c6df447a3f076cd47f756af33f3a18fa073769cfe001a9d71029cafba9d47e15c434fb8b16ec2f48e5d14212097a24174bdf3983ba1a2118df236dd1b3c400f56a6aa9fcce1b86fdb846cf8120befb6e9bf82f2e80be8e2be6f39e193fc34f8e6f2582db90d04cfa4c5bd6341b28e9d9980174bf059b5cd3a3215a8be0296fb283c86bd959f4644ce6680c4156775447c3cf792be0560ca23ca42a6a5eabc0937d64109a87851af42b73b69e00830bfacbd28fcfdb509d88c5e69d4ce8e26495e4789c463d3a5359f2916390e80cd5364f264669c75833c8c718233c2439405e07a1d7ae0373732e9710355698343f070e16dda7400d7f468af4a5d0c0703d61c6f61a18ab67a9229e11aad8119f5c1b55863c2f47c6909d490f06575ae8f48dd2dcfc102376bacccebd09f6612bf90373292933dff244f4317a167ca866826ffeb9d16c2cdcbc4b05a52bb0597431af1eba31cbcac7383ebeb630e467bc1b8f224e888fa154c0ff48217a4a65145c0e33f1c1daf0790d1d0abe4563b309c9c17232779ff4073188bc51b07d091c38fd5888151b4587464f65d4a3c76f1dfe00a9329fb426ca4f21a3461b6a63e614380dea403e0afd509ac2afc097c45960a1a4351cf9431121db143e40f80b9274e80d447a78861b835e0adce86dd390a68446882b88d3635f69f6d5beb01e52181207d065c4db0580e4678ed2f69e18187fc1d5b82016b75766d0b28518d1e48d166dd898e806f99ae3129da46be352fe04f01041c278fca8833fd0cae185b83aec4de5e15f85a8342028aa2f2168e6a344a17918879074991e261e879ea06f075df03a557af61d9b3b9732a8e2a998ef47f09a7ea6c594afc8b0ee3fbb2eef8bc0c56dc02c1cc918acf724a5e91542d9d155a206ffcba5014e0a49924f1622fe824d15be81a40adbc0853bcf94f1e7a7a61a9e112a3cdd49021647347cd46e715ddcc2b382f3e950e84bc270f14408563e81ce87d77810df6d0c4ef75082f14889987ccb8f1c7fe70d8a3e5376c635a8781d00d1140fa488d595942b1e4cab4cdf0955f662dcaaff61d4a08fa509e090ea163fd1a24e3a4ad5d407aa0dfa4d9628fd913808f48b37e2322ae5f02fa0063c5fde95f7b9f3e3e91c83b80d0b00be1010d81f6eb93f8289be1b1b9cbc0c88a17f1293c70961aaa2b194d2f33472106a4fad299f5688a1ab5d765f236c8736e2e1f13d01801703888d5f30206a43c9e0744667dc4da9028dff7280d1cfec91e003e650f1222be23cb08cc65379b2f01c8c087ddc27e2c71cad7e9a26cb3bba51f18d1f903c03a7a0ee90c7740f3e2879528315efb18384f3b8b2c20b298969344be08f92c6e8092d8e7a3c84b2b8a03104effa08dc93da80f80109b1de4be5e595b479e29ab400f93f21486d405d403cd3853719098e9e0460fc7fdc0eb9d693049bcc15169f42c8e86a454afd27486c7b93a1880fb026d013909af310b238d1aee7c54f0203f285d6517fc7658f77095ae030ec323d29292bbc500e9c777ab1f0c345411f2472e7e12cdad987e4a2eec0cdfe44680b77ea61d8600a167d8e89a5f94c88f05e185e6c36c8479fa64911dda9098c9714688ff7f834c00bd5bcfe8639e4652c5ffc29322b3ccf22c19df594e27fc470f8282a32ef9a81d2d52d440fc446890fd601f0155a4cf00860c91f2224c0dfa14ae26e73bab411bf189e94f93c8743500e640bd69dd8bc791d77ca2f6e18e113da006e1c91d47c9b0057768b4d8e9cd25f04972b30c41dc3acbc91c5fba0c212fa8e130c4e630400e77418f656da08d141387c7d1c33547e754ce1a388043adc9317afc7d0014d08678c03a004613b2729f218c6de53ffacf05471caddc08214cf9084aaa5671fbeebc48967e129d07701e4f86c925ddc4d1371170941c5f32c8af91475671ececfcdb17658f81c869f7e528d1e3e6708a7d7c8017a451f4e36832b433f0010f5dfe0367c52aaf85554980e7de4e165305af2073634f99f235e3e8c8038ae46129b77257e3a6bc59a33b872f24ce6c5a74b42a88b5c52fcaa1e25b4061d8eefa7c4c706934482a7b821f80d40351d451501ff12b8c5f5d07d6d974c99b722d1e51d78cdf1218956fc881f7ace2853759f1c361ec09b612f72a0f20fecc4b4a3d59cdf03e6ed875458f18a4544b9162a87dcecca8c86ab91c01dd869b17728c0e129c8083e92366d1caa03c8930d78f48ab8cc741228505eb0419c464207e0190fd1e90010383d8507859fe08c0867032395cbc044e563778486b078c107f070a35d4880ce5649852e92e3c8d3e9c31d67f5f22d9d4d4f15c2e2cbb490d84b90cc1ffc4ba3c744cae1d10c78fc8524497f2678a6133d95720a4bc6fed1c9c7f14660b93709f004229979261cc2f8842d4f5e8aa13fe77248ca866a72e162e414ea387106b89a4075cf63c2df43f1aad378063f7d1549a27eaf47996b6541da7055f0477501f16c762ab8045379ba439dbc1784dcfa30364affa882ee9b4d967faa4a214ddd377cc99fe413f1a4c3377d71b11d9d017c298c2ce8c61972cea99193f6e2c6d3e710aca40169f4f0c5301cef56087344a9989e4468cebb48b1f308817b5f05e267031105e125143973233d347d800e558e486990a3913ada18eababc0b10175a8d233a9eaa6c7053b9c29cc357aad77336754e8aa49e4c5fa77f9228e6f5b0307a3c73a6b6242b2e5c8cdd98472a62f56f1c553d83ea9b2f72c0b081504273236c9d740d1f609fa790eb2d05c2ff5204b4452d177fede2f3224efd399a0d4a171213f5837a34da9e1cb8137844612f86b0f13a6e04715161d1cf90248b8e1280924f0131ea3d213a7e8ab2d4bba013f66110f2e833321b5ee1ad4ecff061c5ad324cd207e44cb80a4450361f17c2ffd0baba7ae9cffff852b01f04ee475ad0a3cf2ca0e1405c08700e1f2cbcdb9e258f47419607b123f338480c1cc129177b4b8914fff232f11ba8e4f932751cdd0b0bc64b3190c5a1299ab8922f033e4318c1ef4166c9c70892e0d7a82dd25132559ea5534a17b1f0e6a6b4f8f0186008ff920738ed6b82efc788e72ab0ae3c8d19c327da10e543ad01fcbe3eaf4faaf2453b6221e47dda1e780c17d2bb9d80ecc5050d9e9002af77806ce148b25268ba176c0f84eec276a7d0bd1b24921ed056f42c8334db4f93312e620789fd4c18bd8bb7197f224720ffdde78dd099fd87228174054351fe07a23b9fc161d45f7d2df12b8c72cf15484fe30905c30d49987a2b5919dccf1f4c3ea748c83301e2c4376d18f3900c08be2119ef6f297bf48ad678f1449ecaec297a44de041e473ea92cd1f714c5a6a445f872cd2ddec94802dfa204ed1d5350fa249f0b9f4a0e228f43c6884b7924e62f34137c120a91be4ea7169e1548c22f32936597d179f48dc0dc38a5401abec905cf8b2df8f36e4910fd4fdb06577ca4ff9de1f822fc7cd8807d5b3cb0818b97a4e2d00fb9d2b38314187ba710e53f8461f1b7c43d1b14080c4e28cc9d9704278fc3415bec40056d5ea5ab8156d158e6e500657f6835c609cc7f3e58a3ce6fd138b417d838f537fe9ced1d1b02fd0ebda4b6506a8fbb31a2c31d20ea390c4c18fe8d20b04775e66d1f22f3e09dc47835125ee045b64cda8f8b777ea5e6e9796860e3cd0731b6a31c823e9158904d6d23e0bb7c22b3974c09fc1fcd27cec3d0eac92471b4f71e4677c1c3f9479254f0497aa89ed181355ff782d23b90c7fc9448371cc016f44bf880f80dab009e64d281efcbd0e77e64825eecd2265790167d519b60b81a2d389b4ad48e2b4e9dfb0aa8020f44c899f6a0e6eee9d212f5a115775e4c140b8e056fc790f7f4ba4c5fbcd985c136d381f4001794ff2522eb072141743b2d9c57fad3664f3b21f9005589b6110667de46c175167f261e8e1d0deee5aef18fb0993e9f2f699c6a0095bb66633e54a0223a4597afe704488b9b82b39fb003a0fb3863c8c9b4d06c2715389e640f9957b9dae46c1c08d07308f4de4f8d146fc54285d3184bf31374716f4a8bd013e0b0e6bb74e5e90f98d01ec40a43db4d9e16eea30da5973aa1f25b529a8dc44c936f03f3e7814c1d3901071cb6dc9920cfc370d01fca2b7b418586f40bb4508ed5a3d246400a84ff20c3a7cb48a2f3b52f40b61407dc83e1cbf4d1091e3e019f020fd608cd8dc039d9360cc3bc0c4219368e30b2570decf05402f6b8544a2a8de7efca0322054a675161e68b1c176c1d97723c803cefcf4d51f499be5278804c509783fab999e67f1a17e0ee41c1cb1bc9d3403f295972367f56ec3f4407cf05058b6fc2c4f8238ed8f90069966f8a44c5497118f17f9b0cb71b38c2474185c5c75812eaffb4f9b1ff2af8c7183bf3837ea6dc009237cffb217b8821507c0600106fc9019ead639191ebf1a44547e273e1803502bcdd9f0ccf0608d34bd04be53116b079a13989bf71c8a39356a1dcdda148eb3003ead79ce9ed4d775aded3568f9daac0daff707366a7a944e7f954856c228553fc52111ede650ad02d5881bd1b221f3c540949ce61480957c167ccd738faf08b4d289ac4571eaf9bbbec2f26b0bc6c02dcd6120a38140a72bbba22f1275101a5b5ee3cfc20111b7492423dff66c1fa61ce7078b22e41fea9d4e20b7181e23b392678023cd2f4010d8d1f8909751f2145f65860e76e5104c43f17d9b4a18c447f269392bde9ece97152535fd7f6e2cbb4aef5901af234d4b8fc29ba137e430b0e3f84e4c23f792368cf4963e33d19cab4039888a87450cc859116c4942106004090754303802401e312004048281c8c05e3e168481636ed031400044e8080c042994ba4d134c67110841031c4000380210418409099235b056700f75368ca0f3fa9c6e5252b0d2a9572127ebf967022fe21c25aa118d097ba678afdc727aa2fc0c4eac6e8ebe8d19247f1e0eef2fd252aab891c2881f9444857e5d4427c6f83b32115f43bb99071208d40063e4ce0dc323fe6d054b4bd558af163fc3364507347bc705a5a3c25a322013d7927a0556c9cbebf1b910ce3a2a5423b28604706916eeb14109b87892bb05d60804144fe24de39b96d5f63e5e158031fcd7a4f87671a650208cde085df6bf42fda5156bcab84db86a047558fa75a5dc6bc7654be54abaf8267e4ba731cf030f7cd8c74e313bfb71f2a1c99205c61a853f71778f210a2523db1a10e35735cf89d23ea40760466baf2ce54fcee8f14ca14a1875569b53a36b7bc78b9e4bde653b590d7e9c68a9cc0660cfa1acd5b29c6bbe18590317afe76ab96c10de01c34ebd509244120c0c0d6f779971e6a045fcfdd2f66d9e6e6526d4eb1f1276f9277949057d40c78d8af40a57d4b5723a704d1fdd626f8a2b3ffe9b6bc3f2671d40fb8f4f57c86e63f39a49e1a8979eeaa9675eac00fa58ab56303505eeb6a6613cca02b34f0f1ad149d6fd1c58f5ae2fac7b9ee4783a7b662f9b9da755c29fdd550601aebdf39bdbe024eb6f4a78a745b28d62cce693f8fc268ce93dbe0f927427b1f9bd947bed243b8db8fdc93ab6d084574f5734b13438960354650215d6c4508d2838c27c4966ca39779027f5b50b65209bfae9baf19201454c4984537deca969b238b7f024d7dc2f88a8bf817556bebaf740e1e56e5cea08d8644bdd3fbaf91e16dca9f61c871bddef9061cdb2737e6d7702b6e0cfdf07c009ddf80d6b77e3385c1f5cd6b20903cd0828d9bd8a5d58758948b9b7499042d23968f361b0c40d1e40d83434f9c1131b5c415e30427e707123e4b8cbea6e83bbb3a20708a3c2955a40bc06181d684fca3235efb968bffeba93cbf934ce4b2e7900d1bb9e1dd2e4db60dec7b8ca16b3a6c598bfb8cf02fdac987d790b646f1365bccd691cc1e7d171cd03a724c048598c520386c6c8e9b753443f0b03b9bb8c4c9307ac78de37d3a86709e1e6ea5a48b2ec78c41c5008f9e868cbcbfc3ad677f834ddbb31706fdaa10d7490172c0e64c0fbceb47a79eb82a43c75984a2a88e4b8d215a975a41b2a2bc792ae23d2acccc2f7c27c19598b07224fd1676f2ab3c21bc5286adaa646082621b1d0c7dbcea9f1f7c184ef797d0f653243cce07870605467e98cebc6bd118b65da1e3384d6770be1160f7547a12d699eeb89fb2068e26046b9f68f65c0181d684beb717f87b853e185d3d2559f2f70e362bce793b6e686b524b267183d8e41451f04831f35a06aba2964b41d4a3c2b3be855d756b45cdaf610139e03e9d131990cd8b596445dd37e88b19f337fd7d9424b4ce110c17fc2800fa5f4d2f7101810208f9687a3125053dc4662294963a021a4150504f6a1452d9908f43404cc82c0096b67c529c076e250812ea2b6e64a7a0da27d92261c0c02a4a135f0732dff58a980863c8ed23a00b0140d6bf0becb1d19914a5c6c602ca9b14f399a70b94c38de06a33b35ee5b83011331ffff775876c2a374f092918f3c19a278f9ec0c54ac86bdf8b18522368f8e4355223e660bf5912cd5610c00977553466e9765fa85f8a6d207708921a865c868b89ec097d9cd1de9f8255f01cce1a70474f6b15d271751e25f654277edfca1869943776eb94e20ff066d3f06b83966dffe6d94b9282849283d86351a80cfddbbb8e68ada57174eb0e662151f502161fc5bea76ffcd5fcf6c75117d9e3e76620c43ae42934a5950e1ef129191a930d5e36f9f18b12de5fa48b54980d9dd351abc9386b45b16a705a1ac427844b1cded3471e1703ec99195a357797479ed0d4525552d69209fd6197341ba8abdc74f4f03f5d87dcaa93403793686ccd60722cd2a9a5378be4fcb0142e2769844c516117ca66c61dbcba59c3dadf8d1e21d1644548ada602a1ad728695f9d54a83a53c04ef37c5ad552b268ae43a8020104c7802fb179ba886b170b562616549a9e6efc9044ec4e3062f40901253bbf4c3a55a3a901c0c9b64ea336c7160471caa4c11ed470abf9d6013b1c56cf31ae91837634d5ae32b37a498a963a9853b04484af70550c6b60e47bcffe1c5dc3c429ef8abcba494c5d1d55a5b7bd27786f625a97d85d4eec2c03a486957e13ede5978a5b41553f9930773004443caf03a35a514e22eb9954ac93478ab4c43eeeaf512733287bd56e01999ca23ab0d3984ab91f17f7f1fbe9d9eee6329e23cb4c388d11ad598f268a1b62638a71bcce7d075d9c0d1f3c5defd4ba6f9fb8147b88534683558c43e96e49210b74e928684a25433b66b0cf4a69479919a5ff0bc350e3606444ed449cabaeb784a895e5eea3fde504df1208d32e169d357e71447d456d1ce53563d0c22c815a66e236e6aac6cd08c38e90b518b29321f7a411ee2a3408969009210940f38f5756c10a7c87144b3005da7c20ba8173bfc87bc200650279da1c73fe1b4d2584c636a527090a3e0f11fe2e333b29a28043fc21f7c085f86507116a9051bd34819f728b556f34d8e778208b0e3639b0f7b04e12dc987df85ff665b2a789d53855f2644d07c25ee904ef9982fe695a43a4b0118122126a5dbee010a11c20150f0d05e7a4b363a8f98485af380d7d78f0d88101620324075a5f3c758c2a0e15e7746992c85e940b34056d38c5025d1f09518ed448f266699fae4c7c38b8fdb34371cffd39c4ca815aaa78abcd96a6e0dedb7a5812aaa7c29859a934537f5100451e1c0bcf41fb8b210c4ab6f769b8da8e2c9c9f643fc93a5536e29a366e0c60148c16fd334b21d2d56c146d102fcc1d144e2d44342c4e0efd50def43179efb43bb0b47eb5d9818446a8f4f48eb4fef06650ed7157a6e409f1138836122cb36e979c008837e4e0aa8b9c71f299506152acd32c7f4f636ae39440ac20cb94fbfa6b4a50052283e651fbd687cc5b76888fe226c65362d3dac6fd173b4bf85f4e00bead9eb84a2929003739529be5173a5df65c184086c3e5375292138fd12cb00a5b926c9be1bfd1c0831d493f66db0c4d6c050e1aecc4d3937cdf285713d88e62308e0b429a2525076943577a3eef511ce89b4aa4f745c929d085a10bac4a35bdc6835b973682577728f5c6974a4c5cd383b21e70b8c8c2c076ea3ab2ac996b29150b3d5b30a1b3909fe906cc37237dee20f85c98138426246833954cb6236033df533012b558b75b06e0f615f1a63d46854525dba7aaa292dfb8a96705fcbcd9d24ad7a01d137708f1d226702b98bdbb08c19ade0fda4e52529d54a711017fdda315698144304a0189fe36975f3c5aefb87fec83614dc53cfbaeecd18e54a127f532446b1a44e7f9754b128e921570711ecd06527ed6af9569d2f8de0230fe58e153557b3fd9668a3fb16808d4cbc424987d5e931c5b381ba9e92e59220c3fad18065a2bcd1bec104c529c423c551e1852a7e05045417c8a30afc5dd2d41494d3cc609e5d39f1bc893b9aa56aed6c2794a981e40ef8b3e861c7511249b5516392443c1954b818ea7927059105278433f2e4c715145910fbd1773e3b321ef4b98a3568fc6fca1f8504391ff033635b9c2b78599233105866858a0cdc6c0fa7cefb2fe95d9d6c5cc1b32c3f5449e2377fc12cc43321d2d53186a8c23d4997ef7174e25f735d0df7bbf74f81945a5eac76bd66ec815b5acb12ce328cb275a2bc82d85595d11741ce3f6938e8fe1cb35b33d3dc5ac3fdcc18078262953e39558145ccd1008423707085111cc6f789d3d82bc83b5cd33ed9f53de2529b3c02a45ab8cbd40b4868c732458a3c7558bc2f00d0819800e2b4acd753a5fd00a06e0ac0ecff5d2d5bd464f227450658ff0a4ec7b068f069661f40694c981d09855021116e0b8c562c51ae6d30e4390a472ae18f4a1d31007d36684bf337a792a1c94978b0f2f845c2d66f9f6b09d73afe34ad911d7181ca146098064188f52f18deaf4ef302a22f78ede3ff5c02ef9b87f033bfe1024a0ca548d80a67ae0644251126c7b8c34384b1f8af717948d4cbe29a8ddff1ba7b2a89b994f3f71aa2343780f0d43aed246271c9f1fabf0f6d6b5696364261fc0bf5202dee7c6133b44f294802a6e6a104c9a3cbbda52ad4b86aaf688559d1b7ca698670d86affd38cc6c9a2aab4e9f1139e359dfe2b6778908823eb1029d647362499318bcef73886154e0132296df54424746f49ff07d70b33e9d4443ad8760e76931ba68ea509eac3b1d7533eb0e426a5d3c5cd041b30a0be55bee760466cfc8aa0ba4b84d0d4168f411b3891e3153345f8ce68d7e35d8472f741f6b8b41e1d7226498be3e8489c9817d78191d3b3061163e6a32c55564acc82b9b0ce4e6ac8eef11dee20e4dae6406a08b49c984ad28168d476bacbbbc46b0a8bf2eda942bc1e2c63ed7316fdd69d9d99ede895cf3415f00b4ca6fb3ae27b3bdf71c027937e6405a0510b44fe0502d0bb2cd0509f8cfbaf837787ed1d7fd7b3b53036fbdc766faa5bd57470e41647b379e0ebe22e8c4836dc47f1d5ad079571bceac9e9dd8d10d4848bdaa589317066bd53f8c5e55265d091618fb255b5e1cdaa5f061272dbc17b9317bb08472ee0c03a94c400e3ddce296b4ec76440c817b1f78df0827697398e54f7117464a5251731e8a3cd653230ae9355fc72fa61f3ca81ee8aa3b89943ba41718b6270d71a047336693e9553aa820a990d39d4f65ea86e158451b009974391ee1a2ddbc2a424c9938fe4682476606656de266d11b45f399be487105ffb7d03ee08285dd7361042344cbb1d6692fb1006d7ae27b18d01dce7495d741212b94c4677fa8c10d3c864a7916e9cdc713130399c49162801e1c55bd26bab05802002a50118a328568cfdd02f030fe037db507e9c2a2812eb311edbe1ac407dde3bebc4edfd824784e6b49bc87a35b1a131f1884e510eac208297e6f23eecffe28ea61480ba8940891ef9cb0b53d440ec6675f26e76eed8ff61aaa4ac592fc1547fdacce8d1f3a7ec29601febc0cf2ca1b582deb00c5271a06b86278dd0a16f8cc9bfc92cab8ef4924d49e53782dd94bf64586a4d655cc827ddfb0e234784c2e617e15ea9ab38357f6e714e9906a0f91be0b8544b1b15a76ff7569f64ee324d06fe5e09e65ba202a7be0064b1e36fa534419375f0067e068ec2ceacb08e01569b9f16a53fbb2646e3025163498aaf196952937ba294f834a706d52cec77f15c451a3ce45cd08730303f64a7c8f9d2aa7244c22dfb6e5a3431d44e4af6f4ec591990cc2dc4e96944c440a0111e25a5c00ad0a14b773c83521e86fe316bd9dc29b3f86aa602bc8826bf7b923ebe2987b4b2ef4ce2ba997777e3a4024e7f3747c2da7b80362f17df49b38b5b507e88db754048359894674ba0a15652f03449587cbed4b194f3d337db4ec3142b3621d89f158df71e30eafb395dd40dcc416e8363be4bddc0ab384165833f102e0c4fa9e9e83901b2ff83d84ec125733a77f94e91300fb633ea3c9f9dada10f00647af963d32e696eb19d40a1af0196388e9271716461fca6614f9856ea00fd98a6ce606c436cfe5271d87813940c316d1faf70fe717ee1b5278dc27399b83d36238e209886924cc093b57c62fcceef85e4e0562094e0ba17eac8afa83c8f622d9af27695575d30287e7b21f6e2f607087924687a4fbe020329cdd07d835df373cfc4f3315d0048a8c7349b8620883158804fe718d7d6bbbbc08481bf49ab3af8d5a84e5b32c852cd1b521758e4812265bd2267eaba6fde12ce21e2a3d433e994e7c5bb911898cd83ce4c197f64e037128c1066f93adecd1effa7b1226f79fe0d409b0afa9cdf0dec5ad67f13dd9b1696accedbc43f94ebe4c75cc12831e2a21df5ed70b8826abfeae1de660fc0982e5dd959163041a9bb0ab39fe294347eeee56d844e6069fc38a481751d25e402a34c7f6d8b6aeeeed0b9a79d4466d39bb65517d9fddee606910d3266f9a32da5af3d79e0ff1fc9d874e1c00d87659621710a0e3bc690d8a34e247afae7976eb1d9244c4f994cceb71a2a29668d7732b3ea44ac5f9a938189e4d4a86d9c51fe2152d21eba198b5d84f92e650914313ca0f70fd48f0038c4ff7e82d58917927434553801a8c4a4173613ed77d04acb02c2f1410f36eedaf881bb6f42892743c8feaa714dde50ef5057280a89c08f8e137813fad640e03e1791aec93f0157f2adb43fb2f9c2240458d58c90179c256d0bec94879b02e89c6b1b3eab7b86324864217385bd8d8180acc16d03aa1bf6a54cf07b02cc9fbf21175732e958a6ef5172e097f99da8d4f4486067f07a08fca2ade20defdcd3e0844fed241987e32e529294bfc75fa7c10d1e10a9007c37416b4766f58988ff3e049466b06088f204867a0f719b157cc4bf841d0bac7891e94ba168e3dfe3bead167aed1057bb0f5b50fe83c82833f58137ddb0290d4fb30792a87202b510c4efa7cce450dc98cf09189118f63e793738bb583ebbe66e2052fda7d3f442fe33b343d4f7bb76c84fb99e0db2906f7c3c5ef0b8170f9743955dd9f13b38d801beef4078d516c99ca2f12e8517ee65f79ba0ebe8f6469850cc95cc61dae743fb9da3ae3be957ece1fcc88c4b3e1ddac3851ec5db23332317bba94d71bbf96189d8d7489ec6b6818f4cc2a4fb7d692eff9df12f86a970b7c4b7aebe97d35739e0a99a196adbe7fee83e33e66aff01e178f6845c9b547957f0521fe6bc44c2f659f7b7b0339bc524a6e9fcaf48cff52ae48dde04b8ed11b092620202c243a8602b6c0a2ce323670db8ff49ef8c411289d0adf6f3e051edde847be34c59bf092b5ff2980b17a7e5ee3a5108c6da72ce8ca8116f9d6ceca3d0c123120ade55bdb6ef506db44651f6063a2fc20a3e3920515d6e73ab34812e51d1c9b32b5b7cc5cd33106e44b222e16b78d663d3f221956b2afa3fb44e4251233baa77ec1edd41ace337b79145ea88a8616946fb2a0448d7c1c600a5b0628094494029b49aa8f12d31cab987bbab2ea8552cedd9056a6515bbe5b44a41172439f5336997d14baf68b5e7dd1e8e35ec7c0a27f21dfee8bd90863e85f4ee67f427d649ff3c06afb0fa77800bc1136882c6249827ff2e95ce3dc9557477ae0e52c34b585db35ee4d5a9cdd3a9807418f6e09d96310dbb4f57cff88a1e20b2f3931086dfa2feb071e259a511a851f47285f01b24fd9b2d92df52a5ec9c69824d9f63318ecc17d5f8b78e7e38a4804a0f33d5285563a4ba5e088e62735f649c6d9ff03c9468f61fbaa2b0d86d2fffa535fe139e33f5ca9733f67d8d3ffa04ee157ef0039fc2b5f2a424783c4560686f58ee5c14d04691ca10b6cf767f80bc597996aa9d064790542b85dd96ab6794e416f902f45d92d49f202b5c528a27acdfd5fd0ce7bfca6febb3d1c70f9bb8b38846bc02bc1be17eb7fc87bfb9560d1759900cb765784d4ba81c1eb979826f1642d83b1088c7484a14e5be9e62d3d2a297a4077cd7f03e024ffeaf2f765d49fc89fe55bc632b0bf6742cc423aa09227accfcff82f9f3090039be185ad1f91394d1753380b8a170b2e757949f83b523a0c640f176d87c2487332562afe3e7e813555d5f6b389fa1da1d67c5be484e26d31d519060e25b0c4111ad9c0ed9f0bf9ca403bffca56b7e6bdd45ad9d89395cba4ce876bf98b167cf6f9bfa56f3141fe30ce9999f2b24e40ce7cba432d11b79aeccd23b4436a9f3b84e90cfeec323d86fe3cf3d6a451a7936c9f725a3c21884cceaf4f4099d5d134f4c06c98b22a8a879c67aaf922acfc4aeeca6d801cc07d8167d0632f9d5ae7053cf39fd56cf943359fac5914dc369779bebd50e7f1c21d70325dc8c21b28c6f9a66e83fd042e4796d5d361309a4e5b3a2ae2391c82907bc4a5e4b71abfac3baecce17e723d31cc53904c56258aaf0c42d959ea1da619d7c67b0e115187ad4821338c6df5ef97d6f7bd490b33dc54dad8b1d9544442c163e3420b85e3724713b445de262212d78a2f84511100c7d22bc83134b0d9e709d747a6cd0654c8e3443ae911e4fd6632b25e97fc84748477b74793c55d6d92f68a4338da946b0f9ea692408099027fcec70396982fee43eed6e02ae9dec6d3254a848c226aa373d6ed17fe45b1890fc148baeca58a4f570faf7a3f93e84a9d443554483bcd0f64a85f513680c8c1bcb0211a9a5263d79fd404faf85b1d8db250f2ac66cf5ba89384da099d0c65dd8f19d4849ef6c7efa90cc21e2f1358a4680eb24a976404cf85c25c2a4f1c4609288084eef11d1123ca978a62bfb01d413b2bbfb6f49daa22f504211e65f3e520318084e159d9c32c25ce0ed2371283ef50eb243cd3da07e07f17160cb71502d07a8fede695d891a2d54e2f4c9643c1f24e30b740b3e8bf30e499d294eb3aa7df377931b4903d7fbd0c052fc8e1a922bc0f126d0283979de969ba072bb5212f0d1d2fa6596170636f77bac1c3e6dc25c79909363c5cd0247c5da8f36a2582453539713ad8cb1ebe60503cc9b04387a7029143032e9386d9db16c2cbc2bf4e590b2f2f62bb1adb6c2df9ac06ad6bbb470bb0116d30c7d51e8517e4d51534f7451d4d33b823143de48e55a4c73018289dc4cf53213a9d51c8f52ac8643dfe3626a646e9de3dc972d62d3147d799adf1501c877f46892d1c79dcea0e9610e1c0952a33128858b692ca4ad2661f79becc9c2b649f003830fc815fd83b191b22784250fac7748e0ecd85938a099b98f447014b501ed0b6471646120ce6c0b5c1f58cfad67dce74535bcbec0933b4d125f4feb1a71f5ecac7a3ec465f68b83ea19b70bdc6e0a3e8df15309da4c1fe6a6fad6d8996d2a97f458a6e4fc2da3014ddccdfe5fb0c2f93691948358ddf9bcc90d0e90d7fc942a93c87c1e745aa67f3ae663bec9b3ff03dc61fdab8c3dbdec29bc8b25a4c6130751387358029d25ccb3a823a789efef815af50ca04f453c4748b96af9fecac914aa3273a17054f3a1a483143b7bbfebbc693477296b32c2ab3d142da774715caf07ba1bceccb68e290028241b6d782060f125cdf1b01dd6b6abdf27b4bce47ecdb573fb6fb669e6ffd790ff9d4d0a6e9656e7494fff423d57ee6f5d56566bd92463ffc0f3c777ee82ed8fd48b2a6d8090a3d636985fac4c3e6c577ebbe81c8cea336949c5c36a489ebf75ac436f9f59d718a7869ca396c10060aaf8900740f794b39206ea437e3acd3873d74cf5aaeb41676e453828675b5f676657866eb689859dc5a0d7e212f943527e15bd5f903801614f7cc3b07bcda909d3ac6328c079a00c53275fbd32c3705ee1123dbbea5e152fe73862fb03acba563c884c6c1837549b7ce88e33ed7fcf2ce8d3adcfad9e02b9024b33fb7fd227a51744bbf53a2ec2c4e1677f44004e2d49c849dadaf09458e52e1d7bfa79bacf3cb4953a550fbd7ecb8d6365a4e744376849804343d24d5f2f984946761162d31952442525d7f43955daf35e0c381c36a3c7775eb894bc428151b06d46d35ae66fba93fac0fd229ef7ea36be9a80b7821041147fded05fdc70c2e4cecdf8a12262d720234b3b8c76f0cf3ac61480a47f7fd92f7bfe28884f981fd478e48704ecb63cf95bd6fa1ae67e42cd7bbce67657a28e200476f44aa29524b916599dd10187faf039ab38c209d50e963591445e153019f7b116877b34a245dd0171e8cea00083b0b0858e5cef62f589949ee81c62de51505c82fd121e54355f52f07d53251a0241b29915cb18b86a362da2bcdd53116c000d4a2e5edcfd595548b541b58456167876142ff6b013350c866c7f25902539a33a7a6258642751d03812fb3e0c506e5c24f16d4365041e4431b354f6f8904a7a2242be9d139118382a465aba64d3a5926d01cd6a1e4e3012d0513af12d065e870f979f2d77e0f674db6edade65db8bedfbb5ddadbdaab61d6ad7417a83d8761f1aaade7c9588de35ac8e8504a332b2a92ee34155e6a604694f8e69517810543832a2431614e9997d11fab589700233153a9aaefc3a1652b8faa88cbab41d4d898305a47135ae45e1a9c996543a220a4ae331edd4dc506292b2d838645df868503af32ba59eb43e59a422934577d0e4575d468134c34153f6e7b5eef5b62bbc520ce09d6ce6a32638b201619bfb5ebc41e78ca5058eb8abc038ab485d733d7ccbd81751c08bd977f20090120ac932b5c9b38143e22b178f044f0dd4a28c9e70072e9e9ebdf4f1524299e0437165ce6e23842d07b13611d4f1fda577c5a9c061a2b9f3b862e83cc74d764aa02ec1a93d07b5711719924409a233bfbb6e5aaf0742a161eb1d09c5cc9f155c821d93583a705b26583b7a6ac114efd955b311b7b7ff324b2bd49c73ec141ebb41ddb6c74b522338433cd99db225d49a1851219a78132eb0e942807f850b343560aaf93c83d3945324a66b160252e58c83e60718afb34deaeea2f368d84c55610995cdd8927fb326051210e5248c3bb54b16ad3e1621561120d0c2c6b146d1d3ad58fa3e395013f1422a4b58eb25265ee1757d2fe853fadd7f7ed0cf8759b96f709fd8a7d23152ec148c88c0f49852d0fa090f3a3c2efe0c77960f5da753984e103ff32f6ded63d89ffe149c8af706353c7c0f7938062196400b33f3f96e3b53ac8b75c38cf81a1b667b51e9f4d5e5609933c5bf36043b950107cbc960e93ad42b21e56201ed6d13b797184782cb586fb6be1a95b389794a571ba76eb90358b66b9e499c3f6ce0a61617954f054e2adc4125d45da37b55bf3e4e5193fc32500afa029cc43cd5e179a33b1f2b82627b45070e8de853e0154e4cff2f2605c58f53a0f3ce3ee1d06d3b5c624ba7c24c6cc955431ea6adfd1848e9e40487ec3b23849f2bfa2c1ab06dc34229f775be49b9efb203920e5a3bfc88903f95c0be71ae19a9caba3d84c7fa8d8f9cb5ab9fc7c91cfcd83b23ee4cd988ce9c5bd7cf09c1db43bec8a81b624ac20703cab3430fd61688e4bec890d299e9278cd096831a3e33cdccf1666fd271911caddfa7a372669931d266078db64813e9de120977fc50d81c0fa8a9c332c12dbbea37aef753a82ef3152b22f2a0f68f28982f9cbc8ce418f607600914dc86d2a35a4957764da0bc0318a32688cc4000209698e4393cc1d71de489cd2f817f21536714c08ef47a896a2575e7317a9219b51c06e06d8eea7d8301bf6146affa89858aae4b2796213d0ce4a4779a7a8a7de482085fdaa7e307fa16c206a9370e5e77ee1a0be7a7df810149a5f58eda55d9efe32cd69823eae5b68fb3c6a61f3ef0ecd316b95f98e45e942a9025403ff310aeb567f01571418c44d0e03ef786be70a2e9ef0b1f25cc3ab81908d618c028241518c4a5dd23592b1ceaa7f24d3e0e7a8a83933d8fe89c8b38878b0a243300e1ef1dc940a0c833f50239398bea9b43c6cce296935fef10f79a1cce94d0195ba0a020cd798d3fdeecd79c8a3dfb4ad4266e21b1fa81329a33fbd9a3fd7ed44bfa96fb4283fd62a35689007ec77ef5c0ccc052077157bb822d0b54f2816d3bbc177d2202b97dee9ff6187c29fada2cf6363dda39132e3d9a04df8a4bfd1a1cbe7bf18e538ae60455e8e17d599eac63a1dedf74e5043769c480f0165bb79b386328da5482f8a134e12f8d35834baf3918c53ce498d0251872c0c51b74efb00428faaba396b4eeba412494d2e11c59a2b1594ae3b426c09d1ead760f4b11a2c0bc03025392ab5a6c9bbde1f0cc59f03597b6dad1a860a1ffbb2638b6608ee072ef8bb15d75168d40d29b99000137da004c7c27b077fca916dbc27c8b215345db23dafaf0afd2699360ce95df92d5d7bd2838e61d79c6ac46442b1cbc41a9d6dff1109b653341976c012ca7017ae21436e3db2989367c3e05df9c8b5bb94ed0b63df6e77fc4a9640a220f358a6630c21fe090bfc2d440fbf01de4e1e62e4793fc27666fca791fb45519c2252cd9e79c4cd924b93845be5da954d125b4149d361d37be4a96934233ae4c1a44d0699d877490f94319f9da737334c188280f03c1db314a609e99382381721b12f5182890d4884a7571e7374761b9731df0f1189a9d9e681c1f979b99347118e142f67d9e3ca3a79effa7a3e2b4c075f2f6ee4ebc7f2b1c7126a0eb135e3e7d330c2b6c871dc4da2e3e72cf41b04dea0b1547b231f574f1572b13109bf3f77db666d046d9aec7fd311f45418d133dc0152333a5f529d044a012d40aca1367856cf5911e71d8ce9f81dbc34fce910101594901d190afe2fd24bf8314a3b36ce859a00288313f91ceeb00d83f822b42b30abc3ef234c2373e7bc8fd685c3bf2bba47cc14bee2273f462adee91704c5289c7c92170ed12cdd580fffb4c05bb3e7d7a20cd0d95cc830261673552294bab70425fc29fc3f8c2d73bd0ea5f6ecabf2b3b25105a102f8d6571b7cb6edd8b83a58b7922ff243a32b94f1b3bbe43c56df3d0b3163b9f4eaecb434f6b0b065256f1b77eb2f3ac4bc0fb2e3077d7b5c6b558910c2edbf180af3d4c741eeaed09b7a6b192d6a735d49ceb33980025bbe567f4a395dba63553386415f90290b51ecf2096a34c864bdc6b79cc9388ed07ce6806a031dfb8a0a955ef1707984061ddad55e98cc73b5c6f6dde5e04de78497131a60b96078f304c69e1cd1a240e263f9f7fa45c6fc4bbf94b8899f79554accb38674f3be2270371169b40cd10137a9225ea8e9caedfdb72f36c4d6d431c7d01014cd2911fa547391e28356880bfa2fe0bba964cb2c977330b371c858cd9c666fc9248ccdfa8698f689be762e7ff80db47e14f0ef2d87239c6bb65a41660973fbb2cba489914e01db35835f64a46a360c04661a4779bd944ba9b978e5a22bc26c6b47f01e83b7e396812ddece61a39922a61ca93c8c8b25fd3ece57db0dcd8fbdfa1099e99d0c9d997e54943ece5f68ec8c83a372ec80018de7d4c84b0ba9da611c6805b67f614999e86f15e6bda5de426a1fe239cef35a896c68cf94be65f1e558bd54d0d85b4667f35c9910ed1507f9120b16055c38f41fc9bb8a13fda6a3bf70adf5cf6beaf567846aacb33035788fdb0481cdf0457cf27f30bb3c3667cd5fd617ee2038ab17350b18a048fc68666fee08c6f5b6ac2598d77c9584b11f950b2693970afdbc2e7c52f30b30ef8045aeeffecb6b3e353ec3fbeecb9d80ef8513a983501d752cc56ba39d607c3603074120ee55227d4f34a9e31cae7d5a2c6fe582e49f8bee1fb3714e953ed9380e2fa4f8cb100e8e670f2def4c582ad7d96b5838d9972c83811fe93bc87ba6fd341c02aaeebb5174291acece273fcd263d6593881b9d9ff7fe452338ff874beb68526ce3c651d501cfc5a006fe8db66d1824a28848bb8477fcb62d3482e8bd3d72110af08bdb3120f9f4f62afe797f31587dd7b05c292310e34fe819106b4d5c7de32c772a2830db548e5c9270a5ac7ca2ca724cfe46af0f35f43d296c3fb9fc7dafe35141f1c2179594648b601736880b4f5639b6300ff0e2f718ac6ac3fd51564a1a404111875459b9c6fb77d19a6f8d6c597e3ed70bf7104f8bc87a08431c12db0e57296d5290afbb2f19013f4fd68632c4cc4739947e07c87c221ac4d607b515129da8d08de052e31cd1c9ff035290cced357691011408fbef22abbaf9dcdae94a428a0e0110b82260c22659dabaa36e637e4eb851259a06ab126c33732be33f27b6c2421a677f036d0ed284218136ece13ae8fc870461ec6d594506c0fe4c3c5d01e0bf5ec214e815f81788d896c0aff62b245d6b1db1b611d734e1bc057d8f5f9263f697808049495d22aa54c60543c58e4360be2850ef8abf64b602ba3a23cc048f7f5c2e3bcb08264479c73719c51d4c18b6a48546a993d1f362372c880250f8ec4f4e4b063a0a7a40b60a218626756bd0ec7228370afff0f80e98064bdf71c7a98e727777188911a363bdeba4511b586a82f81d1ac06446615226aa2700493b009572ed5a1fa38926656441885f229b08fdcfb8f6c1d8b92b04b85d15c37103ce771919e57b7d471730f09cc846229daffff18ae1c3043d901549c46c82e7b3fb7a3b61fa81d054db3c368158703c0c00b214e3e2a638a1a00ad512c09ca6b5d3a9d47365906e2a773fe36403ec9d9ee490541c722bed8ab13e28b03aa24dbe8be83c154a0c512d80843eab4bd559105ba6425fb659fdf0cc9b6decd6f7ba84928ec24aaea291f1296a1111d8f03032ed3a3db5fe8b2edef0b57caac00560ec9b5425f93bb05a260235ef31285c59b4c6092e28153f03ef5bf070d4028d0384f6805fd57994cfae01809bd410210a893940c95e496728db67dea0303bdff453bf78818347a48806ae256136eb6d731d1f7f18529824c4dc231497dd9d8f92e21bca4706258f32ed7e01615ce2772c0a265c984f4d03b9e5b5156d9317c79ee498047862bf949f5e35bbef45dfa9ae73dfe366f4204d4389b2e09873fcc09e0f3098dd31286429fc3d7cdee36cb5247ad2471da69d96c603dbe549c8601402f7be5c5d3a65cf8ecf98e5915802bd41882b379e99a90628ad07a24852a282571ee46022b63cf923bfb85b4bde48ffd18a6527b13417a9b4812cb82330f831dfd2d69506ae7ba2e0985515822477e99e394f45191d93a127b0ff8c7e5e4cfb294642ed7978646fb9462f94d40ba50e493f86b0470e472cd9ebb5172f50690afe782b7c5ba8621d913367d40c8b235270583835222437c3e509231af2a024edc5d66eaeb08ae0c1ad7a36e7b18286f66831415a11cd1e333ef72597b1f61e0ae07712a56c614c0b6ac7eac14f58d4811b1156d6a70c4ebf09532cf83ff99273ac49668178749b12b4220fb6556558f73627a50fa08f29ec700fadcb74e6b0f7bf55f25ee24f624d74e8849244a59e3a4b66f4e4a91ec7390ca7de9cd381cdadea6377767bde45c1f07de2b7b8104fc7b4e5997059204d01a65d8f5e44418cd0dcc626257148c58715531a7b38e22320842c308ce480c56d674fc8b36d90790cf626f51a3584c0245f9fe68cd14a3583b6a07a3373c5206547acbdef0ae459bbf815365b5dced7e0a574485accbad83485498a3c29c4821cc12b9d678dfb6c54fae760e02f3576fc8fd21f2fdef3de157fca0ab2183df6babb6f70666aff01137369f7beb3c0559bde970fd265aade350ecd64cb9b8d6065deee1fa066fcfedae7ec901bdb5a5f04fde543f5b6b63f14c78eefec95995a3e45aa44e1c0f64c35e84d71945c3ddef8d8bf397e773cee975287c00978d0af69bce6a3188b475d9f215289e30cedefdeffb3e9f72965e0cadfa901e1093d6b9a8fdc723f1a7324ddd0e247573d029e691e582e8331ec579a62cf099b39f78e0a17affa66641f8e7cd76c2e6a1320f05cf6f0f6acfd5291fa0a5e9dd52da157e84bed9eded5c236661563c1d1ff4c1714513de8d478073f768f9cf3023f7aa370fb824d3d3aaa0939f5f2814eddddcdcb644d6bed32edfff48861f9adc19f30f99f384ff77dd01c9f8c58a49496e14dc2de614cd0a0334c5c7630fea365439a3a761579fd9048b259895137ff945fb00c7e85cf3d199cfe56e23f837da77a4e4a3cc688fbc796bdb263a616ecea4fbd16528298cea8cc392743b216768363cfbf26da625f52f6ac05a991c435662a6f354a627a8b3d6a1e37fa66387c0939b7976a65cf243f5ec51daeeb2f772d70b9a84ff3be092db5d03d47fee8dbe38fc99445f91e7ef3046d400511dc124f01c5378cf300bad72fdfdbd292aab2ec94a4c1ff746e9cc3d9be80f96c51d689e663cfff0ba3f97ed22d51470dc6799f5ced2d5a8e0eeca28aefab60d83eefbf9072ba91f7d98d3e6669c37670470c2e40b8eaf2c17e5ee6ae35f41df6915f30a878b2ae9b529113ccc2102a89cd2b97dc9ff2b83317858dbe3b641f538db065e33bd936eb83ed4b5c5ba934338af5c5b08e3d1f85484a061801ca60ddbcb3eb42c438228a3b0597b68ed5d98d238e99ab7e4972c8709b67470a2c33392a2f5c63fdcf559861703cf6ca87e25b8ff97a894bd51703dfe77c7f230877a56ac674e5bfc8b1f910eca15737d51d665bb0d2e2361a7d8a0bcfee78bd07cc0c524d5cca0b30271e656c640f9b8695dbff631883f9c05a3023ae7c1291e85ef8f4179498a334394c9e4e858aa482048a5917d4ef4e27c9d0507747ae095fbb5c5af23dd75dfbdbbce2a731aeea21d17aae4b9f1d71a2d0753a43dc55c4aa864f6ca262dcd1d5c2567fb030a31c83afb32776a97b3410270cbffb5e23e7de7d1d1a0ee3394c3732adb3dc4b23d945f5107eb0af2fbce0272efdd8debd66bf72b73ef71d3e292781fd018185a4294ff17e7a9e67ff69abf33f479e934d772d1b3de8571755b8e761b6e3d103b29e14c9dede78be70d39f7d7960a4d7f5194abad33ad775cbb7dd76aca6efb8fe7be4115b378fdf9f3fe9634562f99f579cad29d28952801bfac21eed4d10e0fde8646f0e05da2c35890850a550d8e6654e11a46a32e2dcf6b6760fe2a3ee1c2b1120d31057ad377fc2cdea4d4d4018d7713affadc8451496ea83d098c13be985fbcccc6bb59c98aca6265f0954397ef62b8cc277c40f9e6330951f878629650b78cbb463d293846c6d206ab97c48423d71709283e4d7054eee566e67a7deb54cf87efbf1df9ec5a56f106214bdc3567d9b9a9de62e761d830f609ef2d360dfae92060dfcd3fb75568c1ab9bfba1926e1da357279272aa4cb077d41b84dfa171ff1155514508ee3652ec064224ec82995a23c51588780829d0759bf4ba88fcf805889e2a3723a186a8b53f72198d91bc8d76887355a7c39ee0b0b903cd32d256d9cf1163d6531a3f6b1b28da5060074ce3fc5678f817203f73dc81c1f34623c80f443de8d1ed13f118df73b34edb9d529d8781df887b0f78a8268e6451f74570b59795f8b5dec3e317e13e571e778808bdc90bca941f96d3707b50090df9f8f89417f7d1cf38fab4b14827683fe5d82b96bda4091c6144456eff702ae4ba0e9ba573778d6f9358b74e1ad20d0800cb4477efe48e8ee83d9af81c38453702cc18b367abc123a48977946e70181feb86c4334b85625f4cbedb6f24e3ccb2425fc709bf69af1c7bf34a4f3cd3cffb9a16945646ff75e28944948a5fe87d9ae2f9dceaf065bc4c53b1e2279026ceb9d78d8c1d84ebf58f830bb5346de66657d47f433b9d6eee37f22f66b3c477d9f9c8cc25befc25a06769a912060e85442e468666dd42766d1dd2d0e27e0e7eb8b9f9fb2738fa0fa5c46f6a3776b904f68b795a19ba89b894e0e148f26ee9997f411e5989c712743a9be0b1fb2b85d52d96042d99186652d49567a44ea3bfc9b71e475d4267f00896a3eae7398d92b1607e73da1994b9ee0e3e52952cc5009d576b907199516768c4b1af812b2fca8cda010dbc91e07c45b71d4a5e70fa29ebad0850e65ceeace2ee8b3be4fca2de9c8f5cd6271f0af88d947236eb4f5a3f15bd8cc7a148065954e560ce35ce8d951ade3c66d6eaf6fb6668e49f16a8827a59a2facd5c7b00b8a0267c3eaa53bfb046fab2f2a641740b7eca1b6ce38064ff3b51ec2bda99b80795b224ff86119e17c6db02c04d5e79bd6629ecd79f2b7f061667306952ed907e8a993713a317e3c72f9a3beb0c68b71cd732f9992510e731a3e3a7ed162348b1b7fdd577b897405db157d98b019864e764fb01b659ffd8c22b4aa8b69d4194d3436d5ea06d7a1fbd3db79b5cf99a874e5de1dd010ea10e794160425b9319d0c0367feef9072dfd717272dc1ae5fdee42895aa1fce46ab308e07ce3c9c915a3dfe712bcb233347e135c1b386a7690c6071a542c1bcb209d30788f1d772b7be2f0f4ff9dfaacdd5cab763f4583dcd307077fd3461bc11f5a68afc3a2405755fbe84fe73205e2d60cbc208e2490c7d6183a3ab95d66f494a03a360a7dc6dc99a2ea2b83fa33a003b6b3ebfe5d5c01a7b1ecb796c1d16a8c35ffe225c7a746d07df735eed54a7b0f3d8f00cd9dc221c96585cddd1ced69afca46f41d6c8578528d25f63acbeaebd05c9512ab68d7d66ee8351a25a0603937188f06be93996341f706306ab9d7745943e4fcb6f055cb2b9d384cf4278093ee0870ebd02301574f81845dd043eb1f43bd53e000fd0160843f881c7eeb2d974c18bda47ca8014f58e519697676f1fa55595c778591d120fdc6e3b67e31623d529cac84c6434f3f9a33d9d4e6cb487fce51b2acc1a95144e21ed959295623faad8a1725f9b4b27ef5dd76bde9007a01102cbe4050edda2d628b7a30342cab5daf881b9db9b1ef360c4fad250a3eac33b51648ac77c3122ad909f28a7b3dea36bf24a1afa6de7a6fbbe89cd9dcac962cb63f3c682c4baa308f8240c88fa802b99278f59258d0d3f15f4dcd062218c6809d43c5e56dfd90e355b9c8bad84049ac2eecb19e00b72e5e373421da57c71a2c14033d6176b0d7ff3025f7b2b051a79d4a43cc53e0c4919c516f7335bb210bad2ea80dc6ed4708295418e19a47dd5ec4543604764b5201aeec661e9c2851d2250cba4c1f5901f5964b635717baafb4aa59c2d3cf64c786798d7381dc27922bdfa3730d20c930ac3783734d9a377eadbfc1edddbe8fdb0c9b767401d1e947428bae67c30e9f4f329f34e0d4586998bd7e3c60634c54e2b76b03b53011259e1d7c89dbd4cf3894dd21eea25b1991df4527e46232e89c8e870cbaca95bd1af867d6a30c2251cb401fd8ef6d630a8bec3a46d6a79a24f2790d155192f699a36a5fd2a7902498376d2e784d42e53dce0098bac71107b18f484336580c134f632393e866d64aa22444ad47b9c501c496c2491370ef84ce6777d5c6f877d7e61e18f77e7d05dc10d6ba57029461afdfdf0cd0ee31587546e66e1b65dee57acead2c5ab7a7873286ecce902705100f34313c3354e3807b8fd97b937c3753229107811ac8be569b86e7d7656ad075531b8de7369b8889e727790fdd848975489eabfe9ab50a53f637c760b52e3dbb48fd79a3d5690fb5fa73542402df4f0267af768265e0acd787dc8a30cc138806fcf5a79c138dc97b311e38de1312feff040dbe21c1790023620fcfca12ca1b7cce848d7136b5e3f9cc197478488ab9b3e0ae5fdddded53bf8957f702bdbead66c10e235d12a11e2e54ea8825445e123621b2388c29af5366368ac9e4f72e1affa4ce0b46eed0a32df18932290a7ade17e00310349b446fab1b2781ed2c5f1cfcd786e35efaf9373de2148507efd0528cc74f3fb290f6e45f9f10dad7a7a0b5d81bc2dcfbc15796e91978a1a00760ad0cf0f26809ab97d4434b78bb20c8eeb894d5c5de8d1dce3b5634bf63c807850e168d77984928b187885a6166bdcfd98f84b21a8683d9da3f2cf083acde7aed7fd1ad6ba37dc69838def1335340215fdb68199ff9628cb73778a0786f9fc585e568453720762ba57be73b04984b7eff587f36a8842c800bb00eeb4d3260742a04d1e6db32e31a2262495260b36249f0034be8ce0672b53289f0bd8d352248015b7fcb1cd690d544c1b6f1c2cfcb115f09d57a218ebb728de6bf950db6a49b5c80fe439702cec25b398fce3e7657e8eed95a8e75367f9e3ef555fc3063fc4bb86d918978c52f88531ac9da3126eb2384322e9732ba4497d8045ff7a5b677d1080358655a1282031334585f8871f293693701314db9e163d88cea90f77738fbf6ba47da22834ea0209098cac206bf6d417549559fbd714fb6f8883d1ebc5903f2e1d89506c85450f0b08a64de922a2e6bcbf712f5b57a7b7d23cce4ebada128576ff869855884e1f92185ff6b329ba1582f319ecb576586657923dc3eba6c03b48d75908988aa864486d88b27e1a0e550c0471362cfe56623c6c36d4cf05b98ae78fbc98214f8d5b51dc5dbda5ec7a3d80314edbad40400fe7628f8da53c388b9075be1561f2c70f748ff3688cf93d6302a002527cf11e6219c125805c2b1e7a7e6591858582acb16c82e6e795e2c0db227e00471af508e7cdda8c865a989836bfcc6728dc4360dda167af5858edddeb737237a181665e7c3427101e134e0eb64163970ca001cb8adf155373d2b5331c10521b51669cfe4fd00aa7b0b66ffee382a9ecd577f89dfb375ebd13cc04f37ae6cc1e1185c8e68b780117b1db4dd0b28c433896596686cae2a7d5c412907271882728ddcf07ef20f8ec3af3f371c3303393ab6d7719e69b6d4c7175fa1c95484d3d087178f076c70526932e10bd80d7f7aba7e70da9eb7088dc16d3351f77fed30618383dd218ef4e3a169a6dea45241cda26fa06309f5e2450e171ef76bef450cee59f3b4ec4e5b477b00e3cda9d786f71aebedf1b1f302c7c89889152bf0739c2a7ba1c78e54a6ae95f7559b99ea0925cdbd94578e3497744d9b0b614e0c666977504f9dd029e9e75968b5117d4c15703fcd84980f42e4f04dbd014a27a8d5b0e83ef54cd171dd2484f01bba8c96cb7456f7409ebffb637883e73f499eee04ff6cea7d9e97e5ca402205f46c6eb74a8ca0a317a028630ab7cf321aa628de20da72a64e8295b13475d6196df7081323816693a67e7d9a47577b56d6edbd74e38e09cef3d1aabcf8232009ca70bbe902e5c624cd497c437600cfd0c4d00133bd1c2033442e681903b39f802bc0b5a6f439f8c066acbc61cc4da342323a44863dc822df0bf8ec6eea449fab497be76903efd25ef8f1fb8017717b459c29847133f1aa5b5c3931da73f2a4ff5f97c54c15a3385ef1a41cda6e3b258536c15e2082626684280b4ce917f2c28b2f8a620f83e99f3a27bd416e514ba323f184e78522469c19b73d137abf08346177f67d169f7884c23cd6ba3d38a8c32cff161e305309f4959591864c687744d8c494e6bc26dfc08d4402a48b5dd787dc1032b93eafdfc2ac9b90f7e1ac0b3a3c69ab5eed74484c8fa1da31a3ed66c236c3c74b2854b28d3da433a7d1cf12f0225c7a9cc8adf11f0331abb534afea231ac8154f7a5e518a34ebb9e7d47dbe7074354528245c5d9ef374ea0b8acea084e5e61cfbfd3c45f74a857ce065078e27dfe09ae84099a7138e7c5b155a8da3acbbcb9e2a40c153d8a5d34db70729fa2661452c159aee3803bfb5e62f17f44507e11e9979ff5cc0446e61c9673d0f1a69d33375678e625ded71da17a519f68698502bedcc8e975a48a4a26d9a2d8fcefc3be35ad168e327e6dff1a5f33178369cb6ce0993271bbb0373566685c9544c90ad2490943ac826a8f42d17f1dee1ec55fa0d6b6a157d367e5864423191b1c703d3b7d89b2cda139f32ae52f9409f8686959da054df34a3f77333a2fbd26768ce90e3d50970df387ea01e0b982618974827388c3de5976fb04e0c28d6fa89e52c0522e7b1408d5d6503673f398f0613d73c4f5e441bf685b465fa98f522de5dc7d460ebca35157f60732930e11e6eafc2760c22208941fc684b22f021d869ccb1659ae3cf757a5ce39dc795a1fcedbc92709643caf28c3b87fd4f815bd48e070f2ac6315d32110fd56090b5168cd5a9c4393f2d1a9bf41e9ef63b39aa4fa94f654f7b1ae32663f01f0a416b7a4de8be60ca78a020976cb367bbec5f862f6672b9f41fe1f77f2ab021e5effb572a2d9957be5d47ec13c4c365122ad7877810ec46a4f6804af39fe9e6f77934f18ba49941233f95c09390862d1add6b8e9c0ae63c81c1fc24e28f7096fd742b3de7cfbe8593bac19e6974f513e9dce1e7d9a03ff2969567e454009ef96422de8a833191e89798924edda61451d397ab6c4ea57d57dd94be2d88508f9531a7da70a03f7ff952e52e8b0832738d68da797985b4d771406156fe17932f37697044341fe6967c17f9c8efc6d4f29b8c850b49ce870e290d0f2f195d53b7dd71889bc3bbcca66abd0b99c04b4dab97e9c57337596b7a340293154a8ead17e84fbcc299efb8ff8a8c00b896f6f1e24cf6420c4a4eff14819f10718a4342a22263732af3dc35604b6e22eaa80a4cdd968977b766084b885a14965b0a0e57258bc28a8c028e354863751ef2f86e6887007291e3c9db868bf9da209edf9309c71f9268deb139de4d9c9033160bd1dc0d1266703622a6c5bde0f26ec899c67bd86383529de13c5388843d5c9eb2b6ffd6991a26a7edce9a4eb8f1877d0e6fa2c9992cdb8818227fa6189801d0757f42bccf0a54cd3f970605662ed5ec876e0780c598de0839aa1b92ec7c2c616b7bff1aa98aeafcc07f3409072f55f609bc20e76bf11bbb1394bc2c504a5f478e20dd39dc2eca9083343b49a6ddcbe526d44160324f50e69cedee128635792d53dac775da664ca2d798fd2fd5005f89b2f9988afffc9fac270e24088c70c7238d2bc79bc409e5440d49b87bda533d9ee0abf78fce74dbb15f023e9cb5826bf8b230a67406b3cc4344a53d03d3a4d7499cc1ec839622941b073205fa02109f318e7722e5b2678a0ae68cb9e289986ea582067ca44053448d5426d1b68e0905eadf4beebaff5fc54d897a56b0dd5b757f05717847de4a2863803ca0d6c340d5c6e12584407e740bb0c31fd99c604c2b445e67363c88b68b6d88c668324827917dca1d276f061c9bf8e981dcf9be6861fdcaf0bc46ad4cba815c7230c81a14b6b4173f41b918cb868403865df91a774a914ddcd4701b2b903a0822e2cadb77ea32fe5082049c0e03eeaa45ce0e3440b0a6d0013c0c3c0c3c0c3c0cac31c6ee1ddd7d446b5f262925d3f865f690e1524a29a59422f3f685672f04faa1fadcadbf1c17f90e5f0e690f48e31449234a7dd01d7ef35dc81409625b79293cc525cb9422295d87ba2d99dcfd5c5224768a9f27dea4082f47b1b8c555304bcbddaa6de1c1a3868553ebe5952d46c10c465e40431449b2f1f4c7a0f2782123e8000e1798a0c70be141222272344291f86b1bf784aa39b66ec7101eb863080f5024f6a5b510352556d4fa09aeabc4bc3bd4cb3b49d98c767944659886279283979cd158353a670ed0e844724ea69bb24766519b0f09823e24c87122d9752cc6f5ce78327ef005385c60021c3e84f418810a686c22b35b8bdd19235e515e743c879dee14b2b9cdb155a5120d4d24a7cb222c8366b6dcfc6a80462612ef35cf4b8b6778768c7c101d3b10a2c3477c0829b69848341df54ae6fbb73dfb12497ba9d388e928da47cfb1e54382f890165822e1ba376678f37ca223730875c00a342a915479a47ebe8da6b839901c414a01c9d1021111213a5cc70bc8d0e1011111114a9448426312092ab3eb928e9bdb095187ecd86178b440442497070d4924e8787562738b7bc63212c92e6aabaacaab3a487f201f3c1403342091603f1657495bb28e95901e232043870754c9d0814646780ce9818f4890da9bb3723c088ba61d68382249c70e95643ea665f8ca17683422b994f20e3bf16176e613030d462489db78953cce274d690b038d45249e76bf6c6f9eafa4f319682822f12e2b2df5ba6610974424d98613131fc57cf347444444849b062212a458f4bedbecd6f1a47188a4a42bf74ee796ecd968182279476db8afd411df8e46219292a8cad1dfed795f27446214abcb289edebbb48348ccd9f0243453da9b2f88c4241764ae5f0b163e1c880419eb2ae5d8af2473102012cc52085d5b26836dfa87abae52a9bb6ba560e1332d97c966b760daaaf143b2785026fcc6e4c8c9fb909445e6d59c92a89c47e643529cee73abb81a43767b48b41cbb7ca284d06fdb888e1f6664640405331811a1a18724edf98398331b1d5367c88e9424a091872471ee219f76f45f327cec3811d0c043d2a81c911634a6bce3e5d85a1f26b8439556572a9a9af195cdb6835cd30e5b41e74e4f06193e4cf0c3070a4444c8f0b1e38008f941c30e49aa72ca72315bba745487a42dcf5e493e6bb64e3381061d924c3f7c5df4fd6899ff2026870a76a4caa03187a4b418668425d3f4abe7d8da3184c7b9c00438760c39c10e1d2e228282198c00a12187a4ee98ee41d54f4d9d39b6340649230ec9b331c3dbdc2b2cfc7048b6944c26714973b6bcca40e30d49496905a59456d8ef300d3724ce88b1dfddf0e942a50d8925edadb2d46be3538b020d36247516b99d9459c75b125381c61a12345f52bf27b7d3fc8582198ce4a0a186c4f0cc5c4fd5a6aa2c0080028d34246cea8cbf13d59bf9e700f26d68483eb1bf29e49e8c135f8e2d0e62a5562898c1c81834ced0ab586caf581e7665af164229d5a6a4e70d8988041111e9dc010d3324aa6852e152a65a8ebb0c89feaa7d9d74e8ef60224382278da5e5937c4bcaa53186c4b8b172ce869dcf4dc5903cb2a4ed8d9bcef1320c09d6fd192fc64a88d9c190682232a8dee85371491a5fa8cc44e3a258b5cd9999b76d5a52c2aec335d8fda5a8507b2a6647aa0bc97f2b2a9bb89cb9a3070314cc60240734b890e8f963c2d2dca56e488bc61612f437f7766ef3ec99161111a9203a78a06a21c9b63a4b46d19cb4fb39b67a04dc5948d2ee2364a5b96e4f3616927c2f7d2625fcd2638ec615124529175d95fd29b511203e46843c1958bc56483c159e617ddfebbcdd0cd0a842720a995ee943ddbbe5a990d4252f9a45139f92b753483c69f6a5fa3ec9680b0d292476888ed9d4c73c7f8b461492544e71a77b979abbf3917a8c80af40030ac921dfd379967c7bcb8d051a4f488a1d6c5773bb4ead25481946052222222223c5f7041a4e4852da31c714849dad959a90e83eea3b5d8bfc64314c48109993fab6c50fd24a1a4b482a35b59fd954462d692821c9c74a6e288bcbfd291a49e0528513f3b4acedf8103fd11eddd8d727f2d14042f2e9dd14f465bfb83e3a42829df0105f7de56b6a232456b0b4e7ee614e7fb408895ed6d9d29cb9876f78041a44480ca5ecf2dda577519da3318404d163294c67eb65b53640430889bdb1724b53aa986af43fca70c11466042349ec72fd6cced5e8710130124c4578f8d8f973aa57c88c5f245ec86e7dda51f19f5435e28bc418d772b69bb5306b63c1e0ca1866f42249657b5dffb42df3c701f1a1021191e645c26eba9fec642a6e5f5830d845a2f99a0a52d4aeba485229bf629bf23ddf77c38c5c2478f78da7b64a44044b07583a385c245b07194b09d7b83933286f9158b174ca529d21e794e85520226235826b98618bc4beccf9197f39f38ab548b4533777954f78f857961609426c08f5964a47575d44a4d44819420c0d3366917ca3db43f6cd29954b2222226508111119a9205a861023c30c592406599674c43277fd8c45a2559eead11836acbd3360911843f3d6697a2fff393d0b84e8f080d588101d5eaa7a043108e4d1bd2231e35ce6e06b3afdc675862b922a775b25db90a7d25624cb07216f2d5dd85c2a2222ac48927be973b8ad20f38e5691e0ad41c8d26937f9aa480ae9ed95a4dfda5d8a67a422b18490a54eeb9c0e7ac119a868c5b2553a2b4ba92ccdaa44fbdfaecc885fac878e12189e22d144462f29f78c9b5211111acc304592aaf7b1eb2e9196772912b39667fb7d0ea5bfcd2045a2766f12db332ae69c3d20427ecc18455289874d2563d97b87449198838ecea7e44e650d4592aa12b13f6a74f4108122f9dc73a3fae5d0e1e74f24075defab4dae2792cdb2d385d20f569aec4472a5a5b8254b06cde044529ce6895d54b12c6f8ead1c3c19c605478611721698b189e4946bc359b6e799c85030839115ccd04462fc184d69bcd86d326604333291d49e4df5c9a5a8797c328c0b4444c8304252300313c941e56d52cfa33d69ea12c79893b66759d08e1996a8d35232b3d866c13fa50ad594e6c3401ac823f32ca852208f6230a312c9a7667aaaa9ba35f4385c100220690625924f47fed5e9532bae7d404444488fe731e4f8402222401e4d224997f8f079bba39cb792488ca2d3a714e764c9f7726c194084f448d87e181e3732231249f194fef13e13d2c393638b8c1e3a820019721ec84891819264de0463e01032e4042222401e07400c8f52f6c8470fccaa007861062492d493968f733a2c5c633ccc7844f225b33842d3bf7b9e71b8c00438aa460acd11c936f69931af967778b6e861462312adbb53d47fccee75568dd4c89e1f6630222979a86bb98771d77211c9a1325eeb4b66771e0c331491d4e5294737adb61e4f44e2a51c2fa81215c773c882cc404492999d389d944ac947818888e9404464a44c0716a40b0e330e9178fdbdf9533ef9a9374472b9a5e47f3a9a26aa1c5be8918f2043aeb410494163664bba5712a5448884b5ac2f197fac6af483488a36a74a79ef8248fa54396813b5fbda32100936576f35da49df2667002251637aa8179583896633fe807a65885d50b92f198dd3a7afe279395a6fe587a4127fffa0c2bc2fcce843a2e9e92c79d153ea0c33f890147aae924a4968cf2fea32ccd843f2698e95c6a285adce541766e821c1834c2a977598cba88a88a47161461e1274cebe7c316ddcbf82872451ee6afa544c7cd433ee9070a677fdfe64d29c2e33ec909cbff3281d4d77c63fcda84362ea561925c3fb4727d12131482f75d76a6b39e67348ee7fbbb0f9313253921c926ef4aa6b7be498d0c7018fb1cf16b7a0e5a9a2a9a0948d9fbc1b0ec99953ff735eac95b04544444478a494c7e1015f61c61b92532b8e5a36a59dd29ae186a48b5ef9528c15bd533fa30d49a2316e3a390bd6b133830d099f720e7aabdf838a7d0dc91a3a66a828e12d9a9ea106456bee525cb82dafd53a19b2d2b55c2cdd399d918664d3663a6d3ebd1936ef9e8186c4f1523fab7da3dd72ce90a4ae6f6ece74e6ce316648ceadaf0d6f6aba5237a30cc9d1c3c5fc1a2da7ad1a984186440d33a6932a357df7c7909ca4c6cd24c37fb4852222ff4084b88888884812668821e137cd29b1316fca2d991c0f044d30230cc921333dcdaf43e9395995edc00021c10c3024e50ef24d5d257ff800f20111911f3e80a00eecc0b04044640706487f216963e8494b97bb722cc7c0018212f06086179232ebd929ef9373391281084444b80e00ac98d185440bad3ba7ef45e952e5d80ac27c98c185c42f5925b3591bdee71d666c21b92c65edce6e1cad3187c3052110418e2046043982981c63e018038708708c81a3057498a105abb2797d6cc565c885370b49e2c40675df11a29d0d998185a470bb1075d971aaf546c10c468230e30a0923dd3b6dfe985bb722222222529f98618524195f4588ed9e907193636ba4ee0833aa902497530eba49bee78c8cf8206364081b11665021c97c4c48f1204406717704e931630a09a7632a058dff3136828c1d1266482129ea7e060f9bb2a818cfb13532d256ac0833a290f09536a39bd2c1f4fa50484ae27f748dca31bbf652c59e906096554fe697f7f41a34021111111120427ecc704292798e503d1121f2f78c262407b5a945e6f8d5df1833989058db35d6ab9982e713f398b184a495b994655de38eb271304309c973326a619a5d9dc91c5b5a2798918464fbd49e772c6ee594ef1948484a6255d332c8f8d8212222e4c91011c1e18210e800e2021c63140c444474bb30e3080932eeb69f2e966e6f7f0c19c1810f1f3bd670b82004385680630c1c26c0e10213e0681dcc304292d2171af2ec63f2d01521e9ec533d7b569341663c74e4082244070567308308c9f95e263c88bdb16ce5d8dab1c31cec0102ab1943e0ab44d542662db77b65ddeaf841efc26b73660821295310c2625da99cf9c1482af51015a5730ea647c048b2ac5bf174325749f48b24994a5fb456f1a047c717c9e3a54ab3284f2f92e23f46df50efae205e2466dd3b31162cbb537c1749c2c3e5e7f02f2f265d242995b19379ae3a139a5c24b50825d7ba9be16c5c2465a7a4bfd3eb292df62d922e86ac6856693fa22d922f06a56cf3e920e3712d12644c2d5dca90ff1b5a246dcccc56dbf6b2b7ce2241e5e6f3dd5aae994f16c91d6e66d37698103516c99a628c0813a7f636834562ee2515e683f6aaab5e91a477cc6b645bc870293a76805b653c0e467a87791104c01509ae99311eede3cb74ad48d2166b6f8be14e098d15097eefefa321634af9ad2241f9c58fc8cdfc29a6aa484c1e6246c5b01a936c39b6ce7210805424c73bff9341fc66f2ff801000542407f9295f305177bbd32992d4ffc5d9309e9b37c441a54d254f09cf241b4248fc2cdda036b574de2618c91a444f640a1634091b1889677266741ecb187355e317c58ed03166109d4d695fb0d51e9aed19a665ecc56d56f7f1969e5d6f41ade43dc7f896bbfc11adb21d35789178329bbca895b200870b4c8003052222436aec2229891aa5e9f2263dd8ba48cc96f46aae07af462e92d255e6fc6cfa2c730717c99af36234844a7ae3961ab7484a2927f12dd1a24b266d9134722b5c565026f4a7e4d8ea51aa356a91e8b13df55950426553cab1555d831689e1720c3bd22ec6a465e6408d5924cd47131afe6dfef23e7ee4778cfcd0014448902d2086070f03840c59247a4e5b52635229cc9262d17bca6a7a5cfaba8afb12a6b2f946bb9c83f6b0483a99cdf478ec945b751e82c373c82b926ebb66646c0815aee18a44ebfcd15a640a1d672b122c9a505f2d32ac48382b697136469dcad7088fffb10ab3abba44cbe6daac662c8a066da5efb9b33940c0021c3554919cae1d662c4f68d532158939a8143b7b42ec9c122a9245a93ea9e16b9d1a3d458210a3aa2dccad091525a3870e111116d43045b26e90f918d38a36cfe9408d52247d3695c554f3cf07f5f8111f42466e043548816e19bd71e341233bf81e552e30010e205a409647913c26d47e46535ff85a145b0a1e66b9c64d3dede4737d96b767518622f1f2a668bac2a679908322d9d2a65825c485533253e313491dd6f5e6dc7ed7429e48f8382a8e8a5ecade722792bdb2758d0961f355102043c608f242caf040193538c16d6625536f59cd6a6c22e9929aa8ccfb5c56a21a9a483a61faa27b1232d53b35329118c6f2ec3dc48547cb2e269274cc9ad39c184d25335d6a5c22415f28f5edd4d329dded2dd4b044520c5bd3d5dc9a5234c80b312648428d4a245b8d9b8c9b157d270b420d4a248b4a31b5f96306fb4e0810a3826220d498446255f6d0b2b433e7a21df1410d49247cdec7e456a188c8958da1462412f69214f11a199d636953a801894413beb7b66976c442778f48acfca0b3444524d4704472e834f11e3693dbcf6cf5841a8d483e9dc544c6848a27d38c4850d1341be2397b726f1149216c43c7542e94ac50a08b82198cf8a8a18824954ace8aba5cafe99388e4399d3695661a934f1191746a4e4dc3cc7de7f01049312c898d3187bc0b174324f56610963c55f08d4f2192d25d86e9a770bf362112d5a4abbe87f960e7d5184492ee932a6339de09a523d41044d2cb8fa5d29a749a0faa11880433cdb58ae9a6640901917c1bbdb269c77fb6afc61f92663c87f0d49e22770d3f24e8f8ff592bd3e6f193b149a1461f92931c917a29b3efdb1744444444e4742022c2e3f0a04f07c6e3f0e06af0217194d9f5670a32c3a80c1fc91164c88ff6408d3d24d67fb41bb34d9552af87a4b260322771a544ac55230f0942d375696a9de85b030fc92717f377e796e771ef9028b24c2c878a7648ee1511cd94e5d39375483cade822dbaf3e85990e49755aff4343bf2c65e7a0cd69bb655cd4107e328552613d7248fcd0b54bebaff97489439266bdd3d9425b3f3570287a69c9cacd5aca5d9da2ba622bdcc8cda2466f48b2b22de525fb7d2aa30321d47043720e23ba34cf3b7cbada9098deef4d8a8c619d5736249c164b1157de49462822222252aa588d3524e8b029aa2645cbbfa786e48fa7f4a84c25ae2ea7c1724fad9131b97472298cf48af5b92b2ec6d2a22129877c4fb2c67b3bc5121c6388882ca1c619924c27e53daba9e6a36386243523ee53d45252a32f43f2b9a6ee7ce72b322a1912d3688a5549cf31242513b519d46fce5e0c89e69783eed0701d5318064d978c7a2a770a189284af8c48133a8a5fe80b4952ecba82cc6691b57921292e6bde75b4508b295dc06c46b552956d5c5cda0a9bd6697e2e24e5ff2857417bdc335f44c4b450630b89a3cec497ca7b46931692fc3c844a71c487b59785ed529277734ba772293393ac14b34633866a602129fd7855fc147b17d715927cbde259b67c9ba62de5841a5648fcd5b6ec31ee874e9f2a24b96965df9dec924fa990a0e1d3831e377d4273a690e03d9a4c96d80bf79314123fc4cf48ab78e94f8f429d6919177651aed6b2c58df3a29f52030ac9b1e26590d14f3fa7477c0819f951d735a8f184445d59fd646e1ab4cf6d4084fca8e184a473cde9ca773f7ceae050810970648d2624c7889ddfe4a764f6bed5508309099a722fd8459f33fd2c216993fca7f7e7e0d1ab4e430d2524a89461c1824e3953af35929098f4061dba4cbb87e66b20213167315df9d27589f174a003990e747c60c79013b0958e54e308c9517c2ea3ab9f7cd68795358c90a044c6fa8c72b99bd232a8518424295629a4ce49b3351121e14a960cf9395363085d57d9bbc5a568b9ae52c72ca12c931c712a564308c9a57273ef86d82a29e2704108520e1798608c91213982181184800c1c2e30018e3c84118c444f151ba3af1a09f2a657100630b6b3142e9c79c55bb214366811daa9a272bad4a34118bf487e4f7d8bf312bda91ba932be04af021384055d410e0e7ea42f9246a74b616954bcf4ffa352070e7e040923d17aa6f963398407da9013242222403ec70f1d474718bc480a2a9ba754f35237bf8beeee3653bd5553b5829655ce962d6a09a54214862e12f4fa7c0a553b73f5837c075a408671018e31707c0f74b400c71838827c074e08781c3246827c18b9480a6fdd68a7e4981c9535df811d1d40c10c46c6080317c9697abdaf4f8c855cebb2d7932d923499f7e7b829338aa91649f971bd9ef9a745f2e914b7f421a3aae86791b49b84aa0bd62d6abd2c1eb5131f4369281689fe992b3fc7b878d2814562890e972e5f4ebf9cf38a24214d9dcb5c975ee6b82231c9aebde8fb3dba3aad489ef5b4a0f3686ecf382b922b09a921ac55246eca2144689295155645721233964fa8bb4f4a4f45825cbd509f39a3761c1549a9f3667f86916f9f394582582b59daa66b2a6d8a4413a2dcdccf42c5b64b91e8f12f2e7f483b4f991489a235fbb9fb9bbeb5a3480a21f36cbf3c28755251245e9bac67eba0bb852259e47cbd99c94dcb8222e17267af8d09b527cb4f248adabfcf31d8b7e79e4874f5f841d676143d3b91f89784e9175139eb889c485051bfd4f9274ff13791202c6637f1f194dd729a488c23e6360993977fb34c247c69d11edfdc554998484eaba67933dce84a954b249f4c498fc8d4711896483093ef74919a2c3d692b91a04e63e8a8bdccc59f124929d3bf987f8599ea2c8c4924dc788ee154fa20d7c123842189e4dce93b4c74eba91989a4ce4925cb29548e8d068904dda41d2fa78bea9d7e44d29d8ae13c871c717e3922297c50326b0a6acb3b258c4624e7204a05d5fc418a503322e146a587dc14b37f9645747776495cc4bbbc62ba56afffbe9ae6eae7159118fab974797fb2155322122db42b87579c0fe9232239bb7c73e85cd694fc4324a67a4b8b3de23e636f88042d25735ee7c6b0132a44624ab2e3a64d13de39881009b656aa32e820f2c2671009e667712b4a08a5dc238884cd31564e59d1ce2e271089dedba33f6868d2dd0191183d68874c414fa6ceff9058a3e353b21cc32f64fc90ec6526d4aaa60f0956a5eb848eaa29e9f9909c74b3528ec13e5b4aef21b9dfacc298cc5339ad8764eb7cf9dd2b699d340f492364e3bfb2665492f1901462ea76972f95e57b87e44e6da3c289af4f427648d88ed329a5bf707a538764adb1cdab9f79939a0e49f1d3ebc8cb5bed56e79074aff323c46af4c9520e8971ca7e2d55ae7e280e8959b3e7aa7d99f81c0e491a1ac455c9549b62de90b831c6aec678aba9d10dc95935b6cf892c3d7f1b92c3665d98b7a4d4d6c786e49c47ea08a1ae4e3f6b48f4cf31b899fc57911a927f366e2caa864b95a52131b3b56e66cf29531c0d8949eb6651b71cafda3a4362e91cb3738a1b9f233324962cf919229732245bfc91dd349d7261214382dda98b9e2958aa928e21f1fdf3c584e5d0973631dc96ccb3b4ae72cc5c4a5696ee295b2e6611c390f47d55232b7ea820ff09446c0b618021f9db5ba3a7c73d9d453d76900211111191c442185f48d06c2633e6bfb3c5b080210c2f2469556baac5a79a0a87d18524253373b7dbfb267a2e24e8be9f72cf1736a8b88584addc51fe2ee78796b4909ce5a393541196e5ff2c247b7956c6b2b6be94c44252a9d49326ab498a50e508e30ac95f99d2e3d70575a217111111e991c2d23584618584919117531065ca445548ceb0763265935fd1e25448be20b48585b1f558e92924e5f8b6b678b8afea5248961937212e2f9effa951c0e44ec62aab3d3e66be3a9cd83bf5f3c9e2e55048522a66ed31f7a52b7115c27842b2aef89928cdded0f3938174009142184e48ce31fe680a9f720cae87d1842419767cf367d5861b851006131236b3be8f0e26a67207218c2524aee9b09f64103e1f232524c6d8771d259f242478e9257d41550c1b72184848da94940c9bf36f784e3942d2c69c45854a2a23245a5de8a049b533da45287a6599a58b3bb1b738e5a9b29edd5e3a6724088308c917df57f335a9ed7a6410c610127d7cabc2ebc5ae5bc210425e55e75dc1c6b54d2cdbeaa9cdd93efe2e58c148d2251bd7f4c54ff7554464472a8740031889a7ef5a37465339fef78b64bbabeb937edbedc91164a46a478e20be481aa14de59455497a56073a502f12f3de664c1599d9f17820233ad081ae74a003f12231c7e0b3a753d6deeb2365d82e1233edef96f70735e53f0f1f6408e9e18347a93284f4f0c1233b40431749aaef2cb6bd3c68c78fe181c7e4223148cd7057eac458a777ecc04562ff6e58eba017b2191fd2825b24281583da185fdebf6d1ab6484afa3c9b8bf86ece5f2d12ee672bec2dc78a13ea802d0a6630f2346871b34830cf3163fa1c3d28e9c922c9d5c488bb68f2739d6291a4ca2aca27a1aba38ab0382ca69c865c785d0e0d8f39af2c7efb8a44ad1d8d337bfd3190e791e377b82251e6542dfc6dec76ff030d19413a769c217fad488a9e3998957f0a163a648ca07f2b74c86045526be6e6ecd1e5d8f2e182213ca0b10a135145724aea2d9a7e1b6e93468066a0918ac4a4396ebe12a533593cc7160868a0a220b2fab9b3280f171e4344a4036eced551572344628e7aab719e5e4a5f83c0423f4434c80822a95faee2a6ac15941b88245d3b5faff61d3f1620123bc558729a625364f587c4649f435faceea9ee87e4cdaa9e67f13e24a531addf514445950fc9b31d6fb4da5cdb0401ec21d963bab8f92de2c399f490209684b5f9fd7f4aa53c246c983851cbe961468f87c42b3d1ddb725a9acfdf21c933ea99bceb20d4fd7648dcddf64bae7d1d926355fc20749790b1f2744830ad993497c80d9e417348d054e163d66cb1ab457248b4e0622a059d9aa774e290a4f327cfa3e45b79cc81438296dc91794ec38ffabc21730ba7bd9b3d6e4870efcfa752a7294d9e36248ed0e17a4d2fccc6c386e49cef7e399b7fd690af214166bad31745df67d9d590242664bcdc77f2f93169484a37be5aaa3f2cc50d1a92665e6356ab8f8dae3943c29a14b5d1693324fd7898c586fbdce92e43621c25326b1b422dda6448ea78224e89daa88af518927ec4bbf75db4bad18a21596ccf2b6d3c0cc9a547e9fc71db15130c4931ecaba6aca5b7d47e21a9b22e5b506dab157b21596455c896cb57ecb40b89b1f3ac9bac4b9a772e24098d7b153fd8f585b790b81f9664a66ecabca61612d3678cf29a75b262b29074615cab72feca12221612d3ef8bfebd8d7e3a7785c46c3a936e5f8818911512d4b9fcd6c7947294568504b16dbb95b992e9890a09971ef329cfa1820c4d21297fb654d193a89b2e85e48f5174c692f1186b1492c48cae86bc2bf5b2a09078c13f67b2a896f72724090fda293de384240d6db2a366313db00802684252c878cd5c1532a99e04c08464ebf8ef95f289599b04b08424172b654296ec36350940094915f6d26a4478d6ec0b2009778e4fb174701f09499ee3c9a44b7385b6fc1192fa62449c8a0b4b216484a4fc51fbb94ee4670e1240119254e54ccbb431666c91008890d8415c3a0beba9aa21010c21492999319a772f79cc11801012734cc92ce718cf729f602497662c574baa279380916c6a33e7f511f3a6f38b048d374fdfa02733c67d91b82232bc36a8e6cede8bc4edcbd828ca548ae9bc48ca6d32c7ceeb074df92e927bb4888c3632ba48bacfb2b9415d25cde72239786ec91f7fb96d71911c437f341735791fbd4592d7fa6cbe678b643b5963614d2eb6d2b548baa42d3cbe85699120bb42d393bab348de74eea7bd2b2eca954582f61cf3b4f54947716391203f841471728545724a1a47bce6faba5b5f917c9deacbba652fcdea8a844f1f64e893235a5a6d4562127272c6e44e7854599194f76fb44b6a7e96721509aa4d33c9c85ac754aa22b93fe628e282053d11539198a6569b9e42459268bd1cba7f31de82a74836b7683107b99e397da648faa432b7eb7d3ca552247d276d6e719acc2745928c5529ba5cc7a97d1449517e4eea8569b05614491b979744a8d5ad9c8522413d695a901aa7770545d268ac67b0e4a132558c70e313c97139d765f498447353373c916429cc2dfbf8a87c3a52b8d189e4d7e8bba1662a9ffa8d13092a05f9994fc71495e91211791da4ddd8c4e93daba5604965cdb863479a256e682269e499f2e0d96f1ab61d3bca002224ab702313c971357f5a7fa344cc66beb88189f7d4fdadd39aa92e91dcb6e9c7e4a9e8ef694b24aeebc6a8f8dc6e41e14625125773f06a0b2693f65eaf7083128977aa4a58981ad78e27919c3bbea956fbac93a50216fcf9d3c3478f138888041903c7182f1011e1f14282948183f339c610630b372491f4eaa92fbe7f6d8c0682473e4e90871b9148d66cdfe775f7414c9a632b085619a90210871b90485242de6a58d2b3a911b31e2fa81b8f48b2ada0925a8ddffb508e15d9c10d47247a7cde8bbfa32fd721c38d462447d3e194507b3f73298c4850fe5ba7eb5d5f737c6311896a25f4e68af6712b223146919d82d22f6d3b0f7c784044043d0f7c380311f2e346222cd9f17363d0a2c9cab1a5831b88489675b3fc975757d6e0c6210c957bf9b2f9402222222222754084fc08c02d6e18a2df1ad9192a864b938848bde246210c0f2a89a9da80bc901d3ad20842a6940e03c40588b841085e944a6de27c63ed83d05ac6cd33defac55810ae6ac9673fb5a4321a886754d8aeb7ab874ec80d40204e8a7bf2bf53e16221434ed00304c9821b7f483239d35daad2e592b188e20d3f686539ebecd2da698975650a9bd161a512dce84392de0abb1f2a773ae8dce043929a92ef9bdbc1ed94ce70630f897a1d5eaba45fcedfe65821407cb4e06af5906072a3bd9eae144ab37948103a69de8e96f235d31b7848f298dc3ccd5876ec306360166edc214907256a2e8ed6bcbcda21513c581af9d93f7719328c1034208fce870ebb5187a451bbe5fad9ac43966ed02149e6d1b13a5bb8649acab1e543c899e0c61c92f3d6d584494b39c38337e4903c6bda9e231df4d05116e4870f12dc8843f2094dc983e7cecff4303adcc73137d85bc10d38a0e9b116e52cb9aab9bccb28d535ff2474463e8790911e3e4682e828037bdc7843e2fc9d77fbd7cbbf8f80f81819f9018210ac99c91104931b6e08620744c899c08d362ce0061bca781c8cfc0041082070630d09d2d33f96509dc6ef37fbc00d352486d2a284e6ec66fadb810f202342928e1c41d2909484e626ed5faf3a31a321c9e6cf93d01a37d653ba0337ce90b01dee74a9986b2cda39b67c0819d9da410e1f3d4682bce10ddc3043d2789a2d0f610a6e942169be7d35dce4e8f3518eadae203f52d082ac203f7cdc0d3224a5dd1cc477dce4e1fb3124cd7dc834ba46ff4c460c891e6329d7e875526fcd46c10c46ca8d30246e0c16b36dd8598fe606181254ad9c579041275b4b8e2d10b00047b9f185911102dcf0c2ef102244c70e09dce842b2081d44c9cf29c6f9f68790213e468214bbc185a45fcd7ba1ffdcffe2ba76630b493247772c0b4b85f0e08558f10d2d2498549d1317fa8d2cf470316e60a187dfb8c28e213cac90f896937fd9e9ca500ffa07f2630421c337aa809049c00d2af44048101d3d1870630a097fb6233fdca79ecce5d8ca1d14f3c00d292ce04614c8f81e507842026e38c187041941be1f04b8d184c458ef6dd9d639cb3652868fe860c75f21df8f911f890cbec104844c901f200841036e2c01488e2023376e28a1c71008dc48c248eec007eaa163070fb88104bb11ab54c08d23ecc861041f12a408c8078f11840c016e10e14f0f1f2308b8318405dc10420f93c3003682d1e375f0c0701e3a768c1cc0c62f7698213978316cf8c20790efc1c80331232df0c00946608202d8e8c58f07e2001bbc58808d5dfce071783c8f01d8d0458e203a7ae848b950800d5c08d1d143c72d122ef7eea34513672567b12d50878c0019413e788ce8a0878e1132bec7480faf4572c91b8db6af967ab44ab0418b4435a1c583ac7a04312341805c59e5e6096ccc223947d3eea6a4534e3a1fa2c0862c123e6876571f711b2f4371b820042222303041d0494444444444444444b6468e8d5824c61331730d7a33a54f08323fd0c8480f1d233d8c0ed4e347c8d0e1a5ce43c70e24a3870e2036b0018bc4bcf15d7e3486d6a44c818d572064760ce1317282b82248026cb4e2c9a8800d56ac828cef81001baa102244c8c88e213c186023150eb0818a740a0bd830057a1c04313c46cc0e43011ba5f833043d909113640136482164c7101e11b0318ac4db1c3ae54f428a4491f0175b2bda7357a7031f3c0ea28d5024788732e925ad3fe9f74a03364091a43e774a37e7fcdf208f9127633f91a0673febe52434e6dcd188fd187932ba6d7822297cabea96c539b6900f2123f7c3ac13cf438710204607233c18f901821048c0062792443e54a74dbd1e33f2417cf8d831f229379170b59ad152e4868d72c7101e239f0a03363491bc27c682d2ce535f990f21239fca056c6422f1d35450c9b2b3ec25f148569848dce077a7d2c71c7a71194080983362af0320434e2911d8b804036c580201362a8190a104901c049904016c48e283181f51808d48ec18c2a3003620919c3a5b715be5f731f6d0919211727ec7b73d22416efda655f7a9605b57066c3822492b27ad6974dcddec437ca011330ed86844e2e676f936a1ef6b4346246bb78a8a9914cdac16b16cca9c5b1ab18ab231bde2e696f48198213908a22eb0a188e4587b5932e36944d37e8c08313ad295a6fd4044c10c46d64622847c104494f1e3f0781e232668410b5090a8200505b07188c490b13958cc71934a691b8648b6eeb4e2154aa5b88d4224c52b0f659b4e4224698fcbaac74b4179741049f7e9d1caef4e93ce1184aa5e6975a3ae9a395c4b3385aeec4d0a44c2a8961e6122442619dd914a1b804890fdb39b33349ed0b88d3f24ef8fd0d6d77d97ea5214cc60640636fc908975ac6d8a7d855e99aaf85d7fde6df42131a90ab12abd67326982bce07ba0a3053d7cf0f851061015f010030448078e0e119191a30307085880831636f8909c1bf3e7c558d931e404c803bb17888808493e82888888ec303a28630fc99d622f74b9a668483d80630c1c2236f490a0b67de9222a25d9c11c23df031d417a043d7cec284eb09187442f31b7a93e89dd1363176ce021a9f4cc5aa6aa8da9d75eb07187a4ca9e6ecd53c7861d92920aa2f2eb6752bd241b75480c4d1b43675a8d35251d1237fba46ac720a65c9c43724c1dcacf44e698b26dc821296466dbdcfafff9b38d3824fcaad98b688c7f52da80436207952a2b4388f618f38684d30e96bd3398e651ba21f9d2a787905927de726d48d29cce77548f6cb02139851dcf3af22dc89cdb58c3729e95d2b865d79758eae69bc5a6b0dfda504362d4920b4a6da8a6936da421318ed21419910d36d090f867d7fffd1d9ed5e3828d3324658a7de947ce6be93b021b66484cb9bdadf6b07f3a2ac536ca90f8156eab840ef256836c9021f1455dcc3826c4f55e1b6348dedbf2106a357773fb061b6248982dcf1cdc72d9c68a106c8421296b4e72e675c5e2731b60488a1bfaa9c43dcfeff7d163c4c617927436d19b25e4e5abd70bc91f458fa9105a54bccd46179242b769eed2412b89d3061792f3996bfe94a3aa6ad9d882a2256a5e516336c64d3df4bf6b2f254ba64e5a48d6b720afd6aed94dc959c82c5e33e62a5af63a7b2fdd26a3c664caf6dbc04272574c9b35d26fdd4a574858bd6c1796376e38a91592624fddb27cac19d18d60a30a09f366c94a74caa94ca242528f5e903974f8123d4f2131e65f10ade8f53a5229247f29996536df96fd1285a4ca9779ebed2590df1cd880427290f58a96729a99474f480e3d5af36cca86131235a8dd34322631f3add8684272a8cbb853f79b3e8e3698909ccc374599d9b01da18d2524fdfd9a89f2741fe22a2131bcec69ac0f17543047602309c99e5a834c799be9d8d182175286f7b081842499ef7c3cecc77b9e6c1c21d14be87c3246891e211b464814b9d821c493c689080216e0b05184241d63a525959dc35ae8870fb64184a48d5796bff1e44b25d9184282ac39c0488e113f954b29ee34f30b937f2e7996625029f71dc4f04552ffb99cacf4c22ccdb0e93ffe749a17c9f6a6b3b6a9adec5aef224198e63e9db7d1ed2d2162e822f93253f0caeaa7f2fe5c2447b554a1fd3a89ba9a997f181e23e82b0e62e062dbda17f91d3cb7488e95161f84af39d6167f1e3df5b85597f1538b045156a61b639b7ec8d022c937e6decbaad1baf52c92942add9b82e7f40fb12c124caf5a944c32e9edc40216897b67524de7a0572465a5b060497fae488e96bd7279a76eac582b9252e6ca8b418f32cdb118ac484e49d89adc19b3e0965524587cc55819af54e5716084a02a1c2d31d1b0d139e72f15d5e5cd9a2e23544543457269109dd57ec783c59c02b9fd6141a65395b14da1481519b44a598ac4284a460979513fd34a8aa44ed25be542651ff9e620c628925f2c97768a17e476174592d4be34f22fbe3a2814492383e6f5384a2ecfa0481e3d5161c52b3ba5f0271ecd8dc15f73aaaca42712fb72e4b52f09559e5a4188d189e48f35225e7f2fcfe39c38d8a629b526feda623691f4faeb51c332ad7fecfc2086264ef329b7076526924ef766fc18e244671026d0a04644c3ecf644778904ede7ffd79d27a6b51e3a0c10647d10c312868bcaad499570377b2ca59e478f22258c294b3ffbe76a10a54924997ff2984aa733ed31922899501e4a854fc96f8118914856f3b68df59c7fdef2410c481ceb634c2756a7ddceb1f5427454084ad09110e3115869f8ab178fb11c5128d3963ea52e79ba1c86071b101f424c8bd188a44e7d3129d9b86ca22264088f9132800829c50373021c63e0e891021c63e01803c70fd9616080630c1c6655c69700c718380ac8e708624480630c1c2dd0523f0c8f91c288d2ea43456335b3aed243c458445268b615eb1c56c3b3e587188a48cafed713ad4139b6321109273ab542dfaa2d543f50055741ba04622022c9aaef327dbaca10e3100976c94e5f3279593393632b7d20862192f42b7baa2c7666252a44521695e4f456c7ca4988046d5dba1b653ee52a9d321c2e0841d7160a6630120022883188c40e1793888f1e3ce634c7d648c5400c4124eee6b0dbf962779a9e635fd0230556573c74f40dc4084472cc9a29c96953e65f95b383188048be8e9e57e2e78365bd6110e30f89c1c3d7765059bf54dce020861f9292b2df70153a37c7a5b620461f124eee65cbe2bef241870fc9699a7a74f6ce5aa5d741313688b187448b314737a93ed7b6a8410c3d24bced9c9a8fda733ad282187948d4b87bdd78a32107f1f1e3c020061e92c39ad241dba70efd81fcb0c2e18210f4f8821877484af23f7f89dd764850f24466fa87374d6131ea901caf2e9a974ca1bd0fee910211912038f0e123c796410c3a24884b5149547c2bfdb70a62cc2131357789eef2b2c420861c12b36fb9c83d3bfd8f6299418c3824c95342563c1dabed8339b682f8481d8882187048bc685a194df3e9eb5ae920c61b922ba5f26c23ec634da76e1162b8214944e489ca4e99392d1ec031860f0fd81ec4684392a95dff121f65cddfe682186c48ded29b4dbb8873cf3eb120c61a12d5f2e6b5cbddf466043b7698b382186a480e15691ddad4e61f3d1021235558412e21461a125b4ddb32295577f1d190181f4764a71679ad416748ea943b65c5dd69a8dd0c89ea7ece9da45d86a4f09af74e67cca429244382a5e0a3626bccfc5ac1c18e21de3b4c0f20295000e0428c3124451b95ec536adb74713124da7832a1c5aee2066518927e4478144f17f3590443528bf80b7b3ae97afc0b89e29ba556940a993313c30b4942b72a4751719414bb906cdd25e449713fa5f25c48d02e2a7dfebe680ea32d2466ca8a6332a74bb11f2d24288bd9fac4b2a84d791612d3e8d44aa64c4ef4060b49a9f9fa1696ede5bf429287ba1c9bcf0e8fdb0a09da9ff2dca7b60ac929eb695e7f950ac9f1f3c94f9b72888f4f21f164900da1f5eda54c2924fe6bbac698b011a7282466ff6911aa74fe190ac919cf3b469919dd2d3e21e9c44919d5b9a7434e481acd67991d2a987bac09c973d5d5c9b49e7b8809093acefa58cab92a6c969014bf6a7564cc59bf2503319490b0ed96bcebcdf7ee4f4262bcc80fa12bf8e6899090789b29efcfa7083d3a42d26c0eef2d3a5aa63142c2bf9b98cf94234611123df359fcff70994e2e06119242ff2817991b2ec72ec6101253aeb8e229fba3e82d86109264f7f7c8a48475d51d8ce4da8bbdcd5dbd3b36301277ef3ce34659cc60ff22312eb509958f9edf675f247e87eed318b634dadc8b04cd3f6a36c7a02da9971709c29210537bfa62a87717c96567de69e4a68b04e951ae541075fbd6e622c94bb48b1a1d73454971916cda2d332abf4a577a8ba4abb4c9fe54e80f16b545d2be9d8d0931f122cc5a248b570cd12958524b495a2487e5cdee2b1973fecd2231ec2993b1335d27f5c9222987ddff347e29f3bc58245dc8d6a9df53bb18834552864c515d8bb12b6aaf483aa17454fe7c39cc325724a90b59f3af5b91702737c6b28c5b32ca8a8419155a974f83ccdd2a125474cfa53fe60953952a12e4a89c623a2925a455a9483cf94977f20f2a1253b8cab1f941eca64f9194d475a5285e31c1004c91e81eb3c4c27a7bb65230f31d1e166cd3fa4787ddec1117d71a0029122ca3666639fbcf0d60144965ab214afdc5bcdb0c1145e276dda53b15e3190c201489393bc2a4961096a2840a8a84313932aaf666e711ed3e91bc5b194545dd2a6da11ab23218802792539d6cbaa991f5387522b16390953e9792aca07322313b74d0f3a6b647a32303d84472cc9362d9fb23bd633491149ed2655bbfd63a9a89a438efd85227fb016022f9c3536751b9bf446266d2144b578fdb6f89c42c514aa8a0947c9a330403a84462d0b33c8f1dea0e0c801209b729423f75dcb2701249294aa88c9b3de5724712491b1a3288d2f3654113890479d995a6c4d58edc20916cdaed3ff65a8f48f60bf27333ef4dc9932312ad934ab5cda446247529ddfdf61be3a81323923c688b99d5639d635a4452a8a496e4a7140d31292249c45cc6de936ce7522292e467f516cd3969d01222923d2c542e1326c468e9108996645b4caaed4c8cde10c9e9d455c959126ff2172249fb8611eaad1bd27c4224eea824caa45fce58428348da7953e17f533d0921412458bcb8d79be4790e428148f0537ae75e71791f048884bd9c994ba7c4881afd21713346ad098b273c8cfc90e029beaa9eb00fc9bba2c9459afa942ec487a4b4965257e9f39e7b0f49bed6165cdc738ef49098493d5b6851d57a1e9294bdb5274f6a33a878484c5b3265c87f3e3d9e3b24894bba19ff946750b243e2cf65a8d2acd33975483a6d51f11eadc9733a24650a7ef79ff428939943c207a56247df2e0747be5d683da1c72139c47a6ebdcbc6cf391c126654be1c73478beef91b12cb4ecce475f8dc147743c2290d3ab7685679501b12e3e94af2b35c8ede860d0997ab63847ff78c69d69058ea76c3684a692c346a48caa35e42bfb6aca9983424e8fa669dcce8a88b414362f49421442f36c7a43e4362888be551b3535b509b2169bb325b63b40b23ed3224080da18316f5712a6d3224c95b5fe6e5b4592c3d86a4ac1ff79bb3ac898e18922bcfc94a4a7e0a9be630248f3aed8b616e2737832149affa6d694eea77e62f2425cfa333d908cd95642f2426531e4f6f3b5564dc85e414ac4fb7ff29394a5c484a5145f34e796a50da42625ffc94b36ff8361d2d2449d3e1f22909d941be5948d41d8b3173a65bf3c54282ce24b4a9789a84b6f70a492363b0ab1c7334ab6b852425a3a89495a7b4745b85a418e7c9fc47641027a542d2f98de610a293fe904e21b164fa182666368d46a590f4bad9f306bd277bd4282426e527cc2d99298b291492cbdca2cce7511d9b7c8235627484be3a9d903c621e6aa3e6bcb9661392eeaa3e8c67ff9f9a4c4892ebb97df409cb71e6121254cc435facb22cb71212734eca44bd9c84a457eb95953b25d34542b25f1cb9a3515f737884e4a09eb4258fa3952b3442e251bf3f335f84c424846856ec4784844bb62b7fc13aa6f1063084046da6ab4cbb299139031042e226d11d35c5e3874f309244f57996da75571c189f3a75da1f63ec1789a5b92957e5ec1c35f345c2589cccdf65bd4852b567a5b456f430f12239a8ec9341a80fdd9c5d24d8684dbe17dadf575d2459306dcfd94467bf2a1749d2de828e39ee5c12c245f2c7671137b2faa3e616c9a641c36e97d0a3ac22005b24b676caa1dc92cdcca830f0c08ef3484404c8ff08420601d422b133656c051bddbf595a2498bace0f4bb3ef412c3208601649aa937bca183b64582e8be4f2d09ce326bd9d358a4572a6e7d0e9c48ca9dc60915c41b8c75892ad7b571904f08ae4a463ac28a21bbf42ae48d269b38ad21a6266f3562458c86556dc303b716a4100ac48f4100df2bb2cadb2050d02584572ff8790d5499cf0f0601504a08a04cb9b2bd672b8dc5ae7d8e2f580005291781932855cc5184bed07024045f2bdee6e327dd9b2a673ec8e1d660c211f822c08e014c9bbf7a2bca328f76ce6d81a11f2646c09f910404100a648daf97c398acce09d934a91b85d6954ab655c4c132912378ea94bba962e9ad02892536ff4a539759e4c4814c95e2ae6b13756ee1f852231c60e325baa145024992ca97e7d327d9ad1279294787b909993f0f8224f2426f1142a5f9d1dfcd38984d1ca31c68f339e520e2712646a7b8a29db94936f22493e77be7bb71f65724d24a69af8fc95e65b3fcf44622561ee39f6c5ec6430912cea4ea78faf9a52d25c021915a3c37ecc5822b97a7b43c7d9145a752592743c4fda6208254d892991d8a5c3bfe8e4195fa49348b23079b3a2766b719544720e391ff4c6e7181b8d44d28a4c979d534ef5b3422259c3c22f59c55476e72312e7c46f4c44e664c9744492a8779dd7c8e674c946246f4ca9a3af9268ed9111c997f629e69613b242b888c4d49a3b4787934b1b1591e47ff75731e518b38989484c396cca9cdf64574444247de51c5c3bd8e8ecdf2192aa55836ae639251f3344b209d77495bb4c9c6b8548fc6eef038dfaa8048e6543b250200a06436150200c06192ed701b31300001818201146a3e170200babed14800350543c522e2c18281a120b84e36030140c8501a1601008068381c140301006c2203594909b000a2a11594e3fe036c405c5883ddc3e418700be1e083f5d1086ab8e07a88343ee8e8cb125bf45db1c8fcd9d385ea24a4d2038e40dd60903577005fb8d4be833db3ec7cdc27136f2d800a94ce3c5c8b0edcdaa86fb63334610139862584ac57612dc106b7900db133bafd8dd2dba7435503fda81d8b1ff575e2e47420daf6e4bdfc66eea1921dee85d1ded846ea499ddd269c0bacb0172c7562d6fb846bb08503e277d1aede6cfa80314995da14124bc830706efbe5862cfe08610155a92f7fb130f4b22a7076663968ad9183690b6686b3283b2ae4f4f31331574f08c91473310ceb70cdbcd5cd4c1170ec3402fd8f11c5f58eb3d31c6e8182056479876c4cf2cad7f45d5e7ed97e1374dfddc1197a04863f6dbe22494cc990d50cbd4605eaa30bca97c1b4ea7df3937103de6c4734c19546bd28aca30f3a03cd9823f1eab1966190e9b02bd0b7f2a5ea4384ca0d0664c7223f884c7d4ced56b49d48ab39e15ead14b1c6562b48fc5798a16ce2f9f9287b7c00f4d981990628faf42b60026cfbc43cfba7bd01cf5f480ffba0bfb0fca8f441540dd341f077744d0d115940d8bfd6292b50cff538957f1b34940917bb2323a44b2c6c085b52d69a874c3551fa7c34cf7ba023b291801020d57795a30c82a78bcbb0c4753450b2cbac8357da429b530a3f0c7be9027d4652fb9c97f3f9491a3d5cbd0c2807f9af1be9de387e04ba5c0b39b56f744b652544f67e328aca0d792d6981bb2cfa2d6a16103de335eb23718350f17295411ca3d5c46d68ec06ce6dc2022cb0dda990b8ebc8be83468efccdfd065038c317ebce09a9da0dea441e329eec84c3821d96c33149579da5a4f167062bb35aa7d26d3178e60f17157445e96fdd2d4cdb5a0553d90415b93ba6b1b425f068c6733c38508ef5bd7d041d58a0f468371f8d82b06c4c28c6ac16317e86adf20d6734a414c93ac77ad604c6ce4b8121e405e70383c4a00e460c4c2e718bb34cfa9342153ec165b389e6537d62c7205de576e1281d24c0f151c823c23c3496cdef1cfe0ddd8a3e8fbb690b226e8a4dedb701a8a36e8821d14234b9052b5c3ae69d0fe9e914b794d3b69cac675319a6415bd4062cfb3a09224480fea802eea55c6529537bf92e60466fd931cf454b58189c059d40fe1283a524c7dabd613ca246f353f0c69078ebaefabe402ea8c3479eb055e1e009291b8cad2020035736f4f1f45c58d215f33eb936a9438a23663136cb37fbffbdaa902fd02017cad5f095f94ac0b6625944f31c614c72c2b7dc8da0a04c5d68c4f4a3736766ed07dc59bd0a77888facd160b14f6e535d5585caad2092b6492366736554e445840aa0351f09af80f3f8a8c339838a3aa6f12740a87fd322110cf034a32295ee3a5a598a97a09febf264aea274a1ebb5d27c8867666b5e805e7ba4c8989ca31a6ee8cce4b6b1b5cb3043af403df61a9d29e6d715583305d9b8586740ebda77152302b6070c4de573563d7f7dfb4c9eaacff1828d0e161e62b0bce99bdedb6a4260c71314153ecbbf32255f90434bf22a393723195a140f97ec0987bc845d9ef95fb73570415f6474d0e1c0743ceb8407950887a571228bb603a791c38945d0b2387a23ec016fe7f0bffa70bf7bf318ce4bf14c61cfb87625de4dd2447d5f1f6910661cfeea378df0c67a9b739333013e17f95e14fdfc9e07b328dee334998c79c1119f8b5376301eee5491a80f5cabbcf98b4333b095899b566f7597c76a7c569778a38ed8a1011b81732c0f5ec4231899ea6d0a8693a05354d45a012f4042a414fa011a44d390285a7acb7ae77bf57c62782c554e8b723d9e6bf726f939e0c7aba282f2535e71299e0a535b2126fac0d22f35472b7d24f17edc687cd76c34b32a0a348f37a381868fd75fbe8030c0488a175a0b6afff2be2a8a53d152bab15615533bbc67d5dbadde7872c64ade91a588d40f61a852ddf2d333e112603178abdc2a53b20f9359b9b8708970f8140d680517f068bd98aef3c36167d3b9206c1d3885ac605dfca6bce960e11690b3163ca49ea967dfb1474c169dc8b39713c92417684427ac48ec0481c35233c624b34792ea55a06a989f9529dc9b7e94d1d6026449f6fb8161a9561374feb0840ff486e097ead78879b71883caa0f3881f7183b0e4230248f6c08813427560542c59101a191d6c4c402bf20950cd9a238a4452c0888449009e191069119e100c09456bfd886d14847a2f57ab29843a824af5c7ba3674f46016ab9e1d956e05e49260e6ab2214cdc9d512be01316d214fdb5282000ea87c3b2fda6d6ee28f8928445ad32cd15f764ad894e8b613a9bcc2030b0fd86fafd6c44c6820d8db0aa9f81d53e41dffee07a49deb231b711902488fc7705025b24f515f3c74b055437522aacc418c438af9e7a0a3dc2247245a2e215063c35025fc0892687b0b470652e1b2a3a9c04c185fdc587ee44e0dde905addd38a03a5033da12f142e344674b7c005734e44f558a31a25fa1a6599a9ce2f8e10471d1684c34464cf391a31824aa3819cb1291991b72c0a6fee4e58c4d4f4b6f7db10944bcb303f0f3a6409ee7e9a91ee80bd494d0a06558f05707cd0db35c5229028ae1dd8298a8eecccd7f4ea4c8a5f153d785b44a187311fff04892a0894e49a3d0a86950b5ceac154bcd272d879ea367d3e1e8b9340c0d9f86450357bfecf9517ec9d25af4667a8bde4a6fd0b1d39b682c1a3b8d4563a03168cc34362d338d89c6a4b1d368d2ceda46b6283bddc6f6387accfae3d82100950cc183dd6ccd25c0e93e136657c285033f2cd442be23b3225f49dc1749fb96fe995648dbe426b999dc426e90df5cee30b749873264ffe438099e0427c193a048c48a3435685725cfc926e423f9844c934b04718236f0dc3393198372938941898913dbc47689616225312d653331293176627062e5c432314d82495005c9402d164f451cb325d50610844016ca2be57579893cd9464696f9cbe430819700258012b804de298014280a6c0235014d80df1cc190c60f3b4f67a50fa67f7a7402fbe8531e68d73c1790e1bb1216cc4d9c426ff1ccaa4d98790e817325ffcb5c655234a99bd4261593dac46acf65a899198289f69a5f3203a20ebf022b0540817702802e18f67564f7faf6c818bd0b40ab2d20715da9ec11036e7c18f7f8cc07f6689846dca8c3adf0df6f07afed2e56f7801dc39c0600a99524fb487e49607625dfed0814486b4066ad2579a38c2abe3db1111d6dc6ca067a688bd160a03119f71f8e00068ec9ec0fd64440205504d0b658a346a52b8ba68b1db0c884c6c7bb23fcc8b26bcd3cb746204128225bd5692548b4116f434f4b6d0f236d903e55c416da6145a6b9351a5c66d09405c14116565bcb2420e25c111bb6618c5dae575886e2385d34d89639cddfff71830d39ed6f7fa1adcd2c79c83437986057a06e0a80ec6f015546926fb7a23e8757b7c90a47692b7bbec3cfaeb552af6091e8dc97821fae271081a2256873f6779de8fe33d19969b5e6dc29c96ab0761addcd50919da96916a7755f47ce505a00b37d6c1472b6efe01dc20f00d9c6643f266de30d7a0efce25b81c0b4e8555c9792eadfa168b165cbe22b986616790ed31fccdbc0bb7c3af1cd946aaf9b8442cb53ddef873603cb9b10588a42e8a78893db3c5c40ad57479741a3b0cc2c742f643af31e40170e4184bf9b6e3ff7e1bacf6e5f11409eebf13dfe8fa24d4abf0bfbcf96463bf6214ce7222f55164fd7847083dedd6423d96e33af160f6bb4672029861ee89fbe520e82006eb63139ecbc424c2e15986e83257c69ad89b5342a738ebc74148b3e088c38bd5ec82c6965fa45e28d52e9137b3947ea3af34655b8191ab113baefe291b85c0e8d984c680cfe491bed936837dc01b63228af7215fff5ec15e6a6643849f1d4e9d883a02baecd0cb63c777c63407229a7bb8857c931cbc4904f6bae8fbac1632ba0872a2b5991884216ea6ee766ffdb37ae9b7672d6b5c8239d42d201ba8a64d757ed27a61f168b15e0a516015b5c5194c4599a26357c567120cd5773b00ecd633b4400c26f9f77d8a4b05701f8a0c4c6bc71313b5b6e4524bc052902a9b6c997e363820289d1dc632696254aeb4e1e129f3a3bb46611d133cf83f715cfae7136fd87c3e3aec49094c791fb7762b64add5b0bde87d0c50adc3f982a04fd381e3cd837954c008f9f9d850d44391bb1d9b68e09bd4f76ba9417a59d11b4d1cdb763e3bf9a06bca4e621b01f0c03460eea1782e8b553387ecdcd839b138673224e63fb957ec84aee833bccafb037a150497600e17833528ca57f224eecd31ffcb7220f8519e6d14f4cb26da223ae2b82d4290040b753c9322f35f8e0b0e6991d01b2329b41f46ad3654b3283e87a6cc3083639a9489daed40066d9c23986a66a63c6ae6c23a80394a0034a9f6dbd8e4c2da42327786308c7bfe7d3bba67426c1690901eca0d21483cfb9b384ffb99bd27576f153a1af255ee8b539a671b058933c4e5cdce83ac114d173d5431d933bcb8a691fe63905bb553dc8d30ca9f7b04c246f3ab337fd4cb70440064bc6b568373299e05de3086b1b59ecf853a8e9fe95a0272af00a5cc024b26993eff0c1eccfdc14862c01e865caa4ed167024072dad6da7fc69dc793b498720d90d2f8c91903492dd15a642675c85eec3a7a628184de929566a87f0905198fa648ac2c6d03c0b5cf03ffb970eaa2b1d344cb1c5c379ff215e53bdf33c8d58d5ebb25d499d4af834b2d36be1642308aa5b5646fdb470816d3a9ee04b5ad1aaeb5d3b2ba61496dfefdb3aed0bf60773ac7087e2a43d2e0b1bb755626c574f0650d7a3c8b3894b4f3d88b76159f840c25a2ffa2878da46d447fa6fe8752e52b3a8467886e3a83770c3872fe18480729e105a7f144c10ab9194731eedcd25aa637c4f9a0b24b83f742e5551c449cc18b5a3437e6c233b93a50c7b893dc5bf10cb028a5937bd43c890f2da379f0e146a42504407fc90dae3663aa2775adea1687411d75f371143191f53ee1b4e6b9218fc078150b8c94151f1ab18e4cc449716004eb81287c980e43b115287bd8f9199013f3b51fe8ee440ae4ca7eb5ba52295f7b76c2ff0f52ad5e1ae476e1577e93ee631cd16ad0f2e14e6a1e2c61a254319735ffb51d9454223cd598bd22b29c9faf02ef139cf40dc9f9f421c980bbe2721df6badbdc74a0bb46aa3e42445e5bedcf3fba6019caea8ccca4f7e69175ec2d773ba07fd16471dd4e6e4976b5272d5b026245eb010c89843b4daea962a078fce7c1b5ff7521bb2be81b6f787bfa990c178a5fdbcb8876a5f75d857baad96b1f5c370a0383e6b6ad7a6ebbc339f31f785976de16780e1b053f71a0a579b9ab75c6ba8ea481f528203f436360e9ccaf11f9028af7b01ee2e86155df3d028bda2bfe4039f20287e374879be25db565ffa9aa6f31862af687134526f105b598b387ceff6da5994994146def61ea236852e5f4bfa09bc9c243a91a1a37a15891cfdd99ea0cc0656ea058d6d94a656bfa32d7e3e4f5279add10d1c1f83c4e30e27e892d40f866ee0b05201285a70513bb062e2cf5accc068bae1210f36901365cbf91d27e2aa1468a120f71c30995b56aed38c06dac40847da3714e8745d3137a63a415452244ff87645c8410dcee89aca694ca3a19e3dc993bc4020d6a31b265dcce2374047aaef9afa1dd704764d2f222cbc4829e4ec8812413dad923b4119153e19d02b535c986cd51a80fd79b48421196b801845a12f17c1980ba34153a77ec0717de3cc001dc7d5225b7135e95e3e03680aa677d53ca6fb6d57d4d9575763ec408f27f6f5576aae9a02d5ab3c5157f14a37c3f821f302021cf66310da96756c11bcba01a3964365bb6d95f7ba2a0abca08f7fcbe497553d0931478172f6f5c04eb4d6525497767407a2f0cd0b48b8b85c2e6b147fa0fd97a16c8277f90d76afc7b0873c86ad127a4ed118c4b433b96c0a84469a0f5198278a2de8385b4bb80b491d616a76f0cab05af30de50e22f2929bff037aa886622724f3fbbddf749624a03c0e6f1be5cc3ae366b5d4ef3fe05622d1cf1aa2468dcf72f3ad29bbcc11df8ec45753b8de94d3cc73aee7f048f89edac655533449ad1cf13bf6da694ac7b3e2c1beadd159dcb59f5ccf670f6a7068af8b933078e637b992e827ca02c455b6fb6eaacf656086d4dc99c49e381fe7d5afae063308c51fe7c4ac451e6353b9dae289bab548922d8bceb2938638610d123b4eb838f567d30149be89fba7969a89a820b18ac4b48451eaca152e9cad013c3cda940380909b0d0b7bcef97a1ffdf8184ea7a73b3925e4e25a85e6e9c452f34daf2a280f7cacf2cdfaa0ec47a141b1031285526a032a3340867e4043d706acdd7d9334ea436c431f193b6f3ca28b3a25d4feb42d12b1ea4a0c6d4850000accd266c5ba145d9a2e07129d3ae5c4b7f575ef93c6c6364942c6860cd982213690f1a3f86593a302497739650003360682720a224dcbe9100dc42fd85294771ec0ab31a3cd5f7381811d67af991328e36b6784f47389311a3724d28b30f1008e5b536829177fe430b80a7673b596d7a9d2620c6cfe208a26f2ab56e115e16b03538e1904768c9abac739c3f096fd75417888f00d392e189e711647bbaa46adca19ac3d7f59c73d76328aea048cc5678f91b7aa361650d85ac9250e4abd283d982adaf48647ee227adc5e025e8648cf0c0c945eb1564d8842c1c62f5509b2a73e2780b49d4714dc7471699fee79fc3c9700c4de3228645584b62bd66cc5b5475961204610ecfa58a58d42ffd4b766a879c32536731f6b5a8139084deeac287870d92efe22cd91f68fd37b024cc2144d39aeb44a61bc698502a74821f967d3ca80b1765f16014379f1462ab228160235615da91cc9841cfff2f9cc03d74b6a235ba63b12c30f497dc9354c12c25a13728e90b81f86c930cca7b9f90b6d16d4efccf151980af01475cb0e8c1b2a1f8587d2947005ad58bf71c754fe727e0863650a6382060a05fae1c2b2f4f777497bbb859263ef4bd4f0756c05f69475cc38172e835a0489e441997af0891ff1f6515b99dffdc1109eb349514a4b0d55c2841bc562625b779dd64dbf8e72217a3ff4f2ad3bdb7e9cf4bad24952a24cdf0afb9477b54174da07fd5e7583ae0c8eff0041e79ec64bb22226b71db2f0a173a0afed4485e0fbc835c936f131d65abcecf0f876b2758811b8f059fe113443a4bb6958b238b650ee6ba02fc2aa7eb4d40f3ff88ebe8fe69c9bdc3a15b3884f64361782526f01120636545fe57167d28a48e66de7e422485d23d7f6ebf18fe838e2300ddcc9c6ac10d3b18bb54e51228ca909e772db8e19d2b8dfce1101fa9e014ab322656c7beed8dfc270229f221308cb81d72d3c300d743c27ce9217936ccd626a4f03d3f37904f5a580d2a6ad990af57166b52306cd21c5ef487958fc586044d7d37200d56de69a55172827388656baba6b6912da9b2c35024bc629adf5cc4241eabda30e0c35f2d79a09f572ac7de219619231d02aa719b7f1d91f6b990069d79794dcfd58469082832daaa07c92e22932b2686d333250073b3425103864ff6832d778c384094a414138dbdd04045d3e38dffe26965cd20b2c30d151c9404e99b081c4932b38c0b542b328243187c16addaf1717d5bc051e6c8144a1d3537140b21dc26352566871412ea8130ecdd8365a7cb38633ea7d44a8e37be7fb2c0c2ef99ca475ed5ac551f1550c4e2362e6219f40f6ea10c392848c4880db00549c707fd451f61f5276095e0349e1d0782d0edc7903babfff1d5bae7e75535b5450551afa3cc14a424225702d8c05b91392848fd255d9ab035078f936a2edabfef5869ea56e0a2dd48f598b6f2e9e2df57e9fd2a9c6c2c0a1479512f62e95e7fd536b7c4a8ad4578ecf2e5d26632ea36e26e9a1419846dca9af858d2a0a609a2e096fdc6f2c3ca86db59d9420f9ec66d68188df4460b4e3a661856923d5cc9d40253344ddb175e02c7a077c464d11e60c4d519329e1dd21b2774b1a3c7606a75ff44b1c990cadd34e4724f14d5aac76aab08b2a07fc8988b271e1fdc3b19cf52d326f9dd960029d91e2ab7a80967ab34c0b82ea5aa5dd4f0cf27dfd33ff7f1bea3add79e5fc15a5453ee0ed8d83a5c3eb3cdd07ecb294070ad762d3d63b900a4832a0b04034d31e0522bfe031f5004668322d7743beaed9048b100e93d916af872dfffe2f2b0a077a518ad37152e288e5862d5819434c6d0dc03b2971312d95ae65e093143765ed682adce71cd30f2985761f81e9bb4a8ae5d87ba466665efccc9811314e891daf1266543ad374ab14aa259ab7540add745f9dcfe42230ca1d60606f8905a148d2a1226075a6101d504ecbe9ba2ce9ec20b905008f45eaec91207fbfa02465bbd3f0133c789878f5f7d32a8917ab895e19a8f60f57afbcde818b2407046cfcf63a9148670fd6ecbee73181264a3d87e1831eac0d336770195fbbe8203702029eb97bdfb053ebb8c3faad48bc6cb0d9b82673816df9514ef6716dd2c04f734417297787d72f2cb15638ea70b2673c87f3aa5d223cbe4391ccecf00fae4286ece387ffefece85e9b08ac716d772e616b38332c2b202b83f1c212c7121e5607d1aa5812961843c290622018bbd08b1c041ad559d4aec82e6a576417b52b2c55ec1a4639465d474da324a3d846a1512d53a551c728cea0aa11aef20c99257b4060f867923d8f224e0096c24b1b4df2fb522cbfddb3bf17007ff24b14600b683e77061d8b4aacc3386a0b2e36c079ddb31ca8e00a0fb63fcc7dc4e18266ec0c96f15b08cbed417f857351cee43e75682e1e079f391dd5dde7ab49224a18394656a8e36834de8b1f9b4574d3649c98099e681116094e60448258089aa0675ab5335430a1126bc9deaf2b6eb78dd2f92a9a37ffe8193601b6c03f921e9bc15fea0ccb05a41147c60b6e742fb0cf94c41932f81216b1c181ca2cbbba75161028e64abcf7bddb6e81804626f85d34987e5921a1aab09857cca2cdbd76ac711de0541a62a161ff9212810ac74238f5f0b2214a860e48bc9a789b042a0904bdb0b69f9f227ef292669f3c1bb012bcad269c1d8d70b3bb9432921129f3a02b9cc019d43834ddc7c147d16ea15adb268296a2d7fa31e51a2ca4158f74d97c1362666625469aeb5c62b76577adf08ca7842c149611690a01a4816fc20765dc7dd43881fa34492fec9f93af1705a101e6725e9204a40668469d8c3b6118d3b36290e67c48e752c4b1bacbc4a57f9a9863c5c0df4d2c50a22fa9f38d91ec52d455c74880361eec583d70b577814d30ea4f08fe70b252c41aa6f00a297c99c2cf783a25582b3657714914757b7a186b7ffc91c7df07fa2bc8f3bd336629c0c2a011e224589f6ba04945d5a233528d931608ba4510d10390c37e2084268d6a4a1d121792e4c0965ad0776ca16de7eb5cf814a75b791738789b85e5a8bd0a11eef0f05d5906379666c4ff2b414a48da2520aa551f52f85288708e7fa675a02e0bf4422f2e00a298dcfe5dc8d25472028f66ae568bacf02e79a63efd72f023a8ad713d5a5860007c34de4e325ca51e8fca339d8b6209340929c7535306b18d26896910bb60449ea139c96970809734ec7f60e430653ecc16ca4950748beb6b2878ff62ee7df9c18bead36a55751bbf87219c753e1e5715c885a5be19222abe475bf5288899a92ded7238228a54d6675d8df3219eb87a1248c57eacd50d041bc520db0ea240851bef8ae533b759a6f117bd874416b043e235e862b9588b409953bca74143ade54b2a8aff95e019652ca18ad2b2d2fa3f580a22b7a21b1b59a8d9963881474a2ac3bb806600876782e843070e08f2342243f68d4ab1c1de52d9da0f7571be9eb414387d571dad99ab7b8159657906854af847a5404095eb08053fb2294e726d9481cbb4f0effad24410b226b0748d104f4d0005febca76a0ace6a490ca0901c7e703ea2b3c4755b269d18844f05579b008d796ab8fc4d83c055bbec7ef4b696ddd31828bcf997289034a15d8abcb13c334704c51f6d62a1fac8d3e5f273716f8ca2881660981b109f7bdeaacc1a19638e6aed96683f88a034b2d396fc7416ec8cd9b7444d463c6b14dffd107cb069af158c5e20d3ef0b6edfff7e08b93bf99dd6d97563e404212337a250f362be252997f3ceb239ba588a0db57d096e740b3512eb765f375e7c59071f9acbb14f637f31f7c9cc94088d017f8d054bd61a6994863156f67639c6a9970d8ba5c9924010307d1da26225ec95f10caca9986984c851a4ec756e00c43b957a47978eb184e687fc7680268b97be310014241403fb1226622d587cd7df10ffe6ebce22303e46281ef3a5d81d590e2f421fe1003480b7d52786490971f2544f0a20535f30b453dd09ad428427391621f839613d293d67dd7095fce2e1dae15812fd908142062d6a519cf9417537a4a6db435518fd14d79563e1e6cafec79c2ae0aa41ad112d361e02317fc461916b431b8001d0961f1a8995d9a0fec5d5c0752c1f9b5ac82950d799603dd1248a852a47fa078e36dad5794156a90d9ac3b3eedb10f20e931dd12e597b660bad43a2fa72b5af9819d5c2c8dd56e1f6dff64953e0decff826edeb067a8aed5d1ce62fe8d27b379361647cd362ad8693a900a2025e80c6feda517e5303c08257cd5e8a96a89822d37ff4e22ead35566ce86e6e9df4b4887d6066d586931daf7b400ed6d40d4b7e6eea701993abef7dafd355b5a1510c62a3348c2dff790ce229e86f5c00039f10f5df15f8b0cc8fa93fdad7d115ea3d0bbaba4db6df89a79989bf3599d7612240a030a59cca28221d29d49d9eaa9de353771266216940a63100415c955a0eac742a93499571c210042902bec94ece3b17a57ed3543d291423130f3a7e61e5a18324cf73934f2ca90383b88df7222557092232eaf467457380b127619ad64cd6caeeb50be11342766b50732ca8ca2a9a658e53de133a7af1ba8f6ac11d27bdac07af3e5a903d638b4fd329c15ac1633834d4b5b9837689038c6e0fef755df4b517d46c74251186522ea3b2b3ced5990eaecd6512c946bf305cb02de745dd2043b8e316a32e45b0acf396e550f2c9183287ecbee6e4cc79bbd8624bb58c649656a6735b69dd88b191e7b35ce2dcb72c83507c77b40e482ec10cd1fd28ba28c37e9d99f6129a912ada3486056d7485d6877dc1d8b150e503f31a6d3756cf638c8983ae3713fb121a52791ee6c12cfb7732a9542d6c3b6e6d7d7a23a1590986a04afecf98dd1a116c73fe915a09a784885f5aa8165ffaae04914b5d5221e92851a3c12d02923b995d20f07f6272995fcba1ffcc1e95f102760d1d144b6bdd8a4e5747453b90c80f9738145800bc4a428d9d0dfaa4ad08cd30372eab494df05c7bb381a324b71adab5e4de20d2801a85cd0a9ffff519c10631168f31ade23483930b51e6f08fd4e2f881f924fed330e8cc00a48b4a8c6d615c9c89dc237e6483785fa9f2261f545c35f2b48bacbdc6680825f99d14fac69cc9be54977e2720f65b94b2989fbbb1329e2a670d3243ae022608ab1ead04b705d842d4e0f725b7c11ad072aaba04c9ee75b117021f2bd5fa0b72b9a4b5bce954d597458f6455a5e11a01ddd093856a722ef1a438369799e624198888cfe1f6a110d603603cff985b5a0c4d56d74bcf540ab1e128d8504cd0fc92cd115851fcc57336e08c3912ac9d157d999f479046dcec8f436531762fad23dbe1081c28f4b3054e024adb3322a32627994981a729cc1da93e5bd8b9592ddd99b464c712c0baee7e931422a05d055e106f25be22b94185b55b3dba8f6f3c5d579d17e0688f4697c0162086865c8506632e7c6619e86504a435df71967c2f40aa87658fff813006ff97c455fe600094e4af0ca55aa7f2d50134c2c92d940f4d2874ea20ea52d6c31c6fdfd779566e7ca9e6de360fb247acd186e7b55ecec973dc258c984b613b908292292ff040d011f87ed293c3825e82c840a67c0981e5cb8894c21f83074eae0f333cf2378d701337ea5d9be0d598141fa336bf7a229d1c2300d1db81f5a491d2c3ba5c283bf6424b764b7f06c80a7ac6e5be6d116495d6b808c509c9e53ab36a729e85deb82201c9817aea9dd1209894db637a865fdcb98479743692af50e4ea6be499d1c32782ec57a80d768fcaa37e471f2995878334d9bc9b13d35b9afa7dac22e5135db374f72392f09789051ee923f75d8cc64fe295930b854b68da983312dea3927b5b40d3904981dee1d0a336feb1a15c0ca303c9b8e9f00a7bfac699a0827ee3c895db033d3fe3c2b28a27409f8128e753d75e1d9b9f329f6e704308d2eebfb828294b916b8ecfa1d889c1feed95eb894bce82e17a3b921ce5f74dbedb85adb4c086c25194404e70621c36a382da0e9ecd03bb6c23f4650522e87670725e78a828f92c1be1c6b5399efd6e815bc3ce0c8eda2d1d29d3b529940dc1e4c772c7a6bc55b908882b683773c7b7ae1e9200a80b0022de1f2ca80bc573ae1760e254062bd7eabad15dbf80616c75e0de7ebaf3f6ad5738e076d03b288eae066bde21ca5ea28936b70e2781110d78d66ca907403c0037c3791ee2fff2d98921bff62fa601161725f72a46a5bfd0979908c7ed02965d3399ea724479c5947bb62e122d0e1cc3d4ff06cf132f2410a368414d5e51fcacded1bc518f4ee193428668d25072ff3714dd3cdeb50476efbec93b6e3c4fd27337354a193b4a01a46ab8451fb43cb0195908a8f61f1e407f9fe160ad5a945415c990280e8a008a1bbaded12dfd57084943cde043f170fe2a32a02d2ea72953ff08aaaae6f50678d432dd37046532bb0c756730017102129edc687a81532a385ada71c2830c106966a7ca27ca3f8808acbe27bfb8c618fdf97d14702a4102a5ad347c53bf73a9b069276c0358070c9dd1c24391fb73f27904e8388d3c0e6a480f9aacaaee86f442c51f9bc73178abd9d32845d8916eb40203c65737b68b80bfd8584a0d4ba90c28b422ee37874c24b61b85ba16184a1bc0346f20d2a3075b70c51d3e25087c1a07118b095d2f5a4f1bff8c570e81330e4ef78281e4ca57350caec822486f18ca4a323c296d5ea6350ca1c19937278698ebe1e3aa93ac5879aaf4c24972202ded45a0198e1b3ee14d01f2545f84b56c97b7c79f35a1a13a85e27947d1f05221c3f8354d08a2a85f863eb0d8fa4c927907c4c904c377e8a6782fcfe63ce19da395787718efa6e1e8a1ff61ee378af10491806ccdf07035bb9fa9870efb4fd44cede7b8dd1593e140c1e4c9e7dce7a93941b4cb2b43ebacf499e0710a314050f6c0e489e315bc4203d5b5e00f55d018ce06b6b00410dd4da6a624c1800608a36963350421b934814a10a6e18d4256cdfbf5c59928c626f1046cec697e92b1c225cbc8ed098136e28b9b2996a28b3e47e2efc20dfc8d4e16941d8b2b1d5e616a6a924d242d12f75c293049c981632a7fcaa7f33aecc678370ef933f984534a5fca8085c3594011d1be1f4b3cddbe914643d0e3a2f7f26fb3f59709ee7bfd756662679e61d32978e0dd8cd6aef72192defc15827f3ae8969b1c26888cdcea230ea02403a86baffad22efd2c463ab0ece68ff4482c2d264013f8834130e66a34e8117961dd5b0ee826740636d469d8321d800dea2498ad86767a063b3b8945ce413d07cf4e112574c10b178be1d0c7474df663646a2cdf0ea786ebe0010eb7b8a6b8939c7afb06b65ad2fd2a805c8fc654e617ff187604e1df8fa3235bf548002ac4ae025e0045be0c3860b02485033033333333333333333353c7c6fc367e7f11db5811216d64e672e50bf28a94b429b3f10be1c2efe01dbc7dc8366bed226d6e952f0dc10ca50c85ac304306fa38e6abd4232d63a1f755eacdfac17d460a2cb4349639a58a9d4424640a325ea1f470d2e985f90fe9235728293f56f081491ecdb61890d10a7dd0553969fa4197048b0c566819b46f2f64f0914b4cc62ab47e3bc96e29920f3e55e86d757a79903fa950264d84cbf3f171eaeff10619a8d072f24b57ef8f33dde6586da0025968e1022d6cd8e8838c53e8317b6baeb9107387bb0419a6d07c503f1c39dd3ceee30d24166c8e2d707881a30b162c067084e1c5035c4629d46b9d8e7829628f224f0a7d18b1f7b56be207f38c428f1a1f260bf7215b0ca92043144a760d3d321f8f735bda43a17c556c4f9d3fe13d16149f50bc2d8f24ab07f57e6114647842cde3ecdae3fc117e106d27f44e5e3f1e7c90797c761a410627b4649122068f929c5c2e7e055cbc6f4239e948f1e463b8c6be39c2688166a1451619c8420b2dac9441862694b96c110b4f26d40f7eb25b553ff0ef31f1c844540e9a318f46199750ff479a2db6465a96d072b649fe711c49595f1995d022264f4a791cd2fd207203c9a880400625f89be463d1d39f0e479b1c38b4a84005b2d0228b7f01171908430515e8c0e7c0a1c577c1616861c3c677c15b84d1366c70f1c5dab03109edcd7e9c62b9581ec5a440bb582ec2f802050d862494cab311132185fefe9011095db4ca7af8c9b692446566900109fd22f23067a4c89e5388165f70a1012ffe8b0ad8b001c6175cd023741fc4ab3c7bdda992c60a321ca1a76c92bc62fa18b96aab828c46283f9290ec819fffa6b00c462836ee2e29ffb6851e464090b108e52ff3f92066cf25622a428b21e385ca963ea44c8e6714a8800259684002d75ca08c442821b6b22a4e90e02502642042efcecc1edd0d0d5339849291e5ba7d506d19a31b480707de00631d50508621b4dd10ae736c8d7199ba8154868ec82884d2b5b934c48dd557531201840c426831cf0f34c5d6cc91470f429b394b97f993cdc70b42df3c9e09352e6f1734815032a47ef371f0c9ca1320f47cf2a32c7a39197fd025478a5b967e7c9f7bf841cd1197902963e5818468918516e902197dd03c5c5ddb5d1ad9f2f0414ff136e5e1d77bd083fd7ba50e97936cad07e58386bbcd1693073dc6c922f1e2bd75e581074dc34dcc703ee80e7abc6e6f7fffa81c123b689563b3276232e6e9d4414df2396bd47ee486880e6a4dfecb911f83860d0c1973d0479d8791b9f9ad73b864c841dbf3c15d798794b25864c441939c30f152da3c1efe78dc4032439001074d4effeac72953b9fd37904a1264bc41f36c9363de3c3eccdda0e96954fc81fec49082c36c844164b441cbd9dcdbbb87b9818482a309d8b0415a6441bad0efe20b1568200b0b48208b2b2ec86083be1d31c6cbcc1aeb3e6bd0ee2a5f8a7b09963179fc40861a743dc99b91736e69dc345c6691ab2c6765aaecb269929f468c8a045cf0112828c840833e1ecce67f1fbffa1ccfa07c9c85c69452ce1b4232cca0e4cb0dcd83749741bd093f1f44329b694706cdcf2f495e90540e648c41cbfd830b327133d864c4a066d268db587947231706c5f32fe4e426491a2a3028e359b24b6ca64cfd17d4f892871f3d8a9962fabda0fcd8e45a26eef57bb80bbaebe7999d98e1a31ec8053d66df7c7e8ad40d646c414f96246b0f224cf2208603195a50cbde6ac235eceb77419091053d3c565bcc21e221648c08646041ad7dcbb6fbdc1632aea0e733cf71d2b96bdcc80232ac40365820a30a32a85076790852f3f2af2132a6a064f6b1779ef188bd611b366448414d16d161696abbdada838c28e816397fb039cf50d0c7a3cdb12e64b878a1c71314b120f1bae57a946952329ca08c578c7d67be61af347790d1045d2287d57469dcc40f13f4990cee3efc1fa58cb51d642c410f9fbc98e95fcb7e5782d2f16d7c2c27b1c693042d2ece4cee6affcd27cd410612f47c3dd39072ba1cb1c711f4ce1dbfc67bb3c64bd9186418a1aa34553799b5ad0523878c22a8239daf69fc0f83d8173288a0a4183eebfb604c624ad9a20b1943d0ec53907a9d8a1cdb628b13340e1418173284a047aab83d1ec882cdb1857f18fd5f94a098404610b4cca925f8a02d50b082df82d49101042dc5a4ecf0a127336b58c60f94142c484c97e2edf438e703fde3fa2f871c9a87d36e9106193da0b7c791c7977c5496b70c1ee8953fb3ad539ac7d1179ba3045b94a0462063076a88ab704f9a75af91ad41860e94bafe1cfa2c976872a0b9a75c9b3b0ff27f1cab41060e34b99c72b3c3e606bae75b5f875c39e3391b289f429c89c9c91964d44097eb9f4cd912ab2f84069a8cc5ce5e29ab87d9c723043266a064efe492d2a630fdba1764c840cd91597ebf13a4de63b81023169a8eb4f638fb76a61f7f030916ea76f8f1492ccd213edd20c62b341f6d381f7778060b3601440c5728f923e9a49d537ee9ce0d24301e4781d10abd2627edef1f76ddc55988c10ac5c73e797fe419b20fcb1b48888518abd0e386fe983a05cda926a38aecd3dd8ff24eb582fd2eb6f0623b154d72ad1c99321d03156a8eca9a9d7276f871f814faf99686c6310f175b984291cadaa9e3c464c9bf175c94427fbb2c7e2df679d41f14e4303a52682969fcea9c43f3f8e606120a72101c3146a18fae53ba3449728cce96238628f4d17c668f7a98b952418c5028c12d799c331f7decfc0da42d8c173140a126bb3ecfc17f36bf952888f109af3b42481ec411cbdd4002630b32307209313ca15f1e7645bf945d92080a7274421fa7ef38153ec58fb5c70d24244e383e4c791e429a60e50d24aa1dc4d8c4a3db8e8e3dc81a3df20652e2208626140d5f72492385cab88891093d5f87dddc162c888881092d227c7eef9014df3d333dc4b8843ed818a74795b323fe64871896507bb07938995e925d48da1c625442f781574e7937a6e41083125ae4d69690888dbff024d4b4d755912758c85993849af1f672e965339faae0102312daf8cc98bf56a4f8e371c4861890503a53ccff38f24072f0f1083d7dcb6da4ba378955a1218623f44148e6bce9bc8d916f84f6bd1922cc77880c3118a146c665b9a9aa388d8c21c622d48858d2bf297ec67314a14bd6545cbb591ed78950e7ab777a70f9528e3922f44f3a41c2fcb84478087ddcd2fc2054ec4148b521f4ccf5c33c9c94ff5cb2855092e651fc78d01e21d41fdd5d25999823675c00b8438c41e831d84d87b20fedc913843ec13c65931f4be8e71108357b9c2e5b687df2c10510fa2032fd583b6374d6ed0f4afaf2d1867699fa38f9419720163ecce4ffd3830021461f348d24e19f2fe68f762c366cbc22061f944c2962c69c9e1f46308b3660c3c66ae1812cb4c862021900430526f004fc33c09fd42862ec41cdf92a989f8de68b0d0262e841ff91e8461ebbfd8977f2a0d4f558b2c6d4ff16731a90401e62e041b18989e1fa7e44637907dd652422a7930f8b941df498196d3dcc672f218951075db287ec5cf5e0bda78392246e966bf9c66f660e4a8fa4c9dc30ef239313c490839e25697c8b9f86560a2a21461cd408d93e5f54580cf7992fc480833ec8ba11b90ff2e3d9bc41af4821e985186e505ea3269f66eb5097471b947cc97edcb961e13f6283a6171aa63375dbe708ae41b1c96333912e5d0e793528139a66cc47163bf8891e889106c57c94e35e54e87166120d4adc69984efa0ccac8871ff878b4298b853ca010c30c8a6518b758397f315b80c18559418c326871feee73c6fd43eec9a07ef6f2d26e8920361838bae82d7078aa20c618344b999268e4c4e570c5a025cdf12171241c2bd80287631831c2a09ea418bb0efb31138241afeb14e72e849e96ed9183185fd07d3c968999533c1fc4c70bfaa71f4cd2b91d11d9c0e0c2b7c0e1553688d105c54ffc83e464fef01f5cd0076192e323ed10337f0b7a8a61f2b82e0f2ce414d482f63d1e65964c31759ad40da42d70b899418c2ca89fbe72c32b8fa2472516d4c862397606bbcda3b77830707c11e30a5a75cedb79ae25a6fd1b488c8861053d52680b9e7978b14bca42030d381fc4a8822efaf9c3ebd3458f525888185450b274df72f6c3e49c008c0da308c7f1e2015e7051821c3b81a41c11883105ed2e844e15e23ab38739175b788103bf0b7fc0510c29e82159bad3f781f6b8fb28a8713925bb3cdc103a9831a0a079d7860b3dfe710b319ea0668fb9e9f0d9c16c27e89f2c24c40a3d9caeb016623441cf17f1b11452d688cf042dd287e42933d34fe74bd0c2c614721e9ee5f9854f097a4bff789ca7af2a840818b5458c24a851d741528574f0cb6e201d190ec44082baf7793cd25cb1528fa73421c61194f8b1414b2657878f11f44829f90f243b3b5efa0bbb2e6214412bcdc85bf1597aa01f2268a33942a5d0a1ac72d8ae1063087ad61e794ec9f4c52d148216bf4e5ac24b730e1f5688110425f5c68b88c803411bd91fcb213766e8f80fb46039879cbb53bbcb3b85183e5067b26465bdb85791471262f440cb973ce5571e44d95b4888c10335cdeba710cf711bd30da41c38b6281583183bd0bb5f2bc6255b1043077a083fdc9c3e649d9b25077a72af1c298feb2c4884035de4e3fc7b4f9009316e401ebd1f667b6a886103c5fe927e768e21d86c6aa0f7a7984173fe40cba228c4a0813eb6ede1e7f9a4ac31e8853a21c60cfc98abf3d74ec54ea91832d033885d5f0f6463ce4f2c3289d18f39176288888df820d6e461de44a143094acf6fddb994c4d810f1414712d43ecf1e5f5336312479d081043de68de0d7fa398ccb47e83882be397df820a7173b5ecc088a458e5f4cca7714414fa9b27ca0793642071134bff025ef31e9aa3d4350a3b2a64715513feebc10d4f0b379264f3779f54150347288e05afd62930782b61f435cb038ff401ff7d8c2ca3c23ea661fa8793a8f2f4592e881857ba08d75eccce9dbc47378a0fd30f6499787fdcfef0e74eb0dcdde89ae8eaf0e947eb964c1fe6737b83950734e8fbff284d2b1340e948b3dda0f7ac91b68f93f4946ca497994a30db45032957bdc23d7ecf1a881622f39a323a6d0404d4176425796b83c66a05ec5d61cb2259a721d32d047321e9b528f3ff5308b85fa831c7c94b23a471e5eb0d0079272de60b1cb5c7e855653f9c34b559ece07b9424d136cbae3e24fcbadd0a653f564e8eb414e2179031aac5072a76bf71c5a2f52bc0a4542e69453549eb9e56168a842d31bfdb1a649a6ad39a950638c8b8dff5115f6a3424fe1c52dc42c9ba6e25368412cd264ca79f0c937a65062c4cef84c86ceaf147ace7c6946b7463e8f24851e6452ba9c3c272fc651e8c1c6aa8366a2d02d4548f767a6173442a147fe7ebb0d69eef70685a61df9a7bef2678ce727f451acfc9959dd9262d2135accca9a655c36368c9d503b5865ecbd14762fe48492f3677c05d9cf395f36a1ce7796c8839469d26c9a5067f3280449666542998d3cf2617bae882126d43d6d2bf3c825d43d2ff91e77b21e6c8e25b41462777856de85985309ed7c9c2f459f58b6f729a17bddf78b973909ed7edc3655ade732be24f41c32cfe5e1c76dfb3d127a4857eac1d499865c4342c941cb53f88eef5cf523d4c9e3c8b4f19dcf3eec087d30967db451164be2dc087d3cb16df27f646d7f3262f790675c8412df72654c7938197214a1a68f3ff67185a9dc934722f4f281841e8f22ef048f2142bded9d0d1f94790f3f84b6417ae487b529fd86d0bf22cf849fb56f4c1542cde371feacec8d105a56fcfb20fc20fb4fa4310835238c68a8bf88b31eb73404a1947867580e793016fa4068f12abaf238afee930610fac8bf2245087277dde30f6a48fcf18b69f6383f289ebcf378547152c4f8f4411fbf5c8f355c7894e6f9a00ff4b285dbddff38cb7b50624fb2bdafb01e74bdcf17e6e3acf51e9307a5d355bf9b84aced241e949ddfbcb114bc83e23986e4588da70f1f76504b82eb54747d45675ea05107a5cbefc343ae3c7978d241bdf0c97db79dc1626e0eda56ceb0e972725072c44bffbeb6051f8f38e89647912522561cc90b1c344b294edece11b6c27603c90b2e368ccff106ad7d142e4836d31cba71839e5dc2a78c3ccc3156d1688396657b7b9c7abc210368b041b9eaf3af921412faaf410be1278fc23b63fc570d9a7ee4a41424e41e6498067d906773f861a4d809153428d9e3eff8c3fe4862db16e60334cea0ff387bf6718ff7324e63067dd09d47b2e13a65fa6419b4102f5b07689041ab081b394bae9f5d8f41e998232fc93b56884131a82952578717adf7ec230cea8f477fe5fdad61d942030c6a1e94bddf07ebf1588ec114d0f882f26e17f942c30bda55cc63f7ca1267aba304834617cc060b687081c616680434b4604cb9fd64378cc7d1b2a0c854fe4f112f0f73e419021a58c8f38e76ce61bc37872aa071851c34ac4075461e56d04896c7d9e38a182dff9124ad5812814615b4143e7cc4f6e834a8a0b6c590e2c38237d003848240630afaa54f1e3e8e1c29a829f78ff9402fb2e3864614b4f68164939648d6e36940410de2b13ed4f4349ea0c4866496439c9d6090a8f07f212b4dd03b488e886091bff24483097a8408f351ee85584e4281c612146dcd696a425dc7b02b41fb8e8afb4afa3dae32094abb8560c12705096abf679cf813f963b626d038823ec8229fcf46ec538fa3610435fbff848be70f5f3a1a45502724ab18f7fac75dbc8144a4010f030d22a0f72e614a27b68130b60b06d8b0e1c58711c6767168083486a0dfc720f775529e3b47087a08323e921f5cce6f27086a1e56c8659a9ff9f40341b15026eff3399befff40ddacdbe3b88e210fdbf7811a2e5c30f972dbcfbe076a8c6d1fb521cb62f23cd0b3041f94ccef7cce7b07fa786c417253dee41fa675a06d581a1fb4c439d0c5b6c763dbfc81069371a027f9d0f457c97e38e11b68123a3b7feed1c4f9ac0dd43c1fa2fcac7674363550ebe3a7fc935431e40c0d1ae831dfbfc514797b7f181a33d0c71a379b7772ec9669c84099f8123b316759b5b1d0a52e2ab4690c9a5312166afcd1c62dd1f2159a0fbfece2886fcc55ba420dd171ee2d851c41be56687e9af24d96c70a7d18ee9764e2ed3d77ab50c2f74f1e8f69cfc31faad0ec2634c5df8f47e28f54e8831edfeae003156a78079dd049ec73e8710a4d346c7c1c63f3a5ac29b4f8b8eeb0b53dec61a550772ca79eb694f7831452a8394bfcf0f73a93fca35063d8cdf9e0272bc2a2d07cac3dcaa88a8542bdb6fd4e778342edca1bfe9a7d90c72794caf5294cf9284f28e1c783cb54d5093d9c6b5bb6af0ff21327940ecd1b42f77a1cf326b42c1673861fc44a59d484561f3a68d09463168b6442cb573932228f88850826f4f0af49766b7b649bbe841a2178984b116162c91996507e5851de3111539e9b51093d7c58e52dfd565dc12dbe01366c80e1050e14740b680625f461b57fdcacff1bc9d662c624f4543a3f9ad47b9f932d09ade3e476b47eae4cd78c48a83d179f6a4ae2f95b3320714c7dfecb1f4c98f108fd2b468c58b74198e108f53426ef903ec5c8cb69841e151da3d3788cd0b38fc731f3c9e4ec135a84b6295992baca8e1b2286021fd80206331451e9ccc7dc1a555a1f185ce4d000116624420b9537e5b318fd4bef0c44a89f95e72fc6f338628f1c6ac3c6e67830661c42d3b9c9839a14bc62792cd81c86d052bce651accf2198c7881e740b985108cd3462ceb3c50fff529e8b1c1a280698410835dd85c9fe9f3c48b6c01146d1c1812387050a023306a1cd79c59eba9472de6ec3068e4341681aba5c3ad55624696f2085f12548090442fbdb79b7105923a51b47185e7051cc01424fbbf3972dcb0f32947fd03bff76235f78c9399360861f148db1bff0611ee5cae3dc40eaa28bdda2b84001171208830b1470d1c51637c2f8e2bd0533faa05588848e7049328f3e1f98c1072d548cbd0af7bdfe617b507b7b906a66a2c70c3da827f93d759765936d81197950427eeed9d1ac88183c28ada1ca2efbc71ffe30e782cd91631f77306b620f4f2383e7109a26ccb043e39fe72c244deaad0ea4d208937d1a3e4cb981c40f155c8e30beb018cca043b160c61c66c841b3cc61c3849194385c413c72da9a080e5908f1924edd48b66e207961c61b34dfca830b3d177bcf9a0a640f66b841c955f107d21b2c82f7b061c3860d30cc8c36a849646308c9df16e0608381196c503f4cc6ff287526332f30630d7a1093891fc61cb3c4980acc50832e3d3ff00e1f864b571e1498910645cc7c2cef038bcd111b11cc408352d2de3f65b93df782f1398a07669c4193ca489bea2ebe2497bc70c10216801b9861063d668cd3ce2ce9eafd32281e3a8f36b28f3fe5126790410d15a2c725620ae1f531e8a1a46cce5e9386266b0833c4a0f67027e76185454987c3a0668e1b3ede9aeef4a7d90c3068133d1d34cff5052dd2e4c6672a33c9bd17b409a9b3e7f3c02bff594098d105a526496fd2f8f1f82dcd05a54333e441ed54f984cd7e30630b7a5c0f626eceae4c3b5be0055ca0808b30bea8c0091a47174c9698a105b53527ab728678679b35ccc802d972d00fd25609c30c2ce81d4473e397fb8fbeba81b45bb8175c1cba11860ab2d0220b0da000470e0670c100e3891957d0b2cb266493a415740bf29f429c3c8cffe08c2a6895233bdbc557c00c2a2892c2f8302e7d06ffce14b4bc11f76372e1979782e2e9f257fa9f543747419fea41caf78310312d14f41e4dc5ff782cf1f9c713742f1f6c6be514622b77821e7e8365b8bbb77a4d13d4bd1ce4b276eef130974cd02a9c4b0fdf479143794b5034d9c57c9553f0cb4a095ab8bb8ed93fdcad1c3223094a903421ff0f740b2041ad1f669bd89f7f1d1f4750f6af36e369ce9fc3463090851659900b6461010964d1c5172a08638ba31946d0d2f496079374e139338aa066f908318dfc7022fcb8810406d5cf2082be913dfc58eedc47dd168171841482194350c256cba7935e086aa874a2ad99313bdd4150f7ddfcf32885e6d165670041bdb8943a6b82c54fa92c3420013cccf881da9db12ae96dbc0f24777a98e1037532cecd6cd870dfe3f440bf4b6375793dfa8db7113378f03066c654165ad8b06183123376a04bc576798f21ea3be60652a10a14abf3c30c1d6816225c8790c53687a01866e4401f45789bfc088b93076f415f988103cdb5355e7e66b4c52406aa30e3067a4839b69c04ebf15967860d94f7b3b8f9bf23879a8530a306ba8fad2d720ed38fe9b22bcca081667fdb216149aa5d9c81ded9272a8f44348504fdc51e8f07954c8f8396266cd267180d6138689f2ee50edf7983d64132b47c4ee3d97183363ef6f0ebb4410bf615f240538ffe6cd02aee578cfbf14c7a0d4a87e9f8a4cfbe1b35a8e183f91813c44afa34e88310e2c1248f063d0f2c57c798dd24ed67d0c347ce18f37033a83189a4905e736ce565d0c7930736b1fc3fe664d0cf87e361bc3ec4ec3168d7ee1ffc36652a5b0ccaa6f8f979d82771ea30e8c34e2739ec62cc518341afbad0a29f2f28a9e327f91c61fff5825a39464b8fc7ee61b80bfac0663c521ebec691b9a0bc0fd2e414e32d687ad31746c4bcc7d182e61572bffb2ffcdf2c28496c82051d0bda877896452e84d0afa00ff22083e6fa41dcdc5a411f4eb8d118d3b2b9ad825ac92e7a73b48f65a9a0e6d59f5b5b874f3b052d65074de537777923057d6f27cb879b2868f9d3e393e2f6e50c14943c100d975d63b8974fd0ecfcf370235c644a2768313e5c9c1c9ba04ed60c99feefd00399a0f97c6692509917e112b4b03dcefa39f48fea53827ad9f143f8478c994b827edeed3dbe499fbb232428527926ef23c2558e8ea0e96f920abe11f4f6698b1c36edc41f45d0b43b4c8eb97f1c2f11f4f1e9f6b8f5669543d03305137febf99c870941cb383521ec76849c20e879976e3af6f0521208fa7feef43086f012d60ff45c62fec17ef2667da0ee6bfce9e91cbfec81ba13e37dccb8550f1e28337d6d61210fc2b2033552cef7a135ae13d2819a3fae981ce249fa73a0c48a7161ffd6318d03a524db9bc6bddb84d00df48dfe916f898f3236503f7808b1af935ca7067af4e8addc23001a6897c1b2bc3dde538e0066a0e55d56cc1c6afb3a0290811af9f312fbcd25feb1d0428555a4c9c342ebfbbe37fdeb41fd0ae5ce2f73489687dcaed0340599df906132d8ad506254f08fef7421a459a14588655c79ccd8ab50daeb47591df3f7605528fdf5b9ff72da64722af44c792fc264bdf0312af48b207bd1231f5bc4a7d0737e8b8b9b2e466f0a45825e2c3b3fdf7429f4ef54973c8796c8a4d03da6fc1d7fb3a4dd5128dfa1b36dff6ec715851e44bb6d6724846a43a1e4758f485e9eec8342f1fafa3cd69dbafa139af60f376c84ad0c1b4f682f9963054956c9359d505cac62528613ba6fb5fe68737f546e4213ed10df2e777234a1c978698c1e990935878f6d5e89097ddc92326946dccdb9841e6d7316c763fb872514cf92c8cca3dd1855426ff7f16e4c3d1c0d414a2865e1f3f52287ac27a1a6fc51d262d3e39d24b4b43399b143ebdd9150b43b426e0a3e92cb03127a4a5921e225cdcd3e4289cfe9bd10f383ce116a8cc4748eec9e49698476db316bd68f9b61462867b1f2280f62ef666d115adea7720b15a9a1082d25cf15661efb1b223512a10ffb5332ed41b088215203114a8a31ef66cfb83789d43884565e1253feddea9b901a8650dc24c404b9389621350aa1c49c2e624576d38f2ca00621941eb3ca983af428e6773ea83108753ff368eede33f4481ed41084ee417762cab0feb8788d4068e59f07d93d427ba4bb0620f44961daf3c73c93d35de30fea757cd38d19362c796af8410b3ffe51a65cf129ce55f4801a7d201b7c201b5c630f9a688f539cf0d6016ae8418deca2e1471626b47979d06724876c9f25474e1d3c68a5e3419285f00eda8f83e46b986f073dc6a818cffcab83d2b9c7ae6f3958e573e9a0e761da9c3cd0cb93833c0725ee7e433cf1102e64e5a00ffa2ad84cfe1e680a89833e0edd1293bd2a5e4a38e896d971e276f60d7c0a91b879143b37a8fb771bdadb629f65dba0e4817b5f0a1179b2756cd06d828b66be9462fa6c0dba7ce5734d3e976358d4a00f46e2a68f982434b469d0a64ae6fada2eb667685023d86d48b23f2ad9cc1994983f089e3d4e55d59919b49f8d97245d65d035b8c5e95455313b4506358d6bd6a0ed1ae7426350732e75c60ab631f96250bbf7c7df35e6227d1894702dfaf17b8041edb4298f0766172973e70b7a86f251ae14f6827a19937774ab657359a30bfa678ab1fee26be4be04391e05846a70a1d860418d2dd4d0829622bae5f6d32ba89185836d929cf9971a58507fe38fa432c3c6cd588d2b94ade2c5fd286554c30a6af24ae6913ca7c6f7aa829eb1ec743ee49436d4a082fac142ae7dd44e41d789ada193e41197181b6a48411fe6ad60298ff228e8f3567d9b5384e6a4a1a0d5e87608e2d5a9f1046d42a69dacd72d3b91d250c3096a654f7e0f69ef083741c9b947974cf43a3598a0e72c3235e93fb987ac086a2c4109315e76378b8d9c3e3a25a86d9fa56a53d23cf6ab461294205f298f3c66774d3e12f49057af799c5bf4ba53e308baa4eb76bf92893fa88b09358cf04024f347cb5c320c358aa056bdf9e8e3445eea89a07ffe51fc0f3608861a43d062fc9007617bd0697e084111eb88f9529093209920a85db5f177dd7b91d91b48c503358050357e709cf02c79e4ffa30b357ca0a698644e6a3b43ce833a50a3074a6565c91fa590c7146af040b94a176fad7b929354a45063079a7b995c4b8512f9c11aa8a10325cec5d815d344f0705e161a68c0e1a891037d3c081f54a4e483eafe70a0c41f75e5c6902bf67853b250e3067ab8698685c862a1860d949bfe90392ca5147ad740ff9020fa319b32884b033564ce6977dccb9ac72ad4988162a13b744e22d3126ac840fbcf69b2ddb4a62c9928a123165a8c31bb7e9cc20fefbf1bf8385868a9fe7f18f1a973f29cefe25161e4c001465140c72bb48850b9e51564a6ee1b44872b944ce2a3904796b743472bb4ca21e798e70d2b94b6ef743dc8b955e821e665b1921ea80a5d3b6e67d32f3b52a15caec9eeb4e1273ba50315ea5c548ab8890f3eba3b4ea1849f1a0f731e4ee3ee308596f1bb7cbc314a2e871da550378fca475ff19b531edc410a65c2cccf235ce8b72c023a46a1e46b884a7972041fde1da2d034c4e73493efc7e1a33b42a1560c3ee73f39bce5680f50e83122cc649a88933fbf81543a3ea184f798f5badb462b97c320a1c3137aeaf8aa1052859f5e8ad0d1096dc25745df5cf851ee0618db051885ca103a38a1e987b81143720a4123613c1889838e4d283fd210ed79e29a382a86983b2246ac0c414a43ee8efec831b657f4c8843ef2d603f1b17444496e209da07174b185d30970744115001e7460424d33d172c925a472cb2574d730e1c3c95a4249e1b5fd83ae646eefa312da448a94428610c3f5a81b4894507296db0e6fd390a3a241c724943c1e8d6e84e01295bc1b4846061d9250277a946383ece5e1f73790b840f2e201140935a710392f611286175ca0208c25a5e880841e3d90785a9b3e69d2141d8fd027566756cca9bc1cbc81f439fa881eafe87084523f9a9db210b37af43542bdcf336797794ee67a420723f4303116957ff499ecb308bd47bfe42eccf648378a502cf445b6f0e341989c89d08761cab4ce7f3479e022826c9c8e43a8b1fbe3acaf89d03f1e86d0f3e59aebdca09d462a841ac1bbcc3b85a78d140f741042b1edc9d569b4c37c5b818e41e816f3575bda14c7444341683d3edade93ea2a107a488cef9d7212b9b2041d80d03ca7d91e6727c969071d7fd03f5f849c932a931f7803290b0d340003366cd8681b362a003de8f0837a392f4fe581f87c3954f04575d1d107250789d5d9228d75276f203d5cd0021c26e8e08352d9d19727681eb78961e8d883e6f9e3389907c1d283ba673eae37b31b7288f2a0c46e4c16828f6adceac083a217cf7bf85d27f9c37407a524fdf5380fe747113aeca0661eecc4d24c21631e4643e8a8839241db36327faa343dc0202174d0411f750af379bce962a86c0a1d735022c62c6bb00db6956a42871c94df0babe8291909ed16641d7150efc79d3af338747ae88083fee3d16bbc0c3a75953d41e39040c71bd4bd3c301fe5fff1e83fbe821cdfe106b56d83985e8c5073d1179ba3036dd0c642c5ceb27924133ad8a07de7e032e5af41f341e4eee4717752de2ca1430dda56181fbe8ff328bada34687a1a43c7e6cfc40f4403d91842c719c8c6093acca048e75108d96d5b337920123acaa0bc65281bcd2519f41d0f73e521b6624e080942c718941ff4b0b7e3c73ea14531e83926eb8e9d0cbe57150e3ac2a0dd8f4230b99af848b5163ac0a0e510248f877997efbc8342c717d4901ce2e39778bfc19a4087179492ce4817e3d658cb8f1f7474c1387a75ee317b7041f9cda9071f4b3cdc47b720bb630bbae7f614ae37954f8f5b418716f48a97eea73c7bbc704b181d5950539ee95d6c6c4faa727460410d2dd92c2a3f75f8e145c71594cebcb3d4f997a9a20d038c07163aaca04b8e9c9f77d930c0285968a00175858e2a28592643cc4e5b9f2a2b745041ddbc793febe6ce2796dd764c419bcb59b4ecb3698ec81cd021858e28a8613cfb279388f520ec400714c846c7133493a8fee8881eb6885de87002d9e86882b677d27ef9310f275ee06082d6e3f9810f2797976de8061251161a6800185fe0c8b1001b36c0a042c712f471458a6705e0815eadf9d2a60abd21260bb0037d38fba34d5eea401f5bc873a0d4778554de89bce3401fa41fcbe4fecd0ddf40ffce3fccd97879cfb381de916b7cb8d5401fd60fda47fd8306cabceb77a4b80033d025579fc885f68fb202c840cb7339042f4d1d278b859a43eb66333cef64b0502657766984fab1eb15faa6f71f07b7d215fa55c591f0996b5fca56a8133347ec902db927c90a4d52de4f15925c85723d1e75986acff1915485563166b2ac7b36fb49853eacc949521e9475f6a042d3a0b12363731eff29f4f0631f8f071ae452cc9b42f9711ebf50a19278bc145a6bc6acd7f323b39c144af8493e7c4f8f42998e217bfc711e4ba745a1e699f80c9d50a897623ebaa343ac41a1e509ee1a1a5fb2fb09f5073db6799447d9233da1a4b808a17b987edca3138a75eccbd8294b6ccd09358ed66484e730576f423df3f145d649974ad6841a62edf7b034fd28cf849eb47b2439c4841e3bf7488b463e6f2fd1556eb7981f6b0965f27878bb31fc307c6c2594f6c15fae987decbba1841e3d1b730f3393d0cf83f7fd8b65cc2909cd437c7fa226680a4642cd95bd7d344148a8c132b633f5bffc8f5073bf684890b017738ed0aefb2f4365c6fb1ba1767f94a6deff81cb08a5a2077539f558843e1eef0f2e841e8a50734d5d8c6521f73025116ae8ced88f82082505eff1864eb6fd39841adec93dcc86d0071a3fd23c8e71268f4268e9752348c808a17b0e1f3be583d0c76e1d3371b36dba04a1eda4f423d71f6da52b10ca65b9f648e44f520a106a9e0b1d7a78f5da11fe41bfcf7918c922f483e6637bfbff0ca779827dd034f5c596583d1e4b0e1f9498dfc79de23d28992cd2d9868d95eb41a9f1f3095e5561e741e998df94af197a1c1ef44f113aec7f3ce6e3efa069ea7108e94b963e7650f238e5d01676f97a1d120db7d341ef589eefc939e895da420ff3c5203d5c0efa650de11a5fbf07791c34dfe42da791b747371cb41c7e38392cf4e6d46f507325b3d461768362d29b4376aef196dbd0691ef460831ae1c24854464c7ed7a0568a98796e9f76b36a50df3737e46d678e360d5a7db09c7e3468504ac3650ee7dd2d9933281be1d6e35196f64e9a41ef7bb9f249731b9365d0bfcf871dcc0717be2483de3e12bb107423b36350b3e661a61fc5a4a34131e89962c494938f4c331806c52f5e4e2ec5cde3100c5abbdf8f73d426fd815fd0ec731ecf9985dbf6bca047740c693ae7ebeebaa07eae18333f0a49b4e382e6d3f723d93cb6a05e8c8d716942b9f9d0825ad2d9c7199b77793cb2a0bdd578a7ce0a3e4ec182161612c647ef15f4091b63f23c12db70b182924779e49a7bafb1875541afcaa92ffbe471fa1415b410e6c45baf296891b4ee473e8c8e2149414bd2315645c7b11e745150f38f25e6660c4f1a1d14942eb3dc13d48e375e5631864f91738252fde391be8ec9e5719aa0cfd486fb1c3a85fa99a04d55d878f32f41b1d8f6df1e75871eaf045d7334fc38c79caf7b1294ced2ff23b13c78db91a0ef848eba0fdb95f123a8793c4a21fff8248f6904ad53ec91857b9d5ce92268622edd2da137c79a085ae6d81edc59a7be7a087af9cde5c1a6ec2fb510b44bff9b6ac6dda50e821eb93b7c14b963923410a8cd171f2549ff400971e3e77777cfb90fb4f3815d1c1ff4c8b23dd042857ce2db1dda8379a05430dbc91a9a67e31de8fb12b73734a225ac033dbcb7c78a21e26673a066687ea70c0ed40aeea3fbfdf23ef6064aea1eebf48b5cf5581be8e394ecc7926d0df43457e692079a629a06ea87fd96cde447ceccc0daba3fc93902c8409f90ca58681de3deee7f6f7610165a8c65ea7821e8bdf80a2dd8c49be8923c7ad1157adc8933127998b7b956e8c3f26e0df97ff8dbb14277cf63fbd5ad420d9b2c8fe57f10c77da8428d290fcb42fb4c071fa9d03a3294f4383c6e7ba0429f5c5afbd3e314caa5cae8711e052d8f29f461f87d5bae8dc12c859ac7b653490ea963a45047a793e77ddfb946a1bd8f355bf6bc175211855ee6dec3cfad1ec7a1d07262d3a76059e3c340a1fd6e8a083fd926f909250f42e51e87e9d0a3ca13ca7bb5e4f1707a74420d27e1666e7a7042cb831c12733f4867d363139a5645984d76212cf4d084ba176f3ed39b77963c32a1c76dfe9764917b431e98d053cc30734f33e1248f4b28d6e25765d695fbb184b6fd3d901f7e2aa17926ed9ff194f3e35042cf9f760de1b12fe54c42b76d696d1309e9774968b33b79ac1b325f8c47429b91cae595afa77448289e53f6fed11fa175c6ffff95e7f4704768fe49520e1f6493c51ba15ede9c2d5132537146e852e383998bdde3b917a1dce5f154da72ef582b42719f6c31060bfa552742c9d5bd9d268d0835c585b07910f5ed398476729633eef948b286d07d7bd431eafb5ce442e8231fa52c2182f4fe845037c47e3439febd66109a5ebe2c713fe57e46104a99c4d31429a53a104a7e2a0f7173aef700a18c5624f794fc83f6b7d1a1537e50e36b53ca1b39cb04fba085a4df31c49af41d1f94f91019ff911b3d6e0f4ab8c983a45b39ebd7833ecaab6dc9e6413dcb56293378d07ee096f95c27dcbc83229bc71ede83af54da414d7f93b2a41e26b1eaa0071fd4e7d1d20bd5830e6a98c56e0e3de6a0c9e41472940f27c772509264b1dd588983de967b64c70207edb2ff62e2c79a83f6067d5cb941491f4683c4aab441d11caa62b8ebe43d296cd0c74c26244eb77452d6a0fcb8c332568a3f0a93a206dd45f340be6a9306bd2eefe798b3588f23050d6a4e2f5596edc77f9f33689e2bf7f2cbe5781f3328b93df56e84f8703a65d037267fc5d6c80f3f19b41f8714f3380f22bce6c7a08cf5d9b95df778941783f2959f4268cb113e1e06b52b861e8ff72588e660d0535b7cd9f7a8d2e55fd0a4c716639d5267b6bda06d9a8fa92cdd056db6f2f82da610233317f4c15efa1ea54e212d6f41cfd394f3a75f538eb5a07cae9c92257df9146741cb3dbce4e33c2ab38bb1a07fda917fcf15b4acfd9942fdf0a25f2be8ff2d29fc707f5cb355507a782e97b4226ecc52410dad31d975f2cfd34e41eff1a077279f4e8c8d14341f698a2f2974e81f260a7a87b1ed1ff5a02333507878aef9e4c7c927a81d37530ca907799c4e27a81fe65ede1dbacc6c825e21a7c903dfdd503341771f8fce47215c82a6717e1162c8332154821a9e62056bcb95259804bf3e77def80f097a654af5e3903a4fbf23a8c993d526edfbcdcf087a947744de5c11f4d4e3cddc3ed2902a47046d5cb28ff27cdfa76e08cac6d99cbb6cef1e216892bb7922b3976382a0c7aee49fd9966102410f293784faf14efc1e3fd0b524f73b4ecfa63c7ca0551e4e76ff288fb2f4e881f6c39c7fa4f340ab7495a2f220fec43b50b34d12911fe4895307fa8fbf07b5971ca81b34f2605270a07ee6c848ce3e2ae9068a6b8c7d3e48b101d840b71993b01cd3cc311b400d74d33cb6e0791a28b1b27eb8a30d60065acbc7d8f1791c266403908122631accb7bd53bc62a1a6fcb10ffa8265b00b165fbab35ea1be9c6e964abbd5952b9419bd303afea53faa158acee468ddb8f399154ae4417c1e8f3dda33abd0a72fe55715fa28dc2c9467f1414c2ab4cf1b72eaf1599012154af6f13868b6303e1e4fa164f0bf904b830f3685961347c73e4f70af145adacee3ce791029b44b9dac4ba351281f7763ceb9a7f3118516398b97dded9c855828dcdc19bd141f5028fa1fd27e8ff309a57a7072697bfa53c7135a9ec7d0bb2179889c4e681142c831316977fb9cd05b5247ba1fe6618cf94da89debd7843eeaca4172ec8e097d26f4cb66e1ca2bb9e71c137aaca8931ec6f9b0fc125acca37958137a5c99b684da1a76317f7c7e6057421f44c6fbbd85f0e39912da87e0f6a36c3fa89627a1fbf5a78bfdb399074b42710ddbbc1e1c097d1863c71fb75fc5144342d31c79ac377bd6107e841e3f7ca59c7b58d3631da1d8858ff81f5aa6cf46686e1fae337de5dbc9083db65d59ca7a26722e42fb0e7fb1b267d31c54843e4839d5a63111ba8f25237ef23ccaea10a186093216ee738cf271082579646ccce6fe9e21b4ea10bc83fdf0258f520865ff7c626dbe4f0e21b43dc91fdea7b6c80f42ddfc6521434c107ad21c3fcc960f84a2b9a5ba4273e8b18050f347b1872f9d64b63f28f251794ee2a498941f94081f0f47d2fccba60f8abfeb45ee880ffa75fc38b784f6a089f530e4d4e7aeb71e94bce8c9a942fa619907b562a6709bc3f606f1a0b659f076ef0e4a4c9cdc8b60d2e36107bd7fe43945dc6099d5410da9c28f34c41024468a0e5a327fbf0edb992c5273309d7cfe4b9192832e078cc3a8b46e65e2915020100684c150180804b8dc2e00a31408001030228fc482d1444e5469071400044528223e2e2c14201e161014140a160e044281303818060282a13018200c070382c0359ce67e49a47b5c54f1dd07e8aa76b4850e1d3b6145facf2950e060aa2d9544829c2cd02945ca743ad2a082db9d9f0fcbc84f361047fce44b6847e42966649281cadde704a7bd3597c48b64bc6667f54cff046dfbefdbc53d3ea10d7d7270ddb33143ccc8aa05b24366a686d5c4df934b8e3a151856832aec2e4f75cd676673442b41db20bb6d8de94965f867e9a998450245facaeabdb3453ebab314d70e9e912141de5c22a44385150b0a8cd699457d90ba69b69fe9f64ceb84751d5c3b9554aa0369f4aeb0904b9cc10a7a72de4aa8362a845d9d99f0b650606736b5ef34d7e670a2a773e3b8480146e62ebe04f6d1e141499bc06bf137bf4ee75de6397f0b829c679fa833dd81e5f37b32a7b995cbcfd099cb3e642ec484bbdf8bbb0bb30fef532cbd2bdfe70bd43e6ea002cdec0bd886528d3e1e379986db9e9a4cda2539fd685721927be9ec061dee3a3737cb5cbeaecb4f7daaeed0e83d9fd99865dbb156ba6ee266d399c4ea63b88243c29c920cab7e7a88774d9c2a4d19251d9a7d6f8abf22d6d3d3878a9eff84a7f94e97f8fb7851e09dcd1b1dfdce66c91c71d08dc26c58816e84959339a4df76331ba6ccd638096847d14fb0ee0d66eaa200f549916475796bb517b058e5577fdf8b73cbf922a39f23a977ca1738f169dfcd85dc303541eba7cc5c8d84802434643ce57c9ab20e1d2ad761d71acb67f6e91f3a2d52cc22d9371f47c5881807fb609aa5afd47911d348c699d4258b381584bda66572a95c6efe4d9916715ca2ecbc9b99f2e30602f5897f25e9fbf8d5238a1c4da89bbcfdba9ac762c449de1572cb999fd2fd5d728a53bd998bc46b3d5e40899ab8bb736ee2ac3736dc0efcf8943dc891835cff8e4eec3617c67d1c2412a7d9cbaec45444ac59a070c18cfb9d12c326f4cf40dbbb3da17e8206db8ae9ff6dce6152d4c1954ccefc4c7ad8b5415146aa172387541cc00d263a19b1428fa2750c622adcab13f41d1a84a72226353c1598a9f477a41cf5dc3433c38bcff24d2e058fabbb8eedd1e5d83a0bf3fb3d7bc8838b8cdf1a3954e2d156f97e3dec6dedc31a5b1fa92285ad785b2eb2996135ed6befe8f77da953962688e38e5d26cf329edc79946f75f717beab339d2eda0efc6e5d66bdef959cd7cdaf7d566790462fbad08eec5de56aec4f4966c35ba092dfbfa1aceecc8464e5fe6c8dce29322d40e564e43d627c1fff1145dd6ef71845ff906655d08b384670833a74bcb05703af1e0429503530a2fdb96fd4f70333234f123baf1a1d85e8382f6191f791d7ac7bab67f4bea168e0797d1bcd9a9b9ce6f4ff3e1af449a290cecaa7a8c3da2ea5ada4c5d92fa31fa5e48f423192aad3b8e155be51d8073ecd6cf14f779ab3a2d10b16d07bf6abbfe8fd9cc6bce8a45640fc21117dcb9539509c6a5b83499a981a1f610c2ba9374b1cc62b22b39a3aa9d891021c1a27354a33f798c12d56061649f35bba364b5021b577e71273413b5ecade65ce60aef8939dd3a261b576aa909b397cd46c9551e03ba72ebaf21398383cb46bb4d14e6a6c2463d394361bc17c364d38758529d7a99b3c9cd6d49f72ec44465ff9e83a1ec18ea3a64e1893380008c5a3a212aff9f15a39ba733aca162a0d32cb338af3008d3d3a1c56e38acb7d31d8554def87cc6f26d33c8cff47093a9317bc20438f03fd5f84045efe04649607b7dad305b44c38cbfb7997e4b21aefa6adbe5a76b974fe343ba10ac1701062d30f3c3857e3d661adb005d889277fee2ef260709e40ee55c2ec4c8399b4ef684eb7c81238d734ce51e2197f3073c1c44051f863268c4b2cc2cfe4ba6df2c02437ce97f00698f5c167f5ced4af050e8149edf6f3336df323ac1849846a736e61863b3436b5e0a9d49e52bf39b7f2bf7e1712e6dc9292b12ce4c00534b210582e3d58b56484282c2ef8ec4a6cc2f6cecdcd9f51cf52042748721c63c5d23d394f70e5c5a0366fa9606a4069f944e8c4e5b3f8c64d68455331f91e4569c2f87f42827d5b076147aa3978b0fe60ea8c2e6330717e2196ee0491246188e7bff9f61f0ed1f8fb19557168fd7b9f988c2e4a8a7fd615247b4042320ac502726943ff15788751be2089104ecccfaf184fcbb6293da0f9022df1ac05caa105bd03c8e690eed2fa43c087d4b37dd5741b0192e7a2e985413aeb01b84e05ab36cf4aaaca8a973bcc2d0104c5ec87e2148eb1a6a7a6dbb6b1272fb3b9d526743cf9abdbc8fc528d9b5bfc654381058a864407973ab031eedfb72b0665c46809de4b30a725b35aa65ad37b6d6885a9fe01ce43434e10329a6a17f287b752c8e935b0a1f4a9edc336925885544d969075fecf36720af5c40b75951d49b5df0c012724d71aa54f9a423681f76729a091562aceb6850371e2b6e0b191529f27064ea0bef41c90785b22b582f4fdbfc88b2768999e70308ed929e11e49e6834485c7cf57ae924dd47367b9d17ad68c2726319902cfd0177d692c2af0992dfd702a4317ac295c728ece8627601ab52f0ab6709565b477d7e2b277f1ebc24450a164967ff24d60011bdbfe2dc65eae5d92526658adf08318cd0c1e7e82f28a0117e3ab7534940fd2139811212195e4f55952ffc36e96fc5f586a8b78a5e811be7777cb7d4879b5a0a21e29945fdec4481eecf1a7ec45c06efdf6403706fff8f8952763225dc552609889ac4f6e93b485589e8ca53fd35466d123577c19eafe4b4c9d73dc7474b46866191377e98ea3b932e10aa0b9a25fbb66572b2425a526ad8b01a962628d2534ee240cb3b99864ab9e1bad585a24ac55f536bf1ccd0ccd20b627709963acba796b4e2ba576033554cc018f8114659af930205db0959f06d9adb4c04a52d644533c38d97c93bd17451a1a1ca1b9c65f1fb6c6abd7b7400c0599c9aa207430dad3aa5804e8f9f127d5be40c7d1b15d22027641433ca8e708dc76819c611fc85acbac49210fc84bf0f553b879a0013ba4a2662f3192c756584a6d0bc45f1a03dfbadefcfeae31379d14f4bbf25ae3553a8192eb73b85266774699821a77e3cc1676d72c7ca1d8d4126752dff500631ad547a8ae0a9b519382aac1d61f74475b13d1b437a19752909d9c29780bb61f40124b0e1669ad282937818f6eeaeac1a147f9ddaaa5a00106e97f314346367e01c11d7b0224517e90843b69af7e7a83e04610cfdbd2e06678e40de60917f761552f11d3686481069b4a15aeb10723589ba448e9773f026b65af1fb77ef48383729d2563a0ccbdfc197c0ee454a3311cac8a41d2173101b79505169e995902dc244250ec56429ef79081527478317627268fcad1ee12476ded224cec369793f8b645c21a2cc0f892e4eb591d0b1e76b325b7e48b9b6689d3001a82572598f5015dd85a2cfd51e19b0dc584536300629ed4985424fb8ca9d0b70eaf810d9fc053d7f85c8cfc82f4778618516e85666b91b3597c29e49104ac5a5207cef523d22bfd6aced08a493c325711d89573e40c04da0a2eb155e6b5845aa4129e21f32cb540e1da87c2718613eb28c128575de6c6c7a881e22bc3637b7c907050d50af0ef5d3be2e19f4a57dc53661e95855301f8820a596f30a69f9228321324e8c2a360868822ec30d242440eab23af002035e779af8e7065e2c114e16abe04a83cd675d6f01c36e8a518241376be7f4ef80ca8698b107a3c36c3b4ee160141f3e323a771a51621c8e898d064dca05066ab7e0c959cc4cc8d292093d4f7433386897e12a3cebf97ee1a3ce06ab8972f59884a2c6e79620186850e181aa614afebdb686680797e0bdf55007a8cd5037d9a84a18984aa593a08d6ead11e3303061af95ce603d3e4411e309f50880633ff18cb2ac98ca141c72f2a73a0a366f63c873caa8c301b0a0e66f89667a71345ff23dda58acde8fdda2e6f0107e4727ca005241cb2637bbbae324edaa007e145d0924383a30b906c2a40de20807732fc286741ad68d158909ad2d66d3343dc448a7c2d08bed592f7d5594f2185179751687a1f830f0aa361e2b078b03e89992147865d27fe29b3df53a2981137b961bc00d45e16239736ac51b930c03fcbf5518067995353280fda3afaf3591002e47f9c8d1ad88a043ec20c2b74c6e70550bd01c0c4dbde7820f468be866236d65683420c668ba7e4aabbb22cadb8b39e63ded237b679d8fdfcde522eebc33d18b767295f04b7a9f7b87338affc2271d40394ef33327d0c314a86b034936cacd6f7857c24a62ee0443130fa4ac46ea7fa9305085507ac300e9602b29b3194dca64dfcb282b370686f9e84d94e3a1be8e0e0c428f5f83c1ded00801cb2f6d14431c78c688b5f1dd4099bde41456b34ba8774f84a4657240c9d17d3987612c235396aeda2bbb8af9fe40469b714770c9dd4a76c8809a84ef818b2100fa07a06e77dc5a2581ffcce650a16192aa44d5134b8368260a3d5aa1dac8c6e6ffa45996d72c6ebcf7bb7d205f93b547d30261232f75ee83269d8ee950b66cc41af95f614eeec1f26bb10ef12b04bb009ba8e4481a955b77b83441dea33e23cb8147f6f6fd12d2608a61cb40fd72109420c6df46a596a1c9d4c1160779054a1c96a51c7af1cb40eb4030a5248a256665673ee8ddbeeb080f309512225673a618d8d25ed6e35ee8c17355685402d15ffffb530ca476c244fd19d625803d9db0952bd36cdc438f4f9f74eff9c019065433dea639b002da58acde7a5d8fc07f7c34aabdeaeb3588ae809409de31ae9125c808e4660da224c8e0d858d05a72f4598874c84b7947ba89c20ef8fde9c0928a3c45519f3e04d98db320bf3beb229a7c9704f6e547a1ccff8ec409aa228ea73356dddcbb39bc9bb5649ab141a192a2c7ac84097e0e97e9f159d1c7d9fbaa56a4397437750959ad4337aca914151025022847a023a1abed4a312daa0d3e4a867a93beaa634e555463a00618b6914d2286659181f1163dc15b37722020e4acd5847d640d74f7fc48f1e2f9fd92d471e0002f3f9d7acf10110cb8375c7cecf23d295e11146a6d9fb4cb336cf34bb6d32c98ae513d0d15858e7fae19d2c8cec469a229127161215a644cd022e5d6d4bf06554e6bb78c76412011e11500e98600f2bb90da43502bc4179816ee0391009b0343042c198fb66cf52075716eec2258220a04b70b21811c94206e8c86fa05704d43fca4a85ad07d951d939c449a8421e622c31a5d83578434fd05eb9d738880bcab0e20d614d804c3e9e3fa56231d11371dc4f41aa3b7f36686cfa743713dc749efbdee6a4cdaff8666c740b305dd984a0cdac9945aa2c2b6660df88d7c29961ec41e3514d6e005d51cc1e51318b43af90b24b2c26637c8fb8f7ca23d118d01588708538ba684cc7e77b023077ae3c1b1375b2a360a9b3620c4f93a8145e2c8a1eebcd97fa8a012275f24d38422078bfcb04794654a1ad3c36ab73204177a73f9b5294b79a2a0ed442f5f2a64b22ca267e979644609a51e662fb834603ba05baf2eea1a49b181f06b63d049fb2dcce479122c2c0bbd66a376e2169b1169eff26c64b451b3c031134f96b66980be37c0da1724ab1261ddabb7cb4d4c9f181e7c346053f4d4aec1f9c792a3889967598c1b771381b22920cda1f4f8bcec439e7e0799e9898b7e51cd79d6e8cea5a303ade6c14cfdfb1715afd16681a2def2a5d31e22d761ff244a5d81415742677af783f8436f8955590a128ebdbdf07949b188c4b25a36030d31fc03323e828997274b8144f9b3acaed8b37174d7caad890087e0a6abcd8696ab0b68112272655559bbc69bd836300203ebc44d1d4ab10f16c4127bf1422a6ff84c77918c58471d8754935c407bd01abb6d1af57b7538aba46d0c6353ce3415c07bf6d049905c5de6be26cb7b681056521934a24e94225c5483e558294dd066d2d8a65d6768ff747dabdec6f95f4aa083d253c1bbc53bd9a5e1c4f86e705ef90cadb98675a01bb87dcf3cc73d3dbe425d36d73a6f66c828745efd1f9cc73fe7941bcce9637c15fc47354eb4df1c07b9efcf7d9471e7699c1493e8fc35ba0c64f8970ba87878a0858a4557b0b50eb2604aec050963cb4ab578c8c9437c1f3c71aeae0e5fd5408a70c1e0ac01b94cffb4be3ef449d83f79c8ab0c76e1e7348515c38590c112825f76764ae4a890f78fb062c4d521705f6fe72d61668265eb064ddb74592fd9ba2ce120ba6d7272e449a71cf0e165ebcc40541f85c47e1e6a405cdf1b95c572dfea0ead126a0cff9b0810eeffe9cd032e0fcbb87b5127600ca480eb834e40fb01f8ee1708edfb2a33518db637111e33b582b6a8b0114c500f4acea6bb334da8f7582639e945e1a50c6eadb6664712adade155c4feab69f64919622ba8c17216dc0132404f9b736beed8a637803849440d540bc66d211be82614431f21629212b70880c49c2ef96685209a1a925ea68b011d683c4516ee9046c3d4eb5b65e4f9b37021aabf95c2f695dc8b4ea9019d2238792c602f59a487e4613d73d7cbbef0d7f8d2982b14870dbf70a614a5038cb1a50d7baf5ecde8e480d299e6fe8542e8039430c5d6c5caff185d2f842af9e825a60a9af57696a20abd1544a30ddc23c1105f9ad5c25044b3fdd03ecbc9ff5bbb5c4bd7c19a9548cc923c51bb596d50f9e61e1b9987b11878ad6535abf9634325d5acec033b45b642c793f338cf0ccd1e15adc46c4333b55e1717fe50bb968a0b10e1a8a3002f1e458312624a71c9219f1c752defc60402028588cd260023d48fa7f530ce423422efd2834f3cb6804de8e3671cd4348af5c8b46ba6a082b0ba74d1247004c6d5db81068a4e039f288e3a0c1aa370f3dbec6b550d0e2057f8af0e546927bbe1617c6ba61057ef5405cc90dbf925ec31aaca7d2831aed7ff307b9762db5a7d069e8d169f5ba4dabadd021ad33b258648956dd5229a2aab0a73c336901066781888d26264938311290ba18de49810ad26d51e8105f5ca0e54710905291b1ed85dad6c023949efdffa3485b3dec034b974f663f34526398eb97abe045297a9150d172c4125410984e94cb289a26abce2ab1a72588d4c708d70901724f9edde02821a9e0089536304cc34a5d2575447129f689fc7372df751b7a3895840f27ffdadccd9cd4bfd3eb7e650c6d0eae287e1320cabfe0bb4364303cfafae27af953afb78b8731b99918dc793853350da770c2a7a5010a96949cfe88f485b3ccb3169871f46741992e9c0b0e9edad5af6a3121a1c8f9c44fdcccd9d77bbf734136a8a1dc4182d6520e0e0be46e5fbec72404c5a373f69dea950415b0c59d106e3d76d2bd441349c0fb12af4456e317df5003bcdbdf9cc7bdc55def59e41c3e60331482ef737bee46bdab8bde38651dc8b9b70a14c797c188c3a7d154828cae223f215723bd2234cb612afb5bd8c7bcf6e0d6100a5f6f4a5bec54c018c6bf4244dde57ed02c9150de8856f7ab8ed852357aa1013aaf362c9984817f9c06fd2193ca2e798012eb73b31160dcb26a94c0dea7446cd7da39849024d324b19458d49ca71c7b19431e2d5acadc8eed25bab38faecf356b43cd165f215c5c4c91c6a999213d15fbd8f49452eb275096eabc17470a991378165fc810c0bc230cec8b2f0d2aecd02da835b310763738ade3cf4b7bd701a2af1b62da28b570e110e683331d0f3d81e18c21a7fd6d7de1093ee4b3706172c5388e433330d8178f402e04395fe0cf0e53152c960bc7ed3e4fcf057738eba53403d86441055297b891e62d2ba57be174437411b6dcad79dd3734546a7e3f47a023253df46b49931778ddacc12c797304c4aee2e5909116d143551d59ce2e36d3592fe644c4aeeba4f80b0af45e545b8e13e7bc30e0bb5013c0422f44bc4501f3c8eef5f001f501ad6506b0f7b7784e8436fc77076164b4e7f441e547a5d9c202dd5ccfdd64ca3628e78842cc3d13d5cac27ccd974fbe8d48fef5edaa99dcbd001a741e583ae81339df2e5151e8f93412b13892a3d279911e526d87d071bc4538e34593d54dd099a7255c5c19b8a64b4269574c90a00082c24921c5fe4590a40bf0003686555dee4574461465a088696c4877a26fdb34a925f530b328f64aaa701809a61b3348416de04733629860352caa3d1007599d1889de0cdf06438420fc91e4984554b1564be001c7f1f230956ff6322c4f0174d48ef893d30c3fa42de391f66a72e2ed2427f2ed33dcf2983c11a8cfa039a908f95c9fddc4cfec08d1d910cd93db06b45396a3882c8ea0255466676648255be11ffe2122d9cb6920dd4e658fc9fe9bf07f7da23b61e892532efc7ed5e8760be0413bd03b2a9bafb1c81020ff8d439154c73fd50229b05584ccfa98c394e8383cf077819eece1bc0321403ae4977792be719a09d9a2a2249d35a406e854112df13804f93cb7b83508eb74c8c5497b897148d0bdbb1768b51cb09458a8f79c42d3222d3d35dcbef9965b98dd3505f5b8b060a2d255f2eed39c732ceafe26a516b85167b8176871a411c15ab086e6dd2c3ee27036080e410ebc2889ef60224f3fc95a32c2f760bd607a1559d9819130020209826b86bef9c1e25ffc8003bc2cea31e486fa1641c0ed41132611ec016743f83e718dcae4dc6bac33b81f7421b1af4eba52b469fde083003fbbcae3ef0893a3335ebe42663d7b1149727bb68db117c4b33eee376b7baab15e1bd46eb84cda0e6e6f56fbd5585420ca64b43f99444551105fe70e247ba1ab7e0bd8355588a6b2eb560749b5612e571619d335803bcfe68655ac47ddb8b814793bf0e96da53f9728a77da01b4d65712681467d32185f22966da90e39f7ef6a4774d692f8a02710dd70ee1b598e47bec09fc89cf0b06bece904f50d110c35c6fe6cdd7f4d945bba9b7e3a9c4271f52020c38c9af592d8728b394d9b84faaba36c4d9c1624b35eb8d788f56b1833d2399645fb72a9c7d6f05e75ff682920930c5661f2440fff5abab6a4f7c87873c069fdae269d476b57a102e9e24ac1218e4b36c6b52c73e92d631b0c9d81e1bb4f779b1a53e3ed913a77cf62385668a244208c88f6e6777bcd94654fcde97265d61bc4f0a3d029253147302dcb04cf5abf8d2ec33c0952f88da4c151aae0ce9bd67a10265a3b98206a05ff23cc2520706a499032e1e05a059b9c5fd33008239c2b09569931df0da4a3111a89c731d75c31c29f0600c458438255fa6d046630cfd8f3e6880f425c67d9f0c7dd3af45532d1b9e86bc3429843548839fc5e15ee58479c37d2920ac848d31b6f75a7b0bc6ba71b5d9dadc9b9145aba5b94c4b614d8e81859bdd9847fdd222aa69517070b65c9e51cde55ce908bf1c5c86773ef71ccb7ee39d139461c5f18d3f59ca921f1fe2d877ffce2cf1278df99dd70ca4211e3b04ecf1f51ef0814515d2ab95ee3f3e064761c25657a3c52eb848500cb2042057991966be457d4886bb5663e973253623f8f604ff800b5449e809aedff2ea23e7c3b9ada5c0893fdcb5947ef9b00df41d4da7f0a15e9ee905f754cdd9c9c2886dc731056aaba3db5a1b1a05a28fe98e5fd4948ca121a40fb387f3af5249cb4e6859a8ad79c3bfbaf55a6321ccfa94b02c12805c22f1c13332cb397818e5dee670cb6a402af6cd852553037eaab86eed74cccac9a14bc880b6c3e72a759d452b851e1a0bd66fe929a7781ec3dde77d250a1544307ff04743d96a087f505b3b80035ef7364e5bcc6a9b7b31801cf222e281e7a2fb6205c7f59dca7cb05d86297b49cc9f25435ef506b760e963d3f3fa609006d25671479ea70a34258eea7b12cfadad0a35fce8d1bc73649824e3d5b2d08440548b325b54a060ed38f15c4652b4cff8821f3042ce407c98edaa5f7e51d464ace071d1ac300f5c27324ce508a04bbbd06a5a2ab08b7e3971ba7b92b5b604e361a38a0d64d2f757a6694703e9bd7da750f807ffa26a2a8e466f9a48180dcf5fa4876e52b4d6e8d8e940908804b3732b7aa9a89aac8825e6ecfadb2652354151e290b0404e7515af470738e08d90136bdd58a05a108b5437c35bda033d511284c450410f4f4c4ccb7c710179507a34127569133dfb87357789bcc651935fd760206d42cd98ee86eefa42761d5749fa4888df0f20385fc02658c0247235ee99eb87055502589539cf2186cbacb7ab9c28c4ca91f45cf86a47aa783b71a75a75c5ba7b5484151241eee77277cbec0bd7e96de33be74009f51bf3ba60328c4ecdca486b307e812a821a4207c877bdc2833cfac604d924b6435f0613085a6595745abec450862f21b580f5855716ab08cdfa99c2079c7cde35e9ba44d3132f5468e14bd034fd643c0da10006eb5ae164df21732ef7015454b01489c686d33ad9a090130ebb84e7d853a1f7c5dbed209ed20a1ca7a9e9efe43f2342b203db0a59754675bf86f194b09181af8287ebd83b4ab99bb5b2d5bbaaaa80e410f65d4c433dfa2a6af447290e3340d7cf661f38816d384dd65fce66f482a2de8579b548b381a5b7b61dfd0e70eaa8385bae60b7853dcea92b205b521e4c53c3468984a3e54a51609c5c70280096a18ca880301dbe7b6832fdb951cfd63904f5aac79dce7303c73013d96a58f1a8dd736940ef8a71c9909f774490d3e1fdcec821de3ec9979373f6d78bf41fae6a890719a2cffeefd96847863277f2edf9ec363c0efd18f55dec83849fbaf48ba040d8cc67e28d85be8b5b2b23fa996cd8ee9001df832f40ec3f84d565cdd8b5071ee85e91f8a41eaf0b08903ed35ea944e607803fae0c995e30946c4403575a371cc2938890926d2b568cc3b6e0ead5483bd9e94e1dea710922e7081bb3b2e7d91d076b5a08ceb6fc798654fc77db9050ca9cf65f9c56a99a501d02431d700a76536889cc16d7c9428d1c5ec28755d3d7e8d4f3895aeeb3432264a87c313912fec9ef68ab8d477558a3d5460aa46129a0c3d97fad915897db7cb52c00e5cd4816155229825a85dee61e562399bec571c232556f647a1cb71e2cb0302d43ddcbc5850f5e950ac0ebdbf47d4706809826e4888254f03e2847b4663453e4dd0dbaf928fd0d7e49dc1a76a2cd289ab55432efadce37b7ca00ec4216a252a4eb351c315471474a1f6edcd9d8fd1c0f2bb9e12117187d841099cedb3cd9ff3be25fcf3e82745f52b3e530f487ce9e31cb4de4c5ec68ce83b0158c77f3821501f60ce807050fdaef4b7d0ef4edf427de3b442fe49bf413635e61b01e049a779023a38f10c2de5b9cfa8b8f30d73eac844ae139c4200616fd88970f24d1f2b47aa42a5d36f9cb772e5506e61e6171ec938d5823561e282c72a65ed78fb081616d0891c5ff9e269fc5e824d4be72ff142274c9873dd29c23584cb824a7953645cbcf38b3bfac19a271b1b1e63c6aeca627fab1800e32aac7061c78838abededec12083b2b5a775c8794a67a9af66f5a4a0974b946d5a14d1a3044c0b11e2661c56bb88df9498198d4aa82157e977d87dcc57f1617d965d4cb455c61fc5b5d04030a385469d8de857dba5f812e0d240e5d77bf997101df8fc4074a920f4344f9f79624c90fc2185a18d87d6a813bb4a9f86a70bc1a2902031f37a463d80eeb7bef661e27ba165bd243f0e4a07131a36a195621f5914cdf6f20c65954010504870ff73567b24170054729d2b470c47f158ebd45d8a9630d85c05ab059985dc853802ae6afbaec633f3cad834b54d6b5d059720af48ce0340e2b0cf8661d8edd3297f580c3ce20b8e382672e1088d030141b3b6260ba1ba4d629d4f123a6e150f43687882b10a2e8818ccd0026ce0abfc3457e71c72d3c4398a5a7291bb13f6676c091c6eb2a3fa0b827bc8d16650a415e788e9bafd03d4ad97ae149e4fbd731653cf494c415d16c15ec4d33d42e97b99ad8e30beb64412a42323a7f339aa45d3f3ccf4507121d1b4acc0680a21fbda6118746f07ae12dc5adfaeb3d127ed044b78c18975f22c654f33945616d707be8fcc69c11fd4d658b3e206b2ef9bd0f466def3c6d43f5ad377999f9f31197b4f12c5612165e6a6b1acb542bd2be01d2c71a5739e1097f039d61b8def26aae03a6a764df8a8279996cbf855e7bbee0e9aa45d12b14569a784fa4366d39349fb62b1e2a602feb15738d5514246e714bb75e6f55c3795cb45b0bca11decdf431b656e89b45a97571a7f55821f75e868a42d29bc42a3e7dc773d39051e55264a17ae242413c31e89af4bebe22655f5ca13f9e628fb407a16b8ea56194f1018f36870c49ca808e54454d69bd184708bf33114d56251380c254e07f86e16bb2c3b5754b41300f04957105d7dc68ffbe9073c3098ce21485852d073a7c5795b3daa7dcdfc4aaeab03520a7c61acc9ba081c7d00b71c90fd07fcfcc85a5dcbfb059b8b0df4693bc5773966a7ffe5882687bab6127940aa2a16af55399e363a8f3128d1613a11cf71d3b2f95e48a88f98d6f496a8303cf785d1f4d30d88651e82c8ce43315854746abb9e2924cda064827513161a28a051133261349689a2faca602eecbc4180c3150b25472c33f3f3f3f3f3f3f3fbfe221b0b1496e694e7fa5e7e6a64cca4caa61140000a300b4669b66fa7501a380550004870b110c370ce57fae9040b0229553dc8ec3f4bb0282f70aadce39595e56ffc0eda658517e261e513fb0b1eaf207216d07a1f681ddcc95b2931c8da67ce0aff3fbc78d35d1937b60b2f26a0e7aea81cbf4b20e53764f95cc03af39bc7a4952df76e281cb4b76eb91e70f39f20e4c8c9863e4cb717f1cb4031ffd6ac7f9939ee6b10eeca578313aba8875413a3016244dbcf28d94e01c18eb33358f7278e18172e0d2e74e7949731c24471cf838354a5fcad0981d1cf83fefc8f17b10bab637f0f9d1bb7906b33c3137301eaff3e7460a765a1bf86a4f5e41670353f12c932697a89635f01e723fd5a6af704b0ddcb77ef87135f2a695063687e68f837839cd2c34305dab51aaab19b43a03b79ff22dc40c7c64c924bf4a07a153063eb8747bf4edbb0c19b8a01dfd6f5b4e53c7c08e57fd8748470cbce7f657bf4e18f8d03ab8f9e5c8b1c5c1c0aa5b4893693ce2e75fe0729e9018dd036d8d1738d70fd9dfa99e6cd305de2248b41cf4fd480c17d8209e2db0692be57fef285f5a0b4c0ccf397db2c0ee8da84715d2e5c002ab9ea3f5d29c9d2357e0ed3aceccd50a5c8a93c3f1b85205ae6a722d5854e063aefc217720554f81adfb403549c8e144450afc25afb4dcb9b23651e0d28e7545d574ff4281d74b1d5ed55f80271c9252b3c6bc13d8e49719a52d9a8bdb042687623176d65ecb96097cceae9de38a96324f96c0e41c3b4839addf07122570492cf353c8549d93043eecb073ee8d047ebb3e238ec71edd23f01e2dc6a9eb7c018cc06bb54885fabc48e617a0086c0c92d972d0a1abcb2f0011b81c574f5ac60a3905bf004360a2e8b7e6d4bcbdf00b2004a6d737d5e4588cfcf80204818f69f2dae7f4f7f5f105000263db714894f8e1bec783f10b2ec4ec1b57a525851c0f862fb8288b99237bc1f724cd71ee783078c1457f7b6bfb24a13b1e8c5d30713d96ba20ee993a1e0c5df0b179578c6945d27f0f462eb810a3c7ee7e5f1e730f062eb84f75c9e1e51e8c5b7029784473cfff34efc1b045153db66ac1fa76c5cad92942be68c1aba46e89ce91264bfd2cd8b34bf65e1e2e0b5622c69cd268aba5ea63c1e86fc59dbc29f5430f0b3ea44715f31a8f3afd2b9898ef3907295bb546ef0a4e6205d30fcf46c7d30abec3b4cc61072bb85831540e234e04efac828d99bba35533bd56154cc7c80ded51fc187e2a1849e9fd71fc8f56e3a8603c7b58c1263fce9d53303abe95462a3fd0144cda6e4bfcb28fa34ac1558ef3e85d44dbc99082eba82b0ec62838a95092e949cd238b83210ad6d7a38dbe4a28b8de389d9e7a2c4715507039dac4cfac7c821d9de4baee5bf9b627b81829bee59eae90dc4eb099e9953935521297136cfc1c9a6a79bc39c76e82d55069a2eea561219ae02f4f8c93ef3fe42824134c6930d760124cb011436e9334c95207b904e7b795fdb2dc2a554bf01d5ec86d51af93d24ab0a95fd2754f4af0a5591d468f3ee70d4e82df941cc458a32498b86dbad375d99318096e3cc4689e19b4420809467bd3e758ec3a73c72358f776914cb772ed70041f6a478ceb1dd6564723f8aa9c3cfdc756793c6704379d537b83bba676be082e9deedd4aa7085623a649a407121e4b041f66c93f5521376f86083ed27d5ad4891d323b046b1a436a9884689b32041b720731fc26e4cc94178293ac5c1bc9f2e620e584602f743f088d97b7960f82af088d4c527983562e082ea4ce548b29f661c803c14ed75d4a355fcf1c40301eb44b7a5ea4d3ff0f4c743535d70f5cdf7a7b1e7f0fb2ef03ef9f2269fd368e7ce0c3eff71c5d9687760f4cf6782fa5765696e881df2429761ce57830f2c0e546e7b4a3b9961b03061e58dd1452c8a71335c61830eec076e4f0959668b31803861d3893a841347d47951803461d78b1d8c14b54ffd36dc0a00367d771472239568dda8031072674f9a6e572e05cd4f27be8f4381907be63997ab9e4d8d3c281d31ce8d54793b2f70d6ce43834689aa46811377059338fa65abefcdbc0f4a88bc4081edac20636e6c85e73ee7a6a750d7c89c4983b4d35709e5252462dd3c0e730259a77341d4744039372e758d3079e81b7158da9927e5d6d0676a7ec7c2a5ee4f72803df9145ce31a7205e1d64603b448bb1726e0c4c9ba647be2a4db6183833c3c06a442e8fa6966b3330701de87720591930bec0e6fbc721f2a31c64c880e10547cd83df7f64bac0248b1ec5f3471362b8c076e891f4942db09aba933c2db01acda4b39fc5089d2c3031ad520e2c8f05de543cee94e30aac69dc8db53b55d55b810b91a15386e47184be0a7c1c7d464766bd4b9d0a7cee1b6bfbd6cd69330526bd9ed596460a6cc80e92e3a84a251d05ae4e3f887b7601030aa6249221eab1058c27f0f9f33beb72e0516e0b184ea02e67dcc80b6902a795249ae689dcb184098cc794c3dfbff315c912984e9dc30d7d1f3b234a60f28edde4ad987591243099affe3b12f87e37b10ea29fbe47604a62ea9de6ca4baa11f8ec717f24ea71d6a745e04d42f0379190c253223099339e27cf1078cb4c7e17cac35c1102fbb1cc42c490930e4610f8f43a6e1df4e6980303087ce8c9cd6297bfe0926d724b66fa8249bf1b3287f88d057bc1654ca92cbb684e09f2820f1ec4d770098f225876c1a6a6c77429ea82ef14cfa7d443ace0e582bbcbadd54952868ec3056fbb1ec4ccb92aa7d82df810c37bd342936bca16fcdb654bfbad5fb16ac179748e2bc5aa6ca92ab4e03f7dcaf34823f54b6516bce78821dd279745a9c88289fbb59597738d1d0b6ecca2640e3e2cf858a235047daf16ff15ec74ea90c30f1943ee5dc1b64a59b6ed5bc149a4dca16394a68eb382936029e4a6b1feacaf82ff9861e21faef5c455c147a51d55c4d5cb104f056b221d4f8d4775a8a3820f3b8ea8ed71902a99537062922e193505931453b01f49faf3f0b3eed3a538f00f935a789a147c985d66de9a35df1e05532e796e137cc26b517015db73fff32ec77528f8b4cc4b3935a3a516148cb4a68916ca3eeac927b898246bda649da3219ee0b322a8e7ef78f8924eb0b7fe972d4ffe0809279aa8f136c975139cea04c914b27774ab26b8b7bdcc0e72844a9a09fe4359c8d77b1e3b89092ec71b730e32ed7b262fc19a97de665868ef484bb02ff9c5f3a48eb38f953063d050828f434a21f7794a8993e04e4287b4fd0b0d51125c5eefe439a41425c248f081a414735ed51cd60724b8a47a1699ea973e8fe053a48e39a576ed1447b0fb9a721cea7523b8906f633abd146bbb66049bdbe793e38b6043a6ccd39ca3b46a8ae043fe8ead56c2f4b444f0579136638e33e61b117c947c36297d5ebeeb10ac7d98b6c31c444bd93204dff6fd710c9dda3fa80bc17e8eb4347fd657474d08b6c3904a3b6b3d729c41b091420e9dd925be1d41b076f126ab470fd71208eea3cc659923e4407d40b01b27f2bce3ffc04964bb9565a4d8713fa82f91225ea50ffc6db0cd15fc553be7031377637b8787a5ccf7c0474992fc48228f6ff4c0574e871e6b3c3d79e06274f4beca140f163c701d0689e6793cce63b903bf7942d2c78fdebcedc0e54bed4fb2ebc08712a3a7a91c6af4d0814b15d2fd2773d8d3c7398e3a9ac8814f3ee2ea917d040989032fd213e38514a472040e4c5964e6939473eabc81cf410a71427eca6671033f69ec3dc2fea36d0393438d51192a1bd8501fc4944c3b32d435303932a428a9c93e48d5c087695b39d0320d6ceaa02255336f4e251ab8cc2f2148fdf9fe7806deb23ea6110f9376680626dff5954f49577594810be6d1f6eae5d77664e0272b5f2ecf1fe8c6c6c069a8ba9c2f764a0f13039b4cd73dd20d693d0b03af9582a6e40103e7996d3a75ee9b7e8189b4d7e821b162585e304fa36f4a9d2e30216aaf049f960b7c875c29e6abca4ed26e81b129fbec23ad16784bb5f7141e45325960b249c8cacf16291e2cf0ae7ebe2931b37be70a6cc839d6b6ee7d0f6305c6eb3e9ba4ad55481558d1a9e920f754e0a3de2dcff3a3cb7e0aecf5a878865e0a7c47e7385e8ff6b18e022769937adc39a46a0305ee2e93a7ec255d59f3042ee79a69ef6a5fde09ace7c03ddc9bc0a4dac951d948ba0d13b8bbf7a9fc2884004be0e284903ade60e195253408e666ae09b6a30f2169ea4e345d32c155e4ddfdf8ca36f203137cd0789351f7368f2f7817fcba805ff02ef80ee312dc5abc4c16b3277ef05882abf6509ad3a132b4ce30e3282b6154e2c8d6cd398cc8a10467316e79dc297e962c93e034f7f4e4eb13497725c1a70eea7133c736cd31124c0ec4d433d8a4298b850109c6c34a96234453f11432c27804e3e165b64a88a4972304613882fbfc49bb11f38d60f2bd784e31d4479d1546f03f5929f249d48e83e8221857c9b1675fbfa70986a1082e3cfeb4e992a40cad31c618638c336630c6187d460d7a13816120e2c608e310e77febe83e118461083e5649aecec93ae4e938d405611482536df5d78e4a4f631d062138fb89691d769626db83e0cd927be65a7da4d58521082e7a70ef7c1ea6e6646104828f5247ff1ecf1353bec200045be92c447ae8c0438cfc81cf51fac7f58c1fd82cad1e558a173fd6541f3811edec38ba90f73b990fc2e003a71d058f651d6dcee9ec818fccb2ae50413db09d7cd22509ab4a9592073ed3c31c1e638807cee33079cc97a33b70953f8e33c7fa495dd31d84610736b3e3f82a0ec2a803971f216cef63746042e6499bccbc4c6a3307aecd6c54525447969d1cd811afcefc1e19076efab4f7e376f4f79c8203dbb1ae6fece8f42e585408e30d6ccccccc164277031fdd81640fcf17cfba0d7ce665db89e961c690610317f953d9e4c8629ee71a183f5d916afbb0534a35b097ed753b35a1391cd3c04e0a51cc3386a0be3a1ad8f3bd1c57ce9a9ce37b06ee2aecf762beccc07af630ac36a40e831554e0781046191857adb6902cde8f7964e07f327898f36e7706cf18b8a0d1636df51031b09b42aa9d6a93e813828430c2c086e68e3b19543070d1a3b8512958482be117b8cb110f5a25ffae2747185e603da5ff68bf1f0ba30b6caabf57919c33a70675100617f8d052693246e6eb9d2c0a616c81f3df4d5df993730cde0b1604616881a9c8513a422dcd52c8b18730b2c0fe86eb479b76cece868105c6e279a6981ef7dfd4615c81bfed1493a758953a088661056df2c30f31c6dfcef93f184615d84df13a47317efe45461b210c2ab0993fb3e3601e87eee31458cfd11d713f0c2970965a57ebf3630e3d5160423249ed972757db79868230a0c0daa66433f3ec61bc5020c278025395f726a494fb93629cc087541f2569fe46b54c13b824d593367d6dca95c2045e2a6378eb4bc7b1a42c818fbcf227b13af720ba4ae0623c9d8e3e9a654c3709ecd7eac6106d15c9572430ea9ab37354d123b07e2be1e1213c9e381a81dfa0632958945c1f824560b326692eff4cb6f98308ac85a764e61173a5ff86c07e643eb51b9a10989038be51b24f0c761058f328bbc4b1f4e84c0a03084c468fa3b6e3c81052e717dcf7e42eafdcc841f70593317be0a187781cd2ef05e329b9e75ef5974c0d2fd8efb0a2b3e577174c3ecdbe395c4d17dcee8defb7e77cda6172c1e41432c7cb1fa47e1c8a0b263553ee3cadd398730b2e74107434055d31136dc1664989d439a3ae7bae167c744b31435c69c17e9c3cbc2bfbb03cd066c1871c442778d0d19005ef932f4d07ae1ea47962c1e55bcacb71944e3233b0e05fa3a3caddb13a1e5fc1ded9ea4a9e1c0721e70a56522488f5c7984deb56b029b13bdd8ae59074c20a3e1eadbc922d27ea9dab6042ac98f552fe79eaa8829b68d937b58906b14e033452c1ffe694555443c727390d54f07184ea20670f314bfe2998142cc42d4d932938f730abc518371aa5e0537b5817828648c19705c9ba9b937f1e63038d5130153c6d697845148c59d6cada2e6e559150b0ebe671e441e6a4ec0b14dc779c727ce92289e5141a9f60f2b5a4183afc3873f8d1f004d7718cf4e59fca256b343ac17767eb6c3675d617a4c109aee3f4e44d6a96e44fe3d83a7baa7b99ad092e4acecd58a1427256052b60c19f519a092eca73103d3e9acf474c188d4b701f885844c4f08c2195020d4b7013514fed63d5a5eac719685482b1891c9d95a34dad86126c797a4b884e92630ba13109aea4af3d327aec972e92e0d3fa8f44e89e48f18f0417e1d1879843d4ce9d8704932ce66cb5d9375f873e82b1f21c55df4fb0481e0d47f0715dfced5472b22b1a8d60bdf3ba8398e34fcf7198800623d8cf899dd2ae9e79762c82cb9e3429519e151da7033414c1a40e37c690f06824828f6255f43416ccf4541a8860b3c58e7332e40d0cda9061866f0c681c828b9e735c62ae1d06af82262306358041bf0b0c0d43b0ab7d1d121d2bc4c8518656cd804621d8a9ccd1b3be55ec143a83062178b5b434ed7639f4f87e41c3800c335c734063107c9462f964766b91e84943d01004ebae617d79c7ddb70f041b4983f407f350ed3733d000049793aad245a598daf13ff0913b5d5abcf36bb4fdc07a0afe5daa91a309da621ff830bba7f638c7e10217aca0f0813dcfdabf6929dad38334f6c0eec7fd9ba2376ea0a107beb355eee7dfaa1c3134f2c08ada7798b4223cf0591382db5404a974dea0394077e022c4e390b32abee76b0c34ecc045e57b101937fa77a80c1aa800066f810e6c5d1db83e3fabbcdc568faa0d34e8300736c4a8ae4e297b1c960317c420068de4e4c0de7f744ff97d299a180726ddfe77353830eae1c6ca1e0493e8c03770f13248ed7adcc0d64a57941cc4b440a30d4cb6d3dc72f7d02fe6d9c0455bd14a151e84c61a188f83e5c778e91768a881b33dabd34e9963e83864c081461a98bccfdf4e3f8a80061ab838a9be12c173540759008d33b0136d7a3d0a4fbb92230334ccc08558914b542a8d028d32b0772e392978060a34c8c04f4766f57d68f3c839838c179871c6b9d280524d80c618386bad983e6ef6aa9a2aa021063642fe389aa55886d681031a612030b09e6a2fed85495a2b70410c62d06636a0f1853d6d56f7756c12b532b4687881b39446eb3fcacbd09ac1cfe0cfe802dbd271f41df484f230598696b9800617b8d5f7a8b3da3c684b51b180c616782f9b48bfb6d14a73b4c0ada7a8358f60f6d19e05563b74f83183876588c1021fa6daad85042feff70a9ead87b8d6162b70291e6de7ec359152abc0a68f4c969eb6ac46a5021f88646fe53aa7c0e6c80e6bc76387ff91021fbb6290f44481fb207d57dccb39a63c42817fc9ed8cc83f818b122ca25ec766d5111cbcbf0d1e6b40c3095cd0adcebc3839c0ff3000c0814613b88da57bd13f5267db683081ad98313d5aad2df74a06591a682ca1c6a0a104451a49b031905063d03802df92da399e8e989e23970c348cc0fdc741b2fd483554fb45e042a55bc8e81e3c874e0d3488a077142b87efa18ded6568bd195e3ca03104362bb68ed4c5888eb72ed01002651a21e251ab749c528e4986078b0c083482c0e520d2bfd5bf4d4aee8c32d00002e775c1a53d4504c9f6a842017ec1e5b0eb3cbde3290b9a02f882c9b154b4728f3e767c51a100bd60fb53c7183f4ae6196d180ac00b5e2f423f8e17f2acfb5d7021a4549a42c6b451d3ba603b4796a85ac9b1643517fc67c5241a24db34555028002eb8d839693dff6a76eac8a8d243016ec1068b999133dbf27a98591f0a600b4673707b9758d92a786ac1a4e8f4ced7a8f5711c5af0d99bf971aa743ad59905a71df425dd74ca82c921e64d1f46340daac782ffcf99628ebf2d5ea5c0828d995a3fe593e892d95770e9f2e68fa47a49a17505fbf6693d993ff4de482bb8ececdc1eba3fe45659c1df8634318dff7f0773155c96f65054a55405db31a3c7619ba32783a9e03c7eec74713bec2b880af63f8ef43e07d62938cfa99ac723680a3e7d55f75f785c31739482ff8e3ea5f59c7314f59182af1d3d4bafa951b0e691ee4a55e8258f28f874e9bd59735cf1180a05ffd13925677440c164e57af455b57969fd04571ea5a91fa2ec33ef09ae34879690dad2f2b0136cea28513df23838c144dfe4c7ec6f829364e913553445f2a3430134c14f768e9ef3bd4c5a2a4026989032c43d4ff78f2588092697998478779b110a70093e585bce91e86e097652d2db8ea3f0cd71490d05a80423a2b1c31c63bfea9445280025584b13aae34c933c5e8d150a30093e42e9664969fe5c8942010cc13ecc890497b45379dc595f3d1e124c674d67a51021e28fe02fb27ade8c69ed74049f2ca2cf737823b88e3fb68e9133820fa1935ac6dc1d42b3083eb4accee3ab15c2a2087e2a9366b590434e9544f0571d64ce6b23828f7235df985e8e601f820fd2ea61aab491c23604d3d6b1c73aba109cdba6ad1bd31ca72c21d8247dfd418a5b21b58360472a3d1669ad4e218260a2da727003c5689383d0492c9a0aa62ba76a376b6be81c15ac07219d295687a825418318a7e02b7608db1c32e4f8465330b9a742c73947bae3af14fc4847698f34523039b1236995ca76c1830c3146c14d7f1c96a1f57662c410051f74d3d4bd66cfd13e628482ad0fb33ecae91e3aa618a060b3566ec95a955a1196a19583bb81189f20627882ff20af43b5d2328f2c0d6274828ffd2be5aa0bed29fd32b46ec009763a3b7aff6212c9d318c4d80497dda7c47c2b867d258626d6b839922b6568b5c00c33ca7081afc08c10626482b5b70bb93d2964c8970c6260828dbdef2187a81fc6fbb20c625c824dc145434c663a6a1de7803268b08210ccc0052e6880011c6000187cc0000e3080030cd0010378c0020638a8d0127c5cbfcfb1d92706312ae19a25318b1e25257809be931df5425664124cada5ddb8112354f0be208624d860793c861cb8013122c185464f8cda9dbbc3be0a6240821109bd94c371cb28154205311ec1bfc4fcb03b4f56248f32b4c830c34f066238825b75f3d8d1be1d6688d108264a68a7c7d70cea3146f0116f63a5a07dfb6e21408c45f01d2c337dd02882554d957224ea413ef513c1774c17a2a79083cc410c44b0fb92d29a24ef3c558a71082e7fb277734f1b828b122bdae47883871029047bf6a3b97d1783106c5f4cd59fef171d688360f3e78e38a6d2271ee41a21862098a07a57e521f9fa3f6a108340f0be217ba5a73c4e758a01084e7ccc3b76fcfc810bfdfee167e37174397ee02e5fa67c5f925197d307bee3cf9159a60d2992870f8c8f78a6ffd0436878f6c0be7b8e39c89efef08e1e788f27a60f569d3cf0e96fde51aced20d5c10327a172dce1568c0e357f07c682464d9e35e7d8296f0736c71d86280921cc255f07aee387c9337a74bcb1a703571fb77e8c9aa36d8bcf814d1e72fd36678fb6b81cf83072ea0fc48c037ba67eb14a7298417438b09d83bed08f722269dec0e714430eed4082dbc50d4cca90c38cdf7ea9b781c9a1bb77a463fb940dec7a7d1047d3a389ba06267eca148d21a306be2a79d2d0d7754bc7c14603b769222a5db6474acfc0856458c75f3f0f4933f01e39470e9dab43fd2865e02bb5dd7dd8498366850c6c5772115db7c7c07ff4f4fe1e5e06535b0c4cea1b5f5fafc3c04ff0389adbd633ea05033751f5e97168b19df60bdce4108f29678eb12ae2052e455bcfa039860555bbc07887672dab758da1728193f08858f6133a5eba05de3c8cd1263ed5b9a3053eb61ca95ad4b3c094a7901d4fe88ad4b1c0aea8e4fe770eaa972bf0a97d5744b25660b3a4ecd19fa1f9c22af05932669df42515b85cf7f0b23805de2aaa75a899573993023f297edc1c1de2272ba3c047f96eb126595bb85060ff35d4777b76f7109fc0bf5688f2b2943c4e6024598e976d3ad00fd30436fa834756840cf983099c46ab31d5f158021fc7fe11ed26e7c7a14309dc76c42429720e72a827818921f884ec3d4f5e3924709a9234d6f9448c23f0217a98b204ed183931025fc9afbfa2a257885d113875a99c69d783c8be541552684d2162620c818b789b472bc6d110991842e0230f939e545d86ca1123084cc6918cba95796f2f3180c07ab766cc39bbdc27f50baeaaa31c6a86d96947f9820f2fbde03b487ad371a48de1f182fd74e74187dbafe62ed80f8f394d688cdd51a50b3e9cf4716ef7a8be7372c1e98b5eaeb8ffe53c2e78491e5a9e6a8ecbbd051bc43f9aa8d8828bde29e12984b2e041b5602f96a55b2d55d70ea9410bee3e773c2944f5a90f990597d3c64b39fdc3581ec3430d59b09f35a8ea763c39b78e05abe98107591b3ae20e0b7e2a3f429cd47911cc3ad47805d7ae9d394ac518d4c0ff0131a881bfbbe2742b6d6f8d928839f4ec9362eae0a697a1958333685035821aad48a37a88d6600593695983c59c2dda51656839da2aac862af83edfa4fd41c472fe656891118317987b810372e00218644003638c6166b082179ca5825157f70f6d7b4163880a36f859cc8abae9196a9c82f37c39d4ec51e6d85d620a3607193c65d5a895828d31a7b465d0dcc9bf04dc052b28c301a46062dacf514cb5ce0ecc5170e3a3a982dbea87270a3e59d68e12dcb263e7970b3542c15487b97368d4982a4441c1ff7f9a5a8fea137c559764b6e49ee0dff27d9047caa5634c27f898636b6d7b6d0ba182851a9c60b395a5dad6ca21a1c626f81442a7471ac3634b61086a6882dff548d27d9461a0462638c9f99aa38cf1c404976bd2a8b5a5f1127caed6a4faa86550c3127cbc1ee548fdd145d55f89f7438a21a5ff1812831a94e0d3644df0df0c2f5c418d49b02949f630a7cb51ae7d4b163524c178efd97bba3819736424d8cb815e0e2a25724702095672f44996c852a98e47b0bf295b4c15b523d8d2d431f1d1da18f44670d1f662a9790f83336a50d6001e88c00c0a153518c184d46ae9cddcb3d4bb082659c8b5f31c49ac5043117c8cb64134a737e9e0151a6a24828fcac36ec96d3e79ac29d440041f454c925ef27b085ea73447a79343685b13c0aa61082e9d6e8e466f2c04938267c67c1d722031ad41082e5db41c6e111a438d41b0fa614b88942977483504c1fe96869c9ee37695816034ea4e2d3a0e104cf5470e93878c16439c50e30f4c554e4ddb37c150c30fbcf4fb6bc8719a8c7a01f681fb20e45ed75787317b0c51830f95f8deaae4b0df03bf315bde762531c98c1ef8c991722c670eba6a1d0da2461e388f3d474fc910cad298554fa881073e0ed2c6dadc21c79cda1d989e94cfbd46e200ab3cd4b003ef3e29d953c69c1cf2191c89a85107b64afb24480c7d31230338a00051830eeca5aa6ae5d8b3253cd46a5cd925778e2987ee1863f40bbe0c330332d4900393238f42dea4afada5a9a1e4eac0374713ff2dd48003af59d4473fea2ce7d019aca1c61b388fe3ce1a1d85c8ebde0dac07ef30e7e946777129f0851a6de0c5a3a3fce51d9115abc1066ea7b433e62056630dbc2717dfee8cb5fe9135d4c09945ce10995fa11e3dc618e7881a69606332c97d9dbb3b9a748c31c618e328326a065b35d0c0c48f8e25f4da3ee30cac7e88bd7ac9a3d8f42ea8819bf18231c6d025d43003fb297bd49c420ef516af51063efd56f6447944851a6460f53ad845f4764eed7e0d5cd02018a3c618f8201d22a5261fa350430c4cc89759b5a35229240f50230cec6fcceff7bf8a937140a10618d88fbcf3320b314be935bec0d8c78aa839c7a8fffda08617f8a4ba1a2775f80e6a7481ef94bec56350cf10cf375ce09275e451c2939fc40c0e6a6c81ff30c5af8a7b61a9d125420d2d30a9742ff525b73c5fcbd0aa828022c10009c81000106a64810fd6517fbab43a16f71635b0c069c51fb1ca183c8ee3388ebbbbbbbb3b33333333b3aaaaaaaa5aa2892400b0a871053693478a0e3dd4da56e0835d1c77b3748fd553052edd72ac29f888754f053e10b1b7d09f3fc79629b0413bc4ebbe5e0a4cac8e8e5cd54481cb953d85ec4e9b3e2f50604a43f5a3142442db2730957153964ccd524d9dc0e7680ab973879dcc2a4d603ff2cf1ef91292e3304ce0f772b664b0f53cd15c02fb410e34a473749ce6daa08612d80c0b9a4c3d87d6a1930446aa4b7cd47390ff03096ce4641b35b142faf647e043d5e7f4c8367ab611388ddc9da364397e48bc08ece59426c7e179f76b86086c66d7e8b1db328457725efe4f8e2204c63cca8a5bbea1e3201b04f6a3209dfa345896b2061038cdfce25d23f9055b51dd3ca3bd87fc515f3099565deabce3cd1ded053f316dec38eaa41a5779c1459b9c681ae92ef8f841477e92bdd57e5d301a443a83483ebb3d73c1c49892c41c232d5706174c4a5172f4f96b4269780bfee31c6a9ab672253d6cc1e6e8bb651fdb65ef6bc1f5c4f2d3b6dd4fd3d182fbccb4c1528e27d5cc825df730e77f60ef139a2cd8ec398adef45b8e33b1e093e4dc269d6a82b9050baecef275f54561bc820ba9fdb3a7ef7b7a2c06c2700553fb21059b4c2149dae40261b482f51cd56ac4bbcfa1a39001832f830664182bf84939f44db6d8a66fd0679041031c3c0cae3a10c62ab894776e7ef5515eccc25005d3de5a3d7152736a3d155c670d71f70fdedf51ab1b3407d20807c24005b7c1ab2e5de83805f7296934297d0f1a5953f0b1b4f67f4e398a1c16462998be209e831c99240fd3803048c159cc1d75889eb653f3a360f42ca65051c283a89919d42006872858e98a49772c4de5a0854179208c50f0ed1d87f03833846e7f1d60823040c127f71cd328a119e35d86160d4cc7e005661832fc302a0ee313bca649b93ffe4aec48f404173fac94ecfd3d1525124627d84f77a99fa4aa200c4ed4182c30616c825f4b56d9dfeb6718335ef09e72b8c18b45b37b4ca91e75666ee2c62e8e4f32073156d6cefc92b5e1862eeab0a24af2b1b27351e4783c2a3569087181b6b6260d95b6f75b5ce3e1b67aba60793bdcb085ea9d4376dd89d6e767246ed422d3bcc16aaf5f35c36b300334dca0c51be287d319791b281f6eccc2c99ea6d2a493de4990b8210b7cd5e278a7e9e46bc5c2f6c8733cf94232f3c838dc8045313a781419ff328ba6cf38f20367b8f18a7a73ced25157106119ac2e6cc5931f957b3a567c63fa973a6a15da7b7f440a375461db86a416f9f5fb3da920e28e87f60415254bf9a3df5e8ae4f40a374e51ceec0f7f5ae27ecc8e31c6186398256e9802e98d9924dcef738a136e94c27c716fe9d41d4a0cd51d6e90828d19c73764c8669083afc02838c9943c268b17bcd21405fb29337290d483acfe915030398a5da219a347b0cf0cafc10d50303956bb140fef4ff069d3c42a2fb7fc3c3dc1597d106e67a7593c9d603b6d22fba8e4c714c209d6773b0e422bd524f49be0ed5366de0a538b91d504fb615acc21e44c26aa1833c20427d2ef6e1aab83681d976037e4d856166ff30b5a82dfbb8f9434588a94d31ab08001d870a3127c58a45c3907cdb93d8a093728c1a794a2b98ba9c7366d4e827155fb9e8e6107c6180325517ad8a94663928e7d32dc88049fb3dc37a56a0efd6321c1e746eff8c3c80c6dfe2398aa31dd5ca75529621b841b8ee0b635a3f63e5b307569d05cb8d1083683f8bb5a7d18d1bf69f5648be05c47d36d525c116c7e2076ed714248d6248289273d96b33388e0fd42a5c3b00f5c73f42138edb5b212b7d74fad217857b5cbce192d049383dcae41244a882a477977eaf2442f438fdc1804eb418d24cf7f51d59d2048d6b19a8444b5ab58d20d692c43eb2837dc08448d71031035c61ff8dfd841feb758aa1c3ff0952beb731c7b644b53126ef4810fd242cccd8af94e573e30f651ac586ee11e58ddd8711c224ac8517858116ee881f7df94c662c8f6f8571ef88d94e3d6c4ec20528a410dcc3843057860525ab7ec2987973ce4b8718701dcb003f9c3acdc9976d68195501539fe9e141ea4fb841b74e0747cb5acb3e87dacdd98039f95cc722c851c776b3930d59ae38a9d961f72240e7c94f9248d4f76030e4c0a4f29879403ab88a80b4a59010cc85824dc780317a246a849bf471ee4c7106eb881f57d49f5af49b9d1062696464c13f59e254d5a0cb8c106462779fea7c9b8db71d6c07adc143be588d6b4bb1a38b3f5e8eaf431737269831b69602ab895e6c9eecd81050ddc98ad7d2c1dc719b8cbbec8962ebd36aa66e0c4b365e8f69dec77fd0acef0c041831b65e0d28598a92ce7981ffd64e03cc799ad2ee580c18d31703976d8c69cd2a8fb1103935dc162e79413067e246f9e1033c64fd5fd0d30303908b97e53fb05dea5473d457f78610f37b447195a3930e30c1acce05ddd0c0c907581cdfb414bfa40731cf4c3c10ace72818f83103b5f908a999adf021f7828527d1f254d28cbd04207dcd082295ebab694e794ed230b8c07967bd37b182cb0c1929a77a8bbfb1ad32e88c19fa1a5811b57783bce761fb46b5cadc9d814dcb0029b266e0cf59f61c60a3270a30a5cbefd70f3b77f90a2a5029b21756a9fcc17ec2c221820011966dc9802ab292f34e25e49ca4fbf0bb60737a4c0ebd947678a68195a15b811053ec68ea635872155c7571937a0c044ba946ddca32367c913181bcd6127959863bb5051e08613b80b394e9b45cfecc16e0253fe1f19e57719f25486d60c5650862127b8c104b653547d373c9f8f65094cf80735294755570f2a434b09dce7b846f7d2eb793609ac9e75cad1615aade00612f82ad1606f951f810b7996ffb992b6236204f643f3a45c3fd260052f78811926cb8d2230aa919a9dc364172b15811b44e026a7fef0d18d509b6308ec7d102af3841821307a1221e64912b21b41e03afb658f2d24867b08831b988113b801043efced4a69ba831c45f70bd62a4a2be565f0cecb179caefda6b0f37a61487972a0316d9e17dcc51c7bda2553e91cdbc51d7b5e8817fa751bbae0b33cfeccf0d0e378aa33cc20e3b0910b3ef3d795c7fda8c9df0bcc38e31c2ed8d1fcb18fe52886506917c4800c09b8190eb0710bdea23fce93a1463d646568d5016cd8825dcb51ec3fcb51a6b4d4c26cd0c26ccc82ffb8d2c4dce194a587872cf854152f77a56dd6dc1d32f807980741d9d74006ef6498b7c1c682cbef8f4eb73cb060530cd19384b41942da365ec147684badd21d071a541baea8311260a3157ce598bb777b2bdfa6b0820939b46d7a8bf9e2a4ac8289fae22143323c2c6d872a38b37c316fce59b7a2853652c175ce1ade416f2457720ab0810a562f072a2189e7d1ec29432b07858a9cf13458c161e3148c4bb4ea9bb496a1a6481ba5384871902c1e7d2cd18e60195ab9021ba3d8638aad915fc4ea19c4600687b1210a36a43443f0d0f1b3bf8ab0110a3663544e31cddb600314dc47371d2b48fa94834954363ec165a998412f5de694e3cb202a4fb09639115183c7a06327788b35b91dbb7ed2f6e004ab9ab94c37ef4966ba09fe2f7656cd8eeba7febb1930a0b200a955810d4d70b7c942c75188508f533632c1e7e8b97a1ee2a2e9c104eb9fa727526f5d4cfd12ace5b01834e58c2558dbe89a737ba7c72f2bc17f14de217a7a94e0d4532f1b93e02be3471f6fe5f85fb32109b64f72ed4d6f871d3791e0934bd48e5d87f67d9060cbaff2e75c39f40f738fe024ad9be57825ef7354575ddd046c38826fcb955ae9be11bcc594c63e88d1f265c908beef4cbaffffe3386f3616c1b8d4880689a12218f7ca3ff9ba1e668f9308ced37f2d3bb6caca1122820ff2c5ff360f2297d85eb071083ef75a75b21cc6af960cc14678641582cd1d67ab6c21435dd408c1c6a49edef923d7241d04bfe57e96114c7d43c7046c088297e09ec5228f54b7051b81e0e399a6310f9dfe342964850d40701352baec515cf1cea9fb848d3ff0be1d897d65e6947f990d3f701ee4f66c53ffd598d71fd8e803fbb1c6de9d1c57af2b6528190e6cf081cf48cb9e38c9b423e30c33ce1ed8fb38aef41c45cbd02abba30636f4c07d6ba8fcd549cbe390330c90800c12d8c8039bfb036bc949d71c6ce081cd9ed37a621cf594931c6cdc81fbf178db7335e4183b76e0238e7ff6689a759bd6814dd11d3c536d2cdfde60830e6c576e4cc94b2b36e6c0e7f8b1a714592abd9a11d89003ef71d462889dcca3c56dc481f1e8be52f628f9593b0907ce4a2377e0915b8a39d40ad87803abed1f73b68d3951b20bb0e106c6f3caa2d62a5e78dc461b58cba943ac4df7d07e7a850d36309d453dc7bdc91ad8ab4f21397e6e430d67230d4cbd5f90c89a6dffe331f02a68189053d840031fbe6bc58f5e3fa6ae6d9c81ed4e11cf315f92d09ad9304319b88ed25b8a1f6a3a17572b61830c4c9e942784a4e4b7a68f81b5ec9aa27a14547a5731301da6ddc93adf8f62d046181895d61825b7254d60030cec6dc81cd946c721f71db0f105364323d57fa867397358b0e1053e2552c5ac1ca4e4215b645436c24617b8fc418e92a23f48da6133308003fe058d010378c00206b822a30661830b8c667bd039e5d44f1d320bd8d80213afcf3de84b41b2c5e4c0052e680f64176c688195e02a127352faf4f50461230bdc84c73936314fd37a06890546a2aed4c7b1a47a71f30a6c0c29e952949856e0e368e211db73bef9252561a30abc64444bfd16950d2ab01b2ca668c7e153e03a54071d1df95385500a5cfe7ffba0ec62c8fb4781d78d1339d01b0a5cce6059a2ae471ef64fe0434a39fecf1c71021f574e565db69c3f6d02d3296610a9f7b78e3713589b6ce695337a75331cb0b104cef3f7abf84797f797bd820d25f0f1c5ca1e5b2784b48656b09184b3810426daa3078f3e0881828d237099f7e26bcee96ab5bd32d8300297d42f6ba5b52b43bc085cfe581d8a74920c3688c0487ece9543ffcb443304ee52feedac087d39bb42e0738ce8e9eba3fe350a838d20f093a137879639a3ef12d80002e755e11d6d5b84e78b18bf60347f78b41ba94af3440c5f70dd1ef8498e693cfabb0cad858118bd60e3486ebecf8fa246f58c8f8119ea023178c166f8d5a729570f520e8741fb408c5db02993c4892998c7dddbfa20862e58f38c3a71824dea58920b3e8bbb69be86ea8d1a3170c175f00eeb3f486ec18975464828ff778f2db8f48d3e0f3a993bcad68249ab1f7b72ca19a4775a1435babb45640962cc82f3fcec1c5db39de6a02c2ed78992d45187c4826d2dcfdcb9217f3d7e068a018b927466ed1ec95fc175876a4892aa324275059382f6e486a8a9b342a606315ac179ec6bc1bce3d00c62b082d59432a5eff8eb3bf6090c62ac82d73a7fc939ba6839b4570c55f0297d048d1fa824af85f2582c0e0644c26030140684958e0f93140800003c248ec422c1288e436d7e14800350241c4a3628181c2014181c1287c5a15030100606c340503010088381a1709018800783b0372a7e5b7e368392c0aaa8e3660d8e539adbbb9f10a02802acdefa6f8578299b62eb01781bd474998152d4df3f450a4ffb4ff6be034b3b4d37ab979915c3ad2c3145443446d0ccdcd49523bf5810a23ee867872aa4172aa1d79110f977345a18eced9937cfe33bac996b7e0d89fe053cbde0f83470d5e2784478337415daa126a706a138e5b2e393ae3d23549aaa21b352b57e61a74b2b125265ce208559188bc6535ec1f71db11340133dadd39949c2578cec84470de49ba58e2ed0e2e10821128717418243bc55d07f00074097ded3d1cad20b56ad6525a130db7444193f86d78c8d4ec90044eb70ad405be3722b492d230740de7b1a8cf07dc575dad355f323a9a1ccad6f104b80c544211653fee537b8be507483a59d3393ae4fc876f3502fcc6164f6ef5a553454d73c46699bc354f50e6edab0b846887b1d78c51f9e51b4d1b575732d0d64430f90ca7032c672a05d0d96910f4da2e0ef8515ad1f9ad0c43964128771639ae1eedf701311a9b8968f9298f56ed6b8a7f1f1ff0f3ba28bcc9e640da1037d981f155c121bfb9366b52b6a2ec2e5f44816b8b38f1413079f2e8b0d7c582e2f67fa3967d71f074af8a1e24450f81e30371f567209a325c572fba46bddd50d433ff9f4b900a926775c2f0d4944ec255eb02c9ad58dfb6641cb020ddb3a6ddbf2cfc8f39655433f7da788530fa5cdbce8b5423878ded950872d03504c9b4f98340f411c9b856f707a09a1bf0ed076701d72e69273c1d8e0506fe8e0c08aa90b65d699e5e5bc6734424c5fba3f91ebd011586cedb7f093cd80982e8b2192068d69c2e1aa013775923a9cf4f08a5d2846a0989fe313e22cb1c2c11c4fd44702df72baef45c3854f48141f43eaec980ac950d68e121583ad189e0fb418d98c054acbfc3032f7250e69e46cabc221db11ccce78ff734234758cc15f68bcb3e612c3a079d3a80855737bd4021daa2a6c21a16d930c06c2dc1847f8df40fb0497bd1c4976d6a343d13aaa160d2ba91cc4d8c1d6060c3d6055cfe3580217d825dcb43164524f5c2fe43816aead19d7058b4112607eeba3377c4b176232c77983742a8d1a2dfc321a8988433c735ad251786fba3c3f25ef53eaad403ee63d677b6ad6e1178e195c5678952d83cbc9624db8ee8835ac2affdd55e79cd5ac45a606468658c4953dc552a34b19f189811efb50463c12378ac189f56867e28690d049f8716d57000cdeea9301cea4c1a798df309f8029d1a3a424564fd719e629876c36fbfd82813296f6e5bf42a696af3c91968b8f826971a0c11d78e329168ceced42863e5a7154c6539e0c51001edb1da1021e4a108d2adea6239cba3eeb4db3f00b1af8ee3c15550d1a3f6787bce5c08a98828842a4e82104adb3dca6f90833263741fb83ceb181bd0664161c17dcd337e25a080f9d65f1c88c0ceeec873f2e72e1ca0fe4eb48011917e01181f5edff8a905b2674a81da48319c7adfdb8325ffcf3041ac6c7f4bcb08285f08e09e3433032ae2f6c5e4bc1a9bfcbd4480d781a1cd19acb538b5ba1f917f141d177464b51248f4ecd6b597916f845804de3bcd4b072068d40eed956485326cd71a5e7ca9afe8bae819d7519be9f75120c1a4425104f0bd505e03a344f3bd885c3a2a2511aaba45a034cbee73c48c7daa06654213f2fbc0abb9cafb546fa22a8cc07acdd651413a30d5086489a9b44795266aa781fef4c0b6bddb64c8bccdd74c4e297b22e3b1b2ecb0971e83b05551e330a464f004ad30e9613006139dcd8d146fbe42f9ac9ab38b29df8074a0be226dd656b825af0ed78bf56b6f2383c459da562a8f322b5a50c32dc37ed0454e9cad014b2a769adda1cfd8049751e37150c73a3213882b0563c21150f72ef74089e4f097921ae6ce35e6ebe40cc27fee5682960c713d5ec5cfc10161ce88acbbab179e7c6c1326250d4bbe733f605d0b808f70573bdf4c1259e58a041192e32345b8232cbab1ac2d0d28a6a6e983b8f981a7c82b326ef748836c169da6e2a62932c979f49c3d244f8c1c691f7e596e14a6fcecc252576445d10203a734377f894ffce91d97b4972ed908f91b5aa47f516f2e772ecaf2e1ca1b933cb502f148a6c93d39150de86a9b27b49a10ebcc1137868dbf8a54ced7529654623d648a119ecb8a92a0cca046629adba254d96d2852f45ce4404db1b4dd2d11a0d44f9d8c7ac8fb42682130fd2353d7831b8cba36821a054830505f2447293d0f711c670f8fe22304248c1fc78c2c5fb7049143039b680390e83280b529f4afb912e8253483d05065c741935fcb41da54bd97fa83450220fda011b8bdae0836f05af750af66d11f270ad522d2b5407a5841df893364c70d20ad4f78a6aa73a7a722a2af02502837b41695918ee8e083b9cbfa5c6763bd048303c2eccee294ba2f9b4e5d323e91a0a7b3e8d6457538f9e1a18a6f8830c7c6d3a718272e1bb6987f3a005cd027008d53c8d98effbfcaf1bbfa76d8a3761803e34174cb56807d91c2539a012ad4649902b63dab4b46d68f4deedcc0642c1ceb9dc3af7918cb78c03527ba4972506904d514baecdcdef5aa8dbc4156dead9ce8f1f20cc3bc94c4c838b6e94d63484c3dd8c8f465423f930561d8d4023df23aa7199859c7b1d38e8b494cb2612204d7aae726ed9d924da802d0d36574808604fd62befdb4db2469d5853b57e2b4311d200126afa1a6d9b740fd950346edad9c908c20cfa3dc8047f50ea9528c501d6445e781a000de3da14b9001c7e1437ba962bfae64078281d24429175374974b984682d981d2b6a166cdcc52544f616e6c101082eab102b1261daa146413a6574a5c92c665b65ce75f66d3ec301f88ac6167c68e60879046213bf39012484d722557540f7a7bb194abb035263545da353ca65f9ef6226fe0d58c67b7a1d08acbd6057b515fce7b7caaf7e5eb91bf69788332c40489d95c7ee01ce579a0c5ed4501b66bf350833e556b81b7cfb4416df9fddb0e6d5ee045e261532caeab3e313f2b4042548f48afa6955d5c40b517ae5c8a55c694bc4ef249390a3706ac26dd66cdff5eccb85c0e3687b6cee9e6c32dedea53e5e8d3bd29f59d41279a8ad50ff01637bac091d3e8e83372a855fdeb3d8d52d3aff60d03aa1bf8651e18f03eb5aee17d57c019b94d75697a776459c1b610d08f0181b4f97b7a5e85f4e006c81f67c2b6bcdc1f5bb9591bfd641dc54036cbc68e6e633d03522cad0e3b4f56bf785767c83c73db2c3085cc694daa52a7a3e25cc3c1e03ffa4daf0c05a45f8fd668835d2ea6cfe7b286f17c1516310a96a792c7f70d947030a4770a490df3fc8d1f7bb717eb35d2a882f75aab61805843eb51312a24198f9b555ce80bf6b5a8dc160c1e45394c513e2226f147dba6db2ce958dc4d193a8be63e3e635a6a6220e9d8e4ef10b4ff0eb6fc6cf38a517556d38a37b8c6569ce3c7a5baaf78bc8c0b0e8b20ccbb3f4067f46b6a2e285924e5edfa3b6eb2a3abfdfde3419966dd76dd1ea1304db6dfcb1f54b4b10b03ee05b55cff61e6d86ac93e7ad66380f3e871aca1f7686f263241e845a30b84b985dafd99a1c87f644af856c8cb8dafac034a02f90135fc966d03fe52d630430c6de3e9971ba6d3679c39d1261dfae24a568740b26713b94a1931128779cf7a3e88b946716004891e0d93366304a20a1de4b17f79743ea802352eda8e1e995dbff230502df6680f9c076c04bb075c2dd4bc0d12d482d84f5b8c5659f03ac41cb5cfa9bcb1561549b46fca9616270e48c926dc944c3f9252250053ee757656af1f02ee10cb48216567cb23277571d97cfd316fc62e44347d1559f5a7aee8ac287d70d61f3e30946a7a68eaba25f3f26a2d68d66130364ddacd42b095ae6922da01b7546058b1a5f43d66b443d6e0c0c157778536d693c97c1a6da50cbb8b947dc39ea904f8512ba707dc68d55e4872bbeef137511fba1e8147d2d29e2b6a836f12da042c52af508532950fcd393d5eaf5da1c7925a441a0908fc3723583881308aa39b6e99513d05c0a961ac6da8a27151ec7220ab47cfcc356c5d49160c902a3bb6b523bcf1513139a87afd30ce027b742ea338e76c8dc13f5569a7559f3383b2809b4ed0c2e185bc8fb15b1a24b47af13ed41f62c89c1d9234e904718c07d0bcd5c59b962a12ba7441adc4e60240b9f36dc3c04c1920562c1d1008be5cd1fa34ce7fd5243838599bc3a034c9d02838b1b223408f2073a124a477e6e4333ed3df649f022696822531c8f67ea66026d5882c2206b3319a3bf5933349137df7204700d96dc669b2dd43029d09c1b32652513a30518145091a28255d1bb5f50c7911c856df9f4500d917cdde5b84a0ac4c90ea3c13609d8ea783bf9548af96bf10c28fc71533a9f74d62303d99f4ca8336c1dd8fccdef52bc6b239c378fd8f0193b4ac485af57455827b5127fa5b67675d7e505d643ddc5b00a8aa1b9ed28ee480a6bb9eae56d743b4ce174a4db7142ac9794965f4789d489325a03490d135cbb76c6855c4be8ac4b6ea59f729f6c1b32260882e25736d4b02f872e0a5c0084387bbd3b499b7e53b95af81d018e6aaf50265d3f5cf68979a12fa0b597172b180c0e2b99c54d11c1de59b0f3f95b1117f7be7b460d5240023e135bace39646f795db5e7a3f18320b1e0848a91273816ad40760851ead9fcaef6701cf41437bb9e9142a1cc5561368cda7282db0757d3015b09ce8375be759b316fa962c5039c566fad402d638e718fe87460812c01ef08c86416a41009bb7d1b12c5a49a2dc0adf7013130ed5896d3ffa209ddafcef146a632b87f57d7781a55c1fc67d37ab82d9af8e5df1305decd8886b19e044e8dcc55649e9e127c4207adb585b2e8ddf50d0a6ac9080a2a855ac7e36c0867473d17a415f92685c2c24659873a299f429562ca598e9ed4bc3ef6fea4eaf59d7bdccc594bea849d521f22579213a2f09f31b0d700da92b86b25298a763eb181dc1d98f12965f194068968a9530ae483a1c792f882cc9a3f5e6a3314061e83b8ea75d78c44f05d6cb97506b5cce8abe9fb7fff7ceda9a9eff831300582e9c81d9d5f6a248e76b885a8153a1612036d02a0dc6d80ff5ab5cad39e13cec8d5d7360c02e4ae55ef227ab9603d6e610af7420ae59109f184ce4a22828fc5546005a59e91f855c6005626d3e4d8543c02089a448b508e7d256ff4ad1b25a20700c04e8c4c9b30a497be776b79501dfcafe73cb2ab8d8e8de81a249f8e62c4c9e082f2e4b4597920a4bedcfed34e2317561b3f94439b96309c9a78a7493b828ebe277868ebde283c12fa44c2c72e9bfce1ce50b5d409b016618364dd49bbf2d3ac6e46dc9b7814e214ac789d16d33626066f084f2d5fbc042a1c33019ab5e438204b621dbb18bb2e0c5a74e3903ebbf1a0e7c42c0f02b564e4a05af16d631a9f54b344e24bdbc60282c925a98d190037206112cc460b07f35f9d02cd2e7e49c7e839394378213d4b47831253924259ebb3b92ba42a5cfe0b2518d54abf4d5145aef4995f3789ed8fb9a82efbcb988536a194b20a0414969afb23eb4ab29d10287a3061b156d3d770602a96da0ed357368c2c0f590acf834e8dc3673326f0f98c3a73046d358afc920efc865fcf6815fcfb6d8f0dd9140e995bdd8bffac904cc80b0d3211079c80a3dcf49facb54975a0958954f14d7b99f60dff75763b1036c8e6809c34c370e250b38da3763f8c88b4e53ece7a3b66bda64cc1694f4368c2f9b1f82ce5d9f0805bb0abbb6d6850e00e80b5c70924cb6c657fbc144f7ce9e805200638a255437f41f8ec660d084d93acaedd208ce845710895e89bc7ec6abd36885071c6429dabd7ca6332ebf18e02e1b5159f439a0d2347d09cbe831a454496fb3460b95cfe4cf02ebf26ac0df2944eb714aef3170a7661d71cf8c5edb07bf16fe50dceff618fef0689247831db6d4e1b3a61c62cb1e00c7570dd1652107252d40b219374f54590831d932dac205ab63907a67e3b9112a889272c6b435904b29fbceff59d1de1313a7920c4ef0fa3d10220b9f4ef03d612d045f8921426769db62221beb83af4eb9a17553fef0351c69af034e9c43ad57f545cfbebddbcdfcef552619fe5ea39ac377b6b7a7ace400938ddf38d24aa09ef07b7f0b5f1eede90032b548abfa7455384daa71f7ccf8fd5acac14fcf5ccbf3dd6b205d402d238a24a1e0f8a4c052bfe34d0516474057a7c0f4acf2073929e3408f2539dfb43b4f25f985f9527504d00870060b3dcf47555cb14e6c9737ed6608f7e0fc390de8de5f95ded7566d93cf29f2a82ddd82525763c4b06a8edf1a9aa850bcadcf4ef0931fc66c897498e9544b7eb0a4d0084b412200aed3b38b35936f15d2e09ecc240397a2c4f15582d7fb19905cf364827de568f219788f6c4112ad5380fb75a3475640f2f84256280c38c940788c99c2a64a30e499bd65afe2eddb87f3de420540b0588cb4f9ea2878a00e32b6249d5106a3cec2728e74a970c7fd35d26b334cbc80c90ae3e88ae173789ede170c2f4dd2b6e65c42b38422ba4780170bfe2240fce119789982fa8015661f904e00b4a6444ecd04ee3bd0f4d458dfcfeeea1ac2ff5b0254ec9ccb709249d15c6121da7fda48288327b274bcecb834c8ec8b4c79e60da1f8eeef813b742448b9d4dd338a3273be98bdbf0554e12113c1030b79c7c9373c50dfd8909ea464207bde15b896493d183b08a3c32480e3f4171f3c401a59097ef5fe1ce86b48ee59518db1e02c531c24049a3cfbf7277e9f0a370ecaf536fb5db7375d4fe3a14e07f9a1e600e9d6b68933ade898d9274ee3556a68930aa12200ad53513f28aef70918d957a5f616039ad6510b3629908fdea438b39549e528ac492c86517530756f204dca6908e8c48670cae77174096d39e807f4bb37a9b525d914bf3a27d89424ab4813481c43cb9319baf57a3f82e43c348da5eda8be7b0b79bb704243aa53001a7ae0f9bf0581be2a0d76de708f74c505f1072fbe1297ea38b621de7682e25b468970067c606c43d39428a363bd4b2d517734aa99059674c15fae44626b54e59ea5ead7aaefe6615c3f890a41f5af08a0023b371bfd2b78bfb8effcf9ad01a89ae599098c818188f8e659661f4f679a99780924be5f1e09df000fd43dd196d7543c51f3a4753dcb5e6010c063b327defd312b1a86691c72274ff48b5b45de49aea08867c4b2e1a0e4260803725cb869732105a5c33a41c04ae0a9d49c5cf08a0e9f3eb99d0d565e8373de16492e2dc5c9bda9f7a7a4f2bb5068016c7da8b98a7f55df32f3acfcd22732b467683a85bc71a8dfd50b6c97bedf9812c511578d4af0973b5b205618d00496865961213bf375abcaca94256b84c94057d99b7d2f7416cf0073d1a25e9dfc87da8fcd6bace4353714626371a93ef521a383b542b65cb0510886ff5240552bbc082c2d39c8cf5aaaf07a0870b1f9a2de1f9b808e827b76a5aed4751c16dde25976cad1d7e21efe1f0d92fd33e624a0e86789adeafe5e012e23e5224d18bad1f05b17304defdb12e14ebf950d9a6e8a9fff309afd9e669f1683830a420128e0e56ffad5bfd63de8d70b1587119644acb49f92ff5ee166d0ddefdf5073a389cec7d27906affd6700d0d3c49111789e204566607a1019e900e5b924490704af91542e00bc413c2981879716106745ca4a4055ea654ac8502ca1bdccc0d8cbc8b35ec6c8a98c844bfde6719189652c3b26d3f3cab0314a2a1379522d3463b493922dd872e153389cae7b8248fd342ff530c857097c0500c476e45444bb096f197a81756d96a289c396fe609cf07e804478271c0143bec10c3d8416ca7a0924535d048d430d3ee4eabd16db2ae811dc10fe95c362a475cf4139b089f83a44265c08500f0510d60f45117c06b688be1af220bc955e2900faa4f680f1138a5632197a198fde2a0604ee701679b752b968332657299004f43f9aaaa4f13158f16d0e6a6ce9d6d9ff2b2c0b638c4f2e7d40edddc1e2c43d92e6ed3635150aa94639c592f889651554477ecd5d27835d504362f91fe3c30cc438ce675b42250a730cb4b01d96cb7614482b56cfbf2a49782568199588917a93d2124eab9af96be01a3ee79e099df0fc5cd12980fa7233a8895a617fd36dd8566ebfe9c0bf5da6897aa855fbb3c2e9352b2dcbba985244608653baef57d763c6bb9bddccaac17652caacc8a783eef8d953dfa0b6cf8232840f994bb4a8dd495d7a236031f484856de63b0478c459bc4c1a24975f61993178ef5608d6f8a88fa381164a77df004bad145ac3b5035d66006d58fe10fc0c5168297c82569e361737bab900295b971dbf257ffba63e6bad79d14fd3ec5997cde17aca4e54a4533bef2ca613fd424b735f0f3ad7ec6a01858f1ea6583552391b2809c670f7d5a7785d64cd92729d326126f1acc30065366e3273f86e7d6bd454b84cb09f8143647f0a42c5a29edf38d7c115ec7b0936e55633d651c92178e96e575b494069319589573274de136764c348d1968f3b91a4876ada08a759de9554f829050ab5fb392fc7e61841b67bd1c115f6b6a2e9427ee31043c8495b45800953f1a06b10ffc5a071eb2261c8e2600e019cf0ebef4d8a001d087985ba2fae844f72ab5d123504288d6e5ff4af2bae460a39c32c676d69c080bddb442a2676f946d42e1b43060c6dcd165d079b5279edee8e0d36821fa4a749443913db2626b7c0dd6feea125a32d3f63e0586748730360f41d00cbe2705740b9a597baeb768410490040b9391a0a38c1d2c0f29f78a8d8a279f9aa2983029062988c6e28ee1c69556a54057d0c40821724e232f471eb68cb0038c7287b993a067790bafe3c2087827a7210dac1d40089623869633757689b1c05b214958dbb236339168af33eff0b7b4c0c39c13ce86a3d37cbb87f5578ace44463e71fa3b918a4d71b6bcc3936b922b168050186633363dd017668c3317f2cf7aa2a94b0641e1665ebd05a50f92a89dcb880d8b641991604ecb17309d37e28d10818a139dbd0adbe46d4ad94a2f446888d8148c62887990b6ca73036caf6a502e5dc35fa3dcec7fea762b28e2fb562d1c2ec069c9b0c263bf086428743342cf56d05029da19fac862564ee0807020f84f07142199c5a550e53049d3cbae44834de7a37380e5235a9c7f2f4cf4575c80613b2c3515f44aefeea2687bba65c05d1e1d07ebad8b1cf279818b190be371bbd6dac364acc6834466e8c726704af915ddd683312adb38bbf916246a0317a63d4dc693487f4768e9247558cb4830945711c99ae9ef90601405f716c7d68a4a2a980d3bfbb87b47b47849f7892f437c600d302c40ae3048e24ca58a8e19f479aeebac1177674ec51a41fbbd574d95f6dd85563699b9470571cc04108ab4aab19f0b940c8d89f4e95d40e31331cb0803b4334dff8aa32c6c79171d7bcc5e530e9fdb585ff77d581bf19fb0ad2e2e96c97c104c09128c8827970949277a3e9dbccd20714e482ef4ee8940cb70496614b1898294ca6cf780665272b526b01070758aba9512d43ec6fb10e23f3eff261690a8fd78db9bda6db6fc1ef445bcadaef1d85e0c0e4e1b5b2bd69386783dfdc9a4e60470f52e7038cf7fcd423679e543be7884b2e6914582591d1d70ce02378a3ab7e84c42af6f37614a20161cd93fc4122c68a886d2577c8aae786a2c2087175a63d712495d6f60293228398f42251222125122084a54d0202bfff8795f2b9192e39395baf9f6a4296b783a4b79032bc69ba21f723d99a0fcaf50492edd6ef60a61bdf24f16e9638b26ae37c5cde38f01a68249e8c1055382e9734b37123f18db1ba263f320af2348643c9a101ca48bf2325c55c7e83182a9d430a78729146db591242f807bfcbb993fbc05c0bbd743423161045fec6e333e0e10e63e93fdbb408865c6a02e695bdd06a17082da395611065b5b09a4a0bfdeeca501f7ef5a0aa91b83889cad104aea3c578d9d9ceebf38530949c5ef7bad02b5f1773552f6843e4acbdb5a01e782957c9a7267938799a8e51f06d993af6cad5f19deb748144713b6a99c4571642ac9a0e0a3f6e5baf2041ad9f86d54fbc21fa9c3e1d895c73c52604b55165f9cc2c66aaa08e8ccdfc4b7541ce6710e06aa87b7abd72ca6ac0bcdf39900cecef687bff19e2d6016e78d9b7abb12718de85a2e6342a8ac788d994d3f22c30844188ac850e95c41f1d74ae96b70439616150c79446fe68034e87bfa3a9939ba92ab822e80b0d08130418214a5c62b69485468b05d6ac71b9294853af9271c1236f9f013348cdb415fc4e44821cb872a7a0ebca6aff479589906fe3d997d7a1088501e190f337043b253509b017ab9014649899b33079303e0337f168166ce13ecb0f47ad0d7380fb13b63187b601aa9870a4326531906f504e2f5b93b7dbb819769988a6eb34913e76fc65dafaaa3082a58b5ee4c0cfc4d69d658a7ae20054bbd09323e1dfc3d6636ae0e92fc252e1bdcca9dce587861297e0234fd605a54e2feaf6e48e031f3a34bb84bf869a84c77da85a445c6262942cd5a76287d339a6b4969615a0e03d3f82fc0b05e5b26e24fcfe1e0668e9a69796d8dcf93418cd5375c5e96a8ab60e4e4fd8630cd3f552e302fabe7c8e5da98f9cbc335afa5ea666218b40ccb15b02a34df07226de58b214fb048256545987cc94c75e55b66329b8615130f933b50bcd91731c3871730935e4fc2d66bdf8018d2b07932b89a2ed0b6156d7a2e3e471eb36843ed08b8ff4eee3e8f5c7e7105a3f487580d84b1fb277fe582315a4259aa11fb55783ac51811aca99a110ebbd1f37d5db03139e1120af568e7e82174d19c9922fbb779bd676baeeaeb1d3dce211df39bb7acca4b2092115e0d37c567ec50bc108823eaaf53cc44143ed99df5e4c9820174f32373cb5382841519c4c30f141405de72ce88ad31a34e8b02acba0e5ec090501b1fa6f28e301a6aaff2a8392ab219dec6d301198ae0d4d76a7aa340bfb103fc4ff1b6d059ce88b3dff32a269c6a2598d8e2debf1bc3dfe59b373465d7fbe987129c81ca270b1aba90aa3cfa7b6e421ffc53a18e85a19ec51b414dbab02da4b853862522626bed30c7013bb2830aa5b6bf8eb4ac5442040026089f34854f6c676475452fa0f5d213f5112625406a489407dcc1b93f35f0f1ee15bf9b6c60843d406e02577f7a2d071ed421258a9d9f12ede94d8eec9f05e010ce2cebda9ef5a09792a14f4b298fe52a8217bf8bcac6b176cfdf17ef4d86504409763f913b31518b341887207b059807499780a81b972b09ba1ed42626f4f31307b285651a50b02800e043d4649526f5bb6ca59710f8e0da88f96af4421d057d27fa37ff171dc52b3d16cb14b8c352a7d2f82d8fca00c491c22322e08cb68a34938f4dc9cf3488ca13da33a417846ad94d77493132d6d0869a42dda6c0b9f9cc4752d1912e24baba8c5c115cad1280a98fb124723a22748e9125cd7083f1a3b1a287a179586d64091869a39f524c31b441e5506dd86512b5b85262fe7378a909dde2a4e803284d2095a918cd79c409fee269531aea5200239b61b54c62a3b867346d22a6326d48bd2fe3b9a69bc262ab8406640e5e8ec11c6fa8a75c9724b6d2cfe5c29d0dbdcada68ca79508468493c4ce590ae45504de4c9da62b9c8c42757a89d18b188a77d450bf292e666fd4ad8f2eb011075838894cedbb4160e06a4bb9174acfb6d1e713973d287ddef3650ac1ccb400d58075f703bc2e5d7f75a2dfa444b12e97088b6cd493af06c070987850bde59178c547ca2c11885079a46cadbaf43e34d80841e6f93cccdbfe61f1590266d3498e9d1f448e95e5cacfb13fda993aa4137b078e74e18ee4ec792b8f6963bc7cee78e88fec54ddd0c95e82033a73f773c2d7f190a5b50acb48202f22ce715774740790ef23e21822297a24c35d45c59a6e11cfadd664341051dcf57b589ea478f6bd484d17cfedf31820789b89156eb3abb3f79aca08158cf9cb89ca545cb26c54f135334ad796593a54d9d3d6822796ca823cdcc159604856813aa18eda39b9c8c61d4642e208081fd3242aaa0ee53dc76f8ce66b0ac67b702770b43a81f056110e95b8774420460670982336188a85ad1139668d0c6aefdfa9c5507f5556acab35bdceec4f3dd447a0945b1e658262a5233aae3c3355093ab2d6c134ac3d934ca61c8559e73e434c394ed3833e52face85ffa36fbdb1f2dcf733b83f57f07c075ced23d002e2f65ff20b1c64be088b525672beb4da76b03fbe21ab8eead0cfea589d8cdd214d5857778a5f26ce711ddbd15c67d3a44a421116552819179666e6fd32d8b9fb39c19a1309042a772936fc319974333ad2e91c92c4ae35468a71298d5b524219494c4cc79fc21006ddb96011d66f13f7faaac23e7c980db2b431a9f1a27b119c23ba343706c9c8ca48d864048bde25ec99821d3f6bd9c914ef51aad4211b47e5d71675487946568aae229e336d3bc5822627b26466d86d0ee2927db7eb82eebc5de40a1a1be1b49544c56f4a3c1f11088cc440ccd4599bacca5b0edf810edf35f2ccd6c124048c45aee43a918726c3b2e55808dd6846ec9617b1c81faa9d2c01f7417195c7d93d75096b43958ced01c0ef3ede5d3baed3751586206c544dce0bb13612e5ffd7799db4374182467a8ef3291df8151daa137987e77f2d9b207e2487ae7e6bcee0ad5da8131dda0b701e8dde0e6b9080fcca9ce1a15da953bac08ede492cc83537acd298953a8ff2e45b1eb25546227ff4aa250caeabc8539c209323376ceb12e8ccad3b498bfb6599a80ca78b001e148ddb55713b84bc132680747e842bf36359d577723a761174c0ba9f6a48988aed63dea85deb266671babf31a7d1c63017b20ab1f1a733aab10b1ecb3f41fcd1b5c1e909dfbb433a739772c047f05ad2b046188a3d8805899b29137d8fcf0731c2551815bf29bdaa158d6d15b050c15cbae07684fa82110c9952f97f387f66d0f4589663a8647628666419a9b14932d1f8ac5189ff1011bdd1ff414f630534a1028e6104420b3a50f0a4d001f3ffffffffffff0cf363c1c7cbb1db6ee2fb55882899922c95759595620428a594a44c29a96d20d6460821b0d71df94e0f033150026c0b320b7f0bc9e1e35488b7ae384a5c71aa2ed78ff225ad68748c7f7926332bdf6445fbea95c40e4afb89fd2a7a4b26f2c47cf22f5413022f3c4083024ef050455b92921f73cd732a69928a434539451793ec9ec49524fb25b9313c4cd19fe6e51d39ba94dc68297a939d3948133a677890a2132d7b9097d43498368ac63f4f4fcc96f4f9e739f010459f7f26cb62f50845a3a59b623a5e09efb31634780d30283a31514c09ebd54f16fd4497ddaee1f43fa6b8a58727fafe9c7c2de692190d2d3680048f4e74c2a36aa5acfd557ae544973f9b3ce25e4a9229e3b1893e8cce1fc34aead3d369a20b171ad2c4b24c51cb44d7964c4c765a26b9429868c6672b85f9be26b944e7b9165f4dd8cf8d134b741d1e735af839c1532ad14972f7c6c5a8bfa95b4af423c753923da732f593e8b44d8cccf2a91b36e92189b6e36514b7202b948a7b4422935429593a9a7f370889de826cb6244a7c66703d1ef1c767505eb94de3884e4a9364d1990da30b1b65d7a3119d1c4a98923327d961a211351b384e190e09e0c083117d4e52ecf4e0a5f74b4b038f45341bc3bfe9939358b19228a21393ae98989c92887675e5e49127694c551951ebe215118dccbc26564de5108d6acb92e4249774ebc8c3107d07352909cd511db25388634ce2a42852820721ba92a48a499a9efca8271e83687c4c8cce2f49383c048169ddd560aa9967e213a8c07a04a2598d667246cc46531120daa0b35c09cd258a7e923fb46bdab21fdacf5135a5f0a224f19b5d7af4a14d428bce312d175394f9d0ce960ea67e262a364e0e2ede3df6d067530b3225a16e1ec41e7a68332c6b5ef37c181e79e8e4080fcf1e4b449424236a36bcd8a2059fe4b871030c1174711e8c1c383a80021b30f0c0430c3cee80353cecd0574acd1d27fa040b7d1dfaea68a14ad2984fd679d0a1cd521e46996a791264ee8bd3051a1278cca12f51367f29512b579ee5d065fe595c0fa6ab44d7230e5dc9e6af96a1e46cd203873628495241e7da4399e6f186367edecea0621025f575436b62d8981178b4a14bcdeecd2d931294c0830dfd575f8e31b9383cd6a01e6a3069c8b7e2fbc9fdd1f9420434780da8954878a0e1cf2b9fe4d8a152ef65789cc13cccd06b92717ee5c3861c9947193a792d79c9ebb90719ba6aebfe1379160c788ca10bddf137c5f3c692b1181a6562286db96a26094d18da24f69fa7e43975bc0686becaa4c61c375fa6b4bed0fca768f1da3dc6ef510d0f2f587974c16a6cc0830b5daee72405f19cc716da0b3d97fd6476dc24ac852ea6e7bc65398ff60a8da8dd053cb2d08c8a39934105956451cdba4026f8828007163aeff3144d2fa749932960e4f8c2e30a8d7cbb9920dd3ba31721c377c720e4f47f333af76496b4b4eef5c95a6179312977681397d1892e9b5454d169ba4b556836ba89ba952862b924a3cd597373d99c330be3a042db49296db993a07398dc9dc714fa37e1e4d19a377e0ad28bc171c0430a8de5886cacdc8da8d930d8058f28b42aeb593ee7756c6d2042a1ef4e29a388af66783ca1eba463f288eb2f3de211b5a382c7188d12bb174f4768e784564c58cfeee221e27cce8347133ac147f4a98e267b541213da2a1545e47b9e704287078f25342a5912a49ebc182be54ae872279343b6346cd893d0888ffa392541a8aa92b43a7820a1312558125fae64f7b88ee071847399144b9239cfa3240c1c36d0c3085dca26651a36a60c1e45e894a0796da2852af9d9c483088d58117f264c7d83a2e0318446dc557474cb06c2438c3e671f21379a89a295d3850de4070f2174e2e2e7d8162a8610a507e306b2e11184b664135febbe5c923481d02651a252660e7afca0f13ffddafefba037a1c92fea95543925f1e801c2a29a242571f314c383079da4c43f7174aa78de6f153c76d0e73c913175a96e8bb9870e1a555238514f70f18ee32cbaf43dade699b35e672050597441eba725315e62d1088db9839652b27a34b0e85bc64ba89e3dab245ddc4039c2f000113a5ed169af78522c39ca2f74b8a2135dde273ac95bd1c91f9b49924950131dace873b55bfecbb544c72afaa0f2968eee191d84d8a18a4645483129496b5319f542472a1a5d517366b7aca2a24dcf9071f33b5d44c7299a519298d7142f3e8717e7b4e830452f23336617b10a0dd203414729facc69b2579538a2f65c6c3122031b3772d86052f4a76ea595c41877970b2ec2483a8ad647ac4b98d6d8d0408728ba9c428bdc34412dee35a286a360a023149daabc71f65f1f512b9b80260a8a4edc392d8d9f95b24c8a04782fe8f8441f3d7f8c166e31982a1951a341dac26f74b1617c71386065dce189def477ced75e7574a251b26f9245dce420ceece0449fc23dfab828a5326b36d1b79a78f72d7d9564d34287261a259aa4aa723651fa4d99e8a2be75994c3a4cb4dec9acd772eaca6a1d97685c338810954f87257ab7b832a77c4bce98eaa844eb23724d9b850660e4a044a7a1e3526a67c72ba9bdd13189d64d73ba25191f1afe11351b79c6830e49742558c93163124609471891e8620c7ab39592a36cd0bbc08b2d20d1570c8da9e2eb5cd6c8a0e3117d89ba9e4a271dbd53d7e1884e09a2b38e46b422774f8eda08f2228cc30586d1c188366ccc6f623ed6e47147d472742ca22bf9615a7e154de87ce6a14311fd680a3ea279d4943a12d1899c0d56e2d3e44b1f116d4a19b7b7ca6396890fd1e7142fc5fee467c28b637418a293ca2de3894cf2434721fa7d57cf7fbd26a849da4307215ad9dcda39f274cb890ea2cf8f6ddab1748cdd8c20fa11d791259f2f36c83a02d1c96e9d1d641e01a2ad8e62f98396db7e41b56e1d7ee863f6605236ef30afaaa30f8d34d94c72bf468db22074f0a1cf5dca4a5fb88ca855eea193b4a44cd144e79ab8e5c0e1c507364083023428400113802874e8e1ec13d394f4c47e72440d470e2e3af2801d78b88e3bf4154449a967c451bac30ecd55129412a5256b30dd881a1860bc8d2f4e17ca961d75c00e3a349734f4292d2b9b1f46ff8508e6d08652727d727e6eeec58d2f703c187a810e3934ba2ab6484bd12ea664448d47d011874689c9772b07fd39530b87368b9698ee9794cc71c71bba0a3afa06a995ef220c1c491838ca073adcd06bef6eb9776d683fd3a46462348da8ddc8c1863efb644949b8b96bae8ca83d186534b4d8008d2e9009cc6da0630d6daaf294459e92e5e48a1a1a15b149105b722ee93169e82b74640ead513ee6110d6da7768952b27686d635346cf20bf3fa9aa18d25a81ca23f6e0e298aa0a30c9d101a4a46d3ca2b297a460719fa929929c518e2885a7a306ce4b83134bae5ef7339e3f957cae81043d731285132c90cf2dcd2858e30a8f12499b7f47d30b4a5f955a949fe85ce4d66d09f9e1e26222f7499a992a42ee26418255de89390d6a3ff2d51e8e0427b49d23e72d72ab676980a1d5be8674ed69ecd7517f768a18dfd1c2d93258931aa53e8c842a3e2e68ec94344088fc142a3bfbfe3e71872415bae601d67f4d9cb941c4376c9316a46e9b0429f4cc65a864f49f42d14424719ed6893c49fef862cf1ea404715dad15229c9174ab4a04125a3d78b7f11bf202a74496e53c25f2d9720ee143abd99fd9468540aadc97773ef9c4a3f260a9d18b447bfc8d22782421f3ec6968d6d312b884fe8543c8ece616745ac6342c7188d0e4ab4f2e8c8096d0acda4d33247dddd9ad06b4e82a51cee2255e50e26f49f63788cbf9425742ca137934c7c7658097d10939be3726ce53993d0cc09ff9311e741e63b90d0ce9eecb9d283eb5736c7e33824741ca1133f9b8476152d2abd0e23f42756e1a79e17a1b54e9d530a7a2b25b941a1830889a6f034d3103a252d798bc82c792e2f465f69659e354d6ad4601c4ee81042df67325cb849e24518d811843e6c472f939d748fd27e66a003089ae68e5929a724173a7ee0858c2509baff5964860e1f3c5726997096d42c6727868e1e9892ef85d26ca18307e8fecc1cb5d584edc2d0b1834d44f95b084da2092aa8860e1d5c927e49bdde494c8ca959acdd41e9e934b93424401656a656b97c8d8985ae41ae09e559799f521d65457362c8b23e219bf9aa55a42a0a482a1abd5c9e61c387decba3a21f4d4ae7ac64e9a534a7684f89793fa1e4cd99d114cd09a3712e9e20964fcc162f28340029456b4afea0a2244f626225456f3d4a84a7f828dad112cb57c33f596844d1c94d65e1d3c47cfcf17184a2eb4a7252bc8310f90a28da13cd9324439b3ed1981093f132c913ed9fbc965d9212436bbe135d1ecff0d91373a9ca39d175e8a4f7b326a1f1b489dedd92cc0e2a48136d4aee95c1b308b10e65a22bfd9a2f7fb43e9de460a2ddb76062ac835ce57c89ae5409baa2a728e24916c4126de5283a6a4969253a957392a0e2c40b723194e8c40c9f64ff3e51959f446ba1a34512adb799f6bf0af19f5222d15cf00c1e673d48b4df1a1ac3735c3c8fe8a4558f9279b464b58e6834261d62a93b758ab14658153436ca5b944b995d637a78ed94699d936c294634a36562aa08f5afac16d1695653294d64d6d2bf22fadce12465d595b1825022daf5ec71a35a0e22d0efcf497037e143344287131dab32cc255943f4a1e5377b5b7af84b0bd1fe2555153e2ab2354888e632975052104a06d1c8202afc55c9f433b120fa8e9526bc7d93f43e105d2999ba52549cf79801a20dbf1676a4870b65f93f7472ca96a0e2224b85d70f5d14d792241dd511351b340023471f7a4dd5dbea1504e143b35e5ac34f1234a2662347185f300e903df496fb42365af4d05cc59c5a3ae979683bc92b9d7d3b683ef70920786894b0f032b72063bb2403e40ead26490ccf97a3d173cc0e5d25d1619464d6a1cdbfda2a2aa7d57a3ab4e59da4961c5b4ce69c437ff154f82f5193496ee5d0752c49d41283360e6dcbbaa99854289dd21110383472924a2d39a6e46df21dc81b0c881bda12455b66753ed37440dad075f651ba73d20c4b4f0a206ce8f20825e61b2a3a9c6c0d7d9c855af56908206ae84bf82c7282eeb7b0be0c9034580da3021034809ca1b3348d7947c63e4bcdd04942559c2fa1a3ec094d005286c66298b78b2ae91e945bbce0200b40c8d0a61cc5749f9ab605253786ce7a4b0e9bb192ccd11c51ab13018818baf899041da49ccfa516240c5df8519a2f28693a4511040c05e40b6d869025c93ee293c937a2e65e6cf102dc0088179a0b6261fec4e4781c784702902e7426635e0b11ad26e86c0640b8d097145a52c94a3a597f205bb88392657e52c5a4a185058cd142bf49de189e6ed1008c1cbc02902c344aa87789673145b73e0c18d878b4450cc000c142631df76413747d2edc8b335201d302c8151a5941cc456bd1373929cfe82f49414c695a884a31bc0288196d8aedcdf224548919ef0c102b744226133589b0be94513340ca6874e65f93534e662a3dc98b2daad0294ff952c69caa4915848cb6b77ac4c9a525e3e7c500a142335a92b57cfc5c82a6d08949346c6b924ba111d9da1d554ca2d09a981d279e2458a7f440a1dd784286cbe4d1045d9fd0c6d7911fcf534e9dcf31daf4984a4f75e384cee289f13cf2b284199bd0afac888bce24766f8c09fd7b75ce1aae4d7c76e501c8123a499216379378f9dd644094d08eb99b143ee47427d50c1e8cff1c6c049024f441c73c84181d9baf81d841054b263ba223f4be79dcf2f3aee630ff00c40807176162490c174811fa8af9fb9db2648f0ed90084087da910d1417aae9f94e521342e2643be9f3461f20411a3139a4dbbe73b1021b469e209b265524082d0677459ad985227d59b2f4080d078decaa09745e88cce0fbad89a52985259a693d48882f8a02b399ea02907417a60354e00c2834e2893234ed041901db415260932378ca90c23880edaa056e5c14d5031ac8225f89845bf6162e9d11fd4c27459b495edf9a6ea25ac24b588c5b9657c3b9e4a3d72c1072cba4afa290795c41093a657746292a4c2cb7a989f912bba0a73625a4c92b4a295cb51f6041384ace461454afe549e5574baa6e384aaeaea12251faae8b7848999c306293acb5434964a3f47d912319d828ace9345973941778a4e3c539a260669c2c7ebc314cd95f0f8399812374ebe147dd2fe23297a37afa4f28b7cee98338a3eb86693379f0aa3492c8ac655a38ee70a8d216628f00314adc7d0f94f4c4a313e3ed1964922663f5e7695c713bdcfe554613a37059dec441b93ea4791eb161332d5830f4e741eb6e2280fd3a5a5db44fb9bfb844d37b1c99434d1bb8c2579dec5a4dc4d26fad1e549664f98e8040d7a52e44a9e927489ae4a5ff3c414f4f8ea96e8447453a79833b8fea812bd8993ac936928d15692a4ac8f4e77071f93e8c4a43fe6959ca04ccc1749344a7bdcfcb1e413d54f22d1560c13438930d13d2d24fad19bcd63e7f021a32a8fe844b383d09c23dafd3c5abb63a5117d6bfc8ed818c41b7c30a29d1d99c42fe1543e631f8be84783289d1573c9fbf9a18846a95fb854a2a79224d944342755c77e50aa41468c88469729a14d38a1d4337a0835450b1aeae11ba2ddcbd3ff54425caf5888b6b5728bb7589f94a21a351075f04188bea4245f5eeacd26840cf163109dc798b72bc90cb2922a88e6f4b25f9ed964f011884e0cbda4cf3da88a4a32a286f7800f4034d77bf22649ca233af2f187763f9824f75c5586f3fdd02695262799cdd25a611f3a9d849a4a92e09df1c1874ee8e5c5509d726bf8d84397e4ecee5973c42ac9f5d0753e0fbd1e457a8a9d8f3cf4a64c8f503a1eda783267e8c8d83c49ca1d3ad17c4eac8e129f42b4431fc424f18491a5546be3b9480e7af15187e682f0544929a99248d33fe8f0091e3d63fe9ca7e0630e5daa70c2648e693a5352173ee4d08fbccee817a744b5fa11875e47cc2429a778768e0d38b4155f2969b638b6e0c2c6e7d802ed0d7d4982548b61e639abbc67f87043e72763784d82dec1b8a16de8c4b9ced319dd19cc1c5163439bca622e69b5236a38c0b8f11f6be8e272897272dc8b1b5b24aaa14bc165459ee8172ce4236a8869e883923557ce9abcd5e4e28071833fd0d0688bf30de1d11b317830f2e30c9d92539e5bca49fc9c6c44cd1c0d2d2ca0850f33f4a9425c8887f498f3fc87c105f202036568d7938ca23d3787ca213274c27308253fe531b49e67bada5475534cc5d046b79cc2b53b09269861e874deb0267e3160e82f0799528be53ebed026517e296ba29b9c222ff4ae1fb484cdebad99b9327c74a12d1f2d252fe5123dcc5ce894a8a2e424092f154d088bf0b185f62491f1b94989b29ed4c261b406d9924fb2bc66a18f1363522d5996498bf881854ec9c1e74c1099a32dbb42a35292e1173a676f8f3ecee8d42a8c1c8bd1d4ccfa30a393fb9292e273f261853eb6c9a274fa69442d8c1b05bbf828a3377d9e24bf2c3b4ae32e4a073eaad009cfb9f4cb523849eced838cdebf455e8e12b939f7e26de058f7a29d8b306e8c38e2830a5d4c794ddc249814407c4ca16fefca194c506981175be4b84102c40f29f45962fe3515df4c62670c1f51e8af4cf70972949c7c4250e84d471dbf9265530ce227747193ae86feab80e1638cbec32bdb7212447f6cc5f0e184fc42c931dbb2633ba27657838f2698625b4a5a95629b1f4c68e7ac62b0304d32c667446d101f4be8f4c7e435cf520f1f4ae8722649c94108a131e9c58cf19184de43533ce75b6b8a93166c81e30d1f48e842c482ac94c2f6c5af458d1a1f47e837557fd411990f2334dff93f9418faa424c4647c544974bf8756c889d0484d6e490ea321f465490c716269eeaaf910a30b954f9a79d2635e3a21b4293263922f5b4a266a3e82d027a571e3534e46d40c57f001843e56f582a9a5244e92093e7ed0e60de6af9a171f74a9e414452525562cc87071e3c61736727c0b3e7ad0a72083fccef379d4b21f3ce833c7ae4eba9392f0b18356cd72902e32e70f1f3ae8b77fc3259d39579e860605683806b8b871a3468d597482e62e31fc9229152617060f5974260927e6b2c4e478ca8382472c1a75cf5164ca2529bf81c38b1b14a82bc303167da9f8ea26479fce1bf48a0e4912a3d524a8184344969518cc33d01042ef2146680e1dcdc30c42eb5926867a08195bbe116d5b020d20f4255a85c99c64c340e307583e1d973dc9711968f8a0d7a445496d312755a6079d9f6a4afedfa2949405010d1ef47b264a3f6e8c6c1234061a3b68e3c3a4f6d861ddaaa40328b0c1050d1df4399f9cf262ca9d679945e7a384c9794159744a867393ad319524feb6412416cd9ff61853624583c3c582400416f6afe8b2998955e5dccfeabba2cb3ed31da3855ad158c7145359473f5ccc8a32fce75d453f5e5e7252a5e9da5145bb3aa742778c9983482afab7ec16525b4445fb7332fee47f0a638a562c6a4ab9f272b6eadf20528a46851ed541af34aa0929fa0ebf31e9c88ca28f1f4d345d5ad1bd342e0e8a88a2577d4f9252923c14cd9e14bf1e11e7794940d17f4949fc92db6a92bc9fe883f40a5a7356e50c229ee8bdc49379b4096fd5398a9de847a6c5c939262f8870a22fcfc1fc7cf54d34deb99458269a96203f4d7471e1a3552e693974cc05914c34ab7e5a83ec94fd4a4e0434b4d8008d369805114c885ca2d112c683e8945ba257ed8fd1a27c2295e862b7888e4cca94aa54930825dadf58d9938cef243a39b8ac550eb22f06a9243a9d9abde19d740e3237069148f4fd5f2589215320d1a7b86f2abaa9b0577a4423279a142eb89520a7f217441cd1f86f8c9f6314688499e5f25793f02be1b8f181911c5b94600c1146343a4f12839594b466b0c020b2087ca4f5596a75cae2e7c0e1c516783b1051c49992909f17f5e5000d0ad0a040c1349144b49fcd4d8f5b8a884efc70d2452dcae8123b44ff2a6a42696fd10c1143b4ab23db256c5feffc85e82adea3c50a1d5df9b1820821dabc5e417ff0ccd2cc1944eb299a74d398c2ec8882e852d21e6547a6491d55205acb4f6295e8d513acb2148800a291eddb96e2490844fed0cce73831c79f9820e2874e92f9a49254764de9db1244fad08812ffa45e388fbd213eb41d450653c9f426eb581744f6d065d15672b45a44f4d06735b790333a9b8fd0881a92219287d6e4be98143e5f886124657ec0d0e24c0a227868736e5052ccd5c1f1c51639c2207287563e84c5142c45c40e9dde30f12dc91e5639c482481dda6cf95b35c9e1acca17a14363717536891a2e98c944e6d0258d9b4754127c4c2a816107113974a9c433b5e78467f14a8e3a3788c4a12d4989b7e97e9ede92082270b0aa2fac34991c2188bca11332c50aaa4c4ca1e48a1bda9c525072b64bca797e9136344ab9c8df59d34a840d57c68bba5890f714cf7820b2864e18d18d26c51c6a9db9000e1abc068620a286464e7bc37c09be255a9134f425f698b4ca150ad0c040182322b0408a2182864ef2ec39ab323d3c2339b690c1191a119654fe0a273f97de861b5e41c40c6dc9faf265c24a4e164a6cd4e510018edf22c7165fe098408e19748d1a3966f0501029c38de40506284081068890610c2262608048186c6cf185043ee0c58d2d3e44c0b0c5171278e4c58d0e2440e40b0510f1828d2dbe90c001230c091840a40b0f22c2051b1e225bb0b1c517122881074044b4f02d68c1d1a2012259c081051c1f22573880c81936102062c60044ac6000913208205205018890d12541e9a07fb4a6072254e832432f9e4ccf31e7e4380f06ca3183f681c814fa9435caa5ae9c238848a1ff1295d7fb6114dad3ec9d929656509e1481421f2ba4faa8694fe4097d25419488f94ea6637a2263b43e1a221e643c294f4ac409ed497e217e614974ca8934a130a1882ca1f9cf4c4166970e2e1f1125b41bf39ee5ae8966728048123add29bf58f21711dea7f31b30b8394490d09ad0d59139968ed08972829632f1b22a4146e8541273789333ad3c39456846acf36651e2091f12a131dffcfd299426492a87d09634a1836f381d272a2262a4a3eea9282b958810b6c6ca402408270204911f34fa24a14b7f88f9aaee832e731267339f244910e9417b7951546bf8cc9a771ef4f1e7e4ee89b1cdb5dd41a7152a7f29132b68f8ee4474d06fcc9f5435ac24d16dc82c3a29e9294dd3b34264d15f05d511d17b2c1a5d694aa624273193feb0684dbe14a2b15f41dbe4155d8a41941493c6eaa027ae38db83924b8c65ae155d50b29a6c913935851056344a4ce91a624a2ecb9d85aca2cf7b4946099f3457bc901321aae82cc9cb73629fa426be4d84a4a2cbb578a6164ab35370442ddd58eb02cdc004d80114d80000244250e10831291d51b391e371d8ed293a4d3209a255d1a3867344adee4cd185922abe26774a61558b10d9a599388490a2959f739394c5cb194fa3e8a4aac70e11ba28da8cb9f9bc94a0503442a610fd215f50f4792be7cac57b44c34fb49b3756fc688e1b423cd1ee280b426c6cae923a9d68e3f6494a7cd37d0bbc3821204308275ad124c7f8cb55398bbe893e5aea8a1dfc4b6ef0d610a289ae4cd0372413ed0919a724f19e31a10fc1443b4a860f99a57955cb904b34a741459f6f50c25c7586104b74e2c9bd5bc32bb7fb2195784574f73fc72c84127d98f135cd73dd0f79127d895aa23be5d013af378448a2f9d8cefb2b1ab2d547a2cbf9213c7acc861048f4bee69ad43a8917421ed1660bbe59a39eaaf87c0b218ee8e4b0f3dd31880b218de84576094d72fc669fc788462993a6f1645229cc08842ca22de51ecd628a5f522a4411cdbffa6c5099b19ceb174212d1b6650ba3722c1937280d118288e65c3f7c49a26593e8107d450da1b22d28a54f43f47e294c887a5cbb9814a28dd5e2214e1226f77708d19c79cea96e31be85a8468d1a35e4103288aea44972085dd5cba011449be49bc7301fcb2ca9184202d16555ce5f4bfd93a207884ed6b8b9cc6384a8a03ff4a34a6e18e287ce3a6710f2c48f1e42fad0e9292fdd79acb49b9e8b83e3c6175508e143bbd59d576290d94323f4a74a29e7247ae8ad4ae736714cf2d0774ca9abdd7d3942081e7a0b3adb535ab6b23eef0a2177e8949cd9d17df2465d133b343b42f5e6d268481dfa787d39e82d9d0e7dc79474d22ca339f4a259a2a7a5c5364f0991436bfa635945a49a9cc32808894373924ee1458e3061650f814333b2b54de739d1947c0979436ffe26446ba942dcd05912fe9977651784b4a193429fa8a9f134a276c33706216c48eeb829c5cbac4ac81a9a912b72953763e9dc4f1d0d2d2c70002842881ada14da9238f5135cf34a436b959ab584af1067a62284a0a10fd57d49b6b0ec29e5cd8c903374a282688cc94a66684fffab4b5d4b5b8bac105286367f34757bf9b66c0e199a4b3ae94d82321943676143e9782ae69854434688183a133ec920740efa4048183a29c99d63948f7f82102f1342c0d0495245c69379857ca16d1d558b7baebdc91a51bb198478a1cd1f55b9728b9491814148171a1983c5a0e3ee9f786e8e6f0117da6ca9939ff20f1521763fd645c8167a6bad8abf164e670d7281bcf862be08d1421f4bdc0e3dd945c46934b4b04017373850a38608b0142159e8844c8265c7de4e41a74a08c1425f7d2573eaf58cc6fe0a7debe9b9c876cfe865d375cd4ca9a7960b0e31a30faf986485de4d36d3b322f38295cae83f787ed42b95a43cbf0abd56868bf2f029967732da510db2aa43c64cee54e8f3c40ea20431a2933f85e6521254501349a13f31c971347b124b72a3d08af63c172e080a6d7e8e8ed379748c167a423baf16d563768cded2ff3acfbc096e3aa1df3171b9f583fc6e9ad0891e9494a78209135a7d51929ce269095d8c8a239fe28fcfe628a10db9dc92b4e52c3b27a179f7d4dfe12bcdc24542eb561e7ec3ac47683f29c1244189aa11da60263b4719313a4c2d429f04d3184af8dfee7c12a1775fcbae1d7e2d647208cdc5943349933787d888d1c74c5925d6a7ab624608fd493987f038a953cc4c10da92bb72cae80508ed86c9f120ad645c4cf9419749cc151fb4a33149f13ffe727e7bd0c71293d049bc985be6416bb2c757ad4c9ec4dc417f4ae4ad744ed29a61880e7a2db93c8ee885e9d22c1acb2ff1972668de912cfab378c2f2799fa0af8b455f9e3525293b335b82459bfcf2cc3794eecaf68aae72d025fa5b92c433b9a28f673ac64b35d1ddb6a29f8f1eae5de8bd82aa3ca77410d7e1429f9e644f6e1ccb116fa153e2e74d93da3fd4d542a7992785e9491ab5cd42dfdd1e232ec6faa358e894a678498fa76c62f40abdfcc9a0f2c84e7e9633bad4a434c8b8161dd18c4ee7e4b12b6a857e4f30294cc5c57f57466b2977eaf3cf415fab42bf41e538199d56678e39c5a9d026c1620c32ee28e5f914fa4cb2c9f9662d964752e8a4249796b9478a9644a17993c398a472a0d0c73ca9c4247fbf49394f68b7434bd8ea3451dd317af7531ed38290f9d5095d580f6b2536a1379d4d2f912e6662421f347b4ac14b52a26896d0271dbb34f6f24a68b4b6ecc714fc24f4a1b2fcd3e40a21b44742279c5842cae6943f4b476863e5942a3fee780c324297493cddac996735a808ad88cd732d9fb46f88085d5fe60fefd21da1b38021349e4d3b778ce133092f408c3e34aea74aa1a74549de610142e89450be417b38086dc8139bf2e7706d1508ad896d51a234eac9f983d6d37ab7947042631ff4fbe1a76292a4ad353de8376ccefb67a682090f9ad3d952c9b7eca0534d8257ccb1f2ccf90274d06b926557c02c5ab520b6bb3fe5a3e60a90459fc5e5425914efa80262d15c5f09b2c41613b47205c0a231e916374952cc19f44801afe8672e94c9a932860ad31914e08a3e4d8e7e39a5a44ba55c4905b4a2b3307256b45dc2b5843949d5b48a54b4892a1a15b52fe56ad14f722afa331ffd37f1991da3a2cb983f4f96ec29ba1e13d5c2ea96ca5953b42d72435a92a252f4e69ee409d19ec43c1129fa1fcbf193ec23474e348a4e0cfa56495f26f9bdc43d284014fd8a1213c3c2a518cb50f4999396a689f961048af3f39fb0fb9a4f349b829c12224f74622729bc5f578e0b5127fad81f45c9a34cf2f510271296addb44af9ad2941679a2938634d1864fd12a29738b194399e8d5e35b0c9ae413368530d17f3e3d5da2648ea54bb4f33ab34998f6584996687393494d3ff1a4972ad12625c36490254faf44897eadf7b4849e3ef19f44a3f2a6443f13f3775e12ad26b9ec31dd631c3f12a700487461b1e27a831ed16a0e2346e5fcb962c811bd895f3963e71cca4a1ad19fb09b5b42cb88664dc972f2675209ed221a1125a724279fa90952548022ba98538f4ecb54e2a889e87489a133cf72644a22a2175dae169f4baf6c0ed1c8d112f54f1eef7c490cd1694b515c4736098f4d01856865f62b73e55813bb84106d2c99b1262337c5960ca2d31fa76d5de2adad44106d92c47b7613cf93ac9240b4c9ac933ecb8d9b55028836fc778a71f592b0f81fdaf13c173406f9a151a164a8d29774dc247d68f48caa08cb263ee6f0a1ad9872890f1f4f92b487f63c35a89a9c4c88f4d09b8e53e9a17b7ee6a15739492cbf180f9dffebca5e7b8736ee75a69c6487f6c49cb9bb3545d16a1d1add723a68d179f743874e66c8b06c829705e7d026ffb27025979f54caa10f4ae5dd0bf72053260e9d205ac793ae9282bcc0a115eb395941e834cb9d37744aa49af896dcd049d3e559e5d4863e2c636bc59e0d6dccf134d7c486f3f81a3af1e4901afa303105dd2e4943f326bac5acb12cae040ded289ddad99fa113c74498e55b9273ccd028b12b640ac293249f32f4c1ca24fd8f27d4c264e8754ccf4548c7d06affc7119e189a4d82c73e31260c9d6ecd6ea26486150d83a1abf8972f7449cc29426974f7307aa11f59eda7fb2f97b875a13f95c37f25b933c81473a18f9a4d65af4a29a62df4a32a5ceacc5a68b7afba4d6cb3d08552f91fa239163a9da62b537f1413d5bd42a3e926f794a87a3add339a750f9a336fcc684cf620433e970a2163854effa66cd61a746ccd96d1a90c72bd29b35c53b60a7d38372522ab264eb864742ac5f211a215a2dd52a19165622aabfe673e4da1d3ec592eab5676c94ba1994de24984c80fa247a1cfcf9b356f97968ba0d058cc7934c87b56113da1ef9354b789ec50fd8ed1aa6879a92464a9983aa18bbabb6a427be3cf2634fe3b97ad2613fa4c37d35f39df622ea1bdf29cd1c398ccb2514223830cb2f3750a699924f429ab93984a4442273ba9e63349124e353d4217835730d360962da6466864922aa64c4945684b87bef24a194ff449843ef352c7d21f425b42740ebdc15cc48918bd75329317235bc442e837a5783ac5937b150d42ab3d9b43e6dc5e420984564788cc97ce399e7ed09cef6578ca9cd0d107cda8e91ffdb0e0b9eb412bb27e62522a6bc6f0a04b25832ebdf3d9a3e50e7aebe819b753c59c490ad041a3e449118f7f165d92171b4ecc4eaa9745d739224cc409e115148bc6fc7d3bf72541438645a3278926ce29eda4dd577422ab5274ecd117e43ba266a3ca6868b1011a872348020d57f4eeaff1f262f2724d2b3a357d4246cf537d29ace863d672c68bf8909555580da4a18a46e6c25452522e4d4a457b9a848b1d25272142459b4fd0293fde293a4198247f53c8f8ec4cd19e75cee5ee3c974bd1c88b25ebff491321165234d264ff7892a3e84b26f953cad933c999281acfffe14c190aabd1050d5074e656c104ef139da5ff991253fc4e95f144bba193fc13349d68f762c48976b4e86ef2249b68d6b3cb458592734d34d165c668927332d129dd3116ccf2b506136d924faac5932ddfe34bb4b1257c282586ffbc251a9d7193bb62ee936f25baeedf3c49126745a744bfeedfb12be6205e33894e994951e3e2e452424982ab565c5089445f959488d0f0150f241aa5d55acb643c87b13ca27d3579f7cc56b2f23138cf45b9326838a2f72032f75a68cb229646742642c51815f117bac288d604a19412ed93dfaab2887e4764d04b327f2c254534ba74d27ac14f32e5398d44f426f9ff8f85d0317244b4497643ace410ada96cc68b31490bad213a8bc947e72ddf70b285e834e813b1f9cb3b5742749278b953eb6f3406d18593a953d85210adebae67509e3af440342a88d02c1f2db86c00d12699329bcc9b529573fed00915c3c6963c41957ee854e69225584a1fbaa4524ed5257ce8f79238f9c3a7c70bf7d0c7e855e7b81eda91b1b393874664ee4b69727868e46249df570fbd31bcc3aadda9fd5aa11d7a5161c6a3c6ca494a75e82b73896e3dbfdf263a349b82d05319333af773e84c8eaa7f49b67eece5d0e9e87859ba2d2fc82f0e5d8edef89444eb3f250787de4b92e1c9476e12a77d4327c224c14c36ae37c9d170432707d3240997a6a4181f8d36f4b9e43839570ea6a69369a0c186be54ee85d031bfa5b986764d87cf144d7f37a9864e708d39cfc7ef1463d2d0c54e3121fe6de284686846cf2ffe3f439ffe4997a43b08a5638636f9e666ea12e76f19ba7057d3253c6f7f47867653e5accadd185aef203f26214f148f89a19372f2c94cb130f449d8a0e5cc357c5b07864e935242769abed0c5544b96bbf74227e51f994b0e324ba6d585f684ce73315c4ebe880bbd5ccef3de4c4afc2d343a76c6ef33d1421bbb93263f9d2c347f4a8f4e3163a14f935a623011a2e5bd42efbb9d2dba67f465c2e7dd93191fae19fdbb88578c664bd30a8d36152ff9a5657471af476436abd0f7c87219999a5a1a32baa4f753902f0d2ab47f26322694a6d0ee69ccab3b9e458452e8fabc2bba49d13bd088421faae44aac5cf24484422b5ae4e6cbf84d3a674fe863c79262f54b4f7736462fa3c2b5c5ce9cd0f89ecc5c4ad2b88e59137ad390d5127a96242526746e4185b55fca654a4be8dcc49c314f5892544909ed898d1bc65244b99f844edc6c6a6ad262f81d0985c6113ab1b2478c3ccd396b84be72e3626cf2922c5e84ae4d58521d6345cb88089d24a7afa7e121742ab3e50f8d8ba844b0a4226130128ac4c180388841f8cc7b00b31308001838240d8582a178449235e9031480035528203a302a1c222018161418100e83a15030100a07c2c0701810060443e180303422b1b33d290560029d4034e01950060e02f2803fa721f8567e981a9e0a2e5cac220f451a1087fa547c7dff723a14334008a803a6ccb45ac05ba825ac0c6710fc99858cd7081c1d7f045c79906f9635f993ebd5ccf68113e74b06300383002ed0dda17ec943821e5827420046369641d510106485f2cc363605fc423bc0f9600530d8b80ece29fed9e0020d30dc9aed28e32ce420cc4e4fa783c4d8c4780b38f213b6cdda9135601d2cf882f5804c4e84fb70e59283a33f38aa65a020d3cea0edbc802fc6df12fff788b5db4165436e38d060c521345fb904cb06e4a0e4a53389dc617fc8efec1936678a44768c2116cd3074181d92aae4f1e721e5c0527109cf6512d91bfcd10bde156ee0aef959fa51bec25b9bf65f1e0c039f9cfbce270731f0080c031c86fffb405840a40006494833b10dc20b4332e33236f4171e0f103b6d2a281d082ae096e580a28b520d8f84dbe2f70807b64879284b600468e7b054974810bf10554aebb94e02640fc803e8803a401f70817d7526641420cc342c02fb8837e2878acb442e4ecd1c5c5f43445a74c1a8ba9fa2a678c800038a8b23f9b05b4004e0d45f6acd659c10e1c277d6f907ac8a6f036ce343031c2e8e43e6c902bc9a761856b030c49a0cfd2c4cc1292bc6570bc7e3df90a6c145e80c0b08035c84a4a23f58ac14fbe8e4c4c001f7d398f9be9ae403f1d682dbf00bbcc5ff5e5cce808c0a0100d70532465f1983ff831048653d314f81fb26e3f364301d9a40219c6be7cf01f8c0024e03a3a09bc026101a6006540653ecd3ee418fe1a6b0269c4df813237daba940f27e9aff8a32b8883e0332cf7f5ed7007f9b0e2fc101635e580eb61a110b1b0338019cc77ff925c8e37f19813363d670ca709de7b0de9113047966c32ef982c3ac26f5537cc0a838c1cc96e31797351d0bc2a78eb0701956ea83ac0f0717e574425a28e29abcbba595cbaad95d08133c2e7389d926618562409b00fb6fac8062aaf7051f602f4a488f80fd70287065225ec570101bcc0d3e51ee8937afb929b097e1388007abe86bfd65a7b12d15c275f33e0f35cd9dc31656548abbefb5e00d02a382026413080da01d9402a4034fc0721000e040f18e1a28094e12972ce91f8fd70725ed211044113b2fd180e8549fa28d2d3e19ce22247c5be7eaf303d0960fa622dd7d413269a52da22a81be14576994e2e0c822a30559ae96c14ab4da070cecf0f961253fe42c50639b3f69caf5d3c07ec19c25b9f767243d56089d02431a976a00216609ca8f3d828731b732c1282718572b1f75d30c5661c710cfffd13b82cb3e1f052434719d164a507b73c9fa58db9006d70d3dd8c2cbf21301ef209c0bf785e560638f9b8f45a85c99d995309633b816898b6e2652aa5f6b72c70c6f4fab80365c1a92143065abcf7a78d1171179c06f5899477b6ad982f58515ab8e4c20193c46fc1abc1caead01de09f3f49a0c9cffe870da2000c4fa9461390586b171f96eb365e903aef3db69ffe6b0eaeb752d4f875e16fe711da13a3ff202d2785c14feaa3fdd85d5ef67f9a63e3888002d145c6af1bd2884e2a517cf19bc35301ed26d9aae8a86d7722ef082e729874514c09e766cc18e8e1d7e8aab1b7adc8b1be30f86c70fff6701d0209b3077538196d26cdfccd0196466c14367dc23c67581688bbcea0307f5e1c802972de8180e55b8fadb29adb20f87b41e637c18b0f609d650bac0e67c5ecda2fc1f0753fcd1931c0e4ccd6c4b225e73e3a0dd2efd5e3e1850abfe446d8469e33ffdbc7f82b65c63841e12bce0485149dae47d9ec857e502cc6e4ae1ad0a79a0a4847930efe162386f752d4f84a14c000c03e1993426e429c553edda9c2ad7d82cc56e32d3513c6955d250dc01cc5cdb92497166ea1bc68a1da187cb43f8e9ae06501ca3801b2132ed1fec5576cbccd1fb76e2967fbab5533ac109c22c0589238d07f634d550028e81f4a99e11b9d3424c4f045c1da5b0f2e0563c85f63efa9e00dcb632cd19e30d93e33bc3ac3e0d421fcccd8c5cb9c7b3b2e1bc82b20273615ba59950ff10d379310bb0309a6b0d57192a233c9ed3ffb870e520862c4cc9bdf9836dd71576e7965a3de14abac5225ccf73b615889efe658b4650af3cbea55d001b1e3bb7bdcc582bb607ab6daff090990adb2b25e06ec5b8094b2f910a244f790d21e2d8a89bd9466a786bf45fc09a7f4df3c24ab8731a08280509f1e8c2e28f385e5daedd1c2eb0b42e6acdb43948fd7000322b1a3f45d95092c5903c03032f08785e4a623d5cc44db8b87e1720ef3cce79aa01ff7cf0bceb891d2c734a33786dcddf6da3c0db8ff05870a552e9572c92f8a4dbee9da5692def041ca36c3708943f702ce6ebb1a108f5e5d5c7fa54c696975812cc543a99655602f0d56c25b76861f2e672361f11094baafe4ae6b6c8f2cbd0282def8864011b3c935ced809315cd27db394b001e0eeeaa4588cf3b4e9c9698185a5849f5f8654c03c35e813f69d92e823dd653172c2847d014576a833603ad01069af40cbfa1b09408b11db0b0c536a0d6cb96519bf717f8259e2a4d8887bcbcb9d0190a6fd22f1daf248aa8f5a3f80a61cb226b9cf029184f466d8113eb3589d3ea7ab7f365c9a1491b2f001f0c28a13631e9b620e5987873fe7e77c330fda21b24f63ac72c79fd42406bedd59ab8406c869bd62cb1b04830a69f85d3514eb57449b155f193214255aba35d14042d3d4ea4afd707150dd8e9cc9c18661d83b425dbfbd0f89ba552cbaa164ac61f0cd10a87a289f8432a0f4ec26b800346addb1c711488bf3c78c9072de46265dc16b616468da84889fc1915c648c9035432257dc66c620d0b054e194de77c82fccda41d7dd3db1db099cc25e0de2384f6c34200f66114c941e4209e3192edc8c04813a723e937493c2ca6b9033476002a2160c9884b61133b3dc269c9edaec1d63be177d84ea135b858fd63721639f40b6ce8ad9a6ea9283772c36fcd09c46d28b242dda517dbd12e813217e0247f9cfda779af822f8aba8373140d4ee25745fa9065a8909c5101458dd2e6ed2b1ae12253180472ef5b3358b8c36e53bdda4ccd95802f1593295db81974590c090195579b19d97de92c786923c952b3bac998f427e0239ae83ab8239e3b3e2d9a78427d92b7e8b2730b5c239ff76a5c41311169b93278637c1c9787abc632c99ea62eaae6c6c1b94a32cd0cfd81cfb9d8dc3d0ea481281e1d930e866431bc3230458f7c94047cdb05fa426d54f0d8225bb708900b2b042f37023c1c91b738f038381c6e3a1cd0b1085170623457399f7bbe9129181165902f04958328961e62d515aacd803e71b71a9a9da5a9671010231dc431779cfa9b56d15908819a8531b80de5012a17dfa3a92696b238d7e31c7eb997a98607a819e4f898c6b4308440478ae113a0f415f2f550341f4caf8cda47ee401965226b632a7357d39543a4d6933980a1f0ce2863a1e20163cfaa4693b152dd3df6f932cb15c75d2e1f9de808e6a6e130fec52c4ed92924ac3ba649ce0b3a02afcc158bcde0eb6f83cff2bbf349d9a899c32c0bf30911b21b17e9b913d11cdbcf5db07c426ce85239821634a0e4930b5398b07bc813c402ffd57a983e07853f7b6816546eec8ab62605c7d1f57562c42764642889ad5ade40ae98b228918d0e9ff485193f1a1e2e03584f1800715b18407363006265cadb03ca8e73c567114e6c2960ddd126dd00672c6e0a8768ae06806726e2759a01303872bc265e19dae5da7df9d40142a0e8789bbcc169e726179f0f7d7d23ffa1c696150d056678f5855650f14aaea82fa21911579271832c44c31aedb3b0ca359e17549da46d2c325d64f87b3796a3312a176ec642aba4f0b5be2ec40d574ade1204e3e8760789c4bef9c3a7ad3a001ec02570c392b8a78afa6529c96404a16fda030402c76b20287031ad94b176aeeb460991b360dc811d3ac927154a7f9e531a9b1cf3f73c462c4c7710fc4345a32f5b27fe0d45937380c78de4d1d946c8e1cc1c863a4e394d8c302d5d58446ecc6c7d2dab432969818c79bd7d49dba980e776702aaaaae9e2cf60a63ac0209a386bc5477db4091d424410202d51ce1fbb322dcee700b4cab620dff3918df0a9598dfc71c175eda14343d8de53ef065c7d48746da39cfd973b47abee899b4facb35b565c2f8e963865565663b057041581b460e18d06f4a1c3a04425e27809d4f8fc99b39ae4771cf2f2db02f89f624b80574471136a2e9904473d2863fb14edc67c28bb13c3628775346353c65213c994d4bf29687353af455463908cf25ac9ba7d9d8bac48f21f41c60df799cca2c3b052e51db2ca8cc1d76fb42db2e4199d3df84f0b14018049d89ec55eed5ef85aeb690289e8c772ce62dc21a0f35039e4ec9502d80ea8f79788be5ce4c8d08223dbbddc0fbd02901d6530b97c6c66c88c532b0938835c8ef7c0409c75bdc19f48ad34c905ff4a45ea6b03774943a1ffc9315bd54cc2ce2a5ad8028b886baf8e85f324cdb1302a3f6623bca5826e440e8362910a9640d388a527f5a71fb2e4c52ff599462ae875320c5e3478c3d3cb8ce077e798bb7c55c1a11bf8dc92af26f0c3b493998da2f1c7ab5adff0e00b9d34f41c3c2fb046a5990cc04c5afa097afcbdaf512cee75846b356215091a1d6ad504c652255913601a24d158576069f667050a8e4e194e11853988676112860c4a59c7d58a3c862687ae041e61761a6f32402b053eb7d206a94426f50f10a4a395325b0a519cfef5f47bb4eae811a02dde1d11412c7159dc2485cb64a98605348e9a444fb4217219aed4553cb2e6d9c0afec940d32a1e71f1401003eaff31db956094635897387a1eecc5d5080a4cff66fcb385a310c1813c4df86dbfcaf755089f3bf3903986d5324de433d9c8d62fff0bda72027b2ade96fdfcfe9d81acecd38d4b45d209a1ee1728425466f67ce08cadf0031a0ee48d7b3929f9c1f8d47ea1a1e9793e4946be2d5835a369402ecc6cf8c6ba4d47cc3907ea191ed90a6fd98959c35e657476f184d16202514c1727ff7b4448b74c103fb02ed30dfb004a1f9c6ea0f8629e27b90d9d9cd71e39695c088af2a26b23a3c24470350261990301f3a310660c0c50fb884322648896512eb3f331d4529a952b3e6e53049f472f1bd54ded6f9ede53f187d520301bb5fa038313ba9429d69762b82e6ded0fe702de62f3b2add381ea990fd52a90122a8dc7d68d7e0da48ca1d67c87450e975a90abb73b2605e6f177db3270e939960335ae0394cb8866a8b2139d41c2bb47271c517b2485cf6ea08c328a0c2134e9b2d33b9cb899845d67969e8ecd47d623e7397aff75e4e3b9d0618894339cf9d2c4cc241f632e91164e468b866ce63e31e7fe4d0a2f1049f279fb5af3ed5caa983a3e612b0404eebe7578e3bf4942c9b143ba22c3f70a69d4a242e899cf521f69682add8f1bbb25e1764433a53e85c10976848e887417ea47a2b0e69aacc4d09ac466249d586ce22cd62ea79918f10a4138a8f0570cbef0e68974f59b7af05ec7b9cd8fead04a4906e4d5c2ddb60907e9c83f68941daa836c3f29100117b262b6f4f0fda36e36c54211c9a455ebc704a233162778033d9a38c2f0a203b8b8ca9932ea7339cb83fa88ffd076a4e670428770a862e8c085df13f58307965c3ca9593f238d4c2953152d2d1afa74f8cf972a67669429a46fd9d697587ff07b1ec6ce72e71ae9ae09f48902e0c9a7c0c949d14c5a47a4b9a158f0fd09c73e83b1254247b212ab40f15b8f8c34607fd5e4a3fa0d2c0aa4a776435c08f20e18b259ce43eff097bbd7ccac823a54cd6e2732a5755761649f9ce62fc50b347982b19e41ba422c16c459dda2523805e7870279911468f4414d0717973f7759adb2ab39c451d32db9521005285122f126512b313c248fdbf7189e551d14af54f56ecda852cf9fda90fb2d66abb54ca56f83415a09082c9115adae7cc8dc07150da4e3352af3c05e6a0aed8b6a5ba07f927d8260964299304e3846a8a9b04ada5ffaa2ea92db8f436a73c0a967f75fe2aeddd09722aa98bc7144588910a68144d9346144826ec2bf82b544605fe7c4ea503ad1e8cf8e30b8d37f3f7d200c1497b9502d70ec72b0127a4bdf39cc6a0b7a6c97f64f953a24ee731beca9c40007cc5f612ad13ba961d69de4f552c2c41e73966e8cdb73e7e6efcc18d21860e32b334061c19df6f245f346abffa513fa36c730f3d79ea3b674ab73ac777a60263e86eb3cbcd24d36194fd8ecfbaf6150368654a14b4d70875aa43cf3fd1014a987faff1b1ee4f1f106ee8d072bbe49c198dcce730be55e8823854110a93b0835d7cd5ec81fe7b73980b98c7742f623a53080fcd7987d318a1ea6a4919e143c4c11aa6e809f9344bd4ab0648e49f2bc42f87b4c60346feb9139dc37c5b39543e13643c746976258f8e9a1aa6aee1a7f5f5bda6048c9ff93d2a114fb4c04f44ad76917d269280c15cc64c5e011d40157d09d0be5e0e8ba22a235457e7ea8b3ec12b78b837f1e5687f625c1a6d44824e58deafdda469ced512fc295fdb8320c29f7d7566e256d0de3c5d50de9209b71e9a323390efbb7bbf9a818693a550302ab26372dd53f021a995369c08e50640a45a903acac1852a289432cc772ba5e971f0b37359baacfa423e028a5690d4c9496cfe307aa94e7d6e0a92c6e073f5af1aa8557dc409793fe8ac426fbbe79330dcbd4683e903e5f473bc1282a5e9988ae4967398b3e02bc1ea30fa718d33e43a8482ae25808b52eba28050ca770204e9a4ff371d28066b24e28041b44969e5986e56e34bf7895cbd5505d2696423904de5d545a5b6b200ba2993ae8e04181d7a756d633760f2a87fd7e58abfa95e8746044a898f89c5929c4108aa097ec078de2a4cf7648240256167b18d271042f6483b00618023df2843f83990d960078a5c7aa03696908e693110f92a23b72db7eae665cd7a8ff00f55e3c083930b330cc9b0446373616785cd31172a6842a960026b43c869759f17940a499ef31a0d22b294c3eb423281da48f88c984b7aae85779cda5bfb2e653357d142c7a741d7335259c7b8d0e331b94918502f30d51f7294850be564a9db7b026920f6acc69efca4d100f60b631639e844c859a0258c38170471614b19d15481f42a92b5f0f6bc5982d8bd430d9ed7922721f56126ce370d61d72c6ea0c26be6e4c46a6246e2c01e2b3fe0b14e7e710311b862a2158ab40c52c81d37a8b710672cb8e2bf2fa4aaae0fbc7436597a9b43e9cb7e3528e00f5f69f8065cdee4164b26c4ba57a039cb5aeb8332a62f0e033e6a3205c1e0e80fbfac5ea1589cd19b14d6873e8abe0a9f212a180618a04b7424dd7eb5e91670c8b4b4fbc0979cd3c317d93700fd81dbf2f58b2756947d134738ddfb66673d78b69495de39388fa819f040ebf3769fa15f784a94cf33a9ddf5518c269e8714ce857786bcd65417bdfcf3813d81ea026287cfd76734c2c00a66df183e6df5e5ea568f7593437970a59ba08cbe2783b77ee0a4ba7a871e5989e65a7214679d5775ede268b307e242805a7981cf4dcffa2d9366c3313e6bfa9552b46ed3cafc690b1117417cf6a07acaf4c2cf488cfd84cac936352d66257b3bde5c577e798082cbd911fc6921dca83903398f11b70a1568536924add15c1b529858671f895509086d4193e34158120ce230c45fe1eefb3d0790fcb137b0bf27d039df2b592af020b1492a6e4c4498f2a7b76334f387d1649771cb721b77f6e4c0bdcf40f94623dd4388b71811c41443c475d5b98b90889887c062d02a0e501e4e0e8139313f3af09230ee092980c1db2873f42e284b205a837667b76f26407312be361db2ac58809eb904f7e47d349512ec8fe0cd72c3688205ade88caa3c223e6c540ecc12bac4768cd9199dfa564fba50f7c7c109c684166182e3717f44003cff846ebc218108487ed56d0b12f6d23727a9e14d74a8e367e68889c59374e9e7a927cafc3eab48c1d6ea042cfa78ab6f2a6ce184845891bff0d97787426461a5c21c1cf1dbbc95719a01d16e3de222fecc067791c23e6ad2aa29a9a26549ba29fe2707c0892947dc1adc16096787f140520722c2c38234607670d52dbccfb2ba9f27ecd2e092d78c5ba99763ddb783c056bf41a158b54aada305add80515c9dc8874abab9d000dd401b6905c2dd0846da2f591366749e1a2fc971932f271585389ec7691f7eaa147903751456aab64012f57a1ba219f93cda64070a12eb7c4ab95d9c11e5e1d62a9384f336a6d27713148069d60babf2707cbb30113b22ec2d3423f029fd9ff67ee20b094fa80faf68c4ca4bcd0223e67e6ac3c37f240f311b02c83d3a9d663824d05dbdce2aaf5192df7c49c7935b7495b6875ac36e97b6bffd450d93cf7ea7ef75984332c01c4ca02d5199357320bb90b146b2af456f8a1bdb05bff584f5034bbc941cacce693bc92bc9d6c38f654d9600e2630b004a4a5e90a179b548f315fe2717304644ec3d642d4f0022c186d3cfebf350c8dae2ba6eddd0d0d84f36462eae397ec7db5859b59c8eed3a6656e784165bfb1d5670611f45f01a7e3ec6fdeab7424a6073679a03740cf51fe546a85d51594889eeedf9841e548db4c78e99d178cba0c1baed456596102414549387e7514d19a41bd01aec03bcedd95f85508dfcf63d9965060a6e9f403ca4e12d0ab20f72bf0eb49c6da2509472bd91dab4d497b955c9de18693be87ea2fca985d2948757aec9b6a042aacc92ec371f386437577c1e8f68eaf8e74dcfa2a6b103ae0bcde62aff3892fd53c967558ea5bd757f602e8e784472e6feeac04a3bc7c67c1c209a39d523de107e11eba092917f3cb8c2990e8dc9ea540d463d252da664d4ce711155d238a30460f84fa85570a394a02e6ed4fb2cc539bd172fbc6ce9f9cc86691d872a57fdb98ac35f4b525c0a6b55c32c2daff5991c904cacdfa788be72325134918df218931701b6244b023b0851d20920a5a5a9155b8a30f47f7c30d4bec0d72d83cb1d9a56d66e989e08a3a652fff651827f040fd32c9c376dde489a955f7aec436f907476b8aec0de9c0a4f330d0c212b09806f65aa34c87cbe93046a98f2f1f733574fd0849b12f745b620d92dd1bf1553e9f7d172dd0c518bedd515d2fcb4c48a004ac838644af95c141394dcc5696bb3645b19e075e0470c29b0664db1cb834be70da5e45b43423dcf4c81e0149aa053a3aab711648c5f8af4110f8231d0c9ca063bfcde1526038a8cae48eb03308631bf733cdeef1dcf5a6267810864c6aed59749e4e0e822a31f49c2816e71d190af08c92f0c07d9b4e7fe40d90692fba7f295e5570c23099107687ce624d72288cb7a3e57a453eff899012d166a14328429434a4634ad67722a7e2d2cba2c0ac0e6293140335e09e7a9be6423678fb6f9321cc9d0f86a78a8e995396e9a1ca1b7952825e2d98555fc5ae8a1ddfae4c8d9569b50a40224c70a6973af4af8f5e029c171d7251680a9609f012df821ffadb3e09679fa6cb2a05ea5d5391973319a98a5d1b72c823cee7e80690b0053659b94cfc50d4a443ec431f0f464a0b603d62e5ff02cd214f34c6c57ba0d34f0a76b9c144e2df123b389fb020a9c89d55b6b931f7a255db8a1860313fd83bb5570d5f9007c41a63ce6985d144cb17035ff77a1472d7b22223ac05b1231e5bf94b110ed61e4eb68ca822a7f91d542110d62102664e70e661328ee241309384fba6c061cb45a689b3d0ece75a984d22bf590d13039a6ad3509909f897426e85af70a5c5e00e03d24629b88554295b1432537252de072c6494330a0992363d1ab10a7d6d09f1cb3262343de4f626c64631245ebdbc2c4f16bdc2ff34c88cca04732354ec340cfd67a661c7edcc7096c46b57fd0f111957c8025718e4d255d46419adb33ccdf8d236d29610ef5af62e9bfc3f83ff06f152cc96a52365d800e8561b250821071af80a7629aa3fcdc616ad341846aa0a3d963c159f667b95ed1899844c38821426881a216cb0ee57c8170012e42af4b48b221f6f6e06f00887ce5ae405961b24fa11a3e60042a9b1dbd136cdee8e20c8f6b74610b1da17706ab2b6e91e1114206624cc214aa22e42f48b88680d3a82e5d108f443a4d238bad37b869a1b1c3edaa88d745779f9ef8565a1a354e4808bc5064f116e73b0b3c70979487e3a657c5fad945385c919e77aa816e42b482c7cf813108fc7519c703c37a04573e32cba917ed851f5294fafe7265aac1e556f0bc0c1626cfed07d127b65c91d5a5e674ec3ad3c6912e1c0b1a303c3a055c796d92c9cf02ef9e112693408b094c541fbcc2f5cf99ffc49421515b32acb086c6e310deed5f3f467397f2e496b902f398197b0ee4bcbba486b3b59664b266bbbf6b0e89ef025ee74ec5bce82088d7d8c79858d16e7041b8a706fce5eda4eed25882bdc655ea2fb5c514346383d1b4c75702c0342072c81cf42fbd8337fb22b730d361de709d6a7f68b0414ac573131bc6fea3e9c838e133b9d432266dcb14c71fc5f3963963cface354241e3fd34465b3b3f475756838071753c5e0837ea91c70d1366fcb12911ab8741b4bb985838e557439dac18f372b0178204f0b0ec52ad4b46f921709ddc5219f4ddf25b5f44548199baf0d9306aecccb9668ee4a302a078f90b8f415f975d04736c88b33c66b62f52fa9e8a0d926e1c6d70accaca964caba73a0b8dcbb09a151211ae9103ad289e76eb3266e1d6000a10835a62f2f8a65f9907542e683abd0e6b99e6fa964c2190fa3f3a3421b539f75f6dee546525abeb9e65fe2ec08b95fe319f63ce4e3dfbb5bf6729c069c4cb326a1bfd1639b5a963a9be3539410da64e2bda94aabad0a18a132ee2130508eabba9fa0e630d3778c5a560d6b620cfa4d9aa3d03f23c83a3272b31abb6bcc93a98b5651ef6294045d0834621eacddb247fddf0c179a473878ed833aab87c7e9b90cff917215d91baa79e62473143b05234b2185ddc17f6914632da0c8fc62aad56a7184420d2a263c0650d3921de8a865066cf407936ac352711a96e8c1b4c42396511c3aa52b47a32e2d2bca256fa1fe441cb8272d4a51abd889a785139f82f01aaae7161ff701ea07b7329c8a54cb1314b92eb57fc8240093d409d5f9a312f103c6a4345320377196ea18deed0df0856b60b0b68b79e88b059b02d97438000d45ece27d26e7e9e9b752a6fb0e9a8eeb74918c00253b3656b7929f1817f87e070940afb1231e4a4b6fae009a0df1663d6618baf7e240ee4455ed747016387d98005a229dd4a4324745e5f0b6af17e9481cf3911536945647cdc4e6db4f186101abb31efe3e42a109761dd5a0377f30bd4f14e6bab8034b04fa3db2a7b791bf9619f9ca6e2199dd0c1348ec65f0d2f48180619d1b2fff8ac68af32ec717583f4c2f52bfdd19b9c358fca51e1867def482e6d7dac2add48cc9517d7c157b06da0ec713d99c1423d7fc40cfdca5a679f1d1841a8d306793c98307eadcff9075c4e8dae975e4fb7d4805f3bc7c5a22d9fcfd69f01b438b08aa1bb977d6c214233d7d33a85fb736335882f9397f3b5ee0cb1fc68414dcdd222476211d6cf3c820a65a092f540054c802e85c85124e33242ffb18c17d7d80f500393b730fa7a09c3a0be903f8a1edb3abc0e210f647a9a6a0c675f085db6b90ce8b3cdc7edc4aeadc6365d23ce5e137c48bed5bd35c9c19eecf88d186a030cd3be33060659022bf9d3b598cae34ffae9707dbf97a550cd628fe9facb3cfe69d0a59b9146498ec3701ddceadd77151ea9a27aba072bdc242fb87a65d122e7225f0363ebe1a0265ccce4b585987d75583837972ee0bc6174233d6886dd06976e6c4ba3880bc0a2b68476f1103781f0720f6972b55d667e48c6c570d8587206dbc6b9268819e50cb1e7724748bccbea1db014095eb5f73be8060b400a4373654df6157800a9c22fbeb9db3c32dad1c8281269d7c7f54b05883d919a269a8a91312dc776c8ac323776beecf2a7e5c2ffed0f9f02857057b2cef87190ed893f79cd10646fe1e40dcc5cc8209d927f9b6b3651e4fb6ba34d72346554a3417c01f73ea006ec35a3a1620dddbfb47a77f4d96e8b12a1bf55714cf33c6d90aa9550f898dc48c8651eabb39d235a5c563431ba7096bdb0f3bec06028ab1db7a20707133cd64932e0545abf82e306768c0d67a19e8444f5752eb694974455a66cb63f816aac420a9d104e1a88f5bc34e3e9f0a2cb16d7814485e2e004d695eb066e07a927da878313868f0876572382ecaa105a9ede6d3d8b2261067cbab5ba23c8e8e13839f35773eaf6c2c00d27aff7d660e4809de427fb45076d090eae8064bc8d41d0f0c2445f8d2b2c39b838caf26612a84a4a209dc939f24c709860b1348daa75f200d32c22a07d5bc5505d7274fa382752bde48012b2835080210efb0dd52f8567b3e62b3938fd7aff113ea0ea9dce981bf2f0c78d86915f6960e6b9c23258a4e0fd143a45af295069ca5aa875b205189e5df6016f235844b63f644868bf858eebc12a432d62d0ceb675e526def90a2efe44516d71e33db4302905b475f9d20fc7a6b8e893c85027b81c0d5719c2f5ffcd0cc5104b8b90a76d7537b366892bf300d7ac085f8cae4d163e063a858faf986219295fb9164091770cda519327ece620de38531ce25d59dff76ed89bd45ac22b0df7ac4530b7ffdc81eef0a3072fe63711f031bac76d2e53af69edf9424283990b749258a868c95009e715aebd1069708c859ab1348c0cba470ec414a5e5610972b46bcbc6ce23dda4aacda241de41de2ac039c904677e8aa446a316d7b61119b7471ecccd50e6b45a2990e00eba506cca50bcc7a4ee9388ff5965f1845112a14879d1fe764ecebd683564a9f38c93c17dd24db1adb7e38c632a308bebe29dfdfaf04eb7cd5ca56183062363417788a05e673c51a533797252d10b19c2c0fcf12f0a2e174a5ef07dd10065bd642cf1a94d85dc6a3933c073a38d41afd0027787a2bc4dba0f185dae6a89a77a7018c73c743999a5785195878f8aa84db27c3ac6310b77983826aeab406bf243e407f2f1407949f11f8513c994dd00d0a2be343b9db87625acf429f004d705f04631951c52c43bc1486297665fa935cb3747cfa20da853060e75abaf5628c29e678d1096d9f3ae53389d39df4b0814ea03db20222000a6ebc62ec74b8011dad677d2d61108ec20a2dc0890b9ab0c2bc85e64606b09f57cc786aae5a6e486df6a20abbd083b2e7a714158284477099c74a2ecb714a23f1d3e1ff301c03e202ecb12c3058dc9ff6a84a49b799013768a9a6bc25c640303ccd785681e94ea4eeae385abdda919a7460ad57762404537afcc01b7b128f9e4a444d24d236b3fccfdfc8b64f9a7c7bedd350ed2e1efa8c92f7af72fc56badae58a86905e236ff2568bd045f8edf311a072db08cc154082f804053d03a87b81d9e938398718c285e9af0a0526893a0b0e04abc9cabc84d2cd16845c1921a1a80e5cf645fa5e43964261aca91cc845b606268ab296e3ac9f80b1c3fec454bf32f7669f33378594526c9c3b02cf12aff7d42fe04258cbc988da908a995a72118cd94db8cdbbc79350d019cf708024961b09709cd908c1e96d3ab61bc88f3d15a0e1f75d0c17028fd04025aaaf1efd524f96805fb1581f3860acf5212b139e3864c8ed1bd74927176510f9165d3b4ce902f2f83f8b26da41c864296161902f7e8699ccc0e6d21cbd1eadb635164d3749cbd6be884e4f3a38768f0416bd18d31165db2e53b80337d49666a78ac5dee9ca2a62d8eb461a1946a701fea984f6b1ad18de912160c7b67aa670559ffd80041d1cd172cd684c1951547b3d52a5d51af394d5ca88ea0cead32de2e7dade8b4aaceec62147863e6da3ea30fb872ecdcc52670136e7ffc81dc118bba02d97b4987fc2659a5e903158578d1404a2e35ba20208669dcfddfe954ea7b4f92d52d2f0ac1b5e0332cab02feb8440f0c3560b46439330f0f0f0f0f0f0f8f39c684d4d6da21909a924c124af9319c37644a29c99464df8d90644158f81769c31fede08d5f90810d0cf80b430c793809955490492f040f875331f8a664b102dc8174da95d62aaa49c80e5acc1353c2448c741d342932fda611ed75311d5e4d33ea5dd133640ea4da38a771715eb3298783105679455d88110a1007f3592e5196a6255f0038902de9492aa52eb92ebf81b0a167956e5b2337d429315dea36394a8a6d3877f96611ee351b1615e49fc80caae5351c539ecd05a196e32535d4751b4cee34142f315de38e864392d1e3e3f7caee3467c8c7c46286c2479fa95419ae5221eae575a34a2703f2548685a4b6f55dc7f05852a3421383b17c6448d2730f110b433298756be8c0b024a1c167b4c517fd17921572f3870e4b79572f586e31cce6126ad38563dc1a2d1b2552eba870e15a97f3dd1a992ab285362e6db75be6d40a6aa15029645a76692a0ba7b5ac22336d8605634b645d4cda35ba7a85c34e9c7ca2d2b2c229051366ea42a40805a8422ac5fa152926b13c2a904c2d898ed1bd0cd3148e794662d8440b31a85f0ac7487aff4d371f859388b98ab0e81692daa170dc594d2721e909a7d331f78d1ad37d899c709013d205fb0b42c5899b70aaa0cae24beaaaa0629870ce1161a5cd334b38e9dc6cb2e4c5574f56099a901b6385f3dd24e19827c8163dd944c229e697debdd0c917c7231ced64c82db55fa77e239c6561f64f45386daec969d2fee2c44284f3f96e89a07a86411b0a30044baa9cbb8a423808cb3e2193fcaaff8f0a0508821bad2aed4764d1970284d45ff43d794129fff50705cd72dbf88098e566439e9cd0bef7206171c22e4b0c4958001ea04189d2b72c3146b983ff46bbe868d917e9c0603392b499834e74e9a9ce2493e41d07c6c971016e704c4b79dbb28d0a600345cd8a88b597e6ddab05222ac4ed8a1647d92c257f24ceb6cdb348c47c9dadff75aa65591c54b8e6145d9b581c73c55f103329674a5a589cf436a48a9e2276a4fb0a654b99cae70a4497b294717525cedd8aa375caacd6f62529272b0e419d48924bb88add94960c25830c125520fc94491b35f1236fa9386bb434fa7726f60419a8e86254f4fe267fef14bf6be4f0bcceca8c9bc29a0de1235808517e538ad3a490c9e42d4f8a424cb2efb423bce44771b61efd96b19176f22e8a43f4b5ba3fe115b7e4a13867300b41a5b89aa937284e2a8a65b5d1e6965b7fe22ceb1567e6675450224f1c456dfaf27f3b719c6842933ea55a9a623871aa56f13849d1acd4bc89734cfaf7d195cd829a385804bd7651eccf429389f35fb0fa4b5d31a4a4c54495172a2b5cc6185fe2385a52b288ff88955e4b989410e2be249538cba8481a944495ac494a1c244dea9e24e7492062d6cbe9e5886fad24ce9821a810f248b0aa6a13446320611499a26a953eab1e718a1a9624e6d511a69090e4eede88d3659f5841882fd527230e2aed0499a153375bb8887ff6cfc28c7e47122922f91022250b9361ee4dc4a9c4dfacd7cfafbf82886365887b325888c8213ac431474c5a3d9145a9ce10f748b4a094e94ca3ce42144c8abce815429ca297dcf8c610376277436b1084133b972cbdee8b6fa4c2602f4e86208e5bc1cb4547ae0d83bd386c828c409c3393dacd8d18ec4d409cc4c45c46e9ea492dfde1107ca4c7fbe473dbda0fe730a5e316832635651fce7b7731a83c134658880f47f1526f17ebe667ea1e4e6ad4db64963535b51ecea5646c6cc49987d3fae5f291e9938107c3b45582a49060b9c349f3d465ba89bfc1e47638e88be239d7194d556da3e420a30ee74b41b72296a94977d520830e275dca4583daae2c0132e670101517564aa47c6ff10f5c608b94038cf60ec8e124d2694a21c6b628496b519011876334dfaeecfae37d9d7f7cb40e1c5ec0e17032ee2a5f628e742a5db000038902fa86834a7b9b8949d84c2c3bb0c3254081076cb1c5072ea023078e084841861bce7edee7a6f3b7e118d64de628f9a7b2335f90c186835ea650a5b69f5a6d2a41c61a4e3275c86c72535f2e4d861a4e71a5a934c609f12d54c608196938a6e6d2b96d4b025b3ce06cf1802d20808693d490d338bab933a66738c8ef0b7a92e5e593918f0f2ffa05fb05185e780146172ac830c3f12b3676978798a8de156494e194f27f57dc8ca334a8c9701e7da57e4958cc71221f1f5d903186a346d4d3be8911c3494eff6e583f35273361389d925916d6f54f7426030ce750ab23f4fcd2ac7d36aa0c32be704a17646dce74aea2688620c30bc7c93dd173ad429031eac26135263112f34406174e3d614355b4c4939174432b6520630bc7a87979a22ade454b18385c506020430bc798986c17f8175eb704195938e5d97fcd0ed98c195e24c8c0c2d9b448b8f8a5da3f4969041957386fdc97521f7135e26a85936baf88242e3aa4955538e9ef4a936f5e116450e1945c523e131f1b4338a770ca9b26d4e80d1d2ba9094186144eab954ec498771d5138cc9be504fbd2ea19430c0832a070d29ded946862f303194f38c5dcb45851f2ad76eaa20b1c2a70c249ef45fe6bda0619e3261ccd65f449de4cbacd9a810c261c4292ab143a5a9964b484c38e52bda04a1fc850c229769c0857d55dc97249389bf4be9ca59b45cc031948388b6a665cbe984a56e408e74c4988a0d515c7f6d2810c231cc49514a5f72e1d4538954cfa32cd62eab8fa86968d1d8e02229cb4ef6ba553dda1973184734c8a8dd1c2e5ca6c84708c72db173ce48b340867cbcf9882f69b989064389001849388d147d9049527b493f18383aee8253f72694657860f4e7a262e59055f13056820a307288307a7982b8910265d193b388813763257969873843274705013f4dd7f97fe55282307a724bcaccc43290307e7d96ce30db591634718048c1c89326e704c933a162ed7ae6f1205326c6062d4424d1083162c481c316691c8d0933974793164112316674d32a638a1b1fcd23a581c5e6373c4143b2b950721c62b4e5f6622379e737e22571c4f5cd6dfd36a396432462b8ee619b4964c4177aecf8a93522656a7adfac429ade2dc7fc2d25e6f551ce4c968d15915724f3815c79cbc29a717838a830c4b39a2e8f414a716714ba364e9b6a598e2f035f2275ff929b34a294e16d765448a61d7f4a438880b62c4bb3d8a73dfbd659e18665264a238a99cc82da9be7e55c508c5295494f81b82be0023c7dbb8a18305619032314071146d97f9a4297de298ec644e3a19267bac4988e189e359c87c62233fa4de9d3889695c1022069162843871f84b9bec3f7fa5a38cb1097d530411f484fd2e1143132751aa7d4c6edcf102dff105189d758d88918963369f0dd3287327dd189838ff28df924d99474cee86960d3076d8d0408c4b9c6ae38d66b81853347f5f8861897367b4d269d2ea57f42646250e6e722eab2fba536b4a9c6b4ceacaf96685189338551052c7437f74e6599238a6aab244e268fe5a3bb252dd5f4a0d312071d04d2f1b1233abe4f211c7204dab8c90bc3aa3230e735e71ae26d48853574c3e096230e2a4e75531699ab60b3116519c6b3b6328e2e03a59cbd4c46439520290831889388b8e6779ed8c08a52eb08881886385944bb48f90df78c0160fd8e20139fc46ef600c6cf1802d1a10818f0f1bc80679458c431ce3fb24cb35b1c3521ac31007b554a6e49f59888310c23486f813e2ebef181fd95c8c419cfa92e6923795076c4134b0c503b62019d8e2015b100c6cf1802dc805b678c016640e3104718c669623a2f54a5e550371f84b32d68db290100310a7a49d235436863ded1721c61f4e15724d8af04fd397ce01822d1eb0851662f8e134b96448f9fa364f3a3f3eb462f4e17425b49ce9cf75931846c4e0c321f904493222e94a97f522c61e0ee2ae232717fd2b67f470c8975a4972996b6e350f0769552a24cb0c69b1df7a410c3cbc6e996222ed2a487b428c3b9ccd4af66d84f3102b9f8e187638c436ad9bed4d83d24b8e1875388d3ad72d71e297217a43bb88410753e3fbe4d6236ea4f0ec0731e670301d21d5e90932e59637b40c0662c8e1bc1b34cded65c6caa85a20461c8eb5f1e7cccf4d26c9e660efa24f0231e070d60c22e3d76196fcbca175b5c50724b04599186f38b84c6c7d91ddda93729527861bce124c29d53f2af79f260d31da7096a4d634d6a898f8a33fc460c3d95e4be586af247ecb186b38ee86952072f9c6968a9d21861a0e32ec2a776388a966a7e1f0bd12377da4d37b1e1a4ed972373ec7186738954a12b73463105e6a0c339c5c84f20dd73446198eb12fe526f764386ecc3a39b44596901fc3b9f267093be5179b8be1a031ff224f0ab12a0fc3412e84cc23abbaad048683129a7495e6568c610de77d450ab96059250f4a1c93d2b3dcdb2023c8cd631295e6ee746f4b561e1e514d36698a3c62feb3ba808724ce1f3265baaca5b4da7001e7281bf8f87001e7988147240e7abd2d7596c893a607244e1d2321995026e22c1674c121481b608f471c5d2da8fd8ef817a7b9a145828f8f127838e2203f4d67be6b50972b0b3cf0f181430561e8c88247230e236452a7436b4544ea0ee0c188f38698f7910da337bbd5d12a0803055fe058d691e316718ed199f462cc56df53d58e2f2840030f4528a9ba2aa59458b1d4346b93f29a5341366c7824e22076b762b7f2049f11513e8ac7218e41581227c14ca901c43c0c71d69130ae236251737e430b8c2f6c98198f429c26450a25224fd4823872ecb0414c064860830b0f421c64ca23629fcb8c856c031e603b5c47043c0671c8a2d296fa8d9e15b50ad80b63000f419c35774491667f2737a78047208ec1eb5de45367b7321e80387897ebba6cb5aec53cfe704ab167399af42e5bca0e0f3f1c83945d9112e2e9cf5a87471f4e263bc44dc636ae890fa7d4244484a8cc1e4eb337c15ecc5bd328f5700c29fff2a68c7938bd99ea7f08a5364a160f073922aa79053115420c041400c1c7c78e2f70b0e0e363c717385850bc60c1171e060c76a8c71dd20ee95187f3c95cdb0230767c41010f3a9c465f69d8a442ed9ece630ea4ce430e1e7138a85e9e141f390f389c4704752a7a376f38c41b51926a52653a150f371c62e7aa89d174e0d18663bf5c309db549589c0da7b518522a593ab424cb1ace972d8a85470e19e3656ee0a1866392694b58676d6baa74181c78a4e1bcd133e59c2f95cb180dc7d4ffaec94a4aead89ce1dc1d2a3494bce83066380697dcfae29145a8b70c07dd1b93714925c3f1c4de579a74aa47c4630ce75bef8d30ee2b4a4b311c5289532a8cac5829a14718ce3a56997b54ed0186c376c952a72567636fbe70982c4aa531592a0465218287174ea754eabe92312b29df85f3052933174e49668cb1905479ac650ba75379ab52e1d2c2c13ddb3229112ff6ccc2692e64eaff8875b190207860e178f1925c05bcc3ca8c031e5738fe848fa4747c6d68d6040f2b9c4ccedb8f0551154e2652f0921063527a642a9c3e725969b578db78a7707a7bdd88666a829aa8144e6341d4775ca248ac44e1205bb76ff795f2a25f7058f080c2692f7f5ae94a330b79c221fae8882ce9f742dca0e0e18493127963b94d08cb5eda327834e19826f9d4dc59565b9b09673b8f13215746068f251c77ecafb3d2624934957032993453742d5a1099249ce4a44c377dba73f38884735a5e0c7e1fe19854fded8aeae1e0618453ba18df2c25d1202eb311068f221cb24fd0763f5a73bb66060f221c26688e65ab90c710cee7a73b43beb31483ed2184b3cc89b4d974db2ba58d0d2b7804e168256b824cb2bf00c30b2fc078c0211781f5c10308873cf6e9f396d6523cbf000347cdc1e307e7d55041a890b32e761f9cf3f2088ba09dab750f0eaa257f58bacfbfc98373984c7631ee8a06eb77708e206c4fe6af8353e5f64c16d61c1c4ee97c956caea6e88183f3a4f07b6162f84da2c70d0ea276d729522e95e81e3638a6c6526bf12cbd3cafc541d85889fcf9ffa5725a1c3624f99c9c98894c9ac539aeebe26b4c92c5c96a4566b0d2904432c5e2947f64fd7246539b048b53299b4b4ffb15078b103437eb5864d1156753b115532bdd8af3d9c8d4991483a9a45971fa9a2822687e15c70c4a66c92575c48954718e53751a265608254ec5e9f462aa78763a9245c569dbe2c2c8c913d4884e7112257c2be34389ef90294e6983be1407b519228df0f57b1393a2d469eb1bba8fe278a12bab88c888e298e226496651138ac3a6bc1125084b212f288ec9e59246523f71b66cb78b116b4984eb8983ee9ef4db60274e52349be995b79193e5c4c152881049d398108b6ee27c79f37efe9a4056ca1ecac4216dfbff5c8989a3261d624b26291293f012e70a12b78248d012c758328ed4f82e1b7e254ea7924bda9e85595d28714cb71d723f4bf9b6651287b4b15dfc92c461631c5569cd32424a89c4c12fd826bd9acdb687c4e1bb63d4fe6e9e8e1f41bc8941b9f6788e5853372f766dc461f4c24e6f54d2da33e2e0b3b1cad2a6dc978b38e8cd939151d16c838a38c973bb4b1a34a5c42c11073d277e2fd924661811a7b4d21bf95e1de27c2f31fdc6cb10c7daca1bb137970a932ac459675e37a6b4dfeb13e2389ab5b11b1dc4492ddec90a7dc1a4480c296608e2b451635698fc06e2a42f057fb5d11121df026600e2a8f1a7f9d45de9c8ff0fa6de9c4dac20e387a3a5dcdbc417a91bda3e9cbbfd54fb2557b537f9705241ae65936d49d78f7b388876d1e722a209abc80c3d1c7ee24af23f5922a5f370ce8ba55c6210bba74b33f07088b1b49ae55ed55bfe0e33ec608aa694868b4937a30e27218488ee0fa5197438e957cdf7e61c4ea952c7dbec9f8eee6b891972388e88b54b29d4586a461c0e675d36e1445288dcc3e1b0513d4e86e47a492550cc78c341095dfbb6ae6132253931c30da7e05d628487ecfab436d081ddc48c369ce62a288ffc619a4286d987196c38eebe464ca65294b13ad9d8e1c52b0cb88b3074e048ba33d670cc1242e56dd2666ba91a66a8e1f85f622666cdce48c3c94fb78c2c15e4c20c349c644c9bf37e62dbd32486196738a6d5d1911de373299ae120528acb7b56665a2633ca70eabc4a418acec9708897336867108b71d9180e4959583e115b5c2b258693787ee8fb593b150de4f01d2cd0c0161fb8c0161d701d39bc053820f01ac8e13776e0386e98118663cc64a32fe85df74bc1704896c1f44ab85849fa5f38484d71b9bf47a49d7b4298e18583fc3a15472e2c871944cd1fea4c65e8008086195c389f4ad64d69bcbac28c2d9c7fd3cb28b51e32de450b47d1dc4b8d16cc546d569891854318716215434ce8cd60e1a492c914e2894849857a8553ba52b94e8dccb0c241b8c85acb4c6046154e2567358f45b77c397f2530830a87d1f2e1d91763dffb3682195340b9fa6b04130433a4700a4177dba4f770db90edc08c289c424c5349527231a5cd1b5a5038c9fd4b96736366dbeb8c271c948a50b7ca9722ed84538a4185fecb3e2ab67b8e194d386bde33adddd3d15c99c184434a51e267d57533ef4b384bce97a8512da78ed286b7a00b3074780efbc00c251cf69288506a22a8d4ec0d2db6e13bc068815d604612ce19f2cbadbd3a36db1948d0194730798a6db6c828bfd20c239c46756b04e5f2fda2bfa11586172c40a402338a402c5113be244295ad4bbc86cb99326308267da1477ca5a9d809660881949a2e2d2897d8cd08c25143cce737a41083484ac20c209c4653d2db2072b566ff83532c154ceb68f6e9cb7d7050b2b1432ba67b70aacbbea4d9e7c15194943f93fd5a51e11d9c625ed15539e1f793d3c131fc05adec0ca5379e83630c62bfc7fa9f8183b3f76d9214a2267d313fe3062715163bd636db429f66d8e02c5a76c2478ecead512d0ef71ef9947b4c8b2a83d7c83917f76671ea493e7749999ecbcbe2906b7c6383f60f538ac549e9141122bcf42fc4c0e22062ace4f6842cf3f615c73832d8e5ec72ff93b9e278294a2cf7ad38cee6e62b3d71b3865971ca11f9974bfaabe0ec27d7e4d39b72559c4ee869d1d7669a574cc5168469d20b9bb6a48b8ad35ceaf8db540bbbfb294e39418e246b0b15456e8ac47e54eb529c259c92d9fb9d93325254227a97a75a1ac549b30851c99aa56d2551281a4a6b6da45d3d14079942c4d0ed3528ce5d17a336f36624ab3ff1850278e2904e36e553a9eec4712fba75aacb0921d29ce0f5beea36dec4594b5c268b3651e46c122880268e415b298dab36733967e258697d9547a5feea061347531a345c8875eea3bd441973222d86a690b3c441c51e8d717d5e19abc479e5bead4283669b2c258e992d8b95d9a69a3a8963d4f8953ca48ca44e257134d525d4c8142012a794b535d9bfa97c0d09620c9f6c7ab2c4d8235057a63b33a538bddd11c846505ad9928dd2ba11874d9578963a94b8301971fa92615a73b388a3eda8bad8674aab878a48c4f92aed9b85b820e2702e49b9a97039c429c43625ac4d63a8ae18e2769315e259c87b3252888352d99d21281d210a3088633e99bbb377bfff9d20625080401cc46fca7866f9debc03c4f1436f9cbaa46dceb53f9cde4663332bf383b2a7b4c910b4ebc341a5537216eb166a2c3e58daaadfc341c8107454ba779bac1ece2234891c375a6a93017578556b344b65e9953b35259154a95dda10873c226925fbb5146a17e294a2994e8d572a4fe90871cac9fb15c3a81b3088535216cff28b97ae7c21411cff828bd808a24452321c5e04e2d8bf313ee6c44d96500310a7ca197beb42bac61f4eca34529a5b9e081b6f68b500473a0898b532250335fc70ce9c793409b54b6993fa7090a464d0ab9274ff6df8704ad2472995fda76ec13d1c248cd5c6541b6a6ad3c3a6319967c54a2924e513dd67656741c835f2706c11b578263a7f7fae8187639aaa48e262bd93cc1a77388a68266fb9cc16410c410d3b1cbfff35b62c4ccae5e34307312ad4a8c3219b6db7b946da94b9061d0e79bf940aa75a8d391c56d77ed3424ef59fe4408c16596316af9452cbc33dc3ede8fb76c66ac4e1bc19fc72436b773c183a4aa2f10d1d990112d8a81a7038080b7639bfcb4218708e1c8c35de8068b6884948b9adba26c6755960956347186fa8e1865334c916d465380921a40da76425cac4a6ab94ffd970d64a3d49bce5dabebc865308572a412368be6f6a38a99d89e97aa9fee46938f6cfa69044645ecb161a8e753aa6845d9f461139c3292cb3c93d75a9e26630406986f39f79aaeb843fef3087083287a71a6538bfe8c81a6593e1e4b1252cdae90bf1f6188e3332b8a69ef9eae5da43f0f1a159430ca73022b88736350ce73bb1a7dcd7cf4e48c0608830553b9fa32f1c6324fb8962a3b9feabe18544527196458a5fa30b855a659746f22a4ba92c7986d91b95215a0a430d2e1c434a13638ddc242466bd6dec68ceb1e3055b385e5a1212fc334f5b98981a5a386cc94c97a80d6b408d2c1c73569420a38db31a5838a8784be61a8237b4468d2b6c9ee2195d5d5dac4d2be34515b56955d3cc00096c60a08615cee6216a312c59868820a046158e56396a2ac624e35f860ae791999522d5c5cccf53389c6d66c89a3b556c2b410d291c3353748aaf06b54614ceb7ab1125fb9ee6586e68951a5038e4082b415b48b7908d27903d44d2e48d3f71c231ae694f8bb496a5ae2bc41a4d68410d26bca0c61218af8ef9fdd6f0bd8612486d6159cd2c658aa6de95e96cff26895d2309674b3b8b284ae7e459359070b24f8b143b3428bbbdc6114ee92eb21a4638890c2126f3b26412be1a4538aea5a9fd65d3a43daa4184b3e691155fc91a4378ec6e3462476a08c139a5b2a45021572308a7916c27f4bdaf018473a5532aab6aa5357e70123936996bcc76e0d081a305668b0f48c0868d8f0f1b1f1f94a8e183f3caba8949ca36839c30a0460f0e429a8aca79175dfd83b1434b2b508307e71895ef54907317a9bba1a503c752a0c60ece696a726eb22fb1d5b7b03e504307a692ddbe5699269b03adec5e3626a1ab5d1c7813a289a5e4dc54b9012716d14792be5fe0378891b2f10214e820356c609694b52297564b8a2fd1a5d25e57e46a718ab467b1aeb12742b438c64cd297662342539ec541c8243a31dec610ebcae26c314cdce5a496f24a2cce6642d3c6cb8c31291d2c4e7ab2d58dfd89aed32b8ea6d982a9ca7f1f1a291aae389dfa8d24f625c3d78a5388983744f4bc285967c5315f0a8fef33b1d9cc559c26559beee95e15278b5732f682affe4553713a71c95f63bd43f7888a63ca9af4470a276ed4e514872d95f073299dec8b86294e27d2d55964d791b8a1510ad4bb2484606d2d1ad52ebf7a5e4f8314e7d3af495978a996138fe2245f72b282981bfd4b14e7b690dd92d0cba24f0b83053bf4073442719221bc62e3824071926caea5f6d54f9cd7fa26cd47bc94157be2ec63af7a21749526b71307d3f43b2727cd024e1c368a9ed2772287b8b78953e51871c2648ae476ddd042c1173834b1dabfdbaeea9f89634831d4456e996b1163e2bc394e3493f0cad1f512e7dbccac4d1b4b1cb206bf90979d912a770f7cc00b30767cc13736f0f191c3559083bda8adc469e654c819922eb588a1c441bbce6abd5fbe723b89a37ecabb67a6bebc99240ee797dc57c7d2eeac9138e50c89f577eeafd984c4292cc9941717af4ee88f38289154e345539ad4d11105d5cd104a4e4c23ce159bd6c47cc988d3281d254f5e2f9d8817710aa74e7e5fe5679229459c740661a262ec2bbc823da09188536e68751795d3bb705b817b001167512a04a1d2ebc80d1a291d3abcbb016130068c27c30c90c046d23844c122a66c070e1d3842906387073e3e7474118617388c17340c71d018f4b5c95cded05a81eb28c4b9424a420419930c5b18ec858df485eba1418873a879b884bfedf7bb00a36d803188e3558ad739a607d010041a5d53d335b659d4f24a25c35dd3c23ee594341047ddd4ff3bf6858006208ec1676573944b1ced610668fce120aca2299926a40bfdfbe16022cfc5d68afeeb210abe00a38be5008d3e1cf2888818692399df8902dff1050e3e1cdbb737d64f68933173434bc78e1c1c46c9dfc32188c4ba4a9d95c22a667a38abc4d0a1ddad46e8d0176084f1022f18a09187c3e9c999b29769c35b37b4ba602f70b4a7c3c34973ecaa6e58a83c2a37b4aaca2a40e30e0765f98216959632c4b6c3f1368b18a1275782fe7538e9caae915b1b5b5b7c40021f1f9eced0a0c3394785b465ba177f39529ecc68cce1f47da696c35936572ec4358a6996c4a13e3fe999299adba183059d5b7c4002957060018d37b880861b90f62252266b56128d36ac6a9242709978293935b541830d4755bf1411cda3b186d3c9ec2b1a26613368d58068abc6d46e6ec92ab4ab2e090d6bd2cca24d230d07ada3d4e476c6447c1ca4d0700c52928e5fafc91454b791038c2aa47186d395fadeb0d1a7b25a98d873940f1a6638e8a493e1d6e1365ee0376c680e1a65385c9b308d6e6eaf5eebc8c161181c34c8709a3fdd2db15e2d47674305ee615c7d41630ca794efd344a869d93bf0f171838618ceb3572fa682fe5dbb6884e1682993349c6ac07094705f59e2e234be708a496edc0daa4e726f34bc70bab9905456ba3e15742f6874e1144b77b55aca9f4908d1e0c249840ddaa382ce328d7d81c32a87ebc8c11bb0f2640ea0b18583482c8b7147fb5c3e2d1cc36f297941b756c37b011a5938ad6e664b0aa51dea7d7c78faf8f094c375d0c0c2c95be6f2a8d39d13b2385e070b72d8d081634b47173876b0400134ae70ce947fe2853319aef2ef1068010d2b1c379dfab30c6a32e9530d6854212b1da28249c280c18e2b182c0c722c0d2a1c4e88916bea972f6aa7318593866fc9192cfd6629d190c2d9e6b2724cbe670c9146148eb71be2afef9daa590b04810614ce6a565a66d4c6d59d9f70ec2f292644ca90acab133091e3424c13ac581ac52464d1482132f5cd2c0909b20de0e802093498709c74c144e8502f3ba5259cb4759d091584947096242bd5b6ef77ed422309c7242126a9ffe5656642c2f1dc54e65fc4348e705c7ff90e899531d030c2e94feab76b5816b9df8d1d3b388c8f0f18ec601a4538087535712b6113060d221c3f833e25359b90d57b08872c979da794dd9ff44b0ae1f499b2e5a5bc662a668370cc5693b61694aaa88006100e7fd73a3e277429a0f183ea455cb60b30c0405dd0f0c14162cbd796a424634a6241179e230c5d1a3d38f9ebb8f68d8c23253776f006ca8d1d1c4681c10e2b3478709cdb782a4296b4201b06d0d8c12108896129a618579aa48393b0d7a0512b7a966a3ba09183c3b598ecffa4b4a34c9663870e0bd0c0c1219574fb8f18a5718363c910534e4c4b25b349c306e7f2f88cc984b616e7f4486b7feacb01868d2bad1cbe19f0a0852ad9d2244630d71892294b51d21bea450e3022f0f1e1450e301e081eb33826533163b8b69452aab238c8beb0cb5dcded7f2c4e3a5fe6c2050f162739952788b8cce315a70ca95c548a76c24bec8af3a974b31ded5ec137ad38f5b668bbac9854dec80a33b2ab5b4c29b666caa426a2c6a99a0db2598f551ce5829d9a9cfda1524615c79141baff5cd60c42f548c5418ceae94abd952435c5000f541c64d658aba4580e1cbc016f018e1d9c819a84c729d4145a8ac3ac2953f54f598d16dbf1050e3036905575a438aec69052d2979a29e85f60a31e055eac0e52c4631407b11a91822c959492bfc0d1054243c3431407db9e0b4b7af408c541f5c9b0c1c4dc28a1f500c571728614798da5ede24f9c523ad994254e902022cf919e38d98f8c7023255aa83c3a71d012b994ae7ab58b1b4e2cc982044ded6d667d32e515559267d34d1c3f35a414db0b516ee2a18993e653e962269d6bf22f28f8a2031f1fe90bced1011878e1050b20f071c3231307d53011d921fc5b528e6c7ce128d04a5f70073ae01e9838d964d92032861e973086528bd958e220b2a72c8bc935a1151c11f8f840c117393e3ed2179cc3a31227a638ffec768848a2521c2f8ed58b687a991191e220839e399529a6d1221ac5c9ce43e5b52aa16e43a238689fec8bbd4271783d15940c22ab061502c5e992a9e0ff2a1acd429f382519245fe8effc8b204f9c841a916b4b7af3fd9d388c7b984c4c7f2b13270ebf212f24c4483ea64d1c47c4eb5811d1ca90268e294d4e535127518232715a1151e269d83d8f3171ca783966263f43762f71b4a041738fd212a738bbccf09bafb4acc45983c7a595d7a04c8512c7be932677bb499cae74879e595efd4c128719affb4c5ac4c4227108faae6f26c59038ecbca68d1be1471c444cfdf33176de3be21c3ba336a36fc471537ec3a4e683117f9fecee52321f8b384accee1337882032c97c28e22c79722693af52a9643e1271cebfbc9479bfa406990f442c97f7f3bb3ec4414d8d6e8590528a6c88b368935af24a55a47021ceb2a9c93784c977318438a4131fb941e8b04c0771d0193c4b8a0471ee125935ce84ac950cc4c9ac92e53d1503888359ed9e58e50f8789a74c248d67c9981f8e299fcd6ac55651a93e9c44b959a48a8bc127860fa711e31354d9997c0fe711d277feeb7e33ae87735f8c14c4fcaa44290f279f57b7602ac2337838c56b3edb0ba9eaea3b1ccc4ca64ac93d34dd3b4e67aec3a92b85fc934b2ea94c87534d783f51f964baca1c4ec2cfef3a8395576ee57034b1294729a12926b5713826b5790dd96f2a4f160ea776dd2483ca1b4e7db14ae34fbe85bc1b4e316d53a269c819f2369c548e8e8dd9fac1862fc9728bd7a1351c6d54cf5e92ee2252c36184e86c49a7f1729c86738c86ea0b115e4c8386f35e9e60a71ec2fa331ccd2dc5fccbeb49d40cc75cdaf1af1a278365389d86889d5a922121ec4cf5fb3886637897deeebbeb2e86d37aa8ef058d1063c270f41ea16fd2453b81e1f0f2b7357e7d9afdc2b172c4f56beebce285536e95bc992ea414b12e9ccc747d9f0c17535e5c385f2e132a279a6549740bc73b5d3135befda185a3ea68a524ae7ab5e747160e1e79d4f2820ad1effcc0c251736d729999a435e7c7150e922cc5d45e7a59c4f96105e747b2a8cb9b2a9c6499d49e31a970dca4f67467ec8c993285d3459f9f778b14cee37aae1333cf4f5138a995da90b56aab86c25956528c29e59e701a37bf52573ae118cefa35954836e1bc2b97296b8895f567c2c94f285d565735ee124ee9db7446d7a86174251cbd7c5316bda64a474938add6ad4a6cbd8d1b241c57f3aec9cba632b24738a697dd58fada4b4c239c529e2431e9ae8b122cc261be34e46c50f91b221c466fd88eb3bedbca100ef25e1b6258fe8e0f8570debf10ee2c262b99201c2404e93a13f3bd0884d38824ddae3784bcfc0f0e37776af684852e7d709468595e2a6308a9ee811db1849c89310f0e9232c6edcb0ece996a463684ffb55407c72034a24b7035b9e7e0f02656367b4bfa18717052e9e2c814732aed3738dac59021638a21371f3638e9643311d4f74eadc5294fcc6d123b5a9c4544927e512bb338ce057929ae85e9cf8a2c8efa95c25c4a95581ce7674cf765cd2d72581c3c94ed099d41dde2af38a99084722fc97f93e48a7345be92db34aa82502b4ef6176398acba0f21569c3389127d4af8d9885671f65149ac6543ac95557138db4b39292615e7b3f81f16648c598a8a93245326612d4d9d75a738ebfe5aec57678a83786b9fdcde6dda97e2f0ba9a14a7546a3efaba228dd0284e39feb29cc688e298f49b9c3d14c710d933fc6a38e92e288e2aaa9b84a845487fe2dc22ab765f7be2f8225faf7a77e2e823a7c2aac8947e4e1cc4e4fd4d1f2392ac6ce2903cd5a2a44b9f234d9cd3fa946748be5a33719ed154693497f8ee30718c16a369a74b9c25690b27532c713653964579055dcd2a71ded95832a88c122711d29252f19fc4f94ff26bd2af501397c431a26592ba47e268deea323985c4b127c5b079841e71f8ae7422a5902a32471c93f2758b575282841b714a0df13de355bf31e2a0627b76e6f089d52ee2605be1f4ae24459022ce75b2af1944b0ec6a22ceea9373ed7c4f45c46135cf282d11a4890e71b8901846c889b6350d719e5c92e6e4a51c3d16e2e0e33b262369cb23210e3935cbb7ac9f58ca20ce25926564aafbee4e10c7d03c27c4b40ac421b8a6ae790588d3bc5b5f89d46726a93f9ceb828e6f08eaf6bf1f0e9b32c577d39a3a4a7d38769a4a9a7b21e5cc879357e4d98f91b8dd1e0eaa4685c83075976a3d1c75b265be8ff270d8cab00e0be3e154ca46bc88255709dfe11846dd89688c1d0ed9943453624252a9a9c331b8bc98480821c5950ea7d0a4c5375b3726e91c4ee2828cb490c4dc53399c648e2f7d79c3ae8e713856beb5f7060ec7f3f4ebd04b4aaee50d874db929e8664a22379c527bd8ad083fb7db704897dd4ddbdc5accd8708825f32b8c848b351c66bd725e4a951b5ac6025ca8e13c793b35894c3b354269385d5bce1422b7617915a0e1b0af22e3c814a2e30c27b998bb52fb8caa6a66386eca5db6e984f6d1ca70d05411c63c432e9dc908b820c3e93662d451758de1a011ebf4655913b113c329467e454a9541c77d26e0220c2711efd4c830290b01176038a4107afec2d1c28855e6897053ab178e71d6d9126335cdb43ab8e8c2314dcdd86c4674158b0b2e9c77337a9814918b2d1ce7c4d9ab67584dd9b8d0c2b1aeab7ebe7b349b908b2c1ccd4a59c85fac6b0862e1386a34a8c9fa0e29e152573856485a7f2ff4c40b3e1756389b8964af419890a2e92a9c4a7a69f650bd32497301175428c932614a5d9223635c4cc1982c68ccda1a31d85c48e1244a69ccd03e4931d91586171480810374e8e00d7ce0e3e3830a5c44e1687fa6f3e4e88d19291cff85021188800e76c1c787377f0ee3020a27b91ad3b9e9453ee114a6d9c4af85cf4afb8b71c2f1cd92886397ded03ac39163476982176080610305396cd81000174c38585cd7917e11308e005c2c217d81c3055fb060005c28c132212f7e2e8b48471761b017555c24a11089266a19c943c48694541ae9e0343bbc782e9070343bb55e2a92fb8fb68b2e7058e0e3c3065c1ce1a06e7721a410612dcd5a1fe0c208a7ed2da979dbbaad4c1d4538a630d5a99753229c376dc2f56cc85de22326022e8670780ba1c557cb4226025c08e1206a74df2a5782708822f4895429cc84481d2e80704cb5144b33f76b227c43eb07795e0a32bd6c7ab9f0c1c1c2b85f87c6dd51312e7a7032a5e36dded75ee560efa251025cf0e01462747dc92664cdec5cece064ee726d4145d120ee0dad924a012e74700abae9325287c6968b1c9c44c492efd6f80d2d1c1c93a6dfb4b10bba5774432bd16e709cec7f5fca2b9c88140abec081012e6c708ee46df12c555003356a71dad9202ec9976f68edef6001295a9c6b2e8993a675ebc0e14599408d591c5b4dc62eb2b23865a850262aff992e128b43cc90cf2f9c960465c1e2147287b6049949956c947bc541b89cf286d6590d571c27a8d93ca63795deb4e2203e2d47fd66cef8ff8696b26163870e306ed8f82287819160e0b011868e1aac38e82d19b4c69890a2e53556710ceaa569929d570402355471d0da3bff8abb462a8e6ef25fb4de5664773b500315a70c192f29f447cbf2539c6df5d24cb5925caaa6288b440b2e6e1adb5542f0d1963566cabe612ac52988ae2c49547643cb465ea0010ed0c0052490c949e19d78939ee1ed28ce1ac247a828db063544714ab2b277a68a23d48a6a23b986e25cf99298d11dc20146086c941735407192b9620c4a84bb58658d4f1cb3b24878bb78e25c21fa2cdb52cb54df89a3e435cd9c675ebf1f270ed7a55c6496c6487a13a7f44c72e4421e5d42d3c4b9f24991a849d34ab66a64e2287af49bd037223ef23ca88189939bd6496fdd2193f57a831a9738c6661251ba82b486250e21e4cd15e4d9e86b5e89c35529517a3c4d5aac3d87da28d691dab1063528715c3791446d526908f3240e51cf4dc6914b374a2a8963856ecc7e0dd1312d12a8755599ba68994c21fdb24c522f41e26c2ac7e4ebb29032ff6074e1818f0f1d3bc0e842c7eaa0c6230e1e9af596a2dd9a501d7136954f29d5a4469cda9258516357a73ac30892a90617cf20c94c4bd42a9a12f9f32de2344135e2c90d49546baa65a8a188634dc8593b264cc4b1d4cbc934f3352b39220ec2260599fcd721b6048c6ba864ed2563f2602c140945e270301c0a914e57b3130800183c200d854291503051855d1f1480025f341e38322c1a201c141016148787a160200c060943613018100803426170301c5a8866241f7c84c2eff0ec64423b28a11144a2eb0721c0ecd1103d5a23dec3cbe72d3d5e8ee861ed616b3a03c074a7fa908f61bd21fd0cfb6f941d155434ab9d65b1ac88b1c16ab6429e82cae3bc5a35c694ae55334c495a5eb22194f1398793924c676b3b4853f5cd7dc9ddf05b04b0f96ea8a4a79802a305b522ec12e47b464611e8e6bf719bbf1a7c33bc1d0d65822b53a6fd5d80ef250332b0ef1dbfcbb69e66d2871476122162a5f3d243ac29cac672b0c070399b9859cce10cd63d33fe12bde17f2ada58f363f52748ec7223009fc688349b889476249ef41fb8e17ca520308e14678c19e6c377500547ee4967928bdc05f2d1b1db8787f54ec0aadfd063f29f4af3e6c405635350154b923b56bae8a7a75d635647a339111f5d47ece83bf0c7bc81985a8c4f42807c2449a334519a92e81e9ef879adeb8166cb51a9a70dcd69ed0cbebbf43395fe86a77cd522f83130d68dcc64edf053de009415af9776fc1ca1a3ea501db2396c54a9fa62cf0959a2a1c8cb3467e3aa0616111bf1c181f568f51c834f294116d88c7ad8d7ba38446ae5b1cf42e5c9e4734358abb7e62012833ecb1b8bc867e2744afa03beb16a467c2f2b4b94f5a929a6517f26ea1499cb78847077bf7d0abcea7f2a12957da99b74852b8f3b1356d75a3f72a5f19a448cd244bbc1a14412d61366f0244007aba70ccf0291af8ade0edc47c0acfb5a75bb5972b87f22fe9472d47b557817915e7c4285cf39af758249c5b0f704de8477f25ebdf4f2cc3cba0ed17576aafda148125e4d99c27beeebe04a9fabb4ed62c279a6d9518e42b4905ca376152e8b5fdb52865702d6a8c75955ad1921f8657da01d5a3c44393ece33039a5dddc9857237da40e71a6cc85b847b9c4d0f82d130f2769105cabc4677fb2b2f27eef20e7a6a0e685c2441919fc3c9987b0729b0f6f1d936aede758c887128fb08029c8df7de2bac2821ef1ce0c2d6c60b23df0941e60989e5f74e794d4d7798a58697c57db37d7da48a0090057969a6ab6af63e601faf5e9db67e1546902de75cc885f87e4f2f3c1b25880b522edcd7c539a6941e7eaeaf85a9a025ed8f9343f17223e137703b4f30fb7fc713a3e6d46ee894cdc6767ff5e3dd393c06e6e2413db0c3b5f4a3ffe3f0b597891f8b9bf2a9480940e55a1d369ec42d0a7b6032b0646021f9052aaecfeb24f02fd4016b8d39a659181f91ea209251f363468fc81745c9d4abfd88c3e526cbdae55279c722f569cd29d68bd0493ec4775e9ef617ab58df8d2c6a0492833ccd4c84b9d0649106561bfda2d0d6f93bf88c4e6e5a5388f49d872ae609f71941b73974d339bef57a8d5a81cdb901b480522b235299af35574ff966fb535ab2d817ba7e88b8bc2dc189148675b5782c0dbb1b0181f15e3798665dd0a7c398f707670818463cc2ed4000364eba0daca5c93774d6a78024637d797f2d5262d684801a8b3762202933ac28b2376e3c30c97589b40e9f3360466f429ebe6fbd9ff4f8685d05b0ef32122ba2098f2112cf79b79ef48be3d83f0d66e125d398112c32706584d8a90b9d7d0b871f2902445714ede5f9aca26460d3efe60944d1d86c346a9d306910965a82227a149c79b1cda576d540bf47b64800d80abeaef003928baffdd2ddc3c41c35257e63264ea125a6098b0998e17a169176734f6bc2936c215512f863b71391f9561e4d8d4013e9c662b338196bd17a90736dc2704ec2fad42b72551dc08cddde5c4897807c981b164874c42aca5643deecb62622205bd02b895e9a6c4413552a4b93f089621a42f5cc7b106b333e097e094a741463a18353e536b496132b281de908013f8d100fc39941498353dd822a849eeb2bb617d1c2a68e3ad0b05ccf453c9056af1746d7f0405f00e7581b3ed76687f5aae263dfc2f99125baba8e5ba99037c77ad695e47d9b41f83f3e9db3adb6eca8e691636374bc8d7ee34018e6a06ac97ded5e939a62ccdc06d32e8a12ca87dda4e8c6a626292c5aacca35687aa62a021bd81db77a8d2767ae38b1366656b014b0362a2b2d82ee39d17c049a10d6b93b3bd536b89e98fb4ddc027468bfc371781b32d6b59c86114c517abcafd9a879fe2a48ffc2302fa9d3c393e2eef6672c252e73c380e7143b97972b22977819f9911d7e414fffa8570c458161ed7ab40210ddd4211da9ba46740fed8057f01d6032617e759566b44dd27a6ccc7946a15f8ff041fba785cda49e8badee9dfddc1800d5f0facb0e2a700614c67b268132147619c224a6b0049ef17c2d2b70821366b98feea22767e9e5cfad52420bb28e28cbd0c69acedf324cc2ce08af0926c1301bf15e07e0d8f25ad802e811f47f1d666018a71918b8448b61e6cdb3705ab5d703cda0b6efd894137f3296771469ca92ff8643e3657280ce3dd26d07a1884995b56b8b37054fb8ef3001e5d98f9349347d781c8062a711a896c7b9021a753b29341d24fd87aa7a2ad4c75c8aad36c870e4fb2f28152d5f912b4907e73cd9fd9ea76aca441a597d305a14a8c60e35d825627abfb8837d4b94e6c5f4004bd99efb3acc89965e93d4c4d454b9335b0b88039869f79c490f45f0098e208a08b83efd87182b54acfee3c01f0cf81e0f3a916bc2ab7dfdd840e1f111e3852a2ab5d8765225a04690d565e5357dc581856b0aabe5bee10f0af4e709d5f6ce9af4192059e6d0ddefd724962f4a411cddfe9ccb2faf16d3825aa92a115fad1805d9c9bd06bd3e71c1eb1ff2cd2b638385a741850ba8e19c4ca98607ba1c83a0c7640158a1179e6138fdb1243f81a8a8fb3f00f00cf4391058c7a170a604a6b021335c38ed50d762eb07952ed60649b836ebf808c04973051e5a45cce2aca534cb437040a8fc81d4ce8e74d266a4970cccea2415db8a35a026ae283dff46171e5ce889509a68975211e75990782bbdd0c53bc20108eac21a288374a9960ae502289112554315deaa357b50fbac9bae2a3aeecba95730ccd6e075e3ec9c9e33cb9f6a41321c8d3fe69c8d726fbc0cbd66594265842bd650f521ef71c8051a8c104e418860812b1f0b0e97ab21b092153b7c4e8b7711e6aaf0786e50cab891a34983b006be967e4ef8338e1e35671baa1a8f4bcd52b75816062c7a05b12dee3de11ccb9f126e9a90125c030b125bb4ab207476e4c9f284617ff3d66d33b7fe555d701e6bf564416825312c9fd8beebc76534bdb1f313325d0fef0eda90a4df39c967457b39200e45cba8544db54836db93c0a3a9d20602f398ad9ea0ce64c87f90f304dc3bff433457e8e4db88e3004fecd86361e9768402cdfe4e40c9addd2cdca954e6273b4d51446ec36a3a85491c96b86c5a2933ca8b9bf08afa312f28da2ebce200f96bd52e255e6102616c9774538e602f7dc1dc7e3825737cf5a5bbb623af0694c3fe16e13725ff1852fba2f5239f0184e86d3e8af2296765b679559228322d08420c4839ac303d854183bbd5a9f0a9fe33b21accf67fba45d1626718d37ac71353a151b867e094e4629955b8143290b353554ae83fdf77ebbaaf5a0b8d785beec8a80b32d6d01864ae93e576afe3c0f89764220c047903a89ca496f94ce87d9780c6aa571219ef2ec9b8683c3a7eede506242e8a9e1e68816d2d409368d353c42f6d4c2bb22f338b7e4174c41b4925ba944920b43fa5d74f5042ae51ff2b3f48f6d27def27e77c854199b2df4948f421fa669674e235f3acce67d64e3d240663112113dea6c911fb91531719b0a74706f6c1516e8e8446588bb801afecca5cb990ca94fe16485978592ac7fdfecd02cb5c603b453cca1418f208a452164ff58af46af3dfd7d930b74cb1100840674efa723e12d5990f016c77c18510cc300837492ea0b1fa0a56694e771f2f839ecbdf4a1037c3a3ca31924c8116952d7a9a3440b3500c821b42064b58c59d29297b75ffd6ba51356394dc1ccd169d03735acac6c32c92b472521928058079ff2bbc8fdab9284df7fdcea340d0506ed29cf3b91bc2893d51a767056bee882d75af6b54c65a365ccc270f291cc198b3eb1c20245a4a9694cfc8a20b771bced88d760a5610f81d65126e7f67a727c194b7c9af2f36a42d289df9e7ea8f1a0a8b30b45a139ecd46678e90bdfb39890869ba4dec0d73dab56417446355253a519dab3e42c994ae3d9620572652f045da2cf63779d154459e2771cd92fde01eba89bfe480537d652d4ec9ae16b9c962b6e246694806ca8fb144776d3a768995f4e8342c4130cb727bd14a1fcc9567040fe935acc27171103e9f14eaa163a1e29d573244c2139623fa50f29d1e41119cd90a83703aa0b3493b9400b652eb57a056bd3885470e0841a151e823ef03d471ece7a8c9bdfde07f3fa3dc01af93d96f70321b230eb8dda158c00055b1b3855dcf38fd9dbf2a17f0ae34a7285baa2231af779f06ea938428fe67bf638fa81db58ce998b563523920d86dd6cf6e1a0d85d2fe89755eb1444dc7265f410ec6ed49c5c8b290147e95801f912da0c80988263237119f1261466332193b4923e9d39c65ce259b0dd3ca0cf5d0dd2fbf04e7ee2eb70413e568151bdde4286758da7c215a8d5b8854c5fec9139ca609933c9a14770c74254aa1c82c0713680b562cfaee8bf47453240a2b452d5427df7df7f8dcef90263d9015d931539b0906508a7f00c152fb0e101c3c88054d505f0bd1569a638959704051bd3ecf8140909ca8ed2eef3430b62044c09239308c007844a1a051577546c517bfa7554d35faaaa0ed57214a1aee3952fc95338e8dd3aa3be78b393340b97560424448666cf3786100099ea97ab12601cc424bc21d23a0e1c880f2e618073acb7ea90a1a1e17897bded3caea5db8f341f7fd040fdbb9d31dd138e63a89f422b6fbbebf5b56c993cd3887a00b71b4dee328665246151e4eaf0ddd0067c28b565dad781bbb4c032013d6252e397110e56ba275e045de26c5d6e1e30a0606461cd61255ce45cecd9b589c056288cf181a4b3e015a295252241aa00efc845562a5cd6cdccb84651f9a1d2f91e7fc2007d4b3170901e01842274036b28d6f61095572664e9521310ab1e1045c0d60d47316442802efbd7864c5cf5dfd2c5f51c4b7027443763e1530b4b54a71d94263f020f6dacba42ce63e1c043baeb4e005fd6d81a95261b35deb0ddb572912a3e8504e085769eabbd8e4078738152a73a83643911d208afac0474a23b5611dff452e839180c6480e7e844330d604510617dcba121c325c6c1a923238bafa22027c3101bf7640e49c462c92b99e65c14eba75509795fd5ec03da3251c3df0a644a1de923fbe4ec060448abb300eda72dddd5f922f334aaf2e96a133e71d9116773d187608c45407bea714937913e3593ee116bf71bc7031edf7c97546834d5a24e5872379ac69b414f55955ad01acf1ebf1d20b4bb330e2ffa6f0c3b8fdde2ed058b947011ee120c93f7a833e9f2e55899f685b8d13fd8fb5de4a4cab1cf315408f14365f301340633450f073a50888008ab5d5e99a0f840bcc4a10dabe17ac187eef4b28966644032589716fad1f9a8b45c479d15a01ed9f2fa4c6485b5a6c310e846cbf7d38c4871845655b437dfa8b48e918326e7710c8d2b37b104391383d5b33c890a54b81e42f78c961afb3b6d2d2e2c88cc73937bd1e7250736c480ca1dd9162e426659e01d4405861aeb14adf80d1f08021e5c446299fbe01903cb653e490e32dc830be8a0229f6f620ffcc38c5b9f5cd827c2956065cae0b1bfd6705d254be5e92be8ef6eca8b7a40a14cb74914c23e825afe02ab2aa6633941d2a6a0c34f3f4a68c054648eaebf959dac7a1a0837540b26d663920c7ed7bc6dba65e2c9460554fd8455ffc3d20c3094dc31f7614280c84785de08deb070c3b4412e3ad69e5c5e655e73607d4b20639e327622a7e81f5c7db1c7d79fdb689ea88a2683b3f7f202bfab6da84c251f23aa4a057228a306e1abb45baf331b1da5d4daf933addaa341dfc21c178867835b150809e356ebdc12feeda19143864b114dc118647ce2f9239d5d0c48716d60f7a8a7c25089671315dc6f3a30aeff73fb7377c452f84936583a6b596db4e83987c89118e4e00c7bc950bc3b2f9906111a1782961dc7c602c05f35bf0ca9600b872c4057b42ff345d14e7949441acc8331e1545901c3f9eb1e804f4f4c25f826cd0822ade2a181a7446a5c014a7129ba5804a20fb5a9bb284f1608d6e97c868fbe1fd1704f71932bc184772ed370a281b76736a32ac18c7a10d4ff9451506194e610b2fab4d98999b65545d18d77020d92119a70c396342ba3f6dbdf191e9a7fba52b37c3af5c1fc4089a7b25949b76b64614a590f2a4f4baf2a01a97333018129e756e9b94420f229d1d5af1ce66719b82fdca9692a4b418374637a3fcc9dceec6b0f16e710134974013f3588046499226592318b7d0aa1548eb8fba12651ad20aab08ffdc71546c5006fa1170e6049e278654e92701150de35b126c9461187bb5e61b0ea478ea4187a23e20c2718db5befb746d11ca770fe0e895ed0c59c90b0cec53ae63f298f602b0cf212ef4bc999928d962df608e02406986f67b75bcd9bd5d5c8d7c4204d99f01e96eb0bdb4e11a9d1bf2ba17142bc5d79ffb31b4ab4144e30011b02781004b9a3a3b04dcc7ac9514ac054dda53fa2516a4e16af3ed415eaac51dad501841208f1f61e44128b681077143c408406dc806f8cc0830d75f0b8b88d8370e99889fc63183e7ae7f028a53e61d0eb71f0c66d34c8a900459c35e6359743c81ac04bd5bbcc25da641d29231be02d6caedf7a56c72b853af9c4d6d553a8e71ab86c5163ac4ccadc3a824f99e59da835987001c9d3c546b9f2df19a391a6c2621e13f3ec45577325b97ab40faa372ef90de2e0a8ceb5fe0065a48fcf4feac80751c5409d9c9bdd675eb16d4f33ffc0c277247e43291db22476b24c58c1ec79ba946657d0a588e379c3a3b7d0cdf010267969a94451164b309f128e5e06a16785373f34cb6a24993696c8fcaa54e20fa4ecd86c4bf0edb175592b9fe2292c12c4b6da3c1cb5270ed3db81185d8c011b7dfb787245071ee3335017e58aab235074ad6c478265ee887d138cb90dd6b5676a5cc6123e142dbbf5dd3469665ac8e7678eaf14168e5e935bc1b84d432fd95db8d9aad5fe6d94a02281a6a846459a2096de316a352b87268e11447b1431d543959d5cc68fe3ae8a478e79f403a88ce0290b61523754c48ec655bd1c4c9baded2c4068560379945db3412db93f88bab8a7630a3ecddae773f6f2aab5b7f740834c87eef7906a8a79eeabda44b7397522e1d5dc874219bdd6c77b4fec87addaf76ef89f79c6de4adb9afd7697bcf67ba0b3954eafabf775f7b93518cfa7ba7e87db3f715ef3bddb4a07bee210a192de1055e789bd7adcf3ee850b9b2b7fe55ff18057cf7fc30807689eeee81de73b651c8de79c998dd6cce573933c5eb57f7adbb751af3c973f95baf2dd36012bbde8c72f73ce30efdc7a1eb60ed2e3f475ea1a8bbf77921d7455c7597e3f99dbae0fbc92e7fbd68e8adf3d3135655de7d6d26507783f9cdb837c7bd7b7971db05a7bc757383c940e3d2cd9689932d237969b73be778ebb0078b2619bcf5997c72d52202f1984289cd0793db96fb363ba551dc29e517dfaa9b377e5246b9bf253ea6f92ad6952540c0e315bb12eedb28816b82e6af93d2861611d0b690a38456462f0f285168c4dea8c08ce48972e9d06516780514c6440532cc071a88c409e748ba05110118ab48a4a46bf353a9592a028919e2f9cb69662c12ecb2056484bfcfcce2229b51cd526a583692890642a69646ab723aa10d6276abbd627cf4905d4e696b4abea0c70606fd73c69c671283fe6d673033dc172cf7cb88642ffa2f96eeb117a43157600f8fac1c6c28b4541e391e9903ff0ac0c45d51e9476b46e84f898b676d98b048e4b7b771c0f6e3b2e9769b1fae9c2077d75d2bcc68d3e99a4285593548235e66d1d3fd5a780d2a6295e1b126436128fa1a7722a1bf7d818b97ce5010351622ec97d624d2d4683640db7cf1c26f6677568102612a55dcf29402d4220cac59b484293a887e6f330eb678b7be1ea3b020187074d9d2f4153fba25cbe31f4de7340ff42d7002df94f554804aa76972fa211917c1ea83c5209603540a3e958c9f0421c93ea38c9acd75d0404984a1a08c4237837ee55414ad32beb0a23231fb747a9750c23322cae934ebed9054e1397524baf52e08579a97b7c2d49c1ad4f79604eec474cabb9a9a40590f46222e286b91f1ab83e78c435bed7092330a8fa59f2f005b35b290d1e3ca063faefb0fe963d6880a62ba3c3c7c20be098adc832c2905241be0e171c2e2d3f16322556d1ea338c380fa3248c8d1d5bb749e729af61d9edfdc5b28a0b72b42aba9c642dfa7c2d81026f6ab5c165e4c3284157badb29a79216160259975e95429f6419835bce42f94a09172dec802a762cb6aa16074cd65c2ecd76de1f7ece4ebcf1156ee45877e2b202d98a205d5da8e0f55e80c00d8914fd8fc6cddfb9b175a23ac86ac455c3e40342364a989d4f00f3187d807625b7b7e0f24412037c1f03722d898a84e122f51d232036c99818a2262cd06286a1c032b53522133dd18e2dc02fc8412a3f7e9f93c29f409e15e6b283fba375101f787cf7e6822ae0d0720b74fb65f9a2a2fe24dec10d6705a66176ded08f5568e9de4486bbf63f1136382712130e6363dfcba42d5dd9e8241d8f17111d36e810af64c8591ab900492af40c6b9fecbc2a6816240245780d5047305ae7720d324c40b841867ac7f00bb2b2046c05301896840805e5ac051294cacc6d2a0609f59157ebbb466c12402790219080cb9810c36410440e320c34f18151bc499d7488311db07e2330e79126aceba0637c246a8995034bd0bdb04c41029a0e712c273e593cd1bbad3be1509eef50e3963eed6145a048a431d81b2126d3d388d542d3f28501b03635862a9c0073033b3df82fd2e35a27c568015017a2832fd4ea16b45451f001eeda468d2345302a01780c70090f079957dfcee24cc227078008804195b0eecbd2343275c322e9c27ab4001a6480b2fd6e22eade6d43e524974ad879815c81c1669ec8120c8845e20e292f4b5c0adc5482020593d4c70d883d3332b266a5605ee311e02ee93023c0331df5ce83058f172ded501fb7e18491459862d39c35c24db84f403155bc83297435dbbb73e484d60d217889a11e44aaa04fa98f655b6756a0a6d03d207de456beef207bfbd21a514623337bf60e6176402bb99f88c43a615c799070938c188815efe7deda0850a78fdf64a351e0554ab87ab759e7f8e6f27b90ded574efa3d1ea90cdcbec399d4532bf76ab64780b90fd0c1af93b0069a9110dbb66f15f2a2518be7af7ed5f10ef4469477ebaacfd87b37b21d28aaafa2c801ad0530a329a33e1381013d6b9e2cdef12d39b3e64b51ba83693a9af53ec5e1ed3eb53a71546c3e80124dc738a2b8a715db3c423f012ff4f5e8a2951b86007bf4d4b73bff7a087ab32d54eb03fc3a68ca44397473f11541ec37a9af109eda4f9a01fa2b80eb7544231b9cd4ee71541c69d26533e6b1466f31e49ce02856994c8c5a37ddac731e62697fc040690d2330d8381a3570646c6a58828d45df82d446168d3e6a7cd3c08f1b47ad6873a796c6933684d916abd69a931f9a07615bbdfcbe46596f94a0cf01e26a7224684ecf43fa3c0256fdaf36b62776421222a2a8887e686bf14e45a0884f82756636690c08d5c0425ccb1951ff14d111e5b08f8580a9807ab3b376ee976536035c1c449d1100a7f376908ae770031e97d63a17a07d2c1115d3367065b3219dbda507fa01acfd636fffe7e95488db97baca644da68d57fb34014821a1eb950c8fc06eedafe4c79fc103c46e6e1e2cc99a046e7fac59241294eee21ef90657c4c1b10174957afc84b67608684820d44480e08c970dc8d35446ecd070fbeb06d6356efe870021f5ea5f2706c2b25b845698574bed4ba3bca0375aab165434129df7ebbe81145bec1499dabc5d8c172636645670405f14a3dea19875fbda6ab6d81e2901b9e486271e86a148af662ac35e06eb64c8d9c3901c1acac0d3fc3c5e2c9049ca3b10602c559fa41c06224178c28e41598b8637c36423268fda68c3dda708d14e24d0555dec1818ae61ec4847c73d0dc438ed3340451719ca4d6704a8d1aa5e0e0e8f87ce6be0a4cee000fe55667ac860df5e9efd76d16761240db06961e7e0799e81b2de4c59fbf84448690b9bc900b0c1243449a476a0e4158bd5fe1b325c11e8821cd5cf2bec8c23044256d89c01fa0fdc4e52e27606357cbd7a437178dd7882a1449dc449a1d2e4bc6dd0eb6a498914090d486c931a81204a1f3813f6058806d490ecd43f55584a23db77a1696b0eb08d3080baa8e66d1969ca2bc931231d963f45a7678804041a3776c8311128c765dc78592bcfba6556d4343c99adda27c82229fd111a674b2d3f6574a826a7e756ae69ef9e6c40dcab09e6ef25edf419fc8fe486e5429237c262c042636c595af90d461c10525793afcc4542c2d5aaff30b4b333abc17af9c54adb719a69d86d23d0ca6ea278495d8bd22f3bccaaea92c6e21306eac0f5f6c597d446686aeec1d81e9d585517647b6260a88a9cf9554ac5e50f831076a5080ab9b9b08c68b8641409e6e31fc6ab585270472e783f1bc3ad21c84f226ab62da11656c81431fa2301d1a8c39f58fa605d7a5a04fe4f5885b3c373de38f89cf28759843af086598354bb7511e797ecd2b0d01a429dc002fc7d8af8c1b24d6148b0b4798009556f737cc1eba953686f00f03018a158e165579b56d91804430414363c03d482dcc07931c86fe69dddd6e853df32f92691116be82f4821b651467e055776125acbc5310938aa5865ad934686d853c3996dfdc5823c9dec492d3b8dbac1ae070b3d9d9d284fba663877d9184033007280e350ea99b937b4b318e223b044887c9b6225c68ce1966bb6131b928f8b226e452931233ca245e7c676a4a6ca1936fa4fa16bbfb7b4d3c307603f2691eb5930e06b5236be99466fc070c4d240738cbd450a48aa111c5fca79391d4d8b31e98f1f2083851d3bf0036d78fec70b38a2882a81d30336055ba852aff49431b634f0d210e7002a3d702e5f849eb44000c58705371c66a95b0a70ae0b5b31a5c0fd7ebe83ac199bf67858b4b1ac381c9dfd27dafce7f929bd694ae78c8efbd2892075906f0addd62b78e62114889016b089bfa0155f0828986df616b4bd86803228cbe51960bf4f7760543db92a2c0627ae27e1167e2e7e3fd116f1fbf722267499e7927a1d3d33c25d38ffb1166140e70a998c0e2a4d5c2ec2eafef651f277c8c1ab45f88df32b52e4be8d7f58b229c4ae110fdc864aa1ed056e99e1b0f2a62e085c6b64480de3077c00dcd7876463ae8d1fdf3099679fe5138d4f93b533832c45865f14f9fa7236cb40211607fdb0d791f615590406a0db0da74201c2f73a07a008058c9eef73be86e08585d84681518902379b7dde757889c453772f034ba67f51dee09527b4ac26dd8410f95632f2345eb36999ecbb5cad61e2bfa4457f9794da2112eeecf5fe92ea3d5ea380db83f7fc78ea7867dae0620dc29848c0a3bc0ab0abc0f055091b2ba85c016c5be5bf35018848becf2573184dee8e6556e23e348de62237ad8e9193ff5aa0d71a11c07f7dba8479162db8bb014da86cba9c20f3b292f848304ca368a74f527bcd3eb8f3be0702128d4bef893c4bdad60590a253c1ba08f27ad484b2a118d2d0781ecfbd1e0f60d05a6c30f6c79675cca56f85f71f11af3f347e9a749ea223d8558609010e492ddfeaa5751ece6d13960b438af626bc65800a5cbd80df180b48bdcd36c0939faa7bba32a5f78debaa804eb51fde9719535696c536d146f1b0fd0a83849c3211819828294a8c6de87a2ac1482cccb9e33525d0cca03f58c5cccf49ab7bee93e9837e114b4c45c825d15a52e713c7b61467339b412b5a5f8c5086068e04c99d07142f2983a2560fc9062557a5557f3aaf6c625d13b266bd2fa3fbfec0ae91535fb3ef314ef3ae94b5879dba62ca75e3df25c1c54a7e64e55d7f5d86885e660700fe300b26139af2506f811186971a409b8f23155e11db1689a07311c9b9622a03fbf4746b028958eaa07511259ece4c5af0604db9f3c1c4e948cf815b67d0d5a3335d66aecc9354c0f7d93ba5b6999b3c7d7d076d910890a4e45aedc1ecb0526bc7338d6922c7733abc188b1e0fae993354a161c9ef3deb291b16faa6c34011cf68470657d4b0b85b5172dee220345808c707398c72228819d6f089d6e344bae2dacb58b259350b387bc340346d2f25d40c68f3ccd6c9959685b8233ca830bb5a748156cdc223204e893aa501cfdac12cdb9d7b7ca024f090c17b7aa9e8def893f5353e29a8951b8656fb9a4691327277eacf72d962432a3153baa902188bdb781bd77916790f9f95f56730360d49bb4827f7f1f0d372828faca220680c7477a51f6637f5a1d527303120ff059ac69c50df845284b800c175b44bc2267394236d12c71d15d9b99824f16db21d0abf4adcd059aa56f695fc9d2b3d0cc16898a1ec995b7bc95744de899bea46a530ef74fe2be8f8a2ef4b676ebc142320635f0054e83388facb718bc6400081b2555ea21ea4a3b4840850ec87d6990d3f25b9c65252f1578bc32a8d275de2f8411dd9aadaaed8917777f619c70c1ce020514b0790e0261965627dd5cfd91443fbc1f9b5086bec0d1b55c36c92852e242eb94b91c35e0d52e352acfea2cc1c963d971a28f13cb74707ebc67de170348dae1c8c6fb684bb8ff71daf9d3cd8f67bb943aaaf619a77122e36a92170d9c69db6894e77d0d1a75ae3ea97eba8b13432f7f490b2e4585b0f5ade525ca1503f884941f0a64e2e535c3199ecf55ff3e98f4f1bc040d3ad83d3849c26fc99745b768d9601f0bdbdb2e14024fa9d48883c5f622982d786ae529b153bc6dbddc71ace20f32802a5d50432e0e930d9fbafecdd32814c8990b7490a0669030f1b15dae5d8ee48378d4293d1885d6b53c2454df62f9271ee5d7269cc03ea6de473cb1f8285624708b31db90d3b158b746d07c8287bcaf83608ebbf02aa69fbd580b40684530f728d60bb29b1439784a689a8984926dd83f7b6f2c732213141290009e1ecab0b561be77e5380bd91d1def5689a8f07c2a77e6304ce7e0d0c0cd01fee5e7573c4fc44873ac9d713a0f9b4a622bc1f401f280c68061eba246ae36df501da68bb893740b2a4d001ffffffffffe7aab42bad5b3c631b21b3b5d6ec27c4ca9424195354d8c814c894524a29a5d4cfca0adb7700e77c3c1e9b2a1b8808950850089757527199d2fc6083c3665e2b73a2969a6e16be528e1d15fae2e3847a43f268dbb10b8dcf61350cca0d493948bfdff1b90d7a6a43b278c9f7759d6ce0e53e3dc6bee5347ee3d246a99bb8d69078a566f4e9394b0d49299e129929b4f4c44683060d1a34685c11a1ce4892add00c9d73a74c7d1a92339df020730e7d4113f21f3830bb43a121b931e7929933982ba4d7a63795e7ce0f36365d429921f954fbe5d0a05286e4d3a4bbd5bfd62f32b026b22a5b593ededa826a955efb48139df3a0c690983e8a96a711d13a16312446cfd5e616aa30247e92bdda5e391892f6673e9ad0b9fa426d5a66d6621662f369b6a7832aef6859657921c17f56dfe3b8bca9be0b899bad74dad0dba5c6e44292fc91f1123235c718de426298bbd09e23d542924a9f2bcd2aff779b9585a4cebba2c173d5721e61214989cbe952e62b24c8278b314ab6e27bb566941512e7af3e83d2a43eeeaf42c2e9eb70424ec8a85385fc480868d0d0616e14159253aad77d2da12ff27a0ac9592c960eb1c9a35dffc19663c4a314a1a4808c29ede6514763e03038781c1e213950061585e4a03166325da5d27eae06654692ba67aa91e5390585a46c2163a6b0750368233978fca29e90dcf1a367fbd79c53d4cb09c91ff437c7f77aff7a4d48f88d9a57d34427614a4c4852399a6ecc714ff65a4a2d812f7bab96d96e8bbf9576d49dbe141ba584648da2755dd4e2728c062a098927c3f78b4a5b27db2f242495c5919db74d8990fd83cd6324078f236d98161c24ea084949e63159aaa7367fe346080c176584041fa55d2b07f931a8a543c78e321264742d254aa83de47190c38687e3004977af638c153ce08b6256454850f94574f21c2223d1e2898f9bfe144f4d84242d2ab47243654ed1219c74d4ee4a0849498dd9c959523af91f84a451b12aa29dfd6013238cde4b1410129478f58b50a283db671749213ed37c7ccc079b18617824f3364600824b440d5de829a4c7ca59f65c24c9a4aa425b4c3fd81e0c7f3d5c24e9ceb6b143556e91e82b9bf3c4ba83d42ac4e8f0b0b345f27b5ed3ea26675632b548eedaa02b8976eb18f1834336d5a045e225cd3f71f97ea5994592baf34b39d1d8303f22e211868d1d470d0c1c20e5de73e0485924f7fd5bfe36a5582468cf98637edcee9843b0484cd2c3e68effbc226194bcab7429d4c94ed7704572c9f9aa5125e3a3bd1aad48d81ad1b33dcf146ab02249481bd9f6d99edaf31b7c18e83fbea855247e786dd97519cb36055b5491e82977c9950bed8b5fa4462a92ce3f2f6cbe995ebe5091a4f4bdedeaf767daa807354e919c27df9dcfe2354c61969395332dc58611428d5224ac89dd1483debbfb9cdc410d522475b25c7bd828923278ee4b7add57acc72b354491f81d73174bb58572b7f3c60e1c28831aa148128b215484878e392aa048d864aff51f4f5f7bfa13499e4f37e975fedd6d9e48ca233b66f24aba64b9353a9158ea3f07e9314877b33891a02fabe7206338dd2b6ee2a419454666659a48fc644ab6883fb9e9af1a99487c4bffb992c144925fccbc3aa66b44cf352e911c63ea78bf7c19f2a4352cd1ff67d754c21c449f5e9b72d4a044255b2909ef3a4fba418d4924af8f6c7f93ad86245a714b8d19ad0a5a565a9e1fbff3c1e61c468d4824cc86ce1746a58f592d4824a936f92a5e2148c8880a6a3c22c98407d36de9972caf1a8e488edf95af13a9e399ff1a8d4852a952d8cd69d13ab31a8c48d256593a3a78c0a8b1885d65b64b6ed63bc52a687d8eeb4abfd4cf853c1860e4b071351491d06b16ea44693ed870809cb5441447e6821a8848121eb6263a76dab45ce3108971bd5293990c919c72e7a01d57a355f9350a917c99dd6a9dacd598d586d4dd9041d5474d0d422449b54d5ab6b4c524ea1a8348ca1bf45fba70b24d481049d1e237b86771b994148804d55963e8cbdfb09b06c41d4efba9b01e18d4f8439257251395efeade5d3f246ae79329b4bb3e24a750a9e1e1a4a6acac061f1a519953b19bbbaf9c5ea649bc8cf2576d1b6bec2129c76388e5ee9cd543828bfeb354ed1fdc948744b1e071a13b7848f473bb0aaab943d267afbc98f46366da0e8995b23988d1f97490eb90703eba925241f54a7448d2a6527c7d4ee9f639245d09df146795c3a283655afbe390306a7a43eb4c8434e1909c4ba84d398dd21bbc0eb1dc905c613ac520e53dc76d48fcd6b05c514cf98bd8603a8f9e35242921be66c4a386446f2fb5d63971727346f26832d9bd61c9559386c498fb3b9f4c4143c2e820832cd3154dcd674858abca49b63543927fc8e76bb40c897179a9a74cdbe9c9909452126f99935c7fc690a0f42775aa366a53c490a46263fae4fc3024a8ff941edc7c30246d4a32698f135e22fc2f24bc08657994aad1bbbd171264a6f8bbec8e2fb2ef4292ae9c36e5545294d0732149fcf3eef674d5ee5b484aa1d36e89d80d15a4850475fbd9bf948504fb28f2fdd288d8b19024835c5163c24575e60a8999274bace573b08b159252b8fbc8982d74521592465f734cdc54485eb5b7d3f0149245ae7df88ae249c55248d2b90a1fb58d42b27b86c69c9975773423419cb6641d3f6ddca19074a7929acb5898d29e90a035683315436cef8424d52ea1196783ee9bf06e8fa7cd6039139262b61cc7b4f22524da897ef6110fb24d4a48562fdd3df24f4282bec77bcc9e4bc60c1292ee3f4e990c62ed1d21a93dffc28cd8d4ab182159e3c77dccdcc59c32925df3c90d69fb49ec2224c6a46be386b8ba94c9484cf93a2b9ce5fe6d899068ab41dfd2e9f53c84a4ce223ebca864a7a910122f06f1b2561a9f1384e4131a46798eb9863580901c7e94ca9c611756ee22495e5e189d945817c58aa29a3536948b04a1333aebc68a300de122d973d8d7eb18540e1aba4592d27572af84094f932d9263dedd8aa1d7a74cb5484e21cd7e63b8a9c86991b4977266919cf47283979aeb949145e2077131d7f7c9fd639178ff7ad143eb8f07164951c3f3890eeaa7bd22f9b554cccd5c9178c194d5b5d589c5ad603f58568c5554b74eda4a248caa9e15e1a144728a2b1b7486d2b8b1b24924e55f783d95a2c357e9d924d1c78ca6cd7d76570af37ef72bffe121dff6f811834012c848c823bd9190479148d6a449fa7d8a118dd107978d843cca1a068904755ee9435632070e46d41e81e4f840f688a4cc0fcb9c17b3c9b594c3032de41dcdaa1162b4a7e9530e909b8035226994ee925e56ea0c1823924368cfa6b3addf69e583cd30608bd0c5b2d95bca58d44ae61a7beab5edfe5cb3a68a144c1189a3630e1f434bbb52ff6043233b3c446e7cf88d316e808cd4f5052c1149276d64da864044ccc1eb802122c1e48d9e5bae774f9f0fce432498e8e841f687aabeef83cdccaccd10c9cb69ccbb5a95ac10c9a3f9c3e91c84a56d468864d3a4737d6b21c4bb360863c865f7122f4d1049a32afe7fecd398e214880419ea29e34459d2ed0144b27cc8cb6e9a37269d3f24ec78e770d19d3f63e387440dbfa1927bd21997ed436210fd77297b6e64ca3ed85acbf890ac292b77e60c7eb0d9b061d8d2f690a4af3fe6794a38403c728c7888dc08c99170e4401e0ee2811a03d34372c83bcb8df579ad291c202664e46f181c1e772061e8388f8335410c3c2a60794890a22b489ddf13a5e9e121d9b5d2812304e9048e23a1416387dd21c1f32ec77efb57603c86d92131e8b5787af2d277d6ad0e49a1569b9a2a75e61f8b0334688c801c0f1b01394887c4b86fab964935872411972c956b85cf7a914329a69895ad9de53adbd0eccdd07827bb3f290e09325ae6705163eaf179640c0eb4c121d1ff6c2d9938fd99a42309e443a488e8c8e15116b037248ad0585253c8fcf7a70f1cc6060ade7398a0df73e048343724a7f7facff78ffc3f7d1cc2318648b621a937698bd524636c285cba94f5b214b4c3ec353deb7aa6a6b9fa8db686842bf33b2fedc3286b6a48fa8fd91965948eb56ae5170f781d8088ec78000a48f0000fa4e0018eb033b018e66931d4c5c5d65d63ee34635852a6eb3424c85d5242a5a78a973e434352a6981a44692e25e3636748d44c26baf2b998faef83cdc386c191036444478e4333246e34ed2c2693caf9c461654874d3dbf1d4d54686a4d935915a2a271be18a181b6536b03124681a25934e49c55195f2c1a623870e343124fc6dd8ffe76facb33ed812bd18250c495faf753a943a61eab881d0434ec042303024a9136a5e3b7c0e7d0bec0bc97a75f63eea4d750cc5f010f142d2d6c9697dbd9c34e6695d50bff47abebea92c877121c92bb9a8eccfbeea9b82d8169243a9c9184f29dd9117869816927446b532b50b164ca3366259c8f7e23de72b739880068d7c046243c4ccb090584aa7ca72e35f2149a6f23d6979d7439f5648dc141de7ed3d754c31ab42829631bfcdeca172fc6c54483e8d4965fabd447ec70d3e053685e48df5a91b73560a495b156fb4cf3485cea4122c0ac9734a5f868ad1c276b29d60662445b7be1d593a24181412b573c905651e26d813122baf43484f5b3db9d2a0c143302724af5ad038f1152394da84c452e26387269962f0ef0c634282a75d877ad5a613a725248ba59b875cb0d4ef2821e1f7d6366afc1854105a1292e62b34ae9db07a192121316f285babd2b163c68e90244706b1dade8d90d8f983be7cca5a199b7575d685bc055f1da541fb360621b72224783459a6634a7eb0e520c7f10803191ba7bc8d91b343644d10038f08181989d9d3da35664c84a44b49c5ab279ddc21246caa3955d70921b92df9880dcfd22d088929425fc56905910121f182d8e0715e32a5fe900949693a6c3c08466e788046efd05d245f549351b3ab7b6a25e453dac181c540e9224984103a9c9e56b820ff60eb349282ca45628fde7cadf76a32a38643c4d0a0709128724bde3b6fb74832d72ead1a6725ab52b648101ece62ccb35d5be95a24a5b5a03926ac74774a46d122c1d2338e50d1a9592428315ef263c891e12b5924d685aacda3725490672c12935cda2f59bf41981a16092a6613aac6bf10d42b1233cf6c4dec8fb9855950ae48d2902998d293a95624862b4d2f4fb9a9cc5a41b1a2b1f1d0da8e91cdb414fe7259ed7e18d16dad2261ec3f8d6813f9e2ab226184da534ad5681cf15391e85aba1564dda92046053aa9cf8e0ae24aa7e0ce4ce6ee55de62eb9fd025f364f61b46992231e82f5529126d3387cc986fc49d5ba448ae985cb62ca520e37dd528923d4dfceef3b7258a64cfc12bdfda5f8790ffa04291f0d5256490d3d1db3bb77182310a1489315e6b105e631bd6f38964bdcaec121e3fa994792231c63ef78be1c3a712eac45d5f2d165a75b51e1bd47590165ae3c45e5a9baa6a152e55b00e19b7ac346e6aa84d24679a88baf158fdd5349134f699f734ecca44d2e8d091b17d726d1d2612fe3dc930272f9e56579748cc17bdeef9254b2498576788e9894cadc1418e2f68d0c0418e9090aa44c228655d32da6becee3e284a2497e820b4e88e26911cd4d3452bafa494c94822c9c3376c29cb99bece4864e556da6e75d166e33bf3546ced784122a9e5729af7d45ea11e9194152dfba99359c636a58c7244c2a86cb132bab444de675423924674a6cd22da352f568c483419add447cb4f4d9e5a44f2ac5ada7f5529478e1014a02212fdde4cc8bc5de27c4d4472c570dfb36e9d3cc9bc0d1d858804955d515d435b8ef10f916c177f955e754324ff09cd93ad9d6d4f5e88c40a3ae9f9cf232d5f4788abc45c2cccaa861a537dc9e54a7d3588e4bc399e224eb67c0cfaba1c094708dc06c8c717a50491a46f9b9fa46c52dae281c06664dce4b2c86646eb39a1f76392318048d05cfbe9474c7fe0c6de729d67a5987684d84fca2e3f14d3aeb5b5324cecea53a3db87cf22ca53cead3e2426ad7c4a9dfcdc9c151f926b9498986c6c0f899531a8b5a810a5db7fb085fc088ed74372e81feb9ca307fb1df390a4ae675aae25af74ca0b0f49e762a193f06a11967487a4cf4166bb1a8db1f5db2131e5248307551d73ccb93a2408d9612f1b3ec2ce628a0e09ef6dff97a4c7a918d5a3e69098efd2c38652e1d7a27248cca4aa6ed64a0629f70c2a0e89b3b189595dba2b5d76cc24c63454756d75ed76a3562263f2644a8b32b7068886a4f564ea9b9aab640ccf90602f36a2191235a96e6bcde1937b07cb9094f298afbbcfc99475321c7e2ebac856c6905c49b8dffb5ba5a7114d22ca5230c03024081d74eed39f73c6fc8021b94bebba63f7733c15fc42b2ce5b8ed3eb8b29d4e885d53e5c6eabebb3d64e4ec53f99da98fac32e246d45d3a27739c8852475233f9375e6b94ac12d24eea985d57027b62de714b590f45e95f3ca878d1ba32120c932805948eacf953bfa7c72d800c30307c8c708080eb0a0cac959cbbd8c66e6d2d9323ba8598b3f5f21f9e78377c74c8819f100c10da0151244b6c75ca7f1cb6611ac42c2b9e9f86fd3225221397c0ad66661f7a14b5348fe504d35af16292465ffe84f19738e090f4621b1652c73dc4e3aea6cd737043423f9b2c36f7c7eb3b82a1e4221e1baa4dba9e5cbd1729e909ca172baf0184507b9a2131274a85395a9c6262485eafcf857252624ec9ea8bc4ffb12123f7b3ced1a4a66e92025248b9bb0b2bda0a6f69384e4faf48b8d51838424bbb0b3683a7690397b011e21d9920e4a69ff731376d1bb73a45d46e255dc0f6d322fa8c84570577fd35d3c2623596da3a9d279a3c96d899098ad6383c7d21092f4a3c6d8fe4248984debcbb56710127ef4ff8770d5d4ff7d008190ec9e545c396b1709a6f565af16f3283be922f983aa93ee41a3acf6b9480e964153aa67cd593c641c2e923a76d0a9ad4a255f0b8ab1778b24e14185139d53501654670bbc42c3ea9258aa2e8f6bdb371d7e56d722498378909a555dccb2b448eed3eb758bcfb1467fb348b63f99e36515952c7e74e4649118b25eb3a6d31b8be4fb52de992fd3b048aee095ba4c7ce52899ffbe22f9d386ce75864a5e9f2b92dbd7747888052163b5e258d1786e15b9a594425732a5d9bc1f9c2aee5291e821c47ad6517321e46b5424f6d9cdc6ced775a9ea3bc5d140678adedd2b54eedbd28add6be9dca43c2279a548fcd9edb52432cb9776a4484efbac4987c74de44691a459d75a7dd511901b202468c18922e993c5f7702a85c7b8502406b1b8f1736e911d7b1d078ae44ef2a637d56f8659f7898457b9cba5ffe289c4922d197316f3b019af1389a38275cc7b9b62799cf0b498690eeefeeae03691aa3c1d733b67ee033c908207a0e134815faa18f13d09fe863141ef28bb4c60222f912cbacb839a8fa774e996a882c67aa757c8675df25422e93ba58a4bd3d93051c8512229ddea2ea5ad6a6fcb24126fbe2a378db05c2ade278944932adad5722529ead98b4492322163a9bbb1b49d1f2492e3e5a7c58bccd47b22c23d22e9e2cb730765f69f29758e688418c708a4b108451c2de6cd26026fb10e39b1b7f6b26c6e275b4363a86b329d394424cea96c26315a9b4a2767708748921d211afdee3f6e6988e4b306212de57a85481c79b71664a999dd4888848f1ebbb3b483785283718348eaa8a661feb4df20229d9c2092a39ed0b911421fa2e350041788c431795f651ea6f4080122f97f3bce5ede7f484c8dd6211bd5ac5ef643a2b87ed0d9d5e572a80f89716341c5c6cd946ebd717c309f5cfd375beff6b0e3f4902064e9ffacab109a5e1efc1ba963e2f090d8fd163c2c373cc914e21d189d84ef25df0e265549261d6465ba701dbe1835551d4f09f9513a2467c784454f675dc139944333cb564cb52dcad786f59492b8b00a2787c498e682a90b998b436207cdfba27398abbc1e1c123f66d690f9cd6cc2bd21b13c465ebffd67fe0d9a716e48162d167337065dba49916b4361743ded28951e1b92fd2eb976524a6bd83f5cc831d9931a1234fc68f17ead4a95744672284da7b3589a912da52149de2c6d0adf9be24a6848bcf2787a29e65a1e9d21418de5ea2a5bf5dc793324771252654e8e1215f36548127f9d329f869d7a9f0c49ae9e1f43925e693ad13a55a72486e4a4b63f89fa20fae26148106ad3a7f01b967a0443e28e29ff6cba2f245b756e39d1dc0b89a7be6443e35d4850751117923b2f7fbcb36c2169d4d27297cc9fb3ac8524613279dd958dc5370bc9a7f5ac928e8b3229c2427206bdcdaa397485c4fcae954689f9c9d2b142c2fbbfc6a7bd0ac9492e5490a62f5e10a242e2efe6d9d07123fb3953481cbd7d6a63d82dcf395248ee6b110df515c5435148d0bd4bd7d920733a6624a80d2a27995a53160f149242e75dedcfa51cd53c21c9ba928cba0d66b51927248afe8ef6addc1c2e4d48381d2f850989d1ab52ca399350da2d4b489a191d47645b9490a46c674b28df4d3a589290e82ae234dfc585fe0a1292c2789eebfc34b75d3942c256902132251d44fd4648ae4b7ea772c3c78ebb8ce40df1f094463488b58b9014eb4e4c6a928ce4bf4bfe575dfa4b44486cef1c56b27965be212455acb6e56752325b0b2149cf73b824ed35b9414870d1f9221a2b4c45078404bbb495acb76285cc2e92f4c6376ec5f6cbe92279844e91a744b85b251749a1d1b73e6b5c1c63b677ab0def2d92b369dc6c56aa2dc76b8b449fcbad4efbd62229b3e84bd5a5f152891649b2c4a94ca76c4d27679194ff1533445a8e1f592427cfd941caa7168b04bd95ced4c81416b72759e92b922c94f03f4da9b77b572488c54ef318c34793b722e15352b29e3d3c87ce8ac4990f7fe2d62d07a15524cc7c98568ed92633aa48ea3ea57252d99fde662a92fec744860d4a5424a7d57de77c8630f929925256fef9f8a02992f6d388fc0a9622318d6636ad99713b8da44890296789aeabf814711449c2bea2efa8549937144572bc9882de3b8f39c7301449b5dbf715c3d53d73a048fa14f4dd3daa8e8df789844d1e965d9e3ae7ec3c913476f7669f43257d5d2712642c15742afd5c5a324eb83ba3edc9de44a2adccc918a33f8dab89c47c7aa69ef2c5fd6426125b73ad785fb0cb9461227984a73aa54248192f913cf6415ab2ad72cb2d91a454fc0312c0a2001605f0ea1a90ee93492833020d18c3c6c807182038285034cad23008a011911d210400c018870209f0624464470a06008007c3ec000387b171800088843cdae13942742080055e00c00b0710c20b2fbc00800614d00111101d26010020c30b1191436300013023c66910000066c4788e101a06f042000c4040c87b8481cc8888878702bc00c0020c802023211f22363c3c1090b3d0714316e871808c8501121663e840af4090111b1f22c6c30301e90a0419f11c375a91ac97ffb2e76728edf083fb6cc7d98d0ff11003c43d1c04bd0e9162233840588120236280f80e0f0f04e42a465ee4c6df2840aa22297fc384870fe525d65424a86c277490db8ba02231838d884de68dbfc19ba748509676bec76312dee94037fec6def81b28820364c406c88ee311f2364074dcc881344d911c5ad34feddd6ad4bf4a912c72736f365d262992832c8db172181942bff7c851245b16adbdcc7e3138406e20a72812bd54af84997e0db37cb09d1d22a6810cc595292ae377efc5cbc386b9182428922b69d70cb539f4c8f6c1a6d681fc44f257d6c5ac90f960e30aa42792beb4a8d84b0f2b2ab6225f642792a4a7e41616caecfa45427202a5ce2d9b6813a989cbc461e2ec126689be82c85dc5705b39d9fa5bff9ff7ecb10fc3aca012491aafe4754e192512fc2e2ea978f31ebc3a424e224125153796285dbb17492241e8ee58a3e49685cc91485a758d39cc8fda746b422241af52a807bde42392462c7ffca78b264d85413a82f554b550bb70f7165de5a277859d873defb31189b1fe35c660c26444b258aedb08a1a529e54b107211c92c9b316daf08eb2cace22bd6cef3a8b83211097aae7e73929a21358388a498b459e80e974324ab751edf9cefe3de6788c498ad141fb35588048d9f4bb7b4a920b24488c4dc382657193fe6bb3f5a908348522f266f2998c915d5992012c4853eede49e6ad38c69c84024291de17ab9358c3e6d0222393c6cc5b449445ece3523ff90d43bf72294cc793c99f44392daa4af336692f529da87e43a59badecaf496a6241f700fa887e475cbce516e21a395f250acbbef3acd0a2fd74ac14372ca551df7b2cec1e3503042be0478213f022283bc43991dac0e7ba8a8a568655bb73197a5c2a71c4774b6b0705cd17103e107920e496339770c624a5453ca907348b04df2b4c28496136a39f0b1d43357256444870a320e9a70c0c262b9574a96434d54e482bc52b146e5827df986041d725b8352b547bac168b4a1201b6e0da786448dbd9d96738ef7ef9f9169484e31edefb4e8a93c8e86a48cfe9d4a83ac6f4ae61992355d06bd25376e9d4c33180dbc916528434986637c5752163b6e111d393ac76034c4603470061986e4201f74ae895cb45bf1110986deddc25a57ce0c19a7b14beafe2e467e216947bf3328db30bf5e7a2141c82933654acf2e247cce1843bfa7242dbbbd4472e198f5b1e416125b33659e65ba0a99a516125c83e6145a2a877a511692e6936ce99f909997935848aece9369d943c7d4f415124f8758102145e766437e04e48a905648da60ebe1fef22a24260f6a8ea44262e79bb68c9a163a88a790a4f36fd40e1fa61412532cb318de354b955014927fee947edda98b6b37a5194917e6ce3ffe9f5db8402149561af5fb61e51392722cb1a051ef626a921392cecd2da58f9c4d48d4a4645305d160a6f35d3221f194102ae7b7bccb252458b6c5aefff89a904a68f45a521d94b69290b8792bf3b2e249399dbb4442e2cfed768b6cd1417957461e21713683583efb7852d7bb3442b289cf6ea2d4ca629eb20ca3f1822c42929c6b2759dfb8fa24cc4292911cf62acc768ab59d532e899060174a89505b61fc4b974348b0764b1b572be9bb8f97424818b96fff98d64e742c8360342e819024b2f942d3c6888068ed22e1377fce9bcbae57ec74916816f34ae89874b948ca95b46254ecf17091f8b9f4f98a92f16e9188aad6c89a6ab7d98b6a0af27e2ee5830d6d91a4fb7137e6b1bb5a24c8a03af4cba593e2222d125554057db1f3056d8c9011b30ef91190649c45e2b8b55f57d8ee1c51164922bb25b7ddfb316c8c05ee952a8959b7b6969c70cb5c52b4086171f88a646d13bfe1b2ce3ee85d91a4937dd8d10bb62239c58c231f67b4ad9e901549db97ddde63b61159c2015791e8b32a325ffa9ce1525491942be8edaae9914189539114de446392335f3abc52405424e93cd3f8d151b31cbcc353247696eeb58b0c4d818bb01449e2bbc3662e96f29c435224e5da86174d1bd47785a348544d3696f132c3a75e1449a964b8914bd9cc7628123de9af9f4ba2b6620741917c9d3955cd9998860b3f5114f9fa74d113499db2eacc98a61349e2ef3bdca6c9bea890134939bdf3fd52b6f9accec04d247c90df0ec2eecd72ae89043119d55625672251467f34adb9622249b58c58cf9d72c58ae12592572e8a9231bf434b2458a8bcbb6ce94a60fa1f3359ee9012c9f957ef3b763b7916dd7ad8e1c04924970ead7439e9ee59eb3cecc438944482b6d1714c9ab6cc631e4622a93b958a9ac6cd2e730889e430eabae542c59cb5c1c347247ec79349c66a1b1d9198c7bc4643534ef741d888a4d05439e9b8d6222392774f9c36b7b332155f4462c778a793f80a5b296d838a4894f9df6b191543c6ac0c4c446254d310f3332122123777a630da544fd3623c44f2c50dbaf72e6bd8110d1a888648cefdb2f239d907512516224973bad267d5ea129010651009ae692b7ce747f5130b82cd30e51efbe440741f0444e22915622a3e966687be38c43f74fdd9d74174ee02fa21e1e292da7610f17c6b00fb906c1f6354efd0413e246ebc53b1826bacd21e0f1d1672021484817b4812ca74f8c8a49e47bf912324f589180b500fa8ea74bbd152bc59079887a43c97a97aae43a7f81103e221e137e70d15327d873288c676b6abec90283a9f7a260b551d122efd96bd58e55d1ce990642ae8a435c3e951da3287e4d5d0edd4a21fea3713500e89a396468c4362b4aeefea9c4396d8af80705876f3a6b631d3e31b9257dc437c9296bf53de0d89233a53dc65a80d494a8f1ccd95fdb2aa886c48ca2f159fcc646b2fbd06b442c9933aab2131a97bd3c1e4f6ba85cf088976a8c4ad2643f26030128944e280288e0273cb1413130000000c209146839160349c299a2c0f140003652c183c3c2c2024161416168fc5c380201c0884826140201c080402c1401044516b1c6a7ccdb854e8878d85ff8dfa7777780097f8b9a9ad0816a40dec0076a9fbf669ffd74f05013a005d385edf7f1b0913d0cfe88e8de8912e171121bfadfdf0b4d49a8ab7977b5ab52ec81f201f7241e88e2e569a9293f6dfe93d8d37caa56d1478068c553f92dadb82eb7c35b10b6337f61657f29502306283326d9a20261b9cd617b63b308d44f296afcd14635d596f1a9a5e1464bd6ca79254d66c22967b2f8d68ccca3eaa193960a1836b387c6854e82e21cec2b092f8f119923156a74f30e044bee7be117215499ce7ac48c044906c616da9bd49ad057325222174d3e8af6f8f24955ca740357c2222d217708729b6799c000f3bc850e5b29fc43087b234ea99624c9aace84761737ea94c58b268d3251f42fb646693f96755ea1958b3144632ce162993f068f0658a2627a4da4660379f4317e69d0bfa1baae4f8d7f2c10c30274a084de11b8ea5dc010c111bb4ed8177b9cb6c182644a305659862cf29d0507ccee1bb31e2deeab775e2dc79a95a5fd6571d517e2167d32c8478ee6ad7fae82014ff1962adafbbbac94caad15dc2989f28f84fbf0043e575e6833842f06aa5761a761b8739b02633b744acc67d2d96ba9b71891c4dda12b7bb429b13307af3f4d8c2be6980ba7fc1e0a7fa9ee818c8f3b851d7e7b994f2a00e0637b396ac550ac62f6baaaa8cc2404a335a96cc4fab0fe73566b669a2b2db23e8cc4e5724042d4969afcc77c86cedc29d8429e736f1a4deab704a8517379c60c3eafca007c0fe1183a600d7596575dcbbfe9a0f376696480aec0e8292313ed0952fb83c5eb514a4aaa6e3edc19bd41e3276ede6cb85384620300ac832db1c55ebfc87a047be2a94b3d95caccfb977463c4c959387e1339482173d6f140b1b074355f956ea161a5e915459cc925ec7be7504a099d9e9d164a40c525cd14ee692f99c8187fa8041eef4adb45233e5e72e1947e8a6a8aa48f8ca1accb5a6d548ff86bbf25677030153d9f216e0738c46b36e5737a52822c023083a28fa6af881617e40e96684376b93b96be4fca806360f6c889b0dfdc9c20a18aad9efda2f5d8521b94887053c11062d901ec5e3f05e0c90f75c308d5560ed7653d2234e0afb184e88296059d08031a9bbf4247364a8ab4a47525aa8ae2d0fb698083fb1f50ede74c7936c3d94fb2e2b99c329b19cf506eb208829d8b3355841006375a8407f0d2c48866804d718bd0210100c17724d9c117569d72d1bb146faa5c5f06b09de70f6e3e2bf8c032397dddb6809ce37117bd6382203a1e182a759e39e84fc22a058824d86b5303217ea44aaae60f7551fc04f58c9d89c11081c3b46aa092d54a8e133e2690a6497e6139f974082ca479396288cd9c52bab7587da734970f3d052c5ec3b26f34042e69ba7e5840494684438efd4a197a7e54e990f7f475a4ba464f6036728e00326338401632888a3547b16d2fb90b77895bbb6d9a5c417de47e0101d421c3901b5410b39504f8ea5ed9fb1fdc6cf6bcdc0a32be5f957ed84fd371e5829853b4b8b2f21d30657e342749cd14b044b21b0795d1ca6c9109016045ca13d7e408135edfd57543a8652ae2e85e15671a8ad8104a5f394aa442173ef29299eded50342be15eecc75b1e054d943fb50ce0566557cbab96821ffa438d4da5963fcfce65490c0d6b924303bc93532b1bab7e2052ac71de389424ec1e58467dd94c7702a0dc454901ebd3f9a3e75df45886ab41d1421ac395464d6f29122c4aaeae780d119dbc6d6f2c9031938010df768432567afc2a94570df45401f2e371668a8007525c008460f81c31483c1087d6027cbaef56acf5d158aeae228f69c27d4d46d62a090e225e8470eb3efdce82015a05fa9feada7e99bc9fcf5e7a87de0e660bbf38c53a415d6c0f5680d403869a227fc7349dfbb1d8ec23849fa5c78a01ee5b453ba01ba0200000c14e53bb00c50d4dcd13b18375624e1c43b603b383abf9b3cae3f8d2ed77b973b23c95013055f13dd2d9bd6822cb54139615eae8e1fbdfd35b6428d0b0a3444693f8f58ebebc6dbfe559e8737238452e85863459a348f3d865d87e1f5eb8539c1c7b201d84d80d4da616ecc55a6d990bd7804ba691864e6667fa7d0acbd30d68a2bac8b20a1a2486d2c35d4bbd4497914e11888a6534e3207f796f5cc8b47200f73550d609a13f6f2a88a0d90ca263de88fbb6c8136f4bfd73f620d5579998225d2b0ae50232ad85f292195dbe2a94fbcb78f669818e8b5aa1c502439f4a4acfc52bbed68c3c3f105960fd074b87fc9a884475e37fa23aa901af5fa315ad2f2c4c2f8a81712a21917810e996b86162edd77b2d84635d7035446d0c7cf3a563437c9bfab41c246317e4c6b0600c354aee48956f5bd7f010c9f4db4a13bc0c151b46ec11d16a76ec24578b3f0a8b75b114c87c675923718ace7c621db01baa5aa385a8c0358e56937cce97baff1ebd215568400d0cf508f41971725a1e63914460d89812fdc976b45d3a1f2776bd263f46515c4c54a23ac71e4b544cd86354f274dd8c3ced6960c6ea092a39287183ccf66a9faff4c09c4843f8a4ea323a1a47b0e38a38fc8fa1f9a426ce8e1cb08d8609d4aaf25c28ccf8649a73e4869ab08b31e9378908d1434779916278699790d18cb43d021eda4da59a6c5ce8c49c192304a92d940a22f7930c0e3252623339e51a212de2cda6259d7b38ade15c416e5ef2d29d0f233b3c22551428302c07cb26c193b7e8dc6ede4a3e64f8835e258c676df8bd23bed2632d9674e0b349b4d1d427bf6c80ea4d4e85efcc9ff07753b5af2eda6011e6e34295be272f99f3d4cbbd60bccc20bd21f9ef95415cb4fc87bc342dbb87ea1fd126aa4f357586dd9cfe700b1093e051f1e28ac758c3dc1368818cec2d15f31c453955016a32c1e75a4c358f9c4909607ee4d53779656ee0ccef992dfdf8afcfdf59c0b903611c03e7d79c91e93178f352f133e60694785767fd9a9b36c7fc8c1fb3c5bf25a016ed75695c0e480d3ddc787a59cdbc6b3cfb29a8d768fa992492ae46843237da6b2da41f990923ea3bddb784ac4288dc0293bd66b1adf70f888ae03884ea71e74663cea4eac30637b455223e2f4592dfb21c81429bf94a64bdee6e1cfa769a5fd8335158c1468fec501a12de7c67be8d7543069457b255bd282a778b1b49f525eb8e1e94e70106833bba789e7883636dbe887cc63042d740f2e0de1a39ea95895980a2b540b06ae1fb7d8198fc44904313447d581a2207b30fdc80fd5f183534318c61d65b96f8af9cdb2bc46f3b89b5070784977d83d706aa9ded3e5dd87d1940999535852b1cf3306be204e988f80b21c777c0009363a30ab3108d130c6bce72574a492f4360c0f1c145654a7962046020bf983cc32a4210352f6d256e3a1d2b43b3fad94f5920fd52613aa12bb8d43c9d42a826ee2ec16a0c1b9f9da201e026e835052c2af9f4247833aa801bb0f8ca62a0c4d8a76863a91b1a3624eb188c52847d59e7db212656054ac4571c4ddcc049935210457899f2f8ce097ab5855af22678f306b83e10019d7aca9eddbd2bd3d6f91130f289f99bd03bf6ac190d6b8fe40293ebc67c7292b63b477a688447fab9823431c4a9c8633de0a297d73d2bba7fc08ceda6c51dba930b17e193c030e3d96c8bde7fe85e4a80b2e293af1a8e5ac38082430a04bd219677a69b11663ca48ac83440cf0e2605cdc44f814c129b46dc309a71b42d9ce68e46f8d244ac248b92938d566976812eff054f851dc13d8169350909a92be8994343f0df293f40d035853d1face91efb6fb232c184db7605fc1845cd10a807878460246b5ab5e7871c91889bce05e4d3b739ad342ce77fa1a60470888678f87201f07e0bed2d969c71f38451dd680887b7a74aebcca99a920cbfe9ccfdebabefe36280155596df7cb205a1e06ea59929e2fa2220232d3c8dfc9d70771172a6155a033f370617030b08bfd394802ed7d9f1061294cb2d9c99df7bf80fe6a8a86ce94c0905c15f0cd218e6949e9bd1bc51295af05a38828f1ccf1b2841c66edd896d3e845d0501641a9661029f30476e8d364e3aff26e35a4eb09762b094703563979254e48470b0d99bd116d32297fc7d150bc7267f374fd850f80abca562f1946e53b1a909187e998c123cc32659aba79a595c9828502b8938643e18120e4fdac883d6c475f672182d15dbd8ed878ee896d74cb060c5e454571bfd66f0bc3090e8bc6157f7572745f95ffaaf9bf969b7c1cdb5395a93201687b97c055a71fc0b213fdb0472642a97229defef859124e81d510d072deb87d96f3feeecd10f1a0f74f95667e3544f3df3c4c74df2debe4f2174b75e83af62346ea0423452134034280e4221baeaf27f8f43633662364a68952d160e0db3766132a882ff0b919a3ff79c949ada5836536d01bd9b0bd9b142025306e9d4dc8d0b29ae46408d31db6eac06d381e8e9e07bded4714edc1fa9eb331694d88818106f790d953505659ea2234baabcb75f581bfcea1a4ab718bf9551a82a24a1c1df08cbe9db9512f39022f27c800ce7fe7aaed9189ea19e51061bada6ef05308fc7d0a3da7b8451787ae76eaa52cfb3963c1bdaba6f02cb53ab9c82a04b8f15c6a5aa1927cfc214bd3c367ab39000f1d3dde43ae79538a12902d848da45363f92e64aedcd1b2d84f4c828d99349e53d4a92957e6083d64178a39d809215b8dbefcdb030c554da87175b44fc2252afca894ffe7aa3ad282df146d333b2039695bd17260d267ece3ed54a6d42d1620212b4f16cabccdf815594a57972410edf6e686cacda6a21273f2a67568d49ba33cdab90cb4ba7f3d915b8407bdaf13750fbca1957e25462662b508611603f64c30b23008e4fa72108b9aa7911de513c07466f4c578cc39475deddd2f8586bd48053841ee411b046adcde50b704356a4764ad23284513a90b1554b9ec14912345e963cbcfbea2c1d96f4bbd7bca1e78306568036dc63aa586dd792797a458153817d1372560221076fa6e451fb98fd933f76f77799cd5663245128dd73eb5925c57b2b7ac33e7bd7bb7aa28988ae7ae3d7eb38fce2e12be88d1de988ce2fcf5917f16fd8705d7290e71910b944a85d80b4433aab5c4f5bef9b7b6b9c54024db663ca5633f4da23f85dd4fe9a277910413741108afe9239621d9df62f05964a5804a80fc7d1390a46715540ad1c72d0ab103ec87e8e2e4487346cf3847cd54ac228c0ba66d5ebd688af5710788654a26a323a30b4a0ce9238191263a8d90c9239d20ca89bed000ac9778bc3b7a2699ae9aa09acea722d3eb0d0a34a59bf3e8d3f23775425aba7a3cdcae2b9da67ee3449c7b302c106685ec39f12ca055f8fdcb457c464374e1dc58812cd7826ad281b02be40b47d9c244b6a73259103b1a9c5af296da29025f29b301d1845e7c6dffcbdcd2708fbf22d95fa31e250aa2d6004c20ca0eeff473ce5c5d545c08eab4ce25a7fc300d96e0e518fa8dfee7168f71a803a714ef1eec9f556600f75fd9e2dcaa15e42bec11dd78877e0f1f0b371d9c3908dfa010a116ea50efee21d16212465864cab371208aef322945ad403bf47fb26f88b6da0de308a30f70759c70823841371973f69429c62dc4f343490ea1703de7ddb865e349b1cf8595466639bf9008c5c7b990b503767c659e47a5523d56167064ae0c2f380653380dc3c386a0583b3b07918c597c0bb5af9121908da667b8f9db58e5fb85598ff118a44e3ec5b772be98df31e00017cf4729488b9bc5c925c23e202596b2d46a035dd4bfeeb852443981be5231fe84b7af35d49aca3d854d6c443cc5c992e4d0b32496e6853f56c061509d3ccdfba1e508d9bfc01a3e85cf1e51af8f84fcfa3f4f9dea6e1cb1d8f6fb368f580d3d68ed0e2317734556d3d178f15eeaa4ab8b725028d6bfa268497b7ab35635dbec936c7a2fdb2516ecf86a51a20cb45e19a3d5971f779d8a1a51d84a5eea0a82ffe6033533408a78d2b4bba9a19a172ed0322910430966145fb41fc6d066fdf90f185472c46d4ea1073fa1fa0e3acc67284b05b968e6743c1fb853e3819a7aaa509744e94f234184bf7ce935c7c0f209b173a02d3d9ab1207f56e534432e52c83b61046ea156e37342d8b476ce92baa681e9872703618d1faa2d00e15b385e1236c8c1e8c70255e61f20049da460d3099b745f6c38bff49107af8d27db07ba61d23e205e79c44472ce535ccbfa1a7f67600b0c381fba1ccd8ac7103326cfab35f53c5cfff2fd65e1880047a6170875153d6a3cca2e026676a098aa18ed034628f00bf4d34e858171f2b88bace20407e1a1f50eab2d79913e25f3ec35890f2c17f1588557889af603d98a4e15b322dc9ca9bd581ce70fae9211732f9215548bb9f3ea3ef32c7276fe27361331829d5dbe6413accd6851babe707a1bc35af1abf6cb077a7333fea54b3be85dd96ee528bdbff5dc9b6265279f86bce2d29f77c8d485ab2dc07e86842345061ae5079e8fd898752efb1c36aa52ab1c5bbc5c099f44e7885682370fcf67bd67e6ce82db1f38c7de232d949ea52b6fc63ff68c43dba28a2da26e664f38bd03ced0b99e58f3be3ca2bb31c2c4dbb3a44952735a237158d2527e79993cb00e8ee0a65e757700675f072127014b97ba503a157fd0b044967743e3d22fc080d89f27430da48a509fce067d8c05a5d3e63c8566a7572a70d1461caa126b978c485dd42bfe7e2794154cf9f330fc5e739763066b0f02fe0a044bd8d9831583b3d0c652589a1f3bcdaf8da2842f2dac19e9aa2943bc77984b6647926d8518f2d853d8c3ca97b34db24b10cc27cd2111fc0edea7bd98a3731e48208e8c8b03753194d15cb1e4f19a9215eab642916d442a5f0fef993d44eb62ca0e5bdcb9f1f4c4b4327d3c12e5a9ed1d2550b9c6c04f9872a1e7e8c484c9ac6f454bcb8700c2e9885404cb8d2b0440b43a94c54ffd48313dc76cf66f87f13d23f68e445f8019e6df6f33223ee41eb4d80c317dad5f6584200e62d728403c81754362746ee4cd89d81a7c3685513724068c857d1539543c25f30e6928310a7c5ed9528f6fc320a1037b4d0c912884d6f4d0a2f7f8b806755bedcd5603583ece3d1f39fb174e163d0608b5d8c4cb3498168738ce95afe312dca7af9badf39e1c81e382a1b78a16fe3592f2b3bf9aa13a0e0be7e2f808b6f933ead246c1abc49ec4d863b529f031ae16b889a124fa882c26cb4551c44ab40a6d3886fc0fc770d531f1970bd0146c1e6044cb567bf922a822a8c3798e6fc03f42622d5b0b749b597b9a1d8a8ed51c338f84abbe84e697b2aa0a89c871ea934a85fca03f6a07f97f80580e797570ac55674756084b0f768758fa98351298c5d88322e0b01f5f78bbf4e9c75fd2d7798f091f1c9960e6ce11892ed05a1a9a7cda052c80705eed2da348504c6092677d4b2f334b063db3623dc0b151d25977250ef027bf410531c0073ecc1d247cd06764229c0f03be1643b90e484fcd6c5f9adcd5673d9fd52b541f7420bc351c0174687b71f3da99c2db0365b7336a33d8f12441283e07c5cc393ed8da916ad77a94771f9975127c2b03919cba5348c8ec2eaccf2557f0133d56236bd76c5cb2b150e213deace351b1848de2c6b1043fee0d6435ab25a37a5655cc6109e46d2d64e4433882de4078bf17b73fb89d37137c6a392fcc2bcef04c4fa694f89994fba57a5146721c381c4c23f9700ae0522f49f4449451439093ef12550d4aa21c288c88cd08110261672e9870c10b598abd80298158d07d50419e23cafa02af03eb204baa5d681f6fd24f03e384f89c49723b960164703f74baeda59550491ef7c44e0d2a4ed139127eb84b03f19f942ffd90fdeee0c346f48c423d64ffd60b8bb66662e81dc6ad4f933ed402dc3cb2d5613b7fa332aae4f7ec88173b038f1d61e52a614df61850d23750769184807fa0b8bc9e39c5a12120a06fe33d28ddb8646b28d6913039108e9e0a0175a6eced11d1e13c8448d6be19ce7ad6428f979293b05b172c81632381203bed414ec1ce27095a037f411c583ea0f3832d88003da86a6e0122821dd7c6754c45732d7e229cfa1dc779bea7f47d82c516f5c9f3efdbe45066ab25617f64b5e59911d790e50a4402e21ce4013a4f0109346a93b106e00c34b4721bc5433519f2ce484122be5ef161cb59fd3562d9fb6c177c751a5946772ed51c4dea9aeba3908b7eef2bee1b4a6705ce80ffa4052cc652dbf47706b881d6f96772c57322c03f908376287f56c783cf3c2b8097db4f0a064b65041c288b6045b2260fc748a59b9860a999d3966021bc469161c29d38b29055f778280666b93c7931efb257f6bd1c291a28789356dc4121d3bcf37040ebfae154f2c399c14080a1978e1a6be841058684ffb630d07826d2357bb6cc029921070b46942385a0aa243831798ac0e09dd9d2edffa3c1d1572a5e09a5f52ef12acdaec8128461ec836820a70cd3e55eab21c9c0bbcc64638381c6283426b589a2b2809f7b9de10b1d74fe73cf42dfc46446b0c3175d686eeb387a24c9ea30330a74b96026e2477a436a7f522e5acab45d42230fb51cab8dc7c7c8e9670e533aa0e3af29cdb29483d3fb102c79245aeea29729723757d604cb4bce8fdde1f61bfa7329894db2baae42bbabc58cf1557d4d5fab32aa03ce930745e6409ea3e81d90141e2a6a47a514f27f62e2498a4020069140e380ebd9163b0a2c288e0c1c55655b2635b2ca7aaaa9381d16effb6da9d2e398a4b4287037786dbb7e91c8f59830cebe62fcee944032e3b093b0aa93382763c17b55e88ac1995e63ef56f0a28009c100118b1953b84fda551359b4329f34dc8b999e1544006a48413d902825bbf0b3fd70b9e2d0a9cbbdad762d941bb830f24dbf73096f6184db2a786f54c8e15e2e344347db8004c974491ce1a81b32bdbe9bbd3cd2a6471c8a7161ccd6d794ab04ffa082568bd259a1b7bcd96e20827092b4eb607da3f34288dfdd6d878f6dd3c2088cebb538e9b475b880f776ecfcef53e1b05d3f0616b203a7e16a02e48322c746960430482f9ed97c41f5ef3385c8cc78caf0f0c53db31941826d4004e263d471449fa1ac2fdad8422a964294121fc357fd6410f9f7ac3ade81ab5484379c44d68930d6729194c65688984a087490d3b4449a5ced925f479d458bdce660c212812f518d639706db13b7f9a1558e944dff261376b2a181320451f6b80cbbae3a4640361e24724a9b9c4a6a534c455859db3d5f2f19e875313aa003d556f4d70a1e50547871048756b42dc681110e8d51be7124182a3d2a06f00e72d6a5f52c0452b29b0867d95a02063181af1e5c2965d36ceaeef1d24c8d15da594ffb2179ebe39976a924e9b27a886b64be04676f0600d860f73a4a665595d1f9e8636e4f5b5772de85604f1bf4fad34e1c4ea00d48d35f89a90698a5edd2a4bf352e67ae2621421592e0e31bfc6df1de7c3c21d3e9cd9b07da377c089cf668114ad3ed4f66f296ed85de3fa2e91bfdd6ac8fbafa519d6f98b89f15f7f2f30dd1c059ab6068258acc7d35f29a018e11186d6cfb5a7af3a757909bcc41b88844256b7919f136c7628fe195a941d22e16fb40c78d949dd28a7fcdb75abcea847c7b73e5a6cf84523a11dde54e6df24f45c888a58fd21252dbd1bffb186084e9a6b7014ad0bab5b7d962bc24a0d7426d0d92ded6a8a75402728ed37d8fa61006e7d0089e195ff13f10bc275718cb14930715915e1853cc454ea2af53ea328e30851830a12d2cf8fbad6397cc45b5d97b2a35a5268d22f0f8009d1bb664283124fda276b36811ec240e40c617a3531824a66aefb40f7ff900296c44876d3177a9080534e52586e8790fec0305b94912944cede2643f1379668f17a321c2aea588cbba2dca548aaf7dcbe1222657776c105f345a6bf90d20e3e9dade9a6638eefd95482a341088607b8a003f18371b2a1d77d5305fa027717d1a5553a91d3e293d77ee5a036ecab52b90936933a7087eadae5aadfcaf4ef8c74491c2a9e6efd3231c332b0ba382ff7b10617b9a5af43829da1d8c30174b6a95e19a3250eeb28c9732b882436a35", + "0xcd710b30bd2eab0352ddcc26417aa1944e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xcd710b30bd2eab0352ddcc26417aa1948c27d984a48a10b1ebf28036a4a4444be2440b598d56f335b5f7155d9b03cef974a1a23824f49049675dd63800988661": "0x01000000", + "0xcd710b30bd2eab0352ddcc26417aa1949f4993f016e2d2f8e5f43be7bb259486": "0x00", + "0xcd710b30bd2eab0352ddcc26417aa194e2d1c22ba0a888147714a3487bd51c633cf5030be27db95e3a080000": "0xe2440b598d56f335b5f7155d9b03cef974a1a23824f49049675dd63800988661", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb30e5be00fbc2e15b5fe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e": "0xd17c2d7823ebf260fd138f2d7e27d114c0145d968b5ff5006125f2414fadae698eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a488eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a488eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a488eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a488eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a480390084fdbf27d2b79d26a4f13f0ccd982cb755a661969143c37cbc49ef5b91f27", + "0xcec5070d609dd3497f72bde07fc96ba04c014e6bf8b8c2c011e7290b85696bb3e535263148daaf49be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f": "0x88dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0eed43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27dd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27dd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27dd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27dd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a1", + "0xcec5070d609dd3497f72bde07fc96ba04e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19500b42ace3b5fab73c6265656684020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a1": "0xbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19500e3a507571a62417696d6f6e808eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0xfe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950246b6699fb8b8db670617261808eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0xfe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19504a8e42157609c6c86173676e80d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d": "0xbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19505905fe216cc5924c6772616e80d17c2d7823ebf260fd138f2d7e27d114c0145d968b5ff5006125f2414fadae69": "0xfe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195066b8d48da86b869b6261626580d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d": "0xbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa195079b38849014a07307061726180d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d": "0xbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa19509d4a4cfe1c2ef0b961756469808eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0xfe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950c0cadce9c18510226173676e808eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0xfe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950c7e637254b9ea61962656566840390084fdbf27d2b79d26a4f13f0ccd982cb755a661969143c37cbc49ef5b91f27": "0xfe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950c9b0c13125732d276175646980d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d": "0xbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950d62c40514b41f31962616265808eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48": "0xfe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950ed43a85541921049696d6f6e80d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d": "0xbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f", + "0xcec5070d609dd3497f72bde07fc96ba0726380404683fc89e8233450c8aa1950f5537bdb2a1f626b6772616e8088dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee": "0xbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f", + "0xcec5070d609dd3497f72bde07fc96ba088dcde934c658227ee1dfafcd6e16903": "0x08be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25ffe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e", + "0xcec5070d609dd3497f72bde07fc96ba0e0cdd062e6eaf24295ad4ccfc41d4609": "0x08be5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f88dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0eed43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27dd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27dd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27dd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27dd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d020a1091341fe5664bfa1782d5e04779689068c916b04cb365ec3153755684d9a1fe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860ed17c2d7823ebf260fd138f2d7e27d114c0145d968b5ff5006125f2414fadae698eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a488eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a488eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a488eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a488eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a480390084fdbf27d2b79d26a4f13f0ccd982cb755a661969143c37cbc49ef5b91f27", + "0xd57bce545fb382c34570e5dfbf338f5e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xd5c41b52a371aa36c9254ce34324f2a54e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xd5e1a2fa16732ce6906189438c0a82c64e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xd8bbe27baf3aa64bb483afabc240f68e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xd8f314b7f4e6b095f0f8ee4656a448254e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xda7d4185f8093e80caceb64da45219e30c98535b82c72faf3c64974094af4643": "0x010000000000000002000000697ea2a8fe5b03468548a7a413424a6292ab44a82a6f5cc594c3fa7dda7ce402", + "0xda7d4185f8093e80caceb64da45219e34e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xda7d4185f8093e80caceb64da45219e3c52aa943bf0908860a3eea0fad707cdc": "0x000000000000000002000000697ea2a8fe5b03468548a7a413424a6292ab44a82a6f5cc594c3fa7dda7ce402", + "0xe2e62dd81c48a88f73b6f6463555fd8e4e7b9012096b41c4eb3aaf947f6ea429": "0x0400", + "0xed25f63942de25ac5253ba64b5eb64d14e7b9012096b41c4eb3aaf947f6ea429": "0x0400", + "0xedfb05b766f199ce00df85317e33050e4e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xf0c365c3cf59d671eb72da0e7a4113c44e7b9012096b41c4eb3aaf947f6ea429": "0x0000", + "0xf2794c22e353e9a839f12faab03a911b4e7b9012096b41c4eb3aaf947f6ea429": "0x0100", + "0xf2794c22e353e9a839f12faab03a911b7f17cdfbfa73331856cca0acddd7842e": "0x00000000", + "0xf2794c22e353e9a839f12faab03a911bbdcb0c5143a8617ed38ae3810dd45bc6": "0x00000000", + "0xf2794c22e353e9a839f12faab03a911be2f6cb0456905c189bcb0458f9440f13": "0x00000000", + "0xf5207f03cfdce586301014700e2c25934e7b9012096b41c4eb3aaf947f6ea429": "0x0100" + }, + "childrenDefault": {} + } + } +} \ No newline at end of file diff --git a/bitacross-worker/docker/multiworker-docker-compose.yml b/bitacross-worker/docker/multiworker-docker-compose.yml new file mode 100644 index 0000000000..081077de84 --- /dev/null +++ b/bitacross-worker/docker/multiworker-docker-compose.yml @@ -0,0 +1,257 @@ +services: + relaychain-alice: + image: docker_relaychain-alice:latest + networks: + - litentry-test-network + ports: + - 9946:9944 + - 9936:9933 + - 30336:30333 + volumes: + - relaychain-alice:/data + build: + context: litentry + dockerfile: relaychain.Dockerfile + command: + - --base-path=/data + - --chain=/app/rococo-local.json + - --validator + - --ws-external + - --rpc-external + - --rpc-cors=all + - --name=alice + - --alice + - --rpc-methods=unsafe + - --execution=wasm + environment: + RUST_LOG: parachain::candidate-backing=trace,parachain::candidate-selection=trace,parachain::pvf=trace,parachain::collator-protocol=trace,parachain::provisioner=trace + ulimits: + &a1 + nofile: + soft: 65536 + hard: 65536 + relaychain-bob: + image: docker_relaychain-bob:latest + networks: + - litentry-test-network + ports: + - 9947:9944 + - 9937:9933 + - 30337:30333 + volumes: + - relaychain-bob:/data + build: + context: litentry + dockerfile: relaychain.Dockerfile + command: + - --base-path=/data + - --chain=/app/rococo-local.json + - --validator + - --ws-external + - --rpc-external + - --rpc-cors=all + - --name=bob + - --bob + - --rpc-methods=unsafe + - --execution=wasm + environment: + RUST_LOG: parachain::candidate-backing=trace,parachain::candidate-selection=trace,parachain::pvf=trace,parachain::collator-protocol=trace,parachain::provisioner=trace + ulimits: *a1 + litentry-node: + image: docker_litentry-node:latest + container_name: litentry-node + networks: + - litentry-test-network + ports: + # TODO: maybe not use 9912 as port + - 9944:9912 + - 9933:9933 + - 30333:30333 + volumes: + - parachain-2106-0:/data + build: + context: litentry + dockerfile: parachain-2106.Dockerfile + depends_on: ['relaychain-alice', 'relaychain-bob'] + healthcheck: + test: ["CMD", "nc", "-z", "litentry-node", "9912"] + interval: 30s + timeout: 10s + retries: 20 + command: + - --base-path=/data + - --chain=/app/rococo-dev-2106.json + - --ws-external + - --rpc-external + - --rpc-cors=all + - --name=parachain-2106-0 + - --ws-port=9912 + - --collator + - --rpc-methods=unsafe + - --force-authoring + - --execution=wasm + - --alice + - --node-key=e998e728d8bf5bff6670c5e2b20455f6de1742b7ca564057680c9781cf037dd1 + - --listen-addr=/ip4/0.0.0.0/tcp/30333 + - -- + - --chain=/app/rococo-local.json + - --execution=wasm + environment: + RUST_LOG: sc_basic_authorship=trace,cumulus-consensus=trace,cumulus-collator=trace,collator_protocol=trace,collation_generation=trace,aura=debug + + ulimits: *a1 + bitacross-worker-1: + image: litentry/bitacross-worker:latest + container_name: bitacross-worker-1 + build: + context: ${PWD}/.. + dockerfile: build.Dockerfile + target: deployed-worker + depends_on: + litentry-node: + condition: service_healthy + devices: + - "${SGX_PROVISION:-/dev/null}:/dev/sgx/provision" + - "${SGX_ENCLAVE:-/dev/null}:/dev/sgx/enclave" + volumes: + - "${AESMD:-/dev/null}:/var/run/aesmd" + - "${SGX_QCNL:-/dev/null}:/etc/sgx_default_qcnl.conf" + environment: + - RUST_LOG=info,litentry_worker=debug,ws=warn,sp_io=error,substrate_api_client=warn,itc_parentchain_light_client=info,jsonrpsee_ws_client=warn,jsonrpsee_ws_server=warn,enclave_runtime=debug,ita_stf=debug,its_rpc_handler=warn,itc_rpc_client=warn,its_consensus_common=debug,its_state=warn,its_consensus_aura=warn,aura*=warn,its_consensus_slots=warn,itp_attestation_handler=debug,http_req=debug,lc_mock_server=warn,itc_rest_client=debug,lc_credentials=debug,lc_identity_verification=debug,lc_stf_task_receiver=debug,lc_stf_task_sender=debug,lc_data_providers=debug,itp_top_pool=debug,itc_parentchain_indirect_calls_executor=debug, + - TWITTER_OFFICIAL_URL=http://localhost:19527 + - TWITTER_LITENTRY_URL=http://localhost:19527 + - TWITTER_AUTH_TOKEN_V2= + - DISCORD_OFFICIAL_URL=http://localhost:19527 + - DISCORD_LITENTRY_URL=http://localhost:19527 + - DISCORD_AUTH_TOKEN= + - ACHAINABLE_URL=http://localhost:19527 + - ACHAINABLE_AUTH_KEY= + - CREDENTIAL_ENDPOINT=http://localhost:9933 + - ONEBLOCK_NOTION_KEY=ABCDEFGHIJKLMNOPQRSTUVWXYZ + - ONEBLOCK_NOTION_URL=https://abc.com + - SORA_QUIZ_MASTER_ID=SORA_QUIZ_MASTER_ID + - SORA_QUIZ_ATTENDEE_ID=SORA_QUIZ_ATTENDEE_ID + - NODEREAL_API_KEY=NODEREAL_API_KEY + - NODEREAL_API_URL=https://open-platform.nodereal.io/ + - CONTEST_LEGEND_DISCORD_ROLE_ID=CONTEST_LEGEND_DISCORD_ROLE_ID + - CONTEST_POPULARITY_DISCORD_ROLE_ID=CONTEST_POPULARITY_DISCORD_ROLE_ID + - CONTEST_PARTICIPANT_DISCORD_ROLE_ID=CONTEST_PARTICIPANT_DISCORD_ROLE_ID + networks: + - litentry-test-network + healthcheck: + test: curl -s -f http://bitacross-worker-1:4645/is_initialized || exit 1 + interval: 30s + timeout: 10s + retries: 20 + entrypoint: + "/usr/local/bin/bitacross-worker --clean-reset --ws-external -M bitacross-worker-1 -T wss://bitacross-worker-1 + -u ws://litentry-node -U ws://bitacross-worker-1 -P 2011 -w 2101 -p 9912 -h 4645 + run --dev --skip-ra" + restart: "no" + bitacross-worker-2: + image: litentry/bitacross-worker:latest + container_name: bitacross-worker-2 + build: + context: ${PWD}/.. + dockerfile: build.Dockerfile + target: deployed-worker + depends_on: + litentry-node: + condition: service_healthy + bitacross-worker-1: + condition: service_healthy + devices: + - "${SGX_PROVISION:-/dev/null}:/dev/sgx/provision" + - "${SGX_ENCLAVE:-/dev/null}:/dev/sgx/enclave" + volumes: + - "${AESMD:-/dev/null}:/var/run/aesmd" + - "${SGX_QCNL:-/dev/null}:/etc/sgx_default_qcnl.conf" + environment: + - RUST_LOG=info,litentry_worker=debug,ws=warn,sp_io=error,substrate_api_client=warn,itc_parentchain_light_client=info,jsonrpsee_ws_client=warn,jsonrpsee_ws_server=warn,enclave_runtime=debug,ita_stf=debug,its_rpc_handler=warn,itc_rpc_client=warn,its_consensus_common=debug,its_state=warn,its_consensus_aura=warn,aura*=warn,its_consensus_slots=warn,itp_attestation_handler=debug,http_req=debug,lc_mock_server=warn,itc_rest_client=debug,lc_credentials=debug,lc_identity_verification=debug,lc_stf_task_receiver=debug,lc_stf_task_sender=debug,lc_data_providers=debug,itp_top_pool=debug,itc_parentchain_indirect_calls_executor=debug, + - TWITTER_OFFICIAL_URL=http://localhost:19527 + - TWITTER_LITENTRY_URL=http://localhost:19527 + - TWITTER_AUTH_TOKEN_V2= + - DISCORD_OFFICIAL_URL=http://localhost:19527 + - DISCORD_LITENTRY_URL=http://localhost:19527 + - DISCORD_AUTH_TOKEN= + - ACHAINABLE_URL=http://localhost:19527 + - ACHAINABLE_AUTH_KEY= + - CREDENTIAL_ENDPOINT=http://localhost:9933 + - ONEBLOCK_NOTION_KEY=ABCDEFGHIJKLMNOPQRSTUVWXYZ + - ONEBLOCK_NOTION_URL=https://abc.com + - SORA_QUIZ_MASTER_ID=SORA_QUIZ_MASTER_ID + - SORA_QUIZ_ATTENDEE_ID=SORA_QUIZ_ATTENDEE_ID + - NODEREAL_API_KEY=NODEREAL_API_KEY + - NODEREAL_API_URL=https://open-platform.nodereal.io/ + - CONTEST_LEGEND_DISCORD_ROLE_ID=CONTEST_LEGEND_DISCORD_ROLE_ID + - CONTEST_POPULARITY_DISCORD_ROLE_ID=CONTEST_POPULARITY_DISCORD_ROLE_ID + - CONTEST_PARTICIPANT_DISCORD_ROLE_ID=CONTEST_PARTICIPANT_DISCORD_ROLE_ID + networks: + - litentry-test-network + healthcheck: + test: curl -s -f http://bitacross-worker-2:4645/is_initialized || exit 1 + interval: 30s + timeout: 10s + retries: 20 + entrypoint: + "/usr/local/bin/bitacross-worker --clean-reset --ws-external -M bitacross-worker-2 -T wss://bitacross-worker-2 + -u ws://litentry-node -U ws://litentry-worker-2 -P 2011 -w 2101 -p 9912 -h 4645 + run --dev --skip-ra --request-state" + restart: "no" + litentry-worker-3: + image: litentry/litentry-worker:latest + container_name: litentry-worker-3 + build: + context: ${PWD}/.. + dockerfile: build.Dockerfile + target: deployed-worker + depends_on: + litentry-node: + condition: service_healthy + litentry-worker-2: + condition: service_healthy + devices: + - "${SGX_PROVISION:-/dev/null}:/dev/sgx/provision" + - "${SGX_ENCLAVE:-/dev/null}:/dev/sgx/enclave" + volumes: + - "${AESMD:-/dev/null}:/var/run/aesmd" + - "${SGX_QCNL:-/dev/null}:/etc/sgx_default_qcnl.conf" + environment: + - RUST_LOG=info,litentry_worker=debug,ws=warn,sp_io=error,substrate_api_client=warn,itc_parentchain_light_client=info,jsonrpsee_ws_client=warn,jsonrpsee_ws_server=warn,enclave_runtime=debug,ita_stf=debug,its_rpc_handler=warn,itc_rpc_client=warn,its_consensus_common=debug,its_state=warn,its_consensus_aura=warn,aura*=warn,its_consensus_slots=warn,itp_attestation_handler=debug,http_req=debug,lc_mock_server=warn,itc_rest_client=debug,lc_credentials=debug,lc_identity_verification=debug,lc_stf_task_receiver=debug,lc_stf_task_sender=debug,lc_data_providers=debug,itp_top_pool=debug,itc_parentchain_indirect_calls_executor=debug, + - TWITTER_OFFICIAL_URL=http://localhost:19527 + - TWITTER_LITENTRY_URL=http://localhost:19527 + - TWITTER_AUTH_TOKEN_V2= + - DISCORD_OFFICIAL_URL=http://localhost:19527 + - DISCORD_LITENTRY_URL=http://localhost:19527 + - DISCORD_AUTH_TOKEN= + - ACHAINABLE_URL=http://localhost:19527 + - ACHAINABLE_AUTH_KEY= + - CREDENTIAL_ENDPOINT=http://localhost:9933 + - ONEBLOCK_NOTION_KEY=ABCDEFGHIJKLMNOPQRSTUVWXYZ + - ONEBLOCK_NOTION_URL=https://abc.com + - SORA_QUIZ_MASTER_ID=SORA_QUIZ_MASTER_ID + - SORA_QUIZ_ATTENDEE_ID=SORA_QUIZ_ATTENDEE_ID + - NODEREAL_API_KEY=NODEREAL_API_KEY + - NODEREAL_API_URL=https://open-platform.nodereal.io/ + - CONTEST_LEGEND_DISCORD_ROLE_ID=CONTEST_LEGEND_DISCORD_ROLE_ID + - CONTEST_POPULARITY_DISCORD_ROLE_ID=CONTEST_POPULARITY_DISCORD_ROLE_ID + - CONTEST_PARTICIPANT_DISCORD_ROLE_ID=CONTEST_PARTICIPANT_DISCORD_ROLE_ID + networks: + - litentry-test-network + healthcheck: + test: curl -s -f http://litentry-worker-3:4645/is_initialized || exit 1 + interval: 30s + timeout: 10s + retries: 20 + entrypoint: + "/usr/local/bin/litentry-worker --clean-reset --ws-external -M litentry-worker-3 -T wss://litentry-worker-3 + -u ws://litentry-node -U ws://litentry-worker-3 -P 2011 -w 2101 -p 9912 -h 4645 + run --dev --skip-ra --request-state" + restart: "no" +volumes: + ? relaychain-alice + ? relaychain-bob + ? parachain-2106-0 +networks: + litentry-test-network: + driver: bridge diff --git a/bitacross-worker/docker/ping.Dockerfile b/bitacross-worker/docker/ping.Dockerfile new file mode 100644 index 0000000000..50ea4b7723 --- /dev/null +++ b/bitacross-worker/docker/ping.Dockerfile @@ -0,0 +1,19 @@ +# Copyright 2021 Integritee AG +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM alpine:latest + +RUN apk add --update iproute2 + +ENTRYPOINT ping \ No newline at end of file diff --git a/bitacross-worker/docker/sidechain-benchmark.yml b/bitacross-worker/docker/sidechain-benchmark.yml new file mode 100644 index 0000000000..5158cc3588 --- /dev/null +++ b/bitacross-worker/docker/sidechain-benchmark.yml @@ -0,0 +1,27 @@ +services: + sidechain-benchmark: + image: bitacross-cli:${VERSION:-dev} + devices: + - "${SGX_PROVISION:-/dev/null}:/dev/sgx/provision" + - "${SGX_ENCLAVE:-/dev/null}:/dev/sgx/enclave" + volumes: + - "${AESMD:-/dev/null}:/var/run/aesmd" + - "${SGX_QCNL:-/dev/null}:/etc/sgx_default_qcnl.conf" + build: + context: ${PWD}/.. + dockerfile: build.Dockerfile + target: deployed-client + depends_on: + litentry-node-${VERSION}: + condition: service_healthy + bitacross-worker-1-${VERSION}: + condition: service_healthy + networks: + - litentry-test-network + entrypoint: + "/usr/local/worker-cli/benchmark.sh -p 9912 -A 2011 -u ws://litentry-node + -V wss://bitacross-worker-1 -C /usr/local/bin/bitacross-cli 2>&1" + restart: "no" +networks: + litentry-test-network: + driver: bridge \ No newline at end of file diff --git a/bitacross-worker/docs/README.md b/bitacross-worker/docs/README.md new file mode 100644 index 0000000000..c0e42e94fa --- /dev/null +++ b/bitacross-worker/docs/README.md @@ -0,0 +1,25 @@ +# Knowhow Dump + +This folder contains documents and links that contain some (potentially outdated) information about the worker. +Use with caution, as this is work in progress. Hence, the code is most likely progressing faster than this documentation. + +## Useful links: +### O- / Ecalls +- Ocall Bridge: https://github.com/integritee-network/worker/pull/293 & https://github.com/integritee-network/worker/pull/299 +- Enclave ecalls / ocalls: https://github.com/integritee-network/worker/issues/279 +- Abstract ecalls in enclave: https://github.com/integritee-network/worker/issues/286 +- Abstract ocalls in enclave: https://github.com/integritee-network/worker/issues/279 + +### Sidechain +- Sidechain functionality: https://polkadot.polkassembly.io/post/111 +- Sidechain flow: https://github.com/integritee-network/worker/pull/627 +- Simplified sidechain sequence, of a user call and the STF: https://raw.githubusercontent.com/haerdib/substraTEE_diagramms/main/sidechain-sequence.svg +- Top_pool sequence: https://raw.githubusercontent.com/haerdib/substraTEE_diagramms/main/submit_and_watch_sequence.svg +### Parentchain +- A rough overview of the architecture surrounding the parentchain block import dispatching: https://github.com/integritee-network/worker/pull/530 + +### Runtime +- Enclave runtime: https://github.com/integritee-network/worker/pull/472 + +### Non-worker related graphics +- substrate related graphics: https://github.com/brenzi/substrate-doc diff --git a/bitacross-worker/docs/diagramms/block_import_sequence.svg b/bitacross-worker/docs/diagramms/block_import_sequence.svg new file mode 100644 index 0000000000..369cecb4ab --- /dev/null +++ b/bitacross-worker/docs/diagramms/block_import_sequence.svg @@ -0,0 +1,4 @@ + + + +
For every
sidechain block
For every...
For every
parentchain block
For every...
For every
extrinsic
For every...
For every
shard
For every...
Parentchain BlockImport Queue
pop queue until()
pop queue until()
Light Client
verify block
verify block
import block
import block
! state update
! state update
Node
Validateer / Worker
Validateer / Worker
Substrate Node
Substrate Node
Event: New Finalized Blockget_blocks(last_synced_header)
finalized blocks
finalized blocks
Parentchain BlockImporter
push_to_
import_queue
push_to_...
sync_parentchain(finalized blocks)
last_synced_header
last_synced_header
Sgx Runtime
Sgx Runtime
Sidechain BlockImport Queue
new block
new block
import_block
import_block
Sidechain BlockProducer
create 
sidechain
block
create...
create proposed_sidechain_block
extrinsic
create proposed_sidechain_block...
Top PoolState
calculate state diff
(no state update!)
calculate state diff...
import_parentchain_block(import_until(sidechain block -> parentchain block))Untrusted Listenersubmit_simple_header
Ok()
Ok()
send parentchain extrinsics
send parentchai...
check time
check time
(if_author == self)remove tops (shard, hashes)
Ok()
Ok()
retrieve sidechain blocks
parentchain header
parentchain header
pop until(parentchain header)
blocks
blocks
peek assosciated parentchain header
sidechain blocks
sidechain blocks
latest imported parentchain header
latest imported parentchain header
Sidechain BlockImporter
verify sidechain
block
verify sidechain...
load_state(shard)
load_state(shard)
trigger sidechainblock import
latest parentchain header
latest parentchain header
trusted_calls(shard)
trusted_calls(shard)
get_trusted_calls(shard)Top Pool Execution Loop
intervall trigger
intervall t...
claim_slot
claim_slot
list_shards
shards
shards
exec_aura_on_slot(shards,parentchain header)execute trusted calls(trusted calls)
state_diff, executed hashes
state_diff, executed hashes
sidechain blocks,
extrinsics
sidechain blocks,...
broadcast sidechain block
broadcast sidechai...
Stf::execute(state)
updated state
updated state
Executor
write
(updated state)
write...
execute_indirect_calls_extrinsic(block)
Ok()
Ok()
write(updated_state)
write(updated_state)
For every
parentchain block
For every...
For every
extrinsic
For every...
pop queue until()
pop queue until()
verify block
verify block
import block
import block
! state update
! state update
submit_simple_header
Ok()
Ok()
pop until(parentchain header)
blocks
blocks
latest imported parentchain header
latest imported parentchain header
write
(updated state)
write...
execute_indirect_calls_extrinsic(block)
Ok()
Ok()
import_latest_parentchain_block(parentchain_hedaer)Stf::execute(state)
updated state
updated state
apply_state_update(state, state_diff)+ set_last_block
updated state
updated state
remove invalid tops
Ok()
Ok()
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/bitacross-worker/enclave-runtime/Cargo.lock b/bitacross-worker/enclave-runtime/Cargo.lock new file mode 100644 index 0000000000..ce53c8dc84 --- /dev/null +++ b/bitacross-worker/enclave-runtime/Cargo.lock @@ -0,0 +1,5349 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex 1.9.5", +] + +[[package]] +name = "ac-compose-macros" +version = "0.4.2" +source = "git+https://github.com/scs/substrate-api-client.git?branch=polkadot-v0.9.42-tag-v0.14.0#e4ed74b0fb6c2fd5585f55c2702b97b56d99c7f6" +dependencies = [ + "ac-primitives", + "log", + "maybe-async", +] + +[[package]] +name = "ac-node-api" +version = "0.5.1" +source = "git+https://github.com/scs/substrate-api-client.git?branch=polkadot-v0.9.42-tag-v0.14.0#e4ed74b0fb6c2fd5585f55c2702b97b56d99c7f6" +dependencies = [ + "ac-primitives", + "bitvec", + "derive_more", + "either", + "frame-metadata", + "hex", + "log", + "parity-scale-codec", + "scale-bits", + "scale-decode", + "scale-encode", + "scale-info", + "serde 1.0.193", + "serde_json 1.0.107", + "sp-application-crypto", + "sp-core", + "sp-runtime", + "sp-runtime-interface", +] + +[[package]] +name = "ac-primitives" +version = "0.9.0" +source = "git+https://github.com/scs/substrate-api-client.git?branch=polkadot-v0.9.42-tag-v0.14.0#e4ed74b0fb6c2fd5585f55c2702b97b56d99c7f6" +dependencies = [ + "impl-serde", + "parity-scale-codec", + "primitive-types", + "scale-info", + "serde 1.0.193", + "serde_json 1.0.107", + "sp-application-crypto", + "sp-core", + "sp-core-hashing", + "sp-runtime", + "sp-runtime-interface", + "sp-staking", + "sp-version", + "sp-weights", +] + +[[package]] +name = "aes" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" +dependencies = [ + "aes-soft", + "aesni", + "cipher", +] + +[[package]] +name = "aes-soft" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" +dependencies = [ + "cipher", + "opaque-debug 0.3.0", +] + +[[package]] +name = "aesni" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" +dependencies = [ + "cipher", + "opaque-debug 0.3.0", +] + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom 0.2.12", + "once_cell 1.18.0", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if 1.0.0", + "once_cell 1.18.0", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "0.7.10" +source = "git+https://github.com/mesalock-linux/aho-corasick-sgx#7558a97cdf02804f38ec4edd1c0bb0dc2866267f" +dependencies = [ + "memchr 2.2.1", + "sgx_tstd", +] + +[[package]] +name = "aho-corasick" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" +dependencies = [ + "memchr 2.6.3", +] + +[[package]] +name = "array-bytes" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52f63c5c1316a16a4b35eaac8b76a98248961a533f061684cb2a7cb0eafb6c6" + +[[package]] +name = "array-bytes" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b1c5a481ec30a5abd8dfbd94ab5cf1bb4e9a66be7f1b3b322f2f1170c200fd" + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "async-trait" +version = "0.1.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +dependencies = [ + "proc-macro2", + "quote 1.0.33", + "syn 2.0.37", +] + +[[package]] +name = "auto_impl" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "autocfg" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" +dependencies = [ + "autocfg 1.1.0", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base-x" +version = "0.2.6" +source = "git+https://github.com/whalelephant/base-x-rs?branch=no_std#906c9ac59282ff5a2eec86efd25d50ad9927b147" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.13.0" +source = "git+https://github.com/mesalock-linux/rust-base64-sgx?tag=sgx_1.1.3#dc7389e10817b078f289386b3b6a852ab6c4c021" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "base64" +version = "0.13.0" +source = "git+https://github.com/mesalock-linux/rust-base64-sgx?rev=sgx_1.1.3#dc7389e10817b078f289386b3b6a852ab6c4c021" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "base64" +version = "0.13.0" +source = "git+https://github.com/mesalock-linux/rust-base64-sgx#dc7389e10817b078f289386b3b6a852ab6c4c021" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "bech32" +version = "0.10.0-beta" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98f7eed2b2781a6f0b5c903471d48e15f56fb4e1165df8a9a2337fd1a59d45ea" + +[[package]] +name = "binary-merkle-tree" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "hash-db 0.16.0", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitcoin" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5973a027b341b462105675962214dfe3c938ad9afd395d84b28602608bdcec7b" +dependencies = [ + "bech32", + "bitcoin-internals", + "bitcoin_hashes", + "core2", + "hex-conservative", + "hex_lit", + "secp256k1 0.28.0", +] + +[[package]] +name = "bitcoin-internals" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" + +[[package]] +name = "bitcoin_hashes" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" +dependencies = [ + "bitcoin-internals", + "core2", + "hex-conservative", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "blake2b_simd" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" +dependencies = [ + "arrayref", + "arrayvec 0.5.2", + "constant_time_eq 0.1.5", +] + +[[package]] +name = "blake2b_simd" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" +dependencies = [ + "arrayref", + "arrayvec 0.7.4", + "constant_time_eq 0.3.0", +] + +[[package]] +name = "blake2s_simd" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e461a7034e85b211a4acb57ee2e6730b32912b06c08cc242243c39fc21ae6a2" +dependencies = [ + "arrayref", + "arrayvec 0.5.2", + "constant_time_eq 0.1.5", +] + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding 0.1.5", + "byte-tools", + "byteorder 1.4.3", + "generic-array 0.12.4", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "block-padding 0.2.1", + "generic-array 0.14.7", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + +[[package]] +name = "bounded-collections" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb5b05133427c07c4776906f673ccf36c21b102c9829c641a5b56bd151d44fd6" +dependencies = [ + "log", + "parity-scale-codec", + "scale-info", +] + +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "git+https://github.com/mesalock-linux/byteorder-sgx?tag=sgx_1.1.3#325f392dcd294109eb05f0a3c45e4141514c7784" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.0.1" +source = "git+https://github.com/integritee-network/bytes-sgx?branch=sgx-experimental#62ed3082be2e23cb9bc8cc7ee9983a523de69292" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cargo_toml" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3f9629bc6c4388ea699781dc988c2b99766d7679b151c81990b4fa1208fafd3" +dependencies = [ + "serde 1.0.193", + "toml", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-expr" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aacacf4d96c24b2ad6eb8ee6df040e4f27b0d0b39a5710c30091baa830485db" +dependencies = [ + "smallvec 1.11.1", +] + +[[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 = "chrono" +version = "0.4.11" +source = "git+https://github.com/mesalock-linux/chrono-sgx#f964ae7f5f65bd2c9cd6f44a067e7980afc08ca0" +dependencies = [ + "num-integer", + "num-traits 0.2.10", + "sgx_tstd", +] + +[[package]] +name = "chrono" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "num-traits 0.2.16", +] + +[[package]] +name = "cid" +version = "0.5.1" +source = "git+https://github.com/whalelephant/rust-cid?branch=nstd#cca87467c46106c801ca3727500477258b0f13b0" +dependencies = [ + "multibase", + "multihash", + "unsigned-varint", +] + +[[package]] +name = "cipher" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "common-primitives" +version = "0.1.0" +source = "git+https://github.com/integritee-network/pallets.git?branch=sdk-v0.12.0-polkadot-v0.9.42#eaf611b79bc9d56b20c155150e99b549bf98436b" +dependencies = [ + "derive_more", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "const-oid" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "constant_time_eq" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "core-primitives" +version = "0.9.12" +dependencies = [ + "frame-support", + "litentry-hex-utils", + "litentry-macros 0.9.12", + "litentry-proc-macros", + "pallet-evm 6.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", + "parity-scale-codec", + "ring 0.16.20", + "scale-info", + "serde 1.0.193", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "strum", + "strum_macros", +] + +[[package]] +name = "core2" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239fa3ae9b63c2dc74bd3fa852d4792b8b305ae64eeede946265b6af62f1fff3" +dependencies = [ + "memchr 2.6.3", +] + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "740fe28e594155f10cfc383984cbefd529d7396050557148f79cb0f621204124" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array 0.14.7", + "typenum 1.17.0", +] + +[[package]] +name = "curve25519-dalek" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b85542f99a2dfa2a1b8e192662741c9859a846b296bef1c92ef9b58b5a216" +dependencies = [ + "byteorder 1.4.3", + "digest 0.8.1", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder 1.4.3", + "digest 0.9.0", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle", + "zeroize", +] + +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv 1.0.7", + "ident_case", + "proc-macro2", + "quote 1.0.33", + "strsim", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "data-encoding" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" + +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derive-syn-parse" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79116f119dd1dba1abf1f3405f03b9b0e79a27a3883864bfebded8a3dc768cd" +dependencies = [ + "proc-macro2", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote 1.0.33", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.4", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "ecdsa" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "ed25519-zebra" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" +dependencies = [ + "curve25519-dalek 3.2.0", + "hashbrown 0.12.3", + "hex", + "rand_core 0.6.4", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "elliptic-curve" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array 0.14.7", + "group", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "enclave-runtime" +version = "0.0.1" +dependencies = [ + "array-bytes 6.1.0", + "cid", + "derive_more", + "env_logger", + "frame-support", + "frame-system", + "hex", + "ipfs-unixfs", + "ita-oracle", + "ita-parentchain-interface", + "ita-sgx-runtime", + "ita-stf", + "itc-direct-rpc-client", + "itc-direct-rpc-server", + "itc-offchain-worker-executor", + "itc-parentchain", + "itc-parentchain-block-import-dispatcher", + "itc-parentchain-test", + "itc-peer-top-broadcaster", + "itc-tls-websocket-server", + "itp-attestation-handler", + "itp-component-container", + "itp-enclave-metrics", + "itp-extrinsics-factory", + "itp-import-queue", + "itp-node-api", + "itp-node-api-metadata", + "itp-nonce-cache", + "itp-ocall-api", + "itp-primitives-cache", + "itp-rpc", + "itp-settings", + "itp-sgx-crypto", + "itp-sgx-externalities", + "itp-sgx-temp-dir", + "itp-stf-executor", + "itp-stf-interface", + "itp-stf-primitives", + "itp-stf-state-handler", + "itp-stf-state-observer", + "itp-storage", + "itp-test", + "itp-time-utils", + "itp-top-pool", + "itp-top-pool-author", + "itp-types", + "itp-utils", + "its-block-verification", + "its-primitives", + "its-sidechain", + "jsonrpc-core", + "lazy_static", + "lc-scheduled-enclave", + "litentry-macros 0.1.0", + "litentry-primitives", + "log", + "multibase", + "once_cell 1.4.0", + "parity-scale-codec", + "primitive-types", + "rust-base58", + "rustls 0.19.0 (git+https://github.com/mesalock-linux/rustls?rev=sgx_1.1.3)", + "serde_json 1.0.60 (git+https://github.com/mesalock-linux/serde-json-sgx?tag=sgx_1.1.3)", + "sgx_rand", + "sgx_serialize", + "sgx_serialize_derive", + "sgx_tcrypto", + "sgx_tcrypto_helper", + "sgx_trts", + "sgx_tse", + "sgx_tseal", + "sgx_tstd", + "sgx_tunittest", + "sgx_types", + "sp-core", + "sp-runtime", + "teerex-primitives 0.1.0 (git+https://github.com/integritee-network/pallets.git?branch=sdk-v0.12.0-polkadot-v0.9.42)", + "webpki", +] + +[[package]] +name = "env_logger" +version = "0.9.0" +source = "git+https://github.com/integritee-network/env_logger-sgx#55745829b2ae8a77f0915af3671ec8a9a00cace9" +dependencies = [ + "humantime", + "log", + "regex 1.3.1", + "sgx_tstd", + "termcolor", +] + +[[package]] +name = "environmental" +version = "1.1.3" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "environmental" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48c92028aaa870e83d51c64e5d4e0b6981b360c522198c23959f219a4e1b15b" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "ethbloom" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" +dependencies = [ + "crunchy", + "fixed-hash", + "impl-codec", + "impl-rlp", + "scale-info", + "tiny-keccak", +] + +[[package]] +name = "ethereum" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a89fb87a9e103f71b903b80b670200b54cc67a07578f070681f1fffb7396fb7" +dependencies = [ + "bytes 1.5.0", + "ethereum-types", + "hash-db 0.15.2", + "hash256-std-hasher", + "parity-scale-codec", + "rlp", + "scale-info", + "sha3 0.10.8", + "triehash", +] + +[[package]] +name = "ethereum-types" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" +dependencies = [ + "ethbloom", + "fixed-hash", + "impl-codec", + "impl-rlp", + "primitive-types", + "scale-info", + "uint", +] + +[[package]] +name = "evm" +version = "0.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a49a4e11987c51220aa89dbe1a5cc877f5079fa6864c0a5b4533331db44e9365" +dependencies = [ + "auto_impl", + "ethereum", + "evm-core 0.39.0 (registry+https://github.com/rust-lang/crates.io-index)", + "evm-gasometer 0.39.0 (registry+https://github.com/rust-lang/crates.io-index)", + "evm-runtime 0.39.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log", + "parity-scale-codec", + "primitive-types", + "rlp", + "scale-info", + "sha3 0.10.8", +] + +[[package]] +name = "evm" +version = "0.39.1" +source = "git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65#b7b82c7e1fc57b7449d6dfa6826600de37cc1e65" +dependencies = [ + "auto_impl", + "ethereum", + "evm-core 0.39.0 (git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65)", + "evm-gasometer 0.39.0 (git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65)", + "evm-runtime 0.39.0 (git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65)", + "log", + "parity-scale-codec", + "primitive-types", + "rlp", + "scale-info", + "sha3 0.10.8", +] + +[[package]] +name = "evm-core" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f1f13264b044cb66f0602180f0bc781c29accb41ff560669a3ec15858d5b606" +dependencies = [ + "parity-scale-codec", + "primitive-types", + "scale-info", +] + +[[package]] +name = "evm-core" +version = "0.39.0" +source = "git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65#b7b82c7e1fc57b7449d6dfa6826600de37cc1e65" +dependencies = [ + "parity-scale-codec", + "primitive-types", + "scale-info", +] + +[[package]] +name = "evm-gasometer" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d43eadc395bd1a52990787ca1495c26b0248165444912be075c28909a853b8c" +dependencies = [ + "evm-core 0.39.0 (registry+https://github.com/rust-lang/crates.io-index)", + "evm-runtime 0.39.0 (registry+https://github.com/rust-lang/crates.io-index)", + "primitive-types", +] + +[[package]] +name = "evm-gasometer" +version = "0.39.0" +source = "git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65#b7b82c7e1fc57b7449d6dfa6826600de37cc1e65" +dependencies = [ + "evm-core 0.39.0 (git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65)", + "evm-runtime 0.39.0 (git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65)", + "primitive-types", +] + +[[package]] +name = "evm-runtime" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aa5b32f59ec582a5651978004e5c784920291263b7dcb6de418047438e37f4f" +dependencies = [ + "auto_impl", + "evm-core 0.39.0 (registry+https://github.com/rust-lang/crates.io-index)", + "primitive-types", + "sha3 0.10.8", +] + +[[package]] +name = "evm-runtime" +version = "0.39.0" +source = "git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65#b7b82c7e1fc57b7449d6dfa6826600de37cc1e65" +dependencies = [ + "auto_impl", + "evm-core 0.39.0 (git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65)", + "primitive-types", + "sha3 0.10.8", +] + +[[package]] +name = "expander" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f360349150728553f92e4c997a16af8915f418d3a0f21b440d34c5632f16ed84" +dependencies = [ + "blake2", + "fs-err", + "proc-macro2", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "finality-grandpa" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36530797b9bf31cd4ff126dcfee8170f86b00cfdcea3269d73133cc0415945c3" +dependencies = [ + "either", + "futures 0.3.28", + "num-traits 0.2.16", + "parity-scale-codec", + "scale-info", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder 1.4.3", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fnv" +version = "1.0.6" +source = "git+https://github.com/mesalock-linux/rust-fnv-sgx#c3bd6153c1403c1fa32fa54be5544d91f5efb017" +dependencies = [ + "hashbrown 0.3.1", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "fp-account" +version = "1.0.0-dev" +source = "git+https://github.com/integritee-network/frontier.git?branch=bar/polkadot-v0.9.42#a5a5e1e6ec08cd542a6084c310863150fb8841b1" +dependencies = [ + "hex", + "libsecp256k1", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "fp-account" +version = "1.0.0-dev" +source = "git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42#2499d18c936edbcb7fcb711827db7abb9b4f4da4" +dependencies = [ + "hex", + "libsecp256k1", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-runtime-interface", + "sp-std", +] + +[[package]] +name = "fp-evm" +version = "3.0.0-dev" +source = "git+https://github.com/integritee-network/frontier.git?branch=bar/polkadot-v0.9.42#a5a5e1e6ec08cd542a6084c310863150fb8841b1" +dependencies = [ + "evm 0.39.1 (registry+https://github.com/rust-lang/crates.io-index)", + "frame-support", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "fp-evm" +version = "3.0.0-dev" +source = "git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42#2499d18c936edbcb7fcb711827db7abb9b4f4da4" +dependencies = [ + "evm 0.39.1 (git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65)", + "frame-support", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "frame-executive" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-tracing", +] + +[[package]] +name = "frame-metadata" +version = "15.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "878babb0b136e731cc77ec2fd883ff02745ff21e6fb662729953d44923df009c" +dependencies = [ + "cfg-if 1.0.0", + "parity-scale-codec", + "scale-info", + "serde 1.0.193", +] + +[[package]] +name = "frame-support" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "bitflags", + "environmental 1.1.4", + "frame-metadata", + "frame-support-procedural", + "impl-trait-for-tuples", + "k256", + "log", + "parity-scale-codec", + "paste", + "scale-info", + "smallvec 1.11.1", + "sp-api", + "sp-arithmetic", + "sp-core", + "sp-core-hashing-proc-macro", + "sp-inherents", + "sp-io", + "sp-runtime", + "sp-staking", + "sp-std", + "sp-tracing", + "sp-weights", + "tt-call", +] + +[[package]] +name = "frame-support-procedural" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "Inflector", + "cfg-expr", + "derive-syn-parse", + "frame-support-procedural-tools", + "itertools 0.10.5", + "proc-macro-warning", + "proc-macro2", + "quote 1.0.33", + "syn 2.0.37", +] + +[[package]] +name = "frame-support-procedural-tools" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-support-procedural-tools-derive", + "proc-macro-crate", + "proc-macro2", + "quote 1.0.33", + "syn 2.0.37", +] + +[[package]] +name = "frame-support-procedural-tools-derive" +version = "3.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "proc-macro2", + "quote 1.0.33", + "syn 2.0.37", +] + +[[package]] +name = "frame-system" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-support", + "log", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "sp-version", + "sp-weights", +] + +[[package]] +name = "fs-err" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0845fa252299212f0389d64ba26f34fa32cfe41588355f21ed507c59a0f64541" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.8" +source = "git+https://github.com/mesalock-linux/futures-rs-sgx#d54882f24ddf7d61327a067b2f608d6940a36444" +dependencies = [ + "futures-channel 0.3.8", + "futures-core 0.3.8", + "futures-executor", + "futures-io 0.3.8", + "futures-sink 0.3.8", + "futures-task 0.3.8", + "futures-util 0.3.8", + "sgx_tstd", +] + +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel 0.3.28", + "futures-core 0.3.28", + "futures-io 0.3.28", + "futures-sink 0.3.28", + "futures-task 0.3.28", + "futures-util 0.3.28", +] + +[[package]] +name = "futures-channel" +version = "0.3.8" +source = "git+https://github.com/mesalock-linux/futures-rs-sgx#d54882f24ddf7d61327a067b2f608d6940a36444" +dependencies = [ + "futures-core 0.3.8", + "futures-sink 0.3.8", + "sgx_tstd", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core 0.3.28", + "futures-sink 0.3.28", +] + +[[package]] +name = "futures-core" +version = "0.3.8" +source = "git+https://github.com/mesalock-linux/futures-rs-sgx#d54882f24ddf7d61327a067b2f608d6940a36444" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-executor" +version = "0.3.8" +source = "git+https://github.com/mesalock-linux/futures-rs-sgx#d54882f24ddf7d61327a067b2f608d6940a36444" +dependencies = [ + "futures-core 0.3.8", + "futures-task 0.3.8", + "futures-util 0.3.8", + "sgx_tstd", +] + +[[package]] +name = "futures-io" +version = "0.3.8" +source = "git+https://github.com/mesalock-linux/futures-rs-sgx#d54882f24ddf7d61327a067b2f608d6940a36444" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-macro" +version = "0.3.8" +source = "git+https://github.com/mesalock-linux/futures-rs-sgx#d54882f24ddf7d61327a067b2f608d6940a36444" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "futures-sink" +version = "0.3.8" +source = "git+https://github.com/mesalock-linux/futures-rs-sgx#d54882f24ddf7d61327a067b2f608d6940a36444" + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.8" +source = "git+https://github.com/mesalock-linux/futures-rs-sgx#d54882f24ddf7d61327a067b2f608d6940a36444" +dependencies = [ + "once_cell 1.4.0", + "sgx_tstd", +] + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.8" +source = "git+https://github.com/mesalock-linux/futures-rs-sgx#d54882f24ddf7d61327a067b2f608d6940a36444" +dependencies = [ + "futures-channel 0.3.8", + "futures-core 0.3.8", + "futures-io 0.3.8", + "futures-macro", + "futures-sink 0.3.8", + "futures-task 0.3.8", + "memchr 2.2.1", + "pin-project-lite", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "sgx_tstd", + "slab 0.4.2", +] + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-core 0.3.28", + "futures-sink 0.3.28", + "futures-task 0.3.28", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum 1.17.0", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum 1.17.0", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.1.14" +source = "git+https://github.com/mesalock-linux/getrandom-sgx#0aa9cc20c7dea713ccaac2c44430d625a395ebae" +dependencies = [ + "cfg-if 0.1.10", + "sgx_libc", + "sgx_trts", + "sgx_tstd", +] + +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "hash-db" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" + +[[package]] +name = "hash-db" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e7d7786361d7425ae2fe4f9e407eb0efaa0840f5212d109cc018c40c35c6ab4" + +[[package]] +name = "hash256-std-hasher" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2" +dependencies = [ + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29fba9abe4742d586dfd0c06ae4f7e73a1c2d86b856933509b269d82cdf06e18" + +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.6", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.3", +] + +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + +[[package]] +name = "hashbrown_tstd" +version = "0.12.0" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-conservative" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ed443af458ccb6d81c1e7e661545f94d3176752fb1df2f543b902a1e0f51e2" +dependencies = [ + "core2", +] + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hex_lit" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "http" +version = "0.2.1" +source = "git+https://github.com/integritee-network/http-sgx.git?branch=sgx-experimental#307b5421fb7a489a114bede0dc05c8d32b804f49" +dependencies = [ + "bytes 1.0.1", + "fnv 1.0.6", + "itoa 0.4.5", + "sgx_tstd", +] + +[[package]] +name = "http_req" +version = "0.8.1" +source = "git+https://github.com/integritee-network/http_req#3723e88235f2b29bc1a31835853b072ffd0455fd" +dependencies = [ + "log", + "rustls 0.19.0 (git+https://github.com/mesalock-linux/rustls?branch=mesalock_sgx)", + "sgx_tstd", + "unicase", + "webpki", + "webpki-roots 0.21.0 (git+https://github.com/mesalock-linux/webpki-roots?branch=mesalock_sgx)", +] + +[[package]] +name = "httparse" +version = "1.4.1" +source = "git+https://github.com/integritee-network/httparse-sgx?branch=sgx-experimental#cc97e4b34d2c44a1e3df5bdebef446b9771f5cc3" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "humantime" +version = "1.3.0" +source = "git+https://github.com/mesalock-linux/humantime-sgx#c5243dfa36002c01adbc9aade288ead1b2c411cc" +dependencies = [ + "quick-error", + "sgx_tstd", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.2.0" +source = "git+https://github.com/mesalock-linux/rust-url-sgx?tag=sgx_1.1.3#23832f3191456c2d4a0faab10952e1747be58ca8" +dependencies = [ + "matches", + "sgx_tstd", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-rlp" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" +dependencies = [ + "rlp", +] + +[[package]] +name = "impl-serde" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" +dependencies = [ + "serde 1.0.193", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "indexmap" +version = "1.6.1" +source = "git+https://github.com/mesalock-linux/indexmap-sgx#19f52458ba64dd7349a5d3a62227619a17e4db85" +dependencies = [ + "autocfg 1.1.0", + "hashbrown 0.9.1", + "sgx_tstd", +] + +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", +] + +[[package]] +name = "integer-sqrt" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" +dependencies = [ + "num-traits 0.2.16", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "git+https://github.com/mesalock-linux/iovec-sgx#5c2f8e81925b4c06c556d856f3237461b00e27c9" +dependencies = [ + "sgx_libc", +] + +[[package]] +name = "ipfs-unixfs" +version = "0.0.1" +source = "git+https://github.com/whalelephant/rust-ipfs?branch=w-nstd#52f84dceea7065bb4ee2c24da53b3bedf162241a" +dependencies = [ + "cid", + "either", + "multihash", + "quick-protobuf", + "sha2 0.9.9", +] + +[[package]] +name = "ita-oracle" +version = "0.9.0" +dependencies = [ + "itc-rest-client", + "itp-enclave-metrics", + "itp-ocall-api", + "lazy_static", + "log", + "parity-scale-codec", + "serde 1.0.193", + "sgx_tstd", + "substrate-fixed", + "thiserror", + "url", +] + +[[package]] +name = "ita-parentchain-interface" +version = "0.9.0" +dependencies = [ + "bs58", + "ita-sgx-runtime", + "ita-stf", + "itc-parentchain-indirect-calls-executor", + "itp-api-client-types", + "itp-node-api", + "itp-stf-primitives", + "itp-types", + "itp-utils", + "lc-scheduled-enclave", + "litentry-primitives", + "log", + "parity-scale-codec", + "sgx_tstd", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "ita-sgx-runtime" +version = "0.9.0" +dependencies = [ + "frame-executive", + "frame-support", + "frame-system", + "itp-sgx-runtime-primitives", + "pallet-balances", + "pallet-evm 6.0.0-dev (git+https://github.com/integritee-network/frontier.git?branch=bar/polkadot-v0.9.42)", + "pallet-parentchain", + "pallet-sudo", + "pallet-timestamp", + "pallet-transaction-payment", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-core", + "sp-runtime", + "sp-std", + "sp-version", +] + +[[package]] +name = "ita-stf" +version = "0.9.0" +dependencies = [ + "frame-support", + "frame-system", + "hex", + "hex-literal", + "ita-sgx-runtime", + "itp-hashing", + "itp-node-api", + "itp-node-api-metadata", + "itp-node-api-metadata-provider", + "itp-sgx-externalities", + "itp-stf-interface", + "itp-stf-primitives", + "itp-storage", + "itp-types", + "itp-utils", + "litentry-primitives", + "log", + "pallet-balances", + "pallet-parentchain", + "pallet-sudo", + "parity-scale-codec", + "rlp", + "sgx_tstd", + "sha3 0.10.8", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "itc-direct-rpc-client" +version = "0.1.0" +dependencies = [ + "itp-rpc", + "itp-types", + "itp-utils", + "litentry-primitives", + "log", + "parity-scale-codec", + "rustls 0.19.0 (git+https://github.com/mesalock-linux/rustls?tag=sgx_1.1.3)", + "serde_json 1.0.107", + "sgx_tstd", + "tungstenite", + "url", + "webpki", +] + +[[package]] +name = "itc-direct-rpc-server" +version = "0.9.0" +dependencies = [ + "itc-tls-websocket-server", + "itp-rpc", + "itp-types", + "itp-utils", + "jsonrpc-core", + "log", + "parity-scale-codec", + "serde_json 1.0.107", + "sgx_tstd", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "itc-offchain-worker-executor" +version = "0.9.0" +dependencies = [ + "itc-parentchain-light-client", + "itp-extrinsics-factory", + "itp-stf-executor", + "itp-stf-interface", + "itp-stf-primitives", + "itp-stf-state-handler", + "itp-top-pool-author", + "itp-types", + "log", + "parity-scale-codec", + "sgx_tstd", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "itc-parentchain" +version = "0.9.0" +dependencies = [ + "itc-parentchain-block-import-dispatcher", + "itc-parentchain-block-importer", + "itc-parentchain-indirect-calls-executor", + "itc-parentchain-light-client", + "itp-types", + "parity-scale-codec", + "sp-runtime", +] + +[[package]] +name = "itc-parentchain-block-import-dispatcher" +version = "0.9.0" +dependencies = [ + "itc-parentchain-block-importer", + "itp-import-queue", + "log", + "sgx_tstd", + "sgx_types", + "thiserror", +] + +[[package]] +name = "itc-parentchain-block-importer" +version = "0.9.0" +dependencies = [ + "ita-stf", + "itc-parentchain-indirect-calls-executor", + "itc-parentchain-light-client", + "itp-enclave-metrics", + "itp-extrinsics-factory", + "itp-ocall-api", + "itp-stf-executor", + "itp-types", + "log", + "parity-scale-codec", + "sgx_tstd", + "sgx_types", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "itc-parentchain-indirect-calls-executor" +version = "0.9.0" +dependencies = [ + "binary-merkle-tree", + "bs58", + "core-primitives", + "futures 0.3.8", + "itp-api-client-types", + "itp-node-api", + "itp-sgx-crypto", + "itp-sgx-runtime-primitives", + "itp-stf-executor", + "itp-stf-primitives", + "itp-test", + "itp-top-pool-author", + "itp-types", + "itp-utils", + "lc-scheduled-enclave", + "litentry-primitives", + "log", + "parity-scale-codec", + "sgx_tstd", + "sgx_types", + "sp-core", + "sp-runtime", + "sp-std", + "thiserror", +] + +[[package]] +name = "itc-parentchain-light-client" +version = "0.9.0" +dependencies = [ + "finality-grandpa", + "itc-parentchain-test", + "itp-ocall-api", + "itp-sgx-io", + "itp-sgx-temp-dir", + "itp-storage", + "itp-test", + "itp-types", + "log", + "parity-scale-codec", + "sgx_tstd", + "sgx_types", + "sp-consensus-grandpa", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "itc-parentchain-test" +version = "0.9.0" +dependencies = [ + "itp-types", + "sp-runtime", +] + +[[package]] +name = "itc-peer-top-broadcaster" +version = "0.1.0" +dependencies = [ + "itc-direct-rpc-client", + "itc-direct-rpc-server", + "itp-rpc", + "itp-stf-primitives", + "itp-types", + "itp-utils", + "litentry-primitives", + "log", + "sgx_tstd", +] + +[[package]] +name = "itc-rest-client" +version = "0.9.0" +dependencies = [ + "base64 0.13.1", + "http", + "http_req", + "log", + "serde 1.0.193", + "serde_json 1.0.107", + "sgx_tstd", + "thiserror", + "url", +] + +[[package]] +name = "itc-tls-websocket-server" +version = "0.9.0" +dependencies = [ + "bit-vec", + "chrono 0.4.31", + "log", + "mio", + "mio-extras", + "rcgen", + "rustls 0.19.0 (git+https://github.com/mesalock-linux/rustls?branch=mesalock_sgx)", + "sgx_tstd", + "sp-core", + "thiserror", + "tungstenite", + "webpki", + "yasna", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.5" +source = "git+https://github.com/mesalock-linux/itoa-sgx#295ee451f5ec74f25c299552b481beb445ea3eb7" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "itp-api-client-types" +version = "0.9.0" +dependencies = [ + "itp-types", + "sp-runtime", + "substrate-api-client", +] + +[[package]] +name = "itp-attestation-handler" +version = "0.8.0" +dependencies = [ + "arrayvec 0.7.4", + "base64 0.13.0 (git+https://github.com/mesalock-linux/rust-base64-sgx?rev=sgx_1.1.3)", + "bit-vec", + "chrono 0.4.11", + "hex", + "httparse", + "itertools 0.10.5", + "itp-ocall-api", + "itp-settings", + "itp-sgx-crypto", + "itp-sgx-io", + "itp-time-utils", + "log", + "num-bigint", + "parity-scale-codec", + "rustls 0.19.0 (git+https://github.com/mesalock-linux/rustls?rev=sgx_1.1.3)", + "serde_json 1.0.60 (git+https://github.com/mesalock-linux/serde-json-sgx?tag=sgx_1.1.3)", + "sgx_rand", + "sgx_tcrypto", + "sgx_tse", + "sgx_tstd", + "sgx_types", + "sp-core", + "thiserror", + "webpki", + "webpki-roots 0.21.0 (git+https://github.com/mesalock-linux/webpki-roots?branch=mesalock_sgx)", + "yasna", +] + +[[package]] +name = "itp-component-container" +version = "0.8.0" +dependencies = [ + "sgx_tstd", + "thiserror", +] + +[[package]] +name = "itp-enclave-metrics" +version = "0.9.0" +dependencies = [ + "parity-scale-codec", + "sgx_tstd", + "substrate-fixed", +] + +[[package]] +name = "itp-extrinsics-factory" +version = "0.9.0" +dependencies = [ + "itp-node-api", + "itp-nonce-cache", + "itp-types", + "log", + "parity-scale-codec", + "sgx_tstd", + "sgx_types", + "sp-core", + "sp-runtime", + "substrate-api-client", + "thiserror", +] + +[[package]] +name = "itp-hashing" +version = "0.9.0" +dependencies = [ + "sp-core", +] + +[[package]] +name = "itp-import-queue" +version = "0.8.0" +dependencies = [ + "sgx_tstd", + "sgx_types", + "thiserror", +] + +[[package]] +name = "itp-node-api" +version = "0.9.0" +dependencies = [ + "itp-api-client-types", + "itp-node-api-metadata", + "itp-node-api-metadata-provider", +] + +[[package]] +name = "itp-node-api-metadata" +version = "0.9.0" +dependencies = [ + "derive_more", + "itp-api-client-types", + "itp-stf-primitives", + "parity-scale-codec", + "sp-core", +] + +[[package]] +name = "itp-node-api-metadata-provider" +version = "0.9.0" +dependencies = [ + "itp-node-api-metadata", + "itp-stf-primitives", + "sgx_tstd", + "thiserror", +] + +[[package]] +name = "itp-nonce-cache" +version = "0.8.0" +dependencies = [ + "sgx_tstd", + "thiserror", +] + +[[package]] +name = "itp-ocall-api" +version = "0.9.0" +dependencies = [ + "derive_more", + "itp-storage", + "itp-types", + "parity-scale-codec", + "sgx_types", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "itp-primitives-cache" +version = "0.9.0" +dependencies = [ + "lazy_static", + "sgx_tstd", + "thiserror", +] + +[[package]] +name = "itp-rpc" +version = "0.9.0" +dependencies = [ + "itp-types", + "parity-scale-codec", + "serde 1.0.193", + "serde_json 1.0.107", + "sgx_tstd", +] + +[[package]] +name = "itp-settings" +version = "0.9.0" + +[[package]] +name = "itp-sgx-crypto" +version = "0.9.0" +dependencies = [ + "aes", + "derive_more", + "itp-sgx-io", + "itp-sgx-temp-dir", + "log", + "ofb", + "parity-scale-codec", + "serde_json 1.0.60 (git+https://github.com/mesalock-linux/serde-json-sgx?tag=sgx_1.1.3)", + "sgx_crypto_helper", + "sgx_rand", + "sgx_tstd", + "sgx_types", + "sp-core", +] + +[[package]] +name = "itp-sgx-externalities" +version = "0.9.0" +dependencies = [ + "derive_more", + "environmental 1.1.3", + "itp-hashing", + "log", + "parity-scale-codec", + "postcard", + "serde 1.0.193", + "sgx_tstd", + "sp-core", +] + +[[package]] +name = "itp-sgx-io" +version = "0.8.0" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "itp-sgx-runtime-primitives" +version = "0.9.0" +dependencies = [ + "frame-system", + "litentry-primitives", + "pallet-balances", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "itp-sgx-temp-dir" +version = "0.1.0" +dependencies = [ + "lazy_static", + "sgx_tstd", +] + +[[package]] +name = "itp-stf-executor" +version = "0.9.0" +dependencies = [ + "hex", + "itc-parentchain-test", + "itp-enclave-metrics", + "itp-node-api", + "itp-ocall-api", + "itp-sgx-crypto", + "itp-sgx-externalities", + "itp-stf-interface", + "itp-stf-primitives", + "itp-stf-state-handler", + "itp-stf-state-observer", + "itp-test", + "itp-time-utils", + "itp-top-pool-author", + "itp-types", + "litentry-primitives", + "log", + "parity-scale-codec", + "sgx_tstd", + "sgx_types", + "sp-core", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "itp-stf-interface" +version = "0.8.0" +dependencies = [ + "itp-node-api-metadata", + "itp-node-api-metadata-provider", + "itp-stf-primitives", + "itp-types", + "parity-scale-codec", +] + +[[package]] +name = "itp-stf-primitives" +version = "0.9.0" +dependencies = [ + "derive_more", + "itp-sgx-runtime-primitives", + "litentry-primitives", + "parity-scale-codec", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "itp-stf-state-handler" +version = "0.9.0" +dependencies = [ + "itp-hashing", + "itp-settings", + "itp-sgx-crypto", + "itp-sgx-externalities", + "itp-sgx-io", + "itp-sgx-temp-dir", + "itp-stf-interface", + "itp-stf-state-observer", + "itp-time-utils", + "itp-types", + "log", + "parity-scale-codec", + "rust-base58", + "sgx_tstd", + "sgx_types", + "sp-core", + "thiserror", +] + +[[package]] +name = "itp-stf-state-observer" +version = "0.9.0" +dependencies = [ + "itp-types", + "log", + "sgx_tstd", + "thiserror", +] + +[[package]] +name = "itp-storage" +version = "0.9.0" +dependencies = [ + "derive_more", + "frame-metadata", + "frame-support", + "hash-db 0.15.2", + "itp-types", + "parity-scale-codec", + "sgx_tstd", + "sp-core", + "sp-runtime", + "sp-std", + "sp-trie", + "thiserror", +] + +[[package]] +name = "itp-teerex-storage" +version = "0.9.0" +dependencies = [ + "itp-storage", + "sp-std", +] + +[[package]] +name = "itp-test" +version = "0.9.0" +dependencies = [ + "hex", + "itp-node-api", + "itp-node-api-metadata-provider", + "itp-ocall-api", + "itp-sgx-crypto", + "itp-sgx-externalities", + "itp-stf-interface", + "itp-stf-primitives", + "itp-stf-state-handler", + "itp-storage", + "itp-teerex-storage", + "itp-time-utils", + "itp-types", + "jsonrpc-core", + "litentry-primitives", + "log", + "parity-scale-codec", + "sgx_crypto_helper", + "sgx_tstd", + "sgx_types", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "itp-time-utils" +version = "0.9.0" +dependencies = [ + "chrono 0.4.11", + "sgx_tstd", +] + +[[package]] +name = "itp-top-pool" +version = "0.9.0" +dependencies = [ + "byteorder 1.4.3", + "derive_more", + "itc-direct-rpc-server", + "itp-stf-primitives", + "itp-types", + "its-primitives", + "jsonrpc-core", + "linked-hash-map", + "log", + "parity-scale-codec", + "sgx_tstd", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "itp-top-pool-author" +version = "0.9.0" +dependencies = [ + "derive_more", + "itp-enclave-metrics", + "itp-ocall-api", + "itp-sgx-crypto", + "itp-stf-primitives", + "itp-stf-state-handler", + "itp-test", + "itp-top-pool", + "itp-types", + "itp-utils", + "jsonrpc-core", + "lazy_static", + "litentry-primitives", + "log", + "parity-scale-codec", + "sgx_tstd", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "itp-types" +version = "0.9.0" +dependencies = [ + "frame-system", + "itp-sgx-crypto", + "itp-sgx-runtime-primitives", + "itp-stf-primitives", + "itp-utils", + "litentry-primitives", + "pallet-balances", + "parity-scale-codec", + "sp-core", + "sp-runtime", + "sp-std", + "substrate-api-client", +] + +[[package]] +name = "itp-utils" +version = "0.9.0" +dependencies = [ + "hex", + "parity-scale-codec", +] + +[[package]] +name = "its-block-composer" +version = "0.9.0" +dependencies = [ + "itp-node-api", + "itp-settings", + "itp-sgx-crypto", + "itp-sgx-externalities", + "itp-stf-executor", + "itp-stf-primitives", + "itp-time-utils", + "itp-top-pool-author", + "itp-types", + "its-primitives", + "its-state", + "log", + "parity-scale-codec", + "sgx_tstd", + "sgx_types", + "sp-core", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "its-block-verification" +version = "0.9.0" +dependencies = [ + "frame-support", + "itp-types", + "itp-utils", + "its-primitives", + "log", + "sgx_tstd", + "sp-consensus-slots", + "sp-core", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "its-consensus-aura" +version = "0.9.0" +dependencies = [ + "finality-grandpa", + "ita-stf", + "itc-parentchain-block-import-dispatcher", + "itc-peer-top-broadcaster", + "itp-enclave-metrics", + "itp-ocall-api", + "itp-settings", + "itp-sgx-crypto", + "itp-sgx-externalities", + "itp-stf-executor", + "itp-stf-primitives", + "itp-stf-state-handler", + "itp-time-utils", + "itp-top-pool-author", + "itp-types", + "itp-utils", + "its-block-composer", + "its-block-verification", + "its-consensus-common", + "its-consensus-slots", + "its-primitives", + "its-state", + "its-validateer-fetch", + "lc-scheduled-enclave", + "log", + "parity-scale-codec", + "sgx_tstd", + "sp-core", + "sp-runtime", +] + +[[package]] +name = "its-consensus-common" +version = "0.9.0" +dependencies = [ + "itc-parentchain-light-client", + "itp-enclave-metrics", + "itp-extrinsics-factory", + "itp-import-queue", + "itp-node-api-metadata", + "itp-node-api-metadata-provider", + "itp-ocall-api", + "itp-settings", + "itp-sgx-crypto", + "itp-types", + "its-block-verification", + "its-primitives", + "its-state", + "log", + "parity-scale-codec", + "sgx_tstd", + "sgx_types", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "its-consensus-slots" +version = "0.9.0" +dependencies = [ + "derive_more", + "hex", + "itp-settings", + "itp-sgx-externalities", + "itp-stf-state-handler", + "itp-time-utils", + "itp-types", + "its-block-verification", + "its-consensus-common", + "its-primitives", + "its-state", + "lazy_static", + "lc-scheduled-enclave", + "log", + "parity-scale-codec", + "sgx_tstd", + "sp-consensus-slots", + "sp-runtime", +] + +[[package]] +name = "its-primitives" +version = "0.1.0" +dependencies = [ + "itp-types", + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "its-rpc-handler" +version = "0.9.0" +dependencies = [ + "futures 0.3.8", + "itp-rpc", + "itp-stf-primitives", + "itp-top-pool-author", + "itp-types", + "itp-utils", + "its-primitives", + "jsonrpc-core", + "litentry-primitives", + "log", + "parity-scale-codec", + "rust-base58", + "sgx_tstd", + "sp-core", +] + +[[package]] +name = "its-sidechain" +version = "0.9.0" +dependencies = [ + "its-block-composer", + "its-consensus-aura", + "its-consensus-common", + "its-consensus-slots", + "its-primitives", + "its-rpc-handler", + "its-state", + "its-validateer-fetch", +] + +[[package]] +name = "its-state" +version = "0.9.0" +dependencies = [ + "frame-support", + "itp-sgx-externalities", + "itp-storage", + "its-primitives", + "log", + "parity-scale-codec", + "sgx_tstd", + "sp-core", + "sp-io", + "sp-runtime", + "thiserror", +] + +[[package]] +name = "its-validateer-fetch" +version = "0.9.0" +dependencies = [ + "derive_more", + "frame-support", + "itp-ocall-api", + "itp-teerex-storage", + "itp-types", + "parity-scale-codec", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "js-sys" +version = "0.3.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jsonrpc-core" +version = "18.0.0" +source = "git+https://github.com/scs/jsonrpc?branch=no_std_v18#0faf53c491c3222b96242a973d902dd06e9b6674" +dependencies = [ + "futures 0.3.8", + "log", + "serde 1.0.118", + "serde_derive 1.0.118", + "serde_json 1.0.60 (git+https://github.com/mesalock-linux/serde-json-sgx)", +] + +[[package]] +name = "k256" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" +dependencies = [ + "cfg-if 1.0.0", + "ecdsa", + "elliptic-curve", + "sha2 0.10.7", +] + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "lc-scheduled-enclave" +version = "0.8.0" +dependencies = [ + "itp-settings", + "itp-sgx-io", + "itp-types", + "lazy_static", + "log", + "parity-scale-codec", + "sgx_tstd", + "sp-std", + "thiserror", +] + +[[package]] +name = "libc" +version = "0.2.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" + +[[package]] +name = "libsecp256k1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" +dependencies = [ + "arrayref", + "base64 0.13.1", + "digest 0.9.0", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand 0.8.5", + "serde 1.0.193", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.2" +source = "git+https://github.com/mesalock-linux/linked-hash-map-sgx#03e763f7c251c16e0b85e2fb058ba47be52f2a49" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "litentry-hex-utils" +version = "0.9.12" +dependencies = [ + "hex", +] + +[[package]] +name = "litentry-macros" +version = "0.1.0" +dependencies = [ + "cargo_toml", + "quote 1.0.33", +] + +[[package]] +name = "litentry-macros" +version = "0.9.12" + +[[package]] +name = "litentry-primitives" +version = "0.1.0" +dependencies = [ + "bitcoin", + "core-primitives", + "hex", + "itp-sgx-crypto", + "itp-utils", + "log", + "pallet-evm 6.0.0-dev (git+https://github.com/integritee-network/frontier.git?branch=bar/polkadot-v0.9.42)", + "parity-scale-codec", + "rand 0.7.3", + "ring 0.16.20", + "scale-info", + "secp256k1 0.28.0", + "serde 1.0.193", + "sgx_tstd", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", + "strum", + "strum_macros", + "teerex-primitives 0.1.0", +] + +[[package]] +name = "litentry-proc-macros" +version = "0.9.12" +dependencies = [ + "cargo_toml", + "proc-macro2", + "quote 1.0.33", + "syn 2.0.37", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "git+https://github.com/integritee-network/log-sgx#483383a9be3e2e900042eef9b6b2d0837411783f" +dependencies = [ + "cfg-if 1.0.0", + "sgx_tstd", +] + +[[package]] +name = "matches" +version = "0.1.8" +source = "git+https://github.com/mesalock-linux/rust-std-candidates-sgx#5747bcf37f3e18687758838da0339ff0f2c83924" + +[[package]] +name = "maybe-async" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f1b8c13cb1f814b634a96b2c725449fe7ed464a7b8781de8688be5ffbd3f305" +dependencies = [ + "proc-macro2", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "memchr" +version = "2.2.1" +source = "git+https://github.com/mesalock-linux/rust-memchr-sgx#fb51ee32766cb9a2be39b7fb2b5de26bb86dcdeb" +dependencies = [ + "sgx_libc", + "sgx_tstd", + "sgx_types", +] + +[[package]] +name = "memchr" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" + +[[package]] +name = "memory-db" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808b50db46293432a45e63bc15ea51e0ab4c0a1647b8eb114e31a3e698dd6fbe" +dependencies = [ + "hash-db 0.16.0", +] + +[[package]] +name = "merlin" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e261cf0f8b3c42ded9f7d2bb59dea03aa52bc8a1cbc7482f9fc3fd1229d3b42" +dependencies = [ + "byteorder 1.4.3", + "keccak", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "zeroize", +] + +[[package]] +name = "mio" +version = "0.6.21" +source = "git+https://github.com/mesalock-linux/mio-sgx?tag=sgx_1.1.3#5b0e56a3066231c7a8d1876c7be3a19b08ffdfd5" +dependencies = [ + "iovec", + "log", + "net2", + "sgx_libc", + "sgx_trts", + "sgx_tstd", + "slab 0.4.2", +] + +[[package]] +name = "mio-extras" +version = "2.0.6" +source = "git+https://github.com/integritee-network/mio-extras-sgx?rev=963234b#963234bf55e44f9efff921938255126c48deef3a" +dependencies = [ + "lazycell", + "log", + "mio", + "sgx_tstd", + "sgx_types", + "slab 0.4.9", +] + +[[package]] +name = "multibase" +version = "0.8.0" +source = "git+https://github.com/whalelephant/rust-multibase?branch=nstd#df67fb30e86998f7c10d4eea16a1cd480d2448c0" +dependencies = [ + "base-x", + "data-encoding", + "lazy_static", +] + +[[package]] +name = "multihash" +version = "0.11.4" +source = "git+https://github.com/whalelephant/rust-multihash?branch=nstd#2c8aca8fa1fcbcba26951d925de40fa81696020a" +dependencies = [ + "blake2b_simd 0.5.11", + "blake2s_simd", + "digest 0.9.0", + "sha-1", + "sha2 0.9.9", + "sha3 0.9.1", + "unsigned-varint", +] + +[[package]] +name = "net2" +version = "0.2.33" +source = "git+https://github.com/mesalock-linux/net2-rs-sgx#554583d15f3c9dff5d862a6ae64e227bb38fa729" +dependencies = [ + "cfg-if 0.1.10", + "sgx_libc", + "sgx_tstd", +] + +[[package]] +name = "num" +version = "0.2.0" +source = "git+https://github.com/mesalock-linux/num-sgx#22645415542cc67551890dfdd34f4d5638b9ec78" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits 0.2.10", +] + +[[package]] +name = "num-bigint" +version = "0.2.5" +source = "git+https://github.com/mesalock-linux/num-bigint-sgx#76a5bed94dc31c32bd1670dbf72877abcf9bbc09" +dependencies = [ + "autocfg 1.1.0", + "num-integer", + "num-traits 0.2.10", + "sgx_tstd", +] + +[[package]] +name = "num-complex" +version = "0.2.3" +source = "git+https://github.com/mesalock-linux/num-complex-sgx#19700ad6de079ebc5560db472c282d1591e0d84f" +dependencies = [ + "autocfg 0.1.8", + "num-traits 0.2.10", + "sgx_tstd", +] + +[[package]] +name = "num-integer" +version = "0.1.41" +source = "git+https://github.com/mesalock-linux/num-integer-sgx#404c50e5378ca635261688b080dee328ff42b6bd" +dependencies = [ + "autocfg 0.1.8", + "num-traits 0.2.10", + "sgx_tstd", +] + +[[package]] +name = "num-iter" +version = "0.1.39" +source = "git+https://github.com/mesalock-linux/num-iter-sgx#f19fc44fcad0b82a040e5a24c511e5049cc04b60" +dependencies = [ + "num-integer", + "num-traits 0.2.10", + "sgx_tstd", +] + +[[package]] +name = "num-rational" +version = "0.2.2" +source = "git+https://github.com/mesalock-linux/num-rational-sgx#be65f9ce439f3c9ec850d8041635ab6c3309b816" +dependencies = [ + "autocfg 0.1.8", + "num-bigint", + "num-integer", + "num-traits 0.2.10", + "sgx_tstd", +] + +[[package]] +name = "num-traits" +version = "0.2.10" +source = "git+https://github.com/mesalock-linux/num-traits-sgx#af046e0b15c594c960007418097dd4ff37ec3f7a" +dependencies = [ + "autocfg 0.1.8", + "sgx_tstd", +] + +[[package]] +name = "num-traits" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +dependencies = [ + "autocfg 1.1.0", +] + +[[package]] +name = "ofb" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5e609fc8b72da3dabd56427be9489d8a9f4bd2e4dc41660dd033c3c8e90b93c" +dependencies = [ + "cipher", +] + +[[package]] +name = "once_cell" +version = "1.4.0" +source = "git+https://github.com/mesalock-linux/once_cell-sgx#cefcaa03fed4d85276b3235d875f1b45d399cc3c" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "pallet-balances" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-evm" +version = "6.0.0-dev" +source = "git+https://github.com/integritee-network/frontier.git?branch=bar/polkadot-v0.9.42#a5a5e1e6ec08cd542a6084c310863150fb8841b1" +dependencies = [ + "evm 0.39.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fp-account 1.0.0-dev (git+https://github.com/integritee-network/frontier.git?branch=bar/polkadot-v0.9.42)", + "fp-evm 3.0.0-dev (git+https://github.com/integritee-network/frontier.git?branch=bar/polkadot-v0.9.42)", + "frame-support", + "frame-system", + "hex", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "rlp", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-evm" +version = "6.0.0-dev" +source = "git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42#2499d18c936edbcb7fcb711827db7abb9b4f4da4" +dependencies = [ + "evm 0.39.1 (git+https://github.com/rust-blockchain/evm?rev=b7b82c7e1fc57b7449d6dfa6826600de37cc1e65)", + "fp-account 1.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", + "fp-evm 3.0.0-dev (git+https://github.com/paritytech/frontier?branch=polkadot-v0.9.42)", + "frame-support", + "frame-system", + "hex", + "hex-literal", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "rlp", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-parentchain" +version = "0.9.0" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", +] + +[[package]] +name = "pallet-sudo" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-timestamp" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-inherents", + "sp-runtime", + "sp-std", + "sp-timestamp", +] + +[[package]] +name = "pallet-transaction-payment" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "parity-scale-codec" +version = "3.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dec8a8073036902368c2cdc0387e85ff9a37054d7e7c98e592145e0c92cd4fb" +dependencies = [ + "arrayvec 0.7.4", + "bitvec", + "byte-slice-cast", + "bytes 1.5.0", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde 1.0.193", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "312270ee71e1cd70289dacf597cab7b207aa107d2f28191c2ae45b2ece18a260" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pem" +version = "0.8.2" +source = "git+https://github.com/mesalock-linux/pem-rs-sgx#fdfef4f24a9fb3fa72e8a71bb28bd8ff15feff2f" +dependencies = [ + "base64 0.13.0 (git+https://github.com/mesalock-linux/rust-base64-sgx)", + "once_cell 1.4.0", + "regex 1.3.1", + "sgx_tstd", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "git+https://github.com/mesalock-linux/rust-url-sgx?tag=sgx_1.1.3#23832f3191456c2d4a0faab10952e1747be58ca8" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "postcard" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a25c0b0ae06fcffe600ad392aabfa535696c8973f2253d9ac83171924c58a858" +dependencies = [ + "postcard-cobs", + "serde 1.0.193", +] + +[[package]] +name = "postcard-cobs" +version = "0.1.5-pre" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c68cb38ed13fd7bc9dd5db8f165b7c8d9c1a315104083a2b10f11354c2af97f" + +[[package]] +name = "ppv-lite86" +version = "0.2.6" +source = "git+https://github.com/mesalock-linux/cryptocorrosion-sgx#32d7de50b5f03a10fe5a42167410be2dd3c2e389" + +[[package]] +name = "primitive-types" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" +dependencies = [ + "fixed-hash", + "impl-codec", + "impl-rlp", + "impl-serde", + "scale-info", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell 1.18.0", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote 1.0.33", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote 1.0.33", + "version_check", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro-nested" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" + +[[package]] +name = "proc-macro-warning" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e99670bafb56b9a106419397343bdbc8b8742c3cc449fec6345f86173f47cd4" +dependencies = [ + "proc-macro2", + "quote 1.0.33", + "syn 2.0.37", +] + +[[package]] +name = "proc-macro2" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quick-error" +version = "1.2.2" +source = "git+https://github.com/mesalock-linux/quick-error-sgx#468bf2cce746f34dd3df8c1c5b4a5a6494914d36" + +[[package]] +name = "quick-protobuf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e489d4a83c17ea69b0291630229b5d4c92a94a3bf0165f7f72f506e94cda8b4b" +dependencies = [ + "byteorder 1.4.3", +] + +[[package]] +name = "quote" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.7.3" +source = "git+https://github.com/mesalock-linux/rand-sgx?tag=sgx_1.1.3#83583f073de3b4f75c3c3ef5e174d484ed941f85" +dependencies = [ + "getrandom 0.1.14", + "rand_chacha", + "rand_core 0.5.1 (git+https://github.com/mesalock-linux/rand-sgx?tag=sgx_1.1.3)", + "sgx_tstd", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "git+https://github.com/mesalock-linux/rand-sgx?tag=sgx_1.1.3#83583f073de3b4f75c3c3ef5e174d484ed941f85" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1 (git+https://github.com/mesalock-linux/rand-sgx?tag=sgx_1.1.3)", + "sgx_tstd", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "git+https://github.com/mesalock-linux/rand-sgx?tag=sgx_1.1.3#83583f073de3b4f75c3c3ef5e174d484ed941f85" +dependencies = [ + "getrandom 0.1.14", + "sgx_tstd", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rcgen" +version = "0.9.2" +source = "git+https://github.com/integritee-network/rcgen#1852c8dbeb74de36a422d218254b659497daf717" +dependencies = [ + "chrono 0.4.11", + "pem", + "ring 0.16.19", + "sgx_tstd", + "yasna", +] + +[[package]] +name = "ref-cast" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acde58d073e9c79da00f2b5b84eed919c8326832648a5b109b3fce1bb1175280" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7473c2cfcf90008193dd0e3e16599455cb601a9fce322b5bb55de799664925" +dependencies = [ + "proc-macro2", + "quote 1.0.33", + "syn 2.0.37", +] + +[[package]] +name = "regex" +version = "1.3.1" +source = "git+https://github.com/mesalock-linux/regex-sgx#76aef86f9836532d17764523d0fa23bb7d2e31cf" +dependencies = [ + "aho-corasick 0.7.10", + "memchr 2.2.1", + "regex-syntax 0.6.12", + "sgx_tstd", + "thread_local", +] + +[[package]] +name = "regex" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" +dependencies = [ + "aho-corasick 1.1.1", + "memchr 2.6.3", + "regex-automata", + "regex-syntax 0.7.5", +] + +[[package]] +name = "regex-automata" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +dependencies = [ + "aho-corasick 1.1.1", + "memchr 2.6.3", + "regex-syntax 0.7.5", +] + +[[package]] +name = "regex-syntax" +version = "0.6.12" +source = "git+https://github.com/mesalock-linux/regex-sgx#76aef86f9836532d17764523d0fa23bb7d2e31cf" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "regex-syntax" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ring" +version = "0.16.19" +source = "git+https://github.com/mesalock-linux/ring-sgx?tag=v0.16.5#844efe271ed78a399d803b2579f5f2424d543c9f" +dependencies = [ + "cc", + "sgx_tstd", + "spin", + "untrusted", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell 1.18.0", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes 1.5.0", + "rlp-derive", + "rustc-hex", +] + +[[package]] +name = "rlp-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" +dependencies = [ + "proc-macro2", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "rust-base58" +version = "0.0.4" +source = "git+https://github.com/mesalock-linux/rust-base58-sgx?rev=sgx_1.1.3#13fb3e0a543690e6e19332f37ba85fd74c56cb2f" +dependencies = [ + "num", + "sgx_tstd", +] + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustls" +version = "0.19.0" +source = "git+https://github.com/mesalock-linux/rustls?tag=sgx_1.1.3#95b5e79dc24b02f3ce424437eb9698509d0baf58" +dependencies = [ + "base64 0.13.0 (git+https://github.com/mesalock-linux/rust-base64-sgx)", + "log", + "ring 0.16.19", + "sct", + "sgx_tstd", + "webpki", +] + +[[package]] +name = "rustls" +version = "0.19.0" +source = "git+https://github.com/mesalock-linux/rustls?branch=mesalock_sgx#95b5e79dc24b02f3ce424437eb9698509d0baf58" +dependencies = [ + "base64 0.13.0 (git+https://github.com/mesalock-linux/rust-base64-sgx)", + "log", + "ring 0.16.19", + "sct", + "sgx_tstd", + "webpki", +] + +[[package]] +name = "rustls" +version = "0.19.0" +source = "git+https://github.com/mesalock-linux/rustls?rev=sgx_1.1.3#95b5e79dc24b02f3ce424437eb9698509d0baf58" +dependencies = [ + "base64 0.13.0 (git+https://github.com/mesalock-linux/rust-base64-sgx)", + "log", + "ring 0.16.19", + "sct", + "sgx_tstd", + "webpki", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "scale-bits" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "036575c29af9b6e4866ffb7fa055dbf623fe7a9cc159b33786de6013a6969d89" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde 1.0.193", +] + +[[package]] +name = "scale-decode" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea509715113edab351e1f4d51fba6b186653259049a1155b52e2e994dd2f0e6d" +dependencies = [ + "parity-scale-codec", + "primitive-types", + "scale-bits", + "scale-decode-derive", + "scale-info", + "smallvec 1.11.1", +] + +[[package]] +name = "scale-decode-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66c9d7a1341497e9d016722144310de3dc6c933909c0376017c88f65092fff37" +dependencies = [ + "darling", + "proc-macro-crate", + "proc-macro2", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "scale-encode" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6f51bc8cd927dab2f4567b1a8a8e9d7fd5d0866f2dbc7c84fc97cfa9383a26" +dependencies = [ + "parity-scale-codec", + "primitive-types", + "scale-bits", + "scale-encode-derive", + "scale-info", + "smallvec 1.11.1", +] + +[[package]] +name = "scale-encode-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28be1877787156a2df01be3c029b92bdffa6b6a9748d4996e383fff218c88f3" +dependencies = [ + "darling", + "proc-macro-crate", + "proc-macro2", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "scale-info" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7d66a1128282b7ef025a8ead62a4a9fcf017382ec53b8ffbf4d7bf77bd3c60" +dependencies = [ + "bitvec", + "cfg-if 1.0.0", + "derive_more", + "parity-scale-codec", + "scale-info-derive", + "serde 1.0.193", +] + +[[package]] +name = "scale-info-derive" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf2c68b89cafb3b8d918dd07b42be0da66ff202cf1155c5739a4e0c1ea0dc19" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "schnorrkel" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "021b403afe70d81eea68f6ea12f6b3c9588e5d536a94c3bf80f15e7faa267862" +dependencies = [ + "arrayref", + "arrayvec 0.5.2", + "curve25519-dalek 2.1.3", + "merlin", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.2", + "subtle", + "zeroize", +] + +[[package]] +name = "sct" +version = "0.6.0" +source = "git+https://github.com/mesalock-linux/sct.rs?branch=mesalock_sgx#c4d859cca232e6c9d88ca12048df3bc26e1ed4ad" +dependencies = [ + "ring 0.16.19", + "sgx_tstd", + "untrusted", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array 0.14.7", + "subtle", + "zeroize", +] + +[[package]] +name = "secp256k1" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" +dependencies = [ + "secp256k1-sys 0.6.1", +] + +[[package]] +name = "secp256k1" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acea373acb8c21ecb5a23741452acd2593ed44ee3d343e72baaa143bc89d0d5" +dependencies = [ + "bitcoin_hashes", + "secp256k1-sys 0.9.1", +] + +[[package]] +name = "secp256k1-sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83080e2c2fc1006e625be82e5d1eb6a43b7fd9578b617fcc55814daf286bba4b" +dependencies = [ + "cc", +] + +[[package]] +name = "secp256k1-sys" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dd97a086ec737e30053fd5c46f097465d25bb81dd3608825f65298c4c98be83" +dependencies = [ + "cc", +] + +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" + +[[package]] +name = "serde" +version = "1.0.118" +source = "git+https://github.com/mesalock-linux/serde-sgx#db0226f1d5d70fca6b96af2c285851502204e21c" +dependencies = [ + "serde_derive 1.0.118", + "sgx_tstd", +] + +[[package]] +name = "serde" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +dependencies = [ + "serde_derive 1.0.193", +] + +[[package]] +name = "serde-big-array" +version = "0.3.0" +source = "git+https://github.com/mesalock-linux/serde-big-array-sgx#94122c5167aee38b39b09a620a60db2c28cf7428" +dependencies = [ + "serde 1.0.118", + "serde_derive 1.0.118", +] + +[[package]] +name = "serde_derive" +version = "1.0.118" +source = "git+https://github.com/mesalock-linux/serde-sgx#db0226f1d5d70fca6b96af2c285851502204e21c" +dependencies = [ + "proc-macro2", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "serde_derive" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +dependencies = [ + "proc-macro2", + "quote 1.0.33", + "syn 2.0.37", +] + +[[package]] +name = "serde_json" +version = "1.0.60" +source = "git+https://github.com/mesalock-linux/serde-json-sgx?tag=sgx_1.1.3#380893814ad2a057758d825bab798aa117f7362a" +dependencies = [ + "indexmap 1.6.1", + "itoa 0.4.5", + "ryu", + "serde 1.0.118", + "sgx_tstd", +] + +[[package]] +name = "serde_json" +version = "1.0.60" +source = "git+https://github.com/mesalock-linux/serde-json-sgx#380893814ad2a057758d825bab798aa117f7362a" +dependencies = [ + "itoa 0.4.5", + "ryu", + "serde 1.0.118", + "sgx_tstd", +] + +[[package]] +name = "serde_json" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +dependencies = [ + "itoa 1.0.9", + "ryu", + "serde 1.0.193", +] + +[[package]] +name = "serde_spanned" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +dependencies = [ + "serde 1.0.193", +] + +[[package]] +name = "sgx_alloc" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" + +[[package]] +name = "sgx_backtrace_sys" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" +dependencies = [ + "cc", + "sgx_build_helper", + "sgx_libc", +] + +[[package]] +name = "sgx_build_helper" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" + +[[package]] +name = "sgx_crypto_helper" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" +dependencies = [ + "itertools 0.11.0", + "serde 1.0.118", + "serde-big-array", + "serde_derive 1.0.118", + "sgx_tcrypto", + "sgx_tstd", + "sgx_types", +] + +[[package]] +name = "sgx_demangle" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" + +[[package]] +name = "sgx_libc" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" +dependencies = [ + "sgx_types", +] + +[[package]] +name = "sgx_rand" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" +dependencies = [ + "sgx_trts", + "sgx_tstd", + "sgx_types", +] + +[[package]] +name = "sgx_serialize" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "sgx_serialize_derive" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" +dependencies = [ + "quote 0.3.15", + "sgx_serialize_derive_internals", + "syn 0.11.11", +] + +[[package]] +name = "sgx_serialize_derive_internals" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" +dependencies = [ + "syn 0.11.11", +] + +[[package]] +name = "sgx_tcrypto" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" +dependencies = [ + "sgx_types", +] + +[[package]] +name = "sgx_tcrypto_helper" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" +dependencies = [ + "sgx_crypto_helper", +] + +[[package]] +name = "sgx_tprotected_fs" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" +dependencies = [ + "sgx_trts", + "sgx_types", +] + +[[package]] +name = "sgx_trts" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" +dependencies = [ + "sgx_libc", + "sgx_types", +] + +[[package]] +name = "sgx_tse" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" +dependencies = [ + "sgx_types", +] + +[[package]] +name = "sgx_tseal" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" +dependencies = [ + "sgx_tcrypto", + "sgx_trts", + "sgx_tse", + "sgx_types", +] + +[[package]] +name = "sgx_tstd" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" +dependencies = [ + "hashbrown_tstd", + "sgx_alloc", + "sgx_backtrace_sys", + "sgx_demangle", + "sgx_libc", + "sgx_tprotected_fs", + "sgx_trts", + "sgx_types", + "sgx_unwind", +] + +[[package]] +name = "sgx_tunittest" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "sgx_types" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" + +[[package]] +name = "sgx_unwind" +version = "1.1.6" +source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" +dependencies = [ + "sgx_build_helper", +] + +[[package]] +name = "sha-1" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + +[[package]] +name = "sha1" +version = "0.6.0" +source = "git+https://github.com/mesalock-linux/rust-sha1-sgx?tag=sgx_1.1.3#482a4d489e860d63a21662aaea988f600f8e20a4" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "sha2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + +[[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 0.3.0", +] + +[[package]] +name = "sha2" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "keccak", + "opaque-debug 0.3.0", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "signature" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "slab" +version = "0.4.2" +source = "git+https://github.com/mesalock-linux/slab-sgx#0b0e6ec2abd588afd2f40fd082bc473d100d0f40" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg 1.1.0", +] + +[[package]] +name = "smallvec" +version = "1.6.1" +source = "git+https://github.com/mesalock-linux/rust-smallvec-sgx#b5925f10aa5bc3370a0fb339140ee063f5a888dd" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "smallvec" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" + +[[package]] +name = "sp-api" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "log", + "parity-scale-codec", + "scale-info", + "sp-api-proc-macro", + "sp-core", + "sp-metadata-ir", + "sp-runtime", + "sp-std", + "sp-version", +] + +[[package]] +name = "sp-api-proc-macro" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "Inflector", + "blake2", + "expander", + "proc-macro-crate", + "proc-macro2", + "quote 1.0.33", + "syn 2.0.37", +] + +[[package]] +name = "sp-application-crypto" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-std", +] + +[[package]] +name = "sp-arithmetic" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "integer-sqrt", + "num-traits 0.2.16", + "parity-scale-codec", + "scale-info", + "sp-std", + "static_assertions", +] + +[[package]] +name = "sp-consensus-grandpa" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "finality-grandpa", + "log", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-application-crypto", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "sp-consensus-slots" +version = "0.10.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-std", + "sp-timestamp", +] + +[[package]] +name = "sp-core" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "array-bytes 4.2.0", + "bitflags", + "blake2", + "bounded-collections", + "ed25519-zebra", + "hash-db 0.16.0", + "hash256-std-hasher", + "libsecp256k1", + "log", + "merlin", + "parity-scale-codec", + "paste", + "primitive-types", + "scale-info", + "schnorrkel", + "secp256k1 0.24.3", + "secrecy", + "sp-core-hashing", + "sp-debug-derive", + "sp-runtime-interface", + "sp-std", + "sp-storage", + "ss58-registry", + "zeroize", +] + +[[package]] +name = "sp-core-hashing" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "blake2b_simd 1.0.2", + "byteorder 1.4.3", + "digest 0.10.7", + "sha2 0.10.7", + "sha3 0.10.8", + "sp-std", + "twox-hash", +] + +[[package]] +name = "sp-core-hashing-proc-macro" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "proc-macro2", + "quote 1.0.33", + "sp-core-hashing", + "syn 2.0.37", +] + +[[package]] +name = "sp-debug-derive" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "proc-macro2", + "quote 1.0.33", + "syn 2.0.37", +] + +[[package]] +name = "sp-externalities" +version = "0.13.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "environmental 1.1.4", + "parity-scale-codec", + "sp-std", + "sp-storage", +] + +[[package]] +name = "sp-inherents" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "impl-trait-for-tuples", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-std", +] + +[[package]] +name = "sp-io" +version = "7.0.0" +dependencies = [ + "itp-sgx-externalities", + "libsecp256k1", + "log", + "parity-scale-codec", + "sgx_tstd", + "sp-core", +] + +[[package]] +name = "sp-metadata-ir" +version = "0.1.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "frame-metadata", + "parity-scale-codec", + "scale-info", + "sp-std", +] + +[[package]] +name = "sp-runtime" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "either", + "hash256-std-hasher", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "paste", + "scale-info", + "sp-application-crypto", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-std", + "sp-weights", +] + +[[package]] +name = "sp-runtime-interface" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "bytes 1.5.0", + "impl-trait-for-tuples", + "parity-scale-codec", + "primitive-types", + "sp-externalities", + "sp-runtime-interface-proc-macro", + "sp-std", + "sp-storage", + "sp-tracing", + "sp-wasm-interface", + "static_assertions", +] + +[[package]] +name = "sp-runtime-interface-proc-macro" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "Inflector", + "proc-macro-crate", + "proc-macro2", + "quote 1.0.33", + "syn 2.0.37", +] + +[[package]] +name = "sp-staking" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "sp-std" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" + +[[package]] +name = "sp-storage" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "parity-scale-codec", + "ref-cast", + "sp-debug-derive", + "sp-std", +] + +[[package]] +name = "sp-timestamp" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "parity-scale-codec", + "sp-inherents", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "sp-tracing" +version = "6.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "parity-scale-codec", + "sp-std", + "tracing", + "tracing-core", +] + +[[package]] +name = "sp-trie" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "hash-db 0.16.0", + "memory-db", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-std", + "trie-db", + "trie-root", +] + +[[package]] +name = "sp-version" +version = "5.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-core-hashing-proc-macro", + "sp-runtime", + "sp-std", + "sp-version-proc-macro", +] + +[[package]] +name = "sp-version-proc-macro" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "parity-scale-codec", + "proc-macro2", + "quote 1.0.33", + "syn 2.0.37", +] + +[[package]] +name = "sp-wasm-interface" +version = "7.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "impl-trait-for-tuples", + "parity-scale-codec", + "sp-std", +] + +[[package]] +name = "sp-weights" +version = "4.0.0" +source = "git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" +dependencies = [ + "parity-scale-codec", + "scale-info", + "smallvec 1.11.1", + "sp-arithmetic", + "sp-core", + "sp-debug-derive", + "sp-std", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "ss58-registry" +version = "1.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6915280e2d0db8911e5032a5c275571af6bdded2916abd691a659be25d3439" +dependencies = [ + "Inflector", + "proc-macro2", + "quote 1.0.33", + "serde 1.0.193", + "serde_json 1.0.107", + "unicode-xid 0.2.4", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" + +[[package]] +name = "strum_macros" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck", + "proc-macro2", + "quote 1.0.33", + "rustversion", + "syn 2.0.37", +] + +[[package]] +name = "substrate-api-client" +version = "0.14.0" +source = "git+https://github.com/scs/substrate-api-client.git?branch=polkadot-v0.9.42-tag-v0.14.0#e4ed74b0fb6c2fd5585f55c2702b97b56d99c7f6" +dependencies = [ + "ac-compose-macros", + "ac-node-api", + "ac-primitives", + "async-trait", + "derive_more", + "frame-metadata", + "hex", + "log", + "maybe-async", + "parity-scale-codec", + "serde 1.0.193", + "serde_json 1.0.107", + "sp-core", + "sp-runtime", + "sp-runtime-interface", +] + +[[package]] +name = "substrate-fixed" +version = "0.5.9" +source = "git+https://github.com/encointer/substrate-fixed?tag=v0.5.9#a4fb461aae6205ffc55bed51254a40c52be04e5d" +dependencies = [ + "parity-scale-codec", + "scale-info", + "typenum 1.16.0", +] + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" +dependencies = [ + "quote 0.3.15", + "synom", + "unicode-xid 0.0.4", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote 1.0.33", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +dependencies = [ + "proc-macro2", + "quote 1.0.33", + "unicode-ident", +] + +[[package]] +name = "synom" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" +dependencies = [ + "unicode-xid 0.0.4", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "teerex-primitives" +version = "0.1.0" +dependencies = [ + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-std", +] + +[[package]] +name = "teerex-primitives" +version = "0.1.0" +source = "git+https://github.com/integritee-network/pallets.git?branch=sdk-v0.12.0-polkadot-v0.9.42#eaf611b79bc9d56b20c155150e99b549bf98436b" +dependencies = [ + "common-primitives", + "derive_more", + "log", + "parity-scale-codec", + "scale-info", + "serde 1.0.193", + "sp-core", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "termcolor" +version = "1.0.5" +source = "git+https://github.com/mesalock-linux/termcolor-sgx#fee5ac79b4a90197d646f3df5e1b45ac56be718b" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "thiserror" +version = "1.0.9" +source = "git+https://github.com/mesalock-linux/thiserror-sgx?tag=sgx_1.1.3#c2f806b88616e06aab0af770366a76885d974fdc" +dependencies = [ + "sgx_tstd", + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.9" +source = "git+https://github.com/mesalock-linux/thiserror-sgx?tag=sgx_1.1.3#c2f806b88616e06aab0af770366a76885d974fdc" +dependencies = [ + "proc-macro2", + "quote 1.0.33", + "syn 1.0.109", +] + +[[package]] +name = "thread_local" +version = "1.0.0" +source = "git+https://github.com/mesalock-linux/thread_local-rs-sgx#a8e6e6ce280c53358f7b9e6febe534cba9950547" +dependencies = [ + "lazy_static", + "sgx_tstd", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "toml" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c226a7bba6d859b63c92c4b4fe69c5b6b72d0cb897dbc8e6012298e6154cb56e" +dependencies = [ + "serde 1.0.193", + "serde_spanned", + "toml_datetime", + "toml_edit 0.20.0", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde 1.0.193", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.0.0", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ff63e60a958cefbb518ae1fd6566af80d9d4be430a33f3723dfc47d1d411d95" +dependencies = [ + "indexmap 2.0.0", + "serde 1.0.193", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if 1.0.0", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" + +[[package]] +name = "trie-db" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "767abe6ffed88a1889671a102c2861ae742726f52e0a5a425b92c9fbfa7e9c85" +dependencies = [ + "hash-db 0.16.0", + "hashbrown 0.13.2", + "log", + "smallvec 1.11.1", +] + +[[package]] +name = "trie-root" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4ed310ef5ab98f5fa467900ed906cb9232dd5376597e00fd4cba2a449d06c0b" +dependencies = [ + "hash-db 0.16.0", +] + +[[package]] +name = "triehash" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1631b201eb031b563d2e85ca18ec8092508e262a3196ce9bd10a67ec87b9f5c" +dependencies = [ + "hash-db 0.15.2", + "rlp", +] + +[[package]] +name = "tt-call" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f195fd851901624eee5a58c4bb2b4f06399148fcd0ed336e6f1cb60a9881df" + +[[package]] +name = "tungstenite" +version = "0.14.0" +source = "git+https://github.com/integritee-network/tungstenite-rs-sgx?branch=sgx-experimental#c87a2c08ea00897bb8b127ca0a5c30c3671492b0" +dependencies = [ + "base64 0.13.0 (git+https://github.com/mesalock-linux/rust-base64-sgx?tag=sgx_1.1.3)", + "byteorder 1.3.4", + "bytes 1.0.1", + "http", + "httparse", + "log", + "rand 0.7.3", + "rustls 0.19.0 (git+https://github.com/mesalock-linux/rustls?tag=sgx_1.1.3)", + "sgx_tstd", + "sha1", + "thiserror", + "url", + "utf-8", + "webpki", + "webpki-roots 0.21.0 (git+https://github.com/mesalock-linux/webpki-roots?tag=sgx_1.1.3)", +] + +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if 1.0.0", + "digest 0.10.7", + "static_assertions", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "git+https://github.com/encointer/typenum?tag=v1.16.0#4c8dddaa8bdd13130149e43b4085ad14e960617f" +dependencies = [ + "parity-scale-codec", + "scale-info", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder 1.4.3", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unicase" +version = "2.6.0" +source = "git+https://github.com/mesalock-linux/unicase-sgx#0b0519348572927118af47af3da4da9ffdca8ec6" +dependencies = [ + "sgx_tstd", + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "git+https://github.com/mesalock-linux/unicode-bidi-sgx#eb10728a635a046e75747849fbc680cbbb7832c7" +dependencies = [ + "matches", + "sgx_tstd", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.12" +source = "git+https://github.com/mesalock-linux/unicode-normalization-sgx#c1b030611969f87d75782c1df77975167cbbd509" +dependencies = [ + "smallvec 1.6.1", +] + +[[package]] +name = "unicode-xid" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "unsigned-varint" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fdeedbf205afadfe39ae559b75c3240f24e257d0ca27e85f85cb82aa19ac35" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.1.1" +source = "git+https://github.com/mesalock-linux/rust-url-sgx?tag=sgx_1.1.3#23832f3191456c2d4a0faab10952e1747be58ca8" +dependencies = [ + "idna", + "matches", + "percent-encoding", + "sgx_tstd", +] + +[[package]] +name = "utf-8" +version = "0.7.4" +source = "git+https://github.com/integritee-network/rust-utf8-sgx?branch=sgx-experimental#b026700da83a2f00f0e9f36f813ef28e447a719e" +dependencies = [ + "sgx_tstd", +] + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" +dependencies = [ + "bumpalo", + "log", + "once_cell 1.18.0", + "proc-macro2", + "quote 1.0.33", + "syn 2.0.37", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" +dependencies = [ + "quote 1.0.33", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" +dependencies = [ + "proc-macro2", + "quote 1.0.33", + "syn 2.0.37", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" + +[[package]] +name = "web-sys" +version = "0.3.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.21.4" +source = "git+https://github.com/mesalock-linux/webpki?branch=mesalock_sgx#8dbe6fbeefadf05582ae47c7fa818b04db49c61e" +dependencies = [ + "ring 0.16.19", + "sgx_tstd", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.21.0" +source = "git+https://github.com/mesalock-linux/webpki-roots?tag=sgx_1.1.3#6ff3be547ac13ccd46ae55605ad6506ce30688ef" +dependencies = [ + "sgx_tstd", + "webpki", +] + +[[package]] +name = "webpki-roots" +version = "0.21.0" +source = "git+https://github.com/mesalock-linux/webpki-roots?branch=mesalock_sgx#6ff3be547ac13ccd46ae55605ad6506ce30688ef" +dependencies = [ + "sgx_tstd", + "webpki", +] + +[[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" + +[[package]] +name = "winnow" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" +dependencies = [ + "memchr 2.6.3", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "yasna" +version = "0.3.1" +source = "git+https://github.com/mesalock-linux/yasna.rs-sgx?rev=sgx_1.1.3#a1f50714cd3eb29608ecf7888cacedc173edfdb2" +dependencies = [ + "bit-vec", + "chrono 0.4.11", + "num-bigint", + "sgx_tstd", +] + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote 1.0.33", + "syn 2.0.37", +] + +[[patch.unused]] +name = "getrandom" +version = "0.2.3" +source = "git+https://github.com/integritee-network/getrandom-sgx?branch=update-v2.3#0a4af01fe1df0e6200192e7a709fd18da413466e" diff --git a/bitacross-worker/enclave-runtime/Cargo.toml b/bitacross-worker/enclave-runtime/Cargo.toml new file mode 100644 index 0000000000..c6eae4b549 --- /dev/null +++ b/bitacross-worker/enclave-runtime/Cargo.toml @@ -0,0 +1,175 @@ +[package] +name = "enclave-runtime" +version = "0.0.1" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[workspace] +members = [] + +[lib] +name = "enclave_runtime" +crate-type = ["staticlib"] + +[features] +default = [] +evm = [ + "ita-sgx-runtime/evm", + "ita-stf/evm", +] +production = ["itp-settings/production", "itp-attestation-handler/production"] +sidechain = ["itp-settings/sidechain", "itp-top-pool-author/sidechain"] +offchain-worker = [ + "itp-settings/offchain-worker", + "itp-top-pool-author/offchain-worker", +] +teeracle = [ + "ita-oracle", + "itp-settings/teeracle", + "itp-top-pool-author/teeracle", +] +test = [ + "ita-stf/test", + "itc-parentchain/test", + "itp-attestation-handler/test", + "itp-extrinsics-factory/mocks", + "itp-sgx-crypto/test", + "itp-sgx-temp-dir", + "itp-stf-executor/test", + "itp-stf-executor/mocks", + "itp-stf-state-handler/test", + "itp-stf-state-observer/mocks", + "itp-storage/test", + "itp-test/sgx", + "itp-top-pool-author/test", + "itp-top-pool-author/mocks", + # substrate + "frame-system", +] +dcap = [] + +[target.'cfg(not(target_env = "sgx"))'.dependencies] +sgx-crypto-helper = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", package = "sgx_tcrypto_helper" } +sgx_rand = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } +sgx_serialize = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } +sgx_serialize_derive = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } +sgx_tcrypto = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } +sgx_trts = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } +sgx_tse = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } +sgx_tseal = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", features = ["untrusted_fs", "net", "backtrace"] } +sgx_tunittest = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } +sgx_types = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } + +[dependencies] +array-bytes = { version = "6.0.0" } +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +derive_more = { version = "0.99.5" } +hex = { version = "0.4.3", default-features = false, features = ["alloc"] } +ipfs-unixfs = { default-features = false, git = "https://github.com/whalelephant/rust-ipfs", branch = "w-nstd" } +lazy_static = { version = "1.1.0", features = ["spin_no_std"] } +primitive-types = { version = "0.12.1", default-features = false, features = ["codec", "serde_no_std"] } + +# scs / integritee +jsonrpc-core = { default-features = false, git = "https://github.com/scs/jsonrpc", branch = "no_std_v18" } + +# mesalock +env_logger = { git = "https://github.com/integritee-network/env_logger-sgx" } +log = { git = "https://github.com/integritee-network/log-sgx" } +# Todo #1313: use the `once_cell` included in rusts core library once we use rust v1.70.0 +once_cell = { git = "https://github.com/mesalock-linux/once_cell-sgx" } +rustls = { rev = "sgx_1.1.3", features = ["dangerous_configuration"], git = "https://github.com/mesalock-linux/rustls" } +serde_json = { tag = "sgx_1.1.3", git = "https://github.com/mesalock-linux/serde-json-sgx" } +webpki = { git = "https://github.com/mesalock-linux/webpki", branch = "mesalock_sgx" } + +# for attestation +base58 = { rev = "sgx_1.1.3", package = "rust-base58", default-features = false, features = ["mesalock_sgx"], git = "https://github.com/mesalock-linux/rust-base58-sgx" } + +cid = { default-features = false, git = "https://github.com/whalelephant/rust-cid", branch = "nstd" } +multibase = { default-features = false, git = "https://github.com/whalelephant/rust-multibase", branch = "nstd" } +teerex-primitives = { default-features = false, git = "https://github.com/integritee-network/pallets.git", branch = "sdk-v0.12.0-polkadot-v0.9.42" } + +# local deps +ita-oracle = { path = "../app-libs/oracle", default-features = false, optional = true, features = ["sgx"] } +ita-parentchain-interface = { path = "../app-libs/parentchain-interface", default-features = false, features = ["sgx"] } +ita-sgx-runtime = { path = "../app-libs/sgx-runtime", default-features = false } +ita-stf = { path = "../app-libs/stf", default-features = false, features = ["sgx"] } +itc-direct-rpc-client = { path = "../core/direct-rpc-client", default-features = false, features = ["sgx"] } +itc-direct-rpc-server = { path = "../core/direct-rpc-server", default-features = false, features = ["sgx"] } +itc-offchain-worker-executor = { path = "../core/offchain-worker-executor", default-features = false, features = ["sgx"] } +itc-parentchain = { path = "../core/parentchain/parentchain-crate", default-features = false, features = ["sgx"] } +itc-parentchain-block-import-dispatcher = { path = "../core/parentchain/block-import-dispatcher", default-features = false, features = ["sgx"] } +itc-parentchain-test = { path = "../core/parentchain/test", default-features = false } +itc-peer-top-broadcaster = { path = "../core/peer-top-broadcaster", default-features = false, features = ["sgx"] } +itc-tls-websocket-server = { path = "../core/tls-websocket-server", default-features = false, features = ["sgx"] } +itp-attestation-handler = { path = "../core-primitives/attestation-handler", default-features = false, features = ["sgx"] } +itp-component-container = { path = "../core-primitives/component-container", default-features = false, features = ["sgx"] } +itp-enclave-metrics = { path = "../core-primitives/enclave-metrics", default-features = false, features = ["sgx"] } +itp-extrinsics-factory = { path = "../core-primitives/extrinsics-factory", default-features = false, features = ["sgx"] } +itp-import-queue = { path = "../core-primitives/import-queue", default-features = false, features = ["sgx"] } +itp-node-api = { path = "../core-primitives/node-api", default-features = false, features = ["sgx"] } +itp-node-api-metadata = { path = "../core-primitives/node-api/metadata", default-features = false } +itp-nonce-cache = { path = "../core-primitives/nonce-cache", default-features = false, features = ["sgx"] } +itp-ocall-api = { path = "../core-primitives/ocall-api", default-features = false } +itp-primitives-cache = { path = "../core-primitives/primitives-cache", default-features = false, features = ["sgx"] } +itp-rpc = { path = "../core-primitives/rpc", default-features = false, features = ["sgx"] } +itp-settings = { path = "../core-primitives/settings" } +itp-sgx-crypto = { path = "../core-primitives/sgx/crypto", default-features = false, features = ["sgx"] } +itp-sgx-externalities = { path = "../core-primitives/substrate-sgx/externalities", default-features = false, features = ["sgx"] } +itp-stf-executor = { path = "../core-primitives/stf-executor", default-features = false, features = ["sgx"] } +itp-stf-interface = { path = "../core-primitives/stf-interface", default-features = false } +itp-stf-primitives = { path = "../core-primitives/stf-primitives", default-features = false } +itp-stf-state-handler = { path = "../core-primitives/stf-state-handler", default-features = false, features = ["sgx"] } +itp-stf-state-observer = { path = "../core-primitives/stf-state-observer", default-features = false, features = ["sgx"] } +itp-storage = { path = "../core-primitives/storage", default-features = false, features = ["sgx"] } +itp-test = { path = "../core-primitives/test", default-features = false, optional = true } +itp-time-utils = { path = "../core-primitives/time-utils", default-features = false, features = ["sgx"] } +itp-top-pool = { path = "../core-primitives/top-pool", default-features = false, features = ["sgx"] } +itp-top-pool-author = { path = "../core-primitives/top-pool-author", default-features = false, features = ["sgx"] } +itp-types = { path = "../core-primitives/types", default-features = false } +itp-utils = { path = "../core-primitives/utils", default-features = false } +its-block-verification = { path = "../sidechain/block-verification", default-features = false } +its-primitives = { path = "../sidechain/primitives", default-features = false } +its-sidechain = { path = "../sidechain/sidechain-crate", default-features = false, features = ["sgx"] } + +# litentry +lc-scheduled-enclave = { path = "../litentry/core/scheduled-enclave", default-features = false, features = ["sgx"] } +litentry-macros = { path = "../litentry/macros" } +litentry-primitives = { path = "../litentry/primitives", default-features = false, features = ["sgx"] } + +# substrate deps +frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +frame-system = { optional = true, default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-core = { default-features = false, features = ["full_crypto"], git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# test-deps +itp-sgx-temp-dir = { version = "0.1", default-features = false, optional = true, path = "../core-primitives/sgx/temp-dir" } + +[patch.crates-io] +env_logger = { git = "https://github.com/integritee-network/env_logger-sgx" } +getrandom = { git = "https://github.com/integritee-network/getrandom-sgx", branch = "update-v2.3" } +log = { git = "https://github.com/integritee-network/log-sgx" } + +[patch."https://github.com/mesalock-linux/log-sgx"] +log = { git = "https://github.com/integritee-network/log-sgx" } + +[patch."https://github.com/paritytech/substrate"] +sp-io = { path = "../core-primitives/substrate-sgx/sp-io" } + +[patch."https://github.com/apache/teaclave-sgx-sdk.git"] +sgx_alloc = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } +sgx_crypto_helper = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } +sgx_libc = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } +sgx_rand = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } +sgx_serialize = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } +sgx_serialize_derive = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } +sgx_serialize_derive_internals = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } +sgx_tcrypto = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } +sgx_tcrypto_helper = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } +sgx_trts = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } +sgx_tse = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } +sgx_tseal = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } +sgx_tstd = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } +sgx_tunittest = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } +sgx_types = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } diff --git a/bitacross-worker/enclave-runtime/Enclave.config.production.xml b/bitacross-worker/enclave-runtime/Enclave.config.production.xml new file mode 100644 index 0000000000..00336e8aa8 --- /dev/null +++ b/bitacross-worker/enclave-runtime/Enclave.config.production.xml @@ -0,0 +1,12 @@ + + + 0 + 0 + 0x40000 + 0x20000000 + 32 + 0 + 1 + 0 + 0xFFFFFFFF + diff --git a/bitacross-worker/enclave-runtime/Enclave.config.xml b/bitacross-worker/enclave-runtime/Enclave.config.xml new file mode 100644 index 0000000000..62e08c1a5f --- /dev/null +++ b/bitacross-worker/enclave-runtime/Enclave.config.xml @@ -0,0 +1,12 @@ + + + 0 + 0 + 0x40000 + 0x20000000 + 32 + 0 + 0 + 0 + 0xFFFFFFFF + diff --git a/bitacross-worker/enclave-runtime/Enclave.edl b/bitacross-worker/enclave-runtime/Enclave.edl new file mode 100644 index 0000000000..04c02fea61 --- /dev/null +++ b/bitacross-worker/enclave-runtime/Enclave.edl @@ -0,0 +1,277 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +enclave { + from "sgx_backtrace.edl" import *; + from "sgx_tstd.edl" import *; + from "sgx_stdio.edl" import *; + from "sgx_backtrace.edl" import *; + from "sgx_tstdc.edl" import *; + from "sgx_tprotected_fs.edl" import *; + from "sgx_fs.edl" import *; + from "sgx_net.edl" import *; + from "sgx_time.edl" import *; + from "sgx_env.edl" import *; + from "sgx_thread.edl" import *; + from "sgx_pipe.edl" import *; + from "sgx_file.edl" import *; + from "sgx_dcap_tvl.edl" import *; + + include "sgx_quote.h" + include "sgx_report.h" + include "sgx_ql_quote.h" + include "sgx_qve_header.h" + + trusted { + /* define ECALLs here. */ + public sgx_status_t init( + [in, size=mu_ra_addr_size] uint8_t* mu_ra_addr, uint32_t mu_ra_addr_size, + [in, size=untrusted_worker_addr_size] uint8_t* untrusted_worker_addr, uint32_t untrusted_worker_addr_size, + [in, size=encoded_base_dir_size] uint8_t* encoded_base_dir_str, uint32_t encoded_base_dir_size + ); + + public sgx_status_t init_enclave_sidechain_components( + [in, size=fail_mode_size] uint8_t* fail_mode, uint32_t fail_mode_size, + [in, size=fail_at_size] uint8_t* fail_at, uint32_t fail_at_size + ); + + public sgx_status_t init_direct_invocation_server( + [in, size=server_addr_size] uint8_t* server_addr, uint32_t server_addr_size + ); + + public sgx_status_t init_parentchain_components( + [in, size=params_size] uint8_t* params, size_t params_size, + [out, size=latest_header_size] uint8_t* latest_header, size_t latest_header_size + ); + + public sgx_status_t init_shard( + [in, size=shard_size] uint8_t* shard, uint32_t shard_size + ); + + public sgx_status_t init_proxied_shard_vault( + [in, size=shard_size] uint8_t* shard, uint32_t shard_size, + [in, size=parentchain_id_size] uint8_t* parentchain_id, uint32_t parentchain_id_size + ); + + public sgx_status_t execute_trusted_calls(); + + public sgx_status_t sync_parentchain( + [in, size=blocks_size] uint8_t* blocks, size_t blocks_size, + [in, size=events_size] uint8_t* events, size_t events_size, + [in, size=events_proofs_size] uint8_t* events_proofs, size_t events_proofs_size, + [in, size=parentchain_id_size] uint8_t* parentchain_id, uint32_t parentchain_id_size, + int is_syncing + ); + + public sgx_status_t set_nonce( + [in] uint32_t* nonce, + [in, size=parentchain_id_size] uint8_t* parentchain_id, uint32_t parentchain_id_size + ); + + public sgx_status_t set_node_metadata( + [in, size=node_metadata_size] uint8_t* node_metadata, uint32_t node_metadata_size, + [in, size=parentchain_id_size] uint8_t* parentchain_id, uint32_t parentchain_id_size + ); + + public sgx_status_t get_rsa_encryption_pubkey( + [out, size=pubkey_size] uint8_t* pubkey, uint32_t pubkey_size); + + public sgx_status_t get_ecc_signing_pubkey( + [out, size=pubkey_size] uint8_t* pubkey, uint32_t pubkey_size); + + public sgx_status_t get_ecc_vault_pubkey( + [in, size=shard_size] uint8_t* shard, uint32_t shard_size, + [out, size=pubkey_size] uint8_t* pubkey, uint32_t pubkey_size); + + public sgx_status_t get_mrenclave( + [out, size=mrenclave_size] uint8_t* mrenclave, uint32_t mrenclave_size); + + public sgx_status_t generate_ias_ra_extrinsic( + [in, size=w_url_size] uint8_t* w_url, uint32_t w_url_size, + [out, size=unchecked_extrinsic_max_size] uint8_t* unchecked_extrinsic, uint32_t unchecked_extrinsic_max_size, + [out] uint32_t* unchecked_extrinsic_size, + int skip_ra + ); + public sgx_status_t generate_dcap_ra_quote( + int skip_ra, + [in] const sgx_target_info_t* quoting_enclave_target_info, + uint32_t quote_size, + [out, size=dcap_quote_size] uint8_t* dcap_quote_p, uint32_t dcap_quote_size + ); + + public sgx_status_t generate_dcap_ra_extrinsic_from_quote( + [in, size=w_url_size] uint8_t* w_url, uint32_t w_url_size, + [in, size=quote_size] uint8_t* quote, uint32_t quote_size, + [out, size=unchecked_extrinsic_max_size] uint8_t* unchecked_extrinsic, uint32_t unchecked_extrinsic_max_size, + [out] uint32_t* unchecked_extrinsic_size + ); + + public sgx_status_t generate_dcap_ra_extrinsic( + [in, size=w_url_size] uint8_t* w_url, uint32_t w_url_size, + [out, size=unchecked_extrinsic_max_size] uint8_t* unchecked_extrinsic, uint32_t unchecked_extrinsic_max_size, + [out] uint32_t* unchecked_extrinsic_size, + int skip_ra, + [in] const sgx_target_info_t* quoting_enclave_target_info, + [in] uint32_t* quote_size + ); + + public sgx_status_t generate_register_quoting_enclave_extrinsic( + [in] const sgx_ql_qve_collateral_t *p_quote_collateral, + [out, size=unchecked_extrinsic_max_size] uint8_t* unchecked_extrinsic, uint32_t unchecked_extrinsic_max_size, + [out] uint32_t* unchecked_extrinsic_size + ); + + public sgx_status_t generate_register_tcb_info_extrinsic( + [in] const sgx_ql_qve_collateral_t *p_quote_collateral, + [out, size=unchecked_extrinsic_max_size] uint8_t* unchecked_extrinsic, uint32_t unchecked_extrinsic_max_size, + [out] uint32_t* unchecked_extrinsic_size + ); + + public sgx_status_t update_market_data_xt( + [in, size=crypto_currency_size] uint8_t* crypto_currency, uint32_t crypto_currency_size, + [in, size=fiat_currency_size] uint8_t* fiat_currency, uint32_t fiat_currency_size, + [out, size=unchecked_extrinsic_max_size] uint8_t* unchecked_extrinsic, uint32_t unchecked_extrinsic_max_size, + [out] uint32_t* unchecked_extrinsic_size + ); + + public sgx_status_t update_weather_data_xt( + [in, size=weather_info_logitude_size] uint8_t* weather_info_logitude, uint32_t weather_info_logitude_size, + [in, size=weather_info_latitude_size] uint8_t* weather_info_latitude, uint32_t weather_info_latitude_size, + [out, size=unchecked_extrinsic_max_size] uint8_t* unchecked_extrinsic, uint32_t unchecked_extrinsic_max_size, + [out] uint32_t* unchecked_extrinsic_size + ); + + public sgx_status_t dump_ias_ra_cert_to_disk(); + + public sgx_status_t dump_dcap_ra_cert_to_disk([in] const sgx_target_info_t* quoting_enclave_target_info, uint32_t quote_size); + + public sgx_status_t dump_dcap_collateral_to_disk([in] const sgx_ql_qve_collateral_t *p_quote_collateral); + + public sgx_status_t run_state_provisioning_server( + int fd, + sgx_quote_sign_type_t quote_type, + [in] sgx_target_info_t* quoting_enclave_target_info, + [in] uint32_t* quote_size, + int skip_ra + ); + public sgx_status_t request_state_provisioning( + int fd, + sgx_quote_sign_type_t quote_type, + [in] sgx_target_info_t* quoting_enclave_target_info, + [in] uint32_t* quote_size, + [in, size=shard_size] uint8_t* shard, uint32_t shard_size, + int skip_ra + ); + + public sgx_status_t call_rpc_methods( + [in, size=request_len] uint8_t* request, uint32_t request_len, + [out, size=response_len] uint8_t* response, uint32_t response_len + ); + + public size_t test_main_entrance(); + + public sgx_status_t migrate_shard( + [in, size=shard_size] uint8_t* old_shard, + [in, size=shard_size] uint8_t* new_shard, + uint32_t shard_size + ); + + public sgx_status_t ignore_parentchain_block_import_validation_until( + [in] uint32_t* until + ); + }; + + untrusted { + sgx_status_t ocall_sgx_init_quote( + [out] sgx_target_info_t *ret_ti, + [out] sgx_epid_group_id_t *ret_gid + ); + + sgx_status_t ocall_get_ias_socket([out] int *ret_fd); + + sgx_status_t ocall_get_quote( + [in, size = sigrl_len] uint8_t * p_sigrl, uint32_t sigrl_len, + [in] sgx_report_t *report, sgx_quote_sign_type_t quote_type, + [in] sgx_spid_t *p_spid, [in] sgx_quote_nonce_t *p_nonce, + [out] sgx_report_t *p_qe_report, + [out, size = maxlen] sgx_quote_t *p_quote, uint32_t maxlen, + [out] uint32_t* p_quote_len + ); + + sgx_status_t ocall_get_dcap_quote( + [in] sgx_report_t *report, + [out, size = quote_size] sgx_quote_t *p_quote, uint32_t quote_size + ); + + sgx_status_t ocall_get_qve_report_on_quote( + [in, size = quote_size] const uint8_t * quote, uint32_t quote_size, + time_t current_time, + [in] const sgx_ql_qve_collateral_t *p_quote_collateral, + [out] uint32_t *collateral_expiration_status, + [out] sgx_ql_qv_result_t *quote_verification_result, + [in, out] sgx_ql_qe_report_info_t *qve_report_info, + [out, size=supplemental_data_size] uint8_t *p_supplemental_data, + uint32_t supplemental_data_size + ); + + sgx_status_t ocall_get_update_info( + [in] sgx_platform_info_t * platformBlob, int32_t enclaveTrusted, + [out] sgx_update_info_bit_t * update_info + ); + + sgx_status_t ocall_read_ipfs( + [in, size = cid_size] uint8_t * cid, uint32_t cid_size + ); + + sgx_status_t ocall_write_ipfs( + [in, size = state_size] uint8_t * enc_state, uint32_t state_size, + [out, size = cid_size] uint8_t * cid, uint32_t cid_size + ); + + sgx_status_t ocall_worker_request( + [in, size = req_size] uint8_t * request, uint32_t req_size, + [in, size=parentchain_id_size] uint8_t* parentchain_id, uint32_t parentchain_id_size, + [out, size = resp_size] uint8_t * response, uint32_t resp_size + ); + + sgx_status_t ocall_update_metric( + [in, size = metric_size] uint8_t * metric, uint32_t metric_size + ); + + sgx_status_t ocall_propose_sidechain_blocks( + [in, size = signed_blocks_size] uint8_t * signed_blocks, uint32_t signed_blocks_size + ); + + sgx_status_t ocall_store_sidechain_blocks( + [in, size = signed_blocks_size] uint8_t * signed_blocks, uint32_t signed_blocks_size + ); + + sgx_status_t ocall_fetch_sidechain_blocks_from_peer( + [in, size = last_imported_block_hash_size] uint8_t * last_imported_block_hash, uint32_t last_imported_block_hash_size, + [in, size = maybe_until_block_hash_size] uint8_t * maybe_until_block_hash, uint32_t maybe_until_block_hash_size, + [in, size = shard_identifier_size] uint8_t * shard_identifier, uint32_t shard_identifier_size, + [out, size = sidechain_blocks_size] uint8_t * sidechain_blocks, uint32_t sidechain_blocks_size + ); + + sgx_status_t ocall_get_trusted_peers_urls([out, size = peers_size] uint8_t * peers, uint32_t peers_size); + + sgx_status_t ocall_send_to_parentchain( + [in, size = extrinsics_size] uint8_t * extrinsics, uint32_t extrinsics_size, + [in, size=parentchain_id_size] uint8_t* parentchain_id, uint32_t parentchain_id_size, + int await_each_inclusion + ); + }; +}; diff --git a/bitacross-worker/enclave-runtime/Enclave.lds b/bitacross-worker/enclave-runtime/Enclave.lds new file mode 100644 index 0000000000..e3d9d0ee0d --- /dev/null +++ b/bitacross-worker/enclave-runtime/Enclave.lds @@ -0,0 +1,9 @@ +enclave.so +{ + global: + g_global_data_sim; + g_global_data; + enclave_entry; + local: + *; +}; diff --git a/bitacross-worker/enclave-runtime/Enclave_private.pem b/bitacross-worker/enclave-runtime/Enclave_private.pem new file mode 100644 index 0000000000..529d07be35 --- /dev/null +++ b/bitacross-worker/enclave-runtime/Enclave_private.pem @@ -0,0 +1,39 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIG4gIBAAKCAYEAroOogvsj/fZDZY8XFdkl6dJmky0lRvnWMmpeH41Bla6U1qLZ +AmZuyIF+mQC/cgojIsrBMzBxb1kKqzATF4+XwPwgKz7fmiddmHyYz2WDJfAjIveJ +ZjdMjM4+EytGlkkJ52T8V8ds0/L2qKexJ+NBLxkeQLfV8n1mIk7zX7jguwbCG1Pr +nEMdJ3Sew20vnje+RsngAzdPChoJpVsWi/K7cettX/tbnre1DL02GXc5qJoQYk7b +3zkmhz31TgFrd9VVtmUGyFXAysuSAb3EN+5VnHGr0xKkeg8utErea2FNtNIgua8H +ONfm9Eiyaav1SVKzPHlyqLtcdxH3I8Wg7yqMsaprZ1n5A1v/levxnL8+It02KseD +5HqV4rf/cImSlCt3lpRg8U5E1pyFQ2IVEC/XTDMiI3c+AR+w2jSRB3Bwn9zJtFlW +KHG3m1xGI4ck+Lci1JvWWLXQagQSPtZTsubxTQNx1gsgZhgv1JHVZMdbVlAbbRMC +1nSuJNl7KPAS/VfzAgEDAoIBgHRXxaynbVP5gkO0ug6Qw/E27wzIw4SmjsxG6Wpe +K7kfDeRskKxESdsA/xCrKkwGwhcx1iIgS5+Qscd1Yg+1D9X9asd/P7waPmWoZd+Z +AhlKwhdPsO7PiF3e1AzHhGQwsUTt/Y/aSI1MpHBvy2/s1h9mFCslOUxTmWw0oj/Q +ldIEgWeNR72CE2+jFIJIyml6ftnb6qzPiga8Bm48ubKh0kvySOqnkmnPzgh+JBD6 +JnBmtZbfPT97bwTT+N6rnPqOOApvfHPf15kWI8yDbprG1l4OCUaIUH1AszxLd826 +5IPM+8gINLRDP1MA6azECPjTyHXhtnSIBZCyWSVkc05vYmNXYUNiXWMajcxW9M02 +wKzFELO8NCEAkaTPxwo4SCyIjUxiK1LbQ9h8PSy4c1+gGP4LAMR8xqP4QKg6zdu9 +osUGG/xRe/uufgTBFkcjqBHtK5L5VI0jeNIUAgW/6iNbYXjBMJ0GfauLs+g1VsOm +WfdgXzsb9DYdMa0OXXHypmV4GwKBwQDUwQj8RKJ6c8cT4vcWCoJvJF00+RFL+P3i +Gx2DLERxRrDa8AVGfqaCjsR+3vLgG8V/py+z+dxZYSqeB80Qeo6PDITcRKoeAYh9 +xlT3LJOS+k1cJcEmlbbO2IjLkTmzSwa80fWexKu8/Xv6vv15gpqYl1ngYoqJM3pd +vzmTIOi7MKSZ0WmEQavrZj8zK4endE3v0eAEeQ55j1GImbypSf7Idh7wOXtjZ7WD +Dg6yWDrri+AP/L3gClMj8wsAxMV4ZR8CgcEA0fzDHkFa6raVOxWnObmRoDhAtE0a +cjUj976NM5yyfdf2MrKy4/RhdTiPZ6b08/lBC/+xRfV3xKVGzacm6QjqjZrUpgHC +0LKiZaMtccCJjLtPwQd0jGQEnKfMFaPsnhOc5y8qVkCzVOSthY5qhz0XNotHHFmJ +gffVgB0iqrMTvSL7IA2yqqpOqNRlhaYhNl8TiFP3gIeMtVa9rZy31JPgT2uJ+kfo +gV7sdTPEjPWZd7OshGxWpT6QfVDj/T9T7L6tAoHBAI3WBf2DFvxNL2KXT2QHAZ9t +k3imC4f7U+wSE6zILaDZyzygA4RUbwG0gv8/TJVn2P/Eynf76DuWHGlaiLWnCbSz +Az2DHBQBBaku409zDQym3j1ugMRjzzSQWzJg0SIyBH3hTmnYcn3+Uqcp/lEBvGW6 +O+rsXFt3pukqJmIV8HzLGGaLm62BHUeZf3dyWm+i3p/hQAL7Xvu04QW70xuGqdr5 +afV7p5eaeQIJXyGQJ0eylV/90+qxjMKiB1XYg6WYvwKBwQCL/ddpgOdHJGN8uRom +e7Zq0Csi3hGheMKlKbN3vcxT5U7MdyHtTZZOJbTvxKNNUNYH/8uD+PqDGNneb29G +BfGzvI3EASyLIcGZF3OhKwZd0jUrWk2y7Vhob91jwp2+t73vdMbkKyI4mHOuXvGv +fg95si9oO7EBT+Oqvhccd2J+F1IVXncccYnF4u5ZGWt5lLewN/pVr7MjjykeaHqN +t+rfnQam2psA6fL4zS2zTmZPzR2tnY8Y1GBTi0Ko1OKd1HMCgcAb5cB/7/AQlhP9 +yQa04PLH9ygQkKKptZp7dy5WcWRx0K/hAHRoi2aw1wZqfm7VBNu2SLcs90kCCCxp +6C5sfJi6b8NpNbIPC+sc9wsFr7pGo9SFzQ78UlcWYK2Gu2FxlMjonhka5hvo4zvg +WxlpXKEkaFt3gLd92m/dMqBrHfafH7VwOJY2zT3WIpjwuk0ZzmRg5p0pG/svVQEH +NZmwRwlopysbR69B/n1nefJ84UO50fLh5s5Zr3gBRwbWNZyzhXk= +-----END RSA PRIVATE KEY----- diff --git a/bitacross-worker/enclave-runtime/Makefile b/bitacross-worker/enclave-runtime/Makefile new file mode 100644 index 0000000000..b4dc322eed --- /dev/null +++ b/bitacross-worker/enclave-runtime/Makefile @@ -0,0 +1,58 @@ +# Copyright (C) 2017-2018 Baidu, Inc. All Rights Reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Baidu, Inc., nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +######## Worker Feature Settings ######## +# Set sidechain as default feature mode +WORKER_MODE ?= sidechain + +Rust_Enclave_Name := libenclave.a +Rust_Enclave_Files := $(wildcard src/*.rs) $(wildcard ../stf/src/*.rs) +RUSTFLAGS :="-C target-feature=+avx2" + +ifeq ($(SGX_DEBUG), 1) + OUTPUT_PATH := debug + CARGO_TARGET := +else + OUTPUT_PATH := release + CARGO_TARGET := --release +endif + +ifeq ($(SGX_PRODUCTION), 1) + ENCLAVE_FEATURES = --features=production,$(WORKER_MODE),$(ADDITIONAL_FEATURES) +else + ENCLAVE_FEATURES = --features=test,$(WORKER_MODE),$(ADDITIONAL_FEATURES) +endif + +.PHONY: all + +all: $(Rust_Enclave_Name) + +$(Rust_Enclave_Name): $(Rust_Enclave_Files) + RUSTFLAGS=$(RUSTFLAGS) cargo build $(CARGO_TARGET) $(ENCLAVE_FEATURES) + cp ./target/$(OUTPUT_PATH)/libenclave_runtime.a ../lib/libenclave.a + diff --git a/bitacross-worker/enclave-runtime/README.md b/bitacross-worker/enclave-runtime/README.md new file mode 100644 index 0000000000..a4b88a52d1 --- /dev/null +++ b/bitacross-worker/enclave-runtime/README.md @@ -0,0 +1,2 @@ +# sidechain dependency graph +cargo depgraph --features dcap,sidechain --include enclave-runtime,itp-types,ita-stf | dot -Tsvg > dependency-graph.svg diff --git a/bitacross-worker/enclave-runtime/rust-toolchain.toml b/bitacross-worker/enclave-runtime/rust-toolchain.toml new file mode 100644 index 0000000000..23ed88e6c8 --- /dev/null +++ b/bitacross-worker/enclave-runtime/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "nightly-2022-10-22" +targets = ["wasm32-unknown-unknown"] +profile = "default" # include rustfmt, clippy diff --git a/bitacross-worker/enclave-runtime/rustfmt.toml b/bitacross-worker/enclave-runtime/rustfmt.toml new file mode 100644 index 0000000000..104b9aa998 --- /dev/null +++ b/bitacross-worker/enclave-runtime/rustfmt.toml @@ -0,0 +1,18 @@ +# Basic +hard_tabs = true +max_width = 100 +use_small_heuristics = "Max" +# Imports +imports_granularity = "Crate" +reorder_imports = true +# Consistency +newline_style = "Unix" +# Misc +chain_width = 80 +spaces_around_ranges = false +match_arm_leading_pipes = "Preserve" +match_arm_blocks = false +match_block_trailing_comma = true +trailing_comma = "Vertical" +trailing_semicolon = false +use_field_init_shorthand = true \ No newline at end of file diff --git a/bitacross-worker/enclave-runtime/src/attestation.rs b/bitacross-worker/enclave-runtime/src/attestation.rs new file mode 100644 index 0000000000..5b7f7ded3a --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/attestation.rs @@ -0,0 +1,554 @@ +// Copyright 2022 Integritee AG and Supercomputing Systems AG +// Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in +// the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Baidu, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{ + initialization::global_components::{ + GLOBAL_ATTESTATION_HANDLER_COMPONENT, GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT, + }, + utils::{ + get_extrinsic_factory_from_integritee_solo_or_parachain, + get_node_metadata_repository_from_integritee_solo_or_parachain, + }, + Error as EnclaveError, Result as EnclaveResult, +}; +use codec::{Decode, Encode}; +use itp_attestation_handler::{AttestationHandler, RemoteAttestationType, SgxQlQveCollateral}; +use itp_component_container::ComponentGetter; +use itp_extrinsics_factory::CreateExtrinsics; +use itp_node_api::metadata::{ + pallet_teerex::TeerexCallIndexes, + provider::{AccessNodeMetadata, Error as MetadataProviderError}, + Error as MetadataError, +}; +use itp_node_api_metadata::NodeMetadata; +use itp_settings::worker::MR_ENCLAVE_SIZE; +use itp_sgx_crypto::{ + ed25519_derivation::DeriveEd25519, key_repository::AccessKey, Error as SgxCryptoError, +}; +use itp_types::OpaqueCall; +use itp_utils::write_slice_and_whitespace_pad; +use log::*; +use sgx_types::*; +use sp_core::Pair; +use sp_runtime::OpaqueExtrinsic; +use std::{prelude::v1::*, slice, vec::Vec}; + +#[no_mangle] +pub unsafe extern "C" fn get_mrenclave(mrenclave: *mut u8, mrenclave_size: usize) -> sgx_status_t { + if mrenclave.is_null() || mrenclave_size < MR_ENCLAVE_SIZE { + return sgx_status_t::SGX_ERROR_INVALID_PARAMETER + } + let attestation_handler = match GLOBAL_ATTESTATION_HANDLER_COMPONENT.get() { + Ok(r) => r, + Err(e) => { + error!("Component get failure: {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + match attestation_handler.get_mrenclave() { + Ok(mrenclave_value) => { + let mrenclave_slice = slice::from_raw_parts_mut(mrenclave, mrenclave_size); + if let Err(e) = + write_slice_and_whitespace_pad(mrenclave_slice, mrenclave_value.to_vec()) + { + error!("Failed to transfer mrenclave to o-call buffer: {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + } + sgx_status_t::SGX_SUCCESS + }, + Err(e) => e.into(), + } +} + +// FIXME: add dcap suppoort for call site +pub fn create_ra_report_and_signature( + skip_ra: bool, + remote_attestation_type: RemoteAttestationType, + sign_type: sgx_quote_sign_type_t, + quoting_enclave_target_info: Option<&sgx_target_info_t>, + quote_size: Option<&u32>, +) -> EnclaveResult<(Vec, Vec)> { + let attestation_handler = match GLOBAL_ATTESTATION_HANDLER_COMPONENT.get() { + Ok(r) => r, + Err(e) => { + error!("Component get failure: {:?}", e); + return Err(e.into()) + }, + }; + + match remote_attestation_type { + RemoteAttestationType::Epid => { + match attestation_handler.create_epid_ra_report_and_signature(sign_type, skip_ra) { + Ok(epid) => Ok(epid), + Err(e) => { + error!("create_epid_ra_report_and_signature failure: {:?}", e); + Err(e.into()) + }, + } + }, + RemoteAttestationType::Dcap => { + match attestation_handler.generate_dcap_ra_cert( + quoting_enclave_target_info, + quote_size, + skip_ra, + ) { + Ok((key_der, cert_der, _qe_quote)) => Ok((key_der, cert_der)), + Err(e) => { + error!("generate_dcap_ra_cert failure: {:?}", e); + Err(e.into()) + }, + } + }, + } +} + +#[no_mangle] +pub unsafe extern "C" fn generate_ias_ra_extrinsic( + w_url: *const u8, + w_url_size: u32, + unchecked_extrinsic: *mut u8, + unchecked_extrinsic_max_size: u32, + unchecked_extrinsic_size: *mut u32, + skip_ra: c_int, +) -> sgx_status_t { + if w_url.is_null() || unchecked_extrinsic.is_null() { + return sgx_status_t::SGX_ERROR_INVALID_PARAMETER + } + let mut url_slice = slice::from_raw_parts(w_url, w_url_size as usize); + let url = match String::decode(&mut url_slice) { + Ok(url) => url, + Err(_) => + return EnclaveError::Other("Could not decode url slice to a valid String".into()).into(), + }; + let extrinsic_slice = + slice::from_raw_parts_mut(unchecked_extrinsic, unchecked_extrinsic_max_size as usize); + + let extrinsic = match generate_ias_ra_extrinsic_internal(url, skip_ra == 1) { + Ok(xt) => xt, + Err(e) => return e.into(), + }; + + *unchecked_extrinsic_size = + match write_slice_and_whitespace_pad(extrinsic_slice, extrinsic.encode()) { + Ok(l) => l as u32, + Err(e) => return EnclaveError::BufferError(e).into(), + }; + sgx_status_t::SGX_SUCCESS +} + +#[no_mangle] +pub unsafe extern "C" fn generate_dcap_ra_extrinsic( + w_url: *const u8, + w_url_size: u32, + unchecked_extrinsic: *mut u8, + unchecked_extrinsic_max_size: u32, + unchecked_extrinsic_size: *mut u32, + skip_ra: c_int, + quoting_enclave_target_info: Option<&sgx_target_info_t>, + quote_size: Option<&u32>, +) -> sgx_status_t { + if w_url.is_null() || unchecked_extrinsic.is_null() { + return sgx_status_t::SGX_ERROR_INVALID_PARAMETER + } + let mut url_slice = slice::from_raw_parts(w_url, w_url_size as usize); + let url = match String::decode(&mut url_slice) { + Ok(url) => url, + Err(_) => + return EnclaveError::Other("Could not decode url slice to a valid String".into()).into(), + }; + let extrinsic_slice = + slice::from_raw_parts_mut(unchecked_extrinsic, unchecked_extrinsic_max_size as usize); + + let extrinsic = match generate_dcap_ra_extrinsic_internal( + url, + skip_ra == 1, + quoting_enclave_target_info, + quote_size, + ) { + Ok(xt) => xt, + Err(e) => return e.into(), + }; + + *unchecked_extrinsic_size = + match write_slice_and_whitespace_pad(extrinsic_slice, extrinsic.encode()) { + Ok(l) => l as u32, + Err(e) => return EnclaveError::BufferError(e).into(), + }; + sgx_status_t::SGX_SUCCESS +} + +pub fn generate_dcap_ra_extrinsic_internal( + url: String, + skip_ra: bool, + quoting_enclave_target_info: Option<&sgx_target_info_t>, + quote_size: Option<&u32>, +) -> EnclaveResult { + let attestation_handler = GLOBAL_ATTESTATION_HANDLER_COMPONENT.get()?; + + if !skip_ra { + let (_priv_key_der, _cert_der, dcap_quote) = attestation_handler.generate_dcap_ra_cert( + quoting_enclave_target_info, + quote_size, + skip_ra, + )?; + + generate_dcap_ra_extrinsic_from_quote_internal(url, &dcap_quote) + } else { + generate_dcap_skip_ra_extrinsic_from_mr_enclave( + url, + &attestation_handler.get_mrenclave()?.encode(), + ) + } +} + +#[no_mangle] +pub unsafe extern "C" fn generate_dcap_ra_quote( + skip_ra: c_int, + quoting_enclave_target_info: &sgx_target_info_t, + quote_size: u32, + dcap_quote_p: *mut u8, + dcap_quote_size: u32, +) -> sgx_status_t { + if dcap_quote_p.is_null() { + return sgx_status_t::SGX_ERROR_INVALID_PARAMETER + } + let dcap_quote = match generate_dcap_ra_quote_internal( + skip_ra == 1, + quoting_enclave_target_info, + quote_size, + ) { + Ok(dcap_quote) => dcap_quote, + Err(e) => return e.into(), + }; + + let dcap_quote_slice = slice::from_raw_parts_mut(dcap_quote_p, dcap_quote_size as usize); + + if let Err(e) = write_slice_and_whitespace_pad(dcap_quote_slice, dcap_quote) { + return EnclaveError::BufferError(e).into() + }; + + sgx_status_t::SGX_SUCCESS +} + +pub fn generate_dcap_ra_quote_internal( + skip_ra: bool, + quoting_enclave_target_info: &sgx_target_info_t, + quote_size: u32, +) -> EnclaveResult> { + let attestation_handler = GLOBAL_ATTESTATION_HANDLER_COMPONENT.get()?; + + let (_priv_key_der, _cert_der, dcap_quote) = attestation_handler.generate_dcap_ra_cert( + Some(quoting_enclave_target_info), + Some("e_size), + skip_ra, + )?; + + Ok(dcap_quote) +} + +#[no_mangle] +pub unsafe extern "C" fn generate_dcap_ra_extrinsic_from_quote( + w_url: *const u8, + w_url_size: u32, + quote: *const u8, + quote_size: u32, + unchecked_extrinsic: *mut u8, + unchecked_extrinsic_max_size: u32, + unchecked_extrinsic_size: *mut u32, +) -> sgx_status_t { + if w_url.is_null() || unchecked_extrinsic.is_null() { + return sgx_status_t::SGX_ERROR_INVALID_PARAMETER + } + let mut url_slice = slice::from_raw_parts(w_url, w_url_size as usize); + let url = match String::decode(&mut url_slice) { + Ok(url) => url, + Err(_) => + return EnclaveError::Other("Could not decode url slice to a valid String".into()).into(), + }; + + let extrinsic_slice = + slice::from_raw_parts_mut(unchecked_extrinsic, unchecked_extrinsic_max_size as usize); + + let quote_slice = slice::from_raw_parts(quote, quote_size as usize); + + let extrinsic = match generate_dcap_ra_extrinsic_from_quote_internal(url, quote_slice) { + Ok(xt) => xt, + Err(e) => return e.into(), + }; + + *unchecked_extrinsic_size = + match write_slice_and_whitespace_pad(extrinsic_slice, extrinsic.encode()) { + Ok(l) => l as u32, + Err(e) => return EnclaveError::BufferError(e).into(), + }; + sgx_status_t::SGX_SUCCESS +} + +pub fn generate_dcap_ra_extrinsic_from_quote_internal( + url: String, + quote: &[u8], +) -> EnclaveResult { + let node_metadata_repo = get_node_metadata_repository_from_integritee_solo_or_parachain()?; + info!(" [Enclave] Compose register enclave getting callIDs:"); + + let call_ids = node_metadata_repo + .get_from_metadata(|m| m.register_enclave_call_indexes())? + .map_err(MetadataProviderError::MetadataError)?; + info!(" [Enclave] Compose register enclave call DCAP IDs: {:?}", call_ids); + + let shielding_pubkey = get_shielding_pubkey()?; + let vc_pubkey = get_vc_pubkey()?; + + let call = OpaqueCall::from_tuple(&(call_ids, quote, url, shielding_pubkey, vc_pubkey)); + + info!(" [Enclave] Compose register enclave got extrinsic, returning"); + create_extrinsics(call) +} + +pub fn generate_dcap_skip_ra_extrinsic_from_mr_enclave( + url: String, + quote: &[u8], +) -> EnclaveResult { + let node_metadata_repo = get_node_metadata_repository_from_integritee_solo_or_parachain()?; + info!(" [Enclave] Compose register enclave (skip-ra) getting callIDs:"); + + let call_ids = node_metadata_repo + .get_from_metadata(|m| m.register_enclave_call_indexes())? + .map_err(MetadataProviderError::MetadataError)?; + info!(" [Enclave] Compose register enclave (skip-ra) call DCAP IDs: {:?}", call_ids); + + let shielding_pubkey = get_shielding_pubkey()?; + let vc_pubkey = get_vc_pubkey()?; + + let call = OpaqueCall::from_tuple(&(call_ids, quote, url, shielding_pubkey, vc_pubkey)); + + info!(" [Enclave] Compose register enclave (skip-ra) got extrinsic, returning"); + create_extrinsics(call) +} + +fn generate_ias_ra_extrinsic_internal( + url: String, + skip_ra: bool, +) -> EnclaveResult { + let attestation_handler = GLOBAL_ATTESTATION_HANDLER_COMPONENT.get()?; + let cert_der = attestation_handler.generate_ias_ra_cert(skip_ra)?; + + generate_ias_ra_extrinsic_from_der_cert_internal(url, &cert_der) +} + +pub fn generate_ias_ra_extrinsic_from_der_cert_internal( + url: String, + cert_der: &[u8], +) -> EnclaveResult { + let node_metadata_repo = get_node_metadata_repository_from_integritee_solo_or_parachain()?; + + info!(" [Enclave] Compose register ias enclave (skip-ra) call"); + let call_ids = node_metadata_repo + .get_from_metadata(|m| m.register_enclave_call_indexes())? + .map_err(MetadataProviderError::MetadataError)?; + + let shielding_pubkey = get_shielding_pubkey()?; + let vc_pubkey = get_vc_pubkey()?; + + let call = OpaqueCall::from_tuple(&(call_ids, cert_der, url, shielding_pubkey, vc_pubkey)); + + create_extrinsics(call) +} + +fn create_extrinsics(call: OpaqueCall) -> EnclaveResult { + let extrinsics_factory = get_extrinsic_factory_from_integritee_solo_or_parachain()?; + let extrinsics = extrinsics_factory.create_extrinsics(&[call], None)?; + + match extrinsics.get(0) { + Some(xt) => Ok(xt.clone()), + None => Err(EnclaveError::Other("Could not create extrinsic".into())), + } +} + +#[no_mangle] +pub unsafe extern "C" fn generate_register_quoting_enclave_extrinsic( + collateral: *const sgx_ql_qve_collateral_t, + unchecked_extrinsic: *mut u8, + unchecked_extrinsic_max_size: u32, + unchecked_extrinsic_size: *mut u32, +) -> sgx_status_t { + if unchecked_extrinsic.is_null() || collateral.is_null() { + return sgx_status_t::SGX_ERROR_INVALID_PARAMETER + } + let extrinsic_slice = + slice::from_raw_parts_mut(unchecked_extrinsic, unchecked_extrinsic_max_size as usize); + let collateral = SgxQlQveCollateral::from_c_type(&*collateral); + let collateral_data = match collateral.get_quoting_enclave_split() { + Some(d) => d, + None => return sgx_status_t::SGX_ERROR_INVALID_PARAMETER, + }; + + let call_index_getter = |m: &NodeMetadata| m.register_quoting_enclave_call_indexes(); + *unchecked_extrinsic_size = match generate_generic_register_collateral_extrinsic( + call_index_getter, + extrinsic_slice, + &collateral_data.0, + &collateral_data.1, + &collateral.qe_identity_issuer_chain, + ) { + Ok(l) => l as u32, + Err(e) => return e.into(), + }; + sgx_status_t::SGX_SUCCESS +} + +#[no_mangle] +pub unsafe extern "C" fn generate_register_tcb_info_extrinsic( + collateral: *const sgx_ql_qve_collateral_t, + unchecked_extrinsic: *mut u8, + unchecked_extrinsic_max_size: u32, + unchecked_extrinsic_size: *mut u32, +) -> sgx_status_t { + if unchecked_extrinsic.is_null() || collateral.is_null() { + return sgx_status_t::SGX_ERROR_INVALID_PARAMETER + } + let extrinsic_slice = + slice::from_raw_parts_mut(unchecked_extrinsic, unchecked_extrinsic_max_size as usize); + let collateral = SgxQlQveCollateral::from_c_type(&*collateral); + let collateral_data = match collateral.get_tcb_info_split() { + Some(d) => d, + None => return sgx_status_t::SGX_ERROR_INVALID_PARAMETER, + }; + + let call_index_getter = |m: &NodeMetadata| m.register_tcb_info_call_indexes(); + *unchecked_extrinsic_size = match generate_generic_register_collateral_extrinsic( + call_index_getter, + extrinsic_slice, + &collateral_data.0, + &collateral_data.1, + &collateral.tcb_info_issuer_chain, + ) { + Ok(l) => l as u32, + Err(e) => return e.into(), + }; + sgx_status_t::SGX_SUCCESS +} + +pub fn generate_generic_register_collateral_extrinsic( + getter: F, + extrinsic_slice: &mut [u8], + collateral_data: &str, + data_signature: &[u8], + issuer_chain: &[u8], +) -> EnclaveResult +where + F: Fn(&NodeMetadata) -> Result<[u8; 2], MetadataError>, +{ + let node_metadata_repo = get_node_metadata_repository_from_integritee_solo_or_parachain()?; + let call_ids = node_metadata_repo + .get_from_metadata(getter)? + .map_err(MetadataProviderError::MetadataError)?; + info!(" [Enclave] Compose register collateral call: {:?}", call_ids); + let call = OpaqueCall::from_tuple(&(call_ids, collateral_data, data_signature, issuer_chain)); + + let xt = create_extrinsics(call)?; + write_slice_and_whitespace_pad(extrinsic_slice, xt.encode()) + .map_err(|e| format!("{:?}", e).into()) +} + +#[no_mangle] +pub extern "C" fn dump_ias_ra_cert_to_disk() -> sgx_status_t { + let attestation_handler = match GLOBAL_ATTESTATION_HANDLER_COMPONENT.get() { + Ok(r) => r, + Err(e) => { + error!("Component get failure: {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + match attestation_handler.dump_ias_ra_cert_to_disk() { + Ok(_) => sgx_status_t::SGX_SUCCESS, + Err(e) => e.into(), + } +} + +#[no_mangle] +pub unsafe extern "C" fn dump_dcap_ra_cert_to_disk( + quoting_enclave_target_info: &sgx_target_info_t, + quote_size: u32, +) -> sgx_status_t { + let attestation_handler = match GLOBAL_ATTESTATION_HANDLER_COMPONENT.get() { + Ok(r) => r, + Err(e) => { + error!("Component get failure: {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + match attestation_handler.dump_dcap_ra_cert_to_disk(quoting_enclave_target_info, quote_size) { + Ok(_) => sgx_status_t::SGX_SUCCESS, + Err(e) => e.into(), + } +} + +#[no_mangle] +pub unsafe extern "C" fn dump_dcap_collateral_to_disk( + collateral: *const sgx_ql_qve_collateral_t, +) -> sgx_status_t { + let collateral = SgxQlQveCollateral::from_c_type(&*collateral); + collateral.dump_to_disk(); + sgx_status_t::SGX_SUCCESS +} + +fn get_shielding_pubkey() -> EnclaveResult>> { + let shielding_pubkey = GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT + .get()? + .retrieve_key() + .and_then(|keypair| { + keypair + .export_pubkey() + .and_then(|pubkey| { + serde_json::to_vec(&pubkey).map_err(|e| SgxCryptoError::Serialization(e).into()) + }) + .map_err(|e| SgxCryptoError::Other(Box::new(e))) + }) + .ok(); + + debug!("[Enclave] shielding_pubkey size: {:?}", shielding_pubkey.clone().map(|key| key.len())); + + Ok(shielding_pubkey) +} + +fn get_vc_pubkey() -> EnclaveResult>> { + let vc_pubkey = GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT + .get()? + .retrieve_key() + .and_then(|keypair| { + // vc signing pubkey + keypair.derive_ed25519().map(|keypair| keypair.public().to_vec()) + }) + .ok(); + + debug!("[Enclave] VC pubkey: {:?}", vc_pubkey); + + Ok(vc_pubkey) +} diff --git a/bitacross-worker/enclave-runtime/src/empty_impls.rs b/bitacross-worker/enclave-runtime/src/empty_impls.rs new file mode 100644 index 0000000000..e401fa8d05 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/empty_impls.rs @@ -0,0 +1,56 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +/// Empty tests entry for production mode. +#[cfg(not(feature = "test"))] +#[no_mangle] +#[allow(clippy::unreachable)] +pub extern "C" fn test_main_entrance() -> sgx_types::size_t { + unreachable!("Tests are not available when compiled in production mode.") +} + +/// Empty Teeracle market data implementation. +#[cfg(not(feature = "teeracle"))] +#[no_mangle] +#[allow(clippy::unreachable)] +pub unsafe extern "C" fn update_market_data_xt( + _crypto_currency_ptr: *const u8, + _crypto_currency_size: u32, + _fiat_currency_ptr: *const u8, + _fiat_currency_size: u32, + _unchecked_extrinsic: *mut u8, + _unchecked_extrinsic_max_size: u32, + _unchecked_extrinsic_size: *mut u32, +) -> sgx_types::sgx_status_t { + unreachable!("Cannot update market data, teeracle feature is not enabled.") +} + +/// Empty Teeracle Weather data implementation. +#[cfg(not(feature = "teeracle"))] +#[no_mangle] +#[allow(clippy::unreachable)] +pub unsafe extern "C" fn update_weather_data_xt( + _weather_info_longitude: *const u8, + _weather_info_longitude_size: u32, + _weather_info_latitude: *const u8, + _weather_info_latitude_size: u32, + _unchecked_extrinsic: *mut u8, + _unchecked_extrinsic_max_size: u32, + _unchecked_extrinsic_size: *mut u32, +) -> sgx_types::sgx_status_t { + unreachable!("Cannot update weather data, teeracle feature is not enabled.") +} diff --git a/bitacross-worker/enclave-runtime/src/error.rs b/bitacross-worker/enclave-runtime/src/error.rs new file mode 100644 index 0000000000..da657f87de --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/error.rs @@ -0,0 +1,87 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use derive_more::From; +use sgx_types::{sgx_quote3_error_t, sgx_status_t}; +use std::{boxed::Box, result::Result as StdResult, string::String}; + +pub type Result = StdResult; + +#[derive(Debug, From)] +pub enum Error { + TopPoolAuthor(itp_top_pool_author::error::Error), + Codec(codec::Error), + ComponentContainer(itp_component_container::error::Error), + Crypto(itp_sgx_crypto::Error), + ChainStorage(itp_ocall_api::Error), + ExtrinsicsFactory(itp_extrinsics_factory::error::Error), + IO(std::io::Error), + LightClient(itc_parentchain::light_client::error::Error), + NodeMetadataProvider(itp_node_api::metadata::provider::Error), + Sgx(sgx_status_t), + SgxQuote(sgx_quote3_error_t), + Consensus(its_sidechain::consensus_common::Error), + Stf(String), + StfStateHandler(itp_stf_state_handler::error::Error), + StfExecution(itp_stf_executor::error::Error), + ParentchainBlockImportDispatch(itc_parentchain::block_import_dispatcher::error::Error), + ExpectedTriggeredImportDispatcher, + CouldNotDispatchBlockImport, + NoLitentryParentchainAssigned, + NoTargetAParentchainAssigned, + NoTargetBParentchainAssigned, + ParentChainValidation(itp_storage::error::Error), + ParentChainSync, + PrimitivesAccess(itp_primitives_cache::error::Error), + MutexAccess, + Attestation(itp_attestation_handler::error::Error), + Metadata(itp_node_api_metadata::error::Error), + BufferError(itp_utils::buffer::BufferError), + Other(Box), +} + +impl From for sgx_status_t { + /// return sgx_status for top level enclave functions + fn from(error: Error) -> sgx_status_t { + match error { + Error::Sgx(status) => status, + _ => { + log::error!("Returning error {:?} as sgx unexpected.", error); + sgx_status_t::SGX_ERROR_UNEXPECTED + }, + } + } +} + +impl From for sgx_quote3_error_t { + /// return sgx_quote error + fn from(error: Error) -> sgx_quote3_error_t { + match error { + Error::SgxQuote(status) => status, + _ => { + log::error!("Returning error {:?} as sgx unexpected.", error); + sgx_quote3_error_t::SGX_QL_ERROR_UNEXPECTED + }, + } + } +} + +impl From for StdResult { + fn from(error: Error) -> StdResult { + Err(error) + } +} diff --git a/bitacross-worker/enclave-runtime/src/initialization/global_components.rs b/bitacross-worker/enclave-runtime/src/initialization/global_components.rs new file mode 100644 index 0000000000..8f45ddcc7f --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/initialization/global_components.rs @@ -0,0 +1,501 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Defines all concrete types and global components of the enclave. +//! +//! This allows the crates themselves to stay as generic as possible +//! and ensures that the global instances are initialized once. +use crate::{ + initialization::parentchain::{ + integritee_parachain::IntegriteeParachainHandler, + integritee_solochain::IntegriteeSolochainHandler, + target_a_parachain::TargetAParachainHandler, target_a_solochain::TargetASolochainHandler, + target_b_parachain::TargetBParachainHandler, target_b_solochain::TargetBSolochainHandler, + }, + ocall::OcallApi, + rpc::rpc_response_channel::RpcResponseChannel, + tls_ra::seal_handler::SealHandler, +}; +use ita_parentchain_interface::{integritee, target_a, target_b}; +use ita_sgx_runtime::Runtime; +use ita_stf::{Getter, State as StfState, Stf, TrustedCallSigned}; +use itc_direct_rpc_client::DirectRpcClientFactory; +use itc_direct_rpc_server::{ + rpc_connection_registry::ConnectionRegistry, rpc_responder::RpcResponder, + rpc_watch_extractor::RpcWatchExtractor, rpc_ws_handler::RpcWsHandler, +}; +use itc_parentchain::{ + block_import_dispatcher::{ + immediate_dispatcher::ImmediateDispatcher, triggered_dispatcher::TriggeredDispatcher, + BlockImportDispatcher, + }, + block_importer::ParentchainBlockImporter, + indirect_calls_executor::{filter_metadata::EventCreator, IndirectCallsExecutor}, + light_client::{ + concurrent_access::ValidatorAccessor, io::LightClientStateSealSync, + light_validation::LightValidation, light_validation_state::LightValidationState, + }, +}; +use itc_peer_top_broadcaster::DirectRpcBroadcaster; +use itc_tls_websocket_server::{ + config_provider::FromFileConfigProvider, ws_server::TungsteniteWsServer, ConnectionToken, +}; +use itp_attestation_handler::IntelAttestationHandler; +use itp_component_container::ComponentContainer; +use itp_extrinsics_factory::ExtrinsicsFactory; +use itp_import_queue::ImportQueue; +use itp_node_api::{ + api_client::PairSignature, + metadata::{provider::NodeMetadataRepository, NodeMetadata}, +}; +use itp_nonce_cache::NonceCache; +use itp_sgx_crypto::{key_repository::KeyRepository, Aes, AesSeal, Ed25519Seal, Rsa3072Seal}; +use itp_stf_executor::{ + enclave_signer::StfEnclaveSigner, executor::StfExecutor, getter_executor::GetterExecutor, + state_getter::StfStateGetter, +}; +use itp_stf_primitives::types::{Hash, TrustedOperation}; +use itp_stf_state_handler::{ + file_io::sgx::SgxStateFileIo, state_initializer::StateInitializer, + state_snapshot_repository::StateSnapshotRepository, StateHandler, +}; +use itp_stf_state_observer::state_observer::StateObserver; +use itp_top_pool::basic_pool::BasicPool; +use itp_top_pool_author::{ + api::SidechainApi, + author::{Author, AuthorTopFilter, BroadcastedTopFilter}, +}; +use itp_types::{Block as ParentchainBlock, SignedBlock as SignedParentchainBlock}; +use its_primitives::{ + traits::{Block as SidechainBlockTrait, SignedBlock as SignedSidechainBlockTrait}, + types::block::SignedBlock as SignedSidechainBlock, +}; +use its_sidechain::{ + aura::block_importer::BlockImporter as SidechainBlockImporter, + block_composer::BlockComposer, + consensus_common::{BlockImportConfirmationHandler, BlockImportQueueWorker, PeerBlockSync}, + slots::FailSlotOnDemand, +}; +use lazy_static::lazy_static; +use litentry_primitives::BroadcastedRequest; +use sgx_crypto_helper::rsa3072::Rsa3072KeyPair; +use sgx_tstd::vec::Vec; +use sp_core::{ed25519, ed25519::Pair}; +use std::sync::Arc; + +pub type EnclaveParentchainSigner = + itp_node_api::api_client::StaticExtrinsicSigner; + +pub type EnclaveGetter = Getter; +pub type EnclaveTrustedCallSigned = TrustedCallSigned; +pub type EnclaveStf = Stf; +pub type EnclaveStateKeyRepository = KeyRepository; +pub type EnclaveShieldingKeyRepository = KeyRepository; +pub type EnclaveSigningKeyRepository = KeyRepository; +pub type EnclaveStateFileIo = SgxStateFileIo; +pub type EnclaveStateSnapshotRepository = StateSnapshotRepository; +pub type EnclaveStateObserver = StateObserver; +pub type EnclaveStateInitializer = + StateInitializer; +pub type EnclaveStateHandler = + StateHandler; +pub type EnclaveGetterExecutor = + GetterExecutor, Getter>; +pub type EnclaveOCallApi = OcallApi; +pub type EnclaveNodeMetadataRepository = NodeMetadataRepository; +pub type EnclaveStfExecutor = StfExecutor< + EnclaveOCallApi, + EnclaveStateHandler, + EnclaveNodeMetadataRepository, + EnclaveStf, + EnclaveTrustedCallSigned, + EnclaveGetter, +>; +pub type EnclaveStfEnclaveSigner = StfEnclaveSigner< + EnclaveOCallApi, + EnclaveStateObserver, + EnclaveShieldingKeyRepository, + EnclaveStf, + EnclaveTopPoolAuthor, + EnclaveTrustedCallSigned, + EnclaveGetter, +>; +pub type EnclaveAttestationHandler = + IntelAttestationHandler; + +pub type EnclaveRpcConnectionRegistry = ConnectionRegistry; +pub type EnclaveRpcWsHandler = + RpcWsHandler, EnclaveRpcConnectionRegistry, Hash>; +pub type EnclaveWebSocketServer = TungsteniteWsServer; +pub type EnclaveRpcResponder = RpcResponder; +pub type EnclaveSidechainApi = SidechainApi; + +// Parentchain types relevant for all parentchains +pub type EnclaveLightClientSeal = + LightClientStateSealSync>; +pub type EnclaveExtrinsicsFactory = + ExtrinsicsFactory; + +pub type EnclaveValidatorAccessor = ValidatorAccessor< + LightValidation, + ParentchainBlock, + EnclaveLightClientSeal, +>; + +pub type IntegriteeParentchainBlockImportQueue = ImportQueue; +pub type TargetAParentchainBlockImportQueue = ImportQueue; +pub type TargetBParentchainBlockImportQueue = ImportQueue; + +/// Import queue for the events +/// +/// Note: `Vec` is correct. It should not be `Vec` +pub type IntegriteeParentchainEventImportQueue = ImportQueue>; +pub type TargetAParentchainEventImportQueue = ImportQueue>; +pub type TargetBParentchainEventImportQueue = ImportQueue>; + +// Stuff for the integritee parentchain + +pub type IntegriteeParentchainIndirectCallsExecutor = IndirectCallsExecutor< + EnclaveShieldingKeyRepository, + EnclaveStfEnclaveSigner, + EnclaveTopPoolAuthor, + EnclaveNodeMetadataRepository, + integritee::ShieldFundsAndInvokeFilter, + EventCreator, + integritee::ParentchainEventHandler, + EnclaveTrustedCallSigned, + EnclaveGetter, +>; + +pub type IntegriteeParentchainBlockImporter = ParentchainBlockImporter< + ParentchainBlock, + EnclaveValidatorAccessor, + EnclaveStfExecutor, + EnclaveExtrinsicsFactory, + IntegriteeParentchainIndirectCallsExecutor, + EnclaveOCallApi, +>; + +pub type IntegriteeParentchainTriggeredBlockImportDispatcher = TriggeredDispatcher< + IntegriteeParentchainBlockImporter, + IntegriteeParentchainBlockImportQueue, + IntegriteeParentchainEventImportQueue, +>; + +pub type IntegriteeParentchainImmediateBlockImportDispatcher = + ImmediateDispatcher; + +pub type IntegriteeParentchainBlockImportDispatcher = BlockImportDispatcher< + IntegriteeParentchainTriggeredBlockImportDispatcher, + IntegriteeParentchainImmediateBlockImportDispatcher, +>; + +// Stuff for the Target A parentchain + +/// IndirectCalls executor instance of the Target A parentchain. +/// +/// **Note**: The filter here is purely used for demo purposes. +/// +/// Also note that the extrinsic parser must be changed if the signed extra contains the +/// `AssetTxPayment`. +pub type TargetAParentchainIndirectCallsExecutor = IndirectCallsExecutor< + EnclaveShieldingKeyRepository, + EnclaveStfEnclaveSigner, + EnclaveTopPoolAuthor, + EnclaveNodeMetadataRepository, + target_a::TransferToAliceShieldsFundsFilter, + EventCreator, + target_a::ParentchainEventHandler, + EnclaveTrustedCallSigned, + EnclaveGetter, +>; + +pub type TargetAParentchainBlockImporter = ParentchainBlockImporter< + ParentchainBlock, + EnclaveValidatorAccessor, + EnclaveStfExecutor, + EnclaveExtrinsicsFactory, + TargetAParentchainIndirectCallsExecutor, + EnclaveOCallApi, +>; + +pub type TargetAParentchainTriggeredBlockImportDispatcher = TriggeredDispatcher< + TargetAParentchainBlockImporter, + TargetAParentchainBlockImportQueue, + TargetAParentchainEventImportQueue, +>; + +pub type TargetAParentchainImmediateBlockImportDispatcher = + ImmediateDispatcher; + +pub type TargetAParentchainBlockImportDispatcher = BlockImportDispatcher< + TargetAParentchainTriggeredBlockImportDispatcher, + TargetAParentchainImmediateBlockImportDispatcher, +>; + +// Stuff for the Target B parentchain + +/// IndirectCalls executor instance of the Target B parentchain. +/// +/// **Note**: The filter here is purely used for demo purposes. +/// +/// Also note that the extrinsic parser must be changed if the signed extra contains the +/// `AssetTxPayment`. +pub type TargetBParentchainIndirectCallsExecutor = IndirectCallsExecutor< + EnclaveShieldingKeyRepository, + EnclaveStfEnclaveSigner, + EnclaveTopPoolAuthor, + EnclaveNodeMetadataRepository, + target_b::TargetBExtrinsicFilter, + EventCreator, + target_b::ParentchainEventHandler, + EnclaveTrustedCallSigned, + EnclaveGetter, +>; + +pub type TargetBParentchainBlockImporter = ParentchainBlockImporter< + ParentchainBlock, + EnclaveValidatorAccessor, + EnclaveStfExecutor, + EnclaveExtrinsicsFactory, + TargetBParentchainIndirectCallsExecutor, + EnclaveOCallApi, +>; + +pub type TargetBParentchainTriggeredBlockImportDispatcher = TriggeredDispatcher< + TargetBParentchainBlockImporter, + TargetBParentchainBlockImportQueue, + TargetBParentchainEventImportQueue, +>; + +pub type TargetBParentchainImmediateBlockImportDispatcher = + ImmediateDispatcher; + +pub type TargetBParentchainBlockImportDispatcher = BlockImportDispatcher< + TargetBParentchainTriggeredBlockImportDispatcher, + TargetBParentchainImmediateBlockImportDispatcher, +>; + +/// Sidechain types +pub type EnclaveTopPool = BasicPool< + EnclaveSidechainApi, + ParentchainBlock, + EnclaveRpcResponder, + TrustedOperation, +>; + +pub type EnclaveTopPoolAuthor = Author< + EnclaveTopPool, + AuthorTopFilter, + BroadcastedTopFilter, + EnclaveStateHandler, + EnclaveShieldingKeyRepository, + EnclaveOCallApi, + EnclaveTrustedCallSigned, + EnclaveGetter, +>; +pub type EnclaveDirectRpcBroadcaster = DirectRpcBroadcaster; +pub type EnclaveSidechainBlockComposer = + BlockComposer; +pub type EnclaveSidechainBlockImporter = SidechainBlockImporter< + Pair, + ParentchainBlock, + SignedSidechainBlock, + EnclaveOCallApi, + EnclaveStateHandler, + EnclaveStateKeyRepository, + EnclaveTopPoolAuthor, + // For now the sidechain does only support one parentchain. + IntegriteeParentchainTriggeredBlockImportDispatcher, + EnclaveDirectRpcBroadcaster, + EnclaveTrustedCallSigned, + EnclaveGetter, +>; +pub type EnclaveSidechainBlockImportQueue = ImportQueue; +pub type EnclaveBlockImportConfirmationHandler = BlockImportConfirmationHandler< + ParentchainBlock, + <::Block as SidechainBlockTrait>::HeaderType, + EnclaveNodeMetadataRepository, + EnclaveExtrinsicsFactory, + EnclaveValidatorAccessor, +>; +pub type EnclaveSidechainBlockSyncer = PeerBlockSync< + ParentchainBlock, + SignedSidechainBlock, + EnclaveSidechainBlockImporter, + EnclaveOCallApi, + EnclaveBlockImportConfirmationHandler, +>; +pub type EnclaveSidechainBlockImportQueueWorker = BlockImportQueueWorker< + ParentchainBlock, + SignedSidechainBlock, + EnclaveSidechainBlockImportQueue, + EnclaveSidechainBlockSyncer, +>; +pub type EnclaveSealHandler = SealHandler< + EnclaveShieldingKeyRepository, + EnclaveStateKeyRepository, + EnclaveStateHandler, + EnclaveLightClientSeal, +>; +pub type EnclaveOffchainWorkerExecutor = itc_offchain_worker_executor::executor::Executor< + ParentchainBlock, + EnclaveTopPoolAuthor, + EnclaveStfExecutor, + EnclaveStateHandler, + EnclaveValidatorAccessor, + EnclaveExtrinsicsFactory, + EnclaveStf, + EnclaveTrustedCallSigned, + EnclaveGetter, +>; + +// Base component instances +//------------------------------------------------------------------------------------------------- + +/// State key repository +pub static GLOBAL_STATE_KEY_REPOSITORY_COMPONENT: ComponentContainer = + ComponentContainer::new("State key repository"); + +/// Shielding key repository +pub static GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT: ComponentContainer< + EnclaveShieldingKeyRepository, +> = ComponentContainer::new("Shielding key repository"); + +/// Signing key repository +pub static GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT: ComponentContainer< + EnclaveSigningKeyRepository, +> = ComponentContainer::new("Signing key repository"); + +/// Light client db seal for the Integritee parentchain +pub static GLOBAL_INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_SEAL: ComponentContainer< + EnclaveLightClientSeal, +> = ComponentContainer::new("Integritee Parentchain EnclaveLightClientSealSync"); + +/// Light client db seal for the Target A parentchain. +pub static GLOBAL_TARGET_A_PARENTCHAIN_LIGHT_CLIENT_SEAL: ComponentContainer< + EnclaveLightClientSeal, +> = ComponentContainer::new("Target A EnclaveLightClientSealSync"); + +/// Light client db seal for the Target A parentchain. +pub static GLOBAL_TARGET_B_PARENTCHAIN_LIGHT_CLIENT_SEAL: ComponentContainer< + EnclaveLightClientSeal, +> = ComponentContainer::new("Target B EnclaveLightClientSealSync"); + +/// O-Call API +pub static GLOBAL_OCALL_API_COMPONENT: ComponentContainer = + ComponentContainer::new("O-call API"); + +/// Trusted Web-socket server +pub static GLOBAL_WEB_SOCKET_SERVER_COMPONENT: ComponentContainer = + ComponentContainer::new("Web-socket server"); + +/// State handler. +pub static GLOBAL_STATE_HANDLER_COMPONENT: ComponentContainer = + ComponentContainer::new("state handler"); + +/// State observer. +pub static GLOBAL_STATE_OBSERVER_COMPONENT: ComponentContainer = + ComponentContainer::new("state observer"); + +/// TOP pool author. +pub static GLOBAL_TOP_POOL_AUTHOR_COMPONENT: ComponentContainer = + ComponentContainer::new("top_pool_author"); + +/// Direct RPC broadcaster +pub static GLOBAL_DIRECT_RPC_BROADCASTER_COMPONENT: ComponentContainer< + EnclaveDirectRpcBroadcaster, +> = ComponentContainer::new("direct_rpc_broadcaster"); + +pub static DIRECT_RPC_REQUEST_SINK_COMPONENT: ComponentContainer< + sgx_tstd::sync::mpsc::SyncSender, +> = ComponentContainer::new("direct_rpc_request_sink"); + +/// attestation handler +pub static GLOBAL_ATTESTATION_HANDLER_COMPONENT: ComponentContainer = + ComponentContainer::new("Attestation handler"); + +// Parentchain component instances +//------------------------------------------------------------------------------------------------- + +lazy_static! { + /// Global nonce cache for the Integritee Parentchain. + pub static ref GLOBAL_INTEGRITEE_PARENTCHAIN_NONCE_CACHE: Arc = Default::default(); + + /// Global nonce cache for the Target A parentchain.. + pub static ref GLOBAL_TARGET_A_PARENTCHAIN_NONCE_CACHE: Arc = Default::default(); + + /// Global nonce cache for the Target B parentchain.. + pub static ref GLOBAL_TARGET_B_PARENTCHAIN_NONCE_CACHE: Arc = Default::default(); +} + +/// Solochain Handler. +pub static GLOBAL_INTEGRITEE_SOLOCHAIN_HANDLER_COMPONENT: ComponentContainer< + IntegriteeSolochainHandler, +> = ComponentContainer::new("integritee solochain handler"); + +pub static GLOBAL_INTEGRITEE_PARACHAIN_HANDLER_COMPONENT: ComponentContainer< + IntegriteeParachainHandler, +> = ComponentContainer::new("integritee parachain handler"); + +pub static GLOBAL_TARGET_A_SOLOCHAIN_HANDLER_COMPONENT: ComponentContainer< + TargetASolochainHandler, +> = ComponentContainer::new("target A solochain handler"); + +pub static GLOBAL_TARGET_A_PARACHAIN_HANDLER_COMPONENT: ComponentContainer< + TargetAParachainHandler, +> = ComponentContainer::new("target A parachain handler"); + +pub static GLOBAL_TARGET_B_SOLOCHAIN_HANDLER_COMPONENT: ComponentContainer< + TargetBSolochainHandler, +> = ComponentContainer::new("target B solochain handler"); + +pub static GLOBAL_TARGET_B_PARACHAIN_HANDLER_COMPONENT: ComponentContainer< + TargetBParachainHandler, +> = ComponentContainer::new("target B parachain handler"); + +// Sidechain component instances +//------------------------------------------------------------------------------------------------- + +/// Enclave RPC WS handler. +pub static GLOBAL_RPC_WS_HANDLER_COMPONENT: ComponentContainer = + ComponentContainer::new("rpc_ws_handler"); + +/// Sidechain import queue. +pub static GLOBAL_SIDECHAIN_IMPORT_QUEUE_COMPONENT: ComponentContainer< + EnclaveSidechainBlockImportQueue, +> = ComponentContainer::new("sidechain_import_queue"); + +/// Sidechain import queue worker - processes the import queue. +pub static GLOBAL_SIDECHAIN_IMPORT_QUEUE_WORKER_COMPONENT: ComponentContainer< + EnclaveSidechainBlockImportQueueWorker, +> = ComponentContainer::new("sidechain_import_queue_worker"); + +/// Sidechain block composer. +pub static GLOBAL_SIDECHAIN_BLOCK_COMPOSER_COMPONENT: ComponentContainer< + EnclaveSidechainBlockComposer, +> = ComponentContainer::new("sidechain_block_composer"); + +/// Sidechain block syncer. +pub static GLOBAL_SIDECHAIN_BLOCK_SYNCER_COMPONENT: ComponentContainer< + EnclaveSidechainBlockSyncer, +> = ComponentContainer::new("sidechain_block_syncer"); + +/// Sidechain fail slot on demand. +pub static GLOBAL_SIDECHAIN_FAIL_SLOT_ON_DEMAND_COMPONENT: ComponentContainer< + Option, +> = ComponentContainer::new("sidechain_fail_slot_on_demand"); diff --git a/bitacross-worker/enclave-runtime/src/initialization/mod.rs b/bitacross-worker/enclave-runtime/src/initialization/mod.rs new file mode 100644 index 0000000000..1510341a61 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/initialization/mod.rs @@ -0,0 +1,366 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pub mod global_components; +pub mod parentchain; +use crate::{ + error::{Error, Result as EnclaveResult}, + initialization::global_components::{ + EnclaveBlockImportConfirmationHandler, EnclaveGetterExecutor, EnclaveLightClientSeal, + EnclaveOCallApi, EnclaveRpcResponder, EnclaveShieldingKeyRepository, EnclaveSidechainApi, + EnclaveSidechainBlockImportQueue, EnclaveSidechainBlockImportQueueWorker, + EnclaveSidechainBlockImporter, EnclaveSidechainBlockSyncer, EnclaveStateFileIo, + EnclaveStateHandler, EnclaveStateInitializer, EnclaveStateObserver, + EnclaveStateSnapshotRepository, EnclaveStfEnclaveSigner, EnclaveTopPool, + EnclaveTopPoolAuthor, DIRECT_RPC_REQUEST_SINK_COMPONENT, + GLOBAL_ATTESTATION_HANDLER_COMPONENT, GLOBAL_DIRECT_RPC_BROADCASTER_COMPONENT, + GLOBAL_INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_SEAL, GLOBAL_OCALL_API_COMPONENT, + GLOBAL_RPC_WS_HANDLER_COMPONENT, GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT, + GLOBAL_SIDECHAIN_BLOCK_COMPOSER_COMPONENT, GLOBAL_SIDECHAIN_BLOCK_SYNCER_COMPONENT, + GLOBAL_SIDECHAIN_FAIL_SLOT_ON_DEMAND_COMPONENT, GLOBAL_SIDECHAIN_IMPORT_QUEUE_COMPONENT, + GLOBAL_SIDECHAIN_IMPORT_QUEUE_WORKER_COMPONENT, GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT, + GLOBAL_STATE_HANDLER_COMPONENT, GLOBAL_STATE_KEY_REPOSITORY_COMPONENT, + GLOBAL_STATE_OBSERVER_COMPONENT, GLOBAL_TARGET_A_PARENTCHAIN_LIGHT_CLIENT_SEAL, + GLOBAL_TARGET_B_PARENTCHAIN_LIGHT_CLIENT_SEAL, GLOBAL_TOP_POOL_AUTHOR_COMPONENT, + GLOBAL_WEB_SOCKET_SERVER_COMPONENT, + }, + ocall::OcallApi, + rpc::{rpc_response_channel::RpcResponseChannel, worker_api_direct::public_api_rpc_handler}, + utils::{ + get_extrinsic_factory_from_integritee_solo_or_parachain, + get_node_metadata_repository_from_integritee_solo_or_parachain, + get_triggered_dispatcher_from_integritee_solo_or_parachain, + get_validator_accessor_from_integritee_solo_or_parachain, + }, + Hash, +}; +use base58::ToBase58; +use codec::Encode; +use core::str::FromStr; +use ita_stf::{Getter, TrustedCallSigned}; +use itc_direct_rpc_server::{ + create_determine_watch, rpc_connection_registry::ConnectionRegistry, + rpc_ws_handler::RpcWsHandler, +}; +use itc_peer_top_broadcaster::init; +use itc_tls_websocket_server::{ + certificate_generation::ed25519_self_signed_certificate, create_ws_server, ConnectionToken, + WebSocketServer, +}; +use itp_attestation_handler::{AttestationHandler, IntelAttestationHandler}; +use itp_component_container::{ComponentGetter, ComponentInitializer}; +use itp_primitives_cache::GLOBAL_PRIMITIVES_CACHE; +use itp_settings::files::{ + LITENTRY_PARENTCHAIN_LIGHT_CLIENT_DB_PATH, STATE_SNAPSHOTS_CACHE_SIZE, + TARGET_A_PARENTCHAIN_LIGHT_CLIENT_DB_PATH, TARGET_B_PARENTCHAIN_LIGHT_CLIENT_DB_PATH, +}; +use itp_sgx_crypto::{ + get_aes_repository, get_ed25519_repository, get_rsa3072_repository, key_repository::AccessKey, +}; +use itp_stf_state_handler::{ + file_io::StateDir, handle_state::HandleState, query_shard_state::QueryShardState, + state_snapshot_repository::VersionedStateAccess, + state_snapshot_repository_loader::StateSnapshotRepositoryLoader, StateHandler, +}; +use itp_top_pool::pool::Options as PoolOptions; +use itp_top_pool_author::author::{AuthorTopFilter, BroadcastedTopFilter}; +use itp_types::{parentchain::ParentchainId, ShardIdentifier}; +use its_sidechain::{ + block_composer::BlockComposer, + slots::{FailSlotMode, FailSlotOnDemand}, +}; +use lc_scheduled_enclave::{ScheduledEnclaveUpdater, GLOBAL_SCHEDULED_ENCLAVE}; +use litentry_primitives::BroadcastedRequest; +use log::*; +use sgx_types::sgx_status_t; +use sp_core::crypto::Pair; +use std::{collections::HashMap, path::PathBuf, string::String, sync::Arc}; +pub(crate) fn init_enclave( + mu_ra_url: String, + untrusted_worker_url: String, + base_dir: PathBuf, +) -> EnclaveResult<()> { + let signing_key_repository = Arc::new(get_ed25519_repository(base_dir.clone())?); + GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT.initialize(signing_key_repository.clone()); + let signer = signing_key_repository.retrieve_key()?; + info!("[Enclave initialized] Ed25519 prim raw : {:?}", signer.public().0); + + let shielding_key_repository = Arc::new(get_rsa3072_repository(base_dir.clone())?); + GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT.initialize(shielding_key_repository.clone()); + + // Create the aes key that is used for state encryption such that a key is always present in tests. + // It will be overwritten anyway if mutual remote attestation is performed with the primary worker. + let state_key_repository = Arc::new(get_aes_repository(base_dir.clone())?); + GLOBAL_STATE_KEY_REPOSITORY_COMPONENT.initialize(state_key_repository.clone()); + + let integritee_light_client_seal = Arc::new(EnclaveLightClientSeal::new( + base_dir.join(LITENTRY_PARENTCHAIN_LIGHT_CLIENT_DB_PATH), + ParentchainId::Litentry, + )?); + GLOBAL_INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_SEAL.initialize(integritee_light_client_seal); + + let target_a_light_client_seal = Arc::new(EnclaveLightClientSeal::new( + base_dir.join(TARGET_A_PARENTCHAIN_LIGHT_CLIENT_DB_PATH), + ParentchainId::TargetA, + )?); + GLOBAL_TARGET_A_PARENTCHAIN_LIGHT_CLIENT_SEAL.initialize(target_a_light_client_seal); + + let target_b_light_client_seal = Arc::new(EnclaveLightClientSeal::new( + base_dir.join(TARGET_B_PARENTCHAIN_LIGHT_CLIENT_DB_PATH), + ParentchainId::TargetB, + )?); + GLOBAL_TARGET_B_PARENTCHAIN_LIGHT_CLIENT_SEAL.initialize(target_b_light_client_seal); + + let state_file_io = + Arc::new(EnclaveStateFileIo::new(state_key_repository, StateDir::new(base_dir))); + let state_initializer = + Arc::new(EnclaveStateInitializer::new(shielding_key_repository.clone())); + let state_snapshot_repository_loader = StateSnapshotRepositoryLoader::< + EnclaveStateFileIo, + EnclaveStateInitializer, + >::new(state_file_io, state_initializer.clone()); + + let state_snapshot_repository = + state_snapshot_repository_loader.load_snapshot_repository(STATE_SNAPSHOTS_CACHE_SIZE)?; + let state_observer = initialize_state_observer(&state_snapshot_repository)?; + GLOBAL_STATE_OBSERVER_COMPONENT.initialize(state_observer.clone()); + + let state_handler = Arc::new(StateHandler::load_from_repository( + state_snapshot_repository, + state_observer.clone(), + state_initializer, + )?); + + GLOBAL_STATE_HANDLER_COMPONENT.initialize(state_handler.clone()); + + let ocall_api = Arc::new(OcallApi); + GLOBAL_OCALL_API_COMPONENT.initialize(ocall_api.clone()); + + // For debug purposes, list shards. no problem to panic if fails. + #[allow(clippy::unwrap_used)] + let shards = state_handler.list_shards().unwrap(); + debug!("found the following {} shards on disk:", shards.len()); + for s in shards { + debug!("{}", s.encode().to_base58()) + } + + itp_primitives_cache::set_primitives( + GLOBAL_PRIMITIVES_CACHE.as_ref(), + mu_ra_url, + untrusted_worker_url, + ) + .map_err(Error::PrimitivesAccess)?; + + let watch_extractor = Arc::new(create_determine_watch::()); + + let connection_registry = Arc::new(ConnectionRegistry::::new()); + + // We initialize components for the public RPC / direct invocation server here, so we can start the server + // before registering on the parentchain. If we started the RPC AFTER registering on the parentchain and + // initializing the light-client, there is a period of time where a peer might want to reach us, + // but the RPC server is not yet up and running, resulting in error messages or even in that + // validateer completely breaking (IO PipeError). + // Corresponding GH issues are #545 and #600. + + let response_channel = Arc::new(RpcResponseChannel::default()); + let rpc_responder = + Arc::new(EnclaveRpcResponder::new(connection_registry.clone(), response_channel)); + + let (request_sink, broadcaster) = init(rpc_responder.clone()); + let request_sink_cloned = request_sink.clone(); + + let top_pool_author = create_top_pool_author( + rpc_responder, + state_handler.clone(), + ocall_api.clone(), + shielding_key_repository.clone(), + request_sink_cloned, + ); + GLOBAL_TOP_POOL_AUTHOR_COMPONENT.initialize(top_pool_author.clone()); + + GLOBAL_DIRECT_RPC_BROADCASTER_COMPONENT.initialize(broadcaster); + DIRECT_RPC_REQUEST_SINK_COMPONENT.initialize(request_sink); + + let getter_executor = Arc::new(EnclaveGetterExecutor::new(state_observer)); + let io_handler = public_api_rpc_handler( + top_pool_author, + getter_executor, + shielding_key_repository, + Some(state_handler), + ); + let rpc_handler = Arc::new(RpcWsHandler::new(io_handler, watch_extractor, connection_registry)); + GLOBAL_RPC_WS_HANDLER_COMPONENT.initialize(rpc_handler); + + let sidechain_block_import_queue = Arc::new(EnclaveSidechainBlockImportQueue::default()); + GLOBAL_SIDECHAIN_IMPORT_QUEUE_COMPONENT.initialize(sidechain_block_import_queue); + + let attestation_handler = + Arc::new(IntelAttestationHandler::new(ocall_api, signing_key_repository)); + GLOBAL_ATTESTATION_HANDLER_COMPONENT.initialize(attestation_handler); + + Ok(()) +} + +fn initialize_state_observer( + snapshot_repository: &EnclaveStateSnapshotRepository, +) -> EnclaveResult> { + let shards = snapshot_repository.list_shards()?; + let mut states_map = HashMap::< + ShardIdentifier, + ::StateType, + >::new(); + for shard in shards.into_iter() { + let state = snapshot_repository.load_latest(&shard)?; + states_map.insert(shard, state); + } + Ok(Arc::new(EnclaveStateObserver::from_map(states_map))) +} + +pub(crate) fn init_enclave_sidechain_components( + fail_mode: Option, + fail_at: u64, +) -> EnclaveResult<()> { + let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; + let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; + let direct_rpc_broadcaster = GLOBAL_DIRECT_RPC_BROADCASTER_COMPONENT.get()?; + + let top_pool_author = GLOBAL_TOP_POOL_AUTHOR_COMPONENT.get()?; + let state_key_repository = GLOBAL_STATE_KEY_REPOSITORY_COMPONENT.get()?; + + // GLOBAL_SCHEDULED_ENCLAVE must be initialized after attestation_handler and enclave + let attestation_handler = GLOBAL_ATTESTATION_HANDLER_COMPONENT.get()?; + let mrenclave = attestation_handler.get_mrenclave()?; + GLOBAL_SCHEDULED_ENCLAVE.init(mrenclave).map_err(|e| Error::Other(e.into()))?; + + let parentchain_block_import_dispatcher = + get_triggered_dispatcher_from_integritee_solo_or_parachain()?; + + let signer = GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT.get()?.retrieve_key()?; + + let sidechain_block_importer = Arc::new(EnclaveSidechainBlockImporter::new( + state_handler, + state_key_repository.clone(), + top_pool_author, + parentchain_block_import_dispatcher, + ocall_api.clone(), + direct_rpc_broadcaster, + )); + + let sidechain_block_import_queue = GLOBAL_SIDECHAIN_IMPORT_QUEUE_COMPONENT.get()?; + let metadata_repository = get_node_metadata_repository_from_integritee_solo_or_parachain()?; + let extrinsics_factory = get_extrinsic_factory_from_integritee_solo_or_parachain()?; + let validator_accessor = get_validator_accessor_from_integritee_solo_or_parachain()?; + + let sidechain_block_import_confirmation_handler = + Arc::new(EnclaveBlockImportConfirmationHandler::new( + metadata_repository, + extrinsics_factory, + validator_accessor, + )); + + let sidechain_block_syncer = Arc::new(EnclaveSidechainBlockSyncer::new( + sidechain_block_importer, + ocall_api, + sidechain_block_import_confirmation_handler, + )); + GLOBAL_SIDECHAIN_BLOCK_SYNCER_COMPONENT.initialize(sidechain_block_syncer.clone()); + + let sidechain_block_import_queue_worker = + Arc::new(EnclaveSidechainBlockImportQueueWorker::new( + sidechain_block_import_queue, + sidechain_block_syncer, + )); + GLOBAL_SIDECHAIN_IMPORT_QUEUE_WORKER_COMPONENT.initialize(sidechain_block_import_queue_worker); + + let block_composer = Arc::new(BlockComposer::new(signer, state_key_repository)); + GLOBAL_SIDECHAIN_BLOCK_COMPOSER_COMPONENT.initialize(block_composer); + if let Some(fail_mode) = fail_mode { + let fail_mode = FailSlotMode::from_str(&fail_mode) + .map_err(|_| Error::Sgx(sgx_status_t::SGX_ERROR_UNEXPECTED))?; + let fail_on_demand = Arc::new(Some(FailSlotOnDemand::new(fail_at, fail_mode))); + GLOBAL_SIDECHAIN_FAIL_SLOT_ON_DEMAND_COMPONENT.initialize(fail_on_demand); + } else { + GLOBAL_SIDECHAIN_FAIL_SLOT_ON_DEMAND_COMPONENT.initialize(Arc::new(None)); + } + + Ok(()) +} + +pub(crate) fn init_direct_invocation_server(server_addr: String) -> EnclaveResult<()> { + let rpc_handler = GLOBAL_RPC_WS_HANDLER_COMPONENT.get()?; + let signer = GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT.get()?.retrieve_key()?; + + let cert = + ed25519_self_signed_certificate(signer, "Enclave").map_err(|e| Error::Other(e.into()))?; + + // Serialize certificate(s) and private key to PEM. + // PEM format is needed as a certificate chain can only be serialized into PEM. + let pem_serialized = cert.serialize_pem().map_err(|e| Error::Other(e.into()))?; + let private_key = cert.serialize_private_key_pem(); + + let web_socket_server = + create_ws_server(server_addr.as_str(), &private_key, &pem_serialized, rpc_handler); + + GLOBAL_WEB_SOCKET_SERVER_COMPONENT.initialize(web_socket_server.clone()); + + match web_socket_server.run() { + Ok(()) => {}, + Err(e) => { + error!("Web socket server encountered an unexpected error: {:?}", e) + }, + } + + Ok(()) +} + +pub(crate) fn init_shard(shard: ShardIdentifier) -> EnclaveResult<()> { + let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; + let _ = state_handler.initialize_shard(shard)?; + Ok(()) +} + +pub(crate) fn migrate_shard( + old_shard: ShardIdentifier, + new_shard: ShardIdentifier, +) -> EnclaveResult<()> { + let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; + let _ = state_handler.migrate_shard(old_shard, new_shard)?; + Ok(()) +} + +/// Initialize the TOP pool author component. +pub fn create_top_pool_author( + rpc_responder: Arc, + state_handler: Arc, + ocall_api: Arc, + shielding_key_repository: Arc, + requests_sink: Arc>, +) -> Arc { + let side_chain_api = Arc::new(EnclaveSidechainApi::new()); + let top_pool = + Arc::new(EnclaveTopPool::create(PoolOptions::default(), side_chain_api, rpc_responder)); + + Arc::new(EnclaveTopPoolAuthor::new( + top_pool, + AuthorTopFilter::::new(), + BroadcastedTopFilter::::new(), + state_handler, + shielding_key_repository, + ocall_api, + requests_sink, + )) +} diff --git a/bitacross-worker/enclave-runtime/src/initialization/parentchain/common.rs b/bitacross-worker/enclave-runtime/src/initialization/parentchain/common.rs new file mode 100644 index 0000000000..7db3228214 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/initialization/parentchain/common.rs @@ -0,0 +1,297 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + error::Result, + initialization::{ + global_components::{ + EnclaveExtrinsicsFactory, EnclaveNodeMetadataRepository, EnclaveOffchainWorkerExecutor, + EnclaveParentchainSigner, EnclaveStfExecutor, EnclaveValidatorAccessor, + IntegriteeParentchainBlockImportDispatcher, IntegriteeParentchainBlockImportQueue, + IntegriteeParentchainBlockImporter, IntegriteeParentchainEventImportQueue, + IntegriteeParentchainImmediateBlockImportDispatcher, + IntegriteeParentchainIndirectCallsExecutor, + IntegriteeParentchainTriggeredBlockImportDispatcher, + TargetAParentchainBlockImportDispatcher, TargetAParentchainBlockImportQueue, + TargetAParentchainBlockImporter, TargetAParentchainEventImportQueue, + TargetAParentchainImmediateBlockImportDispatcher, + TargetAParentchainIndirectCallsExecutor, + TargetAParentchainTriggeredBlockImportDispatcher, + TargetBParentchainBlockImportDispatcher, TargetBParentchainBlockImportQueue, + TargetBParentchainBlockImporter, TargetBParentchainEventImportQueue, + TargetBParentchainImmediateBlockImportDispatcher, + TargetBParentchainIndirectCallsExecutor, + TargetBParentchainTriggeredBlockImportDispatcher, GLOBAL_OCALL_API_COMPONENT, + GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT, GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT, + GLOBAL_STATE_HANDLER_COMPONENT, GLOBAL_STATE_OBSERVER_COMPONENT, + GLOBAL_TOP_POOL_AUTHOR_COMPONENT, + }, + EnclaveStfEnclaveSigner, + }, +}; +use itp_component_container::ComponentGetter; +use itp_nonce_cache::NonceCache; +use itp_sgx_crypto::key_repository::AccessKey; +use log::*; +use sp_core::H256; +use std::sync::Arc; + +pub(crate) fn create_integritee_parentchain_block_importer( + validator_access: Arc, + stf_executor: Arc, + extrinsics_factory: Arc, + node_metadata_repository: Arc, +) -> Result { + let state_observer = GLOBAL_STATE_OBSERVER_COMPONENT.get()?; + let top_pool_author = GLOBAL_TOP_POOL_AUTHOR_COMPONENT.get()?; + let shielding_key_repository = GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT.get()?; + let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; + + let stf_enclave_signer = Arc::new(EnclaveStfEnclaveSigner::new( + state_observer, + ocall_api.clone(), + shielding_key_repository.clone(), + top_pool_author.clone(), + )); + let indirect_calls_executor = Arc::new(IntegriteeParentchainIndirectCallsExecutor::new( + shielding_key_repository, + stf_enclave_signer, + top_pool_author, + node_metadata_repository, + )); + Ok(IntegriteeParentchainBlockImporter::new( + validator_access, + stf_executor, + extrinsics_factory, + indirect_calls_executor, + ocall_api, + )) +} + +pub(crate) fn create_target_a_parentchain_block_importer( + validator_access: Arc, + stf_executor: Arc, + extrinsics_factory: Arc, + node_metadata_repository: Arc, +) -> Result { + let state_observer = GLOBAL_STATE_OBSERVER_COMPONENT.get()?; + let top_pool_author = GLOBAL_TOP_POOL_AUTHOR_COMPONENT.get()?; + let shielding_key_repository = GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT.get()?; + let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; + + let stf_enclave_signer = Arc::new(EnclaveStfEnclaveSigner::new( + state_observer, + ocall_api.clone(), + shielding_key_repository.clone(), + top_pool_author.clone(), + )); + let indirect_calls_executor = Arc::new(TargetAParentchainIndirectCallsExecutor::new( + shielding_key_repository, + stf_enclave_signer, + top_pool_author, + node_metadata_repository, + )); + Ok(TargetAParentchainBlockImporter::new( + validator_access, + stf_executor, + extrinsics_factory, + indirect_calls_executor, + ocall_api, + )) +} + +pub(crate) fn create_target_b_parentchain_block_importer( + validator_access: Arc, + stf_executor: Arc, + extrinsics_factory: Arc, + node_metadata_repository: Arc, +) -> Result { + let state_observer = GLOBAL_STATE_OBSERVER_COMPONENT.get()?; + let top_pool_author = GLOBAL_TOP_POOL_AUTHOR_COMPONENT.get()?; + let shielding_key_repository = GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT.get()?; + let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; + + let stf_enclave_signer = Arc::new(EnclaveStfEnclaveSigner::new( + state_observer, + ocall_api.clone(), + shielding_key_repository.clone(), + top_pool_author.clone(), + )); + let indirect_calls_executor = Arc::new(TargetBParentchainIndirectCallsExecutor::new( + shielding_key_repository, + stf_enclave_signer, + top_pool_author, + node_metadata_repository, + )); + Ok(TargetBParentchainBlockImporter::new( + validator_access, + stf_executor, + extrinsics_factory, + indirect_calls_executor, + ocall_api, + )) +} + +pub(crate) fn create_extrinsics_factory( + genesis_hash: H256, + nonce_cache: Arc, + node_metadata_repository: Arc, +) -> Result> { + let signer = GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT.get()?.retrieve_key()?; + + Ok(Arc::new(EnclaveExtrinsicsFactory::new( + genesis_hash, + EnclaveParentchainSigner::new(signer), + nonce_cache, + node_metadata_repository, + ))) +} + +pub(crate) fn create_integritee_offchain_immediate_import_dispatcher( + stf_executor: Arc, + block_importer: IntegriteeParentchainBlockImporter, + validator_access: Arc, + extrinsics_factory: Arc, +) -> Result> { + let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; + let top_pool_author = GLOBAL_TOP_POOL_AUTHOR_COMPONENT.get()?; + + let offchain_worker_executor = Arc::new(EnclaveOffchainWorkerExecutor::new( + top_pool_author, + stf_executor, + state_handler, + validator_access, + extrinsics_factory, + )); + let immediate_dispatcher = IntegriteeParentchainImmediateBlockImportDispatcher::new( + block_importer, + ) + .with_observer(move || { + if let Err(e) = offchain_worker_executor.execute() { + error!("Failed to execute trusted calls: {:?}", e); + } + }); + + Ok(Arc::new(IntegriteeParentchainBlockImportDispatcher::new_immediate_dispatcher(Arc::new( + immediate_dispatcher, + )))) +} + +pub(crate) fn create_target_a_offchain_immediate_import_dispatcher( + stf_executor: Arc, + block_importer: TargetAParentchainBlockImporter, + validator_access: Arc, + extrinsics_factory: Arc, +) -> Result> { + let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; + let top_pool_author = GLOBAL_TOP_POOL_AUTHOR_COMPONENT.get()?; + + let offchain_worker_executor = Arc::new(EnclaveOffchainWorkerExecutor::new( + top_pool_author, + stf_executor, + state_handler, + validator_access, + extrinsics_factory, + )); + let immediate_dispatcher = TargetAParentchainImmediateBlockImportDispatcher::new( + block_importer, + ) + .with_observer(move || { + if let Err(e) = offchain_worker_executor.execute() { + error!("Failed to execute trusted calls: {:?}", e); + } + }); + + Ok(Arc::new(TargetAParentchainBlockImportDispatcher::new_immediate_dispatcher(Arc::new( + immediate_dispatcher, + )))) +} + +pub(crate) fn create_target_b_offchain_immediate_import_dispatcher( + stf_executor: Arc, + block_importer: TargetBParentchainBlockImporter, + validator_access: Arc, + extrinsics_factory: Arc, +) -> Result> { + let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; + let top_pool_author = GLOBAL_TOP_POOL_AUTHOR_COMPONENT.get()?; + + let offchain_worker_executor = Arc::new(EnclaveOffchainWorkerExecutor::new( + top_pool_author, + stf_executor, + state_handler, + validator_access, + extrinsics_factory, + )); + let immediate_dispatcher = TargetBParentchainImmediateBlockImportDispatcher::new( + block_importer, + ) + .with_observer(move || { + if let Err(e) = offchain_worker_executor.execute() { + error!("Failed to execute trusted calls: {:?}", e); + } + }); + + Ok(Arc::new(TargetBParentchainBlockImportDispatcher::new_immediate_dispatcher(Arc::new( + immediate_dispatcher, + )))) +} + +pub(crate) fn create_sidechain_triggered_import_dispatcher( + block_importer: IntegriteeParentchainBlockImporter, +) -> Arc { + let parentchain_block_import_queue = IntegriteeParentchainBlockImportQueue::default(); + let parentchain_event_import_queue = IntegriteeParentchainEventImportQueue::default(); + let triggered_dispatcher = IntegriteeParentchainTriggeredBlockImportDispatcher::new( + block_importer, + parentchain_block_import_queue, + parentchain_event_import_queue, + ); + Arc::new(IntegriteeParentchainBlockImportDispatcher::new_triggered_dispatcher(Arc::new( + triggered_dispatcher, + ))) +} + +pub(crate) fn create_sidechain_triggered_import_dispatcher_for_target_a( + block_importer: TargetAParentchainBlockImporter, +) -> Arc { + let parentchain_block_import_queue = TargetAParentchainBlockImportQueue::default(); + let parentchain_event_import_queue = TargetAParentchainEventImportQueue::default(); + let triggered_dispatcher = TargetAParentchainTriggeredBlockImportDispatcher::new( + block_importer, + parentchain_block_import_queue, + parentchain_event_import_queue, + ); + Arc::new(TargetAParentchainBlockImportDispatcher::new_triggered_dispatcher(Arc::new( + triggered_dispatcher, + ))) +} + +pub(crate) fn create_sidechain_triggered_import_dispatcher_for_target_b( + block_importer: TargetBParentchainBlockImporter, +) -> Arc { + let parentchain_block_import_queue = TargetBParentchainBlockImportQueue::default(); + let parentchain_event_import_queue = TargetBParentchainEventImportQueue::default(); + let triggered_dispatcher = TargetBParentchainTriggeredBlockImportDispatcher::new( + block_importer, + parentchain_block_import_queue, + parentchain_event_import_queue, + ); + Arc::new(TargetBParentchainBlockImportDispatcher::new_triggered_dispatcher(Arc::new( + triggered_dispatcher, + ))) +} diff --git a/bitacross-worker/enclave-runtime/src/initialization/parentchain/integritee_parachain.rs b/bitacross-worker/enclave-runtime/src/initialization/parentchain/integritee_parachain.rs new file mode 100644 index 0000000000..f297c4960e --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/initialization/parentchain/integritee_parachain.rs @@ -0,0 +1,118 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + error::Result, + initialization::{ + global_components::{ + EnclaveExtrinsicsFactory, EnclaveNodeMetadataRepository, EnclaveOCallApi, + EnclaveStfExecutor, EnclaveValidatorAccessor, + IntegriteeParentchainBlockImportDispatcher, + GLOBAL_INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_SEAL, + GLOBAL_INTEGRITEE_PARENTCHAIN_NONCE_CACHE, GLOBAL_OCALL_API_COMPONENT, + GLOBAL_STATE_HANDLER_COMPONENT, + }, + parentchain::common::{ + create_extrinsics_factory, create_integritee_offchain_immediate_import_dispatcher, + create_integritee_parentchain_block_importer, + create_sidechain_triggered_import_dispatcher, + }, + }, +}; +use itc_parentchain::light_client::{concurrent_access::ValidatorAccess, LightClientState}; +use itp_component_container::ComponentGetter; +use itp_settings::worker_mode::{ProvideWorkerMode, WorkerMode}; +use itp_types::parentchain::ParentchainId; +use std::{path::PathBuf, sync::Arc}; + +pub use itc_parentchain::primitives::{ParachainBlock, ParachainHeader, ParachainParams}; + +#[derive(Clone)] +pub struct IntegriteeParachainHandler { + pub genesis_header: ParachainHeader, + pub node_metadata_repository: Arc, + pub stf_executor: Arc, + pub validator_accessor: Arc, + pub extrinsics_factory: Arc, + pub import_dispatcher: Arc, +} + +impl IntegriteeParachainHandler { + pub fn init( + _base_path: PathBuf, + params: ParachainParams, + ) -> Result { + let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; + let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; + let node_metadata_repository = Arc::new(EnclaveNodeMetadataRepository::default()); + + let genesis_header = params.genesis_header.clone(); + + let light_client_seal = GLOBAL_INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_SEAL.get()?; + let validator = itc_parentchain::light_client::io::read_or_init_parachain_validator::< + ParachainBlock, + EnclaveOCallApi, + _, + >(params, ocall_api.clone(), &*light_client_seal, ParentchainId::Litentry)?; + let validator_accessor = + Arc::new(EnclaveValidatorAccessor::new(validator, light_client_seal)); + + let genesis_hash = validator_accessor.execute_on_validator(|v| v.genesis_hash())?; + + let extrinsics_factory = create_extrinsics_factory( + genesis_hash, + GLOBAL_INTEGRITEE_PARENTCHAIN_NONCE_CACHE.clone(), + node_metadata_repository.clone(), + )?; + + let stf_executor = Arc::new(EnclaveStfExecutor::new( + ocall_api, + state_handler, + node_metadata_repository.clone(), + )); + + let block_importer = create_integritee_parentchain_block_importer( + validator_accessor.clone(), + stf_executor.clone(), + extrinsics_factory.clone(), + node_metadata_repository.clone(), + )?; + + let import_dispatcher = match WorkerModeProvider::worker_mode() { + WorkerMode::OffChainWorker => create_integritee_offchain_immediate_import_dispatcher( + stf_executor.clone(), + block_importer, + validator_accessor.clone(), + extrinsics_factory.clone(), + )?, + WorkerMode::Sidechain => create_sidechain_triggered_import_dispatcher(block_importer), + WorkerMode::Teeracle => + Arc::new(IntegriteeParentchainBlockImportDispatcher::new_empty_dispatcher()), + }; + + let parachain_handler = Self { + genesis_header, + node_metadata_repository, + stf_executor, + validator_accessor, + extrinsics_factory, + import_dispatcher, + }; + + Ok(parachain_handler) + } +} diff --git a/bitacross-worker/enclave-runtime/src/initialization/parentchain/integritee_solochain.rs b/bitacross-worker/enclave-runtime/src/initialization/parentchain/integritee_solochain.rs new file mode 100644 index 0000000000..b5ae349479 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/initialization/parentchain/integritee_solochain.rs @@ -0,0 +1,117 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + error::Result, + initialization::{ + global_components::{ + EnclaveExtrinsicsFactory, EnclaveNodeMetadataRepository, EnclaveOCallApi, + EnclaveStfExecutor, EnclaveValidatorAccessor, + IntegriteeParentchainBlockImportDispatcher, + GLOBAL_INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_SEAL, + GLOBAL_INTEGRITEE_PARENTCHAIN_NONCE_CACHE, GLOBAL_OCALL_API_COMPONENT, + GLOBAL_STATE_HANDLER_COMPONENT, + }, + parentchain::common::{ + create_extrinsics_factory, create_integritee_offchain_immediate_import_dispatcher, + create_integritee_parentchain_block_importer, + create_sidechain_triggered_import_dispatcher, + }, + }, +}; +use itc_parentchain::light_client::{concurrent_access::ValidatorAccess, LightClientState}; +use itp_component_container::ComponentGetter; +use itp_settings::worker_mode::{ProvideWorkerMode, WorkerMode}; +use itp_types::parentchain::ParentchainId; +use std::{path::PathBuf, sync::Arc}; + +pub use itc_parentchain::primitives::{SolochainBlock, SolochainHeader, SolochainParams}; + +pub struct IntegriteeSolochainHandler { + pub genesis_header: SolochainHeader, + pub node_metadata_repository: Arc, + pub stf_executor: Arc, + pub validator_accessor: Arc, + pub extrinsics_factory: Arc, + pub import_dispatcher: Arc, +} + +impl IntegriteeSolochainHandler { + pub fn init( + _base_path: PathBuf, + params: SolochainParams, + ) -> Result { + let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; + let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; + let light_client_seal = GLOBAL_INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_SEAL.get()?; + let node_metadata_repository = Arc::new(EnclaveNodeMetadataRepository::default()); + + let genesis_header = params.genesis_header.clone(); + + let validator = itc_parentchain::light_client::io::read_or_init_grandpa_validator::< + SolochainBlock, + EnclaveOCallApi, + _, + >(params, ocall_api.clone(), &*light_client_seal, ParentchainId::Litentry)?; + let validator_accessor = + Arc::new(EnclaveValidatorAccessor::new(validator, light_client_seal)); + + let genesis_hash = validator_accessor.execute_on_validator(|v| v.genesis_hash())?; + + let extrinsics_factory = create_extrinsics_factory( + genesis_hash, + GLOBAL_INTEGRITEE_PARENTCHAIN_NONCE_CACHE.clone(), + node_metadata_repository.clone(), + )?; + + let stf_executor = Arc::new(EnclaveStfExecutor::new( + ocall_api, + state_handler, + node_metadata_repository.clone(), + )); + + let block_importer = create_integritee_parentchain_block_importer( + validator_accessor.clone(), + stf_executor.clone(), + extrinsics_factory.clone(), + node_metadata_repository.clone(), + )?; + + let import_dispatcher = match WorkerModeProvider::worker_mode() { + WorkerMode::OffChainWorker => create_integritee_offchain_immediate_import_dispatcher( + stf_executor.clone(), + block_importer, + validator_accessor.clone(), + extrinsics_factory.clone(), + )?, + WorkerMode::Sidechain => create_sidechain_triggered_import_dispatcher(block_importer), + WorkerMode::Teeracle => + Arc::new(IntegriteeParentchainBlockImportDispatcher::new_empty_dispatcher()), + }; + + let solochain_handler = Self { + genesis_header, + node_metadata_repository, + stf_executor, + validator_accessor, + extrinsics_factory, + import_dispatcher, + }; + + Ok(solochain_handler) + } +} diff --git a/bitacross-worker/enclave-runtime/src/initialization/parentchain/mod.rs b/bitacross-worker/enclave-runtime/src/initialization/parentchain/mod.rs new file mode 100644 index 0000000000..b0045d6ca5 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/initialization/parentchain/mod.rs @@ -0,0 +1,120 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + error::Result, + initialization::{ + global_components::{ + GLOBAL_INTEGRITEE_PARACHAIN_HANDLER_COMPONENT, + GLOBAL_INTEGRITEE_SOLOCHAIN_HANDLER_COMPONENT, + GLOBAL_TARGET_A_PARACHAIN_HANDLER_COMPONENT, + GLOBAL_TARGET_A_SOLOCHAIN_HANDLER_COMPONENT, + GLOBAL_TARGET_B_PARACHAIN_HANDLER_COMPONENT, + GLOBAL_TARGET_B_SOLOCHAIN_HANDLER_COMPONENT, + }, + parentchain::{ + target_a_parachain::TargetAParachainHandler, + target_a_solochain::TargetASolochainHandler, + target_b_parachain::TargetBParachainHandler, + target_b_solochain::TargetBSolochainHandler, + }, + }, +}; +use codec::{Decode, Encode}; +use integritee_parachain::IntegriteeParachainHandler; +use integritee_solochain::IntegriteeSolochainHandler; +use itc_parentchain::{ + light_client::{concurrent_access::ValidatorAccess, LightClientState}, + primitives::{ParentchainId, ParentchainInitParams}, +}; +use itp_component_container::ComponentInitializer; +use itp_settings::worker_mode::ProvideWorkerMode; +use std::{path::PathBuf, vec::Vec}; + +mod common; +pub mod integritee_parachain; +pub mod integritee_solochain; +pub mod target_a_parachain; +pub mod target_a_solochain; +pub mod target_b_parachain; +pub mod target_b_solochain; + +pub(crate) fn init_parentchain_components( + base_path: PathBuf, + encoded_params: Vec, +) -> Result> { + match ParentchainInitParams::decode(&mut encoded_params.as_slice())? { + ParentchainInitParams::Parachain { id, params } => match id { + ParentchainId::Litentry => { + let handler = + IntegriteeParachainHandler::init::(base_path, params)?; + let header = handler + .validator_accessor + .execute_on_validator(|v| v.latest_finalized_header())?; + GLOBAL_INTEGRITEE_PARACHAIN_HANDLER_COMPONENT.initialize(handler.into()); + Ok(header.encode()) + }, + ParentchainId::TargetA => { + let handler = + TargetAParachainHandler::init::(base_path, params)?; + let header = handler + .validator_accessor + .execute_on_validator(|v| v.latest_finalized_header())?; + GLOBAL_TARGET_A_PARACHAIN_HANDLER_COMPONENT.initialize(handler.into()); + Ok(header.encode()) + }, + ParentchainId::TargetB => { + let handler = + TargetBParachainHandler::init::(base_path, params)?; + let header = handler + .validator_accessor + .execute_on_validator(|v| v.latest_finalized_header())?; + GLOBAL_TARGET_B_PARACHAIN_HANDLER_COMPONENT.initialize(handler.into()); + Ok(header.encode()) + }, + }, + ParentchainInitParams::Solochain { id, params } => match id { + ParentchainId::Litentry => { + let handler = + IntegriteeSolochainHandler::init::(base_path, params)?; + let header = handler + .validator_accessor + .execute_on_validator(|v| v.latest_finalized_header())?; + GLOBAL_INTEGRITEE_SOLOCHAIN_HANDLER_COMPONENT.initialize(handler.into()); + Ok(header.encode()) + }, + ParentchainId::TargetA => { + let handler = + TargetASolochainHandler::init::(base_path, params)?; + let header = handler + .validator_accessor + .execute_on_validator(|v| v.latest_finalized_header())?; + GLOBAL_TARGET_A_SOLOCHAIN_HANDLER_COMPONENT.initialize(handler.into()); + Ok(header.encode()) + }, + ParentchainId::TargetB => { + let handler = + TargetBSolochainHandler::init::(base_path, params)?; + let header = handler + .validator_accessor + .execute_on_validator(|v| v.latest_finalized_header())?; + GLOBAL_TARGET_B_SOLOCHAIN_HANDLER_COMPONENT.initialize(handler.into()); + Ok(header.encode()) + }, + }, + } +} diff --git a/bitacross-worker/enclave-runtime/src/initialization/parentchain/target_a_parachain.rs b/bitacross-worker/enclave-runtime/src/initialization/parentchain/target_a_parachain.rs new file mode 100644 index 0000000000..bf24f6fdd4 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/initialization/parentchain/target_a_parachain.rs @@ -0,0 +1,122 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Naive implementation of adding a second parachain handler to the setup. +//! +//! Ideally, most of the redundant code can be abstracted away, but it turns out +//! that this is quite tedious, so for now this is a copy-past of the [IntegriteeParachainHandler]: +//! * https://github.com/integritee-network/worker/issues/1417 + +use crate::{ + error::Result, + initialization::{ + global_components::{ + EnclaveExtrinsicsFactory, EnclaveNodeMetadataRepository, EnclaveOCallApi, + EnclaveStfExecutor, EnclaveValidatorAccessor, TargetAParentchainBlockImportDispatcher, + GLOBAL_OCALL_API_COMPONENT, GLOBAL_STATE_HANDLER_COMPONENT, + GLOBAL_TARGET_A_PARENTCHAIN_LIGHT_CLIENT_SEAL, GLOBAL_TARGET_A_PARENTCHAIN_NONCE_CACHE, + }, + parentchain::common::{ + create_extrinsics_factory, create_sidechain_triggered_import_dispatcher_for_target_a, + create_target_a_offchain_immediate_import_dispatcher, + create_target_a_parentchain_block_importer, + }, + }, +}; +use itc_parentchain::light_client::{concurrent_access::ValidatorAccess, LightClientState}; +pub use itc_parentchain::primitives::{ParachainBlock, ParachainHeader, ParachainParams}; +use itp_component_container::ComponentGetter; +use itp_settings::worker_mode::{ProvideWorkerMode, WorkerMode}; +use itp_types::parentchain::ParentchainId; +use std::{path::PathBuf, sync::Arc}; + +#[derive(Clone)] +pub struct TargetAParachainHandler { + pub genesis_header: ParachainHeader, + pub node_metadata_repository: Arc, + pub stf_executor: Arc, + pub validator_accessor: Arc, + pub extrinsics_factory: Arc, + pub import_dispatcher: Arc, +} + +impl TargetAParachainHandler { + pub fn init( + _base_path: PathBuf, + params: ParachainParams, + ) -> Result { + let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; + let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; + let node_metadata_repository = Arc::new(EnclaveNodeMetadataRepository::default()); + + let genesis_header = params.genesis_header.clone(); + + let light_client_seal = GLOBAL_TARGET_A_PARENTCHAIN_LIGHT_CLIENT_SEAL.get()?; + let validator = itc_parentchain::light_client::io::read_or_init_parachain_validator::< + ParachainBlock, + EnclaveOCallApi, + _, + >(params, ocall_api.clone(), &*light_client_seal, ParentchainId::TargetA)?; + let validator_accessor = + Arc::new(EnclaveValidatorAccessor::new(validator, light_client_seal)); + + let genesis_hash = validator_accessor.execute_on_validator(|v| v.genesis_hash())?; + + let extrinsics_factory = create_extrinsics_factory( + genesis_hash, + GLOBAL_TARGET_A_PARENTCHAIN_NONCE_CACHE.clone(), + node_metadata_repository.clone(), + )?; + + let stf_executor = Arc::new(EnclaveStfExecutor::new( + ocall_api, + state_handler, + node_metadata_repository.clone(), + )); + + let block_importer = create_target_a_parentchain_block_importer( + validator_accessor.clone(), + stf_executor.clone(), + extrinsics_factory.clone(), + node_metadata_repository.clone(), + )?; + + let import_dispatcher = match WorkerModeProvider::worker_mode() { + WorkerMode::OffChainWorker => create_target_a_offchain_immediate_import_dispatcher( + stf_executor.clone(), + block_importer, + validator_accessor.clone(), + extrinsics_factory.clone(), + )?, + WorkerMode::Sidechain => + create_sidechain_triggered_import_dispatcher_for_target_a(block_importer), + WorkerMode::Teeracle => + Arc::new(TargetAParentchainBlockImportDispatcher::new_empty_dispatcher()), + }; + + let parachain_handler = Self { + genesis_header, + node_metadata_repository, + stf_executor, + validator_accessor, + extrinsics_factory, + import_dispatcher, + }; + + Ok(parachain_handler) + } +} diff --git a/bitacross-worker/enclave-runtime/src/initialization/parentchain/target_a_solochain.rs b/bitacross-worker/enclave-runtime/src/initialization/parentchain/target_a_solochain.rs new file mode 100644 index 0000000000..f5cf2ae8ff --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/initialization/parentchain/target_a_solochain.rs @@ -0,0 +1,115 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + error::Result, + initialization::{ + global_components::{ + EnclaveExtrinsicsFactory, EnclaveNodeMetadataRepository, EnclaveOCallApi, + EnclaveStfExecutor, EnclaveValidatorAccessor, TargetAParentchainBlockImportDispatcher, + GLOBAL_OCALL_API_COMPONENT, GLOBAL_STATE_HANDLER_COMPONENT, + GLOBAL_TARGET_A_PARENTCHAIN_LIGHT_CLIENT_SEAL, GLOBAL_TARGET_A_PARENTCHAIN_NONCE_CACHE, + }, + parentchain::common::{ + create_extrinsics_factory, create_sidechain_triggered_import_dispatcher_for_target_a, + create_target_a_offchain_immediate_import_dispatcher, + create_target_a_parentchain_block_importer, + }, + }, +}; +use itc_parentchain::light_client::{concurrent_access::ValidatorAccess, LightClientState}; +pub use itc_parentchain::primitives::{SolochainBlock, SolochainHeader, SolochainParams}; +use itp_component_container::ComponentGetter; +use itp_settings::worker_mode::{ProvideWorkerMode, WorkerMode}; +use itp_types::parentchain::ParentchainId; +use std::{path::PathBuf, sync::Arc}; + +pub struct TargetASolochainHandler { + pub genesis_header: SolochainHeader, + pub node_metadata_repository: Arc, + pub stf_executor: Arc, + pub validator_accessor: Arc, + pub extrinsics_factory: Arc, + pub import_dispatcher: Arc, +} + +impl TargetASolochainHandler { + pub fn init( + _base_path: PathBuf, + params: SolochainParams, + ) -> Result { + let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; + let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; + let light_client_seal = GLOBAL_TARGET_A_PARENTCHAIN_LIGHT_CLIENT_SEAL.get()?; + let node_metadata_repository = Arc::new(EnclaveNodeMetadataRepository::default()); + + let genesis_header = params.genesis_header.clone(); + + let validator = itc_parentchain::light_client::io::read_or_init_grandpa_validator::< + SolochainBlock, + EnclaveOCallApi, + _, + >(params, ocall_api.clone(), &*light_client_seal, ParentchainId::TargetA)?; + let validator_accessor = + Arc::new(EnclaveValidatorAccessor::new(validator, light_client_seal)); + + let genesis_hash = validator_accessor.execute_on_validator(|v| v.genesis_hash())?; + + let extrinsics_factory = create_extrinsics_factory( + genesis_hash, + GLOBAL_TARGET_A_PARENTCHAIN_NONCE_CACHE.clone(), + node_metadata_repository.clone(), + )?; + + let stf_executor = Arc::new(EnclaveStfExecutor::new( + ocall_api, + state_handler, + node_metadata_repository.clone(), + )); + + let block_importer = create_target_a_parentchain_block_importer( + validator_accessor.clone(), + stf_executor.clone(), + extrinsics_factory.clone(), + node_metadata_repository.clone(), + )?; + + let import_dispatcher = match WorkerModeProvider::worker_mode() { + WorkerMode::OffChainWorker => create_target_a_offchain_immediate_import_dispatcher( + stf_executor.clone(), + block_importer, + validator_accessor.clone(), + extrinsics_factory.clone(), + )?, + WorkerMode::Sidechain => + create_sidechain_triggered_import_dispatcher_for_target_a(block_importer), + WorkerMode::Teeracle => + Arc::new(TargetAParentchainBlockImportDispatcher::new_empty_dispatcher()), + }; + + let solochain_handler = Self { + genesis_header, + node_metadata_repository, + stf_executor, + validator_accessor, + extrinsics_factory, + import_dispatcher, + }; + + Ok(solochain_handler) + } +} diff --git a/bitacross-worker/enclave-runtime/src/initialization/parentchain/target_b_parachain.rs b/bitacross-worker/enclave-runtime/src/initialization/parentchain/target_b_parachain.rs new file mode 100644 index 0000000000..be44224c65 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/initialization/parentchain/target_b_parachain.rs @@ -0,0 +1,122 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Naive implementation of adding a second parachain handler to the setup. +//! +//! Ideally, most of the redundant code can be abstracted away, but it turns out +//! that this is quite tedious, so for now this is a copy-past of the [IntegriteeParachainHandler]: +//! * https://github.com/integritee-network/worker/issues/1417 + +use crate::{ + error::Result, + initialization::{ + global_components::{ + EnclaveExtrinsicsFactory, EnclaveNodeMetadataRepository, EnclaveOCallApi, + EnclaveStfExecutor, EnclaveValidatorAccessor, TargetBParentchainBlockImportDispatcher, + GLOBAL_OCALL_API_COMPONENT, GLOBAL_STATE_HANDLER_COMPONENT, + GLOBAL_TARGET_B_PARENTCHAIN_LIGHT_CLIENT_SEAL, GLOBAL_TARGET_B_PARENTCHAIN_NONCE_CACHE, + }, + parentchain::common::{ + create_extrinsics_factory, create_sidechain_triggered_import_dispatcher_for_target_b, + create_target_b_offchain_immediate_import_dispatcher, + create_target_b_parentchain_block_importer, + }, + }, +}; +use itc_parentchain::light_client::{concurrent_access::ValidatorAccess, LightClientState}; +pub use itc_parentchain::primitives::{ParachainBlock, ParachainHeader, ParachainParams}; +use itp_component_container::ComponentGetter; +use itp_settings::worker_mode::{ProvideWorkerMode, WorkerMode}; +use itp_types::parentchain::ParentchainId; +use std::{path::PathBuf, sync::Arc}; + +#[derive(Clone)] +pub struct TargetBParachainHandler { + pub genesis_header: ParachainHeader, + pub node_metadata_repository: Arc, + pub stf_executor: Arc, + pub validator_accessor: Arc, + pub extrinsics_factory: Arc, + pub import_dispatcher: Arc, +} + +impl TargetBParachainHandler { + pub fn init( + _base_path: PathBuf, + params: ParachainParams, + ) -> Result { + let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; + let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; + let node_metadata_repository = Arc::new(EnclaveNodeMetadataRepository::default()); + + let genesis_header = params.genesis_header.clone(); + + let light_client_seal = GLOBAL_TARGET_B_PARENTCHAIN_LIGHT_CLIENT_SEAL.get()?; + let validator = itc_parentchain::light_client::io::read_or_init_parachain_validator::< + ParachainBlock, + EnclaveOCallApi, + _, + >(params, ocall_api.clone(), &*light_client_seal, ParentchainId::TargetB)?; + let validator_accessor = + Arc::new(EnclaveValidatorAccessor::new(validator, light_client_seal)); + + let genesis_hash = validator_accessor.execute_on_validator(|v| v.genesis_hash())?; + + let extrinsics_factory = create_extrinsics_factory( + genesis_hash, + GLOBAL_TARGET_B_PARENTCHAIN_NONCE_CACHE.clone(), + node_metadata_repository.clone(), + )?; + + let stf_executor = Arc::new(EnclaveStfExecutor::new( + ocall_api, + state_handler, + node_metadata_repository.clone(), + )); + + let block_importer = create_target_b_parentchain_block_importer( + validator_accessor.clone(), + stf_executor.clone(), + extrinsics_factory.clone(), + node_metadata_repository.clone(), + )?; + + let import_dispatcher = match WorkerModeProvider::worker_mode() { + WorkerMode::OffChainWorker => create_target_b_offchain_immediate_import_dispatcher( + stf_executor.clone(), + block_importer, + validator_accessor.clone(), + extrinsics_factory.clone(), + )?, + WorkerMode::Sidechain => + create_sidechain_triggered_import_dispatcher_for_target_b(block_importer), + WorkerMode::Teeracle => + Arc::new(TargetBParentchainBlockImportDispatcher::new_empty_dispatcher()), + }; + + let parachain_handler = Self { + genesis_header, + node_metadata_repository, + stf_executor, + validator_accessor, + extrinsics_factory, + import_dispatcher, + }; + + Ok(parachain_handler) + } +} diff --git a/bitacross-worker/enclave-runtime/src/initialization/parentchain/target_b_solochain.rs b/bitacross-worker/enclave-runtime/src/initialization/parentchain/target_b_solochain.rs new file mode 100644 index 0000000000..842baa8129 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/initialization/parentchain/target_b_solochain.rs @@ -0,0 +1,115 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + error::Result, + initialization::{ + global_components::{ + EnclaveExtrinsicsFactory, EnclaveNodeMetadataRepository, EnclaveOCallApi, + EnclaveStfExecutor, EnclaveValidatorAccessor, TargetBParentchainBlockImportDispatcher, + GLOBAL_OCALL_API_COMPONENT, GLOBAL_STATE_HANDLER_COMPONENT, + GLOBAL_TARGET_B_PARENTCHAIN_LIGHT_CLIENT_SEAL, GLOBAL_TARGET_B_PARENTCHAIN_NONCE_CACHE, + }, + parentchain::common::{ + create_extrinsics_factory, create_sidechain_triggered_import_dispatcher_for_target_b, + create_target_b_offchain_immediate_import_dispatcher, + create_target_b_parentchain_block_importer, + }, + }, +}; +use itc_parentchain::light_client::{concurrent_access::ValidatorAccess, LightClientState}; +pub use itc_parentchain::primitives::{SolochainBlock, SolochainHeader, SolochainParams}; +use itp_component_container::ComponentGetter; +use itp_settings::worker_mode::{ProvideWorkerMode, WorkerMode}; +use itp_types::parentchain::ParentchainId; +use std::{path::PathBuf, sync::Arc}; + +pub struct TargetBSolochainHandler { + pub genesis_header: SolochainHeader, + pub node_metadata_repository: Arc, + pub stf_executor: Arc, + pub validator_accessor: Arc, + pub extrinsics_factory: Arc, + pub import_dispatcher: Arc, +} + +impl TargetBSolochainHandler { + pub fn init( + _base_path: PathBuf, + params: SolochainParams, + ) -> Result { + let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; + let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; + let light_client_seal = GLOBAL_TARGET_B_PARENTCHAIN_LIGHT_CLIENT_SEAL.get()?; + let node_metadata_repository = Arc::new(EnclaveNodeMetadataRepository::default()); + + let genesis_header = params.genesis_header.clone(); + + let validator = itc_parentchain::light_client::io::read_or_init_grandpa_validator::< + SolochainBlock, + EnclaveOCallApi, + _, + >(params, ocall_api.clone(), &*light_client_seal, ParentchainId::TargetB)?; + let validator_accessor = + Arc::new(EnclaveValidatorAccessor::new(validator, light_client_seal)); + + let genesis_hash = validator_accessor.execute_on_validator(|v| v.genesis_hash())?; + + let extrinsics_factory = create_extrinsics_factory( + genesis_hash, + GLOBAL_TARGET_B_PARENTCHAIN_NONCE_CACHE.clone(), + node_metadata_repository.clone(), + )?; + + let stf_executor = Arc::new(EnclaveStfExecutor::new( + ocall_api, + state_handler, + node_metadata_repository.clone(), + )); + + let block_importer = create_target_b_parentchain_block_importer( + validator_accessor.clone(), + stf_executor.clone(), + extrinsics_factory.clone(), + node_metadata_repository.clone(), + )?; + + let import_dispatcher = match WorkerModeProvider::worker_mode() { + WorkerMode::OffChainWorker => create_target_b_offchain_immediate_import_dispatcher( + stf_executor.clone(), + block_importer, + validator_accessor.clone(), + extrinsics_factory.clone(), + )?, + WorkerMode::Sidechain => + create_sidechain_triggered_import_dispatcher_for_target_b(block_importer), + WorkerMode::Teeracle => + Arc::new(TargetBParentchainBlockImportDispatcher::new_empty_dispatcher()), + }; + + let solochain_handler = Self { + genesis_header, + node_metadata_repository, + stf_executor, + validator_accessor, + extrinsics_factory, + import_dispatcher, + }; + + Ok(solochain_handler) + } +} diff --git a/bitacross-worker/enclave-runtime/src/ipfs.rs b/bitacross-worker/enclave-runtime/src/ipfs.rs new file mode 100644 index 0000000000..c376456455 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/ipfs.rs @@ -0,0 +1,105 @@ +use cid::{Cid, Result as CidResult}; +use ipfs_unixfs::file::adder::FileAdder; +use log::*; +use multibase::Base; +use std::{convert::TryFrom, vec::Vec}; + +pub struct IpfsContent { + pub cid: CidResult, + pub file_content: Vec, + pub stats: Stats, +} +#[derive(Debug, PartialEq)] +pub enum IpfsError { + InputCidInvalid, + FinalCidMissing, + Verification, +} + +impl IpfsContent { + pub fn new(_cid: &str, _content: Vec) -> IpfsContent { + IpfsContent { cid: Cid::try_from(_cid), file_content: _content, stats: Stats::default() } + } + + pub fn verify(&mut self) -> Result<(), IpfsError> { + let mut adder: FileAdder = FileAdder::default(); + let mut total: usize = 0; + while total < self.file_content.len() { + #[allow(clippy::string_slice)] + let bytes = &self.file_content.get(total..).ok_or(IpfsError::Verification)?; + let (blocks, consumed) = adder.push(bytes); + total = total.saturating_add(consumed); + self.stats.process(blocks); + } + let blocks = adder.finish(); + self.stats.process(blocks); + + if let Some(last_cid) = self.stats.last.as_ref() { + let cid_str = Base::Base58Btc.encode(last_cid.hash().as_bytes()); + info!( + "new cid: {} generated from {} blocks, total of {} bytes", + cid_str, self.stats.blocks, self.stats.block_bytes + ); + match self.cid.as_ref() { + Ok(initial_cid) => + if last_cid.hash().eq(&initial_cid.hash()) { + Ok(()) + } else { + Err(IpfsError::Verification) + }, + Err(_) => Err(IpfsError::InputCidInvalid), + } + } else { + Err(IpfsError::FinalCidMissing) + } + } +} +#[derive(Default)] +pub struct Stats { + pub blocks: usize, + pub block_bytes: u64, + pub last: Option, +} + +impl Stats { + fn process)>>(&mut self, new_blocks: I) { + for (cid, block) in new_blocks { + self.last = Some(cid); + self.blocks = self.blocks.saturating_add(1); + self.block_bytes = self.block_bytes.saturating_add(block.len() as u64); + } + } +} + +#[allow(unused)] +pub fn test_creates_ipfs_content_struct_works() { + let cid = "QmSaFjwJ2QtS3rZDKzC98XEzv2bqT4TfpWLCpphPPwyQTr"; + let content: Vec = vec![20; 512 * 1024]; + let ipfs_content = IpfsContent::new(cid, content.clone()); + + #[allow(clippy::unwrap_used)] + let cid_str = Base::Base58Btc.encode(ipfs_content.cid.as_ref().unwrap().hash().as_bytes()); + assert_eq!(cid_str, cid); + assert_eq!(ipfs_content.file_content, content); +} + +#[allow(unused)] +pub fn test_verification_ok_for_correct_content() { + let cid = "QmSaFjwJ2QtS3rZDKzC98XEzv2bqT4TfpWLCpphPPwyQTr"; + let content: Vec = vec![20; 512 * 1024]; + let mut ipfs_content = IpfsContent::new(cid, content); + let verification = ipfs_content.verify(); + assert!(verification.is_ok()); +} + +#[allow(unused)] +pub fn test_verification_fails_for_incorrect_content() { + let cid = "QmSaFjwJ2QtS3rZDKzC98XEzv2bqT4TfpWLCpphPPwyQTr"; + let content: Vec = vec![10; 512 * 1024]; + let mut ipfs_content = IpfsContent::new(cid, content); + let verification = ipfs_content.verify(); + #[allow(clippy::unwrap_used)] + { + assert_eq!(verification.unwrap_err(), IpfsError::Verification); + } +} diff --git a/bitacross-worker/enclave-runtime/src/lib.rs b/bitacross-worker/enclave-runtime/src/lib.rs new file mode 100644 index 0000000000..9c3b078558 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/lib.rs @@ -0,0 +1,708 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +#![feature(structural_match)] +#![feature(rustc_attrs)] +#![feature(core_intrinsics)] +#![feature(derive_eq)] +#![feature(trait_alias)] +#![crate_name = "enclave_runtime"] +#![crate_type = "staticlib"] +#![cfg_attr(not(target_env = "sgx"), no_std)] +#![cfg_attr(target_env = "sgx", feature(rustc_private))] +#![allow(clippy::missing_safety_doc)] +#![warn( + clippy::unwrap_used, + clippy::unreachable, + /* comment out for the moment. There are some upstream code `unimplemented` */ + // clippy::unimplemented, + // clippy::panic_in_result_fn, + clippy::string_slice, + clippy::panic, + clippy::indexing_slicing, + clippy::expect_used, + clippy::arithmetic_side_effects +)] + +#[cfg(not(target_env = "sgx"))] +#[macro_use] +extern crate sgx_tstd as std; + +use crate::{ + error::{Error, Result}, + initialization::global_components::{ + GLOBAL_INTEGRITEE_PARACHAIN_HANDLER_COMPONENT, GLOBAL_INTEGRITEE_PARENTCHAIN_NONCE_CACHE, + GLOBAL_INTEGRITEE_SOLOCHAIN_HANDLER_COMPONENT, GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT, + GLOBAL_SIDECHAIN_IMPORT_QUEUE_COMPONENT, GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT, + GLOBAL_STATE_HANDLER_COMPONENT, GLOBAL_TARGET_A_PARACHAIN_HANDLER_COMPONENT, + GLOBAL_TARGET_A_PARENTCHAIN_NONCE_CACHE, GLOBAL_TARGET_A_SOLOCHAIN_HANDLER_COMPONENT, + GLOBAL_TARGET_B_PARACHAIN_HANDLER_COMPONENT, GLOBAL_TARGET_B_PARENTCHAIN_NONCE_CACHE, + GLOBAL_TARGET_B_SOLOCHAIN_HANDLER_COMPONENT, + }, + rpc::worker_api_direct::sidechain_io_handler, + utils::{ + get_node_metadata_repository_from_integritee_solo_or_parachain, + get_node_metadata_repository_from_target_a_solo_or_parachain, + get_node_metadata_repository_from_target_b_solo_or_parachain, + get_validator_accessor_from_integritee_solo_or_parachain, utf8_str_from_raw, DecodeRaw, + }, +}; +use codec::Decode; +use core::ffi::c_int; +use itc_parentchain::{ + block_import_dispatcher::DispatchBlockImport, + light_client::{concurrent_access::ValidatorAccess, Validator}, + primitives::ParentchainId, +}; +use itp_component_container::ComponentGetter; +use itp_import_queue::PushToQueue; +use itp_node_api::metadata::NodeMetadata; +use itp_nonce_cache::{MutateNonce, Nonce}; +use itp_settings::worker_mode::{ProvideWorkerMode, WorkerMode, WorkerModeProvider}; +use itp_sgx_crypto::key_repository::AccessPubkey; +use itp_storage::{StorageProof, StorageProofChecker}; +use itp_types::{ShardIdentifier, SignedBlock}; +use itp_utils::{if_production_or, write_slice_and_whitespace_pad}; +use log::*; +use once_cell::sync::OnceCell; +use sgx_types::sgx_status_t; +use sp_runtime::traits::BlakeTwo256; +use std::{ + path::PathBuf, + slice, + string::{String, ToString}, + vec::Vec, +}; +mod attestation; +mod empty_impls; +mod initialization; +mod ipfs; +mod ocall; +mod shard_vault; +mod stf_task_handler; +mod utils; + +pub mod error; +pub mod rpc; +mod sync; +mod tls_ra; +pub mod top_pool_execution; + +#[cfg(feature = "teeracle")] +pub mod teeracle; + +#[cfg(feature = "test")] +pub mod test; + +pub type Hash = sp_core::H256; +pub type AuthorityPair = sp_core::ed25519::Pair; + +static BASE_PATH: OnceCell = OnceCell::new(); + +fn get_base_path() -> Result { + let base_path = BASE_PATH.get().ok_or_else(|| { + Error::Other("BASE_PATH not initialized. Broken enclave init flow!".to_string().into()) + })?; + + Ok(base_path.clone()) +} + +/// Initialize the enclave. +#[no_mangle] +pub unsafe extern "C" fn init( + mu_ra_addr: *const u8, + mu_ra_addr_size: u32, + untrusted_worker_addr: *const u8, + untrusted_worker_addr_size: u32, + encoded_base_dir_str: *const u8, + encoded_base_dir_size: u32, +) -> sgx_status_t { + // Initialize the logging environment in the enclave. + if_production_or!( + { + let module_names = litentry_macros::local_modules!(); + println!( + "Initializing logger to filter only following local modules: {:?}", + module_names + ); + let mut builder = env_logger::Builder::new(); + builder.filter(None, LevelFilter::Off); + module_names.into_iter().for_each(|module| { + builder.filter(Some(module), LevelFilter::Info); + }); + builder.init(); + }, + env_logger::init() + ); + + let mu_ra_url = + match String::decode(&mut slice::from_raw_parts(mu_ra_addr, mu_ra_addr_size as usize)) + .map_err(Error::Codec) + { + Ok(addr) => addr, + Err(e) => return e.into(), + }; + + let untrusted_worker_url = match String::decode(&mut slice::from_raw_parts( + untrusted_worker_addr, + untrusted_worker_addr_size as usize, + )) + .map_err(Error::Codec) + { + Ok(addr) => addr, + Err(e) => return e.into(), + }; + + let base_dir = match String::decode(&mut slice::from_raw_parts( + encoded_base_dir_str, + encoded_base_dir_size as usize, + )) + .map_err(Error::Codec) + { + Ok(b) => b, + Err(e) => return e.into(), + }; + + info!("Setting base_dir to {}", base_dir); + let path = PathBuf::from(base_dir); + // Litentry: the default value here is only for clippy checking + BASE_PATH.set(path.clone()).unwrap_or(()); + + match initialization::init_enclave(mu_ra_url, untrusted_worker_url, path) { + Err(e) => e.into(), + Ok(()) => sgx_status_t::SGX_SUCCESS, + } +} + +#[no_mangle] +pub unsafe extern "C" fn get_rsa_encryption_pubkey( + pubkey: *mut u8, + pubkey_size: u32, +) -> sgx_status_t { + let shielding_key_repository = match GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT.get() { + Ok(s) => s, + Err(e) => { + error!("{:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + let rsa_pubkey = match shielding_key_repository.retrieve_pubkey() { + Ok(key) => key, + Err(e) => return e.into(), + }; + + let rsa_pubkey_json = match serde_json::to_string(&rsa_pubkey) { + Ok(k) => k, + Err(x) => { + println!("[Enclave] can't serialize rsa_pubkey {:?} {}", rsa_pubkey, x); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + let pubkey_slice = slice::from_raw_parts_mut(pubkey, pubkey_size as usize); + + if let Err(e) = + write_slice_and_whitespace_pad(pubkey_slice, rsa_pubkey_json.as_bytes().to_vec()) + { + return Error::BufferError(e).into() + }; + + sgx_status_t::SGX_SUCCESS +} + +#[no_mangle] +pub unsafe extern "C" fn get_ecc_signing_pubkey(pubkey: *mut u8, pubkey_size: u32) -> sgx_status_t { + let signing_key_repository = match GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT.get() { + Ok(s) => s, + Err(e) => { + error!("{:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + let signer_public = match signing_key_repository.retrieve_pubkey() { + Ok(s) => s, + Err(e) => return e.into(), + }; + + debug!("Restored ECC pubkey: {:?}", signer_public); + + let pubkey_slice = slice::from_raw_parts_mut(pubkey, pubkey_size as usize); + pubkey_slice.clone_from_slice(&signer_public); + + sgx_status_t::SGX_SUCCESS +} + +#[no_mangle] +pub unsafe extern "C" fn set_nonce( + nonce: *const u32, + parentchain_id: *const u8, + parentchain_id_size: u32, +) -> sgx_status_t { + let id = match ParentchainId::decode_raw(parentchain_id, parentchain_id_size as usize) { + Err(e) => { + error!("Failed to decode parentchain_id: {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + Ok(m) => m, + }; + + info!("Setting the nonce of the enclave to: {} for parentchain: {:?}", *nonce, id); + + let nonce_lock = match id { + ParentchainId::Litentry => GLOBAL_INTEGRITEE_PARENTCHAIN_NONCE_CACHE.load_for_mutation(), + ParentchainId::TargetA => GLOBAL_TARGET_A_PARENTCHAIN_NONCE_CACHE.load_for_mutation(), + ParentchainId::TargetB => GLOBAL_TARGET_B_PARENTCHAIN_NONCE_CACHE.load_for_mutation(), + }; + + match nonce_lock { + Ok(mut nonce_guard) => *nonce_guard = Nonce(*nonce), + Err(e) => { + error!("Failed to set {:?} parentchain nonce in enclave: {:?}", id, e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + sgx_status_t::SGX_SUCCESS +} + +#[no_mangle] +pub unsafe extern "C" fn set_node_metadata( + node_metadata: *const u8, + node_metadata_size: u32, + parentchain_id: *const u8, + parentchain_id_size: u32, +) -> sgx_status_t { + let id = match ParentchainId::decode_raw(parentchain_id, parentchain_id_size as usize) { + Err(e) => { + error!("Failed to decode parentchain_id: {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + Ok(m) => m, + }; + + let metadata = match NodeMetadata::decode_raw(node_metadata, node_metadata_size as usize) { + Err(e) => { + error!("Failed to decode node metadata: {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + Ok(m) => m, + }; + + info!("Setting node meta data for parentchain: {:?}", id); + + let node_metadata_repository = match id { + ParentchainId::Litentry => get_node_metadata_repository_from_integritee_solo_or_parachain(), + ParentchainId::TargetA => get_node_metadata_repository_from_target_a_solo_or_parachain(), + ParentchainId::TargetB => get_node_metadata_repository_from_target_b_solo_or_parachain(), + }; + + match node_metadata_repository { + Ok(repo) => repo.set_metadata(metadata), + Err(e) => { + error!("Could not get {:?} parentchain component: {:?}", id, e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + info!("Successfully set the node meta data"); + + sgx_status_t::SGX_SUCCESS +} + +/// This is reduced to the sidechain block import RPC interface (i.e. worker-worker communication). +/// The entire rest of the RPC server is run inside the enclave and does not use this e-call function anymore. +#[no_mangle] +pub unsafe extern "C" fn call_rpc_methods( + request: *const u8, + request_len: u32, + response: *mut u8, + response_len: u32, +) -> sgx_status_t { + let request = match utf8_str_from_raw(request, request_len as usize) { + Ok(req) => req, + Err(e) => { + error!("[SidechainRpc] FFI: Invalid utf8 request: {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + let res = match sidechain_rpc_int(request) { + Ok(res) => res, + Err(e) => { + error!("RPC request failed: {:?}", e); + return e.into() + }, + }; + + let response_slice = slice::from_raw_parts_mut(response, response_len as usize); + if let Err(e) = write_slice_and_whitespace_pad(response_slice, res.into_bytes()) { + return Error::BufferError(e).into() + }; + + sgx_status_t::SGX_SUCCESS +} + +fn sidechain_rpc_int(request: &str) -> Result { + let sidechain_block_import_queue = GLOBAL_SIDECHAIN_IMPORT_QUEUE_COMPONENT.get()?; + + let io = sidechain_io_handler(move |signed_block| { + sidechain_block_import_queue.push_single(signed_block) + }); + + // note: errors are still returned as Option + Ok(io + .handle_request_sync(request) + .unwrap_or_else(|| format!("Empty rpc response for request: {}", request))) +} + +/// Initialize sidechain enclave components. +/// +/// Call this once at startup. Has to be called AFTER the light-client +/// (parentchain components) have been initialized (because we need the parentchain +/// block import dispatcher). +#[no_mangle] +pub unsafe extern "C" fn init_enclave_sidechain_components( + fail_mode: *const u8, + fail_mode_size: u32, + fail_at: *const u8, + fail_at_size: u32, +) -> sgx_status_t { + let fail_mode = match Option::::decode_raw(fail_mode, fail_mode_size as usize) { + Ok(s) => s, + Err(e) => { + error!("failed to decode fail mode {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + let fail_at = match u64::decode_raw(fail_at, fail_at_size as usize) { + Ok(v) => v, + Err(e) => { + error!("failed to decode fail at {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + if let Err(e) = initialization::init_enclave_sidechain_components(fail_mode, fail_at) { + error!("Failed to initialize sidechain components: {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + } + + sgx_status_t::SGX_SUCCESS +} + +/// Call this once at worker startup to initialize the TOP pool and direct invocation RPC server. +/// +/// This function will run the RPC server on the same thread as it is called and will loop there. +/// That means that this function will not return as long as the RPC server is running. The calling +/// code should therefore spawn a new thread when calling this function. +#[no_mangle] +pub unsafe extern "C" fn init_direct_invocation_server( + server_addr: *const u8, + server_addr_size: usize, +) -> sgx_status_t { + let mut server_addr_encoded = slice::from_raw_parts(server_addr, server_addr_size); + + let server_addr = match String::decode(&mut server_addr_encoded) { + Ok(s) => s, + Err(e) => { + error!("Decoding RPC server address failed. Error: {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + if let Err(e) = initialization::init_direct_invocation_server(server_addr) { + error!("Failed to initialize direct invocation server: {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + } + + sgx_status_t::SGX_SUCCESS +} + +#[no_mangle] +pub unsafe extern "C" fn init_parentchain_components( + params: *const u8, + params_size: usize, + latest_header: *mut u8, + latest_header_size: usize, +) -> sgx_status_t { + info!("Initializing light client!"); + + let encoded_params = slice::from_raw_parts(params, params_size); + let latest_header_slice = slice::from_raw_parts_mut(latest_header, latest_header_size); + + match init_parentchain_params_internal(encoded_params.to_vec(), latest_header_slice) { + Ok(()) => sgx_status_t::SGX_SUCCESS, + Err(e) => e.into(), + } +} + +/// Initializes the parentchain components and writes the latest header into the `latest_header` slice. +fn init_parentchain_params_internal(params: Vec, latest_header: &mut [u8]) -> Result<()> { + use initialization::parentchain::init_parentchain_components; + + let encoded_latest_header = + init_parentchain_components::(get_base_path()?, params)?; + + write_slice_and_whitespace_pad(latest_header, encoded_latest_header)?; + + Ok(()) +} + +#[no_mangle] +pub unsafe extern "C" fn init_shard(shard: *const u8, shard_size: u32) -> sgx_status_t { + let shard_identifier = + ShardIdentifier::from_slice(slice::from_raw_parts(shard, shard_size as usize)); + + if let Err(e) = initialization::init_shard(shard_identifier) { + error!("Failed to initialize shard ({:?}): {:?}", shard_identifier, e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + } + + sgx_status_t::SGX_SUCCESS +} + +#[no_mangle] +pub unsafe extern "C" fn migrate_shard( + old_shard: *const u8, + new_shard: *const u8, + shard_size: u32, +) -> sgx_status_t { + let old_shard_identifier = + ShardIdentifier::from_slice(slice::from_raw_parts(old_shard, shard_size as usize)); + + let new_shard_identifier = + ShardIdentifier::from_slice(slice::from_raw_parts(new_shard, shard_size as usize)); + + if let Err(e) = initialization::migrate_shard(old_shard_identifier, new_shard_identifier) { + error!("Failed to initialize shard ({:?}): {:?}", old_shard_identifier, e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + } + + sgx_status_t::SGX_SUCCESS +} + +#[no_mangle] +pub unsafe extern "C" fn sync_parentchain( + blocks_to_sync: *const u8, + blocks_to_sync_size: usize, + events_to_sync: *const u8, + events_to_sync_size: usize, + events_proofs_to_sync: *const u8, + events_proofs_to_sync_size: usize, + parentchain_id: *const u8, + parentchain_id_size: u32, + is_syncing: c_int, +) -> sgx_status_t { + if let Err(e) = sync_parentchain_internal( + blocks_to_sync, + blocks_to_sync_size, + events_to_sync, + events_to_sync_size, + events_proofs_to_sync, + events_proofs_to_sync_size, + parentchain_id, + parentchain_id_size, + is_syncing == 1, + ) { + error!("Error synching parentchain: {:?}", e); + } + + sgx_status_t::SGX_SUCCESS +} + +#[allow(clippy::too_many_arguments)] +unsafe fn sync_parentchain_internal( + blocks_to_sync: *const u8, + blocks_to_sync_size: usize, + events_to_sync: *const u8, + events_to_sync_size: usize, + events_proofs_to_sync: *const u8, + events_proofs_to_sync_size: usize, + parentchain_id: *const u8, + parentchain_id_size: u32, + is_syncing: bool, +) -> Result<()> { + let blocks_to_sync = Vec::::decode_raw(blocks_to_sync, blocks_to_sync_size)?; + let events_proofs_to_sync = + Vec::::decode_raw(events_proofs_to_sync, events_proofs_to_sync_size)?; + let parentchain_id = ParentchainId::decode_raw(parentchain_id, parentchain_id_size as usize)?; + + let blocks_to_sync_merkle_roots: Vec = + blocks_to_sync.iter().map(|block| block.block.header.state_root).collect(); + + if let Err(e) = validate_events(&events_proofs_to_sync, &blocks_to_sync_merkle_roots) { + return e.into() + } + + let events_to_sync = Vec::>::decode_raw(events_to_sync, events_to_sync_size)?; + + dispatch_parentchain_blocks_for_import::( + blocks_to_sync, + events_to_sync, + &parentchain_id, + is_syncing, + ) +} + +#[no_mangle] +pub unsafe extern "C" fn ignore_parentchain_block_import_validation_until( + until: *const u32, +) -> sgx_status_t { + let va = match get_validator_accessor_from_integritee_solo_or_parachain() { + Ok(r) => r, + Err(e) => { + error!("Can't get validator accessor: {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + let _ = va.execute_mut_on_validator(|v| v.set_ignore_validation_until(*until)); + + sgx_status_t::SGX_SUCCESS +} + +/// Dispatch the parentchain blocks for import. +/// Depending on the worker mode, a different dispatcher is used: +/// +/// * An immediate dispatcher will immediately import any parentchain blocks and execute +/// the corresponding extrinsics (offchain-worker executor). +/// * The sidechain uses a triggered dispatcher, where the import of a parentchain block is +/// synchronized and triggered by the sidechain block production cycle. +/// +fn dispatch_parentchain_blocks_for_import( + blocks_to_sync: Vec, + events_to_sync: Vec>, + id: &ParentchainId, + is_syncing: bool, +) -> Result<()> { + if WorkerModeProvider::worker_mode() == WorkerMode::Teeracle { + trace!("Not importing any parentchain blocks"); + return Ok(()) + } + trace!( + "[{:?}] Dispatching Import of {} blocks and {} events", + id, + blocks_to_sync.len(), + events_to_sync.len() + ); + match id { + ParentchainId::Litentry => { + if let Ok(handler) = GLOBAL_INTEGRITEE_SOLOCHAIN_HANDLER_COMPONENT.get() { + handler.import_dispatcher.dispatch_import( + blocks_to_sync, + events_to_sync, + is_syncing, + )?; + } else if let Ok(handler) = GLOBAL_INTEGRITEE_PARACHAIN_HANDLER_COMPONENT.get() { + handler.import_dispatcher.dispatch_import( + blocks_to_sync, + events_to_sync, + is_syncing, + )?; + } else { + return Err(Error::NoLitentryParentchainAssigned) + }; + }, + ParentchainId::TargetA => { + if let Ok(handler) = GLOBAL_TARGET_A_SOLOCHAIN_HANDLER_COMPONENT.get() { + handler.import_dispatcher.dispatch_import( + blocks_to_sync, + events_to_sync, + is_syncing, + )?; + } else if let Ok(handler) = GLOBAL_TARGET_A_PARACHAIN_HANDLER_COMPONENT.get() { + handler.import_dispatcher.dispatch_import( + blocks_to_sync, + events_to_sync, + is_syncing, + )?; + } else { + return Err(Error::NoTargetAParentchainAssigned) + }; + }, + ParentchainId::TargetB => { + if let Ok(handler) = GLOBAL_TARGET_B_SOLOCHAIN_HANDLER_COMPONENT.get() { + handler.import_dispatcher.dispatch_import( + blocks_to_sync, + events_to_sync, + is_syncing, + )?; + } else if let Ok(handler) = GLOBAL_TARGET_B_PARACHAIN_HANDLER_COMPONENT.get() { + handler.import_dispatcher.dispatch_import( + blocks_to_sync, + events_to_sync, + is_syncing, + )?; + } else { + return Err(Error::NoTargetBParentchainAssigned) + }; + }, + } + + Ok(()) +} + +/// Validates the events coming from the parentchain +fn validate_events( + events_proofs: &Vec, + blocks_merkle_roots: &Vec, +) -> Result<()> { + info!( + "Validating events, events_proofs_length: {:?}, blocks_merkle_roots_lengths: {:?}", + events_proofs.len(), + blocks_merkle_roots.len() + ); + + if events_proofs.len() != blocks_merkle_roots.len() { + return Err(Error::ParentChainSync) + } + + let events_key = itp_storage::storage_value_key("System", "Events"); + + let validated_events: Result>> = events_proofs + .iter() + .zip(blocks_merkle_roots.iter()) + .map(|(proof, root)| { + StorageProofChecker::::check_proof( + *root, + events_key.as_slice(), + proof.clone(), + ) + .ok() + .flatten() + .ok_or_else(|| Error::ParentChainValidation(itp_storage::Error::WrongValue)) + }) + .collect(); + + let _ = validated_events?; + + Ok(()) +} + +// This is required, because `ring` / `ring-xous` would not compile without it non-release (debug) mode. +// See #1200 for more details. +#[cfg(debug_assertions)] +#[no_mangle] +pub extern "C" fn __assert_fail( + __assertion: *const u8, + __file: *const u8, + __line: u32, + __function: *const u8, +) -> ! { + use core::intrinsics::abort; + abort() +} diff --git a/bitacross-worker/enclave-runtime/src/ocall/attestation_ocall.rs b/bitacross-worker/enclave-runtime/src/ocall/attestation_ocall.rs new file mode 100644 index 0000000000..3a3abbae9e --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/ocall/attestation_ocall.rs @@ -0,0 +1,275 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::ocall::{ffi, OcallApi}; +use frame_support::ensure; +use itp_ocall_api::EnclaveAttestationOCallApi; +use lazy_static::lazy_static; +use log::*; +use sgx_tse::rsgx_create_report; +use sgx_types::*; +use std::{ptr, sync::Arc, vec::Vec}; + +use std::sync::SgxRwLock as RwLock; + +const RET_QUOTE_BUF_LEN: usize = 2048; + +lazy_static! { + /// Global cache of MRENCLAVE + /// will never change at runtime but must be initialized at runtime + static ref MY_MRENCLAVE: RwLock> = RwLock::new(Default::default()); +} + +#[derive(Default, Copy, Clone, Debug)] +pub struct MrEnclave { + pub maybe_mrenclave: Option, +} + +impl MrEnclave { + pub fn current() -> SgxResult> { + Ok(MY_MRENCLAVE + .read() + .map_err(|e| { + error!("fetching current value of MR_ENCLAVE lazy static failed: {:?}", e); + sgx_status_t::SGX_ERROR_UNEXPECTED + })? + .clone()) + } + pub fn make_current(self) -> SgxResult<()> { + *MY_MRENCLAVE.write().map_err(|e| { + error!("writing current value of MR_ENCLAVE lazy static failed: {:?}", e); + sgx_status_t::SGX_ERROR_UNEXPECTED + })? = Arc::new(self); + Ok(()) + } +} + +impl EnclaveAttestationOCallApi for OcallApi { + fn sgx_init_quote(&self) -> SgxResult<(sgx_target_info_t, sgx_epid_group_id_t)> { + let mut ti: sgx_target_info_t = sgx_target_info_t::default(); + let mut eg: sgx_epid_group_id_t = sgx_epid_group_id_t::default(); + let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED; + + let res = unsafe { + ffi::ocall_sgx_init_quote( + &mut rt as *mut sgx_status_t, + &mut ti as *mut sgx_target_info_t, + &mut eg as *mut sgx_epid_group_id_t, + ) + }; + + ensure!(res == sgx_status_t::SGX_SUCCESS, res); + ensure!(rt == sgx_status_t::SGX_SUCCESS, rt); + + Ok((ti, eg)) + } + + fn get_ias_socket(&self) -> SgxResult { + let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED; + let mut ias_sock: i32 = 0; + + let res = unsafe { + ffi::ocall_get_ias_socket(&mut rt as *mut sgx_status_t, &mut ias_sock as *mut i32) + }; + + ensure!(res == sgx_status_t::SGX_SUCCESS, res); + ensure!(rt == sgx_status_t::SGX_SUCCESS, rt); + + Ok(ias_sock) + } + + fn get_quote( + &self, + sig_rl: Vec, + report: sgx_report_t, + sign_type: sgx_quote_sign_type_t, + spid: sgx_spid_t, + quote_nonce: sgx_quote_nonce_t, + ) -> SgxResult<(sgx_report_t, Vec)> { + let mut qe_report = sgx_report_t::default(); + let mut return_quote_buf = [0u8; RET_QUOTE_BUF_LEN]; + let mut quote_len: u32 = 0; + + let (p_sigrl, sigrl_len) = if sig_rl.is_empty() { + (ptr::null(), 0) + } else { + (sig_rl.as_ptr(), sig_rl.len() as u32) + }; + let p_report = &report as *const sgx_report_t; + let quote_type = sign_type; + + let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED; + let p_spid = &spid as *const sgx_spid_t; + let p_nonce = "e_nonce as *const sgx_quote_nonce_t; + let p_qe_report = &mut qe_report as *mut sgx_report_t; + let p_quote = return_quote_buf.as_mut_ptr(); + let maxlen = RET_QUOTE_BUF_LEN as u32; + let p_quote_len = &mut quote_len as *mut u32; + + let result = unsafe { + ffi::ocall_get_quote( + &mut rt as *mut sgx_status_t, + p_sigrl, + sigrl_len, + p_report, + quote_type, + p_spid, + p_nonce, + p_qe_report, + p_quote, + maxlen, + p_quote_len, + ) + }; + + ensure!(result == sgx_status_t::SGX_SUCCESS, result); + ensure!(rt == sgx_status_t::SGX_SUCCESS, rt); + + #[allow(clippy::indexing_slicing)] + let quote_vec: Vec = Vec::from(&return_quote_buf[..quote_len as usize]); + + Ok((qe_report, quote_vec)) + } + + fn get_dcap_quote(&self, report: sgx_report_t, quote_size: u32) -> SgxResult> { + let mut return_quote_buf = vec![0u8; quote_size as usize]; + let p_quote = return_quote_buf.as_mut_ptr(); + let p_report = &report as *const sgx_report_t; + let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED; + + let result = unsafe { + ffi::ocall_get_dcap_quote(&mut rt as *mut sgx_status_t, p_report, p_quote, quote_size) + }; + ensure!(result == sgx_status_t::SGX_SUCCESS, result); + ensure!(rt == sgx_status_t::SGX_SUCCESS, rt); + #[allow(clippy::indexing_slicing)] + let quote_vec: Vec = Vec::from(&return_quote_buf[..quote_size as usize]); + Ok(quote_vec) + } + + fn get_qve_report_on_quote( + &self, + quote: Vec, + current_time: i64, + quote_collateral: sgx_ql_qve_collateral_t, + qve_report_info: sgx_ql_qe_report_info_t, + supplemental_data_size: u32, + ) -> SgxResult<(u32, sgx_ql_qv_result_t, sgx_ql_qe_report_info_t, Vec)> { + let mut supplemental_data = vec![0u8; supplemental_data_size as usize]; + let mut qve_report_info_return_value: sgx_ql_qe_report_info_t = qve_report_info; + let mut quote_verification_result = sgx_ql_qv_result_t::SGX_QL_QV_RESULT_UNSPECIFIED; + let mut collateral_expiration_status = 1u32; + let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED; + + let result = unsafe { + ffi::ocall_get_qve_report_on_quote( + &mut rt as *mut sgx_status_t, + quote.as_ptr(), + quote.len() as u32, + current_time, + "e_collateral as *const sgx_ql_qve_collateral_t, + &mut collateral_expiration_status as *mut u32, + &mut quote_verification_result as *mut sgx_ql_qv_result_t, + &mut qve_report_info_return_value as *mut sgx_ql_qe_report_info_t, + supplemental_data.as_mut_ptr(), + supplemental_data_size, + ) + }; + ensure!(result == sgx_status_t::SGX_SUCCESS, result); + ensure!(rt == sgx_status_t::SGX_SUCCESS, rt); + + Ok(( + collateral_expiration_status, + quote_verification_result, + qve_report_info_return_value, + supplemental_data.to_vec(), + )) + } + + fn get_update_info( + &self, + platform_info: sgx_platform_info_t, + enclave_trusted: i32, + ) -> SgxResult { + let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED; + let mut update_info = sgx_update_info_bit_t::default(); + + let result = unsafe { + ffi::ocall_get_update_info( + &mut rt as *mut sgx_status_t, + &platform_info as *const sgx_platform_info_t, + enclave_trusted, + &mut update_info as *mut sgx_update_info_bit_t, + ) + }; + + // debug logging + if rt != sgx_status_t::SGX_SUCCESS { + warn!("ocall_get_update_info unsuccessful. rt={:?}", rt); + // Curly braces to copy `unaligned_references` of packed fields into properly aligned temporary: + // https://github.com/rust-lang/rust/issues/82523 + debug!("update_info.pswUpdate: {}", { update_info.pswUpdate }); + debug!("update_info.csmeFwUpdate: {}", { update_info.csmeFwUpdate }); + debug!("update_info.ucodeUpdate: {}", { update_info.ucodeUpdate }); + } + + ensure!(result == sgx_status_t::SGX_SUCCESS, result); + ensure!(rt == sgx_status_t::SGX_SUCCESS, rt); + + Ok(update_info) + } + + fn get_mrenclave_of_self(&self) -> SgxResult { + if let Some(mrenclave) = MrEnclave::current()?.maybe_mrenclave { + trace!("found cached MRENCLAVE"); + return Ok(mrenclave) + }; + debug!("initializing MY_MRENCLAVE cache"); + let mrenclave_value = self.get_report_of_self()?.mr_enclave; + MrEnclave { maybe_mrenclave: Some(mrenclave_value) }.make_current()?; + Ok(mrenclave_value) + } +} + +trait GetSgxReport { + fn get_report_of_self(&self) -> SgxResult; +} + +impl GetSgxReport for T { + fn get_report_of_self(&self) -> SgxResult { + // (1) get ti + eg + let init_quote_result = self.sgx_init_quote()?; + + let target_info = init_quote_result.0; + let report_data: sgx_report_data_t = sgx_report_data_t::default(); + + let rep = match rsgx_create_report(&target_info, &report_data) { + Ok(r) => { + debug!( + " [Enclave] Report creation successful. mr_signer.m = {:?}", + r.body.mr_signer.m + ); + r + }, + Err(e) => { + error!(" [Enclave] Report creation failed. {:?}", e); + return Err(e) + }, + }; + Ok(rep.body) + } +} diff --git a/bitacross-worker/enclave-runtime/src/ocall/ffi.rs b/bitacross-worker/enclave-runtime/src/ocall/ffi.rs new file mode 100644 index 0000000000..388cc0c54a --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/ocall/ffi.rs @@ -0,0 +1,138 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use sgx_types::*; + +extern "C" { + pub fn ocall_sgx_init_quote( + ret_val: *mut sgx_status_t, + ret_ti: *mut sgx_target_info_t, + ret_gid: *mut sgx_epid_group_id_t, + ) -> sgx_status_t; + + pub fn ocall_get_ias_socket(ret_val: *mut sgx_status_t, ret_fd: *mut i32) -> sgx_status_t; + + pub fn ocall_get_quote( + ret_val: *mut sgx_status_t, + p_sigrl: *const u8, + sigrl_len: u32, + p_report: *const sgx_report_t, + quote_type: sgx_quote_sign_type_t, + p_spid: *const sgx_spid_t, + p_nonce: *const sgx_quote_nonce_t, + p_qe_report: *mut sgx_report_t, + p_quote: *mut u8, + maxlen: u32, + p_quote_len: *mut u32, + ) -> sgx_status_t; + + pub fn ocall_get_dcap_quote( + ret_val: *mut sgx_status_t, + p_report: *const sgx_report_t, + p_quote: *mut u8, + quote_size: u32, + ) -> sgx_status_t; + + pub fn ocall_get_qve_report_on_quote( + ret_val: *mut sgx_status_t, + p_quote: *const u8, + quote_len: u32, + current_time: i64, + p_quote_collateral: *const sgx_ql_qve_collateral_t, + p_collateral_expiration_status: *mut u32, + p_quote_verification_result: *mut sgx_ql_qv_result_t, + p_qve_report_info: *mut sgx_ql_qe_report_info_t, + p_supplemental_data: *mut u8, + supplemental_data_size: u32, + ) -> sgx_status_t; + + pub fn ocall_get_update_info( + ret_val: *mut sgx_status_t, + platform_blob: *const sgx_platform_info_t, + enclave_trusted: i32, + update_info: *mut sgx_update_info_bit_t, + ) -> sgx_status_t; + + pub fn ocall_worker_request( + ret_val: *mut sgx_status_t, + request: *const u8, + req_size: u32, + parentchain_id: *const u8, + parentchain_id_size: u32, + response: *mut u8, + resp_size: u32, + ) -> sgx_status_t; + + pub fn ocall_update_metric( + ret_val: *mut sgx_status_t, + metric_ptr: *const u8, + metric_size: u32, + ) -> sgx_status_t; + + pub fn ocall_propose_sidechain_blocks( + ret_val: *mut sgx_status_t, + signed_blocks: *const u8, + signed_blocks_size: u32, + ) -> sgx_status_t; + + pub fn ocall_store_sidechain_blocks( + ret_val: *mut sgx_status_t, + signed_blocks: *const u8, + signed_blocks_size: u32, + ) -> sgx_status_t; + + pub fn ocall_fetch_sidechain_blocks_from_peer( + ret_val: *mut sgx_status_t, + last_imported_block_hash: *const u8, + last_imported_block_hash_size: u32, + maybe_until_block_hash: *const u8, + maybe_until_block_hash_encoded_size: u32, + shard_identifier: *const u8, + shard_identifier_size: u32, + sidechain_blocks: *mut u8, + sidechain_blocks_size: u32, + ) -> sgx_status_t; + + pub fn ocall_get_trusted_peers_urls( + ret_val: *mut sgx_status_t, + peers: *mut u8, + peers_size: u32, + ) -> sgx_status_t; + + pub fn ocall_send_to_parentchain( + ret_val: *mut sgx_status_t, + extrinsics: *const u8, + extrinsics_size: u32, + parentchain_id: *const u8, + parentchain_id_size: u32, + await_each_inclusion: c_int, + ) -> sgx_status_t; + + pub fn ocall_read_ipfs( + ret_val: *mut sgx_status_t, + cid: *const u8, + cid_size: u32, + ) -> sgx_status_t; + + pub fn ocall_write_ipfs( + ret_val: *mut sgx_status_t, + enc_state: *const u8, + enc_state_size: u32, + cid: *mut u8, + cid_size: u32, + ) -> sgx_status_t; +} diff --git a/bitacross-worker/enclave-runtime/src/ocall/ipfs_ocall.rs b/bitacross-worker/enclave-runtime/src/ocall/ipfs_ocall.rs new file mode 100644 index 0000000000..d1a5530856 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/ocall/ipfs_ocall.rs @@ -0,0 +1,57 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::ocall::{ffi, OcallApi}; +use frame_support::ensure; +use itp_ocall_api::{EnclaveIpfsOCallApi, IpfsCid}; +use sgx_types::{sgx_status_t, SgxResult}; + +impl EnclaveIpfsOCallApi for OcallApi { + fn write_ipfs(&self, encoded_state: &[u8]) -> SgxResult { + let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED; + let mut cid_buf = IpfsCid([0u8; 46]); + + let res = unsafe { + ffi::ocall_write_ipfs( + &mut rt as *mut sgx_status_t, + encoded_state.as_ptr(), + encoded_state.len() as u32, + cid_buf.0.as_mut_ptr(), + cid_buf.0.len() as u32, + ) + }; + + ensure!(rt == sgx_status_t::SGX_SUCCESS, rt); + ensure!(res == sgx_status_t::SGX_SUCCESS, res); + + Ok(cid_buf) + } + + fn read_ipfs(&self, cid: &IpfsCid) -> SgxResult<()> { + let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED; + + let res = unsafe { + ffi::ocall_read_ipfs(&mut rt as *mut sgx_status_t, cid.0.as_ptr(), cid.0.len() as u32) + }; + + ensure!(rt == sgx_status_t::SGX_SUCCESS, rt); + ensure!(res == sgx_status_t::SGX_SUCCESS, res); + + Ok(()) + } +} diff --git a/bitacross-worker/enclave-runtime/src/ocall/metrics_ocall.rs b/bitacross-worker/enclave-runtime/src/ocall/metrics_ocall.rs new file mode 100644 index 0000000000..0d12dfd7d6 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/ocall/metrics_ocall.rs @@ -0,0 +1,42 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::ocall::{ffi, OcallApi}; +use codec::Encode; +use frame_support::ensure; +use itp_ocall_api::EnclaveMetricsOCallApi; +use sgx_types::{sgx_status_t, SgxResult}; + +impl EnclaveMetricsOCallApi for OcallApi { + fn update_metric(&self, metric: Metric) -> SgxResult<()> { + let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED; + let metric_encoded = metric.encode(); + + let res = unsafe { + ffi::ocall_update_metric( + &mut rt as *mut sgx_status_t, + metric_encoded.as_ptr(), + metric_encoded.len() as u32, + ) + }; + + ensure!(rt == sgx_status_t::SGX_SUCCESS, rt); + ensure!(res == sgx_status_t::SGX_SUCCESS, res); + + Ok(()) + } +} diff --git a/bitacross-worker/enclave-runtime/src/ocall/mod.rs b/bitacross-worker/enclave-runtime/src/ocall/mod.rs new file mode 100644 index 0000000000..7374b63fde --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/ocall/mod.rs @@ -0,0 +1,26 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +mod attestation_ocall; +mod ffi; +mod ipfs_ocall; +mod metrics_ocall; +mod on_chain_ocall; +mod sidechain_ocall; + +#[derive(Clone, Debug, Default)] +pub struct OcallApi; diff --git a/bitacross-worker/enclave-runtime/src/ocall/on_chain_ocall.rs b/bitacross-worker/enclave-runtime/src/ocall/on_chain_ocall.rs new file mode 100644 index 0000000000..95b9183269 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/ocall/on_chain_ocall.rs @@ -0,0 +1,144 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::ocall::{ffi, OcallApi}; +use codec::{Decode, Encode}; +use frame_support::ensure; +use itc_parentchain::primitives::ParentchainId; +use itp_ocall_api::{EnclaveOnChainOCallApi, Result}; +use itp_storage::{verify_storage_entries, Error as StorageError}; +use itp_types::{storage::StorageEntryVerified, WorkerRequest, WorkerResponse, H256}; +use log::*; +use sgx_types::*; +use sp_runtime::{traits::Header, OpaqueExtrinsic}; +use std::vec::Vec; + +impl EnclaveOnChainOCallApi for OcallApi { + fn send_to_parentchain( + &self, + extrinsics: Vec, + parentchain_id: &ParentchainId, + await_each_inclusion: bool, + ) -> SgxResult<()> { + let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED; + let extrinsics_encoded = extrinsics.encode(); + let parentchain_id_encoded = parentchain_id.encode(); + + let res = unsafe { + ffi::ocall_send_to_parentchain( + &mut rt as *mut sgx_status_t, + extrinsics_encoded.as_ptr(), + extrinsics_encoded.len() as u32, + parentchain_id_encoded.as_ptr(), + parentchain_id_encoded.len() as u32, + await_each_inclusion.into(), + ) + }; + + ensure!(rt == sgx_status_t::SGX_SUCCESS, rt); + ensure!(res == sgx_status_t::SGX_SUCCESS, res); + + Ok(()) + } + + fn worker_request( + &self, + req: Vec, + parentchain_id: &ParentchainId, + ) -> SgxResult>> { + let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED; + // Litentry: since #1221 we need 28139 bytes + let mut resp: Vec = vec![0; 4196 * 16]; + let request_encoded = req.encode(); + let parentchain_id_encoded = parentchain_id.encode(); + + let res = unsafe { + ffi::ocall_worker_request( + &mut rt as *mut sgx_status_t, + request_encoded.as_ptr(), + request_encoded.len() as u32, + parentchain_id_encoded.as_ptr(), + parentchain_id_encoded.len() as u32, + resp.as_mut_ptr(), + resp.len() as u32, + ) + }; + + ensure!(rt == sgx_status_t::SGX_SUCCESS, rt); + ensure!(res == sgx_status_t::SGX_SUCCESS, res); + + let decoded_response: Vec> = Decode::decode(&mut resp.as_slice()) + .map_err(|e| { + error!("Failed to decode WorkerResponse: {}", e); + sgx_status_t::SGX_ERROR_UNEXPECTED + })?; + + Ok(decoded_response) + } + + fn get_storage_verified, V: Decode>( + &self, + storage_hash: Vec, + header: &H, + parentchain_id: &ParentchainId, + ) -> Result> { + // the code below seems like an overkill, but it is surprisingly difficult to + // get an owned value from a `Vec` without cloning. + Ok(self + .get_multiple_storages_verified(vec![storage_hash], header, parentchain_id)? + .into_iter() + .next() + .ok_or(StorageError::StorageValueUnavailable)?) + } + + fn get_multiple_storages_verified, V: Decode>( + &self, + storage_hashes: Vec>, + header: &H, + parentchain_id: &ParentchainId, + ) -> Result>> { + let requests = storage_hashes + .into_iter() + .map(|key| WorkerRequest::ChainStorage(key, Some(header.hash()))) + .collect(); + + let storage_entries = self + .worker_request::>(requests, parentchain_id) + .map(|storages| verify_storage_entries(storages, header))??; + + Ok(storage_entries) + } + + fn get_storage_keys(&self, key_prefix: Vec) -> Result>> { + // always using the latest state - we need to support optional header + let requests = vec![WorkerRequest::ChainStorageKeys(key_prefix, None)]; + + let responses: Vec>> = self + .worker_request::>(requests, &ParentchainId::Litentry)? + .iter() + .filter_map(|r| match r { + WorkerResponse::ChainStorageKeys(k) => Some(k.clone()), + _ => None, + }) + .collect(); + + // we should only have one response as we only sent one request + let first_response = responses.get(0).ok_or(StorageError::WrongValue)?; + Ok(first_response.clone()) + } +} diff --git a/bitacross-worker/enclave-runtime/src/ocall/sidechain_ocall.rs b/bitacross-worker/enclave-runtime/src/ocall/sidechain_ocall.rs new file mode 100644 index 0000000000..b961e93752 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/ocall/sidechain_ocall.rs @@ -0,0 +1,139 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::ocall::{ffi, OcallApi}; +use codec::{Decode, Encode}; +use frame_support::ensure; +use itp_ocall_api::EnclaveSidechainOCallApi; +use itp_types::{BlockHash, ShardIdentifier}; +use log::*; +use sgx_types::{sgx_status_t, SgxResult}; +use std::{string::String, vec::Vec}; + +impl EnclaveSidechainOCallApi for OcallApi { + fn propose_sidechain_blocks( + &self, + signed_blocks: Vec, + ) -> SgxResult<()> { + let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED; + let signed_blocks_encoded = signed_blocks.encode(); + + let res = unsafe { + ffi::ocall_propose_sidechain_blocks( + &mut rt as *mut sgx_status_t, + signed_blocks_encoded.as_ptr(), + signed_blocks_encoded.len() as u32, + ) + }; + + ensure!(rt == sgx_status_t::SGX_SUCCESS, rt); + ensure!(res == sgx_status_t::SGX_SUCCESS, res); + + Ok(()) + } + + fn store_sidechain_blocks( + &self, + signed_blocks: Vec, + ) -> SgxResult<()> { + let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED; + let signed_blocks_encoded = signed_blocks.encode(); + + let res = unsafe { + ffi::ocall_store_sidechain_blocks( + &mut rt as *mut sgx_status_t, + signed_blocks_encoded.as_ptr(), + signed_blocks_encoded.len() as u32, + ) + }; + + ensure!(rt == sgx_status_t::SGX_SUCCESS, rt); + ensure!(res == sgx_status_t::SGX_SUCCESS, res); + + Ok(()) + } + + fn fetch_sidechain_blocks_from_peer( + &self, + last_imported_block_hash: BlockHash, + maybe_until_block_hash: Option, + shard_identifier: ShardIdentifier, + ) -> SgxResult> { + const BLOCK_BUFFER_SIZE: usize = 262144; // Buffer size for sidechain blocks in bytes (256KB). + + let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED; + let last_imported_block_hash_encoded = last_imported_block_hash.encode(); + let maybe_until_block_hash_encoded = maybe_until_block_hash.encode(); + let shard_identifier_encoded = shard_identifier.encode(); + + // We have to pre-allocate the vector and hope it's large enough (see GitHub issue #621). + let mut signed_blocks_encoded: Vec = vec![0; BLOCK_BUFFER_SIZE]; + + let res = unsafe { + ffi::ocall_fetch_sidechain_blocks_from_peer( + &mut rt as *mut sgx_status_t, + last_imported_block_hash_encoded.as_ptr(), + last_imported_block_hash_encoded.len() as u32, + maybe_until_block_hash_encoded.as_ptr(), + maybe_until_block_hash_encoded.len() as u32, + shard_identifier_encoded.as_ptr(), + shard_identifier_encoded.len() as u32, + signed_blocks_encoded.as_mut_ptr(), + signed_blocks_encoded.len() as u32, + ) + }; + + ensure!(rt == sgx_status_t::SGX_SUCCESS, rt); + ensure!(res == sgx_status_t::SGX_SUCCESS, res); + + let decoded_signed_blocks: Vec = + Decode::decode(&mut signed_blocks_encoded.as_slice()).map_err(|e| { + error!("Failed to decode WorkerResponse: {}", e); + sgx_status_t::SGX_ERROR_UNEXPECTED + })?; + + Ok(decoded_signed_blocks) + } + + fn get_trusted_peers_urls(&self) -> SgxResult> { + let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED; + const BLOCK_BUFFER_SIZE: usize = 262144; // Buffer size for sidechain blocks in bytes (256KB). + + // We have to pre-allocate the vector and hope it's large enough (see GitHub issue #621). + let mut peers_encoded: Vec = vec![0; BLOCK_BUFFER_SIZE]; + + let res = unsafe { + ffi::ocall_get_trusted_peers_urls( + &mut rt as *mut sgx_status_t, + peers_encoded.as_mut_ptr(), + peers_encoded.len() as u32, + ) + }; + + ensure!(rt == sgx_status_t::SGX_SUCCESS, rt); + ensure!(res == sgx_status_t::SGX_SUCCESS, res); + + let decoded_peers: Vec = + Decode::decode(&mut peers_encoded.as_slice()).map_err(|e| { + error!("Failed to decode peers list: {}", e); + sgx_status_t::SGX_ERROR_UNEXPECTED + })?; + + Ok(decoded_peers) + } +} diff --git a/bitacross-worker/enclave-runtime/src/rpc/mod.rs b/bitacross-worker/enclave-runtime/src/rpc/mod.rs new file mode 100644 index 0000000000..5b359ab270 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/rpc/mod.rs @@ -0,0 +1,19 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pub mod rpc_response_channel; +pub mod worker_api_direct; diff --git a/bitacross-worker/enclave-runtime/src/rpc/rpc_response_channel.rs b/bitacross-worker/enclave-runtime/src/rpc/rpc_response_channel.rs new file mode 100644 index 0000000000..7a84fde928 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/rpc/rpc_response_channel.rs @@ -0,0 +1,40 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::initialization::global_components::GLOBAL_WEB_SOCKET_SERVER_COMPONENT; +use itc_direct_rpc_server::{response_channel::ResponseChannel, DirectRpcError}; +use itc_tls_websocket_server::{ConnectionToken, WebSocketResponder}; +use itp_component_container::ComponentGetter; +use std::string::String; + +/// RPC response channel. +/// +/// Uses the web-socket server to send an RPC response/update. +/// In case no server is available or running, the response will be discarded. +#[derive(Default)] +pub struct RpcResponseChannel; + +impl ResponseChannel for RpcResponseChannel { + type Error = DirectRpcError; + + fn respond(&self, token: ConnectionToken, message: String) -> Result<(), Self::Error> { + let web_socket_server = GLOBAL_WEB_SOCKET_SERVER_COMPONENT + .get() + .map_err(|e| DirectRpcError::Other(e.into()))?; + web_socket_server.send_message(token, message).map_err(|e| e.into()) + } +} diff --git a/bitacross-worker/enclave-runtime/src/rpc/worker_api_direct.rs b/bitacross-worker/enclave-runtime/src/rpc/worker_api_direct.rs new file mode 100644 index 0000000000..4133bccca6 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/rpc/worker_api_direct.rs @@ -0,0 +1,565 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + attestation::{ + generate_dcap_ra_extrinsic_from_quote_internal, + generate_ias_ra_extrinsic_from_der_cert_internal, + }, + utils::{ + get_stf_enclave_signer_from_solo_or_parachain, + get_validator_accessor_from_integritee_solo_or_parachain, + }, +}; +use codec::Encode; +use core::result::Result; +use ita_sgx_runtime::{Runtime, System}; +use ita_stf::{Getter, TrustedCallSigned}; +use itc_parentchain::light_client::{concurrent_access::ValidatorAccess, ExtrinsicSender}; +use itp_primitives_cache::{GetPrimitives, GLOBAL_PRIMITIVES_CACHE}; +use itp_rpc::RpcReturnValue; +use itp_sgx_crypto::{ + ed25519_derivation::DeriveEd25519, + key_repository::{AccessKey, AccessPubkey}, + ShieldingCryptoDecrypt, ShieldingCryptoEncrypt, +}; +use itp_sgx_externalities::SgxExternalitiesTrait; +use itp_stf_executor::{getter_executor::ExecuteGetter, traits::StfShardVaultQuery}; +use itp_stf_primitives::types::AccountId; +use itp_stf_state_handler::handle_state::HandleState; +use itp_top_pool_author::traits::AuthorApi; +use itp_types::{ + DirectRequestStatus, Index, MrEnclave, RsaRequest, ShardIdentifier, SidechainBlockNumber, H256, +}; +use itp_utils::{if_not_production, FromHexPrefixed, ToHexPrefixed}; +use its_primitives::types::block::SignedBlock; +use its_sidechain::rpc_handler::{ + direct_top_pool_api, direct_top_pool_api::decode_shard_from_base58, import_block_api, +}; +use jsonrpc_core::{serde_json::json, IoHandler, Params, Value}; +use lc_scheduled_enclave::{ScheduledEnclaveUpdater, GLOBAL_SCHEDULED_ENCLAVE}; +use litentry_primitives::DecryptableRequest; +use log::debug; +use sgx_crypto_helper::rsa3072::Rsa3072PubKey; +use sp_core::Pair; +use sp_runtime::OpaqueExtrinsic; +use std::{borrow::ToOwned, format, str, string::String, sync::Arc, vec::Vec}; + +fn compute_hex_encoded_return_error(error_msg: &str) -> String { + RpcReturnValue::from_error_message(error_msg).to_hex() +} + +fn get_all_rpc_methods_string(io_handler: &IoHandler) -> String { + let method_string = io_handler + .iter() + .map(|rp_tuple| rp_tuple.0.to_owned()) + .collect::>() + .join(", "); + + format!("methods: [{}]", method_string) +} + +pub fn public_api_rpc_handler( + top_pool_author: Arc, + getter_executor: Arc, + shielding_key: Arc, + state: Option>, +) -> IoHandler +where + Author: AuthorApi + Send + Sync + 'static, + GetterExecutor: ExecuteGetter + Send + Sync + 'static, + AccessShieldingKey: AccessPubkey + AccessKey + Send + Sync + 'static, + ::KeyType: + ShieldingCryptoDecrypt + ShieldingCryptoEncrypt + DeriveEd25519 + Send + Sync + 'static, + State: HandleState + Send + Sync + 'static, + State::StateT: SgxExternalitiesTrait, +{ + let mut io = direct_top_pool_api::add_top_pool_direct_rpc_methods( + top_pool_author.clone(), + IoHandler::new(), + ); + + let shielding_key_cloned = shielding_key.clone(); + io.add_sync_method("author_getShieldingKey", move |_: Params| { + debug!("worker_api_direct rpc was called: author_getShieldingKey"); + let rsa_pubkey = match shielding_key_cloned.retrieve_pubkey() { + Ok(key) => key, + Err(status) => { + let error_msg: String = format!("Could not get rsa pubkey due to: {}", status); + return Ok(json!(compute_hex_encoded_return_error(error_msg.as_str()))) + }, + }; + + let rsa_pubkey_json = match serde_json::to_string(&rsa_pubkey) { + Ok(k) => k, + Err(x) => { + let error_msg: String = + format!("[Enclave] can't serialize rsa_pubkey {:?} {}", rsa_pubkey, x); + return Ok(json!(compute_hex_encoded_return_error(error_msg.as_str()))) + }, + }; + let json_value = + RpcReturnValue::new(rsa_pubkey_json.encode(), false, DirectRequestStatus::Ok); + Ok(json!(json_value.to_hex())) + }); + + // author_getEnclaveSignerAccount + let rsa_pubkey_name: &str = "author_getEnclaveSignerAccount"; + io.add_sync_method(rsa_pubkey_name, move |_: Params| { + let enclave_signer_public_key = match shielding_key + .retrieve_key() + .and_then(|keypair| keypair.derive_ed25519().map(|keypair| keypair.public().to_hex())) + { + Err(e) => { + let error_msg: String = format!("{:?}", e); + return Ok(json!(compute_hex_encoded_return_error(error_msg.as_str()))) + }, + Ok(public_key) => public_key, + }; + debug!("[Enclave] enclave_signer_public_key: {:?}", enclave_signer_public_key); + + let json_value = RpcReturnValue { + do_watch: false, + value: enclave_signer_public_key.encode(), + status: DirectRequestStatus::Ok, + }; + + Ok(json!(json_value.to_hex())) + }); + + let local_top_pool_author = top_pool_author.clone(); + let local_state = state.clone(); + io.add_sync_method("author_getNextNonce", move |params: Params| { + let local_state = match local_state.clone() { + Some(s) => s, + None => + return Ok(json!(compute_hex_encoded_return_error( + "author_getNextNonce is not avaiable" + ))), + }; + + match params.parse::<(String, String)>() { + Ok((shard_base58, account_hex)) => { + let shard = match decode_shard_from_base58(shard_base58.as_str()) { + Ok(id) => id, + Err(msg) => { + let error_msg: String = + format!("Could not retrieve author_getNextNonce calls due to: {}", msg); + return Ok(json!(compute_hex_encoded_return_error(error_msg.as_str()))) + }, + }; + let account = match AccountId::from_hex(account_hex.as_str()) { + Ok(acc) => acc, + Err(msg) => { + let error_msg: String = format!( + "Could not retrieve author_getNextNonce calls due to: {:?}", + msg + ); + return Ok(json!(compute_hex_encoded_return_error(error_msg.as_str()))) + }, + }; + + match local_state.load_cloned(&shard) { + Ok((mut state, _hash)) => { + let trusted_calls = + local_top_pool_author.get_pending_trusted_calls_for(shard, &account); + let pending_tx_count = trusted_calls.len(); + #[allow(clippy::unwrap_used)] + let pending_tx_count = Index::try_from(pending_tx_count).unwrap(); + let nonce = state.execute_with(|| System::account_nonce(&account)); + let json_value = RpcReturnValue { + do_watch: false, + value: (nonce.saturating_add(pending_tx_count)).encode(), + status: DirectRequestStatus::Ok, + }; + Ok(json!(json_value.to_hex())) + }, + Err(e) => { + let error_msg = format!("load shard failure due to: {:?}", e); + Ok(json!(compute_hex_encoded_return_error(error_msg.as_str()))) + }, + } + }, + Err(e) => { + let error_msg: String = + format!("Could not retrieve author_getNextNonce calls due to: {}", e); + Ok(json!(compute_hex_encoded_return_error(error_msg.as_str()))) + }, + } + }); + + let local_top_pool_author = top_pool_author.clone(); + io.add_sync_method("author_getShardVault", move |_: Params| { + debug!("worker_api_direct rpc was called: author_getShardVault"); + let shard = + local_top_pool_author.list_handled_shards().first().copied().unwrap_or_default(); + if let Ok(stf_enclave_signer) = get_stf_enclave_signer_from_solo_or_parachain() { + if let Ok(vault) = stf_enclave_signer.get_shard_vault(&shard) { + let json_value = + RpcReturnValue::new(vault.encode(), false, DirectRequestStatus::Ok); + Ok(json!(json_value.to_hex())) + } else { + Ok(json!(compute_hex_encoded_return_error("failed to get shard vault").to_hex())) + } + } else { + Ok(json!(compute_hex_encoded_return_error( + "failed to get stf_enclave_signer to get shard vault" + ) + .to_hex())) + } + }); + + io.add_sync_method("author_getShard", move |_: Params| { + debug!("worker_api_direct rpc was called: author_getShard"); + let shard = top_pool_author.list_handled_shards().first().copied().unwrap_or_default(); + let json_value = RpcReturnValue::new(shard.encode(), false, DirectRequestStatus::Ok); + Ok(json!(json_value.to_hex())) + }); + + io.add_sync_method("author_getMuRaUrl", move |_: Params| { + debug!("worker_api_direct rpc was called: author_getMuRaUrl"); + let url = match GLOBAL_PRIMITIVES_CACHE.get_mu_ra_url() { + Ok(url) => url, + Err(status) => { + let error_msg: String = format!("Could not get mu ra url due to: {}", status); + return Ok(json!(compute_hex_encoded_return_error(error_msg.as_str()))) + }, + }; + + let json_value = RpcReturnValue::new(url.encode(), false, DirectRequestStatus::Ok); + Ok(json!(json_value.to_hex())) + }); + + io.add_sync_method("author_getUntrustedUrl", move |_: Params| { + debug!("worker_api_direct rpc was called: author_getUntrustedUrl"); + let url = match GLOBAL_PRIMITIVES_CACHE.get_untrusted_worker_url() { + Ok(url) => url, + Err(status) => { + let error_msg: String = format!("Could not get untrusted url due to: {}", status); + return Ok(json!(compute_hex_encoded_return_error(error_msg.as_str()))) + }, + }; + + let json_value = RpcReturnValue::new(url.encode(), false, DirectRequestStatus::Ok); + Ok(json!(json_value.to_hex())) + }); + + io.add_sync_method("chain_subscribeAllHeads", |_: Params| { + debug!("worker_api_direct rpc was called: chain_subscribeAllHeads"); + let parsed = "world"; + Ok(Value::String(format!("hello, {}", parsed))) + }); + + io.add_sync_method("state_getMetadata", |_: Params| { + debug!("worker_api_direct rpc was called: tate_getMetadata"); + let metadata = Runtime::metadata(); + let json_value = RpcReturnValue::new(metadata.into(), false, DirectRequestStatus::Ok); + Ok(json!(json_value.to_hex())) + }); + + io.add_sync_method("state_getRuntimeVersion", |_: Params| { + debug!("worker_api_direct rpc was called: state_getRuntimeVersion"); + let parsed = "world"; + Ok(Value::String(format!("hello, {}", parsed))) + }); + + io.add_sync_method("state_executeGetter", move |params: Params| { + debug!("worker_api_direct rpc was called: state_executeGetter"); + let json_value = match execute_getter_inner(getter_executor.as_ref(), params) { + Ok(state_getter_value) => RpcReturnValue { + do_watch: false, + value: state_getter_value.encode(), + status: DirectRequestStatus::Ok, + } + .to_hex(), + Err(error) => compute_hex_encoded_return_error(error.as_str()), + }; + Ok(json!(json_value)) + }); + + io.add_sync_method("attesteer_forwardDcapQuote", move |params: Params| { + debug!("worker_api_direct rpc was called: attesteer_forwardDcapQuote"); + let json_value = match forward_dcap_quote_inner(params) { + Ok(val) => RpcReturnValue { + do_watch: false, + value: val.encode(), + status: DirectRequestStatus::Ok, + } + .to_hex(), + Err(error) => compute_hex_encoded_return_error(error.as_str()), + }; + + Ok(json!(json_value)) + }); + + io.add_sync_method("attesteer_forwardIasAttestationReport", move |params: Params| { + debug!("worker_api_direct rpc was called: attesteer_forwardIasAttestationReport"); + let json_value = match attesteer_forward_ias_attestation_report_inner(params) { + Ok(val) => RpcReturnValue { + do_watch: false, + value: val.encode(), + status: DirectRequestStatus::Ok, + } + .to_hex(), + Err(error) => compute_hex_encoded_return_error(error.as_str()), + }; + Ok(json!(json_value)) + }); + + // state_getMrenclave + io.add_sync_method("state_getMrenclave", |_: Params| { + let json_value = match GLOBAL_SCHEDULED_ENCLAVE.get_current_mrenclave() { + Ok(mrenclave) => RpcReturnValue { + do_watch: false, + value: mrenclave.encode(), + status: DirectRequestStatus::Ok, + } + .to_hex(), + Err(error) => { + let error_msg: String = + format!("Could not get current mrenclave due to: {}", error); + compute_hex_encoded_return_error(error_msg.as_str()) + }, + }; + Ok(json!(json_value)) + }); + + if_not_production!({ + // state_updateScheduledEnclave, params: sidechainBlockNumber, hex encoded mrenclave + io.add_sync_method("state_updateScheduledEnclave", move |params: Params| { + match params.parse::<(SidechainBlockNumber, String)>() { + Ok((bn, mrenclave)) => + return match hex::decode(&mrenclave) { + Ok(mrenclave) => { + let mut enclave_to_set: MrEnclave = [0u8; 32]; + if mrenclave.len() != enclave_to_set.len() { + return Ok(json!(compute_hex_encoded_return_error( + "mrenclave len mismatch, expected 32 bytes long" + ))) + } + + enclave_to_set.copy_from_slice(&mrenclave); + return match GLOBAL_SCHEDULED_ENCLAVE.update(bn, enclave_to_set) { + Ok(()) => Ok(json!(RpcReturnValue::new( + vec![], + false, + DirectRequestStatus::Ok + ) + .to_hex())), + Err(e) => { + let error_msg = + format!("Failed to set scheduled mrenclave {:?}", e); + Ok(json!(compute_hex_encoded_return_error(error_msg.as_str()))) + }, + } + }, + Err(e) => { + let error_msg = format!("Failed to decode mrenclave {:?}", e); + Ok(json!(compute_hex_encoded_return_error(error_msg.as_str()))) + }, + }, + Err(_) => Ok(json!(compute_hex_encoded_return_error("parse error"))), + } + }); + + // state_getStorage + io.add_sync_method("state_getStorage", move |params: Params| { + let local_state = match state.clone() { + Some(s) => s, + None => + return Ok(json!(compute_hex_encoded_return_error( + "state_getStorage is not avaiable" + ))), + }; + match params.parse::<(String, String)>() { + Ok((shard_str, key_hash)) => { + let key_hash = if key_hash.starts_with("0x") { + #[allow(clippy::unwrap_used)] + key_hash.strip_prefix("0x").unwrap() + } else { + key_hash.as_str() + }; + let key_hash = match hex::decode(key_hash) { + Ok(key_hash) => key_hash, + Err(_) => + return Ok(json!(compute_hex_encoded_return_error("docode key error"))), + }; + + let shard: ShardIdentifier = match decode_shard_from_base58(shard_str.as_str()) + { + Ok(id) => id, + Err(msg) => { + let error_msg = format!("decode shard failure due to: {}", msg); + return Ok(json!(compute_hex_encoded_return_error(error_msg.as_str()))) + }, + }; + match local_state.load_cloned(&shard) { + Ok((state, _)) => { + // Get storage by key hash + let value = state.get(key_hash.as_slice()).cloned().unwrap_or_default(); + debug!("query storage value:{:?}", &value); + let json_value = + RpcReturnValue::new(value, false, DirectRequestStatus::Ok); + Ok(json!(json_value.to_hex())) + }, + Err(e) => { + let error_msg = format!("load shard failure due to: {:?}", e); + return Ok(json!(compute_hex_encoded_return_error(error_msg.as_str()))) + }, + } + }, + Err(_err) => Ok(json!(compute_hex_encoded_return_error("parse error"))), + } + }); + }); + + // system_health + io.add_sync_method("system_health", |_: Params| { + debug!("worker_api_direct rpc was called: system_health"); + let parsed = "world"; + Ok(Value::String(format!("hello, {}", parsed))) + }); + + io.add_sync_method("system_name", |_: Params| { + debug!("worker_api_direct rpc was called: system_name"); + let parsed = "world"; + Ok(Value::String(format!("hello, {}", parsed))) + }); + + io.add_sync_method("system_version", |_: Params| { + debug!("worker_api_direct rpc was called: system_version"); + let parsed = "world"; + Ok(Value::String(format!("hello, {}", parsed))) + }); + + let rpc_methods_string = get_all_rpc_methods_string(&io); + io.add_sync_method("rpc_methods", move |_: Params| { + debug!("worker_api_direct rpc was called: rpc_methods"); + Ok(Value::String(rpc_methods_string.to_owned())) + }); + + io +} + +// Litentry: TODO - we still use `RsaRequest` for trusted getter, as the result +// in unencrypted, see P-183 +fn execute_getter_inner( + getter_executor: &GE, + params: Params, +) -> Result>, String> { + let hex_encoded_params = params.parse::>().map_err(|e| format!("{:?}", e))?; + + let param = &hex_encoded_params.get(0).ok_or("Could not get first param")?; + let request = RsaRequest::from_hex(param).map_err(|e| format!("{:?}", e))?; + + let shard: ShardIdentifier = request.shard(); + let encoded_trusted_getter: Vec = request.payload().to_vec(); + + let getter_result = getter_executor + .execute_getter(&shard, encoded_trusted_getter) + .map_err(|e| format!("{:?}", e))?; + + Ok(getter_result) +} + +fn forward_dcap_quote_inner(params: Params) -> Result { + let hex_encoded_params = params.parse::>().map_err(|e| format!("{:?}", e))?; + + if hex_encoded_params.len() != 1 { + return Err(format!( + "Wrong number of arguments for IAS attestation report forwarding: {}, expected: {}", + hex_encoded_params.len(), + 1 + )) + } + + let param = &hex_encoded_params.get(0).ok_or("Could not get first param")?; + let encoded_quote_to_forward: Vec = + itp_utils::hex::decode_hex(param).map_err(|e| format!("{:?}", e))?; + + let url = String::new(); + let ext = generate_dcap_ra_extrinsic_from_quote_internal(url, &encoded_quote_to_forward) + .map_err(|e| format!("{:?}", e))?; + + let validator_access = get_validator_accessor_from_integritee_solo_or_parachain() + .map_err(|e| format!("{:?}", e))?; + validator_access + .execute_mut_on_validator(|v| v.send_extrinsics(vec![ext.clone()])) + .map_err(|e| format!("{:?}", e))?; + + Ok(ext) +} + +fn attesteer_forward_ias_attestation_report_inner( + params: Params, +) -> Result { + let hex_encoded_params = params.parse::>().map_err(|e| format!("{:?}", e))?; + + if hex_encoded_params.len() != 1 { + return Err(format!( + "Wrong number of arguments for IAS attestation report forwarding: {}, expected: {}", + hex_encoded_params.len(), + 1 + )) + } + + let param = &hex_encoded_params.get(0).ok_or("Could not get first param")?; + let ias_attestation_report = + itp_utils::hex::decode_hex(param).map_err(|e| format!("{:?}", e))?; + + let url = String::new(); + let ext = generate_ias_ra_extrinsic_from_der_cert_internal(url, &ias_attestation_report) + .map_err(|e| format!("{:?}", e))?; + + let validator_access = get_validator_accessor_from_integritee_solo_or_parachain() + .map_err(|e| format!("{:?}", e))?; + validator_access + .execute_mut_on_validator(|v| v.send_extrinsics(vec![ext.clone()])) + .map_err(|e| format!("{:?}", e))?; + + Ok(ext) +} + +pub fn sidechain_io_handler(import_fn: ImportFn) -> IoHandler +where + ImportFn: Fn(SignedBlock) -> Result<(), Error> + Sync + Send + 'static, + Error: std::fmt::Debug, +{ + let io = IoHandler::new(); + import_block_api::add_import_block_rpc_method(import_fn, io) +} + +#[cfg(feature = "test")] +pub mod tests { + use super::*; + use std::string::ToString; + + pub fn test_given_io_handler_methods_then_retrieve_all_names_as_string() { + let mut io = IoHandler::new(); + let method_names: [&str; 4] = ["method1", "another_method", "fancy_thing", "solve_all"]; + + for method_name in method_names.iter() { + io.add_sync_method(method_name, |_: Params| Ok(Value::String("".to_string()))); + } + + let method_string = get_all_rpc_methods_string(&io); + + for method_name in method_names.iter() { + assert!(method_string.contains(method_name)); + } + } +} diff --git a/bitacross-worker/enclave-runtime/src/shard_vault.rs b/bitacross-worker/enclave-runtime/src/shard_vault.rs new file mode 100644 index 0000000000..50bb362ad1 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/shard_vault.rs @@ -0,0 +1,250 @@ +/* + Copyright 2021 Integritee AG + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +use crate::{ + error::{Error, Result as EnclaveResult}, + initialization::global_components::{ + GLOBAL_OCALL_API_COMPONENT, GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT, + GLOBAL_STATE_HANDLER_COMPONENT, + }, + utils::{ + get_extrinsic_factory_from_integritee_solo_or_parachain, + get_extrinsic_factory_from_target_a_solo_or_parachain, + get_extrinsic_factory_from_target_b_solo_or_parachain, + get_node_metadata_repository_from_integritee_solo_or_parachain, + get_node_metadata_repository_from_target_a_solo_or_parachain, + get_node_metadata_repository_from_target_b_solo_or_parachain, DecodeRaw, + }, +}; +use codec::{Compact, Decode, Encode}; +use itp_component_container::ComponentGetter; +use itp_extrinsics_factory::CreateExtrinsics; +use itp_node_api::{ + api_client::{PairSignature, StaticExtrinsicSigner}, + metadata::{ + pallet_proxy::PROXY_DEPOSIT, + provider::{AccessNodeMetadata, Error as MetadataProviderError}, + }, +}; +use itp_node_api_metadata::pallet_proxy::ProxyCallIndexes; +use itp_nonce_cache::NonceCache; +use itp_ocall_api::EnclaveOnChainOCallApi; +use itp_sgx_crypto::key_repository::AccessKey; +use itp_stf_interface::SHARD_VAULT_KEY; +use itp_stf_state_handler::{handle_state::HandleState, query_shard_state::QueryShardState}; +use itp_types::{ + parentchain::{AccountId, Address, ParentchainId, ProxyType}, + OpaqueCall, ShardIdentifier, +}; +use log::*; +use sgx_types::sgx_status_t; +use sp_core::crypto::{DeriveJunction, Pair}; +use std::{slice, sync::Arc, vec::Vec}; + +#[no_mangle] +pub unsafe extern "C" fn init_proxied_shard_vault( + shard: *const u8, + shard_size: u32, + parentchain_id: *const u8, + parentchain_id_size: u32, +) -> sgx_status_t { + let shard_identifier = + ShardIdentifier::from_slice(slice::from_raw_parts(shard, shard_size as usize)); + let parentchain_id = + match ParentchainId::decode_raw(parentchain_id, parentchain_id_size as usize) { + Ok(id) => id, + Err(e) => { + error!("Could not decode parentchain id: {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + if let Err(e) = init_proxied_shard_vault_internal(shard_identifier, parentchain_id) { + error!("Failed to initialize proxied shard vault ({:?}): {:?}", shard_identifier, e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + } + + sgx_status_t::SGX_SUCCESS +} + +/// reads the shard vault account id form state if it has been initialized previously +#[no_mangle] +pub unsafe extern "C" fn get_ecc_vault_pubkey( + shard: *const u8, + shard_size: u32, + pubkey: *mut u8, + pubkey_size: u32, +) -> sgx_status_t { + let shard = ShardIdentifier::from_slice(slice::from_raw_parts(shard, shard_size as usize)); + + let shard_vault = match get_shard_vault_account(shard) { + Ok(account) => account, + Err(e) => { + warn!("Failed to fetch shard vault account: {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + let pubkey_slice = slice::from_raw_parts_mut(pubkey, pubkey_size as usize); + pubkey_slice.clone_from_slice(shard_vault.encode().as_slice()); + sgx_status_t::SGX_SUCCESS +} + +/// reads the shard vault account id form state if it has been initialized previously +pub(crate) fn get_shard_vault_account(shard: ShardIdentifier) -> EnclaveResult { + let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; + + state_handler + .execute_on_current(&shard, |state, _| { + state + .state + .get::>(&SHARD_VAULT_KEY.into()) + .and_then(|v| Decode::decode(&mut v.clone().as_slice()).ok()) + })? + .ok_or_else(|| { + Error::Other("failed to fetch shard vault account. has it been initialized?".into()) + }) +} + +pub(crate) fn init_proxied_shard_vault_internal( + shard: ShardIdentifier, + parentchain_id: ParentchainId, +) -> EnclaveResult<()> { + let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; + if !state_handler + .shard_exists(&shard) + .map_err(|_| Error::Other("get shard_exists failed".into()))? + { + return Err(Error::Other("shard not initialized".into())) + }; + + let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; + let enclave_signer = GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT.get()?.retrieve_key()?; + let vault = enclave_signer + .derive(vec![DeriveJunction::hard(shard.encode())].into_iter(), None) + .map_err(|_| Error::Other("failed to derive shard vault keypair".into()))? + .0; + info!("shard vault account derived pubkey: 0x{}", hex::encode(vault.public().0)); + + let (enclave_extrinsics_factory, node_metadata_repo) = match parentchain_id { + ParentchainId::Litentry => { + let (state_lock, mut state) = state_handler.load_for_mutation(&shard)?; + state.state.insert(SHARD_VAULT_KEY.into(), vault.public().0.to_vec()); + state_handler.write_after_mutation(state, state_lock, &shard)?; + let enclave_extrinsics_factory = + get_extrinsic_factory_from_integritee_solo_or_parachain()?; + let node_metadata_repo = + get_node_metadata_repository_from_integritee_solo_or_parachain()?; + (enclave_extrinsics_factory, node_metadata_repo) + }, + ParentchainId::TargetA => { + let enclave_extrinsics_factory = + get_extrinsic_factory_from_target_a_solo_or_parachain()?; + let node_metadata_repo = + get_node_metadata_repository_from_target_a_solo_or_parachain()?; + (enclave_extrinsics_factory, node_metadata_repo) + }, + ParentchainId::TargetB => { + let enclave_extrinsics_factory = + get_extrinsic_factory_from_target_b_solo_or_parachain()?; + let node_metadata_repo = + get_node_metadata_repository_from_target_b_solo_or_parachain()?; + (enclave_extrinsics_factory, node_metadata_repo) + }, + }; + + info!("[{:?}] send existential funds from enclave account to vault account", parentchain_id); + let call_ids = node_metadata_repo + .get_from_metadata(|m| m.call_indexes("Balances", "transfer_keep_alive"))? + .map_err(MetadataProviderError::MetadataError)?; + + let call = OpaqueCall::from_tuple(&( + call_ids, + Address::from(AccountId::from(vault.public().0)), + Compact(PROXY_DEPOSIT), + )); + + info!("[{:?}] vault funding call: 0x{}", parentchain_id, hex::encode(call.0.clone())); + let xts = enclave_extrinsics_factory.create_extrinsics(&[call], None)?; + + //this extrinsic must be included in a block before we can move on. otherwise the next will fail + ocall_api.send_to_parentchain(xts, &parentchain_id, true)?; + + // we are assuming nonce=0 here. + let nonce_cache = Arc::new(NonceCache::default()); + let vault_extrinsics_factory = enclave_extrinsics_factory + .with_signer(StaticExtrinsicSigner::<_, PairSignature>::new(vault), nonce_cache); + + info!("[{:?}] register enclave signer as proxy for shard vault", parentchain_id); + let call_ids = node_metadata_repo + .get_from_metadata(|m| m.call_indexes("Proxy", "add_proxy"))? + .map_err(MetadataProviderError::MetadataError)?; + + let call = OpaqueCall::from_tuple(&( + call_ids, + Address::from(AccountId::from(enclave_signer.public().0)), + ProxyType::Any, + 0u32, // delay + )); + + info!("[{:?}] add proxy call: 0x{}", parentchain_id, hex::encode(call.0.clone())); + let xts = vault_extrinsics_factory.create_extrinsics(&[call], None)?; + + ocall_api.send_to_parentchain(xts, &parentchain_id, false)?; + Ok(()) +} + +pub(crate) fn add_shard_vault_proxy( + shard: ShardIdentifier, + proxy: &AccountId, +) -> EnclaveResult<()> { + let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; + if !state_handler + .shard_exists(&shard) + .map_err(|_| Error::Other("get shard_exists failed".into()))? + { + return Err(Error::Other("shard not initialized".into())) + }; + + let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; + let enclave_extrinsics_factory = get_extrinsic_factory_from_integritee_solo_or_parachain()?; + let node_metadata_repo = get_node_metadata_repository_from_integritee_solo_or_parachain()?; + let vault = get_shard_vault_account(shard)?; + + debug!( + "adding proxy 0x{} to shard vault account 0x{}", + hex::encode(proxy.clone()), + hex::encode(vault.clone()) + ); + + let add_proxy_call = OpaqueCall::from_tuple(&( + node_metadata_repo.get_from_metadata(|m| m.add_proxy_call_indexes())??, + Address::from(proxy.clone()), + ProxyType::Any, + 0u32, // delay + )); + let call = OpaqueCall::from_tuple(&( + node_metadata_repo.get_from_metadata(|m| m.proxy_call_indexes())??, + Address::from(vault), + None::, + add_proxy_call, + )); + + info!("proxied add proxy call: 0x{}", hex::encode(call.0.clone())); + let xts = enclave_extrinsics_factory.create_extrinsics(&[call], None)?; + + ocall_api.send_to_parentchain(xts, &ParentchainId::Litentry, false)?; + Ok(()) +} diff --git a/bitacross-worker/enclave-runtime/src/stf_task_handler.rs b/bitacross-worker/enclave-runtime/src/stf_task_handler.rs new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/stf_task_handler.rs @@ -0,0 +1 @@ + diff --git a/bitacross-worker/enclave-runtime/src/sync.rs b/bitacross-worker/enclave-runtime/src/sync.rs new file mode 100644 index 0000000000..a348134d6f --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/sync.rs @@ -0,0 +1,104 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +//! Primitives to handle multithreaded state access in the enclave. +//! +//! Note: In general the design should try to minimize usage of these, as potential deadlocks can +//! occur. Documentation of the `SgxRwLock` says that panics __might__ occur when trying to acquire +//! a lock multiple times in the same thread. However, tests have shown that it also might result in +//! a deadlock. +//! +//! @clangenb: Does currently not see any way to entirely get rid of these synchronization +//! primitives because we can only start new threads from the untrusted side. `parking_lot` would be +//! an alternative to consider for the primitives. It has several performance and ergonomic benefits +//! over the `std` lib's. One of the benefits would be compile-time deadlock detection (experimental). +//! Unfortunately, it would need to be ported to SGX. +//! +//! `https://amanieu.github.io/parking_lot/parking_lot/index.html` + +use crate::error::{Error, Result as EnclaveResult}; +use lazy_static::lazy_static; +use std::sync::{SgxRwLock, SgxRwLockReadGuard, SgxRwLockWriteGuard}; + +lazy_static! { + pub static ref SIDECHAIN_DB_LOCK: SgxRwLock<()> = Default::default(); +} + +pub struct EnclaveLock; + +impl SidechainRwLock for EnclaveLock { + fn read_sidechain_db() -> EnclaveResult> { + SIDECHAIN_DB_LOCK.read().map_err(|e| Error::Other(e.into())) + } + + fn write_sidechain_db() -> EnclaveResult> { + SIDECHAIN_DB_LOCK.write().map_err(|e| Error::Other(e.into())) + } +} + +pub trait SidechainRwLock { + fn read_sidechain_db() -> EnclaveResult>; + fn write_sidechain_db() -> EnclaveResult>; +} + +// simple type defs to prevent too long names +type AggregatedReadGuards<'a> = SgxRwLockReadGuard<'a, ()>; +type AggregatedWriteGuards<'a> = SgxRwLockWriteGuard<'a, ()>; + +/// Useful, if all state must be accessed. Reduces the number of lines. +pub trait EnclaveStateRWLock: SidechainRwLock { + /// return read locks of all enclave states + fn read_all() -> EnclaveResult>; + + /// return write locks of all enclave states + fn write_all() -> EnclaveResult>; +} + +impl EnclaveStateRWLock for T { + fn read_all() -> EnclaveResult> { + Self::read_sidechain_db() + } + + fn write_all() -> EnclaveResult> { + Self::write_sidechain_db() + } +} + +#[cfg(feature = "test")] +pub mod tests { + use super::*; + pub fn sidechain_rw_lock_works() { + drop(EnclaveLock::read_sidechain_db().unwrap()); + drop(EnclaveLock::write_sidechain_db().unwrap()); + + let x1 = EnclaveLock::read_sidechain_db().unwrap(); + let x2 = EnclaveLock::read_sidechain_db().unwrap(); + + drop((x1, x2)); + drop(EnclaveLock::write_sidechain_db().unwrap()) + } + + pub fn enclave_rw_lock_works() { + drop(EnclaveLock::read_all().unwrap()); + drop(EnclaveLock::write_all().unwrap()); + + let x1 = EnclaveLock::read_all().unwrap(); + let x2 = EnclaveLock::read_all().unwrap(); + + drop((x1, x2)); + drop(EnclaveLock::write_all().unwrap()) + } +} diff --git a/bitacross-worker/enclave-runtime/src/teeracle/mod.rs b/bitacross-worker/enclave-runtime/src/teeracle/mod.rs new file mode 100644 index 0000000000..c38dd27c2e --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/teeracle/mod.rs @@ -0,0 +1,279 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + error::{Error, Result}, + initialization::global_components::GLOBAL_OCALL_API_COMPONENT, + utils::{ + get_extrinsic_factory_from_integritee_solo_or_parachain, + get_node_metadata_repository_from_integritee_solo_or_parachain, + }, +}; +use codec::{Decode, Encode}; +use core::slice; +use ita_oracle::{ + create_coin_gecko_oracle, create_coin_market_cap_oracle, create_open_meteo_weather_oracle, + metrics_exporter::ExportMetrics, + oracles::{ + exchange_rate_oracle::{ExchangeRateOracle, GetExchangeRate}, + weather_oracle::{GetLongitude, WeatherOracle}, + }, + traits::OracleSource, + types::{TradingInfo, TradingPair, WeatherInfo, WeatherQuery}, +}; +use itp_component_container::ComponentGetter; +use itp_extrinsics_factory::CreateExtrinsics; +use itp_node_api::metadata::{pallet_teeracle::TeeracleCallIndexes, provider::AccessNodeMetadata}; +use itp_types::OpaqueCall; +use itp_utils::write_slice_and_whitespace_pad; +use log::*; +use sgx_types::sgx_status_t; +use sp_runtime::OpaqueExtrinsic; +use std::{string::String, vec::Vec}; + +fn update_weather_data_internal(weather_info: WeatherInfo) -> Result> { + let extrinsics_factory = get_extrinsic_factory_from_integritee_solo_or_parachain()?; + let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; + + let mut extrinsic_calls: Vec = Vec::new(); + + let open_meteo_weather_oracle = create_open_meteo_weather_oracle(ocall_api); + + match get_longitude(weather_info, open_meteo_weather_oracle) { + Ok(opaque_call) => extrinsic_calls.push(opaque_call), + Err(e) => { + error!("[-] Failed to get the newest longitude from OpenMeteo. {:?}", e); + }, + }; + let extrinsics = extrinsics_factory.create_extrinsics(extrinsic_calls.as_slice(), None)?; + Ok(extrinsics) +} + +fn get_longitude( + weather_info: WeatherInfo, + oracle: WeatherOracle, +) -> Result +where + OracleSourceType: OracleSource< + WeatherInfo, + OracleRequestResult = std::result::Result, + >, + MetricsExporter: ExportMetrics, +{ + let longitude = + oracle.get_longitude(weather_info.clone()).map_err(|e| Error::Other(e.into()))?; + + let base_url = oracle.get_base_url().map_err(|e| Error::Other(e.into()))?; + let source_base_url = base_url.as_str(); + + println!("Update the longitude: {}, for source {}", longitude, source_base_url); + + let node_metadata_repository = + get_node_metadata_repository_from_integritee_solo_or_parachain()?; + + let call_ids = node_metadata_repository + .get_from_metadata(|m| m.update_oracle_call_indexes()) + .map_err(Error::NodeMetadataProvider)? + .map_err(|e| Error::Other(format!("{:?}", e).into()))?; + + let call = OpaqueCall::from_tuple(&( + call_ids, + weather_info.weather_query.key().as_bytes().to_vec(), + source_base_url.as_bytes().to_vec(), + longitude.encode(), + )); + + Ok(call) +} + +#[no_mangle] +pub unsafe extern "C" fn update_weather_data_xt( + weather_info_longitude: *const u8, + weather_info_longitude_size: u32, + weather_info_latitude: *const u8, + weather_info_latitude_size: u32, + unchecked_extrinsic: *mut u8, + unchecked_extrinsic_max_size: u32, + unchecked_extrinsic_size: *mut u32, +) -> sgx_status_t { + let mut weather_info_longitude_slice = + slice::from_raw_parts(weather_info_longitude, weather_info_longitude_size as usize); + let longitude = match String::decode(&mut weather_info_longitude_slice) { + Ok(val) => val, + Err(e) => { + error!("Could not decode longitude: {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + let mut weather_info_latitude_slice = + slice::from_raw_parts(weather_info_latitude, weather_info_latitude_size as usize); + let latitude = match String::decode(&mut weather_info_latitude_slice) { + Ok(val) => val, + Err(e) => { + error!("Could not decode latitude: {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + let weather_query = WeatherQuery { longitude, latitude, hourly: " ".into() }; + let weather_info = WeatherInfo { weather_query }; + + let extrinsics = match update_weather_data_internal(weather_info) { + Ok(xts) => xts, + Err(e) => { + error!("Updating weather info failed: {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + let extrinsic_slice = + slice::from_raw_parts_mut(unchecked_extrinsic, unchecked_extrinsic_max_size as usize); + + // Save created extrinsic as slice in the return value unchecked_extrinsic. + *unchecked_extrinsic_size = + match write_slice_and_whitespace_pad(extrinsic_slice, extrinsics.encode()) { + Ok(l) => l as u32, + Err(e) => { + error!("Copying encoded extrinsics into return slice failed: {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + sgx_status_t::SGX_SUCCESS +} + +/// For now get the crypto/fiat currency exchange rate from coingecko and CoinMarketCap. +#[no_mangle] +pub unsafe extern "C" fn update_market_data_xt( + crypto_currency_ptr: *const u8, + crypto_currency_size: u32, + fiat_currency_ptr: *const u8, + fiat_currency_size: u32, + unchecked_extrinsic: *mut u8, + unchecked_extrinsic_max_size: u32, + unchecked_extrinsic_size: *mut u32, +) -> sgx_status_t { + let mut crypto_currency_slice = + slice::from_raw_parts(crypto_currency_ptr, crypto_currency_size as usize); + #[allow(clippy::unwrap_used)] + let crypto_currency: String = Decode::decode(&mut crypto_currency_slice).unwrap(); + + let mut fiat_currency_slice = + slice::from_raw_parts(fiat_currency_ptr, fiat_currency_size as usize); + #[allow(clippy::unwrap_used)] + let fiat_currency: String = Decode::decode(&mut fiat_currency_slice).unwrap(); + + let extrinsics = match update_market_data_internal(crypto_currency, fiat_currency) { + Ok(xts) => xts, + Err(e) => { + error!("Update market data failed: {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + if extrinsics.is_empty() { + error!("Updating market data yielded no extrinsics"); + return sgx_status_t::SGX_ERROR_UNEXPECTED + } + let extrinsic_slice = + slice::from_raw_parts_mut(unchecked_extrinsic, unchecked_extrinsic_max_size as usize); + + // Save created extrinsic as slice in the return value unchecked_extrinsic. + *unchecked_extrinsic_size = + match write_slice_and_whitespace_pad(extrinsic_slice, extrinsics.encode()) { + Ok(l) => l as u32, + Err(e) => { + error!("Copying encoded extrinsics into return slice failed: {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + sgx_status_t::SGX_SUCCESS +} + +fn update_market_data_internal( + crypto_currency: String, + fiat_currency: String, +) -> Result> { + let extrinsics_factory = get_extrinsic_factory_from_integritee_solo_or_parachain()?; + let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; + + let mut extrinsic_calls: Vec = Vec::new(); + + // Get the exchange rate + let trading_pair = TradingPair { crypto_currency, fiat_currency }; + + let coin_gecko_oracle = create_coin_gecko_oracle(ocall_api.clone()); + + match get_exchange_rate(trading_pair.clone(), coin_gecko_oracle) { + Ok(opaque_call) => extrinsic_calls.push(opaque_call), + Err(e) => { + error!("[-] Failed to get the newest exchange rate from CoinGecko. {:?}", e); + }, + }; + + let coin_market_cap_oracle = create_coin_market_cap_oracle(ocall_api); + match get_exchange_rate(trading_pair, coin_market_cap_oracle) { + Ok(oc) => extrinsic_calls.push(oc), + Err(e) => { + error!("[-] Failed to get the newest exchange rate from CoinMarketCap. {:?}", e); + }, + }; + + let extrinsics = extrinsics_factory.create_extrinsics(extrinsic_calls.as_slice(), None)?; + Ok(extrinsics) +} + +fn get_exchange_rate( + trading_pair: TradingPair, + oracle: ExchangeRateOracle, +) -> Result +where + OracleSourceType: OracleSource, + MetricsExporter: ExportMetrics, +{ + let (rate, base_url) = oracle + .get_exchange_rate(trading_pair.clone()) + .map_err(|e| Error::Other(e.into()))?; + + let source_base_url = base_url.as_str(); + + println!( + "Update the exchange rate: {} = {:?} for source {}", + trading_pair.clone().key(), + rate, + source_base_url, + ); + + let node_metadata_repository = + get_node_metadata_repository_from_integritee_solo_or_parachain()?; + + let call_ids = node_metadata_repository + .get_from_metadata(|m| m.update_exchange_rate_call_indexes()) + .map_err(Error::NodeMetadataProvider)? + .map_err(|e| Error::Other(format!("{:?}", e).into()))?; + + let call = OpaqueCall::from_tuple(&( + call_ids, + source_base_url.as_bytes().to_vec(), + trading_pair.key().as_bytes().to_vec(), + Some(rate), + )); + + Ok(call) +} diff --git a/bitacross-worker/enclave-runtime/src/test/Counter.sol b/bitacross-worker/enclave-runtime/src/test/Counter.sol new file mode 100644 index 0000000000..ce3cce3259 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/test/Counter.sol @@ -0,0 +1,31 @@ +pragma solidity >=0.8.0; + +contract Counter { + uint256 private value; + address private last_caller; + + constructor() { + value = 1; + last_caller = msg.sender; + } + + fallback() external payable { value = 5; } + + function inc() public { + value += 1; + last_caller = msg.sender; + } + + function add(uint delta) public { + value += delta; + last_caller = msg.sender; + } + + function get_value() view public returns (uint) { + return value; + } + + function get_last_caller() view public returns (address) { + return last_caller; + } +} \ No newline at end of file diff --git a/bitacross-worker/enclave-runtime/src/test/cert_tests.rs b/bitacross-worker/enclave-runtime/src/test/cert_tests.rs new file mode 100644 index 0000000000..ad3b78df76 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/test/cert_tests.rs @@ -0,0 +1,72 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +use crate::test::mocks::attestation_ocall_mock::AttestationOCallMock; +use hex::FromHexError; +use itp_attestation_handler::cert::{verify_attn_report, verify_mra_cert}; +use sgx_types::{sgx_measurement_t, sgx_status_t, SGX_HASH_SIZE}; +use std::vec::Vec; + +// Test data and tests are mostly copied from: +// https://github.com/integritee-network/pallet-teerex/blob/master/ias-verify/ + +const TEST4_CERT: &[u8] = include_bytes!("fixtures/ra_dump_cert_TEST4.der"); + +const TEST4_MRENCLAVE: &str = "7a3454ec8f42e265cb5be7dfd111e1d95ac6076ed82a0948b2e2a45cf17b62a0"; + +#[allow(clippy::octal_escapes)] +const CERT_WRONG_PLATFORM_BLOB: &[u8] = b"0\x82\x0c\x8c0\x82\x0c2\xa0\x03\x02\x01\x02\x02\x01\x010\n\x06\x08*\x86H\xce=\x04\x03\x020\x121\x100\x0e\x06\x03U\x04\x03\x0c\x07MesaTEE0\x1e\x17\r190617124609Z\x17\r190915124609Z0\x121\x100\x0e\x06\x03U\x04\x03\x0c\x07MesaTEE0Y0\x13\x06\x07*\x86H\xce=\x02\x01\x06\x08*\x86H\xce=\x03\x01\x07\x03B\0\x04RT\x16\x16 \xef_\xd8\xe7\xc3\xb7\x03\x1d\xd6:\x1fF\xe3\xf2b!\xa9/\x8b\xd4\x82\x8f\xd1\xff[\x9c\x97\xbc\xf27\xb8,L\x8a\x01\xb0r;;\xa9\x83\xdc\x86\x9f\x1d%y\xf4;I\xe4Y\xc80'$K[\xd6\xa3\x82\x0bw0\x82\x0bs0\x82\x0bo\x06\t`\x86H\x01\x86\xf8B\x01\r\x04\x82\x0b`{\"id\":\"117077750682263877593646412006783680848\",\"timestamp\":\"2019-06-17T12:46:04.002066\",\"version\":3,\"isvEnclaveQuoteStatus\":\"GROUP_OUT_OF_DATE\",\"platformInfoBlob\":\"1602006504000900000909020401800000000000000000000008000009000000020000000000000B401A355B313FC939B4F48A54349C914A32A3AE2C4871BFABF22E960C55635869FC66293A3D9B2D58ED96CA620B65D669A444C80291314EF691E896F664317CF80C\",\"isvEnclaveQuoteBody\":\"AgAAAEALAAAIAAcAAAAAAOE6wgoHKsZsnVWSrsWX9kky0kWt9K4xcan0fQ996Ct+CAj//wGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAAAAAAAAAHAAAAAAAAAFJJYIbPVot9NzRCjW2z9+k+9K8BsHQKzVMEHOR14hNbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD1xnnferKFHD2uvYqTXdDA8iZ22kCD5xw7h38CMfOngAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSVBYWIO9f2OfDtwMd1jofRuPyYiGpL4vUgo/R/1ucl7zyN7gsTIoBsHI7O6mD3IafHSV59DtJ5FnIMCckS1vW\"}|EbPFH/ThUaS/dMZoDKC5EgmdUXUORFtQzF49Umi1P55oeESreJaUvmA0sg/ATSTn5t2e+e6ZoBQIUbLHjcWLMLzK4pJJUeHhok7EfVgoQ378i+eGR9v7ICNDGX7a1rroOe0s1OKxwo/0hid2KWvtAUBvf1BDkqlHy025IOiXWhXFLkb/qQwUZDWzrV4dooMfX5hfqJPi1q9s18SsdLPmhrGBheh9keazeCR9hiLhRO9TbnVgR9zJk43SPXW+pHkbNigW+2STpVAi5ugWaSwBOdK11ZjaEU1paVIpxQnlW1D6dj1Zc3LibMH+ly9ZGrbYtuJks4eRnjPhroPXxlJWpQ==|MIIEoTCCAwmgAwIBAgIJANEHdl0yo7CWMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEUMBIGA1UEBwwLU2FudGEgQ2xhcmExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0aW9uMTAwLgYDVQQDDCdJbnRlbCBTR1ggQXR0ZXN0YXRpb24gUmVwb3J0IFNpZ25pbmcgQ0EwHhcNMTYxMTIyMDkzNjU4WhcNMjYxMTIwMDkzNjU4WjB7MQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExFDASBgNVBAcMC1NhbnRhIENsYXJhMRowGAYDVQQKDBFJbnRlbCBDb3Jwb3JhdGlvbjEtMCsGA1UEAwwkSW50ZWwgU0dYIEF0dGVzdGF0aW9uIFJlcG9ydCBTaWduaW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqXot4OZuphR8nudFrAFiaGxxkgma/Es/BA+tbeCTUR106AL1ENcWA4FX3K+E9BBL0/7X5rj5nIgX/R/1ubhkKWw9gfqPG3KeAtIdcv/uTO1yXv50vqaPvE1CRChvzdS/ZEBqQ5oVvLTPZ3VEicQjlytKgN9cLnxbwtuvLUK7eyRPfJW/ksddOzP8VBBniolYnRCD2jrMRZ8nBM2ZWYwnXnwYeOAHV+W9tOhAImwRwKF/95yAsVwd21ryHMJBcGH70qLagZ7Ttyt++qO/6+KAXJuKwZqjRlEtSEz8gZQeFfVYgcwSfo96oSMAzVr7V0L6HSDLRnpb6xxmbPdqNol4tQIDAQABo4GkMIGhMB8GA1UdIwQYMBaAFHhDe3amfrzQr35CN+s1fDuHAVE8MA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMGAGA1UdHwRZMFcwVaBToFGGT2h0dHA6Ly90cnVzdGVkc2VydmljZXMuaW50ZWwuY29tL2NvbnRlbnQvQ1JML1NHWC9BdHRlc3RhdGlvblJlcG9ydFNpZ25pbmdDQS5jcmwwDQYJKoZIhvcNAQELBQADggGBAGcIthtcK9IVRz4rRq+ZKE+7k50/OxUsmW8aavOzKb0iCx07YQ9rzi5nU73tME2yGRLzhSViFs/LpFa9lpQL6JL1aQwmDR74TxYGBAIi5f4I5TJoCCEqRHz91kpG6Uvyn2tLmnIdJbPE4vYvWLrtXXfFBSSPD4Afn7+3/XUggAlc7oCTizOfbbtOFlYA4g5KcYgS1J2ZAeMQqbUdZseZCcaZZZn65tdqee8UXZlDvx0+NdO0LR+5pFy+juM0wWbu59MvzcmTXbjsi7HY6zd53Yq5K244fwFHRQ8eOB0IWB+4PfM7FeAApZvlfqlKOlLcZL2uyVmzRkyR5yW72uo9mehX44CiPJ2fse9Y6eQtcfEhMPkmHXI01sN+KwPbpA39+xOsStjhP9N1Y1a2tQAVo+yVgLgV2Hws73Fc0o3wC78qPEA+v2aRs/Be3ZFDgDyghc/1fgU+7C+P6kbqd4poyb6IW8KCJbxfMJvkordNOgOUUxndPHEi/tb/U7uLjLOgPA==0\n\x06\x08*\x86H\xce=\x04\x03\x02\x03H\00E\x02!\0\xae6\x06\t@Sy\x8f\x8ec\x9d\xdci^Ex*\x92}\xdcG\x15A\x97\xd7\xd7\xd1\xccx\xe0\x1e\x08\x02 \x15Q\xa0BT\xde'~\xec\xbd\x027\xd3\xd8\x83\xf7\xe6Z\xc5H\xb4D\xf7\xe2\r\xa7\xe4^f\x10\x85p"; + +pub fn test_verify_mra_cert_should_work() { + let mr_enclave = get_mr_enclave_from_hex_string(TEST4_MRENCLAVE).unwrap(); + let attestation_ocall = + AttestationOCallMock::create_with_mr_enclave(sgx_measurement_t { m: mr_enclave }); + let result = verify_mra_cert(TEST4_CERT, false, false, &attestation_ocall); + + assert!(result.is_ok()); +} + +pub fn test_verify_wrong_cert_is_err() { + let mr_enclave = get_mr_enclave_from_hex_string(TEST4_MRENCLAVE).unwrap(); + let attestation_ocall = + AttestationOCallMock::create_with_mr_enclave(sgx_measurement_t { m: mr_enclave }); + let result = verify_mra_cert(CERT_WRONG_PLATFORM_BLOB, false, false, &attestation_ocall); + + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), sgx_status_t::SGX_ERROR_UNEXPECTED); +} + +pub fn test_given_wrong_platform_info_when_verifying_attestation_report_then_return_error() { + let attestation_ocall = AttestationOCallMock::new(); + let result = verify_attn_report(CERT_WRONG_PLATFORM_BLOB, Vec::new(), &attestation_ocall); + + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), sgx_status_t::SGX_ERROR_UNEXPECTED); +} + +fn get_mr_enclave_from_hex_string(input_str: &str) -> Result<[u8; SGX_HASH_SIZE], FromHexError> { + let decoded_str = hex::decode(input_str)?; + + if decoded_str.len() != SGX_HASH_SIZE { + return Err(FromHexError::InvalidStringLength) + } + + let mut mr_enclave = [0u8; SGX_HASH_SIZE]; + mr_enclave.clone_from_slice(decoded_str.as_slice()); + + Ok(mr_enclave) +} diff --git a/bitacross-worker/enclave-runtime/src/test/direct_rpc_tests.rs b/bitacross-worker/enclave-runtime/src/test/direct_rpc_tests.rs new file mode 100644 index 0000000000..bcb7ba5d45 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/test/direct_rpc_tests.rs @@ -0,0 +1,91 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{rpc::worker_api_direct::public_api_rpc_handler, Hash}; +use codec::{Decode, Encode}; +use ita_stf::{Getter, PublicGetter}; +use itc_direct_rpc_server::{ + create_determine_watch, rpc_connection_registry::ConnectionRegistry, + rpc_ws_handler::RpcWsHandler, +}; +use itc_tls_websocket_server::{ConnectionToken, WebSocketMessageHandler}; +use itp_rpc::{Id, RpcRequest, RpcReturnValue}; +use itp_sgx_crypto::get_rsa3072_repository; +use itp_sgx_temp_dir::TempDir; +use itp_stf_executor::{getter_executor::GetterExecutor, mocks::GetStateMock}; +use itp_stf_state_observer::mock::ObserveStateMock; +use itp_test::mock::handle_state_mock::HandleStateMock; +use itp_top_pool_author::mocks::AuthorApiMock; +use itp_types::{DirectRequestStatus, RsaRequest, ShardIdentifier}; +use itp_utils::{FromHexPrefixed, ToHexPrefixed}; +use litentry_primitives::{Address32, Identity}; +use std::{string::ToString, sync::Arc, vec::Vec}; + +pub fn get_state_request_works() { + type TestState = u64; + + let temp_dir = TempDir::with_prefix("get_state_request_works").unwrap(); + + let connection_registry = Arc::new(ConnectionRegistry::::new()); + let watch_extractor = Arc::new(create_determine_watch::()); + let rsa_repository = get_rsa3072_repository(temp_dir.path().to_path_buf()).unwrap(); + + let state: TestState = 78234u64; + let state_observer = Arc::new(ObserveStateMock::::new(state)); + let getter_executor = + Arc::new(GetterExecutor::<_, GetStateMock, Getter>::new(state_observer)); + let top_pool_author = Arc::new(AuthorApiMock::default()); + + let io_handler = public_api_rpc_handler( + top_pool_author, + getter_executor, + Arc::new(rsa_repository), + None::>, + ); + let rpc_handler = Arc::new(RpcWsHandler::new(io_handler, watch_extractor, connection_registry)); + + let getter = + Getter::public(PublicGetter::nonce(Identity::Substrate(Address32::from([0u8; 32])))); + + let request = RsaRequest::new(ShardIdentifier::default(), getter.encode()); + + let request_string = RpcRequest::compose_jsonrpc_call( + Id::Text("1".to_string()), + "state_executeGetter".to_string(), + vec![request.to_hex()], + ) + .unwrap(); + + let response_string = + rpc_handler.handle_message(ConnectionToken(1), request_string).unwrap().unwrap(); + + assert!(!response_string.is_empty()); + + // Because we cannot de-serialize the RpcResponse here (unresolved serde_json and std/sgx feature issue), + // we hard-code the expected response. + //error!("{}", response_string); + //let response: RpcResponse = serde_json::from_str(&response_string).unwrap(); + + const EXPECTED_HEX_RETURN_VALUE: &str = "0x2801209a310100000000000000"; + assert!(response_string.contains(EXPECTED_HEX_RETURN_VALUE)); + let rpc_return_value = RpcReturnValue::from_hex(EXPECTED_HEX_RETURN_VALUE).unwrap(); + assert_eq!(rpc_return_value.status, DirectRequestStatus::Ok); + let decoded_value: Option> = + Option::decode(&mut rpc_return_value.value.as_slice()).unwrap(); + assert_eq!(decoded_value, Some(state.encode())); +} diff --git a/bitacross-worker/enclave-runtime/src/test/enclave_signer_tests.rs b/bitacross-worker/enclave-runtime/src/test/enclave_signer_tests.rs new file mode 100644 index 0000000000..7698091dd6 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/test/enclave_signer_tests.rs @@ -0,0 +1,169 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +use codec::Encode; +use ita_sgx_runtime::Runtime; +use ita_stf::{Getter, Stf, TrustedCall, TrustedCallSigned}; +use itp_node_api::metadata::{metadata_mocks::NodeMetadataMock, provider::NodeMetadataRepository}; +use itp_ocall_api::EnclaveAttestationOCallApi; +use itp_sgx_crypto::{ + ed25519_derivation::DeriveEd25519, key_repository::AccessKey, mocks::KeyRepositoryMock, +}; +use itp_sgx_externalities::SgxExternalities; +use itp_stf_executor::{enclave_signer::StfEnclaveSigner, traits::StfEnclaveSigning}; +use itp_stf_interface::{ + mocks::GetterExecutorMock, system_pallet::SystemPalletAccountInterface, InitState, + StateCallInterface, +}; +use itp_stf_primitives::{ + traits::TrustedCallVerification, + types::{AccountId, ShardIdentifier, TrustedOperation}, +}; +use itp_stf_state_observer::mock::ObserveStateMock; +use itp_test::mock::onchain_mock::OnchainMock; +use itp_top_pool_author::{mocks::AuthorApiMock, traits::AuthorApi}; +use itp_types::RsaRequest; +use litentry_primitives::Identity; +use sgx_crypto_helper::{rsa3072::Rsa3072KeyPair, RsaKeyPair}; +use sp_core::Pair; +use std::{sync::Arc, vec::Vec}; + +type ShieldingKeyRepositoryMock = KeyRepositoryMock; +type TestStf = Stf; + +pub fn derive_key_is_deterministic() { + let rsa_key = Rsa3072KeyPair::new().unwrap(); + + let first_ed_key = rsa_key.derive_ed25519().unwrap(); + let second_ed_key = rsa_key.derive_ed25519().unwrap(); + assert_eq!(first_ed_key.public(), second_ed_key.public()); +} + +pub fn enclave_signer_signatures_are_valid() { + let top_pool_author = Arc::new(AuthorApiMock::default()); + let ocall_api = Arc::new(OnchainMock::default()); + let shielding_key_repo = Arc::new(ShieldingKeyRepositoryMock::default()); + let enclave_account: AccountId = shielding_key_repo + .retrieve_key() + .unwrap() + .derive_ed25519() + .unwrap() + .public() + .into(); + + let state_observer: Arc> = + Arc::new(ObserveStateMock::new(TestStf::init_state(enclave_account.clone()))); + let shard = ShardIdentifier::default(); + let mr_enclave = ocall_api.get_mrenclave_of_self().unwrap(); + let enclave_signer = StfEnclaveSigner::<_, _, _, TestStf, _, TrustedCallSigned, Getter>::new( + state_observer, + ocall_api, + shielding_key_repo, + top_pool_author, + ); + let trusted_call = TrustedCall::balance_shield( + Identity::Substrate(enclave_account.into()), + AccountId::new([3u8; 32]), + 200u128, + ); + + let trusted_call_signed = enclave_signer.sign_call_with_self(&trusted_call, &shard).unwrap(); + assert!(trusted_call_signed.verify_signature(&mr_enclave.m, &shard)); +} + +pub fn nonce_is_computed_correctly() { + let top_pool_author = Arc::new(AuthorApiMock::default()); + let ocall_api = Arc::new(OnchainMock::default()); + let shielding_key_repo = Arc::new(ShieldingKeyRepositoryMock::default()); + let enclave_account: AccountId = shielding_key_repo + .retrieve_key() + .unwrap() + .derive_ed25519() + .unwrap() + .public() + .into(); + let mut state = TestStf::init_state(enclave_account.clone()); + // only used to create the enclave signer, the state is **not** synchronised + let state_observer: Arc> = + Arc::new(ObserveStateMock::new(state.clone())); + let shard = ShardIdentifier::default(); + let enclave_signer = StfEnclaveSigner::<_, _, _, TestStf, _, TrustedCallSigned, Getter>::new( + state_observer, + ocall_api, + shielding_key_repo, + top_pool_author.clone(), + ); + assert_eq!(enclave_account, enclave_signer.get_enclave_account().unwrap()); + + // create the first trusted_call and submit it + let trusted_call_1 = TrustedCall::balance_shield( + Identity::Substrate(enclave_account.clone().into()), + AccountId::new([1u8; 32]), + 100u128, + ); + let trusted_call_1_signed = + enclave_signer.sign_call_with_self(&trusted_call_1, &shard).unwrap(); + top_pool_author.submit_top(RsaRequest::new( + shard, + TrustedOperation::::indirect_call(trusted_call_1_signed.clone()) + .encode(), + )); + assert_eq!(1, top_pool_author.get_pending_trusted_calls_for(shard, &enclave_account).len()); + // create the second trusted_call and submit it + let trusted_call_2 = TrustedCall::balance_shield( + Identity::Substrate(enclave_account.clone().into()), + AccountId::new([2u8; 32]), + 200u128, + ); + let trusted_call_2_signed = + enclave_signer.sign_call_with_self(&trusted_call_2, &shard).unwrap(); + top_pool_author.submit_top(RsaRequest::new( + shard, + TrustedOperation::::indirect_call(trusted_call_2_signed.clone()) + .encode(), + )); + assert_eq!(2, top_pool_author.get_pending_trusted_calls_for(shard, &enclave_account).len()); + // there should be no pending trusted calls for non-enclave-account + assert_eq!( + 0, + top_pool_author + .get_pending_trusted_calls_for(shard, &AccountId::new([1u8; 32])) + .len() + ); + + assert_eq!(0, TestStf::get_account_nonce(&mut state, &enclave_account)); + let repo = Arc::new(NodeMetadataRepository::new(NodeMetadataMock::new())); + assert!(TestStf::execute_call( + &mut state, + &shard, + trusted_call_1_signed, + Default::default(), + &mut Vec::new(), + repo.clone(), + ) + .is_ok()); + + assert!(TestStf::execute_call( + &mut state, + &shard, + trusted_call_2_signed, + Default::default(), + &mut Vec::new(), + repo, + ) + .is_ok()); + assert_eq!(2, TestStf::get_account_nonce(&mut state, &enclave_account)); +} diff --git a/bitacross-worker/enclave-runtime/src/test/evm_pallet_tests.rs b/bitacross-worker/enclave-runtime/src/test/evm_pallet_tests.rs new file mode 100644 index 0000000000..61a8912e2e --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/test/evm_pallet_tests.rs @@ -0,0 +1,401 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +use crate::test::fixtures::test_setup::{test_setup, TestStf}; +use core::str::FromStr; +use ita_sgx_runtime::{AddressMapping, HashedAddressMapping, Index, System}; +use ita_stf::{ + evm_helpers::{ + create_code_hash, evm_create2_address, evm_create_address, get_evm_account_codes, + get_evm_account_storages, + }, + test_genesis::{endow, endowed_account as funded_pair}, + State, TrustedCall, +}; +use itp_node_api::metadata::{metadata_mocks::NodeMetadataMock, provider::NodeMetadataRepository}; +use itp_sgx_externalities::SgxExternalitiesTrait; +use itp_stf_interface::StateCallInterface; +use itp_stf_primitives::{traits::TrustedCallSigning, types::KeyPair}; +use itp_types::{parentchain::ParentchainCall, AccountId, ShardIdentifier}; +use primitive_types::H256; +use sp_core::{crypto::Pair, H160, U256}; +use std::{sync::Arc, vec::Vec}; + +pub fn test_evm_call() { + // given + let (_, mut state, shard, mrenclave, ..) = test_setup(); + let mut parentchain_calls = Vec::new(); + + // Create the sender account. + let sender = funded_pair(); + let sender_acc: AccountId = sender.public().into(); + let mut sender_evm_acc_slice: [u8; 20] = [0; 20]; + sender_evm_acc_slice + .copy_from_slice((<[u8; 32]>::from(sender_acc.clone())).get(0..20).unwrap()); + let sender_evm_acc: H160 = sender_evm_acc_slice.into(); + // Ensure the substrate version of the evm account has some money. + let sender_evm_substrate_addr = + ita_sgx_runtime::HashedAddressMapping::into_account_id(sender_evm_acc); + endow(&mut state, vec![(sender_evm_substrate_addr, 51_777_000_000_000)]); + + // Create the receiver account. + let destination_evm_acc = H160::from_str("1000000000000000000000000000000000000001").unwrap(); + let destination_evm_substrate_addr = + ita_sgx_runtime::HashedAddressMapping::into_account_id(destination_evm_acc); + assert_eq!( + state.execute_with(|| System::account(&destination_evm_substrate_addr).data.free), + 0 + ); + + let transfer_value: u128 = 1_000_000_000; + + let trusted_call = TrustedCall::evm_call( + sender_acc, + sender_evm_acc, + destination_evm_acc, + Vec::new(), + U256::from(transfer_value), + 21776, // gas limit + U256::from(1_000_000_000), + None, + Some(U256::from(0)), + Vec::new(), + ) + .sign(&sender.into(), 0, &mrenclave, &shard); + + // when + let repo = Arc::new(NodeMetadataRepository::::default()); + let shard = ShardIdentifier::default(); + TestStf::execute_call( + &mut state, + &shard, + trusted_call, + Default::default(), + &mut parentchain_calls, + repo, + ) + .unwrap(); + + // then + assert_eq!( + transfer_value, + state.execute_with(|| System::account(&destination_evm_substrate_addr).data.free) + ); +} + +pub fn test_evm_counter() { + // given + let (_, mut state, shard, mrenclave, ..) = test_setup(); + let mut parentchain_calls = Vec::new(); + + // Create the sender account. + let sender = funded_pair(); + let sender_acc: AccountId = sender.public().into(); + let mut sender_evm_acc_slice: [u8; 20] = [0; 20]; + sender_evm_acc_slice + .copy_from_slice((<[u8; 32]>::from(sender_acc.clone())).get(0..20).unwrap()); + let sender_evm_acc: H160 = sender_evm_acc_slice.into(); + // Ensure the substrate version of the evm account has some money. + let sender_evm_substrate_addr = + ita_sgx_runtime::HashedAddressMapping::into_account_id(sender_evm_acc); + endow(&mut state, vec![(sender_evm_substrate_addr, 51_777_000_000_000)]); + + // Smart Contract from Counter.sol. + let smart_contract = "608060405234801561001057600080fd5b50600160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610377806100696000396000f3fe6080604052600436106100435760003560e01c80631003e2d21461004d57806333cf508014610076578063371303c0146100a157806358992216146100b857610044565b5b60056000819055005b34801561005957600080fd5b50610074600480360381019061006f9190610209565b6100e3565b005b34801561008257600080fd5b5061008b61013f565b6040516100989190610245565b60405180910390f35b3480156100ad57600080fd5b506100b6610148565b005b3480156100c457600080fd5b506100cd6101a4565b6040516100da91906102a1565b60405180910390f35b806000808282546100f491906102eb565b9250508190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60008054905090565b600160008082825461015a91906102eb565b9250508190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b600080fd5b6000819050919050565b6101e6816101d3565b81146101f157600080fd5b50565b600081359050610203816101dd565b92915050565b60006020828403121561021f5761021e6101ce565b5b600061022d848285016101f4565b91505092915050565b61023f816101d3565b82525050565b600060208201905061025a6000830184610236565b92915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061028b82610260565b9050919050565b61029b81610280565b82525050565b60006020820190506102b66000830184610292565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006102f6826101d3565b9150610301836101d3565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03821115610336576103356102bc565b5b82820190509291505056fea2646970667358221220b37e993e133ed19c840809cc8acbbba8116dee3744ba01c81044d75146805c9364736f6c634300080f0033"; + + let trusted_call = TrustedCall::evm_create( + sender_acc.clone(), + sender_evm_acc, + array_bytes::hex2bytes(smart_contract).unwrap().to_vec(), + U256::from(0), + 10_000_000, // gas limit + U256::from(1), // max_fee_per_gas !>= min_gas_price defined in runtime + None, + Some(U256::from(0)), + Vec::new(), + ) + .sign(&sender.into(), 0, &mrenclave, &shard); + + // when + let execution_address = evm_create_address(sender_evm_acc, 0); + let repo = Arc::new(NodeMetadataRepository::::default()); + let shard = ShardIdentifier::default(); + TestStf::execute_call( + &mut state, + &shard, + trusted_call, + Default::default(), + &mut parentchain_calls, + repo, + ) + .unwrap(); + + // then + assert_eq!( + execution_address, + H160::from_slice( + &array_bytes::hex2bytes("0xce2c9e7f9c10049996173b2ca2d9a6815a70e890").unwrap(), + ) + ); + + assert!(state.execute_with(|| get_evm_account_codes(&execution_address).is_some())); + + let counter_value = state + .execute_with(|| get_evm_account_storages(&execution_address, &H256::zero())) + .unwrap(); + assert_eq!(H256::from_low_u64_be(1), counter_value); + let last_caller = state + .execute_with(|| get_evm_account_storages(&execution_address, &H256::from_low_u64_be(1))) + .unwrap(); + assert_eq!(H256::from(sender_evm_acc), last_caller); + + // Call to inc() function + // in solidity compile information you get the hash of the call + let inc_function_input = array_bytes::hex2bytes("371303c0").unwrap(); + + execute_and_verify_evm_call( + sender_acc.clone(), + sender_evm_acc, + execution_address, + inc_function_input.to_vec(), + 1, + 1, + sender.into(), + &mrenclave, + &shard, + &mut state, + &mut parentchain_calls, + 2, + ); + + // Call the fallback function + execute_and_verify_evm_call( + sender_acc.clone(), + sender_evm_acc, + execution_address, + Vec::new(), // Empty input calls the fallback function. + 2, + 2, + sender.into(), + &mrenclave, + &shard, + &mut state, + &mut parentchain_calls, + 5, + ); + + // Call to inc() function + // in solidity compile information you get the hash of the call + execute_and_verify_evm_call( + sender_acc.clone(), + sender_evm_acc, + execution_address, + inc_function_input, + 3, + 3, + sender.into(), + &mrenclave, + &shard, + &mut state, + &mut parentchain_calls, + 6, + ); + + // Call to add() function + // in solidity compile information you get the hash of the call + let function_hash = "1003e2d2"; + // 32 byte string of the value to add in hex + let add_value = "0000000000000000000000000000000000000000000000000000000000000002"; + let add_function_input = + array_bytes::hex2bytes(&format!("{}{}", function_hash, add_value)).unwrap(); + + execute_and_verify_evm_call( + sender_acc, + sender_evm_acc, + execution_address, + add_function_input, + 4, + 4, + sender.into(), + &mrenclave, + &shard, + &mut state, + &mut parentchain_calls, + 8, + ); +} + +#[allow(clippy::too_many_arguments)] +fn execute_and_verify_evm_call( + sender_acc: AccountId, + sender_evm_acc: H160, + execution_address: H160, + function_input: Vec, + evm_nonce: i8, + nonce: Index, + pair: KeyPair, + mrenclave: &[u8; 32], + shard: &ShardIdentifier, + state: &mut State, + calls: &mut Vec, + counter_expected: u64, +) { + let inc_call = TrustedCall::evm_call( + sender_acc, + sender_evm_acc, + execution_address, + function_input, + U256::from(0), + 10_000_000, // gas limit + U256::from(1), // max_fee_per_gas !>= min_gas_price defined in runtime + None, + Some(U256::from(evm_nonce)), + Vec::new(), + ) + .sign(&pair, nonce, mrenclave, shard); + let repo = Arc::new(NodeMetadataRepository::::default()); + let shard = ShardIdentifier::default(); + TestStf::execute_call(state, &shard, inc_call, Default::default(), calls, repo).unwrap(); + + let counter_value = state + .execute_with(|| get_evm_account_storages(&execution_address, &H256::zero())) + .unwrap(); + assert_eq!(counter_value, H256::from_low_u64_be(counter_expected)); +} + +pub fn test_evm_create() { + // given + let (_, mut state, shard, mrenclave, ..) = test_setup(); + let mut parentchain_calls = Vec::new(); + + // Create the sender account. + let sender = funded_pair(); + let sender_acc: AccountId = sender.public().into(); + let mut sender_evm_acc_slice: [u8; 20] = [0; 20]; + sender_evm_acc_slice + .copy_from_slice((<[u8; 32]>::from(sender_acc.clone())).get(0..20).unwrap()); + let sender_evm_acc: H160 = sender_evm_acc_slice.into(); + // Ensure the substrate version of the evm account has some money. + let sender_evm_substrate_addr = HashedAddressMapping::into_account_id(sender_evm_acc); + endow(&mut state, vec![(sender_evm_substrate_addr.clone(), 51_777_000_000_000)]); + + // Bytecode from Counter.sol + let smart_contract = "608060405234801561001057600080fd5b50600160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610377806100696000396000f3fe6080604052600436106100435760003560e01c80631003e2d21461004d57806333cf508014610076578063371303c0146100a157806358992216146100b857610044565b5b60056000819055005b34801561005957600080fd5b50610074600480360381019061006f9190610209565b6100e3565b005b34801561008257600080fd5b5061008b61013f565b6040516100989190610245565b60405180910390f35b3480156100ad57600080fd5b506100b6610148565b005b3480156100c457600080fd5b506100cd6101a4565b6040516100da91906102a1565b60405180910390f35b806000808282546100f491906102eb565b9250508190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60008054905090565b600160008082825461015a91906102eb565b9250508190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b600080fd5b6000819050919050565b6101e6816101d3565b81146101f157600080fd5b50565b600081359050610203816101dd565b92915050565b60006020828403121561021f5761021e6101ce565b5b600061022d848285016101f4565b91505092915050565b61023f816101d3565b82525050565b600060208201905061025a6000830184610236565b92915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061028b82610260565b9050919050565b61029b81610280565b82525050565b60006020820190506102b66000830184610292565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006102f6826101d3565b9150610301836101d3565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03821115610336576103356102bc565b5b82820190509291505056fea2646970667358221220b37e993e133ed19c840809cc8acbbba8116dee3744ba01c81044d75146805c9364736f6c634300080f0033"; + let smart_contract = array_bytes::hex2bytes(smart_contract).unwrap(); + + let trusted_call = TrustedCall::evm_create( + sender_acc, + sender_evm_acc, + smart_contract, + U256::from(0), // value + 10_000_000, // gas limit + U256::from(1), // max_fee_per_gas !>= min_gas_price defined in runtime + None, + Some(U256::from(0)), + Vec::new(), + ) + .sign(&sender.into(), 0, &mrenclave, &shard); + + // Should be the first call of the evm account + let nonce = state.execute_with(|| System::account_nonce(&sender_evm_substrate_addr)); + assert_eq!(nonce, 0); + let execution_address = evm_create_address(sender_evm_acc, nonce); + let repo = Arc::new(NodeMetadataRepository::::default()); + let shard = ShardIdentifier::default(); + TestStf::execute_call( + &mut state, + &shard, + trusted_call, + Default::default(), + &mut parentchain_calls, + repo, + ) + .unwrap(); + + assert_eq!( + execution_address, + H160::from_slice( + &array_bytes::hex2bytes("0xce2c9e7f9c10049996173b2ca2d9a6815a70e890").unwrap(), + ) + ); + assert!(state.execute_with(|| get_evm_account_codes(&execution_address).is_some())); + + // Ensure the nonce of the evm account has been increased by one + // Should be the first call of the evm account + let nonce = state.execute_with(|| System::account_nonce(&sender_evm_substrate_addr)); + assert_eq!(nonce, 1); +} + +pub fn test_evm_create2() { + // given + let (_, mut state, shard, mrenclave, ..) = test_setup(); + let mut parentchain_calls = Vec::new(); + + // Create the sender account. + let sender = funded_pair(); + let sender_acc: AccountId = sender.public().into(); + let mut sender_evm_acc_slice: [u8; 20] = [0; 20]; + sender_evm_acc_slice + .copy_from_slice((<[u8; 32]>::from(sender_acc.clone())).get(0..20).unwrap()); + let sender_evm_acc: H160 = sender_evm_acc_slice.into(); + // Ensure the substrate version of the evm account has some money. + let sender_evm_substrate_addr = HashedAddressMapping::into_account_id(sender_evm_acc); + endow(&mut state, vec![(sender_evm_substrate_addr, 51_777_000_000_000)]); + + let salt = H256::from_low_u64_be(20); + // Bytecode from Counter.sol + let smart_contract = "608060405234801561001057600080fd5b50600160008190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610377806100696000396000f3fe6080604052600436106100435760003560e01c80631003e2d21461004d57806333cf508014610076578063371303c0146100a157806358992216146100b857610044565b5b60056000819055005b34801561005957600080fd5b50610074600480360381019061006f9190610209565b6100e3565b005b34801561008257600080fd5b5061008b61013f565b6040516100989190610245565b60405180910390f35b3480156100ad57600080fd5b506100b6610148565b005b3480156100c457600080fd5b506100cd6101a4565b6040516100da91906102a1565b60405180910390f35b806000808282546100f491906102eb565b9250508190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60008054905090565b600160008082825461015a91906102eb565b9250508190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b600080fd5b6000819050919050565b6101e6816101d3565b81146101f157600080fd5b50565b600081359050610203816101dd565b92915050565b60006020828403121561021f5761021e6101ce565b5b600061022d848285016101f4565b91505092915050565b61023f816101d3565b82525050565b600060208201905061025a6000830184610236565b92915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061028b82610260565b9050919050565b61029b81610280565b82525050565b60006020820190506102b66000830184610292565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006102f6826101d3565b9150610301836101d3565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03821115610336576103356102bc565b5b82820190509291505056fea2646970667358221220b37e993e133ed19c840809cc8acbbba8116dee3744ba01c81044d75146805c9364736f6c634300080f0033"; + let smart_contract = array_bytes::hex2bytes(smart_contract).unwrap(); + + let trusted_call = TrustedCall::evm_create2( + sender_acc, + sender_evm_acc, + smart_contract.clone(), + salt, + U256::from(0), // value + 10_000_000, // gas limit + U256::from(1), // max_fee_per_gas !>= min_gas_price defined in runtime + None, + Some(U256::from(0)), + Vec::new(), + ) + .sign(&sender.into(), 0, &mrenclave, &shard); + + // when + let code_hash = create_code_hash(&smart_contract); + let execution_address = evm_create2_address(sender_evm_acc, salt, code_hash); + let repo = Arc::new(NodeMetadataRepository::::default()); + let shard = ShardIdentifier::default(); + TestStf::execute_call( + &mut state, + &shard, + trusted_call, + Default::default(), + &mut parentchain_calls, + repo, + ) + .unwrap(); + + // then + assert_eq!( + execution_address, + H160::from_slice( + &array_bytes::hex2bytes("0xe07ad7925f6b2b10c5a7653fb16db7a984059d11").unwrap(), + ) + ); + + assert!(state.execute_with(|| get_evm_account_codes(&execution_address).is_some())); +} diff --git a/bitacross-worker/enclave-runtime/src/test/fixtures/components.rs b/bitacross-worker/enclave-runtime/src/test/fixtures/components.rs new file mode 100644 index 0000000000..dd1237672d --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/test/fixtures/components.rs @@ -0,0 +1,69 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::test::mocks::types::{TestOCallApi, TestRpcResponder, TestSigner, TestTopPool}; +use codec::Encode; +use ita_stf::{Getter, TrustedCall, TrustedCallSigned}; +use itp_ocall_api::EnclaveAttestationOCallApi; +use itp_sgx_crypto::ShieldingCryptoEncrypt; +use itp_stf_primitives::{ + traits::TrustedCallSigning, + types::{KeyPair, TrustedOperation}, +}; +use itp_top_pool::pool::Options as PoolOptions; +use itp_top_pool_author::api::SidechainApi; +use itp_types::{Block as ParentchainBlock, Enclave, ShardIdentifier}; +use sp_core::{ed25519, Pair, H256}; +use sp_runtime::traits::Header as HeaderTrait; +use std::{boxed::Box, sync::Arc, vec::Vec}; +pub(crate) fn create_top_pool() -> Arc { + let rpc_responder = Arc::new(TestRpcResponder::new()); + let sidechain_api = Arc::new(SidechainApi::::new()); + Arc::new(TestTopPool::create(PoolOptions::default(), sidechain_api, rpc_responder)) +} + +pub(crate) fn create_ocall_api>( + header: &Header, + signer: &TestSigner, +) -> Arc { + let enclave_validateer = Enclave::new( + signer.public().into(), + Default::default(), + Default::default(), + Default::default(), + ); + Arc::new(TestOCallApi::default().add_validateer_set(header, Some(vec![enclave_validateer]))) +} + +pub(crate) fn encrypt_trusted_operation( + shielding_key: &ShieldingKey, + trusted_operation: &TrustedOperation, +) -> Vec { + let encoded_operation = trusted_operation.encode(); + shielding_key.encrypt(encoded_operation.as_slice()).unwrap() +} + +pub(crate) fn sign_trusted_call( + trusted_call: &TrustedCall, + attestation_api: &AttestationApi, + shard_id: &ShardIdentifier, + from: ed25519::Pair, +) -> TrustedCallSigned { + let mr_enclave = attestation_api.get_mrenclave_of_self().unwrap(); + trusted_call.sign(&KeyPair::Ed25519(Box::new(from)), 0, &mr_enclave.m, shard_id) +} diff --git a/bitacross-worker/enclave-runtime/src/test/fixtures/initialize_test_state.rs b/bitacross-worker/enclave-runtime/src/test/fixtures/initialize_test_state.rs new file mode 100644 index 0000000000..98e23261b6 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/test/fixtures/initialize_test_state.rs @@ -0,0 +1,42 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use super::test_setup::TestStf; +use ita_stf::State; +use itp_sgx_externalities::{SgxExternalities, SgxExternalitiesTrait}; +use itp_stf_interface::InitState; +use itp_stf_primitives::types::AccountId; +use itp_stf_state_handler::handle_state::HandleState; +use itp_types::ShardIdentifier; + +/// Returns an empty `State` with the corresponding `ShardIdentifier`. +pub fn init_state>( + state_handler: &S, + enclave_account: AccountId, +) -> (State, ShardIdentifier) { + let shard = ShardIdentifier::default(); + + let _hash = state_handler.initialize_shard(shard).unwrap(); + let (lock, _) = state_handler.load_for_mutation(&shard).unwrap(); + let mut state = TestStf::init_state(enclave_account); + state.prune_state_diff(); + + state_handler.write_after_mutation(state.clone(), lock, &shard).unwrap(); + + (state, shard) +} diff --git a/bitacross-worker/enclave-runtime/src/test/fixtures/mod.rs b/bitacross-worker/enclave-runtime/src/test/fixtures/mod.rs new file mode 100644 index 0000000000..bc01106db1 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/test/fixtures/mod.rs @@ -0,0 +1,21 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pub mod components; +pub mod initialize_test_state; +pub mod test_setup; diff --git a/bitacross-worker/enclave-runtime/src/test/fixtures/ra_dump_cert_TEST4.der b/bitacross-worker/enclave-runtime/src/test/fixtures/ra_dump_cert_TEST4.der new file mode 100644 index 0000000000000000000000000000000000000000..2e775236d6c14d5dc93b400a44d4d0a2ab195243 GIT binary patch literal 3234 zcmd5O%uFVkC6i3X zigJZgsz^Z*L0qs8E+`Zfd~6c`*RHr%uiSh0m&F;DhKR;Gx@Y?|swl(fKByujjk405fp^wL34e zT)y;rx+|CNyu#ga^(W8$4Sf1rf1tC69(nGyKmPVc@bD|IUPqKyBEEXLdZE1Q=Rf-F z*Z%eDS8s}+e*x0ZH&0#{e)rh>|MbO|zw+n5)Ia<25AJ*M);%Y1>z<~6cGuqZiD#8d z&%VZ8y6@_(d*V-?Et0bvXBfudB#F~Bj?x%TVib+hC?F^RNRnVE6bBSW)6Ci9XZ2z| z-`C-KbM6H&@B|>9z$gbrZ=lo-jC>L&ScU|>VKd+D7gc$71AqK%v2VmOF2ZJR9jbah zsl)oP?*s6rA*r%!Lq|1@j~k*Wh(hnWDZ)Cfc5AgvE3T-b-V!DIT!13M`Cu-j2*h4T z0JNXd7n=VT+{N^^1n1sw85|Ty$YHD?5duTvB!LnlEfS<4@*KuPoZ?9ar5TbXP*kD- z$Fc+?auSD%Jr0K82wFfc!4~&bdEE;o|@%5i262@ z{~ws~PDigK%Jq}X$EmzwC&XknPm$1z#tS^k{Ed;VM%Bi3mhS-V*{7ZvCiZZOZEz8x z=ByH`=w!sJAu>4R(Vh%r3Dfx#?xtM=qf!scjFQlev>Ak~G#D$mVlbJO#tJ9z6@0PY zX=922+K3r+ei>}gvgtdc^E@#gt?}NpKtQ}8W=qs?_7dA^pg48A=tzxAer(j5n*>DX zv2F?``%{eh3%zFZeL%@Ru24ghSn69B_a_4Cbc<8VYz$v2{ML0!HgndI$SsBZBxs@@ z;9ib*h>UNmad^}=O{dk0h0wf(7e+$LTeAxhLF4J#5mqozDo)r6C4!{UF`2a^`*30~ zq1AX9*e1DKKq@bHv=<#}lER$9&>H$xx}J8jUKqJG@=U>>;@MIQq-r(LGPCfkn>U}) zRaLAUo`>x^gRRQJOdUc)RFVR870pj|2xNY;l_zS13zo?7tqXO9IPxbrF{2i02#|%`(fkLEsx;=G&VHBQkgGwGlo9s$^^e zjNC-)IJ1D*DtT<^&g`f=YNrdUld*J(>A7xsp=;mJEjjuvHOOK+^xTuE>2P&XL%%_M zAMYp$aB>@Wtn;Xm_n7h?_kQLNbKS058D8KW37qsFyh4xb99y2qMH4N>TIcr{F=5-T zCQ}jw(>8NKGE+qf^!sp1xlIAhb{JmSm}BK4K&2A<9>yY0GBmb}ZwgIjq$rZjggxqMhH6pxK7w zMnrj=&zn;+K?0H6T4d!lnltXnE-qqgS#jEFHZNeyoo)wA$>~@yowcQ3wzIhj6>l(QYcq%Hy0zPpgs@}>_g-UoXq;?~svzofdRvgI2zjNea;|gC`B^4vYt3{ngp!gAb39z9yOXuU zN!}Rj(Ns7n&=VOQ5*M~YD{@HNA5g2xtpN3NJ}I`&!ITkUe=+JZys%SRJJ6-L^+L|6 zBw2Q_93%>)w2lRFdCn7W6=PnPtczvf>*xG9=h9(@v9*R7%|*YIR%4+6 zI|xSNfL@URF^}$kJ!QhMF;AliEchd!XBN9VEy&WP@md$LPPVmEKJgaP9?>>Z$QBz* zqlOw9TJ2h}X~H?q`XuUtObL~Ah853?TU$A?=xQTVZqt=mt*uM7zYNAA(af5uwyS+V zmAJ`dED$g)=>d-TZkE9!rYqiAoXj+eYEvp^kjThUJj*8N5DVa3x3*D#mG9>PABRB@ zloVMf+xeVv{h$zZ2`Wz1=ArrbRj}bJ@+yTuG#?&g>Cd8)dB1)YyBYqeiQ+?R1 zs$F83nd!R6G8rr40;wa!r4QOtGqW+gdGoD54_D+nfN=Ry1^Zlp3{oPgfo~OO&`bR$WpnCby>n~0I-aNc`@s7{^_4og1k<9}?{V@B|ue9f% O`rhE9ALHNm<$nV!>ga#~ literal 0 HcmV?d00001 diff --git a/bitacross-worker/enclave-runtime/src/test/fixtures/test_ra_signer_attn_MRSIGNER1_MRENCLAVE1.bin b/bitacross-worker/enclave-runtime/src/test/fixtures/test_ra_signer_attn_MRSIGNER1_MRENCLAVE1.bin new file mode 100644 index 0000000000000000000000000000000000000000..d7149d37d53e9f10d74d72c85b4cf516f854c851 GIT binary patch literal 64 zcmV-G0KfluX&Ryyn6JS^EMXg!)k6C~g7p19TtPrYRjrx&>avnmM;e(u@}l?JBjEwh W0K$Yu-;g|KOVI$sR&!?0#cUb+P#>-U literal 0 HcmV?d00001 diff --git a/bitacross-worker/enclave-runtime/src/test/fixtures/test_setup.rs b/bitacross-worker/enclave-runtime/src/test/fixtures/test_setup.rs new file mode 100644 index 0000000000..78c2bef328 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/test/fixtures/test_setup.rs @@ -0,0 +1,128 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +use crate::{ + ocall::OcallApi, + test::{ + fixtures::initialize_test_state::init_state, mocks::rpc_responder_mock::RpcResponderMock, + }, +}; +use ita_sgx_runtime::Runtime; +use ita_stf::{Getter, State, Stf, TrustedCallSigned}; +use itp_node_api::metadata::{metadata_mocks::NodeMetadataMock, provider::NodeMetadataRepository}; +use itp_ocall_api::EnclaveAttestationOCallApi; +use itp_sgx_crypto::{ed25519_derivation::DeriveEd25519, mocks::KeyRepositoryMock}; +use itp_sgx_externalities::SgxExternalities; +use itp_stf_executor::executor::StfExecutor; +use itp_stf_primitives::types::{ShardIdentifier, TrustedOperation}; +use itp_test::mock::{ + handle_state_mock::HandleStateMock, metrics_ocall_mock::MetricsOCallMock, + shielding_crypto_mock::ShieldingCryptoMock, +}; +use itp_top_pool::{basic_pool::BasicPool, pool::ExtrinsicHash}; +use itp_top_pool_author::{ + api::SidechainApi, + author::Author, + top_filter::{AllowAllTopsFilter, DirectCallsOnlyFilter}, +}; +use itp_types::{Block, MrEnclave}; +use sp_core::{crypto::Pair, ed25519 as spEd25519}; +use std::sync::Arc; +pub type TestRpcResponder = RpcResponderMock>>; +pub type TestTopPool = BasicPool< + SidechainApi, + Block, + TestRpcResponder, + TrustedOperation, +>; +pub type TestShieldingKeyRepo = KeyRepositoryMock; +pub type TestTopPoolAuthor = Author< + TestTopPool, + AllowAllTopsFilter, + DirectCallsOnlyFilter, + HandleStateMock, + TestShieldingKeyRepo, + MetricsOCallMock, + TrustedCallSigned, + Getter, +>; +pub type TestStf = Stf; + +pub type TestStfExecutor = StfExecutor< + OcallApi, + HandleStateMock, + NodeMetadataRepository, + TestStf, + TrustedCallSigned, + Getter, +>; + +/// Returns all the things that are commonly used in tests and runs +/// `ensure_no_empty_shard_directory_exists` +pub fn test_setup() -> ( + Arc, + State, + ShardIdentifier, + MrEnclave, + ShieldingCryptoMock, + Arc, + Arc, +) { + let shielding_key = ShieldingCryptoMock::default(); + let shielding_key_repo = Arc::new(KeyRepositoryMock::new(shielding_key.clone())); + + let state_handler = Arc::new(HandleStateMock::default()); + let (state, shard) = + init_state(state_handler.as_ref(), enclave_call_signer(&shielding_key).public().into()); + let top_pool = test_top_pool(); + let mrenclave = OcallApi.get_mrenclave_of_self().unwrap().m; + + let node_metadata_repo = Arc::new(NodeMetadataRepository::new(NodeMetadataMock::new())); + let stf_executor = Arc::new(TestStfExecutor::new( + Arc::new(OcallApi), + state_handler.clone(), + node_metadata_repo, + )); + + let (sender, _receiver) = std::sync::mpsc::sync_channel(1000); + + ( + Arc::new(TestTopPoolAuthor::new( + Arc::new(top_pool), + AllowAllTopsFilter::::new(), + DirectCallsOnlyFilter::::new(), + state_handler.clone(), + shielding_key_repo, + Arc::new(MetricsOCallMock::default()), + Arc::new(sender), + )), + state, + shard, + mrenclave, + shielding_key, + state_handler, + stf_executor, + ) +} + +pub fn test_top_pool() -> TestTopPool { + let chain_api = Arc::new(SidechainApi::::new()); + BasicPool::create(Default::default(), chain_api, Arc::new(TestRpcResponder::new())) +} + +pub fn enclave_call_signer(key_source: &Source) -> spEd25519::Pair { + key_source.derive_ed25519().unwrap() +} diff --git a/bitacross-worker/enclave-runtime/src/test/ipfs_tests.rs b/bitacross-worker/enclave-runtime/src/test/ipfs_tests.rs new file mode 100644 index 0000000000..f1f94d3696 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/test/ipfs_tests.rs @@ -0,0 +1,42 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ipfs::IpfsContent, ocall::OcallApi}; +use itp_ocall_api::EnclaveIpfsOCallApi; +use log::*; +use std::{fs::File, io::Read, vec::Vec}; + +#[allow(unused)] +fn test_ocall_read_write_ipfs() { + info!("testing IPFS read/write. Hopefully ipfs daemon is running..."); + let enc_state: Vec = vec![20; 4 * 512 * 1024]; + + let cid = OcallApi.write_ipfs(enc_state.as_slice()).unwrap(); + + OcallApi.read_ipfs(&cid).unwrap(); + + let cid_str = std::str::from_utf8(&cid.0).unwrap(); + let mut f = File::open(cid_str).unwrap(); + let mut content_buf = Vec::new(); + f.read_to_end(&mut content_buf).unwrap(); + info!("reading file {:?} of size {} bytes", f, &content_buf.len()); + + let mut ipfs_content = IpfsContent::new(cid_str, content_buf); + let verification = ipfs_content.verify(); + assert!(verification.is_ok()); +} diff --git a/bitacross-worker/enclave-runtime/src/test/mocks/attestation_ocall_mock.rs b/bitacross-worker/enclave-runtime/src/test/mocks/attestation_ocall_mock.rs new file mode 100644 index 0000000000..a480890761 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/test/mocks/attestation_ocall_mock.rs @@ -0,0 +1,101 @@ +/* + CCopyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use itp_ocall_api::EnclaveAttestationOCallApi; +use sgx_types::*; +use std::{ + fmt::{Debug, Formatter, Result as FormatResult}, + vec::Vec, +}; + +#[derive(Clone)] +pub struct AttestationOCallMock { + mr_enclave: sgx_measurement_t, +} + +impl AttestationOCallMock { + pub fn new() -> Self { + Default::default() + } + + pub fn create_with_mr_enclave(mr_enclave: sgx_measurement_t) -> Self { + AttestationOCallMock { mr_enclave } + } +} + +impl EnclaveAttestationOCallApi for AttestationOCallMock { + fn sgx_init_quote(&self) -> SgxResult<(sgx_target_info_t, sgx_epid_group_id_t)> { + unreachable!() + } + + fn get_ias_socket(&self) -> SgxResult { + unreachable!() + } + + fn get_quote( + &self, + _sig_rl: Vec, + _report: sgx_report_t, + _sign_type: sgx_quote_sign_type_t, + _spid: sgx_spid_t, + _quote_nonce: sgx_quote_nonce_t, + ) -> SgxResult<(sgx_report_t, Vec)> { + unreachable!() + } + + fn get_dcap_quote(&self, _report: sgx_report_t, _quote_size: u32) -> SgxResult> { + unreachable!() + } + + fn get_qve_report_on_quote( + &self, + _quote: Vec, + _current_time: i64, + _quote_collateral: sgx_ql_qve_collateral_t, + _qve_report_info: sgx_ql_qe_report_info_t, + _supplemental_data_size: u32, + ) -> SgxResult<(u32, sgx_ql_qv_result_t, sgx_ql_qe_report_info_t, Vec)> { + unreachable!() + } + + fn get_update_info( + &self, + _platform_info: sgx_platform_info_t, + _enclave_trusted: i32, + ) -> SgxResult { + Ok(sgx_update_info_bit_t { csmeFwUpdate: 0, pswUpdate: 0, ucodeUpdate: 0 }) + } + + fn get_mrenclave_of_self(&self) -> SgxResult { + Ok(self.mr_enclave) + } +} + +impl Default for AttestationOCallMock { + fn default() -> Self { + AttestationOCallMock { mr_enclave: sgx_measurement_t { m: [1; SGX_HASH_SIZE] } } + } +} + +impl Debug for AttestationOCallMock { + fn fmt(&self, f: &mut Formatter<'_>) -> FormatResult { + f.debug_struct("AttestationOCallMock") + .field("mr_enclave", &self.mr_enclave.m) + .finish() + } +} diff --git a/bitacross-worker/enclave-runtime/src/test/mocks/enclave_rpc_ocall_mock.rs b/bitacross-worker/enclave-runtime/src/test/mocks/enclave_rpc_ocall_mock.rs new file mode 100644 index 0000000000..23003989e8 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/test/mocks/enclave_rpc_ocall_mock.rs @@ -0,0 +1,40 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use codec::Encode; +use itp_ocall_api::EnclaveRpcOCallApi; +use itp_types::TrustedOperationStatus; +use sgx_types::SgxResult; +use std::vec::Vec; + +#[derive(Clone, Debug, Default)] +pub struct EnclaveRpcOCallMock; + +impl EnclaveRpcOCallApi for EnclaveRpcOCallMock { + fn update_status_event( + &self, + _hash: H, + _status_update: TrustedOperationStatus, + ) -> SgxResult<()> { + Ok(()) + } + + fn send_state(&self, _hash: H, _value_opt: Option>) -> SgxResult<()> { + Ok(()) + } +} diff --git a/bitacross-worker/enclave-runtime/src/test/mocks/mod.rs b/bitacross-worker/enclave-runtime/src/test/mocks/mod.rs new file mode 100644 index 0000000000..26551844d6 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/test/mocks/mod.rs @@ -0,0 +1,24 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pub mod attestation_ocall_mock; +pub mod enclave_rpc_ocall_mock; +pub mod peer_updater_mock; +pub mod propose_to_import_call_mock; +pub mod rpc_responder_mock; +pub mod types; diff --git a/bitacross-worker/enclave-runtime/src/test/mocks/peer_updater_mock.rs b/bitacross-worker/enclave-runtime/src/test/mocks/peer_updater_mock.rs new file mode 100644 index 0000000000..63a60108df --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/test/mocks/peer_updater_mock.rs @@ -0,0 +1,24 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +use itc_peer_top_broadcaster::PeerUpdater; +use sgx_tstd::{string::String, vec::Vec}; + +pub struct PeerUpdaterMock {} + +impl PeerUpdater for PeerUpdaterMock { + fn update(&self, _peers: Vec) {} +} diff --git a/bitacross-worker/enclave-runtime/src/test/mocks/propose_to_import_call_mock.rs b/bitacross-worker/enclave-runtime/src/test/mocks/propose_to_import_call_mock.rs new file mode 100644 index 0000000000..fa47ae9539 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/test/mocks/propose_to_import_call_mock.rs @@ -0,0 +1,137 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::test::mocks::types::TestBlockImporter; +use codec::{Decode, Encode}; +use itc_parentchain::primitives::ParentchainId; +use itp_ocall_api::{ + EnclaveMetricsOCallApi, EnclaveOnChainOCallApi, EnclaveSidechainOCallApi, Result, +}; +use itp_types::{ + storage::StorageEntryVerified, BlockHash, Header as ParentchainHeader, ShardIdentifier, + WorkerRequest, WorkerResponse, H256, +}; +use its_primitives::types::block::SignedBlock as SignedSidechainBlockType; +use its_sidechain::consensus_common::BlockImport; +use sgx_types::SgxResult; +use sp_runtime::{traits::Header as ParentchainHeaderTrait, OpaqueExtrinsic}; +use std::{string::String, sync::Arc, vec::Vec}; + +/// OCallApi mock that routes the proposed sidechain blocks directly to the importer, +/// short circuiting all the RPC calls. +#[derive(Clone)] +pub struct ProposeToImportOCallApi { + parentchain_header: ParentchainHeader, + block_importer: Arc, +} + +impl ProposeToImportOCallApi { + pub fn new( + parentchain_header: ParentchainHeader, + block_importer: Arc, + ) -> Self { + ProposeToImportOCallApi { parentchain_header, block_importer } + } +} + +impl EnclaveOnChainOCallApi for ProposeToImportOCallApi { + fn send_to_parentchain( + &self, + _extrinsics: Vec, + _: &ParentchainId, + _: bool, + ) -> SgxResult<()> { + Ok(()) + } + + fn worker_request( + &self, + _req: Vec, + _: &ParentchainId, + ) -> SgxResult>> { + todo!() + } + + fn get_storage_verified, V: Decode>( + &self, + _storage_hash: Vec, + _header: &H, + _: &ParentchainId, + ) -> Result> { + todo!() + } + + fn get_multiple_storages_verified, V: Decode>( + &self, + _storage_hashes: Vec>, + _header: &H, + _: &ParentchainId, + ) -> Result>> { + todo!() + } + + fn get_storage_keys(&self, _key_prefix: Vec) -> Result>> { + todo!() + } +} + +impl EnclaveSidechainOCallApi for ProposeToImportOCallApi { + fn propose_sidechain_blocks( + &self, + signed_blocks: Vec, + ) -> SgxResult<()> { + let decoded_signed_blocks: Vec = signed_blocks + .iter() + .map(|sb| sb.encode()) + .map(|e| SignedSidechainBlockType::decode(&mut e.as_slice()).unwrap()) + .collect(); + + for signed_block in decoded_signed_blocks { + self.block_importer + .import_block(signed_block, &self.parentchain_header) + .unwrap(); + } + Ok(()) + } + + fn store_sidechain_blocks( + &self, + _signed_blocks: Vec, + ) -> SgxResult<()> { + Ok(()) + } + + fn fetch_sidechain_blocks_from_peer( + &self, + _last_imported_block_hash: BlockHash, + _maybe_until_block_hash: Option, + _shard_identifier: ShardIdentifier, + ) -> SgxResult> { + Ok(Vec::new()) + } + + fn get_trusted_peers_urls(&self) -> SgxResult> { + Ok(vec![]) + } +} + +impl EnclaveMetricsOCallApi for ProposeToImportOCallApi { + fn update_metric(&self, _metric: Metric) -> SgxResult<()> { + Ok(()) + } +} diff --git a/bitacross-worker/enclave-runtime/src/test/mocks/rpc_responder_mock.rs b/bitacross-worker/enclave-runtime/src/test/mocks/rpc_responder_mock.rs new file mode 100644 index 0000000000..d466e35a91 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/test/mocks/rpc_responder_mock.rs @@ -0,0 +1,75 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use itc_direct_rpc_server::{DirectRpcResult, RpcHash, SendRpcResponse}; +use itp_types::TrustedOperationStatus; +use std::{marker::PhantomData, vec::Vec}; + +pub struct RpcResponderMock { + _hash: PhantomData, +} + +impl RpcResponderMock { + pub fn new() -> Self { + RpcResponderMock { _hash: PhantomData } + } +} +impl Default for RpcResponderMock { + fn default() -> Self { + Self::new() + } +} + +impl SendRpcResponse for RpcResponderMock +where + Hash: RpcHash, +{ + type Hash = Hash; + + fn update_status_event( + &self, + _hash: Self::Hash, + _status_update: TrustedOperationStatus, + ) -> DirectRpcResult<()> { + Ok(()) + } + + fn send_state(&self, _hash: Self::Hash, _state_encoded: Vec) -> DirectRpcResult<()> { + Ok(()) + } + + fn update_force_wait(&self, _hash: Self::Hash, _force_wait: bool) -> DirectRpcResult<()> { + Ok(()) + } + + fn update_connection_state( + &self, + _hash: Self::Hash, + _encoded_value: Vec, + _force_wait: bool, + ) -> DirectRpcResult<()> { + Ok(()) + } + + fn swap_hash(&self, _old_hash: Self::Hash, _new_hash: Self::Hash) -> DirectRpcResult<()> { + Ok(()) + } + + fn is_force_wait(&self, _hash: Self::Hash) -> bool { + false + } +} diff --git a/bitacross-worker/enclave-runtime/src/test/mocks/types.rs b/bitacross-worker/enclave-runtime/src/test/mocks/types.rs new file mode 100644 index 0000000000..ae939c53e4 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/test/mocks/types.rs @@ -0,0 +1,114 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Type definitions for testing. Includes various mocks. + +use crate::test::mocks::{ + peer_updater_mock::PeerUpdaterMock, rpc_responder_mock::RpcResponderMock, +}; +use ita_sgx_runtime::Runtime; +use ita_stf::{Getter, Stf, TrustedCallSigned}; +use itc_parentchain::block_import_dispatcher::trigger_parentchain_block_import_mock::TriggerParentchainBlockImportMock; +use itp_node_api::metadata::{metadata_mocks::NodeMetadataMock, provider::NodeMetadataRepository}; +use itp_sgx_crypto::{mocks::KeyRepositoryMock, Aes}; +use itp_sgx_externalities::SgxExternalities; +use itp_stf_executor::executor::StfExecutor; +use itp_stf_primitives::types::TrustedOperation; +use itp_test::mock::{ + handle_state_mock::HandleStateMock, metrics_ocall_mock::MetricsOCallMock, + onchain_mock::OnchainMock, +}; +use itp_top_pool::basic_pool::BasicPool; +use itp_top_pool_author::{ + api::SidechainApi, + author::Author, + top_filter::{AllowAllTopsFilter, DirectCallsOnlyFilter}, +}; +use itp_types::{Block as ParentchainBlock, SignedBlock as SignedParentchainBlock}; +use its_primitives::types::SignedBlock as SignedSidechainBlock; +use its_sidechain::{aura::block_importer::BlockImporter, block_composer::BlockComposer}; +use primitive_types::H256; +use sgx_crypto_helper::rsa3072::Rsa3072KeyPair; +use sp_core::ed25519 as spEd25519; + +pub type TestSigner = spEd25519::Pair; +pub type TestShieldingKey = Rsa3072KeyPair; +pub type TestStateKey = Aes; + +pub type TestGetter = Getter; +pub type TestCall = TrustedCallSigned; +pub type TestStf = Stf; + +pub type TestShieldingKeyRepo = KeyRepositoryMock; + +pub type TestStateKeyRepo = KeyRepositoryMock; + +pub type TestStateHandler = HandleStateMock; + +pub type TestOCallApi = OnchainMock; + +pub type TestParentchainBlockImportTrigger = + TriggerParentchainBlockImportMock; + +pub type TestNodeMetadataRepository = NodeMetadataRepository; + +pub type TestStfExecutor = StfExecutor< + TestOCallApi, + TestStateHandler, + TestNodeMetadataRepository, + TestStf, + TrustedCallSigned, + Getter, +>; + +pub type TestRpcResponder = RpcResponderMock; + +pub type TestTopPool = BasicPool< + SidechainApi, + ParentchainBlock, + TestRpcResponder, + TrustedOperation, +>; + +pub type TestTopPoolAuthor = Author< + TestTopPool, + AllowAllTopsFilter, + DirectCallsOnlyFilter, + TestStateHandler, + TestShieldingKeyRepo, + MetricsOCallMock, + TrustedCallSigned, + Getter, +>; + +pub type TestBlockComposer = + BlockComposer; + +pub type TestBlockImporter = BlockImporter< + TestSigner, + ParentchainBlock, + SignedSidechainBlock, + TestOCallApi, + HandleStateMock, + TestStateKeyRepo, + TestTopPoolAuthor, + TestParentchainBlockImportTrigger, + PeerUpdaterMock, + TrustedCallSigned, + Getter, +>; diff --git a/bitacross-worker/enclave-runtime/src/test/mod.rs b/bitacross-worker/enclave-runtime/src/test/mod.rs new file mode 100644 index 0000000000..6f3d7a252e --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/test/mod.rs @@ -0,0 +1,34 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pub mod cert_tests; +pub mod direct_rpc_tests; +pub mod enclave_signer_tests; +#[cfg(feature = "evm")] +pub mod evm_pallet_tests; +pub mod fixtures; +pub mod ipfs_tests; +pub mod mocks; +pub mod sidechain_aura_tests; +pub mod sidechain_event_tests; +mod state_getter_tests; +pub mod tests_main; +pub mod top_pool_tests; + +#[cfg(feature = "teeracle")] +pub mod teeracle_tests; diff --git a/bitacross-worker/enclave-runtime/src/test/sidechain_aura_tests.rs b/bitacross-worker/enclave-runtime/src/test/sidechain_aura_tests.rs new file mode 100644 index 0000000000..36ad0f69b9 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/test/sidechain_aura_tests.rs @@ -0,0 +1,287 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + test::{ + fixtures::{ + components::{ + create_ocall_api, create_top_pool, encrypt_trusted_operation, sign_trusted_call, + }, + initialize_test_state::init_state, + test_setup::{enclave_call_signer, TestStf}, + }, + mocks::{ + peer_updater_mock::PeerUpdaterMock, + propose_to_import_call_mock::ProposeToImportOCallApi, types::*, + }, + }, + top_pool_execution::{exec_aura_on_slot, send_blocks_and_extrinsics}, +}; +use codec::Decode; +use ita_stf::{ + test_genesis::{endowed_account, second_endowed_account, unendowed_account}, + Balance, Getter, TrustedCall, TrustedCallSigned, +}; +use itc_parentchain_test::ParentchainHeaderBuilder; +use itp_node_api::metadata::{metadata_mocks::NodeMetadataMock, provider::NodeMetadataRepository}; +use itp_ocall_api::EnclaveAttestationOCallApi; +use itp_settings::{ + sidechain::SLOT_DURATION, + worker_mode::{ProvideWorkerMode, WorkerMode, WorkerModeProvider}, +}; +use itp_sgx_crypto::{Aes, ShieldingCryptoEncrypt, StateCrypto}; +use itp_sgx_externalities::SgxExternalitiesDiffType; +use itp_stf_interface::system_pallet::{SystemPalletAccountInterface, SystemPalletEventInterface}; +use itp_stf_primitives::types::{StatePayload, TrustedOperation}; +use itp_stf_state_handler::handle_state::HandleState; +use itp_test::mock::{handle_state_mock::HandleStateMock, metrics_ocall_mock::MetricsOCallMock}; +use itp_time_utils::duration_now; +use itp_top_pool_author::{ + top_filter::{AllowAllTopsFilter, DirectCallsOnlyFilter}, + traits::AuthorApi, +}; +use itp_types::{AccountId, Block as ParentchainBlock, RsaRequest, ShardIdentifier}; +use its_block_verification::slot::slot_from_timestamp_and_duration; +use its_primitives::{traits::Block, types::SignedBlock as SignedSidechainBlock}; +use its_sidechain::{aura::proposer_factory::ProposerFactory, slots::SlotInfo}; +use jsonrpc_core::futures::executor; +use lc_scheduled_enclave::ScheduledEnclaveMock; +use litentry_primitives::Identity; +use log::*; +use primitive_types::H256; +use sgx_crypto_helper::RsaKeyPair; +use sp_core::{ed25519, Pair}; +use std::{sync::Arc, vec, vec::Vec}; + +/// Integration test for sidechain block production and block import. +/// (requires Sidechain mode) +/// +/// - Create trusted calls and add them to the TOP pool. +/// - Run AURA on a valid and claimed slot, which executes the trusted operations and produces a new block. +/// - Import the new sidechain block, which updates the state. +pub fn produce_sidechain_block_and_import_it() { + // Test can only be run in Sidechain mode + if WorkerModeProvider::worker_mode() != WorkerMode::Sidechain { + info!("Ignoring sidechain block production test: Not in sidechain mode"); + return + } + + let _ = env_logger::builder().is_test(true).try_init(); + info!("Setting up test."); + + let signer = TestSigner::from_seed(b"42315678901234567890123456789012"); + let shielding_key = TestShieldingKey::new().unwrap(); + let state_key = TestStateKey::new([3u8; 16], [1u8; 16]); + let shielding_key_repo = Arc::new(TestShieldingKeyRepo::new(shielding_key)); + let state_key_repo = Arc::new(TestStateKeyRepo::new(state_key)); + let parentchain_header = ParentchainHeaderBuilder::default().build(); + + let ocall_api = create_ocall_api(&parentchain_header, &signer); + + info!("Initializing state and shard.."); + let state_handler = Arc::new(TestStateHandler::default()); + let enclave_call_signer = enclave_call_signer(&shielding_key); + let (_, shard_id) = init_state(state_handler.as_ref(), enclave_call_signer.public().into()); + let shards = vec![shard_id]; + + let node_metadata_repo = Arc::new(NodeMetadataRepository::new(NodeMetadataMock::new())); + let stf_executor = Arc::new(TestStfExecutor::new( + ocall_api.clone(), + state_handler.clone(), + node_metadata_repo, + )); + let top_pool = create_top_pool(); + + let (sender, _receiver) = std::sync::mpsc::sync_channel(1000); + + let metrics_ocall_mock = Arc::new(MetricsOCallMock::default()); + + let top_pool_author = Arc::new(TestTopPoolAuthor::new( + top_pool, + AllowAllTopsFilter::::new(), + DirectCallsOnlyFilter::::new(), + state_handler.clone(), + shielding_key_repo, + metrics_ocall_mock.clone(), + Arc::new(sender), + )); + let parentchain_block_import_trigger = Arc::new(TestParentchainBlockImportTrigger::default()); + let peer_updater_mock = Arc::new(PeerUpdaterMock {}); + let block_importer = Arc::new(TestBlockImporter::new( + state_handler.clone(), + state_key_repo.clone(), + top_pool_author.clone(), + parentchain_block_import_trigger.clone(), + ocall_api.clone(), + peer_updater_mock, + )); + let block_composer = Arc::new(TestBlockComposer::new(signer, state_key_repo)); + let proposer_environment = ProposerFactory::new( + top_pool_author.clone(), + stf_executor, + block_composer, + metrics_ocall_mock, + ); + + info!("Create trusted operations.."); + let sender = endowed_account(); + let sender_with_low_balance = second_endowed_account(); + let receiver = unendowed_account(); + let transfered_amount: Balance = 1000; + let trusted_operation = encrypted_trusted_operation_transfer_balance( + ocall_api.as_ref(), + &shard_id, + &shielding_key, + sender, + receiver.public().into(), + transfered_amount, + ); + let invalid_trusted_operation = encrypted_trusted_operation_transfer_balance( + ocall_api.as_ref(), + &shard_id, + &shielding_key, + sender_with_low_balance, + receiver.public().into(), + ita_stf::test_genesis::SECOND_ENDOWED_ACC_FUNDS + 1, + ); + info!("Add trusted operations to TOP pool.."); + executor::block_on(top_pool_author.submit_top(RsaRequest::new(shard_id, trusted_operation))) + .unwrap(); + executor::block_on( + top_pool_author.submit_top(RsaRequest::new(shard_id, invalid_trusted_operation)), + ) + .unwrap(); + + // Ensure we have exactly two trusted calls in our TOP pool, and no getters. + assert_eq!(2, top_pool_author.get_pending_trusted_calls(shard_id).len()); + assert!(top_pool_author.get_pending_getters(shard_id).is_empty()); + + info!("Setup AURA SlotInfo"); + let timestamp = duration_now(); + let slot = slot_from_timestamp_and_duration(duration_now(), SLOT_DURATION); + let ends_at = timestamp + SLOT_DURATION; + let slot_info = SlotInfo::new( + slot, + timestamp, + SLOT_DURATION, + ends_at, + parentchain_header.clone(), + None, + None, + ); + + info!("Test setup is done."); + + let state_hash_before_block_production = get_state_hash(state_handler.as_ref(), &shard_id); + let scheduled_enclave = Arc::new(ScheduledEnclaveMock::default()); + + info!("Executing AURA on slot.."); + let (blocks, opaque_calls) = + exec_aura_on_slot::<_, ParentchainBlock, SignedSidechainBlock, _, _, _, _, _, _, _>( + slot_info, + signer, + ocall_api, + parentchain_block_import_trigger.clone(), + None::>, + None::>, + proposer_environment, + shards, + scheduled_enclave, + state_handler.clone(), + ) + .unwrap(); + + assert_eq!(1, blocks.len()); + assert_eq!( + state_hash_before_block_production, + get_state_hash(state_handler.as_ref(), &shard_id) + ); + + let (apriori_state_hash_in_block, aposteriori_state_hash_in_block) = + get_state_hashes_from_block(blocks.first().unwrap(), &state_key); + assert_ne!(state_hash_before_block_production, aposteriori_state_hash_in_block); + assert_eq!(state_hash_before_block_production, apriori_state_hash_in_block); + + // Ensure we have triggered the parentchain block import, because we claimed the slot. + assert!(parentchain_block_import_trigger.has_import_been_called()); + + // Ensure that invalid calls are removed from pool. Valid calls should only be removed upon block import. + assert_eq!(1, top_pool_author.get_pending_trusted_calls(shard_id).len()); + + info!("Executed AURA successfully. Sending blocks and extrinsics.."); + let propose_to_block_import_ocall_api = + Arc::new(ProposeToImportOCallApi::new(parentchain_header, block_importer)); + + send_blocks_and_extrinsics::( + blocks, + opaque_calls, + propose_to_block_import_ocall_api, + ) + .unwrap(); + + // After importing the sidechain block, the trusted operation should be removed. + assert!(top_pool_author.get_pending_trusted_calls(shard_id).is_empty()); + + // After importing the block, the state hash must be changed. + // We don't have a way to directly compare state hashes, because calculating the state hash + // would also involve applying set_last_block action, which updates the state upon import. + assert_ne!( + state_hash_before_block_production, + get_state_hash(state_handler.as_ref(), &shard_id) + ); + + let (mut state, _) = state_handler.load_cloned(&shard_id).unwrap(); + let free_balance = TestStf::get_account_data(&mut state, &receiver.public().into()).free; + assert_eq!(free_balance, transfered_amount); + assert!(TestStf::get_event_count(&mut state) > 0); + assert!(!TestStf::get_events(&mut state).is_empty()); +} + +fn encrypted_trusted_operation_transfer_balance< + AttestationApi: EnclaveAttestationOCallApi, + ShieldingKey: ShieldingCryptoEncrypt, +>( + attestation_api: &AttestationApi, + shard_id: &ShardIdentifier, + shielding_key: &ShieldingKey, + from: ed25519::Pair, + to: AccountId, + amount: Balance, +) -> Vec { + let call = TrustedCall::balance_transfer(Identity::Substrate(from.public().into()), to, amount); + let call_signed = sign_trusted_call(&call, attestation_api, shard_id, from); + let trusted_operation = TrustedOperation::::direct_call(call_signed); + encrypt_trusted_operation(shielding_key, &trusted_operation) +} + +fn get_state_hashes_from_block( + signed_block: &SignedSidechainBlock, + state_key: &Aes, +) -> (H256, H256) { + let mut encrypted_state_diff = signed_block.block.block_data().encrypted_state_diff.clone(); + state_key.decrypt(&mut encrypted_state_diff).unwrap(); + let decoded_state = + StatePayload::::decode(&mut encrypted_state_diff.as_slice()) + .unwrap(); + (decoded_state.state_hash_apriori(), decoded_state.state_hash_aposteriori()) +} + +fn get_state_hash(state_handler: &HandleStateMock, shard_id: &ShardIdentifier) -> H256 { + let (_, state_hash) = state_handler.load_cloned(shard_id).unwrap(); + state_hash +} diff --git a/bitacross-worker/enclave-runtime/src/test/sidechain_event_tests.rs b/bitacross-worker/enclave-runtime/src/test/sidechain_event_tests.rs new file mode 100644 index 0000000000..64294f0121 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/test/sidechain_event_tests.rs @@ -0,0 +1,190 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + test::{ + fixtures::{ + components::{create_ocall_api, create_top_pool}, + initialize_test_state::init_state, + test_setup::{enclave_call_signer, TestStf}, + }, + mocks::{ + peer_updater_mock::PeerUpdaterMock, + propose_to_import_call_mock::ProposeToImportOCallApi, types::*, + }, + }, + top_pool_execution::{exec_aura_on_slot, send_blocks_and_extrinsics}, +}; +use ita_sgx_runtime::Runtime; +use ita_stf::{helpers::set_block_number, Getter, TrustedCallSigned}; +use itc_parentchain_test::ParentchainHeaderBuilder; +use itp_node_api::metadata::{metadata_mocks::NodeMetadataMock, provider::NodeMetadataRepository}; +use itp_settings::{ + sidechain::SLOT_DURATION, + worker_mode::{ProvideWorkerMode, WorkerMode, WorkerModeProvider}, +}; +use itp_sgx_externalities::SgxExternalitiesTrait; +use itp_stf_interface::system_pallet::SystemPalletEventInterface; +use itp_stf_state_handler::handle_state::HandleState; +use itp_test::mock::metrics_ocall_mock::MetricsOCallMock; +use itp_time_utils::duration_now; +use itp_top_pool_author::top_filter::{AllowAllTopsFilter, DirectCallsOnlyFilter}; +use itp_types::Block as ParentchainBlock; +use its_block_verification::slot::slot_from_timestamp_and_duration; +use its_primitives::types::SignedBlock as SignedSidechainBlock; +use its_sidechain::{aura::proposer_factory::ProposerFactory, slots::SlotInfo}; +use lc_scheduled_enclave::ScheduledEnclaveMock; +use log::*; +use primitive_types::H256; +use sgx_crypto_helper::RsaKeyPair; +use sp_core::Pair; +use std::{sync::Arc, vec}; + +/// Integration test to ensure the events are reset upon block import. +/// Otherwise we will have an ever growing state. +/// (requires Sidechain mode) +pub fn ensure_events_get_reset_upon_block_proposal() { + // Test can only be run in Sidechain mode + if WorkerModeProvider::worker_mode() != WorkerMode::Sidechain { + info!("Ignoring sidechain block production test: Not in sidechain mode"); + return + } + + let _ = env_logger::builder().is_test(true).try_init(); + info!("Setting up test."); + + let signer = TestSigner::from_seed(b"42315678901234567890123456789012"); + let shielding_key = TestShieldingKey::new().unwrap(); + let state_key = TestStateKey::new([3u8; 16], [1u8; 16]); + let shielding_key_repo = Arc::new(TestShieldingKeyRepo::new(shielding_key)); + let state_key_repo = Arc::new(TestStateKeyRepo::new(state_key)); + let parentchain_header = ParentchainHeaderBuilder::default().build(); + + let ocall_api = create_ocall_api(&parentchain_header, &signer); + + info!("Initializing state and shard.."); + let state_handler = Arc::new(TestStateHandler::default()); + let enclave_call_signer = enclave_call_signer(&shielding_key); + let (_, shard_id) = init_state(state_handler.as_ref(), enclave_call_signer.public().into()); + let shards = vec![shard_id]; + + let node_metadata_repo = Arc::new(NodeMetadataRepository::new(NodeMetadataMock::new())); + let stf_executor = Arc::new(TestStfExecutor::new( + ocall_api.clone(), + state_handler.clone(), + node_metadata_repo, + )); + let top_pool = create_top_pool(); + let (sender, _receiver) = std::sync::mpsc::sync_channel(1000); + + let enclave_metrics_ocall_mock = Arc::new(MetricsOCallMock::default()); + + let top_pool_author = Arc::new(TestTopPoolAuthor::new( + top_pool, + AllowAllTopsFilter::::new(), + DirectCallsOnlyFilter::::new(), + state_handler.clone(), + shielding_key_repo, + enclave_metrics_ocall_mock.clone(), + Arc::new(sender), + )); + let parentchain_block_import_trigger = Arc::new(TestParentchainBlockImportTrigger::default()); + let peer_updater_mock = Arc::new(PeerUpdaterMock {}); + let block_importer = Arc::new(TestBlockImporter::new( + state_handler.clone(), + state_key_repo.clone(), + top_pool_author.clone(), + parentchain_block_import_trigger.clone(), + ocall_api.clone(), + peer_updater_mock, + )); + let block_composer = Arc::new(TestBlockComposer::new(signer, state_key_repo)); + let proposer_environment = ProposerFactory::new( + top_pool_author, + stf_executor, + block_composer, + enclave_metrics_ocall_mock, + ); + + // Add some events to the state. + let topic_hash = H256::from([7; 32]); + let event = frame_system::Event::::CodeUpdated; + let (lock, mut state) = state_handler.load_for_mutation(&shard_id).unwrap(); + state.execute_with(|| { + set_block_number(10); + frame_system::Pallet::::deposit_event_indexed( + &[topic_hash], + ita_sgx_runtime::RuntimeEvent::System(event), + ) + }); + state_handler.write_after_mutation(state.clone(), lock, &shard_id).unwrap(); + + // Check if state now really contains events and topics. + let (mut state, _) = state_handler.load_cloned(&shard_id).unwrap(); + assert_eq!(TestStf::get_event_count(&mut state), 1); + assert_eq!(TestStf::get_events(&mut state).len(), 1); + assert_eq!(TestStf::get_event_topics(&mut state, &topic_hash).len(), 1); + + info!("Setup AURA SlotInfo"); + let timestamp = duration_now(); + let slot = slot_from_timestamp_and_duration(duration_now(), SLOT_DURATION); + let ends_at = timestamp + SLOT_DURATION; + let slot_info = SlotInfo::new( + slot, + timestamp, + SLOT_DURATION, + ends_at, + parentchain_header.clone(), + None, + None, + ); + + let scheduled_enclave = Arc::new(ScheduledEnclaveMock::default()); + info!("Executing AURA on slot.."); + let (blocks, opaque_calls) = + exec_aura_on_slot::<_, ParentchainBlock, SignedSidechainBlock, _, _, _, _, _, _, _>( + slot_info, + signer, + ocall_api, + parentchain_block_import_trigger, + None::>, + None::>, + proposer_environment, + shards, + scheduled_enclave, + state_handler.clone(), + ) + .unwrap(); + + info!("Executed AURA successfully. Sending blocks and extrinsics.."); + let propose_to_block_import_ocall_api = + Arc::new(ProposeToImportOCallApi::new(parentchain_header, block_importer)); + + send_blocks_and_extrinsics::( + blocks, + opaque_calls, + propose_to_block_import_ocall_api, + ) + .unwrap(); + + // Ensure events have been reset. + let (mut state, _) = state_handler.load_cloned(&shard_id).unwrap(); + assert_eq!(TestStf::get_event_count(&mut state), 0); + assert_eq!(TestStf::get_event_topics(&mut state, &topic_hash).len(), 0); + assert_eq!(TestStf::get_events(&mut state).len(), 0); +} diff --git a/bitacross-worker/enclave-runtime/src/test/state_getter_tests.rs b/bitacross-worker/enclave-runtime/src/test/state_getter_tests.rs new file mode 100644 index 0000000000..f902061e9e --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/test/state_getter_tests.rs @@ -0,0 +1,53 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use codec::Decode; +use ita_sgx_runtime::Runtime; +use ita_stf::{ + test_genesis::{endowed_account, test_genesis_setup, ENDOWED_ACC_FUNDS}, + Balance, Getter, Stf, TrustedCallSigned, TrustedGetter, +}; +use itp_sgx_externalities::SgxExternalities; +use itp_stf_executor::state_getter::{GetState, StfStateGetter}; +use litentry_primitives::Identity; +use sp_core::Pair; + +type TestState = SgxExternalities; +type TestStf = Stf; +type TestStfStateGetter = StfStateGetter; + +pub fn state_getter_works() { + let sender = endowed_account(); + let signed_getter = TrustedGetter::free_balance(Identity::Substrate(sender.public().into())) + .sign(&sender.into()); + let mut state = test_state(); + + let encoded_balance = TestStfStateGetter::get_state(signed_getter.into(), &mut state) + .unwrap() + .unwrap(); + + let balance = Balance::decode(&mut encoded_balance.as_slice()).unwrap(); + + assert_eq!(balance, ENDOWED_ACC_FUNDS); +} + +fn test_state() -> TestState { + let mut state = TestState::default(); + test_genesis_setup(&mut state); + state +} diff --git a/bitacross-worker/enclave-runtime/src/test/teeracle_tests.rs b/bitacross-worker/enclave-runtime/src/test/teeracle_tests.rs new file mode 100644 index 0000000000..bd9a4c8391 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/test/teeracle_tests.rs @@ -0,0 +1,50 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use codec::alloc::string::ToString; +use ita_oracle::{ + create_coin_gecko_oracle, create_coin_market_cap_oracle, + oracles::exchange_rate_oracle::GetExchangeRate, types::TradingPair, +}; +use itp_test::mock::metrics_ocall_mock::MetricsOCallMock; +use std::sync::Arc; + +pub(super) fn test_verify_get_exchange_rate_from_coin_gecko_works() { + // Get the exchange rate + let trading_pair = + TradingPair { crypto_currency: "DOT".to_string(), fiat_currency: "USD".to_string() }; + + let coin_gecko_oracle = create_coin_gecko_oracle(Arc::new(MetricsOCallMock::default())); + + let result = coin_gecko_oracle.get_exchange_rate(trading_pair.clone()); + assert!(result.is_ok()); +} + +/// Get exchange rate from coin market cap. Requires API key (therefore not suited for unit testing). +#[allow(unused)] +pub(super) fn test_verify_get_exchange_rate_from_coin_market_cap_works() { + // Get the exchange rate + let trading_pair = + TradingPair { crypto_currency: "DOT".to_string(), fiat_currency: "USD".to_string() }; + + let coin_market_cap_oracle = + create_coin_market_cap_oracle(Arc::new(MetricsOCallMock::default())); + + let result = coin_market_cap_oracle.get_exchange_rate(trading_pair.clone()); + assert!(result.is_ok()); +} diff --git a/bitacross-worker/enclave-runtime/src/test/tests_main.rs b/bitacross-worker/enclave-runtime/src/test/tests_main.rs new file mode 100644 index 0000000000..8632bfbeea --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/test/tests_main.rs @@ -0,0 +1,810 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#[cfg(feature = "evm")] +use crate::test::evm_pallet_tests; + +use crate::{ + rpc, + sync::tests::{enclave_rw_lock_works, sidechain_rw_lock_works}, + test::{ + cert_tests::*, + direct_rpc_tests, enclave_signer_tests, + fixtures::test_setup::{ + enclave_call_signer, test_setup, TestStf, TestStfExecutor, TestTopPoolAuthor, + }, + mocks::types::TestStateKeyRepo, + sidechain_aura_tests, sidechain_event_tests, state_getter_tests, top_pool_tests, + }, + tls_ra, +}; +use codec::Decode; +use ita_sgx_runtime::Parentchain; +use ita_stf::{ + helpers::{account_key_hash, set_block_number}, + stf_sgx_tests, + test_genesis::{endowed_account as funded_pair, unendowed_account}, + AccountInfo, Getter, State, TrustedCall, TrustedCallSigned, TrustedGetter, +}; +use itp_node_api::metadata::{metadata_mocks::NodeMetadataMock, provider::NodeMetadataRepository}; +use itp_sgx_crypto::{Aes, StateCrypto}; +use itp_sgx_externalities::{SgxExternalitiesDiffType, SgxExternalitiesTrait, StateHash}; +use itp_stf_executor::{ + executor_tests as stf_executor_tests, traits::StateUpdateProposer, BatchExecutionResult, +}; +use itp_stf_interface::{ + parentchain_pallet::ParentchainPalletInterface, + system_pallet::{SystemPalletAccountInterface, SystemPalletEventInterface}, + StateCallInterface, +}; +use itp_stf_primitives::{ + traits::TrustedCallSigning, + types::{ShardIdentifier, StatePayload, TrustedOperation}, +}; +use itp_stf_state_handler::handle_state::HandleState; +use itp_test::mock::handle_state_mock; +use itp_top_pool_author::{test_utils::submit_operation_to_top_pool, traits::AuthorApi}; +use itp_types::{AccountId, Balance, Block, Header}; +use its_primitives::{ + traits::{ + Block as BlockTrait, BlockData, Header as SidechainHeaderTrait, + SignedBlock as SignedBlockTrait, + }, + types::block::SignedBlock, +}; +use its_sidechain::{ + block_composer::{BlockComposer, ComposeBlock}, + state::SidechainSystemExt, +}; +use litentry_primitives::Identity; +use sgx_tunittest::*; +use sgx_types::size_t; +use sp_core::{crypto::Pair, ed25519 as spEd25519, H256}; +use sp_runtime::traits::Header as HeaderT; +use std::{string::String, sync::Arc, time::Duration, vec::Vec}; +#[no_mangle] +pub extern "C" fn test_main_entrance() -> size_t { + rsgx_unit_tests!( + itp_attestation_handler::attestation_handler::tests::decode_spid_works, + stf_sgx_tests::enclave_account_initialization_works, + stf_sgx_tests::shield_funds_increments_signer_account_nonce, + stf_sgx_tests::test_root_account_exists_after_initialization, + itp_stf_state_handler::test::sgx_tests::test_write_and_load_state_works, + itp_stf_state_handler::test::sgx_tests::test_sgx_state_decode_encode_works, + itp_stf_state_handler::test::sgx_tests::test_encrypt_decrypt_state_type_works, + itp_stf_state_handler::test::sgx_tests::test_write_access_locks_read_until_finished, + itp_stf_state_handler::test::sgx_tests::test_ensure_subsequent_state_loads_have_same_hash, + itp_stf_state_handler::test::sgx_tests::test_state_handler_file_backend_is_initialized, + itp_stf_state_handler::test::sgx_tests::test_multiple_state_updates_create_snapshots_up_to_cache_size, + itp_stf_state_handler::test::sgx_tests::test_state_files_from_handler_can_be_loaded_again, + itp_stf_state_handler::test::sgx_tests::test_file_io_get_state_hash_works, + itp_stf_state_handler::test::sgx_tests::test_list_state_ids_ignores_files_not_matching_the_pattern, + itp_stf_state_handler::test::sgx_tests::test_in_memory_state_initializes_from_shard_directory, + itp_sgx_crypto::tests::aes_sealing_works, + itp_sgx_crypto::tests::using_get_aes_repository_twice_initializes_key_only_once, + itp_sgx_crypto::tests::ed25529_sealing_works, + itp_sgx_crypto::tests::using_get_ed25519_repository_twice_initializes_key_only_once, + itp_sgx_crypto::tests::rsa3072_sealing_works, + itp_sgx_crypto::tests::using_get_rsa3072_repository_twice_initializes_key_only_once, + test_compose_block, + test_submit_trusted_call_to_top_pool, + test_submit_trusted_getter_to_top_pool, + test_differentiate_getter_and_call_works, + test_create_block_and_confirmation_works, + test_create_state_diff, + test_executing_call_updates_account_nonce, + test_call_set_update_parentchain_block, + test_invalid_nonce_call_is_not_executed, + test_signature_must_match_public_sender_in_call, + test_non_root_shielding_call_is_not_executed, + test_shielding_call_with_enclave_self_is_executed, + test_retrieve_events, + test_retrieve_event_count, + test_reset_events, + rpc::worker_api_direct::tests::test_given_io_handler_methods_then_retrieve_all_names_as_string, + handle_state_mock::tests::initialized_shards_list_is_empty, + handle_state_mock::tests::shard_exists_after_inserting, + handle_state_mock::tests::from_shard_works, + handle_state_mock::tests::initialize_creates_default_state, + handle_state_mock::tests::load_mutate_and_write_works, + handle_state_mock::tests::ensure_subsequent_state_loads_have_same_hash, + handle_state_mock::tests::ensure_encode_and_encrypt_does_not_affect_state_hash, + handle_state_mock::tests::migrate_shard_works, + // mra cert tests + test_verify_mra_cert_should_work, + test_verify_wrong_cert_is_err, + test_given_wrong_platform_info_when_verifying_attestation_report_then_return_error, + // sync tests + sidechain_rw_lock_works, + enclave_rw_lock_works, + // unit tests of stf_executor + stf_executor_tests::propose_state_update_always_executes_preprocessing_step, + stf_executor_tests::propose_state_update_executes_no_trusted_calls_given_no_time, + stf_executor_tests::propose_state_update_executes_only_one_trusted_call_given_not_enough_time, + stf_executor_tests::propose_state_update_executes_all_calls_given_enough_time, + enclave_signer_tests::enclave_signer_signatures_are_valid, + enclave_signer_tests::derive_key_is_deterministic, + enclave_signer_tests::nonce_is_computed_correctly, + state_getter_tests::state_getter_works, + // sidechain integration tests + sidechain_aura_tests::produce_sidechain_block_and_import_it, + sidechain_event_tests::ensure_events_get_reset_upon_block_proposal, + top_pool_tests::process_indirect_call_in_top_pool, + top_pool_tests::submit_shielding_call_to_top_pool, + // tls_ra unit tests + tls_ra::seal_handler::test::seal_shielding_key_works, + tls_ra::seal_handler::test::seal_shielding_key_fails_for_invalid_key, + tls_ra::seal_handler::test::unseal_seal_shielding_key_works, + tls_ra::seal_handler::test::seal_state_key_works, + tls_ra::seal_handler::test::seal_state_key_fails_for_invalid_key, + tls_ra::seal_handler::test::unseal_seal_state_key_works, + tls_ra::seal_handler::test::seal_state_works, + tls_ra::seal_handler::test::seal_state_fails_for_invalid_state, + tls_ra::seal_handler::test::unseal_seal_state_works, + tls_ra::tests::test_state_and_key_provisioning, + tls_ra::tests::test_tls_ra_server_client_networking, + // RPC tests + direct_rpc_tests::get_state_request_works, + + // EVM tests + run_evm_tests, + + // light-client-test + itc_parentchain::light_client::io::sgx_tests::init_parachain_light_client_works, + itc_parentchain::light_client::io::sgx_tests::sealing_creates_backup, + + // these unit test (?) need an ipfs node running.. + // ipfs::test_creates_ipfs_content_struct_works, + // ipfs::test_verification_ok_for_correct_content, + // ipfs::test_verification_fails_for_incorrect_content, + // test_ocall_read_write_ipfs, + + // Teeracle tests + run_teeracle_tests, + ) +} + +#[cfg(feature = "teeracle")] +fn run_teeracle_tests() { + use super::teeracle_tests::*; + test_verify_get_exchange_rate_from_coin_gecko_works(); + // Disabled - requires API key, cannot run locally + //test_verify_get_exchange_rate_from_coin_market_cap_works(); +} + +#[cfg(not(feature = "teeracle"))] +fn run_teeracle_tests() {} + +#[cfg(feature = "evm")] +fn run_evm_tests() { + evm_pallet_tests::test_evm_call(); + evm_pallet_tests::test_evm_counter(); + evm_pallet_tests::test_evm_create(); + evm_pallet_tests::test_evm_create2(); +} +#[cfg(not(feature = "evm"))] +fn run_evm_tests() {} + +fn test_compose_block() { + // given + let (_, _, shard, _, _, state_handler, _) = test_setup(); + let block_composer = BlockComposer::::new( + test_account(), + Arc::new(TestStateKeyRepo::new(state_key())), + ); + + let signed_top_hashes: Vec = vec![[94; 32].into(), [1; 32].into()].to_vec(); + + let (mut state, _) = state_handler.load_cloned(&shard).unwrap(); + state.set_block_number(&1); + let state_hash_before_execution = state.hash(); + + // when + let signed_block = block_composer + .compose_block( + &latest_parentchain_header(), + signed_top_hashes, + shard, + state_hash_before_execution, + &state, + ) + .unwrap(); + + // then + assert!(signed_block.verify_signature()); + assert_eq!(signed_block.block().header().block_number(), 1); +} + +fn test_submit_trusted_call_to_top_pool() { + // given + let (top_pool_author, _, shard, mrenclave, shielding_key, ..) = test_setup(); + + let sender = funded_pair(); + + let signed_call = TrustedCall::balance_set_balance( + Identity::Substrate(sender.public().into()), + sender.public().into(), + 42, + 42, + ) + .sign(&sender.into(), 0, &mrenclave, &shard); + let trusted_operation = direct_top(signed_call); + + // when + submit_operation_to_top_pool( + top_pool_author.as_ref(), + &trusted_operation, + &shielding_key, + shard, + false, + ) + .unwrap(); + + let calls = top_pool_author.get_pending_trusted_calls(shard); + + // then + assert_eq!(calls[0], trusted_operation); +} + +// The TOP pool can hold any TrustedOperation, which at the moment also includes Getters. +// However, in reality we don't submit getters to the TOP pool anymore, they are executed immediately. +// The filter set in the TOP pool author prevents getters from being submitted. +// In this test however, we set the filter to `AllowAllTops`, so getters can be submitted. +// We want to keep this back door open, in case we would want to submit getter into the TOP pool again in the future. +fn test_submit_trusted_getter_to_top_pool() { + // given + let (top_pool_author, _, shard, _, shielding_key, ..) = test_setup(); + + let sender = funded_pair(); + + let signed_getter = TrustedGetter::free_balance(Identity::Substrate(sender.public().into())) + .sign(&sender.into()); + + // when + submit_operation_to_top_pool( + top_pool_author.as_ref(), + &TrustedOperation::::get(Getter::trusted(signed_getter.clone())), + &shielding_key, + shard, + false, + ) + .unwrap(); + + let getters = top_pool_author.get_pending_getters(shard); + + // then + assert_eq!( + getters[0], + TrustedOperation::::get(Getter::trusted(signed_getter)) + ); +} + +fn test_differentiate_getter_and_call_works() { + // given + let (top_pool_author, _, shard, mrenclave, shielding_key, ..) = test_setup(); + + // create accounts + let sender = funded_pair(); + + let signed_getter = TrustedGetter::free_balance(Identity::Substrate(sender.public().into())) + .sign(&sender.into()); + + let signed_call = TrustedCall::balance_set_balance( + Identity::Substrate(sender.public().into()), + sender.public().into(), + 42, + 42, + ) + .sign(&sender.into(), 0, &mrenclave, &shard); + let trusted_operation = direct_top(signed_call); + + // when + submit_operation_to_top_pool( + top_pool_author.as_ref(), + &TrustedOperation::::get(Getter::trusted(signed_getter.clone())), + &shielding_key, + shard, + false, + ) + .unwrap(); + submit_operation_to_top_pool( + top_pool_author.as_ref(), + &trusted_operation, + &shielding_key, + shard, + false, + ) + .unwrap(); + + let calls = top_pool_author.get_pending_trusted_calls(shard); + let getters = top_pool_author.get_pending_getters(shard); + + // then + assert_eq!(calls[0], trusted_operation); + assert_eq!( + getters[0], + TrustedOperation::::get(Getter::trusted(signed_getter)) + ); +} + +fn test_create_block_and_confirmation_works() { + // given + let (top_pool_author, _, shard, mrenclave, shielding_key, _, stf_executor) = test_setup(); + + let block_composer = BlockComposer::::new( + test_account(), + Arc::new(TestStateKeyRepo::new(state_key())), + ); + + let sender = funded_pair(); + let receiver = unfunded_public(); + + let signed_call = TrustedCall::balance_transfer( + Identity::Substrate(sender.public().into()), + receiver.into(), + 1000, + ) + .sign(&sender.into(), 0, &mrenclave, &shard); + let trusted_operation = direct_top(signed_call); + + let (top_hash, _) = submit_operation_to_top_pool( + top_pool_author.as_ref(), + &trusted_operation, + &shielding_key, + shard, + false, + ) + .unwrap(); + + // when + let execution_result = execute_trusted_calls(&shard, stf_executor.as_ref(), &top_pool_author); + + let executed_operation_hashes = execution_result.get_executed_operation_hashes().to_vec(); + + let signed_block = block_composer + .compose_block( + &latest_parentchain_header(), + executed_operation_hashes, + shard, + execution_result.state_hash_before_execution, + &execution_result.state_after_execution, + ) + .unwrap(); + + // then + assert!(signed_block.verify_signature()); + assert_eq!(signed_block.block().header().block_number(), 1); + assert_eq!(signed_block.block().block_data().signed_top_hashes()[0], top_hash); +} + +fn test_create_state_diff() { + // given + let (top_pool_author, _, shard, mrenclave, shielding_key, _, stf_executor) = test_setup(); + + let block_composer = BlockComposer::::new( + test_account(), + Arc::new(TestStateKeyRepo::new(state_key())), + ); + + let sender = funded_pair(); + let receiver = unfunded_public(); + const TX_AMOUNT: Balance = 1_000_000_000_000; + let signed_call = TrustedCall::balance_transfer( + Identity::Substrate(sender.public().into()), + receiver.into(), + TX_AMOUNT, + ) + .sign(&sender.into(), 0, &mrenclave, &shard); + let trusted_operation = direct_top(signed_call); + + submit_operation_to_top_pool( + top_pool_author.as_ref(), + &trusted_operation, + &shielding_key, + shard, + false, + ) + .unwrap(); + + // when + let execution_result = execute_trusted_calls(&shard, stf_executor.as_ref(), &top_pool_author); + + let executed_operation_hashes = execution_result.get_executed_operation_hashes().to_vec(); + + let signed_block = block_composer + .compose_block( + &latest_parentchain_header(), + executed_operation_hashes, + shard, + execution_result.state_hash_before_execution, + &execution_result.state_after_execution, + ) + .unwrap(); + + let encrypted_state_diff = encrypted_state_diff_from_encrypted( + signed_block.block().block_data().encrypted_state_diff(), + ); + let state_diff = encrypted_state_diff.state_update(); + + // then + let sender_acc_info: AccountInfo = + get_from_state_diff(state_diff, &account_key_hash::(&sender.public().into())); + + let receiver_acc_info: AccountInfo = + get_from_state_diff(state_diff, &account_key_hash::(&receiver.into())); + + // state diff should consist of the following updates: + // (last_hash, sidechain block_number, sender_funds, receiver_funds, fee_recipient account [no clear, after polkadot_v0.9.26 update], events, frame_system::LastRuntimeUpgradeInfo,) + assert_eq!(state_diff.len(), 8); + assert_eq!(receiver_acc_info.data.free, TX_AMOUNT); + assert_eq!( + sender_acc_info.data.free, + ita_stf::test_genesis::ENDOWED_ACC_FUNDS - TX_AMOUNT - ita_stf::STF_TX_FEE + ); +} + +fn test_executing_call_updates_account_nonce() { + // given + let (top_pool_author, _, shard, mrenclave, shielding_key, _, stf_executor) = test_setup(); + + let sender = funded_pair(); + let receiver = unfunded_public(); + + let trusted_operation = TrustedCall::balance_transfer( + Identity::Substrate(sender.public().into()), + receiver.into(), + 1000, + ) + .sign(&sender.into(), 0, &mrenclave, &shard) + .into_trusted_operation(false); + + submit_operation_to_top_pool( + top_pool_author.as_ref(), + &trusted_operation, + &shielding_key, + shard, + false, + ) + .unwrap(); + + // when + let mut execution_result = + execute_trusted_calls(&shard, stf_executor.as_ref(), &top_pool_author); + + let nonce = TestStf::get_account_nonce( + &mut execution_result.state_after_execution, + &sender.public().into(), + ); + assert_eq!(nonce, 1); +} + +fn test_call_set_update_parentchain_block() { + let (_, _, shard, _, _, state_handler, _) = test_setup(); + let (mut state, _) = state_handler.load_cloned(&shard).unwrap(); + + let block_number = 3; + let parent_hash = H256::from([1; 32]); + + let header: Header = HeaderT::new( + block_number, + Default::default(), + Default::default(), + parent_hash, + Default::default(), + ); + + TestStf::update_parentchain_block(&mut state, header.clone()).unwrap(); + + assert_eq!(header.hash(), state.execute_with(Parentchain::block_hash)); + assert_eq!(parent_hash, state.execute_with(Parentchain::parent_hash)); + assert_eq!(block_number, state.execute_with(Parentchain::block_number)); +} + +fn test_signature_must_match_public_sender_in_call() { + // given + let (top_pool_author, _, shard, mrenclave, shielding_key, _, stf_executor) = test_setup(); + + // create accounts + let sender = funded_pair(); + let receiver = unfunded_public(); + + let trusted_operation = TrustedCall::balance_transfer( + Identity::Substrate(receiver.into()), + sender.public().into(), + 1000, + ) + .sign(&sender.into(), 10, &mrenclave, &shard) + .into_trusted_operation(true); + + submit_operation_to_top_pool( + top_pool_author.as_ref(), + &trusted_operation, + &shielding_key, + shard, + false, + ) + .unwrap(); + + let executed_batch = execute_trusted_calls(&shard, stf_executor.as_ref(), &top_pool_author); + + // the top pool doesn't verify signatures, the call will only fail upon execution + assert!(!executed_batch.executed_operations[0].is_success()); +} + +fn test_invalid_nonce_call_is_not_executed() { + // given + let (top_pool_author, _, shard, mrenclave, shielding_key, _, stf_executor) = test_setup(); + + // create accounts + let sender = funded_pair(); + let receiver = unfunded_public(); + + let trusted_operation = TrustedCall::balance_transfer( + Identity::Substrate(sender.public().into()), + receiver.into(), + 1000, + ) + .sign(&sender.into(), 10, &mrenclave, &shard) + .into_trusted_operation(true); + + submit_operation_to_top_pool( + top_pool_author.as_ref(), + &trusted_operation, + &shielding_key, + shard, + false, + ) + .unwrap(); + + let executed_batch = execute_trusted_calls(&shard, stf_executor.as_ref(), &top_pool_author); + + // due to #1488, even invalid nonces will enter the pool ready state, so we can only verify that the call will fail + assert!(!executed_batch.executed_operations[0].is_success()); +} + +fn test_non_root_shielding_call_is_not_executed() { + // given + let (top_pool_author, _state, shard, mrenclave, shielding_key, _, stf_executor) = test_setup(); + + let sender = funded_pair(); + let sender_acc: AccountId = sender.public().into(); + + let signed_call = TrustedCall::balance_shield( + Identity::Substrate(sender_acc.clone().into()), + sender_acc, + 1000, + ) + .sign(&sender.into(), 0, &mrenclave, &shard); + + submit_operation_to_top_pool( + top_pool_author.as_ref(), + &direct_top(signed_call), + &shielding_key, + shard, + false, + ) + .unwrap(); + + // when + let executed_batch = execute_trusted_calls(&shard, stf_executor.as_ref(), &top_pool_author); + + // then + assert!(!executed_batch.executed_operations[0].is_success()); +} + +fn test_shielding_call_with_enclave_self_is_executed() { + let (top_pool_author, _state, shard, mrenclave, shielding_key, _, stf_executor) = test_setup(); + + let sender = funded_pair(); + let sender_account: AccountId = sender.public().into(); + let enclave_call_signer = enclave_call_signer(&shielding_key); + + let signed_call = TrustedCall::balance_shield( + Identity::Substrate(enclave_call_signer.public().into()), + sender_account, + 1000, + ) + .sign(&enclave_call_signer.into(), 0, &mrenclave, &shard); + let trusted_operation = + TrustedOperation::::indirect_call(signed_call); + + submit_operation_to_top_pool( + top_pool_author.as_ref(), + &trusted_operation, + &shielding_key, + shard, + false, + ) + .unwrap(); + + // when + let executed_batch = + execute_trusted_calls(&shard, stf_executor.as_ref(), top_pool_author.as_ref()); + + // then + assert_eq!(1, executed_batch.executed_operations.len()); + assert!(executed_batch.executed_operations[0].is_success()); +} + +pub fn test_retrieve_events() { + // given + let (_, mut state, shard, mrenclave, ..) = test_setup(); + let mut opaque_vec = Vec::new(); + let sender = funded_pair(); + let receiver = unendowed_account(); + let transfer_value: u128 = 1_000; + // Events will only get executed after genesis. + state.execute_with(|| set_block_number(100)); + + // Execute a transfer extrinsic to generate events via the Balance pallet. + let trusted_call = TrustedCall::balance_transfer( + Identity::Substrate(sender.public().into()), + receiver.public().into(), + transfer_value, + ) + .sign(&sender.into(), 0, &mrenclave, &shard); + let repo = Arc::new(NodeMetadataRepository::::default()); + let shard = ShardIdentifier::default(); + TestStf::execute_call( + &mut state, + &shard, + trusted_call, + Default::default(), + &mut opaque_vec, + repo, + ) + .unwrap(); + + assert_eq!(TestStf::get_events(&mut state).len(), 4); +} + +pub fn test_retrieve_event_count() { + let (_, mut state, shard, mrenclave, ..) = test_setup(); + let mut opaque_vec = Vec::new(); + let sender = funded_pair(); + let receiver = unendowed_account(); + let transfer_value: u128 = 1_000; + // Events will only get executed after genesis. + state.execute_with(|| set_block_number(100)); + + // Execute a transfer extrinsic to generate events via the Balance pallet. + let trusted_call = TrustedCall::balance_transfer( + Identity::Substrate(sender.public().into()), + receiver.public().into(), + transfer_value, + ) + .sign(&sender.into(), 0, &mrenclave, &shard); + + // when + let repo = Arc::new(NodeMetadataRepository::::default()); + let shard = ShardIdentifier::default(); + TestStf::execute_call( + &mut state, + &shard, + trusted_call, + Default::default(), + &mut opaque_vec, + repo, + ) + .unwrap(); + + let event_count = TestStf::get_event_count(&mut state); + assert_eq!(event_count, 4); +} + +pub fn test_reset_events() { + let (_, mut state, shard, mrenclave, ..) = test_setup(); + let mut opaque_vec = Vec::new(); + let sender = funded_pair(); + let receiver = unendowed_account(); + let transfer_value: u128 = 1_000; + // Events will only get executed after genesis. + state.execute_with(|| set_block_number(100)); + // Execute a transfer extrinsic to generate events via the Balance pallet. + let trusted_call = TrustedCall::balance_transfer( + Identity::Substrate(sender.public().into()), + receiver.public().into(), + transfer_value, + ) + .sign(&sender.into(), 0, &mrenclave, &shard); + let repo = Arc::new(NodeMetadataRepository::::default()); + let shard = ShardIdentifier::default(); + TestStf::execute_call( + &mut state, + &shard, + trusted_call, + Default::default(), + &mut opaque_vec, + repo, + ) + .unwrap(); + let receiver_acc_info = TestStf::get_account_data(&mut state, &receiver.public().into()); + assert_eq!(receiver_acc_info.free, transfer_value); + // Ensure that there really have been events generated. + assert_eq!(TestStf::get_events(&mut state).len(), 4); + + // Remove the events. + TestStf::reset_events(&mut state); + + // Ensure that the events storage has been cleared. + assert_eq!(TestStf::get_events(&mut state).len(), 0); +} + +fn execute_trusted_calls( + shard: &ShardIdentifier, + stf_executor: &TestStfExecutor, + top_pool_author: &TestTopPoolAuthor, +) -> BatchExecutionResult { + let top_pool_calls = top_pool_author.get_pending_trusted_calls(*shard); + stf_executor + .propose_state_update( + &top_pool_calls, + &latest_parentchain_header(), + shard, + Duration::from_millis(600), + |mut s| { + s.set_block_number(&s.get_block_number().map_or(1, |n| n + 1)); + s + }, + ) + .unwrap() +} + +// helper functions +/// Decrypt `encrypted` and decode it into `StatePayload` +pub fn encrypted_state_diff_from_encrypted( + encrypted: &[u8], +) -> StatePayload { + let mut encrypted_payload: Vec = encrypted.to_vec(); + let state_key = state_key(); + state_key.decrypt(&mut encrypted_payload).unwrap(); + StatePayload::decode(&mut encrypted_payload.as_slice()).unwrap() +} + +pub fn state_key() -> Aes { + Aes::default() +} + +/// Some random account that has no funds in the `Stf`'s `test_genesis` config. +pub fn unfunded_public() -> spEd25519::Public { + spEd25519::Public::from_raw(*b"asdfasdfadsfasdfasfasdadfadfasdf") +} + +pub fn test_account() -> spEd25519::Pair { + spEd25519::Pair::from_seed(b"42315678901234567890123456789012") +} + +/// transforms `call` into `TrustedOperation::direct(call)` +pub fn direct_top(call: TrustedCallSigned) -> TrustedOperation { + call.into_trusted_operation(true) +} + +/// Just some random onchain header +pub fn latest_parentchain_header() -> Header { + Header::new(1, Default::default(), Default::default(), [69; 32].into(), Default::default()) +} + +/// Reads the value at `key_hash` from `state_diff` and decodes it into `D` +pub fn get_from_state_diff(state_diff: &SgxExternalitiesDiffType, key_hash: &[u8]) -> D { + // fixme: what's up here with the wrapping?? + state_diff + .get(key_hash) + .unwrap() + .as_ref() + .map(|d| Decode::decode(&mut d.as_slice())) + .unwrap() + .unwrap() +} diff --git a/bitacross-worker/enclave-runtime/src/test/top_pool_tests.rs b/bitacross-worker/enclave-runtime/src/test/top_pool_tests.rs new file mode 100644 index 0000000000..22776fbd39 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/test/top_pool_tests.rs @@ -0,0 +1,236 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::test::{ + fixtures::{ + components::{ + create_ocall_api, create_top_pool, encrypt_trusted_operation, sign_trusted_call, + }, + initialize_test_state::init_state, + test_setup::TestStf, + }, + mocks::types::{ + TestShieldingKey, TestShieldingKeyRepo, TestSigner, TestStateHandler, TestTopPoolAuthor, + }, +}; +use codec::Encode; +use ita_parentchain_interface::integritee; +use ita_stf::{ + test_genesis::{endowed_account, unendowed_account}, + Getter, TrustedCall, TrustedCallSigned, +}; +use itc_parentchain::indirect_calls_executor::{ + mock::TestEventCreator, ExecuteIndirectCalls, IndirectCallsExecutor, +}; +use itc_parentchain_test::{ + parentchain_block_builder::ParentchainBlockBuilder, + parentchain_header_builder::ParentchainHeaderBuilder, +}; +use itp_node_api::{ + api_client::{ + ExtrinsicParams, ParentchainAdditionalParams, ParentchainExtrinsicParams, + ParentchainUncheckedExtrinsic, + }, + metadata::{metadata_mocks::NodeMetadataMock, provider::NodeMetadataRepository}, +}; +use itp_node_api_metadata::pallet_teerex::TeerexCallIndexes; +use itp_ocall_api::EnclaveAttestationOCallApi; +use itp_sgx_crypto::ShieldingCryptoEncrypt; +use itp_stf_executor::enclave_signer::StfEnclaveSigner; +use itp_stf_primitives::{traits::TrustedCallVerification, types::TrustedOperation}; +use itp_stf_state_observer::mock::ObserveStateMock; +use itp_test::mock::metrics_ocall_mock::MetricsOCallMock; +use itp_top_pool_author::{ + top_filter::{AllowAllTopsFilter, DirectCallsOnlyFilter}, + traits::AuthorApi, +}; +use itp_types::{ + parentchain::Address, AccountId, Block, RsaRequest, ShardIdentifier, ShieldFundsFn, H256, +}; +use jsonrpc_core::futures::executor; +use litentry_primitives::Identity; +use log::*; +use sgx_crypto_helper::RsaKeyPair; +use sp_core::{ed25519, Pair}; +use sp_runtime::{MultiSignature, OpaqueExtrinsic}; +use std::{sync::Arc, vec::Vec}; +pub fn process_indirect_call_in_top_pool() { + let _ = env_logger::builder().is_test(true).try_init(); + info!("Setting up test."); + + let signer = TestSigner::from_seed(b"42315678901234567890123456789012"); + let shielding_key = TestShieldingKey::new().unwrap(); + let shielding_key_repo = Arc::new(TestShieldingKeyRepo::new(shielding_key)); + let header = ParentchainHeaderBuilder::default().build(); + + let ocall_api = create_ocall_api(&header, &signer); + + let state_handler = Arc::new(TestStateHandler::default()); + let (_, shard_id) = init_state(state_handler.as_ref(), signer.public().into()); + + let top_pool = create_top_pool(); + let (sender, _receiver) = std::sync::mpsc::sync_channel(1000); + + let top_pool_author = Arc::new(TestTopPoolAuthor::new( + top_pool, + AllowAllTopsFilter::::new(), + DirectCallsOnlyFilter::::new(), + state_handler, + shielding_key_repo, + Arc::new(MetricsOCallMock::default()), + Arc::new(sender), + )); + + let encrypted_indirect_call = + encrypted_indirect_call(ocall_api.as_ref(), &shard_id, &shielding_key); + + executor::block_on( + top_pool_author.submit_top(RsaRequest::new(shard_id, encrypted_indirect_call)), + ) + .unwrap(); + + assert_eq!(1, top_pool_author.get_pending_trusted_calls(shard_id).len()); +} + +pub fn submit_shielding_call_to_top_pool() { + let _ = env_logger::builder().is_test(true).try_init(); + + let signer = TestSigner::from_seed(b"42315678901234567890123456789012"); + let shielding_key = TestShieldingKey::new().unwrap(); + let shielding_key_repo = Arc::new(TestShieldingKeyRepo::new(shielding_key)); + let header = ParentchainHeaderBuilder::default().build(); + + let ocall_api = create_ocall_api(&header, &signer); + let mr_enclave = ocall_api.get_mrenclave_of_self().unwrap(); + + let state_handler = Arc::new(TestStateHandler::default()); + let (state, shard_id) = init_state(state_handler.as_ref(), signer.public().into()); + let state_observer = Arc::new(ObserveStateMock::new(state)); + + let top_pool = create_top_pool(); + let (sender, _receiver) = std::sync::mpsc::sync_channel(1000); + + let top_pool_author = Arc::new(TestTopPoolAuthor::new( + top_pool, + AllowAllTopsFilter::::new(), + DirectCallsOnlyFilter::::new(), + state_handler, + shielding_key_repo.clone(), + Arc::new(MetricsOCallMock::default()), + Arc::new(sender), + )); + + let enclave_signer = + Arc::new(StfEnclaveSigner::<_, _, _, TestStf, _, TrustedCallSigned, Getter>::new( + state_observer, + ocall_api, + shielding_key_repo.clone(), + top_pool_author.clone(), + )); + let node_meta_data_repository = Arc::new(NodeMetadataRepository::default()); + node_meta_data_repository.set_metadata(NodeMetadataMock::new()); + let indirect_calls_executor = + IndirectCallsExecutor::< + _, + _, + _, + _, + integritee::ShieldFundsAndInvokeFilter, + TestEventCreator, + integritee::ParentchainEventHandler, + TrustedCallSigned, + Getter, + >::new( + shielding_key_repo, enclave_signer, top_pool_author.clone(), node_meta_data_repository + ); + + let block_with_shielding_call = create_shielding_call_extrinsic(shard_id, &shielding_key); + + let _ = indirect_calls_executor + .execute_indirect_calls_in_extrinsics(&block_with_shielding_call, &Vec::new()) + .unwrap(); + + assert_eq!(1, top_pool_author.get_pending_trusted_calls(shard_id).len()); + let trusted_operation = + top_pool_author.get_pending_trusted_calls(shard_id).first().cloned().unwrap(); + let trusted_call = trusted_operation.to_call().unwrap(); + assert!(trusted_call.verify_signature(&mr_enclave.m, &shard_id)); +} + +fn encrypted_indirect_call< + AttestationApi: EnclaveAttestationOCallApi, + ShieldingKey: ShieldingCryptoEncrypt, +>( + attestation_api: &AttestationApi, + shard_id: &ShardIdentifier, + shielding_key: &ShieldingKey, +) -> Vec { + let sender = endowed_account(); + let receiver = unendowed_account(); + + let call = TrustedCall::balance_transfer( + Identity::Substrate(sender.public().into()), + receiver.public().into(), + 10000u128, + ); + let call_signed = sign_trusted_call(&call, attestation_api, shard_id, sender); + let trusted_operation = + TrustedOperation::::indirect_call(call_signed); + encrypt_trusted_operation(shielding_key, &trusted_operation) +} + +fn create_shielding_call_extrinsic( + shard: ShardIdentifier, + shielding_key: &ShieldingKey, +) -> Block { + let target_account = shielding_key.encrypt(&AccountId::new([2u8; 32]).encode()).unwrap(); + let test_signer = ed25519::Pair::from_seed(b"33345678901234567890123456789012"); + let signature = test_signer.sign(&[0u8]); + + let default_extra_for_test = ParentchainExtrinsicParams::new( + 0, + 0, + 0, + H256::default(), + ParentchainAdditionalParams::default(), + ); + + let dummy_node_metadata = NodeMetadataMock::new(); + + let shield_funds_indexes = dummy_node_metadata.shield_funds_call_indexes().unwrap(); + let opaque_extrinsic = OpaqueExtrinsic::from_bytes( + ParentchainUncheckedExtrinsic::::new_signed( + ( + shield_funds_indexes, + target_account, + ita_stf::test_genesis::SECOND_ENDOWED_ACC_FUNDS, + shard, + ), + Address::Address32([1u8; 32]), + MultiSignature::Ed25519(signature), + default_extra_for_test.signed_extra(), + ) + .encode() + .as_slice(), + ) + .unwrap(); + + ParentchainBlockBuilder::default() + .with_extrinsics(vec![opaque_extrinsic]) + .build() +} diff --git a/bitacross-worker/enclave-runtime/src/tls_ra/README.md b/bitacross-worker/enclave-runtime/src/tls_ra/README.md new file mode 100644 index 0000000000..3f4effa148 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/tls_ra/README.md @@ -0,0 +1,33 @@ +# provisioning + +each worker runs a provisioning server for other workers of the same MRENCLAVE and shard to get recent stf state and secrets from. + +Light client storage can also be provisioned to avoid re-synching the entire parentchains with each worker + +enclave instances are short-lived on both sides, just for a single request. + +```mermaid +sequenceDiagram +participant untrusted_server +participant enclave_server +participant enclave_client +participant untrusted_client +enclave_server ->> enclave_server: generate shielding & state encryption key +enclave_server ->> enclave_server: init_shard & sync parentchains +untrusted_client ->> untrusted_server: connect TCP +untrusted_client ->> enclave_client: request_state_provisioning +activate enclave_client +untrusted_server ->> enclave_server: run_state_provisioning_server +activate enclave_server +enclave_server ->> enclave_server: load state and secrets +enclave_client ->> enclave_server: open TLS session (including MU RA) +enclave_client ->> enclave_server: request_state_provisioning(shard, account) +enclave_server ->> enclave_client: write_provisioning_payloads +enclave_server ->> enclave_server: add client as vault proxy for shard +enclave_client ->> enclave_client: seal state and secrets to disk +enclave_client -->> untrusted_client: _ +deactivate enclave_client +enclave_server -->> untrusted_server: _ +deactivate enclave_server +untrusted_client --> untrusted_server: disconnect TCP +``` diff --git a/bitacross-worker/enclave-runtime/src/tls_ra/authentication.rs b/bitacross-worker/enclave-runtime/src/tls_ra/authentication.rs new file mode 100644 index 0000000000..a3c14528de --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/tls_ra/authentication.rs @@ -0,0 +1,158 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Remote attestation certificate authentication of server and client +use itp_attestation_handler::cert; +use itp_ocall_api::EnclaveAttestationOCallApi; +use log::*; +use sgx_types::*; +use webpki::DNSName; + +pub struct ClientAuth { + outdated_ok: bool, + skip_ra: bool, + attestation_ocall: A, +} + +impl ClientAuth { + pub fn new(outdated_ok: bool, skip_ra: bool, attestation_ocall: A) -> Self { + ClientAuth { outdated_ok, skip_ra, attestation_ocall } + } +} + +impl rustls::ClientCertVerifier for ClientAuth +where + A: EnclaveAttestationOCallApi, +{ + fn client_auth_root_subjects( + &self, + _sni: Option<&DNSName>, + ) -> Option { + Some(rustls::DistinguishedNames::new()) + } + + fn verify_client_cert( + &self, + certs: &[rustls::Certificate], + _sni: Option<&DNSName>, + ) -> Result { + debug!("client cert: {:?}", certs); + let issuer = + certs.get(0).ok_or(rustls::TLSError::NoCertificatesPresented).and_then(|cert| { + cert::parse_cert_issuer(&cert.0) + .map_err(|_| rustls::TLSError::NoCertificatesPresented) + })?; + info!("client signer (issuer) is: 0x{}", hex::encode(issuer)); + + // This call will automatically verify cert is properly signed + if self.skip_ra { + warn!("Skip verifying ra-report"); + return Ok(rustls::ClientCertVerified::assertion()) + } + + if certs.is_empty() { + return Err(rustls::TLSError::NoCertificatesPresented) + } + + #[cfg(feature = "dcap")] + let is_dcap = true; + #[cfg(not(feature = "dcap"))] + let is_dcap = false; + match certs.first() { + Some(cert) => { + match cert::verify_mra_cert(&cert.0, true, is_dcap, &self.attestation_ocall) { + Ok(()) => Ok(rustls::ClientCertVerified::assertion()), + Err(sgx_status_t::SGX_ERROR_UPDATE_NEEDED) => + if self.outdated_ok { + warn!("outdated_ok is set, overriding outdated error"); + Ok(rustls::ClientCertVerified::assertion()) + } else { + Err(rustls::TLSError::WebPKIError(webpki::Error::ExtensionValueInvalid)) + }, + Err(_) => + Err(rustls::TLSError::WebPKIError(webpki::Error::ExtensionValueInvalid)), + } + }, + None => Err(rustls::TLSError::WebPKIError(webpki::Error::ExtensionValueInvalid)), + } + } +} + +pub struct ServerAuth { + outdated_ok: bool, + skip_ra: bool, + attestation_ocall: A, +} + +impl ServerAuth { + pub fn new(outdated_ok: bool, skip_ra: bool, attestation_ocall: A) -> Self { + ServerAuth { outdated_ok, skip_ra, attestation_ocall } + } +} + +impl rustls::ServerCertVerifier for ServerAuth +where + A: EnclaveAttestationOCallApi, +{ + fn verify_server_cert( + &self, + _roots: &rustls::RootCertStore, + certs: &[rustls::Certificate], + _hostname: webpki::DNSNameRef, + _ocsp: &[u8], + ) -> Result { + debug!("server cert: {:?}", certs); + let issuer = + certs.get(0).ok_or(rustls::TLSError::NoCertificatesPresented).and_then(|cert| { + cert::parse_cert_issuer(&cert.0) + .map_err(|_| rustls::TLSError::NoCertificatesPresented) + })?; + info!("server signer (issuer) is: 0x{}", hex::encode(issuer)); + + if self.skip_ra { + warn!("Skip verifying ra-report"); + return Ok(rustls::ServerCertVerified::assertion()) + } + + if certs.is_empty() { + return Err(rustls::TLSError::NoCertificatesPresented) + } + + #[cfg(feature = "dcap")] + let is_dcap = true; + #[cfg(not(feature = "dcap"))] + let is_dcap = false; + // This call will automatically verify cert is properly signed + match certs.first() { + Some(cert) => { + match cert::verify_mra_cert(&cert.0, true, is_dcap, &self.attestation_ocall) { + Ok(()) => Ok(rustls::ServerCertVerified::assertion()), + Err(sgx_status_t::SGX_ERROR_UPDATE_NEEDED) => + if self.outdated_ok { + warn!("outdated_ok is set, overriding outdated error"); + Ok(rustls::ServerCertVerified::assertion()) + } else { + Err(rustls::TLSError::WebPKIError(webpki::Error::ExtensionValueInvalid)) + }, + Err(_) => + Err(rustls::TLSError::WebPKIError(webpki::Error::ExtensionValueInvalid)), + } + }, + None => Err(rustls::TLSError::WebPKIError(webpki::Error::ExtensionValueInvalid)), + } + } +} diff --git a/bitacross-worker/enclave-runtime/src/tls_ra/mocks.rs b/bitacross-worker/enclave-runtime/src/tls_ra/mocks.rs new file mode 100644 index 0000000000..e7f6900a0f --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/tls_ra/mocks.rs @@ -0,0 +1,87 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use super::seal_handler::{SealStateAndKeys, UnsealStateAndKeys}; +use crate::error::Result as EnclaveResult; +use itp_types::ShardIdentifier; +use std::{ + sync::{Arc, SgxRwLock as RwLock}, + vec::Vec, +}; + +#[derive(Clone)] +pub struct SealHandlerMock { + pub shielding_key: Arc>>, + pub state_key: Arc>>, + pub state: Arc>>, + pub light_client_state: Arc>>, +} + +impl SealHandlerMock { + pub fn new( + shielding_key: Arc>>, + state_key: Arc>>, + state: Arc>>, + light_client_state: Arc>>, + ) -> Self { + Self { shielding_key, state_key, state, light_client_state } + } +} + +impl SealStateAndKeys for SealHandlerMock { + fn seal_shielding_key(&self, bytes: &[u8]) -> EnclaveResult<()> { + *self.shielding_key.write().unwrap() = bytes.to_vec(); + Ok(()) + } + + fn seal_state_key(&self, bytes: &[u8]) -> EnclaveResult<()> { + *self.state_key.write().unwrap() = bytes.to_vec(); + Ok(()) + } + + fn seal_state(&self, bytes: &[u8], _shard: &ShardIdentifier) -> EnclaveResult<()> { + *self.state.write().unwrap() = bytes.to_vec(); + Ok(()) + } + + fn seal_new_empty_state(&self, _shard: &ShardIdentifier) -> EnclaveResult<()> { + Ok(()) + } + + fn seal_light_client_state(&self, bytes: &[u8]) -> EnclaveResult<()> { + *self.light_client_state.write().unwrap() = bytes.to_vec(); + Ok(()) + } +} + +impl UnsealStateAndKeys for SealHandlerMock { + fn unseal_shielding_key(&self) -> EnclaveResult> { + Ok(self.shielding_key.read().unwrap().clone()) + } + + fn unseal_state_key(&self) -> EnclaveResult> { + Ok(self.state_key.read().unwrap().clone()) + } + + fn unseal_state(&self, _shard: &ShardIdentifier) -> EnclaveResult> { + Ok(self.state.read().unwrap().clone()) + } + + fn unseal_light_client_state(&self) -> EnclaveResult> { + Ok(self.light_client_state.read().unwrap().clone()) + } +} diff --git a/bitacross-worker/enclave-runtime/src/tls_ra/mod.rs b/bitacross-worker/enclave-runtime/src/tls_ra/mod.rs new file mode 100644 index 0000000000..07474f3f8b --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/tls_ra/mod.rs @@ -0,0 +1,81 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Contains all logic of the state provisioning mechanism +//! including the remote attestation and tls / tcp connection part. + +use codec::{Decode, Encode, MaxEncodedLen}; +use itp_types::{AccountId, ShardIdentifier}; + +mod authentication; +pub mod seal_handler; +mod tls_ra_client; +mod tls_ra_server; + +#[cfg(feature = "test")] +pub mod tests; + +#[cfg(feature = "test")] +pub mod mocks; + +/// Header of an accompanied payload. Indicates the +/// length an the type (opcode) of the following payload. +#[derive(Clone, Debug, Decode, Encode, MaxEncodedLen)] +pub struct TcpHeader { + pub opcode: Opcode, + pub payload_length: u64, +} + +impl TcpHeader { + fn new(opcode: Opcode, payload_length: u64) -> Self { + Self { opcode, payload_length } + } +} + +/// Indicates the payload content type. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Decode, Encode, MaxEncodedLen)] +pub enum Opcode { + ShieldingKey, + StateKey, + State, + LightClient, +} + +impl From for Opcode { + fn from(item: u8) -> Self { + match item { + 0 => Opcode::ShieldingKey, + 1 => Opcode::StateKey, + 2 => Opcode::State, + 3 => Opcode::LightClient, + _ => unimplemented!("Unsupported/unknown Opcode for MU-RA exchange"), + } + } +} + +impl Opcode { + pub fn to_bytes(self) -> [u8; 1] { + (self as u8).to_be_bytes() + } +} + +/// The data structure to be sent by the client to request provisioning +#[derive(Clone, Debug, Eq, PartialEq, Decode, Encode, MaxEncodedLen)] +pub struct ClientProvisioningRequest { + pub shard: ShardIdentifier, + pub account: AccountId, +} diff --git a/bitacross-worker/enclave-runtime/src/tls_ra/seal_handler.rs b/bitacross-worker/enclave-runtime/src/tls_ra/seal_handler.rs new file mode 100644 index 0000000000..bb7828dd57 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/tls_ra/seal_handler.rs @@ -0,0 +1,268 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Abstraction of the reading (unseal) and storing (seal) part of the +//! shielding key, state key and state. + +use crate::error::{Error as EnclaveError, Result as EnclaveResult}; +use codec::{Decode, Encode}; +use ita_stf::{State as StfState, StateType as StfStateType}; +use itc_parentchain::light_client::LightClientSealing; +use itp_sgx_crypto::{ + key_repository::{AccessKey, MutateKey}, + Aes, +}; +use itp_sgx_externalities::SgxExternalitiesTrait; +use itp_stf_state_handler::handle_state::HandleState; +use itp_types::ShardIdentifier; +use log::*; +use sgx_crypto_helper::rsa3072::Rsa3072KeyPair; +use std::{sync::Arc, vec::Vec}; + +/// Handles the sealing and unsealing of the shielding key, state key and the state. +#[derive(Default)] +pub struct SealHandler { + state_handler: Arc, + state_key_repository: Arc, + shielding_key_repository: Arc, + light_client_seal: Arc, +} + +impl + SealHandler +{ + pub fn new( + state_handler: Arc, + state_key_repository: Arc, + shielding_key_repository: Arc, + light_client_seal: Arc, + ) -> Self { + Self { state_handler, state_key_repository, shielding_key_repository, light_client_seal } + } +} + +pub trait SealStateAndKeys { + fn seal_shielding_key(&self, bytes: &[u8]) -> EnclaveResult<()>; + fn seal_state_key(&self, bytes: &[u8]) -> EnclaveResult<()>; + fn seal_state(&self, bytes: &[u8], shard: &ShardIdentifier) -> EnclaveResult<()>; + fn seal_new_empty_state(&self, shard: &ShardIdentifier) -> EnclaveResult<()>; + fn seal_light_client_state(&self, bytes: &[u8]) -> EnclaveResult<()>; +} + +pub trait UnsealStateAndKeys { + fn unseal_shielding_key(&self) -> EnclaveResult>; + fn unseal_state_key(&self) -> EnclaveResult>; + fn unseal_state(&self, shard: &ShardIdentifier) -> EnclaveResult>; + fn unseal_light_client_state(&self) -> EnclaveResult>; +} + +impl SealStateAndKeys + for SealHandler +where + ShieldingKeyRepository: AccessKey + MutateKey, + StateKeyRepository: AccessKey + MutateKey, + StateHandler: HandleState, + LightClientSeal: LightClientSealing, + LightClientSeal::LightClientState: Decode, +{ + fn seal_shielding_key(&self, bytes: &[u8]) -> EnclaveResult<()> { + let key: Rsa3072KeyPair = serde_json::from_slice(bytes).map_err(|e| { + error!(" [Enclave] Received Invalid RSA key"); + EnclaveError::Other(e.into()) + })?; + self.shielding_key_repository.update_key(key)?; + info!("Successfully stored a new shielding key"); + Ok(()) + } + + fn seal_state_key(&self, mut bytes: &[u8]) -> EnclaveResult<()> { + let aes = Aes::decode(&mut bytes)?; + self.state_key_repository.update_key(aes)?; + info!("Successfully stored a new state key"); + Ok(()) + } + + fn seal_state(&self, mut bytes: &[u8], shard: &ShardIdentifier) -> EnclaveResult<()> { + let state = StfStateType::decode(&mut bytes)?; + let state_with_empty_diff = StfState::new(state); + + self.state_handler.reset(state_with_empty_diff, shard)?; + info!("Successfully updated shard {:?} with provisioned state", shard); + Ok(()) + } + + fn seal_light_client_state(&self, mut bytes: &[u8]) -> EnclaveResult<()> { + let state = ::LightClientState::decode(&mut bytes)?; + self.light_client_seal.seal(&state)?; + info!("Successfully sealed light client state"); + Ok(()) + } + + /// Seal an empty, newly initialized state. + /// + /// Requires the shielding key to be sealed and updated before calling this. + /// + /// Call this function in case we don't provision the state itself, only the shielding key. + /// Since the enclave signing account is derived from the shielding key, we need to + /// newly initialize the state with the updated shielding key. + fn seal_new_empty_state(&self, shard: &ShardIdentifier) -> EnclaveResult<()> { + self.state_handler.initialize_shard(*shard)?; + info!("Successfully reset state with new enclave account, for shard {:?}", shard); + Ok(()) + } +} + +impl UnsealStateAndKeys + for SealHandler +where + ShieldingKeyRepository: AccessKey + MutateKey, + StateKeyRepository: AccessKey + MutateKey, + StateHandler: HandleState, + LightClientSeal: LightClientSealing, + LightClientSeal::LightClientState: Encode, +{ + fn unseal_shielding_key(&self) -> EnclaveResult> { + let shielding_key = self + .shielding_key_repository + .retrieve_key() + .map_err(|e| EnclaveError::Other(format!("{:?}", e).into()))?; + serde_json::to_vec(&shielding_key).map_err(|e| EnclaveError::Other(e.into())) + } + + fn unseal_state_key(&self) -> EnclaveResult> { + self.state_key_repository + .retrieve_key() + .map(|k| k.encode()) + .map_err(|e| EnclaveError::Other(format!("{:?}", e).into())) + } + + fn unseal_state(&self, shard: &ShardIdentifier) -> EnclaveResult> { + Ok(self.state_handler.execute_on_current(shard, |state, _| state.state.encode())?) + } + + fn unseal_light_client_state(&self) -> EnclaveResult> { + Ok(self.light_client_seal.unseal()?.encode()) + } +} + +#[cfg(feature = "test")] +pub mod test { + use super::*; + use itc_parentchain::light_client::mocks::validator_mock_seal::LightValidationStateSealMock; + use itp_sgx_crypto::mocks::KeyRepositoryMock; + use itp_test::mock::handle_state_mock::HandleStateMock; + + type StateKeyRepositoryMock = KeyRepositoryMock; + type ShieldingKeyRepositoryMock = KeyRepositoryMock; + + type SealHandlerMock = SealHandler< + ShieldingKeyRepositoryMock, + StateKeyRepositoryMock, + HandleStateMock, + LightValidationStateSealMock, + >; + + pub fn seal_shielding_key_works() { + let seal_handler = SealHandlerMock::default(); + let key_pair_in_bytes = serde_json::to_vec(&Rsa3072KeyPair::default()).unwrap(); + + let result = seal_handler.seal_shielding_key(&key_pair_in_bytes); + + assert!(result.is_ok()); + } + + pub fn seal_shielding_key_fails_for_invalid_key() { + let seal_handler = SealHandlerMock::default(); + + let result = seal_handler.seal_shielding_key(&[1, 2, 3]); + + assert!(result.is_err()); + } + + pub fn unseal_seal_shielding_key_works() { + let seal_handler = SealHandlerMock::default(); + + let key_pair_in_bytes = seal_handler.unseal_shielding_key().unwrap(); + + let result = seal_handler.seal_shielding_key(&key_pair_in_bytes); + + assert!(result.is_ok()); + } + + pub fn seal_state_key_works() { + let seal_handler = SealHandlerMock::default(); + let key_pair_in_bytes = Aes::default().encode(); + + let result = seal_handler.seal_state_key(&key_pair_in_bytes); + + assert!(result.is_ok()); + } + + pub fn seal_state_key_fails_for_invalid_key() { + let seal_handler = SealHandlerMock::default(); + + let result = seal_handler.seal_state_key(&[1, 2, 3]); + + assert!(result.is_err()); + } + + pub fn unseal_seal_state_key_works() { + let seal_handler = SealHandlerMock::default(); + let key_pair_in_bytes = seal_handler.unseal_state_key().unwrap(); + + let result = seal_handler.seal_state_key(&key_pair_in_bytes); + + assert!(result.is_ok()); + } + + pub fn seal_state_works() { + let seal_handler = SealHandlerMock::default(); + let state = ::StateT::default(); + let shard = ShardIdentifier::default(); + let _init_hash = seal_handler.state_handler.initialize_shard(shard).unwrap(); + + let result = seal_handler.seal_state(&state.encode(), &shard); + + assert!(result.is_ok()); + } + + pub fn seal_state_fails_for_invalid_state() { + let seal_handler = SealHandlerMock::default(); + let shard = ShardIdentifier::default(); + + let result = seal_handler.seal_state(&[1, 0, 3], &shard); + + assert!(result.is_err()); + } + + pub fn unseal_seal_state_works() { + let seal_handler = SealHandlerMock::default(); + let shard = ShardIdentifier::default(); + seal_handler.state_handler.initialize_shard(shard).unwrap(); + // Fill our mock state: + let (lock, mut state) = seal_handler.state_handler.load_for_mutation(&shard).unwrap(); + let (key, value) = ("my_key", "my_value"); + state.insert(key.encode(), value.encode()); + seal_handler.state_handler.write_after_mutation(state, lock, &shard).unwrap(); + + let state_in_bytes = seal_handler.unseal_state(&shard).unwrap(); + + let result = seal_handler.seal_state(&state_in_bytes, &shard); + + assert!(result.is_ok()); + } +} diff --git a/bitacross-worker/enclave-runtime/src/tls_ra/tests.rs b/bitacross-worker/enclave-runtime/src/tls_ra/tests.rs new file mode 100644 index 0000000000..5cdbd2a184 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/tls_ra/tests.rs @@ -0,0 +1,196 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Tests of tls-ra client / server communication. + +use super::{ + mocks::SealHandlerMock, tls_ra_client::request_state_provisioning_internal, + tls_ra_server::run_state_provisioning_server_internal, +}; +use crate::{ + initialization::global_components::EnclaveStf, + tls_ra::seal_handler::{SealHandler, SealStateAndKeys, UnsealStateAndKeys}, +}; +use ita_stf::State; +use itc_parentchain::light_client::mocks::validator_mock_seal::LightValidationStateSealMock; +use itp_settings::worker_mode::{ProvideWorkerMode, WorkerMode, WorkerModeProvider}; +use itp_sgx_crypto::{mocks::KeyRepositoryMock, Aes}; +use itp_stf_interface::InitState; +use itp_stf_primitives::types::AccountId; +use itp_stf_state_handler::handle_state::HandleState; +use itp_test::mock::handle_state_mock::HandleStateMock; +use itp_types::ShardIdentifier; +use sgx_crypto_helper::{rsa3072::Rsa3072KeyPair, RsaKeyPair}; +use sgx_types::{sgx_quote_sign_type_t, sgx_target_info_t}; +use std::{ + net::{TcpListener, TcpStream}, + os::unix::io::AsRawFd, + string::String, + sync::{Arc, SgxRwLock as RwLock}, + thread, + time::Duration, + vec::Vec, +}; + +static SIGN_TYPE: sgx_quote_sign_type_t = sgx_quote_sign_type_t::SGX_UNLINKABLE_SIGNATURE; +static SKIP_RA: i32 = 1; +static QUOTE_SIZE: u32 = 0; + +fn run_state_provisioning_server(seal_handler: impl UnsealStateAndKeys, port: u16) { + let listener = TcpListener::bind(server_addr(port)).unwrap(); + + let (socket, _addr) = listener.accept().unwrap(); + let sgx_target_info: sgx_target_info_t = sgx_target_info_t::default(); + run_state_provisioning_server_internal::<_, WorkerModeProvider>( + socket.as_raw_fd(), + SIGN_TYPE, + Some(&sgx_target_info), + Some("E_SIZE), + SKIP_RA, + seal_handler, + ) + .unwrap(); +} + +fn server_addr(port: u16) -> String { + format!("127.0.0.1:{}", port) +} + +pub fn test_tls_ra_server_client_networking() { + let shard = ShardIdentifier::default(); + let client_account = AccountId::from([42; 32]); + let shielding_key_encoded = vec![1, 2, 3]; + let state_key_encoded = vec![5, 2, 3, 7]; + let state_encoded = Vec::from([1u8; 26000]); // Have a decently sized state, so read() must be called multiple times. + let light_client_state_encoded = Vec::from([1u8; 10000]); // Have a decently sized state, so read() must be called multiple times. + + let server_seal_handler = SealHandlerMock::new( + Arc::new(RwLock::new(shielding_key_encoded.clone())), + Arc::new(RwLock::new(state_key_encoded.clone())), + Arc::new(RwLock::new(state_encoded.clone())), + Arc::new(RwLock::new(light_client_state_encoded.clone())), + ); + let initial_client_state = vec![0, 0, 1]; + let initial_client_state_key = vec![0, 0, 2]; + let initial_client_light_client_state = vec![0, 0, 3]; + let client_shielding_key = Arc::new(RwLock::new(Vec::new())); + let client_state_key = Arc::new(RwLock::new(initial_client_state_key.clone())); + let client_state = Arc::new(RwLock::new(initial_client_state.clone())); + let client_light_client_state = Arc::new(RwLock::new(initial_client_light_client_state)); + + let client_seal_handler = SealHandlerMock::new( + client_shielding_key.clone(), + client_state_key.clone(), + client_state.clone(), + client_light_client_state.clone(), + ); + + let port: u16 = 3149; + + // Start server. + let server_thread_handle = thread::spawn(move || { + run_state_provisioning_server(server_seal_handler, port); + }); + thread::sleep(Duration::from_secs(1)); + + // Start client. + let socket = TcpStream::connect(server_addr(port)).unwrap(); + let sgx_target_info: sgx_target_info_t = sgx_target_info_t::default(); + let result = request_state_provisioning_internal( + socket.as_raw_fd(), + SIGN_TYPE, + Some(&sgx_target_info), + Some("E_SIZE), + shard, + SKIP_RA, + client_seal_handler, + client_account, + ); + + // Ensure server thread has finished. + server_thread_handle.join().unwrap(); + + assert!(result.is_ok()); + assert_eq!(*client_shielding_key.read().unwrap(), shielding_key_encoded); + assert_eq!(*client_light_client_state.read().unwrap(), light_client_state_encoded); + + // State and state-key are provisioned only in sidechain mode + if WorkerModeProvider::worker_mode() == WorkerMode::Sidechain { + assert_eq!(*client_state.read().unwrap(), state_encoded); + assert_eq!(*client_state_key.read().unwrap(), state_key_encoded); + } else { + assert_eq!(*client_state.read().unwrap(), initial_client_state); + assert_eq!(*client_state_key.read().unwrap(), initial_client_state_key); + } +} + +// Test state and key provisioning with 'real' data structures. +pub fn test_state_and_key_provisioning() { + let client_account = AccountId::from([42; 32]); + let state_key = Aes::new([3u8; 16], [0u8; 16]); + let shielding_key = Rsa3072KeyPair::new().unwrap(); + let initialized_state = EnclaveStf::init_state(AccountId::new([1u8; 32])); + let shard = ShardIdentifier::from([1u8; 32]); + + let server_seal_handler = + create_seal_handler(state_key, shielding_key, initialized_state, &shard); + let client_seal_handler = + create_seal_handler(Aes::default(), Rsa3072KeyPair::default(), State::default(), &shard); + + let port: u16 = 3150; + + // Start server. + let server_thread_handle = thread::spawn(move || { + run_state_provisioning_server(server_seal_handler, port); + }); + thread::sleep(Duration::from_secs(1)); + + // Start client. + let socket = TcpStream::connect(server_addr(port)).unwrap(); + let sgx_target_info: sgx_target_info_t = sgx_target_info_t::default(); + let result = request_state_provisioning_internal( + socket.as_raw_fd(), + SIGN_TYPE, + Some(&sgx_target_info), + Some("E_SIZE), + shard, + SKIP_RA, + client_seal_handler, + client_account, + ); + + // Ensure server thread has finished. + server_thread_handle.join().unwrap(); + + assert!(result.is_ok()); +} + +fn create_seal_handler( + state_key: Aes, + shielding_key: Rsa3072KeyPair, + state: State, + shard: &ShardIdentifier, +) -> impl UnsealStateAndKeys + SealStateAndKeys { + let state_key_repository = Arc::new(KeyRepositoryMock::::new(state_key)); + let shielding_key_repository = + Arc::new(KeyRepositoryMock::::new(shielding_key)); + let state_handler = Arc::new(HandleStateMock::default()); + state_handler.reset(state, shard).unwrap(); + let seal = Arc::new(LightValidationStateSealMock::new()); + + SealHandler::new(state_handler, state_key_repository, shielding_key_repository, seal) +} diff --git a/bitacross-worker/enclave-runtime/src/tls_ra/tls_ra_client.rs b/bitacross-worker/enclave-runtime/src/tls_ra/tls_ra_client.rs new file mode 100644 index 0000000000..442512701e --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/tls_ra/tls_ra_client.rs @@ -0,0 +1,327 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Implementation of the client part of the state provisioning. + +use super::{authentication::ServerAuth, Opcode, TcpHeader}; +use crate::{ + attestation::create_ra_report_and_signature, + error::{Error as EnclaveError, Result as EnclaveResult}, + initialization::global_components::{ + EnclaveSealHandler, GLOBAL_INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_SEAL, + GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT, GLOBAL_STATE_KEY_REPOSITORY_COMPONENT, + }, + ocall::OcallApi, + tls_ra::{seal_handler::SealStateAndKeys, ClientProvisioningRequest}, + GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT, GLOBAL_STATE_HANDLER_COMPONENT, +}; +use codec::Encode; +use itp_attestation_handler::{RemoteAttestationType, DEV_HOSTNAME}; +use itp_component_container::ComponentGetter; +use itp_ocall_api::EnclaveAttestationOCallApi; +use itp_sgx_crypto::key_repository::AccessPubkey; +use itp_types::{AccountId, ShardIdentifier}; +use log::*; +use rustls::{ClientConfig, ClientSession, Stream}; +use sgx_types::*; +use std::{ + backtrace::{self, PrintFormat}, + convert::TryInto, + io::{Read, Write}, + net::TcpStream, + slice, + sync::Arc, + vec::Vec, +}; +/// Client part of the TCP-level connection and the underlying TLS-level session. +/// +/// Includes a seal handler, which handles the storage part of the received data. +struct TlsClient<'a, StateAndKeySealer> +where + StateAndKeySealer: SealStateAndKeys, +{ + tls_stream: Stream<'a, ClientSession, TcpStream>, + seal_handler: StateAndKeySealer, + shard: ShardIdentifier, +} + +impl<'a, StateAndKeySealer> TlsClient<'a, StateAndKeySealer> +where + StateAndKeySealer: SealStateAndKeys, +{ + fn new( + tls_stream: Stream<'a, ClientSession, TcpStream>, + seal_handler: StateAndKeySealer, + shard: ShardIdentifier, + ) -> TlsClient { + TlsClient { tls_stream, seal_handler, shard } + } + + /// Read all data sent by the server of the specific shard. + /// + /// We trust here that the server sends us the correct data, as + /// we do not have any way to test it. + fn obtain_provisioning_for_shard(&mut self, account: AccountId) -> EnclaveResult<()> { + debug!( + "obtain_provisioning_for_shard called, about to call self.send_provisioning_request()." + ); + self.send_provisioning_request(account)?; + debug!("self.send_provisioning_request() succeeded."); + self.read_and_seal_all() + } + + /// Send the shard of the state we want to receive to the provisioning server. + fn send_provisioning_request(&mut self, account: AccountId) -> EnclaveResult<()> { + debug!("self.send_provisioning_request() called."); + self.tls_stream + .write_all(&ClientProvisioningRequest { shard: self.shard, account }.encode())?; + debug!("write_all succeeded."); + Ok(()) + } + + /// Read and seal all relevant data sent by the server. + fn read_and_seal_all(&mut self) -> EnclaveResult<()> { + let mut received_payloads: Vec = Vec::new(); + + loop { + let maybe_opcode = self.read_and_seal()?; + match maybe_opcode { + None => break, + Some(o) => { + received_payloads.push(o); + }, + } + } + info!("Successfully read and sealed all data sent by the state provisioning server."); + + // In case we receive a shielding key, but no state, we need to reset our state + // to update the enclave account. + if received_payloads.contains(&Opcode::ShieldingKey) + && !received_payloads.contains(&Opcode::State) + { + self.seal_handler.seal_new_empty_state(&self.shard)?; + } + + Ok(()) + } + + /// Read a server header / payload pair and directly seal the received data. + fn read_and_seal(&mut self) -> EnclaveResult> { + let mut start_byte = [0u8; 1]; + let read_size = self.tls_stream.read(&mut start_byte)?; + // If we're reading but there's no data: EOF. + if read_size == 0 { + return Ok(None) + } + let header = self.read_header(start_byte[0])?; + let bytes = self.read_until(header.payload_length as usize)?; + match header.opcode { + Opcode::ShieldingKey => self.seal_handler.seal_shielding_key(&bytes)?, + Opcode::StateKey => self.seal_handler.seal_state_key(&bytes)?, + Opcode::State => self.seal_handler.seal_state(&bytes, &self.shard)?, + Opcode::LightClient => self.seal_handler.seal_light_client_state(&bytes)?, + }; + Ok(Some(header.opcode)) + } + + /// Reads the payload header, indicating the sent payload length and type. + fn read_header(&mut self, start_byte: u8) -> EnclaveResult { + debug!("Read first byte: {:?}", start_byte); + // The first sent byte indicates the payload type. + let opcode: Opcode = start_byte + .try_into() + .map_err(|_| EnclaveError::Other("Could not convert opcode".into()))?; + debug!("Read header opcode: {:?}", opcode); + // The following bytes contain the payload length, which is a u64. + let mut payload_length_buffer = [0u8; std::mem::size_of::()]; + self.tls_stream.read_exact(&mut payload_length_buffer)?; + let payload_length = u64::from_be_bytes(payload_length_buffer); + debug!("Payload length of {:?}: {}", opcode, payload_length); + + Ok(TcpHeader::new(opcode, payload_length)) + } + + /// Read all bytes into a buffer of given length. + fn read_until(&mut self, length: usize) -> EnclaveResult> { + let mut bytes = vec![0u8; length]; + self.tls_stream.read_exact(&mut bytes)?; + Ok(bytes) + } +} + +#[no_mangle] +pub unsafe extern "C" fn request_state_provisioning( + socket_fd: c_int, + sign_type: sgx_quote_sign_type_t, + quoting_enclave_target_info: Option<&sgx_target_info_t>, + quote_size: Option<&u32>, + shard: *const u8, + shard_size: u32, + skip_ra: c_int, +) -> sgx_status_t { + let _ = backtrace::enable_backtrace("enclave.signed.so", PrintFormat::Short); + let shard = ShardIdentifier::from_slice(slice::from_raw_parts(shard, shard_size as usize)); + + let state_handler = match GLOBAL_STATE_HANDLER_COMPONENT.get() { + Ok(s) => s, + Err(e) => { + error!("{:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + let state_key_repository = match GLOBAL_STATE_KEY_REPOSITORY_COMPONENT.get() { + Ok(s) => s, + Err(e) => { + error!("{:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + let shielding_key_repository = match GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT.get() { + Ok(s) => s, + Err(e) => { + error!("{:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + let light_client_seal = match GLOBAL_INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_SEAL.get() { + Ok(s) => s, + Err(e) => { + error!("{:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + let seal_handler = EnclaveSealHandler::new( + state_handler, + state_key_repository, + shielding_key_repository, + light_client_seal, + ); + + let signing_key_repository = match GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT.get() { + Ok(s) => s, + Err(e) => { + error!("{:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + let client_account = match signing_key_repository.retrieve_pubkey() { + Ok(s) => AccountId::from(s), + Err(e) => return e.into(), + }; + + if let Err(e) = request_state_provisioning_internal( + socket_fd, + sign_type, + quoting_enclave_target_info, + quote_size, + shard, + skip_ra, + seal_handler, + client_account, + ) { + error!("Failed to sync state due to: {:?}", e); + return e.into() + }; + + sgx_status_t::SGX_SUCCESS +} + +/// Internal [`request_state_provisioning`] function to be able to use the handy `?` operator. +// allowing clippy rant because this fn will be refactored with MU RA deprecation +#[allow(clippy::too_many_arguments)] +pub(crate) fn request_state_provisioning_internal( + socket_fd: c_int, + sign_type: sgx_quote_sign_type_t, + quoting_enclave_target_info: Option<&sgx_target_info_t>, + quote_size: Option<&u32>, + shard: ShardIdentifier, + skip_ra: c_int, + seal_handler: StateAndKeySealer, + client_account: AccountId, +) -> EnclaveResult<()> { + debug!("Client config generate..."); + let client_config = tls_client_config( + sign_type, + quoting_enclave_target_info, + quote_size, + OcallApi, + skip_ra == 1, + )?; + debug!("Client config retrieved"); + let (mut client_session, mut tcp_stream) = tls_client_session_stream(socket_fd, client_config)?; + debug!("Client sesssion established."); + + let mut client = TlsClient::new( + rustls::Stream::new(&mut client_session, &mut tcp_stream), + seal_handler, + shard, + ); + + info!("Requesting keys and state from mu-ra server of fellow validateer"); + client.obtain_provisioning_for_shard(client_account) +} + +fn tls_client_config( + sign_type: sgx_quote_sign_type_t, + quoting_enclave_target_info: Option<&sgx_target_info_t>, + quote_size: Option<&u32>, + ocall_api: A, + skip_ra: bool, +) -> EnclaveResult { + #[cfg(not(feature = "dcap"))] + let attestation_type = RemoteAttestationType::Epid; + #[cfg(feature = "dcap")] + let attestation_type = RemoteAttestationType::Dcap; + + // report will be signed with client enclave ed25519 signing key + let (key_der, cert_der) = create_ra_report_and_signature( + skip_ra, + attestation_type, + sign_type, + quoting_enclave_target_info, + quote_size, + )?; + debug!("got key_der and cert_der"); + + let mut cfg = rustls::ClientConfig::new(); + let certs = vec![rustls::Certificate(cert_der)]; + let privkey = rustls::PrivateKey(key_der); + #[allow(clippy::unwrap_used)] + cfg.set_single_client_cert(certs, privkey).unwrap(); + // ServerAuth will perform MU RA as part of authentication process + cfg.dangerous() + .set_certificate_verifier(Arc::new(ServerAuth::new(true, skip_ra, ocall_api))); + cfg.versions.clear(); + cfg.versions.push(rustls::ProtocolVersion::TLSv1_2); + Ok(cfg) +} + +fn tls_client_session_stream( + socket_fd: i32, + client_config: ClientConfig, +) -> EnclaveResult<(ClientSession, TcpStream)> { + let dns_name = webpki::DNSNameRef::try_from_ascii_str(DEV_HOSTNAME) + .map_err(|e| EnclaveError::Other(e.into()))?; + let sess = rustls::ClientSession::new(&Arc::new(client_config), dns_name); + let conn = TcpStream::new(socket_fd)?; + Ok((sess, conn)) +} diff --git a/bitacross-worker/enclave-runtime/src/tls_ra/tls_ra_server.rs b/bitacross-worker/enclave-runtime/src/tls_ra/tls_ra_server.rs new file mode 100644 index 0000000000..33f72e9095 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/tls_ra/tls_ra_server.rs @@ -0,0 +1,323 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Implementation of the server part of the state provisioning. + +use super::{authentication::ClientAuth, ClientProvisioningRequest, Opcode, TcpHeader}; +use crate::{ + attestation::create_ra_report_and_signature, + error::{Error as EnclaveError, Result as EnclaveResult}, + initialization::global_components::{ + EnclaveSealHandler, GLOBAL_INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_SEAL, + GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT, GLOBAL_STATE_KEY_REPOSITORY_COMPONENT, + }, + ocall::OcallApi, + shard_vault::add_shard_vault_proxy, + tls_ra::seal_handler::UnsealStateAndKeys, + GLOBAL_STATE_HANDLER_COMPONENT, +}; +use codec::Decode; +use itp_attestation_handler::RemoteAttestationType; +use itp_component_container::ComponentGetter; +use itp_ocall_api::EnclaveAttestationOCallApi; +use itp_settings::worker_mode::{ProvideWorkerMode, WorkerMode, WorkerModeProvider}; +use itp_types::ShardIdentifier; +use log::*; +use rustls::{ServerConfig, ServerSession, StreamOwned}; +use sgx_types::*; +use std::{ + backtrace::{self, PrintFormat}, + io::{Read, Write}, + net::TcpStream, + sync::Arc, +}; + +#[derive(Clone, Eq, PartialEq, Debug)] +enum ProvisioningPayload { + Everything, + ShieldingKeyAndLightClient, +} + +impl From for ProvisioningPayload { + fn from(m: WorkerMode) -> Self { + match m { + WorkerMode::OffChainWorker | WorkerMode::Teeracle => + ProvisioningPayload::ShieldingKeyAndLightClient, + WorkerMode::Sidechain => ProvisioningPayload::Everything, + } + } +} + +/// Server part of the TCP-level connection and the underlying TLS-level session. +/// +/// Includes a seal handler, which handles the reading part of the data to be sent. +struct TlsServer { + tls_stream: StreamOwned, + seal_handler: StateAndKeyUnsealer, + provisioning_payload: ProvisioningPayload, +} + +impl TlsServer +where + StateAndKeyUnsealer: UnsealStateAndKeys, +{ + fn new( + tls_stream: StreamOwned, + seal_handler: StateAndKeyUnsealer, + provisioning_payload: ProvisioningPayload, + ) -> Self { + Self { tls_stream, seal_handler, provisioning_payload } + } + + /// Sends all relevant data of the specific shard to the client. + fn handle_shard_request_from_client(&mut self) -> EnclaveResult<()> { + println!( + " [Enclave] (MU-RA-Server) handle_shard_request_from_client, calling read_shard()" + ); + let request = self.await_shard_request_from_client()?; + println!(" [Enclave] (MU-RA-Server) handle_shard_request_from_client, await_shard_request_from_client() OK"); + println!(" [Enclave] (MU-RA-Server) handle_shard_request_from_client, write_all()"); + self.write_provisioning_payloads(&request.shard)?; + + info!( + "will make client account 0x{} a proxy of vault for shard {:?}", + hex::encode(request.account.clone()), + request.shard + ); + if let Err(e) = add_shard_vault_proxy(request.shard, &request.account) { + // we can't be sure that registering the proxy will succeed onchain at this point, + // therefore we can accept an error here as the client has to verify anyway and + // retry if it failed + error!("failed to add shard vault proxy for {:?}: {:?}", request.account, e); + }; + Ok(()) + } + + /// Read the shard of the state the client wants to receive. + fn await_shard_request_from_client(&mut self) -> EnclaveResult { + let mut request = [0u8; std::mem::size_of::()]; + println!( + " [Enclave] (MU-RA-Server) await_shard_request_from_client, calling read_exact()" + ); + self.tls_stream.read_exact(&mut request)?; + ClientProvisioningRequest::decode(&mut request.as_slice()) + .map_err(|_| EnclaveError::Other("matching byte size can't fail to decode".into())) + } + + /// Sends all relevant data to the client. + fn write_provisioning_payloads(&mut self, shard: &ShardIdentifier) -> EnclaveResult<()> { + debug!("Provisioning is set to: {:?}", self.provisioning_payload); + match self.provisioning_payload { + ProvisioningPayload::Everything => { + self.write_shielding_key()?; + self.write_state_key()?; + self.write_state(shard)?; + self.write_light_client_state()?; + }, + ProvisioningPayload::ShieldingKeyAndLightClient => { + self.write_shielding_key()?; + self.write_light_client_state()?; + }, + } + + debug!("Successfully provisioned all payloads to peer"); + Ok(()) + } + + fn write_shielding_key(&mut self) -> EnclaveResult<()> { + let shielding_key = self.seal_handler.unseal_shielding_key()?; + self.write(Opcode::ShieldingKey, &shielding_key)?; + Ok(()) + } + + fn write_state_key(&mut self) -> EnclaveResult<()> { + let state_key = self.seal_handler.unseal_state_key()?; + self.write(Opcode::StateKey, &state_key)?; + Ok(()) + } + + fn write_state(&mut self, shard: &ShardIdentifier) -> EnclaveResult<()> { + let state = self.seal_handler.unseal_state(shard)?; + self.write(Opcode::State, &state)?; + Ok(()) + } + + fn write_light_client_state(&mut self) -> EnclaveResult<()> { + let state = self.seal_handler.unseal_light_client_state()?; + self.write(Opcode::LightClient, &state)?; + Ok(()) + } + + /// Sends the header followed by the payload. + fn write(&mut self, opcode: Opcode, bytes: &[u8]) -> EnclaveResult<()> { + let payload_length = bytes.len() as u64; + self.write_header(TcpHeader::new(opcode, payload_length))?; + debug!("Write payload - opcode: {:?}, payload_length: {}", opcode, payload_length); + self.tls_stream.write_all(bytes)?; + Ok(()) + } + + /// Sends the header which includes the payload length and the Opcode indicating the payload type. + fn write_header(&mut self, tcp_header: TcpHeader) -> EnclaveResult<()> { + self.tls_stream.write_all(&tcp_header.opcode.to_bytes())?; + self.tls_stream.write_all(&tcp_header.payload_length.to_be_bytes())?; + debug!( + "Write header - opcode: {:?}, payload length: {}", + tcp_header.opcode, tcp_header.payload_length + ); + Ok(()) + } +} + +#[no_mangle] +pub unsafe extern "C" fn run_state_provisioning_server( + socket_fd: c_int, + sign_type: sgx_quote_sign_type_t, + quoting_enclave_target_info: Option<&sgx_target_info_t>, + quote_size: Option<&u32>, + skip_ra: c_int, +) -> sgx_status_t { + let _ = backtrace::enable_backtrace("enclave.signed.so", PrintFormat::Short); + + let state_handler = match GLOBAL_STATE_HANDLER_COMPONENT.get() { + Ok(s) => s, + Err(e) => { + error!("{:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + let state_key_repository = match GLOBAL_STATE_KEY_REPOSITORY_COMPONENT.get() { + Ok(s) => s, + Err(e) => { + error!("{:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + let shielding_key_repository = match GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT.get() { + Ok(s) => s, + Err(e) => { + error!("{:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + let light_client_seal = match GLOBAL_INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_SEAL.get() { + Ok(s) => s, + Err(e) => { + error!("{:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + let seal_handler = EnclaveSealHandler::new( + state_handler, + state_key_repository, + shielding_key_repository, + light_client_seal, + ); + + if let Err(e) = run_state_provisioning_server_internal::<_, WorkerModeProvider>( + socket_fd, + sign_type, + quoting_enclave_target_info, + quote_size, + skip_ra, + seal_handler, + ) { + error!("Failed to provision state due to: {:?}", e); + return e.into() + }; + + sgx_status_t::SGX_SUCCESS +} + +/// Internal [`run_state_provisioning_server`] function to be able to use the handy `?` operator. +pub(crate) fn run_state_provisioning_server_internal< + StateAndKeyUnsealer: UnsealStateAndKeys, + WorkerModeProvider: ProvideWorkerMode, +>( + socket_fd: c_int, + sign_type: sgx_quote_sign_type_t, + quoting_enclave_target_info: Option<&sgx_target_info_t>, + quote_size: Option<&u32>, + skip_ra: c_int, + seal_handler: StateAndKeyUnsealer, +) -> EnclaveResult<()> { + let server_config = tls_server_config( + sign_type, + quoting_enclave_target_info, + quote_size, + OcallApi, + skip_ra == 1, + )?; + let (server_session, tcp_stream) = tls_server_session_stream(socket_fd, server_config)?; + + let provisioning = ProvisioningPayload::from(WorkerModeProvider::worker_mode()); + + let mut server = + TlsServer::new(StreamOwned::new(server_session, tcp_stream), seal_handler, provisioning); + + // todo: verify client signer belongs to a registered enclave on integritee network with a + // matching or whitelisted MRENCLAVE as replacement for MU RA #1385 + + println!(" [Enclave] (MU-RA-Server) MU-RA successful sending keys"); + println!( + " [Enclave] (MU-RA-Server) MU-RA successful, calling handle_shard_request_from_client()" + ); + server.handle_shard_request_from_client() +} + +fn tls_server_session_stream( + socket_fd: i32, + server_config: ServerConfig, +) -> EnclaveResult<(ServerSession, TcpStream)> { + let sess = ServerSession::new(&Arc::new(server_config)); + let conn = TcpStream::new(socket_fd).map_err(|e| EnclaveError::Other(e.into()))?; + Ok((sess, conn)) +} + +fn tls_server_config( + sign_type: sgx_quote_sign_type_t, + quoting_enclave_target_info: Option<&sgx_target_info_t>, + quote_size: Option<&u32>, + ocall_api: A, + skip_ra: bool, +) -> EnclaveResult { + #[cfg(not(feature = "dcap"))] + let attestation_type = RemoteAttestationType::Epid; + #[cfg(feature = "dcap")] + let attestation_type = RemoteAttestationType::Dcap; + + // report will be signed with server enclave ed25519 signing key + let (key_der, cert_der) = create_ra_report_and_signature( + skip_ra, + attestation_type, + sign_type, + quoting_enclave_target_info, + quote_size, + )?; + + // ClientAuth will perform MU RA as part of authentication process + let mut cfg = rustls::ServerConfig::new(Arc::new(ClientAuth::new(true, skip_ra, ocall_api))); + let certs = vec![rustls::Certificate(cert_der)]; + let privkey = rustls::PrivateKey(key_der); + cfg.set_single_cert_with_ocsp_and_sct(certs, privkey, vec![], vec![]) + .map_err(|e| EnclaveError::Other(e.into()))?; + Ok(cfg) +} diff --git a/bitacross-worker/enclave-runtime/src/top_pool_execution.rs b/bitacross-worker/enclave-runtime/src/top_pool_execution.rs new file mode 100644 index 0000000000..a8168864e3 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/top_pool_execution.rs @@ -0,0 +1,412 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + error::Result, + initialization::global_components::{ + GLOBAL_OCALL_API_COMPONENT, GLOBAL_SIDECHAIN_BLOCK_COMPOSER_COMPONENT, + GLOBAL_SIDECHAIN_FAIL_SLOT_ON_DEMAND_COMPONENT, + GLOBAL_SIDECHAIN_IMPORT_QUEUE_WORKER_COMPONENT, GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT, + GLOBAL_STATE_HANDLER_COMPONENT, GLOBAL_TOP_POOL_AUTHOR_COMPONENT, + }, + sync::{EnclaveLock, EnclaveStateRWLock}, + utils::{ + get_extrinsic_factory_from_integritee_solo_or_parachain, + get_extrinsic_factory_from_target_a_solo_or_parachain, + get_extrinsic_factory_from_target_b_solo_or_parachain, + get_stf_executor_from_solo_or_parachain, + get_triggered_dispatcher_from_integritee_solo_or_parachain, + get_triggered_dispatcher_from_target_a_solo_or_parachain, + get_triggered_dispatcher_from_target_b_solo_or_parachain, + get_validator_accessor_from_integritee_solo_or_parachain, + get_validator_accessor_from_target_a_solo_or_parachain, + get_validator_accessor_from_target_b_solo_or_parachain, + }, +}; +use codec::Encode; +use itc_parentchain::{ + block_import_dispatcher::triggered_dispatcher::TriggerParentchainBlockImport, + light_client::{ + concurrent_access::ValidatorAccess, BlockNumberOps, ExtrinsicSender, LightClientState, + NumberFor, + }, +}; +use itp_component_container::ComponentGetter; +use itp_enclave_metrics::EnclaveMetric; +use itp_extrinsics_factory::CreateExtrinsics; +use itp_ocall_api::{EnclaveMetricsOCallApi, EnclaveOnChainOCallApi, EnclaveSidechainOCallApi}; +use itp_settings::sidechain::SLOT_DURATION; +use itp_sgx_crypto::key_repository::AccessKey; +use itp_sgx_externalities::SgxExternalities; +use itp_stf_state_handler::{handle_state::HandleState, query_shard_state::QueryShardState}; +use itp_time_utils::duration_now; +use itp_types::{parentchain::ParentchainCall, Block, OpaqueCall, H256}; +use itp_utils::if_not_production; +use its_primitives::{ + traits::{ + Block as SidechainBlockTrait, Header as HeaderTrait, ShardIdentifierFor, SignedBlock, + }, + types::block::SignedBlock as SignedSidechainBlock, +}; +use its_sidechain::{ + aura::{proposer_factory::ProposerFactory, Aura, SlotClaimStrategy}, + consensus_common::{Environment, Error as ConsensusError, ProcessBlockImportQueue}, + slots::{yield_next_slot, LastSlot, PerShardSlotWorkerScheduler, SlotInfo}, + validateer_fetch::ValidateerFetch, +}; +use lc_scheduled_enclave::{ScheduledEnclaveUpdater, GLOBAL_SCHEDULED_ENCLAVE}; +use log::*; +use sgx_types::sgx_status_t; +use sp_core::{crypto::UncheckedFrom, Pair}; +use sp_runtime::{ + generic::SignedBlock as SignedParentchainBlock, traits::Block as BlockTrait, MultiSignature, +}; +use std::{sync::Arc, time::Instant, vec::Vec}; + +#[no_mangle] +pub unsafe extern "C" fn execute_trusted_calls() -> sgx_status_t { + if let Err(e) = execute_top_pool_trusted_calls_internal() { + return e.into() + } + + sgx_status_t::SGX_SUCCESS +} + +/// Internal [`execute_trusted_calls`] function to be able to use the `?` operator. +/// +/// Executes `Aura::on_slot() for `slot` if it is this enclave's `Slot`. +/// +/// This function makes an ocall that does the following: +/// +/// * Import all pending parentchain blocks. +/// * Sends sidechain `confirm_block` xt's with the produced sidechain blocks. +/// * Broadcast produced sidechain blocks to peer validateers. +fn execute_top_pool_trusted_calls_internal() -> Result<()> { + let start_time = Instant::now(); + + debug!("----------------------------------------"); + debug!("Start sidechain block production cycle"); + + // We acquire lock explicitly (variable binding), since '_' will drop the lock after the statement. + // See https://medium.com/codechain/rust-underscore-does-not-bind-fec6a18115a8 + let _enclave_write_lock = EnclaveLock::write_all()?; + + let slot_beginning_timestamp = duration_now(); + + let integritee_parentchain_import_dispatcher = + get_triggered_dispatcher_from_integritee_solo_or_parachain()?; + let maybe_target_a_parentchain_import_dispatcher = + get_triggered_dispatcher_from_target_a_solo_or_parachain().ok(); + let maybe_target_b_parentchain_import_dispatcher = + get_triggered_dispatcher_from_target_b_solo_or_parachain().ok(); + + let maybe_latest_target_a_parentchain_header = + if let Some(ref _triggered_dispatcher) = maybe_target_a_parentchain_import_dispatcher { + let validator_access = get_validator_accessor_from_target_a_solo_or_parachain()?; + Some(validator_access.execute_on_validator(|v| { + let latest_parentchain_header = v.latest_finalized_header()?; + Ok(latest_parentchain_header) + })?) + } else { + None + }; + + let maybe_latest_target_b_parentchain_header = + if let Some(ref _triggered_dispatcher) = maybe_target_b_parentchain_import_dispatcher { + let validator_access = get_validator_accessor_from_target_b_solo_or_parachain()?; + Some(validator_access.execute_on_validator(|v| { + let latest_parentchain_header = v.latest_finalized_header()?; + Ok(latest_parentchain_header) + })?) + } else { + None + }; + + let integritee_validator_access = get_validator_accessor_from_integritee_solo_or_parachain()?; + + // This gets the latest imported block. We accept that all of AURA, up until the block production + // itself, will operate on a parentchain block that is potentially outdated by one block + // (in case we have a block in the queue, but not imported yet). + let current_integritee_parentchain_header = + integritee_validator_access.execute_on_validator(|v| { + let latest_parentchain_header = v.latest_finalized_header()?; + Ok(latest_parentchain_header) + })?; + + // Import any sidechain blocks that are in the import queue. In case we are missing blocks, + // a peer sync will happen. If that happens, the slot time might already be used up just by this import. + let sidechain_block_import_queue_worker = + GLOBAL_SIDECHAIN_IMPORT_QUEUE_WORKER_COMPONENT.get()?; + + let latest_integritee_parentchain_header = sidechain_block_import_queue_worker + .process_queue(¤t_integritee_parentchain_header)?; + + trace!( + "Elapsed time to process sidechain block import queue: {} ms", + start_time.elapsed().as_millis() + ); + + let stf_executor = get_stf_executor_from_solo_or_parachain()?; + + let top_pool_author = GLOBAL_TOP_POOL_AUTHOR_COMPONENT.get()?; + + let block_composer = GLOBAL_SIDECHAIN_BLOCK_COMPOSER_COMPONENT.get()?; + + let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; + + let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; + + let authority = GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT.get()?.retrieve_key()?; + + let fail_on_demand = GLOBAL_SIDECHAIN_FAIL_SLOT_ON_DEMAND_COMPONENT.get()?; + + match yield_next_slot( + slot_beginning_timestamp, + SLOT_DURATION, + latest_integritee_parentchain_header, + maybe_latest_target_a_parentchain_header, + maybe_latest_target_b_parentchain_header, + &mut LastSlot, + )? { + Some(slot) => { + if slot.duration_remaining().is_none() { + warn!("No time remaining in slot, skipping AURA execution"); + return Ok(()) + } + log_remaining_slot_duration(&slot, "Before AURA"); + + let shards = state_handler.list_shards()?; + let env = ProposerFactory::::new( + top_pool_author, + stf_executor, + block_composer, + ocall_api.clone(), + ); + + if_not_production!({ + if let Some(ref fail_on_demand) = *fail_on_demand { + fail_on_demand.next_slot(); + if fail_on_demand.check_before_on_slot() { + Result::Err(crate::error::Error::Sgx(sgx_status_t::SGX_ERROR_UNEXPECTED))?; + } + } + }); + + let (blocks, parentchain_calls) = + exec_aura_on_slot::<_, _, SignedSidechainBlock, _, _, _, _, _, _, _>( + slot.clone(), + authority, + ocall_api.clone(), + integritee_parentchain_import_dispatcher, + maybe_target_a_parentchain_import_dispatcher, + maybe_target_b_parentchain_import_dispatcher, + env, + shards, + GLOBAL_SCHEDULED_ENCLAVE.clone(), + state_handler, + )?; + + if_not_production!({ + if let Some(ref fail_on_demand) = *fail_on_demand { + if fail_on_demand.check_after_on_slot() { + Result::Err(crate::error::Error::Sgx(sgx_status_t::SGX_ERROR_UNEXPECTED))?; + } + } + }); + + debug!("Aura executed successfully"); + + // Drop lock as soon as we don't need it anymore. + drop(_enclave_write_lock); + + log_remaining_slot_duration(&slot, "After AURA"); + + send_blocks_and_extrinsics::(blocks, parentchain_calls, ocall_api)?; + + log_remaining_slot_duration(&slot, "After broadcasting and sending extrinsic"); + }, + None => { + debug!("No slot yielded. Skipping block production."); + return Ok(()) + }, + }; + + debug!("End sidechain block production cycle"); + Ok(()) +} + +/// Executes aura for the given `slot`. +#[allow(clippy::too_many_arguments)] +pub(crate) fn exec_aura_on_slot< + Authority, + ParentchainBlock, + SignedSidechainBlock, + OCallApi, + PEnvironment, + IntegriteeBlockImportTrigger, + TargetABlockImportTrigger, + TargetBBlockImportTrigger, + ScheduledEnclave, + StateHandler, +>( + slot: SlotInfo, + authority: Authority, + ocall_api: Arc, + integritee_block_import_trigger: Arc, + maybe_target_a_block_import_trigger: Option>, + maybe_target_b_block_import_trigger: Option>, + proposer_environment: PEnvironment, + shards: Vec>, + scheduled_enclave: Arc, + state_handler: Arc, +) -> Result<(Vec, Vec)> +where + ParentchainBlock: BlockTrait, + SignedSidechainBlock: + SignedBlock + 'static, // Setting the public type is necessary due to some non-generic downstream code. + SignedSidechainBlock::Block: SidechainBlockTrait, + <::Block as SidechainBlockTrait>::HeaderType: + HeaderTrait, + SignedSidechainBlock::Signature: From, + Authority: Pair, + Authority::Public: Encode + UncheckedFrom<[u8; 32]>, + OCallApi: ValidateerFetch + EnclaveOnChainOCallApi + EnclaveSidechainOCallApi + Send + 'static, + NumberFor: BlockNumberOps, + PEnvironment: + Environment + Send + Sync, + IntegriteeBlockImportTrigger: + TriggerParentchainBlockImport>, + TargetABlockImportTrigger: + TriggerParentchainBlockImport>, + TargetBBlockImportTrigger: + TriggerParentchainBlockImport>, + ScheduledEnclave: ScheduledEnclaveUpdater, + StateHandler: HandleState, +{ + debug!("[Aura] Executing aura for slot: {:?}", slot); + + let mut aura = + Aura::<_, ParentchainBlock, SignedSidechainBlock, PEnvironment, _, _, _, _, _, _>::new( + authority, + ocall_api.as_ref().clone(), + integritee_block_import_trigger, + maybe_target_a_block_import_trigger, + maybe_target_b_block_import_trigger, + proposer_environment, + scheduled_enclave, + state_handler, + ) + .with_claim_strategy(SlotClaimStrategy::RoundRobin); + + // We only check if there are more workers registered, which might not really mean they are + // online and syncing sidechain state but that should be enough for now. + let is_single_worker = match ocall_api.get_trusted_peers_urls() { + Ok(urls) => urls.is_empty(), + Err(e) => { + warn!("Could not get trusted peers urls, error: {:?}", e); + warn!("Falling back to non single worker mode"); + false + }, + }; + + let (blocks, pxts): (Vec<_>, Vec<_>) = + PerShardSlotWorkerScheduler::on_slot(&mut aura, slot, shards, is_single_worker) + .into_iter() + .map(|r| (r.block, r.parentchain_effects)) + .unzip(); + + let opaque_calls: Vec = pxts.into_iter().flatten().collect(); + Ok((blocks, opaque_calls)) +} + +/// Broadcasts sidechain blocks to fellow peers and sends opaque calls as extrinsic to the parentchain. +pub(crate) fn send_blocks_and_extrinsics( + blocks: Vec, + parentchain_calls: Vec, + ocall_api: Arc, +) -> Result<()> +where + ParentchainBlock: BlockTrait, + SignedSidechainBlock: SignedBlock + 'static, + OCallApi: EnclaveSidechainOCallApi + EnclaveMetricsOCallApi, + NumberFor: BlockNumberOps, +{ + let started = std::time::Instant::now(); + debug!("Proposing {} sidechain block(s) (broadcasting to peers)", blocks.len()); + ocall_api.propose_sidechain_blocks(blocks)?; + if let Err(e) = + ocall_api.update_metric(EnclaveMetric::SidechainBlockBroadcastingTime(started.elapsed())) + { + warn!("Failed to update metric for sidechain block broadcasting time: {:?}", e); + }; + + let calls: Vec = parentchain_calls + .iter() + .filter_map(|parentchain_call| parentchain_call.as_litentry()) + .collect(); + debug!("Enclave wants to send {} extrinsics to Integritee Parentchain", calls.len()); + if !calls.is_empty() { + let extrinsics_factory = get_extrinsic_factory_from_integritee_solo_or_parachain()?; + let xts = extrinsics_factory.create_extrinsics(calls.as_slice(), None)?; + let validator_access = get_validator_accessor_from_integritee_solo_or_parachain()?; + validator_access.execute_mut_on_validator(|v| v.send_extrinsics(xts))?; + } + let calls: Vec = parentchain_calls + .iter() + .filter_map(|parentchain_call| parentchain_call.as_target_a()) + .collect(); + debug!("Enclave wants to send {} extrinsics to TargetA Parentchain", calls.len()); + if !calls.is_empty() { + let extrinsics_factory = get_extrinsic_factory_from_target_a_solo_or_parachain()?; + let xts = extrinsics_factory.create_extrinsics(calls.as_slice(), None)?; + let validator_access = get_validator_accessor_from_target_a_solo_or_parachain()?; + validator_access.execute_mut_on_validator(|v| v.send_extrinsics(xts))?; + } + let calls: Vec = parentchain_calls + .iter() + .filter_map(|parentchain_call| parentchain_call.as_target_b()) + .collect(); + debug!("Enclave wants to send {} extrinsics to TargetB Parentchain", calls.len()); + if !calls.is_empty() { + let extrinsics_factory = get_extrinsic_factory_from_target_b_solo_or_parachain()?; + let xts = extrinsics_factory.create_extrinsics(calls.as_slice(), None)?; + let validator_access = get_validator_accessor_from_target_b_solo_or_parachain()?; + validator_access.execute_mut_on_validator(|v| v.send_extrinsics(xts))?; + } + + Ok(()) +} + +fn log_remaining_slot_duration>( + slot_info: &SlotInfo, + stage_name: &str, +) { + match slot_info.duration_remaining() { + None => { + info!("No time remaining in slot (id: {:?}, stage: {})", slot_info.slot, stage_name); + }, + Some(remainder) => { + trace!( + "Remaining time in slot (id: {:?}, stage {}): {} ms, {}% of slot time", + slot_info.slot, + stage_name, + remainder.as_millis(), + (remainder.as_millis() as f64 / slot_info.duration.as_millis() as f64) * 100f64 + ); + }, + }; +} diff --git a/bitacross-worker/enclave-runtime/src/utils.rs b/bitacross-worker/enclave-runtime/src/utils.rs new file mode 100644 index 0000000000..47ff73ce26 --- /dev/null +++ b/bitacross-worker/enclave-runtime/src/utils.rs @@ -0,0 +1,279 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +use crate::{ + error::{Error, Result}, + initialization::global_components::{ + EnclaveExtrinsicsFactory, EnclaveNodeMetadataRepository, EnclaveStfEnclaveSigner, + EnclaveStfExecutor, EnclaveValidatorAccessor, + IntegriteeParentchainTriggeredBlockImportDispatcher, + TargetAParentchainTriggeredBlockImportDispatcher, + TargetBParentchainTriggeredBlockImportDispatcher, + GLOBAL_INTEGRITEE_PARACHAIN_HANDLER_COMPONENT, + GLOBAL_INTEGRITEE_SOLOCHAIN_HANDLER_COMPONENT, GLOBAL_TARGET_A_PARACHAIN_HANDLER_COMPONENT, + GLOBAL_TARGET_A_SOLOCHAIN_HANDLER_COMPONENT, GLOBAL_TARGET_B_PARACHAIN_HANDLER_COMPONENT, + GLOBAL_TARGET_B_SOLOCHAIN_HANDLER_COMPONENT, + }, +}; +use codec::{Decode, Input}; +use itc_parentchain_block_import_dispatcher::BlockImportDispatcher; +use itp_component_container::ComponentGetter; +use std::{result::Result as StdResult, slice, sync::Arc}; + +/// Helper trait to transform the sgx-ffi pointers to any type that implements +/// `parity-scale-codec::Decode` +pub unsafe trait DecodeRaw { + /// the type to decode into + type Decoded: Decode; + + unsafe fn decode_raw<'a, T>( + data: *const T, + len: usize, + ) -> StdResult + where + T: 'a, + &'a [T]: Input; +} + +unsafe impl DecodeRaw for D { + type Decoded = D; + + unsafe fn decode_raw<'a, T>( + data: *const T, + len: usize, + ) -> StdResult + where + T: 'a, + &'a [T]: Input, + { + let mut s = slice::from_raw_parts(data, len); + + Decode::decode(&mut s) + } +} + +pub unsafe fn utf8_str_from_raw<'a>( + data: *const u8, + len: usize, +) -> StdResult<&'a str, std::str::Utf8Error> { + let bytes = slice::from_raw_parts(data, len); + + std::str::from_utf8(bytes) +} + +// FIXME: When solving #1080, these helper functions should be obsolete, because no dynamic allocation +// is necessary anymore. +pub(crate) fn get_triggered_dispatcher_from_integritee_solo_or_parachain( +) -> Result> { + let dispatcher = + if let Ok(solochain_handler) = GLOBAL_INTEGRITEE_SOLOCHAIN_HANDLER_COMPONENT.get() { + get_triggered_dispatcher(solochain_handler.import_dispatcher.clone())? + } else if let Ok(parachain_handler) = GLOBAL_INTEGRITEE_PARACHAIN_HANDLER_COMPONENT.get() { + get_triggered_dispatcher(parachain_handler.import_dispatcher.clone())? + } else { + return Err(Error::NoLitentryParentchainAssigned) + }; + Ok(dispatcher) +} + +pub(crate) fn get_triggered_dispatcher_from_target_a_solo_or_parachain( +) -> Result> { + let dispatcher = + if let Ok(solochain_handler) = GLOBAL_TARGET_A_SOLOCHAIN_HANDLER_COMPONENT.get() { + get_triggered_dispatcher(solochain_handler.import_dispatcher.clone())? + } else if let Ok(parachain_handler) = GLOBAL_TARGET_A_PARACHAIN_HANDLER_COMPONENT.get() { + get_triggered_dispatcher(parachain_handler.import_dispatcher.clone())? + } else { + return Err(Error::NoTargetAParentchainAssigned) + }; + Ok(dispatcher) +} + +pub(crate) fn get_triggered_dispatcher_from_target_b_solo_or_parachain( +) -> Result> { + let dispatcher = + if let Ok(solochain_handler) = GLOBAL_TARGET_B_SOLOCHAIN_HANDLER_COMPONENT.get() { + get_triggered_dispatcher(solochain_handler.import_dispatcher.clone())? + } else if let Ok(parachain_handler) = GLOBAL_TARGET_B_PARACHAIN_HANDLER_COMPONENT.get() { + get_triggered_dispatcher(parachain_handler.import_dispatcher.clone())? + } else { + return Err(Error::NoTargetBParentchainAssigned) + }; + Ok(dispatcher) +} + +pub(crate) fn get_triggered_dispatcher( + dispatcher: Arc>, +) -> Result> { + let triggered_dispatcher = dispatcher + .triggered_dispatcher() + .ok_or(Error::ExpectedTriggeredImportDispatcher)?; + Ok(triggered_dispatcher) +} + +pub(crate) fn get_validator_accessor_from_integritee_solo_or_parachain( +) -> Result> { + let validator_accessor = + if let Ok(solochain_handler) = GLOBAL_INTEGRITEE_SOLOCHAIN_HANDLER_COMPONENT.get() { + solochain_handler.validator_accessor.clone() + } else if let Ok(parachain_handler) = GLOBAL_INTEGRITEE_PARACHAIN_HANDLER_COMPONENT.get() { + parachain_handler.validator_accessor.clone() + } else { + return Err(Error::NoLitentryParentchainAssigned) + }; + Ok(validator_accessor) +} + +pub(crate) fn get_validator_accessor_from_target_a_solo_or_parachain( +) -> Result> { + let validator_accessor = + if let Ok(solochain_handler) = GLOBAL_TARGET_A_SOLOCHAIN_HANDLER_COMPONENT.get() { + solochain_handler.validator_accessor.clone() + } else if let Ok(parachain_handler) = GLOBAL_TARGET_A_PARACHAIN_HANDLER_COMPONENT.get() { + parachain_handler.validator_accessor.clone() + } else { + return Err(Error::NoTargetAParentchainAssigned) + }; + Ok(validator_accessor) +} + +pub(crate) fn get_validator_accessor_from_target_b_solo_or_parachain( +) -> Result> { + let validator_accessor = + if let Ok(solochain_handler) = GLOBAL_TARGET_B_SOLOCHAIN_HANDLER_COMPONENT.get() { + solochain_handler.validator_accessor.clone() + } else if let Ok(parachain_handler) = GLOBAL_TARGET_B_PARACHAIN_HANDLER_COMPONENT.get() { + parachain_handler.validator_accessor.clone() + } else { + return Err(Error::NoTargetBParentchainAssigned) + }; + Ok(validator_accessor) +} + +pub(crate) fn get_node_metadata_repository_from_integritee_solo_or_parachain( +) -> Result> { + let metadata_repository = + if let Ok(solochain_handler) = GLOBAL_INTEGRITEE_SOLOCHAIN_HANDLER_COMPONENT.get() { + solochain_handler.node_metadata_repository.clone() + } else if let Ok(parachain_handler) = GLOBAL_INTEGRITEE_PARACHAIN_HANDLER_COMPONENT.get() { + parachain_handler.node_metadata_repository.clone() + } else { + return Err(Error::NoLitentryParentchainAssigned) + }; + Ok(metadata_repository) +} + +pub(crate) fn get_node_metadata_repository_from_target_a_solo_or_parachain( +) -> Result> { + let metadata_repository = + if let Ok(solochain_handler) = GLOBAL_TARGET_A_SOLOCHAIN_HANDLER_COMPONENT.get() { + solochain_handler.node_metadata_repository.clone() + } else if let Ok(parachain_handler) = GLOBAL_TARGET_A_PARACHAIN_HANDLER_COMPONENT.get() { + parachain_handler.node_metadata_repository.clone() + } else { + return Err(Error::NoTargetAParentchainAssigned) + }; + Ok(metadata_repository) +} + +pub(crate) fn get_node_metadata_repository_from_target_b_solo_or_parachain( +) -> Result> { + let metadata_repository = + if let Ok(solochain_handler) = GLOBAL_TARGET_B_SOLOCHAIN_HANDLER_COMPONENT.get() { + solochain_handler.node_metadata_repository.clone() + } else if let Ok(parachain_handler) = GLOBAL_TARGET_B_PARACHAIN_HANDLER_COMPONENT.get() { + parachain_handler.node_metadata_repository.clone() + } else { + return Err(Error::NoTargetBParentchainAssigned) + }; + Ok(metadata_repository) +} + +pub(crate) fn get_extrinsic_factory_from_integritee_solo_or_parachain( +) -> Result> { + let extrinsics_factory = + if let Ok(solochain_handler) = GLOBAL_INTEGRITEE_SOLOCHAIN_HANDLER_COMPONENT.get() { + solochain_handler.extrinsics_factory.clone() + } else if let Ok(parachain_handler) = GLOBAL_INTEGRITEE_PARACHAIN_HANDLER_COMPONENT.get() { + parachain_handler.extrinsics_factory.clone() + } else { + return Err(Error::NoLitentryParentchainAssigned) + }; + Ok(extrinsics_factory) +} + +pub(crate) fn get_extrinsic_factory_from_target_a_solo_or_parachain( +) -> Result> { + let extrinsics_factory = + if let Ok(solochain_handler) = GLOBAL_TARGET_A_SOLOCHAIN_HANDLER_COMPONENT.get() { + solochain_handler.extrinsics_factory.clone() + } else if let Ok(parachain_handler) = GLOBAL_TARGET_A_PARACHAIN_HANDLER_COMPONENT.get() { + parachain_handler.extrinsics_factory.clone() + } else { + return Err(Error::NoTargetAParentchainAssigned) + }; + Ok(extrinsics_factory) +} + +pub(crate) fn get_extrinsic_factory_from_target_b_solo_or_parachain( +) -> Result> { + let extrinsics_factory = + if let Ok(solochain_handler) = GLOBAL_TARGET_B_SOLOCHAIN_HANDLER_COMPONENT.get() { + solochain_handler.extrinsics_factory.clone() + } else if let Ok(parachain_handler) = GLOBAL_TARGET_B_PARACHAIN_HANDLER_COMPONENT.get() { + parachain_handler.extrinsics_factory.clone() + } else { + return Err(Error::NoTargetBParentchainAssigned) + }; + Ok(extrinsics_factory) +} + +pub(crate) fn get_stf_executor_from_solo_or_parachain() -> Result> { + let stf_executor = + if let Ok(solochain_handler) = GLOBAL_INTEGRITEE_SOLOCHAIN_HANDLER_COMPONENT.get() { + solochain_handler.stf_executor.clone() + } else if let Ok(parachain_handler) = GLOBAL_INTEGRITEE_PARACHAIN_HANDLER_COMPONENT.get() { + parachain_handler.stf_executor.clone() + } else { + return Err(Error::NoLitentryParentchainAssigned) + }; + Ok(stf_executor) +} + +pub(crate) fn get_stf_enclave_signer_from_solo_or_parachain() -> Result> +{ + let stf_enclave_signer = + if let Ok(solochain_handler) = GLOBAL_INTEGRITEE_SOLOCHAIN_HANDLER_COMPONENT.get() { + match &*solochain_handler.import_dispatcher { + BlockImportDispatcher::TriggeredDispatcher(dispatcher) => + dispatcher.block_importer.indirect_calls_executor.stf_enclave_signer.clone(), + BlockImportDispatcher::ImmediateDispatcher(dispatcher) => + dispatcher.block_importer.indirect_calls_executor.stf_enclave_signer.clone(), + _ => return Err(Error::NoLitentryParentchainAssigned), + } + } else if let Ok(parachain_handler) = GLOBAL_INTEGRITEE_PARACHAIN_HANDLER_COMPONENT.get() { + match &*parachain_handler.import_dispatcher { + BlockImportDispatcher::TriggeredDispatcher(dispatcher) => + dispatcher.block_importer.indirect_calls_executor.stf_enclave_signer.clone(), + BlockImportDispatcher::ImmediateDispatcher(dispatcher) => + dispatcher.block_importer.indirect_calls_executor.stf_enclave_signer.clone(), + _ => return Err(Error::NoLitentryParentchainAssigned), + } + } else { + return Err(Error::NoLitentryParentchainAssigned) + }; + Ok(stf_enclave_signer) +} diff --git a/bitacross-worker/enclave-runtime/src/vc_issuance_task.rs b/bitacross-worker/enclave-runtime/src/vc_issuance_task.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bitacross-worker/enclave-runtime/x86_64-unknown-linux-sgx.json b/bitacross-worker/enclave-runtime/x86_64-unknown-linux-sgx.json new file mode 100644 index 0000000000..10d37a7490 --- /dev/null +++ b/bitacross-worker/enclave-runtime/x86_64-unknown-linux-sgx.json @@ -0,0 +1,31 @@ +{ + "arch": "x86_64", + "cpu": "x86-64", + "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", + "dynamic-linking": true, + "env": "sgx", + "exe-allocation-crate": "alloc_system", + "executables": true, + "has-elf-tls": true, + "has-rpath": true, + "linker-flavor": "gcc", + "linker-is-gnu": true, + "llvm-target": "x86_64-unknown-linux-gnu", + "max-atomic-width": 64, + "os": "linux", + "position-independent-executables": true, + "pre-link-args": { + "gcc": [ + "-Wl,--as-needed", + "-Wl,-z,noexecstack", + "-m64" + ] + }, + "relro-level": "full", + "stack-probes": true, + "target-c-int-width": "32", + "target-endian": "little", + "target-family": "unix", + "target-pointer-width": "64", + "vendor": "mesalock" +} diff --git a/bitacross-worker/extract_identity b/bitacross-worker/extract_identity new file mode 100755 index 0000000000..2c79268c15 --- /dev/null +++ b/bitacross-worker/extract_identity @@ -0,0 +1,28 @@ +#!/usr/bin/python3 + +import argparse + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument('--mrsigner', action="store_true") + args = parser.parse_args() + + line = "" + + searched_header = "enclave_hash.m" + output_header = "MRENCLAVE" + if args.mrsigner: + searched_header = "mrsigner->value" + output_header = "MRSIGNER" + while searched_header not in line: + line = input() + value = list() + line = input() + while line.startswith("0x"): + value += line.strip().split() + try: + line = input() + except: + break + value = "".join(map(lambda x: x.replace("0x",""), value)) +print("{}: {}".format(output_header, value)) diff --git a/bitacross-worker/lib/readme.txt b/bitacross-worker/lib/readme.txt new file mode 100644 index 0000000000..7951405f85 --- /dev/null +++ b/bitacross-worker/lib/readme.txt @@ -0,0 +1 @@ +lib \ No newline at end of file diff --git a/bitacross-worker/license_header_scs.txt b/bitacross-worker/license_header_scs.txt new file mode 100644 index 0000000000..6ded8ce2fd --- /dev/null +++ b/bitacross-worker/license_header_scs.txt @@ -0,0 +1,16 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ \ No newline at end of file diff --git a/bitacross-worker/litentry/core/scheduled-enclave/Cargo.toml b/bitacross-worker/litentry/core/scheduled-enclave/Cargo.toml new file mode 100644 index 0000000000..8e6f1904e7 --- /dev/null +++ b/bitacross-worker/litentry/core/scheduled-enclave/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "lc-scheduled-enclave" +version = "0.8.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +codec = { version = "3.0.0", default-features = false, features = ["derive"], package = "parity-scale-codec" } +lazy_static = { version = "1.1.0", features = ["spin_no_std"] } +log = { version = "0.4", default-features = false } +sp-std = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +thiserror = { version = "1.0.26", optional = true } + +# sgx-deps +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", features = ["untrusted_fs"], optional = true } +thiserror-sgx = { package = "thiserror", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3", optional = true } + +# local dependencies +itp-settings = { path = "../../../core-primitives/settings" } +itp-sgx-io = { path = "../../../core-primitives/sgx/io", default-features = false } +itp-types = { path = "../../../core-primitives/types", default-features = false } + +[features] +default = ["std"] +sgx = [ + "sgx_tstd", + "thiserror-sgx", + "itp-sgx-io/sgx", +] +std = [ + "thiserror", + "itp-sgx-io/std", + "itp-types/std", + "sp-std/std", + "codec/std", +] diff --git a/bitacross-worker/litentry/core/scheduled-enclave/src/error.rs b/bitacross-worker/litentry/core/scheduled-enclave/src/error.rs new file mode 100644 index 0000000000..6353db15f5 --- /dev/null +++ b/bitacross-worker/litentry/core/scheduled-enclave/src/error.rs @@ -0,0 +1,51 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +use std::boxed::Box; +#[cfg(feature = "sgx")] +use thiserror_sgx as thiserror; + +pub type Result = core::result::Result; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("poison lock")] + PoisonLock, + #[error("empty ScheduledEnclave registry")] + EmptyRegistry, + #[error("no previous MRENCLAVE")] + NoPreviousMRENCLAVE, + #[error(transparent)] + Other(#[from] Box), +} + +impl From for Error { + fn from(e: std::io::Error) -> Self { + Self::Other(e.into()) + } +} + +impl From for Error { + #[cfg(feature = "std")] + fn from(e: codec::Error) -> Self { + Self::Other(e.into()) + } + + #[cfg(feature = "sgx")] + fn from(e: codec::Error) -> Self { + Self::Other(std::format!("{:?}", e).into()) + } +} diff --git a/bitacross-worker/litentry/core/scheduled-enclave/src/io.rs b/bitacross-worker/litentry/core/scheduled-enclave/src/io.rs new file mode 100644 index 0000000000..9912fe4a6f --- /dev/null +++ b/bitacross-worker/litentry/core/scheduled-enclave/src/io.rs @@ -0,0 +1,179 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +use crate::{ + error::{Error, Result}, + MrEnclave, ScheduledEnclave, ScheduledEnclaveUpdater, SidechainBlockNumber, + GLOBAL_SCHEDULED_ENCLAVE, +}; + +#[cfg(feature = "sgx")] +mod sgx { + use crate::{ + error::{Error, Result}, + ScheduledEnclaveMap, + }; + pub use codec::{Decode, Encode}; + pub use itp_settings::files::SCHEDULED_ENCLAVE_FILE; + pub use itp_sgx_io::{seal, unseal, SealedIO}; + pub use log::*; + pub use std::{boxed::Box, fs, path::PathBuf, sgxfs::SgxFile, sync::Arc}; + + #[derive(Clone, Debug)] + pub struct ScheduledEnclaveSeal { + base_path: PathBuf, + } + + impl ScheduledEnclaveSeal { + pub fn new(base_path: PathBuf) -> Self { + Self { base_path } + } + + pub fn path(&self) -> PathBuf { + self.base_path.join(SCHEDULED_ENCLAVE_FILE) + } + } + + impl SealedIO for ScheduledEnclaveSeal { + type Error = Error; + type Unsealed = ScheduledEnclaveMap; + + fn unseal(&self) -> Result { + Ok(unseal(self.path()).map(|b| Decode::decode(&mut b.as_slice()))??) + } + + fn seal(&self, unsealed: &Self::Unsealed) -> Result<()> { + info!("Seal scheduled enclave to file: {:?}", unsealed); + Ok(unsealed.using_encoded(|bytes| seal(bytes, self.path()))?) + } + } +} + +#[cfg(feature = "sgx")] +use sgx::*; + +// TODO: unit-test +impl ScheduledEnclaveUpdater for ScheduledEnclave { + #[cfg(feature = "std")] + fn init(&self, _mrenclave: MrEnclave) -> Result<()> { + Ok(()) + } + + #[cfg(feature = "std")] + fn update(&self, _sbn: SidechainBlockNumber, _mrenclave: MrEnclave) -> Result<()> { + Ok(()) + } + + #[cfg(feature = "std")] + fn remove(&self, _sbn: SidechainBlockNumber) -> Result<()> { + Ok(()) + } + + // if `SCHEDULED_ENCLAVE_FILE` exists, unseal and init from it + // otherwise create a new instance and seal to static file + #[cfg(feature = "sgx")] + fn init(&self, mrenclave: MrEnclave) -> Result<()> { + let _ = self.set_current_mrenclave(mrenclave)?; + let _ = self.set_block_production_paused(false)?; + let enclave_seal = ScheduledEnclaveSeal::new(self.seal_path.clone()); + if SgxFile::open(SCHEDULED_ENCLAVE_FILE).is_err() { + info!( + "[Enclave] ScheduledEnclave file not found, creating new! {}", + SCHEDULED_ENCLAVE_FILE + ); + let mut registry = + GLOBAL_SCHEDULED_ENCLAVE.registry.write().map_err(|_| Error::PoisonLock)?; + registry.clear(); + registry.insert(0, mrenclave); + enclave_seal.seal(&*registry) + } else { + let m = enclave_seal.unseal()?; + info!("[Enclave] ScheduledEnclave unsealed from file: {:?}", m); + let mut registry = + GLOBAL_SCHEDULED_ENCLAVE.registry.write().map_err(|_| Error::PoisonLock)?; + *registry = m; + Ok(()) + } + } + + #[cfg(feature = "sgx")] + fn update(&self, sbn: SidechainBlockNumber, mrenclave: MrEnclave) -> Result<()> { + let mut registry = + GLOBAL_SCHEDULED_ENCLAVE.registry.write().map_err(|_| Error::PoisonLock)?; + registry.insert(sbn, mrenclave); + ScheduledEnclaveSeal::new(self.seal_path.clone()).seal(&*registry) + } + + #[cfg(feature = "sgx")] + fn remove(&self, sbn: SidechainBlockNumber) -> Result<()> { + let mut registry = + GLOBAL_SCHEDULED_ENCLAVE.registry.write().map_err(|_| Error::PoisonLock)?; + let old_value = registry.remove(&sbn); + if old_value.is_some() { + return ScheduledEnclaveSeal::new(self.seal_path.clone()).seal(&*registry) + } + Ok(()) + } + + fn get_current_mrenclave(&self) -> Result { + self.current_mrenclave.read().map_err(|_| Error::PoisonLock).map(|l| *l) + } + + fn set_current_mrenclave(&self, mrenclave: MrEnclave) -> Result<()> { + let mut m = self.current_mrenclave.write().map_err(|_| Error::PoisonLock)?; + *m = mrenclave; + Ok(()) + } + + fn get_expected_mrenclave(&self, sbn: SidechainBlockNumber) -> Result { + let registry = GLOBAL_SCHEDULED_ENCLAVE.registry.read().map_err(|_| Error::PoisonLock)?; + let r = registry + .iter() + .filter(|(k, _)| **k <= sbn) + .max_by_key(|(k, _)| **k) + .ok_or(Error::EmptyRegistry)?; + Ok(*r.1) + } + + fn get_previous_mrenclave(&self, sbn: SidechainBlockNumber) -> Result { + // TODO: optimise it + let registry = GLOBAL_SCHEDULED_ENCLAVE.registry.read().map_err(|_| Error::PoisonLock)?; + let r = registry + .iter() + .filter(|(k, _)| **k <= sbn) + .max_by_key(|(k, _)| **k) + .ok_or(Error::NoPreviousMRENCLAVE)?; + let v = registry + .iter() + .filter(|(k, _)| **k < *r.0) + .max_by_key(|(k, _)| **k) + .ok_or(Error::NoPreviousMRENCLAVE)?; + Ok(*v.1) + } + + fn is_block_production_paused(&self) -> Result { + self.block_production_paused.read().map_err(|_| Error::PoisonLock).map(|l| *l) + } + + fn set_block_production_paused(&self, should_pause: bool) -> Result<()> { + let mut p = self.block_production_paused.write().map_err(|_| Error::PoisonLock)?; + *p = should_pause; + Ok(()) + } +} diff --git a/bitacross-worker/litentry/core/scheduled-enclave/src/lib.rs b/bitacross-worker/litentry/core/scheduled-enclave/src/lib.rs new file mode 100644 index 0000000000..e71edfb88a --- /dev/null +++ b/bitacross-worker/litentry/core/scheduled-enclave/src/lib.rs @@ -0,0 +1,153 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(feature = "sgx")] +extern crate sgx_tstd as std; + +// TODO: maybe use parachain primitives for single source of truth +use itp_types::{MrEnclave, SidechainBlockNumber}; +use sp_std::collections::btree_map::BTreeMap; +use std::path::PathBuf; + +pub mod error; +use error::Result; +pub mod io; + +#[cfg(feature = "std")] +use std::sync::RwLock; +#[cfg(feature = "sgx")] +use std::sync::SgxRwLock as RwLock; + +use lazy_static::lazy_static; +use std::sync::Arc; + +lazy_static! { + /// Global instance of a ScheduledEnclave + pub static ref GLOBAL_SCHEDULED_ENCLAVE: Arc = Default::default(); +} + +pub type ScheduledEnclaveMap = BTreeMap; + +#[derive(Default)] +pub struct ScheduledEnclave { + pub block_production_paused: RwLock, + pub current_mrenclave: RwLock, + pub registry: RwLock, + pub seal_path: PathBuf, +} + +pub trait ScheduledEnclaveUpdater { + fn init(&self, mrenclave: MrEnclave) -> Result<()>; + + fn update(&self, sbn: SidechainBlockNumber, mrenclave: MrEnclave) -> Result<()>; + + fn remove(&self, sbn: SidechainBlockNumber) -> Result<()>; + + fn get_current_mrenclave(&self) -> Result; + + fn set_current_mrenclave(&self, mrenclave: MrEnclave) -> Result<()>; + + // given a SidechainBlockNumber, return the expected MRENCLAVE + // For example, the registry is: + // 0 -> 0xAA + // 19 -> 0xBB + // 21 -> 0xCC + // + // get_expected_mrenclave(0) -> 0xAA + // get_expected_mrenclave(18) -> 0xAA + // get_expected_mrenclave(19) -> 0xBB + // get_expected_mrenclave(20) -> 0xBB + // get_expected_mrenclave(21) -> 0xCC + // get_expected_mrenclave(30) -> 0xCC + fn get_expected_mrenclave(&self, sbn: SidechainBlockNumber) -> Result; + + // given a SidechainBlockNumber, return the previous MRENCLAVE + // we can't simply use `get_previous_mrenclave(sbn - 1)` due to possible gap + // For example, the registry is: + // 0 -> 0xAA + // 19 -> 0xBB + // 21 -> 0xCC + // + // get_previous_mrenclave(0) -> NoPreviousMRENCLAVE error + // get_previous_mrenclave(1) -> NoPreviousMRENCLAVE error + // get_previous_mrenclave(19) -> 0xAA + // get_previous_mrenclave(20) -> 0xAA + // get_previous_mrenclave(21) -> 0xBB + // get_previous_mrenclave(30) -> 0xBB + fn get_previous_mrenclave(&self, sbn: SidechainBlockNumber) -> Result; + + fn is_block_production_paused(&self) -> Result; + + fn set_block_production_paused(&self, should_pause: bool) -> Result<()>; + + fn is_mrenclave_matching(&self, sbn: SidechainBlockNumber) -> bool { + let current = self.get_current_mrenclave(); + let expected = self.get_expected_mrenclave(sbn); + + if current.is_err() || expected.is_err() { + return false + } + + current.unwrap() == expected.unwrap() + } +} + +#[derive(Default)] +pub struct ScheduledEnclaveMock; + +// todo! +impl ScheduledEnclaveUpdater for ScheduledEnclaveMock { + fn init(&self, _mrenclave: MrEnclave) -> Result<()> { + Ok(()) + } + + fn update(&self, _sbn: SidechainBlockNumber, _mrenclave: MrEnclave) -> Result<()> { + Ok(()) + } + + fn remove(&self, _sbn: SidechainBlockNumber) -> Result<()> { + Ok(()) + } + + fn get_current_mrenclave(&self) -> Result { + Ok(MrEnclave::default()) + } + + fn set_current_mrenclave(&self, _mrenclave: MrEnclave) -> Result<()> { + Ok(()) + } + + fn get_expected_mrenclave(&self, _sbn: SidechainBlockNumber) -> Result { + Ok(MrEnclave::default()) + } + + fn get_previous_mrenclave(&self, _sbn: SidechainBlockNumber) -> Result { + Ok(MrEnclave::default()) + } + + fn is_block_production_paused(&self) -> Result { + Ok(false) + } + + fn set_block_production_paused(&self, _should_pause: bool) -> Result<()> { + Ok(()) + } +} diff --git a/bitacross-worker/litentry/macros/Cargo.toml b/bitacross-worker/litentry/macros/Cargo.toml new file mode 100644 index 0000000000..c3039927e1 --- /dev/null +++ b/bitacross-worker/litentry/macros/Cargo.toml @@ -0,0 +1,12 @@ +[package] +authors = ["Trust Computing GmbH "] +name = "litentry-macros" +version = "0.1.0" +edition = "2021" + +[dependencies] +cargo_toml = "0.16.3" +quote = "1.0.33" + +[lib] +proc-macro = true diff --git a/bitacross-worker/litentry/macros/src/lib.rs b/bitacross-worker/litentry/macros/src/lib.rs new file mode 100644 index 0000000000..b57ac19473 --- /dev/null +++ b/bitacross-worker/litentry/macros/src/lib.rs @@ -0,0 +1,59 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +use cargo_toml::{Dependency, Manifest}; +use proc_macro::TokenStream; +use quote::quote; +use std::fs; + +#[proc_macro] +pub fn local_modules(_item: TokenStream) -> TokenStream { + let mut deps: Vec = vec![]; + read_module_names("", ".", &mut deps); + let output = quote! { + { + let deps: Vec<&str> = vec![ + #(#deps),* + ]; + deps + } + }; + output.into() +} + +fn read_module_names(path: &str, relative_to: &str, module_names: &mut Vec) { + let current_path = relative_to.to_string() + "/" + path; + let cargo_file = current_path.to_string() + "/Cargo.toml"; + let contents = fs::read_to_string(&cargo_file) + .unwrap_or_else(|_| panic!("Should have been able to read the file: {}", cargo_file)); + let manifest = Manifest::from_str(&contents) + .unwrap_or_else(|_| panic!("Could not parse manifest file locate at {}", cargo_file)); + if let Some(package) = manifest.package { + let module_name = package.name.replace('-', "_"); + // skip package if it is unnamed or it was already visited + if !package.name.is_empty() && !module_names.contains(&module_name) { + module_names.push(module_name); + // go through all dependencies and visit the ones that has `path`, which means they are local + manifest.dependencies.values().for_each(|dep| { + if let Dependency::Detailed(details) = dep { + if let Some(path) = &details.path { + read_module_names(path, ¤t_path, module_names) + } + } + }); + } + } +} diff --git a/bitacross-worker/litentry/primitives/Cargo.toml b/bitacross-worker/litentry/primitives/Cargo.toml new file mode 100644 index 0000000000..2ad014ae67 --- /dev/null +++ b/bitacross-worker/litentry/primitives/Cargo.toml @@ -0,0 +1,64 @@ +[package] +authors = ["Trust Computing GmbH "] +edition = "2021" +name = "litentry-primitives" +version = "0.1.0" + +[dependencies] +bitcoin = { version = "0.31.0", default-features = false, features = ["secp-recovery", "no-std"] } +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +hex = { version = "0.4.3", default-features = false } +log = { version = "0.4", default-features = false } +pallet-evm = { default-features = false, git = "https://github.com/integritee-network/frontier.git", branch = "bar/polkadot-v0.9.42" } +rand = { version = "0.7", optional = true } +rand-sgx = { package = "rand", git = "https://github.com/mesalock-linux/rand-sgx", tag = "sgx_1.1.3", features = ["sgx_tstd"], optional = true } +ring = { version = "0.16.20", default-features = false } +scale-info = { version = "2.4.0", default-features = false, features = ["derive"] } +secp256k1 = { version = "0.28.0", default-features = false } +serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } +strum = { version = "0.25.0", default-features = false } +strum_macros = { version = "0.25.0", default-features = false } + +# sgx dependencies +base64_sgx = { package = "base64", rev = "sgx_1.1.3", git = "https://github.com/mesalock-linux/rust-base64-sgx", optional = true } +sgx_tstd = { git = "https://github.com/apache/teaclave-sgx-sdk.git", branch = "master", optional = true, features = ["net", "thread"] } + +# internal dependencies +itp-sgx-crypto = { path = "../../core-primitives/sgx/crypto", default-features = false } +itp-utils = { path = "../../core-primitives/utils", default-features = false } +parentchain-primitives = { package = "core-primitives", path = "../../../primitives/core", default-features = false } +teerex-primitives = { path = "../../../primitives/teerex", default-features = false } + +[dev-dependencies] +base64 = { version = "0.13", features = ["alloc"] } + +[features] +default = ["std"] +production = [] +sgx = [ + "sgx_tstd", + "rand-sgx", + "itp-sgx-crypto/sgx", +] +std = [ + "strum/std", + "hex/std", + "serde/std", + "itp-sgx-crypto/std", + "itp-utils/std", + "sp-core/std", + "sp-std/std", + "sp-io/std", + "sp-runtime/std", + "ring/std", + "parentchain-primitives/std", + "teerex-primitives/std", + "rand", + "log/std", + "bitcoin/std", + "secp256k1/std", +] diff --git a/bitacross-worker/litentry/primitives/src/aes.rs b/bitacross-worker/litentry/primitives/src/aes.rs new file mode 100644 index 0000000000..d63b02432a --- /dev/null +++ b/bitacross-worker/litentry/primitives/src/aes.rs @@ -0,0 +1,134 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate rand_sgx as rand; + +use crate::{Decode, Encode, Vec}; + +use rand::Rng; + +use ring::{ + aead::{Aad, BoundKey, LessSafeKey, Nonce, NonceSequence, SealingKey, UnboundKey, AES_256_GCM}, + error::Unspecified, +}; + +// we use 256-bit AES-GCM as request enc/dec key +pub const REQUEST_AES_KEY_LEN: usize = 32; +pub use ring::aead::{MAX_TAG_LEN, NONCE_LEN}; + +pub type RequestAesKey = [u8; REQUEST_AES_KEY_LEN]; +pub type RequestAesKeyNonce = [u8; NONCE_LEN]; + +// all-in-one struct containing the encrypted ciphertext with other +// metadata that is required for decryption +// +// by default a postfix tag is used => last 16 bytes of ciphertext is MAC tag +#[derive(Debug, Default, Clone, Eq, PartialEq, Encode, Decode)] +pub struct AesOutput { + pub ciphertext: Vec, + pub aad: Vec, + pub nonce: RequestAesKeyNonce, // IV +} + +// Returns the default if any error happens +// We don't propagate the error to upper level as this function is used in too many places, +// it's too verbose to handle them all and pass back to the parentchain as events. +// We rely on the parentchain event consumers to handle them correctly (and they kind of +// have to, because they'll find all fields are 0) +pub fn aes_encrypt_default(key: &RequestAesKey, data: &[u8]) -> AesOutput { + let mut in_out = data.to_vec(); + + let mut nonce = RingAeadNonceSequence::new(); + if nonce.advance().is_ok() { + let aad = b""; + if let Ok(unbound_key) = UnboundKey::new(&AES_256_GCM, key.as_slice()) { + let mut sealing_key = SealingKey::new(unbound_key, nonce.clone()); + if sealing_key.seal_in_place_append_tag(Aad::from(aad), &mut in_out).is_ok() { + return AesOutput { + ciphertext: in_out.to_vec(), + aad: aad.to_vec(), + nonce: nonce.nonce, + } + } + } + } + + AesOutput::default() +} + +// use LessSafeKey::seal_in_place_append_tag to encrypt the data using the given nonce +// don't be scared by the name, it's similar to `SealingKey::seal_in_place_append_tag`, +// except that it accepts an arbitrary nonce. +// It's only used by the one-off verification message calculation. +pub fn aes_encrypt_nonce(key: &RequestAesKey, data: &[u8], nonce: RequestAesKeyNonce) -> AesOutput { + let mut in_out = data.to_vec(); + let aad = b""; + if let Ok(unbound_key) = UnboundKey::new(&AES_256_GCM, key.as_slice()) { + let less_safe_key = LessSafeKey::new(unbound_key); + if less_safe_key + .seal_in_place_append_tag( + Nonce::assume_unique_for_key(nonce), + Aad::from(aad), + &mut in_out, + ) + .is_ok() + { + return AesOutput { ciphertext: in_out.to_vec(), aad: aad.to_vec(), nonce } + } + } + + AesOutput::default() +} + +pub fn aes_decrypt(key: &RequestAesKey, data: &mut AesOutput) -> Option> { + let in_out = data.ciphertext.as_mut(); + if let Ok(unbound_key) = UnboundKey::new(&AES_256_GCM, key.as_slice()) { + let less_safe_key = LessSafeKey::new(unbound_key); + return less_safe_key + .open_in_place( + Nonce::assume_unique_for_key(data.nonce), + Aad::from(data.aad.clone()), + in_out, + ) + .ok() + .map(|data| data.to_vec()) + } + None +} + +#[derive(Clone)] +pub struct RingAeadNonceSequence { + pub nonce: RequestAesKeyNonce, +} + +impl RingAeadNonceSequence { + fn new() -> RingAeadNonceSequence { + RingAeadNonceSequence { nonce: [0u8; NONCE_LEN] } + } +} + +impl NonceSequence for RingAeadNonceSequence { + fn advance(&mut self) -> Result { + let nonce = Nonce::assume_unique_for_key(self.nonce); + let nonce_vec = rand::thread_rng().gen::(); + self.nonce.copy_from_slice(&nonce_vec[0..NONCE_LEN]); + Ok(nonce) + } +} diff --git a/bitacross-worker/litentry/primitives/src/aes_request.rs b/bitacross-worker/litentry/primitives/src/aes_request.rs new file mode 100644 index 0000000000..7c133429e2 --- /dev/null +++ b/bitacross-worker/litentry/primitives/src/aes_request.rs @@ -0,0 +1,69 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +/// A morphling of itp_types::RsaRequest which stems from teerex_primitives::RsaRequest +/// +/// Instead of encrypting the TrustedCallSigned with the TEE's shielding key, we encrypt +/// it with a 32-byte ephemeral AES key which is generated from the client's side, and +/// send the encrypted payload together with the AES key encrypted using TEE's shielding key. +/// +/// After the enclave gets the request, it will decrypt to get the AES key and use that key +/// to decrypt the payload and decode it to get the real TrustedCall. +/// +/// The motivation of having such a struct is: +/// 1. RSA has a limitation of maximum allowed test to be encrypted. In our case, the encoded +/// `TrustedCallSigned` can exceed the limit, AES doesn't have such problem. +/// +/// 2. we want to efface the shielding key setup completely to achieve a better UE. +use crate::{ + aes_decrypt, AesOutput, Box, Debug, DecryptableRequest, RequestAesKey, ShardIdentifier, + ShieldingCryptoDecrypt, Vec, +}; +use codec::{Decode, Encode}; + +#[derive(Encode, Decode, Default, Clone, PartialEq, Eq, Debug)] +pub struct AesRequest { + pub shard: ShardIdentifier, + pub key: Vec, + pub payload: AesOutput, +} + +impl DecryptableRequest for AesRequest { + type Error = (); + + fn shard(&self) -> ShardIdentifier { + self.shard + } + + fn payload(&self) -> &[u8] { + self.payload.ciphertext.as_slice() + } + + fn decrypt( + &mut self, + enclave_shielding_key: Box>, + ) -> core::result::Result, ()> { + let aes_key: RequestAesKey = enclave_shielding_key + .decrypt(&self.key) + .map_err(|_| ())? + .try_into() + .map_err(|_| ())?; + aes_decrypt(&aes_key, &mut self.payload).ok_or(()) + } +} diff --git a/bitacross-worker/litentry/primitives/src/bitcoin_address.rs b/bitacross-worker/litentry/primitives/src/bitcoin_address.rs new file mode 100644 index 0000000000..32dcdcafcb --- /dev/null +++ b/bitacross-worker/litentry/primitives/src/bitcoin_address.rs @@ -0,0 +1,57 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use bitcoin::{ + address::Address, key::PublicKey, network::Network, secp256k1::Secp256k1, XOnlyPublicKey, +}; +use core::str::FromStr; +use std::string::{String, ToString}; + +// Some dependency conflict of bitcoin crate with enclave building +// when putting these functions into core-premitives/utils. +pub fn p2wpkh_address(pubkey_string: &str) -> String { + let pubkey = PublicKey::from_str(pubkey_string).expect("pubkey"); + let address = Address::p2wpkh(&pubkey, Network::Bitcoin); + if let Ok(address) = address { + return address.to_string() + } + "".to_string() +} + +pub fn p2sh_address(pubkey_string: &str) -> String { + let pubkey = PublicKey::from_str(pubkey_string).expect("pubkey"); + let address = Address::p2shwpkh(&pubkey, Network::Bitcoin); + if let Ok(address) = address { + return address.to_string() + } + "".to_string() +} + +pub fn p2tr_address(pubkey_string: &str) -> String { + let pubkey = PublicKey::from_str(pubkey_string).expect("pubkey"); + let xonly_pubkey = XOnlyPublicKey::from(pubkey.inner); + // unisat wallet uses is this way + let secp = Secp256k1::verification_only(); + let address = Address::p2tr(&secp, xonly_pubkey, None, Network::Bitcoin); + address.to_string() +} + +pub fn p2pkh_address(pubkey_string: &str) -> String { + let pubkey = PublicKey::from_str(pubkey_string).expect("pubkey"); + let address = Address::p2pkh(&pubkey, Network::Bitcoin); + address.to_string() +} diff --git a/bitacross-worker/litentry/primitives/src/bitcoin_signature.rs b/bitacross-worker/litentry/primitives/src/bitcoin_signature.rs new file mode 100644 index 0000000000..cb6db71a23 --- /dev/null +++ b/bitacross-worker/litentry/primitives/src/bitcoin_signature.rs @@ -0,0 +1,72 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . +#[cfg(feature = "std")] +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + +use codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; + +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Eq, Clone, Debug)] +pub struct BitcoinSignature(pub [u8; 65]); + +impl TryFrom<&[u8]> for BitcoinSignature { + type Error = (); + + fn try_from(data: &[u8]) -> Result { + if data.len() == 65 { + let mut inner = [0u8; 65]; + inner.copy_from_slice(data); + Ok(BitcoinSignature(inner)) + } else { + Err(()) + } + } +} + +#[cfg(feature = "std")] +impl Serialize for BitcoinSignature { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&hex::encode(self)) + } +} + +#[cfg(feature = "std")] +impl<'de> Deserialize<'de> for BitcoinSignature { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let signature_hex = hex::decode(&String::deserialize(deserializer)?) + .map_err(|e| de::Error::custom(format!("{:?}", e)))?; + BitcoinSignature::try_from(signature_hex.as_ref()) + .map_err(|e| de::Error::custom(format!("{:?}", e))) + } +} + +impl AsRef<[u8; 65]> for BitcoinSignature { + fn as_ref(&self) -> &[u8; 65] { + &self.0 + } +} + +impl AsRef<[u8]> for BitcoinSignature { + fn as_ref(&self) -> &[u8] { + &self.0[..] + } +} diff --git a/bitacross-worker/litentry/primitives/src/ethereum_signature.rs b/bitacross-worker/litentry/primitives/src/ethereum_signature.rs new file mode 100644 index 0000000000..75496fa61d --- /dev/null +++ b/bitacross-worker/litentry/primitives/src/ethereum_signature.rs @@ -0,0 +1,72 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . +#[cfg(feature = "std")] +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + +use codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; + +#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Eq, Clone, Debug)] +pub struct EthereumSignature(pub [u8; 65]); + +impl TryFrom<&[u8]> for EthereumSignature { + type Error = (); + + fn try_from(data: &[u8]) -> Result { + if data.len() == 65 { + let mut inner = [0u8; 65]; + inner.copy_from_slice(data); + Ok(EthereumSignature(inner)) + } else { + Err(()) + } + } +} + +#[cfg(feature = "std")] +impl Serialize for EthereumSignature { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&hex::encode(self)) + } +} + +#[cfg(feature = "std")] +impl<'de> Deserialize<'de> for EthereumSignature { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let signature_hex = hex::decode(&String::deserialize(deserializer)?) + .map_err(|e| de::Error::custom(format!("{:?}", e)))?; + EthereumSignature::try_from(signature_hex.as_ref()) + .map_err(|e| de::Error::custom(format!("{:?}", e))) + } +} + +impl AsRef<[u8; 65]> for EthereumSignature { + fn as_ref(&self) -> &[u8; 65] { + &self.0 + } +} + +impl AsRef<[u8]> for EthereumSignature { + fn as_ref(&self) -> &[u8] { + &self.0[..] + } +} diff --git a/bitacross-worker/litentry/primitives/src/lib.rs b/bitacross-worker/litentry/primitives/src/lib.rs new file mode 100644 index 0000000000..b439190a30 --- /dev/null +++ b/bitacross-worker/litentry/primitives/src/lib.rs @@ -0,0 +1,279 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate core; +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +mod aes; +mod aes_request; +mod bitcoin_address; +mod bitcoin_signature; +mod ethereum_signature; +mod validation_data; + +pub use aes::*; +pub use aes_request::*; +pub use bitcoin_address::*; +pub use bitcoin_signature::*; +pub use ethereum_signature::*; +use sp_std::{boxed::Box, fmt::Debug, vec::Vec}; +pub use validation_data::*; + +use bitcoin::sign_message::{signed_msg_hash, MessageSignature}; +use codec::{Decode, Encode, MaxEncodedLen}; +use itp_sgx_crypto::ShieldingCryptoDecrypt; +use itp_utils::hex::hex_encode; +use log::error; +pub use parentchain_primitives::{ + all_bitcoin_web3networks, all_evm_web3networks, all_substrate_web3networks, all_web3networks, + identity::*, AccountId as ParentchainAccountId, AchainableAmount, AchainableAmountHolding, + AchainableAmountToken, AchainableAmounts, AchainableBasic, AchainableBetweenPercents, + AchainableClassOfYear, AchainableDate, AchainableDateInterval, AchainableDatePercent, + AchainableMirror, AchainableParams, AchainableToken, AmountHoldingTimeType, Assertion, + Balance as ParentchainBalance, BlockNumber as ParentchainBlockNumber, BnbDigitDomainType, + BoundedWeb3Network, ContestType, EVMTokenType, ErrorDetail, ErrorString, + GenericDiscordRoleType, Hash as ParentchainHash, Header as ParentchainHeader, IMPError, + Index as ParentchainIndex, IntoErrorDetail, OneBlockCourseType, ParameterString, + SchemaContentString, SchemaIdString, Signature as ParentchainSignature, SoraQuizType, + VCMPError, VIP3MembershipCardLevel, Web3Network, MINUTES, +}; +use scale_info::TypeInfo; +use sp_core::{ecdsa, ed25519, sr25519, ByteArray}; +use sp_io::{ + crypto::secp256k1_ecdsa_recover, + hashing::{blake2_256, keccak_256}, +}; +use sp_runtime::traits::Verify; +use std::string::{String, ToString}; +pub use teerex_primitives::{decl_rsa_request, ShardIdentifier, SidechainBlockNumber}; + +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; + +pub const LITENTRY_PRETTIFIED_MESSAGE_PREFIX: &str = "Litentry authorization token: "; + +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub enum LitentryMultiSignature { + /// An Ed25519 signature. + #[codec(index = 0)] + Ed25519(ed25519::Signature), + /// An Sr25519 signature. + #[codec(index = 1)] + Sr25519(sr25519::Signature), + /// An ECDSA/SECP256k1 signature. + #[codec(index = 2)] + Ecdsa(ecdsa::Signature), + /// An ECDSA/keccak256 signature. An Ethereum signature. hash message with keccak256 + #[codec(index = 3)] + Ethereum(EthereumSignature), + /// Same as above, but the payload bytes are prepended with a readable prefix and `0x` + #[codec(index = 4)] + EthereumPrettified(EthereumSignature), + /// Bitcoin signed message, a hex-encoded string of original &[u8] message, without `0x` prefix + #[codec(index = 5)] + Bitcoin(BitcoinSignature), + /// Same as above, but the payload bytes are prepended with a readable prefix and `0x` + #[codec(index = 6)] + BitcoinPrettified(BitcoinSignature), +} + +impl LitentryMultiSignature { + pub fn verify(&self, msg: &[u8], signer: &Identity) -> bool { + match signer { + Identity::Substrate(address) => + self.verify_substrate(substrate_wrap(msg).as_slice(), address) + || self.verify_substrate(msg, address), + Identity::Evm(address) => self.verify_evm(msg, address), + Identity::Bitcoin(address) => self.verify_bitcoin(msg, address), + _ => false, + } + } + + fn verify_substrate(&self, msg: &[u8], signer: &Address32) -> bool { + match (self, signer) { + (Self::Ed25519(ref sig), who) => match ed25519::Public::from_slice(who.as_ref()) { + Ok(signer) => sig.verify(msg, &signer), + Err(()) => false, + }, + (Self::Sr25519(ref sig), who) => match sr25519::Public::from_slice(who.as_ref()) { + Ok(signer) => sig.verify(msg, &signer), + Err(()) => false, + }, + (Self::Ecdsa(ref sig), who) => { + let m = blake2_256(msg); + match sp_io::crypto::secp256k1_ecdsa_recover_compressed(sig.as_ref(), &m) { + Ok(pubkey) => + &blake2_256(pubkey.as_ref()) == >::as_ref(who), + _ => false, + } + }, + _ => false, + } + } + + fn verify_evm(&self, msg: &[u8], signer: &Address20) -> bool { + match self { + Self::Ethereum(ref sig) => + return verify_evm_signature(evm_eip191_wrap(msg).as_slice(), sig, signer) + || verify_evm_signature(msg, sig, signer), + Self::EthereumPrettified(ref sig) => { + let prettified_msg = + LITENTRY_PRETTIFIED_MESSAGE_PREFIX.to_string() + &hex_encode(msg); + let msg = prettified_msg.as_bytes(); + return verify_evm_signature(evm_eip191_wrap(msg).as_slice(), sig, signer) + || verify_evm_signature(msg, sig, signer) + }, + _ => false, + } + } + + fn verify_bitcoin(&self, msg: &[u8], signer: &Address33) -> bool { + match self { + Self::Bitcoin(ref sig) => + verify_bitcoin_signature(hex::encode(msg).as_str(), sig, signer), + Self::BitcoinPrettified(ref sig) => { + let prettified_msg = + LITENTRY_PRETTIFIED_MESSAGE_PREFIX.to_string() + &hex_encode(msg); + verify_bitcoin_signature(prettified_msg.as_str(), sig, signer) + }, + _ => false, + } + } +} + +pub fn verify_evm_signature(msg: &[u8], sig: &EthereumSignature, who: &Address20) -> bool { + let digest = keccak_256(msg); + return match recover_evm_address(&digest, sig.as_ref()) { + Ok(recovered_evm_address) => recovered_evm_address == who.as_ref().as_slice(), + Err(_e) => { + error!("Could not verify evm signature msg: {:?}, signer {:?}", msg, who); + false + }, + } +} + +pub fn verify_bitcoin_signature(msg: &str, sig: &BitcoinSignature, who: &Address33) -> bool { + if let Ok(msg_sig) = MessageSignature::from_slice(sig.as_ref()) { + let msg_hash = signed_msg_hash(msg); + let secp = secp256k1::Secp256k1::new(); + return match msg_sig.recover_pubkey(&secp, msg_hash) { + Ok(recovered_pub_key) => &recovered_pub_key.inner.serialize() == who.as_ref(), + Err(_) => { + error!("Could not recover pubkey from bitcoin msg: {:?}, signer {:?}", msg, who); + false + }, + } + } + + false +} + +impl From for LitentryMultiSignature { + fn from(x: ed25519::Signature) -> Self { + Self::Ed25519(x) + } +} + +impl From for LitentryMultiSignature { + fn from(x: sr25519::Signature) -> Self { + Self::Sr25519(x) + } +} + +impl From for LitentryMultiSignature { + fn from(x: ecdsa::Signature) -> Self { + Self::Ecdsa(x) + } +} + +pub fn recover_evm_address( + msg: &[u8; 32], + sig: &[u8; 65], +) -> Result<[u8; 20], sp_io::EcdsaVerifyError> { + let pubkey = secp256k1_ecdsa_recover(sig, msg)?; + let hashed_pk = keccak_256(&pubkey); + + let mut addr = [0u8; 20]; + addr[..20].copy_from_slice(&hashed_pk[12..32]); + Ok(addr) +} + +// see https://github.com/litentry/litentry-parachain/issues/1137 +fn substrate_wrap(msg: &[u8]) -> Vec { + ["".as_bytes(), msg, "".as_bytes()].concat() +} + +// see https://github.com/litentry/litentry-parachain/issues/1970 +fn evm_eip191_wrap(msg: &[u8]) -> Vec { + ["\x19Ethereum Signed Message:\n".as_bytes(), msg.len().to_string().as_bytes(), msg].concat() +} + +pub type IdentityNetworkTuple = (Identity, Vec); + +// Represent a request that can be decrypted by the enclave +// Both itp_types::RsaRequest and AesRequest should impelement this +pub trait DecryptableRequest { + type Error; + // the shard getter + fn shard(&self) -> ShardIdentifier; + // the raw payload - AFAICT only used in mock + fn payload(&self) -> &[u8]; + // how to decrypt the payload + fn decrypt( + &mut self, + enclave_shielding_key: Box>, + ) -> Result, Self::Error>; +} + +pub struct BroadcastedRequest { + pub id: String, + pub payload: String, + pub rpc_method: String, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn verify_bitcoin_signature_works() { + // generated by unisat-wallet API: https://docs.unisat.io/dev/unisat-developer-service/unisat-wallet + let msg: Vec = vec![ + 3, 93, 250, 112, 216, 101, 89, 57, 83, 88, 100, 252, 203, 15, 64, 127, 138, 37, 2, 40, + 147, 95, 245, 27, 97, 202, 62, 205, 151, 0, 175, 177, + ]; + let pubkey: Vec = vec![ + 3, 93, 250, 112, 216, 101, 89, 57, 83, 88, 100, 252, 203, 15, 64, 127, 138, 37, 2, 40, + 147, 95, 245, 27, 97, 202, 62, 205, 151, 0, 175, 177, 216, + ]; + let sig: Vec = base64::decode("G2LhyYzWT2o8UoBsuhJsqFgwm3tlE0cW4aseCXKqVuNATk6K/uEHlPzDFmtlMADywDHl5vLCWcNpwmQLD7n/yvc=").unwrap(); + + let pubkey_ref: &[u8] = pubkey.as_ref(); + let sig_ref: &[u8] = sig.as_ref(); + assert!(verify_bitcoin_signature( + hex::encode(msg).as_str(), + &sig_ref.try_into().unwrap(), + &pubkey_ref.try_into().unwrap() + )); + } +} diff --git a/bitacross-worker/litentry/primitives/src/validation_data.rs b/bitacross-worker/litentry/primitives/src/validation_data.rs new file mode 100644 index 0000000000..aac3427799 --- /dev/null +++ b/bitacross-worker/litentry/primitives/src/validation_data.rs @@ -0,0 +1,96 @@ +// Copyright 2020-2023 Trust Computing GmbH. +// This file is part of Litentry. +// +// Litentry is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Litentry is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Litentry. If not, see . + +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; + +use crate::LitentryMultiSignature; +use codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_runtime::{traits::ConstU32, BoundedVec}; + +pub type MaxStringLength = ConstU32<64>; +pub type ValidationString = BoundedVec; + +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub struct TwitterValidationData { + pub tweet_id: ValidationString, +} + +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub struct DiscordValidationData { + pub channel_id: ValidationString, + pub message_id: ValidationString, + pub guild_id: ValidationString, +} + +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub struct Web3CommonValidationData { + pub message: ValidationString, // or String if under std + pub signature: LitentryMultiSignature, +} + +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[allow(non_camel_case_types)] +pub enum Web2ValidationData { + #[codec(index = 0)] + Twitter(TwitterValidationData), + #[codec(index = 1)] + Discord(DiscordValidationData), +} + +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +#[allow(non_camel_case_types)] +pub enum Web3ValidationData { + #[codec(index = 0)] + Substrate(Web3CommonValidationData), + #[codec(index = 1)] + Evm(Web3CommonValidationData), + #[codec(index = 2)] + Bitcoin(Web3CommonValidationData), +} + +impl Web3ValidationData { + pub fn message(&self) -> &ValidationString { + match self { + Self::Substrate(data) => &data.message, + Self::Evm(data) => &data.message, + Self::Bitcoin(data) => &data.message, + } + } + + pub fn signature(&self) -> &LitentryMultiSignature { + match self { + Self::Substrate(data) => &data.signature, + Self::Evm(data) => &data.signature, + Self::Bitcoin(data) => &data.signature, + } + } +} + +#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo, MaxEncodedLen)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub enum ValidationData { + #[codec(index = 0)] + Web2(Web2ValidationData), + #[codec(index = 1)] + Web3(Web3ValidationData), +} diff --git a/bitacross-worker/local-setup/.env.example b/bitacross-worker/local-setup/.env.example new file mode 100644 index 0000000000..b8fab39de9 --- /dev/null +++ b/bitacross-worker/local-setup/.env.example @@ -0,0 +1,13 @@ +AliceWSPort=9946 +AliceRPCPort=9936 +AlicePort=30336 +BobWSPort=9947 +BobRPCPort=9937 +BobPort=30337 +CollatorWSPort=9944 +CollatorRPCPort=9933 +CollatorPort=30333 +TrustedWorkerPort=2000 +UntrustedWorkerPort=2001 +MuRaPort=3443 +UntrustedHttpPort=4545 \ No newline at end of file diff --git a/bitacross-worker/local-setup/README.md b/bitacross-worker/local-setup/README.md new file mode 100644 index 0000000000..25342ca038 --- /dev/null +++ b/bitacross-worker/local-setup/README.md @@ -0,0 +1,37 @@ +# How to use the local-setup + +## Prerequisite +- worker built with ` SGX_MODE=SW make` +- integritee-node built with `cargo build --release --features skip-ias-check` + +In case you have +- a sgx hardware and compile the worker with `SGX_MODE=HW` (default mode) +- a valid intel IAS key (development key is fine) + +you can omit the `--features skip-ias-check` when building the node, but you must not use the subcommand flag `--skip-ra` in the json file (see [`two-workers.json`](./config/two-workers.json)) you're using to start the worker. + +## Steps +Adapt or create your own config file, as in the example of [`two-workers.json`](./config/two-workers.json). Be mindful of the ports in case you're running the script on a server multiple people are working on. + +### Launch worker and node in terminal one +You can launch the workers and the node with: +```bash +./local-setup/launch.py --config ./local-setup/config/two-workers.json +``` +wait a little until all workers have been launched. You can stop the worker and node simply by pressing `Ctrl + c`. + +### Open a second terminal to show logs +```bash +cd local-setup +./tmux_logger.sh +``` + +You can remove the tmux session of the script by running +```bash +tmux kill-session -t integritee_logger +``` +### Open a third terminal to run a demo +```bash +cd /cli +./demo_shielding_unshielding.sh -p 99xx -P 20xx +``` diff --git a/bitacross-worker/local-setup/__init__.py b/bitacross-worker/local-setup/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bitacross-worker/local-setup/config/benchmark.json b/bitacross-worker/local-setup/config/benchmark.json new file mode 100644 index 0000000000..f997396e3a --- /dev/null +++ b/bitacross-worker/local-setup/config/benchmark.json @@ -0,0 +1,44 @@ +{ + "nodes": [ + { + "bin": "../integritee-node/target/release/integritee-node", + "flags": [ + "--tmp", + "--dev", + "-lruntime=info", + "-lteerex=debug", + "--ws-port", + "9944", + "--port", + "30390", + "--rpc-port", + "9933", + "--ws-external", + "--rpc-external" + ] + } + ], + "workers": [ + { + "source": "bin", + "flags": [ + "--clean-reset", + "-P", + "2030", + "-p", + "9930", + "-r", + "3430", + "-w", + "2031", + "-h", + "4530", + "--ws-external" + ], + "subcommand_flags": [ + "--skip-ra", + "--dev" + ] + } + ] +} diff --git a/bitacross-worker/local-setup/config/one-worker.json b/bitacross-worker/local-setup/config/one-worker.json new file mode 100644 index 0000000000..ff80710677 --- /dev/null +++ b/bitacross-worker/local-setup/config/one-worker.json @@ -0,0 +1,49 @@ +{ + "nodes": [ + { + "bin": "../integritee-node/target/release/integritee-node", + "flags": [ + "--tmp", + "--dev", + "-lruntime=info", + "-lteerex=debug", + "--ws-port", + "9944", + "--port", + "30390", + "--rpc-port", + "9933", + "--ws-external", + "--rpc-external" + ] + } + ], + "workers": [ + { + "source": "bin", + "flags": [ + "--clean-reset", + "-P", + "2000", + "-p", + "9944", + "-r", + "3443", + "-w", + "2001", + "-h", + "4545", + "--ws-external", + "--parentchain-start-block", + "0", + "--data-dir", + "/tmp/data-dir", + "--enable-metrics" + ], + "subcommand_flags": [ + "--skip-ra", + "--dev" + ] + } + ] +} diff --git a/bitacross-worker/local-setup/config/three-nodes-one-worker.json b/bitacross-worker/local-setup/config/three-nodes-one-worker.json new file mode 100644 index 0000000000..f01b325153 --- /dev/null +++ b/bitacross-worker/local-setup/config/three-nodes-one-worker.json @@ -0,0 +1,94 @@ +{ + "nodes": [ + { + "bin": "../integritee-node/target/release/integritee-node", + "flags": [ + "--tmp", + "--dev", + "-lruntime=info", + "-lteerex=debug", + "--ws-port", + "9944", + "--port", + "30390", + "--rpc-port", + "9933", + "--ws-external", + "--rpc-external" + ] + }, + { + "bin": "../integritee-node/target/release/integritee-node", + "flags": [ + "--tmp", + "--chain", + "dev2", + "--force-authoring", + "--alice", + "-lruntime=info", + "-lteerex=debug", + "--ws-port", + "9966", + "--port", + "30395", + "--rpc-port", + "9955", + "--ws-external", + "--rpc-external" + ] + }, + { + "bin": "../integritee-node/target/release/integritee-node", + "flags": [ + "--tmp", + "--chain", + "dev3", + "--force-authoring", + "--alice", + "-lruntime=info", + "-lteerex=debug", + "--ws-port", + "9988", + "--port", + "30395", + "--rpc-port", + "9977", + "--ws-external", + "--rpc-external" + ] + } + ], + "workers": [ + { + "source": "bin", + "flags": [ + "--clean-reset", + "-P", + "2000", + "-p", + "9944", + "--target-a-parentchain-rpc-url", + "ws://127.0.0.1", + "--target-a-parentchain-rpc-port", + "9966", + "--target-b-parentchain-rpc-url", + "ws://127.0.0.1", + "--target-b-parentchain-rpc-port", + "9988", + "-r", + "3490", + "-w", + "2001", + "-h", + "4545", + "--ws-external", + "--data-dir", + "/tmp/data-dir" + ], + "subcommand_flags": [ + "--skip-ra", + "--dev" + ] + } + ] +} diff --git a/bitacross-worker/local-setup/config/three-workers.json b/bitacross-worker/local-setup/config/three-workers.json new file mode 100644 index 0000000000..49e66c49a5 --- /dev/null +++ b/bitacross-worker/local-setup/config/three-workers.json @@ -0,0 +1,76 @@ +{ + "workers": [ + { + "id": 1, + "source": "bin", + "flags": [ + "--clean-reset", + "-T", + "wss://localhost", + "-P", + "2000", + "-w", + "2001", + "-r", + "3443", + "-h", + "4545", + "--parentchain-start-block", + "0", + "--enable-metrics" + ], + "subcommand_flags": [ + "--skip-ra", + "--dev" + ] + }, + { + "id": "2", + "source": "bin", + "flags": [ + "--clean-reset", + "-T", + "wss://localhost", + "-P", + "2010", + "-w", + "2011", + "-r", + "3453", + "-h", + "4555", + "--parentchain-start-block", + "0" + ], + "subcommand_flags": [ + "--skip-ra", + "--dev", + "--request-state" + ] + }, + { + "id": "3", + "source": "bin", + "flags": [ + "--clean-reset", + "-T", + "wss://localhost", + "-P", + "2020", + "-w", + "2021", + "-r", + "3463", + "-h", + "4565", + "--parentchain-start-block", + "0" + ], + "subcommand_flags": [ + "--skip-ra", + "--dev", + "--request-state" + ] + } + ] +} diff --git a/bitacross-worker/local-setup/config/two-workers.json b/bitacross-worker/local-setup/config/two-workers.json new file mode 100644 index 0000000000..9e0194f282 --- /dev/null +++ b/bitacross-worker/local-setup/config/two-workers.json @@ -0,0 +1,76 @@ +{ + "nodes": [ + { + "bin": "../integritee-node/target/release/integritee-node", + "flags": [ + "--tmp", + "--dev", + "-lruntime=info", + "-lteerex=debug", + "--ws-port", + "9944", + "--port", + "30390", + "--rpc-port", + "9933", + "--ws-external", + "--rpc-external" + ] + } + ], + "workers": [ + { + "source": "bin", + "flags": [ + "--clean-reset", + "-P", + "2000", + "-p", + "9944", + "-r", + "3490", + "-w", + "2001", + "-h", + "4545", + "--ws-external", + "--parentchain-start-block", + "0", + "--data-dir", + "/tmp/data-dir", + "--enable-metrics" + ], + "subcommand_flags": [ + "--skip-ra", + "--dev" + ] + }, + { + "source": "bin", + "flags": [ + "--clean-reset", + "-P", + "3000", + "-p", + "9944", + "-r", + "4490", + "-w", + "3001", + "-h", + "4546", + "--ws-external", + "--parentchain-start-block", + "0", + "--data-dir", + "/tmp/data-dir2", + "--enable-metrics" + ], + "subcommand_flags": [ + "--skip-ra", + "--dev", + "--request-state" + ] + } + ] +} diff --git a/bitacross-worker/local-setup/development-worker.json b/bitacross-worker/local-setup/development-worker.json new file mode 100644 index 0000000000..a85b5fbeb3 --- /dev/null +++ b/bitacross-worker/local-setup/development-worker.json @@ -0,0 +1,28 @@ +{ + "workers": [ + { + "id": "dev", + "source": "bin", + "flags": [ + "--clean-reset", + "-P", + "$TrustedWorkerPort", + "-w", + "$UntrustedWorkerPort", + "-r", + "$MuRaPort", + "-h", + "$UntrustedHttpPort", + "-p", + "$CollatorWSPort", + "--parentchain-start-block", + "0", + "--enable-metrics" + ], + "subcommand_flags": [ + "--skip-ra", + "--dev" + ] + } + ] +} diff --git a/bitacross-worker/local-setup/launch.py b/bitacross-worker/local-setup/launch.py new file mode 100755 index 0000000000..ae4a6e5b24 --- /dev/null +++ b/bitacross-worker/local-setup/launch.py @@ -0,0 +1,275 @@ +#!/usr/bin/env python3 +""" +Launch handily a local dev setup consisting of the parachain network and some workers. + +Example usage: `./local-setup/launch.py --config local-setup/development-worker.json --parachain local-binary` + +The worker log is piped to `./log/worker0.log` etc. folder in the current-working dir. + +""" +import argparse +import json +import signal +from subprocess import Popen, PIPE, STDOUT, run +import os +import sys +from time import sleep +from typing import Union, IO +from dotenv import load_dotenv + +import pycurl +from io import BytesIO + +from py.worker import Worker +from py.helpers import GracefulKiller, mkdir_p + +import socket +import toml +import datetime + +log_dir = "log" +mkdir_p(log_dir) + +OFFSET = 100 +PORTS = [ + "AliceWSPort", + "AliceRPCPort", + "AlicePort", + "BobWSPort", + "BobRPCPort", + "BobPort", + "CollatorWSPort", + "CollatorRPCPort", + "CollatorPort", + "TrustedWorkerPort", + "UntrustedWorkerPort", + "MuRaPort", + "UntrustedHttpPort", +] + + +def setup_worker(work_dir: str, source_dir: str, std_err: Union[None, int, IO], log_config_path): + print(f"Setting up worker in {work_dir}") + print(f"Copying files from {source_dir}") + + log_level_dic = setup_worker_log_level(log_config_path) + worker = Worker(cwd=work_dir, source_dir=source_dir, std_err=std_err, log_level_dic=log_level_dic) + worker.init_clean() + print("Initialized worker.") + return worker + + +def run_worker(config, i: int, log_config_path): + id = config.get('id', i) + log = open(f"{log_dir}/worker-{id}.log", "w+") + # TODO: either hard-code 'local-setup' directory, or take from input config.json + w = setup_worker(f"tmp/w-{id}", config["source"], log, log_config_path) + + print(f"Starting worker {id} in background") + return w.run_in_background( + log_file=log, flags=config["flags"], subcommand_flags=config["subcommand_flags"] + ) + + +# Function to check if a port is open +def is_port_open(port): + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.bind(("127.0.0.1", int(port))) + sock.close() + return True + except OSError: + return False + +# Function to reallocate port if it is not available +def reallocate_ports(env_name, port): + # Offset the original port by 100 + new_port = int(port) + int(OFFSET) + while not is_port_open(str(new_port)): + new_port = int(port) + int(OFFSET) + + # Set the new port value in the environment variable + os.environ[env_name] = str(new_port) + print("Port for {} changed to: {}".format(env_name, os.environ.get(env_name))) + + +# Function to iterate over all ports and automatically reallocate +def check_all_ports_and_reallocate(): + for x in PORTS: + if is_port_open(os.environ.get(x)): + continue + else: + reallocate_ports(x, os.environ.get(x)) + + print("All preliminary port checks completed") + + +def run_node(config, i: int): + node_log = open(f'{log_dir}/node{i}.log', 'w+') + node_cmd = [config["bin"]] + config["flags"] + print(f'Run node {i} with command: {node_cmd}') + return Popen(node_cmd, stdout=node_log, stderr=STDOUT, bufsize=1) + + +def offset_port(offset): + for x in PORTS: + port = os.environ.get(x) + new_port = int(port) + int(offset) + os.environ[x] = str(new_port) + + +def setup_environment(offset, config, parachain_dir): + load_dotenv(".env.dev") + offset_port(offset) + check_all_ports_and_reallocate() + + # TODO: only works for single worker for now + for p in [ + "CollatorWSPort", + "TrustedWorkerPort", + "UntrustedWorkerPort", + "MuRaPort", + "UntrustedHttpPort", + ]: + if len(config["workers"]) > 0: + config["workers"][0]["flags"] = [ + flag.replace("$" + p, os.environ.get(p, "")) + for flag in config["workers"][0]["flags"] + ] + +def setup_worker_log_level(log_config_path): + log_level_dic = {} + with open(log_config_path) as f: + log_data = toml.load(f) + + # Section + for (section, item) in log_data.items(): + log_level_string = ""; + indx = 0 + + for (k, v) in item.items(): + if indx == 0: + log_level_string += v+"," + else: + log_level_string += k+"="+v+"," + + indx += 1 + + log_level_dic[section] = log_level_string + + return log_level_dic + + +def main(processes, config_path, parachain_type, log_config_path, offset, parachain_dir): + with open(config_path) as config_file: + config = json.load(config_file) + + # Litentry + print("Starting litentry parachain in background ...") + if parachain_type == "local-docker": + os.environ['LITENTRY_PARACHAIN_DIR'] = parachain_dir + setup_environment(offset, config, parachain_dir) + # TODO: use Popen and copy the stdout also to node.log + run(["./scripts/litentry/start_parachain.sh"], check=True) + elif parachain_type == "local-binary-standalone": + os.environ['LITENTRY_PARACHAIN_DIR'] = parachain_dir + setup_environment(offset, config, parachain_dir) + run(["../scripts/launch-standalone.sh"], check=True) + elif parachain_type == "local-binary": + os.environ['LITENTRY_PARACHAIN_DIR'] = parachain_dir + setup_environment(offset, config, parachain_dir) + run(["../scripts/launch-local-binary.sh", "rococo"], check=True) + elif parachain_type == "remote": + print("Litentry parachain should be started remotely") + else: + sys.exit("Unsupported parachain_type") + + print("Litentry parachain is running") + print("------------------------------------------------------------") + + c = pycurl.Curl() + worker_i = 0 + worker_num = len(config["workers"]) + for w_conf in config["workers"]: + processes.append(run_worker(w_conf, worker_i, log_config_path)) + print() + # Wait a bit for worker to start up. + sleep(5) + + idx = 0 + if "-h" in w_conf["flags"]: + idx = w_conf["flags"].index("-h") + 1 + elif "--untrusted-http-port" in w_conf["flags"]: + idx = w_conf["flags"].index("--untrusted-http-port") + 1 + else: + print('No "--untrusted-http-port" provided in config file') + return 0 + untrusted_http_port = w_conf["flags"][idx] + url = "http://localhost:" + str(untrusted_http_port) + "/is_initialized" + c.setopt(pycurl.URL, url) + + if worker_i < worker_num: + counter = 0 + while True: + sleep(5) + buffer = BytesIO() + c.setopt(c.WRITEDATA, buffer) + try: + c.perform() + except Exception as e: + print("Try to connect to worker error: " + str(e)) + return 0 + + if "I am initialized." == buffer.getvalue().decode("iso-8859-1"): + break + if counter >= 600: + print("Worker initialization timeout (3000s). Exit") + return 0 + counter += 1 + + worker_i += 1 + + c.close() + print("Worker(s) started!") + + # keep script alive until terminated + signal.pause() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="Run a setup consisting of a node and some workers" + ) + parser.add_argument("-c", "--config", type=str, help="Config for the node and workers") + parser.add_argument( + "-p", + "--parachain", + nargs="?", + default="local-docker", + type=str, + help="Config for parachain selection: local-docker / local-binary / remote", + ) + parser.add_argument( + "-l", + "--log-config-path", + nargs="?", + default="./local-setup/worker-log-level-config.toml", + type=str, + help="log level config file path" + ) + parser.add_argument( + "-o", "--offset", nargs="?", default="0", type=int, help="offset for port" + ) + args = parser.parse_args() + + today = datetime.datetime.now() + formatted_date = today.strftime('%d_%m_%Y_%H%M') + directory_name = f"parachain_dev_{formatted_date}" + temp_directory_path = os.path.join('/tmp', directory_name) + parachain_dir = temp_directory_path + print("Directory has been assigned to:", temp_directory_path) + + process_list = [] + killer = GracefulKiller(process_list, args.parachain) + if main(process_list, args.config, args.parachain, args.log_config_path, args.offset, parachain_dir) == 0: + killer.exit_gracefully() diff --git a/bitacross-worker/local-setup/py/__init__.py b/bitacross-worker/local-setup/py/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bitacross-worker/local-setup/py/helpers.py b/bitacross-worker/local-setup/py/helpers.py new file mode 100644 index 0000000000..957e51f162 --- /dev/null +++ b/bitacross-worker/local-setup/py/helpers.py @@ -0,0 +1,104 @@ +import os +import signal +import subprocess +import shutil +import sys +import docker +from typing import Union, IO +from datetime import datetime + + +def run_subprocess( + log_level, args, stdout: Union[None, int, IO], stderr: Union[None, int, IO], cwd: str = "./" +): + """Wrapper around subprocess that allows a less verbose call""" + + # todo: make configurable + env = dict( + os.environ, + RUST_LOG=log_level, + ) + + return ( + subprocess.run(args, stdout=stdout, env=env, cwd=cwd, stderr=stderr) + .stdout.decode("utf-8") + .strip() + ) + + +def setup_working_dir(source_dir: str, target_dir: str): + """Setup the working dir such that the necessary files to run a worker are contained. + + Args: + source_dir: the directory containing the files the be copied. Usually this is the bitacross-worker/bin dir. + target_dir: the working directory of the worker to be run. + """ + + optional = ["key.txt", "spid.txt"] + + for file in optional: + source = f"{source_dir}/{file}" + target = f"{target_dir}/{file}" + + if os.path.exists(source): + shutil.copy(source, target) + else: + print(f'{source} does not exist, this may be fine for DCAP or skip-ra, but you can\'t perform IAS remote attestation without this file.') + + mandatory = ["enclave.signed.so", "bitacross-worker"] + + for file in mandatory: + source = f"{source_dir}/{file}" + target = f"{target_dir}/{file}" + + if os.path.exists(source): + shutil.copy(source, target) + else: + print(f"{source} does not exist. Did you run make?") + + +def mkdir_p(path): + """Surprisingly, there is no simple function in python to create a dir if it does not exist.""" + return subprocess.run(["mkdir", "-p", path]) + + +class GracefulKiller: + signals = {signal.SIGINT: "SIGINT", signal.SIGTERM: "SIGTERM"} + + def __init__(self, processes, parachain_type): + signal.signal(signal.SIGINT, self.exit_gracefully) + signal.signal(signal.SIGTERM, self.exit_gracefully) + self.processes = processes + self.parachain_type = parachain_type + + def exit_gracefully(self, signum=signal.SIGTERM, frame=None): + print("\nReceived {} signal".format(self.signals[signum])) + + print("Save Parachain/Relaychain logs") + client = docker.from_env() + container_list = client.containers.list() + for container in container_list: + if "generated-rococo-" in container.name: + logs = container.logs() + with open(f"log/{container.name}.log", "w") as f: + f.write(logs.decode("utf-8")) + + print("Cleaning up processes.") + for p in self.processes: + try: + p.kill() + except: + pass + + if os.path.isdir(f"log"): + new_folder_name = datetime.now().strftime("log-backup/log-%Y%m%d-%H%M%S") + shutil.copytree(f"log", new_folder_name) + print(f"Backup log into " + new_folder_name) + if self.parachain_type == "local-docker": + print("Cleaning up litentry-parachain...") + subprocess.run(["./scripts/litentry/stop_parachain.sh", "||", "true"]) + if self.parachain_type == "local-binary": + print("Cleaning up litentry-parachain...") + subprocess.run(["../scripts/clean-local-binary.sh", "||", "true"]) + + sys.exit(0) diff --git a/bitacross-worker/local-setup/py/worker.py b/bitacross-worker/local-setup/py/worker.py new file mode 100644 index 0000000000..9492088b6a --- /dev/null +++ b/bitacross-worker/local-setup/py/worker.py @@ -0,0 +1,220 @@ +import os +import pathlib +import shutil +import subprocess +from subprocess import Popen, STDOUT +from typing import Union, TextIO, IO + +from .helpers import run_subprocess, setup_working_dir, mkdir_p + + +class Worker: + def __init__( + self, + worker_bin: str = "./bitacross-worker", + cwd: str = "./", + source_dir: str = "./", + std_err: Union[None, int, IO] = STDOUT, + log_level_dic: {} = {}, + ): + """ + bitacross-worker wrapper. + + Args: + worker_bin: Path to the worker bin relative to `cwd` or as absolute path. + + cwd: working directory of the worker. + + source_dir: directory of the source binaries, which will be copied to cwd because + the rust worker looks for files relative to cwd. + + std_err: Were the workers error output will be logged. Note: `std_out` is intended to be unconfigurable + because the prints from the rust worker are often intended to be used in scripts. Making this + configurable, could cause some weird errors. + + + """ + self.cwd = cwd + self.cli = [worker_bin] + self.source_dir = source_dir + self.std_err = std_err + # cache fields + self._mrenclave = None + self.log_level_dic = log_level_dic + + def setup_cwd(self): + mkdir_p(self.cwd) + setup_working_dir(self.source_dir, self.cwd) + + def init_clean(self): + """Purges all db files first and initializes the environment afterwards.""" + mkdir_p(self.cwd) + print("Copying source files to working directory") + self.setup_cwd() + + def init(self): + """Initializes the environment such that the worker can be run.""" + print("Initializing worker") + print(self.init_shard()) + print(self.write_signer_pub()) + print(self.write_shielding_pub()) + + def init_shard(self, shard=None): + """ + :param shard: Shard to be initialized. Use mrenclave if `None`. + :return msg: `println!`'s generated by the rust worker. + """ + if not shard: + shard = self.mrenclave() + if self.check_shard_and_prompt_delete(shard): + return "Shard exists already, will not initialize." + + return run_subprocess( + self.log_level_dic['bitacross-cli'], + self.cli + ["init-shard", shard], + stdout=subprocess.PIPE, + stderr=self.std_err, + cwd=self.cwd, + ) + + def shard_exists(self, shard): + """Checks if the shard in './shards/[shard]' exists + + :return: exists: True if exists, false otherwise. + """ + return self._shard_path(shard).exists() + + def check_shard_and_prompt_delete(self, shard=None): + """ + Checks if the shard exists and will prompt to delete it. + If shard is none, this will just return. + + :return: + exists: True if file exists at the end of this call. False otherwise. + + """ + if self.shard_exists(shard): + should_purge = input( + "Do you want to purge existing the shards and sidechain db? [y, n]" + ) + if should_purge == "y": + self.purge_shards_and_sidechain_db() + print(f"Deleted shard {shard}.") + return False + else: + print("Leaving shard as is.") + return True + else: + return False + + def purge(self): + """Deletes the light_client_db.bin, the shards and the sidechain_db""" + self.purge_last_slot_seal() + self.purge_light_client_db() + self.purge_shards_and_sidechain_db() + return self + + def purge_shards_and_sidechain_db(self): + if pathlib.Path(f"{self.cwd}/shards").exists(): + print(f"Purging shards") + shutil.rmtree(pathlib.Path(f"{self.cwd}/shards")) + + if pathlib.Path(f"{self.cwd}/sidechain_db").exists(): + print(f"purging sidechain_db") + shutil.rmtree(pathlib.Path(f"{self.cwd}/sidechain_db")) + + def purge_light_client_db(self): + print(f"purging light_client_db") + for db in pathlib.Path(self.cwd).glob("light_client_db.bin*"): + print(f"remove: {db}") + db.unlink() + + def purge_last_slot_seal(self): + print(f"purging last_slot_seal") + for db in pathlib.Path(self.cwd).glob("last_slot.bin"): + print(f"remove: {db}") + db.unlink() + + def mrenclave(self): + """Returns the mrenclave and caches it.""" + if not self._mrenclave: + # `std_out` needs to be subProcess.PIPE here! + self._mrenclave = run_subprocess( + self.log_level_dic['bitacross-cli'], + self.cli + ["mrenclave"], + stdout=subprocess.PIPE, + stderr=self.std_err, + cwd=self.cwd, + ) + return self._mrenclave + + def write_shielding_pub(self): + return run_subprocess( + self.log_level_dic['bitacross-cli'], + self.cli + ["shielding-key"], + stdout=subprocess.PIPE, + stderr=self.std_err, + cwd=self.cwd, + ) + + def write_signer_pub(self): + return run_subprocess( + self.log_level_dic['bitacross-cli'], + self.cli + ["signing-key"], + stdout=subprocess.PIPE, + stderr=self.std_err, + cwd=self.cwd, + ) + + def sync_state(self, flags: [str] = None, skip_ra: bool = False): + """Returns the keys from another worker.""" + + if skip_ra: + subcommand_flags = ["request-state", "--skip-ra"] + else: + subcommand_flags = ["request-state"] + + return run_subprocess( + self.log_level_dic['bitacross-cli'], + self.cli + flags + subcommand_flags, + stdout=subprocess.PIPE, + stderr=self.std_err, + cwd=self.cwd, + ) + + def _shard_path(self, shard): + return pathlib.Path(f"{self.cwd}/shards/{shard}") + + def run_in_background( + self, log_file: TextIO, flags: [str] = None, subcommand_flags: [str] = None + ): + """Runs the worker in the background and writes to the supplied logfile. + + :return: process handle for the spawned background process. + """ + + env = dict( + os.environ, + RUST_LOG=self.log_level_dic['bitacross-worker'], + ) + + worker_cmd = self._assemble_cmd(flags=flags, subcommand_flags=subcommand_flags) + print("worker command is: "+ str(worker_cmd)) + return Popen( + worker_cmd, + env=env, + stdout=log_file, + stderr=STDOUT, + bufsize=1, + cwd=self.cwd, + ) + + def _assemble_cmd(self, flags: [str] = None, subcommand_flags: [str] = None): + """Assembles the cmd skipping None values.""" + cmd = self.cli + if flags: + cmd += flags + cmd += ["run"] + if subcommand_flags: + cmd += subcommand_flags + return cmd diff --git a/bitacross-worker/local-setup/rococo_one_worker.json b/bitacross-worker/local-setup/rococo_one_worker.json new file mode 100644 index 0000000000..10e0e556cc --- /dev/null +++ b/bitacross-worker/local-setup/rococo_one_worker.json @@ -0,0 +1,29 @@ +{ + "workers": [ + { + "source": "bin", + "flags": [ + "--clean-reset", + "--ws-external", + "-P", + "2000", + "-w", + "2001", + "-r", + "3443", + "-h", + "4545", + "-u", + "wss://rpc.rococo-parachain.litentry.io", + "-p", + "443", + "--running-mode", + "mock", + "--parentchain-start-block", + "3299860" + ], + "subcommand_flags": [ + ] + } + ] +} \ No newline at end of file diff --git a/bitacross-worker/local-setup/tmux_logger.sh b/bitacross-worker/local-setup/tmux_logger.sh new file mode 100755 index 0000000000..a0476468cc --- /dev/null +++ b/bitacross-worker/local-setup/tmux_logger.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +# script that setups a tmux session with three panes that attach to the log files +# of the node and the two workers launched by `./launch.py` + +################################################################################# +# If you work with docker: +# +# 1. run: ./launch.py in docker +# 2. open a new bash session in a new window in the running container: +# docker exec -it [container-id] bash +# 3. run this script: ./tmux_logger.sh +################################################################################# + + +if tmux has-session -t integritee_logger ; then + echo "detected existing polkadot logger session, attaching..." +else + # or start it up freshly + tmux new-session -d -s integritee_logger \; \ + split-window -v \; \ + split-window -v \; \ + select-layout even-vertical \; \ + send-keys -t integritee_logger:0.0 'tail -f ../log/node1.log' C-m \; \ + send-keys -t integritee_logger:0.1 'tail -f ../log/worker1.log' C-m \; \ + send-keys -t integritee_logger:0.2 'tail -f ../log/worker2.log' C-m + + # Attention: Depending on your tmux conf, indexes may start at 1 + + tmux setw -g mouse on +fi +tmux attach-session -d -t integritee_logger \ No newline at end of file diff --git a/bitacross-worker/local-setup/tmux_logger_three_nodes.sh b/bitacross-worker/local-setup/tmux_logger_three_nodes.sh new file mode 100755 index 0000000000..5856e910b6 --- /dev/null +++ b/bitacross-worker/local-setup/tmux_logger_three_nodes.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# script that setups a tmux session with three panes that attach to the log files +# of the node and the two workers launched by `./launch.py` + +################################################################################# +# If you work with docker: +# +# 1. run: ./launch.py in docker +# 2. open a new bash session in a new window in the running container: +# docker exec -it [container-id] bash +# 3. run this script: ./tmux_logger.sh +################################################################################# + + +if tmux has-session -t integritee_logger_three_nodes ; then + echo "detected existing polkadot logger session, attaching..." +else + # or start it up freshly + tmux new-session -d -s integritee_logger_three_nodes \; \ + split-window -v \; \ + split-window -v \; \ + split-window -v \; \ + select-layout even-vertical \; \ + send-keys -t integritee_logger_three_nodes:0.0 'tail -f ../log/node1.log' C-m \; \ + send-keys -t integritee_logger_three_nodes:0.1 'tail -f ../log/node2.log' C-m \; \ + send-keys -t integritee_logger_three_nodes:0.2 'tail -f ../log/node3.log' C-m \; \ + send-keys -t integritee_logger_three_nodes:0.3 'tail -f ../log/worker1.log' C-m \; \ + + # Attention: Depending on your tmux conf, indexes may start at 1 + + tmux setw -g mouse on +fi +tmux attach-session -d -t integritee_logger_three_nodes \ No newline at end of file diff --git a/bitacross-worker/local-setup/worker-log-level-config.toml b/bitacross-worker/local-setup/worker-log-level-config.toml new file mode 100644 index 0000000000..44a4ea00b2 --- /dev/null +++ b/bitacross-worker/local-setup/worker-log-level-config.toml @@ -0,0 +1,38 @@ +[bitacross-worker] +RUST_LOG="info" +litentry_worker="debug" +ws="warn" +sp_io="error" +substrate_api_client="warn" +itc_parentchain_light_client="info" +jsonrpsee_ws_client="warn" +jsonrpsee_ws_server="warn" +enclave_runtime="debug" +ita_stf="debug" +its_rpc_handler="warn" +itc_rpc_client="warn" +its_consensus_common="debug" +its_state="warn" +its_consensus_aura="warn" +"aura*"="warn" +its_consensus_slots="warn" +itc_direct_rpc_server="debug" +itp_attestation_handler="debug" +http_req="debug" +lc_mock_server="warn" +itc_rest_client="debug" +lc_credentials="debug" +lc_identity_verification="debug" +lc_stf_task_receiver="debug" +lc_stf_task_sender="debug" +lc_data_providers="debug" +itp_top_pool="debug" +itc_parentchain_indirect_calls_executor="debug" +itc_direct_rpc_client="debug" + +[bitacross-cli] +RUST_LOG="debug" +ws="warn" +sp_io="warn" +substrate_api_client="warn" +enclave="debug" diff --git a/bitacross-worker/rust-sgx-sdk/Readme.md b/bitacross-worker/rust-sgx-sdk/Readme.md new file mode 100644 index 0000000000..4c71699c10 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/Readme.md @@ -0,0 +1,5 @@ +# RUST-SGX-SDK + +This folder contains only the neccessary parts from the [RUST-SGX-SDK](https://github.com/baidu/rust-sgx-sdk). + +All the crates are directly fetched from github. \ No newline at end of file diff --git a/bitacross-worker/rust-sgx-sdk/buildenv.mk b/bitacross-worker/rust-sgx-sdk/buildenv.mk new file mode 100644 index 0000000000..ce28be4e55 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/buildenv.mk @@ -0,0 +1,179 @@ +# +# Copyright (C) 2017-2018 Baidu, Inc. All Rights Reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Baidu, Inc., nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# + +CP := /bin/cp -f +MKDIR := mkdir -p +STRIP := strip +OBJCOPY := objcopy + +# clean the content of 'INCLUDE' - this variable will be set by vcvars32.bat +# thus it will cause build error when this variable is used by our Makefile, +# when compiling the code under Cygwin tainted by MSVC environment settings. +INCLUDE := + +# turn on stack protector for SDK +COMMON_FLAGS += -fstack-protector + +ifdef DEBUG + COMMON_FLAGS += -O0 -g -DDEBUG -UNDEBUG +else + COMMON_FLAGS += -O2 -D_FORTIFY_SOURCE=2 -UDEBUG -DNDEBUG +endif + +# turn on compiler warnings as much as possible +COMMON_FLAGS += -Wall -Wextra -Winit-self -Wpointer-arith -Wreturn-type \ + -Waddress -Wsequence-point -Wformat-security \ + -Wmissing-include-dirs -Wfloat-equal -Wundef -Wshadow \ + -Wcast-align -Wconversion -Wredundant-decls + +# additional warnings flags for C +CFLAGS += -Wjump-misses-init -Wstrict-prototypes -Wunsuffixed-float-constants + +# additional warnings flags for C++ +CXXFLAGS += -Wnon-virtual-dtor + +# for static_assert() +CXXFLAGS += -std=c++0x + +.DEFAULT_GOAL := all +# this turns off the RCS / SCCS implicit rules of GNU Make +% : RCS/%,v +% : RCS/% +% : %,v +% : s.% +% : SCCS/s.% + +# If a rule fails, delete $@. +.DELETE_ON_ERROR: + +HOST_FILE_PROGRAM := file + +UNAME := $(shell uname -m) +ifneq (,$(findstring 86,$(UNAME))) + HOST_ARCH := x86 + ifneq (,$(shell $(HOST_FILE_PROGRAM) -L $(SHELL) | grep 'x86[_-]64')) + HOST_ARCH := x86_64 + endif +else + $(info Unknown host CPU architecture $(UNAME)) + $(error Aborting) +endif + + +ifeq "$(findstring __INTEL_COMPILER, $(shell $(CC) -E -dM -xc /dev/null))" "__INTEL_COMPILER" + ifeq ($(shell test -f /usr/bin/dpkg; echo $$?), 0) + ADDED_INC := -I /usr/include/$(shell dpkg-architecture -qDEB_BUILD_MULTIARCH) + endif +endif + +ARCH := $(HOST_ARCH) +ifeq "$(findstring -m32, $(CXXFLAGS))" "-m32" + ARCH := x86 +endif + +ifeq ($(ARCH), x86) +COMMON_FLAGS += -DITT_ARCH_IA32 +else +COMMON_FLAGS += -DITT_ARCH_IA64 +endif + +CFLAGS += $(COMMON_FLAGS) +CXXFLAGS += $(COMMON_FLAGS) + +# Enable the security flags +COMMON_LDFLAGS := -Wl,-z,relro,-z,now,-z,noexecstack + +# mitigation options +MITIGATION_INDIRECT ?= 0 +MITIGATION_RET ?= 0 +MITIGATION_C ?= 0 +MITIGATION_ASM ?= 0 +MITIGATION_AFTERLOAD ?= 0 +MITIGATION_LIB_PATH := + +ifeq ($(MITIGATION-CVE-2020-0551), LOAD) + MITIGATION_C := 1 + MITIGATION_ASM := 1 + MITIGATION_INDIRECT := 1 + MITIGATION_RET := 1 + MITIGATION_AFTERLOAD := 1 + MITIGATION_LIB_PATH := cve_2020_0551_load +else ifeq ($(MITIGATION-CVE-2020-0551), CF) + MITIGATION_C := 1 + MITIGATION_ASM := 1 + MITIGATION_INDIRECT := 1 + MITIGATION_RET := 1 + MITIGATION_AFTERLOAD := 0 + MITIGATION_LIB_PATH := cve_2020_0551_cf +endif + +MITIGATION_CFLAGS := +MITIGATION_ASFLAGS := +ifeq ($(MITIGATION_C), 1) +ifeq ($(MITIGATION_INDIRECT), 1) + MITIGATION_CFLAGS += -mindirect-branch-register +endif +ifeq ($(MITIGATION_RET), 1) + MITIGATION_CFLAGS += -mfunction-return=thunk-extern +endif +endif + +ifeq ($(MITIGATION_ASM), 1) + MITIGATION_ASFLAGS += -fno-plt +ifeq ($(MITIGATION_AFTERLOAD), 1) + MITIGATION_ASFLAGS += -Wa,-mlfence-after-load=yes +else + MITIGATION_ASFLAGS += -Wa,-mlfence-before-indirect-branch=register +endif +ifeq ($(MITIGATION_RET), 1) + MITIGATION_ASFLAGS += -Wa,-mlfence-before-ret=not +endif +endif + +MITIGATION_CFLAGS += $(MITIGATION_ASFLAGS) + +# Compiler and linker options for an Enclave +# +# We are using '--export-dynamic' so that `g_global_data_sim' etc. +# will be exported to dynamic symbol table. +# +# When `pie' is enabled, the linker (both BFD and Gold) under Ubuntu 14.04 +# will hide all symbols from dynamic symbol table even if they are marked +# as `global' in the LD version script. +ENCLAVE_CFLAGS = -ffreestanding -nostdinc -fvisibility=hidden -fpie -fno-strict-overflow -fno-delete-null-pointer-checks +ENCLAVE_CXXFLAGS = $(ENCLAVE_CFLAGS) -nostdinc++ +ENCLAVE_LDFLAGS = $(COMMON_LDFLAGS) -Wl,-Bstatic -Wl,-Bsymbolic -Wl,--no-undefined \ + -Wl,-pie,-eenclave_entry -Wl,--export-dynamic \ + -Wl,--gc-sections \ + -Wl,--defsym,__ImageBase=0 + +ENCLAVE_CFLAGS += $(MITIGATION_CFLAGS) +ENCLAVE_ASFLAGS = $(MITIGATION_ASFLAGS) \ No newline at end of file diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/assert.h b/bitacross-worker/rust-sgx-sdk/common/inc/assert.h new file mode 100644 index 0000000000..a153995416 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/assert.h @@ -0,0 +1,63 @@ +/* $OpenBSD: assert.h,v 1.12 2006/01/31 10:53:51 hshoexer Exp $ */ +/* $NetBSD: assert.h,v 1.6 1994/10/26 00:55:44 cgd Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)assert.h 8.2 (Berkeley) 1/21/94 + */ + +/* + * Unlike other ANSI header files, may usefully be included + * multiple times, with and without NDEBUG defined. + */ + +#include + +#undef assert + +#ifdef NDEBUG +# define assert(e) ((void)0) +#else +# define assert(e) ((e) ? (void)0 : __assert(__FILE__, __LINE__, __func__, #e)) +#endif + +#ifndef _ASSERT_H_DECLS +#define _ASSERT_H_DECLS +__BEGIN_DECLS + +void _TLIBC_CDECL_ __assert(const char *, int, const char *, const char *); + +__END_DECLS +#endif /* Not _ASSERT_H_DECLS */ + diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/complex.h b/bitacross-worker/rust-sgx-sdk/common/inc/complex.h new file mode 100644 index 0000000000..904cb31fbf --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/complex.h @@ -0,0 +1,134 @@ +/* $OpenBSD: complex.h,v 1.3 2010/07/24 22:17:03 guenther Exp $ */ +/* + * Copyright (c) 2008 Martynas Venckus + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _COMPLEX_H_ +#define _COMPLEX_H_ + +#include + +/* + * C99 + */ +#ifdef __GNUC__ +#if __STDC_VERSION__ < 199901 +#define _Complex __complex__ +#endif +#define _Complex_I 1.0fi +#elif defined(lint) +#define _Complex_I 1.0fi +#endif + +#define complex _Complex + +/* XXX switch to _Imaginary_I */ +#undef I +#define I _Complex_I + +__BEGIN_DECLS +/* + * Double versions of C99 functions + */ +double complex cacos(double complex); +double complex casin(double complex); +double complex catan(double complex); +double complex ccos(double complex); +double complex csin(double complex); +double complex ctan(double complex); +double complex cacosh(double complex); +double complex casinh(double complex); +double complex catanh(double complex); +double complex ccosh(double complex); +double complex csinh(double complex); +double complex ctanh(double complex); +double complex cexp(double complex); +double complex clog(double complex); +double cabs(double complex); +double complex cpow(double complex, double complex); +double complex csqrt(double complex); +double carg(double complex); +double cimag(double complex); +double complex conj(double complex); +double complex cproj(double complex); +double creal(double complex); +/* + * C99 reserved + */ +double complex clog10(double complex); + +/* + * Float versions of C99 functions + */ +float complex cacosf(float complex); +float complex casinf(float complex); +float complex catanf(float complex); +float complex ccosf(float complex); +float complex csinf(float complex); +float complex ctanf(float complex); +float complex cacoshf(float complex); +float complex casinhf(float complex); +float complex catanhf(float complex); +float complex ccoshf(float complex); +float complex csinhf(float complex); +float complex ctanhf(float complex); +float complex cexpf(float complex); +float complex clogf(float complex); +float cabsf(float complex); +float complex cpowf(float complex, float complex); +float complex csqrtf(float complex); +float cargf(float complex); +float cimagf(float complex); +float complex conjf(float complex); +float complex cprojf(float complex); +float crealf(float complex); +/* + * C99 reserved + */ +float complex clog10f(float complex); + +/* + * Long double versions of C99 functions + */ +long double complex cacosl(long double complex); +long double complex casinl(long double complex); +long double complex catanl(long double complex); +long double complex ccosl(long double complex); +long double complex csinl(long double complex); +long double complex ctanl(long double complex); +long double complex cacoshl(long double complex); +long double complex casinhl(long double complex); +long double complex catanhl(long double complex); +long double complex ccoshl(long double complex); +long double complex csinhl(long double complex); +long double complex ctanhl(long double complex); +long double complex cexpl(long double complex); +long double complex clogl(long double complex); +long double cabsl(long double complex); +long double complex cpowl(long double complex, long double complex); +long double complex csqrtl(long double complex); +long double cargl(long double complex); +long double cimagl(long double complex); +long double complex conjl(long double complex); +long double complex cprojl(long double complex); +long double creall(long double complex); +/* + * C99 reserved + */ +long double complex clog10l(long double complex); + +__END_DECLS + +#endif /* !_COMPLEX_H_ */ diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/ctype.h b/bitacross-worker/rust-sgx-sdk/common/inc/ctype.h new file mode 100644 index 0000000000..57ac70ff11 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/ctype.h @@ -0,0 +1,65 @@ +/* $OpenBSD: ctype.h,v 1.22 2010/10/01 20:10:24 guenther Exp $ */ +/* $NetBSD: ctype.h,v 1.14 1994/10/26 00:55:47 cgd Exp $ */ + +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ctype.h 5.3 (Berkeley) 4/3/91 + */ + +#ifndef _CTYPE_H_ +#define _CTYPE_H_ + +#include + +__BEGIN_DECLS + +int _TLIBC_CDECL_ isalnum(int); +int _TLIBC_CDECL_ isalpha(int); +int _TLIBC_CDECL_ iscntrl(int); +int _TLIBC_CDECL_ isdigit(int); +int _TLIBC_CDECL_ isgraph(int); +int _TLIBC_CDECL_ islower(int); +int _TLIBC_CDECL_ isprint(int); +int _TLIBC_CDECL_ ispunct(int); +int _TLIBC_CDECL_ isspace(int); +int _TLIBC_CDECL_ isupper(int); +int _TLIBC_CDECL_ isxdigit(int); +int _TLIBC_CDECL_ tolower(int); +int _TLIBC_CDECL_ toupper(int); +int _TLIBC_CDECL_ isblank(int); +int _TLIBC_CDECL_ isascii(int); + +__END_DECLS + +#endif /* _CTYPE_H_ */ diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/dirent.h b/bitacross-worker/rust-sgx-sdk/common/inc/dirent.h new file mode 100644 index 0000000000..a0ede0375c --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/dirent.h @@ -0,0 +1,48 @@ +// +// Copyright © 2005-2020 Rich Felker, et al. +// Licensed under the MIT license.s +// + +/* Copyright © 2005-2020 Rich Felker, et al. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef _DIRENT_H_ +#define _DIRENT_H_ + +struct dirent { + __ino_t d_ino; + __off_t d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[256]; +}; + +struct dirent64 { + __ino64_t d_ino; + __off64_t d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[256]; +}; + +#define d_fileno d_ino + +#endif /* _DIRENT_H_ */ diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/endian.h b/bitacross-worker/rust-sgx-sdk/common/inc/endian.h new file mode 100644 index 0000000000..2620c5898f --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/endian.h @@ -0,0 +1,33 @@ +/* $OpenBSD: endian.h,v 1.18 2006/03/27 07:09:24 otto Exp $ */ + +/*- + * Copyright (c) 1997 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _ENDIAN_H_ +#define _ENDIAN_H_ + +#include + +#endif /* _ENDIAN_H_ */ + diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/errno.h b/bitacross-worker/rust-sgx-sdk/common/inc/errno.h new file mode 100644 index 0000000000..dbe293cb9e --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/errno.h @@ -0,0 +1,187 @@ +/* $OpenBSD: errno.h,v 1.1 2005/12/28 16:33:56 millert Exp $ */ + +/* + * Copyright (c) 1982, 1986, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)errno.h 8.5 (Berkeley) 1/21/94 + */ + +#ifndef _ERRNO_H_ +#define _ERRNO_H_ + +#include + +#define EPERM 1 +#define ENOENT 2 +#define ESRCH 3 +#define EINTR 4 +#define EIO 5 +#define ENXIO 6 +#define E2BIG 7 +#define ENOEXEC 8 +#define EBADF 9 +#define ECHILD 10 +#define EAGAIN 11 +#define ENOMEM 12 +#define EACCES 13 +#define EFAULT 14 +#define ENOTBLK 15 +#define EBUSY 16 +#define EEXIST 17 +#define EXDEV 18 +#define ENODEV 19 +#define ENOTDIR 20 +#define EISDIR 21 +#define EINVAL 22 +#define ENFILE 23 +#define EMFILE 24 +#define ENOTTY 25 +#define ETXTBSY 26 +#define EFBIG 27 +#define ENOSPC 28 +#define ESPIPE 29 +#define EROFS 30 +#define EMLINK 31 +#define EPIPE 32 +#define EDOM 33 +#define ERANGE 34 +#define EDEADLK 35 +#define ENAMETOOLONG 36 +#define ENOLCK 37 +#define ENOSYS 38 +#define ENOTEMPTY 39 +#define ELOOP 40 +#define EWOULDBLOCK EAGAIN +#define ENOMSG 42 +#define EIDRM 43 +#define ECHRNG 44 +#define EL2NSYNC 45 +#define EL3HLT 46 +#define EL3RST 47 +#define ELNRNG 48 +#define EUNATCH 49 +#define ENOCSI 50 +#define EL2HLT 51 +#define EBADE 52 +#define EBADR 53 +#define EXFULL 54 +#define ENOANO 55 +#define EBADRQC 56 +#define EBADSLT 57 +#define EDEADLOCK EDEADLK +#define EBFONT 59 +#define ENOSTR 60 +#define ENODATA 61 +#define ETIME 62 +#define ENOSR 63 +#define ENONET 64 +#define ENOPKG 65 +#define EREMOTE 66 +#define ENOLINK 67 +#define EADV 68 +#define ESRMNT 69 +#define ECOMM 70 +#define EPROTO 71 +#define EMULTIHOP 72 +#define EDOTDOT 73 +#define EBADMSG 74 +#define EOVERFLOW 75 +#define ENOTUNIQ 76 +#define EBADFD 77 +#define EREMCHG 78 +#define ELIBACC 79 +#define ELIBBAD 80 +#define ELIBSCN 81 +#define ELIBMAX 82 +#define ELIBEXEC 83 +#define EILSEQ 84 +#define ERESTART 85 +#define ESTRPIPE 86 +#define EUSERS 87 +#define ENOTSOCK 88 +#define EDESTADDRREQ 89 +#define EMSGSIZE 90 +#define EPROTOTYPE 91 +#define ENOPROTOOPT 92 +#define EPROTONOSUPPORT 93 +#define ESOCKTNOSUPPORT 94 +#define EOPNOTSUPP 95 +#define EPFNOSUPPORT 96 +#define EAFNOSUPPORT 97 +#define EADDRINUSE 98 +#define EADDRNOTAVAIL 99 +#define ENETDOWN 100 +#define ENETUNREACH 101 +#define ENETRESET 102 +#define ECONNABORTED 103 +#define ECONNRESET 104 +#define ENOBUFS 105 +#define EISCONN 106 +#define ENOTCONN 107 +#define ESHUTDOWN 108 +#define ETOOMANYREFS 109 +#define ETIMEDOUT 110 +#define ECONNREFUSED 111 +#define EHOSTDOWN 112 +#define EHOSTUNREACH 113 +#define EALREADY 114 +#define EINPROGRESS 115 +#define ESTALE 116 +#define EUCLEAN 117 +#define ENOTNAM 118 +#define ENAVAIL 119 +#define EISNAM 120 +#define EREMOTEIO 121 +#define EDQUOT 122 +#define ENOMEDIUM 123 +#define EMEDIUMTYPE 124 +#define ECANCELED 125 +#define ENOKEY 126 +#define EKEYEXPIRED 127 +#define EKEYREVOKED 128 +#define EKEYREJECTED 129 +#define EOWNERDEAD 130 +#define ENOTRECOVERABLE 131 +#define ERFKILL 132 +#define EHWPOISON 133 +#define ENOTSUP EOPNOTSUPP + +__BEGIN_DECLS + +#ifndef errno +int * _TLIBC_CDECL_ __errno(void); +#define errno (*__errno()) +#endif /* errno */ +__END_DECLS + +#endif /* _ERRNO_H_ */ diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/fenv.h b/bitacross-worker/rust-sgx-sdk/common/inc/fenv.h new file mode 100644 index 0000000000..a233172a41 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/fenv.h @@ -0,0 +1,139 @@ +/* $OpenBSD: fenv.h,v 1.2 2011/05/25 21:46:49 martynas Exp $ */ +/* $NetBSD: fenv.h,v 1.2.4.1 2011/02/08 16:18:55 bouyer Exp $ */ + +/* + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FENV_H_ +#define _FENV_H_ + +#include + +/* + * Each symbol representing a floating point exception expands to an integer + * constant expression with values, such that bitwise-inclusive ORs of _all + * combinations_ of the constants result in distinct values. + * + * We use such values that allow direct bitwise operations on FPU/SSE registers. + */ +#define FE_INVALID 0x01 +#define FE_DENORMAL 0x02 +#define FE_DIVBYZERO 0x04 +#define FE_OVERFLOW 0x08 +#define FE_UNDERFLOW 0x10 +#define FE_INEXACT 0x20 + +/* + * The following symbol is simply the bitwise-inclusive OR of all floating-point + * exception constants defined above. + */ +#define FE_ALL_EXCEPT (FE_INVALID | FE_DENORMAL | FE_DIVBYZERO | \ + FE_OVERFLOW | FE_UNDERFLOW | FE_INEXACT) +#define _SSE_MASK_SHIFT 7 + +/* + * Each symbol representing the rounding direction, expands to an integer + * constant expression whose value is distinct non-negative value. + * + * We use such values that allow direct bitwise operations on FPU/SSE registers. + */ +#define FE_TONEAREST 0x000 +#define FE_DOWNWARD 0x400 +#define FE_UPWARD 0x800 +#define FE_TOWARDZERO 0xc00 + +/* + * The following symbol is simply the bitwise-inclusive OR of all floating-point + * rounding direction constants defined above. + */ +#define _X87_ROUND_MASK (FE_TONEAREST | FE_DOWNWARD | FE_UPWARD | \ + FE_TOWARDZERO) +#define _SSE_ROUND_SHIFT 3 + +/* + * fenv_t represents the entire floating-point environment. + */ +typedef struct { + struct { + unsigned int __control; /* Control word register */ + unsigned int __status; /* Status word register */ + unsigned int __tag; /* Tag word register */ + unsigned int __others[4]; /* EIP, Pointer Selector, etc */ + } __x87; + unsigned int __mxcsr; /* Control, status register */ +} fenv_t; + +/* + * The following constant represents the default floating-point environment + * (that is, the one installed at program startup) and has type pointer to + * const-qualified fenv_t. + * + * It can be used as an argument to the functions within the header + * that manage the floating-point environment, namely fesetenv() and + * feupdateenv(). + */ +__BEGIN_DECLS +extern fenv_t __fe_dfl_env; +__END_DECLS +#define FE_DFL_ENV ((const fenv_t *)&__fe_dfl_env) + +/* + * fexcept_t represents the floating-point status flags collectively, including + * any status the implementation associates with the flags. + * + * A floating-point status flag is a system variable whose value is set (but + * never cleared) when a floating-point exception is raised, which occurs as a + * side effect of exceptional floating-point arithmetic to provide auxiliary + * information. + * + * A floating-point control mode is a system variable whose value may be set by + * the user to affect the subsequent behavior of floating-point arithmetic. + */ +typedef unsigned int fexcept_t; + +__BEGIN_DECLS + +int feclearexcept(int); +int fegetexceptflag(fexcept_t *, int); +int feraiseexcept(int); +int fesetexceptflag(const fexcept_t *, int); +int fetestexcept(int); + +int fegetround(void); +int fesetround(int); + +int fegetenv(fenv_t *); +int feholdexcept(fenv_t *); +int fesetenv(const fenv_t *); +int feupdateenv(const fenv_t *); + +int feenableexcept(int); +int fedisableexcept(int); +int fegetexcept(void); + +__END_DECLS + +#endif /* ! _FENV_H_ */ diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/float.h b/bitacross-worker/rust-sgx-sdk/common/inc/float.h new file mode 100644 index 0000000000..e38a7c6a9f --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/float.h @@ -0,0 +1,84 @@ +/* $OpenBSD: float.h,v 1.3 2008/07/21 20:50:54 martynas Exp $ */ +/* $NetBSD: float.h,v 1.8 1995/06/20 20:45:37 jtc Exp $ */ + +/* + * Copyright (c) 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)float.h 7.1 (Berkeley) 5/8/90 + */ + +#ifndef _FLOAT_H_ +#define _FLOAT_H_ + +#include + +#define FLT_RADIX 2 /* b */ + +// The rounding direction can be specified by fesetround() in +#define FLT_ROUNDS 1 /* addition rounding: near */ +#define DECIMAL_DIG 21 /* max precision in decimal digits */ + +// NOTE: FLT_EVAL_METHOD is -1 under FREEBSD x86. +#ifdef __i386__ +#define FLT_EVAL_METHOD 2 /* long double */ +#else +#define FLT_EVAL_METHOD 0 /* no promotions */ +#endif + +#define DBL_MANT_DIG 53 +#define DBL_EPSILON 2.2204460492503131E-16 +#define DBL_DIG 15 +#define DBL_MIN_EXP (-1021) +#define DBL_MIN 2.2250738585072014E-308 +#define DBL_MIN_10_EXP (-307) +#define DBL_MAX_EXP 1024 +#define DBL_MAX_10_EXP 308 + +#define FLT_MANT_DIG 24 /* p */ +#define FLT_DIG 6 /* floor((p-1)*log10(b))+(b == 10) */ +#define FLT_MIN_EXP (-125) /* emin */ +#define FLT_MIN_10_EXP (-37) /* ceil(log10(b**(emin-1))) */ +#define FLT_MAX_EXP 128 /* emax */ +#define FLT_MAX_10_EXP 38 /* floor(log10((1-b**(-p))*b**emax)) */ + +#define DBL_MAX 1.7976931348623157E+308 +#define FLT_EPSILON 1.19209290E-07F /* b**(1-p) */ +#define FLT_MIN 1.17549435E-38F /* b**(emin-1) */ +#define FLT_MAX 3.40282347E+38F /* (1-b**(-p))*b**emax */ + +#define LDBL_MANT_DIG 64 +#define LDBL_EPSILON 1.08420217248550443401e-19L +#define LDBL_DIG 18 +#define LDBL_MIN_EXP (-16381) +#define LDBL_MIN 3.36210314311209350626e-4932L +#define LDBL_MIN_10_EXP (-4931) +#define LDBL_MAX_EXP 16384 +#define LDBL_MAX 1.18973149535723176502e+4932L +#define LDBL_MAX_10_EXP 4932 + +#endif /* _FLOAT_H_ */ diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/inttypes.h b/bitacross-worker/rust-sgx-sdk/common/inc/inttypes.h new file mode 100644 index 0000000000..fbc009c975 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/inttypes.h @@ -0,0 +1,330 @@ +/* $OpenBSD: inttypes.h,v 1.10 2009/01/13 18:13:51 kettenis Exp $ */ + +/* + * Copyright (c) 1997, 2005 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _INTTYPES_H_ +#define _INTTYPES_H_ + +#include + +/* + * 7.8.1 Macros for format specifiers + * + * Each of the following object-like macros expands to a string + * literal containing a conversion specifier, possibly modified by + * a prefix such as hh, h, l, or ll, suitable for use within the + * format argument of a formatted input/output function when + * converting the corresponding integer type. These macro names + * have the general form of PRI (character string literals for the + * fprintf family) or SCN (character string literals for the fscanf + * family), followed by the conversion specifier, followed by a + * name corresponding to a similar typedef name. For example, + * PRIdFAST32 can be used in a format string to print the value of + * an integer of type int_fast32_t. + */ + +/* fprintf macros for signed integers */ +#define PRId8 "d" /* int8_t */ +#define PRId16 "d" /* int16_t */ +#define PRId32 "d" /* int32_t */ +#ifdef __x86_64__ +#define PRId64 "ld" /* int64_t */ +#else +#define PRId64 "lld" /* int64_t */ +#endif + +#define PRIdLEAST8 "d" /* int_least8_t */ +#define PRIdLEAST16 "d" /* int_least16_t */ +#define PRIdLEAST32 "d" /* int_least32_t */ +#ifdef __x86_64__ +#define PRIdLEAST64 "ld" /* int_least64_t */ +#else +#define PRIdLEAST64 "lld" /* int_least64_t */ +#endif + +#define PRIdFAST8 "d" /* int_fast8_t */ +#ifdef __x86_64__ +#define PRIdFAST16 "ld" /* int_fast16_t */ +#define PRIdFAST32 "ld" /* int_fast32_t */ +#define PRIdFAST64 "ld" /* int_fast64_t */ +#else +#define PRIdFAST16 "d" /* int_fast16_t */ +#define PRIdFAST32 "d" /* int_fast32_t */ +#define PRIdFAST64 "lld" /* int_fast64_t */ +#endif + +#ifdef __x86_64__ +#define PRIdMAX "ld" /* intmax_t */ +#else +#if defined(__i386__) +#define PRIdMAX "lld" /* intmax_t */ +#else +#define PRIdMAX "jd" /* intmax_t */ +#endif +#endif + +#ifdef __i386__ +#define PRIdPTR "d" /* intptr_t */ +#else +#define PRIdPTR "ld" /* intptr_t */ +#endif + +#define PRIi8 "i" /* int8_t */ +#define PRIi16 "i" /* int16_t */ +#define PRIi32 "i" /* int32_t */ +#ifdef __x86_64__ +#define PRIi64 "li" /* int64_t */ +#else +#define PRIi64 "lli" /* int64_t */ +#endif + +#define PRIiLEAST8 "i" /* int_least8_t */ +#define PRIiLEAST16 "i" /* int_least16_t */ +#define PRIiLEAST32 "i" /* int_least32_t */ +#ifdef __x86_64__ +#define PRIiLEAST64 "li" /* int_least64_t */ +#else +#define PRIiLEAST64 "lli" /* int_least64_t */ +#endif + +#define PRIiFAST8 "i" /* int_fast8_t */ +#ifdef __x86_64__ +#define PRIiFAST16 "li" /* int_fast16_t */ +#define PRIiFAST32 "li" /* int_fast32_t */ +#define PRIiFAST64 "li" /* int_fast64_t */ +#else +#define PRIiFAST16 "i" /* int_fast16_t */ +#define PRIiFAST32 "i" /* int_fast32_t */ +#define PRIiFAST64 "lli" /* int_fast64_t */ +#endif + +#ifdef __x86_64__ +#define PRIiMAX "li" /* intmax_t */ +#else +#if defined(__i386__) +#define PRIiMAX "lli" /* intmax_t */ +#else +#define PRIiMAX "ji" /* intmax_t */ +#endif +#endif + +#ifdef __i386__ +#define PRIiPTR "i" /* intptr_t */ +#else +#define PRIiPTR "li" /* intptr_t */ +#endif + +/* fprintf macros for unsigned integers */ +#define PRIo8 "o" /* int8_t */ +#define PRIo16 "o" /* int16_t */ +#define PRIo32 "o" /* int32_t */ +#ifdef __x86_64__ +#define PRIo64 "lo" /* int64_t */ +#else +#define PRIo64 "llo" /* int64_t */ +#endif + +#define PRIoLEAST8 "o" /* int_least8_t */ +#define PRIoLEAST16 "o" /* int_least16_t */ +#define PRIoLEAST32 "o" /* int_least32_t */ +#ifdef __x86_64__ +#define PRIoLEAST64 "lo" /* int_least64_t */ +#else +#define PRIoLEAST64 "llo" /* int_least64_t */ +#endif + +#define PRIoFAST8 "o" /* int_fast8_t */ +#ifdef __x86_64__ +#define PRIoFAST16 "lo" /* int_fast16_t */ +#define PRIoFAST32 "lo" /* int_fast32_t */ +#define PRIoFAST64 "lo" /* int_fast64_t */ +#else +#define PRIoFAST16 "o" /* int_fast16_t */ +#define PRIoFAST32 "o" /* int_fast32_t */ +#define PRIoFAST64 "llo" /* int_fast64_t */ +#endif + +#ifdef __x86_64__ +#define PRIoMAX "lo" /* intmax_t */ +#else +#if defined(__i386__) +#define PRIoMAX "llo" /* intmax_t */ +#else +#define PRIoMAX "jo" /* intmax_t */ +#endif +#endif + +#ifdef __i386__ +#define PRIoPTR "o" /* intptr_t */ +#else +#define PRIoPTR "lo" /* intptr_t */ +#endif + +#define PRIu8 "u" /* uint8_t */ +#define PRIu16 "u" /* uint16_t */ +#define PRIu32 "u" /* uint32_t */ + +#ifdef __x86_64__ +#define PRIu64 "lu" /* uint64_t */ +#else +#define PRIu64 "llu" /* uint64_t */ +#endif + +#define PRIuLEAST8 "u" /* uint_least8_t */ +#define PRIuLEAST16 "u" /* uint_least16_t */ +#define PRIuLEAST32 "u" /* uint_least32_t */ + +#ifdef __x86_64__ +#define PRIuLEAST64 "lu" /* uint_least64_t */ +#else +#define PRIuLEAST64 "llu" /* uint_least64_t */ +#endif + +#define PRIuFAST8 "u" /* uint_fast8_t */ + +#ifdef __x86_64__ +#define PRIuFAST16 "lu" /* uint_fast16_t */ +#define PRIuFAST32 "lu" /* uint_fast32_t */ +#define PRIuFAST64 "lu" /* uint_fast64_t */ +#else +#define PRIuFAST16 "u" /* uint_fast16_t */ +#define PRIuFAST32 "u" /* uint_fast32_t */ +#define PRIuFAST64 "llu" /* uint_fast64_t */ +#endif + +#ifdef __x86_64__ +#define PRIuMAX "lu" /* uintmax_t */ +#else +#if defined(__i386__) +#define PRIuMAX "llu" /* uintmax_t */ +#else +#define PRIuMAX "ju" /* uintmax_t */ +#endif +#endif + +#ifdef __i386__ +#define PRIuPTR "u" /* uintptr_t */ +#else +#define PRIuPTR "lu" /* uintptr_t */ +#endif + +#define PRIx8 "x" /* uint8_t */ +#define PRIx16 "x" /* uint16_t */ +#define PRIx32 "x" /* uint32_t */ +#ifdef __x86_64__ +#define PRIx64 "lx" /* uint64_t */ +#else +#define PRIx64 "llx" /* uint64_t */ +#endif + +#define PRIxLEAST8 "x" /* uint_least8_t */ +#define PRIxLEAST16 "x" /* uint_least16_t */ +#define PRIxLEAST32 "x" /* uint_least32_t */ +#ifdef __x86_64__ +#define PRIxLEAST64 "lx" /* uint_least64_t */ +#else +#define PRIxLEAST64 "llx" /* uint_least64_t */ +#endif + +#define PRIxFAST8 "x" /* uint_fast8_t */ +#ifdef __x86_64__ +#define PRIxFAST16 "lx" /* uint_fast16_t */ +#define PRIxFAST32 "lx" /* uint_fast32_t */ +#define PRIxFAST64 "lx" /* uint_fast64_t */ +#else +#define PRIxFAST16 "x" /* uint_fast16_t */ +#define PRIxFAST32 "x" /* uint_fast32_t */ +#define PRIxFAST64 "llx" /* uint_fast64_t */ +#endif + +#ifdef __x86_64__ +#define PRIxMAX "lx" /* uintmax_t */ +#else +#if defined(__i386__) +#define PRIxMAX "llx" /* uintmax_t */ +#else +#define PRIxMAX "jx" /* uintmax_t */ +#endif +#endif + +#ifdef __i386__ +#define PRIxPTR "x" /* uintptr_t */ +#else +#define PRIxPTR "lx" /* uintptr_t */ +#endif + +#define PRIX8 "X" /* uint8_t */ +#define PRIX16 "X" /* uint16_t */ +#define PRIX32 "X" /* uint32_t */ + +#ifdef __x86_64__ +#define PRIX64 "lX" /* uint64_t */ +#else +#define PRIX64 "llX" /* uint64_t */ +#endif + +#define PRIXLEAST8 "X" /* uint_least8_t */ +#define PRIXLEAST16 "X" /* uint_least16_t */ +#define PRIXLEAST32 "X" /* uint_least32_t */ +#ifdef __x86_64__ +#define PRIXLEAST64 "lX" /* uint_least64_t */ +#else +#define PRIXLEAST64 "llX" /* uint_least64_t */ +#endif + +#define PRIXFAST8 "X" /* uint_fast8_t */ +#ifdef __x86_64__ +#define PRIXFAST16 "lX" /* uint_fast16_t */ +#define PRIXFAST32 "lX" /* uint_fast32_t */ +#define PRIXFAST64 "lX" /* uint_fast64_t */ +#else +#define PRIXFAST16 "X" /* uint_fast16_t */ +#define PRIXFAST32 "X" /* uint_fast32_t */ +#define PRIXFAST64 "llX" /* uint_fast64_t */ +#endif + +#ifdef __x86_64__ +#define PRIXMAX "lX" /* uintmax_t */ +#else +#if defined(__i386__) +#define PRIXMAX "llX" /* uintmax_t */ +#else +#define PRIXMAX "jX" /* uintmax_t */ +#endif +#endif + +#ifdef __i386__ +#define PRIXPTR "X" /* uintptr_t */ +#else +#define PRIXPTR "lX" /* uintptr_t */ +#endif + +typedef struct { + intmax_t quot; /* quotient */ + intmax_t rem; /* remainder */ +} imaxdiv_t; + +__BEGIN_DECLS + +intmax_t _TLIBC_CDECL_ imaxabs(intmax_t); +imaxdiv_t _TLIBC_CDECL_ imaxdiv(intmax_t, intmax_t); +intmax_t _TLIBC_CDECL_ strtoimax(const char *, char **, int); +uintmax_t _TLIBC_CDECL_ strtoumax(const char *, char **, int); + +__END_DECLS + +#endif /* _INTTYPES_H_ */ diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/iso646.h b/bitacross-worker/rust-sgx-sdk/common/inc/iso646.h new file mode 100644 index 0000000000..a0c341b658 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/iso646.h @@ -0,0 +1,26 @@ +/* $OpenBSD: iso646.h,v 1.3 2001/10/11 00:05:21 espie Exp $ */ +/* $NetBSD: iso646.h,v 1.1 1995/02/17 09:08:10 jtc Exp $ */ + +/* + * Written by J.T. Conklin 02/16/95. + * Public domain. + */ + +#ifndef _ISO646_H_ +#define _ISO646_H_ + +#ifndef __cplusplus +#define and && +#define and_eq &= +#define bitand & +#define bitor | +#define compl ~ +#define not ! +#define not_eq != +#define or || +#define or_eq |= +#define xor ^ +#define xor_eq ^= +#endif + +#endif /* !_ISO646_H_ */ diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/limits.h b/bitacross-worker/rust-sgx-sdk/common/inc/limits.h new file mode 100644 index 0000000000..9d42cb545c --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/limits.h @@ -0,0 +1,41 @@ +/* $OpenBSD: limits.h,v 1.15 2008/02/10 09:59:54 kettenis Exp $ */ +/* $NetBSD: limits.h,v 1.7 1994/10/26 00:56:00 cgd Exp $ */ + +/* + * Copyright (c) 1988 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)limits.h 5.9 (Berkeley) 4/3/91 + */ + + +#ifndef _LIMITS_H_ +#define _LIMITS_H_ + +#include + +#endif /* !_LIMITS_H_ */ diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/math.h b/bitacross-worker/rust-sgx-sdk/common/inc/math.h new file mode 100644 index 0000000000..6ea425b840 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/math.h @@ -0,0 +1,430 @@ +/* $OpenBSD: math.h,v 1.27 2010/12/14 11:16:15 martynas Exp $ */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * from: @(#)fdlibm.h 5.1 93/09/24 + */ + +#ifndef _MATH_H_ +#define _MATH_H_ + +#include +#include +#include + +#include + +typedef __float_t float_t; +typedef __double_t double_t; + +#define FP_NAN 0x00 +#define FP_INFINITE 0x01 +#define FP_ZERO 0x02 +#define FP_SUBNORMAL 0x03 +#define FP_NORMAL 0x04 + +#define FP_ILOGB0 (-INT_MAX - 1) +#define FP_ILOGBNAN (-INT_MAX - 1) + +#define fpclassify(x) \ + ((sizeof (x) == sizeof (float)) ? \ + __fpclassifyf(x) \ + : (sizeof (x) == sizeof (double)) ? \ + __fpclassify(x) \ + : __fpclassifyl(x)) +#define isfinite(x) \ + ((sizeof (x) == sizeof (float)) ? \ + __isfinitef(x) \ + : (sizeof (x) == sizeof (double)) ? \ + __isfinite(x) \ + : __isfinitel(x)) +#define isnormal(x) \ + ((sizeof (x) == sizeof (float)) ? \ + __isnormalf(x) \ + : (sizeof (x) == sizeof (double)) ? \ + __isnormal(x) \ + : __isnormall(x)) +#define signbit(x) \ + ((sizeof (x) == sizeof (float)) ? \ + __signbitf(x) \ + : (sizeof (x) == sizeof (double)) ? \ + __signbit(x) \ + : __signbitl(x)) +#define isinf(x) \ + ((sizeof (x) == sizeof (float)) ? \ + __isinff(x) \ + : (sizeof (x) == sizeof (double)) ? \ + __isinf(x) \ + : __isinfl(x)) +#define isnan(x) \ + ((sizeof (x) == sizeof (float)) ? \ + __isnanf(x) \ + : (sizeof (x) == sizeof (double)) ? \ + __isnan(x) \ + : __isnanl(x)) + +#define isgreater(x, y) (!isunordered((x), (y)) && (x) > (y)) +#define isgreaterequal(x, y) (!isunordered((x), (y)) && (x) >= (y)) +#define isless(x, y) (!isunordered((x), (y)) && (x) < (y)) +#define islessequal(x, y) (!isunordered((x), (y)) && (x) <= (y)) +#define islessgreater(x, y) (!isunordered((x), (y)) && ((x) > (y) || (y) > (x))) +#define isunordered(x, y) (isnan(x) || isnan(y)) + +__BEGIN_DECLS + +extern char __infinity[]; +#define HUGE_VAL (*(double *)(void *)__infinity) +#define HUGE_VALF ((float)HUGE_VAL) +#define HUGE_VALL ((long double)HUGE_VAL) +#define INFINITY HUGE_VALF +extern char __nan[]; +#define NAN (*(float *)(void *)__nan) + +/* + * ANSI/POSIX + */ +double _TLIBC_CDECL_ acos(double); +double _TLIBC_CDECL_ asin(double); +double _TLIBC_CDECL_ atan(double); +double _TLIBC_CDECL_ atan2(double, double); +double _TLIBC_CDECL_ cos(double); +double _TLIBC_CDECL_ sin(double); +double _TLIBC_CDECL_ tan(double); + +double _TLIBC_CDECL_ cosh(double); +double _TLIBC_CDECL_ sinh(double); +double _TLIBC_CDECL_ tanh(double); + +double _TLIBC_CDECL_ exp(double); +double _TLIBC_CDECL_ frexp(double, int *); +double _TLIBC_CDECL_ ldexp(double, int); +double _TLIBC_CDECL_ log(double); +double _TLIBC_CDECL_ log10(double); +double _TLIBC_CDECL_ modf(double, double *); + +double _TLIBC_CDECL_ pow(double, double); +double _TLIBC_CDECL_ sqrt(double); + +double _TLIBC_CDECL_ ceil(double); +double _TLIBC_CDECL_ fabs(double); +double _TLIBC_CDECL_ floor(double); +double _TLIBC_CDECL_ fmod(double, double); + +/* + * C99 + */ +double _TLIBC_CDECL_ acosh(double); +double _TLIBC_CDECL_ asinh(double); +double _TLIBC_CDECL_ atanh(double); + +double _TLIBC_CDECL_ exp2(double); +double _TLIBC_CDECL_ expm1(double); +int _TLIBC_CDECL_ ilogb(double); +double _TLIBC_CDECL_ log1p(double); +double _TLIBC_CDECL_ log2(double); +double _TLIBC_CDECL_ logb(double); +double _TLIBC_CDECL_ scalbn(double, int); +double _TLIBC_CDECL_ scalbln(double, long int); + +double _TLIBC_CDECL_ cbrt(double); +double _TLIBC_CDECL_ hypot(double, double); + +double _TLIBC_CDECL_ erf(double); +double _TLIBC_CDECL_ erfc(double); +double _TLIBC_CDECL_ lgamma(double); +double _TLIBC_CDECL_ tgamma(double); + +double _TLIBC_CDECL_ nearbyint(double); +double _TLIBC_CDECL_ rint(double); +long int _TLIBC_CDECL_ lrint(double); +long long int _TLIBC_CDECL_ llrint(double); +double _TLIBC_CDECL_ round(double); +long int _TLIBC_CDECL_ lround(double); +long long int _TLIBC_CDECL_ llround(double); +double _TLIBC_CDECL_ trunc(double); + +double _TLIBC_CDECL_ remainder(double, double); +double _TLIBC_CDECL_ remquo(double, double, int *); + +double _TLIBC_CDECL_ copysign(double, double); +double _TLIBC_CDECL_ nan(const char *); +double _TLIBC_CDECL_ nextafter(double, double); + +double _TLIBC_CDECL_ fdim(double, double); +double _TLIBC_CDECL_ fmax(double, double); +double _TLIBC_CDECL_ fmin(double, double); + +double _TLIBC_CDECL_ fma(double, double, double); + +/* + * Float versions of C99 functions + */ + +float _TLIBC_CDECL_ acosf(float); +float _TLIBC_CDECL_ asinf(float); +float _TLIBC_CDECL_ atanf(float); +float _TLIBC_CDECL_ atan2f(float, float); +float _TLIBC_CDECL_ cosf(float); +float _TLIBC_CDECL_ sinf(float); +float _TLIBC_CDECL_ tanf(float); + +float _TLIBC_CDECL_ acoshf(float); +float _TLIBC_CDECL_ asinhf(float); +float _TLIBC_CDECL_ atanhf(float); +float _TLIBC_CDECL_ coshf(float); +float _TLIBC_CDECL_ sinhf(float); +float _TLIBC_CDECL_ tanhf(float); + +float _TLIBC_CDECL_ expf(float); +float _TLIBC_CDECL_ exp2f(float); +float _TLIBC_CDECL_ expm1f(float); +float _TLIBC_CDECL_ frexpf(float, int *); +int _TLIBC_CDECL_ ilogbf(float); +float _TLIBC_CDECL_ ldexpf(float, int); +float _TLIBC_CDECL_ logf(float); +float _TLIBC_CDECL_ log10f(float); +float _TLIBC_CDECL_ log1pf(float); +float _TLIBC_CDECL_ log2f(float); +float _TLIBC_CDECL_ logbf(float); +float _TLIBC_CDECL_ modff(float, float *); +float _TLIBC_CDECL_ scalbnf(float, int); +float _TLIBC_CDECL_ scalblnf(float, long int); + +float _TLIBC_CDECL_ cbrtf(float); +float _TLIBC_CDECL_ fabsf(float); +float _TLIBC_CDECL_ hypotf(float, float); +float _TLIBC_CDECL_ powf(float, float); +float _TLIBC_CDECL_ sqrtf(float); + +float _TLIBC_CDECL_ erff(float); +float _TLIBC_CDECL_ erfcf(float); +float _TLIBC_CDECL_ lgammaf(float); +float _TLIBC_CDECL_ tgammaf(float); + +float _TLIBC_CDECL_ ceilf(float); +float _TLIBC_CDECL_ floorf(float); +float _TLIBC_CDECL_ nearbyintf(float); + +float _TLIBC_CDECL_ rintf(float); +long int _TLIBC_CDECL_ lrintf(float); +long long int _TLIBC_CDECL_ llrintf(float); +float _TLIBC_CDECL_ roundf(float); +long int _TLIBC_CDECL_ lroundf(float); +long long int _TLIBC_CDECL_ llroundf(float); +float _TLIBC_CDECL_ truncf(float); + +float _TLIBC_CDECL_ fmodf(float, float); +float _TLIBC_CDECL_ remainderf(float, float); +float _TLIBC_CDECL_ remquof(float, float, int *); + +float _TLIBC_CDECL_ copysignf(float, float); +float _TLIBC_CDECL_ nanf(const char *); +float _TLIBC_CDECL_ nextafterf(float, float); + +float _TLIBC_CDECL_ fdimf(float, float); +float _TLIBC_CDECL_ fmaxf(float, float); +float _TLIBC_CDECL_ fminf(float, float); + +float _TLIBC_CDECL_ fmaf(float, float, float); + +/* + * Long double versions of C99 functions + */ + +/* Macros defining long double functions to be their double counterparts + * (long double is synonymous with double in this implementation). + */ + +long double _TLIBC_CDECL_ acosl(long double); +long double _TLIBC_CDECL_ asinl(long double); +long double _TLIBC_CDECL_ atanl(long double); +long double _TLIBC_CDECL_ atan2l(long double, long double); +long double _TLIBC_CDECL_ cosl(long double); +long double _TLIBC_CDECL_ sinl(long double); +long double _TLIBC_CDECL_ tanl(long double); + +long double _TLIBC_CDECL_ acoshl(long double); +long double _TLIBC_CDECL_ asinhl(long double); +long double _TLIBC_CDECL_ atanhl(long double); +long double _TLIBC_CDECL_ coshl(long double); +long double _TLIBC_CDECL_ sinhl(long double); +long double _TLIBC_CDECL_ tanhl(long double); + +long double _TLIBC_CDECL_ expl(long double); +long double _TLIBC_CDECL_ exp2l(long double); +long double _TLIBC_CDECL_ expm1l(long double); +long double _TLIBC_CDECL_ frexpl(long double, int *); +int _TLIBC_CDECL_ ilogbl(long double); +long double _TLIBC_CDECL_ ldexpl(long double, int); +long double _TLIBC_CDECL_ logl(long double); +long double _TLIBC_CDECL_ log10l(long double); +long double _TLIBC_CDECL_ log1pl(long double); +long double _TLIBC_CDECL_ log2l(long double); +long double _TLIBC_CDECL_ logbl(long double); +long double _TLIBC_CDECL_ modfl(long double, long double *); +long double _TLIBC_CDECL_ scalbnl(long double, int); +long double _TLIBC_CDECL_ scalblnl(long double, long int); + +long double _TLIBC_CDECL_ cbrtl(long double); +long double _TLIBC_CDECL_ fabsl(long double); +long double _TLIBC_CDECL_ hypotl(long double, long double); +long double _TLIBC_CDECL_ powl(long double, long double); +long double _TLIBC_CDECL_ sqrtl(long double); + +long double _TLIBC_CDECL_ erfl(long double); +long double _TLIBC_CDECL_ erfcl(long double); +long double _TLIBC_CDECL_ lgammal(long double); +long double _TLIBC_CDECL_ tgammal(long double); + +long double _TLIBC_CDECL_ ceill(long double); +long double _TLIBC_CDECL_ floorl(long double); +long double _TLIBC_CDECL_ nearbyintl(long double); +long double _TLIBC_CDECL_ rintl(long double); +long int _TLIBC_CDECL_ lrintl(long double); +long long int _TLIBC_CDECL_ llrintl(long double); +long double _TLIBC_CDECL_ roundl(long double); +long int _TLIBC_CDECL_ lroundl(long double); +long long int _TLIBC_CDECL_ llroundl(long double); +long double _TLIBC_CDECL_ truncl(long double); + +long double _TLIBC_CDECL_ fmodl(long double, long double); +long double _TLIBC_CDECL_ remainderl(long double, long double); +long double _TLIBC_CDECL_ remquol(long double, long double, int *); + +long double _TLIBC_CDECL_ copysignl(long double, long double); +long double _TLIBC_CDECL_ nanl(const char *); +long double _TLIBC_CDECL_ nextafterl(long double, long double); + +long double _TLIBC_CDECL_ fdiml(long double, long double); +long double _TLIBC_CDECL_ fmaxl(long double, long double); +long double _TLIBC_CDECL_ fminl(long double, long double); +long double _TLIBC_CDECL_ fmal(long double, long double, long double); + +/* nexttoward(): +* The implementation in Intel math library is incompatible with MSVC. +* Because sizeof(long double) is 8bytes with MSVC, +* but the expected long double size is 10bytes. +* And by default, MSVC doesn't provide nexttoward(). +* So we only provide Linux version here. +*/ +double _TLIBC_CDECL_ nexttoward(double, long double); +float _TLIBC_CDECL_ nexttowardf(float, long double); + +long double _TLIBC_CDECL_ nexttowardl(long double, long double); + +/* + * Library implementation + */ +int _TLIBC_CDECL_ __fpclassify(double); +int _TLIBC_CDECL_ __fpclassifyf(float); +int _TLIBC_CDECL_ __isfinite(double); +int _TLIBC_CDECL_ __isfinitef(float); +int _TLIBC_CDECL_ __isinf(double); +int _TLIBC_CDECL_ __isinff(float); +int _TLIBC_CDECL_ __isnan(double); +int _TLIBC_CDECL_ __isnanf(float); +int _TLIBC_CDECL_ __isnormal(double); +int _TLIBC_CDECL_ __isnormalf(float); +int _TLIBC_CDECL_ __signbit(double); +int _TLIBC_CDECL_ __signbitf(float); + +int _TLIBC_CDECL_ __fpclassifyl(long double); +int _TLIBC_CDECL_ __isfinitel(long double); +int _TLIBC_CDECL_ __isinfl(long double); +int _TLIBC_CDECL_ __isnanl(long double); +int _TLIBC_CDECL_ __isnormall(long double); +int _TLIBC_CDECL_ __signbitl(long double); + +/* + * Non-C99 functions. + */ +double _TLIBC_CDECL_ drem(double, double); +double _TLIBC_CDECL_ exp10(double); +double _TLIBC_CDECL_ gamma(double); +double _TLIBC_CDECL_ gamma_r(double, int *); +double _TLIBC_CDECL_ j0(double); +double _TLIBC_CDECL_ j1(double); +double _TLIBC_CDECL_ jn(int, double); +double _TLIBC_CDECL_ lgamma_r(double, int *); +double _TLIBC_CDECL_ pow10(double); +double _TLIBC_CDECL_ scalb(double, double); +/* C99 Macro signbit.*/ +double _TLIBC_CDECL_ significand(double); +void _TLIBC_CDECL_ sincos(double, double *, double *); +double _TLIBC_CDECL_ y0(double); +double _TLIBC_CDECL_ y1(double); +double _TLIBC_CDECL_ yn(int, double); +/* C99 Macro isinf.*/ +/* C99 Macro isnan.*/ +int _TLIBC_CDECL_ finite(double); + +float _TLIBC_CDECL_ dremf(float, float); +float _TLIBC_CDECL_ exp10f(float); +float _TLIBC_CDECL_ gammaf(float); +float _TLIBC_CDECL_ gammaf_r(float, int *); +float _TLIBC_CDECL_ j0f(float); +float _TLIBC_CDECL_ j1f(float); +float _TLIBC_CDECL_ jnf(int, float); +float _TLIBC_CDECL_ lgammaf_r(float, int *); +float _TLIBC_CDECL_ pow10f(float); +float _TLIBC_CDECL_ scalbf(float, float); +int _TLIBC_CDECL_ signbitf(float); +float _TLIBC_CDECL_ significandf(float); +void _TLIBC_CDECL_ sincosf(float, float *, float *); +float _TLIBC_CDECL_ y0f(float); +float _TLIBC_CDECL_ y1f(float); +float _TLIBC_CDECL_ ynf(int, float); +int _TLIBC_CDECL_ finitef(float); +int _TLIBC_CDECL_ isinff(float); +int _TLIBC_CDECL_ isnanf(float); + +long double _TLIBC_CDECL_ dreml(long double, long double); +long double _TLIBC_CDECL_ exp10l(long double); +long double _TLIBC_CDECL_ gammal(long double); +long double _TLIBC_CDECL_ gammal_r(long double, int *); +long double _TLIBC_CDECL_ j0l(long double); +long double _TLIBC_CDECL_ j1l(long double); +long double _TLIBC_CDECL_ jnl(int, long double); +long double _TLIBC_CDECL_ lgammal_r(long double, int *); +long double _TLIBC_CDECL_ pow10l(long double); +long double _TLIBC_CDECL_ scalbl(long double, long double); +int _TLIBC_CDECL_ signbitl(long double); +long double _TLIBC_CDECL_ significandl(long double); +void _TLIBC_CDECL_ sincosl(long double, long double *, long double *); +long double _TLIBC_CDECL_ y1l(long double); +long double _TLIBC_CDECL_ y0l(long double); +long double _TLIBC_CDECL_ ynl(int, long double); +int _TLIBC_CDECL_ finitel(long double); +int _TLIBC_CDECL_ isinfl(long double); +int _TLIBC_CDECL_ isnanl(long double); + +/* + * TODO: From Intel Decimal Floating-Point Math Library + * signbitd32/signbitd64/signbitd128, finited32/finited64/finited128 + * isinfd32/isinfd64/isinfd128, isnand32/isnand64/isnand128 + */ +#if defined(__cplusplus) +/* Clang does not support decimal floating point types. + * + * c.f.: + * http://clang.llvm.org/docs/UsersManual.html#gcc-extensions-not-implemented-yet + */ +#if !defined(__clang__) +typedef float _Decimal32 __attribute__((mode(SD))); +typedef float _Decimal64 __attribute__((mode(DD))); +typedef float _Decimal128 __attribute__((mode(TD))); +#endif +#endif + +__END_DECLS + +#endif /* !_MATH_H_ */ diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/mbusafecrt.h b/bitacross-worker/rust-sgx-sdk/common/inc/mbusafecrt.h new file mode 100644 index 0000000000..91d888b3f8 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/mbusafecrt.h @@ -0,0 +1,85 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +/*** +* mbusafecrt.h - public declarations for SafeCRT lib +* + +* +* Purpose: +* This file contains the public declarations SafeCRT +* functions ported to MacOS. These are the safe versions of +* functions standard functions banned by SWI +* + +****/ + +/* shields! */ + +#ifndef MBUSAFECRT_H +#define MBUSAFECRT_H +#include +#include +#include +typedef wchar_t WCHAR; + +#ifdef __cplusplus + extern "C" { +#endif + +extern errno_t strcat_s( char* ioDest, size_t inDestBufferSize, const char* inSrc ); +extern errno_t wcscat_s( WCHAR* ioDest, size_t inDestBufferSize, const WCHAR* inSrc ); + +extern errno_t strncat_s( char* ioDest, size_t inDestBufferSize, const char* inSrc, size_t inCount ); +extern errno_t wcsncat_s( WCHAR* ioDest, size_t inDestBufferSize, const WCHAR* inSrc, size_t inCount ); + +extern errno_t strcpy_s( char* outDest, size_t inDestBufferSize, const char* inSrc ); +extern errno_t wcscpy_s( WCHAR* outDest, size_t inDestBufferSize, const WCHAR* inSrc ); + +extern errno_t strncpy_s( char* outDest, size_t inDestBufferSize, const char* inSrc, size_t inCount ); +extern errno_t wcsncpy_s( WCHAR* outDest, size_t inDestBufferSize, const WCHAR* inSrc, size_t inCount ); + +extern char* strtok_s( char* inString, const char* inControl, char** ioContext ); +extern WCHAR* wcstok_s( WCHAR* inString, const WCHAR* inControl, WCHAR** ioContext ); + +extern size_t wcsnlen( const WCHAR* inString, size_t inMaxSize ); + +extern errno_t _itoa_s( int inValue, char* outBuffer, size_t inDestBufferSize, int inRadix ); +extern errno_t _itow_s( int inValue, WCHAR* outBuffer, size_t inDestBufferSize, int inRadix ); + +extern errno_t _ltoa_s( long inValue, char* outBuffer, size_t inDestBufferSize, int inRadix ); +extern errno_t _ltow_s( long inValue, WCHAR* outBuffer, size_t inDestBufferSize, int inRadix ); + +extern errno_t _ultoa_s( unsigned long inValue, char* outBuffer, size_t inDestBufferSize, int inRadix ); +extern errno_t _ultow_s( unsigned long inValue, WCHAR* outBuffer, size_t inDestBufferSize, int inRadix ); + +extern errno_t _i64toa_s( long long inValue, char* outBuffer, size_t inDestBufferSize, int inRadix ); +extern errno_t _i64tow_s( long long inValue, WCHAR* outBuffer, size_t inDestBufferSize, int inRadix ); + +extern errno_t _ui64toa_s( unsigned long long inValue, char* outBuffer, size_t inDestBufferSize, int inRadix ); +extern errno_t _ui64tow_s( unsigned long long inValue, WCHAR* outBuffer, size_t inDestBufferSize, int inRadix ); + +extern int sprintf_s( char *string, size_t sizeInBytes, const char *format, ... ); +extern int swprintf_s( WCHAR *string, size_t sizeInWords, const WCHAR *format, ... ); + +extern int _snprintf_s( char *string, size_t sizeInBytes, size_t count, const char *format, ... ); +extern int _snwprintf_s( WCHAR *string, size_t sizeInWords, size_t count, const WCHAR *format, ... ); + +extern int _vsprintf_s( char* string, size_t sizeInBytes, const char* format, va_list arglist ); +extern int _vsnprintf_s( char* string, size_t sizeInBytes, size_t count, const char* format, va_list arglist ); + +extern int _vswprintf_s( WCHAR* string, size_t sizeInWords, const WCHAR* format, va_list arglist ); +extern int _vsnwprintf_s( WCHAR* string, size_t sizeInWords, size_t count, const WCHAR* format, va_list arglist ); + +extern errno_t memcpy_s( void * dst, size_t sizeInBytes, const void * src, size_t count ); +extern errno_t memcpy_verw_s( void * dst, size_t sizeInBytes, const void * src, size_t count ); +extern errno_t memmove_s( void * dst, size_t sizeInBytes, const void * src, size_t count ); +extern errno_t memmove_verw_s( void * dst, size_t sizeInBytes, const void * src, size_t count ); + +#ifdef __cplusplus + } +#endif + +#endif /* MBUSAFECRT_H */ diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/netdb.h b/bitacross-worker/rust-sgx-sdk/common/inc/netdb.h new file mode 100644 index 0000000000..264f90ff39 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/netdb.h @@ -0,0 +1,41 @@ +// +// Copyright © 2005-2020 Rich Felker, et al. +// Licensed under the MIT license.s +// + +/* Copyright © 2005-2020 Rich Felker, et al. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef _NETDB_H +#define _NETDB_H + +struct addrinfo { + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + socklen_t ai_addrlen; + struct sockaddr *ai_addr; + char *ai_canonname; + struct addrinfo *ai_next; +}; + +#endif diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/poll.h b/bitacross-worker/rust-sgx-sdk/common/inc/poll.h new file mode 100644 index 0000000000..fc786fc279 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/poll.h @@ -0,0 +1,38 @@ +// +// Copyright © 2005-2020 Rich Felker, et al. +// Licensed under the MIT license. +// + +/* Copyright © 2005-2020 Rich Felker, et al. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef _POLL_H_ +#define _POLL_H_ + +typedef unsigned long nfds_t; + +struct pollfd { + int fd; + short int events; + short int revents; +}; + +#endif diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/pthread.h b/bitacross-worker/rust-sgx-sdk/common/inc/pthread.h new file mode 100644 index 0000000000..e79668ffd6 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/pthread.h @@ -0,0 +1,34 @@ +#ifndef _SYS_THREAD_H_ +#define _SYS_THREAD_H_ + +/* Thread identifiers. The structure of the attribute type is not + exposed on purpose. */ +typedef unsigned long int pthread_t; + +#if defined __x86_64__ && !defined __ILP32__ +# define __WORDSIZE 64 +#else +# define __WORDSIZE 32 +#define __WORDSIZE32_SIZE_ULONG 0 +#define __WORDSIZE32_PTRDIFF_LONG 0 +#endif + +#ifdef __x86_64__ +# if __WORDSIZE == 64 +# define __SIZEOF_PTHREAD_ATTR_T 56 +# else +# define __SIZEOF_PTHREAD_ATTR_T 32 +#endif + +union pthread_attr_t +{ + char __size[__SIZEOF_PTHREAD_ATTR_T]; + long int __align; +}; +#ifndef __have_pthread_attr_t +typedef union pthread_attr_t pthread_attr_t; +# define __have_pthread_attr_t 1 +#endif + +#endif +#endif diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/pwd.h b/bitacross-worker/rust-sgx-sdk/common/inc/pwd.h new file mode 100644 index 0000000000..a45b145a94 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/pwd.h @@ -0,0 +1,40 @@ +// +// Copyright © 2005-2020 Rich Felker, et al. +// Licensed under the MIT license. +// + +/* Copyright © 2005-2020 Rich Felker, et al. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef _PWD_H +#define _PWD_H + +struct passwd { + char *pw_name; + char *pw_passwd; + __uid_t pw_uid; + __gid_t pw_gid; + char *pw_gecos; + char *pw_dir; + char *pw_shell; +}; + +#endif diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/sched.h b/bitacross-worker/rust-sgx-sdk/common/inc/sched.h new file mode 100644 index 0000000000..4d237c4044 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/sched.h @@ -0,0 +1,62 @@ +// +// Copyright © 2005-2020 Rich Felker, et al. +// Licensed under the MIT license.s +// + +/* Copyright © 2005-2020 Rich Felker, et al. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef _SCHED_H +#define _SCHED_H +#include + +typedef struct { + unsigned long __bits[128/sizeof(long)]; +} cpu_set_t; + +#define __CPU_op_S(i, size, set, op) ( (i)/8U >= (size) ? 0 : \ + (((unsigned long *)(set))[(i)/8/sizeof(long)] op (1UL<<((i)%(8*sizeof(long))))) ) + +#define CPU_SET_S(i, size, set) __CPU_op_S(i, size, set, |=) +#define CPU_CLR_S(i, size, set) __CPU_op_S(i, size, set, &=~) +#define CPU_ISSET_S(i, size, set) __CPU_op_S(i, size, set, &) + +#define __CPU_op_func_S(func, op) \ +static __inline void __CPU_##func##_S(size_t __size, cpu_set_t *__dest, \ + const cpu_set_t *__src1, const cpu_set_t *__src2) \ +{ \ + size_t __i; \ + for (__i=0; __i<__size/sizeof(long); __i++) \ + ((unsigned long *)__dest)[__i] = ((unsigned long *)__src1)[__i] \ + op ((unsigned long *)__src2)[__i] ; \ +} + +__CPU_op_func_S(AND, &) +__CPU_op_func_S(OR, |) +__CPU_op_func_S(XOR, ^) + +#define CPU_AND_S(a,b,c,d) __CPU_AND_S(a,b,c,d) +#define CPU_OR_S(a,b,c,d) __CPU_OR_S(a,b,c,d) +#define CPU_XOR_S(a,b,c,d) __CPU_XOR_S(a,b,c,d) + +typedef __pid_t pid_t; + +#endif diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/setjmp.h b/bitacross-worker/rust-sgx-sdk/common/inc/setjmp.h new file mode 100644 index 0000000000..752f0cf763 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/setjmp.h @@ -0,0 +1,65 @@ +/* $NetBSD: setjmp.h,v 1.26 2011/11/05 09:27:06 joerg Exp $ */ + +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)setjmp.h 8.2 (Berkeley) 1/21/94 + */ + +#ifndef _SETJMP_H_ +#define _SETJMP_H_ + +#ifndef _JB_ATTRIBUTES +#define _JB_ATTRIBUTES /**/ +#else +#endif +#ifndef _BSD_JBSLOT_T_ +#define _BSD_JBSLOT_T_ long +#endif + +#define _JBLEN 8 + +typedef _BSD_JBSLOT_T_ jmp_buf[_JBLEN] _JB_ATTRIBUTES; + +#include +#define __returns_twice __attribute__((__returns_twice__)) +#define __dead + + +__BEGIN_DECLS +int setjmp(jmp_buf) __returns_twice; +void longjmp(jmp_buf, int) __dead; +__END_DECLS + +#endif /* !_SETJMP_H_ */ + diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/signal.h b/bitacross-worker/rust-sgx-sdk/common/inc/signal.h new file mode 100644 index 0000000000..c0da74f456 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/signal.h @@ -0,0 +1,104 @@ +// +// Copyright © 2005-2020 Rich Felker, et al. +// Licensed under the MIT license.s +// + +/* Copyright © 2005-2020 Rich Felker, et al. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef _SIGNAL_H +#define _SIGNAL_H + +#include + +typedef struct { + unsigned long _bits[128/sizeof(long)]; +} __sigset_t; + +typedef __sigset_t sigset_t; + +union sigval { + int sival_int; + void *sival_ptr; +}; + +typedef struct { + int si_signo; + int si_errno; + int si_code; + union { + char __pad[128 - 2*sizeof(int) - sizeof(long)]; + struct { + union { + struct { + __pid_t si_pid; + __uid_t si_uid; + } __piduid; + struct { + int si_timerid; + int si_overrun; + } __timer; + } __first; + union { + union sigval si_value; + struct { + int si_status; + __clock_t si_utime, si_stime; + } __sigchld; + } __second; + } __si_common; + struct { + void *si_addr; + short si_addr_lsb; + union { + struct { + void *si_lower; + void *si_upper; + } __addr_bnd; + unsigned si_pkey; + } __first; + } __sigfault; + struct { + long si_band; + int si_fd; + } __sigpoll; + struct { + void *si_call_addr; + int si_syscall; + unsigned si_arch; + } __sigsys; + } __si_fields; +} siginfo_t; + +struct sigaction { + union { + void (*sa_handler) (int); + void (*sa_sigaction) (int, siginfo_t *, void *); + } __sa_handler; + __sigset_t sa_mask; + int sa_flags; + void (*sa_restorer) (void); +}; + +#define sa_handler __sa_handler.sa_handler +#define sa_sigaction __sa_handler.sa_sigaction + +#endif diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/stdalign.h b/bitacross-worker/rust-sgx-sdk/common/inc/stdalign.h new file mode 100644 index 0000000000..93b8f6016e --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/stdalign.h @@ -0,0 +1,15 @@ +#ifndef _STDALIGN_H +#define _STDALIGN_H +#ifndef __cplusplus +/* this whole header only works in C11 or with compiler extensions */ +#if __STDC_VERSION__ < 201112L && defined( __GNUC__) +#define _Alignas(t) __attribute__((__aligned__(t))) +#define _Alignof(t) __alignof__(t) +#endif +#define alignas _Alignas +#define alignof _Alignof +#endif +#define __alignas_is_defined 1 +#define __alignof_is_defined 1 +#endif + diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/stdarg.h b/bitacross-worker/rust-sgx-sdk/common/inc/stdarg.h new file mode 100644 index 0000000000..b2a5d36e82 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/stdarg.h @@ -0,0 +1,48 @@ +/* $OpenBSD: stdarg.h,v 1.14 2010/12/30 05:01:36 tedu Exp $ */ +/* $NetBSD: stdarg.h,v 1.12 1995/12/25 23:15:31 mycroft Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)stdarg.h 8.1 (Berkeley) 6/10/93 + */ + +#ifndef _STDARG_H_ +#define _STDARG_H_ + +#include +#include + +typedef __va_list va_list; + +#define va_start(ap, last) __builtin_va_start((ap), last) +#define va_end __builtin_va_end +#define va_arg __builtin_va_arg +#define va_copy(dst, src) __builtin_va_copy((dst),(src)) + +#endif /* !_STDARG_H_ */ diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/stdbool.h b/bitacross-worker/rust-sgx-sdk/common/inc/stdbool.h new file mode 100644 index 0000000000..86b866d5d7 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/stdbool.h @@ -0,0 +1,44 @@ +/* $OpenBSD: stdbool.h,v 1.5 2010/07/24 22:17:03 guenther Exp $ */ + +/* + * Written by Marc Espie, September 25, 1999 + * Public domain. + */ + +#ifndef _STDBOOL_H_ +#define _STDBOOL_H_ + +#ifndef __cplusplus + +#ifndef __GNUC__ +/* Support for _C99: type _Bool is already built-in. */ +/* `_Bool' type must promote to `int' or `unsigned int'. */ +typedef enum { + false = 0, + true = 1 +} _Bool; + +/* And those constants must also be available as macros. */ +# define false false +# define true true +#else /* __GNUC__ */ +# define false 0 +# define true 1 +#endif + +/* User visible type `bool' is provided as a macro which may be redefined */ +#define bool _Bool + +#else /* __cplusplus */ + +# define _Bool bool +# define bool bool +# define false false +# define true true + +#endif + +/* Inform that everything is fine */ +#define __bool_true_false_are_defined 1 + +#endif /* _STDBOOL_H_ */ diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/stddef.h b/bitacross-worker/rust-sgx-sdk/common/inc/stddef.h new file mode 100644 index 0000000000..62d653029d --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/stddef.h @@ -0,0 +1,70 @@ +/* $OpenBSD: stddef.h,v 1.10 2009/09/22 21:40:02 jsg Exp $ */ +/* $NetBSD: stddef.h,v 1.4 1994/10/26 00:56:26 cgd Exp $ */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)stddef.h 5.5 (Berkeley) 4/3/91 + */ + +#ifndef _STDDEF_H_ +#define _STDDEF_H_ + +#include +#include + +#ifndef _PTRDIFF_T_DEFINED_ +#define _PTRDIFF_T_DEFINED_ +typedef __ptrdiff_t ptrdiff_t; +#endif + +#ifndef _SIZE_T_DEFINED_ +#define _SIZE_T_DEFINED_ +typedef __size_t size_t; +#endif + +#if !defined(_WCHAR_T_DEFINED_) && !defined(__cplusplus) +#define _WCHAR_T_DEFINED_ +#ifndef __WCHAR_TYPE__ +#define __WCHAR_TYPE__ int +#endif +typedef __WCHAR_TYPE__ wchar_t; +#endif + +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else +#define NULL ((void *)0) +#endif +#endif + +#define offsetof(type, member) __builtin_offsetof (type, member) + +#endif /* _STDDEF_H_ */ + diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/stdint.h b/bitacross-worker/rust-sgx-sdk/common/inc/stdint.h new file mode 100644 index 0000000000..e574484062 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/stdint.h @@ -0,0 +1,24 @@ +/* $OpenBSD: stdint.h,v 1.4 2006/12/10 22:17:55 deraadt Exp $ */ + +/* + * Copyright (c) 1997, 2005 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _STDINT_H_ +#define _STDINT_H_ + +#include + +#endif /* _STDINT_H_ */ diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/stdio.h b/bitacross-worker/rust-sgx-sdk/common/inc/stdio.h new file mode 100644 index 0000000000..92d01a0d9e --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/stdio.h @@ -0,0 +1,95 @@ +/* $OpenBSD: stdio.h,v 1.38 2009/11/09 00:18:27 kurt Exp $ */ +/* $NetBSD: stdio.h,v 1.18 1996/04/25 18:29:21 jtc Exp $ */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)stdio.h 5.17 (Berkeley) 6/3/91 + */ + +#ifndef _STDIO_H_ +#define _STDIO_H_ + +#include +#include + +#include + +#ifndef _SIZE_T_DEFINED_ +typedef __size_t size_t; +#define _SIZE_T_DEFINED_ +#endif + +#ifndef NULL +# ifdef __cplusplus +# define NULL 0 +# else +# define NULL ((void *)0) +# endif +#endif + +# define BUFSIZ 8192 + +#define EOF (-1) + +__BEGIN_DECLS + +int _TLIBC_CDECL_ snprintf(char *, size_t, const char *, ...) _GCC_PRINTF_FORMAT_(3, 4); +int _TLIBC_CDECL_ vsnprintf(char *, size_t, const char *, __va_list) _GCC_PRINTF_FORMAT_(3, 0); + +/* + * Deprecated definitions. + */ +#if 0 /* No FILE */ +_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, fprintf, FILE *, const char *, ...); +_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, putc, int, FILE *); +_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, fputc, int, FILE *); +_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, fputs, const char *, FILE *); +_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, fscanf, FILE *, const char *, ...); +_TLIBC_DEPRECATED_FUNCTION_(size_t _TLIBC_CDECL_, fwrite, const void *, size_t, size_t, FILE *); +_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, printf, const char *, ...); +_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, putchar, int); +_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, puts, const char *); +_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, scanf, const char *, ...); +_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, sprintf, char *, const char *, ...); +_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, sscanf, const char *, const char *, ...); +_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, vfprintf, FILE *, const char *, __va_list); +_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, vfscanf, FILE *, const char *, __va_list); +_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, vprintf, const char *, __va_list); +_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, vscanf, const char *, __va_list); +_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, vsprintf, char *, const char *, __va_list); +_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, vsscanf, const char *, const char *, __va_list); +#endif + +__END_DECLS + + +#endif /* !_STDIO_H_ */ diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/stdlib.h b/bitacross-worker/rust-sgx-sdk/common/inc/stdlib.h new file mode 100644 index 0000000000..8128e0d56d --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/stdlib.h @@ -0,0 +1,159 @@ +/* $OpenBSD: stdlib.h,v 1.47 2010/05/18 22:24:55 tedu Exp $ */ +/* $NetBSD: stdlib.h,v 1.25 1995/12/27 21:19:08 jtc Exp $ */ + +/*- +* Copyright (c) 1990 The Regents of the University of California. +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* 3. Neither the name of the University nor the names of its contributors +* may be used to endorse or promote products derived from this software +* without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +* SUCH DAMAGE. +* +* @(#)stdlib.h 5.13 (Berkeley) 6/4/91 +*/ + +#ifndef _STDLIB_H_ +#define _STDLIB_H_ + +#include +#include + +#ifndef _SIZE_T_DEFINED_ +#define _SIZE_T_DEFINED_ +typedef __size_t size_t; +#endif + +#if !defined(_WCHAR_T_DEFINED_) && !defined(__cplusplus) +#define _WCHAR_T_DEFINED_ +#ifndef __WCHAR_TYPE__ +#define __WCHAR_TYPE__ int +#endif +typedef __WCHAR_TYPE__ wchar_t; +#endif + +#ifndef _DIV_T_DEFINED +typedef struct { + int quot; /* quotient */ + int rem; /* remainder */ +} div_t; + +typedef struct { + long quot; /* quotient */ + long rem; /* remainder */ +} ldiv_t; + +typedef struct { + long long quot; /* quotient */ + long long rem; /* remainder */ +} lldiv_t; +#define _DIV_T_DEFINED +#endif + +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else +#define NULL ((void *)0) +#endif +#endif + +#define EXIT_FAILURE 1 +#define EXIT_SUCCESS 0 + +#define RAND_MAX 0x7fffffff +#define MB_CUR_MAX 1 + +__BEGIN_DECLS + +_TLIBC_NORETURN_ void _TLIBC_CDECL_ abort(void); +int _TLIBC_CDECL_ atexit(void (*)(void)); +int _TLIBC_CDECL_ abs(int); +double _TLIBC_CDECL_ atof(const char *); +int _TLIBC_CDECL_ atoi(const char *); +long _TLIBC_CDECL_ atol(const char *); +void * _TLIBC_CDECL_ bsearch(const void *, const void *, size_t, size_t, int (*)(const void *, const void *)); +void * _TLIBC_CDECL_ calloc(size_t, size_t); +div_t _TLIBC_CDECL_ div(int, int); +void _TLIBC_CDECL_ free(void *); +long _TLIBC_CDECL_ labs(long); +ldiv_t _TLIBC_CDECL_ ldiv(long, long); +void * _TLIBC_CDECL_ malloc(size_t); +void * _TLIBC_CDECL_ memalign(size_t, size_t); +#ifndef __cplusplus +int _TLIBC_CDECL_ posix_memalign(void **, size_t, size_t); +#else +int _TLIBC_CDECL_ posix_memalign(void **, size_t, size_t) throw (); +#endif +void * _TLIBC_CDECL_ aligned_alloc(size_t, size_t); +void _TLIBC_CDECL_ qsort(void *, size_t, size_t, int (*)(const void *, const void *)); +void * _TLIBC_CDECL_ realloc(void *, size_t); +double _TLIBC_CDECL_ strtod(const char *, char **); +long _TLIBC_CDECL_ strtol(const char *, char **, int); +float _TLIBC_CDECL_ strtof(const char *, char **); + +long long + _TLIBC_CDECL_ atoll(const char *); +long long + _TLIBC_CDECL_ llabs(long long); +lldiv_t + _TLIBC_CDECL_ lldiv(long long, long long); +long long + _TLIBC_CDECL_ strtoll(const char *, char **, int); +unsigned long + _TLIBC_CDECL_ strtoul(const char *, char **, int); +long double + _TLIBC_CDECL_ strtold(const char *, char **); +unsigned long long + _TLIBC_CDECL_ strtoull(const char *, char **, int); + +int _TLIBC_CDECL_ mblen(const char *, size_t); +size_t _TLIBC_CDECL_ mbstowcs(wchar_t *, const char *, size_t); +int _TLIBC_CDECL_ wctomb(char *, wchar_t); +int _TLIBC_CDECL_ mbtowc(wchar_t *, const char *, size_t); +size_t _TLIBC_CDECL_ wcstombs(char *, const wchar_t *, size_t); + + +/* + * Deprecated C99. + */ +_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, atexit, void (_TLIBC_CDECL_ *)(void)); +_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, rand, void); +_TLIBC_DEPRECATED_FUNCTION_(void _TLIBC_CDECL_, srand, unsigned); +_TLIBC_DEPRECATED_FUNCTION_(void _TLIBC_CDECL_, exit, int); +_TLIBC_DEPRECATED_FUNCTION_(void _TLIBC_CDECL_, _Exit, int); +_TLIBC_DEPRECATED_FUNCTION_(char * _TLIBC_CDECL_, getenv, const char *); +_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, system, const char *); + +/* + * Non-C99 Functions. + */ +void * _TLIBC_CDECL_ alloca(size_t); + +/* + * Deprecated Non-C99. + */ +//_TLIBC_DEPRECATED_FUNCTION_(void _TLIBC_CDECL_, _exit, int); + +__END_DECLS + +#endif /* !_STDLIB_H_ */ diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/string.h b/bitacross-worker/rust-sgx-sdk/common/inc/string.h new file mode 100644 index 0000000000..00a89fde77 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/string.h @@ -0,0 +1,130 @@ +/* $OpenBSD: string.h,v 1.20 2010/09/24 13:33:00 matthew Exp $ */ +/* $NetBSD: string.h,v 1.6 1994/10/26 00:56:30 cgd Exp $ */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)string.h 5.10 (Berkeley) 3/9/91 + */ + +#ifndef _STRING_H_ +#define _STRING_H_ + +#include +#include + +#ifndef _SIZE_T_DEFINED_ +typedef __size_t size_t; +#define _SIZE_T_DEFINED_ +#endif + +#ifndef _ERRNO_T_DEFINED +#define _ERRNO_T_DEFINED +typedef int errno_t; +#endif + +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else +#define NULL ((void *)0) +#endif +#endif + +__BEGIN_DECLS + +void * _TLIBC_CDECL_ memchr(const void *, int, size_t); +int _TLIBC_CDECL_ memcmp(const void *, const void *, size_t); +void * _TLIBC_CDECL_ memcpy_nochecks(void *, const void *, size_t); +void * _TLIBC_CDECL_ memcpy(void *, const void *, size_t); +void * _TLIBC_CDECL_ memcpy_verw(void *, const void *, size_t); +void * _TLIBC_CDECL_ memmove(void *, const void *, size_t); +void * _TLIBC_CDECL_ memmove_verw(void *, const void *, size_t); +void * _TLIBC_CDECL_ memset(void *, int, size_t); +void * _TLIBC_CDECL_ memset_verw(void *, int, size_t); +char * _TLIBC_CDECL_ strchr(const char *, int); +int _TLIBC_CDECL_ strcmp(const char *, const char *); +int _TLIBC_CDECL_ strcoll(const char *, const char *); +size_t _TLIBC_CDECL_ strcspn(const char *, const char *); +char * _TLIBC_CDECL_ strerror(int); +size_t _TLIBC_CDECL_ strlen(const char *); +char * _TLIBC_CDECL_ strncat(char *, const char *, size_t); +int _TLIBC_CDECL_ strncmp(const char *, const char *, size_t); +char * _TLIBC_CDECL_ strncpy(char *, const char *, size_t); +char * _TLIBC_CDECL_ strpbrk(const char *, const char *); +char * _TLIBC_CDECL_ strrchr(const char *, int); +size_t _TLIBC_CDECL_ strspn(const char *, const char *); +char * _TLIBC_CDECL_ strstr(const char *, const char *); +char * _TLIBC_CDECL_ strtok(char *, const char *); +size_t _TLIBC_CDECL_ strxfrm(char *, const char *, size_t); +size_t _TLIBC_CDECL_ strlcpy(char *, const char *, size_t); +errno_t _TLIBC_CDECL_ memset_s(void *s, size_t smax, int c, size_t n); +errno_t _TLIBC_CDECL_ memset_verw_s(void *s, size_t smax, int c, size_t n); + +/* + * Deprecated C99. + */ +_TLIBC_DEPRECATED_FUNCTION_(char * _TLIBC_CDECL_, strcat, char *, const char *); +_TLIBC_DEPRECATED_FUNCTION_(char * _TLIBC_CDECL_, strcpy, char *, const char *); + +/* + * Common used non-C99 functions. + */ +char * _TLIBC_CDECL_ strndup(const char *, size_t); +size_t _TLIBC_CDECL_ strnlen(const char *, size_t); +int _TLIBC_CDECL_ consttime_memequal(const void *b1, const void *b2, size_t len); + +/* + * Non-C99 + */ +int _TLIBC_CDECL_ bcmp(const void *, const void *, size_t); +void _TLIBC_CDECL_ bcopy(const void *, void *, size_t); +void _TLIBC_CDECL_ bzero(void *, size_t); +char * _TLIBC_CDECL_ index(const char *, int); +void * _TLIBC_CDECL_ mempcpy(void *, const void *, size_t); +char * _TLIBC_CDECL_ rindex(const char *, int); +char * _TLIBC_CDECL_ stpncpy(char *dest, const char *src, size_t n); +int _TLIBC_CDECL_ strcasecmp(const char *, const char *); +int _TLIBC_CDECL_ strncasecmp(const char *, const char *, size_t); + +int _TLIBC_CDECL_ ffs(int); +int _TLIBC_CDECL_ ffsl(long int); +int _TLIBC_CDECL_ ffsll(long long int); + +char * _TLIBC_CDECL_ strtok_r(char *, const char *, char **); +int _TLIBC_CDECL_ strerror_r(int, char *, size_t); + +/* + * Deprecated Non-C99. + */ +_TLIBC_DEPRECATED_FUNCTION_(char * _TLIBC_CDECL_, strdup, const char *); +_TLIBC_DEPRECATED_FUNCTION_(char * _TLIBC_CDECL_, stpcpy, char *dest, const char *src); + +__END_DECLS + +#endif /* _STRING_H_ */ diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/sys/_types.h b/bitacross-worker/rust-sgx-sdk/common/inc/sys/_types.h new file mode 100644 index 0000000000..5dc6d5bbfb --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/sys/_types.h @@ -0,0 +1,168 @@ +/* $OpenBSD: _types.h,v 1.2 2008/03/16 19:42:57 otto Exp $ */ + +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)types.h 8.3 (Berkeley) 1/5/94 + */ + +#ifndef _SYS__TYPES_H_ +#define _SYS__TYPES_H_ + +#include +/* 7.18.1.1 Exact-width integer types */ +typedef signed char __int8_t; +typedef unsigned char __uint8_t; +typedef short __int16_t; +typedef unsigned short __uint16_t; +typedef int __int32_t; +typedef unsigned int __uint32_t; +#ifdef __x86_64__ +typedef long __int64_t; +typedef unsigned long __uint64_t; +#else +typedef long long __int64_t; +typedef unsigned long long __uint64_t; +#endif + +/* 7.18.1.2 Minimum-width integer types */ +typedef __int8_t __int_least8_t; +typedef __uint8_t __uint_least8_t; +typedef __int16_t __int_least16_t; +typedef __uint16_t __uint_least16_t; +typedef __int32_t __int_least32_t; +typedef __uint32_t __uint_least32_t; +typedef __int64_t __int_least64_t; +typedef __uint64_t __uint_least64_t; + +/* 7.18.1.3 Fastest minimum-width integer types */ +typedef __int8_t __int_fast8_t; +typedef __uint8_t __uint_fast8_t; +#ifdef __x86_64__ +/* Linux x86_64, from stdint.h */ +typedef long int __int_fast16_t; +typedef unsigned long int __uint_fast16_t; +typedef long int __int_fast32_t; +typedef unsigned long int __uint_fast32_t; +typedef long int __int_fast64_t; +typedef unsigned long int __uint_fast64_t; +#else +/* Android x86, and Linux x86 */ +typedef __int32_t __int_fast16_t; +typedef __uint32_t __uint_fast16_t; +typedef __int32_t __int_fast32_t; +typedef __uint32_t __uint_fast32_t; +typedef __int64_t __int_fast64_t; +typedef __uint64_t __uint_fast64_t; +#endif + +typedef long __off_t; +#ifdef __x86_64__ +typedef long int __off64_t; +#else +typedef long long int __off64_t; +#endif + +/* 7.18.1.4 Integer types capable of holding object pointers */ +#ifdef __i386__ +typedef __int32_t __intptr_t; +typedef __uint32_t __uintptr_t; +typedef __int32_t __ptrdiff_t; +/* Standard system types */ +typedef __uint32_t __size_t; +typedef __int32_t __ssize_t; +typedef long double __double_t; +typedef long double __float_t; +#else +typedef __int64_t __intptr_t; +typedef __uint64_t __uintptr_t; +typedef __int64_t __ptrdiff_t; + +/* Standard system types */ +typedef unsigned long __size_t; +typedef long __ssize_t; +typedef double __double_t; +typedef float __float_t; + +#endif /* !__i386__ */ + +typedef long __clock_t; + +typedef long __time_t; +typedef __builtin_va_list __va_list; +typedef unsigned int __wint_t; +/* wctype_t and wctrans_t are defined in wchar.h */ +typedef unsigned long int __wctype_t; +typedef int * __wctrans_t; + +/* + * mbstate_t is an opaque object to keep conversion state, during multibyte + * stream conversions. The content must not be referenced by user programs. + */ +/* For Linux, __mbstate_t is defined in wchar.h */ +typedef struct { + int __c; + union { + __wint_t __wc; + char __wcb[4]; + } __v; +} __mbstate_t; + +/* 7.18.1.5 Greatest-width integer types */ +typedef __int64_t __intmax_t; +typedef __uint64_t __uintmax_t; + + +typedef unsigned long int __ino_t; +typedef unsigned int __mode_t; +typedef unsigned int __uid_t; +typedef unsigned int __gid_t; +typedef long int __blksize_t; +typedef long int __blkcnt_t; + +#ifdef __x86_64__ +typedef unsigned long int __dev_t; +typedef long int __off64_t; +typedef unsigned long int __nlink_t; +typedef long int __blkcnt64_t; +typedef unsigned long int __ino64_t; +#else +typedef unsigned long long int __dev_t; +typedef long long int __off64_t; +typedef unsigned int __nlink_t; +typedef long long int __blkcnt64_t; +typedef unsigned long long int __ino64_t; +#endif + +typedef unsigned int __socklen_t; +typedef int __pid_t; +typedef long __cpu_mask; +#endif /* !_SYS__TYPES_H_ */ + + + diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/sys/cdefs.h b/bitacross-worker/rust-sgx-sdk/common/inc/sys/cdefs.h new file mode 100644 index 0000000000..71c3c1ce22 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/sys/cdefs.h @@ -0,0 +1,132 @@ +/* $OpenBSD: cdefs.h,v 1.34 2012/08/14 20:11:37 matthew Exp $ */ +/* $NetBSD: cdefs.h,v 1.16 1996/04/03 20:46:39 christos Exp $ */ + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Berkeley Software Design, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)cdefs.h 8.7 (Berkeley) 1/21/94 + */ + +#ifndef _SYS_CDEFS_H_ +#define _SYS_CDEFS_H_ + +/* Declaration field in C/C++ headers */ +#if defined(__cplusplus) +# define __BEGIN_DECLS extern "C" { +# define __END_DECLS } +#else +# define __BEGIN_DECLS +# define __END_DECLS +#endif + +#if defined(__STDC__) || defined(__cplusplus) +# define __CONCAT(x,y) x ## y +# define __STRING(x) #x +#else +# define __CONCAT(x,y) x/**/y +# define __STRING(x) "x" +#endif +/* + * Macro to test if we're using a specific version of gcc or later. + */ +#if defined __GNUC__ && defined __GNUC_MINOR_ +# define __GNUC_PREREQ__(ma, mi) \ + ((__GNUC__ > (ma)) || (__GNUC__ == (ma) && __GNUC_MINOR__ >= (mi))) +#else +# define __GNUC_PREREQ__(ma, mi) 0 +#endif + +/* Calling Convention: cdecl */ +#define _TLIBC_CDECL_ + +/* Thread Directive */ +#define _TLIBC_THREAD_ /* __thread */ + +/* Deprecated Warnings */ +#define _TLIBC_DEPRECATED_MSG(x) __STRING(x)" is deprecated in tlibc." +#define _TLIBC_DEPRECATED_(x) __attribute__((deprecated(_TLIBC_DEPRECATED_MSG(x)))) + +#ifndef _TLIBC_WARN_DEPRECATED_FUNCTIONS_ +# define _TLIBC_DEPRECATED_FUNCTION_(__ret, __func, ...) +#else +# define _TLIBC_DEPRECATED_FUNCTION_(__ret, __func, ...) \ + _TLIBC_DEPRECATED_(__func) \ + __ret __func(__VA_ARGS__) +#endif + +/* Static analysis for printf format strings. + * _MSC_PRINTF_FORMAT_: MSVC SAL annotation for specifying format strings. + * _GCC_PRINTF_FORMAT_(x, y): GCC declaring attribute for checking format strings. + * x - index of the format string. In C++ non-static method, index 1 is reseved for 'this'. + * y - index of first variadic agrument in '...'. + */ +#define _GCC_PRINTF_FORMAT_(x, y) __attribute__((__format__ (printf, x, y))) + +/* Attribute - noreturn */ +#define _TLIBC_NORETURN_ __attribute__ ((__noreturn__)) + +/* + * GNU C version 2.96 adds explicit branch prediction so that + * the CPU back-end can hint the processor and also so that + * code blocks can be reordered such that the predicted path + * sees a more linear flow, thus improving cache behavior, etc. + * + * The following two macros provide us with a way to utilize this + * compiler feature. Use __predict_true() if you expect the expression + * to evaluate to true, and __predict_false() if you expect the + * expression to evaluate to false. + * + * A few notes about usage: + * + * * Generally, __predict_false() error condition checks (unless + * you have some _strong_ reason to do otherwise, in which case + * document it), and/or __predict_true() `no-error' condition + * checks, assuming you want to optimize for the no-error case. + * + * * Other than that, if you don't know the likelihood of a test + * succeeding from empirical or other `hard' evidence, don't + * make predictions. + * + * * These are meant to be used in places that are run `a lot'. + * It is wasteful to make predictions in code that is run + * seldomly (e.g. at subsystem initialization time) as the + * basic block reordering that this affects can often generate + * larger code. + */ +#if defined(__GNUC__) && __GNUC_PREREQ__(2, 96) +#define __predict_true(exp) __builtin_expect(((exp) != 0), 1) +#define __predict_false(exp) __builtin_expect(((exp) != 0), 0) +#else +#define __predict_true(exp) ((exp) != 0) +#define __predict_false(exp) ((exp) != 0) +#endif + +#endif /* !_SYS_CDEFS_H_ */ diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/sys/endian.h b/bitacross-worker/rust-sgx-sdk/common/inc/sys/endian.h new file mode 100644 index 0000000000..1cd7b810c3 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/sys/endian.h @@ -0,0 +1,54 @@ +/* $OpenBSD: endian.h,v 1.18 2006/03/27 07:09:24 otto Exp $ */ + +/*- + * Copyright (c) 1997 Niklas Hallqvist. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Generic definitions for little- and big-endian systems. Other endianesses + * has to be dealt with in the specific machine/endian.h file for that port. + * + * This file is meant to be included from a little- or big-endian port's + * machine/endian.h after setting _BYTE_ORDER to either 1234 for little endian + * or 4321 for big.. + */ + +#ifndef _SYS_ENDIAN_H_ +#define _SYS_ENDIAN_H_ + +#define _LITTLE_ENDIAN 1234 +#define _BIG_ENDIAN 4321 +#define _PDP_ENDIAN 3412 +#define _BYTE_ORDER _LITTLE_ENDIAN + +#define LITTLE_ENDIAN _LITTLE_ENDIAN +#define BIG_ENDIAN _BIG_ENDIAN +#define PDP_ENDIAN _PDP_ENDIAN +#define BYTE_ORDER _BYTE_ORDER + +#define __BYTE_ORDER _BYTE_ORDER +#define __BIG_ENDIAN _BIG_ENDIAN +#define __LITTLE_ENDIAN _LITTLE_ENDIAN + +#endif /* _SYS_ENDIAN_H_ */ + diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/sys/epoll.h b/bitacross-worker/rust-sgx-sdk/common/inc/sys/epoll.h new file mode 100644 index 0000000000..958a4c4fb0 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/sys/epoll.h @@ -0,0 +1,42 @@ +// +// Copyright © 2005-2020 Rich Felker, et al. +// Licensed under the MIT license. +// + +/* Copyright © 2005-2020 Rich Felker, et al. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef _SYS_EPOLL_H +#define _SYS_EPOLL_H + +typedef union epoll_data { + void *ptr; + int fd; + uint32_t u32; + uint64_t u64; +} epoll_data_t; + +struct epoll_event { + uint32_t events; + epoll_data_t data; +} __attribute__ ((__packed__)); + +#endif diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/sys/fpu.h b/bitacross-worker/rust-sgx-sdk/common/inc/sys/fpu.h new file mode 100644 index 0000000000..4c218a91b6 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/sys/fpu.h @@ -0,0 +1,99 @@ +/* $OpenBSD: fpu.h,v 1.16 2018/10/07 22:43:06 guenther Exp $ */ +/* $NetBSD: fpu.h,v 1.1 2003/04/26 18:39:40 fvdl Exp $ */ + +#ifndef _MACHINE_FPU_H_ +#define _MACHINE_FPU_H_ + +#include + +/* + * If the CPU supports xsave/xrstor then we use them so that we can provide + * AVX support. Otherwise we require fxsave/fxrstor, as the SSE registers + * are part of the ABI for passing floating point values. + * While fxsave/fxrstor only required 16-byte alignment for the save area, + * xsave/xrstor requires the save area to have 64-byte alignment. + */ + +struct fxsave64 { + u_int16_t fx_fcw; + u_int16_t fx_fsw; + u_int8_t fx_ftw; + u_int8_t fx_unused1; + u_int16_t fx_fop; + u_int64_t fx_rip; + u_int64_t fx_rdp; + u_int32_t fx_mxcsr; + u_int32_t fx_mxcsr_mask; + u_int64_t fx_st[8][2]; /* 8 normal FP regs */ + u_int64_t fx_xmm[16][2]; /* 16 SSE2 registers */ + u_int8_t fx_unused3[96]; +} __packed; + +struct xstate_hdr { + uint64_t xstate_bv; + uint64_t xstate_xcomp_bv; + uint8_t xstate_rsrv0[0]; + uint8_t xstate_rsrv[40]; +} ___packed; + +struct savefpu { + struct fxsave64 fp_fxsave; /* see above */ + struct xstate_hdr fp_xstate; + u_int64_t fp_ymm[16][2]; + u_int16_t fp_ex_sw; /* saved status from last exception */ + u_int16_t fp_ex_tw; /* saved tag from last exception */ +}; + +/* + * The i387 defaults to Intel extended precision mode and round to nearest, + * with all exceptions masked. + */ +#define __INITIAL_NPXCW__ 0x037f +#define __INITIAL_MXCSR__ 0x1f80 +#define __INITIAL_MXCSR_MASK__ 0xffbf + +#ifdef _KERNEL +/* + * XXX + */ +struct trapframe; +struct cpu_info; + +extern size_t fpu_save_len; +extern uint32_t fpu_mxcsr_mask; +extern uint64_t xsave_mask; + +void fpuinit(struct cpu_info *); +int fputrap(int _type); +void fpusave(struct savefpu *); +void fpusavereset(struct savefpu *); +void fpu_kernel_enter(void); +void fpu_kernel_exit(void); + +int xrstor_user(struct savefpu *_addr, uint64_t _mask); +#define fpureset() \ + xrstor_user(&proc0.p_addr->u_pcb.pcb_savefpu, xsave_mask) +int xsetbv_user(uint32_t _reg, uint64_t _mask); + +#define fninit() __asm("fninit") +#define fwait() __asm("fwait") +/* should be fxsave64, but where we use this it doesn't matter */ +#define fxsave(addr) __asm("fxsave %0" : "=m" (*addr)) +#define ldmxcsr(addr) __asm("ldmxcsr %0" : : "m" (*addr)) +#define fldcw(addr) __asm("fldcw %0" : : "m" (*addr)) + +static inline void +xsave(struct savefpu *addr, uint64_t mask) +{ + uint32_t lo, hi; + + lo = mask; + hi = mask >> 32; + /* should be xsave64, but where we use this it doesn't matter */ + __asm volatile("xsave %0" : "=m" (*addr) : "a" (lo), "d" (hi) : + "memory"); +} + +#endif + +#endif /* _MACHINE_FPU_H_ */ diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/sys/ieee.h b/bitacross-worker/rust-sgx-sdk/common/inc/sys/ieee.h new file mode 100644 index 0000000000..47379b28ed --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/sys/ieee.h @@ -0,0 +1,170 @@ +/* $OpenBSD: ieee.h,v 1.2 2008/09/07 20:36:06 martynas Exp $ */ +/* $NetBSD: ieee.h,v 1.1 1996/09/30 16:34:25 ws Exp $ */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratory. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ieee.h 8.1 (Berkeley) 6/11/93 + */ + +/* + * ieee.h defines the machine-dependent layout of the machine's IEEE + * floating point. It does *not* define (yet?) any of the rounding + * mode bits, exceptions, and so forth. + */ + +/* + * Define the number of bits in each fraction and exponent. + * + * k k+1 + * Note that 1.0 x 2 == 0.1 x 2 and that denorms are represented + * + * (-exp_bias+1) + * as fractions that look like 0.fffff x 2 . This means that + * + * -126 + * the number 0.10000 x 2 , for instance, is the same as the normalized + * + * -127 -128 + * float 1.0 x 2 . Thus, to represent 2 , we need one leading zero + * + * -129 + * in the fraction; to represent 2 , we need two, and so on. This + * + * (-exp_bias-fracbits+1) + * implies that the smallest denormalized number is 2 + * + * for whichever format we are talking about: for single precision, for + * + * -126 -149 + * instance, we get .00000000000000000000001 x 2 , or 1.0 x 2 , and + * + * -149 == -127 - 23 + 1. + */ + +#include +#include + +#define SNG_EXPBITS 8 +#define SNG_FRACBITS 23 + +#define DBL_EXPBITS 11 +#define DBL_FRACHBITS 20 +#define DBL_FRACLBITS 32 +#define DBL_FRACBITS 52 + +#define EXT_EXPBITS 15 +#define EXT_FRACHBITS 32 +#define EXT_FRACLBITS 32 +#define EXT_FRACBITS 64 + +#define EXT_TO_ARRAY32(p, a) do { \ + (a)[0] = (uint32_t)(p)->ext_fracl; \ + (a)[1] = (uint32_t)(p)->ext_frach; \ +} while(0) + +struct ieee_single { + u_int sng_frac:23; + u_int sng_exp:8; + u_int sng_sign:1; +}; + +struct ieee_double { + u_int dbl_fracl; + u_int dbl_frach:20; + u_int dbl_exp:11; + u_int dbl_sign:1; +}; + +struct ieee_ext { + u_int ext_fracl; + u_int ext_frach; + u_int ext_exp:15; + u_int ext_sign:1; + u_int ext_padl:16; + u_int ext_padh; +}; + +/* + * Floats whose exponent is in [1..INFNAN) (of whatever type) are + * `normal'. Floats whose exponent is INFNAN are either Inf or NaN. + * Floats whose exponent is zero are either zero (iff all fraction + * bits are zero) or subnormal values. + * + * A NaN is a `signalling NaN' if its QUIETNAN bit is clear in its + * high fraction; if the bit is set, it is a `quiet NaN'. + */ +#define SNG_EXP_INFNAN 255 +#define DBL_EXP_INFNAN 2047 +#define EXT_EXP_INFNAN 32767 + +#if 0 +#define SNG_QUIETNAN (1 << 22) +#define DBL_QUIETNAN (1 << 19) +#define EXT_QUIETNAN (1 << 15) +#endif + +/* + * Exponent biases. + */ +#define SNG_EXP_BIAS 127 +#define DBL_EXP_BIAS 1023 +#define EXT_EXP_BIAS 16383 + +typedef int fp_except; +#define FP_X_INV 0x01 /* invalid operation exception */ +#define FP_X_DNML 0x02 /* denormalization exception */ +#define FP_X_DZ 0x04 /* divide-by-zero exception */ +#define FP_X_OFL 0x08 /* overflow exception */ +#define FP_X_UFL 0x10 /* underflow exception */ +#define FP_X_IMP 0x20 /* imprecise (loss of precision) */ + +typedef enum { + FP_RN=0, /* round to nearest representable number */ + FP_RM=1, /* round toward negative infinity */ + FP_RP=2, /* round toward positive infinity */ + FP_RZ=3 /* round to zero (truncate) */ +} fp_rnd; + +__BEGIN_DECLS +extern fp_rnd fpgetround(void); +extern fp_rnd fpsetround(fp_rnd); +extern fp_except fpgetmask(void); +extern fp_except fpsetmask(fp_except); +extern fp_except fpgetsticky(void); +extern fp_except fpsetsticky(fp_except); +__END_DECLS diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/sys/limits.h b/bitacross-worker/rust-sgx-sdk/common/inc/sys/limits.h new file mode 100644 index 0000000000..3d1f9673ad --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/sys/limits.h @@ -0,0 +1,77 @@ +/* $OpenBSD: limits.h,v 1.8 2009/11/27 19:54:35 guenther Exp $ */ +/* + * Copyright (c) 2002 Marc Espie. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD + * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _SYS_LIMITS_H_ +#define _SYS_LIMITS_H_ + +#include + +/* Common definitions for limits.h. */ + +#define CHAR_BIT 8 /* number of bits in a char */ + +#define SCHAR_MAX 0x7f /* max value for a signed char */ +#define SCHAR_MIN (-0x7f - 1) /* min value for a signed char */ + +#define UCHAR_MAX 0xff /* max value for an unsigned char */ +#ifdef __CHAR_UNSIGNED__ +# define CHAR_MIN 0 /* min value for a char */ +# define CHAR_MAX 0xff /* max value for a char */ +#else +# define CHAR_MAX 0x7f +# define CHAR_MIN (-0x7f-1) +#endif + +#define MB_LEN_MAX 1 /* Allow UTF-8 (RFC 3629) */ + +#define USHRT_MAX 0xffff /* max value for an unsigned short */ +#define SHRT_MAX 0x7fff /* max value for a short */ +#define SHRT_MIN (-0x7fff-1) /* min value for a short */ + +#define UINT_MAX 0xffffffffU /* max value for an unsigned int */ +#define INT_MAX 0x7fffffff /* max value for an int */ +#define INT_MIN (-0x7fffffff-1) /* min value for an int */ + +#ifdef __x86_64__ +# define ULONG_MAX 0xffffffffffffffffUL /* max value for unsigned long */ +# define LONG_MAX 0x7fffffffffffffffL /* max value for a signed long */ +# define LONG_MIN (-0x7fffffffffffffffL-1) /* min value for a signed long */ +#else +# define ULONG_MAX 0xffffffffUL /* max value for an unsigned long */ +# define LONG_MAX 0x7fffffffL /* max value for a long */ +# define LONG_MIN (-0x7fffffffL-1) /* min value for a long */ +#endif + +#define ULLONG_MAX 0xffffffffffffffffULL /* max value for unsigned long long */ +#define LLONG_MAX 0x7fffffffffffffffLL /* max value for a signed long long */ +#define LLONG_MIN (-0x7fffffffffffffffLL-1) /* min value for a signed long long */ + +#ifdef __x86_64__ +# define LONG_BIT 64 +#else +# define LONG_BIT 32 +#endif + +#endif /* !_SYS_LIMITS_H_ */ diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/sys/sockaddr.h b/bitacross-worker/rust-sgx-sdk/common/inc/sys/sockaddr.h new file mode 100644 index 0000000000..ba6811cbf7 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/sys/sockaddr.h @@ -0,0 +1,32 @@ +// +// Copyright © 2005-2020 Rich Felker, et al. +// Licensed under the MIT license. +// + +/* Copyright © 2005-2020 Rich Felker, et al. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef _SYS_SOCKADDR_H_ +#define _SYS_SOCKADDR_H_ + +typedef unsigned short int sa_family_t; + +#endif diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/sys/socket.h b/bitacross-worker/rust-sgx-sdk/common/inc/sys/socket.h new file mode 100644 index 0000000000..0b16699cc6 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/sys/socket.h @@ -0,0 +1,54 @@ +// +// Copyright © 2005-2020 Rich Felker, et al. +// Licensed under the MIT license. +// + +/* Copyright © 2005-2020 Rich Felker, et al. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef _SYS_SOCKET_H_ +#define _SYS_SOCKET_H_ + +#include +#include +#include + +typedef __socklen_t socklen_t; + +struct sockaddr { + sa_family_t sa_family; + char sa_data[14]; +}; + +struct msghdr { + void *msg_name; + socklen_t msg_namelen; + + struct iovec *msg_iov; + size_t msg_iovlen; + + void *msg_control; + size_t msg_controllen; + + int msg_flags; +}; + +#endif diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/sys/stat.h b/bitacross-worker/rust-sgx-sdk/common/inc/sys/stat.h new file mode 100644 index 0000000000..1cf090a7a1 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/sys/stat.h @@ -0,0 +1,127 @@ +// +// Copyright © 2005-2020 Rich Felker, et al. +// Licensed under the MIT license. +// + +/* Copyright © 2005-2020 Rich Felker, et al. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + + +#ifndef _SYS_STAT_H_ +#define _SYS_STAT_H_ + +#include +#include +#include + +typedef __dev_t dev_t; +typedef __ino_t ino_t; +typedef __ino64_t ino64_t; +typedef __mode_t mode_t; +typedef __nlink_t nlink_t; +typedef __uid_t uid_t; +typedef __gid_t gid_t; +typedef __blksize_t blksize_t; +typedef __blkcnt_t blkcnt_t; +typedef __blkcnt64_t blkcnt64_t; + +struct stat { + dev_t st_dev; + ino_t st_ino; + nlink_t st_nlink; + + mode_t st_mode; + uid_t st_uid; + gid_t st_gid; + unsigned int __pad0; + dev_t st_rdev; + off_t st_size; + blksize_t st_blksize; + blkcnt_t st_blocks; + + struct timespec st_atim; + struct timespec st_mtim; + struct timespec st_ctim; + long __unused[3]; +}; + +struct stat64 { + dev_t st_dev; + ino64_t st_ino; + nlink_t st_nlink; + + mode_t st_mode; + uid_t st_uid; + gid_t st_gid; + unsigned int __pad0; + dev_t st_rdev; + off_t st_size; + blksize_t st_blksize; + blkcnt64_t st_blocks; + + struct timespec st_atim; + struct timespec st_mtim; + struct timespec st_ctim; + long __unused[3]; +}; + +#define S_IFMT 0170000 + +#define S_IFDIR 0040000 +#define S_IFCHR 0020000 +#define S_IFBLK 0060000 +#define S_IFREG 0100000 +#define S_IFIFO 0010000 +#define S_IFLNK 0120000 +#define S_IFSOCK 0140000 + +#define S_TYPEISMQ(buf) 0 +#define S_TYPEISSEM(buf) 0 +#define S_TYPEISSHM(buf) 0 +#define S_TYPEISTMO(buf) 0 + +#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) +#define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) + +#ifndef S_IRUSR +#define S_ISUID 04000 +#define S_ISGID 02000 +#define S_ISVTX 01000 +#define S_IRUSR 0400 +#define S_IWUSR 0200 +#define S_IXUSR 0100 +#define S_IRWXU 0700 +#define S_IRGRP 0040 +#define S_IWGRP 0020 +#define S_IXGRP 0010 +#define S_IRWXG 0070 +#define S_IROTH 0004 +#define S_IWOTH 0002 +#define S_IXOTH 0001 +#define S_IRWXO 0007 +#endif + +#endif /* _SYS_STAT_H_ */ diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/sys/stdint.h b/bitacross-worker/rust-sgx-sdk/common/inc/sys/stdint.h new file mode 100644 index 0000000000..51599456d5 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/sys/stdint.h @@ -0,0 +1,260 @@ +/* $OpenBSD: stdint.h,v 1.4 2006/12/10 22:17:55 deraadt Exp $ */ + +/* + * Copyright (c) 1997, 2005 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _SYS_STDINT_H_ +#define _SYS_STDINT_H_ + +#include +#include + +/* 7.18.1.1 Exact-width integer types (also in sys/types.h) */ +#ifndef _INT8_T_DEFINED_ +#define _INT8_T_DEFINED_ +typedef __int8_t int8_t; +#endif + +#ifndef _UINT8_T_DEFINED_ +#define _UINT8_T_DEFINED_ +typedef __uint8_t uint8_t; +#endif + +#ifndef _INT16_T_DEFINED_ +#define _INT16_T_DEFINED_ +typedef __int16_t int16_t; +#endif + +#ifndef _UINT16_T_DEFINED_ +#define _UINT16_T_DEFINED_ +typedef __uint16_t uint16_t; +#endif + +#ifndef _INT32_T_DEFINED_ +#define _INT32_T_DEFINED_ +typedef __int32_t int32_t; +#endif + +#ifndef _UINT32_T_DEFINED_ +#define _UINT32_T_DEFINED_ +typedef __uint32_t uint32_t; +#endif + +#ifndef _INT64_T_DEFINED_ +#define _INT64_T_DEFINED_ +typedef __int64_t int64_t; +#endif + +#ifndef _UINT64_T_DEFINED_ +#define _UINT64_T_DEFINED_ +typedef __uint64_t uint64_t; +#endif + +/* 7.18.1.2 Minimum-width integer types */ +typedef __int_least8_t int_least8_t; +typedef __uint_least8_t uint_least8_t; +typedef __int_least16_t int_least16_t; +typedef __uint_least16_t uint_least16_t; +typedef __int_least32_t int_least32_t; +typedef __uint_least32_t uint_least32_t; +typedef __int_least64_t int_least64_t; +typedef __uint_least64_t uint_least64_t; + +/* 7.18.1.3 Fastest minimum-width integer types */ +typedef __int_fast8_t int_fast8_t; +typedef __uint_fast8_t uint_fast8_t; +typedef __int_fast16_t int_fast16_t; +typedef __uint_fast16_t uint_fast16_t; +typedef __int_fast32_t int_fast32_t; +typedef __uint_fast32_t uint_fast32_t; +typedef __int_fast64_t int_fast64_t; +typedef __uint_fast64_t uint_fast64_t; + +/* 7.18.1.4 Integer types capable of holding object pointers */ +#ifndef _INTPTR_T_DEFINED_ +#define _INTPTR_T_DEFINED_ +typedef __intptr_t intptr_t; +#endif + +#ifndef _UINTPTR_T_DEFINED_ +#define _UINTPTR_T_DEFINED_ +typedef __uintptr_t uintptr_t; +#endif + +/* 7.18.1.5 Greatest-width integer types */ +typedef __intmax_t intmax_t; +typedef __uintmax_t uintmax_t; + +//#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) +/* + * 7.18.2 Limits of specified-width integer types. + * + * The following object-like macros specify the minimum and maximum limits + * of integer types corresponding to the typedef names defined above. + */ + +/* 7.18.2.1 Limits of exact-width integer types */ +#define INT8_MIN (-0x7f - 1) +#define INT16_MIN (-0x7fff - 1) +#define INT32_MIN (-0x7fffffff - 1) +#ifdef __x86_64__ +#define INT64_MIN (-0x7fffffffffffffffL - 1) +#else +#define INT64_MIN (-0x7fffffffffffffffLL - 1) +#endif + +#define INT8_MAX 0x7f +#define INT16_MAX 0x7fff +#define INT32_MAX 0x7fffffff +#ifdef __x86_64__ +#define INT64_MAX 0x7fffffffffffffffL +#else +#define INT64_MAX 0x7fffffffffffffffLL +#endif + +#define UINT8_MAX 0xff +#define UINT16_MAX 0xffff +#define UINT32_MAX 0xffffffffU +#ifdef __x86_64__ +#define UINT64_MAX 0xffffffffffffffffUL +#else +#define UINT64_MAX 0xffffffffffffffffULL +#endif + +/* 7.18.2.2 Limits of minimum-width integer types */ +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST64_MIN INT64_MIN + +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MAX INT64_MAX + +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +/* 7.18.2.3 Limits of fastest minimum-width integer types */ +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST64_MIN INT64_MIN + +#define INT_FAST8_MAX INT8_MAX +#ifdef __x86_64__ +#define INT_FAST16_MAX INT64_MAX +#define INT_FAST32_MAX INT64_MAX +#else +#define INT_FAST16_MAX INT32_MAX +#define INT_FAST32_MAX INT32_MAX +#endif +#define INT_FAST64_MAX INT64_MAX + +#define UINT_FAST8_MAX UINT8_MAX +#ifdef __x86_64__ +#define UINT_FAST16_MAX UINT64_MAX +#define UINT_FAST32_MAX UINT64_MAX +#else +#define UINT_FAST16_MAX UINT32_MAX +#define UINT_FAST32_MAX UINT32_MAX +#endif +#define UINT_FAST64_MAX UINT64_MAX + +/* 7.18.2.4 Limits of integer types capable of holding object pointers */ +#ifdef __x86_64__ +#define INTPTR_MIN INT64_MIN +#define INTPTR_MAX INT64_MAX +#define UINTPTR_MAX UINT64_MAX +#else +#define INTPTR_MIN INT32_MIN +#define INTPTR_MAX INT32_MAX +#define UINTPTR_MAX UINT32_MAX +#endif + +/* 7.18.2.5 Limits of greatest-width integer types */ +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +/* + * 7.18.3 Limits of other integer types. + * + * The following object-like macros specify the minimum and maximum limits + * of integer types corresponding to types specified in other standard + * header files. + */ + +/* Limits of ptrdiff_t */ +#define PTRDIFF_MIN INTPTR_MIN +#define PTRDIFF_MAX INTPTR_MAX + +/* Limits of size_t (also in limits.h) */ +#ifndef SIZE_MAX +#define SIZE_MAX UINTPTR_MAX +#endif + +/* Limits of wchar_t */ +# ifdef __WCHAR_MAX__ +# define WCHAR_MAX __WCHAR_MAX__ +# else +# define WCHAR_MAX (2147483647) +# endif +# ifdef __WCHAR_MIN__ +# define WCHAR_MIN __WCHAR_MIN__ +# elif L'\0' - 1 > 0 +# define WCHAR_MIN L'\0' +# else +# define WCHAR_MIN (-WCHAR_MAX - 1) +# endif + +/* Limits of wint_t */ +# define WINT_MIN (0u) +# define WINT_MAX (4294967295u) + +//#endif /* __cplusplus || __STDC_LIMIT_MACROS */ + +//#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) +/* + * 7.18.4 Macros for integer constants. + * + * The following function-like macros expand to integer constants + * suitable for initializing objects that have integer types corresponding + * to types defined in . The argument in any instance of + * these macros shall be a decimal, octal, or hexadecimal constant with + * a value that does not exceed the limits for the corresponding type. + */ + +/* 7.18.4.1 Macros for minimum-width integer constants. */ +#define INT8_C(_c) (_c) +#define INT16_C(_c) (_c) +#define INT32_C(_c) (_c) +#define INT64_C(_c) __CONCAT(_c, LL) + +#define UINT8_C(_c) (_c) +#define UINT16_C(_c) (_c) +#define UINT32_C(_c) __CONCAT(_c, U) +#define UINT64_C(_c) __CONCAT(_c, ULL) + +/* 7.18.4.2 Macros for greatest-width integer constants. */ +#define INTMAX_C(_c) __CONCAT(_c, LL) +#define UINTMAX_C(_c) __CONCAT(_c, ULL) + +//#endif /* __cplusplus || __STDC_CONSTANT_MACROS */ + +#endif /* _SYS_STDINT_H_ */ diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/sys/struct_timespec.h b/bitacross-worker/rust-sgx-sdk/common/inc/sys/struct_timespec.h new file mode 100644 index 0000000000..bca02c8809 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/sys/struct_timespec.h @@ -0,0 +1,37 @@ +// +// Copyright © 2005-2020 Rich Felker, et al. +// Licensed under the MIT license. +// + +/* Copyright © 2005-2020 Rich Felker, et al. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef _SYS_TIMESPEC_H_ +#define _SYS_TIMESPEC_H_ + +#include + +struct timespec { + __time_t tv_sec; + long tv_nsec; +}; + +#endif diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/sys/types.h b/bitacross-worker/rust-sgx-sdk/common/inc/sys/types.h new file mode 100644 index 0000000000..b64f89df04 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/sys/types.h @@ -0,0 +1,129 @@ +/* $OpenBSD: types.h,v 1.31 2008/03/16 19:42:57 otto Exp $ */ +/* $NetBSD: types.h,v 1.29 1996/11/15 22:48:25 jtc Exp $ */ + +/*- + * Copyright (c) 1982, 1986, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)types.h 8.4 (Berkeley) 1/21/94 + */ + +#ifndef _SYS_TYPES_H_ +#define _SYS_TYPES_H_ + +#include +#include + +typedef unsigned char u_char; +typedef unsigned short u_short; +typedef unsigned int u_int; +typedef unsigned long u_long; + +typedef unsigned char unchar; /* Sys V compatibility */ +typedef unsigned short ushort; /* Sys V compatibility */ +typedef unsigned int uint; /* Sys V compatibility */ +typedef unsigned long ulong; /* Sys V compatibility */ + +#ifndef _INT8_T_DEFINED_ +#define _INT8_T_DEFINED_ +typedef __int8_t int8_t; +#endif + +#ifndef _UINT8_T_DEFINED_ +#define _UINT8_T_DEFINED_ +typedef __uint8_t uint8_t; +#endif + +#ifndef _INT16_T_DEFINED_ +#define _INT16_T_DEFINED_ +typedef __int16_t int16_t; +#endif + +#ifndef _UINT16_T_DEFINED_ +#define _UINT16_T_DEFINED_ +typedef __uint16_t uint16_t; +#endif + +#ifndef _INT32_T_DEFINED_ +#define _INT32_T_DEFINED_ +typedef __int32_t int32_t; +#endif + +#ifndef _UINT32_T_DEFINED_ +#define _UINT32_T_DEFINED_ +typedef __uint32_t uint32_t; +#endif + +#ifndef _INT64_T_DEFINED_ +#define _INT64_T_DEFINED_ +typedef __int64_t int64_t; +#endif + +#ifndef _UINT64_T_DEFINED_ +#define _UINT64_T_DEFINED_ +typedef __uint64_t uint64_t; +#endif + +#ifndef _INTPTR_T_DEFINED_ +#define _INTPTR_T_DEFINED_ +typedef __intptr_t intptr_t; +#endif + +#ifndef _UINTPTR_T_DEFINED_ +#define _UINTPTR_T_DEFINED_ +typedef __uintptr_t uintptr_t; +#endif + +/* BSD-style unsigned bits types */ +typedef __uint8_t u_int8_t; +typedef __uint16_t u_int16_t; +typedef __uint32_t u_int32_t; +typedef __uint64_t u_int64_t; + + +#ifndef _SIZE_T_DEFINED_ +#define _SIZE_T_DEFINED_ +typedef __size_t size_t; +#endif + +#ifndef _SSIZE_T_DEFINED_ +#define _SSIZE_T_DEFINED_ +typedef __ssize_t ssize_t; +#endif + +#ifndef _OFF_T_DEFINED_ +#define _OFF_T_DEFINED_ +typedef __off_t off_t; +typedef __off64_t off64_t; +#endif + +#endif /* !_SYS_TYPES_H_ */ diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/sys/uio.h b/bitacross-worker/rust-sgx-sdk/common/inc/sys/uio.h new file mode 100644 index 0000000000..2544f06a7d --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/sys/uio.h @@ -0,0 +1,35 @@ +// +// Copyright © 2005-2020 Rich Felker, et al. +// Licensed under the MIT license. +// + +/* Copyright © 2005-2020 Rich Felker, et al. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef _SYS_UIO_H_ +#define _SYS_UIO_H_ + +struct iovec { + void *iov_base; + size_t iov_len; +}; + +#endif diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/time.h b/bitacross-worker/rust-sgx-sdk/common/inc/time.h new file mode 100644 index 0000000000..01cfd6e4e9 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/time.h @@ -0,0 +1,105 @@ +/* $OpenBSD: time.h,v 1.18 2006/01/06 18:53:04 millert Exp $ */ +/* $NetBSD: time.h,v 1.9 1994/10/26 00:56:35 cgd Exp $ */ + +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)time.h 5.12 (Berkeley) 3/9/91 + */ + +#ifndef _TIME_H_ +#define _TIME_H_ + +#include +#include +#include + +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else +#define NULL ((void *)0) +#endif +#endif + +#if !defined (_CLOCK_T_DEFINED_) && !defined (_CLOCK_T_DEFINED) +#define _CLOCK_T_DEFINED_ +#define _CLOCK_T_DEFINED +typedef __clock_t clock_t; +#endif + +#if !defined (_TIME_T_DEFINED_) && !defined (_TIME_T_DEFINED) +#define _TIME_T_DEFINED_ +#define _TIME_T_DEFINED +typedef __time_t time_t; +#endif + +#if !defined (_SIZE_T_DEFINED_) && !defined (_SIZE_T_DEFINED) +#define _SIZE_T_DEFINED_ +#define _SIZE_T_DEFINED +typedef __size_t size_t; +#endif + +#if !defined (_TM_DEFINED) +#define _TM_DEFINED +struct tm { + int tm_sec; /* seconds after the minute [0-60] */ + int tm_min; /* minutes after the hour [0-59] */ + int tm_hour; /* hours since midnight [0-23] */ + int tm_mday; /* day of the month [1-31] */ + int tm_mon; /* months since January [0-11] */ + int tm_year; /* years since 1900 */ + int tm_wday; /* days since Sunday [0-6] */ + int tm_yday; /* days since January 1 [0-365] */ + int tm_isdst; /* Daylight Saving Time flag */ + /* FIXME: naming issue exists on Fedora/Ubuntu */ + long tm_gmtoff; /* offset from UTC in seconds */ + char *tm_zone; /* timezone abbreviation */ +}; +#endif + +__BEGIN_DECLS + +double _TLIBC_CDECL_ difftime(time_t, time_t); +char * _TLIBC_CDECL_ asctime(const struct tm *); +size_t _TLIBC_CDECL_ strftime(char *, size_t, const char *, const struct tm *); + +/* + * Non-C99 + */ +char * _TLIBC_CDECL_ asctime_r(const struct tm *, char *); + +__END_DECLS + +#endif /* !_TIME_H_ */ diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/unistd.h b/bitacross-worker/rust-sgx-sdk/common/inc/unistd.h new file mode 100644 index 0000000000..2ab3a9a042 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/unistd.h @@ -0,0 +1,59 @@ +/* $OpenBSD: unistd.h,v 1.62 2008/06/25 14:58:54 millert Exp $ */ +/* $NetBSD: unistd.h,v 1.26.4.1 1996/05/28 02:31:51 mrg Exp $ */ + +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)unistd.h 5.13 (Berkeley) 6/17/91 + */ + +#ifndef _UNISTD_H_ +#define _UNISTD_H_ + +#include +#include + +__BEGIN_DECLS + +void * _TLIBC_CDECL_ sbrk(intptr_t); + +/* + * Deprecated Non-C99. + */ +_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, execl, const char *, const char *, ...); +_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, execlp, const char *, const char *, ...); +_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, execle, const char *, const char *, ...); +_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, execv, const char *, char * const *); +_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, execve, const char *, char * const *, char * const *); +_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, execvp, const char *, char * const *); + +//_TLIBC_DEPRECATED_FUNCTION_(pid_t _TLIBC_CDECL_, fork, void); /* no pid_t */ + +__END_DECLS + +#endif /* !_UNISTD_H_ */ diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/wchar.h b/bitacross-worker/rust-sgx-sdk/common/inc/wchar.h new file mode 100644 index 0000000000..2db86f28eb --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/wchar.h @@ -0,0 +1,143 @@ +/* $OpenBSD: wchar.h,v 1.11 2010/07/24 09:58:39 guenther Exp $ */ +/* $NetBSD: wchar.h,v 1.16 2003/03/07 07:11:35 tshiozak Exp $ */ + +/*- + * Copyright (c)1999 Citrus Project, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/*- + * Copyright (c) 1999, 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Julian Coleman. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _WCHAR_H_ +#define _WCHAR_H_ + +#include +#include +#include /* WCHAR_MAX/WCHAR_MIN */ + +#ifndef NULL +#ifdef __cplusplus +#define NULL 0 +#else +#define NULL ((void *)0) +#endif +#endif + +#if !defined(_WCHAR_T_DEFINED_) && !defined(__cplusplus) +#define _WCHAR_T_DEFINED_ +#ifndef __WCHAR_TYPE__ +#define __WCHAR_TYPE__ int +#endif +typedef __WCHAR_TYPE__ wchar_t; +#endif + +#ifndef _MBSTATE_T_DEFINED_ +#define _MBSTATE_T_DEFINED_ +typedef __mbstate_t mbstate_t; +#endif + +#ifndef _WINT_T_DEFINED_ +#define _WINT_T_DEFINED_ +typedef __wint_t wint_t; +#endif + +#ifndef _SIZE_T_DEFINED_ +#define _SIZE_T_DEFINED_ +typedef __size_t size_t; +#endif + +#ifndef WEOF +#define WEOF ((wint_t)-1) +#endif + +__BEGIN_DECLS + +wint_t _TLIBC_CDECL_ btowc(int); +int _TLIBC_CDECL_ wctob(wint_t); +size_t _TLIBC_CDECL_ mbrlen(const char *, size_t, mbstate_t *); +size_t _TLIBC_CDECL_ mbrtowc(wchar_t *, const char *, size_t, mbstate_t *); +int _TLIBC_CDECL_ mbsinit(const mbstate_t *); +size_t _TLIBC_CDECL_ mbsrtowcs(wchar_t *, const char **, size_t, mbstate_t *); +size_t _TLIBC_CDECL_ wcrtomb(char *, wchar_t, mbstate_t *); +wchar_t * _TLIBC_CDECL_ wcschr(const wchar_t *, wchar_t); +int _TLIBC_CDECL_ wcscmp(const wchar_t *, const wchar_t *); +int _TLIBC_CDECL_ wcscoll(const wchar_t *, const wchar_t *); +size_t _TLIBC_CDECL_ wcscspn(const wchar_t *, const wchar_t *); +size_t _TLIBC_CDECL_ wcslen(const wchar_t *); +wchar_t * _TLIBC_CDECL_ wcsncat(wchar_t *, const wchar_t *, size_t); +int _TLIBC_CDECL_ wcsncmp(const wchar_t *, const wchar_t *, size_t); +wchar_t * _TLIBC_CDECL_ wcsncpy(wchar_t *, const wchar_t *, size_t); +wchar_t * _TLIBC_CDECL_ wcspbrk(const wchar_t *, const wchar_t *); +wchar_t * _TLIBC_CDECL_ wcsrchr(const wchar_t *, wchar_t); +size_t _TLIBC_CDECL_ wcsrtombs(char *, const wchar_t **, size_t, mbstate_t *); +size_t _TLIBC_CDECL_ wcsspn(const wchar_t *, const wchar_t *); +wchar_t * _TLIBC_CDECL_ wcsstr(const wchar_t *, const wchar_t *); +wchar_t * _TLIBC_CDECL_ wcstok(wchar_t *, const wchar_t *, wchar_t **); +size_t _TLIBC_CDECL_ wcsxfrm(wchar_t *, const wchar_t *, size_t); +wchar_t * _TLIBC_CDECL_ wmemchr(const wchar_t *, wchar_t, size_t); +int _TLIBC_CDECL_ wmemcmp(const wchar_t *, const wchar_t *, size_t); +wchar_t * _TLIBC_CDECL_ wmemcpy(wchar_t *, const wchar_t *, size_t); +wchar_t * _TLIBC_CDECL_ wmemmove(wchar_t *, const wchar_t *, size_t); +wchar_t * _TLIBC_CDECL_ wmemset(wchar_t *, wchar_t, size_t); + +int _TLIBC_CDECL_ swprintf(wchar_t *, size_t, const wchar_t *, ...); +int _TLIBC_CDECL_ vswprintf(wchar_t *, size_t, const wchar_t *, __va_list); + +long double _TLIBC_CDECL_ wcstold (const wchar_t *, wchar_t **); +long long _TLIBC_CDECL_ wcstoll (const wchar_t *, wchar_t **, int); +unsigned long long _TLIBC_CDECL_ wcstoull (const wchar_t *, wchar_t **, int); + +/* leagcy version of wcsstr */ +wchar_t * _TLIBC_CDECL_ wcswcs(const wchar_t *, const wchar_t *); + +__END_DECLS + +#endif /* !_WCHAR_H_ */ diff --git a/bitacross-worker/rust-sgx-sdk/common/inc/wctype.h b/bitacross-worker/rust-sgx-sdk/common/inc/wctype.h new file mode 100644 index 0000000000..0ab9497d78 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/common/inc/wctype.h @@ -0,0 +1,80 @@ +/* $OpenBSD: wctype.h,v 1.5 2006/01/06 18:53:04 millert Exp $ */ +/* $NetBSD: wctype.h,v 1.5 2003/03/02 22:18:11 tshiozak Exp $ */ + +/*- + * Copyright (c)1999 Citrus Project, + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * citrus Id: wctype.h,v 1.4 2000/12/21 01:50:21 itojun Exp + */ + +#ifndef _WCTYPE_H_ +#define _WCTYPE_H_ + +#include +#include + +#ifndef _WINT_T_DEFINED_ +#define _WINT_T_DEFINED_ +typedef __wint_t wint_t; +#endif + +#ifndef _WCTRANS_T_DEFINED_ +#define _WCTRANS_T_DEFINED_ +typedef __wctrans_t wctrans_t; +#endif + +#ifndef _WCTYPE_T_DEFINED_ +#define _WCTYPE_T_DEFINED_ +typedef __wctype_t wctype_t; +#endif + +#ifndef WEOF +#define WEOF ((wint_t)-1) +#endif + +__BEGIN_DECLS + +int _TLIBC_CDECL_ iswalnum(wint_t); +int _TLIBC_CDECL_ iswalpha(wint_t); +int _TLIBC_CDECL_ iswblank(wint_t); +int _TLIBC_CDECL_ iswcntrl(wint_t); +int _TLIBC_CDECL_ iswdigit(wint_t); +int _TLIBC_CDECL_ iswgraph(wint_t); +int _TLIBC_CDECL_ iswlower(wint_t); +int _TLIBC_CDECL_ iswprint(wint_t); +int _TLIBC_CDECL_ iswpunct(wint_t); +int _TLIBC_CDECL_ iswspace(wint_t); +int _TLIBC_CDECL_ iswupper(wint_t); +int _TLIBC_CDECL_ iswxdigit(wint_t); +int _TLIBC_CDECL_ iswctype(wint_t, wctype_t); +wint_t _TLIBC_CDECL_ towctrans(wint_t, wctrans_t); +wint_t _TLIBC_CDECL_ towlower(wint_t); +wint_t _TLIBC_CDECL_ towupper(wint_t); +wctrans_t _TLIBC_CDECL_ wctrans(const char *); +wctype_t _TLIBC_CDECL_ wctype(const char *); + +__END_DECLS + +#endif /* _WCTYPE_H_ */ diff --git a/bitacross-worker/rust-sgx-sdk/edl/inc/dirent.h b/bitacross-worker/rust-sgx-sdk/edl/inc/dirent.h new file mode 100644 index 0000000000..be63f8332d --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/edl/inc/dirent.h @@ -0,0 +1,39 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License.. + +#ifndef _EDL_DIRENT_H +#define _EDL_DIRENT_H + +struct dirent_t +{ + uint64_t d_ino; + int64_t d_off; + unsigned short int d_reclen; + unsigned char d_type; + char d_name[256]; +}; + +struct dirent64_t +{ + uint64_t d_ino; + int64_t d_off; + unsigned short int d_reclen; + unsigned char d_type; + char d_name[256]; +}; + +#endif diff --git a/bitacross-worker/rust-sgx-sdk/edl/inc/stat.h b/bitacross-worker/rust-sgx-sdk/edl/inc/stat.h new file mode 100644 index 0000000000..7f04c3cec9 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/edl/inc/stat.h @@ -0,0 +1,65 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License.. + +#ifndef _EDL_STAT_H +#define _EDL_STAT_H + +struct stat_t +{ + uint64_t st_dev; + uint64_t st_ino; + uint64_t st_nlink; + uint32_t st_mode; + uint32_t st_uid; + uint32_t st_gid; + int __pad0; + uint64_t st_rdev; + uint64_t st_size; + int64_t st_blksize; + int64_t st_blocks; + int64_t st_atime; + int64_t st_atime_nsec; + int64_t st_mtime; + int64_t st_mtime_nsec; + int64_t st_ctime; + int64_t st_ctime_nsec; + int64_t __reserved[3]; +}; + +struct stat64_t +{ + uint64_t st_dev; + uint64_t st_ino; + uint64_t st_nlink; + uint32_t st_mode; + uint32_t st_uid; + uint32_t st_gid; + int __pad0; + uint64_t st_rdev; + uint64_t st_size; + int64_t st_blksize; + int64_t st_blocks; + int64_t st_atime; + int64_t st_atime_nsec; + int64_t st_mtime; + int64_t st_mtime_nsec; + int64_t st_ctime; + int64_t st_ctime_nsec; + int64_t __reserved[3]; +}; + +#endif diff --git a/bitacross-worker/rust-sgx-sdk/edl/intel/sgx_dcap_tvl.edl b/bitacross-worker/rust-sgx-sdk/edl/intel/sgx_dcap_tvl.edl new file mode 100644 index 0000000000..7c5c0d8c69 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/edl/intel/sgx_dcap_tvl.edl @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2011-2020 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +enclave { + + include "sgx_qve_header.h" + include "sgx_ql_quote.h" + + + trusted { + + /** + * Verify QvE Report and Identity + * + * @param p_quote[IN] - Pointer to SGX Quote. + * @param quote_size[IN] - Size of the buffer pointed to by p_quote (in bytes). + * @param p_qve_report_info[IN] - The output of API "sgx_qv_verify_quote", it should contain QvE report and nonce + * @param expiration_check_date[IN] - This is the date to verify QvE report data, you should use same value for this API and "sgx_qv_verify_quote" + * @param collateral_expiration_status[IN] - The output of API "sgx_qv_verify_quote" about quote verification collateral's expiration status + * @param quote_verification_result[IN] - The output of API "sgx_qv_verify_quote" about quote verification result + * @param p_supplemental_data[IN] - The output of API "sgx_qv_verify_quote", the pointer to supplemental data + * @param supplemental_data_size[IN] - Size of the buffer pointed to by p_quote (in bytes) + * @param qve_isvsvn_threshold [IN] - The threshold of QvE ISVSVN, the ISVSVN of QvE used to verify quote must be greater or equal to this threshold. You can get latest QvE ISVSVN in QvE Identity (JSON) from Intel PCS. + * + * @return Status code of the operation, one of: + * - SGX_QL_SUCCESS + * - SGX_QL_ERROR_INVALID_PARAMETER + * - SGX_QL_ERROR_REPORT // Error when verifying QvE report + * - SGX_QL_ERROR_UNEXPECTED // Error when comparing QvE report data + * - SGX_QL_QVEIDENTITY_MISMATCH // Error when comparing QvE identity + * - SGX_QL_QVE_OUT_OF_DATE // QvE ISVSVN is smaller than input QvE ISV SVN threshold + **/ + + public quote3_error_t sgx_tvl_verify_qve_report_and_identity( + [in, size=quote_size] const uint8_t *p_quote, + uint32_t quote_size, + [in, count=1] const sgx_ql_qe_report_info_t *p_qve_report_info, + time_t expiration_check_date, + uint32_t collateral_expiration_status, + sgx_ql_qv_result_t quote_verification_result, + [in, size=supplemental_data_size] const uint8_t *p_supplemental_data, + uint32_t supplemental_data_size, + sgx_isv_svn_t qve_isvsvn_threshold); + }; +}; diff --git a/bitacross-worker/rust-sgx-sdk/edl/intel/sgx_pthread.edl b/bitacross-worker/rust-sgx-sdk/edl/intel/sgx_pthread.edl new file mode 100644 index 0000000000..7a097a7396 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/edl/intel/sgx_pthread.edl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2011-2019 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +enclave { + untrusted { + [cdecl] int pthread_wait_timeout_ocall (unsigned long long waiter, unsigned long long timeout); + [cdecl] int pthread_create_ocall(unsigned long long self); + [cdecl] int pthread_wakeup_ocall(unsigned long long waiter); + }; +}; diff --git a/bitacross-worker/rust-sgx-sdk/edl/intel/sgx_tkey_exchange.edl b/bitacross-worker/rust-sgx-sdk/edl/intel/sgx_tkey_exchange.edl new file mode 100644 index 0000000000..3e18c89582 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/edl/intel/sgx_tkey_exchange.edl @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2011-2019 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +enclave { + trusted { + public sgx_status_t sgx_ra_get_ga(sgx_ra_context_t context, + [out] sgx_ec256_public_t *g_a); + + public sgx_status_t sgx_ra_proc_msg2_trusted(sgx_ra_context_t context, + [in]const sgx_ra_msg2_t *p_msg2, /*copy msg2 except quote into enclave */ + [in] const sgx_target_info_t *p_qe_target, + [out] sgx_report_t *p_report, + [out] sgx_quote_nonce_t *p_nonce); + + public sgx_status_t sgx_ra_get_msg3_trusted(sgx_ra_context_t context, + uint32_t quote_size, + [in]sgx_report_t* qe_report, + [user_check]sgx_ra_msg3_t *p_msg3, + uint32_t msg3_size); + }; +}; diff --git a/bitacross-worker/rust-sgx-sdk/edl/intel/sgx_tprotected_fs.edl b/bitacross-worker/rust-sgx-sdk/edl/intel/sgx_tprotected_fs.edl new file mode 100644 index 0000000000..2dfad370a9 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/edl/intel/sgx_tprotected_fs.edl @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2011-2019 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +enclave { + from "sgx_tstdc.edl" import *; + untrusted { + void* u_sgxprotectedfs_exclusive_file_open([in, string] const char* filename, uint8_t read_only, [out] int64_t* file_size, [out] int32_t* error_code); + uint8_t u_sgxprotectedfs_check_if_file_exists([in, string] const char* filename); + int32_t u_sgxprotectedfs_fread_node([user_check] void* f, uint64_t node_number, [out, size=node_size] uint8_t* buffer, uint32_t node_size); + int32_t u_sgxprotectedfs_fwrite_node([user_check] void* f, uint64_t node_number, [in, size=node_size] uint8_t* buffer, uint32_t node_size); + int32_t u_sgxprotectedfs_fclose([user_check] void* f); + uint8_t u_sgxprotectedfs_fflush([user_check] void* f); + int32_t u_sgxprotectedfs_remove([in, string] const char* filename); + + void* u_sgxprotectedfs_recovery_file_open([in, string] const char* filename); + uint8_t u_sgxprotectedfs_fwrite_recovery_node([user_check] void* f, [in, count=data_length] uint8_t* data, uint32_t data_length); + int32_t u_sgxprotectedfs_do_file_recovery([in, string] const char* filename, [in, string] const char* recovery_filename, uint32_t node_size); + }; +}; diff --git a/bitacross-worker/rust-sgx-sdk/edl/intel/sgx_tstdc.edl b/bitacross-worker/rust-sgx-sdk/edl/intel/sgx_tstdc.edl new file mode 100644 index 0000000000..4124debcfb --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/edl/intel/sgx_tstdc.edl @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2011-2019 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +enclave { + untrusted { + [cdecl] void sgx_oc_cpuidex([out] int cpuinfo[4], int leaf, int subleaf); + + /* Go outside and wait on my untrusted event */ + [cdecl] int sgx_thread_wait_untrusted_event_ocall([user_check] const void *self); + + /* Wake a thread waiting on its untrusted event */ + [cdecl] int sgx_thread_set_untrusted_event_ocall([user_check] const void *waiter); + + /* Wake a thread waiting on its untrusted event, and wait on my untrusted event */ + [cdecl] int sgx_thread_setwait_untrusted_events_ocall([user_check] const void *waiter, [user_check] const void *self); + + /* Wake multiple threads waiting on their untrusted events */ + [cdecl] int sgx_thread_set_multiple_untrusted_events_ocall([in, count = total] const void **waiters, size_t total); + }; +}; diff --git a/bitacross-worker/rust-sgx-sdk/edl/intel/sgx_tswitchless.edl b/bitacross-worker/rust-sgx-sdk/edl/intel/sgx_tswitchless.edl new file mode 100644 index 0000000000..a20669ab59 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/edl/intel/sgx_tswitchless.edl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2011-2019 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +enclave { + + trusted { + public sgx_status_t sl_init_switchless([user_check]void* sl_data); + public sgx_status_t sl_run_switchless_tworker(); + }; + +}; diff --git a/bitacross-worker/rust-sgx-sdk/edl/intel/sgx_ttls.edl b/bitacross-worker/rust-sgx-sdk/edl/intel/sgx_ttls.edl new file mode 100644 index 0000000000..ca0906f578 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/edl/intel/sgx_ttls.edl @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2011-2021 Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +enclave{ + include "sgx_report.h" + include "sgx_qve_header.h" + include "sgx_ql_lib_common.h" + include "sgx_ql_quote.h" + + untrusted { + quote3_error_t sgx_tls_get_qe_target_info_ocall([size = target_info_size, out] sgx_target_info_t *p_target_info, + size_t target_info_size); + + quote3_error_t sgx_tls_get_quote_size_ocall([out] uint32_t *p_quote_size); + + quote3_error_t sgx_tls_get_quote_ocall([size = report_size, in] sgx_report_t* p_report, + size_t report_size, + [size = quote_size, out] uint8_t *p_quote, + uint32_t quote_size); + + quote3_error_t sgx_tls_get_supplemental_data_size_ocall([out] uint32_t *p_supplemental_data_size); + + quote3_error_t sgx_tls_verify_quote_ocall( + [size = quote_size, in] const uint8_t *p_quote, + uint32_t quote_size, + time_t expiration_check_date, + [out] sgx_ql_qv_result_t *p_quote_verification_result, + [size = qve_report_info_size, in, out] sgx_ql_qe_report_info_t *p_qve_report_info, + size_t qve_report_info_size, + [size = supplemental_data_size, out] uint8_t *p_supplemental_data, + uint32_t supplemental_data_size); + + }; +}; diff --git a/bitacross-worker/rust-sgx-sdk/edl/sgx_asyncio.edl b/bitacross-worker/rust-sgx-sdk/edl/sgx_asyncio.edl new file mode 100644 index 0000000000..f46373894e --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/edl/sgx_asyncio.edl @@ -0,0 +1,33 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +enclave { + + include "sys/epoll.h" + include "poll.h" + + trusted { + /* define ECALLs here. */ + }; + + untrusted { + int u_poll_ocall([out] int *error, [in, out, count=nfds] struct pollfd *fds, nfds_t nfds, int timeout); + int u_epoll_create1_ocall([out] int *error, int flags); + int u_epoll_ctl_ocall([out] int *error, int epfd, int op, int fd, [in] struct epoll_event *event); + int u_epoll_wait_ocall([out] int *error, int epfd, [out, count=maxevents] struct epoll_event *events, int maxevents, int timeout); + }; +}; diff --git a/bitacross-worker/rust-sgx-sdk/edl/sgx_backtrace.edl b/bitacross-worker/rust-sgx-sdk/edl/sgx_backtrace.edl new file mode 100644 index 0000000000..4a9e7ef8c4 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/edl/sgx_backtrace.edl @@ -0,0 +1,31 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +enclave { + + from "sgx_fd.edl" import *; + from "sgx_file.edl" import *; + from "sgx_mem.edl" import *; + + trusted { + /* define ECALLs here. */ + }; + + untrusted { + /* define OCALLs here. */ + }; +}; diff --git a/bitacross-worker/rust-sgx-sdk/edl/sgx_env.edl b/bitacross-worker/rust-sgx-sdk/edl/sgx_env.edl new file mode 100644 index 0000000000..d4a77cc816 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/edl/sgx_env.edl @@ -0,0 +1,40 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +enclave { + + include "pwd.h" + + trusted { + /* define ECALLs here. */ + }; + + untrusted { + char **u_environ_ocall(); + char *u_getenv_ocall([in, string] const char *name); + int u_setenv_ocall([out] int *error, [in, string] const char *name, [in, string] const char *value, int overwrite); + int u_unsetenv_ocall([out] int *error, [in, string] const char *name); + int u_chdir_ocall([out] int *error, [in, string] const char *dir); + char *u_getcwd_ocall([out] int *error, [out, size=buflen] char *buf, size_t buflen); + int u_getpwuid_r_ocall(unsigned int uid, + [out] struct passwd *pwd, + [out, size=buflen] char *buf, + size_t buflen, + [out] struct passwd **passwd_result); + unsigned int u_getuid_ocall(); + }; +}; diff --git a/bitacross-worker/rust-sgx-sdk/edl/sgx_fd.edl b/bitacross-worker/rust-sgx-sdk/edl/sgx_fd.edl new file mode 100644 index 0000000000..cd668b71c0 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/edl/sgx_fd.edl @@ -0,0 +1,57 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +enclave { + + include "inc/stat.h" + include "sys/uio.h" + include "time.h" + + from "sgx_mem.edl" import *; + + trusted { + /* define ECALLs here. */ + }; + + untrusted { + size_t u_read_ocall([out] int *error, int fd, [user_check] void *buf, size_t count); + size_t u_pread64_ocall([out] int *error, int fd, [user_check] void *buf, size_t count, int64_t offset); + size_t u_readv_ocall([out] int *error, int fd, [in, count=iovcnt] const struct iovec *iov, int iovcnt); + size_t u_preadv64_ocall([out] int *error, int fd, [in, count=iovcnt] const struct iovec *iov, int iovcnt, int64_t offset); + + size_t u_write_ocall([out] int *error, int fd, [user_check] const void *buf, size_t count); + size_t u_pwrite64_ocall([out] int *error, int fd, [user_check] const void *buf, size_t count, int64_t offset); + size_t u_writev_ocall([out] int *error, int fd, [in, count=iovcnt] const struct iovec *iov, int iovcnt); + size_t u_pwritev64_ocall([out] int *error, int fd, [in, count=iovcnt] const struct iovec *iov, int iovcnt, int64_t offset); + + size_t u_sendfile_ocall([out] int *error, int out_fd, int in_fd, [in, out] int64_t *offset, size_t count); + size_t u_copy_file_range_ocall([out] int *error, int fd_in, [in, out] int64_t *off_in, int fd_out, [in, out] int64_t *off_out, size_t len, unsigned int flags); + size_t u_splice_ocall([out] int *error, int fd_in, [in, out] int64_t *off_in, int fd_out, [in, out] int64_t *off_out, size_t len, unsigned int flags); + + int u_fcntl_arg0_ocall([out] int *error, int fd, int cmd); + int u_fcntl_arg1_ocall([out] int *error, int fd, int cmd, int arg); + int u_ioctl_arg0_ocall([out] int *error, int fd, int request); + int u_ioctl_arg1_ocall([out] int *error, int fd, int request, [in, out] int *arg); + + int u_close_ocall([out] int *error, int fd); + int u_isatty_ocall([out] int *error, int fd); + int u_dup_ocall([out] int *error, int oldfd); + int u_eventfd_ocall([out] int *error, unsigned int initval, int flags); + + int u_futimens_ocall([out] int *error, int fd, [in, count=2] const struct timespec *times); + }; +}; diff --git a/bitacross-worker/rust-sgx-sdk/edl/sgx_file.edl b/bitacross-worker/rust-sgx-sdk/edl/sgx_file.edl new file mode 100644 index 0000000000..c70ec599a2 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/edl/sgx_file.edl @@ -0,0 +1,66 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +enclave { + + include "inc/stat.h" + include "inc/dirent.h" + + trusted { + /* define ECALLs here. */ + }; + + untrusted { + int u_open_ocall([out] int *error, [in, string] const char *pathname, int flags); + int u_open64_ocall([out] int *error, [in, string] const char *path, int oflag, int mode); + int u_openat_ocall([out] int *error, int dirfd, [in, string] const char *pathname, int flags); + + int u_fstat_ocall([out] int *error, int fd, [out] struct stat_t *buf); + int u_fstat64_ocall([out] int *error, int fd, [out] struct stat64_t *buf); + int u_stat_ocall([out] int *error, [in, string] const char *path, [out] struct stat_t *buf); + int u_stat64_ocall([out] int *error, [in, string] const char *path, [out] struct stat64_t *buf); + int u_lstat_ocall([out] int *error, [in, string] const char *path, [out] struct stat_t *buf); + int u_lstat64_ocall([out] int *error, [in, string] const char *path, [out] struct stat64_t *buf); + uint64_t u_lseek_ocall([out] int *error, int fd, int64_t offset, int whence); + int64_t u_lseek64_ocall([out] int *error, int fd, int64_t offset, int whence); + int u_ftruncate_ocall([out] int *error, int fd, int64_t length); + int u_ftruncate64_ocall([out] int *error, int fd, int64_t length); + int u_truncate_ocall([out] int *error, [in, string] const char *path, int64_t length); + int u_truncate64_ocall([out] int *error, [in, string] const char *path, int64_t length); + + int u_fsync_ocall([out] int *error, int fd); + int u_fdatasync_ocall([out] int *error, int fd); + int u_fchmod_ocall([out] int *error, int fd, uint32_t mode); + int u_unlink_ocall([out] int *error, [in, string] const char *pathname); + int u_link_ocall([out] int *error, [in, string] const char *oldpath, [in, string] const char *newpath); + int u_unlinkat_ocall([out] int *error, int dirfd, [in, string] const char *pathname, int flags); + int u_linkat_ocall([out] int *error, int olddirfd, [in, string] const char *oldpath, int newdirfd, [in, string] const char *newpath, int flags); + int u_rename_ocall([out] int *error, [in, string] const char *oldpath, [in, string] const char *newpath); + int u_chmod_ocall([out] int *error, [in, string] const char *path, uint32_t mode); + size_t u_readlink_ocall([out] int *error, [in, string] const char *path, [out, size=bufsz] char *buf, size_t bufsz); + int u_symlink_ocall([out] int *error, [in, string] const char *path1, [in, string] const char *path2); + char *u_realpath_ocall([out] int *error, [in, string] const char *pathname); + int u_mkdir_ocall([out] int *error, [in, string] const char *pathname, uint32_t mode); + int u_rmdir_ocall([out] int *error, [in, string] const char *pathname); + void *u_fdopendir_ocall([out] int *error, int fd); + void *u_opendir_ocall([out] int *error, [in, string] const char *pathname); + int u_readdir64_r_ocall([user_check] void *dirp, [in, out] struct dirent64_t *entry, [out] struct dirent64_t **result); + int u_closedir_ocall([out] int *error, [user_check] void *dirp); + int u_dirfd_ocall([out] int *error, [user_check] void *dirp); + int u_fstatat64_ocall([out] int *error, int dirfd, [in, string] const char *pathname, [out] struct stat64_t *buf, int flags); + }; +}; diff --git a/bitacross-worker/rust-sgx-sdk/edl/sgx_fs.edl b/bitacross-worker/rust-sgx-sdk/edl/sgx_fs.edl new file mode 100644 index 0000000000..2618be9352 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/edl/sgx_fs.edl @@ -0,0 +1,31 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +enclave { + + from "sgx_mem.edl" import *; + from "sgx_fd.edl" import *; + from "sgx_file.edl" import *; + + trusted { + /* define ECALLs here. */ + }; + + untrusted { + /* define OCALLs here. */ + }; +}; diff --git a/bitacross-worker/rust-sgx-sdk/edl/sgx_mem.edl b/bitacross-worker/rust-sgx-sdk/edl/sgx_mem.edl new file mode 100644 index 0000000000..db55802755 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/edl/sgx_mem.edl @@ -0,0 +1,40 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +enclave { + + trusted { + /* define ECALLs here. */ + }; + + untrusted { + void *u_malloc_ocall([out] int *error, size_t size); + void u_free_ocall([user_check] void *p); + + void *u_mmap_ocall([out] int *error, + [user_check] void *start, + size_t length, + int prot, + int flags, + int fd, + int64_t offset); + int u_munmap_ocall([out] int *error, [user_check] void *start, size_t length); + + int u_msync_ocall([out] int *error, [user_check] void *addr, size_t length, int flags); + int u_mprotect_ocall([out] int *error, [user_check] void *addr, size_t length, int prot); + }; +}; diff --git a/bitacross-worker/rust-sgx-sdk/edl/sgx_net.edl b/bitacross-worker/rust-sgx-sdk/edl/sgx_net.edl new file mode 100644 index 0000000000..a803b53ac2 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/edl/sgx_net.edl @@ -0,0 +1,41 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +enclave { + + include "sys/socket.h" + include "netdb.h" + + from "sgx_socket.edl" import *; + from "sgx_asyncio.edl" import *; + from "sgx_fd.edl" import *; + from "sgx_time.edl" import *; + + trusted { + /* define ECALLs here. */ + }; + + untrusted { + int u_getaddrinfo_ocall([out] int *error, + [in, string] const char *node, + [in, string] const char *service, + [in] const struct addrinfo *hints, + [out] struct addrinfo **res); + void u_freeaddrinfo_ocall([user_check] struct addrinfo *res); + char *u_gai_strerror_ocall(int errcode); + }; +}; diff --git a/bitacross-worker/rust-sgx-sdk/edl/sgx_net_switchless.edl b/bitacross-worker/rust-sgx-sdk/edl/sgx_net_switchless.edl new file mode 100644 index 0000000000..ec5c500cfc --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/edl/sgx_net_switchless.edl @@ -0,0 +1,92 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +enclave { + + include "sys/socket.h" + include "poll.h" + from "sgx_fs.edl" import *; + from "sgx_time.edl" import *; + from "sgx_mem.edl" import *; + + trusted { + /* define ECALLs here. */ + }; + + untrusted { + + int u_net_socket_ocall([out] int *error, int domain, int ty, int protocol) transition_using_threads; + int u_net_socketpair_ocall([out] int *error, int domain, int ty, int protocol, [out] int sv[2]) transition_using_threads; + int u_net_bind_ocall([out] int *error, int sockfd, [in, size=addrlen] const struct sockaddr *addr, socklen_t addrlen) transition_using_threads; + int u_net_listen_ocall([out] int *error, int sockfd, int backlog) transition_using_threads; + int u_net_accept4_ocall([out] int *error, + int sockfd, + [in, out, size=addrlen_in] struct sockaddr *addr, + socklen_t addrlen_in, + [out] socklen_t *addrlen_out, + int flags) transition_using_threads; + int u_net_connect_ocall([out] int *error, + int sockfd, + [in, size=addrlen] const struct sockaddr *addr, + socklen_t addrlen) transition_using_threads; + size_t u_net_recv_ocall([out] int *error, int sockfd, [out, size=len] void *buf, size_t len, int flags) transition_using_threads; + size_t u_net_recvfrom_ocall([out] int *error, + int sockfd, + [out, size=len] void *buf, + size_t len, + int flags, + [out, size=addrlen_in] struct sockaddr *src_addr, + socklen_t addrlen_in, + [out] socklen_t *addrlen_out) transition_using_threads; + size_t u_net_recvmsg_ocall([out] int *error, int sockfd, [in, out] struct msghdr *msg, int flags) transition_using_threads; + size_t u_net_send_ocall([out] int *error, int sockfd, [in, size=len] const void *buf, size_t len, int flags) transition_using_threads; + size_t u_net_sendto_ocall([out] int *error, + int sockfd, + [in, size=len] const void *buf, + size_t len, + int flags, + [in, size=addrlen] const struct sockaddr *dest_addr, + socklen_t addrlen) transition_using_threads; + size_t u_sendmsg_ocall([out] int *error, int sockfd, [in] const struct msghdr *msg, int flags) transition_using_threads; + int u_net_getsockopt_ocall([out] int *error, + int sockfd, + int level, + int optname, + [out, size=optlen_in] void *optval, + socklen_t optlen_in, + [out] socklen_t *optlen_out) transition_using_threads; + int u_net_setsockopt_ocall([out] int *error, + int sockfd, + int level, + int optname, + [in, size=optlen] const void *optval, + socklen_t optlen) transition_using_threads; + int u_net_getsockname_ocall([out] int *error, + int sockfd, + [out, size=addrlen_in] struct sockaddr *addr, + socklen_t addrlen_in, + [out] socklen_t *addrlen_out) transition_using_threads; + int u_net_getpeername_ocall([out] int *error, + int sockfd, + [out, size=addrlen_in] struct sockaddr *addr, + socklen_t addrlen_in, + [out] socklen_t *addrlen_out) transition_using_threads; + int u_net_shutdown_ocall([out] int *error, int sockfd, int how) transition_using_threads; + int u_net_ioctl_ocall([out] int *error, int fd, int request, [in, out] int *arg) transition_using_threads; + int u_net_poll_ocall([out] int *error, [in, out, count=nfds] struct pollfd *fds, nfds_t nfds, int timeout) transition_using_threads; + }; +}; diff --git a/bitacross-worker/rust-sgx-sdk/edl/sgx_pipe.edl b/bitacross-worker/rust-sgx-sdk/edl/sgx_pipe.edl new file mode 100644 index 0000000000..00c12f5e7c --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/edl/sgx_pipe.edl @@ -0,0 +1,31 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +enclave { + + from "sgx_fd.edl" import *; + from "sgx_asyncio.edl" import *; + + trusted { + /* define ECALLs here. */ + }; + + untrusted { + int u_pipe_ocall([out] int *error, [out, count=2] int *pipefd); + int u_pipe2_ocall([out] int *error, [out, count=2] int *pipefd, int flags); + }; +}; diff --git a/bitacross-worker/rust-sgx-sdk/edl/sgx_process.edl b/bitacross-worker/rust-sgx-sdk/edl/sgx_process.edl new file mode 100644 index 0000000000..69123df5d8 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/edl/sgx_process.edl @@ -0,0 +1,28 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +enclave { + + trusted { + /* define ECALLs here. */ + + }; + + untrusted { + int u_getpid_ocall(); + }; +}; diff --git a/bitacross-worker/rust-sgx-sdk/edl/sgx_signal.edl b/bitacross-worker/rust-sgx-sdk/edl/sgx_signal.edl new file mode 100644 index 0000000000..fd9b0f0d14 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/edl/sgx_signal.edl @@ -0,0 +1,43 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +enclave { + include "signal.h" + + trusted { + /* define ECALLs here. */ + public int t_signal_handler_ecall([in]const siginfo_t *info); + }; + + untrusted { + int u_sigaction_ocall([out]int *error, + int signum, + [in] const struct sigaction *act, + [out] struct sigaction *oldact, + uint64_t enclave_id); + + int u_sigprocmask_ocall([out]int *error, + int signum, + [in] const sigset_t *set, + [out] sigset_t *oldset); + + int u_raise_ocall(int signum) allow(t_signal_handler_ecall); + + void u_signal_clear_ocall(uint64_t enclave_id); + }; +}; + diff --git a/bitacross-worker/rust-sgx-sdk/edl/sgx_socket.edl b/bitacross-worker/rust-sgx-sdk/edl/sgx_socket.edl new file mode 100644 index 0000000000..6fc8ff7c85 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/edl/sgx_socket.edl @@ -0,0 +1,111 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +enclave { + + include "sys/socket.h" + + from "sgx_mem.edl" import *; + + trusted { + /* define ECALLs here. */ + }; + + untrusted { + int u_socket_ocall([out] int *error, int domain, int ty, int protocol); + int u_socketpair_ocall([out] int *error, int domain, int ty, int protocol, [out] int sv[2]); + int u_bind_ocall([out] int *error, int sockfd, [in, size=addrlen] const struct sockaddr *addr, socklen_t addrlen); + int u_listen_ocall([out] int *error, int sockfd, int backlog); + int u_accept_ocall([out] int *error, + int sockfd, + [in, out, size=addrlen_in] struct sockaddr *addr, + socklen_t addrlen_in, + [out] socklen_t *addrlen_out); + int u_accept4_ocall([out] int *error, + int sockfd, + [in, out, size=addrlen_in] struct sockaddr *addr, + socklen_t addrlen_in, + [out] socklen_t *addrlen_out, + int flags); + int u_connect_ocall([out] int *error, + int sockfd, + [in, size=addrlen] const struct sockaddr *addr, + socklen_t addrlen); + size_t u_recv_ocall([out] int *error, int sockfd,[user_check] void *buf, size_t len, int flags); + size_t u_recvfrom_ocall([out] int *error, + int sockfd, + [user_check] void *buf, + size_t len, + int flags, + [out, size=addrlen_in] struct sockaddr *src_addr, + socklen_t addrlen_in, + [out] socklen_t *addrlen_out); + size_t u_recvmsg_ocall([out] int *error, + int sockfd, + [out, size=msg_namelen] void *msg_name, + socklen_t msg_namelen, + [out] socklen_t* msg_namelen_out, + [in, count=msg_iovlen] struct iovec* msg_iov, + size_t msg_iovlen, + [out, size=msg_controllen] void *msg_control, + size_t msg_controllen, + [out] size_t* msg_controllen_out, + [out] int* msg_flags, + int flags); + size_t u_send_ocall([out] int *error, int sockfd, [user_check] const void *buf, size_t len, int flags); + size_t u_sendto_ocall([out] int *error, + int sockfd, + [user_check] const void *buf, + size_t len, + int flags, + [in, size=addrlen] const struct sockaddr *dest_addr, + socklen_t addrlen); + size_t u_sendmsg_ocall([out] int *error, + int sockfd, + [in, size=msg_namelen] const void* msg_name, + socklen_t msg_namelen, + [in, count=msg_iovlen] const struct iovec* msg_iov, + size_t msg_iovlen, + [in, size=msg_controllen] const void* msg_control, + size_t msg_controllen, + int flags); + int u_getsockopt_ocall([out] int *error, + int sockfd, + int level, + int optname, + [out, size=optlen_in] void *optval, + socklen_t optlen_in, + [out] socklen_t *optlen_out); + int u_setsockopt_ocall([out] int *error, + int sockfd, + int level, + int optname, + [in, size=optlen] const void *optval, + socklen_t optlen); + int u_getsockname_ocall([out] int *error, + int sockfd, + [out, size=addrlen_in] struct sockaddr *addr, + socklen_t addrlen_in, + [out] socklen_t *addrlen_out); + int u_getpeername_ocall([out] int *error, + int sockfd, + [out, size=addrlen_in] struct sockaddr *addr, + socklen_t addrlen_in, + [out] socklen_t *addrlen_out); + int u_shutdown_ocall([out] int *error, int sockfd, int how); + }; +}; diff --git a/bitacross-worker/rust-sgx-sdk/edl/sgx_stdio.edl b/bitacross-worker/rust-sgx-sdk/edl/sgx_stdio.edl new file mode 100644 index 0000000000..5367d9ab97 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/edl/sgx_stdio.edl @@ -0,0 +1,29 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +enclave { + + from "sgx_fd.edl" import *; + + trusted { + /* define ECALLs here. */ + }; + + untrusted { + /* define OCALLs here. */ + }; +}; diff --git a/bitacross-worker/rust-sgx-sdk/edl/sgx_sys.edl b/bitacross-worker/rust-sgx-sdk/edl/sgx_sys.edl new file mode 100644 index 0000000000..bc74b96843 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/edl/sgx_sys.edl @@ -0,0 +1,32 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +enclave { + + include "sched.h" + + trusted { + /* define ECALLs here. */ + }; + + untrusted { + long u_sysconf_ocall([out] int *error, int name); + int u_prctl_ocall([out] int *error, int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5); + int u_sched_setaffinity_ocall([out] int *error, pid_t pid, size_t cpusetsize, [in, size=cpusetsize] cpu_set_t *mask); + int u_sched_getaffinity_ocall([out] int *error, pid_t pid, size_t cpusetsize, [out, size=cpusetsize] cpu_set_t *mask); + }; +}; diff --git a/bitacross-worker/rust-sgx-sdk/edl/sgx_thread.edl b/bitacross-worker/rust-sgx-sdk/edl/sgx_thread.edl new file mode 100644 index 0000000000..71512f0e56 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/edl/sgx_thread.edl @@ -0,0 +1,32 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +enclave { + + include "time.h" + + from "intel/sgx_pthread.edl" import *; + from "sgx_sys.edl" import *; + + trusted { + /* define ECALLs here. */ + }; + + untrusted { + int u_sched_yield_ocall([out]int *error); + int u_nanosleep_ocall([out]int *error, [in]const struct timespec *req, [out]struct timespec *rem); + }; +}; diff --git a/bitacross-worker/rust-sgx-sdk/edl/sgx_time.edl b/bitacross-worker/rust-sgx-sdk/edl/sgx_time.edl new file mode 100644 index 0000000000..adeeeccf92 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/edl/sgx_time.edl @@ -0,0 +1,29 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +enclave { + + include "time.h" + + trusted { + /* define ECALLs here. */ + }; + + untrusted { + int u_clock_gettime_ocall([out] int *error, int clk_id, [out] struct timespec *tp); + }; +}; diff --git a/bitacross-worker/rust-sgx-sdk/edl/sgx_tstd.edl b/bitacross-worker/rust-sgx-sdk/edl/sgx_tstd.edl new file mode 100644 index 0000000000..9b74272f50 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/edl/sgx_tstd.edl @@ -0,0 +1,38 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +enclave { + + from "sgx_time.edl" import *; + + trusted { + /* define ECALLs here. */ + public void t_global_init_ecall(uint64_t id, [in, size=len] const uint8_t *path, size_t len); + public void t_global_exit_ecall(); + }; + + untrusted { + /* define OCALLs here. */ + int u_thread_set_event_ocall([out] int *error, [user_check] const void *tcs); + int u_thread_wait_event_ocall([out] int *error, [user_check] const void *tcs, [in] const struct timespec *timeout); + int u_thread_set_multiple_events_ocall([out] int *error, [in, count=total] const void **tcss, int total); + int u_thread_setwait_events_ocall([out] int *error, + [user_check] const void *waiter_tcs, + [user_check] const void *self_tcs, + [in] const struct timespec *timeout); + }; +}; diff --git a/bitacross-worker/rust-sgx-sdk/version b/bitacross-worker/rust-sgx-sdk/version new file mode 100644 index 0000000000..78e68ab976 --- /dev/null +++ b/bitacross-worker/rust-sgx-sdk/version @@ -0,0 +1 @@ +27bd225ae6dbcd1d0a6d4d9590acc4d73c5195c2 diff --git a/bitacross-worker/rust-toolchain.toml b/bitacross-worker/rust-toolchain.toml new file mode 100644 index 0000000000..23ed88e6c8 --- /dev/null +++ b/bitacross-worker/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "nightly-2022-10-22" +targets = ["wasm32-unknown-unknown"] +profile = "default" # include rustfmt, clippy diff --git a/bitacross-worker/rustfmt.toml b/bitacross-worker/rustfmt.toml new file mode 100644 index 0000000000..104b9aa998 --- /dev/null +++ b/bitacross-worker/rustfmt.toml @@ -0,0 +1,18 @@ +# Basic +hard_tabs = true +max_width = 100 +use_small_heuristics = "Max" +# Imports +imports_granularity = "Crate" +reorder_imports = true +# Consistency +newline_style = "Unix" +# Misc +chain_width = 80 +spaces_around_ranges = false +match_arm_leading_pipes = "Preserve" +match_arm_blocks = false +match_block_trailing_comma = true +trailing_comma = "Vertical" +trailing_semicolon = false +use_field_init_shorthand = true \ No newline at end of file diff --git a/bitacross-worker/samples/teeracle/README.md b/bitacross-worker/samples/teeracle/README.md new file mode 100644 index 0000000000..05758f7d09 --- /dev/null +++ b/bitacross-worker/samples/teeracle/README.md @@ -0,0 +1,58 @@ +# Teeracle install into Securitee's kubernetes cluster + +This example is about to install [Integritee's Teeracle](https://docs.integritee.network/3-our-technology/3.5-use-cases/3.5.3-teeracle-oracle-framework). + +*Prerequisites:* + +* Ensure you have access to a Kubernetes cluster with SGX-enabled nodes and kubectl installed and configured. The easiest way to get started is to order Kubernetes from Securitee [Securitee Kubernetes](https://securitee.tech/products/), which offers SGX-enabled nodes. +* You have [Helm](https://helm.sh/docs/intro/install/) installed + +## Kubernetes deployment walkthrough + +We are now installing Teeracle + +### Install steps + + +* Edit the configuration values in file [kubernetes/values.yaml](kubernetes/values.yaml) + ```yaml + app: + url: "wss://rococo.api.integritee.network" + interval: "2m" + ``` +* Install the Teeracle into the cluster + + ```bash + helm install -f ./kubernetes/values.yaml teeracle ./kubernetes --create-namespace -n teeracle + or run + ./install-teeracle.sh + ``` + + +## Misc. + +### SGX Plugin + +If you are running in simulation mode, or are using a different plugin please edit the [kubernetes/templates/teeracle.yaml](kubernetes/templates/teeracle.yaml) + ```yaml + limits: + sgx.intel.com/epc: "10Mi" + sgx.intel.com/enclave: 1 + sgx.intel.com/provision: 1 + ``` + +### PCCS server + +The DCAP attestation requires a running PCCS server - which is provided by Securitee by default that's why we need to mount the ```/etc/sgx_default_qcnl.conf``` config file +see [kubernetes/templates/teeracle.yaml](kubernetes/templates/teeracle.yaml) + ```yaml + volumeMounts: + - name: qcnl + mountPath: /etc/sgx_default_qcnl.conf + ... + volumes: + - name: qcnl + hostPath: + path: /etc/sgx_default_qcnl.conf + + ``` diff --git a/bitacross-worker/samples/teeracle/install-teeracle.sh b/bitacross-worker/samples/teeracle/install-teeracle.sh new file mode 100755 index 0000000000..dbc21bc2b4 --- /dev/null +++ b/bitacross-worker/samples/teeracle/install-teeracle.sh @@ -0,0 +1,7 @@ +#!/bin/env bash + +namespace=teeracle +helm uninstall -n $namespace teeracle + +helm install -f ./kubernetes/values.yaml teeracle ./kubernetes --create-namespace -n $namespace + diff --git a/bitacross-worker/samples/teeracle/kubernetes/Chart.yaml b/bitacross-worker/samples/teeracle/kubernetes/Chart.yaml new file mode 100644 index 0000000000..d1f3a9a7f8 --- /dev/null +++ b/bitacross-worker/samples/teeracle/kubernetes/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: teeracle +description: teeracle dcap + +type: application +version: 0.1.0 +appVersion: 1.0.0 \ No newline at end of file diff --git a/bitacross-worker/samples/teeracle/kubernetes/templates/teeracle.yaml b/bitacross-worker/samples/teeracle/kubernetes/templates/teeracle.yaml new file mode 100644 index 0000000000..130ad79cb4 --- /dev/null +++ b/bitacross-worker/samples/teeracle/kubernetes/templates/teeracle.yaml @@ -0,0 +1,73 @@ +kind: ServiceAccount +apiVersion: v1 +metadata: + name: teeracle + namespace: {{ .Release.Namespace }} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: teeracle-main + namespace: {{ .Release.Namespace }} + labels: + app: teeracle + role: main + tier: backend +spec: + replicas: 1 + selector: + matchLabels: + app: teeracle + role: main + tier: backend + template: + metadata: + labels: + app: teeracle + spec: + serviceAccountName: teeracle + containers: + - image: {{ .Values.image }} + imagePullPolicy: {{ .Values.imagePullPolicy }} + + args: [ + "-p", "443", + "-u", {{ .Values.app.url }}, + "--enable-metrics", + "--data-dir", "/opt/teeracle", + "run", + "--teeracle-interval", {{ .Values.app.interval }} + ] + name: teeracle + + resources: + # Resource request to use Intel SGX Device Plugin + # If you are running in simulation mode, or are using a different plugin, + # update these values accordingly + limits: + sgx.intel.com/epc: "10Mi" + sgx.intel.com/enclave: 1 + sgx.intel.com/provision: 1 + + volumeMounts: + - name: aesmd-socket + mountPath: /var/run/aesmd + - name: data-dir + mountPath: /opt/teeracle + - name: qcnl + mountPath: /etc/sgx_default_qcnl.conf + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + volumes: + - name: aesmd-socket + hostPath: + path: /var/run/aesmd + - name: data-dir + hostPath: + path: /opt/teeracle + - name: qcnl + hostPath: + path: /etc/sgx_default_qcnl.conf + diff --git a/bitacross-worker/samples/teeracle/kubernetes/values.yaml b/bitacross-worker/samples/teeracle/kubernetes/values.yaml new file mode 100644 index 0000000000..8a423ee3ab --- /dev/null +++ b/bitacross-worker/samples/teeracle/kubernetes/values.yaml @@ -0,0 +1,14 @@ +imagePullSecrets: + - name: regcred + +imagePullPolicy: IfNotPresent + +image: integritee/teeracle:v0.12.2-dev + +# +# To get more insights run: +# docker run integritee/teeracle:v0.12.2-dev --help +# +app: + url: "wss://rococo.api.integritee.network" + interval: "2m" \ No newline at end of file diff --git a/bitacross-worker/scripts/benchmark_local-setup.sh b/bitacross-worker/scripts/benchmark_local-setup.sh new file mode 100644 index 0000000000..40bc700f05 --- /dev/null +++ b/bitacross-worker/scripts/benchmark_local-setup.sh @@ -0,0 +1,31 @@ +#!/bin/bash +set -e + +pushd .. + +pushd bin +./litentry-worker mrenclave | tee ~/mrenclave.b58 +popd + +ulimit -S -n 4096 + +python3 local-setup/launch.py local-setup/config/benchmark.json & +PID=$! +echo $PID > ./benchmark.pid +echo "Benchmark PID: $PID" + +sleep 40s + +pushd bin +./bitacross-cli -p 9930 -P 2030 trusted --direct --mrenclave "$(cat ~/mrenclave.b58)" benchmark 20 100 -w +popd + +sleep 10s + +if test -f "./benchmark.pid"; then + echo "Killing benchmark process" + kill -s SIGTERM "$(cat ./benchmark.pid)" + rm benchmark.pid +fi + +popd diff --git a/bitacross-worker/scripts/changelog/.gitignore b/bitacross-worker/scripts/changelog/.gitignore new file mode 100644 index 0000000000..4fbcc523b0 --- /dev/null +++ b/bitacross-worker/scripts/changelog/.gitignore @@ -0,0 +1,4 @@ +changelog.md +*.json +release*.md +.env diff --git a/bitacross-worker/scripts/changelog/Gemfile b/bitacross-worker/scripts/changelog/Gemfile new file mode 100644 index 0000000000..f2d7c3bd71 --- /dev/null +++ b/bitacross-worker/scripts/changelog/Gemfile @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +source 'https://rubygems.org' + +git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } + +gem 'octokit', '~> 4' + +gem 'git_diff_parser', '~> 3' + +gem 'toml', '~> 0.3.0' + +gem 'rake', group: :dev + +gem 'optparse', '~> 0.1.1' + +gem 'logger', '~> 1.4' + +gem 'test-unit', group: :dev + +gem 'rubocop', group: :dev, require: false diff --git a/bitacross-worker/scripts/changelog/Gemfile.lock b/bitacross-worker/scripts/changelog/Gemfile.lock new file mode 100644 index 0000000000..855d7f91a5 --- /dev/null +++ b/bitacross-worker/scripts/changelog/Gemfile.lock @@ -0,0 +1,79 @@ +GEM + remote: https://rubygems.org/ + specs: + addressable (2.8.0) + public_suffix (>= 2.0.2, < 5.0) + ast (2.4.2) + faraday (1.8.0) + faraday-em_http (~> 1.0) + faraday-em_synchrony (~> 1.0) + faraday-excon (~> 1.1) + faraday-httpclient (~> 1.0.1) + faraday-net_http (~> 1.0) + faraday-net_http_persistent (~> 1.1) + faraday-patron (~> 1.0) + faraday-rack (~> 1.0) + multipart-post (>= 1.2, < 3) + ruby2_keywords (>= 0.0.4) + faraday-em_http (1.0.0) + faraday-em_synchrony (1.0.0) + faraday-excon (1.1.0) + faraday-httpclient (1.0.1) + faraday-net_http (1.0.1) + faraday-net_http_persistent (1.2.0) + faraday-patron (1.0.0) + faraday-rack (1.0.0) + git_diff_parser (3.2.0) + logger (1.4.4) + multipart-post (2.1.1) + octokit (4.21.0) + faraday (>= 0.9) + sawyer (~> 0.8.0, >= 0.5.3) + optparse (0.1.1) + parallel (1.21.0) + parser (3.0.2.0) + ast (~> 2.4.1) + parslet (2.0.0) + power_assert (2.0.1) + public_suffix (4.0.6) + rainbow (3.0.0) + rake (13.0.6) + regexp_parser (2.1.1) + rexml (3.2.5) + rubocop (1.23.0) + parallel (~> 1.10) + parser (>= 3.0.0.0) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) + rexml + rubocop-ast (>= 1.12.0, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 1.4.0, < 3.0) + rubocop-ast (1.13.0) + parser (>= 3.0.1.1) + ruby-progressbar (1.11.0) + ruby2_keywords (0.0.5) + sawyer (0.8.2) + addressable (>= 2.3.5) + faraday (> 0.8, < 2.0) + test-unit (3.5.1) + power_assert + toml (0.3.0) + parslet (>= 1.8.0, < 3.0.0) + unicode-display_width (2.1.0) + +PLATFORMS + x86_64-darwin-20 + +DEPENDENCIES + git_diff_parser (~> 3) + logger (~> 1.4) + octokit (~> 4) + optparse (~> 0.1.1) + rake + rubocop + test-unit + toml (~> 0.3.0) + +BUNDLED WITH + 2.2.22 diff --git a/bitacross-worker/scripts/changelog/README.md b/bitacross-worker/scripts/changelog/README.md new file mode 100644 index 0000000000..4776277e70 --- /dev/null +++ b/bitacross-worker/scripts/changelog/README.md @@ -0,0 +1,3 @@ +## License + +Everything in this folder is GPL 3.0 licensed. The original has been authored by parity and was taken from here: https://github.com/paritytech/polkadot/tree/master/scripts/ci/changelog. \ No newline at end of file diff --git a/bitacross-worker/scripts/changelog/bin/changelog b/bitacross-worker/scripts/changelog/bin/changelog new file mode 100755 index 0000000000..15b17d6166 --- /dev/null +++ b/bitacross-worker/scripts/changelog/bin/changelog @@ -0,0 +1,84 @@ +#!/usr/bin/env ruby + +# frozen_string_literal: true + +# call for instance as: +# ./bin/changelog [] [] +# for instance, for the release notes of v1.2.3: +# ./bin/changelog v1.2.3 +# or +# ./bin/changelog v1.2.3 v1.2.2 +# +# You may set the ENV NO_CACHE to force fetching from Github +# You should also ensure you set the ENV: GITHUB_TOKEN + +require_relative '../lib/changelog' +require 'logger' + +logger = Logger.new($stdout) +logger.level = Logger::DEBUG +logger.debug('Starting') + +owner = 'integritee-network' +repo = 'worker' + +gh_worker = SubRef.new(format('%s/%s', { owner: owner, repo: repo })) +last_release_ref = gh_worker.get_last_ref() + +worker_ref2 = ARGV[0] || 'HEAD' +worker_ref1 = ARGV[1] || last_release_ref + +output = ARGV[2] || 'release-notes.md' + +ENV['REF1'] = worker_ref1 +ENV['REF2'] = worker_ref2 + +pallets_ref1 = gh_worker.get_dependency_reference(worker_ref1, 'pallet-teerex') +pallets_ref2 = gh_worker.get_dependency_reference(worker_ref2, 'pallet-teerex') + +logger.debug("Worker from: #{worker_ref1}") +logger.debug("Worker to: #{worker_ref2}") + +logger.debug("Pallets from: #{pallets_ref1}") +logger.debug("Pallets to: #{pallets_ref2}") + +pallets_data = 'pallets.json' +worker_data = 'worker.json' + +logger.debug("Using PALLETS: #{pallets_data}") +logger.debug("Using WORKER: #{worker_data}") + +logger.warn('NO_CACHE set') if ENV['NO_CACHE'] + +if ENV['NO_CACHE'] || !File.file?(worker_data) + logger.debug(format('Fetching data for Worker into %s', worker_data)) + cmd = format('changelogerator %s/%s -f %s -t %s > %s', + { owner: owner, repo: 'worker', from: worker_ref1, to: worker_ref2, output: worker_data }) + system(cmd) +else + logger.debug("Re-using:#{worker_data}") +end + +if ENV['NO_CACHE'] || !File.file?(pallets_data) + logger.debug(format('Fetching data for Pallets into %s', pallets_data)) + cmd = format('changelogerator %s/%s -f %s -t %s > %s', + { owner: owner, repo: 'pallets', from: pallets_ref1, to: pallets_ref2, output: pallets_data }) + system(cmd) +else + logger.debug("Re-using:#{pallets_data}") +end + +# Here we compose all the pieces together into one +# single big json file. +cmd = format('jq \ + --slurpfile pallets %s \ + --slurpfile worker %s \ + -n \'{ + pallets: $pallets[0], + worker: $worker[0], + }\' > context.json', pallets_data, worker_data) +system(cmd) + +cmd = format('tera --env --env-key env --include-path templates \ + --template templates/template.md.tera context.json > %s', output) +system(cmd) diff --git a/bitacross-worker/scripts/changelog/digests/.gitignore b/bitacross-worker/scripts/changelog/digests/.gitignore new file mode 100644 index 0000000000..a6c57f5fb2 --- /dev/null +++ b/bitacross-worker/scripts/changelog/digests/.gitignore @@ -0,0 +1 @@ +*.json diff --git a/bitacross-worker/scripts/changelog/digests/.gitkeep b/bitacross-worker/scripts/changelog/digests/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bitacross-worker/scripts/changelog/lib/changelog.rb b/bitacross-worker/scripts/changelog/lib/changelog.rb new file mode 100644 index 0000000000..d7cf92e7d2 --- /dev/null +++ b/bitacross-worker/scripts/changelog/lib/changelog.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +# A Class to find Substrate references +class SubRef + require 'octokit' + require 'toml' + + attr_reader :client, :repository + + def initialize(github_repo) + @client = Octokit::Client.new( + access_token: ENV['GITHUB_TOKEN'] + ) + @repository = @client.repository(github_repo) + end + + # This function checks the Cargo.lock of a given + # Rust project, for a given package, and fetches + # the dependency git ref. + def get_dependency_reference(ref, package) + cargo = TOML::Parser.new( + Base64.decode64( + @client.contents( + @repository.full_name, + path: 'Cargo.lock', + query: { ref: ref.to_s } + ).content + ) + ).parsed + cargo['package'].find { |p| p['name'] == package }['source'].split('#').last + end + + # Get the git ref of the last release for the repo. + # repo is given in the form integritee-network/worker + def get_last_ref() + 'refs/tags/' + @client.latest_release(@repository.full_name).tag_name + end +end diff --git a/bitacross-worker/scripts/changelog/templates/_free_notes.md.tera b/bitacross-worker/scripts/changelog/templates/_free_notes.md.tera new file mode 100644 index 0000000000..c4a841a992 --- /dev/null +++ b/bitacross-worker/scripts/changelog/templates/_free_notes.md.tera @@ -0,0 +1,10 @@ + +{# This file uses the Markdown format with additional templating such as this comment. -#} +{# Such a comment will not show up in the rendered release notes. -#} +{# The content of this file (if any) will be inserted at the top of the release notes -#} +{# and generated for each new release candidate. -#} +{# Ensure you leave an empty line at both top and bottom of this file. -#} + + + + diff --git a/bitacross-worker/scripts/changelog/templates/challenge_level.md.tera b/bitacross-worker/scripts/changelog/templates/challenge_level.md.tera new file mode 100644 index 0000000000..c4a8934fd4 --- /dev/null +++ b/bitacross-worker/scripts/changelog/templates/challenge_level.md.tera @@ -0,0 +1,37 @@ +{%- import "change.md.tera" as m_c -%} + +{# This macro convert a merge challenge level into readable output #} +{%- macro challenge_level(e, changes) -%} + +{%- if e >= 5 -%} + {%- set level = "‼️ Breaking Changes" -%} + {%- set text = "This release contains **breaking changes**. Be sure to upgrade the affected interfaces." -%} +{%- elif e >= 3 -%} + {%- set level = "❗️ Attention" -%} + {%- set text = "This release contains some non-trivial updates. Be mindful when upgrading." -%} +{%- else -%} + {%- set level = "Trivial" -%} + {%- set text = "This release contains relatively small updates." -%} +{%- endif %} + + + + +{%- if level %} +{{level}}: {{text}} + +{% if e >= 3 %} +The changes motivating this challenge level are: +{% for pr in changes | sort(attribute="merged_at") -%} + {%- if pr.meta.E -%} + {%- if pr.meta.E.value == e %} +- {{ m_c::change(c=pr) }} + {%- endif -%} + {%- endif -%} +{%- endfor -%} +{%- else -%} + +{%- endif -%} +{%- endif -%} + +{%- endmacro level -%} diff --git a/bitacross-worker/scripts/changelog/templates/change.md.tera b/bitacross-worker/scripts/changelog/templates/change.md.tera new file mode 100644 index 0000000000..25cc04edec --- /dev/null +++ b/bitacross-worker/scripts/changelog/templates/change.md.tera @@ -0,0 +1,42 @@ +{# This macro shows ONE change #} +{%- macro change(c, cml="[C]", pal="[P]", wor="[W]") -%} + +{%- if c.meta.C and c.meta.C.value >= 7 -%} +{%- set prio = " ‼️ HIGH" -%} +{%- elif c.meta.C and c.meta.C.value >= 3 -%} +{%- set prio = " ❗️ Medium" -%} +{%- elif c.meta.C and c.meta.C.value < 3 -%} +{%- set prio = " Low" -%} +{%- else -%} +{%- set prio = "" -%} +{%- endif -%} + + +{%- if c.html_url is containing("worker") -%} +{%- set repo = wor -%} +{%- elif c.html_url is containing("pallets") -%} +{%- set repo = pal -%} +{%- else -%} +{%- set repo = " " -%} +{%- endif -%} + +{# For now don't show pallets or worker #} +{%- set repo = " " -%} + +{%- if c.meta.E and c.meta.E.value >= 7 -%} +{%- set challenge = " 💥 breaking changes " -%} +{%- elif c.meta.E and c.meta.E.value == 6 -%} +{%- set challenge = " ⚡ breaks parentchain interface " -%} +{%- elif c.meta.E and c.meta.E.value == 5 -%} +{%- set challenge = " 🔥 breaks public rpc api " -%} +{%- elif c.meta.E and c.meta.E.value >= 3 -%} +{%- set challenge = " 📢 attention required " -%} +{%- elif c.meta.E and c.meta.E.value < 3 -%} +{%- set challenge = " ✅ easy merge " -%} +{%- else -%} +{%- set challenge = "" -%} +{%- endif -%} + + +{{- repo }} {{ challenge }}[`#{{c.number}}`]({{c.html_url}}) {{- prio }} - {{ c.title | capitalize | truncate(length=120, end="…") }} +{%- endmacro change -%} \ No newline at end of file diff --git a/bitacross-worker/scripts/changelog/templates/changes.md.tera b/bitacross-worker/scripts/changelog/templates/changes.md.tera new file mode 100644 index 0000000000..571f2f4cab --- /dev/null +++ b/bitacross-worker/scripts/changelog/templates/changes.md.tera @@ -0,0 +1,24 @@ +{# This include generates the section showing the changes #} +## Changes + +{# for not now printed until pallet is actually included #} +{# ### Legend #} + +{# - {{ WOR }} Worker #} +{# - {{ PAL }} Pallet #} + +{% include "changes_applibs.md.tera" %} + +{% include "changes_client.md.tera" %} + +{% include "changes_core.md.tera" %} + +{% include "changes_evm.md.tera" %} + +{% include "changes_offchain.md.tera" %} + +{% include "changes_sidechain.md.tera" %} + +{% include "changes_teeracle.md.tera" %} + +{% include "changes_misc.md.tera" %} diff --git a/bitacross-worker/scripts/changelog/templates/changes_applibs.md.tera b/bitacross-worker/scripts/changelog/templates/changes_applibs.md.tera new file mode 100644 index 0000000000..db393f764e --- /dev/null +++ b/bitacross-worker/scripts/changelog/templates/changes_applibs.md.tera @@ -0,0 +1,17 @@ +{% import "change.md.tera" as m_c -%} +### App-Libs + +{#- The changes are sorted by merge date #} +{%- for pr in changes | sort(attribute="merged_at") %} + +{%- if pr.meta.B %} + {%- if pr.meta.B.value == 0 %} + {#- We skip silent ones -#} + {%- else -%} + + {%- if pr.meta.A.value == 2 %} +- {{ m_c::change(c=pr) }} + {%- endif -%} + {% endif -%} + {% endif -%} +{% endfor %} diff --git a/bitacross-worker/scripts/changelog/templates/changes_client.md.tera b/bitacross-worker/scripts/changelog/templates/changes_client.md.tera new file mode 100644 index 0000000000..5e96861812 --- /dev/null +++ b/bitacross-worker/scripts/changelog/templates/changes_client.md.tera @@ -0,0 +1,17 @@ +{% import "change.md.tera" as m_c -%} +### Client + +{#- The changes are sorted by merge date #} +{%- for pr in changes | sort(attribute="merged_at") %} + +{%- if pr.meta.B %} + {%- if pr.meta.B.value == 0 %} + {#- We skip silent ones -#} + {%- else -%} + + {%- if pr.meta.A.value == 1 %} +- {{ m_c::change(c=pr) }} + {%- endif -%} + {% endif -%} + {% endif -%} +{% endfor %} diff --git a/bitacross-worker/scripts/changelog/templates/changes_core.md.tera b/bitacross-worker/scripts/changelog/templates/changes_core.md.tera new file mode 100644 index 0000000000..f88447b9e9 --- /dev/null +++ b/bitacross-worker/scripts/changelog/templates/changes_core.md.tera @@ -0,0 +1,17 @@ +{% import "change.md.tera" as m_c -%} +### Core + +{#- The changes are sorted by merge date #} +{%- for pr in changes | sort(attribute="merged_at") %} + +{%- if pr.meta.B %} + {%- if pr.meta.B.value == 0 %} + {#- We skip silent ones -#} + {%- else -%} + + {%- if pr.meta.A.value == 0 %} +- {{ m_c::change(c=pr) }} + {%- endif -%} + {% endif -%} + {% endif -%} +{% endfor %} diff --git a/bitacross-worker/scripts/changelog/templates/changes_evm.md.tera b/bitacross-worker/scripts/changelog/templates/changes_evm.md.tera new file mode 100644 index 0000000000..92747435fd --- /dev/null +++ b/bitacross-worker/scripts/changelog/templates/changes_evm.md.tera @@ -0,0 +1,17 @@ +{% import "change.md.tera" as m_c -%} +### EVM Feature + +{#- The changes are sorted by merge date #} +{%- for pr in changes | sort(attribute="merged_at") %} + +{%- if pr.meta.B %} + {%- if pr.meta.B.value == 0 %} + {#- We skip silent ones -#} + {%- else -%} + + {%- if pr.meta.A.value == 6 %} +- {{ m_c::change(c=pr) }} + {%- endif -%} + {% endif -%} + {% endif -%} +{% endfor %} diff --git a/bitacross-worker/scripts/changelog/templates/changes_misc.md.tera b/bitacross-worker/scripts/changelog/templates/changes_misc.md.tera new file mode 100644 index 0000000000..1beb2efd91 --- /dev/null +++ b/bitacross-worker/scripts/changelog/templates/changes_misc.md.tera @@ -0,0 +1,37 @@ +{%- import "change.md.tera" as m_c -%} + +{%- set_global misc_count = 0 -%} +{#- First pass to count #} +{%- for pr in changes -%} + {%- if pr.meta.B %} + {%- if pr.meta.B.value == 0 -%} + {#- We skip silent ones -#} + {%- else -%} +{%- set_global misc_count = misc_count + 1 -%} + {% endif -%} + {% endif -%} +{% endfor -%} + +### Misc + +{% if misc_count > 10 %} +There are other misc. changes. You can expand the list below to view them all. +
Other misc. changes +{% endif -%} + +{#- The changes are sorted by merge date #} +{%- for pr in changes | sort(attribute="merged_at") %} + {%- if pr.meta.B %} + {%- if pr.meta.B.value == 0 %} + {#- We skip silent ones -#} + {%- else -%} + {%- if pr.meta.B.value >= 1 %} +- {{ m_c::change(c=pr) }} + {%- endif -%} + {% endif -%} + {% endif -%} +{% endfor %} + +{% if misc_count > 10 %} +
+{% endif -%} diff --git a/bitacross-worker/scripts/changelog/templates/changes_offchain.md.tera b/bitacross-worker/scripts/changelog/templates/changes_offchain.md.tera new file mode 100644 index 0000000000..d298752043 --- /dev/null +++ b/bitacross-worker/scripts/changelog/templates/changes_offchain.md.tera @@ -0,0 +1,17 @@ +{% import "change.md.tera" as m_c -%} +### Offchain + +{#- The changes are sorted by merge date #} +{%- for pr in changes | sort(attribute="merged_at") %} + +{%- if pr.meta.B %} + {%- if pr.meta.B.value == 0 %} + {#- We skip silent ones -#} + {%- else -%} + + {%- if pr.meta.A.value == 4 %} +- {{ m_c::change(c=pr) }} + {%- endif -%} + {% endif -%} + {% endif -%} +{% endfor %} diff --git a/bitacross-worker/scripts/changelog/templates/changes_sidechain.md.tera b/bitacross-worker/scripts/changelog/templates/changes_sidechain.md.tera new file mode 100644 index 0000000000..f953cfbcdf --- /dev/null +++ b/bitacross-worker/scripts/changelog/templates/changes_sidechain.md.tera @@ -0,0 +1,17 @@ +{% import "change.md.tera" as m_c -%} +### Sidechain + +{#- The changes are sorted by merge date #} +{%- for pr in changes | sort(attribute="merged_at") %} + +{%- if pr.meta.B %} + {%- if pr.meta.B.value == 0 %} + {#- We skip silent ones -#} + {%- else -%} + + {%- if pr.meta.A.value == 3 %} +- {{ m_c::change(c=pr) }} + {%- endif -%} + {% endif -%} + {% endif -%} +{% endfor %} diff --git a/bitacross-worker/scripts/changelog/templates/changes_teeracle.md.tera b/bitacross-worker/scripts/changelog/templates/changes_teeracle.md.tera new file mode 100644 index 0000000000..6e94e88b2c --- /dev/null +++ b/bitacross-worker/scripts/changelog/templates/changes_teeracle.md.tera @@ -0,0 +1,17 @@ +{% import "change.md.tera" as m_c -%} +### Teeracle + +{#- The changes are sorted by merge date #} +{%- for pr in changes | sort(attribute="merged_at") %} + +{%- if pr.meta.B %} + {%- if pr.meta.B.value == 0 %} + {#- We skip silent ones -#} + {%- else -%} + + {%- if pr.meta.A.value == 5 %} +- {{ m_c::change(c=pr) }} + {%- endif -%} + {% endif -%} + {% endif -%} +{% endfor %} diff --git a/bitacross-worker/scripts/changelog/templates/debug.md.tera b/bitacross-worker/scripts/changelog/templates/debug.md.tera new file mode 100644 index 0000000000..41f3702d7c --- /dev/null +++ b/bitacross-worker/scripts/changelog/templates/debug.md.tera @@ -0,0 +1,8 @@ +{%- set to_ignore = changes | filter(attribute="meta.B.value", value=0) %} + + diff --git a/bitacross-worker/scripts/changelog/templates/global_challenge_level.md.tera b/bitacross-worker/scripts/changelog/templates/global_challenge_level.md.tera new file mode 100644 index 0000000000..d2108dce4d --- /dev/null +++ b/bitacross-worker/scripts/changelog/templates/global_challenge_level.md.tera @@ -0,0 +1,26 @@ +{% import "challenge_level.md.tera" as m_p -%} +## Upgrade Challenge Level + +{%- set worker_prio = 0 -%} +{%- set pallet_prio = 0 -%} + +{# We fetch the various levels #} +{%- if worker.meta.E -%} +{%- set worker_level = worker.meta.E.max -%} +{%- else -%} +{%- set worker_level = 0 -%} +{%- endif -%} +{%- if pallet.meta.E -%} +{%- set pallet_level = pallet.meta.E.max -%} +{%- else -%} +{%- set pallet_level = 0 -%} +{%- endif -%} + +{# We compute the global level #} +{%- set global_level = worker_level -%} +{%- if pallet_level > global_level -%} +{%- set global_level = pallet_level -%} +{%- endif -%} + +{#- We show the result #} +{{ m_p::challenge_level(e=global_level, changes=changes) }} diff --git a/bitacross-worker/scripts/changelog/templates/global_priority.md.tera b/bitacross-worker/scripts/changelog/templates/global_priority.md.tera new file mode 100644 index 0000000000..87a6d52aaf --- /dev/null +++ b/bitacross-worker/scripts/changelog/templates/global_priority.md.tera @@ -0,0 +1,27 @@ +{% import "high_priority.md.tera" as m_p -%} +## Upgrade Priority + +{%- set worker_prio = 0 -%} +{%- set pallet_prio = 0 -%} + +{# We fetch the various priorities #} +{%- if worker.meta.C -%} +{%- set worker_prio = worker.meta.C.max -%} +{%- else -%} +{%- set worker_prio = 0 -%} +{%- endif -%} +{%- if pallet.meta.C -%} +{%- set pallet_prio = pallet.meta.C.max -%} +{%- else -%} +{%- set pallet_prio = 0 -%} +{%- endif -%} + +{# We compute the global priority #} +{%- set global_prio = worker_prio -%} +{%- if pallet_prio > global_prio -%} +{%- set global_prio = pallet_prio -%} +{%- endif -%} + + +{#- We show the result #} +{{ m_p::high_priority(p=global_prio, changes=changes) }} diff --git a/bitacross-worker/scripts/changelog/templates/high_priority.md.tera b/bitacross-worker/scripts/changelog/templates/high_priority.md.tera new file mode 100644 index 0000000000..117d335efd --- /dev/null +++ b/bitacross-worker/scripts/changelog/templates/high_priority.md.tera @@ -0,0 +1,38 @@ +{%- import "change.md.tera" as m_c -%} + +{# This macro convert a priority level into readable output #} +{%- macro high_priority(p, changes) -%} + +{%- if p >= 7 -%} + {%- set prio = "‼️ HIGH" -%} + {%- set text = "This is a **high priority** release and you must upgrade as as soon as possible." -%} +{%- elif p >= 3 -%} + {%- set prio = "❗️ Medium" -%} + {%- set text = "This is a medium priority release and you should upgrade in a timely manner." -%} +{%- else -%} + {%- set prio = "Low" -%} + {%- set text = "This is a low priority release and you may upgrade at your convenience." -%} +{%- endif %} + + + +{%- if prio %} +{{prio}}: {{text}} + +{% if p >= 3 %} +The changes motivating this priority level are: +{% for pr in changes | sort(attribute="merged_at") -%} + {%- if pr.meta.C -%} + {%- if pr.meta.C.value == p %} +- {{ m_c::change(c=pr) }} +{%- if pr.meta.B and pr.meta.B.value == 7 %} (RUNTIME) +{% endif %} + {%- endif -%} + {%- endif -%} +{%- endfor -%} +{%- else -%} + +{%- endif -%} +{%- endif -%} + +{%- endmacro priority -%} diff --git a/bitacross-worker/scripts/changelog/templates/pre_release.md.tera b/bitacross-worker/scripts/changelog/templates/pre_release.md.tera new file mode 100644 index 0000000000..7d4ad42dd8 --- /dev/null +++ b/bitacross-worker/scripts/changelog/templates/pre_release.md.tera @@ -0,0 +1,11 @@ +{%- if env.PRE_RELEASE == "true" -%} +
⚠️ This is a pre-release + +**Release candidates** are **pre-releases** and may not be final. +Although they are reasonably tested, there may be additional changes or issues +before an official release is tagged. Use at your own discretion, and consider +only using final releases on critical production infrastructure. +
+{% else -%} + +{%- endif %} diff --git a/bitacross-worker/scripts/changelog/templates/template.md.tera b/bitacross-worker/scripts/changelog/templates/template.md.tera new file mode 100644 index 0000000000..2c61f3d5a1 --- /dev/null +++ b/bitacross-worker/scripts/changelog/templates/template.md.tera @@ -0,0 +1,33 @@ +{# This is the entry point of the template -#} + +{% include "pre_release.md.tera" -%} + +{% if env.PRE_RELEASE == "true" -%} +This pre-release contains the changes from `{{ env.REF1 | replace(from="refs/tags/", to="") }}` to `{{ env.REF2 | +replace(from="refs/tags/", to="") }}`. +{%- else -%} +This release contains the changes from `{{ env.REF1 | replace(from="refs/tags/", to="") }}` to `{{ env.REF2 | +replace(from="refs/tags/", to="") }}`. +{% endif -%} + +{# -- For now no pallet changes included -- #} +{# {%- set changes = worker.changes | concat(with=pallet.changes) -%}##} +{%- set changes = worker.changes -%} +{%- include "debug.md.tera" -%} + +{%- set CML = "[C]" -%} +{%- set WOR = "[W]" -%} +{%- set PAL = "[P]" -%} + +{# -- Manual free notes section -- #} +{% include "_free_notes.md.tera" -%} + +{# -- Important automatic section -- #} +{% include "global_priority.md.tera" -%} + +{# -- Important automatic section -- #} +{% include "global_challenge_level.md.tera" -%} + +{# --------------------------------- #} + +{% include "changes.md.tera" -%} diff --git a/bitacross-worker/scripts/init_env.sh b/bitacross-worker/scripts/init_env.sh new file mode 100755 index 0000000000..9b68a64b22 --- /dev/null +++ b/bitacross-worker/scripts/init_env.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# script that sets the correct environment variables to execute other scripts + +export SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +export PROJ_ROOT="$(dirname "$SCRIPT_DIR")" +export CLIENT_DIR="$PROJ_ROOT/cli" +export LOG_DIR="$PROJ_ROOT/log" +export CI_DIR="$PROJ_ROOT/ci" +export RUST_LOG=info,ws=warn,substrate_api_client=warn,ac_node_api=warn + +echo "Set environment variables:" +echo " BASH_SCRIPT_DIR: $SCRIPT_DIR" +echo " PROJ_ROOT: $PROJ_ROOT" +echo " CLIENT_DIR: $CLIENT_DIR" \ No newline at end of file diff --git a/bitacross-worker/scripts/launch.sh b/bitacross-worker/scripts/launch.sh new file mode 100755 index 0000000000..def2dd9e22 --- /dev/null +++ b/bitacross-worker/scripts/launch.sh @@ -0,0 +1,90 @@ +#!/usr/bin/env bash + +set -euo pipefail + +PARACHAIN="rococo" +ROOTDIR=$(git rev-parse --show-toplevel) +ROOTDIR="${ROOTDIR}/tee-worker" + +function usage() { + echo "Usage: $0 " + echo "" + echo " All mode apply to ${PARACHAIN} context." + echo " dev: start worker(s) together with local ${PARACHAIN} for development" + echo " staging: start worker(s) sync with staging ${PARACHAIN} on tee-staging server" + echo " prod: start worker(s) sync with production ${PARACHAIN} on polkadot.js" + echo " mock: start worker(s) together with local ${PARACHAIN} for development" +} + +function start_local_parachain() { + cd ${ROOTDIR} + echo "------------------------------------------------------------" + echo "Start local parachain: ${PARACHAIN} ..." + # TODO: only `rococo` is supported for the moment. And it's hard-coded inside `start_parachain.sh` + ./scripts/litentry/start_parachain.sh + if [ $? -ne 0 ]; then + exit 1 + fi +} + +function start_worker_for_dev() { + start_local_parachain + cd ${ROOTDIR} + worker_num=2 + echo "------------------------------------------------------------" + echo "Start ${worker_num} workers with dev ${PARACHAIN} ..." + ./scripts/launch_local_worker.sh -c true -n ${worker_num} -m "dev" +} + +function start_worker_for_staging() { + cd ${ROOTDIR} + worker_num=2 + # staging_parachain_url + url="wss://tee-staging.litentry.io" + # staging_parachain_port + port=443 + echo "------------------------------------------------------------" + echo "Start ${worker_num} workers with staging ${PARACHAIN} ..." + ./scripts/launch_local_worker.sh -c true -n ${worker_num} -u ${url} -p ${port} -m "staging" +} + +function start_worker_for_prod() { + cd ${ROOTDIR} + worker_num=2 + # production_parachain_url + url="wss://rpc.${PARACHAIN}-parachain-sg.litentry.io" + # production_parachain_port + port=443 + echo "------------------------------------------------------------" + echo "Start ${worker_num} workers with production ${PARACHAIN} ..." + ./scripts/launch_local_worker.sh -c true -n ${worker_num} -u ${url} -p ${port} -m "prod" +} + +function start_worker_for_mock() { + start_local_parachain + cd ${ROOTDIR} + worker_num=2 + echo "------------------------------------------------------------" + echo "Start ${worker_num} workers with local ${PARACHAIN} ..." + ./scripts/launch_local_worker.sh -c true -n ${worker_num} -m "mock" +} + + +[ $# -ne 1 ] && (usage; exit 1) +MODE=$1 + +if [ "$MODE" = "dev" ] || [ "$MODE" = "staging" ] || [ "$MODE" = "prod" ] || [ "$MODE" = "mock" ]; then + echo "Launch in $MODE mode" + start_worker_for_$MODE +else + echo "Unknow mode: $MODE" + usage; exit 1 +fi + +echo "Done" + + + + + + diff --git a/bitacross-worker/scripts/launch_local_worker.sh b/bitacross-worker/scripts/launch_local_worker.sh new file mode 100755 index 0000000000..ccacaaa262 --- /dev/null +++ b/bitacross-worker/scripts/launch_local_worker.sh @@ -0,0 +1,138 @@ +#!/usr/bin/env bash + +# TODO: Sanity check of parameters +while getopts ":c:n:u:p:m:" opt; do + case $opt in + c) + cleanup_flag=$OPTARG + ;; + n) + worker_num=$OPTARG + ;; + u) + node_url=$OPTARG + ;; + p) + node_port=$OPTARG + ;; + m) + mode=$OPTARG + ;; + esac +done + +CLEANUP=${cleanup_flag:-true} +WORKER_NUM=${worker_num:-1} + +NODE_URL=${node_url:-"ws://127.0.0.1"} # "ws://host.docker.internal" +NODE_PORT=${node_port:-"9944"} # "9946" + +# Fixed values: +WORKER_ENDPOINT="localhost" +MU_RA_PORT="3443" +UNTRUSTED_HTTP_PORT="4545" +TRUSTED_WORKER_PORT="2000" +UNTRUSTED_WORKER_PORT="3000" + +F_CLEAN="" +FSUBCMD_DEV="" +FSUBCMD_REQ_STATE="" + +WAIT_INTERVAL_SECONDS=10 +WAIT_ROUNDS=20 + +if [ "${CLEANUP}" = 'true' ]; then + F_CLEAN="--clean-reset" + FSUBCMD_DEV="--dev" +fi + +function wait_worker_is_initialized() +{ + for index in $(seq 1 $WAIT_ROUNDS); do + state=$(curl -s http://localhost:$1/is_initialized) + if [ "$state" == "I am initialized." ]; then + echo "Initialization successful: $state" + return + else + echo "sleep $WAIT_INTERVAL_SECONDS" + sleep $WAIT_INTERVAL_SECONDS + fi + done + echo + echo "Worker initialization failed" + exit 1 +} + +echo "Number of WORKER_NUM: ${WORKER_NUM}" +############################################################################## +### Start execution +############################################################################## + +ROOTDIR=$(git rev-parse --show-toplevel) +ROOTDIR="${ROOTDIR}/tee-worker" +RUST_LOG="info,litentry_worker=debug,ws=warn,sp_io=error,substrate_api_client=warn,\ +itc_parentchain_light_client=info,\ +jsonrpsee_ws_client=warn,jsonrpsee_ws_server=warn,enclave_runtime=debug,ita_stf=debug,\ +its_rpc_handler=warn,itc_rpc_client=warn,its_consensus_common=debug,its_state=warn,\ +its_consensus_aura=warn,aura*=warn,its_consensus_slots=warn,\ +itp_attestation_handler=debug,http_req=debug,itc_rest_client=debug,\ +itp_top_pool=debug,itc_parentchain_indirect_calls_executor=debug" + +# Create the log directory, in case not existed. +mkdir -p ${ROOTDIR}/log + +for ((i = 0; i < ${WORKER_NUM}; i++)); do + worker_name="worker${i}" + echo "" + echo "--------------------setup worker(${worker_name})----------------------------------------" + + if ((i > 0)); then + FSUBCMD_REQ_STATE="--request-state" + fi + + if [ "${CLEANUP}" = 'true' ]; then + echo "clear dir: ${ROOTDIR}/tmp/${worker_name}" + rm -rf "${ROOTDIR}"/tmp/"${worker_name}" + fi + mkdir -p "${ROOTDIR}"/tmp/"${worker_name}" + for Item in 'enclave.signed.so' 'key.txt' 'spid.txt' 'litentry-worker' 'bitacross-cli'; do + cp "${ROOTDIR}/bin/${Item}" "${ROOTDIR}"/tmp/"${worker_name}" + done + + cd "${ROOTDIR}"/tmp/${worker_name} || exit + echo "enter ${ROOTDIR}/tmp/${worker_name}" + + mu_ra_port=$((${MU_RA_PORT} + i)) + untrusted_http_port=$((${UNTRUSTED_HTTP_PORT} + i)) + trusted_worker_port=$((${TRUSTED_WORKER_PORT} + i)) + untrusted_worker_port=$((${UNTRUSTED_WORKER_PORT} + i)) + echo "${worker_name} ports: + mu-ra-port: ${mu_ra_port} + untrusted-http-port: ${untrusted_http_port} + trusted-worker-port: ${trusted_worker_port} + untrusted-worker-port: ${untrusted_worker_port} + " + + launch_command="RUST_LOG=${RUST_LOG} ./litentry-worker ${F_CLEAN} --ws-external \ +--mu-ra-external-address ${WORKER_ENDPOINT} \ +--mu-ra-port ${mu_ra_port} \ +--node-port ${NODE_PORT} \ +--node-url ${NODE_URL} \ +--trusted-external-address wss://${WORKER_ENDPOINT} \ +--trusted-worker-port ${trusted_worker_port} \ +--untrusted-external-address ws://${WORKER_ENDPOINT} \ +--untrusted-http-port ${untrusted_http_port} \ +--untrusted-worker-port ${untrusted_worker_port} \ +run --skip-ra ${FSUBCMD_DEV} ${FSUBCMD_REQ_STATE}" + + echo "${worker_name} command: ${launch_command}" + eval "${launch_command}" > "${ROOTDIR}"/log/${worker_name}.log 2>&1 & + echo "${worker_name}(litentry-worker) started successfully. log: ${ROOTDIR}/log/${worker_name}.log" + + if ((${WORKER_NUM} > 0)); then + wait_worker_is_initialized ${untrusted_http_port} + fi +done + +echo "" +echo "--- Setup work(s) done ---" diff --git a/bitacross-worker/scripts/litentry/cleanup.sh b/bitacross-worker/scripts/litentry/cleanup.sh new file mode 100755 index 0000000000..c1271e4eb9 --- /dev/null +++ b/bitacross-worker/scripts/litentry/cleanup.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +set -eo pipefail + +pid=$(ps aux | grep '[l]ocal-setup/launch' | awk '{print $2}') + +if [ ! -z "$pid" ]; then + echo "killing $pid" + kill -9 "$pid" +fi + +killall litentry-worker 2>/dev/null || true diff --git a/bitacross-worker/scripts/litentry/generate_parachain_artefacts.sh b/bitacross-worker/scripts/litentry/generate_parachain_artefacts.sh new file mode 100755 index 0000000000..e24bc4f940 --- /dev/null +++ b/bitacross-worker/scripts/litentry/generate_parachain_artefacts.sh @@ -0,0 +1,13 @@ +#!/bin/bash +set -euo pipefail + +ROOTDIR=$(git rev-parse --show-toplevel) +DESTDIR="$ROOTDIR/tee-worker/docker/litentry" + +# generate files +cd "$ROOTDIR" +make generate-docker-compose-rococo + +# copy files over to `DESTDIR` +mkdir -p "$DESTDIR" +cp docker/generated-rococo/* "$DESTDIR/" \ No newline at end of file diff --git a/bitacross-worker/scripts/litentry/identity_test.sh b/bitacross-worker/scripts/litentry/identity_test.sh new file mode 100755 index 0000000000..a9159714c8 --- /dev/null +++ b/bitacross-worker/scripts/litentry/identity_test.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +set -eo pipefail + +root_dir=$(git rev-parse --show-toplevel) +root_dir="$root_dir/tee-worker" + +#NODE PORT +node_port=9912 +node_url=ws://litentry-node + +worker_url=wss://tee-builder +worker_port=2000 + +CLIENT="./bitacross-cli --node-url ${node_url} --node-port ${node_port} --worker-url ${worker_url} --trusted-worker-port ${worker_port}" + +cd "$root_dir/bin" +./litentry-worker mrenclave | tee ~/mrenclave.b58 +MRENCLAVE=$(cat ~/mrenclave.b58) + +cd "$root_dir/tmp/worker1" + +# node-js: tweet_id: Buffer.from("1571829863862116352").toJSON().data.toString() +validation_data='{"Web2":{"Twitter":{"tweet_id":[49,53,55,49,56,50,57,56,54,51,56,54,50,49,49,54,51,53,50]}}}' + +# node-js: twitter_username: Buffer.from("litentry").toJSON().data.toString() +identity='{"web_type":{"Web2":"Twitter"},"handle":{"String":[108,105,116,101,110,116,114,121]}}' + +echo "create_identity" +RUST_LOG=warn ${CLIENT} trusted --mrenclave ${MRENCLAVE} create-identity "//Alice" "$identity" + +echo "set-challenge-code" +${CLIENT} trusted --mrenclave ${MRENCLAVE} set-challenge-code "//Alice" "$identity" 1134 + +echo "verify-identity-preflight" +RUST_LOG=info ${CLIENT} trusted --mrenclave ${MRENCLAVE} verify-identity-preflight "//Alice" "$identity" "$validation_data" diff --git a/bitacross-worker/scripts/litentry/release/ReadMe.md b/bitacross-worker/scripts/litentry/release/ReadMe.md new file mode 100644 index 0000000000..3faea84187 --- /dev/null +++ b/bitacross-worker/scripts/litentry/release/ReadMe.md @@ -0,0 +1,106 @@ + +# Release package + + +## Step 0: Preparation + +This package is generated from [litentry-parachain](https://github.com/litentry/litentry-parachain) +From the root folder ~/litentry-parachain/tee-worker/: +``` +make release-pkg +``` +A release package will be generated, within which there are: + +- enclave.sign.so +- litentry-worker +- config.json.eg +- prepare.sh + +
+ +## Step 1: Deploy on production + +Before starting the workers, please make sure the target parachain is already up and accessable. As well as the following directory/files: + +| Name | Value | Comment | +|-----|------|---| +| WORKER_DIR | /opt/worker | Working directory of workers | +| CONFIG_DIR | /opt/configs | Config directory which contains the following 4 secret files | +| +| CONFIG | config.json | Configs for twitter/discord/data provider/etc. url/keys. Take reference from config.json.eg | +| ACCOUNT | account.json | Substrate account exported json file | +| INTEL_KEY | key_production.txt | Intel SGX production key. Need to apply from Intel | +| INTEL_SPI | spid_production.txt | Intel SGX production spid. Need to apply from Intel | + +
+ +1. Extract the release package to one target location. Worker will be executed from there. Then execute `prepare.sh`: + ``` + ./prepare.sh + ``` + This script will generate out `MRENCLAVE` hex value (mrenclave.txt) and `Enclave Account` info (account.txt). They will be used later by ts scripts to setup enclave account. +
+ +2. Startup options. + + The service will start up like this example: + ``` + RUST_LOG=info,litentry_worker=debug,ws=warn,sp_io=error,substrate_api_client=warn,itc_parentchain_light_client=info,jsonrpsee_ws_client=warn,jsonrpsee_ws_server=warn,enclave_runtime=debug,ita_stf=debug,its_rpc_handler=warn,itc_rpc_client=warn,its_consensus_common=debug,its_state=warn,its_consensus_aura=warn,aura*=warn,its_consensus_slots=warn,itp_attestation_handler=debug,http_req=debug,lc_mock_server=warn,itc_rest_client=debug,lc_credentials=debug,lc_identity_verification=debug,lc_stf_task_receiver=debug,lc_stf_task_sender=debug,lc_data_providers=debug,itp_top_pool=debug,itc_parentchain_indirect_calls_executor=debug ./litentry-worker --clean-reset --ws-external --mu-ra-external-address localhost --mu-ra-port 3443 --node-port 9944 --node-url ws://127.0.0.1 --trusted-external-address wss://localhost --trusted-worker-port 2000 --untrusted-external-address ws://localhost --untrusted-http-port 4545 --untrusted-worker-port 3000 run --skip-ra --dev + ``` + The first part is RUST_LOG info. In production env, most of them will be disabled. Or `RUST_LOG=info` is enough. + + Starting from `./litentry-worker`, the following is the real startup options: + + ``` + USAGE: + litentry-worker [FLAGS] [OPTIONS] + + FLAGS: + -c, --clean-reset Cleans and purges any previous state and key files and generates them anew before starting. + --enable-metrics Enable the metrics HTTP server to serve metrics + --help Prints help information + -V, --version Prints version information + --ws-external Set this flag in case the worker should listen to external requests. + + OPTIONS: + -i, --metrics-port + Set the port on which the metrics are served. [default: 8787] + + -M, --mu-ra-external-address + Set the mutual remote attestation worker address to be retrieved by a trusted rpc call. If no port is given, the same as in `mu-ra-port` will be used. + -r, --mu-ra-port + Set the websocket port to listen for mu-ra requests [default: 3443] + + -p, --node-port + Set the websocket port to listen for substrate events [default: 9944] + + -u, --node-url + Set the node server protocol and IP address [default: ws://127.0.0.1] + + -T, --trusted-external-address + Set the trusted worker address to be advertised on the parentchain. If no port is given, the same as in + `trusted-worker-port` will be used. + -P, --trusted-worker-port + Set the trusted websocket port of the worker, running directly in the enclave. [default: 2000] + + -U, --untrusted-external-address + Set the untrusted worker address to be retrieved by a trusted rpc call. If no port is given, the same as in + `untrusted-worker-port` will be used. + -h, --untrusted-http-port Set the port for the untrusted HTTP server + -w, --untrusted-worker-port + Set the untrusted websocket port of the worker [default: 2001] + + SUBCOMMANDS: + dump-ra Perform RA and dump cert to disk + help Prints this message or the help of the given subcommand(s) + init-shard Initialize new shard (do this only if you run the first worker for that shard). if shard is not + specified, the MRENCLAVE is used instead + migrate-shard Migrate shard + mrenclave Dump mrenclave to stdout. base58 encoded. + request-state join a shard by requesting key provisioning from another worker + run Start the litentry-worker + shielding-key Get the public RSA3072 key from the TEE to be used to encrypt requests + signing-key Get the public ed25519 key the TEE uses to sign messages and extrinsics + test Run tests involving the enclave + ``` + diff --git a/bitacross-worker/scripts/litentry/release/build.sh b/bitacross-worker/scripts/litentry/release/build.sh new file mode 100755 index 0000000000..aafd70210d --- /dev/null +++ b/bitacross-worker/scripts/litentry/release/build.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +# this script builds the release artefacts for TEE client and/or the enclave + +set -euo pipefail + +function usage() { + echo "Usage: $0 if-build-worker if-build-enclave" + echo "Example:" + echo " $0 true true" +} + +[ $# -ne 2 ] && (usage; exit 1) + +echo "build worker: $1" +echo "build enclave: $2" + +ROOTDIR=$(git rev-parse --show-toplevel) +WORKERDIR="$ROOTDIR/tee-worker" + +# hardcoded sgx signing key, adjust it accordingly if you call the script manually +SGX_COMMERCIAL_KEY="/opt/enclave_release/sgx_sign_key.pem" + +if [ ! -f "$SGX_COMMERCIAL_KEY" ]; then + echo "Cannot find SGX sign key under $SGX_COMMERCIAL_KEY" + exit 1 +fi + +DESTDIR="$WORKERDIR/enclave_release" +[ -d "$DESTDIR" ] && rm -rf "$DESTDIR" +mkdir -p "$DESTDIR" + +cd "$WORKERDIR" + +make clean + +export SGX_PRODUCTION=1 +export SGX_COMMERCIAL_KEY="$SGX_COMMERCIAL_KEY" +if [ "$1" = "true" ]; then + make service + cp bin/litentry-worker "$DESTDIR" +fi +if [ "$2" = "true" ]; then + make bin/enclave.signed.so + cp bin/enclave.signed.so "$DESTDIR" + make mrenclave 2>&1 | grep MRENCLAVE | awk '{print $2}' > "$DESTDIR/mrenclave.txt" +fi + +echo "Build tee done" +ls -l "$DESTDIR" diff --git a/bitacross-worker/scripts/litentry/release/config.json.eg b/bitacross-worker/scripts/litentry/release/config.json.eg new file mode 100644 index 0000000000..acfdbc872a --- /dev/null +++ b/bitacross-worker/scripts/litentry/release/config.json.eg @@ -0,0 +1,11 @@ +{ + "twitter_official_url": "https://api.twitter.com", + "twitter_litentry_url": "", + "twitter_auth_token_v2": "abcdefghijklmnopqrstuvwxyz", + "discord_official_url": "https://discordapp.com", + "discord_litentry_url": "", + "discord_auth_token": "ABCDEFGHIJKLMNOPQRSTUVWXYZ", + "achainable_url": "https://graph.tdf-labs.io/", + "achainable_auth_key": "88888888-4444-4444-4444-1234567890ab", + "credential_endpoint": "" +} \ No newline at end of file diff --git a/bitacross-worker/scripts/litentry/release/deploy.sh b/bitacross-worker/scripts/litentry/release/deploy.sh new file mode 100755 index 0000000000..72c7060dfa --- /dev/null +++ b/bitacross-worker/scripts/litentry/release/deploy.sh @@ -0,0 +1,555 @@ +#!/bin/bash + +set -eo pipefail + +# This script is used to perform actions on the target host, including: +# - generate: generate the systemd service files from the template +# - restart: restart the parachain, or the worker, or both +# - upgrade-worker: uprade the worker0 to the rev in local repo +# +# TODO: +# the combinations of flags are not yet well verified/organised, especially the following: +# --only-worker +# --build +# --discard + +# ------------------------------ +# path setting +# ------------------------------ + +ROOTDIR=$(git rev-parse --show-toplevel) +BASEDIR=/opt/litentry +PARACHAIN_BASEDIR="$BASEDIR/parachain" +WORKER_BASEDIR="$BASEDIR/worker" +BACKUP_BASEDIR="$BASEDIR/backup" +LOG_BACKUP_BASEDIR="$BACKUP_BASEDIR/log" +WORKER_BACKUP_BASEDIR="$BACKUP_BASEDIR/worker" +RELAYCHAIN_ALICE_BASEDIR="$PARACHAIN_BASEDIR/relay-alice" +RELAYCHAIN_BOB_BASEDIR="$PARACHAIN_BASEDIR/relay-bob" +PARACHAIN_ALICE_BASEDIR="$PARACHAIN_BASEDIR/para-alice" + +# ------------------------------ +# default arg setting +# ------------------------------ + +BUILD=false +DISCARD=false +WORKER_CONFIG= +CHAIN=rococo +ONLY_WORKER=false +PARACHAIN_HOST=localhost +PARACHAIN_PORT=9944 +DOCKER_IMAGE=litentry/litentry-parachain:tee-prod +COPY_FROM_DOCKER=false +PRODUCTION=false +ACTION= + +# ------------------------------ +# Some global setting +# ------------------------------ + +WORKER_COUNT= +PARACHAIN_ID= +OLD_MRENCLAVE= +NEW_MRENCLAVE= +OLD_SHARD= +LATEST_FINALIZED_BLOCK= + +SGX_SDK=/opt/intel/sgxsdk +SGX_ENCLAVE_SIGNER=$SGX_SDK/bin/x64/sgx_sign + +# ------------------------------ +# main() +# ------------------------------ + +function main { + # 0/ check if $USER has sudo + if sudo -l -U $USER 2>/dev/null | grep -q 'may run the following'; then + source "$SGX_SDK/environment" + else + echo "$USER doesn't have sudo permission" + exit 1 + fi + + # 1/ create folders if missing + sudo mkdir -p "$BASEDIR" + sudo chown $USER:$GROUPS "$BASEDIR" + for d in "$LOG_BACKUP_BASEDIR" "$WORKER_BACKUP_BASEDIR" "$RELAYCHAIN_ALICE_BASEDIR" "$RELAYCHAIN_BOB_BASEDIR" \ + "$PARACHAIN_ALICE_BASEDIR" "$WORKER_BASEDIR"; do + mkdir -p "$d" + done + + # 2/ parse command lines + echo "Parsing command line ..." + while [ $# -gt 0 ]; do + case "$1" in + -h|--help) + display_help + exit 0 + ;; + -b|--build) + BUILD=true + shift + ;; + -d|--discard) + DISCARD=true + shift + ;; + -c|--config) + WORKER_CONFIG="$(realpath -s $2)" + shift 2 + ;; + -a|--only-worker) + ONLY_WORKER=true + shift + ;; + -x|--chain) + CHAIN="$2" + shift 2 + ;; + -p|--parachain-port) + PARACHAIN_PORT="$2" + shift 2 + ;; + -z|--parachain-host) + PARACHAIN_HOST="$2" + shift 2 + ;; + -v|--copy-from-docker) + COPY_FROM_DOCKER=true + DOCKER_IMAGE="$2" + shift 2 + ;; + --prod) + PRODUCTION=true + shift + ;; + generate|restart|upgrade-worker) + ACTION="$1" + shift + ;; + *) + echo "Error: unknown option or subcommand $1" + display_help + exit 1 + ;; + esac + done + + # 3/ sanity checks + if [ ! -f "$WORKER_CONFIG" ]; then + echo "Worker config not found: $WORKER_CONFIG" + exit 1 + fi + + WORKER_COUNT=$(cat "$WORKER_CONFIG" | jq '.workers | length') + echo "Worker count: $WORKER_COUNT" + + # TODO: check flags conflict, e.g. + # - having `--discard` together with `upgrade-worker` doesn't make sense + # - `upgrade-worker` should ignore the `--only-worker` flag + + # 4/ main business logic + case "$ACTION" in + generate) + backup_services + generate_services + exit + ;; + restart) + backup_logs + backup_workers + stop_services + prune + build + setup_working_dir + if [ "$ONLY_WORKER" = true ]; then + remove_clean_reset + fi + restart_services + exit + ;; + upgrade-worker) + # build the new worker, the code must be under $ROOTDIR/tee-worker already + build_worker + # update the schedule + set_scheduled_enclave + + # wait until sidechain stalls + wait_for_sidechain + backup_workers + stop_worker_services + get_old_mrenclave + # TODO: actually we only need the copy-up + setup_working_dir + migrate_shard + remove_clean_reset + restart_services + exit + ;; + *) + echo "Unknown action: $ACTION" + exit 1 ;; + esac +} + +# ------------------------------ +# helper functions +# ------------------------------ + +function print_divider { + echo "------------------------------------------------------------" +} + +function display_help { + echo "usage: ./deploy.sh [options]" + echo "" + echo "subcommands:" + echo " generate Generate the parachain and worker systemd files" + echo " restart Restart the services" + echo " upgrade-worker Upgrade the worker" + echo "" + echo "options:" + echo " -h, --help Display this help message and exit" + echo " -b, --build Build the parachain and worker binaries (default: false)" + echo " -d, --discard Clean the existing state for parachain and worker (default: false)" + echo " -c, --config Config file for the worker" + echo " -a, --only-worker Start only the worker (default: false)" + echo " -x, --chain Chain type for launching the parachain network (default: rococo)" + echo " -h, --parachain-host Parachain ws URL (default: localhost)" + echo " -p, --parachain-port Parachain ws port (default: 9944)" + echo " -v, --copy-from-docker Copy the parachain binary from a docker image (default: litentry/litentry-parachain:tee-prod)" + echo " --prod Use a prod configuration to build and run the worker (default: false)" + echo "" + echo "examples:" + echo " ./deploy.sh generate --config tmp.json" + echo " ./deploy.sh restart --config tmp.json --discard --build" + echo " ./deploy.sh restart --config tmp.json --only-worker" + echo " ./deploy.sh upgrade-worker --config tmp.json --only-worker" + echo "" + echo "notes:" + echo " - This script requires an OS that supports systemd." + echo " - It is mandatory to provide a JSON config file for the worker." + echo " - jq is required to be installed on the system " + echo "" + echo "For more information or assistance, please contact Litentry parachain team." +} + +# TODO: in fact, this function only backs up the parachain logs +# maybe we want to remove it as it's not so critical anyway +function backup_logs { + echo "Backing up logs ..." + now=$(date +"%Y%m%d-%H%M%S") + outdir="$LOG_BACKUP_BASEDIR/log-$now" + mkdir -p "$outdir" + cp "$PARACHAIN_BASEDIR"/*.log "$outdir" || true + echo "Logs backed up into $outdir" +} + +function backup_workers { + echo "Backing up workers ..." + now=$(date +"%Y%m%d-%H%M%S") + cd "$WORKER_BASEDIR" || exit + for i in $(ls -d * 2>/dev/null); do + outdir="$WORKER_BACKUP_BASEDIR/$i-$now" + cp -rf "$i" "$outdir" + echo "Worker backed up into $outdir" + done +} + +function backup_services { + echo "Backing up services ..." + now=$(date +"%Y%m%d-%H%M%S") + cd /etc/systemd/system || exit + outdir="$WORKER_BACKUP_BASEDIR/service-$now" + mkdir -p "$outdir" + for f in para-alice.service relay-alice.service relay-bob.service $(ls worker*.service 2>/dev/null); do + cp "$f" "$outdir" || true + done +} + +function prune { + if [ "$DISCARD" = true ]; then + echo "Pruning the existing state ..." + rm -rf "$PARACHAIN_BASEDIR"/* + rm -rf "$WORKER_BASEDIR"/* + fi +} + +function generate_services { + echo "Generating systemd service files ..." + cd "$ROOTDIR/tee-worker/scripts/litentry/release" + cp template/* . + sed -i "s/CHAIN/$CHAIN/g" *.service + sed -i "s/USER/$USER/g" *.service + for ((i = 0; i < WORKER_COUNT; i++)); do + cp worker.service worker$i.service + sed -i "s/NUMBER/$i/g" worker$i.service + # populate args + flags=$(cat "$WORKER_CONFIG" | jq -r ".workers[$i].flags[]") + subcommand_flags=$(cat "$WORKER_CONFIG" | jq -r ".workers[$i].subcommand_flags[]") + args= + for flag in $flags; do + args+=" $flag" + done + args+=" run" + for subcommand_flag in $subcommand_flags; do + args+=" $subcommand_flag" + done + sed -i "s;ARGS;$args;" worker$i.service + done + rm worker.service + sudo cp *.service -f /etc/systemd/system/ + rm *.service + sudo systemctl daemon-reload + echo "Done, please check files under /etc/systemd/system/" + echo "Restart the services to take effect" +} + +function build_worker { + echo "Building worker ..." + cd $ROOTDIR/tee-worker/ || exit + if [ "$PRODUCTION" = true ]; then + # we will get an error if SGX_COMMERCIAL_KEY is not set for prod + SGX_PRODUCTION=1 make + else + # use SW mode for dev + SGX_MODE=SW make + fi +} + +# TODO: take github rev into consideration +function build { + if [ "$BUILD" = true ]; then + echo "Building the parachain and worker binaries ..." + + # download polkadot + echo "Downloading polkadot binary ..." + url="https://github.com/paritytech/polkadot/releases/download/v0.9.42/polkadot" + polkadot_bin="$PARACHAIN_BASEDIR/polkadot" + wget -O "$polkadot_bin" -q "$url" + chmod a+x "$polkadot_bin" + if [ ! -s "$polkadot_bin" ]; then + echo "$polkadot_bin is 0 bytes, download URL: $url" && exit 1 + fi + if ! "$polkadot_bin" --version &> /dev/null; then + echo "Cannot execute $polkadot_bin, wrong executable?" && exit 1 + fi + + # pull or build parachain + if [ "$COPY_FROM_DOCKER" = true ]; then + echo "Pulling binary from $DOCKER_IMAGE ..." + docker pull "$DOCKER_IMAGE" + docker cp "$(docker create --rm $DOCKER_IMAGE):/usr/local/bin/litentry-collator" "$PARACHAIN_BASEDIR" + else + echo "Building parachain binary ..." + cd "$ROOTDIR" || exit + if [ "$PRODUCTION" = true ]; then + cargo build --locked --profile production + else + pwd + make build-node + fi + cp "$ROOTDIR/target/release/litentry-collator" "$PARACHAIN_BASEDIR" + fi + chmod a+x "$PARACHAIN_BASEDIR/litentry-collator" + fi +} + +function restart_services { + sudo systemctl daemon-reload + if [ "$ONLY_WORKER" = false ]; then + echo "Restarting parachain services ..." + + cd "$PARACHAIN_BASEDIR" || exit + ./polkadot build-spec --chain rococo-local --disable-default-bootnode --raw > rococo-local-chain-spec.json + ./litentry-collator export-genesis-state --chain $CHAIN-dev > genesis-state + ./litentry-collator export-genesis-wasm --chain $CHAIN-dev > genesis-wasm + + sudo systemctl restart relay-alice.service + sleep 5 + sudo systemctl restart relay-bob.service + sleep 5 + sudo systemctl restart para-alice.service + sleep 5 + register_parachain + fi + + echo "Restarting worker services ..." + for ((i = 0; i < WORKER_COUNT; i++)); do + sudo systemctl restart "worker$i.service" + sleep 5 + done + echo "Done" +} + +function stop_worker_services { + echo "Stopping worker services ..." + for ((i = 0; i < WORKER_COUNT; i++)); do + sudo systemctl stop "worker$i.service" + sleep 5 + done +} + +function stop_parachain_services { + echo "Stopping parachain services ..." + sudo systemctl stop para-alice.service relay-alice.service relay-bob.service +} + +function stop_services { + stop_worker_services + + # TODO: it means we can't stop parachain service alone + # this needs to be done directly via `systemctl` + if [ "$ONLY_WORKER" = false ]; then + stop_parachain_services + fi +} + +function register_parachain { + echo "Register parathread now ..." + cd "$ROOTDIR" || exit + export PARACHAIN_ID=$(grep DEFAULT_PARA_ID node/src/chain_specs/$CHAIN.rs | grep u32 | sed 's/.* = //;s/\;//') + cd "$ROOTDIR/ts-tests" || exit + if [[ -z "$NODE_ENV" ]]; then + echo "NODE_ENV=ci" > .env + else + echo "NODE_ENV=$NODE_ENV" > .env + fi + # The genesis state path file needs to be updated as it is hardcoded to be /tmp/parachain_dev + jq --arg genesis_state "$PARACHAIN_BASEDIR/genesis-state" --arg genesis_wasm "$PARACHAIN_BASEDIR/genesis-wasm" '.genesis_state_path = $genesis_state | .genesis_wasm_path = $genesis_wasm' config.ci.json > config.ci.json.1 + mv config.ci.json.1 config.ci.json + pnpm install + pnpm run register-parathread 2>&1 | tee "$PARACHAIN_BASEDIR/register-parathread.log" + print_divider + + echo "Upgrade parathread to parachain now ..." + # Wait for 90s to allow onboarding finish, after that we do the upgrade + sleep 90 + pnpm run upgrade-parathread 2>&1 | tee "$PARACHAIN_BASEDIR/upgrade-parathread.log" + print_divider + + echo "done. please check $PARACHAIN_BASEDIR for generated files if need" + print_divider + git restore config.ci.json +} + +function setup_working_dir { + echo "Setting up working dir ..." + cd "$ROOTDIR/tee-worker/bin" || exit + + if [ "$PRODUCTION" = false ]; then + for f in 'key.txt' 'spid.txt'; do + [ -f "$f" ] || touch "$f" + done + fi + + for ((i = 0; i < WORKER_COUNT; i++)); do + worker_dir="$WORKER_BASEDIR/w$i" + mkdir -p "$worker_dir" + for f in 'key.txt' 'spid.txt' 'enclave.signed.so' 'litentry-worker'; do + [ -f "$f" ] && cp -f "$f" "$worker_dir" + done + + cd "$worker_dir" + [ -f light_client_db.bin/db.bin.backup ] && cp -f light_client_db.bin/db.bin.backup light_client_db.bin/db.bin + + enclave_account=$(./litentry-worker signing-key | grep -oP '^Enclave account: \K.*$$') + + if [ "$PRODUCTION" = true ]; then + echo "Transferring balance to the enclave account $enclave_account ..." + cd $ROOTDIR/scripts/ts-utils/ || exit + pnpm install + pnpm exec ts-node transfer.ts $enclave_account + fi + done +} + +function get_old_mrenclave { + cd "$WORKER_BASEDIR/w0" || exit + OLD_SHARD=$(./litentry-worker mrenclave) + $SGX_ENCLAVE_SIGNER dump -enclave ./enclave.signed.so -dumpfile df.out + OLD_MRENCLAVE=$($ROOTDIR/tee-worker/extract_identity < df.out | awk '{print $2}') + rm df.out + echo "old shard: $OLD_SHARD" + echo "old mrenclave: $OLD_MRENCLAVE" +} + +function set_scheduled_enclave { + echo "Setting scheduled enclave ..." + cd $ROOTDIR/tee-worker || exit + NEW_MRENCLAVE=$(make mrenclave 2>&1 | grep MRENCLAVE | awk '{print $2}') + echo "new mrenclave: $NEW_MRENCLAVE" + + latest_sidechain_block + + echo "Setting up the new worker on chain ..." + cd $ROOTDIR/ts-tests/ || exit + pnpm install + pnpm run setup-enclave $NEW_MRENCLAVE $SCHEDULED_UPDATE_BLOCK +} + +function wait_for_sidechain { + echo "Waiting for sidechain to reach block $SCHEDULED_UPDATE_BLOCK ..." + found=false + for _ in $(seq 1 30); do + sleep 20 + block_number=$(grep -F 'Enclave produced sidechain blocks' $WORKER_BASEDIR/w0/worker.log | tail -n 1 | sed 's/.*\[//;s/]//') + echo "current sidechain block: $block_number" + if [ $((block_number+1)) -eq $SCHEDULED_UPDATE_BLOCK ]; then + echo "we should stall soon ..." + fi + if tail -n 50 $WORKER_BASEDIR/w0/worker.log | grep -q "Skipping sidechain block $SCHEDULED_UPDATE_BLOCK due to mismatch MRENCLAVE"; then + echo "we reach $SCHEDULED_UPDATE_BLOCK now" + found=true + break + fi + done + if [ $found = false ]; then + echo "not reached, timeout" + exit 1 + fi +} + +function migrate_shard { + echo "Migrating shards for workers ..." + for ((i = 0; i < WORKER_COUNT; i++)); do + cd "$WORKER_BASEDIR/w$i" || exit + echo "old MRENCLAVE: $OLD_MRENCLAVE" + echo "new MRENCLAVE: $NEW_MRENCLAVE" + ./litentry-worker migrate-shard --old-shard $OLD_MRENCLAVE --new-shard $NEW_MRENCLAVE + + cd shards || exit + rm -rf $OLD_SHARD + done + echo "Done" +} + +function remove_clean_reset { + echo "Removing --clean-reset flag for workers ..." + for ((i = 0; i < WORKER_COUNT; i++)); do + sudo sed -i 's/--clean-reset//' /etc/systemd/system/worker$i.service + done + echo "Done" +} + +# TODO: here we only read worker0 logs here +function latest_sidechain_block { + block_number=$(grep -F 'Enclave produced sidechain blocks' $WORKER_BASEDIR/w0/worker.log | tail -n 1 | sed 's/.*\[//;s/]//') + SCHEDULED_UPDATE_BLOCK=$((block_number + 30)) + echo "Current sidechain block: $block_number, scheduled update block: $SCHEDULED_UPDATE_BLOCK" +} + +# TODO: unused +function _latest_parentchain_block { + # JSON-RPC request payload + request='{"jsonrpc":"2.0","id":1,"method":"chain_getHeader","params":[]}' + + # Make the JSON-RPC request and retrieve the latest finalized block + response=$(curl -s -H "Content-Type: application/json" -d "$request" http://$PARACHAIN_HOST:$PARACHAIN_PORT) + hex_number=$(echo "$response" | grep -oP '(?<="number":")[^"]+') + LATEST_FINALIZED_BLOCK=$(printf "%d" "$hex_number") + echo "Current parachain block: $LATEST_FINALIZED_BLOCK" +} + +main "$@" diff --git a/bitacross-worker/scripts/litentry/release/prepare.sh b/bitacross-worker/scripts/litentry/release/prepare.sh new file mode 100755 index 0000000000..e9817e8d71 --- /dev/null +++ b/bitacross-worker/scripts/litentry/release/prepare.sh @@ -0,0 +1,50 @@ +#!/bin/bash +set -euo pipefail + + +# This WORKER_DIR is the directory where worker will start from. +WORKER_DIR=/opt/worker/ + +# CONFIG_DIR provides all the necessary private secret files. +# They should only exist on the running machine. +CONFIG_DIR=/opt/configs/ +CONFIG=$CONFIG_DIR/config.json +ACCOUNT=$CONFIG_DIR/private_account.json +INTEL_KEY=$CONFIG_DIR/key_production.txt +INTEL_SPID=$CONFIG_DIR/spid_production.txt + +############################################################################## +# Don't edit anything from here +if [[ ! -e "$WORKER_DIR" ]]; then + mkdir -p $WORKER_DIR +fi + +for Item in $CONFIG $ACCOUNT $INTEL_KEY $INTEL_SPID; do + if [[ ! -e "$Item" ]]; then + echo "Error: $Item is not a valid path." + exit 1 + fi +done + +# Generate keys and copy around. +SRC_DIR=$(dirname "$0") +cd $SRC_DIR + +./litentry-worker signing-key | grep -oP '^Enclave account: \K.*$$' > enclave_account.txt +echo "Enclave account is prepared inside enclave_account.txt" + +./litentry-worker shielding-key + +for Item in 'enclave.signed.so' 'litentry-worker' 'aes_key_sealed.bin' 'ed25519_key_sealed.bin' 'enclave-shielding-pubkey.json' 'enclave-signing-pubkey.bin' 'rsa3072_key_sealed.bin' 'sidechain_db'; do + cp -r "${Item}" "${WORKER_DIR}" +done + +cp $CONFIG "${WORKER_DIR}/config.json" +cp $INTEL_KEY "${WORKER_DIR}/key_production.txt" +cp $INTEL_SPID "${WORKER_DIR}/spid_production.txt" + +# Comment out for the moment. Need to adapt together with PR-1587 ts-utils. +cp $ACCOUNT "${WORKER_DIR}/ts-utils/private_account.json" +cp "enclave_account.txt" "${WORKER_DIR}/ts-utils/enclave_account.txt" +cp "mrenclave.txt" "${WORKER_DIR}/ts-utils/mrenclave.txt" + diff --git a/bitacross-worker/scripts/litentry/release/template/para-alice.service b/bitacross-worker/scripts/litentry/release/template/para-alice.service new file mode 100644 index 0000000000..ab1e88e3cd --- /dev/null +++ b/bitacross-worker/scripts/litentry/release/template/para-alice.service @@ -0,0 +1,15 @@ +[Unit] +Description=Litentry Parachain + +[Service] +Type=simple +User=USER +WorkingDirectory=/opt/litentry/parachain +ExecStart=/opt/litentry/parachain/litentry-collator --base-path /opt/litentry/parachain/para-alice --alice --collator --force-authoring --chain CHAIN-dev --unsafe-ws-external --unsafe-rpc-external --rpc-cors=all --ws-max-connections 3000 --port 30333 --ws-port 9944 --rpc-port 9933 --execution wasm --state-pruning archive --blocks-pruning archive -- --execution wasm --chain /opt/litentry/parachain/rococo-local-chain-spec.json --port 30332 --ws-port 9943 --rpc-port 9932 +Restart=always +RestartSec=120 +StandardOutput=append:/opt/litentry/parachain/para.alice.log +StandardError=inherit + +[Install] +WantedBy=multi-user.target diff --git a/bitacross-worker/scripts/litentry/release/template/relay-alice.service b/bitacross-worker/scripts/litentry/release/template/relay-alice.service new file mode 100644 index 0000000000..1263086c97 --- /dev/null +++ b/bitacross-worker/scripts/litentry/release/template/relay-alice.service @@ -0,0 +1,15 @@ +[Unit] +Description=Litentry Relaychain Alice + +[Service] +Type=simple +User=USER +WorkingDirectory=/opt/litentry/parachain +ExecStart=/opt/litentry/parachain/polkadot --base-path /opt/litentry/parachain/relay-alice --chain /opt/litentry/parachain/rococo-local-chain-spec.json --alice --port 30336 --ws-port 9946 --rpc-port 9936 +Restart=always +RestartSec=120 +StandardOutput=append:/opt/litentry/parachain/relay.alice.log +StandardError=inherit + +[Install] +WantedBy=multi-user.target diff --git a/bitacross-worker/scripts/litentry/release/template/relay-bob.service b/bitacross-worker/scripts/litentry/release/template/relay-bob.service new file mode 100644 index 0000000000..14e297bbdb --- /dev/null +++ b/bitacross-worker/scripts/litentry/release/template/relay-bob.service @@ -0,0 +1,15 @@ +[Unit] +Description=Litentry Relaychain Bob + +[Service] +Type=simple +User=USER +WorkingDirectory=/opt/litentry/parachain +ExecStart=/opt/litentry/parachain/polkadot --base-path /opt/litentry/parachain/relay-bob --chain /opt/litentry/parachain/rococo-local-chain-spec.json --bob --port 30337 --ws-port 9947 --rpc-port 9937 +Restart=always +RestartSec=120 +StandardOutput=append:/opt/litentry/parachain/relay.bob.log +StandardError=inherit + +[Install] +WantedBy=multi-user.target diff --git a/bitacross-worker/scripts/litentry/release/template/worker.service b/bitacross-worker/scripts/litentry/release/template/worker.service new file mode 100644 index 0000000000..e218d60278 --- /dev/null +++ b/bitacross-worker/scripts/litentry/release/template/worker.service @@ -0,0 +1,14 @@ +[Unit] +Description=Litentry TEE worker + +[Service] +Type=simple +User=USER +Environment='RUST_LOG=info,litentry_worker=debug,ws=warn,sp_io=error,substrate_api_client=warn,itc_parentchain_light_client=info,jsonrpsee_ws_client=warn,jsonrpsee_ws_server=warn,enclave_runtime=debug,ita_stf=debug,its_rpc_handler=warn,itc_rpc_client=warn,its_consensus_common=debug,its_state=warn,its_consensus_aura=warn,aura*=warn,its_consensus_slots=warn,itp_attestation_handler=debug,http_req=debug,lc_mock_server=warn,itc_rest_client=debug,lc_credentials=debug,lc_identity_verification=debug,lc_stf_task_receiver=debug,lc_stf_task_sender=debug,lc_data_providers=debug,itp_top_pool=debug,itc_parentchain_indirect_calls_executor=debug' +WorkingDirectory=/opt/litentry/worker/wNUMBER +ExecStart=/bin/bash -c 'cd /opt/litentry/worker/wNUMBER && source /opt/intel/sgxsdk/environment && ./litentry-worker ARGS' +StandardOutput=append:/opt/litentry/worker/wNUMBER/worker.log +StandardError=inherit + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/bitacross-worker/scripts/litentry/start_parachain.sh b/bitacross-worker/scripts/litentry/start_parachain.sh new file mode 100755 index 0000000000..2963fee5b2 --- /dev/null +++ b/bitacross-worker/scripts/litentry/start_parachain.sh @@ -0,0 +1,22 @@ +#!/bin/bash +set -euo pipefail + +# check the port before launching the parachain +# this script is called in bothe launch.sh and launch.py +# +# please note this check doesn't apply to standalone litentry-node +# as it's started without any pre-check script bound +# +# 9944: default ws port for parachain node +# 30333: default p2p port for relaychain node +# 4545: default untrusted-http-port for tee-worker (see config.json) +for p in ${CollatorWSPort:-9944} ${CollatorPort:-30333} ${UntrustedHttpPort:-4545}; do + if [ ! -z "$(netstat -nat | grep :$p)" ]; then + echo "port $p is in use, quit now" + exit 1 + fi +done + +ROOTDIR=$(git rev-parse --show-toplevel) +cd "$ROOTDIR" +make launch-docker-rococo diff --git a/bitacross-worker/scripts/litentry/stop_parachain.sh b/bitacross-worker/scripts/litentry/stop_parachain.sh new file mode 100755 index 0000000000..759083e8e5 --- /dev/null +++ b/bitacross-worker/scripts/litentry/stop_parachain.sh @@ -0,0 +1,6 @@ +#!/bin/bash +set -euo pipefail + +ROOTDIR=$(git rev-parse --show-toplevel) +cd "$ROOTDIR" +make clean-docker-rococo || true diff --git a/bitacross-worker/scripts/litentry/ubuntu_setup.sh b/bitacross-worker/scripts/litentry/ubuntu_setup.sh new file mode 100755 index 0000000000..ef02a6418e --- /dev/null +++ b/bitacross-worker/scripts/litentry/ubuntu_setup.sh @@ -0,0 +1,71 @@ +#!/bin/bash + +set -eo pipefail + +# most is copied from +# https://github.com/apache/incubator-teaclave-sgx-sdk/blob/v1.1.4/dockerfile/Dockerfile.2004.nightly + +# install rust +curl -s https://sh.rustup.rs -sSf | sh -s -- -y +# shellcheck source=${HOME}/.cargo/env +source ${HOME}/.cargo/env +rustup show + +# install substrate build deps +sudo apt-get update +sudo apt-get install -y cmake pkg-config libssl-dev git clang libclang-dev gnupg2 protobuf-compiler + +# install llvm +sudo apt-get update +wget https://apt.llvm.org/llvm.sh && chmod +x llvm.sh && sudo ./llvm.sh 10 + +# override binutils +wget https://download.01.org/intel-sgx/sgx-linux/2.20/as.ld.objdump.r4.tar.gz +tar xzf as.ld.objdump.r4.tar.gz +sudo cp -f external/toolset/ubuntu20.04/* /usr/bin/ + +# install sgx_sdk +SDK_URL="https://download.01.org/intel-sgx/sgx-linux/2.20/distro/ubuntu20.04-server/sgx_linux_x64_sdk_2.20.100.4.bin" +curl -o sdk.sh $SDK_URL +chmod a+x sdk.sh +echo -e 'no\n/opt' | ./sdk.sh +source /opt/sgxsdk/environment + +# install runtime sgx libs (psw) +CODENAME=focal +VERSION=2.20.100.4-focal1 +DCAP_VERSION=1.17.100.4-focal1 + +curl -fsSL https://download.01.org/intel-sgx/sgx_repo/ubuntu/intel-sgx-deb.key | sudo apt-key add - && \ +sudo add-apt-repository "deb https://download.01.org/intel-sgx/sgx_repo/ubuntu $CODENAME main" && \ +sudo apt-get update && \ +sudo apt-get install -y \ + libsgx-headers=$VERSION \ + libsgx-ae-epid=$VERSION \ + libsgx-ae-le=$VERSION \ + libsgx-ae-pce=$VERSION \ + libsgx-aesm-ecdsa-plugin=$VERSION \ + libsgx-aesm-epid-plugin=$VERSION \ + libsgx-aesm-launch-plugin=$VERSION \ + libsgx-aesm-pce-plugin=$VERSION \ + libsgx-aesm-quote-ex-plugin=$VERSION \ + libsgx-enclave-common=$VERSION \ + libsgx-enclave-common-dev=$VERSION \ + libsgx-epid=$VERSION \ + libsgx-epid-dev=$VERSION \ + libsgx-launch=$VERSION \ + libsgx-launch-dev=$VERSION \ + libsgx-quote-ex=$VERSION \ + libsgx-quote-ex-dev=$VERSION \ + libsgx-uae-service=$VERSION \ + libsgx-urts=$VERSION \ + sgx-aesm-service=$VERSION \ + libsgx-ae-qe3=$DCAP_VERSION \ + libsgx-pce-logic=$DCAP_VERSION \ + libsgx-qe3-logic=$DCAP_VERSION \ + libsgx-ra-network=$DCAP_VERSION \ + libsgx-ra-uefi=$DCAP_VERSION +mkdir -p /var/run/aesmd || true + +# store env +echo "$(env)" >> $GITHUB_ENV \ No newline at end of file diff --git a/bitacross-worker/scripts/m6.sh b/bitacross-worker/scripts/m6.sh new file mode 100755 index 0000000000..d6ed56786d --- /dev/null +++ b/bitacross-worker/scripts/m6.sh @@ -0,0 +1,23 @@ +#!/bin/bash +set -euo pipefail + +# Runs M6 demo: Either set `CLIENT_DIR` env var directly or run script with: +# +# source ./init_env.sh && ./m6.sh + +echo "$CLIENT_DIR" + +cd "$CLIENT_DIR" || exit + +LOG_1="${LOG_1:-$LOG_DIR/m6_demo_shielding_unshielding_1.log}" +LOG_2="${LOG_2:-$LOG_DIR/m6_demo_shielding_unshielding_2.log}" + +echo "[m6.sh] printing to logs:" +echo " $LOG_1" +echo " $LOG_2" + +touch "$LOG_1" +touch "$LOG_2" + +./demo_shielding_unshielding.sh -p 9944 -P 2000 -C ./../bin/bitacross-cli -t first 2>&1 | tee "$LOG_1" +./demo_shielding_unshielding.sh -p 9944 -P 3000 -C ./../bin/bitacross-cli -t second 2>&1 | tee "$LOG_2" diff --git a/bitacross-worker/scripts/m8.sh b/bitacross-worker/scripts/m8.sh new file mode 100755 index 0000000000..402875a8c8 --- /dev/null +++ b/bitacross-worker/scripts/m8.sh @@ -0,0 +1,21 @@ +#!/bin/bash +set -euo pipefail + +# Runs M8 demo: Either set `CLIENT_DIR` env var directly or run script with: +# +# source ./init_env.sh && ./m8.sh + +cd "$CLIENT_DIR" || exit + +LOG_1="${LOG_1:-$LOG_DIR/m8_demo_direct_call_1.log}" +LOG_2="${LOG_2:-$LOG_DIR/m8_demo_direct_call_2.log}" + +echo "[m8.sh] printing to logs:" +echo " $LOG_1" +echo " $LOG_2" + +touch "$LOG_1" +touch "$LOG_2" + +./demo_direct_call.sh -p 9944 -P 2000 -C ./../bin/bitacross-cli -t first 2>&1 | tee "$LOG_1" +./demo_direct_call.sh -p 9944 -P 3000 -C ./../bin/bitacross-cli -t second 2>&1 | tee "$LOG_2" diff --git a/bitacross-worker/scripts/polkadot_update.sh b/bitacross-worker/scripts/polkadot_update.sh new file mode 100755 index 0000000000..0ba52f86e3 --- /dev/null +++ b/bitacross-worker/scripts/polkadot_update.sh @@ -0,0 +1,97 @@ +#!/bin/bash + +# A script to automate the polkadot update for our repository as far as possible +# Needs the diener and sd (sed replacement) tool. Install with: +# cargo install diener +# cargo install sd + +# These are the values that need to be adjusted for an update +CHECKOUT_DIR="$HOME/polkadot_update2" +DEVELOPER_ID="tn" +OLD_VERSION_NUMBER="0.9.27" +NEW_VERSION_NUMBER="0.9.28" +NEW_NIGHTLY_VERSION="2022-09-12" + +OLD_POLKADOT_VERSION_NUMBER="polkadot-v${OLD_VERSION_NUMBER}" +NEW_POLKADOT_VERSION_NUMBER="polkadot-v${NEW_VERSION_NUMBER}" +DEVELOPMENT_BRANCH="${DEVELOPER_ID}/${NEW_POLKADOT_VERSION_NUMBER}" + +# Make sure that the directory does not exist. We don't want to mess up existing stuff +if [ -d "${CHECKOUT_DIR}" ]; then + echo "Directory ${CHECKOUT_DIR} already exists. Please delete directory first." + exit 1 +fi + +mkdir "${CHECKOUT_DIR}" +pushd "${CHECKOUT_DIR}" + +git clone https://github.com/integritee-network/integritee-node.git +git clone https://github.com/integritee-network/pallets.git +git clone https://github.com/integritee-network/parachain.git +git clone https://github.com/scs/substrate-api-client.git +git clone https://github.com/integritee-network/worker.git + +declare -a REPO_NAMES=("integritee-node" "pallets" "parachain" "substrate-api-client" "worker" ) + +# Create new branch for all repos +for REPO in ${REPO_NAMES[@]}; do + pushd ${REPO};git checkout -b ${DEVELOPMENT_BRANCH};popd +done + +# Update the polkadot version +# We cannot combine the flags into a single call. Don't use the all flag because it relly changes all dependencies +diener update --cumulus --branch ${NEW_POLKADOT_VERSION_NUMBER} +diener update --substrate --branch ${NEW_POLKADOT_VERSION_NUMBER} +# Polkadot uses another branch pattern, because why not... +diener update --polkadot --branch "release-v${NEW_VERSION_NUMBER}" + +# Add commit for all repos +for REPO in ${REPO_NAMES[@]}; do + pushd ${REPO};git add -A;git commit -m "Update polkadot version (Auto generated commit)";popd +done + +# Execute cargo update for all repos. Currently not active as it is not clear when is the "right moment" to do this +#for REPO in ${REPO_NAMES[@]}; do +# pushd ${REPO};cargo update;popd +#done + +# Add commit for all repos +#for REPO in ${REPO_NAMES[@]}; do +# pushd ${REPO};git add -A;git commit -m "Run cargo update (Auto generated)";popd +#done + +#set -o xtrace +# Update internal dependencies by doing search replace +for REPO in ${REPO_NAMES[@]}; do + SEARCH_STRING_VERSION="${REPO}\", branch = \"${OLD_POLKADOT_VERSION_NUMBER}\"" + SEARCH_STRING_VERSION_GIT="${REPO}.git\", branch = \"${OLD_POLKADOT_VERSION_NUMBER}\"" + SEARCH_STRING_MASTER="${REPO}\", branch = \"master\"" + SEARCH_STRING_MASTER_GIT="${REPO}.git\", branch = \"master\"" + REPLACE_STRING="${REPO}.git\", branch = \"${DEVELOPMENT_BRANCH}\"" + sd "${SEARCH_STRING_VERSION}" "${REPLACE_STRING}" $(find . -type f -name 'Cargo.toml') + sd "${SEARCH_STRING_VERSION_GIT}" "${REPLACE_STRING}" $(find . -type f -name 'Cargo.toml') + sd "${SEARCH_STRING_MASTER}" "${REPLACE_STRING}" $(find . -type f -name 'Cargo.toml') + sd "${SEARCH_STRING_MASTER_GIT}" "${REPLACE_STRING}" $(find . -type f -name 'Cargo.toml') +done + +# Add commit for all repos +for REPO in ${REPO_NAMES[@]}; do + pushd ${REPO};git add -A;git commit -m "Update versions for internal dependencies (Auto generated commit)";popd +done + +NIGHTLY_SEARCH_STRING="channel = \"nightly-.*\"" +NIGHTLY_SEARCH_STRING="channel = \"nightly-${NEW_NIGHTLY_VERSION}\"" +sd "${NIGHTLY_SEARCH_STRING}" "${NIGTHLY_NEW_STRING}" $(find . -type f -name 'rust-toolchain.toml') + +# Add commit for all repos +for REPO in ${REPO_NAMES[@]}; do + pushd ${REPO};git add -A;git commit -m "Update rust toolchain to new nightly version (Auto generated commit)";popd +done + +echo "" +echo "" +echo "Search results for old version number ${OLD_VERSION_NUMBER} in Cargo.toml files:" +# Exclude the lock files as they still refer to the old version +grep -F -r --exclude *.lock "${OLD_VERSION_NUMBER}" . + +popd diff --git a/bitacross-worker/scripts/sidechain.sh b/bitacross-worker/scripts/sidechain.sh new file mode 100755 index 0000000000..908c538eb1 --- /dev/null +++ b/bitacross-worker/scripts/sidechain.sh @@ -0,0 +1,17 @@ +#!/bin/bash +set -euo pipefail + +# Runs sidechain demo: Either set `CLIENT_DIR` env var directly or run script with: +# +# source ./init_env.sh && ./sidechain.sh + +cd "$CLIENT_DIR" || exit + +LOG="${LOG:-$LOG_DIR/sidechain_demo.log}" + +echo "[sidechain.sh] printing to logs:" +echo " $LOG" + +touch "$LOG" + +./demo_sidechain.sh -p 9944 -A 2000 -B 3000 -C ./../bin/bitacross-cli 2>&1 | tee "$LOG" \ No newline at end of file diff --git a/bitacross-worker/scripts/teeracle.sh b/bitacross-worker/scripts/teeracle.sh new file mode 100644 index 0000000000..829c67b2a3 --- /dev/null +++ b/bitacross-worker/scripts/teeracle.sh @@ -0,0 +1,19 @@ +#!/bin/bash +set -euo pipefail + +# Runs Teeracle1 demo: Either set `CLIENT_DIR` env var directly or run script with: +# +# source ./init_env.sh && ./teeracle.sh + +echo "$CLIENT_DIR" + +cd "$CLIENT_DIR" || exit + +LOG_1="${LOG_1:-$LOG_DIR/teeracle1_demo_whitelist.log}" + +echo "[teeracle.sh] printing to logs:" +echo " $LOG_1" + +touch "$LOG_1" + +./demo_teeracle_whitelist.sh -p 9944 -P 2000 -d 120 -i 24 2>&1 | tee "$LOG_1" diff --git a/bitacross-worker/scripts/test_transfer/README.md b/bitacross-worker/scripts/test_transfer/README.md new file mode 100644 index 0000000000..13ff80ca8e --- /dev/null +++ b/bitacross-worker/scripts/test_transfer/README.md @@ -0,0 +1,6 @@ +## Test transfer from Alice to random account + +## Install +```bash +npm install +``` diff --git a/bitacross-worker/scripts/test_transfer/package-lock.json b/bitacross-worker/scripts/test_transfer/package-lock.json new file mode 100644 index 0000000000..237b27764d --- /dev/null +++ b/bitacross-worker/scripts/test_transfer/package-lock.json @@ -0,0 +1,1322 @@ +{ + "name": "test_transfer", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "test_transfer", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@polkadot/api": "^10.9.1", + "@polkadot/keyring": "^12.3.2", + "@polkadot/util-crypto": "^12.3.2" + } + }, + "node_modules/@noble/curves": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz", + "integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==", + "dependencies": { + "@noble/hashes": "1.3.1" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@polkadot/api": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/@polkadot/api/-/api-10.9.1.tgz", + "integrity": "sha512-ND/2UqZBWvtt4PfV03OStTKg0mxmPk4UpMAgJKutdgsz/wP9CYJ1KbjwFgPNekL9JnzbKQsWyQNPVrcw7kQk8A==", + "dependencies": { + "@polkadot/api-augment": "10.9.1", + "@polkadot/api-base": "10.9.1", + "@polkadot/api-derive": "10.9.1", + "@polkadot/keyring": "^12.3.1", + "@polkadot/rpc-augment": "10.9.1", + "@polkadot/rpc-core": "10.9.1", + "@polkadot/rpc-provider": "10.9.1", + "@polkadot/types": "10.9.1", + "@polkadot/types-augment": "10.9.1", + "@polkadot/types-codec": "10.9.1", + "@polkadot/types-create": "10.9.1", + "@polkadot/types-known": "10.9.1", + "@polkadot/util": "^12.3.1", + "@polkadot/util-crypto": "^12.3.1", + "eventemitter3": "^5.0.1", + "rxjs": "^7.8.1", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/api-augment": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/@polkadot/api-augment/-/api-augment-10.9.1.tgz", + "integrity": "sha512-kRZZvCFVcN4hAH4dJ+Qzfdy27/4EEq3oLDf3ihj0LTVrAezSWcKPGE3EVFy+Mn6Lo4SUc7RVyoKvIUhSk2l4Dg==", + "dependencies": { + "@polkadot/api-base": "10.9.1", + "@polkadot/rpc-augment": "10.9.1", + "@polkadot/types": "10.9.1", + "@polkadot/types-augment": "10.9.1", + "@polkadot/types-codec": "10.9.1", + "@polkadot/util": "^12.3.1", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/api-base": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/@polkadot/api-base/-/api-base-10.9.1.tgz", + "integrity": "sha512-Q3m2KzlceMK2kX8bhnUZWk3RT6emmijeeFZZQgCePpEcrSeNjnqG4qjuTPgkveaOkUT8MAoDc5Avuzcc2jlW9g==", + "dependencies": { + "@polkadot/rpc-core": "10.9.1", + "@polkadot/types": "10.9.1", + "@polkadot/util": "^12.3.1", + "rxjs": "^7.8.1", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/api-derive": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/@polkadot/api-derive/-/api-derive-10.9.1.tgz", + "integrity": "sha512-mRud1UZCFIc4Z63qAoGSIHh/foyUYADfy1RQYCmPpeFKfIdCIrHpd7xFdJXTOMYOS0BwlM6u4qli/ZT4XigezQ==", + "dependencies": { + "@polkadot/api": "10.9.1", + "@polkadot/api-augment": "10.9.1", + "@polkadot/api-base": "10.9.1", + "@polkadot/rpc-core": "10.9.1", + "@polkadot/types": "10.9.1", + "@polkadot/types-codec": "10.9.1", + "@polkadot/util": "^12.3.1", + "@polkadot/util-crypto": "^12.3.1", + "rxjs": "^7.8.1", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/keyring": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/keyring/-/keyring-12.3.2.tgz", + "integrity": "sha512-NTdtDeI0DP9l/45hXynNABeP5VB8piw5YR+CbUxK2e36xpJWVXwbcOepzslg5ghE9rs8UKJb30Z/HqTU4sBY0Q==", + "dependencies": { + "@polkadot/util": "12.3.2", + "@polkadot/util-crypto": "12.3.2", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@polkadot/util": "12.3.2", + "@polkadot/util-crypto": "12.3.2" + } + }, + "node_modules/@polkadot/networks": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-12.3.2.tgz", + "integrity": "sha512-uCkyybKoeEm1daKr0uT/9oNDHDDzCy2/ZdVl346hQqfdR1Ct3BaxMjxqvdmb5N8aCw0cBWSfgsxAYtw8ESmllQ==", + "dependencies": { + "@polkadot/util": "12.3.2", + "@substrate/ss58-registry": "^1.40.0", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/rpc-augment": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-augment/-/rpc-augment-10.9.1.tgz", + "integrity": "sha512-MaLHkNlyqN20ZRYr6uNd1BZr1OsrnX9qLAmsl0mcrri1vPGRH6VHjfFH1RBLkikpWD82v17g0l2hLwdV1ZHMcw==", + "dependencies": { + "@polkadot/rpc-core": "10.9.1", + "@polkadot/types": "10.9.1", + "@polkadot/types-codec": "10.9.1", + "@polkadot/util": "^12.3.1", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/rpc-core": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-core/-/rpc-core-10.9.1.tgz", + "integrity": "sha512-ZtA8B8SfXSAwVkBlCcKRHw0eSM7ec/sbiNOM5GasXPeRujUgT7lOwSH2GbUZSqe9RfRDMp6DvO9c2JoGc3LLWw==", + "dependencies": { + "@polkadot/rpc-augment": "10.9.1", + "@polkadot/rpc-provider": "10.9.1", + "@polkadot/types": "10.9.1", + "@polkadot/util": "^12.3.1", + "rxjs": "^7.8.1", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/rpc-provider": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-provider/-/rpc-provider-10.9.1.tgz", + "integrity": "sha512-4QzT2QzD+320+eT6b79sGAA85Tt3Bb8fQvse4r5Mom2iiBd2SO81vOhxSAOaIe4GUsw25VzFJmsbe7+OObItdg==", + "dependencies": { + "@polkadot/keyring": "^12.3.1", + "@polkadot/types": "10.9.1", + "@polkadot/types-support": "10.9.1", + "@polkadot/util": "^12.3.1", + "@polkadot/util-crypto": "^12.3.1", + "@polkadot/x-fetch": "^12.3.1", + "@polkadot/x-global": "^12.3.1", + "@polkadot/x-ws": "^12.3.1", + "eventemitter3": "^5.0.1", + "mock-socket": "^9.2.1", + "nock": "^13.3.1", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "@substrate/connect": "0.7.26" + } + }, + "node_modules/@polkadot/types": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/@polkadot/types/-/types-10.9.1.tgz", + "integrity": "sha512-AG33i2ZGGfq7u+5rkAdGrXAQHHl844/Yv+junH5ZzX69xiCoWO1bH/yzDUNBdpki2GlACWvF9nLYh3F2tVF93w==", + "dependencies": { + "@polkadot/keyring": "^12.3.1", + "@polkadot/types-augment": "10.9.1", + "@polkadot/types-codec": "10.9.1", + "@polkadot/types-create": "10.9.1", + "@polkadot/util": "^12.3.1", + "@polkadot/util-crypto": "^12.3.1", + "rxjs": "^7.8.1", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/types-augment": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-augment/-/types-augment-10.9.1.tgz", + "integrity": "sha512-OY9/jTMFRFqYdkUnfcGwqMLC64A0Q25bjvCuVQCVjsPFKE3wl0Kt5rNT01eV2UmLXrR6fY0xWbR2w80bLA7CIQ==", + "dependencies": { + "@polkadot/types": "10.9.1", + "@polkadot/types-codec": "10.9.1", + "@polkadot/util": "^12.3.1", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/types-codec": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-codec/-/types-codec-10.9.1.tgz", + "integrity": "sha512-mJ5OegKGraY1FLvEa8FopRCr3pQrhDkcn5RNOjmgJQozENVeRaxhk0NwxYz7IojFvSDnKnc6lNQfKaaSe5pLHg==", + "dependencies": { + "@polkadot/util": "^12.3.1", + "@polkadot/x-bigint": "^12.3.1", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/types-create": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-create/-/types-create-10.9.1.tgz", + "integrity": "sha512-OVz50MGTTuiuVnRP/zAx4CTuLioc0hsiwNwqN2lNhmIJGtnQ4Vy/7mQRsIWehiYz6g0Vzzm5B3qWkTXO1NSN5w==", + "dependencies": { + "@polkadot/types-codec": "10.9.1", + "@polkadot/util": "^12.3.1", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/types-known": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-known/-/types-known-10.9.1.tgz", + "integrity": "sha512-zCMVWc4pJtkbMFPu72bD4IhvV/gkHXPX3C5uu92WdmCfnn0vEIEsMKWlVXVVvQQZKAqvs/awpqIfrUtEViOGEA==", + "dependencies": { + "@polkadot/networks": "^12.3.1", + "@polkadot/types": "10.9.1", + "@polkadot/types-codec": "10.9.1", + "@polkadot/types-create": "10.9.1", + "@polkadot/util": "^12.3.1", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/types-support": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-support/-/types-support-10.9.1.tgz", + "integrity": "sha512-XsieuLDsszvMZQlleacQBfx07i/JkwQV/UxH9q8Hz7Okmaz9pEVEW1h3ka2/cPuC7a4l32JhaORBUYshBZNdJg==", + "dependencies": { + "@polkadot/util": "^12.3.1", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/util": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-12.3.2.tgz", + "integrity": "sha512-y/JShcGyOamCUiSIg++XZuLHt1ktSKBaSH2K5Nw5NXlgP0+7am+GZzqPB8fQ4qhYLruEOv+YRiz0GC1Zr9S+wg==", + "dependencies": { + "@polkadot/x-bigint": "12.3.2", + "@polkadot/x-global": "12.3.2", + "@polkadot/x-textdecoder": "12.3.2", + "@polkadot/x-textencoder": "12.3.2", + "@types/bn.js": "^5.1.1", + "bn.js": "^5.2.1", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/util-crypto": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-12.3.2.tgz", + "integrity": "sha512-pTpx+YxolY0BDT4RcGmgeKbHHD/dI6Ll9xRsqmVdIjpcVVY20uDNTyXs81ZNtfKgyod1y9JQkfNv2Dz9iEpTkQ==", + "dependencies": { + "@noble/curves": "1.1.0", + "@noble/hashes": "1.3.1", + "@polkadot/networks": "12.3.2", + "@polkadot/util": "12.3.2", + "@polkadot/wasm-crypto": "^7.2.1", + "@polkadot/wasm-util": "^7.2.1", + "@polkadot/x-bigint": "12.3.2", + "@polkadot/x-randomvalues": "12.3.2", + "@scure/base": "1.1.1", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@polkadot/util": "12.3.2" + } + }, + "node_modules/@polkadot/wasm-bridge": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-bridge/-/wasm-bridge-7.2.1.tgz", + "integrity": "sha512-uV/LHREDBGBbHrrv7HTki+Klw0PYZzFomagFWII4lp6Toj/VCvRh5WMzooVC+g/XsBGosAwrvBhoModabyHx+A==", + "dependencies": { + "@polkadot/wasm-util": "7.2.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-crypto": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto/-/wasm-crypto-7.2.1.tgz", + "integrity": "sha512-SA2+33S9TAwGhniKgztVN6pxUKpGfN4Tre/eUZGUfpgRkT92wIUT2GpGWQE+fCCqGQgADrNiBcwt6XwdPqMQ4Q==", + "dependencies": { + "@polkadot/wasm-bridge": "7.2.1", + "@polkadot/wasm-crypto-asmjs": "7.2.1", + "@polkadot/wasm-crypto-init": "7.2.1", + "@polkadot/wasm-crypto-wasm": "7.2.1", + "@polkadot/wasm-util": "7.2.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-asmjs": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.2.1.tgz", + "integrity": "sha512-z/d21bmxyVfkzGsKef/FWswKX02x5lK97f4NPBZ9XBeiFkmzlXhdSnu58/+b1sKsRAGdW/Rn/rTNRDhW0GqCAg==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-init": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.2.1.tgz", + "integrity": "sha512-GcEXtwN9LcSf32V9zSaYjHImFw16hCyo2Xzg4GLLDPPeaAAfbFr2oQMgwyDbvBrBjLKHVHjsPZyGhXae831amw==", + "dependencies": { + "@polkadot/wasm-bridge": "7.2.1", + "@polkadot/wasm-crypto-asmjs": "7.2.1", + "@polkadot/wasm-crypto-wasm": "7.2.1", + "@polkadot/wasm-util": "7.2.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-wasm": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.2.1.tgz", + "integrity": "sha512-DqyXE4rSD0CVlLIw88B58+HHNyrvm+JAnYyuEDYZwCvzUWOCNos/DDg9wi/K39VAIsCCKDmwKqkkfIofuOj/lA==", + "dependencies": { + "@polkadot/wasm-util": "7.2.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/wasm-util": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-util/-/wasm-util-7.2.1.tgz", + "integrity": "sha512-FBSn/3aYJzhN0sYAYhHB8y9JL8mVgxLy4M1kUXYbyo+8GLRQEN5rns8Vcb8TAlIzBWgVTOOptYBvxo0oj0h7Og==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/x-bigint": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-12.3.2.tgz", + "integrity": "sha512-JLqLgfGXe/x+hZJETd5ZqfpVsbwyMsH5Nn1Q20ineMMjXN/ig+kVR8Mc15LXBMuw4g7LldFW6UUrotWnuMI8Yw==", + "dependencies": { + "@polkadot/x-global": "12.3.2", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/x-fetch": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-fetch/-/x-fetch-12.3.2.tgz", + "integrity": "sha512-3IEuZ5S+RI/t33NsdPLIIa5COfDCfpUW2sbaByEczn75aD1jLqJZSEDwiBniJ2osyNd4uUxBf6e5jw7LAZeZJg==", + "dependencies": { + "@polkadot/x-global": "12.3.2", + "node-fetch": "^3.3.1", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/x-global": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-12.3.2.tgz", + "integrity": "sha512-yVZq6oIegjlyh5rUZiTklgu+fL+W/DG1ypEa02683tUCB3avV5cA3PAHKptMSlb6FpweHu37lKKrqfAWrraDxg==", + "dependencies": { + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/x-randomvalues": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-12.3.2.tgz", + "integrity": "sha512-ywjIs8CWpvOGmq+3cGCNPOHxAjPHdBUiXyDccftx5BRVdmtbt36gK/V84bKr6Xs73FGu0jprUAOSRRsLZX/3dg==", + "dependencies": { + "@polkadot/x-global": "12.3.2", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@polkadot/util": "12.3.2", + "@polkadot/wasm-util": "*" + } + }, + "node_modules/@polkadot/x-textdecoder": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-12.3.2.tgz", + "integrity": "sha512-lY5bfA5xArJRWEJlYOlQQMJeTjWD8s0yMhchirVgf5xj8Id9vPGeUoneH+VFDEwgXxrqBvDFJ4smN4T/r6a/fg==", + "dependencies": { + "@polkadot/x-global": "12.3.2", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/x-textencoder": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-12.3.2.tgz", + "integrity": "sha512-iP3qEBiHzBckQ9zeY7ZHRWuu7mCEg5SMpOugs6UODRk8sx6KHzGQYlghBbWLit0uppPDVE0ifEwZ2n73djJHWQ==", + "dependencies": { + "@polkadot/x-global": "12.3.2", + "tslib": "^2.5.3" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@polkadot/x-ws": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-ws/-/x-ws-12.3.2.tgz", + "integrity": "sha512-yM9Z64pLNlHpJE43+Xtr+iUXmYpFFY5u5hrke2PJt13O48H8f9Vb9cRaIh94appLyICoS0aekGhDkGH+MCspBA==", + "dependencies": { + "@polkadot/x-global": "12.3.2", + "tslib": "^2.5.3", + "ws": "^8.13.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@scure/base": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz", + "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/@substrate/connect": { + "version": "0.7.26", + "resolved": "https://registry.npmjs.org/@substrate/connect/-/connect-0.7.26.tgz", + "integrity": "sha512-uuGSiroGuKWj1+38n1kY5HReer5iL9bRwPCzuoLtqAOmI1fGI0hsSI2LlNQMAbfRgr7VRHXOk5MTuQf5ulsFRw==", + "optional": true, + "dependencies": { + "@substrate/connect-extension-protocol": "^1.0.1", + "eventemitter3": "^4.0.7", + "smoldot": "1.0.4" + } + }, + "node_modules/@substrate/connect-extension-protocol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@substrate/connect-extension-protocol/-/connect-extension-protocol-1.0.1.tgz", + "integrity": "sha512-161JhCC1csjH3GE5mPLEd7HbWtwNSPJBg3p1Ksz9SFlTzj/bgEwudiRN2y5i0MoLGCIJRYKyKGMxVnd29PzNjg==", + "optional": true + }, + "node_modules/@substrate/connect/node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "optional": true + }, + "node_modules/@substrate/ss58-registry": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/@substrate/ss58-registry/-/ss58-registry-1.40.0.tgz", + "integrity": "sha512-QuU2nBql3J4KCnOWtWDw4n1K4JU0T79j54ZZvm/9nhsX6AIar13FyhsaBfs6QkJ2ixTQAnd7TocJIoJRWbqMZA==" + }, + "node_modules/@types/bn.js": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", + "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "20.3.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.1.tgz", + "integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==" + }, + "node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/mock-socket": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/mock-socket/-/mock-socket-9.2.1.tgz", + "integrity": "sha512-aw9F9T9G2zpGipLLhSNh6ZpgUyUl4frcVmRN08uE1NWPWg43Wx6+sGPDbQ7E5iFZZDJW5b5bypMeAEHqTbIFag==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/nock": { + "version": "13.3.1", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.3.1.tgz", + "integrity": "sha512-vHnopocZuI93p2ccivFyGuUfzjq2fxNyNurp7816mlT5V5HF4SzXu8lvLrVzBbNqzs+ODooZ6OksuSUNM7Njkw==", + "dependencies": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.21", + "propagate": "^2.0.0" + }, + "engines": { + "node": ">= 10.13" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.1.tgz", + "integrity": "sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow==", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "optional": true + }, + "node_modules/propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/smoldot": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/smoldot/-/smoldot-1.0.4.tgz", + "integrity": "sha512-N3TazI1C4GGrseFH/piWyZCCCRJTRx2QhDfrUKRT4SzILlW5m8ayZ3QTKICcz1C/536T9cbHHJyP7afxI6Mi1A==", + "optional": true, + "dependencies": { + "pako": "^2.0.4", + "ws": "^8.8.1" + } + }, + "node_modules/tslib": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", + "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" + }, + "node_modules/web-streams-polyfill": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", + "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + }, + "dependencies": { + "@noble/curves": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz", + "integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==", + "requires": { + "@noble/hashes": "1.3.1" + } + }, + "@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==" + }, + "@polkadot/api": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/@polkadot/api/-/api-10.9.1.tgz", + "integrity": "sha512-ND/2UqZBWvtt4PfV03OStTKg0mxmPk4UpMAgJKutdgsz/wP9CYJ1KbjwFgPNekL9JnzbKQsWyQNPVrcw7kQk8A==", + "requires": { + "@polkadot/api-augment": "10.9.1", + "@polkadot/api-base": "10.9.1", + "@polkadot/api-derive": "10.9.1", + "@polkadot/keyring": "^12.3.1", + "@polkadot/rpc-augment": "10.9.1", + "@polkadot/rpc-core": "10.9.1", + "@polkadot/rpc-provider": "10.9.1", + "@polkadot/types": "10.9.1", + "@polkadot/types-augment": "10.9.1", + "@polkadot/types-codec": "10.9.1", + "@polkadot/types-create": "10.9.1", + "@polkadot/types-known": "10.9.1", + "@polkadot/util": "^12.3.1", + "@polkadot/util-crypto": "^12.3.1", + "eventemitter3": "^5.0.1", + "rxjs": "^7.8.1", + "tslib": "^2.5.3" + } + }, + "@polkadot/api-augment": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/@polkadot/api-augment/-/api-augment-10.9.1.tgz", + "integrity": "sha512-kRZZvCFVcN4hAH4dJ+Qzfdy27/4EEq3oLDf3ihj0LTVrAezSWcKPGE3EVFy+Mn6Lo4SUc7RVyoKvIUhSk2l4Dg==", + "requires": { + "@polkadot/api-base": "10.9.1", + "@polkadot/rpc-augment": "10.9.1", + "@polkadot/types": "10.9.1", + "@polkadot/types-augment": "10.9.1", + "@polkadot/types-codec": "10.9.1", + "@polkadot/util": "^12.3.1", + "tslib": "^2.5.3" + } + }, + "@polkadot/api-base": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/@polkadot/api-base/-/api-base-10.9.1.tgz", + "integrity": "sha512-Q3m2KzlceMK2kX8bhnUZWk3RT6emmijeeFZZQgCePpEcrSeNjnqG4qjuTPgkveaOkUT8MAoDc5Avuzcc2jlW9g==", + "requires": { + "@polkadot/rpc-core": "10.9.1", + "@polkadot/types": "10.9.1", + "@polkadot/util": "^12.3.1", + "rxjs": "^7.8.1", + "tslib": "^2.5.3" + } + }, + "@polkadot/api-derive": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/@polkadot/api-derive/-/api-derive-10.9.1.tgz", + "integrity": "sha512-mRud1UZCFIc4Z63qAoGSIHh/foyUYADfy1RQYCmPpeFKfIdCIrHpd7xFdJXTOMYOS0BwlM6u4qli/ZT4XigezQ==", + "requires": { + "@polkadot/api": "10.9.1", + "@polkadot/api-augment": "10.9.1", + "@polkadot/api-base": "10.9.1", + "@polkadot/rpc-core": "10.9.1", + "@polkadot/types": "10.9.1", + "@polkadot/types-codec": "10.9.1", + "@polkadot/util": "^12.3.1", + "@polkadot/util-crypto": "^12.3.1", + "rxjs": "^7.8.1", + "tslib": "^2.5.3" + } + }, + "@polkadot/keyring": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/keyring/-/keyring-12.3.2.tgz", + "integrity": "sha512-NTdtDeI0DP9l/45hXynNABeP5VB8piw5YR+CbUxK2e36xpJWVXwbcOepzslg5ghE9rs8UKJb30Z/HqTU4sBY0Q==", + "requires": { + "@polkadot/util": "12.3.2", + "@polkadot/util-crypto": "12.3.2", + "tslib": "^2.5.3" + } + }, + "@polkadot/networks": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-12.3.2.tgz", + "integrity": "sha512-uCkyybKoeEm1daKr0uT/9oNDHDDzCy2/ZdVl346hQqfdR1Ct3BaxMjxqvdmb5N8aCw0cBWSfgsxAYtw8ESmllQ==", + "requires": { + "@polkadot/util": "12.3.2", + "@substrate/ss58-registry": "^1.40.0", + "tslib": "^2.5.3" + } + }, + "@polkadot/rpc-augment": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-augment/-/rpc-augment-10.9.1.tgz", + "integrity": "sha512-MaLHkNlyqN20ZRYr6uNd1BZr1OsrnX9qLAmsl0mcrri1vPGRH6VHjfFH1RBLkikpWD82v17g0l2hLwdV1ZHMcw==", + "requires": { + "@polkadot/rpc-core": "10.9.1", + "@polkadot/types": "10.9.1", + "@polkadot/types-codec": "10.9.1", + "@polkadot/util": "^12.3.1", + "tslib": "^2.5.3" + } + }, + "@polkadot/rpc-core": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-core/-/rpc-core-10.9.1.tgz", + "integrity": "sha512-ZtA8B8SfXSAwVkBlCcKRHw0eSM7ec/sbiNOM5GasXPeRujUgT7lOwSH2GbUZSqe9RfRDMp6DvO9c2JoGc3LLWw==", + "requires": { + "@polkadot/rpc-augment": "10.9.1", + "@polkadot/rpc-provider": "10.9.1", + "@polkadot/types": "10.9.1", + "@polkadot/util": "^12.3.1", + "rxjs": "^7.8.1", + "tslib": "^2.5.3" + } + }, + "@polkadot/rpc-provider": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-provider/-/rpc-provider-10.9.1.tgz", + "integrity": "sha512-4QzT2QzD+320+eT6b79sGAA85Tt3Bb8fQvse4r5Mom2iiBd2SO81vOhxSAOaIe4GUsw25VzFJmsbe7+OObItdg==", + "requires": { + "@polkadot/keyring": "^12.3.1", + "@polkadot/types": "10.9.1", + "@polkadot/types-support": "10.9.1", + "@polkadot/util": "^12.3.1", + "@polkadot/util-crypto": "^12.3.1", + "@polkadot/x-fetch": "^12.3.1", + "@polkadot/x-global": "^12.3.1", + "@polkadot/x-ws": "^12.3.1", + "@substrate/connect": "0.7.26", + "eventemitter3": "^5.0.1", + "mock-socket": "^9.2.1", + "nock": "^13.3.1", + "tslib": "^2.5.3" + } + }, + "@polkadot/types": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/@polkadot/types/-/types-10.9.1.tgz", + "integrity": "sha512-AG33i2ZGGfq7u+5rkAdGrXAQHHl844/Yv+junH5ZzX69xiCoWO1bH/yzDUNBdpki2GlACWvF9nLYh3F2tVF93w==", + "requires": { + "@polkadot/keyring": "^12.3.1", + "@polkadot/types-augment": "10.9.1", + "@polkadot/types-codec": "10.9.1", + "@polkadot/types-create": "10.9.1", + "@polkadot/util": "^12.3.1", + "@polkadot/util-crypto": "^12.3.1", + "rxjs": "^7.8.1", + "tslib": "^2.5.3" + } + }, + "@polkadot/types-augment": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-augment/-/types-augment-10.9.1.tgz", + "integrity": "sha512-OY9/jTMFRFqYdkUnfcGwqMLC64A0Q25bjvCuVQCVjsPFKE3wl0Kt5rNT01eV2UmLXrR6fY0xWbR2w80bLA7CIQ==", + "requires": { + "@polkadot/types": "10.9.1", + "@polkadot/types-codec": "10.9.1", + "@polkadot/util": "^12.3.1", + "tslib": "^2.5.3" + } + }, + "@polkadot/types-codec": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-codec/-/types-codec-10.9.1.tgz", + "integrity": "sha512-mJ5OegKGraY1FLvEa8FopRCr3pQrhDkcn5RNOjmgJQozENVeRaxhk0NwxYz7IojFvSDnKnc6lNQfKaaSe5pLHg==", + "requires": { + "@polkadot/util": "^12.3.1", + "@polkadot/x-bigint": "^12.3.1", + "tslib": "^2.5.3" + } + }, + "@polkadot/types-create": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-create/-/types-create-10.9.1.tgz", + "integrity": "sha512-OVz50MGTTuiuVnRP/zAx4CTuLioc0hsiwNwqN2lNhmIJGtnQ4Vy/7mQRsIWehiYz6g0Vzzm5B3qWkTXO1NSN5w==", + "requires": { + "@polkadot/types-codec": "10.9.1", + "@polkadot/util": "^12.3.1", + "tslib": "^2.5.3" + } + }, + "@polkadot/types-known": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-known/-/types-known-10.9.1.tgz", + "integrity": "sha512-zCMVWc4pJtkbMFPu72bD4IhvV/gkHXPX3C5uu92WdmCfnn0vEIEsMKWlVXVVvQQZKAqvs/awpqIfrUtEViOGEA==", + "requires": { + "@polkadot/networks": "^12.3.1", + "@polkadot/types": "10.9.1", + "@polkadot/types-codec": "10.9.1", + "@polkadot/types-create": "10.9.1", + "@polkadot/util": "^12.3.1", + "tslib": "^2.5.3" + } + }, + "@polkadot/types-support": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/@polkadot/types-support/-/types-support-10.9.1.tgz", + "integrity": "sha512-XsieuLDsszvMZQlleacQBfx07i/JkwQV/UxH9q8Hz7Okmaz9pEVEW1h3ka2/cPuC7a4l32JhaORBUYshBZNdJg==", + "requires": { + "@polkadot/util": "^12.3.1", + "tslib": "^2.5.3" + } + }, + "@polkadot/util": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-12.3.2.tgz", + "integrity": "sha512-y/JShcGyOamCUiSIg++XZuLHt1ktSKBaSH2K5Nw5NXlgP0+7am+GZzqPB8fQ4qhYLruEOv+YRiz0GC1Zr9S+wg==", + "requires": { + "@polkadot/x-bigint": "12.3.2", + "@polkadot/x-global": "12.3.2", + "@polkadot/x-textdecoder": "12.3.2", + "@polkadot/x-textencoder": "12.3.2", + "@types/bn.js": "^5.1.1", + "bn.js": "^5.2.1", + "tslib": "^2.5.3" + } + }, + "@polkadot/util-crypto": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-12.3.2.tgz", + "integrity": "sha512-pTpx+YxolY0BDT4RcGmgeKbHHD/dI6Ll9xRsqmVdIjpcVVY20uDNTyXs81ZNtfKgyod1y9JQkfNv2Dz9iEpTkQ==", + "requires": { + "@noble/curves": "1.1.0", + "@noble/hashes": "1.3.1", + "@polkadot/networks": "12.3.2", + "@polkadot/util": "12.3.2", + "@polkadot/wasm-crypto": "^7.2.1", + "@polkadot/wasm-util": "^7.2.1", + "@polkadot/x-bigint": "12.3.2", + "@polkadot/x-randomvalues": "12.3.2", + "@scure/base": "1.1.1", + "tslib": "^2.5.3" + } + }, + "@polkadot/wasm-bridge": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-bridge/-/wasm-bridge-7.2.1.tgz", + "integrity": "sha512-uV/LHREDBGBbHrrv7HTki+Klw0PYZzFomagFWII4lp6Toj/VCvRh5WMzooVC+g/XsBGosAwrvBhoModabyHx+A==", + "requires": { + "@polkadot/wasm-util": "7.2.1", + "tslib": "^2.5.0" + } + }, + "@polkadot/wasm-crypto": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto/-/wasm-crypto-7.2.1.tgz", + "integrity": "sha512-SA2+33S9TAwGhniKgztVN6pxUKpGfN4Tre/eUZGUfpgRkT92wIUT2GpGWQE+fCCqGQgADrNiBcwt6XwdPqMQ4Q==", + "requires": { + "@polkadot/wasm-bridge": "7.2.1", + "@polkadot/wasm-crypto-asmjs": "7.2.1", + "@polkadot/wasm-crypto-init": "7.2.1", + "@polkadot/wasm-crypto-wasm": "7.2.1", + "@polkadot/wasm-util": "7.2.1", + "tslib": "^2.5.0" + } + }, + "@polkadot/wasm-crypto-asmjs": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.2.1.tgz", + "integrity": "sha512-z/d21bmxyVfkzGsKef/FWswKX02x5lK97f4NPBZ9XBeiFkmzlXhdSnu58/+b1sKsRAGdW/Rn/rTNRDhW0GqCAg==", + "requires": { + "tslib": "^2.5.0" + } + }, + "@polkadot/wasm-crypto-init": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.2.1.tgz", + "integrity": "sha512-GcEXtwN9LcSf32V9zSaYjHImFw16hCyo2Xzg4GLLDPPeaAAfbFr2oQMgwyDbvBrBjLKHVHjsPZyGhXae831amw==", + "requires": { + "@polkadot/wasm-bridge": "7.2.1", + "@polkadot/wasm-crypto-asmjs": "7.2.1", + "@polkadot/wasm-crypto-wasm": "7.2.1", + "@polkadot/wasm-util": "7.2.1", + "tslib": "^2.5.0" + } + }, + "@polkadot/wasm-crypto-wasm": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.2.1.tgz", + "integrity": "sha512-DqyXE4rSD0CVlLIw88B58+HHNyrvm+JAnYyuEDYZwCvzUWOCNos/DDg9wi/K39VAIsCCKDmwKqkkfIofuOj/lA==", + "requires": { + "@polkadot/wasm-util": "7.2.1", + "tslib": "^2.5.0" + } + }, + "@polkadot/wasm-util": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-util/-/wasm-util-7.2.1.tgz", + "integrity": "sha512-FBSn/3aYJzhN0sYAYhHB8y9JL8mVgxLy4M1kUXYbyo+8GLRQEN5rns8Vcb8TAlIzBWgVTOOptYBvxo0oj0h7Og==", + "requires": { + "tslib": "^2.5.0" + } + }, + "@polkadot/x-bigint": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-12.3.2.tgz", + "integrity": "sha512-JLqLgfGXe/x+hZJETd5ZqfpVsbwyMsH5Nn1Q20ineMMjXN/ig+kVR8Mc15LXBMuw4g7LldFW6UUrotWnuMI8Yw==", + "requires": { + "@polkadot/x-global": "12.3.2", + "tslib": "^2.5.3" + } + }, + "@polkadot/x-fetch": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-fetch/-/x-fetch-12.3.2.tgz", + "integrity": "sha512-3IEuZ5S+RI/t33NsdPLIIa5COfDCfpUW2sbaByEczn75aD1jLqJZSEDwiBniJ2osyNd4uUxBf6e5jw7LAZeZJg==", + "requires": { + "@polkadot/x-global": "12.3.2", + "node-fetch": "^3.3.1", + "tslib": "^2.5.3" + } + }, + "@polkadot/x-global": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-12.3.2.tgz", + "integrity": "sha512-yVZq6oIegjlyh5rUZiTklgu+fL+W/DG1ypEa02683tUCB3avV5cA3PAHKptMSlb6FpweHu37lKKrqfAWrraDxg==", + "requires": { + "tslib": "^2.5.3" + } + }, + "@polkadot/x-randomvalues": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-12.3.2.tgz", + "integrity": "sha512-ywjIs8CWpvOGmq+3cGCNPOHxAjPHdBUiXyDccftx5BRVdmtbt36gK/V84bKr6Xs73FGu0jprUAOSRRsLZX/3dg==", + "requires": { + "@polkadot/x-global": "12.3.2", + "tslib": "^2.5.3" + } + }, + "@polkadot/x-textdecoder": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-12.3.2.tgz", + "integrity": "sha512-lY5bfA5xArJRWEJlYOlQQMJeTjWD8s0yMhchirVgf5xj8Id9vPGeUoneH+VFDEwgXxrqBvDFJ4smN4T/r6a/fg==", + "requires": { + "@polkadot/x-global": "12.3.2", + "tslib": "^2.5.3" + } + }, + "@polkadot/x-textencoder": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-12.3.2.tgz", + "integrity": "sha512-iP3qEBiHzBckQ9zeY7ZHRWuu7mCEg5SMpOugs6UODRk8sx6KHzGQYlghBbWLit0uppPDVE0ifEwZ2n73djJHWQ==", + "requires": { + "@polkadot/x-global": "12.3.2", + "tslib": "^2.5.3" + } + }, + "@polkadot/x-ws": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/@polkadot/x-ws/-/x-ws-12.3.2.tgz", + "integrity": "sha512-yM9Z64pLNlHpJE43+Xtr+iUXmYpFFY5u5hrke2PJt13O48H8f9Vb9cRaIh94appLyICoS0aekGhDkGH+MCspBA==", + "requires": { + "@polkadot/x-global": "12.3.2", + "tslib": "^2.5.3", + "ws": "^8.13.0" + } + }, + "@scure/base": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz", + "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==" + }, + "@substrate/connect": { + "version": "0.7.26", + "resolved": "https://registry.npmjs.org/@substrate/connect/-/connect-0.7.26.tgz", + "integrity": "sha512-uuGSiroGuKWj1+38n1kY5HReer5iL9bRwPCzuoLtqAOmI1fGI0hsSI2LlNQMAbfRgr7VRHXOk5MTuQf5ulsFRw==", + "optional": true, + "requires": { + "@substrate/connect-extension-protocol": "^1.0.1", + "eventemitter3": "^4.0.7", + "smoldot": "1.0.4" + }, + "dependencies": { + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "optional": true + } + } + }, + "@substrate/connect-extension-protocol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@substrate/connect-extension-protocol/-/connect-extension-protocol-1.0.1.tgz", + "integrity": "sha512-161JhCC1csjH3GE5mPLEd7HbWtwNSPJBg3p1Ksz9SFlTzj/bgEwudiRN2y5i0MoLGCIJRYKyKGMxVnd29PzNjg==", + "optional": true + }, + "@substrate/ss58-registry": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/@substrate/ss58-registry/-/ss58-registry-1.40.0.tgz", + "integrity": "sha512-QuU2nBql3J4KCnOWtWDw4n1K4JU0T79j54ZZvm/9nhsX6AIar13FyhsaBfs6QkJ2ixTQAnd7TocJIoJRWbqMZA==" + }, + "@types/bn.js": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", + "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "requires": { + "@types/node": "*" + } + }, + "@types/node": { + "version": "20.3.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.1.tgz", + "integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==" + }, + "bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==" + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + }, + "fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "requires": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + } + }, + "formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "requires": { + "fetch-blob": "^3.1.2" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "mock-socket": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/mock-socket/-/mock-socket-9.2.1.tgz", + "integrity": "sha512-aw9F9T9G2zpGipLLhSNh6ZpgUyUl4frcVmRN08uE1NWPWg43Wx6+sGPDbQ7E5iFZZDJW5b5bypMeAEHqTbIFag==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "nock": { + "version": "13.3.1", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.3.1.tgz", + "integrity": "sha512-vHnopocZuI93p2ccivFyGuUfzjq2fxNyNurp7816mlT5V5HF4SzXu8lvLrVzBbNqzs+ODooZ6OksuSUNM7Njkw==", + "requires": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.21", + "propagate": "^2.0.0" + } + }, + "node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==" + }, + "node-fetch": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.1.tgz", + "integrity": "sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow==", + "requires": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + } + }, + "pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "optional": true + }, + "propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==" + }, + "rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "requires": { + "tslib": "^2.1.0" + } + }, + "smoldot": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/smoldot/-/smoldot-1.0.4.tgz", + "integrity": "sha512-N3TazI1C4GGrseFH/piWyZCCCRJTRx2QhDfrUKRT4SzILlW5m8ayZ3QTKICcz1C/536T9cbHHJyP7afxI6Mi1A==", + "optional": true, + "requires": { + "pako": "^2.0.4", + "ws": "^8.8.1" + } + }, + "tslib": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", + "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" + }, + "web-streams-polyfill": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", + "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==" + }, + "ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "requires": {} + } + } +} diff --git a/bitacross-worker/scripts/test_transfer/package.json b/bitacross-worker/scripts/test_transfer/package.json new file mode 100644 index 0000000000..a3e2b769b8 --- /dev/null +++ b/bitacross-worker/scripts/test_transfer/package.json @@ -0,0 +1,16 @@ +{ + "name": "test_transfer", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@polkadot/api": "^10.9.1", + "@polkadot/keyring": "^12.3.2", + "@polkadot/util-crypto": "^12.3.2" + } +} diff --git a/bitacross-worker/scripts/test_transfer/transfer.js b/bitacross-worker/scripts/test_transfer/transfer.js new file mode 100644 index 0000000000..6d45154008 --- /dev/null +++ b/bitacross-worker/scripts/test_transfer/transfer.js @@ -0,0 +1,53 @@ +// Import the API & Provider and some utility functions +const { ApiPromise } = require('@polkadot/api'); + +const { Keyring } = require('@polkadot/keyring'); + +// Utility function for random values +const { randomAsU8a } = require('@polkadot/util-crypto'); + +// Some constants we are using in this sample +const AMOUNT = 1000000000000; + +async function main () { + // Create the API and wait until ready + const api = await ApiPromise.create(); + + // Create an instance of a testing keyring + const keyring = new Keyring({ type: 'sr25519', ss58Format: 42 }); + const alice = keyring.addFromUri('//Alice'); + + // Access the publicKey and address + const { publicKey, address } = alice; + + console.log('Alice Public Key:', publicKey); + console.log('Alice Address:', address); + + const { nonce, data: balance } = await api.query.system.account(publicKey); + + // Create a new random recipient + const recipient = keyring.addFromSeed(randomAsU8a(32)).address; + + console.log('Sending', AMOUNT, 'from', address, 'who has a balance of', balance.free, 'to', recipient, 'with nonce', nonce.toString()); + + api.tx.balances + .transfer(recipient, AMOUNT) + .signAndSend(alice, { nonce }, ({ events = [], status }) => { + console.log('Transaction status:', status.type); + + if (status.isInBlock) { + console.log('Included at block hash', status.asInBlock.toHex()); + console.log('Events:'); + + events.forEach(({ event: { data, method, section }, phase }) => { + console.log('\t', phase.toString(), `: ${section}.${method}`, data.toString()); + }); + } else if (status.isFinalized) { + console.log('Finalized block hash', status.asFinalized.toHex()); + + process.exit(0); + } + }); +} + +main().catch(console.error); diff --git a/bitacross-worker/service/Cargo.toml b/bitacross-worker/service/Cargo.toml new file mode 100644 index 0000000000..f076e26cf5 --- /dev/null +++ b/bitacross-worker/service/Cargo.toml @@ -0,0 +1,107 @@ +[package] +name = 'bitacross-worker' +version = '0.0.1' +authors = ['Trust Computing GmbH ', 'Integritee AG '] +build = 'build.rs' +edition = '2021' + +[dependencies] +async-trait = "0.1.50" +base58 = "0.2" +clap = { version = "2.33", features = ["yaml"] } +dirs = "3.0.2" +env_logger = "0.9" +futures = "0.3" +hex = "0.4.3" +jsonrpsee = { version = "0.2.0", features = ["client", "ws-server", "macros"] } +lazy_static = "1.4.0" +log = "0.4" +parking_lot = "0.12.1" +parse_duration = "2.1.1" +# for litentry-parachain: otherwise we have a conflict in substrate-prometheus-endpoint +prometheus = { version = "0.13.0", default-features = false, features = ["process"] } +regex = "1.9.5" +scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } +serde = "1.0" +serde_derive = "1.0" +serde_json = "1.0" +thiserror = "1.0" +tokio = { version = "1.6.1", features = ["full"] } +warp = "0.3" + +# ipfs +ipfs-api = "0.11.0" + +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +primitive-types = { version = "0.12.1", default-features = false, features = ["codec"] } + +sgx_crypto_helper = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } +sgx_types = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } + +# local +itc-parentchain = { path = "../core/parentchain/parentchain-crate" } +itc-rest-client = { path = "../core/rest-client" } +itc-rpc-client = { path = "../core/rpc-client" } +itc-rpc-server = { path = "../core/rpc-server" } +itp-api-client-types = { path = "../core-primitives/node-api/api-client-types" } +itp-enclave-api = { path = "../core-primitives/enclave-api" } +itp-enclave-metrics = { path = "../core-primitives/enclave-metrics" } +itp-node-api = { path = "../core-primitives/node-api" } +itp-settings = { path = "../core-primitives/settings" } +itp-storage = { path = "../core-primitives/storage" } +itp-types = { path = "../core-primitives/types" } +itp-utils = { path = "../core-primitives/utils" } +its-consensus-slots = { path = "../sidechain/consensus/slots" } +its-peer-fetch = { path = "../sidechain/peer-fetch" } +its-primitives = { path = "../sidechain/primitives" } +its-rpc-handler = { path = "../sidechain/rpc-handler" } +its-storage = { path = "../sidechain/storage" } + +# `default-features = false` to remove the jsonrpsee dependency. +substrate-api-client = { default-features = false, features = ["std", "sync-api"], git = "https://github.com/scs/substrate-api-client.git", branch = "polkadot-v0.9.42-tag-v0.14.0" } + +# Substrate dependencies +frame-support = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +pallet-balances = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-consensus-grandpa = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42", features = ["full_crypto"] } +sp-keyring = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-runtime = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# litentry +config = "0.13.3" +ita-stf = { path = "../app-libs/stf", default-features = false } +litentry-primitives = { path = "../litentry/primitives" } +my-node-runtime = { package = "rococo-parachain-runtime", path = "../../runtime/rococo" } +sgx-verify = { path = "../../pallets/teerex/sgx-verify", default-features = false } +teerex-primitives = { path = "../../primitives/teerex", default-features = false } + +[features] +default = [] +evm = [] +sidechain = ["itp-settings/sidechain"] +offchain-worker = ["itp-settings/offchain-worker"] +production = ["itp-settings/production"] +teeracle = ["itp-settings/teeracle"] +dcap = [] +attesteer = ["dcap"] +# Must be enabled to build a binary and link it with the enclave successfully. +# This flag is set in the makefile. +# +# Must not be enabled to run cargo test without an sgx-sdk providing environment +# https://github.com/rust-lang/cargo/issues/2549. +# +# It has been chosen to not make this a default feature because this makes test execution +# more ergonomic as we can simply do `cargo test` on the whole workspace like this. +link-binary = [ + "itp-enclave-api/implement-ffi", +] + +[dev-dependencies] +# crates.io +anyhow = "1.0.40" +mockall = "0.11" +# local +itc-parentchain-test = { path = "../core/parentchain/test" } +its-peer-fetch = { path = "../sidechain/peer-fetch", features = ["mocks"] } +its-test = { path = "../sidechain/test" } diff --git a/bitacross-worker/service/build.rs b/bitacross-worker/service/build.rs new file mode 100644 index 0000000000..1fb664ecc0 --- /dev/null +++ b/bitacross-worker/service/build.rs @@ -0,0 +1,31 @@ +// Copyright (C) 2017-2018 Baidu, Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in +// the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Baidu, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +fn main() { + // All the linker options are now defined in `itp-enclave-api-ffi` +} diff --git a/bitacross-worker/service/src/account_funding.rs b/bitacross-worker/service/src/account_funding.rs new file mode 100644 index 0000000000..20f3f14a77 --- /dev/null +++ b/bitacross-worker/service/src/account_funding.rs @@ -0,0 +1,168 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::error::{Error, ServiceResult}; +use itp_node_api::api_client::{AccountApi, ParentchainApi}; +use itp_settings::worker::{ + EXISTENTIAL_DEPOSIT_FACTOR_FOR_INIT_FUNDS, REGISTERING_FEE_FACTOR_FOR_INIT_FUNDS, +}; +use itp_types::parentchain::Balance; +use log::*; +use sp_core::{ + crypto::{AccountId32, Ss58Codec}, + Pair, +}; +use sp_keyring::AccountKeyring; +use sp_runtime::MultiAddress; +use substrate_api_client::{ + extrinsic::BalancesExtrinsics, GetBalance, GetTransactionPayment, SubmitAndWatch, XtStatus, +}; + +/// Information about the enclave on-chain account. +pub trait EnclaveAccountInfo { + fn free_balance(&self) -> ServiceResult; +} + +pub struct EnclaveAccountInfoProvider { + node_api: ParentchainApi, + account_id: AccountId32, +} + +impl EnclaveAccountInfo for EnclaveAccountInfoProvider { + fn free_balance(&self) -> ServiceResult { + self.node_api.get_free_balance(&self.account_id).map_err(|e| e.into()) + } +} + +impl EnclaveAccountInfoProvider { + pub fn new(node_api: ParentchainApi, account_id: AccountId32) -> Self { + EnclaveAccountInfoProvider { node_api, account_id } + } +} + +pub fn setup_account_funding( + api: &ParentchainApi, + accountid: &AccountId32, + encoded_extrinsic: Vec, + is_development_mode: bool, +) -> ServiceResult<()> { + // Account funds + if is_development_mode { + // Development mode, the faucet will ensure that the enclave has enough funds + ensure_account_has_funds(api, accountid)?; + } else { + // Production mode, there is no faucet. + let registration_fees = enclave_registration_fees(api, encoded_extrinsic)?; + info!("Registration fees = {:?}", registration_fees); + let free_balance = api.get_free_balance(accountid)?; + info!("TEE's free balance = {:?}", free_balance); + + let min_required_funds = + registration_fees.saturating_mul(REGISTERING_FEE_FACTOR_FOR_INIT_FUNDS); + let missing_funds = min_required_funds.saturating_sub(free_balance); + + if missing_funds > 0 { + // If there are not enough funds, then the user can send the missing TEER to the enclave address and start again. + println!( + "Enclave account: {:}, missing funds {}", + accountid.to_ss58check(), + missing_funds + ); + return Err(Error::Custom( + "Enclave does not have enough funds on the parentchain to register.".into(), + )) + } + } + Ok(()) +} + +// Alice plays the faucet and sends some funds to the account if balance is low +fn ensure_account_has_funds(api: &ParentchainApi, accountid: &AccountId32) -> Result<(), Error> { + // check account balance + let free_balance = api.get_free_balance(accountid)?; + info!("TEE's free balance = {:?} (Account: {})", free_balance, accountid); + + let existential_deposit = api.get_existential_deposit()?; + info!("Existential deposit is = {:?}", existential_deposit); + + let min_required_funds = + existential_deposit.saturating_mul(EXISTENTIAL_DEPOSIT_FACTOR_FOR_INIT_FUNDS); + let missing_funds = min_required_funds.saturating_sub(free_balance); + + if missing_funds > 0 { + info!("Transfer {:?} from Alice to {}", missing_funds, accountid); + bootstrap_funds_from_alice(api, accountid, missing_funds)?; + } + Ok(()) +} + +fn enclave_registration_fees( + api: &ParentchainApi, + encoded_extrinsic: Vec, +) -> Result { + let reg_fee_details = api.get_fee_details(&encoded_extrinsic.into(), None)?; + match reg_fee_details { + Some(details) => match details.inclusion_fee { + Some(fee) => Ok(fee.inclusion_fee()), + None => Err(Error::Custom( + "Inclusion fee for the registration of the enclave is None!".into(), + )), + }, + None => + Err(Error::Custom("Fee Details for the registration of the enclave is None !".into())), + } +} + +// Alice sends some funds to the account +fn bootstrap_funds_from_alice( + api: &ParentchainApi, + accountid: &AccountId32, + funding_amount: u128, +) -> Result<(), Error> { + let alice = AccountKeyring::Alice.pair(); + let alice_acc = AccountId32::from(*alice.public().as_array_ref()); + + let alice_free = api.get_free_balance(&alice_acc)?; + info!(" Alice's free balance = {:?}", alice_free); + let nonce = api.get_account_next_index(&alice_acc)?; + info!(" Alice's Account Nonce is {}", nonce); + + if funding_amount > alice_free { + println!( + "funding amount is too high: please change EXISTENTIAL_DEPOSIT_FACTOR_FOR_INIT_FUNDS ({:?})", + funding_amount + ); + return Err(Error::ApplicationSetup) + } + + let mut alice_signer_api = api.clone(); + alice_signer_api.set_signer(alice.into()); + + println!("[+] send extrinsic: bootstrap funding Enclave from Alice's funds"); + let xt = alice_signer_api + .balance_transfer_allow_death(MultiAddress::Id(accountid.clone()), funding_amount); + let xt_report = alice_signer_api.submit_and_watch_extrinsic_until(xt, XtStatus::InBlock)?; + info!( + "[<] L1 extrinsic success. extrinsic hash: {:?} / status: {:?}", + xt_report.extrinsic_hash, xt_report.status + ); + // Verify funds have arrived. + let free_balance = alice_signer_api.get_free_balance(accountid); + trace!("TEE's NEW free balance = {:?}", free_balance); + + Ok(()) +} diff --git a/bitacross-worker/service/src/cli.yml b/bitacross-worker/service/src/cli.yml new file mode 100644 index 0000000000..e517e6b1bb --- /dev/null +++ b/bitacross-worker/service/src/cli.yml @@ -0,0 +1,227 @@ +name: "litentry-worker" +version: "0.0.1" +about: Worker using Intel SGX TEE for litentry parachain node +authors: "Trust Computing GmbH " + +# AppSettings can be defined as a list and are **not** ascii case sensitive +settings: + - ColoredHelp + - SubcommandRequired + +# All subcommands must be listed in the 'subcommand:' object, where the key to +# the list is the name of the subcommand, and all settings for that command are +# part of a Hash +args: + - node-url: + short: u + long: node-url + help: Set the url and the protocol of the RPC endpoint. + takes_value: true + default_value: "ws://127.0.0.1" + - node-port: + short: p + long: node-port + help: Set the port of the RPC endpoint. + takes_value: true + default_value: "9944" + - target-a-parentchain-rpc-url: + long: target-a-parentchain-rpc-url + help: Set the url and the protocol of an optional Target A parentchain RPC endpoint that contains your business logic specific pallets. + takes_value: true + required: false + - target-a-parentchain-rpc-port: + long: target-a-parentchain-rpc-port + help: Set the port of the optional Target A parentchain RPC endpoint. + takes_value: true + required: false + - target-b-parentchain-rpc-url: + long: target-b-parentchain-rpc-url + help: Set the url and the protocol of an optional Target B parentchain RPC endpoint that contains your business logic specific pallets. + takes_value: true + required: false + - target-b-parentchain-rpc-port: + long: target-b-parentchain-rpc-port + help: Set the port of the optional Target B parentchain RPC endpoint. + takes_value: true + required: false + - data-dir: + short: d + long: data-dir + help: Data dir where the worker stores it's keys and other data. + takes_value: true + - ws-external: + long: ws-external + help: Set this flag in case the worker should listen to external requests. + - mu-ra-port: + short: r + long: mu-ra-port + help: Set the websocket port to listen for mu-ra requests + takes_value: true + default_value: "3443" + - trusted-worker-port: + short: P + long: trusted-worker-port + help: Set the trusted websocket port of the worker, running directly in the enclave. + takes_value: true + default_value: "2000" + - untrusted-worker-port: + short: w + long: untrusted-worker-port + help: Set the untrusted websocket port of the worker + takes_value: true + default_value: "2001" + - trusted-external-address: + short: T + long: trusted-external-address + help: Set the trusted worker address to be advertised on the parentchain. If no port is given, the same as in `trusted-worker-port` will be used. + takes_value: true + required: false + - untrusted-external-address: + short: U + long: untrusted-external-address + help: Set the untrusted worker address to be retrieved by a trusted rpc call. If no port is given, the same as in `untrusted-worker-port` will be used. + takes_value: true + required: false + - mu-ra-external-address: + short: M + long: mu-ra-external-address + help: Set the mutual remote attestation worker address to be retrieved by a trusted rpc call. If no port is given, the same as in `mu-ra-port` will be used. + takes_value: true + required: false + - enable-metrics: + long: enable-metrics + help: Enable the metrics HTTP server to serve metrics + - metrics-port: + short: i + long: metrics-port + help: Set the port on which the metrics are served. + takes_value: true + default_value: "8787" + required: false + - untrusted-http-port: + short: h + long: untrusted-http-port + help: Set the port for the untrusted HTTP server + takes_value: true + required: false + - clean-reset: + long: clean-reset + short: c + help: Cleans and purges any previous state and key files and generates them anew before starting. + - parentchain-start-block: + long: parentchain-start-block + help: Set the parentchain block number to start syncing with + takes_value: true + required: false + default_value: "0" + - fail-slot-mode: + long: fail-slot-mode + help: Set the mode of failing a slot, values [BeforeOnSlot, AfterOnSlot] + takes_value: true + required: false + - fail-at: + long: fail-at + help: Set the slot to fail on + takes_value: true + required: false + default_value: "0" + +subcommands: + - run: + about: Start the litentry-worker + args: + - skip-ra: + long: skip-ra + help: skip remote attestation. Set this flag if running enclave in SW mode + - shard: + required: false + index: 1 + help: shard identifier base58 encoded. Defines the state that this worker shall operate on. Default is mrenclave + - dev: + long: dev + short: d + help: Set this flag if running in development mode to bootstrap enclave account on parentchain via //Alice. + - request-state: + long: request-state + short: r + help: Run the worker and request key and state provisioning from another worker. + - teeracle-interval: + required: false + long: teeracle-interval + short: i + help: Set the teeracle exchange rate update interval. Example of accepted syntax <5 seconds 15 minutes 2 hours 1 days> or short <5s15m2h1d> + takes_value: true + - reregister-teeracle-interval: + required: false + long: reregister + help: Set the teeracle reregistration interval. Example of accepted syntax <5 seconds 15 minutes 2 hours 1 days> or short <5s15m2h1d> + takes_value: true + - request-state: + about: join a shard by requesting key provisioning from another worker + args: + - shard: + long: shard + required: false + help: shard identifier base58 encoded. Defines the state that this worker shall operate on. Default is mrenclave + - skip-ra: + long: skip-ra + help: skip remote attestation. Set this flag if running enclave in SW mode + - shielding-key: + about: Get the public RSA3072 key from the TEE to be used to encrypt requests + - signing-key: + about: Get the public ed25519 key the TEE uses to sign messages and extrinsics + - dump-ra: + about: Perform RA and dump cert to disk + - mrenclave: + about: Dump mrenclave to stdout. base58 encoded. + - init-shard: + about: Initialize new shard (do this only if you run the first worker for that shard). if shard is not specified, the MRENCLAVE is used instead + args: + - shard: + required: false + multiple: true + index: 1 + help: shard identifier base58 encoded + - migrate-shard: + about: Migrate shard + args: + - old-shard: + long: old-shard + help: shard identifier hex encoded + takes_value: true + - new-shard: + long: new-shard + help: shard identifier hex encoded + takes_value: true + - test: + about: Run tests involving the enclave + takes_value: true + args: + - all: + short: a + long: all + help: Run all tests (beware, all corrupts the counter state for some whatever reason...) + takes_value: false + - unit: + short: u + long: unit + help: Run unit tests + takes_value: false + - ecall: + short: e + long: ecall + help: Run enclave ecall tests + takes_value: false + - integration: + short: i + long: integration + help: Run integration tests + takes_value: false + - provisioning-server: + long: provisioning-server + help: Run TEE server for MU-RA key provisioning + takes_value: false + - provisioning-client: + long: provisioning-client + help: Run TEE client for MU-RA key provisioning + takes_value: false diff --git a/bitacross-worker/service/src/config.rs b/bitacross-worker/service/src/config.rs new file mode 100644 index 0000000000..bc9b8b7cdb --- /dev/null +++ b/bitacross-worker/service/src/config.rs @@ -0,0 +1,641 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use clap::ArgMatches; +use itc_rest_client::rest_client::Url; +use itp_settings::teeracle::{DEFAULT_MARKET_DATA_UPDATE_INTERVAL, ONE_DAY, THIRTY_MINUTES}; +use parse_duration::parse; +use serde::{Deserialize, Serialize}; +use std::{ + fs, + path::{Path, PathBuf}, + time::Duration, +}; + +static DEFAULT_NODE_URL: &str = "ws://127.0.0.1"; +static DEFAULT_NODE_PORT: &str = "9944"; +static DEFAULT_TRUSTED_PORT: &str = "2000"; +static DEFAULT_UNTRUSTED_PORT: &str = "2001"; +static DEFAULT_MU_RA_PORT: &str = "3443"; +static DEFAULT_METRICS_PORT: &str = "8787"; +static DEFAULT_UNTRUSTED_HTTP_PORT: &str = "4545"; +static DEFAULT_PARENTCHAIN_START_BLOCK: &str = "0"; +static DEFAULT_FAIL_AT: &str = "0"; + +#[derive(Clone, Debug, PartialEq)] +pub struct Config { + pub litentry_rpc_url: String, + pub litentry_rpc_port: String, + pub target_a_parentchain_rpc_url: Option, + pub target_a_parentchain_rpc_port: Option, + pub target_b_parentchain_rpc_url: Option, + pub target_b_parentchain_rpc_port: Option, + pub worker_ip: String, + /// Trusted worker address that will be advertised on the parentchain. + pub trusted_external_worker_address: Option, + /// Port to directly communicate with the trusted tls server inside the enclave. + pub trusted_worker_port: String, + /// Untrusted worker address that will be returned by the dedicated trusted ws rpc call. + pub untrusted_external_worker_address: Option, + /// Port to the untrusted ws of the validateer. + pub untrusted_worker_port: String, + /// Mutual remote attestation address that will be returned by the dedicated trusted ws rpc call. + pub mu_ra_external_address: Option, + /// Port for mutual-remote attestation requests. + pub mu_ra_port: String, + /// Enable the metrics server + pub enable_metrics_server: bool, + /// Port for the metrics server + pub metrics_server_port: String, + /// Port for the untrusted HTTP server (e.g. for `is_initialized`) + pub untrusted_http_port: String, + /// Data directory used by all the services. + pub data_dir: PathBuf, + /// Config of the 'run' subcommand + pub run_config: Option, + + /// the parentchain block number to start syncing with + pub parentchain_start_block: String, + /// mode to use for failing sidechain slot + pub fail_slot_mode: Option, + /// slot number to fail at + pub fail_at: u64, +} + +#[allow(clippy::too_many_arguments)] +impl Config { + pub fn new( + litentry_rpc_url: String, + litentry_rpc_port: String, + target_a_parentchain_rpc_url: Option, + target_a_parentchain_rpc_port: Option, + target_b_parentchain_rpc_url: Option, + target_b_parentchain_rpc_port: Option, + worker_ip: String, + trusted_external_worker_address: Option, + trusted_worker_port: String, + untrusted_external_worker_address: Option, + untrusted_worker_port: String, + mu_ra_external_address: Option, + mu_ra_port: String, + enable_metrics_server: bool, + metrics_server_port: String, + untrusted_http_port: String, + data_dir: PathBuf, + run_config: Option, + parentchain_start_block: String, + fail_slot_mode: Option, + fail_at: u64, + ) -> Self { + Self { + litentry_rpc_url, + litentry_rpc_port, + target_a_parentchain_rpc_url, + target_a_parentchain_rpc_port, + target_b_parentchain_rpc_url, + target_b_parentchain_rpc_port, + worker_ip, + trusted_external_worker_address, + trusted_worker_port, + untrusted_external_worker_address, + untrusted_worker_port, + mu_ra_external_address, + mu_ra_port, + enable_metrics_server, + metrics_server_port, + untrusted_http_port, + data_dir, + run_config, + parentchain_start_block, + fail_slot_mode, + fail_at, + } + } + + /// Integritee RPC endpoint (including ws://). + pub fn litentry_rpc_endpoint(&self) -> String { + format!("{}:{}", self.litentry_rpc_url, self.litentry_rpc_port) + } + + pub fn target_a_parentchain_rpc_endpoint(&self) -> Option { + if self.target_a_parentchain_rpc_url.is_some() + && self.target_a_parentchain_rpc_port.is_some() + { + return Some(format!( + "{}:{}", + // Can be done better, but this code is obsolete anyhow with clap v4. + self.target_a_parentchain_rpc_url.clone().unwrap(), + self.target_a_parentchain_rpc_port.clone().unwrap() + )) + }; + + None + } + + pub fn target_b_parentchain_rpc_endpoint(&self) -> Option { + if self.target_b_parentchain_rpc_url.is_some() + && self.target_b_parentchain_rpc_port.is_some() + { + return Some(format!( + "{}:{}", + // Can be done better, but this code is obsolete anyhow with clap v4. + self.target_b_parentchain_rpc_url.clone().unwrap(), + self.target_b_parentchain_rpc_port.clone().unwrap() + )) + }; + + None + } + + pub fn trusted_worker_url_internal(&self) -> String { + format!("{}:{}", self.worker_ip, self.trusted_worker_port) + } + + /// Returns the trusted worker url that should be addressed by external clients. + pub fn trusted_worker_url_external(&self) -> String { + match &self.trusted_external_worker_address { + Some(external_address) => external_address.to_string(), + None => format!("wss://{}:{}", self.worker_ip, self.trusted_worker_port), + } + } + + pub fn untrusted_worker_url(&self) -> String { + format!("{}:{}", self.worker_ip, self.untrusted_worker_port) + } + + /// Returns the untrusted worker url that should be addressed by external clients. + pub fn untrusted_worker_url_external(&self) -> String { + match &self.untrusted_external_worker_address { + Some(external_address) => external_address.to_string(), + None => format!("ws://{}:{}", self.worker_ip, self.untrusted_worker_port), + } + } + + pub fn mu_ra_url(&self) -> String { + format!("{}:{}", self.worker_ip, self.mu_ra_port) + } + + /// Returns the mutual remote attestion worker url that should be addressed by external workers. + pub fn mu_ra_url_external(&self) -> String { + match &self.mu_ra_external_address { + Some(external_address) => external_address.to_string(), + None => format!("{}:{}", self.worker_ip, self.mu_ra_port), + } + } + + pub fn data_dir(&self) -> &Path { + self.data_dir.as_path() + } + + pub fn run_config(&self) -> &Option { + &self.run_config + } + + pub fn enable_metrics_server(&self) -> bool { + self.enable_metrics_server + } + + pub fn try_parse_metrics_server_port(&self) -> Option { + self.metrics_server_port.parse::().ok() + } + + pub fn try_parse_untrusted_http_server_port(&self) -> Option { + self.untrusted_http_port.parse::().ok() + } + + pub fn try_parse_parentchain_start_block(&self) -> Option { + self.parentchain_start_block.parse::().ok() + } +} + +impl From<&ArgMatches<'_>> for Config { + fn from(m: &ArgMatches<'_>) -> Self { + let trusted_port = m.value_of("trusted-worker-port").unwrap_or(DEFAULT_TRUSTED_PORT); + let untrusted_port = m.value_of("untrusted-worker-port").unwrap_or(DEFAULT_UNTRUSTED_PORT); + let mu_ra_port = m.value_of("mu-ra-port").unwrap_or(DEFAULT_MU_RA_PORT); + let is_metrics_server_enabled = m.is_present("enable-metrics"); + let metrics_server_port = m.value_of("metrics-port").unwrap_or(DEFAULT_METRICS_PORT); + let untrusted_http_port = + m.value_of("untrusted-http-port").unwrap_or(DEFAULT_UNTRUSTED_HTTP_PORT); + + let data_dir = match m.value_of("data-dir") { + Some(d) => { + let p = PathBuf::from(d); + if !p.exists() { + log::info!("Creating new data-directory for the service {}.", p.display()); + fs::create_dir_all(p.as_path()).unwrap(); + } else { + log::info!("Starting service in existing directory {}.", p.display()); + } + p + }, + None => { + log::warn!("[Config] defaulting to data-dir = PWD because it was previous behaviour. This might change soon.\ + Please pass the data-dir explicitly to ensure nothing breaks in your setup."); + pwd() + }, + }; + + let run_config = m.subcommand_matches("run").map(RunConfig::from); + + let parentchain_start_block = + m.value_of("parentchain-start-block").unwrap_or(DEFAULT_PARENTCHAIN_START_BLOCK); + let fail_slot_mode = m.value_of("fail-slot-mode").map(|v| v.to_string()); + let fail_at = m.value_of("fail-at").unwrap_or(DEFAULT_FAIL_AT).parse().unwrap(); + Self::new( + m.value_of("node-url").unwrap_or(DEFAULT_NODE_URL).into(), + m.value_of("node-port").unwrap_or(DEFAULT_NODE_PORT).into(), + m.value_of("target-a-parentchain-rpc-url").map(Into::into), + m.value_of("target-a-parentchain-rpc-port").map(Into::into), + m.value_of("target-b-parentchain-rpc-url").map(Into::into), + m.value_of("target-b-parentchain-rpc-port").map(Into::into), + if m.is_present("ws-external") { "0.0.0.0".into() } else { "127.0.0.1".into() }, + m.value_of("trusted-external-address") + .map(|url| add_port_if_necessary(url, trusted_port)), + trusted_port.to_string(), + m.value_of("untrusted-external-address") + .map(|url| add_port_if_necessary(url, untrusted_port)), + untrusted_port.to_string(), + m.value_of("mu-ra-external-address") + .map(|url| add_port_if_necessary(url, mu_ra_port)), + mu_ra_port.to_string(), + is_metrics_server_enabled, + metrics_server_port.to_string(), + untrusted_http_port.to_string(), + data_dir, + run_config, + parentchain_start_block.to_string(), + fail_slot_mode, + fail_at, + ) + } +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RunConfig { + /// Skip remote attestation. Set this flag if running enclave in SW mode + skip_ra: bool, + /// Set this flag if running in development mode to bootstrap enclave account on parentchain via //Alice. + dev: bool, + /// Request key and state provisioning from a peer worker. + request_state: bool, + /// Shard identifier base58 encoded. Defines the shard that this worker operates on. Default is mrenclave. + shard: Option, + /// Optional teeracle update interval + teeracle_update_interval: Option, + /// Optional teeracle reregistration interval + reregister_teeracle_interval: Option, + /// Marblerun's Prometheus endpoint base URL + marblerun_base_url: Option, +} + +impl RunConfig { + pub fn skip_ra(&self) -> bool { + self.skip_ra + } + + pub fn dev(&self) -> bool { + self.dev + } + + pub fn request_state(&self) -> bool { + self.request_state + } + + pub fn shard(&self) -> Option<&str> { + self.shard.as_deref() + } + + pub fn teeracle_update_interval(&self) -> Duration { + self.teeracle_update_interval.unwrap_or(DEFAULT_MARKET_DATA_UPDATE_INTERVAL) + } + + /// The periodic registration period of the teeracle. + /// + /// Defaults to 23h30m, as this is slightly below the currently configured automatic + /// deregistration period on the Integritee chains. + pub fn reregister_teeracle_interval(&self) -> Duration { + // Todo: Derive this from chain https://github.com/integritee-network/worker/issues/1351 + self.reregister_teeracle_interval.unwrap_or(ONE_DAY - THIRTY_MINUTES) + } + + pub fn marblerun_base_url(&self) -> &str { + // This conflicts with the default port of a substrate node, but it is indeed the + // default port of marblerun too: + // https://github.com/edgelesssys/marblerun/blob/master/docs/docs/workflows/monitoring.md?plain=1#L26 + self.marblerun_base_url.as_deref().unwrap_or("http://localhost:9944") + } +} + +impl From<&ArgMatches<'_>> for RunConfig { + fn from(m: &ArgMatches<'_>) -> Self { + let skip_ra = m.is_present("skip-ra"); + let dev = m.is_present("dev"); + let request_state = m.is_present("request-state"); + let shard = m.value_of("shard").map(|s| s.to_string()); + let teeracle_update_interval = m.value_of("teeracle-interval").map(|i| { + parse(i).unwrap_or_else(|e| panic!("teeracle-interval parsing error {:?}", e)) + }); + let reregister_teeracle_interval = m.value_of("reregister-teeracle-interval").map(|i| { + parse(i).unwrap_or_else(|e| panic!("teeracle-interval parsing error {:?}", e)) + }); + + let marblerun_base_url = m.value_of("marblerun-url").map(|i| { + Url::parse(i) + .unwrap_or_else(|e| panic!("marblerun-url parsing error: {:?}", e)) + .to_string() + }); + + Self { + skip_ra, + dev, + request_state, + shard, + teeracle_update_interval, + reregister_teeracle_interval, + marblerun_base_url, + } + } +} + +fn add_port_if_necessary(url: &str, port: &str) -> String { + // [Option("ws(s)"), ip, Option(port)] + match url.split(':').count() { + 3 => url.to_string(), + 2 => { + if url.contains("ws") { + // url is of format ws://127.0.0.1, no port added + format!("{}:{}", url, port) + } else { + // url is of format 127.0.0.1:4000, port was added + url.to_string() + } + }, + 1 => format!("{}:{}", url, port), + _ => panic!("Invalid worker url format in url input {:?}", url), + } +} + +pub fn pwd() -> PathBuf { + std::env::current_dir().expect("works on all supported platforms; qed.") +} + +#[cfg(test)] +mod test { + use super::*; + use std::{assert_matches::assert_matches, collections::HashMap}; + + #[test] + fn check_correct_config_assignment_for_empty_input() { + let empty_args = ArgMatches::default(); + let config = Config::from(&empty_args); + let expected_worker_ip = "127.0.0.1"; + + assert_eq!(config.litentry_rpc_url, DEFAULT_NODE_URL); + assert_eq!(config.litentry_rpc_port, DEFAULT_NODE_PORT); + assert_eq!(config.target_a_parentchain_rpc_url, None); + assert_eq!(config.target_a_parentchain_rpc_port, None); + assert_eq!(config.target_b_parentchain_rpc_url, None); + assert_eq!(config.target_b_parentchain_rpc_port, None); + assert_eq!(config.trusted_worker_port, DEFAULT_TRUSTED_PORT); + assert_eq!(config.untrusted_worker_port, DEFAULT_UNTRUSTED_PORT); + assert_eq!(config.mu_ra_port, DEFAULT_MU_RA_PORT); + assert_eq!(config.worker_ip, expected_worker_ip); + assert!(config.trusted_external_worker_address.is_none()); + assert!(config.untrusted_external_worker_address.is_none()); + assert!(config.mu_ra_external_address.is_none()); + assert!(!config.enable_metrics_server); + assert_eq!(config.untrusted_http_port, DEFAULT_UNTRUSTED_HTTP_PORT); + assert_eq!(config.data_dir, pwd()); + assert!(config.run_config.is_none()); + assert_eq!(config.parentchain_start_block, DEFAULT_PARENTCHAIN_START_BLOCK); + assert_matches!(config.fail_slot_mode, Option::None); + assert_eq!(config.fail_at, DEFAULT_FAIL_AT.parse::().unwrap()) + } + + #[test] + fn worker_ip_is_set_correctly_for_set_ws_external_flag() { + let expected_worker_ip = "0.0.0.0"; + + let mut args = ArgMatches::default(); + args.args = HashMap::from([("ws-external", Default::default())]); + let config = Config::from(&args); + + assert_eq!(config.worker_ip, expected_worker_ip); + } + + #[test] + fn check_correct_config_assignment_for_given_input() { + let node_ip = "ws://12.1.58.1"; + let node_port = "111111"; + let trusted_ext_addr = "wss://1.1.1.2:700"; + let trusted_port = "7119"; + let untrusted_ext_addr = "ws://1.723.3.1:11"; + let untrusted_port = "9119"; + let mu_ra_ext_addr = "1.1.3.1:1000"; + let mu_ra_port = "99"; + let untrusted_http_port = "4321"; + + let parentchain_start_block = "30"; + + let mut args = ArgMatches::default(); + args.args = HashMap::from([ + ("node-url", Default::default()), + ("node-port", Default::default()), + ("ws-external", Default::default()), + ("trusted-external-address", Default::default()), + ("untrusted-external-address", Default::default()), + ("mu-ra-external-address", Default::default()), + ("mu-ra-port", Default::default()), + ("untrusted-worker-port", Default::default()), + ("trusted-worker-port", Default::default()), + ("untrusted-http-port", Default::default()), + ("mock-server-port", Default::default()), + ("parentchain-start-block", Default::default()), + ]); + // Workaround because MatchedArg is private. + args.args.get_mut("node-url").unwrap().vals = vec![node_ip.into()]; + args.args.get_mut("node-port").unwrap().vals = vec![node_port.into()]; + args.args.get_mut("trusted-external-address").unwrap().vals = vec![trusted_ext_addr.into()]; + args.args.get_mut("untrusted-external-address").unwrap().vals = + vec![untrusted_ext_addr.into()]; + args.args.get_mut("mu-ra-external-address").unwrap().vals = vec![mu_ra_ext_addr.into()]; + args.args.get_mut("mu-ra-port").unwrap().vals = vec![mu_ra_port.into()]; + args.args.get_mut("untrusted-worker-port").unwrap().vals = vec![untrusted_port.into()]; + args.args.get_mut("trusted-worker-port").unwrap().vals = vec![trusted_port.into()]; + args.args.get_mut("untrusted-http-port").unwrap().vals = vec![untrusted_http_port.into()]; + args.args.get_mut("parentchain-start-block").unwrap().vals = + vec![parentchain_start_block.into()]; + + let config = Config::from(&args); + + assert_eq!(config.litentry_rpc_url, node_ip); + assert_eq!(config.litentry_rpc_port, node_port); + assert_eq!(config.trusted_worker_port, trusted_port); + assert_eq!(config.untrusted_worker_port, untrusted_port); + assert_eq!(config.mu_ra_port, mu_ra_port); + assert_eq!(config.trusted_external_worker_address, Some(trusted_ext_addr.to_string())); + assert_eq!(config.untrusted_external_worker_address, Some(untrusted_ext_addr.to_string())); + assert_eq!(config.mu_ra_external_address, Some(mu_ra_ext_addr.to_string())); + assert_eq!(config.untrusted_http_port, untrusted_http_port.to_string()); + assert_eq!(config.parentchain_start_block, parentchain_start_block.to_string()); + } + + #[test] + fn default_run_config_is_correct() { + let empty_args = ArgMatches::default(); + let run_config = RunConfig::from(&empty_args); + + assert_eq!(run_config.request_state, false); + assert_eq!(run_config.dev, false); + assert_eq!(run_config.skip_ra, false); + assert!(run_config.shard.is_none()); + assert!(run_config.teeracle_update_interval.is_none()); + } + + #[test] + fn run_config_parsing_works() { + let shard_identifier = "shard-identifier"; + + let mut args = ArgMatches::default(); + args.args = HashMap::from([ + ("request-state", Default::default()), + ("dev", Default::default()), + ("skip-ra", Default::default()), + ("shard", Default::default()), + ("teeracle-interval", Default::default()), + ]); + // Workaround because MatchedArg is private. + args.args.get_mut("shard").unwrap().vals = vec![shard_identifier.into()]; + args.args.get_mut("teeracle-interval").unwrap().vals = vec!["42s".into()]; + + let run_config = RunConfig::from(&args); + + assert_eq!(run_config.request_state, true); + assert_eq!(run_config.dev, true); + assert_eq!(run_config.skip_ra, true); + assert_eq!(run_config.shard.unwrap(), shard_identifier.to_string()); + assert_eq!(run_config.teeracle_update_interval.unwrap(), Duration::from_secs(42)); + } + + #[test] + fn external_addresses_are_returned_correctly_if_not_set() { + let trusted_port = "7119"; + let untrusted_port = "9119"; + let mu_ra_port = "99"; + let expected_worker_ip = "127.0.0.1"; + + let mut args = ArgMatches::default(); + args.args = HashMap::from([ + ("mu-ra-port", Default::default()), + ("untrusted-worker-port", Default::default()), + ("trusted-worker-port", Default::default()), + ]); + // Workaround because MatchedArg is private. + args.args.get_mut("mu-ra-port").unwrap().vals = vec![mu_ra_port.into()]; + args.args.get_mut("untrusted-worker-port").unwrap().vals = vec![untrusted_port.into()]; + args.args.get_mut("trusted-worker-port").unwrap().vals = vec![trusted_port.into()]; + + let config = Config::from(&args); + + assert_eq!( + config.trusted_worker_url_external(), + format!("wss://{}:{}", expected_worker_ip, trusted_port) + ); + assert_eq!( + config.untrusted_worker_url_external(), + format!("ws://{}:{}", expected_worker_ip, untrusted_port) + ); + assert_eq!(config.mu_ra_url_external(), format!("{}:{}", expected_worker_ip, mu_ra_port)); + } + + #[test] + fn teeracle_interval_parsing_panics_if_format_is_invalid() { + let teeracle_interval = "24s_invalid-format"; + let mut args = ArgMatches::default(); + args.args = HashMap::from([("teeracle-interval", Default::default())]); + args.args.get_mut("teeracle-interval").unwrap().vals = vec![teeracle_interval.into()]; + + let result = std::panic::catch_unwind(|| RunConfig::from(&args)); + assert!(result.is_err()); + } + + #[test] + fn external_addresses_are_returned_correctly_if_set() { + let trusted_ext_addr = "wss://1.1.1.2:700"; + let untrusted_ext_addr = "ws://1.723.3.1:11"; + let mu_ra_ext_addr = "1.1.3.1:1000"; + + let mut args = ArgMatches::default(); + args.args = HashMap::from([ + ("trusted-external-address", Default::default()), + ("untrusted-external-address", Default::default()), + ("mu-ra-external-address", Default::default()), + ]); + // Workaround because MatchedArg is private. + args.args.get_mut("trusted-external-address").unwrap().vals = vec![trusted_ext_addr.into()]; + args.args.get_mut("untrusted-external-address").unwrap().vals = + vec![untrusted_ext_addr.into()]; + args.args.get_mut("mu-ra-external-address").unwrap().vals = vec![mu_ra_ext_addr.into()]; + + let config = Config::from(&args); + + assert_eq!(config.trusted_worker_url_external(), trusted_ext_addr); + assert_eq!(config.untrusted_worker_url_external(), untrusted_ext_addr); + assert_eq!(config.mu_ra_url_external(), mu_ra_ext_addr); + } + + #[test] + fn ensure_no_port_is_added_to_url_with_port() { + let url = "ws://hello:4000"; + let port = "0"; + + let resulting_url = add_port_if_necessary(url, port); + + assert_eq!(resulting_url, url); + } + + #[test] + fn ensure_port_is_added_to_url_without_port() { + let url = "wss://hello"; + let port = "0"; + + let resulting_url = add_port_if_necessary(url, port); + + assert_eq!(resulting_url, format!("{}:{}", url, port)); + } + + #[test] + fn ensure_no_port_is_added_to_url_with_port_without_prefix() { + let url = "hello:10001"; + let port = "012"; + + let resulting_url = add_port_if_necessary(url, port); + + assert_eq!(resulting_url, url); + } + + #[test] + fn ensure_port_is_added_to_url_without_port_without_prefix() { + let url = "hello_world"; + let port = "10"; + + let resulting_url = add_port_if_necessary(url, port); + + assert_eq!(resulting_url, format!("{}:{}", url, port)); + } +} diff --git a/bitacross-worker/service/src/enclave/api.rs b/bitacross-worker/service/src/enclave/api.rs new file mode 100644 index 0000000000..e3901672d0 --- /dev/null +++ b/bitacross-worker/service/src/enclave/api.rs @@ -0,0 +1,114 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::config::Config; +use itp_enclave_api::{enclave_base::EnclaveBase, error::Error as EnclaveApiError, EnclaveResult}; +use itp_settings::files::{ENCLAVE_FILE, ENCLAVE_TOKEN}; +use log::*; +use sgx_types::*; +use std::{ + fs::File, + io::{Read, Write}, + path::PathBuf, +}; + +use itp_enclave_api::{Enclave, SgxEnclave}; + +pub fn enclave_init(config: &Config) -> EnclaveResult { + const LEN: usize = 1024; + let mut launch_token = [0; LEN]; + let mut launch_token_updated = 0; + + // Step 1: try to retrieve the launch token saved by last transaction + // if there is no token, then create a new one. + // + // try to get the token saved in $HOME */ + let mut home_dir = PathBuf::new(); + let use_token = match dirs::home_dir() { + Some(path) => { + info!("[+] Home dir is {}", path.display()); + home_dir = path; + true + }, + None => { + error!("[-] Cannot get home dir"); + false + }, + }; + let token_file = home_dir.join(ENCLAVE_TOKEN); + if use_token { + match File::open(&token_file) { + Err(_) => { + info!( + "[-] Token file {} not found! Will create one.", + token_file.as_path().to_str().unwrap() + ); + }, + Ok(mut f) => { + info!("[+] Open token file success! "); + match f.read(&mut launch_token) { + Ok(LEN) => { + info!("[+] Token file valid!"); + }, + _ => info!("[+] Token file invalid, will create new token file"), + } + }, + } + } + + // Step 2: call sgx_create_enclave to initialize an enclave instance + // Debug Support: 1 = debug mode, 0 = not debug mode + #[cfg(not(feature = "production"))] + let debug = 1; + #[cfg(feature = "production")] + let debug = 0; + + let mut misc_attr = + sgx_misc_attribute_t { secs_attr: sgx_attributes_t { flags: 0, xfrm: 0 }, misc_select: 0 }; + let enclave = (SgxEnclave::create( + ENCLAVE_FILE, + debug, + &mut launch_token, + &mut launch_token_updated, + &mut misc_attr, + )) + .map_err(EnclaveApiError::Sgx)?; + + // Step 3: save the launch token if it is updated + if use_token && launch_token_updated != 0 { + // reopen the file with write capability + match File::create(&token_file) { + Ok(mut f) => match f.write_all(&launch_token) { + Ok(()) => info!("[+] Saved updated launch token!"), + Err(_) => error!("[-] Failed to save updated launch token!"), + }, + Err(_) => { + warn!("[-] Failed to save updated enclave token, but doesn't matter"); + }, + } + } + + // create an enclave API and initialize it + let enclave_api = Enclave::new(enclave); + enclave_api.init( + &config.mu_ra_url_external(), + &config.untrusted_worker_url_external(), + &config.data_dir().display().to_string(), + )?; + + Ok(enclave_api) +} diff --git a/bitacross-worker/service/src/enclave/mod.rs b/bitacross-worker/service/src/enclave/mod.rs new file mode 100644 index 0000000000..bb9ba4fe84 --- /dev/null +++ b/bitacross-worker/service/src/enclave/mod.rs @@ -0,0 +1,20 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(feature = "link-binary")] +pub mod api; +pub mod tls_ra; diff --git a/bitacross-worker/service/src/enclave/tls_ra.rs b/bitacross-worker/service/src/enclave/tls_ra.rs new file mode 100644 index 0000000000..cc07e3f4e9 --- /dev/null +++ b/bitacross-worker/service/src/enclave/tls_ra.rs @@ -0,0 +1,110 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +use itp_enclave_api::{ + error::Error, + remote_attestation::{RemoteAttestation, TlsRemoteAttestation}, + EnclaveResult, +}; +use itp_types::ShardIdentifier; +use log::*; +use sgx_types::*; +use std::{ + net::{TcpListener, TcpStream}, + os::unix::io::AsRawFd, +}; + +pub fn enclave_run_state_provisioning_server( + enclave_api: &E, + sign_type: sgx_quote_sign_type_t, + quoting_enclave_target_info: Option<&sgx_target_info_t>, + quote_size: Option<&u32>, + addr: &str, + skip_ra: bool, +) { + info!("Starting MU-RA-Server on: {}", addr); + let listener = match TcpListener::bind(addr) { + Ok(l) => l, + Err(e) => { + error!("error starting MU-RA server on {}: {}", addr, e); + return + }, + }; + loop { + match listener.accept() { + Ok((socket, addr)) => { + info!("[MU-RA-Server] a worker at {} is requesting key provisiong", addr); + + let result = enclave_api.run_state_provisioning_server( + socket.as_raw_fd(), + sign_type, + quoting_enclave_target_info, + quote_size, + skip_ra, + ); + + match result { + Ok(_) => { + debug!("[MU-RA-Server] ECALL success!"); + }, + Err(e) => { + error!("[MU-RA-Server] ECALL Enclave Failed {:?}!", e); + }, + } + }, + Err(e) => error!("couldn't get client: {:?}", e), + } + } +} + +pub fn enclave_request_state_provisioning( + enclave_api: &E, + sign_type: sgx_quote_sign_type_t, + addr: &str, + shard: &ShardIdentifier, + skip_ra: bool, +) -> EnclaveResult<()> { + info!("[MU-RA-Client] Requesting key provisioning from {}", addr); + + let stream = TcpStream::connect(addr).map_err(|e| Error::Other(Box::new(e)))?; + + let quoting_enclave_target_info = if !skip_ra { + match enclave_api.qe_get_target_info() { + Ok(quote_size) => Some(quote_size), + Err(e) => return Err(e), + } + } else { + None + }; + + let quote_size = if !skip_ra { + match enclave_api.qe_get_quote_size() { + Ok(quote_size) => Some(quote_size), + Err(e) => return Err(e), + } + } else { + None + }; + + enclave_api.request_state_provisioning( + stream.as_raw_fd(), + sign_type, + quoting_enclave_target_info.as_ref(), + quote_size.as_ref(), + shard, + skip_ra, + ) +} diff --git a/bitacross-worker/service/src/error.rs b/bitacross-worker/service/src/error.rs new file mode 100644 index 0000000000..c99f51e6fc --- /dev/null +++ b/bitacross-worker/service/src/error.rs @@ -0,0 +1,61 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +use codec::Error as CodecError; +use itp_node_api::api_client::ApiClientError; +use itp_types::ShardIdentifier; + +pub type ServiceResult = Result; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("{0}")] + Codec(#[from] CodecError), + #[error("{0:?}")] + ApiClient(ApiClientError), + #[error("Node API terminated subscription unexpectedly")] + ApiSubscriptionDisconnected, + #[error("Enclave API error: {0}")] + EnclaveApi(#[from] itp_enclave_api::error::Error), + #[error("Trusted Rpc Client error: {0}")] + TrustedRpcClient(#[from] itc_rpc_client::error::Error), + #[error("{0}")] + JsonRpSeeClient(#[from] jsonrpsee::types::Error), + #[error("{0}")] + Serialization(#[from] serde_json::Error), + #[error("{0}")] + FromUtf8(#[from] std::string::FromUtf8Error), + #[error("Application setup error!")] + ApplicationSetup, + #[error("Failed to find any peer worker")] + NoPeerWorkerFound, + #[error("No worker for shard {0} found on parentchain")] + NoWorkerForShardFound(ShardIdentifier), + #[error("Returned empty parentchain block vec after sync, even though there have been blocks given as input")] + EmptyChunk, + #[error("Could not find genesis header of the parentchain")] + MissingGenesisHeader, + #[error("Could not find last finalized block of the parentchain")] + MissingLastFinalizedBlock, + #[error("{0}")] + Custom(Box), +} + +impl From for Error { + fn from(error: ApiClientError) -> Self { + Error::ApiClient(error) + } +} diff --git a/bitacross-worker/service/src/globals/mod.rs b/bitacross-worker/service/src/globals/mod.rs new file mode 100644 index 0000000000..ee250661c5 --- /dev/null +++ b/bitacross-worker/service/src/globals/mod.rs @@ -0,0 +1,19 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pub mod tokio_handle; diff --git a/bitacross-worker/service/src/globals/tokio_handle.rs b/bitacross-worker/service/src/globals/tokio_handle.rs new file mode 100644 index 0000000000..54e49d985e --- /dev/null +++ b/bitacross-worker/service/src/globals/tokio_handle.rs @@ -0,0 +1,108 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use lazy_static::lazy_static; +use parking_lot::RwLock; +use tokio::runtime::Handle; + +lazy_static! { + static ref TOKIO_HANDLE: RwLock> = RwLock::new(None); +} + +/// Wrapper for accessing a tokio handle +pub trait GetTokioHandle { + fn get_handle(&self) -> Handle; +} + +/// implementation, using a static global variable internally +/// +pub struct GlobalTokioHandle; + +/// these are the static (global) accessors +/// reduce their usage where possible and use an instance of TokioHandleAccessorImpl or the trait +impl GlobalTokioHandle { + /// this needs to be called once at application startup! + pub fn initialize() { + let rt = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .worker_threads(2) + .build() + .unwrap(); + *TOKIO_HANDLE.write() = Some(rt); + } + + /// static / global getter of the handle (try to keep private!, use trait to access handle) + fn read_handle() -> Handle { + TOKIO_HANDLE + .read() + .as_ref() + .expect("Tokio handle has not been initialized!") + .handle() + .clone() + } +} + +impl GetTokioHandle for GlobalTokioHandle { + fn get_handle(&self) -> Handle { + GlobalTokioHandle::read_handle() + } +} + +/// Implementation for a scoped Tokio handle. +/// +/// +pub struct ScopedTokioHandle { + tokio_runtime: tokio::runtime::Runtime, +} + +impl Default for ScopedTokioHandle { + fn default() -> Self { + ScopedTokioHandle { tokio_runtime: tokio::runtime::Runtime::new().unwrap() } + } +} + +impl GetTokioHandle for ScopedTokioHandle { + fn get_handle(&self) -> Handle { + self.tokio_runtime.handle().clone() + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[tokio::test] + async fn given_initialized_tokio_handle_when_runtime_goes_out_of_scope_then_async_handle_is_valid( + ) { + // initialize the global handle + // be aware that if you write more tests here, the global state will be shared across multiple threads + // which cargo test spawns. So it can lead to failing tests. + // solution: either get rid of the global state, or write all test functionality in this single test function + { + GlobalTokioHandle::initialize(); + } + + let handle = GlobalTokioHandle.get_handle(); + + let result = handle.spawn_blocking(|| "now running on a worker thread").await; + + assert!(result.is_ok()); + assert!(!result.unwrap().is_empty()) + } +} diff --git a/bitacross-worker/service/src/initialized_service.rs b/bitacross-worker/service/src/initialized_service.rs new file mode 100644 index 0000000000..f35a17fa28 --- /dev/null +++ b/bitacross-worker/service/src/initialized_service.rs @@ -0,0 +1,172 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Service to determine if the integritee services is initialized and registered on the node, +//! hosted on a http server. + +use crate::error::ServiceResult; +use itp_settings::worker_mode::{ProvideWorkerMode, WorkerMode}; +use log::*; +use parking_lot::RwLock; +use std::{default::Default, marker::PhantomData, net::SocketAddr, sync::Arc}; +use warp::Filter; + +pub async fn start_is_initialized_server( + initialization_handler: Arc, + port: u16, +) -> ServiceResult<()> +where + Handler: IsInitialized + Send + Sync + 'static, +{ + let is_initialized_route = warp::path!("is_initialized").and_then(move || { + let handler_clone = initialization_handler.clone(); + async move { + if handler_clone.is_initialized() { + Ok("I am initialized.") + } else { + Err(warp::reject::not_found()) + } + } + }); + + let socket_addr: SocketAddr = ([0, 0, 0, 0], port).into(); + + info!("Running initialized server on: {:?}", socket_addr); + warp::serve(is_initialized_route).run(socket_addr).await; + + info!("Initialized server shut down"); + Ok(()) +} + +/// Trait to query of a worker is considered fully initialized. +pub trait IsInitialized { + fn is_initialized(&self) -> bool; +} + +/// Tracker for initialization. Used by components that ensure these steps were taken. +pub trait TrackInitialization { + fn registered_on_parentchain(&self); + + fn sidechain_block_produced(&self); + + fn worker_for_shard_registered(&self); +} + +pub struct InitializationHandler { + registered_on_parentchain: RwLock, + sidechain_block_produced: RwLock, + worker_for_shard_registered: RwLock, + _phantom: PhantomData, +} + +// Cannot use #[derive(Default)], because the compiler complains that WorkerModeProvider then +// also needs to implement Default. Which does not make sense, since it's only used in PhantomData. +// Explicitly implementing Default solves the problem +// (see https://stackoverflow.com/questions/59538071/the-trait-bound-t-stddefaultdefault-is-not-satisfied-when-using-phantomda). +impl Default for InitializationHandler { + fn default() -> Self { + Self { + registered_on_parentchain: Default::default(), + sidechain_block_produced: Default::default(), + worker_for_shard_registered: Default::default(), + _phantom: Default::default(), + } + } +} + +impl TrackInitialization for InitializationHandler { + fn registered_on_parentchain(&self) { + let mut registered_lock = self.registered_on_parentchain.write(); + *registered_lock = true; + } + + fn sidechain_block_produced(&self) { + let mut block_produced_lock = self.sidechain_block_produced.write(); + *block_produced_lock = true; + } + + fn worker_for_shard_registered(&self) { + let mut registered_lock = self.worker_for_shard_registered.write(); + *registered_lock = true; + } +} + +impl IsInitialized for InitializationHandler +where + WorkerModeProvider: ProvideWorkerMode, +{ + fn is_initialized(&self) -> bool { + match WorkerModeProvider::worker_mode() { + WorkerMode::Sidechain => + *self.registered_on_parentchain.read() + && *self.worker_for_shard_registered.read() + && *self.sidechain_block_produced.read(), + _ => *self.registered_on_parentchain.read(), + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + struct OffchainWorkerMode; + impl ProvideWorkerMode for OffchainWorkerMode { + fn worker_mode() -> WorkerMode { + WorkerMode::OffChainWorker + } + } + + struct SidechainWorkerMode; + impl ProvideWorkerMode for SidechainWorkerMode { + fn worker_mode() -> WorkerMode { + WorkerMode::Sidechain + } + } + + #[test] + fn default_handler_is_initialized_returns_false() { + let offchain_worker_handler = InitializationHandler::::default(); + let sidechain_handler = InitializationHandler::::default(); + + assert!(!offchain_worker_handler.is_initialized()); + assert!(!sidechain_handler.is_initialized()); + } + + #[test] + fn in_offchain_worker_mode_parentchain_registration_is_enough_for_initialized() { + let initialization_handler = InitializationHandler::::default(); + initialization_handler.registered_on_parentchain(); + + assert!(initialization_handler.is_initialized()); + } + + #[test] + fn in_sidechain_mode_all_condition_have_to_be_met() { + let sidechain_handler = InitializationHandler::::default(); + + sidechain_handler.registered_on_parentchain(); + assert!(!sidechain_handler.is_initialized()); + + sidechain_handler.worker_for_shard_registered(); + assert!(!sidechain_handler.is_initialized()); + + sidechain_handler.sidechain_block_produced(); + assert!(sidechain_handler.is_initialized()); + } +} diff --git a/bitacross-worker/service/src/main.rs b/bitacross-worker/service/src/main.rs new file mode 100644 index 0000000000..6c5a888eee --- /dev/null +++ b/bitacross-worker/service/src/main.rs @@ -0,0 +1,52 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#![cfg_attr(test, feature(assert_matches))] +#![allow(unused)] + +mod account_funding; +mod config; +mod enclave; +mod error; +mod globals; +mod initialized_service; +mod ocall_bridge; +mod parentchain_handler; +mod prometheus_metrics; +mod setup; +mod sidechain_setup; +mod sync_block_broadcaster; +mod sync_state; +#[cfg(feature = "teeracle")] +mod teeracle; +mod tests; +mod utils; +mod worker; +mod worker_peers_registry; + +#[cfg(feature = "link-binary")] +pub mod main_impl; + +#[cfg(feature = "link-binary")] +fn main() { + main_impl::main(); +} + +#[cfg(not(feature = "link-binary"))] +fn main() { + panic!("tried to run the binary without linking. Make sure to pass `--feature link-binary`") +} diff --git a/bitacross-worker/service/src/main_impl.rs b/bitacross-worker/service/src/main_impl.rs new file mode 100644 index 0000000000..6860fef3d3 --- /dev/null +++ b/bitacross-worker/service/src/main_impl.rs @@ -0,0 +1,1046 @@ +#[cfg(feature = "teeracle")] +use crate::teeracle::{schedule_periodic_reregistration_thread, start_periodic_market_update}; + +#[cfg(not(feature = "dcap"))] +use crate::utils::check_files; +use crate::{ + account_funding::{setup_account_funding, EnclaveAccountInfoProvider}, + config::Config, + enclave::{ + api::enclave_init, + tls_ra::{enclave_request_state_provisioning, enclave_run_state_provisioning_server}, + }, + error::Error, + globals::tokio_handle::{GetTokioHandle, GlobalTokioHandle}, + initialized_service::{ + start_is_initialized_server, InitializationHandler, IsInitialized, TrackInitialization, + }, + ocall_bridge::{ + bridge_api::Bridge as OCallBridge, component_factory::OCallBridgeComponentFactory, + }, + parentchain_handler::{HandleParentchain, ParentchainHandler}, + prometheus_metrics::{start_metrics_server, EnclaveMetricsReceiver, MetricsHandler}, + setup, + sidechain_setup::{sidechain_init_block_production, sidechain_start_untrusted_rpc_server}, + sync_block_broadcaster::SyncBlockBroadcaster, + sync_state, tests, + utils::extract_shard, + worker::Worker, + worker_peers_registry::WorkerPeersRegistry, +}; +use base58::ToBase58; +use clap::{load_yaml, App, ArgMatches}; +use codec::{Decode, Encode}; +use itp_enclave_api::{ + direct_request::DirectRequest, + enclave_base::EnclaveBase, + remote_attestation::{RemoteAttestation, TlsRemoteAttestation}, + sidechain::Sidechain, + teeracle_api::TeeracleApi, +}; +use itp_node_api::{ + api_client::{AccountApi, PalletTeerexApi, ParentchainApi}, + metadata::NodeMetadata, + node_api_factory::{CreateNodeApi, NodeApiFactory}, +}; +use itp_settings::worker_mode::{ProvideWorkerMode, WorkerMode, WorkerModeProvider}; +use itp_utils::if_production_or; +use its_peer_fetch::{ + block_fetch_client::BlockFetcher, untrusted_peer_fetch::UntrustedPeerFetcher, +}; +use its_primitives::types::block::SignedBlock as SignedSidechainBlock; +use its_storage::{interface::FetchBlocks, BlockPruner, SidechainStorageLock}; +use log::*; +use my_node_runtime::{Hash, Header, RuntimeEvent}; +use regex::Regex; +use serde_json::Value; +use sgx_types::*; +use sp_runtime::traits::Header as HeaderT; +use substrate_api_client::{ + ac_primitives::serde_impls::StorageKey, api::XtStatus, rpc::HandleSubscription, storage_key, + GetChainInfo, GetStorage, SubmitAndWatch, SubscribeChain, SubscribeEvents, +}; +use teerex_primitives::{Enclave as TeerexEnclave, ShardIdentifier}; + +#[cfg(feature = "dcap")] +use sgx_verify::extract_tcb_info_from_raw_dcap_quote; + +use itc_parentchain::primitives::ParentchainId; +use itp_enclave_api::Enclave; +use sp_core::crypto::{AccountId32, Ss58Codec}; +use sp_keyring::AccountKeyring; +use sp_runtime::MultiSigner; +use std::{ + collections::HashSet, env, fmt::Debug, fs::File, io::Read, str, sync::Arc, thread, + time::Duration, +}; +use substrate_api_client::ac_node_api::{EventRecord, Phase::ApplyExtrinsic}; + +const VERSION: &str = env!("CARGO_PKG_VERSION"); + +#[cfg(feature = "link-binary")] +pub type EnclaveWorker = + Worker>; +pub type Event = substrate_api_client::ac_node_api::EventRecord; + +pub(crate) fn main() { + // Setup logging + env_logger::init(); + + let yml = load_yaml!("cli.yml"); + let matches = App::from_yaml(yml).get_matches(); + + let config = Config::from(&matches); + + GlobalTokioHandle::initialize(); + + // log this information, don't println because some python scripts for GA rely on the + // stdout from the service + #[cfg(feature = "production")] + info!("*** Starting service in SGX production mode"); + #[cfg(not(feature = "production"))] + info!("*** Starting service in SGX debug mode"); + + info!("*** Running worker in mode: {:?} \n", WorkerModeProvider::worker_mode()); + + let clean_reset = matches.is_present("clean-reset"); + if clean_reset { + crate::setup::purge_files_from_dir(config.data_dir()).unwrap(); + } + + // build the entire dependency tree + let tokio_handle = Arc::new(GlobalTokioHandle {}); + let sidechain_blockstorage = Arc::new( + SidechainStorageLock::::from_base_path( + config.data_dir().to_path_buf(), + ) + .unwrap(), + ); + let node_api_factory = + Arc::new(NodeApiFactory::new(config.litentry_rpc_endpoint(), AccountKeyring::Alice.pair())); + let enclave = Arc::new(enclave_init(&config).unwrap()); + let initialization_handler = Arc::new(InitializationHandler::default()); + let worker = Arc::new(EnclaveWorker::new( + config.clone(), + enclave.clone(), + node_api_factory.clone(), + initialization_handler.clone(), + HashSet::new(), + )); + let sync_block_broadcaster = + Arc::new(SyncBlockBroadcaster::new(tokio_handle.clone(), worker.clone())); + let peer_updater = Arc::new(WorkerPeersRegistry::new(worker)); + let untrusted_peer_fetcher = UntrustedPeerFetcher::new(node_api_factory.clone()); + let peer_sidechain_block_fetcher = + Arc::new(BlockFetcher::::new(untrusted_peer_fetcher)); + let enclave_metrics_receiver = Arc::new(EnclaveMetricsReceiver {}); + + let maybe_target_a_parentchain_api_factory = config + .target_a_parentchain_rpc_endpoint() + .map(|url| Arc::new(NodeApiFactory::new(url, AccountKeyring::Alice.pair()))); + + let maybe_target_b_parentchain_api_factory = config + .target_b_parentchain_rpc_endpoint() + .map(|url| Arc::new(NodeApiFactory::new(url, AccountKeyring::Alice.pair()))); + + // initialize o-call bridge with a concrete factory implementation + OCallBridge::initialize(Arc::new(OCallBridgeComponentFactory::new( + node_api_factory.clone(), + maybe_target_a_parentchain_api_factory, + maybe_target_b_parentchain_api_factory, + sync_block_broadcaster, + enclave.clone(), + sidechain_blockstorage.clone(), + peer_updater, + peer_sidechain_block_fetcher, + tokio_handle.clone(), + enclave_metrics_receiver, + ))); + + #[cfg(feature = "dcap")] + let quoting_enclave_target_info = match enclave.qe_get_target_info() { + Ok(target_info) => Some(target_info), + Err(e) => { + warn!("Setting up DCAP - qe_get_target_info failed with error: {:?}, continuing.", e); + None + }, + }; + #[cfg(feature = "dcap")] + let quote_size = match enclave.qe_get_quote_size() { + Ok(size) => Some(size), + Err(e) => { + warn!("Setting up DCAP - qe_get_quote_size failed with error: {:?}, continuing.", e); + None + }, + }; + + #[cfg(not(feature = "dcap"))] + let quoting_enclave_target_info = None; + #[cfg(not(feature = "dcap"))] + let quote_size = None; + + if let Some(run_config) = config.run_config() { + let shard = extract_shard(run_config.shard(), enclave.as_ref()); + + println!("Worker Config: {:?}", config); + + if clean_reset { + setup::initialize_shard_and_keys(enclave.as_ref(), &shard).unwrap(); + } + + let node_api = + node_api_factory.create_api().expect("Failed to create parentchain node API"); + + if run_config.request_state() { + sync_state::sync_state::<_, _, WorkerModeProvider>( + &node_api, + &shard, + enclave.as_ref(), + run_config.skip_ra(), + ); + } + + start_worker::<_, _, _, _, WorkerModeProvider>( + config, + &shard, + enclave, + sidechain_blockstorage, + node_api, + tokio_handle, + initialization_handler, + quoting_enclave_target_info, + quote_size, + ); + } else if let Some(smatches) = matches.subcommand_matches("request-state") { + println!("*** Requesting state from a registered worker \n"); + let node_api = + node_api_factory.create_api().expect("Failed to create parentchain node API"); + sync_state::sync_state::<_, _, WorkerModeProvider>( + &node_api, + &extract_shard(smatches.value_of("shard"), enclave.as_ref()), + enclave.as_ref(), + smatches.is_present("skip-ra"), + ); + } else if matches.is_present("shielding-key") { + setup::generate_shielding_key_file(enclave.as_ref()); + } else if matches.is_present("signing-key") { + setup::generate_signing_key_file(enclave.as_ref()); + } else if matches.is_present("dump-ra") { + info!("*** Perform RA and dump cert to disk"); + #[cfg(not(feature = "dcap"))] + enclave.dump_ias_ra_cert_to_disk().unwrap(); + #[cfg(feature = "dcap")] + { + let skip_ra = false; + let dcap_quote = enclave.generate_dcap_ra_quote(skip_ra).unwrap(); + let (fmspc, _tcb_info) = extract_tcb_info_from_raw_dcap_quote(&dcap_quote).unwrap(); + enclave.dump_dcap_collateral_to_disk(fmspc).unwrap(); + enclave.dump_dcap_ra_cert_to_disk().unwrap(); + } + } else if matches.is_present("mrenclave") { + println!("{}", enclave.get_fingerprint().unwrap().encode().to_base58()); + } else if let Some(sub_matches) = matches.subcommand_matches("init-shard") { + setup::init_shard( + enclave.as_ref(), + &extract_shard(sub_matches.value_of("shard"), enclave.as_ref()), + ); + } else if let Some(sub_matches) = matches.subcommand_matches("test") { + if sub_matches.is_present("provisioning-server") { + println!("*** Running Enclave MU-RA TLS server\n"); + enclave_run_state_provisioning_server( + enclave.as_ref(), + sgx_quote_sign_type_t::SGX_UNLINKABLE_SIGNATURE, + quoting_enclave_target_info.as_ref(), + quote_size.as_ref(), + &config.mu_ra_url(), + sub_matches.is_present("skip-ra"), + ); + println!("[+] Done!"); + } else if sub_matches.is_present("provisioning-client") { + println!("*** Running Enclave MU-RA TLS client\n"); + let shard = extract_shard(sub_matches.value_of("shard"), enclave.as_ref()); + enclave_request_state_provisioning( + enclave.as_ref(), + sgx_quote_sign_type_t::SGX_UNLINKABLE_SIGNATURE, + &config.mu_ra_url_external(), + &shard, + sub_matches.is_present("skip-ra"), + ) + .unwrap(); + println!("[+] Done!"); + } else { + tests::run_enclave_tests(sub_matches); + } + } else if let Some(sub_matches) = matches.subcommand_matches("migrate-shard") { + // This subcommand `migrate-shard` is only used for manual testing. Maybe deleted later. + let old_shard = sub_matches + .value_of("old-shard") + .map(|value| { + let mut shard = [0u8; 32]; + hex::decode_to_slice(value, &mut shard) + .expect("shard must be hex encoded without 0x"); + ShardIdentifier::from_slice(&shard) + }) + .unwrap(); + + let new_shard: ShardIdentifier = sub_matches + .value_of("new-shard") + .map(|value| { + let mut shard = [0u8; 32]; + hex::decode_to_slice(value, &mut shard) + .expect("shard must be hex encoded without 0x"); + ShardIdentifier::from_slice(&shard) + }) + .unwrap(); + + if old_shard == new_shard { + println!("old_shard should not be the same as new_shard"); + } else { + setup::migrate_shard(enclave.as_ref(), &old_shard, &new_shard); + } + } else { + println!("For options: use --help"); + } +} + +/// FIXME: needs some discussion (restructuring?) +#[allow(clippy::too_many_arguments)] +fn start_worker( + config: Config, + shard: &ShardIdentifier, + enclave: Arc, + sidechain_storage: Arc, + litentry_rpc_api: ParentchainApi, + tokio_handle_getter: Arc, + initialization_handler: Arc, + quoting_enclave_target_info: Option, + quote_size: Option, +) where + T: GetTokioHandle, + E: EnclaveBase + + DirectRequest + + Sidechain + + RemoteAttestation + + TlsRemoteAttestation + + TeeracleApi + + Clone, + D: BlockPruner + FetchBlocks + Sync + Send + 'static, + InitializationHandler: TrackInitialization + IsInitialized + Sync + Send + 'static, + WorkerModeProvider: ProvideWorkerMode, +{ + let run_config = config.run_config().clone().expect("Run config missing"); + let skip_ra = run_config.skip_ra(); + + #[cfg(feature = "teeracle")] + let flavor_str = "teeracle"; + #[cfg(feature = "sidechain")] + let flavor_str = "sidechain"; + #[cfg(feature = "offchain-worker")] + let flavor_str = "offchain-worker"; + #[cfg(not(any(feature = "offchain-worker", feature = "sidechain", feature = "teeracle")))] + let flavor_str = "offchain-worker"; + + println!("Litentry Worker for {} v{}", flavor_str, VERSION); + + #[cfg(feature = "dcap")] + println!(" DCAP is enabled"); + #[cfg(not(feature = "dcap"))] + println!(" DCAP is disabled"); + #[cfg(feature = "production")] + println!(" Production Mode is enabled"); + #[cfg(not(feature = "production"))] + println!(" Production Mode is disabled"); + #[cfg(feature = "evm")] + println!(" EVM is enabled"); + #[cfg(not(feature = "evm"))] + println!(" EVM is disabled"); + + info!("starting worker on shard {}", shard.encode().to_base58()); + // ------------------------------------------------------------------------ + // check for required files + if !skip_ra { + #[cfg(not(feature = "dcap"))] + check_files(); + } + // ------------------------------------------------------------------------ + // initialize the enclave + let mrenclave = enclave.get_fingerprint().unwrap(); + println!("MRENCLAVE={}", mrenclave.0.to_base58()); + println!("MRENCLAVE in hex {:?}", hex::encode(mrenclave)); + + // ------------------------------------------------------------------------ + // let new workers call us for key provisioning + println!("MU-RA server listening on {}", config.mu_ra_url()); + let is_development_mode = run_config.dev(); + let ra_url = config.mu_ra_url(); + let enclave_api_key_prov = enclave.clone(); + thread::spawn(move || { + enclave_run_state_provisioning_server( + enclave_api_key_prov.as_ref(), + sgx_quote_sign_type_t::SGX_UNLINKABLE_SIGNATURE, + quoting_enclave_target_info.as_ref(), + quote_size.as_ref(), + &ra_url, + skip_ra, + ); + info!("State provisioning server stopped."); + }); + + let tokio_handle = tokio_handle_getter.get_handle(); + + // ------------------------------------------------------------------------ + // Get the public key of our TEE. + let tee_accountid = enclave_account(enclave.as_ref()); + println!("Enclave account {:} ", &tee_accountid.to_ss58check()); + + // ------------------------------------------------------------------------ + // Start `is_initialized` server. + let untrusted_http_server_port = config + .try_parse_untrusted_http_server_port() + .expect("untrusted http server port to be a valid port number"); + let initialization_handler_clone = initialization_handler.clone(); + tokio_handle.spawn(async move { + if let Err(e) = + start_is_initialized_server(initialization_handler_clone, untrusted_http_server_port) + .await + { + error!("Unexpected error in `is_initialized` server: {:?}", e); + } + }); + + // ------------------------------------------------------------------------ + // Start prometheus metrics server. + if config.enable_metrics_server() { + let enclave_wallet = Arc::new(EnclaveAccountInfoProvider::new( + litentry_rpc_api.clone(), + tee_accountid.clone(), + )); + let metrics_handler = Arc::new(MetricsHandler::new(enclave_wallet)); + let metrics_server_port = config + .try_parse_metrics_server_port() + .expect("metrics server port to be a valid port number"); + tokio_handle.spawn(async move { + if let Err(e) = start_metrics_server(metrics_handler, metrics_server_port).await { + error!("Unexpected error in Prometheus metrics server: {:?}", e); + } + }); + } + + // ------------------------------------------------------------------------ + // Start trusted worker rpc server + if WorkerModeProvider::worker_mode() == WorkerMode::Sidechain + || WorkerModeProvider::worker_mode() == WorkerMode::OffChainWorker + { + let direct_invocation_server_addr = config.trusted_worker_url_internal(); + let enclave_for_direct_invocation = enclave.clone(); + thread::spawn(move || { + println!( + "[+] Trusted RPC direct invocation server listening on {}", + direct_invocation_server_addr + ); + enclave_for_direct_invocation + .init_direct_invocation_server(direct_invocation_server_addr) + .unwrap(); + println!("[+] RPC direct invocation server shut down"); + }); + } + + // ------------------------------------------------------------------------ + // Start untrusted worker rpc server. + // i.e move sidechain block importing to trusted worker. + if WorkerModeProvider::worker_mode() == WorkerMode::Sidechain { + sidechain_start_untrusted_rpc_server( + &config, + enclave.clone(), + sidechain_storage.clone(), + &tokio_handle, + ); + } + + // ------------------------------------------------------------------------ + // Init parentchain specific stuff. Needed for parentchain communication. + + let (parentchain_handler, last_synced_header) = + init_parentchain(&enclave, &litentry_rpc_api, &tee_accountid, ParentchainId::Litentry); + + #[cfg(feature = "dcap")] + register_collateral(&litentry_rpc_api, &*enclave, &tee_accountid, is_development_mode, skip_ra); + + let trusted_url = config.trusted_worker_url_external(); + + #[cfg(feature = "attesteer")] + fetch_marblerun_events_every_hour( + litentry_rpc_api.clone(), + enclave.clone(), + tee_accountid.clone(), + is_development_mode, + trusted_url.clone(), + run_config.marblerun_base_url().to_string(), + ); + + // ------------------------------------------------------------------------ + // Perform a remote attestation and get an unchecked extrinsic back. + + if skip_ra { + println!( + "[!] skipping remote attestation. Registering enclave without attestation report." + ); + } else { + println!("[!] creating remote attestation report and create enclave register extrinsic."); + }; + + #[cfg(feature = "dcap")] + enclave.set_sgx_qpl_logging().expect("QPL logging setup failed"); + + let enclave2 = enclave.clone(); + let node_api2 = litentry_rpc_api.clone(); + let tee_accountid2 = tee_accountid.clone(); + let trusted_url2 = trusted_url.clone(); + + #[cfg(not(feature = "dcap"))] + let register_xt = move || enclave2.generate_ias_ra_extrinsic(&trusted_url2, skip_ra).unwrap(); + #[cfg(feature = "dcap")] + let register_xt = move || enclave2.generate_dcap_ra_extrinsic(&trusted_url2, skip_ra).unwrap(); + + let mut register_enclave_xt_header: Option
= None; + let mut we_are_primary_validateer: bool = false; + + let send_register_xt = move || { + println!("[+] Send register enclave extrinsic"); + send_extrinsic(register_xt(), &node_api2, &tee_accountid2, is_development_mode) + }; + + // litentry: check if the enclave is already registered + // TODO: revisit the registration process (P-10) + match litentry_rpc_api.get_keys(storage_key("Teerex", "EnclaveRegistry"), None) { + Ok(Some(keys)) => { + let trusted_url = trusted_url.as_bytes().to_vec(); + let mrenclave = mrenclave.0.to_vec(); + let mut found = false; + for key in keys { + let key = if key.starts_with("0x") { + let bytes = &key.as_bytes()[b"0x".len()..]; + hex::decode(bytes).unwrap() + } else { + hex::decode(key.as_bytes()).unwrap() + }; + match litentry_rpc_api.get_storage_by_key::>>( + StorageKey(key.clone()), + None, + ) { + Ok(Some(value)) => { + if value.mr_enclave.to_vec() == mrenclave && value.url == trusted_url { + // After calling the perform_ra function, the nonce will be incremented by 1, + // so enclave is already registered, we should reset the nonce_cache + let nonce = + litentry_rpc_api.get_account_next_index(&tee_accountid).unwrap(); + enclave + .set_nonce(nonce, ParentchainId::Litentry) + .expect("Could not set nonce of enclave. Returning here..."); + found = true; + info!("fond enclave: {:?}", value); + break + } + }, + Ok(None) => { + warn!("not found from key: {:?}", key); + }, + Err(_) => {}, + } + } + if !found { + // Todo: Can't unwrap here because the extrinsic is for some reason not found in the block + // even if it was successful: https://github.com/scs/substrate-api-client/issues/624. + let register_enclave_block_hash = send_register_xt(); + let api_register_enclave_xt_header = + litentry_rpc_api.get_header(register_enclave_block_hash).unwrap().unwrap(); + + // TODO: #1451: Fix api-client type hacks + // TODO(Litentry): keep an eye on it - it's a hacky way to convert `SubstrateHeader` to `Header` + let header = + Header::decode(&mut api_register_enclave_xt_header.encode().as_slice()) + .expect("Can decode previously encoded header; qed"); + + println!( + "[+] Enclave registered at block number: {:?}, hash: {:?}", + header.number(), + header.hash() + ); + + register_enclave_xt_header = Some(header); + } + }, + _ => panic!("unknown error"), + } + + if let Some(register_enclave_xt_header) = register_enclave_xt_header.clone() { + we_are_primary_validateer = + we_are_primary_worker(&litentry_rpc_api, ®ister_enclave_xt_header).unwrap(); + } + + if we_are_primary_validateer { + println!("[+] We are the primary worker"); + } else { + println!("[+] We are NOT the primary worker"); + } + + initialization_handler.registered_on_parentchain(); + + match WorkerModeProvider::worker_mode() { + WorkerMode::Teeracle => { + // ------------------------------------------------------------------------ + // initialize teeracle interval + #[cfg(feature = "teeracle")] + schedule_periodic_reregistration_thread( + send_register_xt, + run_config.reregister_teeracle_interval(), + ); + + #[cfg(feature = "teeracle")] + start_periodic_market_update( + &litentry_rpc_api, + run_config.teeracle_update_interval(), + enclave.as_ref(), + &tokio_handle, + ); + }, + WorkerMode::OffChainWorker => { + println!("*** [+] Finished initializing light client, syncing parentchain..."); + + // Syncing all parentchain blocks, this might take a while.. + let last_synced_header = + parentchain_handler.sync_parentchain(last_synced_header, 0, true).unwrap(); + + start_parentchain_header_subscription_thread(parentchain_handler, last_synced_header); + + info!("skipping shard vault check because not yet supported for offchain worker"); + }, + WorkerMode::Sidechain => { + println!("*** [+] Finished initializing light client, syncing parentchain..."); + + // Litentry: apply skipped parentchain block + let parentchain_start_block = config + .try_parse_parentchain_start_block() + .expect("parentchain start block to be a valid number"); + + println!( + "*** [+] last_synced_header: {}, config.parentchain_start_block: {}", + last_synced_header.number, parentchain_start_block + ); + + // ------------------------------------------------------------------------ + // Initialize the sidechain + let last_synced_header = sidechain_init_block_production( + enclave.clone(), + register_enclave_xt_header, + we_are_primary_validateer, + parentchain_handler.clone(), + sidechain_storage, + &last_synced_header, + parentchain_start_block, + config.clone().fail_slot_mode, + config.fail_at, + ) + .unwrap(); + + start_parentchain_header_subscription_thread(parentchain_handler, last_synced_header); + + init_provided_shard_vault(shard, &enclave, we_are_primary_validateer); + + spawn_worker_for_shard_polling(shard, litentry_rpc_api.clone(), initialization_handler); + }, + } + + if let Some(url) = config.target_a_parentchain_rpc_endpoint() { + init_target_parentchain( + &enclave, + &tee_accountid, + url, + shard, + ParentchainId::TargetA, + is_development_mode, + ) + } + + if let Some(url) = config.target_b_parentchain_rpc_endpoint() { + init_target_parentchain( + &enclave, + &tee_accountid, + url, + shard, + ParentchainId::TargetB, + is_development_mode, + ) + } + + // ------------------------------------------------------------------------ + // Subscribe to events and print them. + println!("*** [{:?}] Subscribing to events", ParentchainId::Litentry); + let mut subscription = litentry_rpc_api.subscribe_events().unwrap(); + println!("[+] [{:?}] Subscribed to events. waiting...", ParentchainId::Litentry); + loop { + if let Some(Ok(events)) = subscription.next_events::() { + print_events(events, ParentchainId::Litentry) + } + } +} + +fn init_provided_shard_vault( + shard: &ShardIdentifier, + enclave: &Arc, + we_are_primary_validateer: bool, +) { + if let Ok(shard_vault) = enclave.get_ecc_vault_pubkey(shard) { + println!( + "[Litentry] shard vault account is already initialized in state: {}", + shard_vault.to_ss58check() + ); + } else if we_are_primary_validateer { + println!("[Litentry] initializing proxied shard vault account now"); + enclave.init_proxied_shard_vault(shard, &ParentchainId::Litentry).unwrap(); + println!( + "[Litentry] initialized shard vault account: : {}", + enclave.get_ecc_vault_pubkey(shard).unwrap().to_ss58check() + ); + } else { + panic!( + "[Litentry] no vault account has been initialized and we are not the primary worker" + ); + } +} + +fn init_target_parentchain( + enclave: &Arc, + tee_account_id: &AccountId32, + url: String, + shard: &ShardIdentifier, + parentchain_id: ParentchainId, + is_development_mode: bool, +) where + E: EnclaveBase + Sidechain, +{ + println!("Initializing parentchain {:?} with url: {}", parentchain_id, url); + let node_api = NodeApiFactory::new(url, AccountKeyring::Alice.pair()) + .create_api() + .unwrap_or_else(|_| panic!("[{:?}] Failed to create parentchain node API", parentchain_id)); + + // some random bytes not too small to ensure that the enclave has enough funds + setup_account_funding(&node_api, tee_account_id, [0u8; 100].into(), is_development_mode) + .unwrap_or_else(|_| { + panic!("[{:?}] Could not fund parentchain enclave account", parentchain_id) + }); + + let (parentchain_handler, last_synched_header) = + init_parentchain(enclave, &node_api, tee_account_id, parentchain_id); + + if WorkerModeProvider::worker_mode() != WorkerMode::Teeracle { + println!( + "*** [+] [{:?}] Finished initializing light client, syncing parentchain...", + parentchain_id + ); + + // Syncing all parentchain blocks, this might take a while.. + let last_synched_header = + parentchain_handler.sync_parentchain(last_synched_header, 0, true).unwrap(); + + start_parentchain_header_subscription_thread(parentchain_handler, last_synched_header) + } + println!("[{:?}] initializing proxied shard vault account now", parentchain_id); + enclave.init_proxied_shard_vault(shard, &parentchain_id).unwrap(); + + // Subscribe to events and print them. + println!("*** [{:?}] Subscribing to events...", parentchain_id); + let mut subscription = node_api.subscribe_events().unwrap(); + println!("[+] [{:?}] Subscribed to events. waiting...", parentchain_id); + + thread::Builder::new() + .name(format!("{:?}_parentchain_event_subscription", parentchain_id)) + .spawn(move || loop { + if let Some(Ok(events)) = subscription.next_events::() { + print_events(events, parentchain_id) + } + }) + .unwrap(); +} + +fn init_parentchain( + enclave: &Arc, + node_api: &ParentchainApi, + tee_account_id: &AccountId32, + parentchain_id: ParentchainId, +) -> (Arc>, Header) +where + E: EnclaveBase + Sidechain, +{ + let parentchain_handler = Arc::new( + ParentchainHandler::new_with_automatic_light_client_allocation( + node_api.clone(), + enclave.clone(), + parentchain_id, + ) + .unwrap(), + ); + let last_synced_header = parentchain_handler.init_parentchain_components().unwrap(); + println!("[{:?}] last synced parentchain block: {}", parentchain_id, last_synced_header.number); + + let nonce = node_api.get_nonce_of(tee_account_id).unwrap(); + info!("[{:?}] Enclave nonce = {:?}", parentchain_id, nonce); + enclave.set_nonce(nonce, parentchain_id).unwrap_or_else(|_| { + panic!("[{:?}] Could not set nonce of enclave. Returning here...", parentchain_id) + }); + + let metadata = node_api.metadata().clone(); + let runtime_spec_version = node_api.runtime_version().spec_version; + let runtime_transaction_version = node_api.runtime_version().transaction_version; + enclave + .set_node_metadata( + NodeMetadata::new(metadata, runtime_spec_version, runtime_transaction_version).encode(), + parentchain_id, + ) + .unwrap_or_else(|_| { + panic!("[{:?}] Could not set the node metadata in the enclave", parentchain_id) + }); + + (parentchain_handler, last_synced_header) +} + +/// Start polling loop to wait until we have a worker for a shard registered on +/// the parentchain (TEEREX WorkerForShard). This is the pre-requisite to be +/// considered initialized and ready for the next worker to start (in sidechain mode only). +/// considered initialized and ready for the next worker to start. +fn spawn_worker_for_shard_polling( + shard: &ShardIdentifier, + node_api: ParentchainApi, + initialization_handler: Arc, +) where + InitializationHandler: TrackInitialization + Sync + Send + 'static, +{ + let shard_for_initialized = *shard; + thread::spawn(move || { + const POLL_INTERVAL_SECS: u64 = 2; + + loop { + info!("Polling for worker for shard ({} seconds interval)", POLL_INTERVAL_SECS); + if let Ok(Some(_enclave)) = node_api.worker_for_shard(&shard_for_initialized, None) { + // Set that the service is initialized. + initialization_handler.worker_for_shard_registered(); + println!("[+] Found `WorkerForShard` on parentchain state",); + break + } + thread::sleep(Duration::from_secs(POLL_INTERVAL_SECS)); + } + }); +} + +fn print_events(events: Vec>, parentchain_id: ParentchainId) +where + R: Debug, +{ + for evr in &events { + if evr.phase == ApplyExtrinsic(0) { + // not interested in intrinsics + continue + } + let re = Regex::new(r"\s[0-9a-f]*\s\(").unwrap(); + let event_str = re + .replace_all(format!("{:?}", evr.event).as_str(), "(") + .replace("RuntimeEvent::", "") + .replace("Event::", ""); + println!("[{}] Event: {}", parentchain_id, event_str); + } +} + +#[cfg(feature = "attesteer")] +fn fetch_marblerun_events_every_hour( + api: ParentchainApi, + enclave: Arc, + accountid: AccountId32, + is_development_mode: bool, + url: String, + marblerun_base_url: String, +) where + E: RemoteAttestation + Clone + Sync + Send + 'static, +{ + let enclave = enclave.clone(); + let handle = thread::spawn(move || { + const POLL_INTERVAL_5_MINUTES_IN_SECS: u64 = 5 * 60; + loop { + info!("Polling marblerun events for quotes to register"); + register_quotes_from_marblerun( + &api, + enclave.clone(), + &accountid, + is_development_mode, + url.clone(), + &marblerun_base_url, + ); + + thread::sleep(Duration::from_secs(POLL_INTERVAL_5_MINUTES_IN_SECS)); + } + }); + + handle.join().unwrap() +} +#[cfg(feature = "attesteer")] +fn register_quotes_from_marblerun( + api: &ParentchainApi, + enclave: Arc, + accountid: &AccountId32, + is_development_mode: bool, + url: String, + marblerun_base_url: &str, +) { + let enclave = enclave.as_ref(); + let events = crate::prometheus_metrics::fetch_marblerun_events(marblerun_base_url) + .map_err(|e| { + info!("Fetching events from Marblerun failed with: {:?}, continuing with 0 events.", e); + }) + .unwrap_or_default(); + let quotes: Vec<&[u8]> = + events.iter().map(|event| event.get_quote_without_prepended_bytes()).collect(); + + for quote in quotes { + match enclave.generate_dcap_ra_extrinsic_from_quote(url.clone(), "e) { + Ok(xt) => { + send_extrinsic(xt, api, accountid, is_development_mode); + }, + Err(e) => { + error!("Extracting information from quote failed: {}", e) + }, + } + } +} +#[cfg(feature = "dcap")] +fn register_collateral( + api: &ParentchainApi, + enclave: &dyn RemoteAttestation, + accountid: &AccountId32, + is_development_mode: bool, + skip_ra: bool, +) { + //TODO generate_dcap_ra_quote() does not really need skip_ra, rethink how many layers skip_ra should be passed along + if !skip_ra { + let dcap_quote = enclave.generate_dcap_ra_quote(skip_ra).unwrap(); + let (fmspc, _tcb_info) = extract_tcb_info_from_raw_dcap_quote(&dcap_quote).unwrap(); + println!("[>] DCAP setup: register QE collateral"); + let uxt = enclave.generate_register_quoting_enclave_extrinsic(fmspc).unwrap(); + send_extrinsic(uxt, api, accountid, is_development_mode); + + println!("[>] DCAP setup: register TCB info"); + let uxt = enclave.generate_register_tcb_info_extrinsic(fmspc).unwrap(); + send_extrinsic(uxt, api, accountid, is_development_mode); + } +} + +fn send_extrinsic( + extrinsic: Vec, + api: &ParentchainApi, + fee_payer: &AccountId32, + is_development_mode: bool, +) -> Option { + // ensure account funds + if let Err(x) = setup_account_funding(api, fee_payer, extrinsic.clone(), is_development_mode) { + error!("Ensure enclave funding failed: {:?}", x); + // Return without registering the enclave. This will fail and the transaction will be banned for 30min. + return None + } + + info!("[>] send extrinsic"); + trace!( + " encoded extrinsic len: {}, payload: 0x{:}", + extrinsic.len(), + hex::encode(extrinsic.clone()) + ); + + // fixme: wait ...until_success doesn't work due to https://github.com/scs/substrate-api-client/issues/624 + // fixme: currently, we don't verify if the extrinsic was a success here + match api.submit_and_watch_opaque_extrinsic_until(&extrinsic.into(), XtStatus::Finalized) { + Ok(xt_report) => { + info!( + "[+] L1 extrinsic success. extrinsic hash: {:?} / status: {:?}", + xt_report.extrinsic_hash, xt_report.status + ); + xt_report.block_hash + }, + Err(e) => { + error!("ExtrinsicFailed {:?}", e); + None + }, + } +} + +fn start_parentchain_header_subscription_thread( + parentchain_handler: Arc>, + last_synced_header: Header, +) { + let parentchain_id = *parentchain_handler.parentchain_id(); + thread::Builder::new() + .name(format!("{:?}_parentchain_sync_loop", parentchain_id)) + .spawn(move || { + if let Err(e) = + subscribe_to_parentchain_new_headers(parentchain_handler, last_synced_header) + { + error!( + "[{:?}] parentchain block syncing terminated with a failure: {:?}", + parentchain_id, e + ); + } + println!("[!] [{:?}] parentchain block syncing has terminated", parentchain_id); + }) + .unwrap(); +} + +/// Subscribe to the node API finalized heads stream and trigger a parent chain sync +/// upon receiving a new header. +fn subscribe_to_parentchain_new_headers( + parentchain_handler: Arc>, + mut last_synced_header: Header, +) -> Result<(), Error> { + // TODO: this should be implemented by parentchain_handler directly, and not via + // exposed parentchain_api + let mut subscription = parentchain_handler + .parentchain_api() + .subscribe_finalized_heads() + .map_err(Error::ApiClient)?; + + // TODO(Kai@Litentry): + // originally we had an outer loop to try to handle the disconnection, + // see https://github.com/litentry/litentry-parachain/commit/b8059d0fad928e4bba99178451cd0d473791c437 + // but I reverted it because: + // - no graceful shutdown, we could have many mpsc channel when it doesn't go right + // - we might have multiple `sync_parentchain` running concurrently, which causes chaos in enclave side + // - I still feel it's only a workaround, not a perfect solution + // + // TODO: now the sync will panic if disconnected - it heavily relys on the worker-restart to work (even manually) + let parentchain_id = parentchain_handler.parentchain_id(); + loop { + let new_header = subscription + .next() + .ok_or(Error::ApiSubscriptionDisconnected)? + .map_err(|e| Error::ApiClient(e.into()))?; + + info!( + "[{:?}] Received finalized header update ({}), syncing parent chain...", + parentchain_id, new_header.number + ); + + last_synced_header = parentchain_handler.sync_parentchain(last_synced_header, 0, false)?; + } +} + +/// Get the public signing key of the TEE. +pub fn enclave_account(enclave_api: &E) -> AccountId32 { + let tee_public = enclave_api.get_ecc_signing_pubkey().unwrap(); + trace!("[+] Got ed25519 account of TEE = {}", tee_public.to_ss58check()); + AccountId32::from(*tee_public.as_array_ref()) +} + +/// Checks if we are the first validateer to register on the parentchain. +fn we_are_primary_worker( + node_api: &ParentchainApi, + register_enclave_xt_header: &Header, +) -> Result { + let enclave_count_of_previous_block = + node_api.enclave_count(Some(*register_enclave_xt_header.parent_hash()))?; + Ok(enclave_count_of_previous_block == 0) +} diff --git a/bitacross-worker/service/src/ocall_bridge/bridge_api.rs b/bitacross-worker/service/src/ocall_bridge/bridge_api.rs new file mode 100644 index 0000000000..71899760c1 --- /dev/null +++ b/bitacross-worker/service/src/ocall_bridge/bridge_api.rs @@ -0,0 +1,264 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use itp_enclave_api::remote_attestation::QveReport; +use lazy_static::lazy_static; +use log::*; +use parking_lot::RwLock; +use sgx_types::*; +use std::{sync::Arc, vec::Vec}; + +#[cfg(test)] +use mockall::predicate::*; +#[cfg(test)] +use mockall::*; + +lazy_static! { + /// global state for the component factory + /// access is always routed through 'Bridge', do not use directly! + static ref COMPONENT_FACTORY: RwLock>> = + RwLock::new(None); +} + +/// The Bridge is the static/global interface to inject concrete implementations +/// (or rather the factories for them) - this is done at startup of the worker. +/// On the other side, it is used by the o-call FFI to retrieve the state and forward calls +/// to their respective implementation. +pub struct Bridge; + +impl Bridge { + pub fn get_ra_api() -> Arc { + debug!("Requesting RemoteAttestation OCall API instance"); + + COMPONENT_FACTORY + .read() + .as_ref() + .expect("Component factory has not been set. Use `initialize()`") + .get_ra_api() + } + + pub fn get_sidechain_api() -> Arc { + COMPONENT_FACTORY + .read() + .as_ref() + .expect("Component factory has not been set. Use `initialize()`") + .get_sidechain_api() + } + + pub fn get_oc_api() -> Arc { + debug!("Requesting WorkerOnChain OCall API instance"); + + COMPONENT_FACTORY + .read() + .as_ref() + .expect("Component factory has not been set. Use `initialize()`") + .get_oc_api() + } + + pub fn get_ipfs_api() -> Arc { + debug!("Requesting IPFS OCall API instance"); + + COMPONENT_FACTORY + .read() + .as_ref() + .expect("Component factory has not been set. Use `initialize()`") + .get_ipfs_api() + } + + pub fn get_metrics_api() -> Arc { + COMPONENT_FACTORY + .read() + .as_ref() + .expect("Component factory has not been set. Use `initialize()`") + .get_metrics_api() + } + + pub fn initialize(component_factory: Arc) { + debug!("Initializing OCall bridge with component factory"); + + *COMPONENT_FACTORY.write() = Some(component_factory); + } +} + +/// Factory trait (abstract factory) that creates instances +/// of all the components of the OCall Bridge +pub trait GetOCallBridgeComponents { + /// remote attestation OCall API + fn get_ra_api(&self) -> Arc; + + /// side chain OCall API + fn get_sidechain_api(&self) -> Arc; + + /// on chain (parentchain) OCall API + fn get_oc_api(&self) -> Arc; + + /// ipfs OCall API + fn get_ipfs_api(&self) -> Arc; + + /// Metrics OCall API. + fn get_metrics_api(&self) -> Arc; +} + +/// OCall bridge errors +#[derive(Debug, thiserror::Error)] +pub enum OCallBridgeError { + #[error("GetQuote Error: {0}")] + GetQuote(sgx_status_t), + #[error("InitQuote Error: {0}")] + InitQuote(sgx_status_t), + #[error("GetUpdateInfo Error: {0}")] + GetUpdateInfo(sgx_status_t), + #[error("GetIasSocket Error: {0}")] + GetIasSocket(String), + #[error("UpdateMetric Error: {0}")] + UpdateMetric(String), + #[error("Propose sidechain block failed: {0}")] + ProposeSidechainBlock(String), + #[error("Failed to fetch sidechain blocks from peer: {0}")] + FetchSidechainBlocksFromPeer(String), + #[error("Sending extrinsics to parentchain failed: {0}")] + SendExtrinsicsToParentchain(String), + #[error("IPFS Error: {0}")] + IpfsError(String), + #[error("DirectInvocation Error: {0}")] + DirectInvocationError(String), + #[error(transparent)] + Codec(#[from] codec::Error), + #[error("Node API factory error: {0}")] + NodeApiFactory(#[from] itp_node_api::node_api_factory::NodeApiFactoryError), + #[error("Target A parentchain not initialized")] + TargetAParentchainNotInitialized, + #[error("Target B parentchain not initialized")] + TargetBParentchainNotInitialized, +} + +impl From for sgx_status_t { + fn from(o: OCallBridgeError) -> sgx_status_t { + match o { + OCallBridgeError::GetQuote(s) => s, + OCallBridgeError::InitQuote(s) => s, + OCallBridgeError::GetUpdateInfo(s) => s, + _ => sgx_status_t::SGX_ERROR_UNEXPECTED, + } + } +} + +pub type OCallBridgeResult = Result; + +/// Trait for all the OCalls related to remote attestation +#[cfg_attr(test, automock)] +pub trait RemoteAttestationBridge { + /// initialize the quote + fn init_quote(&self) -> OCallBridgeResult<(sgx_target_info_t, sgx_epid_group_id_t)>; + + /// get the intel attestation service socket + fn get_ias_socket(&self) -> OCallBridgeResult; + + /// retrieve the quote from intel + fn get_quote( + &self, + revocation_list: Vec, + report: sgx_report_t, + quote_type: sgx_quote_sign_type_t, + spid: sgx_spid_t, + quote_nonce: sgx_quote_nonce_t, + ) -> OCallBridgeResult<(sgx_report_t, Vec)>; + + /// retrieve the quote from dcap server + fn get_dcap_quote(&self, report: sgx_report_t, quote_size: u32) -> OCallBridgeResult>; + + // Retrieve verification of quote + fn get_qve_report_on_quote( + &self, + quote: Vec, + current_time: i64, + quote_collateral: &sgx_ql_qve_collateral_t, + qve_report_info: sgx_ql_qe_report_info_t, + supplemental_data_size: u32, + ) -> OCallBridgeResult; + + /// -- + fn get_update_info( + &self, + platform_blob: sgx_platform_info_t, + enclave_trusted: i32, + ) -> OCallBridgeResult; +} + +/// Trait for all the OCalls related to parentchain operations +#[cfg_attr(test, automock)] +pub trait WorkerOnChainBridge { + fn worker_request( + &self, + request: Vec, + parentchain_id: Vec, + ) -> OCallBridgeResult>; + + fn send_to_parentchain( + &self, + extrinsics_encoded: Vec, + parentchain_id: Vec, + await_each_inclusion: bool, + ) -> OCallBridgeResult<()>; +} + +/// Trait for updating metrics from inside the enclave. +#[cfg_attr(test, automock)] +pub trait MetricsBridge { + fn update_metric(&self, metric_encoded: Vec) -> OCallBridgeResult<()>; +} + +/// Trait for all the OCalls related to sidechain operations +#[cfg_attr(test, automock)] +pub trait SidechainBridge { + fn propose_sidechain_blocks(&self, signed_blocks_encoded: Vec) -> OCallBridgeResult<()>; + + fn store_sidechain_blocks(&self, signed_blocks_encoded: Vec) -> OCallBridgeResult<()>; + + fn fetch_sidechain_blocks_from_peer( + &self, + last_imported_block_hash_encoded: Vec, + maybe_until_block_hash_encoded: Vec, + shard_identifier_encoded: Vec, + ) -> OCallBridgeResult>; + + fn get_trusted_peers_urls(&self) -> OCallBridgeResult>; +} + +/// type for IPFS +pub type Cid = [u8; 46]; + +/// Trait for all the OCalls related to IPFS +#[cfg_attr(test, automock)] +pub trait IpfsBridge { + fn write_to_ipfs(&self, data: &'static [u8]) -> OCallBridgeResult; + + fn read_from_ipfs(&self, cid: Cid) -> OCallBridgeResult<()>; +} + +/// Trait for the direct invocation OCalls +#[cfg_attr(test, automock)] +pub trait DirectInvocationBridge { + fn update_status_event( + &self, + hash_vec: Vec, + status_update_vec: Vec, + ) -> OCallBridgeResult<()>; + + fn send_status(&self, hash_vec: Vec, status_vec: Vec) -> OCallBridgeResult<()>; +} diff --git a/bitacross-worker/service/src/ocall_bridge/component_factory.rs b/bitacross-worker/service/src/ocall_bridge/component_factory.rs new file mode 100644 index 0000000000..e23c509101 --- /dev/null +++ b/bitacross-worker/service/src/ocall_bridge/component_factory.rs @@ -0,0 +1,176 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + globals::tokio_handle::GetTokioHandle, + ocall_bridge::{ + bridge_api::{ + GetOCallBridgeComponents, IpfsBridge, MetricsBridge, RemoteAttestationBridge, + SidechainBridge, WorkerOnChainBridge, + }, + ipfs_ocall::IpfsOCall, + metrics_ocall::MetricsOCall, + remote_attestation_ocall::RemoteAttestationOCall, + sidechain_ocall::SidechainOCall, + worker_on_chain_ocall::WorkerOnChainOCall, + }, + prometheus_metrics::ReceiveEnclaveMetrics, + sync_block_broadcaster::BroadcastBlocks, + worker_peers_registry::PeersRegistry, +}; +use itp_enclave_api::{enclave_base::EnclaveBase, remote_attestation::RemoteAttestationCallBacks}; +use itp_node_api::node_api_factory::CreateNodeApi; +use its_peer_fetch::FetchBlocksFromPeer; +use its_primitives::types::block::SignedBlock as SignedSidechainBlock; +use its_storage::BlockStorage; +use std::sync::Arc; + +/// Concrete implementation, should be moved out of the OCall Bridge, into the worker +/// since the OCall bridge itself should not know any concrete types to ensure +/// our dependency graph is worker -> ocall bridge +pub struct OCallBridgeComponentFactory< + NodeApi, + Broadcaster, + EnclaveApi, + Storage, + WorkerPeersRegistry, + PeerBlockFetcher, + TokioHandle, + MetricsReceiver, +> { + integritee_rpc_api_factory: Arc, + target_a_parentchain_rpc_api_factory: Option>, + target_b_parentchain_rpc_api_factory: Option>, + block_broadcaster: Arc, + enclave_api: Arc, + block_storage: Arc, + peers_registry: Arc, + peer_block_fetcher: Arc, + tokio_handle: Arc, + metrics_receiver: Arc, +} + +impl< + NodeApi, + Broadcaster, + EnclaveApi, + Storage, + WorkerPeersRegistry, + PeerBlockFetcher, + TokioHandle, + MetricsReceiver, + > + OCallBridgeComponentFactory< + NodeApi, + Broadcaster, + EnclaveApi, + Storage, + WorkerPeersRegistry, + PeerBlockFetcher, + TokioHandle, + MetricsReceiver, + > +{ + #[allow(clippy::too_many_arguments)] + pub fn new( + integritee_rpc_api_factory: Arc, + target_a_parentchain_rpc_api_factory: Option>, + target_b_parentchain_rpc_api_factory: Option>, + block_broadcaster: Arc, + enclave_api: Arc, + block_storage: Arc, + peers_registry: Arc, + peer_block_fetcher: Arc, + tokio_handle: Arc, + metrics_receiver: Arc, + ) -> Self { + OCallBridgeComponentFactory { + integritee_rpc_api_factory, + target_a_parentchain_rpc_api_factory, + target_b_parentchain_rpc_api_factory, + block_broadcaster, + enclave_api, + block_storage, + peers_registry, + peer_block_fetcher, + tokio_handle, + metrics_receiver, + } + } +} + +impl< + NodeApi, + Broadcaster, + EnclaveApi, + Storage, + WorkerPeersRegistry, + PeerBlockFetcher, + TokioHandle, + MetricsReceiver, + > GetOCallBridgeComponents + for OCallBridgeComponentFactory< + NodeApi, + Broadcaster, + EnclaveApi, + Storage, + WorkerPeersRegistry, + PeerBlockFetcher, + TokioHandle, + MetricsReceiver, + > where + NodeApi: CreateNodeApi + 'static, + Broadcaster: BroadcastBlocks + 'static, + EnclaveApi: EnclaveBase + RemoteAttestationCallBacks + 'static, + Storage: BlockStorage + 'static, + WorkerPeersRegistry: PeersRegistry + 'static, + PeerBlockFetcher: FetchBlocksFromPeer + 'static, + TokioHandle: GetTokioHandle + 'static, + MetricsReceiver: ReceiveEnclaveMetrics + 'static, +{ + fn get_ra_api(&self) -> Arc { + Arc::new(RemoteAttestationOCall::new(self.enclave_api.clone())) + } + + fn get_sidechain_api(&self) -> Arc { + Arc::new(SidechainOCall::new( + self.block_broadcaster.clone(), + self.block_storage.clone(), + self.peers_registry.clone(), + self.peer_block_fetcher.clone(), + self.tokio_handle.clone(), + )) + } + + fn get_oc_api(&self) -> Arc { + Arc::new(WorkerOnChainOCall::new( + self.enclave_api.clone(), + self.integritee_rpc_api_factory.clone(), + self.target_a_parentchain_rpc_api_factory.clone(), + self.target_b_parentchain_rpc_api_factory.clone(), + )) + } + + fn get_ipfs_api(&self) -> Arc { + Arc::new(IpfsOCall {}) + } + + fn get_metrics_api(&self) -> Arc { + Arc::new(MetricsOCall::new(self.metrics_receiver.clone())) + } +} diff --git a/bitacross-worker/service/src/ocall_bridge/ffi/fetch_sidechain_blocks_from_peer.rs b/bitacross-worker/service/src/ocall_bridge/ffi/fetch_sidechain_blocks_from_peer.rs new file mode 100644 index 0000000000..c6c8b9e89e --- /dev/null +++ b/bitacross-worker/service/src/ocall_bridge/ffi/fetch_sidechain_blocks_from_peer.rs @@ -0,0 +1,193 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::ocall_bridge::bridge_api::{Bridge, SidechainBridge}; +use itp_utils::write_slice_and_whitespace_pad; +use log::*; +use sgx_types::sgx_status_t; +use std::{slice, sync::Arc}; + +/// # Safety +/// +/// FFI are always unsafe +#[no_mangle] +pub unsafe extern "C" fn ocall_fetch_sidechain_blocks_from_peer( + last_imported_block_hash_ptr: *const u8, + last_imported_block_hash_size: u32, + maybe_until_block_hash_ptr: *const u8, + maybe_until_block_hash_size: u32, + shard_identifier_ptr: *const u8, + shard_identifier_size: u32, + sidechain_blocks_ptr: *mut u8, + sidechain_blocks_size: u32, +) -> sgx_status_t { + fetch_sidechain_blocks_from_peer( + last_imported_block_hash_ptr, + last_imported_block_hash_size, + maybe_until_block_hash_ptr, + maybe_until_block_hash_size, + shard_identifier_ptr, + shard_identifier_size, + sidechain_blocks_ptr, + sidechain_blocks_size, + Bridge::get_sidechain_api(), + ) +} + +#[allow(clippy::too_many_arguments)] +fn fetch_sidechain_blocks_from_peer( + last_imported_block_hash_ptr: *const u8, + last_imported_block_hash_size: u32, + maybe_until_block_hash_ptr: *const u8, + maybe_until_block_hash_size: u32, + shard_identifier_ptr: *const u8, + shard_identifier_size: u32, + sidechain_blocks_ptr: *mut u8, + sidechain_blocks_size: u32, + sidechain_api: Arc, +) -> sgx_status_t { + let last_imported_block_hash_encoded = unsafe { + Vec::from(slice::from_raw_parts( + last_imported_block_hash_ptr, + last_imported_block_hash_size as usize, + )) + }; + let maybe_until_block_hash = unsafe { + Vec::from(slice::from_raw_parts( + maybe_until_block_hash_ptr, + maybe_until_block_hash_size as usize, + )) + }; + let shard_identifier_encoded = unsafe { + Vec::from(slice::from_raw_parts(shard_identifier_ptr, shard_identifier_size as usize)) + }; + + let sidechain_blocks_encoded = match sidechain_api.fetch_sidechain_blocks_from_peer( + last_imported_block_hash_encoded, + maybe_until_block_hash, + shard_identifier_encoded, + ) { + Ok(r) => r, + Err(e) => { + error!("fetch sidechain blocks from peer failed: {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + let sidechain_blocks_encoded_slice = + unsafe { slice::from_raw_parts_mut(sidechain_blocks_ptr, sidechain_blocks_size as usize) }; + if let Err(e) = + write_slice_and_whitespace_pad(sidechain_blocks_encoded_slice, sidechain_blocks_encoded) + { + error!("Failed to transfer encoded sidechain blocks to o-call buffer: {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + } + + sgx_status_t::SGX_SUCCESS +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::ocall_bridge::test::mocks::sidechain_bridge_mock::SidechainBridgeMock; + use codec::{Decode, Encode}; + use its_primitives::types::block::SignedBlock; + use its_test::sidechain_block_builder::{SidechainBlockBuilder, SidechainBlockBuilderTrait}; + use primitive_types::H256; + + #[test] + fn fetch_sidechain_blocks_from_peer_works() { + let sidechain_blocks = vec![ + SidechainBlockBuilder::random().build_signed(), + SidechainBlockBuilder::random().build_signed(), + ]; + + let sidechain_bridge_mock = + Arc::new(SidechainBridgeMock::default().with_peer_blocks(sidechain_blocks.encode())); + + let last_known_block_hash = H256::random(); + let shard_identifier = H256::random(); + let mut block_buffer = vec![0; 16 * 4096]; + + let result = call_fetch_sidechain_blocks_from_peer( + last_known_block_hash, + None, + shard_identifier, + &mut block_buffer, + sidechain_bridge_mock, + ); + + let decoded_blocks: Vec = + Decode::decode(&mut block_buffer.as_slice()).unwrap(); + + assert_eq!(result, sgx_status_t::SGX_SUCCESS); + assert_eq!(sidechain_blocks, decoded_blocks); + } + + #[test] + fn returns_error_if_buffer_is_too_small() { + let sidechain_blocks = vec![ + SidechainBlockBuilder::random().build_signed(), + SidechainBlockBuilder::random().build_signed(), + SidechainBlockBuilder::random().build_signed(), + SidechainBlockBuilder::random().build_signed(), + ]; + + let sidechain_bridge_mock = + Arc::new(SidechainBridgeMock::default().with_peer_blocks(sidechain_blocks.encode())); + + let last_known_block_hash = H256::random(); + let shard_identifier = H256::random(); + let mut block_buffer = vec![0; 16]; // way too small to hold the encoded blocks + + let result = call_fetch_sidechain_blocks_from_peer( + last_known_block_hash, + None, + shard_identifier, + &mut block_buffer, + sidechain_bridge_mock, + ); + + assert_eq!(result, sgx_status_t::SGX_ERROR_UNEXPECTED); + } + + fn call_fetch_sidechain_blocks_from_peer( + last_imported_block_hash: H256, + maybe_until_block_hash: Option, + shard_identifier: H256, + buffer: &mut Vec, + sidechain_bridge: Arc, + ) -> sgx_status_t { + let last_imported_block_hash_encoded = last_imported_block_hash.encode(); + let maybe_until_block_hash_encoded = maybe_until_block_hash.encode(); + let shard_identifier_encoded = shard_identifier.encode(); + + fetch_sidechain_blocks_from_peer( + last_imported_block_hash_encoded.as_ptr(), + last_imported_block_hash_encoded.len() as u32, + maybe_until_block_hash_encoded.as_ptr(), + maybe_until_block_hash_encoded.len() as u32, + shard_identifier_encoded.as_ptr(), + shard_identifier_encoded.len() as u32, + buffer.as_mut_ptr(), + buffer.len() as u32, + sidechain_bridge, + ) + } +} diff --git a/bitacross-worker/service/src/ocall_bridge/ffi/get_ias_socket.rs b/bitacross-worker/service/src/ocall_bridge/ffi/get_ias_socket.rs new file mode 100644 index 0000000000..4b48d2b1ad --- /dev/null +++ b/bitacross-worker/service/src/ocall_bridge/ffi/get_ias_socket.rs @@ -0,0 +1,86 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::ocall_bridge::bridge_api::{Bridge, RemoteAttestationBridge}; +use log::*; +use sgx_types::{c_int, sgx_status_t}; +use std::sync::Arc; + +#[no_mangle] +pub extern "C" fn ocall_get_ias_socket(ret_fd: *mut c_int) -> sgx_status_t { + get_ias_socket(ret_fd, Bridge::get_ra_api()) // inject the RA API (global state) +} + +fn get_ias_socket(ret_fd: *mut c_int, ra_api: Arc) -> sgx_status_t { + debug!(" Entering ocall_get_ias_socket"); + let socket_result = ra_api.get_ias_socket(); + + return match socket_result { + Ok(s) => { + unsafe { + *ret_fd = s; + } + sgx_status_t::SGX_SUCCESS + }, + Err(e) => { + error!("[-] Failed to get IAS socket: {:?}", e); + return e.into() + }, + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::ocall_bridge::bridge_api::{MockRemoteAttestationBridge, OCallBridgeError}; + use std::sync::Arc; + + #[test] + fn get_socket_sets_pointer_result() { + let expected_socket = 4321i32; + + let mut ra_ocall_api_mock = MockRemoteAttestationBridge::new(); + ra_ocall_api_mock + .expect_get_ias_socket() + .times(1) + .returning(move || Ok(expected_socket)); + + let mut ias_sock: i32 = 0; + + let ret_status = get_ias_socket(&mut ias_sock as *mut i32, Arc::new(ra_ocall_api_mock)); + + assert_eq!(ret_status, sgx_status_t::SGX_SUCCESS); + assert_eq!(ias_sock, expected_socket); + } + + #[test] + fn given_error_from_ocall_impl_then_return_sgx_error() { + let mut ra_ocall_api_mock = MockRemoteAttestationBridge::new(); + ra_ocall_api_mock + .expect_get_ias_socket() + .times(1) + .returning(|| Err(OCallBridgeError::GetIasSocket("test error".to_string()))); + + let mut ias_sock: i32 = 0; + let ret_status = get_ias_socket(&mut ias_sock as *mut i32, Arc::new(ra_ocall_api_mock)); + + assert_ne!(ret_status, sgx_status_t::SGX_SUCCESS); + assert_eq!(ias_sock, 0); + } +} diff --git a/bitacross-worker/service/src/ocall_bridge/ffi/get_peers.rs b/bitacross-worker/service/src/ocall_bridge/ffi/get_peers.rs new file mode 100644 index 0000000000..2cc380d6e4 --- /dev/null +++ b/bitacross-worker/service/src/ocall_bridge/ffi/get_peers.rs @@ -0,0 +1,37 @@ +use crate::ocall_bridge::bridge_api::{Bridge, SidechainBridge}; +use itp_utils::write_slice_and_whitespace_pad; +use log::*; +use sgx_types::sgx_status_t; +use std::{slice, sync::Arc}; + +#[no_mangle] +pub unsafe extern "C" fn ocall_get_trusted_peers_urls( + peers_ptr: *mut u8, + peers_size: u32, +) -> sgx_status_t { + get_trusted_peers_urls(peers_ptr, peers_size, Bridge::get_sidechain_api()) +} + +fn get_trusted_peers_urls( + peers_ptr: *mut u8, + peers_size: u32, + sidechain_api: Arc, +) -> sgx_status_t { + debug!(" Entering ocall_get_trusted_peers_urls"); + + let peers_encoded = match sidechain_api.get_trusted_peers_urls() { + Ok(r) => r, + Err(e) => { + error!("get peers failed: {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + }, + }; + + let peers_encoded_slice = unsafe { slice::from_raw_parts_mut(peers_ptr, peers_size as usize) }; + if let Err(e) = write_slice_and_whitespace_pad(peers_encoded_slice, peers_encoded) { + error!("Failed to transfer encoded peers to o-call buffer: {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + } + + sgx_status_t::SGX_SUCCESS +} diff --git a/bitacross-worker/service/src/ocall_bridge/ffi/get_quote.rs b/bitacross-worker/service/src/ocall_bridge/ffi/get_quote.rs new file mode 100644 index 0000000000..abf2954170 --- /dev/null +++ b/bitacross-worker/service/src/ocall_bridge/ffi/get_quote.rs @@ -0,0 +1,140 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::ocall_bridge::bridge_api::{Bridge, RemoteAttestationBridge}; +use log::*; +use sgx_types::{sgx_quote_nonce_t, sgx_quote_sign_type_t, sgx_report_t, sgx_spid_t, sgx_status_t}; +use std::{slice, sync::Arc}; + +/// p_quote must be a pre-allocated memory region of size `maxlen` +#[no_mangle] +pub unsafe extern "C" fn ocall_get_quote( + p_sigrl: *const u8, + sigrl_len: u32, + p_report: *const sgx_report_t, + quote_type: sgx_quote_sign_type_t, + p_spid: *const sgx_spid_t, + p_nonce: *const sgx_quote_nonce_t, + p_qe_report: *mut sgx_report_t, + p_quote: *mut u8, + maxlen: u32, + p_quote_len: *mut u32, +) -> sgx_status_t { + get_quote( + p_sigrl, + sigrl_len, + p_report, + quote_type, + p_spid, + p_nonce, + p_qe_report, + p_quote, + maxlen, + p_quote_len, + Bridge::get_ra_api(), // inject the RA API (global state) + ) +} + +#[allow(clippy::too_many_arguments)] +fn get_quote( + p_sigrl: *const u8, + sigrl_len: u32, + p_report: *const sgx_report_t, + quote_type: sgx_quote_sign_type_t, + p_spid: *const sgx_spid_t, + p_nonce: *const sgx_quote_nonce_t, + p_qe_report: *mut sgx_report_t, + p_quote: *mut u8, + maxlen: u32, + p_quote_len: *mut u32, + ra_api: Arc, +) -> sgx_status_t { + debug!(" Entering ocall_get_quote"); + + let revocation_list: Vec = + unsafe { slice::from_raw_parts(p_sigrl, sigrl_len as usize).to_vec() }; + + let report = unsafe { *p_report }; + let spid = unsafe { *p_spid }; + let quote_nonce = unsafe { *p_nonce }; + + let get_quote_result = + match ra_api.get_quote(revocation_list, report, quote_type, spid, quote_nonce) { + Ok(r) => r, + Err(e) => { + error!("[-] Failed to get quote: {:?}", e); + return e.into() + }, + }; + + let quote = get_quote_result.1; + + if quote.len() as u32 > maxlen { + return sgx_status_t::SGX_ERROR_FAAS_BUFFER_TOO_SHORT + } + + let quote_slice = unsafe { slice::from_raw_parts_mut(p_quote, quote.len()) }; + quote_slice.clone_from_slice(quote.as_slice()); + + unsafe { + *p_qe_report = get_quote_result.0; + *p_quote_len = quote.len() as u32; + }; + + sgx_status_t::SGX_SUCCESS +} + +#[no_mangle] +pub unsafe extern "C" fn ocall_get_dcap_quote( + p_report: *const sgx_report_t, + p_quote: *mut u8, + quote_size: u32, +) -> sgx_status_t { + get_dcap_quote( + p_report, + p_quote, + quote_size, + Bridge::get_ra_api(), // inject the RA API (global state) + ) +} + +fn get_dcap_quote( + p_report: *const sgx_report_t, + p_quote: *mut u8, + quote_size: u32, + ra_api: Arc, +) -> sgx_status_t { + let report = unsafe { *p_report }; + + let quote = match ra_api.get_dcap_quote(report, quote_size) { + Ok(r) => r, + Err(e) => { + error!("Failed to get dcap quote: {:?}", e); + return e.into() + }, + }; + + if quote.len() as u32 > quote_size { + return sgx_status_t::SGX_ERROR_FAAS_BUFFER_TOO_SHORT + } + + let quote_slice = unsafe { slice::from_raw_parts_mut(p_quote, quote.len()) }; + quote_slice.clone_from_slice(quote.as_slice()); + + sgx_status_t::SGX_SUCCESS +} diff --git a/bitacross-worker/service/src/ocall_bridge/ffi/get_qve_report_on_quote.rs b/bitacross-worker/service/src/ocall_bridge/ffi/get_qve_report_on_quote.rs new file mode 100755 index 0000000000..2b73894830 --- /dev/null +++ b/bitacross-worker/service/src/ocall_bridge/ffi/get_qve_report_on_quote.rs @@ -0,0 +1,100 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +use crate::ocall_bridge::bridge_api::{Bridge, RemoteAttestationBridge}; +use log::*; +use sgx_types::*; +use std::{slice, sync::Arc}; + +#[no_mangle] +pub unsafe extern "C" fn ocall_get_qve_report_on_quote( + p_quote: *const u8, + quote_len: u32, + current_time: i64, + p_quote_collateral: *const sgx_ql_qve_collateral_t, + p_collateral_expiration_status: *mut u32, + p_quote_verification_result: *mut sgx_ql_qv_result_t, + p_qve_report_info: *mut sgx_ql_qe_report_info_t, + p_supplemental_data: *mut u8, + supplemental_data_size: u32, +) -> sgx_status_t { + get_qve_report_on_quote( + p_quote, + quote_len, + current_time, + p_quote_collateral, + p_collateral_expiration_status, + p_quote_verification_result, + p_qve_report_info, + p_supplemental_data, + supplemental_data_size, + Bridge::get_ra_api(), // inject the RA API (global state) + ) +} + +#[allow(clippy::too_many_arguments)] +fn get_qve_report_on_quote( + p_quote: *const u8, + quote_len: u32, + current_time: i64, + p_quote_collateral: *const sgx_ql_qve_collateral_t, + p_collateral_expiration_status: *mut u32, + p_quote_verification_result: *mut sgx_ql_qv_result_t, + p_qve_report_info: *mut sgx_ql_qe_report_info_t, + p_supplemental_data: *mut u8, + supplemental_data_size: u32, + ra_api: Arc, +) -> sgx_status_t { + debug!("Entering ocall_get_qve_report_on_quote"); + if p_quote.is_null() + || quote_len == 0 + || p_quote_collateral.is_null() + || p_collateral_expiration_status.is_null() + || p_quote_verification_result.is_null() + || p_qve_report_info.is_null() + || p_supplemental_data.is_null() + || supplemental_data_size == 0 + { + return sgx_status_t::SGX_ERROR_INVALID_PARAMETER + } + let quote: Vec = unsafe { slice::from_raw_parts(p_quote, quote_len as usize).to_vec() }; + let quote_collateral = unsafe { &*p_quote_collateral }; + let qve_report_info = unsafe { *p_qve_report_info }; + + let qve_report = match ra_api.get_qve_report_on_quote( + quote, + current_time, + quote_collateral, + qve_report_info, + supplemental_data_size, + ) { + Ok(return_values) => return_values, + Err(e) => { + error!("Failed to get quote: {:?}", e); + return e.into() + }, + }; + + let supplemental_data_slice = + unsafe { slice::from_raw_parts_mut(p_supplemental_data, supplemental_data_size as usize) }; + supplemental_data_slice.clone_from_slice(qve_report.supplemental_data.as_slice()); + + unsafe { + *p_collateral_expiration_status = qve_report.collateral_expiration_status; + *p_quote_verification_result = qve_report.quote_verification_result; + *p_qve_report_info = qve_report.qve_report_info_return_value; + }; + + sgx_status_t::SGX_SUCCESS +} diff --git a/bitacross-worker/service/src/ocall_bridge/ffi/get_update_info.rs b/bitacross-worker/service/src/ocall_bridge/ffi/get_update_info.rs new file mode 100644 index 0000000000..55a9c7bfb4 --- /dev/null +++ b/bitacross-worker/service/src/ocall_bridge/ffi/get_update_info.rs @@ -0,0 +1,61 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::ocall_bridge::bridge_api::{Bridge, RemoteAttestationBridge}; +use log::*; +use sgx_types::{sgx_platform_info_t, sgx_status_t, sgx_update_info_bit_t}; +use std::sync::Arc; + +#[no_mangle] +pub extern "C" fn ocall_get_update_info( + p_platform_blob: *const sgx_platform_info_t, + enclave_trusted: i32, + p_update_info: *mut sgx_update_info_bit_t, +) -> sgx_status_t { + get_update_info( + p_platform_blob, + enclave_trusted, + p_update_info, + Bridge::get_ra_api(), // inject the RA API (global state) + ) +} + +fn get_update_info( + p_platform_blob: *const sgx_platform_info_t, + enclave_trusted: i32, + p_update_info: *mut sgx_update_info_bit_t, + ra_api: Arc, +) -> sgx_status_t { + debug!(" Entering ocall_get_update_info"); + + let platform_blob = unsafe { *p_platform_blob }; + + let update_info_result = match ra_api.get_update_info(platform_blob, enclave_trusted) { + Ok(r) => r, + Err(e) => { + error!("[-] Failed to get update info: {:?}", e); + return e.into() + }, + }; + + unsafe { + *p_update_info = update_info_result; + } + + sgx_status_t::SGX_SUCCESS +} diff --git a/bitacross-worker/service/src/ocall_bridge/ffi/init_quote.rs b/bitacross-worker/service/src/ocall_bridge/ffi/init_quote.rs new file mode 100644 index 0000000000..095e01af6d --- /dev/null +++ b/bitacross-worker/service/src/ocall_bridge/ffi/init_quote.rs @@ -0,0 +1,85 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::ocall_bridge::bridge_api::{Bridge, RemoteAttestationBridge}; +use log::*; +use sgx_types::{sgx_epid_group_id_t, sgx_status_t, sgx_target_info_t}; +use std::sync::Arc; + +#[no_mangle] +pub unsafe extern "C" fn ocall_sgx_init_quote( + ret_ti: *mut sgx_target_info_t, + ret_gid: *mut sgx_epid_group_id_t, +) -> sgx_status_t { + sgx_init_quote(ret_ti, ret_gid, Bridge::get_ra_api()) // inject the RA API (global state) +} + +fn sgx_init_quote( + ret_ti: *mut sgx_target_info_t, + ret_gid: *mut sgx_epid_group_id_t, + ra_api: Arc, +) -> sgx_status_t { + debug!(" Entering ocall_sgx_init_quote"); + let init_result = match ra_api.init_quote() { + Ok(r) => r, + Err(e) => { + error!("[-] Failed to init quote: {:?}", e); + return e.into() + }, + }; + + unsafe { + *ret_ti = init_result.0; + *ret_gid = init_result.1; + } + + sgx_status_t::SGX_SUCCESS +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::ocall_bridge::bridge_api::MockRemoteAttestationBridge; + use std::sync::Arc; + + #[test] + fn init_quote_sets_results() { + let mut ra_ocall_api_mock = MockRemoteAttestationBridge::new(); + ra_ocall_api_mock + .expect_init_quote() + .times(1) + .returning(|| Ok((dummy_target_info(), [8u8; 4]))); + + let mut ti: sgx_target_info_t = sgx_target_info_t::default(); + let mut eg: sgx_epid_group_id_t = sgx_epid_group_id_t::default(); + + let ret_status = sgx_init_quote( + &mut ti as *mut sgx_target_info_t, + &mut eg as *mut sgx_epid_group_id_t, + Arc::new(ra_ocall_api_mock), + ); + + assert_eq!(ret_status, sgx_status_t::SGX_SUCCESS); + assert_eq!(eg, [8u8; 4]); + } + + fn dummy_target_info() -> sgx_target_info_t { + sgx_target_info_t::default() + } +} diff --git a/bitacross-worker/service/src/ocall_bridge/ffi/ipfs.rs b/bitacross-worker/service/src/ocall_bridge/ffi/ipfs.rs new file mode 100644 index 0000000000..e264b49db2 --- /dev/null +++ b/bitacross-worker/service/src/ocall_bridge/ffi/ipfs.rs @@ -0,0 +1,76 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::ocall_bridge::bridge_api::{Bridge, Cid, IpfsBridge}; +use log::*; +use sgx_types::sgx_status_t; +use std::{slice, sync::Arc}; + +/// C-API exposed for o-call from enclave +#[no_mangle] +pub unsafe extern "C" fn ocall_write_ipfs( + enc_state: *const u8, + enc_state_size: u32, + cid: *mut u8, + cid_size: u32, +) -> sgx_status_t { + write_ipfs(enc_state, enc_state_size, cid, cid_size, Bridge::get_ipfs_api()) +} + +/// C-API exposed for o-call from enclave +#[no_mangle] +pub unsafe extern "C" fn ocall_read_ipfs(cid: *const u8, cid_size: u32) -> sgx_status_t { + read_ipfs(cid, cid_size, Bridge::get_ipfs_api()) +} + +fn write_ipfs( + enc_state: *const u8, + enc_state_size: u32, + cid: *mut u8, + cid_size: u32, + ipfs_api: Arc, +) -> sgx_status_t { + let state = unsafe { slice::from_raw_parts(enc_state, enc_state_size as usize) }; + let cid = unsafe { slice::from_raw_parts_mut(cid, cid_size as usize) }; + + return match ipfs_api.write_to_ipfs(state) { + Ok(r) => { + cid.clone_from_slice(&r); + sgx_status_t::SGX_SUCCESS + }, + Err(e) => { + error!("OCall to write_ipfs failed: {:?}", e); + sgx_status_t::SGX_ERROR_UNEXPECTED + }, + } +} + +fn read_ipfs(cid: *const u8, cid_size: u32, ipfs_api: Arc) -> sgx_status_t { + let _cid = unsafe { slice::from_raw_parts(cid, cid_size as usize) }; + + let mut cid: Cid = [0; 46]; + cid.clone_from_slice(_cid); + + match ipfs_api.read_from_ipfs(cid) { + Ok(_) => sgx_status_t::SGX_SUCCESS, + Err(e) => { + error!("OCall to read_ipfs failed: {:?}", e); + sgx_status_t::SGX_ERROR_UNEXPECTED + }, + } +} diff --git a/bitacross-worker/service/src/ocall_bridge/ffi/mod.rs b/bitacross-worker/service/src/ocall_bridge/ffi/mod.rs new file mode 100644 index 0000000000..d146db1046 --- /dev/null +++ b/bitacross-worker/service/src/ocall_bridge/ffi/mod.rs @@ -0,0 +1,36 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Foreign Function interface for all the OCalls. +//! Implementations of C-API functions, that can be called from the Enclave. +//! These should just be wrappers that transform the C-API structures and call the +//! actual implementation of the OCalls (using the traits defined in the bridge_api). + +pub mod fetch_sidechain_blocks_from_peer; +pub mod get_ias_socket; +pub mod get_peers; +pub mod get_quote; +pub mod get_qve_report_on_quote; +pub mod get_update_info; +pub mod init_quote; +pub mod ipfs; +pub mod propose_sidechain_blocks; +pub mod send_to_parentchain; +pub mod store_sidechain_blocks; +pub mod update_metric; +pub mod worker_request; diff --git a/bitacross-worker/service/src/ocall_bridge/ffi/propose_sidechain_blocks.rs b/bitacross-worker/service/src/ocall_bridge/ffi/propose_sidechain_blocks.rs new file mode 100644 index 0000000000..21ff07d0bb --- /dev/null +++ b/bitacross-worker/service/src/ocall_bridge/ffi/propose_sidechain_blocks.rs @@ -0,0 +1,50 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::ocall_bridge::bridge_api::{Bridge, SidechainBridge}; +use log::*; +use sgx_types::sgx_status_t; +use std::{slice, sync::Arc}; + +/// # Safety +/// +/// FFI are always unsafe +#[no_mangle] +pub unsafe extern "C" fn ocall_propose_sidechain_blocks( + signed_blocks_ptr: *const u8, + signed_blocks_size: u32, +) -> sgx_status_t { + propose_sidechain_blocks(signed_blocks_ptr, signed_blocks_size, Bridge::get_sidechain_api()) +} + +fn propose_sidechain_blocks( + signed_blocks_ptr: *const u8, + signed_blocks_size: u32, + sidechain_api: Arc, +) -> sgx_status_t { + let signed_blocks_vec: Vec = + unsafe { Vec::from(slice::from_raw_parts(signed_blocks_ptr, signed_blocks_size as usize)) }; + + match sidechain_api.propose_sidechain_blocks(signed_blocks_vec) { + Ok(_) => sgx_status_t::SGX_SUCCESS, + Err(e) => { + error!("send sidechain blocks failed: {:?}", e); + sgx_status_t::SGX_ERROR_UNEXPECTED + }, + } +} diff --git a/bitacross-worker/service/src/ocall_bridge/ffi/send_to_parentchain.rs b/bitacross-worker/service/src/ocall_bridge/ffi/send_to_parentchain.rs new file mode 100644 index 0000000000..d7e524a254 --- /dev/null +++ b/bitacross-worker/service/src/ocall_bridge/ffi/send_to_parentchain.rs @@ -0,0 +1,67 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::ocall_bridge::bridge_api::{Bridge, WorkerOnChainBridge}; +use log::*; +use sgx_types::{c_int, sgx_status_t}; +use std::{slice, sync::Arc, vec::Vec}; + +/// # Safety +/// +/// FFI are always unsafe +#[no_mangle] +pub unsafe extern "C" fn ocall_send_to_parentchain( + extrinsics_encoded: *const u8, + extrinsics_encoded_size: u32, + parentchain_id: *const u8, + parentchain_id_size: u32, + await_each_inclusion: c_int, +) -> sgx_status_t { + send_to_parentchain( + extrinsics_encoded, + extrinsics_encoded_size, + parentchain_id, + parentchain_id_size, + await_each_inclusion == 1, + Bridge::get_oc_api(), + ) +} + +fn send_to_parentchain( + extrinsics_encoded: *const u8, + extrinsics_encoded_size: u32, + parentchain_id: *const u8, + parentchain_id_size: u32, + await_each_inclusion: bool, + oc_api: Arc, +) -> sgx_status_t { + let extrinsics_encoded_vec: Vec = unsafe { + Vec::from(slice::from_raw_parts(extrinsics_encoded, extrinsics_encoded_size as usize)) + }; + + let parentchain_id: Vec = + unsafe { Vec::from(slice::from_raw_parts(parentchain_id, parentchain_id_size as usize)) }; + + match oc_api.send_to_parentchain(extrinsics_encoded_vec, parentchain_id, await_each_inclusion) { + Ok(_) => sgx_status_t::SGX_SUCCESS, + Err(e) => { + error!("send extrinsics_encoded failed: {:?}", e); + sgx_status_t::SGX_ERROR_UNEXPECTED + }, + } +} diff --git a/bitacross-worker/service/src/ocall_bridge/ffi/store_sidechain_blocks.rs b/bitacross-worker/service/src/ocall_bridge/ffi/store_sidechain_blocks.rs new file mode 100644 index 0000000000..70361d8fd7 --- /dev/null +++ b/bitacross-worker/service/src/ocall_bridge/ffi/store_sidechain_blocks.rs @@ -0,0 +1,50 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::ocall_bridge::bridge_api::{Bridge, SidechainBridge}; +use log::*; +use sgx_types::sgx_status_t; +use std::{slice, sync::Arc}; + +/// # Safety +/// +/// FFI are always unsafe +#[no_mangle] +pub unsafe extern "C" fn ocall_store_sidechain_blocks( + signed_blocks_ptr: *const u8, + signed_blocks_size: u32, +) -> sgx_status_t { + store_sidechain_blocks(signed_blocks_ptr, signed_blocks_size, Bridge::get_sidechain_api()) +} + +fn store_sidechain_blocks( + signed_blocks_ptr: *const u8, + signed_blocks_size: u32, + sidechain_api: Arc, +) -> sgx_status_t { + let signed_blocks_vec: Vec = + unsafe { Vec::from(slice::from_raw_parts(signed_blocks_ptr, signed_blocks_size as usize)) }; + + match sidechain_api.store_sidechain_blocks(signed_blocks_vec) { + Ok(_) => sgx_status_t::SGX_SUCCESS, + Err(e) => { + error!("store sidechain blocks failed: {:?}", e); + sgx_status_t::SGX_ERROR_UNEXPECTED + }, + } +} diff --git a/bitacross-worker/service/src/ocall_bridge/ffi/update_metric.rs b/bitacross-worker/service/src/ocall_bridge/ffi/update_metric.rs new file mode 100644 index 0000000000..0b97de74f9 --- /dev/null +++ b/bitacross-worker/service/src/ocall_bridge/ffi/update_metric.rs @@ -0,0 +1,50 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::ocall_bridge::bridge_api::{Bridge, MetricsBridge}; +use log::*; +use sgx_types::sgx_status_t; +use std::{slice, sync::Arc}; + +/// # Safety +/// +/// FFI are always unsafe +#[no_mangle] +pub unsafe extern "C" fn ocall_update_metric( + metric_ptr: *const u8, + metric_size: u32, +) -> sgx_status_t { + update_metric(metric_ptr, metric_size, Bridge::get_metrics_api()) +} + +fn update_metric( + metric_ptr: *const u8, + metric_size: u32, + oc_api: Arc, +) -> sgx_status_t { + let metric_encoded: Vec = + unsafe { Vec::from(slice::from_raw_parts(metric_ptr, metric_size as usize)) }; + + match oc_api.update_metric(metric_encoded) { + Ok(_) => sgx_status_t::SGX_SUCCESS, + Err(e) => { + error!("update_metric o-call failed: {:?}", e); + sgx_status_t::SGX_ERROR_UNEXPECTED + }, + } +} diff --git a/bitacross-worker/service/src/ocall_bridge/ffi/worker_request.rs b/bitacross-worker/service/src/ocall_bridge/ffi/worker_request.rs new file mode 100644 index 0000000000..7dbd9be957 --- /dev/null +++ b/bitacross-worker/service/src/ocall_bridge/ffi/worker_request.rs @@ -0,0 +1,77 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::ocall_bridge::bridge_api::{Bridge, WorkerOnChainBridge}; +use itp_utils::write_slice_and_whitespace_pad; +use log::*; +use sgx_types::sgx_status_t; +use std::{slice, sync::Arc, vec::Vec}; + +/// # Safety +/// +/// FFI are always unsafe +#[no_mangle] +pub unsafe extern "C" fn ocall_worker_request( + request: *const u8, + req_size: u32, + parentchain_id: *const u8, + parentchain_id_size: u32, + response: *mut u8, + resp_size: u32, +) -> sgx_status_t { + worker_request( + request, + req_size, + parentchain_id, + parentchain_id_size, + response, + resp_size, + Bridge::get_oc_api(), + ) +} + +fn worker_request( + request: *const u8, + req_size: u32, + parentchain_id: *const u8, + parentchain_id_size: u32, + response: *mut u8, + resp_size: u32, + oc_api: Arc, +) -> sgx_status_t { + let request_vec: Vec = + unsafe { Vec::from(slice::from_raw_parts(request, req_size as usize)) }; + + let parentchain_id: Vec = + unsafe { Vec::from(slice::from_raw_parts(parentchain_id, parentchain_id_size as usize)) }; + + match oc_api.worker_request(request_vec, parentchain_id) { + Ok(r) => { + let resp_slice = unsafe { slice::from_raw_parts_mut(response, resp_size as usize) }; + if let Err(e) = write_slice_and_whitespace_pad(resp_slice, r) { + error!("Failed to transfer worker request response to o-call buffer: {:?}", e); + return sgx_status_t::SGX_ERROR_UNEXPECTED + } + sgx_status_t::SGX_SUCCESS + }, + Err(e) => { + error!("Worker request failed: {:?}", e); + sgx_status_t::SGX_ERROR_UNEXPECTED + }, + } +} diff --git a/bitacross-worker/service/src/ocall_bridge/ipfs_ocall.rs b/bitacross-worker/service/src/ocall_bridge/ipfs_ocall.rs new file mode 100644 index 0000000000..1dc1d9beab --- /dev/null +++ b/bitacross-worker/service/src/ocall_bridge/ipfs_ocall.rs @@ -0,0 +1,112 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::ocall_bridge::bridge_api::{Cid, IpfsBridge, OCallBridgeError, OCallBridgeResult}; +use futures::TryStreamExt; +use ipfs_api::IpfsClient; +use log::*; +use std::{ + fs::File, + io::{Cursor, Write}, + str, + sync::mpsc::channel, +}; + +pub struct IpfsOCall; + +impl IpfsBridge for IpfsOCall { + fn write_to_ipfs(&self, data: &'static [u8]) -> OCallBridgeResult { + debug!(" Entering ocall_write_ipfs"); + write_to_ipfs(data) + } + + fn read_from_ipfs(&self, cid: Cid) -> OCallBridgeResult<()> { + debug!("Entering ocall_read_ipfs"); + + let result = read_from_ipfs(cid); + match result { + Ok(res) => { + let filename = str::from_utf8(&cid).map_err(|_| { + OCallBridgeError::IpfsError("Could not convert cid bytes".to_string()) + })?; + create_file(filename, &res).map_err(OCallBridgeError::IpfsError) + }, + Err(_) => Err(OCallBridgeError::IpfsError("failed to read from IPFS".to_string())), + } + } +} + +fn create_file(filename: &str, result: &[u8]) -> Result<(), String> { + match File::create(filename) { + Ok(mut f) => f + .write_all(result) + .map_or_else(|e| Err(format!("failed writing to file: {}", e)), |_| Ok(())), + Err(e) => Err(format!("failed to create file: {}", e)), + } +} + +#[tokio::main] +async fn write_to_ipfs(data: &'static [u8]) -> OCallBridgeResult { + // Creates an `IpfsClient` connected to the endpoint specified in ~/.ipfs/api. + // If not found, tries to connect to `localhost:5001`. + let client = IpfsClient::default(); + + match client.version().await { + Ok(version) => info!("version: {:?}", version.version), + Err(e) => eprintln!("error getting version: {}", e), + } + + let datac = Cursor::new(data); + let (tx, rx) = channel(); + + match client.add(datac).await { + Ok(res) => { + info!("Result Hash {}", res.hash); + tx.send(res.hash.into_bytes()).map_err(|e| { + OCallBridgeError::IpfsError(format!( + "Could not get result from IPFS, reason: {:?}", + e + )) + })? + }, + Err(e) => eprintln!("error adding file: {}", e), + } + let mut cid: Cid = [0; 46]; + let result = &rx.recv().map_err(|e| { + OCallBridgeError::IpfsError(format!("Could not get result from IPFS, reason: {:?}", e)) + })?; + cid.clone_from_slice(result); + Ok(cid) +} + +#[tokio::main] +pub async fn read_from_ipfs(cid: Cid) -> Result, String> { + // Creates an `IpfsClient` connected to the endpoint specified in ~/.ipfs/api. + // If not found, tries to connect to `localhost:5001`. + let client = IpfsClient::default(); + let h = str::from_utf8(&cid).map_err(|_| "Could not convert cid bytes".to_string())?; + + info!("Fetching content from: {}", h); + + client + .cat(h) + .map_ok(|chunk| chunk.to_vec()) + .map_err(|e| e.to_string()) + .try_concat() + .await +} diff --git a/bitacross-worker/service/src/ocall_bridge/metrics_ocall.rs b/bitacross-worker/service/src/ocall_bridge/metrics_ocall.rs new file mode 100644 index 0000000000..a06deff339 --- /dev/null +++ b/bitacross-worker/service/src/ocall_bridge/metrics_ocall.rs @@ -0,0 +1,51 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + ocall_bridge::bridge_api::{MetricsBridge, OCallBridgeError, OCallBridgeResult}, + prometheus_metrics::ReceiveEnclaveMetrics, +}; +use codec::Decode; +use itp_enclave_metrics::EnclaveMetric; +use std::sync::Arc; + +pub struct MetricsOCall { + receiver: Arc, +} + +impl MetricsOCall { + pub fn new(receiver: Arc) -> Self { + MetricsOCall { receiver } + } +} + +impl MetricsBridge for MetricsOCall +where + MetricsReceiver: ReceiveEnclaveMetrics, +{ + fn update_metric(&self, metric_encoded: Vec) -> OCallBridgeResult<()> { + let metric: EnclaveMetric = + Decode::decode(&mut metric_encoded.as_slice()).map_err(|e| { + OCallBridgeError::UpdateMetric(format!("Failed to decode metric: {:?}", e)) + })?; + + self.receiver.receive_enclave_metric(metric).map_err(|e| { + OCallBridgeError::UpdateMetric(format!("Failed to receive enclave metric: {:?}", e)) + }) + } +} diff --git a/bitacross-worker/service/src/ocall_bridge/mod.rs b/bitacross-worker/service/src/ocall_bridge/mod.rs new file mode 100644 index 0000000000..91a5f8887f --- /dev/null +++ b/bitacross-worker/service/src/ocall_bridge/mod.rs @@ -0,0 +1,32 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +// TODO This entire module should be extracted to a separate crate and re-named to o-call tunnel, see #288 and #316 + +pub mod bridge_api; +pub mod component_factory; + +mod ffi; +mod ipfs_ocall; +mod metrics_ocall; +mod remote_attestation_ocall; +mod sidechain_ocall; +mod worker_on_chain_ocall; + +#[cfg(test)] +pub mod test; diff --git a/bitacross-worker/service/src/ocall_bridge/remote_attestation_ocall.rs b/bitacross-worker/service/src/ocall_bridge/remote_attestation_ocall.rs new file mode 100644 index 0000000000..0310f7ad18 --- /dev/null +++ b/bitacross-worker/service/src/ocall_bridge/remote_attestation_ocall.rs @@ -0,0 +1,150 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::ocall_bridge::bridge_api::{ + OCallBridgeError, OCallBridgeResult, RemoteAttestationBridge, +}; +use itp_enclave_api::remote_attestation::{QveReport, RemoteAttestationCallBacks}; +use log::debug; +use sgx_types::*; +use std::{ + net::{SocketAddr, TcpStream}, + os::unix::io::IntoRawFd, + sync::Arc, +}; + +pub struct RemoteAttestationOCall { + enclave_api: Arc, +} + +impl RemoteAttestationOCall { + pub fn new(enclave_api: Arc) -> Self { + RemoteAttestationOCall { enclave_api } + } +} + +impl RemoteAttestationBridge for RemoteAttestationOCall +where + E: RemoteAttestationCallBacks, +{ + fn init_quote(&self) -> OCallBridgeResult<(sgx_target_info_t, sgx_epid_group_id_t)> { + debug!("RemoteAttestationBridge: init quote"); + self.enclave_api.init_quote().map_err(|e| match e { + itp_enclave_api::error::Error::Sgx(s) => OCallBridgeError::InitQuote(s), + _ => OCallBridgeError::InitQuote(sgx_status_t::SGX_ERROR_UNEXPECTED), + }) + } + + fn get_ias_socket(&self) -> OCallBridgeResult { + let port = 443; + let hostname = "api.trustedservices.intel.com"; + + let addr = lookup_ipv4(hostname, port).map_err(OCallBridgeError::GetIasSocket)?; + + let stream = TcpStream::connect(addr).map_err(|_| { + OCallBridgeError::GetIasSocket("[-] Connect tls server failed!".to_string()) + })?; + + Ok(stream.into_raw_fd()) + } + + fn get_quote( + &self, + revocation_list: Vec, + report: sgx_report_t, + quote_type: sgx_quote_sign_type_t, + spid: sgx_spid_t, + quote_nonce: sgx_quote_nonce_t, + ) -> OCallBridgeResult<(sgx_report_t, Vec)> { + debug!("RemoteAttestationBridge: get quote type: {:?}", quote_type); + let real_quote_len = + self.enclave_api.calc_quote_size(revocation_list.clone()).map_err(|e| match e { + itp_enclave_api::error::Error::Sgx(s) => OCallBridgeError::GetQuote(s), + _ => OCallBridgeError::GetQuote(sgx_status_t::SGX_ERROR_UNEXPECTED), + })?; + + debug!("RemoteAttestationBridge: real quote length: {}", real_quote_len); + self.enclave_api + .get_quote(revocation_list, report, quote_type, spid, quote_nonce, real_quote_len) + .map_err(|e| match e { + itp_enclave_api::error::Error::Sgx(s) => OCallBridgeError::GetQuote(s), + _ => OCallBridgeError::GetQuote(sgx_status_t::SGX_ERROR_UNEXPECTED), + }) + } + + fn get_dcap_quote(&self, report: sgx_report_t, quote_size: u32) -> OCallBridgeResult> { + debug!("RemoteAttestationBridge: get dcap quote, size: {}", quote_size); + + self.enclave_api.get_dcap_quote(report, quote_size).map_err(|e| match e { + itp_enclave_api::error::Error::Sgx(s) => OCallBridgeError::GetQuote(s), + _ => OCallBridgeError::GetQuote(sgx_status_t::SGX_ERROR_UNEXPECTED), + }) + } + + fn get_qve_report_on_quote( + &self, + quote: Vec, + current_time: i64, + quote_collateral: &sgx_ql_qve_collateral_t, + qve_report_info: sgx_ql_qe_report_info_t, + supplemental_data_size: u32, + ) -> OCallBridgeResult { + debug!("RemoteAttestationBridge: get qve report on quote, length: {}", quote.len()); + + self.enclave_api + .get_qve_report_on_quote( + quote, + current_time, + quote_collateral, + qve_report_info, + supplemental_data_size, + ) + .map_err(|e| match e { + itp_enclave_api::error::Error::Sgx(s) => OCallBridgeError::GetQuote(s), + _ => OCallBridgeError::GetQuote(sgx_status_t::SGX_ERROR_UNEXPECTED), + }) + } + + fn get_update_info( + &self, + platform_blob: sgx_platform_info_t, + enclave_trusted: i32, + ) -> OCallBridgeResult { + debug!("RemoteAttestationBridge: get update into"); + + self.enclave_api + .get_update_info(platform_blob, enclave_trusted) + .map_err(|e| match e { + itp_enclave_api::error::Error::Sgx(s) => OCallBridgeError::GetUpdateInfo(s), + _ => OCallBridgeError::GetUpdateInfo(sgx_status_t::SGX_ERROR_UNEXPECTED), + }) + } +} + +fn lookup_ipv4(host: &str, port: u16) -> Result { + use std::net::ToSocketAddrs; + + let addrs = (host, port).to_socket_addrs().map_err(|e| format!("{:?}", e))?; + for addr in addrs { + if let SocketAddr::V4(_) = addr { + return Ok(addr) + } + } + + Err("Cannot lookup address".to_string()) +} diff --git a/bitacross-worker/service/src/ocall_bridge/sidechain_ocall.rs b/bitacross-worker/service/src/ocall_bridge/sidechain_ocall.rs new file mode 100644 index 0000000000..667901cced --- /dev/null +++ b/bitacross-worker/service/src/ocall_bridge/sidechain_ocall.rs @@ -0,0 +1,282 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + globals::tokio_handle::GetTokioHandle, + ocall_bridge::bridge_api::{OCallBridgeError, OCallBridgeResult, SidechainBridge}, + sync_block_broadcaster::BroadcastBlocks, + worker_peers_registry::PeersRegistry, +}; +use codec::{Decode, Encode}; +use itp_types::{BlockHash, ShardIdentifier}; +use its_peer_fetch::FetchBlocksFromPeer; +use its_primitives::{traits::Block, types::SignedBlock as SignedSidechainBlock}; +use its_storage::BlockStorage; +use log::*; +use std::sync::Arc; + +pub struct SidechainOCall< + BlockBroadcaster, + Storage, + WorkerPeerRegistry, + PeerBlockFetcher, + TokioHandle, +> { + block_broadcaster: Arc, + block_storage: Arc, + peer_registry: Arc, + peer_block_fetcher: Arc, + tokio_handle: Arc, +} + +impl + SidechainOCall +{ + pub fn new( + block_broadcaster: Arc, + block_storage: Arc, + peer_registry: Arc, + peer_block_fetcher: Arc, + tokio_handle: Arc, + ) -> Self { + SidechainOCall { + block_broadcaster, + block_storage, + peer_registry, + peer_block_fetcher, + tokio_handle, + } + } +} + +impl SidechainBridge + for SidechainOCall +where + BlockBroadcaster: BroadcastBlocks, + Storage: BlockStorage, + WorkerPeerRegistry: PeersRegistry, + PeerBlockFetcher: FetchBlocksFromPeer, + TokioHandle: GetTokioHandle, +{ + fn propose_sidechain_blocks(&self, signed_blocks_encoded: Vec) -> OCallBridgeResult<()> { + // TODO: improve error handling, using a mut status is not good design? + let mut status: OCallBridgeResult<()> = Ok(()); + + // handle blocks + let signed_blocks: Vec = + match Decode::decode(&mut signed_blocks_encoded.as_slice()) { + Ok(blocks) => blocks, + Err(_) => { + status = Err(OCallBridgeError::ProposeSidechainBlock( + "Could not decode signed blocks".to_string(), + )); + vec![] + }, + }; + + if !signed_blocks.is_empty() { + info!( + "Enclave produced sidechain blocks: {:?}", + signed_blocks + .iter() + .map(|b| b.block.header().block_number) + .collect::>() + ); + } else { + debug!("Enclave did not produce sidechain blocks"); + } + + // FIXME: When & where should peers be updated? + debug!("Updating peers.."); + if let Err(e) = self.peer_registry.update_peers() { + error!("Error updating peers: {:?}", e); + // Fixme: returning an error here results in a `HeaderAncestryMismatch` error. + // status = sgx_status_t::SGX_ERROR_UNEXPECTED; + } else { + info!("Successfully updated peers"); + } + + debug!("Broadcasting sidechain blocks ..."); + if let Err(e) = self.block_broadcaster.broadcast_blocks(signed_blocks) { + error!("Error broadcasting blocks: {:?}", e); + // Fixme: returning an error here results in a `HeaderAncestryMismatch` error. + // status = sgx_status_t::SGX_ERROR_UNEXPECTED; + } else { + info!("Successfully broadcast blocks"); + } + + status + } + + fn store_sidechain_blocks(&self, signed_blocks_encoded: Vec) -> OCallBridgeResult<()> { + // TODO: improve error handling, using a mut status is not good design? + let mut status: OCallBridgeResult<()> = Ok(()); + + let signed_blocks: Vec = + match Decode::decode(&mut signed_blocks_encoded.as_slice()) { + Ok(blocks) => blocks, + Err(_) => { + status = Err(OCallBridgeError::ProposeSidechainBlock( + "Could not decode signed blocks".to_string(), + )); + vec![] + }, + }; + + if let Err(e) = self.block_storage.store_blocks(signed_blocks) { + error!("Error storing blocks: {:?}", e); + } + + status + } + + fn fetch_sidechain_blocks_from_peer( + &self, + last_imported_block_hash_encoded: Vec, + maybe_until_block_hash_encoded: Vec, + shard_identifier_encoded: Vec, + ) -> OCallBridgeResult> { + let last_imported_block_hash: BlockHash = + Decode::decode(&mut last_imported_block_hash_encoded.as_slice()).map_err(|_| { + OCallBridgeError::FetchSidechainBlocksFromPeer( + "Failed to decode last imported block hash".to_string(), + ) + })?; + + let maybe_until_block_hash: Option = + Decode::decode(&mut maybe_until_block_hash_encoded.as_slice()).map_err(|_| { + OCallBridgeError::FetchSidechainBlocksFromPeer( + "Failed to decode optional until block hash".to_string(), + ) + })?; + + let shard_identifier: ShardIdentifier = + Decode::decode(&mut shard_identifier_encoded.as_slice()).map_err(|_| { + OCallBridgeError::FetchSidechainBlocksFromPeer( + "Failed to decode shard identifier".to_string(), + ) + })?; + + info!("[O-call] fetching blocks from peer.."); + + let tokio_handle = self.tokio_handle.get_handle(); + + let signed_sidechain_blocks = tokio_handle + .block_on(self.peer_block_fetcher.fetch_blocks_from_peer( + last_imported_block_hash, + maybe_until_block_hash, + shard_identifier, + )) + .map_err(|e| { + OCallBridgeError::FetchSidechainBlocksFromPeer(format!( + "Failed to execute block fetching from peer: {:?}", + e + )) + })?; + + info!("[O-call] successfully fetched {} blocks from peer", signed_sidechain_blocks.len()); + + Ok(signed_sidechain_blocks.encode()) + } + + fn get_trusted_peers_urls(&self) -> OCallBridgeResult> { + let peers = self.peer_registry.read_trusted_peers().unwrap(); + Ok(peers.encode()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + globals::tokio_handle::ScopedTokioHandle, + tests::mocks::{ + broadcast_blocks_mock::BroadcastBlocksMock, + update_worker_peers_mock::WorkerPeersRegistryMock, + }, + }; + use codec::Decode; + use its_peer_fetch::mocks::fetch_blocks_from_peer_mock::FetchBlocksFromPeerMock; + use its_primitives::types::block::SignedBlock as SignedSidechainBlock; + use its_storage::{interface::BlockStorage, Result as StorageResult}; + use its_test::sidechain_block_builder::{SidechainBlockBuilder, SidechainBlockBuilderTrait}; + use primitive_types::H256; + use std::{collections::HashMap, vec::Vec}; + + struct BlockStorageMock; + impl BlockStorage for BlockStorageMock { + fn store_blocks(&self, _blocks: Vec) -> StorageResult<()> { + Ok(()) + } + } + + type TestSidechainOCall = SidechainOCall< + BroadcastBlocksMock, + BlockStorageMock, + WorkerPeersRegistryMock, + FetchBlocksFromPeerMock, + ScopedTokioHandle, + >; + + #[test] + fn fetch_sidechain_blocks_from_peer_works() { + let last_imported_block_hash = H256::random(); + let until_block_hash: Option = None; + let shard_identifier = H256::random(); + let blocks = vec![ + SidechainBlockBuilder::random().build_signed(), + SidechainBlockBuilder::random().build_signed(), + ]; + let peer_blocks_map = HashMap::from([(shard_identifier, blocks.clone())]); + let sidechain_ocall = setup_sidechain_ocall_with_peer_blocks(peer_blocks_map); + + let fetched_blocks_encoded = sidechain_ocall + .fetch_sidechain_blocks_from_peer( + last_imported_block_hash.encode(), + until_block_hash.encode(), + shard_identifier.encode(), + ) + .unwrap(); + + let fetched_blocks_decoded: Vec = + Decode::decode(&mut fetched_blocks_encoded.as_slice()).unwrap(); + + assert_eq!(blocks, fetched_blocks_decoded); + } + + fn setup_sidechain_ocall_with_peer_blocks( + peer_blocks_map: HashMap>, + ) -> TestSidechainOCall { + let block_broadcaster_mock = Arc::new(BroadcastBlocksMock {}); + let block_storage_mock = Arc::new(BlockStorageMock {}); + let worker_peers_registry_mock = Arc::new(WorkerPeersRegistryMock {}); + let peer_block_fetcher_mock = Arc::new( + FetchBlocksFromPeerMock::::default() + .with_signed_blocks(peer_blocks_map), + ); + let scoped_tokio_handle = Arc::new(ScopedTokioHandle::default()); + + SidechainOCall::new( + block_broadcaster_mock, + block_storage_mock, + worker_peers_registry_mock, + peer_block_fetcher_mock, + scoped_tokio_handle, + ) + } +} diff --git a/bitacross-worker/service/src/ocall_bridge/test/mocks/mod.rs b/bitacross-worker/service/src/ocall_bridge/test/mocks/mod.rs new file mode 100644 index 0000000000..298b05435a --- /dev/null +++ b/bitacross-worker/service/src/ocall_bridge/test/mocks/mod.rs @@ -0,0 +1,19 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pub mod sidechain_bridge_mock; diff --git a/bitacross-worker/service/src/ocall_bridge/test/mocks/sidechain_bridge_mock.rs b/bitacross-worker/service/src/ocall_bridge/test/mocks/sidechain_bridge_mock.rs new file mode 100644 index 0000000000..dc1ba7d8da --- /dev/null +++ b/bitacross-worker/service/src/ocall_bridge/test/mocks/sidechain_bridge_mock.rs @@ -0,0 +1,54 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::ocall_bridge::bridge_api::{OCallBridgeResult, SidechainBridge}; + +#[derive(Default)] +pub struct SidechainBridgeMock { + peer_blocks_encoded: Vec, +} + +impl SidechainBridgeMock { + pub fn with_peer_blocks(mut self, blocks_encoded: Vec) -> Self { + self.peer_blocks_encoded = blocks_encoded; + self + } +} + +impl SidechainBridge for SidechainBridgeMock { + fn propose_sidechain_blocks(&self, _signed_blocks_encoded: Vec) -> OCallBridgeResult<()> { + Ok(()) + } + + fn store_sidechain_blocks(&self, _signed_blocks_encoded: Vec) -> OCallBridgeResult<()> { + Ok(()) + } + + fn fetch_sidechain_blocks_from_peer( + &self, + _last_imported_block_hash_encoded: Vec, + _maybe_until_block_hash_encoded: Vec, + _shard_identifier_encoded: Vec, + ) -> OCallBridgeResult> { + Ok(self.peer_blocks_encoded.clone()) + } + + fn get_trusted_peers_urls(&self) -> OCallBridgeResult> { + Ok(vec![]) + } +} diff --git a/bitacross-worker/service/src/ocall_bridge/test/mod.rs b/bitacross-worker/service/src/ocall_bridge/test/mod.rs new file mode 100644 index 0000000000..0c205a3799 --- /dev/null +++ b/bitacross-worker/service/src/ocall_bridge/test/mod.rs @@ -0,0 +1,19 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pub mod mocks; diff --git a/bitacross-worker/service/src/ocall_bridge/worker_on_chain_ocall.rs b/bitacross-worker/service/src/ocall_bridge/worker_on_chain_ocall.rs new file mode 100644 index 0000000000..9218186ae8 --- /dev/null +++ b/bitacross-worker/service/src/ocall_bridge/worker_on_chain_ocall.rs @@ -0,0 +1,257 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::ocall_bridge::bridge_api::{OCallBridgeError, OCallBridgeResult, WorkerOnChainBridge}; +use codec::{Decode, Encode}; +use itp_api_client_types::ParentchainApi; +use itp_enclave_api::enclave_base::EnclaveBase; +use itp_node_api::{api_client::AccountApi, node_api_factory::CreateNodeApi}; +use itp_types::{parentchain::ParentchainId, WorkerRequest, WorkerResponse}; +use log::*; +use sp_runtime::OpaqueExtrinsic; +use std::{sync::Arc, thread, vec::Vec}; +use substrate_api_client::{ + ac_primitives::serde_impls::StorageKey, GetStorage, SubmitAndWatch, SubmitExtrinsic, XtStatus, +}; + +#[cfg(feature = "link-binary")] +use crate::main_impl::enclave_account; + +pub struct WorkerOnChainOCall { + enclave_api: Arc, + integritee_api_factory: Arc, + target_a_parentchain_api_factory: Option>, + target_b_parentchain_api_factory: Option>, +} + +impl WorkerOnChainOCall { + pub fn new( + enclave_api: Arc, + integritee_api_factory: Arc, + target_a_parentchain_api_factory: Option>, + target_b_parentchain_api_factory: Option>, + ) -> Self { + WorkerOnChainOCall { + enclave_api, + integritee_api_factory, + target_a_parentchain_api_factory, + target_b_parentchain_api_factory, + } + } +} + +impl WorkerOnChainOCall { + pub fn create_api(&self, parentchain_id: ParentchainId) -> OCallBridgeResult { + Ok(match parentchain_id { + ParentchainId::Litentry => self.integritee_api_factory.create_api()?, + ParentchainId::TargetA => self + .target_a_parentchain_api_factory + .as_ref() + .ok_or(OCallBridgeError::TargetAParentchainNotInitialized) + .and_then(|f| f.create_api().map_err(Into::into))?, + ParentchainId::TargetB => self + .target_b_parentchain_api_factory + .as_ref() + .ok_or(OCallBridgeError::TargetBParentchainNotInitialized) + .and_then(|f| f.create_api().map_err(Into::into))?, + }) + } +} + +impl WorkerOnChainBridge for WorkerOnChainOCall +where + E: EnclaveBase, + F: CreateNodeApi, +{ + fn worker_request( + &self, + request: Vec, + parentchain_id: Vec, + ) -> OCallBridgeResult> { + debug!(" Entering ocall_worker_request"); + + let requests: Vec = Decode::decode(&mut request.as_slice())?; + if requests.is_empty() { + debug!("requests is empty, returning empty vector"); + return Ok(Vec::::new().encode()) + } + + let parentchain_id = ParentchainId::decode(&mut parentchain_id.as_slice())?; + + let api = self.create_api(parentchain_id)?; + + let resp: Vec>> = requests + .into_iter() + .map(|req| match req { + WorkerRequest::ChainStorage(key, hash) => WorkerResponse::ChainStorage( + key.clone(), + api.get_opaque_storage_by_key(StorageKey(key.clone()), hash).unwrap(), + api.get_storage_proof_by_keys(vec![StorageKey(key)], hash).unwrap().map( + |read_proof| read_proof.proof.into_iter().map(|bytes| bytes.0).collect(), + ), + ), + WorkerRequest::ChainStorageKeys(key, hash) => { + let keys: Vec> = match api.get_keys(StorageKey(key), hash) { + Ok(Some(keys)) => keys.iter().map(String::encode).collect(), + _ => Default::default(), + }; + WorkerResponse::ChainStorageKeys(keys) + }, + }) + .collect(); + + let encoded_response: Vec = resp.encode(); + + Ok(encoded_response) + } + + fn send_to_parentchain( + &self, + extrinsics_encoded: Vec, + parentchain_id: Vec, + await_each_inlcusion: bool, + ) -> OCallBridgeResult<()> { + // TODO: improve error handling, using a mut status is not good design? + let mut status: OCallBridgeResult<()> = Ok(()); + + let extrinsics: Vec = + match Decode::decode(&mut extrinsics_encoded.as_slice()) { + Ok(calls) => calls, + Err(_) => { + status = Err(OCallBridgeError::SendExtrinsicsToParentchain( + "Could not decode extrinsics".to_string(), + )); + Default::default() + }, + }; + + if !extrinsics.is_empty() { + let parentchain_id = ParentchainId::decode(&mut parentchain_id.as_slice())?; + debug!( + "Enclave wants to send {} extrinsics to parentchain: {:?}. await each inclusion: {:?}", + extrinsics.len(), + parentchain_id, await_each_inlcusion + ); + let api = self.create_api(parentchain_id)?; + let mut send_extrinsic_failed = false; + for call in extrinsics.into_iter() { + if await_each_inlcusion { + if let Err(e) = api.submit_and_watch_opaque_extrinsic_until( + &call.encode().into(), + XtStatus::InBlock, + ) { + error!( + "Could not send extrinsic to node: {:?}, error: {:?}", + serde_json::to_string(&call), + e + ); + } + } else if let Err(e) = api.submit_opaque_extrinsic(&call.encode().into()) { + error!( + "Could not send extrinsic to node: {:?}, error: {:?}", + serde_json::to_string(&call), + e + ); + send_extrinsic_failed = true; + } + } + + // Try to reset nonce, see + // - https://github.com/litentry/litentry-parachain/issues/1036 + // - https://github.com/integritee-network/worker/issues/970 + // It has to be done in a separate thread as nested ECALL/OCALL is disallowed + // + // This workaround is likely to cause duplicate nonce or "transaction outdated" error in the parentchain + // tx pool, because the retrieved on-chain nonce doesn't count the pending tx, meanwhile the extrinsic factory + // keeps composing new extrinsics. So the nonce used for composing the new extrinsics can collide with the nonce + // in the already submitted tx. As a result, a few txs could be dropped during the parentchain tx pool processing. + // Not to mention the thread dispatch delay and network delay (query on-chain nonce). + // + // However, we still consider it better than the current situation, where the nonce never gets rectified and + // all following extrinsics will be blocked. Moreover, the txs sent to the parentchain are mostly + // "notification extrinsics" and don't cause chain state change, therefore we deem it less harmful to drop them. + // The worst case is some action is wrongly intepreted as "failed" (because F/E doesn't get the event in time) + // while it actually succeeds. In that case, the user needs to re-do the extrinsic, which is suboptimal, + // but still better than the chain stalling. + // + // To have a better synchronisation handling we probably need a sending queue in extrinsic factory that + // can be paused on demand (or wait for the nonce synchronisation). + // + // Another small thing that can be improved is to use rpc.system.accountNextIndex instead of system.account.nonce + // see https://polkadot.js.org/docs/api/cookbook/tx/#how-do-i-take-the-pending-tx-pool-into-account-in-my-nonce + #[cfg(feature = "link-binary")] + if send_extrinsic_failed { + // drop &self lifetime + let node_api_factory_cloned = self.integritee_api_factory.clone(); + let enclave_cloned = self.enclave_api.clone(); + thread::spawn(move || { + let api = node_api_factory_cloned.create_api().unwrap(); + let enclave_account = enclave_account(enclave_cloned.as_ref()); + warn!("send_extrinsic failed, try to reset nonce ..."); + match api.get_account_next_index(&enclave_account) { + Ok(nonce) => { + warn!("query on-chain nonce OK, reset nonce to: {}", nonce); + if let Err(e) = enclave_cloned.set_nonce(nonce, ParentchainId::Litentry) + { + warn!("failed to reset nonce due to: {:?}", e); + } + }, + Err(e) => warn!("query on-chain nonce failed: {:?}", e), + } + }); + } + } + status + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::tests::mocks::enclave_api_mock::EnclaveMock; + use itp_node_api::{ + api_client::ParentchainApi, + node_api_factory::{CreateNodeApi, Result as NodeApiResult}, + }; + use mockall::mock; + + #[test] + fn given_empty_worker_request_when_submitting_then_return_empty_response() { + mock! { + NodeApiFactory {} + impl CreateNodeApi for NodeApiFactory { + fn create_api(&self) -> NodeApiResult; + } + } + + let mock_enclave = Arc::new(EnclaveMock {}); + let mock_node_api_factory = Arc::new(MockNodeApiFactory::new()); + + let on_chain_ocall = + WorkerOnChainOCall::new(mock_enclave, mock_node_api_factory, None, None); + + let response = on_chain_ocall + .worker_request(Vec::::new().encode(), ParentchainId::Litentry.encode()) + .unwrap(); + + assert!(!response.is_empty()); // the encoded empty vector is not empty + let decoded_response: Vec = Decode::decode(&mut response.as_slice()).unwrap(); + assert!(decoded_response.is_empty()); // decode the response, and we get an empty vector again + } +} diff --git a/bitacross-worker/service/src/parentchain_handler.rs b/bitacross-worker/service/src/parentchain_handler.rs new file mode 100644 index 0000000000..8a58f10876 --- /dev/null +++ b/bitacross-worker/service/src/parentchain_handler.rs @@ -0,0 +1,260 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::error::{Error, ServiceResult}; +use codec::{Decode, Encode}; +use itc_parentchain::{ + light_client::light_client_init_params::{GrandpaParams, SimpleParams}, + primitives::{ParentchainId, ParentchainInitParams}, +}; +use itp_api_client_types::ParentchainApi; +use itp_enclave_api::{enclave_base::EnclaveBase, sidechain::Sidechain}; +use itp_node_api::api_client::ChainApi; +use itp_storage::StorageProof; +use litentry_primitives::ParentchainHeader as Header; +use log::*; +use sp_consensus_grandpa::VersionedAuthorityList; +use sp_runtime::traits::Header as HeaderTrait; +use std::{cmp::min, sync::Arc}; +use substrate_api_client::ac_primitives::{Block, Header as HeaderT}; + +const BLOCK_SYNC_BATCH_SIZE: u32 = 1000; + +pub trait HandleParentchain { + /// Initializes all parentchain specific components on the enclave side. + /// Returns the latest synced block header. + fn init_parentchain_components(&self) -> ServiceResult
; + + /// Fetches the parentchain blocks to sync from the parentchain and feeds them to the enclave. + /// Returns the latest synced block header. + /// + /// Litentry: `overriden_start_block` to forcibly start from the given parentchain block number + fn sync_parentchain( + &self, + last_synced_header: Header, + overriden_start_block: u32, + is_syncing: bool, + ) -> ServiceResult
; + + /// Syncs and directly imports parentchain blocks from the latest synced header + /// until the specified until_header. + /// + /// Litentry: `overriden_start_block` to forcibly start from the given parentchain block number + fn sync_and_import_parentchain_until( + &self, + last_synced_header: &Header, + until_header: &Header, + overriden_start_block: u32, + ) -> ServiceResult
; +} + +/// Handles the interaction between parentchain and enclave. +pub(crate) struct ParentchainHandler { + parentchain_api: ParentchainApi, + enclave_api: Arc, + parentchain_init_params: ParentchainInitParams, +} + +// #TODO: #1451: Reintroduce `ParentchainApi: ChainApi` once there is no trait bound conflict +// any more with the api-clients own trait definitions. +impl ParentchainHandler +where + EnclaveApi: EnclaveBase, +{ + pub fn new( + parentchain_api: ParentchainApi, + enclave_api: Arc, + parentchain_init_params: ParentchainInitParams, + ) -> Self { + Self { parentchain_api, enclave_api, parentchain_init_params } + } + + // FIXME: Necessary in the future? Fix with #1080 + pub fn new_with_automatic_light_client_allocation( + parentchain_api: ParentchainApi, + enclave_api: Arc, + id: ParentchainId, + ) -> ServiceResult { + let genesis_hash = parentchain_api.get_genesis_hash()?; + let genesis_header = + parentchain_api.header(Some(genesis_hash))?.ok_or(Error::MissingGenesisHeader)?; + + let parentchain_init_params: ParentchainInitParams = if parentchain_api + .is_grandpa_available()? + { + let grandpas = parentchain_api.grandpa_authorities(Some(genesis_hash))?; + let grandpa_proof = parentchain_api.grandpa_authorities_proof(Some(genesis_hash))?; + + debug!("[{:?}] Grandpa Authority List: \n {:?} \n ", id, grandpas); + + let authority_list = VersionedAuthorityList::from(grandpas); + + ( + id, + GrandpaParams::new( + // #TODO: #1451: clean up type hacks + Header::decode(&mut genesis_header.encode().as_slice())?, + authority_list.into(), + grandpa_proof, + ), + ) + .into() + } else { + ( + id, + SimpleParams::new( + // #TODO: #1451: clean up type hacks + Header::decode(&mut genesis_header.encode().as_slice())?, + ), + ) + .into() + }; + + Ok(Self::new(parentchain_api, enclave_api, parentchain_init_params)) + } + + pub fn parentchain_api(&self) -> &ParentchainApi { + &self.parentchain_api + } + + pub fn parentchain_id(&self) -> &ParentchainId { + self.parentchain_init_params.id() + } +} + +impl HandleParentchain for ParentchainHandler +where + EnclaveApi: Sidechain + EnclaveBase, +{ + fn init_parentchain_components(&self) -> ServiceResult
{ + Ok(self + .enclave_api + .init_parentchain_components(self.parentchain_init_params.clone())?) + } + + fn sync_parentchain( + &self, + last_synced_header: Header, + overriden_start_block: u32, + is_syncing: bool, + ) -> ServiceResult
{ + let id = self.parentchain_id(); + trace!("[{:?}] Getting current head", id); + let curr_block = self + .parentchain_api + .last_finalized_block()? + .ok_or(Error::MissingLastFinalizedBlock)?; + let curr_block_number = curr_block.block.header().number(); + + info!( + "[{:?}] Syncing blocks from {} to {}", + id, last_synced_header.number, curr_block_number + ); + + let mut until_synced_header = last_synced_header; + let mut start_block = until_synced_header.number + 1; + if overriden_start_block > start_block { + start_block = overriden_start_block; + // ask the enclave to ignore the parentchain block import validation until `overriden_start_block` + // TODO: maybe ignoring the next block import is enough, since the given `overriden_start_block` + // should be the very first parentchain block to be imported + self.enclave_api + .ignore_parentchain_block_import_validation_until(overriden_start_block)?; + } + + loop { + let block_chunk_to_sync = self.parentchain_api.get_blocks( + start_block, + min(start_block + BLOCK_SYNC_BATCH_SIZE, curr_block_number), + )?; + info!("[{:?}] Found {} block(s) to sync", id, block_chunk_to_sync.len()); + if block_chunk_to_sync.is_empty() { + return Ok(until_synced_header) + } + + let events_chunk_to_sync: Vec> = block_chunk_to_sync + .iter() + .map(|block| { + self.parentchain_api.get_events_for_block(Some(block.block.header.hash())) + }) + .collect::, _>>()?; + + info!("[{:?}] Found {} event vector(s) to sync", id, events_chunk_to_sync.len()); + + let events_proofs_chunk_to_sync: Vec = block_chunk_to_sync + .iter() + .map(|block| { + self.parentchain_api.get_events_value_proof(Some(block.block.header.hash())) + }) + .collect::, _>>()?; + + self.enclave_api.sync_parentchain( + block_chunk_to_sync.as_slice(), + events_chunk_to_sync.as_slice(), + events_proofs_chunk_to_sync.as_slice(), + self.parentchain_id(), + is_syncing, + )?; + + let api_client_until_synced_header = block_chunk_to_sync + .last() + .map(|b| b.block.header.clone()) + .ok_or(Error::EmptyChunk)?; + info!( + "[{:?}] Synced {} out of {} finalized parentchain blocks", + id, until_synced_header.number, curr_block_number, + ); + + // #TODO: #1451: fix api/client types + until_synced_header = + Header::decode(&mut api_client_until_synced_header.encode().as_slice()) + .expect("Can decode previously encoded header; qed"); + + start_block = until_synced_header.number + 1; + println!( + "[{:?}] Synced {} out of {} finalized parentchain blocks", + id, until_synced_header.number, curr_block_number, + ); + } + } + + fn sync_and_import_parentchain_until( + &self, + last_synced_header: &Header, + until_header: &Header, + overriden_start_block: u32, + ) -> ServiceResult
{ + let id = self.parentchain_id(); + + trace!( + "[{:?}] last synced block number: {}. synching until {}", + id, + last_synced_header.number, + until_header.number + ); + let mut last_synced_header = last_synced_header.clone(); + + while last_synced_header.number() < until_header.number() { + last_synced_header = + self.sync_parentchain(last_synced_header, overriden_start_block, true)?; + println!("[{:?}] synced block number: #{}", id, last_synced_header.number); + std::thread::sleep(std::time::Duration::from_secs(1)); + } + Ok(last_synced_header) + } +} diff --git a/bitacross-worker/service/src/prometheus_metrics.rs b/bitacross-worker/service/src/prometheus_metrics.rs new file mode 100644 index 0000000000..64a3615135 --- /dev/null +++ b/bitacross-worker/service/src/prometheus_metrics.rs @@ -0,0 +1,300 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Service for prometheus metrics, hosted on a http server. + +#[cfg(feature = "teeracle")] +use crate::teeracle::teeracle_metrics::update_teeracle_metrics; + +use crate::{ + account_funding::EnclaveAccountInfo, + error::{Error, ServiceResult}, +}; +use async_trait::async_trait; +use codec::{Decode, Encode}; +#[cfg(feature = "attesteer")] +use core::time::Duration; +use frame_support::scale_info::TypeInfo; +#[cfg(feature = "dcap")] +use itc_rest_client::{ + http_client::{DefaultSend, HttpClient}, + rest_client::{RestClient, Url as URL}, + RestGet, RestPath, +}; +use itp_enclave_metrics::EnclaveMetric; +use lazy_static::lazy_static; +use litentry_primitives::{Assertion, Identity}; +use log::*; +use prometheus::{ + proto::MetricFamily, register_counter_vec, register_histogram, register_histogram_vec, + register_int_gauge, register_int_gauge_vec, CounterVec, Histogram, HistogramVec, IntGauge, + IntGaugeVec, +}; +use serde::{Deserialize, Serialize}; +use std::{net::SocketAddr, sync::Arc}; +use warp::{Filter, Rejection, Reply}; + +lazy_static! { + /// Register all the prometheus metrics we want to monitor (aside from the default process ones). + + static ref ENCLAVE_ACCOUNT_FREE_BALANCE: IntGauge = + register_int_gauge!("litentry_worker_enclave_account_free_balance", "Free balance of the enclave account") + .unwrap(); + static ref ENCLAVE_SIDECHAIN_BLOCK_HEIGHT: IntGauge = + register_int_gauge!("litentry_worker_enclave_sidechain_block_height", "Enclave sidechain block height") + .unwrap(); + static ref ENCLAVE_SIDECHAIN_TOP_POOL_SIZE: IntGauge = + register_int_gauge!("litentry_worker_enclave_sidechain_top_pool_size", "Enclave sidechain top pool size") + .unwrap(); + static ref ENCLAVE_STF_TASKS: IntGaugeVec = + register_int_gauge_vec!("litentry_worker_enclave_stf_total_tasks", "Litentry Stf Tasks", &["request_type", "variant"]) + .unwrap(); + static ref ENCLAVE_STF_TASKS_EXECUTION: HistogramVec = + register_histogram_vec!("litentry_worker_enclave_stf_tasks_execution_times", "Litentry Stf Tasks Exeuction Time", &["request_type", "variant"]) + .unwrap(); + static ref ENCLAVE_SUCCESSFUL_TRUSTED_OPERATION: CounterVec = + register_counter_vec!("litentry_worker_enclave_successful_trusted_operation", "Litentry Successful Trusted Operation", &["call"]) + .unwrap(); + static ref ENCLAVE_FAILED_TRUSTED_OPERATION: CounterVec = + register_counter_vec!("litentry_worker_enclave_failed_trusted_operation", "Litentry Failed Trusted Operation", &["call"]) + .unwrap(); + static ref ENCLAVE_PARENTCHAIN_BLOCK_IMPORT_TIME: Histogram = + register_histogram!("litentry_worker_enclave_parentchain_block_import_time", "Time taken to import parentchain block") + .unwrap(); + static ref ENCLAVE_SIDECHAIN_BLOCK_IMPORT_TIME: Histogram = + register_histogram!("litentry_worker_enclave_sidechain_block_import_time", "Time taken to import sidechain block") + .unwrap(); + static ref ENCLAVE_SIDECHAIN_SLOT_PREPARE_TIME: Histogram = + register_histogram!("litentry_worker_enclave_sidechain_slot_prepare_time", "Time taken to prepare sidechain extrinsics for execution") + .unwrap(); + static ref ENCLAVE_SIDECHAIN_SLOT_STF_EXECUTION_TIME: Histogram = + register_histogram!("litentry_worker_enclave_sidechain_slot_stf_execution_time", "Time taken to execute sidechain extrinsics") + .unwrap(); + static ref ENCLAVE_SIDECHAIN_SLOT_BLOCK_COMPOSITION_TIME: Histogram = + register_histogram!("litentry_worker_enclave_sidechain_slot_block_composition_time", "Time taken to compose sidechain block") + .unwrap(); + static ref ENCLAVE_SIDECHAIN_BLOCK_BROADCASTING_TIME: Histogram = + register_histogram!("litentry_worker_enclave_sidechain_block_broadcasting_time", "Time taken to broadcast sidechain block") + .unwrap(); + +} + +pub async fn start_metrics_server( + metrics_handler: Arc, + port: u16, +) -> ServiceResult<()> +where + MetricsHandler: HandleMetrics + Send + Sync + 'static, +{ + let metrics_route = warp::path!("metrics").and_then(move || { + let handler_clone = metrics_handler.clone(); + async move { handler_clone.handle_metrics().await } + }); + let socket_addr: SocketAddr = ([0, 0, 0, 0], port).into(); + + info!("Running prometheus metrics server on: {:?}", socket_addr); + warp::serve(metrics_route).run(socket_addr).await; + + info!("Prometheus metrics server shut down"); + Ok(()) +} + +#[async_trait] +pub trait HandleMetrics { + type ReplyType: Reply; + + async fn handle_metrics(&self) -> Result; +} + +/// Metrics handler implementation. +pub struct MetricsHandler { + enclave_wallet: Arc, +} + +#[async_trait] +impl HandleMetrics for MetricsHandler +where + Wallet: EnclaveAccountInfo + Send + Sync, +{ + type ReplyType = String; + + async fn handle_metrics(&self) -> Result { + self.update_metrics().await; + + let default_metrics = match gather_metrics_into_reply(&prometheus::gather()) { + Ok(r) => r, + Err(e) => { + error!("Failed to gather prometheus metrics: {:?}", e); + String::default() + }, + }; + + Ok(default_metrics) + } +} + +impl MetricsHandler +where + Wallet: EnclaveAccountInfo + Send + Sync, +{ + pub fn new(enclave_wallet: Arc) -> Self { + MetricsHandler { enclave_wallet } + } + + async fn update_metrics(&self) { + match self.enclave_wallet.free_balance() { + Ok(b) => { + ENCLAVE_ACCOUNT_FREE_BALANCE.set(b as i64); + }, + Err(e) => { + error!("Failed to fetch free balance metric, value will not be updated: {:?}", e); + }, + } + } +} + +fn gather_metrics_into_reply(metrics: &[MetricFamily]) -> ServiceResult { + use prometheus::Encoder; + let encoder = prometheus::TextEncoder::new(); + + let mut buffer = Vec::new(); + encoder.encode(metrics, &mut buffer).map_err(|e| { + Error::Custom(format!("Failed to encode prometheus metrics: {:?}", e).into()) + })?; + + let result_string = String::from_utf8(buffer).map_err(|e| { + Error::Custom( + format!("Failed to convert Prometheus encoded metrics to UTF8: {:?}", e).into(), + ) + })?; + + Ok(result_string) +} + +/// Trait to receive metric updates from inside the enclave. +pub trait ReceiveEnclaveMetrics { + fn receive_enclave_metric(&self, metric: EnclaveMetric) -> ServiceResult<()>; +} + +pub struct EnclaveMetricsReceiver; + +impl ReceiveEnclaveMetrics for EnclaveMetricsReceiver { + fn receive_enclave_metric(&self, metric: EnclaveMetric) -> ServiceResult<()> { + match metric { + EnclaveMetric::SetSidechainBlockHeight(h) => { + ENCLAVE_SIDECHAIN_BLOCK_HEIGHT.set(h as i64); + }, + EnclaveMetric::TopPoolSizeSet(pool_size) => { + ENCLAVE_SIDECHAIN_TOP_POOL_SIZE.set(pool_size as i64); + }, + EnclaveMetric::TopPoolSizeIncrement => { + ENCLAVE_SIDECHAIN_TOP_POOL_SIZE.inc(); + }, + EnclaveMetric::TopPoolSizeDecrement => { + ENCLAVE_SIDECHAIN_TOP_POOL_SIZE.dec(); + }, + EnclaveMetric::SuccessfulTrustedOperationIncrement(metric_name) => { + ENCLAVE_SUCCESSFUL_TRUSTED_OPERATION.with_label_values(&[&metric_name]).inc(); + }, + EnclaveMetric::FailedTrustedOperationIncrement(metric_name) => { + ENCLAVE_FAILED_TRUSTED_OPERATION.with_label_values(&[&metric_name]).inc(); + }, + EnclaveMetric::ParentchainBlockImportTime(time) => + ENCLAVE_PARENTCHAIN_BLOCK_IMPORT_TIME.observe(time.as_secs_f64()), + EnclaveMetric::SidechainBlockImportTime(time) => + ENCLAVE_SIDECHAIN_BLOCK_IMPORT_TIME.observe(time.as_secs_f64()), + EnclaveMetric::SidechainSlotPrepareTime(time) => + ENCLAVE_SIDECHAIN_SLOT_PREPARE_TIME.observe(time.as_secs_f64()), + EnclaveMetric::SidechainSlotStfExecutionTime(time) => + ENCLAVE_SIDECHAIN_SLOT_STF_EXECUTION_TIME.observe(time.as_secs_f64()), + EnclaveMetric::SidechainSlotBlockCompositionTime(time) => + ENCLAVE_SIDECHAIN_SLOT_BLOCK_COMPOSITION_TIME.observe(time.as_secs_f64()), + EnclaveMetric::SidechainBlockBroadcastingTime(time) => + ENCLAVE_SIDECHAIN_BLOCK_BROADCASTING_TIME.observe(time.as_secs_f64()), + #[cfg(feature = "teeracle")] + EnclaveMetric::ExchangeRateOracle(m) => update_teeracle_metrics(m)?, + #[cfg(not(feature = "teeracle"))] + EnclaveMetric::ExchangeRateOracle(_) => { + error!("Received Teeracle metric, but Teeracle feature is not enabled, ignoring metric item.") + }, + } + Ok(()) + } +} + +// Function to increment STF calls with labels +fn inc_stf_calls(category: &str, label: &str) { + ENCLAVE_STF_TASKS.with_label_values(&[category, label]).inc(); +} + +// Function to observe STF call execution time with labels +fn observe_execution_time(category: &str, label: &str, time: f64) { + ENCLAVE_STF_TASKS_EXECUTION.with_label_values(&[category, label]).observe(time); +} + +#[derive(Serialize, Deserialize, Debug)] +struct PrometheusMarblerunEvents(pub Vec); + +#[cfg(feature = "attesteer")] +impl RestPath<&str> for PrometheusMarblerunEvents { + fn get_path(path: &str) -> Result { + Ok(format!("{}", path)) + } +} + +#[cfg(feature = "attesteer")] +pub fn fetch_marblerun_events(base_url: &str) -> Result, Error> { + let base_url = URL::parse(&base_url).map_err(|e| { + Error::Custom( + format!("Failed to parse marblerun prometheus endpoint base URL: {:?}", e).into(), + ) + })?; + let timeout = 3u64; + let http_client = + HttpClient::new(DefaultSend {}, true, Some(Duration::from_secs(timeout)), None, None); + + let mut rest_client = RestClient::new(http_client, base_url.clone()); + let events: PrometheusMarblerunEvents = rest_client.get("events").map_err(|e| { + Error::Custom( + format!("Failed to fetch marblerun prometheus events from: {}, error: {}", base_url, e) + .into(), + ) + })?; + + Ok(events.0) +} + +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Encode, Decode, TypeInfo)] +pub struct PrometheusMarblerunEvent { + pub time: String, + pub activation: PrometheusMarblerunEventActivation, +} + +#[cfg(feature = "attesteer")] +impl PrometheusMarblerunEvent { + pub fn get_quote_without_prepended_bytes(&self) -> &[u8] { + let marblerun_magic_prepended_header_size = 16usize; + &self.activation.quote.as_bytes()[marblerun_magic_prepended_header_size..] + } +} +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Encode, Decode, TypeInfo)] +#[serde(rename_all = "camelCase")] +pub struct PrometheusMarblerunEventActivation { + pub marble_type: String, + pub uuid: String, + pub quote: String, +} diff --git a/bitacross-worker/service/src/setup.rs b/bitacross-worker/service/src/setup.rs new file mode 100644 index 0000000000..4bd056edd8 --- /dev/null +++ b/bitacross-worker/service/src/setup.rs @@ -0,0 +1,242 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::error::{Error, ServiceResult}; +use itp_settings::files::{ + LITENTRY_PARENTCHAIN_LIGHT_CLIENT_DB_PATH, SCHEDULED_ENCLAVE_FILE, SHARDS_PATH, + SIDECHAIN_STORAGE_PATH, TARGET_A_PARENTCHAIN_LIGHT_CLIENT_DB_PATH, + TARGET_B_PARENTCHAIN_LIGHT_CLIENT_DB_PATH, +}; +use std::{fs, path::Path}; + +#[cfg(feature = "link-binary")] +pub(crate) use needs_enclave::{ + generate_shielding_key_file, generate_signing_key_file, init_shard, initialize_shard_and_keys, + migrate_shard, +}; + +#[cfg(feature = "link-binary")] +mod needs_enclave { + use crate::error::{Error, ServiceResult}; + use codec::Encode; + use itp_enclave_api::{enclave_base::EnclaveBase, Enclave}; + use itp_settings::files::{ + LITENTRY_PARENTCHAIN_LIGHT_CLIENT_DB_PATH, SHARDS_PATH, SHIELDING_KEY_FILE, + SIDECHAIN_STORAGE_PATH, SIGNING_KEY_FILE, TARGET_A_PARENTCHAIN_LIGHT_CLIENT_DB_PATH, + TARGET_B_PARENTCHAIN_LIGHT_CLIENT_DB_PATH, + }; + use itp_types::ShardIdentifier; + use log::*; + use std::{fs, fs::File, path::Path}; + + /// Initializes the shard and generates the key files. + pub(crate) fn initialize_shard_and_keys( + enclave: &Enclave, + shard_identifier: &ShardIdentifier, + ) -> ServiceResult<()> { + println!("[+] Initialize the shard"); + init_shard(enclave, shard_identifier); + + println!("[+] Generate key files"); + generate_signing_key_file(enclave); + generate_shielding_key_file(enclave); + + Ok(()) + } + + pub(crate) fn init_shard(enclave: &Enclave, shard_identifier: &ShardIdentifier) { + use base58::ToBase58; + + match enclave.init_shard(shard_identifier.encode()) { + Err(e) => { + println!( + "Failed to initialize shard {:?}: {:?}", + shard_identifier.0.to_base58(), + e + ); + }, + Ok(_) => { + println!("Successfully initialized shard {:?}", shard_identifier.0.to_base58()); + }, + } + } + + pub(crate) fn migrate_shard( + enclave: &Enclave, + old_shard: &ShardIdentifier, + new_shard: &ShardIdentifier, + ) { + match enclave.migrate_shard(old_shard.encode(), new_shard.encode()) { + Err(e) => { + println!( + "Failed to migrate old shard {:?} to new shard{:?}. {:?}", + old_shard, new_shard, e + ); + }, + Ok(_) => { + println!( + "Successfully migrate old shard {:?} to new shard{:?}", + old_shard, new_shard + ); + }, + } + } + + pub(crate) fn generate_signing_key_file(enclave: &Enclave) { + info!("*** Get the signing key from the TEE\n"); + let pubkey = enclave.get_ecc_signing_pubkey().unwrap(); + debug!("[+] Signing key raw: {:?}", pubkey); + match fs::write(SIGNING_KEY_FILE, pubkey) { + Err(x) => { + error!("[-] Failed to write '{}'. {}", SIGNING_KEY_FILE, x); + }, + _ => { + println!("[+] File '{}' written successfully", SIGNING_KEY_FILE); + }, + } + } + + pub(crate) fn generate_shielding_key_file(enclave: &Enclave) { + info!("*** Get the public key from the TEE\n"); + let pubkey = enclave.get_rsa_shielding_pubkey().unwrap(); + let file = File::create(SHIELDING_KEY_FILE).unwrap(); + match serde_json::to_writer(file, &pubkey) { + Err(x) => { + error!("[-] Failed to write '{}'. {}", SHIELDING_KEY_FILE, x); + }, + _ => { + println!("[+] File '{}' written successfully", SHIELDING_KEY_FILE); + }, + } + } +} + +/// Purge all worker files from `dir`. +pub(crate) fn purge_files_from_dir(dir: &Path) -> ServiceResult<()> { + println!("[+] Performing a clean reset of the worker"); + + println!("[+] Purge all files from previous runs"); + purge_files(dir)?; + + Ok(()) +} + +/// Purge all worker files in a given path. +fn purge_files(root_directory: &Path) -> ServiceResult<()> { + remove_dir_if_it_exists(root_directory, SHARDS_PATH)?; + remove_dir_if_it_exists(root_directory, SIDECHAIN_STORAGE_PATH)?; + + remove_dir_if_it_exists(root_directory, LITENTRY_PARENTCHAIN_LIGHT_CLIENT_DB_PATH)?; + remove_dir_if_it_exists(root_directory, TARGET_A_PARENTCHAIN_LIGHT_CLIENT_DB_PATH)?; + remove_dir_if_it_exists(root_directory, TARGET_B_PARENTCHAIN_LIGHT_CLIENT_DB_PATH)?; + + remove_file_if_it_exists(root_directory, SCHEDULED_ENCLAVE_FILE)?; + Ok(()) +} + +fn remove_dir_if_it_exists(root_directory: &Path, dir_name: &str) -> ServiceResult<()> { + let directory_path = root_directory.join(dir_name); + if directory_path.exists() { + fs::remove_dir_all(directory_path).map_err(|e| Error::Custom(e.into()))?; + } + Ok(()) +} + +fn remove_file_if_it_exists(root_directory: &Path, file_name: &str) -> ServiceResult<()> { + let file = root_directory.join(file_name); + if file.exists() { + fs::remove_file(file).map_err(|e| Error::Custom(e.into()))?; + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use itp_settings::files::{SHARDS_PATH, TARGET_A_PARENTCHAIN_LIGHT_CLIENT_DB_PATH}; + use std::{fs, path::PathBuf}; + + #[test] + fn purge_files_deletes_all_relevant_files() { + let test_directory_handle = + TestDirectoryHandle::new(PathBuf::from("test_purge_files_deletes_all_relevant_files")); + let root_directory = test_directory_handle.path(); + + let shards_path = root_directory.join(SHARDS_PATH); + fs::create_dir_all(&shards_path).unwrap(); + fs::File::create(&shards_path.join("state_1.bin")).unwrap(); + fs::File::create(&shards_path.join("state_2.bin")).unwrap(); + + let sidechain_db_path = root_directory.join(SIDECHAIN_STORAGE_PATH); + fs::create_dir_all(&sidechain_db_path).unwrap(); + fs::File::create(&sidechain_db_path.join("sidechain_db_1.bin")).unwrap(); + fs::File::create(&sidechain_db_path.join("sidechain_db_2.bin")).unwrap(); + fs::File::create(&sidechain_db_path.join("sidechain_db_3.bin")).unwrap(); + + fs::create_dir_all(&root_directory.join(LITENTRY_PARENTCHAIN_LIGHT_CLIENT_DB_PATH)) + .unwrap(); + fs::create_dir_all(&root_directory.join(TARGET_A_PARENTCHAIN_LIGHT_CLIENT_DB_PATH)) + .unwrap(); + fs::create_dir_all(&root_directory.join(TARGET_B_PARENTCHAIN_LIGHT_CLIENT_DB_PATH)) + .unwrap(); + + purge_files(&root_directory).unwrap(); + + assert!(!shards_path.exists()); + assert!(!sidechain_db_path.exists()); + assert!(!root_directory.join(LITENTRY_PARENTCHAIN_LIGHT_CLIENT_DB_PATH).exists()); + assert!(!root_directory.join(TARGET_A_PARENTCHAIN_LIGHT_CLIENT_DB_PATH).exists()); + assert!(!root_directory.join(TARGET_B_PARENTCHAIN_LIGHT_CLIENT_DB_PATH).exists()); + } + + #[test] + fn purge_files_succeeds_when_no_files_exist() { + let test_directory_handle = TestDirectoryHandle::new(PathBuf::from( + "test_purge_files_succeeds_when_no_files_exist", + )); + let root_directory = test_directory_handle.path(); + + assert!(purge_files(&root_directory).is_ok()); + } + + /// Directory handle to automatically initialize a directory + /// and upon dropping the reference, removing it again. + struct TestDirectoryHandle { + path: PathBuf, + } + + impl TestDirectoryHandle { + pub fn new(path: PathBuf) -> Self { + let test_path = std::env::current_dir().unwrap().join(&path); + fs::create_dir_all(&test_path).unwrap(); + TestDirectoryHandle { path: test_path } + } + + pub fn path(&self) -> &PathBuf { + &self.path + } + } + + impl Drop for TestDirectoryHandle { + fn drop(&mut self) { + if self.path.exists() { + fs::remove_dir_all(&self.path).unwrap(); + } + } + } +} diff --git a/bitacross-worker/service/src/sidechain_setup.rs b/bitacross-worker/service/src/sidechain_setup.rs new file mode 100644 index 0000000000..a499c85fed --- /dev/null +++ b/bitacross-worker/service/src/sidechain_setup.rs @@ -0,0 +1,129 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + config::Config, + error::{Error, ServiceResult}, + parentchain_handler::HandleParentchain, +}; +use futures::executor::block_on; +use itp_enclave_api::{ + direct_request::DirectRequest, enclave_base::EnclaveBase, sidechain::Sidechain, +}; +use itp_settings::{ + files::{SIDECHAIN_PURGE_INTERVAL, SIDECHAIN_PURGE_LIMIT}, + sidechain::SLOT_DURATION, +}; +use itp_types::Header; +use its_consensus_slots::start_slot_worker; +use its_primitives::types::block::SignedBlock as SignedSidechainBlock; +use its_storage::{interface::FetchBlocks, start_sidechain_pruning_loop, BlockPruner}; +use log::*; +use std::{sync::Arc, thread}; +use tokio::runtime::Handle; + +pub(crate) fn sidechain_start_untrusted_rpc_server( + config: &Config, + enclave: Arc, + sidechain_storage: Arc, + tokio_handle: &Handle, +) where + Enclave: DirectRequest + Clone, + SidechainStorage: BlockPruner + FetchBlocks + Sync + Send + 'static, +{ + let untrusted_url = config.untrusted_worker_url(); + println!("[+] Untrusted RPC server listening on {}", &untrusted_url); + let _untrusted_rpc_join_handle = tokio_handle.spawn(async move { + itc_rpc_server::run_server(&untrusted_url, enclave, sidechain_storage) + .await + .unwrap(); + }); +} + +#[allow(clippy::too_many_arguments)] +pub(crate) fn sidechain_init_block_production( + enclave: Arc, + register_enclave_xt_header: Option
, + we_are_primary_validateer: bool, + parentchain_handler: Arc, + sidechain_storage: Arc, + last_synced_header: &Header, + overriden_start_block: u32, + fail_mode: Option, + fail_at: u64, +) -> ServiceResult
+where + Enclave: EnclaveBase + Sidechain, + SidechainStorage: BlockPruner + FetchBlocks + Sync + Send + 'static, + ParentchainHandler: HandleParentchain, +{ + // If we're the first validateer to register, also trigger parentchain block import. + let mut updated_header: Option
= None; + + if we_are_primary_validateer { + info!( + "We're the first validateer to be registered, syncing parentchain blocks until the one we have registered ourselves on." + ); + updated_header = Some(parentchain_handler.sync_and_import_parentchain_until( + last_synced_header, + ®ister_enclave_xt_header.unwrap(), + overriden_start_block, + )?); + } + + // ------------------------------------------------------------------------ + // Initialize sidechain components (has to be AFTER init_parentchain_components() + enclave.init_enclave_sidechain_components(fail_mode, fail_at).unwrap(); + + // ------------------------------------------------------------------------ + // Start interval sidechain block production (execution of trusted calls, sidechain block production). + let sidechain_enclave_api = enclave; + println!("[+] Spawning thread for sidechain block production"); + thread::Builder::new() + .name("interval_block_production_timer".to_owned()) + .spawn(move || { + let future = start_slot_worker( + || execute_trusted_calls(sidechain_enclave_api.as_ref()), + SLOT_DURATION, + ); + block_on(future); + println!("[!] Sidechain block production loop has terminated"); + }) + .map_err(|e| Error::Custom(Box::new(e)))?; + + // ------------------------------------------------------------------------ + // start sidechain pruning loop + thread::Builder::new() + .name("sidechain_pruning_loop".to_owned()) + .spawn(move || { + start_sidechain_pruning_loop( + &sidechain_storage, + SIDECHAIN_PURGE_INTERVAL, + SIDECHAIN_PURGE_LIMIT, + ); + }) + .map_err(|e| Error::Custom(Box::new(e)))?; + + Ok(updated_header.unwrap_or_else(|| last_synced_header.clone())) +} + +/// Execute trusted operations in the enclave. +fn execute_trusted_calls(enclave_api: &E) { + if let Err(e) = enclave_api.execute_trusted_calls() { + error!("{:?}", e); + }; +} diff --git a/bitacross-worker/service/src/sync_block_broadcaster.rs b/bitacross-worker/service/src/sync_block_broadcaster.rs new file mode 100644 index 0000000000..b0752c900d --- /dev/null +++ b/bitacross-worker/service/src/sync_block_broadcaster.rs @@ -0,0 +1,57 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(test)] +use mockall::predicate::*; +#[cfg(test)] +use mockall::*; + +use crate::{ + globals::tokio_handle::GetTokioHandle, + worker::{AsyncBlockBroadcaster, WorkerResult}, +}; +use its_primitives::types::block::SignedBlock as SignedSidechainBlock; +use std::sync::Arc; + +/// Allows to broadcast blocks, does it in a synchronous (i.e. blocking) manner +#[cfg_attr(test, automock)] +pub trait BroadcastBlocks { + fn broadcast_blocks(&self, blocks: Vec) -> WorkerResult<()>; +} + +pub struct SyncBlockBroadcaster { + tokio_handle: Arc, + worker: Arc, +} + +impl SyncBlockBroadcaster { + pub fn new(tokio_handle: Arc, worker: Arc) -> Self { + SyncBlockBroadcaster { tokio_handle, worker } + } +} + +impl BroadcastBlocks for SyncBlockBroadcaster +where + T: GetTokioHandle, + W: AsyncBlockBroadcaster, +{ + fn broadcast_blocks(&self, blocks: Vec) -> WorkerResult<()> { + let handle = self.tokio_handle.get_handle(); + handle.block_on(self.worker.broadcast_blocks(blocks)) + } +} diff --git a/bitacross-worker/service/src/sync_state.rs b/bitacross-worker/service/src/sync_state.rs new file mode 100644 index 0000000000..21d2d4d7e0 --- /dev/null +++ b/bitacross-worker/service/src/sync_state.rs @@ -0,0 +1,99 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +//! Request state keys from a fellow validateer. + +use crate::{ + enclave::tls_ra::enclave_request_state_provisioning, + error::{Error, ServiceResult as Result}, +}; +use futures::executor; +use itc_rpc_client::direct_client::{DirectApi, DirectClient as DirectWorkerApi}; +use itp_enclave_api::{ + enclave_base::EnclaveBase, + remote_attestation::{RemoteAttestation, TlsRemoteAttestation}, +}; +use itp_node_api::api_client::PalletTeerexApi; +use itp_settings::worker_mode::{ProvideWorkerMode, WorkerMode}; +use itp_types::ShardIdentifier; +use sgx_types::sgx_quote_sign_type_t; +use std::string::String; + +pub(crate) fn sync_state< + E: TlsRemoteAttestation + EnclaveBase + RemoteAttestation, + NodeApi: PalletTeerexApi, + WorkerModeProvider: ProvideWorkerMode, +>( + node_api: &NodeApi, + shard: &ShardIdentifier, + enclave_api: &E, + skip_ra: bool, +) { + // FIXME: we now assume that keys are equal for all shards. + let provider_url = match WorkerModeProvider::worker_mode() { + WorkerMode::Sidechain => + executor::block_on(get_author_url_of_last_finalized_sidechain_block(node_api, shard)) + .expect("Author of last finalized sidechain block could not be found"), + _ => executor::block_on(get_enclave_url_of_first_registered(node_api, enclave_api)) + .expect("Author of last finalized sidechain block could not be found"), + }; + + println!("Requesting state provisioning from worker at {}", &provider_url); + + enclave_request_state_provisioning( + enclave_api, + sgx_quote_sign_type_t::SGX_UNLINKABLE_SIGNATURE, + &provider_url, + shard, + skip_ra, + ) + .unwrap(); + println!("[+] State provisioning successfully performed."); +} + +/// Returns the url of the last sidechain block author that has been stored +/// in the parentchain state as "worker for shard". +/// +/// Note: The sidechainblock author will only change whenever a new parentchain block is +/// produced. And even then, it might be the same as the last block. So if several workers +/// are started in a timely manner, they will all get the same url. +async fn get_author_url_of_last_finalized_sidechain_block( + node_api: &NodeApi, + shard: &ShardIdentifier, +) -> Result { + let enclave = node_api + .worker_for_shard(shard, None)? + .ok_or_else(|| Error::NoWorkerForShardFound(*shard))?; + let worker_api_direct = DirectWorkerApi::new(enclave.url); + Ok(worker_api_direct.get_mu_ra_url()?) +} + +/// Returns the url of the first Enclave that matches our own MRENCLAVE. +/// +/// This should be run before we register ourselves as enclave, to ensure we don't get our own url. +async fn get_enclave_url_of_first_registered( + node_api: &NodeApi, + enclave_api: &EnclaveApi, +) -> Result { + let self_mr_enclave = enclave_api.get_fingerprint()?; + let first_enclave = node_api + .all_enclaves(None)? + .into_iter() + .find(|e| e.mr_enclave == self_mr_enclave.to_fixed_bytes()) + .ok_or(Error::NoPeerWorkerFound)?; + let worker_api_direct = DirectWorkerApi::new(first_enclave.url); + Ok(worker_api_direct.get_mu_ra_url()?) +} diff --git a/bitacross-worker/service/src/teeracle/mod.rs b/bitacross-worker/service/src/teeracle/mod.rs new file mode 100644 index 0000000000..420a175b26 --- /dev/null +++ b/bitacross-worker/service/src/teeracle/mod.rs @@ -0,0 +1,142 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{error::ServiceResult, teeracle::schedule_periodic::schedule_periodic}; +use codec::{Decode, Encode}; +use itp_enclave_api::teeracle_api::TeeracleApi; +use itp_node_api::api_client::ParentchainApi; +use itp_types::parentchain::Hash; +use itp_utils::hex::hex_encode; +use log::*; +use sp_runtime::OpaqueExtrinsic; +use std::time::Duration; +use substrate_api_client::{SubmitAndWatch, XtStatus}; +use teeracle_metrics::{increment_number_of_request_failures, set_extrinsics_inclusion_success}; +use tokio::runtime::Handle; + +pub(crate) mod schedule_periodic; +pub(crate) mod teeracle_metrics; + +/// Schedule periodic reregistration of the enclave. +/// +/// The `send_register_xt` needs to create a fresh registration extrinsic every time it is called +/// (updated nonce, fresh IAS-RA or DCAP-Quote). +/// +/// Currently, this is only used for the teeracle, but could also be used for other flavors in the +/// future. +pub(crate) fn schedule_periodic_reregistration_thread( + send_register_xt: impl Fn() -> Option + std::marker::Send + 'static, + period: Duration, +) { + println!("Schedule periodic enclave reregistration every: {:?}", period); + + std::thread::Builder::new() + .name("enclave_reregistration_thread".to_owned()) + .spawn(move || { + schedule_periodic( + || { + trace!("Reregistering the enclave."); + if let Some(block_hash) = send_register_xt() { + println!( + "✅ Successfully reregistered the enclave. Block hash: {}.", + block_hash + ) + } else { + error!("❌ Could not reregister the enclave.") + } + }, + period, + ); + }) + .unwrap(); +} + +/// Executes a periodic teeracle data update and sends the new data to the parentchain. +/// +/// Note: Puts the current thread to sleep for `period`. +pub(crate) fn start_periodic_market_update( + api: &ParentchainApi, + period: Duration, + enclave_api: &E, + tokio_handle: &Handle, +) { + let updates_to_run = || { + if let Err(e) = execute_oracle_update(api, tokio_handle, || { + // Get market data for usd (hardcoded) + enclave_api.update_market_data_xt("TEER", "USD") + }) { + error!("Error running market update {:?}", e) + } + + // TODO: Refactor and add this back according to ISSUE: https://github.com/integritee-network/worker/issues/1300 + // if let Err(e) = execute_oracle_update(api, tokio_handle, || { + // enclave_api.update_weather_data_xt("54.32", "15.37") + // }) { + // error!("Error running weather update {:?}", e) + // } + }; + info!("Teeracle will update now"); + updates_to_run(); + + info!("Schedule teeracle updates every {:?}", period); + schedule_periodic(updates_to_run, period); +} + +fn execute_oracle_update( + node_api: &ParentchainApi, + tokio_handle: &Handle, + get_oracle_xt: F, +) -> ServiceResult<()> +where + F: Fn() -> Result, itp_enclave_api::error::Error>, +{ + let oracle_xt = get_oracle_xt().map_err(|e| { + increment_number_of_request_failures(); + e + })?; + + let extrinsics = >::decode(&mut oracle_xt.as_slice())?; + + // Send the extrinsics to the parentchain and wait for InBlock confirmation. + for call in extrinsics.into_iter() { + let node_api_clone = node_api.clone(); + tokio_handle.spawn(async move { + let encoded_extrinsic = call.encode(); + debug!("Hex encoded extrinsic to be sent: {}", hex_encode(&encoded_extrinsic)); + + println!("[>] Update oracle data (send the extrinsic)"); + let extrinsic_hash = match node_api_clone.submit_and_watch_opaque_extrinsic_until( + &encoded_extrinsic.into(), + XtStatus::InBlock, + ) { + Err(e) => { + error!("Failed to send extrinsic: {:?}", e); + set_extrinsics_inclusion_success(false); + return + }, + Ok(report) => { + set_extrinsics_inclusion_success(true); + report.extrinsic_hash + }, + }; + + println!("[<] Extrinsic got included into a block. Hash: {:?}\n", extrinsic_hash); + }); + } + + Ok(()) +} diff --git a/bitacross-worker/service/src/teeracle/schedule_periodic.rs b/bitacross-worker/service/src/teeracle/schedule_periodic.rs new file mode 100644 index 0000000000..cde09af452 --- /dev/null +++ b/bitacross-worker/service/src/teeracle/schedule_periodic.rs @@ -0,0 +1,46 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use std::{ + thread, + time::{Duration, Instant}, +}; + +/// Schedules a periodic task in the current thread. +/// +/// In case the task takes longer than is scheduled by the interval duration, +/// the interval timing will drift. The task is responsible for +/// ensuring it does not use up more time than is scheduled. +pub(super) fn schedule_periodic(task: T, period: Duration) +where + T: Fn(), +{ + let mut interval_start = Instant::now(); + loop { + let elapsed = interval_start.elapsed(); + + if elapsed >= period { + // update interval time + interval_start = Instant::now(); + task(); + } else { + // sleep for the rest of the interval + let sleep_time = period - elapsed; + thread::sleep(sleep_time); + } + } +} diff --git a/bitacross-worker/service/src/teeracle/teeracle_metrics.rs b/bitacross-worker/service/src/teeracle/teeracle_metrics.rs new file mode 100644 index 0000000000..8fe62c2092 --- /dev/null +++ b/bitacross-worker/service/src/teeracle/teeracle_metrics.rs @@ -0,0 +1,76 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::error::{Error, ServiceResult}; +use itp_enclave_metrics::ExchangeRateOracleMetric; +use lazy_static::lazy_static; +use prometheus::{ + register_gauge_vec, register_int_counter, register_int_counter_vec, register_int_gauge, + register_int_gauge_vec, GaugeVec, IntCounter, IntCounterVec, IntGauge, IntGaugeVec, +}; + +lazy_static! { + /// Register Teeracle specific metrics + + static ref EXCHANGE_RATE: GaugeVec = + register_gauge_vec!("integritee_teeracle_exchange_rate", "Exchange rates partitioned into source and trading pair", &["source", "trading_pair"]) + .unwrap(); + static ref RESPONSE_TIME: IntGaugeVec = + register_int_gauge_vec!("integritee_teeracle_response_times", "Response times in ms for requests that the oracle makes", &["source"]) + .unwrap(); + static ref NUMBER_OF_REQUESTS: IntCounterVec = + register_int_counter_vec!("integritee_teeracle_number_of_requests", "Number of requests made per source", &["source"]) + .unwrap(); + + static ref NUMBER_OF_REQUEST_FAILURES: IntCounter = + register_int_counter!("integritee_teeracle_request_failures", "Number of requests that failed") + .unwrap(); + + static ref EXTRINSIC_INCLUSION_SUCCESS: IntGauge = + register_int_gauge!("integritee_teeracle_extrinsic_inclusion_success", "1 if extrinsics was successfully finalized, 0 if not") + .unwrap(); +} + +pub(super) fn increment_number_of_request_failures() { + NUMBER_OF_REQUEST_FAILURES.inc(); +} + +pub(super) fn set_extrinsics_inclusion_success(is_successful: bool) { + let success_values = i64::from(is_successful); + EXTRINSIC_INCLUSION_SUCCESS.set(success_values); +} + +pub fn update_teeracle_metrics(metric: ExchangeRateOracleMetric) -> ServiceResult<()> { + match metric { + ExchangeRateOracleMetric::ExchangeRate(source, trading_pair, exchange_rate) => + EXCHANGE_RATE + .get_metric_with_label_values(&[source.as_str(), trading_pair.as_str()]) + .map(|m| m.set(exchange_rate.to_num())) + .map_err(|e| Error::Custom(e.into()))?, + + ExchangeRateOracleMetric::ResponseTime(source, t) => RESPONSE_TIME + .get_metric_with_label_values(&[source.as_str()]) + .map(|m| m.set(t as i64)) + .map_err(|e| Error::Custom(e.into()))?, + + ExchangeRateOracleMetric::NumberRequestsIncrement(source) => NUMBER_OF_REQUESTS + .get_metric_with_label_values(&[source.as_str()]) + .map(|m| m.inc()) + .map_err(|e| Error::Custom(e.into()))?, + }; + Ok(()) +} diff --git a/bitacross-worker/service/src/tests/commons.rs b/bitacross-worker/service/src/tests/commons.rs new file mode 100644 index 0000000000..df6b5c9172 --- /dev/null +++ b/bitacross-worker/service/src/tests/commons.rs @@ -0,0 +1,63 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use serde_derive::{Deserialize, Serialize}; +use sgx_types::*; +use std::str; + +#[cfg(test)] +use crate::config::Config; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Message { + pub account: String, + pub amount: u32, + pub sha256: sgx_sha256_hash_t, +} + +#[cfg(test)] +pub fn local_worker_config( + worker_url: String, + untrusted_worker_port: String, + mu_ra_port: String, +) -> Config { + let mut url = worker_url.split(':'); + + Config::new( + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + url.next().unwrap().into(), + None, + url.next().unwrap().into(), + None, + untrusted_worker_port, + None, + mu_ra_port, + false, + "8787".to_string(), + "4545".to_string(), + crate::config::pwd(), + None, + "0".to_string(), + None, + 0, + ) +} diff --git a/bitacross-worker/service/src/tests/mock.rs b/bitacross-worker/service/src/tests/mock.rs new file mode 100644 index 0000000000..0587669dc4 --- /dev/null +++ b/bitacross-worker/service/src/tests/mock.rs @@ -0,0 +1,68 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use itp_node_api::api_client::{ApiResult, PalletTeerexApi}; +use itp_types::{Enclave, MrEnclave, ShardIdentifier, H256 as Hash}; +use std::collections::HashSet; + +pub struct TestNodeApi; + +pub const W1_URL: &str = "127.0.0.1:22222"; +pub const W2_URL: &str = "127.0.0.1:33333"; + +pub fn enclaves() -> Vec { + vec![ + Enclave::new([0; 32].into(), [1; 32], 1, format!("wss://{}", W1_URL)), + Enclave::new([2; 32].into(), [3; 32], 2, format!("wss://{}", W2_URL)), + ] +} + +impl PalletTeerexApi for TestNodeApi { + type Hash = Hash; + + fn enclave(&self, index: u64, _at_block: Option) -> ApiResult> { + Ok(Some(enclaves().remove(index as usize))) + } + fn enclave_count(&self, _at_block: Option) -> ApiResult { + unreachable!() + } + + fn all_enclaves(&self, _at_block: Option) -> ApiResult> { + Ok(enclaves()) + } + + fn worker_for_shard( + &self, + _: &ShardIdentifier, + _at_block: Option, + ) -> ApiResult> { + unreachable!() + } + fn latest_ipfs_hash( + &self, + _: &ShardIdentifier, + _at_block: Option, + ) -> ApiResult> { + unreachable!() + } + + fn all_scheduled_mrenclaves(&self, _at_block: Option) -> ApiResult> { + let enclaves = enclaves(); + let mr_enclaves: HashSet<_> = enclaves.into_iter().map(|e| e.mr_enclave).collect(); + Ok(mr_enclaves.into_iter().collect()) + } +} diff --git a/bitacross-worker/service/src/tests/mocks/broadcast_blocks_mock.rs b/bitacross-worker/service/src/tests/mocks/broadcast_blocks_mock.rs new file mode 100644 index 0000000000..2df5f65506 --- /dev/null +++ b/bitacross-worker/service/src/tests/mocks/broadcast_blocks_mock.rs @@ -0,0 +1,28 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{sync_block_broadcaster::BroadcastBlocks, worker::WorkerResult}; +use its_primitives::types::block::SignedBlock as SignedSidechainBlock; +use std::vec::Vec; + +pub struct BroadcastBlocksMock; + +impl BroadcastBlocks for BroadcastBlocksMock { + fn broadcast_blocks(&self, _blocks: Vec) -> WorkerResult<()> { + Ok(()) + } +} diff --git a/bitacross-worker/service/src/tests/mocks/direct_request_mock.rs b/bitacross-worker/service/src/tests/mocks/direct_request_mock.rs new file mode 100644 index 0000000000..a2c572dfc6 --- /dev/null +++ b/bitacross-worker/service/src/tests/mocks/direct_request_mock.rs @@ -0,0 +1,26 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use itp_enclave_api::{direct_request::DirectRequest, EnclaveResult}; + +pub struct DirectRequestMock; + +impl DirectRequest for DirectRequestMock { + fn rpc(&self, request: Vec) -> EnclaveResult> { + Ok(request) + } +} diff --git a/bitacross-worker/service/src/tests/mocks/enclave_api_mock.rs b/bitacross-worker/service/src/tests/mocks/enclave_api_mock.rs new file mode 100644 index 0000000000..af27dd3fae --- /dev/null +++ b/bitacross-worker/service/src/tests/mocks/enclave_api_mock.rs @@ -0,0 +1,124 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use codec::{Decode, Encode}; +use core::fmt::Debug; +use itc_parentchain::primitives::{ + ParentchainId, ParentchainInitParams, + ParentchainInitParams::{Parachain, Solochain}, +}; +use itp_enclave_api::{enclave_base::EnclaveBase, sidechain::Sidechain, EnclaveResult}; +use itp_settings::worker::MR_ENCLAVE_SIZE; +use itp_storage::StorageProof; +use itp_types::ShardIdentifier; +use sgx_crypto_helper::rsa3072::Rsa3072PubKey; +use sp_core::ed25519; +use teerex_primitives::EnclaveFingerprint; + +/// mock for EnclaveBase - use in tests +pub struct EnclaveMock; + +impl EnclaveBase for EnclaveMock { + fn init(&self, _mu_ra_url: &str, _untrusted_url: &str, _base_dir: &str) -> EnclaveResult<()> { + Ok(()) + } + + fn init_enclave_sidechain_components( + &self, + _fail_mode: Option, + _fail_at: u64, + ) -> EnclaveResult<()> { + Ok(()) + } + + fn init_direct_invocation_server(&self, _rpc_server_addr: String) -> EnclaveResult<()> { + unreachable!() + } + + fn init_parentchain_components( + &self, + params: ParentchainInitParams, + ) -> EnclaveResult
{ + let genesis_header_encoded = match params { + Solochain { params, .. } => params.genesis_header.encode(), + Parachain { params, .. } => params.genesis_header.encode(), + }; + let header = Header::decode(&mut genesis_header_encoded.as_slice())?; + Ok(header) + } + + fn init_shard(&self, _shard: Vec) -> EnclaveResult<()> { + unimplemented!() + } + + fn init_proxied_shard_vault( + &self, + _shard: &ShardIdentifier, + _parentchain_id: &ParentchainId, + ) -> EnclaveResult<()> { + unimplemented!() + } + + fn set_nonce(&self, _: u32, _: ParentchainId) -> EnclaveResult<()> { + unimplemented!() + } + + fn set_node_metadata(&self, _metadata: Vec, _: ParentchainId) -> EnclaveResult<()> { + todo!() + } + + fn get_rsa_shielding_pubkey(&self) -> EnclaveResult { + unreachable!() + } + + fn get_ecc_signing_pubkey(&self) -> EnclaveResult { + unreachable!() + } + + fn get_ecc_vault_pubkey(&self, _shard: &ShardIdentifier) -> EnclaveResult { + unreachable!() + } + + fn get_fingerprint(&self) -> EnclaveResult { + Ok([1u8; MR_ENCLAVE_SIZE].into()) + } + + fn migrate_shard(&self, _old_shard: Vec, _new_shard: Vec) -> EnclaveResult<()> { + unimplemented!() + } +} + +impl Sidechain for EnclaveMock { + fn sync_parentchain( + &self, + _blocks: &[sp_runtime::generic::SignedBlock], + _events: &[Vec], + _events_proofs: &[StorageProof], + _: &ParentchainId, + _: bool, + ) -> EnclaveResult<()> { + Ok(()) + } + + fn execute_trusted_calls(&self) -> EnclaveResult<()> { + todo!() + } + + fn ignore_parentchain_block_import_validation_until(&self, _until: u32) -> EnclaveResult<()> { + todo!() + } +} diff --git a/bitacross-worker/service/src/tests/mocks/initialization_handler_mock.rs b/bitacross-worker/service/src/tests/mocks/initialization_handler_mock.rs new file mode 100644 index 0000000000..e4539afc0e --- /dev/null +++ b/bitacross-worker/service/src/tests/mocks/initialization_handler_mock.rs @@ -0,0 +1,36 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::initialized_service::{IsInitialized, TrackInitialization}; + +pub struct TrackInitializationMock; + +impl TrackInitialization for TrackInitializationMock { + fn registered_on_parentchain(&self) {} + + fn sidechain_block_produced(&self) {} + + fn worker_for_shard_registered(&self) {} +} + +pub struct IsInitializedMock; + +impl IsInitialized for IsInitializedMock { + fn is_initialized(&self) -> bool { + true + } +} diff --git a/bitacross-worker/service/src/tests/mocks/mod.rs b/bitacross-worker/service/src/tests/mocks/mod.rs new file mode 100644 index 0000000000..cfe0d6fc76 --- /dev/null +++ b/bitacross-worker/service/src/tests/mocks/mod.rs @@ -0,0 +1,23 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pub mod broadcast_blocks_mock; +pub mod direct_request_mock; +pub mod enclave_api_mock; +pub mod initialization_handler_mock; +pub mod parentchain_api_mock; +pub mod update_worker_peers_mock; diff --git a/bitacross-worker/service/src/tests/mocks/parentchain_api_mock.rs b/bitacross-worker/service/src/tests/mocks/parentchain_api_mock.rs new file mode 100644 index 0000000000..712441b24c --- /dev/null +++ b/bitacross-worker/service/src/tests/mocks/parentchain_api_mock.rs @@ -0,0 +1,104 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use itc_parentchain_test::{ParentchainBlockBuilder, ParentchainHeaderBuilder}; +use itp_node_api::api_client::{ApiResult, Block, ChainApi, SignedBlock}; +use itp_types::{ + parentchain::{Hash, Header, StorageProof}, + H256, +}; +use sp_consensus_grandpa::AuthorityList; + +pub struct ParentchainApiMock { + parentchain: Vec, +} + +impl ParentchainApiMock { + // Todo: Remove when #1451 is resolved + #[allow(unused)] + pub(crate) fn new() -> Self { + ParentchainApiMock { parentchain: Vec::new() } + } + + /// Initializes parentchain with a default block chain of a given length. + // Todo: Remove when #1451 is resolved + #[allow(unused)] + pub fn with_default_blocks(mut self, number_of_blocks: u32) -> Self { + self.parentchain = (1..=number_of_blocks) + .map(|n| { + let header = ParentchainHeaderBuilder::default().with_number(n).build(); + ParentchainBlockBuilder::default().with_header(header).build_signed() + }) + .collect(); + self + } +} + +impl ChainApi for ParentchainApiMock { + type Hash = Hash; + type Block = Block; + type Header = Header; + type BlockNumber = u32; + + fn last_finalized_block(&self) -> ApiResult> { + Ok(self.parentchain.last().cloned()) + } + + fn signed_block(&self, _hash: Option) -> ApiResult> { + todo!() + } + + fn get_genesis_hash(&self) -> ApiResult { + todo!() + } + + fn header(&self, _header_hash: Option) -> ApiResult> { + todo!() + } + + fn get_blocks(&self, from: u32, to: u32) -> ApiResult> { + let num_elements = to.checked_sub(from).map(|n| n + 1).unwrap_or(0); + let blocks = self + .parentchain + .iter() + .skip(from as usize) + .take(num_elements as usize) + .cloned() + .collect(); + ApiResult::Ok(blocks) + } + + fn is_grandpa_available(&self) -> ApiResult { + todo!() + } + + fn grandpa_authorities(&self, _hash: Option) -> ApiResult { + todo!() + } + + fn grandpa_authorities_proof(&self, _hash: Option) -> ApiResult { + todo!() + } + + fn get_events_value_proof(&self, _block_hash: Option) -> ApiResult { + Ok(Default::default()) + } + + fn get_events_for_block(&self, _block_hash: Option) -> ApiResult> { + Ok(Default::default()) + } +} diff --git a/bitacross-worker/service/src/tests/mocks/update_worker_peers_mock.rs b/bitacross-worker/service/src/tests/mocks/update_worker_peers_mock.rs new file mode 100644 index 0000000000..1f9173f686 --- /dev/null +++ b/bitacross-worker/service/src/tests/mocks/update_worker_peers_mock.rs @@ -0,0 +1,33 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + worker::{Url, WorkerResult}, + worker_peers_registry::PeersRegistry, +}; + +pub struct WorkerPeersRegistryMock; + +impl PeersRegistry for WorkerPeersRegistryMock { + fn update_peers(&self) -> WorkerResult<()> { + Ok(()) + } + + fn read_trusted_peers(&self) -> WorkerResult> { + Ok(Vec::new()) + } +} diff --git a/bitacross-worker/service/src/tests/mod.rs b/bitacross-worker/service/src/tests/mod.rs new file mode 100644 index 0000000000..0ef2c4f253 --- /dev/null +++ b/bitacross-worker/service/src/tests/mod.rs @@ -0,0 +1,48 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pub mod commons; +pub mod mock; + +#[cfg(test)] +pub mod mocks; + +// Todo: Revive when #1451 is resolved +// #[cfg(test)] +// pub mod parentchain_handler_test; + +#[cfg(feature = "link-binary")] +use clap::ArgMatches; + +#[cfg(feature = "link-binary")] +pub fn run_enclave_tests(matches: &ArgMatches) { + use crate::{config::Config, enclave::api::*, setup}; + use itp_enclave_api::enclave_test::EnclaveTest; + + println!("*** Starting Test enclave"); + let config = Config::from(matches); + setup::purge_files_from_dir(config.data_dir()).unwrap(); + let enclave = enclave_init(&config).unwrap(); + + if matches.is_present("all") || matches.is_present("unit") { + println!("Running unit Tests"); + enclave.test_main_entrance().unwrap(); + println!("[+] unit_test ended!"); + } + + println!("[+] All tests ended!"); +} diff --git a/bitacross-worker/service/src/tests/parentchain_handler_test.rs b/bitacross-worker/service/src/tests/parentchain_handler_test.rs new file mode 100644 index 0000000000..30339e92bb --- /dev/null +++ b/bitacross-worker/service/src/tests/parentchain_handler_test.rs @@ -0,0 +1,51 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + parentchain_handler::{HandleParentchain, ParentchainHandler}, + tests::mocks::{enclave_api_mock::EnclaveMock, parentchain_api_mock::ParentchainApiMock}, +}; +use itc_parentchain::{ + light_client::light_client_init_params::SimpleParams, + primitives::{ParentchainId, ParentchainInitParams}, +}; +use itc_parentchain_test::ParentchainHeaderBuilder; +use itp_node_api::api_client::ChainApi; +use std::sync::Arc; + +#[test] +fn test_number_of_synced_blocks() { + let number_of_blocks = 42; + + let parentchain_api_mock = ParentchainApiMock::new().with_default_blocks(number_of_blocks); + let last_synced_block = + parentchain_api_mock.get_blocks(2, 2).unwrap().first().cloned().unwrap(); + + let enclave_api_mock = EnclaveMock; + let parentchain_params: ParentchainInitParams = + (ParentchainId::Litentry, SimpleParams::new(ParentchainHeaderBuilder::default().build())) + .into(); + + let parentchain_handler = ParentchainHandler::new( + parentchain_api_mock, + Arc::new(enclave_api_mock), + parentchain_params, + ); + + let header = parentchain_handler.sync_parentchain(last_synced_block.block.header).unwrap(); + assert_eq!(header.number, number_of_blocks); +} diff --git a/bitacross-worker/service/src/utils.rs b/bitacross-worker/service/src/utils.rs new file mode 100644 index 0000000000..fd0b60fe82 --- /dev/null +++ b/bitacross-worker/service/src/utils.rs @@ -0,0 +1,53 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use base58::{FromBase58, ToBase58}; +use itp_enclave_api::enclave_base::EnclaveBase; +use itp_types::ShardIdentifier; +use log::info; + +pub fn extract_shard( + maybe_shard_str: Option<&str>, + enclave_api: &E, +) -> ShardIdentifier { + match maybe_shard_str { + Some(value) => { + let shard_vec = value.from_base58().expect("shard must be hex encoded"); + let mut shard = [0u8; 32]; + shard.copy_from_slice(&shard_vec[..]); + shard.into() + }, + _ => { + let mrenclave = enclave_api.get_fingerprint().unwrap(); + info!("no shard specified. using mrenclave as id: {}", mrenclave.0.to_base58()); + ShardIdentifier::from_slice(&mrenclave[..]) + }, + } +} + +#[cfg(not(feature = "dcap"))] +pub fn check_files() { + use itp_settings::files::{ENCLAVE_FILE, RA_API_KEY_FILE, RA_SPID_FILE}; + use log::debug; + use std::path::Path; + debug!("*** Check files"); + let files = [ENCLAVE_FILE, RA_SPID_FILE, RA_API_KEY_FILE]; + for f in files.iter() { + assert!(Path::new(f).exists(), "File doesn't exist: {}", f); + } +} diff --git a/bitacross-worker/service/src/wasm.rs b/bitacross-worker/service/src/wasm.rs new file mode 100644 index 0000000000..fe99445759 --- /dev/null +++ b/bitacross-worker/service/src/wasm.rs @@ -0,0 +1,62 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +use sgx_types::*; + +extern "C" { + fn sgxwasm_init(eid: sgx_enclave_id_t, retval: *mut sgx_status_t) -> sgx_status_t; +} + +#[derive(Debug, Serialize, Deserialize)] +pub enum SgxWasmAction { + #[codec(index = 0)] + Call { module: Option>, function: String }, +} + +#[derive(Debug, Serialize, Deserialize)] +pub enum BoundaryValue { + #[codec(index = 0)] + I32(i32), + #[codec(index = 1)] + I64(i64), + #[codec(index = 2)] + F32(u32), + #[codec(index = 3)] + F64(u64), +} + +pub fn sgx_enclave_wasm_init(eid: sgx_enclave_id_t) -> Result<(), String> { + let mut retval: sgx_status_t = sgx_status_t::SGX_SUCCESS; + let result = unsafe { sgxwasm_init(eid, &mut retval) }; + + match result { + sgx_status_t::SGX_SUCCESS => {}, + _ => { + println!("[-] ECALL Enclave Failed {}!", result.as_str()); + panic!("sgx_enclave_wasm_init's ECALL returned unknown error!"); + }, + } + + match retval { + sgx_status_t::SGX_SUCCESS => {}, + _ => { + println!("[-] ECALL Enclave Function return fail: {}!", retval.as_str()); + return Err(format!("ECALL func return error: {}", retval.as_str())) + }, + } + + Ok(()) +} diff --git a/bitacross-worker/service/src/worker.rs b/bitacross-worker/service/src/worker.rs new file mode 100644 index 0000000000..638e4f081b --- /dev/null +++ b/bitacross-worker/service/src/worker.rs @@ -0,0 +1,297 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +///! Integritee worker. Inspiration for this design came from parity's substrate Client. +/// +/// This should serve as a proof of concept for a potential refactoring design. Ultimately, everything +/// from the main.rs should be covered by the worker struct here - hidden and split across +/// multiple traits. +use crate::{config::Config, error::Error, initialized_service::TrackInitialization}; +use async_trait::async_trait; +use codec::{Decode, Encode}; +use itc_rpc_client::direct_client::{DirectApi, DirectClient as DirectWorkerApi}; +use itp_enclave_api::enclave_base::EnclaveBase; +use itp_node_api::{api_client::PalletTeerexApi, node_api_factory::CreateNodeApi}; +use its_primitives::types::SignedBlock as SignedSidechainBlock; +use its_rpc_handler::constants::RPC_METHOD_NAME_IMPORT_BLOCKS; +use jsonrpsee::{ + types::{to_json_value, traits::Client}, + ws_client::WsClientBuilder, +}; +use log::*; +use std::{ + collections::HashSet, + sync::{Arc, RwLock}, +}; + +pub type WorkerResult = Result; +pub type Url = String; + +#[derive(Clone, Hash, Eq, PartialEq, Encode, Decode, Debug)] +pub struct PeerUrls { + pub trusted: Url, + pub untrusted: Url, + pub me: bool, +} + +impl PeerUrls { + pub fn new(trusted: Url, untrusted: Url, me: bool) -> Self { + PeerUrls { trusted, untrusted, me } + } +} + +pub struct Worker { + _config: Config, + // unused yet, but will be used when more methods are migrated to the worker + _enclave_api: Arc, + node_api_factory: Arc, + initialization_handler: Arc, + peer_urls: RwLock>, +} + +impl + Worker +{ + pub fn new( + config: Config, + enclave_api: Arc, + node_api_factory: Arc, + initialization_handler: Arc, + peer_urls: HashSet, + ) -> Self { + Self { + _config: config, + _enclave_api: enclave_api, + node_api_factory, + initialization_handler, + peer_urls: RwLock::new(peer_urls), + } + } +} + +#[async_trait] +/// Broadcast Sidechain blocks to peers. +pub trait AsyncBlockBroadcaster { + async fn broadcast_blocks(&self, blocks: Vec) -> WorkerResult<()>; +} + +#[async_trait] +impl AsyncBlockBroadcaster + for Worker +where + NodeApiFactory: CreateNodeApi + Send + Sync, + Enclave: Send + Sync, + InitializationHandler: TrackInitialization + Send + Sync, +{ + async fn broadcast_blocks(&self, blocks: Vec) -> WorkerResult<()> { + if blocks.is_empty() { + debug!("No blocks to broadcast, returning"); + return Ok(()) + } + + let blocks_json = vec![to_json_value(blocks)?]; + let peers = self + .peer_urls + .read() + .map_err(|e| { + Error::Custom(format!("Encountered poisoned lock for peers: {:?}", e).into()) + }) + .map(|l| l.clone())?; + + self.initialization_handler.sidechain_block_produced(); + + for url in peers { + let blocks = blocks_json.clone(); + + tokio::spawn(async move { + let untrusted_peer_url = url.untrusted; + + debug!("Broadcasting block to peer with address: {:?}", untrusted_peer_url); + // FIXME: Websocket connection to a worker should stay, once established. + let client = match WsClientBuilder::default().build(&untrusted_peer_url).await { + Ok(c) => c, + Err(e) => { + error!("Failed to create websocket client for block broadcasting (target url: {}): {:?}", untrusted_peer_url, e); + return + }, + }; + + if let Err(e) = + client.request::>(RPC_METHOD_NAME_IMPORT_BLOCKS, blocks.into()).await + { + error!( + "Broadcast block request ({}) to {} failed: {:?}", + RPC_METHOD_NAME_IMPORT_BLOCKS, untrusted_peer_url, e + ); + } + }); + } + Ok(()) + } +} + +/// Looks for new peers and updates them. +pub trait UpdatePeers { + fn search_peers(&self) -> WorkerResult>; + + fn set_peers_urls(&self, peers: HashSet) -> WorkerResult<()>; + + fn update_peers(&self) -> WorkerResult<()> { + let peers = self.search_peers()?; + self.set_peers_urls(peers) + } +} + +pub trait GetPeers { + fn read_peers_urls(&self) -> WorkerResult>; +} + +impl GetPeers + for Worker +where + NodeApiFactory: CreateNodeApi + Send + Sync, + Enclave: EnclaveBase + itp_enclave_api::remote_attestation::TlsRemoteAttestation, +{ + fn read_peers_urls(&self) -> WorkerResult> { + if let Ok(peer_urls) = self.peer_urls.read() { + Ok(peer_urls.clone()) + } else { + Err(Error::Custom("Encountered poisoned lock for peers".into())) + } + } +} + +impl UpdatePeers + for Worker +where + NodeApiFactory: CreateNodeApi + Send + Sync, + Enclave: EnclaveBase + itp_enclave_api::remote_attestation::TlsRemoteAttestation, +{ + fn search_peers(&self) -> WorkerResult> { + let worker_url_external = self._config.trusted_worker_url_external(); + let node_api = self + .node_api_factory + .create_api() + .map_err(|e| Error::Custom(format!("Failed to create NodeApi: {:?}", e).into()))?; + let enclaves = node_api.all_enclaves(None)?; + let mut peer_urls = HashSet::::new(); + for enclave in enclaves { + // FIXME: This is temporary only, as block broadcasting should be moved to trusted ws server. + let enclave_url = enclave.url.clone(); + let worker_api_direct = DirectWorkerApi::new(enclave_url.clone()); + match worker_api_direct.get_untrusted_worker_url() { + Ok(untrusted_worker_url) => { + let is_me = enclave_url == worker_url_external; + peer_urls.insert(PeerUrls::new(enclave_url, untrusted_worker_url, is_me)); + }, + Err(e) => { + warn!("Failed to get untrusted worker url (enclave: {}): {:?}", enclave_url, e); + }, + } + } + Ok(peer_urls) + } + + fn set_peers_urls(&self, peers: HashSet) -> WorkerResult<()> { + let peers_vec: Vec = peers.clone().into_iter().collect(); + info!("Setting peers urls: {:?}", peers_vec); + + let mut peer_urls = self.peer_urls.write().map_err(|e| { + Error::Custom(format!("Encountered poisoned lock for peers urls: {:?}", e).into()) + })?; + *peer_urls = peers; + Ok(()) + } +} +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + tests::{ + commons::local_worker_config, + mock::{W1_URL, W2_URL}, + mocks::initialization_handler_mock::TrackInitializationMock, + }, + worker::{AsyncBlockBroadcaster, Worker}, + }; + use frame_support::assert_ok; + use itp_node_api::node_api_factory::NodeApiFactory; + use its_primitives::types::block::SignedBlock as SignedSidechainBlock; + use its_test::sidechain_block_builder::{SidechainBlockBuilder, SidechainBlockBuilderTrait}; + use jsonrpsee::{ws_server::WsServerBuilder, RpcModule}; + use log::debug; + use sp_keyring::AccountKeyring; + use std::{net::SocketAddr, sync::Arc}; + use tokio::net::ToSocketAddrs; + + fn init() { + let _ = env_logger::builder().is_test(true).try_init(); + } + + async fn run_server(addr: impl ToSocketAddrs) -> anyhow::Result { + let mut server = WsServerBuilder::default().build(addr).await?; + let mut module = RpcModule::new(()); + + module.register_method(RPC_METHOD_NAME_IMPORT_BLOCKS, |params, _| { + debug!("{} params: {:?}", RPC_METHOD_NAME_IMPORT_BLOCKS, params); + let _blocks: Vec = params.one()?; + Ok("ok".as_bytes().to_vec()) + })?; + + server.register_module(module).unwrap(); + + let socket_addr = server.local_addr()?; + tokio::spawn(async move { server.start().await }); + Ok(socket_addr) + } + + #[tokio::test] + async fn broadcast_blocks_works() { + init(); + run_server(W1_URL).await.unwrap(); + run_server(W2_URL).await.unwrap(); + let untrusted_worker_port = "4000".to_string(); + let mut peer_urls: HashSet = HashSet::new(); + + peer_urls.insert(PeerUrls { + untrusted: format!("ws://{}", W1_URL), + trusted: format!("ws://{}", W1_URL), + me: false, + }); + peer_urls.insert(PeerUrls { + untrusted: format!("ws://{}", W2_URL), + trusted: format!("ws://{}", W2_URL), + me: false, + }); + + let worker = Worker::new( + local_worker_config(W1_URL.into(), untrusted_worker_port.clone(), "30".to_string()), + Arc::new(()), + Arc::new(NodeApiFactory::new( + "ws://invalid.url".to_string(), + AccountKeyring::Alice.pair(), + )), + Arc::new(TrackInitializationMock {}), + peer_urls, + ); + + let resp = worker + .broadcast_blocks(vec![SidechainBlockBuilder::default().build_signed()]) + .await; + assert_ok!(resp); + } +} diff --git a/bitacross-worker/service/src/worker_peers_registry.rs b/bitacross-worker/service/src/worker_peers_registry.rs new file mode 100644 index 0000000000..156408b634 --- /dev/null +++ b/bitacross-worker/service/src/worker_peers_registry.rs @@ -0,0 +1,56 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(test)] +use mockall::predicate::*; +#[cfg(test)] +use mockall::*; + +use crate::worker::{GetPeers, UpdatePeers, Url, WorkerResult}; +use std::sync::Arc; + +/// Updates the peers of the global worker. +#[cfg_attr(test, automock)] +pub trait PeersRegistry { + fn update_peers(&self) -> WorkerResult<()>; + fn read_trusted_peers(&self) -> WorkerResult>; +} + +pub struct WorkerPeersRegistry { + worker: Arc, +} + +impl WorkerPeersRegistry { + pub fn new(worker: Arc) -> Self { + WorkerPeersRegistry { worker } + } +} + +impl PeersRegistry for WorkerPeersRegistry +where + WorkerType: UpdatePeers + GetPeers, +{ + fn update_peers(&self) -> WorkerResult<()> { + self.worker.update_peers() + } + + fn read_trusted_peers(&self) -> WorkerResult> { + let peer_urls = self.worker.read_peers_urls()?; + Ok(peer_urls.into_iter().filter(|urls| !urls.me).map(|urls| urls.trusted).collect()) + } +} diff --git a/bitacross-worker/sidechain/block-composer/Cargo.toml b/bitacross-worker/sidechain/block-composer/Cargo.toml new file mode 100644 index 0000000000..f1be550d61 --- /dev/null +++ b/bitacross-worker/sidechain/block-composer/Cargo.toml @@ -0,0 +1,64 @@ +[package] +name = "its-block-composer" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +# sgx dependencies +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } +sgx_types = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } + +# local dependencies +itp-node-api = { path = "../../core-primitives/node-api", default-features = false } +itp-settings = { path = "../../core-primitives/settings", default-features = false } +itp-sgx-crypto = { path = "../../core-primitives/sgx/crypto", default-features = false } +itp-sgx-externalities = { path = "../../core-primitives/substrate-sgx/externalities", default-features = false } +itp-stf-executor = { path = "../../core-primitives/stf-executor", default-features = false } +itp-stf-primitives = { path = "../../core-primitives/stf-primitives", default-features = false } +itp-time-utils = { path = "../../core-primitives/time-utils", default-features = false } +itp-top-pool-author = { path = "../../core-primitives/top-pool-author", default-features = false } +itp-types = { path = "../../core-primitives/types", default-features = false } +its-primitives = { path = "../primitives", default-features = false, features = ["full_crypto"] } +its-state = { path = "../state", default-features = false } + +# sgx enabled external libraries +thiserror_sgx = { package = "thiserror", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3", optional = true } + +# std compatible external libraries (make sure these versions match with the sgx-enabled ones above) +thiserror = { version = "1.0", optional = true } + +# no-std compatible libraries +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +log = { version = "0.4", default-features = false } +sp-core = { default-features = false, features = ["full_crypto"], git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + + +[features] +default = ["std"] +std = [ + "itp-node-api/std", + "itp-sgx-crypto/std", + "itp-sgx-externalities/std", + "itp-stf-executor/std", + "itp-stf-primitives/std", + "itp-time-utils/std", + "itp-top-pool-author/std", + "itp-types/std", + "its-primitives/std", + "its-state/std", + "log/std", + "thiserror", +] +sgx = [ + "sgx_tstd", + "itp-node-api/sgx", + "itp-sgx-crypto/sgx", + "itp-sgx-externalities/sgx", + "itp-stf-executor/sgx", + "itp-time-utils/sgx", + "itp-top-pool-author/sgx", + "its-state/sgx", + "thiserror_sgx", +] diff --git a/bitacross-worker/sidechain/block-composer/src/block_composer.rs b/bitacross-worker/sidechain/block-composer/src/block_composer.rs new file mode 100644 index 0000000000..d87f7e61d3 --- /dev/null +++ b/bitacross-worker/sidechain/block-composer/src/block_composer.rs @@ -0,0 +1,185 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::error::{Error, Result}; +use codec::Encode; +use itp_settings::worker::BLOCK_NUMBER_FINALIZATION_DIFF; +use itp_sgx_crypto::{key_repository::AccessKey, StateCrypto}; +use itp_sgx_externalities::{SgxExternalitiesTrait, StateHash}; +use itp_stf_primitives::types::StatePayload; +use itp_time_utils::now_as_millis; +use itp_types::{ShardIdentifier, H256}; +use its_primitives::traits::{ + Block as SidechainBlockTrait, BlockData, Header as HeaderTrait, SignBlock, + SignedBlock as SignedSidechainBlockTrait, +}; +use its_state::{LastBlockExt, SidechainState, SidechainSystemExt}; +use log::*; +use sp_core::Pair; +use sp_runtime::{ + traits::{Block as ParentchainBlockTrait, Header}, + MultiSignature, +}; +use std::{format, marker::PhantomData, sync::Arc, vec::Vec}; + +/// Compose a sidechain block and corresponding confirmation extrinsic for the parentchain +/// +pub trait ComposeBlock { + type SignedSidechainBlock: SignedSidechainBlockTrait; + + fn compose_block( + &self, + latest_parentchain_header: &::Header, + top_call_hashes: Vec, + shard: ShardIdentifier, + state_hash_apriori: H256, + aposteriori_state: &Externalities, + ) -> Result; +} + +/// Block composer implementation for the sidechain +pub struct BlockComposer { + signer: Signer, + state_key_repository: Arc, + _phantom: PhantomData<(ParentchainBlock, SignedSidechainBlock)>, +} + +impl + BlockComposer +where + ParentchainBlock: ParentchainBlockTrait, + SignedSidechainBlock: + SignedSidechainBlockTrait, + SignedSidechainBlock::Block: SidechainBlockTrait, + <::Block as SidechainBlockTrait>::HeaderType: + HeaderTrait, + SignedSidechainBlock::Signature: From, + Signer: Pair, + Signer::Public: Encode, + StateKeyRepository: AccessKey, + ::KeyType: StateCrypto, +{ + pub fn new(signer: Signer, state_key_repository: Arc) -> Self { + BlockComposer { signer, state_key_repository, _phantom: Default::default() } + } +} + +type HeaderTypeOf = <::Block as SidechainBlockTrait>::HeaderType; +type BlockDataTypeOf = + <::Block as SidechainBlockTrait>::BlockDataType; + +impl + ComposeBlock + for BlockComposer +where + ParentchainBlock: ParentchainBlockTrait, + SignedSidechainBlock: + SignedSidechainBlockTrait, + SignedSidechainBlock::Block: SidechainBlockTrait, + <::Block as SidechainBlockTrait>::HeaderType: + HeaderTrait, + SignedSidechainBlock::Signature: From, + Externalities: SgxExternalitiesTrait + + SidechainState + + SidechainSystemExt + + StateHash + + LastBlockExt + + Encode, + ::SgxExternalitiesType: Encode, + ::SgxExternalitiesDiffType: Encode, + Signer: Pair, + Signer::Public: Encode, + StateKeyRepository: AccessKey, + ::KeyType: StateCrypto, +{ + type SignedSidechainBlock = SignedSidechainBlock; + + fn compose_block( + &self, + latest_parentchain_header: &ParentchainBlock::Header, + top_call_hashes: Vec, + shard: ShardIdentifier, + state_hash_apriori: H256, + aposteriori_state: &Externalities, + ) -> Result { + let author_public = self.signer.public(); + + let state_hash_new = aposteriori_state.hash(); + + let (block_number, parent_hash, next_finalization_block_number) = + match aposteriori_state.get_last_block() { + Some(block) => ( + block.header().block_number() + 1, + block.hash(), + block.header().next_finalization_block_number(), + ), + None => { + info!("Seems to be first sidechain block."); + (1, Default::default(), 1) + }, + }; + + if block_number != aposteriori_state.get_block_number().unwrap_or(0) { + return Err(Error::Other("[Sidechain] BlockNumber is not LastBlock's Number + 1".into())) + } + + // create encrypted payload + let mut payload: Vec = + StatePayload::new(state_hash_apriori, state_hash_new, aposteriori_state.state_diff()) + .encode(); + + let state_key = self + .state_key_repository + .retrieve_key() + .map_err(|e| Error::Other(format!("Failed to retrieve state key: {:?}", e).into()))?; + + state_key.encrypt(&mut payload).map_err(|e| { + Error::Other(format!("Failed to encrypt state payload: {:?}", e).into()) + })?; + + let block_data = BlockDataTypeOf::::new( + author_public, + latest_parentchain_header.hash(), + top_call_hashes, + payload, + now_as_millis(), + ); + + let mut finalization_candidate = next_finalization_block_number; + if block_number == 1 { + finalization_candidate = 1; + } else if block_number > finalization_candidate { + finalization_candidate += BLOCK_NUMBER_FINALIZATION_DIFF; + } + + let header = HeaderTypeOf::::new( + block_number, + parent_hash, + shard, + block_data.hash(), + finalization_candidate, + ); + + let block = SignedSidechainBlock::Block::new(header.clone(), block_data); + + debug!("Block header hash {}", header.hash()); + + let signed_block = block.sign_block(&self.signer); + + Ok(signed_block) + } +} diff --git a/bitacross-worker/sidechain/block-composer/src/error.rs b/bitacross-worker/sidechain/block-composer/src/error.rs new file mode 100644 index 0000000000..6baba32eb7 --- /dev/null +++ b/bitacross-worker/sidechain/block-composer/src/error.rs @@ -0,0 +1,59 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use sgx_types::sgx_status_t; +use std::{boxed::Box, format}; + +pub type Result = core::result::Result; + +/// Block composer error +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("SGX error, status: {0}")] + Sgx(sgx_status_t), + #[error("STF execution error: {0}")] + StfExecution(#[from] itp_stf_executor::error::Error), + #[error("TOP pool RPC author error: {0}")] + TopPoolAuthor(#[from] itp_top_pool_author::error::Error), + #[error("Node Metadata error: {0:?}")] + NodeMetadata(itp_node_api::metadata::Error), + #[error("Node metadata provider error: {0:?}")] + NodeMetadataProvider(#[from] itp_node_api::metadata::provider::Error), + #[error(transparent)] + Other(#[from] Box), +} + +impl From for Error { + fn from(sgx_status: sgx_status_t) -> Self { + Self::Sgx(sgx_status) + } +} + +impl From for Error { + fn from(e: codec::Error) -> Self { + Self::Other(format!("{:?}", e).into()) + } +} + +impl From for Error { + fn from(e: itp_node_api::metadata::Error) -> Self { + Self::NodeMetadata(e) + } +} diff --git a/bitacross-worker/sidechain/block-composer/src/lib.rs b/bitacross-worker/sidechain/block-composer/src/lib.rs new file mode 100644 index 0000000000..038f348c1d --- /dev/null +++ b/bitacross-worker/sidechain/block-composer/src/lib.rs @@ -0,0 +1,36 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +//! Sidechain block composing logic. +#![feature(trait_alias)] +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +// re-export module to properly feature gate sgx and regular std environment +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod sgx_reexport_prelude { + pub use thiserror_sgx as thiserror; +} + +pub mod block_composer; +pub mod error; + +pub use block_composer::*; diff --git a/bitacross-worker/sidechain/block-verification/Cargo.toml b/bitacross-worker/sidechain/block-verification/Cargo.toml new file mode 100644 index 0000000000..9265b86517 --- /dev/null +++ b/bitacross-worker/sidechain/block-verification/Cargo.toml @@ -0,0 +1,52 @@ +[package] +name = "its-block-verification" +description = "Verification logic for sidechain blocks" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +homepage = "https://litentry.com/" +repository = "https://github.com/litentry/litentry-parachain" +license = "Apache-2.0" +edition = "2021" + +[dependencies] +log = { version = "0.4.17", default-features = false } +thiserror = { version = "1.0.26", optional = true } + +# local deps +itp-types = { default-features = false, path = "../../core-primitives/types" } +itp-utils = { default-features = false, path = "../../core-primitives/utils" } +its-primitives = { default-features = false, path = "../primitives" } + +# substrate deps +frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-consensus-slots = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-core = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# sgx deps +sgx_tstd = { branch = "master", features = ["untrusted_fs", "net", "backtrace"], git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } +thiserror-sgx = { package = "thiserror", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3", optional = true } + +[features] +default = ["std"] +std = [ + "log/std", + "thiserror", + # local + "itp-types/std", + "its-primitives/std", + # substrate + "frame-support/std", + "sp-consensus-slots/std", + "sp-core/std", + "sp-runtime/std", +] +sgx = [ + "sgx_tstd", + "thiserror-sgx", +] + +[dev-dependencies] +itc-parentchain-test = { path = "../../core/parentchain/test" } +its-test = { path = "../../sidechain/test" } +sp-keyring = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } diff --git a/bitacross-worker/sidechain/block-verification/src/error.rs b/bitacross-worker/sidechain/block-verification/src/error.rs new file mode 100644 index 0000000000..bac9b8d60b --- /dev/null +++ b/bitacross-worker/sidechain/block-verification/src/error.rs @@ -0,0 +1,46 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Error types in sidechain consensus + +use itp_types::BlockHash as ParentchainBlockHash; +use its_primitives::types::{block::BlockHash as SidechainBlockHash, BlockNumber}; +use std::string::String; + +pub type Result = std::result::Result; + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub use thiserror_sgx as thiserror; + +#[derive(Debug, thiserror::Error)] +#[non_exhaustive] +pub enum Error { + #[error("Message sender {0} is not a valid authority")] + InvalidAuthority(String), + #[error("Could not get authorities: {0:?}.")] + CouldNotGetAuthorities(String), + #[error("Bad parentchain block (Hash={0}). Reason: {1}")] + BadParentchainBlock(ParentchainBlockHash, String), + #[error("Bad sidechain block (Hash={0}). Reason: {1}")] + BadSidechainBlock(SidechainBlockHash, String), + #[error("Could not import new block due to {2}. (Last imported by number: {0:?})")] + BlockAncestryMismatch(BlockNumber, SidechainBlockHash, String), + #[error("Could not import new block. Expected first block, but found {0}. {1:?}")] + InvalidFirstBlock(BlockNumber, String), + #[error("Could not import block (number: {0}). A block with this number is already imported (current state block number: {1})")] + BlockAlreadyImported(BlockNumber, BlockNumber), +} diff --git a/bitacross-worker/sidechain/block-verification/src/lib.rs b/bitacross-worker/sidechain/block-verification/src/lib.rs new file mode 100644 index 0000000000..b496bcc0be --- /dev/null +++ b/bitacross-worker/sidechain/block-verification/src/lib.rs @@ -0,0 +1,492 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +#![feature(assert_matches)] +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), not(feature = "sgx")))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be disabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +#[macro_use] +extern crate sgx_tstd as std; + +use crate::slot::{slot_author, slot_from_timestamp_and_duration}; +use error::Error as ConsensusError; +use frame_support::ensure; +use itp_utils::stringify::public_to_string; +use its_primitives::{ + traits::{ + Block as SidechainBlockTrait, BlockData, Header as HeaderTrait, + SignedBlock as SignedSidechainBlockTrait, SignedBlock, + }, + types::block::BlockHash, +}; +use log::*; +pub use sp_consensus_slots::Slot; +use sp_core::ByteArray; +use sp_runtime::{ + app_crypto::Pair, + traits::{Block as ParentchainBlockTrait, Header as ParentchainHeaderTrait}, +}; +use std::{fmt::Debug, time::Duration}; + +pub mod error; +pub mod slot; + +type AuthorityId

=

::Public; + +pub fn verify_sidechain_block( + signed_block: SignedSidechainBlock, + slot_duration: Duration, + last_block: &Option<::Block>, + parentchain_header: &ParentchainBlock::Header, + authorities: &[AuthorityId], +) -> Result +where + AuthorityPair: Pair, + AuthorityPair::Public: Debug, + ParentchainBlock: ParentchainBlockTrait, + SignedSidechainBlock: 'static + SignedSidechainBlockTrait, + SignedSidechainBlock::Block: SidechainBlockTrait, +{ + ensure!( + signed_block.verify_signature(), + ConsensusError::BadSidechainBlock(signed_block.block().hash(), "bad signature".into()) + ); + + let slot = slot_from_timestamp_and_duration( + Duration::from_millis(signed_block.block().block_data().timestamp()), + slot_duration, + ); + + // We need to check the ancestry first to ensure that an already imported block does not result + // in an author verification error, but rather a `BlockAlreadyImported` error. + match last_block { + Some(last_block) => + verify_block_ancestry::(signed_block.block(), last_block)?, + None => ensure_first_block(signed_block.block())?, + } + + if let Err(e) = verify_author::( + &slot, + signed_block.block(), + parentchain_header, + authorities, + ) { + error!( + "Author verification for block (number: {}) failed, block will be discarded", + signed_block.block().header().block_number() + ); + return Err(e) + } + + Ok(signed_block) +} + +/// Verify that the `blocks` author is the expected author when comparing with onchain data. +fn verify_author( + slot: &Slot, + block: &SignedSidechainBlock::Block, + parentchain_head: &ParentchainHeader, + authorities: &[AuthorityId], +) -> Result<(), ConsensusError> +where + AuthorityPair: Pair, + AuthorityPair::Public: Debug, + SignedSidechainBlock: SignedSidechainBlockTrait + 'static, + ParentchainHeader: ParentchainHeaderTrait, +{ + ensure!( + parentchain_head.hash() == block.block_data().layer_one_head(), + ConsensusError::BadParentchainBlock( + parentchain_head.hash(), + "Invalid parentchain head".into(), + ) + ); + + let expected_author = slot_author::(*slot, authorities) + .ok_or_else(|| ConsensusError::CouldNotGetAuthorities("No authorities found".into()))?; + + ensure!( + expected_author == block.block_data().block_author(), + ConsensusError::InvalidAuthority(format!( + "Expected author: {}, author found in block: {}", + public_to_string(&expected_author.to_raw_vec()), + public_to_string(&block.block_data().block_author().to_raw_vec()) + )) + ); + + Ok(()) +} + +fn verify_block_ancestry( + block: &SidechainBlock, + last_block: &SidechainBlock, +) -> Result<(), ConsensusError> { + // These next two checks might seem redundant at first glance. However, they are distinct (see comments). + + // We have already imported this block. + ensure!( + block.header().block_number() > last_block.header().block_number(), + ConsensusError::BlockAlreadyImported( + block.header().block_number(), + last_block.header().block_number() + ) + ); + + // We are missing some blocks between our last known block and the one we're trying to import. + ensure!( + last_block.header().block_number() + 1 == block.header().block_number(), + ConsensusError::BlockAncestryMismatch( + last_block.header().block_number(), + last_block.hash(), + format!( + "Invalid block number, {} does not succeed {}", + block.header().block_number(), + last_block.header().block_number() + ) + ) + ); + + ensure!( + last_block.hash() == block.header().parent_hash(), + ConsensusError::BlockAncestryMismatch( + last_block.header().block_number(), + last_block.hash(), + "Parent hash does not match".into(), + ) + ); + + Ok(()) +} + +fn ensure_first_block( + block: &SidechainBlock, +) -> Result<(), ConsensusError> { + ensure!( + block.header().block_number() == 1, + ConsensusError::InvalidFirstBlock( + block.header().block_number(), + "No last block found, expecting first block. But block to import has number != 1" + .into() + ) + ); + ensure!( + block.header().parent_hash() == Default::default(), + ConsensusError::InvalidFirstBlock( + block.header().block_number(), + "No last block found, excepting first block. But block to import has parent_hash != 0" + .into() + ) + ); + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use core::assert_matches::assert_matches; + use frame_support::assert_ok; + use itc_parentchain_test::ParentchainHeaderBuilder; + use itp_types::{AccountId, Block as ParentchainBlock}; + use its_primitives::types::{block::SignedBlock, header::SidechainHeader as Header}; + use its_test::{ + sidechain_block_builder::{SidechainBlockBuilder, SidechainBlockBuilderTrait}, + sidechain_block_data_builder::SidechainBlockDataBuilder, + sidechain_header_builder::SidechainHeaderBuilder, + }; + use sp_core::{ed25519::Pair, ByteArray, H256}; + use sp_keyring::ed25519::Keyring; + + pub const SLOT_DURATION: Duration = Duration::from_millis(300); + + fn assert_ancestry_mismatch_err(result: Result) { + assert_matches!(result, Err(ConsensusError::BlockAncestryMismatch(_, _, _,))) + } + + fn block(signer: Keyring, header: Header) -> SignedBlock { + let parentchain_header = ParentchainHeaderBuilder::default().build(); + let block_data = SidechainBlockDataBuilder::default() + .with_signer(signer.pair()) + .with_timestamp(0) + .with_layer_one_head(parentchain_header.hash()) + .build(); + + SidechainBlockBuilder::default() + .with_header(header) + .with_block_data(block_data) + .with_signer(signer.pair()) + .build_signed() + } + + fn block1(signer: Keyring) -> SignedBlock { + let header = SidechainHeaderBuilder::default().with_block_number(1).build(); + + block(signer, header) + } + + fn block2(signer: Keyring, parent_hash: H256) -> SignedBlock { + let header = SidechainHeaderBuilder::default() + .with_parent_hash(parent_hash) + .with_block_number(2) + .build(); + + block(signer, header) + } + + fn block3(signer: Keyring, parent_hash: H256, block_number: u64) -> SignedBlock { + let header = SidechainHeaderBuilder::default() + .with_parent_hash(parent_hash) + .with_block_number(block_number) + .build(); + + block(signer, header) + } + + #[test] + fn ensure_first_block_works() { + let block = SidechainBlockBuilder::default().build(); + assert_ok!(ensure_first_block(&block)); + } + + #[test] + fn ensure_first_block_errs_with_invalid_block_number() { + let header = SidechainHeaderBuilder::default().with_block_number(2).build(); + let block = SidechainBlockBuilder::default().with_header(header).build(); + assert_matches!(ensure_first_block(&block), Err(ConsensusError::InvalidFirstBlock(2, _))) + } + + #[test] + fn ensure_first_block_errs_with_invalid_parent_hash() { + let parent = H256::random(); + let header = SidechainHeaderBuilder::default().with_parent_hash(parent).build(); + let block = SidechainBlockBuilder::default().with_header(header).build(); + + assert_matches!(ensure_first_block(&block), Err(ConsensusError::InvalidFirstBlock(_, _))); + } + + #[test] + fn verify_block_ancestry_works() { + let last_block = SidechainBlockBuilder::default().build(); + let header = SidechainHeaderBuilder::default() + .with_parent_hash(last_block.hash()) + .with_block_number(2) + .build(); + let curr_block = SidechainBlockBuilder::default().with_header(header).build(); + + assert_ok!(verify_block_ancestry(&curr_block, &last_block)); + } + + #[test] + fn verify_block_ancestry_errs_with_invalid_parent_block_number() { + let last_block = SidechainBlockBuilder::default().build(); + let header = SidechainHeaderBuilder::default() + .with_parent_hash(last_block.hash()) + .with_block_number(5) + .build(); + let curr_block = SidechainBlockBuilder::default().with_header(header).build(); + + assert_ancestry_mismatch_err(verify_block_ancestry(&curr_block, &last_block)); + } + + #[test] + fn verify_block_ancestry_errs_with_invalid_parent_hash() { + let last_block = SidechainBlockBuilder::default().build(); + let header = SidechainHeaderBuilder::default().with_block_number(2).build(); + let curr_block = SidechainBlockBuilder::default().with_header(header).build(); + + assert_ancestry_mismatch_err(verify_block_ancestry(&curr_block, &last_block)); + } + + #[test] + fn verify_works() { + let signer = Keyring::Alice; + let signer_account: AccountId = signer.public().into(); + let authorities = [AuthorityId::::from_slice(signer_account.as_ref()).unwrap()]; + + let parentchain_header = ParentchainHeaderBuilder::default().build(); + let last_block = SidechainBlockBuilder::default().build(); + let curr_block = block2(signer, last_block.hash()); + + assert_ok!(verify_sidechain_block::( + curr_block, + SLOT_DURATION, + &Some(last_block), + &parentchain_header, + &authorities, + )); + } + + #[test] + fn verify_works_for_first_block() { + let signer = Keyring::Alice; + let signer_account: AccountId = signer.public().into(); + let authorities = [AuthorityId::::from_slice(signer_account.as_ref()).unwrap()]; + + let parentchain_header = ParentchainHeaderBuilder::default().build(); + let curr_block = block1(signer); + + assert_ok!(verify_sidechain_block::( + curr_block, + SLOT_DURATION, + &None, + &parentchain_header, + &authorities, + )); + } + + #[test] + fn verify_errs_on_wrong_authority() { + let signer = Keyring::Alice; + let signer_account: AccountId = signer.public().into(); + let bob_account: AccountId = Keyring::Bob.public().into(); + let authorities = [ + AuthorityId::::from_slice(bob_account.as_ref()).unwrap(), + AuthorityId::::from_slice(signer_account.as_ref()).unwrap(), + ]; + + let parentchain_header = ParentchainHeaderBuilder::default().build(); + let last_block = SidechainBlockBuilder::default().build(); + let curr_block = block2(signer, last_block.hash()); + + assert_matches!( + verify_sidechain_block::( + curr_block, + SLOT_DURATION, + &Some(last_block), + &parentchain_header, + &authorities, + ) + .unwrap_err(), + ConsensusError::InvalidAuthority(_) + ); + } + + #[test] + fn verify_errs_on_invalid_ancestry() { + let signer = Keyring::Alice; + let signer_account: AccountId = signer.public().into(); + let authorities = [AuthorityId::::from_slice(signer_account.as_ref()).unwrap()]; + + let parentchain_header = ParentchainHeaderBuilder::default().build(); + let last_block = SidechainBlockBuilder::default().build(); + let curr_block = block2(signer, Default::default()); + + assert_ancestry_mismatch_err(verify_sidechain_block::( + curr_block, + SLOT_DURATION, + &Some(last_block), + &parentchain_header, + &authorities, + )); + } + + #[test] + fn verify_errs_on_wrong_first_block() { + let signer = Keyring::Alice; + let signer_account: AccountId = signer.public().into(); + let authorities = [AuthorityId::::from_slice(signer_account.as_ref()).unwrap()]; + + let parentchain_header = ParentchainHeaderBuilder::default().build(); + let curr_block = block2(signer, Default::default()); + + assert_matches!( + verify_sidechain_block::( + curr_block, + SLOT_DURATION, + &None, + &parentchain_header, + &authorities, + ) + .unwrap_err(), + ConsensusError::InvalidFirstBlock(2, _) + ); + } + + #[test] + fn verify_errs_on_already_imported_block() { + let signer = Keyring::Alice; + let signer_account: AccountId = signer.public().into(); + let authorities = [AuthorityId::::from_slice(signer_account.as_ref()).unwrap()]; + + let parentchain_header = ParentchainHeaderBuilder::default().build(); + let last_block = SidechainBlockBuilder::default().build(); + // Current block has also number 1, same as last. So import should return an error + // that a block with this number is already imported. + let curr_block = block3(signer, last_block.hash(), 1); + + assert_matches!( + verify_sidechain_block::( + curr_block, + SLOT_DURATION, + &Some(last_block), + &parentchain_header, + &authorities, + ) + .unwrap_err(), + ConsensusError::BlockAlreadyImported(1, 1) + ); + } + + #[test] + fn verify_block_already_imported_error_even_if_parentchain_block_mismatches() { + // This test is to ensure that we get a 'AlreadyImported' error, when the sidechain block + // is already imported, and the parentchain block that is passed into the verifier is newer. + // Important because client of the verifier acts differently for an 'AlreadyImported' error than an 'AncestryErrorMismatch'. + + let signer = Keyring::Alice; + let signer_account: AccountId = signer.public().into(); + let authorities = [AuthorityId::::from_slice(signer_account.as_ref()).unwrap()]; + + let parentchain_header_1 = ParentchainHeaderBuilder::default().with_number(1).build(); + let parentchain_header_2 = ParentchainHeaderBuilder::default().with_number(2).build(); + + let block_data = SidechainBlockDataBuilder::default() + .with_layer_one_head(parentchain_header_1.hash()) + .with_signer(signer.pair()) + .build(); + let last_block = SidechainBlockBuilder::default() + .with_block_data(block_data) + .with_signer(signer.pair()) + .build(); + + let block_data_for_signed_block = SidechainBlockDataBuilder::default() + .with_layer_one_head(parentchain_header_1.hash()) + .with_signer(signer.pair()) + .build(); + let signed_block_to_verify = SidechainBlockBuilder::default() + .with_block_data(block_data_for_signed_block) + .with_signer(signer.pair()) + .build_signed(); + + assert_matches!( + verify_sidechain_block::( + signed_block_to_verify, + SLOT_DURATION, + &Some(last_block), + &parentchain_header_2, + &authorities, + ) + .unwrap_err(), + ConsensusError::BlockAlreadyImported(1, 1) + ); + } +} diff --git a/bitacross-worker/sidechain/block-verification/src/slot.rs b/bitacross-worker/sidechain/block-verification/src/slot.rs new file mode 100644 index 0000000000..5eb2ede417 --- /dev/null +++ b/bitacross-worker/sidechain/block-verification/src/slot.rs @@ -0,0 +1,45 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::AuthorityId; +pub use sp_consensus_slots::Slot; +use sp_runtime::app_crypto::Pair; +use std::time::Duration; + +/// Get slot author for given block along with authorities. +pub fn slot_author(slot: Slot, authorities: &[AuthorityId

]) -> Option<&AuthorityId

> { + if authorities.is_empty() { + log::warn!("Authorities list is empty, cannot determine slot author"); + return None + } + + let idx = *slot % (authorities.len() as u64); + assert!( + idx <= usize::MAX as u64, + "It is impossible to have a vector with length beyond the address space; qed", + ); + + let current_author = authorities.get(idx as usize).expect( + "authorities not empty; index constrained to list length;this is a valid index; qed", + ); + + Some(current_author) +} + +pub fn slot_from_timestamp_and_duration(timestamp: Duration, duration: Duration) -> Slot { + ((timestamp.as_millis() / duration.as_millis()) as u64).into() +} diff --git a/bitacross-worker/sidechain/consensus/aura/Cargo.toml b/bitacross-worker/sidechain/consensus/aura/Cargo.toml new file mode 100644 index 0000000000..a7a52de35e --- /dev/null +++ b/bitacross-worker/sidechain/consensus/aura/Cargo.toml @@ -0,0 +1,105 @@ +[package] +name = "its-consensus-aura" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "chain-error"] } +finality-grandpa = { version = "0.16.0", default-features = false, features = ["derive-codec"] } +log = { version = "0.4", default-features = false } + +# sgx deps +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } + +# substrate deps +sp-core = { default-features = false, features = ["full_crypto"], git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# local deps +ita-stf = { path = "../../../app-libs/stf", default-features = false } +itc-parentchain-block-import-dispatcher = { path = "../../../core/parentchain/block-import-dispatcher", default-features = false } +itc-peer-top-broadcaster = { path = "../../../core/peer-top-broadcaster", default-features = false } +itp-enclave-metrics = { path = "../../../core-primitives/enclave-metrics", default-features = false } +itp-ocall-api = { path = "../../../core-primitives/ocall-api", default-features = false } +itp-settings = { path = "../../../core-primitives/settings" } +itp-sgx-crypto = { path = "../../../core-primitives/sgx/crypto", default-features = false } +itp-sgx-externalities = { path = "../../../core-primitives/substrate-sgx/externalities", default-features = false } +itp-stf-executor = { path = "../../../core-primitives/stf-executor", default-features = false } +itp-stf-primitives = { path = "../../../core-primitives/stf-primitives", default-features = false } +itp-stf-state-handler = { path = "../../../core-primitives/stf-state-handler", default-features = false } +itp-time-utils = { path = "../../../core-primitives/time-utils", default-features = false } +itp-top-pool-author = { path = "../../../core-primitives/top-pool-author", default-features = false } +itp-types = { path = "../../../core-primitives/types", default-features = false } +its-block-composer = { path = "../../block-composer", default-features = false } +its-block-verification = { path = "../../block-verification", optional = true, default-features = false } +its-consensus-common = { path = "../common", default-features = false } +its-consensus-slots = { path = "../slots", default-features = false } +its-primitives = { path = "../../primitives", default-features = false } +its-state = { path = "../../state", default-features = false } +its-validateer-fetch = { path = "../../validateer-fetch", default-features = false } + +# litentry +itp-utils = { path = "../../../core-primitives/utils", default-features = false } +lc-scheduled-enclave = { path = "../../../litentry/core/scheduled-enclave", default-features = false } + +[dev-dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +env_logger = "0.9.0" +itc-parentchain-block-import-dispatcher = { path = "../../../core/parentchain/block-import-dispatcher", features = ["mocks"] } +itc-parentchain-test = { path = "../../../core/parentchain/test" } +itp-storage = { path = "../../../core-primitives/storage" } +itp-test = { path = "../../../core-primitives/test" } +its-test = { path = "../../../sidechain/test" } +sp-keyring = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +[features] +default = ["std"] +std = [ + #crates.io + "codec/std", + "finality-grandpa/std", + "log/std", + #substrate + "sp-core/std", + "sp-runtime/std", + #local + "ita-stf/std", + "itc-parentchain-block-import-dispatcher/std", + "itc-peer-top-broadcaster/std", + "itp-enclave-metrics/std", + "itp-ocall-api/std", + "itp-sgx-crypto/std", + "itp-sgx-externalities/std", + "itp-stf-executor/std", + "itp-stf-primitives/std", + "itp-stf-state-handler/std", + "itp-time-utils/std", + "itp-types/std", + "its-block-composer/std", + "its-block-verification/std", + "its-consensus-common/std", + "its-consensus-slots/std", + "its-state/std", + "its-validateer-fetch/std", + "its-primitives/std", + "lc-scheduled-enclave/std", +] +sgx = [ + "sgx_tstd", + "ita-stf/sgx", + "itc-parentchain-block-import-dispatcher/sgx", + "itc-peer-top-broadcaster/sgx", + "itp-enclave-metrics/sgx", + "itp-sgx-crypto/sgx", + "itp-sgx-externalities/sgx", + "itp-stf-executor/sgx", + "itp-stf-state-handler/sgx", + "itp-time-utils/sgx", + "its-block-composer/sgx", + "its-consensus-common/sgx", + "its-consensus-slots/sgx", + "its-state/sgx", + "its-block-verification/sgx", + "lc-scheduled-enclave/sgx", +] diff --git a/bitacross-worker/sidechain/consensus/aura/src/block_importer.rs b/bitacross-worker/sidechain/consensus/aura/src/block_importer.rs new file mode 100644 index 0000000000..fb6f4e246a --- /dev/null +++ b/bitacross-worker/sidechain/consensus/aura/src/block_importer.rs @@ -0,0 +1,367 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +//! Implementation of the sidechain block importer struct. +//! Imports sidechain blocks and applies the accompanying state diff to its state. + +use codec::{Decode, Encode}; +use core::fmt::Debug; +// Reexport BlockImport trait which implements fn block_import() +use crate::{AuraVerifier, EnclaveOnChainOCallApi, SidechainBlockTrait}; +use itc_parentchain_block_import_dispatcher::triggered_dispatcher::TriggerParentchainBlockImport; +use itc_peer_top_broadcaster::PeerUpdater; +use itp_enclave_metrics::EnclaveMetric; +use itp_ocall_api::{EnclaveMetricsOCallApi, EnclaveSidechainOCallApi}; +use itp_settings::sidechain::SLOT_DURATION; +use itp_sgx_crypto::{key_repository::AccessKey, StateCrypto}; +use itp_sgx_externalities::SgxExternalities; +use itp_stf_primitives::{traits::TrustedCallVerification, types::TrustedOperationOrHash}; +use itp_stf_state_handler::handle_state::HandleState; +use itp_top_pool_author::traits::{AuthorApi, OnBlockImported}; +use itp_types::H256; +pub use its_consensus_common::BlockImport; +use its_consensus_common::Error as ConsensusError; +use its_primitives::traits::{ + BlockData, Header as HeaderTrait, ShardIdentifierFor, SignedBlock as SignedBlockTrait, +}; +use its_validateer_fetch::ValidateerFetch; +use log::*; +use sp_core::{crypto::UncheckedFrom, Pair}; +use sp_runtime::{ + generic::SignedBlock as SignedParentchainBlock, + traits::{Block as ParentchainBlockTrait, Header}, +}; +use std::{marker::PhantomData, sync::Arc}; + +/// Implements `BlockImport`. +#[derive(Clone)] +pub struct BlockImporter< + Authority, + ParentchainBlock, + SignedSidechainBlock, + OCallApi, + StateHandler, + StateKeyRepository, + TopPoolAuthor, + ParentchainBlockImporter, + PeersUpdater, + TCS, + G, +> { + state_handler: Arc, + state_key_repository: Arc, + top_pool_author: Arc, + parentchain_block_importer: Arc, + ocall_api: Arc, + peer_updater: Arc, + _phantom: PhantomData<(Authority, ParentchainBlock, SignedSidechainBlock, TCS, G)>, +} + +impl< + Authority, + ParentchainBlock, + SignedSidechainBlock, + OCallApi, + StateHandler, + StateKeyRepository, + TopPoolAuthor, + ParentchainBlockImporter, + PeersUpdater, + TCS, + G, + > + BlockImporter< + Authority, + ParentchainBlock, + SignedSidechainBlock, + OCallApi, + StateHandler, + StateKeyRepository, + TopPoolAuthor, + ParentchainBlockImporter, + PeersUpdater, + TCS, + G, + > where + Authority: Pair, + Authority::Public: std::fmt::Debug + UncheckedFrom<[u8; 32]>, + ParentchainBlock: ParentchainBlockTrait, + SignedSidechainBlock: SignedBlockTrait + 'static, + <::Block as SidechainBlockTrait>::HeaderType: + HeaderTrait, + OCallApi: EnclaveSidechainOCallApi + + ValidateerFetch + + EnclaveOnChainOCallApi + + EnclaveMetricsOCallApi + + Send + + Sync, + StateHandler: HandleState, + StateKeyRepository: AccessKey, + ::KeyType: StateCrypto, + TopPoolAuthor: AuthorApi + OnBlockImported, + ParentchainBlockImporter: TriggerParentchainBlockImport> + + Send + + Sync, + PeersUpdater: PeerUpdater, + TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, + G: PartialEq + Encode + Decode + Debug + Clone + Send + Sync, +{ + pub fn new( + state_handler: Arc, + state_key_repository: Arc, + top_pool_author: Arc, + parentchain_block_importer: Arc, + ocall_api: Arc, + peer_updater: Arc, + ) -> Self { + Self { + state_handler, + state_key_repository, + top_pool_author, + parentchain_block_importer, + ocall_api, + peer_updater, + _phantom: Default::default(), + } + } + + fn update_top_pool(&self, sidechain_block: &SignedSidechainBlock::Block) { + // Notify pool about imported block for status updates of the calls. + self.top_pool_author.on_block_imported( + sidechain_block.block_data().signed_top_hashes(), + sidechain_block.hash(), + ); + + // Remove calls from pool. + let executed_operations = sidechain_block + .block_data() + .signed_top_hashes() + .iter() + .map(|hash| (TrustedOperationOrHash::Hash(*hash), true)) + .collect(); + + let _calls_failed_to_remove = self + .top_pool_author + .remove_calls_from_pool(sidechain_block.header().shard_id(), executed_operations); + + // In case the executed call did not originate in our own TOP pool, we will not be able to remove it from our TOP pool. + // So this error will occur frequently, without it meaning that something really went wrong. + // TODO: Once the TOP pools are synchronized, we will want this check again! + // for call_failed_to_remove in _calls_failed_to_remove { + // error!("Could not remove call {:?} from top pool", call_failed_to_remove); + // } + } +} + +impl< + Authority, + ParentchainBlock, + SignedSidechainBlock, + OCallApi, + StateHandler, + StateKeyRepository, + TopPoolAuthor, + ParentchainBlockImporter, + PeersUpdater, + TCS, + G, + > BlockImport + for BlockImporter< + Authority, + ParentchainBlock, + SignedSidechainBlock, + OCallApi, + StateHandler, + StateKeyRepository, + TopPoolAuthor, + ParentchainBlockImporter, + PeersUpdater, + TCS, + G, + > where + Authority: Pair, + Authority::Public: std::fmt::Debug + UncheckedFrom<[u8; 32]>, + ParentchainBlock: ParentchainBlockTrait, + SignedSidechainBlock: SignedBlockTrait + 'static, + <::Block as SidechainBlockTrait>::HeaderType: + HeaderTrait, + OCallApi: EnclaveSidechainOCallApi + + ValidateerFetch + + EnclaveOnChainOCallApi + + EnclaveMetricsOCallApi + + Send + + Sync, + StateHandler: HandleState, + StateKeyRepository: AccessKey, + ::KeyType: StateCrypto, + TopPoolAuthor: AuthorApi + OnBlockImported, + ParentchainBlockImporter: TriggerParentchainBlockImport> + + Send + + Sync, + PeersUpdater: PeerUpdater, + TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, + G: PartialEq + Encode + Decode + Debug + Clone + Send + Sync, +{ + type Verifier = AuraVerifier; + type SidechainState = SgxExternalities; + type StateCrypto = ::KeyType; + type Context = OCallApi; + + fn verifier( + &self, + maybe_last_sidechain_block: Option, + ) -> Self::Verifier { + AuraVerifier::::new( + SLOT_DURATION, + maybe_last_sidechain_block, + ) + } + + fn apply_state_update( + &self, + shard: &ShardIdentifierFor, + mutating_function: F, + ) -> Result<(), ConsensusError> + where + F: FnOnce(Self::SidechainState) -> Result, + { + let (write_lock, state) = self + .state_handler + .load_for_mutation(shard) + .map_err(|e| ConsensusError::Other(format!("{:?}", e).into()))?; + + // We load a copy of the state and apply the update. In case the update fails, we don't write + // the state back to the state handler, and thus guaranteeing state integrity. + let updated_state = mutating_function(state)?; + + self.state_handler + .write_after_mutation(updated_state, write_lock, shard) + .map_err(|e| ConsensusError::Other(format!("{:?}", e).into()))?; + + Ok(()) + } + + fn verify_import( + &self, + shard: &ShardIdentifierFor, + verifying_function: F, + ) -> Result + where + F: FnOnce(&Self::SidechainState) -> Result, + { + self.state_handler + .execute_on_current(shard, |state, _| verifying_function(state)) + .map_err(|e| ConsensusError::Other(format!("{:?}", e).into()))? + } + + fn state_key(&self) -> Result { + self.state_key_repository + .retrieve_key() + .map_err(|e| ConsensusError::Other(format!("{:?}", e).into())) + } + + fn get_context(&self) -> &Self::Context { + &self.ocall_api + } + + fn import_parentchain_block( + &self, + sidechain_block: &SignedSidechainBlock::Block, + last_imported_parentchain_header: &ParentchainBlock::Header, + ) -> Result { + // get new peer list on each parentchain block import + if let Ok(peers) = self.ocall_api.get_trusted_peers_urls() { + self.peer_updater.update(peers); + } + + // We trigger the import of parentchain blocks up until the last one we've seen in the + // sidechain block that we're importing. This is done to prevent forks in the sidechain (#423) + let maybe_latest_imported_block = self + .parentchain_block_importer + .import_until(|signed_parentchain_block| { + signed_parentchain_block.block.hash() + == sidechain_block.block_data().layer_one_head() + }) + .map_err(|e| ConsensusError::Other(format!("{:?}", e).into()))?; + + Ok(maybe_latest_imported_block + .map(|b| b.block.header().clone()) + .unwrap_or_else(|| last_imported_parentchain_header.clone())) + } + + fn peek_parentchain_header( + &self, + sidechain_block: &SignedSidechainBlock::Block, + last_imported_parentchain_header: &ParentchainBlock::Header, + ) -> Result { + let last = last_imported_parentchain_header; + debug!("Peeking parentchain header"); + debug!( + "sidechain block parentchain head: {}", + sidechain_block.block_data().layer_one_head() + ); + debug!( + "last imported head: {}, number: {:?}, parenthash: {}", + last.hash(), + last.number(), + last.parent_hash() + ); + + let parentchain_header_hash_to_peek = sidechain_block.block_data().layer_one_head(); + if parentchain_header_hash_to_peek == last_imported_parentchain_header.hash() { + debug!("No queue peek necessary, sidechain block references latest imported parentchain block"); + return Ok(last_imported_parentchain_header.clone()) + } + + let maybe_signed_parentchain_block = self + .parentchain_block_importer + .peek(|parentchain_block| { + parentchain_block.block.header().hash() == parentchain_header_hash_to_peek + }) + .map_err(|e| ConsensusError::Other(format!("{:?}", e).into()))?; + + maybe_signed_parentchain_block + .map(|signed_block| signed_block.block.header().clone()) + .ok_or_else(|| { + ConsensusError::Other( + format!( + "Failed to find parentchain header in import queue (hash: {}) that is \ + associated with the current sidechain block that is to be imported (number: {}, hash: {})", + parentchain_header_hash_to_peek, + sidechain_block.header().block_number(), + sidechain_block.hash() + ) + .into(), + ) + }) + } + + fn cleanup(&self, signed_sidechain_block: &SignedSidechainBlock) -> Result<(), ConsensusError> { + let sidechain_block = signed_sidechain_block.block(); + + // Remove all successfully applied trusted calls from the top pool. + self.update_top_pool(sidechain_block); + + // Send metric about sidechain block height (i.e. block number) + let block_height_metric = + EnclaveMetric::SetSidechainBlockHeight(sidechain_block.header().block_number()); + if let Err(e) = self.ocall_api.update_metric(block_height_metric) { + warn!("Failed to update sidechain block height metric: {:?}", e); + } + + Ok(()) + } +} diff --git a/bitacross-worker/sidechain/consensus/aura/src/lib.rs b/bitacross-worker/sidechain/consensus/aura/src/lib.rs new file mode 100644 index 0000000000..193eb0501c --- /dev/null +++ b/bitacross-worker/sidechain/consensus/aura/src/lib.rs @@ -0,0 +1,773 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Aura worker for the sidechain. +//! +//! It is inspired by parity's implementation but has been greatly amended. + +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(test, feature(assert_matches))] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +#[macro_use] +extern crate sgx_tstd as std; + +use codec::Encode; +use core::marker::PhantomData; +use itc_parentchain_block_import_dispatcher::triggered_dispatcher::TriggerParentchainBlockImport; +use itp_ocall_api::EnclaveOnChainOCallApi; +use itp_sgx_externalities::SgxExternalities; +use itp_stf_state_handler::handle_state::HandleState; +use itp_time_utils::duration_now; + +use itp_utils::hex::hex_encode; +use its_block_verification::slot::slot_author; +use its_consensus_common::{Environment, Error as ConsensusError, Proposer}; +use its_consensus_slots::{SimpleSlotWorker, Slot, SlotInfo}; +use its_primitives::{ + traits::{Block as SidechainBlockTrait, Header as HeaderTrait, SignedBlock}, + types::block::BlockHash, +}; +use its_validateer_fetch::ValidateerFetch; +use lc_scheduled_enclave::ScheduledEnclaveUpdater; +use sp_core::ByteArray; +use sp_runtime::{ + app_crypto::{sp_core::H256, Pair}, + generic::SignedBlock as SignedParentchainBlock, + traits::{Block as ParentchainBlockTrait, Header as ParentchainHeaderTrait}, +}; +use std::{string::ToString, sync::Arc, time::Duration, vec::Vec}; + +pub mod block_importer; +pub mod proposer_factory; +pub mod slot_proposer; +mod verifier; + +pub use verifier::*; + +#[cfg(test)] +mod test; + +/// Aura consensus struct. +pub struct Aura< + AuthorityPair, + ParentchainBlock, + SidechainBlock, + Environment, + OcallApi, + IntegriteeImportTrigger, + TargetAImportTrigger, + TargetBImportTrigger, + ScheduledEnclave, + StateHandler, +> { + authority_pair: AuthorityPair, + ocall_api: OcallApi, + parentchain_integritee_import_trigger: Arc, + maybe_parentchain_target_a_import_trigger: Option>, + maybe_parentchain_target_b_import_trigger: Option>, + environment: Environment, + claim_strategy: SlotClaimStrategy, + scheduled_enclave: Arc, + state_handler: Arc, + _phantom: PhantomData<(AuthorityPair, ParentchainBlock, SidechainBlock)>, +} + +impl< + AuthorityPair, + ParentchainBlock, + SidechainBlock, + Environment, + OcallApi, + IntegriteeImportTrigger, + TargetAImportTrigger, + TargetBImportTrigger, + ScheduledEnclave, + StateHandler, + > + Aura< + AuthorityPair, + ParentchainBlock, + SidechainBlock, + Environment, + OcallApi, + IntegriteeImportTrigger, + TargetAImportTrigger, + TargetBImportTrigger, + ScheduledEnclave, + StateHandler, + > +{ + #[allow(clippy::too_many_arguments)] + pub fn new( + authority_pair: AuthorityPair, + ocall_api: OcallApi, + parentchain_integritee_import_trigger: Arc, + maybe_parentchain_target_a_import_trigger: Option>, + maybe_parentchain_target_b_import_trigger: Option>, + environment: Environment, + scheduled_enclave: Arc, + state_handler: Arc, + ) -> Self { + Self { + authority_pair, + ocall_api, + parentchain_integritee_import_trigger, + maybe_parentchain_target_a_import_trigger, + maybe_parentchain_target_b_import_trigger, + environment, + claim_strategy: SlotClaimStrategy::RoundRobin, + scheduled_enclave, + state_handler, + _phantom: Default::default(), + } + } + + pub fn with_claim_strategy(mut self, claim_strategy: SlotClaimStrategy) -> Self { + self.claim_strategy = claim_strategy; + + self + } +} + +/// The fraction of total block time we are allowed to be producing the block. So that we have +/// enough time send create and send the block to fellow validateers. +pub const BLOCK_PROPOSAL_SLOT_PORTION: f32 = 0.7; + +#[derive(PartialEq, Eq, Debug)] +pub enum SlotClaimStrategy { + /// try to produce a block always even if it's not the authors slot + /// Intended for first phase to see if aura production works + Always, + /// Proper Aura strategy: Only produce blocks, when it's the authors slot. + RoundRobin, +} + +type AuthorityId

=

::Public; +type ShardIdentifierFor = + <<::Block as SidechainBlockTrait>::HeaderType as HeaderTrait>::ShardIdentifier; + +impl< + AuthorityPair, + ParentchainBlock, + SignedSidechainBlock, + E, + OcallApi, + IntegriteeImportTrigger, + TargetAImportTrigger, + TargetBImportTrigger, + ScheduledEnclave, + StateHandler, + > SimpleSlotWorker + for Aura< + AuthorityPair, + ParentchainBlock, + SignedSidechainBlock, + E, + OcallApi, + IntegriteeImportTrigger, + TargetAImportTrigger, + TargetBImportTrigger, + ScheduledEnclave, + StateHandler, + > where + AuthorityPair: Pair, + // todo: Relax hash trait bound, but this needs a change to some other parts in the code. + ParentchainBlock: ParentchainBlockTrait, + E: Environment, + E::Proposer: Proposer, + SignedSidechainBlock: SignedBlock + Send + 'static, + OcallApi: ValidateerFetch + EnclaveOnChainOCallApi + Send + 'static, + IntegriteeImportTrigger: + TriggerParentchainBlockImport>, + TargetAImportTrigger: + TriggerParentchainBlockImport>, + TargetBImportTrigger: + TriggerParentchainBlockImport>, + ScheduledEnclave: ScheduledEnclaveUpdater, + StateHandler: HandleState, +{ + type Proposer = E::Proposer; + type Claim = AuthorityPair::Public; + type EpochData = Vec>; + type Output = SignedSidechainBlock; + type ScheduledEnclave = ScheduledEnclave; + type StateHandler = StateHandler; + + fn logging_target(&self) -> &'static str { + "aura" + } + + fn get_scheduled_enclave(&mut self) -> Arc { + self.scheduled_enclave.clone() + } + + fn get_state_handler(&mut self) -> Arc { + self.state_handler.clone() + } + + fn epoch_data( + &self, + header: &ParentchainBlock::Header, + _shard: ShardIdentifierFor, + _slot: Slot, + ) -> Result { + authorities::<_, AuthorityPair, ParentchainBlock::Header>(&self.ocall_api, header) + } + + fn authorities_len(&self, epoch_data: &Self::EpochData) -> Option { + Some(epoch_data.len()) + } + + // While the header is not used in aura, it is used in different consensus systems, so it should be left there. + fn claim_slot( + &self, + _header: &ParentchainBlock::Header, + slot: Slot, + epoch_data: &Self::EpochData, + ) -> Option { + let expected_author = slot_author::(slot, epoch_data)?; + + if expected_author == &self.authority_pair.public() { + log::info!(target: self.logging_target(), "Claiming slot ({})", *slot); + return Some(self.authority_pair.public()) + } + + if self.claim_strategy == SlotClaimStrategy::Always { + log::debug!( + target: self.logging_target(), + "Not our slot but we still claim it." + ); + return Some(self.authority_pair.public()) + } + + None + } + + fn proposer( + &mut self, + header: ParentchainBlock::Header, + shard: ShardIdentifierFor, + ) -> Result { + self.environment.init(header, shard) + } + + fn proposing_remaining_duration(&self, slot_info: &SlotInfo) -> Duration { + proposing_remaining_duration(slot_info, duration_now()) + } + + // Design remark: the following may seem too explicit and it certainly could be abstracted. + // however, as pretty soon we may not want to assume same Block types for all parentchains, + // it may make sense to abstract once we do that. + + fn import_integritee_parentchain_blocks_until( + &self, + parentchain_header_hash: &::Hash, + ) -> Result, ConsensusError> { + log::trace!(target: self.logging_target(), "import Integritee blocks until {}", hex_encode(parentchain_header_hash.encode().as_ref())); + let maybe_parentchain_block = self + .parentchain_integritee_import_trigger + .import_until(|parentchain_block| { + parentchain_block.block.hash() == *parentchain_header_hash + }) + .map_err(|e| ConsensusError::Other(e.into()))?; + + Ok(maybe_parentchain_block.map(|b| b.block.header().clone())) + } + + fn import_target_a_parentchain_blocks_until( + &self, + parentchain_header_hash: &::Hash, + ) -> Result, ConsensusError> { + log::trace!(target: self.logging_target(), "import TargetA blocks until {}", hex_encode(parentchain_header_hash.encode().as_ref())); + let maybe_parentchain_block = self + .maybe_parentchain_target_a_import_trigger + .clone() + .ok_or_else(|| ConsensusError::Other("no target_a assigned".into()))? + .import_until(|parentchain_block| { + parentchain_block.block.hash() == *parentchain_header_hash + }) + .map_err(|e| ConsensusError::Other(e.into()))?; + + Ok(maybe_parentchain_block.map(|b| b.block.header().clone())) + } + + fn import_target_b_parentchain_blocks_until( + &self, + parentchain_header_hash: &::Hash, + ) -> Result, ConsensusError> { + log::trace!(target: self.logging_target(), "import TargetB blocks until {}", hex_encode(parentchain_header_hash.encode().as_ref())); + let maybe_parentchain_block = self + .maybe_parentchain_target_b_import_trigger + .clone() + .ok_or_else(|| ConsensusError::Other("no target_b assigned".into()))? + .import_until(|parentchain_block| { + parentchain_block.block.hash() == *parentchain_header_hash + }) + .map_err(|e| ConsensusError::Other(e.into()))?; + + Ok(maybe_parentchain_block.map(|b| b.block.header().clone())) + } + + fn peek_latest_integritee_parentchain_header( + &self, + ) -> Result, ConsensusError> { + let maybe_parentchain_block = self + .parentchain_integritee_import_trigger + .peek_latest() + .map_err(|e| ConsensusError::Other(format!("{:?}", e).into()))?; + + Ok(maybe_parentchain_block.map(|b| b.block.header().clone())) + } + + fn peek_latest_target_a_parentchain_header( + &self, + ) -> Result, ConsensusError> { + let maybe_parentchain_block = self + .maybe_parentchain_target_a_import_trigger + .clone() + .ok_or_else(|| ConsensusError::Other("no target_a assigned".into()))? + .peek_latest() + .map_err(|e| ConsensusError::Other(format!("{:?}", e).into()))?; + + Ok(maybe_parentchain_block.map(|b| b.block.header().clone())) + } + + fn peek_latest_target_b_parentchain_header( + &self, + ) -> Result, ConsensusError> { + let maybe_parentchain_block = self + .maybe_parentchain_target_b_import_trigger + .clone() + .ok_or_else(|| ConsensusError::Other("no target_b assigned".into()))? + .peek_latest() + .map_err(|e| ConsensusError::Other(format!("{:?}", e).into()))?; + + Ok(maybe_parentchain_block.map(|b| b.block.header().clone())) + } +} + +/// unit-testable remaining duration fn. +fn proposing_remaining_duration( + slot_info: &SlotInfo, + now: Duration, +) -> Duration { + // if a `now` before slot begin is passed such that `slot_remaining` would be bigger than `slot.slot_duration` + // we take the total `slot_duration` as reference value. + let proposing_duration = slot_info.duration.mul_f32(BLOCK_PROPOSAL_SLOT_PORTION); + + let slot_remaining = slot_info + .ends_at + .checked_sub(now) + .map(|remaining| remaining.mul_f32(BLOCK_PROPOSAL_SLOT_PORTION)) + .unwrap_or_default(); + + std::cmp::min(slot_remaining, proposing_duration) +} + +fn authorities( + ocall_api: &ValidateerFetcher, + header: &ParentchainHeader, +) -> Result>, ConsensusError> +where + ValidateerFetcher: ValidateerFetch + EnclaveOnChainOCallApi, + P: Pair, + ParentchainHeader: ParentchainHeaderTrait, +{ + Ok(ocall_api + .current_validateers(header) + .map_err(|e| ConsensusError::CouldNotGetAuthorities(e.to_string()))? + .into_iter() + .filter_map(|e| AuthorityId::

::from_slice(e.pubkey.as_ref()).ok()) + .collect()) +} + +pub enum AnyImportTrigger { + Integritee(Integritee), + TargetA(TargetA), + TargetB(TargetB), +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test::{ + fixtures::{types::TestAura, validateer, SLOT_DURATION}, + mocks::environment_mock::{EnvironmentMock, OutdatedBlockEnvironmentMock}, + }; + use itc_parentchain_block_import_dispatcher::trigger_parentchain_block_import_mock::TriggerParentchainBlockImportMock; + use itc_parentchain_test::{ParentchainBlockBuilder, ParentchainHeaderBuilder}; + use itp_test::mock::{handle_state_mock::HandleStateMock, onchain_mock::OnchainMock}; + use itp_types::{ + Block as ParentchainBlock, Enclave, Header as ParentchainHeader, ShardIdentifier, + SignedBlock as SignedParentchainBlock, + }; + use its_consensus_slots::PerShardSlotWorkerScheduler; + use lc_scheduled_enclave::ScheduledEnclaveMock; + use sp_core::ed25519::Public; + use sp_keyring::ed25519::Keyring; + + fn get_aura( + onchain_mock: OnchainMock, + trigger_parentchain_import: Arc>, + ) -> TestAura { + Aura::new( + Keyring::Alice.pair(), + onchain_mock, + trigger_parentchain_import, + None, + None, + EnvironmentMock, + Arc::new(ScheduledEnclaveMock::default()), + Arc::new(HandleStateMock::from_shard(ShardIdentifier::default()).unwrap()), + ) + } + + fn get_aura_outdated( + onchain_mock: OnchainMock, + trigger_parentchain_import: Arc>, + ) -> TestAura { + Aura::new( + Keyring::Alice.pair(), + onchain_mock, + trigger_parentchain_import, + None, + None, + OutdatedBlockEnvironmentMock, + Arc::new(ScheduledEnclaveMock::default()), + Arc::new(HandleStateMock::from_shard(ShardIdentifier::default()).unwrap()), + ) + } + + fn get_default_aura() -> TestAura { + get_aura(Default::default(), Default::default()) + } + + fn now_slot(slot: Slot, header: &ParentchainHeader) -> SlotInfo { + let now = duration_now(); + SlotInfo { + slot, + timestamp: now, + duration: SLOT_DURATION, + ends_at: now + SLOT_DURATION, + last_imported_integritee_parentchain_head: header.clone(), + maybe_last_imported_target_a_parentchain_head: None, + maybe_last_imported_target_b_parentchain_head: None, + } + } + + fn now_slot_with_default_header(slot: Slot) -> SlotInfo { + now_slot(slot, &ParentchainHeaderBuilder::default().build()) + } + + fn default_authorities() -> Vec { + vec![Keyring::Alice.public(), Keyring::Bob.public(), Keyring::Charlie.public()] + } + + fn create_validateer_set_from_publics(authorities: Vec) -> Vec { + authorities.iter().map(|a| validateer(a.clone().into())).collect() + } + + fn onchain_mock( + parentchain_header: &ParentchainHeader, + authorities: Vec, + ) -> OnchainMock { + let validateers = create_validateer_set_from_publics(authorities); + OnchainMock::default().add_validateer_set(parentchain_header, Some(validateers)) + } + + fn onchain_mock_with_default_authorities_and_header() -> OnchainMock { + let parentchain_header = ParentchainHeaderBuilder::default().build(); + onchain_mock(&parentchain_header, default_authorities()) + } + + fn create_import_trigger_with_header( + header: ParentchainHeader, + ) -> Arc> { + let latest_parentchain_block = + ParentchainBlockBuilder::default().with_header(header).build_signed(); + Arc::new( + TriggerParentchainBlockImportMock::default() + .with_latest_imported(Some(latest_parentchain_block)), + ) + } + + #[test] + fn current_authority_should_claim_its_slot() { + let authorities = + vec![Keyring::Bob.public(), Keyring::Charlie.public(), Keyring::Alice.public()]; + let aura = get_default_aura(); + let header = ParentchainHeaderBuilder::default().build(); + + assert!(aura.claim_slot(&header, 0.into(), &authorities).is_none()); + assert!(aura.claim_slot(&header, 1.into(), &authorities).is_none()); + // this our authority + assert!(aura.claim_slot(&header, 2.into(), &authorities).is_some()); + + assert!(aura.claim_slot(&header, 3.into(), &authorities).is_none()); + assert!(aura.claim_slot(&header, 4.into(), &authorities).is_none()); + // this our authority + assert!(aura.claim_slot(&header, 5.into(), &authorities).is_some()); + } + + #[test] + fn current_authority_should_claim_all_slots() { + let header = ParentchainHeaderBuilder::default().build(); + let authorities = default_authorities(); + let aura = get_default_aura().with_claim_strategy(SlotClaimStrategy::Always); + + assert!(aura.claim_slot(&header, 0.into(), &authorities).is_some()); + assert!(aura.claim_slot(&header, 1.into(), &authorities).is_some()); + // this our authority + assert!(aura.claim_slot(&header, 2.into(), &authorities).is_some()); + assert!(aura.claim_slot(&header, 3.into(), &authorities).is_some()); + } + + #[test] + fn on_slot_returns_block() { + let _ = env_logger::builder().is_test(true).try_init(); + + let onchain_mock = onchain_mock_with_default_authorities_and_header(); + let mut aura = get_aura(onchain_mock, Default::default()); + + let slot_info = now_slot_with_default_header(0.into()); + + assert!( + SimpleSlotWorker::on_slot(&mut aura, slot_info, Default::default(), false).is_some() + ); + } + + #[test] + fn on_slot_returns_no_block_if_slot_time_exceeded_for_multi_worker() { + let _ = env_logger::builder().is_test(true).try_init(); + + let onchain_mock = onchain_mock_with_default_authorities_and_header(); + let mut aura = get_aura_outdated(onchain_mock, Default::default()); + let slot_info = now_slot_with_default_header(0.into()); + + assert!( + SimpleSlotWorker::on_slot(&mut aura, slot_info, Default::default(), false).is_none() + ); + } + + #[test] + fn on_slot_returns_block_if_slot_time_exceeded_for_single_worker() { + let _ = env_logger::builder().is_test(true).try_init(); + + let onchain_mock = onchain_mock_with_default_authorities_and_header(); + let mut aura = get_aura_outdated(onchain_mock, Default::default()); + let slot_info = now_slot_with_default_header(0.into()); + + assert!(SimpleSlotWorker::on_slot(&mut aura, slot_info, Default::default(), true).is_some()); + } + + #[test] + fn on_slot_for_multiple_shards_returns_blocks() { + let _ = env_logger::builder().is_test(true).try_init(); + + let onchain_mock = onchain_mock_with_default_authorities_and_header(); + let mut aura = get_aura(onchain_mock, Default::default()); + + let slot_info = now_slot_with_default_header(0.into()); + + let result = PerShardSlotWorkerScheduler::on_slot( + &mut aura, + slot_info, + vec![Default::default(), Default::default()], + false, + ); + + assert_eq!(result.len(), 2); + } + + #[test] + fn on_slot_with_nano_second_remaining_duration_does_not_panic() { + let _ = env_logger::builder().is_test(true).try_init(); + + let mut aura = get_default_aura(); + + let nano_dur = Duration::from_nanos(999); + let now = duration_now(); + + let slot_info = SlotInfo { + slot: 0.into(), + timestamp: now, + duration: nano_dur, + ends_at: now + nano_dur, + last_imported_integritee_parentchain_head: ParentchainHeaderBuilder::default().build(), + maybe_last_imported_target_a_parentchain_head: None, + maybe_last_imported_target_b_parentchain_head: None, + }; + + let result = PerShardSlotWorkerScheduler::on_slot( + &mut aura, + slot_info, + vec![Default::default(), Default::default()], + false, + ); + + assert_eq!(result.len(), 0); + } + + #[test] + fn on_slot_triggers_parentchain_block_import_if_slot_is_claimed() { + let _ = env_logger::builder().is_test(true).try_init(); + let latest_parentchain_header = ParentchainHeaderBuilder::default().with_number(84).build(); + let parentchain_block_import_trigger = + create_import_trigger_with_header(latest_parentchain_header.clone()); + let authorities = default_authorities(); + + let mut aura = get_aura( + onchain_mock(&latest_parentchain_header, authorities), + parentchain_block_import_trigger.clone(), + ); + + let slot_info = now_slot(0.into(), &latest_parentchain_header); + + let result = + SimpleSlotWorker::on_slot(&mut aura, slot_info, Default::default(), false).unwrap(); + + assert_eq!( + result.block.block.block_data().layer_one_head, + latest_parentchain_header.hash() + ); + assert!(parentchain_block_import_trigger.has_import_been_called()); + } + + #[test] + fn on_slot_does_not_trigger_parentchain_block_import_if_slot_is_not_claimed() { + let _ = env_logger::builder().is_test(true).try_init(); + let latest_parentchain_header = ParentchainHeaderBuilder::default().with_number(84).build(); + let parentchain_block_import_trigger = + create_import_trigger_with_header(latest_parentchain_header.clone()); + let authorities = default_authorities(); + + let mut aura = get_aura( + onchain_mock(&latest_parentchain_header, authorities), + parentchain_block_import_trigger.clone(), + ); + + let slot_info = now_slot(2.into(), &latest_parentchain_header); + + let result = SimpleSlotWorker::on_slot(&mut aura, slot_info, Default::default(), false); + + assert!(result.is_none()); + assert!(!parentchain_block_import_trigger.has_import_been_called()); + } + + #[test] + fn on_slot_claims_slot_if_latest_parentchain_header_in_queue_contains_correspondent_validateer_set( + ) { + let _ = env_logger::builder().is_test(true).try_init(); + let already_imported_parentchain_header = + ParentchainHeaderBuilder::default().with_number(84).build(); + let latest_parentchain_header = ParentchainHeaderBuilder::default().with_number(85).build(); + let parentchain_block_import_trigger = + create_import_trigger_with_header(latest_parentchain_header.clone()); + let validateer_set_one = create_validateer_set_from_publics(vec![ + Keyring::Alice.public(), + Keyring::Bob.public(), + ]); + let validateer_set_two = create_validateer_set_from_publics(vec![ + Keyring::Alice.public(), + Keyring::Bob.public(), + Keyring::Charlie.public(), + ]); + let onchain_mock = OnchainMock::default() + .add_validateer_set(&already_imported_parentchain_header, Some(validateer_set_one)) + .add_validateer_set(&latest_parentchain_header, Some(validateer_set_two)); + + let mut aura = get_aura(onchain_mock, parentchain_block_import_trigger.clone()); + + let slot_info = now_slot(3.into(), &already_imported_parentchain_header); + + let result = + SimpleSlotWorker::on_slot(&mut aura, slot_info, Default::default(), false).unwrap(); + + assert_eq!( + result.block.block.block_data().layer_one_head, + latest_parentchain_header.hash() + ); + assert!(parentchain_block_import_trigger.has_import_been_called()); + } + + #[test] + fn on_slot_does_not_claim_slot_if_latest_parentchain_header_in_queue_contains_correspondent_validateer_set( + ) { + let _ = env_logger::builder().is_test(true).try_init(); + let already_imported_parentchain_header = + ParentchainHeaderBuilder::default().with_number(84).build(); + let latest_parentchain_header = ParentchainHeaderBuilder::default().with_number(85).build(); + let parentchain_block_import_trigger = + create_import_trigger_with_header(latest_parentchain_header.clone()); + let validateer_set_one = create_validateer_set_from_publics(vec![ + Keyring::Alice.public(), + Keyring::Bob.public(), + ]); + let validateer_set_two = create_validateer_set_from_publics(vec![ + Keyring::Alice.public(), + Keyring::Bob.public(), + Keyring::Charlie.public(), + ]); + let onchain_mock = OnchainMock::default() + .add_validateer_set(&already_imported_parentchain_header, Some(validateer_set_one)) + .add_validateer_set(&latest_parentchain_header, Some(validateer_set_two)); + + let mut aura = get_aura(onchain_mock, parentchain_block_import_trigger.clone()); + + // If the validateer set one (instead of the latest one) is looked up, the slot will be claimed. But it should not, as the latest one should be used. + let slot_info = now_slot(2.into(), &already_imported_parentchain_header); + let result = SimpleSlotWorker::on_slot(&mut aura, slot_info, Default::default(), false); + + assert!(result.is_none()); + assert!(!parentchain_block_import_trigger.has_import_been_called()); + } + + #[test] + fn proposing_remaining_duration_works() { + let slot_info = now_slot_with_default_header(0.into()); + + // hard to compare actual numbers but we can at least ensure that the general concept works + assert!( + proposing_remaining_duration(&slot_info, duration_now()) + < SLOT_DURATION.mul_f32(BLOCK_PROPOSAL_SLOT_PORTION + 0.01) + ); + } + + #[test] + fn proposing_remaining_duration_works_for_now_before_slot_timestamp() { + let slot_info = now_slot_with_default_header(0.into()); + + assert!( + proposing_remaining_duration(&slot_info, Duration::from_millis(0)) + < SLOT_DURATION.mul_f32(BLOCK_PROPOSAL_SLOT_PORTION + 0.01) + ); + } + + #[test] + fn proposing_remaining_duration_returns_default_if_now_after_slot() { + let slot_info = now_slot_with_default_header(0.into()); + + assert_eq!( + proposing_remaining_duration(&slot_info, duration_now() + SLOT_DURATION), + Default::default() + ); + } +} diff --git a/bitacross-worker/sidechain/consensus/aura/src/proposer_factory.rs b/bitacross-worker/sidechain/consensus/aura/src/proposer_factory.rs new file mode 100644 index 0000000000..61fa21557f --- /dev/null +++ b/bitacross-worker/sidechain/consensus/aura/src/proposer_factory.rs @@ -0,0 +1,131 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::slot_proposer::{ExternalitiesFor, SlotProposer}; +use codec::Encode; +use finality_grandpa::BlockNumberOps; +use ita_stf::{Getter, TrustedCallSigned}; +use itp_ocall_api::EnclaveMetricsOCallApi; +use itp_sgx_externalities::{SgxExternalitiesTrait, StateHash}; +use itp_stf_executor::traits::StateUpdateProposer; +use itp_top_pool_author::traits::AuthorApi; +use itp_types::H256; +use its_block_composer::ComposeBlock; +use its_consensus_common::{Environment, Error as ConsensusError}; +use its_primitives::traits::{ + Block as SidechainBlockTrait, Header as HeaderTrait, ShardIdentifierFor, + SignedBlock as SignedSidechainBlockTrait, +}; +use its_state::{SidechainState, SidechainSystemExt}; +use sp_runtime::{ + traits::{Block, NumberFor}, + MultiSignature, +}; +use std::{marker::PhantomData, sync::Arc}; + +///! `ProposerFactory` instance containing all the data to create the `SlotProposer` for the +/// next `Slot`. +pub struct ProposerFactory< + ParentchainBlock: Block, + TopPoolAuthor, + StfExecutor, + BlockComposer, + MetricsApi, +> { + top_pool_author: Arc, + stf_executor: Arc, + block_composer: Arc, + metrics_api: Arc, + _phantom: PhantomData, +} + +impl + ProposerFactory +{ + pub fn new( + top_pool_executor: Arc, + stf_executor: Arc, + block_composer: Arc, + metrics_api: Arc, + ) -> Self { + Self { + top_pool_author: top_pool_executor, + stf_executor, + block_composer, + metrics_api, + _phantom: Default::default(), + } + } +} + +impl< + ParentchainBlock: Block, + SignedSidechainBlock, + TopPoolAuthor, + StfExecutor, + BlockComposer, + MetricsApi, + > Environment + for ProposerFactory +where + NumberFor: BlockNumberOps, + SignedSidechainBlock: SignedSidechainBlockTrait + + 'static, + SignedSidechainBlock::Block: SidechainBlockTrait, + <::Block as SidechainBlockTrait>::HeaderType: + HeaderTrait, + TopPoolAuthor: + AuthorApi + Send + Sync + 'static, + StfExecutor: StateUpdateProposer + Send + Sync + 'static, + ExternalitiesFor: + SgxExternalitiesTrait + SidechainState + SidechainSystemExt + StateHash, + as SgxExternalitiesTrait>::SgxExternalitiesType: Encode, + BlockComposer: ComposeBlock< + ExternalitiesFor, + ParentchainBlock, + SignedSidechainBlock = SignedSidechainBlock, + > + Send + + Sync + + 'static, + MetricsApi: EnclaveMetricsOCallApi, +{ + type Proposer = SlotProposer< + ParentchainBlock, + SignedSidechainBlock, + TopPoolAuthor, + StfExecutor, + BlockComposer, + MetricsApi, + >; + type Error = ConsensusError; + + fn init( + &mut self, + parent_header: ParentchainBlock::Header, + shard: ShardIdentifierFor, + ) -> Result { + Ok(SlotProposer { + top_pool_author: self.top_pool_author.clone(), + stf_executor: self.stf_executor.clone(), + block_composer: self.block_composer.clone(), + parentchain_header: parent_header, + shard, + metrics_api: self.metrics_api.clone(), + _phantom: PhantomData, + }) + } +} diff --git a/bitacross-worker/sidechain/consensus/aura/src/slot_proposer.rs b/bitacross-worker/sidechain/consensus/aura/src/slot_proposer.rs new file mode 100644 index 0000000000..2baea76519 --- /dev/null +++ b/bitacross-worker/sidechain/consensus/aura/src/slot_proposer.rs @@ -0,0 +1,206 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use codec::Encode; +use finality_grandpa::BlockNumberOps; +use ita_stf::{Getter, TrustedCallSigned}; +use itp_enclave_metrics::EnclaveMetric; +use itp_ocall_api::EnclaveMetricsOCallApi; +use itp_sgx_externalities::{SgxExternalitiesTrait, StateHash}; +use itp_stf_executor::traits::StateUpdateProposer; +use itp_time_utils::now_as_millis; +use itp_top_pool_author::traits::AuthorApi; +use itp_types::H256; +use its_block_composer::ComposeBlock; +use its_consensus_common::{Error as ConsensusError, Proposal, Proposer}; +use its_primitives::traits::{ + Block as SidechainBlockTrait, Header as HeaderTrait, ShardIdentifierFor, + SignedBlock as SignedSidechainBlockTrait, +}; +use its_state::{SidechainState, SidechainSystemExt}; +use log::*; +use sp_runtime::{ + traits::{Block, NumberFor}, + MultiSignature, +}; +use std::{marker::PhantomData, string::ToString, sync::Arc, time::Duration, vec::Vec}; + +pub type ExternalitiesFor = >::Externalities; +///! `SlotProposer` instance that has access to everything needed to propose a sidechain block. +pub struct SlotProposer< + ParentchainBlock: Block, + SignedSidechainBlock: SignedSidechainBlockTrait, + TopPoolAuthor, + StfExecutor, + BlockComposer, + MetricsApi, +> { + pub(crate) top_pool_author: Arc, + pub(crate) stf_executor: Arc, + pub(crate) block_composer: Arc, + pub(crate) parentchain_header: ParentchainBlock::Header, + pub(crate) shard: ShardIdentifierFor, + pub(crate) metrics_api: Arc, + pub(crate) _phantom: PhantomData, +} + +impl< + ParentchainBlock, + SignedSidechainBlock, + TopPoolAuthor, + BlockComposer, + StfExecutor, + MetricsApi, + > Proposer + for SlotProposer< + ParentchainBlock, + SignedSidechainBlock, + TopPoolAuthor, + StfExecutor, + BlockComposer, + MetricsApi, + > where + ParentchainBlock: Block, + NumberFor: BlockNumberOps, + SignedSidechainBlock: SignedSidechainBlockTrait + + 'static, + SignedSidechainBlock::Block: SidechainBlockTrait, + <::Block as SidechainBlockTrait>::HeaderType: + HeaderTrait, + StfExecutor: StateUpdateProposer, + ExternalitiesFor: + SgxExternalitiesTrait + SidechainState + SidechainSystemExt + StateHash, + as SgxExternalitiesTrait>::SgxExternalitiesType: Encode, + TopPoolAuthor: + AuthorApi + Send + Sync + 'static, + BlockComposer: ComposeBlock< + ExternalitiesFor, + ParentchainBlock, + SignedSidechainBlock = SignedSidechainBlock, + > + Send + + Sync + + 'static, + MetricsApi: EnclaveMetricsOCallApi, +{ + /// Proposes a new sidechain block. + /// + /// This includes the following steps: + /// 1) Retrieve all trusted calls from the top pool. + /// 2) Calculate a new state that will be proposed in the sidechain block. + /// 3) Compose the sidechain block and the parentchain confirmation. + fn propose( + &self, + max_duration: Duration, + ) -> Result, ConsensusError> { + let mut started = std::time::Instant::now(); + let latest_parentchain_header = &self.parentchain_header; + + // 1) Retrieve trusted calls from top pool. + let trusted_calls = self.top_pool_author.get_pending_trusted_calls(self.shard); + + if !trusted_calls.is_empty() { + debug!("Got following trusted calls from pool: {:?}", trusted_calls); + } + + if let Err(e) = self + .metrics_api + .update_metric(EnclaveMetric::SidechainSlotPrepareTime(started.elapsed())) + { + warn!("Failed to update metric for sidechain slot prepare time: {:?}", e); + }; + + started = std::time::Instant::now(); + // 2) Execute trusted calls. + let batch_execution_result = self + .stf_executor + .propose_state_update( + &trusted_calls, + latest_parentchain_header, + &self.shard, + max_duration, + |mut sidechain_db| { + sidechain_db.reset_events(); + sidechain_db + .set_block_number(&sidechain_db.get_block_number().map_or(1, |n| n + 1)); + sidechain_db.set_timestamp(&now_as_millis()); + sidechain_db.set_parentchain_block_number(latest_parentchain_header); + sidechain_db + }, + ) + .map_err(|e| ConsensusError::Other(e.to_string().into()))?; + + let parentchain_extrinsics = batch_execution_result.get_extrinsic_callbacks(); + + let executed_operation_hashes: Vec<_> = + batch_execution_result.get_executed_operation_hashes().to_vec(); + let number_executed_transactions = executed_operation_hashes.len(); + + // store the rpc response value to top pool + let rpc_responses_values = batch_execution_result.get_connection_updates(); + self.top_pool_author.update_connection_state(rpc_responses_values); + + // Remove all not successfully executed operations from the top pool. + let failed_operations = batch_execution_result.get_failed_operations(); + self.top_pool_author.remove_calls_from_pool( + self.shard, + failed_operations + .into_iter() + .map(|e| { + let is_success = e.is_success(); + (e.trusted_operation_or_hash, is_success) + }) + .collect(), + ); + + if let Err(e) = self + .metrics_api + .update_metric(EnclaveMetric::SidechainSlotStfExecutionTime(started.elapsed())) + { + warn!("Failed to update metric for sidechain slot stf execution time: {:?}", e); + }; + + started = std::time::Instant::now(); + + // 3) Compose sidechain block. + let sidechain_block = self + .block_composer + .compose_block( + latest_parentchain_header, + executed_operation_hashes, + self.shard, + batch_execution_result.state_hash_before_execution, + &batch_execution_result.state_after_execution, + ) + .map_err(|e| ConsensusError::Other(e.to_string().into()))?; + + if let Err(e) = self + .metrics_api + .update_metric(EnclaveMetric::SidechainSlotBlockCompositionTime(started.elapsed())) + { + warn!("Failed to update metric for sidechain slot block composition time: {:?}", e); + }; + + info!( + "Queue/Timeslot/Transactions: {:?};{}ms;{}", + trusted_calls.len(), + max_duration.as_millis(), + number_executed_transactions + ); + + Ok(Proposal { block: sidechain_block, parentchain_effects: parentchain_extrinsics }) + } +} diff --git a/bitacross-worker/sidechain/consensus/aura/src/test/block_importer_tests.rs b/bitacross-worker/sidechain/consensus/aura/src/test/block_importer_tests.rs new file mode 100644 index 0000000000..447aa18c62 --- /dev/null +++ b/bitacross-worker/sidechain/consensus/aura/src/test/block_importer_tests.rs @@ -0,0 +1,318 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + block_importer::BlockImporter, + test::{fixtures::validateer, mocks::peer_updater_mock::PeerUpdaterMock}, + ShardIdentifierFor, +}; +use codec::Encode; +use core::assert_matches::assert_matches; +use itc_parentchain_block_import_dispatcher::trigger_parentchain_block_import_mock::TriggerParentchainBlockImportMock; +use itc_parentchain_test::{ParentchainBlockBuilder, ParentchainHeaderBuilder}; +use itp_sgx_crypto::{aes::Aes, mocks::KeyRepositoryMock, StateCrypto}; +use itp_sgx_externalities::SgxExternalitiesDiffType; +use itp_stf_state_handler::handle_state::HandleState; +use itp_test::mock::{ + handle_state_mock::HandleStateMock, + onchain_mock::OnchainMock, + stf_mock::{GetterMock, TrustedCallSignedMock}, +}; +use itp_time_utils::{duration_now, now_as_millis}; +use itp_top_pool_author::mocks::AuthorApiMock; +use itp_types::{Block as ParentchainBlock, Header as ParentchainHeader, H256}; +use its_consensus_common::{BlockImport, Error as ConsensusError}; +use its_primitives::{ + traits::{SignBlock, SignedBlock}, + types::SignedBlock as SignedSidechainBlock, +}; +use its_state::StateUpdate; +use its_test::{ + sidechain_block_builder::{SidechainBlockBuilder, SidechainBlockBuilderTrait}, + sidechain_block_data_builder::SidechainBlockDataBuilder, + sidechain_header_builder::SidechainHeaderBuilder, +}; +use sp_core::{blake2_256, ed25519::Pair}; +use sp_keyring::ed25519::Keyring; +use sp_runtime::generic::SignedBlock as SignedParentchainBlock; +use std::sync::Arc; + +type TestTopPoolAuthor = AuthorApiMock; +type TestParentchainBlockImportTrigger = + TriggerParentchainBlockImportMock>; +type TestStateKeyRepo = KeyRepositoryMock; +type TestBlockImporter = BlockImporter< + Pair, + ParentchainBlock, + SignedSidechainBlock, + OnchainMock, + HandleStateMock, + TestStateKeyRepo, + TestTopPoolAuthor, + TestParentchainBlockImportTrigger, + PeerUpdaterMock, + TrustedCallSignedMock, + GetterMock, +>; + +fn state_key() -> Aes { + Aes::new([3u8; 16], [0u8; 16]) +} + +fn shard() -> ShardIdentifierFor { + blake2_256(&[1, 2, 3, 4, 5, 6]).into() +} + +fn default_authority() -> Pair { + Keyring::Alice.pair() +} + +fn test_fixtures( + parentchain_header: &ParentchainHeader, + parentchain_block_import_trigger: Arc, +) -> (TestBlockImporter, Arc, Arc) { + let state_handler = Arc::new(HandleStateMock::from_shard(shard()).unwrap()); + let top_pool_author = Arc::new(TestTopPoolAuthor::default()); + let ocall_api = Arc::new(OnchainMock::default().add_validateer_set( + parentchain_header, + Some(vec![validateer(Keyring::Alice.public().into())]), + )); + let state_key_repository = Arc::new(TestStateKeyRepo::new(state_key())); + + let peer_updater_mock = Arc::new(PeerUpdaterMock {}); + + let block_importer = TestBlockImporter::new( + state_handler.clone(), + state_key_repository, + top_pool_author.clone(), + parentchain_block_import_trigger, + ocall_api, + peer_updater_mock, + ); + + (block_importer, state_handler, top_pool_author) +} + +fn test_fixtures_with_default_import_trigger( + parentchain_header: &ParentchainHeader, +) -> (TestBlockImporter, Arc, Arc) { + test_fixtures(parentchain_header, Arc::new(TestParentchainBlockImportTrigger::default())) +} + +fn empty_encrypted_state_update(state_handler: &HandleStateMock) -> Vec { + let (_, apriori_state_hash) = state_handler.load_cloned(&shard()).unwrap(); + let empty_state_diff = SgxExternalitiesDiffType::default(); + let mut state_update = + StateUpdate::new(apriori_state_hash, apriori_state_hash, empty_state_diff).encode(); + state_key().encrypt(&mut state_update).unwrap(); + state_update +} + +fn signed_block( + parentchain_header: &ParentchainHeader, + state_handler: &HandleStateMock, + signer: Pair, +) -> SignedSidechainBlock { + let state_update = empty_encrypted_state_update(state_handler); + + let header = SidechainHeaderBuilder::default() + .with_parent_hash(H256::default()) + .with_shard(shard()) + .build(); + + let block_data = SidechainBlockDataBuilder::default() + .with_timestamp(now_as_millis()) + .with_layer_one_head(parentchain_header.hash()) + .with_signer(signer.clone()) + .with_payload(state_update) + .build(); + + SidechainBlockBuilder::default() + .with_header(header) + .with_block_data(block_data) + .with_signer(signer) + .build_signed() +} + +fn default_authority_signed_block( + parentchain_header: &ParentchainHeader, + state_handler: &HandleStateMock, +) -> SignedSidechainBlock { + signed_block(parentchain_header, state_handler, default_authority()) +} + +#[test] +fn simple_block_import_works() { + let parentchain_header = ParentchainHeaderBuilder::default().build(); + let (block_importer, state_handler, _) = + test_fixtures_with_default_import_trigger(&parentchain_header); + let signed_sidechain_block = + default_authority_signed_block(&parentchain_header, state_handler.as_ref()); + + block_importer + .import_block(signed_sidechain_block, &parentchain_header) + .unwrap(); +} + +#[test] +fn block_import_with_invalid_signature_fails() { + let parentchain_header = ParentchainHeaderBuilder::default().build(); + let (block_importer, state_handler, _) = + test_fixtures_with_default_import_trigger(&parentchain_header); + + let state_update = empty_encrypted_state_update(state_handler.as_ref()); + + let header = SidechainHeaderBuilder::default() + .with_parent_hash(H256::default()) + .with_shard(shard()) + .build(); + + let block_data = SidechainBlockDataBuilder::default() + .with_timestamp(duration_now().as_millis() as u64) + .with_layer_one_head(parentchain_header.hash()) + .with_signer(Keyring::Charlie.pair()) + .with_payload(state_update) + .build(); + + let block = SidechainBlockBuilder::default() + .with_signer(Keyring::Charlie.pair()) + .with_header(header) + .with_block_data(block_data) + .build(); + + // Bob signs the block, but Charlie is set as the author -> invalid signature. + let invalid_signature_block: SignedSidechainBlock = block.sign_block(&Keyring::Bob.pair()); + + assert!(!invalid_signature_block.verify_signature()); + assert!(block_importer + .import_block(invalid_signature_block, &parentchain_header) + .is_err()); +} + +#[test] +fn block_import_with_invalid_parentchain_block_fails() { + let parentchain_header_invalid = ParentchainHeaderBuilder::default().with_number(2).build(); + let parentchain_header = ParentchainHeaderBuilder::default().with_number(10).build(); + let (block_importer, state_handler, _) = + test_fixtures_with_default_import_trigger(&parentchain_header); + + let signed_sidechain_block = + default_authority_signed_block(&parentchain_header_invalid, state_handler.as_ref()); + + assert!(block_importer + .import_block(signed_sidechain_block, &parentchain_header) + .is_err()); +} + +#[test] +fn cleanup_removes_tops_from_pool() { + let parentchain_header = ParentchainHeaderBuilder::default().build(); + let (block_importer, state_handler, top_pool_author) = + test_fixtures_with_default_import_trigger(&parentchain_header); + let signed_sidechain_block = + default_authority_signed_block(&parentchain_header, state_handler.as_ref()); + let bob_signed_sidechain_block = + signed_block(&parentchain_header, state_handler.as_ref(), Keyring::Bob.pair()); + + block_importer.cleanup(&signed_sidechain_block).unwrap(); + block_importer.cleanup(&bob_signed_sidechain_block).unwrap(); + + assert_eq!(2, *top_pool_author.remove_attempts.read().unwrap()); +} + +#[test] +fn sidechain_block_import_triggers_parentchain_block_import() { + let previous_parentchain_header = ParentchainHeaderBuilder::default().with_number(4).build(); + let latest_parentchain_header = ParentchainHeaderBuilder::default() + .with_number(5) + .with_parent_hash(previous_parentchain_header.hash()) + .build(); + + let latest_parentchain_block = ParentchainBlockBuilder::default() + .with_header(latest_parentchain_header.clone()) + .build_signed(); + + let parentchain_block_import_trigger = Arc::new( + TestParentchainBlockImportTrigger::default() + .with_latest_imported(Some(latest_parentchain_block)), + ); + let (block_importer, state_handler, _) = + test_fixtures(&latest_parentchain_header, parentchain_block_import_trigger.clone()); + + let signed_sidechain_block = + default_authority_signed_block(&latest_parentchain_header, state_handler.as_ref()); + + block_importer + .import_block(signed_sidechain_block, &previous_parentchain_header) + .unwrap(); + + assert!(parentchain_block_import_trigger.has_import_been_called()); +} + +#[test] +fn peek_parentchain_block_finds_block_in_queue() { + let previous_parentchain_header = ParentchainHeaderBuilder::default().with_number(4).build(); + let latest_parentchain_header = ParentchainHeaderBuilder::default() + .with_number(5) + .with_parent_hash(previous_parentchain_header.hash()) + .build(); + + let latest_parentchain_block = ParentchainBlockBuilder::default() + .with_header(latest_parentchain_header.clone()) + .build_signed(); + + let parentchain_block_import_trigger = Arc::new( + TestParentchainBlockImportTrigger::default() + .with_latest_imported(Some(latest_parentchain_block)), + ); + + let (block_importer, state_handler, _) = + test_fixtures(&latest_parentchain_header, parentchain_block_import_trigger); + + let signed_sidechain_block = + default_authority_signed_block(&latest_parentchain_header, state_handler.as_ref()); + + let peeked_header = block_importer + .peek_parentchain_header(&signed_sidechain_block.block, &previous_parentchain_header) + .unwrap(); + + assert_eq!(peeked_header, latest_parentchain_header); +} + +#[test] +fn peek_parentchain_block_returns_error_if_no_corresponding_block_can_be_found() { + let previous_parentchain_header = ParentchainHeaderBuilder::default().with_number(1).build(); + let latest_parentchain_header = ParentchainHeaderBuilder::default() + .with_number(2) + .with_parent_hash(previous_parentchain_header.hash()) + .build(); + + let parentchain_block_import_trigger = Arc::new( + TestParentchainBlockImportTrigger::default(), // Parentchain block import queue is empty, so nothing will be found when peeked. + ); + + let (block_importer, state_handler, _) = + test_fixtures(&latest_parentchain_header, parentchain_block_import_trigger); + + let signed_sidechain_block = + default_authority_signed_block(&latest_parentchain_header, state_handler.as_ref()); + + let peek_result = block_importer + .peek_parentchain_header(&signed_sidechain_block.block, &previous_parentchain_header); + + assert_matches!(peek_result, Err(ConsensusError::Other(_))); +} diff --git a/bitacross-worker/sidechain/consensus/aura/src/test/fixtures/mod.rs b/bitacross-worker/sidechain/consensus/aura/src/test/fixtures/mod.rs new file mode 100644 index 0000000000..54d47324fa --- /dev/null +++ b/bitacross-worker/sidechain/consensus/aura/src/test/fixtures/mod.rs @@ -0,0 +1,27 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pub mod types; + +use itp_types::{AccountId, Enclave}; +use std::time::Duration; + +pub const SLOT_DURATION: Duration = Duration::from_millis(300); + +pub fn validateer(account: AccountId) -> Enclave { + Enclave::new(account, Default::default(), Default::default(), Default::default()) +} diff --git a/bitacross-worker/sidechain/consensus/aura/src/test/fixtures/types.rs b/bitacross-worker/sidechain/consensus/aura/src/test/fixtures/types.rs new file mode 100644 index 0000000000..b5eae68582 --- /dev/null +++ b/bitacross-worker/sidechain/consensus/aura/src/test/fixtures/types.rs @@ -0,0 +1,48 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{test::mocks::environment_mock::EnvironmentMock, Aura}; +use itc_parentchain_block_import_dispatcher::trigger_parentchain_block_import_mock::TriggerParentchainBlockImportMock; +use itp_test::mock::{handle_state_mock::HandleStateMock, onchain_mock::OnchainMock}; +use itp_types::Block as ParentchainBlock; +use its_primitives::{ + traits::{ + Block as SidechainBlockTrait, Header as SidechainHeaderTrait, + SignedBlock as SignedBlockTrait, + }, + types::block::SignedBlock as SignedSidechainBlock, +}; +use lc_scheduled_enclave::ScheduledEnclaveMock; +use sp_runtime::{app_crypto::ed25519, generic::SignedBlock}; + +type AuthorityPair = ed25519::Pair; + +pub type ShardIdentifierFor = + <<::Block as SidechainBlockTrait>::HeaderType as SidechainHeaderTrait>::ShardIdentifier; + +pub type TestAura = Aura< + AuthorityPair, + ParentchainBlock, + SignedSidechainBlock, + E, + OnchainMock, + TriggerParentchainBlockImportMock>, + TriggerParentchainBlockImportMock>, + TriggerParentchainBlockImportMock>, + ScheduledEnclaveMock, + HandleStateMock, +>; diff --git a/bitacross-worker/sidechain/consensus/aura/src/test/mocks/environment_mock.rs b/bitacross-worker/sidechain/consensus/aura/src/test/mocks/environment_mock.rs new file mode 100644 index 0000000000..58f98d3687 --- /dev/null +++ b/bitacross-worker/sidechain/consensus/aura/src/test/mocks/environment_mock.rs @@ -0,0 +1,58 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + test::{ + fixtures::types::ShardIdentifierFor, + mocks::proposer_mock::{DefaultProposerMock, OutdatedBlockProposerMock}, + }, + ConsensusError, +}; +use itp_types::{Block as ParentchainBlock, Header}; +use its_consensus_common::Environment; +use its_primitives::types::block::SignedBlock as SignedSidechainBlock; + +/// Mock proposer environment. +pub struct EnvironmentMock; + +impl Environment for EnvironmentMock { + type Proposer = DefaultProposerMock; + type Error = ConsensusError; + + fn init( + &mut self, + header: Header, + _: ShardIdentifierFor, + ) -> Result { + Ok(DefaultProposerMock { parentchain_header: header }) + } +} + +pub struct OutdatedBlockEnvironmentMock; + +impl Environment for OutdatedBlockEnvironmentMock { + type Proposer = OutdatedBlockProposerMock; + type Error = ConsensusError; + + fn init( + &mut self, + header: Header, + _: ShardIdentifierFor, + ) -> Result { + Ok(OutdatedBlockProposerMock { parentchain_header: header }) + } +} diff --git a/bitacross-worker/sidechain/consensus/aura/src/test/mocks/mod.rs b/bitacross-worker/sidechain/consensus/aura/src/test/mocks/mod.rs new file mode 100644 index 0000000000..f5c7248d2f --- /dev/null +++ b/bitacross-worker/sidechain/consensus/aura/src/test/mocks/mod.rs @@ -0,0 +1,20 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pub mod environment_mock; +pub mod peer_updater_mock; +pub mod proposer_mock; diff --git a/bitacross-worker/sidechain/consensus/aura/src/test/mocks/peer_updater_mock.rs b/bitacross-worker/sidechain/consensus/aura/src/test/mocks/peer_updater_mock.rs new file mode 100644 index 0000000000..b474e825c1 --- /dev/null +++ b/bitacross-worker/sidechain/consensus/aura/src/test/mocks/peer_updater_mock.rs @@ -0,0 +1,7 @@ +use itc_peer_top_broadcaster::PeerUpdater; + +pub struct PeerUpdaterMock {} + +impl PeerUpdater for PeerUpdaterMock { + fn update(&self, _peers: Vec) {} +} diff --git a/bitacross-worker/sidechain/consensus/aura/src/test/mocks/proposer_mock.rs b/bitacross-worker/sidechain/consensus/aura/src/test/mocks/proposer_mock.rs new file mode 100644 index 0000000000..00f78298c3 --- /dev/null +++ b/bitacross-worker/sidechain/consensus/aura/src/test/mocks/proposer_mock.rs @@ -0,0 +1,73 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::ConsensusError; +use itp_time_utils::now_as_millis; +use itp_types::{Block as ParentchainBlock, Header}; +use its_consensus_common::{Proposal, Proposer}; +use its_primitives::types::block::SignedBlock as SignedSidechainBlock; +use its_test::{ + sidechain_block_builder::{SidechainBlockBuilder, SidechainBlockBuilderTrait}, + sidechain_block_data_builder::SidechainBlockDataBuilder, +}; +use std::time::Duration; + +pub struct DefaultProposerMock { + pub(crate) parentchain_header: Header, +} + +impl Proposer for DefaultProposerMock { + fn propose( + &self, + _max_duration: Duration, + ) -> Result, ConsensusError> { + Ok(Proposal { + block: { + let block_data = SidechainBlockDataBuilder::random() + .with_layer_one_head(self.parentchain_header.hash()) + .build(); + SidechainBlockBuilder::random().with_block_data(block_data).build_signed() + }, + + parentchain_effects: Default::default(), + }) + } +} + +pub struct OutdatedBlockProposerMock { + pub(crate) parentchain_header: Header, +} + +impl Proposer for OutdatedBlockProposerMock { + fn propose( + &self, + _max_duration: Duration, + ) -> Result, ConsensusError> { + let past = now_as_millis() - 1000; + Ok(Proposal { + block: { + let block_data = SidechainBlockDataBuilder::random() + .with_layer_one_head(self.parentchain_header.hash()) + .with_timestamp(past) + .build(); + SidechainBlockBuilder::random().with_block_data(block_data).build_signed() + }, + + parentchain_effects: Default::default(), + }) + } +} diff --git a/bitacross-worker/sidechain/consensus/aura/src/test/mod.rs b/bitacross-worker/sidechain/consensus/aura/src/test/mod.rs new file mode 100644 index 0000000000..7c40ba019d --- /dev/null +++ b/bitacross-worker/sidechain/consensus/aura/src/test/mod.rs @@ -0,0 +1,20 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +mod block_importer_tests; +pub mod fixtures; +pub mod mocks; diff --git a/bitacross-worker/sidechain/consensus/aura/src/verifier.rs b/bitacross-worker/sidechain/consensus/aura/src/verifier.rs new file mode 100644 index 0000000000..0c1f64b138 --- /dev/null +++ b/bitacross-worker/sidechain/consensus/aura/src/verifier.rs @@ -0,0 +1,89 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{authorities, EnclaveOnChainOCallApi, ShardIdentifierFor}; +use core::marker::PhantomData; +use its_block_verification::verify_sidechain_block; +use its_consensus_common::{Error as ConsensusError, Verifier}; +use its_primitives::{ + traits::{Block as SidechainBlockTrait, SignedBlock as SignedSidechainBlockTrait}, + types::block::BlockHash, +}; +use its_validateer_fetch::ValidateerFetch; +use sp_runtime::{app_crypto::Pair, traits::Block as ParentchainBlockTrait}; +use std::{fmt::Debug, time::Duration}; + +#[derive(Default)] +pub struct AuraVerifier +where + SignedSidechainBlock: SignedSidechainBlockTrait + 'static, + SignedSidechainBlock::Block: SidechainBlockTrait, +{ + slot_duration: Duration, + last_sidechain_block: Option, + _phantom: PhantomData<(AuthorityPair, ParentchainBlock, Context)>, +} + +impl + AuraVerifier +where + SignedSidechainBlock: SignedSidechainBlockTrait + 'static, + SignedSidechainBlock::Block: SidechainBlockTrait, +{ + pub fn new( + slot_duration: Duration, + last_sidechain_block: Option, + ) -> Self { + Self { slot_duration, last_sidechain_block, _phantom: Default::default() } + } +} + +impl + Verifier + for AuraVerifier +where + AuthorityPair: Pair, + AuthorityPair::Public: Debug, + // todo: Relax hash trait bound, but this needs a change to some other parts in the code. + ParentchainBlock: ParentchainBlockTrait, + SignedSidechainBlock: SignedSidechainBlockTrait + 'static, + SignedSidechainBlock::Block: SidechainBlockTrait, + Context: ValidateerFetch + EnclaveOnChainOCallApi + Send + Sync, +{ + type BlockImportParams = SignedSidechainBlock; + + type Context = Context; + + fn verify( + &self, + signed_block: SignedSidechainBlock, + parentchain_header: &ParentchainBlock::Header, + _shard: ShardIdentifierFor, + ctx: &Self::Context, + ) -> Result { + let authorities = + authorities::<_, AuthorityPair, ParentchainBlock::Header>(ctx, parentchain_header)?; + + Ok(verify_sidechain_block::( + signed_block, + self.slot_duration, + &self.last_sidechain_block, + parentchain_header, + &authorities, + )?) + } +} diff --git a/bitacross-worker/sidechain/consensus/common/Cargo.toml b/bitacross-worker/sidechain/consensus/common/Cargo.toml new file mode 100644 index 0000000000..6408dd9c08 --- /dev/null +++ b/bitacross-worker/sidechain/consensus/common/Cargo.toml @@ -0,0 +1,85 @@ +[package] +name = "its-consensus-common" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +log = { version = "0.4", default-features = false } +thiserror = { version = "1.0.26", optional = true } + +# local deps +itc-parentchain-light-client = { path = "../../../core/parentchain/light-client", default-features = false } +itp-enclave-metrics = { path = "../../../core-primitives/enclave-metrics", default-features = false } +itp-extrinsics-factory = { path = "../../../core-primitives/extrinsics-factory", default-features = false } +itp-import-queue = { path = "../../../core-primitives/import-queue", default-features = false } +itp-node-api-metadata = { path = "../../../core-primitives/node-api/metadata", default-features = false } +itp-node-api-metadata-provider = { path = "../../../core-primitives/node-api/metadata-provider", default-features = false } +itp-ocall-api = { path = "../../../core-primitives/ocall-api", default-features = false } +itp-settings = { path = "../../../core-primitives/settings" } +itp-sgx-crypto = { path = "../../../core-primitives/sgx/crypto", default-features = false } +itp-types = { path = "../../../core-primitives/types", default-features = false } +its-block-verification = { path = "../../block-verification", optional = true, default-features = false } +its-primitives = { path = "../../primitives", default-features = false } +its-state = { path = "../../state", default-features = false } + +# sgx deps +sgx_tstd = { optional = true, git = "https://github.com/apache/teaclave-sgx-sdk.git", branch = "master" } +sgx_types = { git = "https://github.com/apache/teaclave-sgx-sdk.git", branch = "master" } +thiserror-sgx = { package = "thiserror", optional = true, git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3" } + +# substrate deps +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +[dev-dependencies] +# local +itc-parentchain-test = { path = "../../../core/parentchain/test" } +itp-sgx-externalities = { default-features = false, path = "../../../core-primitives/substrate-sgx/externalities" } +itp-test = { path = "../../../core-primitives/test" } +its-test = { path = "../../test" } +fork-tree = { path = "../../fork-tree", default-features = false } + +# substrate +sp-core = { default-features = false, features = ["full_crypto"], git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +[features] +default = ["std"] +std = [ + "codec/std", + "log/std", + "thiserror", + # local + "itc-parentchain-light-client/std", + "itp-import-queue/std", + "itp-enclave-metrics/std", + "itp-extrinsics-factory/std", + "itp-node-api-metadata/std", + "itp-node-api-metadata-provider/std", + "itp-ocall-api/std", + "itp-sgx-crypto/std", + "itp-sgx-externalities/std", + "itp-types/std", + "its-primitives/std", + "its-block-verification/std", + "its-state/std", + "fork-tree/std", + # substrate + "sp-runtime/std", +] +sgx = [ + "sgx_tstd", + "thiserror-sgx", + # local + "itc-parentchain-light-client/sgx", + "itp-import-queue/sgx", + "itp-enclave-metrics/sgx", + "itp-extrinsics-factory/sgx", + "itp-node-api-metadata-provider/sgx", + "itp-sgx-crypto/sgx", + "itp-sgx-externalities/sgx", + "its-state/sgx", + "fork-tree/sgx", + # scs + "its-block-verification/sgx", +] diff --git a/bitacross-worker/sidechain/consensus/common/src/block_import.rs b/bitacross-worker/sidechain/consensus/common/src/block_import.rs new file mode 100644 index 0000000000..826b1e456b --- /dev/null +++ b/bitacross-worker/sidechain/consensus/common/src/block_import.rs @@ -0,0 +1,201 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Abstraction around block import + +use crate::{Error, Verifier}; +use codec::Decode; +use itp_enclave_metrics::EnclaveMetric; +use itp_ocall_api::{EnclaveMetricsOCallApi, EnclaveSidechainOCallApi}; +use itp_sgx_crypto::StateCrypto; +use its_primitives::traits::{ + Block as SidechainBlockTrait, BlockData, Header as HeaderTrait, ShardIdentifierFor, + SignedBlock as SignedSidechainBlockTrait, +}; +use its_state::{LastBlockExt, SidechainState}; +use log::*; +use sp_runtime::traits::Block as ParentchainBlockTrait; +use std::{time::Instant, vec::Vec}; + +pub trait BlockImport +where + ParentchainBlock: ParentchainBlockTrait, + SignedSidechainBlock: SignedSidechainBlockTrait, +{ + /// The verifier for of the respective consensus instance. + type Verifier: Verifier< + ParentchainBlock, + SignedSidechainBlock, + BlockImportParams = SignedSidechainBlock, + Context = Self::Context, + >; + + /// Context needed to derive verifier relevant data. + type SidechainState: SidechainState + LastBlockExt; + + /// Provides the cryptographic functions for our the state encryption. + type StateCrypto: StateCrypto; + + /// Context needed to derive verifier relevant data. + type Context: EnclaveSidechainOCallApi + EnclaveMetricsOCallApi; + + /// Get a verifier instance. + fn verifier( + &self, + maybe_last_sidechain_block: Option, + ) -> Self::Verifier; + + /// Apply a state update by providing a mutating function. + fn apply_state_update( + &self, + shard: &ShardIdentifierFor, + mutating_function: F, + ) -> Result<(), Error> + where + F: FnOnce(Self::SidechainState) -> Result; + + /// Verify a sidechain block that is to be imported. + fn verify_import( + &self, + shard: &ShardIdentifierFor, + verifying_function: F, + ) -> Result + where + F: FnOnce(&Self::SidechainState) -> Result; + + /// Key that is used for state encryption. + fn state_key(&self) -> Result; + + /// Getter for the context. + fn get_context(&self) -> &Self::Context; + + /// Import parentchain blocks up to and including the one we see in the sidechain block that + /// is scheduled for import. + /// + /// Returns the latest header. If no block was imported with the trigger, + /// we return `last_imported_parentchain_header`. + fn import_parentchain_block( + &self, + sidechain_block: &SignedSidechainBlock::Block, + last_imported_parentchain_header: &ParentchainBlock::Header, + ) -> Result; + + /// Peek the parentchain import queue for the block that is associated with a given sidechain. + /// Does not perform the import or mutate the queue. + /// + /// Warning: Be aware that peeking the parentchain block means that it is not verified (that happens upon import). + fn peek_parentchain_header( + &self, + sidechain_block: &SignedSidechainBlock::Block, + last_imported_parentchain_header: &ParentchainBlock::Header, + ) -> Result; + /// Cleanup task after import is done. + fn cleanup(&self, signed_sidechain_block: &SignedSidechainBlock) -> Result<(), Error>; + + /// Import a sidechain block and mutate state by `apply_state_update`. + fn import_block( + &self, + signed_sidechain_block: SignedSidechainBlock, + parentchain_header: &ParentchainBlock::Header, + ) -> Result { + let start_time = Instant::now(); + + let sidechain_block = signed_sidechain_block.block().clone(); + let shard = sidechain_block.header().shard_id(); + let block_number = signed_sidechain_block.block().header().block_number(); + + debug!( + "Attempting to import sidechain block (number: {}, hash: {:?}, parentchain hash: {:?})", + block_number, + signed_sidechain_block.block().hash(), + signed_sidechain_block.block().block_data().layer_one_head() + ); + + let peeked_parentchain_header = + self.peek_parentchain_header(&sidechain_block, parentchain_header) + .unwrap_or_else(|e| { + warn!("Could not peek parentchain block, returning latest parentchain block ({:?})", e); + parentchain_header.clone() + }); + + let block_import_params = self.verify_import(&shard, |state| { + let verifier = self.verifier(state.get_last_block()); + verifier.verify( + signed_sidechain_block.clone(), + &peeked_parentchain_header, + shard, + self.get_context(), + ) + })?; + + let latest_parentchain_header = + self.import_parentchain_block(&sidechain_block, parentchain_header)?; + + let state_key = self.state_key()?; + + let state_update_start_time = Instant::now(); + self.apply_state_update(&shard, |mut state| { + let encrypted_state_diff = + block_import_params.block().block_data().encrypted_state_diff(); + + info!( + "Applying state diff for block {} of size {} bytes", + block_number, + encrypted_state_diff.len() + ); + + let update = state_update_from_encrypted(encrypted_state_diff, state_key)?; + + state.apply_state_update(&update).map_err(|e| Error::Other(e.into()))?; + + state.set_last_block(block_import_params.block()); + + Ok(state) + })?; + info!( + "Applying state update from block {} took {} ms", + block_number, + state_update_start_time.elapsed().as_millis() + ); + + self.cleanup(&signed_sidechain_block)?; + + // Store block in storage. + self.get_context().store_sidechain_blocks(vec![signed_sidechain_block])?; + + let import_duration = start_time.elapsed(); + info!("Importing block {} took {} ms", block_number, import_duration.as_millis()); + if let Err(e) = self + .get_context() + .update_metric(EnclaveMetric::SidechainBlockImportTime(import_duration)) + { + warn!("Failed to update metric for sidechain block import: {:?}", e); + }; + + Ok(latest_parentchain_header) + } +} + +fn state_update_from_encrypted( + encrypted: &[u8], + key: Key, +) -> Result { + let mut payload: Vec = encrypted.to_vec(); + key.decrypt(&mut payload).map_err(|e| Error::Other(format!("{:?}", e).into()))?; + + Ok(Decode::decode(&mut payload.as_slice())?) +} diff --git a/bitacross-worker/sidechain/consensus/common/src/block_import_confirmation_handler.rs b/bitacross-worker/sidechain/consensus/common/src/block_import_confirmation_handler.rs new file mode 100644 index 0000000000..be93feb51c --- /dev/null +++ b/bitacross-worker/sidechain/consensus/common/src/block_import_confirmation_handler.rs @@ -0,0 +1,130 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::error::{Error, Result}; +use itc_parentchain_light_client::{ + concurrent_access::ValidatorAccess, BlockNumberOps, ExtrinsicSender, NumberFor, +}; +use itp_extrinsics_factory::CreateExtrinsics; +use itp_node_api_metadata::{pallet_sidechain::SidechainCallIndexes, NodeMetadataTrait}; +use itp_node_api_metadata_provider::AccessNodeMetadata; +use itp_settings::worker::BLOCK_NUMBER_FINALIZATION_DIFF; +use itp_types::{OpaqueCall, ShardIdentifier}; +use its_primitives::traits::Header as HeaderTrait; +use log::*; +use sp_runtime::traits::Block as ParentchainBlockTrait; +use std::{marker::PhantomData, sync::Arc}; + +/// Trait to confirm a sidechain block import. +pub trait ConfirmBlockImport { + fn confirm_import(&self, header: &SidechainHeader, shard: &ShardIdentifier) -> Result<()>; +} + +/// Creates and sends a sidechain block import confirmation extrsinic to the parentchain. +pub struct BlockImportConfirmationHandler< + ParentchainBlock, + SidechainHeader, + NodeMetadataRepository, + ExtrinsicsFactory, + ValidatorAccessor, +> { + metadata_repository: Arc, + extrinsics_factory: Arc, + validator_accessor: Arc, + _phantom: PhantomData<(ParentchainBlock, SidechainHeader)>, +} + +impl< + ParentchainBlock, + SidechainHeader, + NodeMetadataRepository, + ExtrinsicsFactory, + ValidatorAccessor, + > + BlockImportConfirmationHandler< + ParentchainBlock, + SidechainHeader, + NodeMetadataRepository, + ExtrinsicsFactory, + ValidatorAccessor, + > +{ + pub fn new( + metadata_repository: Arc, + extrinsics_factory: Arc, + validator_accessor: Arc, + ) -> Self { + Self { + metadata_repository, + extrinsics_factory, + validator_accessor, + _phantom: Default::default(), + } + } +} + +impl< + ParentchainBlock, + SidechainHeader, + NodeMetadataRepository, + ExtrinsicsFactory, + ValidatorAccessor, + > ConfirmBlockImport + for BlockImportConfirmationHandler< + ParentchainBlock, + SidechainHeader, + NodeMetadataRepository, + ExtrinsicsFactory, + ValidatorAccessor, + > where + ParentchainBlock: ParentchainBlockTrait, + NumberFor: BlockNumberOps, + SidechainHeader: HeaderTrait, + NodeMetadataRepository: AccessNodeMetadata, + NodeMetadataRepository::MetadataType: NodeMetadataTrait, + ExtrinsicsFactory: CreateExtrinsics, + ValidatorAccessor: ValidatorAccess + Send + Sync + 'static, +{ + fn confirm_import(&self, header: &SidechainHeader, shard: &ShardIdentifier) -> Result<()> { + let call = self + .metadata_repository + .get_from_metadata(|m| m.confirm_imported_sidechain_block_indexes()) + .map_err(|e| Error::Other(e.into()))? + .map_err(|e| Error::Other(format!("{:?}", e).into()))?; + + if header.block_number() == header.next_finalization_block_number() { + let opaque_call = OpaqueCall::from_tuple(&( + call, + shard, + header.block_number(), + header.next_finalization_block_number() + BLOCK_NUMBER_FINALIZATION_DIFF, + header.hash(), + )); + + let xts = self + .extrinsics_factory + .create_extrinsics(&[opaque_call], None) + .map_err(|e| Error::Other(e.into()))?; + + debug!("Sending sidechain block import confirmation extrinsic.."); + self.validator_accessor + .execute_mut_on_validator(|v| v.send_extrinsics(xts)) + .map_err(|e| Error::Other(e.into()))?; + } + Ok(()) + } +} diff --git a/bitacross-worker/sidechain/consensus/common/src/block_import_queue_worker.rs b/bitacross-worker/sidechain/consensus/common/src/block_import_queue_worker.rs new file mode 100644 index 0000000000..fc7d9a23ef --- /dev/null +++ b/bitacross-worker/sidechain/consensus/common/src/block_import_queue_worker.rs @@ -0,0 +1,120 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{Error, Result, SyncBlockFromPeer}; +use core::marker::PhantomData; +use itp_import_queue::PopFromQueue; +use its_primitives::traits::{Block as BlockTrait, SignedBlock as SignedSidechainBlockTrait}; +use log::debug; +use sp_runtime::traits::Block as ParentchainBlockTrait; +use std::{sync::Arc, time::Instant}; + +/// Trait to trigger working the sidechain block import queue. +pub trait ProcessBlockImportQueue { + /// Pop sidechain blocks from the import queue and import them until queue is empty. + fn process_queue( + &self, + current_parentchain_header: &ParentchainBlockHeader, + ) -> Result; +} + +pub struct BlockImportQueueWorker< + ParentchainBlock, + SignedSidechainBlock, + BlockImportQueue, + PeerBlockSyncer, +> { + block_import_queue: Arc, + peer_block_syncer: Arc, + _phantom: PhantomData<(ParentchainBlock, SignedSidechainBlock)>, +} + +impl + BlockImportQueueWorker +where + ParentchainBlock: ParentchainBlockTrait, + SignedSidechainBlock: SignedSidechainBlockTrait, + SignedSidechainBlock::Block: BlockTrait, + BlockImportQueue: PopFromQueue, + PeerBlockSyncer: SyncBlockFromPeer, +{ + pub fn new( + block_import_queue: Arc, + peer_block_syncer: Arc, + ) -> Self { + BlockImportQueueWorker { + block_import_queue, + peer_block_syncer, + _phantom: Default::default(), + } + } + + fn record_timings(start_time: Instant, number_of_imported_blocks: usize) { + let elapsed_time_millis = start_time.elapsed().as_millis(); + let time_millis_per_block = + (elapsed_time_millis as f64 / number_of_imported_blocks as f64).ceil(); + debug!( + "Imported {} blocks in {} ms (average of {} ms per block)", + number_of_imported_blocks, elapsed_time_millis, time_millis_per_block + ); + } +} + +impl + ProcessBlockImportQueue + for BlockImportQueueWorker< + ParentchainBlock, + SignedSidechainBlock, + BlockImportQueue, + PeerBlockSyncer, + > where + ParentchainBlock: ParentchainBlockTrait, + SignedSidechainBlock: SignedSidechainBlockTrait, + SignedSidechainBlock::Block: BlockTrait, + BlockImportQueue: PopFromQueue, + PeerBlockSyncer: SyncBlockFromPeer, +{ + fn process_queue( + &self, + current_parentchain_header: &ParentchainBlock::Header, + ) -> Result { + let mut latest_imported_parentchain_header = current_parentchain_header.clone(); + let mut number_of_imported_blocks = 0usize; + let start_time = Instant::now(); + + loop { + match self.block_import_queue.pop_front() { + Ok(maybe_block) => match maybe_block { + Some(block) => { + latest_imported_parentchain_header = self + .peer_block_syncer + .sync_block(block, &latest_imported_parentchain_header)?; + number_of_imported_blocks += 1; + }, + None => { + Self::record_timings(start_time, number_of_imported_blocks); + return Ok(latest_imported_parentchain_header) + }, + }, + Err(e) => { + Self::record_timings(start_time, number_of_imported_blocks); + return Err(Error::FailedToPopBlockImportQueue(e)) + }, + } + } + } +} diff --git a/bitacross-worker/sidechain/consensus/common/src/block_production_suspension.rs b/bitacross-worker/sidechain/consensus/common/src/block_production_suspension.rs new file mode 100644 index 0000000000..ae664925da --- /dev/null +++ b/bitacross-worker/sidechain/consensus/common/src/block_production_suspension.rs @@ -0,0 +1,112 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Mechanisms to (temporarily) suspend the production of sidechain blocks. + +#[cfg(feature = "sgx")] +use std::sync::SgxRwLock as RwLock; + +#[cfg(feature = "std")] +use std::sync::RwLock; + +use crate::error::{Error, Result}; +use log::*; + +/// Trait to suspend the production of sidechain blocks. +pub trait SuspendBlockProduction { + /// Suspend any sidechain block production. + fn suspend_for_sync(&self) -> Result<()>; + + /// Resume block sidechain block production. + fn resume(&self) -> Result<()>; +} + +/// Trait to query if sidechain block production is suspended. +pub trait IsBlockProductionSuspended { + fn is_suspended(&self) -> Result; + + fn is_sync_ongoing(&self) -> Result; +} + +/// Implementation for suspending and resuming sidechain block production. +#[derive(Default)] +pub struct BlockProductionSuspender { + is_suspended: RwLock, + sync_is_ongoing: RwLock, +} + +impl BlockProductionSuspender { + pub fn new(is_suspended: bool) -> Self { + BlockProductionSuspender { + is_suspended: RwLock::new(is_suspended), + sync_is_ongoing: RwLock::new(false), + } + } +} + +impl SuspendBlockProduction for BlockProductionSuspender { + fn suspend_for_sync(&self) -> Result<()> { + let mut suspended_lock = self.is_suspended.write().map_err(|_| Error::LockPoisoning)?; + *suspended_lock = true; + + let mut sync_is_ongoing_lock = + self.sync_is_ongoing.write().map_err(|_| Error::LockPoisoning)?; + *sync_is_ongoing_lock = true; + + info!("Suspend sidechain block production"); + Ok(()) + } + + fn resume(&self) -> Result<()> { + let mut suspended_lock = self.is_suspended.write().map_err(|_| Error::LockPoisoning)?; + *suspended_lock = false; + info!("Resume sidechain block production"); + Ok(()) + } +} + +impl IsBlockProductionSuspended for BlockProductionSuspender { + fn is_suspended(&self) -> Result { + Ok(*self.is_suspended.read().map_err(|_| Error::LockPoisoning)?) + } + + fn is_sync_ongoing(&self) -> Result { + Ok(*self.sync_is_ongoing.read().map_err(|_| Error::LockPoisoning)?) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn initial_production_is_not_suspended() { + let block_production_suspender = BlockProductionSuspender::default(); + assert!(!block_production_suspender.is_suspended().unwrap()); + } + + #[test] + fn suspending_production_works() { + let block_production_suspender = BlockProductionSuspender::default(); + + block_production_suspender.suspend_for_sync().unwrap(); + assert!(block_production_suspender.is_suspended().unwrap()); + + block_production_suspender.resume().unwrap(); + assert!(!block_production_suspender.is_suspended().unwrap()); + } +} diff --git a/bitacross-worker/sidechain/consensus/common/src/error.rs b/bitacross-worker/sidechain/consensus/common/src/error.rs new file mode 100644 index 0000000000..f6ba8b6fd0 --- /dev/null +++ b/bitacross-worker/sidechain/consensus/common/src/error.rs @@ -0,0 +1,99 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Error types in sidechain consensus + +use itp_types::BlockHash as ParentchainBlockHash; +use its_block_verification::error::Error as VerificationError; +use its_primitives::types::{block::BlockHash as SidechainBlockHash, BlockNumber}; +use sgx_types::sgx_status_t; +use std::{ + boxed::Box, + error, + string::{String, ToString}, + vec::Vec, +}; + +pub type Result = std::result::Result; + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub use thiserror_sgx as thiserror; + +#[derive(Debug, thiserror::Error)] +#[non_exhaustive] +pub enum Error { + #[error("SGX error, status: {0}")] + Sgx(sgx_status_t), + #[error("Unable to create block proposal.")] + CannotPropose, + #[error("Encountered poisoned lock")] + LockPoisoning, + #[error("Message sender {0} is not a valid authority")] + InvalidAuthority(String), + #[error("Could not get authorities: {0:?}.")] + CouldNotGetAuthorities(String), + #[error("Chain lookup failed: {0}")] + ChainLookup(String), + #[error("Failed to sign using key: {0:?}. Reason: {1}")] + CannotSign(Vec, String), + #[error("Bad parentchain block (Hash={0}). Reason: {1}")] + BadParentchainBlock(ParentchainBlockHash, String), + #[error("Bad sidechain block (Hash={0}). Reason: {1}")] + BadSidechainBlock(SidechainBlockHash, String), + #[error("Could not import new block due to {2}. (Last imported by number: {0:?})")] + BlockAncestryMismatch(BlockNumber, SidechainBlockHash, String), + #[error("Could not import new block. Expected first block, but found {0}. {1:?}")] + InvalidFirstBlock(BlockNumber, String), + #[error("Could not import block (number: {0}). A block with this number is already imported (current state block number: {1})")] + BlockAlreadyImported(BlockNumber, BlockNumber), + #[error("Failed to pop from block import queue: {0}")] + FailedToPopBlockImportQueue(#[from] itp_import_queue::error::Error), + #[error("Verification Error: {0}")] + VerificationError(its_block_verification::error::Error), + #[error(transparent)] + Other(#[from] Box), +} + +impl core::convert::From for Error { + fn from(e: std::io::Error) -> Self { + Self::Other(e.into()) + } +} + +impl core::convert::From for Error { + fn from(e: codec::Error) -> Self { + Self::Other(e.to_string().into()) + } +} + +impl From for Error { + fn from(sgx_status: sgx_status_t) -> Self { + Self::Sgx(sgx_status) + } +} + +impl From for Error { + fn from(e: VerificationError) -> Self { + match e { + VerificationError::BlockAncestryMismatch(a, b, c) => + Error::BlockAncestryMismatch(a, b, c), + VerificationError::InvalidFirstBlock(a, b) => Error::InvalidFirstBlock(a, b), + VerificationError::BlockAlreadyImported(a, b) => Error::BlockAlreadyImported(a, b), + _ => Error::VerificationError(e), + } + } +} diff --git a/bitacross-worker/sidechain/consensus/common/src/header_db.rs b/bitacross-worker/sidechain/consensus/common/src/header_db.rs new file mode 100644 index 0000000000..f15acd5028 --- /dev/null +++ b/bitacross-worker/sidechain/consensus/common/src/header_db.rs @@ -0,0 +1,44 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +use itp_types::H256; +use its_primitives::traits::Header as HeaderT; +use std::{collections::HashMap, convert::From, hash::Hash as HashT}; + +/// Normally implemented on the `client` in substrate. +/// Is a trait which can offer methods for interfacing with a block Database. +pub trait HeaderDbTrait { + type Header: HeaderT; + /// Retrieves Header for the corresponding block hash. + fn header(&self, hash: &H256) -> Option; +} + +/// A mocked Header Database which allows you to take a Block Hash and Query a Block Header. +pub struct HeaderDb(pub HashMap); + +impl HeaderDbTrait for HeaderDb +where + // TODO: the H256 trait bounds are needed because: #1203 + Hash: PartialEq + HashT + Into + From + core::cmp::Eq + Clone, + Header: HeaderT + Clone, +{ + type Header = Header; + + fn header(&self, hash: &H256) -> Option { + let header = self.0.get(&Hash::from(*hash))?; + Some(header.clone()) + } +} diff --git a/bitacross-worker/sidechain/consensus/common/src/is_descendant_of_builder.rs b/bitacross-worker/sidechain/consensus/common/src/is_descendant_of_builder.rs new file mode 100644 index 0000000000..5e13c6f69a --- /dev/null +++ b/bitacross-worker/sidechain/consensus/common/src/is_descendant_of_builder.rs @@ -0,0 +1,133 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +use crate::header_db::HeaderDbTrait; +use core::{hash::Hash as HashT, marker::PhantomData}; +use itp_types::H256; +use its_primitives::traits::Header as HeaderT; + +pub struct IsDescendantOfBuilder(PhantomData<(Hash, HeaderDb, Error)>); + +impl<'a, Hash, HeaderDb, Error> IsDescendantOfBuilder +where + Error: From<()>, + Hash: PartialEq + HashT + Default + Into + From + Clone, + HeaderDb: HeaderDbTrait, +{ + /// Builds the `is_descendant_of` closure for the fork-tree + /// used when adding and removing nodes from the tree. + pub fn build_is_descendant_of( + current: Option<(&'a Hash, &'a Hash)>, + header_db: &'a HeaderDb, + ) -> impl Fn(&Hash, &Hash) -> Result + 'a { + move |base, head| { + // If the base is equal to the proposed head, then the head is for sure not a descendant of the base. + if base == head { + return Ok(false) + } + + let mut head = head; + if let Some((current_hash, current_parent_hash)) = current { + // If the current hash is equal to the base, then it will not be a descendant of base. + if current_hash == base { + return Ok(false) + } + + // If the current hash is the head and the parent is the base, then we know that + // this current hash is the descendant of the parent. Otherwise we can set the + // head to the parent and find the lowest common ancestor between `head` + // and `base` in the tree. + if current_hash == head { + if current_parent_hash == base { + return Ok(true) + } else { + head = current_parent_hash; + } + } + } + + let ancestor = + >::find_lowest_common_ancestor( + head, base, header_db, + )?; + Ok(ancestor == *base) + } + } +} + +pub struct LowestCommonAncestorFinder(PhantomData<(Hash, HeaderDb)>); + +impl LowestCommonAncestorFinder +where + Hash: PartialEq + Default + Into + From + Clone, + HeaderDb: HeaderDbTrait, +{ + /// Used by the `build_is_descendant_of` to find the LCA of two nodes in the fork-tree. + fn find_lowest_common_ancestor(a: &Hash, b: &Hash, header_db: &HeaderDb) -> Result { + let header_1 = header_db.header(&a.clone().into()).ok_or(())?; + let header_2 = header_db.header(&b.clone().into()).ok_or(())?; + let mut blocknum_1 = header_1.block_number(); + let mut blocknum_2 = header_2.block_number(); + let mut parent_1 = Hash::from(header_1.parent_hash()); + let mut parent_2 = Hash::from(header_2.parent_hash()); + + if *a == parent_2 { + // Then a is the common ancestor of b and it means it is itself the ancestor + return Ok(parent_2) + } + + if *b == parent_1 { + // Then b is the common ancestor of a and it means it is itself the ancestor + return Ok(parent_1) + } + + while blocknum_1 > blocknum_2 { + // This means block 1 is further down in the tree than block 2 + let new_parent = header_db.header(&parent_1.clone().into()).ok_or(())?; + + if new_parent.block_number() >= blocknum_2 { + blocknum_1 = new_parent.block_number(); + parent_1 = Hash::from(new_parent.parent_hash()); + } else { + break + } + } + + while blocknum_2 > blocknum_1 { + // This means block 2 is further down in the tree than block 1 + let new_parent = header_db.header(&parent_2.clone().into()).ok_or(())?; + + if new_parent.block_number() >= blocknum_1 { + blocknum_2 = new_parent.block_number(); + parent_2 = Hash::from(new_parent.parent_hash()); + } else { + break + } + } + + // At this point will be at equal height + while parent_1 != parent_2 { + // go up on both nodes + let new_header_1 = header_db.header(&parent_1.into()).ok_or(())?; + let new_header_2 = header_db.header(&parent_2.into()).ok_or(())?; + parent_1 = Hash::from(new_header_1.parent_hash()); + parent_2 = Hash::from(new_header_2.parent_hash()); + } + + // Return any Parent node Hash as in worst case scenario it is the root which is shared amongst all + Ok(parent_1) + } +} diff --git a/bitacross-worker/sidechain/consensus/common/src/lib.rs b/bitacross-worker/sidechain/consensus/common/src/lib.rs new file mode 100644 index 0000000000..adb91d9ec8 --- /dev/null +++ b/bitacross-worker/sidechain/consensus/common/src/lib.rs @@ -0,0 +1,114 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Common stuff that could be shared across multiple consensus engines + +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(test, feature(assert_matches))] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +#[macro_use] +extern crate sgx_tstd as std; + +use its_primitives::traits::{ShardIdentifierFor, SignedBlock as SignedSidechainBlockTrait}; +use sp_runtime::traits::Block as ParentchainBlockTrait; +use std::{time::Duration, vec::Vec}; + +mod block_import; +mod block_import_confirmation_handler; +mod block_import_queue_worker; +mod error; +mod header_db; +mod peer_block_sync; + +// The feature flag will be removed once we use the module outside of tests. +#[cfg(test)] +mod is_descendant_of_builder; + +#[cfg(test)] +mod test; + +pub use block_import::*; +pub use block_import_confirmation_handler::*; +pub use block_import_queue_worker::*; +pub use error::*; +use itp_types::parentchain::ParentchainCall; +pub use peer_block_sync::*; + +pub trait Verifier: Send + Sync +where + ParentchainBlock: ParentchainBlockTrait, + SignedSidechainBlock: SignedSidechainBlockTrait, +{ + /// Contains all the relevant data needed for block import + type BlockImportParams; + + /// Context used to derive slot relevant data + type Context; + + /// Verify the given data and return the `BlockImportParams` if successful + fn verify( + &self, + block: SignedSidechainBlock, + parentchain_header: &ParentchainBlock::Header, + shard: ShardIdentifierFor, + ctx: &Self::Context, + ) -> Result; +} + +/// Environment for a Consensus instance. +/// +/// Creates proposer instance. +pub trait Environment< + ParentchainBlock: ParentchainBlockTrait, + SignedSidechainBlock: SignedSidechainBlockTrait, +> +{ + /// The proposer type this creates. + type Proposer: Proposer + Send; + /// Error which can occur upon creation. + type Error: From + std::fmt::Debug + 'static; + + /// Initialize the proposal logic on top of a specific header. + fn init( + &mut self, + parent_header: ParentchainBlock::Header, + shard: ShardIdentifierFor, + ) -> std::result::Result; +} + +pub trait Proposer< + ParentchainBlock: ParentchainBlockTrait, + SignedSidechainBlock: SignedSidechainBlockTrait, +> +{ + fn propose(&self, max_duration: Duration) -> Result>; +} + +/// A proposal that is created by a [`Proposer`]. +pub struct Proposal { + /// The sidechain block that was build. + pub block: SignedSidechainBlock, + /// Parentchain state transitions triggered by sidechain state transitions. + /// + /// Any sidechain stf that invokes a parentchain stf must not commit its state change + /// before the parentchain effect has been finalized. + pub parentchain_effects: Vec, +} diff --git a/bitacross-worker/sidechain/consensus/common/src/peer_block_sync.rs b/bitacross-worker/sidechain/consensus/common/src/peer_block_sync.rs new file mode 100644 index 0000000000..945c1c014e --- /dev/null +++ b/bitacross-worker/sidechain/consensus/common/src/peer_block_sync.rs @@ -0,0 +1,320 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{BlockImport, ConfirmBlockImport, Error, Result}; +use core::marker::PhantomData; +use itp_ocall_api::{EnclaveMetricsOCallApi, EnclaveSidechainOCallApi}; +use itp_types::H256; +use its_primitives::{ + traits::{ + Block as BlockTrait, Header as HeaderTrait, ShardIdentifierFor, + SignedBlock as SignedSidechainBlockTrait, + }, + types::BlockHash, +}; +use log::*; +use sp_runtime::traits::{Block as ParentchainBlockTrait, Header as ParentchainHeaderTrait}; +use std::{sync::Arc, vec::Vec}; + +/// Trait for syncing sidechain blocks from a peer validateer. +/// +/// This entails importing blocks and detecting if we're out of date with our blocks, in which +/// case we fetch the missing blocks from a peer. +pub trait SyncBlockFromPeer +where + ParentchainHeader: ParentchainHeaderTrait, + SignedSidechainBlock: SignedSidechainBlockTrait, +{ + fn sync_block( + &self, + sidechain_block: SignedSidechainBlock, + last_imported_parentchain_header: &ParentchainHeader, + ) -> Result; +} + +/// Sidechain peer block sync implementation. +pub struct PeerBlockSync< + ParentchainBlock, + SignedSidechainBlock, + BlockImporter, + OCallApi, + ImportConfirmationHandler, +> { + importer: Arc, + ocall_api: Arc, + import_confirmation_handler: Arc, + _phantom: PhantomData<(ParentchainBlock, SignedSidechainBlock)>, +} + +impl< + ParentchainBlock, + SignedSidechainBlock, + BlockImporter, + OCallApi, + ImportConfirmationHandler, + > + PeerBlockSync< + ParentchainBlock, + SignedSidechainBlock, + BlockImporter, + OCallApi, + ImportConfirmationHandler, + > where + ParentchainBlock: ParentchainBlockTrait, + SignedSidechainBlock: SignedSidechainBlockTrait, + <::Block as BlockTrait>::HeaderType: + HeaderTrait, + BlockImporter: BlockImport, + OCallApi: EnclaveSidechainOCallApi + EnclaveMetricsOCallApi, + ImportConfirmationHandler: ConfirmBlockImport< + <::Block as BlockTrait>::HeaderType, + >, +{ + pub fn new( + importer: Arc, + sidechain_ocall_api: Arc, + import_confirmation_handler: Arc, + ) -> Self { + PeerBlockSync { + importer, + ocall_api: sidechain_ocall_api, + import_confirmation_handler, + _phantom: Default::default(), + } + } + + fn fetch_and_import_blocks_from_peer( + &self, + last_imported_sidechain_block_hash: BlockHash, + import_until_block_hash: BlockHash, + current_parentchain_header: &ParentchainBlock::Header, + shard_identifier: ShardIdentifierFor, + ) -> Result { + info!( + "Initiating fetch blocks from peer, last imported block hash: {:?}, until block hash: {:?}", + last_imported_sidechain_block_hash, import_until_block_hash + ); + + let blocks_to_import: Vec = + self.ocall_api.fetch_sidechain_blocks_from_peer( + last_imported_sidechain_block_hash, + Some(import_until_block_hash), + shard_identifier, + )?; + + info!("Fetched {} blocks from peer to import", blocks_to_import.len()); + + let mut latest_imported_parentchain_header = current_parentchain_header.clone(); + + for block_to_import in blocks_to_import { + let block_number = block_to_import.block().header().block_number(); + + latest_imported_parentchain_header = match self + .importer + .import_block(block_to_import, &latest_imported_parentchain_header) + { + Err(e) => { + error!("Failed to import sidechain block that was fetched from peer: {:?}", e); + return Err(e) + }, + Ok(h) => { + info!( + "Successfully imported peer fetched sidechain block (number: {})", + block_number + ); + h + }, + }; + } + + Ok(latest_imported_parentchain_header) + } +} + +impl + SyncBlockFromPeer + for PeerBlockSync +where + ParentchainBlock: ParentchainBlockTrait, + SignedSidechainBlock: SignedSidechainBlockTrait, + <::Block as BlockTrait>::HeaderType: + HeaderTrait, + BlockImporter: BlockImport, + OCallApi: EnclaveSidechainOCallApi + EnclaveMetricsOCallApi, + ImportConfirmationHandler: ConfirmBlockImport<<::Block as BlockTrait>::HeaderType>, +{ + fn sync_block( + &self, + sidechain_block: SignedSidechainBlock, + current_parentchain_header: &ParentchainBlock::Header, + ) -> Result { + let shard_identifier = sidechain_block.block().header().shard_id(); + let sidechain_block_number = sidechain_block.block().header().block_number(); + let sidechain_block_hash = sidechain_block.hash(); + + // Attempt to import the block - in case we encounter an ancestry error, we go into + // peer fetching mode to fetch sidechain blocks from a peer and import those first. + match self.importer.import_block(sidechain_block.clone(), current_parentchain_header) { + Err(e) => match e { + Error::BlockAncestryMismatch(_block_number, block_hash, _) => { + warn!("Got ancestry mismatch error upon block import. Attempting to fetch missing blocks from peer"); + let updated_parentchain_header = self.fetch_and_import_blocks_from_peer( + block_hash, + sidechain_block_hash, + current_parentchain_header, + shard_identifier, + )?; + + self.importer.import_block(sidechain_block, &updated_parentchain_header) + }, + Error::InvalidFirstBlock(block_number, _) => { + warn!("Got invalid first block error upon block import (expected first block, but got block with number {}). \ + Attempting to fetch missing blocks from peer", block_number); + let updated_parentchain_header = self.fetch_and_import_blocks_from_peer( + Default::default(), // This is the parent hash of the first block. So we import everything. + sidechain_block_hash, + current_parentchain_header, + shard_identifier, + )?; + + self.importer.import_block(sidechain_block, &updated_parentchain_header) + }, + Error::BlockAlreadyImported(to_import_block_number, last_known_block_number) => { + warn!("Sidechain block from queue (number: {}) was already imported (current block number: {}). Block will be ignored.", + to_import_block_number, last_known_block_number); + Ok(current_parentchain_header.clone()) + }, + _ => Err(e), + }, + Ok(latest_parentchain_header) => { + info!("Successfully imported broadcast sidechain block (number: {}), based on parentchain block {:?}", + sidechain_block_number, latest_parentchain_header.number()); + + // We confirm the successful block import. Only in this case, not when we're in + // on-boarding and importing blocks that were fetched from a peer. + if let Err(e) = self.import_confirmation_handler.confirm_import(sidechain_block.block().header(), &shard_identifier) { + error!("Failed to confirm sidechain block import: {:?}", e); + } + + Ok(latest_parentchain_header) + }, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test::mocks::{ + block_importer_mock::BlockImportMock, confirm_block_import_mock::ConfirmBlockImportMock, + }; + use core::assert_matches::assert_matches; + use itc_parentchain_test::ParentchainHeaderBuilder; + use itp_test::mock::sidechain_ocall_api_mock::SidechainOCallApiMock; + use itp_types::Block as ParentchainBlock; + use its_primitives::types::block::SignedBlock as SignedSidechainBlock; + use its_test::sidechain_block_builder::{SidechainBlockBuilder, SidechainBlockBuilderTrait}; + + type TestBlockImport = BlockImportMock; + type TestOCallApi = SidechainOCallApiMock; + type TestPeerBlockSync = PeerBlockSync< + ParentchainBlock, + SignedSidechainBlock, + TestBlockImport, + TestOCallApi, + ConfirmBlockImportMock, + >; + + #[test] + fn if_block_import_is_successful_no_peer_fetching_happens() { + let parentchain_header = ParentchainHeaderBuilder::default().build(); + let signed_sidechain_block = SidechainBlockBuilder::default().build_signed(); + + let block_importer_mock = Arc::new( + BlockImportMock::::default() + .with_import_result_once(Ok(parentchain_header.clone())), + ); + + let sidechain_ocall_api = + Arc::new(SidechainOCallApiMock::::default()); + + let peer_syncer = + create_peer_syncer(block_importer_mock.clone(), sidechain_ocall_api.clone()); + + peer_syncer.sync_block(signed_sidechain_block, &parentchain_header).unwrap(); + + assert_eq!(1, block_importer_mock.get_imported_blocks().len()); + assert_eq!(0, sidechain_ocall_api.number_of_fetch_calls()); + } + + #[test] + fn error_is_propagated_if_import_returns_error_other_than_ancestry_mismatch() { + let block_importer_mock = Arc::new( + BlockImportMock::::default() + .with_import_result_once(Err(Error::InvalidAuthority("auth".to_string()))), + ); + + let sidechain_ocall_api = + Arc::new(SidechainOCallApiMock::::default()); + + let peer_syncer = + create_peer_syncer(block_importer_mock.clone(), sidechain_ocall_api.clone()); + + let parentchain_header = ParentchainHeaderBuilder::default().build(); + let signed_sidechain_block = SidechainBlockBuilder::default().build_signed(); + + let sync_result = peer_syncer.sync_block(signed_sidechain_block, &parentchain_header); + + assert_matches!(sync_result, Err(Error::InvalidAuthority(_))); + assert_eq!(1, block_importer_mock.get_imported_blocks().len()); + assert_eq!(0, sidechain_ocall_api.number_of_fetch_calls()); + } + + #[test] + fn blocks_are_fetched_from_peer_if_initial_import_yields_ancestry_mismatch() { + let block_importer_mock = + Arc::new(BlockImportMock::::default().with_import_result_once( + Err(Error::BlockAncestryMismatch(1, H256::random(), "".to_string())), + )); + + let sidechain_ocall_api = Arc::new( + SidechainOCallApiMock::::default().with_peer_fetch_blocks(vec![ + SidechainBlockBuilder::random().build_signed(), + SidechainBlockBuilder::random().build_signed(), + ]), + ); + + let peer_syncer = + create_peer_syncer(block_importer_mock.clone(), sidechain_ocall_api.clone()); + + let parentchain_header = ParentchainHeaderBuilder::default().build(); + let signed_sidechain_block = SidechainBlockBuilder::default().build_signed(); + + peer_syncer.sync_block(signed_sidechain_block, &parentchain_header).unwrap(); + + assert_eq!(4, block_importer_mock.get_imported_blocks().len()); + assert_eq!(1, sidechain_ocall_api.number_of_fetch_calls()); + } + + fn create_peer_syncer( + block_importer: Arc, + ocall_api: Arc, + ) -> TestPeerBlockSync { + let import_confirmation_handler = Arc::new(ConfirmBlockImportMock {}); + TestPeerBlockSync::new(block_importer, ocall_api, import_confirmation_handler) + } +} diff --git a/bitacross-worker/sidechain/consensus/common/src/test/mocks/block_import_queue_worker_mock.rs b/bitacross-worker/sidechain/consensus/common/src/test/mocks/block_import_queue_worker_mock.rs new file mode 100644 index 0000000000..fb2b0d8bcc --- /dev/null +++ b/bitacross-worker/sidechain/consensus/common/src/test/mocks/block_import_queue_worker_mock.rs @@ -0,0 +1,263 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +use crate::{header_db::HeaderDb, is_descendant_of_builder::IsDescendantOfBuilder}; +use core::marker::PhantomData; +use fork_tree::ForkTree; +use itp_types::H256; +use its_primitives::{ + traits::{Block as BlockT, Header as HeaderT}, + types::{header::SidechainHeader as Header, Block}, +}; +use its_test::sidechain_block_builder::{SidechainBlockBuilder, SidechainBlockBuilderTrait}; +use std::collections::VecDeque; + +#[derive(Default)] +pub struct BlockQueueBuilder { + queue: VecDeque, + _phantom_data: PhantomData, +} + +impl BlockQueueBuilder +where + Builder: SidechainBlockBuilderTrait + Default, + B: BlockT + From, +{ + fn new() -> Self { + Self { queue: VecDeque::new(), _phantom_data: PhantomData::default() } + } + + /// Allows definining a mock queue based and assumes that a genesis block + /// will need to be appended to the queue as the first item. + /// Returns: BuiltQueue + fn build_queue(self, f: impl FnOnce(VecDeque) -> VecDeque) -> VecDeque { + f(self.queue) + } + + fn add_genesis_block_to_queue(self) -> Self { + let mut self_mut = self; + let genesis_header = Header { + block_number: 0, + parent_hash: H256::from_slice(&[0; 32]), + ..Default::default() + }; + let block: B = Builder::default().with_header(genesis_header).build().into(); + self_mut.queue.push_back(block); + self_mut + } +} + +pub trait BlockQueueHeaderBuild { + type QueueHeader; + /// Helper trait to build a Header for a BlockQueue. + fn build_queue_header(block_number: BlockNumber, parent_hash: Hash) -> Self::QueueHeader; +} + +pub struct BlockQueueHeaderBuilder(PhantomData<(BlockNumber, Hash)>); + +impl BlockQueueHeaderBuild + for BlockQueueHeaderBuilder +where + BlockNumber: Into, + Hash: Into, +{ + type QueueHeader = Header; + /// Helper trait to build a Header for a BlockQueue. + fn build_queue_header(block_number: BlockNumber, parent_hash: Hash) -> Self::QueueHeader { + Header { + block_number: block_number.into(), + parent_hash: parent_hash.into(), + block_data_hash: H256::random(), + ..Default::default() + } + } +} + +#[derive(Debug)] +pub enum TestError { + Error, +} + +impl From<()> for TestError { + fn from(_a: ()) -> Self { + TestError::Error + } +} + +impl std::fmt::Display for TestError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "TestError") + } +} + +impl std::error::Error for TestError {} + +#[cfg(test)] +mod tests { + use super::*; + + fn fork_tree_from_header_queue(queue: VecDeque) -> ForkTree + where + B: BlockT, + { + // Store all block_headers in db + let db = HeaderDb::( + queue.iter().map(|block| (block.hash(), *block.header())).collect(), + ); + + // Import into forktree + let is_descendant_of = + , TestError>>::build_is_descendant_of(None, &db); + let mut tree = >::new(); + queue.iter().for_each(|block| { + let _ = tree + .import(block.header().hash(), block.header().block_number(), (), &is_descendant_of) + .unwrap(); + }); + tree + } + + #[test] + fn process_sequential_queue_no_forks() { + // Construct a queue which is sequential with 5 members all with distinct block numbers and parents + let mut queue = >::new() + .add_genesis_block_to_queue() + .build_queue(|mut queue| { + for i in 1..5 { + let parent_header = queue.back().unwrap().header(); + let header = >::build_queue_header( + i, + parent_header.hash(), + ); + queue.push_back(SidechainBlockBuilder::default().with_header(header).build()); + } + queue + }); + + // queue -> [0, 1, 2, 3, 4] + assert_eq!(queue.len(), 5); + + let mut tree = fork_tree_from_header_queue::(queue.clone()); + + // We have a tree which looks like this. H0 is the only root. + // + // H0 - H1 - H2 - H3 - H4 + // + + // We see that the only root of this tree is so far H0 + assert_eq!(tree.roots_hash_and_number(), vec![(&queue.front().unwrap().header.hash(), &0)]); + + // Now finalize H0 and so the new Root should be H1 + tree.finalize_root(&queue.front().unwrap().header.hash()).unwrap(); + let _ = queue.pop_front(); + assert_eq!(tree.roots_hash_and_number(), vec![(&queue.front().unwrap().header.hash(), &1)]); + } + + #[test] + fn process_sequential_queue_with_forks() { + // Construct a queue which is sequential and every odd member has 2 block numbers which are the same + let mut queue = >::new() + .add_genesis_block_to_queue() + .build_queue(|mut queue| { + for i in 1..8 { + let parent_header = queue.back().unwrap().header(); + if i % 2 == 0 && i != 1 { + // 1 is not even want all odds to have 2 of the same block_number + let header = >::build_queue_header( + i, + parent_header.hash(), + ); + queue.push_back( + SidechainBlockBuilder::default().with_header(header).build(), + ); + } else { + // build a Queue with 2 headers which are of the same block_number + let headers = vec![ + >::build_queue_header( + i, + parent_header.hash(), + ), + >::build_queue_header( + i, + parent_header.hash(), + ), + ]; + headers.iter().for_each(|header| { + queue.push_back( + SidechainBlockBuilder::default().with_header(*header).build(), + ); + }); + } + } + queue + }); + + // queue -> [0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 7, 7] + assert_eq!(queue.len(), 12); + + let mut tree = fork_tree_from_header_queue::(queue.clone()); + + // We have a tree which looks like the following + // - (H5, B3).. + // / + // - (H3, B2) + // / \ + // - (H1, B1) - (H4, B3).. + // / + // / + // (H0, B0) + // \ + // \ + // - (H2, B1).. + // + // + + // H0 is the first root + assert_eq!(tree.roots_hash_and_number(), vec![(&queue.front().unwrap().header.hash(), &0)]); + + // Now if we finalize H0 we should see 2 roots H1 and H2 + tree.finalize_root(&queue.front().unwrap().header.hash()).unwrap(); + let _ = queue.pop_front(); + assert_eq!( + tree.roots_hash_and_number(), + vec![(&queue[1].header.hash(), &1), (&queue[0].header.hash(), &1)] + ); + + // If we finalize (H1, B1) then we should see one roots (H3, B2) + let _ = queue.pop_front(); // remove (H1, B1) + tree.finalize_root(&queue.front().unwrap().header.hash()).unwrap(); + let _ = queue.pop_front(); // remove (H2, B1) + assert_eq!(tree.roots_hash_and_number(), vec![(&queue[0].header.hash(), &2)]); + + // If we finalize (H3, B2) we should see two roots (H4, B3), (H5, B3) + tree.finalize_root(&queue.front().unwrap().header.hash()).unwrap(); + let _ = queue.pop_front(); // remove (H3, B2) + assert_eq!( + tree.roots_hash_and_number(), + vec![(&queue[1].header.hash(), &3), (&queue[0].header.hash(), &3)] + ); + } + + #[test] + fn process_non_sequential_queue_without_forks() { + // TODO + } + + #[test] + fn process_non_sequential_queue_with_forks() { + // TODO + } +} diff --git a/bitacross-worker/sidechain/consensus/common/src/test/mocks/block_importer_mock.rs b/bitacross-worker/sidechain/consensus/common/src/test/mocks/block_importer_mock.rs new file mode 100644 index 0000000000..9d6060561f --- /dev/null +++ b/bitacross-worker/sidechain/consensus/common/src/test/mocks/block_importer_mock.rs @@ -0,0 +1,168 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{test::mocks::verifier_mock::VerifierMock, BlockImport, Error, Result}; +use core::marker::PhantomData; +use itp_ocall_api::EnclaveMetricsOCallApi; +use itp_sgx_crypto::aes::Aes; +use itp_sgx_externalities::SgxExternalities; +use itp_test::mock::onchain_mock::OnchainMock; +use itp_types::H256; +use its_primitives::traits::{ShardIdentifierFor, SignedBlock as SignedSidechainBlockTrait}; +use sp_core::Pair; +use sp_runtime::traits::Block as ParentchainBlockTrait; +use std::{collections::VecDeque, sync::RwLock}; + +/// Block importer mock. +pub struct BlockImportMock +where + ParentchainBlock: ParentchainBlockTrait, + SignedSidechainBlock: + SignedSidechainBlockTrait::Public> + 'static, +{ + import_result: RwLock>>, + imported_blocks: RwLock>, + _phantom: PhantomData<(ParentchainBlock, SignedSidechainBlock)>, +} + +impl BlockImportMock +where + ParentchainBlock: ParentchainBlockTrait, + SignedSidechainBlock: + SignedSidechainBlockTrait::Public> + 'static, +{ + pub fn with_import_result_once(self, result: Result) -> Self { + let mut imported_results_lock = self.import_result.write().unwrap(); + imported_results_lock.push_back(result); + std::mem::drop(imported_results_lock); + self + } + + #[allow(unused)] + pub fn with_import_result_sequence( + self, + mut results: VecDeque>, + ) -> Self { + let mut imported_results_lock = self.import_result.write().unwrap(); + imported_results_lock.append(&mut results); + std::mem::drop(imported_results_lock); + self + } + + pub fn get_imported_blocks(&self) -> Vec { + (*self.imported_blocks.read().unwrap()).clone() + } +} + +impl Default + for BlockImportMock +where + ParentchainBlock: ParentchainBlockTrait, + SignedSidechainBlock: + SignedSidechainBlockTrait::Public> + 'static, +{ + fn default() -> Self { + BlockImportMock { + import_result: RwLock::default(), + imported_blocks: RwLock::default(), + _phantom: Default::default(), + } + } +} + +impl BlockImport + for BlockImportMock +where + ParentchainBlock: ParentchainBlockTrait, + SignedSidechainBlock: + SignedSidechainBlockTrait::Public> + 'static, +{ + type Verifier = + VerifierMock; + type SidechainState = SgxExternalities; + type StateCrypto = Aes; + type Context = OnchainMock; + + fn verifier( + &self, + _maybe_last_sidechain_block: Option, + ) -> Self::Verifier { + todo!() + } + + fn apply_state_update( + &self, + _shard: &ShardIdentifierFor, + _mutating_function: F, + ) -> Result<()> + where + F: FnOnce(Self::SidechainState) -> Result, + { + todo!() + } + + fn verify_import( + &self, + _shard: &ShardIdentifierFor, + _verifying_function: F, + ) -> core::result::Result + where + F: FnOnce(&Self::SidechainState) -> core::result::Result, + { + todo!() + } + + fn state_key(&self) -> Result { + todo!() + } + + fn get_context(&self) -> &Self::Context { + todo!() + } + + fn import_parentchain_block( + &self, + _sidechain_block: &SignedSidechainBlock::Block, + _last_imported_parentchain_header: &ParentchainBlock::Header, + ) -> Result { + todo!() + } + + fn peek_parentchain_header( + &self, + _sidechain_block: &SignedSidechainBlock::Block, + _last_imported_parentchain_header: &ParentchainBlock::Header, + ) -> core::result::Result { + todo!() + } + + fn cleanup(&self, _signed_sidechain_block: &SignedSidechainBlock) -> Result<()> { + todo!() + } + + fn import_block( + &self, + signed_sidechain_block: SignedSidechainBlock, + parentchain_header: &ParentchainBlock::Header, + ) -> Result { + let mut imported_blocks_lock = self.imported_blocks.write().unwrap(); + imported_blocks_lock.push(signed_sidechain_block); + + let mut imported_results_lock = self.import_result.write().unwrap(); + imported_results_lock.pop_front().unwrap_or(Ok(parentchain_header.clone())) + } +} diff --git a/bitacross-worker/sidechain/consensus/common/src/test/mocks/confirm_block_import_mock.rs b/bitacross-worker/sidechain/consensus/common/src/test/mocks/confirm_block_import_mock.rs new file mode 100644 index 0000000000..a810da2f3b --- /dev/null +++ b/bitacross-worker/sidechain/consensus/common/src/test/mocks/confirm_block_import_mock.rs @@ -0,0 +1,29 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{error::Result, ConfirmBlockImport}; +use itp_types::ShardIdentifier; +use its_primitives::types::header::SidechainHeader; + +/// Mock implementation of the `ConfirmBlockImport` trait. +pub struct ConfirmBlockImportMock; + +impl ConfirmBlockImport for ConfirmBlockImportMock { + fn confirm_import(&self, _header: &SidechainHeader, _shard: &ShardIdentifier) -> Result<()> { + Ok(()) + } +} diff --git a/bitacross-worker/sidechain/consensus/common/src/test/mocks/mod.rs b/bitacross-worker/sidechain/consensus/common/src/test/mocks/mod.rs new file mode 100644 index 0000000000..1408ce9402 --- /dev/null +++ b/bitacross-worker/sidechain/consensus/common/src/test/mocks/mod.rs @@ -0,0 +1,21 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pub mod block_import_queue_worker_mock; +pub mod block_importer_mock; +pub mod confirm_block_import_mock; +pub mod verifier_mock; diff --git a/bitacross-worker/sidechain/consensus/common/src/test/mocks/verifier_mock.rs b/bitacross-worker/sidechain/consensus/common/src/test/mocks/verifier_mock.rs new file mode 100644 index 0000000000..e6d8cbeb0e --- /dev/null +++ b/bitacross-worker/sidechain/consensus/common/src/test/mocks/verifier_mock.rs @@ -0,0 +1,62 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{Result, ShardIdentifierFor, Verifier}; +use itp_types::H256; +use its_primitives::traits::SignedBlock as SignedSidechainBlockTrait; +use sp_core::Pair; +use sp_runtime::traits::Block as ParentchainBlockTrait; +use std::marker::PhantomData; + +/// Verifier mock implementation. +pub struct VerifierMock< + ParentchainBlock, + SignedSidechainBlock, + BlockImportParameters, + VerifierContext, +> { + _phantom: PhantomData<( + ParentchainBlock, + SignedSidechainBlock, + BlockImportParameters, + VerifierContext, + )>, +} + +impl + Verifier + for VerifierMock +where + ParentchainBlock: ParentchainBlockTrait, + SignedSidechainBlock: + SignedSidechainBlockTrait::Public> + 'static, + BlockImportParameters: Send + Sync, + VerifierContext: Send + Sync, +{ + type BlockImportParams = BlockImportParameters; + type Context = VerifierContext; + + fn verify( + &self, + _block: SignedSidechainBlock, + _parentchain_header: &ParentchainBlock::Header, + _shard: ShardIdentifierFor, + _ctx: &Self::Context, + ) -> Result { + todo!() + } +} diff --git a/bitacross-worker/sidechain/consensus/common/src/test/mod.rs b/bitacross-worker/sidechain/consensus/common/src/test/mod.rs new file mode 100644 index 0000000000..43e6cb274d --- /dev/null +++ b/bitacross-worker/sidechain/consensus/common/src/test/mod.rs @@ -0,0 +1,18 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pub mod mocks; diff --git a/bitacross-worker/sidechain/consensus/slots/Cargo.toml b/bitacross-worker/sidechain/consensus/slots/Cargo.toml new file mode 100644 index 0000000000..41070d0af0 --- /dev/null +++ b/bitacross-worker/sidechain/consensus/slots/Cargo.toml @@ -0,0 +1,78 @@ +[package] +name = "its-consensus-slots" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +derive_more = "0.99.16" +lazy_static = { version = "1.1.0", features = ["spin_no_std"] } +log = { version = "0.4", default-features = false } + +# local deps +itp-types = { path = "../../../core-primitives/types", default-features = false } +its-block-verification = { path = "../../block-verification", default-features = false } +its-primitives = { path = "../../primitives", default-features = false } + +# only for slot-stream +futures-timer = { version = "3.0", optional = true } + +# sgx deps +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true, features = ["untrusted_time"] } + +# substrate deps +sp-consensus-slots = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# local deps +itp-settings = { path = "../../../core-primitives/settings" } +itp-time-utils = { path = "../../../core-primitives/time-utils", default-features = false } +its-consensus-common = { path = "../common", default-features = false } + +# litentry +hex = { version = "0.4", default-features = false } +itp-sgx-externalities = { path = "../../../core-primitives/substrate-sgx/externalities", default-features = false } +itp-stf-state-handler = { path = "../../../core-primitives/stf-state-handler", default-features = false } +its-state = { path = "../../state", default-features = false } +lc-scheduled-enclave = { path = "../../../litentry/core/scheduled-enclave", default-features = false } + + +[dev-dependencies] +itc-parentchain-test = { path = "../../../core/parentchain/test" } +its-test = { path = "../../test" } +sp-keyring = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +itp-test = { path = "../../../core-primitives/test" } +tokio = { version = "1.6.1", features = ["full"] } + +[features] +default = ["std"] +std = [ + "codec/std", + "log/std", + # only for slot-stream + "futures-timer", + # substrate + "sp-consensus-slots/std", + "sp-runtime/std", + # local + "itp-time-utils/std", + "itp-types/std", + "its-primitives/std", + "its-block-verification/std", + "its-consensus-common/std", + "itp-stf-state-handler/std", + "itp-sgx-externalities/std", + "its-state/std", + "lc-scheduled-enclave/std", +] +sgx = [ + "itp-time-utils/sgx", + "its-consensus-common/sgx", + "sgx_tstd", + "itp-stf-state-handler/sgx", + "itp-sgx-externalities/sgx", + "its-state/sgx", + "lc-scheduled-enclave/sgx", +] diff --git a/bitacross-worker/sidechain/consensus/slots/src/lib.rs b/bitacross-worker/sidechain/consensus/slots/src/lib.rs new file mode 100644 index 0000000000..9c22327580 --- /dev/null +++ b/bitacross-worker/sidechain/consensus/slots/src/lib.rs @@ -0,0 +1,613 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Slots functionality for the integritee-sidechain. +//! +//! Some consensus algorithms have a concept of *slots*, which are intervals in +//! time during which certain events can and/or must occur. This crate +//! provides generic functionality for slots. + +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(test, feature(assert_matches))] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +#[macro_use] +extern crate sgx_tstd as std; + +use codec::Encode; +use core::str::FromStr; +use derive_more::From; +use itp_sgx_externalities::SgxExternalities; +use itp_stf_state_handler::handle_state::HandleState; +use itp_time_utils::{duration_difference, duration_now}; + +use its_consensus_common::{Error as ConsensusError, Proposer}; +use its_primitives::traits::{ + Block as SidechainBlockTrait, Header as HeaderTrait, ShardIdentifierFor, + SignedBlock as SignedSidechainBlockTrait, +}; +use its_state::SidechainSystemExt; +use lc_scheduled_enclave::ScheduledEnclaveUpdater; +use log::*; +pub use slots::*; +use sp_runtime::traits::{Block as ParentchainBlockTrait, Header as ParentchainHeaderTrait}; +use std::{fmt::Debug, sync::Arc, time::Duration, vec::Vec}; + +#[cfg(feature = "std")] +mod slot_stream; +mod slots; + +#[cfg(feature = "sgx")] +use std::sync::SgxRwLock as RwLock; + +#[cfg(feature = "std")] +use std::sync::RwLock; + +#[cfg(test)] +mod mocks; + +#[cfg(test)] +mod per_shard_slot_worker_tests; + +use itp_types::parentchain::ParentchainCall; +#[cfg(feature = "std")] +pub use slot_stream::*; +pub use slots::*; + +/// The result of [`SlotWorker::on_slot`]. +#[derive(Debug, Clone, Encode, From)] +pub struct SlotResult { + /// The result of a slot operation. + pub block: SignedSidechainBlock, + /// Parentchain state transitions triggered by sidechain state transitions. + /// + /// Any sidechain stf that invokes a parentchain stf must not commit its state change + /// before the parentchain effect has been finalized. + pub parentchain_effects: Vec, +} + +pub struct FailSlotOnDemand { + // we need to keep a internal counter because node's slot number is a function of slot_beginning_timestamp and SLOT_DURATION + current_slot: RwLock, + fail_at_slot: u64, + mode: FailSlotMode, +} + +impl FailSlotOnDemand { + pub fn new(fail_at_slot: u64, mode: FailSlotMode) -> Self { + Self { current_slot: Default::default(), fail_at_slot, mode } + } + + pub fn next_slot(&self) { + let mut current_slot_lock = self.current_slot.write().unwrap(); + *current_slot_lock += 1; + } + + pub fn check_before_on_slot(&self) -> bool { + let current_slot = self.current_slot.read().unwrap(); + *current_slot == self.fail_at_slot && matches!(&self.mode, FailSlotMode::BeforeOnSlot) + } + + pub fn check_after_on_slot(&self) -> bool { + let current_slot = self.current_slot.read().unwrap(); + *current_slot == self.fail_at_slot && matches!(&self.mode, FailSlotMode::AfterOnSlot) + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum FailSlotMode { + BeforeOnSlot, + AfterOnSlot, +} + +impl FromStr for FailSlotMode { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + match s { + "BeforeOnSlot" => Ok(FailSlotMode::BeforeOnSlot), + "AfterOnSlot" => Ok(FailSlotMode::AfterOnSlot), + _ => Err("no match"), + } + } +} + +/// A worker that should be invoked at every new slot for a specific shard. +/// +/// The implementation should not make any assumptions of the slot being bound to the time or +/// similar. The only valid assumption is that the slot number is always increasing. +pub trait SlotWorker { + /// Output generated after a slot + type Output: SignedSidechainBlockTrait + Send + 'static; + + /// Called when a new slot is triggered. + /// + /// Returns a [`SlotResult`] iff a block was successfully built in + /// the slot. Otherwise `None` is returned. + fn on_slot( + &mut self, + slot_info: SlotInfo, + shard: ShardIdentifierFor, + is_single_worker: bool, + ) -> Option>; +} + +/// A slot worker scheduler that should be invoked at every new slot. +/// +/// It manages the timeslots of individual per shard `SlotWorker`s. It gives each shard an equal +/// amount of time to produce it's result, equally distributing leftover time from a previous shard's +/// slot share to all subsequent slots. +pub trait PerShardSlotWorkerScheduler { + /// Output generated after a slot + type Output: Send + 'static; + + /// The shard type 'PerShardWorker's operate on. + type ShardIdentifier: Send + 'static + Debug + Clone; + + /// Called when a new slot is triggered. + /// + /// Returns a [`SlotResult`] iff a block was successfully built in + /// the slot. Otherwise `None` is returned. + fn on_slot( + &mut self, + slot_info: SlotInfo, + shard: Vec, + is_single_worker: bool, + ) -> Self::Output; +} + +/// A skeleton implementation for `SlotWorker` which tries to claim a slot at +/// its beginning and tries to produce a block if successfully claimed, timing +/// out if block production takes too long. +pub trait SimpleSlotWorker { + /// The type of proposer to use to build blocks. + type Proposer: Proposer; + + /// Data associated with a slot claim. + type Claim: Send + 'static; + + /// Epoch data necessary for authoring. + type EpochData: Send + 'static; + + /// Output generated after a slot + type Output: SignedSidechainBlockTrait + Send + 'static; + + /// Scheduled enclave context for authoring + type ScheduledEnclave: ScheduledEnclaveUpdater; + + /// State handler context for authoring + type StateHandler: HandleState; + + /// The logging target to use when logging messages. + fn logging_target(&self) -> &'static str; + + /// Get scheduled enclave + fn get_scheduled_enclave(&mut self) -> Arc; + + /// Get state handler for query and mutation + fn get_state_handler(&mut self) -> Arc; + + /// Returns the epoch data necessary for authoring. For time-dependent epochs, + /// use the provided slot number as a canonical source of time. + fn epoch_data( + &self, + header: &ParentchainBlock::Header, + shard: ShardIdentifierFor, + slot: Slot, + ) -> Result; + + /// Returns the number of authorities given the epoch data. + /// None indicate that the authorities information is incomplete. + fn authorities_len(&self, epoch_data: &Self::EpochData) -> Option; + + /// Tries to claim the given slot, returning an object with claim data if successful. + fn claim_slot( + &self, + header: &ParentchainBlock::Header, + slot: Slot, + epoch_data: &Self::EpochData, + ) -> Option; + + /// Creates the proposer for the current slot + fn proposer( + &mut self, + header: ParentchainBlock::Header, + shard: ShardIdentifierFor, + ) -> Result; + + /// Remaining duration for proposing. + fn proposing_remaining_duration(&self, slot_info: &SlotInfo) -> Duration; + + /// Trigger the import of the given parentchain block. + /// + /// Returns the header of the latest imported block. In case no block was imported with this trigger, + /// None is returned. + fn import_integritee_parentchain_blocks_until( + &self, + last_imported_parentchain_header: &::Hash, + ) -> Result, ConsensusError>; + + fn import_target_a_parentchain_blocks_until( + &self, + last_imported_parentchain_header: &::Hash, + ) -> Result, ConsensusError>; + + fn import_target_b_parentchain_blocks_until( + &self, + last_imported_parentchain_header: &::Hash, + ) -> Result, ConsensusError>; + + /// Peek the parentchain import queue for the latest block in queue. + /// Does not perform the import or mutate the queue. + fn peek_latest_integritee_parentchain_header( + &self, + ) -> Result, ConsensusError>; + + fn peek_latest_target_a_parentchain_header( + &self, + ) -> Result, ConsensusError>; + + fn peek_latest_target_b_parentchain_header( + &self, + ) -> Result, ConsensusError>; + + /// Implements [`SlotWorker::on_slot`]. This is an adaption from + /// substrate's sc-consensus-slots implementation. There, the slot worker handles all the + /// scheduling itself. Unfortunately, we can't use the same principle in the enclave due to some + /// futures-primitives not being available in sgx, e.g. `Delay` in our case. Hence, before + /// reimplementing the those things ourselves, we take a simplified approach and simply call + /// this function from the outside at each slot. + fn on_slot( + &mut self, + slot_info: SlotInfo, + shard: ShardIdentifierFor, + is_single_worker: bool, + ) -> Option> { + let (_timestamp, slot) = (slot_info.timestamp, slot_info.slot); + let logging_target = self.logging_target(); + + let remaining_duration = self.proposing_remaining_duration(&slot_info); + + if remaining_duration == Duration::default() { + debug!( + target: logging_target, + "Skipping proposal slot {} since there's no time left to propose", *slot, + ); + + return None + } + + let latest_integritee_parentchain_header = + match self.peek_latest_integritee_parentchain_header() { + Ok(Some(peeked_header)) => peeked_header, + Ok(None) => slot_info.last_imported_integritee_parentchain_head.clone(), + Err(e) => { + warn!( + target: logging_target, + "Failed to peek latest Integritee parentchain block header: {:?}", e + ); + return None + }, + }; + trace!( + target: logging_target, + "on_slot: a priori latest Integritee block number: {:?}", + latest_integritee_parentchain_header.number() + ); + // fixme: we need proper error handling here. we just assume there is no target_a if there is an error here, which is very brittle + let maybe_latest_target_a_parentchain_header = + match self.peek_latest_target_a_parentchain_header() { + Ok(Some(peeked_header)) => Some(peeked_header), + Ok(None) => slot_info.maybe_last_imported_target_a_parentchain_head.clone(), + Err(e) => { + debug!( + target: logging_target, + "Failed to peek latest target_a_parentchain block header: {:?}", e + ); + None + }, + }; + trace!( + target: logging_target, + "on_slot: a priori latest TargetA block number: {:?}", + maybe_latest_target_a_parentchain_header.clone().map(|h| *h.number()) + ); + + let maybe_latest_target_b_parentchain_header = + match self.peek_latest_target_b_parentchain_header() { + Ok(Some(peeked_header)) => Some(peeked_header), + Ok(None) => slot_info.maybe_last_imported_target_b_parentchain_head.clone(), + Err(e) => { + debug!( + target: logging_target, + "Failed to peek latest target_a_parentchain block header: {:?}", e + ); + None + }, + }; + trace!( + target: logging_target, + "on_slot: a priori latest TargetB block number: {:?}", + maybe_latest_target_b_parentchain_header.clone().map(|h| *h.number()) + ); + + let epoch_data = match self.epoch_data(&latest_integritee_parentchain_header, shard, slot) { + Ok(epoch_data) => epoch_data, + Err(e) => { + warn!( + target: logging_target, + "Unable to fetch epoch data at block {:?}: {:?}", + latest_integritee_parentchain_header.hash(), + e, + ); + + return None + }, + }; + + let authorities_len = self.authorities_len(&epoch_data); + + if !authorities_len.map(|a| a > 0).unwrap_or(false) { + debug!( + target: logging_target, + "Skipping proposal slot. Authorities len {:?}", authorities_len + ); + } + + // Return early if MRENCLAVE doesn't match - it implies that the enclave should be updated + let scheduled_enclave = self.get_scheduled_enclave(); + let state_handler = self.get_state_handler(); + // TODO: is this always consistent? Reference: `propose_state_update` in slot_proposer.rs + let (state, _) = state_handler.load_cloned(&shard.into()).ok()?; + let next_sidechain_number = state.get_block_number().map_or(1, |n| n + 1); + + if !scheduled_enclave.is_mrenclave_matching(next_sidechain_number) { + warn!( + target: logging_target, + "Skipping sidechain block {} due to mismatch MRENCLAVE, current: {:?}, expect: {:?}", + next_sidechain_number, + scheduled_enclave.get_current_mrenclave().map(hex::encode), + scheduled_enclave.get_expected_mrenclave(next_sidechain_number).map(hex::encode), + ); + if let Ok(false) = scheduled_enclave.is_block_production_paused() { + let _ = scheduled_enclave.set_block_production_paused(true); + info!("Pause sidechain block production"); + } + return None + } else { + // TODO: this block production pause/unpause is not strictly needed but I add it here as placeholder. + // Maybe we should add a field to describe the reason for pausing/unpausing, as + // it's possible that we want to manually/focibly pause the sidechain + if let Ok(true) = scheduled_enclave.is_block_production_paused() { + info!("Resume sidechain block production"); + let _ = scheduled_enclave.set_block_production_paused(false); + } + } + + // TODO: about the shard migration and state migration + // - the shard migration(copy-over) is done manually by the subcommand "migrate-shard". + // - the state migration is done via conditionally calling on_runtime_upgrade() by comparing + // the current runtime version and LastRuntimeUpgrade, see `stf_sgx.rs`. + // It means we need to bump the runtime version for the new enclave if we want the state + // migration to be executed. + + let _claim = self.claim_slot(&latest_integritee_parentchain_header, slot, &epoch_data)?; + + // Import the peeked parentchain header(s). + let last_imported_integritee_header = match self.import_integritee_parentchain_blocks_until( + &latest_integritee_parentchain_header.hash(), + ) { + Ok(h) => h, + Err(e) => { + debug!( + target: logging_target, + "Failed to import Integritee blocks until nr{:?}: {:?}", + latest_integritee_parentchain_header.number(), + e + ); + None + }, + }; + trace!( + target: logging_target, + "on_slot: a posteriori latest Integritee block number: {:?}", + last_imported_integritee_header.clone().map(|h| *h.number()) + ); + + let maybe_last_imported_target_a_header = + if let Some(ref header) = maybe_latest_target_a_parentchain_header { + match self.import_target_a_parentchain_blocks_until(&header.hash()) { + Ok(Some(h)) => Some(h), + Ok(None) => None, + Err(e) => { + debug!( + target: logging_target, + "Failed to import TargetA blocks until nr{:?}: {:?}", + header.number(), + e + ); + None + }, + } + } else { + None + }; + trace!( + target: logging_target, + "on_slot: a posteriori latest TargetA block number: {:?}", + maybe_last_imported_target_a_header.map(|h| *h.number()) + ); + + let maybe_last_imported_target_b_header = + if let Some(ref header) = maybe_latest_target_b_parentchain_header { + match self.import_target_b_parentchain_blocks_until(&header.hash()) { + Ok(Some(h)) => Some(h), + Ok(None) => None, + Err(e) => { + debug!( + target: logging_target, + "Failed to import TargetB blocks until nr{:?}: {:?}", + header.number(), + e + ); + None + }, + } + } else { + None + }; + + trace!( + target: logging_target, + "on_slot: a posteriori latest TargetB block number: {:?}", + maybe_last_imported_target_b_header.map(|h| *h.number()) + ); + + let proposer = match self.proposer(latest_integritee_parentchain_header.clone(), shard) { + Ok(p) => p, + Err(e) => { + warn!(target: logging_target, "Could not create proposer: {:?}", e); + return None + }, + }; + + let proposing = match proposer.propose(remaining_duration) { + Ok(p) => p, + Err(e) => { + warn!(target: logging_target, "Could not propose: {:?}", e); + return None + }, + }; + + if is_single_worker { + error!("Running as single worker, skipping timestamp within slot check") + } else if !timestamp_within_slot(&slot_info, &proposing.block) { + warn!( + target: logging_target, + "⌛️ Discarding proposal for slot {}, block number {}; block production took too long", + *slot, proposing.block.block().header().block_number(), + ); + + return None + } + + if last_imported_integritee_header.is_some() { + println!( + "Syncing Parentchains: Integritee: {:?} TargetA: {:?}, TargetB: {:?}, Sidechain: {:?}", + latest_integritee_parentchain_header.number(), + maybe_latest_target_a_parentchain_header.map(|h| *h.number()), + maybe_latest_target_b_parentchain_header.map(|h| *h.number()), + proposing.block.block().header().block_number() + ); + } + + info!("Proposing sidechain block (number: {}, hash: {}) based on integritee parentchain block (number: {:?}, hash: {:?})", + proposing.block.block().header().block_number(), proposing.block.hash(), + latest_integritee_parentchain_header.number(), latest_integritee_parentchain_header.hash() + ); + + Some(SlotResult { + block: proposing.block, + parentchain_effects: proposing.parentchain_effects, + }) + } +} + +impl + Send> + SlotWorker for T +{ + type Output = T::Output; + + fn on_slot( + &mut self, + slot_info: SlotInfo, + shard: ShardIdentifierFor, + is_single_worker: bool, + ) -> Option> { + SimpleSlotWorker::on_slot(self, slot_info, shard, is_single_worker) + } +} + +impl> + PerShardSlotWorkerScheduler for T +{ + type Output = Vec>; + + type ShardIdentifier = ShardIdentifierFor; + + fn on_slot( + &mut self, + slot_info: SlotInfo, + shards: Vec, + is_single_worker: bool, + ) -> Self::Output { + let logging_target = SimpleSlotWorker::logging_target(self); + + let mut remaining_shards = shards.len(); + let mut slot_results = Vec::with_capacity(remaining_shards); + + for shard in shards.into_iter() { + let now = duration_now(); // It's important we have a common `now` for all following computations. + let shard_remaining_duration = duration_difference(now, slot_info.ends_at) + .and_then(|time| time.checked_div(remaining_shards as u32)) + .unwrap_or_default(); + + // important to check against millis here. We had the corner-case in production + // setup where `shard_remaining_duration` contained only nanos. + if shard_remaining_duration.as_millis() == u128::default() { + info!( + target: logging_target, + "⌛️ Could not produce blocks for all shards; block production took too long", + ); + + return slot_results + } + + let shard_slot_ends_at = now + shard_remaining_duration; + let shard_slot = SlotInfo::new( + slot_info.slot, + now, + shard_remaining_duration, + shard_slot_ends_at, + slot_info.last_imported_integritee_parentchain_head.clone(), + slot_info.maybe_last_imported_target_a_parentchain_head.clone(), + slot_info.maybe_last_imported_target_b_parentchain_head.clone(), + ); + + match SimpleSlotWorker::on_slot(self, shard_slot.clone(), shard, is_single_worker) { + Some(res) => { + slot_results.push(res); + debug!( + target: logging_target, + "on_slot: produced block for slot: {:?} in shard {:?}", shard_slot, shard + ) + }, + None => info!( + target: logging_target, + "Did not propose a block for slot {} in shard {:?}", *slot_info.slot, shard + ), + } + + remaining_shards -= 1; + } + + slot_results + } +} diff --git a/bitacross-worker/sidechain/consensus/slots/src/mocks.rs b/bitacross-worker/sidechain/consensus/slots/src/mocks.rs new file mode 100644 index 0000000000..409fb41987 --- /dev/null +++ b/bitacross-worker/sidechain/consensus/slots/src/mocks.rs @@ -0,0 +1,158 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{slots::Slot, SimpleSlotWorker, SlotInfo, SlotResult}; +pub use itp_test::mock::handle_state_mock::HandleStateMock; +use its_consensus_common::{Proposal, Proposer, Result}; +use its_primitives::{traits::ShardIdentifierFor, types::SignedBlock as SignedSidechainBlock}; +use lc_scheduled_enclave::ScheduledEnclaveMock; +use sp_runtime::traits::{Block as ParentchainBlockTrait, Header as ParentchainHeaderTrait}; +use std::{marker::PhantomData, sync::Arc, thread, time::Duration}; + +#[derive(Default)] +pub(crate) struct ProposerMock { + _phantom: PhantomData, +} + +impl Proposer for ProposerMock +where + B: ParentchainBlockTrait, +{ + fn propose(&self, _max_duration: Duration) -> Result> { + todo!() + } +} + +#[derive(Default)] +pub(crate) struct SimpleSlotWorkerMock +where + B: ParentchainBlockTrait, +{ + pub slot_infos: Vec>, + pub slot_time_spent: Option, +} + +impl SimpleSlotWorker for SimpleSlotWorkerMock +where + B: ParentchainBlockTrait, +{ + type Proposer = ProposerMock; + + type Claim = u64; + + type EpochData = u64; + + type Output = SignedSidechainBlock; + + type ScheduledEnclave = ScheduledEnclaveMock; + + type StateHandler = HandleStateMock; + + fn logging_target(&self) -> &'static str { + "test" + } + + fn get_scheduled_enclave(&mut self) -> Arc { + todo!() + } + + fn get_state_handler(&mut self) -> Arc { + todo!() + } + + fn epoch_data( + &self, + _header: &B::Header, + _shard: ShardIdentifierFor, + _slot: Slot, + ) -> Result { + todo!() + } + + fn authorities_len(&self, _epoch_data: &Self::EpochData) -> Option { + todo!() + } + + fn claim_slot( + &self, + _header: &B::Header, + _slot: Slot, + _epoch_data: &Self::EpochData, + ) -> Option { + todo!() + } + + fn proposer( + &mut self, + _header: B::Header, + _shard: ShardIdentifierFor, + ) -> Result { + todo!() + } + + fn proposing_remaining_duration(&self, _slot_info: &SlotInfo) -> Duration { + todo!() + } + + fn import_integritee_parentchain_blocks_until( + &self, + _last_imported_parentchain_header: &::Hash, + ) -> Result> { + todo!() + } + + fn peek_latest_integritee_parentchain_header(&self) -> Result> { + todo!() + } + + fn import_target_a_parentchain_blocks_until( + &self, + _last_imported_parentchain_header: &::Hash, + ) -> Result> { + todo!() + } + + fn peek_latest_target_a_parentchain_header(&self) -> Result> { + todo!() + } + + fn import_target_b_parentchain_blocks_until( + &self, + _last_imported_parentchain_header: &::Hash, + ) -> Result> { + todo!() + } + + fn peek_latest_target_b_parentchain_header(&self) -> Result> { + todo!() + } + + fn on_slot( + &mut self, + slot_info: SlotInfo, + _shard: ShardIdentifierFor, + _is_single_worker: bool, + ) -> Option> { + self.slot_infos.push(slot_info); + + if let Some(sleep_duration) = self.slot_time_spent { + thread::sleep(sleep_duration); + } + + None + } +} diff --git a/bitacross-worker/sidechain/consensus/slots/src/per_shard_slot_worker_tests.rs b/bitacross-worker/sidechain/consensus/slots/src/per_shard_slot_worker_tests.rs new file mode 100644 index 0000000000..b9bcaf92f0 --- /dev/null +++ b/bitacross-worker/sidechain/consensus/slots/src/per_shard_slot_worker_tests.rs @@ -0,0 +1,100 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{mocks::SimpleSlotWorkerMock, PerShardSlotWorkerScheduler, SlotInfo}; +use itc_parentchain_test::ParentchainHeaderBuilder; +use itp_settings::sidechain::SLOT_DURATION; +use itp_time_utils::duration_now; +use itp_types::{Block as ParentchainBlock, ShardIdentifier}; +use its_block_verification::slot::slot_from_timestamp_and_duration; + +type TestSlotWorker = SimpleSlotWorkerMock; + +#[test] +fn slot_timings_are_correct_with_multiple_shards() { + let slot_info = slot_info_from_now(); + let mut slot_worker = + TestSlotWorker { slot_infos: Vec::new(), slot_time_spent: Some(SLOT_DURATION / 10) }; + + let shards = + vec![ShardIdentifier::default(), ShardIdentifier::default(), ShardIdentifier::default()]; + + let _slot_results = PerShardSlotWorkerScheduler::on_slot( + &mut slot_worker, + slot_info.clone(), + shards.clone(), + false, + ); + + assert_eq!(slot_worker.slot_infos.len(), shards.len()); + + // end-time of the first shard slot should not exceed timestamp + 1/(n_shards) of the total slot duration + let first_shard_slot_end_time = slot_worker.slot_infos.first().unwrap().ends_at.as_millis(); + let expected_upper_bound = (slot_info.timestamp.as_millis() + + SLOT_DURATION.as_millis().checked_div(shards.len() as u128).unwrap()) + + 2u128; + assert!( + first_shard_slot_end_time <= expected_upper_bound, + "First shard end time, expected: {}, actual: {}", + expected_upper_bound, + first_shard_slot_end_time + ); + + // none of the shard slot end times should exceed the global slot end time + for shard_slot_info in slot_worker.slot_infos { + assert!( + shard_slot_info.ends_at.as_millis() <= slot_info.ends_at.as_millis(), + "shard slot info ends at: {} ms, total slot info ends at: {} ms", + shard_slot_info.ends_at.as_millis(), + slot_info.ends_at.as_millis() + ); + } +} + +#[test] +fn if_shard_takes_up_all_slot_time_subsequent_shards_are_not_served() { + let slot_info = slot_info_from_now(); + let mut slot_worker = + TestSlotWorker { slot_infos: Vec::new(), slot_time_spent: Some(SLOT_DURATION) }; + + let shards = + vec![ShardIdentifier::default(), ShardIdentifier::default(), ShardIdentifier::default()]; + + let _slot_results = PerShardSlotWorkerScheduler::on_slot( + &mut slot_worker, + slot_info.clone(), + shards.clone(), + false, + ); + + assert_eq!(1, slot_worker.slot_infos.len()); +} + +fn slot_info_from_now() -> SlotInfo { + let timestamp_now = duration_now(); + let slot = slot_from_timestamp_and_duration(timestamp_now, SLOT_DURATION); + let slot_ends_at = timestamp_now + SLOT_DURATION; + SlotInfo::new( + slot, + timestamp_now, + SLOT_DURATION, + slot_ends_at, + ParentchainHeaderBuilder::default().build(), + None, + None, + ) +} diff --git a/bitacross-worker/sidechain/consensus/slots/src/slot_stream.rs b/bitacross-worker/sidechain/consensus/slots/src/slot_stream.rs new file mode 100644 index 0000000000..1c738419bf --- /dev/null +++ b/bitacross-worker/sidechain/consensus/slots/src/slot_stream.rs @@ -0,0 +1,116 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Slots functionality for Substrate. +//! +//! Some consensus algorithms have a concept of *slots*, which are intervals in +//! time during which certain events can and/or must occur. This crate +//! provides generic functionality for slots. + +use crate::time_until_next_slot; +use futures_timer::Delay; +use std::time::Duration; + +/// Executes given `task` repeatedly when the next slot becomes available. +pub async fn start_slot_worker(task: F, slot_duration: Duration) +where + F: Fn(), +{ + let mut slot_stream = SlotStream::new(slot_duration); + + loop { + slot_stream.next_slot().await; + task(); + } +} + +/// Stream to calculate the slot schedule with. +pub struct SlotStream { + slot_duration: Duration, + inner_delay: Option, +} + +impl SlotStream { + pub fn new(slot_duration: Duration) -> Self { + SlotStream { slot_duration, inner_delay: None } + } +} + +impl SlotStream { + /// Waits for the duration of `inner_delay`. + /// Upon timeout, `inner_delay` is reset according to the time left until next slot. + pub async fn next_slot(&mut self) { + self.inner_delay = match self.inner_delay.take() { + None => { + // Delay is not initialized in this case, + // so we have to initialize with the time until the next slot. + let wait_dur = time_until_next_slot(self.slot_duration); + Some(Delay::new(wait_dur)) + }, + Some(d) => Some(d), + }; + + if let Some(inner_delay) = self.inner_delay.take() { + inner_delay.await; + } + + let ends_in = time_until_next_slot(self.slot_duration); + + // Re-schedule delay for next slot. + self.inner_delay = Some(Delay::new(ends_in)); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::{thread, time::Instant}; + + const SLOT_DURATION: Duration = Duration::from_millis(300); + const SLOT_TOLERANCE: Duration = Duration::from_millis(10); + + #[tokio::test] + async fn short_task_execution_does_not_influence_next_slot() { + let mut slot_stream = SlotStream::new(SLOT_DURATION); + + slot_stream.next_slot().await; + let now = Instant::now(); + // Task execution is shorter than slot duration. + thread::sleep(Duration::from_millis(200)); + slot_stream.next_slot().await; + + let elapsed = now.elapsed(); + assert!(elapsed >= SLOT_DURATION - SLOT_TOLERANCE); + assert!(elapsed <= SLOT_DURATION + SLOT_TOLERANCE); + } + + #[tokio::test] + async fn long_task_execution_does_not_cause_drift() { + let mut slot_stream = SlotStream::new(SLOT_DURATION); + + slot_stream.next_slot().await; + let now = Instant::now(); + // Task execution is longer than slot duration. + thread::sleep(Duration::from_millis(500)); + slot_stream.next_slot().await; + slot_stream.next_slot().await; + + let elapsed = now.elapsed(); + assert!(elapsed >= 2 * SLOT_DURATION - SLOT_TOLERANCE); + assert!(elapsed <= 2 * SLOT_DURATION + SLOT_TOLERANCE); + } +} diff --git a/bitacross-worker/sidechain/consensus/slots/src/slots.rs b/bitacross-worker/sidechain/consensus/slots/src/slots.rs new file mode 100644 index 0000000000..7f8a910a97 --- /dev/null +++ b/bitacross-worker/sidechain/consensus/slots/src/slots.rs @@ -0,0 +1,421 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Utility stream for yielding slots in a loop. +//! +//! This is used instead of `futures_timer::Interval` because it was unreliable. + +use itp_time_utils::duration_now; +use its_block_verification::slot::slot_from_timestamp_and_duration; +use its_consensus_common::Error as ConsensusError; +use its_primitives::traits::{ + Block as SidechainBlockTrait, BlockData, SignedBlock as SignedSidechainBlockTrait, +}; +use lazy_static::lazy_static; +use log::warn; +use sp_runtime::traits::Block as ParentchainBlockTrait; +use std::time::Duration; + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use std::sync::SgxRwLock as RwLock; + +#[cfg(all(feature = "std", not(feature = "sgx")))] +use std::sync::RwLock; + +pub use sp_consensus_slots::Slot; + +/// Returns the duration until the next slot from now. +pub fn time_until_next_slot(slot_duration: Duration) -> Duration { + let now = duration_now().as_millis(); + + if slot_duration.as_millis() == u128::default() { + log::warn!("[Slots]: slot_duration.as_millis() is 0"); + return Default::default() + } + + let next_slot = (now + slot_duration.as_millis()) / slot_duration.as_millis(); + let remaining_millis = next_slot * slot_duration.as_millis() - now; + Duration::from_millis(remaining_millis as u64) +} + +/// Information about a slot. +#[derive(Debug, Clone)] +pub struct SlotInfo { + /// The slot number as found in the inherent data. + pub slot: Slot, + /// Current timestamp as found in the inherent data. + pub timestamp: Duration, + /// Slot duration. + pub duration: Duration, + /// The time at which the slot ends. + pub ends_at: Duration, + /// Last imported parentchain header, potentially outdated. + pub last_imported_integritee_parentchain_head: ParentchainBlock::Header, + /// Last imported parentchain header, potentially outdated. + pub maybe_last_imported_target_a_parentchain_head: Option, + /// Last imported parentchain header, potentially outdated. + pub maybe_last_imported_target_b_parentchain_head: Option, +} + +impl SlotInfo { + /// Create a new [`SlotInfo`]. + /// + /// `ends_at` is calculated using `now` and `time_until_next_slot`. + pub fn new( + slot: Slot, + timestamp: Duration, + duration: Duration, + ends_at: Duration, + last_imported_integritee_parentchain_head: ParentchainBlock::Header, + maybe_last_imported_target_a_parentchain_head: Option, + maybe_last_imported_target_b_parentchain_head: Option, + ) -> Self { + Self { + slot, + timestamp, + duration, + ends_at, + last_imported_integritee_parentchain_head, + maybe_last_imported_target_a_parentchain_head, + maybe_last_imported_target_b_parentchain_head, + } + } + + pub fn duration_remaining(&self) -> Option { + let duration_now = duration_now(); + if self.ends_at <= duration_now { + return None + } + Some(self.ends_at - duration_now) + } +} + +/// The time at which the slot ends. +/// +/// !! Slot duration needs to be the 'global' slot duration that is used for the sidechain. +/// Do not use this with 'custom' slot durations, as used e.g. for the shard slots. +pub fn slot_ends_at(slot: Slot, slot_duration: Duration) -> Duration { + Duration::from_millis(*slot.saturating_add(1u64) * (slot_duration.as_millis() as u64)) +} + +#[allow(dead_code)] +pub(crate) fn timestamp_within_slot< + ParentchainBlock: ParentchainBlockTrait, + SignedSidechainBlock: SignedSidechainBlockTrait, +>( + slot: &SlotInfo, + proposal: &SignedSidechainBlock, +) -> bool { + let proposal_stamp = proposal.block().block_data().timestamp(); + + let is_within_slot = slot.timestamp.as_millis() as u64 <= proposal_stamp + && slot.ends_at.as_millis() as u64 >= proposal_stamp; + + if !is_within_slot { + warn!( + "Proposed block slot time: {} ms, slot start: {} ms , slot end: {} ms", + proposal_stamp, + slot.timestamp.as_millis(), + slot.ends_at.as_millis() + ); + } + + is_within_slot +} + +pub fn yield_next_slot( + timestamp: Duration, + duration: Duration, + integritee_header: ParentchainBlock::Header, + maybe_target_a_header: Option, + maybe_target_b_header: Option, + last_slot_getter: &mut SlotGetter, +) -> Result>, ConsensusError> +where + SlotGetter: LastSlotTrait, + ParentchainBlock: ParentchainBlockTrait, +{ + if duration == Default::default() { + return Err(ConsensusError::Other("Tried to yield next slot with 0 duration".into())) + } + + let last_slot = last_slot_getter.get_last_slot()?; + let slot = slot_from_timestamp_and_duration(timestamp, duration); + + if slot <= last_slot { + return Ok(None) + } + + last_slot_getter.set_last_slot(slot)?; + + let slot_ends_time = slot_ends_at(slot, duration); + Ok(Some(SlotInfo::new( + slot, + timestamp, + duration, + slot_ends_time, + integritee_header, + maybe_target_a_header, + maybe_target_b_header, + ))) +} + +pub trait LastSlotTrait { + fn get_last_slot(&self) -> Result; + fn set_last_slot(&mut self, slot: Slot) -> Result<(), ConsensusError>; +} + +pub struct LastSlot; + +lazy_static! { + static ref LAST_SLOT: RwLock = Default::default(); +} + +impl LastSlotTrait for LastSlot { + fn get_last_slot(&self) -> Result { + Ok(*LAST_SLOT.read().map_err(|e| ConsensusError::Other(format!("{:?}", e).into()))?) + } + + fn set_last_slot(&mut self, slot: Slot) -> Result<(), ConsensusError> { + *LAST_SLOT + .write() + .map_err(|e| ConsensusError::Other(format!("{:?}", e).into()))? = slot; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use core::assert_matches::assert_matches; + use itc_parentchain_test::ParentchainHeaderBuilder; + use itp_types::Block as ParentchainBlock; + use its_primitives::{ + traits::{Block as BlockT, SignBlock}, + types::block::{Block, SignedBlock}, + }; + use its_test::{ + sidechain_block_data_builder::SidechainBlockDataBuilder, + sidechain_header_builder::SidechainHeaderBuilder, + }; + use sp_keyring::ed25519::Keyring; + use std::{fmt::Debug, thread, time::SystemTime}; + + const SLOT_DURATION: Duration = Duration::from_millis(1000); + const ALLOWED_THRESHOLD: Duration = Duration::from_millis(1); + + fn test_block_with_time_stamp(timestamp: u64) -> SignedBlock { + let header = SidechainHeaderBuilder::default().build(); + + let block_data = SidechainBlockDataBuilder::default().with_timestamp(timestamp).build(); + + Block::new(header, block_data).sign_block(&Keyring::Alice.pair()) + } + + fn slot(slot: u64) -> SlotInfo { + SlotInfo { + slot: slot.into(), + timestamp: duration_now(), + duration: SLOT_DURATION, + ends_at: duration_now() + SLOT_DURATION, + last_imported_integritee_parentchain_head: ParentchainHeaderBuilder::default().build(), + maybe_last_imported_target_a_parentchain_head: None, + maybe_last_imported_target_b_parentchain_head: None, + } + } + + fn timestamp_in_the_future(later: Duration) -> u64 { + let moment = SystemTime::now() + later; + let dur = moment.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_else(|e| { + panic!("Current time {:?} is before unix epoch. Something is wrong: {:?}", moment, e) + }); + dur.as_millis() as u64 + } + + fn timestamp_in_the_past(earlier: Duration) -> u64 { + let moment = SystemTime::now() - earlier; + let dur = moment.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_else(|e| { + panic!("Current time {:?} is before unix epoch. Something is wrong: {:?}", moment, e) + }); + dur.as_millis() as u64 + } + + fn assert_consensus_other_err(result: Result, msg: &str) { + assert_matches!(result.unwrap_err(), ConsensusError::Other( + m, + ) if m.to_string() == msg) + } + + #[test] + fn time_until_next_slot_returns_default_on_nano_duration() { + // prevent panic: https://github.com/integritee-network/worker/issues/439 + assert_eq!(time_until_next_slot(Duration::from_nanos(999)), Default::default()) + } + + #[test] + fn slot_info_ends_at_does_not_change_after_second_calculation() { + let timestamp = duration_now(); + let pc_header = ParentchainHeaderBuilder::default().build(); + let slot: Slot = 1000.into(); + + let slot_end_time = slot_ends_at(slot, SLOT_DURATION); + let slot_one: SlotInfo = SlotInfo::new( + slot, + timestamp, + SLOT_DURATION, + slot_end_time, + pc_header.clone(), + None, + None, + ); + thread::sleep(Duration::from_millis(200)); + let slot_two: SlotInfo = + SlotInfo::new(slot, timestamp, SLOT_DURATION, slot_end_time, pc_header, None, None); + + let difference_of_ends_at = + (slot_one.ends_at.as_millis()).abs_diff(slot_two.ends_at.as_millis()); + + assert!( + difference_of_ends_at < ALLOWED_THRESHOLD.as_millis(), + "Diff in ends at timestamp: {} ms, tolerance: {} ms", + difference_of_ends_at, + ALLOWED_THRESHOLD.as_millis() + ); + } + + #[test] + fn duration_remaing_returns_none_if_ends_at_is_in_the_past() { + let slot: SlotInfo = SlotInfo { + slot: 1.into(), + timestamp: duration_now() - Duration::from_secs(5), + duration: SLOT_DURATION, + ends_at: duration_now() + SLOT_DURATION - Duration::from_secs(5), + last_imported_integritee_parentchain_head: ParentchainHeaderBuilder::default().build(), + maybe_last_imported_target_a_parentchain_head: None, + maybe_last_imported_target_b_parentchain_head: None, + }; + assert!(slot.duration_remaining().is_none()); + } + + #[test] + fn duration_remaining_returns_some_if_ends_at_is_in_the_future() { + let slot: SlotInfo = SlotInfo { + slot: 1.into(), + timestamp: duration_now() - Duration::from_secs(5), + duration: SLOT_DURATION, + ends_at: duration_now() + Duration::from_secs(60), + last_imported_integritee_parentchain_head: ParentchainHeaderBuilder::default().build(), + maybe_last_imported_target_a_parentchain_head: None, + maybe_last_imported_target_b_parentchain_head: None, + }; + let maybe_duration_remaining = slot.duration_remaining(); + assert!(maybe_duration_remaining.is_some()); + assert!(maybe_duration_remaining.unwrap() > Duration::from_secs(30)); + } + + #[test] + fn slot_info_ends_at_does_is_correct_even_if_delay_is_more_than_slot_duration() { + let timestamp = duration_now(); + let pc_header = ParentchainHeaderBuilder::default().build(); + let slot: Slot = 1000.into(); + let slot_end_time = slot_ends_at(slot, SLOT_DURATION); + + thread::sleep(SLOT_DURATION * 2); + let slot: SlotInfo = + SlotInfo::new(slot, timestamp, SLOT_DURATION, slot_end_time, pc_header, None, None); + + assert!(slot.ends_at < duration_now()); + } + + #[test] + fn timestamp_within_slot_returns_true_for_correct_timestamp() { + let slot = slot(1); + let time_stamp_in_slot = timestamp_in_the_future(SLOT_DURATION / 2); + + let block = test_block_with_time_stamp(time_stamp_in_slot); + + assert!(timestamp_within_slot(&slot, &block)); + } + + #[test] + fn timestamp_within_slot_returns_false_if_timestamp_after_slot() { + let slot = slot(1); + let time_stamp_after_slot = + timestamp_in_the_future(SLOT_DURATION + Duration::from_millis(10)); + + let block_too_late = test_block_with_time_stamp(time_stamp_after_slot); + + assert!(!timestamp_within_slot(&slot, &block_too_late)); + } + + #[test] + fn timestamp_within_slot_returns_false_if_timestamp_before_slot() { + let slot = slot(1); + let time_stamp_before_slot = timestamp_in_the_past(Duration::from_millis(10)); + + let block_too_early = test_block_with_time_stamp(time_stamp_before_slot); + + assert!(!timestamp_within_slot(&slot, &block_too_early)); + } + + #[test] + fn yield_next_slot_returns_none_when_slot_equals_last_slot() { + let _lock = + LastSlot.set_last_slot(slot_from_timestamp_and_duration(duration_now(), SLOT_DURATION)); + assert!(yield_next_slot::<_, ParentchainBlock>( + duration_now(), + SLOT_DURATION, + ParentchainHeaderBuilder::default().build(), + None, + None, + &mut LastSlot, + ) + .unwrap() + .is_none()) + } + + #[test] + fn yield_next_slot_returns_next_slot() { + let _lock = + LastSlot.set_last_slot(slot_from_timestamp_and_duration(duration_now(), SLOT_DURATION)); + assert!(yield_next_slot::<_, ParentchainBlock>( + duration_now() + SLOT_DURATION, + SLOT_DURATION, + ParentchainHeaderBuilder::default().build(), + None, + None, + &mut LastSlot + ) + .unwrap() + .is_some()) + } + + #[test] + fn yield_next_slot_returns_err_on_0_duration() { + assert_consensus_other_err( + yield_next_slot::<_, ParentchainBlock>( + duration_now(), + Default::default(), + ParentchainHeaderBuilder::default().build(), + None, + None, + &mut LastSlot, + ), + "Tried to yield next slot with 0 duration", + ) + } +} diff --git a/bitacross-worker/sidechain/fork-tree/Cargo.toml b/bitacross-worker/sidechain/fork-tree/Cargo.toml new file mode 100644 index 0000000000..6b9c4fc561 --- /dev/null +++ b/bitacross-worker/sidechain/fork-tree/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "fork-tree" +version = "3.0.0" +authors = ["Parity Technologies "] +edition = "2021" +license = "Apache-2.0" +homepage = "https://substrate.io" +repository = "https://github.com/paritytech/substrate/" +description = "Utility library for managing tree-like ordered data with logic for pruning the tree while finalizing nodes." +documentation = "https://docs.rs/fork-tree" +readme = "README.md" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"], default-features = false } + +# sgx deps +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } + +[features] +default = ["std"] +std = [ + "codec/std", +] +sgx = [ + # teaclave + "sgx_tstd", +] diff --git a/bitacross-worker/sidechain/fork-tree/src/lib.rs b/bitacross-worker/sidechain/fork-tree/src/lib.rs new file mode 100644 index 0000000000..0af11b653b --- /dev/null +++ b/bitacross-worker/sidechain/fork-tree/src/lib.rs @@ -0,0 +1,1552 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Utility library for managing tree-like ordered data with logic for pruning +//! the tree while finalizing nodes. + +#![cfg_attr(not(feature = "std"), no_std)] +#![warn(missing_docs)] + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +#[macro_use] +extern crate sgx_tstd as std; + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use std::vec::Vec; + +use codec::{Decode, Encode}; +use core::cmp::Reverse; + +/// Error occurred when iterating with the tree. +#[derive(Clone, Debug, PartialEq)] +pub enum Error { + /// Adding duplicate node to tree. + Duplicate, + /// Finalizing descendent of tree node without finalizing ancestor(s). + UnfinalizedAncestor, + /// Imported or finalized node that is an ancestor of previously finalized node. + Revert, + /// Error throw by client when checking for node ancestry. + Client(E), +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + let message = match *self { + Error::Duplicate => "Hash already exists in Tree".into(), + Error::UnfinalizedAncestor => "Finalized descendent of Tree node without finalizing its ancestor(s) first".into(), + Error::Revert => "Tried to import or finalize node that is an ancestor of a previously finalized node".into(), + Error::Client(ref err) => format!("Client error: {}", err), + }; + write!(f, "{}", message) + } +} + +impl std::error::Error for Error { + fn cause(&self) -> Option<&dyn std::error::Error> { + None + } +} + +impl From for Error { + fn from(err: E) -> Error { + Error::Client(err) + } +} + +/// Result of finalizing a node (that could be a part of the tree or not). +#[derive(Debug, PartialEq)] +pub enum FinalizationResult { + /// The tree has changed, optionally return the value associated with the finalized node. + Changed(Option), + /// The tree has not changed. + Unchanged, +} + +/// Filtering action. +#[derive(Debug, PartialEq)] +pub enum FilterAction { + /// Remove the node and its subtree. + Remove, + /// Maintain the node. + KeepNode, + /// Maintain the node and its subtree. + KeepTree, +} + +/// A tree data structure that stores several nodes across multiple branches. +/// +/// Top-level branches are called roots. The tree has functionality for +/// finalizing nodes, which means that that node is traversed, and all competing +/// branches are pruned. It also guarantees that nodes in the tree are finalized +/// in order. Each node is uniquely identified by its hash but can be ordered by +/// its number. In order to build the tree an external function must be provided +/// when interacting with the tree to establish a node's ancestry. +#[derive(Clone, Debug, Decode, Encode, PartialEq)] +pub struct ForkTree { + roots: Vec>, + best_finalized_number: Option, +} + +impl Default for ForkTree { + fn default() -> ForkTree { + ForkTree { roots: Vec::new(), best_finalized_number: None } + } +} + +impl ForkTree +where + H: PartialEq, + N: Ord, +{ + /// Create a new empty tree. + pub fn new() -> ForkTree { + ForkTree { roots: Vec::new(), best_finalized_number: None } + } + + /// Rebalance the tree, i.e. sort child nodes by max branch depth (decreasing). + /// + /// Most operations in the tree are performed with depth-first search + /// starting from the leftmost node at every level, since this tree is meant + /// to be used in a blockchain context, a good heuristic is that the node + /// we'll be looking for at any point will likely be in one of the deepest chains + /// (i.e. the longest ones). + pub fn rebalance(&mut self) { + self.roots.sort_by_key(|n| Reverse(n.max_depth())); + let mut stack: Vec<_> = self.roots.iter_mut().collect(); + while let Some(node) = stack.pop() { + node.children.sort_by_key(|n| Reverse(n.max_depth())); + stack.extend(node.children.iter_mut()); + } + } + + /// Import a new node into the tree. The given function `is_descendent_of` + /// should return `true` if the second hash (target) is a descendent of the + /// first hash (base). This method assumes that nodes in the same branch are + /// imported in order. + /// + /// Returns `true` if the imported node is a root. + // WARNING: some users of this method (i.e. consensus epoch changes tree) currently silently + // rely on a **post-order DFS** traversal. If we are using instead a top-down traversal method + // then the `is_descendent_of` closure, when used after a warp-sync, may end up querying the + // backend for a block (the one corresponding to the root) that is not present and thus will + // return a wrong result. + pub fn import( + &mut self, + hash: H, + number: N, + data: V, + is_descendent_of: &F, + ) -> Result> + where + E: std::error::Error, + F: Fn(&H, &H) -> Result, + H: std::fmt::Debug, + { + if let Some(ref best_finalized_number) = self.best_finalized_number { + if number <= *best_finalized_number { + return Err(Error::Revert) + } + } + + let (children, is_root) = + match self.find_node_where_mut(&hash, &number, is_descendent_of, &|_| true)? { + Some(parent) => (&mut parent.children, false), + None => (&mut self.roots, true), + }; + + if children.iter().any(|elem| elem.hash == hash) { + return Err(Error::Duplicate) + } + + children.push(Node { data, hash, number, children: Default::default() }); + + if children.len() == 1 { + // Rebalance may be required only if we've extended the branch depth. + self.rebalance(); + } + + Ok(is_root) + } + + /// Iterates over the existing roots in the tree. + pub fn roots(&self) -> impl Iterator { + self.roots.iter().map(|node| (&node.hash, &node.number, &node.data)) + } + + /// Iterates over the roots and gives just the hash and block number + pub fn roots_hash_and_number(&self) -> Vec<(&H, &N)> { + self.roots.iter().map(|node| (&node.hash, &node.number)).collect::>() + } + + fn node_iter(&self) -> impl Iterator> { + // we need to reverse the order of roots to maintain the expected + // ordering since the iterator uses a stack to track state. + ForkTreeIterator { stack: self.roots.iter().rev().collect() } + } + + /// Iterates the nodes in the tree in pre-order. + pub fn iter(&self) -> impl Iterator { + self.node_iter().map(|node| (&node.hash, &node.number, &node.data)) + } + + /// Map fork tree into values of new types. + /// + /// Tree traversal technique (e.g. BFS vs DFS) is left as not specified and + /// may be subject to change in the future. In other words, your predicates + /// should not rely on the observed traversal technique currently in use. + pub fn map(self, f: &mut F) -> ForkTree + where + F: FnMut(&H, &N, V) -> VT, + { + let mut queue: Vec<_> = + self.roots.into_iter().rev().map(|node| (usize::MAX, node)).collect(); + let mut next_queue = Vec::new(); + let mut output = Vec::new(); + + while !queue.is_empty() { + for (parent_index, node) in queue.drain(..) { + let new_data = f(&node.hash, &node.number, node.data); + let new_node = Node { + hash: node.hash, + number: node.number, + data: new_data, + children: Vec::with_capacity(node.children.len()), + }; + + let node_id = output.len(); + output.push((parent_index, new_node)); + + for child in node.children.into_iter().rev() { + next_queue.push((node_id, child)); + } + } + + std::mem::swap(&mut queue, &mut next_queue); + } + + let mut roots = Vec::new(); + while let Some((parent_index, new_node)) = output.pop() { + if parent_index == usize::MAX { + roots.push(new_node); + } else { + output[parent_index].1.children.push(new_node); + } + } + + ForkTree { roots, best_finalized_number: self.best_finalized_number } + } + + /// Find a node in the tree that is the deepest ancestor of the given + /// block hash and which passes the given predicate. The given function + /// `is_descendent_of` should return `true` if the second hash (target) + /// is a descendent of the first hash (base). + pub fn find_node_where( + &self, + hash: &H, + number: &N, + is_descendent_of: &F, + predicate: &P, + ) -> Result>, Error> + where + E: std::error::Error, + F: Fn(&H, &H) -> Result, + P: Fn(&V) -> bool, + { + let maybe_path = self.find_node_index_where(hash, number, is_descendent_of, predicate)?; + Ok(maybe_path.map(|path| { + let children = + path.iter().take(path.len() - 1).fold(&self.roots, |curr, &i| &curr[i].children); + &children[path[path.len() - 1]] + })) + } + + /// Same as [`find_node_where`](ForkTree::find_node_where), but returns mutable reference. + pub fn find_node_where_mut( + &mut self, + hash: &H, + number: &N, + is_descendent_of: &F, + predicate: &P, + ) -> Result>, Error> + where + E: std::error::Error, + F: Fn(&H, &H) -> Result, + P: Fn(&V) -> bool, + { + let maybe_path = self.find_node_index_where(hash, number, is_descendent_of, predicate)?; + Ok(maybe_path.map(|path| { + let children = path + .iter() + .take(path.len() - 1) + .fold(&mut self.roots, |curr, &i| &mut curr[i].children); + &mut children[path[path.len() - 1]] + })) + } + + /// Same as [`find_node_where`](ForkTree::find_node_where), but returns indices. + /// + /// The returned indices represent the full path to reach the matching node starting + /// from first to last, i.e. the earliest index in the traverse path goes first, and the final + /// index in the traverse path goes last. If a node is found that matches the predicate + /// the returned path should always contain at least one index, otherwise `None` is + /// returned. + // WARNING: some users of this method (i.e. consensus epoch changes tree) currently silently + // rely on a **post-order DFS** traversal. If we are using instead a top-down traversal method + // then the `is_descendent_of` closure, when used after a warp-sync, will end up querying the + // backend for a block (the one corresponding to the root) that is not present and thus will + // return a wrong result. + pub fn find_node_index_where( + &self, + hash: &H, + number: &N, + is_descendent_of: &F, + predicate: &P, + ) -> Result>, Error> + where + E: std::error::Error, + F: Fn(&H, &H) -> Result, + P: Fn(&V) -> bool, + { + let mut stack = vec![]; + let mut root_idx = 0; + let mut found = false; + let mut is_descendent = false; + + while root_idx < self.roots.len() { + if *number <= self.roots[root_idx].number { + root_idx += 1; + continue + } + // The second element in the stack tuple tracks what is the **next** children + // index to search into. If we find an ancestor then we stop searching into + // alternative branches and we focus on the current path up to the root. + stack.push((&self.roots[root_idx], 0)); + while let Some((node, i)) = stack.pop() { + if i < node.children.len() && !is_descendent { + stack.push((node, i + 1)); + if node.children[i].number < *number { + stack.push((&node.children[i], 0)); + } + } else if is_descendent || is_descendent_of(&node.hash, hash)? { + is_descendent = true; + if predicate(&node.data) { + found = true; + break + } + } + } + + // If the element we are looking for is a descendent of the current root + // then we can stop the search. + if is_descendent { + break + } + root_idx += 1; + } + + Ok(if found { + // The path is the root index followed by the indices of all the children + // we were processing when we found the element (remember the stack + // contains the index of the **next** children to process). + let path: Vec<_> = + std::iter::once(root_idx).chain(stack.iter().map(|(_, i)| *i - 1)).collect(); + Some(path) + } else { + None + }) + } + + /// Prune the tree, removing all non-canonical nodes. We find the node in the + /// tree that is the deepest ancestor of the given hash and that passes the + /// given predicate. If such a node exists, we re-root the tree to this + /// node. Otherwise the tree remains unchanged. The given function + /// `is_descendent_of` should return `true` if the second hash (target) is a + /// descendent of the first hash (base). + /// + /// Returns all pruned node data. + pub fn prune( + &mut self, + hash: &H, + number: &N, + is_descendent_of: &F, + predicate: &P, + ) -> Result, Error> + where + E: std::error::Error, + F: Fn(&H, &H) -> Result, + P: Fn(&V) -> bool, + { + let root_index = + match self.find_node_index_where(hash, number, is_descendent_of, predicate)? { + Some(idx) => idx, + None => return Ok(RemovedIterator { stack: Vec::new() }), + }; + + let mut old_roots = std::mem::take(&mut self.roots); + + let curr_children = root_index + .iter() + .take(root_index.len() - 1) + .fold(&mut old_roots, |curr, idx| &mut curr[*idx].children); + let mut root = curr_children.remove(root_index[root_index.len() - 1]); + + let mut removed = old_roots; + + // we found the deepest ancestor of the finalized block, so we prune + // out any children that don't include the finalized block. + let root_children = std::mem::take(&mut root.children); + let mut is_first = true; + + for child in root_children { + if is_first + && (child.number == *number && child.hash == *hash + || child.number < *number && is_descendent_of(&child.hash, hash)?) + { + root.children.push(child); + // assuming that the tree is well formed only one child should pass this + // requirement due to ancestry restrictions (i.e. they must be different forks). + is_first = false; + } else { + removed.push(child); + } + } + + self.roots = vec![root]; + self.rebalance(); + + Ok(RemovedIterator { stack: removed }) + } + + /// Finalize a root in the tree and return it, return `None` in case no root + /// with the given hash exists. All other roots are pruned, and the children + /// of the finalized node become the new roots. + pub fn finalize_root(&mut self, hash: &H) -> Option { + self.roots + .iter() + .position(|node| node.hash == *hash) + .map(|position| self.finalize_root_at(position)) + } + + /// Finalize root at given position. See `finalize_root` comment for details. + fn finalize_root_at(&mut self, position: usize) -> V { + let node = self.roots.swap_remove(position); + self.roots = node.children; + self.best_finalized_number = Some(node.number); + node.data + } + + /// Finalize a node in the tree. This method will make sure that the node + /// being finalized is either an existing root (and return its data), or a + /// node from a competing branch (not in the tree), tree pruning is done + /// accordingly. The given function `is_descendent_of` should return `true` + /// if the second hash (target) is a descendent of the first hash (base). + pub fn finalize( + &mut self, + hash: &H, + number: N, + is_descendent_of: &F, + ) -> Result, Error> + where + E: std::error::Error, + F: Fn(&H, &H) -> Result, + { + if let Some(ref best_finalized_number) = self.best_finalized_number { + if number <= *best_finalized_number { + return Err(Error::Revert) + } + } + + // check if one of the current roots is being finalized + if let Some(root) = self.finalize_root(hash) { + return Ok(FinalizationResult::Changed(Some(root))) + } + + // make sure we're not finalizing a descendent of any root + for root in self.roots.iter() { + if number > root.number && is_descendent_of(&root.hash, hash)? { + return Err(Error::UnfinalizedAncestor) + } + } + + // we finalized a block earlier than any existing root (or possibly + // another fork not part of the tree). make sure to only keep roots that + // are part of the finalized branch + let mut changed = false; + let roots = std::mem::take(&mut self.roots); + + for root in roots { + if root.number > number && is_descendent_of(hash, &root.hash)? { + self.roots.push(root); + } else { + changed = true; + } + } + + self.best_finalized_number = Some(number); + + if changed { + Ok(FinalizationResult::Changed(None)) + } else { + Ok(FinalizationResult::Unchanged) + } + } + + /// Finalize a node in the tree and all its ancestors. The given function + /// `is_descendent_of` should return `true` if the second hash (target) is + // a descendent of the first hash (base). + pub fn finalize_with_ancestors( + &mut self, + hash: &H, + number: N, + is_descendent_of: &F, + ) -> Result, Error> + where + E: std::error::Error, + F: Fn(&H, &H) -> Result, + { + if let Some(ref best_finalized_number) = self.best_finalized_number { + if number <= *best_finalized_number { + return Err(Error::Revert) + } + } + + // check if one of the current roots is being finalized + if let Some(root) = self.finalize_root(hash) { + return Ok(FinalizationResult::Changed(Some(root))) + } + + // we need to: + // 1) remove all roots that are not ancestors AND not descendants of finalized block; + // 2) if node is descendant - just leave it; + // 3) if node is ancestor - 'open it' + let mut changed = false; + let mut idx = 0; + while idx != self.roots.len() { + let (is_finalized, is_descendant, is_ancestor) = { + let root = &self.roots[idx]; + let is_finalized = root.hash == *hash; + let is_descendant = + !is_finalized && root.number > number && is_descendent_of(hash, &root.hash)?; + let is_ancestor = !is_finalized + && !is_descendant && root.number < number + && is_descendent_of(&root.hash, hash)?; + (is_finalized, is_descendant, is_ancestor) + }; + + // if we have met finalized root - open it and return + if is_finalized { + return Ok(FinalizationResult::Changed(Some(self.finalize_root_at(idx)))) + } + + // if node is descendant of finalized block - just leave it as is + if is_descendant { + idx += 1; + continue + } + + // if node is ancestor of finalized block - remove it and continue with children + if is_ancestor { + let root = self.roots.swap_remove(idx); + self.roots.extend(root.children); + changed = true; + continue + } + + // if node is neither ancestor, nor descendant of the finalized block - remove it + self.roots.swap_remove(idx); + changed = true; + } + + self.best_finalized_number = Some(number); + + if changed { + Ok(FinalizationResult::Changed(None)) + } else { + Ok(FinalizationResult::Unchanged) + } + } + + /// Checks if any node in the tree is finalized by either finalizing the + /// node itself or a node's descendent that's not in the tree, guaranteeing + /// that the node being finalized isn't a descendent of (or equal to) any of + /// the node's children. Returns `Some(true)` if the node being finalized is + /// a root, `Some(false)` if the node being finalized is not a root, and + /// `None` if no node in the tree is finalized. The given `predicate` is + /// checked on the prospective finalized root and must pass for finalization + /// to occur. The given function `is_descendent_of` should return `true` if + /// the second hash (target) is a descendent of the first hash (base). + pub fn finalizes_any_with_descendent_if( + &self, + hash: &H, + number: N, + is_descendent_of: &F, + predicate: P, + ) -> Result, Error> + where + E: std::error::Error, + F: Fn(&H, &H) -> Result, + P: Fn(&V) -> bool, + { + if let Some(ref best_finalized_number) = self.best_finalized_number { + if number <= *best_finalized_number { + return Err(Error::Revert) + } + } + + // check if the given hash is equal or a descendent of any node in the + // tree, if we find a valid node that passes the predicate then we must + // ensure that we're not finalizing past any of its child nodes. + for node in self.node_iter() { + if predicate(&node.data) && (node.hash == *hash || is_descendent_of(&node.hash, hash)?) + { + for child in node.children.iter() { + if child.number <= number + && (child.hash == *hash || is_descendent_of(&child.hash, hash)?) + { + return Err(Error::UnfinalizedAncestor) + } + } + + return Ok(Some(self.roots.iter().any(|root| root.hash == node.hash))) + } + } + + Ok(None) + } + + /// Finalize a root in the tree by either finalizing the node itself or a + /// node's descendent that's not in the tree, guaranteeing that the node + /// being finalized isn't a descendent of (or equal to) any of the root's + /// children. The given `predicate` is checked on the prospective finalized + /// root and must pass for finalization to occur. The given function + /// `is_descendent_of` should return `true` if the second hash (target) is a + /// descendent of the first hash (base). + pub fn finalize_with_descendent_if( + &mut self, + hash: &H, + number: N, + is_descendent_of: &F, + predicate: P, + ) -> Result, Error> + where + E: std::error::Error, + F: Fn(&H, &H) -> Result, + P: Fn(&V) -> bool, + { + if let Some(ref best_finalized_number) = self.best_finalized_number { + if number <= *best_finalized_number { + return Err(Error::Revert) + } + } + + // check if the given hash is equal or a a descendent of any root, if we + // find a valid root that passes the predicate then we must ensure that + // we're not finalizing past any children node. + let mut position = None; + for (i, root) in self.roots.iter().enumerate() { + if predicate(&root.data) && (root.hash == *hash || is_descendent_of(&root.hash, hash)?) + { + for child in root.children.iter() { + if child.number <= number + && (child.hash == *hash || is_descendent_of(&child.hash, hash)?) + { + return Err(Error::UnfinalizedAncestor) + } + } + + position = Some(i); + break + } + } + + let node_data = position.map(|i| { + let node = self.roots.swap_remove(i); + self.roots = node.children; + self.best_finalized_number = Some(node.number); + node.data + }); + + // Retain only roots that are descendents of the finalized block (this + // happens if the node has been properly finalized) or that are + // ancestors (or equal) to the finalized block (in this case the node + // wasn't finalized earlier presumably because the predicate didn't + // pass). + let mut changed = false; + let roots = std::mem::take(&mut self.roots); + + for root in roots { + let retain = root.number > number && is_descendent_of(hash, &root.hash)? + || root.number == number && root.hash == *hash + || is_descendent_of(&root.hash, hash)?; + + if retain { + self.roots.push(root); + } else { + changed = true; + } + } + + self.best_finalized_number = Some(number); + + match (node_data, changed) { + (Some(data), _) => Ok(FinalizationResult::Changed(Some(data))), + (None, true) => Ok(FinalizationResult::Changed(None)), + (None, false) => Ok(FinalizationResult::Unchanged), + } + } + + /// Remove from the tree some nodes (and their subtrees) using a `filter` predicate. + /// + /// The `filter` is called over tree nodes and returns a filter action: + /// - `Remove` if the node and its subtree should be removed; + /// - `KeepNode` if we should maintain the node and keep processing the tree. + /// - `KeepTree` if we should maintain the node and its entire subtree. + /// + /// An iterator over all the pruned nodes is returned. + pub fn drain_filter(&mut self, filter: F) -> impl Iterator + where + F: Fn(&H, &N, &V) -> FilterAction, + { + let mut removed = vec![]; + let mut retained = Vec::new(); + + let mut queue: Vec<_> = std::mem::take(&mut self.roots) + .into_iter() + .rev() + .map(|node| (usize::MAX, node)) + .collect(); + let mut next_queue = Vec::new(); + + while !queue.is_empty() { + for (parent_idx, mut node) in queue.drain(..) { + match filter(&node.hash, &node.number, &node.data) { + FilterAction::KeepNode => { + let node_idx = retained.len(); + let children = std::mem::take(&mut node.children); + retained.push((parent_idx, node)); + for child in children.into_iter().rev() { + next_queue.push((node_idx, child)); + } + }, + FilterAction::KeepTree => { + retained.push((parent_idx, node)); + }, + FilterAction::Remove => { + removed.push(node); + }, + } + } + + std::mem::swap(&mut queue, &mut next_queue); + } + + while let Some((parent_idx, node)) = retained.pop() { + if parent_idx == usize::MAX { + self.roots.push(node); + } else { + retained[parent_idx].1.children.push(node); + } + } + + if !removed.is_empty() { + self.rebalance(); + } + RemovedIterator { stack: removed } + } +} + +// Workaround for: https://github.com/rust-lang/rust/issues/34537 +use node_implementation::Node; + +mod node_implementation { + use super::*; + + #[derive(Clone, Debug, Decode, Encode, PartialEq)] + pub struct Node { + pub hash: H, + pub number: N, + pub data: V, + pub children: Vec>, + } + + impl Node { + /// Finds the max depth among all branches descendent from this node. + pub fn max_depth(&self) -> usize { + let mut max: usize = 0; + let mut stack = vec![(self, 0)]; + while let Some((node, height)) = stack.pop() { + if height > max { + max = height; + } + node.children.iter().for_each(|n| stack.push((n, height + 1))); + } + max + } + } +} + +struct ForkTreeIterator<'a, H, N, V> { + stack: Vec<&'a Node>, +} + +impl<'a, H, N, V> Iterator for ForkTreeIterator<'a, H, N, V> { + type Item = &'a Node; + + fn next(&mut self) -> Option { + self.stack.pop().map(|node| { + // child nodes are stored ordered by max branch height (decreasing), + // we want to keep this ordering while iterating but since we're + // using a stack for iterator state we need to reverse it. + self.stack.extend(node.children.iter().rev()); + node + }) + } +} + +struct RemovedIterator { + stack: Vec>, +} + +impl Iterator for RemovedIterator { + type Item = (H, N, V); + + fn next(&mut self) -> Option { + self.stack.pop().map(|mut node| { + // child nodes are stored ordered by max branch height (decreasing), + // we want to keep this ordering while iterating but since we're + // using a stack for iterator state we need to reverse it. + let children = std::mem::take(&mut node.children); + + self.stack.extend(children.into_iter().rev()); + (node.hash, node.number, node.data) + }) + } +} + +#[cfg(test)] +mod test { + use crate::FilterAction; + + use super::{Error, FinalizationResult, ForkTree}; + + #[derive(Debug, PartialEq)] + struct TestError; + + impl std::fmt::Display for TestError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "TestError") + } + } + + impl std::error::Error for TestError {} + + fn test_fork_tree<'a>( + ) -> (ForkTree<&'a str, u64, ()>, impl Fn(&&str, &&str) -> Result) { + let mut tree = ForkTree::new(); + + #[rustfmt::skip] + // + // - B - C - D - E + // / + // / - G + // / / + // A - F - H - I + // \ \ + // \ - L - M - N + // \ \ + // \ - O + // - J - K + // + // (where N is not a part of fork tree) + // + // NOTE: the tree will get automatically rebalance on import and won't be laid out like the + // diagram above. the children will be ordered by subtree depth and the longest branches + // will be on the leftmost side of the tree. + let is_descendent_of = |base: &&str, block: &&str| -> Result { + let letters = vec!["B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O"]; + match (*base, *block) { + ("A", b) => Ok(letters.into_iter().any(|n| n == b)), + ("B", b) => Ok(b == "C" || b == "D" || b == "E"), + ("C", b) => Ok(b == "D" || b == "E"), + ("D", b) => Ok(b == "E"), + ("E", _) => Ok(false), + ("F", b) => + Ok(b == "G" || b == "H" || b == "I" || b == "L" || b == "M" || b == "N" || b == "O"), + ("G", _) => Ok(false), + ("H", b) => Ok(b == "I" || b == "L" || b == "M" || b == "N" || b == "O"), + ("I", _) => Ok(false), + ("J", b) => Ok(b == "K"), + ("K", _) => Ok(false), + ("L", b) => Ok(b == "M" || b == "O" || b == "N"), + ("M", b) => Ok(b == "N"), + ("O", _) => Ok(false), + ("0", _) => Ok(true), + _ => Ok(false), + } + }; + + tree.import("A", 1, (), &is_descendent_of).unwrap(); + + tree.import("B", 2, (), &is_descendent_of).unwrap(); + tree.import("C", 3, (), &is_descendent_of).unwrap(); + tree.import("D", 4, (), &is_descendent_of).unwrap(); + tree.import("E", 5, (), &is_descendent_of).unwrap(); + + tree.import("F", 2, (), &is_descendent_of).unwrap(); + tree.import("G", 3, (), &is_descendent_of).unwrap(); + + tree.import("H", 3, (), &is_descendent_of).unwrap(); + tree.import("I", 4, (), &is_descendent_of).unwrap(); + tree.import("L", 4, (), &is_descendent_of).unwrap(); + tree.import("M", 5, (), &is_descendent_of).unwrap(); + tree.import("O", 5, (), &is_descendent_of).unwrap(); + + tree.import("J", 2, (), &is_descendent_of).unwrap(); + tree.import("K", 3, (), &is_descendent_of).unwrap(); + + (tree, is_descendent_of) + } + + #[test] + fn import_doesnt_revert() { + let (mut tree, is_descendent_of) = test_fork_tree(); + + tree.finalize_root(&"A"); + + assert_eq!(tree.best_finalized_number, Some(1)); + + assert_eq!(tree.import("A", 1, (), &is_descendent_of), Err(Error::Revert)); + } + + #[test] + fn import_doesnt_add_duplicates() { + let (mut tree, is_descendent_of) = test_fork_tree(); + + assert_eq!(tree.import("A", 1, (), &is_descendent_of), Err(Error::Duplicate)); + + assert_eq!(tree.import("I", 4, (), &is_descendent_of), Err(Error::Duplicate)); + + assert_eq!(tree.import("G", 3, (), &is_descendent_of), Err(Error::Duplicate)); + + assert_eq!(tree.import("K", 3, (), &is_descendent_of), Err(Error::Duplicate)); + } + + #[test] + fn finalize_root_works() { + let finalize_a = || { + let (mut tree, ..) = test_fork_tree(); + + assert_eq!(tree.roots().map(|(h, n, _)| (*h, *n)).collect::>(), vec![("A", 1)]); + + // finalizing "A" opens up three possible forks + tree.finalize_root(&"A"); + + assert_eq!( + tree.roots().map(|(h, n, _)| (*h, *n)).collect::>(), + vec![("B", 2), ("F", 2), ("J", 2)], + ); + + tree + }; + + { + let mut tree = finalize_a(); + + // finalizing "B" will progress on its fork and remove any other competing forks + tree.finalize_root(&"B"); + + assert_eq!(tree.roots().map(|(h, n, _)| (*h, *n)).collect::>(), vec![("C", 3)],); + + // all the other forks have been pruned + assert!(tree.roots.len() == 1); + } + + { + let mut tree = finalize_a(); + + // finalizing "J" will progress on its fork and remove any other competing forks + tree.finalize_root(&"J"); + + assert_eq!(tree.roots().map(|(h, n, _)| (*h, *n)).collect::>(), vec![("K", 3)],); + + // all the other forks have been pruned + assert!(tree.roots.len() == 1); + } + } + + #[test] + fn finalize_works() { + let (mut tree, is_descendent_of) = test_fork_tree(); + + let original_roots = tree.roots.clone(); + + // finalizing a block prior to any in the node doesn't change the tree + assert_eq!(tree.finalize(&"0", 0, &is_descendent_of), Ok(FinalizationResult::Unchanged)); + + assert_eq!(tree.roots, original_roots); + + // finalizing "A" opens up three possible forks + assert_eq!( + tree.finalize(&"A", 1, &is_descendent_of), + Ok(FinalizationResult::Changed(Some(()))), + ); + + assert_eq!( + tree.roots().map(|(h, n, _)| (*h, *n)).collect::>(), + vec![("B", 2), ("F", 2), ("J", 2)], + ); + + // finalizing anything lower than what we observed will fail + assert_eq!(tree.best_finalized_number, Some(1)); + + assert_eq!(tree.finalize(&"Z", 1, &is_descendent_of), Err(Error::Revert)); + + // trying to finalize a node without finalizing its ancestors first will fail + assert_eq!(tree.finalize(&"H", 3, &is_descendent_of), Err(Error::UnfinalizedAncestor)); + + // after finalizing "F" we can finalize "H" + assert_eq!( + tree.finalize(&"F", 2, &is_descendent_of), + Ok(FinalizationResult::Changed(Some(()))), + ); + + assert_eq!( + tree.finalize(&"H", 3, &is_descendent_of), + Ok(FinalizationResult::Changed(Some(()))), + ); + + assert_eq!( + tree.roots().map(|(h, n, _)| (*h, *n)).collect::>(), + vec![("L", 4), ("I", 4)], + ); + + // finalizing a node from another fork that isn't part of the tree clears the tree + assert_eq!( + tree.finalize(&"Z", 5, &is_descendent_of), + Ok(FinalizationResult::Changed(None)), + ); + + assert!(tree.roots.is_empty()); + } + + #[test] + fn finalize_with_ancestor_works() { + let (mut tree, is_descendent_of) = test_fork_tree(); + + let original_roots = tree.roots.clone(); + + // finalizing a block prior to any in the node doesn't change the tree + assert_eq!( + tree.finalize_with_ancestors(&"0", 0, &is_descendent_of), + Ok(FinalizationResult::Unchanged), + ); + + assert_eq!(tree.roots, original_roots); + + // finalizing "A" opens up three possible forks + assert_eq!( + tree.finalize_with_ancestors(&"A", 1, &is_descendent_of), + Ok(FinalizationResult::Changed(Some(()))), + ); + + assert_eq!( + tree.roots().map(|(h, n, _)| (*h, *n)).collect::>(), + vec![("B", 2), ("F", 2), ("J", 2)], + ); + + // finalizing H: + // 1) removes roots that are not ancestors/descendants of H (B, J) + // 2) opens root that is ancestor of H (F -> G+H) + // 3) finalizes the just opened root H (H -> I + L) + assert_eq!( + tree.finalize_with_ancestors(&"H", 3, &is_descendent_of), + Ok(FinalizationResult::Changed(Some(()))), + ); + + assert_eq!( + tree.roots().map(|(h, n, _)| (*h, *n)).collect::>(), + vec![("L", 4), ("I", 4)], + ); + + assert_eq!(tree.best_finalized_number, Some(3)); + + // finalizing N (which is not a part of the tree): + // 1) removes roots that are not ancestors/descendants of N (I) + // 2) opens root that is ancestor of N (L -> M+O) + // 3) removes roots that are not ancestors/descendants of N (O) + // 4) opens root that is ancestor of N (M -> {}) + assert_eq!( + tree.finalize_with_ancestors(&"N", 6, &is_descendent_of), + Ok(FinalizationResult::Changed(None)), + ); + + assert_eq!(tree.roots().map(|(h, n, _)| (*h, *n)).collect::>(), vec![],); + + assert_eq!(tree.best_finalized_number, Some(6)); + } + + #[test] + fn finalize_with_descendent_works() { + #[derive(Debug, PartialEq)] + struct Change { + effective: u64, + } + + let (mut tree, is_descendent_of) = { + let mut tree = ForkTree::new(); + + let is_descendent_of = |base: &&str, block: &&str| -> Result { + // A0 #1 - (B #2) - (C #5) - D #10 - E #15 - (F #100) + // \ + // - (G #100) + // + // A1 #1 + // + // Nodes B, C, F and G are not part of the tree. + match (*base, *block) { + ("A0", b) => Ok(b == "B" || b == "C" || b == "D" || b == "E" || b == "G"), + ("A1", _) => Ok(false), + ("C", b) => Ok(b == "D"), + ("D", b) => Ok(b == "E" || b == "F" || b == "G"), + ("E", b) => Ok(b == "F"), + _ => Ok(false), + } + }; + + let is_root = tree.import("A0", 1, Change { effective: 5 }, &is_descendent_of).unwrap(); + assert!(is_root); + let is_root = tree.import("A1", 1, Change { effective: 5 }, &is_descendent_of).unwrap(); + assert!(is_root); + let is_root = + tree.import("D", 10, Change { effective: 10 }, &is_descendent_of).unwrap(); + assert!(!is_root); + let is_root = + tree.import("E", 15, Change { effective: 50 }, &is_descendent_of).unwrap(); + assert!(!is_root); + + (tree, is_descendent_of) + }; + + assert_eq!( + tree.finalizes_any_with_descendent_if( + &"B", + 2, + &is_descendent_of, + |c| c.effective <= 2, + ), + Ok(None), + ); + + // finalizing "D" is not allowed since it is not a root. + assert_eq!( + tree.finalize_with_descendent_if(&"D", 10, &is_descendent_of, |c| c.effective <= 10), + Err(Error::UnfinalizedAncestor) + ); + + // finalizing "D" will finalize a block from the tree, but it can't be applied yet + // since it is not a root change. + assert_eq!( + tree.finalizes_any_with_descendent_if(&"D", 10, &is_descendent_of, |c| c.effective + == 10), + Ok(Some(false)), + ); + + // finalizing "E" is not allowed since there are not finalized anchestors. + assert_eq!( + tree.finalizes_any_with_descendent_if(&"E", 15, &is_descendent_of, |c| c.effective + == 10), + Err(Error::UnfinalizedAncestor) + ); + + // finalizing "B" doesn't finalize "A0" since the predicate doesn't pass, + // although it will clear out "A1" from the tree + assert_eq!( + tree.finalize_with_descendent_if(&"B", 2, &is_descendent_of, |c| c.effective <= 2), + Ok(FinalizationResult::Changed(None)), + ); + + assert_eq!(tree.roots().map(|(h, n, _)| (*h, *n)).collect::>(), vec![("A0", 1)],); + + // finalizing "C" will finalize the node "A0" and prune it out of the tree + assert_eq!( + tree.finalizes_any_with_descendent_if( + &"C", + 5, + &is_descendent_of, + |c| c.effective <= 5, + ), + Ok(Some(true)), + ); + + assert_eq!( + tree.finalize_with_descendent_if(&"C", 5, &is_descendent_of, |c| c.effective <= 5), + Ok(FinalizationResult::Changed(Some(Change { effective: 5 }))), + ); + + assert_eq!(tree.roots().map(|(h, n, _)| (*h, *n)).collect::>(), vec![("D", 10)],); + + // finalizing "F" will fail since it would finalize past "E" without finalizing "D" first + assert_eq!( + tree.finalizes_any_with_descendent_if(&"F", 100, &is_descendent_of, |c| c.effective + <= 100,), + Err(Error::UnfinalizedAncestor), + ); + + // it will work with "G" though since it is not in the same branch as "E" + assert_eq!( + tree.finalizes_any_with_descendent_if(&"G", 100, &is_descendent_of, |c| c.effective + <= 100), + Ok(Some(true)), + ); + + assert_eq!( + tree.finalize_with_descendent_if(&"G", 100, &is_descendent_of, |c| c.effective <= 100), + Ok(FinalizationResult::Changed(Some(Change { effective: 10 }))), + ); + + // "E" will be pruned out + assert_eq!(tree.roots().count(), 0); + } + + #[test] + fn iter_iterates_in_preorder() { + let (tree, ..) = test_fork_tree(); + assert_eq!( + tree.iter().map(|(h, n, _)| (*h, *n)).collect::>(), + vec![ + ("A", 1), + ("B", 2), + ("C", 3), + ("D", 4), + ("E", 5), + ("F", 2), + ("H", 3), + ("L", 4), + ("M", 5), + ("O", 5), + ("I", 4), + ("G", 3), + ("J", 2), + ("K", 3), + ], + ); + } + + #[test] + fn minimizes_calls_to_is_descendent_of() { + use std::sync::atomic::{AtomicUsize, Ordering}; + + let n_is_descendent_of_calls = AtomicUsize::new(0); + + let is_descendent_of = |_: &&str, _: &&str| -> Result { + n_is_descendent_of_calls.fetch_add(1, Ordering::SeqCst); + Ok(true) + }; + + { + // Deep tree where we want to call `finalizes_any_with_descendent_if`. The + // search for the node should first check the predicate (which is cheaper) and + // only then call `is_descendent_of` + let mut tree = ForkTree::new(); + let letters = vec!["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K"]; + + for (i, letter) in letters.iter().enumerate() { + tree.import::<_, TestError>(*letter, i, i, &|_, _| Ok(true)).unwrap(); + } + + // "L" is a descendent of "K", but the predicate will only pass for "K", + // therefore only one call to `is_descendent_of` should be made + assert_eq!( + tree.finalizes_any_with_descendent_if(&"L", 11, &is_descendent_of, |i| *i == 10,), + Ok(Some(false)), + ); + + assert_eq!(n_is_descendent_of_calls.load(Ordering::SeqCst), 1); + } + + n_is_descendent_of_calls.store(0, Ordering::SeqCst); + + { + // Multiple roots in the tree where we want to call `finalize_with_descendent_if`. + // The search for the root node should first check the predicate (which is cheaper) + // and only then call `is_descendent_of` + let mut tree = ForkTree::new(); + let letters = vec!["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K"]; + + for (i, letter) in letters.iter().enumerate() { + tree.import::<_, TestError>(*letter, i, i, &|_, _| Ok(false)).unwrap(); + } + + // "L" is a descendent of "K", but the predicate will only pass for "K", + // therefore only one call to `is_descendent_of` should be made + assert_eq!( + tree.finalize_with_descendent_if(&"L", 11, &is_descendent_of, |i| *i == 10,), + Ok(FinalizationResult::Changed(Some(10))), + ); + + assert_eq!(n_is_descendent_of_calls.load(Ordering::SeqCst), 1); + } + } + + #[test] + fn map_works() { + let (mut tree, _) = test_fork_tree(); + + // Extend the single root fork-tree to also excercise the roots order during map. + let is_descendent_of = |_: &&str, _: &&str| -> Result { Ok(false) }; + let is_root = tree.import("A1", 1, (), &is_descendent_of).unwrap(); + assert!(is_root); + let is_root = tree.import("A2", 1, (), &is_descendent_of).unwrap(); + assert!(is_root); + + let old_tree = tree.clone(); + let new_tree = tree.map(&mut |hash, _, _| hash.to_owned()); + + // Check content and order + assert!(new_tree.iter().all(|(hash, _, data)| hash == data)); + assert_eq!( + old_tree.iter().map(|(hash, _, _)| *hash).collect::>(), + new_tree.iter().map(|(hash, _, _)| *hash).collect::>(), + ); + } + + #[test] + fn prune_works() { + let (mut tree, is_descendent_of) = test_fork_tree(); + + let removed = tree.prune(&"C", &3, &is_descendent_of, &|_| true).unwrap(); + + assert_eq!(tree.roots.iter().map(|node| node.hash).collect::>(), vec!["B"]); + + assert_eq!( + tree.iter().map(|(hash, _, _)| *hash).collect::>(), + vec!["B", "C", "D", "E"], + ); + + assert_eq!( + removed.map(|(hash, _, _)| hash).collect::>(), + vec!["A", "F", "H", "L", "M", "O", "I", "G", "J", "K"] + ); + + let removed = tree.prune(&"E", &5, &is_descendent_of, &|_| true).unwrap(); + + assert_eq!(tree.roots.iter().map(|node| node.hash).collect::>(), vec!["D"]); + + assert_eq!(tree.iter().map(|(hash, _, _)| *hash).collect::>(), vec!["D", "E"]); + + assert_eq!(removed.map(|(hash, _, _)| hash).collect::>(), vec!["B", "C"]); + } + + #[test] + fn find_node_backtracks_after_finding_highest_descending_node() { + let mut tree = ForkTree::new(); + + // A - B + // \ + // — C + // + let is_descendent_of = |base: &&str, block: &&str| -> Result { + match (*base, *block) { + ("A", b) => Ok(b == "B" || b == "C" || b == "D"), + ("B", b) | ("C", b) => Ok(b == "D"), + ("0", _) => Ok(true), + _ => Ok(false), + } + }; + + tree.import("A", 1, 1, &is_descendent_of).unwrap(); + tree.import("B", 2, 2, &is_descendent_of).unwrap(); + tree.import("C", 2, 4, &is_descendent_of).unwrap(); + + // when searching the tree we reach node `C`, but the + // predicate doesn't pass. we should backtrack to `B`, but not to `A`, + // since "B" fulfills the predicate. + let node = tree.find_node_where(&"D", &3, &is_descendent_of, &|data| *data < 3).unwrap(); + + assert_eq!(node.unwrap().hash, "B"); + } + + #[test] + fn rebalance_works() { + let (mut tree, _) = test_fork_tree(); + + // the tree is automatically rebalanced on import, therefore we should iterate in preorder + // exploring the longest forks first. check the ascii art above to understand the expected + // output below. + assert_eq!( + tree.iter().map(|(h, _, _)| *h).collect::>(), + vec!["A", "B", "C", "D", "E", "F", "H", "L", "M", "O", "I", "G", "J", "K"], + ); + + // let's add a block "P" which is a descendent of block "O" + let is_descendent_of = |base: &&str, block: &&str| -> Result { + match (*base, *block) { + (b, "P") => Ok(vec!["A", "F", "H", "L", "O"].into_iter().any(|n| n == b)), + _ => Ok(false), + } + }; + + tree.import("P", 6, (), &is_descendent_of).unwrap(); + + // this should re-order the tree, since the branch "A -> B -> C -> D -> E" is no longer tied + // with 5 blocks depth. additionally "O" should be visited before "M" now, since it has one + // descendent "P" which makes that branch 6 blocks long. + assert_eq!( + tree.iter().map(|(h, _, _)| *h).collect::>(), + ["A", "F", "H", "L", "O", "P", "M", "I", "G", "B", "C", "D", "E", "J", "K"] + ); + } + + #[test] + fn drain_filter_works() { + let (mut tree, _) = test_fork_tree(); + + let filter = |h: &&str, _: &u64, _: &()| match *h { + "A" | "B" | "F" | "G" => FilterAction::KeepNode, + "C" => FilterAction::KeepTree, + "H" | "J" => FilterAction::Remove, + _ => panic!("Unexpected filtering for node: {}", *h), + }; + + let removed = tree.drain_filter(filter); + + assert_eq!( + tree.iter().map(|(h, _, _)| *h).collect::>(), + ["A", "B", "C", "D", "E", "F", "G"] + ); + + assert_eq!( + removed.map(|(h, _, _)| h).collect::>(), + ["H", "L", "M", "O", "I", "J", "K"] + ); + } + + #[test] + fn find_node_index_works() { + let (tree, is_descendent_of) = test_fork_tree(); + + let path = tree + .find_node_index_where(&"D", &4, &is_descendent_of, &|_| true) + .unwrap() + .unwrap(); + assert_eq!(path, [0, 0, 0]); + + let path = tree + .find_node_index_where(&"O", &5, &is_descendent_of, &|_| true) + .unwrap() + .unwrap(); + assert_eq!(path, [0, 1, 0, 0]); + + let path = tree + .find_node_index_where(&"N", &6, &is_descendent_of, &|_| true) + .unwrap() + .unwrap(); + assert_eq!(path, [0, 1, 0, 0, 0]); + } + + #[test] + fn find_node_index_with_predicate_works() { + let is_descendent_of = |parent: &char, child: &char| match *parent { + 'A' => Ok(['B', 'C', 'D', 'E', 'F'].contains(child)), + 'B' => Ok(['C', 'D'].contains(child)), + 'C' => Ok(['D'].contains(child)), + 'E' => Ok(['F'].contains(child)), + 'D' | 'F' => Ok(false), + _ => Err(TestError), + }; + + // A(t) --- B(f) --- C(t) --- D(f) + // \-- E(t) --- F(f) + let mut tree: ForkTree = ForkTree::new(); + tree.import('A', 1, true, &is_descendent_of).unwrap(); + tree.import('B', 2, false, &is_descendent_of).unwrap(); + tree.import('C', 3, true, &is_descendent_of).unwrap(); + tree.import('D', 4, false, &is_descendent_of).unwrap(); + + tree.import('E', 2, true, &is_descendent_of).unwrap(); + tree.import('F', 3, false, &is_descendent_of).unwrap(); + + let path = tree + .find_node_index_where(&'D', &4, &is_descendent_of, &|&value| !value) + .unwrap() + .unwrap(); + assert_eq!(path, [0, 0]); + + let path = tree + .find_node_index_where(&'D', &4, &is_descendent_of, &|&value| value) + .unwrap() + .unwrap(); + assert_eq!(path, [0, 0, 0]); + + let path = tree + .find_node_index_where(&'F', &3, &is_descendent_of, &|&value| !value) + .unwrap(); + assert_eq!(path, None); + + let path = tree + .find_node_index_where(&'F', &3, &is_descendent_of, &|&value| value) + .unwrap() + .unwrap(); + assert_eq!(path, [0, 1]); + } + + #[test] + fn find_node_works() { + let (tree, is_descendent_of) = test_fork_tree(); + + let node = tree.find_node_where(&"B", &2, &is_descendent_of, &|_| true).unwrap().unwrap(); + assert_eq!((node.hash, node.number), ("A", 1)); + + let node = tree.find_node_where(&"D", &4, &is_descendent_of, &|_| true).unwrap().unwrap(); + assert_eq!((node.hash, node.number), ("C", 3)); + + let node = tree.find_node_where(&"O", &5, &is_descendent_of, &|_| true).unwrap().unwrap(); + assert_eq!((node.hash, node.number), ("L", 4)); + + let node = tree.find_node_where(&"N", &6, &is_descendent_of, &|_| true).unwrap().unwrap(); + assert_eq!((node.hash, node.number), ("M", 5)); + } + + #[test] + fn post_order_traversal_requirement() { + let (mut tree, is_descendent_of) = test_fork_tree(); + + // Test for the post-order DFS traversal requirement as specified by the + // `find_node_index_where` and `import` comments. + let is_descendent_of_for_post_order = |parent: &&str, child: &&str| match *parent { + "A" => Err(TestError), + "K" if *child == "Z" => Ok(true), + _ => is_descendent_of(parent, child), + }; + + // Post order traversal requirement for `find_node_index_where` + let path = tree + .find_node_index_where(&"N", &6, &is_descendent_of_for_post_order, &|_| true) + .unwrap() + .unwrap(); + assert_eq!(path, [0, 1, 0, 0, 0]); + + // Post order traversal requirement for `import` + let res = tree.import(&"Z", 100, (), &is_descendent_of_for_post_order); + assert_eq!(res, Ok(false)); + assert_eq!( + tree.iter().map(|node| *node.0).collect::>(), + vec!["A", "B", "C", "D", "E", "F", "H", "L", "M", "O", "I", "G", "J", "K", "Z"], + ); + } +} diff --git a/bitacross-worker/sidechain/peer-fetch/Cargo.toml b/bitacross-worker/sidechain/peer-fetch/Cargo.toml new file mode 100644 index 0000000000..63e2612d91 --- /dev/null +++ b/bitacross-worker/sidechain/peer-fetch/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "its-peer-fetch" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +# crates.io +async-trait = { version = "0.1.50" } +jsonrpsee = { version = "0.2.0", features = ["client", "ws-server", "macros"] } +log = { version = "0.4" } +serde = "1.0" +serde_json = "1.0" +thiserror = { version = "1.0" } + +# local +itc-rpc-client = { path = "../../core/rpc-client" } +itp-node-api = { path = "../../core-primitives/node-api" } +its-primitives = { path = "../primitives" } +its-rpc-handler = { path = "../rpc-handler" } +its-storage = { path = "../storage" } + +[dev-dependencies] +# crates.io +anyhow = "1.0.40" +tokio = { version = "1.6.1", features = ["full"] } +# local +itp-node-api = { path = "../../core-primitives/node-api", features = ["mocks"] } +itp-test = { path = "../../core-primitives/test" } +its-storage = { path = "../storage", features = ["mocks"] } +its-test = { path = "../test" } + +[features] +default = ["std"] +std = [] +mocks = [] diff --git a/bitacross-worker/sidechain/peer-fetch/src/block_fetch_client.rs b/bitacross-worker/sidechain/peer-fetch/src/block_fetch_client.rs new file mode 100644 index 0000000000..320d916d7a --- /dev/null +++ b/bitacross-worker/sidechain/peer-fetch/src/block_fetch_client.rs @@ -0,0 +1,141 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{error::Result, untrusted_peer_fetch::FetchUntrustedPeers, FetchBlocksFromPeer}; +use async_trait::async_trait; +use its_primitives::{ + traits::SignedBlock as SignedBlockTrait, + types::{BlockHash, ShardIdentifier}, +}; +use its_rpc_handler::constants::RPC_METHOD_NAME_FETCH_BLOCKS_FROM_PEER; +use jsonrpsee::{ + types::to_json_value, + ws_client::{traits::Client, WsClientBuilder}, +}; +use log::info; +use serde::de::DeserializeOwned; +use std::marker::PhantomData; + +/// Sidechain block fetcher implementation. +/// +/// Fetches block from a peer with an RPC request. +pub struct BlockFetcher { + peer_fetcher: PeerFetcher, + _phantom: PhantomData, +} + +impl BlockFetcher +where + SignedBlock: SignedBlockTrait + DeserializeOwned, + PeerFetcher: FetchUntrustedPeers + Send + Sync, +{ + pub fn new(peer_fetcher: PeerFetcher) -> Self { + BlockFetcher { peer_fetcher, _phantom: Default::default() } + } +} + +#[async_trait] +impl FetchBlocksFromPeer for BlockFetcher +where + SignedBlock: SignedBlockTrait + DeserializeOwned, + PeerFetcher: FetchUntrustedPeers + Send + Sync, +{ + type SignedBlockType = SignedBlock; + + async fn fetch_blocks_from_peer( + &self, + last_imported_block_hash: BlockHash, + maybe_until_block_hash: Option, + shard_identifier: ShardIdentifier, + ) -> Result> { + let sync_source_rpc_url = + self.peer_fetcher.get_untrusted_peer_url_of_shard(&shard_identifier)?; + + let rpc_parameters = vec![to_json_value(( + last_imported_block_hash, + maybe_until_block_hash, + shard_identifier, + ))?]; + + info!("Got untrusted url for peer block fetching: {}", sync_source_rpc_url); + + let client = WsClientBuilder::default().build(sync_source_rpc_url.as_str()).await?; + + info!("Sending fetch blocks from peer request"); + + client + .request::>( + RPC_METHOD_NAME_FETCH_BLOCKS_FROM_PEER, + rpc_parameters.into(), + ) + .await + .map_err(|e| e.into()) + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::{ + block_fetch_server::BlockFetchServerModuleBuilder, + mocks::untrusted_peer_fetch_mock::UntrustedPeerFetcherMock, + }; + use its_primitives::types::block::SignedBlock; + use its_storage::fetch_blocks_mock::FetchBlocksMock; + use its_test::sidechain_block_builder::{SidechainBlockBuilder, SidechainBlockBuilderTrait}; + use jsonrpsee::ws_server::WsServerBuilder; + use std::{net::SocketAddr, sync::Arc}; + + async fn run_server( + blocks: Vec, + web_socket_url: &str, + ) -> anyhow::Result { + let mut server = WsServerBuilder::default().build(web_socket_url).await?; + + let storage_block_fetcher = Arc::new(FetchBlocksMock::default().with_blocks(blocks)); + let module = BlockFetchServerModuleBuilder::new(storage_block_fetcher).build().unwrap(); + + server.register_module(module).unwrap(); + + let socket_addr = server.local_addr()?; + tokio::spawn(async move { server.start().await }); + Ok(socket_addr) + } + + #[tokio::test] + async fn fetch_blocks_without_bounds_from_peer_works() { + const W1_URL: &str = "127.0.0.1:2233"; + + let blocks_to_fetch = vec![ + SidechainBlockBuilder::random().build_signed(), + SidechainBlockBuilder::random().build_signed(), + ]; + run_server(blocks_to_fetch.clone(), W1_URL).await.unwrap(); + + let peer_fetch_mock = UntrustedPeerFetcherMock::new(format!("ws://{}", W1_URL)); + + let peer_fetcher_client = BlockFetcher::::new(peer_fetch_mock); + + let blocks_fetched = peer_fetcher_client + .fetch_blocks_from_peer(BlockHash::default(), None, ShardIdentifier::default()) + .await + .unwrap(); + + assert_eq!(blocks_to_fetch, blocks_fetched); + } +} diff --git a/bitacross-worker/sidechain/peer-fetch/src/block_fetch_server.rs b/bitacross-worker/sidechain/peer-fetch/src/block_fetch_server.rs new file mode 100644 index 0000000000..592153f6eb --- /dev/null +++ b/bitacross-worker/sidechain/peer-fetch/src/block_fetch_server.rs @@ -0,0 +1,103 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::error::Result; +use its_primitives::types::{BlockHash, BlockNumber, ShardIdentifier, SignedBlock}; +use its_rpc_handler::constants::{ + RPC_METHOD_NAME_BLOCK_HASH, RPC_METHOD_NAME_FETCH_BLOCKS_FROM_PEER, + RPC_METHOD_NAME_LATEST_BLOCK, +}; +use its_storage::interface::FetchBlocks; +use jsonrpsee::{types::error::CallError, RpcModule}; +use log::*; +use std::sync::Arc; + +/// RPC server module builder for fetching sidechain blocks from peers. +pub struct BlockFetchServerModuleBuilder { + sidechain_block_fetcher: Arc, +} + +impl BlockFetchServerModuleBuilder +where + // Have to use the concrete `SignedBlock` type, because the ShardIdentifier type + // does not have the Serialize/Deserialize trait bound. + FetchBlocksFromStorage: FetchBlocks + Send + Sync + 'static, +{ + pub fn new(sidechain_block_fetcher: Arc) -> Self { + BlockFetchServerModuleBuilder { sidechain_block_fetcher } + } + + pub fn build(self) -> Result>> { + let mut fetch_sidechain_blocks_module = RpcModule::new(self.sidechain_block_fetcher); + fetch_sidechain_blocks_module.register_method( + RPC_METHOD_NAME_FETCH_BLOCKS_FROM_PEER, + |params, sidechain_block_fetcher| { + debug!("{}: {:?}", RPC_METHOD_NAME_FETCH_BLOCKS_FROM_PEER, params); + + let (from_block_hash, maybe_until_block_hash, shard_identifier) = + params.one::<(BlockHash, Option, ShardIdentifier)>()?; + info!("Got request to fetch sidechain blocks from peer. Fetching sidechain blocks from storage \ + (last imported block hash: {:?}, until block hash: {:?}, shard: {}", + from_block_hash, maybe_until_block_hash, shard_identifier); + + match maybe_until_block_hash { + Some(until_block_hash) => sidechain_block_fetcher + .fetch_blocks_in_range( + &from_block_hash, + &until_block_hash, + &shard_identifier, + ) + .map_err(|e| { + error!("Failed to fetch sidechain blocks from storage: {:?}", e); + CallError::Failed(e.into()) + }), + None => sidechain_block_fetcher + .fetch_all_blocks_after(&from_block_hash, &shard_identifier) + .map_err(|e| { + error!("Failed to fetch sidechain blocks from storage: {:?}", e); + CallError::Failed(e.into()) + }), + } + }, + )?; + + fetch_sidechain_blocks_module.register_method( + RPC_METHOD_NAME_LATEST_BLOCK, + |params, sidechain_block_fetcher| { + debug!("{}: {:?}", RPC_METHOD_NAME_LATEST_BLOCK, params); + let shard = params.parse::()?; + match sidechain_block_fetcher.latest_block(&shard) { + None => Ok(None), + Some(e) => Ok(Some(e)), + } + }, + )?; + + fetch_sidechain_blocks_module.register_method( + RPC_METHOD_NAME_BLOCK_HASH, + |params, sidechain_block_fetcher| { + debug!("{}: {:?}", RPC_METHOD_NAME_BLOCK_HASH, params); + let (block_number, shard) = params.parse::<(BlockNumber, ShardIdentifier)>()?; + match sidechain_block_fetcher.block_hash(block_number, &shard) { + None => Ok(None), + Some(e) => Ok(Some(e)), + } + }, + )?; + Ok(fetch_sidechain_blocks_module) + } +} diff --git a/bitacross-worker/sidechain/peer-fetch/src/error.rs b/bitacross-worker/sidechain/peer-fetch/src/error.rs new file mode 100644 index 0000000000..569cd01a1d --- /dev/null +++ b/bitacross-worker/sidechain/peer-fetch/src/error.rs @@ -0,0 +1,44 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Sidechain peer fetch error. + +pub type Result = core::result::Result; + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("RPC client error: {0}")] + RpcClient(#[from] itc_rpc_client::error::Error), + #[error("Node API extensions error: {0:?}")] + NodeApiExtensions(itp_node_api::api_client::ApiClientError), + #[error("Node API factory error: {0}")] + NodeApiFactory(#[from] itp_node_api::node_api_factory::NodeApiFactoryError), + #[error("Serialization error: {0}")] + Serialization(#[from] serde_json::Error), + #[error("JSON RPC error: {0}")] + JsonRpc(#[from] jsonrpsee::types::Error), + #[error("Could not find any peers on-chain for shard: {0:?}")] + NoPeerFoundForShard(its_primitives::types::ShardIdentifier), + #[error(transparent)] + Other(#[from] Box), +} + +impl From for Error { + fn from(error: itp_node_api::api_client::ApiClientError) -> Self { + Error::NodeApiExtensions(error) + } +} diff --git a/bitacross-worker/sidechain/peer-fetch/src/lib.rs b/bitacross-worker/sidechain/peer-fetch/src/lib.rs new file mode 100644 index 0000000000..5af3326970 --- /dev/null +++ b/bitacross-worker/sidechain/peer-fetch/src/lib.rs @@ -0,0 +1,49 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod block_fetch_client; +pub mod block_fetch_server; +pub mod error; +pub mod untrusted_peer_fetch; + +#[cfg(feature = "mocks")] +pub mod mocks; + +use crate::error::Result; +use async_trait::async_trait; +use its_primitives::{ + traits::SignedBlock, + types::{BlockHash, ShardIdentifier}, +}; +use std::vec::Vec; + +/// Trait to fetch block from peer validateers. +/// +/// This is used by an outdated validateer to get the most recent state. +#[async_trait] +pub trait FetchBlocksFromPeer { + type SignedBlockType: SignedBlock; + + async fn fetch_blocks_from_peer( + &self, + last_imported_block_hash: BlockHash, + maybe_until_block_hash: Option, + shard_identifier: ShardIdentifier, + ) -> Result>; +} diff --git a/bitacross-worker/sidechain/peer-fetch/src/mocks/fetch_blocks_from_peer_mock.rs b/bitacross-worker/sidechain/peer-fetch/src/mocks/fetch_blocks_from_peer_mock.rs new file mode 100644 index 0000000000..09f9bb92fc --- /dev/null +++ b/bitacross-worker/sidechain/peer-fetch/src/mocks/fetch_blocks_from_peer_mock.rs @@ -0,0 +1,61 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{FetchBlocksFromPeer, Result}; +use async_trait::async_trait; +use its_primitives::{ + traits::SignedBlock as SignedBlockTrait, + types::{BlockHash, ShardIdentifier}, +}; +use std::collections::HashMap; + +pub struct FetchBlocksFromPeerMock { + signed_blocks_map: HashMap>, +} + +impl FetchBlocksFromPeerMock { + pub fn with_signed_blocks( + mut self, + blocks_map: HashMap>, + ) -> Self { + self.signed_blocks_map = blocks_map; + self + } +} + +impl Default for FetchBlocksFromPeerMock { + fn default() -> Self { + FetchBlocksFromPeerMock { signed_blocks_map: HashMap::new() } + } +} + +#[async_trait] +impl FetchBlocksFromPeer for FetchBlocksFromPeerMock +where + SignedBlock: SignedBlockTrait, +{ + type SignedBlockType = SignedBlock; + + async fn fetch_blocks_from_peer( + &self, + _last_imported_block_hash: BlockHash, + _maybe_until_block_hash: Option, + shard_identifier: ShardIdentifier, + ) -> Result> { + Ok(self.signed_blocks_map.get(&shard_identifier).cloned().unwrap_or_default()) + } +} diff --git a/bitacross-worker/sidechain/peer-fetch/src/mocks/mod.rs b/bitacross-worker/sidechain/peer-fetch/src/mocks/mod.rs new file mode 100644 index 0000000000..392f8e9b82 --- /dev/null +++ b/bitacross-worker/sidechain/peer-fetch/src/mocks/mod.rs @@ -0,0 +1,19 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pub mod fetch_blocks_from_peer_mock; +pub mod untrusted_peer_fetch_mock; diff --git a/bitacross-worker/sidechain/peer-fetch/src/mocks/untrusted_peer_fetch_mock.rs b/bitacross-worker/sidechain/peer-fetch/src/mocks/untrusted_peer_fetch_mock.rs new file mode 100644 index 0000000000..8b37b69e00 --- /dev/null +++ b/bitacross-worker/sidechain/peer-fetch/src/mocks/untrusted_peer_fetch_mock.rs @@ -0,0 +1,35 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{error::Result, untrusted_peer_fetch::FetchUntrustedPeers}; +use its_primitives::types::ShardIdentifier; + +pub struct UntrustedPeerFetcherMock { + url: String, +} + +impl UntrustedPeerFetcherMock { + pub fn new(url: String) -> Self { + UntrustedPeerFetcherMock { url } + } +} + +impl FetchUntrustedPeers for UntrustedPeerFetcherMock { + fn get_untrusted_peer_url_of_shard(&self, _shard: &ShardIdentifier) -> Result { + Ok(self.url.clone()) + } +} diff --git a/bitacross-worker/sidechain/peer-fetch/src/untrusted_peer_fetch.rs b/bitacross-worker/sidechain/peer-fetch/src/untrusted_peer_fetch.rs new file mode 100644 index 0000000000..7ff9434103 --- /dev/null +++ b/bitacross-worker/sidechain/peer-fetch/src/untrusted_peer_fetch.rs @@ -0,0 +1,59 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::error::{Error, Result}; +use itc_rpc_client::direct_client::{DirectApi, DirectClient as DirectWorkerApi}; +use itp_node_api::{api_client::PalletTeerexApi, node_api_factory::CreateNodeApi}; +use its_primitives::types::ShardIdentifier; +use std::sync::Arc; + +/// Trait to fetch untrusted peer servers. +pub trait FetchUntrustedPeers { + fn get_untrusted_peer_url_of_shard(&self, shard: &ShardIdentifier) -> Result; +} + +/// Fetches the untrusted peer servers +/// FIXME: Should probably be combined with the peer fetch in +/// service/src/worker.rs +pub struct UntrustedPeerFetcher { + node_api_factory: Arc, +} + +impl UntrustedPeerFetcher +where + NodeApiFactory: CreateNodeApi + Send + Sync, +{ + pub fn new(node_api: Arc) -> Self { + UntrustedPeerFetcher { node_api_factory: node_api } + } +} + +impl FetchUntrustedPeers for UntrustedPeerFetcher +where + NodeApiFactory: CreateNodeApi + Send + Sync, +{ + fn get_untrusted_peer_url_of_shard(&self, shard: &ShardIdentifier) -> Result { + let node_api = self.node_api_factory.create_api()?; + + let validateer = node_api + .worker_for_shard(shard, None)? + .ok_or_else(|| Error::NoPeerFoundForShard(*shard))?; + + let trusted_worker_client = DirectWorkerApi::new(validateer.url); + Ok(trusted_worker_client.get_untrusted_worker_url()?) + } +} diff --git a/bitacross-worker/sidechain/primitives/Cargo.toml b/bitacross-worker/sidechain/primitives/Cargo.toml new file mode 100644 index 0000000000..548f4d5a18 --- /dev/null +++ b/bitacross-worker/sidechain/primitives/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "its-primitives" +version = "0.1.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +homepage = 'https://litentry.com/' +repository = 'https://github.com/litentry/litentry-parachain' +license = "Apache-2.0" +edition = "2021" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "full"] } +itp-types = { path = "../../core-primitives/types", default-features = false } +scale-info = { version = "2.4.0", default-features = false, features = ["derive"] } +serde = { version = "1.0.13", default-features = false } + +# substrate dependencies +sp-core = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-std = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + + +[features] +default = ["std", "full_crypto"] +full_crypto = [ + "sp-core/full_crypto", +] +std = [ + "codec/std", + "scale-info/std", + "serde/std", + "itp-types/std", + # substrate + "sp-core/std", + "sp-runtime/std", + "sp-std/std", +] diff --git a/bitacross-worker/sidechain/primitives/src/lib.rs b/bitacross-worker/sidechain/primitives/src/lib.rs new file mode 100644 index 0000000000..708d9a7942 --- /dev/null +++ b/bitacross-worker/sidechain/primitives/src/lib.rs @@ -0,0 +1,21 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod traits; +pub mod types; diff --git a/bitacross-worker/sidechain/primitives/src/traits/mod.rs b/bitacross-worker/sidechain/primitives/src/traits/mod.rs new file mode 100644 index 0000000000..06e1e2d393 --- /dev/null +++ b/bitacross-worker/sidechain/primitives/src/traits/mod.rs @@ -0,0 +1,176 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Some basic abstractions used in sidechain +//! +//! Todo: This crate should be more generic and supply blanket implementations for +//! some generic structs. + +use codec::{Decode, Encode}; +use sp_core::{crypto::Public, H256}; +use sp_runtime::traits::{BlakeTwo256, Hash, Member}; +use sp_std::{fmt::Debug, prelude::*}; + +pub trait Header: Encode + Decode + Clone { + /// Identifier for the shards. + type ShardIdentifier: Encode + Decode + sp_std::hash::Hash + Copy + Member + Into; + + /// Get block number. + fn block_number(&self) -> u64; + /// get parent hash of block + fn parent_hash(&self) -> H256; + /// get shard id of block + fn shard_id(&self) -> Self::ShardIdentifier; + /// get hash of the block's payload + fn block_data_hash(&self) -> H256; + + /// get the `blake2_256` hash of the header. + fn hash(&self) -> H256 { + self.using_encoded(BlakeTwo256::hash) + } + + fn next_finalization_block_number(&self) -> u64; + + fn new( + block_number: u64, + parent_hash: H256, + shard: Self::ShardIdentifier, + block_data_hash: H256, + next_finalization_block_number: u64, + ) -> Self; +} + +pub trait BlockData: Encode + Decode + Send + Sync + Debug + Clone { + /// Public key type of the block author + type Public: Public; + + /// get timestamp of block + fn timestamp(&self) -> u64; + /// get layer one head of block + fn layer_one_head(&self) -> H256; + /// get author of block + fn block_author(&self) -> &Self::Public; + /// get reference of extrinsics of block + fn signed_top_hashes(&self) -> &[H256]; + /// get encrypted payload + fn encrypted_state_diff(&self) -> &Vec; + /// get the `blake2_256` hash of the block + fn hash(&self) -> H256 { + self.using_encoded(BlakeTwo256::hash) + } + + fn new( + author: Self::Public, + layer_one_head: H256, + signed_top_hashes: Vec, + encrypted_payload: Vec, + timestamp: u64, + ) -> Self; +} + +/// Abstraction around a sidechain block. +pub trait Block: Encode + Decode + Send + Sync + Debug + Clone { + /// Sidechain block header type. + type HeaderType: Header; + + /// Sidechain block data type. + type BlockDataType: BlockData; + + /// Public key type of the block author + type Public: Public; + + /// get the `blake2_256` hash of the block + fn hash(&self) -> H256 { + self.header().hash() + } + + /// Get header of the block. + fn header(&self) -> &Self::HeaderType; + + /// Get header of the block. + fn block_data(&self) -> &Self::BlockDataType; + + fn new(header: Self::HeaderType, block_data: Self::BlockDataType) -> Self; +} + +/// ShardIdentifier for a [`SignedBlock`] +pub type ShardIdentifierFor = +<<::Block as Block>::HeaderType as Header>::ShardIdentifier; + +/// A block and it's corresponding signature by the [`Block`] author. +pub trait SignedBlock: Encode + Decode + Send + Sync + Debug + Clone { + /// The block type of the [`SignedBlock`] + type Block: Block; + + /// Public key type of the signer and the block author + type Public: Public; + + /// Signature type of the [`SignedBlock`]'s signature + type Signature; + + /// create a new block instance + fn new(block: Self::Block, signer: Self::Signature) -> Self; + + /// get block reference + fn block(&self) -> &Self::Block; + + /// get signature reference + fn signature(&self) -> &Self::Signature; + + /// get `blake2_256` hash of block + fn hash(&self) -> H256 { + self.block().hash() + } + + /// Verify the signature of a [`Block`] + fn verify_signature(&self) -> bool; +} + +#[cfg(feature = "full_crypto")] +pub use crypto::*; + +#[cfg(feature = "full_crypto")] +mod crypto { + use super::*; + use sp_core::Pair; + + /// Provide signing logic blanket implementations for all block types satisfying the trait bounds. + pub trait SignBlock< + SidechainBlock: Block, + SignedSidechainBlock: SignedBlock, + > + { + fn sign_block(self, signer: &P) -> SignedSidechainBlock + where + ::Signature: From<

::Signature>; + } + + impl SignBlock + for SidechainBlock + where + SidechainBlock: Block, + SignedSidechainBlock: SignedBlock, + { + fn sign_block(self, signer: &P) -> SignedSidechainBlock + where + ::Signature: From<

::Signature>, + { + let signature = self.using_encoded(|b| signer.sign(b)).into(); + SignedSidechainBlock::new(self, signature) + } + } +} diff --git a/bitacross-worker/sidechain/primitives/src/types/block.rs b/bitacross-worker/sidechain/primitives/src/types/block.rs new file mode 100644 index 0000000000..8e7902d62d --- /dev/null +++ b/bitacross-worker/sidechain/primitives/src/types/block.rs @@ -0,0 +1,159 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + traits::{Block as BlockTrait, SignedBlock as SignedBlockTrait}, + types::{block_data::BlockData, header::SidechainHeader as Header}, +}; +use codec::{Decode, Encode}; +use sp_core::{ed25519, H256}; +use sp_runtime::{traits::Verify, MultiSignature}; + +pub type BlockHash = H256; +pub type BlockNumber = u64; +pub type ShardIdentifier = H256; +pub type Timestamp = u64; + +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; + +//FIXME: Should use blocknumber from sgxruntime +// Problem: sgxruntime only with sgx, no std enviornment +// but block.rs should be available in std? +//use sgx_runtime::BlockNumber; + +pub type Signature = MultiSignature; + +/// signed version of block to verify block origin +#[derive(PartialEq, Eq, Clone, Encode, Decode, Debug)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub struct SignedBlock { + /// Plain sidechain block without author signature. + pub block: Block, + /// Block author signature. + pub signature: Signature, +} + +/// Simplified block structure for relay chain submission as an extrinsic. +#[derive(PartialEq, Eq, Clone, Encode, Decode, Debug)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub struct Block { + /// Sidechain Header + pub header: Header, + + /// Sidechain Block data + pub block_data: BlockData, +} + +impl BlockTrait for Block { + type HeaderType = Header; + + type BlockDataType = BlockData; + + type Public = ed25519::Public; + + fn header(&self) -> &Self::HeaderType { + &self.header + } + + fn block_data(&self) -> &Self::BlockDataType { + &self.block_data + } + + fn new(header: Self::HeaderType, block_data: Self::BlockDataType) -> Self { + Self { header, block_data } + } +} + +impl SignedBlockTrait for SignedBlock { + type Block = Block; + + type Public = ed25519::Public; + + type Signature = Signature; + + fn new(block: Self::Block, signature: Self::Signature) -> Self { + Self { block, signature } + } + + /// get block reference + fn block(&self) -> &Self::Block { + &self.block + } + + /// get signature reference + fn signature(&self) -> &Signature { + &self.signature + } + + /// Verifies the signature of a Block + fn verify_signature(&self) -> bool { + self.block.using_encoded(|p| { + self.signature.verify(p, &self.block.block_data().block_author.into()) + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::traits::{Block as BlockT, BlockData, Header, SignBlock}; + use sp_core::Pair; + use std::time::{SystemTime, UNIX_EPOCH}; + + /// gets the timestamp of the block as seconds since unix epoch + fn timestamp_now() -> Timestamp { + SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis() as Timestamp + } + + fn test_block() -> Block { + let header = Header::new(0, H256::random(), H256::random(), Default::default(), 1); + let block_data = BlockData::new( + ed25519::Pair::from_string("//Alice", None).unwrap().public().into(), + H256::random(), + Default::default(), + Default::default(), + timestamp_now(), + ); + + Block::new(header, block_data) + } + + #[test] + fn signing_works() { + let block = test_block(); + let signer = ed25519::Pair::from_string("//Alice", None).unwrap(); + + let signature: Signature = + Signature::Ed25519(signer.sign(block.encode().as_slice().into())); + let signed_block: SignedBlock = block.clone().sign_block(&signer); + + assert_eq!(signed_block.block(), &block); + assert_eq!(signed_block.signature(), &signature); + assert!(signed_block.verify_signature()); + } + + #[test] + fn tampered_block_verify_signature_fails() { + let signer = ed25519::Pair::from_string("//Alice", None).unwrap(); + + let mut signed_block: SignedBlock = test_block().sign_block(&signer); + signed_block.block.header.block_number = 1; + + assert!(!signed_block.verify_signature()); + } +} diff --git a/bitacross-worker/sidechain/primitives/src/types/block_data.rs b/bitacross-worker/sidechain/primitives/src/types/block_data.rs new file mode 100644 index 0000000000..a48d4148e4 --- /dev/null +++ b/bitacross-worker/sidechain/primitives/src/types/block_data.rs @@ -0,0 +1,82 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::traits::BlockData as BlockDataTrait; +use codec::{Decode, Encode}; +use sp_core::{ed25519, H256}; +use sp_std::vec::Vec; + +pub type Timestamp = u64; + +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; + +#[derive(PartialEq, Eq, Clone, Encode, Decode, Debug)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub struct BlockData { + pub timestamp: u64, + /// Parentchain header this block is based on. + pub layer_one_head: H256, + /// Must be registered on layer one as an enclave for the respective shard. + pub block_author: ed25519::Public, + /// Hashes of signed trusted operations. + pub signed_top_hashes: Vec, + /// Encrypted state payload. + pub encrypted_state_diff: Vec, +} + +impl BlockDataTrait for BlockData { + type Public = ed25519::Public; + + /// Get timestamp of block. + fn timestamp(&self) -> Timestamp { + self.timestamp + } + /// Get layer one head of block. + fn layer_one_head(&self) -> H256 { + self.layer_one_head + } + /// Get author of block. + fn block_author(&self) -> &Self::Public { + &self.block_author + } + /// Get reference of extrinisics of block. + fn signed_top_hashes(&self) -> &[H256] { + &self.signed_top_hashes + } + /// Get encrypted payload. + fn encrypted_state_diff(&self) -> &Vec { + &self.encrypted_state_diff + } + /// Constructs block data. + fn new( + block_author: Self::Public, + layer_one_head: H256, + signed_top_hashes: Vec, + encrypted_state_diff: Vec, + timestamp: Timestamp, + ) -> BlockData { + // create block + BlockData { + timestamp, + layer_one_head, + signed_top_hashes, + block_author, + encrypted_state_diff, + } + } +} diff --git a/bitacross-worker/sidechain/primitives/src/types/header.rs b/bitacross-worker/sidechain/primitives/src/types/header.rs new file mode 100644 index 0000000000..962917f534 --- /dev/null +++ b/bitacross-worker/sidechain/primitives/src/types/header.rs @@ -0,0 +1,91 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//!Primitives for the sidechain +use crate::traits::Header as HeaderTrait; +use codec::{Decode, Encode}; +use scale_info::TypeInfo; +use sp_core::H256; +use sp_runtime::traits::{BlakeTwo256, Hash}; +use sp_std::prelude::*; + +#[cfg(feature = "std")] +use serde::{Deserialize, Serialize}; + +pub use itp_types::ShardIdentifier; + +#[derive(PartialEq, Eq, Clone, Encode, Decode, Debug, Copy, Default, TypeInfo)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub struct SidechainHeader { + /// The parent hash. + pub parent_hash: H256, + + /// The block number. + pub block_number: u64, + + /// The Shard id. + pub shard_id: ShardIdentifier, + + /// The payload hash. + pub block_data_hash: H256, + + /// The latest finalized block number + pub next_finalization_block_number: u64, +} + +impl SidechainHeader { + /// get the `blake2_256` hash of the header. + pub fn hash(&self) -> H256 { + self.using_encoded(BlakeTwo256::hash) + } +} + +impl HeaderTrait for SidechainHeader { + type ShardIdentifier = H256; + + fn block_number(&self) -> u64 { + self.block_number + } + fn parent_hash(&self) -> H256 { + self.parent_hash + } + fn shard_id(&self) -> Self::ShardIdentifier { + self.shard_id + } + fn block_data_hash(&self) -> H256 { + self.block_data_hash + } + fn next_finalization_block_number(&self) -> u64 { + self.next_finalization_block_number + } + + fn new( + block_number: u64, + parent_hash: H256, + shard: Self::ShardIdentifier, + block_data_hash: H256, + next_finalization_block_number: u64, + ) -> SidechainHeader { + SidechainHeader { + block_number, + parent_hash, + shard_id: shard, + block_data_hash, + next_finalization_block_number, + } + } +} diff --git a/bitacross-worker/sidechain/primitives/src/types/mod.rs b/bitacross-worker/sidechain/primitives/src/types/mod.rs new file mode 100644 index 0000000000..2056953387 --- /dev/null +++ b/bitacross-worker/sidechain/primitives/src/types/mod.rs @@ -0,0 +1,22 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pub mod block; +pub mod block_data; +pub mod header; + +pub use block::*; diff --git a/bitacross-worker/sidechain/rpc-handler/Cargo.toml b/bitacross-worker/sidechain/rpc-handler/Cargo.toml new file mode 100644 index 0000000000..58cf470bf7 --- /dev/null +++ b/bitacross-worker/sidechain/rpc-handler/Cargo.toml @@ -0,0 +1,57 @@ +[package] +name = "its-rpc-handler" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +# sgx dependencies +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } + +# local dependencies +itp-rpc = { path = "../../core-primitives/rpc", default-features = false } +itp-stf-primitives = { path = "../../core-primitives/stf-primitives", default-features = false } +itp-top-pool-author = { path = "../../core-primitives/top-pool-author", default-features = false } +itp-types = { path = "../../core-primitives/types", default-features = false } +itp-utils = { path = "../../core-primitives/utils", default-features = false } +its-primitives = { path = "../primitives", default-features = false } + +litentry-primitives = { path = "../../litentry/primitives", default-features = false } + +# sgx enabled external libraries +futures_sgx = { package = "futures", git = "https://github.com/mesalock-linux/futures-rs-sgx", optional = true } +jsonrpc-core_sgx = { package = "jsonrpc-core", git = "https://github.com/scs/jsonrpc", branch = "no_std_v18", default-features = false, optional = true } +rust-base58_sgx = { package = "rust-base58", rev = "sgx_1.1.3", git = "https://github.com/mesalock-linux/rust-base58-sgx", optional = true, default-features = false, features = ["mesalock_sgx"] } + +# std compatible external libraries (make sure these versions match with the sgx-enabled ones above) +futures = { version = "0.3.8", optional = true } +jsonrpc-core = { version = "18", optional = true } +rust-base58 = { package = "rust-base58", version = "0.0.4", optional = true } + +# no-std compatible libraries +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +log = { version = "0.4", default-features = false } +sp-core = { default-features = false, features = ["full_crypto"], git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +[features] +default = ["std"] +std = [ + "futures", + "itp-rpc/std", + "itp-stf-primitives/std", + "itp-top-pool-author/std", + "itp-types/std", + "its-primitives/std", + "litentry-primitives/std", + "jsonrpc-core", + "log/std", + "rust-base58", +] +sgx = [ + "futures_sgx", + "sgx_tstd", + "itp-rpc/sgx", + "itp-top-pool-author/sgx", + "jsonrpc-core_sgx", + "rust-base58_sgx", +] diff --git a/bitacross-worker/sidechain/rpc-handler/src/constants.rs b/bitacross-worker/sidechain/rpc-handler/src/constants.rs new file mode 100644 index 0000000000..bff9ea019b --- /dev/null +++ b/bitacross-worker/sidechain/rpc-handler/src/constants.rs @@ -0,0 +1,24 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Sidechain constants + +// RPC method names. +pub const RPC_METHOD_NAME_IMPORT_BLOCKS: &str = "sidechain_importBlock"; +pub const RPC_METHOD_NAME_FETCH_BLOCKS_FROM_PEER: &str = "sidechain_fetchBlocksFromPeer"; +pub const RPC_METHOD_NAME_LATEST_BLOCK: &str = "sidechain_latestBlock"; +pub const RPC_METHOD_NAME_BLOCK_HASH: &str = "sidechain_blockHash"; diff --git a/bitacross-worker/sidechain/rpc-handler/src/direct_top_pool_api.rs b/bitacross-worker/sidechain/rpc-handler/src/direct_top_pool_api.rs new file mode 100644 index 0000000000..5de9ed776a --- /dev/null +++ b/bitacross-worker/sidechain/rpc-handler/src/direct_top_pool_api.rs @@ -0,0 +1,320 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; +use core::fmt::Debug; + +#[cfg(feature = "std")] +use rust_base58::base58::FromBase58; + +#[cfg(feature = "sgx")] +use base58::FromBase58; + +use codec::{Decode, Encode}; +use itp_rpc::RpcReturnValue; +use itp_stf_primitives::types::AccountId; +use itp_top_pool_author::traits::AuthorApi; +use itp_types::{DirectRequestStatus, RsaRequest, ShardIdentifier, TrustedOperationStatus}; +use itp_utils::{FromHexPrefixed, ToHexPrefixed}; +use jsonrpc_core::{futures::executor, serde_json::json, Error as RpcError, IoHandler, Params}; +use litentry_primitives::AesRequest; +use log::*; +use std::{ + borrow::ToOwned, + format, + string::{String, ToString}, + sync::Arc, + vec, + vec::Vec, +}; + +type Hash = sp_core::H256; + +pub fn add_top_pool_direct_rpc_methods( + top_pool_author: Arc, + mut io_handler: IoHandler, +) -> IoHandler +where + R: AuthorApi + Send + Sync + 'static, + TCS: PartialEq + Encode + Decode + Debug + Send + Sync + 'static, + G: PartialEq + Encode + Decode + Debug + Send + Sync + 'static, +{ + let watch_author = top_pool_author.clone(); + io_handler.add_sync_method("author_submitAndWatchRsaRequest", move |params: Params| { + debug!("worker_api_direct rpc was called: author_submitAndWatchRsaRequest"); + let json_value = match author_submit_extrinsic_inner( + watch_author.clone(), + params, + Some("author_submitAndWatchBroadcastedRsaRequest".to_owned()), + ) { + Ok(hash_value) => RpcReturnValue { + do_watch: true, + value: vec![], + status: DirectRequestStatus::TrustedOperationStatus( + TrustedOperationStatus::Submitted, + hash_value, + ), + } + .to_hex(), + Err(error) => compute_hex_encoded_return_error(error.as_str()), + }; + Ok(json!(json_value)) + }); + + // author_submitAndWatchBroadcastedRsaRequest + let watch_author = top_pool_author.clone(); + io_handler.add_sync_method( + "author_submitAndWatchBroadcastedRsaRequest", + move |params: Params| { + let json_value = match author_submit_extrinsic_inner(watch_author.clone(), params, None) + { + Ok(hash_value) => { + RpcReturnValue { + do_watch: true, + value: vec![], + status: DirectRequestStatus::TrustedOperationStatus( + TrustedOperationStatus::Submitted, + hash_value, + ), + } + } + .to_hex(), + Err(error) => compute_hex_encoded_return_error(error.as_str()), + }; + Ok(json!(json_value)) + }, + ); + + // author_submitRsaRequest + let submit_author = top_pool_author.clone(); + io_handler.add_sync_method("author_submitRsaRequest", move |params: Params| { + debug!("worker_api_direct rpc was called: author_submitRsaRequest"); + let json_value = match author_submit_extrinsic_inner(submit_author.clone(), params, None) { + Ok(hash_value) => RpcReturnValue { + do_watch: false, + value: vec![], + status: DirectRequestStatus::TrustedOperationStatus( + TrustedOperationStatus::Submitted, + hash_value, + ), + } + .to_hex(), + Err(error) => compute_hex_encoded_return_error(error.as_str()), + }; + Ok(json!(json_value)) + }); + + // Litentry: a morphling of `author_submitAndWatchRsaRequest` + // a different name is used to highlight the request type + let watch_author = top_pool_author.clone(); + io_handler.add_sync_method("author_submitAndWatchAesRequest", move |params: Params| { + debug!("worker_api_direct rpc was called: author_submitAndWatchAesRequest"); + let json_value = match author_submit_aes_request_inner( + watch_author.clone(), + params, + Some("author_submitAndWatchBroadcastedAesRequest".to_owned()), + ) { + Ok(hash_value) => RpcReturnValue { + do_watch: true, + value: vec![], + status: DirectRequestStatus::TrustedOperationStatus( + TrustedOperationStatus::Submitted, + hash_value, + ), + } + .to_hex(), + Err(error) => compute_hex_encoded_return_error(error.as_str()), + }; + Ok(json!(json_value)) + }); + + let watch_author = top_pool_author.clone(); + io_handler.add_sync_method( + "author_submitAndWatchBroadcastedAesRequest", + move |params: Params| { + let json_value = + match author_submit_aes_request_inner(watch_author.clone(), params, None) { + Ok(hash_value) => RpcReturnValue { + do_watch: true, + value: vec![], + status: DirectRequestStatus::TrustedOperationStatus( + TrustedOperationStatus::Submitted, + hash_value, + ), + } + .to_hex(), + Err(error) => compute_hex_encoded_return_error(error.as_str()), + }; + Ok(json!(json_value)) + }, + ); + + // author_pendingExtrinsics + let pending_author = top_pool_author.clone(); + io_handler.add_sync_method("author_pendingExtrinsics", move |params: Params| { + debug!("worker_api_direct rpc was called: author_pendingExtrinsics"); + match params.parse::>() { + Ok(shards) => { + let mut retrieved_operations = vec![]; + for shard_base58 in shards.iter() { + let shard = match decode_shard_from_base58(shard_base58.as_str()) { + Ok(id) => id, + Err(msg) => { + let error_msg: String = + format!("Could not retrieve pending calls due to: {}", msg); + return Ok(json!(compute_hex_encoded_return_error(error_msg.as_str()))) + }, + }; + if let Ok(vec_of_operations) = pending_author.pending_tops(shard) { + retrieved_operations.push(vec_of_operations); + } + } + let json_value = RpcReturnValue { + do_watch: false, + value: retrieved_operations.encode(), + status: DirectRequestStatus::Ok, + }; + Ok(json!(json_value.to_hex())) + }, + Err(e) => { + let error_msg: String = format!("Could not retrieve pending calls due to: {}", e); + Ok(json!(compute_hex_encoded_return_error(error_msg.as_str()))) + }, + } + }); + + let pending_author = top_pool_author; + io_handler.add_sync_method("author_pendingTrustedCallsFor", move |params: Params| { + debug!("worker_api_direct rpc was called: author_pendingTrustedCallsFor"); + match params.parse::<(String, String)>() { + Ok((shard_base58, account_hex)) => { + let shard = match decode_shard_from_base58(shard_base58.as_str()) { + Ok(id) => id, + Err(msg) => { + let error_msg: String = + format!("Could not retrieve pending trusted calls due to: {}", msg); + return Ok(json!(compute_hex_encoded_return_error(error_msg.as_str()))) + }, + }; + let account = match AccountId::from_hex(account_hex.as_str()) { + Ok(acc) => acc, + Err(msg) => { + let error_msg: String = + format!("Could not retrieve pending trusted calls due to: {:?}", msg); + return Ok(json!(compute_hex_encoded_return_error(error_msg.as_str()))) + }, + }; + let trusted_calls = pending_author.get_pending_trusted_calls_for(shard, &account); + let json_value = RpcReturnValue { + do_watch: false, + value: trusted_calls.encode(), + status: DirectRequestStatus::Ok, + }; + Ok(json!(json_value.to_hex())) + }, + Err(e) => { + let error_msg: String = + format!("Could not retrieve pending trusted calls due to: {}", e); + Ok(json!(compute_hex_encoded_return_error(error_msg.as_str()))) + }, + } + }); + + io_handler +} + +// converts the rpc methods vector to a string and adds commas and brackets for readability +pub fn decode_shard_from_base58(shard_base58: &str) -> Result { + let shard_vec = match shard_base58.from_base58() { + Ok(vec) => vec, + Err(_) => return Err("Invalid base58 format of shard id".to_owned()), + }; + let shard = match ShardIdentifier::decode(&mut shard_vec.as_slice()) { + Ok(hash) => hash, + Err(_) => return Err("Shard ID is not of type H256".to_owned()), + }; + Ok(shard) +} + +fn compute_hex_encoded_return_error(error_msg: &str) -> String { + RpcReturnValue::from_error_message(error_msg).to_hex() +} + +// we expect our `params` to be "by-position array" +// see https://www.jsonrpc.org/specification#parameter_structures +fn get_request_payload(params: Params) -> Result { + let s_vec = params.parse::>().map_err(|e| format!("{}", e))?; + + let s = s_vec.get(0).ok_or_else(|| "Empty params".to_string())?; + debug!("Request payload: {}", s); + Ok(s.to_owned()) +} + +fn author_submit_extrinsic_inner( + author: Arc, + params: Params, + json_rpc_method: Option, +) -> Result +where + R: AuthorApi + Send + Sync + 'static, + TCS: PartialEq + Encode + Decode + Debug + Send + Sync + 'static, + G: PartialEq + Encode + Decode + Debug + Send + Sync + 'static, +{ + let payload = get_request_payload(params)?; + let request = RsaRequest::from_hex(&payload).map_err(|e| format!("{:?}", e))?; + + let response: Result = if let Some(method) = json_rpc_method { + executor::block_on(async { author.watch_and_broadcast_top(request, method).await }) + } else { + executor::block_on(async { author.watch_top(request).await }) + }; + + match &response { + Ok(h) => debug!("Trusted operation submitted successfully ({:?})", h), + Err(e) => warn!("Submitting trusted operation failed: {:?}", e), + } + + response.map_err(|e| format!("{:?}", e)) +} + +fn author_submit_aes_request_inner( + author: Arc, + params: Params, + json_rpc_method: Option, +) -> Result +where + R: AuthorApi + Send + Sync + 'static, + TCS: PartialEq + Encode + Decode + Debug + Send + Sync + 'static, + G: PartialEq + Encode + Decode + Debug + Send + Sync + 'static, +{ + let payload = get_request_payload(params)?; + let request = AesRequest::from_hex(&payload).map_err(|e| format!("{:?}", e))?; + + let response: Result = if let Some(method) = json_rpc_method { + executor::block_on(async { author.watch_and_broadcast_top(request, method).await }) + } else { + executor::block_on(async { author.watch_top(request).await }) + }; + + match &response { + Ok(h) => debug!("AesRequest submitted successfully ({:?})", h), + Err(e) => warn!("Submitting AesRequest failed: {:?}", e), + } + + response.map_err(|e| format!("{:?}", e)) +} diff --git a/bitacross-worker/sidechain/rpc-handler/src/import_block_api.rs b/bitacross-worker/sidechain/rpc-handler/src/import_block_api.rs new file mode 100644 index 0000000000..a34ff829ef --- /dev/null +++ b/bitacross-worker/sidechain/rpc-handler/src/import_block_api.rs @@ -0,0 +1,126 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexport_prelude::*; + +use crate::constants::RPC_METHOD_NAME_IMPORT_BLOCKS; +use itp_utils::FromHexPrefixed; +use its_primitives::types::SignedBlock; +use jsonrpc_core::{IoHandler, Params, Value}; +use log::*; +use std::{borrow::ToOwned, fmt::Debug, string::String, vec::Vec}; + +pub fn add_import_block_rpc_method( + import_fn: ImportFn, + mut io_handler: IoHandler, +) -> IoHandler +where + ImportFn: Fn(SignedBlock) -> Result<(), Error> + Sync + Send + 'static, + Error: Debug, +{ + let sidechain_import_import_name: &str = RPC_METHOD_NAME_IMPORT_BLOCKS; + io_handler.add_sync_method(sidechain_import_import_name, move |sidechain_blocks: Params| { + debug!("{} rpc. Params: {:?}", RPC_METHOD_NAME_IMPORT_BLOCKS, sidechain_blocks); + + let hex_encoded_block_vec: Vec = sidechain_blocks.parse()?; + + let blocks = Vec::::from_hex(&hex_encoded_block_vec[0]).map_err(|_| { + jsonrpc_core::error::Error::invalid_params_with_details( + "Could not decode Vec", + hex_encoded_block_vec, + ) + })?; + + debug!("{}. Blocks: {:?}", RPC_METHOD_NAME_IMPORT_BLOCKS, blocks); + + for block in blocks { + info!("Add block {} to import queue", block.block.header.block_number); + let _ = import_fn(block).map_err(|e| { + let error = jsonrpc_core::error::Error::invalid_params_with_details( + "Failed to import Block.", + e, + ); + error!("{:?}", error); + }); + } + + Ok(Value::String("ok".to_owned())) + }); + + io_handler +} + +#[cfg(test)] +pub mod tests { + + use super::*; + + fn rpc_response(result: T) -> String { + format!(r#"{{"jsonrpc":"2.0","result":{},"id":1}}"#, result.to_string()) + } + + fn io_handler() -> IoHandler { + let io_handler = IoHandler::new(); + add_import_block_rpc_method::<_, String>(|_| Ok(()), io_handler) + } + + #[test] + pub fn sidechain_import_block_is_ok() { + let io = io_handler(); + let enclave_req = r#"{"jsonrpc":"2.0","method":"sidechain_importBlock","params":["0x04a7417cf9370af5ea5cf64f107aa49ebf320dbf10c6d0ef200ef7c5d57c9f4b956d000000000000007dba6b8e1f8f38f7f517dbd4a3eaeb27a97958d7a1d1541f69db5d24b3c48cd0dc376b08fcb44dca19a08a0445023a5f4bef80019b518296313e83fc105c669064000000000000005f08a5f98301000081bd02d7e1f8b6ab9a64fa8fdaa379fc1c9208bf0d341689c2342ce8a314e174768f40dfe0fadf2e7347f2ec83a541427a0931ce54ce7a4506184198c2e7aed3006d031b2cc662bbcd54ca1cc09f0021d956673c4905b07edf0b9f323d2078fc4d8cbaefe34353bc731f9a1ef14dfd6b58274a6efbbc6c2c4261d304b979305f501819df33452f2f276add2f3650b825c700abf23790a6787baf1cabb208633eb33fb66e987a99193fbd2c07374502dc0fdff6d7a5d462b2a9c0196711437aa6a30ce52ae6e4818a643df256c026b08d7ccca2de46f368630512073b271397719f34c9b8612c7f1707d06b45206da268f49b5b5159b3418093512700ecb67ccbc5bd9a1731a9c67372b39ec3761d12afb445a6c8580b97a090f4bb06ff70001bc44f7f91ada7f92f0064188d08c16594ddb4fd09f65bee5f4b3c92b80091d3fe5bc89f3fb95a96941563126a6379b806981dd7f225c7e3ac4e1ee0509de406"],"id":1}"#; + + let response_string = io.handle_request_sync(enclave_req).unwrap(); + + assert_eq!(response_string, rpc_response("\"ok\"")); + } + + #[test] + pub fn sidechain_import_block_returns_invalid_param_err() { + let io = io_handler(); + let enclave_req = + r#"{"jsonrpc":"2.0","method":"sidechain_importBlock","params":[4,214,133,100],"id":1}"#; + + let response_string = io.handle_request_sync(enclave_req).unwrap(); + + let err_msg = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid params: invalid type: integer `4`, expected a string."},"id":1}"#; + assert_eq!(response_string, err_msg); + } + + #[test] + pub fn sidechain_import_block_returns_decode_err() { + let io = io_handler(); + let enclave_req = r#"{"jsonrpc":"2.0","method":"sidechain_importBlock","params":["SophisticatedInvalidParam"],"id":1}"#; + + let response_string = io.handle_request_sync(enclave_req).unwrap(); + + let err_msg = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid parameters: Could not decode Vec","data":"[\"SophisticatedInvalidParam\"]"},"id":1}"#; + assert_eq!(response_string, err_msg); + } + + pub fn sidechain_import_block_returns_decode_err_for_valid_hex() { + let io = io_handler(); + + let enclave_req = + r#"{"jsonrpc":"2.0","method":"sidechain_importBlock","params": ["0x11"],"id":1}"#; + + let response_string = io.handle_request_sync(enclave_req).unwrap(); + + let err_msg = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid parameters: Could not decode Vec","data":"[17]"},"id":1}"#; + assert_eq!(response_string, err_msg); + } +} diff --git a/bitacross-worker/sidechain/rpc-handler/src/lib.rs b/bitacross-worker/sidechain/rpc-handler/src/lib.rs new file mode 100644 index 0000000000..5daf1d1f7e --- /dev/null +++ b/bitacross-worker/sidechain/rpc-handler/src/lib.rs @@ -0,0 +1,38 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#![feature(trait_alias)] +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +extern crate core; +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +// re-export module to properly feature gate sgx and regular std environment +#[cfg(all(not(feature = "std"), feature = "sgx"))] +pub mod sgx_reexport_prelude { + pub use futures_sgx as futures; + pub use jsonrpc_core_sgx as jsonrpc_core; + pub use rust_base58_sgx as base58; +} + +pub mod constants; +pub mod direct_top_pool_api; +pub mod import_block_api; diff --git a/bitacross-worker/sidechain/sidechain-crate/Cargo.toml b/bitacross-worker/sidechain/sidechain-crate/Cargo.toml new file mode 100644 index 0000000000..dee5728123 --- /dev/null +++ b/bitacross-worker/sidechain/sidechain-crate/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "its-sidechain" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[features] +default = ["std"] +std = [ + "its-block-composer/std", + "its-consensus-aura/std", + "its-consensus-common/std", + "its-consensus-slots/std", + "its-rpc-handler/std", + "its-primitives/std", + "its-state/std", + "its-validateer-fetch/std", +] +sgx = [ + "its-block-composer/sgx", + "its-consensus-aura/sgx", + "its-consensus-common/sgx", + "its-consensus-slots/sgx", + "its-rpc-handler/sgx", + "its-state/sgx", +] + +[dependencies] +its-block-composer = { path = "../block-composer", default-features = false } +its-consensus-aura = { path = "../consensus/aura", default-features = false } +its-consensus-common = { path = "../consensus/common", default-features = false } +its-consensus-slots = { path = "../consensus/slots", default-features = false } +its-primitives = { path = "../primitives", default-features = false } +its-rpc-handler = { path = "../rpc-handler", default-features = false } +its-state = { path = "../state", default-features = false } +its-validateer-fetch = { path = "../validateer-fetch", default-features = false } diff --git a/bitacross-worker/sidechain/sidechain-crate/src/lib.rs b/bitacross-worker/sidechain/sidechain-crate/src/lib.rs new file mode 100644 index 0000000000..59821318a8 --- /dev/null +++ b/bitacross-worker/sidechain/sidechain-crate/src/lib.rs @@ -0,0 +1,39 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Reexport all the sidechain stuff in one crate + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +pub use its_block_composer as block_composer; + +pub use its_consensus_aura as aura; + +pub use its_consensus_common as consensus_common; + +pub use its_consensus_slots as slots; + +pub use its_primitives as primitives; + +pub use its_rpc_handler as rpc_handler; + +pub use its_state as state; + +pub use its_validateer_fetch as validateer_fetch; diff --git a/bitacross-worker/sidechain/state/Cargo.toml b/bitacross-worker/sidechain/state/Cargo.toml new file mode 100644 index 0000000000..538fb34c50 --- /dev/null +++ b/bitacross-worker/sidechain/state/Cargo.toml @@ -0,0 +1,56 @@ +[package] +name = "its-state" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "chain-error"] } +frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +log = { version = "0.4", default-features = false } + +# optional std deps +thiserror = { version = "1.0.9", optional = true } + +# sgx deps +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", optional = true } + +# sgx forks +thiserror_sgx = { package = "thiserror", version = "1.0.9", git = "https://github.com/mesalock-linux/thiserror-sgx", tag = "sgx_1.1.3", optional = true } + +# local deps +itp-sgx-externalities = { default-features = false, path = "../../core-primitives/substrate-sgx/externalities" } +itp-storage = { path = "../../core-primitives/storage", default-features = false } +its-primitives = { path = "../primitives", default-features = false } +sp-io = { optional = true, default-features = false, features = ["disable_oom", "disable_panic_handler", "disable_allocator"], path = "../../core-primitives/substrate-sgx/sp-io" } + +# substrate deps +sp-core = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +[features] +default = ["std"] +std = [ + "log/std", + # substrate + "sp-core/std", + # local crates + "itp-sgx-externalities/std", + "itp-storage/std", + "its-primitives/std", + "sp-io/std", + # optional std crates + "codec/std", + "thiserror", + "sp-runtime/std", +] +sgx = [ + # teaclave + "sgx_tstd", + # local crates + "itp-sgx-externalities/sgx", + "itp-storage/sgx", + "sp-io/sgx", + # sgx versions of std crates + "thiserror_sgx", +] diff --git a/bitacross-worker/sidechain/state/src/error.rs b/bitacross-worker/sidechain/state/src/error.rs new file mode 100644 index 0000000000..82838b376e --- /dev/null +++ b/bitacross-worker/sidechain/state/src/error.rs @@ -0,0 +1,31 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +use crate::sgx_reexports::*; + +use std::string::String; + +#[derive(Debug, thiserror::Error, PartialEq, Eq)] +pub enum Error { + #[error("Invalid apriori state hash supplied")] + InvalidAprioriHash, + #[error("Invalid storage diff")] + InvalidStorageDiff, + #[error("Codec error when accessing module: {1}, storage: {2}. Error: {0:?}")] + DB(codec::Error, String, String), +} diff --git a/bitacross-worker/sidechain/state/src/impls.rs b/bitacross-worker/sidechain/state/src/impls.rs new file mode 100644 index 0000000000..b69727085c --- /dev/null +++ b/bitacross-worker/sidechain/state/src/impls.rs @@ -0,0 +1,184 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Implement the sidechain state traits. + +use crate::{Error, SidechainState, StateUpdate}; +use codec::{Decode, Encode}; +use core::fmt::Debug; +use frame_support::ensure; +use itp_sgx_externalities::{SgxExternalitiesTrait, StateHash}; +use itp_storage::keys::storage_value_key; +use log::{debug, error, info}; +use sp_io::{storage, KillStorageResult}; + +impl SidechainState for T +where + ::SgxExternalitiesType: Encode, +{ + type Externalities = Self; + type StateUpdate = StateUpdate; + + fn apply_state_update(&mut self, state_payload: &Self::StateUpdate) -> Result<(), Error> { + info!("Current state size: {}", self.state().encoded_size()); + debug!("Current hash: {}", self.hash()); + debug!("State_payload hash: {}", state_payload.state_hash_apriori()); + debug!("self is: {:?}", &self); + debug!("state_payload is: {:?}", &state_payload); + ensure!(self.hash() == state_payload.state_hash_apriori(), Error::InvalidAprioriHash); + + self.execute_with(|| { + state_payload.state_update.iter().for_each(|(k, v)| { + match v { + Some(value) => storage::set(k, value), + None => storage::clear(k), + }; + }) + }); + + ensure!(self.hash() == state_payload.state_hash_aposteriori(), Error::InvalidStorageDiff); + self.prune_state_diff(); + Ok(()) + } + + fn get_with_name(&self, module_prefix: &str, storage_prefix: &str) -> Option { + let res = self + .get(&storage_value_key(module_prefix, storage_prefix)) + .map(|v| Decode::decode(&mut v.as_slice())) + .transpose(); + + match res { + Ok(res) => res, + Err(e) => { + error!( + "Error decoding storage: {}, {}. Error: {:?}", + module_prefix, storage_prefix, e + ); + None + }, + } + } + + fn set_with_name(&mut self, module_prefix: &str, storage_prefix: &str, value: V) { + self.set(&storage_value_key(module_prefix, storage_prefix), &value.encode()) + } + + fn clear_with_name(&mut self, module_prefix: &str, storage_prefix: &str) { + self.clear(&storage_value_key(module_prefix, storage_prefix)) + } + + fn clear_prefix_with_name( + &mut self, + module_prefix: &str, + storage_prefix: &str, + ) -> KillStorageResult { + self.clear_sidechain_prefix(&storage_value_key(module_prefix, storage_prefix)) + } + + fn set(&mut self, key: &[u8], value: &[u8]) { + self.execute_with(|| sp_io::storage::set(key, value)) + } + + fn clear(&mut self, key: &[u8]) { + self.execute_with(|| sp_io::storage::clear(key)) + } + + fn clear_sidechain_prefix(&mut self, prefix: &[u8]) -> KillStorageResult { + self.execute_with(|| sp_io::storage::clear_prefix(prefix, None)) + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + use crate::StateUpdate; + use frame_support::{assert_err, assert_ok}; + use itp_sgx_externalities::{SgxExternalities, SgxExternalitiesTrait}; + use sp_core::H256; + + pub fn default_db() -> SgxExternalities { + SgxExternalities::default() + } + + #[test] + pub fn apply_state_update_works() { + let mut state1 = default_db(); + let mut state2 = default_db(); + + let apriori = state1.hash(); + state1.set(b"Hello", b"World"); + let aposteriori = state1.hash(); + + let mut state_update = StateUpdate::new(apriori, aposteriori, state1.state_diff().clone()); + + assert_ok!(state2.apply_state_update(&mut state_update)); + assert_eq!(state2.hash(), aposteriori); + assert_eq!(state2.get(b"Hello").unwrap(), b"World"); + assert!(state2.state_diff().is_empty()); + } + + #[test] + pub fn apply_state_update_returns_storage_hash_mismatch_err() { + let mut state1 = default_db(); + let mut state2 = default_db(); + + let apriori = H256::from([1; 32]); + state1.set(b"Hello", b"World"); + let aposteriori = state1.hash(); + + let mut state_update = StateUpdate::new(apriori, aposteriori, state1.state_diff().clone()); + + assert_err!(state2.apply_state_update(&mut state_update), Error::InvalidAprioriHash); + assert_eq!(state2, default_db()); + } + + #[test] + pub fn apply_state_update_returns_invalid_storage_diff_err() { + let mut state1 = default_db(); + let mut state2 = default_db(); + + let apriori = state1.hash(); + state1.set(b"Hello", b"World"); + let aposteriori = H256::from([1; 32]); + + let mut state_update = StateUpdate::new(apriori, aposteriori, state1.state_diff().clone()); + + assert_err!(state2.apply_state_update(&mut state_update), Error::InvalidStorageDiff); + // After an error, the state is not guaranteed to be reverted and is potentially corrupted! + assert_ne!(state2, default_db()); + } + + #[test] + pub fn sp_io_storage_set_creates_storage_diff() { + let mut state1 = default_db(); + + state1.execute_with(|| { + storage::set(b"hello", b"world"); + }); + + assert_eq!(state1.state_diff().get(&b"hello"[..]).unwrap(), &Some(b"world".encode())); + } + + #[test] + pub fn create_state_diff_without_setting_externalities_works() { + let mut state1 = default_db(); + + state1.set(b"hello", b"world"); + + assert_eq!(state1.state_diff().get(&b"hello"[..]).unwrap(), &Some(b"world".encode())); + } +} diff --git a/bitacross-worker/sidechain/state/src/lib.rs b/bitacross-worker/sidechain/state/src/lib.rs new file mode 100644 index 0000000000..01f5e086ec --- /dev/null +++ b/bitacross-worker/sidechain/state/src/lib.rs @@ -0,0 +1,208 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +extern crate sgx_tstd as std; + +mod error; +mod impls; + +pub use error::*; +pub use impls::*; + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +mod sgx_reexports { + pub use thiserror_sgx as thiserror; +} + +use codec::{Decode, Encode}; +use itp_sgx_externalities::{SgxExternalitiesDiffType, SgxExternalitiesTrait, StateHash}; +use its_primitives::{ + traits::Block as SidechainBlockTrait, + types::{BlockHash, BlockNumber, Timestamp}, +}; +use sp_core::H256; +use sp_io::KillStorageResult; +use sp_runtime::traits::Header as ParentchainHeaderTrait; + +/// Contains the necessary data to update the `SidechainDB` when importing a `SidechainBlock`. +#[derive(PartialEq, Eq, Clone, Debug, Encode, Decode)] +pub struct StateUpdate { + /// state hash before the `state_update` was applied. + state_hash_apriori: H256, + /// state hash after the `state_update` was applied. + state_hash_aposteriori: H256, + /// state diff applied to state with hash `state_hash_apriori` + /// leading to state with hash `state_hash_aposteriori` + state_update: SgxExternalitiesDiffType, +} + +impl StateUpdate { + /// get state hash before the `state_update` was applied. + pub fn state_hash_apriori(&self) -> H256 { + self.state_hash_apriori + } + /// get state hash after the `state_update` was applied. + pub fn state_hash_aposteriori(&self) -> H256 { + self.state_hash_aposteriori + } + /// reference to the `state_update` + pub fn state_update(&self) -> &SgxExternalitiesDiffType { + &self.state_update + } + + /// create new `StatePayload` instance. + pub fn new(apriori: H256, aposteriori: H256, update: SgxExternalitiesDiffType) -> StateUpdate { + StateUpdate { + state_hash_apriori: apriori, + state_hash_aposteriori: aposteriori, + state_update: update, + } + } +} +/// Abstraction around the sidechain state. +pub trait SidechainState: Clone { + type Externalities: SgxExternalitiesTrait + StateHash; + + type StateUpdate: Encode + Decode; + + /// Apply the state update to the state. + /// + /// Does not guarantee state consistency in case of a failure. + /// Caller is responsible for discarding corrupt/inconsistent state. + fn apply_state_update(&mut self, state_payload: &Self::StateUpdate) -> Result<(), Error>; + + /// Get a storage value by its full name. + fn get_with_name(&self, module_prefix: &str, storage_prefix: &str) -> Option; + + /// Set a storage value by its full name. + fn set_with_name(&mut self, module_prefix: &str, storage_prefix: &str, value: V); + + /// Clear a storage value by its full name. + fn clear_with_name(&mut self, module_prefix: &str, storage_prefix: &str); + + /// Clear all storage values for the given prefix. + fn clear_prefix_with_name( + &mut self, + module_prefix: &str, + storage_prefix: &str, + ) -> KillStorageResult; + + /// Set a storage value by its storage hash. + fn set(&mut self, key: &[u8], value: &[u8]); + + /// Clear a storage value by its storage hash. + fn clear(&mut self, key: &[u8]); + + /// Clear a all storage values starting the given prefix. + fn clear_sidechain_prefix(&mut self, prefix: &[u8]) -> KillStorageResult; +} + +/// trait to set and get the last sidechain block of the sidechain state +pub trait LastBlockExt { + /// get the last block of the sidechain state + fn get_last_block(&self) -> Option; + + /// set the last block of the sidechain state + fn set_last_block(&mut self, block: &SidechainBlock); +} + +impl + LastBlockExt for E +{ + fn get_last_block(&self) -> Option { + self.get_with_name("System", "LastBlock") + } + + fn set_last_block(&mut self, block: &SidechainBlock) { + self.set_last_block_hash(&block.hash()); + self.set_with_name("System", "LastBlock", block) + } +} + +/// System extension for the `SidechainDB`. +pub trait SidechainSystemExt { + /// Get the last block number. + fn get_block_number(&self) -> Option; + + /// Set the last block number. + fn set_block_number(&mut self, number: &BlockNumber); + + /// Get the last block hash. + fn get_last_block_hash(&self) -> Option; + + /// Set the last block hash. + fn set_last_block_hash(&mut self, hash: &BlockHash); + + /// Get the timestamp of. + fn get_timestamp(&self) -> Option; + + /// Set the timestamp. + fn set_timestamp(&mut self, timestamp: &Timestamp); + + /// Resets the events. + fn reset_events(&mut self); + + /// Litentry: set the parentchain block number from the parentchain header + /// The reasons to put it here instead of calling `ParentchainPalletInterface::update_parentchain_block` somewhere are: + /// 1. The Stf::update_parentchain_block is too heavy weighted, where the whole state is loaded upon each parentchain + /// block import - btw it's not reachable for now as `storage_hashes_to_update_on_block` is always empty + /// 2. It represents the parentchain block number on which the current sidechain block is built, it's more natural to + /// call it in the state preprocessing before proposing a sidechain block + fn set_parentchain_block_number(&mut self, header: &PH); +} + +impl SidechainSystemExt for T { + fn get_block_number(&self) -> Option { + self.get_with_name("System", "Number") + } + + fn set_block_number(&mut self, number: &BlockNumber) { + self.set_with_name("System", "Number", number) + } + + fn get_last_block_hash(&self) -> Option { + self.get_with_name("System", "LastHash") + } + + fn set_last_block_hash(&mut self, hash: &BlockHash) { + self.set_with_name("System", "LastHash", hash) + } + + fn get_timestamp(&self) -> Option { + self.get_with_name("System", "Timestamp") + } + + fn set_timestamp(&mut self, timestamp: &Timestamp) { + self.set_with_name("System", "Timestamp", timestamp) + } + + fn reset_events(&mut self) { + self.clear_with_name("System", "Events"); + self.clear_with_name("System", "EventCount"); + self.clear_prefix_with_name("System", "EventTopics"); + } + + fn set_parentchain_block_number(&mut self, header: &PH) { + self.set_with_name("Parentchain", "Number", header.number()) + } +} diff --git a/bitacross-worker/sidechain/storage/Cargo.toml b/bitacross-worker/sidechain/storage/Cargo.toml new file mode 100644 index 0000000000..7df586896f --- /dev/null +++ b/bitacross-worker/sidechain/storage/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "its-storage" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +# crate.io +codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +log = "0.4" +parking_lot = "0.12.1" +rocksdb = { version = "0.20.1", default_features = false } +serde = { version = "1.0", features = ["derive"] } +thiserror = "1.0" + +# integritee +itp-settings = { path = "../../core-primitives/settings" } + +its-primitives = { path = "../primitives" } + +# Substrate dependencies +sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +[dev-dependencies] +# crate.io +mockall = "0.11" +temp-dir = "0.1" +# local +itp-time-utils = { path = "../../core-primitives/time-utils" } +its-test = { path = "../test" } +itp-types = { path = "../../core-primitives/types" } + +[features] +mocks = [] diff --git a/bitacross-worker/sidechain/storage/src/db.rs b/bitacross-worker/sidechain/storage/src/db.rs new file mode 100644 index 0000000000..6e51a749fb --- /dev/null +++ b/bitacross-worker/sidechain/storage/src/db.rs @@ -0,0 +1,67 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use super::{Error, Result}; +use codec::{Decode, Encode}; +use rocksdb::{WriteBatch, DB}; +use std::path::PathBuf; + +/// Sidechain DB Storage structure: +/// STORED_SHARDS_KEY -> Vec<(Shard)> +/// (LAST_BLOCK_KEY, Shard) -> (Blockhash, BlockNr) (look up current blockchain state) +/// (Shard , Block number) -> Blockhash (needed for block pruning) +/// Blockhash -> Signed Block (actual block storage) + +/// Interface struct to rocks DB +pub struct SidechainDB { + db: DB, +} + +impl SidechainDB { + pub fn open_default(path: PathBuf) -> Result { + Ok(SidechainDB { db: DB::open_default(path)? }) + } + + /// returns the decoded value of the DB entry, if there is one + pub fn get(&self, key: K) -> Result> { + match self.db.get(key.encode())? { + None => Ok(None), + Some(encoded_hash) => Ok(Some(V::decode(&mut encoded_hash.as_slice())?)), + } + } + + /// writes a batch to the DB + pub fn write(&mut self, batch: WriteBatch) -> Result<()> { + self.db.write(batch).map_err(Error::Operational) + } + + /// adds a given key value pair to the batch + pub fn add_to_batch(batch: &mut WriteBatch, key: K, value: V) { + batch.put(key.encode(), &value.encode()) + } + + /// adds a delte key command to the batch + pub fn delete_to_batch(batch: &mut WriteBatch, key: K) { + batch.delete(key.encode()) + } + + /// add an entry to the DB + #[cfg(test)] + pub fn put(&mut self, key: K, value: V) -> Result<()> { + self.db.put(key.encode(), value.encode()).map_err(Error::Operational) + } +} diff --git a/bitacross-worker/sidechain/storage/src/error.rs b/bitacross-worker/sidechain/storage/src/error.rs new file mode 100644 index 0000000000..983909f1a4 --- /dev/null +++ b/bitacross-worker/sidechain/storage/src/error.rs @@ -0,0 +1,34 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use thiserror::Error; + +pub type Result = std::result::Result; + +#[derive(Error, Debug)] +pub enum Error { + #[error("Could not interact with file storage: {0:?}")] + Operational(#[from] rocksdb::Error), + #[error("Last Block of shard {0} not found")] + LastBlockNotFound(String), + #[error("Failed to find parent block")] + FailedToFindParentBlock, + #[error("Could not decode: {0:?}")] + Decode(#[from] codec::Error), + #[error("Given block is not a successor of the last known block")] + HeaderAncestryMismatch, +} diff --git a/bitacross-worker/sidechain/storage/src/fetch_blocks_mock.rs b/bitacross-worker/sidechain/storage/src/fetch_blocks_mock.rs new file mode 100644 index 0000000000..4ba476164d --- /dev/null +++ b/bitacross-worker/sidechain/storage/src/fetch_blocks_mock.rs @@ -0,0 +1,71 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{error::Result, interface::FetchBlocks, storage::LastSidechainBlock}; +use its_primitives::{ + traits::{Block, ShardIdentifierFor}, + types::{BlockHash, BlockNumber, SignedBlock}, +}; + +#[derive(Default)] +pub struct FetchBlocksMock { + blocks_to_be_fetched: Vec, +} + +impl FetchBlocksMock { + pub fn with_blocks(mut self, blocks: Vec) -> Self { + self.blocks_to_be_fetched = blocks; + self + } +} + +impl FetchBlocks for FetchBlocksMock { + fn fetch_all_blocks_after( + &self, + _block_hash: &BlockHash, + _shard_identifier: &ShardIdentifierFor, + ) -> Result> { + Ok(self.blocks_to_be_fetched.clone()) + } + + fn fetch_blocks_in_range( + &self, + _block_hash_from: &BlockHash, + _block_hash_until: &BlockHash, + _shard_identifier: &ShardIdentifierFor, + ) -> Result> { + Ok(self.blocks_to_be_fetched.clone()) + } + + fn latest_block( + &self, + _shard_identifier: &ShardIdentifierFor, + ) -> Option { + self.blocks_to_be_fetched.get(0).map(|block| LastSidechainBlock { + hash: block.block.hash(), + number: block.block.header.block_number, + }) + } + + fn block_hash( + &self, + _block_number: BlockNumber, + _shard_identifier: &ShardIdentifierFor, + ) -> Option { + None + } +} diff --git a/bitacross-worker/sidechain/storage/src/interface.rs b/bitacross-worker/sidechain/storage/src/interface.rs new file mode 100644 index 0000000000..03b41d6d76 --- /dev/null +++ b/bitacross-worker/sidechain/storage/src/interface.rs @@ -0,0 +1,154 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#[cfg(test)] +use mockall::predicate::*; +#[cfg(test)] +use mockall::*; + +use super::{ + storage::{LastSidechainBlock, SidechainStorage}, + Result, +}; +use its_primitives::{ + traits::{ShardIdentifierFor, SignedBlock as SignedBlockT}, + types::{BlockHash, BlockNumber}, +}; +use parking_lot::RwLock; +use std::path::PathBuf; + +/// Lock wrapper around sidechain storage +pub struct SidechainStorageLock { + storage: RwLock>, +} + +impl SidechainStorageLock { + pub fn from_base_path(path: PathBuf) -> Result> { + Ok(SidechainStorageLock { + storage: RwLock::new(SidechainStorage::::load_from_base_path(path)?), + }) + } +} + +/// Storage interface Trait +#[cfg_attr(test, automock)] +pub trait BlockStorage { + // Type is not working because broadcaster needs to work with the same block type, + // so it needs to be defined somewhere more global. + // type SignedBlock: SignedBlockT; + fn store_blocks(&self, blocks: Vec) -> Result<()>; +} + +pub trait BlockPruner { + /// Prune all blocks except the newest n, where n = `number_of_blocks_to_keep`. + fn prune_blocks_except(&self, number_of_blocks_to_keep: u64); +} + +#[cfg_attr(test, automock)] +pub trait FetchBlocks { + /// Fetch all child blocks of a specified block. + /// + /// Returns an empty vector if specified block hash cannot be found in storage. + fn fetch_all_blocks_after( + &self, + block_hash: &BlockHash, + shard_identifier: &ShardIdentifierFor, + ) -> Result>; + + /// Fetch all blocks within a range, defined by a starting block (lower bound) and end block (upper bound) hash. + /// + /// Does NOT include the bound defining blocks in the result. ]from..until[. + /// Returns an empty vector if 'from' cannot be found in storage. + /// Returns the same as 'fetch_all_blocks_after' if 'until' cannot be found in storage. + fn fetch_blocks_in_range( + &self, + block_hash_from: &BlockHash, + block_hash_until: &BlockHash, + shard_identifier: &ShardIdentifierFor, + ) -> Result>; + + // litentry + fn latest_block( + &self, + shard_identifier: &ShardIdentifierFor, + ) -> Option; + + fn block_hash( + &self, + block_number: BlockNumber, + shard_identifier: &ShardIdentifierFor, + ) -> Option; +} + +impl BlockStorage for SidechainStorageLock { + fn store_blocks(&self, blocks: Vec) -> Result<()> { + self.storage.write().store_blocks(blocks) + } +} + +impl BlockPruner for SidechainStorageLock { + fn prune_blocks_except(&self, number_of_blocks_to_keep: BlockNumber) { + self.storage.write().prune_shards(number_of_blocks_to_keep); + } +} + +impl FetchBlocks for SidechainStorageLock { + fn fetch_all_blocks_after( + &self, + block_hash: &BlockHash, + shard_identifier: &ShardIdentifierFor, + ) -> Result> { + self.storage.read().get_blocks_after(block_hash, shard_identifier) + } + + fn fetch_blocks_in_range( + &self, + block_hash_from: &BlockHash, + block_hash_until: &BlockHash, + shard_identifier: &ShardIdentifierFor, + ) -> Result> { + self.storage + .read() + .get_blocks_in_range(block_hash_from, block_hash_until, shard_identifier) + } + + fn latest_block( + &self, + shard_identifier: &ShardIdentifierFor, + ) -> Option { + self.storage + .read() + .last_block_of_shard(shard_identifier) + .map(|e| LastSidechainBlock { hash: e.hash, number: e.number }) + } + + fn block_hash( + &self, + block_number: BlockNumber, + shard_identifier: &ShardIdentifierFor, + ) -> Option { + match self.storage.read().get_block_hash(shard_identifier, block_number) { + Ok(Some(block_hash)) => + Some(LastSidechainBlock { hash: block_hash, number: block_number }), + Ok(None) => None, + Err(e) => { + log::error!("failed to get block_hash. due to:{:?}", e); + None + }, + } + } +} diff --git a/bitacross-worker/sidechain/storage/src/lib.rs b/bitacross-worker/sidechain/storage/src/lib.rs new file mode 100644 index 0000000000..7b6030ff90 --- /dev/null +++ b/bitacross-worker/sidechain/storage/src/lib.rs @@ -0,0 +1,70 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#![cfg_attr(test, feature(assert_matches))] + +use its_primitives::types::BlockNumber; +use std::{ + sync::Arc, + thread, + time::{Duration, SystemTime}, +}; + +mod db; +mod error; +pub mod interface; +mod storage; + +#[cfg(test)] +mod storage_tests_get_blocks_after; + +#[cfg(test)] +mod storage_tests_get_blocks_in_range; + +#[cfg(test)] +mod test_utils; + +#[cfg(feature = "mocks")] +pub mod fetch_blocks_mock; + +pub use error::{Error, Result}; +pub use interface::{BlockPruner, BlockStorage, SidechainStorageLock}; +pub use storage::LastSidechainBlock; + +pub fn start_sidechain_pruning_loop( + storage: &Arc, + purge_interval: u64, + purge_limit: BlockNumber, +) where + D: BlockPruner, +{ + let interval_time = Duration::from_secs(purge_interval); + let mut interval_start = SystemTime::now(); + loop { + if let Ok(elapsed) = interval_start.elapsed() { + if elapsed >= interval_time { + // update interval time + interval_start = SystemTime::now(); + storage.prune_blocks_except(purge_limit); + } else { + // sleep for the rest of the interval + let sleep_time = interval_time - elapsed; + thread::sleep(sleep_time); + } + } + } +} diff --git a/bitacross-worker/sidechain/storage/src/storage.rs b/bitacross-worker/sidechain/storage/src/storage.rs new file mode 100644 index 0000000000..4e5667627b --- /dev/null +++ b/bitacross-worker/sidechain/storage/src/storage.rs @@ -0,0 +1,1176 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use super::{db::SidechainDB, Error, Result}; +use codec::{Decode, Encode}; +use itp_settings::files::SIDECHAIN_STORAGE_PATH; +use its_primitives::{ + traits::{Block as BlockTrait, Header as HeaderTrait, SignedBlock as SignedBlockT}, + types::{BlockHash, BlockNumber}, +}; +use log::*; +use rocksdb::WriteBatch; +use serde::Serialize; +use sp_core::H256; +use std::{collections::HashMap, fmt::Debug, path::PathBuf}; + +/// key value of sidechain db of last block +const LAST_BLOCK_KEY: &[u8] = b"last_sidechainblock"; +/// key value of the stored shards vector +const STORED_SHARDS_KEY: &[u8] = b"stored_shards"; + +/// ShardIdentifier type +type ShardIdentifierFor = + <<::Block as BlockTrait>::HeaderType as HeaderTrait>::ShardIdentifier; + +/// Helper struct, contains the blocknumber +/// and blockhash of the last sidechain block +#[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, Debug, Default, Serialize)] +pub struct LastSidechainBlock { + /// hash of the last sidechain block + pub hash: H256, + /// block number of the last sidechain block + pub number: BlockNumber, +} + +/// Struct used to insert newly produced sidechainblocks +/// into the database +pub struct SidechainStorage { + /// database + db: SidechainDB, + /// shards in database + shards: Vec>, + /// map to last sidechain block of every shard + last_blocks: HashMap, LastSidechainBlock>, +} + +impl SidechainStorage { + /// Loads or initializes the DB at a given path. + /// + /// Loads existing shards and their last blocks in memory for better performance. + pub fn load_from_base_path(base_path: PathBuf) -> Result> { + // load db + let db = SidechainDB::open_default(base_path.join(SIDECHAIN_STORAGE_PATH))?; + let mut storage = SidechainStorage { db, shards: vec![], last_blocks: HashMap::new() }; + storage.shards = storage.load_shards_from_db()?; + // get last block of each shard + for shard in storage.shards.iter() { + if let Some(last_block) = storage.load_last_block_from_db(shard)? { + storage.last_blocks.insert(*shard, last_block); + } else { + // an empty shard sidechain storage should not exist. Consider deleting this shard from the shards list. + error!("Sidechain storage of shard {:?} is empty", shard); + } + } + Ok(storage) + } + + /// gets all shards of currently loaded sidechain db + pub fn shards(&self) -> &Vec> { + &self.shards + } + + /// gets the last block of the current sidechain DB and the given shard + pub fn last_block_of_shard( + &self, + shard: &ShardIdentifierFor, + ) -> Option<&LastSidechainBlock> { + self.last_blocks.get(shard) + } + + /// gets the block hash of the sidechain block of the given shard and block number, if there is such a block + pub fn get_block_hash( + &self, + shard: &ShardIdentifierFor, + block_number: BlockNumber, + ) -> Result> { + self.db.get((*shard, block_number)) + } + + /// gets the block of the given blockhash, if there is such a block + #[allow(unused)] + pub fn get_block(&self, block_hash: &BlockHash) -> Result> { + self.db.get(block_hash) + } + + /// Get all blocks after (i.e. children of) a specified block. + pub fn get_blocks_after( + &self, + block_hash: &BlockHash, + shard_identifier: &ShardIdentifierFor, + ) -> Result> { + // Ensure we find the block in storage (otherwise we would return all blocks for a specific shard). + // The exception is, if the hash is the default hash, which represents block 0. In that case we want to return all blocks. + if block_hash != &BlockHash::default() && self.get_block(block_hash)?.is_none() { + warn!("Could not find starting block in storage, returning empty vector"); + return Ok(Vec::new()) + } + + // We get the latest block and then traverse the parents until we find our starting block. + let last_block_of_shard = self.last_block_of_shard(shard_identifier).ok_or_else(|| { + Error::LastBlockNotFound("Failed to find last block information".to_string()) + })?; + let latest_block = self.get_block(&last_block_of_shard.hash)?.ok_or_else(|| { + Error::LastBlockNotFound("Failed to retrieve last block from storage".to_string()) + })?; + + let mut current_block = latest_block; + let mut blocks_to_return = Vec::::new(); + while ¤t_block.hash() != block_hash { + let parent_block_hash = current_block.block().header().parent_hash(); + + blocks_to_return.push(current_block); + + if parent_block_hash == BlockHash::default() { + break + } + + current_block = + self.get_block(&parent_block_hash)?.ok_or(Error::FailedToFindParentBlock)?; + } + + // Reverse because we iterate from newest to oldest, but result should be oldest first. + blocks_to_return.reverse(); + + Ok(blocks_to_return) + } + + /// Get blocks in a range, defined by 'from' and 'until' (result does NOT include the bound defining blocks). + pub fn get_blocks_in_range( + &self, + block_hash_from: &BlockHash, + block_hash_until: &BlockHash, + shard_identifier: &ShardIdentifierFor, + ) -> Result> { + let all_blocks_from_lower_bound = + self.get_blocks_after(block_hash_from, shard_identifier)?; + + Ok(all_blocks_from_lower_bound + .into_iter() + .take_while(|b| b.hash() != *block_hash_until) + .collect()) + } + + /// Update sidechain storage with blocks. + /// + /// Blocks are iterated through one by one. In case more than one block per shard is included, + /// be sure to give them in the correct order (oldest first). + pub fn store_blocks(&mut self, blocks_to_store: Vec) -> Result<()> { + let mut batch = WriteBatch::default(); + let mut new_shard = false; + for block in blocks_to_store.into_iter() { + if let Err(e) = self.add_block_to_batch(&block, &mut new_shard, &mut batch) { + error!("Could not store block {:?} due to: {:?}", block, e); + }; + } + // Update stored_shards_key -> vec only if a new shard was included, + if new_shard { + SidechainDB::add_to_batch(&mut batch, STORED_SHARDS_KEY, self.shards().clone()); + } + // Store everything. + self.db.write(batch) + } + + /// purges a shard and its block from the db storage + pub fn purge_shard(&mut self, shard: &ShardIdentifierFor) -> Result<()> { + // get last block of shard + let last_block = self.get_last_block_of_shard(shard)?; + + // remove last block from db storage + let mut batch = WriteBatch::default(); + self.delete_last_block(&mut batch, &last_block, shard); + + // Remove the rest of the blocks from the db + let mut current_block_number = last_block.number; + while let Some(previous_block) = self.get_previous_block(shard, current_block_number)? { + current_block_number = previous_block.number; + self.delete_block(&mut batch, &previous_block.hash, ¤t_block_number, shard); + } + // Remove shard from list. + // STORED_SHARDS_KEY -> Vec<(Shard)> + self.shards.retain(|&x| x != *shard); + // Add updated shards to batch. + SidechainDB::add_to_batch(&mut batch, STORED_SHARDS_KEY, &self.shards); + // Update DB + self.db.write(batch) + } + + /// purges a shard and its block from the db storage + /// FIXME: Add delete functions? + pub fn prune_shard_from_block_number( + &mut self, + shard: &ShardIdentifierFor, + block_number: BlockNumber, + ) -> Result<()> { + let last_block = self.get_last_block_of_shard(shard)?; + if last_block.number == block_number { + // given block number is last block of chain - purge whole shard + self.purge_shard(shard) + } else { + // iterate through chain and add all blocks to WriteBatch (delete cmd) + let mut batch = WriteBatch::default(); + let mut current_block_number = block_number; + // Remove blocks from db until no block anymore + while let Some(block_hash) = self.get_block_hash(shard, current_block_number)? { + self.delete_block(&mut batch, &block_hash, ¤t_block_number, shard); + current_block_number -= 1; + } + // Update DB + self.db.write(batch) + } + } + + /// Prunes all shards except for the newest blocks (according to blocknumber). + pub fn prune_shards(&mut self, number_of_blocks_to_keep: BlockNumber) { + for shard in self.shards().clone() { + // get last block: + if let Some(last_block) = self.last_block_of_shard(&shard) { + let threshold_block = last_block.number - number_of_blocks_to_keep; + if let Err(e) = self.prune_shard_from_block_number(&shard, threshold_block) { + error!("Could not purge shard {:?} due to {:?}", shard, e); + } + } else { + error!("Last block not found in shard {:?}", shard); + } + } + } + + fn add_block_to_batch( + &mut self, + signed_block: &SignedBlock, + new_shard: &mut bool, + batch: &mut WriteBatch, + ) -> Result<()> { + let shard = &signed_block.block().header().shard_id(); + if self.shards.contains(shard) { + if !self.verify_block_ancestry(signed_block.block()) { + // Do not include block if its not a direct ancestor of the last block in line. + return Err(Error::HeaderAncestryMismatch) + } + } else { + self.shards.push(*shard); + *new_shard = true; + } + // Add block to DB batch. + self.add_last_block(batch, signed_block); + Ok(()) + } + + fn verify_block_ancestry(&self, block: &::Block) -> bool { + let shard = &block.header().shard_id(); + let current_block_nr = block.header().block_number(); + if let Some(last_block) = self.last_block_of_shard(shard) { + if last_block.number != current_block_nr - 1 { + error!("[Sidechain DB] Sidechainblock (nr: {:?}) is not a succession of the previous block (nr: {:?}) in shard: {:?}", + current_block_nr, last_block.number, *shard); + return false + } + } else { + error!( + "[Sidechain DB] Shard {:?} does not have a last block. Skipping block (nr: {:?}) inclusion", + *shard, current_block_nr + ); + return false + } + true + } + + /// Implementations of helper functions, not meant for pub use + /// gets the previous block of given shard and block number, if there is one. + fn get_previous_block( + &self, + shard: &ShardIdentifierFor, + current_block_number: BlockNumber, + ) -> Result> { + let prev_block_number = current_block_number - 1; + Ok(self + .get_block_hash(shard, prev_block_number)? + .map(|block_hash| LastSidechainBlock { hash: block_hash, number: prev_block_number })) + } + fn load_shards_from_db(&self) -> Result>> { + Ok(self.db.get(STORED_SHARDS_KEY)?.unwrap_or_default()) + } + + fn load_last_block_from_db( + &self, + shard: &ShardIdentifierFor, + ) -> Result> { + self.db.get((LAST_BLOCK_KEY, *shard)) + } + + fn get_last_block_of_shard( + &self, + shard: &ShardIdentifierFor, + ) -> Result { + match self.last_blocks.get(shard) { + Some(last_block) => Ok(*last_block), + None => { + // Try to read from db: + self.load_last_block_from_db(shard)? + .ok_or_else(|| Error::LastBlockNotFound(format!("{:?}", *shard))) + }, + } + } + + /// Adds the block to the WriteBatch. + fn add_last_block(&mut self, batch: &mut WriteBatch, block: &SignedBlock) { + let hash = block.hash(); + let block_number = block.block().header().block_number(); + let shard = block.block().header().shard_id(); + // Block hash -> Signed Block. + SidechainDB::add_to_batch(batch, hash, block); + + // (Shard, Block number) -> Blockhash (for block pruning). + SidechainDB::add_to_batch(batch, (shard, block_number), hash); + + // (last_block_key, shard) -> (Blockhash, BlockNr) current blockchain state. + let last_block = LastSidechainBlock { hash, number: block_number }; + self.last_blocks.insert(shard, last_block); // add in memory + SidechainDB::add_to_batch(batch, (LAST_BLOCK_KEY, shard), last_block); + } + + /// Add delete block to the WriteBatch. + fn delete_block( + &self, + batch: &mut WriteBatch, + block_hash: &H256, + block_number: &BlockNumber, + shard: &ShardIdentifierFor, + ) { + // Block hash -> Signed Block. + SidechainDB::delete_to_batch(batch, block_hash); + // (Shard, Block number) -> Blockhash (for block pruning). + SidechainDB::delete_to_batch(batch, (shard, block_number)); + } + + /// Add delete command to remove last block to WriteBatch and remove it from memory. + /// + /// This includes adding a delete command of the following: + /// - Block hash -> Signed Block. + /// - (Shard, Block number) -> Blockhash (for block pruning). + /// - ((LAST_BLOCK_KEY, shard) -> BlockHash) -> Blockhash (for block pruning). + /// + /// Careful usage of this command: In case the last block is deleted, (LAST_BLOCK_KEY, shard) will be empty + /// even though there might be a new last block (i.e. the previous block of the removed last block). + fn delete_last_block( + &mut self, + batch: &mut WriteBatch, + last_block: &LastSidechainBlock, + shard: &ShardIdentifierFor, + ) { + // Add delete block to batch. + // (LAST_BLOCK_KEY, Shard) -> LastSidechainBlock. + SidechainDB::delete_to_batch(batch, (LAST_BLOCK_KEY, *shard)); + self.delete_block(batch, &last_block.hash, &last_block.number, shard); + + // Delete last block from local memory. + // Careful here: This deletes the local memory before db has been actually pruned + // (it's only been added to the write batch). + // But this can be fixed upon reloading the db / restarting the worker. + self.last_blocks.remove(shard); + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::test_utils::{ + create_signed_block_with_shard as create_signed_block, create_temp_dir, get_storage, + }; + use itp_types::ShardIdentifier; + use its_primitives::{traits::SignedBlock as SignedBlockT, types::SignedBlock}; + use sp_core::H256; + + #[test] + fn load_shards_from_db_works() { + // given + let temp_dir = create_temp_dir(); + let shard_one = H256::from_low_u64_be(1); + let shard_two = H256::from_low_u64_be(2); + // when + { + let mut sidechain_db = get_storage(temp_dir.path().to_path_buf()); + // ensure db starts empty + assert_eq!(sidechain_db.load_shards_from_db().unwrap(), vec![]); + // write signed_block to db + sidechain_db.db.put(STORED_SHARDS_KEY, vec![shard_one, shard_two]).unwrap(); + } + + // then + { + // open new DB of same path: + let updated_sidechain_db = get_storage(temp_dir.path().to_path_buf()); + let loaded_shards = updated_sidechain_db.load_shards_from_db().unwrap(); + assert!(loaded_shards.contains(&shard_one)); + assert!(loaded_shards.contains(&shard_two)); + } + } + + #[test] + fn load_last_block_from_db_works() { + // given + let temp_dir = create_temp_dir(); + let shard = H256::from_low_u64_be(1); + let signed_block = create_signed_block(20, shard); + let signed_last_block = LastSidechainBlock { + hash: signed_block.hash(), + number: signed_block.block().header().block_number(), + }; + // when + { + let mut sidechain_db = get_storage(temp_dir.path().to_path_buf()); + // ensure db starts empty + assert!(sidechain_db.load_last_block_from_db(&shard).unwrap().is_none()); + // write signed_block to db + sidechain_db.db.put((LAST_BLOCK_KEY, shard), signed_last_block.clone()).unwrap(); + } + + // then + { + // open new DB of same path: + let updated_sidechain_db = get_storage(temp_dir.path().to_path_buf()); + let loaded_block = + updated_sidechain_db.load_last_block_from_db(&shard).unwrap().unwrap(); + assert_eq!(loaded_block, signed_last_block); + } + } + + #[test] + fn create_new_sidechain_storage_works() { + let temp_dir = create_temp_dir(); + let shard = H256::from_low_u64_be(1); + let shard_vector = vec![shard]; + let signed_block = create_signed_block(20, shard); + let signed_last_block = LastSidechainBlock { + hash: signed_block.hash(), + number: signed_block.block().header().block_number(), + }; + { + let mut sidechain_db = get_storage(temp_dir.path().to_path_buf()); + // ensure db starts empty + assert!(sidechain_db.load_last_block_from_db(&shard).unwrap().is_none()); + // write shards to db + sidechain_db.db.put((LAST_BLOCK_KEY, shard), signed_last_block.clone()).unwrap(); + // write shards to db + sidechain_db.db.put(STORED_SHARDS_KEY, shard_vector.clone()).unwrap(); + } + + { + // open new DB of same path: + let updated_sidechain_db = get_storage(temp_dir.path().to_path_buf()); + assert_eq!(updated_sidechain_db.shards, shard_vector); + assert_eq!(*updated_sidechain_db.last_blocks.get(&shard).unwrap(), signed_last_block); + } + } + + #[test] + fn add_last_block_works() { + let temp_dir = create_temp_dir(); + let shard = H256::from_low_u64_be(1); + let signed_block = create_signed_block(8, shard); + + { + let mut sidechain_db = get_storage(temp_dir.path().to_path_buf()); + let mut batch = WriteBatch::default(); + sidechain_db.add_last_block(&mut batch, &signed_block); + sidechain_db.db.write(batch).unwrap(); + + // ensure DB contains previously stored data: + let last_block = sidechain_db.last_block_of_shard(&shard).unwrap(); + assert_eq!(last_block.number, signed_block.block().header().block_number()); + assert_eq!(last_block.hash, signed_block.hash()); + let stored_block_hash = + sidechain_db.get_block_hash(&shard, last_block.number).unwrap().unwrap(); + assert_eq!(stored_block_hash, signed_block.hash()); + assert_eq!(sidechain_db.get_block(&stored_block_hash).unwrap().unwrap(), signed_block); + } + } + + #[test] + fn delete_block_works() { + let temp_dir = create_temp_dir(); + let shard = H256::from_low_u64_be(1); + let signed_block = create_signed_block(8, shard); + { + // fill db + let mut sidechain_db = get_storage(temp_dir.path().to_path_buf()); + sidechain_db.db.put(signed_block.hash(), signed_block.clone()).unwrap(); + sidechain_db + .db + .put((shard, signed_block.block().header().block_number()), signed_block.hash()) + .unwrap(); + assert_eq!( + sidechain_db + .db + .get::<(ShardIdentifier, BlockNumber), H256>(( + shard, + signed_block.block().header().block_number() + )) + .unwrap() + .unwrap(), + signed_block.hash() + ); + assert_eq!( + sidechain_db.db.get::(signed_block.hash()).unwrap().unwrap(), + signed_block + ); + + // when + let mut batch = WriteBatch::default(); + sidechain_db.delete_block( + &mut batch, + &signed_block.hash(), + &signed_block.block().header().block_number(), + &shard, + ); + sidechain_db.db.write(batch).unwrap(); + } + + { + // open new DB of same path: + let updated_sidechain_db = get_storage(temp_dir.path().to_path_buf()); + // ensure DB does not contain block anymore: + assert!(updated_sidechain_db + .db + .get::<(ShardIdentifier, BlockNumber), H256>(( + shard, + signed_block.block().header().block_number() + )) + .unwrap() + .is_none()); + assert!(updated_sidechain_db + .db + .get::(signed_block.hash()) + .unwrap() + .is_none()); + } + } + + #[test] + fn delete_last_block_works() { + let temp_dir = create_temp_dir(); + let shard = H256::from_low_u64_be(1); + let signed_block = create_signed_block(8, shard); + let last_block = LastSidechainBlock { + hash: signed_block.hash(), + number: signed_block.block().header().block_number(), + }; + { + // fill db + let mut sidechain_db = get_storage(temp_dir.path().to_path_buf()); + sidechain_db.db.put(signed_block.hash(), signed_block.clone()).unwrap(); + sidechain_db + .db + .put((shard, signed_block.block().header().block_number()), signed_block.hash()) + .unwrap(); + sidechain_db.db.put((LAST_BLOCK_KEY, shard), last_block.clone()).unwrap(); + assert_eq!( + sidechain_db + .db + .get::<(ShardIdentifier, BlockNumber), H256>(( + shard, + signed_block.block().header().block_number() + )) + .unwrap() + .unwrap(), + signed_block.hash() + ); + assert_eq!( + sidechain_db.db.get::(signed_block.hash()).unwrap().unwrap(), + signed_block + ); + assert_eq!( + sidechain_db + .db + .get::<(&[u8], ShardIdentifier), LastSidechainBlock>((LAST_BLOCK_KEY, shard)) + .unwrap() + .unwrap(), + last_block + ); + + // when + let mut batch = WriteBatch::default(); + sidechain_db.delete_last_block(&mut batch, &last_block, &shard); + sidechain_db.db.write(batch).unwrap(); + + // then + assert!(sidechain_db.last_blocks.get(&shard).is_none()); + assert!(sidechain_db + .db + .get::<(ShardIdentifier, BlockNumber), H256>(( + shard, + signed_block.block().header().block_number() + )) + .unwrap() + .is_none()); + assert!(sidechain_db + .db + .get::(signed_block.hash()) + .unwrap() + .is_none()); + assert!(sidechain_db + .db + .get::<(&[u8], ShardIdentifier), LastSidechainBlock>((LAST_BLOCK_KEY, shard)) + .unwrap() + .is_none()); + } + } + + #[test] + fn verify_block_ancestry_returns_true_if_correct_successor() { + let temp_dir = create_temp_dir(); + let shard = H256::from_low_u64_be(1); + let signed_block = create_signed_block(8, shard); + let last_block = LastSidechainBlock { + hash: signed_block.hash(), + number: signed_block.block().header().block_number(), + }; + let signed_block_two = create_signed_block(9, shard); + { + let mut sidechain_db = get_storage(temp_dir.path().to_path_buf()); + sidechain_db.shards.push(shard); + sidechain_db.last_blocks.insert(shard, last_block); + // when + let result = sidechain_db.verify_block_ancestry(&signed_block_two.block()); + + // then + assert!(result); + } + } + + #[test] + fn verify_block_ancestry_returns_false_if_not_correct_successor() { + let temp_dir = create_temp_dir(); + let shard = H256::from_low_u64_be(1); + let signed_block = create_signed_block(8, shard); + let last_block = LastSidechainBlock { + hash: signed_block.hash(), + number: signed_block.block().header().block_number(), + }; + let signed_block_two = create_signed_block(5, shard); + { + let mut sidechain_db = get_storage(temp_dir.path().to_path_buf()); + sidechain_db.shards.push(shard); + sidechain_db.last_blocks.insert(shard, last_block); + + // when + let result = sidechain_db.verify_block_ancestry(&signed_block_two.block()); + + // then + assert!(!result); + } + } + + #[test] + fn verify_block_ancestry_returns_false_no_last_block_registered() { + let temp_dir = create_temp_dir(); + let shard = H256::from_low_u64_be(1); + let signed_block = create_signed_block(8, shard); + { + let mut sidechain_db = get_storage(temp_dir.path().to_path_buf()); + sidechain_db.shards.push(shard); + // when + let result = sidechain_db.verify_block_ancestry(&signed_block.block()); + + // then + assert!(!result); + } + } + + #[test] + fn verify_block_ancestry_returns_false_if_no_shard() { + let temp_dir = create_temp_dir(); + let shard = H256::from_low_u64_be(1); + let signed_block = create_signed_block(8, shard); + { + let sidechain_db = get_storage(temp_dir.path().to_path_buf()); + let result = sidechain_db.verify_block_ancestry(&signed_block.block()); + assert!(!result); + } + } + + #[test] + fn add_block_to_batch_works_with_new_shard() { + // given + let temp_dir = create_temp_dir(); + let shard = H256::from_low_u64_be(1); + let signed_block = create_signed_block(8, shard); + let mut new_shard = false; + { + let mut sidechain_db = get_storage(temp_dir.path().to_path_buf()); + let mut batch = WriteBatch::default(); + assert!(batch.is_empty()); + + sidechain_db + .add_block_to_batch(&signed_block, &mut new_shard, &mut batch) + .unwrap(); + + assert!(new_shard); + assert!(!batch.is_empty()); + } + } + + #[test] + fn add_block_to_batch_does_not_add_shard_if_existent() { + let temp_dir = create_temp_dir(); + let shard = H256::from_low_u64_be(1); + let signed_block = create_signed_block(8, shard); + let last_block = LastSidechainBlock { + hash: signed_block.hash(), + number: signed_block.block().header().block_number(), + }; + let signed_block_two = create_signed_block(9, shard); + let mut new_shard = false; + { + let mut sidechain_db = get_storage(temp_dir.path().to_path_buf()); + let mut batch = WriteBatch::default(); + assert!(batch.is_empty()); + sidechain_db.shards.push(shard); + sidechain_db.last_blocks.insert(shard, last_block); + + sidechain_db + .add_block_to_batch(&signed_block_two, &mut new_shard, &mut batch) + .unwrap(); + + assert!(!new_shard); + assert!(!batch.is_empty()); + } + } + + #[test] + fn add_block_to_batch_does_not_add_block_if_not_ancestor() { + let temp_dir = create_temp_dir(); + let shard = H256::from_low_u64_be(1); + let signed_block = create_signed_block(8, shard); + let last_block = LastSidechainBlock { + hash: signed_block.hash(), + number: signed_block.block().header().block_number(), + }; + let signed_block_two = create_signed_block(10, shard); + let mut new_shard = false; + { + let mut sidechain_db = get_storage(temp_dir.path().to_path_buf()); + let mut batch = WriteBatch::default(); + sidechain_db.shards.push(shard); + sidechain_db.last_blocks.insert(shard, last_block); + + let result = + sidechain_db.add_block_to_batch(&signed_block_two, &mut new_shard, &mut batch); + + assert!(result.is_err()); + assert!(!new_shard); + assert!(batch.is_empty()); + } + } + + #[test] + fn store_block_works() { + let temp_dir = create_temp_dir(); + let shard = H256::from_low_u64_be(1); + let signed_block = create_signed_block(20, shard); + let signed_block_vector: Vec = vec![signed_block.clone()]; + + { + let mut sidechain_db = get_storage(temp_dir.path().to_path_buf()); + // db needs to start empty + assert_eq!(sidechain_db.shards, vec![]); + sidechain_db.store_blocks(signed_block_vector).unwrap(); + } + + { + // open new DB of same path: + let updated_sidechain_db = get_storage(temp_dir.path().to_path_buf()); + // ensure DB contains previously stored data: + assert_eq!(*updated_sidechain_db.shards(), vec![shard]); + let last_block = updated_sidechain_db.last_block_of_shard(&shard).unwrap(); + assert_eq!(last_block.number, signed_block.block().header().block_number()); + assert_eq!(last_block.hash, signed_block.hash()); + let stored_block_hash = + updated_sidechain_db.get_block_hash(&shard, last_block.number).unwrap().unwrap(); + assert_eq!(stored_block_hash, signed_block.hash()); + assert_eq!( + updated_sidechain_db.get_block(&stored_block_hash).unwrap().unwrap(), + signed_block + ); + } + } + + #[test] + fn store_blocks_on_multi_sharding_works() { + let temp_dir = create_temp_dir(); + let shard_one = H256::from_low_u64_be(1); + let shard_two = H256::from_low_u64_be(2); + let signed_block_one = create_signed_block(20, shard_one); + let signed_block_two = create_signed_block(1, shard_two); + + let signed_block_vector: Vec = + vec![signed_block_one.clone(), signed_block_two.clone()]; + + { + let mut sidechain_db = get_storage(temp_dir.path().to_path_buf()); + // db needs to start empty + assert_eq!(sidechain_db.shards, vec![]); + sidechain_db.store_blocks(signed_block_vector).unwrap(); + } + + { + let updated_sidechain_db = get_storage(temp_dir.path().to_path_buf()); + assert_eq!(updated_sidechain_db.shards()[0], shard_one); + assert_eq!(updated_sidechain_db.shards()[1], shard_two); + let last_block_one: &LastSidechainBlock = + updated_sidechain_db.last_blocks.get(&shard_one).unwrap(); + let last_block_two: &LastSidechainBlock = + updated_sidechain_db.last_blocks.get(&shard_two).unwrap(); + assert_eq!(last_block_one.number, 20); + assert_eq!(last_block_two.number, 1); + assert_eq!(last_block_one.hash, signed_block_one.hash()); + assert_eq!(last_block_two.hash, signed_block_two.hash()); + } + } + + #[test] + fn store_mulitple_block_on_one_shard_works() { + let temp_dir = create_temp_dir(); + let shard = H256::from_low_u64_be(1); + let signed_block_one = create_signed_block(20, shard); + let signed_block_two = create_signed_block(21, shard); + let signed_block_vector_one = vec![signed_block_one.clone()]; + let signed_block_vector_two = vec![signed_block_two.clone()]; + + { + // first iteration + let mut sidechain_db = get_storage(temp_dir.path().to_path_buf()); + sidechain_db.store_blocks(signed_block_vector_one).unwrap(); + } + { + // second iteration + let mut sidechain_db = get_storage(temp_dir.path().to_path_buf()); + sidechain_db.store_blocks(signed_block_vector_two).unwrap(); + } + + { + let updated_sidechain_db = get_storage(temp_dir.path().to_path_buf()); + // last block is really equal to second block: + let last_block: &LastSidechainBlock = + updated_sidechain_db.last_blocks.get(&shard).unwrap(); + assert_eq!(last_block.number, 21); + // storage contains both blocks: + // (shard,blocknumber) -> blockhash + let db_block_hash_one = + updated_sidechain_db.get_block_hash(&shard, 20).unwrap().unwrap(); + let db_block_hash_two = + updated_sidechain_db.get_block_hash(&shard, 21).unwrap().unwrap(); + assert_eq!(db_block_hash_one, signed_block_one.hash()); + assert_eq!(db_block_hash_two, signed_block_two.hash()); + + // block hash -> signed block + let db_block_one = + updated_sidechain_db.get_block(&signed_block_one.hash()).unwrap().unwrap(); + let db_block_two = + updated_sidechain_db.get_block(&signed_block_two.hash()).unwrap().unwrap(); + assert_eq!(db_block_one, signed_block_one); + assert_eq!(db_block_two, signed_block_two); + } + } + + #[test] + fn wrong_succession_order_does_not_get_accepted() { + let temp_dir = create_temp_dir(); + let shard = H256::from_low_u64_be(1); + let signed_block_one = create_signed_block(7, shard); + let signed_block_two = create_signed_block(21, shard); + let signed_block_vector_one = vec![signed_block_one.clone()]; + let signed_block_vector_two = vec![signed_block_two.clone()]; + + { + // first iteration + let mut sidechain_db = get_storage(temp_dir.path().to_path_buf()); + sidechain_db.store_blocks(signed_block_vector_one).unwrap(); + } + { + // second iteration + let mut sidechain_db = get_storage(temp_dir.path().to_path_buf()); + sidechain_db.store_blocks(signed_block_vector_two).unwrap(); + } + { + let updated_sidechain_db = get_storage(temp_dir.path().to_path_buf()); + // last block is equal to first block: + let last_block: &LastSidechainBlock = + updated_sidechain_db.last_blocks.get(&shard).unwrap(); + assert_eq!(last_block.number, signed_block_one.block().header().block_number()); + + // storage contains only one blocks: + // (shard,blocknumber) -> blockhash + let db_block_hash_one = updated_sidechain_db + .get_block_hash(&shard, signed_block_one.block().header().block_number()) + .unwrap() + .unwrap(); + let db_block_hash_empty = updated_sidechain_db + .get_block_hash(&shard, signed_block_two.block().header().block_number()) + .unwrap(); + assert!(db_block_hash_empty.is_none()); + assert_eq!(db_block_hash_one, signed_block_one.hash()); + } + } + + #[test] + fn get_previous_block_returns_correct_block() { + let temp_dir = create_temp_dir(); + let shard = H256::from_low_u64_be(1); + let signed_block_one = create_signed_block(1, shard); + // create sidechain_db + { + let mut sidechain_db = get_storage(temp_dir.path().to_path_buf()); + sidechain_db.store_blocks(vec![signed_block_one.clone()]).unwrap(); + // create last block one for comparison + let last_block = LastSidechainBlock { + hash: signed_block_one.hash(), + number: signed_block_one.block().header().block_number(), + }; + + // then + let some_block = sidechain_db + .get_previous_block(&shard, signed_block_one.block().header().block_number() + 1) + .unwrap() + .unwrap(); + + // when + assert_eq!(some_block, last_block); + } + } + + #[test] + fn get_previous_block_returns_none_when_no_block() { + let temp_dir = create_temp_dir(); + let shard = H256::from_low_u64_be(1); + { + let mut sidechain_db = get_storage(temp_dir.path().to_path_buf()); + sidechain_db.store_blocks(vec![create_signed_block(1, shard)]).unwrap(); + + let no_block = sidechain_db.get_previous_block(&shard, 1).unwrap(); + + assert!(no_block.is_none()); + } + } + + #[test] + fn purge_shard_works() { + let temp_dir = create_temp_dir(); + let shard = H256::from_low_u64_be(1); + let block_one = create_signed_block(1, shard); + let block_two = create_signed_block(2, shard); + let block_three = create_signed_block(3, shard); + { + // create sidechain_db + let mut sidechain_db = get_storage(temp_dir.path().to_path_buf()); + sidechain_db.store_blocks(vec![block_one.clone()]).unwrap(); + sidechain_db.store_blocks(vec![block_two.clone()]).unwrap(); + sidechain_db.store_blocks(vec![block_three.clone()]).unwrap(); + + sidechain_db.purge_shard(&shard).unwrap(); + + // test if local storage has been cleansed + assert!(!sidechain_db.shards.contains(&shard)); + assert!(sidechain_db.last_blocks.get(&shard).is_none()); + } + + { + let updated_sidechain_db = get_storage(temp_dir.path().to_path_buf()); + // test if local storage is still clean + assert!(!updated_sidechain_db.shards.contains(&shard)); + assert!(updated_sidechain_db.last_blocks.get(&shard).is_none()); + // test if db is clean + assert!(updated_sidechain_db.last_block_of_shard(&shard).is_none()); + assert!(updated_sidechain_db.get_block_hash(&shard, 3).unwrap().is_none()); + assert!(updated_sidechain_db.get_block_hash(&shard, 2).unwrap().is_none()); + assert!(updated_sidechain_db.get_block_hash(&shard, 1).unwrap().is_none()); + assert!(updated_sidechain_db.get_block(&block_one.hash()).unwrap().is_none()); + assert!(updated_sidechain_db.get_block(&block_two.hash()).unwrap().is_none()); + assert!(updated_sidechain_db.get_block(&block_three.hash()).unwrap().is_none()); + } + } + + #[test] + fn purge_shard_from_block_works() { + let temp_dir = create_temp_dir(); + let shard = H256::from_low_u64_be(1); + let block_one = create_signed_block(1, shard); + let block_two = create_signed_block(2, shard); + let block_three = create_signed_block(3, shard); + let last_block = LastSidechainBlock { + hash: block_three.hash(), + number: block_three.block().header().block_number(), + }; + + { + // create sidechain_db + let mut sidechain_db = get_storage(temp_dir.path().to_path_buf()); + sidechain_db.store_blocks(vec![block_one.clone()]).unwrap(); + sidechain_db.store_blocks(vec![block_two.clone()]).unwrap(); + sidechain_db.store_blocks(vec![block_three.clone()]).unwrap(); + + sidechain_db.prune_shard_from_block_number(&shard, 2).unwrap(); + } + + { + let updated_sidechain_db = get_storage(temp_dir.path().to_path_buf()); + // test local memory + assert!(updated_sidechain_db.shards.contains(&shard)); + assert_eq!(*updated_sidechain_db.last_blocks.get(&shard).unwrap(), last_block); + // assert block three is still there + assert_eq!(*updated_sidechain_db.last_block_of_shard(&shard).unwrap(), last_block); + assert_eq!( + updated_sidechain_db.get_block_hash(&shard, 3).unwrap().unwrap(), + block_three.hash() + ); + assert_eq!( + updated_sidechain_db.get_block(&block_three.hash()).unwrap().unwrap(), + block_three + ); + // assert the lower blocks have been purged + assert!(updated_sidechain_db.get_block_hash(&shard, 2).unwrap().is_none()); + assert!(updated_sidechain_db.get_block_hash(&shard, 1).unwrap().is_none()); + assert!(updated_sidechain_db.get_block(&block_two.hash()).unwrap().is_none()); + assert!(updated_sidechain_db.get_block(&block_one.hash()).unwrap().is_none()); + } + } + + #[test] + fn purge_shard_from_block_works_for_last_block() { + let temp_dir = create_temp_dir(); + let shard = H256::from_low_u64_be(1); + let block_one = create_signed_block(1, shard); + let block_two = create_signed_block(2, shard); + let block_three = create_signed_block(3, shard); + { + // create sidechain_db + let mut sidechain_db = get_storage(temp_dir.path().to_path_buf()); + sidechain_db.store_blocks(vec![block_one.clone()]).unwrap(); + sidechain_db.store_blocks(vec![block_two.clone()]).unwrap(); + sidechain_db.store_blocks(vec![block_three.clone()]).unwrap(); + + sidechain_db.prune_shard_from_block_number(&shard, 3).unwrap(); + + // test if local storage has been cleansed + assert!(!sidechain_db.shards.contains(&shard)); + assert!(sidechain_db.last_blocks.get(&shard).is_none()); + } + + { + let updated_sidechain_db = get_storage(temp_dir.path().to_path_buf()); + // test if local storage is still clean + assert!(!updated_sidechain_db.shards.contains(&shard)); + assert!(updated_sidechain_db.last_blocks.get(&shard).is_none()); + // test if db is clean + assert!(updated_sidechain_db.last_block_of_shard(&shard).is_none()); + assert!(updated_sidechain_db.get_block_hash(&shard, 3).unwrap().is_none()); + assert!(updated_sidechain_db.get_block_hash(&shard, 2).unwrap().is_none()); + assert!(updated_sidechain_db.get_block_hash(&shard, 1).unwrap().is_none()); + assert!(updated_sidechain_db.get_block(&block_one.hash()).unwrap().is_none()); + assert!(updated_sidechain_db.get_block(&block_two.hash()).unwrap().is_none()); + assert!(updated_sidechain_db.get_block(&block_three.hash()).unwrap().is_none()); + } + } + + #[test] + fn prune_shards_works_for_multiple_shards() { + let temp_dir = create_temp_dir(); + // shard one + let shard_one = H256::from_low_u64_be(1); + let block_one = create_signed_block(1, shard_one); + let block_two = create_signed_block(2, shard_one); + let block_three = create_signed_block(3, shard_one); + let last_block_one = LastSidechainBlock { + hash: block_three.hash(), + number: block_three.block().header().block_number(), + }; + // shard two + let shard_two = H256::from_low_u64_be(2); + let block_one_s = create_signed_block(1, shard_two); + let block_two_s = create_signed_block(2, shard_two); + let block_three_s = create_signed_block(3, shard_two); + let block_four_s = create_signed_block(4, shard_two); + let last_block_two = LastSidechainBlock { + hash: block_four_s.hash(), + number: block_four_s.block().header().block_number(), + }; + { + // create sidechain_db + let mut sidechain_db = get_storage(temp_dir.path().to_path_buf()); + sidechain_db.store_blocks(vec![block_one.clone(), block_one_s.clone()]).unwrap(); + sidechain_db.store_blocks(vec![block_two.clone(), block_two_s.clone()]).unwrap(); + sidechain_db + .store_blocks(vec![block_three.clone(), block_three_s.clone()]) + .unwrap(); + sidechain_db.store_blocks(vec![block_four_s.clone()]).unwrap(); + + sidechain_db.prune_shards(2); + } + + { + let updated_sidechain_db = get_storage(temp_dir.path().to_path_buf()); + // test if shard one has been cleansed of block 1, with 2 and 3 still beeing there: + assert_eq!( + *updated_sidechain_db.last_block_of_shard(&shard_one).unwrap(), + last_block_one + ); + assert_eq!( + updated_sidechain_db.get_block_hash(&shard_one, 3).unwrap().unwrap(), + block_three.hash() + ); + assert_eq!( + updated_sidechain_db.get_block(&block_three.hash()).unwrap().unwrap(), + block_three + ); + assert_eq!( + updated_sidechain_db.get_block_hash(&shard_one, 2).unwrap().unwrap(), + block_two.hash() + ); + assert_eq!( + updated_sidechain_db.get_block(&block_two.hash()).unwrap().unwrap(), + block_two + ); + assert!(updated_sidechain_db.get_block(&block_one.hash()).unwrap().is_none()); + assert!(updated_sidechain_db.get_block_hash(&shard_one, 1).unwrap().is_none()); + // test if shard two has been cleansed of block 1 and 2, with 3 and 4 still beeing there: + assert_eq!( + *updated_sidechain_db.last_block_of_shard(&shard_two).unwrap(), + last_block_two + ); + assert_eq!( + updated_sidechain_db.get_block_hash(&shard_two, 4).unwrap().unwrap(), + block_four_s.hash() + ); + assert_eq!( + updated_sidechain_db.get_block(&block_four_s.hash()).unwrap().unwrap(), + block_four_s + ); + assert_eq!( + updated_sidechain_db.get_block_hash(&shard_two, 3).unwrap().unwrap(), + block_three_s.hash() + ); + assert_eq!( + updated_sidechain_db.get_block(&block_three_s.hash()).unwrap().unwrap(), + block_three_s + ); + assert!(updated_sidechain_db.get_block_hash(&shard_two, 2).unwrap().is_none()); + assert!(updated_sidechain_db.get_block_hash(&shard_two, 1).unwrap().is_none()); + assert!(updated_sidechain_db.get_block(&block_one_s.hash()).unwrap().is_none()); + assert!(updated_sidechain_db.get_block(&block_two_s.hash()).unwrap().is_none()); + } + } +} diff --git a/bitacross-worker/sidechain/storage/src/storage_tests_get_blocks_after.rs b/bitacross-worker/sidechain/storage/src/storage_tests_get_blocks_after.rs new file mode 100644 index 0000000000..0795e54ca7 --- /dev/null +++ b/bitacross-worker/sidechain/storage/src/storage_tests_get_blocks_after.rs @@ -0,0 +1,124 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::{ + error::Error, + test_utils::{ + create_signed_block_with_parenthash as create_signed_block, default_shard, + fill_storage_with_blocks, get_storage, + }, +}; +use its_primitives::{traits::SignedBlock, types::BlockHash}; +use std::assert_matches::assert_matches; + +#[test] +fn get_blocks_after_works_for_regular_case() { + let block_1 = create_signed_block(1, BlockHash::default()); + let block_2 = create_signed_block(2, block_1.hash()); + let block_3 = create_signed_block(3, block_2.hash()); + let block_4 = create_signed_block(4, block_3.hash()); + + let temp_dir = + fill_storage_with_blocks(vec![block_1.clone(), block_2.clone(), block_3, block_4.clone()]); + + { + let updated_sidechain_db = get_storage(temp_dir.path().to_path_buf()); + let blocks_after_1 = updated_sidechain_db + .get_blocks_after(&block_1.hash(), &default_shard()) + .unwrap(); + + assert_eq!(3, blocks_after_1.len()); + assert_eq!(block_2.hash(), blocks_after_1.first().unwrap().hash()); + assert_eq!(block_4.hash(), blocks_after_1.last().unwrap().hash()); + } +} + +#[test] +fn get_blocks_after_returns_empty_vec_if_block_not_found() { + let block_1 = create_signed_block(1, BlockHash::random()); + + let temp_dir = fill_storage_with_blocks(vec![block_1.clone()]); + + { + let updated_sidechain_db = get_storage(temp_dir.path().to_path_buf()); + let block_hash = BlockHash::from_low_u64_be(1); + // Off-chance that random() generates exactly the same hash + assert_ne!(block_1.hash(), block_hash); + + assert_eq!( + updated_sidechain_db.get_blocks_after(&block_hash, &default_shard()).unwrap(), + Vec::new() + ); + } +} + +#[test] +fn get_blocks_returns_none_if_last_is_already_most_recent_block() { + let block_1 = create_signed_block(1, BlockHash::random()); + + let temp_dir = fill_storage_with_blocks(vec![block_1.clone()]); + + { + let updated_sidechain_db = get_storage(temp_dir.path().to_path_buf()); + + assert_eq!( + updated_sidechain_db + .get_blocks_after(&block_1.hash(), &default_shard()) + .unwrap(), + Vec::new() + ); + } +} + +#[test] +fn get_blocks_after_returns_all_blocks_if_last_known_is_default() { + let block_1 = create_signed_block(1, BlockHash::default()); + let block_2 = create_signed_block(2, block_1.hash()); + let block_3 = create_signed_block(3, block_2.hash()); + + let blocks = vec![block_1.clone(), block_2.clone(), block_3.clone()]; + + let temp_dir = fill_storage_with_blocks(blocks.clone()); + + { + let updated_sidechain_db = get_storage(temp_dir.path().to_path_buf()); + let default_hash = BlockHash::default(); + + assert_eq!( + updated_sidechain_db.get_blocks_after(&default_hash, &default_shard()).unwrap(), + blocks + ); + } +} + +#[test] +fn given_block_with_invalid_ancestry_returns_error() { + let block_1 = create_signed_block(1, BlockHash::default()); + // Should be block_1 hash, but we deliberately introduce an invalid parent hash. + let block_2 = create_signed_block(2, BlockHash::random()); + + let temp_dir = fill_storage_with_blocks(vec![block_1.clone(), block_2]); + + { + let updated_sidechain_db = get_storage(temp_dir.path().to_path_buf()); + + assert_matches!( + updated_sidechain_db.get_blocks_after(&block_1.hash(), &default_shard()), + Err(Error::FailedToFindParentBlock) + ); + } +} diff --git a/bitacross-worker/sidechain/storage/src/storage_tests_get_blocks_in_range.rs b/bitacross-worker/sidechain/storage/src/storage_tests_get_blocks_in_range.rs new file mode 100644 index 0000000000..c0c505d4d0 --- /dev/null +++ b/bitacross-worker/sidechain/storage/src/storage_tests_get_blocks_in_range.rs @@ -0,0 +1,104 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::test_utils::{ + create_signed_block_with_parenthash as create_signed_block, default_shard, + fill_storage_with_blocks, get_storage, +}; +use itp_types::BlockHash; +use its_primitives::traits::SignedBlock; + +#[test] +fn get_blocks_in_range_works_for_regular_case() { + let block_1 = create_signed_block(1, BlockHash::default()); + let block_2 = create_signed_block(2, block_1.hash()); + let block_3 = create_signed_block(3, block_2.hash()); + let block_4 = create_signed_block(4, block_3.hash()); + let block_5 = create_signed_block(5, block_4.hash()); + + let temp_dir = fill_storage_with_blocks(vec![ + block_1.clone(), + block_2.clone(), + block_3, + block_4.clone(), + block_5.clone(), + ]); + + { + let updated_sidechain_db = get_storage(temp_dir.path().to_path_buf()); + let blocks_2_to_4 = updated_sidechain_db + .get_blocks_in_range(&block_1.hash(), &block_5.hash(), &default_shard()) + .unwrap(); + + assert_eq!(3, blocks_2_to_4.len()); + assert_eq!(block_2.hash(), blocks_2_to_4.first().unwrap().hash()); + assert_eq!(block_4.hash(), blocks_2_to_4.last().unwrap().hash()); + } +} + +#[test] +fn get_blocks_in_range_returns_empty_vec_if_from_is_invalid() { + let block_1 = create_signed_block(1, BlockHash::default()); + let block_2 = create_signed_block(2, block_1.hash()); + let block_3 = create_signed_block(3, block_2.hash()); + let block_4 = create_signed_block(4, block_3.hash()); + + let temp_dir = fill_storage_with_blocks(vec![ + block_1.clone(), + block_2.clone(), + block_3.clone(), + block_4.clone(), + ]); + + { + let updated_sidechain_db = get_storage(temp_dir.path().to_path_buf()); + let invalid_block_hash = BlockHash::from_low_u64_be(1); + + assert!(updated_sidechain_db + .get_blocks_in_range(&invalid_block_hash, &block_3.hash(), &default_shard()) + .unwrap() + .is_empty()); + } +} + +#[test] +fn get_blocks_in_range_returns_all_blocks_if_upper_bound_is_invalid() { + let block_1 = create_signed_block(1, BlockHash::default()); + let block_2 = create_signed_block(2, block_1.hash()); + let block_3 = create_signed_block(3, block_2.hash()); + let block_4 = create_signed_block(4, block_3.hash()); + let block_5 = create_signed_block(5, block_4.hash()); + + let temp_dir = fill_storage_with_blocks(vec![ + block_1.clone(), + block_2.clone(), + block_3.clone(), + block_4.clone(), + block_5.clone(), + ]); + + { + let updated_sidechain_db = get_storage(temp_dir.path().to_path_buf()); + let blocks_in_range = updated_sidechain_db + .get_blocks_in_range(&block_2.hash(), &BlockHash::from_low_u64_be(1), &default_shard()) + .unwrap(); + + assert_eq!(3, blocks_in_range.len()); + assert_eq!(block_3.hash(), blocks_in_range.first().unwrap().hash()); + assert_eq!(block_5.hash(), blocks_in_range.last().unwrap().hash()); + } +} diff --git a/bitacross-worker/sidechain/storage/src/test_utils.rs b/bitacross-worker/sidechain/storage/src/test_utils.rs new file mode 100644 index 0000000000..c0c7d8fdd8 --- /dev/null +++ b/bitacross-worker/sidechain/storage/src/test_utils.rs @@ -0,0 +1,96 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::storage::SidechainStorage; +use itp_time_utils::now_as_millis; +use itp_types::ShardIdentifier; +use its_primitives::types::{BlockHash, SignedBlock as SignedSidechainBlock}; +use its_test::{ + sidechain_block_builder::{SidechainBlockBuilder, SidechainBlockBuilderTrait}, + sidechain_block_data_builder::SidechainBlockDataBuilder, + sidechain_header_builder::SidechainHeaderBuilder, +}; +use sp_core::{crypto::Pair, ed25519, H256}; +use std::{path::PathBuf, vec::Vec}; +use temp_dir::TempDir; + +pub fn fill_storage_with_blocks(blocks: Vec) -> TempDir { + let dir = create_temp_dir(); + let mut sidechain_db = get_storage(dir.path().to_path_buf()); + sidechain_db.store_blocks(blocks).unwrap(); + dir +} + +pub fn create_temp_dir() -> TempDir { + TempDir::new().unwrap() +} + +pub fn get_storage(path: PathBuf) -> SidechainStorage { + SidechainStorage::::load_from_base_path(path).unwrap() +} + +pub fn default_shard() -> ShardIdentifier { + ShardIdentifier::default() +} + +pub fn create_signed_block_with_parenthash( + block_number: u64, + parent_hash: BlockHash, +) -> SignedSidechainBlock { + let header = default_header_builder() + .with_parent_hash(parent_hash) + .with_block_number(block_number) + .build(); + + let block_data = default_block_data_builder().build(); + + SidechainBlockBuilder::default() + .with_header(header) + .with_block_data(block_data) + .build_signed() +} + +pub fn create_signed_block_with_shard( + block_number: u64, + shard: ShardIdentifier, +) -> SignedSidechainBlock { + let header = default_header_builder() + .with_shard(shard) + .with_block_number(block_number) + .build(); + + let block_data = default_block_data_builder().build(); + + SidechainBlockBuilder::default() + .with_header(header) + .with_block_data(block_data) + .build_signed() +} + +fn default_header_builder() -> SidechainHeaderBuilder { + SidechainHeaderBuilder::default() + .with_parent_hash(H256::random()) + .with_block_number(Default::default()) + .with_shard(default_shard()) +} + +fn default_block_data_builder() -> SidechainBlockDataBuilder { + SidechainBlockDataBuilder::default() + .with_timestamp(now_as_millis()) + .with_layer_one_head(H256::random()) + .with_signer(ed25519::Pair::from_string("//Alice", None).unwrap()) +} diff --git a/bitacross-worker/sidechain/test/Cargo.toml b/bitacross-worker/sidechain/test/Cargo.toml new file mode 100644 index 0000000000..d80a6a825b --- /dev/null +++ b/bitacross-worker/sidechain/test/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "its-test" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +homepage = 'https://litentry.com/' +repository = 'https://github.com/litentry/litentry-parachain' +license = "Apache-2.0" +edition = "2021" + +[dependencies] + +# sgx dependencies +sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", features = ["untrusted_time"], optional = true } + +# Substrate dependencies +sp-core = { default_features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# local +itp-types = { path = "../../core-primitives/types", default_features = false } +its-primitives = { path = "../primitives", default_features = false, features = ["full_crypto"] } + +[features] +default = ["std"] +std = [ + "itp-types/std", + "its-primitives/std", + # substrate + "sp-core/std", +] +sgx = [ + "sgx_tstd", +] diff --git a/bitacross-worker/sidechain/test/src/lib.rs b/bitacross-worker/sidechain/test/src/lib.rs new file mode 100644 index 0000000000..e9164d6d8b --- /dev/null +++ b/bitacross-worker/sidechain/test/src/lib.rs @@ -0,0 +1,29 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#![feature(trait_alias)] +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "std", feature = "sgx"))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); + +#[cfg(all(not(feature = "std"), not(feature = "sgx")))] +compile_error!("feature \"std\" and feature \"sgx\" cannot be disabled at the same time"); + +#[cfg(all(not(feature = "std"), feature = "sgx"))] +#[macro_use] +extern crate sgx_tstd as std; + +pub mod sidechain_block_builder; +pub mod sidechain_block_data_builder; +pub mod sidechain_header_builder; diff --git a/bitacross-worker/sidechain/test/src/sidechain_block_builder.rs b/bitacross-worker/sidechain/test/src/sidechain_block_builder.rs new file mode 100644 index 0000000000..1261cf51bc --- /dev/null +++ b/bitacross-worker/sidechain/test/src/sidechain_block_builder.rs @@ -0,0 +1,105 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Builder pattern for a signed sidechain block. + +use crate::{ + sidechain_block_data_builder::SidechainBlockDataBuilder, + sidechain_header_builder::SidechainHeaderBuilder, +}; +use its_primitives::{ + traits::{Block as BlockT, SignBlock}, + types::{block_data::BlockData, header::SidechainHeader as Header, Block, SignedBlock}, +}; +use sp_core::{ed25519, Pair}; + +type Seed = [u8; 32]; +const ENCLAVE_SEED: Seed = *b"12345678901234567890123456789012"; + +#[derive(Clone)] +pub struct SidechainBlockBuilder { + signer: ed25519::Pair, + header: Header, + block_data: BlockData, +} + +impl Default for SidechainBlockBuilder { + fn default() -> Self { + SidechainBlockBuilder { + signer: Pair::from_seed(&ENCLAVE_SEED), + header: SidechainHeaderBuilder::default().build(), + block_data: SidechainBlockDataBuilder::default().build(), + } + } +} + +pub trait SidechainBlockBuilderTrait { + type Block: BlockT; + fn random() -> Self; + fn with_header(self, header: Header) -> Self; + fn with_block_data(self, block_data: BlockData) -> Self; + fn with_signer(self, signer: ed25519::Pair) -> Self; + fn build(&self) -> Self::Block; + fn build_signed(&self) -> SignedBlock; +} + +impl SidechainBlockBuilderTrait for SidechainBlockBuilder { + type Block = Block; + fn random() -> Self { + SidechainBlockBuilder { + signer: Pair::from_seed(&ENCLAVE_SEED), + header: SidechainHeaderBuilder::random().build(), + block_data: SidechainBlockDataBuilder::random().build(), + } + } + + fn with_header(self, header: Header) -> Self { + let mut self_mut = self; + self_mut.header = header; + self_mut + } + + fn with_block_data(self, block_data: BlockData) -> Self { + let mut self_mut = self; + self_mut.block_data = block_data; + self_mut + } + + fn with_signer(self, signer: ed25519::Pair) -> Self { + let mut self_mut = self; + self_mut.signer = signer; + self_mut + } + + fn build(&self) -> Self::Block { + Block { header: self.header, block_data: self.block_data.clone() } + } + + fn build_signed(&self) -> SignedBlock { + let signer = self.signer; + self.build().sign_block(&signer) + } +} + +#[test] +fn build_signed_block_has_valid_signature() { + use its_primitives::traits::SignedBlock as SignedBlockTrait; + + let signed_block = SidechainBlockBuilder::default().build_signed(); + assert!(signed_block.verify_signature()); +} diff --git a/bitacross-worker/sidechain/test/src/sidechain_block_data_builder.rs b/bitacross-worker/sidechain/test/src/sidechain_block_data_builder.rs new file mode 100644 index 0000000000..e1197ce65c --- /dev/null +++ b/bitacross-worker/sidechain/test/src/sidechain_block_data_builder.rs @@ -0,0 +1,102 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Builder pattern for sidechain block data. + +use itp_types::H256; +use its_primitives::types::{ + block::{BlockHash, Timestamp}, + block_data::BlockData, +}; +use sp_core::{ed25519, Pair}; +use std::{time::SystemTime, vec}; + +type Seed = [u8; 32]; +const ENCLAVE_SEED: Seed = *b"12345678901234567890123456789012"; + +pub struct SidechainBlockDataBuilder { + timestamp: Timestamp, + layer_one_head: H256, + signer: ed25519::Pair, + signed_top_hashes: Vec, + encrypted_state_diff: Vec, +} + +impl Default for SidechainBlockDataBuilder { + fn default() -> Self { + SidechainBlockDataBuilder { + timestamp: Default::default(), + layer_one_head: Default::default(), + signer: Pair::from_seed(&ENCLAVE_SEED), + signed_top_hashes: Default::default(), + encrypted_state_diff: Default::default(), + } + } +} + +impl SidechainBlockDataBuilder { + pub fn random() -> Self { + SidechainBlockDataBuilder { + timestamp: now_as_millis(), + layer_one_head: BlockHash::random(), + signer: Pair::from_seed(&ENCLAVE_SEED), + signed_top_hashes: vec![H256::random(), H256::random()], + encrypted_state_diff: vec![1, 3, 42, 8, 11, 33], + } + } + + pub fn with_timestamp(mut self, timestamp: Timestamp) -> Self { + self.timestamp = timestamp; + self + } + + pub fn with_signer(mut self, signer: ed25519::Pair) -> Self { + self.signer = signer; + self + } + + pub fn with_layer_one_head(mut self, layer_one_head: H256) -> Self { + self.layer_one_head = layer_one_head; + self + } + + pub fn with_signed_top_hashes(mut self, signed_top_hashes: Vec) -> Self { + self.signed_top_hashes = signed_top_hashes; + self + } + + pub fn with_payload(mut self, payload: Vec) -> Self { + self.encrypted_state_diff = payload; + self + } + + pub fn build(self) -> BlockData { + BlockData { + timestamp: self.timestamp, + block_author: self.signer.public(), + layer_one_head: self.layer_one_head, + signed_top_hashes: self.signed_top_hashes, + encrypted_state_diff: self.encrypted_state_diff, + } + } +} + +/// gets the timestamp of the block as seconds since unix epoch +fn now_as_millis() -> u64 { + SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_millis() as u64 +} diff --git a/bitacross-worker/sidechain/test/src/sidechain_header_builder.rs b/bitacross-worker/sidechain/test/src/sidechain_header_builder.rs new file mode 100644 index 0000000000..fca8b52b8c --- /dev/null +++ b/bitacross-worker/sidechain/test/src/sidechain_header_builder.rs @@ -0,0 +1,92 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +//! Builder pattern for a sidechain header. + +use its_primitives::types::{header::SidechainHeader as Header, ShardIdentifier}; +use sp_core::H256; + +pub struct SidechainHeaderBuilder { + parent_hash: H256, + block_number: u64, + shard_id: ShardIdentifier, + block_data_hash: H256, + next_finalization_block_number: u64, +} + +impl Default for SidechainHeaderBuilder { + fn default() -> Self { + SidechainHeaderBuilder { + parent_hash: Default::default(), + block_number: 1, + shard_id: Default::default(), + block_data_hash: Default::default(), + next_finalization_block_number: 1, + } + } +} + +impl SidechainHeaderBuilder { + pub fn random() -> Self { + SidechainHeaderBuilder { + parent_hash: H256::random(), + block_number: 42, + shard_id: ShardIdentifier::random(), + block_data_hash: H256::random(), + next_finalization_block_number: 1, + } + } + + pub fn with_parent_hash(mut self, parent_hash: H256) -> Self { + self.parent_hash = parent_hash; + self + } + + pub fn with_block_number(mut self, block_number: u64) -> Self { + self.block_number = block_number; + self + } + + pub fn with_shard(mut self, shard_id: ShardIdentifier) -> Self { + self.shard_id = shard_id; + self + } + + pub fn with_block_data_hash(mut self, block_data_hash: H256) -> Self { + self.block_data_hash = block_data_hash; + self + } + + pub fn with_next_finalization_block_number( + mut self, + next_finalization_block_number: u64, + ) -> Self { + self.next_finalization_block_number = next_finalization_block_number; + self + } + + pub fn build(self) -> Header { + Header { + parent_hash: self.parent_hash, + block_number: self.block_number, + shard_id: self.shard_id, + block_data_hash: self.block_data_hash, + next_finalization_block_number: self.next_finalization_block_number, + } + } +} diff --git a/bitacross-worker/sidechain/validateer-fetch/Cargo.toml b/bitacross-worker/sidechain/validateer-fetch/Cargo.toml new file mode 100644 index 0000000000..7aca2dbf7a --- /dev/null +++ b/bitacross-worker/sidechain/validateer-fetch/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "its-validateer-fetch" +version = "0.9.0" +authors = ['Trust Computing GmbH ', 'Integritee AG '] +edition = "2021" + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "chain-error"] } +derive_more = "0.99.16" + +# substrate deps +sp-core = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-runtime = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } +sp-std = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +# local deps +itp-ocall-api = { path = "../../core-primitives/ocall-api", default-features = false } +itp-teerex-storage = { path = "../../core-primitives/teerex-storage", default-features = false } +itp-types = { path = "../../core-primitives/types", default-features = false } + +# litentry +frame-support = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.42" } + +[features] +default = ["std"] +std = [ + "codec/std", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", + "itp-types/std", + "itp-ocall-api/std", + "frame-support/std", +] + +[dev-dependencies] +itp-test = { path = "../../core-primitives/test" } +itc-parentchain-test = { path = "../../core/parentchain/test" } diff --git a/bitacross-worker/sidechain/validateer-fetch/src/error.rs b/bitacross-worker/sidechain/validateer-fetch/src/error.rs new file mode 100644 index 0000000000..3b5d4a3f2c --- /dev/null +++ b/bitacross-worker/sidechain/validateer-fetch/src/error.rs @@ -0,0 +1,27 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use derive_more::{Display, From}; + +pub type Result = core::result::Result; + +#[derive(Debug, Display, From)] +pub enum Error { + Codec(codec::Error), + Onchain(itp_ocall_api::Error), + Other(&'static str), +} diff --git a/bitacross-worker/sidechain/validateer-fetch/src/lib.rs b/bitacross-worker/sidechain/validateer-fetch/src/lib.rs new file mode 100644 index 0000000000..8c68402101 --- /dev/null +++ b/bitacross-worker/sidechain/validateer-fetch/src/lib.rs @@ -0,0 +1,24 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#![cfg_attr(not(feature = "std"), no_std)] + +mod error; +mod validateer; + +pub use error::Error; +pub use validateer::*; diff --git a/bitacross-worker/sidechain/validateer-fetch/src/validateer.rs b/bitacross-worker/sidechain/validateer-fetch/src/validateer.rs new file mode 100644 index 0000000000..4af8d86274 --- /dev/null +++ b/bitacross-worker/sidechain/validateer-fetch/src/validateer.rs @@ -0,0 +1,104 @@ +/* + Copyright 2021 Integritee AG and Supercomputing Systems AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +use crate::error::{Error, Result}; +use frame_support::ensure; +use itp_ocall_api::EnclaveOnChainOCallApi; +use itp_teerex_storage::{TeeRexStorage, TeerexStorageKeys}; +use itp_types::{parentchain::ParentchainId, Enclave}; +use sp_core::H256; +use sp_runtime::traits::Header as HeaderT; +use sp_std::prelude::Vec; + +pub trait ValidateerFetch { + fn current_validateers>( + &self, + latest_header: &Header, + ) -> Result>; + fn validateer_count>(&self, latest_header: &Header) + -> Result; +} + +impl ValidateerFetch for OnchainStorage { + fn current_validateers>( + &self, + header: &Header, + ) -> Result> { + let count = self.validateer_count(header)?; + + let mut hashes = Vec::with_capacity(count as usize); + for i in 1..=count { + hashes.push(TeeRexStorage::enclave(i)) + } + + let enclaves: Vec = self + .get_multiple_storages_verified(hashes, header, &ParentchainId::Litentry)? + .into_iter() + .filter_map(|e| e.into_tuple().1) + .collect(); + ensure!( + enclaves.len() == count as usize, + Error::Other("Found less validateers onchain than validateer count") + ); + Ok(enclaves) + } + + fn validateer_count>(&self, header: &Header) -> Result { + self.get_storage_verified(TeeRexStorage::enclave_count(), header, &ParentchainId::Litentry)? + .into_tuple() + .1 + .ok_or_else(|| Error::Other("Could not get validateer count from chain")) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use codec::Encode; + use itc_parentchain_test::ParentchainHeaderBuilder; + use itp_test::mock::onchain_mock::{validateer_set, OnchainMock}; + use std::string::ToString; + + #[test] + pub fn get_validateer_count_works() { + let header = ParentchainHeaderBuilder::default().build(); + let mock = OnchainMock::default().add_validateer_set(&header, None); + assert_eq!(mock.validateer_count(&header).unwrap(), 4u64); + } + + #[test] + pub fn get_validateer_set_works() { + let header = ParentchainHeaderBuilder::default().build(); + let mock = OnchainMock::default().add_validateer_set(&header, None); + + let validateers = validateer_set(); + + assert_eq!(mock.current_validateers(&header).unwrap(), validateers); + } + + #[test] + pub fn if_validateer_count_bigger_than_returned_validateers_return_err() { + let header = ParentchainHeaderBuilder::default().build(); + let mut mock = OnchainMock::default().add_validateer_set(&header, None); + mock.insert_at_header(&header, TeeRexStorage::enclave_count(), 5u64.encode()); + + assert_eq!( + mock.current_validateers(&header).unwrap_err().to_string(), + "Found less validateers onchain than validateer count".to_string() + ); + } +} diff --git a/bitacross-worker/ts-tests/.editorconfig b/bitacross-worker/ts-tests/.editorconfig new file mode 100644 index 0000000000..347fc689b2 --- /dev/null +++ b/bitacross-worker/ts-tests/.editorconfig @@ -0,0 +1,6 @@ +# Editor configuration, see http://editorconfig.org + +[*] +indent_style = space +indent_size = 4 + diff --git a/bitacross-worker/ts-tests/.gitignore b/bitacross-worker/ts-tests/.gitignore new file mode 100644 index 0000000000..3a8fe5ede8 --- /dev/null +++ b/bitacross-worker/ts-tests/.gitignore @@ -0,0 +1 @@ +.env.local \ No newline at end of file diff --git a/bitacross-worker/ts-tests/.prettierrc b/bitacross-worker/ts-tests/.prettierrc new file mode 100644 index 0000000000..b65f49a91b --- /dev/null +++ b/bitacross-worker/ts-tests/.prettierrc @@ -0,0 +1,7 @@ +{ + "trailingComma": "es5", + "singleQuote": true, + "printWidth": 120, + "tabWidth": 4, + "semi": true +} diff --git a/bitacross-worker/ts-tests/README.md b/bitacross-worker/ts-tests/README.md new file mode 100644 index 0000000000..fbf3cafc34 --- /dev/null +++ b/bitacross-worker/ts-tests/README.md @@ -0,0 +1,23 @@ +## Description + +ts-tests of bitacross-worker + +## Environment setup + +- Install [nvm](https://github.com/nvm-sh/nvm) +- Inside the repository, run `nvm use` to set the correct Node version. + - If the version is not installed, run `nvm install`. + +## Prerequisite + +Before running the ts-tests, the client-api types generation needs to be completed. + +See client-api [README.md](https://github.com/litentry/litentry-parachain/blob/dev/tee-worker/client-api/README.md) + +## Installation + +``` +nvm use +corepack enable pnpm +pnpm install +``` \ No newline at end of file diff --git a/bitacross-worker/ts-tests/package.json b/bitacross-worker/ts-tests/package.json new file mode 100644 index 0000000000..346af2afda --- /dev/null +++ b/bitacross-worker/ts-tests/package.json @@ -0,0 +1,9 @@ +{ + "type": "module", + "license": "ISC", + "scripts": { + "format": "pnpm run --recursive format", + "check-format": "pnpm run --recursive check-format" + }, + "packageManager": "pnpm@8.7.6" +} diff --git a/bitacross-worker/ts-tests/pnpm-lock.yaml b/bitacross-worker/ts-tests/pnpm-lock.yaml new file mode 100644 index 0000000000..797ec499ee --- /dev/null +++ b/bitacross-worker/ts-tests/pnpm-lock.yaml @@ -0,0 +1,9 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: {} diff --git a/bitacross-worker/ts-tests/pnpm-workspace.yaml b/bitacross-worker/ts-tests/pnpm-workspace.yaml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bitacross-worker/upstream_commit b/bitacross-worker/upstream_commit new file mode 100644 index 0000000000..6faa76f0b6 --- /dev/null +++ b/bitacross-worker/upstream_commit @@ -0,0 +1 @@ +e40355f8 diff --git a/scripts/pre-commit.sh b/scripts/pre-commit.sh index f55b8fbebc..beec0ab4a9 100755 --- a/scripts/pre-commit.sh +++ b/scripts/pre-commit.sh @@ -44,22 +44,39 @@ make fmt echo "[Step 1], Parachain clippy" cd "$root_dir" && parachain_check -echo "[Step 2], Worker clippy" +echo "[Step 2], tee-worker clippy" cd "$root_dir/tee-worker" && worker_clippy -echo "[Step 3], Enclave clippy" +echo "[Step 3], tee-worker enclave clippy" cd "$root_dir/tee-worker/enclave-runtime" && worker_clippy -echo "[Step 4], Worker cargo test" +echo "[Step 4], tee-worker cargo test" cd "$root_dir/tee-worker" RUST_LOG=info SKIP_WASM_BUILD=1 cargo test --release -- --show-output -echo "[Step 5], Service test" +echo "[Step 5], tee-worker service test" clean_up cd "$root_dir/tee-worker" SGX_MODE=SW SKIP_WASM_BUILD=1 make cd "$root_dir/tee-worker/bin" ./litentry-worker test --all +echo "[Step 6], bitacross-worker clippy" +cd "$root_dir/bitacross-worker" && worker_clippy + +echo "[Step 7], bitacross-worker enclave clippy" +cd "$root_dir/bitacross-worker/enclave-runtime" && worker_clippy + +echo "[Step 8], bitacross-worker cargo test" +cd "$root_dir/bitacross-worker" +RUST_LOG=info SKIP_WASM_BUILD=1 cargo test --release -- --show-output + +echo "[Step 9], bitacross-worker service test" +clean_up +cd "$root_dir/bitacross-worker" +SGX_MODE=SW SKIP_WASM_BUILD=1 make +cd "$root_dir/bitacross-worker/bin" +./bitacross-worker test --all + end=$(date +%s) echo "Elapsed Time: $((end-start)) seconds" From 3571560d61a91f50de87fa0e8960a209f931ce9e Mon Sep 17 00:00:00 2001 From: Igor Trofimov Date: Tue, 30 Jan 2024 12:29:40 +0200 Subject: [PATCH 44/51] Worker Docker release build without using cache (#2423) * omit using cache for docker build --- .github/workflows/create-release-draft.yml | 10 ++++++---- tee-worker/build.Dockerfile | 18 +++++++++++++----- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/.github/workflows/create-release-draft.yml b/.github/workflows/create-release-draft.yml index 52d5890518..d6f4075f01 100644 --- a/.github/workflows/create-release-draft.yml +++ b/.github/workflows/create-release-draft.yml @@ -176,10 +176,6 @@ jobs: with: ref: ${{ env.RELEASE_TAG }} fetch-depth: 0 - - name: Set env - run: | - WORKER_DOCKER_TAG=$(echo ${{ env.RELEASE_TAG }} | sed 's/.*\(w.*\)/\1/;s/w/v/') - echo "WORKER_DOCKER_TAG=$WORKER_DOCKER_TAG" >> $GITHUB_ENV - name: Free up disk space if: startsWith(runner.name, 'GitHub Actions') @@ -197,6 +193,11 @@ jobs: # see https://docs.docker.com/build/drivers/ driver: docker + - name: Set env + run: | + WORKER_DOCKER_TAG=$(echo ${{ env.RELEASE_TAG }} | sed 's/.*\(w.*\)/\1/;s/w/v/') + echo "WORKER_DOCKER_TAG=$WORKER_DOCKER_TAG" >> $GITHUB_ENV + - name: Build local builder uses: docker/build-push-action@v5 with: @@ -207,6 +208,7 @@ jobs: build-args: | WORKER_MODE_ARG=sidechain ADDITIONAL_FEATURES_ARG= + IMAGE_FOR_RELEASE=true - name: Build worker uses: docker/build-push-action@v5 diff --git a/tee-worker/build.Dockerfile b/tee-worker/build.Dockerfile index fca84ba6eb..1e187e85ad 100644 --- a/tee-worker/build.Dockerfile +++ b/tee-worker/build.Dockerfile @@ -47,17 +47,25 @@ ENV WORKER_MODE=$WORKER_MODE_ARG ARG ADDITIONAL_FEATURES_ARG ENV ADDITIONAL_FEATURES=$ADDITIONAL_FEATURES_ARG +ARG IMAGE_FOR_RELEASE=false +ENV IMAGE_FOR_RELEASE=$IMAGE_FOR_RELEASE + ARG FINGERPRINT=none WORKDIR $HOME/tee-worker COPY . $HOME RUN \ - rm -rf /opt/rust/registry/cache && mv /home/ubuntu/worker-cache/registry/cache /opt/rust/registry && \ - rm -rf /opt/rust/registry/index && mv /home/ubuntu/worker-cache/registry/index /opt/rust/registry && \ - rm -rf /opt/rust/git/db && mv /home/ubuntu/worker-cache/git/db /opt/rust/git && \ - rm -rf /opt/rust/sccache && mv /home/ubuntu/worker-cache/sccache /opt/rust && \ - make && sccache --show-stats + if [ "$IMAGE_FOR_RELEASE" = "true" ]; then \ + echo "Omit cache for release image"; \ + make; \ + else \ + rm -rf /opt/rust/registry/cache && mv /home/ubuntu/worker-cache/registry/cache /opt/rust/registry && \ + rm -rf /opt/rust/registry/index && mv /home/ubuntu/worker-cache/registry/index /opt/rust/registry && \ + rm -rf /opt/rust/git/db && mv /home/ubuntu/worker-cache/git/db /opt/rust/git && \ + rm -rf /opt/rust/sccache && mv /home/ubuntu/worker-cache/sccache /opt/rust && \ + make && sccache --show-stats; \ + fi RUN cargo test --release From d275492da2622b394826a8117a3bd77bb724f2a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Jan 2024 11:54:02 +0100 Subject: [PATCH 45/51] Bump chrono from 0.4.31 to 0.4.33 (#2443) Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.31 to 0.4.33. - [Release notes](https://github.com/chronotope/chrono/releases) - [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md) - [Commits](https://github.com/chronotope/chrono/compare/v0.4.31...v0.4.33) --- updated-dependencies: - dependency-name: chrono dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index de4b8b49da..c721281ff8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -961,9 +961,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb" dependencies = [ "android-tzdata", "iana-time-zone", @@ -971,7 +971,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.48.1", + "windows-targets 0.52.0", ] [[package]] @@ -15491,6 +15491,21 @@ dependencies = [ "windows_x86_64_msvc 0.48.0", ] +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -15503,6 +15518,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" version = "0.34.0" @@ -15521,6 +15542,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[package]] name = "windows_i686_gnu" version = "0.34.0" @@ -15539,6 +15566,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[package]] name = "windows_i686_msvc" version = "0.34.0" @@ -15557,6 +15590,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[package]] name = "windows_x86_64_gnu" version = "0.34.0" @@ -15575,6 +15614,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -15587,6 +15632,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.34.0" @@ -15605,6 +15656,12 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + [[package]] name = "winnow" version = "0.5.1" From 080a45738b86b1763934305b38038003e2d480b1 Mon Sep 17 00:00:00 2001 From: Kai <7630809+Kailai-Wang@users.noreply.github.com> Date: Tue, 30 Jan 2024 16:38:44 +0100 Subject: [PATCH 46/51] Update rococo bootnode (#2448) --- node/res/genesis_info/rococo.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/res/genesis_info/rococo.json b/node/res/genesis_info/rococo.json index e5f07f00f6..f08fa0ea1a 100644 --- a/node/res/genesis_info/rococo.json +++ b/node/res/genesis_info/rococo.json @@ -38,7 +38,7 @@ "jcSQcu5RXtWXKPN78zJogNyqsff1j4ffgoJ2njNcDbZ3qNEh5" ], "bootNodes": [ - "/dns/rpc.rococo-parachain-sg.litentry.io/tcp/40333/ws/p2p/12D3KooWC9WbcoS2fuaohQXfk3RwXH1hwffEG7iudbJRfNK54hTT" + "/dns/rpc.rococo-parachain.litentry.io/tcp/40333/ws/p2p/12D3KooWD9t5jsf5m2g4acXfyVf2KxR5j11F9SLC5wKhv6JRquyv" ], "telemetryEndpoints": [ "wss://telemetry.polkadot.io/submit/" From 918f879e0b36c249a3f3dca7da10b7e1927099ac Mon Sep 17 00:00:00 2001 From: BillyWooo Date: Tue, 30 Jan 2024 16:48:38 +0100 Subject: [PATCH 47/51] remove unused log target (#2447) --- .../sidechain/consensus/aura/src/lib.rs | 26 ++++---- .../sidechain/consensus/slots/src/lib.rs | 60 ++++--------------- .../sidechain/consensus/slots/src/mocks.rs | 4 -- 3 files changed, 26 insertions(+), 64 deletions(-) diff --git a/tee-worker/sidechain/consensus/aura/src/lib.rs b/tee-worker/sidechain/consensus/aura/src/lib.rs index dbbe099e4e..2ca8b88edb 100644 --- a/tee-worker/sidechain/consensus/aura/src/lib.rs +++ b/tee-worker/sidechain/consensus/aura/src/lib.rs @@ -211,10 +211,6 @@ impl< type ScheduledEnclave = ScheduledEnclave; type StateHandler = StateHandler; - fn logging_target(&self) -> &'static str { - "aura" - } - fn get_scheduled_enclave(&mut self) -> Arc { self.scheduled_enclave.clone() } @@ -246,15 +242,12 @@ impl< let expected_author = slot_author::(slot, epoch_data)?; if expected_author == &self.authority_pair.public() { - log::info!(target: self.logging_target(), "Claiming slot ({})", *slot); + log::info!("Claiming slot ({})", *slot); return Some(self.authority_pair.public()) } if self.claim_strategy == SlotClaimStrategy::Always { - log::debug!( - target: self.logging_target(), - "Not our slot but we still claim it." - ); + log::debug!("Not our slot but we still claim it."); return Some(self.authority_pair.public()) } @@ -281,7 +274,10 @@ impl< &self, parentchain_header_hash: &::Hash, ) -> Result, ConsensusError> { - log::trace!(target: self.logging_target(), "import Integritee blocks until {}", hex_encode(parentchain_header_hash.encode().as_ref())); + log::trace!( + "import Integritee blocks until {}", + hex_encode(parentchain_header_hash.encode().as_ref()) + ); let maybe_parentchain_block = self .parentchain_integritee_import_trigger .import_until(|parentchain_block| { @@ -296,7 +292,10 @@ impl< &self, parentchain_header_hash: &::Hash, ) -> Result, ConsensusError> { - log::trace!(target: self.logging_target(), "import TargetA blocks until {}", hex_encode(parentchain_header_hash.encode().as_ref())); + log::trace!( + "import TargetA blocks until {}", + hex_encode(parentchain_header_hash.encode().as_ref()) + ); let maybe_parentchain_block = self .maybe_parentchain_target_a_import_trigger .clone() @@ -313,7 +312,10 @@ impl< &self, parentchain_header_hash: &::Hash, ) -> Result, ConsensusError> { - log::trace!(target: self.logging_target(), "import TargetB blocks until {}", hex_encode(parentchain_header_hash.encode().as_ref())); + log::trace!( + "import TargetB blocks until {}", + hex_encode(parentchain_header_hash.encode().as_ref()) + ); let maybe_parentchain_block = self .maybe_parentchain_target_b_import_trigger .clone() diff --git a/tee-worker/sidechain/consensus/slots/src/lib.rs b/tee-worker/sidechain/consensus/slots/src/lib.rs index 9c22327580..e09e55b1d6 100644 --- a/tee-worker/sidechain/consensus/slots/src/lib.rs +++ b/tee-worker/sidechain/consensus/slots/src/lib.rs @@ -195,9 +195,6 @@ pub trait SimpleSlotWorker { /// State handler context for authoring type StateHandler: HandleState; - /// The logging target to use when logging messages. - fn logging_target(&self) -> &'static str; - /// Get scheduled enclave fn get_scheduled_enclave(&mut self) -> Arc; @@ -281,15 +278,11 @@ pub trait SimpleSlotWorker { is_single_worker: bool, ) -> Option> { let (_timestamp, slot) = (slot_info.timestamp, slot_info.slot); - let logging_target = self.logging_target(); let remaining_duration = self.proposing_remaining_duration(&slot_info); if remaining_duration == Duration::default() { - debug!( - target: logging_target, - "Skipping proposal slot {} since there's no time left to propose", *slot, - ); + debug!("Skipping proposal slot {} since there's no time left to propose", *slot,); return None } @@ -299,15 +292,11 @@ pub trait SimpleSlotWorker { Ok(Some(peeked_header)) => peeked_header, Ok(None) => slot_info.last_imported_integritee_parentchain_head.clone(), Err(e) => { - warn!( - target: logging_target, - "Failed to peek latest Integritee parentchain block header: {:?}", e - ); + warn!("Failed to peek latest Integritee parentchain block header: {:?}", e); return None }, }; trace!( - target: logging_target, "on_slot: a priori latest Integritee block number: {:?}", latest_integritee_parentchain_header.number() ); @@ -317,15 +306,11 @@ pub trait SimpleSlotWorker { Ok(Some(peeked_header)) => Some(peeked_header), Ok(None) => slot_info.maybe_last_imported_target_a_parentchain_head.clone(), Err(e) => { - debug!( - target: logging_target, - "Failed to peek latest target_a_parentchain block header: {:?}", e - ); + debug!("Failed to peek latest target_a_parentchain block header: {:?}", e); None }, }; trace!( - target: logging_target, "on_slot: a priori latest TargetA block number: {:?}", maybe_latest_target_a_parentchain_header.clone().map(|h| *h.number()) ); @@ -335,15 +320,11 @@ pub trait SimpleSlotWorker { Ok(Some(peeked_header)) => Some(peeked_header), Ok(None) => slot_info.maybe_last_imported_target_b_parentchain_head.clone(), Err(e) => { - debug!( - target: logging_target, - "Failed to peek latest target_a_parentchain block header: {:?}", e - ); + debug!("Failed to peek latest target_a_parentchain block header: {:?}", e); None }, }; trace!( - target: logging_target, "on_slot: a priori latest TargetB block number: {:?}", maybe_latest_target_b_parentchain_header.clone().map(|h| *h.number()) ); @@ -352,7 +333,6 @@ pub trait SimpleSlotWorker { Ok(epoch_data) => epoch_data, Err(e) => { warn!( - target: logging_target, "Unable to fetch epoch data at block {:?}: {:?}", latest_integritee_parentchain_header.hash(), e, @@ -365,10 +345,7 @@ pub trait SimpleSlotWorker { let authorities_len = self.authorities_len(&epoch_data); if !authorities_len.map(|a| a > 0).unwrap_or(false) { - debug!( - target: logging_target, - "Skipping proposal slot. Authorities len {:?}", authorities_len - ); + debug!("Skipping proposal slot. Authorities len {:?}", authorities_len); } // Return early if MRENCLAVE doesn't match - it implies that the enclave should be updated @@ -380,7 +357,6 @@ pub trait SimpleSlotWorker { if !scheduled_enclave.is_mrenclave_matching(next_sidechain_number) { warn!( - target: logging_target, "Skipping sidechain block {} due to mismatch MRENCLAVE, current: {:?}, expect: {:?}", next_sidechain_number, scheduled_enclave.get_current_mrenclave().map(hex::encode), @@ -417,7 +393,6 @@ pub trait SimpleSlotWorker { Ok(h) => h, Err(e) => { debug!( - target: logging_target, "Failed to import Integritee blocks until nr{:?}: {:?}", latest_integritee_parentchain_header.number(), e @@ -426,7 +401,6 @@ pub trait SimpleSlotWorker { }, }; trace!( - target: logging_target, "on_slot: a posteriori latest Integritee block number: {:?}", last_imported_integritee_header.clone().map(|h| *h.number()) ); @@ -438,7 +412,6 @@ pub trait SimpleSlotWorker { Ok(None) => None, Err(e) => { debug!( - target: logging_target, "Failed to import TargetA blocks until nr{:?}: {:?}", header.number(), e @@ -450,7 +423,6 @@ pub trait SimpleSlotWorker { None }; trace!( - target: logging_target, "on_slot: a posteriori latest TargetA block number: {:?}", maybe_last_imported_target_a_header.map(|h| *h.number()) ); @@ -462,7 +434,6 @@ pub trait SimpleSlotWorker { Ok(None) => None, Err(e) => { debug!( - target: logging_target, "Failed to import TargetB blocks until nr{:?}: {:?}", header.number(), e @@ -475,7 +446,6 @@ pub trait SimpleSlotWorker { }; trace!( - target: logging_target, "on_slot: a posteriori latest TargetB block number: {:?}", maybe_last_imported_target_b_header.map(|h| *h.number()) ); @@ -483,7 +453,7 @@ pub trait SimpleSlotWorker { let proposer = match self.proposer(latest_integritee_parentchain_header.clone(), shard) { Ok(p) => p, Err(e) => { - warn!(target: logging_target, "Could not create proposer: {:?}", e); + warn!("Could not create proposer: {:?}", e); return None }, }; @@ -491,7 +461,7 @@ pub trait SimpleSlotWorker { let proposing = match proposer.propose(remaining_duration) { Ok(p) => p, Err(e) => { - warn!(target: logging_target, "Could not propose: {:?}", e); + warn!("Could not propose: {:?}", e); return None }, }; @@ -500,7 +470,6 @@ pub trait SimpleSlotWorker { error!("Running as single worker, skipping timestamp within slot check") } else if !timestamp_within_slot(&slot_info, &proposing.block) { warn!( - target: logging_target, "⌛️ Discarding proposal for slot {}, block number {}; block production took too long", *slot, proposing.block.block().header().block_number(), ); @@ -558,8 +527,6 @@ impl, is_single_worker: bool, ) -> Self::Output { - let logging_target = SimpleSlotWorker::logging_target(self); - let mut remaining_shards = shards.len(); let mut slot_results = Vec::with_capacity(remaining_shards); @@ -572,10 +539,7 @@ impl { slot_results.push(res); debug!( - target: logging_target, - "on_slot: produced block for slot: {:?} in shard {:?}", shard_slot, shard + "on_slot: produced block for slot: {:?} in shard {:?}", + shard_slot, shard ) }, None => info!( - target: logging_target, - "Did not propose a block for slot {} in shard {:?}", *slot_info.slot, shard + "Did not propose a block for slot {} in shard {:?}", + *slot_info.slot, shard ), } diff --git a/tee-worker/sidechain/consensus/slots/src/mocks.rs b/tee-worker/sidechain/consensus/slots/src/mocks.rs index 409fb41987..c84467640d 100644 --- a/tee-worker/sidechain/consensus/slots/src/mocks.rs +++ b/tee-worker/sidechain/consensus/slots/src/mocks.rs @@ -62,10 +62,6 @@ where type StateHandler = HandleStateMock; - fn logging_target(&self) -> &'static str { - "test" - } - fn get_scheduled_enclave(&mut self) -> Arc { todo!() } From 0f2935d239d67b1f904d58d08293c2ff1d513f26 Mon Sep 17 00:00:00 2001 From: Kai <7630809+Kailai-Wang@users.noreply.github.com> Date: Tue, 30 Jan 2024 18:49:27 +0100 Subject: [PATCH 48/51] Update some cargo deps (#2449) * Update some cargo deps * revert changes --- Cargo.lock | 16 +++++++-------- bitacross-worker/litentry/macros/Cargo.toml | 2 +- .../litentry/primitives/Cargo.toml | 4 ++-- bitacross-worker/service/src/main_impl.rs | 2 ++ primitives/core/Cargo.toml | 4 ++-- primitives/core/proc-macros/Cargo.toml | 2 +- tee-worker/Cargo.lock | 20 +++++++++---------- tee-worker/enclave-runtime/Cargo.lock | 12 +++++------ tee-worker/litentry/primitives/Cargo.toml | 4 ++-- 9 files changed, 34 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c721281ff8..a548deca91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -877,9 +877,9 @@ dependencies = [ [[package]] name = "cargo_toml" -version = "0.16.3" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3f9629bc6c4388ea699781dc988c2b99766d7679b151c81990b4fa1208fafd3" +checksum = "3dc9f7a067415ab5058020f04c60ec7b557084dbec0e021217bbabc7a8d38d14" dependencies = [ "serde", "toml 0.8.2", @@ -1208,8 +1208,8 @@ dependencies = [ "sp-io", "sp-runtime", "sp-std", - "strum 0.25.0", - "strum_macros 0.25.3", + "strum 0.26.1", + "strum_macros 0.26.1", ] [[package]] @@ -13493,9 +13493,9 @@ dependencies = [ [[package]] name = "strum" -version = "0.25.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +checksum = "723b93e8addf9aa965ebe2d11da6d7540fa2283fcea14b3371ff055f7ba13f5f" [[package]] name = "strum_macros" @@ -13512,9 +13512,9 @@ dependencies = [ [[package]] name = "strum_macros" -version = "0.25.3" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +checksum = "7a3417fc93d76740d974a01654a09777cb500428cc874ca9f45edfe0c4d4cd18" dependencies = [ "heck", "proc-macro2", diff --git a/bitacross-worker/litentry/macros/Cargo.toml b/bitacross-worker/litentry/macros/Cargo.toml index c3039927e1..f8c3e9f862 100644 --- a/bitacross-worker/litentry/macros/Cargo.toml +++ b/bitacross-worker/litentry/macros/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" edition = "2021" [dependencies] -cargo_toml = "0.16.3" +cargo_toml = "0.19" quote = "1.0.33" [lib] diff --git a/bitacross-worker/litentry/primitives/Cargo.toml b/bitacross-worker/litentry/primitives/Cargo.toml index 2ad014ae67..86242bc003 100644 --- a/bitacross-worker/litentry/primitives/Cargo.toml +++ b/bitacross-worker/litentry/primitives/Cargo.toml @@ -20,8 +20,8 @@ sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot- sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -strum = { version = "0.25.0", default-features = false } -strum_macros = { version = "0.25.0", default-features = false } +strum = { version = "0.26", default-features = false } +strum_macros = { version = "0.26", default-features = false } # sgx dependencies base64_sgx = { package = "base64", rev = "sgx_1.1.3", git = "https://github.com/mesalock-linux/rust-base64-sgx", optional = true } diff --git a/bitacross-worker/service/src/main_impl.rs b/bitacross-worker/service/src/main_impl.rs index 6860fef3d3..a4f58a459b 100644 --- a/bitacross-worker/service/src/main_impl.rs +++ b/bitacross-worker/service/src/main_impl.rs @@ -225,6 +225,8 @@ pub(crate) fn main() { setup::generate_shielding_key_file(enclave.as_ref()); } else if matches.is_present("signing-key") { setup::generate_signing_key_file(enclave.as_ref()); + let tee_accountid = enclave_account(enclave.as_ref()); + println!("Enclave signing account: {:}", &tee_accountid.to_ss58check()); } else if matches.is_present("dump-ra") { info!("*** Perform RA and dump cert to disk"); #[cfg(not(feature = "dcap"))] diff --git a/primitives/core/Cargo.toml b/primitives/core/Cargo.toml index 4534063d76..85419e9e06 100644 --- a/primitives/core/Cargo.toml +++ b/primitives/core/Cargo.toml @@ -6,8 +6,8 @@ version = '0.9.12' [dependencies] serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } -strum = { version = "0.25.0", default-features = false } -strum_macros = { version = "0.25.3", default-features = false } +strum = { version = "0.26", default-features = false } +strum_macros = { version = "0.26", default-features = false } codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } diff --git a/primitives/core/proc-macros/Cargo.toml b/primitives/core/proc-macros/Cargo.toml index 3c7ddb598e..436fd463d4 100644 --- a/primitives/core/proc-macros/Cargo.toml +++ b/primitives/core/proc-macros/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" proc-macro = true [dependencies] -cargo_toml = "0.16.3" +cargo_toml = "0.19" proc-macro2 = { version = "1" } quote = { version = "1" } syn = { version = "2", features = ["full", "visit-mut"] } diff --git a/tee-worker/Cargo.lock b/tee-worker/Cargo.lock index 8882ec6ce8..d169e2cd36 100644 --- a/tee-worker/Cargo.lock +++ b/tee-worker/Cargo.lock @@ -968,9 +968,9 @@ dependencies = [ [[package]] name = "cargo_toml" -version = "0.16.3" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3f9629bc6c4388ea699781dc988c2b99766d7679b151c81990b4fa1208fafd3" +checksum = "3dc9f7a067415ab5058020f04c60ec7b557084dbec0e021217bbabc7a8d38d14" dependencies = [ "serde 1.0.193", "toml 0.8.2", @@ -1317,8 +1317,8 @@ dependencies = [ "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", "sp-runtime", "sp-std 5.0.0", - "strum 0.25.0", - "strum_macros 0.25.3", + "strum 0.26.1", + "strum_macros 0.26.1", ] [[package]] @@ -7163,8 +7163,8 @@ dependencies = [ "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", "sp-runtime", "sp-std 5.0.0", - "strum 0.25.0", - "strum_macros 0.25.3", + "strum 0.26.1", + "strum_macros 0.26.1", "teerex-primitives", ] @@ -14176,9 +14176,9 @@ dependencies = [ [[package]] name = "strum" -version = "0.25.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +checksum = "723b93e8addf9aa965ebe2d11da6d7540fa2283fcea14b3371ff055f7ba13f5f" [[package]] name = "strum_macros" @@ -14195,9 +14195,9 @@ dependencies = [ [[package]] name = "strum_macros" -version = "0.25.3" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +checksum = "7a3417fc93d76740d974a01654a09777cb500428cc874ca9f45edfe0c4d4cd18" dependencies = [ "heck", "proc-macro2", diff --git a/tee-worker/enclave-runtime/Cargo.lock b/tee-worker/enclave-runtime/Cargo.lock index c825310a29..136db05d22 100644 --- a/tee-worker/enclave-runtime/Cargo.lock +++ b/tee-worker/enclave-runtime/Cargo.lock @@ -490,9 +490,9 @@ checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cargo_toml" -version = "0.16.3" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3f9629bc6c4388ea699781dc988c2b99766d7679b151c81990b4fa1208fafd3" +checksum = "3dc9f7a067415ab5058020f04c60ec7b557084dbec0e021217bbabc7a8d38d14" dependencies = [ "serde 1.0.193", "toml", @@ -5159,15 +5159,15 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "strum" -version = "0.25.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +checksum = "723b93e8addf9aa965ebe2d11da6d7540fa2283fcea14b3371ff055f7ba13f5f" [[package]] name = "strum_macros" -version = "0.25.3" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +checksum = "7a3417fc93d76740d974a01654a09777cb500428cc874ca9f45edfe0c4d4cd18" dependencies = [ "heck", "proc-macro2", diff --git a/tee-worker/litentry/primitives/Cargo.toml b/tee-worker/litentry/primitives/Cargo.toml index a809fa5a35..076b5aab29 100644 --- a/tee-worker/litentry/primitives/Cargo.toml +++ b/tee-worker/litentry/primitives/Cargo.toml @@ -20,8 +20,8 @@ sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot- sp-io = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -strum = { version = "0.25.0", default-features = false } -strum_macros = { version = "0.25.0", default-features = false } +strum = { version = "0.26", default-features = false } +strum_macros = { version = "0.26", default-features = false } # sgx dependencies base64_sgx = { package = "base64", rev = "sgx_1.1.3", git = "https://github.com/mesalock-linux/rust-base64-sgx", optional = true } From 0b690cd25f3f7fe12815349e218969377a54e023 Mon Sep 17 00:00:00 2001 From: Verin1005 <104152026+Verin1005@users.noreply.github.com> Date: Wed, 31 Jan 2024 11:46:50 +0800 Subject: [PATCH 49/51] Data providers tests for vip3 (#2431) * revert data-provider-tests * fix CorePrimitivesIdentity imports * Restore the data-provider test. * remove vipp3 test file * debug vcPayloadJson * refactor credential definitions * format * add event listening * complete vip3 tests * fix vip3MembershipCardSliver * add to ci.yml * fix ci * extract and reuse function * test cli * format * remove client config * format * random substrate wallet * test client * fix cli issue * fix cli path * env example * debug local * format * fix cli path * test mock address * withdraw account test. * modefy into json file * delete ts file * format * improve logic * fix comments --- .github/workflows/ci.yml | 1 + tee-worker/docker/lit-data-providers-test.yml | 24 ++ .../integration-tests/.env.local.example | 4 +- .../ts-tests/integration-tests/.env.staging | 1 + .../common/credential-json/index.ts | 28 +++ .../credential-json/vip3-credential-test.json | 30 +++ .../integration-tests/common/helpers.ts | 6 + .../common/utils/assertion.ts | 1 + .../integration-tests/data-providers.test.ts | 134 ++++++++++++ .../ts-tests/integration-tests/package.json | 7 +- tee-worker/ts-tests/pnpm-lock.yaml | 207 ++++++++++++++++-- 11 files changed, 426 insertions(+), 17 deletions(-) create mode 100644 tee-worker/docker/lit-data-providers-test.yml create mode 100644 tee-worker/ts-tests/integration-tests/common/credential-json/index.ts create mode 100644 tee-worker/ts-tests/integration-tests/common/credential-json/vip3-credential-test.json create mode 100644 tee-worker/ts-tests/integration-tests/data-providers.test.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 37d6fb6c2e..7785e11e78 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -705,6 +705,7 @@ jobs: - test_name: lit-parentchain-nonce - test_name: lit-ii-batch-test - test_name: lit-test-stress-script + - test_name: lit-data-providers-test steps: - uses: actions/checkout@v4 diff --git a/tee-worker/docker/lit-data-providers-test.yml b/tee-worker/docker/lit-data-providers-test.yml new file mode 100644 index 0000000000..be0ee66ed4 --- /dev/null +++ b/tee-worker/docker/lit-data-providers-test.yml @@ -0,0 +1,24 @@ +services: + lit-data-providers-test: + image: litentry/litentry-cli:latest + container_name: litentry-data-providers-test + volumes: + - ../ts-tests:/ts-tests + - ../client-api:/client-api + - ../cli:/usr/local/worker-cli + build: + context: .. + dockerfile: build.Dockerfile + target: deployed-client + depends_on: + litentry-node: + condition: service_healthy + litentry-worker-1: + condition: service_healthy + networks: + - litentry-test-network + entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_integration_test.sh test-data-providers 2>&1' " + restart: "no" +networks: + litentry-test-network: + driver: bridge diff --git a/tee-worker/ts-tests/integration-tests/.env.local.example b/tee-worker/ts-tests/integration-tests/.env.local.example index 846607efed..74060ff00f 100644 --- a/tee-worker/ts-tests/integration-tests/.env.local.example +++ b/tee-worker/ts-tests/integration-tests/.env.local.example @@ -1,3 +1,5 @@ NODE_ENV = local WORKER_ENDPOINT = wss://localhost:2000 -NODE_ENDPOINT = ws://localhost:9944 \ No newline at end of file +NODE_ENDPOINT = ws://localhost:9944 +BINARY_DIR=../../bin +LITENTRY_CLI_DIR=../../bin/litentry-cli diff --git a/tee-worker/ts-tests/integration-tests/.env.staging b/tee-worker/ts-tests/integration-tests/.env.staging index 6b035ce969..50124dd6bb 100644 --- a/tee-worker/ts-tests/integration-tests/.env.staging +++ b/tee-worker/ts-tests/integration-tests/.env.staging @@ -2,3 +2,4 @@ NODE_ENV = staging WORKER_ENDPOINT = wss://litentry-worker-1:2011 NODE_ENDPOINT = "ws://litentry-node:9912" BINARY_DIR=/usr/local/bin +LITENTRY_CLI_DIR=/usr/local/bin/litentry-cli \ No newline at end of file diff --git a/tee-worker/ts-tests/integration-tests/common/credential-json/index.ts b/tee-worker/ts-tests/integration-tests/common/credential-json/index.ts new file mode 100644 index 0000000000..19f9c55267 --- /dev/null +++ b/tee-worker/ts-tests/integration-tests/common/credential-json/index.ts @@ -0,0 +1,28 @@ +import { CorePrimitivesAssertion, CorePrimitivesNetworkWeb3Network } from 'parachain-api'; +import type { Codec } from '@polkadot/types-codec/types'; +import type { U8aLike } from '@polkadot/util/types'; +type DataProvider = { + id: string; + name: string; + url: string; +}; + +type AssertionGenericPayload = string | Array | Record; + +import vip3Json from './vip3-credential-test.json' assert { type: 'json' }; +export const vip3CredentialJson = vip3Json as unknown as CredentialDefinition[]; + +export interface CredentialDefinition { + id: string; + name: string; + description: string; + assertion: { + id: CorePrimitivesAssertion['type']; + payload: AssertionGenericPayload; + }; + dataProvider: DataProvider; + network: CorePrimitivesNetworkWeb3Network['type']; + mockDid: string; + mockWeb3Network: string; + expectedCredentialValue: boolean; +} diff --git a/tee-worker/ts-tests/integration-tests/common/credential-json/vip3-credential-test.json b/tee-worker/ts-tests/integration-tests/common/credential-json/vip3-credential-test.json new file mode 100644 index 0000000000..a4c8573d43 --- /dev/null +++ b/tee-worker/ts-tests/integration-tests/common/credential-json/vip3-credential-test.json @@ -0,0 +1,30 @@ +[ + { + "id": "vip3-membership-card-gold", + "name": "VIP3 Membership Card Gold", + "description": "VIP3 Membership Card Gold", + "assertion": { + "id": "Vip3MembershipCard", + "payload": "Gold" + }, + "dataProvider": "vip3", + "network": "ethereum", + "mockDid": "litentry:evm:0x651614cA9097C5ba189Ef85e7851Ef9cff592B2c", + "mockWeb3Network": "bsc,ethereum", + "expectedCredentialValue": true + }, + { + "id": "vip3-membership-card-silver", + "name": "VIP3 Membership Card Silver", + "description": "VIP3 Membership Card Silver", + "assertion": { + "id": "Vip3MembershipCard", + "payload": "Silver" + }, + "dataProvider": "vip3", + "network": "ethereum", + "mockDid": "litentry:evm:0x10CdF7F7A32E2F24c853AE6567b75D862Ee2B46f", + "mockWeb3Network": "bsc,ethereum", + "expectedCredentialValue": true + } +] diff --git a/tee-worker/ts-tests/integration-tests/common/helpers.ts b/tee-worker/ts-tests/integration-tests/common/helpers.ts index 6d41721001..af685bf982 100644 --- a/tee-worker/ts-tests/integration-tests/common/helpers.ts +++ b/tee-worker/ts-tests/integration-tests/common/helpers.ts @@ -5,6 +5,7 @@ import type { KeyringPair } from '@polkadot/keyring/types'; import type { HexString } from '@polkadot/util/types'; import './config'; import { IntegrationTestContext, JsonRpcRequest } from './common-types'; +import { randomBytes } from 'crypto'; // format and setup const keyring = new Keyring({ type: 'sr25519' }); @@ -71,3 +72,8 @@ export function nextRequestId(context: IntegrationTestContext): number { context.requestId = nextId; return nextId; } + +export function randomSubstrateWallet(): KeyringPair { + const keyring = new Keyring({ type: 'sr25519' }); + return keyring.addFromSeed(randomBytes(32)); +} diff --git a/tee-worker/ts-tests/integration-tests/common/utils/assertion.ts b/tee-worker/ts-tests/integration-tests/common/utils/assertion.ts index a9cf3176ca..fee60cc199 100644 --- a/tee-worker/ts-tests/integration-tests/common/utils/assertion.ts +++ b/tee-worker/ts-tests/integration-tests/common/utils/assertion.ts @@ -272,6 +272,7 @@ export async function assertVc(context: IntegrationTestContext, subject: CorePri // step 4 // extrac proof and vc without proof json const vcPayloadJson = JSON.parse(decryptVcPayload); + const { proof, ...vcWithoutProof } = vcPayloadJson; // step 5 diff --git a/tee-worker/ts-tests/integration-tests/data-providers.test.ts b/tee-worker/ts-tests/integration-tests/data-providers.test.ts new file mode 100644 index 0000000000..78903f6a7f --- /dev/null +++ b/tee-worker/ts-tests/integration-tests/data-providers.test.ts @@ -0,0 +1,134 @@ +import { randomBytes, KeyObject } from 'crypto'; +import { step } from 'mocha-steps'; +import { assert } from 'chai'; +import { buildIdentityFromKeypair, decryptWithAes, initIntegrationTestContext, PolkadotSigner } from './common/utils'; +import { randomSubstrateWallet } from './common/helpers'; +import { assertIsInSidechainBlock } from './common/utils/assertion'; +import { + getSidechainNonce, + getTeeShieldingKey, + sendRequestFromTrustedCall, + createSignedTrustedCallRequestVc, +} from './common/di-utils'; // @fixme move to a better place +import type { IntegrationTestContext } from './common/common-types'; +import { CorePrimitivesIdentity, RequestVCResult } from 'parachain-api'; +import { aesKey } from './common/call'; +import { $ as zx } from 'zx'; +import { subscribeToEventsWithExtHash } from './common/transactions'; +import { KeyringPair } from '@polkadot/keyring/types'; +import { u8aToHex } from '@polkadot/util'; +import { vip3CredentialJson, CredentialDefinition } from './common/credential-json'; +describe('Test Vc (direct invocation)', function () { + let context: IntegrationTestContext = undefined as any; + let teeShieldingKey: KeyObject = undefined as any; + const substrateIdentities: CorePrimitivesIdentity[] = []; + + const clientDir = process.env.LITENTRY_CLI_DIR; + const reqExtHash = '0x0000000000000000000000000000000000000000000000000000000000000000'; + const keyringPairs: KeyringPair[] = []; + let argvId = ''; + const credentialsJson: CredentialDefinition[] = [...vip3CredentialJson]; + + this.timeout(6000000); + before(async () => { + context = await initIntegrationTestContext(process.env.WORKER_ENDPOINT!, process.env.NODE_ENDPOINT!); + teeShieldingKey = await getTeeShieldingKey(context); + }); + + // usage example: + // `pnpm run test-data-providers:local --id=vip3-membership-card-gold` for single test + // `pnpm run test-data-providers:local` for all tests + const argv = process.argv.indexOf('--id'); + argvId = process.argv[argv + 1]; + const { + protocol: workerProtocal, + hostname: workerHostname, + port: workerPort, + } = new URL(process.env.WORKER_ENDPOINT!); + const { protocol: nodeProtocal, hostname: nodeHostname, port: nodePort } = new URL(process.env.NODE_ENDPOINT!); + + async function linkIdentityViaCli(id: string) { + const credentialDefinitions = credentialsJson.find((item) => item.id === id) as CredentialDefinition; + + const keyringPair = randomSubstrateWallet(); + keyringPairs.push(keyringPair); + const formatAddress = u8aToHex(keyringPair.publicKey); + + const substrateIdentity = await buildIdentityFromKeypair(new PolkadotSigner(keyringPair), context); + substrateIdentities.push(substrateIdentity); + const eventsPromise = subscribeToEventsWithExtHash(reqExtHash, context); + try { + // CLIENT = "$CLIENT_BIN -p $NPORT -P $WORKER1PORT -u $NODEURL -U $WORKER1URL" + const commandPromise = zx`${clientDir} -p ${nodePort} -P ${workerPort} -u ${ + nodeProtocal + nodeHostname + } -U ${workerProtocal + workerHostname}\ + trusted -d link-identity did:litentry:substrate:${formatAddress}\ + did:${credentialDefinitions.mockDid}\ + ${credentialDefinitions.mockWeb3Network}`; + + await commandPromise; + } catch (error: any) { + console.log(`Exit code: ${error.exitCode}`); + console.log(`Error: ${error.stderr}`); + throw error; + } + + const events = (await eventsPromise).map(({ event }) => event); + assert.equal(events.length, 1); + } + + async function requestVc(id: string, index: number) { + const credentialDefinitions = credentialsJson.find((item) => item.id === id) as CredentialDefinition; + + const assertion = { + [credentialDefinitions.assertion.id]: credentialDefinitions.assertion.payload, + }; + + let currentNonce = (await getSidechainNonce(context, teeShieldingKey, substrateIdentities[index])).toNumber(); + const getNextNonce = () => currentNonce++; + const nonce = getNextNonce(); + console.log(nonce, substrateIdentities[index].toHuman(), u8aToHex(keyringPairs[index].publicKey)); + + const requestIdentifier = `0x${randomBytes(32).toString('hex')}`; + const requestVcCall = await createSignedTrustedCallRequestVc( + context.api, + context.mrEnclave, + context.api.createType('Index', nonce), + new PolkadotSigner(keyringPairs[index]), + substrateIdentities[index], + context.api.createType('Assertion', assertion).toHex(), + context.api.createType('Option', aesKey).toHex(), + requestIdentifier + ); + const res = await sendRequestFromTrustedCall(context, teeShieldingKey, requestVcCall); + await assertIsInSidechainBlock(`${Object.keys(assertion)[0]} requestVcCall`, res); + + const vcResults = context.api.createType('RequestVCResult', res.value) as unknown as RequestVCResult; + const decryptVcPayload = decryptWithAes(aesKey, vcResults.vc_payload, 'utf-8').replace('0x', ''); + const vcPayloadJson = JSON.parse(decryptVcPayload); + + assert.equal(vcPayloadJson.credentialSubject.values[0], credentialDefinitions.expectedCredentialValue); + } + + if (argvId && credentialsJson.find((item) => item.id === argvId)) { + const credentialDefinitions = credentialsJson.find((item) => item.id === argvId) as CredentialDefinition; + + step( + `linking identity::${credentialDefinitions.mockDid} via cli and request vc::${credentialDefinitions.mockDid}`, + async function () { + await linkIdentityViaCli(argvId); + await requestVc(argvId, 0); + } + ); + } else { + credentialsJson.forEach(({ id }, index) => { + step( + `linking identity::${credentialsJson[index].mockDid} via cli and request vc::${credentialsJson[index].id}`, + async function () { + await linkIdentityViaCli(id); + await requestVc(id, index); + } + ); + }); + } +}); diff --git a/tee-worker/ts-tests/integration-tests/package.json b/tee-worker/ts-tests/integration-tests/package.json index fbe5595223..1e5f3dd9a7 100644 --- a/tee-worker/ts-tests/integration-tests/package.json +++ b/tee-worker/ts-tests/integration-tests/package.json @@ -18,7 +18,9 @@ "test-ii-vc:local": "pnpm exec eslint . && pnpm exec cross-env NODE_ENV=local mocha --exit --sort -r ts-node/register --loader=ts-node/esm 'ii_vc.test.ts'", "test-ii-vc:staging": "pnpm exec eslint . && pnpm exec cross-env NODE_ENV=staging mocha --exit --sort -r ts-node/register --loader=ts-node/esm 'ii_vc.test.ts'", "test-ii-batch:local": "pnpm exec eslint . && pnpm exec cross-env NODE_ENV=local mocha --exit --sort -r ts-node/register --loader=ts-node/esm 'ii_batch.test.ts'", - "test-ii-batch:staging": "pnpm exec eslint . && pnpm exec cross-env NODE_ENV=staging mocha --exit --sort -r ts-node/register --loader=ts-node/esm 'ii_batch.test.ts'" + "test-ii-batch:staging": "pnpm exec eslint . && pnpm exec cross-env NODE_ENV=staging mocha --exit --sort -r ts-node/register --loader=ts-node/esm 'ii_batch.test.ts'", + "test-data-providers:local": "pnpm exec eslint . && pnpm exec cross-env NODE_ENV=local mocha --exit --sort -r ts-node/register --loader=ts-node/esm 'data-providers.test.ts'", + "test-data-providers:staging": "pnpm exec eslint . && pnpm exec cross-env NODE_ENV=staging mocha --exit --sort -r ts-node/register --loader=ts-node/esm 'data-providers.test.ts'" }, "dependencies": { "@noble/ed25519": "^1.7.3", @@ -49,7 +51,8 @@ "scale-ts": "^0.2.11", "sidechain-api": "file:../../client-api/sidechain-api", "websocket-as-promised": "^2.0.1", - "ws": "^8.8.1" + "ws": "^8.8.1", + "zx": "^7.2.3" }, "devDependencies": { "@ethersproject/providers": "^5.7.2", diff --git a/tee-worker/ts-tests/pnpm-lock.yaml b/tee-worker/ts-tests/pnpm-lock.yaml index da37548f54..edaf43607f 100644 --- a/tee-worker/ts-tests/pnpm-lock.yaml +++ b/tee-worker/ts-tests/pnpm-lock.yaml @@ -97,6 +97,9 @@ importers: ws: specifier: ^8.8.1 version: 8.14.2 + zx: + specifier: ^7.2.3 + version: 7.2.3 devDependencies: '@ethersproject/providers': specifier: ^5.7.2 @@ -797,12 +800,10 @@ packages: dependencies: '@nodelib/fs.stat': 2.0.5 run-parallel: 1.2.0 - dev: true /@nodelib/fs.stat@2.0.5: resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} engines: {node: '>= 8'} - dev: true /@nodelib/fs.walk@1.2.8: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} @@ -810,7 +811,6 @@ packages: dependencies: '@nodelib/fs.scandir': 2.1.5 fastq: 1.15.0 - dev: true /@polkadot/api-augment@10.9.1: resolution: {integrity: sha512-kRZZvCFVcN4hAH4dJ+Qzfdy27/4EEq3oLDf3ihj0LTVrAezSWcKPGE3EVFy+Mn6Lo4SUc7RVyoKvIUhSk2l4Dg==} @@ -1287,21 +1287,52 @@ packages: resolution: {integrity: sha512-VOVRLM1mBxIRxydiViqPcKn6MIxZytrbMpd6RJLIWKxUNr3zux8no0Oc7kJx0WAPIitgZ0gkrDS+btlqQpubpw==} dev: true + /@types/fs-extra@11.0.4: + resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==} + dependencies: + '@types/jsonfile': 6.1.4 + '@types/node': 20.7.1 + dev: false + /@types/json-schema@7.0.13: resolution: {integrity: sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==} dev: true + /@types/jsonfile@6.1.4: + resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==} + dependencies: + '@types/node': 20.7.1 + dev: false + + /@types/minimist@1.2.5: + resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} + dev: false + /@types/mocha@10.0.2: resolution: {integrity: sha512-NaHL0+0lLNhX6d9rs+NSt97WH/gIlRHmszXbQ/8/MV/eVcFNdeJ/GYhrFuUc8K7WuPhRhTSdMkCp8VMzhUq85w==} dev: true + /@types/node@18.19.9: + resolution: {integrity: sha512-oZFKlC8l5YtzGQNT4zC2PiSSKzQVZ8bAwwd+EYdPLtyk0nSEq6O16SkK+rkkT2eflDAbormJgEF3QnH3oDrTSw==} + dependencies: + undici-types: 5.26.5 + dev: false + /@types/node@20.7.1: resolution: {integrity: sha512-LT+OIXpp2kj4E2S/p91BMe+VgGX2+lfO+XTpfXhh+bCk2LkQtHZSub8ewFBMGP5ClysPjTDFa4sMI8Q3n4T0wg==} + /@types/ps-tree@1.1.6: + resolution: {integrity: sha512-PtrlVaOaI44/3pl3cvnlK+GxOM3re2526TJvPvh7W+keHIXdV4TE0ylpPBAcvFQCbGitaTXwL9u+RF7qtVeazQ==} + dev: false + /@types/semver@7.5.3: resolution: {integrity: sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==} dev: true + /@types/which@3.0.3: + resolution: {integrity: sha512-2C1+XoY0huExTbs8MQv1DuS5FS86+SEjdM9F/+GS61gg5Hqbtj8ZiDSx8MfWcyei907fIPbfPGCOrNUTnVHY1g==} + dev: false + /@types/ws@8.5.6: resolution: {integrity: sha512-8B5EO9jLVCy+B58PLHvLDuOD8DRVMgQzq8d55SjLCOn9kqGyqOvy27exVaTio1q1nX5zLu8/6N0n2ThSxOM6tg==} dependencies: @@ -1671,6 +1702,11 @@ packages: ansi-styles: 4.3.0 supports-color: 7.2.0 + /chalk@5.3.0: + resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + dev: false + /check-error@1.0.3: resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} dependencies: @@ -1811,7 +1847,6 @@ packages: engines: {node: '>=8'} dependencies: path-type: 4.0.0 - dev: true /doctrine@3.0.0: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} @@ -1825,6 +1860,10 @@ packages: engines: {node: '>=12'} dev: true + /duplexer@0.1.2: + resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + dev: false + /ecurve@1.0.6: resolution: {integrity: sha512-/BzEjNfiSuB7jIWKcS/z8FK9jNjmEWvUV2YZ4RLSmcDtP7Lq0m6FvDuSnJpBlDpGRpfRQeTLGLBI8H+kEv0r+w==} dependencies: @@ -2060,6 +2099,18 @@ packages: - utf-8-validate dev: true + /event-stream@3.3.4: + resolution: {integrity: sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==} + dependencies: + duplexer: 0.1.2 + from: 0.1.7 + map-stream: 0.1.0 + pause-stream: 0.0.11 + split: 0.3.3 + stream-combiner: 0.0.4 + through: 2.3.8 + dev: false + /eventemitter3@4.0.7: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} requiresBuild: true @@ -2082,7 +2133,6 @@ packages: glob-parent: 5.1.2 merge2: 1.4.1 micromatch: 4.0.5 - dev: true /fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} @@ -2096,7 +2146,6 @@ packages: resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} dependencies: reusify: 1.0.4 - dev: true /fetch-blob@3.2.0: resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} @@ -2156,6 +2205,19 @@ packages: fetch-blob: 3.2.0 dev: false + /from@0.1.7: + resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} + dev: false + + /fs-extra@11.2.0: + resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} + engines: {node: '>=14.14'} + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + dev: false + /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -2184,6 +2246,11 @@ packages: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} dev: false + /fx@31.0.0: + resolution: {integrity: sha512-OoeYSPKqNKmfnH4s+rGYI0c8OZmqqOOXsUtqy0YyHqQQoQSDiDs3m3M9uXKx5OQR+jDx7/FhYqpO3kl/As/xgg==} + hasBin: true + dev: false + /get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -2268,12 +2335,27 @@ packages: slash: 3.0.0 dev: true + /globby@13.2.2: + resolution: {integrity: sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + dir-glob: 3.0.1 + fast-glob: 3.3.1 + ignore: 5.2.4 + merge2: 1.4.1 + slash: 4.0.0 + dev: false + /gopd@1.0.1: resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} dependencies: get-intrinsic: 1.2.1 dev: false + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + dev: false + /graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} dev: true @@ -2349,7 +2431,6 @@ packages: /ignore@5.2.4: resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} engines: {node: '>= 4'} - dev: true /import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} @@ -2516,7 +2597,6 @@ packages: /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - dev: true /js-base64@3.7.5: resolution: {integrity: sha512-3MEt5DTINKqfScXKfJFrRbxkrnk2AxPWGBL/ycjz4dK8iqiSJ06UxD8jh8xuh6p10TX4t2+7FsBYVxxQbMg+qA==} @@ -2556,6 +2636,14 @@ packages: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} dev: false + /jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + dev: false + /keyv@4.5.3: resolution: {integrity: sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==} dependencies: @@ -2607,10 +2695,13 @@ packages: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} dev: true + /map-stream@0.1.0: + resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==} + dev: false + /merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - dev: true /micro-base58@0.5.1: resolution: {integrity: sha512-iwqAmg66VjB2LA3BcUxyrOyqck4HLLtSzKnx2VQSnN5piQji598N15P8RRx2d6lPvJ98B1b0cl2VbvQeFeWdig==} @@ -2622,7 +2713,6 @@ packages: dependencies: braces: 3.0.2 picomatch: 2.3.1 - dev: true /minimalistic-assert@1.0.1: resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} @@ -2721,6 +2811,15 @@ packages: engines: {node: '>=10.5.0'} dev: false + /node-fetch@3.3.1: + resolution: {integrity: sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + dev: false + /node-fetch@3.3.2: resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -2811,11 +2910,16 @@ packages: /path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} - dev: true /pathval@1.1.1: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + /pause-stream@0.0.11: + resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} + dependencies: + through: 2.3.8 + dev: false + /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} @@ -2857,13 +2961,20 @@ packages: engines: {node: '>= 8'} dev: false + /ps-tree@1.2.0: + resolution: {integrity: sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==} + engines: {node: '>= 0.10'} + hasBin: true + dependencies: + event-stream: 3.3.4 + dev: false + /punycode@2.3.0: resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} engines: {node: '>=6'} /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - dev: true /randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} @@ -2902,7 +3013,6 @@ packages: /reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - dev: true /rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} @@ -2915,7 +3025,6 @@ packages: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: queue-microtask: 1.2.3 - dev: true /rxjs@7.8.1: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} @@ -2999,6 +3108,11 @@ packages: engines: {node: '>=8'} dev: true + /slash@4.0.0: + resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} + engines: {node: '>=12'} + dev: false + /smoldot@1.0.4: resolution: {integrity: sha512-N3TazI1C4GGrseFH/piWyZCCCRJTRx2QhDfrUKRT4SzILlW5m8ayZ3QTKICcz1C/536T9cbHHJyP7afxI6Mi1A==} requiresBuild: true @@ -3016,6 +3130,18 @@ packages: engines: {node: '>=0.10.0'} dev: false + /split@0.3.3: + resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==} + dependencies: + through: 2.3.8 + dev: false + + /stream-combiner@0.0.4: + resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} + dependencies: + duplexer: 0.1.2 + dev: false + /string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -3075,6 +3201,10 @@ packages: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} dev: true + /through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + dev: false + /to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -3207,6 +3337,15 @@ packages: which-boxed-primitive: 1.0.2 dev: false + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + dev: false + + /universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + dev: false + /uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: @@ -3221,6 +3360,11 @@ packages: engines: {node: '>= 8'} dev: false + /webpod@0.0.2: + resolution: {integrity: sha512-cSwwQIeg8v4i3p4ajHhwgR7N6VyxAf+KYSSsY6Pd3aETE+xEU4vbitz7qQkB0I321xnhDdgtxuiSfk5r/FVtjg==} + hasBin: true + dev: false + /websocket-as-promised@2.0.1: resolution: {integrity: sha512-ePV26D/D37ughXU9j+DjGmwUbelWJrC/vi+6GK++fRlBJmS7aU9T8ABu47KFF0O7r6XN2NAuqJRpegbUwXZxQg==} engines: {node: '>=6'} @@ -3260,6 +3404,14 @@ packages: isexe: 2.0.0 dev: true + /which@3.0.1: + resolution: {integrity: sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: false + /wordwrap@1.0.0: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} dev: false @@ -3312,6 +3464,11 @@ packages: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true + /yaml@2.3.4: + resolution: {integrity: sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==} + engines: {node: '>= 14'} + dev: false + /yargs-parser@20.2.4: resolution: {integrity: sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==} engines: {node: '>=10'} @@ -3368,6 +3525,28 @@ packages: resolution: {integrity: sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==} dev: false + /zx@7.2.3: + resolution: {integrity: sha512-QODu38nLlYXg/B/Gw7ZKiZrvPkEsjPN3LQ5JFXM7h0JvwhEdPNNl+4Ao1y4+o3CLNiDUNcwzQYZ4/Ko7kKzCMA==} + engines: {node: '>= 16.0.0'} + hasBin: true + dependencies: + '@types/fs-extra': 11.0.4 + '@types/minimist': 1.2.5 + '@types/node': 18.19.9 + '@types/ps-tree': 1.1.6 + '@types/which': 3.0.3 + chalk: 5.3.0 + fs-extra: 11.2.0 + fx: 31.0.0 + globby: 13.2.2 + minimist: 1.2.8 + node-fetch: 3.3.1 + ps-tree: 1.2.0 + webpod: 0.0.2 + which: 3.0.1 + yaml: 2.3.4 + dev: false + file:../client-api/parachain-api: resolution: {directory: ../client-api/parachain-api, type: directory} name: '@litentry/parachain-api' From c5b984d653fbc98d3bfe86e73755aad391e6c877 Mon Sep 17 00:00:00 2001 From: "will.li" <120463031+higherordertech@users.noreply.github.com> Date: Wed, 31 Jan 2024 15:44:46 +1100 Subject: [PATCH 50/51] fix: remove network option from nodereal NODEREAL_API_CHAIN_NETWORK_URL config, and always use mainnet for network (#2451) Co-authored-by: higherordertech --- tee-worker/litentry/core/data-providers/src/lib.rs | 2 +- tee-worker/local-setup/.env.dev | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tee-worker/litentry/core/data-providers/src/lib.rs b/tee-worker/litentry/core/data-providers/src/lib.rs index 26bc0b4cf7..973da9c4ff 100644 --- a/tee-worker/litentry/core/data-providers/src/lib.rs +++ b/tee-worker/litentry/core/data-providers/src/lib.rs @@ -222,7 +222,7 @@ impl DataProviderConfig { nodereal_api_retry_delay: 5000, nodereal_api_retry_times: 2, nodereal_api_url: "https://open-platform.nodereal.io/".to_string(), - nodereal_api_chain_network_url: "https://{chain}-{network}.nodereal.io/".to_string(), + nodereal_api_chain_network_url: "https://{chain}-mainnet.nodereal.io/".to_string(), contest_legend_discord_role_id: "1172576273063739462".to_string(), contest_popularity_discord_role_id: "1172576681119195208".to_string(), contest_participant_discord_role_id: "1172576734135210104".to_string(), diff --git a/tee-worker/local-setup/.env.dev b/tee-worker/local-setup/.env.dev index 4dee670bfd..7c9cf8f7bb 100644 --- a/tee-worker/local-setup/.env.dev +++ b/tee-worker/local-setup/.env.dev @@ -44,7 +44,7 @@ ONEBLOCK_NOTION_URL=https://api.notion.com/v1/blocks/e4068e6a326243468f35dcdc0c4 SORA_QUIZ_MASTER_ID=1164463721989554218 SORA_QUIZ_ATTENDEE_ID=1166941149219532800 NODEREAL_API_URL=https://open-platform.nodereal.io/ -NODEREAL_API_CHAIN_NETWORK_URL=https://{chain}-{network}.nodereal.io/ +NODEREAL_API_CHAIN_NETWORK_URL=https://{chain}-mainnet.nodereal.io/ CONTEST_LEGEND_DISCORD_ROLE_ID=1172576273063739462 CONTEST_POPULARITY_DISCORD_ROLE_ID=1172576681119195208 CONTEST_PARTICIPANT_DISCORD_ROLE_ID=1172576734135210104 From 1c29873489fc1065c26ffe6d03a78c28d8cd78e2 Mon Sep 17 00:00:00 2001 From: Kai <7630809+Kailai-Wang@users.noreply.github.com> Date: Wed, 31 Jan 2024 10:06:48 +0100 Subject: [PATCH 51/51] Adds pallet-account-fix to rococo (#2450) * call filter * bump version * add account-fix for rococo --- Cargo.lock | 1 + runtime/litentry/src/lib.rs | 4 +++- runtime/rococo/Cargo.toml | 4 ++++ runtime/rococo/src/lib.rs | 11 +++++++++-- tee-worker/Cargo.lock | 16 ++++++++++++++++ tee-worker/app-libs/sgx-runtime/src/lib.rs | 2 +- 6 files changed, 34 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a548deca91..4e9e5bcfdd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10515,6 +10515,7 @@ dependencies = [ "orml-tokens", "orml-traits", "orml-xtokens", + "pallet-account-fix", "pallet-asset-manager", "pallet-aura", "pallet-authorship", diff --git a/runtime/litentry/src/lib.rs b/runtime/litentry/src/lib.rs index e82fdf1278..7158892b06 100644 --- a/runtime/litentry/src/lib.rs +++ b/runtime/litentry/src/lib.rs @@ -996,7 +996,9 @@ impl Contains for NormalModeFilter { // Identity RuntimeCall::ParachainIdentity(_) | // Balance - RuntimeCall::Balances(_) + RuntimeCall::Balances(_) | + // AccountFix + RuntimeCall::AccountFix(_) ) } } diff --git a/runtime/rococo/Cargo.toml b/runtime/rococo/Cargo.toml index 9ce0f6106b..e5593685ef 100644 --- a/runtime/rococo/Cargo.toml +++ b/runtime/rococo/Cargo.toml @@ -86,6 +86,7 @@ frame-benchmarking = { git = "https://github.com/paritytech/substrate", default- frame-system-benchmarking = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.42", optional = true } # Rococo pallets +pallet-account-fix = { path = "../../pallets/account-fix", default-features = false } pallet-asset-manager = { path = "../../pallets/xcm-asset-manager", default-features = false } pallet-bridge = { path = "../../pallets/bridge", default-features = false } pallet-bridge-transfer = { path = "../../pallets/bridge-transfer", default-features = false } @@ -179,6 +180,7 @@ runtime-benchmarks = [ "pallet-sidechain/runtime-benchmarks", "pallet-teeracle/runtime-benchmarks", "pallet-vc-management/runtime-benchmarks", + "pallet-account-fix/runtime-benchmarks", ] std = [ "codec/std", @@ -268,6 +270,7 @@ std = [ "pallet-sidechain/std", "pallet-teeracle/std", "pallet-vc-management/std", + "pallet-account-fix/std", "moonbeam-evm-tracer/std", "moonbeam-rpc-primitives-debug/std", "moonbeam-rpc-primitives-txpool/std", @@ -323,4 +326,5 @@ try-runtime = [ "pallet-vesting/try-runtime", "pallet-xcm/try-runtime", "parachain-info/try-runtime", + "pallet-account-fix/try-runtime", ] diff --git a/runtime/rococo/src/lib.rs b/runtime/rococo/src/lib.rs index 5d991f2eb9..069c4fa555 100644 --- a/runtime/rococo/src/lib.rs +++ b/runtime/rococo/src/lib.rs @@ -245,7 +245,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_name: create_runtime_str!("rococo-parachain"), authoring_version: 1, // same versioning-mechanism as polkadot: use last digit for minor updates - spec_version: 9172, + spec_version: 9173, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, @@ -775,6 +775,10 @@ impl pallet_sudo::Config for Runtime { type RuntimeEvent = RuntimeEvent; } +impl pallet_account_fix::Config for Runtime { + type Currency = Balances; +} + parameter_types! { pub const ReservedXcmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); pub const ReservedDmpWeight: Weight = MAXIMUM_BLOCK_WEIGHT.saturating_div(4); @@ -1257,6 +1261,7 @@ construct_runtime! { Ethereum: pallet_ethereum = 121, // TMP + AccountFix: pallet_account_fix = 254, Sudo: pallet_sudo = 255, } } @@ -1344,7 +1349,9 @@ impl Contains for NormalModeFilter { // EVM // Substrate EVM extrinsic not allowed // So no EVM pallet - RuntimeCall::Ethereum(_) + RuntimeCall::Ethereum(_) | + // AccountFix + RuntimeCall::AccountFix(_) ) } } diff --git a/tee-worker/Cargo.lock b/tee-worker/Cargo.lock index d169e2cd36..151af2fadf 100644 --- a/tee-worker/Cargo.lock +++ b/tee-worker/Cargo.lock @@ -8592,6 +8592,21 @@ dependencies = [ "libm 0.1.4", ] +[[package]] +name = "pallet-account-fix" +version = "0.1.0" +dependencies = [ + "frame-support", + "frame-system", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io 7.0.0 (git+https://github.com/paritytech/substrate.git?branch=polkadot-v0.9.42)", + "sp-runtime", + "sp-std 5.0.0", +] + [[package]] name = "pallet-asset-manager" version = "0.1.0" @@ -11160,6 +11175,7 @@ dependencies = [ "orml-tokens", "orml-traits", "orml-xtokens", + "pallet-account-fix", "pallet-asset-manager", "pallet-aura", "pallet-authorship", diff --git a/tee-worker/app-libs/sgx-runtime/src/lib.rs b/tee-worker/app-libs/sgx-runtime/src/lib.rs index 9f94fe2d99..0c2c5f715a 100644 --- a/tee-worker/app-libs/sgx-runtime/src/lib.rs +++ b/tee-worker/app-libs/sgx-runtime/src/lib.rs @@ -139,7 +139,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("node-template"), impl_name: create_runtime_str!("node-template"), authoring_version: 1, - spec_version: 102, + spec_version: 103, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1,